diff --git a/.build.sh b/.build.sh new file mode 100755 index 000000000000..1f7a1488371b --- /dev/null +++ b/.build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -ue + +# Expect the following envvars to be set: +# - APP +# - VERSION +# - COMMIT +# - TARGET_OS +# - LEDGER_ENABLED +# - DEBUG + +# Source builder's functions library +. /usr/local/share/tendermint/buildlib.sh + +# These variables are now available +# - BASEDIR +# - OUTDIR + +# Build for each os-architecture pair +for platform in ${TARGET_PLATFORMS} ; do + # This function sets GOOS, GOARCH, and OS_FILE_EXT environment variables + # according to the build target platform. OS_FILE_EXT is empty in all + # cases except when the target platform is 'windows'. + setup_build_env_for_platform "${platform}" + + make clean + echo Building for $(go env GOOS)/$(go env GOARCH) >&2 + GOROOT_FINAL="$(go env GOROOT)" \ + make build \ + LDFLAGS=-buildid=${VERSION} \ + VERSION=${VERSION} \ + COMMIT=${COMMIT} \ + LEDGER_ENABLED=${LEDGER_ENABLED} + mv ./build/${APP}${OS_FILE_EXT} ${OUTDIR}/${APP}-${VERSION}-$(go env GOOS)-$(go env GOARCH)${OS_FILE_EXT} + + # This function restore the build environment variables to their + # original state. + restore_build_env +done + +# Generate and display build report. +generate_build_report +cat ${OUTDIR}/build_report diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000000..7f662a4fd9f3 --- /dev/null +++ b/.clang-format @@ -0,0 +1,116 @@ +--- +Language: Proto +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +RawStringFormats: + - Delimiters: + - pb + Language: TextProto + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... + diff --git a/.codecov.yml b/.codecov.yml index 80b53809f8bc..63c053e74b20 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -15,12 +15,15 @@ coverage: threshold: 1% # allow this much decrease on project app: target: 70% - flags: app + flags: + - app modules: target: 70% - flags: modules + flags: + - modules client: - flags: client + flags: + - client changes: false comment: @@ -46,6 +49,11 @@ ignore: - "docs" - "*.md" - "*.rst" + - "**/*.pb.go" + - "types/*.pb.go" + - "tests/*" + - "tests/**/*" + - "x/**/*.pb.go" - "x/**/test_common.go" - "scripts/" - "contrib" diff --git a/.gitattributes b/.gitattributes index 66cf55801745..f13ccebea0d7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -client/lcd/swagger-ui/* linguist-vendored +client/docs/swagger-ui/* linguist-vendored diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml deleted file mode 100644 index 0ed0b48a954f..000000000000 --- a/.github/workflows/build_and_test.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Build and test -on: - push: - branches: - - master - - next - - release/* - pull_request: - branches: - - master - - next - - release/* - -jobs: - build-and-test: - runs-on: ubuntu-18.04 - timeout-minutes: 15 - - steps: - - uses: actions/checkout@v2 - - name: Build Docker container - run: | - docker build \ - --no-cache \ - --progress plain \ - --tag fetch_cosmos_sdk:$GITHUB_SHA \ - --file ./ci.Dockerfile \ - ./ - - name: Run make test - run: | - docker run --rm fetch_cosmos_sdk:$GITHUB_SHA \ - make test diff --git a/.github/workflows/clean-artifacts.yml b/.github/workflows/clean-artifacts.yml new file mode 100644 index 000000000000..edebe53a8dad --- /dev/null +++ b/.github/workflows/clean-artifacts.yml @@ -0,0 +1,17 @@ +name: Remove old artifacts + +on: + schedule: + # Every day at 1am + - cron: '0 1 * * *' + +jobs: + remove-old-artifacts: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Remove old artifacts + uses: c-hive/gha-remove-artifacts@v1 + with: + age: '7 days' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000000..9456ebf30255 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,54 @@ +name: Build & Push +# Build & Push builds the simapp docker image on every push to master and +# and pushes the image to https://hub.docker.com/r/interchainio/simapp/tags +on: + push: + branches: + - master + tags: + - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 + - "v[0-9]+.[0-9]+.[0-9]+-rc*" # Push events to matching v*, i.e. v1.0-rc1, v20.15.10-rc5 + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + with: + fetch-depth: 0 + + - name: Prepare + id: prep + run: | + DOCKER_IMAGE=interchainio/simapp + 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}" + if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + TAGS="$TAGS,${DOCKER_IMAGE}:${VERSION}" + fi + echo ::set-output name=version::${VERSION} + echo ::set-output name=tags::${TAGS} + echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ') + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Publish to Docker Hub + uses: docker/build-push-action@v2 + with: + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.prep.outputs.tags }} diff --git a/.github/workflows/linkchecker.yml b/.github/workflows/linkchecker.yml new file mode 100644 index 000000000000..d16019158c89 --- /dev/null +++ b/.github/workflows/linkchecker.yml @@ -0,0 +1,12 @@ +name: Check Markdown links +on: + schedule: + - cron: '* */24 * * *' +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@1.0.11 + with: + folder-path: "docs" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000000..e49fa0bce8c4 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Lint +# Lint runs golangci-lint over the entire cosmos-sdk repository +# This workflow is run on every pull request and push to master +# The `golangci` will pass without running if no *.{go, mod, sum} files have been changed. +on: + pull_request: + push: + branches: + - master +jobs: + golangci: + name: golangci-lint + runs-on: ubuntu-latest + timeout-minutes: 6 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: golangci/golangci-lint-action@master + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.39 + args: --timeout 10m + github-token: ${{ secrets.github_token }} + if: env.GIT_DIFF diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml new file mode 100644 index 000000000000..6020d49aa87a --- /dev/null +++ b/.github/workflows/proto.yml @@ -0,0 +1,22 @@ +name: Protobuf +# Protobuf runs buf (https://buf.build/) lint and check-breakage +# This workflow is only run when a .proto file has been changed +on: + pull_request: + paths: + - "**.proto" +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@master + - name: lint + run: make proto-lint + # disabled until stargate land on master + # breakage: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@master + # - name: check-breakage + # run: make proto-check-breaking diff --git a/.github/workflows/release-sims.yml b/.github/workflows/release-sims.yml new file mode 100644 index 000000000000..68ca31051135 --- /dev/null +++ b/.github/workflows/release-sims.yml @@ -0,0 +1,49 @@ +name: Release Sims +# Release Sims workflow runs long-lived (multi-seed & large block size) simulations +# This workflow only runs on a pull request when the branch contains rc** (rc1/vX.X.x) +on: + pull_request: + branches: + - "rc**" + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'" + + build: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'skip-sims')" + steps: + - uses: actions/checkout@v2 + - run: | + make build + + install-runsim: + runs-on: ubuntu-latest + needs: build + steps: + - name: install runsim + run: | + export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + + test-sim-multi-seed-long: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + - name: test-sim-multi-seed-long + run: | + make test-sim-multi-seed-long diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml new file mode 100644 index 000000000000..d50ddb63a289 --- /dev/null +++ b/.github/workflows/sims.yml @@ -0,0 +1,155 @@ +name: Sims +# Sims workflow runs multiple types of simulations (nondeterminism, import-export, after-import, multi-seed-short) +# This workflow will run on all Pull Requests, if a .go, .mod or .sum file have been changed +on: + pull_request: + push: + branches: + - master + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'" + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + build: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'skip-sims')" + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - run: make build + + install-runsim: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + 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 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + + test-sim-nondeterminism: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-nondeterminism + run: | + make test-sim-nondeterminism + if: env.GIT_DIFF + + test-sim-import-export: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + SUFFIX_FILTER: | + **/**.go + go.mod + go.sum + SET_ENV_NAME_INSERTIONS: 1 + SET_ENV_NAME_LINES: 1 + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-import-export + run: | + make test-sim-import-export + if: env.GIT_DIFF + + test-sim-after-import: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + SUFFIX_FILTER: | + **/**.go + go.mod + go.sum + SET_ENV_NAME_INSERTIONS: 1 + SET_ENV_NAME_LINES: 1 + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-after-import + run: | + make test-sim-after-import + if: env.GIT_DIFF + + test-sim-multi-seed-short: + runs-on: ubuntu-latest + needs: [build, install-runsim] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + with: + SUFFIX_FILTER: | + **/**.go + go.mod + go.sum + SET_ENV_NAME_INSERTIONS: 1 + SET_ENV_NAME_LINES: 1 + - uses: actions/cache@v2.1.3 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + if: env.GIT_DIFF + - name: test-sim-multi-seed-short + run: | + make test-sim-multi-seed-short + if: env.GIT_DIFF diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 000000000000..7e1068f303a2 --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,26 @@ +name: Release +# This workflow helps with creating releases. +# This job will only be triggered when a tag (vX.X.x) is pushed +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Go + uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Unshallow + run: git fetch --prune --unshallow + - name: Create release + uses: goreleaser/goreleaser-action@v2 + with: + args: release --rm-dist --release-notes ./RELEASE_CHANGELOG.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000000..90251aae31c3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,242 @@ +name: Tests / Code Coverage +# Tests / Code Coverage workflow runs unit tests and uploads a code coverage report +# This workflow is run on pushes to master & every Pull Requests where a .go, .mod, .sum have been changed +on: + pull_request: + push: + branches: + - master +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'" + + install-tparse: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - name: install tparse + run: | + export GO111MODULE="on" && go get github.com/mfridman/tparse@v0.8.3 + - uses: actions/cache@v2.1.3 + 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: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - name: Display go version + run: go version + - uses: technote-space/get-diff-action@v4 + id: git_diff + with: + PREFIX_FILTER: | + cosmovisor + PATTERNS: | + **/**.go + go.mod + go.sum + - name: Run cosmovisor tests + run: cd cosmovisor; make + if: env.GIT_DIFF + + split-test-files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Create a file with all the pkgs + run: go list ./... > pkgs.txt + - name: Split pkgs into 4 files + run: split -d -n l/4 pkgs.txt pkgs.txt.part. + # cache multiple + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-00" + path: ./pkgs.txt.part.00 + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-01" + path: ./pkgs.txt.part.01 + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-02" + path: ./pkgs.txt.part.02 + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-03" + path: ./pkgs.txt.part.03 + + tests: + runs-on: ubuntu-latest + needs: split-test-files + strategy: + fail-fast: false + matrix: + part: ["00", "01", "02", "03"] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-${{ matrix.part }}" + if: env.GIT_DIFF + - name: test & coverage report creation + run: | + cat pkgs.txt.part.${{ matrix.part }} | xargs go test -mod=readonly -timeout 30m -coverprofile=${{ matrix.part }}profile.out -covermode=atomic -tags='norace ledger test_ledger_mock' + if: env.GIT_DIFF + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-${{ matrix.part }}-coverage" + path: ./${{ matrix.part }}profile.out + + upload-coverage-report: + runs-on: ubuntu-latest + needs: tests + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-00-coverage" + if: env.GIT_DIFF + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-01-coverage" + if: env.GIT_DIFF + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-02-coverage" + if: env.GIT_DIFF + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-03-coverage" + if: env.GIT_DIFF + - run: | + cat ./*profile.out | grep -v "mode: atomic" >> coverage.txt + if: env.GIT_DIFF + - name: filter out DONTCOVER + run: | + excludelist="$(find ./ -type f -name '*.go' | xargs grep -l 'DONTCOVER')" + excludelist+=" $(find ./ -type f -name '*.pb.go')" + excludelist+=" $(find ./ -type f -name '*.pb.gw.go')" + excludelist+=" $(find ./ -type f -path './tests/mocks/*.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/cosmos\/cosmos-sdk/g') + echo "Excluding ${filename} from coverage report..." + sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + if: env.GIT_DIFF + - uses: codecov/codecov-action@v1.2.1 + with: + file: ./coverage.txt + if: env.GIT_DIFF + + test-race: + runs-on: ubuntu-latest + needs: split-test-files + strategy: + fail-fast: false + matrix: + part: ["00", "01", "02", "03"] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2.1.3 + with: + go-version: 1.15 + - uses: technote-space/get-diff-action@v4 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-${{ matrix.part }}" + if: env.GIT_DIFF + - name: test & coverage report creation + run: | + xargs --arg-file=pkgs.txt.part.${{ matrix.part }} go test -mod=readonly -timeout 30m -race -tags='cgo ledger test_ledger_mock' + if: env.GIT_DIFF + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-${{ matrix.part }}-race-output" + path: ./${{ matrix.part }}-race-output.txt + + liveness-test: + runs-on: ubuntu-latest + timeout-minutes: 10 + 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: start localnet + run: | + make clean build-simd-linux localnet-start + if: env.GIT_DIFF + - name: test liveness + run: | + ./contrib/localnet_liveness.sh 100 5 50 localhost + if: env.GIT_DIFF + + docker-build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - name: build docker image + run: | + docker build --pull --rm -f "Dockerfile" -t simapp:latest "." diff --git a/.gitignore b/.gitignore index 8f17e23022eb..2bf18165980a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,20 +5,23 @@ *.swl *.swm *.swn -.vscode -.idea *.pyc +# private files +private[.-]* +private + # Build vendor build -tools/bin/* -examples/build/* docs/_build docs/tutorial docs/node_modules +docs/modules dist tools-stamp +buf-stamp +artifacts # Data - ideally these don't exist baseapp/data/* @@ -37,8 +40,10 @@ sim_log_file vagrant # IDE -.idea/ +.idea *.iml +.dir-locals.el +.vscode # Graphviz dependency-graph.png diff --git a/.golangci.yml b/.golangci.yml index 9d790040dbcc..34738ccf7e6f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,13 +1,16 @@ -# run: +run: + tests: false # # timeout for analysis, e.g. 30s, 5m, default is 1m # timeout: 5m linters: + disable-all: true enable: - bodyclose - deadcode - depguard - dogsled + # - errcheck - goconst - gocritic - gofmt @@ -29,9 +32,10 @@ linters: - typecheck - unconvert - unused + - unparam - misspell - disable: - - errcheck + # - wsl + - nolintlint issues: exclude-rules: @@ -47,6 +51,13 @@ issues: - text: "ST1003:" linters: - stylecheck + # FIXME: Disabled until golangci-lint updates stylecheck with this fix: + # https://github.com/dominikh/go-tools/issues/389 + - text: "ST1016:" + linters: + - stylecheck + max-issues-per-linter: 10000 + max-same-issues: 10000 linters-settings: dogsled: @@ -54,3 +65,8 @@ linters-settings: maligned: # print struct with more effective memory layout or not, false by default suggest-new: true + nolintlint: + allow-unused: false + allow-leading-space: true + require-explanation: false + require-specific: false diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 000000000000..4d33ce325014 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,27 @@ +--- +project_name: cosmos-sdk + +release: + github: + owner: cosmos + name: cosmos-sdk + +builds: + - skip: true + +archives: + - format: tar.gz + wrap_in_directory: true + format_overrides: + - goos: windows + format: zip + name_template: "{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + files: + - LICENSE + - README.md + +snapshot: + name_template: SNAPSHOT-{{ .Commit }} + +changelog: + skip: true diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 000000000000..fbf9d7015ae8 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,10 @@ +pull_request_rules: + - name: automerge to master with label automerge and branch protection passing + conditions: + - "#approved-reviews-by>1" + - base=master + - label=automerge + actions: + merge: + method: squash + strict: true diff --git a/CHANGELOG.md b/CHANGELOG.md index e56dae0de232..c7bd85ca42d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,16 +29,609 @@ Types of changes (Stanzas): "Client Breaking" for breaking CLI commands and REST routes used by end-users. "API Breaking" for breaking exported APIs used by developers building on SDK. "State Machine Breaking" for any changes that result in a different AppState given same genesisState and txList. - Ref: https://keepachangelog.com/en/1.0.0/ --> # Changelog -## [Unreleased] +## [v0.42.5](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.5) - 2021-05-18 + +### Bug Fixes + +* [\#9235](https://github.com/cosmos/cosmos-sdk/pull/9235) CreateMembershipProof/CreateNonMembershipProof now returns an error +if input key is empty, or input data contains empty key. +* [\#9108](https://github.com/cosmos/cosmos-sdk/pull/9108) Fixed the bug with querying multisig account, which is not showing threshold and public_keys. +* [\#9345](https://github.com/cosmos/cosmos-sdk/pull/9345) Fix ARM support. +* [\#9040](https://github.com/cosmos/cosmos-sdk/pull/9040) Fix ENV variables binding to CLI flags for client config. + +### Features + +* [\#8953](https://github.com/cosmos/cosmos-sdk/pull/8953) Add the `config` CLI subcommand back to the SDK, which saves client-side configuration in a `client.toml` file. + + +## [v0.42.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.4) - 2021-04-08 + +### Client Breaking Changes + +* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) By default, the `tx sign` and `tx sign-batch` CLI commands use SIGN_MODE_DIRECT to sign transactions for local pubkeys. For multisigs and ledger keys, the default LEGACY_AMINO_JSON is used. + +### Bug Fixes + +* (gRPC) [\#9015](https://github.com/cosmos/cosmos-sdk/pull/9015) Fix invalid status code when accessing gRPC endpoints. +* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) Fixed the bug that caused the `gentx` command to fail for Ledger keys. + +### Improvements + +* [\#9081](https://github.com/cosmos/cosmos-sdk/pull/9081) Upgrade Tendermint to v0.34.9 that includes a security issue fix for Tendermint light clients. + +## [v0.42.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.3) - 2021-03-24 + +This release fixes a security vulnerability identified in x/bank. + +## [v0.42.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.2) - 2021-03-19 + +### Improvements + +* (grpc) [\#8815](https://github.com/cosmos/cosmos-sdk/pull/8815) Add orderBy parameter to `TxsByEvents` endpoint. +* (cli) [\#8826](https://github.com/cosmos/cosmos-sdk/pull/8826) Add trust to macOS Keychain for caller app by default. +* (store) [\#8811](https://github.com/cosmos/cosmos-sdk/pull/8811) store/cachekv: use typed types/kv.List instead of container/list.List + +### Bug Fixes + +* (crypto) [\#8841](https://github.com/cosmos/cosmos-sdk/pull/8841) Fix legacy multisig amino marshaling, allowing migrations to work between v0.39 and v0.40+. +* (cli) [\#8873](https://github.com/cosmos/cosmos-sdk/pull/8873) add --output-document to multisign-batch. + +## [v0.42.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.1) - 2021-03-10 + +This release fixes security vulnerability identified in the simapp. + + +## [v0.42.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.0) - 2021-03-08 + +**IMPORTANT**: This release contains an important security fix for all non Cosmos Hub chains running Stargate version of the Cosmos SDK (>0.40). Non-hub chains should not be using any version of the SDK in the v0.40.x or v0.41.x release series. See [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) for more details. + +### Improvements + +* (x/ibc) [\#8624](https://github.com/cosmos/cosmos-sdk/pull/8624) Emit full header in IBC UpdateClient message. +* (x/crisis) [\#8621](https://github.com/cosmos/cosmos-sdk/issues/8621) crisis invariants names now print to loggers. + +### Bug fixes + +* (x/evidence) [\#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) Fix bech32 prefix in evidence validator address conversion +* (x/gov) [\#8806](https://github.com/cosmos/cosmos-sdk/issues/8806) Fix q gov proposals command's mishandling of the --status parameter's values. + +## [v0.41.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.3) - 2021-03-02 + +### Features + +* [\#7787](https://github.com/cosmos/cosmos-sdk/pull/7787) Add multisign-batch command. + +### Bug fixes + +* [\#8730](https://github.com/cosmos/cosmos-sdk/pull/8730) Allow REST endpoint to query txs with multisig addresses. +* [\#8680](https://github.com/cosmos/cosmos-sdk/issues/8680) Fix missing timestamp in GetTxsEvent response [\#8732](https://github.com/cosmos/cosmos-sdk/pull/8732). +* [\#8681](https://github.com/cosmos/cosmos-sdk/issues/8681) Fix missing error message when calling GetTxsEvent [\#8732](https://github.com/cosmos/cosmos-sdk/pull/8732) +* (server) [\#8641](https://github.com/cosmos/cosmos-sdk/pull/8641) Fix Tendermint and application configuration reading from file +* (client/keys) [\#8639] (https://github.com/cosmos/cosmos-sdk/pull/8639) Fix keys migrate for mulitisig, offline, and ledger keys. The migrate command now takes a positional old_home_dir argument. + +### Improvements + +* (store/cachekv), (x/bank/types) [\#8719](https://github.com/cosmos/cosmos-sdk/pull/8719) algorithmically fix pathologically slow code +* [\#8701](https://github.com/cosmos/cosmos-sdk/pull/8701) Upgrade tendermint v0.34.8. +* [\#8714](https://github.com/cosmos/cosmos-sdk/pull/8714) Allow accounts to have a balance of 0 at genesis. + +## [v0.41.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.3) - 2021-02-18 + +### Bug Fixes + +* [\#8617](https://github.com/cosmos/cosmos-sdk/pull/8617) Fix build failures caused by a small API breakage introduced in tendermint v0.34.7. + +## [v0.41.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.2) - 2021-02-18 + +### Improvements + +* Bump tendermint dependency to v0.34.7. + +## [v0.41.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.1) - 2021-02-17 + +### Bug Fixes + +* (grpc) [\#8549](https://github.com/cosmos/cosmos-sdk/pull/8549) Make gRPC requests go through ABCI and disallow concurrency. +* (x/staking) [\#8546](https://github.com/cosmos/cosmos-sdk/pull/8546) Fix caching bug where concurrent calls to GetValidator could cause a node to crash +* (server) [\#8481](https://github.com/cosmos/cosmos-sdk/pull/8481) Don't create files when running `{appd} tendermint show-*` subcommands. +* (client/keys) [\#8436](https://github.com/cosmos/cosmos-sdk/pull/8436) Fix keybase->keyring keys migration. +* (crypto/hd) [\#8607](https://github.com/cosmos/cosmos-sdk/pull/8607) Make DerivePrivateKeyForPath error and not panic on trailing slashes. + +### Improvements + +* (x/ibc) [\#8458](https://github.com/cosmos/cosmos-sdk/pull/8458) Add `packet_connection` attribute to ibc events to enable relayer filtering +* [\#8396](https://github.com/cosmos/cosmos-sdk/pull/8396) Add support for ARM platform +* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Aditional client denom metadata validation for `base` and `display` denoms. +* (codec/types) [\#8605](https://github.com/cosmos/cosmos-sdk/pull/8605) Avoid unnecessary allocations for NewAnyWithCustomTypeURL on error. + +## [v0.41.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.41.0) - 2021-01-26 + +### State Machine Breaking + +* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON for IBC messages in order to support Ledger text signing. +* (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 + +* (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 +* (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 + +## [v0.40.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.1) - 2021-01-19 + +### Improvements + +* (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 + +* [\#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. + + +## [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. + +### Client Breaking Changes + +* __Modules__ + +* __CLI__ + * (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`. + * (x/auth) [\#5702](https://github.com/cosmos/cosmos-sdk/pull/5702) The `x/auth` querier route has changed from `"acc"` to `"auth"`. + * (x/bank) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) The `/bank/balances/{address}` endpoint now returns all account balances or a single balance by denom when the `denom` query parameter is present. + * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Remove CLI and REST handlers for querying `x/evidence` parameters. + * (x/gov) [#6295](https://github.com/cosmos/cosmos-sdk/pull/6295) Fix typo in querying governance params. +* __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. + * (x/auth/vesting) [\#6859](https://github.com/cosmos/cosmos-sdk/pull/6859) Custom JSON marshaling of vesting accounts was removed. Vesting accounts are now marshaled using their default proto or amino JSON representation. + * (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()`. + * (client/keys) [\#5820](https://github.com/cosmos/cosmos-sdk/pull/5820/) Removed method CloseDB from Keybase interface. + * (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 + * (crypto) [\#5880](https://github.com/cosmos/cosmos-sdk/pull/5880) Merge `crypto/keys/mintkey` into `crypto`. + * (crypto/hd) [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) `crypto/keys/hd` moved to `crypto/hd`. + * (crypto/keyring): + * [\#5866](https://github.com/cosmos/cosmos-sdk/pull/5866) Rename `crypto/keys/` to `crypto/keyring/`. + * [\#5904](https://github.com/cosmos/cosmos-sdk/pull/5904) `Keybase` -> `Keyring` interfaces migration. `LegacyKeybase` interface is added in order + to guarantee limited backward compatibility with the old Keybase interface for the sole purpose of migrating keys across the new keyring backends. `NewLegacy` + constructor is provided [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) to allow for smooth migration of keys from the legacy LevelDB based implementation + to new keyring backends. Plus, the package and the new keyring no longer depends on the sdk.Config singleton. Please consult the [package documentation](https://github.com/cosmos/cosmos-sdk/tree/master/crypto/keyring/doc.go) for more + 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` + determines which committed heights are flushed to disk and `SnapshotEvery` determines which of these + heights are kept after pruning. The `IsValid` method should be called whenever using these options. Methods + `SnapshotVersion` and `FlushVersion` accept a version arugment and determine if the version should be + flushed to disk or kept as a snapshot. Note, `KeepRecent` is automatically inferred from the options + and provided directly the IAVL store. + * (types) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Refactored `AppModuleBasic` and `AppModuleGenesis` + to now accept a `codec.JSONMarshaler` for modular serialization of genesis state. + * (types/rest) [\#5779](https://github.com/cosmos/cosmos-sdk/pull/5779) Drop unused Parse{Int64OrReturnBadRequest,QueryParamBool}() functions. +* __Modules__ + * (modules) [\#7243](https://github.com/cosmos/cosmos-sdk/pull/7243) Rename `RegisterCodec` to `RegisterLegacyAminoCodec` and `codec.New()` is now renamed to `codec.NewLegacyAmino()` + * (modules) [\#6564](https://github.com/cosmos/cosmos-sdk/pull/6564) Constant `DefaultParamspace` is removed from all modules, use ModuleName instead. + * (modules) [\#5989](https://github.com/cosmos/cosmos-sdk/pull/5989) `AppModuleBasic.GetTxCmd` now takes a single `CLIContext` parameter. + * (modules) [\#5664](https://github.com/cosmos/cosmos-sdk/pull/5664) Remove amino `Codec` from simulation `StoreDecoder`, which now returns a function closure in order to unmarshal the key-value pairs. + * (modules) [\#5555](https://github.com/cosmos/cosmos-sdk/pull/5555) Move `x/auth/client/utils/` types and functions to `x/auth/client/`. + * (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Move account balance logic and APIs from `x/auth` to `x/bank`. + * (modules) [\#6326](https://github.com/cosmos/cosmos-sdk/pull/6326) `AppModuleBasic.GetQueryCmd` now takes a single `client.Context` parameter. + * (modules) [\#6336](https://github.com/cosmos/cosmos-sdk/pull/6336) `AppModuleBasic.RegisterQueryService` method was added to support gRPC queries, and `QuerierRoute` and `NewQuerierHandler` were deprecated. + * (modules) [\#6311](https://github.com/cosmos/cosmos-sdk/issues/6311) Remove `alias.go` usage + * (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` + and returns a new `Route` type. + * (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+ + + +### State Machine Breaking + +* __General__ + * (client) [\#7268](https://github.com/cosmos/cosmos-sdk/pull/7268) / [\#7147](https://github.com/cosmos/cosmos-sdk/pull/7147) Introduce new protobuf based PubKeys, and migrate PubKey in BaseAccount to use this new protobuf based PubKey format + +* __Modules__ + * (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Separate balance from accounts per ADR 004. + * Account balances are now persisted and retrieved via the `x/bank` module. + * Vesting account interface has been modified to account for changes. + * Callers to `NewBaseVestingAccount` are responsible for verifying account balance in relation to + the original vesting amount. + * The `SendKeeper` and `ViewKeeper` interfaces in `x/bank` have been modified to account for changes. + * (x/auth) [\#5533](https://github.com/cosmos/cosmos-sdk/pull/5533) Migrate the `x/auth` module to use Protocol Buffers for state + serialization instead of Amino. + * The `BaseAccount.PubKey` field is now represented as a Bech32 string instead of a `crypto.Pubkey`. + * `NewBaseAccountWithAddress` now returns a reference to a `BaseAccount`. + * The `x/auth` module now accepts a `Codec` interface which extends the `codec.Marshaler` interface by + requiring a concrete codec to know how to serialize accounts. + * The `AccountRetriever` type now accepts a `Codec` in its constructor in order to know how to + serialize accounts. + * (x/bank) [\#6518](https://github.com/cosmos/cosmos-sdk/pull/6518) Support for global and per-denomination send enabled flags. + * Existing send_enabled global flag has been moved into a Params structure as `default_send_enabled`. + * An array of: `{denom: string, enabled: bool}` is added to bank Params to support per-denomination override of global default value. + * (x/distribution) [\#5610](https://github.com/cosmos/cosmos-sdk/pull/5610) Migrate the `x/distribution` module to use Protocol Buffers for state + serialization instead of Amino. The exact codec used is `codec.HybridCodec` which utilizes Protobuf for binary encoding and Amino + for JSON encoding. + * `ValidatorHistoricalRewards.ReferenceCount` is now of types `uint32` instead of `uint16`. + * `ValidatorSlashEvents` is now a struct with `slashevents`. + * `ValidatorOutstandingRewards` is now a struct with `rewards`. + * `ValidatorAccumulatedCommission` is now a struct with `commission`. + * The `Keeper` constructor now takes a `codec.Marshaler` instead of a concrete Amino codec. This exact type + provided is specified by `ModuleCdc`. + * (x/evidence) [\#5634](https://github.com/cosmos/cosmos-sdk/pull/5634) Migrate the `x/evidence` module to use Protocol Buffers for state + serialization instead of Amino. + * The `internal` sub-package has been removed in order to expose the types proto file. + * The module now accepts a `Codec` interface which extends the `codec.Marshaler` interface by + requiring a concrete codec to know how to serialize `Evidence` types. + * The `MsgSubmitEvidence` message has been removed in favor of `MsgSubmitEvidenceBase`. The application-level + codec must now define the concrete `MsgSubmitEvidence` type which must implement the module's `MsgSubmitEvidence` + interface. + * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Remove parameters from `x/evidence` genesis and module state. The `x/evidence` module now solely uses Tendermint consensus parameters to determine of evidence is valid or not. + * (x/gov) [\#5737](https://github.com/cosmos/cosmos-sdk/pull/5737) Migrate the `x/gov` module to use Protocol + Buffers for state serialization instead of Amino. + * `MsgSubmitProposal` will be removed in favor of the application-level proto-defined `MsgSubmitProposal` which + implements the `MsgSubmitProposalI` interface. Applications should extend the `NewMsgSubmitProposalBase` type + to define their own concrete `MsgSubmitProposal` types. + * The module now accepts a `Codec` interface which extends the `codec.Marshaler` interface by + requiring a concrete codec to know how to serialize `Proposal` types. + * (x/mint) [\#5634](https://github.com/cosmos/cosmos-sdk/pull/5634) Migrate the `x/mint` module to use Protocol Buffers for state + serialization instead of Amino. + * The `internal` sub-package has been removed in order to expose the types proto file. + * (x/slashing) [\#5627](https://github.com/cosmos/cosmos-sdk/pull/5627) Migrate the `x/slashing` module to use Protocol Buffers for state + serialization instead of Amino. The exact codec used is `codec.HybridCodec` which utilizes Protobuf for binary encoding and Amino + for JSON encoding. + * The `Keeper` constructor now takes a `codec.Marshaler` instead of a concrete Amino codec. This exact type + provided is specified by `ModuleCdc`. + * (x/staking) [\#6844](https://github.com/cosmos/cosmos-sdk/pull/6844) Validators are now inserted into the unbonding queue based on their unbonding time and height. The relevant keeper APIs are modified to reflect these changes by now also requiring a height. + * (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 + falling below their minimum self-delegation and never having been bonded. The validator may immediately unjail once they've met their minimum self-delegation. + * (x/staking) [\#5600](https://github.com/cosmos/cosmos-sdk/pull/5600) Migrate the `x/staking` module to use Protocol Buffers for state + serialization instead of Amino. The exact codec used is `codec.HybridCodec` which utilizes Protobuf for binary encoding and Amino + for JSON encoding. + * `BondStatus` is now of type `int32` instead of `byte`. + * Types of `int16` in the `Params` type are now of type `int32`. + * 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. + * The `internal` sub-package has been removed in order to expose the types proto file. + * The `x/supply` module now accepts a `Codec` interface which extends the `codec.Marshaler` interface by + requiring a concrete codec to know how to serialize `SupplyI` types. + * The `SupplyI` interface has been modified to no longer return `SupplyI` on methods. Instead the + concrete type's receiver should modify the type. + * (x/upgrade) [\#5659](https://github.com/cosmos/cosmos-sdk/pull/5659) Migrate the `x/upgrade` module to use Protocol + Buffers for state serialization instead of Amino. + * The `internal` sub-package has been removed in order to expose the types proto file. + * The `x/upgrade` module now accepts a `codec.Marshaler` interface. + +### Features + +* __Baseapp / Client / REST__ + * (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}`. + * (tx) [\#6089](https://github.com/cosmos/cosmos-sdk/pull/6089) Transactions can now have a `TimeoutHeight` set which allows the transaction to be rejected if it's committed at a height greater than the timeout. + * (rest) [\#6167](https://github.com/cosmos/cosmos-sdk/pull/6167) Support `max-body-bytes` CLI flag for the REST service. + * (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` + * `store.Query` now only returns chained `ics23.CommitmentProof` wrapped in `merkle.Proof` + * `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 + * [ICS 004 - Channel and Packet Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-004-channel-and-packet-semantics) subpackage + * [ICS 005 - Port Allocation](https://github.com/cosmos/ics/blob/master/spec/ics-005-port-allocation) subpackage + * [ICS 006 - Solo Machine Client](https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client) subpackage + * [ICS 007 - Tendermint Client](https://github.com/cosmos/ics/blob/master/spec/ics-007-tendermint-client) subpackage + * [ICS 009 - Loopback Client](https://github.com/cosmos/ics/tree/master/spec/ics-009-loopback-client) subpackage + * [ICS 020 - Fungible Token Transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer) subpackage + * [ICS 023 - Vector Commitments](https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments) subpackage + * [ICS 024 - Host State Machine Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements) subpackage + * (x/ibc) [\#6374](https://github.com/cosmos/cosmos-sdk/pull/6374) ICS-23 Verify functions will now accept and verify ics23 CommitmentProofs exclusively + * (x/params) [\#6005](https://github.com/cosmos/cosmos-sdk/pull/6005) Add new CLI command for querying raw x/params parameters by subspace and key. + +### Bug Fixes + +* __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 + types (eg. keys) to the `auth` module internal amino codec. + * (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 +falling below their minimum self-delegation and never having been bonded. The validator may immediately unjail once they've met their minimum self-delegation. +* __General__ + * (types) [\#7038](https://github.com/cosmos/cosmos-sdk/issues/7038) Fix infinite looping of `ApproxRoot` by including a hard-coded maximum iterations limit of 100. + * (types) [\#7084](https://github.com/cosmos/cosmos-sdk/pull/7084) Fix panic when calling `BigInt()` on an uninitialized `Int`. + * (simulation) [\#7129](https://github.com/cosmos/cosmos-sdk/issues/7129) Fix support for custom `Account` and key types on auth's simulation. + + +### Improvements +* __Baseapp / Client / REST__ + * (baseapp) [\#6186](https://github.com/cosmos/cosmos-sdk/issues/6186) Support emitting events during `AnteHandler` execution. + * (baseapp) [\#6053](https://github.com/cosmos/cosmos-sdk/pull/6053) Customizable panic recovery handling added for `app.runTx()` method (as proposed in the [ADR 22](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-022-custom-panic-handling.md)). Adds ability for developers to register custom panic handlers extending standard ones. + * (client) [\#5810](https://github.com/cosmos/cosmos-sdk/pull/5810) Added a new `--offline` flag that allows commands to be executed without an + 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. + * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Tendermint Consensus parameters can now be changed via parameter change proposals through `x/gov`. + * (x/evidence) [\#5961](https://github.com/cosmos/cosmos-sdk/issues/5961) Add `StoreDecoder` simulation for evidence module. + * (x/ibc) [\#5948](https://github.com/cosmos/cosmos-sdk/issues/5948) Add `InitGenesis` and `ExportGenesis` functions for `ibc` module. + * (x/ibc-transfer) [\#6871](https://github.com/cosmos/cosmos-sdk/pull/6871) Implement [ADR 001 - Coin Source Tracing](./docs/architecture/adr-001-coin-source-tracing.md). + * (x/staking) [\#6059](https://github.com/cosmos/cosmos-sdk/pull/6059) Updated `HistoricalEntries` parameter default to 100. + * (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__ + * (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) + * Added `Validate` function that returns a descriptive error + * (types) [\#5581](https://github.com/cosmos/cosmos-sdk/pull/5581) Add convenience functions {,Must}Bech32ifyAddressBytes. + * (types/module) [\#5724](https://github.com/cosmos/cosmos-sdk/issues/5724) The `types/module` package does no longer depend on `x/simulation`. + * (types) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) IBC additions: + * `Coin` denomination max lenght has been increased to 32. + * Added `CapabilityKey` alias for `StoreKey` to match IBC spec. + * (types/rest) [\#5900](https://github.com/cosmos/cosmos-sdk/pull/5900) Add Check*Error function family to spare developers from replicating tons of boilerplate code. + * (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 + +### Client Breaking + +* (x/auth) [\#6861](https://github.com/cosmos/cosmos-sdk/pull/6861) Remove public key Bech32 encoding for all account types for JSON serialization, instead relying on direct Amino encoding. In addition, JSON serialization utilizes Amino instead of the Go stdlib, so integers are treated as strings. + +### Improvements + +* (client) [\#6853](https://github.com/cosmos/cosmos-sdk/pull/6853) Add --unsafe-cors flag. + +## [v0.39.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.0) - 2020-07-20 + +### Improvements + +* (deps) Bump IAVL version to [v0.14.0](https://github.com/cosmos/iavl/releases/tag/v0.14.0) +* (client) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) `CLIContext` additions: + * Introduce `QueryABCI` that returns the full `abci.ResponseQuery` with inclusion Merkle proofs. + * Added `prove` flag for Merkle proof verification. +* (x/staking) [\#6791)](https://github.com/cosmos/cosmos-sdk/pull/6791) Close {UBDQueue,RedelegationQueu}Iterator once used. + +### API Breaking Changes + +* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and `Result` from the execution. + +### Client Breaking Changes + +* (x/auth) [\#6745](https://github.com/cosmos/cosmos-sdk/issues/6745) Remove BaseAccount's custom JSON {,un}marshalling. + +### Bug Fixes + +* (store) [\#6475](https://github.com/cosmos/cosmos-sdk/pull/6475) Revert IAVL pruning functionality introduced in +[v0.13.0](https://github.com/cosmos/iavl/releases/tag/v0.13.0), +where the IAVL no longer keeps states in-memory in which it flushes periodically. IAVL now commits and +flushes every state to disk as it did pre-v0.13.0. The SDK's multi-store will track and ensure the proper +heights are pruned. The operator can set the pruning options via a `pruning` config via the CLI or +through `app.toml`. The `pruning` flag exposes `default|everything|nothing|custom` as options -- +see docs for further details. If the operator chooses `custom`, they may provide granular pruning +options `pruning-keep-recent`, `pruning-keep-every`, and `pruning-interval`. The former two options +dictate how many recent versions are kept on disk and the offset of what versions are kept after that +respectively, and the latter defines the height interval in which versions are deleted in a batch. +**Note, there are some client-facing API breaking changes with regard to IAVL, stores, and pruning settings.** +* (x/distribution) [\#6210](https://github.com/cosmos/cosmos-sdk/pull/6210) Register `MsgFundCommunityPool` in distribution amino codec. +* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent `ChainAnteDecorators()` from panicking when empty `AnteDecorator` slice is supplied. +* (baseapp) [\#6306](https://github.com/cosmos/cosmos-sdk/issues/6306) Prevent events emitted by the antehandler from being persisted between transactions. +* (client/keys) [\#5091](https://github.com/cosmos/cosmos-sdk/issues/5091) `keys parse` does not honor client app's configuration. +* (x/bank) [\#6674](https://github.com/cosmos/cosmos-sdk/pull/6674) Create account if recipient does not exist on handing `MsgMultiSend`. +* (x/auth) [\#6287](https://github.com/cosmos/cosmos-sdk/pull/6287) Fix nonce stuck when sending multiple transactions from an account in a same block. + +## [v0.38.5] - 2020-07-02 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.33.6](https://github.com/tendermint/tendermint/releases/tag/v0.33.6). + +## [v0.38.4] - 2020-05-21 + +### Bug Fixes + +* (x/auth) [\#5950](https://github.com/cosmos/cosmos-sdk/pull/5950) Fix `IncrementSequenceDecorator` to use is `IsReCheckTx` instead of `IsCheckTx` to allow account sequence incrementing. ## [v0.38.3] - 2020-04-09 +### Improvements + * (tendermint) Bump Tendermint version to [v0.33.3](https://github.com/tendermint/tendermint/releases/tag/v0.33.3). ## [v0.38.2] - 2020-03-25 @@ -48,8 +641,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (baseapp) [\#5718](https://github.com/cosmos/cosmos-sdk/pull/5718) Remove call to `ctx.BlockGasMeter` during failed message validation which resulted in a panic when the tx execution mode was `CheckTx`. * (x/genutil) [\#5775](https://github.com/cosmos/cosmos-sdk/pull/5775) Fix `ExportGenesis` in `x/genutil` to export default genesis state (`[]`) instead of `null`. * (client) [\#5618](https://github.com/cosmos/cosmos-sdk/pull/5618) Fix crash on the client when the verifier is not set. -* (crypto/keys/mintkey) [\#5823](https://github.com/cosmos/cosmos-sdk/pull/5823) fix errors handling in UnarmorPubKeyBytes (underlying armoring function's return error was not being checked). -* (x/distribution) [\#5620](https://github.com/cosmos/cosmos-sdk/pull/5620) Fix nil pointer deref in distribution tax/rewward validation helpers. +* (crypto/keys/mintkey) [\#5823](https://github.com/cosmos/cosmos-sdk/pull/5823) fix errors handling in `UnarmorPubKeyBytes` (underlying armoring function's return error was not being checked). +* (x/distribution) [\#5620](https://github.com/cosmos/cosmos-sdk/pull/5620) Fix nil pointer deref in distribution tax/reward validation helpers. ### Improvements @@ -266,6 +859,7 @@ generalized genesis accounts through the `GenesisAccount` interface. * (sdk) [\#4758](https://github.com/cosmos/cosmos-sdk/issues/4758) update `x/genaccounts` to match module spec * (simulation) [\#4824](https://github.com/cosmos/cosmos-sdk/issues/4824) `PrintAllInvariants` flag will print all failed invariants * (simulation) [\#4490](https://github.com/cosmos/cosmos-sdk/issues/4490) add `InitialBlockHeight` flag to resume a simulation from a given block + * Support exporting the simulation stats to a given JSON file * (simulation) [\#4847](https://github.com/cosmos/cosmos-sdk/issues/4847), [\#4838](https://github.com/cosmos/cosmos-sdk/pull/4838) and [\#4869](https://github.com/cosmos/cosmos-sdk/pull/4869) `SimApp` and simulation refactors: * Implement `SimulationManager` for executing modules' simulation functionalities in a modularized way @@ -302,14 +896,78 @@ to detail this new feature and how state transitions occur. ### Bug Fixes -* (rest) [\#5508](https://github.com/cosmos/cosmos-sdk/pull/5508) Fix `x/distribution` endpoints to properly return height in the response. -* (x/genutil) [\#5499](https://github.com/cosmos/cosmos-sdk/pull/) Ensure `DefaultGenesis` returns valid and non-nil default genesis state. * (client) [\#5303](https://github.com/cosmos/cosmos-sdk/issues/5303) Fix ignored error in tx generate only mode. * (cli) [\#4763](https://github.com/cosmos/cosmos-sdk/issues/4763) Fix flag `--min-self-delegation` for staking `EditValidator` * (keys) Fix ledger custom coin type support bug. * (x/gov) [\#5107](https://github.com/cosmos/cosmos-sdk/pull/5107) Sum validator operator's all voting power when tally votes * (rest) [\#5212](https://github.com/cosmos/cosmos-sdk/issues/5212) Fix pagination in the `/gov/proposals` handler. +## [v0.37.14] - 2020-08-12 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.32.13](https://github.com/tendermint/tendermint/releases/tag/v0.32.13). + + +## [v0.37.13] - 2020-06-03 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.32.12](https://github.com/tendermint/tendermint/releases/tag/v0.32.12). +* (cosmos-ledger-go) Bump Cosmos Ledger Wallet library version to [v0.11.1](https://github.com/cosmos/ledger-cosmos-go/releases/tag/v0.11.1). + +## [v0.37.12] - 2020-05-05 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.32.11](https://github.com/tendermint/tendermint/releases/tag/v0.32.11). + +## [v0.37.11] - 2020-04-22 + +### Bug Fixes + +* (x/staking) [\#6021](https://github.com/cosmos/cosmos-sdk/pull/6021) --trust-node's false default value prevents creation of the genesis transaction. + +## [v0.37.10] - 2020-04-22 + +### Bug Fixes + +* (client/context) [\#5964](https://github.com/cosmos/cosmos-sdk/issues/5964) Fix incorrect instantiation of tmlite verifier when --trust-node is off. + +## [v0.37.9] - 2020-04-09 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.32.10](https://github.com/tendermint/tendermint/releases/tag/v0.32.10). + +## [v0.37.8] - 2020-03-11 + +### Bug Fixes + +* (rest) [\#5508](https://github.com/cosmos/cosmos-sdk/pull/5508) Fix `x/distribution` endpoints to properly return height in the response. +* (x/genutil) [\#5499](https://github.com/cosmos/cosmos-sdk/pull/) Ensure `DefaultGenesis` returns valid and non-nil default genesis state. +* (x/genutil) [\#5775](https://github.com/cosmos/cosmos-sdk/pull/5775) Fix `ExportGenesis` in `x/genutil` to export default genesis state (`[]`) instead of `null`. +* (genesis) [\#5086](https://github.com/cosmos/cosmos-sdk/issues/5086) Ensure `gentxs` are always an empty array instead of `nil`. + +### Improvements + +* (rest) [\#5648](https://github.com/cosmos/cosmos-sdk/pull/5648) Enhance /txs usability: + * Add `tx.minheight` key to filter transaction with an inclusive minimum block height + * Add `tx.maxheight` key to filter transaction with an inclusive maximum block height + +## [v0.37.7] - 2020-02-10 + +### Improvements + +* (modules) [\#5597](https://github.com/cosmos/cosmos-sdk/pull/5597) Add `amount` event attribute to the `complete_unbonding` +and `complete_redelegation` events that reflect the total balances of the completed unbondings and redelegations +respectively. + +### Bug Fixes + +* (x/gov) [\#5622](https://github.com/cosmos/cosmos-sdk/pull/5622) Track any events emitted from a proposal's handler upon successful execution. +* (x/bank) [\#5531](https://github.com/cosmos/cosmos-sdk/issues/5531) Added missing amount event to MsgMultiSend, emitted for each output. + ## [v0.37.6] - 2020-01-21 ### Improvements @@ -515,6 +1173,7 @@ that error is that the account doesn't exist. * (simulation) PrintAllInvariants flag will print all failed invariants * (simulation) Add `InitialBlockHeight` flag to resume a simulation from a given block * (simulation) [\#4670](https://github.com/cosmos/cosmos-sdk/issues/4670) Update simulation statistics to JSON format + - Support exporting the simulation stats to a given JSON file * [\#4775](https://github.com/cosmos/cosmos-sdk/issues/4775) Refactor CI config * Upgrade IAVL to v0.12.4 @@ -1120,7 +1779,8 @@ BREAKING CHANGES FEATURES * Gaia REST API - * [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface + +* [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface * Gaia CLI (`gaiacli`) * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying @@ -2905,6 +3565,9 @@ BUG FIXES: [v0.38.2]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.2 [v0.38.1]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.1 [v0.38.0]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.0 +[v0.37.9]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.9 +[v0.37.8]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.8 +[v0.37.7]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.7 [v0.37.6]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.6 [v0.37.5]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.5 [v0.37.4]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.37.4 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a139c5ee031d..c9f9cc2405e0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at adrian@tendermint.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at community@interchain.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34789be8e24a..46780a54e99b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,7 @@ - [Updating Documentation](#updating-documentation) - [Forking](#forking) - [Dependencies](#dependencies) + - [Protobuf](#protobuf) - [Testing](#testing) - [Branching Model and Release](#branching-model-and-release) - [PR Targeting](#pr-targeting) @@ -14,6 +15,7 @@ - [Pull Merge Procedure](#pull-merge-procedure) - [Release Procedure](#release-procedure) - [Point Release Procedure](#point-release-procedure) + - [Code Owner Membership](#code-owner-membership) Thank you for considering making contributions to Cosmos-SDK and related repositories! @@ -35,8 +37,8 @@ contributors, the general procedure for contributing has been established: 4. Follow standard Github best practices: fork the repo, branch from the HEAD of `master`, make some commits, and submit a PR to `master` - For core developers working within the cosmos-sdk repo, to ensure a clear - ownership of branches, branches must be named with the convention - `{moniker}/{issue#}-branch-name` + ownership of branches, branches must be named with the convention + `{moniker}/{issue#}-branch-name` 5. Be sure to submit the PR in `Draft` mode submit your PR early, even if it's incomplete as this indicates to the community you're working on something and allows them to provide comments early in the development process @@ -56,9 +58,11 @@ Other notes: - Looking for a good place to start contributing? How about checking out some [good first issues](https://github.com/cosmos/cosmos-sdk/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -- Please make sure to use `gofmt` before every commit - the easiest way to do - this is have your editor run it for you upon saving a file. Additionally - please ensure that your code is lint compliant by running `make lint` +- Please make sure to run `make format` before every commit - the easiest way + to do this is have your editor run it for you upon saving a file. Additionally + please ensure that your code is lint compliant by running `golangci-lint run`. + A convenience git `pre-commit` hook that runs the formatters automatically + before each commit is available in the `contrib/githooks/` directory. ## Architecture Decision Records (ADR) @@ -124,7 +128,7 @@ Please don't make Pull Requests from `master`. ## Dependencies -We use [Go 1.11 Modules](https://github.com/golang/go/wiki/Modules) to manage +We use [Go 1.14 Modules](https://github.com/golang/go/wiki/Modules) to manage dependency versions. The master branch of every Cosmos repository should just build with `go get`, @@ -134,6 +138,35 @@ get away with telling people they can just `go get` our software. Since some dependencies are not under our control, a third party may break our build, in which case we can fall back on `go mod tidy -v`. +## Protobuf + +We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use in Cosmos-SDK. + +For determinstic behavior around Protobuf tooling, everything is containerized using Docker. Make sure to have Docker installed on your machine, or head to [Docker's website](https://docs.docker.com/get-docker/) to install it. + +For formatting code in `.proto` files, you can run `make proto-format` command. + +For linting and checking breaking changes, we use [buf](https://buf.build/). You can use the commands `make proto-lint` and `make proto-check-breaking` to respectively lint your proto files and check for breaking changes. + +To generate the protobuf stubs, you can run `make proto-gen`. + +We also added the `make proto-all` command to run all the above commands sequentially. + +In order for imports to properly compile in your IDE, you may need to manually set your protobuf path in your IDE's workspace settings/config. + +For example, in vscode your `.vscode/settings.json` should look like: + +``` +{ + "protoc": { + "options": [ + "--proto_path=${workspaceRoot}/proto", + "--proto_path=${workspaceRoot}/third_party/proto" + ] + } +} +``` + ## Testing All repos should be hooked up to [CircleCI](https://circleci.com/). @@ -185,7 +218,7 @@ only pull requests targeted directly against master. ### Development Procedure - the latest state of development is on `master` -- `master` must never fail `make test` or `make test_cli` +- `master` must never fail `make lint test test-race` - `master` should not fail `make lint` - no `--force` onto `master` (except when reverting a broken commit, which should seldom happen) - create a development branch either on github.com/cosmos/cosmos-sdk, or your fork (using `git remote add origin`) @@ -194,7 +227,7 @@ only pull requests targeted directly against master. ### Pull Merge Procedure - ensure pull branch is rebased on `master` -- run `make test` and `make test_cli` to ensure that all tests pass +- run `make test` to ensure that all tests pass - merge pull request ### Release Procedure @@ -206,58 +239,176 @@ only pull requests targeted directly against master. - **no PRs targeting this branch should be merged unless exceptional circumstances arise** - On the `RC` branch, prepare a new version section in the `CHANGELOG.md` - All links must be link-ified: `$ python ./scripts/linkify_changelog.py CHANGELOG.md` + - Copy the entries into a `RELEASE_CHANGELOG.md`, this is needed so the bot knows which entries to add to the release page on github. - Kick off a large round of simulation testing (e.g. 400 seeds for 2k blocks) - If errors are found during the simulation testing, commit the fixes to `master` and create a new `RC` branch (making sure to increment the `rcN`) - After simulation has successfully completed, create the release branch (`release/vX.XX.X`) from the `RC` branch - Create a PR to `master` to incorporate the `CHANGELOG.md` updates +- Tag the release (use `git tag -a`) and create a release in Github - Delete the `RC` branches ### Point Release Procedure -At the moment, only a single major release will be supported, so all point -releases will be based off of that release. +At the moment, only a single major release will be supported, so all point releases will be based +off of that release. -- start on `vX.XX.X` -- checkout a new branch `pre-rc/vX.X.X` -- cherry pick the desired changes from `master` - - these changes should be small and NON-BREAKING (both API and state machine) -- add entries to CHANGELOG.md and remove corresponding pending log entries -- checkout a new branch `rc/vX.X.X` based off of `vX.XX.X` -- create a PR merging `pre-rc/vX.X.X` into `rc/vX.X.X` -- run tests and simulations (noted in [Release Procedure](#release-procedure)) -- after tests and simulation have successfully completed, create the release branch `release/vX.XX.X` from the `RC` branch -- delete the `pre-rc/vX.X.X` and `RC` branches -- create a PR into `master` containing ONLY the CHANGELOG.md updates -- tag and release `release/vX.XX.X` +In order to alleviate the burden for a single person to have to cherry-pick and handle merge conflicts +of all desired backporting PRs to a point release, we instead maintain a living backport branch, where +all desired features and bug fixes are merged into as separate PRs. -## Code Owner Membership +Example: + +Current release is `v0.38.4`. We then maintain a (living) branch `sru/release/v0.38.N`, given N as +the next patch release number (currently `0.38.5`) for the `0.38` release series. As bugs are fixed +and PRs are merged into `master`, if a contributor wishes the PR to be released as SRU into the +`v0.38.N` point release, the contributor must: + +1. Add `0.38.N-backport` label +2. Pull latest changes on the desired `sru/release/vX.X.N` branch +3. Create a 2nd PR merging the respective SRU PR into `sru/release/v0.38.N` +4. Update the PR's description and ensure it contains the following information: + - **[Impact]** Explanation of how the bug affects users or developers. + - **[Test Case]** section with detailed instructions on how to reproduce the bug. + - **[Regression Potential]** section with a discussion how regressions are most likely to manifest, or might + manifest even if it's unlikely, as a result of the change. **It is assumed that any SRU candidate PR is + well-tested before it is merged in and has an overall low risk of regression**. + +It is the PR's author's responsibility to fix merge conflicts, update changelog entries, and +ensure CI passes. If a PR originates from an external contributor, it may be a core team member's +responsibility to perform this process instead of the original author. +Lastly, it is core team's responsibility to ensure that the PR meets all the SRU criteria. + +Finally, when a point release is ready to be made: + +1. Create `release/v0.38.N` branch +2. Ensure changelog entries are verified + 1. Be sure changelog entries are added to `RELEASE_CHANGELOG.md` +3. Add release version date to the changelog +4. Push release branch along with the annotated tag: **git tag -a** +5. Create a PR into `master` containing ONLY `CHANGELOG.md` updates + 1. Do not push `RELEASE_CHANGELOG.md` to `master` + +Note, although we aim to support only a single release at a time, the process stated above could be +used for multiple previous versions. + +## Code Owner Membership In the ethos of open source projects, and out of necessity to keep the code alive, the core contributor team will strive to permit special repo privileges -to developers who show an aptitude towards developing with this code base. +to developers who show an aptitude towards developing with this code base. Several different kinds of privileges may be granted however most common -privileges to be granted are merge rights to either part of, or the entire the -code base (though the github `CODEOWNERS` file). The on-boarding process for +privileges to be granted are merge rights to either part of, or the entirety of the +code base (through the github `CODEOWNERS` file). The on-boarding process for new code owners is as follows: On a bi-monthly basis (or more frequently if agreeable) all the existing code owners will privately convene to discuss potential new candidates as well as the potential for existing code-owners to exit or "pass on the torch". This private meeting is to be a held as a -phone/video meeting. Subsequently at the end of the meeting, one of the existing -code owners should open a PR modifying the `CODEOWNERS` file. The other code -owners should then all approve this PR to publicly display their support. +phone/video meeting. + +Subsequently after the meeting, and pending final approval from the ICF, +one of the existing code owners should open a PR modifying the `CODEOWNERS` file. +The other code owners should then all approve this PR to publicly display their support. Only if unanimous consensus is reached among all the existing code-owners will an invitation be extended to a new potential-member. Likewise, when an existing member is suggested to be removed/or have their privileges reduced, the member in question must agree on the decision for their removal or else no action -should be taken. If however, a code-owner is verifiably shown to intentionally +should be taken. If however, a code-owner is demonstrably shown to intentionally have had acted maliciously or grossly negligent, code-owner privileges may be -stripped with no prior warning or consent from the member in question. +stripped with no prior warning or consent from the member in question. + +Other potential removal criteria: + * Missing 3 scheduled meetings results in ICF evaluating whether the member should be + removed / replaced + * Violation of Code of Conduct Earning this privilege should be considered to be no small feat and is by no means guaranteed by any quantifiable metric. It is a symbol of great trust of -the community of this project. +the community of this project. + + +## Concept & Release Approval Process + +The process for how Cosmos SDK maintainers take features and ADRs from concept to release +is broken up into three distinct stages: **Strategy Discovery**, **Concept Approval**, and +**Implementation & Release Approval** + + +### Strategy Discovery + +* Develop long term priorities, strategy and roadmap for the SDK +* Release committee not yet defined as there is already a roadmap that can be used for the time being + +### Concept Approval + +* Architecture Decision Records (ADRs) may be proposed by any contributors or maintainers of the Cosmos SDK, + and should follow the guidelines outlined in the + [ADR Creation Process](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/PROCESS.md) +* After proposal, a time bound period for Request for Comment (RFC) on ADRs commences +* ADRs are intended to be iterative, and may be merged into `master` while still in a `Proposed` status + +**Time Bound Period** + +* Once a PR for an ADR is opened, reviewers are expected to perform a first + review within 1 week of pull request being open +* Time bound period for individual ADR Pull Requests to be merged should not exceed 2 weeks +* Total time bound period for an ADR to reach a decision (`ABANDONED | ACCEPTED | REJECTED`) should not exceed 4 weeks + +If an individual Pull Request for an ADR needs more time than 2 weeks to reach resolution, it should be merged +in current state (`Draft` or `Proposed`), with its contents updated to summarize +the current state of its discussion. + +If an ADR is taking longer than 4 weeks to reach a final conclusion, the **Concept Approval Committee** +should convene to rectify the situation by either: +- unanimously setting a new time bound period for this ADR +- making changes to the Concept Approval Process (as outlined here) +- making changes to the members of the Concept Approval Committee + +**Approval Committee & Decision Making** + +In absense of general consensus, decision making requires ⅔ vote from the three members +of the **Concept Approval Committee**. + +**Committee Members** + +* Core Members: **Aaron** (Regen), **Bez** (Fission), **Alessio** (AiB) +* Secondary pool of candidates to replace / substitute: + * **Chris Goes** (IG), **Sunny** (Sikka) + +**Committee Criteria** + +Members must: + +* Participate in all or almost all ADR discussions, both on Github as well as in bi-weekly Architecture Review + meetings +* Be active contributors to the SDK, and furthermore should be continuously making substantial contributions + to the project's codebase, review process, documentation and ADRs +* Have stake in the Cosmos SDK project, represented by: + * Being a client / user of the Comsos SDK + * "[giving back](https://www.debian.org/social_contract)" to the software +* Delegate representation in case of vacation or absence + +Code owners need to maintain participation in the process, ideally as members of **Concept Approval Committee** +members, but at the very least as active participants in ADR discussions + +Removal criteria: + +* Missing 3 meetings results in ICF evaluating whether the member should be removed / replaced +* Violation of Code of Conduct + +### Implementation & Release Approval + +The following process should be adhered to both for implementation PRs corresponding to ADRs, as +well as for PRs made as part of a release process: +* Code reviewers should ensure the PR does exactly what the ADR said it should +* Code reviewers should have more senior engineering capability +* ⅔ approval is required from the **primary repo maintainers** in `CODEOWNERS` + * Secondary pool of candidates to replace / substitute are listed as **secondary repo maintainers** in `CODEOWNERS` + +*Note: For any major or minor release series denoted as a "Stable Release" (e.g. v0.39 "Launchpad"), a separate release +committee is often established. Stable Releases, and their corresponding release committees are documented +separately in [STABLE_RELEASES.md](./STABLE_RELEASES.md)* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000000..af8b4c1febce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# Simple usage with a mounted data directory: +# > docker build -t simapp . +# +# Server: +# > docker run -it -p 26657:26657 -p 26656:26656 -v ~/.simapp:/root/.simapp simapp simd init test-chain +# TODO: need to set validator in genesis so start runs +# > docker run -it -p 26657:26657 -p 26656:26656 -v ~/.simapp:/root/.simapp simapp simd start +# +# Client: (Note the simapp binary always looks at ~/.simapp we can bind to different local storage) +# > docker run -it -p 26657:26657 -p 26656:26656 -v ~/.simappcli:/root/.simapp simapp simd keys add foo +# > docker run -it -p 26657:26657 -p 26656:26656 -v ~/.simappcli:/root/.simapp simapp simd keys list +# TODO: demo connecting rest-server (or is this in server now?) +FROM golang:alpine AS build-env + +# Install minimum necessary dependencies, +ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 +RUN apk add --no-cache $PACKAGES + +# Set working directory for the build +WORKDIR /go/src/github.com/cosmos/cosmos-sdk + +# Add source files +COPY . . + +# install simapp, remove packages +RUN make build-linux + + +# Final image +FROM alpine:edge + +# Install ca-certificates +RUN apk add --update ca-certificates +WORKDIR /root + +# Copy over binaries from the build-env +COPY --from=build-env /go/src/github.com/cosmos/cosmos-sdk/build/simd /usr/bin/simd + +EXPOSE 26656 26657 1317 9090 + +# Run simd by default, omit entrypoint to ease using container with simcli +CMD ["simd"] diff --git a/Makefile b/Makefile index 8d947640f75a..aa418969609f 100644 --- a/Makefile +++ b/Makefile @@ -2,79 +2,204 @@ PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') -VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') +VERSION := $(shell echo $(shell git describe --always) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') LEDGER_ENABLED ?= true BINDIR ?= $(GOPATH)/bin +BUILDDIR ?= $(CURDIR)/build SIMAPP = ./simapp MOCKS_DIR = $(CURDIR)/tests/mocks +HTTPS_GIT := https://github.com/fetchai/cosmos-sdk.git +DOCKER := $(shell which docker) +DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf export GO111MODULE = on +# process build tags + +build_tags = netgo +ifeq ($(LEDGER_ENABLED),true) + ifeq ($(OS),Windows_NT) + GCCEXE = $(shell where gcc.exe 2> NUL) + ifeq ($(GCCEXE),) + $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S),OpenBSD) + $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) + else + GCC = $(shell command -v gcc 2> /dev/null) + ifeq ($(GCC),) + $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + endif + endif +endif + +ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) + build_tags += gcc +endif +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) + +whitespace := +whitespace += $(whitespace) +comma := , +build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags)) + +# process linker flags + +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=sim \ + -X github.com/cosmos/cosmos-sdk/version.AppName=simd \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" + +# DB backend selection +ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb +endif +ifeq (badgerdb,$(findstring badgerdb,$(COSMOS_BUILD_OPTIONS))) + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=badgerdb +endif +# handle rocksdb +ifeq (rocksdb,$(findstring rocksdb,$(COSMOS_BUILD_OPTIONS))) + CGO_ENABLED=1 + BUILD_TAGS += rocksdb + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=rocksdb +endif +# handle boltdb +ifeq (boltdb,$(findstring boltdb,$(COSMOS_BUILD_OPTIONS))) + BUILD_TAGS += boltdb + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=boltdb +endif + +ifeq (,$(findstring nostrip,$(COSMOS_BUILD_OPTIONS))) + ldflags += -w -s +endif +ldflags += $(LDFLAGS) +ldflags := $(strip $(ldflags)) + +BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' +# check for nostrip option +ifeq (,$(findstring nostrip,$(COSMOS_BUILD_OPTIONS))) + BUILD_FLAGS += -trimpath +endif + all: tools build lint test # The below include contains the tools and runsim targets. include contrib/devtools/Makefile -######################################## -### Build - -build: go.sum - @go build -mod=readonly ./... -.PHONY: build - -update-swagger-docs: statik - $(BINDIR)/statik -src=client/lcd/swagger-ui -dest=client/lcd -f -m - @if [ -n "$(git status --porcelain)" ]; then \ - echo "\033[91mSwagger docs are out of sync!!!\033[0m";\ - exit 1;\ - else \ - echo "\033[92mSwagger docs are in sync\033[0m";\ - fi -.PHONY: update-swagger-docs +############################################################################### +### Build ### +############################################################################### + +BUILD_TARGETS := build install + +build: BUILD_ARGS=-o $(BUILDDIR)/ +build-linux: + GOOS=linux GOARCH=amd64 LEDGER_ENABLED=false $(MAKE) build + +$(BUILD_TARGETS): go.sum $(BUILDDIR)/ + go $@ -mod=readonly $(BUILD_FLAGS) $(BUILD_ARGS) ./... + +$(BUILDDIR)/: + mkdir -p $(BUILDDIR)/ + +build-simd-all: go.sum + $(DOCKER) rm latest-build || true + $(DOCKER) run --volume=$(CURDIR):/sources:ro \ + --env TARGET_PLATFORMS='linux/amd64 darwin/amd64 linux/arm64 windows/amd64' \ + --env APP=simd \ + --env VERSION=$(VERSION) \ + --env COMMIT=$(COMMIT) \ + --env LEDGER_ENABLED=$(LEDGER_ENABLED) \ + --name latest-build cosmossdk/rbuilder:latest + $(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/ + +build-simd-linux: go.sum $(BUILDDIR)/ + $(DOCKER) rm latest-build || true + $(DOCKER) run --volume=$(CURDIR):/sources:ro \ + --env TARGET_PLATFORMS='linux/amd64' \ + --env APP=simd \ + --env VERSION=$(VERSION) \ + --env COMMIT=$(COMMIT) \ + --env LEDGER_ENABLED=false \ + --name latest-build cosmossdk/rbuilder:latest + $(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/ + cp artifacts/simd-*-linux-amd64 $(BUILDDIR)/simd + +cosmovisor: + $(MAKE) -C cosmovisor cosmovisor + +.PHONY: build build-linux build-simd-all build-simd-linux cosmovisor mocks: $(MOCKS_DIR) - mockgen -source=x/auth/types/account_retriever.go -package mocks -destination tests/mocks/account_retriever.go + mockgen -source=client/account_retriever.go -package mocks -destination tests/mocks/account_retriever.go + mockgen -package mocks -destination tests/mocks/tendermint_tm_db_DB.go github.com/tendermint/tm-db DB + mockgen -source=types/module/module.go -package mocks -destination tests/mocks/types_module_module.go + mockgen -source=types/invariant.go -package mocks -destination tests/mocks/types_invariant.go + mockgen -source=types/router.go -package mocks -destination tests/mocks/types_router.go + mockgen -source=types/handler.go -package mocks -destination tests/mocks/types_handler.go + mockgen -package mocks -destination tests/mocks/grpc_server.go github.com/gogo/protobuf/grpc Server + mockgen -package mocks -destination tests/mocks/tendermint_tendermint_libs_log_DB.go github.com/tendermint/tendermint/libs/log Logger .PHONY: mocks $(MOCKS_DIR): mkdir -p $(MOCKS_DIR) -######################################## -### Tools & dependencies +distclean: clean tools-clean +clean: + rm -rf \ + $(BUILDDIR)/ \ + artifacts/ \ + tmp-swagger-gen/ + +.PHONY: distclean clean -go-mod-cache: go.sum - @echo "--> Download go modules to local cache" - @go mod download -.PHONY: go-mod-cache +############################################################################### +### Tools & Dependencies ### +############################################################################### go.sum: go.mod - @echo "--> Ensure dependencies have not been modified" - @go mod verify - @go mod tidy + echo "Ensure dependencies have not been modified ..." >&2 + go mod verify + go mod tidy -distclean: - rm -rf \ - gitian-build-darwin/ \ - gitian-build-linux/ \ - gitian-build-windows/ \ - .gitian-builder-cache/ -.PHONY: distclean +############################################################################### +### Documentation ### +############################################################################### -######################################## -### Documentation +update-swagger-docs: statik + $(BINDIR)/statik -src=client/docs/swagger-ui -dest=client/docs -f -m + @if [ -n "$(git status --porcelain)" ]; then \ + echo "\033[91mSwagger docs are out of sync!!!\033[0m";\ + exit 1;\ + else \ + echo "\033[92mSwagger docs are in sync\033[0m";\ + fi +.PHONY: update-swagger-docs godocs: @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/cosmos-sdk/types" godoc -http=:6060 +# This builds a docs site for each branch/tag in `./docs/versions` +# and copies each site to a version prefixed path. The last entry inside +# the `versions` file will be the default root index.html. build-docs: @cd docs && \ - while read p; do \ - (git checkout $${p} && npm install && VUEPRESS_BASE="/$${p}/" npm run build) ; \ - mkdir -p ~/output/$${p} ; \ - cp -r .vuepress/dist/* ~/output/$${p}/ ; \ - cp ~/output/$${p}/index.html ~/output ; \ + while read -r branch path_prefix; do \ + (git checkout $${branch} && npm install && VUEPRESS_BASE="/$${path_prefix}/" npm run build) ; \ + mkdir -p ~/output/$${path_prefix} ; \ + cp -r .vuepress/dist/* ~/output/$${path_prefix}/ ; \ + cp ~/output/$${path_prefix}/index.html ~/output ; \ done < versions ; sync-docs: @@ -85,25 +210,43 @@ sync-docs: aws cloudfront create-invalidation --distribution-id ${CF_DISTRIBUTION_ID} --profile terraform --path "/*" ; .PHONY: sync-docs -######################################## -### Testing +############################################################################### +### Tests & Simulation ### +############################################################################### test: test-unit test-all: test-unit test-ledger-mock test-race test-cover -test-ledger-mock: - @go test -mod=readonly `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger test_ledger_mock' - -test-ledger: test-ledger-mock - @go test -mod=readonly -v `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger' - -test-unit: - @VERSION=$(VERSION) go test -mod=readonly $(PACKAGES_NOSIMULATION) -tags='ledger test_ledger_mock' - -test-race: - @VERSION=$(VERSION) go test -mod=readonly -race $(PACKAGES_NOSIMULATION) - -.PHONY: test test-all test-ledger-mock test-ledger test-unit test-race +TEST_PACKAGES=./... +TEST_TARGETS := test-unit test-unit-amino test-unit-proto test-ledger-mock test-race test-ledger test-race + +# Test runs-specific rules. To add a new test target, just add +# a new rule, customise ARGS or TEST_PACKAGES ad libitum, and +# append the new rule to the TEST_TARGETS list. +test-unit: ARGS=-tags='cgo ledger test_ledger_mock norace' +test-unit-amino: ARGS=-tags='ledger test_ledger_mock test_amino norace' +test-ledger: ARGS=-tags='cgo ledger norace' +test-ledger-mock: ARGS=-tags='ledger test_ledger_mock norace' +test-race: ARGS=-race -tags='cgo ledger test_ledger_mock' +test-race: TEST_PACKAGES=$(PACKAGES_NOSIMULATION) +$(TEST_TARGETS): run-tests + +# check-* compiles and collects tests without running them +# note: go test -c doesn't support multiple packages yet (https://github.com/golang/go/issues/15513) +CHECK_TEST_TARGETS := check-test-unit check-test-unit-amino +check-test-unit: ARGS=-tags='cgo ledger test_ledger_mock norace' +check-test-unit-amino: ARGS=-tags='ledger test_ledger_mock test_amino norace' +$(CHECK_TEST_TARGETS): EXTRA_ARGS=-run=none +$(CHECK_TEST_TARGETS): run-tests + +run-tests: +ifneq (,$(shell which tparse 2>/dev/null)) + go test -mod=readonly -json $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) | tparse +else + go test -mod=readonly $(ARGS) $(EXTRA_ARGS) $(TEST_PACKAGES) +endif + +.PHONY: run-tests test test-all $(TEST_TARGETS) test-sim-nondeterminism: @echo "Running non-determinism test..." @@ -118,24 +261,24 @@ test-sim-custom-genesis-fast: test-sim-import-export: runsim @echo "Running application import/export simulation. This may take several minutes..." - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 50 5 TestAppImportExport + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppImportExport test-sim-after-import: runsim @echo "Running application simulation-after-import. This may take several minutes..." - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 50 5 TestAppSimulationAfterImport + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport test-sim-custom-genesis-multi-seed: runsim @echo "Running multi-seed custom genesis simulation..." @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." - @$(BINDIR)/runsim -Genesis=${HOME}/.gaiad/config/genesis.json -SimAppPkg=$(SIMAPP) 400 5 TestFullAppSimulation + @$(BINDIR)/runsim -Genesis=${HOME}/.gaiad/config/genesis.json -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation test-sim-multi-seed-long: runsim @echo "Running long multi-seed application simulation. This may take awhile!" - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 500 50 TestFullAppSimulation + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation test-sim-multi-seed-short: runsim @echo "Running short multi-seed application simulation. This may take awhile!" - @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) 50 10 TestFullAppSimulation + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 10 TestFullAppSimulation test-sim-benchmark-invariants: @echo "Running simulation invariant benchmarks..." @@ -170,37 +313,43 @@ test-sim-profile: .PHONY: test-sim-profile test-sim-benchmark test-cover: - @export VERSION=$(VERSION); bash -x tests/test_cover.sh + @export VERSION=$(VERSION); bash -x contrib/test_cover.sh .PHONY: test-cover -lint: golangci-lint - $(BINDIR)/golangci-lint run - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s - go mod verify -.PHONY: lint - -format: tools - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs gofmt -w -s - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs misspell -w - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" | xargs goimports -w -local github.com/cosmos/cosmos-sdk -.PHONY: format - benchmark: @go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION) .PHONY: benchmark -######################################## -### Devdoc +############################################################################### +### Linting ### +############################################################################### + +lint: + golangci-lint run --out-format=tab + +lint-fix: + golangci-lint run --fix --out-format=tab --issues-exit-code=0 +.PHONY: lint lint-fix + +format: + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs misspell -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs goimports -w -local github.com/cosmos/cosmos-sdk +.PHONY: format + +############################################################################### +### Devdoc ### +############################################################################### DEVDOC_SAVE = docker commit `docker ps -a -n 1 -q` devdoc:local devdoc-init: - docker run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" tendermint/devdoc echo + $(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" tendermint/devdoc echo # TODO make this safer $(call DEVDOC_SAVE) devdoc: - docker run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" devdoc:local bash + $(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" devdoc:local bash devdoc-save: # TODO make this safer @@ -213,3 +362,121 @@ devdoc-update: docker pull tendermint/devdoc .PHONY: devdoc devdoc-clean devdoc-init devdoc-save devdoc-update + +############################################################################### +### Protobuf ### +############################################################################### + +containerProtoVer=v0.2 +containerProtoImage=tendermintdev/sdk-proto-gen:$(containerProtoVer) +containerProtoGen=cosmos-sdk-proto-gen-$(containerProtoVer) +containerProtoGenSwagger=cosmos-sdk-proto-gen-swagger-$(containerProtoVer) +containerProtoFmt=cosmos-sdk-proto-fmt-$(containerProtoVer) + +proto-all: proto-format proto-lint proto-gen + +proto-gen: + @echo "Generating Protobuf files" + @if $(DOCKER) ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGen}$$"; then $(DOCKER) start -a $(containerProtoGen); else $(DOCKER) run --name $(containerProtoGen) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ + sh ./scripts/protocgen.sh; fi + +# This generates the SDK's custom wrapper for google.protobuf.Any. It should only be run manually when needed +proto-gen-any: + @echo "Generating Protobuf Any" + $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) sh ./scripts/protocgen-any.sh + +proto-swagger-gen: + @echo "Generating Protobuf Swagger" + @if $(DOCKER) ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGenSwagger}$$"; then $(DOCKER) start -a $(containerProtoGenSwagger); else $(DOCKER) run --name $(containerProtoGenSwagger) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ + sh ./scripts/protoc-swagger-gen.sh; fi + +proto-format: + @echo "Formatting Protobuf files" + @if $(DOCKER) ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoFmt}$$"; then $(DOCKER) start -a $(containerProtoFmt); else $(DOCKER) run --name $(containerProtoFmt) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ + find ./ -not -path "./third_party/*" -name *.proto -exec clang-format -i {}; fi + +proto-lint: + @$(DOCKER_BUF) check lint --error-format=json + +proto-check-breaking: + @$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master + + +TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint +GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos +COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master +CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3 + +TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto +TM_ABCI_TYPES = third_party/proto/tendermint/abci +TM_TYPES = third_party/proto/tendermint/types +TM_VERSION = third_party/proto/tendermint/version +TM_LIBS = third_party/proto/tendermint/libs/bits +TM_P2P = third_party/proto/tendermint/p2p + +GOGO_PROTO_TYPES = third_party/proto/gogoproto +COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto +CONFIO_TYPES = third_party/proto/confio + +proto-update-deps: + @mkdir -p $(GOGO_PROTO_TYPES) + @curl -sSL $(GOGO_PROTO_URL)/gogoproto/gogo.proto > $(GOGO_PROTO_TYPES)/gogo.proto + + @mkdir -p $(COSMOS_PROTO_TYPES) + @curl -sSL $(COSMOS_PROTO_URL)/cosmos.proto > $(COSMOS_PROTO_TYPES)/cosmos.proto + +## Importing of tendermint protobuf definitions currently requires the +## use of `sed` in order to build properly with cosmos-sdk's proto file layout +## (which is the standard Buf.build FILE_LAYOUT) +## Issue link: https://github.com/tendermint/tendermint/issues/5021 + @mkdir -p $(TM_ABCI_TYPES) + @curl -sSL $(TM_URL)/abci/types.proto > $(TM_ABCI_TYPES)/types.proto + + @mkdir -p $(TM_VERSION) + @curl -sSL $(TM_URL)/version/types.proto > $(TM_VERSION)/types.proto + + @mkdir -p $(TM_TYPES) + @curl -sSL $(TM_URL)/types/types.proto > $(TM_TYPES)/types.proto + @curl -sSL $(TM_URL)/types/evidence.proto > $(TM_TYPES)/evidence.proto + @curl -sSL $(TM_URL)/types/params.proto > $(TM_TYPES)/params.proto + @curl -sSL $(TM_URL)/types/validator.proto > $(TM_TYPES)/validator.proto + @curl -sSL $(TM_URL)/types/block.proto > $(TM_TYPES)/block.proto + + @mkdir -p $(TM_CRYPTO_TYPES) + @curl -sSL $(TM_URL)/crypto/proof.proto > $(TM_CRYPTO_TYPES)/proof.proto + @curl -sSL $(TM_URL)/crypto/keys.proto > $(TM_CRYPTO_TYPES)/keys.proto + + @mkdir -p $(TM_LIBS) + @curl -sSL $(TM_URL)/libs/bits/types.proto > $(TM_LIBS)/types.proto + + @mkdir -p $(TM_P2P) + @curl -sSL $(TM_URL)/p2p/types.proto > $(TM_P2P)/types.proto + + @mkdir -p $(CONFIO_TYPES) + @curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto +## insert go package option into proofs.proto file +## Issue link: https://github.com/confio/ics23/issues/32 + @sed -i '4ioption go_package = "github.com/confio/ics23/go";' $(CONFIO_TYPES)/proofs.proto + +.PHONY: proto-all proto-gen proto-gen-any proto-swagger-gen proto-format proto-lint proto-check-breaking proto-update-deps + +############################################################################### +### Localnet ### +############################################################################### + +# Run a 4-node testnet locally +localnet-start: build-linux localnet-stop + $(if $(shell $(DOCKER) inspect -f '{{ .Id }}' cosmossdk/simd-env 2>/dev/null),$(info found image cosmossdk/simd-env),$(MAKE) -C contrib/images simd-env) + if ! [ -f build/node0/simd/config/genesis.json ]; then $(DOCKER) run --rm \ + --user $(shell id -u):$(shell id -g) \ + -v $(BUILDDIR):/simd:Z \ + -v /etc/group:/etc/group:ro \ + -v /etc/passwd:/etc/passwd:ro \ + -v /etc/shadow:/etc/shadow:ro \ + cosmossdk/simd-env testnet --v 4 -o . --starting-ip-address 192.168.10.2 --keyring-backend=test ; fi + docker-compose up -d + +localnet-stop: + docker-compose down + +.PHONY: localnet-start localnet-stop diff --git a/README.md b/README.md index 6263c14645ec..39904e8d516a 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,45 @@ parent: order: false --> -# Cosmos SDK +
+

Cosmos SDK

+
![banner](docs/cosmos-sdk-image.jpg) -This is a fork from the original [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk) repo. It contains Fetch.ai specific updates required for the test networks. For this reason it is versioned independantly. Please refer to the [releases](https://github.com/fetchai/cosmos-sdk/releases) section for the compatiblity with upstream versions. +
+ + Version + + + License: Apache-2.0 + + + GoDoc + + + Go report card + + + Code Coverage + +
+
+ + Lines Of Code + + + Discord + + + Imported by + + Sims + Lint Satus +
+ + + The Cosmos-SDK is a framework for building blockchain applications in Golang. It is being used to build [`Gaia`](https://github.com/cosmos/gaia), the first implementation of the Cosmos Hub. @@ -15,7 +49,7 @@ It is being used to build [`Gaia`](https://github.com/cosmos/gaia), the first im **WARNING**: The SDK has mostly stabilized, but we are still making some breaking changes. -**Note**: Requires [Go 1.13+](https://golang.org/dl/) +**Note**: Requires [Go 1.15+](https://golang.org/dl/) ## Quick Start @@ -29,9 +63,9 @@ For more, please go to the [Cosmos SDK Docs](./docs/). The Cosmos Hub application, `gaia`, has moved to its [own repository](https://github.com/cosmos/gaia). Go there to join the Cosmos Hub mainnet and more. -## Scaffolding +## Starport -If you are starting a new app or a new module we provide a [scaffolding tool](https://github.com/cosmos/scaffold) to help you get started and speed up development. If you have any questions or find a bug, feel free to open an issue in the repo. +If you are starting a new app or a new module you can use [Starport](https://github.com/tendermint/starport) to help you get started and speed up development. If you have any questions or find a bug, feel free to open an issue in the repo. ## Disambiguation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 000000000000..aee920607c24 --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,25 @@ +# Cosmos SDK v0.42.5 "Stargate" Release Notes + +This release includes various minor bugfixes and improvments, including: + +- Fix support for building the Cosmos SDK on ARM architectures, +- Fix the `[appd] keys show/list` CLI subcommands for multisigs, +- Internal code performance improvment. + +It also introduces one new feature: adding the `[appd] config` subcommand back to the SDK. + +See the [Cosmos SDK v0.42.5 milestone](https://github.com/cosmos/cosmos-sdk/milestone/44?closed=1) on our issue tracker for the exhaustive list of all changes. + +### The `config` Subcommand + +One new feature introduced in the Stargate series was the merging of the two CLI binaries `[appd]` and `[appcli]` into one single application binary. In this process, the `[appcli] config` subcommand, which was used to save client-side configuration into a TOML file, was removed. + +Due to [popular demand](https://github.com/cosmos/cosmos-sdk/issues/8529), we have introduced this feature back to the SDK, under the `[appd] config` subcommand. The functionality is as follows: + +- `[appd] config`: Output all client-side configuration. +- `[appd] config [config-name]`: Get the given configuration (e.g. `keyring-backend` or `node-id`). +- `[appd] config [config-name] [config-value]`: Set and persist the given configuration with the new value. + +All configurations are persisted to the filesystem, under the path `$APP_HOME/config/client.toml`. For the list of all possible client-side configurations, please have a look at this `client.toml` file, as it is heavily commented. + +Environment variables binding to client-side configuration also works. For example, the command `KEYRING_BACKEND=os [appd] tx bank send ...` will bind ENV variable to the `keyring-backend` config. The order or precedence for config is: `flags > env vars > client.toml file`. diff --git a/SECURITY.md b/SECURITY.md index 8d3322dbd080..ce82a957f9bb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -26,12 +26,14 @@ in for Tendermint and other lower-level libraries (eg. [IAVL](https://github.com - [`x/auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) - [`x/bank`](https://github.com/cosmos/cosmos-sdk/tree/master/x/bank) +- [`x/capability`](https://github.com/cosmos/cosmos-sdk/tree/master/x/capability) - [`x/staking`](https://github.com/cosmos/cosmos-sdk/tree/master/x/staking) - [`x/slashing`](https://github.com/cosmos/cosmos-sdk/tree/master/x/slashing) - [`x/evidence`](https://github.com/cosmos/cosmos-sdk/tree/master/x/evidence) - [`x/distribution`](https://github.com/cosmos/cosmos-sdk/tree/master/x/distribution) -- [`x/supply`](https://github.com/cosmos/cosmos-sdk/tree/master/x/supply) -- [`x/ibc`](https://github.com/cosmos/cosmos-sdk/tree/ibc-alpha/x/ibc) (currently in alpha mode) +- [`x/ibc`](https://github.com/cosmos/cosmos-sdk/tree/master/x/ibc) +- [`x/ibc-transfer`](https://github.com/cosmos/cosmos-sdk/tree/master/x/ibc-transfer) +- [`x/mint`](https://github.com/cosmos/cosmos-sdk/tree/master/x/mint) We are interested in bugs in other modules, however the above are most likely to have significant vulnerabilities, due to the complexity / nuance involved. We diff --git a/STABLE_RELEASES.md b/STABLE_RELEASES.md new file mode 100644 index 000000000000..fd6a88213557 --- /dev/null +++ b/STABLE_RELEASES.md @@ -0,0 +1,131 @@ +# Stable Releases + +*Stable Release Series* continue to receive bug fixes until they reach **End Of Life**. + +Only the following release series are currently supported and receive bug fixes: + +The `0.37.x` release series will continue receiving bug fixes until the Cosmos Hub +migrates to a newer release of the Cosmos-SDK. + +* **0.37** will continue receiving bug fixes until the Cosmos Hub migrates to a newer release series of the Cosmos-SDK. +* **0.39 «Launchpad»** will be supported until 6 months after **0.40.0** is published. A fairly strict **bugfix-only** rule applies to pull requests that are requested to be included into a stable point-release. + +The **0.39 «Launchpad»** release series is maintained in compliance with the **Stable Release Policy** as described in this document. + +## Stable Release Policy + +This policy presently applies *only* to the following release series: + +* **0.39 «Launchpad»** + +### Point Releases + +Once a Cosmos-SDK release has been completed and published, updates for it are released under certain circumstances +and must follow the [Point Release Procedure](CONTRIBUTING.md). + +### Rationale + +Unlike in-development `master` branch snapshots, **Cosmos-SDK** releases are subject to much wider adoption, +and by a significantly different demographic of users. During development, changes in the `master` branch +affect SDK users, application developers, early adopters, and other advanced users that elect to use +unstable experimental software at their own risk. + +Conversely, users of a stable release expect a high degree of stability. They build their applications on it, and the +problems they experience with it could be potentially highly disruptive to their projects. + +Stable release updates are recommended to the vast majority of developers, and so it is crucial to treat them +with great caution. Hence, when updates are proposed, they must be accompanied by a strong rationale and present +a low risk of regressions, i.e. even one-line changes could cause unexpected regressions due to side effects or +poorly tested code. We never assume that any change, no matter how little or non-intrusive, is completely exempt +of regression risks. + +Therefore, the requirements for stable changes are different than those that are candidates to be merged in +the `master` branch. When preparing future major releases, our aim is to design the most elegant, user-friendly and +maintainable SDK possible which often entails fundamental changes to the SDK's architecture design, rearranging and/or +renaming packages as well as reducing code duplication so that we maintain common functions and data structures in one +place rather than leaving them scattered all over the code base. However, once a release is published, the +priority is to minimise the risk caused by changes that are not strictly required to fix qualifying bugs; this tends to +be correlated with minimising the size of such changes. As such, the same bug may need to be fixed in different +ways in stable releases and `master` branch. + +### What qualifies as a Stable Release Update (SRU) + +* **High-impact bugs** + * Bugs that may directly cause a security vulnerability. + * *Severe regressions* from a Cosmos-SDK's previous release. This includes all sort of issues + that may cause the core packages or the `x/` modules unusable. + * Bugs that may cause **loss of user's data**. +* Other safe cases: + * Bugs which don't fit in the aforementioned categories for which an obvious safe patch is known. + * Relatively small yet strictly non-breaking changes that introduce forward-compatible client + features to smoothen the migration to successive releases. + +### What does not qualify as SRU + +* State machine changes. +* New features that introduces API breakages (e.g. public functions removal/renaming). +* Cosmetic fixes, such as formatting or linter warning fixes. + +## What pull requests will be included in stable point-releases + +Pull requests that fix bugs that fall in the following categories do not require a **Stable Release Exception** to be granted to be included in a stable point-release: + + * **Severe regressions**. + * Bugs that may cause **client applications** to be **largely unusable**. + * Bugs that may cause **state corruption or data loss**. + * Bugs that may directly or indirectly cause a **security vulnerability**. + +## What pull requests will NOT be automatically included in stable point-releases + +As rule of thumb, the following changes will **NOT** be automatically accepted into stable point-releases: + + * **State machine changes**. + * **Client application's code-breaking changes**, i.e. changes that prevent client applications to *build without modifications* to the client application's source code. + + In some circumstances, PRs that don't meet the aforementioned criteria might be raised and asked to be granted a *Stable Release Exception*. + +## Stable Release Exception - Procedure + +1. Check that the bug is either fixed or not reproducible in `master`. It is, in general, not appropriate to release bug fixes for stable releases without first testing them in `master`. Please apply the label [0.39 «Launchpad»](https://github.com/cosmos/cosmos-sdk/labels/0.39%20LTS%20%28Launchpad%29) to the issue. +2. Add a comment to the issue and ensure it contains the following information (see the bug template below): + * **[Impact]** An explanation of the bug on users and justification for backporting the fix to the stable release. + * A **[Test Case]** section containing detailed instructions on how to reproduce the bug. + * A **[Regression Potential]** section with a clear assessment on how regressions are most likely to manifest as a result of the pull request that aims to fix the bug in the target stable release. +3. **Stable Release Managers** will review and discuss the PR. Once *consensus* surrounding the rationale has been reached and the technical review has successfully concluded, the pull request will be merged in the respective point-release target branch (e.g. `release/launchpad/0.39.X` being `X` the Launchpad's upcoming point-release) and the PR included in the point-release's respective milestone (e.g. `0.39.5`). + +### Stable Release Exception - Bug template + +``` +#### Impact + +Brief xplanation of the effects of the bug on users and a justification for backporting the fix to the stable release. + +#### Test Case + +Detailed instructions on how to reproduce the bug on Launchpad's most recently published point-release. + +#### Regression Potential + +Explanation on how regressions might manifest - even if it's unlikely. +It is assumed that stable release fixes are well-tested and they come with a low risk of regressions. +It's crucial to make the effort of thinking about what could happen in case a regression emerges. +``` + +## Stable Release Managers + +The **Stable Release Managers** evaluate and approve or reject updates and backports to Cosmos-SDK Stable Release series, +according to the [stable release policy](#stable-release-policy) and [release procedure](#stable-release-exception-procedure). +Decisions are made by consensus. + +Their responsibilites include: + * Driving the Stable Release Exception process. + * Approving/rejecting proposed changes to a stable release series. + * Executing the release process of stable point-releases in compliance with the [Point Release Procedure](CONTRIBUTING.md). + +The Stable Release Managers are appointed by the Interchain Foundation. + +*Stable Release Managers* for the **0.39 «Launchpad»** release series follow: + +* @alessio - Alessio Treglia +* @clevinson - Cory Levinson- +* @ethanfrey - Ethan Frey diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 6dc32dc974a1..000000000000 --- a/Vagrantfile +++ /dev/null @@ -1,51 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -Vagrant.configure("2") do |config| - config.vm.box = "ubuntu/xenial64" - - config.vm.provider "virtualbox" do |v| - v.memory = 4096 - v.cpus = 2 - end - - config.vm.provision "shell", inline: <<-SHELL - - # add docker repo - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" - - # and golang 1.9 support - # official repo doesn't have race detection runtime... - #add-apt-repository ppa:gophers/archive - add-apt-repository ppa:longsleep/golang-backports - - # install base requirements - apt-get update - apt-get install -y --no-install-recommends wget curl jq \ - make shellcheck bsdmainutils psmisc - apt-get install -y golang-1.9-go docker-ce - apt-get install -y language-pack-en - - # cleanup - apt-get autoremove -y - - # needed for docker - usermod -a -G docker vagrant - - # use "EOF" not EOF to avoid variable substitution of $PATH - echo 'export PATH=$PATH:/usr/lib/go-1.9/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile - echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile - echo 'export LC_ALL=en_US.UTF-8' >> /home/vagrant/.bash_profile - - mkdir -p /home/vagrant/go/bin - mkdir -p /home/vagrant/go/src/github.com/cosmos - ln -s /vagrant /home/vagrant/go/src/github.com/cosmos/cosmos-sdk - - chown -R vagrant:vagrant /home/vagrant/go - chown vagrant:vagrant /home/vagrant/.bash_profile - - su - vagrant -c 'source /home/vagrant/.bash_profile' - su - vagrant -c 'cd /home/vagrant/go/src/github.com/cosmos/cosmos-sdk && make tools' - SHELL -end diff --git a/baseapp/abci.go b/baseapp/abci.go index d9674325f8fe..36c36d1e118b 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -1,15 +1,24 @@ package baseapp import ( + "crypto/sha256" + "errors" "fmt" "os" "sort" "strings" "syscall" + "time" + "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" "github.com/cosmos/cosmos-sdk/codec" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -17,18 +26,32 @@ import ( // InitChain implements the ABCI interface. It runs the initialization logic // directly on the CommitMultiStore. func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { - // stash the consensus params in the cms main store and memoize - if req.ConsensusParams != nil { - app.setConsensusParams(req.ConsensusParams) - app.storeConsensusParams(req.ConsensusParams) + // On a new chain, we consider the init chain block height as 0, even though + // req.InitialHeight is 1 by default. + initHeader := tmproto.Header{ChainID: req.ChainId, Time: req.Time} + + // If req.InitialHeight is > 1, then we set the initial version in the + // stores. + if req.InitialHeight > 1 { + app.initialHeight = req.InitialHeight + initHeader = tmproto.Header{ChainID: req.ChainId, Height: req.InitialHeight, Time: req.Time} + err := app.cms.SetInitialVersion(req.InitialHeight) + if err != nil { + panic(err) + } } - initHeader := abci.Header{ChainID: req.ChainId, Time: req.Time} - // initialize the deliver state and check state with a correct header app.setDeliverState(initHeader) app.setCheckState(initHeader) + // Store the consensus params in the BaseApp's paramstore. Note, this must be + // done after the deliver state and context have been set as it's persisted + // to state. + if req.ConsensusParams != nil { + app.StoreConsensusParams(app.deliverState.ctx, req.ConsensusParams) + } + if app.initChainer == nil { return } @@ -52,16 +75,32 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC sort.Sort(abci.ValidatorUpdates(req.Validators)) sort.Sort(abci.ValidatorUpdates(res.Validators)) - for i, val := range res.Validators { - if !val.Equal(req.Validators[i]) { + for i := range res.Validators { + if !proto.Equal(&res.Validators[i], &req.Validators[i]) { panic(fmt.Errorf("genesisValidators[%d] != req.Validators[%d] ", i, i)) } } } - // NOTE: We don't commit, but BeginBlock for block 1 starts from this + // In the case of a new chain, AppHash will be the hash of an empty string. + // During an upgrade, it'll be the hash of the last committed block. + var appHash []byte + if !app.LastCommitID().IsZero() { + appHash = app.LastCommitID().Hash + } else { + // $ echo -n '' | sha256sum + // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + emptyHash := sha256.Sum256([]byte{}) + appHash = emptyHash[:] + } + + // NOTE: We don't commit, but BeginBlock for block `initial_height` starts from this // deliverState. - return res + return abci.ResponseInitChain{ + ConsensusParams: res.ConsensusParams, + Validators: res.Validators, + AppHash: appHash, + } } // Info implements the ABCI interface. @@ -86,19 +125,23 @@ func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { if app.addrPeerFilter != nil { return app.addrPeerFilter(info) } + return abci.ResponseQuery{} } -// FilterPeerByIDfilters peers by node ID. +// FilterPeerByID filters peers by node ID. func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery { if app.idPeerFilter != nil { return app.idPeerFilter(info) } + return abci.ResponseQuery{} } // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { + defer telemetry.MeasureSince(time.Now(), "abci", "begin_block") + if app.cms.TracingEnabled() { app.cms.SetTracingContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, @@ -124,7 +167,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // add block gas meter var gasMeter sdk.GasMeter - if maxGas := app.getMaximumBlockGas(); maxGas > 0 { + if maxGas := app.getMaximumBlockGas(app.deliverState.ctx); maxGas > 0 { gasMeter = sdk.NewGasMeter(maxGas) } else { gasMeter = sdk.NewInfiniteGasMeter() @@ -134,8 +177,8 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg if app.beginBlocker != nil { res = app.beginBlocker(app.deliverState.ctx, req) + res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents) } - // set the signed validators for addition to context in deliverTx app.voteInfos = req.LastCommitInfo.GetVotes() return res @@ -143,15 +186,22 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // EndBlock implements the ABCI interface. func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + defer telemetry.MeasureSince(time.Now(), "abci", "end_block") + if app.deliverState.ms.TracingEnabled() { app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) } if app.endBlocker != nil { res = app.endBlocker(app.deliverState.ctx, req) + res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents) } - return + if cp := app.GetConsensusParams(app.deliverState.ctx); cp != nil { + res.ConsensusParamUpdates = cp + } + + return res } // CheckTx implements the ABCI interface and executes a tx in CheckTx mode. In @@ -161,10 +211,7 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // will contain releveant error information. Regardless of tx execution outcome, // the ResponseCheckTx will contain relevant gas execution context. func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { - tx, err := app.txDecoder(req.Tx) - if err != nil { - return sdkerrors.ResponseCheckTx(err, 0, 0, app.debug) - } + defer telemetry.MeasureSince(time.Now(), "abci", "check_tx") var mode runTxMode @@ -179,9 +226,9 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type)) } - gInfo, result, err := app.runTx(mode, req.Tx, tx) + gInfo, result, err := app.runTx(mode, req.Tx) if err != nil { - return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.debug) + return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseCheckTx{ @@ -189,7 +236,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? Log: result.Log, Data: result.Data, - Events: result.Events.ToABCIEvents(), + Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents), } } @@ -199,14 +246,22 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { // Regardless of tx execution outcome, the ResponseDeliverTx will contain relevant // gas execution context. func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { - tx, err := app.txDecoder(req.Tx) - if err != nil { - return sdkerrors.ResponseDeliverTx(err, 0, 0, app.debug) - } + defer telemetry.MeasureSince(time.Now(), "abci", "deliver_tx") - gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx) + gInfo := sdk.GasInfo{} + resultStr := "successful" + + defer func() { + telemetry.IncrCounter(1, "tx", "count") + telemetry.IncrCounter(1, "tx", resultStr) + telemetry.SetGauge(float32(gInfo.GasUsed), "tx", "gas", "used") + telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted") + }() + + gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx) if err != nil { - return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.debug) + resultStr = "failed" + return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseDeliverTx{ @@ -214,7 +269,7 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? Log: result.Log, Data: result.Data, - Events: result.Events.ToABCIEvents(), + Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents), } } @@ -226,14 +281,17 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx // against that height and gracefully halt if it matches the latest committed // height. func (app *BaseApp) Commit() (res abci.ResponseCommit) { + defer telemetry.MeasureSince(time.Now(), "abci", "commit") + header := app.deliverState.ctx.BlockHeader() + retainHeight := app.GetBlockRetentionHeight(header.Height) - // Write the DeliverTx state which is cache-wrapped and commit the MultiStore. + // Write the DeliverTx state into branched storage and commit the MultiStore. // The write to the DeliverTx state writes all state transitions to the root // MultiStore (app.cms) so when Commit() is called is persists those values. app.deliverState.ms.Write() commitID := app.cms.Commit() - app.logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID)) + app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) // Reset the Check state to the latest committed. // @@ -262,8 +320,13 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { app.halt() } + if app.snapshotInterval > 0 && uint64(header.Height)%app.snapshotInterval == 0 { + go app.snapshot(header.Height) + } + return abci.ResponseCommit{ - Data: commitID.Hash, + Data: commitID.Hash, + RetainHeight: retainHeight, } } @@ -289,9 +352,60 @@ func (app *BaseApp) halt() { os.Exit(0) } +// snapshot takes a snapshot of the current state and prunes any old snapshottypes. +func (app *BaseApp) snapshot(height int64) { + if app.snapshotManager == nil { + app.logger.Info("snapshot manager not configured") + return + } + + app.logger.Info("creating state snapshot", "height", height) + + snapshot, err := app.snapshotManager.Create(uint64(height)) + if err != nil { + app.logger.Error("failed to create state snapshot", "height", height, "err", err) + return + } + + app.logger.Info("completed state snapshot", "height", height, "format", snapshot.Format) + + if app.snapshotKeepRecent > 0 { + app.logger.Debug("pruning state snapshots") + + pruned, err := app.snapshotManager.Prune(app.snapshotKeepRecent) + if err != nil { + app.logger.Error("Failed to prune state snapshots", "err", err) + return + } + + app.logger.Debug("pruned state snapshots", "pruned", pruned) + } +} + // Query implements the ABCI interface. It delegates to CommitMultiStore if it // implements Queryable. -func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery { +func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { + defer telemetry.MeasureSince(time.Now(), "abci", "query") + + // Add panic recovery for all queries. + // ref: https://github.com/cosmos/cosmos-sdk/pull/8039 + defer func() { + if r := recover(); r != nil { + res = sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrPanic, "%v", r)) + } + }() + + // when a client did not provide a query height, manually inject the latest + if req.Height == 0 { + req.Height = app.LastBlockHeight() + } + + // handle gRPC routes first rather than calling splitPath because '/' characters + // are used as part of gRPC paths + if grpcHandler := app.grpcQueryRouter.Route(req.Path); grpcHandler != nil { + return app.handleQueryGRPC(grpcHandler, req) + } + path := splitPath(req.Path) if len(path) == 0 { sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no query path provided")) @@ -315,23 +429,326 @@ func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query path")) } +// ListSnapshots implements the ABCI interface. It delegates to app.snapshotManager if set. +func (app *BaseApp) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { + resp := abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{}} + if app.snapshotManager == nil { + return resp + } + + snapshots, err := app.snapshotManager.List() + if err != nil { + app.logger.Error("failed to list snapshots", "err", err) + return resp + } + + for _, snapshot := range snapshots { + abciSnapshot, err := snapshot.ToABCI() + if err != nil { + app.logger.Error("failed to list snapshots", "err", err) + return resp + } + resp.Snapshots = append(resp.Snapshots, &abciSnapshot) + } + + return resp +} + +// LoadSnapshotChunk implements the ABCI interface. It delegates to app.snapshotManager if set. +func (app *BaseApp) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { + if app.snapshotManager == nil { + return abci.ResponseLoadSnapshotChunk{} + } + chunk, err := app.snapshotManager.LoadChunk(req.Height, req.Format, req.Chunk) + if err != nil { + app.logger.Error( + "failed to load snapshot chunk", + "height", req.Height, + "format", req.Format, + "chunk", req.Chunk, + "err", err, + ) + return abci.ResponseLoadSnapshotChunk{} + } + return abci.ResponseLoadSnapshotChunk{Chunk: chunk} +} + +// OfferSnapshot implements the ABCI interface. It delegates to app.snapshotManager if set. +func (app *BaseApp) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { + if app.snapshotManager == nil { + app.logger.Error("snapshot manager not configured") + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT} + } + + if req.Snapshot == nil { + app.logger.Error("received nil snapshot") + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} + } + + snapshot, err := snapshottypes.SnapshotFromABCI(req.Snapshot) + if err != nil { + app.logger.Error("failed to decode snapshot metadata", "err", err) + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} + } + + err = app.snapshotManager.Restore(snapshot) + switch { + case err == nil: + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} + + case errors.Is(err, snapshottypes.ErrUnknownFormat): + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT} + + case errors.Is(err, snapshottypes.ErrInvalidMetadata): + app.logger.Error( + "rejecting invalid snapshot", + "height", req.Snapshot.Height, + "format", req.Snapshot.Format, + "err", err, + ) + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} + + default: + app.logger.Error( + "failed to restore snapshot", + "height", req.Snapshot.Height, + "format", req.Snapshot.Format, + "err", err, + ) + + // We currently don't support resetting the IAVL stores and retrying a different snapshot, + // so we ask Tendermint to abort all snapshot restoration. + return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT} + } +} + +// ApplySnapshotChunk implements the ABCI interface. It delegates to app.snapshotManager if set. +func (app *BaseApp) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { + if app.snapshotManager == nil { + app.logger.Error("snapshot manager not configured") + return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ABORT} + } + + _, err := app.snapshotManager.RestoreChunk(req.Chunk) + switch { + case err == nil: + return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} + + case errors.Is(err, snapshottypes.ErrChunkHashMismatch): + app.logger.Error( + "chunk checksum mismatch; rejecting sender and requesting refetch", + "chunk", req.Index, + "sender", req.Sender, + "err", err, + ) + return abci.ResponseApplySnapshotChunk{ + Result: abci.ResponseApplySnapshotChunk_RETRY, + RefetchChunks: []uint32{req.Index}, + RejectSenders: []string{req.Sender}, + } + + default: + app.logger.Error("failed to restore snapshot", "err", err) + return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ABORT} + } +} + +func (app *BaseApp) handleQueryGRPC(handler GRPCQueryHandler, req abci.RequestQuery) abci.ResponseQuery { + ctx, err := app.createQueryContext(req.Height, req.Prove) + if err != nil { + return sdkerrors.QueryResult(err) + } + + res, err := handler(ctx, req) + if err != nil { + res = sdkerrors.QueryResult(gRPCErrorToSDKError(err)) + res.Height = req.Height + return res + } + + return res +} + +func gRPCErrorToSDKError(err error) error { + status, ok := grpcstatus.FromError(err) + if !ok { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) + } + + switch status.Code() { + case codes.NotFound: + return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, err.Error()) + case codes.InvalidArgument: + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) + case codes.FailedPrecondition: + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) + case codes.Unauthenticated: + return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, err.Error()) + default: + return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, err.Error()) + } +} + +func checkNegativeHeight(height int64) error { + if height < 0 { + // Reject invalid heights. + return sdkerrors.Wrap( + sdkerrors.ErrInvalidRequest, + "cannot query with height < 0; please provide a valid height", + ) + } + return nil +} + +// createQueryContext creates a new sdk.Context for a query, taking as args +// the block height and whether the query needs a proof or not. +func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, error) { + if err := checkNegativeHeight(height); err != nil { + return sdk.Context{}, err + } + + // when a client did not provide a query height, manually inject the latest + if height == 0 { + height = app.LastBlockHeight() + } + + if height <= 1 && prove { + return sdk.Context{}, + sdkerrors.Wrap( + sdkerrors.ErrInvalidRequest, + "cannot query with proof when height <= 1; please provide a valid height", + ) + } + + cacheMS, err := app.cms.CacheMultiStoreWithVersion(height) + if err != nil { + return sdk.Context{}, + sdkerrors.Wrapf( + sdkerrors.ErrInvalidRequest, + "failed to load state at height %d; %s (latest height: %d)", height, err, app.LastBlockHeight(), + ) + } + + // branch the commit-multistore for safety + ctx := sdk.NewContext( + cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, + ).WithMinGasPrices(app.minGasPrices) + + return ctx, nil +} + +// GetBlockRetentionHeight returns the height for which all blocks below this height +// are pruned from Tendermint. Given a commitment height and a non-zero local +// minRetainBlocks configuration, the retentionHeight is the smallest height that +// satisfies: +// +// - Unbonding (safety threshold) time: The block interval in which validators +// can be economically punished for misbehavior. Blocks in this interval must be +// auditable e.g. by the light client. +// +// - Logical store snapshot interval: The block interval at which the underlying +// logical store database is persisted to disk, e.g. every 10000 heights. Blocks +// since the last IAVL snapshot must be available for replay on application restart. +// +// - State sync snapshots: Blocks since the oldest available snapshot must be +// available for state sync nodes to catch up (oldest because a node may be +// restoring an old snapshot while a new snapshot was taken). +// +// - Local (minRetainBlocks) config: Archive nodes may want to retain more or +// all blocks, e.g. via a local config option min-retain-blocks. There may also +// be a need to vary retention for other nodes, e.g. sentry nodes which do not +// need historical blocks. +func (app *BaseApp) GetBlockRetentionHeight(commitHeight int64) int64 { + // pruning is disabled if minRetainBlocks is zero + if app.minRetainBlocks == 0 { + return 0 + } + + minNonZero := func(x, y int64) int64 { + switch { + case x == 0: + return y + case y == 0: + return x + case x < y: + return x + default: + return y + } + } + + // Define retentionHeight as the minimum value that satisfies all non-zero + // constraints. All blocks below (commitHeight-retentionHeight) are pruned + // from Tendermint. + var retentionHeight int64 + + // Define the number of blocks needed to protect against misbehaving validators + // which allows light clients to operate safely. Note, we piggy back of the + // evidence parameters instead of computing an estimated nubmer of blocks based + // on the unbonding period and block commitment time as the two should be + // equivalent. + cp := app.GetConsensusParams(app.deliverState.ctx) + if cp != nil && cp.Evidence != nil && cp.Evidence.MaxAgeNumBlocks > 0 { + retentionHeight = commitHeight - cp.Evidence.MaxAgeNumBlocks + } + + // Define the state pruning offset, i.e. the block offset at which the + // underlying logical database is persisted to disk. + statePruningOffset := int64(app.cms.GetPruning().KeepEvery) + if statePruningOffset > 0 { + if commitHeight > statePruningOffset { + v := commitHeight - (commitHeight % statePruningOffset) + retentionHeight = minNonZero(retentionHeight, v) + } else { + // Hitting this case means we have persisting enabled but have yet to reach + // a height in which we persist state, so we return zero regardless of other + // conditions. Otherwise, we could end up pruning blocks without having + // any state committed to disk. + return 0 + } + } + + if app.snapshotInterval > 0 && app.snapshotKeepRecent > 0 { + v := commitHeight - int64((app.snapshotInterval * uint64(app.snapshotKeepRecent))) + retentionHeight = minNonZero(retentionHeight, v) + } + + v := commitHeight - int64(app.minRetainBlocks) + retentionHeight = minNonZero(retentionHeight, v) + + if retentionHeight <= 0 { + // prune nothing in the case of a non-positive height + return 0 + } + + return retentionHeight +} + func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { if len(path) >= 2 { switch path[1] { case "simulate": txBytes := req.Data - tx, err := app.txDecoder(txBytes) + gInfo, res, err := app.Simulate(txBytes) if err != nil { - return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx")) + return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx")) + } + + simRes := &sdk.SimulationResponse{ + GasInfo: gInfo, + Result: res, } - gInfo, _, _ := app.Simulate(txBytes, tx) + bz, err := codec.ProtoMarshalJSON(simRes, app.interfaceRegistry) + if err != nil { + return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to JSON encode simulation response")) + } return abci.ResponseQuery{ Codespace: sdkerrors.RootCodespace, Height: req.Height, - Value: codec.Cdc.MustMarshalBinaryLengthPrefixed(gInfo.GasUsed), + Value: bz, } case "version": @@ -363,11 +780,6 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.R req.Path = "/" + strings.Join(path[1:], "/") - // when a client did not provide a query height, manually inject the latest - if req.Height == 0 { - req.Height = app.LastBlockHeight() - } - if req.Height <= 1 && req.Prove { return sdkerrors.QueryResult( sdkerrors.Wrap( @@ -424,48 +836,20 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) abci. return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no custom querier found for route %s", path[1])) } - // when a client did not provide a query height, manually inject the latest - if req.Height == 0 { - req.Height = app.LastBlockHeight() - } - - if req.Height <= 1 && req.Prove { - return sdkerrors.QueryResult( - sdkerrors.Wrap( - sdkerrors.ErrInvalidRequest, - "cannot query with proof when height <= 1; please provide a valid height", - ), - ) - } - - cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height) + ctx, err := app.createQueryContext(req.Height, req.Prove) if err != nil { - return sdkerrors.QueryResult( - sdkerrors.Wrapf( - sdkerrors.ErrInvalidRequest, - "failed to load state at height %d; %s (latest height: %d)", req.Height, err, app.LastBlockHeight(), - ), - ) + return sdkerrors.QueryResult(err) } - // cache wrap the commit-multistore for safety - ctx := sdk.NewContext( - cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, - ).WithMinGasPrices(app.minGasPrices) - // Passes the rest of the path as an argument to the querier. // // For example, in the path "custom/gov/proposal/test", the gov querier gets // []string{"proposal", "test"} as the path. resBytes, err := querier(ctx, path[2:], req) if err != nil { - space, code, log := sdkerrors.ABCIInfo(err, false) - return abci.ResponseQuery{ - Code: code, - Codespace: space, - Log: log, - Height: req.Height, - } + res := sdkerrors.QueryResult(err) + res.Height = req.Height + return res } return abci.ResponseQuery{ diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go new file mode 100644 index 000000000000..8a61a0aebfc2 --- /dev/null +++ b/baseapp/abci_test.go @@ -0,0 +1,141 @@ +package baseapp + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmprototypes "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGetBlockRentionHeight(t *testing.T) { + logger := defaultLogger() + db := dbm.NewMemDB() + name := t.Name() + + testCases := map[string]struct { + bapp *BaseApp + maxAgeBlocks int64 + commitHeight int64 + expected int64 + }{ + "defaults": { + bapp: NewBaseApp(name, logger, db, nil), + maxAgeBlocks: 0, + commitHeight: 499000, + expected: 0, + }, + "pruning unbonding time only": { + bapp: NewBaseApp(name, logger, db, nil, SetMinRetainBlocks(1)), + maxAgeBlocks: 362880, + commitHeight: 499000, + expected: 136120, + }, + "pruning iavl snapshot only": { + bapp: NewBaseApp( + name, logger, db, nil, + SetPruning(sdk.PruningOptions{KeepEvery: 10000}), + SetMinRetainBlocks(1), + ), + maxAgeBlocks: 0, + commitHeight: 499000, + expected: 490000, + }, + "pruning state sync snapshot only": { + bapp: NewBaseApp( + name, logger, db, nil, + SetSnapshotInterval(50000), + SetSnapshotKeepRecent(3), + SetMinRetainBlocks(1), + ), + maxAgeBlocks: 0, + commitHeight: 499000, + expected: 349000, + }, + "pruning min retention only": { + bapp: NewBaseApp( + name, logger, db, nil, + SetMinRetainBlocks(400000), + ), + maxAgeBlocks: 0, + commitHeight: 499000, + expected: 99000, + }, + "pruning all conditions": { + bapp: NewBaseApp( + name, logger, db, nil, + SetPruning(sdk.PruningOptions{KeepEvery: 10000}), + SetMinRetainBlocks(400000), + SetSnapshotInterval(50000), SetSnapshotKeepRecent(3), + ), + maxAgeBlocks: 362880, + commitHeight: 499000, + expected: 99000, + }, + "no pruning due to no persisted state": { + bapp: NewBaseApp( + name, logger, db, nil, + SetPruning(sdk.PruningOptions{KeepEvery: 10000}), + SetMinRetainBlocks(400000), + SetSnapshotInterval(50000), SetSnapshotKeepRecent(3), + ), + maxAgeBlocks: 362880, + commitHeight: 10000, + expected: 0, + }, + "disable pruning": { + bapp: NewBaseApp( + name, logger, db, nil, + SetPruning(sdk.PruningOptions{KeepEvery: 10000}), + SetMinRetainBlocks(0), + SetSnapshotInterval(50000), SetSnapshotKeepRecent(3), + ), + maxAgeBlocks: 362880, + commitHeight: 499000, + expected: 0, + }, + } + + for name, tc := range testCases { + tc := tc + + tc.bapp.SetParamStore(¶mStore{db: dbm.NewMemDB()}) + tc.bapp.InitChain(abci.RequestInitChain{ + ConsensusParams: &abci.ConsensusParams{ + Evidence: &tmprototypes.EvidenceParams{ + MaxAgeNumBlocks: tc.maxAgeBlocks, + }, + }, + }) + + t.Run(name, func(t *testing.T) { + require.Equal(t, tc.expected, tc.bapp.GetBlockRetentionHeight(tc.commitHeight)) + }) + } +} + +// Test and ensure that negative heights always cause errors. +// See issue https://github.com/cosmos/cosmos-sdk/issues/7662. +func TestBaseAppCreateQueryContextRejectsNegativeHeights(t *testing.T) { + t.Parallel() + + logger := defaultLogger() + db := dbm.NewMemDB() + name := t.Name() + app := NewBaseApp(name, logger, db, nil) + + proves := []bool{ + false, true, + } + for _, prove := range proves { + t.Run(fmt.Sprintf("prove=%t", prove), func(t *testing.T) { + sctx, err := app.createQueryContext(-10, true) + require.Error(t, err) + require.Equal(t, sctx, sdk.Context{}) + }) + } +} diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e2d5bf428864..a79aed801b6f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -1,23 +1,22 @@ package baseapp import ( - "encoding/json" "errors" "fmt" - "io/ioutil" - "os" "reflect" - "runtime/debug" "strings" "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" "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/codec/types" + "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -27,17 +26,10 @@ const ( runTxModeReCheck // Recheck a (pending) transaction after a commit runTxModeSimulate // Simulate a transaction runTxModeDeliver // Deliver a transaction - - // MainStoreKey is the string representation of the main store - MainStoreKey = "main" ) var ( _ abci.Application = (*BaseApp)(nil) - - // mainConsensusParamsKey defines a key to store the consensus params in the - // main store. - mainConsensusParamsKey = []byte("consensus_params") ) type ( @@ -54,17 +46,17 @@ type ( // BaseApp reflects the ABCI application implementation. type BaseApp struct { // nolint: maligned // initialized on creation - logger log.Logger - name string // application name from abci.Info - db dbm.DB // common DB backend - cms sdk.CommitMultiStore // Main (uncached) state - storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() - router sdk.Router // handle any kind of message - queryRouter sdk.QueryRouter // router for redirecting query calls - txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx - - // set upon LoadVersion or LoadLatestVersion. - baseKey *sdk.KVStoreKey // Main KVStore in cms + logger log.Logger + name string // application name from abci.Info + db dbm.DB // common DB backend + cms sdk.CommitMultiStore // Main (uncached) state + storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() + router sdk.Router // handle any kind of message + queryRouter sdk.QueryRouter // router for redirecting query calls + grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls + msgServiceRouter *MsgServiceRouter // router for redirecting Msg service messages + interfaceRegistry types.InterfaceRegistry + txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth initChainer sdk.InitChainer // initialize state with validators and state blob @@ -74,6 +66,11 @@ type BaseApp struct { // nolint: maligned idPeerFilter sdk.PeerFilter // filter peers by node ID fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed. + // manages snapshots, i.e. dumps of app state at certain intervals + snapshotManager *snapshots.Manager + snapshotInterval uint64 // block interval between state sync snapshots + snapshotKeepRecent uint32 // recent state sync snapshots to keep + // volatile states: // // checkState is set on InitChain and reset on Commit @@ -87,14 +84,17 @@ type BaseApp struct { // nolint: maligned // absent validators from begin block voteInfos []abci.VoteInfo - // consensus params - // TODO: Move this in the future to baseapp param store on main store. - consensusParams *abci.ConsensusParams + // paramStore is used to query for ABCI consensus parameters from an + // application parameter store. + paramStore ParamStore // The minimum gas prices a validator is willing to accept for processing a // transaction. This is mainly used for DoS and spam prevention. minGasPrices sdk.DecCoins + // initialHeight is the initial height at which we start the baseapp + initialHeight int64 + // flag for sealing options and parameters to a BaseApp sealed bool @@ -104,11 +104,30 @@ type BaseApp struct { // nolint: maligned // minimum block time (in Unix seconds) at which to halt the chain and gracefully shutdown haltTime uint64 + // minRetainBlocks defines the minimum block height offset from the current + // block being committed, such that all blocks past this offset are pruned + // from Tendermint. It is used as part of the process of determining the + // ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates + // that no blocks should be pruned. + // + // Note: Tendermint block pruning is dependant on this parameter in conunction + // with the unbonding (safety threshold) period, state pruning and state sync + // snapshot parameters to determine the correct minimum value of + // ResponseCommit.RetainHeight. + minRetainBlocks uint64 + // application's version string appVersion string - // debug flag turns on more error reporting - debug bool + // recovery handler for app.runTx method + runTxRecoveryMiddleware recoveryMiddleware + + // trace set will return full stack traces for errors in ABCI Log field + trace bool + + // indexEvents defines the set of events in the form {eventType}.{attributeKey}, + // which informs Tendermint what to index. If empty, all events will be indexed. + indexEvents map[string]struct{} } // NewBaseApp returns a reference to an initialized BaseApp. It accepts a @@ -119,19 +138,20 @@ type BaseApp struct { // nolint: maligned func NewBaseApp( name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), ) *BaseApp { - app := &BaseApp{ - logger: logger, - name: name, - db: db, - cms: store.NewCommitMultiStore(db), - storeLoader: DefaultStoreLoader, - router: NewRouter(), - queryRouter: NewQueryRouter(), - txDecoder: txDecoder, - fauxMerkleMode: false, - debug: false, + logger: logger, + name: name, + db: db, + cms: store.NewCommitMultiStore(db), + storeLoader: DefaultStoreLoader, + router: NewRouter(), + queryRouter: NewQueryRouter(), + grpcQueryRouter: NewGRPCQueryRouter(), + msgServiceRouter: NewMsgServiceRouter(), + txDecoder: txDecoder, + fauxMerkleMode: false, } + for _, option := range options { option(app) } @@ -140,6 +160,8 @@ func NewBaseApp( app.cms.SetInterBlockCache(app.interBlockCache) } + app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware() + return app } @@ -158,6 +180,9 @@ func (app *BaseApp) Logger() log.Logger { return app.logger } +// MsgServiceRouter returns the MsgServiceRouter of a BaseApp. +func (app *BaseApp) MsgServiceRouter() *MsgServiceRouter { return app.msgServiceRouter } + // MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp // multistore. func (app *BaseApp) MountStores(keys ...sdk.StoreKey) { @@ -181,8 +206,8 @@ func (app *BaseApp) MountStores(keys ...sdk.StoreKey) { } } -// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp -// multistore. +// MountKVStores mounts all IAVL or DB stores to the provided keys in the +// BaseApp multistore. func (app *BaseApp) MountKVStores(keys map[string]*sdk.KVStoreKey) { for _, key := range keys { if !app.fauxMerkleMode { @@ -195,18 +220,20 @@ func (app *BaseApp) MountKVStores(keys map[string]*sdk.KVStoreKey) { } } -// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp -// multistore. +// MountTransientStores mounts all transient stores to the provided keys in +// the BaseApp multistore. func (app *BaseApp) MountTransientStores(keys map[string]*sdk.TransientStoreKey) { for _, key := range keys { app.MountStore(key, sdk.StoreTypeTransient) } } -// MountStoreWithDB mounts a store to the provided key in the BaseApp -// multistore, using a specified DB. -func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) { - app.cms.MountStoreWithDB(key, typ, db) +// MountMemoryStores mounts all in-memory KVStores with the BaseApp's internal +// commit multi-store. +func (app *BaseApp) MountMemoryStores(keys map[string]*sdk.MemoryStoreKey) { + for _, memKey := range keys { + app.MountStore(memKey, sdk.StoreTypeMemory) + } } // MountStore mounts a store to the provided key in the BaseApp multistore, @@ -217,12 +244,13 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { // LoadLatestVersion loads the latest application version. It will panic if // called more than once on a running BaseApp. -func (app *BaseApp) LoadLatestVersion(baseKey *sdk.KVStoreKey) error { +func (app *BaseApp) LoadLatestVersion() error { err := app.storeLoader(app.cms) if err != nil { - return err + return fmt.Errorf("failed to load latest version: %w", err) } - return app.initFromMainStore(baseKey) + + return app.init() } // DefaultStoreLoader will be used by default and loads the latest version @@ -230,70 +258,15 @@ func DefaultStoreLoader(ms sdk.CommitMultiStore) error { return ms.LoadLatestVersion() } -// StoreLoaderWithUpgrade is used to prepare baseapp with a fixed StoreLoader -// pattern. This is useful in test cases, or with custom upgrade loading logic. -func StoreLoaderWithUpgrade(upgrades *storetypes.StoreUpgrades) StoreLoader { - return func(ms sdk.CommitMultiStore) error { - return ms.LoadLatestVersionAndUpgrade(upgrades) - } -} - -// UpgradeableStoreLoader can be configured by SetStoreLoader() to check for the -// existence of a given upgrade file - json encoded StoreUpgrades data. -// -// If not file is present, it will peform the default load (no upgrades to store). -// -// If the file is present, it will parse the file and execute those upgrades -// (rename or delete stores), while loading the data. It will also delete the -// upgrade file upon successful load, so that the upgrade is only applied once, -// and not re-applied on next restart -// -// This is useful for in place migrations when a store key is renamed between -// two versions of the software. (TODO: this code will move to x/upgrades -// when PR #4233 is merged, here mainly to help test the design) -func UpgradeableStoreLoader(upgradeInfoPath string) StoreLoader { - return func(ms sdk.CommitMultiStore) error { - _, err := os.Stat(upgradeInfoPath) - if os.IsNotExist(err) { - return DefaultStoreLoader(ms) - } else if err != nil { - return err - } - - // there is a migration file, let's execute - data, err := ioutil.ReadFile(upgradeInfoPath) - if err != nil { - return fmt.Errorf("cannot read upgrade file %s: %v", upgradeInfoPath, err) - } - - var upgrades storetypes.StoreUpgrades - err = json.Unmarshal(data, &upgrades) - if err != nil { - return fmt.Errorf("cannot parse upgrade file: %v", err) - } - - err = ms.LoadLatestVersionAndUpgrade(&upgrades) - if err != nil { - return fmt.Errorf("load and upgrade database: %v", err) - } - - // if we have a successful load, we delete the file - err = os.Remove(upgradeInfoPath) - if err != nil { - return fmt.Errorf("deleting upgrade file %s: %v", upgradeInfoPath, err) - } - return nil - } -} - // LoadVersion loads the BaseApp application version. It will panic if called // more than once on a running baseapp. -func (app *BaseApp) LoadVersion(version int64, baseKey *sdk.KVStoreKey) error { +func (app *BaseApp) LoadVersion(version int64) error { err := app.cms.LoadVersion(version) if err != nil { - return err + return fmt.Errorf("failed to load version %d: %w", version, err) } - return app.initFromMainStore(baseKey) + + return app.init() } // LastCommitID returns the last CommitID of the multistore. @@ -306,39 +279,29 @@ func (app *BaseApp) LastBlockHeight() int64 { return app.cms.LastCommitID().Version } -// initializes the remaining logic from app.cms -func (app *BaseApp) initFromMainStore(baseKey *sdk.KVStoreKey) error { - mainStore := app.cms.GetKVStore(baseKey) - if mainStore == nil { - return errors.New("baseapp expects MultiStore with 'main' KVStore") - } - - // memoize baseKey - if app.baseKey != nil { - panic("app.baseKey expected to be nil; duplicate init?") +func (app *BaseApp) init() error { + if app.sealed { + panic("cannot call initFromMainStore: baseapp already sealed") } - app.baseKey = baseKey - // Load the consensus params from the main store. If the consensus params are - // nil, it will be saved later during InitChain. - // - // TODO: assert that InitChain hasn't yet been called. - consensusParamsBz := mainStore.Get(mainConsensusParamsKey) - if consensusParamsBz != nil { - var consensusParams = &abci.ConsensusParams{} + // needed for the export command which inits from store but never calls initchain + app.setCheckState(tmproto.Header{}) + app.Seal() - err := proto.Unmarshal(consensusParamsBz, consensusParams) - if err != nil { - panic(err) + // make sure the snapshot interval is a multiple of the pruning KeepEvery interval + if app.snapshotManager != nil && app.snapshotInterval > 0 { + rms, ok := app.cms.(*rootmulti.Store) + if !ok { + return errors.New("state sync snapshots require a rootmulti store") + } + pruningOpts := rms.GetPruning() + if pruningOpts.KeepEvery > 0 && app.snapshotInterval%pruningOpts.KeepEvery != 0 { + return fmt.Errorf( + "state sync snapshot interval %v must be a multiple of pruning keep every interval %v", + app.snapshotInterval, pruningOpts.KeepEvery) } - - app.setConsensusParams(consensusParams) } - // needed for the export command which inits from store but never calls initchain - app.setCheckState(abci.Header{}) - app.Seal() - return nil } @@ -354,12 +317,24 @@ func (app *BaseApp) setHaltTime(haltTime uint64) { app.haltTime = haltTime } +func (app *BaseApp) setMinRetainBlocks(minRetainBlocks uint64) { + app.minRetainBlocks = minRetainBlocks +} + func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) { app.interBlockCache = cache } -func (app *BaseApp) setDebug(debug bool) { - app.debug = debug +func (app *BaseApp) setTrace(trace bool) { + app.trace = trace +} + +func (app *BaseApp) setIndexEvents(ie []string) { + app.indexEvents = make(map[string]struct{}) + + for _, e := range ie { + app.indexEvents[e] = struct{}{} + } } // Router returns the router of the BaseApp. @@ -369,6 +344,7 @@ func (app *BaseApp) Router() sdk.Router { // any routes modified which would cause unexpected routing behavior. panic("Router() on sealed BaseApp") } + return app.router } @@ -381,11 +357,11 @@ func (app *BaseApp) Seal() { app.sealed = true } // IsSealed returns true if the BaseApp is sealed and false otherwise. func (app *BaseApp) IsSealed() bool { return app.sealed } -// setCheckState sets the BaseApp's checkState with a cache-wrapped multi-store -// (i.e. a CacheMultiStore) and a new Context with the cache-wrapped multi-store, +// setCheckState sets the BaseApp's checkState with a branched multi-store +// (i.e. a CacheMultiStore) and a new Context with the same multi-store branch, // provided header, and minimum gas prices set. It is set on InitChain and reset // on Commit. -func (app *BaseApp) setCheckState(header abci.Header) { +func (app *BaseApp) setCheckState(header tmproto.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ ms: ms, @@ -393,11 +369,11 @@ func (app *BaseApp) setCheckState(header abci.Header) { } } -// setDeliverState sets the BaseApp's deliverState with a cache-wrapped multi-store -// (i.e. a CacheMultiStore) and a new Context with the cache-wrapped multi-store, +// setDeliverState sets the BaseApp's deliverState with a branched multi-store +// (i.e. a CacheMultiStore) and a new Context with the same multi-store branch, // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. -func (app *BaseApp) setDeliverState(header abci.Header) { +func (app *BaseApp) setDeliverState(header tmproto.Header) { ms := app.cms.CacheMultiStore() app.deliverState = &state{ ms: ms, @@ -405,30 +381,72 @@ func (app *BaseApp) setDeliverState(header abci.Header) { } } -// setConsensusParams memoizes the consensus params. -func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) { - app.consensusParams = consensusParams +// GetConsensusParams returns the current consensus parameters from the BaseApp's +// ParamStore. If the BaseApp has no ParamStore defined, nil is returned. +func (app *BaseApp) GetConsensusParams(ctx sdk.Context) *abci.ConsensusParams { + if app.paramStore == nil { + return nil + } + + cp := new(abci.ConsensusParams) + + if app.paramStore.Has(ctx, ParamStoreKeyBlockParams) { + var bp abci.BlockParams + + app.paramStore.Get(ctx, ParamStoreKeyBlockParams, &bp) + cp.Block = &bp + } + + if app.paramStore.Has(ctx, ParamStoreKeyEvidenceParams) { + var ep tmproto.EvidenceParams + + app.paramStore.Get(ctx, ParamStoreKeyEvidenceParams, &ep) + cp.Evidence = &ep + } + + if app.paramStore.Has(ctx, ParamStoreKeyValidatorParams) { + var vp tmproto.ValidatorParams + + app.paramStore.Get(ctx, ParamStoreKeyValidatorParams, &vp) + cp.Validator = &vp + } + + return cp } -// setConsensusParams stores the consensus params to the main store. -func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) { - consensusParamsBz, err := proto.Marshal(consensusParams) - if err != nil { - panic(err) +// AddRunTxRecoveryHandler adds custom app.runTx method panic handlers. +func (app *BaseApp) AddRunTxRecoveryHandler(handlers ...RecoveryHandler) { + for _, h := range handlers { + app.runTxRecoveryMiddleware = newRecoveryMiddleware(h, app.runTxRecoveryMiddleware) } - mainStore := app.cms.GetKVStore(app.baseKey) - mainStore.Set(mainConsensusParamsKey, consensusParamsBz) +} + +// StoreConsensusParams sets the consensus parameters to the baseapp's param store. +func (app *BaseApp) StoreConsensusParams(ctx sdk.Context, cp *abci.ConsensusParams) { + if app.paramStore == nil { + panic("cannot store consensus params with no params store set") + } + + if cp == nil { + return + } + + app.paramStore.Set(ctx, ParamStoreKeyBlockParams, cp.Block) + app.paramStore.Set(ctx, ParamStoreKeyEvidenceParams, cp.Evidence) + app.paramStore.Set(ctx, ParamStoreKeyValidatorParams, cp.Validator) } // getMaximumBlockGas gets the maximum gas from the consensus params. It panics // if maximum block gas is less than negative one and returns zero if negative // one. -func (app *BaseApp) getMaximumBlockGas() uint64 { - if app.consensusParams == nil || app.consensusParams.Block == nil { +func (app *BaseApp) getMaximumBlockGas(ctx sdk.Context) uint64 { + cp := app.GetConsensusParams(ctx) + if cp == nil || cp.Block == nil { return 0 } - maxGas := app.consensusParams.Block.MaxGas + maxGas := cp.Block.MaxGas + switch { case maxGas < -1: panic(fmt.Sprintf("invalid maximum block gas: %d", maxGas)) @@ -446,9 +464,23 @@ func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error { return fmt.Errorf("invalid height: %d", req.Header.Height) } - prevHeight := app.LastBlockHeight() - if req.Header.Height != prevHeight+1 { - return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, prevHeight+1) + // expectedHeight holds the expected height to validate. + var expectedHeight int64 + if app.LastBlockHeight() == 0 && app.initialHeight > 1 { + // In this case, we're validating the first block of the chain (no + // previous commit). The height we're expecting is the initial height. + expectedHeight = app.initialHeight + } else { + // This case can means two things: + // - either there was already a previous commit in the store, in which + // case we increment the version from there, + // - or there was no previous commit, and initial version was not set, + // in which case we start at version 1. + expectedHeight = app.LastBlockHeight() + 1 + } + + if req.Header.Height != expectedHeight { + return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, expectedHeight) } return nil @@ -484,12 +516,14 @@ func (app *BaseApp) getState(mode runTxMode) *state { func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context { ctx := app.getState(mode).ctx. WithTxBytes(txBytes). - WithVoteInfos(app.voteInfos). - WithConsensusParams(app.consensusParams) + WithVoteInfos(app.voteInfos) + + ctx = ctx.WithConsensusParams(app.GetConsensusParams(ctx)) if mode == runTxModeReCheck { ctx = ctx.WithIsReCheckTx(true) } + if mode == runTxModeSimulate { ctx, _ = ctx.CacheContext() } @@ -498,7 +532,7 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context } // cacheTxContext returns a new context based off of the provided context with -// a cache wrapped multi-store. +// a branched multi-store. func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheMultiStore) { ms := ctx.MultiStore() // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 @@ -523,7 +557,7 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context // Note, gas execution info is always returned. A reference to a Result is // returned if the tx does not run out of gas and if all the messages are valid // and execute successfully. An error is returned otherwise. -func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk.GasInfo, result *sdk.Result, err error) { +func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, result *sdk.Result, err error) { // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. @@ -545,26 +579,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. defer func() { if r := recover(); r != nil { - switch rType := r.(type) { - // TODO: Use ErrOutOfGas instead of ErrorOutOfGas which would allow us - // to keep the stracktrace. - case sdk.ErrorOutOfGas: - err = sdkerrors.Wrap( - sdkerrors.ErrOutOfGas, fmt.Sprintf( - "out of gas in location: %v; gasWanted: %d, gasUsed: %d", - rType.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), - ), - ) - - default: - err = sdkerrors.Wrap( - sdkerrors.ErrPanic, fmt.Sprintf( - "recovered: %v\nstack:\n%v", r, string(debug.Stack()), - ), - ) - } - - result = nil + recoveryMW := newOutOfGasRecoveryMiddleware(gasWanted, ctx, app.runTxRecoveryMiddleware) + err, result = processRecovery(r, recoveryMW), nil } gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed()} @@ -587,16 +603,24 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. } }() + tx, err := app.txDecoder(txBytes) + if err != nil { + return sdk.GasInfo{}, nil, err + } + msgs := tx.GetMsgs() if err := validateBasicTxMsgs(msgs); err != nil { return sdk.GasInfo{}, nil, err } + var events sdk.Events if app.anteHandler != nil { - var anteCtx sdk.Context - var msCache sdk.CacheMultiStore + var ( + anteCtx sdk.Context + msCache sdk.CacheMultiStore + ) - // Cache wrap context before AnteHandler call in case it aborts. + // Branch context before AnteHandler call in case it aborts. // This is required for both CheckTx and DeliverTx. // Ref: https://github.com/cosmos/cosmos-sdk/issues/2772 // @@ -604,12 +628,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. // writes do not happen if aborted/failed. This may have some // performance benefits, but it'll be more difficult to get right. anteCtx, msCache = app.cacheTxContext(ctx, txBytes) - + anteCtx = anteCtx.WithEventManager(sdk.NewEventManager()) newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate) + if !newCtx.IsZero() { - // At this point, newCtx.MultiStore() is cache-wrapped, or something else - // replaced by the AnteHandler. We want the original multistore, not one - // which was cache-wrapped for the AnteHandler. + // At this point, newCtx.MultiStore() is a store branch, or something else + // replaced by the AnteHandler. We want the original multistore. // // Also, in the case of the tx aborting, we need to track gas consumed via // the instantiated gas meter in the AnteHandler, so we update the context @@ -617,6 +641,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. ctx = newCtx.WithMultiStore(ms) } + events = ctx.EventManager().Events() + // GasMeter expected to be set in AnteHandler gasWanted = ctx.GasMeter().Limit() @@ -627,9 +653,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. msCache.Write() } - // Create a new Context based off of the existing Context with a cache-wrapped - // MultiStore in case message processing fails. At this point, the MultiStore - // is doubly cached-wrapped. + // Create a new Context based off of the existing Context with a MultiStore branch + // in case message processing fails. At this point, the MultiStore + // is a branch of a branch. runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) // Attempt to execute all messages and only update state if all messages pass @@ -638,6 +664,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. result, err = app.runMsgs(runMsgCtx, msgs, mode) if err == nil && mode == runTxModeDeliver { msCache.Write() + + if len(events) > 0 { + // append the events in the order of occurrence + result.Events = append(events.ToABCIEvents(), result.Events...) + } } return gInfo, result, err @@ -650,8 +681,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. // Result is returned. The caller must not commit state if an error is returned. func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*sdk.Result, error) { msgLogs := make(sdk.ABCIMessageLogs, 0, len(msgs)) - data := make([]byte, 0, len(msgs)) events := sdk.EmptyEvents() + txMsgData := &sdk.TxMsgData{ + Data: make([]*sdk.MsgData, 0, len(msgs)), + } // NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter. for i, msg := range msgs { @@ -660,34 +693,59 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s break } - msgRoute := msg.Route() - handler := app.router.Route(ctx, msgRoute) - if handler == nil { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i) + var ( + msgEvents sdk.Events + msgResult *sdk.Result + msgFqName string + err error + ) + + if svcMsg, ok := msg.(sdk.ServiceMsg); ok { + msgFqName = svcMsg.MethodName + handler := app.msgServiceRouter.Handler(msgFqName) + if handler == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message service method: %s; message index: %d", msgFqName, i) + } + msgResult, err = handler(ctx, svcMsg.Request) + } else { + // legacy sdk.Msg routing + msgRoute := msg.Route() + msgFqName = msg.Type() + handler := app.router.Route(ctx, msgRoute) + if handler == nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i) + } + + msgResult, err = handler(ctx, msg) } - msgResult, err := handler(ctx, msg) if err != nil { return nil, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i) } - msgEvents := sdk.Events{ - sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type())), + msgEvents = sdk.Events{ + sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msgFqName)), } - msgEvents = msgEvents.AppendEvents(msgResult.Events) + msgEvents = msgEvents.AppendEvents(msgResult.GetEvents()) // append message events, data and logs // // Note: Each message result's data must be length-prefixed in order to // separate each result. events = events.AppendEvents(msgEvents) - data = append(data, msgResult.Data...) - msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint16(i), msgResult.Log, msgEvents)) + + txMsgData.Data = append(txMsgData.Data, &sdk.MsgData{MsgType: msg.Type(), Data: msgResult.Data}) + msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint32(i), msgResult.Log, msgEvents)) + } + + data, err := proto.Marshal(txMsgData) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to marshal tx data") } return &sdk.Result{ Data: data, Log: strings.TrimSpace(msgLogs.String()), - Events: events, + Events: events.ToABCIEvents(), }, nil } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 6ee5aabbb0a8..b0eace4d3294 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -3,24 +3,33 @@ package baseapp import ( "bytes" "encoding/binary" + "encoding/json" "fmt" "io/ioutil" + "math/rand" "os" + "strings" "sync" "testing" + "time" + "github.com/gogo/protobuf/jsonpb" "github.com/stretchr/testify/assert" "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" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/snapshots" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/rootmulti" store "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) var ( @@ -28,6 +37,43 @@ var ( capKey2 = sdk.NewKVStoreKey("key2") ) +type paramStore struct { + db *dbm.MemDB +} + +func (ps *paramStore) Set(_ sdk.Context, key []byte, value interface{}) { + bz, err := json.Marshal(value) + if err != nil { + panic(err) + } + + ps.db.Set(key, bz) +} + +func (ps *paramStore) Has(_ sdk.Context, key []byte) bool { + ok, err := ps.db.Has(key) + if err != nil { + panic(err) + } + + return ok +} + +func (ps *paramStore) Get(_ sdk.Context, key []byte, ptr interface{}) { + bz, err := ps.db.Get(key) + if err != nil { + panic(err) + } + + if len(bz) == 0 { + return + } + + if err := json.Unmarshal(bz, ptr); err != nil { + panic(err) + } +} + func defaultLogger() log.Logger { return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") } @@ -35,40 +81,117 @@ func defaultLogger() log.Logger { func newBaseApp(name string, options ...func(*BaseApp)) *BaseApp { logger := defaultLogger() db := dbm.NewMemDB() - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) return NewBaseApp(name, logger, db, testTxDecoder(codec), options...) } -func registerTestCodec(cdc *codec.Codec) { +func registerTestCodec(cdc *codec.LegacyAmino) { // register Tx, Msg - sdk.RegisterCodec(cdc) + sdk.RegisterLegacyAminoCodec(cdc) // register test types cdc.RegisterConcrete(&txTest{}, "cosmos-sdk/baseapp/txTest", nil) cdc.RegisterConcrete(&msgCounter{}, "cosmos-sdk/baseapp/msgCounter", nil) cdc.RegisterConcrete(&msgCounter2{}, "cosmos-sdk/baseapp/msgCounter2", nil) + cdc.RegisterConcrete(&msgKeyValue{}, "cosmos-sdk/baseapp/msgKeyValue", nil) cdc.RegisterConcrete(&msgNoRoute{}, "cosmos-sdk/baseapp/msgNoRoute", nil) } +// aminoTxEncoder creates a amino TxEncoder for testing purposes. +func aminoTxEncoder() sdk.TxEncoder { + cdc := codec.NewLegacyAmino() + registerTestCodec(cdc) + + return legacytx.StdTxConfig{Cdc: cdc}.TxEncoder() +} + // simple one store baseapp func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { app := newBaseApp(t.Name(), options...) require.Equal(t, t.Name(), app.Name()) - // no stores are mounted - require.Panics(t, func() { - app.LoadLatestVersion(capKey1) - }) - app.MountStores(capKey1, capKey2) + app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) // stores are mounted - err := app.LoadLatestVersion(capKey1) + err := app.LoadLatestVersion() require.Nil(t, err) return app } +// simple one store baseapp with data and snapshots. Each tx is 1 MB in size (uncompressed). +func setupBaseAppWithSnapshots(t *testing.T, blocks uint, blockTxs int, options ...func(*BaseApp)) (*BaseApp, func()) { + codec := codec.NewLegacyAmino() + registerTestCodec(codec) + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + kv := msg.(*msgKeyValue) + bapp.cms.GetCommitKVStore(capKey2).Set(kv.Key, kv.Value) + return &sdk.Result{}, nil + })) + } + + snapshotInterval := uint64(2) + snapshotTimeout := 1 * time.Minute + snapshotDir, err := ioutil.TempDir("", "baseapp") + require.NoError(t, err) + snapshotStore, err := snapshots.NewStore(dbm.NewMemDB(), snapshotDir) + require.NoError(t, err) + teardown := func() { + os.RemoveAll(snapshotDir) + } + + app := setupBaseApp(t, append(options, + SetSnapshotStore(snapshotStore), + SetSnapshotInterval(snapshotInterval), + SetPruning(sdk.PruningOptions{KeepEvery: 1}), + routerOpt)...) + + app.InitChain(abci.RequestInitChain{}) + + r := rand.New(rand.NewSource(3920758213583)) + keyCounter := 0 + for height := int64(1); height <= int64(blocks); height++ { + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: height}}) + for txNum := 0; txNum < blockTxs; txNum++ { + tx := txTest{Msgs: []sdk.Msg{}} + for msgNum := 0; msgNum < 100; msgNum++ { + key := []byte(fmt.Sprintf("%v", keyCounter)) + value := make([]byte, 10000) + _, err := r.Read(value) + require.NoError(t, err) + tx.Msgs = append(tx.Msgs, msgKeyValue{Key: key, Value: value}) + keyCounter++ + } + txBytes, err := codec.MarshalBinaryBare(tx) + require.NoError(t, err) + resp := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.True(t, resp.IsOK(), "%v", resp.String()) + } + app.EndBlock(abci.RequestEndBlock{Height: height}) + app.Commit() + + // Wait for snapshot to be taken, since it happens asynchronously. + if uint64(height)%snapshotInterval == 0 { + start := time.Now() + for { + if time.Since(start) > snapshotTimeout { + t.Errorf("timed out waiting for snapshot after %v", snapshotTimeout) + } + snapshot, err := snapshotStore.Get(uint64(height), snapshottypes.CurrentFormat) + require.NoError(t, err) + if snapshot != nil { + break + } + time.Sleep(100 * time.Millisecond) + } + } + } + + return app, teardown +} + func TestMountStores(t *testing.T) { app := setupBaseApp(t) @@ -89,9 +212,7 @@ func TestLoadVersion(t *testing.T) { app := NewBaseApp(name, logger, db, nil, pruningOpt) // make a cap key and mount the store - capKey := sdk.NewKVStoreKey(MainStoreKey) - app.MountStores(capKey) - err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) emptyCommitID := sdk.CommitID{} @@ -103,29 +224,28 @@ func TestLoadVersion(t *testing.T) { require.Equal(t, emptyCommitID, lastID) // execute a block, collect commit ID - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res := app.Commit() commitID1 := sdk.CommitID{Version: 1, Hash: res.Data} // execute a block, collect commit ID - header = abci.Header{Height: 2} + header = tmproto.Header{Height: 2} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res = app.Commit() commitID2 := sdk.CommitID{Version: 2, Hash: res.Data} // reload with LoadLatestVersion app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadLatestVersion(capKey) + app.MountStores() + err = app.LoadLatestVersion() require.Nil(t, err) testLoadVersionHelper(t, app, int64(2), commitID2) // reload with LoadVersion, see if you can commit the same block and get // the same result app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadVersion(1, capKey) + err = app.LoadVersion(1) require.Nil(t, err) testLoadVersionHelper(t, app, int64(1), commitID1) app.BeginBlock(abci.RequestBeginBlock{Header: header}) @@ -137,18 +257,6 @@ func useDefaultLoader(app *BaseApp) { app.SetStoreLoader(DefaultStoreLoader) } -func useUpgradeLoader(upgrades *store.StoreUpgrades) func(*BaseApp) { - return func(app *BaseApp) { - app.SetStoreLoader(StoreLoaderWithUpgrade(upgrades)) - } -} - -func useFileUpgradeLoader(upgradeInfoPath string) func(*BaseApp) { - return func(app *BaseApp) { - app.SetStoreLoader(UpgradeableStoreLoader(upgradeInfoPath)) - } -} - func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { rs := rootmulti.NewStore(db) rs.SetPruning(store.PruneNothing) @@ -168,7 +276,7 @@ func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { rs := rootmulti.NewStore(db) - rs.SetPruning(store.PruneSyncable) + rs.SetPruning(store.PruneDefault) key := sdk.NewKVStoreKey(storeKey) rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) err := rs.LoadLatestVersion() @@ -184,19 +292,6 @@ func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestSetLoader(t *testing.T) { - // write a renamer to a file - f, err := ioutil.TempFile("", "upgrade-*.json") - require.NoError(t, err) - data := []byte(`{"renamed":[{"old_key": "bnk", "new_key": "banker"}]}`) - _, err = f.Write(data) - require.NoError(t, err) - configName := f.Name() - require.NoError(t, f.Close()) - - // make sure it exists before running everything - _, err = os.Stat(configName) - require.NoError(t, err) - cases := map[string]struct { setLoader func(*BaseApp) origStoreKey string @@ -211,26 +306,6 @@ func TestSetLoader(t *testing.T) { origStoreKey: "foo", loadStoreKey: "foo", }, - "rename with inline opts": { - setLoader: useUpgradeLoader(&store.StoreUpgrades{ - Renamed: []store.StoreRename{{ - OldKey: "foo", - NewKey: "bar", - }}, - }), - origStoreKey: "foo", - loadStoreKey: "bar", - }, - "file loader with missing file": { - setLoader: useFileUpgradeLoader(configName + "randomchars"), - origStoreKey: "bnk", - loadStoreKey: "bnk", - }, - "file loader with existing file": { - setLoader: useFileUpgradeLoader(configName), - origStoreKey: "bnk", - loadStoreKey: "banker", - }, } k := []byte("key") @@ -249,14 +324,12 @@ func TestSetLoader(t *testing.T) { opts = append(opts, tc.setLoader) } app := NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) - capKey := sdk.NewKVStoreKey(MainStoreKey) - app.MountStores(capKey) app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) - err := app.LoadLatestVersion(capKey) + err := app.LoadLatestVersion() require.Nil(t, err) // "execute" one block - app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: 2}}) + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 2}}) res := app.Commit() require.NotNil(t, res.Data) @@ -265,15 +338,11 @@ func TestSetLoader(t *testing.T) { checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil) }) } - - // ensure config file was deleted - _, err = os.Stat(configName) - require.True(t, os.IsNotExist(err)) } func TestAppVersionSetterGetter(t *testing.T) { logger := defaultLogger() - pruningOpt := SetPruning(store.PruneSyncable) + pruningOpt := SetPruning(store.PruneDefault) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) @@ -298,39 +367,37 @@ func TestLoadVersionInvalid(t *testing.T) { name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) - capKey := sdk.NewKVStoreKey(MainStoreKey) - app.MountStores(capKey) - err := app.LoadLatestVersion(capKey) + err := app.LoadLatestVersion() require.Nil(t, err) // require error when loading an invalid version - err = app.LoadVersion(-1, capKey) + err = app.LoadVersion(-1) require.Error(t, err) - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) res := app.Commit() commitID1 := sdk.CommitID{Version: 1, Hash: res.Data} // create a new app with the stores mounted under the same cap key app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) // require we can load the latest version - err = app.LoadVersion(1, capKey) + err = app.LoadVersion(1) require.Nil(t, err) testLoadVersionHelper(t, app, int64(1), commitID1) // require error when loading an invalid version - err = app.LoadVersion(2, capKey) + err = app.LoadVersion(2) require.Error(t, err) } func TestLoadVersionPruning(t *testing.T) { logger := log.NewNopLogger() pruningOptions := store.PruningOptions{ - KeepEvery: 2, - SnapshotEvery: 6, + KeepRecent: 2, + KeepEvery: 3, + Interval: 1, } pruningOpt := SetPruning(pruningOptions) db := dbm.NewMemDB() @@ -338,9 +405,10 @@ func TestLoadVersionPruning(t *testing.T) { app := NewBaseApp(name, logger, db, nil, pruningOpt) // make a cap key and mount the store - capKey := sdk.NewKVStoreKey(MainStoreKey) + capKey := sdk.NewKVStoreKey("key1") app.MountStores(capKey) - err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + + err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) emptyCommitID := sdk.CommitID{} @@ -351,61 +419,33 @@ func TestLoadVersionPruning(t *testing.T) { require.Equal(t, int64(0), lastHeight) require.Equal(t, emptyCommitID, lastID) - // execute a block - header := abci.Header{Height: 1} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res := app.Commit() + var lastCommitID sdk.CommitID - // execute a block, collect commit ID - header = abci.Header{Height: 2} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID2 := sdk.CommitID{Version: 2, Hash: res.Data} - - // execute a block - header = abci.Header{Height: 3} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID3 := sdk.CommitID{Version: 3, Hash: res.Data} - - // reload with LoadLatestVersion, check it loads last flushed version - app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadLatestVersion(capKey) - require.Nil(t, err) - testLoadVersionHelper(t, app, int64(2), commitID2) - - // re-execute block 3 and check it is same CommitID - header = abci.Header{Height: 3} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - recommitID3 := sdk.CommitID{Version: 3, Hash: res.Data} - require.Equal(t, commitID3, recommitID3, "Commits of identical blocks not equal after reload") + // Commit seven blocks, of which 7 (latest) is kept in addition to 6, 5 + // (keep recent) and 3 (keep every). + for i := int64(1); i <= 7; i++ { + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}}) + res := app.Commit() + lastCommitID = sdk.CommitID{Version: i, Hash: res.Data} + } - // execute a block, collect commit ID - header = abci.Header{Height: 4} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID4 := sdk.CommitID{Version: 4, Hash: res.Data} + for _, v := range []int64{1, 2, 4} { + _, err = app.cms.CacheMultiStoreWithVersion(v) + require.NoError(t, err) + } - // execute a block - header = abci.Header{Height: 5} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() + for _, v := range []int64{3, 5, 6, 7} { + _, err = app.cms.CacheMultiStoreWithVersion(v) + require.NoError(t, err) + } - // reload with LoadLatestVersion, check it loads last flushed version + // reload with LoadLatestVersion, check it loads last version app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores(capKey) - err = app.LoadLatestVersion(capKey) - require.Nil(t, err) - testLoadVersionHelper(t, app, int64(4), commitID4) - // reload with LoadVersion of previous flushed version - // and check it fails since previous flush should be pruned - app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadVersion(2, capKey) - require.NotNil(t, err) + err = app.LoadLatestVersion() + require.Nil(t, err) + testLoadVersionHelper(t, app, int64(7), lastCommitID) } func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) { @@ -431,12 +471,12 @@ func testChangeNameHelper(name string) func(*BaseApp) { // Test that txs can be unmarshalled and read and that // correct error codes are returned when not func TestTxDecoder(t *testing.T) { - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) app := newBaseApp(t.Name()) tx := newTxCounter(1, 0) - txBytes := codec.MustMarshalBinaryLengthPrefixed(tx) + txBytes := codec.MustMarshalBinaryBare(tx) dTx, err := app.txDecoder(txBytes) require.NoError(t, err) @@ -517,7 +557,7 @@ func TestInitChainer(t *testing.T) { db := dbm.NewMemDB() logger := defaultLogger() app := NewBaseApp(name, logger, db, nil) - capKey := sdk.NewKVStoreKey(MainStoreKey) + capKey := sdk.NewKVStoreKey("main") capKey2 := sdk.NewKVStoreKey("key2") app.MountStores(capKey, capKey2) @@ -543,11 +583,20 @@ func TestInitChainer(t *testing.T) { app.SetInitChainer(initChainer) // stores are mounted and private members are set - sealing baseapp - err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + err := app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) require.Equal(t, int64(0), app.LastBlockHeight()) - app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty + initChainRes := app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty + + // The AppHash returned by a new chain is the sha256 hash of "". + // $ echo -n '' | sha256sum + // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + require.Equal( + t, + []byte{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, + initChainRes.AppHash, + ) // assert that chainID is set correctly in InitChain chainID := app.deliverState.ctx.ChainID() @@ -565,7 +614,7 @@ func TestInitChainer(t *testing.T) { app = NewBaseApp(name, logger, db, nil) app.SetInitChainer(initChainer) app.MountStores(capKey, capKey2) - err = app.LoadLatestVersion(capKey) // needed to make stores non-nil + err = app.LoadLatestVersion() // needed to make stores non-nil require.Nil(t, err) require.Equal(t, int64(1), app.LastBlockHeight()) @@ -574,7 +623,7 @@ func TestInitChainer(t *testing.T) { require.Equal(t, value, res.Value) // commit and ensure we can still query - header := abci.Header{Height: app.LastBlockHeight() + 1} + header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.Commit() @@ -582,6 +631,52 @@ func TestInitChainer(t *testing.T) { require.Equal(t, value, res.Value) } +func TestInitChain_WithInitialHeight(t *testing.T) { + name := t.Name() + db := dbm.NewMemDB() + logger := defaultLogger() + app := NewBaseApp(name, logger, db, nil) + + app.InitChain( + abci.RequestInitChain{ + InitialHeight: 3, + }, + ) + app.Commit() + + require.Equal(t, int64(3), app.LastBlockHeight()) +} + +func TestBeginBlock_WithInitialHeight(t *testing.T) { + name := t.Name() + db := dbm.NewMemDB() + logger := defaultLogger() + app := NewBaseApp(name, logger, db, nil) + + app.InitChain( + abci.RequestInitChain{ + InitialHeight: 3, + }, + ) + + require.PanicsWithError(t, "invalid height: 4; expected: 3", func() { + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{ + Height: 4, + }, + }) + }) + + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{ + Height: 3, + }, + }) + app.Commit() + + require.Equal(t, int64(3), app.LastBlockHeight()) +} + // Simple tx with a list of Msgs. type txTest struct { Msgs []sdk.Msg @@ -606,6 +701,7 @@ func (tx txTest) ValidateBasic() error { return nil } const ( routeMsgCounter = "msgCounter" routeMsgCounter2 = "msgCounter2" + routeMsgKeyValue = "msgKeyValue" ) // ValidateBasic() fails on negative counters. @@ -615,6 +711,11 @@ type msgCounter struct { FailOnHandler bool } +// dummy implementation of proto.Message +func (msg msgCounter) Reset() {} +func (msg msgCounter) String() string { return "TODO" } +func (msg msgCounter) ProtoMessage() {} + // Implements Msg func (msg msgCounter) Route() string { return routeMsgCounter } func (msg msgCounter) Type() string { return "counter1" } @@ -655,6 +756,11 @@ type msgCounter2 struct { Counter int64 } +// dummy implementation of proto.Message +func (msg msgCounter2) Reset() {} +func (msg msgCounter2) String() string { return "TODO" } +func (msg msgCounter2) ProtoMessage() {} + // Implements Msg func (msg msgCounter2) Route() string { return routeMsgCounter2 } func (msg msgCounter2) Type() string { return "counter2" } @@ -667,15 +773,38 @@ func (msg msgCounter2) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counter should be a non-negative integer") } +// A msg that sets a key/value pair. +type msgKeyValue struct { + Key []byte + Value []byte +} + +func (msg msgKeyValue) Reset() {} +func (msg msgKeyValue) String() string { return "TODO" } +func (msg msgKeyValue) ProtoMessage() {} +func (msg msgKeyValue) Route() string { return routeMsgKeyValue } +func (msg msgKeyValue) Type() string { return "keyValue" } +func (msg msgKeyValue) GetSignBytes() []byte { return nil } +func (msg msgKeyValue) GetSigners() []sdk.AccAddress { return nil } +func (msg msgKeyValue) ValidateBasic() error { + if msg.Key == nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "key cannot be nil") + } + if msg.Value == nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "value cannot be nil") + } + return nil +} + // amino decode -func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { +func testTxDecoder(cdc *codec.LegacyAmino) sdk.TxDecoder { return func(txBytes []byte) (sdk.Tx, error) { var tx txTest if len(txBytes) == 0 { return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty") } - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) + err := cdc.UnmarshalBinaryBare(txBytes, &tx) if err != nil { return nil, sdkerrors.ErrTxDecode } @@ -685,25 +814,39 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { } func anteHandlerTxTest(t *testing.T, capKey sdk.StoreKey, storeKey []byte) sdk.AnteHandler { - return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { + return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { store := ctx.KVStore(capKey) txTest := tx.(txTest) if txTest.FailOnAnte { - return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") } - _, err = incrementingCounter(t, store, storeKey, txTest.Counter) + _, err := incrementingCounter(t, store, storeKey, txTest.Counter) if err != nil { - return newCtx, err + return ctx, err } - return newCtx, nil + ctx.EventManager().EmitEvents( + counterEvent("ante_handler", txTest.Counter), + ) + + return ctx, nil + } +} + +func counterEvent(evType string, msgCount int64) sdk.Events { + return sdk.Events{ + sdk.NewEvent( + evType, + sdk.NewAttribute("update_counter", fmt.Sprintf("%d", msgCount)), + ), } } func handlerMsgCounter(t *testing.T, capKey sdk.StoreKey, deliverKey []byte) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) store := ctx.KVStore(capKey) var msgCount int64 @@ -714,12 +857,21 @@ func handlerMsgCounter(t *testing.T, capKey sdk.StoreKey, deliverKey []byte) sdk } msgCount = m.Counter - case *msgCounter2: msgCount = m.Counter } - return incrementingCounter(t, store, deliverKey, msgCount) + ctx.EventManager().EmitEvents( + counterEvent(sdk.EventTypeMessage, msgCount), + ) + + res, err := incrementingCounter(t, store, deliverKey, msgCount) + if err != nil { + return nil, err + } + + res.Events = ctx.EventManager().Events().ToABCIEvents() + return res, nil } } @@ -767,9 +919,9 @@ func TestCheckTx(t *testing.T) { anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, counterKey)) } routerOpt := func(bapp *BaseApp) { // TODO: can remove this once CheckTx doesnt process msgs. - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + bapp.Router().AddRoute(sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return &sdk.Result{}, nil - }) + })) } app := setupBaseApp(t, anteOpt, routerOpt) @@ -778,15 +930,16 @@ func TestCheckTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) for i := int64(0); i < nTxs; i++ { - tx := newTxCounter(i, 0) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) + tx := newTxCounter(i, 0) // no messages + txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) r := app.CheckTx(abci.RequestCheckTx{Tx: txBytes}) - assert.True(t, r.IsOK(), fmt.Sprintf("%v", r)) + require.Empty(t, r.GetEvents()) + require.True(t, r.IsOK(), fmt.Sprintf("%v", r)) } checkStateStore := app.checkState.ctx.KVStore(capKey1) @@ -796,7 +949,7 @@ func TestCheckTx(t *testing.T) { require.Equal(t, nTxs, storedCounter) // If a block is committed, CheckTx state should be reset. - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.EndBlock(abci.RequestEndBlock{}) app.Commit() @@ -816,32 +969,37 @@ func TestDeliverTx(t *testing.T) { // test increments in the handler deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) nBlocks := 3 txPerHeight := 5 for blockN := 0; blockN < nBlocks; blockN++ { - header := abci.Header{Height: int64(blockN) + 1} + header := tmproto.Header{Height: int64(blockN) + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) + txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) + events := res.GetEvents() + require.Len(t, events, 3, "should contain ante handler, message type and counter events respectively") + require.Equal(t, sdk.MarkEventsToIndex(counterEvent("ante_handler", counter).ToABCIEvents(), map[string]struct{}{})[0], events[0], "ante handler event") + require.Equal(t, sdk.MarkEventsToIndex(counterEvent(sdk.EventTypeMessage, counter).ToABCIEvents(), map[string]struct{}{})[0], events[2], "msg handler update counter event") } app.EndBlock(abci.RequestEndBlock{}) @@ -865,23 +1023,25 @@ func TestMultiMsgDeliverTx(t *testing.T) { deliverKey := []byte("deliver-key") deliverKey2 := []byte("deliver-key2") routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) - bapp.Router().AddRoute(routeMsgCounter2, handlerMsgCounter(t, capKey1, deliverKey2)) + r1 := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + r2 := sdk.NewRoute(routeMsgCounter2, handlerMsgCounter(t, capKey1, deliverKey2)) + bapp.Router().AddRoute(r1) + bapp.Router().AddRoute(r2) } app := setupBaseApp(t, anteOpt, routerOpt) // Create same codec used in txDecoder - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) // run a multi-msg tx // with all msgs the same route - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(0, 0, 1, 2) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) + txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -901,7 +1061,7 @@ func TestMultiMsgDeliverTx(t *testing.T) { tx = newTxCounter(1, 3) tx.Msgs = append(tx.Msgs, msgCounter2{0}) tx.Msgs = append(tx.Msgs, msgCounter2{1}) - txBytes, err = codec.MarshalBinaryLengthPrefixed(tx) + txBytes, err = codec.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) @@ -941,10 +1101,11 @@ func TestSimulateTx(t *testing.T) { } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx.GasMeter().ConsumeGas(gasConsumed, "test") return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) @@ -952,27 +1113,27 @@ func TestSimulateTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - cdc := codec.New() + cdc := codec.NewLegacyAmino() registerTestCodec(cdc) nBlocks := 3 for blockN := 0; blockN < nBlocks; blockN++ { count := int64(blockN + 1) - header := abci.Header{Height: count} + header := tmproto.Header{Height: count} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(count, count) - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err := cdc.MarshalBinaryBare(tx) require.Nil(t, err) // simulate a message, check gas reported - gInfo, result, err := app.Simulate(txBytes, tx) + gInfo, result, err := app.Simulate(txBytes) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) // simulate again, same result - gInfo, result, err = app.Simulate(txBytes, tx) + gInfo, result, err = app.Simulate(txBytes) require.NoError(t, err) require.NotNil(t, result) require.Equal(t, gasConsumed, gInfo.GasUsed) @@ -985,10 +1146,14 @@ func TestSimulateTx(t *testing.T) { queryResult := app.Query(query) require.True(t, queryResult.IsOK(), queryResult.Log) - var res uint64 - err = codec.Cdc.UnmarshalBinaryLengthPrefixed(queryResult.Value, &res) - require.NoError(t, err) - require.Equal(t, gasConsumed, res) + var simRes sdk.SimulationResponse + require.NoError(t, jsonpb.Unmarshal(strings.NewReader(string(queryResult.Value)), &simRes)) + + require.Equal(t, gInfo, simRes.GasInfo) + require.Equal(t, result.Log, simRes.Result.Log) + require.Equal(t, result.Events, simRes.Result.Events) + require.True(t, bytes.Equal(result.Data, simRes.Result.Data)) + app.EndBlock(abci.RequestEndBlock{}) app.Commit() } @@ -1001,20 +1166,21 @@ func TestRunInvalidTransaction(t *testing.T) { }) } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // transaction with no messages { emptyTx := &txTest{} - _, result, err := app.Deliver(emptyTx) + _, result, err := app.Deliver(aminoTxEncoder(), emptyTx) require.Error(t, err) require.Nil(t, result) @@ -1041,7 +1207,7 @@ func TestRunInvalidTransaction(t *testing.T) { for _, testCase := range testCases { tx := testCase.tx - _, result, err := app.Deliver(tx) + _, result, err := app.Deliver(aminoTxEncoder(), tx) if testCase.fail { require.Error(t, err) @@ -1058,7 +1224,7 @@ func TestRunInvalidTransaction(t *testing.T) { // transaction with no known route { unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false} - _, result, err := app.Deliver(unknownRouteTx) + _, result, err := app.Deliver(aminoTxEncoder(), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1067,7 +1233,7 @@ func TestRunInvalidTransaction(t *testing.T) { require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err) unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false} - _, result, err = app.Deliver(unknownRouteTx) + _, result, err = app.Deliver(aminoTxEncoder(), unknownRouteTx) require.Error(t, err) require.Nil(t, result) @@ -1082,11 +1248,11 @@ func TestRunInvalidTransaction(t *testing.T) { tx.Msgs = append(tx.Msgs, msgNoDecode{}) // new codec so we can encode the tx, but we shouldn't be able to decode - newCdc := codec.New() + newCdc := codec.NewLegacyAmino() registerTestCodec(newCdc) newCdc.RegisterConcrete(&msgNoDecode{}, "cosmos-sdk/baseapp/msgNoDecode", nil) - txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err := newCdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1117,7 +1283,7 @@ func TestTxGasLimits(t *testing.T) { } }() - count := tx.(*txTest).Counter + count := tx.(txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return newCtx, nil @@ -1126,16 +1292,17 @@ func TestTxGasLimits(t *testing.T) { } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) - header := abci.Header{Height: 1} + header := tmproto.Header{Height: 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) testCases := []struct { @@ -1164,7 +1331,7 @@ func TestTxGasLimits(t *testing.T) { for i, tc := range testCases { tx := tc.tx - gInfo, result, err := app.Deliver(tx) + gInfo, result, err := app.Deliver(aminoTxEncoder(), tx) // check gas used and wanted require.Equal(t, tc.gasUsed, gInfo.GasUsed, fmt.Sprintf("tc #%d; gas: %v, result: %v, err: %s", i, gInfo, result, err)) @@ -1201,20 +1368,20 @@ func TestMaxBlockGasLimits(t *testing.T) { } }() - count := tx.(*txTest).Counter + count := tx.(txTest).Counter newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") return }) - } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) @@ -1249,12 +1416,12 @@ func TestMaxBlockGasLimits(t *testing.T) { tx := tc.tx // reset the block gas - header := abci.Header{Height: app.LastBlockHeight() + 1} + header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute the transaction multiple times for j := 0; j < tc.numDelivers; j++ { - _, result, err := app.Deliver(tx) + _, result, err := app.Deliver(aminoTxEncoder(), tx) ctx := app.getState(runTxModeDeliver).ctx @@ -1283,6 +1450,49 @@ func TestMaxBlockGasLimits(t *testing.T) { } } +// Test custom panic handling within app.DeliverTx method +func TestCustomRunTxPanicHandler(t *testing.T) { + const customPanicMsg = "test panic" + anteErr := sdkerrors.Register("fakeModule", 100500, "fakeError") + + anteOpt := func(bapp *BaseApp) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { + panic(sdkerrors.Wrap(anteErr, "anteHandler")) + }) + } + routerOpt := func(bapp *BaseApp) { + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + return &sdk.Result{}, nil + }) + bapp.Router().AddRoute(r) + } + + app := setupBaseApp(t, anteOpt, routerOpt) + + header := tmproto.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + + app.AddRunTxRecoveryHandler(func(recoveryObj interface{}) error { + err, ok := recoveryObj.(error) + if !ok { + return nil + } + + if anteErr.Is(err) { + panic(customPanicMsg) + } else { + return nil + } + }) + + // Transaction should panic with custom handler above + { + tx := newTxCounter(0, 0) + + require.PanicsWithValue(t, customPanicMsg, func() { app.Deliver(aminoTxEncoder(), tx) }) + } +} + func TestBaseAppAnteHandler(t *testing.T) { anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { @@ -1291,16 +1501,17 @@ func TestBaseAppAnteHandler(t *testing.T) { deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + bapp.Router().AddRoute(r) } - cdc := codec.New() + cdc := codec.NewLegacyAmino() app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) registerTestCodec(cdc) - header := abci.Header{Height: app.LastBlockHeight() + 1} + header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) // execute a tx that will fail ante handler execution @@ -1309,9 +1520,10 @@ func TestBaseAppAnteHandler(t *testing.T) { // the next txs ante handler execution (anteHandlerTxTest). tx := newTxCounter(0, 0) tx.setFailOnAnte(true) - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err := cdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx := app.getState(runTxModeDeliver).ctx @@ -1323,10 +1535,11 @@ func TestBaseAppAnteHandler(t *testing.T) { tx = newTxCounter(0, 0) tx.setFailOnHandler(true) - txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err = cdc.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx @@ -1338,10 +1551,11 @@ func TestBaseAppAnteHandler(t *testing.T) { // implicitly checked by previous tx executions tx = newTxCounter(1, 0) - txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err = cdc.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.NotEmpty(t, res.Events) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx @@ -1383,14 +1597,15 @@ func TestGasConsumptionBadTx(t *testing.T) { } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - count := msg.(msgCounter).Counter + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + count := msg.(*msgCounter).Counter ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } - cdc := codec.New() + cdc := codec.NewLegacyAmino() registerTestCodec(cdc) app := setupBaseApp(t, anteOpt, routerOpt) @@ -1404,12 +1619,12 @@ func TestGasConsumptionBadTx(t *testing.T) { app.InitChain(abci.RequestInitChain{}) - header := abci.Header{Height: app.LastBlockHeight() + 1} + header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) tx := newTxCounter(5, 0) tx.setFailOnAnte(true) - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err := cdc.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1417,7 +1632,7 @@ func TestGasConsumptionBadTx(t *testing.T) { // require next tx to fail due to black gas limit tx = newTxCounter(5, 0) - txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + txBytes, err = cdc.MarshalBinaryBare(tx) require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1436,11 +1651,12 @@ func TestQuery(t *testing.T) { } routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + r := sdk.NewRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { store := ctx.KVStore(capKey1) store.Set(key, value) return &sdk.Result{}, nil }) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) @@ -1461,17 +1677,17 @@ func TestQuery(t *testing.T) { require.Equal(t, 0, len(res.Value)) // query is still empty after a CheckTx - _, resTx, err := app.Check(tx) + _, resTx, err := app.Check(aminoTxEncoder(), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) require.Equal(t, 0, len(res.Value)) // query is still empty after a DeliverTx before we commit - header := abci.Header{Height: app.LastBlockHeight() + 1} + header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - _, resTx, err = app.Deliver(tx) + _, resTx, err = app.Deliver(aminoTxEncoder(), tx) require.NoError(t, err) require.NotNil(t, resTx) res = app.Query(query) @@ -1483,6 +1699,40 @@ func TestQuery(t *testing.T) { require.Equal(t, value, res.Value) } +func TestGRPCQuery(t *testing.T) { + grpcQueryOpt := func(bapp *BaseApp) { + testdata.RegisterQueryServer( + bapp.GRPCQueryRouter(), + testdata.QueryImpl{}, + ) + } + + app := setupBaseApp(t, grpcQueryOpt) + + app.InitChain(abci.RequestInitChain{}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + app.Commit() + + req := testdata.SayHelloRequest{Name: "foo"} + reqBz, err := req.Marshal() + require.NoError(t, err) + + reqQuery := abci.RequestQuery{ + Data: reqBz, + Path: "/testdata.Query/SayHello", + } + + resQuery := app.Query(reqQuery) + + require.Equal(t, abci.CodeTypeOK, resQuery.Code, resQuery) + + var res testdata.SayHelloResponse + err = res.Unmarshal(resQuery.Value) + require.NoError(t, err) + require.Equal(t, "Hello foo!", res.Greeting) +} + // Test p2p filter queries func TestP2PQuery(t *testing.T) { addrPeerFilterOpt := func(bapp *BaseApp) { @@ -1516,18 +1766,180 @@ func TestP2PQuery(t *testing.T) { func TestGetMaximumBlockGas(t *testing.T) { app := setupBaseApp(t) + app.InitChain(abci.RequestInitChain{}) + ctx := app.NewContext(true, tmproto.Header{}) + + app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 0}}) + require.Equal(t, uint64(0), app.getMaximumBlockGas(ctx)) - app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 0}}) - require.Equal(t, uint64(0), app.getMaximumBlockGas()) + app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -1}}) + require.Equal(t, uint64(0), app.getMaximumBlockGas(ctx)) - app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -1}}) - require.Equal(t, uint64(0), app.getMaximumBlockGas()) + app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 5000000}}) + require.Equal(t, uint64(5000000), app.getMaximumBlockGas(ctx)) + + app.StoreConsensusParams(ctx, &abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -5000000}}) + require.Panics(t, func() { app.getMaximumBlockGas(ctx) }) +} - app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 5000000}}) - require.Equal(t, uint64(5000000), app.getMaximumBlockGas()) +func TestListSnapshots(t *testing.T) { + app, teardown := setupBaseAppWithSnapshots(t, 5, 4) + defer teardown() - app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -5000000}}) - require.Panics(t, func() { app.getMaximumBlockGas() }) + resp := app.ListSnapshots(abci.RequestListSnapshots{}) + for _, s := range resp.Snapshots { + assert.NotEmpty(t, s.Hash) + assert.NotEmpty(t, s.Metadata) + s.Hash = nil + s.Metadata = nil + } + assert.Equal(t, abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{ + {Height: 4, Format: 1, Chunks: 2}, + {Height: 2, Format: 1, Chunks: 1}, + }}, resp) +} + +func TestLoadSnapshotChunk(t *testing.T) { + app, teardown := setupBaseAppWithSnapshots(t, 2, 5) + defer teardown() + + testcases := map[string]struct { + height uint64 + format uint32 + chunk uint32 + expectEmpty bool + }{ + "Existing snapshot": {2, 1, 1, false}, + "Missing height": {100, 1, 1, true}, + "Missing format": {2, 2, 1, true}, + "Missing chunk": {2, 1, 9, true}, + "Zero height": {0, 1, 1, true}, + "Zero format": {2, 0, 1, true}, + "Zero chunk": {2, 1, 0, false}, + } + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + resp := app.LoadSnapshotChunk(abci.RequestLoadSnapshotChunk{ + Height: tc.height, + Format: tc.format, + Chunk: tc.chunk, + }) + if tc.expectEmpty { + assert.Equal(t, abci.ResponseLoadSnapshotChunk{}, resp) + return + } + assert.NotEmpty(t, resp.Chunk) + }) + } +} + +func TestOfferSnapshot_Errors(t *testing.T) { + // Set up app before test cases, since it's fairly expensive. + app, teardown := setupBaseAppWithSnapshots(t, 0, 0) + defer teardown() + + m := snapshottypes.Metadata{ChunkHashes: [][]byte{{1}, {2}, {3}}} + metadata, err := m.Marshal() + require.NoError(t, err) + hash := []byte{1, 2, 3} + + testcases := map[string]struct { + snapshot *abci.Snapshot + result abci.ResponseOfferSnapshot_Result + }{ + "nil snapshot": {nil, abci.ResponseOfferSnapshot_REJECT}, + "invalid format": {&abci.Snapshot{ + Height: 1, Format: 9, Chunks: 3, Hash: hash, Metadata: metadata, + }, abci.ResponseOfferSnapshot_REJECT_FORMAT}, + "incorrect chunk count": {&abci.Snapshot{ + Height: 1, Format: 1, Chunks: 2, Hash: hash, Metadata: metadata, + }, abci.ResponseOfferSnapshot_REJECT}, + "no chunks": {&abci.Snapshot{ + Height: 1, Format: 1, Chunks: 0, Hash: hash, Metadata: metadata, + }, abci.ResponseOfferSnapshot_REJECT}, + "invalid metadata serialization": {&abci.Snapshot{ + Height: 1, Format: 1, Chunks: 0, Hash: hash, Metadata: []byte{3, 1, 4}, + }, abci.ResponseOfferSnapshot_REJECT}, + } + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + resp := app.OfferSnapshot(abci.RequestOfferSnapshot{Snapshot: tc.snapshot}) + assert.Equal(t, tc.result, resp.Result) + }) + } + + // Offering a snapshot after one has been accepted should error + resp := app.OfferSnapshot(abci.RequestOfferSnapshot{Snapshot: &abci.Snapshot{ + Height: 1, + Format: snapshottypes.CurrentFormat, + Chunks: 3, + Hash: []byte{1, 2, 3}, + Metadata: metadata, + }}) + require.Equal(t, abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, resp) + + resp = app.OfferSnapshot(abci.RequestOfferSnapshot{Snapshot: &abci.Snapshot{ + Height: 2, + Format: snapshottypes.CurrentFormat, + Chunks: 3, + Hash: []byte{1, 2, 3}, + Metadata: metadata, + }}) + require.Equal(t, abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, resp) +} + +func TestApplySnapshotChunk(t *testing.T) { + source, teardown := setupBaseAppWithSnapshots(t, 4, 10) + defer teardown() + + target, teardown := setupBaseAppWithSnapshots(t, 0, 0) + defer teardown() + + // Fetch latest snapshot to restore + respList := source.ListSnapshots(abci.RequestListSnapshots{}) + require.NotEmpty(t, respList.Snapshots) + snapshot := respList.Snapshots[0] + + // Make sure the snapshot has at least 3 chunks + require.GreaterOrEqual(t, snapshot.Chunks, uint32(3), "Not enough snapshot chunks") + + // Begin a snapshot restoration in the target + respOffer := target.OfferSnapshot(abci.RequestOfferSnapshot{Snapshot: snapshot}) + require.Equal(t, abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, respOffer) + + // We should be able to pass an invalid chunk and get a verify failure, before reapplying it. + respApply := target.ApplySnapshotChunk(abci.RequestApplySnapshotChunk{ + Index: 0, + Chunk: []byte{9}, + Sender: "sender", + }) + require.Equal(t, abci.ResponseApplySnapshotChunk{ + Result: abci.ResponseApplySnapshotChunk_RETRY, + RefetchChunks: []uint32{0}, + RejectSenders: []string{"sender"}, + }, respApply) + + // Fetch each chunk from the source and apply it to the target + for index := uint32(0); index < snapshot.Chunks; index++ { + respChunk := source.LoadSnapshotChunk(abci.RequestLoadSnapshotChunk{ + Height: snapshot.Height, + Format: snapshot.Format, + Chunk: index, + }) + require.NotNil(t, respChunk.Chunk) + respApply := target.ApplySnapshotChunk(abci.RequestApplySnapshotChunk{ + Index: index, + Chunk: respChunk.Chunk, + }) + require.Equal(t, abci.ResponseApplySnapshotChunk{ + Result: abci.ResponseApplySnapshotChunk_ACCEPT, + }, respApply) + } + + // The target should now have the same hash as the source + assert.Equal(t, source.LastCommitID(), target.LastCommitID()) } // NOTE: represents a new custom router for testing purposes of WithRouter() @@ -1535,8 +1947,8 @@ type testCustomRouter struct { routes sync.Map } -func (rtr *testCustomRouter) AddRoute(path string, h sdk.Handler) sdk.Router { - rtr.routes.Store(path, h) +func (rtr *testCustomRouter) AddRoute(route sdk.Route) sdk.Router { + rtr.routes.Store(route.Path(), route.Handler()) return rtr } @@ -1558,28 +1970,29 @@ func TestWithRouter(t *testing.T) { deliverKey := []byte("deliver-key") routerOpt := func(bapp *BaseApp) { bapp.SetRouter(&testCustomRouter{routes: sync.Map{}}) - bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + r := sdk.NewRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + bapp.Router().AddRoute(r) } app := setupBaseApp(t, anteOpt, routerOpt) app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder - codec := codec.New() + codec := codec.NewLegacyAmino() registerTestCodec(codec) nBlocks := 3 txPerHeight := 5 for blockN := 0; blockN < nBlocks; blockN++ { - header := abci.Header{Height: int64(blockN) + 1} + header := tmproto.Header{Height: int64(blockN) + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) for i := 0; i < txPerHeight; i++ { counter := int64(blockN*txPerHeight + i) tx := newTxCounter(counter, counter) - txBytes, err := codec.MarshalBinaryLengthPrefixed(tx) + txBytes, err := codec.MarshalBinaryBare(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) @@ -1590,3 +2003,35 @@ func TestWithRouter(t *testing.T) { app.Commit() } } + +func TestBaseApp_EndBlock(t *testing.T) { + db := dbm.NewMemDB() + name := t.Name() + logger := defaultLogger() + + cp := &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxGas: 5000000, + }, + } + + app := NewBaseApp(name, logger, db, nil) + app.SetParamStore(¶mStore{db: dbm.NewMemDB()}) + app.InitChain(abci.RequestInitChain{ + ConsensusParams: cp, + }) + + app.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return abci.ResponseEndBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{ + {Power: 100}, + }, + } + }) + app.Seal() + + res := app.EndBlock(abci.RequestEndBlock{}) + require.Len(t, res.GetValidatorUpdates(), 1) + require.Equal(t, int64(100), res.GetValidatorUpdates()[0].Power) + require.Equal(t, cp.Block.MaxGas, res.ConsensusParamUpdates.Block.MaxGas) +} diff --git a/baseapp/grpcrouter.go b/baseapp/grpcrouter.go new file mode 100644 index 000000000000..95186f0b16da --- /dev/null +++ b/baseapp/grpcrouter.go @@ -0,0 +1,159 @@ +package baseapp + +import ( + "fmt" + "reflect" + + gogogrpc "github.com/gogo/protobuf/grpc" + abci "github.com/tendermint/tendermint/abci/types" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding" + "google.golang.org/grpc/encoding/proto" + + "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 + // 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 +} + +// serviceData represents a gRPC service, along with its handler. +type serviceData struct { + serviceDesc *grpc.ServiceDesc + handler interface{} +} + +var _ gogogrpc.Server = &GRPCQueryRouter{} + +// NewGRPCQueryRouter creates a new GRPCQueryRouter +func NewGRPCQueryRouter() *GRPCQueryRouter { + return &GRPCQueryRouter{ + returnTypes: map[string]reflect.Type{}, + routes: map[string]GRPCQueryHandler{}, + } +} + +// GRPCQueryHandler defines a function type which handles ABCI Query requests +// using gRPC +type GRPCQueryHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) + +// Route returns the GRPCQueryHandler for a given query route path or nil +// if not found +func (qrt *GRPCQueryRouter) Route(path string) GRPCQueryHandler { + handler, found := qrt.routes[path] + if !found { + return nil + } + return handler +} + +// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC +// service description, handler is an object which implements that gRPC service/ +// +// This functions PANICS: +// - if a protobuf service is registered twice. +func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) { + // adds a top-level query handler based on the gRPC service name + for _, method := range sd.Methods { + fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) + methodHandler := method.Handler + + // Check that each service is only registered once. If a service is + // registered more than once, then we should error. Since we can't + // return an error (`Server.RegisterService` interface restriction) we + // panic (at startup). + _, found := qrt.routes[fqName] + if found { + panic( + fmt.Errorf( + "gRPC query service %s has already been registered. Please make sure to only register each service once. "+ + "This usually means that there are conflicting modules registering the same gRPC query service", + fqName, + ), + ) + } + + qrt.routes[fqName] = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) { + // call the method handler from the service description with the handler object, + // a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data + res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(i interface{}) error { + err := protoCodec.Unmarshal(req.Data, i) + if err != nil { + return err + } + 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 + } + + // proto marshal the result bytes + resBytes, err := protoCodec.Marshal(res) + if err != nil { + return abci.ResponseQuery{}, err + } + + // return the result bytes as the response value + return abci.ResponseQuery{ + Height: req.Height, + Value: resBytes, + }, nil + } + } + + qrt.serviceData = append(qrt.serviceData, serviceData{ + serviceDesc: sd, + handler: handler, + }) +} + +// SetInterfaceRegistry sets the interface registry for the router. This will +// also register the interface reflection gRPC service. +func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) { + qrt.interfaceRegistry = interfaceRegistry + + // Once we have an interface registry, we can register the interface + // registry reflection gRPC service. + reflection.RegisterReflectionServiceServer( + qrt, + 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/grpcrouter_helpers.go b/baseapp/grpcrouter_helpers.go new file mode 100644 index 000000000000..2ea74b55fc65 --- /dev/null +++ b/baseapp/grpcrouter_helpers.go @@ -0,0 +1,68 @@ +package baseapp + +import ( + gocontext "context" + "fmt" + + gogogrpc "github.com/gogo/protobuf/grpc" + abci "github.com/tendermint/tendermint/abci/types" + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryServiceTestHelper provides a helper for making grpc query service +// rpc calls in unit tests. It implements both the grpc Server and ClientConn +// interfaces needed to register a query service server and create a query +// service client. +type QueryServiceTestHelper struct { + *GRPCQueryRouter + Ctx sdk.Context +} + +var ( + _ gogogrpc.Server = &QueryServiceTestHelper{} + _ gogogrpc.ClientConn = &QueryServiceTestHelper{} +) + +// NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps +// the provided sdk.Context +func NewQueryServerTestHelper(ctx sdk.Context, interfaceRegistry types.InterfaceRegistry) *QueryServiceTestHelper { + qrt := NewGRPCQueryRouter() + qrt.SetInterfaceRegistry(interfaceRegistry) + return &QueryServiceTestHelper{GRPCQueryRouter: qrt, Ctx: ctx} +} + +// Invoke implements the grpc ClientConn.Invoke method +func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error { + querier := q.Route(method) + if querier == nil { + return fmt.Errorf("handler not found for %s", method) + } + reqBz, err := protoCodec.Marshal(args) + if err != nil { + return err + } + + res, err := querier(q.Ctx, abci.RequestQuery{Data: reqBz}) + if err != nil { + return err + } + + err = protoCodec.Unmarshal(res.Value, reply) + if err != nil { + return err + } + + if q.interfaceRegistry != nil { + return types.UnpackInterfaces(reply, q.interfaceRegistry) + } + + return nil +} + +// NewStream implements the grpc ClientConn.NewStream method +func (q *QueryServiceTestHelper) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { + return nil, fmt.Errorf("not supported") +} diff --git a/baseapp/grpcrouter_test.go b/baseapp/grpcrouter_test.go new file mode 100644 index 000000000000..64b2a97b9b00 --- /dev/null +++ b/baseapp/grpcrouter_test.go @@ -0,0 +1,76 @@ +package baseapp_test + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestGRPCGatewayRouter(t *testing.T) { + qr := baseapp.NewGRPCQueryRouter() + interfaceRegistry := testdata.NewTestInterfaceRegistry() + qr.SetInterfaceRegistry(interfaceRegistry) + testdata.RegisterQueryServer(qr, testdata.QueryImpl{}) + helper := &baseapp.QueryServiceTestHelper{ + GRPCQueryRouter: qr, + Ctx: sdk.Context{}.WithContext(context.Background()), + } + client := testdata.NewQueryClient(helper) + + res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + require.Nil(t, err) + require.NotNil(t, res) + require.Equal(t, "hello", res.Message) + + require.Panics(t, func() { + _, _ = client.Echo(context.Background(), nil) + }) + + res2, err := client.SayHello(context.Background(), &testdata.SayHelloRequest{Name: "Foo"}) + require.Nil(t, err) + require.NotNil(t, res) + require.Equal(t, "Hello Foo!", res2.Greeting) + + spot := &testdata.Dog{Name: "Spot", Size_: "big"} + any, err := types.NewAnyWithValue(spot) + require.NoError(t, err) + res3, err := client.TestAny(context.Background(), &testdata.TestAnyRequest{AnyAnimal: any}) + require.NoError(t, err) + require.NotNil(t, res3) + require.Equal(t, spot, res3.HasAnimal.Animal.GetCachedValue()) +} + +func TestRegisterQueryServiceTwice(t *testing.T) { + // Setup baseapp. + db := dbm.NewMemDB() + encCfg := simapp.MakeTestEncodingConfig() + app := baseapp.NewBaseApp("test", log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, encCfg.TxConfig.TxDecoder()) + app.SetInterfaceRegistry(encCfg.InterfaceRegistry) + testdata.RegisterInterfaces(encCfg.InterfaceRegistry) + + // First time registering service shouldn't panic. + require.NotPanics(t, func() { + testdata.RegisterQueryServer( + app.GRPCQueryRouter(), + testdata.QueryImpl{}, + ) + }) + + // Second time should panic. + require.Panics(t, func() { + testdata.RegisterQueryServer( + app.GRPCQueryRouter(), + testdata.QueryImpl{}, + ) + }) +} diff --git a/baseapp/grpcserver.go b/baseapp/grpcserver.go new file mode 100644 index 000000000000..c1db08a555aa --- /dev/null +++ b/baseapp/grpcserver.go @@ -0,0 +1,108 @@ +package baseapp + +import ( + "context" + "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/metadata" + + "github.com/cosmos/cosmos-sdk/client" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "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(clientCtx client.Context, server gogogrpc.Server) { + // Define an interceptor for all gRPC queries: this interceptor will route + // the query through the `clientCtx`, which itself queries Tendermint. + interceptor := func(grpcCtx context.Context, req interface{}, info *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { + // Two things can happen here: + // 1. either we're broadcasting a Tx, in which case we call Tendermint's broadcast endpoint directly, + // 2. or we are querying for state, in which case we call ABCI's Query. + + // Case 1. Broadcasting a Tx. + if reqProto, ok := req.(*tx.BroadcastTxRequest); ok { + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req) + } + + return client.TxServiceBroadcast(grpcCtx, clientCtx, reqProto) + } + + // 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 + } + + // 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 + } + + // returnType is a pointer to a struct. Here, we're creating res which + // is a new pointer to the underlying struct. + res := reflect.New(returnType.Elem()).Interface() + + err = protoCodec.Unmarshal(abciRes.Value, res) + if err != nil { + return nil, err + } + + // Send the metadata header back. The metadata currently includes: + // - block height. + err = grpc.SendHeader(grpcCtx, outMd) + if err != nil { + return nil, err + } + + return res, nil + } + + // Loop through all services and methods, add the interceptor, and register + // the service. + for _, data := range app.GRPCQueryRouter().serviceData { + desc := data.serviceDesc + newMethods := make([]grpc.MethodDesc, len(desc.Methods)) + + for i, method := range desc.Methods { + methodHandler := method.Handler + newMethods[i] = grpc.MethodDesc{ + MethodName: method.MethodName, + Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, _ grpc.UnaryServerInterceptor) (interface{}, error) { + return methodHandler(srv, ctx, dec, grpcmiddleware.ChainUnaryServer( + grpcrecovery.UnaryServerInterceptor(), + interceptor, + )) + }, + } + } + + newDesc := &grpc.ServiceDesc{ + ServiceName: desc.ServiceName, + HandlerType: desc.HandlerType, + Methods: newMethods, + Streams: desc.Streams, + Metadata: desc.Metadata, + } + + server.RegisterService(newDesc, data.handler) + } +} diff --git a/baseapp/helpers.go b/baseapp/helpers.go deleted file mode 100644 index aa9c10d328d7..000000000000 --- a/baseapp/helpers.go +++ /dev/null @@ -1,33 +0,0 @@ -package baseapp - -import ( - "regexp" - - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString - -func (app *BaseApp) Check(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeCheck, nil, tx) -} - -func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeSimulate, txBytes, tx) -} - -func (app *BaseApp) Deliver(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { - return app.runTx(runTxModeDeliver, nil, tx) -} - -// Context with current {check, deliver}State of the app used by tests. -func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { - if isCheckTx { - return sdk.NewContext(app.checkState.ms, header, true, app.logger). - WithMinGasPrices(app.minGasPrices) - } - - return sdk.NewContext(app.deliverState.ms, header, false, app.logger) -} diff --git a/baseapp/msg_service_router.go b/baseapp/msg_service_router.go new file mode 100644 index 000000000000..ea2ed4b4eb6a --- /dev/null +++ b/baseapp/msg_service_router.go @@ -0,0 +1,114 @@ +package baseapp + +import ( + "context" + "fmt" + + gogogrpc "github.com/gogo/protobuf/grpc" + "github.com/gogo/protobuf/proto" + "google.golang.org/grpc" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// MsgServiceRouter routes fully-qualified Msg service methods to their handler. +type MsgServiceRouter struct { + interfaceRegistry codectypes.InterfaceRegistry + routes map[string]MsgServiceHandler +} + +var _ gogogrpc.Server = &MsgServiceRouter{} + +// NewMsgServiceRouter creates a new MsgServiceRouter. +func NewMsgServiceRouter() *MsgServiceRouter { + return &MsgServiceRouter{ + routes: map[string]MsgServiceHandler{}, + } +} + +// MsgServiceHandler defines a function type which handles Msg service message. +type MsgServiceHandler = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) + +// Handler returns the MsgServiceHandler for a given query route path or nil +// if not found. +func (msr *MsgServiceRouter) Handler(methodName string) MsgServiceHandler { + return msr.routes[methodName] +} + +// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC +// service description, handler is an object which implements that gRPC service. +// +// This function PANICs: +// - if it is called before the service `Msg`s have been registered using +// RegisterInterfaces, +// - or if a service is being registered twice. +func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) { + // Adds a top-level query handler based on the gRPC service name. + for _, method := range sd.Methods { + fqMethod := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) + methodHandler := method.Handler + + // Check that the service Msg fully-qualified method name has already + // been registered (via RegisterInterfaces). If the user registers a + // service without registering according service Msg type, there might be + // some unexpected behavior down the road. Since we can't return an error + // (`Server.RegisterService` interface restriction) we panic (at startup). + serviceMsg, err := msr.interfaceRegistry.Resolve(fqMethod) + if err != nil || serviceMsg == nil { + panic( + fmt.Errorf( + "type_url %s has not been registered yet. "+ + "Before calling RegisterService, you must register all interfaces by calling the `RegisterInterfaces` "+ + "method on module.BasicManager. Each module should call `msgservice.RegisterMsgServiceDesc` inside its "+ + "`RegisterInterfaces` method with the `_Msg_serviceDesc` generated by proto-gen", + fqMethod, + ), + ) + } + + // Check that each service is only registered once. If a service is + // registered more than once, then we should error. Since we can't + // return an error (`Server.RegisterService` interface restriction) we + // panic (at startup). + _, found := msr.routes[fqMethod] + if found { + panic( + fmt.Errorf( + "msg service %s has already been registered. Please make sure to only register each service once. "+ + "This usually means that there are conflicting modules registering the same msg service", + fqMethod, + ), + ) + } + + msr.routes[fqMethod] = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + interceptor := func(goCtx context.Context, _ interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + goCtx = context.WithValue(goCtx, sdk.SdkContextKey, ctx) + return handler(goCtx, req) + } + // Call the method handler from the service description with the handler object. + // We don't do any decoding here because the decoding was already done. + res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), noopDecoder, interceptor) + if err != nil { + return nil, err + } + + resMsg, ok := res.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting proto.Message, got %T", resMsg) + } + + return sdk.WrapServiceResult(ctx, resMsg, err) + } + } +} + +// SetInterfaceRegistry sets the interface registry for the router. +func (msr *MsgServiceRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) { + msr.interfaceRegistry = interfaceRegistry +} + +func noopDecoder(_ interface{}) error { return nil } diff --git a/baseapp/msg_service_router_test.go b/baseapp/msg_service_router_test.go new file mode 100644 index 000000000000..34f9c080277e --- /dev/null +++ b/baseapp/msg_service_router_test.go @@ -0,0 +1,122 @@ +package baseapp_test + +import ( + "os" + "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" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func TestRegisterMsgService(t *testing.T) { + db := dbm.NewMemDB() + + // Create an encoding config that doesn't register testdata Msg services. + encCfg := simapp.MakeTestEncodingConfig() + app := baseapp.NewBaseApp("test", log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, encCfg.TxConfig.TxDecoder()) + app.SetInterfaceRegistry(encCfg.InterfaceRegistry) + require.Panics(t, func() { + testdata.RegisterMsgServer( + app.MsgServiceRouter(), + testdata.MsgServerImpl{}, + ) + }) + + // Register testdata Msg services, and rerun `RegisterService`. + testdata.RegisterInterfaces(encCfg.InterfaceRegistry) + require.NotPanics(t, func() { + testdata.RegisterMsgServer( + app.MsgServiceRouter(), + testdata.MsgServerImpl{}, + ) + }) +} + +func TestRegisterMsgServiceTwice(t *testing.T) { + // Setup baseapp. + db := dbm.NewMemDB() + encCfg := simapp.MakeTestEncodingConfig() + app := baseapp.NewBaseApp("test", log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, encCfg.TxConfig.TxDecoder()) + app.SetInterfaceRegistry(encCfg.InterfaceRegistry) + testdata.RegisterInterfaces(encCfg.InterfaceRegistry) + + // First time registering service shouldn't panic. + require.NotPanics(t, func() { + testdata.RegisterMsgServer( + app.MsgServiceRouter(), + testdata.MsgServerImpl{}, + ) + }) + + // Second time should panic. + require.Panics(t, func() { + testdata.RegisterMsgServer( + app.MsgServiceRouter(), + testdata.MsgServerImpl{}, + ) + }) +} + +func TestMsgService(t *testing.T) { + priv, _, _ := testdata.KeyTestPubAddr() + encCfg := simapp.MakeTestEncodingConfig() + testdata.RegisterInterfaces(encCfg.InterfaceRegistry) + db := dbm.NewMemDB() + app := baseapp.NewBaseApp("test", log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, encCfg.TxConfig.TxDecoder()) + app.SetInterfaceRegistry(encCfg.InterfaceRegistry) + testdata.RegisterMsgServer( + app.MsgServiceRouter(), + testdata.MsgServerImpl{}, + ) + _ = app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 1}}) + + msg := testdata.NewServiceMsgCreateDog(&testdata.MsgCreateDog{Dog: &testdata.Dog{Name: "Spot"}}) + txBuilder := encCfg.TxConfig.NewTxBuilder() + txBuilder.SetFeeAmount(testdata.NewTestFeeAmount()) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + err := txBuilder.SetMsgs(msg) + require.NoError(t, err) + + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: encCfg.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: 0, + } + + err = txBuilder.SetSignatures(sigV2) + require.NoError(t, err) + + // Second round: all signer infos are set, so each signer can sign. + signerData := authsigning.SignerData{ + ChainID: "test", + AccountNumber: 0, + Sequence: 0, + } + sigV2, err = tx.SignWithPrivKey( + encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData, + txBuilder, priv, encCfg.TxConfig, 0) + require.NoError(t, err) + err = txBuilder.SetSignatures(sigV2) + require.NoError(t, err) + + // Send the tx to the app + txBytes, err := encCfg.TxConfig.TxEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.Equal(t, abci.CodeTypeOK, res.Code, "res=%+v", res) +} diff --git a/baseapp/options.go b/baseapp/options.go index 6e767ef22103..33de1a3aa6e8 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -6,6 +6,8 @@ import ( dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/snapshots" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -38,29 +40,67 @@ func SetHaltTime(haltTime uint64) func(*BaseApp) { return func(bap *BaseApp) { bap.setHaltTime(haltTime) } } +// SetMinRetainBlocks returns a BaseApp option function that sets the minimum +// block retention height value when determining which heights to prune during +// ABCI Commit. +func SetMinRetainBlocks(minRetainBlocks uint64) func(*BaseApp) { + return func(bapp *BaseApp) { bapp.setMinRetainBlocks(minRetainBlocks) } +} + +// SetTrace will turn on or off trace flag +func SetTrace(trace bool) func(*BaseApp) { + return func(app *BaseApp) { app.setTrace(trace) } +} + +// SetIndexEvents provides a BaseApp option function that sets the events to index. +func SetIndexEvents(ie []string) func(*BaseApp) { + return func(app *BaseApp) { app.setIndexEvents(ie) } +} + // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) { return func(app *BaseApp) { app.setInterBlockCache(cache) } } -// SetDebug will turn on or off debug flag -func SetDebug(debug bool) func(*BaseApp) { - return func(app *BaseApp) { app.setDebug(debug) } +// SetSnapshotInterval sets the snapshot interval. +func SetSnapshotInterval(interval uint64) func(*BaseApp) { + return func(app *BaseApp) { app.SetSnapshotInterval(interval) } +} + +// SetSnapshotKeepRecent sets the recent snapshots to keep. +func SetSnapshotKeepRecent(keepRecent uint32) func(*BaseApp) { + return func(app *BaseApp) { app.SetSnapshotKeepRecent(keepRecent) } +} + +// SetSnapshotStore sets the snapshot store. +func SetSnapshotStore(snapshotStore *snapshots.Store) func(*BaseApp) { + return func(app *BaseApp) { app.SetSnapshotStore(snapshotStore) } } func (app *BaseApp) SetName(name string) { if app.sealed { panic("SetName() on sealed BaseApp") } + app.name = name } +// SetParamStore sets a parameter store on the BaseApp. +func (app *BaseApp) SetParamStore(ps ParamStore) { + if app.sealed { + panic("SetParamStore() on sealed BaseApp") + } + + app.paramStore = ps +} + // SetAppVersion sets the application's version string. func (app *BaseApp) SetAppVersion(v string) { if app.sealed { panic("SetAppVersion() on sealed BaseApp") } + app.appVersion = v } @@ -68,6 +108,7 @@ func (app *BaseApp) SetDB(db dbm.DB) { if app.sealed { panic("SetDB() on sealed BaseApp") } + app.db = db } @@ -75,6 +116,7 @@ func (app *BaseApp) SetCMS(cms store.CommitMultiStore) { if app.sealed { panic("SetEndBlocker() on sealed BaseApp") } + app.cms = cms } @@ -82,6 +124,7 @@ func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) { if app.sealed { panic("SetInitChainer() on sealed BaseApp") } + app.initChainer = initChainer } @@ -89,6 +132,7 @@ func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) { if app.sealed { panic("SetBeginBlocker() on sealed BaseApp") } + app.beginBlocker = beginBlocker } @@ -96,6 +140,7 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { if app.sealed { panic("SetEndBlocker() on sealed BaseApp") } + app.endBlocker = endBlocker } @@ -103,6 +148,7 @@ func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { if app.sealed { panic("SetAnteHandler() on sealed BaseApp") } + app.anteHandler = ah } @@ -110,6 +156,7 @@ func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) { if app.sealed { panic("SetAddrPeerFilter() on sealed BaseApp") } + app.addrPeerFilter = pf } @@ -117,6 +164,7 @@ func (app *BaseApp) SetIDPeerFilter(pf sdk.PeerFilter) { if app.sealed { panic("SetIDPeerFilter() on sealed BaseApp") } + app.idPeerFilter = pf } @@ -124,6 +172,7 @@ func (app *BaseApp) SetFauxMerkleMode() { if app.sealed { panic("SetFauxMerkleMode() on sealed BaseApp") } + app.fauxMerkleMode = true } @@ -138,6 +187,7 @@ func (app *BaseApp) SetStoreLoader(loader StoreLoader) { if app.sealed { panic("SetStoreLoader() on sealed BaseApp") } + app.storeLoader = loader } @@ -148,3 +198,38 @@ func (app *BaseApp) SetRouter(router sdk.Router) { } app.router = router } + +// SetSnapshotStore sets the snapshot store. +func (app *BaseApp) SetSnapshotStore(snapshotStore *snapshots.Store) { + if app.sealed { + panic("SetSnapshotStore() on sealed BaseApp") + } + if snapshotStore == nil { + app.snapshotManager = nil + return + } + app.snapshotManager = snapshots.NewManager(snapshotStore, app.cms) +} + +// SetSnapshotInterval sets the snapshot interval. +func (app *BaseApp) SetSnapshotInterval(snapshotInterval uint64) { + if app.sealed { + panic("SetSnapshotInterval() on sealed BaseApp") + } + app.snapshotInterval = snapshotInterval +} + +// SetSnapshotKeepRecent sets the number of recent snapshots to keep. +func (app *BaseApp) SetSnapshotKeepRecent(snapshotKeepRecent uint32) { + if app.sealed { + panic("SetSnapshotKeepRecent() on sealed BaseApp") + } + app.snapshotKeepRecent = snapshotKeepRecent +} + +// SetInterfaceRegistry sets the InterfaceRegistry. +func (app *BaseApp) SetInterfaceRegistry(registry types.InterfaceRegistry) { + app.interfaceRegistry = registry + app.grpcQueryRouter.SetInterfaceRegistry(registry) + app.msgServiceRouter.SetInterfaceRegistry(registry) +} diff --git a/baseapp/params.go b/baseapp/params.go new file mode 100644 index 000000000000..14701d524798 --- /dev/null +++ b/baseapp/params.go @@ -0,0 +1,86 @@ +package baseapp + +import ( + "errors" + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Paramspace defines the parameter subspace to be used for the paramstore. +const Paramspace = "baseapp" + +// Parameter store keys for all the consensus parameter types. +var ( + ParamStoreKeyBlockParams = []byte("BlockParams") + ParamStoreKeyEvidenceParams = []byte("EvidenceParams") + ParamStoreKeyValidatorParams = []byte("ValidatorParams") +) + +// ParamStore defines the interface the parameter store used by the BaseApp must +// fulfill. +type ParamStore interface { + Get(ctx sdk.Context, key []byte, ptr interface{}) + Has(ctx sdk.Context, key []byte) bool + Set(ctx sdk.Context, key []byte, param interface{}) +} + +// ValidateBlockParams defines a stateless validation on BlockParams. This function +// is called whenever the parameters are updated or stored. +func ValidateBlockParams(i interface{}) error { + v, ok := i.(abci.BlockParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.MaxBytes <= 0 { + return fmt.Errorf("block maximum bytes must be positive: %d", v.MaxBytes) + } + + if v.MaxGas < -1 { + return fmt.Errorf("block maximum gas must be greater than or equal to -1: %d", v.MaxGas) + } + + return nil +} + +// ValidateEvidenceParams defines a stateless validation on EvidenceParams. This +// function is called whenever the parameters are updated or stored. +func ValidateEvidenceParams(i interface{}) error { + v, ok := i.(tmproto.EvidenceParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.MaxAgeNumBlocks <= 0 { + return fmt.Errorf("evidence maximum age in blocks must be positive: %d", v.MaxAgeNumBlocks) + } + + if v.MaxAgeDuration <= 0 { + return fmt.Errorf("evidence maximum age time duration must be positive: %v", v.MaxAgeDuration) + } + + if v.MaxBytes < 0 { + return fmt.Errorf("maximum evidence bytes must be non-negative: %v", v.MaxBytes) + } + + return nil +} + +// ValidateValidatorParams defines a stateless validation on ValidatorParams. This +// function is called whenever the parameters are updated or stored. +func ValidateValidatorParams(i interface{}) error { + v, ok := i.(tmproto.ValidatorParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if len(v.PubKeyTypes) == 0 { + return errors.New("validator allowed pubkey types must not be empty") + } + + return nil +} diff --git a/baseapp/params_test.go b/baseapp/params_test.go new file mode 100644 index 000000000000..6507e17a8aea --- /dev/null +++ b/baseapp/params_test.go @@ -0,0 +1,66 @@ +package baseapp_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" +) + +func TestValidateBlockParams(t *testing.T) { + testCases := []struct { + arg interface{} + expectErr bool + }{ + {nil, true}, + {&abci.BlockParams{}, true}, + {abci.BlockParams{}, true}, + {abci.BlockParams{MaxBytes: -1, MaxGas: -1}, true}, + {abci.BlockParams{MaxBytes: 2000000, MaxGas: -5}, true}, + {abci.BlockParams{MaxBytes: 2000000, MaxGas: 300000}, false}, + } + + for _, tc := range testCases { + require.Equal(t, tc.expectErr, baseapp.ValidateBlockParams(tc.arg) != nil) + } +} + +func TestValidateEvidenceParams(t *testing.T) { + testCases := []struct { + arg interface{} + expectErr bool + }{ + {nil, true}, + {&tmproto.EvidenceParams{}, true}, + {tmproto.EvidenceParams{}, true}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: -1, MaxAgeDuration: 18004000, MaxBytes: 5000000}, true}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: -1, MaxBytes: 5000000}, true}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: 18004000, MaxBytes: -1}, true}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: 18004000, MaxBytes: 5000000}, false}, + {tmproto.EvidenceParams{MaxAgeNumBlocks: 360000, MaxAgeDuration: 18004000, MaxBytes: 0}, false}, + } + + for _, tc := range testCases { + require.Equal(t, tc.expectErr, baseapp.ValidateEvidenceParams(tc.arg) != nil) + } +} + +func TestValidateValidatorParams(t *testing.T) { + testCases := []struct { + arg interface{} + expectErr bool + }{ + {nil, true}, + {&tmproto.ValidatorParams{}, true}, + {tmproto.ValidatorParams{}, true}, + {tmproto.ValidatorParams{PubKeyTypes: []string{}}, true}, + {tmproto.ValidatorParams{PubKeyTypes: []string{"secp256k1"}}, false}, + } + + for _, tc := range testCases { + require.Equal(t, tc.expectErr, baseapp.ValidateValidatorParams(tc.arg) != nil) + } +} diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go index fbefda4a8dd8..1727b2ab2df6 100644 --- a/baseapp/queryrouter.go +++ b/baseapp/queryrouter.go @@ -22,14 +22,16 @@ func NewQueryRouter() *QueryRouter { // AddRoute adds a query path to the router with a given Querier. It will panic // if a duplicate route is given. The route must be alphanumeric. func (qrt *QueryRouter) AddRoute(path string, q sdk.Querier) sdk.QueryRouter { - if !isAlphaNumeric(path) { + if !sdk.IsAlphaNumeric(path) { panic("route expressions can only contain alphanumeric characters") } + if qrt.routes[path] != nil { panic(fmt.Sprintf("route %s has already been initialized", path)) } qrt.routes[path] = q + return qrt } diff --git a/baseapp/recovery.go b/baseapp/recovery.go new file mode 100644 index 000000000000..7f0687800c65 --- /dev/null +++ b/baseapp/recovery.go @@ -0,0 +1,77 @@ +package baseapp + +import ( + "fmt" + "runtime/debug" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// RecoveryHandler handles recovery() object. +// Return a non-nil error if recoveryObj was processed. +// Return nil if recoveryObj was not processed. +type RecoveryHandler func(recoveryObj interface{}) error + +// recoveryMiddleware is wrapper for RecoveryHandler to create chained recovery handling. +// returns (recoveryMiddleware, nil) if recoveryObj was not processed and should be passed to the next middleware in chain. +// returns (nil, error) if recoveryObj was processed and middleware chain processing should be stopped. +type recoveryMiddleware func(recoveryObj interface{}) (recoveryMiddleware, error) + +// processRecovery processes recoveryMiddleware chain for recovery() object. +// Chain processing stops on non-nil error or when chain is processed. +func processRecovery(recoveryObj interface{}, middleware recoveryMiddleware) error { + if middleware == nil { + return nil + } + + next, err := middleware(recoveryObj) + if err != nil { + return err + } + + return processRecovery(recoveryObj, next) +} + +// newRecoveryMiddleware creates a RecoveryHandler middleware. +func newRecoveryMiddleware(handler RecoveryHandler, next recoveryMiddleware) recoveryMiddleware { + return func(recoveryObj interface{}) (recoveryMiddleware, error) { + if err := handler(recoveryObj); err != nil { + return nil, err + } + + return next, nil + } +} + +// newOutOfGasRecoveryMiddleware creates a standard OutOfGas recovery middleware for app.runTx method. +func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recoveryMiddleware) recoveryMiddleware { + handler := func(recoveryObj interface{}) error { + err, ok := recoveryObj.(sdk.ErrorOutOfGas) + if !ok { + return nil + } + + return sdkerrors.Wrap( + sdkerrors.ErrOutOfGas, fmt.Sprintf( + "out of gas in location: %v; gasWanted: %d, gasUsed: %d", + err.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), + ), + ) + } + + return newRecoveryMiddleware(handler, next) +} + +// newDefaultRecoveryMiddleware creates a default (last in chain) recovery middleware for app.runTx method. +func newDefaultRecoveryMiddleware() recoveryMiddleware { + handler := func(recoveryObj interface{}) error { + return sdkerrors.Wrap( + sdkerrors.ErrPanic, fmt.Sprintf( + "recovered: %v\nstack:\n%v", recoveryObj, string(debug.Stack()), + ), + ) + } + + return newRecoveryMiddleware(handler, nil) +} diff --git a/baseapp/recovery_test.go b/baseapp/recovery_test.go new file mode 100644 index 000000000000..b75892c63818 --- /dev/null +++ b/baseapp/recovery_test.go @@ -0,0 +1,64 @@ +package baseapp + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +// Test that recovery chain produces expected error at specific middleware layer +func TestRecoveryChain(t *testing.T) { + createError := func(id int) error { + return fmt.Errorf("error from id: %d", id) + } + + createHandler := func(id int, handle bool) RecoveryHandler { + return func(_ interface{}) error { + if handle { + return createError(id) + } + return nil + } + } + + // check recovery chain [1] -> 2 -> 3 + { + mw := newRecoveryMiddleware(createHandler(3, false), nil) + mw = newRecoveryMiddleware(createHandler(2, false), mw) + mw = newRecoveryMiddleware(createHandler(1, true), mw) + receivedErr := processRecovery(nil, mw) + + require.Equal(t, createError(1), receivedErr) + } + + // check recovery chain 1 -> [2] -> 3 + { + mw := newRecoveryMiddleware(createHandler(3, false), nil) + mw = newRecoveryMiddleware(createHandler(2, true), mw) + mw = newRecoveryMiddleware(createHandler(1, false), mw) + receivedErr := processRecovery(nil, mw) + + require.Equal(t, createError(2), receivedErr) + } + + // check recovery chain 1 -> 2 -> [3] + { + mw := newRecoveryMiddleware(createHandler(3, true), nil) + mw = newRecoveryMiddleware(createHandler(2, false), mw) + mw = newRecoveryMiddleware(createHandler(1, false), mw) + receivedErr := processRecovery(nil, mw) + + require.Equal(t, createError(3), receivedErr) + } + + // check recovery chain 1 -> 2 -> 3 + { + mw := newRecoveryMiddleware(createHandler(3, false), nil) + mw = newRecoveryMiddleware(createHandler(2, false), mw) + mw = newRecoveryMiddleware(createHandler(1, false), mw) + receivedErr := processRecovery(nil, mw) + + require.Nil(t, receivedErr) + } +} diff --git a/baseapp/router.go b/baseapp/router.go index 77d2567c6cb2..7e2e70a0c6f3 100644 --- a/baseapp/router.go +++ b/baseapp/router.go @@ -21,15 +21,15 @@ func NewRouter() *Router { // AddRoute adds a route path to the router with a given handler. The route must // be alphanumeric. -func (rtr *Router) AddRoute(path string, h sdk.Handler) sdk.Router { - if !isAlphaNumeric(path) { +func (rtr *Router) AddRoute(route sdk.Route) sdk.Router { + if !sdk.IsAlphaNumeric(route.Path()) { panic("route expressions can only contain alphanumeric characters") } - if rtr.routes[path] != nil { - panic(fmt.Sprintf("route %s has already been initialized", path)) + if rtr.routes[route.Path()] != nil { + panic(fmt.Sprintf("route %s has already been initialized", route.Path())) } - rtr.routes[path] = h + rtr.routes[route.Path()] = route.Handler() return rtr } diff --git a/baseapp/router_test.go b/baseapp/router_test.go index 86b727568d5d..1e11dc0ca089 100644 --- a/baseapp/router_test.go +++ b/baseapp/router_test.go @@ -17,15 +17,15 @@ func TestRouter(t *testing.T) { // require panic on invalid route require.Panics(t, func() { - rtr.AddRoute("*", testHandler) + rtr.AddRoute(sdk.NewRoute("*", testHandler)) }) - rtr.AddRoute("testRoute", testHandler) + rtr.AddRoute(sdk.NewRoute("testRoute", testHandler)) h := rtr.Route(sdk.Context{}, "testRoute") require.NotNil(t, h) // require panic on duplicate route require.Panics(t, func() { - rtr.AddRoute("testRoute", testHandler) + rtr.AddRoute(sdk.NewRoute("testRoute", testHandler)) }) } diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go new file mode 100644 index 000000000000..407ebd9a7cd9 --- /dev/null +++ b/baseapp/test_helpers.go @@ -0,0 +1,46 @@ +package baseapp + +import ( + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +func (app *BaseApp) Check(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { + // runTx expects tx bytes as argument, so we encode the tx argument into + // bytes. Note that runTx will actually decode those bytes again. But since + // this helper is only used in tests/simulation, it's fine. + bz, err := txEncoder(tx) + if err != nil { + return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) + } + return app.runTx(runTxModeCheck, bz) +} + +func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { + return app.runTx(runTxModeSimulate, txBytes) +} + +func (app *BaseApp) Deliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { + // See comment for Check(). + bz, err := txEncoder(tx) + if err != nil { + return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) + } + return app.runTx(runTxModeDeliver, bz) +} + +// Context with current {check, deliver}State of the app used by tests. +func (app *BaseApp) NewContext(isCheckTx bool, header tmproto.Header) sdk.Context { + if isCheckTx { + return sdk.NewContext(app.checkState.ms, header, true, app.logger). + WithMinGasPrices(app.minGasPrices) + } + + return sdk.NewContext(app.deliverState.ms, header, false, app.logger) +} + +func (app *BaseApp) NewUncachedContext(isCheckTx bool, header tmproto.Header) sdk.Context { + return sdk.NewContext(app.cms, header, isCheckTx, app.logger) +} diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 000000000000..37f716cabaa2 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,34 @@ +version: v1beta1 + +build: + roots: + - proto + - third_party/proto + excludes: + - third_party/proto/google/protobuf +lint: + use: + - DEFAULT + - COMMENTS + - FILE_LOWER_SNAKE_CASE + except: + - UNARY_RPC + - COMMENT_FIELD + - SERVICE_SUFFIX + - PACKAGE_VERSION_SUFFIX + - RPC_REQUEST_STANDARD_NAME + ignore: + - tendermint + - gogoproto + - cosmos_proto + - google + - confio +breaking: + use: + - FILE + ignore: + - tendermint + - gogoproto + - cosmos_proto + - google + - confio diff --git a/client/account_retriever.go b/client/account_retriever.go new file mode 100644 index 000000000000..8e2fd14c1fcd --- /dev/null +++ b/client/account_retriever.go @@ -0,0 +1,24 @@ +package client + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Account defines a read-only version of the auth module's AccountI. +type Account interface { + GetAddress() sdk.AccAddress + GetPubKey() cryptotypes.PubKey // can return nil. + GetAccountNumber() uint64 + GetSequence() uint64 +} + +// AccountRetriever defines the interfaces required by transactions to +// ensure an account exists and to be able to query for account fields necessary +// for signing. +type AccountRetriever interface { + GetAccount(clientCtx Context, addr sdk.AccAddress) (Account, error) + GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (Account, int64, error) + EnsureExists(clientCtx Context, addr sdk.AccAddress) error + GetAccountNumberSequence(clientCtx Context, addr sdk.AccAddress) (accNum uint64, accSeq uint64, err error) +} diff --git a/client/attestation/attestation.go b/client/attestation/attestation.go deleted file mode 100644 index 192f160b36ef..000000000000 --- a/client/attestation/attestation.go +++ /dev/null @@ -1,80 +0,0 @@ -package attestation - -import ( - "bytes" - "encoding/hex" - "github.com/tendermint/tendermint/crypto" -) - -type Attestation struct { - PublicKey crypto.PubKey - Signature []byte -} - -func NewAttestation(key crypto.PrivKey) (*Attestation, error) { - - // create the basic attestation - att := &Attestation{ - PublicKey: key.PubKey(), - Signature: []byte{}, - } - - // sign the attestation - err := att.sign(key) - if err != nil { - return nil, err - } - - return att, nil -} - -func NewAttestationFromString(encoded string) (*Attestation, error) { - - // decode the string - bz, err := hex.DecodeString(encoded) - if err != nil { - return nil, err - } - - // unmarshall the attestation - att := &Attestation{} - err = UnmarshalBinaryBare(bz, att) - if err != nil { - return nil, err - } - - return att, nil -} - -func (at *Attestation) sign(key crypto.PrivKey) error { - - // sign the payload - signature, err := key.Sign(at.PublicKey.Address().Bytes()) - if err != nil { - return err - } - - // update the signature - at.Signature = signature - - return nil -} - -func (at *Attestation) Verify(address crypto.Address) bool { - - // ensure that the address derived from the public key matches the required address - if !bytes.Equal(at.PublicKey.Address().Bytes(), address.Bytes()) { - return false - } - - // validate the signature present matches the public key - return at.PublicKey.VerifyBytes(at.PublicKey.Address().Bytes(), at.Signature) -} - -func (at *Attestation) Bytes() []byte { - return AttestationCdc.MustMarshalBinaryBare(at) -} - -func (at *Attestation) String() string { - return hex.EncodeToString(at.Bytes()) -} diff --git a/client/attestation/attestation_test.go b/client/attestation/attestation_test.go deleted file mode 100644 index c0796b807217..000000000000 --- a/client/attestation/attestation_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package attestation - -import ( - "bytes" - "crypto/rand" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "testing" -) - -func generatePrivateKey(t *testing.T) crypto.PrivKey { - - // generate seed for the private key - bz := make([]byte, 32) - _, err := rand.Read(bz) - require.NoError(t, err) - - // create the key - return keys.SecpPrivKeyGen(bz) -} - -func TestBasicAttestation(t *testing.T) { - privKey := generatePrivateKey(t) - - // create the attestation - att, err := NewAttestation(privKey) - require.NoError(t, err) - - // verify that it is correct - require.True(t, att.Verify(privKey.PubKey().Address())) -} - -func TestBasicAttestationMarshalling(t *testing.T) { - privKey := generatePrivateKey(t) - - // create the attestation - att, err := NewAttestation(privKey) - require.NoError(t, err) - - // marshall it to binary - bz, err := MarshalBinaryBare(att) - require.NoError(t, err) - - // recover the attestation - recoveredAtt := &Attestation{} - err = UnmarshalBinaryBare(bz, recoveredAtt) - require.NoError(t, err) - - require.True(t, att.PublicKey.Equals(recoveredAtt.PublicKey)) - require.True(t, bytes.Equal(att.Signature, recoveredAtt.Signature)) - - // check that it sis correct - require.True(t, recoveredAtt.Verify(att.PublicKey.Address())) -} - -func TestAttestationAsString(t *testing.T) { - privKey := generatePrivateKey(t) - - // create the attestation - att, err := NewAttestation(privKey) - require.NoError(t, err) - - // recover the attestation - recovered, err := NewAttestationFromString(att.String()) - require.NoError(t, err) - - require.True(t, att.PublicKey.Equals(recovered.PublicKey)) - require.True(t, bytes.Equal(att.Signature, recovered.Signature)) - require.True(t, recovered.Verify(privKey.PubKey().Address())) -} diff --git a/client/attestation/codec.go b/client/attestation/codec.go deleted file mode 100644 index 79e68ee78c94..000000000000 --- a/client/attestation/codec.go +++ /dev/null @@ -1,24 +0,0 @@ -package attestation - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var AttestationCdc *codec.Codec - -func init() { - AttestationCdc = codec.New() - codec.RegisterCrypto(AttestationCdc) - AttestationCdc.RegisterConcrete(Attestation{}, "cosmos-sdk/Attestation", nil) - AttestationCdc.Seal() -} - -// marshal -func MarshalBinaryBare(o interface{}) ([]byte, error) { - return AttestationCdc.MarshalBinaryBare(o) -} - -// unmarshal -func UnmarshalBinaryBare(bz []byte, ptr interface{}) error { - return AttestationCdc.UnmarshalBinaryBare(bz, ptr) -} diff --git a/client/broadcast.go b/client/broadcast.go new file mode 100644 index 000000000000..0912de81e818 --- /dev/null +++ b/client/broadcast.go @@ -0,0 +1,171 @@ +package client + +import ( + "context" + "fmt" + "strings" + + "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/mempool" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +// BroadcastTx broadcasts a transactions either synchronously or asynchronously +// based on the context parameters. The result of the broadcast is parsed into +// an intermediate structure which is logged if the context has a logger +// defined. +func (ctx Context) BroadcastTx(txBytes []byte) (res *sdk.TxResponse, err error) { + switch ctx.BroadcastMode { + case flags.BroadcastSync: + res, err = ctx.BroadcastTxSync(txBytes) + + case flags.BroadcastAsync: + res, err = ctx.BroadcastTxAsync(txBytes) + + case flags.BroadcastBlock: + res, err = ctx.BroadcastTxCommit(txBytes) + + default: + return nil, fmt.Errorf("unsupported return type %s; supported types: sync, async, block", ctx.BroadcastMode) + } + + return res, err +} + +// CheckTendermintError checks if the error returned from BroadcastTx is a +// Tendermint error that is returned before the tx is submitted due to +// precondition checks that failed. If an Tendermint error is detected, this +// function returns the correct code back in TxResponse. +// +// TODO: Avoid brittle string matching in favor of error matching. This requires +// a change to Tendermint's RPCError type to allow retrieval or matching against +// a concrete error type. +func CheckTendermintError(err error, txBytes []byte) *sdk.TxResponse { + if err == nil { + return nil + } + + errStr := strings.ToLower(err.Error()) + txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes)) + + switch { + case strings.Contains(errStr, strings.ToLower(mempool.ErrTxInCache.Error())): + return &sdk.TxResponse{ + Code: sdkerrors.ErrTxInMempoolCache.ABCICode(), + Codespace: sdkerrors.ErrTxInMempoolCache.Codespace(), + TxHash: txHash, + } + + case strings.Contains(errStr, "mempool is full"): + return &sdk.TxResponse{ + Code: sdkerrors.ErrMempoolIsFull.ABCICode(), + Codespace: sdkerrors.ErrMempoolIsFull.Codespace(), + TxHash: txHash, + } + + case strings.Contains(errStr, "tx too large"): + return &sdk.TxResponse{ + Code: sdkerrors.ErrTxTooLarge.ABCICode(), + Codespace: sdkerrors.ErrTxTooLarge.Codespace(), + TxHash: txHash, + } + + default: + return nil + } +} + +// BroadcastTxCommit broadcasts transaction bytes to a Tendermint node and +// waits for a commit. An error is only returned if there is no RPC node +// connection or if broadcasting fails. +// +// NOTE: This should ideally not be used as the request may timeout but the tx +// may still be included in a block. Use BroadcastTxAsync or BroadcastTxSync +// instead. +func (ctx Context) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, err + } + + res, err := node.BroadcastTxCommit(context.Background(), txBytes) + if err == nil { + return sdk.NewResponseFormatBroadcastTxCommit(res), nil + } + + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + return sdk.NewResponseFormatBroadcastTxCommit(res), err +} + +// BroadcastTxSync broadcasts transaction bytes to a Tendermint node +// synchronously (i.e. returns after CheckTx execution). +func (ctx Context) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, err + } + + res, err := node.BroadcastTxSync(context.Background(), txBytes) + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + + return sdk.NewResponseFormatBroadcastTx(res), err +} + +// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node +// asynchronously (i.e. returns immediately). +func (ctx Context) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { + node, err := ctx.GetNode() + if err != nil { + return nil, err + } + + res, err := node.BroadcastTxAsync(context.Background(), txBytes) + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + + return sdk.NewResponseFormatBroadcastTx(res), err +} + +// TxServiceBroadcast is a helper function to broadcast a Tx with the correct gRPC types +// from the tx service. Calls `clientCtx.BroadcastTx` under the hood. +func TxServiceBroadcast(grpcCtx context.Context, clientCtx Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { + if req == nil || req.TxBytes == nil { + return nil, status.Error(codes.InvalidArgument, "invalid empty tx") + } + + clientCtx = clientCtx.WithBroadcastMode(normalizeBroadcastMode(req.Mode)) + resp, err := clientCtx.BroadcastTx(req.TxBytes) + if err != nil { + return nil, err + } + + return &tx.BroadcastTxResponse{ + TxResponse: resp, + }, nil +} + +// normalizeBroadcastMode converts a broadcast mode into a normalized string +// to be passed into the clientCtx. +func normalizeBroadcastMode(mode tx.BroadcastMode) string { + switch mode { + case tx.BroadcastMode_BROADCAST_MODE_ASYNC: + return "async" + case tx.BroadcastMode_BROADCAST_MODE_BLOCK: + return "block" + case tx.BroadcastMode_BROADCAST_MODE_SYNC: + return "sync" + default: + return "unspecified" + } +} diff --git a/client/broadcast_test.go b/client/broadcast_test.go new file mode 100644 index 000000000000..5ac6e47c24b1 --- /dev/null +++ b/client/broadcast_test.go @@ -0,0 +1,71 @@ +package client + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/tmhash" + "github.com/tendermint/tendermint/mempool" + "github.com/tendermint/tendermint/rpc/client/mock" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type MockClient struct { + mock.Client + err error +} + +func (c MockClient) BroadcastTxCommit(ctx context.Context, tx tmtypes.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return nil, c.err +} + +func (c MockClient) BroadcastTxAsync(ctx context.Context, tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { + return nil, c.err +} + +func (c MockClient) BroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { + return nil, c.err +} + +func CreateContextWithErrorAndMode(err error, mode string) Context { + return Context{ + Client: MockClient{err: err}, + BroadcastMode: mode, + } +} + +// Test the correct code is returned when +func TestBroadcastError(t *testing.T) { + errors := map[error]uint32{ + mempool.ErrTxInCache: sdkerrors.ErrTxInMempoolCache.ABCICode(), + mempool.ErrTxTooLarge{}: sdkerrors.ErrTxTooLarge.ABCICode(), + mempool.ErrMempoolIsFull{}: sdkerrors.ErrMempoolIsFull.ABCICode(), + } + + modes := []string{ + flags.BroadcastAsync, + flags.BroadcastBlock, + flags.BroadcastSync, + } + + txBytes := []byte{0xA, 0xB} + txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes)) + + for _, mode := range modes { + for err, code := range errors { + ctx := CreateContextWithErrorAndMode(err, mode) + resp, returnedErr := ctx.BroadcastTx(txBytes) + require.NoError(t, returnedErr) + require.Equal(t, code, resp.Code) + require.NotEmpty(t, resp.Codespace) + require.Equal(t, txHash, resp.TxHash) + } + } + +} diff --git a/client/cmd.go b/client/cmd.go index afddf20fe2b9..f27aea3691f4 100644 --- a/client/cmd.go +++ b/client/cmd.go @@ -2,30 +2,70 @@ package client import ( "fmt" + "strings" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" ) +// ClientContextKey defines the context key used to retrieve a client.Context from +// a command's Context. +const ClientContextKey = sdk.ContextKey("client.context") + +// SetCmdClientContextHandler is to be used in a command pre-hook execution to +// read flags that populate a Context and sets that to the command's Context. +func SetCmdClientContextHandler(clientCtx Context, cmd *cobra.Command) (err error) { + clientCtx, err = ReadPersistentCommandFlags(clientCtx, cmd.Flags()) + if err != nil { + return err + } + + return SetCmdClientContext(cmd, clientCtx) +} + // ValidateCmd returns unknown command error or Help display if help flag set func ValidateCmd(cmd *cobra.Command, args []string) error { - var cmds []string - var help bool + var unknownCmd string + var skipNext bool - // construct array of commands and search for help flag for _, arg := range args { + // search for help flag if arg == "--help" || arg == "-h" { - help = true - } else if len(arg) > 0 && !(arg[0] == '-') { - cmds = append(cmds, arg) + return cmd.Help() + } + + // check if the current arg is a flag + switch { + case len(arg) > 0 && (arg[0] == '-'): + // the next arg should be skipped if the current arg is a + // flag and does not use "=" to assign the flag's value + if !strings.Contains(arg, "=") { + skipNext = true + } else { + skipNext = false + } + case skipNext: + // skip current arg + skipNext = false + case unknownCmd == "": + // unknown command found + // continue searching for help flag + unknownCmd = arg } } - if !help && len(cmds) > 0 { - err := fmt.Sprintf("unknown command \"%s\" for \"%s\"", cmds[0], cmd.CalledAs()) + // return the help screen if no unknown command is found + if unknownCmd != "" { + err := fmt.Sprintf("unknown command \"%s\" for \"%s\"", unknownCmd, cmd.CalledAs()) // build suggestions for unknown argument - if suggestions := cmd.SuggestionsFor(cmds[0]); len(suggestions) > 0 { + if suggestions := cmd.SuggestionsFor(unknownCmd); len(suggestions) > 0 { err += "\n\nDid you mean this?\n" for _, s := range suggestions { err += fmt.Sprintf("\t%v\n", s) @@ -36,3 +76,234 @@ func ValidateCmd(cmd *cobra.Command, args []string) error { return cmd.Help() } + +// ReadPersistentCommandFlags returns a Context with fields set for "persistent" +// or common flags that do not necessarily change with context. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) { + if clientCtx.OutputFormat == "" || flagSet.Changed(cli.OutputFlag) { + output, _ := flagSet.GetString(cli.OutputFlag) + clientCtx = clientCtx.WithOutputFormat(output) + } + + if clientCtx.HomeDir == "" || flagSet.Changed(flags.FlagHome) { + homeDir, _ := flagSet.GetString(flags.FlagHome) + clientCtx = clientCtx.WithHomeDir(homeDir) + } + if !clientCtx.Simulate || flagSet.Changed(flags.FlagDryRun) { + dryRun, _ := flagSet.GetBool(flags.FlagDryRun) + clientCtx = clientCtx.WithSimulation(dryRun) + } + + if clientCtx.KeyringDir == "" || flagSet.Changed(flags.FlagKeyringDir) { + keyringDir, _ := flagSet.GetString(flags.FlagKeyringDir) + + // The keyring directory is optional and falls back to the home directory + // if omitted. + if keyringDir == "" { + keyringDir = clientCtx.HomeDir + } + + clientCtx = clientCtx.WithKeyringDir(keyringDir) + } + + if clientCtx.ChainID == "" || flagSet.Changed(flags.FlagChainID) { + chainID, _ := flagSet.GetString(flags.FlagChainID) + clientCtx = clientCtx.WithChainID(chainID) + } + + if clientCtx.Keyring == nil || flagSet.Changed(flags.FlagKeyringBackend) { + keyringBackend, _ := flagSet.GetString(flags.FlagKeyringBackend) + + if keyringBackend != "" { + kr, err := NewKeyringFromBackend(clientCtx, keyringBackend) + if err != nil { + return clientCtx, err + } + + clientCtx = clientCtx.WithKeyring(kr) + } + } + + if clientCtx.Client == nil || flagSet.Changed(flags.FlagNode) { + rpcURI, _ := flagSet.GetString(flags.FlagNode) + if rpcURI != "" { + clientCtx = clientCtx.WithNodeURI(rpcURI) + + client, err := NewClientFromNode(rpcURI) + if err != nil { + return clientCtx, err + } + + clientCtx = clientCtx.WithClient(client) + } + } + + return clientCtx, nil +} + +// readQueryCommandFlags returns an updated Context with fields set based on flags +// defined in AddQueryFlagsToCmd. An error is returned if any flag query fails. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func readQueryCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) { + if clientCtx.Height == 0 || flagSet.Changed(flags.FlagHeight) { + height, _ := flagSet.GetInt64(flags.FlagHeight) + clientCtx = clientCtx.WithHeight(height) + } + + if !clientCtx.UseLedger || flagSet.Changed(flags.FlagUseLedger) { + useLedger, _ := flagSet.GetBool(flags.FlagUseLedger) + clientCtx = clientCtx.WithUseLedger(useLedger) + } + + return ReadPersistentCommandFlags(clientCtx, flagSet) +} + +// readTxCommandFlags returns an updated Context with fields set based on flags +// defined in AddTxFlagsToCmd. An error is returned if any flag query fails. +// +// Note, the provided clientCtx may have field pre-populated. The following order +// of precedence occurs: +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, error) { + clientCtx, err := ReadPersistentCommandFlags(clientCtx, flagSet) + if err != nil { + return clientCtx, err + } + + if !clientCtx.GenerateOnly || flagSet.Changed(flags.FlagGenerateOnly) { + genOnly, _ := flagSet.GetBool(flags.FlagGenerateOnly) + clientCtx = clientCtx.WithGenerateOnly(genOnly) + } + + if !clientCtx.Simulate || flagSet.Changed(flags.FlagDryRun) { + dryRun, _ := flagSet.GetBool(flags.FlagDryRun) + clientCtx = clientCtx.WithSimulation(dryRun) + } + + if !clientCtx.Offline || flagSet.Changed(flags.FlagOffline) { + offline, _ := flagSet.GetBool(flags.FlagOffline) + clientCtx = clientCtx.WithOffline(offline) + } + + if !clientCtx.UseLedger || flagSet.Changed(flags.FlagUseLedger) { + useLedger, _ := flagSet.GetBool(flags.FlagUseLedger) + clientCtx = clientCtx.WithUseLedger(useLedger) + } + + if clientCtx.BroadcastMode == "" || flagSet.Changed(flags.FlagBroadcastMode) { + bMode, _ := flagSet.GetString(flags.FlagBroadcastMode) + clientCtx = clientCtx.WithBroadcastMode(bMode) + } + + if !clientCtx.SkipConfirm || flagSet.Changed(flags.FlagSkipConfirmation) { + skipConfirm, _ := flagSet.GetBool(flags.FlagSkipConfirmation) + clientCtx = clientCtx.WithSkipConfirmation(skipConfirm) + } + + if clientCtx.SignModeStr == "" || flagSet.Changed(flags.FlagSignMode) { + signModeStr, _ := flagSet.GetString(flags.FlagSignMode) + clientCtx = clientCtx.WithSignModeStr(signModeStr) + } + + if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) { + from, _ := flagSet.GetString(flags.FlagFrom) + fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly) + if err != nil { + return clientCtx, err + } + + clientCtx = clientCtx.WithFrom(from).WithFromAddress(fromAddr).WithFromName(fromName) + + // If the `from` signer account is a ledger key, we need to use + // SIGN_MODE_AMINO_JSON, because ledger doesn't support proto yet. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8109 + if keyType == keyring.TypeLedger && clientCtx.SignModeStr != flags.SignModeLegacyAminoJSON { + fmt.Println("Default sign-mode 'direct' not supported by Ledger, using sign-mode 'amino-json'.") + clientCtx = clientCtx.WithSignModeStr(flags.SignModeLegacyAminoJSON) + } + } + + return clientCtx, nil +} + +// ReadHomeFlag checks if home flag is changed. +// If this is a case, we update HomeDir field of Client Context +/* Discovered a bug with Cory +./build/simd init andrei --home ./test +cd test/config there is no client.toml configuration file +*/ +func ReadHomeFlag(clientCtx Context, cmd *cobra.Command) Context { + if cmd.Flags().Changed(flags.FlagHome) { + rootDir, _ := cmd.Flags().GetString(flags.FlagHome) + clientCtx = clientCtx.WithHomeDir(rootDir) + } + + return clientCtx +} + +// GetClientQueryContext returns a Context from a command with fields set based on flags +// defined in AddQueryFlagsToCmd. An error is returned if any flag query fails. +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func GetClientQueryContext(cmd *cobra.Command) (Context, error) { + ctx := GetClientContextFromCmd(cmd) + return readQueryCommandFlags(ctx, cmd.Flags()) +} + +// GetClientTxContext returns a Context from a command with fields set based on flags +// defined in AddTxFlagsToCmd. An error is returned if any flag query fails. +// +// - client.Context field not pre-populated & flag not set: uses default flag value +// - client.Context field not pre-populated & flag set: uses set flag value +// - client.Context field pre-populated & flag not set: uses pre-populated value +// - client.Context field pre-populated & flag set: uses set flag value +func GetClientTxContext(cmd *cobra.Command) (Context, error) { + ctx := GetClientContextFromCmd(cmd) + return readTxCommandFlags(ctx, cmd.Flags()) +} + +// GetClientContextFromCmd returns a Context from a command or an empty Context +// if it has not been set. +func GetClientContextFromCmd(cmd *cobra.Command) Context { + if v := cmd.Context().Value(ClientContextKey); v != nil { + clientCtxPtr := v.(*Context) + return *clientCtxPtr + } + + return Context{} +} + +// SetCmdClientContext sets a command's Context value to the provided argument. +func SetCmdClientContext(cmd *cobra.Command, clientCtx Context) error { + v := cmd.Context().Value(ClientContextKey) + if v == nil { + return errors.New("client context not set") + } + + clientCtxPtr := v.(*Context) + *clientCtxPtr = clientCtx + + return nil +} diff --git a/client/cmd_test.go b/client/cmd_test.go index 675cc296aeb4..02e2c414f63a 100644 --- a/client/cmd_test.go +++ b/client/cmd_test.go @@ -1,12 +1,16 @@ package client_test import ( + "context" + "fmt" "testing" "github.com/spf13/cobra" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" ) func TestValidateCmd(t *testing.T) { @@ -37,10 +41,12 @@ func TestValidateCmd(t *testing.T) { args []string wantErr bool }{ - {"misspelled command", []string{"comission"}, true}, // nolint: misspell + {"misspelled command", []string{"COMMISSION"}, true}, {"no command provided", []string{}, false}, - {"help flag", []string{"comission", "--help"}, false}, // nolint: misspell - {"shorthand help flag", []string{"comission", "-h"}, false}, // nolint: misspell + {"help flag", []string{"COMMISSION", "--help"}, false}, + {"shorthand help flag", []string{"COMMISSION", "-h"}, false}, + {"flag only, no command provided", []string{"--gas", "1000atom"}, false}, + {"flag and misspelled command", []string{"--gas", "1000atom", "COMMISSION"}, true}, } for _, tt := range tests { @@ -48,3 +54,60 @@ func TestValidateCmd(t *testing.T) { require.Equal(t, tt.wantErr, err != nil, tt.reason) } } + +func TestSetCmdClientContextHandler(t *testing.T) { + initClientCtx := client.Context{}.WithHomeDir("/foo/bar").WithChainID("test-chain").WithKeyringDir("/foo/bar") + + newCmd := func() *cobra.Command { + c := &cobra.Command{ + PreRunE: func(cmd *cobra.Command, args []string) error { + return client.SetCmdClientContextHandler(initClientCtx, cmd) + }, + RunE: func(cmd *cobra.Command, _ []string) error { + _, err := client.GetClientTxContext(cmd) + return err + }, + } + + c.Flags().String(flags.FlagChainID, "", "network chain ID") + + return c + } + + testCases := []struct { + name string + expectedContext client.Context + args []string + }{ + { + "no flags set", + initClientCtx, + []string{}, + }, + { + "flags set", + initClientCtx.WithChainID("new-chain-id"), + []string{ + fmt.Sprintf("--%s=new-chain-id", flags.FlagChainID), + }, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) + + cmd := newCmd() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + cmd.SetArgs(tc.args) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + clientCtx := client.GetClientContextFromCmd(cmd) + require.Equal(t, tc.expectedContext, clientCtx) + }) + } +} diff --git a/client/config.go b/client/config.go deleted file mode 100644 index 226d056fea06..000000000000 --- a/client/config.go +++ /dev/null @@ -1,167 +0,0 @@ -package client - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path" - "strconv" - - toml "github.com/pelletier/go-toml" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/flags" -) - -const ( - flagGet = "get" -) - -var configDefaults = map[string]string{ - "chain-id": "", - "keyring-backend": "os", - "output": "text", - "node": "tcp://localhost:26657", - "broadcast-mode": "sync", -} - -// ConfigCmd returns a CLI command to interactively create an application CLI -// config file. -func ConfigCmd(defaultCLIHome string) *cobra.Command { - cmd := &cobra.Command{ - Use: "config [value]", - Short: "Create or query an application CLI configuration file", - RunE: runConfigCmd, - Args: cobra.RangeArgs(0, 2), - } - - cmd.Flags().String(flags.FlagHome, defaultCLIHome, - "set client's home directory for configuration") - cmd.Flags().Bool(flagGet, false, - "print configuration value or its default if unset") - return cmd -} - -func runConfigCmd(cmd *cobra.Command, args []string) error { - cfgFile, err := ensureConfFile(viper.GetString(flags.FlagHome)) - if err != nil { - return err - } - - getAction := viper.GetBool(flagGet) - if getAction && len(args) != 1 { - return fmt.Errorf("wrong number of arguments") - } - - // load configuration - tree, err := loadConfigFile(cfgFile) - if err != nil { - return err - } - - // print the config and exit - if len(args) == 0 { - s, err := tree.ToTomlString() - if err != nil { - return err - } - fmt.Print(s) - return nil - } - - key := args[0] - - // get config value for a given key - if getAction { - switch key { - case "trace", "trust-node", "indent": - fmt.Println(tree.GetDefault(key, false).(bool)) - - default: - if defaultValue, ok := configDefaults[key]; ok { - fmt.Println(tree.GetDefault(key, defaultValue).(string)) - return nil - } - - return errUnknownConfigKey(key) - } - - return nil - } - - if len(args) != 2 { - return fmt.Errorf("wrong number of arguments") - } - - value := args[1] - - // set config value for a given key - switch key { - case "chain-id", "output", "node", "broadcast-mode", "keyring-backend": - tree.Set(key, value) - - case "trace", "trust-node", "indent": - boolVal, err := strconv.ParseBool(value) - if err != nil { - return err - } - - tree.Set(key, boolVal) - - default: - return errUnknownConfigKey(key) - } - - // save configuration to disk - if err := saveConfigFile(cfgFile, tree); err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "configuration saved to %s\n", cfgFile) - return nil -} - -func ensureConfFile(rootDir string) (string, error) { - cfgPath := path.Join(rootDir, "config") - if err := os.MkdirAll(cfgPath, os.ModePerm); err != nil { - return "", err - } - - return path.Join(cfgPath, "config.toml"), nil -} - -func loadConfigFile(cfgFile string) (*toml.Tree, error) { - if _, err := os.Stat(cfgFile); os.IsNotExist(err) { - fmt.Fprintf(os.Stderr, "%s does not exist\n", cfgFile) - return toml.Load(``) - } - - bz, err := ioutil.ReadFile(cfgFile) - if err != nil { - return nil, err - } - - tree, err := toml.LoadBytes(bz) - if err != nil { - return nil, err - } - - return tree, nil -} - -func saveConfigFile(cfgFile string, tree io.WriterTo) error { - fp, err := os.OpenFile(cfgFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) - if err != nil { - return err - } - defer fp.Close() - - _, err = tree.WriteTo(fp) - return err -} - -func errUnknownConfigKey(key string) error { - return fmt.Errorf("unknown configuration key: %q", key) -} diff --git a/client/config/cmd.go b/client/config/cmd.go new file mode 100644 index 000000000000..523939f39ed0 --- /dev/null +++ b/client/config/cmd.go @@ -0,0 +1,96 @@ +package config + +import ( + "encoding/json" + "fmt" + "path/filepath" + + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" +) + +// Cmd returns a CLI command to interactively create an application CLI +// config file. +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "config [value]", + Short: "Create or query an application CLI configuration file", + RunE: runConfigCmd, + Args: cobra.RangeArgs(0, 2), + } + return cmd +} + +func runConfigCmd(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + configPath := filepath.Join(clientCtx.HomeDir, "config") + + conf, err := getClientConfig(configPath, clientCtx.Viper) + if err != nil { + return fmt.Errorf("couldn't get client config: %v", err) + } + + switch len(args) { + case 0: + // print all client config fields to sdt out + s, _ := json.MarshalIndent(conf, "", "\t") + cmd.Println(string(s)) + + case 1: + // it's a get + key := args[0] + + switch key { + case flags.FlagChainID: + cmd.Println(conf.ChainID) + case flags.FlagKeyringBackend: + cmd.Println(conf.KeyringBackend) + case tmcli.OutputFlag: + cmd.Println(conf.Output) + case flags.FlagNode: + cmd.Println(conf.Node) + case flags.FlagBroadcastMode: + cmd.Println(conf.BroadcastMode) + default: + err := errUnknownConfigKey(key) + return fmt.Errorf("couldn't get the value for the key: %v, error: %v", key, err) + } + + case 2: + // it's set + key, value := args[0], args[1] + + switch key { + case flags.FlagChainID: + conf.SetChainID(value) + case flags.FlagKeyringBackend: + conf.SetKeyringBackend(value) + case tmcli.OutputFlag: + conf.SetOutput(value) + case flags.FlagNode: + conf.SetNode(value) + case flags.FlagBroadcastMode: + conf.SetBroadcastMode(value) + default: + return errUnknownConfigKey(key) + } + + confFile := filepath.Join(configPath, "client.toml") + if err := writeConfigToFile(confFile, conf); err != nil { + return fmt.Errorf("could not write client config to the file: %v", err) + } + + default: + panic("cound not execute config command") + } + + return nil +} + +func errUnknownConfigKey(key string) error { + return fmt.Errorf("unknown configuration key: %q", key) +} diff --git a/client/config/config.go b/client/config/config.go new file mode 100644 index 000000000000..6297cf2c26b1 --- /dev/null +++ b/client/config/config.go @@ -0,0 +1,97 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client" +) + +// Default constants +const ( + chainID = "" + keyringBackend = "os" + output = "text" + node = "tcp://localhost:26657" + broadcastMode = "sync" +) + +type ClientConfig struct { + ChainID string `mapstructure:"chain-id" json:"chain-id"` + KeyringBackend string `mapstructure:"keyring-backend" json:"keyring-backend"` + Output string `mapstructure:"output" json:"output"` + Node string `mapstructure:"node" json:"node"` + BroadcastMode string `mapstructure:"broadcast-mode" json:"broadcast-mode"` +} + +// defaultClientConfig returns the reference to ClientConfig with default values. +func defaultClientConfig() *ClientConfig { + return &ClientConfig{chainID, keyringBackend, output, node, broadcastMode} +} + +func (c *ClientConfig) SetChainID(chainID string) { + c.ChainID = chainID +} + +func (c *ClientConfig) SetKeyringBackend(keyringBackend string) { + c.KeyringBackend = keyringBackend +} + +func (c *ClientConfig) SetOutput(output string) { + c.Output = output +} + +func (c *ClientConfig) SetNode(node string) { + c.Node = node +} + +func (c *ClientConfig) SetBroadcastMode(broadcastMode string) { + c.BroadcastMode = broadcastMode +} + +// ReadFromClientConfig reads values from client.toml file and updates them in client Context +func ReadFromClientConfig(ctx client.Context) (client.Context, error) { + configPath := filepath.Join(ctx.HomeDir, "config") + configFilePath := filepath.Join(configPath, "client.toml") + conf := defaultClientConfig() + + // if config.toml file does not exist we create it and write default ClientConfig values into it. + if _, err := os.Stat(configFilePath); os.IsNotExist(err) { + if err := ensureConfigPath(configPath); err != nil { + return ctx, fmt.Errorf("couldn't make client config: %v", err) + } + + if err := writeConfigToFile(configFilePath, conf); err != nil { + return ctx, fmt.Errorf("could not write client config to the file: %v", err) + } + } + + conf, err := getClientConfig(configPath, ctx.Viper) + if err != nil { + return ctx, fmt.Errorf("couldn't get client config: %v", err) + } + // we need to update KeyringDir field on Client Context first cause it is used in NewKeyringFromBackend + ctx = ctx.WithOutputFormat(conf.Output). + WithChainID(conf.ChainID). + WithKeyringDir(ctx.HomeDir) + + keyring, err := client.NewKeyringFromBackend(ctx, conf.KeyringBackend) + if err != nil { + return ctx, fmt.Errorf("couldn't get key ring: %v", err) + } + + ctx = ctx.WithKeyring(keyring) + + // https://github.com/cosmos/cosmos-sdk/issues/8986 + client, err := client.NewClientFromNode(conf.Node) + if err != nil { + return ctx, fmt.Errorf("couldn't get client from nodeURI: %v", err) + } + + ctx = ctx.WithNodeURI(conf.Node). + WithClient(client). + WithBroadcastMode(conf.BroadcastMode) + + return ctx, nil +} diff --git a/client/config/config_test.go b/client/config/config_test.go new file mode 100644 index 000000000000..c058edf8301a --- /dev/null +++ b/client/config/config_test.go @@ -0,0 +1,107 @@ +package config_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" +) + +const ( + nodeEnv = "NODE" + testNode1 = "http://localhost:1" + testNode2 = "http://localhost:2" +) + +// initClientContext initiates client Context for tests +func initClientContext(t *testing.T, envVar string) (client.Context, func()) { + home := t.TempDir() + clientCtx := client.Context{}. + WithHomeDir(home). + WithViper("") + + clientCtx.Viper.BindEnv(nodeEnv) + if envVar != "" { + os.Setenv(nodeEnv, envVar) + } + + clientCtx, err := config.ReadFromClientConfig(clientCtx) + require.NoError(t, err) + + return clientCtx, func() { _ = os.RemoveAll(home) } +} + +func TestConfigCmd(t *testing.T) { + clientCtx, cleanup := initClientContext(t, testNode1) + defer func() { + os.Unsetenv(nodeEnv) + cleanup() + }() + + // NODE=http://localhost:1 ./build/simd config node http://localhost:2 + cmd := config.Cmd() + args := []string{"node", testNode2} + _, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + require.NoError(t, err) + + //./build/simd config node //http://localhost:1 + b := bytes.NewBufferString("") + cmd.SetOut(b) + cmd.SetArgs([]string{"node"}) + cmd.Execute() + out, err := ioutil.ReadAll(b) + require.NoError(t, err) + require.Equal(t, string(out), testNode1+"\n") +} + +func TestConfigCmdEnvFlag(t *testing.T) { + const ( + defaultNode = "http://localhost:26657" + ) + + tt := []struct { + name string + envVar string + args []string + expNode string + }{ + {"env var is set with no flag", testNode1, []string{"validators"}, testNode1}, + {"env var is set with a flag", testNode1, []string{"validators", fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2}, + {"env var is not set with no flag", "", []string{"validators"}, defaultNode}, + {"env var is not set with a flag", "", []string{"validators", fmt.Sprintf("--%s=%s", flags.FlagNode, testNode2)}, testNode2}, + } + + for _, tc := range tt { + tc := tc + t.Run(tc.name, func(t *testing.T) { + clientCtx, cleanup := initClientContext(t, tc.envVar) + defer func() { + if tc.envVar != "" { + os.Unsetenv(nodeEnv) + } + cleanup() + }() + /* + env var is set with a flag + + NODE=http://localhost:1 ./build/simd q staking validators --node http://localhost:2 + Error: post failed: Post "http://localhost:2": dial tcp 127.0.0.1:2: connect: connection refused + + We dial http://localhost:2 cause a flag has the higher priority than env variable. + */ + cmd := cli.GetQueryCmd() + _, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expNode, "Output does not contain expected Node") + }) + } +} diff --git a/client/config/toml.go b/client/config/toml.go new file mode 100644 index 000000000000..0393a5b6acc8 --- /dev/null +++ b/client/config/toml.go @@ -0,0 +1,70 @@ +package config + +import ( + "bytes" + "io/ioutil" + "os" + "text/template" + + "github.com/spf13/viper" +) + +const defaultConfigTemplate = `# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +############################################################################### +### Client Configuration ### +############################################################################### + +# The network chain ID +chain-id = "{{ .ChainID }}" +# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory) +keyring-backend = "{{ .KeyringBackend }}" +# CLI output format (text|json) +output = "{{ .Output }}" +# : to Tendermint RPC interface for this chain +node = "{{ .Node }}" +# Transaction broadcasting mode (sync|async|block) +broadcast-mode = "{{ .BroadcastMode }}" +` + +// writeConfigToFile parses defaultConfigTemplate, renders config using the template and writes it to +// configFilePath. +func writeConfigToFile(configFilePath string, config *ClientConfig) error { + var buffer bytes.Buffer + + tmpl := template.New("clientConfigFileTemplate") + configTemplate, err := tmpl.Parse(defaultConfigTemplate) + if err != nil { + return err + } + + if err := configTemplate.Execute(&buffer, config); err != nil { + return err + } + + return ioutil.WriteFile(configFilePath, buffer.Bytes(), 0600) +} + +// ensureConfigPath creates a directory configPath if it does not exist +func ensureConfigPath(configPath string) error { + return os.MkdirAll(configPath, os.ModePerm) +} + +// getClientConfig reads values from client.toml file and unmarshalls them into ClientConfig +func getClientConfig(configPath string, v *viper.Viper) (*ClientConfig, error) { + v.AddConfigPath(configPath) + v.SetConfigName("client") + v.SetConfigType("toml") + + if err := v.ReadInConfig(); err != nil { + return nil, err + } + + conf := new(ClientConfig) + if err := v.Unmarshal(conf); err != nil { + return nil, err + } + + return conf, nil +} diff --git a/client/config_test.go b/client/config_test.go deleted file mode 100644 index 242facaeea5f..000000000000 --- a/client/config_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package client - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client/flags" -) - -// For https://github.com/cosmos/cosmos-sdk/issues/3899 -func Test_runConfigCmdTwiceWithShorterNodeValue(t *testing.T) { - // Prepare environment - t.Parallel() - configHome, cleanup := tmpDir(t) - defer cleanup() - _ = os.RemoveAll(filepath.Join(configHome, "config")) - viper.Set(flags.FlagHome, configHome) - - // Init command config - cmd := ConfigCmd(configHome) - assert.NotNil(t, cmd) - - err := cmd.RunE(cmd, []string{"node", "tcp://localhost:26657"}) - assert.Nil(t, err) - - err = cmd.RunE(cmd, []string{"node", "--get"}) - assert.Nil(t, err) - - err = cmd.RunE(cmd, []string{"node", "tcp://local:26657"}) - assert.Nil(t, err) - - err = cmd.RunE(cmd, []string{"node", "--get"}) - assert.Nil(t, err) -} - -func tmpDir(t *testing.T) (string, func()) { - dir, err := ioutil.TempDir("", t.Name()+"_") - require.NoError(t, err) - return dir, func() { _ = os.RemoveAll(dir) } -} diff --git a/client/context.go b/client/context.go new file mode 100644 index 000000000000..cacdb6ee961c --- /dev/null +++ b/client/context.go @@ -0,0 +1,340 @@ +package client + +import ( + "encoding/json" + "io" + "os" + + "github.com/spf13/viper" + + "gopkg.in/yaml.v2" + + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" + rpcclient "github.com/tendermint/tendermint/rpc/client" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Context implements a typical context created in SDK modules for transaction +// handling and queries. +type Context struct { + FromAddress sdk.AccAddress + Client rpcclient.Client + ChainID string + JSONMarshaler codec.JSONMarshaler + InterfaceRegistry codectypes.InterfaceRegistry + Input io.Reader + Keyring keyring.Keyring + Output io.Writer + OutputFormat string + Height int64 + HomeDir string + KeyringDir string + From string + BroadcastMode string + FromName string + SignModeStr string + UseLedger bool + Simulate bool + GenerateOnly bool + Offline bool + SkipConfirm bool + TxConfig TxConfig + AccountRetriever AccountRetriever + NodeURI string + Viper *viper.Viper + + // TODO: Deprecated (remove). + LegacyAmino *codec.LegacyAmino +} + +// WithKeyring returns a copy of the context with an updated keyring. +func (ctx Context) WithKeyring(k keyring.Keyring) Context { + ctx.Keyring = k + return ctx +} + +// WithInput returns a copy of the context with an updated input. +func (ctx Context) WithInput(r io.Reader) Context { + ctx.Input = r + return ctx +} + +// WithJSONMarshaler returns a copy of the Context with an updated JSONMarshaler. +func (ctx Context) WithJSONMarshaler(m codec.JSONMarshaler) Context { + ctx.JSONMarshaler = m + return ctx +} + +// WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec. +// TODO: Deprecated (remove). +func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context { + ctx.LegacyAmino = cdc + return ctx +} + +// WithOutput returns a copy of the context with an updated output writer (e.g. stdout). +func (ctx Context) WithOutput(w io.Writer) Context { + ctx.Output = w + return ctx +} + +// WithFrom returns a copy of the context with an updated from address or name. +func (ctx Context) WithFrom(from string) Context { + ctx.From = from + return ctx +} + +// WithOutputFormat returns a copy of the context with an updated OutputFormat field. +func (ctx Context) WithOutputFormat(format string) Context { + ctx.OutputFormat = format + return ctx +} + +// WithNodeURI returns a copy of the context with an updated node URI. +func (ctx Context) WithNodeURI(nodeURI string) Context { + ctx.NodeURI = nodeURI + return ctx +} + +// WithHeight returns a copy of the context with an updated height. +func (ctx Context) WithHeight(height int64) Context { + ctx.Height = height + return ctx +} + +// WithClient returns a copy of the context with an updated RPC client +// instance. +func (ctx Context) WithClient(client rpcclient.Client) Context { + ctx.Client = client + return ctx +} + +// WithUseLedger returns a copy of the context with an updated UseLedger flag. +func (ctx Context) WithUseLedger(useLedger bool) Context { + ctx.UseLedger = useLedger + return ctx +} + +// WithChainID returns a copy of the context with an updated chain ID. +func (ctx Context) WithChainID(chainID string) Context { + ctx.ChainID = chainID + return ctx +} + +// WithHomeDir returns a copy of the Context with HomeDir set. +func (ctx Context) WithHomeDir(dir string) Context { + if dir != "" { + ctx.HomeDir = dir + } + return ctx +} + +// WithKeyringDir returns a copy of the Context with KeyringDir set. +func (ctx Context) WithKeyringDir(dir string) Context { + ctx.KeyringDir = dir + return ctx +} + +// WithGenerateOnly returns a copy of the context with updated GenerateOnly value +func (ctx Context) WithGenerateOnly(generateOnly bool) Context { + ctx.GenerateOnly = generateOnly + return ctx +} + +// WithSimulation returns a copy of the context with updated Simulate value +func (ctx Context) WithSimulation(simulate bool) Context { + ctx.Simulate = simulate + return ctx +} + +// WithOffline returns a copy of the context with updated Offline value. +func (ctx Context) WithOffline(offline bool) Context { + ctx.Offline = offline + return ctx +} + +// WithFromName returns a copy of the context with an updated from account name. +func (ctx Context) WithFromName(name string) Context { + ctx.FromName = name + return ctx +} + +// WithFromAddress returns a copy of the context with an updated from account +// address. +func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { + ctx.FromAddress = addr + return ctx +} + +// WithBroadcastMode returns a copy of the context with an updated broadcast +// mode. +func (ctx Context) WithBroadcastMode(mode string) Context { + ctx.BroadcastMode = mode + return ctx +} + +// WithSignModeStr returns a copy of the context with an updated SignMode +// value. +func (ctx Context) WithSignModeStr(signModeStr string) Context { + ctx.SignModeStr = signModeStr + return ctx +} + +// WithSkipConfirmation returns a copy of the context with an updated SkipConfirm +// value. +func (ctx Context) WithSkipConfirmation(skip bool) Context { + ctx.SkipConfirm = skip + return ctx +} + +// WithTxConfig returns the context with an updated TxConfig +func (ctx Context) WithTxConfig(generator TxConfig) Context { + ctx.TxConfig = generator + return ctx +} + +// WithAccountRetriever returns the context with an updated AccountRetriever +func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { + ctx.AccountRetriever = retriever + return ctx +} + +// WithInterfaceRegistry returns the context with an updated InterfaceRegistry +func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { + ctx.InterfaceRegistry = interfaceRegistry + return ctx +} + +// WithViper returns the context with Viper field. This Viper instance is used to read +// client-side config from the config file. +func (ctx Context) WithViper(prefix string) Context { + v := viper.New() + v.SetEnvPrefix(prefix) + v.AutomaticEnv() + ctx.Viper = v + return ctx +} + +// PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout +func (ctx Context) PrintString(str string) error { + return ctx.PrintBytes([]byte(str)) +} + +// PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. +// NOTE: for printing a complex state object, you should use ctx.PrintOutput +func (ctx Context) PrintBytes(o []byte) error { + writer := ctx.Output + if writer == nil { + writer = os.Stdout + } + + _, err := writer.Write(o) + return err +} + +// PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is +// either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint +// will be JSON encoded using ctx.JSONMarshaler. An error is returned upon failure. +func (ctx Context) PrintProto(toPrint proto.Message) error { + // always serialize JSON initially because proto json can't be directly YAML encoded + out, err := ctx.JSONMarshaler.MarshalJSON(toPrint) + if err != nil { + return err + } + return ctx.printOutput(out) +} + +// PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type +// and uses amino JSON encoding. +// Deprecated: It will be removed in the near future! +func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { + out, err := ctx.LegacyAmino.MarshalJSON(toPrint) + if err != nil { + return err + } + return ctx.printOutput(out) +} + +func (ctx Context) printOutput(out []byte) error { + if ctx.OutputFormat == "text" { + // handle text format by decoding and re-encoding JSON as YAML + var j interface{} + + err := json.Unmarshal(out, &j) + if err != nil { + return err + } + + out, err = yaml.Marshal(j) + if err != nil { + return err + } + } + + writer := ctx.Output + if writer == nil { + writer = os.Stdout + } + + _, err := writer.Write(out) + if err != nil { + return err + } + + if ctx.OutputFormat != "text" { + // append new-line for formats besides YAML + _, err = writer.Write([]byte("\n")) + if err != nil { + return err + } + } + + return nil +} + +// GetFromFields returns a from account address, account name and keyring type, given either +// an address or key name. If genOnly is true, only a valid Bech32 cosmos +// address is returned. +func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddress, string, keyring.KeyType, error) { + if from == "" { + return nil, "", 0, nil + } + + if genOnly { + addr, err := sdk.AccAddressFromBech32(from) + if err != nil { + return nil, "", 0, errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode") + } + + return addr, "", 0, nil + } + + var info keyring.Info + if addr, err := sdk.AccAddressFromBech32(from); err == nil { + info, err = kr.KeyByAddress(addr) + if err != nil { + return nil, "", 0, err + } + } else { + info, err = kr.Key(from) + if err != nil { + return nil, "", 0, err + } + } + + return info.GetAddress(), info.GetName(), info.GetType(), nil +} + +// NewKeyringFromBackend gets a Keyring object from a backend +func NewKeyringFromBackend(ctx Context, backend string) (keyring.Keyring, error) { + if ctx.GenerateOnly || ctx.Simulate { + return keyring.New(sdk.KeyringServiceName(), keyring.BackendMemory, ctx.KeyringDir, ctx.Input) + } + + return keyring.New(sdk.KeyringServiceName(), backend, ctx.KeyringDir, ctx.Input) +} diff --git a/client/context/broadcast.go b/client/context/broadcast.go deleted file mode 100644 index 67251b6da89d..000000000000 --- a/client/context/broadcast.go +++ /dev/null @@ -1,140 +0,0 @@ -package context - -import ( - "fmt" - "strings" - - "github.com/tendermint/tendermint/crypto/tmhash" - "github.com/tendermint/tendermint/mempool" - - "github.com/cosmos/cosmos-sdk/client/flags" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// BroadcastTx broadcasts a transactions either synchronously or asynchronously -// based on the context parameters. The result of the broadcast is parsed into -// an intermediate structure which is logged if the context has a logger -// defined. -func (ctx CLIContext) BroadcastTx(txBytes []byte) (res sdk.TxResponse, err error) { - switch ctx.BroadcastMode { - case flags.BroadcastSync: - res, err = ctx.BroadcastTxSync(txBytes) - - case flags.BroadcastAsync: - res, err = ctx.BroadcastTxAsync(txBytes) - - case flags.BroadcastBlock: - res, err = ctx.BroadcastTxCommit(txBytes) - - default: - return sdk.TxResponse{}, fmt.Errorf("unsupported return type %s; supported types: sync, async, block", ctx.BroadcastMode) - } - - return res, err -} - -// CheckTendermintError checks if the error returned from BroadcastTx is a -// Tendermint error that is returned before the tx is submitted due to -// precondition checks that failed. If an Tendermint error is detected, this -// function returns the correct code back in TxResponse. -// -// TODO: Avoid brittle string matching in favor of error matching. This requires -// a change to Tendermint's RPCError type to allow retrieval or matching against -// a concrete error type. -func CheckTendermintError(err error, txBytes []byte) *sdk.TxResponse { - if err == nil { - return nil - } - - errStr := strings.ToLower(err.Error()) - txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes)) - - switch { - case strings.Contains(errStr, strings.ToLower(mempool.ErrTxInCache.Error())): - return &sdk.TxResponse{ - Code: sdkerrors.ErrTxInMempoolCache.ABCICode(), - TxHash: txHash, - } - - case strings.Contains(errStr, "mempool is full"): - return &sdk.TxResponse{ - Code: sdkerrors.ErrMempoolIsFull.ABCICode(), - TxHash: txHash, - } - - case strings.Contains(errStr, "tx too large"): - return &sdk.TxResponse{ - Code: sdkerrors.ErrTxTooLarge.ABCICode(), - TxHash: txHash, - } - - default: - return nil - } -} - -// BroadcastTxCommit broadcasts transaction bytes to a Tendermint node and -// waits for a commit. An error is only returned if there is no RPC node -// connection or if broadcasting fails. -// -// NOTE: This should ideally not be used as the request may timeout but the tx -// may still be included in a block. Use BroadcastTxAsync or BroadcastTxSync -// instead. -func (ctx CLIContext) BroadcastTxCommit(txBytes []byte) (sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return sdk.TxResponse{}, err - } - - res, err := node.BroadcastTxCommit(txBytes) - if err != nil { - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return *errRes, nil - } - - return sdk.NewResponseFormatBroadcastTxCommit(res), err - } - - if !res.CheckTx.IsOK() { - return sdk.NewResponseFormatBroadcastTxCommit(res), nil - } - - if !res.DeliverTx.IsOK() { - return sdk.NewResponseFormatBroadcastTxCommit(res), nil - } - - return sdk.NewResponseFormatBroadcastTxCommit(res), nil -} - -// BroadcastTxSync broadcasts transaction bytes to a Tendermint node -// synchronously (i.e. returns after CheckTx execution). -func (ctx CLIContext) BroadcastTxSync(txBytes []byte) (sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return sdk.TxResponse{}, err - } - - res, err := node.BroadcastTxSync(txBytes) - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return *errRes, nil - } - - return sdk.NewResponseFormatBroadcastTx(res), err -} - -// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node -// asynchronously (i.e. returns immediately). -func (ctx CLIContext) BroadcastTxAsync(txBytes []byte) (sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return sdk.TxResponse{}, err - } - - res, err := node.BroadcastTxAsync(txBytes) - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return *errRes, nil - } - - return sdk.NewResponseFormatBroadcastTx(res), err -} diff --git a/client/context/broadcast_test.go b/client/context/broadcast_test.go deleted file mode 100644 index 530645b9d187..000000000000 --- a/client/context/broadcast_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package context - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/tmhash" - "github.com/tendermint/tendermint/mempool" - "github.com/tendermint/tendermint/rpc/client/mock" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/client/flags" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -type MockClient struct { - mock.Client - err error -} - -func (c MockClient) BroadcastTxCommit(tx tmtypes.Tx) (*ctypes.ResultBroadcastTxCommit, error) { - return nil, c.err -} - -func (c MockClient) BroadcastTxAsync(tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { - return nil, c.err -} - -func (c MockClient) BroadcastTxSync(tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { - return nil, c.err -} - -func CreateContextWithErrorAndMode(err error, mode string) CLIContext { - return CLIContext{ - Client: MockClient{err: err}, - BroadcastMode: mode, - } -} - -// Test the correct code is returned when -func TestBroadcastError(t *testing.T) { - errors := map[error]uint32{ - mempool.ErrTxInCache: sdkerrors.ErrTxInMempoolCache.ABCICode(), - mempool.ErrTxTooLarge{}: sdkerrors.ErrTxTooLarge.ABCICode(), - mempool.ErrMempoolIsFull{}: sdkerrors.ErrMempoolIsFull.ABCICode(), - } - - modes := []string{ - flags.BroadcastAsync, - flags.BroadcastBlock, - flags.BroadcastSync, - } - - txBytes := []byte{0xA, 0xB} - txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes)) - - for _, mode := range modes { - for err, code := range errors { - ctx := CreateContextWithErrorAndMode(err, mode) - resp, returnedErr := ctx.BroadcastTx(txBytes) - require.NoError(t, returnedErr) - require.Equal(t, code, resp.Code) - require.Equal(t, txHash, resp.TxHash) - } - } - -} diff --git a/client/context/context.go b/client/context/context.go deleted file mode 100644 index de3408306e3f..000000000000 --- a/client/context/context.go +++ /dev/null @@ -1,298 +0,0 @@ -package context - -import ( - "fmt" - "io" - "os" - - "github.com/pkg/errors" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/libs/cli" - tmlite "github.com/tendermint/tendermint/lite" - rpcclient "github.com/tendermint/tendermint/rpc/client" - rpcclienthttp "github.com/tendermint/tendermint/rpc/client/http" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// CLIContext implements a typical CLI context created in SDK modules for -// transaction handling and queries. -type CLIContext struct { - FromAddress sdk.AccAddress - Client rpcclient.Client - ChainID string - Keybase keys.Keybase - Input io.Reader - Output io.Writer - OutputFormat string - Height int64 - HomeDir string - NodeURI string - From string - BroadcastMode string - Verifier tmlite.Verifier - FromName string - Codec *codec.Codec - TrustNode bool - UseLedger bool - Simulate bool - GenerateOnly bool - Indent bool - SkipConfirm bool -} - -// NewCLIContextWithInputAndFrom returns a new initialized CLIContext with parameters from the -// command line using Viper. It takes a io.Reader and and key name or address and populates -// the FromName and FromAddress field accordingly. It will also create Tendermint verifier -// using the chain ID, home directory and RPC URI provided by the command line. If using -// a CLIContext in tests or any non CLI-based environment, the verifier will not be created -// and will be set as nil because FlagTrustNode must be set. -func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext { - var nodeURI string - var rpc rpcclient.Client - - genOnly := viper.GetBool(flags.FlagGenerateOnly) - fromAddress, fromName, err := GetFromFields(input, from, genOnly) - if err != nil { - fmt.Printf("failed to get from fields: %v\n", err) - os.Exit(1) - } - - if !genOnly { - nodeURI = viper.GetString(flags.FlagNode) - if nodeURI != "" { - rpc, err = rpcclienthttp.New(nodeURI, "/websocket") - if err != nil { - fmt.Printf("failted to get client: %v\n", err) - os.Exit(1) - } - } - } - - ctx := CLIContext{ - Client: rpc, - ChainID: viper.GetString(flags.FlagChainID), - Input: input, - Output: os.Stdout, - NodeURI: nodeURI, - From: viper.GetString(flags.FlagFrom), - OutputFormat: viper.GetString(cli.OutputFlag), - Height: viper.GetInt64(flags.FlagHeight), - HomeDir: viper.GetString(flags.FlagHome), - TrustNode: viper.GetBool(flags.FlagTrustNode), - UseLedger: viper.GetBool(flags.FlagUseLedger), - BroadcastMode: viper.GetString(flags.FlagBroadcastMode), - Simulate: viper.GetBool(flags.FlagDryRun), - GenerateOnly: genOnly, - FromAddress: fromAddress, - FromName: fromName, - Indent: viper.GetBool(flags.FlagIndentResponse), - SkipConfirm: viper.GetBool(flags.FlagSkipConfirmation), - } - - // create a verifier for the specific chain ID and RPC client - verifier, err := CreateVerifier(ctx, DefaultVerifierCacheSize) - if err != nil && viper.IsSet(flags.FlagTrustNode) { - fmt.Printf("failed to create verifier: %s\n", err) - os.Exit(1) - } - - return ctx.WithVerifier(verifier) -} - -// NewCLIContextWithFrom returns a new initialized CLIContext with parameters from the -// command line using Viper. It takes a key name or address and populates the FromName and -// FromAddress field accordingly. It will also create Tendermint verifier using -// the chain ID, home directory and RPC URI provided by the command line. If using -// a CLIContext in tests or any non CLI-based environment, the verifier will not -// be created and will be set as nil because FlagTrustNode must be set. -func NewCLIContextWithFrom(from string) CLIContext { - return NewCLIContextWithInputAndFrom(os.Stdin, from) -} - -// NewCLIContext returns a new initialized CLIContext with parameters from the -// command line using Viper. -func NewCLIContext() CLIContext { return NewCLIContextWithFrom(viper.GetString(flags.FlagFrom)) } - -// NewCLIContextWithInput returns a new initialized CLIContext with a io.Reader and parameters -// from the command line using Viper. -func NewCLIContextWithInput(input io.Reader) CLIContext { - return NewCLIContextWithInputAndFrom(input, viper.GetString(flags.FlagFrom)) -} - -// WithInput returns a copy of the context with an updated input. -func (ctx CLIContext) WithInput(r io.Reader) CLIContext { - ctx.Input = r - return ctx -} - -// WithCodec returns a copy of the context with an updated codec. -func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { - ctx.Codec = cdc - return ctx -} - -// WithOutput returns a copy of the context with an updated output writer (e.g. stdout). -func (ctx CLIContext) WithOutput(w io.Writer) CLIContext { - ctx.Output = w - return ctx -} - -// WithFrom returns a copy of the context with an updated from address or name. -func (ctx CLIContext) WithFrom(from string) CLIContext { - ctx.From = from - return ctx -} - -// WithTrustNode returns a copy of the context with an updated TrustNode flag. -func (ctx CLIContext) WithTrustNode(trustNode bool) CLIContext { - ctx.TrustNode = trustNode - return ctx -} - -// WithNodeURI returns a copy of the context with an updated node URI. -func (ctx CLIContext) WithNodeURI(nodeURI string) CLIContext { - ctx.NodeURI = nodeURI - client, err := rpcclienthttp.New(nodeURI, "/websocket") - if err != nil { - panic(err) - } - ctx.Client = client - return ctx -} - -// WithHeight returns a copy of the context with an updated height. -func (ctx CLIContext) WithHeight(height int64) CLIContext { - ctx.Height = height - return ctx -} - -// WithClient returns a copy of the context with an updated RPC client -// instance. -func (ctx CLIContext) WithClient(client rpcclient.Client) CLIContext { - ctx.Client = client - return ctx -} - -// WithUseLedger returns a copy of the context with an updated UseLedger flag. -func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext { - ctx.UseLedger = useLedger - return ctx -} - -// WithVerifier returns a copy of the context with an updated Verifier. -func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext { - ctx.Verifier = verifier - return ctx -} - -// WithChainID returns a copy of the context with an updated chain ID. -func (ctx CLIContext) WithChainID(chainID string) CLIContext { - ctx.ChainID = chainID - return ctx -} - -// WithGenerateOnly returns a copy of the context with updated GenerateOnly value -func (ctx CLIContext) WithGenerateOnly(generateOnly bool) CLIContext { - ctx.GenerateOnly = generateOnly - return ctx -} - -// WithSimulation returns a copy of the context with updated Simulate value -func (ctx CLIContext) WithSimulation(simulate bool) CLIContext { - ctx.Simulate = simulate - return ctx -} - -// WithFromName returns a copy of the context with an updated from account name. -func (ctx CLIContext) WithFromName(name string) CLIContext { - ctx.FromName = name - return ctx -} - -// WithFromAddress returns a copy of the context with an updated from account -// address. -func (ctx CLIContext) WithFromAddress(addr sdk.AccAddress) CLIContext { - ctx.FromAddress = addr - return ctx -} - -// WithBroadcastMode returns a copy of the context with an updated broadcast -// mode. -func (ctx CLIContext) WithBroadcastMode(mode string) CLIContext { - ctx.BroadcastMode = mode - return ctx -} - -// PrintOutput prints output while respecting output and indent flags -// NOTE: pass in marshalled structs that have been unmarshaled -// because this function will panic on marshaling errors -func (ctx CLIContext) PrintOutput(toPrint interface{}) error { - var ( - out []byte - err error - ) - - switch ctx.OutputFormat { - case "text": - out, err = yaml.Marshal(&toPrint) - - case "json": - if ctx.Indent { - out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ") - } else { - out, err = ctx.Codec.MarshalJSON(toPrint) - } - } - - if err != nil { - return err - } - - fmt.Println(string(out)) - return nil -} - -// GetFromFields returns a from account address and Keybase name given either -// an address or key name. If genOnly is true, only a valid Bech32 cosmos -// address is returned. -func GetFromFields(input io.Reader, from string, genOnly bool) (sdk.AccAddress, string, error) { - if from == "" { - return nil, "", nil - } - - if genOnly { - addr, err := sdk.AccAddressFromBech32(from) - if err != nil { - return nil, "", errors.Wrap(err, "must provide a valid Bech32 address for generate-only") - } - - return addr, "", nil - } - - keybase, err := keys.NewKeyring(sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), input) - if err != nil { - return nil, "", err - } - - var info keys.Info - if addr, err := sdk.AccAddressFromBech32(from); err == nil { - info, err = keybase.GetByAddress(addr) - if err != nil { - return nil, "", err - } - } else { - info, err = keybase.Get(from) - if err != nil { - return nil, "", err - } - } - - return info.GetAddress(), info.GetName(), nil -} diff --git a/client/context/errors.go b/client/context/errors.go deleted file mode 100644 index 2c83c375dd53..000000000000 --- a/client/context/errors.go +++ /dev/null @@ -1,22 +0,0 @@ -package context - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ErrInvalidAccount returns a standardized error reflecting that a given -// account address does not exist. -func ErrInvalidAccount(addr sdk.AccAddress) error { - return fmt.Errorf(`no account with address %s was found in the state. -Are you sure there has been a transaction involving it?`, addr) -} - -// ErrVerifyCommit returns a common error reflecting that the blockchain commit at a given -// height can't be verified. The reason is that the base checkpoint of the certifier is -// newer than the given height -func ErrVerifyCommit(height int64) error { - return fmt.Errorf(`the height of base truststore in the light client is higher than height %d. -Can't verify blockchain proof at this height. Please set --trust-node to true and try again`, height) -} diff --git a/client/context/query.go b/client/context/query.go deleted file mode 100644 index 542ba4c38e59..000000000000 --- a/client/context/query.go +++ /dev/null @@ -1,215 +0,0 @@ -package context - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" - tmbytes "github.com/tendermint/tendermint/libs/bytes" - tmliteErr "github.com/tendermint/tendermint/lite/errors" - tmliteProxy "github.com/tendermint/tendermint/lite/proxy" - rpcclient "github.com/tendermint/tendermint/rpc/client" - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/store/rootmulti" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GetNode returns an RPC client. If the context's client is not defined, an -// error is returned. -func (ctx CLIContext) GetNode() (rpcclient.Client, error) { - if ctx.Client == nil { - return nil, errors.New("no RPC client defined") - } - - return ctx.Client, nil -} - -// Query performs a query to a Tendermint node with the provided path. -// It returns the result and height of the query upon success or an error if -// the query fails. -func (ctx CLIContext) Query(path string) ([]byte, int64, error) { - return ctx.query(path, nil) -} - -// QueryWithData performs a query to a Tendermint node with the provided path -// and a data payload. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) QueryWithData(path string, data []byte) ([]byte, int64, error) { - return ctx.query(path, data) -} - -// QueryStore performs a query to a Tendermint node with the provided key and -// store name. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { - return ctx.queryStore(key, storeName, "key") -} - -// QuerySubspace performs a query to a Tendermint node with the provided -// store name and subspace. It returns key value pair and height of the query -// upon success or an error if the query fails. -func (ctx CLIContext) QuerySubspace(subspace []byte, storeName string) (res []sdk.KVPair, height int64, err error) { - resRaw, height, err := ctx.queryStore(subspace, storeName, "subspace") - if err != nil { - return res, height, err - } - - ctx.Codec.MustUnmarshalBinaryLengthPrefixed(resRaw, &res) - return -} - -// GetFromAddress returns the from address from the context's name. -func (ctx CLIContext) GetFromAddress() sdk.AccAddress { - return ctx.FromAddress -} - -// GetFromName returns the key name for the current context. -func (ctx CLIContext) GetFromName() string { - return ctx.FromName -} - -// query performs a query to a Tendermint node with the provided store name -// and path. It returns the result and height of the query upon success -// or an error if the query fails. In addition, it will verify the returned -// proof if TrustNode is disabled. If proof verification fails or the query -// height is invalid, an error will be returned. -func (ctx CLIContext) query(path string, key tmbytes.HexBytes) (res []byte, height int64, err error) { - node, err := ctx.GetNode() - if err != nil { - return res, height, err - } - - opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Prove: !ctx.TrustNode, - } - - result, err := node.ABCIQueryWithOptions(path, key, opts) - if err != nil { - return res, height, err - } - - resp := result.Response - if !resp.IsOK() { - return res, resp.Height, errors.New(resp.Log) - } - - // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil - } - - err = ctx.verifyProof(path, resp) - if err != nil { - return res, resp.Height, err - } - - return resp.Value, resp.Height, nil -} - -// Verify verifies the consensus proof at given height. -func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) { - if ctx.Verifier == nil { - return tmtypes.SignedHeader{}, fmt.Errorf("missing valid certifier to verify data from distrusted node") - } - check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier) - switch { - case tmliteErr.IsErrCommitNotFound(err): - return tmtypes.SignedHeader{}, ErrVerifyCommit(height) - case err != nil: - return tmtypes.SignedHeader{}, err - } - - return check, nil -} - -// verifyProof perform response proof verification. -func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) error { - if ctx.Verifier == nil { - return fmt.Errorf("missing valid certifier to verify data from distrusted node") - } - - // the AppHash for height H is in header H+1 - commit, err := ctx.Verify(resp.Height + 1) - if err != nil { - return err - } - - // TODO: Instead of reconstructing, stash on CLIContext field? - prt := rootmulti.DefaultProofRuntime() - - // TODO: Better convention for path? - storeName, err := parseQueryStorePath(queryPath) - if err != nil { - return err - } - - kp := merkle.KeyPath{} - kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) - kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) - - if resp.Value == nil { - err = prt.VerifyAbsence(resp.Proof, commit.Header.AppHash, kp.String()) - if err != nil { - return errors.Wrap(err, "failed to prove merkle proof") - } - return nil - } - err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value) - if err != nil { - return errors.Wrap(err, "failed to prove merkle proof") - } - - return nil -} - -// queryStore performs a query to a Tendermint node with the provided a store -// name and path. It returns the result and height of the query upon success -// or an error if the query fails. -func (ctx CLIContext) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { - path := fmt.Sprintf("/store/%s/%s", storeName, endPath) - return ctx.query(path, key) -} - -// isQueryStoreWithProof expects a format like /// -// queryType must be "store" and subpath must be "key" to require a proof. -func isQueryStoreWithProof(path string) bool { - if !strings.HasPrefix(path, "/") { - return false - } - - paths := strings.SplitN(path[1:], "/", 3) - switch { - case len(paths) != 3: - return false - case paths[0] != "store": - return false - case rootmulti.RequireProof("/" + paths[2]): - return true - } - - return false -} - -// parseQueryStorePath expects a format like /store//key. -func parseQueryStorePath(path string) (storeName string, err error) { - if !strings.HasPrefix(path, "/") { - return "", errors.New("expected path to start with /") - } - - paths := strings.SplitN(path[1:], "/", 3) - switch { - case len(paths) != 3: - return "", errors.New("expected format like /store//key") - case paths[0] != "store": - return "", errors.New("expected format like /store//key") - case paths[2] != "key": - return "", errors.New("expected format like /store//key") - } - - return paths[1], nil -} diff --git a/client/context/verifier.go b/client/context/verifier.go deleted file mode 100644 index 9f91dc519da3..000000000000 --- a/client/context/verifier.go +++ /dev/null @@ -1,56 +0,0 @@ -package context - -import ( - "path/filepath" - - "github.com/pkg/errors" - "github.com/tendermint/tendermint/libs/log" - tmlite "github.com/tendermint/tendermint/lite" - tmliteproxy "github.com/tendermint/tendermint/lite/proxy" - rpcclienthttp "github.com/tendermint/tendermint/rpc/client/http" -) - -const ( - verifierDir = ".lite_verifier" - - // DefaultVerifierCacheSize defines the default Tendermint cache size. - DefaultVerifierCacheSize = 10 -) - -// CreateVerifier returns a Tendermint verifier from a CLIContext object and -// cache size. An error is returned if the CLIContext is missing required values -// or if the verifier could not be created. A CLIContext must at the very least -// have the chain ID and home directory set. If the CLIContext has TrustNode -// enabled, no verifier will be created. -func CreateVerifier(ctx CLIContext, cacheSize int) (tmlite.Verifier, error) { - if ctx.TrustNode { - return nil, nil - } - - switch { - case ctx.ChainID == "": - return nil, errors.New("must provide a valid chain ID to create verifier") - - case ctx.HomeDir == "": - return nil, errors.New("must provide a valid home directory to create verifier") - - case ctx.Client == nil && ctx.NodeURI == "": - return nil, errors.New("must provide a valid RPC client or RPC URI to create verifier") - } - - var err error - - // create an RPC client based off of the RPC URI if no RPC client exists - client := ctx.Client - if client == nil { - client, err = rpcclienthttp.New(ctx.NodeURI, "/websocket") - if err != nil { - return nil, err - } - } - - return tmliteproxy.NewVerifier( - ctx.ChainID, filepath.Join(ctx.HomeDir, ctx.ChainID, verifierDir), - client, log.NewNopLogger(), cacheSize, - ) -} diff --git a/client/context/verifier_test.go b/client/context/verifier_test.go deleted file mode 100644 index bd2539492349..000000000000 --- a/client/context/verifier_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package context_test - -import ( - "io/ioutil" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client/context" -) - -func TestCreateVerifier(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "example") - require.NoError(t, err) - - testCases := []struct { - name string - ctx context.CLIContext - expectErr bool - }{ - {"no chain ID", context.CLIContext{}, true}, - {"no home directory", context.CLIContext{}.WithChainID("test"), true}, - {"no client or RPC URI", context.CLIContext{HomeDir: tmpDir}.WithChainID("test"), true}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - verifier, err := context.CreateVerifier(tc.ctx, context.DefaultVerifierCacheSize) - require.Equal(t, tc.expectErr, err != nil, err) - - if !tc.expectErr { - require.NotNil(t, verifier) - } - }) - } -} diff --git a/client/context_test.go b/client/context_test.go new file mode 100644 index 000000000000..978f31732c1c --- /dev/null +++ b/client/context_test.go @@ -0,0 +1,115 @@ +package client_test + +import ( + "bytes" + "context" + "os" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func TestMain(m *testing.M) { + viper.Set(flags.FlagKeyringBackend, keyring.BackendMemory) + os.Exit(m.Run()) +} + +func TestContext_PrintObject(t *testing.T) { + ctx := client.Context{} + + animal := &testdata.Dog{ + Size_: "big", + Name: "Spot", + } + any, err := types.NewAnyWithValue(animal) + require.NoError(t, err) + hasAnimal := &testdata.HasAnimal{ + Animal: any, + X: 10, + } + + // + // proto + // + registry := testdata.NewTestInterfaceRegistry() + ctx = ctx.WithJSONMarshaler(codec.NewProtoCodec(registry)) + + // json + buf := &bytes.Buffer{} + ctx = ctx.WithOutput(buf) + ctx.OutputFormat = "json" + err = ctx.PrintProto(hasAnimal) + require.NoError(t, err) + require.Equal(t, + `{"animal":{"@type":"/testdata.Dog","size":"big","name":"Spot"},"x":"10"} +`, string(buf.Bytes())) + + // yaml + buf = &bytes.Buffer{} + ctx = ctx.WithOutput(buf) + ctx.OutputFormat = "text" + err = ctx.PrintProto(hasAnimal) + require.NoError(t, err) + require.Equal(t, + `animal: + '@type': /testdata.Dog + name: Spot + size: big +x: "10" +`, string(buf.Bytes())) + + // + // amino + // + amino := testdata.NewTestAmino() + ctx = ctx.WithLegacyAmino(&codec.LegacyAmino{Amino: amino}) + + // json + buf = &bytes.Buffer{} + ctx = ctx.WithOutput(buf) + ctx.OutputFormat = "json" + err = ctx.PrintObjectLegacy(hasAnimal) + require.NoError(t, err) + require.Equal(t, + `{"type":"testdata/HasAnimal","value":{"animal":{"type":"testdata/Dog","value":{"size":"big","name":"Spot"}},"x":"10"}} +`, string(buf.Bytes())) + + // yaml + buf = &bytes.Buffer{} + ctx = ctx.WithOutput(buf) + ctx.OutputFormat = "text" + err = ctx.PrintObjectLegacy(hasAnimal) + require.NoError(t, err) + require.Equal(t, + `type: testdata/HasAnimal +value: + animal: + type: testdata/Dog + value: + name: Spot + size: big + x: "10" +`, string(buf.Bytes())) +} + +func TestCLIQueryConn(t *testing.T) { + cfg := network.DefaultConfig() + cfg.NumValidators = 1 + + n := network.New(t, cfg) + defer n.Cleanup() + + testClient := testdata.NewQueryClient(n.Validators[0].ClientCtx) + res, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + require.NoError(t, err) + require.Equal(t, "hello", res.Message) +} diff --git a/client/debug/main.go b/client/debug/main.go index 6b6a52c000c9..54b75e712b64 100644 --- a/client/debug/main.go +++ b/client/debug/main.go @@ -9,23 +9,22 @@ import ( "github.com/spf13/cobra" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/version" ) -func Cmd(cdc *codec.Codec) *cobra.Command { +func Cmd() *cobra.Command { cmd := &cobra.Command{ Use: "debug", Short: "Tool for helping with debugging your application", RunE: client.ValidateCmd, } - cmd.AddCommand(PubkeyCmd(cdc)) + cmd.AddCommand(PubkeyCmd()) cmd.AddCommand(AddrCmd()) cmd.AddCommand(RawBytesCmd()) @@ -35,19 +34,19 @@ func Cmd(cdc *codec.Codec) *cobra.Command { // getPubKeyFromString returns a Tendermint PubKey (PubKeyEd25519) by attempting // to decode the pubkey string from hex, base64, and finally bech32. If all // encodings fail, an error is returned. -func getPubKeyFromString(pkstr string) (crypto.PubKey, error) { - var pubKey ed25519.PubKeyEd25519 - +func getPubKeyFromString(pkstr string) (cryptotypes.PubKey, error) { bz, err := hex.DecodeString(pkstr) if err == nil { - copy(pubKey[:], bz) - return pubKey, nil + if len(bz) == ed25519.PubKeySize { + return &ed25519.PubKey{Key: bz}, nil + } } bz, err = base64.StdEncoding.DecodeString(pkstr) if err == nil { - copy(pubKey[:], bz) - return pubKey, nil + if len(bz) == ed25519.PubKeySize { + return &ed25519.PubKey{Key: bz}, nil + } } pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, pkstr) @@ -65,10 +64,10 @@ func getPubKeyFromString(pkstr string) (crypto.PubKey, error) { return pk, nil } - return nil, fmt.Errorf("pubkey '%s' invalid; expected hex, base64, or bech32", pubKey) + return nil, fmt.Errorf("pubkey '%s' invalid; expected hex, base64, or bech32 of correct size", pkstr) } -func PubkeyCmd(cdc *codec.Codec) *cobra.Command { +func PubkeyCmd() *cobra.Command { return &cobra.Command{ Use: "pubkey [pubkey]", Short: "Decode a ED25519 pubkey from hex, base64, or bech32", @@ -76,21 +75,23 @@ func PubkeyCmd(cdc *codec.Codec) *cobra.Command { Example: $ %s debug pubkey TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz -$ %s debug pubkey fetch1e0jnq2sun3dzjh8p2xq95kk0expwmd7synm9tl - `, version.ClientName, version.ClientName), +$ %s debug pubkey cosmos1e0jnq2sun3dzjh8p2xq95kk0expwmd7shwjpfg + `, version.AppName, version.AppName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + pk, err := getPubKeyFromString(args[0]) if err != nil { return err } - edPK, ok := pk.(ed25519.PubKeyEd25519) + edPK, ok := pk.(*ed25519.PubKey) if !ok { - return fmt.Errorf("invalid pubkey type; expected ED25519") + return errors.Wrapf(errors.ErrInvalidType, "invalid pubkey type; expected ED25519") } - pubKeyJSONBytes, err := cdc.MarshalJSON(edPK) + pubKeyJSONBytes, err := clientCtx.LegacyAmino.MarshalJSON(edPK) if err != nil { return err } @@ -108,7 +109,7 @@ $ %s debug pubkey fetch1e0jnq2sun3dzjh8p2xq95kk0expwmd7synm9tl } cmd.Println("Address:", edPK.Address()) - cmd.Printf("Hex: %X\n", edPK[:]) + cmd.Printf("Hex: %X\n", edPK.Key) cmd.Println("JSON (base64):", string(pubKeyJSONBytes)) cmd.Println("Bech32 Acc:", accPub) cmd.Println("Bech32 Validator Operator:", valPub) @@ -127,7 +128,7 @@ func AddrCmd() *cobra.Command { Example: $ %s debug addr cosmos1e0jnq2sun3dzjh8p2xq95kk0expwmd7shwjpfg - `, version.ClientName), + `, version.AppName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -171,9 +172,9 @@ func RawBytesCmd() *cobra.Command { Example: $ %s debug raw-bytes [72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100] - `, version.ClientName), + `, version.AppName), Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { stringBytes := args[0] stringBytes = strings.Trim(stringBytes, "[") stringBytes = strings.Trim(stringBytes, "]") @@ -181,7 +182,7 @@ $ %s debug raw-bytes [72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 11 byteArray := []byte{} for _, s := range spl { - b, err := strconv.Atoi(s) + b, err := strconv.ParseInt(s, 10, 8) if err != nil { return err } diff --git a/client/docs/config.json b/client/docs/config.json new file mode 100644 index 000000000000..32580f8bef53 --- /dev/null +++ b/client/docs/config.json @@ -0,0 +1,143 @@ +{ + "swagger": "2.0", + "info": { + "title": "Cosmos SDK - Legacy REST and gRPC Gateway docs", + "description": "A REST interface for state queries, legacy transactions", + "version": "1.0.0" + }, + "apis": [ + { + "url": "./client/docs/swagger_legacy.yaml", + "dereference": { + "circular": "ignore" + } + }, + { + "url": "./tmp-swagger-gen/cosmos/auth/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "AuthParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/bank/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "BankParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/base/tendermint/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "BaseParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/distribution/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "DistributionParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/evidence/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "EvidenceParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/gov/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "GovParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/mint/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "MintParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/params/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "Params" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/slashing/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "SlashingParams" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/staking/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "StakingParams", + "DelegatorValidators": "StakingDelegatorValidators" + } + } + }, + { + "url": "./tmp-swagger-gen/cosmos/tx/v1beta1/service.swagger.json", + "dereference": { + "circular": "ignore" + } + }, + { + "url": "./tmp-swagger-gen/cosmos/upgrade/v1beta1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "UpgradeParams" + } + } + }, + { + "url": "./tmp-swagger-gen/ibc/core/channel/v1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "IBCChannelParams" + } + } + }, + { + "url": "./tmp-swagger-gen/ibc/core/client/v1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "IBCClientParams" + } + } + }, + { + "url": "./tmp-swagger-gen/ibc/core/connection/v1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "IBCConnectionParams" + } + } + }, + { + "url": "./tmp-swagger-gen/ibc/applications/transfer/v1/query.swagger.json", + "operationIds": { + "rename": { + "Params": "IBCTransferParams" + } + } + } + ] +} diff --git a/client/docs/statik/init.go b/client/docs/statik/init.go new file mode 100644 index 000000000000..7d91b40fcd37 --- /dev/null +++ b/client/docs/statik/init.go @@ -0,0 +1,3 @@ +package statik + +//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/docs/statik diff --git a/client/docs/statik/statik.go b/client/docs/statik/statik.go new file mode 100644 index 000000000000..131219c92561 --- /dev/null +++ b/client/docs/statik/statik.go @@ -0,0 +1,13 @@ +// Code generated by statik. DO NOT EDIT. + +// Package statik contains static assets. +package statik + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8\x00\xbd\x01B\xfe\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x01\x84IDATx\x01\x95S\x03Luq\x1c\xfd\x8c\xf1\xc3\xec0\xa7)\xcda\xb6k6\xb2\x9b\xf9\xb2k\xc85/\xdb\x8dqx\xc6\x94m\xcc{\xef\x7fO\xff\xf3l\xdc\xed\xf2\xe0\xfe\xf8\xc9\xffP\x14\x11/\x14[\xa3P\xc4\xa1\xbc?\xf1t>7\x12s\x13\x03\x85\xca7IR a\xb5j\x8f\xa71\xbe]\x88\xf6\xb9L\xf0\x1c\x93\xcf\xda\xe3)\x10\x93f\x8d\xe4\x06\x13\xcf\xde<\x9b\xd14\x95\x8a\x92\x81OA\xcfF\x89\xdd<\x9b M\xe6}L\xe4\x07\x15\xc5\xf5\xe3\xffI\x0c{\xd6\x8d\xffs\x994\xbasfh\xae?\xafk\x1aprw\x10 <\xb9\xdb\xc7\x86\xa6\xd1\x19I\n\xa8\xb1\xd7\x84y3g\x171T$\xb5c\x7fq\xfbbq\xbfk\x8e'\x1dQ\xb0\xc2,\x92\x0bx|;F\xe5\xf0\xef\x00\x83\xf2\xa1\x1fx|?q\xbd\xcb\xc2\x16\x80ZF\xf0\xc4J\xf3\xe3\xe4n1\xcc\x17k`:}\xcby\xe8\x98\xcbB\xc7|6z\x97r\xd14\x9d\x06\xd3\xf9\x8a\xe4\x94\x90\x8b\xb6\xd9\x0cP\xebc@\xd0|\xbe*\xc94\xc8\xa7\x98'\xcdh\x00\xe3\xd92\xa6vK}\x0cB\xa4\xf0+D\n\xc7\x81)\xb0\x10\x9a\xe3\xa9\xd8\x8bx\xe4(\xa2\xbb\x8dl\x0d\x01\xb6\x8a-\xf378\xbe\xdd\xc7\xa6\xb6\xc9\xd9\xc6d\xd8\\m\xf4\x0c\x92 uQ\x0e\xd2\xf5\xb3\xd1\xf1w\xdfQ\x16\xb34a$\xa1\xc4\xc4(V\xbcF\xd9\xdf\xa4\x91\xe9\xb0&,\x12+\xcd\x93\xcf\x1c\x1cb\xdc\xca\x00qt\xeb\xcc-\x14\x89\xfe\xfc\x0fm2j\x88\xec\xccs\x18\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x08\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8\x00u\x04\x8a\xfb\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x04|ID\xc4\xcf\xd0@\x04&%\xad\x1e\x16\x0f\xf7\x8d\x97AR\xfa\xca\xe7l\x87\x05\xf8\xd2\xfb\x0c\x84\x1d\x0dLVY\xdc/ju\x13\x1a\x88\xd2\xa0\xaaa\x82|nzp_\xf4\x03\xc8 \xd4;^\x8a9}\xeeu\x9a\x91 `\x04\x14s\xec\xe1\x0c\xc6]\xa3\x05``\xd1w\x12*~ \x00\xf3\xae\xd3\xa0\x9cb\x82\xa2bx(\xb3n\x1fqx\xd2\xf2\xda4\x1d\x8a}\x1ck\xd4>\x9cI+\xeb\xb3\xf4k\xc8u`L\x93\xf3]4\xb5\xd0\xc3\xe33\xd9\xee\xd7\xf2\xd9\x19\xea\x18\xc9\xc1Y:\x18\xfb(-\xadN\x82\x06e\xd5\x1f0\xa2\x1dV\xf8\xbe0\xc1\x985\x01\xf8\xd2~\\\xa6\xa5\xb5)&\xf6\x98V\x80l\xe4\x03\xf8\x03\x04\x00s\x9a^\xec\x85\x00\xf4+\x0b\x00\xe1:G\xf2p\x96\x0e\xc4,\xe46\x1e5\xbbP\xdd\x15J\x80}\xce\xa4\xe2\xc8{m\xa4\xe2\xc3\xc2\x01\x07\xc0\xdb\xa4\x18-\xa1\x931\xba\x10S\xfa%\xb6P`\x10\x19v\x99#|Gg\x9b \x10W\xf6\x8dI1\xba\x92\xd66\x17E\x12\xfa\xd9\xa8\xf3UTe\n\x1b\x95\x9d\x81f\xe5\x18\xa5umc\x81\x86\xa6\xeb\xec \x804\xcbg\x17\xa19\xfa\xc6\xf7<\xa3\xbd\xf2\x0e\x7f\x02\x80\x97Y\xc7\xac\x184$h\xa3v\xba! \xcc{\xcd\xb4!\xb1\xd8\x92%h\xe3\x93\xdc\xd3_\xda1\xe6\xaei\xcf\x83\xa6p\xbc$\xf0\xb2\xda\x94\xa2q\x14B@\x13\xdb\xff\xf3\xd7\x0d\xfaA\xb9\xc5n{\x8e\xd6Y\x08\x01u\xc1'~\x16\x8e\xe9\x04\xa2\xfbA+\xc74\x0c\x98\xab\xd7:\xfc0\xd1v\xaf$\xa2#\xb7\xf1\x08\xfdm!OXh8\x10j|g\xd1\xe0a\xb2\x99\x04\x9a[y\x9a\xbdk\xf24C$\xa0\x9e#\x9f\xa3\xa8\x001\xc6\x1a\"\xc0\xe4i\xa6\xcc0\xf3\xf7\xb7\xf5XE\xb8\xe0\xa1\xc9\xc2\x0c\x90\x83\x80$\x838\xdf\xd6\xe3\xd4\x82FNG\x0f\x876\x8a\xbf1\xa8d(\xa7@\x8cQX\x90\xdb\x19\x9f\xc5YG\xe9\x9e\x00\xa5y3]\x9aJ\xe1\"\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x086B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00 \x00index.htmlUT\x05\x00\x01\x80Cm8\x9cT]k\xdc:\x10}\xdf_1Q\x1e\x92\\\"\xfb&\x81p\xf1\xb5\xfd\x90\xa6\xa5\x81\x94\x06\x92}(\xa5\x14\xd9\x1a{\xa7\x91\xa5E\x92\xf7#!\xff\xbdX\xf6\xae\xb7\xdd\x90BYX\x8f\xe7\x9c9\x1a\x1d\x8d\x9c\x1ep\x0e\x1f\x1f>\xddBe,8/<\x95 \xc9yKE\xeb\xc9h(Z-\x15B\xd1\x92\x92\xc0y>I\x0f\xae?\xbf{\xf8r\xf7\x1ef\xbeQ\xf9$\xed\x1e\xa0\x84\xae3\x86\x9a\xe5\x13\x80t\x86Bv\x01@\xda\xa0\x17P\xce\x84u\xe836}\xf8\xc0\xffc\x03\xe4\xc9+\xcc\xef\x97\xa2\xae\xd1\xc2\xf4&\x8d\xfbL\x8f*\xd2\x8f`Qe\xcc\xf9\xb5B7C\xf4\x0c\xfcz\x8e\x19\xf3\xb8\xf2q\xe9\x1c\x83\x99\xc5*c\xae\xd7\xe0-E!\xbb'A\xa5\xd1\x9bbjD\x8d\xf1\\\xd7\x9b\xeaJ,:\x9c_\x9c\xaf.\xce\xa3\x008zB\x97\xb1\x90a\x10\xff\x9d\xde\xd9\xe5\xea\xec\xf2\x17\xbd\x90\x19\xf5\xc2\xc6\xfa\x18\x82\x9bC\xf8<<\x01\n\xb3\xe2\x8e\x9eH\xd7 \x14\xc6J\xb4\xbc0\xab\xff\xb7\xb8Y\xa0\xad\x94Y&\xc0\x1b\xf3\xc4]i\x8dR\x85\xb0\x8e/\xd0z*\x85\xda\xe7\xf2u\x02=q\x83\xbdL\x86\xe0\x9f\xd3M\x90\x14X\x19\x8b\xe3\xbb\xa8<\xda7\xfb#=CK~O\xb40r\xbdW\xd8\x08[\x93N\xfe\x1d\xdb+D\xf9X[\xd3j\x99\xc0a%\xba\xdf(\xd5\xfd\xa7\xf1\xd6\xaf4\xee'\xac\x0b;\xf9\xc1OI\x0b \xb9;\x0e,OcI\x8b|2\x18^Z\x9a{p\xb6\xdc%\xf1~\xc6\xa3\x1f\x8e\xe5\xdd*\x81\x94\xbfY\xe1\xbc\xd0R(\xa3\x91\xcf-:\xf4o\x14\xf7/K\xd2\xd2,#\xa3\x95\x11\x122\xa8Z]v\x17\xec\xf8\x04\x9e7N\xc51\\\x85{&\xc0\xad\x9d\xc7f\xc8\x97F;\x0f-A\x06\xc3m\x99\xde\\\x85\x9e\x8fGG[\xab\x12`Q\xeb\x8c\xd8v\xfb_}K7\xd3F\xfe]\xb1\xa1\x82h%q{\x8b\x9b6\x88/\xc4i }\xc07u~}\xe5\xad\xfd\xc9\x98\xe7q\xd8_}o\xf1\x92%\x9dx\x15\x9f\xd3yO\xbdX]\x1aA\xc9>t\xd6o\x93\xd3\x92\xf2\x04l\xc5\x8d\x92jz\xc1jN\xd6\xf2\xa9\x87\xfa\xb5]\x05\xcc\xf9\x1acB\xa9,\x9f\xd0\x08\x05\xb7\x962\xec\xdb\xb6\xe2\x16b\xc6\xd5\x942H\x05KfI\x06\x7f\x9c\x98\xa8\xc0\xd5\x9c\xa2\x0c\x13\xa3\xe7U\x8e\xb55;'Nk\xe6\xd0\x9d;\xd4%^\x14\xbd\xd5\xf7\x92QN\x8e.\x1c`\x079m\xe3\x9e\x8a\xfe\xed\xa2\xad\xe0y>\xe6\xe23\xdc\xf8u\xa7=\xa3\xf6\xa1\x98\xb4\x17g\xa9\xf4\x1dA\xa8Z\xe4\xf6\x88_\xfc)\xf8\xd5N\xcf,\xea\xb4\xabS\xf2\xd2\xe0v\x10\x90\x82\xbd\xb3\xe1\xc1g\xc8>\x120\x0c{\x1d\xbd\x1c\xd1\x7fd\xb4\xbf\x82|\xf7\x9f\xd0\xa7\x1e\x82\xc5`H\xc0\x94F3p0$H.\x0f]v3\xaa\x9b\x1c\x83EW}\xba4\x12O`_\xb5!H5\xd1 \x9a\x0c\xaa\xcd\x04\x8cE\xe7M:\xe1\x08\xfe\xefQ\xab\x02\xfe\xb7A\xeb\xb6k\xbb\x05{\xef\x8e\xde\x84\xcb\x9c\xb2\x8f\x04\xd7U\xf9\x9aQ:\xbe\xf51\xf1\x1a\xaaW\x97uR\xdd\xe7\xf59\x974\xb7\xfc5s\xd0\xc4P\xdf\xdd\"\xd7\x96\xc2\xdab7x\xb8;\xfc\x01\xfa'\x00\x00\xff\xffPK\x07\x08]\x12r 9\x03\x00\x00T \x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00 \x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8\xec\xfdyw\xdb6\xf68\x8c\xff\xffy\x15\xd7\xfa\xf6\x9b!kZ\xb1\x9d\xa5\xad\x13\xc5\x93\xc5m\xb3g\xe2\xa4\xcb\xa8\x1a\x1fZ\x82,6\x14\xa8\x90\x90m\xb5\xf2\xef\xb5\xff\x0e.\x00\x12$\x01\x10r\xdc\x99\xf9<\xcf\xc3s\xdaX\\\xb0\\\\\\\xdc\xfdn\xc1tI\xc7,\xc9h@\"`!\xfc\xf9?\x00\x00\xbd\xec\xf4w2f=\x18\x0c\x80\xad\x16$\x9b\x02\xb9\\d9+\xe0\xd6-\xd3\xd3y6Y\xa6\x04\x0e\xe5\x1f}\xf5\xf6\x00X\x10\xc2\x01\xf4T7\xfaG\x132M(\xe1-\x8a\xbf\xfa\xf1|\x02\x87\xf2G0\x1c\xe1\x80\x0e\\\x839T\x7f\xf5\x8f/\xe2\xb33\x92\x7f|\xfedI'));&\xe6'\xffs\x15\xb0YRD\xd5\xf4\xd5\xd4s\xc2\x969\xd5\xc0\xa2\x1e\xf0\xeb<\xce\x81\xc1\x00\xfe\xbcz\xf0?\xe5M\xf5*\xd0 \xd7_\xe6W2\x85\x80\x0d\xf3Q\xa8\xda\xe5?\x14t\x1e\xd4^\xe5mg|t\xc3|\xc4\xbb\xa8=\xc4\xb6\x0e \x8fZw\xd3\x03\xd8\xdak\xdf\x96]\x1c\xc0\x9fW\xb5gW\xf5N\xe5\xa8\x08\x1f\xd58N\xd3 S\x83\x8b \x8b@\xfbEC\xfe3\x85\x01l\xedj\x0f\xca\xd6\xaand\x9b\xb4?\x87\x01\x90\x08h\x7f\xcc\xa7\xc5\xff\x98\xc0\xa0\x8ep\x11\xb4@F\xfb\x99\xc4\xc5\xf5\x1a\xde\xe2\xd2\xf7\x05J\xbc\xcb\xb3\x05\xc9\xd9J~\xd9\x86\xd08\xa3\xd3\xe4l\x99\xc7\xa7)\xb1\x80\x85.\xe7D=\xdfm??#\xec\x00\xf2:\xc4\xc2j\x8e|\x0e\xb46\x87\xe6\xe8\x15\x86 Z\x93\xfe\xc9 )^\xab\xbd\xd1\xc25\xfdR+\xc1\xe7\x1a/SV\x1f\x03\x1c\xf8}\xed\xb1\xd6\xb4? X\x04\xbd\xb8\xc7\x81\x1c\x01\xabO/k.Q\xb3;\xd9\x8c\\\x99E\x9e\xb1\x8c\xef\xca\xfe,.\xde^P\xb5F\x02\x9b\xf0\xfbz\xfb\x0b\x18@\xef\xf6$)X/\x02\x1a\xd0>'\x12w\xef\xde\x13\xaf]\x05\xc3\x06~P\xbd\xff\xde\xb2 P\xb0<\x19\xb3^59\x9d\xdc\xd0\xe0\x1b\xd5T\xd4D\xb5ZS\xf5\x8f\xbe\xbdw'\x0c\xbc\xbe3\x0f\x81\xe9+-\xb6\x08S+\xd9\x05PN#\xb6\x02\x02 -XL\xc7\x9c\xbe\xb10\x046\xcb\xb3\x0b\xa0\xe4\x02>\xac\x16\xe4(\xcf\xb3<\xe8=\x8d)\xcd\x18p\xe0B\x0c\xe34.\n\x88\x0b\x88\xcb\x1ezacG\xde\xcct\xaaG\x1c\xc1\xf3\x08)\x15\x0d\xf6\xef\xef\x87\xf5M\x94\xc0\x00\x82\x1c\x06\x90\x85|\x07\xe4\xf5\x1d\x90\xc3\x81\x01y%\x9cZ\x1bO\x1f\x8f\x01\x96M8\x96t\x98\x18\xc1\x8c\xafd9\x04|\x06|\x13\xef>\x00\n\x0f\x81\xf5SB\xcf\xd8\xec\x01\xd0\xedm\xd3G\xa0f\x8d\xc4\x99\x8e\x1e\x18\xdf\xc8\xfb\x15m\x81A\xfd\xe7z\xcd\x89\x11\xe4}\x9d@I4\xe9\x9d\xc7\xe9\x92\xf4 \xa1\x90s\x88\x05y\xff\"OX\xf9F\x18A\xb0\x1bA\xa2 \x10\xf2\xc9\xe5\xfdOd\xc5igk(\x0djo\xda\xb9%\x009.\x18\x08\xb0\xf6*E*\x16h\xdb\\\x1c\x04\xb9\xbc\xcf\xbf\xd6)H\xbd\xcf+\xbf\x1d\xa5\xef\xc4\xfaHJ\xc4\xa0\xc17\xf7\xef70\xadB,N\xca\xff\x9dX\x7f\xf7\xde\x7f\x0e\xe9\xad\x04\x84\xe8\x14\xe3=\x99\x92\x9c\xd0\xb1\"\x1b\x9c\xd7\x81Y\\\xd0\xbf18%\x84BB\x13\x96\xc4iR\x90 \xec@\xb1\\\x90<\x08kop\x12C&\xbd\xd0x\x86l1\x8e\xd3%c\xb65\x18@p\x9e%\x13\xd8\x85\x01\xe7\xd2\xe0\x10zK*N\xedI\x0f\x0e\x9a(\xcc\xe9\x1bg$+\xaep\xab\xe4\xed\xf8\xc7\x04\x0e\xf4s\xe9\xaf[R\x18@\x1cp\xec\xfa6l\xaci&\x1f\xdd\xb9\xfb]\xf3Q\"\x1f\xdd\xbd\x17\x86&>0n\xb3\x05\xea|6p\x05\xc4\x8d\x1e\xc4\xb6\xb9\xae\x87'\x16\x90\xdf\xba\x05t\x99\xa6\xb8\x92\xccr\xf6\x1cs,\xe1\x8ceN\x8a\x82\xcfs\xbe,\x18\x90\x84\xcdH\x0e\xa7D4\x90\xe5\xdaa\x14\x01?\xacz\xb0\xbd1v4\xd0\x8eT\x04\x88o5d@\xab\xd7\xf9\xe8k$\xca\xc8\x19\x16,_\x8eY\x96\x9b\xa0\x0d\x88\x0f\xe9\x92\x1c\x00i3\x85\xd0d\x1c\x0d\x8c%\xbf\x14\xdd6\xb3\x96\xd0fPw[/5\xc87'\xae\xf2PPk|\x88\xd3\xcfk\xc7\x01\x13\x92\xce\xc9 \xc2\xe0\xe4\x84\x1fT\x1b\xf2\x01\xb8\x1b*\xa0\xe7\xae\x83\xd6\xbc\xd5T+|\x85\x1e\xe7y\xbc\xd2x\xc3\"M\xc6D\xdb*\xa0o\x17f=\xae\xc5\xdc\xeb\x8b/\xf9\xceqNbV;\x99\xc20\xd2\xf1\xa4\xaf-9\xe7\xc7\x1b\xdb\xc8<\x14\x03C\x0f\xd5\xee\xc5}-6\xec\x8b\x80\x84^-\xe6\xce\x16\x97U\x8b\xbf\xfa\xb6\x989[,\xaa\x16_\xfa\xb6\x98t\xcf\xfa\xd6-\xd8J\xab\xa6\x7f\xf0m\xda@\n\xb5\xa6\xb7\x82-\xc1\x1c\x91\xe1t\xe4\xd7\xe0\xd2\xb7\xc1\x85g\x83\x85o\x83\x13\xcf\x06\xd3\xee\x15_\xaf\xb1[\xaf\xe6\xc6\xbe\xe3\x9b\xb5\xc6\xa7\xffbA.X7\x16d\xea\x8fD\xfcA\xfbI\xf1\x9c\x95\x9ck,\xee\xbc$+\xc2\xc5\xf5\xa5|\x81N\xc8%\xde(\xc4\x8d\xc7E\x91\x8d\x93\x98%\xe7\xfc\xa3T\xdc|\x9bOH\x8eo\x8d\xf9\x0d\xd5\x06\xef\xba_\xb5\xc0\x07\xd0?&\xfc\xbcJ\xda\xf4c\xca\x05\xc4\xbf\xff\xfd\xe4\xe4\xf9\xeb\xd7\x1f?<~\xf2\xea\xe8\xe4\xf9\x87\xa3\xf7\xf8\xc7\xc9\xdf\xff\xdekS\xd6E\xfb\x8b\x97G\xbf\x1e=\xb3\xbc>1t\xf0\xe6\xd9\xd1/\xd6\x0ff\xed\x0f\xde\xbe\x7fv\xf4\xde\xfa\xc19\x0c\xe0^\xfb\xf6\x1c\x06\xb0\x07\x0f\x1f\xc2\xb9A\xf1\x00\x03\x98\xc3\x0e\x18\x8e\x96\x15*\x9c\xda\xf7O\x8dZ\"\xa8\x8e\xb2\xad\xbd\xd6SC3'\xd7i\xc6F\xcb/\x9c\xd8J\xfa\xd8$g\xc4\xf6\"O\x92|dn\x91\xc8\xa3\xa1lp\xd7o;]\xf2\xd3\xcc\xf6\xf0\xd8q\x12q\xbee\xbd\x86\xdd\xb6\xf4W\x13*_\xc7l\xd6\x9f\xc7\x97\xfc\x90&R\xb2\x84\x1dT\xb4\xf0c\x88\xb3Tx8\x06\xa8O\x13Rh\x06\x0f\x81>\x80\x8c\x8b\x9f\xf90\x1b\xf1\xe3j\x98\xc160\x83\xac)A\x99{\xcd\xf6\xa9s94\x9e\x8c\xf4\x8b\xe4\x0f\x05S\xfcs\x80\x0cE\xc2\xe9\x02#\xc1cq\xba\xf2'^\x1d\x7f\xb2B\x12\x99P\xba\x9c\x9f\x92\xbc\xc6\x82\xba$o\x8a\xd0\x7f\xf4\xe8\x91 \xfc\xa0\x1a\xe5|&\x15\x1c,_\xa9\xbb\xfb\xdf\xdd\xfd\xee\xfe7\xfb\xdf\xdd\xc3\x19\xd2R\x05\xfb&~cn\x85/2m\xe3\xba\x0d|\x0c\x1e\xc2.\x1c\n o\x03\xab\xc9,\xe0\x00\xcec\x97\n\xaf\xc1\x14\xda\xdaxkb\xe2\x1aM\x05rm94\xe4Zs\xe8\x08\xa1\x1e\x1e\x0e`\x87\xe2\xc9^g\xce\x0d/3x\xc4\x01\xe85\xb0w\xd6\x95\x97\xa3z-G\xee\xb9a?\xf8\xb6\xc7\xfc\xda{\xed\x018}c\xc0!P\xce]\xcb\xc5\xd6\xf77\x83m \x9c\xf5n\x087\x9cC\x12\xef%\xa8di\x9d\xf4\xfa/\x8e\xdf\xcf9\x1dhS\xe6\xdf\xf9y\xd1\xbe\xfd\x06\x06\xb0\xdf\xbe\xfd\x9e\x9fR\x95tW\x19K\x8eW\xf3\xd3,\xe5\xeb(\xfe\xea\x8bM\x9d\x19\x8c \xcf\xc4I\xa7^0\x1cm\xaf`\x00\xef9\x8e<\xb3\x1d\x01\x1f\xcd4\x87\xcd\x92\xa2O\xc9%\xf3f\xc6?\xab\x95\xb2\xe8\xa8\x94\xc1\xa4Z(\xbe\x05\xf7j\xcb6\xe4\xdf;\xa8(\x1cB^\x9e!\x19\x1c \x91v\x9e\x86\x99Y\xb2\x9bd\xd4v\xe2z\xd2\xea\xef]T\xc19$\x81~\xcequJ\x9a\x96A\xfd\xe1\xe6>\xb7~\xf4ec\x9f\xb8\x19\x83\x866H\xb3\xf4!\xcexu\xf1\x93\xb9\x0be\x91\xe1C\xb5\"\x82\xd4!\x08\xa3\x85\xdf\x8c~tw'\x0e\xd3\xf7Hk\x87\xefG|\xcb\x90\xe1\xb3\x91a\x08\x0d\xb5\xcc@?\x13\xd5\xf0\xbcF\xf4\xb3\x07\x8c\xd5\xc9\xabCXp)^]\xbcpv\x81\x1a\xa0\xe6\x91\xa3\xb6cB\xd0 \xab\x84\xe8>\xcb\x8e\xc9g\xbc\xa5Z7\xb7\x0d\x1aP\x0b\"\xc5'\x93M\x18\x95X\xe4\x02\x181\xae4(M\xa9M\xbfut\xb9 cF&\x82A\x83,\x87DIE\xa27\xc8\xa6b\xcb\x15\x11\x7f\xfa \xa5\x1b\xf1\xe8\x00\xb5\\\xb6n\x8d\xab\xc8\xaf+_d\xfb\xf5\xcb\xe0\xdeg\x19\xcab\n\xe2r\x11\x96\xed\xb5 \xfdi\x9e\xcd\x8f(\xcbW\xe5\xcb\xc4w\x94/\xbfl\x94\x86\x81\x11} |\x9cR\x8aT\xb7\x96\xdec\xfb\xc19\xb6\xe0\xcb\x07\xa7F\x13\"4\x19\xdeo\x8cL\xff\xf5QSU\xb1\xec\x98\xe5 =s)\xdd\xb4\xc1\xf6\x86\xcf\xe5\x01=\xea\xd5{\x88\xe0c\xff\xe5\xd1\xaf\xc70\x80\xe7\xfc\xef\x9f\x1e\xbf\xfax\xc4\x7f\xfd\xce\x7f\x1d\xbd\xf9\xf0\xfe9\xfe|\x13\xd5\xfaOh\xc1Q\x1f\x06\xcdQe\xcb|Le\xf2\xd9\xb3M\xd3\xd8^\\\x7fQ\x11|''%\x00{|$\x7f\xf6\"\xe8]\xf5\x9cc\x1e\xc7\xe3\x19yO\x8a\x0e\xeb\xa8\xd6\xd5\x96\xe8\x0b?\xc4sOt-e\xbd\x8f\x14\x1fL\xf0\xfc\xd2\xdf\x1c\x88\x17+\xac\xef\xb3L\xc8\xb2a$\x1eI\xc1Q\xfbH\x9e-\xf2\x05\xd74\xca\xfe\xbb\xac\x18\xdaDR\"\xbdx\x04\xa3\xd8\xd2\x01\x98{\xc8\xf2\x0d\xba\x18wv\xc1\x82_#x\x11F\xf0km\xf1\x15\xbd\xf5\\\x133\xa6\xbf\x14-\xbf\xf4\xc7\xf4\x97\x0eL\x7fY\x1b`EI=\x9b6\x0d\xf1\xe5\x0d#\xfc\x90#\xfc\xa8\x8d\xf0/o\x18S\xf6\xbcz\xf8\"Liw\xc1\x82\x1f\xc4z\xfe\xe0\xbf\x9e?8\xd6\xf3\x87\x06\xe5b_\xb6\x96/\xfaI!Z\xc8\x08\xff\xa5\xb4\xb7\x1c\xbd\xa5\xba\x96\x8f_S\xe4\xbelko\xbf\x8a\xe0\x9f\x11\xfc\x12\xc1?\xdaJ\xd3\xe3\xa3\x7f\xa0\xc2\xd4&9\x12\xe2\x10\x1dOb\xe4\xca\xd0\xa3L'6\x1b\xb1\xaf\xcc\xd2\x83\xe2/\xa5q\xe9\x13Y\x15F\x1eR\x8cDr\x83\xd5PN\xf8\x07\xc2\xc7\xadF\x077\x19\x1auN>\xa9\xf4\xf3\x96\xf9\xa3\x80\xe1\xaf\xa0\xcb\xbb\xbb\x93\x86\xb3\xa8q\xef\xa9<\x0c\x86#\xaf\x8e2KG\xea,\xaa\x0c\x18\xff\xf04\xb0 7fm\xf0+\xdeZ\xf0\x95\xd4\xb5\x12\x12\x0cG\xa1_\xbbq\x07r\x08\xa3fR\x883\x0fy@\xd9\x05 \xdb\\\xf3\x93\xea\x8d\xdc\xfc\xc6\x1f\xd5\x1b\xd4\xfc\x86Q\xca9\xac\x84\x9cR\xf5d\x16*\xbfL\xd2\x19~\x8a\xe0|\x04\xfc\xb8O6\x92x6\x92Y\x97\x1d@/\xcc\xc2\xdc\x97OO\x08r74\x8b\xc2\x8d\xe4?7\xb0\xc5\x80\x1e\x06|(W\xd7k\x08)\xf1T\x97\x11\xc9\x9a\x99\x81\x9a\xd9D\xf0\xd2\xca\x91\xf0\x03\xa2\xb2l\xecE\x10\x0b3F\x0c\x0f\x07\x90<\x80\xd8\xeeF\x07r\x1cK\xde\xc6\x90r\xd1\nv \xe6\xb2\x95\xc5\xad\x0e\xd4b\x0b\xbd\x1e\x0b\x96\xc3\xbdQ\x84\x8a\xbb\xe5pw\xc4\xbf\x8c\x80\x84\xa5\xa6$\x86mh+\xe1\xa0%~\xa9K}\xd6zhU\xfb\x936\xab\x8c\x9et~Df\xfc\x17/\x93q\x85\xac\x90\x15+\xe7\x02\x0c\xc7\xc6\x8f\x81\x93\xa5P\x97r\xfe\xf0_X\x05\xfc\xedmx\x04 \x1c:\x1a\x07?u\xa7\xba\xacjOu]\xc1\x01|F\x07F.\xcaKL\x12\xe8L\x86{\x8d\x93\xa8\xfc\xa8}\xdb\x03M\xb2\xfc\x1ax2\xb5;\xb1*\xca\xa4y\x94\x0b_L\x8eR\x11XQ\x83\xe3M\xfd\x0c\xa3\xd5\xbe\x91\xba\xcf\x0c\x9bx\x19\xd0\xb0?\x8f\x17\xd5\xba\xbb\xda\x05m\xd2\x08Q\x0c\x1d\xa06\x10:Ts\x13b\x1d\xd2\xaf\xff\x81!\xa9-\xd0^t\xb4\xeaD\xd0\xeb\x99|\xcd\xf8\xd5\xeb5=\xf7\xf0;N\xd3\x17\xde*\xab\x85\xfbT1\xf0#/9\x1b\xc1\xa1\xb4 \\:\x7f\x95\x14\"\nfB\xc4\xf3_\xeb\xcf_\xc7\x0b\xa1\xbb\xf2\x1a\xce\xc4=\x1ce=\xae\xf9]\x0d\x14O\xdd\xd4\xaa\xe9\xaf\xf9Acf\xdf\x11\x1cwHe\xbe$\xb0%\xf5\xef\x0c-\xcc%Fm\xd9\x18%\xc1\x82j/\xeem\xa0\xa6\x97N\x08o\xa7V#\x06So\xb8\xb6f \xb8y\xf9f\x10\x868\xa1\x00=\x0f\xf4\xbb\x9bN\x10\xec\x93\xf4\xa7f[f\xc7Q\xd2'\x9f\x97qZ\xa0J\xde\xf4\x02\xd3^\xd8Ro\x07\xcc\x93#?\xf7Z\xf2\xee\xe5\x8d\x03\x11M\xa4\xd9\xb5+\x87\x07\xed&+o\xca\xc7\xda\xcd\xe6\xe7''\xb3\xb8\x98\xb5\x1a\xa8n\x97\xaf\xd4\x1e\xac\xd7B\x7f\xcco.\xe5\xb0\nu\xa3\x907\xc6\xea\xc6\x18=\xa5;\x90\xb2\xe9\xc1!\x0d\xd1\xf8\xdb \x1b\xe5Z\x81\x9e}\xe6\xb6\xf9H\\\xac\x06J\x88})#\x04\x1d\xe6\x8f>9'\xf9*\xe8T\xa8\xa8K\xb1B9\xda\x00\x83P\xec\x82Nv\"\xe3@\x98\x91 CNQ8/\x06\x94\xc3\x15o\xeeb\\\xa1\xed(\x00\xf4\xdf\x97\xfdq.\xc2c\x8f\xa8q\xda\x16\xa8\xe5gc\xee\xbc\xf1\xaaZ@\x0b\xcd\xd1\xd5\xbe\x88m\xda\x0d\xdbB\x90\xb4 \x0exg\x0d\x0f\xf9\xe6\xa5xK\xc7\x12\x10\xa9\x05\x81\x01$f\x08\x1b\xa17\x15\xc10\xc6/\x16 \xb6\x8frE*\xd1\xc7\x14<\xa8_\x1c\x9e\x9c\x13\xdd\xc2\xd8\xb4\x00\x9d\xa43\xfe{\x86<\x01\xe9\x9f\x11\xf4\x8a\\\x85\xfc \xbf\xab\xddB\x1cQ\x185\x95\x1ek\x06\x8a \x885V\xf1q\xaa\x11\x13\xbe\xa8\x0b/\xba7w\xd3\xbd-T4\xea\xf1bsM\x02\xe2\x1c\xbbj\xc0\x8c\x8fB\x9f\xa3\xbc\x1e\x1a\xfa\xa4\x86/\xcb\x1e\xdc\x86\xdd\xd2\x9fE\xfa\xbd\x84\x91zC}\xe8:\xd8\xfeY\x0e\xed\x9ff\xc4\xf9\xa7\xb4\x19tl5\x1b\xb4\xce:\xa0U\x8b\x8c\x11*\x02O_\xa1\x15q9\x0b\x99\x97b\xd5X\n\xad\x0d\xf3j\x9c\x91@\xbaZE\xa0\xe2\xfb\nF\x16\x10\xc3\xfb\x98\x9e\x118]\xc1n/\x8cpo\xe19\xb4\x1b\xd5W \x0d5\xe8[z\x1bv\xc3\x08i\xba\xf6\x02\xc5e\x94K\x18\x9f\x16\xe8z\xc8\xe0\xa1\xe4\xd8\xf8\xdb;T\x99pN\n\x16\xe75\xdd&\xa1\x13M\xb5y\x82C\xc3\xc1\xeaX\xa3\xa3\x07\xfe=&I\x1a\x04\x0cv8\x01\xbe\x0d\x94\x8bV!\x97\xcd7\xc3\x9d_JX\xfeb\xc6\x9d_\xbe\x0cwN\xcd\xbaD\x81/\x9aJ\xe9\xf1i\xc1\xf2x\xcc\x9a\x96 K\xb3'\xc4\xe5fz\xe1|z$\x9f\xea\x0f53\xd6\xf0\x1f#\x15`\x1a\x10\x12\xc1K\x8e\x19z\xdc\xc3\x19\xe9\x0c\x04\x82\x86\x15\x86\x93G\x94\x0f4M\xfb\xf0\x932g\x84\xa3\xb6gc\xa3\xcf\x8dL25\x7fY\xadG\xe9![S-U\x1e\xb2\x03\xc8\x85\x8b\xac\x15W\xa4\x8a\x88\x04t\xc80\xecn\x07=\xba\xb2\x11\n\x7f\xbc\xa3jgf\x1c\x15\xadT;\xf3\x9a\xac\x9fu\xc84Q\xe3\x14Z\x937\xbe\x95\x9956\x9bikJ \xaa7\xbd\\M\xa8/\xf4\xc3CbD\xf9Z\xdf\xb3\xb8p&\x02\x80\xa6\xa5S4\xdd\x08\x93o\xa9\x02\x1a\xbd|\xe9\xc6\x12\x9d\x8a\x9dU\x99\xaa\"\xc9V\xeb;-\x11;-\xe1;-{\x00\x89;\x16:\xe6\xdf\xe3bf\xb0\x03 \x1c@b\xd1\xf35vf<\x8a n\xee\xc6\xc4\xa8\xb4\xb5\n\xa3\x89\x17\xc8\xae\xb3=%\xb8\xac\xfbS\x03\xa1uw\xe6\x9d{8\xb9\x89=\xbc\xd9*(\xc8\xa1\xa65\xfb\xf7\xed\xf9\x98\xef\xf9\xd8o\x8fk\x8b8\x9cU\x87\x1c\x95\x87\x1c5\xee\x8b\xd2[\xc5c\xad\x91\xf7\x0dk\xbb\xb2&4iB\x86\x85{V\xd8\xf2SP7\xcb\x86v\x94\xb1\xe8$\x9e\x04\xd4\"\x83\x96\xbb8{\x00[\x01F\x9cKyT\x08\xa4\x18\x8b\xb7'\xb4\x10A&d\xe2\x08\xf2\xedm\xb9\xab\x1e\xd8\xa5\x91\xbc s#L+}\xf5\x8d\x025\xcb7\x86\xaaE\x9d\xf3D\xd7\x12\x8b\xed\xf2\xbd\xa5Y\xcb\nl\xbe\xd5\x98\xb6\x0e\x1dZ\x0e\\$\xe1\x8c\x8e{@,\x8dX(\xaf\x8d\x10\xe4\x12\xe5\xf3\xff\x02\x94\xaf\x0e\x15\xfd\x14)C\x08D\xca\xa2\xb6\x83\x80~\xa0\x94\xc6\xa8\x07\x1e\xcc[6LF\x11'T\xadC\xc226\xbeK\xa8\xa6%\x12\xbb\xe4A\x17\xdd\xa4.m\x12\x9a\xd8\x86\xc9H\x84C\x96c\x8b\xeb\x03;\xcdI\xfc\xa9\xbd\xa06lk\x1d[\xc6\xe5\xfd\x8f\xed\xbe\xc6\xc2Z \x9ai\xb1\x8d/\xdf\x08\xab\x8a+\x01\x8f\xaac\xb5Ka\xd8\xbdQA\xc1\x0d\x11\xa5\x02\x9eC\xb1(\x82\xf2\xe4\x1e6\xbe\xe6\xb4.+\xf67\x1f\xfa3\xbcsI\x03\xe6\xe4\xfa.v\x0dA\x1b\x0e\xa1\xf7\x9e,H\xcc`8\xea\xc1A\xf5\x0b\xbd \x98\xa6\x16\xda\x86^u\x0f\xbf\xe5wX2'\x05\xb4\x9d\x8e\xe7\xd7g\xcaML\xb8\x18\x82\x81\x01\xaf\xf5\x93\xd0q\xba\x9c\x10o.|Ft\xc5W;*\xab\xd1<\xa6,\xf0\x99Hm\xffpPYQ^\x8b\xd9\x13S\x85\x03\xa5\xad\xab\x8d\xec\x83\xb0\x13\xc3\x8e\x08\xa6k2\n\xcd\x91\xe6\xe4\x9c\xe4\xc5&n\xda\x1dp\x9d\x90\xcb\xb7\xd3\xeb\x83\x15\x0eQc\xb8\xb3\xe7\xec&\x8d\x0b\xf6\xfc\x06\xba\xaa0\xb4\xb3\xcb\xeb\x0bS*UT\xb9\xc4\x98+\xcaJ\xb0\xca\x03\xa36\\\xda<\xd1\xa8S A\xbd\xe6\xb2\xb9\x94\xb3\x11\xab\xba\x19\xb1Vl&<\x04\xaa(N\xc5\x02Q \x89\xd0\x98\xf0F]7\"~xP\xd8\x1a4\xa5\x91\xd2\x13\x0fI]\xf5\x0e\x87m\xcc\xd4\xa6z\xde\xb6\xf7s\xfa\xbe\x92\xf4}u\xc3\xf4\x1dU\xc6\x8a\xbc\x8b\x1f\x1au\x17\xda\xddm\xe8\xf5\xfb\xfd\xea.\xa1\x13\xd8\x86@\x08\x15\xeaE\xb2\xe0\xed\xc1\xe9\xaa\xf69Y\xf0\x86{!\x9e\x07\xed\x93`u\xb3'\x81\x1an\xa5\x8b\x84\xaf\xebCi\x9d\x11\xabk\x9d\x11\x8as\x08\x08\xec\xe8}\x87p[\xeb\xcf\xba?0@zW\x18\xe452!n\xf05B\x9d\xf84\xcd\x0c\xb6\x87\xc6\x90\xbd\xcf\x9d\xc6\xa1Rv\xaa\x1d.\xe8R \x02\xb2\xcb\xa7\x91\xb0\x15\xe0\x19S\xdd\x0d\xe1\xe1\xa0\xf4-]\x91`7\x82\xddP\x1eO+\x89\xdcg\x84\x05\xbaU@\x99\x0c\xf8}f\xb8\x8f k\x9f]\xab\xeb\x1c6\xe7eTemy,\xf6-\xf8\xbf:\x92\x0c\x06|.vi@d\x17p\xaf3\x94\xf6D\xb5\xd0\xb5\xf3 4\x13mp\x89\x03\xed\xc3j\xf5\x85\xe7#\x0eGB\xd4@sV7s\x16V\xd8\x8dz\xc3J$\xe0\x90\x93\xf2`k\x03S\xf8\x1a\xf3\xe0iw\xeb*G\xeaT9\xd6%\xc4\x08\x12\xa3\x06\xd1\xbcl\x19l\x8b\x11\xed\xf0\x01\xe4\xfe\x0b\xd4\x92\xd7\x8c\x00\xdc\xfc\x00\xae\x80g\x1co\x03\xa0\x969\xf9\x02\xd9\x0c\xce\x9b8\xec\x95 \x9d9\xd5!\x0d\xe8\xf3E\x7f\x84\x16\xc9\xbf\x98\x03P\xca\x17\x94\xd7c\x1f\x91kuC\x0c\xc1\x8a4\x16F\xf8}\xc8\x1fe\xb8\x1d\x9aU\xc5\x13\xfegy_\x92,\xf9 \x9eq\xe7ed\x91\x81\x8f8%*\x9d\xd3 \x89\xe0\x94\xe0\x9f\x17\xd5\x9fG\xea\xcfSRF\xf4\x887\xb5@\x1e\xf1\xbe\x0c\xf29jH0|\xa1/\x89-\xbb\x04\x9el\xc9|\x89 &v\xf6\xab\xd3\x8e\xdf\x0b\xaa$,\x11\xec\x87*\x7f\x06\xbe~\xe0\xbfk\xee\xdf\xbbw\xe7\x1e\xdc\xe2\xe7\xd9\x9a\x13s\xfb\xc6)\xdfd\xe2M;\x92\xe3^\xd9F\xb7\xbbG\x8f\x1e\xc1\xde\xfdP\xde\xe1O\x02V\xde|\xf8\x10\xf6\xee\x8b\xdc3!\xac\x9b\xce\xf8\xb6P\xa6\xe3._Il\x1en\xc1\xde\xee7w\xbe\xb9\xbb\xf7\xed\xfe]X\xc3\x9d\xfd\xfd\xbd\xfd\xfd{w\xbf\xe1O\xfc\x9c2\x9fZ:\xd2)&\xac\xd7\x8e\xe0\xeb\x92\x86Z4\xd5\xdd>\x8f\xaa\xa3\xb6\x07\xa3\xbb\xe3\xae\x9e\xb7\x9a#4Px\xc5\x18\xa8qY\xe6P\xa5=\x18\xd8}\xce\x12\xf4)\xdc\x92C\x15\x0e;\xc2\xa7\xc21P\xd0\xf0t\x17\xd66\xe7(q\xec\x8d\xe0\xbd\x80\xf5\x1b\x993\x83`:\x1cxF0\xf1\x19>\xe7T\x1c\x1b\xe7K}\x9d,\x0bp :\xdb\x08\xc7gq1{\x9aM\x88\x06\x19u\xcb\xa4\\\xc4\x96\xaa\x90-\x1d\xa4\x9e \xb43\x9e\x1f\x9a\xbe\xaa\x08\xbfw\xc2c\x8d\x84a\x97\x1a3\xa9\x9c\x0b\xcb\xaf\xc9\xf09\x19y}\xb9\xf5\xd6:n\xb05\xceOS\xb4q?/\x8e\xaaT\xd8\xe8\x0egz\xe25\x16[g\xdd\xe0\xd5\xbf\x96\xa3\xa0\xd9\x84|X-\xf8\x96\xdb\x0d\xa1\xb8H\xd8x\x06Au\xbf\xab)~\x8d\xe3\x82\xc0\xdeA\xe7{\xa0\xd1\xfe\xfe\x92&\x9f\x97\xe4\xf93\xfb\x1c\xd5\x85\xcd\x7f\xb7a\xf3\x93l\x8c\x01\xc3G)\xe1\xff\x88\xc96n\x96cp6mVj\x83\xdcR\xdaj\x19\xdf3\x7f\xcd\x97k{\xfb5\x89\xf4\xa3\xef\x16\xbc\x16{\xff5\xee}G\x88\xc8\x07\x12r\xac/\xa4,z=G\xd7\x06\n=V6\xd5\x01\xfe@\x97\xe7\xa6\xc7`\xefMFw\xc8%#\xb4H\xaa@\xc2\x02\xe2\x9c`\x92\xe38M\xb3\x0b2\x81\xb8\x80OdU\xf4\x9b\x89\xb3\x9b\xdd\xf3\x0de-n\xf1\xdc\x98\xc3X\xbf|\xd2\x11\xab\xab\xbb*\x86~iI\x8c;\xde\x94|\xbay\xf1\x01\xcc~\xb1\xea\xc2\x15j\xac\xc3\xa6$C\xb2\xc9Z$\x89\xc6\xc1\x9b>\x08\xad\x0d\xb9\xd5m\xfa\xa5\xcb\xda\xfe=\xf7\xe3\xc5\"]I6\xde\x12\xd1\xaf_W\x91\x83L\xf23\xb0\x03\xb2\xddD\xb0\xe6\x94^\x91\xbc\x16\xde\x7f\xa4\x08!\x96AA\x18\xc4@\xf9>\xa8 \xa7\xc6\x08\x19\x95{\xc2\x89\xfa\xfc*\xe7`\x9f\xfd\x06\xf4\xc4y\xeaot\xda+\xe5kI\xd68\xc3\xa0e\xb41\xe6\x03h@\xeb'4]\xf1&\x85\xd6\x14\xd5\xa4c\xe1\xd4{J\x80s\x0fd\xd2\xf7\xf4\"\xfdd\xe1\xedKu\x0c\x13\x8c\x92f\xa1 \xf5b\x16\xfc\x85;{\xf0\xb5HU\xd8\x1f\xcf\xe2\x9c3/\x8fY@Q\x98\xb1\x8aG\xc7\xa4\xed#\xad\xff\xe2\xbd?&U\xc6\x84\xa48*ic\x9bj\xbc\xf5\xdaa,_9\xf0V\xa9;\x8d4\xf3\xcf\xab\x08z\x7f\xefE\x82]\xb4\xea\x04\xc6\xb18\xe2]{\\\xf6cs\xf57\xa0Y\xd8\x16\x97\xdf\x91\x08>XE\xe6\x9fI\xfc\xe9u\xdc\xd02\n\x06/xGd\xe6\x02\xf9\x92\xa1qqF\xb6\xa1\xfc\x1c;<9I\xe6\xf3%\x92p\x8em''\x8d\x14\xed\x1d)\"\x03lE\xfc\x0e\x9e\x93&\xd2\xf3\xfe\x7f\xe7o\xec\xdd7$\xa6\xe4\x0f\xf6\xef\x192\x1f\xbf\xb7\x0cY\xb2\xf86)\xfa\x95e\x03\x9c\x91@\xc4f\xa1tV\xb9\xcd/H>\xcd\xf2\xb9P\x7f\xc7\xa2\x8d\x8b\x84\xcd \xa6\x90\xd0iB\x13F\xa0H\xfe \xbe;\xf0\xa3[\x8cw&\x0d\xfbE$\x0d\xfb\x8cMp\xfeb\x1c\x94\xf9\xd3\xf9\xb3>\x1f\xd9\xeb%\x8byO\x85\x16\xd6\xd2\xa5\xab\xce\xad\xe9\xed^\x91\x80*-?\xedO\xb3\xfc(\x1e\xcfj\xf1V\xc6@\x06u)R\x8a\xdc\x15m\xa9\x9b\xd4e\x8a\x82\xf6\x03\xe7g\xef\\ \x7f\x90\x8el\xe6\x1fI\x04'|\x9e\x1f\x89G2\x9d\xd2| B\x8a\xcb\x038r\xa9\x88\\\x8bd%!\x1d\x15\x86`{\x00\xfb]\xa2\x14\xda\x85\xe1Q\x95@\xc6p,\xbfN\x8a\"\xa1g\x82 \xc3^?\x91\x95\xc8f\xc1\x86\xd4\x94fR]\x82y\xe6/E\xfcU\xde\x97-\xdc\xbds\x9d\x11\xfc\xd76_\n\x85\xa7\x96\x01\xeau\xbc\xb0\xa6<\xfb\xf8\x85\x96\xc5\x93<\xcb*\x959\xff\x81\xa2s\x19K#\xf26\x85&\x93b\xad\xebb\xa3\xae\xff\xa1'\x85r\xcf\xa9 \xec9\xdd\xa0i\x9c\xc8r1\x89\x19y\x8e/\xaf\x0c\xd5\x0cm\xdfn\xba\xb29\x99g\xe7\xa4S\xd26\xccz\xe5nxBR\xc2'\xe0\xdbtk\xd6\xbeS^m:e\xd1IsA\xdc\x89\xa3\x85\x08Y\x92\x17\xa5G;\x94\xae \xa12\xce\x94\x13\x18\x92\x91l\xd4c,m\xf4\xb0\x8c\x06\x83]\xd1)R\xc6b\n\x14w\xf8\xc8\x96$\xda'\x91\xc4\xb9\x8c\x03\x15\xa6\x8d\x95]'\x1aw\xfa\xe2qr\x17K?<;Q<\x97)c\x12YM\xcbb\xd6RW\x01\x03\xc8\x82\xa5\x83\x06\xca\xe5*p\x02K\xe9\xac\xdb\x8e!\x03\xab\xd4qF\x82\x04cH\xd0p\xc3\xf7n\x04\xbd\x84\x9e\xc7i2\xe1\x94\xf8]\xccf69\x88\xcf&\x85\x01\xc4.\x0fT\xfe\xd2XNy\xc5\xa7\x8c\xd4*\xe5\xfb\xc9\xfe\x01?\x07I0\xae\x16\xd0\xa9(\x9d\xe2\xec\xc7r\xf6\xe2\xd7\x8a\xff\x92\xbb=H9\xbe\x06I\xc5\xcb\xb0\x10\xcf\x8e4\x82\xa9\x81\x07\x90{\x9eR\xd4\xe9Z\"\x1ee\xdfy\xd9\x9b\xe4\x9aZu\xd0\x1a;`\x9c\x92\xd8Y\x94Hk\xbc\xed\x16\xc3\x84?\x84Ym\xc0:\xea\x8d\xb3\xee\xf6k2P\xe7\x04J\x8b,_\xa9\xb8x-t\x11&\x06@\x8e\x86 b\xb1\xfeE\\<\x16\xf44@\x1f\xb6\xfe\xc9 \xa1\xc52'o9\xbd\x0e\xea\xc4[\xb1R\xce\x81\x97\xbd{\xee\xc1\xd6\xf9P?7\xf4\xd1pQ\xec\xd2\x0d\xb6\xb8x\xae41\x9b\xf5\xaf\xf7\xd3\xb12%\xc86\xebA\x9e[\xce\xb67spR\x1a\x11r\x01/\xfde\x9e\x8d\xbc\xd0\xbe\xd4\x89Y;\xdcKo\x1b\x94\x03\xdb\x99E:\x88\x08\xba3\x93\x80a\x82\x19\x86\x19eL6\xf7H\x94}\xea\x80\x80\xb6\xda\x9d{K\xed\x98\x8a\xc11`+?\xd2\xfeI*\xd6Fgk\xa2*\xaf\x03\xb24\xc8\xe15\x1a\xd2r?\xe8\x0c\xce\x9edp\x0c\xd3I\n.\xb9\x0f\xe0\xb3\xc1s\xe8{\x12\x01\xb2W\x8dd\xc0\xaf\x1f\xbf\xb3TO{\xc2\xdf\xd6\x81dS\x0f\xfedO\xfc\x81\xc3oOH&*j\x19\x1f\xac5>\x9c @,\x9d\x9c&l\x8e\xe0PN\xb14\x13.\xc8\xd4\xab\xcf\x9f\xaf\xd3\xe78[Rv\xed._\\\xa7\xcbOd\xf5\xa3`\x8aY\x0b\xba~\xdd\xfezs\xdd\xae\xbc;}\xd9\xdd\xe9 \x13\xa5FK\xa7\xe6*\xc2\x86V\xbe\xcd\xf1\xf8\x93H\xd3\xa9(\xcaW$\x90\xbf\xfc\xb4\xa1?t\xa6x\x14\x15\x90D\xc6\xaaVRJ[\xb3_u6k\xa6m\x1ce\xac\xe5o\xd1\xab\xf8\xc0\xe6\x8eyr\xb2\xc8\xc9\xb9\xc9\x14\xec\x97\x85\xe5\x9f\xbeIQ\xeb\xc5_\x9f8\xf2\xf6fJ\xaa#\x11d\xa5H\xc7\xf0\x87F\xe9\xa8\xb8!\xa5\xbb\\\xfc\xaa\x13\xbd\xcck\n\xbf8\x93R\x7f\x8fz\xed\xe0{>\xa0\x7f\x92`\xd73\xff\xdd?\x9c\xb8z.k\x92\x9b\x8d\x9c\n\x15-\xab\xadt8\x17\xc1\xa9\xc5\x9d\x12d~\xd8\x8b\xe0\xc4\xa1\xbc\xc1\x04pL\xf5\x86\x91/\n\xbc\x11h\xcaU\xb1\xb8I\x04q\x18\xc1\x96T}T~U\xe6\x0eD\x1e\\\x19~\x18$\xb2P\xd7!\xe7\x02\xa4\xf6`g\x0fK~\x1d4\xab\xc9\xf1\xeb\xcae\n\x17zvl\xc6g\x14{U\xf9\xc6\x9fp\x9bW\x93\x1cZ\xa1'\x8a\x8f\x19\x1f\x9b\x82@m\xc8C\xea*\x8b\xb2>c\x16\x95\xd4\x07Q\x97\xb4\xd5\x14\xa4\xa5\xa3@O\xb8\\p\x08\x19\xee6\x93\xbe\xc2\x82\x8f\xd2\xe9\xa6\xd4/\x89\x05\x8d`\xe9\xe4U\xb8D%$\xb6\xc0\xf8\xe9\x01GD\xb9\x9e\x84\xf3#G\xc12\x8c\xe0(\x881\xeb\xc3\x05?'D\x0e\xd7!\xff\xcc7\x9d;cn\x1e\xaa\x95\xa8\xf4W\xe1\xf6\xd9\xba\xff\xc2\xcf\x13\x976\x80c\xea[l\xcc\xf2\x08\x1b\x0c\xf8\x02h\xac\xf3\x8br\xa6\xb2\xbaP\x04\x99\xc9\x96\x83\xbbW$\xde\x0e\xaa$_U\xcb\x07\xda\xdf\x8f\x1e=\xe2\xf4\xe3\x16\x9c\x99\xf7\xf9\xb2\xde\x08\xba\xe9k\x1fY),\x1f\xef\x8f8^\xaci\x1b\xc3Z\xfc\xb1\xc4qI\xbd\xea\xb0\x82\nl\xc3\xb9\x84\xccH\xe8\x15\x07\xf5\xd5\xcdB\xfe\xe5C\xf1\x1d\xe1+\x0d\x070L\" \xbeK\x9e3\x17\xbd\xac\x12k`\xf5\x82Z\x86\x02Z\x9a\xe8:\x12\xdfph\xd1a2\xb2\xd3\xcc\x02M\xb46\xeds\x1c,\xd1-:\xe0\xaf\x15\xf5\x8c\xc6>~ \xd3V4\xa1\xba\xae\xc2\x90\x1f_\x8be1\x0b\x0c\x9eEV\xf2\x12+\xa0e~@\xce\x9c@.w=zmUj\x95[\xb7\x00\xb3\xb0\xd6\xd4+\"'c\x99\xd8Wl\x7f?\xce\x12\xc1S\x82\xc9h\x87\xbc\xa3QX\xe3\xc8\x98\x0fG\xa6.\xe5l\xc0\x86\xb6\x04x\xea\xca\x10\xab%\xf9'5\x115FEKl\xad\xfe\x01F.J]\n\xd9\xcd\xb4\x99wU8\x8d\xf2|\n\x0b\x90\xd1a\x9a\x82W\xc9\x99\xd6\x8e\xb9d\xb7\xe0\xb8\x85\x14\xa9\xe8\xb2\xf9\x1f\"\x7f\x9dJ\xdb\xff\x0e\xec\xc1!L\xfa\x8bLT\x82\x98\x0cSN\x8dZ7\x86|\xe4\x9c\x1f\x9f\x08\x06S\xfc\x0e#\xec9hh\xff&\x95)\\ \xcc\x11L\xbaX\xd2\xab\x08~\xbc693F\x97!vY6+\n\xf5\\\\ \x82z\xfdp\x11\xf9IP\xf6\xb1hF\x12EC\x84\xa6\xd7J\xd8x\xc3\\\xce\xb9%\xb8\xbb24\x1b\x95\xb3\xc3%\x13\x8f03\xf2H\xc4q \x19\x89\x99\xd8\x89&x\xaeM\x17k\x99\xa1U\x02\xe8\xa7$\xc8m\xa0\xd2\x04D&Y\x1e\x8a@b\x0e\xa9\xb2P\xf0]\x9a\x9f\xa7u\x18\x9a_\x1acL\xe5\xd6\x00\x82\x14n\x81 \xb5\x91\xae!\xa1\xce\x1a\xca\x1c3AUtz\xc9D\x93\x08|s\xe7\x0b5B\\.\xf3;|\xef\x8d\xe1\x10\x16\xc3\xe9\x08\xdc!\xeb3\xa1(\x9b\x08\x0b\x8cX\xe8\xfaZ\x99g'\xd4\x04\x13\x8f\x83B\xc0\x01E\x97\x85F\xde\xc7N\xf2\xeep\xf3\xaaU\xfc\x92\x0c\x01\xdf\xcf\xa2\xde\xcc<\x8c\x103v\x1fHV\x9f>\x80%\xa6\xf9\xe1\xb81\x80\xbd\x10\xe2\xe1r\x84hp\x0b5\x0bl\x98lo\x8f\x1c5\xeb@\x13J\x87\xf9H\xa8\xb8\x84/|\x80 \x05\xb7\xb1\xda\x98\x81\x90\xf0\xc7\x8b\x08\xd2\x08\x96\x11\xcc,\x90\x94\xe79\xff\xbf\x08S/\xa1\xc4\xe5?\x16,\x86{\xf0/\x98j\x9c\x8b\xba\xe3h\x0f?\xde357\xab\xda\x99\x99\x11\xf1tSr\x7f\"\xd1m\x86\x14\xfc\x00R\xf8\x17\x92\xfd\x14\xd6`\xc1\xd0\x0b\xed\x93\x82\x05\x8b\x08\xa6\x11\xcc\"8\x0d\x9b\x01\xf8\x1d\xe2\xc7yY\xed\xa3\xf2\x80\xb0\x1f\xb5B\xbdZ\xa6\xbf\xc9\xb5\x08Z!\xc5P\x80O\xb9\xa7\x1eb\x99=Q\xf3\xacslz\x97\x88\xf6\xf5\x0e\xdd*\x8d\xa4\xfa\xcc1\x06\xb7\xa2#\xe9\x92\x16\xf0%\xb5L5\x00\xa8\xbbn\x19\xa2\x81_0\x80\xafH\x90X\xed\xe7\xe0\x14\x17\xc6\x19e \xdd\xa8\xf8C\xbb\x7f\xedW_\xf8\xccv\xecj\xa8\xb6\xa7mct\xe6J\xb5\xe6Im\x10\x90:0\xf9*\xa7|\x06s\xb8\x0dw\xdb-\x8f\xd5\xb3\xfd\xf6\xb3i\xf9\x9d\xcds\x7fa\xf1\x188\x97\xb1CG\xc6\x80a\xe4\x9b\xbb\xf3XZ\xe4\xea \xe6\xc9+\xa9\x9d\x99/\xa4\x18:\xec\xaa\xe7D\xdd5\x1e\xc4`r\xa9\x03\n^\x89\xe3:\x87G\"kt\x0e\x0fa\x0e\x87p\x81\x99\x07\xf2\x08U\x0c\x18g\x8a\x85 X@\xfb,\x13\xf2w\x88ei\xd9\xc6n1\xe8'r\x9c\xfc!z6\xa4\x01\xe9\xd2\xf4\x96\x9a\xda\x0e\x7f\x13\x93\x17\x89\x9f\xa7\xc5\xc4\xed0\xa2\xe5\x01\x99\xb1\x8e< \x0b\x16\xc1\x05\xe1l2\xf3\xc8\x03\xa2 \x1f\x81=\xc6r\xc1\xb4#\xeeKsZ\xbcJ\n\x06\xc3^\x04\xbdQ;\xa9E\xad'\xcf\xa4\x16\x89\xaa\x15_%\xc5\x0f\xcb\xac\xe4\xa4\x9e\x95\xdcq\x9ar\x01\xb6d-1I3\x8e<\xcb\x93\xb3\xc4\xe6\xd9\xa6d.\xde\x13\xed\x8b2\xa1\x04n\xc1\x99!\x14\xd2\n '\x0c6\xcb\xae\xe1k\xbf@\x901\x04\x99d\xabjU\xf3\x1dE\xa00\xb1\x7f\xe5\xc4\xc6\xe0\xa1\x96\x0dvs\x975\xc0c\xe1!\xec\xc2!|\x92\x19\x0cq\x9b\xed\xca\x08SqsW\xa8\x1f\xf7\xc43f\x8c.\x03\xb0'\xd8c\xe8\xfb\xa4\x16\xd3\xfcNe\xcf9aq\x92\xba\x19*\xe5\xdeo})q\x06\n \x14\xdfb\x94\xc08^\xc4\xe3\x84\xad\x84A|\x00\x97Xo\xbb\x195 \xe4A\x14\xb12\xf1R\xd6x\x89\xf4ORrN\xd2\xea]\xfb\"n%~\xe1\x06\x89\x08\x9b\xa8BL\xcbuV^\xf6b\x14\x1c^\x9b\xb8\xdc;7\xd3\x05\x82E\xac\x14~\xad \xa4\xcf13z\x17^\xb9\xe2,k\xdbj\xb3\xf4-H \xcaJ\x1c\x9aU\x03 \xcb,\x992T\\h2\xaf\xcah\xaf^R\xba\x0d\xf1p\x91&c\xe4\xdb\xf6lQ\xbb\xb5\xc1&\xb4 \xf9&d\xa0\xd1\xcbn'8\xfe\x0d\xc9$tjZ\xfeTK\xab'\x9b\xc0\x15\xe6\xf8\xd3\xc8>!%%\x81j\xd7NE\xc1\x19)'(\x16\xcbb\xd6\x05 %\xbcU\x11\xfa\x96]\xae\xc1\xc9\xca \xe1\x1b\x16\xbai%\xe0\x9f\x90\x11\x91dQ\xd9R-;\xbe\xe6\x16\xbc\x8b2\xbb\x96\x16\x11%w*\xe8*l\xe3\x1e\x1e\xe6^%\xd9\xea`\xcb|\xf3:|R\x87\xecn\x04;{\xeeV\x97\x14wWW\xcb\xad\xf5\xb8\x16\xb0\xad\xa1a\x9f\xf0\xc8\xd9\xf1\x05\xb3#\xfbd\x99HnH7\x07\xb1\x17(\x9a@\xee\x00\xf0&\x89W\x1e\xfb'^i\xf7\xe1\x95\x90\xa3\xd9\x91o\xe2\x95vw\x1b\xe4\x19y\xec\x97g\xc4\xdc\x87\xd7\xb4\xce\xaf\x93\xd7\xe3qg\x9e\x91&\x9fx,\x08\xad\xd7\x89\xa6o\xc2v\x11\x8dz\xcb\xbe\xf5\x97\xce\xbf\xa8\xee_9\"Y\xe2\xaf\xac\xfa\xe7\x1e\xddfI\x19\xca\xedi\x17gOJ\xe4\xb3\xaf\xcd\x06\x05a0\x14\xb1\xabB.\x9e\xa8\xa7\xec\xdfW\x04\x86b\xd1\xd6\x8d)\xd0F\xd9)\x9aur\xa5\xfe\xd8 _\xbc\x02\xa1s@\xa1\x04\xc1\xa2\xd7w\xa6\xd7\xad\xec\xdc\x98\xc8_\x92d\xe2\x82\x05:\x9b\x135\xb8\x9c\x1a\x87\xa3s7\x91\xc6\xdcl\x94\x90\xc2\xb4\\I\x81\x12\xf6\x00&\xac\xad\xc1\x9a\xb1v\xe2\x89W\xcf\x8f?X2O\x9c\xa3\x05]\x83\x9cM\x7f5gV<\xc0\xb1\xa3h\xac%-\xa8f\xd2\x8cn\xd3\x7f\x9d\xb3\xe1\x8c\xa9`\x90sV\x05\x83\x9c\xb32\x18\xe4\x9c\x95\x89\"\x9f\xc8\x9c\x91\xda\xbbx\xbf|[\xbd\xa5~\xe1\x8b\xa5\xfd\xed\x89\xb2\xc5i\xb7\xd5\x17\xea\x17>\xaaR{=)\xf3|U\x0f\xcadOOj\xd9\x9f\xf0\x85f\xe2\xa0'\x0d\x89\x19_\xd2\x93\xf4<\xd1r\xf6\xc8\x87z\x0e\x9d'\xb5\xa4:\xa2\x0b=\x03\xce\x13=#N\x04\xf3\xb6\x08\xf4\x84L\xb3\xdcd}\xb4iZh\xe9\xd0\x84\xde\xcc\x0c#\xdb\xca\x8d\x81\xeb\\\x86^hL\x97Y\xbb\x88\xfaC\xe1\x13e\x0e\xad\x15\x0e\x80\x8f\\\xadK=\xe1p\xc4O2s7\x99\xf4\xbb\x10\xaaHs/LT\xbd\xb0S\xf2\x18\xf4Q\x0c]\x06,,R\x1fs\xba\x15\xd7\xc0\x8c\xb0\x85\x1d\xd4q\x86!\x8e\x06\xdfJj\xa0jSe\xe3\x80\x85\x95,\xf3\x80\xf2\x12\x06p\\\xe5\xce2\xcf\x7f+1\xabTj\x8e\x13\xbb\x0f\xa0\x10.\xa6\x05\xfaIJX\x14\xa3R\xfc\xb2\x12\xe4\x0c\xddD\x96%\xf48\x8d\x0f#X6)\x98\x01G\x1fO\x19i\x1d\xef\x9d(\x1a\xd4q\x14\x83\x8c\xbf\x00S\xa5\xf5\x13\x85\xfa\x0e\x84\xcd\xdc\x08k\xee\xc4\x0b\x07\x93:\x0e\xda,J\x88\x839&\xcb\xe4\xd8\xa5\x83\xd1\x80\x82\xf8Rf\x86\x0c\x1a\xbf6DN\xb5Y\x9c('\x9b\x8ceoRY\x91\xa1\x92/\x92~mq9M\xceD\x85\x11\xc4udi\x1fog,\x82\x15\x8b8\xd3\xe0J\xa3~b?\xad*^]\x1d\xe2F\x08KEay\xb2\x1b_\xc2\x04-,\xc8\x1dQ3Ryf\x87O-\x91\x88d\x1cv\xc3\xc6\xc4\xa0\x16\xf7\xcc\xe7\xb6\x8c\xc0jc\xad\xe9q\x96\xb5rV\x16O\x13u)b\x12K\xff\xa5C\x85`\xe2x?PQ\xee\xf8\xd3\xce\xa3\x82\xf4K\x89e\xe5\xc3]\xf4\x8c\xdd\x81\xd8\xfd \xaa\x18\xf9k\x16\xbe\x11_y\x04s\xc4\x1d\xfe\xf2\xdca\x0f\x95@\xe8\xe4\xe1\xd5\x95\xa0\xe3,\x9fvZ\xee\x87SG\xd1\x11\xd0\xd4\x12X\xedq'\x85\x03N5\xdd\x9f\xc8\x96\xd1\xb3k9$\xe6\\)`\xdcvx\x97/a\xd1t\xcb\xcfPs\xdc\xb1\xac\xc2\xa9\xd5\x7f\x01S$/\xf5\x05L\xe0\xd1#\xc8\xdc\xdf\x8d1\x00f\x9b\x1f\xeb\xea\x03\xc72\x8d\xcb\x05\x1d\xdf\xf0\x82\xe2\xb9\xf6\xc0\xea`\xa1_|\xed\x8d\x19]L\x97Z\xf4\xa5M\xe8k^\x89,\xb2\xc7E\x9d.\x85|\xf3ZJUh\xe7\xcbv;\xbe\xba\xf80\xd2\x86/a\x17\x82\x83.\xf5#\x92\x8f\xe1\x00\xd2.$\x079\xf2X\xb8\xa2\x17\x98y?\x13\x87R\xc2Q\x83\xf2S;\x0b\xedn \xe0\x9c\x92co ]l=\xf6K(qaL\xf6c;D\x96\xad\xec\\\xe7\x0e\x8d\xc2\xb2T\x93\xc3\x0e\x17\x92\x96\x9a\xaa\\\xfc\xd4T\xe5\x0co(=9\xc5_U\xd6\xa3e\xa9$\xcf\xf0\x87&5&\xe2\x86\xd4\x97\xc7\xe2W=\xb9\xd7\xd2\x0b\x14G\xcc\xa5Q;c\x18\x06}\xc6\x07$\xec\xfa\\|\xf34\x85_\xb6\xa1l\x03q,\xfc\xf1er\x1ewL\x05\x11N\xf3\x0f\x15qS\x8a\xd9\xd6\x07\xc8\x0b#^j\xbe\x14\x99kc\n\x96\xb3\x83sK\x1b\xc4u\xb8td\xcc\x19\x0b\x13\x9f\xb4\xe5\x89\x8d\xa1`\xe1\xd4$\x8d\xc5 \xa5\xf2F\x05\x92\x0d\x136\xde\xb2c\x18\xc0\xd8\x1c6h[\xd1\xa2>\xf2\xf2\xf8'\x95[\xa6\xdeUT\x83\x9d\x80<\n;-\xde\x12\x0e\xcb\x9b\xcaD\x16\xeb\xe3l\xc7 \xd8\xf0\xe6\xd8\xce\xd3\x95j6\xf4\x07(c\xf0\x88\xe6\x99J\xa4\x07\xea\x9c\x05\"?\x97dK\x91+\xe5\xa3\xe2\xe2\xa5g\x1a\xc3\xa7\xf6\x91\x94\x16\xf4\x86\xedW\xb7\xac\x9a\xf9A\xf1\xe5C!\xd0(V\x10\xb6\xe1\xdc\x86t5sD\xc9DJ\xbe\x15\xbf~ \xfc\x16\xd0\x15\x07\x0b\xab\x0eJ\x1f\x06\x11\xaa\x95\xa3'\x03\xffhg\x00\xe7N\xc4\xeb*\xf3n\xad\xe8\xe5L\xd2\xa3\x05\xbd\xa8\xa83Q\xeeX\x7f\xa2\xe2\x0f,\xe5\x8d5\xb3\xbe\x9en\x07\xf33\xd8\xd9\xf6\x0e\xf6?\xf1a\xff1\xc6\x03\xb6m\xc5\x19\x96\xa5\xcc\x8c\xd8H\x91\x9b>@\xb3\xd1.\xfe\xbd\x8d!c\xbc\x05\x83\xc7\x02\xc7\x87\xb8\xb9\xbf\x92.2\x15s\xdc[j\xd8\x86\x86_\x13\xa7R\x13\xfb+\xd1#\xd5\x91i\xac\x82N\xb7a\xccG\xfd \xc4\xe7r\x1fa\xf5\xac\xb4\xbe\xe3\x0fa\xa8\x8cG\xe9H\xee*.\xd8\x8da[e\x1f(\xf8\x9f\xe7\x86\x11\x8d\x85L\xc8\x1f\x8f#QF}\xcc\x0f\x00\xf1o\x82\xff\xba&2\x15\xd2X\x82\x11\x04\xf8\xe72|\x00\x0b\x0e\x11\xec\xb9\xe0\xbb\xc9k\n\xb5\xa1\x8b\xf1\x9a\xf1n\xd2\xe5N2\xc3 \x8a\x87\x18#!\xc8\xc6RH\xdc\x07|`x[Soat\xe3\xc4\xbc\xb2X0]|s\xeb\x16\xc6\x01\xa3h6i\xa8 :h\xc5\x1c#X\x90\x90\xa7bz\x9c\xdf(\x1e\xc0\n\x1e\xc19\xff\x87S\x82.Y\xe2\x14\x060E\n\xb22+I\xd4\xc5\xbb\x9bK\x92s:\x12\xfdV\xbf\xad \xa4\xcc\xfc\x9d\xfaP\xf4|\x8e\xb4\x0b\x060\xe9\xa0L\xa0\x18|\x05\xb2\x80/\n\xc6\xac\xcfj\x8a\x93\x1c\xd9\x98e\x88g\xdd\xa3\x01,B\x8898\x16\xb8h\xf8o!\xdc\x16*\x07\x85VSR\x0f(\xda2\x85O\x96\xee\xc8\\8\xce8\xa5B\xfcp\xae\x9c\xdc\x87\xa9S\x98\xe1\x0bs\"\x84\xeeG\x8f\xf8\x81\xeeZ\x18>\x80\x13\xa4\xae\x8b\xea\xf5\x10Ns\x12\x7f\xb2\x7fu\"\x05\xb5\xed\x01\x04bK\x85\xf05\x9c\xe0&\xd9)!#\xf7\xd3\xf0\xc4,\xdc\x9a\x177\x15X\xfdH\xaa\x11E;M\x90\x16|ev`\xcc\x97(\x15\xfb\xe1\xa1\xd8\x0f\xb5\x0f\xca\xe5,8%\x90\xef+\xea\xb2#\xa9\xca\x8e1\x8ar\xe3\x94\xa4KTkT\xc7\x89`\xbbI\x8d\x9d_V\xba\x1d\xc08\xce\xca\xbd*\xd5\xdd\xabf\xbe\xeeU\x9cL\\\xb0 \x16\xe2\x0eFj6\xa3\x1b-\xc7\xf1c\xbf|\x91\xb9\x9e/\xb2\x16A_eY[\xba#B0)\xb6\x93 F \xc6\x9a\xbe'\x15\x10~$\xf7l\x82\xeb++\xfd\xc5A!RJ\x8aU\xbf\xe9\x94\x92\xb9\x88GK7@\x8f\x04\x1e)\xa7\xc9[\xb7D\x82\xa8\xca+9A\x92\xa2 \xdf\xccrcY\xa9\xb7])\xe6\x84[\xf5.*\xe5\x94\xce\xfa\x9co\xcas\xaf\xf6\xdf\xb9\xdbw\x16z|.\xdc\xe1>\xb0\xaa\xbe#\xbf\xb5\xb1\xdf\xcd\xf9\xff\xfa\xfa\x8e\x1f\xdcP,Ka\x8e\x9b\x08gk\xf0\xb5oJ\xbe\xba\xea\xe1\x9dfT\xb1+!\xaa\x14\xe1(\x02\xe1\x8f\x03\xb4\xdb\xf7OD\xea \x91;<\x15\xf6e\x8f\xdc\xe1^sz\xeeT&\xac\x842a\xc5{|\xcd\x02Q\xdd\xe6\x88\x05\xadP?K\xeb\xbf\xbb%\x0ci\xda\x89\x14KoM\xbd\x14K>8)\x1c\xfc\xbcHI\xc1,\n\xff\xa2\xe2\xf8\xf9\xd1\xba\xb4\xa9\x12\x06\"o\x93\x19o\x85~\xa2KQ\x18K\xf28\x10\xda\xd3\xea\xe7>|\x0d\x89r\xdcD\x1b\x910V\xb6\x93\x9fZDXu\xc9\xfe\xb5\xf9H\x15\x0bJk\x96}\x14\xf6Y\xf6\x92\xac\xc8\xe4\x98|\x0e\xc2\xcd)3\x19\xeeZ\xb8\x86\xb0?M\x93E\xc0;x\x1d\x8b|:\x1anr\xa2\x9b\xd7p\xb5\x8e\xb9\xba\x933:\\\xa0\xf1L\x95}c\xa10\xfe)%\x86\xe6\xdc\x1bkj\x0bND\x96J45(/\xb5X3\xabm\xa6B\x80\x18Qi\x19\x0e\xf7F]\x8b\x9d\x0b\xd5\x9eXG9\n\x91j\xdd:\x081?\xe9L\x1f+\x12Z\xb5\x10\xcbB)\xb2\x19+\xc9\xb0\xf1=\xb9\xfc\x9e(\xca!|\xc3%\xe5\xc8\xcc\x9c\x0c\x07\xe3kt\x7f\xf7\xcc\xbc\xfc\xa6\xc3\xeb\x04\xdd\x954\xaf\x93\x93eA^\x92U\x01U)\x0bE\xf1\xdaI|m\x9d\xbe\xb7\xd0tc\x8f\x9b7\xff\xec\xafm\xfe\xd5_\xdb\xfc\xc7\x8e8\xb6\x7f0W\x8aXV\x1bA\xbd{~\x83o\xf1.\xafN\xad9CR\xe6\x08\x8b9\xaa\xe2%\x9d\x0d\x9d\x97e\x92\xe5G\xb2\xfe\x19\xfa^9\x15b\xfe\x83\x05}7\xc9n\x02\x0b#\x12\x99*\x8a\xf09\xcd\xe2\xa2\xd3\x0d\x15\xf4\x8e\x12:N\x97\x13R4\xab\xda\x97-\xaa\x176kv\x16\xdb[\x1c\xc7\xe3\x19yO\x8a%\x86Q\x12\x1aaE3\xe9Q\xf8\x91\xe2\xe3Z\xd9.W\x04\x93\x12C\xcc\xce\x14P\xa7P\xadzV\x9e\x8c\xa1\xf4:\x14\xbc\xa1]\x1da-v\xa5y\xa7n:?\xa1\xef\xe5\x07\xc1\x9b.\xa9^i7UW\xa2]\xbb\x98\xaeXx?'Vu)\xbbf\xee,_\xab.\xe4RHg\x1d[uU\xfb\x0c\xdd\\\x87\xbb\x1d\xd9\x90\x00\xc3:\xd5\xbb\xda\x87{\xa3H\xfb\xbb\xe5^\xd8\xbc\xdcfQ+\x19Q\x97-\x8b\xb9\x1f>\xf2\x95\xc2\x15\xfe\x9d\xcbLp\x00\xbf[\x11\xa9v\xd3F{?ws\xba\x9d\x148o\x12\xdd|s\xd2b\xa7\x01y3\xa4\xd3\xa7\xa82\xc6\x81bbz7\xc5\xadj\xa6d\x18&\x8c\xbe\xf6\xa2\xc4Nn\x14\xedp@N\x02\xe43\xbck\x13\xa0\xac\xc3\xd9\xa6N\x83\xf2\xa0\x9a\x91\xfaXZ\x04mD)\xeb\x98\xb2\x99(\xf9\xcc\xb9\x86\xc3o:\xeb*o@i\x94\xf8\x9atR\x19t\xb4\x93\x04F\xc9\xaf\xf6\xb7\xcf\xa5OZ&h\x83\xdbE\x05}\x13\x9c4H\xc9\xef\x1cZ\xcbHC\xb6\x18)\xd0\x92\xe3\x9bq\x01\xc0\xa2NhUE\xb4\xec\xf1\xef\xbb=\xd7\xdc\x1b\x9c\xea,\x16m\xeev\xba s\xe4\xe2\xb2\x88`\x7f\xd02\xe7\xcd \xa9S\xe0\xa3y\x06\xa0sW\x1b\x8c\x13\xf4\xbd(\xa4D\xdb\x961pW\xa8Yj\x90-W:\xc1\xb2'\xd4\x04\xc8\xbc\x8f;{\xb0cHa\x0d\x92{h\xd2X+WP\xa7\xb1\xb5\xc6--_\x8f\x8d\xeb\xe0\x0e\xa9\x81\x97\xa3\xe6\xe8\x90\xff8\x0f\xd7Q\x8c\xe4*\x82-\x1b\xec\xcc\xb1E\xae\x19\x19\xcfx{\x0f^[\xfe\x0f_\x95_\xc7\xc9\x8e\x9b1k\xa2\x9a\x15\x8f\xcf\xcbD\xbd~\xc7o\x86\xc7\xd4\x8a\xf7\xb2\xb5U\x11\xc4\xccq\xfaf\x7f-;P\x8e\xa7\xcd\x0bH[\xbb\xa1\xb4P(t\x98\x0e\xa6\xc0\xe5My\xae\xc5 \xd8\xcf\x98\xa5\xb9*/t#|\xe2p\xeb\x05%5\xe8|\x02~P%R\xdc\xde\x8e \xe3\x0d\xe5\x12\x02hn\xb6\xe7\xf9\xe4Sm\xfa\x84\x81Z<7\x1f\xe1\x03\xa6&\x1f\x918*/v\x03m\x036\xc3\xd3\xf9S\xe1\\\xdc\xc9\x8d\x80\n\xca\xa8s$\x89\xfb\x0be\x08K|\xb8\x12\x906\xb1b\xb8\xeb\xb0\x9a\xa9\x0b\xb3Y\x1a\x13\x83\xeaW\x1d_\xc6h*\xd4r\x02}\xc6\x8a\x882\xb7:\"\xcf\xd8\xcap\x82U\xf01\xf3;~\xb6\x81'\xbe\xc4\x8fX\"N\xf9\x0c7r#\xe2B\xc4\x1e\xdcF\x1f\x1c\x0cDD\x9f\x1c\xf9\xfe[Y\xc1,\xeb\xcc\x9b\xc4\xd1\xe6\x9d\xa8cf\xb7'|@\ni \xc8\xe1\x04\x0c\x12X\xaf!\xe6\x7f\xc5e\x8f\x1c&}\x96 \x15\xbav\x10\x07a\x05)\xf3\xa0\xa4\x93w\x0c;&\xcc,`0\x10\x9e~\x01\xdfl\x85tD\xda\x85\x03c\xa5\x89s\xe9\xd5\xe8>vR\xc5bV\xe1\x06K\xac\xac\xa5\x8c\xa1\xcb\xca\x80\x18\xc1\x16\x9eR\x992\x8b-\xcb4>A\xda<+<\x8ea\x99\xe1\x86\xc9p\xd3*)\x10\x93E\x15\x15\x93\xb6\xcd\xe9$\xa6\x9b1\xf8\xb1\x85\x11\xa4_\xa6\xa7\xca\x9c\xe09\x96!\xda\xa4\xc2\xbcf!F\x11\xb4\xdd\xe5\xaf\xf45\xbe\x9e\xb2N\xda\xf4x\xff^K\xe4\xd6\xd3)\xb4\xd1Zm\xab\xf8\xec\xeb\xe3\xb1\xbc7|\x96\xaa\xb5z\x10B\xd6yZrxmo\x17\xf0HC\xf9\xae\x93\xd8+\xfa\x1d\xba\"\xe0\xf9u\xe5V\x13\x10T\x13tM\xa1\xe4\xaa1 \x96\xd2\xe2\x11\x0c\xb0g\x91\xa8\xa3\x13\xc9'\xcfU\x92\\\xf4\xc6\xd05\x95\x9b(\x08\xeaXk;0\x7f\xf2=0\xddd\xfb\x86x`;\x19K|\xf6\x08 \x1c.\xef\xe72\xc8\xc2E\xa7\xba\x11\xdd\xc1i\xa7\x9d\xa4J\xa4\xe4\xc6\xd3\xb2\xc9u\xa7aE\xb5\x8a\x16\xdb]\xb8\xd9\xee0\x02C\xa0\xe5\xcd\xf0\xdc7\xb0,Y\xee\xb3.\x9b0\xf7_~\xdel@\xb0p\x93\xe3\"\x19\x12\xb5\xabk\x92uP\xa4De\x1d\\JZ\x11\xd6Y\x7f\xa4\x0cY\x832d\x918\xc2\xb2.\xba\xd0-7L+\xabG\x07\x8f\xcf1\x04+\xf9\x8d\xf1/\xde\x81\xe0\xf2\x8a\x1a\xde\x8ee<\x93\x83\xbd\x87\x8bY\x92\x12\xb0:\xe5\x81\xae\x0e@\xdb\x95>\xf3\x04\xfb\xd8\x88\xe6\xf9 ?\xde\x88\xe1\xe3\x8b-\x01\x0e\xfcE:e\xa9s$\x07P\xce\x86\x04E\x07\xed9WUC\xac[\x99_\x85\x89\xb2e\x1d\n\x04\xd0\xb8\xe7-\xf4\xbcJ\xe1!\x16\xac\xb9\x05q\x80U\xfb\x90(\xa7\x18\xa8\x0d\x07*M7R\x04*\xcb\x01$()\x86\xa5$\xb1\xb5\x8b\xc59\xedxeW\x95\xf3\x85\xe5_\xb7K(\xfd\x15\xa6\x8c\xdc.\xae\x81\\\xc5aG\xa1\xf3\x1b\xa3R\x92\xadJ\xbc\x94\x14\xc4\xcbd\x02\xea\xdc\x92\xa9\xe672\xcf\xa6\xbe\xf4\x06d/\xb9\xa4\x00\xa5\xfb\xf5po\xc4%T\xd4\x10\x06K\x15O\x81\xd8\xc5\x8f\xd18H\xab#\x93\x96\x84#\x8f\xc4\xf9\x99v\x93E~-\x85sn\"K\xa3\xa5\xad\xe5u\xb6\xa0\\\xb4\x90\xac\xa3g\x97\x1di\xbb(`\xd7\xaa\xdd C\xbb\x01E\xf533\xfd\xec\xa4\xa8\xc2#\x13]@M\xf2\x8b\"\xb8Kk\xda\xe8\xccN-\xc5\x9eT\xda\x8d\x9a\x83 \xeb(\xe2$\xe1>\xccq\xe4\x99(\xbdx\x08\xe2C\xe9^\xc6\xac\xee\x83e\x96i\xeb\x11\x91\xf4\x8b,g~\xd2\xacb\xa2\x022\xbc3\x8a\x80\x0e\xef\x8c\x10\xcb\xc9p\x7f\x04;@\x87\xfb\x86\x0c\xc1aU\x90\xbc\x91\x95\xc1j\xb1I\x86l\xa4v\xd2\x00\xf6\xdbm6+\xf4\xb9\x1a\xe2\xa0\x1f\xee\x99\x06&8\xd7_e\x8d\x0f\xe1\xd6\xfdR\xfc\xfa!h(\x04m8\xf5\xc2\x89S\xc2\xdfE\xc3+\x0f\xbb\xd1\x17\xe2 \x1fJ\x89\x1bV\xbc\xc8\xc9d9\xde@\x87![\xff\x15=+\x05;G\xd1\x87S(*,\xf9\xf2\xdd\xb6\x0c\xd4\x8a\xe5&\xdfWG@\xca&\x03\xaf\x0f:\x12\x89\xf9\xcc\xc3\xf5\xf4|\xff\xd5\x8b'\x13\xf5s\xec[N%\x8f\xbfu\x0b\xa8\xa6\xbf\xad\x85M\xae\xd7U4\x82\xf8\x05[\x03\xde\xedz-b[\xbd\xc6\xfb\xb2\x8a\xbf\xf8\x02\xa1Y\xea:\xf91OH\x90\xfbz8\x97k\xd6\xf2\xb3\x04\x81\x84\xf3\x84\x06u\xcb\x14\x0c\xfc\xf6u3\x0b\x9f\xf0\xf3\xac\xce\xc4\xdfE\xbcv&Bx\xb6T\xfd\x0bM\xa2\x81Z\xfa=i\xa9\x10\xe4\x95\xd9\x92\xf0\x81\x06\x94\xf6|\xba\x05Y\xe2\xc1\xb9\xe5\x9e\xc0U\x97\x022_\x1f~2\xc1O\x01\x86\xb0W>\x97\x1c\xdf\x1d\x07\xfe\xf5\xf5m\x1e\xec\xff\x06\x9c!\xaef\xa7\x00\x86\xba \\\xce\xe4\x9a\x80\x92X\xe0\x02\x88H@\xd2/\xb29\xb9N\x07\x1c\xbd\x1c\xcd\xcb\xfaR\xffFFJ\xe5\xc7\x8c\x11\xbb\xa5\xb3\xaf,Gq](\xe2\x00]\xb3\xbcy\x81\xf8\x87\xce\\\x08\xc2\xc4\"jr\x90\xfe8\xa3\x05\xcb\x97c\xd4,\xfb\xd1\xf7\xaf,\x8e\xdeI\x99\xcdFD a\x89\x116\xcb\xb3\x0bD\xf1\x0f\xab\x059\xca\xf3,\x0fzG\x97\x0b2fd\x02\xc3\x97\x11\xfc4\x02\xb6\\\xa4\xe4\x00z\xb0\xdd\xcaHk\x19\xc3?\xdd\xd1U\xaf\x88\x8cG\x08#x\xea\x1b`\xf5\x8b\xbb\xcd\xa5\x00[^\xb1A\x19\x17x\xbd\x9a\xfe\x87\xbb\xe9z\xc4V {\xfaUc\xb88\xb7\x15j\x81\\^\xbd\x12\x8f\xea\x1c\x9c\x14\xd7\\zT\xee\xf6\xd6\x13\xb41\xce\x9aY\xdd\xf1-\xe9\xa4/\xf3\xac\xbf\xd0\xb3\xcbW\xdf\x0bm\x13k\xa7.\xb5\x8c\x9eu\xe6\xba'\xf0Hf\xa3<\x10\xc5>\xe0\x10v\xf8\x0f\xbfs\x9fZ\xb6\xf2\xb9\xf4E\xfb\xc9x\xe0\xa3\x14m\xe7\xa5\xf9\xd3\x9f=0\x1f\x8f\xc0\xd3\x94@\x96\x03\x06E\xef\xa4\xc9\xa7r\x0f\x98I\xbc\x18\x14\x1f\xb5\x81@X\x97\xd9\x0b\x16yG\xe2d\xc1A\x94$\xd0\x99SLX\xb0\x13Z\xb0\x98\x8eI6\xd5*\x9e;\x9c\"\x10r\x88\x1e\xf5Ok\xc9>\xf3\xc0\xa6z.\x9bpr\xe8\xfc\xa2\xa8\x96\xea\xd6\xb2\xc6U(\xe5'\xb2*\xac~\x89\xea\xda\xf2\xe3\xca\xf4\x8b\xe5+\x8f\xb7\xf8\xc5\x8c\x11\xae^\x9d\xa8K\xceeB\xa6 %\xef\xf2lAr\xb6\x92\x9c\xaf\x7f+\xfc:#L\x13-7\x19\x83\xbat\x12$\xc2&7j\xe2\xaa\xdb F\xbf\x8a\xdax;\x8fo\xd3uF\x1a\x89\x98#\xe8=\x8d)\xcd\x18o\x1d2\n1\x85\xa4L\xcf\x9b\x93q\x96O\xfa\xbd\x92d\x8ah;\x07\x8bi\xba\xba3\xb7\xa9\xcb\x12\x8d\xd0\xbc\xae\xfa\xa7 \x9d\x04U\xd4]\xf7gW0\x8e\xd9x\x06\x086\xf7\x80\xae\x02\xe5\x9a\xae\x8e\x88X\xea'\x90\xeb\xa7\xf1\x9c\x94\xa1\xc3\x9fD(^\x8c?&d\x1a/S\xf6\x13\xe7\x960\xe7\x8c\xb5\x1b\xfb\x00\xc4\xea\x88\x80\xc3\x8f\xa4\xa9\x98P\x97\x05q2\x94)\xcaS\xab\x15C\x9d\x99t]\xa5\xe4\xa7\xb1P\"\xda\xb1\xa9h\xd3\x7f\xb1\xe0\x1d\x8b\xe0#gL\xde\xdd\\\x95\xaew7Y\xa5\xebm>!9\x99\xbc\x8e\x17\xf0g/\x82\xdeU\xbbV\xd7\xbbk\xd4\xea:\xd7k\x04\xf0\x95\x125\xfc\xed\x90\xadyh\xc9b:\x18F\x8a\x1f\xd2PT\xa6m\xd5\xd0z\xf7o\xaenS\x96\x9d\xe1S\x92I\x95\"}\xb4\xb5{\xa1\xcc\x88\xe0\x1c\xf5f\x95\xbf~g\xae\xdaG\xef\xae_\xfbHo\xb8]\x06\xb5\xd6p-\xf5\xb8\x0f\xb0+\x90U\x9f\x06\xa8\xb8\xd1 \xa7?rv\xbf\x91nDGD+\xf2i\xa30\xd8\xd2\xba\xdc\xe8E\xbe\xb9\x80\xa1\x0e\x90\xa1\x05\xd6\x12\xde\xe57/\xbf\x12\x17\xed\xa1O\xf3l~DY\xbe\x12\xbaRM\xf9\xd3\x8d+\x9b\x15J\x10\xc2\xdf\xa0U%\xc1#\xbf6\xab\x11\x85Z\xb7V3BEH\xe4\x12\xd5?\xb2.+\xdf\xd5\xaf\x99t\xe5$\xfe\xd5\x16\xd4\xd1\xc2\xf4\x9d-\xf2^\x18$\x1a\x84dRh\x84t\x00\x1fX\x1d\xbe\xc3\x99\xaanP\x83zY\xe7\xc0\xb0o#`\xc1\x1b\x16\xc1\xafa\x04o\xaeA\x81\xdb\x82\x1fR`\x13&\xd4\x9ao\xc4\x0dt\x96K\x13m\x8b\xa2i\xce\x86Q?rL>oD3\xb0q\xf5e\x9b.\xbc\xa9\xc3\xcd+T\xe8\\\xab\xc8l\xc67\x0e\xdf\xef\x159\xdc2%\x1b\xac\x8dQ%\x1b@\xa3\x86\xf74A\xd7\x1d\x89y*+\x87=8\xfc*l\x05\x896\x80 0\xb7\x13;t\xb2h\x06\x02\xa7\x02\x9fk\x87\xcd\x06`\xc8\xaf\x03\x06\xda\x00\xc3<^\x18\xf0\x15$\x18Z\x85_\xde|\xd9\x19\x119B\x94\xda(\xa99\xe0\xd6&\xaf\x99\xf3<\x1c\x97I\xc0l1KW\x9c@\xa9|\xcb\xff\x14\xeb\x10\x8a,=e\x0fV\xd5y\xd9|\x16\xc9|\xcd\x14\x0eD1 SWa'Q\xd8\xechB\x1b\x9f\x0e\x96\xd0\x01Au<\x99\x8f\x0bZ\xd7=\xb5\x0c\x1aV\xd4m\x82\xcd\xba\xa8\x9e\nye\x19\xa2N\xef\x8bRL@\x83\x8aP\x1a\xa2\xa2Y\xac\x02\x16\xc4G\xbf\xb0\xd2\xbcbZ\x0e\xd7RT' \x0b\xde\xb3\x08^\x86\x11\xbc\xd7\x97\xca\x14\x08\xe8I\xc4\xcbh\xc06%\x7f\xffe\x9b\xab\x93\xd2\xd8\xd7\xc7\xb8\xe9\xbcy3\xdca\x08r_\x96\xcc8S?\xbc\xff\"\x84\xbd\x11\x0ce\xbe\x18\xca\x14\x862\x85\xa1\xa2\xda\x96\xc2K\xaf\x9aa,x\xc6\"\xf8!\x8c\xe0\xd9\x97s\x10\x0e\xe4{v#\xc8\xf7Wb\x18\xf3\xc7/\xe3dn\x0c\xbf\xfe\xc3HT\xe1\xcf\x86\x88\xf4Jr\xba\xaft\xe8\x10)\xcct\xf1\x10\xedu\x94,D\xb3\x9fW\xff\x95\x88\x84\xc7\xa5\xed!\xbf\xbeb\x81\xb5\x88\x9e\xe6d\x11;\xdf*\xd1\x15K\xf4\xa30 \xaa\x12\xa3\xd8Z\xdd\xdc\x157-R,\xbf\xdaz9#\xa2\x1b\x81\xfd_\x83\xe8\x1e\x91\xa1~{\x01\xca\xf0\xca\x9a[\xb8\xa3\xa2\x86Z/\xd6\xe5e\x89\xde\x95\xae\x11\x82@\x0eS\x18\xa0~)\xde%\xee|S\x0e\x1e\xf7r\x06\x87\"\x91\x8b@\x89\x1cQ\xa2\xba\xb9'n\xee\xb5\xf3\xe5\xeb\x97\xc5e\xd1\x83&\xd4\xce\xe1z\x1a\x827\xf6G\xcf\xec\x8f^\xd9\x1fa\x8e\xaa \xa7\x11\x9c\x10.ZP\xed\xcd/T\xb0.\xa9\xe4A\xb7\xa1g\xd5\xb0\xd6:\xdc\xf8\xf8\xaci\xd4\xf9\xe7o/he\xf2qw\xe6\xa9L\x10v\xd0YY\x1d\xdd\x85\xe6\xf5\xcd[\x1b\xdc\x90\x18\xe2\x94ks\xe1\xe2\xeba\xf5\xb7\xd2Y\x18b6\x9b3\xf1R\xfeV\x92\x89Qe%\xfa\xbfuK\x1b@M\x9fk\x9eli\x1f\xd7l\x03v\x9dT\xff\x84\xcc\x17l\x85br\xf9c\x001\x95\xa2\xf6/\xa4\x9d\xf2\xb41UO\x8dq{\xd1*+\xb5\xb0P\xffM\xb3j-\xe9'\x9a]P\xf8DV\xd0\xfb\x1bl\x03\x81m\xf8[\x0f2\n\xfc\x97\xc2c\x8b\x91\xbc\x06\xbd\xad\n|\xb2\x98~Y\x8b\xc3\x8c\x14\x1ez\xc3\x9a1\xa1\xbeD\x85\xd2ku\xe0V\xad,\x846\x9a\n\xe7\xe0\xa0Z\x87v\x1d\xe6\xda\x1ax*\xd7\xed\x1b\xc7OCZ\x9f\xa9\xccS\xea\xca\xac\xd8\x9a)\xeb\x9ci\xfb\xe8\xae\xcd\xf4\x86\xb4\xfd\xce>\xae\xcf\x1eX!\x91\x07\x06\\k:jZ:\x00])e1Y_uk\xd8\x8dS\xbc9v\xf3\xdf8C\xe25\xc1\xff\x84 \xa1\xbeA62\x0dT\x1b@\x06\x0d\xf8\x1a\x04\x1ap\xa8w\x82\xcc\x16z\xd7j\xc0\xb1\x15\xa8\x8c\xc5\nuxO\xd7\xed\xd3\xf2\xd7\x19a\xefT\xf3o\xa7\x9c\xb4\xd8\x11E\x1b\x7f\xde\xcc\xe4\xed\x17(\xb2\xec(\x99--\xfe\xebu\xdd\xcb\xb0\xaf\xee\xf6\xde\xa3\x93D\xcf\xab\xb3\xc2\xdd\x993'\xfd9E\xff\xde\x94\xcacgk\x1c\x94\xc9\xe9\xf9\xb3k'\xa7O\xae\x9d\x9c\xde\xc5\xc1\x97\x92t<\x99\xd8\x8b\x11\x18\xb6\xa6\x17 S7 \xb7\x82-\x04\xe1\x16\x19N\x9b9\xa4\xeb,zF+[UFK\x0bUy\x1b\xeb`\x97\x0f\xda\xe5\xb73*Jdk\xd5\xb2\xab\x9b?'\x18\xd4\xa2\x1e\xf0\x9f\xd5\xc3V\xf9m\xf5\xe0\x19!\x8bF\xf1\xed\xfa\xc3F\xb3\xeaV\xfd%c\x01\xef\x8c\x1aJ\x8dg\xd4XA\xbc\xbc\xdd\xae \x9eQ\x8f:\xe0\x19\xed\xdb\xeb\x80\xe3CW\x1dp\x16\x144\x82#\x8ey\x05\xbd1\x07\x93\x82\xa2-Yf\xd0\xf6\x96D\x02Nq\xfb\x9f\x88\xb0?\x9bZ\xbd1\xa9\xaawL\x98U\x9a*\xbeH\x9a\xaa\xb8Vg\xbb\xf1d\xe2\xdb\xee\xa4\xc0\x9aq\xac\xac\xbcC\xb7\xb7CH\x026\xa4\xa3\xb0}\xec85\x8a\xe5\xb1\xcd\x8f\x1d\x8b\xfa\xc6x\xec(\x07\xa9Z$\xc1p\xb7yx4\x96>\xa1\x8c\xe4\x05\x19\xb3\x9b]\xfe*\xa3\x12\xf3\xab\xbd.0\xc4/\xbeC6\x94\x98NeS\x18\x9f\x17\xcb~-,0\xf0\x14N\xbfg\xd6'\xe7$_y\xb4\xac\xae\x12\x1dJ#\x8cE\xf5\x0b\x02 \x90\xcd\x93\xa4\xc5\xa6$\xeefZ\x1aHR,OY\x1e\xff\x7f8\xf2o\xc2\x91\xeb\xc6ry\xa2\x08&\xb2\xbai\x14Q<\xa4\xcf1\x85`\xc43G\xab\xe5\x10\x81\x93\xebi\xf4$9H7I=/K\xaf6\xd1q\xafCM\xd3\x1e\\[\xe7T\xdf!Y\xce|y\x819\x0d~.\xbdw:Nf\xde\xee\x93\x95\x8f^\xc2\xd08\xebn\xff/\xd2 \x15\x7f\xadz\x85iZ\x85\xb61\xcf#3t\x90c\xcc\xb9\xafa\xd88\x1d?\x85Xk\xc4\x9b\xea\x80L\xf9\xb0;\xd5[\xc5\x7f^\xfb\xb3\x99\xc2G\xf65\x8f?\x91\xe0\x0bu>8\xfb\xa48FM|J\xdb*\xa01\x8d`\xcaq\xac\xf7\xf7\xbf\x9f\x9c<\x7f\xfd\xfa\xe3\x87\xc7O^\x1d\x9d\x1c\x1f}89\xf9\xfb\xdf{mG\x90\x05\x7f\xbb\xf0P\x1aM:\x11\x81X\xaa5\xb1f\xb5&\x05\x05U([j\x88\xb1\x1c\x9c<4\xa5w<\xae\xf0|\xc1V\"|\xba\x04\xa3\x9f\"b\xd6\xbd\x17\xebJ\xae\x85#\x08\xa3\xcaf\xdf(_G\xd5\xb4\x88\xc8\xea]\xad)\xf3M\xc2}\xee\xa4Kc\xcc;\x10\x8c\xf9xg40\x99j,\xed\xce\xbf@\xa5u!TZg\xb4\xd2d]\xfc\xbfM\x93u\xe6\x86_\xa9\xee3\x14X\xd4\x7f-\xe8pJ\x95\x03\xddBSj-*\xa5\xd6\xa2\xae`R?\xeb\x0f$k\xb0\xa0\xba\xcej\xe1\xa3\xf0Y\xb8\x14>\x8b.\x85\xcf\x82\xaa}\x08\x038\xa7\xf2\x06\xdf\x8a\x88\x92\x11\xb0`N9q\n#\x98\xdf\x9cFh\xfe\x97h\x84\xe67\xa9\x11\x92\xfe\xf7.\xc5\xd0\x9cV~\xfa\x82r\x9f\x19(\xf7\x8aFp\xca\xf7\xc9\xdc\x83\x16\x9flJ\xd8N\xffC\x84\xed\xc2 \xcd\x95 l+>\xde\x13\x1a<\xf7/\xbby\xf4\x05\x84\xed\xad l\x97\x1aa\xe3\xb7\xfaKZ\xcc\x92){\x9c\xa6\xbe\xd1\xfc\x97\xde\x8a\xee\xa7nE\xf7)\xad\x1clO\xf5\xbdvA\xe5\x0d\xb9\xd7Np\xaf\x1d\xd1\x08.8\xb5<\xba\xb9\xbdvt\x93\xbb\xe2\x98\xc5\xe3O0\xe4\x1bb\xd4\xde\x10G\xd7p\x05\xa9\x1b\xe3g$6\x14\xaaG\xbd\x15\xd1\x92r\x93\xf0\x81H\xbcNvv\x1e\x84\xf8\xbd\xf0\xaa\xb2\xef\x058\x04\x99\x84\xc6\x14\xf7W\x1b\xf9\x82\x90O\x1b\x01\x88\x8f\xba2\x1c\xf2_\x86\xec\x1d\xad^\x96\xc5\xac\xab\x97J\xdbP\xae\xaf\x9f\xd6\xa1\xd4\xf4\x95\xce$\xb8\xfb\xb7[\xedD\x1a\x03\xcc\x07\x1e!0\x9bo\xc1\x0e\xecq\x88?\x12j\xc3\x9d\x9d\x10?\xb3\xf1\x05\x98Y\xa5lcH-\xb9\x0f\xf9\x825\xd7\x82_\x86D\xcbu|\xb4\x04S\x96\x9c6\xae\x87\x16o\xd5\xac\x18*\xef\xd6\xcb\x9f3\xe9\xda\xff\x98\x9a\xc5\x93\xd6\xe2=\xe6\xa4\xc8C0\x91\xead\xb4u\x05$\x0c\x05G\xe4^\xbf*\x07I\x87\xd4\x82\x0c\xb8\x19\xba\x1d\x9b\xaa\xe4\xed\xcb\xf0\xa0\x0d84&\xb2\xe4\xd9P\x00*4pT\xa7\x10\xeb\xdfN\x9d\x0f-2\x8aw\xca\xc0X\xdb\xfa\xb3\xc6\xfa\xd3\xeb\xae\x7f\xdb\xfd\xba\xb5\xfeYge*\x1de\x8b4\x19\x93`\xcf\xdd\xa6<\xa66i\x97\xa3\xa1\xa7:\xca\xd4\x95\x0f\x067\xbb3\x9d\xa2\x8d\xd67\x9fF\xb6\xb8\xce,6\xb12}i|\xb6D\xa9\x06\x06m\x82W\x9c\x15q\x83\x8d#\x89\xcf\x91\xc9\x89\xca[\xe9\xe8Q\x0e\xd6\xc7\x15\x8cbq\x11\xa2\x7fe\xd6p\x7f\x08jM\xd7-TeG\x17\xa49\xfa*M\x8f5\xc6\xaf<\x99\xf2\xda\xc9\x84e\xce\xb2:\xc9\xe2\x07\xcd\x83\x10\xeff\xee\xd3\xdd\xbd\x88yc\x11\xb3k\xad\xdfcj\xaa0\xddX\xc3\xcd\xd4V\xa5.\xa9\xad\xb9\xaa\x10\x94\xe3\xeacZMH\x9f\xcc\x86a\xc8\xfa\xcc\xf6,z\xa8\xa3kkAe\xdc\x81\xbe$\xd5\xd1\xa2y~\xb9\x90\x82\x8a=\x977\x10!\xaf%\x13\xccU0\x08\xd5\x92 \xe27y\x07\x13\xe85Y?\x1d\xa9\xd7l3\xb3\x0e\xb1\x9a\xa9\xf1\xec\xcb\xfdNn\xcf\xc8\x84N\xaf\x7f\xc5O\xe4]\xf1\x03\xb2\xdf\n\xd0\x91\xf0\xec\x17\xcb`Q\xd1\x98g(Z\xead\x1e\xba\xb2\xf393\xf3\xf9D\x05\x1c\xa1\xd6\x15\x85\x9a\x01\\\x1a\xa4\xf7c\x1a\xc1S\x93\xde\xf5\xc3\xe3\xa7/-\x9a\xd7O\xfc\xfd#\x0fi\xffq\xe9\xae\xd7\x91?\xb4.\xf3\x7frf\x94\xa9\x98\xe1L\xe7\x84\xb3\xa6\xa3^V\xd1\xbf\\\xfc\xaaS\x07\xbf\x94\x81o\x9d\xa7\xee\xb1\xd0\x03\x1cs\x80<\xa6A\xcb=\xc5\xd2\xe8\xbbnq\xb1D{\xabYR;\x9c\x86\xa8\xa3cCjH\x84k\x85\xa4\x9e\xbe\x8bU\xbc1\x0d#\xa8\\&\xb5\xd0\x88\xe3\xd5\xfc4K\xb1B\x82\xeby\xb3\xadf}|\xfd\xd7':|Z\xaa\x17?\xf9h\x03?\xb9\xb4\x81\x9f\xba\xb4\x81\xbc\x0b\xdd\xb6\xf6D\xb7\xb5E@\xfb\xcf+\x02\xf91\xe2\xcbDM\xe9\xbfdJl\x8f4_\xafH\xe0bE@.8\x91\xb9qE\xa6\xed\xeah_\xaf\x8d6zh0\x06U\xbe\x07\x8b\xe9\xcdi\xdaV\xd8c\xa61\xad\x15\xc4\xbbm\x9a\xc0\xb2\xe7tB.\xc9\xe4\x98|\xf6\x00\x8cF\xe2\xdf\xcb\xa8s\xbf^^\x1c\xfb\xb7\x8e\xc01\xa6\xc2\xf6\xd1\xccc\x82\xdf\x9e\xfa\xa4\x07\x9c\x85Y-H6\xc5\xfc\xda/\x8eQ\xe7\xc8\xff\x10\x16\x1e\x0b\xf8P\xbb\xc4\xdf\xf1\x9d\xde\xdb7\xff-\x13|\xfb\xa6\x9c\xe2\xdb779\xc9\x97du\x0dAC\xf8\x13\xd8\xfa\xa4\x93F\x8f\x1eU\xa3\x10\x98\xfcS\xcc\x89\x1aX\xcc\x1b\xa0\xebI\x0f1\xa1\x89\xb9<\xb8aXB+\xb4\x19,j\xc8\x125W\x9c\xa1\x84\x8ay\xbbYh.Sc\x18\x08\xe7@|6o\xa3oRZR\x04=\x84C\xe8aE\x028\x80^\xd4\xb3c2\x83\x01\xf4\x0czTu} \xa6\xbbp\x9c\xcaR\xfd[{\xe8\xb2\xba-,%\xfc_t3\xdaR%\xa4\xb4I\xe1\x9a\x96^4x\xe6\xf4\xda\x9c%\xc8\x1d\xe0\xc5\xb7}\"\xab/ ?\xcf\xbdVt^\x93C=\xd0\xaa\xdcb\xf5\x94\x9d^\x9d\x89\xb3t\xc3\x0d\x16A\xe6\\\xe0\x06\xae\xb5\x1cT\x1e\xc2>\xe6G\xe4\x98\x02\x07b\xc3\xb6\xb6\x83\xae\x06\xc0\x9a\xb5\x0e\xe4\xc8\xe0\x10\x82LR9l.\x94\xed\x92\xb2\xf4\xad\xa8\x18\x988\x0b2\xe7\xfe {\x9f\x9c\xcd\xd8\x86pS\x84Ig\x84*C\x94\x9b>I\xaeG\x9a\xdes\xab\xdd\x1dl\x83\xc6^\xfcq\xb7D*=\x19\xaeWWh\\\xbe&\x06?\xb9\xde!\xc1\xb9\x91\xcdz\x14yYD\xac\xdc\x1b\x8a\xa5\xc2LY0L]\xe5^5&\x9a3\xb3\x06\xe4\x80\xb9\x1f\x94\xba\xbf\x80\xd6\xfc\xee\xd5\xcb\xe9\x92\xbd\x8a7Q\x0f\x88}\x8d\x1e2\xbb\x11\xec\xecy\xf5\x92\x14G\xf3\x05\xf3\xb11\xc8^4\"\xae\xcb\xe9M\xc9\xfd@.c\x9d\x19\xf5\xe0EmFH\xaf\xd9\x8c\xb3%m\xee\xfc\x8e\xf9<\x0dH\xa5J\x12\xdb^\n\xb0\xe2\xe3\x0d\xf4*\xd8\xfb\x13_\xf6T\xf6\xefK\xa5@\xa3T\x1fI\x10V\x06)W\x06<%\xe5\x98\x88w\x17\xeb\x8a\xdf\xcb\xbc AU\xa7\\T\x12\xe7\xbbR\xcfy\xec%\xb5i2\x97\x99\xddU\x97\xa3\x94\n\x9e\x05\xba\xb9\xcdR!\xefJ?o}V\x8f|^\xc6\xe9&\xc2\xd69)\xc9\x86W\xfb2k\xa6\xc7V\xd3\x1dN\xcdk\x8b\x81Z\xfd\x13L\x97W+\xceDHu\xdf\xcd)\xd6\xab\xb7\xfeN\xc3\x86\xaa\xd5\xcd'\xd6\xaa\x1at\xf9\x8e5>&\xc6<\xa0\xea\xba\xf2\xe4\xf7\xc4.}\x93m\xb8\xdf\xa5\xf8\x81;|\xa3\xd3\xa5\x14Y6\xe7,,\xd5\";xn\xea']V\xc2%m\n\x97\xbc\xefa\x16\x01\x1d9\x05L/\xd6\x8aO\xff%\xf1%n5o\xf4M\x84=T\x8dQc\xa9]\xf3\x98\x1agd\xc7\x8a\xe8 7\xb3z8\xda\xb2\x99MF\xb1!rx\x0e\xa5\x02\xdc\xa6\xe3\xf1_-\xcf\xa1\xbc$r\x05\xfdF\x91o\xcc\xbc \xe8\x1f\xfb5\x9f\xc6\xec\xf5\xb5\xa51\xdf5\x02m\x13\xffb\xae\x93\xa4\xae&m\xabk\xea\xbb6\xb2\xd6Bn8k]\xc7\xa1\xae\x895o\xf1\x8d%O\xd9\xe2\x06ga \xd9\x1f5)\xc1WD\xd0\x8f\x12\x7f\x8c\xe1\xa7\xdd\xab\x0d\xcc\x90\xf5\x82y\x1e\xd8R\xa1\xa4.\xef\xfa\x14\x1f\x9fa]m\x9b>5\xaa\xfcd}\x07\xfe\x9cz\x0e\xddTnZ\xf8\x03c\xa1MUa:\xabU\x98\xee\xcc\xb6\x9c`\\\x90GV\xe4\x00}\x1a\xb1Z:\xc6-\xa9\xa4\xc4I\x04+\xceJ\xafB\x14\x13V\x95\xbf\xa7\x19D\xaee\xf1:\xad\xce\xf2l\xb9\xf8w\xb0\xe2~6\xbc@f\xbb{\xc7P\xd5\xc5\xf9wO\x06\xde\xc8\xb9w\xe9\\\xf8\x95\xb59w\xfe\x99\xe0\xdc\xbb\xf7\xb5~I\xf0\x04\"\x04r\xbd\x86\xe1(\xc4\x18\x06\xccY>\x8c#HFp\x00\x89\x87q\xd0A\xc7\xec0P(\xe8G\x81\xb3:\xe5\xed4?U\x14\x8cD\x90\x04&\x12\xa9.\xcb\xf87\x165f\xf1&r\x06\xd2!\x99py%b\x08V\x9e\xbd<\xdf\x84\x86\xab~\x9e\xd3M{J\x8a\xe3\xe5\xa9g\x81\xcfR\x06\x1c\xd8|\xc2\xcaJ)\xc2\xea,y\xf4J'\xe4\xb7\xb4\xe5y\\&\xc6\xd9 \x9f\x96y\x8a\x0b\xce\x0bm2\xc9\xc05K 3m\x96ay\xd3\xffT\xfbDVo\xa7\x1b\x0c\xa9<\xd483\xb7\x11$o\xc0H(\"\xce\xfd\x8f\xf8\x9aV\x86\xef\xea\xe7-)\xd5\xa7\xdbts5Z\xab\xe4W\x1f\xf9Y\xff\xfe^^g],\xbc7\xae\xb11\x97U\xbb\xefy|\xb9A\xaf/\xd8F*\x8cy|\xb9\xe9\x99\xfa\xa2\x96\x8f\xc8\xab\x13?\xa3Yk\x06p\x08\xef\xa9pa\xf9\xe8'(\xcd\x13z\xfd\xe9\x88\xee\x98\xe8\xcewn9\xd9\x18\x13\x8d!\x8f`n\xbe\xf8\x94,6\x80\x9d\xd6\xfe\xeb\x98\xcd\xfa\xf3\xf82\xb0T$\xb6t\xd6\x14\xbe}\xa5\x04\xcb\x1e\xe3M\x06D\xbb\xe3=\x90\x9fgI\xba\xa1\x99\xa1\x1c\xccO\xd74l|J\x16\x1f)K\xd2\xcd\xba\x15@WC\xdeL\x05%\x12\x82m\xd6_\xdb\xcaa\xc8\x0c\x06\xe6\xfeX\xfc\x89l\xb0\xbc\xacf\x80\xb8\x06J\xf1\xfen\x18\xa5x\x93\x9b\xa3\x14\xff\xeaKP\xea:\x92\xc4?\xbc\xb8[\xad\x84\xd1G\x8aj\xdeZ\xf26\x8c\xac\xec`x\x15;\xcd\xac\xdaeuq\x91.\xab\xc7\xe6i\x05Zja \xd8\xb1\xbb\xb5sY\xcf\xbf\xa3\xec\x7f\xc9\xb8\x19\x04\x1f\x82*\x91e\xd7\x0c\xb5f*\xe9\xa7\xfc\xf6\xd6-\xd8\xde\x8eQH\x95\x0dZ\n\x95\xab\xeb*\x8c \xb6\xbeq\x15\x81^\x06\xe9\xbfhU\xb2|\x93e!5o,\xfe\x9d[\xae\xe5\xd7\xd2\xe1Q\xa2.9N\xcf(K\xfdB\xdf\xa9e9\xd3\xee\x0f\xc0?\xe2Q\xbf\x9c\xd1\x8f\xfae\x89\x95\xd0/e\xba\x89;\x8bS\xa9K\xe8\xf0kE\xaa<\x1c\x1aUD\xa3\xac\xdf\xeb7\xd1B:\xab\xfa\xbd\x9d\xe2\xdb{\x1d\xae\xad`\xdaki\x04\x05j<\x0f9i\x1b\x0c\xe0\x8d\x14s>s\x8c,\xf0\x05\x91\xe6o)=C\xfe\x0b\x16\xb7\x8b\x088)\x80\xf1\xe1\xe6\x9aW~\xf0\\\x97\xa9(\x0f\xad\xcd\x98\n\x15C\xb0!_\xba\xb9\x186\x8b\x8b\xd9\xd3l\xb2\x81\xa3\x0b\x9bU\xd9\x05\xb0\x8a\xf3L\xcf6\xd0\xcd#@\xb9\xbd\x84\x83\xf2`\x00{p\x1bv\xcb\x8d\xe6 ]\xcaL:\xeeT\xf0\xf9\xb9\xf2\xa36\x16\x0ea\xcf\\\xf5\xb6|M\x0c\xcck\xf1\x1b\xdf\xf0\xd1^\xa2\x90~\xe7\xee\x9d\xfd\xef\xf6\xbe\xbds\xefN\x18\x95\xb7\xe1\xe1C\xd8\xbb\x07k`\xf0\xe8\xd1#\xd8\xd9\xbb\x17\xc1\xdd\xfb{\xdf\xde\xbd\xf7\xdd\xee7\xcd\xf7\xeeh\xef\xdd\x89\xe0^\xf5\x1c\xd3\xb9\x07\x0c\xb6\xe1\xce\xb7\xf7\xef\xee\x7f\xb7\xbf\xf7\xdd}Xs\x98\xfe\x8bo\xe9\x7f\xc9\xcf\xf6\xeeG\xb0\xbf\x7f\xf7\xfe\xb7\xfb\xfb\xf7\xca\xe6\x8f\xe5\xe7\xd8M\xf9\xe6\x9d\x08\xee\xec\xdf\xbf\x7f\xf7\xdb\xef\xbe\xdb\xfd.\xd4\x9bpl\xb9@\xe7\x0f(\xd6\xba<\xdc\x10j0\x80;{\xf05\xe4\xb0\x0d\x9fi\xf0\x94\xe0\xa6yJ\x02\x16\x86|F\xf6\xce\xc1sw\xaaKh\xc5\xaf\xd1K}R>\xdd\x943\xc2\x8e:;\xd8\xacq\xcfvCc9k( \xa2\x89\x14\xd6\xee4\x95\xc1|/~\x10\xc9\xc9\xb4\\\x00\xfa\x1b\x1f\xe8p\xaa\x02\xbc?\xd0\xe1+\xfe\xf7\x07i\xb2(\xf8-\x19:*n\xcb\xc0\xea\xf2\xbe\x1e8\x04\x03xF\xf1IB\x8b\x85\xc8\x8d\x8f\x9f\x1cg\xcb\xbc\x9eW\xc6\x04\xb2\x86\x12I\xba\xb7\xd6g\x87\xad\x8fgqBE\xdb\xd2\x96)ng\x94\xc5 F\xa5\xe3\x10\x84\xee\x12c\xc4s\xd3)9M\x93\x0dB#K\x01\xe5#\xb3\xae\x84I\xed\xb38j\xb9\xf7\xfbZ\xff\xedT1\xb7\xcb\x02N\xe1n#\xc3j)M('\x89a\x12A6\xb2\x17\x9f\x06\x10FU\xcd&\xe9)4\xce\xe3\xc5\xcb\xba\x0f\xb2/\x8c\xae\x01\x04\xbe\xeeMXt\x89\x19-X\x88h\x04\x07\x10\xb0\x93\xeb\xec\xd6\xd7\x14\x93\x9btf\xeexn\x07\x92\xdaI\xf5\xbe,\xed\xfc\xde\xd9\xce\x90E@F^\x8d\xbd\xb1\x90\xc3\xe6\xd9\xdc\xb1\xd9\xb6\x88O2.h\xc3\xd32\xac\xf773\xac\x9d\x1b\x1e\xd63\xf7\xb0z\x05\xd2\xc0\x9a\xf1\x03\x0e\xe1\xc5\xf1\xdb7}\xf1(\x99\xae\x84\xdaVRK\xcf\xdc\xa2\xaf\x9c\x04\xf8\xd8\x9a\xc9\xd3\xd2\xdc\xc7N\x0c\"\xf0\xb0\xe4\xe0\x08<\xc2\xbfw\x90\x9d\xf3\xea\xe0\xb3G\x07\x9c\xf5\xd9\x86\xfd\xfb\xf7\xee\xde\xbds\xef\x9b\xfb\xdf\xc16\x04\x843d\xf7C\xf1\xe7\xa3G\xb0\xdf>}\xeb\x0b%[{M\x87\x0bu$\xbe\xae\x8eD\x19\xa8\xc5\xef5\xceD\x91^\xa0|\xd08\x14;\x89\x9a\xec\xb6\xb1\xb0\x0c\xa3o\x0f0\xfc\x161\xa5>p<\xd82s\xf2\x93/M\xdf\xe0\xa73\xbf\xd1\xc0\xa9=\xbf\x93b\x9a\xd0 JO\x9e\xdd~\x817\xdd!:\xd3\xc1\x01\xec\xb4\xfd\xffLfN>*?\xc3\xd5\xb9\x9e>S\x99\xa8\x9c\xa3\xd1\xd2\x0c\x97{\xc7\xcb\xd53\x8d\x0b\xf6\xfc\x9a#+\x8dq\x7f\xd9\xe8n\"~\xc3\x13qn2~\xc3\xb7\xcb\xc5\x06}*Dm\x86\x15\xd9\x9d\x98\xf9:U\x96\x02.u\x8a\xa0Z\xb1\x10\x98\xf6j_\xfe\x89\x15\x8c;\xb23\xf2\x8b\xa8\xec\x8c\x9c`\xef*\xe7~t\xce\xafRDt\x04\x85VI\x15\x959\xa3\x03{J0\xef\xc9\xd1\x1eB\x0e\x07\x90\xab\xd0\xfdc=\x02x_94\x88\xd61\xc7\x81gP\xb0r\xee\xfc\"\xf2Qz\xab\xfe\x15$\xe4:\x8e\x9f\xa2\x9a\xbdW\xeb7\xe4\x9a\xe8\x89\xfd\x1b;\x0d6\xd2k\x87\x88\x82\xaa\x14]]\x0b\xa5e^\xafG\xd3\xdc\xba%\xf8\x8b\x99\x96dU\xe1\xed\xb5\xfc\x11EUmKV\xa5M\xdd\x117s^j\xc1\xe3\xd1\x00v1\x07\x85%\x90\xc8\x02(d\xbefUt\xd1\xce^\xf5\xa5<\xb4Z\xd5\x14\xc1v\xc61\x92/\xb2b\x13\xd3\xe6\xf5\x93|\xf8\x99\xf5\xaa\x12\x03%\n\xec\xc3\xd7\xea\xd7\x0e\xec\x89\x02\x03\x0e\xcb\x9f-\xf5\xa1~)\xa3\x01s\xca\xe5\xeaJ\xbe\xd8V\xd79 \xad\x8d`+\xc1R\x00b]Eh)\x17\xd1\xb30\xd4\x92\x96b\xb3\xf2\xbe\xb3\xe5+\xde{\xe4\xca\xa3\xa1C\xd4l\xb6\xf3\x06i\x84\xb0\xaa\x19\xd0~\xc7\xfe;'\xefo\x0f\xbd\x86\xfd\xac\x84l\xc6!\x1b\xc3\xff\xe5\xb2\x03\xdfz\x1c\x07\x92\x9a\x0b0\xc6\xfc\x1e\x88w\xe0\x10>\xf3\xb9\xc7\"\x1d)Zm\xd4\xcfL\xa5\x8c\xed\x02\xbf\xd3ZbIU^Q \xefm\x9c\x92\xf8\xdc\x87\xf3Rf\xb9!\xefbd8\x94C\xc7bq\x1e\xe5\xa5 \x00J\xff\x12\xc1\xcb~6EgZ\xebg\"?\x89\xe6\x9d\xef}\\\xc3\xbf\x8e\x1f\xf8\x9e\x11\xaa7\xed\xde\xe3y\xf2\xffq-\xbd\xeaK\xf5\xc7+\x1a\xb9\x90\xcd{\x0c?'l\xe6sN)\x99G\xef\xc5\x8do\x9c\xa7S\x01\x02\xed\xf1\xdbL\x96\xb5;W!\xa7\x08Uz\xd8\x89\xd27\xe87\xcb\xba-\xef\xd0q\xbd=\xfc\x8dy,\xc4 Q\x0bZ\x9a\x95\xbd\xe4\xb4\xeb\xe6\xd31T\x9d\x86\x9b\xd9l\xd8|\x95\xc3\xcd\x03\xda\x89\x96g[\x94\xd0\xaeY \xf4\xc7\x9a%A\xbf]3)\xfc\x1a\xe9J\xda\x10\xef\xbd\xac-\x9f\xb8\xf7C\xadiq\xef\x84\x18>\xbe \x86\xaf\x8fH\xf3\xf36TT~\xb9\x03\xa0m\xb8\"P_\xb4\xef?\xcd\xd2\x94 \xa4\x0f\xe0\xd4\xe0\x03\x81\x01b\x1f\x0d\x0f\xf4\xb4\x92\xefX\xfb\xb9\xc8\xcb\xb70<\x91\xa9\x02\x8f\x8c\xa3d\x07P\x18\x1e\xe8Y%\xe7\x86\xe7\xef\xc98\xcb'\x07\x90\x9b\x9e\xc5\xf4\x8c\x1c\xc0\xca0\x89\xf7dAb\xde\xa4\xe1YR\x1c\xc0\xccp\x7f\x9agsLmkK\x97|\x15\x01\xe9\x93\xcbE\x96\xb3\x02\x93\xc4 \xac\xbcr\xfb\xb4\xf5\x96\x05\x81\x82\xe5\xc9\x98i\xf9i\x94 ]\xdbn\x9a\x0f\x8d\xdeQ\xb3u\x15\xfb\x16G\xb0\x8c\xa0hn$L\xc6\x1e\xb00\x82-\xe3\x1e\xe6]\xa7m\xfa\xa7\xa5\x01C=OX&L;\xca\xf3,\x0fz\xaf\x13\x9aL\x132\x01r9&\x0b> \xc8\xc6\xe3e\x9e\x93\xc9\x03\xe0\x93d3\x024\xa3;s\xf5\xe2\x84\x9c\x03\xa1\xe7I\x9eQNu1\x02\x8b\xbf4]\xa6)\x10\xde*\xccIQ\xc4g\x04b:\x81x2Ix\xb3q\n3\x92.\xa6\xcb\x14.\xe2\x9c&\xf4\xac\xe8\xf7\x0c\x14\x9b\xa4\x05q\x90\xfc1\xe7i\x9a\xc0r\xf8\xf7L\xed\xfcfP\x07\x05\xeb\xe7d\x91\xc6c\x12\xdc\xfe\xbf\xc5\xed\xb3\xa8\x9b\xa8AE\xd8\xc6\xc3\xe9\xf6v;\x84\x17\x90\x8a\x85a\x9f\xc6s\x0c\x8dxN\xcf\xe3<\x89)\x83\x9f\x92,\xc5\xe4\xdb\x86\xfc\x92\xad;l\x96g\x17\x90\xf6\xa7y<'\xc5\x87\xec\x1dV\x91\xd9k\xa6b\xd3\xb0\xfa\xcb\x91\x98\x06w\xee\x86f\xdc\xcd\xaf\xdf\xba#K\xa2L~>!\xd3\x84\x12\x95\xfc\x9c\x8bE\xbd\x93\x13R\xbc\xce&\xcb\x94\xf4L\xa4T:I5\\\x9e0\x8f\x12\xe7\xbb\x9ef\xf3yF\x8f.\x19\xa1\x85\xcc\x7f\x8e\xf7\x1bwH1\x8e\x17XS\xf1UB?\xbd\x8b\xb1\xae\xa2J\x9d\xdf\xba]\xcc\xe24\xcd.\x8e>/\xe3TV#d\xfd\xd3e\x92N\xbe\xcf\xf2\xf9\xb3\x98\xc5\xe2\xb5,g$\x97OY&o\x92<\x89\xd3\xe4\x0frL\xe2|,\xda[\xc4y\xa1\xff>#\xec8\x9e/Rr<\x9e\x91\xb9\xf8\xee\xaf\x17\xc7o\xdf\x88\x9d\xd1\xe9\x01\xc6\xf2U\x07\xb3\x8c\xb6*D5\xab\x8eF\xe8\xa8o\xdd\x82^\x86\xbd\xf6D\x11\xb2\x86\xb1\xa0\xb7\xa4b\x9fNzp\x00\\\x82*\xf8\xc6\x8d\x97)\x0b\x03\x16\x86\x8ex\xd7+\x18\xc7l<\x03q8\xb6\x1e\xcb\xef\x1a\xd9\x1b\xae\xf8^\x16\x03J\xa6\xabNH\xc8F\x8e\x05\xc3|$\xf9f-\xa9<\x1c4\xfb\xc6\x1e\xe2<\x8fW\x1bt@d\xb3\xe8]\xa3\xff-\xeaI\n+\xefp\xd4\xeeH\xb0%\x92O\xd2z\x03b\x0eM\xe3\xabr\x84\x1eT\n\xae\xe6\xb3\x9eAB\x0b\x16\xd31\xc9\xa6\xb0RK\xd2\xe7[\xd2\xf5i /\xc6\x01U\xcf\x86\x8b\xb7\xd2\xb2)\xce\xb8\xcb\xb4\xbc$\xec\x8b\x8c\xce8\xdb\xea\x95\x8a\xd9\xac\xde4\xd5Nd\x98`\xf0Cv\xcc<\x0b\x05)\x15\xa3)\x87\xbb\xd2\xfd\xecF\xb0\xacP\x91\xb4\xb3\xf3v [\xe6\xf0\xc5!3$\xe80\x14\xbe\xeb*\xc6N\x879\x17\x0f\xc90\x1f\x89\xf4\x8at\x99\xa6fMt+\x13&\x82\x8cf\xf9\x1c\x0f\x0f\x81s\x03\xb8\x8c\x90N|O}\x91\xd6<\xc1vOIQ\xd2\x9dc\xd9\xc7\x92\x8eo\xbe\x175\x11\xaff\x9b\x99\x9a\x8dT\xe2u\xbc\xf0A'+\xca4\x93\xfa\xba\xf4\xa2\xf5ue\x01_Y\xa1\x8a5\xe5\xee\x84?\xdb\xa5\x84p\xc8\xef\xb1\xcb\x7f\xdb\xa8K\xc5x9^\xa7\xee$s\x1e\x08Y\xd7\x81 U\xda\xfcn\\\xdd\xa5\x18r\xb1\x01\x98\x8aU\xc1\xc8\xfc\xc3lI?\xbdN&\x93\x94\\\xc49\xf1E\x9c\xee\xfd\xcf\xfa\x93\xa4X\xf0\xb3I2\x8eH\x97\x9cp\xe9n\xd4\xf4\xb2\xd3\x82\x05\x1d[\x08\xcd\x93\x01 0\x959\x0b,\xbel`\x14#\xccw\x0d\xe7\xa0\\#\x0e\x80e\xf14\x9btC\xf9\xbcL\xb2\xa5\xaal[I4+55\xc1\x05?[.\xf8D\xfc\x93\xa8+\xe0\xec\xf7Ty\xd4m\xe8\xf5Bc\x06\xa5\x10\x19pK0\xf3\x95\\f~\x82\xf9l<\x8c\xce\xa9N9\xa5\xc0\xe1\xbc\xa7\xfc3\xd0\x8a)V/\x8a\x13\xb2\x0d\x0eu\x9a\x11\x99\x83\xc0p\xec2\xce>\xb0\x91\x1d\x96\xf5^\xfaI\x81\x9dQ\x91\xf8\xfe\xa05\x88\xf6\xfcg\xc9\xd9,M\xcef\xdd\xdc\xa5Z\xe1I6Fu\xab\x99\x01\xd9\xaa\xf8\x8c\x9e!s\xaf\x08N`\xe4\x92=\xcd(#\x94\xa94\xac\x8f\xe0\x1e\xb9S\xc5\x03\xe9\xafX'\xdf\x8d+\xb5\xec0\xba\xd2@\xa4\x83\xab\xfa\x88\x90\x0b\xdf\x8dP=\xb2\x1c\xee\x8e\"\xd44\xecE\xa8@ \xfd\x84R\x92\xff\xf8\xe1\xf5+\x91q\x18\x16\xa8V\x10r\xb2\xa8g\xbb\x80\x87\xf0\x0d\x92\xc9\xdf~\xc3\xfdJ\xa5\xe7\xdc\xd8\x99m\x86\x03\x84\xf7\x94\xaa\xae\xb7\xb7\x8b\x910\xafM+\xd8\xecE\xb05\x86\xf5\x1a\x16\xf0\x08\xbe\x15\xbd\x08\xaa\x80w\x87\xb7\x7f;\xbe\xddg\xa4`\xc18\x8c\xf8\xdb\xfc\x83\xdb\xc3\xaf~\xbb\x18i\xf7\x83\xdem9\xb2\xf5\xbal\x80\"iN\"\xf8[\xefo\xa0\xdcN\x92\x08z\x7f\xeb\xe9?\x97\xc3\x02v\xe0\xee\x08\xb6\xd1)\x9e\xf2g\xbd\x9d\x9d\xdf.\xefp\x99\xbc\xba\xf5\xf5\xed\xdeh\xb8\x18\xb9\x8de\xb8,SQ\x98\xa1\x1f/\x16\x84N\x9e\xce\x92t\x12\xc4\x9a\xc8}\x94\x12\x8efA\xafX\xc4\xb4\x17\x86\xfd\x82\xb0\xc7\x8c\xe5\xc9\xe9\x92\x91\xa0W\xb0\x15\xaa\x03\x86\xbdq\x96f\xf9\x01\xfc\x9f{\xf7\xee=\x80iF\xd9\xce\x05\x11 qO\xb3t\xf2\xa0\x17\xe1\x8a\xe1\x7f\xfa\xabxo4\\\xc0!\xae\xdd\x1d8\x84}8@\x08\xdf\x87C\xb8+\xff\xe6\xf7\xef\xc0\x01l\xdf\xfeW\x10\x07\xa7\x05\xcb\xe31[\xa7I\\\xac\xe9d\xadL\x0fk\xbeg\xd7E0_\x17$g\xe1\xe1z\xc9\xb2p}\x1a\xc4\x05Y\x93\xb3\x84\xae\xb3,\x0dHL\xc3\xc3uN\xe2O\xeb\x15#\xe1z\x8c\x8f\xf9\x81\xb3\x9e\xc5\xf9\x1aE\xdb\xc9:\x8d\x8bb\x9df\x94\xac\xb3\xf9\"]g\xb4`\xeb\x8c\xb2\x84.I\xb8\x9e\x90\xe0tyvF\xf2\xf58\x99\xc7\xe9z\x9c\xc69YO\x03\xbe\xc7\xd7$\x0f\x0f\xd7 M\xd8:\x0d\xc8Y\xcc\xc8\x9a0\x12\x1e\x86\xebI\xb6\x9ed\xcb\xd3\x94\xacI0\x9ee\xeb\xb48L\xa6\xeb\xb4 A2\x0d\x0f\xf9<\xb0\xf6\xe8\x9a.\xe7\xebsB\xd9\xfa2\x18\x93\x05[\x93\xf1z\x11\xa4\xc98a\xeb,g\xe1\x9a\x91\x80N\x8a5*M\xd69\x0d\xc3\x90w\x9d\xa6l\x96g\xcb\xb3\xd9:N\x0b\xb2Nh\x9c\x06\xe9\x8a\x0f\xe5\x92O'\x8b\xf9\xd7\x01\x89\xc73>\xfb\x84p\xb0e\xf3\xf5\x92\x8e\x03\xbe{\xf9\x00\xcf\xd2\xec4N\xd7g\x19\xcb\xd6g\xcb8\x9f\xac\x93`\xba\x9e/\x02\x81\x03\xc5Z\x1b\x04\x0d\x12\xb6F\x95~p\x92\xd11 \x0f\xd7i\xc2\xa1\xb5dk%\xfa\xacY@\xf2i<&k\x92\xd38\x0d\x0f\xc3\xc3u\x11\xae\xd3 \x9e\x9fN\xe25a\xebl\xfci\x9d\xd1\xb3p=\x0f\x92q\x9e! \\\xa3\x8ai-\xd4\x08\xe1\xfaM\xfcfM\x83xN\x8a\x05o)f\xc99Y\x93K\xb6&\x17\xeb$]gl\xbdL\xd3p\x9d\x05\xc8\x16\xad\x17\xc2\x10\xbe\xce\xd7K\xb6>'y\x9eLH\xb8^\x04\xf1\xf8S|F\xd6q\x1e\xcf\x8bu\x9e\x9c\xf3u\xc93F\xc6\x8cp@\xb0l\x9c\xa5\xeb\xe5i\x9a\x8c\xc3u\x1e\xc4 \xc7\x98 \x9ed4]\xf1\x85\x9b\xae\xcf\x92\x82\x91|\xbd 1[\x7f^&y5\xefb\xbc$k\xa1b[\xb3|\xb5\xe6T1\x0c\xd7Ep\xba\xe2\x8b\x1f\xa7d\xb2&\xe9t=\xcbr\xb6N\xce(\x99\xac\x93?\x10<1K\xc6kT\xe7\xacY\xbe\x1c\xb3\xf5\xf2\xb4\x18\xe7\xc9\x82\xad\x97\x0b\x92\xafWt<\xcb3\x9a\xfcA&\xeb\x8b\x84\x8dg!\x87\xe8|\x91\xf2\xc1\xcf\x08]\xcf\x92b=\xcb\xb3\x8b\xe2p\x9d\xc7\xb4H8\xd2\xe4K\xb2\xceW\xeb\xd5\x82\x041\xee\x8f \x99\xae\x93\xc9\x9a\xc6s\xb2\xce\xa6a\xb8^\x064\x18K4\x9f\x90i\xc0\xd9E\x8e'\x19]\xa7\xa4(\xd6\x85\x18#K\xd2p]\x90u\x91\xf0\x05:\x0f\xe2|\x9d\xe4l\x19\xa7\xeb,\x99\xacQm\xca\xd7\xe7\"\x18\xcf\xe2\xfc\x84\x89\x01\x91\x9c\xacgIJ\xd6 \x9b\x85\xeb\xcb,_\xaf\x12\x92N\xc2\xaf$\x01\x9cr~iw\x14r\x16T'9\x8a\xdc| \x97\xecM6!\xc14\x0cC\x91Al\xc1)\x94\xa0\xeb\x9cF\x1c\xf0\xf3c\xaa\x1d\x00{{\x0f`k\xb8\x17\xc1\xed\xe1o\xb7\xff\xbc\x1a\x06\xbf\xedl\x7f=x\xf8\xe8\xe0\xc1\xfa\xb7\xdf\xfa\xd1\xe1\xd6\xad\xbf\xff\xfft\xfa{{\xf8\xdb(\xac\xdfhPhI\xa0\xc7\xbc\xe3\x0cS\x93sR\xff\xb0\x07[x\xceH\x12=.\xa9\xf3\x98\x1fS\xdb\x90\xc26\x12\xe8m\xd8\x1b\x95\x7f\xee\x8f\x90 \xffvyg\xbc\xb5\xb3\xd3So\xf2{\xb7\xbf\xae\xff\xbc\xcdi\xe1\xff\x11-\x8e\x86;;\x8b\xd1\x03\x87\x07\xcf\x14\xb6\x070\xf6e.\x8d2\xda<^|\xc8\x1a|\x97M\xf5as\xb1\xe4\xc7b#\xc9~\xf9\xcapo\x04\x87\xf5\x9f\x07\xd0\xfbDV\x06\x96D)\x06\x0d\xed\xef[\xdb\xdf\xaf\xb7\xbf?\xaa1[\xaf\xe3\x85\x89\xe1k0\x90\xaf\xe3E?)\x84\x96\x04=\x81\x84\xf7\xc3\x06\x1cd\x9dc\xa4\xa2\x82\x0dE\x0b\x89\x89g\xe4\xfd\xd3*\xef\xfd^\xa5\x11\xea\xcfI~F\x02\x93\x14x.\xa3\xe5\xbbG\xc3\xdf\xe4\x8c\x155V\x07\xe2O\x0bK\xf4\xbc2\xecl\xed\x99\x9fM-:]p*=K\xe6o\x11\xc1\x04\x06(~&\x9a\x96RE\x06\x04!\xa6 \xe4\x83\x0b\xf8\xb6\x9e\xd4\x1c\x85\xc2\x07r\xd8..\x8e\xf72\xe3\x14\xc3'8\xfd\\\x8e%\xab\xc62C\x17Y\xe7Ws\x0e\x83\xceP\xf63|k\xaf\xe3\xad\x15\xe7i\x83\xb3\x08h\x99m'\x82\x9c3X\xc12\x82yS\x0d\xad_mTPB\xc7\x8a\x0b\x1d\xb1r\xfe\xc0\xec\x87\xb1H\x9a\xb72s\x83\x06b\xa1\xab\x86\x8d\xdf\x8c\xa5k\x05r\xe5\x86\xef\xa7\x9c\xfbHm\x18a\xc7\x15~ma \xdeI_n\n\xedo[\xe2\xe6\x8e\xee@\xf1\xf7\xa14\xe0M}\xe1\xd0\xba#\xc7\x14\xb7I)\xb9D\x8e\xf4\xfb$%o\xe29\xf9>\xcf\xe6R\xa6y\x96\x14\x8b\xac@\xe3\xeb\x8f$\x9ex\x94\x95W\"\xde\xedi\x92\x12~l\x0fz\xc1\xf0_\x0fF_\x87\x0f\x0e{\xb7\x93>\xb9$c\xa3\xe1\x00\xcb\x9e\x08\xdb\x00g\xea\xebm\x94MT-\xd8\x88\x93\xaa\x9e\x82\xcdh\xb2\xa1F\xaa\x8c\xf9\x19\x94\x12n\x99\xa6m\x08-\xe2b\x1c\xa7O\xe3\x82\xc0\x00\x9e\xd6\xef|/\x07\xd9 \x1a\xd9\xc3\xd3\x80Tf\xe2\xdf\xfa\xc3\x7f\xf5o\x8f\xbe\xfe\xea6\x17%B\x93\xc6*\xa6 K\xfe \x1f\xf3\xb4\xb3\x07\x0e\x802vlK\x8b\x1d\xe3\xc2\x9a\xd0u\xb8ekM18\xd6{\x0e\x8dG\xf0\x19a\x8f\xc7\x9c\xcb\xe7\xd8\x92gi\x9a\xd0\xb3\xf7\xa4Xd\xb4\xe8\x86F\xe3$\xab\x14\xfe\xfd\xa4\xd0\xb4\xff\x9a:\x84/\x8dMcP?\xf6\xccoV\xfa\xa5\xbaCx\x97Wry\xc2\x15,\xceY\xf1s\xc2fAo\xbfW\xea#u\x15*:\xe9\xf5\xc6b\xf7\xf4\xf04\xfd\xf3*\xac\xb0\xd0V\xa8\xc1LlK\xd5N\xd0\x93]\x88&\x8dv\x12K\x1b|\xcb\x06\xd40.s#a\xa9|\x93\xa6.5v\xa1\x0d2CVA\x887\x9b\xb7\xf1dB\xc8\"]\x1d\xb3\x8e\xbaLmJ\xf3\xdeP\x86\xffye\x0eLi\xe0hf09\xd9\x15\xdaU\x1cQ\x1edC6\xc2\xbdr\x08\x13\x92\x12F\x80\xdf\xe1B\x0d\xff\x87\xf3\x03\xe2\x0dj\xcce`\xcaV\xabl\x03\x06\xb2\xa7\xa2!\xbd\x08\x89)`\xd6\x95\x19HV We=\x95Y\xd7r\xa6X\xad\x16\xa4k\xc1\x89\xb0Z\x94\x87\x12 \x1d\x0c\x84F|s\xad\x89\x08\x84}o\xdf\x00R\xc5\xect\x19$\xcdQ\xc2\xe0\xe2\x13\x88#\x15\x03\xebS\xf4\xbd\xf8\x90\x95\xfe\x1c\x1ek$\xbe\xb1\xac\x91\xd6\x9b\x15M\x1a\xa6\xbf\xfa{\xe7\xb2\x92\xe7I@\x83oL>\x12ctH\xba\xf7\xcd\x9e\xe1\xd9T~x\xef\x1b\xa3{\xc5B\xb9f|\xbbkz<)\x1f\xdf5=\x9e\x95\x8f\x8d\xe3:\x97\x8f\xef\xdf36>W.%\xbb\xf7L\x8f\xcfpV{\xdf\x99x\xff\x95\xfc\xf4\x8eqR\xa7\nX\xfbw8\xe2\xd7\x9e\x97\x04\xfa\xa4\xc3w\xe1\xd6-\x0c\xe1P\xbeU\xd2\xb5\xd8\x8c\x8b\x12\xa5M\xa5\xea\x9bQ\xf3\xfa/\xbe\xb0\x170\x80\xf2\x08lO\xe5\xc8\xe0\xc0\xd3\xad\xd9o\xc9\xc8fsL{\xb06`]ndv\xae\n\x047&on\xfc\xd8\xd9\xf8\xd6\x16q\xdaW}(\x95c\x0dtO\xa9\x89\xfa\xc8\x06\x86\xa7\xce\x91\xf2~\x17U\xbf\xfc\xe7\xd4\x7f\x18u\x07\xaeN\x16\xce\xa1\xf8\xd9\x8c\x8b\x18Z\xc4a\x0b\x8br\xc7\xda\xf8\x9dz\xe3wD\xe3NN\xbcn\xa2\x97} \xefQ\x7f\xc8\xca\x87\xeb5 `\xcfk\xc7\x88\x0e-\xab\xfd\x18\x9d\x84\xab\xfc\xdf\xb4b\xbfM\x9a\x15\xd0\xfd\x00\x86\xd4\x92\xf6\xces\xa3\xc1!h\x02AR\x04\x182\xc5Q\xd5\xcaq\xf9\xa05\n?\xb6\x06|\xfc\x0e\xf0\x08'\xf8i\xd6&\x06\x82{k\xd4l\xeb*`\xb3\xc5{\x99k\xc3\x1cR\xceY\x0d\xa9\xc1\xeau\xd5\xdc\x12\xeds\xef\x93\xc5\xe1\xb1s\x7f\x80\xb2\xa7\xc2#\xa8\xc2\xc4{?\xc5\xe9\x92\xc0|Y08%\x90\x92\xa2\x006\x8b)\xc8\x96\xbd\xca\xd9?\xb68fn0\xa6\x87\xf61\x9d\xa1\xc2=\x97\xc3\x12\x8d{\x0d\xeb\xad\xd9\x85\xb4\xfb\xb4@9\xf3\xf6\xbfv\x0e\x7f\x9bl\x07\xbf\xf5\xf9?\xe1\xa1\xb2\x0chRjc\xa01H\xb6\xc7gp\xef,>\xaf\x9b\x8d\xcecP\x14#\x01\xcf<\x87\xf5\xc1\xe4\x9b\xeb7&<\x95\xb6\x02\xe2\xf0)\xb4Cn\x9a\xa4\xc4k\x80\xaf-\x0e\xc5~c\xec\xb1|Iz\xb2n0?D\xa7qZ\xe87\xb6v\xb5\xbf\xf7\x14#o\x1b\xf5\xa9\xe8\xdek\xe0\xcf\xcd\xce\xd1~\xe3\x16\x835\xa8{\xecc\x93/\xfb\x0c\xedw\x9b3\xb7\xdf\xe0\x92\xe2M\xfc&\xe0\x9f\x95\xce\xc2\x8e\x95V\xcd{\x8d\xec\x8d\xc9\xef\xdcoTJ\xd8S\xa2F\x9fe\xaf\xb2\x0b\x92?\x8d\x0b\x12\x84\x11l\xdd\xfe\xd7\xf0\xcf`t8\xdc\xdd\xf9.\xde\x99\x8e\xfe\xfc\xf6j\xa7\xfc\xfb\xae\xc7\xdf{\xfbW\xc3\xf0j\xe4E\x18\xf8\xc8\xbd&\xfc\xde\xea~\xefOL+\xde\xc4\x8f\xce\x8b.\xbc\x86\xf7\xcc\x1a3\xb0\xf9\xf06 \xf9\x1b\x8c\xf0\x95%\xd2\xc1{|[\x94\\\xc0{rvt\x89\xfe\xc8\xae\xa5\x9dfi\x9a]\xc0Bv\xd2\x83m\x93\x03{\xfd\x0co\xc7et\x8e\xec\xba\x9c\xed\xad[\xb5\xdfv\xae\xd6\xc6\xf1\"\xab\x87\x94\xe74\x9b\xac\xa4RY\xa8\x17\x13\xda\x13N\xf2\xf8\x0b\xcdX'\x97\xf3\xb4\x87\xee\xf2\xda\xcd\x9eEU\x99T\xea\xce\x9c\xa0\x9b\xc2\xc4\xf6j\x0c\xc2;J\xbe^`\x84\x8b\xe8\xc8\xa2\"\x8e\xcb\xd5\xca\xedv\xc7X47\x97|\x8e\xa5\xf3\xb1\xf6\xa6d=,oN\xab79q\xb6\xbd\xb6\xa8^\x9bf\xf9\x8f\xe0,\x82\xd3\x08N\"\xb8\x88\xe0(\x82\xcb\x08\x8eG\x0d\xe1\xd59\xf6J\xdfd|\xc5V\x92\x0eYB\xe4\x9f\x9f\x86\xcd\xb9\xbf\x97\xb4\x1e\xa6 I'\x90\x14@3\x06\x8b<;O&x\x02\x98(\xb6j\xf4\xdc5X>\xf1\x8f0\x80WA\x16\xc1\xb9\xc3%\xe1#\x1a8\xc4x>\xfa\xba\x1a\x80\x1c\xc2\xa4\xda:\x93\xae\xd1|\x86\x01\xbc\xe7\xa3\x998F\xf3Y\x1b\xcd\xe7MG3\xeb\x1a\xc2\xf70\x80g|\x083\xc7\x10\xbe\xd7\x86\xf0\xfd\xa6CXV\x00q\x96\x1d\xe1\xa3\xf9\x03S]a\x91\x11\xfbh\xfe\xd0F\xf3\xc7\xa6\xa3\x19W\xa3\x19w\x8d\xe6 \x0c\xe01\x1f\xcd\xd81\x9a'\xdah\x9el:\x9a\xfa\x91\xd85\x9e\x9f\x1c^K\xeaB\xee&\xf8 5\xe41#;\x8c\xcbQ\xd8\xfc\x02\x0e\xe1\xf7\x00Uh\xbd%\x176\xca\xbbo\xc4\xdd\xe7\x82\x88\xda\xf9\"u\xc9\xd9\xfedsb\xa9\xc8l\xfd`\xeb\x9a\xdf\x8f0\x80\xd7\x81\xab\xda\n\xce\xee\xc7\x0d\xc6\xf8c\xf7\x18k\x87g\xd7\x10\x7f\x86\x01\xbc\xed\x1e\xe2\xcf\x1b\x0c\xf1\xe7\xee!\xd6O\xe8\xae1\xbe\xc0\xec\x8d\x9dc|\xb1\xc1\x18_t\x8fQg\xb0\xbaF\xf8k\xc7\xd0N\x91\xf9)\xd90\x9f\x81\xfe\xaax\xd6\xe74\x18\xf6\x12F\xe6E/\x02\xc1g\x8f0\xc9N\xcb\xcc\xdd\xe5\xe9\x01\x9a`\xd5\xb5\xed\xf8U\xc3\xa4_\xd1E\x82#\x0b\x86\xaa\xd6\x97P=|'\x1f\xeaT\xe0Wd\xc0\xf8\xd3\xe7\\\xa8\x8c\xa4\xb9]\xac\x83{\xb0\xfcJDVKC\xde\x95\xe6\x85\x995\x0e,\x99\xc4\xd4\xe5\xac7\xdb\x89\x13\x1a\x83\xdc\x85\x12/a\x00\x1f\xba\x91\xf6\xa5\x0f.H`\xbd\xf4\xa5\xc6V\xab\xb7\xc1{\xa5\x9dF\xc1\xcd))7\xa3/w66X:Az\x05m*\xf6\xb7\x0cZ\xa6\xf8g\x0e\xef\xdb\x97\xf3T\xea\xae\x98U\xbeK\x84\xcf\xd5\xe5<\xc5m\x8b\x7fa~\x12\xd7\x9a\x0b=\x0f\xff\x86K\xf9\xf2\xdb?\xaf\"\xfe\xfdW_\xe5d\xaa;\x03\xac\x16\xe8\xb4F\xfa\xb8\xaf\xc5\x9f\x0b\x91\xcf#!\xf2w\x95\x16\xe6]\xf5\xe4\x10\xfe\xf6\xf0\x907~N\xf2\"\xc9\xe8\xa0\xb7\xd7\xdf\xed\x01\xa1\xe3l\x92\xd0\xb3A\xef\xe3\x87\xefw\xbe\xed\x1d>\xfa\x8dJ\xb7v\xf8\xe5\xf5+ \x97\xb8\xc40\x8e)g>O \x9c\x11\x8a\xc9\x19' B\x94\xfef\xf5~R\xd7yY^\n\xa7\xd3\x9fsQ \xb8\xfd\xdb\xf1\xd7\xbf\xdd\x0e~;\xde\x0e\xbf\xba\xed@\xf6\n\x88\xb2\x84\x94'*C\xddXx\xa6,\xb5\x93\xa7\xa8/\xfb\xe5\xf5\xab#17\xe1J\xe2\xe3\x01r.\xcb\xaa\xd5\xdb\x13\x9b\xe0\xfb<\x9b\x8b\x8d \xdbk\xcfH)\xc5l\x92]\xd2%\xd9%a\x08\x87M?\x98\xa4\xf2\x83\x81\x83F\x8eJ\xe9\xa3\xa9\xa7?q\xba}\x9d\xcb\xcc\x86\x7f\x1at\x85 \x93\x17V\xe2|\x9a\x8d1\xcbN\xbf\xc0\xc6-\xfa\xa5Joi\xdbZ=\xa1\xa4w)MD\x16\x94byZ\xb0<\xd8\x0b\xfb\xc5\"MX\xd0\xbbe\xd2\xc6\x80\xee\x9f\x9eCB\x81\x86@\xfb\xb3\xb8x{A\xcb\xdc7\xb9pS\xc4(\xc3a>R-\x0e\xb8XE\x86\x132\xce&\xe4\xe3\xfb\xe7O\xb3\xf9\"\xa3\x84\xb2 \x1f\xee\x8e\xc2\x11\x0c \xe7T\xe8\xd6-0\xbe\xb37\x12v\xd5\x9e\x0f>\xa9m\xdd^\xb3v\x1a\x1b7m\xb5Z\xc5\xfd\xca\x97\xab\x81\xd0\xd6\x8cD\xca\xfdA\x0f\xb6MO\xc9\x90\x19\x0d\xb3\xfd\xdf\xb3\x84\xe2\xf2\xb4\xa7&S\xf5\xb8\x07\xa5\xe6S\xcb\xb9\xa1r\x17Sr\x01$`\x9a\xb9\"\x82\xde\x92Mw\xbe\xed\x85au\xb7w\x1a\x17\xe4\xfe]\xd3\x18\xaa\xd4A\xed\xae3\x0c6K2Z\x1c\xe3[6\xaf\x9d8]\xccb\xcf\\\x83\xa0\xbb\x8f)m\xe2\xac\x17\xe2\x16J \x07h\x9c\xf3)i\xcf,G\xb6yc\xce \x9be\x93k\x8fF|n\x1b\x8fz\xea\xcdD\xb4\xc7\xc8\xe2\xb3\xbf\n\x9c\x8d!{\x0f\xd2\x80\x99\x8d\x14S~\xec\x8c\xc9I\xa5\x8a\x8d\xe6\xe4\xc7z\xfa+_^b\xf5\x10\xd1\xd8\x96\x1c5\x88\xbd\xeao&x\xbb!\x8d\xf8\x06\x8dL\xfb3\x0f\xb5\xc4k\xfb\xbb\xb7\xcf\"\xe8m\xf7\xc2\x91\xdc\x9f\xa6%\xb5R)\xe6\xda\xd4\x86\x94]\xb5\x95\xb48\xd6\x94J3N\xb8f\x15\xe1\xa2\x9aSN\x97\xcb\xc8F\x1e#\xf5\x91\xd7a\xae\x94b\x96\xbcd^\x04\xd8X\xa0\x063\x8ektL\x9a\xb31\xa5Q\x9e\xcc\x03m\x91~\xc3\xecx\xbd\x13\xb4\xd8\xf4z\xae\xe1Z\xb2\xaay\x0d\x93\xc3\xec\xb4\x82\xd9\xc7\xb6{Yd\xc8\xe3\xe6\xd54ig\x9b\xe8N\xc2z\xfb_\x97;%s\xdd\xb9l\x915\xf7\xdc_9Bi\xffY\x97\xf6\xa5ui=ZK\xbb\xd8ZZ\xbd\xfc\xa7\xf2?\xd5\x83\xb2\x90\x16\x0d\xee\xdd\x0d\xfbO\x96\xd3)\x91\xde\xe2\xd7\xca\x06hN\x88\xd9\x9cfI\xa9\x8c\x92\x99\xc8\x15\x0f\xff\x7f\xf2\xde\xbc\xbbm\x1cK\x14\xff\xbf?\xc55\xa7_\x8a,\xd3\xb4$\xaf\x91\xedx\xb28\xdd\x99\xc9\xf6b\xa7\xea\xd7\xa3\xf2xh\n\x92\xd8\xa1H\x15\x17;\xae\xb2\xe7\xb3\xff\x0e.\x00\x12\x04\x01\x92rR\xd3\xfd\xde\xe3\xc9\x89E\x12\xc4r\x01\\\xdc\xfd\x9e@\x15\xcb\xf2\x13\xf1\x83\x9c\xc7\xa2\xfc\x17$\x0b(\x81p\x047a\x16\xe6\xb0\xc8\xf3\xd5x{{\xe6\x07\xe4:I\xbex\xf30_\x14\xd7^\x98l\xa7\xf4\xbb\xedi\x12d\xdb\xf8\xf1\x16#\x9fRo\x91/\xa3\xd3P\xc4nd\x94\x86\xcb\xf3\xb9A\n\xc7\x90\x1fA\xba\xb9\xe9@\x0c\x9b'`=\xf1\xd3y6\xb94Q$\x157\x97\xa2\xcb\xaeB\x1f\xb2:\xeaq5ED\xcd$\xed\x1f\x94\xb3\n\xc8\x99uG\xe2l\xa2\x99\xa4\x16\x1dS\xe5\x15\x98C[\xd2\x1a\xd8\x12\xc58j\xc4\xca\xca\n\xef\xbb\xc4\xa8'\x14\xd8\xe7\xa4\x1f\xac\x932\x1a\xf1#\x9a\xacB\x19\xcbcf\x1d\xa8nz\xf5#\xcb\xfd\xe0\xcb#\xba\x80\x11\x98\xd9\xb8\xe9/:r\xfa\xb7W\x9b!\xb7\xd0}D\xb3\xc2\xb8\x17[\xd6\x18\xfd\xf6j?\xc5H\xcfk\xb5^\xd4\xb3\xbd\x88\xa8=\xad\xca\xa8\xf2\x84\xc84'\x04\x8b\xac\xc3\x8c\x102x\x06{p\n\x19l\xc1\x1e\x8c1\xf3R\x00'\xb0w\x04\x01\x1cCv\x04\x01E\xe3\xd1$\xa0\x05.\xe5\xda&AKb\xf0\x1b\xee\xa5n\xb6\xa3\x86R\xdb3\x93\xe9\xac\xd4c\xc1\xb0\x8d\xe2:q\xd1\x16\xd0\xd4\xc4\x9eux\x8a\x03\xb75 \xdb\xe5\xdf\x1c\xdcR,h\x8a\xc3\xa3p\x8afOSzb\xc2\x7f\xd1\x9f\x05\xfd\xf9_\x90\xcc\x90Zd\xcfV\xecYV\xacV\x11=\x7f\xf2\x84=O\xf0\xb9\x0b\xe4\xeb\n\x03\x9c\x80\x1fC\xe9\xd8\xe1\xfd=\xe3\xa1\xbf=\x8d\xe8A\\z)\x19\xc8\xb3\xbch\xe5X\xc4EK\xde \xe7\xb2\xe8H\xe9\xde\xa9\x8b\x16\x97\xb0\x8d\x99\x95\xd9\x03\xdb\xacN\xe4\x0b\x1d\xf3y\x1eJ\x91~h\xb2taQ\xaeo\n9\x8f\xc2pQfP\x88\xda<\xf1\xc5E;?/\xe5W\xf3\xd6\xf2f\xd8\x1a\x82\xc5\xf5\xda\xe4\xd9\xc2_\x911\xac\x9aoD\xa07\xed\xcb\xa5\xbfzY\xbe\xef\x8d\x1ef\x88\x9c\x1ew\x06F\x18\xe5>\xb3\xf5\xe7\xb6\xb6\x87X\xbc\xd9Z\xdb\xf9\x8a\x9f\xf4<+\xb5'#V\xd0<\xeb\xdaN6\xb9\xcd\xae\xb3\xcap2\xb1V\x0dg\x8d\xae\x9f\xbf\xf2~\xfe\xca\xfb\xf9+\xf6\xf3WM\xd9\x94\xc7\xfb\xcfl\x8b\xed\x7f\xcb\xed?\xe1D\x87.\x9b\xb3\xadi6,S,d\xf6\x9a\xc7\x99\xec&&z\n~\xb3\xaf\x82+\x11|t}\xbb\xf2\x11h\x9c\xc7\x84\xfeu\\\x1f\x1e\xb3R\xa5\xef\x85\xfc}\xac\x8e_\xf4\x97\x16\xaa0+r\x1ae\xcen\xbb\x14>\x03\x06F\xac\x05\xdf}\xd0\x8c\xac\xd00]\xe2]\xce\x8f\xe1\xb4\x0c\x9e\xa7\x9b\xb0\xb5N\xe0}~\x02\xefK'\xf0\xbe\xee\x04\xde\xef>\x81\x05\xd5\x00'\x80\xa6+)\x0b\x9e\xc7\x8c\x1c]\xe1\xbd\xcb\xe2\xb3\x9e\x02QQpm`2\xe2\xe5\xc9\xe8\xa5\xe3\xb14u\xa2\xc0\xf6\x1b\xe7\xe3\xad\xcfl\x9f\xb2\x15 \x18S\x16\xc6\xac@\x88\x05<\x94\x97\xb0\x86\xebk\xad\xb1\xa2\x98&A\n\x0f\xbc1t\xb4++\xf6\xc2\xac\xec\x96\xfa\xcd\xa0\x16\\U7\xed\x99\x96\xfco\xd2ar\xf4D\xed\xec\x8b\x89\xa7P6\xa9X\xec\xac\xd5\xe44B\xda\xa6#\x87\x8f\x81X \xdb\x89\x95\xa8/\xb1\xf2_\xa5\xac\xe0\xbft\x14\x8aQ\xec\xd8\x8c;\xe2\xb4\xc2=2\xc9\x1b\x9b\xa0\xaf\xe0\xaeI\n\x02\xf2\xc6\x8b\xb4\x1b/(7^\xc4I\xdfH\"}g\x8c\xf4\x9d\xc11DG0\xa3\x1b/\x98\xcc\x9a\xa4\xef\xcc\x10\xd0i\x85\xaa\xa6\xc44\xe7\xb1\xbdj\x9ds\xbaf\x0b3\xfd\x84F\xd0\xf6\xeaQKB\xa2_3\xcd\x92X\x18\x96D\xd8E\xbf\xa2K\x00#\xd5\xfa,\x10fW\xc1'S\xef\xe7\xa3\x19\x00-#\x1ce\x0d]\xc4y_\xa5\xc9\xea\xa2\x1cS\xd6\xe8{\xb9\xe2\xb4\x99V\xca\x95s\x83\x91\xab\xca\xc8\xf5.\x92\xb8\x03\x97\xd3\xac<\xa1-,\xe1\x18\xe6G\xb0\xa4\x8b\xc4<\xa5\x18ZJE\xb27.,\xcbEL{9\xa1\xfd]\xd2_\x97V\x89t\x03\x13\xb5K\x81x'\x9f\x82\x08\xae\x12\x80w\x1d\xf3\xd0\xb1\x19\x85xC\x17.\xbb\xb9\x1f[\xb7`\xa2\xdd\x82a\xb9\x05\x13\xc7\xe5 \x10\xc1\x87cH\x8e\xc0\xa7\xd0\x0c'~}\xbb\xf9\xe6s\x0eQ\x07vU\x01r\x88:]\x16\x7f \xf3\x8d\xb8r\xb7\xab!\xa2[\xae~\xfe\xcaq\x84\xdaq\xf8\xe58B\x8eJB \x95\x14\x0c\x95\x14p\x0c\xe1\x11\x14t\\\xfe\xa4h\xa2\x92\xc2\xa4E\xe2(\x8cLrC \xe3^\xca\xda\xf6\xd2\x17r\x97]H\xfb\xc9NV\\\x08\x9a\x91 \x89\xa7e\xd7\x9c\xe6V\x8bM[\xad\xc9\xe6\xb6o5\x90\xa1\x8b\xe1~\xe5H=\xe5\xbe\x9b\xb1}G\xb1jP\xee;\x8a\x9cW\x1c9\x9b9T\x81N3u\xef\x05.\xcc\xca\x99G\xa4\xb8\xf5\x8c\x02\xc5\xa6\xe3\x08&\xb3K\xfa\xcc\xa9v\xa1\xdf\xc6s2\x8bi\xe3Nl\x92\xe5\xa0\xc5\x8a\x0fNs\xf5\xea\x0f\x98l\x9d\x9d<3\xd3\xe7\x92\x05\x8bb\xb7U1\x060\xae\xbdk\x9eK\xb1\xa9\"\xb4\xd1\xd2r\x15\xb5:G\x97Z\"\xee\xff\xa5\xd3\xfe\xb1\xc7y\xd1~\x9cO\xff\x87\x8e\xf3\x9b2\xcec%\xffi=X\xbb4\xebK\xc4x7-\x18o\xd9\xb5\xeb\xe9)\xbdTw\xfd\xc2\x85\x9b\xda\x89\x8b\x1c\xe2M\xf7Y\x0b=%J\x9d\xc6\n\xed[u\xd5\xdc\xaa\x95|G\xfeT\xfc\x925\x85\xcc~\xecQ\x8a\xa3\xed\x1f\xcb\x9f\x8c\xc3\xde\xf2\xb3,\x9cWl\x92\x1d8p\x1e\xc6\xd3\x94\xc0y\x92.\x8a\n\x01\xfdk\x14\x06$\xce\x08\xbc{sQ>\xfcq\xbb\xfc)tR<\x8d\xd9\x9c\xe4\x92)\xd7\xf9\xdd\xf2:\x89\xb2\xa6\xae\x8a\x97\xae%\xb9\x94\xbek\xea\xae\x1a\x1fp\xcb\xca\xbb7\xd9Y\\,\x19\xda9\xd2\xc2\xcdH\xc4\xe8=\xa9pS\xf3\xe6\x18\x94Z\xc3\x89\xdcp\xbb<\xba\x83\x85u\x93\x7f\x1d\x98|\x11\xc9\x04\xb1\x8e5%\x96\x0b\xd6\x1e\xb34\xd4\xc2\xee\xbd\xbf$\x99M\x9c\xc9\xe0\xb2\xb5\x0355\xf1\xef\x0fL)<8\x82\x18\x8eaH\xffR\x84\x97O\xac+\xba\x15X\x0f1\x0f\xd3\xcb\x85\x9f\xbeL\xa6\xc4\x8e\xd1t.\xd6\xf7\xd7\x1a\x0cG;\xbb{\xfb\x07\x87O\x99}KK_s\xc5\xa6\xadK\xc4\x95\xabq\x84\x00$\x0b5\xab=\x8c\x8bXw-I\x91\xe8\xc9p3\xb4\xb6\xb2\xd2\xb6\xc2\x94\xd7\xc4\xbb\x9aE\xfe<\x83'PPZ\xe5\xa5\x1f,\x08K\xa5@[\xd1\xcbxo\xcaLG\x154\xe8\x17)\xd1$\x80\x06\x11\xa7\x82%m\xc2\x82M\x9c@\xc6\xb2\xb8\x02\xed\xe7\xb55!zV\xed\xea\xc3Vm\xfb\x0d\x8fx\x1fO\xc2\x8e8\xea\x19\x02\xddw\xbc\xabi\xb2|\xf3\xaa\x9d\xa2f\x16\xb2Z\xaeN\xbepTGU\xd4\xd1\xe4\x08\xa1\x91`P\xfa\xf3\xf0:\n\xe3\xb9Yy..\xda`d'\x94\x8b\xecjP\\3\xdbw\xa1\xcd\xa3K\xbe\x02\x9e\x91FC\x08\xa8\x97Y\xe7L\xaf\xd4\xb6vF\x16\xed\xa7\xb1\x98A5\xdd\\\x12bi\xde\x9f\xe8\xd7\xe6\x9f\xf4\xdf\xeb\xb6\xc0\xb4\xb9\xb5\x19\xd1\x9aU4(\xbd92\xec~&qa\x96\xd7\xb0\x81%M\xc4\x03w\x7f#\x98\xda\xdb[\xf9)\x89q\xc3:\xb2vA\xb3\x01p?U\xc5\x0d\x83\x83jI\x91\xd2U\x11\x87q\x84U\xa4\xde*Y\xd9\x8e\x83\xd8\x8a\xf6Y\x98U>y\x02+z\x96\xaa(E\x90\xac\x7fj\xb6%\xb8\xe3\xfa8\xe7$\x7f\x19%\x19\xc9rq\xc6\xbcN\x93%\xed\xf2\x18\xa6\xaeZ\xb4Y\xa6\x9d\xfc\x12\xf4\xfeT\x1b\x97^\x82 \xca\x0b\x99I\xba\x84\x13y\x18\xc2\x9c\xfb\x87\xd5\x81\xd8\xe8\x1c\xfd\x86vLt\xb2\xabsa=\xfb:\x91Z\xc6\x98\xcc\xd6\xce\x0e\xba\xf2T\xcf%7\xba\xf2Y\x07\xa7\xc3V\x98T\xdc\x11V\xf7\xa4\xaa\xfb#\xae\x13\xd4\x8f\xda\xd6\xce.\xb6\n'\xf5\xb7\x86v\x8e\xca@\xfcl\xc5\xe4b\xc5\xe01!\xf7\xdd\x08\x7f\xa9P\x1b\x84W) \xe8\x96\xadvl\xc3nD\x14\xe1KC!ub\xf9]\xafe\xd3\nf&L\xe7\xd1\xb2\xe9\xc9Y\x1b.\xdd/E\x14\x19\x8d\xa5\xf5<\xf8\x02\x9f\xaa\x04\xa4\xdc\xc5\xea\xb0\xac\xbeR\xce{\xe6\x1d9\x06k\xe4\xedy{\x96\xaeMM\xc0\xe6\xab+\x86\x01\xe8\xdf\x13q^~+);\xd0\x19\xe0N\xac/a<\xa5|}J\xb2$\xba!,\xf7Z\x9ca\xae)z#D\xc8\x1ff\xf4n\x95\x92i\x18\xf89a\x9f\xacR\x92\x91\x18\xcbq\xf3\xffs\x9e\xec\x8de}{\x1e\x85~F2\xeb\xb2I.O\xac,\xf0#?\xc5\xb2\xe4\xd7\x82\xc4\x01~\xb7\xf4W\xab0\x9e[\x97\x1d\x92\x11#y\xe5\x82__ \xe1\x8c\xe5\xb9\xc8\x85'\xac\xcc\xe1\xe6}\xc3\xb4\xd3Z\xb6x\xd8 \x0f\x9d\xc1?\xcc\xd0w\xb7b\x1bS\xfb\x87\xcf\xf1\x978\xb9\x8d\x81\xa9.\xc0\xfa\x81\x13\xa8?X\x10f\xb0$9%\x80\x90KD\x03oHf\xac\x0cae\xfe\xf6\xfc\xdd[\\\x04\xde\x0f\xcaju\\\xc8\x17a\xe6\xe5\xfe\x9c\xae8~G'\x0f7:\xfe\xe0\xf1\xed\xf9;>\xa1\xf8Z\xfc\xbe\xbf7\x8b\x96@b\xd3\x15\xb3\x07^c\xb9.\x98[Ky'\xd7\xda\xea*\xa1\xad\xb5Z`,\xbctu[\x1fO\xb9\xf4\x18f+\xef\xd4Q\xf35\xc9\xc7-\xee\xea\xa5\xe4\xc5\x8a\x05k\x0f\xeae\xe5\x85\x8c\xec\x1cs\x1e\x95\x9f\x96\x1f\xf8B\x9e%hB\x8c1 \xaf\xb7\xb8\xaf\x08'\x9e\x90\xcb\x9eK\x93^\xfe\xa4d\xc6LR\x9f\xc6\x82\xf2\x1d\x17\xf8\x92\x0e\xab%-\xd6\x95ii\xe3Rc\x0b\xbb\\\x82b\x81W\x165\xf4@\xea\\\xd9\xbdx\xf4\n\x85\x8dvG\x8em\xdd~\xc9\xd4\xf8j\x8c+\x1f\xee\x1b\xd8\xf2\x1d\xc7cR\xdd&s\xaeM\xdc+\x99\xe3\xda\xfd\xfc^\xf8\x02G\x91\xdb\xfd=\xd8\\\xf6\xe6\xd3\xd9\x0f\xc5C\x1f\xf5\xb0cH\x1c\xdbb\xfda\xc6`\x92\xb3\xd4\x83\xe3ey\x82\xa9\x92\xd3>\xb0\xd1#\xfd\\\x0e\x15_\x0f\xdc%\x80\x19\xda\xb1\xbd\xb7\x7f\xa8\x06\xacO\xf8\xab\xa7CG+7\x08\x8dC\xef\x1f\xa3\xde\x10\x9f\xfe\xe1O\xcd_\xe5\xbel\x13\x89\x0bmD\xdb\xc1\x00\x1c\x81\xab\xf6}\x15\x11\xa7\x17\x81)\xce\xf1\xa5\xf0\xae\xfa\xb0\xb3Y\x90\x08\x05S\xb0Gz\xa5,_\x96\xf1}\x88!\xe1\xcc\xef\xfd\x8e`*\xed1\xd8J:\xb5`bH%\xeb\x19\xc1\xbck\x98\xe3\xa6@\xd5u-\xef\x1a\xe3V\x18%[\xb0\xbcj\x94EbHW\x8e\xa4\x9e;G|\x9c\x06\xe6\xb5_`\xb7\x90\xa7\x16\xf3\xb5\x88\x0e\xa0_\xbe\xaf\xee\xa0t\x1b\xe8\x18\x9bIi\xc6\xb2\xf64c\xd0\xb3i\xe0\xcb+\x14(\xd67W\xa7\x1f\x9f\xf6\xa9\xe0\xa1\x1a/\x1f\xd8\xea\xd4\xd0\xcd:\x91\xb7\xd0\xe6\xfayN\x96\xab\x1c\xf2\x04\xa6\x84\x1d\xf5E\xca\xbc\xd9\x84\xbdni`\xa0*\x03\xaa\xcdl\xf7\xa2^%:u\xbf\x1d\xc9\x0f\xf7\xb5H~4\xfc\xbf\x16\xc9K\x07\xa0^\x1c=\xdc\xd3\x82d\xf7\xa9F\x1a\x1d\xdb\x0d!u\xc1\x1e\xab\xa9M\xfaz]\xa3\xf2\xc1\x05f\xbd\xb2\x02\x0c\xe0\x0d\x99\xf7Z\x8f\xaa\xa6e\x81\xbf\xe8\x0b,\xca\x02\xe7\xfa\x027e\x81\x8f\xfa\x02\xcb\xb2\xc0\x0b}\x81yY\xe0g}\x81;8\x81)\x9cB\"\x92.\xd1\x99\xe5\xd9\x97~7e\x11\xbb\xc6h&\xa5\xb6W_\xe8\x8a\xd7\x9c\xc2\x18\x16\xf4/\xcb\xecd\xa7\xbc\x95\xdf\x1f\x9c\xaa\n\x03\x9b\x8f\x9a\x9ei)\"\xca\x1d:1\x98\x9a|\x03\xf3\xe0^)\x11\x8a\xae&\x11\xd3\xb1\x14\xf6\x1d\xaa\x7f\xe8h(\xb1\x1d\xc0)\xbe\x841\xaa\x81\\\xb8c:!\xac[k\xbf\x85\xa5O\xb14\x8caI\xcb\xd1JB{\x86&yc\x98c\x07\xb0\x9a\x13\x98\xc1i\x07c\x00\x12\x83_\xd1\xb8z\x0b?\xf9B\x96n\x11f\xb5x\x1e]\xe2\xd3\x0c\xf3#\x83\xad\xea\xd6\xba\xbe\xa3W\xe0g\x04\x06\xe3\xcerP\xb7\x8f\xd1L\xa1za\xcd\xc3\xf5k\xb6u\xf8\\\xbd\xb0\xf2\xd1c*\xd7\xc60\x92\xaf\x0ea\xb1Z\x996W\x99\xb8\xccu\x95b)f5C\xe7\xdc\xad\x94\xa3\xfa\x1a5\xdau\x90\xc4\xa1\xd5\xfebr\xd9r\xc3\xea\x02\x88\xb3d\xd47\xca\x86\xa8N\x91\x19\xae\xfe\xd7\xfc\x0d\xaa5]\xc0of.\xfb\xcc\xb6\xef\xbc\x1b\x96\x14\x1b7^u\x87\xb8\xc4a[n\xe6r\x8c\xf4\x89~sM\xff\xdb\xb8\xa6\xaf\x9e<\x01\xdf\xbev\x01\xab5\xa7(\xc9\xbc\xd7\xcci;\xf3\xfe\x02'0\xa2?\xce\xe1\x04v\xe9\x8f\x8fp\x02\x87\xf4\xc7\x0bZf\x9f\xfe\xfa\x19N`\x07K}\x86\x13\xd8\xc7b\x9f\xe8\xdb\xd1\xa1[\x93\xb70Q\xfc\xbaR09\xeeT\x85=n\xc3x\x9a\xdc\xd2!\xb1_\xde;\x0c2q\x82ZL8\x15\xef\xc7\x86\xcf3\x12a\x10e\xfaW\xfd\x14\xdf\x8dAL\x84m\x89\xd9^\x84\x99\xe5\xc8\xa6_Zq\xdb\x9c\x8b\xdb\xe6\xdf(n\xeb\xe2\xbc\\~b\x8f\xf6\xd5\xd3\x16\x03\x81\xd1S\x9eE\xcaN\xeb\x9cT\xda\xceI\xa5\xa6e\xa1e\xa0\xda=\x1aPBEx`\xb0\xb0\x96\xd9(w\xb5\xc7\x7fT\x901h\xd4\x83\xa44r\x1ak9\x9b \x89g\xe1\xbch)q\x9b\x86\xb9x[\x1f\"\x86\xa0g\x07r\xec\xd6T\xb1\xd0=wfym \xd1\xd8\xde\xdb\xd9Q\xa6\xa8\x9a\x91Z\x7f\xf4M\xeavH\x8d\xfb\xd4\x8b7\xe3>\xfd\xff\xc6\xb5\xa7\x8e\xeb\x8f_z\xe52j\x17\x15\xd6\x94%\xc3#\xc8\xb5\x860\xb9\xde\x10\xe6F\xcd\xd4\xa0\xb5NoDr\xeb\xb0\xea+\x0dUx\x8072I/\xb9\xf7\x94\x89\xe3\x01\xbd\x89\x00=\xa8\xde\xef\xef\x0d\x06\x07\xec\xfd\xfe\xde\xde\xce\x1e]I\xfc\xd7\x13`\xf2&z\xb7\xaby.*\x1c\x94\x95\x1d\xb2\xe7\xc3a\x95]J\x14\x1a\xee\x96\xa5v\x86\xb5\xcf\x87\xa3\x83\xf2\xd5p\xef\xa9\x03<\xbf\xd63\x18\x0e\x87\xbb\xc3\xe1\xd0a\x97\x04\xd3&T4\xbe\xba!\xcf\x02\x87\x9d6\xa11\x8a\xfe\x18\xc06\xc1\xb6 l\x9d`\xf9}\x07\x9e=\x83\xa1\xca\xbe\x8b\x8b\"\xbf\xbd\xfd\x9d\xd1\x80~5\x1c\x8cv\x10&FM\xaf\xce\xac\xb6I\xf5k\xd1\x9a\xeeS\xad)\xf8\x0dw6\xdd~bO\xfc\xad\xdf\xfe\xe5\x92\xfe?\xd8zz\xf9\xfb\xd0\xdd\x19>8G\xdbs\xc5\xe0\x8dR\xc5\xdb\xff\xf9/\xb6}:\xfe:\xf1\xb7f\xbc\xf0\xe1\xc3\xfd\xa4\xfc\xe98\xdb\xcaW,\xe7\xec\xeep_+\xb4n7\xc5R\xc4\xa5|\x88\x89\x1d\xf0\x14\xcc\x01\xe3\xd0w\xf6PO\x92{\x01\x1f\xf1\xf3\xdc\x1e\xe0\xb2\x88Dx.F\xabc|\xab\xaf\xcc\x946\x9f\x0c/\xeb\xb9\xaf\xe0\x140\x80\xea\x9b8\xb7\xf3\xd2D\xcf\x85\xe1>\xa5h\x1a\xaf\x86\xf4\xd5\x00\xe3\xb4\x16v\x8cD\x8f\x01\xcc+\n\xb8\xc9\x93\xe3g\xd6\xe5v\x1d8S\xe9\xcd\xbc\xfe\xaai\x02B/\xeb\x895\x06\xeb\x89\xbf\\\x1diB#[\xc7\xf86\xca\xb5/\x9f\xe1\xcb\xb9\xf6\xe5\x0f\xd6\x0f\xf4\xe5\xafE\x92\x1f5b\xd15\xa7\xed\xc6\x88S\x16\xb2\x11\xb6\xac-\xe0V\xba=\x84x\x93K\x06a\x86\x1eK\x9a\xc1\x85\xe1:\xfa\xe0\xd6dVR2Lq\x0c\xe6z#c\xb4`\x149H\xf8W\x06\xe6\xbeKum\x0coH/2\x89/y\xe4\x1bm\x19]\x0c\x91\xfa<95Z\xdb\xc5l\xc0=\xd2\xe9q\xa0[\x1368\x8e@.y\x04\xf3V \x11\xff\xb4q<\nSW~\xbe5\xcd\xa9\xeb\xdd\\\xf8xN\xd3\x9fE\xcc\"\x1d\xbek\xcfgWJ\x1e\x84b\xd4\xfa\xe5\x17\xcb\x81c\x18p\xcd\x16)\xe3,\x86.X\x7f\x1eZ\x8e\n\x99\x9f\xfc(\x9c\x9e\xc5y\x98\xdf\xbddf(>}\x81x3\x99\x92\x8fI\x88j\xea\xc2e\x9ajZ\x17\x96\x0eI/A\xb4\xd4\xb5'\x86\x9ee\xae\x9c\x18\x08\xbb\xc5\x06\xff\xd7\x1c\x03\x84w\xb6\xb1\x12I\xd80\"\x83\xa8v\xea\xc2\x8d\x0e\x19\xb51Ak\xc9\xd8\xa5\xa0\xd6U\xe0\xcbS)\xc1;\x8c\xf5\xf2\x98\xae\x1e\x19E\xeb\x0dn\x8f1K\xfb\xeai\xcbD\xeb{\x87Z\xd1\xfa\x81Z \x13\xad\x0fGj-\x8f\x93\xad\xbb\x92\xf4\xdc ^_t\x89\xd7o\xba\xc4\xeb\xcb.\xf1\xfa\xbcK\xbc~\x07'L\xb6\x8d\x923.\xe3f\n\x13!A7\x8a\xbc\xcd\xa2\xf5\xc5\xba\xf2\xf8+8\x81kI\xd8G\xbf\xb9\xae \xff~\xd7\xa5Q\xaaD\xechY)\x89\xd8\xd1+\xd3f\x82v\x14\x91\xdfA]\xd0~\x87\x82\xf6S\xb8\x831\xc4\x0eJ\xd4\xe9\xb1\x8c\xc2\xa5\x00\x8fp!&G\xc9\xb9Q\xa0X\x98\x04\x8aw\x8c\xc4\xb8c\xe2@!2\xfc\xec\xb8\x80\xb2\xc2\x0d\x9ee,\xe4\x02\xc3\x15\x06\x08\x10\x02y\xf1\xd6\xbe\xe2\"G\xa301\xf5\x02\xa6\x9eJ\xdc\xffi\xc1\xa2Y\xf5\xa5*\xb3\xb8\xeak\xa0\xaa\xc4\xf8\x06Uw\"\xdd\xa0\xdb\x96J\x00\x15\x9a}hP=\xdc\xf0\xa8\x01\xdc\xcc&\xc4\x1c\"\xda\x85W``KtM0R\xdf<\xf22*\x95\xed\x82\x85\x11\x15~\xec?\x9c\xa0\xe1\x0coH\n\xba\xec\xbb%\xf9\xe4\xa0U\xcd\x0f\x0e\x8fF\xf6\xactu?\xde.}\"\x9e\x19\x03\xfe\xaegP\xa7\xf1X\x8b\x99\xea3\xb7\x0b\xc7\x85\xd4N\xbd\x8f\xb0 \xa9\xf7\x1a~\x84\xa4=\x02\x83\xe0o,\x0b&\xe4\xd2\xa6c0\x02)gF\x03\n\x05}\x7f\x0f9w\x88\xa3_K\xd9\xe0\xeb\xc3u0 #\xc6O\xae\xb15\xddG\x15\x8e\xba\xeaU\xdc\xc3\xfa$_\x84\x95\xd1\xfa\x83,on\x9a\x19\xd0\xfab:\x0c\xa3\xb4\x1aq\xd5\xc0\x05r\xe3G\x8em\xb1\xc7U\xf5F# \xcd\xb1Y\xc9\xdc\x11\x93\xb1[\x1d\xaf\xf6\x9d\xa4\x905Q\xe3S\xdd\xe6\xfc\xfe\xa2\xc6^\x9e\xb37\"\x19E\xa3\x01\x91xb\xacMT\xb1\x08\xb3SV\x160\xf1\xf0j\xb9\xd0\x84\xe7C\x91\xd89\xf6\xb2\x15 \xceIDh/2\xcd#\xbc\xfb\xb7,i\x15\xf7\x89\xa3\xcc\xf4\xad. \x8e\xb8x\xa7}\xbb\xa0\x0cmi \\\xd7\x1e\xd25\xa8XH\xff\xfe\x80\xb1lb\x9d\xa5\x80|}H\xc3\xb1\xc6\xdeF\\\x0f\x18\xd5\xd3\xd4l\xeeB\xd8\xf7x\x85j0\xe2\xd4\xb8\xf5\xd3\xd8\xb6p\x95\xde\xa6\xfejE\xd21\x04I\x11M\xe3\x1fr\x98\x13\x16\x17\xd4r\xdc\xa6\x9fa\xb3 \xad\x17\x99@dt{\x0c\xfe\xa1\x86\xf4\xcd\x86[\"\xe3\xf2\xcdGiZ\x7f\x15\xaa\x9bO0\xae\xcd\x944\xcc\xf9\xae\xbe\xc9v\xbc\x81g!\x8d\x9fW\x0c\xdan\x17\x13f\xe6\xfe\x0f\x9d.\xeeU\x1d\x15:\xc1\xa7h\xe3\xcf\x08\x91J\xde\x8eqCE\x02l?\xe6\"\xf7\x0d\xc3\x88\x1f-R\x1c\x1d\xa8RBLy\xd1\xe4\xd1d*\xa0\xa4\x06\x18\xda\x96\"\xb2\x887M\x8e*\xa5\xfcb\xd2\xcaQ\xea\xa1\xa7\x0f\xcf$\x8f\xa6\x1f\xaco\xfa\xc4V\x16\xae\xbdL\x03[\x03\x03\xed\xba\"\x0d[s\xa9tx?\xd6\xfc\xb2\xdb\xcc\x7f\xae\x8b\xf9E\x92D2\xb3\xd9\xab}I\x90\xac\xda\xa7\x0b\xab\x1bu1\x84\xdcv[uZ\xf2+k\x80\xfa\x99-\x9f\xb23\xa6\xf1\xdc\x95\xa2\xe6\xd4\x0b\xab\xd1s4\x87\x13\xba\xb4\xa3\xeb1\xda\xe8P\xb4\x8a\xe4Qj\xc7\x8ekN\xdb_\x1e\x0d\xa2\xdaZ\x89\x1a\xe1\xfe\xd0h\xcf\x9a\x93\xdcb\x91j\xe8\x9cg\xe2\xae\xb9I\xad\xe7A@\xb2\x8c\x9e\x7f\x18\xab\xb9X\xd19#S\xd36\xb5\x90d\xe1u3\x86\x8c\x99\x87\x95\x0e)kn\xe4~Vb\x0dw\x84\xb5\xac\xc4\x1e\xd7\xa4\xbab\xbe\xa5\xc9N\xb7a\x83\xcb\x81\xce\x88,\xb6w\xf6v\xb5\x8a\x91}Uz[\xf0\xe2\xaa\xe7\x02J\x9f\xecCu\xafD\xac\xd1]u\xe4L\xf1\xaf\x96\x9ei\\\xadV\x18\xb0\xb3\x0eS\xb4L\x9b\x93\xfcc\x92Dd\xaa\xe6\x87Xh\xe4\x1a7%2)\x1f\x97'\xeb\xb2\xc1\x1d\x9cy\x98\xde\xea\x13 \x928\x08#r\x91\xfaq\xe6\xb3\xd2O\x9e\xc0\x0d0'\xff\xe1h\xc72YOP\xeem\xa2l\xdb8\xccY6\xcfq;\xe3\xc5<]\xc34\xbf+i\xdb\x8ce\x18\xc3\xbc\x18\xecX\xae}\xa5\x88\xa54\x82\xabu\x1a\xd98\xa9\x9a\x81S\xb0g(\xb5\x0d\x08%\x19\xcd\x9f9.\xdc\xdaH\xfe\x95\xdf\x9e\x18\xc3\xb0?\xa8t\xe6z\xc0 \xfc(\xba\xf6\x83/\xff\xbb \x05\xf1R\x92\x91\\\x11{<\x16\"\xf5\x9a\xe3$\x0fgw\xcf\xa3H\xad\xbd\x1a\xc8\xa5nI\xdd5\xe3\xff1\x1f\xe7j\x98\xd2\x9a\xb2\x9d6\xb8\xf2\x95\xebj\xfa\xd7\xd8\x07\xa2\x19\xcd\xba=i[\xd5R%\x1b\x83v\xdb\xa8\xeb6\xe35\xe2]-\x93\"\xce1\x15\x06lA.\xdf\xb7V{\xd5F\xdej\xe1\xa2\x88G\xeb\xab\x96\xc5\xfe\x18\x8ev-\xc4\x9c\xe2\xb9C\x7ffI\x9a\xdb\xd7\x8e\x0b\xab\xcd\xcdz%Ud\xba*\xaca\xce\xa3\x1a6\xd7\x0b\x17tR\x04:\x9b\xc4\x06\x0fQ\x1f\xe7\xe8jE\xe2i\x18\xcf_\xf2\xd9\xcb\x9a\x0c\x1c\xba\x156\x0b\x96\xb3_xQ2\xbfHVo\xc9\x0d\x89>a\x88'c\xa0\xa3\x1b\x1e\xbd\xd6\x90\x9e(\xf4\xae\x82\"MI\x9cs\xc6\x0c\xf3\x89c\x9e\x03?\xc8E\x1b?3\x16\x0b\x8f\xe4\x88\x8d\xa2\x11g\xcba\n\x03\x8be\x03,VS?',\xb8WD\x97\xd4{\x7fI\xe8\xaa\x14\x0c\\\x1e.\x89\x9dt\x19\xab\x00\x87F\xe6\xadH:K\xd2\xe5g\xac\xf7\xcd\xec=\xa1\x84\x85\x9f\xde\xd9\xa1\x8bF\x0d\xcd\x85\xcct\xa7 *n\xa5F\xcf\xe2)\x8b\x0c\xae\xe7>{D\xbe#\nf \xf1\xaf\xf4\xaf\xedO\x82K\x97\xef\xc2\xe2:\n\x03\x11\xb8\xc6V}>\xfe\xd4\xfc\x95\xd8\xb2\xdf\x19D*R\x9c\x93\\\x1a\x1b\x9f\x90\xac\x03\x8d\xf1\xad8oC\x87\xc2-4I\xfb\xe0\xc4v\xb4\x14z)\x89\x88\x9f\x11\xbb\x89\xa0\x1c\x03\xd6b_\xb6!\xa4Z\x9d\xba\x99\xee@v]\xa1\x86\xf8\xd2\xea&\xb6\xa1\x02i$\x16$\xcf\xd1\x89>M\xc6N\x88\xc2-E\\\xd0\x93\xe2\xd5R\xa1k\xd6\xf3\xa7S\x8a\x9c\xc3x~\x91\xd8w\x8a8\xef\xb6M\xcc\xc9\xa3\x0b\x95h\xf1\xfe\x1e\x16\xc6(Y\xb3\x0e\xb7:\xa1\x88\xbb\x93\x8f\x1c=\x86!b\xf0\xf6\x95HKO\xd7\xc2]9\xad\xba\xd4v\xdaN\x19{\xc3\xa8<}\xf3\xe2\xe4\xd0\x04\xb5\x03-\xfd\x08\xb9|\xd4\xd7\xd6tWG\x8d\x82\xa4\xb3\x06/`\\\xed,2V}\x81^Sn\x8cL\x19\xee\xcb\x9a\xeb\xb4\xcc\x17\xd3\xb2`\x97t,7^\xbd\xaaf\x05m\xfb\x84\xe3\xb9\xcf\x1c\xb5\x97\xe75\xd1\xdbP\xf2\x16\xc3\xec\x05m3\x8c\xe7\xbcQFFb\xa0\x81\x9c\x0b\xe8PZ\xe0]\xb1C\x03\x8b\xbfGm\x08\x17Ji^\x9c`N\xbc!\xd2\x98\xdaQ\xb5\x8ed\x16\x15\xd9\xe2\x85\x02\xd5[\x85\x19\x8a)G\xceT\xca\xcd\xe5\x88/\xf5\xf3g\x16\xb1\x88\x8b\x94L\xc3\xbe\xe5\xb4\xe2>\xbd\xb6\xb0I^\xb0\xfe\x08@\x9f\xe7\xa9\x9f\x93\xf9\xddz}9\xa0}\xd1gOQ\x00\\\x92T\x87\xf8\xc95\xdd:\xbe\xf2Es\xda\xc5GO\xe9G7\xfa\x91\xb5M\x9a\x9f\xf9\xab\x1e\xa9T\x03[\xb3\xe6\\N\x97\xf0[\x8f\xd5\xf5\xd2\x8f\x7f\xc8\xc5\xb2\x06?\xc6&@\x1cP\x10\xc6\xe0c\xe8E\xf25\x87\xdb\x05II\xc1\x87\xe2c\x08\x85\x1c\xaeI\x18\xcf\xc5\xf6\xf4\xe8\xb8\xa6%5\x80\xfds\x19n2\xb2>z\x81\xd6\x19>]C\xce\xb0\x11\xdb{C\xc7l\xb4\xc3q\xc0\x01\x9d!\xbd*\xe9\xf7\x07\x17,\xbf\xa1B\x02FytP\x06r\x13]s\xeaxU\x9c\x8c\x87G\xa84\xc5\xd3.O9\xcc~@\xc1\xf2T\x17\x1f\x07_\x8d\x86\xea\xab\xd0\x14h\xa2\xd4b\xa0\xcd_\x861!\xe4\xf7\xa5\xf6\xa4\xd3[^\xc8tUSWz=@\xd7\x8e\x95\xf5\x0b\xdd\x1d%U|\xaf$\xe5Q\xcf\xe4\xd7,\xe2i\xa9\xa0\xa9\xcc*O\xab1\x8e\x0d]]\xcf\x83\xe8\xbb*D\xc4/\xd9;\xb1\x1b\x18\xd2\xac\x9d@hW\xfa\xae\xd6)\xe3\xfd\x97\xc3JR\xe8H\x86\x00c\xd4\x03U\xddk\x9d\xc3\x7f\xc4\xfc\xad\xd1\xf7\xc7oG\xb3\xd4\x93\xb3\x97J\xc4O}S&\xfc\xd6 \xd0\x9a^Bgx\xfe=\xc6( T\x0d\x86\xe6\xaa\x84\x94\x0bTu\xf2T;\xb6\x9f:.L\xaci\x98\xad\xe8\x01\xf2\x12=\xa9-\x17\xac\xab\xdcOylVz\x1b\xfbyx\xc3\xfc+1\x96c\xf6\x8a\xcd\xf7\xc7\x94\xd0gd\xca\x9eRT\xee\xcf\xd1\x08\xee\xa5\xa94B\x1f\xca\xdd%j\xd8p\xdf\x18K\xdb\x10\x1d\xad4\xfb\xd3ft\x03\\\xd4\xa7\xd8i\x96\x01\x8e{\xe3Y\x0c\x00\xec`\xf0y \x8f=D\xc5\xecX\xfa&\x9e\xf8\x9a\xdc!\x0d\xe8\x08Y\x1d\xe6B\xf5\xd4Y\x87S\xdd\xc31l\xb08\x8e1\xb7\xde\xfb\xa9i\xbc(i\x84\xbd&\"\x80\x13\xa0\xdcU\xd8\xb0\x9aR\xf6\x1bZY\x89\xc8\x9d\x1a\xc4\x81<\xb1\xbe\xfc\x9f\x9acN\xedL\x96\\\xd5\xa7l\xc5\xfa\xf6J\x9c\xea=$L\xcdAmh&\\H \xd4\xd5\xda,\xc9t\xd5\xc4\xabw\x05}\xa1\xea\x8fl\x87\xd9\xf8a\x88\xcc:7#M\x08\xafM~r\x02h\xadf\x9e\x95\xc6\x8c\xb4r\xa7Y\x9e\xac\xa4I\xe9\x00\xda\xfa\x80P\xeaGH(\xcfZ@\xc1\xb0\xea\x0bD\xbd\xbc\xc2\xda\xa3\x13\xa6\x80\xee\xbd\xb8:\xc1\xb1\"i\x86\x99\xc4\xbb\xd7N\x98}d\x85\x19\xdaj\xb4\xd3\xd6\x8c\xfc\xadv\xbf\xd4J\xf7\x96\x9a\xd6\xa6\xa7\x07\xae\x84z\x0c\x0d\x96\xd1\x0c\xf1\x0f\xd3\x84k\xa3\xd3\xeb\x94\x15\x95\xd0\x9aebB\x146\x89//\xb5\x12\xd1j_;.dU\xe7\x98kc\xe6\xf9\xc5|I\xe2\xfce\xe4g\xbd\x1dNd\xb8\xa8\xbe'5\x1f.\x84\x8d!b\xda\x0d\x8fn\x10\x93[\xf5\x18J\x99\xec\xbf\xfc\xd0\xa9\xdda\"\x16\xf9A\x9d\x98\x06\x8c\xa6.\x8f3E&\x18\xfbR>f<\x9e\x8b\x98\xa4\x19\x908H\xa6a<\xafgD\xc8\x17$\xc6\x8d\x87\xc9\xd2\xca\xc3\x0fD\xe0\x17\x1fx\x03\x06e\xb88c\xb9\xc1@/\xd57\xffF\x18\x19\x18\xcc\x04\xf4S\x13\xb5\x88\x85\xc0\x0cCC\x8c\x9b\x1f\x84}n}\xdc<\x9b\xa6\x0f\xac\xa2\x16gp\xbd\x03\x1d\xae\xdb\x17\x0c\xdb=y\x82LO\xb9\x1e\xe4w\xcdC\xbe\x85P\xc3\xd0>\xde\xf5]N\xde\xf2l\xdd1FWA\xcf\xf3\xea1\x1cWv\xcb\xeaV\xfd!\x99\xcd2\x92\xff@\x97@R\xe4\x90\xcc\xe0:)\xe2if\x9a]\xb5MZ9l\x82\x8d\xb6\xfd\x03\xc7\xd8\x0e\xdbs\xfd\xdb\xc9\xeb\x99\xd1\x99!juO!\xd5@\nuE\x80\xae\x08n\xe0\xb1\xee1\x05\xb3\xbe'\xad\x88)oCD\xb4\x00\xcf|\xd8\xbaU4J\xe2\xda\xec\x8f\xf5\xde,\xdd\x04\xa1\xb84\x9f#@\xcb\xe8\x0e\xf7\xf7\xcc\xed\xde*\xf2\xd9a\xdb\xd4od^\x98\x9dq\xbca\xc7\x8ei\x13 \xd4bIh\x83\x1d\n\xac+%\xee\xd1\xed$\x90\xce\xd3\x01\xdc\xc3\x82M\x9c\xde\xe2\x10\xf8\xe1\x8a\xd3\x81\xc7V\xea8\xdem\x1a\xe63/HX\xa7\xdcL\x8d\xe1\x98\x11\x91\x84rZ$\xb9)\x1bUJi\x08\xfag\xf3\x04\x86t`\x18\xbax\xb4\xb7\x07O \x9f\xa4\x1a=\xd7Z#\xd4$^\x85r\xdd<;\xa1\xbc\x95\x89jy^e\x96\xf1#\x0c\xbfB\xf8\xce\x82\xc8O\xe7\x842\xa8~\x0cK\xffk\xb8,\x96\x90\xa1;\xc7\xe0+\xe5\xb3}9\xcd\xf5p\xdfAWNJ6i)\x9e\x12a\xdf\xf7\x1c\xd4\xa2u%J'\x8b\x9c;JH\xcb\xf5\xdb\xb4\x0f\x92\xd6\xdasHe\xbc0\xfb)$,\xd0H\xf31\x9d\x88\xfb{ \x06\x14/\xf7\xb4\"0\x9b\xbd\xd5\xb8\xd6W\x8c\x9e\xa5\x13r\x80\xb4\x9c\xdb\xa1\xc0\xa9\xcd\xb2'\x9a\xedU[\xbe\x1b\xc3\xa3#\xa7\x14\x0d\x1bOB\x14\x88Z~\x16\x84\xa1\xa5\x17\x8b\xb2\x12\x91\x9f\x87\xf1\xb0\xb5\xc8u\x18\xfb\xe9\x9d\xa1\x08H\x12(\xfdq\xc2*A2\xaf\xad\x95\"\x9fm\xb5\x96`\x84vg/^\xdb\xc41\x02\x1c\xaa\xe6\x82l\xd4\xde\x9f \xdb\xea(\x91\xcf\x86\xfb\x11\xe9*\xb3\xd5R\x08\xaa~\x8f\xe0\xc7v\x08.\xc8\xd7\xeeZbx\xf6\xec\x19\x18\xac\xb6\xf9t\xfa\x19\xd9\xdf\xed\xae\xea\xb7.@\n\xa32cE\xa8\xedpzO\x0cp&\xcc\xc6\x1d\x95;\xf5\xe8f.\xcf\x8f\xd6\xf8T\x95\xbe\xeb\xd1\xd7M\x1b\xc7\"\xf6\x16\xd1F\xc6\xe7riz\xfc\xb9\xe2\x10L{5\xba\x94\x98*\x83\xc6\xa1B\x01\xa4\xa4\x189\xc0\xb64\xd3h\x10\xb7\xc4\x94;L\x99\xf0\x1cOn\xe49\xe1\x99,\x91;\xc575\x11\x1d=\xdd\xb7\xca'\x87 b\xa1I\xcf\x1cV\xe1f\xecB\x98\xbd\xf7\xdf\xdb\xb1S\x16K\xf8\xe1\\\xca\xb7\xb6`\xe8\x08\x91\x80(T\xbe\xdcDZ?\xa6\x07 \xe9p\x84@\xcb\x95V8\x00\x8f\xfe$7\xdd\\\x19@\xa2\x8c`m1\xa3\xd7\xcc\xcdm\xf4k\xafk\xf9A\x8bH\x8c\xd9\xdd#\xcf>K\x93%\xe5\x15S\x07\x15\xc35\xae\xac\xc6J\xe5\x15\xfb\xb45\x841\xcc\x95\x15eX!Z\xe1\x13\xaf8\x87'H\xeb\xb8\x069\x83\xe9\xd0\xad\xc4\x17\x92\xf6\x97\xc7\xd9\xc5\x08\xa4\xa7\xadE*\xf5\x04\xe7Z\xb5\x85#?\xcb\xdf\x18>\xc0\xb1O\xf2\xcb\xb6\xd1ky\x97\x1b?* {\xc1\xae0\x08Q\xce\x843Z\xfd\xe8q\x15\xfe\x06d\x12\xb2\xf0l\x86\xd8o\x85\xb4p\xf5%2\x89\n\xd6O\xb1\x14\\\x95\x89\x14\xd8\x89\xc6\xf8\xef\xb4\x8a\xc6\x99*h\x14\xe9!~\xb8q\xa1\x15>\xe0gY\xfd\xd1\x96\xf4\xcc(/@\xb2\xb6\xa2\xd8GL\x18X\xddw\xee+\x9fEO-`\x9bEQ\xe5\x7fc\xfc\xab\xd9o\x8dG\x8a`\xd6\xd4Q\xde\x8dai\x92FX\x00{\xe2\xa5\xc4\x9f~~\x13\xe7\xc3\xfd\x17gv\x0e?\xea\xdc\x18\xf5\xfb\xdc\xa8E\x16\xce\x8e\xa6A#M\x87j\x98#\x08\xe1\x18\x8a#\x0877\xf5L\x19\xf0\xc6px\xa1\x83\xfdG\xad4OQ\x1cp<\x1c\xc2\x16\x04\xadr\x1dQS\xf9!]9\xb4\x9b\xa1\xe3\xb2\xcfa\x93\x03(+\xe7-\xa0\x001V\xc9\x91\xec\x16K\"\xc1j\x0ca\xeb\x84\xf7\xc6\xe5P0 g3lb\xd8\x84\x0c\x9eAQ\x9e$\x05lA\xe60\x7f`\x84\xda3d\xe6\xc2\xad\xad\xb6!\x97\xc4\xf3\x8c\x07\x0b\\1\x1ep\x05\xc7\x90\x1d\xc1\xaa\x0d\xe8P\x03[{>\x1cCz\x04\x9b\x9b~\x1b\xfa\xa0\xc7\x84\x9c\xf7\xa2\xb8\xce\xf2\xd4\xa6|\x82\xef\x02O\x8d\xa1_X8H\xa4\xd6\x8a\x8a\xa0\xf0\xf5e\xc9\x84\xee4f\xba\xdb\x03\xe9\x89\xcaz-\x9a\xeb\x8eE\xc3+{a\xbf\xa6\x1bJ^\x16\x0e\xaa\xe4\x9a&@\xa6\x96\xae\xfa\xb6d6\x18(\xeb\x94smM.]Y\x14V\xb2\xf2L\"\x963\x87K&8\"r\x02\x94\xb8C\xa2\xafK\xa8\x98\xaf;\xe8\xdb~\x83\xae\xc1\xa6W\xc5g\xfd*~a\xff\xb6~\xa7\xbf\xf6\xad\xbb\x97V\xa3\x92W\x96\xde\xb6|\xd6\xa4\xadF\xa4\xa0\x15\x1b\xb6\x9d\xd3\xd3i\x84i!\x1c\xbe \x19+!\xcd\x9f\xcf\xf9M\xcaO\xc3!\x8f\xdaL\xd1\xc6\xde\xbe\x0b!\x9b\xf6\xc4)\x7f\x9a4yF\x94\xfc\xf0\xad\x0b\xfe\xbc\x8d\x9f\xad\xb3\x10t\xd8q\x8d\xc5\x84SH\x91\x07yq\x97\x13\x91\xf1\x9dbU\xf5!WQ\xe5u\x9b\xae\xb6~\xbdl\xeb\x17\x05\xf3;?_x\xcb0.i\xc6\x1e\"[:\x9f\xe8\x1aq\x04 \x8an\xdb\xd0&\xa5\xbd]\xb4\xafu1F\x07\x99$-\xc9\xe5\x03\x11,\xc1X\x82\x9e\xe0\x11e\xa5w\x9e\xc2)\xec\xc2\x98\xdd\x8dv\xe0\x14v\xf8\xdd\xf0\xe9\x10Na\x04c\x93\xe8\x05iE\xd8\x84\x19\x1c\xa3\xb0O\xc8\xeffm4D\x9f\x04\xb8\x11\x1c\xc3ptX\x12rQ\x8b^ \x04\x9da.\xd2'-.m\x8er\x19\xc3\xa7#x\xc2\x88X2\xa1\x83\x1b^:L8@\xd9\x17{g\x08O r\xe0\xf8\x18\xf6\xe1\x1e\xf6w\xe0 %^\x9f\x89\x0cb\xd8\xdd\xec;t\xd7`\xf6).\xb9\x7f<3>\xde\x8d.]e(!\xf6\xbe\xfe\xcc\x97F4\xdc+G4\x1c\xc1=\xd8bL\xf2\x10}:\xc4\xd1`\xf7\x80\x7fw\xcc\x13\x96\xdd\xdf#9+%x\xfb^\xe3\xdf}\xfc\xf8\x8b\xf2ng\x0dh\xd4\x9f\x15\x06\x08\x1d*\x10\x92@\xe6\xd7AV8\"\xef\x1b\xad\x89\x82\x8c\xa5\x92\x1bI`\xd2\x0eQO\x12\x97\xc6X\x94/\xc2\xcfi\xdd;.\xee\xe4!\xc5s\x81\xdc\x9e\x1d\x94i\xe4\\H\x19>\x0f\x98\x18u\x00O\x00\xf3\xc5\xdd\xb3I\xe4\xdc\x0c\xcb%w\x0f<\x95\x1cer\xc4w\x18\x1bg\xf3\x04fM\x8co\xc2\xd2\xdd\x14\xc9M\x19\xa7\xa9M|\x8a\x8aq\x8a^\xbe\x94$\x9f&\x1d\x1d\xb71>\xe7b\x10\x9d\xde\x02$\xdd\x85\xa5\xc9V&\xaeT\xaf\x0c\x04(\xc3\xa2\xa4\xa8=\xa4\xc7\xeb\xe6I\x9f\xce\xf0\xe3&u\x99j\xeeK\x07\x11\x157\x81l7\x8eO\xf9.\xf7\xb8b\xe9\x84\x1e\x0e\xb9w\x1e%\xb7\xe5\x93\xf6y\xd8$U\x84N\x82\x12V\x0dC\xc0\xba\x95y\xa8\xba\xb37\x1b\x1e8\x90{o\xde\x9f\x7f<{yq\xf5\xee\xf9\xffw\xf5\xe2o\x17g\xe7t=\x0dL\xb2\xb8\x139\x89\x0e1\x98\x05\xe9\x9fwy\xf6\x18\x83\xdf\x0b\xdf\x1a\xc5di\xd8a\xa2R\xb3J2\x9fie)\xbd\x00\xb0\xe5\x18N\x92\x1e\x01\x13\xc4\xc5{\xb5\xdb\x94\x1f\x89K\x8f;\x1e\\\xd8\x1dqZi\x96$\xb6c\x14\x87\x12\xca\x901K\xd3'O\x84'x\xf9\xcc\x1eb\xc2\xbcJ\xa9\xd8\\\xaa\x9d\xd9\x0d\xf8\x1864\xb2\x93\xfa\xbab\xf1u\xbe\xbc\xf3\xbf\x96\x91\xa3|\x1b\x05\xcb\xab$\x89\xce\xc3\xdf\xe8t\x1e\x0e\x9fb\xf2\xa1+\xeea\xd3\xb9\xe2\xb5\x13[sJT=\xbf\xb8`\xbb\x87\x1f\x8cT\x7fd\xf3\xf0EZ\x0b\xcc\x16!\xb5\xec Y\xeb\xa3v]\xd1\x91k\xcb\xb8\x06\xfb\xc9st\xf5\xa7\x0d\xb1_\x18\x1cJ+!\x13\xdetY\xa9Xa_hmM\x98\xe1K\xdd\xd5\xad\xcd\xccAV\xec16\x08\x02ZGc\xdf\xd43\xd0\xc9\xb5\xd5\\j\xb5\xd0B\x0c\x933\x0c\xd2\"\xd5\xa5\xbc\x07\x99\xc4\x97FvK\xc8\xa5j\xc7\x83\xad\xcb\xb3\x0f\xdcV\xdc\x84\xee\xcc\xbd0\x13\xe7>7F1\xb3\x812\n\xf7\xff\xa0\xf9\xa3\x97\xcf\x8c\xb9Q\x13\xce\x19_\xe1 \xdf\xb1\x16\xa1Z\xb7is\x91J\xce\x1e'\xb0p\xa1F\xe9I\xc7\xe7\xc6\xa0\xfe.\xbb\xf5W\xc3\xfd\xb6x\x9d\xa0\x06\x0fh\xd3\x13\x11\xad\x9eH6\xd7\xe4=\xc9(\x89]\x99\x0e/\x8b(\x0fW\x11\xa1\x10\x1c\xeeo]\x87\xb9\xf6X\xac)\x1a\x06Gh\xbeK\x8e\xd8\xf2\x1b9p#\xe2\x9f\xba\x98\xb4R\xc7\x7f e\x82\x1cB\x04\x04\x10\xeb`\xd9\x19}W\xb0\xec~#XvF\x8f\x02\xcbn\x03,;\x8e[=\xa2`b\x7ftZ\xb85\xa0\xb5\xbf\xfb]\xa1u\xf8\x8d\xd0\xda\xdf}\x14\xb4\x0e\x1b\xd0:\xd0Ck_y\x9d\xe8\xda\xf9\x83F0\xcc\xe6LX}a\xfc\x16x&\x8f\xa7\xf2(\xb1\xfa\xd5\x8b~S\xb1Z\x890\x90\x90\x1f\xa2\x19\x1e.\xba>M\xa0\xd9(\x96>>\xa1\xbd\xe5w\x9d\x1f\xe3\xeac \xa4\x89\xe4\xcc%\x19(\x1b\xa5\x1b\xd0\x83\xee\x14\x17\xef\xc5\xc7j1\x9b\x9c\xac\xa0\x0f\xb5\n\xbd(Vq\xf1\xc6_\xae\xd3x\x1b\x9d+.^\xef\xf3u\xeam\xa5\x8e\xa1\x1f\x85,.\xde\xfe\x87u\xda\xef\xb4\x1d\x86\xaa\xe2\xf3u*n\xa1\xc6\xa1\x17E\x0e=\xa9rX\x872\x87j4\x17\xfdF\xd3I\xac\x03\x94v\xd1Z\xc6\xfa3\x8b\x0eUz+\x8e\xb51\x14\xd4\x8b0w\xc4M\xb0\xac\xbef\xd3\xa0\xa5\xc9\x1eD\x0c\x12\x1c\xac)\x0cI\x1d\xa9\x93_\x0b?j\x8f\x1f\x01ZiC\x87lA:\x0c\x85\x8df\xeb\xc1\xc3\xcf\x80\xfb{\x8e,KY\x88\xde/\\\x19E\x18g+L+\xd6\xefd2)F\x98\xffRC\xca\xdf\xdaqq>=\xe3f\xd3%]Q\xba\xf3 \x8e\xe4\xfe\x92\xde\xd2\xcf\x83\x85\xbd\xed\xfd>z\xd8\x9e;\xde\xdf\x930\xb6-\xb0Dx\xb0\xb22\x9e\xec\x89\xa5P\xf7<\x0f,\xc7q\xc1:\xe6\xf4\x06\xae+]6\xf4:\\\x0c\xf2\xa4N\xa3\xf6\xef?\xd5*\x8fW;YU\xcfmf{\x8e\xda\x11\x0e\x90\xb1Z.-\xed\xb6\x94\x17\xcc\xd6,i\x9c\xa8\xb9\xf0u\xa7'pY\xef\xfd=\np\x06,\xd5\x9cr4\xeb)>\xee\x8f\x9e\xd2G\x80\xf6\xd1\xa6\xf1\xa6\xf0\x8c\xf7'\xa7\xbfZ\xdd\x84\xaa\xf2\x9d.\x04Je\xe6RH\x07\xb8\x10\x97\xbf\xd2\xf2WR\xfe\xaa6_/\xf1^\x88\xae\x03[t\xf5`\x0e,\xd8\xa2\xcb\xa9\x90%z\xa1\x0b\xbe\xc3\xcc7\x10\x9c\xa5^0\xe1*\xd8\x9ae\n\xd3\xec\x0e\x8e`\xc6\x0ci77gf `4\x991 `0\x99\xb5J\x00i7ia\xd6KZ\xda\x8c\x83\x1f!\x01\x0c\xe1\x18\x8d\x90Q\x02\xe8\xc31\x84f \xa0\x8c\xa5\x82\xa8\x98\x92>\xb1\xc6\xa4\xb6\xb8q.\x82\x92\x9b\xe3\xdbf z\xd3\xba\x7f\xad\xc6\x96\xf5\x90\x1a\x98:\xaf\xad\x11\xc9\xe4\xff[\x1b\x1a\xb66\x84\x1e\xfaz\x0cf=\xbdp\xdf\xd4E\x10\x86\x1cm}\xa5\x10?X\xac\x0f\xda0@\\X\"\xe2\x87\x984\xd99\xba\xa8\xf1\xe5\x1f\x1a\x03\x03\xa9\x91\xfe\xd4\xd8t\xa6\xeacz&IB\x07s\x1c\xcc)\xf9\n\xb2x\xa1'D\xff\xde\xc1\x0c\xe5\xa5O\x7f\xce\xed\xa9\xf7p\xc2\xf5z\xc9\xda\xeeU\xadud\xaf\x17\x17Fu\xc3\x1d\xee\x8e\x96\\\x02\xea!\x9e`P\x9e\xe3c8\x84\x1f)\xfd{\n \x8ca\x08[\x908\x0e\xdahk^\xf4\x1a\xf0\xfb\xb5\x06\xbc;z\xba\xfbt\xff`\xf4\xf4;\x8dz\xd7<\xea\xbc9\xac\x1d\x1c\x16\x03F\xaf\xc1}\xea\xbd?\xbeea\x99\x96j\x0b>y\xf4\xfa|U\x1bQ[J\xc6\x90\xeeB\x04\xc0\xc0e\xa0v!\xe1<\xae\\\xc7h\x87\xbd\xa3\x10\xd8\xed\xd5\x87\xb7\x8f\xee\xc3\xa1\xa1\x0f{#\xf6\x8e\xf6\xe1P\xe9\x83|\x97\xa9t]\x1f\xfb\x1d\xe1\x15\xd7OI}\x02\xff\xfd\xdf\xc4U\x83`\xe6p\x8a\xa9Z\xfe\xfb\xbfs\x97\x9d\x14,\x0c\xe5&=\xb5\xcb\x1dBD\xc4\x11B\x0f\xf6\xf2Q\xeaT!\xc9\xec\\\xf9&\x17\xdf\xe4\xe57\xb9\xf4\x0d)\x9f\x10\xc7`\x03\xecT:\xcf\xd2\xea\x1aaa\x0c\x90\xb9\x96\xfc\xa4\xa4\xc0`K\x8d\xcb/\xae\xb8\x0c\xf3\x9b\x08q\x86\x81\xbb\xa81\xe7\x9cNH8\x19\x13S\"\x80\x0d\x04)\x00\xd2\x95\n\x07\xaa\x85V\xf7\x80P\xd8\x0f\x11\xd5\xe0\xedYO\xb9\x1a\xe1\x92\x19!\xb8A\xaaM\x90\x13\xb2|\xa3\x05\xf7\x89\xe56!\xdcgoX\x12G\x9b\x9bt\xd89\x17\xae\xffxB\xe9\x1e\xe7\x88\x13\xb5\xec\x1b\xd8\x84\xf0\x12~\xd4\xb9v\xebIY\xfd\x88_\xfccF\x0c\x9b\xb0\xb5\x95\x8bq\x1f\xe1\xd2\x1et\x0c\x97~\xf0\xed\x03>\xec\x83\x10\x84\xc6\xa9\x1c\xe3\xd0U\x15\x1cl\xe2\xfa\xb48\xdco.\xab^\x8d\x8e\x0c\x8drK\x0f\x04\xca\xf0\x12\xcf\xfc~\xfdhN\xf6\xb7\xf5\x03\xa9\x8dZg\xfa\xf4cg\xf4Hx\xec\xaa\xfd\xb0\xcd\x00\x91\x1f\x8d\xf0\x11\x8b\xf37\xdc?88\x18\x0d)\x17Q\xbe\xdf\xe9\xd9\xedG\x82\xaf\xd1\xedF\x1f(gc+#\x18\xee7\x87P\x1b\xd5\xcee\xab\x08\x9fv\xfb\xff:\x8c\x06\xcfN\xf8\xe7\xc3\xd1\xa1\xc3E\xe1[\x9cv\\%\xb76\xa5\x12(X\x1d\xc7\xedF\x07\xff\x10\xf4W\x03\x8c\x84\xdb\xd2\xcb#$/\x9bX0T\xb0`\xda\x0e\xa4P\x03\xa4\xd0\x08\xa4\xb0\x07\x90\xbe\x13\xcaD\xdf\xebr\xc5\xa3:\xefG\xc0\x88\x10[\xd2>@\xaf\xd3\x9e\xd8u\x0d\xe4j\xc4fM8\xde\x88\xd8\xaaF\xe4b\x84\xfd\xce\xe8`\x9f\x0e2\x86S\xc6\x08\x0d\x86\x07\xfb\x03\xb8\x87\x18\xc6\xdd\x14\xc8\x1a8\xfa\xd1\xc3a\x83\xb8\xaf\xa1\xf0?n8\xdf\x0f\xd5\xaf\x87\xe9\xebx\x92>\x1b\xed\xf6\xean?\xe8\xf7\xef.\xb6\xdc\xect\x0f\xe4\xde\xd5\xdd\xd7Q\xe2k\xb0\xfb\xe3\xba\x9b`\x95\x95\xa2ac \xb8\xbe^\xdd\xf8^Pktc\xd8\xb7\x1b\xaf\x92\xe2:\"\x8f\x04\xc7ag?\x06\x82\x01\xed\xd7\x8fG\xc2\xa3\xbb\x1f\xc3>\xfd@\xe6\xd9\xc8\xcd\x18\x848\xc8\x86n\x92\xda\x01\xc7\xacXPm\xfbF5 P\x0f\x93\xd8\x81-\x8a\xf2M\x8e(\x899\xc6_\xd8\xe2\xf4\x81\x1b\"\xafBN\x13AI\xc4\x8dc\x92\x15eD\xc4 \x10\xd8\x86\x84\xc9\x81\x8c\xe8\x8d\x16n\xc5b%$\xb5d\xc2?\x10\x921\x161BSc\xa4$AS\x88\xcfJ\x88nm%\x18 \x8e\x93\n\x1a\x90&\x02\xa4\xe1w\x03i\x83\xa8h\xb7`\xd1\x00U\x85%E\x16{{.\xeaQ\x8c\xf9~pv\x10\xe4\xb3(IP\xd2\xcd\xb1\xb5\xbc\xca\xb8\xc9\x7f\xaf\x81\xe8(\x90o\x1e\xcb\xc8e\x92\xe3\xb6\xd1\x9cj\xb6\x87[\xcd\xd9\x90\xcd\x19\x8aH)M\xf5\xf7Z\x03,G*=|z\x0e\xb27\xa5\xfc\x07\x0e\x92\x8fF\x1d$\x1f\xbbf\x90\xc3\xb5\x06\xa9\xa3V\xbey\x90\xbb\xae$\x12\xef5RF\xb3\x88\xd1\x8ev\xa5\xe1\x8e\xaa\xe7\xc3}\xc3\\k\x963\x85\xcc{\xfd\xf4\xb7\x92E\x12d\xfe\x80\xe9_\x1f2\x06\xa8\x0c\x0dP\x19\xe9\xd7\xccN;d\x86\xbd!\xb3\xe6\x11+\xa4\xc72X6\x8c\x06G\x02\xd57\x8e\x07\x0c\x1d\xad\x97\x9d6\xce\x96\x84\x1d%[\x1a7o\xbd=\x18\x9e\xc5\xfa\x83\xa5#J\xef#Op_:n\x88\x10y3\x89z\xc1~\nsLv\xb6\xd3\x01]\xe2\x97\x05\x86(r\x95s\xdf\xa6\xa7\x94\x0f\xcf\x9e\xc1\x80\x9e\xa3\xc5w9\xaf\xd6\xa4\x00\xfeO\x99\xe8\x16*\xe2\x9b&[\xcc\x85D`\x84\x15\x81\xb1\xf6\x8co\xfecf\xfc\x0f!P\x86\xa3\x03\x17\xb6\x86\xa3\xc3\xb5i\x14R\xd3!Q\xd02\x9f\x84\xe1\xb7\xd0/\x7f \xf9\xb23:\xd8\xa7cE\x19B?\xd4\xfe\x07\xd20\x7f \xf3\x88\x81\xfe\x81t\xcc\x1fH\xc6T\xf9\x10\\%\xedA\x8f!\xb7\xcfm\x0f\x12\xa7F\x12}\x13A\xf3\x07\xd23f\x10\xd5\xb7o\xcdHB\xec\xe2\x1eP\xfc'\"~\x0c\xf2\xa7v(\xbeR\xe6\xac\xcb\xab\xa2ji\xdd\xf9RZ\x1a\xf6j\xc9$Ejo\xea\xedc\x06e\x12\x14\xad\xd5T\xe7\xa8\x82du\xb7\x1e\xddR\xa5\x9b\x1c\xa0Cd\xe9\"X\xd9\xd5\xe7\x8a\xa7\x97\x94\xa5\xa42E\x90\x0b\xd0\x0f\xf3\xb2F\xae\xe2HK\x12\x10\x9d\x17\x98\xf7eWz\xa7\xb0\x11 \xa5\xea\xa0\xdc\xad\x8e*\xf26\xc3\x9b\xdcO\xe7$?\xcf\xfd4\xef\xce\x86Z\x9a\xf1\x003\xd6T\xba\xa1o!K\x8a4 k\xb4\x90\xb6\xf5\x97\xd5v\x16O\xbb\xebJ\xeb\xce\x17%\xf4\xeb3*\xd9_\xe5\x18{iK\x9a\xa8\xda\xcbM\xadU.\x12\xb4L\xbf\x95\xea\xe3\xd6\xe3\x1cTn\xa8\x18t\x99+\x07\xb1\xc5\x96\x904 \xb0t \xc3#HxV\x83\xad-4\x0bK`\x13\x10I\"\xae\xa3w\xba\xb8/\xa5\x93\x11eA\x86d\x07X\x18\xaf\xf5\xb2\xfe\xb105\x8aY\xda\x1a\xedk\xf3\xb9d$\xaf\xf2\xb8\xd4Lubf\xf6\x14:\xfa\\\x98B\xef\xd7\x86\x08fa\x14\xad\x87\x084NWkg\xb6\x16\xe9 0\xa4\x06?6\x95\x1d\xa2M\x9f+\xe1\x85\xe6'.\xcf\xba\xd1\x95\x19 $\xde\xaa\x16\xb0\xdcdy\x04\x18\x80\xe8\x18m\x8c\xc5Am\x88\x8ff\xce\xb7\xaa&\x9b\xd1\xe4\xc33\xf9\xb3\x97\x19\xbf\xfb&\xf36\x80\x1d\xdb\xad\xe7\x02NM^\xc5&\xcf\x8fF{\x95\x12`:-\xc9\x9b)\xcb-\xe2T\xe9\x17a9\x00n\xab\x87>\xca\xb5A\x08\xbc\xe8OB\xf8_P\xaca\xb3\x977b\xe4\xd4\xfb@\x07\xfb\x19N`{\xf2\x9f\x9b\xbfl\x0f\xb6\x9e>\xdf\xfa\x0f\x7f\xeb\xb7\xad\xab\xcb\xed\xb9\xc9\xf5\xe6\xd7\xf6\x10\xae\x80\xca\xd9S\xb0\x06\xe8\xf4_O\x13:V\x1e\xd4\xfbfh\xf0\xb5Q\x01x\xa3\x0f\xd0\x96\x03\x8f\x8a3\x84\xed\xce\x1c\x97\x95\x83L\"\xc2\xf3\xeb\xf2:\xb4\xa7P Y`\x9bFb\x07\x07\x9ea4\xef=qD\xef\x1d\xec\xec\xee\xb6!\xdc\x90\xe7\x873\x97\x80r\x93>\x83\xbd\xfd\x9d\xe1\xd3\xae\xc2\xf4b\x89(vh\x7f\xb6\x86\xb43<\x99\xc4h\xe7\xa9\x0b\xc3\xa7C\x17\x86\x87O[\xd0\xba\xb8\x82$\xce\xc3\xb8\xd0\xe7R\x12\x979{\x10\xf0\xbe\xfb R?\x19\xa5z\xf2\xf5O\xd4{\\$\xed-u\xb6\xd2\x9e] \x97\xc9\xfe\xce\xc8\x98BP\\\xfd\xa0\xe2\xfe\xc1]\x8e\xb9\x8f\xc6>lR\xban\x8b\xa7 8>\x86!3t\xd9\xe2\xa3\xd1\xd6\xc0O\xc5\x84\xf3==\xc6c>\xc9\xab\xfd\x1b\xb3D\x15]\xfb\x8c58d\xd9Y\xba\xd2\x1f\xf0\xce\xc4\xad\xe3\x10\xf37\x1a\xec\xf6l}\xb4^\xeb\xf0\xec\x19\xe62\xc0\x00\xdb\x98\xd0 \xa6w\xa3\xc3^\xdd\xc2y\xea\xd7\xaf\x9d\xf5\xfb\x85I\x17F\xa3]\x16\xc2\x03\xf6\xe1 \xed!\xf6n\x8d\xbev\xa0F\x1c\x07O\xd9\xa0\x8b3 \xd2i\x05\xc9\x94\xc0*1x\x91\xc9U\xb2\xf1\xee>b\xbc\x87t\xbc\xbb\xe4\xeb*I\xf3\x0cN\xe0\xf7\x07\x89v,\xc1\x106<\xd2\x1b\x9b7#\xf9E\xb8$I\x91\xc3\xc2g~\xa0\xd7\x84\xc4 B\xe6W\xf0~\xd04\xe0w7\x10D\xc4O\xbf\xa1\x89\xa2\xb9\xe0\x19n\xc5\x18`e\xef\xab\xe8\xc2\xe5#\n>\x95o\x16T\xe3\xc9 \xf3\xe2\xda`\xf9\x8e5\xf5\xd0C\xb6z\xecv\xd4\xab\xcf\xb7!\xaab_\xd4\x97\x81\xc8\x0f\xa17\x955\xa6\xef\x10U\xb2\xa5SF\xcb\xd79\xfc\xb7\xb6\xd0\xac\xab\x94\xd2v\x07\x0f\xa8&l\xa3Z\xac\x8d\x95\xa0\x1d\x03f\x9d\x11\xdf\xc8\xbc\xa6\xb4\x10O\xe5\x9b\xb1\x8av[\x13k\xd0\xeaU4-\xdf\x19\xe6\xc9\xd4\xa9\xda\xe2=\xad\xdf\x8e\xd5,\x89\xad\x1d\xa3M\xa8Y\x15\xcb_\xb6\xb4\x9a\xe8\x1e\xe7\xa9\xcd&Jb\xb3\x00C\xbf\xd4\x9f\xcdx\x12\xda\xe6\xc6Y5f\x04\xb3\xb7b\x1a\x0b\x9bW\x05\xa5X\xe0\x14[\x14\x01\xc4\xed\x08\xc3\xa7b\xdd.D\x92\xecuj;\xed\xfbu\xdah\x16\x89\x88\xc0\xc4L\xd2\xb3\xad\xb0W\x1a\x8a\x01\xfb\xd8\xc6KR\xa6S\xf4\xed\x083\x11\xe9\xd79~@\xb1d$\xe0\x8aA\xc4x\xf6\"\x9e\xf2cv\xe9\xa5El\x9b<\xfc8(\xe4&;v \xf0D\xcfl\x8f\xea\xe6N\\\xfd\x8ev&T\xa7\x98K^\x86U\x1a_\xe9\xa1\xdd\x16P\x12Q \xab\xc8G\x14\xc8b5h+\xa5\xabV~\xe1\xf6o\xc6\x8c\xc2\xc4\x95\xda\x06\xf9\x12\xf4\xc2^\xe2\xean\x08d\xf2K\xc6\x9b\xe6\xe6a\xad.@\xa3\x01\x8eL;\x1a0\x8f^\xfb\xe6A\x05\xd8C\xebN\\h\x858(\x0b\x9c\x15(9\xe1B{\x96\xe6\xe8D\xcaZ\xaa\xab\xee\x86n\xec\xaa\xc5\xc4\x8b\xc9\xd7\xfc\"\x0c\xbe\xb4\x12\xa7b\x9fR\x8a\x80\xd1\xbc\x8d\xb8\xcdM\x93!\x94W\xa8\xc5\x9e\xc1\xb0 \xce\x12\x17\xc4\xcc'\x93\xb2*\xea\x97G\x10onRr-f\x86XR\xe8\xe8F\x98\xfd\x883\x1b\xe4V\x80\x0fe\xf7\x98\x15Z\xa2\x07\x03\xfa_aO%T\xe8\xc2B\xb6\xabG\x00\x9b\xcfF> <\x1c+[\x8e\xd5\\\xd4\xaaM\xbc<\xcc#\x0cJz\x9d&\xb7\x19I-\xfa\x90\xff\xe6a\xf2\x13\x8f\xc47H\x07\xd2\xdf~:\xbf\x11y5\xbd\x1b\x92ft\xfeX$\x93\xf2>+K\xe3\xbb\x1b\xfcn:}\x1bf9\x89\xb1\xde\x1b\xf6\x12\xdd\xd1\xd9\xef\xd9L\xfcL\xc92\xb9!ja\xf6\xf4y\x14\x89\x17\x99xC\x96a.~\xafR\xb2\"q\xa3%\xfe\xf8C\x1c4\xea\x8d\xa4\xea\xccK\x8d\xef\xc0\xc9e\x1dz\xd7a\xdc\x99\\\xa5A\xb5\xae\xd2$ YV~\xccC\xa4HA\xf1\xea\x8d\x04\xb7\xd3\xb6\xf9\x16\xac\xd2\xb6\xa5|\xb6\x98\x86\xe9\xe3z\xc6>\xed\xeaW\xb1\xf4\xb3/=z6\x90\xb6>h\xb8\x10E\xc5o\x15\x19AEO\x90KL\x9c\xcc\x90\x98G\x84\x1a\xa0\x8a\xd8\xda\x90Uu:}\x0f\x06\xb1\x15\x03\xf5\xcb\x8aU\x19C\x83k|\xc4@\x9aH/\xd5\xe2\xd0\xca\xbe\xe6\xa4\x0bk&f\x94\xd8\xc0p\xc7'0\xa4\x88E\xd2\xdeT\x98jx\xc9\x835\xc8\x8f\x9a\xf4DlLx+duZ\xb0\x19\xd7\x07\xa8\xc2{\xb5\xd7Lt\xcfP{\xea\xa8\x02|\x9fb\xdep\xe2\xd7\xb1\xaeof\x961\x17\xd6\x86\x88\xa2\x19\x0b\xd0 \xc3&\x91\xa1\xa1GnHzW\xcb\"\xdd\x95\xda\x0c\x19\xb7x\x92^j\xf8\x1bts\xb1\x19W\xcdp2\x9b\x04\x17B\xc7a:\xb5\xd05s\xf2Z\xde\xbb1\xf15\xc2\xb5 \xc7\xb8\x84cN\x0f;8\xc5\xe0\x14C\x1e\xd98e\x07\x1c\xcb\xb9 )\x85k3\xa9\x9d\xe4-\xa0\x16\x97\x00]\xfb\xa6\xef\x03}6\xc4Y\x9a,[Yv;4\xcc\xc3\x83\xf1\xb8\x8f\xbc\x94dE\x94\xbf.\xe2\x80\xae%\x17\x9f\x04\xc9rU\xe4~\xce\xd9\x94\xce\xcd&6Z\xe3\xe5\x03\xab/#\xf9\xa7GWJgH[q\xed\xa1L\x0c\x88_\xb9wuE\xb2w\xc9\xb4@\xf6\x8d\xf2i\x98:\xd6/\xa2\xfc\x1dY&,soB\x9f\"\xda$\x02\x8b\xbedH\x94\x11\x1d\xe5\xcb<-\x82\xbcH\xc9\xb4D\xb6}\x18\xefGP\x99\xbeBe6\x99s+\xc1<\xb8F\xea]\xc8\xfeM\x1dg\x87C\x06\xb30\xcd\xf2*^\";\x18\xfc\x18X\xf5p\xbb )\x01\xe2\x07\x0bX\xf1\\\xbb\x94\x11\xf0A\x9c%\x9a\xa3\xc3Gk\xb0\xb2SG\x0d\xa0\xd0\xbd\xc6\xd3\xf8~!wYC\x88UR\x8bq\x1dU\xb5\xf9\xc3\xd3\x0dY_\x0e\x8e\xdb\x93\xe4\"Z\x84\x9cW\x08\x81\xd3~\x03F\xfb\x11N\xfb\xe5\x93\xb4\x9d\xee\x03i(^J\xa6E@l\x85\x13\xea\"\x98\xc9\x84R\xcb\x97\xcc\x18R\xa3\x8es\xe1\xf7\x07E %\xb1\x9fu\x91\xb6\x8f\x04L}\x99\xd3\xf5m'z\xb5\x97\xc2\xa7 \xee#\xb6\x87\xc3\x03\xe5@D\xc6\xc6\x1e\xed\xee8zV4\xb6\x87\x83\x01\xa5\xfc\xda\x1a\x00Y\x84'\xd2'$6Z\xabK\x83\xea\x91TLZ\x12\xcc\x18tM\x96\xb4\x1a\xea\xc1\xaeaD\xed\xcc\xf5\x86\x1c\x0b\xd5\xc4G\x8b=\xb6\xf1H>Z\xedq\xac*$\xeb\xfb\x8e\xc9\x9c\xc6`\x8d\xbc=o\xcf\xd2\xad\x12\x8d\xfd\xe1\xd5\x153\xd4\xa4\x7fO\x84\xdb@o\xf0\x8d\x0e\x0e\xd6\x86\x9f\xcc\x85\xca)\xe7j\xb2\xeau\xa7Q\xbf`\xf7\x0ev\x95\xe7!\x7f\xbe\xa7<\xa7{\xc7\x9ap\x9c\xf8\xbe\x88\xa2K%Tx!\x17\xf8,\xd2\x9d\xab\xa524n?E\x13\x04f\x0fx\xe1\xcf\xcb\xcc\xde\xdf\x01R\xd2\x89Bo\x0b\xcc|2\xe6\n\x16\x08c\x8ev\x99q'\nF\xc6\xc8&?\x16\xb0{OGz\xc8>\xdd\xeb\x9cx\x0d\xbd,\x96q\xc2\xdej\xb7E\xca\xb2\\\xc4%\xd8\x1e\xdb\xf7\xd1Su\x96Y\xdf\xf7w\xd41\xb1Uqp\xd89$\xc3\x0c\x85\x0c\xde)\x83w\xb26\xbc\xf5\xb2> !\xef\x0e4#\x91NXJl\xb4\x93\xd4\x82V\x99h\xce0\x89s c\xa42\x84U\x98\xf9\xbc\xab\xbdx0\xc0\xad>\x96\x90\x1f\x14\xfbR\xb5\xa1\x17\xc6\x0b\x92\x86\xfc\x149\x1c:\xcd3-\xb6w\x06\xeaL\x16\xac\xae\xda*\xac\xea\xb2g.\xf8\xd2\x9br\x80\x19\xae\xbd\xa2\xd2\"\xf0\x14I\x83#\x88\xe0\x18*uFD \x80\xe6\xda\xa5\x04t6\x89\x14\x18\xce\xaa\xfa&\xc1%\x8a\xb9\x94G\x94)\x93\x1f\xb4\xebwg\x86C\x879\xc7\x88@\xda\xc9\x0cfU~IJ\x12\xce\x1a\x84\x96_W\x95\xb9P\xa8\x0f\x10\xfbo\x08\xd7\x89\x94\xf8S\xff:\xe2\xb1c\x17aV=9a^\x80\xf5\xf2\xb7i\x98\xd7\xcb\x97Oxy\xa6q\x89\xa2\xe4\xf6\xaf~4\xfb\xb0\"1'\xd3\xeb\x15\xd5K\x94\xb55>,\xabL\xe2\x80\xd8\x16\x89\xa7\x96\x0b\xabvp6\xb5\xf4\x9a\xba\x85\xc3\xc1\x95\x18\xc0y\xee\xe7\xc4#\xf1\x94L\xe9\xcb\xb4\xd4\xc5\xd9S\xd6\x85.\x1d}c\x0e\xb16[E\x0d\xf4\xe2;\x99\x1d*\x1f9\x19.\xaf!\x17,\xd1\xaf\xbf\x86\xf3\xc5\xcf~N\xd2w~\xfa\xc5r\xd56\xe2bIRZn\xdc\xd0\x85\xcfI>n\xa7\x98\xc5\xe6\xd6\x00b!7[\xdf\xfc\xd5\x80\x1c\xb7\xd7P\xa6$\xcb\xd3\xe4\x8eL\x1b\xdd\xef\xddE\xc9\x9f\x86\xf5V\xacS\xec-]@\x8d\x12\xb5\xf1TK\xac\xfe\xa5W\xf6\x0d\xbd\xce4\x80(\x0b(d\xb9B\x08\xd4\x06\xa2\xc7\xc8\x7f\xfc\x10*\xfd\xb3i\x10\xb4\x88Q\xe1M\x19,I\xe1z\xc5\xbf\xea:\xe4\xb1Av\x80\x14Q$6,\xae}W\xdeGyM{\xff]\x0e\xca\x9d\xe1\xc8\xb1\x1f{\x8a\x93\xca=\xabT\x91t\xd1\xe8k\xf6o\xff@w\x90\xb3\x10\xf7\xfe\xd7G\xf6;\xb1\x07.\xd2\x1e\xdf\x00\xccu\xcbk\xa9\x94\xa1flvl\x1f:]\xf2\xbe\x90;~z\xe2l\xfb\x98$\xc2\x16\xc0\xc4@\x0b\x82\xa6\xf9\x1d*8\xf4\xb2;\x19\xc1 \xc3Pz\n6\x05\xd6F\x0bez\xd0\xd2\xef\x1b\x86\"\x1a\x9a\xb2}\xd4D>\xca\xf1h\xa7\xe7\x8cm\x8d\xf6,t\xb7\xc5\xedVP.\xde\x16\x9bH\x03\x1f8\xe6\x1b.I\xa2\xf3\xf07R\xe2\xad:L\xe8vl\xa4o\xad\xdd\xfa((\xab=*\x1a\\&\x16\x9cNi\x9d\x94\xb9I\xc6\xed\xa8@\\%\xfb\xda:-q\xad\xcf\xdc\xba\"\xf6\xe6$\xa7\xf7\x88\xac\xd0\x01\xca\xa7O\xcb\xf1\xa2czu{\x02\xc3\x81C\x0b\xa4$\"~F\x98\x84\xaf)\xa1}\xd0\xa8oc\"\xd2\xa9b\x83\xe9X\x05\x08\xbd\xf2\xdbD-\xd5\x0b\x06\x8fY\xe4 \xeb\xa6\xd6Y\xe8\xa0[\xec1\x8b\x10\xe0\xe8\xc0\x01\xda5\x0f\xbauO\xab\xe8\x03\xce|\x91\x92\x06@\xbbD;\xe2\xfa\x16h\xa5\xdf\x05Zi\x19G\xa9\x114Z\\\xfd\x01\xd6\x88\xc8\x00z\x98\xcd\x92\"\xed\x02Y\x8bT\xf1[\xa0\x96|\x17\xa8%R\xf4\xa9\xd4Q\xf5\xf9\xe2Z\x0bp\xae\xd6\xf1\xb8\x8e\xca\xf4Gg\x81O\xdb\xe4ju\x03\x7fmq\xb3\x98tO\x95.%\xfcy\xb7l\xc4p\x94\xa7v\xb2\xfe9.\xf7\xe8\xd1-s\xb9\xd1#\xc8\x08\x89\xfa\xda\xd1\xcb\x8a\x0e\xb5\xe2\x96\xe1P}\xce\x98\xfd\xe1\xfe\x81c[Y\x1aX\x1a\x9e\xff5\xefH)_k\xca\xdfX\xfe\xc1\xc2\xf1\xb2U\x14\xe6\xb6%J\xcaR\xd8\xd8\xde\x1f8\"a\xf99F\xca\xe8\x03$\xce=\x93\x9a\x05\x98m\x94~\xe1\xda-tr\x84\xc8d\x0d\xafx4FH\xe4\x87\x14s[\xb1\xbf$\x16\x1a\xd1$\xd5=7\x9fDIxi\xd2cK\x9f\xf9\xd5\x17>/\x87\xf2\xd6M\xf6{\x0c\x19\xb3H\xe0\xde\xcb\xb9\xe3\xb0\xa8b,\xb6\xcbi)c\x871\x14\xe2\xb6\xf64\xa9\xd6\xc4\x18\xec)\x89HN\xf0\xbd+\xbd\x92\xd7\x94c\x97\x93(3\x85\xe54\xb5hu\xf84h!\x87\x04\x14\xa7}&>Ja$a\x87\xdc\xfeZH\xa1sM\x94z:9\xf4\xc1\xa9\xc4A\xc0\xb8\xcb^\xa5\xd76\xeb\xa4\xbe\xf5\x9bo\xb4o\x10\x81\xef\xeckw\xdf\xde\xaeJ\xc53Q\xdb\x81Z<\xe3\xc5UYj\xc4\x9f\xab\x12\xbb\x80?W\xeb\x99\xf1\xe7*2X\xa1\xd0\x8ci\xb3\xce\"B\x0f\xc4z\x81\xa9T\xe0\xb5O\xc9\xe4\xbbz\x81\x05+\x10%\xb1\xbe\x82\x1b8\x81\xb4\xfeh\xd9I\xb47t7\xd0<\xc8\xe7Z\xb2\xf9\xe5\"\x8c\xa6)\x89\xc7\x86sx\xe9\xaf\xc6\x10zK\x7f\xd5$\x0b\x80 1\xcf\xfc`A\xcb\xf0\x9f\xfarAR\xc49-\x85?\xf4e\xf2\x045\x9f\xb4\x14\xff\xa9/\x97\xc4\xd1\xdd\x18f\x8dw\x1a\xca\xe5e\xb2\\%1\xa1M'^y\xd3,\xf7\xb1HI\xadl\xedA\xb3|m\x05\x8cA\x03\x1cy\x86\xc7\xa0\x81J\x98\xfd\xe4G\xe1\xb4,Rx\xf5'\x9aN\xa6\xc9\xea\x82\x99De\xa6.\xbd\x8c\xfc,\x1bC`z\xcf\xd7\xe4\x18\xa6\xa6\x12\xef\xc2\xafa<\x86e\xf3\xfd\xab\x0f\xef\xc6\xe07\x9f\x97J>\x8d\xf1\xe9\xd5U\xb6J\x89?\x1d\xc3M}q\xea)\x829>\xfdc\x90Nc\x93\x87L\x12\xf0\x94\xb2\x1e\xf6h\x7f\xbf\x12\x14V\xe2\xa5\x85\x9f}\xb8\x8d\x85\xc8P\x8b\x9cF\xfb\xaa\x9eO\xcf\xa1~!wc\xd8\xd0XA\xa6d\xa6\x7fqu\x95\x91\xc8\xfc\x0e)\x84\xb1\x9a\xbeX\xeb\x10\x9a\x19O\nI\x9cG\xbc\x94T\xbbJ'?\x8e\xfaU\xf3\x85\xdcI\xd5\x88_BU\xa1\xe1\x1cX2C\x03Y\xd2\xd4*\xd3\xeb\xcf\x7ff'\x96vE\xe6\x98^\x994_\xe0\x1ch\xb6\x16NA\xdc|\xbeJ\x93U6\x86B\x03\xff\xe46\xa6|PhZ\xd6P\x01\xa7\x8a\x0b#\xbd\x0f\xea\xc7\x88\x060:`\xa4\xcc\xd0\xfaw\x1d\x97\x06&\x0b\xf0\x15\xe8,\xc0\xd1\x9b\x96\x11\x04:\xde\x19\xd5S)\x84t\xf1\xe4,3\xcf\nm9R2s\\\x88\xc4\xc3\x19:\x98\xc0&\xa0\xd2\xcfqky\x06=\xb6\x84\x05\xe91.\x9f4\x8b1z\xb7^\x10\x9f!\x1d\x14\x96\x921\xe6\xb5\xb6Q([\xd3\xe6\x99\x87}f\x1f\x93OR5\xe3.\x05\xdfTg\x18\xb5\x05\xa3&d\x98\x0eh\xea\x80\xef\x05\xfc\x8c\x84Fl\x8f2\xe2\xc3\x14\xbd\x944\xcb\xb4T\xf2-J\xc3\x9e)\x85\x11S\xef\xdd\xc01L\x8f\xe0fs\xd3\x81\xc5\xe4\xa6n\xd8s\x83\x811\x9b\\\xee\xc0\xad\xf7\xa9\xee\x8f\xf8\xd0\x18 \n\xdf\x88\xb0?\xa3\xf0\xcat=\xa5\x9d\\\xa21\x87\\\xb2\xd9|\xb5.\x96N\xcd\x96\x8c\x02^\x9a\x81e\xc3\xe0\xfeA\xb77\x02\xba\xdag.\xac0\xa9&z4\x05E\x9a\xd2\x03\x10\xfc\x1aK\x13\xd4\xc9\xaa^Fp\xca&C\xb7\x9e\xd2 P\xbbWs\x8f\"\x0f\xae\xa4P\x9a\xa7G\xfa\xf3x\xfa\x89\xc5F\xf8w\xd2\xa9t\xa8\xc6\xe81\x86\"w\x19\x96\xa5\x7f\xf8>\xa0?\xf8:'\x1e\xc3*\xf4\x17b\x1eu\xfc\x12M\xd1\x13_\xf8\x0c\xb8\x94\xa8\xb4\x7f\x7f\xa8*n\" \xd4\xba\xd0-\xdc|\xb5\x00~8h\xce~\x0cj\xdd2\x16\x8d\x87_\x17\xd2\xf1kHg!\x90\x0e\xdb5\xe5\xf2\x90q\xd0T\xc5A\x0c\xdel\xe1\xe39.\xaf\xe9\x12mi\xde9\n\xb6\xf1\x0d\xd8\x86=\xb7e$F\xf9\xbb\xba~\x8c\xe2\xbd\x15\xf3\x81\x99\xd1?cqG\xcbj\xb0\xd3rM\xec\xb4t`\xd5\x07;-;\xb1\xd3\xbc\xc4NK\xc7\x85;\x86\x9d\xee\xe0\x18\x96GpG\xb1\xd3|rW\xc7Nw\x06\xecT\xeb\xd0\xbc\xd7\xfe\xe7{c\xea\xc2B \x81\x9b\xba\xfe\x9c.\xfe:u\xfch&\xb8\xa6Gc\x0bD\x90\x12\x0c\x8d\xc9\xad\xca\xa4i\xf0'\xe8&M%\xb1\xd3\x81\xe3\x9d\xdf-\xaf\x93HO\xe9\xa6\xebU7:\xd4\x9b\x0d\x0d\x0f\xbf\xcd\xd6m\x83C!\xa9\x0c\xd0q\xc1\x7f\x8b\xdd\xdb\xc8 \x81|\xaa\xaa\x19\x19\xd3\xbf\xdf\xb0#bt\xf5\xfe\xb0sdf\x94+E\x12\xe4f]p\n\x13r\x89\x96g\xfe\xb7\xc8\x131\x1e~cxJ\xf8\xbb~\x13\x11\x1aB\x972\x95\x1b\xa9\xechH\x13W`\xe0b\xd8lD\xe1\x11k\x7f\xc0j\xa4\x93I\xfbF\xe8\xddV\x02\xa7`m\x0d,J_u\x8c\xbf\xc6p\xe9$E\x9cUb\xe7+F\x1c\xea9C\xc4\xcb\x8a\x15I\xaf\xb8yq\xc5lU\xd6c\xacR;\x97eqM\xec\x15$\xb1\xd0E\x9a\xc4\x17\x98\x98_\xcb @\x87]\x8a\xb8\x84\x89\x82\x9e\x0b\x03\xd6\x8dY8/D=\x1a\x9f\x81\xda\x93\x87\xbaU\xf1\xa3\xc0\xd6\\\x0e\xaa\xd7\xb9\xc2\x88\xc45(\xd7\xe0Z\x9f\x80\x98\xdc\xa2\xe9r-.w f\xf8\xfe\xb6\x07\xfb\x9d\x9b\\\xb7kj\xa6\xceJ\x98\xd8\x97~\x1c'9\xd0\x86\x11\xc5%)\x14q\x19sH\xbb[\xbe\xcb\xa0\x1a^\x1f\xcaxyt@\xfb\xa0\x81@P\x10\x91b\x04_\xba_S\xb9\"\xe6\xfb\xdb\\\xdd\x9ch\x19\xab\x99c\xe5\xfe\xf02\x9d\xd0\xec\xe3\xc9\xf4\x87x.\x89\x93\xa8>\x04\xdd\x0c\xd9\x03\x17B1 g\xed\xc3\xa9\xe7\x8c\xb9\x06\xa0\xb5\x18\x0d\xab;M\xf2\x99\x16f\xab\x18\xff\xf7\xc3\x8cr\xa8\x98X\xe6\xfe\xbeK\xceT\xc6\xd6\xe6Lm\xccX*\xd2dj\x1b\x10|\x048\xca\xc7\xa5\x9c'\xed\x92\xf30S\xef\xfb{a\x06\xde\xc4\x0b \xefg/\xcc\xde'\xf9\x82EcH\xdd\xda\x0b\x06\x8a>\x04K7=W\xf5An\x83\x0b\x93\xfb4\xa1\xee\x04NBpjbB\xc9\x079\xd5o\xad\x99\x94\xac\x88\xdfo\xdd0\xcf\x1e\xf5\xe8\xc6\xa5\x133\xda;f^\xd61lb\xd4L\xccP\x85\xc5\\\xefL\xcf\xc1\xe6F\xf4[e\x81\x1a\xcby1\x18/\x8c\x83\xa8\x98\x12\xa1\x95\xe9p\x1fG\xef\xe0\xb2\xad\xda\xeb\x07\xae\xc9\xed[S\xb3\\\x9bEM\xee\xe5\xfe\x9c\x9b[\xd3_O\x9eP\x1e>\xa4\x8b\x88\x89\x92\xe9O<\x13M!a\x1f\xd0\xaeJkJ\x86ofa\x94\x93\xd4n]\x91PAn\x8b\xc7J.\xb1v\xaeV*\xad\x93\xe6\x84i\xa2\x16r\xf3\x15\x9c\x0e\x14:\x88\xdf\xf7\xf7hK\xc6\xde/WQ\x18\x84,\x1dIy#\x97 _\xa5\x12\xe5\x8d\xae\x8e\x9e3\x85\xb2A/J\xfc\xe9\xbfs [Y\xe0G~jq1\xbex%\xd3Y\x89m]\xa0s&\xbac\xc6I\xbc\xc5\xbeA\x84LO\xbc|A\xa0\xec\x7f\x14f\x18\x07\xdf\x87,X\x90\xa5\xef\xc1\x1b\xf1*%Y\x12\xdd\xd0\x13!\x99AV\x04\x0b\xe6\xed\xdf\x08l\xe3Y\xcdIe\x86=\xc9r\x15Fd\xfa\xa6\x82\x9c\xcf]\x08,\xd1\x01\xcb\x85\xc9\xa5\xfa\xc1\xd9\xd7\xe6\x07\x02\x9e\xda\x0f(m\xf9\xce_)\x14v\x03\x9etK\xf2\x1d\xa4\xd5X\xd0\x8b\x01k\xac\x95\xdf\xe3{\xf2kA\xe2\x80\x98K,\xfd\xd5\ns\x1f\x98\n\xcc\xfc(\xba\xf6\x83/c9h\x97\xb8\x1e\x94H\xf3\xd0q\xea\x8b+\x9e\xb0\xadx9\xc1m\x8af\x16\x9eh\xa9z\xa6\xf1\x15m6GQ9a\xa8\\\xe7\xa7|\x84q\xed\xf3#\x16,v\xe8H2'R!!U\xae\x08Fj\xd2\xd6\xae\x16\xc3\x9aP\xc9Jz\x15\xde\xab\xb3\xd7\xcf?\xbf\xbd\x10\xfa\x95R\xc1\xdf\xb6\"\xc4j\xa8w3\xbb\x0d1\xb2\x9c:h\x1d\xdc\x03?#0\x1ck\xe7\x03\x83'\x8a~)p\x9c\x0c\x0c1\x02\x0c\xf1\x96\xb1\x9d\x91\xb9\x1d\xb9b\xb5)\xd5G\\\\\x86\xa6\x04\xd3\xa2\xfd\xa6\x86d~N\x93x\x0e\xcc3\x141\x88h\x12\xd7\xcf9\xc3&|\x16J\xe9D\x9b\xba!\xe4y.SA\x0e\xa2\x83u^{\x92;.l\x90^\xf1_\xc49+[K\x17\n\xa2R\xf0\xe6\xf9\x8a\x04\xe1,$\xd3\x12-\"C\xcfQc\x06v\x92RD\x19\xc6\xf3\x88\xf0\x11r_]\x07\x83\xc6\xfba,pn\xed\xad\xa72\xb5k\x84\xb1\xd1\x0d#\\w\x18\x7f{\xfe\xee-\xc7\xde\xb51P\xbci\x1a\x81\xf4\xae\xd1\x7f\xb1\x8f\xc9-\x14\xb6\xe6\xdcb\xc7\xa7V\xaa#\xf0\xf8X\xf5\x05\xac \x93\xbb\xad1\xd7$\xf6\x86\xc3\x9a\x19\xdf\xa1\x96\x96K\xda\xe4\x956\x81'\xf4\xa5\x1aXLn+\xd4\x1e+\xef>\x9f_\\}>?\xbb\xfa\xf8\xe9\xc3\xc7\xb3O\x17\x7f\x1b\xeb\x92\xa1\xfe\xf5\xf9\xf9\xd5\x8b\x0f\x1f\xde\x9e=\x7f\x7f\xf5\xd3\xf3\xb7\x9f\xcf\xc6\xb0\xab/\xf5\xfe\xf3\xbb\xb3Oo^\x8aR\x87\xfaR\x1f?\x9c\xbfA\xd6@)>2\xd4\xfa\xe1\xa7\xb3Oo?<\x7fu\xf6J\xed\xc6\xce\xa8\xf9E\x18\xd3\x85\xf1\xea\xc3;\xc1\x10\xbfD\x19[\x97\xf3\x12H\xb2\xd1P\x7f:\x02'v\x89\xc7\xab\x0e z8\x98NS\xe0\xe2h\xe2\xbd\xfa\xf0\xeey\x9e\xa7\xe1u\x91\x93\xf7\xfe\x92d+?\xe8\xfe6\xd3\x7f\xdb\xf5Y$>\x13\x00\xe8\xf5U \xbez\xc7\xe3\x9d\xbc#\xf9\"\x99\xf2\xef\xf4\x98\xba\x94W\xccP^\xe1\x85\xd9\xcb\"\xcb\x93e\xd9_J\x18\x16\xdeU\xe3\xb9\xb0\x97\xe4^U\x9a/\x9d\x16\xba\x1f\xf0`]\x95s\xa0\xea\xd7fL\x12f[\xbb\x87\x96\x0b\xb3\x16co\xdaw\xa4\xcd\xbc&Y\x98\x877\xc4X\xa7\x1e\xcb\xf5\xab\xfc\xc3\x0dI)\x07E\xa6\xc6\xe1\x9b\x90b\x93\xc9\x95/\xc3F\x06~\xf2/<\x05\xe2\xb0 \xf8L\x1e\xa5x\xa6\xefd\x19*(\xb5\xad\xbd\x01\xee?\x174[\xb4ms\x03\xdf\x9a7\xe8\x9c>\xeb\x08[\xb5\xf0j{\x02N\x14sA\xf9\xd2\xbbi\x00:\x96k\xb1\x88\xad\xd4\x8e;\x0es|\xcd(\xaf\x17\x19\xbf\x92w\x1b\x9c@\xc4\xca\x07\xc6\xf2\xf5\xcd\x06'\x10\xb0/dD7\x99]6lv\xc4\xa5\xe1\xd7jO4\xbeq\xd6\xf8\xf9\xd6\x7f\\\xf9[\xbf\xfd\xf2K1\x18\xbc\x1cl\xe1\xdfW\xfb\xec\xcf!\xbb}\xcdn_\xb3\xdb\xd1\xeb\xd7\xf4\xcf\xce\x01+\xbcs\xf0\x8a\xfdyMo\x87\xaf\xf1\xedh0x\xb9\xc5\xfe\xbe\xc2?\xac\xf0hx\x88o_\x0e\xd8\xed\xeb3z\xbb3\x18\x0c\xe9\xed\xab\x03\xfc\xf6\xf5S\xf6\xf6\xf5\xab\x97x\xfb\xea5\xbb}\xfd\xfa\x95&|Is\x05\xbdyu\xf5\xfc\xe2\xe2\xd3\x9b\x17\x9f/\xce\xae\xde?\x7fw6\x06k\xea\xe7\xfeVJ\xfc \x0f\xa7Vs\xfb}\xfa\xf0\xe1\xa2\xed\xa34Ir\xcdg\xf5/\xae\xce/\x9e\x7f\xba\xb8z\xf9\xd7\xe7\x9f\xb4F\x85Ji^\x0e6\xc1\xfa\xe5\x97-o\xb0\xf5\x14\x81\xfc\xe2\x00\xa19\xe0\xc0\xddg\xd0\xdcy\xcd\xa0\xb9;\xd0t\xa3Z\x1cz\xae\x1e]\x0d\xb3,d\x8e\xd2\xf1\xd4O\xa7\x0c\xff\xeb\x91y\xcbQ=n\xa4\x16\x00\xb4DV\xca\xf7\xa1\xb3\xea\xfa \xa6\xfai'\x13jj!3\xe2\xc00\xf5\x03\xb7\xbd\xb2I~\xe9\xc8\nr\x8d\xd6\x15\x8c\xa8B|3ln7\x13)\x8a\xe6\xcdFS\xcf\xef\xceO\x1c\x1c\xee\xd4\x18\x8a\x1df\xa3\xfc\xd4\xc0W4x\n\x8a\xef\xfc`\xf1\x89\xcc2.\xe1Bi\xc7\x157\x9d\xe264:a\x87\x9e\xcfX&E\x9cK\xf6\xf1\xea\xd8P\x98\x1f\xa2\xb5\x94^.V eZ\xaf\xc6\xae\x7fi\x94\xe7\x10\xb5\xdf\x92\xce\xa7\xf9\xd2K\xc9\x8cI\x91\xe7$\xffD7\xff;\xda\xea'\xe2O\xefl\xc7#\xf1\xaf\x05)\x08z\x04R\xcc\xdc\x86_\xe7$\xffk\x92\xe5\xef\x93i\xe7\x8e(\xbb*}c\xb7:6\x17q+P\xb5\x8dxSRN+3\xb1S&\x94>S+n\x08\xb0\xeb\xfd\xe0\xf1\xf3Z'74M+\xe3\x8c\x94^4'\x12\x95:(T\xc6\xc4\x13!\x97/_\x05I\x9c\x93\xafF\xdfdM\n\x10\x90\xd6S\xeae\x8b\xa4\x88\xa6\x9fWS?'\x08\x14_\x9ft\x18\xf0\xacA-B\x1d\x82\xbe\xc3\xec1\xeb \xb0\xc5\xa8]\xf6\xd5\xe3\x16`\xdcc\x016\x11P\xdbT\xadH:K\xd2%\x1b\xef\x9b\xd9{\x12\x90,\xf3\xd3\xbb~\xfe\xcb\xc4\xbb*\xf0\xcb\x17~\x1e,\x98\x86\x8f'\x8a\xc51\x9ajo\xac\x9f\nk\xe81`\xf8=0\xe0\xc8\x10\xedo\xb8\xfbT\xab?\x1b\x19\xfc6w\xf6\xd4\xf2\x183\xad2\x08\x91\"YN\x93\xa0\x10\xd3\xab J'^{\xe2\xc7\xbb\x84)q\xf4\xb5\xc5\xfeM8\xc7h\x9erf\xe5\x93\xe6{\xaf\xc8H\xfa|\xce\x1b\xde\xfe\xe5\xfal:'\xbfl\xff2\xdd\xf6r\x92\xe5\xb6\xa6\xa0\xf6\x1c\xd0\xf8x\xd0\x8d\xd7\xf0\xa9\x00\xd9\x82\xcc\x8b\x93\xa9\xc1:*\xe69V\x995\xa7~W\x8b8\xedz\x8e\xa5\x16?\x9e\xc7\xb1\x8cK:\x00\xc3Y\xb2,h\x93\xf4\xd2\xc5\x1d\xa5\xd9\xbch\xc5Z\xed\xb6E\xbe\x8c0\x8a\x1c\xda\x8e\xd1;\x07\xc6\xd2{\x8aP(\x1c}V\x00\xf1\x8bi\xfd\xd6\xd6]\x84Q)\xbbv\xd2p\xc8=\x16(\xdc\xf0?\x94db\x02\\\xdd\x0b:\xf7\x95\xd9B\xed=\xa5\xe1\xea2\x0bf\xeb\xc1\x03\xeb\x89\x92\x82a\xf9\xfc\xe9\x0d\xc6\x83\xd2C\xe1\x1c+\x10\x85\x84\xd2\x94A\x8e\xb7\xaf>\xbc\x93\x7f\xb3\xca\xc5\xddE\xf2\x85\xc4\xec\xc6\xcf\xfd\x8b\xd4\x8f\xb3\x19I\xdf\xe4d\x89\x0f_\x87\xbcQ\xba\x9d\x9fG\xd1\xcb$\x8a\x18\xc7\x8bO\x94\xdb\xd7I\xba\x14\x0e\xca\xf4\x9e\x85t\x16O\xde\x91i\xe8ce\xef\xc2%\x1e\x80\xcc\x8d\x9b\x9e\x03S\x8a\xce\xde\xf9+\x97\xfe\xc52\x1f\xfd\x90\x8e\xe1\xd7\x82d\xac\xeb\x1f\xa3b\x1e\xc6\xfc\x0f\xfb\xf2\xfc\xa7\xbf\xbc\xc5\xb5\x8e\x05\xce\x7f\xfa\x0b#\\\xc5\xddG?_\x9c\x93yy\x9b\x84q.n$(\x9c\xff\xf4\x176\xee$e\x83f\xd15^\x14\xb3\x99\xa8\x8b\x82\xfb|A\x08\xfb\x9c\xa2\xa1\x8b\xd4\x0f\xbe\xbc\xe4\x00/\x1f\xb0\xbb\xa4\x08\xb0G\x96\x88\xe7\xe1\xd2y\xcc\x18\x99\x93\xa1(Dl\xd1L\x1f\xb4\x93\xee\xccb\x92iv&\xddK)\xdd\x89\x8d73\xe0\xfb-\xa8,G\x15t\x81\xce\x1b3\xee\x8a\x94`\xc8Q\x17\"\xba\x10'\xd1%\xdd\xee\x1e\xc2\xb5c\xcd\xab8\x91\xa1\xa62\xbcI\x17\x024\x1c\xe9\xb1\x08T\xe2eQ\x18\x10\xfb\xd0\x85\xada\x97!\xafi\xbb\x9b[\xeb\xce3\xd5\x99c\xea{\x04\xc7\xeem\xd8o$xj\xee \xf6\x10\x9e\xd0s\xbf\xb9\\\xea\xee\x07\xf6\xc8PNrd\xb0w\x0de\xb8\xbb\x84\xa2;_\x0fAJ\xb8pG\xe5\xbd8\x0f\xb7o\x8a\xd8\xde;xp\xe5\xe5\xe3B\xd2\xb5\x84\x8c\x1d\xdc\x1d8\xdeL\xd7\xc3=},\xe6&\xee\xee\xda z&\x82E\x99M\xd0\x1e%\xe6&\xc6D\xf6\xc9\x08\xb9\xf6\x93\xa0l\xac\xb92T\x97\x93\xbe3\xb9&\xa4\xba\x98\xf4\xdd\xbd=\xc7\xde\x18\xd4D\x95\xa3\x9d\x03\x87\xc7\xedq\xc1jF\xcf\xd1\x9bG^QR\x8eG\xfb!\xc2\xfe\xee\xaa\x9e\x82\xe3\xa1%\x06\x8f\xb0\xb6\x12\xd1\xc2\xae4>\xfee\xb8\xba\xabPooRK\xfe}\xaa\xa5\xa8\x10\xa8<]L\xe3\xf54\x895\xe1\x18\x90\xdbB\xff\xdb\x9c\xf1Wbl\x9b'\xa5\xaf\x84n\x8e\xcd\xaeK\xbc\x9d\xa1qn\x1d\xed\xe4\xfe\x13!\xf5\x162n#\xb6\x87\x83\xa1c\x1b\xa7\x9a\xb7{@\x11\xbb>\xae\xef\xef\x0f.X~#\x8c/\xf4\n\xe5+7\xd1x\xa9\x88\xe7\x1c\xcf_\x07\xe8\xfd\xe0\xda\x9aQ|c\xa3!Vn\xcf>\xadU\x8ftat#\x89\xddk6e\xb3(\xdd\x01\xc0\x02\xcb\x86\xf1#\x17\x1c\x81g0@\x1e#ET\xf1t08\x18>}:\xda\xdb=\xd8\x1d<}:\xa4,\xc7\x9a4\xfd\xb7d\xb5lM\xa1\x07[0d\xe6\xc0\xd6\xbb0fVs(\x12\x06B\xc9\x0f\xf8\x17\x0cyFi\x90#\xb8 \xb30\x87E\x9e\xaf\xc6\xdb\xdb3? \xd7I\xf2\xc5\x9b\x87\xf9\xa2\xb8\xf6\xc2d\x1b\x15\x99\xdb\xd3$\xc8\xb6\xf1\xe3\xad) \x92)ar\x9f\xd30\xbe\xf1\xd3\xd0\x8f\xf3\x13\xac\xb2\x96:\xa6L\x1bHQ\x8e\xf5\xc4O\xe7\xd9\xe4\x92\x95\x8bi\x15\x9f?\xbd\xa9d\xdfRb\x19\xd8\x84\xa1\xeao\xc4\xea\xc0Qc\xae\xb6\"\x8a`I\xb2\xcc\x9f\x13t\xb4\xcb\x08>\x8f\x93xk)F<%7@\xe2\x9b0Mb\x14\xaf\xd2\x8f\xf1C\x1cG\x06~<\x05\x7f:\x0d)\x80\xfd\x08\x16$Z\xcd\x8a\x08n\xfd4\x0e\xe3y\xe6)n27<,d\x95oHM \xc0\xa8\xbc\x04\x85d\x14\xf6o\x04p\xe0\xa70\x89\x90\x9d\xc2\x8c\xb8\xb3\xd4_\x92\xec\"\xf9\x98\xac\xe0\x84\xceT\xf2\xc8\x8d\xd1\x87\xbe\xe3IC)]CJ\xb7\xeb\x1c\xc9\xd3\xf5Vk\x8bI\xa7x\x03\xedj\xaa\x86\xf7\x998\x03\x1a\x91\x04\xa1\x81\xf4r\xe1\x1d\xd5\xba+\xa4\xc6j.Up\xdat\xb1\x1aW)L\xf0\xd9%\x93\x94\xc6\xcd\xc8\xc0\xd887T\xe9\xdb\xbcu\xcd\xca\x9b\x932\xf2z\xdf\xa3\xdc\xb5_\xa5\x1a\xaf7\xa5\xa6\x0fi\x99\x8ee\xcdJMu2}M\xbf\xaa4\xda\x0bm\xadl\xd6{\xd7\xaaqU\xd7\xd6\x8aa\x0f\xfa\xd7\x8a\xc5;k]\x1b\x9e\xb2\xab\xa2\xae\xc2Od~\xf6u\xd5\xb7\xb6r\x8d\xb2\xcf:\x16i\x0f\xa7F\xb9\xee\xfe\x8e\x8dR\x1b\xaf\x14\x0f\x84^\xbd\xa7\x1fu\xf4\x1dq\xea\xda\x15\xe3WR\xcd\x0c\xcfIf\xe5X@\xd7\x9e0\xea\xe8\xdd\xa4(\xd5\xb9d>\xa6\xe1\x12\x0d\xfc\xfaV]\xedk\xd4\xeb\xe9P\x07\xbe\xd0l/|n\x88\xe5\xa0[\xe2P\xcf\xc4\xa7\xed?\x93O1\x970~S\x16{p\xca\x185\xb1\xbd\xb7\xebx\xec\xbd\x9e\n]\xdf\xfdWs\x8e\xe1\x04J\xc1K9'#\x0e\xd9\xbf=\x7f\xf7\xf6\xeck@V\xfcx\xc5\x97)\xf13\x9cY\xc2\x1f,\xfd\xf4\x0b\x0b\xfc\xc0n9\xe9pR%v\xa1\xe5)\xcc\xec\"\xfe\x12'\xb71\xb0g\x8e\xe5\xc0&/\x85\x95\x9c\x82\xc52\xfe\x89'\xe5)f\xe3\x99b9n\xd9\xe5U^\xa4\xe4<\xf7\x83/\x17\xa9\x8fQ\xc6\x0codk\x19)\xee\x01\xad\x10\x9fe\xb4$\x86\x0d\x14\xc4\x87\xc3\x9f\xd1.K\xe9\xcd\xca_iK|\x0b\xd6 9\xedOj\x8c\xbb\x90\xd6_\x8a\xb1\xb6\xae\xec\x1b9\x1b\x01\xce\xd3&Xc\xd0G\x0c\xc9)e\xd79 .lT\xc1\xfcq\x1e0\xe1\x07\xa3\nM\xd3\xe1(\xa1\xb4\xd6\x8e\x83\xd3%\x8884E\x91\xa0\xd3\x94*>$\xa5\xff\xc8$\xb6wv\x07\x8e\"h\x15\xbe\x83\xf8\xfe`o\x88\x96W\x07{#\xb5\\\xe5j\x82\xe5vx\xb9]\xfew\x8f\xff\xddw$w\xf1G\xecN\xf1T\xe6\xaat\xe9:b{\xd4Hu\x11r\x13\x08\xf5\xb90\x8dP\xa5\\E\x15\x103\xf5\xe6L\x14NX\x0c\xaf&\x92\xc8L\xd2-\xd1\xd3\xb61\xaaeso\x1af+\xca\xc82O\x0fo\xb5\xf032\xfdD\xe6a\x963\x05\x08Z\xeeNbs\x14\x89\xc2&\x8d\xa0\xec\x0f\xf4Y\xdc\xb4\nJ\x99\xaa\xdd\xbb\x12\xcd\x8a\xa1\xa2\x01\x8b\xf6\x05\x8b\x1c/\xbdy\xc3\xcf\xb6\xc6'\xe5\x0b\x17\xeaq\x86\x9a@\xd4\x04\xd4\x14\xe1\xfaz\xc1\x03\xa5\xfc^\x9e\xfa7$\xcd\xc8\xc5m\xf2\x91\x96\xb3\x89w\x95\xfb\xe9\x9c\xe4\xb4+.dJN\x9bf?\x02\xbd\x18}\xad\xbe\x98\xe6\x97\xd9\x99\xc8\x1dj\x14\x03!\x9e\xa3|=\xa6\xd6@\x05\xb8\x00$\xd3M7#X\xd2K3\xfaX\x1d1@]\xe6\xd1\x1c\xff\xcc\xb4H\xd1\xc8\x85\x99s)PH\x95\xf1\xb7-\xef\xce\x8f\xf5 \xa1\xfb\x9a\xafj\xcd\xc0\x1f\xb3\x84\x93o[\xc2\xd0 \xc8U\xdf\x05\xadB\x80\x16\x9a\xa9\x0bw\xa0I\xc6\x04\x1c\xae\xd3\x86\xce\xd7\x0f\x82bYD~^.\x85W\xbcM\x92u\x19pb\xf0\x83\xa8\xd5R\xb2\xad\xfa\xf3/\xe1\xea\x02;\xde\xab!U\x15nj\xe8U\x98\x92 _s\x14\xab\x9e\x95\x9f\xc59I\xdf\x12\xff\xc6\x00\xa6\xd2\xb4W\xd7R\xb5\xed\xaajlf\xcd;\xe3 ]L\xabF\x7fRO\xf1\xe97\x1f\x8d\x86\x93Q\x1fy\xaeyb\xf2\x88\xceC\xdd\xc9\xa8;I3\xc3I\x1aUI\xa6~Ws0a\xcc\xf9\x86\xc9\xd1\xacK\x8c\x04b+\xd9\xa1G\xbe\x92\xa0\xc8\xa5y{\x13\x7fH\xa7\x84\xd3\xedh\xfb\x95}$i\x86\x1b?\xb7\x193&\x13\x94\"\x0f\x91\xdd\xd8\xdd\xf5^\xf5f\x8f\x11\x81n\x0cZ+\xeb\xcd\xb9\xb3\xca\x86\xad\x95-\xfaVfy(\xe9\xf4\xae\xd2$A\x93\xaa7\xaf\xea\xf5\xd6\x17\xd2M\x03\xadH\x1e\x00\xcdF\xd8\xcb\xb3\x1b\x12\xe7\xccl\x01\xe7a\x0c\x89\xa7\x7f\xd3D\xf4\x8dr\xd9\x0b\xee\xde\xa7\xa9\x83\xbfk\x9d\xb2\xa2\xa4\xdb\xfa\x19\x06ku\xe51S@ZOw-\xfcR<\xd6\x1cD7\xdce`\xd1H\xf4I/;\x9a\xe4,\xfbh\xc4\"\x81\xfd\xfe\xe08\x93\x10#H\xe8\xeb\xc2\x94_\x8d\xf3\x81\xd9\xebd\xda0b>\x1a|z\xd3p\xfa\xb1\x1a\xbc\xeeY \x866\x00J\x84o\x0f\xa3|\xa1I\x8b\xb4=\xa3\xe4C\x9f9\x00)6\x84v1\x8b\x0b\x835XI\xfc2\n\x83/\x96>\x90B\xa3\xdcK\xc6\xe6\xf6(\xfe*)\xae#\xd2\xb7r\xa9t\xff&\xde%EF^%\xb7\xf1:e\xd7\xac\xfe]r\xb3V\xd95\xab\xff\xbc\xea_\xb2\xbbj\x90\xf4t\xf6\x06\x92\x8a\xfeu\xc4\x12\xbcbT\xc0\xdc\x05\xeb\xba\xc8s\xb6Cy2H+\x8cWE.?\xc8\xd0\x14K~\x92\x93\xaf\xb9\x9f\x12\x9f?sZ\xbc\xa8[#s\x88K\xf4\xb2\xe98\x05\xa0\xea \xc4\x85\x87s\xe3\xcd\x03\xb3\xceV]'DDJ\xf59\x8bY\xed\xc8b:=\xeeH\x8dx\xa8T\xf2SZ~\x92^\xb6a\x00\x96/\xe8\x11H`=\xb4\xc5\xf9\x8a\xdb0\x8a^\xd5Z4=g\xed\x9bG\xae\xc7AX\x1dO\x81\x94N(tz\x0c\xfey\x14\x95lC\x17\xd5)\x98<=\xe0\xeby\xbc\x15\x12[\\\x14O6\xfcpc\xb4\x82\x89&\xf1\xe5$\xbflC\x8ab\xfcf\xf0\xeb\xc4\x06\xe2B\xf8\xa4\x86i\xd0=\xb7\xb9\xa1<\x87)\xef`\x8f=\xf1\xa0J\x90\xf2\xd4\xe7\xc7{\x7f\xca\xbb\x84g\xe8\xf2\xa3r\xc5H\x83\x9a\xfd\xa1\xdff\x7f(.a\x87\xe8O2\x03|p^\xba@O \xda\xc8\xab\x8dF\x1e\x83\x19\xf2\xccv8D.7\xa4\\\x91~q4\x11K\xf3 \xdf\xdea+\xbc\x99\xebU\x13\xdefR;\xc0\xbe\x05\x1a.X!\xba\xd2$ Y\x86U\xffo\xdaHW\xf5b\xcf\x04M\xe8\x94\xfc\x01d\x88%\xe1\x14V0\x86\xa9\xe32\x80Q\xaa\x0c\x93\xb1\xfa^JP\xd5\xfd\xd2/\xe6\x8b\x9c\xe9\xc2[\xbbyu\xb5*\xd29\xe90\x81\x89*S\x0fc=\x12\x91\xf4\xc2\x8f\xbf\xf4\xcb\x8f\x1d\xd5\xeb,\xef\x0c,!\x0b\x01\xf0\x8d,a#\x85\x97` \xd5$A\xfa\xe8:7!\xb9\xed\x9aK(\x83\xe9\xd1\xd2U\xd0n\xbc\xd5\xaf~1\xfd\x89\x16e\x82\xf0\x99\xf4n\xc3x\x9a\xdc2\xcb\x81\xb2b\x8d\x87%H\x87P\xeea\xe2\x85W\xdcKM_\xb8<\x0eO!\x16!o\x7f\n\xc9-\xc6t\xe5\xfe'?\xb3\xc6\xc7\xc0z\xd1\xdc\x85MffJr?\x8c\xfa\x00\xac\x04\x12\xfb\x84\xb6\xdb\x199\xbb5B\xa6\x0b\x89\xda\x16oCRZIy@\x1bf\xa3\xf8\x85\xe7\x17s\n5\xcc\xa3e\xfb\xcc\x0bT^\x94\xfe\xb7/J\xb5\x93\xcb\xe4\xa6\x13_\x10\xcc\xa7\x1e\xe4o\xe2\x9c\xa4\xb1\x1f \x01\x1d\xdd&\xa8El\xdb\xae=\xc4R\xe5t\xe8\x9bi\xab}\xe1w\"\xd3\xbaF\x9e{\xff\xae\xdd\x90\x92\xbe\xde$#1C\xcah\xd7\xac\xc7?\xbdTS8\xa9\xd5\xf7\xdb?nH\x8d\xbcLVwi8_\xe4`\x07\x0e\x8c\x06\xc3}\xf872\x85\x9f\xfd\xdcT\xec\xefdz\xcb\xea\xabl\xc5\x02\xbaz\xd1E\xb0,\xff\xe3\xf6\xffQ}\xdc0\x1f(\xfa\xcd\x05u\xab\xd6:)\xa9D\xbd,\x91G3t\x02\xc8\x14\x16\xe1\xd9\xbe\xa5\x10\x17\xcdh\x95-\xe1,\xc4\x86\xafl\xeat\xf49plo\xcc\x9f\x0c\x92\x90\x85\xcbaR3Q\xa5$\x958\x81P1Y8\x81\xd0\x01\xc2\x9c\xfe\xda\xa8\xb32}L\xddb+u\xca\xaf\x13\xcf_\xad\xa2;\x9eP\xa9\x95\xbf,+\xaby\xc3\x86z\x82O\\\xe5D`F\xa0\xd4\x11\xc6\xc6\xa9\xc8\xcb\x93rG\x17\xde\x1f\xff\x9b\xe9G\xc2\xf2\xceZ\xd0\x1aKR\xc6c\xacy\x814\xeai0\x92\xd2\x85\x0eGk\xd7\xb4\xa2-x\xb2\x9e\x9e\xfa\x81C9\xc7\xd8\xb4(\xcb\xade\xf7\x95T\x9e\x0f\xf6zV\xc8\xdc.\xb8\x0f\x8a\xe3\x9e\x1b:\xd5\xf3?\x81A\xaf\xda]\x16*\xbc\xde\x9a\xe8i\xea\xc7\xd3diw\xfan\x18\xbak1\xf36\xdb\xf2\x82$\x0e\xfc\xdc\xae\x85\xc4\xc74\xc6cJeX\xce\x95\xe5\x82\xbd\xb9\x19\xc3&\xa4Ne\x0e\xb1\xb3\xff\xf8\xe43\x8dh\x06<\xb5e\xe39Sp\xec6\xe6\xcb\x07\x83\xd5|\x05\x8d\xdcc\xd9o\x87\x83\x81\x03\xa7\xfa\xd2\xd0-ZF\x94V\x06Y\x0d\xe9\xf2\xdd\x188.\xa46\xe5\x9d\x13\xa7\xdd\xd0\xdd\x14\x8c\\\xb6v\x7fh\xb4g\xcdInQ\\\xc1\xacW2q\xd7t\xfc\xb2\x9e\x07\x94aKR%\xdc\xb4\xc9\xf3\xcbBw\x0c^7\xe5\x0cE\xb2i\x0f_P\"\xf1\x11KTsP\x89\"\xeb\x9a\x17\xc7e\xce\x88F\\\x9f>=\xc1\x9d\x11\x9002l\x9aY\x94$iW\xef\x0c]\x0b\xb3\xf7\xfe{\xf4\x81\xd9\xc44\n\x03\xe6\x12\xc3v}\nc\x88\xd7O\xe8!\xe1\xa4Q\xaf\x87J\xe3>\xc3\x99\xa6\x91\x1b\xb4\xc4qn\xf4\xc1 \\R\xcaK\xddh\x98\xd6\x88\xcb\xd4\x93\x9d\xfe=\xd1\xb0n\x9aO\xea\x9d\xa91p\xf2\xa5\xf0\x8c\xba\x05\xd9\xe7\x0c&\xd5\xa9[\x92ofC\x08X\xe3\xd05\xef\x97\x7f\xa0\xe7\xaa\xd9Gr_\x9f\xc8b\xcf\xe4\xc3\xd9\x89\x0eR;Y?\xffZ\x97\x98gO/\xe69\xd0Iy\x98\x87Y\xf3\\\xc4A\xd5\x1f3\xbd\xff\xb0;\xc7\x9e\xd9\x14.cF<\x1ao[\x96\x94\xdeGk%\xcb\x82 \xb9\xd4\xb9\xf7\xa2\\\x7f`\xf0\x06\x8f\x1a\x11\xd8C\xb3\xe7\x1cH\x82']8`!^\x9ad\x97]\x84\xaaT\\\xe3%\xe72\xef<6\xa6f\x02\x0ds\xc21X\x1f,\xd8\x84\xcdMM\xf2oq\xddj\x93l@\xe3\xdc\xc1'\xad\x92\xf9\x99H\xeb\xa2\x8dfB\xaf\x7f?\xfb\xdb\x184\xf6#\xef\xcf\xce^\xe9\xd3\x17\xce\xfc,\xffw\xa2\x86\x873mg\xcc\x1a\x90\xc8A5\xb5n\x0b\xcc[]\x9f\xb6\xf2\x14\xacs\xca\xfdX\x1f\xd1X\x9f\x98e\x1d\x1b!NOk\x04a,\x97\xd5:\xf4\xdaj\x97{lT\xd4\x9bu\xd6R6P]_\xc4\xa5\x9fLq\x86N\xd2K/lNl\x13\xf2s\x92\xffL\xfc/\xeb@\xfeQ\x00\xd90\x84H\x84&<6\x86\x7f\x088zi\x05\x92\xf8uJ\xc8o\x9dBn\xa8*\x8f\xd0\x1e\xd4\xa3\x8b\x9b\xfe\xc2\xd8vO\x9e\x80\x00\x13\xfd\x1d\xd8u\xb6K\\:\x02\xb0\x8d6c\xfc\xee\xef\x0fe\xb8\xe77\xd9Y\x19yC\xfb\xf5Z\xb4\xc9\xef\xdf\"]\xd6W\xadw{\xcf]\xb0\xaa\xc8F\x0d\xf7w\x8e\xf2\xe4xG\x947\xf7^\xbe={\xfe\xe9\xea\xc5\xdfPs\x847\xf8\xeb\xfd\xd9\xcfW\xcf?_\xfc\xf5\xea\xecS\xf5\xe0\xfc\xe3\xd9K\xfa\xe0\xea\xc5\xf3\x8b\x97\x7fm<.\x1f\\\xfc\xf5\xd3\x87\x9f\xdfkJV/J\xc5\x05\xedCLn/(}\x1b\x9f\xa5\xed\x9eg|u4\x97\x0e\xc5A\xda\xa8\xcd+\xff.J\xfc\xe9\xb8%\x83$\xd4\x89y\xb5C\x18/\xf3[z\xa59@\xca^\x91\x8e^\x9c\xafH\xf0\x8d@\xc9\xbe\xbd\xf9o\x06\x81&\xbe^\xef>\xbf\xba\xa6;\xd7j2\x01\x0d\xc4]~\x9c\xadH\xa0i92\x1f\x02\x8dO\xb5\xad\x06\xbac\xa5\xfc\xd4/\xf2\x85\xa6\xd5Y\xedT\xc2\xd2\xb8\x80\x95b\xab\xaa\x18;\xc9\xaa\x92W\xd7w\xcc-\xb37_\xb6\xaf2X\\\xc6\xaeK\xdcY\xba?3\xa5\xc0\xe5\xda\xe1C\xdaH\xed\xfb{\xb4\x0fa6?\xc4\xa1\xef*\xeasMfs\x7f\xc7\xe1\xec\x96\x0b\x16s?5E\xaf\xeaE\x98H5\x0f\xf4\xee\x88\xfb\x0d\x19\x0bO\xf7?\xd03\xb0\xfb\x03\xbd\xf0e\x7f\xb0\xdb7\xdc\xb1\x10nli\x98\xa1\x98[U\x01W\xd3\x0c0\xe6\x16W\xe2\xd6\xd7\\\x92r?c\\@\xb6s\x04\x9b\x9b9\x1cCl\x0c\xb3\x99\x1a3\\3\xafa\x92\xdb)f\xcfK'\xc3\xcbv)\"\xbd2\xd9\x0b\x98\x9f@\xa9[{\xccm\x0fO \xa9?\x9f\x13\x96\xfc\xaa\xf6p\xe1\xa3\xe5J\xfda\x86%\x8b\xbauK\xb6\xde\xdc\x0f\x07{}$c*\xd8$\x93\xd0\x13)_x\xbc\xb5u\xd4\xe4C\xb8\x94~\x12_\xb2\xfc\x83\x92\x19\xb0\xf6\xac\xd8\x1a>z\x8f\x0c\xba\x93\xd1kFS\x0d\xe4\xeaj\xea\xe7\xfe\xd5\x95\xb6_\xa9\x9d;p\n\xf1D\xc3:\xe7\x94u\x16\x8f\xc7`-\xfcla\xd1\x134\xf6\x96\xfe\xea\xd1\xe31\xb8C\xed7\xe2\xf2\x89\xf0v\x06w\xa8]\xfd\xc6\xec\x11\n\xd7\x84\xeeD \x9dlA\xde\xa5!\x85\x86.:\xc6)\xf86*\x93\x12\x9b\xe0\xba tg\x89T\xddc\x94\xb8v\xc0M\xee\xdbZ\xbd'\xde-\xb9^\xf9\xc1\x97\x8fIt7\x0b\xa3\x88\xab\xe4\xa7d\x95\x92\xa0\x99\x17\x14=\xdeW~\xbe\xc8\xb8=I\x15z\x99\x7fY\xde\x9e\xb0\xf4\xb3z\x06\x8f\xb8`\xb1dM\xda\xd8f\xb5p\x91\x9a\xf0tk\xc5>#^\xd4x\xad0\xd6\xad\xfd\x0c\xffG\xfa\xa8\x11\xc64\xfa\xd8\x9c\xad\x13\x18>R_\xab\x9a&\xd4\x07@w\xdd\xf6\x7f\xda\xa7\xe3\xc1\xfdd\xb8\xf5\xf4\xf2\x97\xe9\x8f\xce\x9f\xb7\xbb\xb6\x88\x01\xa3$\x95\xb1\x8f>\xef\xfb\xc6\x86\xfd\xff\xb3\xf7\xef}q\xe3\xc8\xe20\xfe\xff\xbe\x8a\xc2\xe7\x9c\xac=\x18\x03I&\x97\xce\xb0,\x03\x9d\x1d\xce\x06\xc8\x0f\xc8\xcc\xce\xaf\xc3\x971\xb6\xba\xdb\x1b\xb7\xddk\xab\x9b\xb0\x9b<\xaf\xfd\xf9\xa8$\xd9\xb2,\xd9\x86\xb0{.\xcf\xd7\x7f@[\xd6]\xa5RU\xa9.T9\xd3\x18\n\xc9`\xc4*{\xf2\x04\\\xd5EI\xde\xf0A\xb2\xb1\xc7M\x87\x0b\x1e]\x80xX\x80\xc0\x1f`k\x97\xff\xfa\x0f\xf4e\xcfi}\x8c\xc5\xfb\x80\x99\xd2]L\xf5\xcd\x82\xed(\x17\xfa5\x8a\xe9\xa2\xf9z\x8b+\xd8\x18\xf1\n\x86\x03P\xba\x82*\xae}\xc8\xa1\x83\x90\xd2\xb1\xa1`\x1f^Y\xc8\x9dg\xfa\xfd\x99 w\x9e\xe9\x0e\xc6\x05V}\xa6\xd3\x99\xa5\x99*M\xc5%\x81^\x0d^\x18\xb9\x85\xd7&\xa4S7\xf7\xdats\xea&Zj\x8c\xa9\xa1\x96:\xc7\xd4\x95\x96\x8a\xe1\xdd\xea%q\xb9\xe1\x91\xe2m(\xfc9!\xb7W\x08vk\x97\xbb\xe3`\x7fQ\x97\x8c\xbb\xacqw=\xae\xd5\x947\xca\x9e\x84K\xb5X\xee\xf1\xd01j\x96\xf7E\xbeHJ\"\xb3%\x01\x0f*N\\^_\xd8\xc8|A\xa8Z_\x88YV\x8d,\xbf\x90\xf0\x93\xd6\xec\x8ao\x0fw=\x08ZK\xe3=_\xa62\n|c\\9r\xcf6\xfd\xbc\xd8\x9d\x8b\"\xf4\xc1>\xa4n\xc6\xdd\xdbh\xd7~\\\x81P*)\x18/\xf7\xf1Z>\xea\xbc\x967\xac\\\x9b\xa6\xc5z\xa6\xc3\xea\xc1\xe9\xb4T\xb1\x1cVE\xb5\xca\x96j\xe2a\xd5\xe0\xfa[\xaa\x98\x0f\xab\xa2\x82\x8fFn\xa3\x8a\x81\x8235\x05\xf2AV\x0d\n\x89\xfd\xecu/\x95e\xbf|\xce5\xaeG\x88nF`\xb4%\x13}W\xb4arq\xaa\xf49F\xb4v\xbf%T\xe1\xd8\xf2\xd5\xce\x90Au\xf2\x0d;\xdc\xb9>\x1e\x82\xe8[\x97x^\xcdJ\xc8x0l\xf3f\xf0\x03$o<\x94i\x91I\xee\xd2I\xb6\xb9y\xe5]\x19\x07\xcf\x8d\xf2\x90\xd7\x16\xf4\xa8\xa6_?h\x02\xccr\xfb\xfaZ\xb45\xb4\x0d\x1a\xacIQ&\xdc\xef\x92PE\x92IA\x92\xc5\xe4\xf3\xd9\xd4u\xd6;\x81\xe3u\xe7\xd8e9\x9e<\x11\x02:s\x8eW,\xcf~\xcf\x85cF>\xd3\xcb$\xd2n\xb1z\xf4u\xfaUX\x18V\xad\xd5X~\xefDa\x9a\xde\x84\xd1'\xa7\x92\x1eb\xf8Y\xb8!\x8aZ\xcb\xef-\xaa\xc5ka\x07\xc7c(\xb4\x94\xb3\x8de$\x8e4\x06F\x92\x0f\xa2\x85\x9d\x1e+_\x8b\xc2\x97|$*\x08\xe4LZ\x8d}\xa0G}K>\xed\x1a{ie\xf5\x11\x1aT\\]\xdb\xa2X&\x1f=\x10\x89\xfat\xe9w\xc9\xe7Q\xbbjU>\x93Ooo\x9f\xffk{k\xd5N\x93OW\x87\x07\xd9b#.D\x12SRS\xee\n\xb6\x90\xb3 \xb9\xb9B\xc8\xd0\x9e\xdc \x1e$\x93ps\xf3\xaaa\x8d\x10\xf6D\xe5\xfd\xe6YQ\xcd\x03zt\xfd\xbf\x0e\xbd\x81\xd68<\x14\xe3\xd5hL=wU\x07\x89\xdf{f\xcdx\xbb\xa6\xb5\x89\xcc/\x84\x97E\x93<2\xe9;\xb2\x92\x0c\x91\xe0$\xbb\xc2s(S\xfc\xc2u\xd9\xb5Y\x84\x10y\xf5]\xa9F\xfe\xca\x83i\x91/\x00\x9d\x83\x85i\x9aG\xca\xcf\x0fY\x19NI+\xe1\"\xcdo\xb5#\x81\x91\xa3n\xe2\x16\xdc\xa7\x0c\x0d*w\x94\xa1\xe7C\xe2\xe6<~b\xc8\xdb\xea\xa7G\xf0h0x\xce4\x1f\x0c\xceA\xe34\xc8rq\"\x88\n\xcc\x94\x8biRX\x0f\xf9\x1c\xdc\xb3\x8b\xbdg\x97\xd6\xc5\x8e\xeeI\xb0j\x9b{6I\xae\x0d\xc1\x14\x98\xc2\x05\xc2>\x14\xc14\x91Z\xc1\x8c\x86\x13\xaf\xcaoT\xb07\x8c],z\xaf\xf2\xe9?a\xec\xf5\xd2\x98\x16E\x01\xbe\xff\xc2\xce\x15\x01\xeb\x81`G{\x05\x87\x83h=u#e\xee\x8b\x97\xdf{\xae3\xcd\x8bq\x18\xcd\x9dA\xa8\xa8O\xe3\xf5\xd9\xaeY\x10\xf1\xcc\xe2\x06r\xf7\xb5.)\x10\x82\x88W\xaa\x18\xd7\x1dL\x8c#R\xc3\xf8$+T\xcfL\x8d3\xdb\xbaC\xfe\x01\x9e6\\\xe5n4\x84\xban)\x9c\xc3r\x97\xb1D\xb0/\x0c\xc2\xcb\xc6\xd1\xf5T\x04\x8c\x94\x8c\x0dFO[\xa1I\x13\xe7\x0b6\xd0n\x08\x93\xc3J\x7f\xd3\x89\x1c\x11\x93KI#2\x04\x97\x92v\xebx\x9e\xcf\x0d\xe1\x1b\xa3\x82Z\x91\xc6\xe0\xc6\xb0\x19\x96%kgP\xc5\x9fI\xfbs\x1d\xa2G\x8fK\x0c%\xdb\xfen\xee\x96\xac[ld\xb5x\xf6\xab\x17\xcc\x86\xf2\x83b\xa9|\xdd\xef@u\x0di^\x15\x945\xf1@\x06\xe6\xc5I\x1b\x8b\xf3LY\x1c\x86\xceh\xa5\xec\x03#H\xc4=\x88\xf8\x8e\x16\xe8\xcd\xef\x19\xb7qS\x1a\xe5\x1fqA\xd3\xba\x0f\xca\x17\x0d\x18$ \x945 \xac\x0c\x80P\xb6\x00\x01},\x98\x16\x1d\x05\xd3\x86%G\x9bd\xc3J7A\xc1\xa0\x01\xa4\x82B\xa9\xafv*V;\xf5D\x0c\xbd\xe8~(\xa9\xc6\x12\xadp\xb9\x02I<5_\x01={f2\x18\xcb\\\x8b\xb0rwW\x17nrt\xb7\xfbB\xc7M\xdc\xa7D[R\xa9\xaa\xbd\xb8TS\x82\xd5\x87\x88\xbe\x05\x97&\xb8\x8e}\x98\xfb\xb0\xf6a\xe1\xc3\x0c\xf6`\xa9\xaa\x89\xdbhU);n}dD\xa5Y\x94w\x87\xc2\x06\xde\x11\x06\xd9Oa\x04:\xbae\xcf\x0d\x92\xe0\xcd \xb6q\xc6\xb3\x1e\xe3\x8e\x84r8i\x99v\xb0\x1a\x13wf\xd4\x19E\xba3\xe6\xa6\x072F\xef\x1b\x88\xe1\x0fp\xf3\x06n67\xcd\xd46\xab\xd1]\x08G\xacwn\xe8\xce\x91T\xbd\xb9\xf2\xf0\x8em.\xee\xd8\xee\\L\xf3P\x06\x81\xb7_\x0b\x1e\x0b\xb2\xba\x9a]4!\x1a\xcd\x7f\xcd}\\\xc3\x1eTq'\xde\xc0\x066\xb9F\x8e\xc3\xf5\xbc \xce3b\xb8\x14\x06\xb5\xb3\xb9\xbb\xf6\xe1\xce\x879\xb7\xc5\xe3w\xc4\x03\xba\xf6\xd5\x0b~<\x1f\x1f\xfc\x99\xc7j\xa5\xc1\xf9\xf8\xf2\xc3\xf9)\xec\x89\xdd\xf6\x8d\xe7\xb3\xd5'u\x11\x1c\x8d\xdf\x1e|xw \xfd\xfe\xa9ww^\xf5\xf8\x9d~)\xfcL\xbf\x12\xff_\xdf\xdb\xdf\xb4BR<\xb7\xdcm\xec\xe8\xdb<1\\\xf1\xdc\xdf\x94\xd1rH\x85Fm\x8aD1pD\xee\xc5\x0d\xb1\x18\xddd\x83\x00\xad6a&\x1f\xec\x96\xd6+W\xa8\x869O_\xeaGCU\xcchc]}\xb5-\xdc\x0e\xa7}\xd9\x7f\xdep\x05\xa7\x07\x82\xc9\x8cxp\xf8\xda \xb39FQ\xde\xe2(\x10\xa6I\x16\xa6ig\xd7:;\x0eP\xb9&\xeb\xcf\x08r\xa4Q\x9a\x97b\x00\x9d\x05\x9aF\xe6\xdcu\xc5\xe0\n\x86\x0c\x0e\xba\xe6\xde\x93\xa8\x15{\x1a@\xba\xd2\xb0\xd9)\x81d-\xb0\x11s\x03a\xdbu\x8b|V\xed\xab\x05\x90\xd8\x81\xfb\x83GM?\xae\xff\x93U\xbcNh\xe7u*\xcffA$\xa0\xf8\x80\xbaa\xa7+\n\xae\x01\xd6\xa3T\xc5\x88,\xe7\xc9\xdfV9}\xd3\xe1\x8b\x83=7\x05 ?\xd9\xb3\xf0\xd6^\x0di-\\,\x1f\xa5\xb1\xd7C\x1a\xfb\xb7\xcfO_>Fk/:\x14\x0d\xa1j-}\x94i|\xd1\xa3b\xc8\xdb\x9a}k[\x83t\xd8\xa2<\xa3I\xb6j\xdf\x0c\x81\x95\xc5\xe3|0j\xf6\xbb l2\xfcX\xaen\xf8\xb5\xb5\xbb\xf2!\xf4\xe4e>\xe3@\x19+\xbc\xa9#:s\xe5b\xaf\xca\xfa\xf7Y\xc9v\xe50\xd2C\x0c<\x92\xbaH\x83\xea2\xfa\xa67\x851\x0b\x852\xb5\xd9@\xaf\xcd\\\x96\"\xbf\xce@ [\x92\x96FId\xb8\xb5\x9d\xa2p\xa1\x99\xb6l\xa3\xabvx>\xf6\xd0|yp\x93\x17t\x04N\xc8\xfe\x1b\xd0\x1f\xcb\x92%\x0b\x0c\xe11\xce\xe2\x11\x94\xae\x13\xca\x04\x92\xc5\\\xff\xb9\x99\xd4]\xcb1%<\"H\xb3\xaeD&\xeb5\xd6\x1f\xba\xeb\xbd\xa0!\x1b\x89Zg\xc9\x92\xf4\xfax\xa2\xb1\xae\x1f\xd3U1\x02\xe7&]\xe9&\xed\"\xc3a\x98\xbdO\xc3\xbb\x118Q\x98-\xd3\xf0\xae3\xdb\xe5\xbc\xc8W\xb3y\x9d\x9b\xf2\x04K\xa1y\x98\xcd\x08\xcb\x8c?,\x99RT\x01w\"\x8c e\xce\x92/\x96y\x99T\x0b\xe6Du\x82uu\x94Bb\x1e\xd5b\x1dS\xa6\x14\xfc\xb0\x8cQ&\xa0\x96\\a\x9a\xadhF\xc9gzB\xb2\x15\x16\xc2\xb7\x05\xc9V\xb6\xecK\x9c\xf8|i\x9b\xf5\x15v{e\xe9\xa9\x12\x1ek\x04N|\x93v\xcc\xe1Q\x11\xceX\xa6\"\x9c\xd93\xf0\xd9ey\xac\xd3\xca\xb3QRT\x19)\xb1\x80\x16f\xfd\x9cP\x99\xf3sb\x1bG\x11\xce0\xc0\xa3\xc8\x99\xb2\xdf\xf6\xacg\xeb\xaa\xf5|\xdd\xd5\xb8\\w\x96\xb3c\xc1\x8f\x8a|\x89\xb9\xf2\xa5%\xc3\x8ao\xd7\n\x9ec\x91\xd0\x05\xd7\xe3\xc5\x92&\x84\xcd'\xe1\xbf,\xd9\xb2\xa8\xb8[R\x9eQ\xfe\xb6e\x8dE\xb6\xd8\x9a\xa5(r67\x84\xfd7gy\x9bG\xabr\x04\xce\x94\xfd7g9\xce\x96\x08x<\x02\x981\xcb\x9f\xc9\xddQ~\x9b\x8d\xc0\xf9D\xee\xe2\xfc\xd6\x82\xca\xfeL\xee\xde\x17\xa4,y\xbe%\xfbi\xcd\xf8a\xc9s\xad,\xab\xf0\x0e-\x93\x19\x0f2\x92f\xca\x8cs\xe9\xca|Bh\x18\xab\x05\x16\"\xc1^H\xc2\x0c\xcb\xdf\x013U\xe0\xb8\x118\x0b\xf6\xdb>\x07U\x108\x99\x95qW\x1dY\xcfp\xee1gn\x9b~\x9e\x91\xef\x03\x9e\xd3\xba\x11D\x988\x99\xd16\xbb\xef\xc3\x121\xdd\x92\xfd\xb7eY\x95<\xcb\xaa\xb4e\xe1G\x89\xfd\x1ca\x19\x92l&\xf2$\x99\x05\x19\xbd/\xf2\x99\x80\x9b\xa5\xf8i\xcex\x1eRRm\xcb\"\xa4\xa4kKr \xdb\x08\x9c\x12\x7fX2\x11\xf2 \xb7Y\x89?\xec\x99\xf80J\xfe\xcb\x96-\xe5\x91=\xab.\x962\xa5\xb3\x9f4LS\xde\x07\xfe\xcb\x92mU. b\xec\x92\xff2g\xbb$\x9f\xa9\xdc\xd1T\xfe\xb6dM\x16\xa4:\xf3h\xb2 ]\x87\xdde\xbe\x8a\xe6\x87a\x16\x116\xa5\x94\xbdE\xf8\xd6\x91\x9d\x1f0\x98\xd7\xde_\xf6U\xec\x17\xcci\xdf/\x98U\xeeX\xcc\xdb\xb1e\xf1\xda/Q\xa9>Z\xa5\xd4d_3\xcdX\xd1\xcfy\xbaZ\xd4P\xb7\xc6\xd7\xae\xf5\xfc%L(\x87\x96[\xfe\xcb\x92mNp*o\xd9\x7f\xcd\x04\xb4Y`\xcex(\x1e\x85\xa6\n\xa2w|\xe4\xc0\xa6\x90\x18\xb9\x8d8\x04^P\xa6ID\xdc\xa7^\x93\x1dX\xa3j\xdb?\xbe\xa2VE\x93\x94>'2\xd2Z\x1d\xa4\xb0}\x990 p\xad\xa9\xa2~\xf99:\x8f\xf9)\xcc\xe2\x94\\\xe6\xcbwdMRw\x1d\xcc\x1b \x9e\x0f\xeb\xa0]=\xec\xf5{ll\x8e\xa2$t\x9ca@\xcc\xbe\xae\x19\xdb{\xf2\xc4\x98\x1e\xd4\xd5\xb6\\\x01j\xb3X\xb6\x9b7\xb5.5\x88\xdc\x0dc?\xbe|\x01\xe3\x87\xa0\xaa\xdf\xed\x0e1\x97b\x81\xcb|\x80S\xd1\x86\xa4\x98\xfa\xd0\xed;O>b\x00=j}\x95\x16\xde\\D\"\x99\xcc\xaf`\x0f\x96\x9b\x9b>D\x13\xf6&\x82\xfcV\xaf\xed\xe5\xe6\x11 `\x0f\x92V\xc0\xc6#\xc20%\xc9\xa2\x84\x94\x13r\xd50f\xcb\x87\x08\xb3P\xcb\x9d\xed\x1c\xabu[\xa1\xc7\x99\\\x89X2+\x1e\xa7\xd8\x91{\x9d\xcb\x86Wht/v\xbd\x07\xfbfp\xa2E\xb8\xfcqu\xc3\xd6\x11?(\xb5\xf8\x12e\x08\xb3\x9d\xd4\xe5G\xfd7\xd5\xa8\xd4 \xaa}@%Gg'H~\\\x88\xf3\x96W\xe4TGqc\x02\xe4\xa1\x0c\x1b;\x9d}\x16\x01o\x95\xf6\xaa\xea\xeb:\xee\xd9cC\x0d\xc6\xc2\xbf\x1c\x9f\x1e\x9d\xfdr\xfd\xd3\xc1\xe9\xd1\xbb\xb1\x1c\x0bR\xd4r(x\x86p\xbe\xbb\x1e\x9d\x9b\xba\x92\xde\x16\xa3s\xef1\xbc\xb7\xa2dUEf\xc1}\x96\xf2\xd8\x17_\n\x01 \xf3\x04\x90`uI\xe6\x08\x15\xd7\xc1\x93\xd5\xecO\x92\xf5\xf5\xa8U\x81\xec\x10\x96G\x1a\x97u\xca\x87\"\x10\x1f\x85N\n\xbeck\x98\xc0\xba\x1d\x9b\xf7\xd6\xb0\xb6W>\xc4\x93\xd5\x15\xef.n\xc7\xbdVHy\xe8;.\xf4Z\xfb\x03\xd5\x80b\x867\xa8\x9f-\x85bK7\x1aK\xfd8\xfdhB\xcf\x90\x8e\x88\xc86<4\xe9\xfbpF\xfe\xf2k\xcfA\x86\xb7\x17\xfa\xad\x1e+\xdd\xe9Kz-\x9c\x86\x9a\n\xba\x0e\xa2\x19\xfcm\xd2\xe3\x92\xf7$\xaa\xd3\x06UQ\xa0k|$+W\x85\xc0`?\x87\xe9\x8a\x9c\xe4YB\xf3\x02 \xba\xdeq*\xae.\x90T\xc0K\xdcu`\x984\x97\xed\x80\x0d\xcc\xb41\xed:|\xd8$\xac\x82\x82L\x0bR\xce\x95~\x95\x96\xfb@\xd3R/\xf8\x18\x94\xd2\xe8\xebzZ\x87\xecR\x1fm?To_-\x06\x08\x83<\x904\xc5\xd4Ur\xa5\xd1P\xb4\xe6\x94k\xb4^\x17\xab\x94\x94\xd7\xd7\x0d\xdd\xf0\xeb(\x8c\xe6\x04\x13-\xd7\x8b\x85Bp\\_O\x93,\xc6\xdcv\xaa\xa5\xad\xf7W5-\xc8\x04~\x8d\xb7\xb5\xfb\x06\xa8\xd5\xb1`\xb3\xe0ds3\xbbB\x85\x01\xae*s\x0fO\x83\xbe6\x82(_,\x93\x944\x07a\xbaB'\xa2\xfb\x06\x96\x83M\xa1\xe3hT\x0cQ\xc6)\xecI\xddn\xda\x8e\x04\x84\x13\x98\xfc~\xe3\xf5\x18\x07\xa8\x95\xa2\xae\xfe?\xd0\x07q\xaby[ OY\x92\xc7\xda\xe2\xae\xf3:\x86oD\xa9\xec\xc9\xd4)p\xd1!X\x86\x13!\x07G\xf9\xe0\xbe|\xd1Z\xe5#\xcd\x82if\x88M\xdd\x1a\xad\x0d\x1cB:\xd0\xf2\xa5\xa8a\x99o\x01\xa3\x11\x1a^\x12\xb1\xbe\xea>\xa3\x19Doq\xb5\x81B\xb5\x8c\x16V\xd1\xef\xc3\xa2$\x05\xb0\xe9C\xc3\xb2i\xbeB~\x1f6A7K\xd7\xf6Eq\x15L\xa5\xf1g\xebK\x98b$c\xfc\xff\xe5\xcb\x90]\xdf\x9c\x9d\x1b2\xcd\x0bb4\xf7k\xb9\xb1ZK\xcfx\xbd\x93\x94Hm\x9c\x8eI\xca\x1fs\x92\x82r\x89l|\xee\xc3\x8e\xc9\xf5!C+F\x13R\"\xd9K\x93C\xc4if4/\x0dS:\x82\xa4\x9e\xf2\xd6\xb6\xbb\xd7\n\x84SJ\x8a\xff=\x0b\xc0o~\xff\xa7-\x02\xc34\xf7@\x13F\x04\xa0M\x08\"/\xdb$\x18T[z'\xc10q8 \xc5cM\x02\xefA\x9f\xf2\x17\xcb\xd0\x0cJ\x8b\xae` \x8c\x00e\x06\xdc\xe3cs.\x86\x1dy\xf5Y\xd9\xd2\xa0\xe7\x87\xd9\xb0j4\xba\xa4\xda%fU!\xca\xce\x1e\xc3N8g]\x87E\x98\x853R\x8c \xc9\xd6a\x9a\xc4bg0\"\xc5\xb4'\xa0\x8d\xbd\xe9\x95:*=\x84\x13\xe6\xbe\xef:\xc5I\xd9Z(}\"\xdc\xeee\xf2\xfe\x17\xcc\xe5\xeec\xcc\xe5\x8cP\xde\xbb\x01jo\xc2\xcb\xc1\x9e\xdeB\x0d\xef\x15\xe1\xe9\xb6\xfa1!W\xda\x1e\xfd\xea\xdf\xdf\xf3{\xbf\xbb\x93\xce\xbd\xbb\xe6nC\nn1hq\xd6\x8e\x16\xc0\xc12/O\xc2\xcf\xed\xaf+\xf9\xb5\xfd\xa9\xc4OIy\x9c\xbd\x0boH\xda>x\x94\x8f^M\xc7\x9b\xf2\xa5,\xcf\x87l\x11\xd2hN\xe2\x8b(_\x92\xb2\x8e\x0dj\xfc\xbc\xb5\xe5\xb7*C>\x05{\x8bf\xf5x4)\x9d\x10\xa2\x14F\\\xed\xbe\xe1\xa3\x82\x1f 4z\x9ag\xfdz\xcd\x0fN7\x07\xa1\xca\xaf\xea\xecaq\xcf\xf3 \xdb\xdclCr\x15\x82\xfb\xf53\xe1\xdb\x11\xbd\x04\xb2\x9f[[V\xd2\x99\x0b{\xcc\xbc+\xea\x80\xb5\xbe\xb4u\xabP)\xb7$EP~J\x96\x97\xf9'\x92\xd9\xc3\xef\x80\xa2\x11\x0f\xfb\xdc\xc5\x19_l\xcb\xa4\xc3\x1e\xf7\x0cb\xfd\x9a\xc1\x16\x9ft\xbe\x06+}\xfeK\xff\xe1a\x15^\xdb\xa2`r)\xba\xeb\xfc\xdd\xf1\x8cq\xa5\\%\xb6r\xa7V\xaa\xd4w\xbd\xa8=B\x15\x02\x8f\"\xc1C]\xc7a\xc3\x17\x0d\xf6j\xa3\xa9\xf5\x0f\xd3\xb8m\xc8IL\xa1H\x9d\xc30\xfb=\x85(LSX\x10:\xcfc\xc830b\xd4\x96\xcb\x8d{\xcew+&\xa20S\xd8\xf5\x02)x\xd2no\xd0a\x87\x08\xe0\xe2\xe6M%\xf5^\x1f\xa4\x96\xc5H`\x1f\xb4\xaa\\\xf4:\xaf\xd8\xb1\xdd\x7f`}\x9d1 S\x14\xd5\x15jD8\xcdW\xb8\xc0\xb6y\x1b\xc1!\x8dd\xf2\x97\xedr\xedt\x19\xae\x9c\x87]+\x10\xe1\xc8\x18\xd3^\xdd\x9e\xa1\xe6\x8eJ\xd1?\xc7\xd9\xf4\xfeun\xfcs\xbak\x83\xe4<[\x93\x82\x82p\xfbKsX\x16\xc9\"\xa1\xc9\x9ap\xefON\xdf.\xd3\xd6\xb9\xe9\x0c\xec\xfb\x9d\xfb\xfa\xe5\xd0\xadpd\xd4w\xdd'\xb8\xf0\xf4\xf5B\xd7\x1f\x0dE\xfa\xae\xe7:\xc7\xe3\xeb\xf7\xe7g\x97gz\xd0\xd1U+jA\xe3s\xd9%\xc8\x02)\xcc\x12\x8e\x99\xdc\xdd\xef_x\xae\x93L\x8bpA\xf4\x86\xe4S\xe0\x05\xa0\xcdS+\x8f\xc2\x12\xa0I\x10#7\x97ix\x07{\xe0dyF\x1c\x1f\xa3R\xecx\x0d;\x17\xee\xa4\xb0,\"\x96\xed\xaf\xe1:\xe4VE#\xc7\xe7\xa4(\x0dP\xe3/\xa3\xbf$Y\x9c\xdfV\x08\xc3\x0b\xf2%\xc9\\\x1e*\xa0H(q\x9d\x1fx\xd1?T\xc2\xec\xb7{\x1c\xbf\xfe\xf0q[|r0?\x1a\xbc\xba\xc2\x95\x14 \xde\xbe\x81bk\xeb\x8d\x07\"<\x8b\x12oe\x92L\x8a+\xc3\x8d\xa4\x00\xcc\xd2\xd5\x0e\xc4\xaecE\xa0\x1eP\xa3\xb6Zi-#\x02\x16\xa2v\xe9.Kq\x8e\xcf\x8f\x17N\x91\xa0\x03t\x1f\x9a\x9f\x85\x93\xd3I\x88n,\xd1\xfe\x04=\x9fka\xd4\xa5\xe3h7\xfb\xff^D\xfa\x17O=\xd7\xf9D\xeeJs`\xdf\xdd\xdd\xfe83\x96\x8e\x17\x82\x86w\xf1\x07w(\xf9\xe0~>5\xd9$\x17\x13\x871\x11\x05\xd9\xfaky]\xce\xc3\x82\xc4\xd7\xd7\x8el\xd4\xfc\x0d\xef\xfb\x1f8\xa2\\\x8e(\xe7#\xfa\xc7\xd7\xbe\xf1\xd8\x10\xab\xa38\xd2\xf7\x9b\xd7\x90~R\xbe\x97 |6\xf5M\x04\x99O\xf3wy\x14\xa6\x84\x9f#\xbe\xe4\x9e'\xb0u\x82~\x07\xd1\xa1\xacsVG]B\xbb\xb2\x02\xcd\"-T\x18;\\\xc34%8be\xe9F\xc2\x12\x19\x1e\x008\xde5#8773\xd8\x84\xc2\xab\x18\x13F\xc4\xf7\x9dl\xd6\xbd\xf0\xd2\xe2\xea\xf7\xd9\xffx\xb6\xf7y\x0f\xa9\xf4\xe2\xe5C{\xfb\xa8\xa4\xd2\xee\xeeK/\x98\x9a\x899\x93\x07\x17\x13\x9e\xea\x1b\x87\xf9\xbe\x07\x95a6r$V3!='5A\xeeC\"\x03\x84\xa2\x03\xb6\xf6foz\xa25\xdd\xecH\x87\xc6\xcd\x8d~\xcf\xb9\xea\xf5\x80\xf3t\xd74\x03\x18{\xbdw-\x19#b\xcf\x04\n\xcem3X(\x03_\xf2\x18B\x82\xa7!\x0d\xdf\x11\xc6XI\xa0\x13L\x8c\xa5\xf9\xf2Eu\xd4\x9e\x19$a?\x86\xb1\x8cW\x04\n9ju\xcf\xc7=)g\x95\xec]}\xaa\xcb3\x11\xd5J\xa0\xd1*\x11e\x13\xe8\x8eVc\x1d\xbf\x81uy\xfa\xbdY\xd4\xf0\xbdM\xce\xd9\x07\xbe F\xefd\xc8\xbf5W|k\xfc\x9b\x03\x9b\x90\xa1\xbf\xdb8'e\xf6{\na\x14\x91%\x85\x82\xcc\xc8\xe7\x96\xd3[\x01\x11\x02\xa9~\xdb\xa6f[\x14\xa5\xc5\xfd\x9b\xd3x\xc6\xc3\x1el\x07\xdb\x9aH\xc9x\xe2:\xdb\xc1\xb6\x03\x13r\xe5jnu\xaa\xa3\xd6(\x80\xef=\xbe\xe9\xa4\xb8\xe2\xf6\xb8\xb0am\x03z\x8et\xd3\xfcn\xdc3\xe0\x11\xc5\x8d\x8c\xb4\xfd\x90\xec=L(\xb27F\xac\xda2Q\x16\xa2\xad\xd6 \xc9M\xa0\x9f\xefx\xc1\xf4\xa1k\x9b\x07\xfc\xcc\xe7\xec\xa9|\xe1\x81\xa1\xfe\xf1\x15\x83.\xd4\x19\xfe\xa1Gtq\xae\x91\xc4!xAs@\xdd\x1d\xd4\x97'\x90d\x1c\x93\xac0f\x95 c\x0b|\x1c\x06\xd3\xd65I\x1f\xac\xb7\x97DH\x8cf\x84*\xfc0\xef\xb6\xd9\x8d\x07\x0fXz\x7fT\xdf\xa1\xcd\xb5\xfd\xddFs\x90\xdf\xc1\x1fc\xc2\x05iI\x9e\xc19\x89VE\x99\xac\x89\x94\xb8\x92\xcf\x94dq\x92\xcdZ\xc5\xc2\x15\x9d\xe7\x05\xfc\x9c\x84\xd1\x9c\x94i\xb8\x86w9-\x17a\x96\xaf\xe1\x87T\xfe|\xf5\xfa\x8f\xb3E\x98\xa4A\x94/\xfe\xd0\xaa#M\"\x92\x95\x04N\x8e/\xb5oz\xd6\xcb9\xe6\x82w\xa2\x84{r|\xe9\xf5\x949\xcc\x97wE2\x9bSp#\x0f\x9e\xee\xec>\xdbz\xba\xb3\xfb\xca\xd8\xe5\x9e\xaa\xde\x93b\x91\x94\x18\x14,)aN\nrs\x07\xb3\"\xcc(\x89}\x98\x16\x84@>\x05\x06_3\xb6L9\x84\xd9\x1d,IQ\xe6\x19\xe474L\xb2$\x9bA\x08Q\xbe\xbc\x83|\xaaW\xcf\xce\x11(\xf3)\xbd\x0d\x0b\x02a\x16CX\x96y\x94\x84\x94\xc4\x95\x1e/Zf\xc04II .\x9d\x13p.D \xc7\xc36c\x12\xa6\x90d\xed\xca \xc8\x9cp\x9b\xd0y\xbeb(\x9d\x83M\x92g\xbe\xf0s\xcdz(?\xa7\xc9\"\x11\x0d\xb2\xe28\x8b%\xd0\\\xaf{U\x12\x1f\x07\xe5\xc3\"\x8f\x93)\xfbOp\x0e\x96\xab\x9b4)\xe7>\xc4 k\xe9fE\x89\x0f%K\xc4\x05\xf4\xd9(\xb7\xf3\x02J\x92\xa6\xac\x86\x84\x94\xc6\x89\xa9\xfb\x8eE\xf0\n\x80-\x06\x15\xd3\xcbz\x05\xb7\xf3|\xd1\x1cgR\xc2tUdI9'X&\xce\xa1\xcc}\xbd\xfarU\xdd+\xb0\xd2\xd3>\x1a\x1f\x81sp\x01\xc7\x17\x8e\x0f\xbf\x1c_\xfet\xf6\xe1\x12~98??8\xbd\xfc\x15\xce\xde\xc2\xc1\xe9\xaf\xf0\xe7\xe3\xd3#\x1f\xc6\x7fy\x7f>\xbe\xb8\x80\xb3s\xbd\xe6\xe3\x93\xf7\xef\x8e\xc7G>\x1c\x9f\x1e\xbe\xfbpt|\xfa'\xf8\xf1\xc3%\x9c\x9e]\xc2\xbb\xe3\x93\xe3\xcb\xf1\x11\\\x9ea\xfb\xa2\xe6\xe3\xf1\x05\xab\xfbd|~\xf8\xd3\xc1\xe9\xe5\xc1\x8f\xc7\xef\x8e/\x7f\xf5\xe1\xed\xf1\xe5\xe9\xf8\xe2B\xaf\xff\xed\xd99\x1c\xc0\xfb\x83\xf3\xcb\xe3\xc3\x0f\xef\x0e\xce\xe1\xfd\x87\xf3\xf7g\x17c88=\x82\xd3\xb3\xd3\xe3\xd3\xb7\xe7\xc7\xa7\x7f\x1a\x9f\x8cO/\x038>\x85\xd33\x18\xff<>\xbd\x84\x8b\x9f\x0e\xde\xbd\xc3\x96\x0f>\\\xfetvn\xea\xfd\xe1\xd9\xfb_\xcf\x8f\xff\xf4\xd3%\xfct\xf6\xeeh|~\x01?\x8e\xe1\xdd\xf1\xc1\x8f\xef\xc6\xbc\xe5\xd3_\xe1\xf0\xdd\xc1\xf1\x89\x0fG\x07'\x07\x7fb}?\x87\xb3\xcb\x9f\xc6\xe7\x98M\xf4\xfd\x97\x9f\xc6,\xa957\xa7pp\n\x07\x87\x97\xc7g\xa7l\xcc\x87g\xa7\x97\xe7\x07\x87\x97>\\\x9e\x9d_V5\xfdr|1\xf6\xe1\xe0\xfc\xf8\x82\xcd\xde\xdb\xf3\xb3\x13\x1f\xd8R\x9c\xbdeY\x8eO\xdb\x9d>=\x1d\xf3J\xd9\xaa5\x17\xf7\xec\x1c\xdf?\\\x8c\xeb\x9e\x1e\x8d\x0f\xde\x1d\x9f\xfe\xe9\x82uH\xcd\xacC\xcdv\xe3]\x9e%`!\xf7\xa5\xf4\x02\x92\x8c\xc1g\xc4\xe3\xfc\x8a\xf3\xb5J9\x12\x97$\x8d\xc4s2\x1b\x7fn:\xf1S\xe2oAS\xc7\xdd\xd88\xea\x874Z\xb6q\x10R&AE\x04\xaa}\xf9\xab\x0e\xca\x00#dI\xa8\x12\xa6\xc1XU\xa5x\xc26<\x1a\xd0\x19\xbc\x92\xf7w\x95M\x89\xa7\xb2U,\xc1E%\xa4\xcbdA\x1a\xd2.k%|\n\x1b\xd5\xf0$\xa3ZVK\x17\xebCF>/I\xc4N\x992\xa1+\xe1\x83e\xd0\x8a\xe4VI\x97\x14\xd3\\_#o|}\xedT\xf7PUh\x99\x96\xb0\xab9ak\xe1\x94\xcbH%\xda\x00\xc1\x10\xe0h\x17\xad\xccd\xd4\xfa:\xd0G\x1d g\xe7\xaa\xd3\x96\xc6R\xefS\xaf%\xab\x9c\xec\x18\xae\x14\xe5M,7\x9e\xec\xce+*\xe4jz\xb5N\x1aZ$\xf3\xeb\xf3\xaa\xbc\x0f\xbb\x06\x9d=k\x14M\xc3\x04\xa0\xf9]%\xe0\xc4\xb7\xa6~\xe0\nidA\xb2~\"w\xa5\xbb24iu\xa1\x0f\nc\x84\x12\x9f\x90\xfb\xa2G\xe1I\xee\xa2gz\x1e\x19$T\xc1\xc2\xd0S\xd2\xe8\xa9\x8c\x9c\xeb\x86\x93\xb2\xba\xf54h6\xaay*\x90%f\xeb\x06\xf5Y\x0b\xa5\xea\xc9\xd0x\x8cm\x03\ntN\xd5\xdd\n\xa8\x8b\xa2\x85G\xaf\xee\x83\xd9~i\x8e\x0c\xa35\xe5\xe2\xba\x97\x8bw\xb3F\xa2\x90\xf9\x8a\xb7\x04-\xd6\xd5\x94\xb6\xf7-\xf5\xf9\xea\xf9\x90[s|E\xdd\x96\x11?\x06\x9a\x13\\\x88O\x86\xd5\xa3\x8d\xd5\xa3m8\xa3ze\xbc\xd7\xbc\xc2f:\x0f,l\xec\xa0!d%\x1bMhA1\xcd\x80\x94\xcf=\x11Oq\x10\xbf|\x1f\xa5K\x9b\x00\xbb\xbd\xf4D\x89\x92\xc4\xd6\xd6b\x94\x88\xcc\xba\x01u\xb4\xd4{qZ'W(\x11n\xe7\xcf\xb8>\xba\x1et\x9a=\xea\x8e\xa7\x86\x1do\x0d7,Q6\x9d\xe4\x96\xbdc\x0c\xb9\x94\x08\xffqO\x9e\x98\xa6\x85\xf1\xf7[\xbb\\\xc6W[\x08M\xf2+6\xbcb\x92_a<\xf7\xc3\xa4\x88ViX\\90\x92\xa9\x04\xb3\xf9\x90 \x97\x0e;\x08P\xe2\xa3!\x00\xaa)\n\xac!\xf6#\xe56ih\x9f(\xcc\xd3D\xda\xd0\xf2\x0bR\x96\xe1LV!\xdf\xf6\xea/C+*i\x18}\x12\xd5\xf0\xdf{2\xd5P\x85\x14\xc57w\x04\x03\xf0 \x06\x922\xde\x06\xe1m\xca\xe4\xad\xf8\xc2-?\x84\x1f_\xe0~\xd5\xf2\xecn\x91\xafJ\xc7\x83Mpp\xfe\x1f\xacP\xf8\xfd+\xf35\xe3\x0bc\xc8#\x96n\xf2|\xcc\xd2\xf5k\x80\x95H\x7f\xed\x99\xcc'K\xbb\xd8\xc9\xa4\x10\x8d\xda8J\x84\xbb\x1d\xae\xf0j\xd0\x9d\xe2zS\xdc\x19? \x0b\xd7{\x03\x9b\x9b\x14~\x80\xcc\xa8S,g\xa2\x1do \xa4\xec\xbc$\xd4-0\xfeW1\xd9\xbd\xb2\xe9\xed\xd6\xbf\x14\xa5'\xde\x07\x86\xac\xfdF\xb2P\x8f\xc2`\x1ceS\x15\x9em\x94f\xe2{\xe9\xf9\xe0\x9c\x84K\x9b\x10x\x90V\xbc\"Un\x85\xd0\x13\x10e\xf1\xea\xf8\xc2\"\xd2|\xd1\x12\x81\n\x88\xda\xd5E\xf4\xa5H\x7fi\x84\xb4\xd4\x0ei\xc2< \x0ei\xc8\xad\x140\x1a\x99\xd1\xca\xaaL\xfe\xce\xf1\x05\xfbaX\xf4\xd4\xb0\xe8\xb9\xdfH\xae\x16=i\xa6\xf3E\x0f\x9b\x89|\xd1W\xcdD\xbe\xe8es\xd1S\xe3\xf2\xa8C\x1e\xacN\xdb\xf0\x9b\xb2\xb5\xcb\x1d\xa7\xd0\xca\x9c\x98\xeb\xdcK\x1f$\x9b\x9b\x19\xfc\x00\xc5\x1b\x0f\xc8$\x87M\xc0\xf81\xed\xb05\x92o\xd3\xe6l08\xbdx\xaa#\x1c\xa1\xf2\xfcZ\x07\x1bcL6\xa3\xaaS\x0b\xda\xba\x84\xc4m\x18\x0c\xd5\xe0\x8a]\xec\xb9\x8a\xb1\x90,@B\\Q\x1e(\xdc\x90\x1b\xb6[E\xc7Z\x8dj\x10\xb8V\xbe\xaf\xba\x03\x1dF\x83\x9a\xf7\xf4\xea\xbe\x8b`>%\x9e\xebkcZ\x83\xf6t'\x9a\x97\x8c\xf6\x14'\x03\x16\x0eq\xd37\xaa\xb6\x08u\xc7A\xab\x99\xb3\xaf<\xe8L\x15E\x15\xd56\xb8\x87\x92\x8dU;\xbd\xd9\x9ey)\x06!\xed\x0e\x1b\xb1z\x95\x9e\xe9\xab\x015\xf2m!e\x90\xbaB\x16\x8e\x08\xffl\xd0 \xcbcry\xb7D\xd2\xc9d\xfe\x88\xf7Af:\x92;\xa4\xc7zH\xa3\x1e\x83\xe9%\xdfW8\xbb\xd5\xd4\xec\xf1\xab&\x19t^\xb0&&\xbf\xe0l\x1e\xdd\x15\xec\xc3*HJ-7\xb2\xd4\x9a\xde{{\xfeAgPv\x9f=\xf7\xaa\xcb\xd5!z7\xafwv^\xee\xbe~\xfd\xf4\xfb\xe7/\x9f\xef\xbc~\xbd\xfbP6\xc5\xe4\xbf\x1d\xe7\xf1\x0f\x8c(\xc7_\xff\x81\xbe\xf1\xb93\x02\x02?\xec)\xa2\xb0\xfek\xb1{\xf5\xa6\x1b1I\xdc\xde\xba\xd4\xed\xe9\xceC\x80\xfb\xe9K\x9d\xc0\x04\x01\xdd\xdf\x08\xc1l\x13\xe4\x8f\x00\xc1\xd5NH\x1a\x10\x8cU\xa3\xb9cDJ\x83\xc5\x9env\xd0\xca\x00\x9d\xf7\xe0 \xe5]u\xeb\x05\xf9\xdb*)H\xe3\xc5uV4I\x1d/`\x03\xb3xb\x01U\xae\xfc\xe5\x8b\xdc\x8e7 \xdeD6^du\xc6zz\x02[}u=\xfbf\\=`3v(W\x99\xaf\xd6[FT\x0c\x04\xb6?\x06_>N\xdc\xfd\xd1\xe4\xffL>^]}\xf7\xc5\x9d8\xbf\xbf\xf2\xdc\xfd\x91\xbb\xbf\xf1q\xd7\x9b\xfc\x9f\x8f\x1f\xaf\xbe|\xfc\x18x\xdf\xed\x7f\xdc\xf5>\xea\x81Yx\x00\x98\x8f\xb7\xdf\xfd{oH\x07\x8b!S\xc3\x8eI\x17\x8bV\x92t\x01\x98F\"k\xc3\xad\xb0\xc7\xc6\x1ed\x08\xd4%R1JB\x158B\xa64\xdc\x0em\xa0F .?\x8f\x05\xc2\xa3\xc8n$\xea\x9b,A\xf9\xf6H\xa4\xd3<\xf7^\x86\x0e\xf7BD\xf7\xa4\x1f\xcd\xf2\"A\x99pm\xd3\xcaE\x17\xf5\xc1\xb9\xbe&\xe5I\x1e\xafR\xe2\xe8\x1a B\x1bAU\x08AC\x9b\x05Y\xe4\xc9\xdfI|\x11.\x96)y[\xe4\x8b\x8bhN\x16\xa1\x90*\xf0\x8f\x87\xa8,\xf8\x97\x93w\xe3\xcf\x98\x8d\xb3\x10\xf8\xf3/\x8bT+\x94dSR(\xefe\xbbfq\x00\x824\x81i\xd4\xac(z(\xec\x98\x89\x1b\x0b\xdd\xcc}\xf1\xfd\x0b\xcf\xb0\x0f\xf0\xd3\x8b\xd7\x9e\x91\x97\n\xed\xeb\x83\xa0\x10\xd4\xf3(T\xf5\xdaXKFF\xd0\xddZ\xfd\xae\xfdk-|\x19\xb6+\xe1\xa2\x99\xe1qm\xa5,\xa7\x95\xc7\x10F\x8bg\xbd&\x8b0I\xef\xd1\xc2\xaa$\xc5\x1f _\x8c \xca\x17\x83\xda\x12\xfdb,(\xd9\xa2\xc9\x828\xc3[t\xe5\xf5\x95\x17\xd0\xfc\xf8\xe2L\xa8\x84\x19\xf8\x02\x83<\x05\xd1\xc4\xf0\xb6\x06\xc5u\xe3\x95^O\xd3<\xa4\x8f\\u\x92Q2{\xf4\x0e\x0bT\xd8G\xff\x83\xb2\xca*\xf6\x94\xb88\x10 \x8dW\xad\xf2\xa5\xdd~\x13\xdc\xdb\xbcLw'\xa4\xcc\x82mt\x17\x9d\x0frr%\x99\xdeyF\xff3 \xc4f4h3a\xf2AO6\xc14/\x16\xa1\x812\x02\x81\x12V\x13\xd4O\xbcv`\x13\xb8\xa9\xcc\xca\x18\xd5S\xc2%\xf6.)\xdf\xae\xb2\xc8s\x13\xc6c%\\O\xda\xf9\x90}\xca\xf2\xdb\x0c\xb5 \x85K\x1b\xec]\xd7\xd4\xa46\\Xa%\xcb\x0d\x93<2[7\x89\x7f\x00\xa4\xa3\x15U\xd6\xfa\x8ep\xf7\n\xf6\x9b\xaf\xa3\x96)\xa8|r\xd3RP\xcbR \x99\xd9\xb1\x14\xca\x97\"P\xe1\x8035V\xb3Vg\xaa9\xef\x1c[\x16\x00m\xce\xb26\x844\x93\xcf\xa2\xe3\xdb\x0c\xc9\xb0\xcf\x0bC\xc0f\xf60\x1c6\xc3;j\xf3\xf7\x1b\xfc\xbe,\xc841x\xb4b\xcfuU\x03F\xab5g\xba\xe5S\x9b\xad\x16\xe6\xef\xe3\x8aG\xb6\x1c\xe0a\xc7\x01\xceN\x90\xd4C\xa8\xfa\x97\x9c\xe2a\xdf)\xee\xb2Y\xbd\xc3K\xff,\xa7\xe1\x8cM\x8e\xc3\xcd\xa5\xdc\x1b\xd8\x87\x1bF\x96\x8f\xd0>\x16u\x01\xee|\xb8\xe6\xde\xd2\x17\x13\xf6\xdd\xf9\xbcH\xb3r\xc4\xce\x8e\x1b\x96 _\xd1_\xc1\xb5\x85\xc0Q\x0f\x05\xc48\x91\x0d\xf9\xb2\xdc\x11\x83\x07\xd8\x03\xfe\xff\xcb\x17\x98qK\x10\x9f\xa7HU\x0d\xe5\x85\xe5\xe1P\x023\x11\xa9>\xae\x88\xbf\xf5$\x93nn\x9b'\x04\x9e\x0d\xd3\x81ns\xe5\x13\xc9\x1d\xc8\xfd\xb6\xb2\xca\x85\xdf^v\"\xe4V\x9d\xa6\xd6\xf94g\xad\xcf\xef\xdd\xba|\xb6\xac\x8b\xfb\x8d\x0bs\xaf\xf6E\xaeV\xa6\x01\xe4\xb6U;\x91M\xfd\x85\x99\xdc\xee!\xa7\x0f\x199\xad\xec\x19\xb4$\x95\x1b\xf0\xc2N\x9d\xb2\xbe]\xe8q\n\x0e9\xde\xd8\xb8\x98\x1c*\x84\xf7\x97/\xb0T?\xd4$7#\xc6-\xd3\xd5h\x87\x95\xe2H\xa2\xfa){(\xde\x03\x06\xb3h\xa9\xd2\xb5l\xf2a\x03\xff\xd4R\xbc\xc3\xba\x90Jc\x9d\xad\xde&;Wv\x96E}\x0ed\xff:\x0fm\xfd9\x93\xa5\x04D\xd91\xbd|\x16\x93j\xd4\x12\x1d\x1e^UG\x16\x92M\x07l\x04\x07\xd04\xb5\x9dN\x0e\x91\xef\xc1\xff\xcdOg,\xfd\x8c%~b\x7fJ\x9c\x8b\xee\x85\xf9\xdaw\x80\xc9\xa7\xd9\xd9=hw\xbe\xe1\xf3H\x9dA\x8d\x18\x94\x03p\x1byx\xba\x05\xce\xd5\x87\xad\xfa{d\x99.\x86\x15h\x82\xc7{Tw\xe5;\x05\xd1\xa8pa\xf0^\xa2[\x8e\x04\xde\xf7L[\x17j\x94\xcc\xa4h\xa8\x0fQ7\xa9\xcd\x118\x07\xd9\x1d\x9d\xa3\x0dT\x98\xc1\x0dAc7\x0bU\x80\xe1Q\x86\x9e\x08zC\xa5\x8doeH\xee\x11\xcf\x99\x018R\xcc\xdc\xb8 \xffSv\xd4W,\x15&\xcd\xd9\xf9\xdbB\xff\xb7lQo9WV\xa2]\xb8Xa\xc6\xe1M\xcc}\xb7\xf6\xfb\xab\x0fcV\xd1X\xef\xfaW\xe3=\xc8\xd4x\x89'\x05\x8e\x11\xff\xda\x84R\x86\x0d\xb3\x86\x9c+\x97x\xc3s3\x93\x19lL\xa24\x94\x81{M~\x0b\x92,\xc6\xc0*\xceG\xaa\x85c\xd3\xaf\xe1\x00\xcda;.\xa5X\x7f\x92\xba?\xd3\xbe\x1b.-\x7f\xda\xaf&Q\xcd][t\xcf\xd5\xf0\xc8\x9aq\x87\x95V\x9ex\x15\x87\x05O[\x84\x9f\xabxrU\xc6Fb\x85\x1b\x95 hw\xc1`\xd7$\x85\"2OCl\xd8YY~?\x8ds\xd5\xd8\xa0\xbb\xe2\xc4Z\xb1\xeaz\xc5\xb0\xd2\x0dGY>d\x01\x06W\x19/\x12\xca\xdd\xdcc\x9a\x12\xac\xa3\x9ayy\xbb\xd8\xf8\xaaMz\x9dG\xac\xfeI\xf3\xfb\xaeV\xbe$z\x0e\xbb\xd4\x03\xa9&\xe5\x06\x9b*\xc6(D\x06\xa8\x10\xbe\xebL\x1e\x152X\xacJ\xca\xd0g\x08<\x1e\xf2\x9a\x88[)\x8b\x1b\x05#\\\x11\x0eo\xf5\xcc6GD\x16 \xed\xb7\x9f\xe7\xfe\x8f|X\xf9P\xfa`\xf0\xc4\xac\x83\xb9\xabm\x03\x0c!'\"\xe5\n+\x1c$\xc4\xd4l\x01~F\x05'\xb7\x9d\xce\xd5\xd2\xda\xe9\xd2\xd0\xceDo\xb1\x9e\xa1\x8b#U^\xe3\xa9\xc6oc^5\x9f|\x03\xcd\xc3F\x1f eZ\xbe.\xbf\xff\x90E\xe1j6\xa7>\xac\xb2rI\xa2d\x9a\x90\xb8\x1a\x1bv-\x00\xf7\xf7\xb0\x89\x0e\xa2\x1d\xcf\xe4.\x84\xb7\x17\x05\"j5\xa7\xde\xa3&\xdak\xcdq\x82^\xa2\xd4\x19\x98\x90+\xbb\x92\x05\xd7\xc2\xc8<\x0f\xca\xdb\x04UXt9\x97i\xca\xa2\xb0$\xb0k\x8e\xf4/\\\xb0\xa2[t3\xd5\x82>\xa4\xdb\x9f\xb0\xd2\xa7\xbd\x95\xfa\xcdu\xba\x7f\x13\xcf\xee\xd9\x84\xfa\xf6\xf4\x9e\x0d\xca\x9b\x7fc\x99UE\xd4\xf7[\xe1\xb1\xfd\x18.\x97\xe9\x9d\xe8\xe0J\xd7{\xad\x84\xf4\xb9k\n\\\x83,\xd4\xfd\x1a\xc4C/\xc5\xeb-n\xda\xe2y\x95^t\xc9C4r\xc7\xe5Pnnz\x90N\xca+\xad\x8bF\xfc\xa3j\x954\xb1L\x18\xc7J\xcc\xd0N\xe5!\xb6\xe3\xc26$oX\xfc\xce\xa4\xb2\xda\x1aYV\xa7^\x17\x96\xecAU\x0d<\x93\x91[5\x02)~cx\xd3u\x94/\x0e\xfa\xff(\\\x1a\xc8.y(\x90\xaf:8\x02\xaaU\x94\x04\x08/\xa5\x9f\xf6\xae\x074\x87$\x8b\n\xc2\x90\x0d\xfa\xb7\x08\x9c\xd6\x92J\xe4\xea\x9b\xe9/\xd9\x7fZ\x84\x11\x1e\x82\x8d\x04\x0cL\xd7u^\xe7h\xe6\x00\x1b`\x15\xb9&<\xfa\x8du5\xd9\xc3\x03\x88d\x12\x83\xee\x83[\xfd\xdec\x8c\x8dyU\xd0\x08[F\xd8J8M\xf0\xad\xeb\xd4\xbf\x13\xfb\xb7\xdaA\x9a\x0e\xe3\xad\xd6F\x07\x81\xad\xed\xd1\xb3\x156:\xc6\\\x15\xe5\x9ci\xeb\x8ax_g\xf4\xd1\x87\x98~\xe6>y\xd2\xb9/\xda]2\xb7f\x05t\x8a\x0e\xc8\x1a#\xd6\x97G8\x02\x90K\xd8\x9eh\xa3\x0d\xb7J+\x19\x8a\xe8\x8dh\xf0#cC\xaa\x0b\x0eF\x9e\xa6\xb0\xf04\x96\x93!\xb3\xa1\x03\x83\xc6\x04N\xd0\x9bjo\xbc\xb1W:\xa9\xf6\xcc\x16\xb4\xf8\x0e1\x13]\xcbh\x03\xeat\x10,\x9b\xc8\xd26\x8d\xc4\xdd\xf1\xea\xdbx\xbfE\xfc\x19(?I\xe3\xc3H\x8b\x16e\xea\xeba\xbe\xca\xba\x05\x02:\xbboS\xae\xa0\xed\x85m\xc3YRy\x94\x14\xd3`q\xa0R\x87+\x96\x16\x9c\xfd\xf8F\xe3F\xec#4\x1c\xe6\x95\xbaJ\xa3T\xbfI\x80n\x0cD5\x0f4\x99\xfbl\xe7{\xcf\x0b.hA\xc2\x85\xa0H\x82s\x12\xc6\"\x02\x1b\xbe\xffR$T\xbcg\xee\xee\xeb\xefQ\x80y\xb4Z\xa6\xe437\x80\xe3)\x97E\x98\x95\xd3\xbcX\xf0\x8aww0\xf5}X\x96\x97\xf3\"_\xcd\xe6<\xf3\x8b\xe7\x83LMz\x1d\x01\xf28_&T,\xdc9>\xdf\xf1l\xf4\x9fA\xd7\x1e481II\x12\xc6|\xa1|\x84\x07\xaa\xe0\xa7PF\x8b\xbbf\xd24\xc9\x92f\xc0E\xdb9\xbd\xd19\x07\xfa#-\x0f\x08o\xd4~\xb6\x93F\xaf\xec\xf9\x04R*\x8c\xe6\xfb\xea\xb3\x16^d\nd\xe0o\xc2\xc8 \x82P\x1f\x1a,\xb9\x93\xc5\xe8fk\x8b\xf1y\x18v\x1d+`3h-k\xbe\x07\x02\xac1\xca\x8bO$>'\x7f[\x91\x92\x96o\x0b\xf4\xe9mJ\x96\x8bDP/\xcdPlO\xd3\xdb\x92\xcfW\xee\x91\xa5\xf5\xedk\xc7\xeeV\xb7\xd3]\x9b\x0fYq\x11\xc6\x06\x0dn\x8a\xfc\xb6\xe4\xd4\xcb\xc4Y\xef\x04\xbb;\x8e\x0f\xec\xc7\xeb\xc0\xb9\xaa]\x81\x04kR\x94I^y\xf9\xf0\xe1{\x8fk\xd2\n{\xda\x04\x87w\x99\xe8KpW\xed\xd3\x0b\x1a\xa2-\xfc\xac\xdd\x9dT\xd8\xad\xbc\xd0\x8e\x954H\xb29)\x12\x81\x15^\xed\x1aX\xaa\xc8h-\x02(|\x12z\xa6#\xdc\xe0\xcf\x06\x99IL\x05\xfe\xd1=\x0e\x80\xd4uvw\x9f\xefJG6\xed,\\u\xebC\x92\xd1W(i\x025`\x8d\xd7R1e\x03\x98\xfb\xa8\xa1\xc5\x1a}iE\x0d\x0b,l\xf983bg\x10\"6\xee\x82\x8a\xa3C\x0420\x84Q\x05e\x1fSU\xf6k \xd5\x11\x99\xf0\x8b\x8e\x93\xd9\x15\xfc\xeaz\x7f\xea/\x10\x19z\xb7\x0f\xbb/`\x04\xbb/\x9e\xbdzn\x99\x85FW\xd0\xaa\xf4\xcb\x17A\x0c\xe7\xb0\x0f9\x8c\xc4\\\xa4\xf5\x87\x94Q$)\x8c \xf2\xcd\x95\xd4\xb1~\xdc\xf6w\xafF\xe6az\x18\xa62,\xa7/\x0f\x02\x12\x1f\x15a\x92\xa9\x89\x1c\xe7i)\xcdr\xfclh\xa6\xc5\xa4\xa4E~'\x12\xcd+\x82\xf1\xf99\x7fE\x82\x98Dy,\xa2\xc9\xd8N\xaaF\x1eVxZ\xb5\x86B\xb2q\x16\xe5\xa2\xb7\xa4\x95\xf6\xe5\x0b8+:}%\xe5I*\x13\x87 l\xc5\xb5\xa1rD\xab\xe4)\xef\xb2HJL\xd8\xfb\x0dn\xe5\xf7\xdcZW+\x9cg\xa8\xff\xd2\xab\xb8\x0b\xedC\xb3\xef\xc4\xe4A\xdc\xaeoU\xec\xd8\xad\x84RpY\xf4]\x16u\xe7\xe3\x81\xe0\xb0\xe3\xd1\x8d\xfd@d\x14c\xff\xa8\xe4C\xb4\xb9%\xb2\x81\x8a\xc6 \x15\x7f \xf7\x1eII\xe6+\xbf\xd9\"X\x1b\xf9\x8a\x871\xf5\x0c\xc4\x87\x99\xa6\xd2\x9f\xad-\xe5x\xf71r\x80[\x9fJn\xeeC\xe1\xf9\xca9\xe5^\x08\xa6\xdco\xad\x03\x97\x9br\xb9\xa8\x14\xa9\x12\xc1\xd8\xf3+,V\x19\xe3\x15\xdc\xdc-\x1e\\\x81\x0f\x17\x1cT\xecZ(\xe89\x8aO\x00es\xd0A\\\xf5+\xf8\xe0\xad\x01\xec\xc1\xd8\xd5YD\xfd \xf1\xcc\x90{\x07\x7f\xb7\xb6 C\xde2\xb9\xa2dX\xea-gB}\x8cfZ\xba\xd78\xcd\xfcj4gsv\xed*\xef\xf6\x91\x1b\xbfXi!\x05\x01\xa8@Y'\n\xf8kl\xfa\xba\xdb\x8d\xfciX\xd2\x1f\xbb2T`\xa6\xd4\x88\x8a\xcem$\xaa\x03\xc2\xae\xb9\x03\x92\xdf\xdai`-\x8d<\xcc\xc8-\x84\xfcf\xb11\x016\xba\xe0\xce\xbc\xad\xb9\xe6s\x930\xd8p\xe7\xfc\x12\xec\x8ew\x00\x8d\xbe\xd9\x8f\x06-\xe05\x1c\xa0\xdeY|\x9f2n\xf6V#\xfaX~N\xa6(\xe1\xa2ok\x0e\x0e7\x08\x9e\x94f}\x0c\xbe\x86\xca\xc5\x87\xc4\xcb\xe2\x8b\xed\"A|^\xeb%\xd7u\xd1\xb5\xbd\xac8\x01\x95\xc22e\xaf\xfej/\x8eg\xb4R\x98\xbf\xef\xc9/\x9e\xe7\xc3T\xb9-\x1e\xb4\xa67M\xa4\xc8E\xe9\xc6k\x03\x15\xec\x19\xfaP\xf6F(_\x05>\xc7\xcb\x03\xe5\\\xc4\xa8+r\xa6\x18\xe6\xa4\xf2$\xe4a\x87\xf9\x17\x97\xb7^\x7fSk\xd9\x1d4\x9ake4\xa6Ad\xd0\x17\xf0Q>\"\x06\xa3<\x83\x9e<\x01\xaa\x10C\xb8\x06-\xe2Hb\xe4\x98\xa59\x06,\xfc\xd5\x15\x07\x84\xc68\x16n\x8d\xbb\x07\x8d\xf3\xd6\xdawj\xa4?\x0c\xb6\x0c\xeb\xca\xb1\xb2\x86:\xcc\xb2\xa0j\xf9PD\xcfo#\xd8\xc9g\x9b\xbf\x8a\xf87b&;\xc1\x91\x8b\xcd\xcd5\xf4\x8a\x0e\x83AtZi@l\xe6\x93(\xa9e\x05\xe6\x0c\x95R\xf4\x8a\xa3\xcd\x92\xcf\x1b:\xfd\xcb\xf1\xc6\x82k=\xa1w \xbc'\xc3\x1c\xbb2\xd0'\xce\x86\x0f+\xd8\xdc3\xc9\xd3\xd8\x93\x07a\x9a\xf2\x83\xa0\xe4^\xd8\xe4\xee\xe3;\xa6\xf2\x92\xe6\x83\xe30\xd2\x82\x1f\x00Mx\xd9\xdc\xc4\xac\x1dG\n'I\x18\xb9b\x11\x0b$\xa2\xaf\x89*\xe7\xf1\xecb\x04qN`?l\xe7L\x1b\xd6\xbb(\x08)&\xee\x94\xc8T\x9c|\x10\xcdW\x99\x85\xd1\x92\x0f\xea\x0b\x05DP\xf6\xddy\xb99r\xbf\x88\x87\xc1}\xb5B\xbb\x88\x99\x1a\xdc\x1c\x8c \xad\x16-\xf5\x19\x036\xd5\xc0\xc1\x0b\xae\n\xb9\xa3\x81S\xdau\xf4\xca\x83\xbd\xa6\xb9\xf9\x1e\xb2\xd4ZW\xa9\x87\x0bhn\xa4Z\xb4\xc8H^\x86\x06fM\x07\x9d\xc2\xa7\\\x8f\xb4\xbc:\x85*\xf1\x96\xb6\x07xx\xf0\xc9\xd5\x1b o<6\x0c\xb4=\x92\xa28\x9c6\xebJk\xe1\xe9\x0c\xc2\xca>A~\xb7\x171\xb3s$e\x1e|p\xf8pZ.\x92\xf4gF\xe8\x08\x0d\xad\x84\xc8\xb5\xdbI\xa3\xfe\xa8\xb7{\xd5\xd4\x1b\xdc\xda\xa8\xcfW\x1f\x1c\x8d\xe9\xe6}\x85\xa4\xacE\xbfBYI\xcbX//\xe3nH\x18\x07\x8e\x0f\xce\xd1\xf8\xfd\xce\xce\xce3\x8b\x8f3ho\xf0*\xb9\xd7\xfd\x99\x85E\x10\xb1\xb4\x9e<\x11\xbf\x82yX\x1e\x0b~\x0bl\xa1C\xa5\x9b\xe8z\x99&\xed\xd2Wh(\x07{\x03s\xfb\x16X\xb8\xf3\x0d=\xeb\x08\xe0\xd5/O\x92Z\x90\x1bsU\xdf\x94\xd4\xfc&\xdb\xed\x9c\xe3\x92\x0e\xa6\x9a\xbc\xa4\xc2\x8f\xce\xfaN\xcb\xaf\x88\x85\xe6\xbd\xe2;y\xce5\"\x9c\xb4\xee\xe5}P\x15G\x97\xc9\x92\xf4a\x07.\x01h\x1e4uP\x90\xc30\xcbr\n\xac\"\x1f\xd8\xafB\xdcp\xea\xac\x88\xd6r[$i\xbf\xa3C\xb2\x9e\x1b\xf0\x1b\x18s\xbb\x8d\xfd\x86\xc1#7\x88\x0b\x85\x8d\\\xa5\xab\xd01:W\xa1_V\xae8\xdd\x02\x17\xb4P'4\xb6\x1fi+$\x0d\x94\xe2\xdc\xed\xaa;L\xf0**Y\x06\xd3\"_\xe8\xf1\xe3\x00DH\x05\xcb\x16D\"\x85\xebWpT\x8dT\x18\xe3\x0b\xf6\xf1U\"@FmsEX\xbc\xe1\xd1$\xd3\xcd\xdak;\x86\xac\xaa}\xe1\xf9\x90\x0b\xb9\xfb\xfe\xb0\xb3[R\x03\n\xc8\xf0\xa5\x0f\xa7\x94\x14@\xb2\xd8\x16d\xd3D\xdd(G\xb4\xc5y\x86\xd8\x8b\x19\x9e\xdc\xab\x16\xe7m\xe7\xd2A\xb9\x9e1Y-\xc9'\xb4\\$\x80B\xdc\xd4\xa4\xf2>\xf7\nN\x1az\x80'\xe1\x1dn\x15>\x11\x98\x1bQ\x0fF'+Q_\xc0\xf1\x8c\xd1\xa3\xb9,A\xb1\xa3\xc989\xd4\xbc\x8er\x0dm\x1eg\xeb0Mb\xc8\xf2l\x8bW\xbb-N\x1a\xe4s\x1c\x0f\x95\xc5\xb9/\x8e\xe6\xbc\x87\xcdy/xJ.\xf9\xd0v\x10\x10\xb9\x069\x97\x99\xf2\x00\xd2n\xde$\xc0B\xc3\xde\xaf\xa4A\xb6\xf5AU\xae\xdek|S\xd5}\x078\xd1o\xf4\x8c\xd7Axw#\x17E\x8b[\x82{Jl_\xda\xe1\xc2G>F\xf2H}\xbeVz\x18\xf6\x8a\n\xee\xb2\xa4\xda\xa0\x8c\x88\xcc\x95\x0d\xcf\x15\x03,\xce#\xcc|\x9e\x94F\x18\xf8\xce\xc2\x18\xb9@>\x95\xd8j\xd3\xaa\x1b\xc9\xeaF\x0b\xb8:8\x12m\xde\x0c\x9a\xcb \xed\xfd\xa6\xeck\xa7\xc3GR-\x18\xc4\xed\xc1\x05\x0c}p\xc3=\xb6\x19\xd8Z\xfb\xfc\xdb\xb8\xe0n`\xc3\x1d7\x02\xc3\xcd\xbb\xfaH\xb1\xc2\x08\xf4P\x84\xda\x83\x07\xce\x08\xb2\x1eY\x85\x90<\x8c \xe9\xce\xc8v:\x8fgo\x07M\x1f-\x86S)\xca1O\xc3\xc8\xc8\xe4\x1b\xf3Z\x85<\x9b{\xd0vs\x06\xb5\xa4G\x95\x94\xacj\xfc\xd1\x89\x9e\xcb.\x8c\xb5\xf2A\xa2\x8cvL\xa0& \xc3\xa0j\x10\xf1\xa4\x11\xee\x1c\x1a77\xbb\xea^eCjo\xf0l\xcdV\xda3 \x1b\x16H\x9e\xbflm\xf9\xca\xad(:\x82\xac\xef\xcb\x14\xa9\x07\xbe\x19o\xcf\xda\x02\x13\xbc=\x93$q'\x11X\x12z\xd4\xba1\xef\xa6\x95\xd0\xd6\xd2\xe2\"O\xb8\x99\xa2\xf9\xbb\xfc\x96\x14\x87a\xc9\x8d,6\xdc\x893'\x9f\x19w$\xee\xdd\xd9\xff-\xfc\x11\x96Q\x92\xb0\x1f7I\x16\x16w\xf8+,\xc9\x8b\xe7\x98+*\x9f\x8a\xff[OE\xb1\xdd\x17\xe8k\x17k\x90\xbf\x8b\xf0VQ3r l\x82\xe3xZ?P\xcf\xa8\xb2\n\xd0Ng\xe9`\xb2\xde\xf3\xe8d\xb2G]W\x83+\x83\xf2\x81I3\xd7\xca&5X\xe6[\x93\xda\x89\x91\x83&U\x9c\x83\x91\x91\xe2F\xae\xba\x97\x93\xee\x18W\xe3\x80h\xef\xdd\xe6\xe8\xbc&\x84]\xdf\x87\xcf\xc8\\\x85J\x15\xd7C\x1e\xe3\xc4\x19\xb1\x96,\x96)Y\x90\x8c\x92\xb8\x87\xb5\xa9/\xe7\xb8h\\\xfdF\xb2x`g\xaa\xbb\x8c!{\xdb\x1a\x90 \xa9\x02\xc2\x055\xe2\xeeW\x11\xbd\xdf\x8b\x99\xa8\xcd\xbf\xa1\xe9$\x83{\xa8\xaf\xee\xa8\xa5\xcc\xabP\xf1MQ\xab\xb0\xc8\xcbc\x8e\xe2p\x87\x16R6\xcb\xd8\xad\x06\xd2\x192S\x80\x07q\xad\x1f\xb4S 7\xfdJX]\xd5\xb9\xaf\xd2\xb2\x19\xbf \xcc\xb3\x88TB\xb7\x0e\xd2\x8d\xd6*G;\xbe\xa2\x9a\xd5\x16Q\x83r\xa8\x14-Fe\xe0\x16\xacT\x97\x8c\xdb\xee^\xdbJY-\xd3\xd5v\xa5\x84\xae#\x14\xd1\x81\xf6\xd8\xda\xdb\xbcl\xf4\xc7\xca\xe7Z\x9aw;\xdb\xc7\xd8\x8d\xf7\xdc\xf9\xf5%\xf7Z\xfe\xd6\xb6\xe9*S\xf3ToZ\xae:O/\xbf\xcb%%Y\xecz>\xd0V\x0c\xf8\xdf\xd5=U\x03\n~\xcf\xa0\xd4}\xb6\xf3\xcac\xc7\xe1\xf1bA\xe2$\xa4\x04\x13w\x87\x85\x0ex\x8c(\x83F\x04\xf2\xbbf\xe7\xbf\xb9\x1b\x99\xfb\xe2\xf5\x8e\xe7z\x95\xdbN\xc6-a\x98\xc8\x17\xafw\xbfa\xa8\xeb\xcam\xfc\xcb\x1ds\xf0\x84\x17\xa6\x88?\x99\xfb\xea\xa9!\x86\x97n]-\x0e\xf6f\xc6\x95)jSWx\xa0R*E\x867\x9a\xff\xc5\xb4\xa1.y\xdf\x05\\W^\x1b\"_u\xa5\x0f\xb51\xa2\x12\x9f!\xb4\x98W6\xcb\xe1\x85@\x86\xc1W\xb9A\xb0W\x9b\xbaF\x9a\x93\x05~F\xa0sI\xf4p\x11y\"\xce]\x04\x7f\xd8\x83\x1d\xc6&\xb0\xb4\x914H\x96vN[\x90\xba\xa5\x1by\xde\x1b\xe0a\xee`s\xd3p\x1d\x85z>\xaa\x94\x95rq\xc2T\x1c\x8d\x13z\xe5C\xe1N\xbdz\x8c\x1a\xbf&R\x15w\xc9\xdf\x00\xcd\x0d#\x89\xd6i$\x05\x95Z\x07\x86\x11\xb5&\xd1\x1b1\xd3\x8bHaJ\xc2\xc4nD\n\x8aT\xb8\xf1\xe1+\x97\x12tw\xaa\x06,\x967\xce#\\r\x11\xc0\xe1\x92|\xa6\xa7yL\\\xc7\xe9p\x1cn\xd0\x00QT\xaf\x06\xdc\xaf \x83\xd3\xc1\xe6{\xf2\x80\xe7\x97\xeb\xdc=\x16\xb5\x9d\xdfC\xfc_f\xfd\xfe/\xb11\xe3W\xb3D\x05\xad\xd6\x9a\xe4\x94E\x8e[;Z\"B\xf3\xa3\xca\x8f'8\xd1c\xd0\xc8\x077l\x1e\xc4!\xe5\xe1|\xf6`s3\x81\xff\x80\xa7\\\xdd\x01k\x0b\xcay2\xa5.z\xa1\x10\xe2\x17ix-(\\6\x82 \xad\x96qH\xc9\xbb\xf0\x8e\xcd\xf3\x00*\xd7@\xb2cD\x0f\x83\x80u\x19\xde\xa5y\x18w\x84\xfb\xa9;\xf06I)\xe9>\xe5{:`\x10\xc9\x0e\xeb@9\xcfo\xfb\xc9C\xc6\xa0\xb6|B\xf5\xf8>\xe7\xc1\xb4\x94\x04#UE*\x17\xb0\xba\xfby\x06\xc5\xb6\xe1\xae:\x86ke\x1b\xb3\xd9\xc8\x14\xbf\x8e=l\x16\xb2\x91\xe1.\xc5f]\x88s\x17\xcd\xc3lF\x84UW\xff\x0c\xdes\xfe\xda\xbe\xe3\x1d\xe7\x11\xa70|\xe4)\\\xe41\xb9\xd7\x0c\x9a\xb8/c\xd0\xae\xf6\x06vR\xdc\xb1\xd7|\xf7\\\xf37\xa7\xcd\x9f\xb5\x91\x81Vr\x8a\x1b\xcfi\xb3p:Z\xd1\xca\xb1\xc1:m~\xae\xc2J2;\x83+\xee\xa2\xf2\xbf\x1ea\xe2\xf5mH\xc9\x8fd\x9a\x17d\xfc\x99D+\x14l\xd2 \n3\xf1\x8a~.y\"k\x0cOR%m\x1e\x96?\xe5\xe2\x12\xa6\xfa\xfeKB\xe7'\x84\xf2Y[\x86E\xb8 \x94\x14\xe6\xd4\xe3,JW%\xab\x94P\x9ad\xb3\xb7ya.\xf6\xe3\xddqL2\x9a\xd0;\xfc\x1e\xa6i~{Y\xdc\x1d\xd3\xb3\x15\x95\x85\x16\xec\xa8\xafn\x0ddj\xa1\xbf\x96\xcb<+\x89\xb9P\xa9\x16)\x1b\x05\xf8\x1b\x0dg3\x12\x9f\xc9\xb1\x96\xcd\xa1\x97\xac\xbb\x97\xe1\xac\xca{Dh\x98\xa4\xd5\xab)\xfby\x9e\xd3c\xaet\x87r)\xca\xa3Z\x88\xf6\xe6rzo\xc2\x92\xbc\x0f\xd1\xacO\x00@Rw`\x9ad\xf1Q\x95\xc6+!\xd1\xaaH\xe8\xdd\x91\x96U\xa6\xf3i.\xf2x\x15\x89\xa6\xa2<+W\xb2\xdd\xbc9\xc2eH\xe7\xb2\xfcb\xcd\xfd!I\xe3g\xfcM>SRdaz\x94G<_\x92M\xf9^M\xca\xb3\x83\x8bg\xbc\xec\x92D\xd5\x8f\xff,9\xa8\x9c\x932O\xd7$\xbeX\xdd\xd0\x82\x88\xe6Y\x06\xedC+\xbdQS\xf5r\x91\xaf\x8a\xa8\xce|Ay_WE}\x19\x8b,\xaf!>\x82\xa2\x15\x94\xb9\xafLA\xdaQ\xa5'GyA\xd1\x0c\xf1Wt\x87\xf8+\x9aH\xafn\x13cm\xbf\x97\xd0nVa\xb0\x1c\xfd\x08\x17\xecL\x9d\\1\x96bF\xe8q\xe6N\x9c\x05\xa1\xa1\xe3\x83\x83K\xe6T.\x9e5G\xb5\xd4\xf3a\xe2T\xdb\xact\xae<\x1f\x0f\x8d\x12Eh\xffy\xe1\xb9\x93+\xcfC\xc8\xea\xb1\x87\x94\x97\xa0\xc1I\xb8\x0c\x92\xf2$\\\nE%\xec\x93\xeb`\xb0\x06\xaf\xd6\xf4\x16\xc9I&\x12\xb5\xb9A2\x81\xf7\xe4$\\z*9\xea\xab\x98\xe1g\xae\xe0\xd2\x7f\xf7a\x9a\xae\xf7Bj%)\xbf \xb1O\x94\xe7\xf1\x0e+\x93%\xa7\xea]RR\xcf\xf5\xbc\xa0 l\x1f\xb9\x8d\xaet\xdd\xc1\xc8\x08\xa4\xb1\x081A\x959\xd9\x97o\x88\xb8\xaf?/R\x87[5\xd4\x89]r\x19F\x9c\xbbj}\x9b\xe0\x04\x0el\xca\n\xf8r0\xb0j\xce\xbb\xbe\xfc\xffP\xa3\xa87\xa7\xbe<\xe6AX\x8e\xb3\xff\x1a:\x87\xf1\x84|\xf2\x83\xa4d\xffT\x81$ \xca|A\xbe\x11f+\xe0\xd4\x94\x8d\xfbf\xe4\x92\x07\x1d\xba\xf49>\xa5$\xa3,\xc9\x0c\xabz\xc7\x14\x08}\xd3\x9aH6\xd5\xb1K\xbcj\x9f\xf7\xed\xef\xd6~f\x0b\xda&\xd5\xb8\x8b\x92\xfb\"\x8f\x81\x953Tz\"n\xceZ\x1fQ\xa7\xac\xb5\xb5x\\]r+vW\xbb\xd8\n\x1d\x93`1yb]\x8bM\x811\xd2\xcd_Fp\x89\xd1\xf30j\x15\xcb\xe8,V)M\x96aA\xb7\xa7y\xb1\xd8\x8aC\x1a:u\xb6\xbcX\x1c\xb1\x14\xcc\xcapE\x12\xe1q\xb8\xfdy\xeb\xf6\xf6v\x0b\x8b\xac\x8a\x14\xaf\xd7I\xecT~\xda\x8d\x04\xb96U\x06h\x14\n*\x15\xc0\x189\x1aI\x894\xf2\xe5\x9d\x00Z\x1d\xe3\x87\xf5\xe1\xde \x83&dy/\xb0c\xc7\x8a\x9c}\xc3\xa1\xd2\xc6*\xd1\xaa(HF\xdf\x0bR\x84\xd3e'\xcdS\x19A\xc5\xfd^\xbfrY\x99y\x04~1\xf4\xd2k\xd6\xc1\xce\xff\x893#\x14\xe1{\xc5\xff\xe5%\xfe\xe7\x1e\xba\xd8\xaf|\x89D\x0f\xfb9'a,\xf6B4g?\xd0\xcb\xa6\xa3E\xd2\x88z\xc5\xde\x15Wf;\xd7\x00Z\xf7\x9fS\x1e%M\xa5VX\xd1P\x08\xcb/HJ\"\x9a\x17\x9e\x1b\xf5\x05\x82\xac\xb0\"\xee\x8b\xaaBM\x9d\x9fs\x04\x9cHz\x94\x86V\x85\x1e\x15\x9d7Q\xd3d\x8f\xd2\x0c\xab\x8e\xa3\x0cG\xf7\xfc\xef\xeb\x04\xe1\xa35\xc8k\x14\xcdf9\xdd\"qB\xf3\xc2\xd6\x01A\x9e>J\xf3\x7f-\xf3\xac\xa2>8\x18\xe9\xb3\xacm\x86%\x87$\x8dp~\x94\xce\x14\xa2\xbe\x9e\x0e\xf9Vz\xbe\x97\\R\xdbC\xecSh\xccB\xf7\x11\xc5Qr\x8b\xce\x91\xcd\xca\x80\x89\xc3\xe8\x03~M\xa8\xa6d\xdc\x8f1\xce\x05\x8f\xca\x8a \"~b\x19\x9c\x151)H\xccg%X\x90bF\x18\xc3S\xd3\xa9#\xdd\x16K[\xbbx\x08\xb3\xf4mK\xd9\xdd\xd3\xa5\xdf\x00<\xcf\xd7\x97\xbeZ\x87\xf6\xaa7\xde\xe7*\xff7\xa8c\xd3\x96\xbaC\xb3\xc6\xb5\x88#)\xb9K\xf34\xcc\xfd\xee\x0b\x16\xd1\x98n\x0f\x8a0+8\xd8\xfe\x8a\xbb\x86\xf1Wi\xaf#\xc8\xcai\xde\x9e*m\xae\x16|d\x1aG\xfd\x98\xddP\xab6\xac\\\x83\xb57\xb7\xbb\x1e\xd8\xae\xda\xaa\xa8\xb3u,h\xc3\x9f \x84%\xe5\x0c\xe6\x0e,\x06v`{\xbd\xefNv\xb6^_}\xe7}\x0c\xda\xbf\xb6\x93\x80|&\x11#p\xb8\x0b\xb7]\xd3lH\xe9\x87\xb9+\xf1\xc0\xae\x10I\xeb2\x02\xaag\x12\xee\xdaB\x18s\xe3\xb3\xbe\xc6\xf1\x0e\x9a\x07\x0e \xca\xe4\xef\x04~\x80]\xaf\xb9\xfb\x05\x17\xdbf)%\x03\xd7\x93\xad\xb9\xd6\"\n\x1d\xec\x83K\xda!\xe9H\x87\xca]\xdd\xd5\x8d\xaad\xd5Uk\x18bc\x1bV\x83\x1c\x10F\xae\\\xb3\xb6\xf0d0\x15\x97K\xd9\xf0\x9a\xb7\x8f\\W\x1f\xb6\x9a\xbd\x9a\xf2\x0bB\xe7y\xdc\xab\x9f_-\xb7U\xa6.\x9f\x84U\xc6\x18\xfb-\xc6\xd8\x9bU\x07\x80\xc3\x95\xe5J\xdat/\x8f\x87\xf0\xa8\xb9\xda\xfanh\xbc\xdf\xe8r\xc3oCR\xbc\xe1\x0bB=\x974\xd9\xb8\xbe\xe3\xe5Z\x97f>vGd\xd5}\x1d\xb9\x95\xc8\xab\x12\xb2~[O$\xd5)\xeak \x9e\x0c\xc8\xca,\xf8}\xd4n(U\x1b\x89\xfc\x968\xba\x97\xd0\xab]\xbfY)=d\xd3\xeav}\xa0W\xbe\xd031\x82xS\xb0!\x08g[\x15v\xb5\"\xd4 F\x99D\xeb\xa6\xdcoI\xe2\x1fe\x96\xd5.\xda\x85\xa1P\xcd\xb6r3\xf0(\xed\xcb\xfa\x8cK+\xee#\x1e\xa5!V\x97\x99I\xac.@\x1e\xa5\x1dQ\xdd\x006\xa5\xfbf\xc6\xdc\x99;\x1fn|\xb8\xee\xbe\xceku\xac\x11\xd8\xdd\xaa\xc5Qe\xe7\xd7\x8c\xaeSu\xd0\xe9\x9b\x02\xf9\xa0\xd7\xa3\xae\x0c2\xd3FS\x18\xda\xaf\xb5\x06j\x07o\x13:\x97\xaa6\xe5\x80\x91\x19+\xd1p>'Z\xe4\xd0\xab\xf4\xa1#W\x1f\x03b\x17|\x8ekP\x11\xd5\x9f\xaf5\xe3S\x1f\x04\xcd\xdeU\xe9\x8f\xdc;\x83E\xb2\xfe|m\x85\xb6o\xe7\xb0~\xb6\xfbpnt\xca\x80|\xe4c$%\xb4\xbd\xa5\xa1h\xae\x97#\xeeC\x1fe\x8b\xb3\xbaz\x0f\xc7\xc6\xfbg\xd9\x87\xfa\x8a\xb6\xf7\x94\x92S\x82~\x81*\xc4\\]\x02q\xe5\x01W\xd9G\x83\xee\xcf\xa05\x1a\xe5\xc6\xcc\xa0?\xd1\x89\xc6\x9a\x83\xbc\xd0\xd8\x08\xe5z\xda<\xed\xb7>\x8c\xfd\xc1\x13A\x06\xdf{\x81r\xc6+`N\xab\xf3YEl|5\xaflJ\xb7\xf2d\x0e\"\xf4\xab\xcfH\xf8]\xf4\xcc'\xf7\xa2\x10\x02\xe9\xf0\xd0\x07QZ\xfdD\x06\xce\xb2@=\xc6A1\x8c\xbf\xd32\\G\xe8\xd9\x03\xfb\x08C\xfb \xf6\xed\xff\xd5\xea2\xf4^\xcbZuC\xb9w\x94w\x8c\x1d\xfb\x11TPn\xc8\x9fz6\xee!'\xb1\x0d\x8a\x18\x83\x10F\x95i\x10\x9c\xe2x\x0e\xf3l\x9a\xccJ\xb6<\xf6\x85\xc5\xcb,\x06\xb8\x17yAM>\xd0\xe5\xc3\xfd\x10\xd7{\x92\xe7\xef\x04\xf5\x0b\x94O\xe4\x05\xfd\xf1n\xd8\x9a(e\xcd\xee\x00\xba\x02\xd4\xea\x8f\x9c\x0f\xa3\xdej!t\x1fV\xd8?R\x94\xca\x1cL\nK\x14}P\xe9\xeb}\x90]\xe8\xb0\x11\xff\xea5)\xa6>\x0f\x0c\xf2\x9e\xdd\xd8g\xe9\x83\xbc\xee\xb3\xbe\x1a\x93\xbc'^z\x02{8t\x8aU\xb8\x05^\xd0\xf7\x0eV\xc1\xdb\xdd[\xbb>\x96F\xdc\xd9[\xd6\x01z\xa0\x8a\x0e\xca\x11$\xf7F\x04\x86\x9d\xd9\xdc\x82\xbe\xa6\x07e><\x86\xca\x9ck\x192\xaf\xf0~\x17\x1a\x9f\xf0LST\xb4\x1e\xa93\xbc\xbe>&\xa1\xf1~\x80]ik\x90=J\x8f\xb4j\xef\xd5\xb13\x8e#\x9b\xban\xf7\xe0O\x0e\x95\x1b_\x96U\xb2\xc9&\xa8P\xb4\xeb\xee\xd1\xc2\xa7\xc1-\x98\xb4\xfa\xee\xd1\xd0\xc1\xe0\x86\x0c:\x85U;\x1d\x0dh\xc6)M\xbd\x10\xa3\xfa\xe2\x90\xdeK\x04v\xef\xbbw\xa3JW\xf3|5\xa3\x92\xfcA\x8a \x03\x9b\xb4\xcaW\x8a\x81\x9c\xb0\x14E\xe7\xb89\xb2\x06\x9d,\x15\x9c2y\xc9\xe2\xd8\xc6\x08\xe2\xa4\x1eX\x0b\xa6\xcd\xc3r\xce\xc5\xac\xf8\xf30\x8f\x89q@\xa0\xe3y\xc3\xa5\x9aXq\x93\x11\xca\x03Y\x85JQI\xed\xb6Y\xf7NMi\xb7o^\xb7N,\xf3\x9ec\x99\x1ee^\x1d\xda-\xc2y\xe9)+\xab\x16\xc2@\x13\xa9c\x7f8\x98^'\xb2\xa3\x0c\xab\xe6\x0cf7\xf4{\x1f\xe3.\xbe\xffh\xfe\x19\xdb\xf7\x1b\x01\xa5\xb0\x80\xc7P\x90\xb0\xae\xca\x99\x98\x93\xdc0\x95&\xe5\xf0oD\x83\xbc\xd0\xd5c\xa1\xb8\x07T\x97\xd4\x9ah]\xba\xa1\x0d\x04\xd7y1\xa5N\xa4<\xac\x0c\xb8\x02p/Z\xd7\xc1\x8e}\xd0\xf7\x17\xf2i\xcd\x0e'\xfa>W\xf5\x93k\x1d\xff\x07Hj$\xdanH|\x8d:r\x06\x17<\xdc\xcc\xb1V\x1a\xc5\xf8\xcf\xce\xb6\x08K9\xd9Q\x02\x12\xaa\x11\xa2do\xe0\xd2\xde\x9f\xff\x81*\xa9lRz\x95R\x0d\xb3p\xf2\xaf\xd155\\\xa3\xa0\x99\xb2\xf4\xf1\xd2\xb9\xbd\x1f\x88\xd0\x85\xccU(y^y\x9d\xf7A\xb9T7\xe5#\xaa\xe5\xb5;\xbd\x97@x\xff\x83A\xac\x1a\xaa\xa0x\xa7\xd4\\\x8a\xdf\xb5\x7f\xb11\x1e7\xe5p\x95\x05M\x1f\nl\xcc\x8fP\xaa\x0b\x16!\x8d\xe6\xee\xf6\xffq'\xe1\xd6\xdf\xaf\xd8\x9f\x9d\xad\xd7\x9b\x1f\xb7\x82\xab\xef\xbc\xd1\xb6E\x0b\x97\xbb\xa0HJ\x19\x90\x80\xb1\xed\x1c\x92\xb3V\xd0\xc1\xd6)\xcb/P$\x8a\x14\x92\xef\xd6G\xe7Z\xac\x0f\x1f\x9e\xc33\xe6\x9ar^\xc3\xf6\xc1`h\xd47%\xa2s\x13gN\xe9\x12\xd54)]\x96\x8a\xb7\xac\xe3\xaa$\xf7\x90U\xb7\xdce\xf4\xd4)\x0d\xe9\xdd,zd\x8a\xc7\xa1S\xecF\x19-\x8d\x07\xdb\xe6Rp/z\xdf,M\x96\x03\x02\xcfJqj\xe5\xfa\xd1\xa0\x0b\x93\xa9\xeb\xd8\xc65\x7fm\xf7\xc4\x8c\xd6\xf61\xde#W\xf3> \x97\xda\xb6\xf9\xaf\xb7\x8d#\x8a5\x9c\xf8\xddp8\x98\xcf\xd4\xd7\x92p3\xf3\xa6W\xc2\x92\xd0\xd6+\xe7\xc7\xb9E\x12J\x80\xc7\x8b%\xbdC\xfb\x9f\x8az\xc6\xaf\x12N\xf1\x93\xb4\xa8\x92\x89\x9a\x16\xe0a\x18\xcd\xd5:M\x86S\x82O7\x7f\xc2\xb4\x0bi\x9c\xb5\x0c\x8b\x92\\\xe6\x95U\xd5\xc5\xf8\xf2\xfa\xe2\xf0\xa7\xf1I\xc3\x9c\xfa||q\xf6\xee\xe7\xf1\xd1\xf5\xc5\x87\x1f/\xcf\xc7\xc6oj\xda\xd9\xfb\xf1\xf9\xc1\xe5\xf1\xd9\xe9\xf5\xc9\xf8\xf2\xe0\xfa\xe7\x83w\x1fx\x99\xc3w\xe3\x83s\xf6~\x8c\xf9\xde\x1f\x9c\x1f\x9c\\(_\xce\xc7\xff\xbf\x0f\xe3\x8b\xcbF\xca\xc5\xfb\xb3\xd3\x0b^\xfc\xdd\xd9\x9f\x1aYXoO>\\\x1e\\\x8e\x8fZ\xe9\xedw\xa5\"S\x0fD\xdf\xc7'\xef/\x7f\xe5\xe9\xd7\xc7\xa7\x87\xef>\\\x1c\x9f\x9d\xaa\x19\xf0\x93\x9a\xf0\x9f\x17\xcd\x0c\x1f\xce\xdf\xa9\xaf\x17\xef\xc7\x876\x034\xd8\x83\x1b7s\x9f~\xaf\x93\x9d\xb9\xf8\xf2\xea\xb9\xfe%\x91e\x9e\xe9_B\xf1\xe5\xf9S\xfd\xcbJ\x96\xd9i\x15*\xc5\xa7g\xcf^\xe9\x9f\xd2\xea\xd3k\xfdS$\x9b\xfa\xdek\xd0\x8f\x1c&/\xfaT?%\xb6z\xc7\xe8\x8e\x82,\xd30\"\xee\xf6G\xba=\xf3\xc1\x01\xd0\xf1\x96\xcdkc\xad/\xd6Fsh/q\xdd>\x1f+3g\x8d\xaej\x9e\x1c\xcd\xbd\xf5-\xb6\xf9\xa7\x1d]\x18\xe0\x1c\xe0\x03j\xe9?\xb8\xf5\xdbok\x9d\xa1\x85\xde\xc5\xec\xe9\xc2\xf8\xa1]\xe0\x06\xf6\x88\x13\xcd\xbc\xb8! bO_>w\xf4\xc5\xcc\xa9q\x95?\x8b\x86\x9e8P,\xf7?x\xb4\x9f\x86\x0b2\x02K\xf0\xa8%?\n\xac*\x85I\xf9\x97E\xaa[\xfd\x00\x0crL\x80\xf3\xd6)\x89\xb4\x1b\x9b\xfe\x8b\xa6\x0f\x87o\x9d\x1c1\xb9\xddSS\xdcsjR\x12\x16?\xeb\xa7\xed\x83A\xfb\xf8A\xf3q\"\x14D\xdbj\x1c\x03\x96U\x9av\xa1\x91a\x1f)\xdb\xd3\xfd\xbf>\xa8\xfb}\xbb\xc1\xb2\x9c\x9f\xc8\xdd\x08tS\xbd\x87\xcc\x80\xb4\x1d\xfb\x1f:\x03\x1a\x1f{\xcf\x19`\xf0\xab\x10\x96\xdf2\xf6\xcb\xc7\x1d\xbbT{\xbe\x87\x0f\x10eD\x92r\xfe\x96\x01\x9d\xfc\xb7\x18PI\xe8}\xd9[\xdb\x80\x8e\xee= \xce\x9ew \\6^\x0bx\xca\xf1\x1ad\xc3\xb6\xf16\x89\xd9iEd\xbe4\xd9\xa5e\xaen\xd1\x19W\x05Z\xf4\xe5\\|\xda}\xd9\xfa\xb4\x96Ti\x9b\xcc]\x88O/_\xb4\xc8\xdcY\xf5\xa9Ej\xdfI\xc3R\x13\x93{c=\x14dh\x1e\xd51\x04\xe9v\x0ca%w\x1a\xf3xm`\x1e\xd0\x14Q\xfa\x9fA;\xc8\xe6\x18n\xdb\xfcG\xa3\xc8\xaaH\xb5\x12c\x03\x07\xd3(\xc2\x95\xa8\x1be>\x9b\xd8\xa0F!<\xd2\xb5R\x83\xb8\xabF-\x84\xf1\xc9\xbc\xae\xfa\xfaF\xab\xf5\xd0\xc2\xc7\xf1\x8a$\xf3l\xec\xd0'\x13O\xc8\xcb\x95\x84^\xcb\x8bt\xad\xd4\x81\x81\xb3T\x0b!\n\xd3\xca\x9cup\xa9uYq\xe9m\xa9\xe3\xbd\x81\xf3\xe5e\xd3|f)ca\xa0y1D\xb9\xb6Q\x9e\x18\x99\xf1fAS\x8b\xc7\x9d\xec\xbdZ\xbesi\xfe:@\x8a\xd0\x00\x95J\xccz\xbd 4\x14\x87j\xb3\xceS\x8b\xb4\xa2QOm\xde\xda({\xde#\x051\xd6q]r\x81\x8bV\xd7Q\x05\x0c\x95\x80\xc5a\xcb/e\xaa\x8d\xcc\xef\x86\xaa\xb8\xb9;>\xba\xa8\x16R\xc5J\xdc\xa6\x9bH\xab\\zS\xe8\xd3K\xfeV\x19:\xad9\xb8\xc5\xe7\x01\xe6,\xcdGLQe\x937J\x96\x8c\xdc\x99\x10)\x8a\xce\xea\xf8\x95\x9c027g \x85{R\x83\x1c\xd4\x1a\x16\x10\xc3@\xc0\x97/\x90\xb8\x18\xb0\n\xc1\xb6C\x87\xabD\x0bqF\xda\xb1i-\xda$\x1d{\xbez\"h\x91\\\xaa\xa0\x0c\xa7\xe4]\x1e\xc6\xc6h]j4=\xf3T\xf2\xa5a\xf4t\x9e\x8aX\xfb\xe8\xf1-\x0f2r\xcbx\xf6qq\x9fN\x9b\xa7\x8f=)Y\x93t\x042\xa0\x935\xdf\x82\x94e8c\xc4GP\x90\xb0\xcc;\xcc\xe4\xd2$\xc3|\x8b\xb0\xf8\xc4OQ\xf6+`\xc9\xa8\xdb[\xbfmb\xe4 .:\xb3\xcck{\xf2l[\x05\x03\x1d)\xde6\xf7\xc0Uba\x85\xb0\x0f\xce*\xe3\"et\xf2\xc1\xb6VTo\xad\xd0\xe3&\xe0M\xd1\x88\x1bz\xec\xd0\x1fH#}0\xc4\x95\xfb[\xa5\xbf\xa5Hf; a0\xecM\xab\x86d\xe5\x85\xa8\x7f\x7fBus6`\x8f\x82t\x83\xde\xbbO\xa1\xf2\xff2\xed\x00\x8a\x15\xecA\x18L \x8d\xe6\xf6L%f\x12S\xd5\x01`\x98\xed\xe0\xc2\xc0\xe3\xc8'\xaaD\xb2\xb8\xfa)\xec\xc3?\xbe\xc2\x08R{\x91\xa9\xbcT\x14:\xc2f\xb5\xa0\x0fh, 7\xe6mXd\xdc\x91\x84\x98\xa2\xc6:7\xc2tB\x99d\x11\x81\xf5\xb3`w'\xd8\x810\x8b\xe16IS\xb8!P\x90E\xbe&1$\x19\xac\x9f\x07;\xc1\xce\x1bX\x95\x04,r~\x11\xd0s\xc3\xf1|\x0ep\xb6XW\x0c4\x18i>\xedRv\x8e10\xd9\"\x8fI*/ZN\xc2\xa8\xe8\x88*5\xc7\x12\xd5\xcdVO\xee5\xe6\x16C9\xce()\"\xb2\xa4y\x87R\xf5B\x94\xe0\x04\x8cR\xc42\xcaz\x95\xeb8?y\xe5i\xc1\xad\x9dG\xf0\xfb\xf6\xca%x\x1e\xac\x8a\xd4\xaa\xfe\xc5&\x8fq\x15\x11\x83\x88wIFNW\x8b\x1bR\xbc\xcd\x0b\xb4\xcf\xdb\xb7}h\x86\xdd0\x84\xc2\x90\xcf]\xd5\xcd\x0bZ\xd8\\w\xcb\x1b\xb7\x0eT\x8f[\xca\xe8cH>\xac\x8dN3\xe4\x9b\xb0$Gyd\xe5\x1dA\xb8\x00mB\xc8\x08b{\xf6&x\x8c\xa0c\xd3\xb7ac\x04\xeb\xae\xec-\xc0\x18\xc1\xc2\x98\xfd\xab\x17\xd09\xc9\x06\xe8WA\xe3\x8e\x95M\x98\xbd\x03\xec\xe1\xf6\xad\xfc\x1a\xd6\xae*\x9eL\xc1Mz \x0c\xa8$\x02\x0e\xba\xf3\xcf\xcc$\x06\x082\xa3y\xfb\x9f\xe1\x1do\xa6(\xd6t\x0d\x11T\xe5\xbc\x81\xda\x9a\xeac%K\x08?\xcf\xd9\xa4LWi*\xb6\xc8\xcc\xbd\xf3\x95\x14i\x15\xc0\xd2\x96\xdc\xc8\xb5\x91\xbd~ \xfe\x9a'\x99\xeb\x04\x8eZ\x04)\x15FU\xcb\xd8\x93$\xa0\xdcE\x9b\x9c7\x1f\xb5s\x84\x8b iu\xccr\x9a\xef\x93\x89\x0f\x8e kz\xa3?\xcb\xa7\x11\xcf\xaa#\x10\xa8\xfa\x08\xb9! Dc\xbd\x85\x86X\x01\xda\xa1\x8e= #\x13/qV\xc6E\xf1#j\x99\xe4\xdf`9XhWfvS\xaaVr\xcb\xfc`r\xa5\x1dGo\x85>\xda\xa2&\xc6\xd8kZ\xbf\x96\x15Y\xcdh\xc7\nh\x81X\x03\xdfQ5b\xa8\x0f!\x0f\x80\xe2C\xec\xc3\xdc\x87\xb5\x0f\x0b\x1f*k\xdf[\x1f\xc6V\x85\xa1\xba\xed\xdbb\xd0\x86\xc1p\x0bo\xdexP\xde&\x9c\xca\x0f\x96\x05F\xfc\xe2\xc1\xd0\xbb6Z\x14\x96\x04vF\xddk;\xe5\xe7\xd7\xdf\x82\xf2\xae\xa4d1d\xe3\x12\x19\x8c\xf1y7\xdc\xb0\xe7\xa6 a;\x92\x9a\xfa\xd8\xc1\x05lH\xc2\x89\xc9\x8d\x00\x1e\xe9\x05`\x04q\x9e\xfd\x9e\xc2<\\\x13\x08\x81\x0f\x06h.\x0c`\x08\xe4\x99\x0f\xe1M^\xd0$\x9b\x05\xdcaQxS\xac\x96h\xe2\xc1\xda\xb0\x05\x07\x069\x93\xcf\xfbg2\xd3yQ\xc1\xc6\x92\xa2\xa8)d\xc1\xb1N3\x1fi\xe2\xbc\xa2\xf2\xf8P8\xef\x97#E\xaaS\x9e\xa1\xa4\xfc\xade\xee9\x04\x94\xd6\"R\xe8`\xacK\x0dw\xf3\xb6\x87U\x1eb\xe8\xd4\x14\x91\xf0\x12\x91\xf0\xa2\x1fh\xe1\x1bp\xb0\xe9\xf9\x16\xbclz\x86\xe0j\xd3S)\x14\x8au{\xeaw\x99\x1b\x9a\x1el\xf9\xe9\x83[\x0e9\x91K2\xea\x0b\xb6\xbc \xe5*\xa5'\xe1\xd2\x17\xbc5\x83\xf2_\x12:?\xe4\x0e=%\xcaV\xa0\xed\xa5\x0f\x89\x9b\xe2\xf9z\xbfi\x93O\xc5tL9\x1f6\x8c\x96\xd2\x1f\x13[r\xf7\xb0\xaat\x96\xe5\xe6a\xd5\x98\xd8\x19\x83\xa2\xd2\x90\xc7\xc8\xea\xdc\xde\xbb\xaa>bQ\x7f\x10\xbc^>\x18\xbc\"\x05\xbc\x96\x88x9\x9f\xc4\x8f\xba\x88sWP\x04a\x9a\xe2 R\xba\x1e\xf7f\x86\x8c\xcc\x10n\xc9\xf6\x0c\xe4\xa2lO\x9b\xbbZ\"w\xb5\xd4\xcc\x16\\.\xa1\xb8?\xfbdz*l`b\xa0\xe6\xee\xfa\x7f\x1b\x03ez\x1e\xc2T\x99\x9e{3Z\xa6\xa7\x9f\xf92=\xa8Pm`\xba\x16\xd2\xbd\xf6\xac>WW\x885\xe3\xf6\x87\xb4\xfa\xd0\xa2\x83\x1e:\xbd\x15f\xef\x94\x10u=\x96\xa3`\x04\xf6\x08\xf0\xb6\xe7A\x88h\xf7\xfb\xfba\",\xe4\x90,v\xeeW\x0e\xd4\xcdX\xd2|i\xf1\x91cz\xba\xa9g\xf9|\xc5\xe8\xf1&G\xb6\xc6\xdc6\xc9\xa4\xfa\xb4\xae\xf0z|)\xa8O5Xs\xd0\xcf\xde:\xba\x07\xfd\x95Q\xc3\xab\x8an\x13\xb8d\x00bW \xd6\x9d\x9a\x9c\x0d\xbb\x93\xab\xcac\xcfR\x9a\xd0\x074\xff\xcf\x8b!D\x84\x15\x9c\xa7\x8a\xc8X\xd4\xd6=\xc0\xae\xf5\xe1\x90\xdb\xc3~\x8e\x95\x83\x92{-\xafxz\x1f\xaf\x8dx0\x10I&>\xed\x06\x07\xe4\xf1\xfaz\xf4\xba\xbbG5c\xf1\x1aO\x87\x1d\xec!^V\xba\xbb\xbb\x9e\xafK\xfe\x02j\xbb{\x80\x8aL\xed\xa1Sc\xb3\xa1\x83\xcb\xc6>\xae \xd3\xdef\x9e\xd9\x9b\x19\x8a\x11\x86\xec\xfe6\xd0\xab\xbb\xda\x87\x89\xb1\xd4\x841j\xbb\xaf\xafZ\x1f\xaf\xda\x0e2\xe0\xd9\xf7\x0d\x9d{\xab\xb5\xc77^\xec\xffM\xc6\xc1\xf4+\xa8\x03\x0cC\xfaV\xf7LX\xbd}m\xdb\x02\xdc\xd3\x11x\x8fJ\xdcy{\xff~\x8b\x8e\x9fT\xd8l\xaf\x99m\x80\xfe\x10\xdb\x1c+o\xfdO\x1a\xdd\xc4\xe2\xc0F\x0cO\xc5\x83\xf7\x1bi\xcb0\xe9[\xd6\xee\xf0A\xa3\xab\xb4\xa5\xcdC\xe4.\xc1\xef\xbd\x84]\xf6X\xdf\xae'\x7f\xf1\xcf\x18\xe9#\x98\x13\xf0\xb058\xea\x9f\x85\xe9\xc2\xf0iS\xb7v\xd3\xbc\xed\xc1j\xae\x03&\xa5_=\xd7\xfc\xb9`'\xb6\xc9\xcd\x81e\xc9>uAK\xc3\xb8\xef\xbf\xe7h\xffv\xaf\xd1\x1e\xf4\x8c\xb6e\xe0\xf8\xbfa\xd0g]\x83n\x18y\xf6\x1e\x9c\x1d\xe34\x8c\x857\xff\xbe\xab\xf9\x96\xd9io\x17\x86*\xe5\xd9Tn\x8aa*{\xf9P\x95\xbd\x95&\xeb6\xe7\x12\xf1\x06\xc3\xf2YOu)\x12\x96\x0c<\x18\xca3\xe7\xe1r$qW`\xcc1\xc5\x1c\x95\x8e\xa8\x05m\xc2\x1e\xacl\x9c\xc1\xfd\xb4S\xac\x9a)\xe6\xec3\xbc0\xe0\xacD\x9b|M\xa6\xe0\xce\xe0\xc9\x13\x98)\xa1\xc7\xf4w)y\xd2\x93\x85{\xd2~\xf1\x93\xa4iY\x0d\x1bBK\x86{\xc7\xaa\xcf\x89\xf6\x1e3\x98\xa5w\xc6\x0b\xcf;\x1d\x07\xb9\x93\xd4\x87\xe8\x8am\x84\x8c\xad6\xd2X^\x17\x9bJ\xd4)\xd9k\xbe~\xf9b\x8d\x1f\x00\xca\xd6P\xcbLx\xc3\x1d\x1e\x0c\xdd\x0dt\x0e\x8e\xa1\xfcv\x84\x8b\xa52\xf9;w\xda\xe1\x9a\xea\x82=p\x0c\xbe\x97\xc0\xcc#\xa0H\x07\x83\xc8}\xa6\x1f\xaa\xc8Lq-\xfa\x91\xcaH\x01\xcd/\xd0\x12\x96\xb1\xcf\x02<*\x00?\x8eQ\xc8\xa7\xbe\xefi\xdfG\xbcP\xca\xfeD\xa2\xf3\xcd\xfcY\x90/\x8fcw\xc6\xefc<\xd4)\xe5d\x96k]\x136\xa97\xb0\x07)l\x823r`\x13\"\xf3\\2v\xb6\xe0\xb1>\xca\xa0D\x1c@\xe2\x0bLro\x90ko%w\xe8_]\x8bjX\xbe\x9f\xc3\" oR\xd2\xa5\n\x05\x18,\x9d\xe5\x1eU=\xe9\x96\x08\xb0\xa5,\x97aDFpc\xcd\xf8\xb5_\xbap\xfb\x08=\xedo\xbf{\xce\xabv+\xf7>\x15t]{\x12\x91\xec\xc35\x8c\xe0\xd6G5^=R\x1d\x0e\xa2\x9d\xec\"\xa0\xf0\"\xad\xa8u\xa2L+\x9d\x17B\x87!\xdfm\x7f\xe7\xd8\x17y\xac\xb6\xfac\x1es\x9c\xc4\x8b\x9bK\xb1\xc1\xdd\x05I\xf9\x9f\x17g\xa7\\0\xed\xb9cT\x8cW\xab\x81=`\x19\xb86\xbc;\xf6F0f\xfba\x8csi\xc8<\x16\x93\x0c\xa3\xf6\xa7\xf6\x86n\xa5\xb0\xa1|\x163\xaf\xb8\x01\xf9\x07z\xe6m\x8f\xe33\xee\xc4\x9bU\x92J2\xcc\xfd\xec\xf9P(\xc4\xa8\xab\x1c\x90\xf5A\x08\x9f\x0d\xb5\x11\xc3\x11\xa6R\x19\xbd\xfeq\xd7\x0d!\xe0\x84\xea*:\xea\x93\x9bG\x99u\xab0\x16m\xc2\xd32\xc0\xbc\xe1\x9bD>_U\xf8k\x0e\xd3p\x97\xcc\xc6u\x01{p\x14R\x12d\xf9mG\xa8\x9bLRg.\xd1\xd5\x05\xad\xd3F\x83x\xc5Qj\xa3\x0d\xd8\x82\x8bj\x0dyO-c4\xa8O}\xf5\x84\xa0\xad\xbfyuJ{\x1a\xea8c\xb9\xf6F\xd7}\x0b)\n.^\x98\xab~m\xccg\x9ei@\x8d$\x0b\xafI\xdan{\xf4aK\xf5\x04\x83\xa3\xaf\x1d\xab\xa3\xaf\x9d\xa6\xa3\xaf\x9d+T\xe37P\xef\x15%\xda\xfe\x96uR\xa0\x89\xd8\x07\xb9b\x9e\xc3}\xfeP\x0c1\xc9\xcb9Wf\x1fi\xdd\xa4\x9bT\xd2$\xc14\xebR\x9a\x0f+}\xd5\x01\xf4;\xe9\xe7\x07\xca\xea\xf6\xdf\x16\xa5\xce\xed>\x0c\xb9\xfa\x80\xe6\x1d\x8b_K\xd8\xa9\xfc\xb0\x1d_W8x\xednl\x8a\xf7\xc9\xed\x03\xcb\xce\x08D\xa6\xa3\xca\x9c\x9d\xd1J\xdb\x9f\x17\xe9v\x12P\x86\xac\xa6\x96N\xccq\x00\x15\x81\xd8\xe8\xbe\x0f\xb1\xfd\xec\x16\x80\xb0\xd2\xb8C\xd4},\x9a\xb85\xb1md\xa1\xfcm\xd1\xbf\xe7\x8a\xdf\x96\xa5\x96\xd8\xa2\xdfb\xd8V^\x92\xc4V\xednS,\xdc\xa9\xa5\xab\xc2\xb4\xd9b\x9fa\x0c\x97\xbb4\xa0\x1c+\xce\xc1_=\xce\xa8H@>/\xf3\x02\xfd>7\xe7\xbb\xb2\xf1\xcd\xdc\x97\xcf\x9ej\x90P\xdb\x087\xbdO\x19\x9b\xb4\xb57@,\x89\x91]\\n\x00\x12f\x11\xbaUD\nKA\x80\xe8\x11\xb4\x80$\x03\xe2\x01\xde\xea\x03\x9b,T\xb4p\xd1\x1f\xeb\x08\x92,\xca\x8b\x82D\x14\x92l\x9ds\x07x\x1b\x16W\x8e\xe4~3hv\xe7U\xd9(\xb9\xaf\x9f+\xcdT\xc3\x0f\xa6CD\"\x19\xb9\x1d\x805Y\x8f\xda{\x8d\xd15\xc1\xb2\xc8\x17 \x8a4YUdX\x9096\xe9\xca\xfcRm\xbe\xb3\xf6,;?\x861\xbc\x17mEyV\xd2b\xc50\xb3M\x97\x11O \x1f\x0f\x1b\x83\xbc\xd6\xf3y\xe7\xc5\x05*\xcb\x84\xbe\xe5D\"\xa3~1M\x0b.\xf3U\xb5;\x1c\xb4t\xf5\"}\xbfcZ\xa4\x01bB\xd4\xb0\xe3GW\x921\xd8D~\x9aLrv\x16\xe3\xbf=\xa0\xec\xdf\x08\nVG\xee\xe3\xeb\xbf\x04\xf2^>\xdf\xb5\x8c\xaax\x8c\xea_\xbd\xb0\xd4\xce@M\xd7g\"\x9f\x97i\x12%t\x04\x13\xd6\xb1\xe7\x8c\xe0u_>\xff^\xfc\x7f\xe1\xa9\xdeP\x1f\xde\xbb\x0eJR\x99\x97\x17\xbb\x167\x93\xec\x9b\x8e\xea@\xd0=\x9a\xc7\xca`s\xeb\xea\xbb\x91\xb7\xef~\xdc\xfe\xb8\xed\xed\xbb\x93\x8f\x17\x1fK\x0c\xc9\xd9.\x1eb\xf1\xc9\xc1\xd6\xff\x1f+\xe0\xffw\xb6^on\x05W\xdf\x8dX\x05\xdb\xedB\x8c|\xb1\\\xad:\xff\x86\x9e#\xc3r\xae\x87\xf3\xae\xb3\xec\xb3,\x7f[\x91\xe2\xce\x9eg[\xfatDG\xca\xd6l\x7fd\xd9\xc2\x15\x92x\xbb\xb6\\\xa7\xe1)\xeb\x13\x8fH.\xaf\x86w;\nl\x8f\xdc\x8f\xf1\xa6\xf7\xef\xdb\x18\xc8\xbch\x14\xebo\x04{\xac5\xd4*c\xa8\xa6}\xce\xc9\x87M\xe7\x08v\xcd-\xe3D\x8e`\xb7\xf5Q\xf5# \xaa\x9b\x8d\xd4\x8e\xaf3\xaepo\xb3\x94C\x015\xfa\x83s+\xc3m\x1a\xa4\xe2\xd4\xe2\xc2@\x8bp\xd5\xb9I\xf3\x9b\x91#d\x9e\xcb\"\xa7y\x94\xa7\x1e\x87{v\x96\xb8\xab\x8c\x94Q\xb8\x94\xbc\x13\x9bF\xcf7WH\xd2\x92\xe8\x8e\xea\xf6t\xf7\xd8\xf2A<\x981\x1cX\xb7E\xb0b\x1fJO\xeaz\x14\x93\xcc \x91\xac\x1bR-\x99\xad\xda\xd6uS\x84\xa1\xdb$\x03\x94\x90\xba\xacr6_\x93LG\xaf\xf2Ql\x14\x8a\xa0L\xc3rNP\xfc\xec\xd6o\x8c\xb0\xa5\x9cQ\x9f\x17dj\x8a\xfa\xd3J\x91\xbc\xe9\xef\x9a\xd9\xccp\x11u{;\xad\x02\xfaZ\x89g\xf3\xa4\xc8\xb5\x1e\x01\xe5\x0e\x9f\xd9\xbf\x80\xe6\xef\xf2[R\x1c\x86%A)\x8fc\xb1v\x17\xa3\x1f\xc1\xc6\x06\x9d<\xb5\xec\xbe\x82\x94\x94U\xff\xac\xbd\xd1\xf4+V\xf3\xd0\xa7\xb6C\x14*J\x8f\x1d\xf1*\xb17\xad\xbdPW0E\xcd\x82\x176\x83\xdc\xec\xa9\x94\x1a\xf7sn\xc1\xb0\x12\xc1\x91-\xdc\xcc\x02j\x97\xdd\xe6\x1c3\x96c\x9eX\xb8\x8a;\xd8\x83\x9dv\x7f\x10L+\x88f\x84\xd3\x02\xad\xf5\xe5f\xaaR\xb8=\x8e\x8f\xcb\xcf\x1d@s\"B \xfe\xb3Q\xf50\xabJ\xe4\\\xcc\xe7\xf1\x82)RH\xec\x9c\xdap\xd9q\x13\xb9\x84{.\xf6\xbc\n\x0f\xe0\x85H(A\xdd\x87Y\x03\xea\xe5\xef/_ \xe1\x1eu\x95\x8cU\x15\xc8\xf8\xc9\x17DL\xea\x9b\xe3\xf8\\l\xc1h7\xea7ku\xd7\x93\xa7l\x83N\xb6\xdd\xe0;o\xbbq\xf4xo\xe0\x0e~\x80\xb5\x10s\xbc\x81\xbb\xcdM\x0f\x91\xb5\xcbx\xd8\xf5\xe4\xee\xca\x9b\xec\\\xf9\xdc\x12{\xb2{\xe5C\xc9f\xa5\x84}\x98M\xe6\xb8\xef\x19|\xb7]j\xb2\x1c\xff\x8f\x1b\xa3,@\xfaX.=~\xc9\xe1dh\xfe\xa2f_\xb2>\xee\x83++\x15\xa0\xb3#tT\x95\xa4\x1861\xb7\x87A\x87\xb5\xfczf,\xcfs\xc6(\xfc\x15\xbb\x9c\xf7C\x14\x8eq\\z1\xdek\xcf\xf3\xe5@\xf1\x9f\\\xa5\xe5\xe4\xd9\x15\xae\x96Hd+\xb0\x9c<\xbfR\xebe\xff\x9a\xa8\xc0\xb0}8`\xcd\x02<\xe9\x90\x14\x12\xbf=\x84+\x15 @\xf1c?\xab\x8e\x91 \x9a\x87\xc5\x01uw\xc4\xdc\xea\xdfy\xef8GQ\x9f=\xa2\xd5*\xd3\x00?\x11\xa0\x92\xdd\x18\xe9\x0c9\x14g\xdb\xf1\x82r\x99&\xd4\xe5?\xe5\x0cn\xedz\xd2a5Q2x\xbep\"\xc1A\x8e\x1b\xbce\x93\x02\xb6\x18\xfd\xc1\xb7\xd2.7s\xdby\x03\xc5\xd6\xd6\x1b\x0f#{\xe0M\xd9\xa4\xb8B\xcf\x19\xac\xba\x08#\x13\xec\"~\x0d\x9a\x19\xdcf\x0e\x1fB\x06\xd6#\xee\xb7\xc3\xdd\xa9\x03Z\xb8 \xf7j\xe0C\xab\xc4\xd6V\xb7\x94\x19\xd7&\x0bVY9O\xa6\xd4u\x1c\xcf\xc7~\xb2\x89\xceq\xa9\x82\xea\xed\xcb\x17\xc8\xb8\x0e\x1cf\xcb\x84\xce\xfc\xb6)\xa2\x8a\xb2*\xbe\xbabl\xde\xd8\xb7\xbc\xa0*f\xe0\xfa\xa93\x19a\x97\xff\xe0\x85yf~{\xc8\xdeV%)\xc4b\xb36\xca\xf26/b\xfc\xcc\xbe2B\x13\xa7d\x89\xdf\xd9\xab\\\xb5Q\xab\xfcr\xb2S\x81}\xa3.\x86#\x04\x02d_\xf2\"\x99%\x19oP\xc1\x86\xa2\xbb\x88l\x93\x94\x8c*\x98\x95y\xf6\xd5\x97Mp\xb6\xb7\x1d\xd8\x94\xc5F\xe00|\x8dM3b\x01\xab\xaf/3\xb53Q}\x9b\xf2J\x85)B\x1b\xc4KBG\xbd\xac\xa7|\xf0\xe0\x13'\x94\x19R*\xeb\xaf\xae\x0bh\xae2\xca9\x86n\xa5\xd1\xdeX\x17\xd2\xdd\x84\x8b\xd4\xaa<\xa8x\xa0\x85d\x82\x17\xc9=\xe6_C4{9\xd7\xd0c\xee*Zc0K}H\x14p\xdd\x17~1\x12 \xb2I\x05\xb2\xd5\x95/\x0f(o\xc8Q\x8d\xc3\xe92\xd7\x84\xa1#\xa98\x9a\xa1\xa3I\xf8\x96\xe2\x13\xbd\xb9'\xba\xcbS\xd9$\xcb\x1e?\xc64#O7\xb4c\xdb\xa3\x8f\xf1\xe6\xbfos\x1a\x9a\xb2Yv\x85\xffxe\x0b'\x12!\xd0`\x99/\xdd\xaa\xc3bSS\x81\x96F\x8e\xa7\xcc\xbf\xfc\xa8\x14\x7f\x9c\xc9\x97 \xd17F\x95\x08\xa2\xcd\xf3\x94\xf5\xa9\xa6\xa56z\xa2N\x0f\xeb\x95\xa4\x8d\xfa\x94\xbcQ\x0c\xd0o\xf4=\xc8\xd6\x13\x0dW\xd9\xc4V\xad\x0b'3\xfbx\xe0\x8f\xc0\xf97\xcb\xb5\xb6\xfaHhP(\x82\x0da\x16\x1e\xb2M\x05&\xe5V\xf5\xf9*X\xc2\xc7@\x15R\x8c=\x08~\x8d\x99\xccF\x1f\x15\x05Rr\x02\xa1\x84\x1f`U\x91\xaf%;\xe7\xed\xf3\xcd\xca10ZM\xca\x0e\x0d\x9dT\xd2q\xc9$\x9d\xec^\xb1\x1e\x8a_\x1a5w\x8fnK\xa2\xa1>\x11\x93\xc6\x89\x98\x18O\xc4D=\x11\x13\xc3\x89\x98\xe8'b\"O\xc4\xa4\xa1\xde\xd3\x0e\xeei\xba\x9f\x14\x05F=\xb2o@\xd7vMNI\xf1\xa5\x8f\x04\x89\xf0\x8c\x84\xf5%\xd3\xbb\x0e\xcd\x1b\xca\xe5\xd1v>\x0f@\xc6\xc9\x95\xe3\xb7\xd0e\xd8%1s\x85\xdc\x04\x85<\x1c\xb7\x18\xa9\x88B\x07\x81\xb8;\xfa\xc4\xe3\xb4n\"\x1d)\xd0\xcb>\x9f\xf2\x91\x1d\xf9U\x97\xfc\x15\x9d\xc4 \xcc\xcd=%\x8d\x11\x7f\x15\xb9T}\xe7\xc7H\xfd\x05I\x7f\x96\xfeGG\xfe\xcc\xf8J\xf3\\\x92\x10\xcf\x87\x8d4X\xa6\xabY\x92\x95\x93\xec\xaa\x0biR\xb9\x86\xe35\xc9h)\xeby)\xeaQ\xab\xe9>5\xe4)G\x03\xb2\x167\xab\x1d\x1e\xad\x14D\x9fd\x10z\xb0r\xc3Iy\x85\xeb\\z\xb2\x17\xaf\x1c\x94;\x19<_\x82\x11\x17\xab\xd7\xb4\xed\x95\\\xd9h\xfe\x94w\xf94\\\x90\xa3\xa4\\\x864\x9a\x0b\xedd\xb6\x19\xcen\xb3\xcaP\x99{\xc9b]{\xed\xa0*BGY!8m\xceA\xad\x8f\xb1\x9c\x87%\x89\xcf\xc9,))\xd7q`uhS\xc6A\xcd\xb0|\xd5\xfc%l\xfe\xacR]\xaeS\xab\x0d\"\xf1<(\xdd|\x92\\\x89\xe9\xe8\xd9\xe9P\xa3?=\xae\xed\xefLy6HPh\xc3B\xfcR\xba\xed\x0f\xa2\x07>c\xd3;\x17\xaf\xb4/\x9e^'\xbfB/\x19\xf5\xc1\x17kwg\xa7\x02\xe7\x8e\xccH\x06\xb7s\x1c\x91%\xc9b\x92EI\x95M\x01\xf1Iv\x15\xc4J\x0ee\x10\xf2\x97\xa4K\x9a\xfd\x16\xfb\xaam\x95e\x83\xa7\xb6\xda\x91e,\xfd\x19\xd5!\xb5s/\xf3\xb2LnR\xd2\x82M\xe1\x01\xa0 \xa1\x19;\x9e\x10y\xbc\xc7\x11a\x8c\xc9>\"#\xafVf\x97\x9d\x81u0\xba\x8a\x83\xe7\x92&~0\xb0\x95\x0bu\xd6\xbf\xa7\x1b\xe5\x8fw\\)e\xc0M?\n\xa5,\xb2f.\x0e\xc3k\x11\xeb\x0e#m4\xd1G\xa7\xe6\xe2N\xc5\x8e!\x133\xeeI\x10\xadH\xb9\x93\x8b\xafr.\x9f\n\x9c\xc4\xf3\xe0\xad8\x17\x80\x0dD\x9fH\xa1\xf6L\xf4\x8c\x88 \xe6\xc0\xf66/p\xd2\x87\xce3 \xe2\x06T\xb7\xc7\x8flUk\x13V\x17\x16\xf6\x1d\xdc.\x84\xb2*\xb3[g]\x1b\xc3\x86\x8e\xbbNqn83\x08\x8f\xcb\xa7\x02)\xd4\xac1`^\xf9\xe0\xc9\xaeC@\xd1 V\xa0\x80\x96}\x96\xb2Iq\xd5\x01uP\x1f:b\xc2\xdbQ\x85\xe4\xd3u\xfe\xcaG\x92\xcd\xab4\xed\x82\xaa\xeb\x82\x94\xa4\xb1}Gv5Nh\x11[\xb9\xb8\xe4A\x8fg\xad\x8d\xc3\xe5\xe1\xe2\xb2\x94\x91]\xed\xe1Wd\x8e\xe4'\x8c\x97O\x12\x88\xedg~\x1f\x12\xa1\x1e\x0f\x9e\xdb\xde\xd7\xa2{\xd4\x88\x13$Yk]\xd6\x8evC\xbc>\xf6\xa0\xd0\xdb\x0d\xd5v\x8bI\xd8\xbc\x804j\xd9\xaa\xf4;_\xcf\x87S\xe9\xdc\xa3\xa2\x99VG/\xd0\xee\xd3\xdd\xa7\n\xdd+Hw\xf7\xb51\xfe\xc6\xaaC\xdd\xad\xa6\xb9P4\xfc\xe5\x0b8\xab\xecS\x96\xdff[\xb8\x8e\x9a\xf0\x85\x04\x11w\xe9p\x19\x163B\xf1biF\xe8i\x1e\x93\xb7E\xbe8\x16\xf7\xa8n\x81\x97\x84\xfb\x10\x06I\xb6\xce?\x91?\xad\xc2\"&\xf1a\x98\xa67a\xf4 }Cp\x7f\x99\xd8-\x82W\x14\xe6\xbcU\x16\xdf\xd0zc\xef4\xa9\x8a\xb6\xdeER\x8e\xb38)\xe7}\xf8X\xecK\x87\xe6\xcb\x93|U\x92\x0fK)\x94b\xd3C\xf3\xe5e\xbe\x8a\xe6\xe3,6%\x1f\xb2\xf1\xa7\xe2K\xd7\xb6N\xca\x93|M\x1e\xd0\x1dV\xcc\xd4\xb2\x92\xde\xdd\xee\x05\x0d\x0b\xfa\x80\x86\x8f\xf2\xdb\xcc\xd40\xd67\xa0e\xa1\x82{\x94\x14$\xa2\x129\xf4u\xa2>\x1c\xaf\xe5\xe9\xf8.))\xc9\x88M\x0b;k\xe6\x960i\xc0\x03M?T\x94\xd3\x10\x8cXx\xe6\x18\xa1\x8dA\xb4\x19\xde3\xcf\x18\x18\x18\x14\xfc\xc4\nS\x97\xd83J\x95<#\x90\xfb\xc6 0}\xac\xc6[},\x06-\n/M\xca\xe36\x95j\xb9\x16]WV\x80C\x97\xa6\x18\xbc4\xec\x9c\xd5\x9d0w\xe8\x01I4\xb6\xf3\x06r\xf8\xa1v\xd5\xfc\xe4 l\x90 )\x19b\x0fg\\[\x9e\xe6\xcb%\x89]\xef\x0d\xe4\x9b\x9b^\x8d\x1d'\xf9\x95\x0fE[U\x12\xa4\xc2\x10^X7\x90\xa9!\xe3\x03W\xe9!K\xc4Fr@/\x8b\xd5`J\xbe_\xbay\xff\xed\x06\xf7\xdar`\\[\xdaI\xbc)\x84!\xbf\x19\x87\x1f\x1a7\x7f\x1d+\\lnv;\x18B\x8azR\\\xb1Ue\xe4\x9f\xa2\xfd3)\xdajG\xa0\xdc\x15\xa0\x87\xe0'O\xd8\xa6\xe6\xc1\xb3e\xc1n!\xa9\xbe\xd8Xe\x97\xfaU\xe7\xde\xee\x847\xda\x05U\xf3\xb0\xac!\xaa\x0f\x80\x14\xf1E\xbb\xbd\xaeV0\x9e7\xef4C\x98\x0cq\x0el\xab\x08\x0ce\xf5@/\xed\xd6t\xd4|\x9f\xd6Zh\xbd\xbb\xb5\xa4<`k\x81\x0e#{\x91\xa5\xe4\x18\x82\xba\x14\xcf\xdb3\x9ew\xf9-Zw,\x16y\xf6\x90\xe6,U\x0cj\xfb}\xc8\xce\xa1{\xce$6\xd9,\xd93\x8f\xb4\x08\xd7\xa4(\xc9\xe5m\xfe\x9e1\x8c\xc3\x14\x11\xaa\xe6\xf4\xe2U\xa1!m\x8e3J\x8aw$\\\x1bZE\xd7\xe6FYu\xab\xed\xba\x1a\xadp'\xfc\xa0\\&\xc93\x93g\x0f\xfe\xf10_,\xf3\x8c\x11\x03\x05\xe9]\x00\x90'l\x1b\xbf\xb4Q7\xaf\x9fU{\xc9\xc7\x10\xa6C\xea\xcf\xcd\xf5\xff\xce\xfcfa\x8f8\xc6x8{\x042 U\x95\\\xf1:\xb9\x0dd\xcc\xb1\xaah\xcb\xa4\xa33j\x14kUQ\xa1\xc2\xc9\xee6\x86\x02\xe5^M\xe3FL\xccN\xcb\xca\xac\x9b}je/\x08\x1a\xca\x1c\x86\xab\xd9\x9c\n\xd7\xe1\x9d\xb2\x02v\x8aY\xcdr\xd6\xc2&\xd4\x12\x14\x86\xdb\xe4\x14\xf5Y\xf4\xadp\x91<\x1c.\xcc\x164&n\x97S7\x94\x13\xd7_\xbe\x00 \xca\"\x1a\xa7dA2|\xbfM\xb28\xbf}\xa3O+\xdb\xef4@\x9b\xaer\x99gq\x92\xcd>\x94D\x96\x93\xfaG\xd6\x1c\x9e\x0f\xcfxh\x9c \xcbc\x82F\xfd\xfb<\x8c\x1c\xc9\xf0\xe0i\xe8(|\xab5\x8e\xd0-t\x9f\xaa\x163y\x10\x85\xd9\x87\x92\x1c\x9d\x9dT\xe0\x1b\xe7\x11\x1a\xef\x06\xc9b\xc9{\xca/'\x9f<\xb1}\n\xe6a\xf9\x96\x84tUH\x7f'\x1b{\xd6z\x94\xcc\xae\xe3\xf8\xa8\x1d\xdc\x98\xd9\xed\xef\xbekB\xcdwp8'\xd1\xa7\x92Af\x98q\x81?$%\x94\xab%[_\x1e\xc0\x89\xce \x08.IP\xc7\xe82=['E\x9ea7\xb4J\xf56N\xcf.\xc7#\xb8\x9c'%\x8f\x0f\x95\xe5\x14n\xf3\xe2\x13\x08\xa3\xbd\xf4\x0e\xa9\xce,\xcf\xb6f\x8c\xc6I\"\xde\x13\xd6\x8fh\x0ea \xbf\xf1H\xca\xbf\xf9z\xd5\xbf\xa1\xb8\xee7\x1f~K\xf30f\xff\xd1\x08\xfc7\x1f\xa3Q\xfd\xc6\x1ds\xfc\xd6\xd7\xc1\x1f\xf3\xa2\xc8oK\x98\x16\xf9\x02N\xf2\x98\x14Y\xf2\xf7\xa2\xaf\xd4\x1f\xd1^\x14\xfe\xc1\xb5\x0f\xbe\xd6\xd7%\x17\xab\xe94\xf9\x0c(D\x84L\x98\xaf\xcf\x02p\xa24\x89>9z\xbdUE\xfb7y\x9e\x920chq\x89K\x8e\xab\xc3\x16\x07\xd7@$\xa2\x9c\xb7\xb1J\xed\x1a\xa51AU#c\\dE\xedenW\x90\xb036\x0b\xd3\xd6\x874\x89HV\x92z\x9a\xe0Y\xb0\x13\xec,\x0b\x02\xee\xe1\xaa\xa4\xf9\x02~\\%i\xec\xc1\x1789\xbe\xd4\xcao7\xde}\xbb-\x9e\x8eL\xd0~@\xddS_\xbe\xf0[\x82\x0d\xd7 \xe3\x18\xe7Z\xd2\xc8\x0e\x83Z\xb9GjVA\xbfY\x91\x1c\xb5\x93g\x0el\x9a\xfc`\xa1PP\xad\xecM\xbbOF\x92e-\xae\xa0\xab\x8d\x1a\x15$\xa4\x12=\xb9N\x9c\xacM\xea\x1daP\x12z@i\x91\xdc\xac(q3\x1f\x84\xb3\xe47\x8e\xd0\xfe7\xaa\xc2\x84\x93\xcc&2\x05\x85\x9d@Mb\xae\xbdr;'\x95\xd8\x0c\xa4~\xf2\x10\xac\xc2\xef\xe6\x03^\xde\x07\xe7Y\xb0\x83\xaa\xd6\xc9\xa3!\xd3\xd6\xd1}\x90\xd2\x118aJ\xffL\xee\xf4\x90\xbayF\x8b<\x1d\x81\x13\xd1\"m\x7f?!4\x1c\xa1\xdb\x82\xb0\xfd\xf1b\x9eLY\xcd\xa8W\xcd>\xd7C\xb0\xd0:\xb6\x03\x0e\x0dW\xb3\x90&k\x82\xf3\xd3\x86\x12\xf43v\x92\xc7\xc94!\xc5\x05\x0di}\x8d\xd4\xfe\xd4bO%\xa0\x16\xad\x1b\x83\x8aS\xc43dc\x83\xaa\x90PC\xc1\xb0\xf3\xbau\xcd\xf2\x08K\x99\xb9\xaf^\x1b\xd4_2\xf7e+=\xe1j1\xbb\xdcv\xf4\xd9k\xfc\xf7t\xf7\x95\x1e\xfd\x9a\x8b\xe4w\x9f\xeb\xe5W\x98\xfe\xec{\xb3X\xbe4b\x151d\x93h\x92S\x18\x93\xdd+!\\\xa7\xe8\xb5\xf8\"\xb9I\x93l\x86\x1eu\xa6IQ\xd2\xc3y\x92\xc6\x86)_\x8b\xab\xf6\xc4\xedc\xafH\x90d%)\xe8\x8fd\x9a\x17\xc2\xb1D]\xa1q0\x91\xad\xaeB\xd4\xc58\x0dQ_\x8b?3\xe94XM\xb7Z3\xb3ob\xdcl(07+\xeaTaK\xec\x840\x8fI\xa4\xcc\xb8]\xb8\x95\xba\xdc\xee\xba\xe0\xd7\xf7\xdc\x82\xbdCk4\xafh_\xf5\xd1\x88g\x1c\x1cZ$Q\xb4\xdaA\x91s:l2\x97\xd6\x03l\x88\x1c\xae\xba\xcf\x9d\xec\x1a\xee\xdfb\xac\x1b?\xef\\\xf1;v\x12\xf0`\x9b\x08\x89-\x0eK\x0355+\xed\x1eFl\x83\x89\x8e\xe5\xab\xc4\xef\xddK\x87|P\xcfR5\xfbZ\x0cc\xfc\xe6\x0861\xa3\x15\x8b|U\xa6w\xe7d\x99\x86\x11a$?\xe3\xe3N\xc2\xe2\xd3j\xd9DS\xeb\xb6k\x8c\x9e\xf2-\xef \x05\xcfuD\xd2d\x91P\x12_\x92\xcf\x03\x0d<\xe4\x84\x11\x8571K~\xf9\xbda\xe7\xb4\xe6\"\x1c\xe8>\x17\x9e\xa7n\xe1\xeb\x14\x08\xeb\x19\x8a\xf6\x18\xe4\xe4x=\x02\xfb\xe0\xae\xf0\xde\xcf\xf3!v\xf9u(E\xd5||\xeb\x95]-\x8b<\"e\xf9\x01=\x14\x97\x03\xc4e\x0d\xeb\xae\x9d7\x90)\"\xe67\x90\xd9u\xab+\xf0\xb2\xea\xabHS\x98\x02oXm\xf5@\xa5]\x7f|z1>\xbf\xbc>98\xff\xf3\x87\xf7=j\xf6\x88u\x0b\xe9\xd8\xc7\xe7GJ\x11\x84SJ\n6\xa7}\xd1\x0d\x06\xd9\x05\x9c\x9c\xfd<\xbe\x1e\xff\xe5\xf8\xe2\xf2\xf8\xf4O=\x1d\x9a\xf2\x0eL\x85\xb8\xf6\x9f\xd4\xa3\x8b\xf1\xc0\xf9 \x1b\xf3\xf3\x18M_\x8e\xffry}xvz9>\xbd\xeci|\xf5\xe8\x8d\x9f\x8fq-N\xcf\x8e\xc6=m/\x9b\xeb0T\xc9\xe9\x9e\xf2\x9a5\xa6>\x88\x1a\xb3{\x01\x9a\xd3\x05#\x9f\xe7\x94.G\xdb\xdb\xb7\xb7\xb7\xc1\xed\xb3 /f\xdb\xbb\xaf_\xbf\xde\xfe\xcc>kd\xf3\"\xa4s{\x99W\xdb'!\x9d\xe3\x9f\x93wZ\xc9r=3\x16{\xba\xb3\xb3\xb3]\xaeg\n\x01\xfe8C\xed%u\xd5\xe8\xe9\xb5\x0d\xf6\xc9\xc5\xc1r\xc9\x10(\xfe@S\xde\x0f\x19\x0f~\x1f\x85\xe9[y>*\x94P%\x826\xaa\xbfvV\xd3\x1f\xd6N^L\xa9\xad\xb4aI\x17\xac\x8e\x1e\xdb\xdb\x8cQ\x8d=s_\xed\xbc4\xd0\xf1\x99\xfb\xf4\xc5+\xcf\xcd\xdc\x97\xdf{AR\xfe\x1c\xa6I\\\xc9\xe6\x1a\xb9CE\x19\xdee4\x7f{\x12nV\x94\xe6\x99\xd9\xaf_4'\xd1\xa7\x9b\xfc\xb3\xf9k\xb2\xc0\xf8\xfe\xa6O\xf3$\x8e\x89\xa5\xd2\"\x8c\x93\xdc\xf2\x89\xa0\xed\xa6\xe9S\xb9\xbaY$t\xd4\xd2L\xb6i \xe9\xeb\x8d\xe2\xee\x0dv\xc8\xe3\xa0H\xfc.\xc9>10\xac?`x\x04\x99\\\xb8\xce\xab\x97N\xaf\xae\xb2\xde\xcc\n\x95X]\xadR\xa9\x9f\xc8\x93\xf2\xec\x10\xe5mR\xc7\xfc\xd5\xab\x9ev\x0c\xdePZ\xed\x88Q\xf5\xb4\xf4\xba\xd1\x92\xfc\xc5\xc002\x9a\xd2\x8a\x88\x11Ch-P\x18f2\xa1\xa8\x93\x19N\xb8.\xd6\x15\x17N\xcb\xee\xf0\xb7\x82\x84\xf1Y\x96\xde\xf1\xb78)\xc3\x9b\x94\xc4\x8c\xbcb\xfd\x1f\xa1\xcb\n\xe1 \xeb\xd7|%\xc3\x83\xc6\x10\xc2o\xd8\xad\xdfX\xd2\x12h\x0e!\xa3y\x160MH\x1a\xc3mB\xe7\xf9\x8aB\x98\xc1o\xb2\xc1\xdf`\x1efqJ\x8a@\x91\x93\x16$\x8bI\x01!\xb0\x8el\xe5\xac'XC\x00\xc7\\\x90\xc7\xeb+\xe7\xf9*\x8d\xe1\x86\xc0bEY\x171\xd4\xfeo\xc22\x0e\xbd\xf7\xfd\x16\xc0\x19\x9d\x93\xe26)\x19\x99@(\x90\x84\xbd\xab\x1d\xc8\x0b\xf8M\x8e\xf8\xb7\xc0d2n\xd9~$~\xf8\xfc?\xe2\x94\x8b\xbe\xfc\xb7\x98\xf4C\xd1\x97\x7f\xd2\xb4\xcb\xd2#H\x026\xf3\xbf\xeb\xc8?\xb5\xda\x13-\xdb\x9b\x16u\xc8m|\n\xbf\xcb\x99\x11\x94q\xdb\xfc\xbf\xd3J\xb0\xe5\x08\xe95\x9b31\xa9\xdc\xff\"\xe4S\xf8\x8d[~m\x82\xf3[\xd0\x0ckh\x94]::m\x00\xa2Oq\x0b) \x18\xbc/\xf2%\x1aE\x0c\x83\xcc\xa62td\x03^6\xbe\xc8\xa4\n-%\x16\xd1\xa4\xb8b\xc74\xe7\x9a\x1c\x06\x88\x8e/\xee\xeb\xf2\x0e\xcb\xa9D\xf5\x89\x83\xe0\xcd%\xdb\x89\x0c\xfb\xc7\xba5\xedV\xdb\x99T\x99\xafP\xd5\xdeN\xde.u!\x81|zI\xd4&d\xcd\x08\xfdY\xc7\xbe\xa6.V\x9a5\xf5\xf1\xb5\x8f68(\xbc\xa8\x12\xff_\xf6\xfew\xbdm\x1cY\x18\xc4\xbf\xf7U\x94\xf9;\xa7\x0f9\xa6\x15\xc9v\x9cD\x89\xe3\xe3v\xdc\xd3\x997\x89sbg\xfa\x9d\x9f\xc6G\x0f-A\x16'\x12\xa9CRv<\x93\x9c\xeb\xd8o{\x0d{\x01\xfb\xec%\xed^\xc2>(\x00$\x08\x14H\xcaq\xf7\xf4\xec;\xfc\x90X\x04\x88?\x85B\xa1\xaaP\x7f\xc4_\"X\xf5\x8d\x15\xc4\xdf\xee\xfb\xc4\xa6=\x8d\xbd\xeb\xa7\xea\x11\xaa\x8d\x84\xd9a\xf5Z\x1f\x81|\xdd4\x06i)vVn\xc6V\xc1\xb7+$T\x94Ql\xd7/\xe4\xfd\xa9\x1c^m|M\xb3q\xb4\"\xab\xc8vJ\xf2{\xa4\xfd\x10\xce.*\xf8\x1aFI\x10?\x1c;\xd5!\xb1\x08\xe8\xfd\x12|\xa7\xe4\x18\xb7\xcc2\xfb\xe2\x1f*\xf5\x8c\xa9\xc4\xb1]\x88\xa0\xd2f\xa0\xda)cI\xa9\xd5\xa0k7Z\x95T\x15N\xab\xcb\xd26|UO\xe5\x98\xb4/b*\x90\xb3@\x92L\x96\xc8h\x18\xc4\\@\x06\x8f#\x8a\xc4M\xb6\xc1\xc1\xaa\xa7\x95<\xd0X\xf0\x0dv\x06\n\x0bd\xae\xd6\xca%\xabN\x83\xdd\xa6)\x0e\xb9\x8f\x95\x8a2q\x9f\x8e\xcc\x87\x16\x0du\x00\x8f\xb0\x0e\xfeQ\xf0}\x82\xdc*\xda\x1f\xa2\xa0Xa>9\xe5FB\x80N-\xa2\xa4\xba\x9a\xec\xdbwFZl\xb1\x9a\xcf{i\x16#\xec\xc2\xedZE\xadV\xd1z\xff)\xa1\xfb\x89\xdd!%\xb2q\xdc\xa8cjW\x84\x87\x90\xb4\x10\x15\xe1\x04\xc4\x0fg\xcf\x9aK\x08*\x00#\xcd\x8a\xf89\x06Q\xb2\x071\x03\x7f+\xab\xdc\xb3G\x91H\x99\xb9\x95\xfal\xc4\x7f\xa1\xaa\x1e\xffp\xdf\xf8\x96\xd06\xd6\xef^\xc8\xd9y\xc1\x15\x9c\xeb\x0b\xb75\x10\x7f\x132\xa6^\xb7\xd0\xea\x12\x17\x8b\x18\x81'\xab\xaca\x85\xbd\x94\xbd\xceU\xd0I\xd7=\xb7B\x1e\x12b\xf5\x10\x91\x88wUl5\xfe\xe6\xa8^%\xb6\xaa\xc40\x84Z\xfcG\xbc\x8dV\xe9\x9a\xd1T\x07\xff\xc4\x97\x9f\xd8\x9d|\xf7\x89\xdd=\xc4Z\xd17\xcb\"Tf\x1bAV\xac/M\xaa\xbdCo\x08\xdea\xdf\x11y\xd1\x1bb\xf1\xae\x9d\xba\x9bH\xf8\xa3\x80\xfd/\x9c9\xf6=4J\x08\x14u\xf7\x1f\x8d\x0e\x87\x97\x8f\xae\xc3\x0e\xe7\x87\xbaZ\x1e1\"\x96c\xa3._\xc5\x0f\xfdV\xa0\xf4q\xda.\xa0\x1c\xee\xf2\xe2\xe1&@\x11\xe0\xf0U\x8466\xea\xa3\xb7)\x87\x95\xf8\x8dQ1Y/__ D\xf4w\x05\x83S\xbd\x18\x04\x81\x06M\xff\xb0\xff\xe5p7xx\x80V\xf8J\xd3\x8a\x07 \xce\xec\xe2\x8a\xf6\x0fP\x916\x18\xec\x9a\xd7\xe6\xf2z]\xde\xab\xef\xef\x05\x9d=\xda\"BN\xec\xb1\xe4\xbf\xd6l\xcd\x04\xdfP\x8f\xccm\xb7@h\xbbJ\xdb I\x94\x1a\xcf?\xfd\x14+\xe8C\x0csQ\xa9\xb8\xe4\x82\x8ah/z*B!\x11\x014\xb3\x8e@\x92\x04fF\x8a\x8e\xf2\xf7\x0b\xd8\xed\xe3\x95\xdb6x\xe0\xf3&\x86\xc0q5\x93a\xaeB\xf0\x02^\x16x\xa0g\xffs\x87\x16p\x9d\x1fh\xeb\xed\x1a^\xa2\x0e}\xad\x03\xbd\x01\xdb\xed?\xce\xdf\xa6\xeb\xa4h\x97\xa0\xd4R\xd1\xfd\x83n\x86RH3\x94\xdeXH\xfclZ\xdaT\xd77\x89!I d\xaa\xecr\xbb\x08\xed\x8b2\xd9k\xe9\xbc\x88U\xed\xe1\xa9mc\xaf-\x94\x9cEu\x84\xd2\xeeb\xbd\xf1\x8a\xa1\x95\xa9\xea,\x87#\xea\xad\x08\xbf\x88\"\x13\xf5\xcd!\x8c\x8a\xcb\x10\"\xebB\xbb\x11 \xaf\xa51^\x07\x11\x93\x91\x03%\xdej\x03\xa5\xbe)\x07\xda\xecM \x07\xfac\x9aM$-\xe8\x8aM\xf4bH\xe3\xder@Z\xc3(\x98\xf0\x11\x15fJ\x0crH\xf2\xe6\x1e-\xaa\xba!T3\x9aH#\xf4rd\xd8\xf0\x7f\xf0\x9e\x14\xac\xaa2\xbdo9l=\xc1\x82\xa6\xd4\x97\xbf|\x02\x99\x85\xf5_\xd5\x90\x17\x84\x9b\xa2a\xd2\x80\x86\xc9e \xf0\xb0\x0b0\xcfYA\x01\xd2\x05\xc5\xc4 E1[?\xa1\xc0\xf8\xe5\x0b\xd0\x05\x870\xba\x0c\x02\x85\xb0|\xd4\xa6{\"=jy\xe3\xe4\xd8=\x0e,\xa86\x8327\xc7h,\xac7\x96\xc9\x0e\xf9\xf9\xdb\xbe1\xcc\xe5\xec\x0093\xd6\x99.\xf7I]\xc0\xee\xae\x87#\xe7\x07\xea\x86l\xc77x\xc9'\xfe`/\xa0\xb8\x90\xbd}\x9a\x0b\xe1<\x86\xee\xaf\xa9\x8f#\xbd\xff8\xba\xdd\xed\xdeT\xc1\xdeP\x928I\xa7\x8c\x16j&\xf3(\xe3\xa5h/\xccP\x1b\xc0yI_(\xbaU)^M\x0d\x84?ARZ\x06\x0e\xf6\xf8\xde\x92\xc8P\xc0\xcbC\xd8\xdbE\xd5\xc1^\xa9[(`\x08\x1bJ\x9a\x15h\xad<\x15\xd2\xc5`\xf7)y\xdd\xbao\xde\xc2b\x98\xc7\x91`\xa1${si\xb0\xe3k8\x04u\x0d]\xe9V\xeaurB\xfbR\xaf\x81q\x0e\xcb \x80\xf5\xb2 \x86,\xa8+k\xec\xdb\x89\x85\x90\xeae\xde\xc3M\x97[\x18a\xf3\xf7\x18\xaa\x8b\x05|\xdfD\x8dJ\x0fdf,\xf2\x84\xe24\xa15\xe9\xd3\x0c\xe7\xa4\xd4Ex\xb5\x8c8\xa8$\xd2yO\x1a\xf7\xaam~X\x0f\xfe\x9e\xe8w\x01\xc2\x8eK\xf4\x94\x04\xbc\xea\xec\xbe\x08\xb5\xfb\xecI a\x8c>\x83j5\xcff!4\x82\xbe\x93\xbc\xa2\xf7\xe3\xcaJ\xd3\xb2eA&1\xd2a\xe7\xb3\xde\xd5]\xc1\xde\x08u\x12\xcd\xf8b6\x9a\"\xe8\xe5\xac\xf0\xc5\x0f\x0cb\xdd\xe6\xdec\x8e^\x05\x87\xc4\xf5\x9b\xc7yo*\xe6\xa5R \x0e!\xe2EJmm\x16\xba\xc1\xa0\x00\xaam\xfc\x01n\xf2G\xfa\xc6\xff\xef\xbe\xd8\xf8\xfa\xbeG\x94\xc4\xa8\x0b\xc5\xfc\x03\x9b\xac\xb3<\xc6$\x86\xebP\xf8r\xf1\xf7mWB\xb8w\x8d\x8dk\xedX\xc5\x95H\xaabs\xab\x9e\xa7|(\x84s\xb8f\x1c%\xe84z\xda\xce\xd2u\x82~\xbcY\x9a\x16\x8e\x9c\x98\xe6~\xc6I\xce\xa3\xfc\xa3BhmB\xc0\xec`\xf3q\x15\xc4\xb0\x99{\x16&B$fuq\x8e\x01\xcb{ \x94\xfe&u\xec\xc5c\x90\xfc\x1a\x14\xf4}\xe4\xc0\x02\x02\xd9\xd4\xf3\x95\xcc\\V^\x94\xb9\xc6\xa7\xae\xdbb\xdf\xb4u\xd5\x9f\x08\x15\xaar\xd4\xeeyjg|\xd4qV\xe9(\xb9l\x99\x18\xb9\xdb\xaa\xe4w_\xeb\xb2~3\xef^\xa2E\xa1\x19(;\"yH\xc3\x12\x91\x92\xbdL\xf9\xa9l\x9cD\x96,\xe1K\x89\xb9 \x12\xf9\x13\x0fl.\x89\xc8\xdfe.fyh\xf0wE\xc6\x98\xe5\xd8EN\x14\xcd\xb5Y]B\xf0q\xdbh{\xa3\xe8!w)l\xb1:\xc6\xd0\xa8d \xcb7Q\x08\xef\x83\xc7\xa6\xbeD\x08\xefOLY_\xba8\x0e\x1e\x93.\x8e\xcf\x06OZ%\xac\x86k\x04\xce\x06Q\x97\xc0\xbc\x81]G\x19\x17\xf2\xf7\x1ce\\\xc8\xdfw\x94q\xf1\xfe\xc0Q\xb6\x82Cx\x0c\xea:\x9cH\xa2<\x05y\xfd\xbd&iV9\xd9\"\xe4\xb4w\xde\xc8D\xdf\x84\xb0\x0c1\xd1\x1bnKL\xea\x96\xfa\xd7A\x08W\x98kv\x8d\xd9\xe4\xf6\x82\x10\xc6\xfcL\xf1\xef*6\xfbV\x90\x99S\xf4\x05?\x82)\xefo\xccE\xa4\\\xfd\xeaW\x06R\xcfa\x0c/\xe1\xf69\xdc\xba\xb6*\xdf\xa6\xfe\nc_p\xa2,\xa3\xe4/\xe1\x10\xae\xfc\x1b8\x84\xbb\xd1\xede\x08\xb7!\xf0\xc1\x99Z>\xb3\xa1$\x80\xd3\xd1-\xe7\xf5\x974\x11\xe1OI\xc5\x96A\xb7TA\xa0\x18\x9a\xbdf\xbf\x17\xd0\xcfjw\xff\xa0\x9a{\xdc\xb9\xb9\x9b\x0e\xad\x1dtn\xed\xb6Ck\xbb\xed\xad\x9d\ny\xe5\xc6\xbd$\xda\x891i\xe4\x7f\x14\n\xc3\x11\x17K\x86\x80\xd9\xf5&p\x04\x13\x18\xc2i\xad\xba\xe9\xeax/\xcd\xa9\x14\xdb\xc4a^j$\x8a\x10\xbc*\xd3\xb7g\xfa^H\xd3z\x9d\x0d\xe3T\x13Sv\xa5Y\xfcW\x95\xde\x1d\xcf\xdf\xf2\xe5\xf1\x04\xed\xca\xa4-\xda\x0fQ\x1eO\x8e\xd7\xc5\x9c%E\\\xa6bpV\xff1\xcd\x96\xef\xa3,Z\xe6F\xad\xd5jA~\xfe\xbeJ V\xf4V\x19;V\x05\xaf\x97\"!1\x16\x9c\x9c\xbd\xfb\xf1\xf5\xef?~8\x1d\x1f\x7f\xbc\xf8 _\xfd\xf1\xf8\xcd\xebW\xc7\x17\xa7\xf8\x83\xbf=\xfb\xf0\xfa\xff\x7f:>\xe3\x7f\xee\xe2\xcb\xf7\xb2\xbaU\xf0\xe6\xec\xf7g\x1f/\xea\x1f\xe2\xaf\xf3\x9f\xce~\xc6O\xc6\xef\xcf\xde\x7f|\x0f\x87\x8a(|W\x81T\x86\xcf\xf5\x13\x7f\xff\xb1yE\x9f\xca\x92\xdd=\xea\xf2\x1e\xbf\x19\x04\xb5C*\x9f\xa7\xb7\xaf\xf8\xa2\xc6\x1c4\x9d|\x9e\xecm_`\xea\xf9 A\xa1\xa3\xbbE\x1aM\x87\xcdbG\xb9\x16\xdf\xd2;A\xfe\xbb\xf5\xbeH\xaf\xd3u'V\xdf\xd5\xf5\xea\xbe]\x97\x13?\xe3\x7f\xed~\xcb\x18\xa6\xf7\x1d\xc3\x04\xa3=\xaf\x05\xe2\x7f\xcb\x08\xe6\xf7\x19A\x1d\xb1#\x85\xbe\xfdg&\xfe\xaee\xd1\x9ee\x96\x92\x0bV\xa7OZ\x9e\x10nEJn\x13&\x1e\x15\xf5\x92\x8a\x1c{zJ\xacv\xcf\xa26\x89\x89c'{|\xab\x8dW\xe9j\xbd\xf2\xec+\x8c:%\xf0J\xcc0\xaa\xae\xea\xf4\xc3\x13\xc8kT\x9ab\xcaK\x17\xf9\xf1V\x19\x1b\x97\xed\x8fSD=/\xa4\x89\x98gU4\xa0?\x17}i\xc4\xd0S\x17\x97\xd8\xa6E8\xbd\x12\xe1p\x10^\x8d\x1a9\xe8o+NV\x9c\x1c\xc5\x95\x94\xcay\xdcp\xc7X\xb3!\xe2m\xd1cY\xd6XKx\xd2\xf3\xc6\xe8\xf2H\xc4,K?\xb1\x84\xae ,\xa8\xa5[#]e!\xf2RM\xe6l\x19\xd15&\"\xc2E\xb4t\xf8\xfb\x8b\x9b\xb1kV\xf8\xdel\x91\xdeR\xe1\x82d\xc4\xf4uO\xe2x/\xbf\x8d\xae\xafY\xf6\xf1\xf5\x076\xc5\xb8\xcf\x822\x85\xe0E\xe51+t\x063\xcep\x88\x1c;\xbd\x84\xdd\xf2e;\xcd\xcc\xa4\xfe\xea\xe1\x8d\xbc\x9e\x92G\x04\x7f\xf2t\x9dM\xd8P\xe5\x90\xa7\xe1\xc1n\xd8b\x08\xdem\x94%qr\xed\xa8%%\xc1!x\n\x8f\xc4\x91\xbf\x8c\xee\xe0\x8a\xc1\x1a\xddgCXEy\xce\xa6\x90\xa3y\xc5m\x94\x83\x88\x0e\x86J\x8e\x9ce7,\x83\xf7F\x95\xe4\xdf\n\x89ml*\xc2|a\x1eRQ\x9b\xb0C\x0cB\x88z\x18J\x0c\xed+~M\x10a\xafm\x00\xf2\xfb!\xc4j\xdd\x03?\xa2<\x821\x13\x97qH5\x0c\xdf\no\xa8\x1e\xdc C\x88\x88.\\$U\xa7\n\x14\xaf\xf6\xeb\x92\x04\xd6\xb8\x11c\x11X\xc3\xb9\x11\x059(\x13\xab\x91u\xd62\x84\x87\x98\xa0\x9b$Tu.\xac\x8bt\xf5L\x84zu\x11\xb3\xa4x\xedhk\xa6\xd59g\x93\x8c92\x9b\xaf\x9c&\xba\xfc\xb9\xce\xa2\xa4\x18\x8b\xf3\xdfS\x03s`\x1e\x7f\xf2I\xca\xabrp\xa6+\x96K\xfbF |\x16\x01\xac+A\xf5\xa0\xc7\x9e\xa3l.}\x15\xcd\xf7JKy\xc5\xa5 A\xc0\x16p\x04\xf3^\x9dL\x1c\x82\x87\xf2\x06\x9a_\xf2\x1d\x92\xf7\xae\x8a4\n\xfc\xa8\xcc\xf8\xba\xc6\xbbM^\x96V\xbbgEy\x9d\xf3G-:\x89\xfc\xae\x8f\x14 \x87\xb0&\xe9\x8a\xcc\xc1[\xce\xc2\x9f\xa0\x06`*\x97s\x1cs\x08M\x82\x10f\xf5\xf79\xae3\xdf<\xe8\xba\xd5y\xf2\x93r\xf2\xb3\x00\xd3\xec\x99\xf2\x9b\x83&\\\xa5\xd3\xbb\xa1ji\x1d/\xa6\\8{\x15\x15Q\xe0\xaf\x1c\x8a\xcdu\xb6\x18\x8a\xe0\xce\xbe\x87T\xe3c\xb60Y\x0e\xf5\x08\xb8\xc6\x0eD`\xd1\x94e9\xc9\x96\xf2\x07AH\xb2\xcdPR3\xe2N\xdcI\xafB\xb7\xb0\xf9[\"U\xa9\xac\xc1w\xdf\xb7\x10\xb3f\xe2\xb2\xeeH\\l\x93b\xfd\xa9a\xe7\xb0\xcb\xce\xdc\x84\x8a\xd0\xc1\x00\xd4S#lr\xfbL26eI\x11G\x8b\xbc\x9d\xc4\xa5m\xb4\xcdI\xa3\x1eb{M\xee\xb3e6\xd9{r\x83\xb4\xec=\"r~\xc7\x0d\xe4\xd6\xe9\xb4\xdb\x00\xb98\xf3D\xba:\n\xc6\xf6c\xb6hV\n;m\x8f\xb3\xb2\x8fV!\xa1h\xe5\x1b\x8a\x96\xadVt\xd8j\xc57o\xb5\x1a\xbaG\xfa\xbe\x1bO8\xc7\xefF\xf7 f\x08(z\x13g\xd81\xac\xa5\x0e\xa6!8`\xa1\xd5\x12\xc7\xd4\x10\xd6\xee\x9aj\x11\xc7\xeb,\x1e\x12V\x04\xd0\xb8\xc3\xb2\x07\xd8af\xd2U\xf5\xb4\xef\xb0t\x93\x1df'\x9c\xbe\xd7\x0e\xa2\x95\xa8\xff\xdcJ\xb5\xe7a\xb6\xd2o\xe6\xd4\xfa\xbbm\xe3\xbf\xff\xe6\xbc\xff\xf1\xb7\xd9\xe6\xfc\xa5\x8e\xbf\xeaZ\xe4\xc1x\xc7\x99C\x13%\x90\xfe\x9a\x152\xeb\x1f]+\xef\xc6\x7f.:i\xcf\x84\x824\x8d\xf2\xbds\x0c\xae\x9e\xbaR\x15 \xbdh\xbeb\x93\x96\x8a\xabrx-\x15\xa7Ho8\xe68\x96\x0e\xcbQ6\xa0+\xdc\x94W2(}\xcd\xe1\x08\xfe\xf6\x15\x9cR\xc6\x12\xdb\x93\x08AW\xb9\xae\xb7\xb8T-.\xe9\xeaw-\xec\xf9\x95\xd05dD\xa4 \xfe\x8c[4\x97\xb7p\x08\xfeJ\xc3\x07\x1f\xad\xe2\xff\xf65\xe8E\xd3)\xde\x11E\x8b\xff\xe0\xf0\x11\xd6\xfa\x82-\xa3\xdb:%\xae\xaf\xf4\xb2Y/\xce\xcf\x8e\xcf\xf7\xfc\x80\xcb\xb0\xfd\x10\xa2J\xa0\xbe\na\xd2\x13\xb1\xf7\xd9\xf4\x1cul\xbe\xc8\xac\x0cC\xa2\xee\x8c\xcfXV\x08\xeb^\xe2\xbaU\xd1-\x1c\xd5\"\xf6\x89\xa6\xb2\xaa\xa9\xdb@\\\xa6\x9f\xca\xb4\xf4\x87`\x08\xfa\x7f\xfb\x1a\x82,\x0c\xe1\x96\xb2\xe3\xe3[\xee3\x1c\xc2i\xe9\xd1\xe0;\x88\xc89\xd1\xbc\x93\xa8\xf2\xf3|\x85a\xcc+\xd9\xf2\xd1_\xf24 \xa1`\x9f\x8bG\xabE\x14'!\xfc\xee\xd1\xef\x1a\xa8\xbcw\"\x82[\xee\\\xdc\xad\x98g4\xf6y\xe7\xf6\xf6vg\x96f\xcb\x9du\xb6` ?\n\xa6\xb6b\x13\x04\xb5\xba\xa6\\\xb3z3VL\xe6\x8eY }\xfd\xec\xd8'\x18\xd6i\x08\xde*\xcd\xcd\xdb\x0c\xf5\x94d\xf5\x9c.\x97\x12\xfd\x8dc_\xe0i\xe18\xf9e\x9c\x1bt\xf3\xe2`N\xb3!\xac\xfd\xa0g\xbfw}\x9f\xaf\xd2$gD\x03V\x81\xd5\xc0\xd7\xa0\xc7\xf92\xbf\x99[\x02\x8d+\xd3,KYo\xcaO<\xf7\x92#\xf5\x97.\x91B\x1b\xfd\xe5\x0bx\xaes\x0d\xd4\x15\x88\xfc\x02;9\xd5>\xa3\xed X/\xfd\x84\x0e\xcc_\xbe@\x06G\xb0hWw\x83\xa6\xf2v\xd0Z\xe8\xa8\xd2\x86\x8e\xeaqhP\x7f\x13\x16\x85\xa0T\xe0yG\x158\x94\x8c\xc1\xd8=\x00\xa9\n\xb7\xf9zP\xdd\xfd\x03\x00\x8f\xf5\xf2\"*\xd6\xf9\x05\xfb\xec\x9a\x08\x85\xe6\x98\xaai\x03<\xaf\xacQY\xa0l\xfch\x04D\xcb\xc5r\xb7\x89\x9b]\xf5K\xec\x90\x06\xae\xf9\xa6\x0c\x00P\xfb\xc4m\xf2C\xe7\xa6\xd2\x1f%\xdbh!M*\x17\xad#}\x03\x8bL\xa4\xcd\xe6E\x99\xdc\xb9\xc2sp\xfb\x10\xbc\x10\x98H\x16%\xc2\x04\xe0\x0ft\xee\xc5\xbf\xc6S\x96O\xb2x\x85b\x9e\xfe\x91\xf6\xbe\xf6\xa9\xfeA\x93m\x92\x96k\xcb\xf6\x0e\x02\xa0|\x86\x00\xfd\xec\x7f\xf3\x18\xbd\x01\x1a\xd7^\xfd\xf6l\xab\x10\xad\xfe\x14-\x17\x82\x81s\x99\x10\x95\x19\xa7\xc8\xe8\xbb\x98k*\x15!U\xeb&\x12Y\xb3\x89\x84\x91\xbb\xb6v\xb7o\x0d\xac\xd1\xd8\x94\xdedR\xea\x89\xab\x0bk\x0c\x87\x1cM-g\xea\xc6\xc4p\xb2\x19\x91\x0fT\x13X8\xa2^\xcc\xb3\xf46\xe1\xa8\xaa\xd3\x9f 4q\xfe\xb7\xb7\xf4\x8b4\x9a2a\xc8vq\xf6\xfb\xdf\xbf9\x1d\x0b\xeb\x8bs|\xf5\xf1\xfd\xab\xe3\x0b\xfdU3^\x98\x16\xc5\xbf\x14Z\xacUh\x86Flh\xb1=\"\xb4\x11\xa5\xed\x91q\xd2s\x0e\x9e\xd9 *PrH\x16\xe9\xf5\xf5\xe2\x9b\xcc\xd1\x08\xe5\xe5}\xac\xa1\x88e\x93\x064\xf9X@\x8ep\xc9&\x96\xbf\xfcH\xcc\xcc\xd3W\xa0D\x9br\xb2m\xba\x86\x1a\xfd\xbf\x07\xf6\x97\xafK;\xadL}D\x07AG\x03\xfd<\xc3\x8bmi\xae\xcf\x92\x9b\x9aA\x7f!\xcd\x17\x95\xc9?\x92\x1b\xe4e\x95}?\xe7\xbcr\xcd\xe0\x7f\x95\xe6\xc20[\xfdz\x1bq\xc1M\xf5%\xed\xb7e1\x9e\x9e\xd6Z\x90j\xe3\xf1U:\xbd\x1b#\xf6y\xb6,e5&\xb3T\x8d/\xfe\xf4\x9enN2Vx\xbfk4\x18\xd5\x1b<\x7f\x7f\xf6\xee\xfc\xb4\xa9E\xb1\xd3\x9b\x9a\\\xd7\xe1\xc5\xc14\xfe\xe3\xf1\x87\xd7\xc7?\xbc9%\xe6,\xa06\xbe\x91\x08/\xa7\x8d-\xde\xeb\xd8\xbf\xd1\x02\x95R1\xc2\x12\x7f\xb7O\xba\xc2\x0e\x1e\x9b\xf1\xad\x84/\xecc\xb3\xbap\x85}b\xbe\x16\xee$\xfb\x8f\xcd\xf0\xa8\x0b\xe19kjK&b,\xfbf\xf5\x99\x18\xcc\xb3\xc0\xf7\xe2\x82e\x11Fv\xaaWYq\xfe\xdf\x1f]b,\x14\x8c\x9c\x91p\x8e\x1a\xe2\x04\xe4K\xdf\xf4ui\x94\xd2@Sl\xcc\xe3\xbc\xbe-*\xc8:\xdd}Q\xfa\x9a\x87\xca\xd3\xd5l>\xf7\x13\xacdFQ\xe2+u\x17\xc2U\x08c\xe1\xea\xda\xae\xe0\xc50\x10\x98 \x0b\xf3R\x9c\x94\x9e\x8e'V~Z\xf5tr;\x15148\xe4\x1a\xf2\xad\x89J\x88\x9fM\xd5\x80\x96{\x1b\xebk\xdf$\xec\x16\x12\xe9\xa7\xee\xc8\xe7\xa6\x9eMT\xa9\x9b\x8c\xa8\xfbH\xec\xbe\x08\xf3\x13\xf4P\xc4\x10\xb5\xaf\x15B\xdb\x95>K\x07 \x0e[8<\xa4n\xe3\xce\x85\xd8k\xbd?\x11\xdc\x02\x1d#\x8e?\x9f\xe0\x10NF3\xcc\xfas2\xf2\xfe\xfd\xdf\xcb\x8d\x85\xafn8>\x9d\x8cn.\xed/\x8f\xe1\x10>\xa1\xc3\xb4\x7fC\xdc|\x9d\xc1!\xdc\xc0\x11|\x86#\xb8\xf5=\x96\x14Y\xccr/\x80!\x1c\x97~\xd9\xf6g\xe8\xd4\x85\xb1&\x84~\x1f\xfb\xef\xc9\xafyoF\x82@\x8e\xf5\xefQ\x1f?\x86C\x98\xf8\xefeT6v\x0b,\x08\x02\x8c\xe5i\x86\xbc\xe2\xd5\xc7\x98\xb3\x13?\\\xf8\xe3\x10N\xe55\xb7\xb8\x93S\xa8\xa0\xdf1\x8c%\x94\"^}\x16\xc24\x08B\xf8\xcc[\xc0\xbc_\xe5\x02\xf1\x1e?\x89X \xbc\xf5s\x19i\xf4\xb8#\x95\xf9T\x05c0\xb4i8\xba\xef\xbf\x87\xadk\x0c>\x8f[}\xeb\\,\x90\x1a\xda \x0e\xed8\x08a=*\xb8\xa8z\xcc\xff:\xe5\x7fMC |\xa49\xfc\xee\x9c\xf6ObNC\\D\xbej\xb7\xbe\x9a\xa6\xe3\xaeS\xc4Y^V\xd5\x91n8*\xcbU\x1d\xc2\x19\xb1U\xe0\x9a\xdeV(\xd8_I\x1f}\xfc\xff\x84O=\xe6S\xbf\n\xe1ntuI\\\xa8\xa2\x03x\xea\xa7\xbd\xf7\xb0\x0di\xefG\xf8\x1d\x08o\xff\xf3\x00\xe9\xef\x1d\x1d\x80e\xc3(\xf7\xfa)\xb0\x95\xf8\xfb\xfb\xa8\xd5\xddJ\xfc\xc7\x83\xc0\x9dQP\xf6\xf5\x04\xb6\x0e\x1d\x829?\x80\x0f\x02\x99\x9f>\x04/\xb2ds\x10\xc9w\x86\xedDL\xf5f\x83\xdc\xc0\xb6^\xe5\\!\xefg:\x07\xdaxLG\xc9|B\xe5\x85\xe1l\xc1^\xe0[9cd\xb0\x8d\x83A\xe0{\xafO\xc7\xef?\x9c]\x9cy\xf7\x0e\xb0\x11\"g\x92\x92\x894\x84\xc2\xd2z\xbdp\xc5M\xc3P\x82\xeb\x00\x12\x0ci\x89z{\x7f\x8d\xb0\xc0\xa8\x902\xc4/\xf1\xe1\xf32 \x0e\xbc\x84\xfcy \xbf\xe3G\xc0(\xdf\xde\xbe\x14f2\xff\x1d\xfb\x0bl\xed\xcb\x97\xaa5\x1a=\xcd\xa8\xe2\x9d\x17hw\x10\xf4T\nb\x1a\xa4\x99\xb8\x8fP\x95d\xd0\xdd\xcdzq\xa1\x01u\x0bb/\xb5\x8d\x0e&\x1d\xa7GN\x06\xd3\xac\x07\x8btj\xe4$\x8a\x08\xcdy\x8ca\xe8F\xf1%\x0c\xe9\x13\xc1\x0en\xaf\x07 \xad\x97\x1e\x19\x91\xef\xab\xc3hX\xffL\x86\x88:\x82\x08\x86T\xe4\xf8\xce\xd0\xdf\xdb#\xa0\x9f\x8d\xbc\xf1x\x92fl\xe7/\xf98\x9fG\x19\x9b\x8e\xc7\xe2\xa8\xf7]e\x87\xf0\xb7\xaf\xad\x1b\xcf\x01\xd2t$r8\xfa\xa9\xd0\x9c\xfe\xedk\xd02\x1f\x17=\xbd\x9fF\x91%\xeb%\xcb\xb8\xf04\x84-\x7f\x00\xdf\x03E\x01\x94\xf7\xb4\xaa\xb7\xeb\xa8w\x9b\xc5\x85\xaa\xb3\xef\xa8\xa3\x14#\xb5\x82o\xba\xd8\xa9Z.\xb7\xef\xfe\xe3\xc0\xdf\xd2\xb5\xd4\xfc\xddA\xe0\xcbh\xbf\xe0\x89?\xbc\xa6$\x1a\xa8g\x1e\x17p\x08\xd2\xa2\xaeT\xca\x8f\xe3\xfa\xcdG\xe8>U\xf8\x98\x98L}/\xda\xb3!Rj\xe0\xc71I\xc5\x12xyXQ\xc6#b\x15%L]<\xe34M\x98\x9d\xe0\x15\x86\x18\xcc\x0d2\x91\x7f\xa0\x9a\xdb\xf6a\x19V\x8f:Feg\x04\xaf,\xfb\x19\xd4\xfb\xd1\x10z\xc3cr0\xa0\x03R=\xde\xbb\xefv++4\x05\xd3\x8fC\x88\xc4y(\x17>\xf5\x0bS&V\x0f\x1e\x05~\xe2(\x15A\xa6]\xd1\xd2\xe4\x98rx\x01}\xe1\xd7\xfeR\xb8V28\x02\xcf+\x85\x00\xbeP1\xb6\xa4\x05/\xcc\x83\x00^\xc0\xe3\xc7\xbb\xcf\x0e\x90\xbd\x83\x97\xf0\xf8`o\xf0L4\xb4\x0d\x03\xe9\xa8\xc9iKd}\xcc+\x88\x06\x0e\xf6v\xb1\xf3\x887\xf0do\x7fO\xf6/\xeacG0\xc44H\xe2m\xbe\x88'\xcc\xcfC\xec\x04s\xd5D\xb0#\x9b\xd9\xe6\xe3\xdc\x91\x83z\xf1\x02\x06\xfd\x00\xb6\xe1\xe0\xf1\xe3\xbd\x83_v\xb7\x9b\xfa\x11\xa9\xab1\xb1G\x86-3\xe9\xbeT\xd5\x98\x1a\x9c\xb5\x0c\xf1a\x9e\xc6RWs@\xebj\x06\x96ng\"\xeb\x9b\x83\x94\xca\x9a'\xffT\xd6\x10\xcf?\x955\xfa\xf3Oe\x0d>\xffT\xd6\xfcSY\xf3Oe\xcd/\xa6\xacqjj\x06duw\x18\xd1\x03\xc7\xdd\xc9\xe3\xbe\x83o\xd3\xc2\xb3w\x12DQ\xfcL\xdb$\xa5\x0d\xf9\xca\xb7Q1\xef-\xa3\xcf6\xcf J\xe2\xa4\xc3 \xe9\x18\xb0d\xb4\x19\xf2\\}8\xe2b4l\x83\n\xc2\x19\xfb\xcc\x88\xc9\x0f\x1b\xac\x8f\x9e\xc8#4\xb2\x96\xc4\xb9\x9e1c%_\xbf\xceOK\xb9/,\xd27\xe9$Z0)\x1b\x95)Qpo\x9c\xcd\xbc^\xbeZ\xc4\x85\xef\x85\xde\x86\xec\xfb\xde\xde\xaf\xa2Dq\x04\xad\xdd\xa5\x95i\xc8o\xe5+6A\xfa}\x8f\x15\x95\xea\xb2H.hk\xca\x14\xcd\x13,\xc2CH\xfd\x16Q\x923?\nF\xf1e \x13\xef\xa4z\x92\xf3\xeeh-b\x17\x87J)h\xddR\n^v\xff\x89 \xab\\nL\x07/{`\xf2\xc4\x13Zs\xc2Y\xd9\x89\xca\xcdl\xb3\xb0\x93^\xce\x8a\xd7\xcb%\x9b\xc6Q\xc1l~u\xd2\x9b,X\x949j\xcc\xb1\xc6[a4\x7f2\x8f\x92\x84\x19~\x867X\xe3U\x9c\xaf\xa2bb\x98},m\xe5\xe55\x11\xca\xe7\xae\xed@CA\x1e\x0ea\x9b\x9fe6I\xe6'\xcf\xb5\x99:\x85\xce\x90\x01\x9a\xe1\xc5\xb5\x93\x9b\x95A\xd2x\x85\x10\n\x9f\xf0 \xa8\xbd1\xa6s\xd5\xcad\xdf\xc9\\ \xc2Q\xa5\xdeV5\"<\x96\xa7(D\xae\x1a\x9b\xac\xa5\xfd\x18]\n\xad\xed\xe09D\xd95n\xed\xbcR\xec&\xcf\x03\x95C\xa3,\x1d%\xdb\xdb\xe6I'\xf7\xcf\xf5h{{y\xd9\xb6\xd0\x02(\x7f\xe5\x0c&_\x87\x9b^\x92\xde\xb6\xb6\x86\xb5\x9c\x0d\xcd\xe1H(\x13|$\x93\xec\x16\xe6A\x8f\xd3\xbd\xdd\x10R\xfcc\xd0K\x93*\xb4\xf9\x95\x08T\x1f\xf9qo\x95\xe6\x85\xdc\x85Hk\x06\x18\xcfi\xd2\x8b\xa6\xd3\xd3\x1b\x96\x14o\xe2\xbc` C\x9aN.\x86\xd6\x00r{\x93^\xbc\xe4=\x9e\xa3\x17P\xceG\xd6<\xb5\x89>\x06<@=/\x04\xefw\xf54\x07\xf6\x88|ON\xc8C\xaejK\x8c\x1c]\xa5\xd2$c\xd1\xf4\x0e\x03\xee\x89p|(]/|O\xf8&a\xaa\x15\xf7\x88\xf2^\xb4Z\xb1d\x8a\xf9\xe8}\xed\xab\xa0g\xb7\xdc\x86\xc3y/c\xcb\xf4\x86\x89\xc6\x90g\x0e\xcb}\xea\xf4\x1c\x80\xa6\xcc\x959+.\xe2%K\xd7\x85\x86\x11\x9c\xe9\xa8\xbe\x0f\xeaF\xb3\xd6\xf7V\xa4Y\xa4\xd5C\x98VM\xe0_]\xb9\x15\xf7`\x1b\x9doh:\x8a\xeaF\x9a\x1f\xbf\x19\x02k'\x9b]\x1cv\xdc]\x13\"\x1f\xc8\xae\xdb:n\x81\xde\xa6\xec\xce\x13:D\xff\xe0I{V3G\x9e\x8f\x0cie\xea\x17vj8\x91\x90\xa8-\xb5q\xdc\x9b\xb9\xb2\xfe\xfa\xfd\x10\x92^\xc6\xf2tq\xc3\x02\x8cl\x8f\xa9\xfc\x96\xb1\x96\xdfjC\xc0X\x10\x10\x80yF+\x01\x91\x0dDg\x86v&\x90\xe2\x00\xe9|\xf3\x98\xc7\x8f\xcb\xc9Z\xdaT\x91wF\xb2x[[\x9c\xc9\xf3>\xb0\xeb\xd3\xcf+\xa4\x8di-%\xe6\x86s\xb6\xf8<\x95\xb0\x81\x9c\xf3\xe3{\xe1\x82ZN?\xed\xc9\xab7\x11\x9aA^\\\x89w\x9cK\xb10>\"\xc2\"F\xd2A\xc0O\xf0\x161\xeb\x9d\xa3C(\x17ac\xb7\x05\x00\x88l\x9e\xb6\nA&\x8c\xf1B\x88\xee\x0d\xc4g\xae\xdb\x84Zf\x97Nr\xa9\xa6\xeb\xc9\xea\xc9\xc57\x1a\xd1\xee\x9eC\xa69\xd8Cyc\x12\x15\xbe'\xf8)O0\x1dB\xc2\xab\x875\x9e\xd5\xeez5\xbe\xf4]\xb4d\xbf\x8e\x9c\xbdk\"\xa2\xdc\x934~Z\xe6\x0fR\x9aylj\xce\x854c\xdd\x9eKaf\xcf\x14Z\x16.@\xbc\x92\x0e\xc8\xba\xe4&\xe0&lS\x8e`\x01- peF$\xcc\x98'\xae\xf9\"\xbf\x90\xda\xb7\xd2\xccL|`\x1eH_\xad\xaedN\xa5\x92\xf4\xa6\xfeV\xd6\x9bii\xfdB`\xa3\xe2\xb2m\xc5\xcc\xe5Jp\xa7\x96\xb1C\x1el;\xa8D\xae\xf8\xc9\xa5\xe0\x8a-~\xa6\x13R\xb9Y\x94\xd2\xdd3\xf1\x1f\xef\x99\x18Ty\xeb\xd4\xfdr\xbat\xd9v\xed\xf4\xec\x80\xde\xa4O\xcc\xf7\xb1c3\x08\xf4\xb6\xac=\xe4\xbd\x93\x95tGS\x94Ey\x1e_;\xd4Q[\xb8\xb5[L\xaa\x944KE\xb4-\x1c\xef9\x92\x9c\xdf-\xaf\xd2\x05\x15[\x06\xb9\xe9\xe8j2e\xb3\xeby\xfc\x97O\x8be\x92\xae\xfe+\xcb\x0b\x8f<)e:\xd1'!dJ\xbf\xe4\x05\xbdY\x9a\x9dF\xad\xd1\x1a\nq\x86\x18\x0e\xadA(,\xc4r\xe1l\x1b\xf0\x0e\xca\xf3I\xdc\x95\x89\xa2\"\x08d\x98L\x0f\x93\xeeVn\x16_\xeb\xcc~\x9b\xd7\\\x84{\x9e\xc3\xdc\x94rC\xa49\x83PFK\x9f\x85\xa8!\x89{\xb3\xe7\x90\xc3KX<\xb7\xf9\xd2\xb2\xe5\x95\x90=\xd7\x9ap\xbc\xe0\xc2q(\x14!\\\xfe\xf3\xa7\xe510\xf1\xa7B\x98\xf1\xa7A\x88\x8a\x90y9\x86\xa5H\xc2u\x03/a\xf9<\x00I&\xa6!\xead\xe6\xa3eiQ\x95\x8cV\xa8S\x1f\xad\x1c2\xb8\x96a\x0d\x86\xdd\xb2J\xb5\xed\x9eA\x9f\xe6\xd7\x06\xa6nI\xec\x9e\xdd\x03j\xf7\xf8\xbc\xe0\x80s\x8f\xfe`\xf7 \xa8\xd9{<\xc5\xd7\x8f\xf7\x1e\x93)\x1a\xd6\xd4\x98\xa1t\xd7\xcc\xd2U\xae\xb9\xfdV)\xd4\x95_o\xc6f\xb9\xcc\xe2\xc7\x7f\n\xafh\x9c\x19\xea\xef5Jc\xf7\x9d\xff\x1d\xfb^\xd4\xdd\xa8\xd7\x9aof\x9c\x7f`\xd1\xa4\xd0\xf3\x10\xf2\xed\xa2W\xc9e>\xfd6\x9e\xb1\x8c\x85e\xe4\x82wg\x89\xc7\xbc\xbe[\x87e\xca\xf8\xa7\x8f\xbd\xa0>\xbf\x9e\x91\xd3\xbf\xbc\xaf\x0ceD\x05\xa2\xae\xcab\xafR\xb7\x85\xe0\xa9)\xd4u\x06\xfa$gi6a\x1f\xed\x00\x01\xe4j\x19\x1d\xfeX}\xab\x04x\xd6qp,\x04O\xeb\xba>\xbeE-\xab\xf1Z\xcfj\x9c\xd7\xf3#\xb3[X\xd4^\x1a)\x97s.\xd3\xe5z\x03ZkA\xfd\xcb8\x7f\xbf\xce\x98\x85\x15[\xfd&\x95AY\xd3r\xe5\xe2\x8di\xa5\xb9\x86\xa8p_\x82\x92\xf8\xcf\x02\x9b\xbc\x18\x0bc\xf5l\xfe\x90\xae\xafa\x861\x0c\xba\xfe\x07\x91\xcb\x13q\xb5k\x1fjk\x10\xf5+X;nb\xee\xbf\x04\n\xe8z\xc2\xb0\x07n\x9aT'\n^\x84\xef.\xf1\x17\xdf\xb8\xf5_\xbe\x97q\xdc\xed1q\xaf\xe4\xa1\xc9\xf0A\x7f\xd0\xdf\xfb\xc5F\x9a\xf8\x8f\xf7\xefm\x9d\x86\xe2\xd6\xd6`C\xd6\x98\x1eP\xed\x82\xf0\xfc\xf4\xe4\xc3\xe9\xc5\xf8\xd5\xd9\xf8\xdd\xd9\xc5\xf8\xfd\xf1\xf9\xf9\xf8\xe2\xa7\xd7\xe7\xe3\xb3\x0f\xe3?\x9d}\x1c\xff\xfc\xfa\xcd\x9b\xf1\x0f\xa7\xe3\x1f_\x7f8}\xf5\x0d\xees\x0f\xe65O\xc1u\xd7\x12\x0f\xa51\xe0\x01\xed\x92\xf7\xd82\xd0\x92v^\x074\xc3\xbd\xfb\xe4q\xdd^\xf4\xc9\xbe\xfe\xbb\x87)\x13=\x91k\xfe\xbcH3\xe65\x98}\xaa\x05\xed]i\xb3\n\xabV\xd2\xe5U\x9c\xb0\x0fl\xba\x9e\xa0\xd7gkKi\xcd\xdb\xa0j\xe9*N\xa6\"\x8c\xd0 \x1fY\xda\xa9\xb1\xd8\xd1X\xb4Z-\xee\xde\xc6\xd3\xe9\x82\xddF\x9d&\x189Z\x9ap2\x9fwia\xbd\xb1\x1b\x85\xe3 Ps\xe8\xd0g\\\x1bs\xd1\xd3o\xcb\x80\xc9|\xb0V\xf46\x8e\x8aFJO\x92.a\xf4\xb3\xda\xad/\xe7\xb1\x11\xf9\xc4\xb5\x98(38m-\x15\xf1\x16\xff\x88:\x9f0\xa5/\xc5BED*\xe5\xd3\xcf+\x8c\xf9\x00\xc5\x9c\x01K\xe6Q2a\x19\x14)\\1\x88\xca\xe9\xf6\xa8\xe8\x8ajq}\x16\x08C\xd9Z\x0d[+A\x8e\xa9h\x1bS&\xb0\xbf}H72\x99/\xa1g\xc6{j\xfb\xf5\x84pM\xe1\xef\xf1\x9e\xda~\xbd\x92\xa7W\xad\xa0D\x88)\xa9\x8e\x9c\xe1\xda\x8a\x1c(\xe2\xfa[X\xc6\x06&\xb0\xe8F\xe7MVS\x8bNM\xdc\xd0L\x8csAX\xd3\x82,\xd4\xe5]\xebj\x80v}M\xa5O\x95s\x98\xfaA\x08\xb32\x9a\x8dU\x0d\xb4\xa94\xda(\x8a\xd4\xdb\x0d\x15@\xea,\xb6\x06!\xef\xd5\x1e\x91\xfe(\xd9}&\xb23\x9f\xd9W\x14\xe63C\xfd\xc4\x84\xf9I\x08\x03\xda\x8a\x0b\xac]A\xbfu\xad\xe4\xd2\xbd\x92[Y/B;\x02k\xe9d\xf08X\xae\xf3\x82/\x19\xc6\xe2\x05!x\xe5=\xf8\x983\x98\xac\xf3\"]\xc2\xb2\xa4\xe8\xa8e\x88\xf2\xbbd\x02\x91\xf8\x9c\\^#-:\xeb\xa1l`\x0d\xe1\xdf\xca!Dw\x98\xb2}\x1e\xdd0\x88\x12(\x83\x1d\x83\x87jiPvG=\xf8\x89W\xb9K\xd7\xb0\x8c\xf3|\xc5\x16\x0b6\x85\x08PD\x89\x92\xe2\xe8\xdf\x1c\xa3Y\x11\x00P\xa7g\xd9\xfdT\x1a\x804\xce\xcd\x1dFs%E\x1bNSr\x7fA\x9a\xc2~\x85Y\x9cD\x8bEc\x1b\x03\xfb3\x9b|\xe8\xf6\x12\x9c\\\xcd\xc4\xd9 \x93\xa6k\x89\xe1\xb7\xb7]\xc8\x7f#3\xb6\x17\xa3\xc4aD\x92\xb6^\x80\x82\xa6\x92\xfb\xce]m\xe9\x0c\xc8\x15\xf7^\xbf{}Q\xff\x94V\"\xadI\xc3L\xb5hd\xec\xf1|}\x95O\xb2\xf8\x8a\x91\x11\x96\xafKq\x87\n\xf5\"\xe4'\x89$m\x92\x1f\xdc\x9bp\xf2\x93,a\x9f\x8b\x0f]O3\xf5H\x1d\x0f\x05Y\xf58!\xac\x1e*Th})BX\x8f\xd2^\xd4j?sS\xf9)\x11I\xacu+Fz\xb8\xdaJ\xb5C\x1a\x14\xb4 5\x91\x0e\xeb\x8b\xbb\x15\xa3\xe0\x9d^\xc9t\x89\x12\xd8\x8a\xec!\xac\x9d=\x96\xe4\xb6\xddJ\x9f\x95\xf6\xd4\xe2/\x7fn\x9e\xeb\xfaC\x93~@)\xa2\xe1pQ\xa2Ma9\xc3\xeaO\xa3\x0d\x82z\xd6\x89\x06\x7f;l\x90z\xba\x9cQ\xf8&\xe8\x843P\x0d\xcf\xf2&\x01\x81|\xcc\xc2\xc6\xf2\x05\x11)\x87\x0b]\xb4K\xecc\xeb\x0e0&Q\x91\xef\x94!x\xff\xfe\xef\x9c\xb9\xfc\xfc\x88\xff\xac\x07\x93\xff\x06\x89Z\x17\xf1\x1d~i\xd6\x9d\x8d\x14E\x1f\x9bWB\\\x1a(o\xc7\x84\xd8|I\x84\xc2Qfk.\x9f\x87\x9cp\xfa\xad\xd7\x10\x1eh\xa5Mo\xad\x8c\x1f;\xb9a\xb3X\xaf!\x92\xb9\xe2\xb5\x81\xe8\xa6v\xc1\x1c5\xea4\x90{\x89\x91{\x01\xcc\xd7\x8a\x7fm\xa1hS*\xdal^\xbc\xc0\x1b\x93\xc8b\xcbxs\xa8$\xe6\x1cIQ5\xd1\xb7\x9bH\x90\x1d\x17\x8e\x07a\xcd:\xda\xb3mY\xc8\xa3\xca-\xd7%\xba+2\xbe\x91\xf0I\x02^uV\xa1\xf7\x83 \xda\xe3~\xd0\x8bzB\xa3e\x82~cm\xd5\xa6\xf5\x9dkm.u\xc9\xcc0\xf2.\xacP\x97\xc7x_\xa6q9exIq\x19\xa8Y\x83^\xda\x8b/xQ\xc5\x18\x95\x08\xd0|\xda\xd0\xac\x8d\xdd\xf8\x80n\xbc\x18\xf5/I\x04)zBz\xf5k\xb0l\x18AWB\xca\xfc\xa2\x87j\x18\xc9\x80\x87\x15T\x88\x13\xc88\xec\x1fDq\xf8`J\xbc\x10\n\x15\x00\xb9\x8b\xf2S\\\x10\xd5(\xb7&}\xc0\x11xq\x12\x17q\xb4\x107P\n,*\xabr\x91\x82\xae\x9b\x83!\xa6\x1c\xbf\x89\xd3u.\xd3)gl\xc2\xe2\x1b6\x85\xab;]\xffP\x8b\xec\xaakM\xcb\xd1w\x81e\xb5g\x9f8\x9cQ-\xdb{y\xb1i\x1e\x19\xca\x84\x9frG\x1d\xc0#\xd3\x98]\xb8Q\x1cA=b\x02\xe5\x90\x86r\x0d\x1cA^\x1e\x07e\xc5j\xf5)}5GJ\x8a\xba\x13y\x06\n\x97Q \xaf\x1f\xfb5\xcb\x95\x82KXh\xc3kW\x8d\xf4\xaa\x0bL\xee!\xe8y\xc0\x17\xd6\xa3i~A4\xa6\x08z_\x18\x9fp\x1c\xe3@,\xf8\xaf\x9d5\xc7\xaa\x9d>G\x96d\xb3\xadS\xed{\xa7\xbd\x9c\x96\x0f\xa8\x84\x0e\x9e>\xe2\x08\x92\xb6t\x87\xa5G\x1f\xbe\xae\x0f^_\x0cm\x80Ay\xb6%\xfe\x9e2\xf0\xde\xdc\xfc\xb6\xcd\xbcag l\xbf\xe5\xa9\x8b\xb6\xf4}\x18j\xb1\x01\xd2\x92\xb0g\xc1s\xd8\xde\xe64={\x1e@*\xe8y\xe1\xb3Qr\x89\xcaT\x87\x1dh\xba\x19\xd4\xb5\x83\xf1\xc9A\xe0{E\xfaq\xb5b\xd9I\x943\x97\x15'}Hv\x02\x0eqA\xaf\x06\xb0C\xd8\x1c\x8bh\x97\x94\xaf\x7f\x81>_\"%\xc6!\xec\x14\xf0\x12R \xcb\x14\xb6\xd1h\x0b]\x81\x12Y\x90r|\x0c\xca\x8f\x12\xd8>\x844\x10\xe0\xe6\x1f'\xf2\xe3\x04v\xf8\xef\x97/1v7\xff\xe3\xd0\xcczU.h\\.U\x8aK\x95\xc1\x0bH\x9f\x07\x10\x8f2\xb4\xa5\x19e|$\xf4a\x17\xb7\xac\x92\xb9D|.\xc2\xc2\xd5\xf7F\x7f\xfe\xf3z\xb7\xdf\x9f\xfe\xf9\xcf\xeb\xe9\xd3~\x7f\x87\xff?\x9b\xcd\xfe\xfc\xe7u\x7fO\xfc\xec\xef\x1d\xf0\x9f3\xb6\x8b?glw\x86\xdfL\xf1\xe7n\x7f&J\xfbL\xfc7\xbb\xdc\xdc`W\xce#\xe9\x15,/\xdaM\xcf\xbabG\x08\x19\x85 \xa9\x03A\xe2\x86\xbdD\xac\x1a\xdee\xc6\x12\x03\xf8\nmo\xa7\x97\xb8v)\xbc\x80\xf8y h\x9e\xcfw\xd7(\xbdD\x0f0\xc76\xdb\x90\xb8U\xdbl\xf0\x9420\xae\x84\xf1J\xcdA\xc6\xd7\x8fI\"\xe3\xd6\xb3\xa0\xe1\x9a4\x04)\x9c\xf6\"\x05\xad\"H\x89[\x83\xa4M\x84US-\x99,ZQ-v\xde\x11(\xdeLXldhx5\xea\x13\xa6\xcf\xa0\xd6[\x04*\xb7\xc5{<\x0f\xb9\xec\xe5\xa7\xd5A\x17c\x1eHs\" \xc7)r`\xd7\x07`\xd7,q]e\x00\x88{9o\x14/\xb4\xbe|A'\xc1\xdaG_i\x94)\xbfO\xd8\xad\x1f\xf7N\xf0\x17\x97\xe38\x0bo\xe0\x13\x7fT\x15\xcc\x8e\xa0\xef\x9ax3\x94\xb3ng\x05\xfbd\x19\xf5\xc6\xba\x04}\x9c\xdf%\x13%,\x9b\x82tM\xd6vUZ\xeb\x95~\xcf\x12\x116\xc0U;\xd7k\xbf\xcf\xd2\xcfw\x97\x8e\xab\xf7\x16\xf9\x18\xad\xff\xdb\xc4\xe1\xcc\xe5F\x81\\\x0c:\x95\xe2_\xeb\xf2\xaf\xb8\xfc\xab\xcd\xc8\x86\xa2\xdd\xb6\xd6\xa1\xc52\xb8y\x92\xa5i\x17\xb5\x01\xdd\xeax\x0d\x11m\xff'\xfe\xb4d\x86jmY\xf8\x8fm\xd2\xecWj\x11\xf4\xd4\x10\x1b\xa2\xfa\xa0\x1f\xf8\x89\x7f\xb0\xff$\xd8\x88{ih\xd0\xdc%b\xf3\xec?i92\xcbKo\x19\xfa\xc8q\x80\nv\x15\xad\x0c\x95.\x06\x8a\x92h\xab\xa2-\xe53\xb4\x95\xfa\x89\xf0kV\xf4\x1c#\x02&h\xae\xaa\xf7\xc7x\x97m\xa7r\xc3\xacim\xdc\xee3\xda0\xe4\xc0\xca2\x14\xa1\xb1n\xed\x15\xa7\x07\xbbm\xd8\xae\xd8\x80<\x84E\x08\x13\x8a\x19@g\x02\xf8\x9e\x0c \xaf1\x8cv\xa9\xc8\xa8Dq\x07x\x1f\xc6\x019E \xfb3@\x1f\xdd\x97\xb0j&%\xc2\x8f\x9a\x9f0\x94nm\xce[\x11\xc5\x9a\xe85\xc7%\xb6\xdb\xbaq\xf08Kq\x87f\xbd\xbf\x96`\xe0\x12\x17?\xb63B\xf4\x04\xc5\xf9\xa0\xbb\xb8\xa0N\"!k!dE\xce\xfb\xdc\xc0\x0bX=w\x1d\xe5\x98\xa7\x96\x8c\xef\x02\xd2)\xba\x18\xdd\x10we\x1c\x00y\x80M\x8c\xf9\ns)\xd9\xbf\n\xe1\x0eC\x1d\x15\x88\xa1\x13\xcc\xca\xe8\x8b8F7\"\x9d\x13\x7fK\xb7\xa6\x99r\x8c]*\x1f^o\x1c`\xea\x9a8Y;\x92\x0c.\x0d\xcb:\xfd\xb9\xcaX\xf4\xc9*\xb1I!:\xa77\x8db\x0b\xa5\xf1V]V\xed\x93\xd8\xbf\xc6j\x9cA\xbd\x13\x9a\x1a\xbe\xfb\x17\xd2\xcdTl\x8bIP\xe1\xd2\xb50\x06p&\xbdl\xea\xb1 \n\xe0\x84\x04\x90\xd0\xf8*\xe2\xa7\xc4\x18+\x86/\xd0\x15\xee\xa3\x85\\\xdar\xe0\x8e\xe1|\xeb\x82\x90\x87\xc8\xa4'<\xcaQCZ\xfe(\xeaN\xe9\xf8\xd7\xbd\x84\x95o\x92\xf35\xc9\x9e\xc4\xac\x9a\x98\xefT\xcc\x97\x84\xa9e>N2\xbf\xf7$\xe8}\x8c\x93\xe2)\x8a\xb1\x0fr^\xee>\xa3B\x80r\xb1\x87\xbe\xc79\xd8\xbf\xaf\xe8)\xe2\xa5~\x93/\xddSz\xac\xbb\xedcr\xeb2b\xa1\xa5q(g\xf8l\x8e0\xf4_\xe6\xc7!$\x1dp\xa4D8x\xfc8\xf03\xc7\xd6M7\xebc\xd0\xa7\xa3RqN\xcd\xbf\n!'&v\x0d\x870\xf2X\x96\xa5\x99\x17\x827Y\x08\x7f5o\xca\xf2\"K\xef0\xb0N\xb4\x16\xef2\x96\xaf\x97\xcc\xbbt\xb9\x08\xdd9\x11&\x06y\x1b\xc3a\x88\xde\xe0ROf\xce\x154\x1aU\xe8F\x86\xb1]\x0f\xbd\xc9\xc5\xed\xd3\xdbt\xca\x9b\xdc\xdab\xda\x0b\x19Z\xd9\xb7\xeb\x99o\xbe|\xc1O3\xb9\x7f\xce\xca\x12\xc7\x1d\xa40r\x98\xc7\xd7\xf3\x9f\xa3\x82eo\xa3\xec\x93\xbd& id\xd5\xeeO\xed\x1f\xac\x89\xd1\x1d\xc1\xe0\x00\x8608\xd8{\xba\xef\x80Bm(\xfc,\xe0S\x12'\xa42\xa5\x10\xb0\x88\xaa\x82(\x90\xd9c\xd6!\xdd\x08\xc6\xfb\x9d-\xd24\xf3\xedr\x15\x96@\x08\x8a \\\xeeo\xca\x84\xed\x18\xe4R\xcb\xd8\x1e\x8b<\xe9\x9c\x8f\xd5_\x9d\xa4k\xf4\xa5W\xf5f\x8b\xf4V\xa4\x1a\xd7j\xb2D\xa4\xc8/\xf3\xb5\xb3d*\xe8W\xed-\x87\xb2\xf8\xb6|\x85.>\xc2\x9d\x05\x7f'\x8cM\x15\x91\xac5(Z\xa3\x8a\xd4\xda\x89 \x8aF\xfbbC\x9cO\xe6l\xba^\xd4G#\xf7\x8f\xf9\x12-\xe9N\x93I*\x87\xca\xacw\\\xae^\x17\xb3\xa7*\xe3|t\x1b\xc5\xc5\xab,\x8a\x13\x0dNr\xaeo\xd3\x8c\xd5\xdb\x9f\xa4S\x96\x99\xe0+{\x13oY\xf5\x8a\xa3\xc4\x1c/\xb2\xe6\x92\x82<\x0bzBE\xf1J\xb4\x15\xd8M\xb3[\x98\xfbU#\x81\xdd\x8fVX\xc3W\x97\xe7\xd7\x95\xdb\xf3\xcb\xa4\x1c[\x88\x8b:e\xb8\xaa8\x08>\xb4+\xd2\x95\x0dG8\xce\x8c\x03\x92\xd7\x17DK\x04\xa9\xa8\xad\xb8\n\xf1 \x14\"4\x03\xcc\xebV4\x06\xdb/w|\x10\xba\xd8f\x89\x1b\xda\x87\xea\xcdaU\x1a`\x14\nW\xdcx\x07 \xc7\xd5m\\\x16B\xeab\xe9%\x17\xc1\x0c\x88\xd8`\xabL\xcd\xe1\x08\xfc\xc8\xd8c\x9d\xf8\x04\xd4\x8d\x8b=\xac\xd6\xc9\xee\xa7\xaa(\xf1\xcc\xd5\x1ah\x9c{Y\x99\xb7\xde\xe4b\"\x94\x01\x8a*!\xd4%\xddRy\xd3\xc2*\xb1\xd06o\xb8N}aX\xb1\x91d'\xf6\xed\n\xa0\xb9xI\xb9\xfa!\x9c\x93\x97\xf7\x1ct\x11\x86.\xf2\x91f#\xbew\x82+B\x81\x9es&\xa2\xe4,zq.\xd8'?\x13\xce\x07\xfa\xb6A\xcd%e\xbb\nztn\xa5*1NKa\xa8W\xf7Mz\x9d\xdcD\x8bx\nI\x9a\xec\x88f\x1f\xc9\xc3a2_'\x9f<39\x9dz\xf0\xb8wLDnk\x02n\x11F\xb0\n!F\xe1\x93\x13p\xbf\xe4bb\xcc\xc7c\x0cY\x1a\x9c\x96\xf1\x97\xfb\x1c\xa3]\xf37?&\x93\xc5qi\x16\xb3\x0bi6\xc7\x1c6\xcdv\xde\xc6\xdc\x16\xbdY\x96.i\xdc\xc0 f\xfc\x94\xd6\x8f<{\xbe\x9aC\x9e\xe0({\xeb$\x9f\xc7\xb3\xc2\x0f \x9a\x15,\x03\x96L\x81\xdd`\xf0\x8f\x00s80\xb48\x10!\xfa\x10X\x02U\xbb\xb4\x8d[F5|z\xf6\xa3h\xd2\"\x0eQyd`nK\x0em\x8c\x0bXn\xda\xdb,\x96\x97{&\xb4\xa5\x8e\xaeJ\xf5\xa5\x8fw\xc0{\xfbT\xed\x9bz\x99\x0ci\x8c\xe9\x9ej\x03\xa2\xb0\xcfT,\xb6\xad\xd5\x16\x93`\xe2$\x84\xd5\xb9 \xdc$r\xc0/L\xe6\xb0b\xba\x98\x93\x8e|\xf5\xcd\xf8\xe3\x0e\x1a\x7f\xab\xd1xj\xc0E\xc9E}\xff=\xd4\xddEp)\n\xc1\x16\x1d\xf1)\x88\xb5\x9eFE\xc4\x97\x1ac s\xa0\xf9}\xb1\xa6\x1d\x89\xa2@\xd2\x92\xa6*\xe4Kx\x1b\x14\xa5\xad\x01\xee\xfb\xef\x914\x06\xa1XT3\x10d\xed\x17\xed\x94q\xa5\x87q\xf2J\xc6\xeb\xdb\x93\x9f\xea\nc\x82\x7fP\x01\xad\xea\xaf+\xce\xcf^bB\n\xae\x8d\xc7\x89\x80\x8e\xee\xfd\xc6\xfe\xf9 \xdf\xee,\x13\x82\x06\xbf^\xc5\x88,\xd5\xdf\xf5\n\xe3u\xa2\xd7)\x7f\x19\xb5\xaa:\xad\x87\x99\x90\x06\x10;\xd6\x8b\x05G\x10+\xccw\xbdq^\xb7K\xc37\"EE\x06\xe4\xf29\xc9AVG\xf4\x04\xcfoC{Th1\xdb|\xa4kxld&7/r\x15eu\x86\x9b\xa1;\xa1 \xfb\xc2\xba\x07U\xac\x9e\xf4\n\xc3\xa0\xa9\xe3*\x1c\x1a\x126;\xfcH\x1d&r\xcf\xb5\x9e\xe4\x97/_\xc2\xa0\xf6k\xb7\xf6k\xbf\xf6\xebi\xfd\xbb\x83\x10\xd8\xf6v`:]\x83\xe0\xb6\x03T>\xbd\xa8q\x17\x0c\xe7\xab\xa0\xa9\xcf\xbc\xb04\x06\xfd\x10\xfa\x1dc\xdb\x9c\xd3PPW*\xed\xc2\x97\xdd;\x97\xf3-e\x05\xc7\xfa\xa9\xef\xf1\xd7\xea\x9d\x17V\x8b\x1eP\xdfH\x9d\x88\xe2\x04\xd2*\xf5\xc6 \xba\xa3\x0d\xe1\xa4f\xe6\x02\x0d\xf3<\xa1\xe7)\x87\x04j\x92\x9e\xc8\xb0\x80\x0c\x87\xfe\xee\xc2N\xea@\xf7\xf3\xc9}\x82\xd4\xf4!\xc8\x82\x9b\x1a\x92~\xa8O\xf2X\x10\xd6\x8e\x13\xbb\xca!\x864\"\x01\x0bXV\x9c\x16\x17\x10\xce\x9c\xab\\\xeaK8x\x8bx\xf2\x89\x1ag\xa7>\xde\xb7\xaf\xb0\xc2v\xa1y\xa3zB|w(\xe6,eZ\x85\x90\xa8\xd9\x96\xe8\x18\x82\xb9d\xdarn6\xa5\x8bo%\x02\x88bS\xdf\xe3\xe3\xa9m\xeb\xe7\xf5AJ\x0b\x01\xa5|\xf2\x83\xe7\x86\xc0\xe3\x1a\xe1\xdb\xb6C\xc88z\x8eDWH\x1d-F\xa9{\xaf\xe3\x98\xdeu\x13I\xfaB\xfbU\xb9\xb0\x08\x07\x16\x0c7D\xe2\x15_$\x91\x93\xa4\x16^\x8a\xb8g\x92%;\xa6\xf4\xa0\xff\xd2\x15:\x99\xd8\x93\xcd\x1a\x02)Mx\xe2\xecd\x9a\x91$\x9f\xef\xc0\xb4\x95\x02\x0d\x01 \xa5\x0dM 1\x8a\x00\x8d\x9er\xfd\xa4r\x832\n(\xa9\x9b\xd0\xfeZ\x9al\x0d\xc3\x0f-\x99\xee\xcb\x17\xa5f\xa8n\xac\xe5\x8c\x87`\x89\xef\xa2\x9d\xb0\xfc$l\xd4\x01\xbd\x16\x97\xc40\x84s\x95q\x81\x13D\xd7<%\x81>T*\xa8@k-p0\xfe\xdf\x7f\xafzq\xb5\x8d|\xb2\x0c\xd0Q\x03\x8d\x13}\xa6\xbe\xc7\xebUJ\x82\x10C|\x18Q\xae\x04\xe4\xaa\x93\xc6\x96\x97q\xfcS\xe5\xf6\x00\x0b\x96\xe7P\xcc\xa3\x04ny\x8de\x94}\xf2\xc4\xb8P\xb9\xaa\xc0\x86\xcd*\xd1\xeeH\xad\x05\xff\x91\xe2\x95\x19\xde!\xa4b\xe1\x91\xbf\x93R\xf94\xc5\x01{A\xa8}_S\xa9HM\x91\x05@J\xa3T\xd38\x9aJ\xb5@or\x10\x1a\x82\xb0X\xc1\x04WP\xae\x8aX\xdaL\x1e\xf1}8*\x05\xbc\xa1<\"\x8f\x1cz-\xfe\x7f?\xd0u\x7f;\xa8\xec$gQ\x02\xd01\xa3\xa4\xdaJ\x9a\xc2C\xe2\x8f\x1a*\xea\xc6\xcbk\x94\xda]\x14?\xb0\xea\xa7\x9b\xa1 \x1ew\"(Z\xc3\xc4\x85\xa6\x80x\x00q\x8e\x81s\xe3\xe5JdH`6\x1d6n b\xcc2\xd2\xca\x8c\x96\x82\xd6\xf7B\xb8#\x8b\xa7Y\x14'^\x083\xb2T\xed\xcf%Y*g\x17\xc2\"\x109S\x8d\x8f\x13N\xaa'\x0deWd\x99\xa467AX\xc6\xbd\xde\x8au-!^\xeb\x8fo\xb3\xb8\xa8]\xbcn\x99/\x91\x08\x96\x9f\xcc\xa88\xb9_\x1b\xd6w\xe2\xbc\x8a\xc6\xb5E\xceP\x18\xeeM;\xc5\xb2\x8e\xeb\x06#\x1a\xef\x8b\x04\xf2\x8c\xab\x8cQ9^\\X\x17\"\xea!|\xeb\xc9X\xc6\x02\xc6\xd5.\xa0A\xac\xb20Pes 24\x00\xd4\xb2!8O\x05\xc4$1\xc1P\xb6\x14*j\xc5Jk\x1c\x8e\xbeBt\x91\xd1@k\xe4\x12\x1d&%qW\xa1\x0ej\x15^\xc2\x80W\xda\x11\xcd\xbe\xf3+\xfa/x\xcc\xad\x95b\xa2f\xd1\"g\x80\xddB\xc6\xf2U\x9a\xe4,\x04ek\x9e\x98\x17\xb0\xb5%n(\xdd\xde\x96\x93\xeb\x8bl\xca\xbc\xbdMw\xe3\xb2\x05\x88\x8aT\x15A\x08W~+5\x13\x08'\x10L\xbc\x17\xe7\x82\xc1\x98\x10\x11!\x9a\x06y\xed\xdcV-\x84\xf9\x8a\xa4 \xee\x8e\xee\x9ai\x93l\xbb\xf5\xb8\xd8\xb4\xdb\xab\xa6n\xab\xc3.\xe9\x89\xbf\xbb\x9d\xfdJ\x9e\x15;\xb1$\xfed7]o\x07\x00\xac`n\xba\xb1\xef*c+\x96L\x15P*/=\xb3D\xe4\x98iP\xa1\xf7\xc6h\xc2\x97\x0b\xe4\x91?F\xc5%\x1cA\xe4\xeb/\x02\xb4\xe3\xab~\xd7-\xb2j\x9f\x1e\xc2( k\xaf.\xb1\x8a\xf0\\J\x1c\x04OCeu`\x8b\x03\xa5\xce\x1f\x88w\x06W \x90^\x9e3|3\xc7%\xa1\x95w{\xc8\x8aU7r\x89\xbc\xcd\xf3\x03\xebR\xdf2\x82\xb1\x18\xf3&\x9d\xd5F*\x03\xf7\xdaWL\xd4\x90Jz\xc1\x1f\xc2\xc9%\xd6b9\xeb\x1c\xbdR\x11\xce\xe3\x9c\xfeh\xe0\xfe\x88U\xcc\xa5,\x87#lIXq(\x89Q\x96\xe1Qi8f\xd8^\x19\xfa)8\x90\xd6\xf0j\x11KvA\x18\x13%R\x92%p\x18\x9d\xfd\x9c\xfcB\xe9\xf0#\x0f\x0b'\xa8S\xa8\xcf\x9c\xde,\x9b\xce\x8an\xa5\x163\xb4\xff\x1cb\x0c\x15\n\xf1\xf6v\x00\xd9(\xbet\xc1\xa0Qak\x19\x0e\x01I\xa6nd\x9c\xc3w~Q\x9d\x9f\x0d:8D\x89H[l\xf9\x99\xca\xd9\x13\x850\x08\x0c@\xec\xa0\xe4cc\x93d~\x14\x08\xe5_\xa3\xfe\xa5\xb6{]\x0b\xdf\xb49S\xeb\xc6\xb5Ib\xcek_Vn\x10\xd2p\x83\xc60A\xd1\x05g\x12\x94\x82\x98\xdb\x00\xadT=(\x02C\xf0l*FRe\xb3\xa2\xdao\xc1\xe5.B=\xe0]Q]\x89\x9c\x11.G|\xe7R\xef\xc5\x85\x88\xa5\xc9\xc9\x1c\x0eM\x99\xa6\xec\xca4}\xcey\xa9<\xd4\x04\x853\xb9\xa6\x9b\x1c\xabM\xeb\x1fM\xcb\x93\x0e\x0e\x0d\xcc\x08\x0dU1\xdav\xb4\x98\x19\xde\xc8@\xfb\x9d\x00]\x9e\xb9\xc6QS\x9d2\xcc`\xf7[1\x15\xa4YJ\xdd\xd0D\x19\x1fY\xe6'\xf5\x1b\x88\xf7\xa4\x01\x12\xe0\xd9*\xd1<\x08(;CC\x0f\xc5\xb9\xdb6@U\xaaV\xbe\x8b\x04\x87\x0dr\xb2B\xc7\xd1\xb0E\x82\xb0\xe3>\xc2\x83\x1b\x99w\x87\x05e\xfd\x1c\xd1\x14s\xf2\xab\x0e\xd3\xbd\xcd\xa2\xd5F\xa7\xbb\xfb8\xef|\xf6g\x8e#\xa2<\x1eR\x8c\xc7\x83\x0c\xa5\x10\xa7[\xc5^NN\xa6\xbe\xc7g\xb3bS\x90\xc2}R\xf7\x97P\xba\xf8f\xc9\x99 \xcb\x87nnP\xf2\xec\xd6\xaf\x0f\\Z3p^c\x16\x9a\xa9\xb6\x8d\xbc\xa5&A\xf2\xd6%,HW4\xfe\xe8\x90P\xc2i\x0d\x14~Z\x9b\xa3\x90SS\x8e.[\x89\xe17R*\x95QS\xafY\xef\xa7B\xa4\xf7\xcd\x0f\xb0\x9e\xb2JQb?\xce/\x0d\x04\xd1U\xba\xf1R\x90\xa4\xb6l\x806\x93\xba\xcf\xd4<\xceG\xe9%\xd4c7kR\x81,\xf4UE\x0d\xa9\xdb\x1c\xee[\xd1K\xab\xcb8\xf3/B%3=\x85F\xc7\xf5\xfe\xca\xe1\xdc\x80\xfa\x1agt]^1\"\x83\x84Hp=\x8a/\xb5\x9d\xde\xbb\x8a\x93\xa9\xa4n\xbc\xa8\xc1#\xa7\xd0\xbd)\xdb!\xa3\xa1\xd0X\xde\x1f\x16\x81\xf2\xfe\xce\x14\xe7Z\x89\x11\xf6Di\xda\xd3\xc5\xddD\x91\x90\x9ao7\xe9z\xc2\x92\xf5\x92e\xbc.\x97\x13lj\xb3\x91k\nEak\x17G\xf6\x1c\xeb\xb3C\xbf\x8f\xf1,K\x97\xfcT\x86Cx\xfb]UV\xcf\xac\x10b\n\x1eG\x82\x05C0\xae\xe5j\xb0\xe3Mti\xa2-\x1b\x90\x88\x99Q\x16\x94\n\x83\x94<\xaa\x1b\xb4,_\xc9Q\xd7?\x97~,\x1d\x0c\x8f\xee}\xd7\x03m~D\xee\xd0\x02\xe23K;M\xbc\xaeZsn:\xf4\xb2\x8e\x84\x9f\xde\x11:\xe1\x94\xd6\x9b\x1b\xf4\x83p\xae\xb1\xb3%\xd3\x93*yA9Y\x08s\x9d{\xba6i\x17\xa7\xd6\xc0\xfcF\x08\xd4?\x96\xaf\xfd\xf2\x04 ;h\xb8\xb7\xe4=\xce\x11\xe7\xcb\xf5 &bv 5(\xf3e\x1dV8(\xbc~E\xd0\x92\xfa,\x87\x9cU\xfbYzd\xb5\x10\x93{\xc3}@\xf3w\x99\x1d~\xc1\xf2\xa1\x996\xb6`\x84u\xf8\x96\xe5\x1d\x90\xdf\x12#\xb0\xca\xcd)\xd4+\x08]Vs\x1b\xc6\xa2\x9aNU\x06\xf9\xe9\x9ca\x87\x0c\xc8\x96\x95\xa1g\xaa\xfbvDd\xafL>\xabG\xcf\xca\xd9B\x04\xb5\xe4\xff\x7f\xf9\x02\xb7q2Mom\xfa\x92\xd2\xe1\xef\x91\x93p93\xd1Y.\xa0\xc4\xb4xZ\xf9N\xf5\xc6h\x89\xfd#\xd2K\x07x\xf0\xcb^\xce\x8a\x8bx\xc9\xd2u\xd1Q\xccI\xd8-\xc4~*N\xb0\xeak\x8c\x87P1@!\xe0\x00d\xa1\xa5\xb7\xc0~_'\x05\xcbn\xa2\xc5=;V\x9f\xd3=\xabR\xa2k}d\xa8\x80\xa9}\xd0*\xffH.\x1f5\xb1\xbe\xd5|\\S\x97fl\x86\xb6\x91\xba\xec=3\xe6k|\x84\xed\xb6\x81\xa4\xb6\xc6\x02\"YX\xe2\x011g\x96d\xe9b\xd1EA\xa4C\xc7g\xbc\xb9\x05\x93?_OQ\xfc\xd0_\xd9\xf8\xc5{['D\x7f\x0f\xd2\x99i\x0e\xc7{\x1b#\x9c\x8f'E|#\xb4\xaf\x91\xfa\xf3[:\xa7/\x08\xe5M\xaaV\xd5\xaeW\xc0\xcbC\x99S\xc9l\x15\x0e\xa1\xda2~+/\xcaz\xe34Q\x93\x17\x97\x12\xe5o\xea\xb6\x87p\xb9\n1\xa4\xd5n\xa0\xf6\xdcr\xc9\xa6\xb1\x08\xce\xd2N\xc2\xea_Ta+*Rh\xd5\xe08X\xb2.za\xb9\xf36\x1c\x82\xf1\x0d9\x08\xbbNm\x18\xf5\xe2\xea|\xe8\x94\xe0lc\xe6\xd9\x11S-Eeb\x9c\xebq\x88\x9a\xf1SY$\xe1\x9d\x82\xe7\xc16\x17\x82q\xbeE\xfa&\xbd\x15 \xc9|\xa7\xfd7\x1a\x11ys\xf6\xd9\xa3\x8d{D9FBj\xa9\xb0\xd3\\#\xca'q\xdcX\xe3*N\xa2\xec\xae\xb9J\x94\xb3\x83\xfd\xe6\x91L\xf2\xdd\xb6\n;-5\x8a\xd9\xe0`\xc1\xda\xea\xec\xb4V\xca\xa2[G9h\x1e\xda\xfd{\xda\\\x95\x1e\xde\xf6\x16\xaf\xefnG6,\x8a\x931\x08\x95B.\xdc \xac\xab'\xb8\"\x81\xed\x0c\xbc\xba\x90\x92S\x11x\xd6r\x11T<\x7f\x1e\x94\x03s\xb6\x0c]p\x17:\xe1\xafz:\x0c\x12\xba\xa0!tBE\xe8\x88\x8e\xd0\x15%\xd5\xa3M\x03k\xb7\xcdd\x11\x15q2h\xed\xbdq\xf7\xaaG\xf5-\xdbl\xeb\xbaq\xbbC'\xd2\x02\x1dh\x9cz\x94\xba\xae\xc1\xe8\xa9mO\x82r\xb1h\x0e\xb2\xa5\x1eN\xb3}I\xb4\xeb\xf4ZD\xa3\xd0R\xd8\xea\x0f\xa5#\xa4n&\x1d\xd1{\xc5\xe5b\xed\x989<\x94\xd1\nE\x120\xdb+\xc4\xfb\x98|J\xd2\xdb\x04\x14\x15\x18\x82\x18\xb6[{\x88V{uJT\x05v(#\xd3Q,W\x07\xb4\xc7F\n\xf6\x99C)/\xdb\xe4\xac\xd3B\x80\x8e\x88\xd1\x08n#\xd7VR\x81\x1d\xcc\xe2\xc5\xe2M\x84z\xba\xf5\xfd{i\xc4j}^\x93\xda\xbcf\xa2\xc7\xbd\x8dzlDX]\x89),\xc0\x0ea\x15\"\xe7\xe4k\x1d\x9b\x92B\xed\x17\xd6[Dy\xf1\x8e\xa1\xa0\xadB#\xf2W\x17i\x81\x92\x92\xfe\xeed\x1e \x9f:\xdd\x1f\xb0\xa6\x0d,\xff,\xcf\xaa\xc8&\xf3\xa5\xa9\xc5\x8bC\x18\xec>QIb\xe0\xe5Kx\x0c\x87\x87p #B\xe3\x9b}\xfef\xb0\x0fG\xb0\xa7^\xed\xf1W{}8\x82}\xf5\xea\x80\xbf\xda\x85#\xd8\x19\xc0\x10vv\x1b\x87\xb4v\x1c\x9fJ\x1bXM\x7f\xa7\x0e\"[\xca\xdf\xc4\x05\x1a-Ov\x9f\xf2\xbd\xec\x0f\x9e\xed\xc2\xf7\x98\x14<\xd0\xac\x99\xeaK\xe1\xfd\xdf\xff\xd7\xff\xe9\xa0\xb2\xe8cTU\x97\x16\x83\x9ak\xd8\xa0\xe9h\xa5\x062p\x0dd\xd08\x10\xa0\x06\xb3k\x0c\x06\x7f\x9b\x1d\xee\xba:\xdc\x95\x1dv&\x9e\x85T\x88>\xa7\x90L\x93$\x12t\xb0\x1f\x1aX\xffB\xf36\xc3x^\xe8\x97YCy\\V}\x1f\xf0\x0f\x03c_\x94\x89\x0d\xeb\xfcVho*\x11\x17\xac\xa9\xa32\xc2\x99\xbe\x9f\xcb\x11\xefh!\xd0\x9a\xf7^N\xaa\x00\xf8z\x95\xd9T8\x8a\x07\xf0\xaf\xb0\xcb7P\xbfI)_\xa5n\xf4K\xf2\xee\xb6#i\x0e\x04\x80\xd7\x91\x93y\x94\x9d\xa4Sv\\\xf8\x9a\x0f\xac\x199Z=\x18b\x9f\x8b\xdd\x8f\x1f\xef>;\x004\xcc\x7fq\x08\x8f\x0f\xf6\x06\xcfj&_\x06.Y\x04m\xdfX\xb8Q_\xa4-\xd6 \xb2{i\xd6\x19Xu\x06\x97!$\x95\xa3\xfa\xce\xe0\xfeF\x1e\x14\xde\x9a3\x19\x103\xd9m\x9f \x1f\xa5c\xe1*4C\xa87\"\xd2\xc2M1\xeb7\xe2G\xda\x81$n?\xa8\x9c\xec\xf5\x8d\xd4r\x11\xe4&\xc7\x0d\xdc\xcb\xb6ksj\x10\xe8\xdb\x01\xc1\xc8\x95h\x84\xcc\x84\xdcbj\xfc\xd66\xdb#\x89T_z\x9b\x1c\xd5\xd6J\xb2\x1a\xd2\xf1\xcc71b\x0fv !\xb0bOY\xa4%j5\x1a\xf1\xa3\xd6\xf47\xed\x87 t\x0c\xbf\x86iI\x0b\xcd\x9a=\x1c\xaa\x91[\xe9\xa8\x11;\xcaA\xf7C\x04\xb0\x81\xa9\xc3\x16lX\xb9\x99\x1d\xc7\xf9\xd0\x0c\x8ci\x03\xf3\xd4\x06\x0b\xada\xf5WQ\x8f\xe7\x06\x87\x10\xd75\xd3\x8a\x91t\x0b\xff\x95\xcdmy\x06\x95\x82\xa1\x01~\\\xb6\xd0t|\xee\xb4\xff\xe3*\xef%\xfab\x96\xac\x99b\xe2\x85\x9c\xe3\xe8\x18t\x03%\xd5Mhs\xbb\xf5\xbd/\xec\x14\xd1\xe5\x9bD\xa3\x04c\x92V\x00\xd71\x89\xf3\xfc\x9c\x10$\x81\xe2/\xeao\xf0:I[\x91:\xd4\xa5\x88\xd0xK\xf5\xc0\xf8\x8f\x1cV\x1d\x9d\xebc\x92RL\xe3]\xc2\x8d\x99\x17\xbd\x81\x01\xae\xec\x93+\x8aAs\x0e\x19\xbc\xe0M(\xd2hW\xba\x91\xd9\x03\"\xbf\x18e\x97\x0e\xfe#E\x0d}\xd9L\x8a\x8e\xbcB_\xaf\xa1@\x8aG_\x08)\xdd\xc8\xce\x0e\x0e\x86\xaf\xde\xce\xae\x10\xb3\x9b\x06\x86\x8c\x956\xb2\xa0\xf3\x18v\x7f\xfd1\xc8\xb60\xf8\xce\xa1\xca\xd2Y\x1f\xd5\x1e=*\xd5y}\xfb\xb8M\x8bQOhly\x9b*\x96\x01\xfb\x8d\xaf\xad\xf3-\xb1\xa9\x8c\x1e\xa0\x01v\xc0O,\xcaMn\x0c\x9a\x05\xef\x0b\xcfijh\xf5|a\xf5\x0d\xa3\xa9\x17\x9a\xa9g};\xbe \x08\xa9C4h\xe4\x85\x1eT@\xa9C\xeb\xde\xc3\xd1\xc4\x98\xfa\xa45 \xc68\xa5\xeeu5\xa3\x9b\x1ei9Nn\xb4\\Pt\xa63LcS\x164\xa9\xd7\x11\x87\x11\x04\xb5\x84*\xf5\xb4 \xb1\x9d\x01\xabfu_Zc\x14Y\x94\xe4\xb34[\ns\x0c\xca3\x06C\x83_\xa8z\x1dl\xa7\xc0d\x9b\x8d^h\xa9*\xe9\x95\xb5\x9a]9*\xb1\x0d\x0f\x9c\xc9\x95[J\xdb\xca\xea\xf2\x983v\x80\xe068\x84\xae\xa2\xc9'\x15\xaaf\xb9^\x14\xf1j\xc1\xa0\x88\x97,w\x86\xbcW\x03\x99\xaf\x93O\xa5\x9bJ9\xba\xea\x8d\xcc\xfaW\x94W\x852ut\x88Y\xf8\xdc\x93M\xbb\xda\xc5\xf3'5Lw\xfc\xd4\x8al\xaeLd\xe1\x05\xa4D\xe0\x8d\xaa+\xdf,\xb6z\xfcZ\x99\x81Ri\x04\x19\x9bj\x88C\x99I\xeakN\xd7\x90`\x14\xf1.\\\xc5\x1c\xf4\x8d5*u3\xafT?/h\xfb%\xc2\x13\x83\xaa\xa6E\xf3h\xcc-RNT3y\xaa\xde\x1d\xea5\xdc\xa9Ff\x8bu>\xd7\x1a\x10\xbf\x0fU\x89\xb2\xbaG\x9b\xedU\xc6J_\xbd\xa8M1J\xf1S\xca\x1d\xa3\x8eg\xe4\xc8\xf4\xd1\x1c\xe9\xbfj\x99\xd3Hnl]\x12\xd7\xfa\xa2p.r-\xc9U\xb5\x7f\x9a\xe7\xb1v\xb1}\xb5\xab\x14\xc2\x88\xd4\xe6\x12j\x99GY\x15\xee\xde\x8a\x14\xa0\x0eL\xeb\xa2\xe3$Z,\xf86\xac\x16y\x9a&\x0cn\xe7,\x81\xdb2\xa9\xd2\xd6!\xf4\xcd\\\x86B\x8bi\x10\xcd\x1au\xdc\xb0\xbb\xbc\x88\x17\x8b\xdaV3\xbb,!C\xb8\x03TB[j\xa5V\x0b\xb5w~,\xd8\x95x\xc3\xe0\xee:\x816']\xa3 \xa5\xdfS\xbd}\xcb\x9d\xac\x1ay}0\xb5\xfd\xd6&)X\x00\xae\xbev\xc4\x98qvk\x8b\xb2t\x97ug\xb3\xa63\x13\x85\x13\xfd\x80\xe1P\xa9\x1dB\xac|\xa3]\xb7\x17!le\x06\"\xd1\xf2Q\xe7#\xc7\xcf\x8c5\xc2\xf3\xe5\x17:q\xbe:Al:\x174\xdf\xaa4\xc2\xb6t;)t\x88\xe25\x82\x02\xb8\x88\"\\cW0\x0c\x93\xc9\xc0\xf4-.\xcb\xd7\x1b\x0dU\x93\x15\x03\\\xf4\xea\xdc\x960!\xb6\xb7A\xdf \x89\x8e\xa9\x1at\xfe\xccd\x14\xed\xd6\x8c-\xd6l\x90Q\xf8\xc2fZ\x10Y\xe1Cn\x12w\x83\xb8\xdc\x8b\xd7\xd6\x98j3\xeb$G_\xcc#\xa9KEiv\x1aM\xe6\xf5\x8aq\x95\xdf~\x92\xb1\x1a.tK\xdf\xab\xf0*\x16D\x93\xa4\xaa\xd2\x8a\xb4\xb4\x1am\x03 \xe7\x069\x8eug\xb4iV\x10M]\x12\x99`\xbe\xc08\x80\xc0F\xc9\xa5U\xf9\xab/\xf3f\xa3\\`\xaeUX\xd34\xc2}\x97\x8b\x84g\x00\x7f\xfb\x86&5\x0c\xd0Sen\x92\xb7\x16\x89\x1d\xb9jq\xfe.z\xe7c\xfa_\xd4b\x14B\x7f\x817w\xdf\x7f/\xd5\x15;\x98\x9b!\xc5\xe8\xd6\xc32\xfc\n^ \xb5\xa7O\xef4\xc7\xba\x0b\xce\xc1\x93\xa7\x81\xcf\x87$\x916\xca\xf3\xf8:\x81!\x16=\xfbV\x9b\xc2\x10\xd2\x10\xb3\xc9\x85\xb0\x0eA\xf5h\xec\xadNv\xbd\xd6\x85\x05\x7f\xb4\xb8 Evg|E{g-B\x90Q\x00I'\xacI\x9a\xcc\xe2\xeb\xb5r\xc3\xea\xd3\xcc\x7f\xe4t\xd2js\xe2\xc2,\xd8C0\xcc\x80\xb5u\x85IT\xda\x8fU\xa7\x93\xb8\xf4Xhw\xb9\x99%Y7\x0f\xdd=\xec\xfa\x90\xab\x91\x88\xd0\x86$\x14\xc3\x8d\x13\xd4\xa35\x0cJ\xa6\xa5.\x0b\x1d!ez\x0d?\x13\xf9\xc1\x05K\x81\x9eZ\xd5*e\xfa\xad\n^\x17\xc9\xd4\xd2\x83\x83 \xc4\x8c\xa8\xa3\xcb\x10\xe2v\xaa\x1aR\x1ap\xce\xf9\xacG\xec\xb2d\xe6\xf9\x8fz\x15${\x05\xf6\xf3\x1c\xd8\xce\xce\xf3@\xb9\xb9z\x91\x07\xdb\xe0oo'A\xa5\x82\xda;0\xe5zM\x8f\xa2\xdc&|o\x96\x88\x9c\xb9XTJ\x1c>o\xb0\x90Q\xeeC\xf0\x02\xd8\xe6\xff\xfcM\xb51K\xa4\xc3\xa68;+\xc7\x81\xe7\xf0\xf5y\x9de\xec\xbcF\x04\xc5G\xf9\xc6\xb1f\xaeD\xf2 \x9eZE`\xa9\x1e\xec\xbd\xc9\x9f\xc8OB3\x01\x95\x03\xfd\x81\xba^\xfe\xfa\xad\xc4I\x88\x1cT&u\x1a\xe9\xeb\x00\xaa\xaa]\xb3\xe2\xec6Q\xd5^\xb1|\x92\xc5\xab\"5\x0c\xa8#\xd7\x07\xef\xa2\xa5\x19\xd3d\xed\xaa{~\xb7\xbcJ\x17y\x87\x93\x89\\cA\x82\xe5\xd1\x9c\xf9\x85\x89\xa7('\xea50\xca@\xe4\xe7\x81bv*\xf1\x9b\xce-G\xae4\x7fpOg\xa1H\xba\x9eQ>\xb6\xfa\xd2\x93M\xa0\xa1\x86\xfd]\x1d\x81\\\xaa\x0e\xcc\xe7\xbe\xfe\x07\x9b\x89n\xe0SJ\xe8\xb4\x9c\xfd]\xbd\x95o\xdc\x15\x8f)\xfe7\xf1\x07\xfb\xe6n\x89iO0\xce\x9e\xde\x17I\xf9\xc1Fd\xc2\xe3\xfb\xa7\xa4v\xa3\xddK\x12\x0c\x19\x92+\\!\xbd#\xc1\x87\xac\xa9\xe5HF\xd9%\xfa8)_\x8a\x08\x05\x12\xf5\x85\xb5$I\x0b\xa0\xf5>\xba1\xfcr\xe8[[R\xdb'B\x10\xd4\xd3\xc8}\xf9\xe2P\xe0![\xefR\x10\xceY\xdbh;\xa1\x05\xcdH\x15!x\xe31\xcb\xdf\xa6\xd35\x9a\x9c\x98K\x89\x8c\x8e.W\x06\"\xde<\xda}v\x81\x88\xbdX9\x17\xae\xdf/\xd6\xd7q\x92\x0f\x1d{\x8be\x99\xab\x08\xb0\xed\xe9z\xc2\xb2|\x08~\x9f\x0b\xbar\xe9\xcd\xe2E\xc1\xb2\xee\xc4\x80\xf5>\xb1\xbbs\xf6_~\xd0c7,\xd3\xc8\xb4\x13\xb4`u_\xb4d\x0bD\xa9mT4d6Q\xb2?z\xb8f\"\x16aw\xb2\xefDg\xd6[\xb2\xec\x9a\xf9N \x19\xc5T\";\xdc\x06X0\xfe\xe1O\x0f\x8d\x08\x9a\x1e\xa3\xf2 N~\x0dtH\xe8pZ\xbf\x06\x805)\xb2.\xc2\xc5B\xe5\xb6k^\x97\x89\xcb\x0f\xf3m%\x94\x0f:\x0b\xe5j2\xa6\\./e\xec\xc9\x95\xaa\x03\xc3{\xfa;\xfb/>\x83\x85uG\xc5\x19\x9b!\x18WS\x0bv\xc3\x16\xc32`|\xadl\xc9\xf2<\xba\xe6Go\xe9\xe6\x8d\xb5\x8c\x1e\xff\xbe\xa2\xb7K\xaf\xd5\xa4\xe1\xb4`\xfb\x97\xfc|\xc5&C(z\x9c\xc98W\xda$\xfc\xf5\x87\x04\xd6\x91\xb28f\xf35\xe8\xc0\xb1\xaaok\xa2\x80\xd8\xa1\xf8b\x15 \xbe\xc4l\xba\xc2G\x87\xf6\xf0\xc9\xae\xa9\xd4\x7fH\xed!Er\x08\xf7\xf8\xff\x15\xf4\x80 \x87\x8e7\xd3\x11\xd2\xe4]q\x8f\xc6\xff\xdc\xab\xfe\xdc\x0f\x02a:\xf3\xf7'_\xb4!\xa3\xeb\xc0\xe8\x80\xc67e\xb41\xc4ZI\xc7\xbd\xa0\x17'S\xf6\xf9l\xe6{\xd2\xe21\x9dA\x84g\xbd\x9f\x07\xa6\x11)\x947\xd1/a\xc7\xe9\xf6\x7fS:q\x1b] \x07ft \xa3:S\x96\xb6\x98\x05\xa1\xf0\xbd\x90\xea\x1e\xf4i\xe7z\xfb\xa1\xab\xc3>\x92\xd8\xed\x0ebB\xadqq3\xe1\x9b\x88\xd0\x90\xd7\xcdh\"\x91i\xdc*'4\xb1\xab\xe5\xef\x970\xc0\x83}\x1b\xbc4\xc3\x18)\x05\x0c!\x1b%\xb0\x0d\x83K\xa3\xea\xae\xac\x8a\xc0\x0b\xc1\xd3kj%X\x80\xbf\x9c\x03\xfc\x1a\x82\x97\xcf\xd3\xf5b\nW\x0c\"\x97Z\xc3O6\xc9$\xe0&~\xbf\xe9\xfdD\x9c\xbdEO\x1c\xfc$\xa1\xd1nu\x1dD}\xb0\xf7TCZ\x071\x0f\x91_\xfcMC\xe6\x1b(\x8dkw\xfa\x14\xf9\x11&@\x9e\xf2s\xeay\"e\xeaj\x11M\x98\x9f\xb0[\xf8\xc0\xaeO?\xaf\xfc$\x04\xef\x9aW\xf7\xbc\x80\xd2\x1b({\xa2\xdf:\x1e.\xa2\xbc@ss\x11Yr\xb1\xc0\x1fy\x19\x16\xd6@+R\xb4\x10\x98\xf6\xd8|\x1d[M\n\xa5\x8b0{U\x0cl\xd0q\xf5\xea\x80l\xd3\xb1\x94k\xae\x8b}JXU\x9a\x16cm\xaa\xa9\xd6\xc1B\x8f:n\x1aB\xd9=oG\xe3\xc8\xbf\xc5$\xe9A\x97\x9d\x90F\x1cs\xb0a\xdb\xe5\x92}\x11\xdd\xa5\xeb\xa2\xdb={)\x88\xfc\x03\xdc\xafS8\xfeP\x1c2}\xbf\xbe\xdb\xef\xbb\xef\xd7\x9fv\x16\xe5\xffW\xe0\xab\xff\xbe\xdb\xca\xc6\x99P\xaahvM\xa3\xa8HaM\xfc\xd0X\xb3& \xb4\xb0\xab\xe6\x98\xa4\xd3\xb8\n\x96hm\xaen\xe7\xa3J/\x90\x86\x90\xf7>\xbe\x7fu|q:~s\xfc\xa7\xb3\x8f\x17-\x8a\x82\xfaQ+\x88\x00\x9e\xa0R\xb9\xa7S\xc2\xc6\xde~|\xfd\xe6\xe2\xb4M\x91\\\xefM\x08\xde\x9b\xf5v\xfe\xd3\xd9\xcf-\x9dX\n\xca^>Oo\x13\x9b\x0e\xa9\xa3b]j\xed\xabO\x8ay\x9c\\\xbb\x1c\xe0\x94\x16\x1f\xdb\x95\x87T\xd5\xc8\xdf\xf8\xd8;\x1ev\x1c\x0e\x19\xe1\xd8\xd8\n\x07 \xf5\xb7g\xafN7\x06\x07\xce\x8d\x06GUi\x99N\x99c\xfa\x18\xea\xdc\x1fy\xbcJ\xee]\xaa\xfb\xab\x84\x0f5\x13\xb1C\xd0\xc6\xd9\xabO#\xfd\xad\x1c\xa5|\xd9\xce\xd7\xcbe\x94\xdd\xe1\x94o\xe7\x91\xc8\x0f\xc4\x7f\xc4\xf99_U\x11\x86}\x9de,)~D<\xd5\xdf\xb8\x98-u\xec<\xdd\xfbUO\x1d\x82\x95\x13de`Z\x97\xe5\x92\xda\xe8T\xa5\x9aS\x07\xf6\xe8Z#\x13\xda\xf2\x86\x04\xb4\xba\xb6&\xc9\x80S\xdd\xb50\xd6\xa5 {\xb4\xd6\x8brw'i\xb6\x8c\x16\xf1_\x19\xba{\x05\xd2\xfe\x1d\xfb\xd6wp\xae\xef\xe0\x00\xcb\xeb\xaf\xf9w 9\xcc\x1a\x0eu\xda\x8d\xa5\xdd\xab.\xa0\xd7SX\xe9\xa6\xb1pT\xff\xe9\x8e\x9e\xd3>kj\xef\x1a\xea\xe5\"0\xa6jo\x1bA\x94\xbaK\x06\xb6\xfc\xdb\x81\x1d\xdfBf\xc3c\xd3\xb8Hk\x18\xd2\x89\x94T\xf2\xcf\xdeAG\xd7/N\xa5\x8c\xa1\xd0jt9\xc0\x14\xf3\xe6d~\x12\x8c\xfa\x97!$\xa3\xc1%zc\xfa&EoTm\xab\xbb!\xd6\x13\xcd\xda\xc2\xa90\x14\xd7\x90#\x16\xfec\xd2\xc8Y\xa4\x0e\xac\xf7\xf8]\xfd\xaf\xce\xb0zb\xd2\x0c\xa9\x96x\x16\xf8^\\\xb0,\xc2\xa5\xb0\xc9\x9b\xe1K\xd9\x06o\xc7\x8a\x9b\xa1\xf4\xfd\xac\x87\x0dk\xc9\xc71{\xdaa\x8d\x9f\xddp\x8a\x8dsI\x8d\xb0\"\xf6\xfa\xab\xe5\x1a=\xb9\x1ce\x97f\xfe\xbdX.b\x93\xa4\x06\xaa\x1f#*Q(\xa1\xc8)NM^\xa5\x1a\x108\xb1[oA\x83 \xedx\xd3\xd9r_\xc4AB?\xe6*\x84\x93\x19oE\x913\xf3=\xbdi4\xc0\xd1R!?\xccb\x02\xa6X\x86Y\x97\xda\xa0\nMr\xb0z\xa6i\xc2\x86b\xdc\x9d\x83^\x878\xb0\x0d\xba\x8f\xa86\x98\x1f;\x08\x03\xeb\xe0\x1e\xd5\x05\xcb\x7f\x05\xfe\xe9\x97VE\xe4xk\xea^\xbe\xdb,Z\x1d+\xfdBC\xee\xe8\x7fH\x85\xc5\xde\xaf\xcb:.Paa\x99\x94\xaf\xcb\xa2\x81Y\x94\xcb\xa2\xbd\xfd\x03Z\x97AD_\xfd\xa7.\xe3\x97\xde\x97$:\xadHw\x81X\x95\xec\x99%\x91,yj\x954i),!c!\x9b\xd9\xb3\xba\x9eH\xb5\xc6\xc0x?\x93\xefwI\x84j\x08S\xfaK\xd8\xb9\xd4\xf4,\x99\xa6g\xd1\xac\x0f\xb3\x10fJ\x06?\x7f\x7fz\xd2M\xefQ\xe6G\xd0\xa2\")\x81\x1b\xa3\xe9\xa2Z\x04-Ru\xa5\x08\xe8\xa3V\n\x01\xc7`>~x\xd3m,\xb2\xb3u\xb6\xd0\xfb\"\xc4\xf6\x86\xce\xfep~\xf6n\xa3\xde\xfe\x92\xa7\xa6\xb4u\x96MY\xc6\xa6\x9a\xee%\xe8\xdc\xff\x87\xd3\xf3\xb37\x7f<}\xb5\xc1\x18P\xf8\xc9X\x9e.n\xd8\xd4\xbb|\xf8\xb1\x8c\xcf?\xfep\xf1\xe1tc\xad\x0c\xad\x8fI\x84\x13\xbd]\x98J\x13\xdab\xde\xa2\xa4Qs=__\x15\x193e>]\xad\x14\x04\x0ehd\xdd\xa1\xf0\xfe\xf8\xc3\xf1\xdb\x87\x9a:\x9f\x9d{\xe6Y\xb4|\x17- \xd0\xc4U\x85\xd7\x84\xd6o]\x15\xdb\x85y\x13\xcc1\x9cg/\xce\xff\xe7\x92\x88 7!tB\xea\xbd\xf0T\xe6\xe7\xcf\xfc$\x9d\"\xd1\xda\x8a\x05g\x0dG\xb0\x16\xaa\x88$Z2\xa17\xeby\xb0\xad\xde\xc6\x89|\xc7?\xde\x11\x05\xaa\x1d\x1f\xf3\xf7\x97_\xc4\xf61\xca\xe9\xea\x02\x8e\xc0\xc3\x19\x8d?/\x17\x1e\x0c\xe5/Z\x7f\xa0i\xf7\x18\xe6\xf3F\xeb$7\xd6dA\x08#\x0f\xa1\xc9\n\x86Wv\x93\x10f\x97A\x08yg\xac9}\xfb\xfe\xe2O\x02w\xc6\xaf\xdf\x9d\xbc\xf9x\xfe\xba\x95\xb0l\x84EoY1O\x89\x1a\x0f\x83Kq2Y\xac\xa7\xect\xb9*\xee\xfe\xc8Ak\xf3-\xc2\x1cx+.y\x1ee\xc2v\x1be\x89\xef\xfd\x1ce \x06\x1el\x02\x08L\xd0\xe4\"I\x0b\xb8f \x17^\x19D\x80c\xfb\x1f\xec\xae\x87\x16d6\n\xe4\x18\x1d\xd7\x81#\x0f\xb3\xe8c\x04@\xce\xd9g/\x84\x9c\xaf\xfd\xba}\xed\xffx\xfc\xe6uE3\xce\x7f\xbd\xe5\x8e\xf3\xb3\xe3\xf3=z\xad5\x05YGH\x04\x84\xfa\x9f0\"\xe7\xb4\xe3\xd1\xe7\xe5\xe2Q\xdc+X^\xf8\xb1\xd8\xde\x1c\x0d\xd6K\x96\x8f\xc5\x96\xa4\xbe\xe4{x\xd2\xe3\x9ca\xc4\xa1\xf3s\x8c\xf3\x8bd\xcc\x10ArB\x18\xb1\x86!6\xdfcl4]c\xb7_R\xd3\xefx\xfb1S\xd6\x8f\x1a\xed\x10m\x95\x8e\x15\x94\x01\x95K\xecV\x18\"\x8e\xb0\x9bh\x11\xf3\xc9\xbd\xe7\xad\xa3\x91\xfb\"\x84\xb4\x835\x18\x87FAR\xe4\xa2\xa2\xc8!(\x0b\x85Ks\xfe\xa4\xd1\x93\x1d\x15\xa5}\x7f\x08\x93\xfco\xdc%\xdavx(\x1cH\xdaq`t\xd9\x15\x07\xbaX\x03\x81\xc5F\xd6\xacCj\xdd\x12\xb0\xdf\x18\xf0\xe7\xa7\x17\x9c\x9b{\x7f\xf6\xee\xfc\xc1\xb8\xb8\xcc\x8c\x07\x035\x1e\xce.\xc3k\x9d\xde\xd2A\xc8\xd6\x0ef\xc3_\xa3\x13\x1d\xc2\x07\x8e\xc0\xd0\xea\xdb\xa0\x15\xd6\xd2dP,\x8e\xfcC\xd1V/!\xcf\xc6\xd2\x90_T\x92? \x9e\xaa\x88\x8au\xce\x19\x16U\xb5zS_\x9bP\x96g,_\xa5I\x8eY\x02\xb2\xa07g\xd1\x94\xa19\xd2\xba\xfc\xfb\xcb\x17K?\xc0\x17c\x824\\\xe3}\xb1\x1d\x8e*i\x08\x91\x8b\xdd_;(\xe4B\xc1\xae\xf7\xc3\"\xbd\x12\xda\x97iTDzPm\xbb\x8e?A\x8a\xed\x1aD\x08^\xc1>\x17\x9cr\x88\xd6\xf8\x112\xe9\x88\x95\xff\xf1\xf1\xf4\xbc\xedJ\x7f\x03\xa4\xfc\xaf\xcd\x902\xd6\x90\xb2U\xec\xf8\xaf5\xcb\x0b9\xe9\xd8\x05\xf9.\xa2\x05\x9f\xf9\xdb\x8f\x17\xc7\x17\xa7\xaf\xfe\x91 \xb0\\\x17Q\xc1\xa6\x1f\x1e\x0e\x10\x929<{\x7f\xfa\xe1\xf8\xe2\xf5\xd9\xbb\xf1\xdb\xd3\x8bc~B||0:\xd5$r9\xa4\"\x01\x92O\xec\x8e\x96\xa6F\xad,\x85\x83[\xeaz\x1eYN\xa0\xe5J(V\x0e\xb5\x0e\xae\xcf\xf3 \x080{dY\xbd\xd2\x0el\xfcI\xab\x90\x8d\x9f\x1eUX\xe2\xaa\xb7\xe0\x87ll\x9f\xaci\xd0M\x1b$\x98\x87\x87>\xc5\x9a\xb0\xa3qOL\xd9\x82I&C'\x87Y\x08\xe9e;\xde\xab\xc9<\xe8\xd6\x7f\x98\xb9\x94{\xbb\xe3T8-;?\xf9\xe9\xf4\xed\x83\xadI>\x993\xeat\xfe&*\x96\xf2s,\xd6\x11\xd5\x13\xfdTT,\x13\xca\x87/_\xb0\x9e\xbc\xb6\x1dR\x1fxc \x83s\xf1\xe6\xb2\x9e\x97$(\x7fv\xbe\xbf\xdd\xa3c\x99=\xdb'4\xdd\xf2\xb67_\xb1I\xccr\xaf\x8b\x1d\x00\xb9\x16!\xb2d\x99\xcf\xd0_?/\xb2\xf5\xa4H3\x12zZ*\xa8HK\x0f\x7fx\x08~\x82mD\x01\xdf\xdb\x98\xdbh\x08\xa9n+\xd0\xe9*\xe1\xa6\x16\x87\x15\xe7\xb8\xff\x8cV\xd8\xef\x99 \x91\x86\x85\xfb\x94\xce>\xf1\x07V\x948\xa9\xb1\xa7\x14\xf6\x93\xde*K',78\xdbU\xc9\xfd\x94\x89\xf6k\xe5S,\xafg\xc0\xaf\xd7\x98c\x8d\xb7\x82\x9f<\x99GI\xc2\x0c\x85\xdb\x0d\xd6x\x15\xe7\xab\xa80\xc35/1\x1di\xed\xd55\x11\x80\xee\xae\xed*\xf7F\xa67\xd8\xb6\xc3_\x83\xd4\xea\\\x1bWJ>s\xe6\xbeW\x97Z\xd7V(R\xf5\x08\xba\x82\x15B(|B\x92\xa9\xbd1\xa6s\xd5h\\\xc1\x1fu\xe1%x\xcez[\xd5\x88V|\xe7O1\xc6\xc1\xaa\xb1\xc9*G\xba\x8c\xd6\xcaQ{\xf0\x9c2lJ\xaa\xe8\xaa\x95\x11S\xb2\xbd\xed\xb8g\xbb\x1emo/[o\xda\xd7\x8e$\x1a\xf2\x06\xe8\xc7j\xe0\xa1\x15\xae:\x84\xcc_\x06!,\xbf\xd3^5\xc7\x86\xd7VG\xff\xc8\x93[\x00\x87\x90\xf8\xcf\xf6\x02\x7f\x16\xe0\xb5l#\xec\xd0\x94\xe1\"\x9e|\xf2#\xff\x0e\xe3\x94\x0ct\xfe\x0f\x86p\x83\xc6`\xbd$\xbdmm\x0dk9\x1b\xc2\xd0\xc2\xb12\x19N\xd8-\xcc\x83\x1e'{\xbb\xfct\xe2\x7f\x0czi\"\x8578\x84\xab\x10\xbb\x8b\xfc\xb8\xb7J\xf3B\xeeB$5\x03d>&\xbdh:=\xbdaI\xf1&\xce\x0b\x96\xb0\x0c\\\x01\x0b\xb5\x06P\xdb=\xe9\xc5K\xde\xe39\x86S\xcdU\xd0c\xf7\xd4&\xfa\x18|tt\xe3\x07\xca\xef\xea\xa6\x87\xf6\x88t\xa7\xa1\xab\x10\xb6\xc4\xc8y_^\x9ad,\x9a\xde\xa1\x1d\xc2d\x1e%\xd7\xcc\x838\x81\x85\xef\x89 \xaf\x1e_>\xf7\x88\xf2^\xb4Z\xb1dz2\x8f\x17S_\xfb*\xe8\xd9-\xb7\xe1p\xde\xcb\xd82\xbda\xa21\x91 \xa7\xdc\xa7\x06\xce\xd6\x16\xb5a|\xac\xb8\x88\x97,]\x17\x1aF\x84\xd0\xaf\x1f\xb8\xfa\xd1g}?\x84\x95q\x06pZ=\x84i\xd5\x04\xfe\xf5\xedq2\x1bM\xebh:\xea\x08\xc2\xcd\x9f\x9b!\xb0v\xb2\xd9\x18\xc9\xb5\xb5kBQ\x02\xb2\xeb\xb6\x8e[\xa0\xb7)\xb3\xb3\xfb\x94dvv\xfb\x8f\xef\xc3\xe2`\xb2\x10\xa4\x95\xa9_\x88|\x1b:\x9b#\xed\xedJK\x08[\xf1\x82\x91\xa2{3;\xa5\x98\xf8\x82\xf3\xc2\xa8\x05\xe3b\x92\xb4\xa4\xe5\xec\xc32\xce7\x8cs[\x8fu\xffd\xef[\x02\xda\x17\xba\xe5\xc0!l\xb9\xcc\xb9w\xfb\xbf\xa4Q\x8e>\x1eY\xa7\x8b\xa5d+\xf3\"\x9c%\x1d\xa1\xc5]\xa8\x8f\x89\xe1\xd40j\x8aw2\x9a\x13\xd8\xe3\x81\xccOC\x88\\\xb5\xa112\x85zn\xa4\xb3}1J/\xfd\x88\xd0\x10\x98\x8f\xd0\x0e\xa2\x8a\xc2Y\xb7=\x8a\xb3ztF\x9e\x0c$\xa3\x1e\xdb\xe0K=x\xeb\xb7\xeeM\xd3\xa4\xda7%`\xd5N\xf0\xf3\x00c\xfav\xd0\x80\xab'\xf3=\xce\x15\xcb\xc8\x1b\x89\x88\xd7 \xd2'\\\xb6exq\x918\xc2^\nM\xc0\xb7R_\x84\xc9\x8e\xe5\xff\x98\x0d\x87\x8b\xdb\x9b\xa1Q5\xe9\xc1>}\xca>1\xe5j\xa9R\xd83St\xca\xfc\x15\xe6\xa1,\xc4\xf0\xa7\xfd.g2\xba\x1f\xe4\xd4\xc9\xbc\x15\xa1d\xa9TP\xf5\x8dX\nb\\\x84\xdf\x19\x84(\xb2\xa3\xa7|\x8aQ\xe2\x82@Jb\xa1\x90\xdaa\x07\x06!J\xe9\xecy\x99o\x12\xc5\xbe\xed\xed\x05\xbc\x80\xc9s\xd7\x81\xc2%\xa4\xb5_\x8c\x16\x97\x0e\x82\xcc\x05w\xc2y\x81O\x01{\x995I\xc7\\\xa6_\x8d\xa6\x0e\xe9XO\xaf\xcd\xbb\xe1\xc2C\xee\xdf\x840\x0da\xc5\x99{QA\x98r\xceQ\x80\xb9\xe1\x9c\xfc\x0d\x0c!\xe6c\xc6@\x17\xfc\xcd\xe8\x92\x9f\xceT\xf8!\xebM\xe6\xaf\xb0\x83y \x00\xc6\x87\xf7\x9d\xfb\x13\xb5>\xf7E\xc2\xbd\xfdN\xbc\x1bq\x14{\xe31\x9a\xb9\x8e\xc7b\xaf\xe0\x9e\xe0\x8c\x88\xfc\xc0\x86z{V\x9cZ\x12\x19\xa2\\Z\xa1\x12V1Zb\x1a\xc3\xbf\x01\x95\xd7\xa3\x82\x0b\xf7\x1b\x9a\xb5k\xf4\xc9\xe4\xc5\xd261\xab9\x10\x16C\x95\x9c0\xc4\x0d\xc1\xab\x9b\xe2\xb6\xc5\x8f\xc10\x94\\&E\xb3\x07B\x06p\x9b\xf7\x7f\xf5\x1d\x8b\x9dv\x81\xc7/lN\x1cBQ7\xa1\xc8Q\x17\xcd>\xb3\xc9\xba`\xf2N\x0b_ \xfb\x81?\xe4ir\xbeb\x13\xed\x95\xfc\xe9\nJ\x11\xfb\x89\xbfO\x862\xe7%\x83=\x87\xa3<\x91\xecX\xad\xc5/c\x0b\\\x9bL\xa3\x0cU\xa9\xec\xf3\x15\x9bH\x07\x05R\x1aj\xc4VfX\xf6TL{(L\xd1rv\x91rx\xcbz\x89^\xc55\xa1\x90Z\xa9_c655\xa1\xa9\x1b\x0c+\xc71\x14 #\xcc\xe5\x04\x11\xbc\x80\xe29D\xdb\xdb\x01\xc4\xa3\xe8\xb2\x96&$\"\x0e\x08\x13d1\x82*N\x14\x06\x7f\xa8_\xcf\x9dD\x939\xa3\\\x8c\x94\xd4\x11\x8f\xfa\x0e\x07\xa5\xdc\x0eP\xbf\x0e\xab;\xce\x80\xb2K\xe0\x8f_\x8f\xb9I\xe5\xacq\xf2\xe9F\x7f9\x1a{\x05\xbd\x7f\xc9\xd8\x8c\xa3<\xdeb\xf3\xedh\xcc\xd2W\xa3\n\x81]n\xc2\x80\x87\xd4F\x7fh\\!\xcd\xb8\x94\x0c\xda[\xa4\xd7\xb2k\xe1\xb6\xea\x9b\x1a\xdc\xfah-J\xb5\xc1h\xcb\xb0\x8c\xf7\x1f/\xc3`\xc7\xd2\xae\xd0\x8aRcP\x95\xbf?]\xef\xa2c\xb8\xd1c\xbd\x9d\xa4\xcbU\x9a`VJ\x0b\x04e\x94\xb6\xf3\"\xcd\x1c\xd6\x01Z\xa0b\xbb\x02\xde\xaa\xd5z\xb1\xeb\x08\xab\xa6\x8c%S\x96\xd9\xa5\xb9\x0c\x1c\xfe\x89\xbd\x8dV+6=I\x93\"\x8a\x13\xaa\xea\xa2\xdc\xbeK\xb6L\xe3\xbf\xb2\xc0\x8fDvr\x91>:F\x1e\xdcJ\xa2\xe5T\x0bfiZ\xbcN\xf8\xda8\x9d\xd9\xf4\x99\x0d\x810\x1c\xe7\x0f1\xf8\xa19\xd0\xdc\x1e\xe8\x02\xc7J7)\xa05\x84\xb5\xfdYd\xdd\x88\x80\xc5\xcb\xba=\xd5Z/\x9a6r\xf6\x02\x0d\xd9(\xc2\xd9\xe2\xf4\x05\xbf\xa8\xe3\x17Tk\xeft\xfe\x02d\xe58\xf3\xfe\x94bf\xd0=\xea7\xb2\xf1uTD\xfa'p\x04\xff$0\xb0\x81y\xbb\xe6\xcc\xdbcj\xbe\xd7$[\x17\xcb\x12\xda\xe5\x0cK\xac\xd6\xd6\xaa5\xca\x01\x11?1\x0b\x16\xb2\xc0\xead\"\x0b\xac>f\xb2\xe0\xc0,X\xe1\xd2\x99\x97\xe4S\xac\xbe2\xde\xcee#O\x9eXC\xbd\x11\xe2\xffc\xf3\xfa|)?y\xfa\xf8\x19\xcd\xe6^\xff\xbal._W+\x1d\xb4C\xe5k\x13\x81\x06\xa3l \x8eR\xa7\"Y=\x9a&\xb9\xad*\xd4\xaf\x18\xf2\x8aM\x12\x1a\xefL\xda\xe1L\xcc\x02?\xeb\x952\xb3\x8a\xe8\xbf\xae\x19\x9594\xe7n\x0d)\x90:\x04\xfd\xd1F:\xab\x19\x06%r\x98\x8b\xda\xdbQ\xfb\xdc?\xb1\xbb!xb\x1f{\xf4A\xa0?\x9224r\xec\xd4#\x07>-\xf5\xd7\"\xee\xc7\xa9Hl\xcf\xe9\x91a\xbf\xf67\xf4u\x0fdn\xf3U\x96\xaer\xf9\xf7$M\n\xf6\xb9h\x81#\xb4\xc2\xf2\xebe\x10\x12\xe1\xd8\xcbb\x7f\xd5+\x89\x9dK9\x8d\x98KC-\x95\x9c\xc2\x0d\x1fp\xc2&\x85\x16\xdb\xa4-\x80\xeb\x8dL\x8eo\x9a_\x7fE31\xe6S\xd1'\xd5\xa3PD?\xbe\x96\xd1\ns\xd0_\xa4\xfc\x04@\xdb\xe7v\xa9\xc1h\xb0}\x9d\xf1\xde\x9a\xba\xc7\xd4\x1f\xf7\x9a|\x0d\xfc\xa4\x8c\xf1D\x146d\xf6Ij7\xee\x0d\xd4d#J\xb2\x01\x15\xf9\xadP\x107t\x1f\x96rl@5\xeeC1Z\xa8\xc5M\xef}\x96\xde\xc4\x9c\x97\xef\xd0\x18 j\xa6Y+j\x82\xe0\xb16\xa3Qn\xf2t_:\xdf@\x97Zh\xd2W\xb1\x81`h$\x0ci\xb4\xf4j\x8c(]r\xc6)\xe7\x8c\x1b=\xa7by\xd9JS&\xd2\xba'\x1670\xc9(\xbd\x0c!\xc3\x7f\x19\x99\x88\xa6i6c\xbc\xacp\xb0\x9f\xc44\x85\xcdc\x830\xde,\xb1C\x9d0\xb8x\x1c\xf58(\x82\x9b|\xeb\xa4\xff>\x14C\xa4\xac\xc5\xda8\xb6\xf6\x93\xe2\x8a\x03'\x12Z~\x8c\xb2G\xa3^\x13=\xb5\xa9J\xb1)U\x11\x14e\xa2\x90\xfb\xe7x\xb1\xf8\xc0&,\xbeA\xa1%o 2&\x81id%\xf9\xa3M\xb8\xda\xbd\x9b\xd2\xd4\xafM\xa4\xa7#y\xdc\x944\xaa\xcb\x06\x0e\xd8e\x1d7\x14 \x8a\xa4\xd3\x96\xa6\xee\x8b8A\x18\xb9n\xdc\xf4\xa7@a#\x0e\xc1\xcb\xd2\xb4p\xdd\\\xa8\xa7\x9d\xa5\xdb\xd8\xec\xc1A\xfa\x1a\xc8\xde\xd7P\x97B\xc9\xedn\xc5c\x03\x8db\xa9\xaaY\x08\xde\xf1j\xe55\xcc}\xde\xabl/x\x7f\xbek\xe6q\x88\xb7\xa2\x81\xc5\xcc\xb4\x1aUTJ\xb3$Z\x12z\x8e\x16\x90{\xd3\xf8\xc6\x92\xe5\xd5\x93\x17w\x0b\xd6\x14\x14i\x15M\xa7\xe8B\xee\x0d\xd8\xb2\x01k'\xe9\"\xcd\x86\xe0\xfd\xff\xa2(r\xe4\xbd\xb3W0\x04\xef\xff\xf9\xdf\xff\xb7\xff\x03<\xf7\xf9\xea\xc5\x9e\x00\\\x08\xdeI\xe9\xa8.\xd7\x96/\x0c\xe6\xbf>\x84\x02\x8e\xc0\xe38\x0f%\xb5\xf0`\xc8\x17\xd1\x0b!g\x0c\x8a9+\xbd\xe3=+\xe4w}b\xb7\xad\xca(\xb5&\xdd\x18f\xb9B[>\xab\xd8o!oW\xdcx\x9c\x7f`\xd1\xa4h\x17.\x9a\x0dI\xf5\xa7\xf3\xd1\xa5\x9e\xf2\x08k\xa7:\xd0\xc2\xdf&N\xfe6i<\xad\x92{\xf0\xb7\xd0*\xd5\xd1'RB\x9eHI+\x9f\x0b\xdd\x89\xb9z6%\xea\xea\xa9\xae\x02:\x9cI\xea\xe9 \xe1&n\x1a\xdcI\xc2\xc5\x1bwz\xda\xd2\xbd\xa8Dl\x01\xa3\x06\x0d\xa8Y\xb5\xed\xde\x1dZM\xfdJ\x06\x95\x91\xb7\x83Yy;\x88\x96\xa9\xe2v0\x85\x17\xc0\x9eC\xba\xbd\x1d \xd7Y\xbb\x1dt1\xb0\xa0\xdf.\xe9h\x9b9 \xd7\xc9TP\xb6XOG\xc5\x87\xea\"\x92\xe36\x89G:d;VL=\xc27\xbb\xc0c\xc6\x8d\x1f\x8e\x99Q\xd4\xddPgW0\xb4\x94\xc6\xf6\x19\x9d\x86\x10\x9b@\x8ag\xe0\x97\xc6[U\xe2\xbf4\x90A+\x13v\x0b\x17w+v*\x12x\xbdcl\n\x11\x88\x0fB(R\x981\x0e\xfd\xa8:#z\xf0s\x94\xc3u|\xc3\x12\x880\xd5\x8d\xaf\x99\x04\xa5\xfcPY'BM>\xe5\xe7\x89q\xe1\x9aZA08\xd6 \xa3-3*\x84\\U\xce\x8b\xc5\xbc]\xe4(\xb0\x1b\xfe\xf3N\xb1\x9f>\xfa\x14\xe0\xcf[?\xc2\x1f\xb7\x82[\xf3\x99\x1f\xf4\x16\xe9\xb5\x0c\xeeR\x9d\x86\xb38\x99j\xc7\x1e\xe70$\xb3Q\x0e\xa0\xd3%\xa1\xdb|_Nx\x08\x89\xff\xe4\x89i\xc8W\xe9\x8c\xeb\x97\x03]\xba\xa4\xaf'\xdc\x03\x99G9^\xb3\x0bG\x89w\xe9\x94\xe5C\x18\xddX\x12\xc2:\x04\xe1V\xa4\x90\xd5w\x10T4\xdb\x16\xb1\x93\x1c'\x838\x94\xd7x\n$x\np\xc4Jz\xf2,\x80\xa1\x8a_\x87\xb1\x89\x9d:\xee\x05\xca\x11\x92\xfd\xec)\xa4\xc6hl[\xfd\xc6\x03\xd0\x81\x8e\x8dwR4,\x0b\xa1U\xd1\x1b4\xb8@\xd26[g\xd0\x84\x1b\xec7\xf1\\\xf5Q\xcbKC\x93\xceO\xd1b\x8cz[\xc4K\xa2\xc4SE;\x8bt\x12-<\xbb\x06[F\xf1\xc2~\xbdL\x93bn\xbfN\xd6\xcb+F\x8ck\x15\xe5\xf9m\x9aM\xed\x92\x8c\xef\x07\xfbu\xce\xa2lBtP0b0\x9c\xef'\xde\x923^gD\x03\xb7\x8c}\xaak`\xdb\x94tN.W\\N*v\xb6\xfe\xab\xce\xb5\x92\xac\xae\xce\xe5\x16p\x04[[\xd9Hp\xce\x98b\x8e\xcf4\xcaX$+T\xe3}p\xfc\x12\xa9\x03\xcf'\\\x8c|\xc3f\xc5\xd0\x0c\xe1U\xabq\x91\xae\xac\n\x19\x9be,\x9f\x8b\n\xb8m\xf3\xb6}\x98\xf5\xac~Q:\xf8\x1c\x9aE\x17)\xfaK\xf7\xeejm\xb4\xee\xc3\xec\xdb\xe1\xe4R\x83\xfa\x83\xc7\xa6u\xbatM\xb7B\xc1E]\xd4W\x9c\x82\xb7\x86\xd6f\xbdY\x9c\xe5\x05\xaa\xf4\xddZ\x1b\x94\x9f\x12\x112\x06\xd3ic}\xferO\x8aS\x1cC/\xeeV\xd5\x89s\x93\xc6S_\xbc\xc7\xa5\x83\xc3v\x0f\x15@`k\xeaX\x8bU\xd2V\xc5T\xfbvW\xf9r\xae\xba\x15\x82{\"a]918\xe2\xc4]\x04\xd3AMy}j\x15\xde\x04F0\xa6o\xa0\xdc\xdd(\x07}\x1f\xcbz\xb3t\xb2\xce\xcds\x86v^~\xf0\xdd\x1f%\xf1\x12c\xdb\xbf.d\x90\xfb\x93t\x9d\x104\xf6*\xcd\xa6,{\xbd\x8c\xae\xd9\xd9\xba@\x06\xbf\xa1\xca\xf9\"\x9e\x10$Y\xab\xf1s<\xa5\x8e\x95\xab\xf4\xf3\x8f\x0b\xf6\xd9Y\xf0\xfb,]\xaf\xc8\xd2\xb3l\x1a'\xd1\xc2Qa\x92.\xd6K\xd7\xdcDan\x17\xcc\xc8\xa1\xcc\xc48n\xe9\x92\xf7i\x1e\x17\xf1\x0d1{^z>\xcf\xe2\xe4\x13]\xf6\x8e]G\xee/1\\\xb1]t\x9d\xc5\xd3\x0f\xd4Xd\xc1iB\x1c\xc5\xb2\xec|\x15%\xee\xc2\"\xca\x08X\xf1\xd2\x13\x84WS\x99\xb3WQ\xec\xeeX\x96\xd3}\xcf\xd2\xa4\xf8\x99\xc5\xd7s\xa2l\x11'\xecd\x11-\x89\xb5\xe7E?9>KW\xd1$.\xee\x88\x02\x1a\xdci\xb6\x9aG\x14\xaa\x14\xd1\xd5y\xfcWb\xedn\xe3izK|\xf0\xd7\xd7\xc9\x94\xc2\xae\xbf\xa6\xe9\x92\x98z\xbcX\x9c\xb9\xc6:[\xa4\xe9\xd4Y\xca\xb9\xd9\x86\xc2,\xfd\xc4^E\xf9<\xca\xb2\xa8\xb1B:\x9b\x91\xdb^\xd4x\x1b\x17,[\xc4\xcb\xd8Y\xa3e\x0c%A(\xcb\xbe\xda\x17p#\xefgv\xf5).\xbc\x10\xbce\xce\xff}\x9b\xfe\x95\xffw\xe6i\x9a\x1e\xa9\x89\xf9\xc4\xeer?\xeb\xe2\xee\x9d\xdauh\xa7\xe3Q\xeba\x0e\x9a:\x11\x13WL\xe6Qv\\\xf8\xfd\xa0W\xa4\x1f\xb90+5\x99\xbc,__ \xc3\x0b\x7f@\xd9\xa4\xa3!\xe8%gf\xf4\xd0\x97X\xa6\xa98\x8d{\xca\xd8\xa2\xf1q\xfe1\x89\x8b\x05\xcb\xf3w\x92i7\xdcs\xf3y\x9a\x15\xf3(\x99*\xad\xd5\xe9\xe7U\x94\xe4\"'\xa3=\xc5\xabh\xf2\xe9:K\xd7|\x8f\xd3\x00\xa8j\x1c\x17E4\x99/\x19Ev\xed\xda'\xb4\xaccW\xc4#\xa4KEA\x8d\xd3\xe4\x7fnR\xf9O]*\x7f`+\x16\x15C*\x8d)\xa1:\xb1;i\x87\xdd\xfd\xc7\xdeiD\x92\xc29F\x81\xa5\x8eC\xba^\xe9\\\x98\xc76W*W\xb6\xfb\xd0~H\x8b\x82\x93\xc2\xa6\x01\x8a:\x9d\x86)\xaav\x1a\xac\xa8z\x8f!\x0b\xf1\xa9i\xc0\xbcF\xa7\xe1\xf2\x8a\x9d\x06\xcb+\xdec\xa8\x1f\xc4y\xd84V\xac\xd2i\xb0X\xb3\xd3h\xb1\xe6=\x86\x8bbg\xd3`/\xd2U\xa7\xa1^\xa4\xabN\x03\xbdHW\x1b\x0d\x93\xf3&\xae\x11\xf2\xb2\x96Ny\x95?FY\x1c5\x11\xca&\xfeG\xafC3\"\xeaib\x87\xd4\xc3[\xf91Z\xc6\x8b\xbb\xae\xf3O\xd7\x05o\xd8\x05\x02Y\xdc\xb2D\xb2V\x0b\xacd\xad\x86\xe5\xf9\x8e\xfe\xe5P\x15\xc4\xf8\xf6\x9b\x84\xaa\xc4\x7fj\x06\xe3K\x85a\xd0`\x1f\xe3\x02\xee\x89\xf0\x80O\xfb\x96\x83\xbc4 \xc2rv\x0b\x1f\xd8\xf5\xe9\xe7\x95\xef\xfd\xe7\xc8\x83m\xc8z\xc7\x17\x17\x1f^\xff\xf0\xf1\xe2t\xfc\xee\xf8\xed\xe9\xf8\xfc\xe2\xf8\xc3\xc5\xf8\xe4\xa7\xe3\x0f\xb0\x0d\xde%]\xa9,\xfe\xdd\xbfXi\xcd\"\"\x1e\xfbZ\x06\x80(_\x96w\xa5\xb9\xf3\xaetkkmG`\xc7\x00\x81\x11\xf1\x9e\xcb\xfd2\xfb\x1a\x1a\xb4\xf9\xeb\x11\xbb\xc4\xb0\xaf\xa8\xdd\x85!\xf8\x91\xf6\xa6\x16H\x9bNs\xdc\xc5\x9e\x10\xf3\x84\xcc\xa3\xfc\x874]\xb0(\x11:\x80\xef\xbf\x87\xad\xaa\xe8\xddz\xc9\xb2xR\x16\xc5\xf9\xbb\xe8\x1dg\xfeT\x05%\xce\x99\x15\x0bx\x01\x83\xb2\xd6\xd9\x0d\xcb\x16i4eS\xab\xaf\x01\xa9\xc0\x03\x89<\x13[\x1f\x87V\xcbo\xa3\xec\xd3z\xf5c\x9a\xbd~\xd5\xaaJ\x13\xd3\xcez\xaf_\x8d\xeb\x88\xc0q\xe0\x90cHj\x85\xb4\xae#@\xce\x8a\xe3\xa2\xc8\xe2\xabu\xc1\xac>\x1d\x8c.f\x9b(\xbf\xf2\x89\xee\x89\xe0\xefM3\xfd\x90\xa6m\xd7\x95\xe5T?\x9c\x9d]\xd8\x93\xfd\xb7C\xcf\xfb\xb7\x0d\xe6i\xf4HB\xd7\x9a&\xd1uXK\xdcK\xf4k\xccT\xed\x8c\x0ePV\xea?\xbc\xfc\xe6\x1f\xc5,'\xf6\xd7Q\xad\xc2\x08U\xc8\xb4Q\x15j ]\x82\x0bF\x8b\x14.\x1f\xa5~\xd0\xf3huc\xe9\x07\xd6\x8b\x14tl\xb3\x0e\xf5\x94\xf6\xff\xe6n\xfc\xf2E\xbcl\xd8@\xfdRE\x1e\xab5\x86!\xfe\xad\x90\xbb\x93\xbe\xb2\xc4\x9d8?Y\xe7E\xba\xac\x16\x15\x01X\x91\x0d\xbc\xc1\x1a\xa2\xf8V\xf5 \x01\xba\xc1*\x1b\xbdtXl9\xc4\\RL\x15{\xa7\xc00#\xc6`<\xaf\x05\xd1\x11\x80ndk\x880\x92\xb6\xe0[a\xe1[\xd1\x8co\xa4\x1f!h8\x94\xf60cW\x9c&T\xbeD\xf5\xf0\xa6\xe2@hw]\x06~l\x913GgP\"x\x8a\xee\xbd\xba\x02\\\x98}\x89\xabb\x13pb\xb9\xe8\xeeT\x9b|\x02y\xf11/\xed>\xd0$Q\x81\xe8\x8eo\x8cK:@\xabzZ\x06\x0e\x9a\xbdQZ\xdfq4\x93\xa4?k\xfb\xa3|\x15M\x1c{\xb5\xfa\xea\xc8\xa0~\xef\xce\xfd\xb5\xc8\xa2\x877\xbc\xe8.O\xed\xe8\xb4\xd3\x8eN\xac\xf6}l:P\xa9\x8c\x8c\xf7\xd8\xa5s\xc4\x8e+|\x9b0\x08Hc\xd0}\x82\x14\x14\x06^Lz\xdaV\xd2(\x86\xdcA\x1d\xf7\xa0\x8b\x0886a.\xf3\x00\xf8\x8a& P\x89\x84\x15\xfaXmH\x15%\xa4\x1a\xc7V\xc7\xf4Mh\x145\x8c\xee==\xf0\xc9\xb71%r\x9e|\xa5\x85\x7fgJ\x94\x06\x9c\xad\nU\xf0\xe3\x06r\x84\x1d\xdb\x04\xc2\xbd\xd9\xab\xa3U' \xee\xddj\x1f\xabG\xc0F1\xb2\xd3\x03\x0c\xfb\x8b\x7f{\x0e\x9fc1J{a\x8d\x93\x9d8d\xc5\x97\xf4>\x12\x17\xe2m\xc8R\xfer\xc8f\"9\xe77\xcaf\x03*lq\xe2\xef\x0e\x1c\x11\xc6\xcdp\xeb2\xcf\x97\xd9\xca\xba\x92\xdc\xb6\x06\xa4\x91lnq\xb1x\xd7\x8bV\xccY\x9a\xa25\xcd\xebW\x95\x0dv\xcd\xdci\xc5\x92i\x9c\\\x7fD\xa3\"\n]\xda\xbe\xc1\xe5\xb7\xb1\xc6\xf0.\x10w\xed\xf2\xcaU\x06C \xf1\x04\xc3\x9aW\xf6B\x94\xfdL\xc5\xb1|\xff=(\x03>\x89\x98>\xeb-\xd7\x8b\"^-\xa8\xb4P\x15\x1e8\xc5=\x82X\xde\x94\xd9\xd8\"\xcc\x81B\x1b(\xf5\xd2UaGEu\xde\xba\xa3\xbbA&\xc4d\xdd\xe5 \xa9\xbb\x1cd#AhG\xe9\xe5\xff\xcb\xde\xbbv\xc7\x8d\x1b\x0d\xc2\xdf\xf3+J\xcc\xacCF4\xad\x8b\xc7c\xb7G\xd1\xeb\xb1\xe5\x8d\xb3\xe3\xcbZ\x9e\xe4\xeci+Z\xaa\x1b\xdd\xcd\x11\x9bdH\xb6de\xac\xe7\xb7\xbf\x07\x85\x0bA\x12 \xc0\xb6<\x93d\x1f|\xb0\xd5$\x88K\xa1P\xa8*\xd4\xe5\xac\x93\xc0\xa4\xd5\x92\xd2B\xdcn\xc1L\x89X\xd0\xcd\x0e\xb1\x8b\xa7\xf9\x197\xa4\xd2\x93\x02\xacPaLU2\xc7[\xf1\x0d\x9e\"\xed\xe7Gj\x82xQ:\x1a\x13\x137\"A\xc3\xa6\xde\x02O{r\xda\x01R\x907\xb3@&\xa0l\xdb!t\x87\xba\xa3#\xac\xb1\xe2k\xe2\xc7\xd3\xbd\xee\x17F\xcc\x12\x7f\xe9\x05\xef%\xa9\xff\x9cW5\x06Mq8\x9f\x84<\xc1b\x19\x99\xecA\xf3\x8c\xd9\x01Nz\xd6\x8c\xe2\x8d~\xb3q_xv\xb8\xf4\x97k\xf0\xc8]\xe7\x9b\xac\xfe\x1b\xeb\xcba\"\xe2\xa0U\xf6\xb6\x8e\xdd\xed\x8c\xbf\x07>QZ$\xc8\x9c1*\xc9\x92:\x89Sn\xb9*\x08\x07et2\x984!?\xf1\xbdI\x8f\xc9\x12\x8eU\xecs\x83\xaeP\xc2\x7fX\xcc\x17EXw\x8d%\x8e\xa20@\xf2\x10\xceoy\xe7\xec\"\xcf|~\xeb\x0e\x04\xdf\x85\xba\x9b\xd8\x0eP\xcd\xb9\xe3*.|\x1ec\xcb\x18\xd5\xe0\x96\x85\xaa5\xd9\xf9_\xc7\xd5kN\xbc'\x92\xa0\xd7\x0dA\xefch\xa8\xa6\x8d\xa8\xf9\x8eW\x13r\x1eu\x16\x99\xbe\xdc\xa0\xc9\xcfF\xb7\x8d\xc3\xee^e\xc1\xa3\xf1\xd3\xe7\xcc!\xc8\xb6\xc6\x06/\x0f\x15\x13\x87\xfa,\xf2\xaaf\xa0\xd7\xec-\xd3\xc6bVmZD\xb2n\xb1\xd6\xc8\x0cY\xe7\xa1e\"\xd6\xfe\\Y4{_Je8\xd2-\xb1\xbe\xdf\xd2N8\xc4\xde.\x99\x7f\xb6\x8da \xd9q\xaf\x19A\x08%Ztex\xb6i*42\xd3N\x0f\xbb\x8e\x07\x9amW\xa5]\x0c\xd5\x15?D>\x13\xaf\x17)G\xfe\xfa\xaaLm7\xb0m\xae\xe7u\x19O\xfbx\xbf\x1b\x91\x80g\xcdy\xd45q\xdc\xf0\xe7\xdd\xfb\x8c\x8a;:\xd3\x0e\x809<3\xdewx\x13 \x19\x93N<==\xb4\x96m\xd6\xab\xf7\x11\xcd\xfb<\x1c\x97\x91\x8fxz\xa2}\x91/\x8f\xee\x88\x98\xc7\x00\xf1\xd3\x0e^J\xb9\xccc\xd9\x92Zi\x8e\x86\xf4b\x86\xb3\x88)\xb1h\x03z\xb9S\xeb:\x84A\xfc4\xa1:z!=D\x11|\x8bI%\xbb\x17\xc2\x0cv]\xbc@Ax\xf9\x0eU\x80\x16\x0d\xa3\xbcu\xbc\xd6\xe6nP\x0bg\xab\x85\xf2\x18\x9e\xaf\xc8\xec\x12\x03K\xf1\xc05,\xf55\xe4\x0b\xf8\xbf\xe8\xa3\x05\xbb\xe0\xfd\xdfH/\x9a\x82Q\xb1\x03\x8a!\xb5A\xac\xf5\xf3\xe8<\xbf\xceHI \x87\xef\xed\x1f\xeeyMX\x89\x04\xd5\xc9\x13 \xf2\x10f6\xae\x98\x16MV,\xb6\xec\xc8\xb7\x1c\xc1\x86#\xdc\xab\xac&e\x16\xa72|\x8b\x8f\xc1%]<`\xc4\xac\x1a\x8cQ3p\xdd\xbb'NPf\xf5\xda\n\x95\xa5\xffF\x8dfK9\xc3&\xa4\x8c\xcb'%\x0b%(?\xea\x03\xc9g\x10\x088\x082r\x0d\x15\x9b\xae/~\xb3\x1a~\x1e\x04\x11\xe7\xb2)\xa3\x83\x87}\xd6zr\x04\x19C4\xbcr\xcb\xe7]r\xc16\xae)7\x99\xc7\x9c\x12\xba9\x89\xdb\x0b\xc3\x9d+s\x0c\x1c\xe1#\xb5G\xec\xd8\xf7\xc2\x86\x02\xb4q\\\xde^\x9c#\x00\xd1p\x8fy\x8f\xcbGk\x96\xc1\x97\xb9)w\xf3+\xd1\x92\xfb\x95\xea\xbf\x98t\x05\x86s\x16\xc9\xa1N0g\x8a\x1a\xe4l\x02\xcd\xadC7\x81,{\xf3uN\x92\xef\xbay\xd6\x94P\x17}\xd4\xfd\xf3\xdb\xd3\x0f=\xc7\x00Z\x9e\xbf}\xfd\xee\xed\xe9\xab\x0f'\x13\xd0\x88\x02'\xaf\xdf}\xf8?\x138\xe8\xbfY\x92\xfa\xc3M\xe1\xc4\xb8\xb7/~;'\x01\xdd\xe8\x11v\x83\xea\xea\xa4\xfak\x9c&s\x11\x15\n\xd1\xd6\xb0 \xf8\xbeN\"9\x05\x98@\x12\xd1\x99\x8a\xa4g\xa5\xef\x1d<\xd2'o\xec\x88\xd4\x067\xf1/\xb5=`\"x\x1f, f\xc68Y\x17\xf5\x8dD\xa4\x97\xf1\xac\xce\xcb\x1b'\x88R\x92o\x9bR\x1f;\xfa\x8d\xb1]\xe7\xd4\xa5\x90\xa7\xed\xb0l`\x90Dl\xa2\x94k8\x82<\xbcS\xd8\x9a7\x07\xdf\x05,Ve\x0f\nm\xf5\xf3\x95\xd6K\xdcpL\xd8\x00\xc5\x81\x94S\x04\xa7Tk\x9fR-\x86\xa9\xdc~\xc4v\xd5\xaf%\x83\x8e\xddb\x82ZK\xfbI\xf5\x01\xdd;\xc6M\xa8\x15\xc8&\x19l_\xac\xb7\xce\xd2\x88\xbd\xfc\x9f$#e2\x93cx\x9e\xc6\x95\xd5! \xf8\xd2j\xb0\xbeO\x9bX?\xad\x89:w\x92\xb8l-\xf9\xeb\xeby\x19\x9aQ\xfb\xe1#\xc6\xe1\xef\xf7rj\x08YB\x97\x81S\xec \xff\xa0\x9fiD\xd1\x94{\x91\xa7\x11,\xbc\x89\xe7.\x08H\x9c\xa1\xfc\x8b\x86\x7fW\xef\xceItIn\xe0\x18\xe2\x88T\xb3\xb8 >>\x08P\xc5T\xe7,G\xaa\x7f\xf8H57\x12\x7f\x8d\x89\xd9\xd51=\xa2\xc7\xc6\x9e\x92+\x9e\xa7\xa9\na\x16\xea\x13q\xd2E)BLr\xc2gQ\x1b\x04 %\xd2\x1e\xe5\x00\xd1\xb7\xcb\xbb`\x92\xaaxD\xf9\xaa\x9a\x13\xa2&\x94\x9a\x88\x94\xd10O\xbc\xae\xc26\x89'\x0dTy\x17u\xf4\xcd7|d\x18\xf4Or\xf83\x7f\x81 \xf1\x85p\xa2\x07\x8b\xc6\x0e\xa3\xf7\x84\x13\x94U\xeb\x05\x86\xda\xf0\xbc\xae\xb9\xc5\x97\xfaA\xb2\xd0\xa9h\xcb\xb2 \xa1\xc2tn3v(\xeeuo\x7f\x17\xec\xf6\xf7Q'\xe0%S\x7f\xe9N\xad\xc2\xec4\xfe\x92\xd7Q\x04lq\n\xf5\x177k\x02\xe4\x98\xf2\xa9\xf5?\xa2G\xbb\xb4!\xf6\x98\x07\x12\x06\x89\x0c\xa2\x92\x14i<#\xfe\x83\xe9\xc7\x8f\x7f\xff&\xfa\xe3\xee\xb1\x1fL?\x9e\xfdr\xfb\xf9\xec\xc12\x04\xef\xe3\xc7o\xeeyJ\xb5vW\x9f\xa5oT\x10\xfd\xf1\xd8?>\xfa\xf8\xf1\xa3\x1f|\xc6m\x1b\xed\xf2\x07g\x01\xb6\xf4\xcd~\xf4\xc7c\x86\x18\xdft\x03\xc2\xeb\xbd`\x85~\x8d\x8fV\xa7n\x96\x06|hF\xdc\x0d\x10?\x184X\xd8,\xef\xb7\xbf\xf9]\xff\xaf\x8e\xb2\xae\xe1*\xd8\x11\xb3(\xf3\xb5Qm\xf2:\xc6T\xde\x85\xff:.Z\x06|\xaf\xe3\xc2AQ\xd3\xaa\x85\xdbL\xb6\xd6y\x1e\x18\xdb8%5\xfb\xe8\x94\xd4\xad!\x9c\x92\xdaa\x08\xadZ\xca\x10\xfa\xcf{q\xa4\xaex\x92r*#\xbc\x8e\x8b>b\xae\xf8\xcbS\xd2am\x9c\x12\x9a\xcd\xa3\x8a\xd4\xecm{\x0d\xc3v\x0e\xea\xa1\xe5\x9fGK\xd2\xd7@\xb3D\xb8\xc3\x0d\xcc\xb9i\xa0\xe6\xe3\xd8\x16T\x8ew\xde\xe0\x8f?g4\xb4g\xa1\x85l\xf2\xf0@VQ<\x9fkF1\xecx\x0e<\x07\x83a\n\xd6\x98\x94\xfd)\xac\xf4Sh6\x94\x8e)\xba\xe2\x99\xe6\xbb\xee\x07\xc0\xb3\xf2\xe9\x9e/\xad\x13\x03Eg\x1a\xe9C\x1ai\xda\xbd\x19\xd3.&~~\x95\xd5>\xe1\x1e\x9b\xfe>ej\xf74\x8a\x8a-P[\\\xdf-\xb5T\xef\x8ae\xc8\xac\xc7c\xbd8s\xf4\xed\n\xab\x8bi}6~?\x0c7\xcd#.\xe9\x9av\xdd-*\xafq\x15D\xeb\xb8\xf0o\xb6\xd8.\xc3\xe3\\\xb3l\xf8\xddD\xf9.\xbb\xc9 \x00k\x0d\x00\\\xf7\x9a\n\x80\xb5\x1e\x00\xbf\xeb\xffE\x87E\x05\x85\xe9\x99\x8e/97\xf3%yo\x1eF\xf3\xa8+\x99\xc2y\xb6J\xd2\xf9\xab\x17:\x99\x0c\xc3Oe\xd2\xab\xfa|\x8c\xb5\xd7\xb5E\xc8\xf6>f\xd8G\xc6B\xd13\xcd\xffO\xd9e\x96_g\xc8s\xf8h\xc2\x0f~\\\x03c\x80\x16I\xca\xa2\xf2H\xd6\xe6\xef\xd1\x1f\xa7\x1f?~|p\xf6\x80Y\x1c\xef\x827au\xd3$#\xccM\x9a>\x0c<\x14<\xb19\xa69\x9b\xc3\xc5\x0d6\x9b\xc9\xf7\xaa\xf3\x87nB'}\xb8k\xf4\x05\xde\xef\xc9\xba\xa8o\xb0\xc1q\xf7\x1b\xde\xefk\xf2\xa96}(\xd4\xd8\xfc\x8f \xff#\x9a'U\x91\xc6hY\xca\xdc\x98\xf0i\xc6\x7fJ\x80\x0e\xce\xec\x93\x01\xa3B\xc4\x90Sz\xde\xbeh\xba\xd1Z\x97\x94\xa2b\xa3\x91\xefW\xcaE\xa5\xb7\xd7\x19)_\xbd\xe8a\xab\xd4\x8b\xa2\xe5\x8c\xae\xef<\x08B\xb8\xc6\xfc\x91\x80\xb1\xc8\xcf\xab|S\xce\xda\x1cE{'\x9d\xf6\xb4\xb6yvJXH\x9d\x92dcL\xab\xf4\xd6\x92\x14\xd03\xdf\xdb\x7f\x88\xd1\x923\xb9\xa1\xe8\xee\xeaW\x97\x92z\xc9$\xf5\xb2\xa5\xbe(\x87-\nY\x8e\xb9\xd2\x90Z\x1f\xb8\x0e/\xf7\x13\x93m\xa1\x1ck+:\x7f\xdc\x8cY\xaf\x8c\x8b#\xc2\x83\xf9(\xcch\xeb!6\xbaO\x1b\x8d\xa3\xa4z\x9do2\xba\xc9Xo\xdf\xed\xb7;+\xe2\x92d57\x90R~\x1ea\x8cr\xe5\x01^\x8e\xca\xd6\x0f<&\xec\xc9\xf7.\x176\x1d\xd5h\xf6\x03Y\xe4%y\xdd\xbaAu3\xe7/}c\xb8H\x0e\x87 h2\xaf\x03FSc\x03\x9e@\xa6\xaf\xc0\xec\x9e\xcc\xf6oby&05\xac\xbd\x84\xb9\xd9V\x8f\xc55\xe4\xc1s\xc6Z#\n\xc8\xfd\xc4\x1b\xd1\x83n\x9b\xddC1JA\x194\xfe\x91\x98\xd5\x8bb\xd5\x1b\x96y)\x87N|\xfd`\xea\xf6V\xae\x95a1\x97Va\xf1\xa6b\xf0\xc6r\x95\x92g\x030\xdbf\x8c\xa8\xc7m\x01\xac\x8e\x94\xb5\xdd\xdd\xb5\x8c&[\xdf)\xc8X\xa4\xc7\x16\xa4\xf6\xf5\x90\xaa|\xa2K\xc7x!\x82\xf7\x0f\x8d\xbb\xd8\x94K\xc2\x87N\xe6r\xf0\x95\xc5\xd5\x14\xc3j\x9eF\xe7EI\xaeHV\xbf\xdb\x94\xcb$3*j[\xc9\x94\xf6\x9e\x02\x81\xef\xe1B\xd2fb\xa6\xcd\xb4\x9c\xfb\x17Sr\xe6\xaa8\x03\x9c\xf8@\xd0\xfa\xe1[\xdaf\xb7\x7f\xc9\xe2 \x85\xcaN\x17\xa9\x86\xfa^\x92\xfa9\x8f\xecW\xc7\xb3\xcbg\xf39\xc9\xe6\x9b\xb5\xebHtVO\x836L\x82~\x9c\x0c\x86\xaf.\x99\xe5$Z\n\xe9\xcf\xbe\x1av\x8f\x18\xeb@\x1a\xae\x81s\x11\xd2*\xcav\x9e\x80\xa2\xe4Z\x88\x08\x87\x06\x8aL\xc1N\x9b\xcf\xa3\xf39\xb9\xd8,_\xbd0\xae\x00\x8e\x0d\x99\x9d\x16L\x7f\xb8y\xf5B\xc4\x9c\x17EcB\xdb\xfd\xc4\xb6\x14\x12\xcd\xf9z\x00y\x1a\xb0!|B\x8e\x9f\x08\xce\xeb\x1d\xdf\xbcC\xc8\xd3\x15i{\xb8\"\x8f.7\xfc\x18\xc4T*\x124\x12\x0b\xa6\xf5\xb4t\xaf0\x8f\xae#\xe8\xf0\xb1\x83\x839q\xf3)n\x1at\x1d\x84\x03\x18\xc4\x19\xe9\xd4=g\xb9]\xbbw\x87\x01\x12\x0e\xb6\xefpT\xecO\x89\xf2n\xa3{'\x19$\xb7\xe19@G\x1e\xcfk$Gi\xff\x15Y&UMJ\xc2\xe8U\xdc\xe5@\xaa\xd5\x9b<;\xad\xe3l\x1e\x97\xf3\xbf\xc5e\x96dK$\xbe\x0e\\\xb0\xf1FB\xa4>,I(\xf2\xc2N\xaat\xd8\xecH\xa2N2\x94;\xb5/\xc6\x86\xda?\xc5\xa7\xdb\x1b\x010G\x97\xeeu\xbf\xde\x9e\x969\x1b\xba\xe9{\xa09gH\x14\xcf\xe7'T\x80\xfc\x91{+2'\xa8\xeeSn\x1e\xb6\xb3\xaf\xb5\xadn\x1a]\xe7Wc\xd2\x8a\x08\xff{C_c1\x90\xc5\x9b\x881\xa4'6\xc9'\xd3<\xf0=\x8a\x00\xbb\x0c4w<\x959\xd1w\xb3\xcd,L~\xb5\xfd\xed?\x8b\x8bzS:\x06\xee\x80\xedW~\xef\xae\xc15\xb0\xf2\x9a\x8bKQ\x06`f\x1f]\xa9\xff\xd8\x05\xcc%\xe7\xa0^\x88$\xba\xeaL\x8d\xe6\xdf\xad\x84kwA\x0d\x1e\x1f\xe8\xc2\xf8\xd1\xe7\xfaP\x11\x87\x8f\xba\x99\x00\xb8[\xddw\x07A\xbb\xfd\x8d.M/\xf3aM\xf2\xecy\\\xc4\x17I\x9a\xd4\x89=u\xc2\xd5\x97&\xa0\x80\x8e\x14\xe6\xb7SQ\xdc\xbb\xc7\xb2Ox<\x8d\x00^\x1b}\xfe\xdcKI\xc1\x9e\x95\x1b\"*\xceXL\xff\x93yR\xc7\x17]\xa7`\x93\x03o\x92g\xaf\xb2E^\xb2(\xf4\x16\x0c\x17\x1a\xb6x`Jz4\xc5\x18\xfb\x04\xdd>\x8c)\xbe+1\xa0\xf7\xccc\x1c\x03\x1cj\x97\xc8G\xb7\x91M\xa4\xce\xc2'Zy\x1el'nI\xaa:/\x89l\xc7i\xf9\xd9\x05[lJ\xda\xc3tZ\xca\x9c\x0d\x13\xc6j\xedi\xeb\x14\xed;G\x9c\xe9\xc7\xab\xb52\x84\xdc7\xe5l`\xa1\xe30!\x90\x19z%\xd6\xd8D\x95\n\xbe2\x84*\x08!\xf1\xcb\xe1\xd0E*\xcc\x9d`\xa5\xd7\x1azr\xda\x18l\x1e\x13Q\x90\x007\x96\x1e\x83*\x16\x93^\x81\x17~\xa8\x87,\xc9\xe6\xad\xaa'\xd9\xbc\x8f\x15\xfd\x81I\xebP ^\xd9B\x7f\xb3\xab\xbb\xd6\xb4\xf1m\x12a\xbf\x1f\xee'\x87\xb8`\xf2\xf5\xcc\xb8\x8eD\x08*\x01\xf7\xb4\x12\x18b>)8\x10\xefg\x11=1\x10\x80\xbe7[\xc5e<\xabI\xe9\x85p\x9f\xa7\xf9\xe2\n\xee\x01\xb1\x04A\xcc\x1b\xa2\xcc\xe3`3\xdaV4Y\xfa\xb9\xddR-\xd2]\xbd\xc5\x98\xf7\xd5\xb0*\xe1\xf3\xe7a\x941\x98\xb8\xe3\x04F\xaa\xef+\x03\xf2[n\xd0\xea\xa82\xe3*3\xbb$\x99&\xd6\x15E\xc5V\xaa\x7f\x91\xb6\x9b2w\x86\x1d\xd4\xdd \xb4v\xd8\xd9\x0bp\x04\xaf\xe3z\x15\xad\x93\xccG\xa7\xad\xd6b\xfd\xc6\xfb\x02\x1dt\xf86\xf8@>\xd5\x83[!\x89fy\x9a\xc6EE|d\xe1\x12\x13bg\xf2e\x0fYs\xb8\xcf_\xb3Y\xe9\x12\xcf\x8aH[\x95\x82\x93CQ\x94\xf4<\x12\xcb/\xb8\x15\x8f\xe4\x96\xe2\xa6\x830>\x01\xee\x8d\xd9q\\\x11\x02\xa2XO8n\xfe\x14\xdcy\xd0\x84\xe2\xeb+B\xf5\xea\xa5\x86\xf7\x9e\xd5\xc9\x15Q\xf2\x08\x91\xe8\"\x9fwRH \x81z(\xbc\x8f\xee\xbb\xdf\xb5\xff\xda\n\x9cW6\xef\xdb\xc7z\x86\xb3\x17f:\xd6\xfb\xea\xb2(\x0e\xfb\xdfv\x1b\xafZ.^}\x0f\xaf\x94\xf5\xf2\xb0+\x15\xcf\xf8\xf3n?\xcc8\xfe\xf0\xdb\xee\xf3\x82\xcf\xad\x1bub\xce\xfa\x17\xe1\xb0\x1f>\xea\x0e`\xc5:z\xdcy|\x85\x8f\x0f\x0e\xba\xe3Z\x8364\xdb\x92u\xdf\xcb\xdfu\xc3\xb9\xf6n3\x17\xaa\x03\xdb\xfe\xc3'\xddQ\x9d\xf3\xee\xbb\xd3\xb9n\x1c\xdb\x92~\x00\xe4N\xe5\x13\x8cQ\xa6\x8b\x1f\xdc\xaa\xf6 \x8e\xba\x9e\xd2\xa7p\x04O\xda\x8f\x9e\xd3Z\x9dj\x97\xc68\xde\xcf\x8c&h\xcc4L&\xcf\xa2\xbb\xf6\x14\x1fu\x93qMZ)\xc8\xba\xac\xae\xce:\xec\xad\xb9Sz\xb6\xca\xa0\x80\x8c\x84\xabO\xfck\x96\x8ew\xd8\xfa\xec\x9d\xd8n!\xf2\xa4\xdd\xbe\x90\x96\xb7\xa9\x06%O\x8b\xa8\x9f5\xdbtv\xc6\xe6\xe8=\xec.\xd1\x14\xf2\x03\x8e\xc0C/~\x16\x8ck\xc2L\x155w$1\x1cC\x0c\x13\x88\xbb\xf6x1\x9a\xe2\x05\xa1T\x95\xd5\xc9\x9a\xf4\xaet{\x13\xa6\xfb~\xd5\x89\xf3@\xc1\x94\x85<6\x01w\xa9D\x07\x98n\xf8\xa8DU\xcd\xd1\xfe\xe8Q\x95`\xc8\x81s\x16\xbdC1\xa0\x88\xcek\x0eD\x1e\x0e\x89e\x87\xffQ\x8d\x88\xf0*\xabsLa\xbd\xc1\x85\"\xb8P\xd9\xb0\xb5\xe4\x07eUuKJ\xc9\xe3:B\xe0\xbe'\xb3<\x9b%)\xf9P\xc6Y\x153\xfeuI\xeawy\x9e\x92\xb9\xbf\x83\xcc\xc1,\xdaT\xe49\x9e\xe6|\x01;\xb3\xce\xa3\x82\x94T\x02\xf5\xdf \xb1\x11\xe4|\x10\xe1`\x7f%I \xe5)\xf2\xe1i\xbd6\xe9\x8d\xf0*d/\x84U\xb4\xc94\xeb\x86\xd6D\x9d\xed)\xf8\xec\x9e\xf4\x15<\x85\xbaI\xfb\xf74\x80\x9a\xab\x81\xf0\xb7\xaf\xbc\x1b\x1e\xec+\xb3\xa5\xf0\xb3\xf1\x96\xc2U\xa4\xcbj\xae\xf3Q\x13f%t\xe9>\x7f\x86\x9d,:_\xe5\x15\xbf\xdb\x18cC\xfc\xb3\x91\xf4\xec\xf8;\xdc\xdeU\x02u\x07\xfd\xde$\x1f)\x9f\x9dj\x9e=\x1f\x06\xdc\x1b3\xe0\x1c$U\x0e^=\x9b\xce.\x88\xef\xdd\x1b\x0fN\xdc\x06mX\xf20{\xfd\x9bW\x93e-\xbb\xf6\xc2\x16\x9e\xe7Y\x1d'\x19)_e\x8b\xbcO\x05z\x07\x83\xf8\x8bN\xf1}\xffl{a\xb3\x88\xc7\x08R%^\xbe\xc2\x11\xbc\xefZ\xa95\xc3}\xa1\xf8(%U;\x88\n\x0f\xe7\xf9\xa2\x15\xd9\x06\xe3\x11\x0d\xf4.\xe6N\x07\xa0\x10\xfdfn\xb4A\xde\xd3\x87\x1e1T#\x82\xd2\xb9\xff\xd8\x93\x8c;\xdfL\xe0E\x87\xeb\x10A\x11\xaa\x1fn\x18\x01B(L\xe0\xb2\xc3\xd4a\xa2\xd4\xd7y\x96\xd4\xb9K\xc4\xc7\xae\x84\xd1\x112\xcf\xd9\xbd8\xedl\xc0\xd2U\x7f\xe8B\x03\xb6\x1f\xa3\xd6\xb8\xfc2\xb4\xab\xaf\xaf\"\x92\xfdcC6\x82T\x8b\x00\x19\x92x\x86L\x08\x95\xf5\x9e\xc7iz\x11\xcf.\xd5\x8a\xb9F~\xa2\x87\xd8\xe0\x9c\x196\xbc!\xd7\xd6ik\xe7\xfc3\xcf\x19R\xfa\xde\xe1w^\x10\xc2&\"Y\xb5)\x89\x92\x14\x97\x03\x02\x93J\xf77\xab\x10=1\xde<\xc6\x13\xee\xd6XG\x17T`!sf\x0dQ\xf9\x1f\xd0\xacY\x8cJ\xdf$\x0b\x8c+1\x89o$#\xad\xb8\x9c\xc6g\xf4\x8bp8\n\x07\x83\xd6\xe9\xe6\xa2. \x9e\xf2\x92(8C\xacc\xc6\x82\\`\x11\xadbT\xaerH>\xa6\x90\xfcQ0\x1f\xba\xee\xd4N\x1c\xd6\xf7\x8bF|\x15]\xc5i\x82&#\x1c\xeb\xfc<\xe4|\xde\x8b\xb7\xaf9A\x11\x96\xec\xad0C\x0dr<\xf1B\x93\xad\x8c\x07\x94\xaa\x93\x18\x83\xa3\x15qU%\xd9\x12b`\x95!M. \xfca\x9e\\\xfd!\xc4\x97\x80\xfdr=\x85\xe8\x07\xdf\x07\x90\x97\xf0\xfd<\xb9\x82\x07\x7f\x8a\xd0-DL\xd0\xb1\xc7YJ\xdb\xc7\x0e_\xe6\xf9@w/\xf3\x9cu\xf62\xcfEg\x99\x1a\x03Z\x89U\xc6\xf9f\xec\xf5\xc3*\xa9`\x1d\xdf\xc0\x05\x81Y\xbc\xa9\x98W\xcd&K\xf0\x02!\xc9\xb38Mo \xcd\xe39\x1dP}\x9dC\x92\xcdIA\xe1\x9b\xd50\xcb\x8b\x84Tt\xc8lL\xdc\x07\xc7\xb0\xa5\x98\x9fX\xdc\x19\xf9\x0b\xd3m\x1bR\xf8 h\xe2\x9ci:\xb0\x9a\x9fRq\xbb\xe0n\xa7\x06\x05\x122H\xe7E\x99\xcfHU!o\xc6\xc3\x99\xfaUt>c\x7f\x1a\x15B\xf4\xeb\xa5~\xe2T\x92\x7f\xe3\xeb\xf2d`\x12\x8c\xa1QSa?\x1d\x12{\x0cSY\x80\x7f\xee\xcf\xd8\x15\x80Y\x07L{X\xb0\x1e\xfaB\x05\xe5\xde7\x17i2\x93\xf1\xbb-\x96)sa,k=[\xd4\x9237\xf3\x85\xf9\"\x14@\xab\xa1\x17E\x9eq\xba\xc3\xd2O1\xac@\x82\xa4d\x1e\x84\xb0\xd0\xb6\xa3\xbfk\xfd\xb1'\x07<\xc3\xd8xvS\x0e\xe0\xc0]!\x1f\x99\x19\x00\xb7\xa6\x12\"r\x84;o}\x93\x82\xfd\x06\x8e\xe0\x95\xb1\x89\x0b*\x82a\x13)\xfe\xab C\x00\\9\"\x89w\xf7d\xa5\"a\x16\xc2E\x08I\xe0\x88\x08\xc6C\x8b\x1bK\xe3\x92^\x07!\\\xdb\x8f.\xb7\xfb\xfcf\x95\x07N Ud\x1c\xce\x08\xa2_X\xdb%\xd6\xcf\xcd\x81\xf8p\xcfD\xe6j\xdc\xed:\"\x83\x8e\x0c\xc6T\xb5\xaf\xd0n{_Q\x96\x7f\xe0\x01\x020\xd4D\xa3\x9191\xd0/!V\xed; '\xaf\xcb\xddc/\xa7u\x8f/9\x0b\xfb\\\xcek\xa1;@\xeb\x98\x9e\xb7n\xeb\xa7F\xf7\xa0;\xde\x93\x10b\x1dD(\xac\x14N\x8e\xb9\xa5\x0d\x86c\xdd\xe0^\x1b\n\xee3\x8ffq\xf6\x9el*\x9e\x19\x8a\x8eb\xd3\xc92C\xc5\x0b2\x8bg+\xc2v:\xad\xa1oQP\xf6M[_6\x8f\x9e\xff\xf9\xe4\xf9\xff:\xfd\xe95\xaa\x16\x99\xf6Q\xdf\xc2\xa6\x97\x93c\xc4\xc7\xe2t\xd8D\xf9\xa6&\xe5\x9f?\xbc\xfe\xd1\xd4Ke\x1b_\x08\xdd\xa8\xbc\xa2\x88\x13b \xb5Q\xe1\xe2Y\xaf\x16\xe9\xba\x90\xa9\x97O\xe2\xce)\x94\x9e\x94A\xa8\xfaWf\xcc\xb1r\xb0e\x10\x8c\x80H\xf5\\\x06\x9c\xe1\x91\xbf\xe5j\x1b\x1c\xec\x85P\xc0.\x1c\xec\xa1S\xf4\xc7\x0c\xfc\x8a\x94W\xa4d\xd5g\xe6\xea\xfa\x99\xe9tWtg\x1dx!h\xaee\xfb4\x03\xb5K\x86F\x0e\x19\xaf\xdd\xd3\xef\x19P\x81\x07\x98r\xd5\x90\xe9'\x94GIV\x91\xb2\xfeP\x12\xc2\x1c\x1b}F\x9d\xe81`\xe4\xd3.X\n\x80P\xb3\xd3kE\xab>\xf2:\xefG|\xfa\x85\xf7O\x87\x8f\xbe\x0d\xf4\xcd\x9b\x8f\xa5\xc6\x0fH\x03$TM*\x1a\xe37|\xed\x98\x95@\xd9DS}\x1a\xa01\x8fN\xb9l\xd0A\xb1\x060\x00\xeb\xb1\xf6;\x98\xc8Z,\xe4+\xcf\xeb\xd7\xb3\xf8\xfb\x82\xab\xbb::?'\xd5\xeb|\xbeI\x89F\xcd\xc3C\xb2f~\xf7\xea\x0d\xc3\xe7b\xbc|4\x7f)\xd5f\x8e\xa1\xd4Z\xd8\xcd\x859\\\xdb\xb4\xeeV\x1d\x0d\xaf\x83r>\xff;\xaaVqA:f\xd3t\xe7\xce\xca\xe4\x82L\x94\x8at\xfa\xa8\xc2\xfa\xc7&)\xc9\xbc=\xe2yR\x15\xf4,v\xfe\x80\xf9\x94\xd5C=4+\x10\xdc\xe1\x12\x84-8\x98\x11W\x7f\x0b\xcd\xaf<\xc0\x14\x16I\\\x89\x90\xb2\xccK\xf5\x8e\x04\x1f\xf4\xb8.\xfd\xddt\xbd*\xf3k\x8c\x80t\xc2\xbfj/\xa9\xde\xbc\xdb O\x95\xcb\xe4\xc7\xdd\x1bJ~\x9b\xdc\xb3S\x14\xa9\xae\xba7\xa41\xaf\xdf\xc5\xde\x0d\x7f\xdem\xbf\xe2\xcf\xbb\x17\xc0\xfc\"\xb9\x97^\x80_$\xf7\xd2\x0b,\xf8\xf3\xee\xc5/\xbbH>x\xa2\xbbH\xce\xfc\xc3\xc7\xddy\xb1\xfb\xe3\xfd\xc3n\xfbW\xbc\xfd\xee\xb5\xfa\x9a_\xabw\xdbY\xf2\xe7\xddy\xb1\x1b\xe4\xde=\xf4\x05\x07\x7fw\xba\xe7\xbc\x99\xeep\xae\xf9\xf05W\xc4\xb4zw\x94\x9f\xf0y\xef\xda\xfa\xb4\xafN\x7f\x0eG\xddh\xda\x97p\x04\x0f\xdb\x8f\x9eQN@\x04\x00|V.\xf1\x12\xa9:\xebD\x18|\xab\xd6\x12\xa1\xeb\xba\x95\xde\xa9\x950\xf4n\\\xe7\xa5\xa9\xf6\x07\xb5\xb6\x88<\xd8\xae\xf2\x9a\xdfb\xcb\xdf\xd3gg\x94g\x9b*\x03.\xe3\x9b3O\xf7\xf4\x87\xcdbA\xca\xde\xbb\x17q\x1d\xff5!\xd7\xbd\x17<\xc7\x87\xee\x03\xd2{\xf82\xcd\xe3\xfa\xf0@\xdf=\xbe|\xf4P\xff\xf2UV?6\xbe\xd9\x7fd|e\xea\xecu\\\xf4\x9e1\x17\x14\xf1\xf8C\xe7-\x8b \xd8\xfb\xe8\x94\xd4\xfdg\xc8\xdf\xf5\x1f\xdf\xac/\xf2\xb4\xf7\xf8\xa7\xc487|\xf5<\x8d\xd7\x05\x99\x9bk\x98\xa6O\xdf\xb5\xe6O\xc9\xbc\xf2\x1e\xc9\xa8\xf8\xeam\xe7\xe3\xbf\x91\xf8R\x02ig?\xd4262,\xef\xab\x10~\x0e\xe1M\x08\xefu\xb7w/B\xbc\xbb\xc9\xe0\x1e\x9c\xf6\x99\xeb\x9f\xf8\xab\xe7\xfdW\xff\xe0\xaf.\xdb\xe7\x03ei_\xe1%\xee\x0b*\xb5\xc31\xbc\xa2\xe3\x90#\x98\xd0\xdfA\x10\xaa\xda\xd3\x17R\x84x\xd1ol\xe7Z\xcd[\xdaa\x9e\xe8\x0c^\xe2\xbdBWJ\xa5\x9f\xbe4\x89\xc1thW~M%\xee\x1fe\xd3\x18\xd5\xf7E\xf7\xe02\xc4\xbf\xa5\x1d\xff\x13\x8e`E[\xe9\xbd\xa5\xe5\x078\xa25\x8e\xe0-\x15\xb8\xf1\xafwz\x05\xc6\x85:\xc1\x8a\x8e\xe2G\x83\xaa\x03[\xf9 \xdb{F\xff\xfa\x01\xb5ToLr\x81\x98\xeeO\xac\xee1\xfcr\x0b\x13Xv'\xff\x13\x1c\xc3\x82v\xbd\xf1_0\x1d\xe7\x04f\xf4w\xcc\x7f\xf7\x1a7\x82F\xf4\xba\xf3z\xfa\xcf3\xd9\xc1\x1b\xee/\xfb\x8bA\xefH\xc7\xb8\xa6\x1d\xfe\x93N\xbf\xdf\xdb\xef\xcc\xbf\xde\xa3\x0d\xde{`!\x18\xcb\xa0\x8f\"\x7f\x85#x\x8f\x9aj\x1d\x9a\xfcU\x0e\xf2\xaf\xfd\x97\xef16#bF\x88~\xed\x0d*\xca\x08`\x92}\xe9\xd9t\x00\xde\xdcbXC\xbf\x14\xbb\xb1D&\xe7}\xd7\x12<\x08u\xe8\x7fn\xeb\xd2p\x9f\xf3\x02\xc7\x9d\x87\xa0t\x9c\xbbvLa\xf6g8\x82\x7f\xc01b\xc6\x1c&P\xc0\x04\xff\xbe$7\xd5\xab\x0c\x03\xe2\xf6:\xfd\x1b\x1c\xc1K8\x16{{\x02\x7f\xee\x01\\h5\xfd\xbf\xd1U\xab\x15\xde\xcf4\x93\xbf!5)1\xc6\x13z\xe8\x9e\xa1%\xfd\x0b\x9c\x8f\xdb\xec\xe4\x93\x91\x1c\xe7\xc1\x93.\x87$8N}\"\xaa\xef\x1e\x8f\x9669<\x12\xe6u\x81W~;\x18Z\xbc\x95\xeb`\xe4\xb8\xf7\x1f\x1b\x92\xc2\x1ety2\xce)?\xd6g\x85=x\xd2}\xbei\xc2\xf62\x0f[\x11A\x97\x1d\xa0\x15%#\x83\n\xdfV\x94\x8d\xe9\x19\x8b\xb2\x81\xce[\x14\x04<\xcc\xc6\xb0{{{}a\x02\xb1\x1e\xe8N\x06\xc1\xeab\xeb\x81v\xd8cX\xb9{\xd4\xf6\xab\x8d\xcb\x9c\xb4\xaeuG\xae\xf0\xe3\xc7z\xcc<\xec\xc9H|\xb0\x8f\x0f\xb7\x1dl\xe2+\xa9\xa0\x99\xc9\x18&\xec\xf7\xbe`\xf0]4\xcc\xa5\xde2\xfed\x1b\xa6\xfeF\xa3Q\xa3@\xaeZi\xd7\xa8L\xe1Z\xc6\xfb\xb0\x0f\x13\xc0\xe0\xfd}\xe2e\xbdc\x93\xa8KA\x1a\x0b\xb9\x82\xc5\xfd\xbc\xbf\xcf\xaebs?i:c\x1d\xa1\x14\xc9\x82\xf7o\x82\xa7\xb0\xbb\x1b\xc3\xf7\xb0y\x1a@\xc5\xcd\x11\xa65\xecB|\xa6?\x17Y\xe3\xfawr@\xa9\xec\x816\xb5/{\xa9\x9f\x06\x90\x8a^L=\x08\xf6\x87\x05\x0c\xcd\xfc\nS\x8a\x11\x96S3\x04\x9d\xdeo\xfb\x85\xefn%a\x0f\xbe\x1f\xf8\xa5\x01A\xbf\xc0\xf7\x91S*\xa6\x15i\x12\xab\x87\xe05*\x16\xaf{Y\xce\xb3\xd3*w1\xb7\x81A\x05c@B\x0d\xd5\xcbzZ\xae\xa6\xf5\xa7=H\x99\xf7$\xea\xe2\xd9\x0dV3\x05\xc9\x1f\x90\xfe1^w\x04N\xd1\x884M\xe9/\xafr\x9b\xc0\xbc^,q\xdayTs\\\x11\xb4\xdedQ}\xc94;3\xd8\xdb)\xb0\xa4k\xd9\x80\xc2\xcf\xfc\xfd'\x07\xc1\x17h\xcf\xbe\xf6\x92\x1bM \xf54\x03\xc3\x88\x18\xbd\xa4\x92l\x91k3\x87\xd1\x92\xe6Km\xee0\xc0\x94\xb5e6\x81C\xfdKT\xdcM\xe0a\xef\xa5\xc659\xb3\x1ao\x82\xb2nSrF\xb9\xb6\xfb\x9a\xfb\xd0~\xd3\xccOs\x96g\x8bdYEi\xbeDs\xc0~=F\x02J5\xdb\x00\xa8f\xa7\x89\x8d\x91`\x97Z\x92 \xcb[\xafDR\xc5\x12\xfe\x04\xfb\xa8\x87f'\x00\xa5\xca\x94\xb0\xee?\x05J&\xcb\xa7\x10\xef\xee\x06\x94F\xd2\ngjkZ\xb2\x89\xa0\xfa\xd3\x91\x12\x92\x95+M\x83)9\x8b\xe2\xa2H\x11\xe5\x06\x0d\xda\xc5\xe9\x1a\xd1\xb5D\xfd6&)f\x17\xee\x1e}\x88\xf7\xb3\\/\xdb}\x8fOY\x05\x8aD\xbd\xf7\xf4!{\x8d\x18\xd8{\x8fO=\xad[>^Vc\x0e\xa8\xca\xe4\x17\x8f\xa8\x99\xf4\x91\xc00]\xa7S\xc2\x9a\x07\x8e21]M\xe3\xd7\xb9vpc\x8f\xc4\xc6\x978\xae\xa5u\xfa\xb3\xc0\xc0`\x90\xce}\xc4:\xbe$\x7f\xae\xeb\xc2\xa7\xc4\x97\xbc\xa4\xaf)Y*\xf2\xaa\xc6\x1f\x06\xd5\xc3\xc5&I\xe7\xef\xc9?6\xa4\xaa\xd5\xe6\xd4\xe7\x06\xd2\xc1r{\xab\x1f\xf1G\xfa\xfa%\xa9\xf2\xf4\xaaU\x9f?\x1a\xac\xcfMM4\x9f\xf17\xfa\xaf+R&q\x9a\xfc\x93\xbc'\x95\xfa\xad\xfa\\\xffe^\xbc\x9a\xab_\xacHZ\x90\xb2\x8a\xe8\xf3\xbbEc7\xdc\x91\xc4\xad\xd6\xeb\x0c\xf0\x84\x9e\x96\x8d\xfa\x84\xfe\x10-\xf7\xe9\xd1\x15w\x1d\xa1\xb5\x8cGQ2\x81\xd2p\xd2\x98\xa3\xe3\xf2.'\xba\xa8<\x1aM\x8e\xe0C\xe8h\x91+\xc8\xc5\xa0Q>W~\xa1\x97N\x94r\xcd\xa7|a\x00=\xf0If\x1anF2\x15k\xceNDx\x0d\x83\xe7wGp\xd0\xb9\xdd\x00^\xb9\xe5\x9c\x7f\xf9\xfc\xd9\xc0A\xb0\xaf\xf5\x90e\xfb\x7fS\xc6\x17)\x19\x00e\xb6Y\x13Q\xc7\xc0\x10,I\x8f.\x01h\x82\x10C\x1d\xd9On\x01\xb0\x1e\xbf\xa8\n\xe9\x96#\x9f\x88-\xd3\x1f\x138Dl\x11\xad\x8c\xc0\x9d:\x9a\xfbY\x08^\xcc\xfd\x8a\xb3\xfe\xd4s\x17\xfb\x18\xde\x9c+\xef\xdaO\xbdRG\x05KL\x05\xb5_Gt?\x1f\x1c*\"\xaf?\x1d\x1c\x82J\x072\xff\xe1\x81\xf2e8<\xf8\xce\x97\xdfn\xfbek\xb4\xe3\xbe\xdc\xba\xcf\xc3\xc3\xc7\xe6O5R{\xfb\xd0o\xbd\x92$\xb2\xd4c\xb7@-\x0dr\x13c@\x1fy\xf6\xdb\x93T\xea\x07\x93\x1b\xf1M\xec\xb6.\x1f\n\x7f\x82\x83\x8e\xb5x\xc3\\\x1e\x9c\xc1q\xfb\xe7\xc4\x98\n\x8d\xb29\xbe\xa6\xf5Cc\xeb\x87\xed\xd6\x0f\xcfP\xff\x1eDW\x07o\x0bRbL\x9aWh^\x12\xd7 \xc6/\xb9y\x9d\xcf5\x1e\x9f*\xa8[\xa9\xddTE\x0b&kP,\x10&\xe8\xf87\x13\xf4#\xf0I\x10\xb0(Qy\xd39s\x84U\xd2r}\xac0\xc7\x96\x174\x86a\xab\xf6'\x01L \xe1W[\xfaE\x1e\x9e\x9e\x9e\xbej\xfd\xc5\xcc\x02\xc9@8K\xdd\x12\x8dC\x00\xfb\x12\x99\xc8\xad\xc0A\xbfnG\x84\x80]\xf0\xce1}P+QZ\xb5\xf3\xff\xfd\xfe\x9b\xff\xf1\xf7{\x7f\xf4\x83\xf3\xdd\xa3\xe9/\x1f\xcfn\x9fN\xbe\xff\xd3\xe7\xe8\xe3\x83\xe3\xf0\xe3\xc7?x\xde}\x96<\xed\\g\x99\x0b\x0df\xb0\\\xe8\xcc\xf3\xb0\xb1\xa1\xdbo\xfa\xad\x95~}\xff<\xf8\xe5 \xbc\x0dD\xd3J\xe6\x12\xff<\xf8\xa3@\x80\xe6\x83\xe9\xf9Y\xf0\xc7o\xf8s\xcb\xc6UF\x851X\xe7~M\x87\xd1\x0f\xa4nX\xdc\xd8v\xa0\xf0\x06\xbd\xfb\xfdtL\xa667\xb66+N\x1fw\xf6\x90\x03q\xc6\xc4\xcaDWA\xdc\xc1\xb1\xe0Vb\xcf\xeel\xb3g?\x7f\x86\x1d\x12\x15q\xbd\xaa\xfa\x8du\xaa\xb3jC\xb1-@Qs\xf1\xea\xfd\nR\xb6\xcf!\xc9\xa0\xd4\x9b\xa8*\xeaXZi\x9a\x1b\xa2\xcc\x03\x87\x85\xf7\xee\xd9\xfbg\xafO>\x9c\xbc?e\x83O\xa2:\xff\xa9(laSD\xb9\xe2\x0eg\xb4\xa7ibP\xa6\x8aB;\x8c\x07\xe9el\x83}\x1cX\x87\x04\xd0\x18j\xdbk\x8aR\x15df\x8c\x13\xa6+t\x95XX\xd1\xdc\xfd\xa35\xa9W9\n]-(\xbb7 i\xfed \x9c\xa8Z4:(]\xc1\x0c4\xbe\xc9\x06]-(\x85\xa1W\xb2D\xe8\xcd\xe0Gz\xa7\x97\xfe\x9b\xf6\xaf\xadT\x96\xa0U[b\xe3\x9a\x0bp*g\x95~\xe6\xef?\xee\x06\xff\x00n\xb6\x86o\xbby(\xea(\xa9\xde>;=t\x125\x98.$/H\x16\x17\x89\x91\x89\xe0Y\x15(\xae\x17\x0d\xae\xd3\xc9\x1ez\x1a\x16<\xa9N\xaf\xe3\xe5\x92\x94\x07#\xc6P\xb1O\xb6\x18\xc3\x81n\x0cy\xf1j\xce\x12\xf0\xd7Q2\x7fY\xe6\xebwq\xbdz\x8d\xf8\xcd\xdcI\xeb(%\xcbxv\xf3\xaa\xff6\xa6o\x97\xa4\x96\xc7\xf9\xfb\xf8z\x84\xf8\xc2\xd9[F}\x8f\xd9Ib\xd7\xd7J\xc9/\x12[\xd7\xbc5\x18!f\xbb\xd5\\+\x11\x8b\xcb&\xa1\xdf;x\xe2$\x83'Nb\xa3z\x89\x12\x19i\xc7p\xef%H^\xa2\xf2\x85\x83\x0c\xca4\xf7\x13\x19\xf0\"\xf6\xf9\x1f\x9b\xb3\xa8\xca\xd7\xc4\xb7\x03\x14\xba+\xc2\xee\x16\xb5uu\x91\xd7\x0c\xd9\x10\xd0>>\x9bK\xdc\x80#\xd8\xd0\x87$\x9e\xad\xd4\x87\x15\x8b\x93Q\xaeQ\xcb\xc5w\xc4\x98\x0dQ\x90\x99~mY\x005D/\xb3\xd4\xa1\xb3\xd9\xc1\xb5F\x96\xaf\x8e\xbe\xf9F\x8emn\xba\x8b\x82\xde\x89m\x0c2+\x0e\xda\xccx\xca\"\x9f\xbd\x17\xc2\xa2uZ\x0e\xac\x9d\xc0\x18\xcc\x92\x15\xafIMJ\x0d\xdb!\x8a\x1cgE\xc7\x19\x07\xb0\xe3\xb0\xe7D\x91r\xe0\x948\xf0\x08;\x9did\x0d\xf6{\xb3<\xab\x93lC4\xa9a\xd4r\xc5]qs\x9f9\x7f\x99\x9cqE\xa1\xddj\x83\x02uK9\xad\xa8tB\xffc\x91\xca3\x8a\xc6\xf8\xf4\x08\xa6\x99ev\xc0\x87\x86\x87\xcb\xb4r\xa8M\x076k\x84\xa6\xfd\x00f}{'\x13\xbd\xd4\x15\x12\x9d\x9f\xe7e\xb2L\xb28U\xc4)\xe6\x96\xa1}\x83\x12\x8cBT\xc2\xf6O\x96\xb7\x9f%L\xe7W\xed\xd6\x81\xe8\\\xab\xbbE\x86\x00Td\xc4\xac-\xf4\xba\xcd\x98\x02\xbc\x80#\x98M\xf7\x1c\x00NKa\x84\x91\xe9\x0d\x15P\xda0*:0\xaa\xac=\x9b\x19%\xfb[\xe4\xe5\x9bm\xcc\xce\x18\xeb\xb6\x04\x0e\x9d\xb9%U\x84ZV\x06\xda\xd7-\x92^\\QzQ\x07\xe0\x15e>\xdf\xcc\x08\x1f\xdc\x15\n\x02\xb3<\xab6\xeb\xf6\xb3\x8a\xcc6eR\xdf\x88g\x9f?\x83\xbf\x9a^\x9d\xa1\xb1\xdb\xd5Y\x08s\xb6\xf3V\xba\x0ca\xddB\x01\xb3A\xc6f\xa5\x909v\xa64\xed\xd0\xbf\xb97\xa0\x03\xc8\x80\x83m\xcd\x14\xf5N\xf5\x81{\x18\x98\x14\xe1\xbar\x03G\\Ab\x9f'X3pt\x8b\\\xa0\x8b\x10\x9d\x16(\xd1M\x1b\xa2;\x0f\x9e\xc2\x8eO\xa7\xe8_\xc0\x11\x9cG\x19\xf9T\xfbA\x10\xcd\xf3\x8c\x04O\xf9\xe4]\xc1%\n\xed\x8f\xb2z\x17,\x00\xa8\xdb\xbcD\x91#>\xa1(um'3\xdd\xc2n\x90N\xce\xc6\x8eZ\x94\xde.\xa3\x0c\xcf\xc9\xb6\xad\x01\x87\xc7\xa7\x91h\xa4+\xa7#QKW\x9e\x8fD7]\x19\x87\x82\xba\"\x17\xf92D\xa7\x95\x0eZ^\xd3\xe5\xa3\x98I\xa1\xe6_\xc2\x11<\xebb\xe6'\x8e\x99;\xf6\xab\x981\xe5\x8a\x87\"\xbf\xdc\x06uu\x85bb\x87\xd7v>\xc5mE\xde\x1be\x1e\x81\xb7\x19*p\xc4\\\n\xc4\xbcq\xfe\xd4q\x9d\xac\xb5\xb6\x150n\xfdJ\x0f\x1b\x8d\xf9K\xef\x89<\x89T\x85\x08G\x8e\xceMQ_E\xbb\xe0J\xd8\x87\xdf\xe9T\xb4\x85P\xd1\xf6\x82Z\x03\xf7\x17\xb6k(\xf8\xf0\x98\x07\xa4b\x11\xa1\\\x15rs\x08\x8d\x06\xab\xdf\xe9jL\xa7D\xb9w\xfc\xfb\xc7\xeb\xb3\x07\xcb\x84]\xfe\x0d\x80u\x9c\xe9\xc1\xe3'\x036\x16\xffo\x98\x1e\xdc\xcd\xd5s\x9a\xc7\xf3S\xa3\xc2\xb0\x94\x9c3\xd3R\xd0\xe6\x0d\xe9\xdb\xf5\xc9\xc6\xe4\xdb\xcb \x90(\xbf43\xf2\x9b2\xa5U6e\xca\\\xc5\x8c\x15\xab:\xae7\x15\xe6$\xc1\xbfl5Y\x8aPQ\x9b\xfe2\x7f\xb1\"\xf1\x9c\x94\xd5\x04\x12\x9fD\xfc\x87\x81B\xe8\x1b\x89\xe1\x08r\xf1\xe5\xd4\xe3y\x84\xee\xd3\x9d\xe7\x19\xf4\x10\x1b\xccC\xf9\xf93\x9c\xfb\xb1\xd9\x0f\xca\xdf\xa0kKM>\xb1\xf8\xe5\x17i~\xc1\x14X\x17\xe8'\x1e\x88\xcd\x1c\xd5+\x929(\xb9)\xc9\xceY{hH\x97G\xf3\xb8\x8e\xd9\xdf\x9b\xc0r\x00]\xf5\"\x01;(\xea\x84\xa63.\x8a4\x99\xa1\x02\xe9\xc1\xcf\x15\x8bO\xc1\\w\xfer\xfa\xf6MT\xc4eE|LA\xb4l\x8c>\xe3\x05\xf91\x8f\xe7C\x0c\xf4-\x1d\x85\x0e\x84\xa2\xe4\x98\x01\x01\x8e(\x85\xc8\xa3\xfc\xe2g0j\xf5\x9dX\x83\x9c\x8d\xf5\x84\xdbl\xeb\xb9\x01\xfd\xe9\xc3a\x91\xf7\xa9\x83\x9b\xe1B2\x9cT\xaaO\x19\xf6\x8c\x94a\xafM\x19\xf6\x18e\xd0\xe3\xaa\xce\xbf\x04\x94\xa5\x15\xe3SC\x8e\x10\xa1\xd6e\xf6@:\x1d\xaf\xf9r@ \xba9\xcd\xe8@\x85\xbf \x9a\xfaGI\xc5\x1d\xa1\xa6\xd9Y\x00\xc7\xac\xd2\x04\xa6\xf4\xff\xb3\x10\x7f\n\xb9\x8b\xe2\x93\xf0U\xd1@\x1d\xf1\xb7\x1b,s\xc0ld\xe0\xa4\xd0Gfy\x99\xf0#C\xc4\x89\x13\xcfd\x9c\xd1\xa3\xadl\xaeVm\xfb\x0dS\xe0\x17\x12\x15I\xf1\xa5\x06,\xcdM\xe3,Oy\xd6\x9a\x97\x98\xf0\xcc||\x90(N\xd3\xfc\xfad]\xd47\x18;\xd8|||\xd9\xcc\x8fE\xf2\x1dJ\x1f\xf5WX\xdd\x04@es\xfdb\xc8\xc8\x1f\xfb9\xcb\xdfp\xc1\xa2k\xa8 \xcd\xe5\xd7y\xff\xe3+\x91~'\x9b\xe5s\xf2\xd3\xfbW\x86\x80P\xa0p\x92\xa8\xcdM\xb8j\xe8\xa6\x99]\x1eX\x1dma\xd0\xfc\x16l\x81\x19\x95\xcf;\xf7\xe4:\xee0\x08\xcdW\xbe\xb9m\xa9rfd\xd4\xde\xbf8C\x97G\x18\xfe\x1d\x8e!\x8f\xd6q\xe1'A\xf4s\x9ed\xbe\x17zt\xf3z\xebMZ'\x0c}\xd4J0\xe9\xd4\xd7\x03`V]M\xc0\x0b\x0d\x06\x99\x15\xbe\xfd\x1f\x07{\x86\xf75{\xbf\xf7\xc4\xf0\x9en\xbfj\x02\xdeg\xaf\x0fP\xa4^\x94\xe9\xc0\x14\xd0\x9e\xe7\xb4M\xab\xe1{\xe0\xceU#\xda\x02\xce73U'7Dx\x85\xd1\xd64\x1b\xb8>\xa1\x9bvg\xa7\x8c\xaa\xcb\xa48\xa1\x88\x9ed\xcba\xab\x82\x9c\x87\xeb\xefo\x0bc\x88V\xe0l\x95\x1d\x83EQ9\xf6/\xa2)\xc6^ny\xe2\xbf\x9d6\x82v\xa3Q\x88\"6\xf84\xa1\xc7\xcf\xc6\x8f\x8d\xeeJ\xa2pc\x1fC\x1a\xd2\x10\xf2 \xd4\x05v\x0e)Oo$0\xeb\x86\x9dB\xa90Y\xa0\xe1\x91~\x14l\x85\xcc\x0e\x0eI6Of\x14\xa3u\xf1R\xbb9o`\x00\x8f\xd3\xdf\x8e\x95Aq\xc3*\xf9\x08\xee\xd4\xf3\xd0\x9d\\[=\xc7\xd6\xfe\xb1!\xa5!\x8203\xa9Y\xe4\xe5Z\x7f\xd0\x0c\x86fM\xfb\xfb9 \xc6X\xb3@\x83\x04\xb1\x9fL\xc9\x19;)\x07\x10|`3\x168\x15\x83\x8c\xc3d\x12\xf9\xf29\x7f\xf9\x01_\x9a\xed;P\xe8{\x80\xf4\xbb\x88\xcb\xfa\xe3\x03\n\xa9\xfbT\"y\x90D5\xa9j\xbf\xb0\x9a|\xf08j\xa6\xf8\x9d\x80J\x04.\x01d\xe4\x1a\xe6\xa1\x06\xa8=\xf6\xd4*\xd6\xb06\xa3\xb8(H6gAu\x92i}\x86\xf6\xbdC\x00\xd6om\xa6\xf4\x94\xe3\xac\xfc\xc40\x1d\x1ez\x98\xe1T\x7f\x07j\x91L\x1bq\x058\xf8V\x98)\xb2*\xd2\xa4\xf6\xbdco\x00\x01\xae\xa0g\x0b\xbc\n\xa1\x1b\x8aB-K\xba\x9b\xa6{\x03G ^ O\xf7\x07j\\\xa0=\x86\x19\x85nl\xf8q\x8e\xe9\x96\x04 db\xe6\xcd\x00\xb2t\x90#\xd7 \x87\xeb\xa6\xe3\x8bu>%f%6e\xab.ZCl\xa8\xf4\xf9PFmP\xa9u?\x0b\xa7(&\x8c3\"\xc4\xb5-\x9d\x8d(\xf2fSG\xb0C\x96\x0c\x08\xcfG\x12\xb0l\xbf{O!\x83\xef\x81<\x85lw7\x10bYC\xb8\x87\xac\x8d\x04gRG\x8b$\xadI9~1\xccZ\xfb[\xc1O\xde3\xb9@@\xd3LI\x8f\x84c\x0fv\xf1(\xf7\xfal\x1d \xa3p\x11BE\x99^}{L\xe1u\x04K\xd8\x85\xeb\xb0\xd9\xd4x\x928\xecj\xed\x94\xbe\xb2\xc1q\x08uT\xad\xf2M:\x7f\x91_gi\x1e\xcf\x9f\xa1Z\x8deg%\xe9\xc2p\xdd.\xed\xc3\xfc\xcc?\xe8eK\xa4Eh\xc5\xf7\x86\x94\xe2Z\xa3\xe6\xb9\xd0\xa7\xeb^\xae\x1a\x8b\xe7\xfe\xcb+\xf1Rc\x0f\xad\xba\x1a\x0b\x9b`\xf9\xec\xcf\xec\x8c\x136\xc1l\x07Ri\xf8m\xf9\xbf\xe9\xea K\xce5)\x97\xe4U\x86\xcf\xde\x96\xb4\x02\x1cA\x8ao\xb8\xc3\xb7C\xc0\x1bh\xd6Zz\xdf\xd8\x11\xdf,\x11\xb2]Y\x7fq3\xda\xfa\xb2E\xad\xfb\xad(B\xf2\xeeg\x90a \xbaK\xab\x9b\x03\xaa\x8c\xf5,2\x08\x82\xaa\x01\xbf_\xf2\xc8\xe85\xfe\x95\xf9\xa4\x97\xa8[6\xd1F}Z\xf9\xe0;\x8d\xc5\xfdZ\xa0\xb5\x169\x97\x02\xc5\xbe\xd5\xbd\xbd\x11\xdf\xf6Ru\x02?\xf5\xe4\xae\xd2\x83\xa3\xed(op\xda\xe8\x83a\x02\x9a\xf4\xee\xdd\x1d\xc0\x8f\"\xdbI \x88?=2\xaf\x14S+y\x94\xad\xe3\xf2RRj f\xae\nUL,!\x17Kn\xa0\x97\x01\xf6\x8d2\xc0~[\x06\xd8?\x1b\x08C(Ng9\xcc\xeb2.\x1c\x0f\x14\x16\x82\xfdi\x00\xd5u\xc2T\xc5QQ\x92+\xe4\x8d3\xf2\xc9\xca6\xce\xe2\x8a\xc0\xded\xb0\x0e\x08\xd3,\x93\x10[\xdb\x84X\x91\xc2\x1e5\x02\x14\x96u@O\x1c\x0c6\xbf\x92\x04\xac\xf9\xfb\xf3gL.\xa7\xdd6q\x10\xc2N\x1c\x95,\xa4\x04\xa6)\x9b\x91\xa2\xce\x07w\xb9Z\x18`\xe0\x08\xf6\x1d\x0d\xb1.J\x12_Zk\xda\xef\x87\xe5\xb5$\xef\xff\x11\x9d~\x7f\x1e\xda\xfb\x17\xb5\xe0\x9a=r[3\x12\xd5{\xcc\x1c\x9fdu\x08\xf4\xe7h8=\xf9u\xc1\xc4\x87\x1c;\x00\xe1\x89\x1d\x08,\xe3lmYjlm\xdfa\x1f(\xa7_<$|\xc6&\xe13\x1c\x96/y8+\xce\x81\x19\xbb\x90<\x9a\xb1\x1f~\xb8\x88\x08z\x92,\xec\x1f\x86\xca\x0ex\x14\x82\x8f\xf9\x1eJ\x8c\xed\x82\x071\x06y\xa1O\xcbt\xf8\"\x0b$\xe0\x1c\x90Q\xb2\xab*2\x8aa<\xa1{]=@|\x16\xaf\xd4\xadw\x07,\xa0[A\xed\x1a HU\xe4YE\xbe\x84\x82\x1c|\xf7\xebn\x8d.\x0598d$\xa47\x13\xa3\x0eP\x14\x84\xdc\xc1\xa1\x1b\xe4HT\xef\xb7\x89\xc8\xfexP=\xfauA\xc5\xc7l\xc9\x0f\xc3\xc0\xe0\x82\xbe\x8c\x8c\x18\x9c\xc3Da\xcd}goN\x82\xe5\xd0\x01\x83\x10$.\x1d;n\x04I\x0b\x0e\x9e\xe0b\x1e\xb0\xbb\xb4\xb8\x9e\xad\xfc\xfd\xc3\xc0\x10\xafFW\x9ai\x1c\xda\xa7\x01w\xb8\xba\xcc\xc4\x8b\x8e\xdd\x01.\x87\x0eh\xce\x1a\xf4s\xae\x94c\x19%J\xc5Z#\x08\xf8\x8f\xe7\xf9\x1c\xc3\xc5\xf2\x9fL]\xc5L@ \x97{Q\xde\xc6G\xf5A\xa8\xbb\x99S\x0b\x1b\xa5\x03\xda \x19\x8b\xf2\xcb\xd1\xeb\xf3\xd0\x02'Q\xeev}\xf0\x16\xd1\x0d\x9c\x89\x0e\x9c\x89\x04'}\x1cv\x93\xcfw\x0b\x82\xf1\xe1\x81\x1d\x8c\x92\x8c\xc6\x17\xe5\xa6\xa8}\x8f=\xf0\xc2^ \xefna]X\xf0 +y$\x9b{#\x86R\xd5y1`\"\xa9\x07\xf9-K\x93\x871S\xa7\xc6o\xa7\xf4\xcc?x\xa2\xd7\xf9i\x02\x18\xdc\xea\xd4D|\xa0v\x85t\x03\\\x16\x92\x10\x07'%![(\x8d\xdbnVB\xa125*{\x06%B>\x98\x07\xfe\xcfU\x9e}\xfe\xb4N?\xdf\xc4\xeb\xf43\xa6\x00\xfdx\xf1\x80\xf1\\_|\xb9\xd3\x8d\x10\xb2\xad9\xe1\xc3\xfd\xffxk\xc2\x81\xc1\xb4/1I\xa0\x06Q\xfe\x1eCi\xe2\xd5\x97\xf7\x00\x83\xa0\xe0M\xba]F\x16\xe6\x04\x99`\x02\xddkTS\xe3\xb3\x01\x13)#\xa3\x85\xbaR\xba9\xd8\xbc\x9b\x00\xcfti\xce\x95\xa5\x19GZ5S\x991+g\x9d9\xaa#i]\x0c3\x19\xeeW\xa4\xfc\x0b\x85\xf1\xd2\x8d\xcaiL\x85\x9d\xf1\x19i\x94ua6\xca2\x0db\xee0\x08Q\xb9e&\xeb\xd4\xfaJ\xdf:zAY\xf6\xb8\x88\x9b4x!\xe1\xc5\xf3\xb9\xb0\x8a\xff\xfc\x99\xb2#\xeb\xfc\x8a\xb4\x9f0\x06\xc5\x10\x99\xc6\xb8/;\xc6Z\xa6 ^\x0d\x82\x0f\xa7\xff\xf93\xd0\xb9\"$\xd7\x9b:\x16\x90D\xc9\xfb\xc6\xd1\xd4x=\xd8\xcf\x15o\xdfo\xe0AA\xd7\x07\x80|\x8a\xb7\x16\xbag/\x08)\x9a\xe7n8\xb4t\xc0\xa1\xaf\x8e\xc87Fcl\xb3\x87\x06\x1f\xe1\xa9\xbc\xd6Z\x92\x1aM\xaf\x7f\xb8y\x97'\x19\xa5\x08\xfd\x18\xb8\x00.n\x0f\x82\xbcw\xb2\x86\x86\xda\x88\xd1\xbf3\xff\xbas\xa3\x84\xbe\xecz1t\xeb\x7f\xce_\x1ej\x0d\x06\xae\x87\xec\x10N\xc4\xa7\xda\xdb\xdcO\xe26W\xf7\xf2T|\xaa\xb5~x>d\xc3p)>\xd5:\x0c>\x13o\x1f\xf7\x8d\x18\x9a+\xdc>4\xe3\xf9|2,'\x8b2(3\x81\x90\x9b\xe8>\x1d0\x1c\x1c\x92\x9b@\x91\x9d\xb4\x154\x08\xd6o\x89\x93\x85 $\xbaw\x94\x8a\xde\xe9|9a\xb6Ny\xfb !\xf5\xba\xab1S\xba\xe8\x1a'\x8a8\x899\x19\xca\x86\xa3\xe5\xdc\x06\xdd %\xad\xb7!L\x87\xb6\xa3\x89\x9a\x9b\x0e\x1ae=\xdb\x8a\x0b\xdd\x9a\xdaV\xf1\xaa!\xb6\xe6\x11f\xcc\xeb\xf85\xa9c\x1c\x1d\xa9\x00\x83}\xadI\x8d\xaa\xcd\xb5_3\xd5B\xc7\x8f\\\xd0\xfc\xcf\x9f[xEk^\xe9)\xd7U\xc8\x9b\x15\xe9l\xafl00\x9e\x85\xf5Y\x10\xde\xf1\xc8m\xc0\\v\x0e\xc7a<\xbb\xd0\x83`)A0\x1ee\x14\x06\xe0\xc2\xc8\x00h\x9f\x8a\xdd\xd7{\xa9a\xcf\x8a\xb8$Y\x8d\xa1\xba5<\xda\x10\x83\xd6\xf1\xf0\xac\xed\xf1\xaa\x95\x84\x9aG\x98B\x17\xf1\x95]\x9b0\xbf\x97\x92\xf9\xbd\x18aE\xfbE\x9f\x18\xd4\xc3\xa2s\xb0\xa5O\xf1\xba\xef\xfd\xa3\x01\xc6\"\x8d\xeb\x9ad\x13\xd0\x04}Yl\xd2\xf4\xe6\x8d\x08g\x84s\x1e\xe1;\xbe\xf0g~\xea\x93\xae\xf6\x1a\xf4\xe3\xc8:\xddh<1\x93\xea]\x99\xaf\x93\x8a\x8c\x18D\xc1\xb5\x86s\x9f`,\x14\xa7\xb1p\xcf\xae7\xe4\xda\x117\x86\xe3\xa3\xf0\xa1\xe0}m\xa5U\xb5\x01\xb8\xa8\xdb`\x08\xcf\xc1U\xc4j&\xf7\xaeL\xd6I\x9d8kA\xdcg\xb9\xf9\xcdg\x99T\x7f\xa9\xf2\x8c\xcb`+\xdd\xfb\xe7L\xde\xed\x89i\x16\x84\x92jn!/\x9b\xb4\xdc`\x1a\x18\xefQ\xe3\x1b\x9fT\xaf\xb9&b\x02W\xba\xd7\xcf\xe6s\\\xb0\xa6\xdaZW\xed\x7f\x92\x8c\x94q\x9d\x97#\xe6\xf5\\\x92d\xe5\xfb\x97\xcd\xd7ns\x13\x1fL@\x93P \xa9\x18\xdb=\x81B\xf7\xf2\x84\xe5\xaeu\x1eq+x\n~\xdc\x1fc\xeb \x95\xdf\x15C\x1f\xa9\x0c\xfd\x9dRap#t\xa3\x8e}A\xae\xb4'\xdb~\xba?\x94fm\xf8\xd3'{\x03\x86M\xb6O\xb7\xcebw\xb0\xf7\x9d\xf9\xd3\xff`s*q\xbfw\x07\xfeJz>\x8c\xe5o\xe8;\xae\xe8k\x97\xbcv\xcfF]_\x9d\x850\xb8N\xea\xd5\xf3\x92\xccIV'qZ\xc11xI6K7s\x82&`U\xbc&\xf7Y\x9cx\x8d+\xb6`\x03\xc4z\xdb\x14yd@hB\xe7\xbe\x81Pm\"p\x9d9\xbd&`G]XML\x01\xecX\xf5\x1e\xb0\x8cyTA\x8d\x177,\xfc=\x9b\xd1\xb6&\x9a\xd0g\xc6\xcf\x06\xd2\x1b\xcd\x9a\xe5\x99h\"\x88\x01\x8aw\xaea\xe0@\x95c/\xf2\xb9>x\xa7.\xcb\xc9\xef\xcc\xbf~\x85\xdb\xbdd\xe8\xb2,\x1e\xf0\xe9]\xc7\x97,\xb7\xf2_N\xdf\xbe\x11N\xbd\xb3\x94\xc4\xe5\xf3x\xb6\"6\xbb\xd6**\xd2\xcd2\xc9\xaa\xa8$\x8bJ\xf9\xb0cB|\xeb\x9aQ\x1eT\xc2R\x9b\x17J\x10\x97z\x95\x18\x92\x99\x9c\xa0X\xd8\x19\xe0<\x9f\xe1\xf0X\x14]\x12\x84\xdd\x19,TX\xf8\xd7C\xeae\xddf2\x84;\x01\xd3f\xba0\xe0\x97~JB\x8c\x9a\xb6\x07m\xd0i\n\xeb \x01N\xd5\xb0cI\x81\x931MM\xd3X\x13\xf2>\x08\xf5\xdf\xad\xf5\xdf1\x9cN\x08~\xc7\x8f.$\xec\x85\xb6~\x9c\xa6o\x17A\xd8\x8d\xf9n\x06\xb55k\x9b\xbc\x11\x1a\xa6<\x17qE^\xe4\xb3 \x9clCi\xf8\xf0\x07IfW[\xa1\xe5\xbdE\xa1\x82\xfe\x8b\xa4\x9aQ1$c\xec\xaa\x86\xebmj\xf3\xd5y\x1d\xcf\xca\\\xcb?\x8b\xb2\xce\xe7$\x15\x94\x86W\xefGE\x01\x854\x9e\xbb\xe4E\x86\x8eos\xdc\xac]b\xf4mv\xd5\x1b&\xdb\xb8\x1d\x8b\xf2\xa5\xee\xc7\xa2\xb8\xba!\x8b\"\xcf\x8a\x9e\x07\x87\xc9\x16\xb4[\x98\xeb\xa0[\x8fc\x1c:D\x91#\xb48v\x882\xac\xf2\xe6\x8e\x1e\xe6f\xb4>\x1b\xa283D\x9d\x0f\x9c}8D1(\xd2\xfd\x00&0\xeb%\x13\xb3\x9d\xe6\xa0\x90^\xc2N\x083\x8b9\x94pl1\x1cd\x8bE\x92\xa2{W\xff~\xde\xc4\x8fT(\x8c\xbe\xee\xaa\x1d\xb0\x0b3\x17\x19R\xdc\xb1]\xd2\xa3E\xfa\xcak9\xc66}\xd1\xd7^\xf2\xa6U\xc2\xa5\xaf\x89\xf1\xe3\x9dy\xf9\x0b^\xdb\x91\x97?g\xebr\x99\x14B\x97\x87<\xa7\xbe\xf25\x8b\xe7U\xd7\x1a\x19\x1d\xb8\xc1\x13\x89\xf8Ibd\xfai\xad\x13tc\x0e\xb1E\xbc\xd5\xbe\xa6\xffl\x04\x9d\x0b1fN\xed\x97\x18\x91\xd1\xcck\x8c\xe03\x1cy\x8c\xdb\xc0?\xe1t\xbf\x9b\xfa\xbd\xcfZn8\xf7\xa8\xb5\xb4\xe2\xd2\xfc\xbe\xe6\x15K\xbbY\x19Rnf\xfe\xd6\xba\x83\x83\xbd\xad\x93\xbb?\xd9Z\xfe\xdfZ\xfa\x1f\x18\xabU\xf6W\xdf\xdc\xb9\x10a\xe2\xc8\x0d\xfaOy\xa2\x9b\xd9\x03TAE\xb3\xb8\xa87%9\xad\xe3\xd9\xe5\x872\x9e\x1186\xbd\xe1\x04\x9d\xfe\x1b\xcd\xf2\xac\xaa\xcb\xcd\x0c\xdd\xdf'\xecYEkR^C\xfan\x06\xec\x99\xe5\xaaA\x1fx+k\x05\xde*Y\xe0\xad\x92\x05\xde*ww\x03\xc8\xa6e;\xf0Vi\xe0\xacqpkRU\xf1\x92`\xae\xc6\xbd\xb3\x90\x99\xd0\xd4\xad\x93J\xa7l7\x11\x8c\xac\xb9\x8bW\x9dUC\xf5\x05\xcf\xedC\x8f`\xf5\xa9\x02:\xfai\xd8q\xa8\x1a\xad\xf5\xfb\xed\xf12\xa9^\x96\x84\xa47o\xe25\xb1\xe7w\x90\x86\xe4S\xd2\xf2\xc7\xd1\xae\x1d;\xc4\xa5\x0b\x9d\x91\x80\x97Q\x92\xcd\xc9\xa7\xb7\x0b\xca\xa5\xfc \xee\xefS\xda\x9d\xcb\x87Y\xf30q\x0d=)WZ4BX#}$\xb1\x12e\xf4i\xf2\x1a\xb9K\x17M?\xc7:\xb80 \x1dX\xe5\x85\xa0f5\x0b\xc1\x13\xe7\x05\xfe\x10\xf9\xf8^\xb4\xbf\x98\x89\x90\xb4\xd5\x83j\xb6\"\xeb\xb8\xfb\xb4\xd5\x88\xf2\xbc\xdd\x95\xda\x0c\xef\xe8\x946\xa7\x1f{\x82cg\xfd= \x9f\xe2u\x91\x12\xefl\x0c\xc6v\xc8\xf7\xc3/ \xc3\xadW\xff\x96*X$G\xc6\xedp\x07\n\xda\xfe6B\xf3\x86~03\n\x87\x8cG\xf9\xc3`\xef\x8c\x9c\xed \xc5T\xef3r%\x91>\xb9F\xab\x8f~'\x1d!TP\xdd~E\xb1g\x90r\x97\xa4\xca\xd3+\xe2w\xb5\x82\x96}[G\xf3\xa4\x8a/R\xc6]-\xe2\x19\xc1\x00Q\xdd1\x84\x18]\xfb\x92<+\x92\xeaC\xbc\x94\xd9C\xfd:\xd0G)\x1e\xa2A\xb34!\x99\\\xc1Nt\xb7\xdfL\xcbxh\xd62\xfah\xed\xffm\x80\x91\xe4\x1e\x05\xba\x8a\x82\xa1\xd4\xa7\xf3\xa9\xc4[\xad\xb7A\x8a\xbb\xf9;\x03SY\xfa\xa9!\x8cb\xe6\xef?2\x06Q\\\x0cEP\xd4\x86\xb0[17\xf9'\x86\x00\x8a\x99\xff\xad\x8e#^s\xbe\xb7\x0d\xd8\x1ce\x0d48\x94\x82A\xae\x06CL\xe5\x8f\xe8\"\xc9\xe6~\xb6I\xd3\x90\x7f\x16\xf0X\x1f\x14\x9f1m\xad\xd2\x04\x7f|\xba\xb9\xa8KB\xdf\xce\xd5\xb7\xe4\x13\x99mj\xb4\xd0\x11\x7f\xd3\xc7\x9d\x18\x8fi\xebA\xabB\x13\xf01\xed=\xa4\x15\xdbJd\xe5g\xc82\x85\xb0\xb3\xe1\x87M\x92\xf2f\xae\xa2w\xcf\xde?{}\xf2\xe1\xe4\xfd\xf9\x0f?\xbd\xfa\xf1\xc5\xc9\xfbS\xd3f\x82#Xi_\xd0\x0f.h\x9b\xef\x99\xd4\x84\xed\xaa\x0f\x10r$-X\x9f\xfd\xdd\x90\x17\xaf\xe6\x13Xc\xe2\xfb\xf6\x86\xc0q+-\xc8\xac\xd1\xe2\xf1\xffY\xd8\x17\xfe\x00\x9d\xfc\x98 \xc5\xfe4\x99\x8e\xdao [\x14\xa5\xbd\xcbm\x17o*n\x0d \x84`\x1d(.\xe8y4\x96fe/l\xf4R\xc8\xc3xt\xef{\x83\xbe\xbb\x94\x08WRi\xcf\x02\x88\xd7\x06\xed/\x89Vy\x85\xbe\xba>\xff\xf3\x082\xfc#@ 3I\x80\xbf\x17\xbf\x8e`\xca\xc5\xdcY\x9e\xca\xe8(\xde\x84\x8a\x13^p\x86_^\xc4\x15y\x17\xd7+\xfe\xa9\xfcy\x04T\xba\xb3/\x80\xaa\x03\xc9\xc7\n\xca\x16e\xd3\xde\x80\xd01\xfc\xe9\xfe\x17\x98\xb8l\xadW{\xb2\xf7h\xdbO\x0f\x1fn\xad\x1f{\xb27` \xf4\xef%\x9a\xa9\xbf\xee\x9c\x1bG\x9bdv\x01\x89\xb8I \xd5\xeb\xb8\x18\x08.\x9e\xc3@\x84\xf0d\xc8\x1dX\x1a\x0chu\xbe\x9b![\x83j\xc8W8\x15\xedj\x87$\x82\xa1\x1fj\x9d\x85\x17C\x9e\xc42C\xa86h\xb4\xe0\xe5\x0f\xf6\x86\xdc\x81\x87Y2E\x14\xbd\xf6I@E\xc1\x02\x8d\xb6\xad\xaa\x1a\x11n\xfdP+5\x89x\xeb\xda\x81\x8b8\xda\x87\xda\xb7\"\x8e\xf6Cm\xc3\"\x8e\xf6C\xed2 o\xf0\x87Z\xafm\xe1\x0e\xfeP\xeb\x98\xed\x94\x08A\xb9\x00\x1e<\x80;\xf9\xb5\x98\x98K\x82^.\x12\xf6b\x98\xcdd,\x92g\xf1'\x99\x93\x8b\xcd\xf2GrE(\xe7\x98d\x8b\xdcR_\xde\xfaO-\xael\xac\xe2\x9f\x93\xaa\xce\xcb\x1b\xb3\xd5\x9a(\x8cy\xb07+|s\x1d\xaa\x16\xcc:|.Y:\xdb\x07U\x1dSi\xc46\xd4\xc2\xb5\xbd\xc6\x0c\xc3\xd2\"\xaf\xf8\xa1$d\x82\x9b\xea\xdc,4\xa9\xa5Z\xe5\xd7/\xe8\x02\x9a31\x89\x12\xa7\xa93\x1c\xd8\xd2Q2M\xa5 FY-h\x91&\x17\xafI\xbd\xca\xe7\xd5\xa4\x8b\xab\x9dd0\x14u\x035\x10\xbcu\xdc\x1d\xc6\\\x93RJ\x14\xca\xc1\x04\xfc\x06eI$\xb7w\xbe$5S\x16\xf0\xceE\x05n\xf3\xad\xd6\xe3\x8f\xfa\xd5Wq\xf5~\x93\xc9\xaa\xecg\xbf\xdau\x19\x17\x05\x99\xbfk\xce&\xfaT\x98\xfa\xac\xe3\xc2\x97\xd5X\x1d\xa5\x89@\x84\xe4\x91\xc0\x89\x1a\x13j\xd1\x01\xc7>fD\xd4T\x8c\xe7s\x7fz\x166\x1cp`\xf9\x80\xe3\\\xf3\x11\x7f \xbf\xdb\x14\xf3\xb8&\x1c\xec\xbe\xda\x94\xde\xd2`\xd0\x11\x87\"\xc1\xbcA\x02\x12\xc2\xd4L\xbd.\xc9\xcd\x04<\xa4L\x03h\xc7Y\x03\xbb\xee@\x14\xe4\xef\xe94\x1a\x9a\xc7\x8c\xf5m\x1f\x82z\x9bV\x87Z-1\xbbBc\x17j\x19\xaa\x8c\x8f!\x83\xfb\xb0\x0f\x13\xd8\x0bBd?\xf6\x9fB\x0e\xdfC\xf6\x14\xf2\xdd\xdd\x00\xcai\x8e73\xadK\xb6\xdc\xc1%\x17\xdd\xbfy\x94\x95 J\xf3e\x13\x86Jc\xbd\xa1\x16\xb39\x8b\xc1Fd\xe8\x90a\xcbtE\xca\x8b\xbc\x1a\x8a\x04\xb1\xd5B\xc9v\x99\xf3_{\xd9l\x0d\xc0\xbf\xcf\x82M\xbd)\x06\xce\x84]\xf0\xce(C\x7ff\x8b\xca&\xcaWX\xcb\x86*\x8dYNKx\x05P\x04dAE\\lk\xd4\x827\xb9\x83*\x13Qr\x83\x08\xd0-B\xfa\x99*\xf4\x99\x9ex\x98F\xb8d\xd70h\xf4\xde\xab\x10\xc0\x04t\x04\xda\xc7\xb0m9\xbf\xc9Qk0\xe9G\xc4\xab\xca\xad\xdcu\xb7\\m\x93P[\x14>\xd1\x9d^\x889\xcc\xc5G\xaeHy3\xce\xb1Y-R\x86<\xe2I\x98\x9d\xbe4$\x1bkU\xb1o*\xde\xb7T\xd4tL-K?\x0f\xc1\x988\xb1[0\x16D\x08\xb3\x10\x16!\x14\xe8\x14\xbf\na\x8d\xee\xab7\xf6\xb1\x80n\x85p\x1a\xc2\xf3\x10.Cx\x16\xc2\xdb\x10\xde\xb9A\xbe[,+\x11o;~\xd0\xadL,V&\xdeje\xbae\xdb\x95\xea\x16\xcch\xdd\xa7A\xf9\xa8\x00\x16C%\x96\xf9r\xb6[\xa4nq\x0fk1T\xec!*l\x85\xa5b\xb8$7x\xd3\xbf\x98.T#\x9a;\x07\xde\xc3\xff,\xe0\xf1\x9d\xd7L\x0f\xe3D\xe3\xd9\xe9\xa3>\xf9\x92\xdc \x0d1%.u-,\xe2\xff\x97o\x93f\xa4\x8f\xbfl@\xe0\x96\x11\xc4V\\\x93H\xd9\n\x9a\x89)\x98\x1b\xa2\xe2m1\x9d\x9f\x85\xa8G[H\xab+\xd5l*\x08Q\x8d\xa6>\xc2\x93\x1dC\xa9\xcc\xf1\xcfu\x88\x87B\xa2\x0dD1\x9b\xe6\xd17\xdf\x94dq\xc6\xb2\x95\xee\xec\x85\xa8=\xdb\xd9gf\xbf\"\xed\x91\xa4\x99\xfb\x0fC\xb4\x0d\xee\xb8\xbe\xd0\x9fU\xf3\xd3\x98 \xd3\xb58\xa7C\xb2\x15J\x1c0\xce\xc5'8\x82\x13\xc4\x1d?\x08\xa2y\x9e91r.Eb\xe4\xe1\x7f\x18m\xc0\xe8&p\x04\x9fD\x10\xf9\xe7p\x04\xf9\xf4\xf4,\xc4\xf8\x95\x0b!\xf7\x9c\x06!\x86\xac\xd4\x9c^\xcf\x83\x10\xdeb\x96\x17\xc4\xb2\x10\x06\xd3\xfa\x8e)\xf1\xd8\x84H\xb6\xf2\xaf\x04\xf5\x9dg\xff\x0d&K\x91^W:\xb2\xf6\x16\xe5\xb6\xd9\xf4\xed\x19\xd2\xb4\x80Y\xb8\xa5d\x19\xd7\xe4\xff$$\x9d\xfb\xa5\xcf\xd8\xd6\"\x08\xc1\xab\xf7\xbc\x10\x0e\x1e\xdd\x05\xcdr\xc9\x81e+\x18x\x9aJ{\xa7,d\x0c=\x83\xef\x1c\x1f\x0e-)\xb8\\\xcb\xbf\n>P\xa0\xbd\xc3\xcc\x06\x19\x8b\xd0\x96a$\xbbw\xff\x0d8K\xe9r\x80\x87\xfb\n\x0b\xf8\x1c%\xbcK\xcc\xddZ\xdc\xc5\xfe8tt\x15\x1c*\x82Q\x89\x9b\xf4\x8b_62\xb8CV\xf0\xf0Ny\\\xc7\xcc\xaaC\xe5\xce&v\x07\x94M\xb2\x91\x87\x98\xb3\x153\x0b\xc6\"c\xde\xc3\x80\xf3\x9e{\x8c\xf7\x8c\xadi\x02m\x85\xc9\x1cw \x9b\xcbq?Ty\xe1\x87\xfb!\xec\\P2s\x12\xf1]\xa4\xfc\xddM\xc05\xb68\xa5Hs)\x9426c>\x0ca\xe7\xfc\xce\x89\xe2\xc3;\xd8\x81\xf0/D\x14Y\xde\xbd\xeb/\x9b\x14[\xc1;\xd86\x92D/\x92,\xa9V\xfe\xc3\xc3;\xc1-\x87D\x89\xb6\xd2\x1b\xd9\xde\x9d\x8c\xec\xf1\x97\x8dl\x1b?sS\x913t\xf4?7\x95\xedp\xf26\x84\xd8\x9e\x98\xd0V\xa6Tj\xa7$\x97\x92\xaf\x87\x8f\x1dB\x1a\x9b\xca\x94\xd2\xbc\x10\xa9\xc8\xc3\xef\xdc\xee\x0e\xba\xc5\x10\x15r\xa8\xdc\xb2\xc4\xf1\x9d\x8b\x83\x9b D\x9b+\x0c\xc9\xcb\xcf\x8d\x82\xeb.\xe6\x8a\xeeBj\xe2\x1f\x852f\xac\xa2\xba\xc8uw\xf8\xdd8mc\xf5\x19\x88\x81[`1\xa5\xd5\x18\x84x\x8d\x1e\x02w\xa1\xae(%\x97\xb4\xa5zb;\x9a<\x1e\xdf\xf9N[\xc2\x11\xac\x85\xc6\xa1\xec\x88m7\xfeR\xbcZ\xf28\xa3K)\xc1\xed\xefo\xb3J\xfb[p\xa4\x02\xdd$l\xb7\xd0En\xc1\x97\xb1\xf1n\xc1`\xcaq\x1el\xc1Pn=\xd0-N>\xb9W\xf7\x1fQ\xe8\xb2\xd4\xd3\x9cA|\x14\xf0\xfd\xbd\xc7\xf6w9\x9a?d\x12\xfa\x16\xfc\xa0\x1c\xd6\x81JO\x0e(\xff\xb7\xa0<\xdfJ\xe1\xffV[\xf2\x7f\xce\x99\xc4\xbb\x85%3\x16c\xa2\xfc\xdd\xd6\xf7}\xe5\x97j\x8b~-Z\xc1\xf8\xb3\xf9\xb8An\xad\xa0\x91\xee\x8c\x9c\xcb9\x18\xcb\x7f9\xe73\xef\x96^\xcfc\xf9+\xd6\xf3\xc8\x93\xe8K\xf8'9\xe2\x91\xfc\x92\x1b\x0e\xdc\x86P\x8e\xe7\x87\xa6\x8fB$(t\xf7\x1e\x8ca\x7f\xa6\x07\xc8\xee\xd0Mu\xe0\xc8\xee8\xb07\x16k\x8a[\x9f\x04}\x03\xe2\x9c\x99\x1d\x96\x81\xcd\x8a\x18\xa4=\xe8\x9bxM&\xc0\xa3.|\xfe<\x14~Q\x94V\xe8Y\x95!\x92\x8f\xfd\xdc2\xfa\xd1Q\x8d\xecVN\x94(\x8d\xb6r\xb2\xd1@\xbbw\x9b(\x8aE\xe4\xaam\x16\xdb1\x1eU\xbc?\x9c\xcc\n\xa4\xf7\xd6\x92\xd4\x82\xd3\xac^\xe6%k\xce\xaf\xd5\x8c\xae\xbf\x0d\xd0U\x83\xec;\x84\xbd4\xec\xecX|\xb72\xd8J\xc9K`\xa1\x0c\xb9\xd2\xfb\xcc-u\xa7Z$\xe8q\xe8\x16\xe0~\x05\xe8. \xc7hno?\x02\xb8\xd6\xf9\xa9Q\x13\"\xd9\x11\xa5\x06>\xb1\x1c\x1f\xaa\xd7n\xcb\x1f`Z\xf3\xfc3_\x11\x14\xef7\xd9\xf3|\x93\x0de\xb0\x1a\x0d\x0buB]\x98\xfbDl\xb0\xaf8)\xde\xd7\x87d\xc8 \x7f\xf4\xb4\xf4K\xdc\xcc\xcbm\x951\xe2\xcf\xb4V\xedeX\xf2\xaa\xaf\x08\x0fA\xe7^es\xf2\xe9W\x03\xc9\x87\xa4\xc0\xe4\xcbj\xe7N0\xf2\xb2\xcd\xfa\x82\x94\x1e\xec4\xbe\xd9p\x0c\xf7\xf7\xc1\x94&\x0d\xee\x04Lt\xb7\xde%t$\xbdkX\x83\xbb\x1f=w@\xd8\x96\xae9\xd8\xc8\xb6\xcc\x92\xc7\x916_C\xd4\xb2\xb3\xb6\xbf\x87\xf2\x9c\xa7TG\x1f\x8c\xa1x\x91_\x08+v\x80}E(\x0d\x03\xa5a\xf1\xda\xe9;\xe8f\xe1y&F\x1e\xach\x8d\xd7\x0b\xec\x1f@\xc6\xbd\xcd\x19Dm\x8bE\x0bf\xd8\x19NY\xa1\x16\xb4\x9b\xd0\x1aqKV\x025\x82\x19sK\xf0\xbb+\x00\xde\xff\xcck\x88!\xcb\xb3\xfb,\x0f0\xf3\x1b\xf3Bp\x19-\xf0!d\x91\xf4\xf1b\xb1\x83\x1b?.1\xf5\xb0\xc5Ys\x1e\xcb'2=\x91\xf0\xd5\xec\xb19\xcd\xf7l\"\xad\xf7\x1fV$s\x82+h\x8cM\xd5\\\x1a\x1a\x88U\xd2\xcd\xca'\\\xed&\x86\xbb]\x7f\xe2\x14\xd0\xf4\xc5\x96E\xb2\xc3\xba\xcc\x15\xdd\xe2\x96\x93D-\xfd\x8c\xc7]\xfc\xb463,\xb0~\x0d\x8e\xbc\x03\x991D\xc3\x06\x97v\xe6\xebvL\x16\xb1\xd2hO\xd1qJP^!\x19\xd5\x19\xe3\x88Z\\\xf5\xae\xc8\xb4\xbf\xdc6xdA$q\xba+\xfesM\xe2)\xe6BW\xc75\xc1\xf0\xbev\x14p\x0c\x1ebY\xe1\xe1\x11\xb3\xc0\x14\xd8\xaet\x81mvp3dJ\xa7\xbf\x02\xb2\xb0\\\xc6\xdb\npV\x84iq[]:\xd5\xc4\x07\xb4\x81\xe8{\xd8\x13!n8U\xfeP&d\x0eu\xce\xf3;C\xdc\xf6\n\x86z\x15\xd7\x90T\xd9\x1fj\xa8W\xa4$;\x9e\x0c\xb7\xd9\x1dFU\xa4 \x95\x18C\xd8\xff\n\x00\xee\x11\xdf\xaf\x05^'>\xb5\xd9c\xfc\xafN\x14\x19''!\x11eN\xb7M]\xb6\x154S\xcd\xac\x95m\xfb\x070\xbe\x81\x06\x8d\xd9\xfe\xe9x\xbb\xda\xdc(\x03~\x890\x0e \xee\xfdkB\xa5\xaa\xe5k\x1c\x07\xaa\xd2h\x0c\xee90\x90\x8d\x97\x18\xa0\xe6p/\xd4\x0bBH\xe1\x04\x15h\xa8\x1c\x93'\x05\x95k\x9eW\xb8\x1f-\x01\xd8\xbf\x00\x1c\xcf7eI\xb2\xad\xa0\xe2\x08\x11!w\xe8\xb4u\xfc\x15\x1f\x04\x7f\xfa\x95tG\xfd\xfeG\xccu\x14\xf5\x89\xf4\x92\xbb\x95\xb6\x9b\x00\xe6\xd7\xb0\xfbU\xe8q\x17\xf4#\x00b\x83\x87:\x97\x99\xda\xc7W\x99\x05')o\x17\x1fn\x8aQ:\x80\x11\x1b[\xd8<|\xa5\x8d\xf8cr1b\xe0\x8e\x83F\xf07a+\xee~\xe0\xe7K\xf25t\x8f\x0d\xcb\x8a\xc9\xf1\xdb\xdc\xeaW\x80\xbf\x12\x14\xe3+\xcc\x86m\x82&\xfc \x9d\xd4\x90\xb8\xb4\xf54\xaa\xadf\xe1\xbe\x07z\x13\xa9\xe8D\xbe\xce\xd9\xc4\x83\x8f\x8c\x99\xc8\x98Y\xf44\xe8\xc6\xc3\x08\xfe\x04>;\xd1\xbf\xc6,gi\x9e\x8d\xa2X\x8e\x93\xfc\xcb\xe9\xdb7<@\x1feMsE6\xfd\x1a\xe7\xab\x88\x8d5b&\xb6\x89H\x97lb\x9f4-\x84 \xce-\x81W\x93\xcc\x97k.\xda\xac( a\xfbH\x14\xd09\xfe\xedW\xc6\x99sM\x19\xc0\xba\xb9\xcf\xb5\x19\xc9\xa0R\xcf\xc9\x11_D\x8ck:h\xf1\xec\x0e\xc2\x06\xed+\x97\xda\xa8\xdc1\xb8v\xb7\x88}i\x8a\xb0\xa6+}\xe9\xe4\xeb\xf6f\x87\x85\x88\x96\xed6\n5\xb6+\x9ekN_\x89\x00b\xf8\x1d\xfba\xfd\xce=\xca\x04\x1b\x8d\xaa\x8a\xf5\x13\x11\x0eI\xa0I\xa3\x9a\x0dB\xf5\x9e\x99\x07\xb3M\xbed\x131]0\xbbV@\x9a\x8c\x11C\xd5\xdfx\xd3\x16\xb6\x1f\xb2\x0c\x1e~\xef\x19Rl\xca8k\xea\xff \xf6\xf7\xb4\xd7\xe5\xd6\x98\xbc\xa2\xb0\xf5\xcb\\\x17O,\x9cT\x99r?P\x99\xf4\xc3\xf7\xfeF\xfepE\xa0$\xf1lE\xe6\x10\xc3*.\xe7\x90&\xeb\xa4\x86|A\xc7\xcbMT\xa0\xdcd\x95g\xa3V\x0eD\xa2DW\xb9>\x87.5\x93zK\x03\x97}&\x92\x08i\x9b\x19oy\x00\xe3\xac\x0f\xc0\x01\x00\x00\xd0_\xfe8M\xfd\xcd\x97\x8e\x0fi\xa0\x88\x97\x13\x82\x0cmfm\xe56p\xcdN\xd0-\xdb\x91\xb4/\xd8\xa9\xbc\xc3Q\x03\xcd:Xv\x04\xa5}\x89\xc4\xb9\x9aE\x1a]\x85o \xab'J\x8e\x0dtu-p\x1f\x1cla\xc7]\xa6\x95\xaa\xd9\x97\x0bPD\x11\x87\xc7P&_]\x89\x99\xf1\xfe\xa8o6\x8e\xd1\xa3\xd4\xe2\x0e\x06Qdh\xb2\x8a\x99 w\\\x08J\xbf\x0e\xd9\xaa\xfe\x98\\\xf8A\x10<\x85\x1d\x9fB\xc0\xaf0\xa9A\xcb\x8c\xff)\x87M\x00\xc4\xaf\xf8\xe5\x87\xf3`\xc6\xdft\x89\x12s\xcbi\n0;\xc5\x11\xe5\x16\x16I\x16\xa7\xe9X\x80\x8d\x071-; %\xd7\x85bL]Hc\xeaQ\x8dm;l\x10\xeer\x01\xb70\xde\x8c\xfa\xdc\xcd\x86\x15\x9ck\xde\xb2;p\xd2G0\xeb\xe7\x12Q\xac\xe2\xb0(\xed+Q\x8ck\xeeO-\x91A\x9d\x8cQEa'\xfe\x04\xfaY\xfeu\xe56p\xb1\xa4\x1d\xb9\xceRTj\x99K\x95cf\xd12!2%\xec\xee\x16\x97\xf8i\xd6\x1a\xd2,\xc0\xf1`\xbc\x1dxo\x90\x8d1&}\xef\xd5\xad\xeel:1J\x07%YT\x13X\x0b4\xd1\xd3sL\xa1<\x81\xe5p\xad&\x05\xd7\x04n,Ue\x04\x9c \\\x88\xaa\xfd\xa9\xb4O 5\x0c\xf9u;By\x93ay\\<\xf8\xc3\x87\x03\xf1\xe0\x87?=x\xfc\xdd\xb6\x9f>\xde:\xa5\xe4\xc1\xf6\x91\xef\xf7\xf7\xb6\xfdt\xff\xbb\xed\x13\x04\xec\x7fIF\xca\xd6+\xa9\x94\xf9\x8d\xe2\xed\xeb\x07\x93\x1b\x95\x98,2LT\x93\x8aY5\xe9\x07\x80\xb5jq\x80Q\x99\xecm\xebV\x9d\xe5Z\x8a\xa1$i\\'W\x04~z\xffc\x08\xd7I\xbd\xca75\xac\xe2\xab$[B\x0c\"\x13E\x84Y\xbe'\xf0\x07\x19\xf4\xf4\x0f\xf2\x1d\x7fZ\xe3S].Bh\xa0\xf8\xa9'\x97\xd6Z\xf5w\x9f2\x89ep\x82^b\x84\x9e \x9f\x0c \xcf\xf3M:\x87,\xaf%DJ\xb2 %\xc9f\x04.\xc8,\xa6X\x93/&\x80\xb3\x16\xb92\x11\xc3:c6\x0d$\x1e\xc4)\x1f!\xe9\x05h\xa3P\xfb\xde\xef=\xb7V7\xc6\xe9 \x9b\xbfwS\xa2\x89o\x8b\xda\x084\xe09\xd5\x98\x9eeA0\xc0\xb1 \xab\x80\x14\x99\x90\xe1U\xa6\x0c\xc2E\xc3 ,{\x8b>\xec\xbfr~\xce\x15\xabz\x1eA\x97\x91\xc6\xca\x10\xf3\x91\xa9C\xe1v\x81\xee\xb8W\xf9\xa4+\xce\xda\xfaKM\xf8\xed\xb6\xd0\x95\xbe\x03!B\xeaWY\x88\xcep\x0c\xbae\xae\x038\x86\x1a&\xd0_\x96:\x80 \xf8\xb4U8\x82W,G\xf8_N\xdf\xbe\xe9\xcf\xdb\xc8O\xf2\xcey\x1b\xb5>U`\x88\xef\xdd@\x90Zq}\xa6\xbd\x85f\x9a7.\x17\x7f\x0f\xfbR5V\xf7\xeb\n\xdc>\xed\xde\xd1\xe91\x1d\xcd\x18\x9b\xac\xe4e\x87\xca\xf6\x89J\x91'YMJNG\xe8\x9e\x87yN*\xacC>%U\x0dI\x06\xf3|\x86\xa1\xa9\xb5\xf9Th\x91\xadh\xce\x14\xcd(\xf9t\xbb\xc9\x16\xf5P\x9e\xe9\x11\xad\x95\xfe\xb21\xf9 \xea\x8c?\xdc\x14\x84\xeb\xfbN>\x15dV\xa3\xaa\x8f}\x14\xc2\x12\xadi\xe9\xbcU\x90\xd1\xc3\xd3\xdbd,\xaf\xcc\xdc\x03\x96|\xe0\xaau\xa3c\x9e\x92\xf7\x80Y(\x92\xe9\xde\x99\xbc!!Q\xb5\xb9\xa8\xea\x12s\xc1\x80\xe7\xc9~\xa6g0\xc1\x0cXHb\x1fx\x01\xd3\x86\xb9a\xdfb\x90~\xeb@\xc3\xd9\x82\x13\x89J\x9b\x8cT\xb3\xb8 >\x91\xc9\x9f\x1e\xfc\xd7\xfe\x83e\x88\xb9\x9d\x94g{\xf8\xec\xbf\xbazP\xd3\xd0\x8a\xc1\xa15\xfdkzg\x1d\xed\xa9\xbd\x7f|\xc0\x1e\xee\xbbv?\x1fdP~\xf6\xeb\xc6\xa4wG\xa3\x95\x11\x9b\x97D\xb3U\\>\xab\xfdZ\xda\x0b\xe9\xe9\n\xcb^\x86\xa6C\xf7u\x1e\xfe\xbc/\x8e_j\xdac\x8a!;\x98\xb9^ \x0e\xfb\xf1{\xfe\x03k\xd0_;t3;M~%\xf8\xcc\x10\xb4:1q\x0d\xf5\x01\xef\xc5K\xcdpsL\xf5\x95\xf3\xc0\x15\x1f\xf0\xda\xb9\x0cA\x1b2Sh\xd2\xec\xa7\x0e\xf4\x01\xc1)\xe01\xdd\x12\x13\x84\x00\xb22q\xe1\x17A\x93@Z\xdb\xda\xad\x9f\x19V#\x86#\xf0\xf1\xee\xc2\xfb\xbe*\xc8l\x1d\x17\xf7);\xf8'/\xa0\xd4\xed\xf7\xd8\x89\x9ep\xd6p\x84\xce\xfc\x1d\xdb\x81\xe9Y\x80i\xcf^\xe43\x0cZ\xea'\x98\xca\xd0\x86B\x1b8\x02\xcf3Q\xffq\x19\xadi[\x1b:|\x84Q\x81\xb7\xaa\xf9t\x83$\x86\xfe\xef\xda\x9c\xd2$n\x92\x18c\xb6\xcf\xfd\xd8h\xe8\xa1\xe3h\x86\xe7\x9eO\x13\xbc\"\xc2\xff\xb9\x93\n\xbf\x7f\x89\xbb\xfbW\xfdu\xe7 \xbd\xdaC\xa3Kr5\x94\x93k=\x94Xk9\x98\xb0K\xa6\x82\xd2~{1\x94X\xeb\x9c%\xba\xd5e\xb3\xbd\x16}jSH\x9d\x88>\xb5\xcd~\x1aL\xf2{:\x94\x13\xeb\xb9\x18\xae\x16J\x97B&\xef\xbfz\xc6\xd3\xea\xbf'\xcb\x93O\x85\xef\xfd\xdd\x9f\xc6\xf7\xffy\xb6;y\xf0\xe0\xf3\x83\x07\x81\x17\x82\x97x\x9a\xef\xder}\xf5\xf3\xe6\x8c\xf5(k\xf7\x9e,\xf0\xf0\xf6\xec2\xb4(x\x03&2M\xe2\xc7,_\x7f\x87\xebGk\x00\xe0\x17\x9c:\x04\xef\x0f\xf2\x1d#\x87\xbd\xe7\x1f\xf8\xa4\x07\x94?\xaf\x8d\x8a(f\xcd\xf1MI\x16\x06K\x0e\xa1\x91\xec\xce\xdf@\xdbE\xc1\x8b\x00\xbc\x86a\xa7\xd2^\x08\xda\x83I\x14\x94\xc8i\xad\xcb(\xa9^\x96\x84\xa47o\xe25\x99\x07~e\x0d\xeeN\xfb\xc2\xb4sJ\xf6#?\x93\x14\xd3~1\xaag\xe2\xda\xc20\x05\xd1\x04\xd6\x9b\xaa\x86\x0b\"Y8\xf0)\x9a\xdc\x7fO\x16\x81\x913U\x0bk\xc5\xe1\xfe\x98\x8f}\x02\x0e\xd9A\x16\x1b\xbc\xa3_\xd9,\xcamW\xa4\x14\x8e\x0b8B\xb1\xdc\xdek\x81\xa1\xb7\xf7\x1c\"E`\xd8\xee)\xf3\x9b\xb5en\xa3\xe5\xca\xf1\xbe\xca\xed\x02\x85\xb6\x96\xd2\xae\x0b8\x86\xdc/BH\xa9 gL.+\xca\xb8\xdb\x01\x8e, =-\xec\xb5A\x15X\xe6v\x88\xc0\x18\xd4\x01\x8e>\x0c%\xae\xdc>p\xc5!\xd0\x1f\xc8\xad\xd7V$[6\x91\xc7\xac\x9d\xdd8\"\x03\x12\x90\x95?\x0f\xe1*\x84\n\xcd\xbb\x1c\x16\x029\xa1M\x9aR\xb6\xeb\n\x8e\xc1\xbfA\x91y.\xfc\x07\x19\x9f\xe8/\x05u\xf1o\x02\xc62/9\xd1\x1dV\x93q\x99\xf6_\x06%\\)\n\x8c\xc6\x88\x80\xee\xa9%OhD\xe9(Bh\xe3_\x850\x0f\x82\x88+\xad\xe0\x18\x96\xf2\xef ,\xbb&]N[\x0ddl\xa3\x11\xbb\x0d\xb6\x00/\x8c\x051l\x01f\x18 j\xb0o@\xe0j\xa4\xa5\xc6\xc5\x98\xd3\xa9\xe9\xa9\xa2\xdeZ\xe7W\x84\n3\xb0t\xc8\xfaE\xf7\xefEK\x1b$\xa4\xe4\n\xd3\xdf\xb8-\xc77\x1c\xae\xd6\xca\xb63\x0b\x84\xc6\x89\xee\xca+\x14R\xd3f\x96\x17\xa12N\x91\x1b\xd0\x9acT\x14\xb9\x94W\xd6\xea\xb7\x81\x03\xe8\xdc\xce+\x10\xc4l\x9c\xc5\xb6Z\x84\xfa@\xab\x005\x15iST\xc4\xf5**\xc9|3#\xfe\xd6C\x00\xf52\x96ytNk\xbc:\x9d\xd6nA\xa2h\xc1\x8c\xfd\xee\xfb\x08F$\xa55\x15>hU7\xcc\x9d\xe4\xb9\xb2$S\xb5'\x7f:\x82=\xd4U\xec\x85\xcdmn\xe0\xd7AG\x1cv\xf2\xa4\xd3\x15q\xb1\xe3\xd7\xd3\xcc\xe1\xb2\xbf[\x86\xe2\xf2\xe8\xca\xad_\x8f1\xb7\xb9\xf5K\xe1\xa5q\xd1\x88\xe4\x17\xd6o\xed7\x12\xdd\"p\xc9\xc6\xb5\x81\x95\x011\xbf5\\\xf8\xf7\x9ejd\xb0W\\\x80T$\xbc\xd7&23\xcfg\xcf\xe3\xd9\x8aL\xe0\x9d\x1e\xb5\xe3\x8b*O75I\x167\x13\xc8\xf5uf)\x89K\xde\x8c\x9b\xd2\x85\xf33;\\\xf1;')\xa9 \xbb\x8a\x98t\xf1\xf7\xdd6\x91-\x94\x16\xcd 6\xa8x\xf4\x93TE\xf0 \xbc\xd5W\xba.\xe3\x82\xd7H\xf45\x96\xa4F2n0\xbfG\xdd\xf7\x04b\xfd[\xf2\xa9.\xe3Y\xfd\xb2\xcc\xd7\xd8\xc8F_M\xde\x06\xb9.\x87r\x19x\xce\xee\x920\x81\xec0\x88W$\x9e\xa3\xa1\x87}\xd3<\x9b\xcdHQO\xc0\x8b\x8b\"Mfh\x8f\xf3\xe0\xe7*\xcfBP\x9f\xdc\xc4\xeb\xd4\x1b\xde/\xc3\xf47\xcd\xe3\xf9)\xdaF\xef\x98\xe3\xaf\xdd:\xdf\x0c\x8a\"\xe8^\x84G\xf6\x80\x91\xce\xb6-_K\x02_\xc5\x0b\xf2c\x1e\xcf\x07=\xb4F\xe1-\xc7\x19#\x0fH\x97\xe1\x1dcF?\xe4\xe8\xa42\x81\x99\xbe\xaa\xb8\x1f\xf9\x8b\xfa\xc9%\xc9&\xb0\xe8\xd3\xa5\xa0k\xb9\xc3\xa7\x08G\xf0\xaa\xaf\x8a\xfc\xd9\xaa4\x17*V\xa2^\x0f\x10\xf5z\xa0cp\xd0\xeeD5J\xa9{\xe6FcMZ\x1enm\x0ds\xf0\xed\xf6\x9f>\xfa\x02C\x1a\xf5\xcd\xaf\xa0Z.\xad\xeb \xdb\x1a\xec\xc0\xb0\xd1\x0e\xe8\x8fI\x93\xc29\x17\n\\3\xba\xf6\x87\xc1\x14\x95h\x12\xa7Q!\x99\xb5\x94 ^1\xe8\xa7\x85lv\x1c\xadI\x1dS\xa4\xe6\x7f\xb24\\6\xe5\xe6f\x1b\xe5f\xdeUnn\xacZ\nf\xd0\xd4Isk\xfb\x08T\x0dl\xfb\x16\x1a!\xd8\xe813\x88i\x9b&\xc3$\xb5\x08;\x8fH\x88\xabL\xb1m\x89\x003\xf8Vhn],\xdag\x98\xee\x04\xb7\xc3\xf0X7[\xf0.\x80\x1d`B,8\x82Y\xcf\xfe\xa2[\xa8x\xcd\xf8\x1d\xfc\xc0\xdfca\xd89\xfb\xf4\xcbm\x08\xb3 \x88\x10\xd6n:\xd7i\"\xe5\xe8M\x08\xbf\xdc\x062c6\xe9\xf8\xa78\nb\x887I;\xc4\x97\xfd+\xe0_624\xe5\xb8\xed\xb8A\x0b.\xa4\xa3\x8b\x81\xa0W]\x13\x89\x94`\xfeqH2h#*\x8b\xbdT\xb9\xe0)(\xe6\x1d\x1d\\\xb5\x9bU;\x9b\x18'\xd1\x9a\x94K\xf2\x82\x90\x82\xae\x98E`\xba\xb5\xc5n\xe2\xad.\x98\xac\xdci|\x16\x04!\xcc\x18]\xa2\x84J\xd6\xe2\xba\x9b\xa9D\x96M\x08\x1eV\xf3\x02\xfaM\x9fG\x10\xc5Y\xd6i=\xc1XTc\x0eu\xeb\x19\xd9z%e\xf7\xdf\xc8\xd8T\xfd\xf5+\x1c\xd8\xf9\xd0\xadl\xd2\\\x90\x8e?&\x1b\x9b\xf0Qgei9+{\xd9\xd6q\x1d\xec^\x82\xe2\xbc\xec8\xa6O\xcf\xec\xea\x9d\xfe\x1d\xa2E\x1c\xe9wC\xa9q\xd2\xb1]+\xa3\xaa \xb3\x10\xaa\xa1})e\x90\xfey\xe2@\x84\xdd\xb4}\x9bi}\xa6,h\x19\xc9\xa5{\x1d\xcf\xca\xdcO\xed\xa4e\x94.E\xe0]\xe3\x87j\x0bR\x03\x0d$\xf2\x0e9\x1dv\xec\x18P\xb4\x04\xea\x8a\x88s/\x0bac\x10\xb3\xb4O%!\xd64d5\\\xfdoJ\xf6oB\xc9\x9a\xa4\xcd\xa3(\x99i/\xd0\xd1\xc6z\x1aa\xda\x08\xd2\xb1qC\xd9\x122d\x06NK<\xdd\xb4w\xf4:\x9f\x93T\xc0\x9d\xedjZ\xc7\x80\xeaN\xbbY\xe5\xed\xed\xbbx\x14\xe3>~\xaf\xc5\xff\x8f\xef5\xfd`\xcc.*\xd2T@\xdf\xf3l\x95\xa4\xf3\x92d\x13]\x8cq\x16e\xb0v3BM\x86l\x95\xe4\xe1&b\"\xca`\x0b$*\xca\xbc\xce\xff\xca\x9fgp\x8c\xbbe\xd3\xde-\x99R\xab\x89P\x8a\xc6\xc4W\xec\x99\xbf\xa7\x04\x8c\x08|\x12\x89\x99i\x94\xcb\xc6\xd3T\xb5\x84e_Ok\xc3\xa5V\xab\n\x1cAB\x913\x13\xa3\xd1\xba\x19t=\xf9~u\xc2\x19\x0fY\xfcm\xf8\xcbC\xdd\xcbJ\x98\xd7i-\xe8RA\x90\xb5\x0d\xcfTM\x91 \xf2\xae\x17i\x9d\xb4\xf6\xcc\xb0M\x86o-\xf3\x9cR\xc1\xdc7\x9a\xba\x81\x8d\xe8t\x1c\xc9I\x08S\xf3hd\\\xac\x11\x81\x89\\\xb8\xb9\xabnP\xf5\xb8$\x19\xc6\xc2\xda\xb1\xa5\x1bB\x1b\x13[\xfb\xa0\x08\xc5dJ\xd4t\x03v\xd5\x08p\xa3\xe3L\xee\x00;K\x17O\xcb38\x86\xc4\xa7\x7f\x0821a\x8fq\xbd\xe8\x83\xc1V\xb8\xe7u\xe2\xcb\x85f\xcdl\xd2t@\x91\xae_\x7f{\xc0\xa9;\x8e;G\x17\xc5\x97\xb1;\xa7g\x81\xd6\x19FL\xccE\xed$\xd9\x04\x19\x15\x92\x81$S\xd3,*\x7fS\x9ei\xef)\xe4\xf0}c\x87~\xef\x1e\xf8\x0c\x03\xf2\xb3\x10|D\xb8\x86lN\xcb\xb3\xe0)\xe4\xbb\xbb\x01\x0b\x911--\xd7\xfbb\x1a\x18\xe0E\xa1\xd7_eu\xd8\x8e\x18\xb3F\x0e\xdb\xaeu\x03A\x945\x82cfi4Q\x9f\x1e\x888\xc9Hu\xd0\xafE\x11\x1cu6\x0dN\xfb\x12Ui\x8dA\xa8\x05\x0f@\xdd\xc9#6\xa4\x98j9\xcd\xd0\xa8\x9eE\x8e-Y\xfe\x85\x1c\xad\xd4\xd0\xe8?\x04\xfalxg*\xc4w\xf4V4\xfa\xb7\x9b\x99\xf7\xd9X\x06o\xf8\xd6\xe5p\xc0\xf1\xf9\xdf\x8b5T\x7f\xfd\n\xdc\x84\x10\xc3\x1e\x0e\x89aZnB\xf0!\xfbZ\x8b{\xc1\x88\xeck\xe5;\xc9\x89<2q\"\x99\xff\xed\x00\xf6\x0cr\"W<\x03Y\x87\x99\x94\xa2\x1bKs\xab\xf2*\x03\x9b\x1a\xb7%f\x0b\x9e\x85\xb0\x08\xa1\x08a\x1e\xc2\nMF\xd7h\xbdv\x03G\x10\x97Kt5T2m\x1d\xa0uYc@!\xabL\x0f\xe8!\xda\xfaI\xf9v\xfdn\x97Z\x141\xf6\xeb\xd29\xf2\x14\x9e.O\x9f\x06P]'L>\x14\xd9, \x86\xce\xb1\xd11LW\xe8\x90\xd5S(\xce\xe1\x08nx\\\x99\x93\xacNJ\xf2\xa1$\x84\xa5\x18\xbe\x11\x86\xf5,\xb50\xad\xf6\x8f\x0d\xa9\xeaWYM\xca\x19)\xea\xbcd\xc9\x86\xe9\x9b\xaa\xc8\xb3\x8a\xb4^\x15\xf8\xaa\xad\xe7b\xd9Jo4\xb22\xcbGl'\xd2\x80\xa10\xea\xd5\x8b\xa4\x9a\x95\xc9:\xc9X~\xbe\xcc\x8d{\x92\xa6~\x06+\x90n\xe9O\xd9x\x83\xdf-\x1a\x98L`\xe1\xf6m\x1bh\x13(\xdc>\xebCu\x02s\xeb\x97\xb7!\xda\xce3\xf6[\xa6\xbe9\xbd\x8e\x97KR\x06\x0e!\xf3\xa0 {h\xadKe\xb15\x86\xf2d\x8aY\"\xb2\xac~\x1bv%\x8cN\xea\x0d*\x8c\xael\x863\xa2\xb0\xe1\xac\xdd\xc0\xd6\xcf\x80\xe1\x1a\xad\xab\xbaL\n\x11\x85\x14\xedl\x06\xadcD\xb1^\x12\xe1&\xfe\xd6y\x13/\x99\xe3/\xc9\xea\x10vJJ\xc2\xda\n|\xe6\xdb\x99\xa9\xcc\xe7\x12\xc1\xcfW]\x91\xf8\x97|Y2\xf4\xd6C\x16\x9f\xaeQ|Qn\x8a\xda\xf7X\x87^\x08K\x97\x19X2\xad\x8e\xc9\xac*\xb5\x18\x96L\xaaF\xc6\x960VI\xebb\xd8\x9f\x8a\xb8\xa5\x93j\x8b\x81\xc3F\x0e\x0d\x93\xb0p\xb9X\x9e\x14V\x9d\x99\x1f\x8ce\xaa\xfe\xbdX#\xfd`\xf2A&@s2\xef\x19O\xe6\xbd\xf6\xc9\xbcg:\x99{kjSE1\x0b\xe97\xf1z\xc0+\x809d\xaf1\n\xbb\xb9\x16\xc6\xe2\x8d(Yf\xe1\xb2\x0c\xb9\x9a\x9dG\x08|\x94\x89\x1eV\xfbFX\xed\xb7a\xb5?\xc4\xc5\x80\x8a\xdb\xe4\x13\x99mj\x16rZa\xcf\x86\x891#\xc2\x04I\x8ay\xc7\x86]\x1aDB\xf0\xfa\xe7\xae\x87O{G*}\xbc\xa9H\xf9\x92\xd4\xb3\x95g\x8d\xc1&V\xd4\xca0\xb0%\x9d@9\\M\x0d\xcaeI)\xac,\xffP\xa8\xb4\xdb\x10\x12\x831\xb7\xf5\xd6\xde\xac\x1f6\xed\xb6\x9a\x1d\x1d\x94\xe6k\xbb\xe4*\xd9\x0b\xfd\xdbF\xcd\xc1\x03\n\x1c\x03\x95\xd4\x0d\xa0\xcd\xb1-\xbe\xcc\x1f\xe2\xa5\xbeV\xd2n3\x87c\xf0\xf87\x1e\x18\xcd\xa4c\x96\xec\xe7\xe0m\x03\xe4\xe7\xf9\xba\x88\xeb\xe4\"I\x93\xfa\xe6u>7\xec\xe2\x8d\xc1\xdb\x96\x96\x05\xbe3\x92\x12\xc6\xaf\x90x\xb6\x92\xdd\x06\xf4\xa8\xb0s\xfa\x8d\xb6\xdbNb\x18\xd8l$&\xc5Z\x12\xc7\xf4[\xdaO\xa3:^Vp\x0c3\xfeg\x00\x13\x98&gc\xcd\xc0[\xce\xb4G\xaa3\xad]\xbb\x8a1\x1cX`\x1c\xfc\x8f\xddF\x0c~\x06\\\x97\xcd\x00\x9e\x17\xaf\xe6\x81\x9f\xe2\xfd_n\xdb\xf0\xa2\x0c\xa3\xc6\x04bk+:W\xedn)PDv\x1b\x11\xe7\x98\xed\x8d\xc2\x18\xba%\x8a\xa0_\x86\xfd\xd2-\x12q\x9c\xfd\xd9Z\xe4\xccL\xdeE\xb1\xf9wQ\x8c\xdaLgg\x01\xd0\x7fwwCH\xa6\x9e\x07\xbb0\x83]|D\xf1\xa5\x18n\x83\xa9\xa9\x9b\xb0D\xf4\xecK\xb0M\xfb\x8aP\xcc\xa4\xa2)\xed\x8a\xa2\xa4C\x04a\xacz\x04s\x16\x8a|\xfcp\x81wK\xe5^:L{m\xeeyA+\xb7:\x9c\xd3\xde\xcc\x89\x9bAQ\xe2\xb31\x17\xc6\xba\x06\x06Z\x7f\xa9\xd66;\xfb\xcaj\xb0\x10\xea\xa8\"\xe9\xc2\xe0'\xac\xde\xb2\x1d\xf6-\x10\xd6\xf1%9aL\x0c\x1cQ\xb2\xc1\x1e=+\x92\xeaC\xbc\x94\xb4\xa1\x92\x7f5\x95\x9d\xf4Vw\xc0\xb2\xea\xf7\x1dj\xce\xd4\xe1\x1b\x9d\xf63^\xb3hMh\x80\x1a\xd9h\xe2v\x07*t8?s\xad\xd9\x85Ic`\xa2\xb5\xa5\xe1@\x96w29$\x99\xe9>KVJh\xa5r\x9a\x9f\x0d*\x9c$\x81\xab\xb47\xf4\xc0x\xb5l\x9a\x9f\x05\xd8Xs\xf8V,,\x8d\xb9i\xceMO\xf0\xebi\xa2W\xf2\x9b\xf9\x0e}\xc3q\x91T\xba`\x81=\x1b\x0d=\xe6\xffK\"\xfaV \xf8\x8f\xd9\x03nK\xd9\x9e*=K\xfa\x84Q(\xf6\xbf\xd5\x9a T\\u\xdf\x7f\x93\xda\xb0\x02\x9a%\xd1\xbalj\xd6z6\xc6}\xa5g\x89\xca\xb4\x12:\xd7CMW\x0b\x16.\x8d\x1d\x1a\xfa~\xba\xf03:\x17*\x88\xa9\x13\xdf\x9a\xa5\x19w\x07\xf6\xe4` \xce\xf1\x7f\x86\xa6\xe7\x0b\x85O\x85\xd14\x1f\n>\x89*2\xdb\x94I\x9d\x90*\x04\"\xee*0JPV\x7f\xb8)\x08{\xca\x14\x08\xcac\xc3I\xc3\xa4\xaej\xb6\"&\xd9\x8c\x89\x9c\x9a;\x11m\xed\x8a\xd7\xee\xdf\x93h\xab\xcf\x98\xdc\xcd\"\x19\xfcT\x1ax\xf2\x05\xd6\x92\xea\x0f}\xa5\x82\x81\x87\x0f\xf4\x87|~\x13\xa2\xb6\xb8\xbc\"\xa5a\xf2s\xaeP\xa6U\xfe\x1a\x97I|\x91\x12\x83S\xed\n\xab\xae\xea\xdapE\xb1\xe4R\xaeP\x93\xe8k\xdd\xb4k\xfd\xb0I\xd2\xb9\xb1\xb2\x08\xe2\xf5)J\xaa\xb7\xcfN\x0f\x03\xbf\xd6\x1c\x147\xe8\xaeO\x1b~\x0b\xc7p.\xef!\x95\x88\xe8\x86 \x83\xef\x8c\xc4bS\xa6\x13cd\xa3YI\xe6$\xab\x938\xad&\x80Z\xf6Ut\x9d\xd4\xab\xe7\xcds8\x06/\xc9f\xe9fN0\x0ca\x15\xaf\xc9}\x16C\xcc\xd0h\xe3\x08l85gy~\x89q\xdeuF\x84\xfd\xf9\xc5\xa8\xfd\x7f\xa7A[z\xb4\x07!T\xb2B\x0fS\xe1\x08*\xca\xf4\xf3\x1a\x12\xed(=7\x80\xf2\x83\\\xaa%\xa9%\x91}\x1f_\x07CQew>\xa8\x91U\x9f\xfb^\xc3\xa4P\x89'\xc3\xd0\xb1Y^\xc3\"\xdfds\x9d\xab\x10\xed\xfb5F\x9e\x94\xd4C\x0f\xbeWmm\xd3k8\x86_na\x02\xaf\xf5\xd5\x7f\xc66\x87t1o\xb0\x86\x10\xd7\xf5\xf3{\x17m\xca\x14v\x8f\x8c\xa6\xa1\x83\xaa\x01F\x93\xcc\x01\x03$\xcd0\xdeT\xb2\x8dm\xbcU\xec\xec{c\x18\x9dF'\xf1\xc6pdr\x1d\xc4\xcf}\xcc\x0cB\xd8\xc9\xa4\xa5\x8d\x88(\x10ql\x0e\xe1]\x1fr\x12joBx\xc7\xd7\x80\xa2\x17J\xc1?\x07Q\x9d\xffT\x14\xa4|\x1eW\xc4\xc7\xa08G\xb0d\xca%=~\xbc\x97*\xfej\xfa\xe6\xccT\xb3\xe4\xd8\xce7b\x14\xa3\xbb=e\xa7\x0ch\xf7\x02\x8e\xe0\x99\xe2\xa9u\xea\xbfR\xc8_\x104\xcf\xdf\xb7\x9ek\x9a{1B+'4\x8a7S\x12%\xd9\x80-ai\x89\xb3\x85\xaa\xbd\x8b|~\xe3\xc9\x18\xb2\x8ca@\xbc\x8b\xd5\xbf\xa3\xc6h_Z\xb4-;\x11\xb5\xd0:\x8a}\x94\xc5k\xfck9e\x7f\x9fQn\xce\xf0>\xc1M\x1e\xb10\xadX\x19&p\xe9\xb3\xbfCx\x11tn;D\xc2\x96\xeb\xb8\xcc|\xef\x9d\x80+\x8f\xd4\xcf\x9a\xc6p\xfdI\x05\xf1\xfa\"Yn\xf2M%\x83\xdb\xd7+\x02<\n3\xee=X\xc5\x15\xac\xf3\x92\xbe\x893\xc83\xd2(\xfa1;\x00~\x91!\xee\xf7z\x88\xb39\xbe.\xe2\xaa\"\xf3\xfbI\xa6|\x8b\xba\x8d\n\xe6 \x8b#\xc6\xfa\x848\x83?$\xd9\x1f\xd8\xdb\xc8\x0bB\x11\\\xebh8\xf6bG\xd5%u\xeb\x8a8\x86\x91\xb9\x1bsCy\xf2\x85\xbd\n\x8cCHJ2\xa7\xbfvH\x84\xb7\xe2'\xeb\xa2\xbe\xf9+3\xf9nH2\xf7\xe2|/>h&\xd8\x06\x06\x856\x9dgQ\xe6W\xc9\x9chI\xb5:\x99\xb7]L\xf3\x98;\xa8@E\x8ev\xf5M\x81\x88\xa2\xd1@\x976\xaf\x0d\xe0[@I\xa3:\x90.\xdf\xcdK\x03d\xa02\x058M\xb48\xec\x85;\xb6vqA\x84\x97\x8c+\x1c\x91!\x041\x18\x15s\x80l\xf2\xbd{\x90Y\xb4\xce%\xf9\x871\x0e\x8d(rl\xd6@h\"3\xc1p-E\xa9\xfcj\xb8\xa6\xcdz\xc4\xd9\x9c\\\xa7f\xa6\xa4\xf1\xc7\xbe\xa9\xc3/\xcc*@\x0f6u\xe8N\x9d\xa0\x9d\xf1;\xcem\xd2\x9e\xae\x9b\x9e~\x0c\xe1]\xc0\x83\xef\x9ct\x1e\x07\xe2\xcc\xc3M\xda\xb6\x80\x97\xe7a`\xf1\xbd\xa43\xfc\xa9\x9f\x8aM\xf9~l\x98/q\x9c\xc8&\x8c\xde\x18\xa0J\x96\xbb\xe0cP\xfb{\xc8\xdeb\x18\xec&goE\xca\x04M\x8b\x06l\xceoC\xfa\x99\xbe\xa7\xe6\x10~\x8ec\x82#\xf8\xa9\xbf6\xfd\x13\x9c\x0d\xee\x9d\n\xe8>\xc3\xc1\x02#\xa17\xf6\xab\xec\x7foHy\xf3\xb6|\x99\x97\xeb\xc0\x7f\x17\x84\xf0\xeew\xed>Z?m\xf7\xac\xcama#\xb20\xb9\x97\x9e\x80ng\xbbMV\x06)/\xdbo\x14K\xa7\x1b\xc5\\\x11\x02\xcd\xb5\x12'A\x15\xa4\xbc\xec$TB+\x99!\x12\xffXp\xe6\x03\x86{\x15\xdf\x02J\x92\xb6:\x84\xa9\x87<\x9e\x87\xf7\x85~\xc9\x82\xd3Rv\xf1\xc7\xfc\xbaa\x17=6\xb0\xca;\x0bD\x9c\xb7\x81f\x1cj75\xcc\x03N1n\xbb\xf9\xfd\x8c\xc7\xd94sj9\xc5fDi\x97,\xae\x14\x91\n*\xc6\x8dL\x85*\xcd@6\xa59*\xdb\xd0\x0d_!c\xe9\xe5\x01\xfc \xee#\xcf\xe6\xa7\xec&\x86\xce\xb2\x9a\xaaUL>\x93;io\xba\xb2\xa1j\xbawF\xc7'\xda\xdb;\x0b(1\x14\x8dz\xbfxM\xcfn3o9zL\xcf\x98\x87\xc7\x83_\xfc\xe9\xdfo\xcfv\x83\xdb\x07K\xd5\xcf\xe3)\x0bs\x81\x862> \x9e\x06T\xb6\xd8T+\xbf\x9c\xee\x9f\xd9}6\x0d*`?\xdd\xe6f~\x16]\x89\xfd\x85\xbcq\xf3sJ\xac\x97\xa1b\xc2\xed\xaf\x86\x8fo\xe0\xc4g\xc3\xef\xf3\xa5\x0d\x9b\xfd\xb3\xb2\x13\xc9\xfd\x17\x99\x1c\xe6\xd6\x0b\xc1[\xda\x02\x81\xd0\xa5O\xa5\x97j9\xe8\xccd\xba\xdb\xd4\xf7\xd0\xb5\xc6\xb2m\xac;\xb9\x1c\xb1\x85\xcd\xae\xef\xc2\xe2\xcb\xd6 ]\xca\x95<\xb6\x19\x93l\x8b\xdfPj\xbe\xa9-\xdf\xd0\x13\xe6\x9d\xcf\x1dLgy\x8a\xb4\xf4\x9d_\xb6\x1f\xd8F\x9b\xe0\xbe[\xe5\x15z\x1e\x96\xf8\xd7\xf0\x17\xcc\x85\x8e\x92s\x14T\x1c\xfap\xc9\xac\xcb\xf1E\x84O\xf3\xe97H\x9e\x138\x86\x9cb\xf4\xe4\x01\xe6\xd4\xf0\x13\xd8\x85\x18\x9d\xf0\x82\xe9F\xf5\x00\x84c\xd8\xb4\\\x99`b\xc8\xbaz\xeb\xa7!hr\xb2\xdf\xfa\xe8\x9bk\xa7\x15\xe3x\x8a!=8H\x8e\xc2\x85\x0b\xc8\xdb\xc7z)R\xb2XX\x8c.j\xe5\x03\xa8E\x97\xb7}oT\xf3 T\x98\xf4K\xfc`;\x0e\xfd\xad\x8cma\xf4/\x8a!1\xc3\xcd\xa4\x83\x9b\xab\xba.\x06p\x87\x19\xf4\n\xdcL\xe4_C\xf8\x96\xe27\"\xb0\xbb\xad\xf6\xcc\x82\x99]\xac\x9caz\x17>\xc9\xae\x99+\x96\xf6\x89\xf0\x1b\x17&\xc6\xf2\xbfy\xf80E\xdd\xc4n\x98e\x8di&i\xa2\xe6nU\x03\x82\x7flH\xf9\x95V\xc86{ &\xb3\x8e\xbd\x8ep|\x08\x03\xf6\x17\x87\xc0\xce>w{\xbbw\x0f\xbc\x8b'?\xbd\x7f\xf5<_\x17yF\xb2\xda\xcf4\xbe\xa7:\xcb\xea\xbc\\\xbf\x88\xeb\xf8_\x12\x00~\xc64\xc1=\x0b\x16F\xa5\xe8\xd8\x11<\xf8\x87D\x13\xfa\xcbiC\x89-a\x1ee\xa7\xe3I\x7f,\xe6o]\xb6\xab\x1ei\x1d\xfc\x05\xfe\x93\x03\x0d\xa8\xbf\xee\x9c\xc5\xe8\xcb\xf9\xf9\x90\x12P\xc4`\xd2\x8a\xc8B-\xf9\xed\xe3q\x81r\xff\x05\x08\x8e\xb9bC\xa9\xcdu\x10*QU\xdf\xa4\x03\x95P/K\xd14\x1d\xf6\xae\xe9\xabr\x86%\x18\x8c_g\x1b!8moZp\x16\x13HP?_%\xeb\x82\"\xd4\xe0\x17|J\x13\xd8\xd0ol\x990X6\xa0 \xec\xec\x1b\xab\x99$\xcb!\xfa\x9f\x0b\xd2\xaf\x0bL\xf2\x1f\xc9\x98\x99\x19\xb06K5\xcc\x88l\xfa\x91\x0e\xbcM\xc6mF=n\xdb\xa5\x04+\xd2\x99\xb6\x8b\xe2\xcd )\xde*\x86\x8d|Op\xc3\xb1\\me\xa4\xb4\x0f\nq\xca\xacY!\xdb\\$\xc5\x8c\xa9\xbc}?\xf3\x86\x0fAQ\xf8n\x19\xb5\x15E\xc1-\xe9\x98r\x95\xf7\xe3\xe8\xce\xcew\xa7\ni\xb7\x0f\xc5\xb6\xe3\x07\xf6{\x82f\xb4\xf0\xd0IP\xcd\xc6\x1dJ\xee;e\xf4\xa1\xd0\xdf\x1e\xad'\xb7}U\x0b]\xdf\xa9\xc7S(K\xe6\x8c\x12\x9e\x9a\xbf\xec\x9ad\x11\x14\xbb\xa6g\xae\xdd\x81\xeat!\xc1\xb0\xff\xa8\xe3\xe5\xac\xdf`[t\xe2\xfd\x0f\x14\xfcM\xed\xfd\x9c'\x99\xefi\x9c\x13\x95w\xd0E\xd8_]#\x9b\x0cid\xe3F#\xdb\xd5\xb9\xb2[\x90\x17I\x85\\!\x99S\xfc\x88g5;\x01\xf3P\x1f\xc3\xdeb\xb8i8_\xb5VF\xf5X/\xb0Krcc\x04\x9cTl\x16M,3\xfd\xb42D\xcc\xafk\x88\x1e\x00W\xeb\xda\xe7(\n\x87\x13\xe6\xd6\xb2Ku\xe2(\x1c\x8e\xe1h8\x8f\xa0\x7f\xe6\x88\xc2\xa2\\2\xa6\x92\xb15M\xb6\xdc\xf1{lc\xca;/7Qhrv\xc1\x81\xa4\xf1\x05I\xbb\xe3`.\xf2_e4\xd1\xe0h\xd6q]&\x9f\xbe2X\xc6&r\xe1M\xb2,2 \x1c\xd3\x83\x84\xb9\xfbQ\x06\xef)\x05U\xcdX=\x0c#2a\xaa\xce\x10\x7f\xe9\xc70\xe0\x8e\x8a``\x8a\xb4#\x9b\xa7\xbe\x90`\x13\xee\x1c\xdb\x8ccB\xfb73\x9e[\xc0\x15\x1c`\x0b\xcaBkn\x02\xc0(\xed\xb3-Q\xc43\xf2\x82\xa4\xc9:\xa9)\x93\xee4\xfd\x94O_\x99\xf8o;o\x0f\x83\x15\x18RX\x0d\xcc\xbeH\x8a\xd1\x93\x9f\xfd\xcbM\xfe3\xc6\x0eu\x9dh\xde\x0d H\xeb\xa1AE\xc7\x1d\x92\xbe}\xc2\x1c\x92\x1e\xe9\x1d\x92\x985\xf9#]~\xff\xd4i%\x05\xec&\x0f\x8e\x7f?=\xfb\xffv\xbe\xb9\xf7\x07?\xf8\xe3n\xf8\xf4\xc8\x93\xf7\x19\xdcp\xb6?\x15\x8d&~L\xa7\x0f\xfe>\x8d\xef\xffs\xef\xfe\x93\x8f\xf7\xa3\xf3\xff:\xdb\xfd\xe6A\x12\xd5\xa4\xaau,\xd7\xb6~\x01O\x0e\xf7\xb7\xb7\xd1?\xd8\xfe\xd3\xc3/0\xefo\xbd\xfa\xb7\xd4\x8a\xca\x00\xa9f\x95\xa6\xdd5\xb5\xec[ a\xcc\x9a\xc1\x84(\x96\x08\x95\x9a|(\xd8\xe6`\"\x14\xb3\xdb\xef\xa2\xef=\x8bw\xa3\x86\xcbbtR\x8c\x84\xc2\x9d\x18\xdc{\xe7\xed1\x16b\x8c\x06\xdfeLx \x80\x89F[q\xeb\xd7\xd4\x10n\xe4\n\xb3-\xdc\xbb\x07;;\x1d\xfd\xea\\D\xc8\xd2\x7f\xb8\xee\xc7\xc6\x8aC\x98z3a\xf6\xac:\xfd\xde\x9c\xb2\xf0\x00<\xb6\xcfP*)\xe5\xa6l\xd1\xbd\\]H\xe3\xb4E\xdb8\xad3\xf42P\x14\xd8W\xf4\x1f\x16\xd3\xa6s}\xd5\xc0\x0bG\xd5\xfc\x94a\x7f\x8e\xc1_il4\x06X\x13\x19\xe0&\x83$\x1bN\xde\"8\x98\xf9t(\xb6$p\xa4^O\xb3\x01{\x0f\xb4\x07\xb0\x9d\xd3R\xa1\xcb\xf3\xd6\x7f\xfel\xbb\x10\x03\x8e\xfd9zN\x0c\x9b\x9b\xb0!X\x9bCy?.\x92\xffEx4\xcc8\x00\x0f\x17\x93\xdf3\xf2\xe0\x98\xfeB8\x19\xc8\xeb\xf0$\x08\xc1c(\xd1\xab+.\xcf;\xb5\xd9\x9dp\xaf\xb6\x08\xc0\xa6\xd6\x1e\x9e\x1d\xa8>\x18\xcc/^\x8c\xde\xce\xf2\x80\x8c\x01\x1aW\xc9L\x8c\x86\x85\xccp\xfd\x1e\x14\xae \xc1@\xc1\xf6[\xcfnAuYT\xc4Uu\x9d\x97\x03a\xcatE\xc8\xb3\x8a\x7f,\x0buA\xd9\xa3\xca\x01z\xa2\xc8\xb5\x8a\x9e\xa9w\x8ep\x04\xde\x0f\x14\xfcN\xf1\xbf\xbc\xe5\x81*-R\xae>R\xa1\xe0r\xf9\xb9\x87a\xdf\xe9\x06\x8eVq\xf5\xf6:\x13'`{x\xb9-_\xb2d\xb3 \xcf)Bi\xfa\xdeS\xa8\xe1{8\xf8\xf6\xd1S\xd8\xdd\xad\x03 ,\xda&\xf3\xca\xa1t\xff{\xd8\x7fD\xb9\xb1=\xc5\xf2\xb1\xe5\x17\xd4q\x0c2\xab\xef:>:\xbeR\xb3\x8ebJ:?\xe4l\xca\xb6\xb3V\x91\x18\x8e\x00s\xce\xd5Q\x91\xc6I\xc6>\xa7\x9c\x1a\x87\xdd\xac$qM\xfcl\x93b|y\xca\x0b\x96l\xda%|/\x1d\xb8\xe8\xdc\xcb@UV\x91iy\x86\xf8\x98\xd1?\xd8\xef\xee\x92sS\xe9f\xcd1)6)\x97\xa43\xfe,\xec;\x92\xa2\xba\xb6IC\xd9\xe1\xc3\xd9\x0d\x99T\x7f \x9d\x9b\xd6\x03\x81\xd6\xed\xc6\x0e\x96\xeb\xa8\xb3\xa5E*gVDk\xfa%r\x9cS:\x1d\x83\xe8\xe5\xe7\xedE\xf8\xfc\x99\x8a(i\x9a_\xbf\x13\x18\x8c\x0fw\xcah\x16\xa7\xa9\xdfEo\xba7\x18\x11 S\x0cv\xbb\xb37b\xc3\x0fy\x809LK&\xcd\xecBLp\x87D\xbb\xfa\xbd\xa0\xcd}\xef\xdf\x8c\xcd)A'\xd0\x16\x9aS\xdc@m\xa7\xae\x95^#\xc7\xe0g}\xc1:\x0b!\xd1*\xc0\x18\x8c \xbe>\x062M\x10\x9f\x15\xad\xb6\x84\x02}\xc5k\xfc\xff\xec\xbdk\x97\x1c\xc7\x95 \xf6]\xbf\"P3KU\x0d\n\x8d\xee\x06@\x11MAt\xa3\xbb\x014\xd4\xe8n\xf6\x03 \x00a\xa0\xac\xcc\xa8\xaaDge&\xf2Q\xdd\x8d\x11\xe6\x90#\x8a\xc2\x83;\xb3\xde\x91\xa8\x91=cy\xd6$H\x00\xb3^\xdb\xeb\xb5\xd7\xf6\x8e\xf7\x1c>\xd6>Gs\xa8\x99\xbf\x80?\xb0\xfe >\x117\"2\xf3\xde\xc8\xac\x02 R\x9c\x1d\xd59\x12\x1by\xe3\x1d7\xee+\xee\xbdqFcp[\xfcSc\xeeB\x81M\xe2o(X%\xf9B\x8e\x97\xbe\x9cjS\xf7\xf8a\xda\x0e\xada4\xd6\xe1j\xd2\x1b^\xf7\xebc6ms\xc2#v\xf4\x88\x01\xe8t1bT\xde.\x01\xbe\x90\xa6\xfe \x9cDs\xd4\x18\xca\xf3\xcb\xa6\x0f\x13\xd2H\n\x88\x9d]\x0foX\x06\xc6\xd1\xc0<.$\x95F'A\xfb\x8b\x93\xaa7\xa8_\xc9\xb1X\xce.|Tf\x17f-\x946\xc0<e\xbe\x9e\x9e5_O\x7f\xc7|\x9d\x9b\x9f\x97q\xc5G\xf5\xc0\xe4\xa0\xd8\x82\x80\xb2\xb9\xf9W40\x12\xd8\x0e_\xe7gO\x96>\xcf\x9d\x9eg\xb2\xd9\xef\xb1\x97o\xb0\xa3\xe2\xcb\xfc+\xecG\xec\xe5\x13\xec%f\xea\x9c:5\x7f\xfae\xd3\xff\xa9\xef\x9c8y\xb2hb~\xfe\xa4nbn\xbe\xdc\x06\xb4\xca^b/\x9f\xb07\xddND\x0bs]\xb9\xb0/\x9f:u\xe2e)S\xcc\xcd\xce\xcb\"\x1d\xf6\xdd\xef\xb2\xb9Y\xf6#\xa6\xbe\xa0\xb5\x97; C89k\x86\xf0\n\x19\xc2\xdc<\x19C\xf3\xd0:\x0d\xac\xc2\xce\xd5\xddh\x14;ns\x14n\xf5\xcd6\x8aaQ\xefV\xdd\xc5Cd\xbdr\xa0\xe2g\x9cD\xf1\x02kE\xd5\x0c{\x96fI\xeef\x91zH\xbb\xf4\xa1\xe8\xab\x16\"4\x85b|\xdfb_VaU3/\x16C \x1bTS=\xfe\xcf\xe6g\x8f\x0f\x8a\x16\xca\xf7\xc4\xd5\xc50\x97\xb2\xad\xadsK'N\xbf\xf22J\x1f\xd3\x97i\x89\xe1m \x8a\xbd[\xe7\x96\xe6\xbes\xe2\x95ib\x8c\x88\x90\x19uY\xeb\xa8-\xf3\x04\xa5\x13jh\xcf\xd1\xcd\xc4+\xe6j'f\x1e-\xf5W\x8b\xc0a\x00f\x95\x9eo_\xf5\x0e\x02E(6P\xbe\xbdF\xb7/l\x9f\x9e\xc3a4\xbe\xfa>\x8f\xbe\x9b0W\xb5\xbd\x93n\xfdY\xe9\x04H\xef\xc8P\xbf{\x02O\xb9H\xc7\xac6/;\x9b,;\x99<\x13\x19\xf9\xf8\x1a\xe33\x03\x9e\xed\xf8#\xde\xee@\xf5\xd2\xbf\x17T\xbc\xfe\x11x\x19\xcf\xa2!Vt\xa6\xe2\xbb\xcc\xf62\x03\xe7@\xca\x9f0\xb0\x05\xf9\x97\xfcc\x9aY2\xb5\xf0A\x97\xb9\xf5t;oC\n\x97\\\x12h\xb52G,~f\xba\x02/\xf6\x0fhp\xf1\xef\xa9\xea\xfb\xd2\x80\xa0\x0b\x1e\xf1\x85\"\xa03\xe3\xe8\xd3\xd1\x01\xf3\x91\xfag\xd6\xe92\xc7\xcc\xb4\x81\x07\xa5\xb2\xe9z&#\xad\"\xe94\x13ef\xb2\xca\xbc\x083E\xbaDSm\xc9\xd0\x02`bA\xc5\x18\x14\x1c=\xda|\xe7);\xbe\x1e\xdcP,.\xb81U\x87\xba\xc8\xb4\xe9\xfeX\xad~\xa7\x7fc\xf5\xe8W4\xf1\x8d\xd4X\x96\xcaj\\\xf6\xb4\xc67M\xd2\x8c\xba\xe4s\xb5{\xde/v\x88\xc5\xd3n\x90\xdc\x9c\xfeL\x1a%Y\xbb\xd3e\xb1\xf9K\x06\xea\x95\x9e\x88\x14{\xf7=\xd8\xc3c\xc7\xeawM\x0e\x04v\x8c\xc5\xd3l\x98\xc1\x8e/\xd8\x99\x8c\xed\xbb\x1e\xdc\xe8\xb2#N\x9b_wotY&\xff?\x9c\x8c\xdbZx\xd14\xa8\x90yi\xfa\xfd\xbb\xc5\xb1\xab\xc0\xee\x96\x1c\xa6\x8c\x7fR\xde,kHu\x9c\x15Y\x17\xcfT\x1e\xce\xbaki0\xadm\xf0H\x1bH\xab\x95\xa8\x8a\xef:\xffV\xe9\xbbA\x0e\xe9\xcc\xa9;\xa9(\xfb3n\x14\xcb\xb7\xf8j\xc0\x92_I\xf1\xa8\xa0\x0c\xea!d[\x8f\xd7go<\xaf\x04\xa49%=(\xc0\x0e\xe8u\xb3\x8d}\x9e8=ka\x9f\x13/\x98\xd5\xe2Fj`H\xad\xbbK\x19o\xd8\x9e?1[1\xb4_L\xa3pS\x1cw\xfd\xa0\x9b3S\xfc\x13\xacN<^\n\xa2P>*=s\xd3\xfc\xb3*\xee\xe5\xd6%p#\xfe[G\xc8s\xa9+\xd4\x11\xa2\\&O\xa9;\xdc\xf9\x8c\xf8o\xf5@\xd9\x14\xaa\xc0*\xa9Kw\x03\xd0K\xean5\xb5\xd5\x9e.\xa7d\x02\xa2w\x0b\x17P\xd4\x1f\x8f\xab\xfcO\xc3i\xe4Mt\x97\x85\xb0q\xa6\x8cM\x8bs\x95\x93JR\xe3\xa7R ~\xd3\xd2\xcf\x91\xb9\"\xbc\xeb\x8cN|.\x1f\x98?2\xdb\xe9\xaa\x82V--a\xaf\xb1Dp\xc2\xd9.\xe3\xf2\xeeDH[l\x81\xc5\xf2\xa3\xcc\xb8\xdcR\x179\x00\xa2\xab4V\x99\x0d\xed\xe8XAE\x8b\xa5\x95\"=x\xb0{\x9e\xee7\x8a\xcd\xce\xb93\xa5\xe6\xe4\x1d\x8a:\n\x16\x9b\x9dlF\x9d\xc7\xe7jJ\x8bl\xe2T\xd6\xb7,\xa5C\xd3\xacT\xa3\x05\x8eO\xd1\x93D\xd4\x10D\x94.\xc3\x0d\x89\xad\xaa\x0c\xa1S?\x06ql\xca\x1d\xdaw@\x9a@\xe4\x11cg\x04\xf75\x88\xd81Od\x01\xb8\xc3\xb2a\x12\xed\x8b-#\xcai\xbb\xb5#\x1a0\xce\xc1\xac\xef\xf8\x01\xf7Z]\xd6\xdaY\xd9\xde\xb9\xb9\xb1\xb9\xb2\xb5\xb8\xb3\xba\xb1~\xf3\xdc\xe2\xea\xda\xcarK\xa2T\xd8e|\x82\x18\x86\x16G\xac8E\x92\xba\xcd\xad\xae]i\xc5\xab[\x88\xb7:\x0f\xecf^\xd9\xaa<\xef\xb4\xcd\xb0\x90\x18j\xeb&\xcd+h\x1e\x81g?\x8c\xe2\x1f\xca\x8bL\x9ed\x87\xccOY\x18eL\xa8\xf9Q\xbfX\xe2\x94\xa9\xa8J\xe6\x87l\xeb\xdc\xd2\xb1\x97O\xcf\xce\x8b\x05/\xd6zc\xf3\xe6\xea\xfa\xe5\xc5\xb5\xd5\xe6\xf5\xd6\xcbR%V\x95\x7fE\xca\x92\x8fT)\x8eU)m\xe6l\x03=`\x90WW2\xd0\xac\xdd:\xde\xb2\xd8>a\x17\xc8\xe7!;\xc3,\x8f\x16\x8cKv>\x0b\xb31!b\x146h\x80\x1d\xd6\x84\xe3J\xd3\xe2\xa1|\x1a\xae\x8e:\nb\xf8\xaa\xf5\xcaWl\xf9@\xda\x16\x877\x14\x95-\x11a\x08\xde.\xc7\xb3]\x1f\xdc`\xaf\xc9)\xf4\xc18\xd6\x9e\xed\xb2\xa1N\xc5z\\f\xe7\x1b\x8a\xee\xc7\xec\x18\xe4\xe2o\x8f\x98\xa1\xbc\x95\x00^\xd9\xf8aA\xb8G\x82R\x0f\x8f\x1e\xc5\xf7\xc8^\xad\x89_\xe2\xfa1@\xf4AG.\x9e\xa7\xad\xee\xd6\n\x0d\xae\x8aL\xe3\xbf\xb4\xf6\x95\xa5\xd2A\xa7\xf9H\xac\x1c\xc4\xdc\xcd\xb8\xc7\x9c\x90\xe5a\xea\x0f\x04\xba\xf7\x9c\x94\x1f\x9b\x9be\xea9d\xa6\x08\xf3\xc8\xd9\xf3\xc3\x01\xcb\x86\\6\x96\xf0>Ox\xe8r\x0f\nH\x80\xf4\xe9c<\xe0\xf2\xa8\xef\xfb\xd9P~\xbe\xc3\x93\xe8\x98h\xd6\x03\x81\xb5z\x8a6\x17w.\xdc\\][[9\xbf\xb8vsqkk\xf1\xea\xcd\xd5\xf5\xe5\x957\xd4\x99\x02\xed\x8e5\xbd\xe5W\x9d\xb2\xdc9\xb1\xa0\x7f\xfc\xc7\x83iu\x1b\xa6\x96p\xc8\xbew\x86\x8d'\xdd\xcb\xc8\x85\xae\xf2H\xf1e\xc0\xbeg6q\x021\x1fr\x19\xc6\xe1\xf7}\xbd&\xec\xd2\xee\xf6\x0e[\xdf\xd8a=\xce\x06\xd2W7a\xd9\xd0 a\xc5\xa5\xc1V\xd0'\xb5\xb8\xa9\xa0Jf\xc9\xab\x0bzyqmw\xe5\xe6\xc6\xee\xce\xcd\x8ds7\xcfn\xec\xae/oO\xbf\x96\xf2\xde \xd8\x92\xb4\xdc\xa7\xd7\xc5\xf4n\xc0\xedV\xd8e^\x97\x0d\x04\x99\xeb|\xfd<\x8b\xd5\xd1R\xfd\xb3\x08\xccE \xc3@\xb9\xc5\x1c9\xc3\x06E\xaa\x83?n\x15\xf8\xe2\xcc\xe4!\xe4\x9a\xdct\xb2a\xe1)8\x90\xa7\xbb\x113\xf0\xaa\xe5\xdf\x9cU\xab]1\xbaZ\x1e\x032Y\xc3\xa8l\x02s\x7fz\x81\xd9&\x16\x13\x07\xe1\xe6\xa5\x91\x7f\xb3\x94\xdf\xce\x05\xe5a\xa3<\xcd\xc4qq\xc2\xe2\x18l\xaf\xbc\xbe\xbb\xb2\xbe\xb4rs}c\xe7\xe6\xe2:\x10\x14\x1c\xe12-\xbb5\x9e>\xf2F\x9f\xef3\x1d\xd6\xa4\x0e\xb9\xf2\x00\xebB>Msk\x9a\xb3\xef\xb2\xf4U\x96\x1f=\xdaa\xfe\xf5\\\x86`\xcau\xba\x9e\x0bN\x05\xf7\xf7\x12R\x16\x8d\xac\xda\x8bO\x054\xbfqC\xe2 \x1bRw\x0bU\xbd\xf6\xa2^\xf4\xd3IVJ\x96rB\xa6\xba\xa9\x10&\xb5%\x1bg/\xae,\xed\xb4\x00k\xc5z\xbcJFy$\xbf\xce\xc5\x01\x9a\xb6\xdf\xafD\xa2\xab\x1f\x9eq\xbe-_\xd9\x81\x826\xe5xEa:b\x87\xa9\x86-\x0cr\x8aa)\x9f(9\x92\x82\xc4\x1d\x07\x12\xa7>\x177\x81\x8dc\xfdv\xfdX\xe5\xa9K3'Q\x1c\xbeu\xbc\xf5\xed/6\xde\xb2\x1a\xc7\xa9\x1a\xc7\xa5\x02 X\xadm\xb9\xa5\x027\xedr\x8b\xc2t\xb9\xe3\x84\xa7\xe2X\xb5U\x88\\/\xe0\x025~(F\xf5C\xe6\x84\x1e\xfb\xa1\x18\xcd\x0fK(\xd4\xa9n\xcd\xb9\xad\x8dK7\xb7V^\xdf]\xddZ\x994W#/\x98\xa9V\xd4c\xf3\xb5P+\xcd\x02\x94o\xa1\xb5Eq\xca\x99\xcb\xd2\xd3O\xdd\xf1\xbc\x1fv\xd9\x0f\xd5\xc8\xd4\"\x88\x115,\x02\xc8\x1b_\xfd*83C'\xdd\xd5\xc9n\xdaz%\xbeyK\xb1\xb4\xb8.H\xdd\xd2\xc6\xfa\xce\xe2\xea\xfa\xcd\xdd\xf5\xe5\x95s\xab\xeb\x13\x96\xc6r%Q6\xc5\xa8e\xa87cB\xa0\xb4<\xe3\x85:\xd8\x98_\x83)kxD+\xd8E 1\x1e_\xd2\x98\x94\x1d\x05\x15I\xfd\xb3y\x0f\x96\x9cP.4OdT\xb2\xa3\x16\xb7$\xe48\x99\x14f=\x9e\xfa \xf7\xa4u\xcfB\x03\xd5\xba..\x97W\xb2I\xe6\xab\xc1\xad\xb2\xe5\xc2|,\x0c\x0fM+\xed\x83W\x99\xa3\xdc\xac\xa2\xe7\x9a\xb8\x98be\xce\x8e\x9c\xa9\x10\xf33\xe6E\x1c\xf0\x91\x1f\xf8if\x99\xfd\xee\xfa\xd6\xca\xf6\xc6\xda\xe5\xc5\xb3k+\xd3\xce\x7f\n\xfaZ\x8fQ\x81\x10\x07\xdb\x16\xff}\xfdk2\xd0\xea\x1f\x18j\x81\\O\xbc\xa3\xab\xc9}.~wo\xd0c\xa3\x7fb\xaa\xd2\xeb\xbdq\xc9\xe4\x9c\x03\x99\xf9\xe2K\xec\x9a\x98\xc7\xd4\xfb&\xd9\xc3\xd4\xfb\xd6(\xd7yZ\xae\xc3;f\xf7\x8b\x93B\xd4\xf3Iq/J\xb8\xd6\xdd\x87\x1d\xd6oW\xe4\xeb\xb0\xd3\xc5\x02\xb7\xd0\x03~\xf4#\xa1\x11\xd0F\x1aL\x1e\x89L\x19\xf6\xa3\x1f\xd5\xe5\x01\xac\x84t(\xd7\xfc\xc2\xab1\x12\x82y\xd2\xe6\xd7\xa3\x1b\xd2\xb79\xd4\xc6\x9dI1\x0b\xcd\xee\x81\x926\x94\xfdn\xf1\x1a\xd7]\x81\x88\x1f\xecLm0\x99\xf9K:\xed\xca\xf7\x92\xcf\x1enF~\x98I\x0f\xfa\xc0Du\x17\xfc\xee\x0cs\xcdW\xd8\xdb3\xaco\xbel\xc9p\xbd\x04\xc7\xe7\xe2y\xe9\x0b2u\x8bb\x91\xd4A\xebM\xbe>\xc5V\xadaR\xd6\x8c\x8a\x85\x12\x13\x1c;\x81\xef9\x99\xf4\xe9\x8aK\x1f\x84\xd6\xe5}K\x15\x9b\xc6\xb3-l\xcf\xbfR\xea\xbd\xd6w\xdb\xa6h\x1dI\x94\xb72\x9f\xb9\x99\x81{\xac^\x9e\x9d\xc3\x98\xab5Y\x0de@U\xe6\x0b\xa9#\xe1.\xf7\xc7<\xe92\xf3\x96\x84L)\"x\xe2\x11|\xcc4*!\x1c\xf9BQ\x0b_(\xad\x0cM)SN'Sr\ni\xcf\xcfw*\x8ew\x96<25\xbe\x93\xf4\x909\xfd\x8c'k\x91\xe3M\x13a \xafk\x93(\xcaVC\x08\xc4>C?\xe9w\xc9\xd1\xf7\x19?\xf4\xb3\x8d\xc5<\x1bB\xb2\x98<\x1b.\xca\xde\xd2\x197\n\xfb\xfe O\xb8\x80Zj\xc6 7)\xdc\x16e*(is\xee\xf9\xa1\xd7\x86\xcb\x0f\xe94\xdeT\x0d\xf2\x1a\x9dan\xb5\x16%O\x94\xa5\xa6\x99\x93\xf1\xcd \x1f\xf8\xa15\x0eD\xfcD?u0&W_\x12\x87t\x81Ez\xb3\xeay\xb7\x03\xcb\xd2\x185\x96\xf2\x80\xbbY$Z\xb4\xbf\x0fY\x93\x95\x16r\xdd\xd4\x0ft?q\xe2E\xdd\xbf\xfdQ\xae\x89\xee!U\xdaa\xdd\x05\x0c(v\xb5\x8a\xf0\x91B\xf8\x13\xa7O\xe2\x9c\x19>\xbc<\xd4\x9e?A\xb2M:\nt\xe2\xf4)\x0c\xca\x0dH\xe6\xd90\xb0&\xb7c`C(\xdbc\xd3\xed{&\xa3J(iWQW6\xbc#\x89\xea&$\xe80\x91D*\x05@\x06\xd1\xdf\xfczX\x93K\xa2L$x9\xff\xa7M6\nj}\xaf\xa7\xcfzY\x93\xf1\xb2Y(s5\x89\xb5\x18\xdb\n\x9d\xacL;\x0c\nQ|/\x1e\x0d\xd9\xd6\xa7\x85\x16\xca\xa5\xcdR\x14\x12\xdc\xd5r\xfaMz5?\xddX\xdc>\xd1\x91 \xcd&>\xb2\xc1\x16\xd8\xf5\x96%\xd3b\xcb\x12\xa6*\xd4\x82\xbc\xdd\x11r\xc8j\xd8\xben\xd2E\xa4]v=\xbbA\xd2\xc1\xc0F\x04\xec5\xe6\xcb\x07\x99\x13\x94\n\xb3![\x99\xfd\xdc\xebdq\xb5\xae5:u\x9c\xcd\xcf\xd2F0\xc5\"8\x0b,\x98\xc9\xa2\x8b\xdb\xe8=gHS+NB#\"\xf4\xeb\x1c\x8d4U\x98\x1a\x0b\xfci\xb0\xc0\x81\xb7[j\xb1 7O ~eX \xc3\x98-X\x907aA\xca^c\xd1\xf3b\x81\x0d\xcb\xd5\x96\xa5So\x19\xfb\xa6\x89F]\xed\n-\xa5#\xca+$\x84d^r\x14d\x8e<\x00\x90Kq\xf5;\xe8+$\x1b\x9e\xc3\x11\x16\x81\x8a\x87\x98\xb7\xf2\x14\xf7\xeb!\xa7\xfa\xaf2\xa9\x97\xfeT:'kT\xca\xc9\xdae\xc1\xcc\xf6\x85\x8d+7\x17ww.\xdc\xdc\xdc\xd8\xdc\xdd\x9c\x90oY\xfb\x95e3\xb1-\x9f\x9f\x9e\xd1L\xca\xb3v+\x1dF\xfbe\x84\x17\xa8Q\xda;\xfbx\xc4P6\xb6V\xaf\xad<\xefH(B'&Op?\x89F\x17\xb7;BW&\xa5\x80\x90\x0c\xc4\x80\x8b\x1c\xc1-x8CV\xbe\xe4\xc4\x1d\x1c\xf8n\xd4%\x1ef\xc9\xe16\xbf\xdd\x9e6\xe3\xba\x96\x0dP\xbaN\xdee8\xb0U\xff\xe4,\xaf\xcf\xd6\xe46H$t\xae\x06\nIe\x159i\xc1 \x17T*\x939\xcfjl\x0c\x95T\xab2\xc7H\xe9\xa5\x1d\xbf#W,\x92[\x1c\xda\xcdG\x85\xa9\xac\x94\xdf\xd4\x9a\x97\x87\x95\xc2}\x8aq\xca\x93.\x86\xa9\xb9R\xebFC\xfca`\xaf\xab\x19\x96u\x9aLm|\xdb\xccET\x0e\xbbL\xd5ot\x9f.xe^?*H3\xb7P\xce\xa6\n\x8f\x93\xf5\xb2\xc8)?\xdaS\xf7Ls\xa7S\x1e\x96\xda\xba\x1b]\x98j[\x7f\x98\x98\x11B\x066\xc3y,\xa1\xb7\x10\xad\xa6?\x8a77\xc4\x9f\xf3/\xe6D\x86\x92Q\xdb\xcfaX\x97,\xd9\xa9\xf1u2\xe7\x10\xde\xeb!o\xfd\n\xaa\x17u \xcfH\x95\x14$z]$\xd6T\x96\xc6\x81\x15\x96\x88\xd7\xb9\xd1-\xe7\x05\xac[\xaa\xb5\x8d\xf3\x1b\xbb;/f\x81,\xc4hf\xdf\xcf\x86\x97\xf2\x0c\xaeG\xa6\xc8\xa8h\xc9\xe4\xd5\xf8\x8c+\x9f\x81\xc0\xb2\xda\x10^\x0b\x9a\xd5\x98N,\xb8\x96L^\xc0\xa5\x8d\xf5s\xab\xe7w\xb7V$/z\xde\x85l\x1a \x18\x16,\xdcG\x8d\xea\xb7+\xc0t\xc1\xf6\xb8\x04\x83\x94s\xf2\xd3E\xb3x\x90\xd4\xad\xfaO\xaf`\xa9\xe7\xa2d\x0bLY\xe0\xbe\xa4\xd2\x0f\x94\x98\xee\xd9\xc3ug\xc4S\\q'2}H\x90`\xd5a\xa9\x9a\xe5\xb8i\xdbS\xde\x0e\xdb'\x89t\x15)\x08\x95\xa1 o\xc3),D9J\xb4z\xbe8\xe2\xafDV\x1a\xab\x04B\xf5\xc7\x8a\x9a\x05\xcb\x967\xcb\xe2\x01\x19\x82\xec\x90Z\xe5\xe8\x08enr\x1f\x8a\xbc#\xd9\xa9\x83p\xa6v/'\xf7\\\xd3\xf1tb\x0b\xd2\xa2l\x0f \xb4\x8d\xec\xe4\x80\xecT\xfb\xcaQh\xe4\xa05?\xcd\x88\x90\xc5\xca\x96\x8b\xe7\x16\xb4\x18\x12\xb6\xa2\xa9\x84-fD\xaa:\x81\x8b)\x9c\xae\x17\xbaXIYt\xac\xe2c\xb9T.\xc9T\xd2\x95/%\x86\xe0\x1b\x9b\xa7\xc3vn#\xb9]\x9c\x17\x91\x92\x12\xeb\xe1o$\xa7S#@H\x11\x80\xce\xcb\x8d\xc24\n\xf8\xcc\xbe\x93\x84\xed\xd6\x95\xc5\xad\xf5\xd5\xf5\xf3\x0b\xcc>2?e\x1e\x8f\x13\xee:\xe00\xeb\xb1}?\x08X\x8f\xeb0\x1e\xed\x91\x19\xf2\x83\x8c\x8d\x9c[Q\xc2\xc6\\g\x9aB7\xe2;\xd3\x04\xbb\x11\xe7\x99\xce`,I\x98?\xa1W\x1b\x8f\xc1\xbf\xca\x9b\x039PF\xa9\xba(\xd7\x95T\xd0\xbc\x97^b\xed6\xbcp\xa1$\xe3(\xe6i\xab\xd3\x99\xd9\xe3_h%\x99\xf4~v\xa30s\xfc0U\x17N\xb2\x87T\x8bI\xdc\"w\xeb\xdf]\xe5\xc1\x98+I(\x08\xa2}\xeem\xc3\xa8\xba,\xed\xa8\xe46\x99\x84\xfb]f9\xe9\xba\x1d\x1f\x9e\n\x95\xb9\xcd\xec\xf4\xc0\xaf\xa3\x07\xddI\xa2B\xfdbh|u\x92\x81\xbc\x08L\x0b\x07\xb79V\xcd\x15f\x8a\\\x9f\xbb\xc1^\xab\xfes\xa1\xe9TMEtT\xa16\x18\xfa\n\xaec\xe7~e\xc6\xa3\xfa\xecL\x9f\x84\xdc\x1c\xf14\x1a\xf1)\xc5fSG \x1e/\xe1\x9b\x9f\xa4Y\xbb\x06G\xac\xb2t\xd3.V\xe4\xbf\xc9\xfc}\x82da3rh\xa2\x84\xb8 \x92D_$\x13\xa9\xeeg1\xa6\x06\xe2\x0b\x9b:\xe3\xa7\xe2?\x10\x1b|\xe4H\xa6\x8c\x95\xcf\xbd\xcf*\x97#2\x9b\xf2\xce\xcc\xc8\x89\xa7h\xa5\xd4\xd2\x91#!\xec\x7f\xddv\x1b\xaf\xd1#s\xb6\xad\xd7\x87\x0b\x99W\x19E\x84\x8a\xa2\xf0\xa5\x11A+F\xe5]\xff\x16\xfbFhD\xfc\x80\xbb\xb9\xf4,\xb0j!]\x95\xe5f\xfe\x94E\xd7\x90\xd6\xceH2\x88\xa4\xaa($\xcd\x8aB5^\xb8\"\xe1\x17\xe3\x99R/\xad\xa0\xb7]\xcd\xcf\x9a\x04)|\x9aj\x9f\x83\x89\x94\x1a\\\xe7\x8e\xe8\xa8\x0c\xd6\xd90\xaayr,\x97%\xa6x\xc1M,C\x968\x0d\xcf\xc9\xd6\x1f\x95\xe2\x80/(\x03\x90>\xeeb\x9f\xaa_\xd4\x89\xae\x97\x1eJ\xd4\x7f\x81%5*\x88\xdc~+hb\xfb\xe5W\xdd\xca\x1d\xe0VMS\xf6s_K\xc8x\x1b[\xa9\xac\x0d\x80\x93_\xcd\x1by\xb0\xa3\x0b\xcc\xb1\x83K\x0f\xde\xd4\xd8(\xcb\xaf\xe6X^\xbf\x95rJ\x1d-\xfa\x86P\x89/\xe3\xf1\xd2\x0f\xebnB\xd3\xa1\x94\xd8Vn\xe7N\xf0}~\x08(\x86\xbe\xd1\xf5\xaa[*j?\x917G\xdf\x80\x15\xa4#K\xdba\xfb$y\xe7:2>\x16\x13\xfd\x8dj\x05I>\xd3\xb7\x10\x16{\x82\x02\xf1\xf3\xa2\xfd0\x98\xd2\x1d\x89Y\xc8emj\n\xfd+\xf4D\x9e$\xea\x02\xb9Y]aZQ\x9at\x8d\x8c\x7f\x8e\xa94u?\x10\xf8Tp\xfb\xc95\x02I\x9f\xfb\xa0\xc4v\xcc\xddv6\x93 ~'\xf4\x8a< \xda\x9d\"\x93\xbf.\xb6\x9b\x04u6\n\xfdk\x1e\xbbL\x14#8\xac\xea\xa2[7\xc6\x00\xfe ,\xdc\x0d\xb8\x934\xbc\x8d\xa1\x7f\xcf\x83dB\xfe\x0f\xa6h3O\x82\x05[\x9e\x16\xfc\x13\x03\xde\x96^\xd1G\x1a\x1e<\xd4?\xf5 \xe9j\x98\xf1\xc4\xe5q\x16%\x0b2=\x0f\xfe*\x96j:\xf9\xb5\xfc#w\x8du\xbf\x1a\xef\xee\xf2/\xe1i\x1c\x85)'C%\x9f\x7f\xfbcu\x13\xee\xf10\xf3\x9d ]`\xad\xd4\x19qEg\x1b\xe2\xe0\xf4O\x91\xb7&\xa7\xf6\xf2OP\xc98[\xa8\xbe\xe2y+\x8d\xc2\xee\x1f\x1c\xff\x83\xc9\xe4\xad\xf9\x94\xdc\xed\xccdC\x1e\xb6\xfb]\xd6o\xb8$\xb0Bj\x96\xc9r\xc8\xa6\xd5\x8c\xb4@x\x1d\xa2\x1d\xcc\xd1\xec\xb2V\x11*\xa4i\x8a\xf9\x08zG\xab\xe1\x0d\xf4\xaa\x1553&Nx\\N\xdf\x01r\x95\x11G\xfcg\x01\xc4p)\x90Ws h\xdf\xa8\x92\x1d6\xebLdT\xd9a,\xa8\x85\x90\xb5n\xc2\x02\xddT\x93\xbb B\xf8\x04\xbcQ\xae#\xb6\x04n\xfaW\xb3I\xe4\xab\xcd\xff\xb9V\xb7\x0d\xaa\xdbh7\xe3N\xb7\xb9\xc6)\xa2\xce\x8c_\xfe\xddm\xb2\x0c\x97\x7fU+qe\xb8pc@\xcc\xd4\xfag\xbb\xd9\xb0\xda5i\xe7\xd3\x04\xd8L\x8a[113\x8d\xd9!u\x10N3v\xd5\xa3\xd5B\xb3\x0d\xd8\xf6S\xb3\xb6\xbc.g<\x98 \xd1)]\xf0nQD\xe6;m&=\xf5\x98\xdc`\xed,\xa2\x88j\x1e\xa0\xa2\x9b\xfa-\xfb\xbf\x90\xb5k\x82\xe7O\xf5\xab \xca\x99\x9f:&\xe7\xab\xf2 \xfa\xed\xda\xe5\xbe\xace\xf3\x85\x9e\xa4\x1a\xf32\xab\xe2M\xdf\x8e7\xf6\xba\xea\xdai\xbaH\xb9t\xe6EG\xca}\xe9x6j7u\xdba\xfb\xf4 \x12\x9c\xa6\xee\xa8N\x9c\xb0\\R\xc9\x00NZ\xc5Q\xa0\x93\xb3\xb3\xb6P\x04\x00\x11\x0bm\xaa\xc6pr\xb6\xe6\xecXB\xb9\xfe\xe9\xc5\xb3}\xcd\x01\x18c\x95T\xb2\xda\xc8\x80gk\x91\xeb\x04 `-4\x9b\x03\xb5\xf7\x834K\xc4N\x92\xf2\xab\xceHU\xed\xb4\x0bi\xa9q,\xbf}bf\xec\xd8g\x0fw\x130Tk\xfb>|op6\x85\xf3S\xb9v\xc0U'^w7_\xa2\x96\x169\x9b\xe9\x87`C\xef`E\xb9\xee\"^O\xe9\xb9\\#\xac\x06*}\x99[\xb9*\xa0\xf2\xb7<\xb7\xe6\x9cFh9\xda\\)\x1f~\x97\xf96\x03\xbf9\x0d~\xfd\x1dIh5\xe2\x87U#>{\x8d\xb5\xa3&\xfb\xbdR!:\x02w\x9f\xab\xd8n\x12\xb4[\xe2CU\x89\x08KV\xfd\xc2\xa8?\x93'\x81@2x\x81]HH\x99\x8a\x84#\xe7%\x04\x03\x89ED\xfd\x06\x9f\x9f2\xe6\x0fx6%\xa6q\x15\x0d\x83\xdf\xdf\x94\xf6\xfc\x05\x19J\xf8\x0d\x9d\xa5v\xef\xe8*\xe1q\xde\xf6\xda\x9f\xf4\xf0\xf0\xbf\xbc\x87\x07e\xb0u\xb1~\x82U\xdb\xef>e\x00\x91\x8e\xad+\xc5sE]\x96\xce\xecn./\xee\xac\xdc\x84\xd8\x86\xed A\x0df\xef\xe0\xb9\xf1j\xb4J\xa1\x04\xd0P\n\xdc\xeb\xce\xc6\xf9\xf3k\xd3\xf6\xfa\\1)8U\x89\x19\xb2\x8a\x05;\x82\x02=\xa2o\xc2=\xf7\xf3\xc9\xd3\xd7\x0d[\xb5\xd9\x1f\xa6\x91\xad\xa7\x90o+ \x16\xea\x8b1e-\xe0\xf8\x15\x8d\xe7\xd09\x9f\xfb\xbe\x91C&\x1b\x95c\xb4[xtNa\xb2f%\x84\xda\xf7C/\xda/.3\x86NZ\x93\x00\x0d\xff\xb2\x99\xc09\x8c\xf2L\xc7uKJ\xbe\xccy\xbc\xe6\x87{\x17\x9ct8\xcd\xfd\xd2\x04\x1b]-\xf4K\x98|\xc4\xae\x9a\xfc\xb6\xb5\x1b[\xf2\xcc\x99\x90\x06\xc4$\x1d\xdaq\x06\x0b\x85\xbb\x10\x1dJ\xe5\xcb\xdd\"\xd1\xacEUq\xa4\x9a`UU\x00\xf4\xb2-|\x07@\xdf\xb1+\x17\xce\xd7'W\xff\xf6 \x89\xbc\xcc\xd8v\x93(\x08v\xc0\xf5.U\xffPw\xe0\xf2[\xc2\x1d\xefp'\x82r\x8a\xb8\"\x1c\xae\xd45!X\xcd\x0e\x8f\xfd\xda\xb8\xf6\xbe5\xf2\n\x0c-'g\xb1\x97d\xaej\x9c>AR\xa34\x86\xb6c\xde(\xdf\xa0l\x07V\xac\xe8\x7f}X\xc1\xd4*\xc5\xe5e\x9cH/\x0b\xc67\xc9\xcf\x06\x9c5\x81&5\xc4\xbdLKp+\xef\xf8c\x0f{\xd8h-\xafU\xde\xc2\xcfT\xee\xe3\x08r\x1f\x17\x9e\xf6y\x8d\x99\x1e\xb2*V\xa9y\xd4\xe9\xb2\xb0\xdd\x91\x8f0\nT\xf4\xc3Ag\x8aG`\xc5\xfeG\x13#D\\Yj\xae\xe1\xd6 0O@k\xa14\x10Bi \x84\xd2\xa0\xa1\x9eV\xa6\x13!\xef\x8b\xe3#+\x9fK\xa2\xd1j\xba=\x8c\xf6\xc3\xef\xf3C\x89\x88u\x0d\xc8\xdca}\xf4:ls\x7f1\x8d&\xeeO\x8e\xa5\xf1\xd8\x19\x16O\\\xa9\xa1,\xd5\xb4Rr\xc0n\xa7\xac\x9e:B\xcc\x12\x93\xef\xc8\xa4\xa2\xf5u\xe7\xe5\x9d\x8cyX\xf65\\\xbb-\xe3\xd0\xe1\xcaA\xd3\xa4M'\x83v\xd9Q\xe6Iw\x16\xf1\xd7P\xaaTs\xd5\xf6^z\xe9\xb9\x1b\xac\x8b\x84\x98\xea.\xbe\xaa\x07N\xff\xb2Z\x95hT7\xc4\xc3\xf4\xb7\xf9j\xa4\xd6\xd8\xca\x8a\x8b( \x107\xa1\xcd\x9bYTs\xfdd\xae\x9dp\x1eIE\x06\xafs\xfaTW\xe3T\x86\xb5\x0cf\xaa95[GX\x85RV\xe4\xb2z\x0c\x9f\x92`2\x85\xe6`z)\xa8p\xa7J\x9f$\xbbh\xc2\x8f\xb1\xc9\x06\x04\x0f\x90\xcc5\x1c\x8d\xd6\x11\xf08\x13\xc4\x8c\xe9\xcc\xf9\x91\xa9\xd8\xe9J\xc4o*\xd1L4|\x9c\xf9w\xfah\x12\xfd\xd3'\x9e\xebwhT\xba\xdd\xf6\xf1\x9b\xc7\x07]\xd6b\xad >\x1c\x13(\x94#\xe9\xa8o\xe8\xa6\xa0\xa2\xbb%\xaa\xda\xf6\x1b\xe6\x18J\xfe\xdav\xba\xf0\xdc@h\x8eP\xdby!\xe7rl\x95\x9f&2\xf3\xa9,l\xac\xe2\xf7\x8b\xd0S\xe0\x9f\x96\xeb\x043\xa9Y\x03\xd7xi\xf9i;\x01\xfd;0Z:\xef\x80\xe1:D\x1a\x0c\x92\x11%g\xc7e*\x92\xa5-t\xacq\xddF5\xb2\xe8\x8b[\xb9f!A\xca\xbd`&\xec\x87\xc5Zn:\x89\x98/\x17\x92\x8cY9u\xd7-\x0b\xc8G\x1eg\xb2\xa8\x96\xac\xff\xd68\xc4@\xae(\x96\xf7\xa7\xb1\xd7O\xc3%d\xbb\x8aWP\x87\x1340\xbb\xe5\xa9\xda\x8d=\x9e\x01m\xc4\x94f\x04M\xf0\x8d\x97\xaf\xfeC\xe1U3\xe5\x97\x84|\x14\xe7\x19\xf7\xb6\xb3\xc3@\xe6#\xae\xad \xd6\xb4\xe5\xf4\xd2(\xc83\x95S;\x99\x89\xa3T\xc6\xea\xd4W\x93\xf1\xf7\xec5v\xbc\xed\xe4Y\xf4#X\xc7\x1f\x0d}\xcf\xe3a\xe78[\xa8\x02:\xc7\xeb\x99O\xab\xef\x1fp\x0f\xf7\\\xbc\x90f\xafidx\x99^\xf0U\xf9\x1fG\xf0\xe0b\x91^\xad\xa7\xd221\xbdm\xa5\x9cN\x97\xb5\x8f\xc8wTZi\xe6d\xbe\x0b\xae\xd3\xe5\x81\xbd\xf4\x12\xf3eZ\xe0v2\x13\x8dy\xd2\x0f\xa2}v\x94\x15\xff\xb8Z\xf9\xd7\x1b\x9d\xc2\xdd\xde>\x17=\xd3IX\x88\x14\xc5 \x960\xc0\xf3\xdaT\xa9\x93\x8d_\x88\x96-\xb0\x86D\xe7\xba\xec\x02\xab\x89q\x13\xbf\xcaQ^`\x83\x06,.\xb3\x9f\x056\xae/I\xa4\xae\x056\xb4\x13\x1f{\x1b\xa5{\xe9\xfa\x95\xa8r\xa6i\x1d\xbf\x18\xc3\x9e\xccM\xef$\xf5UZ\xac\xed\x01\xb4_\xd4{\xa44\x8b&\xa9\x1e^;\xf1\xbb,\xb7SgDX\xb2\xa1\x9fvY\x9d]\xd5\x08\xc1\xa9\xd5\x90\xed\x1aCv\xda\xe9J\xeb\xed\xec\xab\xac\x0f\x8f\xf8\xf5\x8f\x1e\xed0\xf7z\xbfj\xc8\xee7\xbf\x16/\xd8\x9cO3\xa7\xc2 \xe5\xbb\x83\xc1\xcc\xcd\x9b\xd2\xb9\xec\xe6M\xed\x12]\xf2)\x0f:\x1d\xe9a\xa6L\xe2\xbc\xcb\xae\x8b\xba&\xc9\xb2\xdb\xe9\xc8\xf0\x99(\\\x8b\x1co\xa2\xfdL\xff4\x07\xf6g\xe2$\x8a\xd3\"\x93\xc2L\x16\xc1\xc1j\xca5\xc0\x14\x17F\x92G8\x939\x83\xae|\x04U}]\xf5\x1a8*\xbe2\xadH\xb0\x82?\xd4\xe9\xc4p\xc3\x10\x12G\x02{V\"J\x96K\xe6\xe9\xbc\xb4\xd2\xf06<\x92I\x82.\xaby\xf6hO\x88=\xad\x84\x87\x1eOj\xcc\xa6\x8a\xdaL\xbc]a\xc5\xa0Rdq0Q\xaai\xec\x84\x84\x9c\xd1F\xfa\x0b\xf0\x9c\x04\xe0Cm\xe1\xbb\xdd\xda\x9e\xb8z\x90B\"F\x1d?\xa7\xab|\xa3\xd3E)\x19\xee\xb6\x8b.\xcc\x15\xf37\xda\x87\xe7\x1bG\xfaCi\x176\xff\xfc\x1d\xd9/\xfd~G\xf6\xbf8\xd9\xb7\xe8\x85\x9a\x13d\xce\xe0\x0b\xd3\xec\xf0w4\xfbw4\xfb\xab\xa6\xd9\xcf\xe7\x1ag!?\xb5It\xa28='\x13\xb2=\x87\xe3R10\xc4Kt\xba\xaf\x93\xb3\xa7-L\xe3E\xe5\xfb\xfa\xe6\xeeG\xa3\xb7(\xc9{gy/\xa5TA\xbe\xd5~\x86\x85&`\x13\x87\x0f\xfc\x97\x85\xa1\x93\xcc\xd4l\x8a`\xa8)\xed\x19\xcc\x04\xeaB$\xf9tlD\xff\xa6\xf5\x1e\xc2?U/\x91\x0f\xc0w\x1b\xbc7'\xb6f7\x9a\x19h\xb3\n\x03\x13\xbf\x98F!\x9e\xfc\x146L\xf6%\xe6os\xe3jwf\xa2P\x90\xdc\x80g\x96G!m?\xb3\x8c/\xbd\xc4Zz\x10\xe5@\xcdP^\xec\xa6<\xdb\xf1G<\xca\xa5\xbb3<\xb8\x7f\x86\x1d\x99\xeb|\x95+_\x0b\xad1s\x92\xaf\xd3\xd2Y9\x15\xeb\xa1/\xefF\xf9\xbd\xc6\x96\xe7d\xce\x82?r\x06\xfcx:\x1e\x1c=\x18\x05\xaf\xf6\x9c\x94\xbf|\xb2\xbbya}\xfe\xda\xe1\xd9\x13\xce\x95\xadYgy\xd6\xbftkq\xdf\xbd0\xf0W\x97\xceF\xd7\xae\x04\xa1s\xe1\xf5\xd3\xab\xb7V\xf7/]8{r\xd5_\x1c\xf0\xf3si/\xbctzu4\x9c\xf5.,\xbe\xbcvx\xfa\x84w\xc2\xcd\xbd;\x97\xf2\xde\x89\x8b\xe1\xda\x9d\xd5\xfdK\xcb\x8bc\xf7\xc4\xb5p\xd5?;\xef\\\xb9|\xe2\xf5\xd1\xe9\x93\x9b\xdb\xab\xfb\xab\xcb\x8b\x83K;\x8b\xfb\xab\xcb+\xfb\x97\x96V\x07\xee\x85\x8b\x81;\x7f\xf9\xd0\x1b]>\xeb\x9e8\x1b\\=\xb1\xb5}\xf5\x8d\xad\xb8wg\xd6\xe7+s\xf1\xb5s\xc1\xbas\xe5u\x7f\xf5\xfczz\xf5\x8d\xf5;\x9b\xdb\x17\xd3k\x17.e\xee\xe8t\xda;\x1f\xe4\xd7\x0eW\x07\xee\x89\xadS\xbd\xf3\xbb\xa7WG\x17\x87W\xe7\xb3\xd0\x1d\x9d\x9e\xeb\x8d^\xcf\x9c+s\xc3k\xf3\xbb/\xaf\x9e?5\xee\x8dv\xbf\xb3z\xbe\nw\xcf\x9f\xbe\xe3\x88\xbe\xe6O\xbe\xbcz>\xc8\xc5\xdfW\xaf\xec\x0f\x9c+\xa7b\xef|0\xec-\xa7\x83\xab\xa3s\xb7\x9cy\xef\xb0w\xe2r~mi\xee\xf0\xda\x1bg\x83\xabo\xbc^W\xde\xdf\xbcup\xcby\xe3\xe2\xad\xde\xf9\xdd\xc1\xd5\x13\x83\xd3\xab\xb7v\xf7W\xfd\xb3\xb7\xf8\xce\xac\xbf\xbe\xb3\xe8\xaf\x9e\xbf\x16\xf7\xce\xef\x9f^\x1d\xc91\xf9\xab\xe7O\x85kW\xce\xcdz\x17V3\xf7\xc4\xd6ao>\x0b6\xb7/~\x87\xcf\xaf\x8f{\xa3k\xf1\xb5\xc3S\xb7z\xf3\x07c7\x9c;\xbd\xea\x9f\xcd\xaf\x1d\xce\x0d\xbd\x0b[\x87ko\xac\xcf\xba\xa3\xd3\xc9\xb5\xed9\xb3o\xfcDv\xab7\x7fj\xe4\\qso>\xd8\xf3\xce\x0fO\xf7\xb7W\x07\xbd\x91\x9b]}ck\xd6\xf5\xe7\x0eQ\xdb\x87W\xafl\xc5\xde\x1b\xeb\xb8\xdc\x1d\xef\xc2\xc5\xb13\xbf\x9b];\x7f\xee\x8es\xfe\xdc\xa1;:w\n\xd5\xdd\xbb\xfa\xc6zt\xf5\x8d\x8b\x87W\xdf\x08d\xfdb\xfc\xab\xb7\xd6wv\xe7\xc4\xffV\xfd\xb3\xa6-\x18\x93X\x93\x15\xb1&\x87\x9b\xdb\xabw\xd6K\xf5\xd6\xael\x0d\xdd\xf9\xe1\xd0\x0d/\x0e\xc5z]\xda\xb9:\xbbvk\xef\xce\xa5;W\x0f\xd6\x97/\x1d\\\xba\xf3\xfa\xfc\xfa\xf2\xca\xdc\xea\xf2\xee\xfc\xda\xad\xbd\x13\xebw\x06'.\xed\xbc~g\xfd\xce\xe0\xf0\xd2\xce\xa5\x93\xab\xb7N\xber\xf5\xca\xa9\xb8w\xe5\xdc\xec\xb5\xcb[\x87W\xaf\x9c\xbasmt\xfa\xb0\xb7}V\xae\x99s\xe5\xe2\x9cw\xfe\xf2\xc6\xd5+sb\x8dg\xdd\xd1\xb9\xdc\x9d\xbf6vG\xb3\xfe\xea\x85\xadS\xae\xc0\xa1\xf0\xe2\xd8;\x7fn\xf6\xda\xf6\xea\xe0\xea\xfc\xb9\xf4\xea\xec\xdc\xf8\x9a\xc4\xad\x83\xb87\xbau\xf9|\x90]{\xe3\xd2\xe9\xd5[\x8b\xdf\xb9\xb4\xbd:\xb8v\xe1\xb2\x98\xf3\x81{\xb8:\xb8:\xba\x1c:WN\x9e^\xbdu\xf6\x8eX\x0b\xc0\xab\xade\x81g\xde\xf2\xac\xef\\9\xb5w\xed\xca\xb5\xb87\n\xc4X\x8en.\x9d\x1e\xf6F\x81\xd8\x9f\xe0\xf2\x85\x8b\xc3^\xb8>\xea\x9d\xb8\x98m\xde\xda\x1f_\x9d\x0f\x0e\xaf\xce\x1f\x04\xe2oq\xe66\x07\xd1\x99\xd67D\"X\x8a\x82\xc0\x89Sx\xbab\xcd\x0f\xf7\xe4\x1f\xe0\xcb#\xff\\\x0d\xe3\x1c\xfe\xda\xe1\x07\xd9b\xc2!\x0d\xea\xd9<\xcb\"\xe0\x16[\xd2KX6\xa5\xfe+\xb3}\xcb\xb7{\xeb\x82\x11\xa5\xff51Ch\xcf\xecW\xac\xafS\xf6mF\x10G7f3i\xf4mF\x90T\x01H\xef\x81\x02\x10#\x88\xab\x00\x15#\x88\xf4\x13\xb7\x9b\xbf\xbf&\x87m\xdaqLx\xbd\xb10p\xab\x85!3\x16\x06\xae^L\x98}\x95\x85\xec\xbb\x8c\xbf\xca\xc2\xa3G;L\xc5\x0d\x17\x16\x86\x10\xa9\xe1jb\xd9tI\xa3U\xe9#G\xd0\xac:3\xb7\"?l\xb7X\xab3\x93%\xfe\xa8\x8dEg&\xb5\xfc2f\xd5wd\x96#\x9b\x14\nLl \x99R\xdbSb\x1c\xc9\xa8a\xa4|G\xdc\xe9(\x99\x05\x8a\x17\x12K]\xec+\x1aIPj\x0b\x9e\xdfE6\x85\xccj=\x98`9\x98\xd6j\xa0\x11\xa4\xd0\xd6\xebET\x95\x834\x0f\x82\xd4M\xb8\xed\x81)\xfd\x0bM\xc9\xfa2\x96\\q\xbc\xcb\xae\xb7\x8a\xf6e&\x9d<\x08j\xdf\x1e\x93\xc9\xec\x8cg\x8e[k\xf5\xe0 \x88B4\xaf\xad!\xed\x84\xd4J\xf7\x9d\xc1\x80'\xc7\\\x8dn2\xabN\xc8^c\xadcr(l\x81\xb5\xea\xbc\xc6\xa7\x1fG\x9b>3\xe97\x99e\xdc\xc0I\xd3u\xf9XZ\xdc\xf6g\xcc?+\xafj\x95\x7fw'\xbb>\xde\xe8Tb\xfd\xdb\xae\xc5\xceR\xa5\xde\x1e\xf1\x97\x1bE=?\xe0bI\xaa\xfb\x9c9\xbd\x80g\x0b\xacu\x0c\xfeB`\x8f\xa7{Y\x14\x0b\xb8\xfa\x13\x15\x08\x9cd \x9a=6\xf4JW\xb3\xafV\xe8A\xf0;J\x00\xbf\xdf\x1a%\x18\xfa^CV8\xa0\x01{\x9c\xc7K\x90\x8d\xb3\xa1=I\x0b\xf8\x0c\xa0\x93\xd0\x02\x01m\xba\xd2\x9bB\"\x88\xf8Sb\x05\xf1\xdb\x90DC\x0cE\x90\x8brw\xe2\xdf\xd0\xa2|\xabQ!\"k\x19\x94c-\xd9b\x8b< k\x86%\x93\xf1\xbe\xf4\x12;\x12NAe\xc0\xb6*C\xe8\x9b\xa9\xcc\xf5\x1a{\xb6\xe1\xd89\xf3C\xe65\xbb>z(\xedG;\xefL\xd2\xf6\xf5u\x83W\x1b\xec\xa4\x7f\xa2\x83\x1c\x1e\x0d2F\xdc)L :\xc8\xa9\xa85\xb1'\xa6z\x0b\xd8w\xd9\xdc4}0\x99\xd4Q\xbe\xe5\xd2\n\xa3\x90\x0b\x02=mT\xad\xa0\xea~\x98O\x91hob =\x84^\x10\xb9{0\x86\xae\xf9\xe8F\xc11\xf9(\xa5\xfc\xde\xd8\xd6\xf3\xda%t\x0cW\x8c\x0c%\xd7K\\\xc1\\\xca8u\x88=\x11\x97\xbf0\xa7J\xb3\xc3\xa0\xf6yl\xfd\xf3\xfc4\x0e\x9c\xc3\x05\xe9}\xacv\xd1\xf2nG\xf9\xd7`9+1\xc7\x9a\x14J/\x86\x19v\x8d\xc2\xf3;\xb6\xf3\xe2\xd8\xce$T\xf4\xfc\xb1\x1d\x0dK|jZ\xc9\xa9\xa8R\x16\xa1Z\xfb\x89\x13\xc7<\xa9u\xd2{!\xd8S\x1c\xc4vI\x85\xfe\x1d&}}\x98\xd4\x93\x8b\xfeU#\x93\xea\xe5+\xc5\xa5\x8e\xfe&\x98?\xcd\x91Y\x1af\xabF|.\x19t\xeaQp\xd2\x82f\xfc s\x12\xee\xb4*\xb7\xec2\xb5\x936\x1d}\xf1\xc6}\xd1\x02j\xb9r\x86\x8c\xa1j\xaa3Tw\xa1Ws\x80(\xdb\xd4\xe6\xab/z\xb0dV6(-\xc7b\xe9b\x08\x85lo\x81\xeb\xe8\xcc\xba\x17 \xd4jB\x00\xa7<02\x15&\xfc\xb5\xc0\xf8\xcc(\x0f2?\x96V\xa7\xeb\xad\x96\xf4\x0bo\x89S \xaf\xf6j\xb3\xac\xaa\xa3\x17Q\xa4\xedZ/~\xf5\xef\x1bC\x13\x9e_\xa9Q\x0f\x0d^\x16\x1d4\x14\x06\xedF\xafj}\xb9\xa4hte\x14g\x87\xb2\xdd\xfa\xe2\x91\x1e\xab\xdc\x17\xd8?\xf9<\x12{\xcd\xfe\xbd-\xb3u!\xc8\x17\x15\xfa\xc4\x81jt\x0f)Q\x16+\xf9\xab\xad\xa8\x17\xaa1\xab\xac\xc6\xb6\x86\xe5 \x97\x86N8\xe0\xc6?\x05\xfei-/P\x94\xbdV?\xdd(V\"n\xfdt\xd5\x80Z\xf6d\xd6w\xbb\xacu\xecX\xab\xa3DWA\xf6\xaaq\xca\xd3\x054|\x99\x012}R\x1a\xa2 Y1\x91m\x999\xb7)}\xfd\xddnQ\xe8\xb7\xc9\xc2\n|92\x87\xac\xfe\xd5\xa3T\xbd\xd7\xa8\xda\xab\x86\x93BM\xcb\xd4\x81\x9e\x99\n\x8a\x95\x9b\x9a\x18\xf2\xc9'\x91\x1a\x08\x9e\xd6m7\x93\x83p\n*\xe3K\xab\x02\x84\xd7+N3\x939\xc9\x80g3\x80Ei\x83\xf3\xb43\xe1\xa5\x1b\x01\x8f\xd8k\xcc\x9f\xce\xd0\xaf\x7f\xc6\xb7\x06\xe8\n\xb7\xfb\x91\xdd}\x9e\xe0~\xd3\xa4\xc4\xe7\x9a\xf6\x04=\xd4\x93\x97\xe5\xba\x103\x04\x81!\x13\x0f\xbbS\xd3l\x17\xdc\x1a\x12[\x88>\xc2\xff\xeaR\x8f\x85\xd0`.\xd8\x9a':A\xe8g\xbfe\xc1\x9f\x91\xb9\xb2\x17\xc2\xec\xd9d\x86\xcf\x9e\x83\xe9\xb3)\x88\xab\xf3e\xf4\x00\xe8 X`\xad0\x8ab\x1e\xf2\x84\x85Q\xc2\xfb\x9fCe\xd5e\xb0\xce\xb6\xd1\x8c\x98c\xf3\x04\x9d;\xf4\x03/\xe1\x96\x90\xeeIK\x0e\x9a\xbc}U'\x9a\x8d\x86\xdc\x1f\x0c\xe5c\x13ymR\x18\xf1\xebE\x89\xc7\x93\x05eUj\x10H\x9cd\xe0\x87\x0b\xac\xe1\xa1\x92\xd8\xf1\x95\xfa\xf2O\xc9\x04\xb0\x1ee\x8b\xa1?r2\xee} \xc9_\xdfN\x17'\xccO7\xc4Y\xf5\x1a\x84\xc2\xb1\x8e\x19,\x1fL\x85\xf0\x82\xb1\xd4\xe2v\x18\xa5n\xe2\xc7\x99\xbe\x00\x98@6\xef\xda\xce\xc1oO\xe5Q\xab=I\xdb\xd1\x0b8I\xdb\xa9'\x11\xac\xb41\xec5p:\x0e\x95\x8f1,\xfc\xc4\x9dI:F\xe3!\xe8by\xb3\xe3\xc5\x8b\xa6z\x15,\xa2\xa9\x1a\xc6\x82v\x00d\xec\x9b\xe1\xffK\x9dp\xbcZ'\x1c\xcf\xe6j\xe3\xeb*6\x1f\x1c\xcf\xe6j\x93+\x8057\xa2gs\xb5 \x14\x80\xe4\xecw\x15\xe0\xf4+\xa71\xa8\xaf@sd`\xb1\x86\xd8\xfdt\xbc\xaf\xc7OG\xffE\xb4\x91\xe7\xa5\xf5E\xfcQ\xd2\xb5\xa5 \xc1d\xbc\xd6\x8c5!\xee(\xa8\xc4\x1d\xb9\xe0\x15\xe4B\xdc\x91{\xf4h\x87\x05\xd7\xdd\xaaW\x90k\xb9\xe0SK)\xa8\x866\x99\xe5\x84\x11\x81\xdf\x19aF\x115\x9b\xd5\xc5\x1c\x052\xe6(\x99\x19\xf0\xecR\xe4\xf1@HO\x13E\xec\xd2\xf8\x94\x17?7^\xfc\xad\xdf;^z\x15\xfbxKf\x93+2\x87\xfd\xe1\xcc\x1f\xfc\xde\x0f\xca%~p\xfcx\x97\xb5\xa4\x05\xc0\xd6\x96k\xd2\xd8\x1eO\xdd!\x1f9\xa4\xc9\x9aB\xbaQ\xd0\xca\xc8\x14\xee\xaaIo\xf1\xfe\xb6\xac\xf2<\x93N\x14[\xab\xbc\xbf;\xd3\xf7C\xafx\xde\xdbf!\xb8\xdb\x85\x9c\x14\x84\xa1'\xc4 \xa5V8H\xad\xc2\x81\xf3<\xc2\xc1\xd7\xca\x18Uj!\xb9=\xcdJ:\x9f\x98\xff\x94)2\xca\xa7}\xf9\xd8\x81\xc2r\x83\xebK\xe5\xb2T\xc2o\xe7~\xd2\xc4\x99SY.l4\xd2\xb9\x8a\xcbo\xf1~}\xa1\xbe\x99\xc3f\xeds\xf9L\x11`>\xa3nz\x9b\x8d\x832\x8dd\xbb\x05\xecN\x9e\xe4V\x83\xb9b\x08\xa5%\x95\x9aXx\x0c\x857\x13\x7f\xe4g\xfe\x98O\xac0bgX+\x92#i\xd0\x1e\x06\x82\x04\xc2\xab\x902)\xd0\xef\xff~\xc2\xfbuna2 \xa9|\xccx\x00\xe1\x0f\x1a\x07\xcbt\xab=\x10\xb4\xec\x88S\x14sJ\xc5\xccIo\xa7P\xcc\xb8\xa3\x04\xb5\xd6\xdcI\xa1~\xe5[\xa2\x91\x18\x06\x93\xff\x7f,\xf3\xb3\x80\xd7Z<_`\x7f\xd0\xd3\xcd\x9b\x19?\xc8j\xfb\x8b\x05_\x10\xbc\xa8\xb6c\x7f4h\xec7M\xdc\x05\x16\xb6O\xce\xcd5!\x95V/\xe7g\xe3\x83\x86\x8d\xdf\xf7\xbdl8\xb9\xd8Du\x96\x19\x15t\x8d\xf7E\xbfs|4\xe9\xa5=\x95\xbcL\x92\xc2\xc0\x11\xd8<\xa1F/\xca\xb2h\xb4\xc0Zb\xb0\xb5%k\xe2_\xea\\G\x04\x15=\x94\x89\x1a\xfctcq\xfbD\xbbS:\x07\x1e\x8f\x13\xeeJ\xcd\xad\xa6z\xba\xef\xcbL\x84\xae1:J\xbe\xe9\n\xa5\x8c-\xb0#G\x06]y\x06\xcb\xa7+;\x8c9\xbc\x997j2\xf9\xb8N\xca\xcd\xd9]h\\\x99 \x87\xc7\xa3\xb6\xa1\xc6\xe6\x18Bo5\x86\xc6:\xcfelb*\xc0N\x90\xdc\x05\xd6@\x9d\xf5\xaf\xe0F\x8d\xf7)\xfa\x07\\\xa6\xf1\xa12\xfd\x0b\xe5\x14\xa7xL\xbf\xc0\x85\x05v8\xb9\xb8d;\x0b\xccm^\xb4\xa6\xcc\xb1\xb0\xff\x8e\xe0\x0b_n\xfb\x87_r\xfba\x08/v\xf7\xff\xf1m\xa8\x96I\xea\x1e\x8b\xd3\xbf)\xf6T\xbd\xf8X\xbf\xa9P,\xccG=\x9eL,\xe6\x87\x19\x1fLQ\xae\x17E\x01w\xc2\x86rZ\x03\xfc2\xc86\xfe\x92vh\xa6\x91C\xc9\xa9\x13\xef\x02\xd9\x7f\xe9\xd8d\x85O\x8c\xe7\xac\xb5\x0c\x95\xb0s(\xb7d\xe70\xe6\xd4,\xa4\xd7\xa8o\xf6YZ\xa2\xb9w\xc9\x89\xa5Lm\x93\xd0\xab\x1b\x17\x9b\xaaB\x97i\xae\xa46o\xca*\x15\x95\xa3\\\x0b8Um=\xd8\xcd\xa28\x1c\xc4j\x99\x92\x88?\xa9\xa8\xa2\xf1E!q\xc4\xaaE\x8a}n*\xc5\x0fbG(\xac\xb1`\x87EA \x00hx\xd3\x14*\xf1VS.\xf0\xd3\xf2\xc2\x14\xa8Q\x8d\xa6\x87L\xa5\xbf]\xfb\x9e\x18Q\xea\x08\xdd\xfd\x8e\x0c\x90\n\xa8\xc1/\xb7Y\xd6\x84\xe6\xda\xce\xc1J\xd6\x95EN\xce\x9d\xea\xd8\x8c\x7f\xb2\xd0\xec)\xab\xfdO\xc2\xe6N\xd8\x0dm\xf9\xd7kh36\xb0\x19\xc7\xf3.D\xd1^\xbb\xd5\xe3\xfd(\xe1\xdbjy\x14\xd9M\x1b\xd3:\x9a{\xe6a\xc2\xfb0\xcc\x94g\x8bY\x96\xf8\xbd<\xe3m!\x80\xb7\xba\xf6\xdb\xbfN\xb74LlzM\xa7q\x89;\xfe\x87\xd7\x17\x8f]\xfbA:{\xec\xf4\x91\xd7~0s\xe3\xe8\xef\x1f\x1f\xa8d\xc5Ug8\xba\xda\xf5i\x98\x8a\x85\xd1\x88\"\xf0\x94\xae\xf5\xe2\xf2\xf2\xcd\xc5\x9d\x9d\xad\x05v\xbd\x05\x97\xe8\xadj\x86P\x92\xda\x82\xd5\xe6c\xc2C).\x11\xd3(O\\\x8bE\x00\xee\x19\x1a\xfc\x89\xfcBm8s\x06\xee\x0eZ\xd2w\xbc*B\x08\x95;mgE\xd6\xe6\xa4N{\xac\xbb\x94\xach\xabN\xb2\xe7E\xfbaU\xa4\xbbK\x0d\xac\x10\xbbq\x86\x85|\xbf\xb0c\xd6\x08\x8f\xc3l\x14\x88clg}\xd9a\x1c\x0d\x12'\x1e\xf2\xa4\xbeP/\xe1\xce^Z\x0f\x0f\xfcp\xcf\xef\x1f6\x17\xd8\x91\x9b\xbc\xc0Z7{\x81\x13\xeeY\xd2\xa8w\xd4EK;\xb3(\xd0\xae\xcc\x12\x96\xa3\x850w\xff\xafI\x15\x05\xf8\x9fq\x8d\x91\xe3\x8aa\x7fJ\x86\xa6\x01\x04\xb1FN \xd6\xeb\xd9Gx\xd7\x17/m.\xb0\xd6K\xa4|l\xf9\xba\x18J\xccy\xfc\xe7\xb84|\xbf\xf7!\xfd\xae@\x8f\x7fNA\x00\xf8K\nH\x83H>)\xf1\xec\xf1_P\xe0X\x02\xfe\x1b\x02\x90\xb3\xbbGvDz\xa6\xb6\x9e=z\x9f\x02d\x94\xac\xb5\xca(\x85\xf9`,\x02\x90\xe3\xc8\x16?\xb2\x03{\x12\xf8\xd8\x0e\x94\x07\xf2\xd1\x13;P\xf6\xf9\xe8\xa9\x1d\x08\xb3\xf8\x1b;P\xe2\xfc\xa3\x7fm\x07\xca\x85y\xf4?\xda\x81\x12#\x1f\xfd\x1b\nL2\xb9\x02\xbf\xb2A\xc6r\x8e\x0f\x08]\x01\x18L\xe3\xaf(0\x05\xfc\xbfGhE8HEo\x9f\xfc\x84\x02\xee8\x89\xc0\xe7g\xff\xfc?`T\x8c\x06\xd2\xee\xfa)9\xd0\x1a\x80[[\x8c\xe2>\x1c\xf5\x7fO\xaa(\xc8\xcf\xff%\x86\x88S\xf0\xec\xfe=\xf2Y\x10>\x89\x88d\xe9bID\x1fcJ\xe6\x00F\xdf\x7f@\xbe\xfbr\xc1\xee?$\x80(]`\xado\xe3Y\xc4qpxN1#+\xa9s\xe28\x89\x0ej\xc6-@\xfc\xb6u$\x8b\x89\xf4\xac\xb2l\x83\x06|\x80k\xa4.\x10\xcf\x7fI\x0e\xb1\x81\xfco\xa4N\xea\x0f\xe4\xc0\xef\xff\x8cT\x12X\xf0\x07\xe4\xeb\xe1\xa8f\x17\x04DM\xe6\x9f\xe3n2?\xf0$\x8d&L\xd1@\xfe\x07\\'\x17\x02G\xeb\x13\x82Q\xea;!!\xfbn\x14\xfa!\x1c\x14\xcc2\x9d}\x05\xf9\x08S\xf5\x9e\xe3\xee\xb9\x11\xd0\xab\xfb\xefZ\x80Z\xcf\xee\xbdG\xa0\x89\xa4\xbaO1}\xef9\xc9\x98\xcb\xb1<\xc0\xfd\x9du\x92}.1\xfb]\xcc\xbb{\x05\x08\xa3\x1a\x80\x80dS`/\xd9\x13\x80?%\xf3\xee%{\x99\x06\x92%\xab]\xeb\xb3 s\x90\xfd\x81\xcf\x98\xe7\xf6\xbc\xdby$\x97\x1dK\n=\xee:y*W\x0e\x8f\xec\xac\x04q+\xac\xd7\x08\x1b\xc5\xd9\xa1\\\xf4G\x98\x92\xf4\x04~X\x91\x83'a\x94\x8b:oc>qV\x82\x82\xc0Ok\xc0\x99\x9430\xf9\xeb\xa9\xef\xff\x0b\xfd\x0e\xa2\x0c\x1dB\xb6\xcf9\x1co\xd2\x89\x96\xb4\xc8\xbej\x00f6=\x7f\xe0\x02\x05~\x88\x05O\x01\x02\xd1\xf3\xd9/0 \x16\xb0\x1c\xaa\xe1\xc3\xdf\xf3\x07\x91\x17\xc1\xb9\xc4\xb2\x93\x80\xc5\x01l\xe4GX~\x12\xc0\xcc\x1fq\x80ZF\x93\xdeV}~D\xd0\xdd\x1f\xa4\x99#\xb9\xc5_\x90\xa9\xfb\x83,\xf1\xa5,\"\xf4&Q\xe6=rr\x8b2\xd0\xc3{\x98\xd6\xf4\xfcAnF\x8e\xa9W\xcf\x1f\xa83\xfa\xd02)s\xda\x1e\x92\xe5\xd8s\x92h_\x80\xde\xc7\xd4\xa2\x178\xee^\x10\xdd\xe1J\xb8\xfa\x10\xcb,\xb2@z;w\x12 \x7f\x0f\x0b<\x12\xae'%K`5\xa1R\xc2,\x0d\x968*\xa5\x02\xb8\xb5}\xf6\x0b\xb2;\xe5R\x89\xbaT~\xf6\x1e\x96\x02\xa4\xae- \xff\x023\x86^\xb077/\xeb\x90\x03\x12\xec\xcd\x9d\x94\x10BE\x82\xbd\x13\x00\xc1\xc2\xb2LO !\x98\xa1\xf5B\xb1\x18g\x9e\xfd\x183\xda^\xc8o\xe7\xbe$\x07\xf7\xff\xda\x02^\x07\x94~\x8a%\xc0^\x08\x80w\xb1\xbau\xd6\xc8B\xff\x07\xaebd!2nh\xeb\x01\xe9]_i\xdb@\xfb\x99\x0f\xe8E\xe6\x1a\x1d\xf4@J\xf9\xf0>\x05-\xaf \xc8\xcf\x7fa\x81\x04\x12\x82YT/:\xf0\xa0\x0eV4\x04D\xd6\xf9\x19^\x04\xd1\xda\x96\xac\x83%\x11\x01\x91\x07\xd6\xb2\x08\x07\x1e\xd4!\xa8\x10\x1dx\xb2\xce\xcf\x08O\x8f\x0e.\xc8*\x96\x01H2\xfa3r\xf6\xa2\x83\x0b\xcb\xb2\nVo\x05D\xb2\xce\x9fciD4\x06u\xe8.\x1c\x0ce\x9d\x9fa\x92,Z\xdb\x95u\xb0\xbe\" \x92\x95\xfc\x9c\xf0\xfc\xe8`\x08u\xb0\x02$ \xb2\xce\xcf\xc8i\x8e\x0eF~\x08\x04\xea\x01\xa1\xf2\xd1\x81&^\x0f\x08k\x8d\x0e\x0c\xd5}\x80\x15\xb5^t\xb0\x0b{\x8e\x95\x0d\x01\x01<\xc1\x82i/:\xc8\xa1\xce\x7fk\x81\x00\x9e`\xa5S\xb4\x06{\x8e\xb5N\x01\x01<\xf9\xa5\xa55\xa8ci-\x07<\xb1`\xddeY\x85\xd0\x92\xe8@\x9e\xfd\x9f\x11\xca\x16\x1d\\\x06\xd4\xb2\xec\xece\x89[?'\xb49:\x18C\x1dB\x95\xa3\x831\xe0#V\xb6Dk\xb0j\x844F\x07\x97a\xa5\xb1V'Z\x83:XA\x11\x10Xi\x0b\x0e_\x86U\xb3\xec\xf5eXi\x0b\xfa\x8c\xa1\x8e\x05y\xc6\xb0\xd2\x04\x0b\xeae\xe8\xb3\xca\x98\xf6k\xb2o\xf5\x80qO\xb2\xf7\x8f\xf1a=\x0bZ\x10\x95\xb7zF=\xfa\xdf \x84\x8f\x84p\xf7\xec\xad?#\x90:\xc9>Us!R}/\x8d\xc4:\xff\xe0\x07\x96\xefR\x85\xff\x90\xc8#i\x14\x0c\xd3\\\x02\x7fEHv\x1e\xc8m{\x93lu\x1e@j1\x1bH)o\x7fj\x01HM\xf9 \xb6L\x08\x08\xe8\xcax \xce\xe6F\xdf\xb35\xa7@\xb8\xd6\x92\xb6E~\x8a%3\xd7@~J\xea\x80\xfc\x88\x89\xbc\x12G\xefar\xe9:\xb16ta\xf9\xcbu\xe2^\xa2d\xc3\xc7\x98\xd5\xb9N\xac\x9a|\x8c\xf5\x7f\x01R\xb5\xf0\xe8\\'VB\xecc\xcc9\x96\x9c\xd8\xcf\x9c`\xd9\xef\xf7y\xc2\xc3\xccw\x02\xc9\x14~\x82w\xdaubPY\x1e\xff\xe7\x7f\x8f\x1bq\x9d\x04\xb6\xf3-,1\xbaN\"\x15\xd3_\xd3\x05;\x0c\xf8!h\x17X\nqu_\x8f1\x82.\xe9\xf6>\xc5<\xd35\x10Z\x87{\xbe\xd4\xc7\xc9\xb2\x18\x08\xe6YKJW\xf8\x14\xa3\xb4\xab\x01xc\x96J\xaa=V\xc0\\7W\xf3\xa1\xa3\xce\xe34\x95\xc7\xf41f\xf6K\xb0e\x9fb\xb3\x8b\xab\xbe\x93\xfdW\x93\xf9\x18\xcb\xa9K\x02\x1086\x90[R\x1b\xb1\xce\xe6J\x7f\x86\xd6\xc7\xf8\x84.\xf10\xe3\xc9\xb2\x1c\xc4\xc7\x98\x1c\xb9\x12\xe8\xd9\x81K\xfd\xc4\xbe\xdfZ\x9f\xc3D|\xe9\x02\xa8\xd6x{\xdc\xa1\xfc\xfe\x0fdC\x87\x1c$\xe5\xbf\xc4b\x98\x84\x8c\x9c\xc4\x0e]\x1a\n\x12\xfa9\xedF\xaa\xcd\xa4\x17\xb0\xe4\xfd\x82l\x00\xa0\xc6\xaf \xd5\xf0\x13W\x91\x1a,\x9f\nP\xc0\x9d$\x89\xf6\xb56\xf2\xce\xffY_\xc6\xe8\"\xef\xfc_\xd6B\x1eX\xc4\x9e=\xc0\xb2\x8a\x02k\x0d\xf8\x01\x96K\x14\xdcS\x06\x9d\x07X>Z\x92\xf0e%\xd0c\xd9E\xd5\x16L\xf5cL\x9c\x15l[T\xfcs|\x9a\xa0\xd9KF\xd2\xc3B:\xc07\xb5\xb0\x87%u\x00\xef\x18y\xcf\xb2\xba\x92c|\x88\xb5z\xd7\x07=\xd3\xb6\x1f}}\x8c?\xc2\x07\xd2\xf5\x93\x11\xd8^\x9fb\x0b\x82\xeb'\xa9B\x8b\x0f\xb1\xcc\xb5$\xd4\xb7}?\xe5KQ\x98Ey\xb2\x1af|\x908\x923\xde\xc3\x87n)\x88R\xbe\x94'\xc1\xe1r\x94\xf7\x02\xfez\x1ee w\x90-1%\x8b2dc\x82\xbc'\x97\xe6\x97X\x0c\x93\x90\xdc\xcf\xac\xc0\xa5\x08\xac\x89\xcf\xee\x91\xe3\xad \x0b\xb6\x1ap\x03\x83Ey\xd7\x80\x88\xfd\x16@\xb7k`\xa3\x91 Y]\xdbw1\xec\xff\x8a\x02\x80\xd5\x12\x16\x14\x8d\xe2>L\x07Kb\xae|\x19a\xc4\x15\xdd\xb6\xd5\x0c\xf8\x01`\xd7\xdbx_\x8d\x99\x90p\xca(\x1chv\x8bI\xddR\x14\x0e\x92\\ux\x1f\x0b\xbaK\x05\x0f!\x18V\x80\xf0\x11\xb3\xe1\x15-#\xb5t\xdb,\xb4\xfaNw N\"\xb8\xd6\"\xacI\x82r7\xb3C76\xaf\nR@d\x9e(>\xac\xfb\x9e\x02g\xc0\xe7q)\xca\x05?i%\xa2e\xa6\x90\xec!\x99M\xee9I\"W\xe7}26 \x93\xeb\xf3>^\x1f7\xe7\xb1\x84<$s\xcdy*9\xc7C\xacM\xb9y\xa0\x97\x1b\xdbv\x01$\xa7\xf5>\xd6A\x96\x94\xbd\x95\xf0i\xf8~\x0f\xab\x9an.\x84b%\xf9\x126\x92\xc7J\xfe&\xd7:nn\xe4e\xc2\x96s#/\x13\x11+\xd7\xf2\xf2\x03K\x83\x11\\\xe4\x91c\xaf\x84\xbc{O,\x02rn\x90\x92\x90T \x92\"\xe0\xfbX\x8dv\x05y\xe7\xb7\xe3\x84\xbb5\xdb\"\xe1i\xee\xd6mN\x12\x1cjc.\xd6\x80$\xb00\xe7\x12\\\xcd\x93D\x1a\xe6?\xc6J\xb7\x9b'c$\xb3\xd0\xad\xd7E\n\x91\x85N\xbc~d\xea\xba\x87\x0e\xaa|\x83F\x04V}\x83v\x0f_\xc5\xb8\x87\x81\x9b \xda\xf3\xec]L\x90\x97e\xaep\x01z\x13Sc\xaf\x00a\xc1\xd4s\x02}\xa3\x81\x0f\xd8\xb2\xdeh\xd2\xdc\"\x00~\x8aq\xde\xd35(\x00\xc4\xb171QXv\xd2!\\\xb0\xe1\xbd\xf14\xe4\x01f\xea^\xc9>\x8f\x97\xd5\xeb\x05\xd2\xd3\xe0\xd7X\xc8X6Z\x15\xde#\xcf@pc\xcb \xb3cv\xe2\xc1g,\x1e,\xdb\xb5M\xf0\xf5\xf8 >\xb3\x9e\xd7\xb0]z\x1d\x7f\x8a\x8f\xf3\xf2r\x94%\x0e\x984\xdf\xc7\x94\xd7\xf3\xa2,\x05!\xe41FQ\x8f\x0b\x0e\xff1\xd6\xe7\x969p\x1e\xac\x18,\xf3\x00\xae\xbf\xc8\xdc5\x00\xcf\xde+\xe9_\x18i\xbd\xbe\x9f\xc2\xd1\xf9\x00\xbb\xe0,k\x85 \x8f\xc0\xd3\x00\xb28\x17\xe0B\xe9\x03l\xeb\xf5\x86\x0ep\x8a\x9fb!Y@`=\xb1\xcc\xb0\xec;n\xe2g\xbe\xeb\x04\x8bun[\xa52\xa06\xfc\x1a\x0b\xa7\x95\x12B\xd6\xd5mQ,,J\x9eW\x9eT?\xac/\xb2\xa3\xae\xeb\x7f\x8d\x8dx\x9e\xefH2\xfb\x10[\\\x96}g\x14\x815\x86\xc0\xbc\xc90#Gcs\x9e\x80\xa75\x10\xb9h\xd8 N\xad0\xe4\x00\xf8\x03\x07\x04\xe3\xdf\xe0U\xf2\xfc\xd4\x97b\xeeCL\x18=y\x13\xf4 \xc1n\x7f\xec\x83c\x83\x1d\x12\x85\xc6\x94\xfe\x90 \x9a?\x8e\xc2\x03+h\xf9\"\x9ct\x8c5\xde-P\xda\xb1\x1c\xe3\x05n\x94\xc8\x81\xbf\x8b\xf9\x9b\x17\xb8\x89|b\xe0\xd9\xbb\x98\x0f{Q\x10H\x94\xfe}\xdc\xbd\xb9\xa9\xc2:\xb2gD]\xacH*c\x06\xde\x0e\xaf\x06q\xa3Li\xc2?&(\x16eJ\x9f\xc1$[B\x94Pq\x1f\xd3\xa0\xe5([\xb9\x9d\x83>8+:f\x01S\x0c\xae\x01\xd8Z\xc1\xb5\x9d\xf4\xd9}\x8c\x1f+\xb0hX\x0d\xe5\xb0fX\xca\xe1\xcbJ\xd2 \xaa\xc9\x8a\xba\x05\xc2\x83\xd5Fz\"cpU\x01\x1fR8\x9f?\xc1R\x1c\xef\xeb\x860cZ\xd1:\x066\xc3p\x0d\xc07FR\x8bz\xf6\x04o\xc5\x8a \x8b -\x19\x08fy| \x89\xf7\x132\xedA\xaa\x8e\xca\x13l\xe4\x05e\xed \x96\xe2VJ\x86_\xd2\x7f\xe0\x87\x19OdW\x7f\x86 \x13\x87K\xed\xb71\x93\xe2\x01\x0c\x0d\xef8\x0f\xcc\xd0\xf0\xda\xaf\xe8\xe8\x0b\xbc\xc6\\\x03H'B_\x94c\xc6\x04IBR\xb8\x86%@\x99ky{\xe4\x04\xc1\xb6\x91\x08\x7f\x81\xe5\xe3B\x17\xb5\xd7\xbf\xcc\x13\xdc\xc6{\xd8Y\x84\x8fRI{\xdf\xc4\x9cS\x00\xe6NH\x10V\xa3$H\xba\xbe\xbdI\xfa]?\xbf\xc0Z\x9f\x91\x83'-\xef\x9f\xe1\x0b8\x1e\xaa\xce1G^\xd1.\xfe\x0474\x80`\x87\xd1\"\xb0M\x8e\x1b-\x82\xe0`\x0cT\xf4!\xc1\x80\xd8IR\xe0\n\xd8*\xc3\xb5\xf4\xfe\x18Sx\xe5\xb4\xfb9&\xd6+\xc6\xd9\xfbs\xda\x8f\x01\xe1Z\x02$\xb6\xf67\x04p[_\n\x12\xba\xc7o\xd7\x931~[y\x97\xdc\xc7k\xcdo\xa7\x81\x13f\x83,\xb1\x1fT\x00\x07<\xb5\x9f\x16\xa3\x07=\xa6#\xcd\x1dy\xc4\xce\xd8\xaah\xad\xdf6\xa0\x9c\xc3\xb5\xe8}\xcc\x92Vn\xe7~\xe0\xf7\x12?\x97s\xf9)\x16\x18JN\x946\x08\xd8\xae\x1ec\xa5\x81\xdf\x1e\x17\x1b\x8e\xa5h\xaeY\xe0\x07d\xc3\x13Mq\xf1\xa1_\xd1nA\xd8\x10\xc55\x00\xf3m\xaeI\x0e\xd1&W\xd4\xbe=\xc6\xd7&\xbcnCW\xc0tE\xf8\x06|&|i\xe7\x82\xa0\xdb\xb8[\xb0\x96~\x82'\xb0\xa2\"%\xc8IV\xdf y\xc9\x13\xe9R\xff'\xd8A\x8a\x1f\xb8\xa2\xc2\x11\xf2\xd9\x87\xad\xbf\x87\xe9\xd1\x8a\x80\xa4V\x10?\x88\xb9\x9b9:^\x86\xac\xfa\xca\x01${\xf0\x9d@^/S\xdeY\x14\xb03\xd7\xbe\x13\x04\xbe\xbc$T\x96G\xc2d\xcf\x81\x98\x80\xa5\xe6>\x88 \x98\x82\xf6\xf9Hu\xf5K|\xf3\xd0\xef\xfb\x10\xf8\xf8\x9f\xff\x06\xcf\xb3\xdf\xd7\x10Z)\xd0 \xdc\xd59\xcd\xe4\xb1\x9c\xd6\xd7\x00L\xe2\x8a\x01`5\xe2\x9c\x1f\x04\xdc\xc3l \x13\\(ec>X\xec\xea\xdf\x82\x9e\xfa\xb70 p\xc0B\x87\xc5\xaeb\x9e\x18\xeb\xfbA\x16J\xf4x\x0f\x9f\xd3~\x18 \x06\xf0\x9f\xc8\x96\x19\x96\x81\xf5\xb3\xbea\x19\xf8\x10\x9d\x8b\x92E\x10'\xee\x91=\x88\x12\xa7\x1e$\xfdX\x1eb\xc3\x87\x00\xc0\xbd\x00\xe6g\xe7\xa2<\xf1y\x92%p\x0bL\xe6\x14;I\xa6\xfd\x1e\xb0\x10\xdaO\x1cW\xba\xb3\x7fL&& \x92\xa9\xff\x04\xd3, \x12L\xfdc\xbc\x9f\x12rJV\xc2\xc4_\x82^\x96 <\x01 zE\x82\xb0\xe0.@\xf30\n\xb2 \x02\x04}aF$@\xd2\xe1\xfec\xac(I\x08T\xc2\xfb%A0\nl\xfa\x13\xa0\x93P\x0bK\x19\x02t\n\xa6\x85e` \x82\x06\xb1=W\x80\xbe\x03 l\x13\xe8'\x0e\xb0\x97\xb7\x08%HT\xe8\xc3\xbbX\x08?\xa7y\x05\xd9{\xa3\xfbb\x81p\xa0U\xaf\xff\x07\xf3\xe2\xf3\xca\x08\xfd9\xdevm\x9d\xfe\x1c\xb3\x17Y\xc3\x13\x12\x08^\xb8\x81\x81\xe0\x15\x18\xc0\xcd\xed\x13l\x970\xa2\xc9\x13L\xd6\x00$\xf9\xfb\x13L\x8e\x15\x0c\xe6\x8a\x91~\xc0S5Yz\xf3.`0\xc8'\x988\x9c\xd7\x1c\x0b\xab\x17\x03\x0d\xc0\xec\xf7\xbcTd\x1fb\xda4\x00? ,\xac\x0c\x065\xc5\xfd\x11l\xce\xdbXx:\xaf\xaeN0\xa7\x1e\xa8\xab\x13\x82qpc\x80\x9b\x19Hg\xcfgO\xc8\x1e\x83\xbc\xf2\x04s\xaeApK~\xc7\xd3\x1d\x84\xea\x00\x92\x05\n\x8b\x98a\x0b\x10\x10\x98\xec\xc5\x9ckud]\x96U}\xaf\x82\xcf\xb4\xaf\x01X\xc6\xf0G\x0eh^\xb6\xb6\x06~\xe8$\x87\xab\xf6\xd5\x199\x83@\x9d\xe8\xb71j\x0b`\xec@\xca$\xbaw#\x99\xc5\xb4\xf5)\xd6\xd4\xfd\x91\xb4<={\x80Y\xb8?\x8a\xa5\xc3\xec\x7f\xc2\xf8\xb4:\x8a\x03\x1f\xd4\x1f\xe2`\xe2\x87l\xc1v\xf9\xe5\x87\xae2\xb0\xbd\x8d\xafc\xcc\xde\xdd\xc3\x8a\xb7\x84\xa8\xd0\xfd\x0f\xb1\xbe\xec\x87*\x87\x06\x99\xd1\xaa\xc2\x12\x82q\xea;\xd9\x8d0s\x81\xc6<\xc0B\x9c\xca\x08\x0d\xb1\x1a\x98\x81V\x9c\x97,\x8d\xf2\xa4\xae\xd9Uy\x11\xc8M\xf6$\x92X\xc4\x0f\xb3\xc0I\x86\xd2 \xf7\x11\x16\xda\xfc0\xd3A\x14\x1fa!q5\x1c\xfb\xa9/\x1d\xac\xc0fb![\xba\x88\x89qz\x0bK\xe5\xab\x1b@I\xb0m\xd5\x8f@\xf4!X\xabo\xbc0\xc1\xf35\x00\xdf%\xac\x1a\xae\x86\xf9\x92o \xd8\xac\xb5\n'\xf9s\xcc\x07\xd5 \xff\x1c\x0b\x16~\xed*\xf9Z\xca\xfe\x18\xb3\xf9U\xcd\x15\xc9\xe12\\\x11k?\xdaC\x92\xe2|\xea\x87Z\xf0&49\xf5A\xc8}HF\x9d\xfa`#~\x88\xbd_%DZb\x1fb\xca$@c\xfb 2\xfb\x0e\xeb\xfcS\x9f\xe2\xcbp\xdf@\x08\xc1\xcc\xf7\x00-\xb0\xee\xe1+\xc0?`s\xe8\xaa\xbaq\xc1\xac\xdbW\xdf1V\\\xd4\")\x9e\xfa-\x0d\xc0\xeb\xa8l\x1b\x18%\xc0\xb4\xf1\xf7xm/j\x06\x86y\xff-\x0d\xc02\xca-E6\xff_L\x1d/\x1a4\xc5\x87\xe4\x96\x81`}\xea\xa2\xc1!,\x94\xde2\x10\x8c\x90\x17S\x9e\xc0d\xf0\xce\xde\xd2\x90\x7f\xc0\xf2\xc4E\xbdQ\xd8\xa6uKo\x14\xe6\xf8\xdfw\xe2X\x9e!|\xe6\xf64\x00\x930 \x90\x97\xbfX<\xf9\xbe1\x8abo\xa5=\x03\xc1\xab\xf9}\x18/\xe9\x1d>\xe3\xbe\xbf\xafw\x0b\x0b^{\x1a\x80\x91zo\x90@B\xa8O\xb1\x90\xf5}\x15\x0d\x8cwdOE\x03cn\xf5}\x85qX8\xd9S\xd64,\x7f|\xdf`\x03\xa6\xf1{\x06B\xea\x18l\xc0\x82\xd6\x9e\x86\xfc9&\x9b\xc1\xa2\xd6\\\xf0\"\xae\x99\xfc\x02\xf88\x04\x06\x82W8pJ1\x04\xf80\x06\xce q\xe0\x16\x13\xb3\xff5g\xd4\xf3$\xbe`\xdc\x0f\x0c\x04\xabOk*k\xe6\xaf\xb0\xf8\x14h\x00\xdeM\x01\x80\xfc\x8e\x98\x11\x05\xc6\xb3\xccR \xcc\x8exC\xd7\x1c\xf9\xe2\x9a\xbe\xc4\xc23\n\x1cH\xb8\xf61f\xf0kZ\xab\xc7RK\xa0\xed\x00\x98\x85\x98\x986\x1b@\xc6\xf6\xfd\x14\x8b\x18\x12\xd2\x97\xec\xe0}|\xf9 `\n\x84e#\x01\x02\xe1\x81\xa8\xa2\x02\x14\xc8\x95x\x07\xcfH\x06\xd6I\x81\xe5}\x8a)\x89\xb6\xe7|\x80y\x8f\x80e\xb2\xda;\x98\xcb\xa8\x1b\xd2'\xa4\xa7\xc5\xcc\xf1\xa1'\x8a'\x06\x84\x89z\xe0@D\xf2\x13,\xfe\x0b\x00\x98\xa8\xfe5\xb5\x18\x05g\xd5\xb2\xbf\x8f\xa9E\xd0\xd3\x10|\x98\x03\x9d\xe4\xef\xaf\xb0n\x10\xf4\x12\xb0:\xfc\x91\x0d \xea\\\xa7\x80=9\xecGX\xd1\x16\x904\x00D\xc6\x1c\x12`2\x8f\xd1#\xcc\xac\xd6\x8c\xb7!V\xd0\x03\x03\xc1B\xca\x9a!\xbd\xf8\xf8\x05\x06\x82\xa5\xa4\xc0\xe5\xb0\x13\xefb\xd6\x13\xb82\x16\x15\xaf\xc1\x1a\x90F\xb2\xa5\xf0\x99t\xec\xb9R@}\x1f\xb3\x89\xc0\xe48\xc4\x84QB\xc0\xe2AN\x9d\x97x\xda\xe1\x143\xf1\xc0K\xf2T\x03\xc9.x`\xd2x\x87l5\x18!1 \x06\xf2r\x1f\x9fT\xe9\xf2/\x88\xcfY\x81\x07\xe01GhP%.\x80\x90\x81\xb5\xb2\x0d\x89R\x8f\x8a\x85\xc9V\xb7\xec\xedN(\x89)\x80\"\x04\xb0,g\xba\xd1\xc7\x90\x1cj\xd1\xd2\x12\xf7\x03H\xc7J\x91C\xc0\xc1\xf9\xbf\xbc\x14x\x19\xa1\x94t\xd7.\xf9\x8dc\x0b\x85.Ur\x1b\xc7\xb6\x9ej\x11\xed5\x8ei\x87(u.\x88\xa0\x8dw\xb1\xe9VLZy\xe0\xeb,\x7f\xc4\x1f\xbeT\x06\x02|\xdf!\xe7\x85\xf73\xb3|\xa0\x1ec+5\x0d\xf8 FaQ\xa4j+$\xf6\x99\x80\x14!\xadT\x8b\xa4\xb5[-\xcb\xa8iA)r>t\xa9\xf4v\xee\x0f\x8a\x1e1\x11\xb6\x05'`\x8a[\x8a\x9e!\xa1\xa4\nV,\x8c\x0d\x83\xab\xd8\x82%\x1d1\xd4l\x98p^\x84\x98\xe1\xd9\xc8FJ)\x1f\x1f\xe0S_.\xa0\x90\xe9CL\x9c\xcbe\x8c}\xf2\x01\x16\x93D)\x08\x92)\x0d\x19\x0b,P\xa8:-|\xa7\x0feJ\xa1\x1aXG(\x17\xd0\x07\x00\xeb\x04(\xda\x03\xe3.\x8d\xf4 \x82\xd0\n8\\S\xfc\x80\x0bi\xba\x19p\xc1CD\x1a}\xf3C k\xc9'\x80\x9e\xbe\xb4\xee\xbb\xba\x99#\xf2\x9e\xf1 x\x8c\xd7+(\xf9\x04`\xedM\xc1\xe4\x1a<\xc1\xb4&\xe0\xa9\x9a\xacE\xce\xe0\xa9r\\x\x82o\xd4\x03\x9e\xa6\xa5\xab;,\x81\n\xb0\xb6\x13`\x0dZ\xc0\xf8m\xe5\xf7jYc\x01\xd5`\xb25kO\xaa*\x14\xa1U\xa2\x08\x12\xb0 \xe1\x8a\xeeHrA\x94\x80\"\x95\xb8\x0d&\xcdC$\xc7x\x00k\xd9\xb6|\x06\xd7\x92GD\x18\xd0~:T\x1eOJ\x04\x92X{\x12\xa5\xc0R\x01=1\xb4\x91\xec\x00\xa4\x00z\x93X>\x12E3\x1f\x10\xca\x98:Z\xf9\xc6\xf8\xb9\xa6\xafF\x88dh\x8c\x92X\x98ZS\xaa5\xa1\x95\xb5\xdfk\xa4\x81\xc08}ac\x88\x80\x80`J8vz\xbbg\xb3\xc7\xa4z\x82\x041Rc] B\x92vb\xf8\x8c\xc8\x8b\x06\x82\xed\xbbk;\x0b\xac\xf5]\xfcQ\"\x05\xe5\x9a\x99\xa5l\xa0\x9d\xce\x08\xdd6Ng\x84\x86d\xb5\x82\xa4T\x8c\x16l:QP\xa8K\x84=e\x9a\x9d\x7f@hQ\xc9U\x8d\x98v4K&t$K\xe0:\x97hK\x81\x0e1&\x89\xf3\x83,\xd1\xeerdRy\xe2\x19\xc3\x0e9\xb3ybB\x90\xc9\nV|\xd0>\xb2H\xf3\xda\x07\xcd\x02S\xb7\xfa\x1f\xe3\xdb+\x13.\x83g0r\x80\x16\xfc%\xd6\xec\x04\x80\xc3\xe3\x1b\x04v \xc4\x89\xf71\x91\x1e\xc1\xf7w\xf0\x94\n\xfeT\x032\x96\x0dl\x1e\x03\xb0a)Xa\x03\xb0\xb2y\xe0k\x92\x91\x93\xec\x01\xc5z\x0f\xdf\xfd\x8et\xb6\xc5g\x1fa\x99\xf9\x12H\xa0\xd8\xbc7\x82\xcf\x98\xbd\x8eL\xca*l\xe5\x18\xe9H\xe6{\x98\xb1\x8f\xb8\x93\xe6 \xf7\x8a\x07\xb6\xb0\xf2q\x89{~>2Ndoa\x82{\x89\x07\x81\x1f\xeak\x01l\xf4\xbe\xa4\xd5\x01l\x88\x1bi\x00>\xe2\xa3\xa1\xdc\x9c\xb7\xc9\xea\xfb\xae\x0c?\xfb\x18K:*-\xe8=l(\x19\xf9\x9e\xfd\x8d\xa2\x91\xef)\xba\xf0\x14\x13\xd6\x91\xef\xd5\xa4\xcf-\xb2\xc0`\xb2.!\xf0\xc6\x16^\x1b \x82\xd1a \x0e@R]\xf9\x08/\x81\xcc\xc9\xaa\x13\xaf\xde\xc3\x8cq\x14\xb8\x90\xad\x10\xdb\x8fG\x01\xb3\xb4g\x1e\x1a\xa3\xb0\x0c\x1e9\xf8%\xa6M\x12\x02f\x85:\x18\xf8\xfc`\x1f\xbb\xb0'\x9d\x8c?\xc6\xd4:,R\xcc\xd3\xb1\x97r\xc9S\xa0\xce$\x89\x97}]\xdf\xe5|\x86\xb7*4\x10lz_\xd7w9\x9fa\xae\x11\x1a\x08\x96:C\x93r\x96\xf6S\xce9k\x19\xb9Jt\x89Q|\x1d\xc88\xd6\x14B\xf8\x8c\x15\xca\xd0Pw|\xbaT\x82_\xb2\xd4\\{F\xbd\x8fYU\xc8\xf5\xdd+V*D% y\xc7\nQ\xaa\x02\x85\x99\x88g2\xfdu>p2\x7f\xcc\x11\x1fy\x13KW\xba\xdc\xce\xd0w\xf7\xa6*\x16N.u\x99'\x87\xcd%Ko\xf5`KS\xc8S\xaer\"a[AX\x04l[&\x9cf\xdc\xa3A%$\x82\x02\n\x96-\x7fD\xde]\xe7\xfb\xca1\xf9\x07!\x19\x82 \xaf&\xf4\x86\x17\xf1\xd5\x18\xb6\xae\xf9.6\xb8\x85\x1a\x80\x87\x19\xea\x988\x8a\xd9*,\x0e;\x16\x86:\xce\xcd\x06\xb8]\xdfX9\xd6\xcd\x06O\xeb@:4\xccRI\xef\x13\x96\x1aB\x1d\xd6b!\xc9\x03\x00a\xb95\xd4\xc6[\x028\x9f\x01\x06=\xa5\x030\xd1\x0eX\xb7\x0cM\xb8\x03!\xacCexx\x8a\xd5\xbbPj\x0b\xf7\x08\x0e\xc3Cq\x0f1\xf3\x0b}\x10>\x1eb\xa9/\x04\x8c'\x0d\xad+\x93'V\x11Be\xf2\xc4\xea^h|8\xb0\xba\x19\x1a'\x0eZGI)XD\x0e\xf5E2]Du\x97\x8c\xa5\xb5\xb0z\x13L\xc7P\xb9\n&\x03\xb1\xdc \x92M\xb2\\!\x92\xed\xd278dx\xc5\x15\x8emJ\xe5[\x1c\x1b\x19jM\xdbr\x0e@\x1b\xa3\xddh\xb5\xf5!&W\xa1\xd1[\x1fbkZ\xb8\xa6\xce\xc8\x13:8-\xc1c6\xb5\x1e\x9dM\xb8#Y\xd8[\x98\xbb\xadG\xa1\x04\xfa\xe1@\x13w\"l\xac\xebX\x11\"\x9d\x18\x01\x16K\xec\xfam62|\xd0\n\xf0\xe7\xf5(\xab&\x95\xc7\x86\xc9_\x01.\x06\x81)\x7fQ\x06\xc5b\xda\x86b\xe3\x9d\x0d\xe5\x0c\xf7\xc4V\x9e\xa2\x08\x0e\xcclh\xadX&\xcc2\xd6\xa3\x8c\x86\xe2\xd8ZB\xf18\x14\xe1\xa3L\xb9B\x13I\\@\x8c/\xb4\xbd\xa2r\x87\xb6\x03\xc7N}\xbb\xf0\x10\xf4C\xac\xd9\x02\x0cr\x98c\xe3\xd5z\x94aO\x00r\xe8Q\x19\xe3\x0c`[\x19\xabG\x00\xa1\x15\xb2`\x0d\x8dS\xb0by1\xd5U\x05\xca\xc8c\x1dHY\xea\xb2\x0f\x95^\xac\xd6\x95+p\x06\x93\xd7\xf5(\xab\x93\x07\x9f\xfc+[sT(|\xf2\xd7\xb6\xadV\xa2\x00\xf6\xc8\x93\x10\x85\x04v\x18 \x01\xd6\xa9\x01\x06H\x805\x8f\xf5(\xdbL\xb8\xcb=\xf5\xd2\x0b\xb6\xf3\x95\xe0f\xad\x9e\xfc\x1b\xdb\xe4t\xb1\xea\xba>\xb4P\xac->\xe6I\xca\xcbD\x0fOG\x94\x92\x195\xcb\xc8IdlTHc\xa7EOA%\x8b\xe1Y\xa86\xe4\xc1\xd9\xce{*\xe7\xdb\x03+\xb6\x97K\x15\xcdYX\x84.\x18\x8b9C\x83\xd6\x01V\xcb\x15Mb\xd3\x97(Z\x8c\xedO(k7\x05\n\xb7\x1c\xa2#\x8b\"\xae\xcb\xb9\x07\xbb\x8e\x0d\xfa%x\xb1\xeb\xd4XQ*\x86v\x1d\x1b\x1aK%\x8b\xf3\xf4\x1f\xed\x0d\x96\x16\xea\xc75\xb3Ck\xf4\xc0\xc23\x8bn,\x93\x93\xc0\x82\xccXx\xa2,Qeg\xc4Z\xa4J\x15=Y\x86\x81\x99?\xd1\xd6\xe3\x1a\xa9@\x00\x9c P \xf1mPH\xcd\xf1\xf4o\xe9+\xb4\xa1\x8e\x80\xbbG\xa5\x810\x8e\x02\x1d\\\x88M\xc9!?}\xc7Z &Id\xcc4\x8f\x1b\x88\xb2\x02\xabI\xd6T\xd6\x93\xb4\xf4\x9b\xa9|;D\xc8\xd7qx\x9f\x10\x8b\x96\x81\x10;T\xa6\xbc\xd1h/\xe8yr\xaa\xe2\x96K\xc0d\xa8\xaeK\x9e/\xa7\x07\xbfRD\xb5C\x04\x0dy\xa5A\xec\xc3\xf2+1\x0f\xcb,\x9a\xbfG\xbfrH\xda\xf86\xbe\x13\x0es\x9d-\x96\xd8\xb3\xc7\xfa='\xcb.^^\xd6\xcf\x14\x12+\xd8e\xf3\x82!\xb1\x18\x8cM-B\xe6\xc6\xa6\x16Y\xc6\xb1N\xbbe\x19\xc7\x18\xf2\xcf\xd8 \x17t\xb8\n9\xbc\xe3\"\xfe\x1d\xdf\\\x85cm\xcbz\x1f\xdb\xe9\xc3\xb1\x8ee\xb0\xf5\x06. v\x88\xb9\xc4\xb7\x815\x0b{\x9f\xd0\xdd\xb1\xe1\n\x0f\xfe\x9d\xad\xa6~[\xf8?X\x80\xfb\xc6\xe8Oh\xda\xbe\xe6\x99\x04\x15\xf65\xcf\xb4B\x14W\xa3\xb0P\x9b\xc7\xf1\xd5\xe1\x86I\x11\x81\xef*\"\x03\xc1W\x81Q\xdd\xf3\x99\x91\xba\xac%\xeffn\xe8\xf4\x11XF\x894\x00kc*\\\x1b\xef=Dk\xff=\xd6\x89\xa2\xda\x1797\xf4\x9bM\x9f\xe1k\xed\xc8@05\x8a\xe0!\x98g\x1fa\x9a\x13\xe9\xd7\xce\xb0\x93V\xe4\xa5\x91\n{\xc2\x96\xdd\x8d\x15H\xbd\xf0\x19\xde\xff\x88+\x00Y\xf8\xbeZ\xc6G\xd8\x95iC\x1b\xfeI[\x1a\x80\x0f\xa6\nV\xff5\xde\xa9\x0d\x93\xc4\x824e \xd8\xa4\x1d\x81\xb1\xfdC\xcc\xba\"\x9d\xa8\xe7\x116\xc3DC\x81\xfd\x9fc9&\xaa{\xa112\xa6hl\x06\x8f\x02\xbd&d\xeb\x03\xf3(\xe1#\xec\xb4\x13\xe9\xc4\x12o\xd2Z0\x17,\xcbn(O\x98\xcf\xb0\n\x1bi\x006]o\x8c\xf8\xc0\xb1\xceR\x01~\x83\x19\xe8\x86\xf4\x8f\x90\xe9\xa7\xb1M3*@x\xef#%R=\xc2\x86\x9fhT\xfb.\xec\x861\x9e\xe2+\xd2\xc8@\xb0\n`\\)\xb1\xf1i#\xe6\xa1\xf5\xc5U|\xbdo\n\x16E\xb0_Z\x14sx\xf0\xf0\x11\x96\x11\x8c\xef%y\xc5vC\x0e\xeb1\xa1 N\xe2k\xbf\xc8(\x17\x04)\xc0\xb3\xf01\xa6\x14Q\xe2\x81\xb5\xe7mL\x8b$\x04R\x8a\xd8`2\x13\x17\x16>\xa2\xc4\x13\xb8\xff1A\xe4\xc4\x1f\xa8\xec$d#\x13\xf5b\"\xde\xc6(I\x83\x08D\xb9\xc7\xf8>7J$\xa9zLH\xb1\xfd%\xe1\x0d\xa3\\\x90\x01k\xc7\x0fB\x89u\x8a\xa4O\xc8.\x1a\x08!\x94\xeau\x8f\x07\xb8\xca\x86\x11\xf4\xf0\xf6F\x06\x82\xa9\xc8F\xe1s\x8bq\xb2p\xc7%\x8f\x1a\x03\xc8\x81zx\xa97T\xb6\x06\xb2\xd2\xea;\xd9\x9a\xb1\"q\xefbanc\xccu|\x11!2\x12\xa6\x82k\x9f\xfd\x19fe\x1a\xaa\xc2 \xff\x94\xac\xfb\x98'\x9bN\xc2\xc3l\xc8S\xb86\xfc3|\xd4\xb42\x85M\x06B\xd7\x13\xd8\x87\xe7Q\xd1\x01-\x95\x94\xb8\xf2\x14s\xfc\x92}\x82B\x94m\x02\x016\x9d\xc4<\xcfF\x81\xc0\xc61\xf9\x8b\xe13&}1O\\\xc91\xfe\x19\x05\xf82\x1f\xca\x0c\x05\x8c \xd6\xf3Mlt\xd6\x94\xe7\x01\x99>O2\x1eJ\x81\xecM\xac\x85lj\xfe\x8ayu\xac\x01XX\xde\x84\xa7\xd2\xb1\x96\x1b\xc3S\xe9\x98\x1c\xc7Cxu\x00\x1f\x8ax\xa8^q\xa6\xfeX\xf1P=\x17\xfd\x17\xf8&tS\xf6\x8c\xe9z,;\xc6\xfc.\xf63wX\x9b';\x86Q\xe1S\x12\x07N\x08\xef\xc7\x93\xa4i\x00\x82\x84jx\\\x02\x06i\xb7-\xd5$\xd1?j\xf9\xec(\xc6\xff\x11\x16\x92\x05\x104\x7f|\xb2\x04D\xd7\xc2\xa6\x04\x01\xf3\xa4\x9aE\xde\x81\x93 p\xf3#\xb8\x11\xe4\xe0\xd3\xfa\x18\x0bE\x9bA\x9e\xea\x87\xd9?\xc6h#\xaa\x8d\xc2:\x88:l\x1f\x11\x1c \xf24\xdb\x97c\xfc\x08\x8b\xeb\xf1\xc8\xd6\xdaf\x04\xc9\xa8\xc4\n\xcba\x92\xcc\x83\xb1\x90\xb9\xb4\xa1\x10c\xd9\xa6\xbe|\xc5bml\xa4\x04l\xbf\x8a\xa3\\>\xf6\xf81\xde\x95M\xb9\xecO0\xd3\x05S\xe4}\xcc\x0d\xe3DE\x18a\xc2nL\x94\xf7\xb1<\x1d\xc3[\xf5O\xc8y\xd0\x96K\xfa\xdd\xad\xe9\x9b\xbb\xa50&:\x02\xee\xaaw\x83\xad\xe3(\xdf\xb3\x90\xb6-\x97,5%\xaa\x96\xf6\xda^\n\xab4f2e\xe3\xab\x05T\x8e\xd4\xc2\xb2\x96\x84+;\xce\x13\xccu%P\x87Ya\xe9J\x00\xb5\xc5\x10\x0fh3Q\x16\xc37\xe9\x16i\x08>E\x12\x92\xdaq0\xd1Qht\xf8p\xc1j\x19z\xc3\xc0\xd5S\xed\x98\x02m\x96\x1ej'\xd4)\x89\xfaN\xa0\x04\x00\xac\xb3\x08\xa0V3\xde\xc5\xca\x94\x00\xa698\\\xbfKx\x87z\x7f\xed\x1e\x96D7\x93(\x8e\x12\x9dI\xed\x1e\xc6\xcc\x02\xac\x12\xb5\xe1\xfa\xa2a\xf0\x9b\xb7\x80\xea\xb6-N\xf2\x04\x04\x83\x07\x98en\x1a\xa1\x11\xdb\xc6bc\x91\xc6\x86\xc9Mx\x95\x87\xac\xbf\xfc\xfc\x1b,\x96\xc6y\xe8*\x13\x17\x06\xbd\xae9,&\xd7\xb75\x00\xef\xc8\xed\xbal\x8b\xafk:\x87\xcd\x13\xb7\x0d\x9d\xc3\xec\xe2\xb6\xc1\xd9\xb7\xb0\x80\xf9\xbaY\x15\xact\xdf6\xab\x82\xf9\xfc\xed\xdc\xc9x\x12\xfa*3\x01\xc9\x8c*\xe0z\xf4\x98\xeb\xea\xd8\x94\xd7l\xdf\x15\x91\xc2\x02\xd5\xeb\xbb\x1b;\x0b\xec\xdb\xado\xe3*Qf\xf9\x9c\x98\x84KX\x9b\xd0B\xec\xbd\xbf\xfd;\xcc{\xb6\x8c/5\xde\xa0\xc4@0\xc3I\x1c\x0f\x12\x90\xde\xc3;\x91\x94\xb34a\xfa\xb1\xa5c;1\x1a&\x1a\x80u\xf0\xc4\xa4U\xc2'S@\xe4\x94\x1ea^\x9f\x14 \x97hs*s\x12fo[Z\xd9\xc4R\x97\xb9\xfc\xa2\xfd\xab\x1a6\x00\x10\xbc\x0f0]KLR%:\xe6\"\xa9\x12\x19Bq\x97f\x81\xa8JX\x84J\x8atKXQL\x8atK\x18\xf1\x13\x93n\xe9\x03L\x0f\x92R\xba%\xac\xe9l\x99tK\xefc\xa4O\x8aLLX\xd2(]\x03\x92E7 \x97\xb0\xc2\x94\x14\xb9\x98(\xeae>\x10M\xac5IH\xa8\xfd\xe7q\xbd-\x93\x8d [\x18\x13\x03\xc1\x1c%1y\x9a0\x05HL\x9e&\xb2[:O\xd3]\x1b@\xd4\xb9A\x01*O\x13\xa6\x84I)O\x13\x16\xd3\x93R\x9e&<\xa3-\xe3\xa7\x8f\x15\xfb\xc4@0\x03\xdf2~\xfads\x0d\x04\xd3\xd6\xc4\xe4i\xc2\xc6\xb3\x04\xf24\xe15\xd8\x02\xcd\x91\xe0>8\xc3b\xad'\xd1y\x9a0kM\xbc\xc0\xa4\\\"\x87\xdf\xe4p\"\xf8V\xe4p\xa2 \x15\x17Jh\x19\xc8\xe9\x04?9\xf0t+@g\xc9%\xd4\x99;\x81\xc9\x92k\xab\x08\x88K\xc6\xc6A\xdey\x0f\xeb\xae[+\xe7\x05\x91\xc3|5\x81W\xfe\xf1g\x8b\xff\x0fvV\xd6E\xd03r5\xc5vcT\x90<\xb7\x9a\x14\x890\xb0=\")\x12a\x90\xe6U\x0eh\xb2BZ\x90 \xdd\xe8\xc4\x16\xf8\x16\xdb\x84'\x93\x17\x7f\x13\x9d\xd8\xe2\xa7\x04\xe7\x8a\xc4\x16\x98ln\xc98\xba\xcf\xb1\x8e\x95\xc8\xcf\xbf\xa1]DR+'\x8cX\xc6\x88\xe3|]\x18\x8bQ$9\xe6>\xc8}\x820\xa7\xaa\xf7\x84\xb5v%g\x17fTE\x89J\xd4\xfbO\xf1\xfd_\xd1\x91I\xda\x85\xe9\xbfl\xaa\x9c\xb5\x0b\x93\nY\x80\xa6\xed\xc2*\xb5*\x86\xf3v\xe1\xd3b\x8a\x95\x12wa\xb3\x16*\xa3\xf3\x0ea\xf1G\x16;W\x8b\xa7\xe5\x04V:\xc2\x95\"Z\xa9\x10\xf8\x06P\x8c\x13EP\xf6.\xeb:\x97\xf2\x80A)\xc2.D)\x9c{\x8bPf\x9ff\xd4\xb2.\xa2N\x97\x85em\x0d,\xb0\x13[F,\xcfr\x13Z(\x8a\xa0\x8cYx:\xc4\x17\xf1\x01\xa1\xceVG\xc4\xa6B\x85\xf7\x1a\x96\xdad1\x925\x0bK\x04\xaaTur\x98R\xa9B\xa5\xa4WX\x8b\xab\x94\xd0\xf8\x87\x05s\x94\xd3\x8c N \xae\x9b\xc0\xbak\x02\x87\xee\xd7D\x88\xf2\xd3\xea\x83\x8d\xa4\xa2I\xa6CP1\xd0\xe9 \x08\xfa\x05\x90\xf3\x81HQEf\x1bL\x0c\x93jf\x1b\x02\xd6\x81\x0cO \x933 d0WLL\x02\x19\xbc\xe8\x89I \x83iKbn\xd3\xb0&\xb8\xa5uQ\xc2\x95\x8d.J\x04\xde\"/ \x1duqGB\xf0/\xcaC\xaf\x94\xe0\xfe\x03\xac\xde'0\xc6\x8e\xe53\xdc\xf8>\"\x9a]\\r;$<\xc2d\x03!\x04\x19\x85\xf0\x90\xb3[d\xea\xc0\x06\xb5-};E\xebh]\x1b\xfb\xc6l)\xc9\x8b\xec}\xedw\x99\\\x83\x08\xd1&\xb9\x06\x16l\x93\"\xb9\x06\x01\x15\xa9)\x082\x17t \xc7ni\xdf\xc3\xf7\xb0\xa5\xab\xe4db\x81H\xc2zE:\xe2\xc5\x93\xf7d\xbc\xb5\xe8:\xf2a0\xefR\x88\xdc\xc9'd'G*\xaf<65\x08\x00\x84\xaa\xfd\x0d\xcd\x02\xb5\xbdqn\x07\xce*\xa9\x16\xf538\xadX\x9c\x01G\x9f\xe3\xf4\xab$\xe3\x1fb!_\x00\xd4E\x1aa!F\xf0\xc5rQj d\xc9bG]\xc1\xfe\x92\xa0\x99\x04\xe9w\xfd,\xd0\xc4z\xf0\xd3\xdbJ\x96x@\x98\x9f\x80\x80\xaf\xd1\x9f\xd3\xb5Ko\xab\xdc!\x0f\xb0\xb0,!P\xefg\x965\xbf\xad\xfcg\x88\xd4t[\x076`\xb5\xa7\x08\x94x@(\xce\xedR\xf8\x82\xb5^\xe1\xd7o\xab\x0b3 \xb4\xd4D_<\xc04P\x82L \\\x0dPuH\xebJK\xd9{\x98\xd5\x97^\xae'R@=\x08j\xe1g\xa8\xc8.\xd2p\xc0\x86\x02\x85R\x8f\x17\xcb\x16\x06\xd8X\xa4h\x8a\xb0\x11Yn7\xd4#\xa6\xf8\x93;p\x83L\x1e\xf2Oo\xe75\x80\xda\xeb\xa5msk\x89u\xc8\xd4hR\x98#\xa7\x0d\x02I\x03mJ35\xee\x87\x98jogp\xfa\x08 U\x80\xbf\xb0\x01d[\x7fAD\xc6,q\x04\x9f\xe6q\xea\x07r \x7f\x83\x95$]D9_as\\\x9a%\xd2\xeeE\xb2\xdfm\xc3\x01|H\xf0Z\x1dL\xc2r\xf3\x9e~\xb3\x9b\xa8\x0e&\x16\x89\x02\xe0d\x91\x19\xe7=\x9d\xaa\xe7)\xe1\xbayo\x94\x83\x07\xf3S\"[\xe7=\x90\xfa\x9fb\xbb\xa2\x80@_\x84\xc0\xe6=\xcdE\x9f`\xb2\x9c\xe6=\xc3E\xb1^Z\x1c#\xdb\x1a\x990*+H\x11\x05\xcb\xb4\xcb\x11T\xd6\x0e\x8b\xb3d\xaf\xad\x12\n\xdb\xa6 \xd0\xdbu\xeb\xa3\xfd\x1f\xb1-A\x80`\xd3\x9f\x12\xec\x11 \xc8\xf2F8\x86\n\xf6\xa2\xfaj\xee\x96]\x8f\xb0\xd6*\xc0e\xd7#\x8cL\xe5`_\xd2\xb6%\xd2\xb7\xa6\x04r=\xaa\xeb\xa5\x14\xe1k\x19\xa7\x0eY\xb3\x80\xca\xaeGD5\x15p\xedzD\xd4S\x01\xacUPs\xb7^\x0b\xcd\xdd\xe1\xce\xd0\xb1_Bm\xc3e\xd2=\xc2\xf7j\xbf\x83!\xf0\x97\x98\xb8n\xc3v?\xa4\x15\x80}\xd2\xd3\x1a\xcf \xf2\x82OO\x9a\xc7\xf3\xe2;\x91M\xf3\xf8\x84\xf8N\x84\xc7<\xd6\xe4\x05[ \x05H#(\x11XM\x84 \x05\x009\xa0\xd8\x1e\x1b\xd2\x83\x05\xb8j@w\x0d\xb08\xa0\x96\xa6\x87\xca7\xfcWXQ\x9405 |!\x9c\xe6\xb1I\xdbJOSl\xa8!\xa55\xb1\xa2\x86Dp\xcdcE\x0d)\x1d\x8855|J\xc45#\xed\xd8\xb6\xbfn]*b\x90eI\xca\xe1\x94V\xa8\xa6h\x96\xa1\x96)\x9ae\x8e\x9a\xa2\x11\x9e\x9e\xc7z\xad\x89\xc0!@@\xd1\x08\xbb/b\xd6\x88\x19\xc6\xc4\xacachjb\xd6\xac\x90\x9a\xbc\xd7\xe9~\xa8\x8d'D\xba\xb9\x03\x91S\x9f`=q\xc7\x113\xfaA\x86>gN2\x80\x9dy\x17Oh\xc7\x91!\x9aX\xaf\xc8\xe4\xe7\xdf`\xe4\xcf\x94\x9d\x9f\xf8\xea\xef\x18k\"i\xc9@\xb0\xa6\xb1cl\x80\xd8\xfe\x92\x19\x08\x96\xa9\x94zF+H\xdd\x0c#\xbf\xce\x9c\xfcclw\xcdx\xa0\xbcb\xdf\xc5\xeclG\xdb\x8b\xf0 \xcc4\x00\xdb\xcd\xb3!O\xf8I\xd1\xd8=\xb2,\x02\xd4\x8f@b'\xd0\xac\x11\xba3\xe4\xf0\x06*\xa6g\x99\x06`\xb6)\x01\xe9\xa1\xc0\xf7\xdf\xe0\xc3)ac;\xc4w\xf7J\x197\xf1A\x91\xf0:cJ5\x03\xe2[\xbf\xa2/\xf5gC?T\x9e\x8d\x98\xdeU\xb3\x1dbh6\xdcS\xb1\xbdtD\xf5\xe3\xb9\xb0\xb1\xb5.N\x066\xc7d\xc3(\x11X\xf8 \xe6\x1c\x86\xbb\x93\xb6t<\xce\xaf\xb1%\x1a\xa5\xdb\xc0\xc4\xce\x92k\x03\x8bq(\xd1\x06\x99\xa0\xba!\xf9\x84\xe0\xa0\x00\x80\xec\x8d\x15z\x00\x01\xc1\xf8\x88\xa0\xa8\x00\xc2\xbb\xb9XP\xc9\xea\x1e\xe0\xce\"\x0e>B\xd8n\x99\x81\xd7\xee\x03r\xd2\xa3\xb8\x07\xe7\xed],\xd0dQ\xac\xd3\x18\xe3\xa1\xed\x18\xdb\x06\xa6\xed\x99\x81`\xca! *d\xe3)6\x1bdQ\n\xc3\xc6rSVx_\x93\xa3\xb6\xb5\xb8,\x99\xe4\xdb\x84\xb0$\x0e\xec\x91\x05R\\\x9f\xbf\x87\x15.\x0d\xd4\xde\x0b\xefaA\x0d\xc7\xee\x93\xac\xea4t\x9f\xa4W\xd7E@F\xc6HJ\xe2\xfa\xc9\xa5\x9a%\xac\x9f\\\xafe\x89zU\xe5\xd9/\xb0IL_\xc9\xd9z6\xb6\xc1\x8f\xb0\xdc\xbb\x93\xf8q\xc0\x97\xeb\xe8\xb2\x80\xaa\x9a\x96\xe1\x02\xea\x7f\x88]\x06\xb3\xc4\xcf\xd4\xd6~\x84e\xa3,\x89\xf9\x1d\xe5F\xf5gx\x0fw\x8c-\x00k\xbe\x99\xb1\x05\x10\xa2\xa5nz0\xfb\xcf\xd4U\x0f\x96_v\xb4\xf9\x9f\xa0\xb7\xb6\xff\xe3E\xd81\xcf\x0f\xd0>4\x04_\xc0d\xfb>\\\x8c\xdc'\xdb\xb4\x1f\x0d\xb9\xe3U\xf3K\x12\xea\x08\x85\x90w\x13&1\xbb& \x1e\x1f\xba\xdc@\xf0~\xefj\xd1\x07\x8b*\xb9\x96\x960?\xcau\x0d\x0c\x10M\xe9\x00\xfb\x0f\xf0\xb6\xec\xf6\xd4\x93\xca\xf8\xa67W\x80\x7f\xc0s\xde\xed%\\\xc6y\x7f\x86\x97,7\x10L\x13wu\xb4>\xde\xb3\\\x030\xfe\xed\xc2\xa8\xb0\x1c\x93\xc3\x98\xf0\xa9\xcf=\xed:\x809\xc6\xae \xd6\xc7\x04<7\x10LZs\xe3\xca\x89M]y\xe1?\x88\xf9\xe1\xae\x16s\xb0\xd8\x91k\x00V\xd7vM\xc0<\x16as\x03\xc1\x879\xd7\x9e\x85da\x86N\x02\xeen\x98d\xe6& -\x1ern\xde\xc5\xc2\xdaJ.\xdf\xa7\x12\xa0w1\x95\xca\xcbOWY\x80*6\xe5]l\x1e\xcd\xcdC\x18X\xfc\xda\xd5\x11\xf2X\\\xcf5\x00\xbb\xedC\xb0\xed\xc7\x98\xc1\xee\x86\x9e\x8e\xa9\xc5\xef\xe5\x00\xc8\x84\xd4\xe2Ce\xc0:\xa6\x16\xd3sY\x00\x07\xd5\xe2{(c\x8a}\x88\xf1SBt\xb6\xff\x07\xf8\xa8\xed\xaad\x0b\x9fa\x0c\xc95\x00k\xf4\xbb\x86\xc5c\xcd-7\x10L\x04\x9b.\x1cw\xe3\xc2\xb9\x86\xd0\x95\x02f\xa9Wv\xda|\x1f\xdb\x8c\x15\xb8r'KOh\\\xbd\xb3\xc5\x8a\xc5n,\xa4\x81b|\x18\x9eW\xe1\x96\xfa\xd8+\x98\x9c\xeaX91\x9aw?\xc8\x19\xd2%\x8a\xa7\xa4\xc8a\x8ak\xb77\x8e\xf1[MX\x9b\x94E\xd0\xad1\x96awU\x08\x14^\xe4\\}\xc7\xeb*\xbe\x0fm\x15v\x8d\xc1\xfbs, \xe6\x85-\x9cn\x93v\xbf\xc4\x95$\xa4\x187mSa\x10x\x7fb\x99=O\x0c\xa9\xc1\xe7)/?\x02e\x01jRC\x16\\9\x19~F6Z\x03\xb0\xd8\x92k\x0f\xaa_`\x82\xbbkD\x1d\xc2?\x8c\xa8\x83U\xb7\xdc\xbc<\x84\xeb\xecj\xdd\xe83L\xbbr\x03\xc1\xf2w\xae\x9d\xbb0M\xca\x8d\x0b\x17\x96ps-\x0b\x90\xd5\xdeUy\n\x08\xe1V\xdf\xb1.\x97\xef\x1ba\xfd\x11\x96\x9d\xc6N8\x80;\xc8G\xb8\xb9\xb1\x934\\\xab\x8c\x9dD(\xce\xd2c\x01\xaf\xd0\xd8I\xc2H\xe8\xbe\xf0\x9a\x06\xc6\xc2\xb1\x93\xd4\\\xc6\x08\x88o\x0b:\x17\x80\xfa\xb8\xc6\xb1\x16\xa7,\xed%Vz\"\x00\xe0`\x8f\xe5\x86\xb1\x93\x18O\x0clR\x11\xb0\xea\x1d\x03\xbd\xd2-\x97Q7\x0d5\x85*\xa6\xbd\xe62\xca\xc0g-\xa4-\"\xc4\xb6!`H\xd3\"\xaf\x03\x97\xca\x18\xaaH\xfc\xa1/+\xcd\xfa)f\xe1c\xc53\x9e\xe2\x83 \x002\x8a\xef)>\x08\x97A$\xc4\xe4l\x0c\x9f\xf1\xf0\x8a$f\xb8\xeb\"\x87\x19\xee\xa1HaFFe\xea`]H\xb6&%\xaf\xa7\x98\xe3^V\x9e\x9c\xf8\xa6m\x0c\xdfI\xea\x991\xe7j\xb9\x1e`qx\xcc\xb9\xd2W\xb1\n1\xe6A\xe0\xc3\xbd\x02&w\x97y\xa2\xda{\x93\x1c\n\x0d\xfa\x11\xad\x93\xd5\xd5\xc8j\xca\x97\x13\x9bb\xb9T\xc3\xd5\x13\x17u\xd5\xb7y\xec$\x8e\xf2+\xff+,B\xebR\x85\xe5\x07#3}\x04\x04\x13\xe5\xcbZ\x0c\xc7\xc2\xf6X\x030\xee\x8e\xb5\xc4JQ\xdf\xe4\x8e\xb4dz\x1c\x9b\x9c\x8b\x96\x0c\x89\x97\x8dx\x86\x95\xf1\xb1\x81\x10:[\x1b\xef=6o\x17\x92sg\xd8\x16!R\x86ma\xc5z\\\xba\x01\xb6\x90\x8b\xd2-\xb0\x15j\xeeKj\xa0\xbc\x8eZ].\x0e\x17\xd6\x00\xc6w\xfc\xc1\x1dG\xb2\x82G\x18\xf1\xafh\xbfV\xcc\xfd\xf65\x00\xf3\x9d}\xee\xa9\xf3\xf0\x18+\x00W\xb8\x07Q\xbd\x0f\xf1\xe8\xf65\xe4\x1e\xde\x17 \x81C\x89qj\x9f\xfb*[\xcc\xdb\x18\x97\xafht\xc3\xf3\xd9\xd7\x00<\x9f+\x063\xb0\xa0\xb3o \x98\x94\xec\xdb;\xdfO\xac\xa7g?\xe1N6\xb4\x82\xae\x18D\xc2\x87`\xdf \x12\xd6A\x0e\x94'\xd4C\xcc\x04\x0f\xd4\xce<\xfb\x05\x16\xc0\x0e\x94\x13\x14\xd1\x9c\x0e<-\xfe\xe0k\xe67\xf4za\x9b\xc2\x81\x06\xe0\xfd?\xd0\x0f\xb5\x90\xb7o\x0f\xb4\x8eL\x9e\xbb}Cf#\xc06\x90\x03\xf9\x15\xab\x00\x07:\xbd$y\xcb\xf7@\xdfA\x927|\x0f\xd4\xf3d\xe4!\xdd\x03\xfd\xe2\x0bf\x05\x07:\x99\xe0Gx\xaf\xde0\xe8\x80\x95\xef\x03\x03\xc1,\xef\xa0\x88\x0d\xc1l\xea 2\xd6A\xb2\x91:<\x9d\xbc\xdc{\xa0}>\xc8\x83\xbdo\x18L\xc2\xc4\xea\xc0`\x12&\x8a\x07\xc6;\xee#l\x1f<0\n\xd7G\xf8\xb6\xed\xc0\x88\xcc\xa4\xa7q\x0dK>\xd8\xaf%\x00W\x8d\x8d\x0e\x93\xdfC\x03\xc1\xb8yu\x11\x84\x12\x8c\xe6\x87\x0e\xd8\xaf\xf0\xfe\\\xd5$\x0b/\xda\xa1\x06`\xbc\xbc\n\x1d`\xd9\xe6\x10\xda\xc7\xa4\xfd\x90\xcbdBX5\xbb\xaaO\n\x96\xdf\x0f5\x00\x8f\xe7\xea*\xf4\x8b\xef\xa2\x0f}\xe8\x18+\xadW\x0d\xe2a?\x9fC\x03\xc1D\xff\xaaA\x14L \x0f\x0d\xa2`JxU\xd9\x0b\xb1\x08t\xa8\x0c\x86\xa4<\xe8;\x9f\xe1\x83z\xa8\xf4 l\x00\xb8fBQ0\xc2\xdf1\x10LT\xae\x99\x1b\\\x8c\x1ew\x0c\x04\x93\x90k0\x0d\xbc\x8cw\xe03F\x82k\xea\xe5vL\"\xee\xa8\xef\x98\xa6\xdc\xe1\\?\xe2\x89\x19\xc65\x9eDW|/\x1b\xd6?\xa3vM]\x9fb\xc9\xf0\x8e\xfa\x8eq\xe5\x9a\n\x9b\xc6]\xdd\xd1\xc8E\xa6\xa3,\xfe\xa4\x030\xf8\xff=\xee\xe0\x8e?0!c\xf8l^\xd3ar\xf8\xb6\xed\x8e\xc1;|v\xae\x19\xbc\xc3D\xfa\x8e\xc1;|p\xef\xec\xdf\x92k\x85 \xd7\x9d\xfd\x10\x00\xef\xb6\xcc\xf7\xbb\xf2\xaf\xbb]\xd6\xcfC\xe9g\xda\xe6]\x96uY\xd8a\x7fd\n\xb5\xf2\x94\xb34K|7k\xbdj\xbe\x8e\x9d\x84%\xec\x0c\x0b\xdb'\xe7^\xe9T\xbb\x8a\xe4\xf7\xf9\xeftf\xf2\x90\xa7\xae\x13\xf3K^Q\x93\xcf\xf0\x838J\xb2\x94\x9d\xa9\xf6[\xeeTw\x11v\x99\xdfeN\x97\xe5\xec\x0c\xcb\xaa\xdd\x88\x9fh\x84\xcf\xc4Qz\xc99x\xb5\x02\xf5\xfb\xac\xfd\xf2,;sF\x14H\x13w\xc6\x1d:\xc9R\xe4\xf1\xc5\xac\x9dup_\xe2\xd7\x8f\x12\xd6\xce\x8e\x1e}\x95e\xec\xbb,}\xd5VF\xb7<\x07-\xb7Cfo\xbe\xc3\x12\x9e\xe5I\xc8\x8e\xcc\xbdZ\xdb\xc8\xcb\xf3\xb2\x91\xd0\x14v\xd8\x19\x96\xb4\xa36\xb4\x98\x06\xbe\xcb\xdb9;\xca\xe6\xc4\xeat:]v\xe4\x08\x9f\x89\x9d$\xe5\xc9\xcc\xd8 |\xcf\xc9\xf8\x9a\x1f\xee\xb5\x9d\x0e{\xe9%\xd6\x96+!\x16\n\xea\xf0\x99\xc0\x0f\xf7\x96\xa20\xe3a\xc6\xce\x88e<2\xdb\xb1\x8f\xe7\xb4\x1a\x8bhGV\x17K\xc0^\x13\x7f\x9fa\xf3l\x81eG\x8f\x92\x8aw\xc9\x173\xebo\xd5\x97\x93\xeb\xec\xb33lV\xad\xb4\xe8\xf3\xc4<;\xd2\xb4\xa0\xa2\xcc\x91v\xc8\xbe\xc7^\x11\x7f\x86\xec\xbbl\xeed\xe7\xd5\x0e\x19\x81XX\xebd:j.t\xfe\xfe\x83\xf4\xe8\xf1A\x97\xb5X\xab3\x93E\xf2\x0eg\xc9Iy\xfb\x85\xe0\xf0F\xef\x16w\xb3\x19\x8f\xf7\xfd\x90o&Q\xcc\x93\xec\xb0\x9duY\xeb\xe6M\x9e^\x8a\xbc<\xe0\xad.\xc1\xd6 \xe7\x0b\xec\xc8l1\x82N\x97\xc9V\x9c<\xc8\xca\xd3\xac\x99%\xc5\x147\x1a\xc5Q\xc8\xc3,]`\x8en\x89\"\xfb~\xe2\xc4K\xa5\xa2y}\xd14s2\xbe\x19\xe4\x03?L\x17jXA\x1as\xb7\x0e\xc6Tw\xdb<\x90\xb9&\xd2\x05\x96\xd0^\xf4/-J\xf9\xd6Bw\xedu\x9d<\x1b>\xc7\x08\xa2\xe7i;r\xd2\x13Mm;r\x8f\xd2\x05\x96\xd6\xcf+\xe1^\xeer\xd1\xb5[\xbf\xd4\xfaWZ\x84\xc0>P\xf2\xf5n\xcd)\xbcK\xe9l\xdc\x0e\xdb'\xe7\xe7;\x16\xc9\x14@'0\xc87\xa0\x93\x18$\x88W_\x82NaP\xaeA'H\xadT58\x7f\xe2e\x0c\nt_'\xc9\x08]\xdd\xe0\xc9\x13\x9d\xce\xab\xdf20}JX\xbf\x9e\x1c\x08\x02\xc6g\x8a\xc3\xc8^c\x9c\xd96Um\xce\x02\xe3u+j\xe98\xa6\x1d\x0b\x92Mz-\x88t\x95\xd4j\x0e\xfeGw)\xbb \xf3 `G\xce0N\xe59\xc9P$\xcfc~\xc8xG\x93\xa18\x89\xb2(;\x8c\xf9\xcc\xd0I7\xf6CM\x90f\\'\x08\x04Q\x0bA\xd6\xc9\xae\x877\x04S\xb9\x1e\xde@|N\x0d\xb3L\x8b\x04-,-\x02\xfbF\x90J?\xdd\xdew\x06\x03\x9e\xcc\x0b\x8e7\xe3\xa7\x1b\x8b\xdb'\xe4\x9f)O\xc6\xb7\x1b(\x82\x103y\x91\x942\xc5#KtY.\xddJ\xa4\xec\xaa\x93\xe6\xc7\x03&\"\x99\xb0\x90\x00\n\x17^l\xb1\x97{fz\xaek\xcd\x03\xcc\x9f9o0\xefp\xde\xa4=/2+vD\x00\x01 \"\x80$)Y\xd5}\xb0\x96\xad$\"\x10\xd7\x1d;\xf6}'a\x00\x9b*\xfaf\xe7\xbe\x92\x1bl\xbf\x0d\xf1\xed\xd6\x8e\x12\xc6}-\x8cW[\xd1\xde\x07]=\x1d\x13W\x0d\xd8;#\xc5\xe1U^\x10z\x91R\x1c_aP\xfc\xeb\xbb\x9c6\xa2&\xday_\xf6\xa6\x0b!\xdf\x16\xc7\xce\x1cz\xec\xcb\x85\xcdc\xa7\x851\xd5\xf8\xec\xa3\xcc\x94\xf7t\xc8\xb0/\x9fq\x03\xf4\xc5L\xd94s\xb7\x89\x85\xf1o E\xe3\xdf\x12\xfe\xc6\xbfk\xdc\xce\xfe\xac\xd0\xfe\xddLI,e\xffvUw\x8f\x91C\x1d\x82\x83)\x84\x13\xbcXn\x86\x7f\x95\xb8\x17\x87\xed\x85\xf9K\x1f\x89\x15F\xfe\x18\xcee=\xbd\xce=\xfb\xb9MP\x0c\xed6\x93\xc4_\xbf?=#\xe1\x9f\xa3\xe4IY,\x92,\xfc\x99\x18\x88\x8a\x9cR\xd1JZ\x9e\x96\x8c\x1e\xa8Hy\x05!\xe2+ \x91\xd2D\x88\xe4\x9f\x86\xd8\x16\xbf\xe8\x84#\x0d\xaan.\x95-\xee\xceP\x7f7k\x87.\x83}\x7f\xed6\xccvq\xab\x8c'\xdc\x01\xc2+>t\xdf{\x11\xe6\x85\xd3\x06\xfe\xeav#q\x91]\x1d\x92\xbf\xdb\x8e7O\xb2\x03\x7f\xb60\xcc\x0d\xa4[\x93\x1d\x06\xbe\xee\x0e\x1d\xc7\xd8Q3\xa2\x14R\x8a\xe9\xe6\xb1\xba\x14u\x0e\xd3\x91\xa6\x94\xe2\xdf\x92Q\x01\x94\x0d\xb1\x14g\xd8J(\xcb>\xb6P\xbe\x84bn\xfe\xc1c\x7f\xf6}D\xf7|\xd2\x04\x00m\xfdk\x0d\x03\x11#\x03\x92\x96\xf9\xc2\x8e\xc9\x05\xf8\x14\x81\xf3\x1b\xbd\xda\xd6_\xaeQ\x056\xf3\xe6aT\x90l\x00|@}\x88\x18FE\x91-Q\xd6\xbdv\x1cG\xc1v8.X\x8b\xa2H-\xfc\x14!\xd7\xf2\xd3\xf0\xcf\xe4J\xbc\xa1\x84\xc2\n\xc3/;\xfd\xd0>\xe2?\xc8\x7f\xadt\xe5*\x99\xbfJV@o\x8d\x8a\xad\xf2\"\x12\x9f\x15\x0b&2\x7f\x92e\xfe\x95\x9d\xc1c\x18\xc1>d\xb0\x01#\x98\xc0\xa6\xe3\".\x18=\x82\x10\xbe\x82\xec\x11\x84\xeb\xeb\x0e$\xd3\x90V8\x96[\x9b\x86\xc7\xdd\xcd\xa4}\xfaws\xd9\x97\x155\xe3\xd3\xcb=j1\x8b\xd3\xe2\x98\x92\x8b3\xbf\xb0\x13\x87r\x93mV3\xd1^\xff\xac\xe0\xf7\xbf\xff[\xf2\x8c\x9a\x9a\xbdK\xa1\x82\xdc\x06W\x1f\x0f\xe3\xebVe\x91\xef\x84\x8d\\\x99\x81\xbd3\xd6y \x03+\x13%\xf5\x86\xa1Z\xa7GB\xa0\xd5\xe4E\x1d\xde\xd6\xc8\xd7\xe6m\xbev\x18\xf1\xb2\x12\x8f\xe3\xf6*#\xccK[\xe1\x9fB\x89\x7f\xe2\n\xff\x14\x1c\xff\x14\x12\xfe\xc9\x18\xfe\xc9\xe0+(\x1eAF\xf1O<\xcd\xba\xf8'\xd3\xe0\x9f\x04Ug\xb7\xc6?\x127E\xf1\x8f\xdfB/1\xc59]\xd1\x8e\xe9\x88\xaf\x84\xd7?)+E>gV\xa9\x8b\x07\x99\x0e\xa2\xa3MH\xaa\xa2\xfb*N\x88\x15u\x98\xa4Z\xa9\xf1P\xaf\xd4\xd8T)5X\xd1H%\xcdcEz\xa5\xc6\xd6\xef\xab\xd4\x10\xbfd\x91\x7f\xb3\xa1\xa7~\x14\x9d\xfa\xb3\xf7\xf9\xa4&b\x9as\xf9\xb6(\xd2'\xa8\x88\x8b\xd4\x15\xde\x12Lc\xf5u\x12\\Mj\xfa\xbcY\xe7\x90a#\xad\xfa\x92\x97?M\xe2\xc2\x0f\xd1\xdfL\xa3\xbc\x94:;\x08B\xf4V\xc8\xd55_\xa7\x84%\xff\xa9\xfa\xd6(\xe9\x12Q\xf1E\x18\xbf\x9f@(j}\xe6\x87\xc3\xb7c\xbb\xab\x9fKxI\x07\x90C\xbc\xbe\xec\xd8\xa6p\x8cUF\x14l\x91\xa8XQ'\xf1\xd1A\xb4\xff.%\xa8\xf5B\xc0\xedr-\xb1\xb8\x18*ex\xb7\x0e7\x0cI\xc9\xec\x8d_,\xba\xe5LJbU@TA\xa6\xa5\xb0)\x0b\xe7`\xaf\x15\x95\x1e\xb0:\x03\x9cH\xe0\xe9ul+O}J\xf5\xd0\xdb\xc4\x05\xebU\x02\xd5$\xda\xcc4\x9d'SI-\xfd\xb4\xa6-z\x94@\xda\x8e\x83\xf0\xbc\x03e\xe2yO\xae&\x12c\"\x9ekW\xdf\xdcb\\\xcd\"\xc6\xeb\xaf=\xc8\\\xc7\xaa\xf1\x81Z_|\x91\x91\xb9\x10\x13\xecc[0\xb9\xd9\xf8A\xcc!W\x16_\xab\xc6\x17\x99XI\xba\x9b\xf2\x00\xa3jc\xe90\xd5\x8c-\xf0=\x9bUR\xaaa\x02\x83\n\xf7LZ\n\x0c\xf9\xd1q\xd3\xd0\xbf\xf3\xa5\x0b\n\xfe\x94\x98\xd6\x12pX\x13\x98\x99\xc5\x01\xb8\xe4Q\x8f\xc8\x00\xfd\x86,s\xa5%)\x16I\xd0\xdbV\x8a\xee1=\xa2\x15q\x9e\xe9=\xc3\xd8t\x17r\xba\xdd=\x12\x99(J.\x8e\xb2\xab\xe7\xc5\xeb\xb2\x98\xb4\x8d9\xe5\xe7Z!<\xd0\xbdo\xbfko\xe3\xb0C\xcb\x8eY\xfey\x194uo\xa3Pu\xe7\xd0\xcb\xc8\x0e\xc5\x9d\x13\xf6\xdf9\xe1\xe7}\xe7d5\xf1\xa1\xbbu\xa4*\xdf\xd3\x85\xeb\xd6\x0b\x07\xdfNX'\x9e\x87g\n\xa8/\xab\xfb\xabb \xba\x95\x98\xb1\xf8<\xee\x96D\xec\x0ee\x06\x84GW\xa9b\x9c3\xac\x12\xe6\x07\x97dV\x16\x8a\n\xf3\x9e+4\xc5\xf2$~\xba\xf0\xe33\xc5\xf7\x01\x82\x8d\xf5\xd2\xcf\xde\x07\xc9E\xac\x92?.X\x95e\x12\x90\xe8\xe0\xd2_\xa6\x11QU;g\xd5:\xb4\xa1\xaa\xee\x12\xb85q\xc1\xe4\x01\x01\xc9gY\x98\xd2\xad\xb7*]f\xf7\xb3\xb3\xd6g|\xe9\xf8'\xe4\x02\x12\xefu\x16\x90\x8c\x04/\xfd\xb4y\xce\xe9ZG\xb4\xda\x99\xf7\x9e\x08\xe1w\x98\xe5E\x9bu\xa3\x80v\x05{p\x86]\xa8\x90\xd6)\xec\x81\x95\xe0)fw\xd3U\xcd\xef\xa3\n\xdar\x81\xc9f\xdb\xb6?H\xa2\\\x19n2\xbc\xf5(\xeb\x1b\xce\xf0B\xba\x97\xcc\nRl\xe4EF\xfc%\xbf\x08\xe9$\x98\x91k\xe4\x85q@._\xcfm+\\\xfag\xe4\x1e[\x88N\xa1_\x06a\xa2+<\x0f\x03B\x0bu,\xf0 \xdb\xd6\xe7qZ\x16*m\x03\x9f\xcb\x0c\xf6\xeb\x0b\xae\x85DOt7\x1d\x93f[\xf3\x90b\xecK\xf3;\xc1\x0e\xa1\x82V\x98t\n\xb5\xa3)\\lL;(.'\xd0\x8f*/\xae\"b\xb2^\x07\xf4\x1a\x880\x98\x07\x1d\x9d\xb6b\xf72\x026F\xeb\xdf\xfe\xf5\x8f\x96\x90}\xdf\x14\x07\x81\x0e:NN\xf0p\xea:/]\x88(\xc0\xdf|\x85\x1a\xbdfI\xba\xc1O\xb8v\xba\xf6\x17\xfc^p,\xe7#L7 iFf~\xa1\xdb\x0b\xca\x95\x0b\xbcQ\xd5\xa4\x97\x82\xfc\xb7\xd8\x0d\xd3\xf8nw\x88dj\xb8w\x9c\x12\xe1\xec\x1a\xa9\xb0\x06+\xab\xabta\x1a\xf6<6\xf2\xfeA\x98\xa7~1[<\x8f\xc3\"\xf4\xa3\xef9\xcb\xaa`J\xc4\xc3n\xff (\xf8\x12\xf1H\x13\x9c\xa0\x9f\x94\x05\x1b`\xc1\xbaz\x01\xb4\xcd\xc8\x9c\xde\x04B}E\xcehs\x13\x06\x8a\xcf\xe7\xb0\x0f\x01L`\xae\xffhU*\x15\x18\xa5\x8azu\x83\xfd\x86z\xef\x9d\n\x1f(\xa5\x1dZC<\x18p\x07\xc9 \xb24\x9d\xfd@\x05'yRf32\x81es\x04\x86\x83\xb2P5\xd3\xbbW5K>\x01_\xc1p\xcb\xfc\xf8\x04\xcan\x0dr\x99\xfaq\xf0\x8c\xa4\xc5b\x02#\x85t@\xf0\xdbJ\x01\x9c\x80\xda+a\xb8\x83$\xac\x02\xf8jA\xd8\x9c \xc2d\xe2WQ\x9f\x13&z.\xe4\\w:3Y\xfb\xa3!\x12j M\xd5\x15\x90\xd58B\x96L#\x06\xec\xdd\x19\xe8]\xe9 \xefz\x8c\xa7\x15\xe9\xa2\xad\xd2\x90\xbc\xc5\x14\xeb\x95\xb0\xaf\xad\x9e\x18g\xcc\x89\x9d\xee\xed\x05B\x98\xc8\x996\xedh\xd2L\x12\x03VJn\xf8\x17\x0b\x8dW-\xfa\xaf~\xb2\x19\xff\xd4\xd4\x81\\\xc9zS\x818X=f\xaf\xf2\x83\"i!\x04Y\xdbCQd2\x87Z\xd1nY\xbd\x8a\xd1\xc2\xcb\xd3(,l\xeb\xc7\xd8r\x86)\xd3\x15\xad\xc4\xf0\x186a\x9f\x1b\xb3\x11X\x87\x91\xe3\xfd\x94\x84\xb1m\x81\xe5\xc0:\x14`V\xe0\xf2\xcat\x10\xeaM\xa3\xb8\xaa\xa5\xa9\xf5\xc5\x06\x8d\x1d&/\xfa\xe5z\xd8\xb6\xa8\xa8\xf3\xe6=q\xdc4,\xb4#\xafF\x91\xb2\xe5#\xef\n\xf6 \xc5\xb7\x9f\x1b\xf13S\x918 /\xe8\x908!/\xe8\x908>/Pz\xbb\xcfT$N\xce\x0b:*\xcf\x88\xdb\xe9\xd6c\x9d *gf\xa0rf\x9f\x9e\xca1;e\xf6P9x\xa5\xbb=\xc2\x90U\xa1'L\xce\x18\xd3\xd3k\x88M\x9f\xd0\xcbI\xc1\xbe\xaa\xd5Hx\x06\x14gY\xee\xe3{?\x0b\xfd\xd3\x88\xa0\xc8c\x85\x0e\x85R;\xec#\xc8bn\xb3^(\xfa\xd3\x7f\x951O\xfc2\xcbH\xcc\xbf4\xd3j\xd5\xa4\xcfH\xf1\xa4(\xb2\xf0\xb4,\x88m\x05~\xe1o\x9c\xf3>\xfb\xe8\xac\xe6\xc2\xa9\xaf\x06K,\x8d\x05{\xd5\x8d\x82\x91pb\x83\xa9\x0e3\xa66\xc68AZ9\xd1\x97\x9f\xfb\xd1\x04|e\xf1\xb5f\x8f\xabE\x1f\xb4\xa3\x8c\xe3\xc0\xddd_R.\x97\x04\xac\x85\x8e\xe9/\xef\x04\xcd\xdc:\xdc\x00\xfa\xafh\x90\x08\xb4\xbd7T\x9cE8\x8c\xb3\xa8\\\x8b\x9f\x85\xc1\xcb\xa4\x8c\xdb\xc9\xff\xe0\xa32\x19\xdcB^\x0d'\xa4 \xbcH\xf9\xd3\x96\xebcZ\x08%>#\xc7\xcb,\xb2\xfa/^\x15Y\xd7Z\x8b\x1f\xc2(zKf$<\xc7\xcb2\x1f\xb0&\xbd\xa7|\xc8\xa2\xc4\xb2sJ\xdf\xc9^\x15\x1f$\x955{\xe3+\xf5\xdaS\xba\xaf\x1eqk#\xd0\xb5\xab\xf9\xceD\xc4\xd1\x15@/\x19o\x1e\xc6\x81D\xfc\x0d\xa4\xfc\niwyl\xc5F\xdf\xda6LF{h\x8c\x11Vdl\x0b\xb0b\x15`\xe9\x1b\xb3CVO`\xc9\xdc\xaa<>\xa2\x96:zu\xfa7\xb1[\xf3\xc5o>|\x80\xac\xc7\xb0\x11$\xac\xd9n\xa2\xf7Cf\x92\xda_\x0fqj\xa1P\xb7Zz\xe6\x0e\xd4\x08\xb7\xa7Ha\xb31\xf4`\xdf\xa9\xf8\xc4\x8c\xd3\xee\xfc\x98\x0f\xdc7\xcd\xe9\x1e `9\x98\xcf\xc9\xac\x08\xcf\x89\xf8\xd2\x88E\xd0\xfb\xaa}\x92{\xd5\x1d\xb2k\x94|\x92MgW{\x82\x06\x1e5\xb3\x04\x87\xc7\x14\xf4\xf2\xf0g\x0d\n\xe4c\xceo*\x14\x91\xd5|\xc2\x13L\x0d\xd8\xae\xbe\x93\xc8?%\x91\xb1\x9bE\xb1\x8c\xbeA%\xf3\x8d;aa\xd1\x8c\xbd\xd4\xea\x03\x04\xf0&y\xad\xeb0fT 3\xb7k\xda\xa2\x98\x00\xa6o\xe1\x13&p\xeb3\xa0\xe6g[\x8693:C\\!W\xd7\x03\xa7\xdb\xa8\xa7\xb3G\xf6\x8a\x841N\x8e\x905\xf5\x00\x1374\xbe\x0b\x88\xa3\xb4LY\x90`\x83\x8eP\xb7A\xd6S^\x0b\xde\xbd}1\xb1\x0c]7Dg\xa1\x9d\xe1\x8c\xb4\xb5\x17\xdb\xb5d\x8b\xd3\x0c\xd2y5|\xd8\xb4s\xd2Wk\xd89\xf9\xab\xdd\xa9}\xe0\xd5c\x89\x03z\x7f\x0d\xf1\x98\xce\x1a\xda\x06\xd4~\x1bC\xea\xf1\xdb\x95\xc4\xe5\x12\xcd\x11ns\x8e\xe9\xd3\xe2\xe8z\xaf\xf9\xfa\xec\x13\x13\xcfkZ\x8e\xc6\x14V@\x050`\xbf\x06\xa2\x03\xa8\xe2?\x92`B/\xf3\xbd=Hl$\xa6\xfa\xa9\x1c\x86\x1a\xfa\xeb \x9cc\xacH\xb1\x87\x89\xfaq`\xa2\x9fm\x88\x96\xb8}\x93\xe5\xa6\xb5\x05\xb9T\xf1s\xf2\xc3G\xccW\xa2\xcf&\x0e\x86\x83\x83\xb9\x91.\x0c\x9a\x16D\xeb\xf0Q[Ctj\xf4\x88[\xeb\x05\xee\x13\xbb\xce\xf1\xed\xe7&v\x8dtb\xd7H'v\x8dtb\xd7H'v\x8dtb\xd7\x88\x89]\xebQEL\xc0\xaa\x12\xabF\x9f^\xac:\xbb\x8dXU\x12\xac(\xa4\xa7]\xad\xadVy\xdc\x92Z\xdeJy|+\x11\xcf\x9dr?}\xbcM1\xc4)F\x19\xe9\xa3\xa6Q4\xb7\xa5\xeb\xb5\x10\xb2\xa5\x98\x81I\xdbMk\x1f\xa1w\xee1+\xa4p~\xe5\xd8\xed:\x15\xd2\x17\xb0>GI8\x962\x0fE4\xe5a\xf3\xe8\xe3\x9d\xb9\x8b\xdb\x0fYX\x90\xd7qt\xd5\xc0\xbc\xedG\xa7\xabp%\xb0\x1f\x0c\x08\x83\xa1\xb7W\xcc\xc0\x80\x96\xe9\xee\xaa\xd3g\x02\xd9\x85\x1f\x07\x11y\xbd\xea\x88[\xa0;\x14\xd0(\x10\xdf\xfb)O\xe2{\xa1W\x90\xbc\xb0\x0b\x16\xc0^\xb6\x1d\xe0yf`2\xc8\xa6\x00VY\xbe\xf6\xe17m\xaf\xbc\x91vlX\xc1\"9;\x8b\xc8\xf3\xfc \x08\x8b\xaf\x93K0$\x99\x91\x1f\x19\xbf\xb2\xb1\x0f[y\xe9\xdb~\xb9W(F5\x815\x8c'\xc0\xfe2~\xa7\xb6\xc0\x84\x1e\x98\xc7\xa46\x9d\x08W\xf2#\x8fE\xe1|!\x9e\x0e\x82\xd6W\xe5\xa7A\xa3p\xa4\xc3\xea\x14t'w{f\x1bV\xb2\xa9\x80\x15\xf8o\xfa\x08\x05u\xe3\x16\xaa/\xf1\xc1*S\x1d\xf6[\xdd\x02\x02V\xb1\x82\x001\x85\x16\x9e\xe0\xb6\x04\xf5\xdf_~\xa9\x9e\xaa-Ur\\X\x93\x1a\xab\\N\x18\x11\xd8\xf8\xb3\xd2\xeb\x0f@\x0b2d\xae\x8e\xf1o\xbc\xd4\xcf\xc2\xe0]\x1a\xf8\x85.\x08\xc2M\xd7X\xa2\x11\xf8*\xcbo\xb4\xeb\xac\xda\xa5;\x9a\xb2V\x10\x05+\x1e\x86a\xeaxXA%\x0f\x15ie\x88\xb6\"?\x99P\x9f\x0f\x101A\xa5\x9f\x1fx?\x86\x98O\xce\xfa\xba,\n\xb3c#p\xba+\xb3\xad#rY<\xc9\x88\xd2\x15M~JV}\x11\x9e-\xa2\xf0lQ0\xb0\x9a\xf4T\xe1\xee\xab\x97\x9ef\\zz\x13W\xe0\x81\xd2\xd3\x94U\xcc\x0c\xa3@\xf2\xad\x8f\"\x1f\xaa\xf0\xd5SK\x91M\xcer!9\xee\xd9'\xc7\x85s\x13\xa3a-vk\xab\xe7*o^`\x19XS\xbfo\x99fC\xe6%b\x11\xa8\x82R\xf4\xcf\xe9\xc6c\xab|\x13\xf8\x94\xdfqH\x9bX\xb8Rz\xfe\xb4\x15\x01\x15,\x17\xce\xf1_\n\xa2\x06 \x83y8\xbd|\x1e\xacd\x17\x0b\x9ck 3\x12\xe0\xed&\"b\xf6~\xc5\x08\xa2\xfa\xe0\xf5\x7f\xd1q\xae\xe8\x91\xc7\x00\xdb\xbb\xbb\xdc\xbc7~\x9e_$Y\xb0\xf2\xe6\xfd\x11\x9fO\xb1w7\xdb\x0d\xbf,\x12z\xddG\xa4\xa0\xbb\x12\x93\x8b\x8d\x94\xcfu\xc0\xd7\xb1\x08\"8\xf8\x0b\x0ea+|q\xf3\xdd_\xe8\xfdkz\xc2z\x88\xa7\x07\xdd\xe7C\xf6\x85>\x84^\x9e\x83,\xe4\xa1\nf\xda[\xd5\xe0\"\xc8\x8a\x0dF\xf4\xda\x12\x11\xb6\xe4\x94\xf8\x19\xc9\xf8\xbdj\x82\xf7\xdf\xe9\xc6\xc3\xe1\xdd\xea\xca\xbb\xf1u\x87\xd7B\xf0\xd9]u7\xba\xe6\xee\xf6\x8ac\x16\x89\x16.\xcf\xe7\x86\"\x87_m\xab\"\x9c\xbb@6w\x81h\x86#\x99\x01\x08\xc6\xe8\x7fl\xda\xa9a\x08\x81,\xfb\xeb\xd4\x11\xab\x12\x0c\xf6\xfe\xed\xd1\xd1\x1b\xccLK\xe2\x82\xcbR'P\xc6y\x99\xa6IV\x90\x80IR\x08\xa5\x97\xac\xffh\xc1:\xa4\xb0N\x7f\xddN\xfc[\x0f\xaf\x16\x017W8\xed\xb3e\x919\xf6.{\xd1\x002\xb9)c4r\xc6\xab7-\x98\xf4\x1b\xcf\xb4\xab\xccLH_+D\x0b\xb5\x1e\xd5$3c33\xf1e\x95\x82\x92\xaf\x1d\xcf\xe9\xc3\xc4e\xfd\x02$w\xb3\x00\x9d\x99\xa8\xb2\x92\x1b\xb3\xbe\xd1;'O}J\xe3\xd6\xab\xa7\x96\x1e*s\x9d\xd1\x01\x9d\x99\x00\xca\xb4\x9cd\xc8r2Q\xbby9\xd9\xc5=h9\xd9\xeau\x86l\x17\xd5\xec\x15\x06\xb7\xf54\xe5\x15\x87\x9e\x94\xbf\xe2\x11\xa4E\xefT3\x96g\xbe\x17r\xe2\x95\xa7*\x0f\xdbp\xdbK\xd0\x90\xd5\xd0\xa0\x1fL\x15\xe9G\x0d0tM\xb4k\xa9r\xbc\xfa\xf4\x07q\x05LT-\xa7j\xe4\x03\x82\xc8\x19h;\xe5)T\xc7\xa9Q\x07\x8d\xcb\xebxn\xd2\xd5\xe17\x12\x08B\x87\xa0\xba\xbd\xfa\xf2ws\xf6MZY~\xfbp\x03\x85\x82\xde\xaaYGW\xa7\x06 \x96\xf7\x95R>k\xf1\x80$\xa1\xe7\xbc\x8d+u\xe5;pKo\xea\xa2\x11[p\xb8;t\xdb\xa1\xba\x9eT6(\xc2\x9b\xd6\xa3Z4\xa4*U\xef\xfe\x8d\xe2Yw\xe5J\xffhB\x83\xed-\xbd\xd4`\xab\xc3\xd3\x87UQ\xc7\xad\xd9\xaf\x8a\x1e\xe8d\x07\xdb[\x0fu\xd2\x83\xedme\x8ckV\xf4yX\xf2\xc9\xfb\xd9lHX\x8dHym\x9aSyR\x16\x8b\xe7\x05YJ\xb9\xc7\x9b\x15\xea\xec\x0c\x93ZR\xd0\xacR\xa7\xa26\xa6<%3\x1e\xb6\xd0\x9ba?\x98\x90\xeb\xeb\xab\xe7\x01\x89\x8b\xb0\xc0\xa06b\x08\x7f&W\xa8*\xc2\xbe;\x8db`mQ\xf5i\x12\xe7\xe5\x92\xe4?0\x01\xd1JB\xfb\xdea\x17\x8aa\x8b\x0eQX\xe0\xd8Ek\xd0\x9a\xe12_\xcf#\xfft\xd0\x00\x05\n\x97\xd2\xf2\xb1\xbc\x0f\xb0\x8f\xd1\xe0z-%\xea\x0f\xbf\x0f\xf3\x10\x85'k\x9bj*\x8d>\x14FN\xfd\xd9\xfb\xba\xb2:\x1c\x14\xa2QK\xd4^uP\xdd^\x0cCR\xcd\xc00(FO\xab\xd7\xde\xec\xc2\xa5\x98\xbbzT\xca5U\xf6\xa8A\x1f\xf0\xb9j9\xf4\xbb04z\x04\xd3n%\xf1Qv\x95\x94\x05:\x07\xeb+'\xbc2\xf3g\xee\xa9\x1cr\xbd\x99X{}M\x96\xe5\xd2\x8f\xa2\xe4\xe2(\xbbz^\xbc.\x0d\x96P,\x87e\xc1\xeb\x1d\xc4\xfei\xa4\"\xd5\xc4\x83\xf1\x1f\xbc\xb9A\x0b\x12\xad\x10\x0e#\xa8\xebb\x1ag}\xcd\x05\xd6\x1c\x18L\xf6\xbc\xaa\xdc\x1b\x1fv\xc9\xb6`H(\xd9\xb3\xaa\xea\x80!\\UZ\xce\x97\xa8\xc5\xd4\xd7<\xad\x06\xfb\xc6\xa8\x13=a\xdd\x0b\xad\x8e\xbe\xe2\x05\x86e\xaeQf\x8f\xc3\xd8\x01\xab. \xa5?\xd2\xc8%\xfb\x80\x07\x85;BZZ_\xfb\x90\xd5~Z\xa1\xca\x1e\x0f\xb0\xa7\xac\xfe\xdb\xdaM\xbc\xef\x8b\xf7\xb0\x07%\xa5m\x0c>\x7fO(Q\xe5\x859e\xbe\xf4\xb5^\xc3\x1e\x9c0\x16ArS7\xcd\xee\x0d\xec\xc1\xa9\x97G\xe1\x8cP\x9c\xb51rx\x82\xef\xc6\xf7F\xe5\xdf\x8dS\xad\x1a\xb4oZ\xcd\xcd\xc7\xe8\xacO\x05w'}\x0eP\xf5\xdd\xb8\x9f\xd5\x838T>~\x155\xd3\xcc\x1c\xac\xfdX# \x02\xc5l\xc3\x82,\xc1\x82u\x9e}\x8b\xd9\x93v\xae^\n\xf7\x96\x8f\xaa\x1b]2S\xc3\xca\xac\xa0\x13\x1c\xa6\x04\xd5\xf6\xc4#2W>F\xf5ZQv\x86\x1f\xba\x9a\x9er\x0c\xd9x?\xd1~J\x83\xf9h\xdb\xd9\"\xb9\xfe17\xb3F\xedR\xcce\x17\xcd\x9bu-\x1c\x98\x06J\x18\x0d\xa2\x14\x8b\x88\xa7A3\x193=6H1]r 9K\xb3\xf1\xb4\xdd\x02*\xe5\xf5\xaf\x1b\x1e\x10r=\xf4fI\x19\x17\xf6\xad\xceD\x0b\x1c#2\xa0cmg\"7\xcf\xb0\xee$\xc4\xb8zO\x14\xe7W\xa0\xa6\xaf\x96\x0d\xa8\xb3\x18<\xe2Y\x12\xc1,\x89N\xd8\x85\x03\x8d\xdd\x8aN\xd0IK7\x13\xeb\x15\xbap}\x8aq\xc8nO\xda\xe1<\x93}\xa3\x1c\xe3\xb8\x1a\x99\x94\x06\x99P\x82\x8c:%\x9f \xee7\x9fV]\xbd\xf4S/\xcc_\xfa)\xf3\x17R\xd8\x1f\xd2\xe7\xda\x0e\xa5\x8e\x07&o\xd2\xcd\xe7\xa2\xcf\x8fh\x1e\x1bc\x95@G\xcaj\x88ZB\x1fA\xc1O\xe0\x94\xd1\x80}\xd9\x84j\xb6g\x02\x06\xfe\x80>\x99\x7f\x81W\xe6\x04z\xe2T\xa4\xac\xd6\xa2F]?\x84\xc8\x82\xf8\xb5|\xc9\xbe\xc2\xf4%\xc6v\x98\xdb\x94\xec\x94h\xae\xdf\xcc\x04\xd4\xe7\xa3#\x7f!\xa4H\xf2\x97-QV\xff\xbaK\xb2t\x03\x07%jsNo\x02\xe7}\x8b)\xb8\xb7 \xf4\x04\xd7\xaeBEN\xe0\xbd\xb6\xa2.^h#;\x1c\x06\xd8\xbb\x0b,\x7f\x13\xe31m\xc7i}\xdd\xbfJ m\x90o0\x01\xcbj\xdc\x9bm\xb2\xe6\x8e\xee\xad\x8a\"\xab\xef.\xb8\xcbY\x1e\x1a\x07\":\x9f\xf0\xb0\xe2\x98Z\xb2K\xb8\x1a\x0e\x8a\x8c!\x14,c\x1f\xc1y]-\xf5\x13\xdb\xa1\xa4\xe2\xeb:t\xab\x9e9\xb8\x93\x95\xff\x87d/oJ\x0f\xd7\xe0}\x82w=\xa3\xda_\xd7r\x01\x8c7\x80; \xfd\xa9\xbd\x81\xb9$\x03#%\x1a \x83\xa6\x87\xb1\xae\xda\xa5iN\\\xe6y&\xe2\xfb>\xade4\xdc\xff\xe8\xccmk\x8a\xafL + y\xf2 \xf05\x10\xe9\x00\x1c\xef=\xb9\xc2\x1b\xdfH\xa8\xf3\x8b\xa1_\xd8/\x9e\xa5\x97\x93\xe2mg\x06\x03r\x1c\x8bh\xf8fd\x0dm\xdcn\xacmr\x0f\x1e\xc6\xfeI\xd1<\xf9\xd2m\xa0\x06Zw\xcaM@r\x93\x83t\x17\xb8\xf1\xa9\xd1,\xb7Blo\xf4+\xd2\x08\xfc\xf8zP\xbd\xef[\xe0\\\xbd3\x01s\x9d\xf8\xa1/\xf9\xaf|i\xaf\x06\xc1\x03\xdc\xdc\xb5\xa6T\xedG\xa85W\x9be?\x84\x03W0\xcck\xea\xdb\x8e)\x0f\x19C\xe3\n3D\x9d\x12\x0f'\xb5\xe5sY\x0dr\xc0\xa9\x84\xd5h)\xf1\xf0\xc3\x9c\xd0^\x9f\xc7L5\xd4\xfba_\xa4\x90\xc1\x88g\x95 ~Fh\xa7F\x97\xab_\x03Z|t\x03\x8bo\x95\xa5\xf7\xb9\xe8M\x1dD\xb6%\xa9\xe9\xcb\xb5\xd4\x12\x01\xf5Uoi\xb8\xba\xda\xcd\x86\xbe\xac\xab\x92\x95\x94\xdb\x13\x98\xd6!SZ\xf1h\xe9\xaa\x06\x06\x1b\xaf\xf3\xcf\xd0\xa8\xc6e\xa6\x0b\x1d\x03\x16\xcc)\x95\xc1\x1e$H\xecdM\xd3\x91\xccl:\xd2\xf4\x93k\x81\xac_[\xe8\x89W\xab\x98)\x0e4\x94SZ\x83\x85\x83\x84\x9a\xbaZ\\?\xadod\xe9G\xea$\xedyq\x15\x11\x9de)%\xfb\xcf\xb2\xa4\x8c\x83\xa7I\x84\x19\xdc\xff\x7f\x0f\x1e\x9e\xce7\xb7\xbb\xf7t\xeb\xe4\x19\xc6\x92fj\x19\x9dL\"\x9c3\x1bx\xab\xdd\xa8E\x17\xdf\x92O\xfegj\x0d\xd6\x03E\xd9\x10(\xd2\xd8K5\x0dj?\xcf\xe9\x07\xdax\x16\x81\xce\x18.\xd0\x19\xc3\x05:c\xb8@g\x0c\x17\xacf\x0c\x17\xa8\x8d\xe1\x82\xda\x18\xae\xebd\x93r\x0f\x81-\xa5\xb1[\xf0\xe9\x8d\xdd\xcc)\xfe$c7\x15\xed'\x19\xbd(L\xde:\x9e\xc2\x83M\xdbn\x95Q\xf8\xf31\xbf\xe93\xae)jO\xe0\x1es\x11JPO-t\xde\xd98M.\xadc\x03}O!L\xeb%\xcc\xd7i\x8d\xf9M\x88\xe0\xc2\"\xeeX\x9a\x91\x99_\x08i\x80\x1dsI\x8e\\\xc0.\xd7>U\xda0\x86\x8e\xcd\xa7n}\xe3\xc2\xcf\xe20>3\x89\xffE\xdd\x89uW|e\xec\xfd\x94\x84\xb1m\x81^\xe8\x91\xe8{J\xbd\x97t\x16\x1d\xfa\xf3\x97kW\x86\x01\xc3Pd\xb9\xb9\xc9\xb6\x88\xa4\x94#5d\x0b#\x97\xa9\x1f\x07\xcfX\xbd\xbaoOzO\xcf\x9b:\x01\xd4\xcd\x1c!\xfb\x1c \x19_\xa6\xbf\xb3\x16\x9f\xe75\xf4\xef\x0e\x1a\x9f\xad\x83\x86\xc15C\xaf\xa8\x890\x91c\x97\x89\x02~\x93\x87\xde<\xc9\x96\xbe\xa2_\xee\x92\xc1\x03\x9a\xab\xfd1\x84K\xd7\xda\xde\x1eD\x18\xd9\xfb4\x8c\xfd\xec\x8a\xbd\xc1\xecB\xd6\xa9\x9f\x93\xddm\xf1F\xef\xa9\xc1@_\xef\xd2\xa0\xf4\xe4\xe0\x01\x12\xe7\xa12\xdd\x90\x84\xeaJ\x1eS\n\xf6\xc1\n\xe3s?\n\x03\x8b\xc9\xe0\xbbm\x86E\xd4\xfc\xa2\xd4\xd4\\E$\x9a\xdbU\xcaK:\xda|\xba\xa9\x08\xd2\xaf\x90\x07\x04a\xce\xd9\xdc\xc2\x0b\xf3g\xfc\xaf\xe6a\xf8\xcch{\xb7\xca\xbd\xdfL\xef\x0duR~\xe1\xe8\x9e+\xde\xd5u3\x92\xa7I\x9c\x13I\xea\x01R\xa6\\\xcd\xebJ\xde\xc3\xdbnEN\xd2\xb9\xcb\xc6\xf6}\x05\xd6\xd3\"\xb7P\x8b\xdc\x8c\x84R\x15\xf0\xacP\x06<\x8b\xab\x80g\x94\x88\xccX\xc0\xb3\x0c\xbe\x82\xe2\x11d\xeb\xeb\x0e\xc4\xd3\xac\x19\xf0,\xd3\x07<\xab\x15\xf0&\x92\xadJzwx\x95\x17di;M\xdb\\\xfc\xeb\xbb\x9cN\xc7HW1Z\x96\xd9e:v\xc6r\xbf2j\x96\xad8?\xde\x0d^L<\xad\xdb\xf6\x0f\xdd_\x8a\x8d\x0c\xcd\xd1J\x854\xb6\x80}\xc0\xd4\x18\xcd\x06\xacc`\x81t\x9b/\x95x\x0e)\xd5\xe7\xb1\x1d\xf3\xec\x05-XW\xc0]kl\n\x03\x88V\xd3Sag\xfa\xcc/|\x8b}\xe22\x85\x03\xcbZr\x8c}\xb78YWw\x18\xee\xaa\xffn\xe3\xa6\x81\xa8N\xeb\xdd\x8d\xa4\xd3\xba~(j\x84\xd2?\x14q\x1eT\xae\xcc\x98\xb8\xa1\xbe\xf0\x84\x0f\xb3\xd6\xc9:\x91P\x9b\x9are~\x00Ul*\xc59\xc6\x80\xa2\xfb0\x0d\x11|;s\xc2\x98\xcf.\xc4\x02\x94\xf5\x15\x9a\xe7\x0bH\x94\x13\x15S\x8b\xbc\x96\xa6\x9d\xa2\xdb\x8ei\x1b\xb3a{\x93\x0f?\xc8\x9f\xc9\xa6\xc4C6\xc5\xbc#\x03\xb7#6n\xc7\n{\x11W\xaa\xb4\xcc{\x9dq\x17\xf5\xd4\xb1\x1d\xe5\xd6t.\xed!\xfb\xe3Br\xbb\x9d {w\xc6\xef\xdb\x99\x84\xc5\xddeq>\xf7k\x84\xe2\x9b6\x8a%#\x17\xa8G_M\xb5e\x08Mn\x9d\x82\xa8\xa7\x89G\x9de\xa3\xb4}\xa2\xbcrl\xdah\xac\xd9\xb6\x81\xb1\xbai\xeb\xa5\x97\x914\xf2g\xc4\x8e\xc9\x05\xbc%g\x07\x97\xa9m\xfdb\xc1:`D\xc6k\xcb\x05\xeb\xccr:*9\n\x11\xa5\x04\x1f\xf8\xf3\xf7\xa5+\x95\xca\x8e\xd2\x8e\xedqG\n\x1a\xf2\x92Q'4\x0fSX\x8c\xb7v\x95T]\xf9;\xb2\xac\x14\xfb\xfer\xed\xb6\xa5\x82\x99\x0b\xbe\xf7\xee\xcd\xb3'G\x07'\x87\x07/\x0e\x9e\x1e\x1d<;9}\xfd\xea\xe8\xe0\xd5\xd1\xc9\xd1\xdf\xde\xfc\xfbZ\xaa\x88\xe0\xd5\x16\xf5\xf0\xcd\xebW\x87\x07\xbf\xcf\xaa\xeadR\xaa\x98\xac=\xeb\x91\xb8\x10\xeaH\xf1U\x16\x84a\xaf\x93\xef\x9f\xbc}\xfe\xe4\xeb\x17\x07w{du$\xc4 \x0c\x16{\xef\x89\xc2\xa8\xc5\x17K\xad\x069 \xef)\xef\xfe\xcc\x85\xd0H\x11b\x05\xe3V\x94.\xf8\xcd\xf5\xcdnq%\xd72\x8fQ[\xbd\x97\xf0\xd7;\x0f\xa4\xfb6\xa1\xcb\x82y\xf4\x92\xec\xc0\x9f-l\xbdh\x01\xe9>\xef^\x18\x07\xe4\xd2\xfb)gr?-\xd5Gw4\xb1U1\"\x88G.\xd3$+\xf2)#\x80R?\x9f\xf9\xd1S?'\xdf\x84\x11\xa1\xdb\xe8\xd8\x85s\x8c\x1b#.\xd1}\xe9w\xdbAH\xba~\x07-\\loo\xefR\xb2H\x8c\x03\xd7eg\xb43\xe8k\xc3\xb2\x0b\x1b\x8d\xad\xb1L\xd0\xd4\x11\xbd\xecU\x0c5*Z#\x93\xa6W P\xdfd\xc92\xcc\x91r\x89\xed\xed\x9d\xfb\x8e\x0b\x87H\x91\xd7\xa65^^\xf8Y\x91\xff\x102\x0dIlo?\xd8\x1d4\xc3\xd8~8FM\xef\xc3\x07\x9dU\xda\xde\x19\xd6F\x1fpno?TB\xe7\xf6\x8e\xca\xc0%\xb6\xef\xb7_3b\xef\xfeHZ\xe9\xe6H\xc7[\xf7\x1d\x1b\x05n.X\xf8\xaf\xd5\x83\x87P\xbbt\x82\xd2;\x9b\x08'\xb3\x13\xda\xff\xa6\xf8\xe3=ES\xf5~\x18\x92x4T\xa6'\n!|\x15\xac\xe0Da\xd7\x18W\x85\xe1\xfa\xba\x12{\xac\x11\xdcTxL\x19\x94J\x9cm\xd7s\x10\xa2\xb9\xc4\x1e\xa1MzB\x0f\x9bE\x0f;\x8b\xd3\xc6\x8d\x0cYZ\xd9\xfa\x1d\x992\x99C\xec\xe2O\x89;\xbav\xab\xcah]\xf3D\x08*Q\xd7\xc0W:\xb3Y\x17\x0e\xfe\xac\xabg\xb6E\xe2\"\x0b\x890\x9co\xc3\x8f\xbc~\xf2F\xca\x0b\xac\x8e\xd0\xd8\xfb\xa5j\xaf\xf9*\xaaP\x17\x8b\xb9\xda\xdd\x93 \x89)\xdb\xb2f\xa6\xfdoy.F;\xeas\xf1\xb0\x1d\x95\x91\x1d\x8b\x87m\xc1\xb6\x8f\x9c\xc6#\xe9,\xeflb4\xf3\xd8\x1e=tl+,H\xe6\x17\x98CV\x0f\xbb|q(,\xd5\xb3k\xa1\x82>y\x1b\xa9\x11\x11\xc6\xef\xf6U:\x9e\x98\\\x16\x142Gn;u\x00\xed.\xc4\xb6)+\x0b\xcf\xaba\xaf\xb6\xdc\x12\xc2Q\xdf\x86[\xbb\xeau\xdd\xd5\xe2\x95\xedm\x07\xf6\x95\x9coHr\xe81@N\xecv\xa2\xa1Jk\x10\xbb\xb8y!\xaa\x07\x90\xda\xadT\x079S\x16\x94\xf0\x18\xf2G\x0ed\xde\xdc&\\\x182\xcd\xd7\xd7\x8f](\xa6q[\x08!\xa8\x8c\x9b.\xd8\xfd\x91\x9a|\x18\xa9!q{g[\xb3duw\x1a8\xab)\x0e\x96wFGQ\x94l%\xf4q-#$9\x84\xcaES U\xa3\x14\x1c#\x05iBI\x1cv\xa9\xc2\xda\x9e\xde\xb5\x117\xed\x11D\xf0\x18f\x8f\xf46\xc0\xb45\x9bne>\x9d\xad\xaf\x1f;\xb4\xcd\xd2\xa9\xcdU:\x1f2\xe1S\x7f\x970[_\xef\xe9\x16\xaf\x87\x19\x841\xe4Ho\xe4\xd3\xd91\x0b+\xea\xd4r\x0f\xac\xf2\xe1\x03j\xa2\xaak\xe5\xcb/a\xa3\x19\xbbhE\x1c'a\xb3]\xd5\xa9{\xe9\x17\x0bo\xe9_v\xc1\x88\x95\x84q\x1f \xe9\x11\xba\xcd\xb0\x0dq\x1c\xf8\n6a\x9f\x9e8X\xa7C\xdc\xa4\x97 C)7F\"\xea\xf9P\xac\xbds'\xc0\xaf\x83\xfc\x10\x83\xb8SHbD\x9eM k\x0d|\xb3#\xa2\xf3k\x8dPp\xc8\x0e\x88B+\xc1\xc6\x94\xe3\xda}\xf8\x009%/\"\x14\x87\xf1X\xb4\x9c\x9a\x9d\x80\x8dr8o\xb6\xf0\xb3\xa7I@\x9e\x14v\x8ek\xbe\xb33~\xb8K\xbf\x0d\xe11\xec\xecn\x8d\x1e\xb2\x86\xd6a\x84\xe0\x87\xb6\x04\xb6\xdf\xf9\x98V`\x0d\xecn\x8d\xb1s\x9f6p\x7fk{\x8b\xf7\xcf\xeacGt'a\xc2\xdf2/\xbd\xdc\xc5N\xc6\xb4\xcc\x87\x0d\xde\xcc:\x1d\xe7\x06\x1f\xd4W_\xc1h\xd3\x81u\xd8\xdd\xd9\xd9\xda\xbd\x1b\x08\xef\xdc\x1f\x1c vu\xd8\x90\x02\x8b\x83\x12e~\xa5\x0d\x8a*\xdc\xbd7\x90\x19\x13\x1f\xb6\xc4\xf0\xc5\"K.\x802\xef\x98%\x1dO\x80\x05a\x0eqR\x00R\x00\xa7\x11Y\xd3X~dv\xc1\xa2\xf0\x11g\xc5sB/\x81\x07\xc88\x8c\xb7\xb7\xf1\xdf\xed\xdd\x87\xec\xdf\xfb[\xec\xdf\x07\xfc\xfd\x83\x9d\x0eg\xb1\xbb\xe9\x08\xaefHg\xbd\x84\xd4\xaejgd\xd2(\x99\xc6\xf6\xe8\xbec[E\xc2N\xd5\x91\x7ff!\xdbi\xfdlQVn\x9d\x82\xfc\xda\x1eX\xd3\x04o{\xf8\xf9\xd8b\x0c\xd7\xfd-\xc7\xe6\x14@\xed\xc9\x00UCV?mU\xb5\x89\xe9j\x90l\xa7\x90i\x1dK\x1ah\x0c\xa94d-\xe4\x85\\\xa3\x1c\xfe\xa6\xc32\xac\xd8\xa3\xcdQ\xbf\x0d\xf5}:I\xb5(\x9f\xae\xe3\x03\x87Y\x1e:.X\xbe\xd2\xfe\x10\x83ik{i\xf7\xd6)l\x99\x088\x9e_\xaf\xc1\xa0\xf9KDK?\x11\xa2\xb8;0)\x0d\xbb4\xc4\xd5\xf8\xa8s\x0c\xd5z0Le#\x9d\xc3*\x02\xb6\xcdTG\x02$\xd8\x86d6\x13U\x89\xf3U\xf5\xa7\xd2\xb0\xe9\x1bE\x1e\xe5\xf5|\xf56\xd7>\xcep\xdb\xf8\xc6z\xea\xc7\xff\xb1\x80Y\x12\x9f\x93\xac\x00\x0e\xe9E\x02i\x16.\xc3\"<'\x8c\xcdZ\x95\x9a\xef;\xf3\xdb\xbbm\xc91\xc3\xc6\xe3\xed-%\xcd:RJ\x15Z\xec\xd3\x03\xc1>\xdd\xff\xef\x99}\xd2\xb0\xa5\xdb\xbb\xea\x95\x1dw\xc48>\xc7\xca\x94 }~p\xf2\xe6\xed\xeb\xa3\xd7\xed\x80\x15e\x9b\xdfo\x16\xb7\xc5\x01\x9d\xf58g\xb9+\x0b\xde\x15E\\\xe1<3D\xc6@+\x0c-5\x84$w\xe1\xa1S\x90\x17\x84y\x1a\xf9W\xf4v\x88\x93\x18\xf3E\xdb\xe3\x9d\x11\x9a\xf5\x938x\xba\x08\xa3\x00Y\xb7\xc2\xcb3\xcacX?\xf9\xe7>\xf3\xe9\x9dXU\x16J\xee\xfb\xf7C\x18\x07\xc9\x85\x17$3\x14\xa18^\x92\x92\xd8F\x18\xb9\xc8\xc2\x82\xd8\xd6W\xec\xd3\xc7\xa2\x8a\xf7\xcd\x1eC\xd1_\xfdx\x8f\x17\xa1j\xd7\x9bEI\x8e\xe9\x0ds<\xc1\xdf<\x82lc\xe3\x91\x03\x01\x89HA \xaf\x01i\x1aN\xb3c\xbdMYn\xb7`H\x8dI\xf9E\xc1,8)\x9dfD\xad\x889\x95tF\\F\x11J\x90)\x15g\x97-x'\x0ecpcrA\xf9\xbef1s\xff\x8aYZ^\x82\xa6g\x98\xd5\xc2qei\xab\x90p%v|+\x9a\x7f\xa46\x1e\xec\x9c\x08\x0e\xf9\xdb\x0f\xf4\x94\x1f\xbd\x98\xff{\x90\x1d\x8cF\x0f\xd4d\xf1\xb8\x8d\xa0\xb9\xf0`w\xd7\xb1\xd7\xda\x02\x075\xca\xb8\xc1\xfd\xce\x97\xa8\xe4\x84t\x17\x17\xe0\"u_Sfiz\xacX\xf3\x98\xf2\xd5\xa5\xc3\xa4\x04>\x8a\xf31%<^\x9b\x91\x88,\xa4\xf8\xf0\x11\x14BX\xcb\xf7\x03\xbf\xa3\xa8\x01w\x83\xb9\xa8\xfc\xa7\xd0\x8e\xb0\xb5\x0f\x1f\xea\xd6\xd4[\x14\xddt\x8b\x1e>\xd4\xac$\x83N\xdb\xfa\xd9r\xd0\xd5\x82\xd2\x81\xcf\xf3\x83\xb8\\2\xbe\xc1\x96`\x18L\xe6\xd1\x82\xd2=\xac\x93\x83\xd0s\x8d\xe6;y\x1a\x85\x85ma\x8e}\xde!\xb9\xf9 \xed@\x95\xd0ti.\xa7m\xdd\xdc{'\xd3\xe0\xd6\xff]T\xf5\xdf\x92\xa8J\x83\xb2\xb6w\xdb\xef\xc3\x01\x94\x8c__\x94\xd5\xc5e\xbcN\xcfH\xf1FT|=o^\xab\x1aX$\x02\x9d\x01fp\x0e\xf1dMQ\x1b\xad\xa2\xf0)\xa9\x90\xc4y\x91\x95\xb3\"\xc9\xd0\xe4 \xc28/\xfcx\xd6-\xddo\xfe-\xdd\xbe\x93\xe6g\x1c\x0f\xec\x83\xdf6\x00_q\xfdw\xb6nz&9\xfe\xc8V\x17XT\xf7'g\x1f(;P\xb1\x0c\x0f( \xcd\x98\xca-\xc7\x15\xde\xf0[\xfc\x82E\xc6\x80'\x8f\xb5G\x9bc\xc7\xe5>\xb5\x94Z\xc0\x83\x1b\xb5\xb8\x05\xf6\xaa!kp\xd1s6\x17\xba\xb3\xa0\x13m\xe1\xe9\xe1\xe1\xdb2\"/\xc2\\\x11\xec\xe0\xe9\xe1\xe1!%M\x9f\x91Y\xe4\xb3x\xd3\xdd\x80 O\x0f\x0f\xd1\x14\x817\xd1.\x8dB\x12\x17o\xc9\xacP\x97?{\xfd\xd2X\xc8\xe6\xa2->J\xde\x93X=\xf8g~\xe1\x1fe~\x9c\xcfI\xf6\xbc Ku\x1b\xdf\x84\x91f\xe4\xdf\x1e\xbd|\xf1$\x8a\x9e&Q\xc4\"P\xa9\xab\xf4\x95\x7f\x93dK\xee\x85\xa4\xae\xc0\x9c%\xb4U^\x92 \xf4\xd53|\x19. e\x89qs\xbb_\xbe\xf2\x97$x\x95\x04\xe4\xa5\x9f*J\x93@\xb3\xebo\xfc0\x16\xe1O\xd4K\xf3&*\xcfB\xc5|\xd9{\xcdp\x0e\xbf\xff\xd3\x0b\xbc\x8a\xd4m\x1e~\xff\xa7W\xe5\xf2\x94d\xda\xe27\x98%X\x03\x0b\xb4< c\xcd\x80\x0f\xbf\xff\x93 \x90\x0e\xbf\xff\x13\x83\x94$\xd3\x80\xc9!f\\\xfb\xba\x9c\xcf\xb5\x03\xa4\x07\xe5pAH\xa1^\xd5#rY\x1ce\xfe\xec\xfdS\xddQ\xa9jh\x8a\x93rV\xad]Ur\xed\xa2+zb\x07\x945a\x94\xf89|\x05\x0b\xc1s\xc2\xf9\xfa\xba\x8aZ]\xba\x18\xc9~1=W\x18\xbcQ&4\x98\x9e)JN\x91\xacW\x95\x9c\xc0\x1e\x9cR\xa4\x7f\xaa\xba\x90\x80_\xc5'H~\x9e\xd0\xfb\xf7\xc3\x07(\xed\x13\x17f.\xa4\x8e\x0b'\xd3y\xfdn\xee\xc2\x19E~\xd33\xca\x80\xa5.\xa8\xe2\xd2 r]\xd2[=s\xe0d\xba\xc4\xcfC\xfa\xf9\xd2\x85l\xba<\xae\xc5\x9b0\x14a\xf7\n\x804J\xcb\xed\xfbj\xbe\x03\x11w\xe3\xbd_Q\x94:&n\xbc\xbd\xfb\xefv%\xff8v%z\x82\xef\xbec[e\x9c\xcf\x92\x14\xbdU\xda$\\\"\xfc\xf5T\x07\xa6\x123@2\xcd\x8e\x99R`\xe7\x01\x1a\xaff.\xfc\xa2\x97\xf6u\x98\xfaiv<%\xf4\x18\xc9\xf6\xf0\xca\x99\xe8$\xfeF\xd8\xfb\x0c\xed\\\x84\xb1\xa9/(\xa9\xf1v[\xc2\x92W\xc4V\xe35\xa7\xb0\xc6\xaa\xb8%*\x8d\xcf\x9c5\xdf\x16\xd4\xb0p%\xf7\xb7[\xaf\x03\xdez\x1b\x85,8\ni\xd7?\xe7\xef\xdb\xf6\x10K\xd6\xebN\x1b\xb5\x9c\xf1\xf7[\x8e\x97\x93\xd6\xba_\xb1\xb6\x1elvb\xe1\x9dr`m\x8f\xea\x84\xb7\xd6\x1e\xd5\x05\x7f\xdf\x1e\xd5\x01R\x9a\x95\x8c\xbeYx\x89\x85i\x96\xccH\xde\xf2D?\xc4\"\xae\x98k\x16=\x85=\xb0\xf8Gx\xceg\xf6e\xab\xd7\xf7f\x89\xee\x13\xb4\xb0\xdd\x83So\xde,xM\x0f\xc4\x9aY\xda[dW\x1a\x9eW\xe0\xc8C/#y\x12\x9d\x13\xbb\xbdz\xf2\x83\x1e\x1aM\xf6g\x8f\x1ea\xa1\x1e\xccS2C\xfcr<(\x1b\x96x\x88\xfd\xde\x85\xf7z\xd6\xf7\xba\xcb\xd2\x83d\xc7\xf0\x14\xfdQU|\x1c\xdf\x8b\xb7\xe4'F\xd9\x1e\x9c\x93\xb8p\x98\x0fK\xb1 \xb1\xfd\xde\x919\xb4\xa2\xd3\xcd5\xcc\xfcb\xb6\x00\x9cCK\xf9\xd6\x06\xbf7\xbdsF\x15\xb5V\xa8\xbcf\xaf\xa5\xf4\xbb\xe6d*m\xb5\xcd\xe21\xd0a;8\x85\xe6h[\xe0r\xd4\x87\xed@\xe8\xb9\x88w\xa2\x95\x88\xd02\xc4\xb7\xea\x0d8\xe7\xb6\xcb\xc4;\x99\xa9k\\\xe95\xaa\xf2\xd3\xe0.\x89wr\xcex\xcb\x11`\x8c\x9a\x93\x9c\xb1\x97\x9b\x8c\xb5\xac\x05K}p\xc5\x85\x995\x02M`\x1f\n/y\x0f\x13(\xbc\xb9\x1f\xf6\x84@\x87*A\x14?\x1c\xfd\xd5#^\x9d\x02\\\x7fm\x9649H\x96~\x18\xab\x17P<\xfa\x13,?%\xa5?\x124\x1b\x19\xf3\xb5[PP\xf9 \x89)\xfck\x0fF\x8e+\xe2\xff\x94H\x81\xec\xa1I\xb5\x8d\x81*f\x1e\x89\x0b\x92\xd9\\\xa7P\xda\x19\xf2\xe8\x98\xa1\xd8#\x97aas\x06\x7fm\xd3au\xf6\xd0\x1b\x81\xdbX\xefCd\x1f\xd8\x16?w\x1b\xb3\x85\x1f\xc60\xbb\x9aE\xc4B\n\x08Ma\xde\xd8\x14\x82\xf7!d\xda\xd2\x18\xfdK\"Z\x9cc\xc9\x04\"[\x91\x1dP~\x1a\xe7\xb2wYp\xfck>\x9f\x1f\x9fDd\xf7\x84\xdf\xbc6\xe0#\x88k\xd9t\xf8\xc8\x01\xdf\x8e\xa7\xe1\xfaz[9 ?\xf4\x90\xa0\x90\xdc\xad\x8e\xd5\xc8\x05\xd42\xaf\x89}z\xa9\x1b\x93\"z\xe6\xb5\xe9\xf8\xbf\xec\xc5Egl\xf1s\x03\xfd,\x1eD[(\xc4\xe5f\xfbxB\xb5\x13\xa5[\xfc\xbc\xa3\x80\xa9J\xe7\x14\x08(|\xc0C\xe0\xf0\xa3c\xea\xed\xa7\xde\xdeV\x85_54\xca\x80U-\xfa\xb7l7,\x01S\x05\x87\xa9\xaa\x02\xdf.v\x0b\x9b\x92u\x0e\x00'\x01J\xf4L\x0d>\xfa\xc6\x9dz\xd5\xbbv\xc2T\x8er\xaa\xddu)\xbc\x93\x00\xaf\x10\xfcA1\xbd\xcb\xd6\xa0\xf0N.hA\xe1x'\x94\xa2\xa7d\x85wB/\xc81\xfe\xf2\xc5W\xccG\xfdd\xc6\xed\x0d\xe9Eqd\x17(\xc40\x8e\xfc\xed\xb0\x91\xbb\x15o\xaeV\xf5\xac\xc5\xdeI\xa0\x03\x86\xb8\x9e\x14*\xcd\xf9\x9c4\xd7\xaf\xf9\xda\xa5\x9d\xb1\x1b\xb0:X\xf5\xe5\x073\xb4\xec9\xa5\xa7\x19\x89\x87\x00\xc2\"'\xd1\\\x97?\x8f>\xb8\xceo\xd0\xbcj\x7f(\xf1\x04\x12\xaf\xde\x7f\x17\x9e\\L\xc0\x90l\xb1\xaa\x16h\xd3\xb2\x8aGC\x95\x8bg\x18\xc5\"\x0c(\xe9}\xfc\x16/\x98\x11\xde\xcd\xaf\xf8\xef\xbb$\x03^\xb1\xbe\xb2\xde\xc0\xdb\x86\x9b\xdf\xa1wL\x05\xfe1\x03\xff\x11\x85\xef\xd8\x855\xddx\x87\x8d\x93\x8f\xcf<\x91\x01\xfb\xd7\xb3w\xd7\xda\xf9w\xe7\xdd\"2\xea\x1d\x7f\x8dg\xfd\xd0x`\x17<\x82\xe7\xa1\x0b\xe2PX.X'\x0b\xcbq1\xd4\xa9\x0bY\x9d\xc5\xbau*\xd4\xe0Cl\x04\x13\xd6n\x05)\xe2\xcf\x16r1.\xfa\xabf\xfe\xec\xe6\x97\xd5_\xd7.\xbb\xc4\xf5\x93d\xd2>A\xd9\xb1\xbf\xe4\x9b\x97\xbd\xc9e f h?\xfc\xeb\xbcSy!Wf\x84b= \xa7i\xdeco?\x189\xf6\xa1l[\xdb\x1e\x1f\x89\x07\x84\xfa\x17\xac\xdc\x13{)v\xcd\x9cS\xfc=\xec)\xd9T\xa6\x7f\xc6\xb3A\x19\xacf\xad\x9a3G\xba\x97br\xce\xfd \x19C\xefb\xfe\xe7\xa4\xb5&\xb3*\x07U\xb5\xc6\"Y\xcc\x89\xdf.\xcbi\xd9\x11\x9f\xc7\x1a\x05\x93Xp(\xcd}n\x9e#\x04\x97\xbe(v\x92\xc5\"\x13!\x88q\xeaa\x88kG{\xe5\xd41\xb9\x80\xecQ\x17\xba\x04U\xc8n\\\xfa\x86\xdf(\xa8'}\x8b \xd5GNU\x84Z\xe6=v2\xb0D\x86\xe6SoNwy\x88\xb2\x98\xe0\xcdv\x88\xdb\x89?}JA\x93\x0b\x16\xf4m\x82\n\xf5\xc6$\xe7\xf6\xdc\xfb\x13\xac\xc3\xdc\xfb\x01\xff\xff\x0d\xfc\x11\xd6^\xb7\x01\xf2\x8d \x8a\x0e\x1b\x1f3\x13S[\xc6\x15\xdc\xfe}\xec\xd8\xf2+\xa6v\x90L\xe0Y\xc7\x87\x8d.%|\xd3\x9e\x1b]\x9e\xbeM\x16\x04\xd2\x13\x15f\x02I\xf4\xb4\xe9V\xdc\xbe\xc3\x14\x16j@\xeb\xacS=\\\xbb\xa4+\xbc\xf6\xda1\x8e\x1a\xf7\xbbo\xd8|T\x17v)\x0eG\xb5o\x870\x81>\\\xd7\x19\xda\x9a\xfd\x9a\xc9\xeb\xb7\x1fl\x99\xa2\x85\x1ez\xcc\xea\xd9\xc3\x13d\xbf\x97\xc1\xc24-?\x8a\xfa\xa6$\x93\xaa\xea[\x8fa-\x9d\xf1\x10\x8b\x86`\x14\xdf$\xbc\x8a^d\x13\x0e\xe7T\x05\x1e\x9d\x1a\"4\x03o\xd2\x90$\x1f\xb8~m\xa4\xa7\xb1\xce).\xa7\xd7\xc8p9\xeb9\x0f\xb6\x14\xae\xaf\xf7S\x80\xe8!a\xe8\x1f\x90\x98F\xcc\xcbP =\x9b\xeb\xebn--\xa3\x10\x81(r\xf8\x08\x01;\xa6\xa4E.\x88\xf4iy\xcc0\xdf\xc6\x062\x18\x99\x1d\xf7Q\x85Z\xa6\x198\x98KM)\xeb]\xeb\x8f|\xe8\xa1-Ub\x87\xde\xf9\xd0\x8b%\xf3g\xbdg\xf7\xae\x00]\x0f\xc5\xc9\nP\xbc:luw\xbd>v`\x90\xe6i\x93\x08jw a;\x90\xd9\x89i\x07$\x14\x84?o\xa4\"dB\xaf\xf6\xd4\x91\xc7\xb4\x1b\xb6]\x05\x8a\xed\xb9\xaasmo\x0f\x98\x84\x07\xc2\xb8f\x0dk\xa7\x8f\x18\xd6\xc1\x9a@\x18\xcf\x92,\xa3\xb7u\x18\x9f'34K\xd2\xb9\x9a\xdd\xdc\xbe\xb8\xa3\x02\x14z~\xb5;\xf7\xf6}\x95\x9f\xbc\xc2\x86\xbb\xe4f\x01m\xcdc\xce\x9bi\xdb\x02F,\xb0W\xe3\xdd\xac\xe5C\xc2u\x1c\xa6\xdd\x98\xbb\x90\xaa\x08\xa8\xc0\x85\x85\x0b\xe7\xae\xb0\x07Ia\xbf_2\xd4Y\\\xf1\\\xa30Ze\xff|\xc5|Fq E-p\xeb\xd4;E\x13\x96\x0e\xdc(I\xe6\xb3\x9b\xfa!\xa20\xd5>sT\xf3C\x9dJ\x802|a\x9d\xe0<\x82\x00\x1e\xc3\xe9#8\xd5Y\x9a\xa2\x95\xe9\x92\x07\x8c\xbd\xb2}\x9b2#dzz\xecL7\x8f]XLG\x18+\xf0\xca\xc6wN\xed\xa7\xba\xc4\x9f\xb3\xca\x0cu\xd9<\x8ej\x13X\xa6\xf7\xc1da\xdcq\xea\x11\xaca\x97\xe7^L.\x0b\xdbq\xbc \x89\x89\xc6\x1a\xb7\x1alb\x9f\xbbp\xe5\xc2\x82\x07\x82\x82b\xd8\xd0\xae\x1d\xef\xeb\xb7\x07O\xfeL\xc9ezq\xbd=8z\xf7\xf6\x15\xec\xc1l\xb5C\xb6\xd3o%-\xe07\xe90\x90JFW\xe0:\xd8\x87\xc2\xa6\xf7\x14.\x7f\xcc\x97\xbfh_\\\x15\xafk\x8c,I<\xd6\xacB\xe6\x87\xe0'\xe1\xaf\x90\xa1\xd8\xb0rhs\xdb\xfa\xc6?4\x7f\x0d^\xab\xae!QR\x1b\x99Hf\xa0M@7Y\x98\x0c3\x1f\xe1+*\xcd\x11\xaf\x11;cv3L\x8c\x87\x86W\xd3\xe4\x98\x0b\xf5n&:\x8d\x1c/a\x98\xc3NuY\xa1f\x0b?\xf3g\x05\xc9\x9e\xf9\x85?Q\xba\x94q\xfb\x9c\xde\x85H\xbd\xc0/\xd0j\x8aNe\xde\x03\xdfJ$\\\xf5\xa1\x9a\x85'\xde\xdc.\xd0TOA\xf0a\x82\xb4\x12\xb9\xe0\xaeK\n\xac\x1aX\xa5\x90\xe3M\x88\xa7u\x14nLo\x18\x89\xfc\xa4%U\xed\xde\x7f\x82Y\x9b\xde?\x9ef\xc7m,\x1br\x16\xae\xef\xec'M3y`\x13`,\xd4\xac\xd3q H\x04\xe3\xaaB:\x1d\x1c\xc5\xd3\x12t\xfc\x01\xb8\xf3C#t\\fg\xde\x1bX\x87\xcc{kP1\xcd\xc3\xd8\x8f\xa2\xab\xa1\xd2w\x9f+\x8d\x93*j0\xe5\x88\xc5\x1f\x1a\xd1{\xacSr\xab\x92\xd9\xb4\xd5\xc7\xb1,\xa7\xd4\x1ab\xf3\xcfJ\xcchj;m\xbd\x8a\x89\xcc\xeal\xb4\xfc\xa8\x8c\xcb(\xebF\xa9\x8b\x8f<.\x86`V\x1b\x96^u\xf9\x11\x81\xb7\xebP\"\x02\xf7l\xb7\xc0\xf1\xd0\x00\x88E6\x18\x08\xf1\"\\\x84\xb9\x01\xdcB\xa5}\xad\xd0J\xc7\x1eACwn\x0b0\xa9\x953\x8e\x1d\xa3\xd2\xa4_M=dAc{\xfb\xc1}\xae\xa5\x7f\xc0\xff}\xd8\x8cj\xc7\xc3co?\xe4Q\xed\x1e\x8a\xf7;\xfc_\xfe\xfdC\xfe\xfdC\xf6\xfd\x0e%G\xf0\xdf\x11\xffw\xcc\xff\xdd\xe2\xffn\xf3\x7fw\xf8\xbf\xbb\xfc\xdf\xfb\xfc\xdf\x07\xfc_\xde\xde\x88\xb77\xe2\xed\x8dx{#\xde\xdeh[\x19e\x8f9\xdb\x0eY\x8b^0\x1aw\xc2x\x87U\x90J\xbc\x92\x9f\xf2\x10\x8f]\x94(WJ\x02\x82\xfe\xc1-\xc8CD\x88\xe6\x04k\xcc\xd0}\x84\xf1V\xaa\xa0\x19Ul\x91\x0e\x82\x94\x1b\xed\x83\xd0:o\x9f+\xb4\xdc8\xe9n\n?_$\xed{\x0c\xbeVL\xc0\xa2\xc2\xed\xc1z\x9d\xc8\xcf\xc78; \xc5'\xa3\xd1h{4\x1a9\"v>C\x18o\xfd\xf8\x8c\xebH\nYG\xe2\x03\xa6\xb3\x84Y\x12\x10H\xe9dtv\x96\\i]\xc0W,\xba%\xecc4 \x0cy\xca\xa2_\xae\x83m\x17\xb0\xb1\xc7\xca\x1dx\xfc\x18\x10~\n\xf8\x0f0\xda\x1co\xc3:\x8b\x99\xd9\x9b1\x17$\xfc\xcb\xb3\x0c[\xb7\xc3a\xbd`\xa6\x8b\x1b4\xda\xdcR`+\x0dPd\xfe\xc5pP`\xb15\xbc\xcc\xbf\xe0LiX\xcbnM\xe0A\x81\xa7d`\x12\xc3c(\x1f9\xc0-\xb9x\xe4\xd6bZ\xae\xaf\x1f;\x18F\xe2+&kiV\xa8\xc1\xa6<6X\xab\xf9w\xb3\xf4\xea\xeb\x83\xe2\xacM\xc7\xb6\x8a,\\Z&\x85y\x9b\x9bV-\xaa`\x059\x15\xb2u\xbb\x01\xf7\xc2\xca\x8e&\xd6\xdf\xa6:\xbc\xd4\xf6\xc3\xf6{\xba}\xd6\xd4\x82u\xf0YD\xce\xaeXS$\xdb\xfa\xff\xd3Z%\xff\xcf\xfac\x9b/\x8a\xea\xaau\xa5/\xda\xb5f\x03\xb8o\x90\x85\x12\x8aT\xb2\xc0\xc7\x1d\x0e#S\x04k\xb2\xe6O\xc9\xb1\xcd\xbc\xf3~\xfb\xf5\xff\xf8\xb7\xff\xc2\xe2\x9d\xf2\x9fX\xa6l\xe3Zs\x8b\xd3\xb5I\x98;s\x89J\xbe9\x86\xe3\xed0\xca\x807\xfe\x97_\x82\x9dLcZ;GWnA\xfbR\x94_\xca\x07\xb9e\xf9\xd2Z\x809\xec\xc1\xcc\xa3\xb0\xda\xc7\xa0\x81\x04\x8er0eT\x05\x8e\x803\xef6\xe1jE\x96]-w\xc1\xc2\xbc\xeccM\x85HTh\x11\x1ej\xc1\x82Z\x0b+\x8fT\xaem\xfdX\xfc\x18\xffx\xfe\xe3\xfc\xc7\x0c\xfe\xed_\xff\xeb\xff\xf5\xeb\x7f\xfd\xd7\xff\xf3\xb7_\x7f\xfd\xed\xd7\xff\xfc\xdb\xaf\xff\xc3o\xbf\xfe\x8f\xbf\xfd\xfa?\xfd\xf6\xeb\x7f\xf9\xed\xd7\xff\xf9\xb7_\xff\x97\xdf~\xfd_\x7f\xfb\xf5\x7f\xfb\xed\xd7\xff\xfd\xb7_\xff\x9f\xdf\xfe\xf3\xff\xfd\xff\xfe\xfa\xeb\x8f\xe5xs\xfc\x00\xff\xff\xf0\xc7rN\xe6sk\xc8\x19\xbb!M9\xde\xde\xc1(n-vF\x8f\x91g\xe2\x8a~\xd2{I\x0b\xd5q\xafm\xf3 $r\xc3 \xea\x02\x8a\x8d:\xe1%(n\xb1,\x8f\xc4\x01\xe6_Q1x\x14\xc8\xe9\xa7[\x8em\x89z\x96\x81\xa6\x11u\xfaVJ\\_\xa1X*\x17\xe4\xf6\x95\xe76V\xdcg\xf0\x18F\xb0/\xa5#\x1e\x1d\xd7\x06\xcc\xcaV2\x96\xf1\xc7\x1c\xd3\xacl\xe9Iy\xee\x1b\x11\xf9\xddN\xd0\xe493 \x18~j\x0d\xbc\x82O\xc7\xcdM\xe1\xd1\x0f\xb3DM \xf7\xdc)a\x03\xeaK\xbbd6\x15\xf9\xef\x02O\xf7\xc7J\xde_\x06\x8d0\x9eEe\xc0\x82]\xe8@C\xd4\xe9\x03\x8d\n\xed\xff\xa7D\x02\x8e\xba\x07\x0fS;\xbd\xc6\x08\x91\xab\x80\xc3\xed\x0ecc\x99\x06\xe3\x8e\x8c\xa4\xc4/&x\x83\xef:+v\xd9\xb7_\xa3\x91\x96\xb6\xb8\xa9\xb4\xb8\x0e\xdcO\x99`\x05x\xa3\xc0E\x91\x89>\xe4\xf1P[\"S\xf48\xe5a\xfaC\xd8\xdb\x83\x11\xdc\x83M\x05Ca=M\xca\xb8\xa8\x1d\xb7br\xe6\x17\xe19is\x12\x0f/\xc9\xdd\x0f\xbd(>\xc9\xd8\x93\xb8\x98%\xd1\xc78\xb2\xb4i:|\xd1\xfc\xc7<\xb6\xb4\xaf<\xfc\x99|\xbcY\xf0\xd6?\xe6$\xc2\xc2\x8f\xc2Y\xbe\xd2\x1c\x86L!\xfc\x14\x80\xb42\xf2\x19\xb4\xfa\x88\xf6\x17\x19\x99\x7f\xe4\xa5\xcf\x97~\x14\xad4\xfc!\xa3\x17\xad~\xf4\xc5\xa7\xef\xdf\xaf\x06\xfc\x83\xc6/\x9a\xfd\xf8\x13(O\xef~\xf4\xe5'\xc1\xfey\x99~\x84\xa1\xa7w4\xf4\xd8\x1e\x8d)\xb9\xbc\xf4\x8b\xd9\xc2rad\xae.\x0dfZ\xd5S\x8a?\xd5k\"\x1e\xc1\x19\x10\x93\x921\x91e\x0f(z\xa8\xd2\x99\xc5\xd3B\x9f\x19C2\xafO`_\xd8\xe11/\xaa \x9a\xc0q)o\xecL\x8bc!\xc8\xcf:qA >\xbe\xe1jrQ\xa3\xe5\xc2\xf8\x06\xeb\x99)<4`\xd0\x92\x86}K\xea7\x964\x93\x974\x1b\xb8\xa4\x12?\x91a\\\xb3\x04W\x95\xbd\xe1k\x19:,N\xd3\xdd\xadhN\xfc\xec\xdf\x01\xf4\xee\x963\x8d\xc2B \x9e\x1d\x03K\xfd: \x0dGl\x8fw\xda\xbe& D!\xdd\xd7L\xef\x86J\xb4\xae\x90\xc4\x9a\xa1\xf1\x8a\xe5\x9f\x9e\xce,\x9ew\xe2\x9e}\xea\xfc\xf1\x9eC\x99\xe3\x0f\x1f`\x1bu\x1e\x05\xc9\x8b\xba|\x7f\xe2\xdcsac$\xc2:\xd1zc\xac\xe7\x9f\xca\xb5|lH\xaa\xc4\x1a\xf3\xea:\xde\xbeC\xffkT\x92\xcb\x1d[*\xa3\xdc;-\xaf\x8a\xbd\xfd\xaaP\x05r\xe7\xdc\xf7Y\x12\xa8\xde\xb3\x9d\xfd\xfd{\x1e\xb9$3\xdb\xb2\xe8\x1c\x15P3DO\x02\x92\xad\x9a\xd0]\xaa\xe3\x06@\xd3'gOx!\xf14<\x95%\\;\x95\x8a\xfc\xedZ\"\xa7_\xab\x83\xe8\xe1\xe8\xd4\x9f\x9d3K\xff\xdc\x85\x08\xc3T\xcfY8}\x93\x93z\xc0B}\x86gq\x92\x91\xa7>\xc6\xf6\xb3B\x0b&\xf4\xda\x83uZ\xb6,\xa3\"\x8c\xc2\x18\x8b\x96\x8d\xa22\x0eQ\x11\xbf\x0fV\xd9(\xc8\x8bp\xf6\xfe\x8a\xbe\xbf\xe2\xef\xf5CX\x98}\xe4\xcf\x9b\xbbY\xc0>l\x8f\x1fn?\xdc\xbd?~\xb8\x83\xe6\xfe\x8f\x1f?65\x80\xd1g\xeb\x03O\xbc\x1c\x83\xa3\xbb\x10\xc0:Xg:\xfb\x01\x94\xfea\xd0\x06t\x8e\x90Z`J\xce%o\x876\xf2\x85\xbd\xbf\xf6\xe3\x8f\xb9c\xb9\x10\xa84\xd4\xd5\x83\xfe\xeeK\x06\x8b<\xbe\xe7\x9amG\x18y\x0cE\xcd\xb0\x0e\xf9t\xf3\xb8\x82\xf0\xc7\x80\xf1\xd5\xec\x94\x07?\xe12\xa5\x85+>p\x1c\x17\xd6\xd0\xb6\xbf!\xf1\xc2\xa4!\x9b\xc7\x95F.s\xcd\xe4O\xe3\xc1\xa9\xcf1.\x01\xcc\xe1\xab\xae\xe4{\x03\xc6\x8f`\xbe\xbe\xee\xc8;S\x8b\xd8\xe6h\xe8k\xe3\x8f=\xa5D\xbc\xf1\\;nw\xf0|9\xbe\xaaC0\xa2]\x00s\x14J\xe9\x07l%F\x0e\xcf.!-\x1b\x8b1\x1f\xb9\x90V\xad\xee\xc1\xb9\xe3|\x00\xbec,\xa3O{\xfb\xe8\xa0\xeb\xc1\xc19\xecC\xca\xcb6]8\xc7O:#hY.3\x8f\x06kS\xa0F!\xd3\xdct\xa4\x15\xb3\x07a\xb6\xe6\xa5\xd9FW\xb0\x0f\xd3c\x98\x08\x1cT g\xdb\xdc\xa0Z\xcc-\xd1\x08\x1a\xa2\xeb\x06d\xd5\x8d\x08\x01\x89\xac\x8ak\xb2*\xeb\x90U\xb1\x8a\xac\xcaV\xa5\x03\xcc\xf2\xfa\xd4\x8e\xed\xedQ[\xec\x9c\x88\x92q\xbb$\x14%;\xed\x12\x9f\x97\x8c\xee?h\x17\x95\xbchgk\xb3]\x94\xf3\xa2\xadNO\x11/\xb9?\xden\x17\xcdz\x03\xf7U)\x98\x88wrB\xf2\x97IPFD\x97C\x14$\x99\xff/\nW\x10\x8c\xbb\xc7r\xe2\xe9B\x99\xd5\xf9\xdex\x0c\x86v\x8a!o\xe1\xe7\xaf/b\x91\xbe\xb5\nC\x17s\x95\x0d3\xb6 \xdd\x84oP\x83\x10&\xa6\xf3\xcb\xa8\xe0\xa1\x99\x9a\xa0A7e\xbb\xb3Ts\xae|q\x1e\xfd\xa1z/\x96\x0eR-\x8b\xdaY;\xcc\xf4<\x18Y\xa3.E\x92\xd6Y0\xde\xdd\xd9\xdd\x1c\x05-E\x1b\xbdv\xad-o\xf4\xc0\x1b\xb7J\xe8}j\x9d\xfa\xf1OI\xab\xe0\x8c\x16\x1c\xfa\x85\x0b\xe3\x1dxR\x9e\xc1xs\xf4\x006\xefOv\xc6\x93\xf1.\xfc\xe9\xe5\x91t\x10\x86\xe9\ns\xb1\xf4\xde9\xc9\xf20\x89s\xbc*;/?|\x80_\xae]E\x89\x97_\xf8gg${\x17*\x9d\x97x\xb5 (\x02\xdd\x9e\x85\xc5[r\x1e\xb2\xf2\x85\xb2\xfcY\x98\x15W\x13\x08\xba\x85\xa7e\x18\x05G\xe1\x92\xe4\x85\xbfL'p\xd6\xad\xb2\xf4g\x8b0&\x93v\x0c\x85.\x07Ph\x1d\xaf\x82dy\x12\x06,\xcf\x94\x1ao\x06\xc9\xf2U\x12\x10S\x95<%\xb3\x89\xde\x88*\x8b&J5,/\xccMMG\xfeUR\x16\x13\xb0\xbe\xf6s\xf2\x02\xff\xd0\xb4\x14$\xb3\x83\xcb\xd4\x8f\xd9r[Q\x98\xebj.\xfd\xcbg,\xf5( \x8e\xfc3c\xff\xf30*Hf\xaa\x81\xe6\xa4~\x91d\xefp\x9e\x8b\xa2H\xf3\xc9\xbd{IL)^\x01=^\x98\xdc\xab*j\x86\xc5|\x97r\xfdB\xce\xca\xbcH\x96\xfar\x9eO\xf5uJX\xea\xaa\xe7A7\xa9N\xab.\xcfz\xf4\xac\xd4%\xbb\xaa\xea\x13\x92\xbe\x08\xe3\xf7a|\xa6\xaf\x94\xb1\xd6\x9e\xc7\x05\xc9f$-\x92\xacOc[\x7f\xc9\xb0\x97\xb2\x82f\xba\x19\xc9\xd3$\xce\xc9'\xea._$\x17\xe8\xd3M\x02\xbejj\x073\xa8q\xeb\xcb$ \xd1[\x12\x07$\xc3u\xb3\xc8\xa5\xbfL#\xa2\x83`\xe9+\x04\xe5\xe0\x19I\x8b\xc5\x04\xb4{R\xd7\xcf\x87|@\xa7ppY\x10<#\xb9~\x1fi\xbd\xa7\xc9r\x99\xc4\x83j\x97)\xc5\xc3$8,O\x97a\xc1\xa2M\xe4\x13\x98Zg\x04\xd5.i\xc9\xfeIr\xfc\x97e\xd1\xa5\xbf\x92\x94nU\x8e\xfa\x01\xe2\x07X\x89\xcb8\xad\"\xf3g\xc4\xd20\x9eiFrR\xd0>\"\x81\xb0u51C\x17\xad\xa9\xa9\x10\xc6a\x11\xfa\xd1!\xddX\xfd\xd1\x9a\xc7\x86c\x99,\xd3$\xa6|\xcb\xa4\xed<\x05jp\xa2\xfc?%\xd3\xe7^\xeag99D\xb9Y'M p\x82\x89x\x1c\x057\xf1:OF\xac)\xa5X?\xe5\xdd\xf8b\x8d\x1c\x9b\xdeq\x05\xd2\xde\xb1\xa2\xb7+\xed5\x91_\xe5\x05Y\xaa\xc8\x08\xf1T\xd8+\xf5\xf8\xcfU\x0eW\xb5M\xa9\xc7\xf7V\x03kl\x9b\xda\xb3\xd2\x8eJ\\\x1ff~U\xd4J=\xf6K\xdd\xb7x\xc4\x95\x90z\xec\x97\xb6\xb2f\xaeP\xdf\x98\xc6~X\x1d\xdd\xc5)\x1e\xbc]S\xaf\xcc\"\xfd84;\x01\xa9'C\x7f\x97@V\xc4&\xe8\xfb\xa4\xa2\xa7O)=\xdd\xaa\xdd\xfa\xbbEZ\xdb\xa7HRK\xfdS\x15\x9a\x078`\xb2\xdc#\xa5\xc0\x86\xb0\x073\xc7\x85\x13/'\x05\x1bCn\x97\x8e\x0b\x17\x02;=\xc1\x99\xe7^\x94\xf8\x01 0\x8fI\x9d=\x9d6\xb5\x16\xd3CE\x7fZ \xf2\x84\x16KQ\xb0\xe9BX\x8f\xb2\xc4y3^p\xd3\x85\xa4S\"%|ck$:.\xd3\xc0/\xc8\xbb,\xb2-\x0b\x07\xd6-|\x91\xf8A\x18\x9fQ\xe8/s\xdb\xca\xcb\x19\x06~\xd1\xd4>L\xc9\xcc\xa6\x83\xc8:\x83\xc0d)\xcdo\x82\xe4\"\xa6s\x07\x0c\xea\xc1g\xaa\x1d\"\xd6\xe8\xf4+\xda\xe0\xc5\xe8\x81#6\xc0\x81\x0b/C\xd2\xa7\xde\x14\x17\xac'i\xaa\x93\x97V\x91J\xb0\xfeI\xa8\x0d\xcd\x0f\x1c0s9\xb2\xc6\xdfK\x92] \xf8\xab\x9b\xd0\x8bR\xab\xe1\xe5bXj4\xc9\xa3\x89P\xe0\xc0T8\xbceL\x06\xd0x\x89`\xf7\xe1\x03\xf04\x1e\"k\xc7\xe1\xfb0MI\x00YM\x07\xc6 \xfc\x0bk\xe5_ \xc9\xf07\xfd\xf8_\xe0\xc2\xcf\x11\xed\x87\xf3\x90\x04\xbau\xe2x\xe8\xa2\x8b\x18\xba\xe7\xeb\x92bB\x0e\xf2L\xa6\xc8~\xbf\xcb\"\xa5\xac\x0d\xe5\x98\x8dM\xee\xbc\xa0G\x9b\x9d\xa8\xaf\xaf\xdeq\xb0Y3\xd6\xf8\xf0\xc1\xd8\x82\xe2\xfa\xc6K\xed\xb2;\x1d\nlo\xc92)\x08\xfb^M\x81\xab\xd8\x90\xd4\xeb\xbeU}\xa9`)\xe8\xa7\x9d\xd7M\x1c\xec\xc2\x01fb\xb0\x8d\xf3\xbc\xa4\xd5\\\xb8\xa0\x87\xf1@r\x03\xba\x96\x91,\xe9\xa5E\x1c2\xe1\xd8\xde\x19=\xe88\xf0\x8ev\x1c\x8f\x8b\xfd\xde\x93\xab|HC\xf5\xcau\xac\xa0\x99\xb6\xf5\xe1\xae4\xe1\xd8\x1e\xef\xdcwx\xbaM\x03\x95\xd1631\xbb\xed4\xb3s\x03\xacnX\"/C\xb3\xa3J8\x18\xdb;\x9d\xc0\xb0\xb5pq\xd2\x9fb\xb3\xb3\x03\xdc\x83\x1b\x1d\xbe[\xfbp\x7f\xdb\xf1\xe6rL\x94!-\x0e\x9cD{\x9bn7\x89\x9d1\xf3\x07\x1f\xdd\xe7~\xe4c\xeeW>\xbe\xaf\x04\xaf\xc3\xab\xe5i\x12\x0di\xbb\xd7J_\x9d\x8e\xb7\x13\n\x83G\xe9m\xe7\xb2\xe4\x913\xda[\xca\x83\xf4\xee\xb4\x83\xf1\xf2\x19\x8c\xb7\x1d\xef\xcf\x07\x7fk\x96\xb1\xd4\xa1;\xed\xf1\x88\xcc\xa1\xed\x011\x81\xf6\xc3vX\xa1\x94{\x87\xb4\x8d\x13x\xea\xd0\xb6O\xc2\xa2\x82\x94\xe6\xfbs\xfe^\x9d9tg\xdc\xae/2\x87\xb6'\xcc\xb2\x86n\xb5G\xc3R\x86\x8e\xdb\xb5Y\xc6\xd0N\xdc\x87\x0b\xbe\x9a\xed\xb9\x1e\xb0%h\x8f\xf1\x92Wo\xcf\xf5\x90\x8f\xbd]\xff)\x1bL'X\xca{\xb6\xe5\xed\xd7O\x04Bj\xbe~\x0d{\xf0\xb4\x9d$\xf4\x0d\xec\xc1\xfb\xf6\xcb#\xcc\xfb\xd9z\xf9\x12/\x08\x06\xd7\xcd\x92\xe7\xd5\xd5\xd1|\xff\x13\xec\xc1sJ.<\xafQz\xb3\x06\xbd`\x02\xdb:Y\x84A@\xe2\xb6\xca\xff-+-\x927Y\xb8\x0c\x99\xbfM\xb3\xc63\xd4\x03y)g(\x9f\xe7\x07q\xb9d!\x91\x9b\x15_\xd0\x1b\xd2\xb6r\x1c\xfd\x06c\x05\xb3\xabvs\xef\xe4Z\x9dd\xc6\x7fg\xa5I\xba\xa1\xa9\xf0\x0d\xecu\xb4I\xcd\x1a?\xeb\x02\xc2\xbcl\xd6\xfb\x1aW\xf4/\xac\xb1f\xd1\xf7\xb0\x07k_cf\x88\xaf\xa5\x8c/\xad\xbf\xbdy\x18\x07O\x17a\xd4R4|\x0b<\x82odvr\xe6w\xce}X\xdb\x83K\xfb\x0d\xf2fh\xd7\xab&\xd0\x87\xc5\xd8\x82\xba\xe17\xb2\xad\xb0Y*\xc2\x93,\xdf\xd7V\xbav\xbcn\xd0#P\x8aA\xae\x9dv\xddkG\x0eg\xa3\xb1]\x03 !\xbf\xb6\xbfQ\x9b\xd3d\x92\xac\xe2\x9biq\xec\xc2\x9b\xaa=\x1e\x10\x92 \xb7\xf9\x0d\xfd\xf9\x06\x9b\xe9\x04\xc0\xbf\x86 \xbcin\xd9\x0f\xbd|\xbb\xe0\xd9\xdf1\xaf\xf1K\xfbe\x0d\x08&\x1d%fL\xef\xaa'\x9b\xdd\x7f\x07{\xf032\xc5\x0c\xea\x1bP\xeb\x89\x9b\xbb\xb1\x88\x06\x80R4B:\x0b0\xa8\xa5F\x94\xfd\x97\xa6\x19\xfcm`l\x80\xaa\xe1=\xb1I\x7f\xb3\xff^m\xe0\x15\xcb\xe2\x02{p\xc13\xd6\xd1w\xb4$\xb1\xdf\xa1\x91\xc4>\xc6\xd7\xa9\x10\x10f\\\xa5\xfd\xbdby\x85\xa7\xaf\x8e\xa7\x053s\x11\xbf\xf7x\x0e\"\xdc\xb4Xw\x10\xea&)\x17\xb1\x89\x89\x8bT\x90\x0d\x93\xba\xc3\x0f\x1f\x18\xf4\xbdr\xe1\xc0\x1ea6uJ\xa6\xd4\xfd\xd2\xe1\x7f[\xad\x06\xfd\xb6\x86V\xd3b\xfey\x88q\xc8\x95\xd2\xf5\xad\xd6\xbc\xb3\xe0\x1fK\x9e\xe8\xb3\xa0CKXj+\x16e\x97IP\x98\x1fe\xf2\xc8\x81\xbf\xa1\xfe\x1d\xc3\x05&\x18\x06\xa60j\xdf\x8d)7\xfe4\xf88=k\x18\xaf\xe0\xc6\x13\x96\xaaP\xdb\xf3\x1a\xd6\xae\x01\x08A\x83\xe5\xf7\\K(0\x11f\xc1e\xaf\xd9\x05\xa2\xec\xda\x17\x9f\xff\xf9N\xfc\x16%\x0cz\xe8o\xbay\xe4\x18\x0b\xdbv4\xcd)~1d\x8f\x98\xdd\x05]\xff.\\\x0b)\x11\x89\xa9\x9e\x94\xff\xc8\x11{\x82\x87\xcd\x17\xb3\x8a9\x04\x7f#v+dSz7-\x0c\xe70l\xce\xaa\xae\xf73nmi\xdb/M\x81\x0d1\x08\x14=N2\xa2\xef&\xc4\xb0\x18IZ\x87{\x92\x92\xd0w\xf2b\x9c\xf3\x8cj\xa9\xca\xebw\xb3\xe1\xf5\xbb)\xf9\xe6\xbb\x9d)6\"B*\xaf\x13\xe0Y\xdajl\xc0SZ\xfe\x9d](\xcd\x03\xce\xfe\x9a\xbe:\x16\xf8\xc2\xae\x8f\xbc\xb8'\xbe\xad\x0d\xe9\x10\xa9\xab\xd2\x1d]+\xa5|H\xf2}O\xff\xf7-\xdd\xc3N.@\x18\x14I5\xa7T^\x8bXp\\\xf8\xa1\x99\xeeM\xce8h\x15I\xe5\xe3\xdd'\x04)0C\xdf\xfb?\xc8M?\xc5\xa4t_\xb8\x94E\x81=\xf8\x1bF\x90\xdby\xe8\xe0_\x87\xf8\xff\x7fF\xae|\xbc\xc3\xde\xfd\x89\xf1\xe8\xbb\xec\xaf\xbf\xf2\xfc\xc6k\x94\xdf\xdc\xc6e-\xe9\xfc-\x15\xc3`\xb9\xf4kD0\x0b\xfc\xbaWR\xf5\x83\x1d4$2t\xc4\xbe\xedc\xaa;\x1fS\xdd\xf9,[\xda\xcf\xed\xf5f ;\x91\xe8\x16Y\\V\x1d\xe7\xbfPva\xe1\xe7\xcf\xf9\x01p\xc3\xfci\x12\xcf\xfc\xe20\xcd\x88\x1f \x9b#(0\x17\x9d\x85\\n\xbd\xeb2\xd7\x0c\x97\x07\xe8u\xd1\xde\xd3\x958)W\xec\xcc\x91\x7f\xe6\x96q>KR\xda\\.LC-\xd7\xa2\x17\x01a8\xe2/\xf5!!\xe4\x91\x03\x81\xfd\x97)!\xcd\xb4\xe65\x12\"\x98\x8f*\xf0\xf2\"\xc9\xe8\xe5\x12\xf3V\nR7\x13\xd3f\xce\xed\x82L\xe3V;t\x05\x0f\x1bk\xc7Ox7B]\xbf\xfdG%;{Ao\xb5\xf5=\xb47\xdf\x87\x17\xf4TM\xd8?{\xdd\xe4\xea-\x04\xfc\x9e\\}\xd3\xdf\x15Z\xe0\x7f\x87\x16\xf8\xc6\x9c=>0\x1a\xb8\x83\x9b\xa0\x19<-\x8c\xe1\x85ZCA{z\x81t\xdc\x9e\x9c\xba\xc3H\xc6\x9799$\x05\xaa\xb1\x8d|\xda\xf7\xaa\xf0\xc0\x9d\x96\xc2e\x1a\x91!-5\x93\xcd^w\x8eJk\xa3\x19\xc3\xdb\x8dq\x84A\xd4\x07$+\xedZ%\x17\xb0\x0f\x976\xa6\xa5\xfc\xb3}\xc9h\x1d\xe3f\x07d\x1e\xc6D\xa8\xa8'\xf07CqH\xf2 \xfc\xb9Y\xe1\x8c\x14\x92\x8a\xfb\x19\xc9gY\xc8\xd4\n_\x98*\xbe\xf2\x97\xb4\xb1\x7f6\xd5a\xc7 \x9f\xc0_\x1b\xeb\x88\"\x96\xe6b\xdakx\xc5\x1a\x98|q\x11\xbel\xc7<\x16\x8c\xda4.\xa3\xe8\x18c\x99\xfdd\x0b\xba\xd3\xfa\xe5\x9a\xbf\xe9\xae\xbd\xdf1,m}\xc26\xb7\x851\x1d\x17\xac\xef\x0e_\xbfR\x04\x01\xa9\xb4\x0c+\x10?\x9cd#\xc7\x8c\xa3\x18=R\xc5\xe0\xa1,\x05\xa7\xc9\xea\xeb>ib!\xf1\xf0L\xde\x9c \x1a\x1d\xbb`\x9f\xda\x9d\xa4n\x9c\xc4\xffN\xf6\xbf9\xe3\xd5\xecb\x089.\xfaRJ\x87X\x987\xa44;\x06\xf5\x8eK\xfb-\x1c\x0d\x1a\x00\x0e$t\xect\x1a.\xfc\xc4\xb5*\xcf\xbb\xc2\x87\x06XIB\x84\xe9[$\xc6c{g\xd3\x91\x85\x0b.\xbcm\xd4cI\xb6^\xcf1_\xe8\xcb\x1aq\xb3\xbf\xfdb\xe1\x82E\xff\xb1\xf8=;\xe7j\xa6\x1a\x06\xd66\x07\xa9\x00j\xe9xG\xca)\xa2B\xa9\x93\xd8QBaU\xbd\x94\xe0\x073e\xda\xb7\x98\xc5\xe5\xed\x1a\xce(2HV\xa0\xea\xbb\\\x00O\xf1\x11\xed=\xf4\xe6,/\xcb\xe6#(kH\x8d\x1e9\x90W\x16\xe8\x94`/\xa7\x11\x12\xe5HN2\x10V\x1f`Ia\xb8\xda\x8av\x84\xdb\xc2\x9b\x90\x92]\xdd5\xfd\xe5\xda\x13\xa4D\xb3\x10\x83\x03\xd5\x86\x14\x02\x96/\xc28H.P\xc9\\\xfd\xe2BS\x05F\x84}C\xa1\xcdZ\xa0\xb8]v\x8b\xab\xb5\xa3\x83\xa88\x0c\x8akM\xd9H\xe1\x07l\xf2\x18G\\\xe58\xeb\x95n\xe9\x93\xd5T\x04\x88\xca\xda\xaa7\xf9\xbb\x18\"w\xf4Q4\xd1<\xc06\xcf\xbf\xdc\xd4\x14\x0e\x02\x00\xa6K\xb1-?\xbf\x8ag\xcfWR\xc8\x89OY\xfa\x12\xa4\xa5\x07}\xa7\xd6|\x15\xde\xe9UA^\xb0#0\xe4\\F\xdas\x89\xe9\xa5:%\x19\x96\xb4}:\xf9Ro\xd1\xdb\x13\x83/9p\x0f\xb6aC\xe2\xcd\xaf](\xbc\"\xf9\xfa\xaa <3\x9catm\x9e\xfd\xa4\xb0\xe7\xce1|\xf5\x15\x8c\x1e\xc0\x87N\x11\xac\xc3\x88\x17\x8f\xd5\xc5cV\xbc\xab.\xddr\xe8JL\xf3\xf5u\xbc\xa60\xb2\xf2.| \xe3\x9d\x9d\xf6\xfb\x07\x9d\xd7\xe3\x9d\x1d\xf8\x12Z\x89\xa4\xc6<\xc5\xb5\xb8:\xd5\x93\xd1\x0c\x96\xce\xe5\xf1c\xd8\xeev\xd2\xc2\xb6\xa3A\xbd\x8c6\x8dK\xb6\xad_\xb1\xc7\x8fa\xa6\x87wZ\xb0u\xfd\x12v\xb7\xe8\x0bko\xcfB)\xf7\x98\xb7\"\xf6\xcbf\xed\x8cq\x1f\x1e8\xb0\xaemx\xb4)Z\xa6\x80Q\xb5\xcc\xbb\x1aK]Y\xed\xa1\x0b)L7\xdc\xf4\xb5\x82\x7f\x16B\xc7D\x12>Ze\xcc8\x8f@N\x0f\xfb.\x8c\x8b\x07l\x1f\xf7\xe5?&,\x9f\x0b\xdb\x14\xeb\xc9\xd7O\x9f\x1d|\xf3\xa7o\x9f\x7f\xf7\xe7\x17/_\xbd~\xf3\x97\xb7\x87G\xef\xbe\xff\xe1\xaf\x7f\xfbg\xfft\x16\x90\xf9\xd9\"\xfc\xe9}\xb4\x8c\x93\xf4\xefY^\x94\xe7\x17\x97W?o\x8e\xc6[\xdb;\xbb\xf7\x1f<\\\xbfg\xf1h\xdc\x0c\x8f\xf8\x95t\xbe\x84\xaf \x7f\x04\xeb\xeb\xa5\x03\x19K\xc6\xedOK:\xf0\xa9/\x83r\xe9`,c\x95[[\xa4\xc7\xea\x02\xd8\xba\x84U\x01\xff\x01\xb6)\x1a\x13\x8c6E\x9e\\\x16\xf8\xc1vn\xc2\x84!f:^9mfw\x1df:\x8c_g\x8cB\xf7S9:z\xc1v \xa6\xff\xac\xef\xc1\x96\x83\x00c\x13\xba\x13\x14\xe5P\xec9\xda\xbd?\x1a\xed>\xd8d>\xf6\xd3\x92\x9e-\x06\xe9\x14\\w\xc6\xbc\x84\xa1\x0fV>>\xa6\xac\xb9\x80|;\xc4\x8cZ\x08\xff\x0f$\x98\x0f\xf1\xcd\xb8\xfdfWz\xb1\xbb\x05_B\xd8\xe6\xa9*\x8a\xa6{\x14\xaa_\xc9\xd4\xda\xb0d\x08\xdaD\x08\xda\x1dS\xd0\xb2NTE[JzC^\xcd\xc2\xcb\x88\x1f(T\x81<(\x8a\x02\x0cCW\x10\xea\x0f\xe0\x8f\x90PZ\x80b\x06\x85`\x94.\xfc\x88\xaek\xe9\xa8k\xa0\xbf>\xaeY\xb7\x8c^\xcb\x1b\xf7\xbb\xef\xd1~\x06\xf6\xb1\xe3\x11LT\x01\x0bR^e\x83\x96+\x9a\x0e\x10QR2a\xde\"w\xb8\xc3\xfe\xfa\x1e\xa4\x0c\xc3\x04\xf0%\x9f\xc3\xc6\x8cM\x02\x02x\xfcx\x0f6f\x94rX\xa7'\x18f\x18\xd8\x14\xeb\x8fwv\xe1\x8f\x10\"\xc2d\x1d\xb8 \xda\x9b\xc1\xc6\x1e\xcc_\xf9\xaf\xb8\x8c\xa7\xc0\xb6\x18x\xec\x83\x8dY\x04D1o\x92!\xef\x19j\xe9}\xd1\xd6R5\xcf?\x85\x0dX\x1c\xc3\x87=\x18\x8d\xe9\xc1:o\xddp7b\x8a\xb9\x10\xa4)\x9c\xb6\x0b\x17\xac\xda\xac\xb5#B\xe5\x96S\xb2\xb1\xab4bAj^)\xa3G$\xbcd\xac\x8c+\x81%[\xaa\xb8\x12X\xa2\x8a*A\x0b:_\xe4\xbc\xa0\x13l\x82\x99\x9a\x8e\xef\xb7U\xaf\xcc\xd6\xb4mf9\xc7ff\xad\xb7)o\\\x11\xe6\x82\xd9\x9a\xee\xec\xb6\x03]/\xaaO\x1e\xb6?\xe1\xf6\xa6\xe3v\xdfK1\xb7\xce\xac\x99\xc5\xa9&\xa0\xc3\xd5\xa7\x0f\xe8p:D\x1a&%\x1bm\x82\xca\x89IU_M\x8b(UA\x92t\x9e\xb15J\xe5{\xed\n\xb8\xd6\x88\x0d\xb4y\xdc\xd5\xcb\xab\x82\x7f\xb4\xdc\xc9\x84a\x8d\x8b\x05i\xbb@-p\xcb\xcd^\xc1\xbd\xce\xc5+\xb8\xcd\x9a\xbc\xe3L\xde\xc7\xd0\xf1@\xd6\xd7\xcb\x92\xa4x\x1eS\xd4\xd1S\x11\xe7\xfdF\xccN\xe1\xd4\x0c]M\x99xN\x932\x0e\x0e\xc5\xc45\x95\x8a$\x89N\x93K\x8d\xc34bz4\x00\xa8\\\x18\xe9\x1d\x81\x16\x01\xd5\x1b\xef4\x8c\x03\x1e\xf0\x87\x95\xa1\x82\x99\xdd<{p\xeaVn\xd63\x14r|w\xc8\xf6\x9ayUr\xe1[\xb3\x93\xfe\xb0\x85\xe2\xa9\x18s\xda\xfe\x99\xc7\xf6\xf9hQ\xc6\xef_\x86A\x10\x91\x0b?#\x8e\x1d;\x86\xc0i \x06\xf2\x12\xe1FNN\xde\x1e<{\xf7\xd7\x93g\x07\xdf\x1f\xbd~\xfd\xe2\xf0\xe4\xe0\xafG\x07\xaf\x0e\x9f\xbf~u\xf2\xf4\xf5\xcb7\xaf\x0f\x0fNNP\x87\xc7\xbcGsE$\x1c\x90\xc8\xc6M\x97\xd6D=\xe9!\xaa\xdd\xf9\x84\x12;b\xfa\x9ez\x98\\\xffS\xa5*wTf$6?\xaf\x8eXk\x0cO\xc2\xbdK\xd1\x1a\x05\xdfVN\xb5\xf8\x17?\x1e:\xadRk\xbce}$\x89\x0b\xd3\xee\xba\xbf'W\x13\xb0\xe8f\xd1\x19)\xdc\xa2\xf9\x05gTCC\xcb\xc2\x04a\xa6;\xdf\xe6\x90U\xe8\x81\x8dFLx\xc0hz}l\xd7\xd4\xa9\x07txp\xc4t\xb0\xf2\x0b=\xb0\xc9y\x80\x81\xd8&\xd0\x16\x0f\xe5}\x18t\x879\xa37\x1cJ\x91b\xc09\xfe\x1a\xc5JNC\xdb\xa8\x06KU\x9b\xdf\x94\xf1\xac\xf1-\xb1\x0b4\xa0\xd5y\xf9\xaa\x1aQ\x8c\xc0[\xfai-:\xd7jW\xe5\xa7\x1e@\xc7\xde\xb5\xfd\\;^F\x82rF\xec\x0b4\xa35\x0f\x957\xacA\xa0\xc0t4mTg\xeb\x02\x00^p\xfc\xc5qU\x8c,\x01\xb7\x06m\x1cH\x85\xfe\x03\x9a\xd7r\x1f\x00\x08\xfcF\x9b\xd6O\xf1\x9c\x07\x17U\xc0\xedX\x0b\xb7\xe3\xe6\xfd=>\xeeq\x0d\x07Nd&\xde\xc2\xcf_\xa0\xb7\xb6yD(T\xd0W\x19\n\xd3\xa8\x07T\xa9\xdf\x0b\xcf\x9f\x17${\xc1\x9d\xa7\x91\x83X\xdbt\xe1\xc0\x96J\x1cY3\x1f\x9bB:\x9a\xcf\x84\xdc\x0c?\x1e}\x1e\x12\xd52M\x14\xd9\x9f\xc5c\x82\xdc\xbb=`\xcd\x99dB\x18\xd1\x7f*\x07\xcd\x03\x00TY\x80\xeb\"\xfd4\x85\x95\x18\xb0z\xd3\xc5\xbb\xa1\xad\xf0\x18T\xba\xe3\xd13\x02\xceG\x16\x82K\xe2o\x06u\xfe|9\x81\xb9XZ}\xb5\xb7\xc4\x9f\x15\x93:H\xa2\x1as\nn\x8cqi\x12\xcf \x18\xc6\xe5\x96p\xce\xa7u{p\x92\x07\xa9\x8bX5xdw9\xb0\x01\xc2\x82!c\x87\xce\xf8\xbbo\x0c3\xcaW\x99\x91\x96\xb7Q\x0c\x14\xf6\x14q\xf7\x06\x0f\xab\x894\x07\x0c\xcdxE2b\xc4p\xef {(b`\x0bLmW\x97\x18\x9f\x99,.a\xbea\x8c|JN\x7fz\xe9\xa7\x0e\xbdA\xfa\x97\ndZ\x89\xf1\x18\x99fW\xb9\x87V+\xd6\x0f\xa9X\x93\x9a8\x1bB\xe6\xf7RH<\xc6-F\x82&\xd3\xf8x\x85H\xe0\x82\x10Y\x91\x0c\xe9J\xf8br\x013\xef\xa5\x9f\x9a\x19\x05\xe0\x84\x89\xcc\x15\xf7s\x93k\x99)\xc2\xb0\xfc\x08\x93\x80lZx\x94\x1d\x18\xd0x/\xa3\x0d\x12'u`\xc7\x8e\xc9_N~\xf8\x88\xab D \x97\x0c'\xc6/\xf5\xac(\xa8\xc4\xbe\xed\x07aO\x0d\x95\xc8\x0f\xbbm\xa8,\xe4\x08X\x9b.\x04\xde,Y\x9e\x86\xb18M\xb9\xc3r\xea\x9f\xf6&\xc97\xa3\xdf\xa3\xabt\x88L\xa8W\nC\xa6\x9b\xc7^\x91\xbcKS\x92=\xf5sb\xa3\x11P\x15+\xbeW\xec\x86\xa7\x9e\xcd\xcd\xb1\xf5H\xa2\x1aP\xacH\xe7!?\xe7<\xb6y\xac\xcc\xf8-\x1eTT;\xf28\x92&}\x9c\xc1:\xc5u\xa1\x9aU\xba\xcd\xa5L\xc9\x13A+\x0f\xd8\x80!\xb72\xdfN\xdb\xca\xab\x86o7@N\xef\xdfbx\x02\x915\xc7\xe7\xf3v\x07\x82\x05^\x06d\xc5\xcb\xa0\x03T\xc4`\xd6\xa2z\x1a\x02\x06\x8a^\x1c\x13\xa0\x14\x9dL\xe0\xf2\xa3a\xb5o ?j\xeel\xc0n\xf5\x9ef\xba]\xc3\x98\xd1\x06_\xa8\xf2W\x07\xdd\x86\xc6\xcd\xfd\xe8\xbfpi\xaf*\xac0\x8d\xeb\x0c\x0e\x1b\xf7\x9dc\xef\"\xf3S>\xa4\xdeK:\xe3\xf8U\x03h\x03\x04\xbe\xe2\x0e\xca\xa6q\xcf\xb5\xc6\xbbD\xe3K\x14\x10 A\x91\x9d0\x1f\x17\xb4UL\x8e\x1d\n]m\x9ad\xc8P@Z\xaa\xde\xa3\xd9~\xc4\xbd\x88\x87\xa3!\xaci\xa9:\x14Q\xc4t\x8fB\xbf\xd8~\x90\x90\x90\xcfY\xe6\xc8\x16\x89\x92\x87\xb2\xb4\xad\x10\x13\x12\xe4P$\x954\xaa\x96\xd2\x16\x0b\xbf\xe0\xafs\xf0\xb1\x91\xaa\xcc\x0e \x14\x0b\x02\x17\xec\xe4\x00CD\x8e\x0e\x11\xc9\x0f\xef\xe8\xc0\xcez$\xdd<\xf0\xe67\xbcO)\x88\x08\xbd\xafM$\x82\xb6\xf8n\xf1\xc4*\xd7\x8e Q\n\xa2\xce\x8c,\xb26\xb2\xa8%D\xfd\x01\x0e\x9a'S\xce\xa5\xa3J\xe7%?\xe2TN3 9<4)\x16A\xb87)qL\xc2\xd0J5\xf8^\xc4\x12v\x10K\xb1\xc2\xf0A\x16\xcaO\xb3a\x88\xc5\xef\"\x16\x9f!\x16\xb4x\xf5\x99M\xaa\x82\xd9\xe9\x1d\nH\x14\xd5\xca\x88\xa5\xb2\xbe\x0d\x15\x1c\x0d3Mb\x83\x0d\x1dn#\xcdlr\xc3GP\xae\xaf;h\x0e\xdd\xe0M\xca\x9e\xe5\x10\x8f@\xf1\xc8\xcf\x990\xda\x94\xcb\x8b\x9e\xc7v\xe2\x1cS\x8e{\xe6\x17\xb6\xaf \xad\xdb\xcfM\x10\\hBp\x02\xc0~?\x0c\x17\xf6\xa1\xb7\xc2\x80\xde\xd4<\x0e\x08\xf4\xa6a\x81n\x87\xdeP\xca7\x08\x99\x0d\x90\x94fM\x0b\x17\x15.X]^\xd0\x14\x08\x10\njL\xec\xad^\x0e\xf7v\xe2\xbe\xa6|\xfd\x1fg]\x06#\x16\xc1m\xb3C\xabr\x11\x15\xcf\xf5G\\\xe3o\xe2\x01K{c\x99\xe5\xc4+\x93\xc7z\xeaV\x83\x92\xaa\xb05<\xb6\xf9\xbe~\xf4\xd0\x96,\x8b\xb2[m\xce\x9d\xd2jJz\xaa\xd2\x98T\x14\x99\xb3\xa2\x84EEa\xf5RFz6\xb0\x97\xc1\xe1-\xf4\x1e/\xf9ix\x84u\xc9\x8f\xb0\"?2\xa7\x8a\xe6\xe4\xc3W\x90=\x02\x9f\x92\x1f\xe1\xd4o\x92\x1f\xfe\x00\xf2\xe3\x9c\xa7C=\xb0cAl`*$\x0d\xa9\x11\x1a\x93W\xf2\x87O^i\\\x81\x89(m\xd6c\xe9\xd8\x85\xcd\xa2\xca\x1b\xdb4X\xd7|\x14q\xc5] )\x08\xc6\xe6\xfa\xf0\xa1\xa3\xf1\x13jt\xf5R\xcah\xca\xab\x85[\xed\xc8\x1d\xe2Q\x9f\x18\x99\x84\x1f\x80nl4(<\x0d\xc5\xbc\x9ff\xc4\xa7\x07\xcd\xa9\x10\x17\x90\xc1\xa6 \xd2\xc6\xd7\xce\x8b\x85\x99\xcd\xe8k\x1a\xe4\xeb\xb4\xe8\xb3\xe1\x82\x017\x9b\xfc\x08\xe9\x1f\x05\xfd~\xf8\xd6\xbb\xff\xb7\x1f\x94(\xdeB*!\"\x06\x0cZ\x1e\xe0\x1d\x0e\xabI\x1f\xba5\x138\xf7^\x1d\xfcpr\xf4\xed\xdb\xd7?\xbc:9x\xfb\xb6_\x03#\x1e\xcc\x80\xa0\xcf\x92\xa5zR\xff*J\xfc\x80\xa5\xf8Y\xc8j\x84AM\x98\xb5\x1bX\x03\xe6a\xecG\xd1\xd0-\x12@\xd5[\xd9\xdc\xb5\xc9\x02\xb0p\xb42\xd7[b\xaa\x97~\xca(\xe8\xe4M\x96\xa4C\x90\xd5\x10\xf9\xb7\x11\xcf\xf4\xb6\x04M\xac\xd2\xb2\xe3!\x03H\x9a\xdb.\xc93\x8e^\x87\xaf\xca \x92q\xd8\xb2\x0c!\xee\xec\xa6\x87\x02\x8a\xe5\x0dVL\xc8\x81\xd5VG:P\xea[\xb6c\xfam\xf5\xea\xdaV:\xaa\\hCG\xddZ\xc5\xab2\x02-\xd4\x0d\x9b\xac\xa2\x1b\x0d\x8fT\xde!\x0dA\x860\x03\x95\xb4\"\x83\xea\xcbF\x9a\xcd\xea\x05\n\xd8j\x96\x04)\x9a\xd6\xd5\xd6\xaa2\x80Z\x15T*\x91\xc8r\xe6\x1a$\x91\xf0*\xf9\x1a\x067\xe8H\xe9\xf7\xc1n}\x89&\xb6\x9c\x8c\x9b\xc6\x14\x18x\xf4\xea\xf6`\xa7\xd91\x86\x95\xc1yu\x1b\x99&.\xc4\xc7\xc6\xaf\x9bp\xa7\xd0\x19\xb7\xbe\x91\x13\xfdk\x9a\xd5\xba\xee\xcb\x8c}w[\xdb\xbb\xaa\x8a\xa1Y;\xddC\x18\x9b]B\x98\xa261$\xe5ow\x18V\xa9\xa3\x1aoe\xd5\x8f6\xc2.\xc8\xb2\xd5a\xca\xa2j.%\x9d\x8b\xdfG6\x9c\xf3,K~\xaf\xa8\xb2 `9\x93\xd6\xd2O\xa7\xf9\xb1+$\x9fye\xb1\xde\xd8\x96\xee\x9bir\xac|)O\xb2\xb7\x02\xed\x13\xe3z\xf4Ub\xf3\x13\xb0\xdfW\xdd LU_\xf2}\x88W\x8d\xf4I#2\xa1*J\xc4\x81>Z\xc6\xaa\x9e$*\x9c\xe9xQr\x86\x02]\x850$\x96\x93\xa9\xef1Ij\xcb\xf7\xc3D\xec\x0b'F#\xb1\xa0'\xa3\xa5\xb0\x98*N8\xab8\xe1B\x84\x12\x7f\x04 |\x05\xc5#H('\x9cQ\xf8\x92W@wb\x05\x82GcpN\xa7\x13\x17\xa6\xf4\xba\xaf\x00&SY\xae\x0c\x8d\xe5\x85\x11C\x9a\x19\xc3\x08\xcfE\xd7\x036\xd7\x7f\xe8\xfe\x92\x13\x8d\x9f\xe0\xdb\xdeX];[c\x85\x17\xb0\x9c\x14\xa9.U\x07\xc8S{\xca \x9dE\xdbI\x99\xb4\xa3\xca_\x0f\x19g=\xae\xf1\xa64\xdc\xcc\xce0\xcce\xc6b\x86\xb2|7\xda\xb8\xa1\xedX\x9e\x98+\xc5\x9b\xd7#q\x86\x0c\x85.\xd9\xb6)\x87\x94\x9f\xe7\xe1Y<\xa4\xa9\xfeY\xe9'\xc3z\x99`\"\x98-g\xc59\x98\x93\x0c\xc9\xa7\xf2Z\xbd\xfb\xd9\xed{\xa1\xeb\xd8\xf6\x9ef\xb1\x055\xc1\x1a\xb7\xd4\xb9\x8cv\xb6\xdaYyJ\xcc\x1aP\\$O\xf8\x01\x7f\x93$\x11i\xa5{\xc3Yx\xf3\xa4\xccL\xb5\"\xd8\x83{?\xde[\xbfw\xa6\"\x86gZ\xbfi\xdb\xb2`\x1d\xd0\"\x13MG\xed\xc8\x05\xeb\x8b/\xefYf\x94>W\xca>Q\xd0C\xeb\xf0\xfc\x1c\xf4\xcfY\x12\x17\xe4\xb2`1<\xf9\x9b2\xa6\x7fo\x1a{Hu\xe7Ul\x0b\xc1\x9e\xba\x18_\xd0\x9e\xd8m\x0b\xd33_\x99\x84\x19\x0f\xb1\x81\xac\xaf\x9bg\x1aHaI\x94\xf3\xcdH\xce\xf0\x98\x98\xf1{r\xf5&#\xf3\xf0R\x9a3_\x94\xb8\xb3(\xd9J\x8b\xb2\xe8_\x146\x9c\xee\xb2\xf8XZ\x8d\xad[\xa14\xaci.\xafi\xb7\x98\x02_\xc9\xd66o\xadms\x03\x9a\xc4WD\xa9\xfbs\nq\x19\xaeo\xe8\x15\x0b\xbfx\xcb\xd4\xac\x02\xd8)\x05\xcf\x13\x9e\x02\xcb\xe1\x98xa\xfe\xbd\x1f\x85\xc1ADh\x0d\xda\x0e}\x1f1\xc6 Jb\xf2$\x0e\xde2x\xfe3\xb9\xa2\x1d\xf8\xb0\x0e\xf6ZD\xe7\xcf\xe2\x9e MF\xff\xa2T\x01{\xbf\x0f\x96\x05\x13\x98\xd9\xf8\xa7\x03\xeb`\xdd\xb3\x1c\x0cU\xe8\xb8\"\xf0n\xe4\x98\xc1\xe5\xdc\xee\x0f\xcf\x04{`Y\xcd\x85\x113dq\xb9h\x8d\x19e\xc0\xd9\x10\xba\x1c\x03\xdd\xab\x802\xd2\x88\n\x02\xbb\xc0([\xd8a\xb3\xb2O\x87\xb3p\xa1\xa4\\\x92\x97\x91\x88\xf89\xb1K\xf3\x1c\x96=We\xe3\xce\xaf\xef\xf4\xb9\x14P7 \"\x95\x81I\xcd\xd88\x1a(\xaco\x9d\x8e\xc6\xcb\xce\x01\xa1\x9b\xe2\x07\x01]\x830>;J\xec\xb9\x98\xe8\x8d\x06R\x1dd\xa9W\xf9,K\xaf\xefp\xcc\x81\x0by\x8b\xae9\xeb\xc8>\xe7Iv\xe0\xcf\x16\x93^b\x06\x84-7\xb3\xb5\x96\xa2\xac+\xec\xc5\xabk\xb4 I*\xb7f\x84\xa3\x94\x85\x84\x9aWp\xd4\x8e\xc3\xdc\xc4\x0cK?\xfdH\x03\x9e*\xa8`\xfe\x15\x9e\xbf\xcc\x15\xbb\xc0\x9c\x8f\x8diJ\x96~\xfa<.\x92\x1f\xc2b\xf1g\xb1\xdb\x98?5\xf6\xa3 \x9c7+\xe3\x8e\x0e\xd0\x00\xf2\xd1\xe0\xb2-\xd9h\x8ckU$\x88\x12\xfb$y\x82\x95\xe8[\x80B,\x80\x1a\xa5vRg\xd5\xf0\xa9\xa6\xa2\xce\xf0\xed-\xa9\xa8\xd1f\x9b.\xc2\xc0\x7f\xb1\xfd\xc0\xe9\xb34\x16)U<\x91R\x85B+g\xa3\x86H<\x9b\xdf\xa5I\xda\xa3\x83b\xa7\x17\xfdjY(\x16Epr\xdd\x06\xc4\xe4\x02\xbf\xef$gP\xd0\x8a\xe6Y7R\x85\xd1&1)\x8fm\x8dw0\xc7\x85\x84\xdb*\x1fN\xc5\xfaPv\x92\x16\xa5I\x12\x1d\x86?\xd7n\x9d\xcd5\xa1\x97\x9b9\x9d\x04\xa5 \x92.\x01\xdb\x1d\xb7\x8c\xdf\x06\x9c\x15\x90\xc5`\xc6m\x89\x1bc\xe61%\xe3\x1a{\x01g\xf0}\xfa\xb6\x9a/K\xc7T\xfd\xb9\x07#L\xc6$\xb0\x18\xec\xd1\xbbS\x91\x9bIAZ\xc6\xa4I\x83O\xda\x0bB\x9f\x0e=?p\x0dn\x02\xe4 \xad\xddJ\x80\x0e*`\x8fyl~\xd5r\x80\x12\xe6A\x05\xf7\x9dT\x15\xa0^\xceb\x91\x91\xce\x82\x0e\xb90\xe0\x96\xab\x95\xdd\xc9je\xae\xf0\xcb\xeb\\1\xe2\x19\xbe`\xcax\x1e\x8a5\xeb\xf2\x81\xdd%3\x98\x91\xdcf\xd5\x92;Y\xb5\xa4Z5FM\xa8\x9d\xc0VZ\xb8NB\x88n\x0b\x9a{\x8d\x99k|\xac{m\x9b\xa5Z\x1e\xef\xdeW\xc5\xa2\x8b\xed\x9d\xadv\"]\xbf\xbe\x10c{g\xbb\x13^\xaed\xe5\x0f\x1d\x17,\xaf\x9d\xc6\x95N\xc8\x9aX\x9ax\xc5\n\xc4#\x08-\x0c \xd2\xcdx\x80\xef\x05cB8\x8b\xe4{$\x9f\xf9)\xb1 c\x92&\x18Z\x9e\xe5Q\xb0\xb7v\xdb\xd22\xb8\x990\xae\xa2\x06y\xdc\xccj\"\x84\xc7w\x9a\xb90\xd7\x11H\xa9\x8bq\xf2\x84\xb9F\x1761_I#05\x86\x91\xfd\x12\xacSz\xa2\xfcX\xbc\x12YP\x90|sk\x07F\xbcd,\x16\xab\xd9\xc27X\xd7\x8a\xcb\xe5)\xc9\xe47\xf5\xaa\xf2.\n\xef\x8b/\xf8\xc8\xd0\x15\xb2\"wg\x94{)\\\xca\x83\xb2\x00\xcd\xfbP\xc2: \x05\xb2\x89L\xb0\xe3\xc2HM\x13/0\xc6\xa5\xf2\xc8\x9c#\xb3)59\x81\x18\xd6A\xa1y\xa1\xab\xd2\xe4\xcf\x0b\x8d\x06\xa1\x92j/\x99\xc4zII\x8c*\xbc\xf6r}\xdd\x81\x05\xac\xef\x01\xb1S\xba\x0f\xd3\xe5\xb1\x0b\xe78\x97\xd4\x85\xa5\xc3w\xaf;\x02Ml[\x90\xd8\xa2P\x99\x8d\x10\xf8\xf0\xcf\xfaP\xd8\x95\x8b\xd1\x04\xcf8m\xd7\x13Z\xe6\x0c\xc1\xa0\xf0H\\d!\xe91s\xa9\x16\xe5\x84-\xca\x9a}\x05{p\xea\xc5\xe4\xb2\xb0\x1d\xc7\x0b\x12L\x1d&-\xcc\x15K;#\xad\xcd\xc9\xfa\xba~u\xc4CW\xa9\x7f$\xda\x01\xe8\x17H\x91i\xd2\x8e\xe1\xae\xcdSU(\x92P\xdd\xc1\xca4\xc7\xca\x0e\xc2P\x0e_\x0d\xc6\xd6\x9e5\x01koS\x03\xc1\xd6\x04\x8b\xc7V\x17J\xb4\xf2\x02\xeb\x0b\n\x93\x1d5\xc0\xbd\xe9\xde\xe4\xf8\xdeY\x1fc.5TL\xc9q\xb7_#GY\xc6w\xb3(\x9b8m\xdd\xa2\xec\x8di\xf1d\x95Ea\xcba[\x1e;\xccd\xba\x89\x1az\xbaV\xeco\xd4D\x13//O\x19\x15`\x8f\xd1\x97Pz1r\x1ci5\xed\xbd\xcd\x0f{c\xe7\xee\x17\xb4\x86W\xf5\xd9\xb9\x13\xfd\xd7\xfd]\x87\xc7\xe8\xfc\xc6\x9f\x15Iv\xd5=\xc5\n)\xc0\x84\xa2H\xbfM\xa5b\xd1\xe9i\xc6JOO3e\x85 \xc8H\x9e\xb3:\xec\xb7\xb2ZFx/\x19Qw\x94\x15\xe1,\"\xbc\x0e\xfeVV\xcb\xc3\x80W\xa2\xbf\x94U\xca LX\x15\xfaKU\xe5\x14\x8bO\x95E~\xce\xda\xa7?\x94\x15\x82\x90\x95\x07\xa1\xba8\xe1\xc5\xea\x9e\xc33V\x1c\x9e)\x8b\xa3d\xf6\xfe\xefeR\xf01T\x7f*+'\xc1\x15\xab\x96\x04W\xca\nl\xeb\xd4\x1bwZ\x16E\x12\xb3\n\xf8SUi\xe6\xc7\xe7>\xdb\\\xf6S])\xa5\xe0\xcak\xe1oe\xb5\x90\xcf\x8a\xfePVH\xf8\xd6\xd2\x1f\xea\n\x11/\x8f4\xc5gYR\xa6\xa2\x0e\xfe\xa1\xaa\x18\xf8\x05\x03F\xfaCW!\n\xf3\xa2\xaaD\xffPV\x0cX\x95@YH\xd8p\x03\xa2\x1cn@\n?\x8cr^\x05\x7f+\xab\xcd\xd9\xca\x06s\xe5\xaa\x06\xa1\x1f%\x0c\xa6\xd8Ou\xa5s^\xe3\\Y\xcc\xc7\xa9\x1e&_\x05\xe5\xfc\xc9\x12\x0b\xc9R]xJ\x02^~J\x94K4\x0fI\x14`\xd2\xe7\xcc\xb6\xc4\x1f\xea\x8ag2\x98\xd5\x7fj*\x97\x19\x11\x15\xcbL L\xf3$\xc1\\\xb5\xff\x1f{o\xda\x1d7\x92$\x08\xbe\xdd\x8f\xf5+\x9c\xf1\xaa% \x03\x0c1H\x89\x94B\xa2\xd8J%\xb3[\xdd\x99\x92FRVMw0\x8a Fx0PB\x00Q8xdQ\xef\xf5\xcc\xec\xdc\xf7\xee\\=\xf7\xd9\xb3;\xf7\xb1\xc7\xec\xce\xf4\xf4\x87\xce\xfc#\xf3\x07\xf6/\xecs3w\xc0\x017\x07\x10$\x95U\xbbo\xf1\x81D\xf8\x05wssss3s3Q\x08^\xe9B\xc9R\x16I\xc81.\x86\x90\xbd\x18\x92\x99\xdb\x98\xb9Mf\xee`\xe6\x0e\x99y\x1f3\xef\x93\x99\x0f0\xf3\x01\x99\xb9\x8b\x99\xbbd&\xf7qB\xc4\x8b\xad\x80\x04\n\xbe\x92\x85\xcaU\xb6\xb0\xae\xb1\x85l\x85n![\"\xca\x89\x17\xaa\x00\x92X\x92\xc0\x06\xf3\xc4_\xe2\xe4\xe2+Yh\x89K\"X\x92\xeb!\x88V9\xe2\x1c\xbc\xd1ERY\x80\\\x95\xefO\x10\x90\xefOH8\xbe\xe7\x97\xa7\x1cQ\x15_\xa9B\xa1\x7f\")\x04\xbc\x91E\xf8)\x8f\xf0K\xf8J\x16Bh\x85$\xb8\xc2 z/\xb3\xa3\xf7T\x81\xa5\x1f`G\xc5\x0b]`%\xf3\xc9\x89^\xfa\xc9{\x99\x9f\xd0\x1f\xe0Q\x8e\x05x\x94\xdb\n\x04\x99$%\xea\x07]P\xd2m\xf1b) \xb1\x17\xde\xa8\"\x91\x8f\xa40\xf2IR\x18\xc5\x18M\x19\xcb\xc8\x1fTA<0B1y\xac\xa5\n\xe1\xf4\xd2\xdbU\xbc\xca\xca\x85\xa4~X\n*\xba\x17[i^\x9cg\n\xa7\xf1\x95*\x84\xdf\"?\xb2\xf2\x13\x1fg\x00\xde\xc8\"\xc14StU\xbe\x93\xc5T\x11[v|Zp\x8c\xea\x07U\xf0gP\xe2gTV\x82\x03I\xc8\x91$\x08\x85\x84\x84@\x92\x9f \xcf$^\xa8\x02\xd8/\xb2C\xa9\xbf\xc4\xef\x8a\x17\xb2@\x89:v\xc4I\xf9\xb4\x98N\xf9N\x17\x0b\x15~\xe1+Yh\xe9\x87\x88b\xf0F\x16\x89\xf3d\x8a\x13\x82\xafd\xa1\x95/;\xb4\xf2\xe9\xdedI\x1c!I\xc5W\xba\xd0\xa5d\xe0\xe1\x8d,\x92#\xeb\x9d\xe6$\xf3\x9d\xe6\xcb\xa5\x9f\\\xca\"\xf0N\x17\x93\xf3@\xaf\x97\xcc?\x91\xfd\xc80R,Q\xa4\xe0\x9d3\x1b\xf3\x9c!\xd9\xcdH\x92\x9b\xf1\x8b\xac8\xd2\xa8\x1fdA\xc1[`)\xf1F\x16Y`\xfe\x82\xceT[vf\xdb\xb3\xb3@n\x87\xe2\x85.\x90)x\x887\xb2\x08R\xcd\x8c$\x99Y\xe2O\xdf\xcb|\x7fJ\xd2x$\xf0$u\xcf\x11As\x12;\xcf|\xfc\xf0\x99O~\xf9,\x98qW\xfc\xfa\x9c$\x11<\x0c\x83\x95<@\xcaw\xaa\x18\xae$\x9a5Y\xfa\xa7\x92\xbb\x11oT\x910\x88\xb0\x84x\xb1\x15\xf0\x93_K\xfcY\xc0\xa3\xac(Z&Q\x95\x96~\xaa\xf6\xf1\x94\x9c\xe3\x95\x82\xd0\xca\x02\x9d\x95\x9fe<\x89T\x19\xf1N\x16\x8b\xc3\xcbSI\x00\xe5\xbb\xadX1R\xf5\x83*(\xc6\xe4\x87\x95\xd1V\x93\xc8J\x8a\xb8&6\xd2\x9a\xc5\x92\xc8d1M\xec\xcf$=<#\xe7Q\x10\x85\x82:\x90\x05\n\xa2\x9b!\xd5\xad\x94\xb0\xc8\x88P\x05{\x0b2\xa2\xaa]f\xb5w2\x1a\xfb\xae\x1e|\xac\xd2 eMv\xc3~\x18\xc6\xd7\xf8\xe1\xba\xe95j`)\xfdk\xe4\x0c\xeb\xe1\xb5r\xd9\xf7zq\xb4\xa8\x7fp\xff\xbeeL\x8df\x1f\xcal\xe3&\xf2s&\x8doi\x19\xba\xfa\xcaT\x94x\xf2\xc4\x8f\xe2\xe8r\x19\xe7\xe9\xd3\xa7\x84\xa8tn\x95\xaf\xfah\x99v\xe6\xf4\xe0\x8dB;\x06\x82#\xc1\x98\x9e9\x85\x12\xd5RN\x0c\x17\xca\x15\xe3\xb6\x14Dm*\x14\x95\x8aUKA\xc55\x9f5q\xcd\x0c\x19\x8e@0\x1cg\x8eR\xde\xda\n\x02\xd0\xb1 \xbc\xda\n\xfa\xd1\xe5\x88-\x9cD7\xb3{ \xdab;(_\xcd\xdb\xe4\xdd\xeaQ\x9a\x9c\xaa\x7f\x1fk|\xcc\xfaS\xd3wh\xb7\x9a\\\xdd\x94b\xe6\xf4\xd4U\x13\xf6u\x8f\xf5!8j\xefk\x16\xcf\xcbx]\x98\x91`\xc6\xc2OY \x03\x16\x8b\x9a\xef.W\x9cEq\xe6\x83\x8a>\x88\xd2`\xc6\xd5P\x07m~\xb0\xce\xe4\xbd\xc0\xac\xd5\x99#\xdcn\xad;[k\x83\x01\x93\x9f\x00+F\xc7\xef\xee\xf4CBF\x05f\x16\xc3\x8f\xc5\xf0\xeb \x12 \xc5\xb4\x14\xd3\xd2|\xb5\n\x03>cY\xacC\xcdc\xfcb\xc5\xa7\x19\x9f1?B\xe8\x0c\x08g\xb1\xfa\xd3|Q\xbfP8\x87\xa8p\x0e\xd9\x13-\xc8u\xd8\xefw\x05\x0d\xdc\xd6p|\x8f\x85\x05f\x89\x1e\x8fE\xdfC\xf16\xe9y,\xef\x0091AS\xddf\x11.\xe5\x95\x16\x0e7\x18,ey^\x7fl>T\xe8\xa5\xc8q\x93\xea\xe0Q\x80\xdd|%\xae\x89\xe4|\x0d\xc4\xce?>b\xe7\x9d\x11\x9b\xa5At\x1ar\x8c\xbf \xd9\x80\x9ba\xf9M&\xde\x16^Ja\xe8\xf7J\x887\x1cp\xba\xa6\xad\x0e\xdey\x8e\xf1\xeeN\xe4/\xc1\x98\x95\xb8\x9fC=y\xab}\xb1\xedA\x1c\x1cL\xe3\xa8\xb8;qu\xc5\xaa)\xd0\x9bri\xb7c\x9fz\x94\xd1\x99\xd1X\xa7\x16>\x00\x14\x7f)\x90]\xcd\xa4\xa8\x0e%|(\xf1\x8bCw\x0b\x17\x05\xfa\xafk\x12\xb9\xc6\xbbL\xf5\x07\xd0f\xe9\xf0q6q\xeb\x0c\x86>\x01I9\x01\xb1\x05\xd8\x91IY\x80\xa4\xbc\x8cg\xbc\x95\xa3\xb8 \x0cm$\x03\xf9\xca\xef\x95`\xfc\xc2875\xd6V@\xeb\xbbZ;M\xea\xc6\x81UL\xba6*\xf1\xec\xd7_\xcb\xebpd\xf8\xcd\xd61k\\\x17\xf8\xa5h\x1d\xb6\x18\x90?X\xf8\xe9\xab\xf3\xa8\xb8[\x1ev\"\xfd\xac\x99A\x1b\x00\x83\xd6\x8d5c7e\xcf\xd8/\x80t\xc5\xd1\x1a[4q:\xd0<\xe5\x18\x07\xb4\x06\xbb\xbe\x9b-\xdd\x02A\x8a\x95\xa1{X\xe6\x05\x83\x9e\xeb\x17\x8fm\x8f\x18\xd4J\xcc<\x07\x7f\x1e:\x8c\xdb\x97\xa6Xp\xbf\xf1\xf6\xd5\xcb\x01\x9eu\x83\xf9\xa55\\\x80z\xd6\\i`\x1f\xaao~\x1d\x96Z\x1c\xc1\x8eY,\xcf\xa6\xfd\xf2\x1a\xe8\xf2\xee\xb2\xdd\x9cL=\xb7\x862\x157\x1f[\x8fYV\x99\xe9\xac\xfd(\xa6dAb\xef\xec@\x1f\xa9\x9d!*:\x1e8\x1bC\x8f\x15\xb3\xa7\x9c\x87T\xe6\xa6\x80\xd5\x80\x1d\xd6\x8f\xa5\xb0},\xf8\xf4}\x01\xc6\xd4c'y\xc6\x12>\xe5\xc1\x19\x9f\xb1_I\x99\x9f\xb1 \x9a\xf1\x0b\xf6+\xe9\xa0\xe7\xb1\x13\xf4\xed\x05\xf7\xa4k`\xb3\xcf\xee\xf7\xb2\x04\xa5o\xd1r:\xfc\xf6\xe9`\xda\n\xe2\x9d\xbc\x8f\xeaWX\xd3jo\x05\x81v;QG\xd6\x99\xc6vY\x9f\x96\xa5x{\xeb-]t0\xddT\xcf\x0d\xa7\xf4\xff;\xac\xc6\xd7\xf8\xc5\xaf\xd7\xe44:\x1d\xe0\nfa\x1cv\xc4\xd9i\x97f\x99lz\x0en n\x85\x0f\x99\x17\xa0\x9e\xb7\xd6i^\x12\xdd\x16\xcc\xed1%\xfc\x02BK~oX\x9fv\xc6\xfa\x10\xb0\xbe\xee`\xae\xfe\x18X\x1f\xde\x00\xeb\xc3[\xc7z\x85\xc2>:\x93\x04\xfe\xa9\x8dk)V\xca\\\xac\x94N(-J\xaf`\xa5\xcc;\xae\x94\x8d\xd5zpz\xcf\xe5\x99l\xdeL\x8e\x8f\xa2O\xfdY\xa1\xc2\x10\x195\x9e\x0da\x80\xd7\xf9{L^\x139\x8a@\xd3\x06\xb7J\xc8Z\xfa%\x13\xe5\xa7K\xd6\xef\xb0L\xcf\xe4\xa5\xb2\x95\x93zln\xae\xf6y\xb7\xd5.\xe0\xb6(\xc0\xb6\xf8\x05\xadc#\xf5\x83vE\x92\x99>\x87(\xfcQR+y\xfd\xef\xa0pR\x7fu\xc5\x86\xec\x1ed\xc0K\xc6F\x8c\xc3\x85I\xb8\xed\x07\x0cZ\xa5\xb5\x0f\x96o\xcfhJ\x02\x17g\x97J\"\x81\xe8\x84\xe2=\xf0\xd8\x1c`\x92\xa37\x1ep\xb1\x13#+\xfa\xdc\x0f\xc3 :-D\x0e)\x83\x95\x03\x8e\xb9\xd9,H\xf84\x0b/Y\x90\xb2(F65N\x04\xd18\xb9\x84\xc0*_\xaf\x92x\xb5)\x88N\xfa5[\xf9\xd3\xf7\xfe)\x1f\xb0\xafR\xce\xbe.\x1a\x1c\x00\xc3Z\xfct\xdc\xaf\xc5:\x9b\xfaa(\x9aX\x0e\xd8\x1b\xee\xcf\xd82N\xb8\xe0\\\x17Y\xb6\x1a\xdd\xbb7?\x19,\xf9\xbd<\xe5\x9bP{\xb3\xfc\x8eu\x91hx(f<\x19\x07\x13v\x007+\x8b\xcb\xa1*\x0d\x89\xc4\xbb\x05/\xcf:\x15\xa2\x19\xa4`\xe5(\x18\xef\x94%\xfcgy\x90\x80TQ?O!\xdf\x1dd\xa9$\x067b\xdc\xa9\xe0H\xdb\xa5k\xa6+\xe61\xbc3\x92\xa1\x0d*\xb4^\xba\xd6B\x1co\x10\xd7\xdd\xd5#\xc6\x10c,\x91\xa4\xdbm\xee\xa4v\x9b\xbb\x8b\x10\xe11\xdb\x80\x10\x91A\xed\x16ucMV\xeaBb\xbcB\xadM\xe4\xd0\x0e\x9a5nvS}\xea\xc8\xf5\x82\x17\x9f\xae7\xbbAx-\xf0cc\xe9\xf8\xe3\xe1\xa4\xd3@X\x17\xd9\x8e\x0d\xa3\xa5[\xd8\xf6\x05k~\xbf\xeeu\x96&s\xa7\xcdWL\x95\x9e\xc5\xba?\xd5\xe5\x85\xec\x80I\xbb(\xe0\xfc4\xf1\xfa\x1b~zx\xb1*\xef\x81\xf7XGG@\xf2K\xca\xf4\x08\xaf\x9c\x82;\x89\xb7ZJ6\xee\xfd\xea\xaf*\xd7\x1b\xef\xfc\xd3\x1e,\xe0\x16k\xb2L\xef &\x9bpD\xa7W\xa2\xe3\xaa\x07\xf58r6\xe0^\xda\xddwiN\x98a,\x05\xb5+UZx\x07\xd9\x84\xbc\x9a\x9bSR~m8\x01ht\xb0T\x99\xa1\xcf\xfcL\xfb\xfa\xcc\xcfx\x8f\xc6J\xa3&\xcemY7\xe1\xa7\xfcbE\\1\xb6\xa1Q7x\x9e4#+-\xd0/v\xec\xe6\xad\x1a\x91\xb6i\x1bn\xdd\xf6\xd4\xe8\xfd\x088\x9b\xc6=\xb4y+\xc620\x03M\x05$\x98;\xf4\xa8\xa9C]iL\x9b\xd3\xb7\xea/YIs>\xc9\xf6Q\xc5V\xa6xl^;\xa9\xb0}\xc1J\xcf\x07z\xc2\xdc\xd3\xa4b7\xf0C\xd0\xe4x\xa7P\xe9\xdfR\xfb\xbd\xe1\x83\xc1\xee@z\x1e\xb8Vkg\xa5\x8f\xe9\xdd\xfb\xee\xa0\x88\x98@Y\xf3\xb6\x19\x1b\x07\xb2\x9d\x07\xa4}\xef\x83\xfb{\x16\x83]\xdfQ\x92\xb9\xdb\x18\x87aG\x8c\x9d\x1fn\xd3n\xa3\xeb&\xca\xa2\xb3\xbdep\x11Di\xc7I\xad/xuf\x19\x13\xd2\xc3\xd4j\xef\x8b\x9f\x1c\xb1\xdeg\x87\x9f\xbfxyx\xfc\xe5\xb3\x97\xbfe\xf1\xad\x90f~\x16L\xbb\x95])\x0c\xefTZ\xfaS]\xa3\xc2\"\x08g\xcf\xd7\xadu\xca\xb3\xcf\x90\x1a@\x84\x9dj\x9d\xe3/\x0f\xdf\xfc\xda\xe1g\xf6\xaa/\xa2 \x0b\xfc\x10\"\x17\xadY\xf5\xb9\xd6\xddu\xaa&<\x82\xbb\xb4\xaa\xc6\xab\x97\xcf\x0f\xad \x94+\xe8\xc7A\x18~\x89\x8eK;\x80\xa4\xa8\xf6Y0\xbbF-\xf1\xb17\xa8($@j\xc3\xa3E\x9c\x0bp\xc86\xbeZ\xcd*\x10\xed:\xc8z\xbd.\xfd\xfd,\x98]\xa7\x1a|.Zv\x86\xcfW/\xdf>\xfb\xfc\xf0\xf8\x9asB\xd5^\x1b\xc8T#k\x0c=\x87\xa2\xc5\x1c\x8dX\xef\xd5\x8f\x0e\xdf\xbcy\xf1\xd9\xe1\xf1\xa7\xcf\xde\x1e\x12\xbc\x8f\xd9Nh%:\xb0\x10\x93\xe0\x8c\xcf`5}\x9e\xc4\xcb\x86\x15\xd9\xe5[S\xeb\xb7fA\xba\n\xfd\xcb\x97p\xe3\xbb\x13G\xce\x80\xf0j\xf5X]\xac\xab\x1e\x8b\xd6H\xd1\xd4\xce_\x13\x1cgK(\xb9B\xed\x11\xa1\x9a;\xaa\xb8a\x8b\xfa}W\n\xb4\xc7\xd1d-\x15\x17AJ;\xf7\x9b\x0f\x8c\xda\xe2\x88.C\xa6\x19y\xa4\xabP\xd6\xd0\xb5k\xf7\xca\xd2\xa1\x1b\xf4\xc5\xd8;\xd6\xe8N\xad.8\x13\xaa\xa7\xed\xb3\x85c\xa4B\xcb#\xb2\xf4Z\x08\xa9\xed\xc6kt{\xa5q\xa9\n\x84E\xda\xba\xf0+\x98\x87\xce\x1d\xd8\xe8^\x94u[C\xac\xba\x8e\x82\xa8\xbdU\xf5(>\xaf\xdd\xa6_=\xd0\x9f\xba)`\xd4\xd9\x14\x90)\xb1\x97\xe0\x16A\xd3\xd9\xed\xb3\xe2 \x9c\x8d\xd8cw\xc1\x88\xf6y\xe8\xa7\xe9\x88\xfdV\x9c3\x1f\xf4!\x19_\xae\xb2 :eY,C\xcf0\x9f%<\xe5\xc9\x19\x9f\x01\xa6\x88\x9ez\xec\xeb_I\xbf\xf60\x16>n\xd8\xd1\xd1\xdd\x8c\x9dp\x06\x11\xf2A\xb4\x0b3\xdac\xef\xf9\xe5\x80}\x86M\x05\x19\xf3S\xe6G\xa5\xc1\xb4j\x11R\xb8?{,\xca\x9c\x07a\xc8\xd2L\xfc=\xe1\xcc\x9fNy\x9a\x06'a\xd1\xb8n.~\x97vRo{\x94\xd8\x0b\x80\xd6A\xea\xa5\x1e\x90~\xad3;L\xe3\xb9Cs\xa2\xd9\x01\x0b\xc7\xd1D\xca\xe9\xbb\xf7\x83\x95\xa7\xcb\xc0\xa1\xb6C\x10{\xe4\x1e\xebu\x9e_1\x95\x02\xb2\x97q\x9eh\xb6\xc2\xa0 \xcb\x16~\xc4\xe2h\xca\x07\xec\xdd\"H\x05\xe4\xe7a0\xcd\xd8\xd2\xbf\x14s3\xcb\xb9h\xc9\xc7Mm\xd0C\x07\xc8gq0s8\xc6\x95_\xc0\x8b\xc7\xa8\x80S\xb6\xa7Y\xff\xab?\xf2#\xb4\xc7\xe5\xfa\xd3\xde\xac\xbd\xc4\x07\xa42\xeb\xd04?\xcf\xe2\x93 \x9aU-\xee\xd7PA\xd3\x81u\x98f#\x98\xd6\x11+\x13\x88\x95\x8e3;b\x9d\x10U\xee\xdc\x11\xc8Te\xe1\xd0Ml\x05\x8f \x12\xc2\xdc\x9fr\x1bB\xc5g`\x87Q\x9a#\x86eXj\xc9\xb3ENDg\x9f\xe5Y\xfci\x10\xcd^\xfbAb\x89TY\x8dR\x19\xd5\x97\x99\x0f\xcbl:@\xee\x1f\xa6T\xbe\xbb\xa4\xbfw\xf5\xc0\x1c\xd7\x1bC\xbb\x8a\x1cC\"\xb6\xedJg\xf2^h4\xce;X\x8e\xad`\xd8\xc6\xf7\xda\xf5\x80sg\x85!w\xa6fm\x97M\xc7\xf9D\x0c:li\xa9\xc1\xef\xb3\xfe\x881\xcd(\x02\xd8\xd6S\xd6d7\x0d\xc6+\xe0\xac{\x05\xb7\xdc\x86H*\x06\x8a\x92w\xdb\xc1\xc0P\xbfmR\xf4\xe7L\xba\xcfN[\x03\x96\xeaO\xe0\x80\x13q;\x13\xb0\xac\x13@\x99\\_\x81_E\x85\x11\x81 \xd1l\x15\x87\xc1\xf4\x92\xfdJ\n(\xfd\x9e\xc3\xeb\xf9\x82G\xb8\x02O\x81\xdd,\x96\xa6\xa8\x02\xc4x\x89\xb3\xdf\xd0\x9d\x03\x96`\xe4\xd2\x85#^\x042\xb0\x11\xd5C\xf4\xe0\x8be\xcf\x8a\xb2\xdd\xa0/\xddA\xcb\xda\x1d8+(\x1ec\xd0\x93\\|\xc7+*7\xd6m\xe0\x15\xcc-\xbe\x13\xa1\x9fY\xf7\xfb\xea\xb1$p\xa4AY\x83\xaf~\"=\xf3Xo\xc9\x93S\xaeB\x1c\xbd\x8c?\xcbW\xa1\xd8\x90\xf9o\xf2\xcb\xd4qG\xec\xb9\x1f\x89m\x17\x8a\xb1(\x8e6\xb1\x99\x14\x08x\xe62\xe2\xc8\x82Q\xca*:=`\xf8Z\xbf\xf5.\x91\x06-\xf8\xb5\xec<\x96\xf4;\xc5\xed^p\xfa\xa9\xbf\xe4\x18\x06]l\xbd\x9dv\xd6\xc7\x02D+\xf0\xf0*\xf6\x044\x92SE\xa7~\x9eJk\xb2\xf3\xb8.\xb6u\\\xb1\xc5\xd5\x0e\xd3\x8e\xab8\x0e\xc9w\x8b\x15P\xe9\xa7\xd8\x1c\x17\"\xf5=\xbfL\x15\x0b,\x19S\xcb\x0dUeB\xd8 -\x16m\x96\x88:{i\xdd\xf70\xb04F\x83\x15\x10\xf1\xcaH\xb2\x96{\x8e\xe2\x81C\xad\xa5\x96]=\xaaL\xe2\xca{(I{\xe1\xd2\xd6#\xb2\xef\xde\xe0^\x98\xf0\xd5\xcc4\xa5\x9b\x13\xe3\x14\xc0\x0b\x1dV\xa4\xdbz<\xbb1\xe0\xad\x00\xb7\x02\xf5\x9a]]\xb6\x1e\x1524\x9e\xa3\x94\xc4\n\xec\xb5/\xd5[1C\xd1\xa9\x87P\x13\xb4\x82\x86)\x83\xd6\xe3\xe3 \x85J`\xe3\xb7\xb1E\x96&H\xaa\x89\xb4\x97\xed\x1d\xac\x88\xea\xaf\xddG\xda\xde\xa5S\x1fO\xac}\x94\xfe\xc1\xa5\x02\xa9\xb3p\x0b\xfa\x87\xf2\xf8d\xc0\xa3\x9f\xe5<\xe7o\xb4\xa6$\x86\xad}z-\x06\xdc\x11N\xca\x16g\xa3\x0e\xb0\xeb\xc3\xea\xd8\x1e\xd6\x97iF\xa2\xce\xb1\xaeT\xd7y{vB\x90\xb6\x12\xb2M\xe42\xab\xa9T\x93\x06sPV\xa2\x89yXP\x91\xd7\xee\xdc\xe9\xf0e\xf5T.\x11r\xb2]\xcf\"\xeag\xfd}\xb6\xdd\xd6>\xab\xc9,\xdb\x8f\x05L\x9e\x88\xb2q\xc4\xfal\xd8\x81O\x85\xe0\x0b\xfbH\x99\xe2\xeb\xfaA\xf8\x00\xe8\xab\"\xda\xad\xa4t\x9b[C\xe7&|\x0e\x0e\xc4\xbc\xca\xbaP6\xeaQi1\x9fq\x19\xcb\xc7>\x90\xc2\xcaWT\xa9\xb1\n\xec\x80Lv\xdcV\x81^\xe0\x10\xacY\x0evuUs2`\xa6\x7f\x85\xf8\xc4\x88-\xc5\xc9W\xa2\x7fq]]\xf0.\xe2\xd3=\xb1\xb9\xe8\xea)q\n@~_P\xc14\xd0\x14w=\xb7\x06\x91\x9c^\xad-'\xde\x04\x84\xe5\x15c\x97\x88\x9f\xb3cOO\xac\xf8\x10\xc1h\xc8Z&\x85\xe22\xa8_>\x90!O\x9d\x95n\x00\x9e\xb9\xae\xc7VN\xe6\xb1S\xf5\xc2\xd5\xcb%\xec\xb0u\xb5\x08\\EP\xc1\xe6\x0bMI\xbd\x98\xe3\x82\xacB\xef\x1c*\xda=\xd6\xc3\xc0\x07pnr\x06\x83\x81`\x98M\xd1\x16NO\xb0\\\xa15\n\xf3\xd9\xd7\xd8\xc0\xd7\x92\x93\x04f:u\xf5\xf1\xcb@%N-I\x86\x9bj\xe4w\x9a,\x93n`\xd0s\xd6\x12\xd3\x0c\x0co\xca\xe2\x91cs\xe6g\xa7zr\x00F\x0cg\xee\xca\xe0\x96\xc3\xfb;\x10\xdd\xf2v\xc7\xb3\xbdG\xdb\xe2)\x1b\x00\xb1\xd5\xc5.Ek\xfd\x12*5Z\x0b\xc1X\x1f\xeby\x96#$\x8f\xf2%O\xd0\x01\xfe\x86%\xd0\xe8)\xef*]Q[\xf3\x80\x96\xb5\x13b\x82\xc6\xbe\x07\xdf{\xbf\x83[\xe9\xb7D\x93\x8e\x9d'\x1b\xcf\xea\x08\xc4\xf6\xd9\xd0Bv\x18uz\xb8\xc1\xfao\xa3E\x80\xb7\x9e\x14A\xe3M\xa3*\xca\x927\x95\xe0&\xf5 >Iyr&\x86.\xce\xdcp\x0bXK\x1a\xc9\xa0\xbc\xe2P\xad\x12{\x10\xd1]+\xb4\x8fvr\x19:\xc7\xd6\n\x92;\xf0\xf7\x02\x91\x8a\x80\xc7\xf0\xcf\x00Bn\xa4\x98[\x8fYP\x11\xf0\x04\xb4\xcb\xa2\xb3\xc2)N@\xc8f\xb6<\x1a\xc4|\xecO\xf0\xe2\xa7xA\x07G\xb6\xbd\x8ai\"\x11\xbd\xc7u\xeb\xab-\x93\xd8\xa6\x16F\x8a\xe6\xbc6:\x08\xca\xaa +\x04\x04E\xc5F\x91\xe9\x99\xe6a\xabY\xf2\x85\x07C\xec\xbamm\xeaO\x06\x1e\xc7\x04;\xfb\xe2\xe5\x8bw\x8d\xc5?\xb4\\Q\xd5No\xb1\xcb\xb2E\x12\x9f\x83P\x05n\x119w\xdf\xf0Y>\xe5 \xeb\xdde}\x96\x81\x1b\x90\x9e\xc4`>c\xc5V\xc9fy\x82*[\x90 \x05\xdfH\xe3\x9b\x17sT\xaf\x81\xd8g\xe5\xa7)j\xe2DZ\"[\x0e\xd2\xb2\x19\x8f]\xc69\xca5\xf8\xc5*\x0c\xa6A\x16^\x16\x0bf\xc1U\xfb\xd8\xe0\x80\xbd\xab'\x81\xfe-\x8a\xc1B\xb0h\x15\xba!\x1a\x9e\xc5\xd1\xdd\x8c\x9d\xfbQ&:\x91\xf2\x8c\xf9\xd2\x01\x81X'\xa0\xbf\x93\xbd\xc2\x8eL\xfd\x08\x0c?\x80\xb9\x91\x86\x83,\x9ek-7\xb9\x96\x11\xd3\x1f -\x10\xad^\xdc{\xfd\xe6\xd5\xa7\x87\xc7_\xbd\xfc\xcd\x97\xaf~\xfc\xf2\xf8\xd9\xf3w/^\xbd<\xee\xb1>\xfb\xd2\xcf\x16\x83\xc4\x8ff\xf1\xd2q+\xa1\xcd\xb5\xe0\x9e{\xee ]\x85A\xe6\xf4z*\x80o\xe3\xe7k\x93\xdb\x15\xbd\x10\xb5\xe8\xed\x86\x01>\xdd\x00K@\xbb\xbfJ\xe2\x13\xf1\x1ed\x0b\xe63\x1c6|v\xc0>\x83 \x12\xcb5\x8b\xd9\xc2\x8ff!z\x99P\x98\xce\xfa\xec.\x8b\x13\x16g\x0b\x9e0\x1f\xd6 \x88\x18z\x08\xe1Ozh\xd6\xb5\xf2\xd1<\x8a_\x82\x8d\xd54\x06/\xa3 X\x96\x06g\x80:\x85yO\x81q\x1a\x9aM\xf3$\x01\xa3\x03\xc0)\x81\x1c~t\xc9\xf2\xe8}\x14\x9fG\xea\xbb\x1e\xcb\xa3\x90\xa7)\x0b\xb2\x1a\x12\x07\x11;_\x04\xd3\x05\xde \xa4>PAZ\x8f%\xfc\xd4Of\xd0X\x8c+\x06\xbf!\xc1\xd2\x0d\xcd\xd1\xa9\x86\xc0\xd9\x13D\xd9\xc1]\x8b&\x86\xd0\xfe95\xd3\xa0\xca\x01\xd3(\x0e\xc2\xf1\x06\xfa\xddEo)\x96\x87\xd83\x0b\x9d\xa4\xd2`\xc6\xb2\x12\x14\xc9\x80\x8f\xb2\xf8*/\xbd\xbc\x88\xceb4\xdcz\xed'>\x84u\xff\xb2\xf0\xb1\x9b\x15\xac\x84\xf4\xf4@\x124\xf0\x16$\xb6\xae]\x97\xd8\xbbD\xd6\x83]#+(\xb2\xf6\\\xf2X\xeb[\x95\xba\xd2v\xa4\xb2\xfey\xf3\xfa\xb7\x1e\xc0\xb5\x05_\x1bj\xa2\xe6\xd8[\x0bd\xb1^\x8d\x82\xff/1\xe9\x15\xbds\x04\xe5%\xa61P3L\xcdU\xf0}\xcf\x15E\x9c\xed\x8e\x9f\x82\x1a\x89\xa6\x0e\xb5\x1b\x81\xa4\xb9\xa5'\xbb\xb7Y\x9cp6\x8b9zc^\xf8g\x1c%\xf3\xc1L\xc9\x1c\x06\xecK\xff=g\xf2*//#\x8c\x94J\x85\xfa\xe6\x1b\xa4\xday\xf7|\x11\xa7\x1c\xa7&\x05\x99\xb0l7\x1d\x10\xc1k}I'\x0b\x14s\x0d\xed\x13\xba\x0d-\xb6\x84\x17\x19\xaaM\x07A\xaa^\xf5\xb8.\x85\xbbd\x1f$\xd8A\x8aB\x91\xe2\\\x9e\xd5\xa2\xa2\xa8\xc1e18&\x88*\x81\xdf^,\x979\xc4\x83/\xbeZ\xdec\x9a\xc7a\x18\x9f\x07\xd1\xa9rx\x10\x80S\xaa\xbb\xac\xcf\x02T\x1a\xdc\xedy\xacw\x17eL\x83\xbb\xe6\xd8\xe1\xc0%f\xef-\xff\x19(#\xf0\\\xe8\x0e\xe6A\x98\xf1\xa4\xe5\xa8 \xc7\xbba\xdc\xdf\xaa\x1da\xeaZ)Y/\xd7e\xc0\x07\xac\xa7]\x19\x04\x81\x04^\x94,J\x1d\xb0\x9e\xf2\xeb\xd0c\xa3\xe2G\xc0S\x14\x97\xe1\xc0ss\xe0l\x1e\xe7\x118\xa5\xbe\xab&E\x03\x7f\x16\xb3y\x10\x15a\x83\x04\\Q\xf0\xaf\xe4_\x853 \xbcC.\xc5\x1a\x0dp\xd6\xef>\x96\x9dD\xff\x13'\\J\xeaf\x83\xbbuw\xca\xb7\xbf\x1b\xde\x1aE\xf3\xd6\"\x0euo\x9c]tH\xa4d\x13UH\xa0\x1a\x12X\xaed\xa7\x97+)\x0bEQ\xe7\xad\xc8?\xeb\x02(M\xb6y+\x13\xa4W\xacB\xab\xa0\xd0b\xd7\xae\x07\x00/\xe7\xa9:#]>\x199\x8fP\xc4\xfd\xe8\xa1[\xedy\xe4<\xd8\xdb\xead\xe0Y\x1e\xa1\x87\x86\xafC\xe9l\xf0\x91\xeb\xf4\x8a\xd8\xe0\xa4\xad\xf3\xde\x96\xc5\x8a;r\x86\x0f\\\x8d\x8a\xaeq*\xb0\x1d\x084ER6\x8e\xd1c\xad\x16\xbb\x1c\xee\x14@4\x81:\xcdJ\x1c]~\xd7 \xc0\xcdV\x86\xf7~\xe2\xfc\xca\xf6\xd6\xd5Q\xea~\xe2\xfc\xd4?\xf3\xd3i\x12\xac\xb2\xab\x99\x9f\xf9\xee\xbd`i\xc2\xf2\xde\xf8'G\x17\xdb[\x9bG\x17{\x87\x93{\xa7\xf5\"\x01\xb69\xfe\xc9h\xd2wG\xf7N\x97\xe6qk\xdc\x1b\x08Bt\xaf7\xa1\xe1]\x05h\xeaGA\x16|\xc3\xbfJ\xc26a\xd5\x99\xb4\xb5\xf1\xe4\x8e!\xaf\x95\x89cA\x8fRKw\x12\x10j\x05\xfd\x010\xec\xaf\xe6\x0e\x1foM\\\xf6\x94m\x12\xee\x97\x9d\xdc\x95&\xe7N\x04\x12\xc0\xa5\x9fM\x17N\xe0\x8ad4\xd9\x11\x873\x96\x0c2\x9ef\xe8\xb6\xa4\xe7\x9f\xc4y6: \xfd\xe8\xbd\xd86r\xb8\x1d\xae'V\xbe\xb3\xa6\x15e\xb9<\x1e\xd8\xec\xff\x1f\x0e]#\xdci\xc3f\n.\xa2\x07Y\xfcE|\xce\x93\xe7~\xca\x1dpG\x02\xfa\xa3\x03&\x90\x94\x8d\x0c\x1f\x1f\x96\xe5\x15\xaf7\x84]\xca\x9e>r\xb6\x1f\xda\x96\xaf}z\x95\xb0\xdbI\x1c\xeeVG\xb3\xe6\x1a+\xbb\xb7W\x17]|/\xa6\xe4`H\xdelF\xde\x0d$g\xff\xbf1y1\xc7\xf5 \x8e\xba\xd9\x8cw\x03t!d\xb9\x96\xe5\xb8\xbe\xa2)\x84\x13\xeb\xc1r\xa3g\x8f\xf2\xaf\x0b\xcb\xea\x9aCh\x96\xf5\x80\xc5\x03\x19\x94@\x814F\x12\x18 \xd1\x90\xe2y\xa34\x93\xa8\x0e\x96\x91hd\x91\x0d\xa6\x0b?y\x969[\x16%L*\xcb'N\xe4\xb1\xa1\xb2P\x82\x08!\xd9 \x0d\x83)w\x1a\"\xb0\xe4c>\x01\xc5wU\xd8\x7fm\xda\xbb\xfd\xb0\x1d\xc4\xf6cl\x0c;\x9a\x14\xdf\x93\x98T,2\xe9\x02\xea\x80\xc5\x82w\xf7\xd8\x06\x98\x01D\xec\xe9>\x8b\x95Ux\xf1\xa9\xeb\x8e\xe6\xc1^\x9d l\xc1\xbb\x9b\xd0g\x8e\x08\x02\x97\xb4\x92\xf6\xc5b\xe3h[\xbf\xc4Ks\xb65>\xa1\x10\xb97>:\xcag\x0f\xb7\xb66\xc5\xff\xf9|^\xbf\xf4\x96\xa8B[;Xhkgw~t\x94\xcf\xf96\xfc\x9c\xf3m\xf1s{k\x06?\xb7\xb7\xcc&\xe0\xc6\x00|fg:\xc6\xcf\x9c\xd8>\x07\x86~\xe3\x9f\xb4t\n.\xf49\x07#\xbd\xd1\x19\xdf\x85\xe2\xb3\xf9|\xe2\xfe|\xfb\x03y\xc5Oo\xf7d>\x9f@\xc2\xd4\xfe\xa1T~\xa8\x08\xe1sU\x84\x01r\xc5[\xef\xa0V!T\x9f\x99\xf3-\x8e\xff\xe6\x93\x03\x15\xe1\xc9\x91\x9d\xde\xde\xda\x9a\xc9V\xc7\x18\x93)\x9f\xc8\x95~\x85A\xe2\\k\x1b=\xf7\x93\xfaY`\xaa\xf5r\x1c\xa8\xae\x1e\xf4\xf0\x1a<(\x08\xa3z\xfb\xb5~\xcf\xd9\xbe\x0c\x8c\xe0\xc0\xe8\x9c\x83\xfdr\xa40\xe8)F\x8a\xec\x9d\xf6\xae\xbb&\xb8\xe4*\xe7p_t<\xb9\xee2\xde~hc\x08m\xcb\x98\xf2%/G\xdb\x1b\xdf\xfdo\xbf\xf3\xbb\x93\xde\x8dF\xd6\xbc\x9d\xa8\xdd\xdd \x1c\xb1o\x14,\xbe\x0f,\xbe\x0b\xce\x1ez\xbd\x1b\xdd9\xd2h\x9c\x058\x06\x0b\n\x87\x9e\xf1\xd1\xc5T\x1c\x8bf\xbbG\x17\xb3\x87\x9bG\x17\xf3\xdd\xa3\x8b9\xbc\xcc\x8f\xf2\xad\xa1X\x19\xf9\xd6po>\xb9w\xda\x00\xc2u\xc9\xc3M`\xed\x80\xd0\x1a\xa4\x82 \xa9U\xd0\x0c<\x96\xd4a{} \xdew\x9d\xea\xd7{\x7f\xf8;\xbd\x11\xeb=\xab\xad\x9b\xde\x1f\xfe1:\xf9\x8f\xd3\xc9\x7f\x82N\xfe\x1f\xe8\xe4?I'\xffC\x91\xec\x1b\xc9\xff\x88N\xfe\xc7t\xf2?\xa1\x93\xff)\x9d\xfc\xcf\xe8\xe4?-\x92\x9f\x1b\xc9\xff\\$O\x8d\xe4\xbf\"\x92\xeb\xde\xf1{\x7f\xf8\xefD\xf2\xccH\xfe3\"\xb9\xee;\xbe\xf7\x87\x7f\x96N\xfest\xf2\x9f\xa7\x93\xffg\x91\xcc\x8d\xe4\xff\x85N\xfe\x17t\xf2\xbf\xa4\x93\xff\x82H~a$\xffE:\xf9/\xd1\xc9\x7f\x99N\xfeW\"90\x92\xff5\x9d\xfco\xe8\xe4\x7fK'\xffU\x91\xfc\xd2H\xfe\xf7\"92\x92\xffG\x91\xfc\xcaH\xfe\x9f\xe8\xe4\xbfF'\xffu:\xf9o\xd0\xc9\x7f\x8bN\xfe\x0f\"96\x92\xff#\x9d\xfc\xbf\xd2\xc9\xff\x1b\x9d\xfc\xbf\xd3\xc9\xff\x89N\xfe]\x91\xfc\x95\x91\xfc\xb7\xe9\xe4\xbfC'\xff]:\xf9\xff\x14\xc9\xb9\x91\xfc\x7f\xd1\xc9\xff\x99N\xfe/t\xf2\xdf\x13\xc9\xf5\xd8\x01\xbd?\xfc}\x91|i$\xff\x01\x9d\xfc\xa7D\xf23s9\xfc\x9eH\xf7\xcd\xf4\xbf/\xd2\xdf-\x8c\xf4\xff*\xd233\xfd\x1f\x88\xf44\xad\xa7\x7fK\x93\xe5oi\xfa\xfb-Mh\xbf\x05\"n\x90\xb7o\xff\x04\x9d\xfc'\xe9d\x80\x80A\x0c\xbf\xfd3t\xf2\x9f\xa3\x93\xff\x02\x9d\x0c\x84\xd6\xa0\xa8\xdf\xfeY:\xf9\xcf\xd3\xc9\x7f\x91N\x06\x12d\x90\xe5oij\xfd-P&\x83Z\x7f\xfbW\xe9d \x13\x06\xfd\xfd\xf6\xaf\xd1\xc9\x7f\x83N\xfe[t\xf2\xdf\xa6\x93\x81\x04\x19\xf8\xf6\xed_\xa7\x93\xff&\x9d\xfc\xbbt\xf2\xdf\xa1\x93a\xcd\xfe\x9a\x91\xfc\xf7\xe9\xe4\x7fH'\xffc:\x19\x16\xe7\xa9\x91\xfc\x0f\xe8\xe4\x7fD'\xff\x13:\x196\xfb_7\x92\x7f\x8fN\x06\x1e\xc0X\x98\xdf\xfes:\x19\xb6Xc\x07\xfb\xf6_\xd0\xc9\xff\x8aN\xfe7t\xf2\xbf\xa3\x93a\xfb66\xb6o\xff%\x9dLo\x9a\xdf\xd2\xbb\xe3\xb7\xff\x9eN\x86\xed\xe47\x8cd\xd8N~j$\xc3v\xf2\x9bF\xf2\xff!\x92\xdf\x1b\xc9\xff\x89N\x86\x9d\xe0\x0b#\xf9?\xd3\xc9\xbfO'\xff\x01\x99\xfc\xdd\x1f\xa3K\xc3.\x13\x1a\xc9\xff\x85N\xfe\xafd\xf2w\xbfC'\xffq:\x19H\xaf\xc1\x8d|\xf7'\xe9\xe4?M'\xff9:\x196\x01\x83\xa5\xf9\xeeO\xd1\xc9\x7f\x86N\xfe\xf3t2\xd0o\x83I\xf9\xee/\xd1\xc9\x7f\x85N\x06Bm\xf0\x17\xdf\xfde:\xf9\xaf\xd2\xc9@c\xdf\x18\xc9\x7f\x83N\xfe[t2P\xcd\xc4H\xfe\x9bt\xf2\xef\xd2\xc9@\xa8\xdf\x1a\xc9\x7f\x97N\xfe\xfbt\xf2?\xa4\x93\x81\"\x1b\\\xc1w\x7f\x8fN\xfe\x07t\xf2?\xa2\x93\x81\"\xbf3\x92\xff)\x9d\xfc{t2\x90\xde\xccH\xfegt\xf2?\xa7\x93\x81\x98\x1aL\xe1w\xff\x82N\xfeWt\xf2\xbf\xa1\x93\xff\x1d\x9d\xfc\x1f\xe8d\xa0\xb1\x06\x0b\xf9\xdd\xbf\xa4\x93\xff5\x9d\xfco\xe9\xe4\x7fO'\xffG:\x19H\xef\x8f\x8dd \xbd\xe7F2\x90^\x83\xc7\xfd\x0eH\xaf\xc1\xcc~\xf7\x9f\xe8\xd2@z\x7f\xdbH\xfe\xcft\xf2\xef\xd3\xc9@L\xbf1\x92\xff\x0b\x9d\xfc_\xc9\xe4oav^\x98\x1b\x0f\xc0*0v\x9e\xef\xf0\xb8fp.\xdf\x01\xb3\x14\x9b\xe9\xc0X\xde5\xc9\x1b\xec\x1bi\xa9\xd9\xb5)Hi\x8f>\xd7\x16rw\x12\xb0\x11\xce\xd4F`\xa3[\xa9p\x03\xc9Z=\xf6\xa3\x12;R\x96\xdf\x84\xc4M\x9am?l\xf7\xbcG\xabT\n\x0b\xc5}\xd0+x\xba\xea\x04u\xf4\xfa\xc0AA%\xd5\x10~\xa9\x86\x80\x00T(\x87\xcd\xba\xc9a)\xb5\x01\x18Tlmm\x1e]l\xcf\x8f.v\xfc\xcd\xa3\x8b\xfb[G\x17\x0fN6\x8f.v\xb7\x8e.\xf6\xc4\xcb\xde|\xd2\xbfw]%\xa3\xeadt\x93N\xfa\x9b\xdfL\xc6\xcf6\x7f{r\x05\x7f\x7f\xbe\xed}\x80\xb4\xab\xf1\xd6\xe6\xa3\x89x\xc5L\xf9\x02\xa9W\xe3\x9f\xe0\xcf\xad\xcdGlr\xef\x9a\xdd\x8f\xd0Pb-\xb5O\xa1\x939:\xba\xf0\xa7GG\x17'\xc3\xa3\xa3\x8b\xd9\xde\xd1\xd1\xc5\\\xfc\x01\x01\xab\x008B\x1c@\x8e0\x07\xa0#\xd4\x8f.NP\xe0\xba%\x05\xae\xbbsvt\x94\x89\xea'GG\xa2\xae\xbf\x05r\xd9\xf9\xfc\xe8(::J\xa0\xd0\xf6C\xfc\xf7\xe8\xe8(\x1f\xee>\x14%\x86\x0fA\xf9 \x1a\xc2\x7fC\xfc\xb7\x8d\xffv\xf0\xdf}\xfc\xf7\x00\xff\xed\xe2\xbf=\xfc\x87mn=\xc2\x7f>~\x01;\xf7@\xfc\xdb\xd9\xda\xda\xaa\x11\x18\xd46\xf5X\x9fE\xac\xcfz\x16M\xd2\xac\xdf3\x17\x1cH\xa1\xb7\xf7\xe4\xb0\xf7Nh\xa5\x91\x98j\x01\xd4\xb9\x80\xd4|\xf7\x08\xa5\xddG\x17\xa6\xea''5Q\xaak\xa0\x18\xa9}\xd0\xda\xf4\xb3\xcd\xdf>BA;H\xdaQ\xd4~t1\xe36u\xd3\x1az\xad\xf0Zz-\xd0\x18\x8d;\xf7k\xae)\x98\xfcB\x0d\x96S\x8a\xa4\x95Vt\xda\\t&\x8b\xae\xa9>\xb8\xb2\xa9\x12\xdd\xba2naU\xc6\xcd,\xca8R\xf5\xc8R\x8f\x85\x9d\xf4s3Z?wV\xd1\xcf\xd1\xed\x89\xbc\xda}\xcbe\xa9b\x19OQ\xa3\xa7\xe0\xdf\x17`\x03\xc5\x95s0\x9a]\x85\xe1\xd5\xf2*\xe1W\xe9Uvu\xc6]\xf7@\xaa\xef\xc6\x89\xc7\xa6\x1e\xeb\xfd\xb0g\xaa\xff\xd8\xcah\xe8\xb3\xab/\xbe\xb8\xfa\xf2\xea\xcd\xe1\xd5\xdb\xabwW?:\xac5\xc4\xfalnk\xac\xec\xdf\xbcK\xffT\x8d\xb6\xcf\xf79\xc0\x1d\xeb\x87\xd7\xa6\xec\x1b\xce\x06\xd8t \xea\xa6l\x10\xc0\x14\x97\x1d\xb0\x15\x18A#\xe3\xef\x17\x0eG\xd9Z\xa8S\xdc\xb5~d\xbdk}o\xfc\x93\xc1\xa4\xff\xc3{\x03~\xc1\xa7N,z\x10\xc35\xb1\xf2m\xf0\xe2\xf0\xf8\xf5\x9bW\xef^\x81\x91~\x0f\xac\xb8{\xe8\xc8\xd1I\x93\xa9{<\x1c\xa0E\xd3\x88\xf5z\xd7\x85\xc4F >\x18@`\xd6k\x8c\x14\x91~\xcf\x1d\xf7\x8e\x8f\xa7q\xc27\x7f\x9a\x1e\xa7\x0b?\xe1\xb3\xe3c\x9b\x95\xfdu\xa5\nv\xdf6\xed2\x83\xf6s[7\xb0\xa9\xad\x01\x88\xcb\xc2\x87\xcd\xe3\xce\x1de\xde[!JcN{\x05)\xe9\xd2\xe6>\xcb\xd8\x01\x1b\xb2\x11l\xda\xd7\x05\xbf\xa0\x9e\xc4 \xeb\xf88\x8cg~\xba8\x16{\xfdqqg\xe8\xf8\x988v\xb5\xb8OX\x17\xb9*PR\xf0\xa8\x02#\x983\xc7pZ\xcc\xb4\xf3sf\xc0\x8fULN\xf7\xd1\xa6\xb4\x98\xee\xa6@J\xb2VPx\x15\x86\x95.\xbeP\xd8\xfd\xde.\xf0\xbf\x7fx\x16\xc6\xe7\x07\xd5+>0\xc4X\x1b\xf8\xed\x0e\xb4\x01\xcb\xda\x06\xd9\xe4=\xacu\x9c\xe5\"\xeaW\x17#rdC\x8fEb\xe8\xfbh\x8d\xaf\x89\xd82i\x9d\x9c!\x83pS\x02\xd1\xc6\x96\x8c'\xb7\xc4\x88\x0cw(\xf6\x18\x83\xd7h\xcc\xd8*\x0c\xa6\xbc\x0d\xf2\x9d\xd0\x8bf}\x13D\"rN6\x9c\x88=A\xc7\x11N\x04\x9e\xa0\xd4\xd5\xd4M6\x14\xebm\xb0\x8a\xd1WD\x89\x8f`\x1e\xef\xb1\xcd\xcd\x02H\x1e\xdb\xba\xd6\x9e[@\xe9\x174z\x1c\xbb.\xba\x1dG\x93\xf1\xb0m\x0b\xba\xd5\xa1\x146\xaa\xd5\xb1\x08rW\xb91\xf6\x11\xba\xd2u5\x9b\x80\x8d\x01\xb0\x91\x15\xb0\xb1\x04\xac\xd3\xefkH\x12a\xec\xd0\xb1\xf8\xf0\xc4\x85\x08P\xe3X\xc0[F9j_\xdb\x0d\xc3\xddn\x1d\xae\x0d\x89\x12\x15\xf9\xcd\x95G+\xdb-\xa1\xebr\x01\xad\x14\xc9\x8e\xdf\xd2S\x1d\xd9\x9d\x1e\x9e\xe8\xd1\x81\x1b\xf0\x9bQ\xbe<\xe1\x89\x96\x90\x02\xe7\xa9%\x9c\xc4q\xc8}\xe9\xf4M\xf0\xa6\xc7\xc7@\x89\x8e\x8f{2\x10\xc0Hs\xce\xf7}\xceFe\x1d\xc0d\x9c\xf2\x0eb\xfc\x8f\xdc\x07\xdc\xa1>f\x1f\x1a\x16a\xd9\x0fz\x05F\x80\x8c4e\x03\xc1\x034\xeeU7\xdeHnk\xc8\x8a\xc9\x8d\xf7fK\x8f\xb6{7\xae\x8eI\xe5\xdc\xfdV\x90X\xa6\xa5(\x80{\x10\xe9u\xef\xac\xe2w\x9d\xbcI\x06\x8e/b's\xa9\xfa\xaa\x8dT\x11\xb8\x1d\xa2\x05&o\xaa\x05\xe0{(j\xec\xbb\xfe\xc8q\xa4N>\xe6\x13\xb8|\x90wu3k\xa6\x9cI\x8f\xbc\xbc\x00\x87\x95\xf3\x0ea'a\x07,\x1f\xa7\xc0C\x87\x82\xc1\x0c F\x9a\xb1\x1bH\x03w\x87\xf5[ \xf2\x02\x84!`AL\xd8~\xd4*A\xb2\x12\xc6\xd8F\xa3\x87\x15&\xe6\xce\x1d\x96\x8d\xb7&\xe3\xed \xde\x19\x14\xef[\x82\xbd\x13/\xc3\x89\xd8\x82\x8ao5\xdd`\x8e\xa4\x13Q\x88\xb6\x16QAB\xaf\x0d\xb5\xa1qwF]\x8d\xa3\xa064%U\xdbm0\xc4\xaf\x0bd#\x80\x99\x02\x1d\x91n4\x8d\xe1\x0b\x04K\xcd\xe4)\xdbg\x1b\xb9y8,\xce\xf4\x85\xdf\x98\x8dZ\xfc\n\x10\xb0\xf2\x8a\xc7\x03\x96nnZ\xa5\xabs\xd1\xbdqjq}=\x85`\xa18\xbbs\xc1G\xc0\x166\x9e\x8f\xb7&\x02\xb97\x1c\xf1\x06b\x92\xd2\x93\xcdFS\xac\x0f\xe8\xdec\xd6\xef\xa7\xec \x0b\xad\xbdZ\xb1}\xe6\xa8\xae\xb9V\xe7i3\x10\x0d\xaf,\xb9\x0b1IV\xaf\xde\xc5\xd0l\x04\xa5\xe6\x90\x04B\xdco8\xab\xe6\xd1\x8aG\xc6}\xb7\xd3\xbe3\x86Q)\x1bBQ\xe7.\x94\\\xb2}\x96;3\x8f-<\xb6\xc2U\xe1\xb13\x0b\xc5\x04\xba\xabwy f\x12\x0b\x8f\xcd<\x16\xb0+y_\xeeL,\xcae\xf3\x08\x1afP\xd5\xba\xc1\xa1\xad\xf5\xeai}J\xea\x07HT\xd1\xacu\x86\xbc\x01\x8b\xd8~\x04\xca:\xf3\xb5\xa2\xac\xe4\xd5o\xbd\xc3\xfa\xc7T\x7f\xbb\xf1x\xb7\xf4\xad\x9b\xf2r\x16\x8d\xe0C\xea~\x9fH\xaf\x97\x07b\xbd\xd5\xead\xa1\xeb\xa9\x8c \xbfLy\xd9\x8a\xe7ft1\xa6\xb1G\x91\xa5\x15V\xf0Gb\xab+\xdcT=a>\xdbd\xc3bM\xe6\x95\x83\\\x15\xd3\xfb\xfdH\xa2\x90H5\x9b7\xc6!\x17L\xe0\xe4\x1d\\M[\xf8Z\xc5\xd6\xde\x90\x93\xb5n\xc5u1\x9ade\xb7\xa9x\xa7\"\x9d\xd2\x1c \x14\xaa\xab?Sl\xbf\xaeq\x08ew\xea\xcdL%\xdfTO\x9f\x9b\x9c\xc1J\x0f\xac\xfaLy\xf0\xac\x9b\x97\xcc\xaa\xa5\x12\xff\xb2^b\xa1\x97\xc0M\xbb^\xe4\xec\xe6\xc2S\xc5\xa2,=v\xea\xb1K\n\xffO\x04+\xe2PG\xa1c\xc8\xc9\x88\x9cs\xb6\xcfN\xd8\x01\x9b\xb1\x11\xcb\xc9\xba\x87l\x9f\x1d\x17%\xa86.\xc4^/\x1a:\x17\x9c\xcd\x8a\x1d\xb0\x05\x1b\xb1sW\xfc\"8\xa6\xb7\xa2\xb8h\xf5P/~h+\xfe\\5|h.\xe7\xe7bK\x0fA\xd7e\xaedX\xa5!\x9cb\x8a\x8d\xd2\\l'\xe0+\xc5\x83A42>\xc5\xf76.\x8a\x06/A*x\xa964\xd7c'\"e\x8a\"\xdb\x98\x98\xb5\x11\x0bd\xeay%\xc3\x1c\xdb\x86\x13\xb1;lN\x0eM\xcc\xf6{\xb6\xcf.@\x0c\\\xb8\x96\xe9\x1d\x1f\x9f'\xfej\x05\x82jb\xa2\xc4\xf3\x8c\xed\xb3\xb7Z\xb5\xac^\x8d&w\xef\xc5\xb8\x9e5\x9d\x07_\xb1}\xf6\x9e\x1d0>\x00Wr \x11mp\x9a\xfe\x9a\xed\xb3g >-\x8bg4[d\x05\xf6\xa9\xf3\xcac\xaf\x15\x1c/\xdb|^\xd3l\xd0\x06L\xaac\xb6\xee\x9b\xd3w\xfd\xad\xd1\xd8\xea\xe4\xc1o\x9b6\x96\xd9\xdd\x1ev\xf5\xe3zv\xcbf\x1du.M\xb7\xef\x80\x02\xfel\xe6\x80w\xe1\x1a0\xc4\xe3k\xf4\xcd\x9f\xcd\xc0\xabP\x99\"\xb6D4\xca\xf0\x0d\xfb\x8b\xa0jj\xe1\x93\xf0\xad\x037\xba\x99\xae\xa6\x13O$w\xd3\xc8\xed\xb4s~\x9f\x8cX\xfb\xb7\xec\xbae\x00\xbb\x93\xb5}\xc2\x8a\xd06/I\x86\xb9\x93d\xf5\xb6(7\x17\x14\xdf\x90K\xfc\xafo\xf8\xa9L\xaf\xb7\x13\x9a\x1b\xbb\xe0\x01\xb6\xcd\xed\xbf\xd8\xa3?E o}\x93\xae\xf0\x03\x9f\xf9\x99aiZa\x05\xc0\xa3e#+\xf0\xa5\xbf\xa2\xf8\x00-\xd8\xfb\xf2\x84\x1bM,\xf5\"h\x97R/r\xaa\x17y\xcb\x0dn\xe3\xb2\x92\x0f\x12\xf0z\x91\x93J\x11\x10\x81\xd7\x8b\x1c\x1b\x8c\xcf\xa7\xf9|nv\xf8\xbc\x066\xffG\x01?\xaf\x17:,\x9c\xaa\x15\xeb\xde\xe2\x9b\xea\x02\x18\x83\x03v\x88\xfb\xc2\xabyg\xd7k\x8aX'\x1e;\xf4\xd8[\x8f=\xaf\xe3~z\x1e\x80\x0f4R\x8e\x05q\xdc\xceGF:\x93; \x1f\x9c\\f\xfc\x0bd\xf77\xc41P\xfb}u\xc50\xff\xd5|\x9e\xf2\xac\xcc\xc7\xdf\x8d\x1c\x88x8x\xa3:\x01\x00{\xd2\x1b \xfe2\xcbCG\x8f\xe9\x8e\x16:\xcb\xb6\xden\xbcu\x04u\x8f1\x18\x0c\xbce\xaeKl\xfe\xf0\xb5\xb9\xf95H_Y\xd2\xcf\x1a{\x178}\xee\xb1>%y\x86\xda\xb3\xc6\xda|\x10\x81Oq1&x\x03O+K\xe53\x1c\xc2\x9d\xe0\x0fK\xf3KK\xa7/\x9b?\x8b\xfa\xa0~\xc5(\xa9R\x7fA\xd7W\xbcZn\xa9vj\xaf\xf6\x0c5\xfd,\xb4\x8b\x8b\x80/sD\xfb)x{\x85\xb3\xde\x86\x12R\x00\xbb\xfa\xac\x15\xfb\x14\xfb\xf6\\\n\x1b\xec\x9f{U\xb4\xf5\n\xe0aa\xd8\xd8\xd5>\x9bz\xecyy\x14\xb5\x7f\xf858\xb4{\x0f\x88\xf8\x1eC\x15\x94\x0b\xb8\x91!|^\nm<\xf6\xda\x02\xde\x13\xfb\x8a.\xf9\xf8\x0b\xe55P\x0cJ\xfe\xb0J\xaf\x99\xb6\xce\xda\x94\xcf\xed[\xf4\xba\xec\x9c\x0c\xe1\x04\xd3K\xcb\xaa\xb8\x195\x82\n\xa5\x0e\x0d\x8e\xfb\xfdl\xc2\xf6\xc1\x86\x9e\xd7\xee\xa2\xb9\x1fC\xc4\xf5q\x86\xd786\xbe\xf6\xb0\xecv\xb3\x8f(\xf1\xc7\xd0\xe4xn\xe9\xb0\x8f\xf2\xde\x94\x02\"\x08@\xd8\x1d\x16\x9bp\x9c\x82f\x8e:\xcb\x0b6hJ\xf2\xffb=\xcc\x05\xe1H\x9c\xcc\xd5tC\x1b\xa1\x95z\x14\xd1\x8a\x04\xe34\x7f\xccV\x0dJ\n\xc1:M\xc7+\x8b$\x7f\xc3 A\xc0\x00^\x9aG\x9aA\xdb\xcc\xed\xa8\x95\x10\xdfX\x80\x190E\xc1\xc47`4\xa9\x0c\x87R4\xba \xa8\x98\x12\xf0o\xd4\xbc\xab\xa6\xba`-U\xf1P\xea\xdf*\xa0\"\x18\xb9P\x1c\x9eV\xec \x9b[!s\n\x1a\x10\x05\x1f\x8b\"\xe4\x12,\x07g\x16\xf0\xf9n!\xfe \xe1B\xe5%\x1cWg\x80E\x1c\xf0g\xc4|G\x9c`!\x15\xd1+\xb5)~u\x05\xc4 ;\x10=\xdc\xdf\xc7\xd3w.\x1bA\xd4\x84vO\xecJb\x90\xa8\xd0\x14\xfc$\xe1\xfe{#\xc7T\xe1.a{\x03\x9exZ\x1a\x92\x83m\xc6\xac\x89>\x83\xea\x07\xf0wi\x03\xfc1\xb0\\Z\xab4\xe8\xcf\x81\x17\xd3\x8a\x99\x03:\x16\xeb\xe6\\|\xad\xda\xc9@F\xec0R3\xd4D\x91\x01\x06\x8fE\xde\xb1.\xa6\x86\x14\xb2,|\xf3\\/{\x8eF\xdf\x08\xfa\x0e\x1bX\xaao\xa1\xc5\x0f\x81\xe0g?\xa8V\\\x9f\xf4\x13\x87\xcfJ|\xc7\xcd!F\x83\xb5 (\xd0\xdc|\x0b\x03>\x8e'b)E\xec K\xacK\xc9\x87\xa5T\x8fZ(\x9e\xcc\xf1\x01i\xd1\xac\xd9 \xc6q\xbf\x0f\xb1\x0e;\x80(\xf8\xde\x00\xa1\xa23\xaa\x91\xf2\xc7.K0(cf\x04'\x91\xbdKZzg7E\xa0\x05\xf9\xf7\xa9\xfb\xe2\x94\x94\xbcm\x0b\xb3\xc8\x1dbiZ\x9eHf\xeb\xc6\xd0\xb5|\xa7\x953[\x170C\xcbMz\x03`>\x84)-\xc1\xe3\x8f\x0b\xf0}\x1e\xc6~\xb6\xb3-\xb5\x08\x80\x80\xb5\xcc\xdd\xfbt\xe6\x8b({h\xcd\x19\xeeZ\xb3l\x1f\xfb*\xb06\x08Y\xcfC\x7f\xb9\xe23{ \xdb7E^\xe5\xa3\x1b[\x9e\x9e\xafaP\xad&\xdd^E\xf0P\xcb+\xe48\xb5\xf4R\x08afp#Q\nr\xea\xb3!q\xc5\xc8\x00\xa9N-MIrj\xc9J\x17TKVB\x9dZ2\x08r\xeaiRxSK\xfe1\xf7\xdf\x17\xfd\xd8\x18z\xeb-\xc1@.\xc1\xd8\xe1E\x94&\xb1\x1fm\xf8c\xb1*o`\xdaK\xfb\xa0\xd85\xac\xdfn\x81C\xae\x8f\x0dc5\xe9\xf1\x98L\xfb'u\xf6\x18O,,[$6\xe7\xc2\xec\xc6\xd5\x9c\xf6G\xae\xb9\x91o\x00\x03~\x87e\xa8\xea\xb5\x10\xe86\xcb\xd7\x86\xb3\xc6\x9e\xebh\x81\xb6<\xd93\x8b\xe9\x05}\xfd\xc8N\xe5v\\\x07\xae8y\xac\xa7\xd6\x8b\xed\xe2\xd9\x0d\x9a~\x9d\xc4\xcb \xe5\x1f\xa1\xe5\xb7<\xfb\x08\xad\xca\x95uK-o\x1b\x97v\xe5\x8aX\xdf\xc0\xb3\x12\x856.B8gE\x00\xda\xa8\xe1\xf4\x15\xc0\xf1!\xb2\x1c.\x90m\n(\xb6 \x99\x0f\xe9\x06\x96\x95\xd2E0\xcf\x9c\x06D\xd5.\xfe\x03k\xd1\xb64E\xf9\xc0\x89\x8b\xbd\xcb\xde\xb2x\x00\xf8q\xc3\xa2\xa2)-\x99\x8aS\xe1$\xec\xa9\xf4%\xa6\xf6\xbc\x91\xd8\xc0Y\x9f9\xd2\xc8\xfd\x80\xf5\x9e\xdc\x13TM\xfe\xee\xb3\xde\xd3\x9e^Jn\xa0\x82\xa1\x8aD\xe9\xa3Hf\x83\xa6\x10\xe4\xa0\xd4\xc2\xb3\xcfb`\xdf\xc2\xd4)kC\xc7\x138J\x96\xbf\x07\xfej\xc5#\xf0\xef\xe0\xe9\xf84\xc0\xc4\xb8\x92\xa8\xcc\x18\x9c\x0dq\x06\xdd\xd8\xeaB\"\xe0N\x06br\x01\xb5*\xbc4pi\x80*W\xbf2s=`=\x86e\xb5\x072\x0e\xd6\xabN/\x8a3\xe6\xa7ip\x1a\xf1\x19\xcbb\xe6\xb3\x95\x9f\xf0(\xdb\xa0\xf8\x07\xf5\x9ci\xfe\x91\xe8^\xaa\xa7\xf4H\xa3 f\xec\x0d\xe7\x8e\xd6[IT#\xaf\xd2\x02\x8a\x80\xfa\x82\xc1P\x94\xd6\xf5\x9agE\x7f\x14{\xe9P\xbc\xa2zlT\xca\xc2f\x08\x9a\xd7uJ\xb4\x0d\x17\x0d<\xc4\xd0\xe0\x84\xcb\x95\xd7\x1d\xc1\xe7\xaa\x1c\xd1\xd3\xce$\xd3*\xfa\xac]d+~}pK\xc7\xc3\xce\x83\x07\xf2\x80\xdd$\xe8W\xdbyu\x80\xbd;\xbd\x11\xeb\xdd\xf1\x97\xab\xc75\xa2x\xb7wW\xe4\xfc,\x8f\xb3zV\xef.VZ\xc5\xa9\x91\xf5\x04\xb2B\xb3\xceS\xc88\xcd\x1ek\xc1\xfa\xda\x04\xe3\x16\xa9\xb8$^\x92\xb2\x01\xf1*\xc4=\xce\xf8N\xef\xc9\xd3\xbb\x18c\xa1U\xd8\xa6\x04\xccFP>\xe0\xd9\xca\x8e\x92\xd0\xad\x91G}\x08\xf1\xe3\n\xdc\xa5\x19\xc1\xa3\x1dwpx\xc6\xa3\xecp\x19d\x19O(o\x1f\xe6A:\x913\xbd\x08\x0cu\xb5x\"\xe7\xe1\xd0ub\x0f\xfc\x97\xc4\x837%\xc5\x14_\xbc\x0f\x89?N\x82\xacH\xdc\xdd}\x00\x89\x9f\xe5\xab\x90_\xc8\xa4]Hz\x97\xf8Q:\x8f\x93\xa5L\xdd\x83\xd4\xd7~\x9a\xbe[$q~\xba\x90\xe9\x0f!\x1de\xe2x\xb0\x8bu\x97\x1f\xc1\x8a\xb7\xe97\xce4\xdf]6\xc9yL\x9fF\xf9\xe0\\\x0d\x07U \xb8\xd5\x88D.j\x80\xd5\xd8\xca\xcfS\xae\xbd\x1a\xc7&\xfa\x93\x01I\x85\xa2r\x1f\x82\x16\x13\x9e\xe6\xcb\xca{\xe3\xa9,\x1a\xc4Q\xc1\x92\xc5`,\x08 \x89\x1fD=\x8f\x05\x90r\x1c\xa4o\xb3Y\x00r\xfcL\x1b\x18\x1e\x9e\xc1\x119\xd4\x12l\x9c\xc7r`\x88\xc4od\xdb<\x96\xd6\xa5xg\xd2Ztch\x83oN\x0e\xd6\x87\x8f\xf9r\xc7\xe5H\xc7\xbaA/\xed\xd0 y\xa9\x8d\x0ff<\xcd\x92\xf8\x12\x17\xb6\xfc\xd1\xf5\xb3!M\xb7\xc5\x16:u\\OZ\x02$\x830H3\x1e\xf1\xe4\xb9\xd8\x87\xa4\x13\xe1\x1e\x17\x9bi\xcfU\xfbk\x9d\xde\xd2_\x9cZ\xd1d\x19\x9f\xf1/\xe4wjsndj\xf3oV\xd5\xe7\xb9\x9eW\xce9Y\x13F$\x98%\xea\xabz\xae\xed\xab\xd3\xc6\xafN\xc9v\xcb\xdc\x86\x95\xa0\xc8-br\xa5\x9f\xf5\x14\x1d\xdb\xa7\x06\xb6O\x8b:\xd5\x14<\xca\x08\x02\x04gL\xaf\x95\x86\xbb\x10`\xa9\x89\xac\xf7\x04!I\xb3$\x98f=\x92\xaa\xdf\x1f\xba\x03\xbc\xadDZ\x08\xec\xb6z\x9c\xaf\xe3R\x81f\x9cD\xb3\x8d\xf6m\x8d\x15\xa6\x91\x9ci7E3Wg#\xdf]\xae\xb8d%\x9f\xfb\x91\xe0&\xc5>\xc3|6\x0d\xfd4e~\xca\xfc\xe2K\xc4\xb9\xf0C\xe9\x86\x1b\x19\x9e\x05\xf7g\xd2LK\xa6d~\x10VS\xe4y`\xdf\xea\\\x99i\xbb\xbc\xe9E\xaa\x99QS\xbc\xad\xe5h\xe9g\xbe\xd5;Y\xc4/2\x94G\x99\xe34y3}(O\xc1\x16\xa9\x18.\x88}@Q>\xaa@%\xab\x82$\xf3\x98\x8c\x01\x80\xcdT\xa1\xe1U\xc6\x9eG \xfc\xfe\xf8\xc3/\xfa\xdb\x05\x062\x06\x89\x06 \x10\x06\xebc\xac!\xc6:c6Fl#\xf0R\x00V\xb6\xdat`\xe5\xeaH#z4\x10\x10\xa1\xcf3\x12\x01\x87\xc6\x10\x0f\xaa\x03\xaa\xe1x}\xca\x8b/ \xf0\x16\x91A\x949\x05a\xce\xde\x04\x11\x15\xf5\xae\x11\"M\xbdkY\x81\xd5\xaf\xfd4\x0e\xda\x1d\xb8#\xfc\xf7\xeb\xf0\x97\xd0\xa3|\xe6Tn4\x15\x9d\xc5kM=\x14\xc7\xc3\xacHoH\x02n\x8f]\x16\xb1\xfe>\xe8\xc03\xcb\x9c\xd1f\"5\xf8\xc5\xd1\xd4o_D\xcdcJ\x06~\x18\xc6Sg\xcbb\x8an`LQ\xb3\x0d\xedJ\xc8\xc0\xb19F\xb3)\xf9\xbd\xaf\xa2\xd4\x9fs\x87\xb3\xa7O\x9f\x82x\xd2\xaf\x82/\x17\xd3\xf9\x98\xf9\x8f]\x00\x9c\x0f\xdf@\xa8\x06x\xa3>\xf7@\x97\xb6\xbaD\x9b\x1fQ\xa5\xaf\nV\x0c||\x04\xba\x0d\xc4\x81\x01\xe2\"\xe1\x83`\xb5d\xf4\xb7 JW|\x9aU~\x0c\xa6y\x9a\xc5K \x13\xa5t\xa6\x98\xa0q\xbd\xe0\xa4 \xd9\xd5j.*\x11r5\x1c\xd6\x88YI\x8e\xe5\xf2\xa6(\xae]\xfa,to\xa0/\xd2\xc6k=rw6H\xa2\xb6\xef\xea\xeeN+nH\x8eD=\xb0\xefC0\xcb\x17\xcb%\x9f\x05~f\x95jH\x05\x0d\x1a\x19I\xbf3\xe6}7\xfd \xe1\xa2\xbb=\x7f\xda\xa0\x9baRw\xc3\x07\xb3x\n\x922{\xb9Uitt\xca\xb3\xd7\nI^\x81R\x83\xcc\xb0\xba\xb0\x12M\xad\xc0\x92D\xc0\xe4]\xb0\xe4q\x9e\xc9\xe8\x88\xdc+\xfd\x1c\xac\x92x\xca\xd3t\xd2\x835\xfc\xf3\x0fEpIy!x \x0b\xa0\xb1m\x1b\x1dQ\x8f\xa6\x07j\xa4\xdc\xfa\xb3p\x88\x0b_\xea\xb1 \xb8\xd8HG\x9d\xa6O\x80\x12u\xb0\x8a\xd3\xecK\xe9@M\x9c6\xf9 X\x8a%\xf9v\x9a\x04\xab\xccj\xef\xa3\x1eE\xc47\xb6\x9a\xa5\x88LJ\x12\x05\xb3nu\xd1\xa6?\x05\xf3W\x94o\xdb\xf4\xeaOF\xeb\x10\xf4\x07\xf7\x86\x12\x02N\xaf\xe7\xb1\xde'=y\xaa(?\x1c\xd5o\xd9UZ\xa1g\xc2qA\"%\x9b~\xbe\xf0\xa3\x88\x838\xdb\x01{J~\xce\xaaY\xee@\xc0}H\x0f\xb8\x11\xb9\x16\x0e\x07\nn\x93y\xae\x81\xa7\x01tb\xbb\x02\x14\x0b\x16\x82l\x0c\x16b/\x8e\x12\xee\xcf.\xd3\xcc\xcf\xf8t\xe1G\xa7\x1c|\xdd\xcc\x07\xd3\x84\xfb\x19\x97\xa2w\xa7\x97\x02R\xf5\x04`\xc0\x8eq^\x90\x00Yd\x9d\xae*\xd4\xb3~\xc5\x8e`\xd9\xc0\xec\xf1:\xe8%E\xbdt+\xc8d\xc5\xf2d\xfc|\x11\x8430s\xced\x9e\x1d\x8fD-\x94m\xabZv\xc0w\x87SI\xed\x9c\x85\xc7\xb6\x8c\x1bF\xea\x11\xa4\x03\xc43=}\xcf\xf8\xa1\xd8\xed\xe0\x16P\xe2G\xb3x\xe9\xc8@\xb5\xc8m\x14=h4a\xcc\x06i\x9c'S.ob\x08\x8c\xd1\x83sI\x1b\xa5\x812\xe9\x93|\x172%A4\xe3\x17\xaf\xe6\x8e\x0f\x02\xbd\x85\xd3\x97\xe9\xa0pq\x14\xd3b3q\x14\xeb\xd8\x9f\xcd@\xd8\xaad\x14\xb0*\xeb\x89NO.\xba\x1el\x7f\x1bC\x10\xfc\x0e\xfc,\xf3\xa7\x0b(\xe9\xf4\x8a\x85)\x052Ig\x00T\x89\x8c/XX\xa43\x96\xf9\xf5p\x93*&\xa1\xf3\\kR\xb5\x8d\x9a\x19/\x97DGy7q\x80\xd1\xe6MF\x7f\x156\xbd48.\x14\\\xea\x10\xb1 \x11\x0f#\xe4>#\xf6DwM\xd0\xef\xbb\xca\x97@Qo\x0c\xaaA\x8b\xdd>\xd3\xec\xbe\x9aW\xa1\xd8\x8fO\xfc\xe9\xfbF_\xe3\xe2\xf1\x93\xd3\x942\xb8S\x0fq\xacU\x8f\xdc\x86\xc2q:A\x01w\xe2\xa4\xae\xc7\xd2~\xdf\x86p+<\xa2\xe9sG\x1c\xa4\x1b\x8c\x08f\x0d\x16%\x18\x947\xac\xdfhd-M6\x18\xa9\x80t\xd4\xa5\x88\x04\x0d\x94\x86\xe88L#\xca!\x19\xebV=p\x85\xad\x8d\xc8N ?|\xf5'K.;p\x02\x1b\x1dW\x8f\xfe\xa8\x81\xa0RW\xa0Y;\x83\xa3\x9e\x04\xea \xack\xee\xbdz\x94\x91u\xd2\"\xbb\xa0\x1e0\xbc\xde\xb2\x1b\xdfRO\xa3\x01%\xf5\xb4\x98i\xd7\x1f\xe8\xd3p\xdd>%\xe3-\xeajw\xd3s\x9d~m_\xa7_\x1eK\xc6\xc3\xef\xa3w;\xd7\xef\x9d\xf8\xbb\xfd\x91\xfb\xd8j\xebM=\xa0\xb0\x0fA\xe4@\xd8{P\x0f\xcdQWJ\xd8\x98\xa3\xa2\x00\x9b\x07\x91\x1f\x86]\xe8\xc3\x0c\xd8\xb9i\x87\xf3\x825\xb7\xab\xe1oM\xb6\xe7\xf4\x8a\x98\x05:/\x94\xf2p^^aW\xf7W\xb3E\x90\xc2\x0d\xd7\x11\x14\xd0\x94\xc0\xba\x11\xc0\x0e\xec\xc5v[\x80\xee\xd7\xa2\x8a\xed\xc3B6\xed\xc4\x17\xadV\x06a<\xf5\xc3\xb7Y\x9c\xf8\xa7\xbc9\xe6\xda\xd4\x07\x02\xd8\xe6\x15\xa45\xda\x19\xd3U\xca\x95\xef7\xc6^\x97>#\xc0\x9c\xac\x97%9\xc7\xc3?\x9e\xfb\x9d\xc8\x1dd\xf1\x17\xf19O\x9e\xfb\x84\x06Y\xff\xd5\xf9^\x1fS\x97a\x9c^\x14\x7f\xc6W \x9f\x82\xe9ZO\xbb\x97g\xf6Wi\x9b(\xd7\xaa\xf5\x9b\x82M\x1b\xfe\x06ycS/\x119=\xd0\x10\xd5\xbaV7>\xb29\xf7f`\x90\xd0\xcb\x12\x7f\xca+M\xb0\x036\x8d\xa34\x0e\xf9\x002\x1d\xf0w\xa4\x92\xce\xfd$B7\xe0\xb0\xf7w\\SL\x17\x17 \xa9\xc9@%UZb\xb5\xadC\xebR\xea\xb4\x86hA\\\xc5\xf9N\x99\\j\x0cw\x86\x96+\xe5[\xbbd\x00\x98\xc0\\\x1f\xa8\xdc\x03\xc2\xa0\xe9\xf7\x82\x12\x890v\x98\xe1N\xbb4%!\x02\xe8\x8b'\x1e\x04\xd1\x82'A&\x1d\xc1\x0c\xc1\xd2C\xa59\x01\x9a\x99\x04\x9a`\xfd8\xd3\x8cF\x9a\xa0\xc5\x007\xf0\x94\xdc\xea/\xa4\xc1\xb6&r\x86\x8f\x1et\x9a\x9fj\xad\xdd\xebT\x1a>\xba\xef\x96f1\xd7\xac\xaf\x19\xd0ti\xa1M\xe3\xbc3\xa4\x02\xe8\x8bt\x8bK\x82\xbd\xf6[\xea\xf5\x89\x92\xaa\x08\xbc\xac]\x1e\xe0\x0c^H\xa2\x9b?\x88\xe2d\xe9\x87\xc17<\x81k\xa9\xa0\x96s2\xed\x8678.+\x95\x0d\xa5G\x0c\x7f\xe0\xa7\x97\xd1\xd4E\xcf\x04\xfe`\x95\x04\xcb \x0b\xce\xc4\xd6\xa7\x8c`\xd8A\xf5\x13p\xb1z\x0b\x0e\xeb\x19\\\xb3\xc0\xaaF\x89m\x17<\x7f\x8f\xea\xb5\xb5vE\xb1\x1d\x17bQU\x13\xf70Q\xbc>\x84f\x8a\xae\x82\xe5\x8f\xb3\xb7\xf5\xc8\x95Q\x8d\x96\x8146r\xf6\x86\xa0\x9f\x19\xcc\x82t\x15\x97\x89\xbb\x90\xb8\xf4/\x9e\x9d\x16i{*M&lc\xcd\x84\xcf\xc1@\x85'*}[\xac8\x81(\xfe\x9a\xab\xa6\x0d\x91v\xf7(D\x02\xa1\x8f\x7f\x92\x9a\xa8\x049\xf30\xd6\x1dbwC'\xa5>J_\xfa/\xd1_\x05\xba\xe8\x00,\x11Get\xa7\nN?\xee\xdcaA\xfay\x10\x05\xe0\xa2\x1a\x1c\x0dq\xf0\xf2\xe1\xc4\xd2\xdfP\x9bQG'0\xd4\x88\xc3\xde\xb6\x0b\x82[\x18c\x1a\x9cF0\xf5\xbb{;\x9d\x88F\xfb'\xac\xfb\xb3Re\x15\x1f&\x17\x18m6\x05h/\x0d\xe0\x9c!z\xa5\xdbT\xbf7\xb7\xb7\xd6u\xe7\xb1\xc60\xec\xb6\x99\xdadz\xe5\x8c\x03Q\xd0=\xb2pi:\x81>pn\xa3\x9f%b?\xa0\xbd\xd2\x0e\xef\xd7\xfd\xdaH\x02Y\xf7\x98$\x03V\xee\xd1\x01+\x05\x9dm\x86\x0e\xe3\xb4\xb3\x81\x08oCUgX\xec\xe5\xe8\x10\x03n^I\x97\n\x15\x9a\xebjtG\xd1\x1b\xc2\"\xfc\xd5J|\x1d\xf3 l\xe8\xca\x9f\xf4\xb4\xe6\xce\xa8\xe5\xcc\x9bbEt\xd8z\xa0\xda =6\xf7X4\xe6\x13\x88\xe9\x81Nx\xc8K\xe5\xb6\xe3\xea\xad\xe0\xf2\xae%\x16\xe0\xce\x90\xf6K9\xbco\x89 \xfcp\xcf\x1d,y\xb6\x88g)Ejw\x0d\xff\xc0\xa9\xe4\xec\xeaG\xa8\x90^\x0cp,\xac\x96\x9cv]6\xf3re\xa0\xa6\xb1\x9a\xad\xd9(\xa0(G\x12\xcb\x80\xd7\x86\x82!1\xe3\x9a\xdf\x80\x05\xa4\xf2e\x90uXX\xc4Q\n\xec\xbb=vVD*\xf5\xd8\x89\xc7\x8e!\xc8\xec\xa1\xc7.0\x9a\x96\xc7\xde{\xec\x99\xc7^y\x10tk\x0e\xe7/\x9a\xe2c\x00\x11y\xa1\x14i\xb9\xdc\xbd\x0b\xf14\xee\xd6\\#\xe8\x1aW-\x10\xff\x02\x9cu\xea\xc9\xae\x07Qq.\x06\xa7<\xf3 \xf2\xcd\xc5 \x15\xaf\x97\xf0\x8a\x9a\x0d\x0f\x02\xd9\\\xa0\x06\xc5\xf5J\xc1\xcc \xe1i\x1c\x9e\xf1$\x85\xe6_\xc9\xad\xa5H\x15\x8b\xfa\x19SA\xf3\xed\"-Vn\xc0\xd2\xb4\xaa\xa0 &\xf9\x10\x1b\xf2+\xf8\x1e\xf8\xbeq\x02\xb7\xec\xd2>n\xd2K\x91\x08\x8aIb\x9b|-f\xab8\x89C\xe0]_Z&\x9f\xf2\xac\x07\xab6@s<\xd7c\xaf\xc9\xe8%\xa2\x0f\xe8tO\xf0LAi\x808-\xe8 \x9e\xe2\x83\xf1\xd6DP\x80\xb0\x9e\xae\xfa\xbc\x8f\x9e\xa1\xecB!bd\x8a\xb7H\x9c\xde\xf3 \x99\xe6\xa1\x9f\xb0 :\x8b\xa54\xc7c\xbd\xe7/\xde<\xff\xea\x8bgo\x8e_\xbc\xfc\xd1\xab\xe7\xcf\xde\xbdx\xf5\xd2\xa6x\x17\xad\x9e:\x01!\x8bA\xa5\x92\xe8C\x03\x18o\xa9'r6^\xa3J2\xf6\xd8s}^R5/R\x89/\xf8\x90*\xfd\xf4\xd8\x99[x\x15\x14\xeb\xa3Q\xe0\x06\xc7gzV-C\xc5\xbb\x02\x8dh\xa3\xae\x13\x14\xa8[\xe2\x90\xc5\xaa\x10\xf4m:\xb2\x97xT\xc7\x97Rf\xc6F5$s=\x1b\x9a\x17\x9d\xbe\xe5IB\x93\x000\x19&\xa6\xa9\xb8C\x8eV\xad\xa6'l\xdd\x93\xfa\xed\x92\x02\xfd\x8e'lyRT\x0c\xab\xd0\n\xa6\xb8qZ\xe3*5\xa0\xfc\xda\xc12\xbd)5h\xe8\xdc-O\xdf8\x16k,\"'/V\xf3\x16U\x82\xf21\\c>\xa9\xfc\x8f\x93\xe04\x88\xfc\x90T\xf8+n}\xc4\x9e\x99\x99\x92\xd5\x7f \xde\x83`\xb7W?\xcd\xb2\xa7<\xebr\x15T\x0e\xf2U\xc1\xe8\xbdr\xb8\x0b\xbb\xdc\x01[\xa2\xb3\x07\x89\x14\\L\x86I\xf5\xcc//\xfct\x8d/[\xe6\x91r\x12o~\n\xf7\xdb._\xb3\x900\x86\xfd\xa5{\xc00\xaa\xfa\x9d;\xec\x12-\xa5\xd8>{\x0d\xbc\xaa\xb4`\xc0\x1f\xefu\xb4\xc0\x9c\x1e\x86\xa8\xa3\x1cE\x99\x83\x006a\xd4\xae\xf2P\xa2\x15\"N(\x83\x80\xc8w\xee\xb0\x13q\xe6\xd3X#\xaf\xe8\x18|\xa5\xd7\x15\xb0q4j?\xb52M\xa0#\x16\x7f!\x10y\x0bz\x0f6\x02\x1b\xac2\xf9y\x91,\xa1TZRA\xfcW\xf0\xe41\xab\x08\xf5i\xdf\x15f\x7f\xc5\x18Glaf\x14\x87\xe1\x0e\x00\xe6\xc8\xd9\xca\xe5i~\xb6\xbe\xbc\x8fMV\xcd~\x95\x05-\x8b\x1a\x883.A8\xe5\xe1\xf1\xae\xe4d2\xe0d\"\xe4\xd1\xfc2\xc6]\xbdC\xeb\xec\xe9\x85\xa8[\xb6&7\xbfj\x93\xacmi\x11\xe4\xa3\xdcTp\x17\xf1\xcb\x00}\xf5\xfe\x9e\x83\x14\xbd\x95\xf5\xe0\xad\xb0\x93\xdd(\x87.\xf7\xdc\x91\xda\xef4\xb0r9k\x02\xa0%u\x8b\xb0\xb3bE\x9b\x82\x97\xc3\x8f\xd6O\x1f\x82\xd8K\xd8\x93\xdd-\xb1\xa0\xa1\xe3\x1210\xe6\xbe\xd9\xff\x95\xf3\xcc#\xfa\xac\x0b\xbfF,\x00\xd7UV\x12\x1b8\xc7D\xae\xa4]\x81\xe3\xab\xd3\x8e\xf9\x15\xd8\x89\x02\xe7\x9c\xca\x83\xbd\"p\x0e\xcd>\xfbE\xca\xad\x1c\xf1w\x86T \x10q$\xb7h\x99\xea\xe2-\xb1\x97\x83`r0\xf5WY\x9e\xf0\xb7\x99?}\xff.\xf1\xa7\x9a(\xa9\xe2\xab\xa3U#\x15I{D\x94wR\xd1n\xf3\x8aphH\x88\x90\xd2\x9a\x90\x89<\x0b\x07N*\xddm\xe5\xb8\xa9I\x8f\xa4\xca\xa9=hdR\x19\xd50\xc2\x9b\xb8\x81*\x1b\x0d\xa6\xf1L\xe0^\x0eWu \x08D\x84\x8c\xea\x9a\x0e\xa8\xd7\x90\xc7\x93j\x05\xdc\x81\xa5\x90\x02}\x85t\xd7.H\xf7n\x0e\xed\x15e\x1e\xc7#\xd6K\xfcozu\x1ae\x96=\x11\x18\xdf\x9b\x9d\xfb\x1d\xcaf\xc97\x97#\xd6\x13\xffz\x06\x8a\xf3\xc1<\x8eY\x9f\xf1\xc1\x89\x9f\xc0\x7fQ\x0eh\x83\xe8\xca\xec\xdc\x87z\xb7,\xb8\xdd5\xa2B5Hn\xd7\x08\x9c`\xd1\x10\x94\x17q\x02\xc3\xe4\xd6c\xdb5\xbe\x1blu\xb9.\xe9\x04n\xb4b\xa4M\x8a\x1a\xedV<|\x9c@\xfc\xd1qBX\x9b\xb6\x9a\xecD\xe8\xac@\xac\xebV\xf3\x0bd\xf8\x87\x8f\x99\xcf\x9e\xb0\xf41\xeb\xf7}y\x85\xadX\xa0\xfe\xc4\xc3\xf8\xd4\xca=Q\xee\x9a\xea\x13\xcd5KT\xe8EHL\xff\x18\xaa\xc3\x87CT\x1dj\"vT\x1e>\xdc\xfe\xd8\xcaCz\x12\x15\x8f\xa1\xf9\x96\xed\x15Z\xf5\x1ex[\xac\xceC\xe3\xa4\xd26X\xb7-P\xa6\x94#\xda\x00\xda\x96S\xbd\xe3\xb2\xd31x\xc3-\xe6\x06\x8fg\xeb\x1a\x9f\\\xab\xef\x04\xc5\x94\x9f\x18\x91\x97\xa6\xf0\x16\xda\xc8\x98\x9ak\x0e\x1c\x86}\xe7\x0e\x8b\xc7J11\x11\xebr\xdd\x10\xb9\xed\xa8)\xd0\xfc\x01\xe2\xbf\xbc.W\xb9s\x9b\xf9A\xa4V\xc3\xee\x0dV\x83\x82\xb6N\xe6\xd7\\+M{]R\xf6Ulz\x1b\xcae\x88Ju`\xf7R\xbe\xeb\xeby\xf38\xee\xdd\x8e\xaa]\x0d\xd3\x00\xa5\xbc\x0es]l\xa8\x1d\x11+\xcae\xf6\xf46\xf5\xef\xb5\xeb\xa4\x9er\xc8N\xe9\x80\xe6\xb4^t\xd5Y\x953\xeb\xaa\xcaY4\xabr\xce,\xaa\x9c\xda\xe7\x96]5>\xa7\xed\xc1n\xab\x15.I\x8a1\x8d\xa3yp\x9a\x83\xf6\x95\xa6\x1a\xbc\xd0\xce\xd2\xae\xaf\x95\xa7\xa4&\xba\x92\x1b\xdf\x164*i\xe3V\x98\xe2X\xac\x87\xb69\x185\x9c\xea\xb8\xd7;>\xe6\x1c\x0c\x07\x0e4\x07s\x90&\xcer\"\xe9rp\xe6\x87\xb9\xe0h\x16J\"sV\xab\xed\xb1K\xd7\xd3\n\xcab\xd1\x98O\xd8\x01\xe5t]\xe6\x88\x7f\xe8\xb1\x0d\xacO!u\x9f\x8dQ\x9b\x9aM\xca$\xe9\xad\xa3\n\xb1\x1a\x8d\x8f\xa6|\x04\x94\xbe\x1b\x94<\xdd'\x98z*\x80\x8a\x95[>c\xb9F]\xee(J5u\x8c5\xe0*\x992\xdah\xb7\x8a\x05\x07;\x02\xba\xaf\xa2i\xe1\xd4\xe7\xf8\xb8#(\xe6\xf3\x11\xf0\xbe]!!\x89\x04-\xe7F`l\xd0hS\xf1\xa7@\xd7\x97q\x80J\xc4r\xc7|\xd2\xa1\x9e\x896\xe8`T\xd46!\xc6\x14\xeb\x1d\xe0\xed71y\xc98\x98\x08\x1e6pY\\\xfa\xe5\x8d)\xb8b\xae`\x94\xb7\x95s*%\xd2\x97(\x98\x8c\x03i%7\x14\x88\x99\x0c\xd2\x15\xdc|\x0c<6\xa4\xee\xee\x81*-)?\x9b4~V\x8ac\xa3&\xeb\xf8\xb6iG \xa2\xdfzG\xf1\xac\xf0j\xd18\xef\x16:!\xb6\xe3\xb8:\xa1\xf6\x19\xa1\xe7\xb1\xd9\x19<\xccbD(\xc9d\xac6-\xde\n\xdew\xcc\xf0\xc8\x92\xb1',\x12\xd3\x9d\xb9,\x18g\"\xb3z\xd91k\xb8\x08\x07\x1f\x8d\xc1\x81\x05^h\x95\xedn=\x06\xc2\x1b\x8b\xca\xd8\xb4\\\xc5I\xa9\xc9!\x1b\x95\xbaTu\xa3\xac>\x96&\x00t\xb9\xb55+\x88\x0b\xe8\xa9\xec\x03c\xedw\x8b\xba\xdc\xc6\xaa~\xaf\xc6\xb0\xdc\xfc\xeb-\xb7\xad\x9a\xbe\xeeU\x84G7\xebK\xa7[U\xbf\x10\xfc\x14\xcf\xaa\x06\x05\x1b\xe6\xfd\x80\xfe\xf5\x81\xf2\xc6,8\x8b\xa9S\x17z\xe2^:u\xe2z\xba\xd8X\xa6N\xe0R\x84g\xea\xe8\xe6\xd0hG\xb8t~\xfe\x01\x85q:{\xdc\xec\xf5G\x19\x8bi\xa1*\x17N\x88\xce\x88\x8bSc5T\xa4\xc72e\xb4\xc4\xf6Y\xfe\x03vS\x8eY\x9e\xa3\xea\xb1~\x1b\x04\xab\x04\xdb,\xf88\xd2=q\xf9\xbdf\xe7\x01\x1a\xdd\x1f,\xfdU\xbb#hU\x81\x1d\xb0\xcc\xe1\xe3\x08T\xcf\xe2\x7f\x15%\\\xe9|\xc9\xc9+Zi\xf3\n\xff\x07o\xbdc\x0d\xc8\xbd@\xe0\xd516O O\xc5\xbe\xa1Zq\x05\xd7u\x12D\xb3\xf6P\xb6\xddg\x16\x8f=\x8f(S9\x9c\xa8 \x85\xff\xd7<\xd5\xc5(\xda\xe0\x10\xce\xfdv\xba\xdd\xe9 \xadD\xcb\xc8\x98\xe2H\xe6I\\\x0b\xc8\xd5t\xdcF\xff\xed\xe0]\x00\xe6p\x0c\x82d\x0fe\xc4\x13\xd7c\x9f\xc6q\xc8\xfd\xc8\x01V&+}.C\x01\xd4\x05\x81]\xf4m\x8cY\x13\xe4<\xdav\x07A\xc6\x13?\x8big\x8e\xc6\\\xca%\xfa\xc8fAN\x1a\x90\x1bK7\xa5\xe5\xc9!\xbd\xfe\xa7\xf2\x9bur1\xaf\xe3U\xa7c\xb5yX\x9e\xdd\xc6a\x94\xc8\xd7\x0f\xa3f.\x1c\xe6\x08\x1f\x8c\x1f\xac'\xf9\xeaQ}\xddET\xb2\xa5V\x13\xcaV]\xd2\xdbF]\x128Z*%\xf3)J\xe6C\xe7B\x06\x08\xbf\x90\x0e\x12\x99t\x19\x0eh\x0e\x13'R\x02\xf4\xf8\xec\x16\xbe\xf2\xaa\x8d[\xfc1\xc0 \xe8\xc2zF\x9c3y\x89F\xaeN4\xf7tN\xb5\x10\xc5\x82\xa4 \x16\xc9\xdb\xdb\xf2\xc2\x9e8\x9f;\xcb\n\xc71t!b\xd9>\xe3p\x19}i\xe1\x86\xf0T'\xbe\xda\xc2\x85W[\xaft\xaa\xe2f\xe4T\xb05\x91\xcb\x96h\xcc\xc7I\x0bJ\xf5\xc8\x91.\xc9\x02\xe6\xa5R3e !\x03\x7f`/\x040\x9f\x1bzdf*'\x9cs\xe8n2\xb1\xc2\x02\xe0p\x02f\xae\xe7\xf2J*\x1a\xd2\x08\x82\xa9\xe0#\x0e\xc8\xe2l~\x02\xce\xc5\x9c\x128\x1b\xc7\x83Y\x1c\xf1\xc7.(\xe0/\xd8\x81b\xe2\xd0\x1a\xf8\x18%&\xd2\x90\xbd\xf8%\xf6ogVHS\x0e=\xb6p\x96\xb02fp\xddJ\x82\xf9\xb0\xfe\xd1~\xdf\x125K\xcc\x1c\x11\"\xa84\xf7\x9c6`\x03@\xe0\xb4\x123\xdb\x1c=\x8c\xd7\x03\xb9]\x0d'\x0e%B\xc8Py\"GZ%\xed\xb3\xc3\xc1t\xe1'\xcf\xe3\x19\x7f\x969[\xae\xcb\x9e\xee\xb3\x07\x0f\xb6\x1f\xed\x82\xc5\x12{\xb2\xcf\x1e\xec\xee\x0c\x1fA\xf9Cp:9\xee\xf7\xa3\x89\xb4g0\xc0y(\xedG\x0e\xad <+Ax&A\xd8\xef\x9f\xd9\x81v\xd6\x82\x8e\x1a:\x89=\xf0\xd4D\xb8\x02z\xbe\xa3\xad\x9d\x1a\x00\x9dS\x97^P\xe40%4\x15o\xd7\x1d_H~\x00\xbb2\xab\xc8\xee<\xb6,/\x89B\x8c\x90\xa2\xe6\x0d\xf6\xf5\x9a\x96\xe2\xd1\x8e\xd4R\\.O\xe2\x10U\x12\x8f\xee\xdf\x82J\xa2v\xc2)\xf48\xb5-\x1e>[\x91\xc3\xb6\xe9vH\xbe\xcb\xdcb\xc8{(8J\xcd\xf9Bm\xf7`\xfb\xb2\x88\xd3\xcbx\x9a\xc9\xee\xd5\x8d:i\xf5\xa22o\xac\x9b>\xddD\x89\xa8\x97\xd9H\xc6\x95Q\x14,\xd9\x04\x953F~\x16\xbfV\xdaM(B\x95\xc0N\xbf\xf3O'\xb7\xc74\xea\xba\x0e\x8b\x8aC!_\xfdZL\xd8\xac\x90\x98v\xd54\xcc\xbbi.V\x84B\xc2d\xfa\xc2\xfa\xed\x90\x1az\xed\x1b\xe8U;\x97\x14X\xb5\x06\x1a%\x8e+=\xda6i\xa5\xeb\xeaf&\xe7`\x81\x9b\x80\xb3(\xbb\xef50}57\xbb \x92\xc0\xc5\x98c\xac?\x8c\xa1q-wF\xe3\xca)\xb4z\x98\x8f\xbb\\\x8f5\x89[\xbd\xb3\xfc\xd6:\xeb\xc3\xcdrP\x04\x01\xf4CG\xf3j!\xc5h\xda^\x0b\x01\x1a{\xa5\x15\xa1\xe0B\xa6ND[ \xce8\xfa\xa2\x0c\xe2\xe8\xf8x\xc4r\xf0/\x9aQ\xe6|\xc7\x91\xbf\xe4e\x993\xa7n\x02\xfd\xa1*\x1f\x99:q\xfd\x93\xf38\x11\xd5\x9b\xb1L\x0ez\x86\x8a0\xf87\xc2\x7f\xfb,v\n\x8anHE*\xbf\xdf\xf3\xcb\xcf\xbb|\xccb:\x0e\x8b/cA\xc4R`jgv!\xfel\x9cM\xd0\xd6\xb9\xd4\xdc4vm\xe1\xa7/$\x96(X&\xa8\x06\xd1r\xd0\xa2\xaf\xa7\xa5\x18\x01\xd3\x83\xf49\xc8\xaa\xde\xaeT\xc8\x97Zsf\x01\xd9\xaa\x99a6.\xf7\xb1z\x932Y5$\x7f\x1a\xd5\x97\x82\x1c\xd6\xeaB\x9a\xac\x08\xefF-\x19\x19\xa9VO\xc5N\xc2\x9a\xf2\x97Q7\xe5~b|\x12\x13eM\xfcaV\\\xf1i\xc0\xd3zMLUU\xf1\x17Q7\x0c2\xa3f\x18dE\xbd0\xc8\x8cZ\x1a\x0fP\xab\xab\xe5\xc8\x16\xb4\x14\xa2\x9d\x82S0\xda)r\x8av\x8a\x14\xa3\x9dW\xddS\xdfoT!\xeb\xc2_E\x95j+\xae\xd6\xb1\xd8\xde1\xfd\xcb]\xbe\xaa\xc8\xb7\x031\xdcQ\xf01\xa8\x91Q\xd6g=\xd70 \xad\xfc\x863\xc5\xaby\xd7\xaf\xa6\xb5\x98Z\xcc\x1c\xe5\xbc:\xcaXG&\xaf\x0d\xac\xea\xfa\x89\xfc\x0e-\x1e\x95\x8cw-B<8\xc8(0\xce\xd1;E\xf7\xaa@D\xe8\xd5\xb4\xe7)\x98\xf6\xb0B\xd0^!\xae8\xe3\xafp\xcct\x13UHPM\x94l\xf9M\x1cj\xe9\x02\xda\xdd\xb5=\x19\xa1\xdf3\x108P\x9c\x03\xba\xf6/\xf8\x06\xfa\x1c$'\xeb\xd6\x8dG[E\xfc\x1b\x1bx\xd9\x87D\x93\xab+\x91\xaf\xc7*\xc0\xb2o\x8b\xb2\xe0\xc6\xb4\x1e\xca\xe0\xce\x1dV-2\xae\x16\xaa\xce\xfcm\x0cYM\xa0a\x12\xa5>U]\xc6`K\x81\x12\x88.\xcb\xb8\x10\xc0V\x17\xb2\xe3\xae\x8d*Uk9\xee\x02x\xe2_,\x04\"gg\xb8}\xed\xa1\xd8\xdd\x06\xfdR\x0d\xb2\x12\xf2|\xbd\x01\xa6\x86CqX\x18\x88\xe6\xa6)\x88\xf2\xcf\xa1\x1d)\xb0o\xa2R\x0d&\xee\xedY\xcc\x9e\xe9^`\xd6\x1d*\xc1N7O\xef\x01\xb1XR\x9e\x91\xd7g\xe1\xaeQ-\xea\x9d8\x12\xd1\x91\xa4\xa0t\xe2\xf0\xc1)'.\xd3i\x01R\x07)\x071a\x06/\xfbP'\xe5\x10\x9d\\\xdenC\x15\xa0\xfa\x81%\xf0\x07\xdc9\x93\x01\x8f\xb0\x90\n~$\xca\xe0\xad)\x88\xd1\x0d\xfd\x94\x1f\xc8\xd0\xc1Dv;\x14k\x8d\x89)\x04 J\xdej\x1eb\xb5\xa0\xff\xbd\xff\xbeW\xcd\x97\x87\xa2\xfd\xf2\xd20\xc8e'\xeec\xb6\xb9\x99@D\x9f\xfe>\xeb\xfdw V\x00q4\x89 \xd9\xf77j\xb5\x19\xea\xf7%Ik\xbfB\xd8\x12\x95\xc3\xcb\xf0\xd6`\x82\xf2{A\x02\xb8\x18h\xac\xc2<\xe1@\xb3q\xbf\x9f48\xf61\xd0\xb5\xcb>Q\x8b'\x7f\xcb\x17\x18\x86\x86\n8\xae\x8b\xf8Z\x00mc\x1f ]i\x06*)3=\x82\xd3\xbc\xdd\xc5\x8beA7\x9f\xe6\x99f\xc2JwG=\x01\xd8\x8bZ\xb3}\xeb\"QOPD\xdf\xf2\x8b\x15\x13\x8c}\xb8\xba Fe\xaf%>-J\xda\x06\xc0\x14>>f1{\xc2|\xb6\xc9\x86\x8f\x9b\n3\xd9\xb0t\xa7\x07\"\"\xb9?\x04\xa0\xed\xe4\xe3x\xe2j\x0eW\xad\xdd+Z\x83.\x0e'\xa0C\xe9\xf7ckaS\x05\xa9\x1e\xf9\xad\x96>\xb1\x03\x15\x8eN~N\x81\x8fl\x97\xfe\x9a6*#\x9f\xb8M\x9eV\xd0\xc8jo)\xd0(@ao\x03\x1a\xe5\xcdh\x04\xd2\xc4\x8eh\x94\xba,\xc7\x10\x0e\xfd\xbe%\xf0PK`\x03@\x1ah\xe3\xeaJ\xbe\xec\xb3q\xe3DS+\xb3\x9ao\xcd\x9e\xc8\xab{\xe2;\xf2V\x9c\xc4\xd4M\xe9\xfc\xc3 \xcaI\xcfa\xd2c\x81\xf6h(\x1b@\xd5-i\xe4\x0e\x19\xa2\xa2\xc7\xf2\xf1P&~\xc4\xae\x17}\x1fN\xc6\x01\xe0\xb8\xff\xf8F\xfdv=\xd5\x18N\xe05\xf0WJ8\xc9p\x8b\xe6P\xd7\xf3\x8e!\xdd\xc74`\xb2\xdf\x8c\xc9\xb9\xb4/o\xc6\xf5\\\xe9\xc1\xad\xa5B\xd8\x0e:\xac\x05\xc9l\xf9\x02\xbb\xec\x8bAT\x81X\x80\xe3\xb4\x0b=\x0d4,\xedNO5\xee\xdf\x07t\xc8\xc7\x81FO\x9bIi\x88\x88\xe2\xa3\xa7&\xec\xebp2\x8e\x01\xe9\x82k\x10\xd6[\xe9Yq\x15\xb7\xe8\x8c\xa8\xaf\x0c\xf7c\x0f\x10Z\xe4U\x92\x1e\xb3\x0d(&\x15\xe0w\xee\xb0P\x117\x176\xdcp\xb0\x8aW\x8e\xeb\xe1\xa4\xc8_n\x87\x96\xd7X.\xda}\x80.\xeb\xa4\xab\x03\x16\xc9\xa7\xe8|\x89\xd9\xfc\x0f\xe8_7\xe0\xca\xaa\x9a\xff\xbd-y?\x11\xdd\xd2\x0e\xc0\xa9\x9dt\xec|\x93+\x89k1q\xfa\xb7\xd79\xca\x81\xc2\x9b;?\xff\x00\x84\x92;/\xfd\x97x\x0b\x91;;\xf7\xbf\xcf\xb3N\xc1\xf5o\xec\xdf\x8e\x1c\xac\xca:_\x13\xack\xf2\xc6u\"y\x1bl\xb1F.2\x0f,\xe1,fpU\xe6-.\xb9\xb4h\x1cwZuU&\xab\xcd\x7fh\x8642\xc1\x03W\x84\xbf\xfa}\xee~\x9c\xbdP\x93XA\x10)\xd8\xf87`\xa0x\x86\xaf\x12\xab\xa8\xf2\x9b\xa0\n\xb7Ct\x08~\xe5#\xd0\x9b\xdb<\x05\xd2B\x06\x1a\xd5#++j\xe3\xe3\x08x\x10%\x83\x1b\x1e#\xad\xbe\xaf\n\x89@\xc1:\xa1\xa142\x11\xbc\x95\x89h\xdc\xa6\xb3\xca6\xddr \xeb\xc434\xb2\x96-\xfd(\x97\xb7\xfc\x8c\xf5\x10\xd6\xba\xd2\xad\xc7\xa9\x02\x9c\xd2\x00i\x0b\xaf\xdcD\x8fY\xae\x81\xb3\xe0\xc0\xfd\xb2\xa7\xa9\xe4\xc0s\xc5\x81\x8b\xbcT\xe3\xc0surH;\x9c\x1c\x9aN\x0d\x96\x13\x03\x9c\x16R\xf8\xe8p\x02N>\xfa\xfd\xbc\x0b\xdd\xbc\xce(\\O}\x06\xce\x11\x99\xc7\x02\xb0/\x10hHxN\xee@\x0b;a8\x1es\x91\xcb\xc7\xc1\n\xb2\x14\x82\x18 \x93\xc7\xbbk\xe3<\x9e\xa1B8C\xb5\xb3\xa6)B$W\xc1\xbf\xe5)\x0d\x91\xdf_\x03\xf9eo6\x1a{\xd3rd\xc8\xf4\xcf\xe7&#\x9b\x13,r^e\x91\xd3*\x8b\x9c\x16,r^\xfe\"Xd\xb3ekO%G,f\xaa#xn\xb0e\xd9 9\xbb\xe6\xf2\xf2t\"nv\xf5\x07\xf4\xaf[\xda\x03m\xbe\xc1\xe9\xcb3;C\xfa\x82\x9b\xe9K\\\x1aY\x1a\x17_R\xdb\xcd\xb7j\xb1\xf5\\\x84[6m\x88\x16!\xe3\x18\xb4\xdcx\x97B\xd3\xb9\xc7V\x1e\xd8WN\xa5\x81\xa21\x1f\x8b\xa6\xcc3\xd0n(\xc7sf\xfe\x12\xf2\x95\x13\xc6*F\x97\xf5\xc0$\xbc\x99\x97S\x9cF\xe9_\x98\xc4\xad\x04|C\xa9\xa8\x0ep\xaf\xd4*\xa9\xa7\x9d\xad0\xe5\xb1/A3\xbb\xb4`\x9f\xb7<\xb69\x14[\xc3\x99\xbc}2/\x9c\"\xac\xc4\x9b\xa9s\xead\xb1\x1c8\x1a\x00\xd9Y\x83\xe1\xf2\x87\x1a\xf8\xe2H\xb9\xe9m\x87]\xe3\xf5v\xf2\x02%+\xcc\xdd4\x17\x05$\xcct\xc3\xbd}6\x9e\x81\xcb\x8aH\x19\xf1!u\x8f\\\xd4\xc1\x01h \xeeM= nH`\x91\x89tb%}L@\xa8|e\x93\xdfbD\xa3\x1e\xe0?\xect\x94\xf2\x15\xbb\x901\x0d`\xbf^\xa0\xf7\x8d\xd2%2\xac-\xf4\x07\x1b\xe0~%\xbd\x19'\x10M!\x8e2~\x91A,\xa6\xe44u\x0b\xfb\xcd\x04\xe3G\xc4\x88)A\x89BbNlq\xa2[I#\x86\xfb\x96k\xab\xcd\x0d\xc7\x19^\x8c\x94F\xe1\xd6E\x11\x89\xa1\xf3jd-\xe9\xffC5\xcf\xb8\x1da\x14\xff\x8c,\x05\x1f\x043\xbb\xe4O\xfa\xc2d\x8d\xf1\xfc\x01\x03q\xbb\x13\xadaOf\xe3\xb4t\xdb\x8b?\xe2R'ct>\x03W\x9a\xa9t\x80\xc8\x0e\x98\xd2\xec:\xe0P\xdcY\xa0\xe0\xdc\xde \x86\xf6lbnG\xb8\xe2\x1b\x8bbh\xe7\x06Q_\x89Ri\x89R\xa9G\xaf\xaeXF6\x88\x8b;\xc9nCI\x14\xc3\xd5/\xc7C\xf5n\xd7\x90\xf5Gk\x8c\xb7\xdc\xb4gr\\\xe8)\xdc\xc2\xb5\xa1\x087wBy\x9b\xd9\xf4\xfeB\x1d\xb6q+\xa6\xa8\x00\x97\xbc\xb4\x94\xb3\xca\xae.U\xb3\x1c\xe2\x03NOp\xc9E\xb8\x00}\xcd\x05\xf9\xb2\xc5\xfd\xcc\x07OR\xd9\xb4\x03\x95\x85\x95#I\xe1\x1adr0=\xa9Q\xca\xc1\xf4\xc4-\x0d\xa0\xc5\xcf\x02\xd7\xf1G4\x08\xc4\x96)\x9d\xef\x001e\xa3\x12\xa9\x89\xeb\xe38\x8a\xc2\x9bu\xfbvA\xb0\xeb\x14\xb1\x9c\x01\xb1\xbc\xba\x02BY\xec\x9c\x0b\xdd\xabv\x95\x84b\xa2FEU$\x19 \x98 n\xb1\xf5^\xb9\xbcn\xa7r\xa2\x0bD\xff5>\xa6\xe8\x0f4\xaa\xba\x13\x0b\x8cl_\x1d\x92\xce\xc8\x9e\xf3\xa2\xe7&\xea\x1ac)~\xde\n3k2\xad\xc8\xcc\xee\x191\x18\x03\x99^\xbf\xc4\xed\xcb\xf4\xba7]\x15K\x8c\x0epc2\xb9\x1dn\x0c\xc5N/[p\xf0\xd8/\xfe\x8fd$d\xb8X\x1fG\\\xfd/\xd2\xdd:[\xabB\x19val\xb5\x0b7\xc6\xac\xc4M\x99s\xea\xa6\x11S\xa62[\xca\xec_]\x0e\xac\x96)\x14T\x1c\xfc\xa3\n\xf2\xb3\x01\x91\x96\xe8k!w{\xac\x0f\xde\x1eX\x9f\xf5\xee*3\xcf3?\x0cfL\x0dv\x19\xcf\xb8q\xf1\x8d\"I \xee\xeb\xb65\x11Z\x02\xf4\xc2\xb0r\xc7/ES1:X\xf5\xa5\xc9\x14\xb1Q%\xf4\xe14\xc2\x8aC\x8f\xcde\x13f\x19\xd1\x95i\xabS&\xbd4`\xee\x98\xb2\xb7Q\x8f\x18BH\x04\x9c\xfb\x12yj\xce\xb8\xf8=b\x9f\xf1\x8cO3>cy\x14'3\x9e\xf0\x19\x13\x88x%\xb0\x8e\xdd)\"sC\xf8\x9e\\t\xcec\xe7\x8b`\xba`A\xc4\x002K\xff=O\x19F\x1fc3hMpC\xf1\x9c\xa5\xf9t\xca\xd3\xf4\xde\xdc\x0f\xc2<\xe1,X\xae\xe24\x0dNB\xce\x9c\xf3\x05\x8fD\x13wu\xec\xbe\x0b\x13\xeb\x1eE\xcf\xe3(\x0df\x80N\x04m3*?\x1c7\x1f\x1b\xc6 \x15\xbd\xc8\x02\x89\xb5N\x0e\x84'T\x9dc\xac\xf0\x96:\xbbh9S$k\x9d)H\x13\x97\x8fz\x8a\xa8\x8b\xa6\xa5\x90\xe0#\xe9\x89\x9b\x14\xb7JOY\x06\x90k\x06[\x86\xe7\xe3\xfa\xc5\xfc\xea\xe5\xf3\x9b\x03\x88p}\xa5NYm\x91\x96\xad\x86*\xe8\xf9\xfdV\xe7Q\x9c\xca\xd6\xbf\xbd\xd1\xe8\xa2\x1f\xaf\xe28\xe5\x15\x19p\xe8\xa6]\xfc\xd3\xa2\x895H\xad\xcd\x89\xa3\x0eC\xaf\xfd4\xe5\xb3B\x10\xa3\x05\x84\xc6K4\xc1\x9c\xcf\xea\xf1\x8cn\x17~{\x86JG\xcc\xf3\xbd\xf1Qt\x94\x1c\xe5\xdb[\xdb\x0f\xe1\xef\xa3\xc9\xbd\xd3u\xc1\xac\xd0_\xcc:\x89\xfb\x85\xc2\xe2)\x1bnm1\xe5\x80.\x93\x0eX\xb7<\xf6\xe8\x11\x1c\x13\xff\xdb\xef\xfc^O\xde\xff\xcf\xd4=iAq\x9b\x97\x8a\xfc\xcao\xbc}\xf5r\xa0\xc0y\xe9pW6?\x04\xc5Fm\x19\xdd.p\xff_\x83\x9cJ\xcf1~\x19G\x9b\xd3\x98'S<\xc6e\xb1DD\x17o\xf2N>\xea\x85\x8d\xdb\x88\x11o\xd3&\x96\xdf\x0b\x06\xb3 ]\xc5\xa6L\x85p\xa9)\xfaV\xb3\x81\x08 6\xa5\xa2\x9dg\xa7]W\xe0\xcc\x03\xa7B\x1e\xab\xf93\x05\x89#\xf8\xe4AY\x0b\xdbg+\xc5\x96.@\x89P,\xd0\xd4\xb2@\xd3\xe2\xc7\x01\xeb\xe1za#\x06\xbea\ny#\xeb\x8b\xcf\x17\x1d%\xf1u\x86\x0e\xd6R\x9e\xbd\x0b\x96<\xce\xb3\xf6sO!\x00\x8aH\xe1\n\xb7\xe9\xbb\xc4\xa7\x06y\x94\xf0\xb9\x18@\xf9\xcb\x81\x88\xa7\xe0UNt\xe6\xce\x1d\xd6\x8b\xf8E\xf6.\x98\xbe\xef\x81u\x90J\x86\x05\xa4\xba)\x12E\xc5\xf5\xfb/\x8f,\xcb\xbasa\xd9\xff3[\xff\x97\x95\xfe/\xb5\xfe\xb7hpj\xf3@.\xfb\xca\xd8f\x18\xef\xbf\xd0\x98\x8a\xb3\x15B\xc8\x80\x0c\xa7 \xa3\xd7^\x92A\x15\x05.\xf1\xcf\xb9\xd8XE\xb3g\x18\x1ct\x7f\x7f_\xcf\xb9\xba\x92Q\xdb\xcb4\xb1m\x0fvvv\xd8\x88M\x9d\xb9\x83\xa6\xe8z>\x1aGmI\xcc^\xb2}\xf6\xf3\x0f\xd2\xaf\xd6\x90m\xb23\x97}\x82\xd2M%\xaa\xa8\x03\x07t\xde9\x05\"\x18\xec\xd5\x15\x83\x01\xb2}\x0dK<\x16\xb4O\xbbE\xda!\x1e\x0d\xaa\xfb\x1aT\x1d\x0d\x84\x9e\xae\xb0\xabl\xa1h\xbb\xe6\xc4\xae\x8b\nA\x08\xe8W\xb1\xb3\x91\xc6\x03\xd2b\xae\xb2\x8c}'@Hu\x12O\x84\x1e\x0b5 \x05\xfc\xa4$\x9c\xa6\xdf\xa7\xea\x1eT\x839\xbd\x0d\xcd\xdaP\x96\xd5\xd1\x96\xdc\x8b\xd0\\I \x01bp\xec,\xbb4\\Ctn`\xb9\xe5c\x88q\xc6\xf8\x8b\xdf\xb7\xb2\x05\x1a\xbe\x98\xd5\x11\xf3\xd1\xda\\\xb3\xe0\xca\xa4\x01\x87\xd8\x0e\x9e\xb2\xb8\xc9\xb7\x08\xbf\x98r>K\xd9\xd2\xbf\x08\x96\xf9\x92\x15z\x8b\x0c\xa1\xf2}9\x1b\xd9\x1e\xde\xdf\xbb\xffpg\xf7\xfe\xde\xf5\xdbk\x07\xe76\xad\x17\xdd\xd5\xafx\x04bG\xee\xb8\x1d\xcb8R\xc4^\x9c\x14{q.\xdd\xc0Kk\xf258\xe5\xe6\x8d\xd8G\x13\x9bf\xc4\xd7\xdd\xfb\x02\x8b0X\x04\x99\xeaZ\xbb\xc1\xc0i\xf9)b\x0b\x12\xa3W^\x11\x0cr\x00\x99\xd2\x1d\xc2m K\xcb\xe46(\x9f\x83\xf6xW\xeb\xae\xb1\xb32\x044q\xf3\x01\xc2F\x9a\xc9y)\xff23\xd3\xa6\xcc\x10\xda*R\x1f\xed\x15\xa9\xc3\xedm\xb8\x0f\np\x02\x18 \n\x8e]\xae&\x02\xdcz\xff\xf7\x1f\xfc~\xafq\x1d\x9av\xef\x84\x1d\x85\x8e\xb1 \x82\xc178j{\x15D\x96a>\xabK\xb5\xea\xbe;\xd1\x05\x87\x1f\xdc\xe2\xc2N\xe4\xec\x0co\xe2\xdb\x93\xf4]/\x1a\xee\x1d\x1f\xf3\xf4\xcbx\x96\x87\xbcW\xa7\xda2T\x90\x1eJ\xc1EY\x0f\xc4\xd3k\xb2UQF\x00\x89*\xec\xb1X\xbd\x96\x1b\xd0\x07\x93\xdd\x08\x1cq\xb8}Pw\xf3\x1b\xcb\xac\xfb\xdb\x10\x95\xb3\xc8S\x1d\xc0\x90cd\x1f8\x12\x99r\x9c\xd2\xef+\xb5Ca\x9c\xc0\xba\x9f\xbe\xf5\x88\xe9/\xc7\x04\xa8}\x87&\x8b\xd3x\xb9\x8a#A\x0e)8\xa8\xe7\xd9j5b\x97\xc5\x0cZ\xcb\xf9y\xb6\x88\x93\xe0\x1b_\xf4\xe4u\xbc\xcaW#v\xd2\xbd\x1a\xff4\x8bF\xecx\x8d\n\xafV<\x81\x8fA\xcd\xf3n5\xd3\x11;l/\xf9,\xcf\x16/2\xbe\x1c\xb1\x8b\xf6\xc2\xa2\xd9C4{{\xdb^:\x16\xc5\xb7G\xecY{Q\x7f\x15\xfc&\xbf\x14}\x19\xb1\xe7\xed\xc5O\xfc4\x98b\xe9\xf7\xed\xa5\xe5\x91\xe4U{\xc908\xe3ox\xba\x8a\xa3\x94\x8f\xd8\xeb\xf6\nA4\x8fG\xec\x8f\xb4\x17|\x11\xcd\xe3\xe7\x18\xd8\x9d'#\xc6y{\x95\xdf\xc8\x97\xabw\xf1k_\x8c2\xebP>\x8e\xc2 \xe2?\xf2\xc3`\xe6gq\xf2\xa9?;\xe5#\xf6\xaeCE\x85]\xe9\x88}\xb9F\xf1\x11\xfbi{\xe9\x02u\xdf\xe6\xcb\xa5\x9f\\\x8e\xd8\xcb\xf5+} A1G\xec\xcd\xfaU\x11~\x9f\xb5W\\\x04\xa7\x8b08]d\x82\xe1\x18\xb1\x9f\xb5\xd7H$\xa6\xa4#\xf6y\xf7\xd2#\xf6M\xf7\xc2\x9f\xc6\xb3\xcb\x11\xfb\xb4\xbd\xc2\xcaO\xfc%\xcfx\x92\x8e\xd8\x8f\xd6(\xfe&>\x1f\xb1\xdfh\xaf\xc0/\xf84\xcf\xf8\x88\xfdV{\xd9\x05\xf7g\xd0\x91\xdfl/\x0bF\xb4\xe9\x88\xfdZ{Q\xb8\xc5\x17e\x82y\x1d\xb1\x1f\xb6\x97\x8f\xcfxr\x16\xf0\xf3\x11\xfb\xed\xf6\xc2\xf38\xce\xc4\xc2\x8c:,\xb4\xcf\x830\xe3\x89\xb6\x9a\x93\x0e\x95^\x0b\x88\xe3t\xc6\x1d\x8aO\xf3$\x1c\xb1\xa0C\xc9t\xba\xe0K\x81\x83~\x87\xc2o\xb1\xb0\xd6\xf7\xbcC\xade<\xe3\xe1\xe1\x85\xbf\\\x85|\xc4\xc2\x0e5\xbe\x145~\x9c\xf8\xab\x95\xf8\xc6\xb4k\x8d\xe7q\x18\xfa+\xb1F\xd2\xaeUFl\xde\xb5h:b\xab\x0ee\x0f\xa3|)\x9b\x9eu(\x8e\x8c\x8e\xac\xb0\xe8P\x01\xcc6e\xf9\xb3\x0e\xe5\x0bg\xf7\xb2\xce\xb2S\x1dd\xb8F\xec\xb4C\xe9w\xc9\xe5\x8b\xecU\x9e}\x9ag\x99 \xeb\x97\x1d\xea|\xe9'\xefg\xf1y4b\x17\x1dJ\x7f\xea\xa7\xfc\x0b\xff2\xce\xb3\x11{\xdb\xa1\xfc\x8fx\x92\n\xde*\xf1O\x97>\xae\xb7\x11;\xe9^\xf1m\xe6/W#v\xdc\xa1F\xb1a\x1c^d#\xf6\xc5z\x15\x80|~\xd5^\xe7\xb5\xa2\xb7\xf0\x91__\xa3\xc2\x8bh\x1a\xe63~\xb8\\\x89\xd9\xfcq{\xcd\xa2{\x10i\xe4\xc5\x1a\x154\xaap\xda^\xed3\xceW_\x04\xd1\xfb\x11;\xef\x00e\xc1\xff|%H\xda\x1f\x1d\xc8\xd7\xe6\xb2\x02ap\xeb\xc6\n\xeaw\x03i;;}\x96\xa6\\p\xf8\x87E\x87\xc8\xd2\x9d\xe4\xd8\xb4\x9frV;K<\xef\xa4F\x88:\xb5\xf5\x9eh\x8b\xd4\x1c\x8dg\x05\xbc\xd9\xbc|M\xcbW\xbf|\x0d\xcaW\xeal\x8az@\xf9\x8a\x87\xbb\xb0L\x88<6-\x7f\xad\xca\xd7E\xf9zV\xbe.\xd5k\xe3\x89\xf7\x15\x87\xe0\x03\x8f\xa8#/\xe6m\xef\x1a\x11\x8e\x8a\xbc\x9d\xedz\x9e_\xe4\xdd\xdf3\xa2\xe5\x14y\x0f\xef\x1b\xf1\x80\xca<\xe3\xf8\x1d\x96yF_\xa6E\xde\xa3\x9dz\xde\xbc\xcc3\xfa\xb2*\xf3\x1e\xd6\xf3fe\x9e\x01\x97\x85\xca\xbb\xbfe|\xef\xac\xcc3\xda\\\x16y\xc3\xadz\xde\xa9\xca{\xb4c\x8c\xef\xb2\xcc3\xc6pR\xe6\x19\xdf;.\xf3\x8c1\x9c\x17y\xf7\x8d\xbe\x1c\x96y\xc3z\xdeE\x99g\xcc\xfb\xdb2\xcf\x80\xcb\xf32\xcf\x98\xf7\xf7e\x9e1\xef\xcf\xca<\x03.\xaf\xca\xdaq\x07\xdc\xebv\x11G\xab6\xcd5\xd9\x1amW\xc7\xceQzs\xa8\xc5\xe8=}\x10\xa0\xad\x1a\x04D\x10\xa0\xadj3b\x1a5w\xc9\x807\xbfU5\xb2\xf5x\xfd]ugDN48\x81\x1eD\x837\xf0\x03tX7#\xd7\x12\x8e\xa3\x00X)\x8d\xb3\xdb\x87.>\xaa\xdd\x02\xb2\xaaM\xf1\xc1\xaf\xf3\x14Y\x11\x8f\x84)\xc3\xf6\xd4j\x82\x10\xaf\xb4F\xf5\x98\x06z\xc2\xff\x8c\xf9H\xf5-\\j6\xaf\xbe&\x13\xc9\xd0\x19\x14&\xc5\x1b\xd3\xd1\x0c\xc6\xc2\x82D\xff\xda\xaalar\xad\xaf\xb54\xe7\x05ab\x9b\xe7\xac5\xd6\x1a\xec\xe4Y\xe5\xae\x1d\xb1s\xdd\xc7\x01n\x96\x06\xb8\xa9\x0c\x106]\xb7_$\xa9\x86;\xb8\xbfg0\x14.\xe7\xac\xa9\xcc\xb93D|\xc1\x83\x0c\x83\x9b\xd1\x1b\x98\xa3!G\xe2\xac\xf3\x00x\xcf!\x85\x97\xb0|\x0e\xcb^\xcf\x05\x8c\xea\xbe\xec\xc3\n&p\xed\xac\xa7\xcbY\x1f\x96\x8c\x8c\xb0\xaf\x86\x10+\xe6^\x99\xf4-\x0e\xc6\xb5p\xf7\xc7A<\x87\x0e:f,\x06!\xbdM\x1d\xd7E\x0f\n\xcd\x10\x88\xb3@\x17\xadi4\xc0\xab\xe8>\xb0\x01q\x8b)Q\xa4\x19\x944b\x924}\x9f5W\xc9%\xa6\xe0\xfd7!\x1b\xd5\x8d\xcd\xc9\xc6\xb3\x9d/<\xc10{6;\xc9\xe3\xc1B\xd4\x89\x9c!\xab\xc8\xa6NyT\xeb\x07\x12\xef\xd0\x19\xed\xed!)\x15\x14\xf5\xd9\xa6 \xac[\xe2\xef\x9e\xf8\xfbTKh?p\xf3\xc46]Y\xc0\x95\x87\xcd\xec\xcb0\xbf\xb5\x88i\xbc\xcb\x9a\x83A\xa0'\xd0\x92$VI\xe8BO\xb8\xd7\x82u\xa9\x14\xcf\xf9zU\x87r)\x1a\xa9\x96_\xf3N\xb7\xab\xe5+A\xe7\xab\xe5KQ\xbe\xe3\x0e\x12ZQ\xcb\xde Z\xbf\xe3:U^_\xf4^\x9d\xda\xb9h\xad*Y\xde\x88\xf2*;u\x88\xb1ws+\xb3\xf2\xc3[\x1eI;\x8e<\x9aT\x82q\x9e\xe0#\xb1\xee\xe5G\xaf\x18\x05\x17/!\x01\xf7\x9c\xdb*w_1\x0f\xa9(b\x0f`\x1fw\xc9\xc5`Q~p\xcc\xd8\x97\x8e\xdd\x04T\xef\xcf\x0e\x8a\xdd\xc9\xc9\x00\xa3\x8f]S\xa7\x8aG\xea\x87QC\xa7\x9cZ\x17\xed\xa6\xa6\xa13z\xe6*\xb9\xcbg\xad\xac\xfd\xe4\x87:W}\xb82\x1b\xc3\x1b\xa2\xe1\x08\xc2\xe5\xbcb\xf4]{>\x8a\xb5\xf8H\xff\xe0\x11\xd3\x0e\xafi\xc8M\xdb(w;\xbbr\xd5\x94\xa7\x9a\xa0\xf7\xe6 \xc8\x9f\xab\xe8\xf7\xa1q\xce\xd7\xf5\x8c\xa5P\xcc\xa3\xe3t\xd6\x0e\x8fi\xa9\x8b\xea\x84G\x11\x1f\xb6p\xa2)\x0f\xa7<\x98\xd3\xa6`\x85 M\xf0\xe9\xe0\\\xebM\x0bH\x83\xcfCt\xa7\xd4/\xc0\xb5\x08xH\x07\xe7\x9e\xbe\xc6]\xb3\xc5-\xa8\xd2#O\x18z~\xcd\xcd.\xd1\xd0\x91\x0e\xce\x93RZ\x8c\xbcE\xa37\xb9\xfc\x08c\xd8\x82|F\x18\x817\xba\xc2\x98\xa5\x0b\xe2[nq\xe4'\x11\xf1.ps4W\x0fDu\x86p\xcd\xb5=\xac=\x8fV\xc4oH\xede\xde\xc1\xea'c\xf2\x0c\x1at:\x9b\x02v\xe8\x14\xfb\x07\xda\xb5\xe2\xaf}tj\x15\x0e\xb2\xac>\x97\x83\xc6\xe0\xa0\xb9\xbd7\xa0aJcG\xf0\x1f\x19\xba\xbap\xdfPo@o\xfd\xd4\x11\xeed\x9d\xa1\xcb\xeb\xb0\xdd\xa6\xd8\xe2\x07\xce\xa1\xd3\x15\xfbn\xc3\xbb$~\x08\xde\x9d\x17\xd0.\x0fI\xcd\xd6\xf1\x83\x13rk\xd8<1N\"\x9cA\x13\x87\x9f\xd8\x81\x13\x9b\xa9\x01T\xf7e#Xp\xfc\x1d\"\xe6'&\x11\xe8\xdc.\xd5\x8f\xde\x95\x07\x9f\xd4\xf8\x8d\xc8\xb7\x08\xaf\xec\x89 O\xec\xa08uR\x94D\xad#\xff\xd8n\xe4\xfch\xd2\x0f\x9e{\x15\x0e\xce\x8d\x01=\xc3bR(`\x8b9\x19\x8e_\xfb\xb1\x8b:q\x19\x98\x99o\xac\xe2\xf0\x03\x8f\x84\x8f1\x8c\x98`\x1e\xe6\xe0\xa7 \x0d\x16\xb60\xba\x08\xe7\x0f\xe8&=i\xcb<\x81\"Z7\x9f\x85\xe77c\x08\x9b9\x93\xf3\xf9X\xcd\xf1\xaf\xfb\x18\xb8r\xf9i\xc7\xb1\xa4\xf9E@\xe0|\x14\x01\x9e\xd9\xf7#\xf1\xfd[\xb2\x01Gy\xbe\x8c/?\xf9]v\xc6\xe4\xe8\x1fr\xf4\x1f1\xfc\x0e\xfb\xd01\x8d\xb7\xdd8\xc5\xf8\xec\x13i\xb1~\x0dk\xf7\xd98\x7f\x8deQy\xbb*\xfe\x11\xb8\xd7O\xac\x1b\xf6RD.>\xe9\x83\xdc\x14\xdd>t\xcf/\xbbn\x1f\xe6\xdc\xd5Jx\xcc\\\xfaU\x17;=\xfaP\x07\xd1\x84\xb7\x9bc\x8a\xfcY!.V\xa0\x1f\x15=\xd7\xe0\xa1\xa8\xbb\xfa\xfc\x107O\x925Ppv\xfc\x97z\xf2\xf2\x92\x84\x8b/\xfc\xc7\\\xf2~\xf8\xeb\xbaV\xf9R\xad\xcc\x19\xc5b@nq\xa5&\xd4\x1d\xbb\xaes\xa2\xc4\x8c\xaa\x8d\x8f\x86\xe3fQP\x8ar\x07\xceJ\xae\x9ak\xd3\x15FWe\x9dtGI\xce\xca\xcey\xb67\x98\x80e\xd4\\\xe3\xd9\xc9jq\xe9\x07\xd9\x18v\x16\x8b\x9f\xe3\nL\xbc\"\x97\x8f\x841k\x80\x7f\xad>K\xd8\xb3S1\x8f\xceH\x0dTS^\xe7\xf2>Bti\xd2\xdc\xcb\xebH\xd6\x11\xaa\x10\xe48\xcd8$\x82\xe8\x18\x89\xb9\xd4\xc1\x84\xf4\xa6\xea\xb8\x89\xdd\x14\xe9\x07\xa8\x98\xa18Q0\x04\xecG\xbc\xaf\x1a\xb9\xf9#\xc6\xa4\xe0\x93#\xf1D\xc5\xe6\x8b\xc1\x82\xad\xb2\x15\xa5\x8b\x08\x0f\xfb\xfb\x80>r\xfc+a\x1c4\xbd\xe1\xbe[c\x0c-R\x9a\xe4\xc2Y\x0c~\x82\x1e,\x06\xbf\xe1\xffx\xbfr\\E\xc8\x0f\x92):)\xbd\x1c:\xcf\xf6\\G%\x15B\xbb\xba\xeb:j\x11\xa9*Xy\xbf'\xa5\x1e\x15rS\x9d\x1a\x83N\xd3\x1aK\xfe\xe8@G\x98@\xd1<1\xf4\x14\x10w\x1d\x1e\x8aD\x8bg50\x15\xc3u2\x06\xe0\xce\xb1k\x1d5.w\xd3\xb0\xc5\xa8n\x9cL\xee\x8d|\xd9Nro_+\x9aV \xe9\x1c\xb3\x86\x1ao\xc8N\x06x\x84\xbb\x03\xdc@\xce\x95\x8a\x15\xb6i\x91 h\x9a\x92\xca\xa9\xea\x0f=N\xb4R\x83\xd2\x92\xbb\xf2Z\xb57\x91\xa8b\xd6\xd8\xf8\xed\x05UIFm\xb9 A4iI\x90\x0f2\x96\x8b\x99\xc5\xbaf\xa4\x9c\x9d\"\xed\xd5\xac\x18|\x01\xf6\xc1\xef\xf5\x9a\x19\xc0\xc4\x90\xb6C\xfd\x88\xec\xc9\x9c\x02\xb2\xbd\xd9\xeb\xf5\x0be\x19\xc3\x88\x96\xa9\x0e\xd4O\x82\x9cE\x92'q\xc8D\x12\x89\x8d\x0d\x94/b'lb\n\x8d23\x084W\x9a\xd2\xd6\xd3eG\x90.\xc6\x03\x1e}\xc2\xf1\x07\xd7m\xcf\x95\x98x\x8d{\xf7[!\xba\x19\x8b\xa3\x07`\xf1\xc3q\xab\xbe\xea\xc5\xb6\x03\x8b2O#\xdd\x82}\x05\xa2\x81\x08\xc0\x1b\xd9V@!A\xf8\xf5KmMtgu\\\xdcuc\x94\xc1\xf2P\x93\x1b\x1f\xb9\xce4\x8f\\P\x87\x9cG\x12\n\xc3\xb1~%e\xb8\xa1 P\x8c%L\x85\x9aT\x03\x12lg\xd4\xa2\x9dt:\x9c\xa9m\xf5!\xd5gd\xc7\x167[\xb6\xc8Z\x19i\xda\x15\xe5\x86\xd6\xb7\x1e\xd4:\xfb\x7f\xd3\xd8\x87xj\xe8i\xfb\x0bzb\xffo5\xf4'\xea\x180N\xe9B\xc4=\xc66\x94SQ\x8b\x91f\xbb\xb1\xea\x8d\\d\xb9\x1d\xc5\x14\x84\x83\xf7Y\x8a.1\xc7\x17 \x8d\xaf)\x06v\x88\x07\xbf\xd1\x8b_\xfc\xb4\xfa\xac\xfc>O#\xad\xbd\xde\xcc\xf0\x91\xf6z3\xa9^o\x86\xce\xb3-\xd7!M\xd7\xf9ZNX\x1ay\xb5\xca+\x19\xf7ui\x13\xf0> \xa5\x00\x94\xde\x88\x90*\xa4\x06\x16o\x00\x9e\x035&\x98\xe6J\xeeE\xd8G\xbe\x9c\xa2\xdd\xc5\x97(\x88\"M\xd2\x0cPEScl4\xc8\xa3\xd5cl\x1c$\x04\xa9\")\xb6\x8d>V/)\xb5\"\x00\xc2\xaf|\xca\xf8\\\x9e\xaf\xbf\x00'qy\"D\xdb\x9a\x90\x81\x0cv\xe9\x04\xd6\x06\xf3D\x1e\x1d\x9fcgH\xae\xfd%I\xa5n<\xff9HR\x12\xceI\x10\x85\x1a\xad\x05\xc6\x7fC\x83\x1ey\xda\x98\x00z-\xf2\x7f\xe5\x15\x1d\x83\x1a\xaeq\x8a\xf2\xe3\x89\xc8\xa5\xadu)|\xce\xad\xda\x8frU\x95.M\xb5\x06\x92\xfa\xdd\xb1\xe0\\\x94\xb6\x8b5\xec\xc3<\xf2x\x94\x1c\x1e\xff\xeb\x94\xde\xa6G\xd1\x9c:]\x9d\x8e\x92\x8b~\x81;\x888\xe5p\xd6\xba\xb0Q\xec\xe3]\x92\x98x)\x8d_\x93\x94\x8c\xaby2@J|m\x00\xb1\x1e\xccI\x8a\xb7\xbel*\x8b\x06\xfc\xd6\x12\xe1\xbc\x0f\xedf\xbb\x16A\x08\xf5\xdd/\xc21\xc4\x06~\x0cS\xb2\xf2\x9d\xd4\xb4D\x80\xfb\x8e\xc7\xb2b\xef\xc1>\x86\xcf\xa5<\xfe\x0c\xcf\x0e\x1a\xa2\x9e\x1c\x1f\x19\xe6\xd4\xea\xdch2\xbd2\x9c&5\x93J_o\xa8\xc5\xc5\xef\x9a!\x8fLA\xae\xda\x804\xd0\xfe\xdaN\x95,\xb0>\xc1,\x8f\xa8\x15\xf1\x88Zq-D!W\x07\xe1ej\xcaD\x06\x8cf\xbapR\x0c\x93\xaaa\xc0\xa2p\xe1/\xb3\x98\\p#\xdb\xfa\x12/i\xda\"\x0c\xa0\xa2\x0djB\xcd\x07\x9e\xff\x8d\xeb\xa87\xa13\xaccm\xd5\x89\xc1\xf2*\xcbm\xa2\x8aNc'\x1e|\x80\x1e\xc4\x83\x8f\x16i^\xa4\xf7j+\xe8\x10\xa1\x9e\x8b$G\xc1\xf6\x82/\x7f\x18\xa4\x9c\xd0\x84\x1e\x9a\xa0c5E]\x08\x93blF\x93\x17\xf1\x1aOH\xe0\xb8U\x11\xd6v H\xe5\xa8\xb6\x82\xee\x1a\x8f1\x99}\xf8\xee\xe3\x12\x91\xd3\x1e4,\xb3\x96\xe8;\"o\xddt\xcf\xcfM\xf7\xca\xe8xbA\xc44n\x8d\x84\x11#\x11\x987\xda\x88n\xbe\xd6\x92A*\x00\xc3\x01E\x93\"\xa1u\x1d\x17r\xb0\xeb\x84(\x9f6k\x04\xdb\x00T\x82\xce\xba\xde&b\xf4\xd9A\xa32\x99_\xc2\xe9*\x15\xbb5+J\x0c\x01?\x88\xe9\x92\x864f\x0c\xd8\xc7,L\xfd\x15\n\xdd\xc2\xa9gIS\xc5\x95\xe7\x88\xach\xe2\xc4\xee\xc0\x0f\xe7\xf4\xf6x\xc1\xda\xaf\xbe\xdcu\xe1eM\xe3\xe5\x83\x08c\xa7\xeb\xae\x809&{\xd1\x0d\xa8\xe0c\xcb\xd6\xb7{\xec\xd4\xc2\xb4\xec\xfa\xb7\x94\xc8\xf9\xc8;\xd5yx\x11}S\xf7~\xb1p\xc6\xeb%\xeb`\x8b\xf7\xb5\xeb\xae\xb6\xa5\x18u\xd6\xeel\xf4;\x0c\n\xa37tU\xaf\xf8`\xd5\xb1\x9c/v\xd95\xab^\xcb7\x91\xdd\x93\xbb\xd5E\x14\xc0D~\x19\xd7\xccVA\x9c5\xfe\xc0O9@\xd0\xbe\xf1?\xffS\xfe\xec\xd6\xeb\xa3\x8e\x92\x87}}[~\xa9T\xa6y3\xc17e\xb0\xc3S\xb2\x14\xef)%\x9a\xb7\xf0\x92*BX\x95\xce\x94zMOX\xf7\x99\x91\x15\x04\xc2z.\x04\xc8\xf0\xa9\xa8\xe9\xb9\xad8w\xc7\xd4\x0d\xecC\x80\xb9\xa6d\x93\x0c\xde\xee\xe0&&\x8c\x99?\xaf\x93))\x03t\x93,Y\xd3pN\xe7')\x89S\x0d\x0c@H\x04E\xcd\xbf\xfa4\x98\x1bj\xa2C\n\x8f\xa9\xe4\x87:\x90\x820\x06\xefz\xd1j\xcd\xf6\x92\xa9\xa5k\x9ePA\xfbl\xa5qC\xc4\xf2)\x995\xd1Bhb\xce\xf4\xc0Z\x16\xbbfI\xd3\x0fr\xe3\x1c/\xf4#\xbc\x83}X\xb2e^:K\xe7\xbd3\x9d\xb9\xbaKS\xf48\xb9C\xb3(\x14n\x85pw\x87I\xb3ej\x91;\xcd\x8blD\x17h\x9c\xad\xde\xf9\x1e\x96~\x95\x028;+M+\xb7\xa5\xfa\x17\x15\xeb\xed\x93>\x9cT\x8an\xfbp2M\x18\x88o1MW@\x90\xc6\xb3\xe5\xfcIb\xa4(\xbf\xf8\xa5\xcf\xd7mp6\xc3\x83\xd2\x19\xb2\x0fW8m\x8c'\xaeu+\xb5!j$n\xe8\xaf\x9cs\xf5\x0d{dh\xed\xde`\xa7\xf9\x04\"t\xca\xe2\x1e]\x0f\xb9'\xcbU\xcb\"\x9f\x0e\xe5\x8e]Jk\xfa%\xd0\"\xf7+\xc4\x8f\x8b*vuY\xd97 \xb2}\xb8\xc8O\xe3\x074\xd6\x9d\xf2\xd3\x18\xf2\x01Ur\x1e\x82\\\xe0+z\xd7\x9c\x8a\x04\x14R35\xa46\xa8\xf9\xaf\xa7\xd2\xa8\xc4\xba\xbe\xec\x94\xbe\xa6qB\xab\\\xb4\xfa\x91\xa3\x83f;>\x91\xd9@\xde\x1d\x19\x15\xd4\xeaG\xca\x06\xe9`\x1d\xadMZM\xf5\x83\x0c\xb5\x98fn\xd0\xc3\x91\x08\xd3h\x84\x1c\xb5\xb8\x91\x92^l\x94\x1f\xb3\xa5\x1c(\x02q\xde\xde\xd0\xd6\x9e\x96Hx|`l\x91\xdf\xf7\xe1\xb4D\xe8\xf4\xa0Q\x0e\x8c1\x9c\xeaW%\xa6 m\xb4\x02\x91\x1f\xccz\xc1\xedp\xe8\xb5b\x9a%\x14y\xf2gBCy\x81;8\x17?B\xf1L\x81'\xffM\x03\xba$\x18\xa5\x84'\x92\xc4\xd2\x15\x86 \x95\xd9\xc0\xba\xa2\x94\xc4K\xa5\xa54\xbe;\x0c\xd3\xd8\xa7\x89\xcc\x97\xec|p\xfb\xd0i\xb0h,\xa2\x9d\xb3uG\x91\x17\xbaiWxo\x88P\xdbCW\xe1N\xb8v\x86;Kux\xea\xb4\x9eL\n;\x12 \x86X\x1d\xe1[i :z\xf0'i\xb4n\xa1\\\x03i\x00\x95\xa3\x8f\x19\xb7\xa5\x0dU\x05H\xd3\xe1l XP?\xb2\xb8\xd8`*}\xd4\x93p\x98\xd0\x01\x1eJ\xf2\n\x86-\x82\xf9eU\xd3\x14_\x93zb\x020\x83\x821\"L\x8c<\xbc\xf5\xe8:\xc5\xa8\xb4\x0f\xc4J\x06\x9c|\xa0v\x00\x156\xdf\xcd\xb4*vL\xa9\xf6\xd5\x8f\xd4J\x0d\xc4\x96\x140\xecC&\xf0\x16m\xc4\xc5NA\xef\x11\xae\x04\xaf\xa3\xba\xc4s\x86\xcc\x1d\x8b_\x85y\xe4\x12\xc5\xfd:\x1aHg\x9d\x0d\x18=\x07\x1fU\x11\xcfacC\x1b\x17B\xfd\\\x8b\x1c\xffU\xac\xf2\x1b\xcc{@H\xb1\xa4\x15\xf2\x81D\xc08\x8a\xc4\x9e$\xac\xb7w\x91\x97\x13\xe8\xd8\xe9\xd2pn3\x1d\x97\xad\xc8W\xe1\xc5>\xe4d\xabi\xa2 &\x8b\xb9kD6\xf4>tQ\xc3\xf1.\xf2\xba\x96\xd3M\xfd\x04\xe5\xd7\x85J\x18\x1bhw,\xe1\x9dm\xd0f\xb4P\xa3\xcc/0=/\x1f\xb0\x02\xb7\xa2\x10\x1d\x10\x9a\xc7\x01\xda\x96\x8b\xb9\x94\xdaV\x8a\x1b\x1b\xfe\\\\z&\xdfs\x8a\x8d\x0d\x7f6i\x1et\x1f\xbc\xa3\x0d\xd4\xfc\x1b\"\xf7F\x1a\xdfA\x92\x92\x94b\xd6\xf4\x1b?\xbd\x8c\xb2T(\xc5\xa2X\xde\x07\xb4Yy\xf8n\x10\xb7\xd6\xb0\x98\xf9?\x84\x84\x93\x8b8[\xa7-l\xac\xe5G\xe15\xed\x94*\xcc)\x95\xf1Z@~r&\xb0B\xa9B\x03\xbf+?\\\xb9\xaa\xa1\x18\n+\x10W\xb6rny-\x96*.-U3VI\"m\x10\xe8\xd5\xcfEL\xc9\xd57]D@}&\xa6)\xc5\xc6\xc5y\x8f\xfa\x02\x99>\xac+}z\xf0\x16Q\x01\x0e\xc8\xd4%\xbe2el\xcc\x17\xac\x9c\x05\xdb\xe5a\xe2s\xd7\xd7\xfc`@-^#wA\xe4\x11K\xfb@\xc4a\x99\xf6\xb11\xc7\xc2=\x8a\xa3W\x1do\x1f\xae]a\x0e,GA\x1d\xf2 \x06N\xbe\xf6\x00\xa4\xff\x16\x1cVi\xc58<4\xcb\xc6\x1fLJ\xf3\xc7\xf6a\x0c\xe2\xea\xa3R\xd3\xc9Y7\xb9\x83\x04\xf3\xc2\xfe\xd6\x98s\xd1D\x19\xc0\xfctf=\x84Q\xbc\"A\xa9\x07y5\xed\xa8o\xa4n\x1f\x0c\x1e\x7fz\xa0/\xfc\xd0O\x1a\xfd\x13\xf2\xda\x05\xc7o'2iNd\xda\xf9\xd3k\x88L\xda\x82\xc8\x84\xea\x8e\x11\xdbKe\x9csL\x0c\x95\xad\x81\xc9\x89\x17)\x8d\x19e\xe9\xa3\xe3\xb8 h\xf0P\xb2\xdd\xca\xdbC~\xfe\xfd\xa0)\xa8\x92\x80d;\xa2\xcb\x8d\x84\xdb\xb2\xa4\xa0\xd9\xb5\xb1\xd8\xb5\xcd\xfd\x81\xa26\x8b\xed\xbb[\xfd|0\xd9d\xab\x1f\xfb\xb1\x0e\x05\xc10\xcb\x11\xf0\x85GG\x8d\x0b\xf2\x03&\xca\x07\x82\xef!iJW\xeb\xb4\xfb j*\xb5\x01x\xe32\xae\xea%\xad&\x82\xea\x0eR\x94\n\xf6\xe5\x91Woc\x8c7`\xe7\xecc\x9adAzDVt\x0c\x0d\x01-\x18]{\x17yc\x83m\"p\x85\x0e?\x9d\xb8\xe2A\xa1\xab9u,\xc4@\x03q\xac\x95VM\xc0J?sy\xf6\xbcA\xcd+q\x95\x9f\xf1\x8a\x9eI\x89\x0fs(\xf2\xe6\x1d\xea\x01Q\xcb\xa7\xe9D\xaa\x82[\xfb\x0e\x11Z\xe5S\x07\xef8\xa7:[f\xb1\xc8\xfe\xe0\xdc\x0f\xaf#\x8c\x02j\xb3\x15P?\xb9\xdd\x80U\x8b\x99\xb7f\x8a\x95(?\\s\xc8\xd6n\xae\x11\x08rm-\xf8 \x90 \xa6d~\x07q\x16\x86~\xb8\xb4\x89\x01E\xabZc\xf9jU\x95\x1e\xe5\x19\xc6\x0d\xd9\xf0\xe5GL\xf4\xadA9\x0e\xcd\x9a\x85\xb0\xe0\x00\"<\x96\x10O\xfd\xe7\x8d*Z\xc9\xf6\x85\xf9\x06m&\xef\xa4\xa9Q\x10\x0dg\xe8\x14B\x18\x064\xd3W4\x96m\xd32\xc8\xca\x08\xe3\xeb\"\xafns\x1f\xa0(\x85\x1a+\x7f\xa9x\x06\x12\x13\nZ\"\x97\xc7\x85Pjb\xc3B\x0d\xdb|\xfe\xe4\x92\xb9\x8a]E\xa3\xcd0+\x90x!q\x92m\xbc\xcb~\x9b\xde\x01\x9d\xa9j\xba@\x07_m\xf0v\xe2C/1\xb6\xa1BU\xc3\x01\x97O\x9d\x82o\xe5\xad6l\x18\xd8\x87\xb9\xbd\x8a\xd4\x17\xdd\xe4D\xa8\x19\xb1K\xdcq\xd2\x9a\x99\x10\xc0\x957 \x13\xb8\x841\xac\xfb \x8e\x8b\x87\"i\xe3u\xa6\xfa\x11I\xfd\xb0\xabvZ06\xc6\xb1\x18k\xe3\x0b_\xb3\x07T\\MrQ\xc3\xc9\xf1\xae\x90Y\xa4ZV\xd2\xad\xc4\x8eX\x06F\xbaV\xfa\x99-}\xd8\x07\xe2\xf6+\xc97M\xc7\xf0\x8d\xed\xc42;S4\xaeX\x8ai\xb5$z\x99\xd7\x89\xc4\xcb\xdc\xb3\x07\x87\xd1v\xa6\x8d\x11\x1c\xda\x0eQ,E\xc3\x08\xdb\x0e\xab\x15\xd0\x0f1\x9e\xa0\xe1\xe1\xad\xed\xe1\x89\xed\xe1+=0\xa6R\x01\x91c\x9d$=\xb3\xfc\xce\xcal\xd8&?\"hg;\xf1Le\x83\x05\x93\x84v\xb2\xadW\xb7j\xee\xaa\x9f\xf0\x95\xc5\x9a\xb4Nu\xd4\xd1\xa83\xb1\x19\x1a\xe4]\xf9\xad,\x8d\xe9\x8dt\xa7W \xda\xc0\xc3A\xc9\xb2\x90\x07\xbc\x8ey\x90\xbc\xa6\xd7@\xe1:n\x1c:\x0dg\x18a n\xc9{Hr\xd5\xd9\xdf\x177Fm:\x04\xe5\xa8\xc9\xda\x13a\x10\xd7\x11 \xbf@n\x1e!\x14pE\xcb=\x8dE`\xa0(E\x03L\x05\x8bV/]\x17&r\x1dr\xef\xa2` \x9e>\xc8\xb8\xa3\xfaI\x1d\xb9\x99\xa8X\xa2V\xaf~~\x88\xeb\xae\xfaI\x9d|\xd3>\xacC\x17\xc6u\x10|\xd5\xd4\x93\xdc$\x01C\xc9'-\x07\xd2j\xc8\xcd\n\x04\xe2d-x/\xb1w\xd2Z\xb0\xf8R\xad\xb6T\x08\x14J\x06\"K;\x87\xa0\x8f{z\xcc\xa8B\x9dv\xb5\"]\x07\xd6\xc8/<\xec\xa6\xd4\x0bL\xe5\xfd\xacF\x11U\xb0\xb9F\x99\x13or\xea&\x0e*\xb3\x92\xb6`\xac}L:/\xc74\x10\x80\xa9^\x1f\x17\xca\xd8\xc2PB\xcc\xd5\xd0e\xaev\xbc6\xd3\x84T\xc3:\xe5\x1d\x943\xd0\x9f^\xd2\\\xa1\x02\xf3\x88&\x10F)\xac\xe3\xe8\xda\x9fS \xf0\x18\xdf\x7f\x0c\xbcA\x93b\xc8\x86\x0b\x9aH}\xdaE\x8c\x90*\xc7}e%\xc5\xa85\xf4\xb9&H\x0bz,\xf1\xcf\x02\x80Hh\xc5\xebK\xac\x81\xa8\xbc\xeb\x89\xf4B\x90Tm\xe0\x95\x88\xe0\xed\x9dt\x8a4D\xe8\x9dfx}!\xe2\x99\xa7\x85B_\xa8\x9b\n\xee\x02\xcf\x95\xb4\xa4P\xb2\xdb\x19\xe8f\xc0\xb3\xcd\x8f\xcb\xef6\xa0@\xbe\xfc|\xd0\xe0s\x1c !\x88#\xc4\xd4W\xab\x9d{lwa\xd1o \xae\x1d\x1e\x03\x9d\x0egu\xf4\xa9\xaf\xc3\x88\x9b\x9ar\xa0\xc9\xcbd\xcc\xc72\x9a\xb9}\xd8T\x1f\xabz|\xa0\xdc\x1d>\xd7\xd2c\xd1\xd6\xcc\xad\x9b+\xa19]\xdan\xce\x1f\xecs\xa6\xea\xed\xd9\xfd\xbd\xf6\xfa,\xcdMR\xa4L \xbd:R\x8e\xbf\xa5F\xf6\xab\xd1\x94\x0d\x03;\xd5\x0f\xac2W\xd8\x87\xa9}]\xb8\xa9G}e08\xacd\x92\x8f9\x10\x8b\xc8N M\x9d\xea\xfd\xbei\xa4\xef\xf5#E\xaaj\xd3\x16\"|\xa7\xc4p\x07\x81\xb4]\xa1\x12|\x7f R\x9fom\x8fJ\xcf_\x1d\x7f<,?/eU\x1a\xbc>|s\xf0\xe9\xdd\xe9y\xb5\x9fQ\xa5\x1fY\xef\xcd\xa7w\xefJ\xf5\xb6wJ\xf5\x82\x88\xcc\xf1\xc2\x94}\xa9>8\x08\x82\xfc\xd9\x01\xe3 \x8a\xc7 Y\xd0w\xf2]\xf9CWA\xb6\xa1\xfcV\xab\xcd\xb3\xd5\x1a\xb95\xf6\xa5\xfa\xfek\xf9P\xfeP+\xfc\xf5\xe0\xfd\xbb\\q-`\xb0W\x9a\xdb\xfb\xb7Go\xdf\x1f\xbc\xb3-G[0Z \x98x\x84\xbb\xedv\xd9\xb7n\xe9\xd9\x9a\xc4\x18F\xd1w\xba\xf8\xb5\xfc\x14\x93\x19\xcb\xe7\xe2G\xb9\x06\x99\xcf_\x95<\xa5|\xa7[.\xeb~\x93M\xfc\xb4\xea\x06\x1d\x15\x00-\x95\x8b\xb4Z\xdb\xfaDq\x08\xbdRyV\x80\xacT\x9eh\x9cE\xad^\xa1\x01F\xbd-\x15y\x18\x07\xbaL\xaba\x1f\xb6\xcaE\x0c\x81\xb6\xcbE\xf3z[\x97\xf5\xb6\xae\xebm\xad`\x1f\x9eL\xcfn\x87\xc3\x8d\xb3\xdb\xe1\xd3\xb3\xdb\xe1\x8fg\xb7\xc3Wg\xb7\xc3\xc3\x8d\xb3\xdb\xd1\x9b\xb3\xdb\xbd7\x1bg\xb7O\xb7\xcfn\x9f\xeen\x9c\xdd>{s\x96\xbdy\xf3\xe6\x10\xff\x7f3\xbb\x9f\x9ee\xaf\x9f\xb2\x97\xb3\xd7?\xbey3s&\x1dV\xf2\x8a\x97\xb0\x1a\xee\xbd3\x19O\x7f/W\xbb\xff\xdd\xadT{R\x1e\xd6R\x0c\xeb\xe9\xceY\xb69\xdc|\x8a\xff?\xab\xd6\xba\xc3Z\xfd\xb3\xe9\xd9\xec\xec\x1fg\x9f\xab\x8f/\xd8\xe3\xdf\x9d\xc9\xb8s\xdf\xe9\xdcw\xa6d\xe3\xefg\x1b\xb3^\xc7\xfd\xf3\x13\xbf\\\xf3\xbc\xa89\xfd\xbdh\xcfu&\xe3\xff\x98\x0e7\x9e\x91\x8d\xc5\xec\x1f\x9b\x9f\xef\xf9\xf7\xbf\x9fm\xfc_\xcf\xcf\x9e\x9cM\xc6\xff\xf9h\xff\xacw\xf6\xe7\xfe\xf9\xd9\xa0\xf3?g?<>s\xce\\\xf6\xf6\xcc\xfd\xe1\xcfO|\xddYqc<+F\xc3\xc2\x8an\xb4\xc5\xbf+\xd4\xbc\xde\xd4\xa1\xb1\xa9gEK[\x9b-Z\xba}HK8\xbe\x87\x8e\xf5\xc4\xd8\xc3\xf6v\xd1\xd4\xb3\x91\xf2}K\xe9b\xb3\xf4c\xa7E\x87\x1a\xbd\xbaF\xc5,\xc7\xf0\x14^\xec\x0bgI\xf6mg\x0f\x13Zn\xb0\x07cx\xb6\xc7\xca0\xaa\xf8\xd6&\xdc\x0b\x9bF4a\x1c\x0d7\xd1\x9ca\x83U\xea1\xb0\x8cacd\x1d\x98F\xff]\x8c\x82Or\x02\xdd\xb3a\x97\xf7\x9c\x97\xfc\xff\xb0@\xadr\xc1JF\xa3]\xa5(\xc5J\xd5\x82Q\xbe\\\xac(\xe4EjK\xd7X4\xdcT\x8a\x16\xbc\xd6\xb6R\x14\xf3Z\xa3\xa2\xe8\xff\xcfJ\xb6\x94\xd7\x00\x0b\x8a\x97\x1ew\x1f\xc3\x18\xb6\x95i<\xc1\x11\xaa=\x9d\xb1\x92=e8\xff\xe7\x7fc\x9d\x1d\xa5\xe4\xff\xc6:\xeaL\x91*\xb0\xd2\xa7\xc3J\xe93V\xda\xedZ\x17\xe1\xc0\xb8\x08\xb8\xfe\xbb;;[;0\x01\xeet\x87y\x0b_]\x92\xf8U4\xc7\x9c\xa8c\xed\x83\x9d\x9d\xcdg\xbb\xd0\x03\x87!\x0eka\x17^\xbe\x84\x11\xe3uvv\xb76\x87\xe5G\x8f\x18\xbc\xb7\x14o\xd9\x82_\xcb\xed\xe4\x8e\x85\x9a\x043\xee9\x9b;\x8c5\xfb\xa0);\x054\x97;\x85\x17\xb0\xb9\xb3\xfb\x1cN{=\x17\x8e\xa7\xa73\xd8\x87+\xe7\xd4\x85 \x8c`\x0c\xc3>|(\nu\xc4\xe9\xbdV\xc1\xa9\\\x94Dx\xdf\xc7\xc3\x17\x0f\x16~@C\xb2\xa2\xa8,\x0b\xd7Y\x8aN\xb4Q\xe2\xa7huH\x07\x81\x1fR\xb5\x0c6D!:\xd0\x97\xe6^\x1f\xcb[\xedX8\xcf,\xc6i}\xff\x0f\xed\xfbt\x10\x85\xbf\x918\xf4\xc3%w\x8d\xce\x7f\x8a@\x85\xa8U\x12\xed\xeb\x16\x87\xad\xcbQMe\xc4\x18\xb7\x9a\xd1\x99V\xb9{]$\xa4\xab\xcb\x8e\"7\xf0>\xd0\xc15\x8d\x136\x8dG\x8f8$\xba\xf3l\x1d\xf8\x1eF\x1d\x84h\x01\xff\xc1\xba\x84\xb9\x1fS/\xf5\xaf\x91\xc7\xe2IC\xf2\xa4:\xf9\x9b\xe5\x9a@<\xc6`&@o\x89\x97\x06w\xc0d]\x99\x03\x12\xe3E\xb3A\xb0-\x85w\xe0O~w\xd8\xa17\xeb\xb9g\x03\xf9\xed\xcfO\x06\xf4\x96zN8\x1d\xce\xb8\x17\x1b\xef\xc8\x0f\x82\x8dE\x14\xaf\x98\xa4\"\x1a\x04L\xb0I\xa1>Z\xc6\x8e!\x03\xf96L\x9d\x18\xc3B\xe2^\xf1\xcb\xe5\x9b\xb2\x9c\xcf.*z\xcbB>\x13r\x11\x88\xf6%\xccD\x9f20\x1b\xe7?\xe5\xc3}\x081\x12%\x1dx\x97\xd4\xbbz\xe7\x87\xf4\xc7\x98\x92+\x0c{\xc1v\x90\xec\n\x0d\xdc7\x8b\xaf\x7f\x88^\x93l\xcd8Y:o\xe8\xb4\xb4\xba\xd5\xccb\x07?=\x0c]\xea\xb8\xb2iX\xed\xd3\x83\x9f,\x8b\x9d\xdeDE\xc2O\x06\x988\x07\x08\xf2\xc7\xb8\x0e\x17\x83\x94&\xa9\x13\xa3\xa8][\xda\x94,\x81'o\x01g\xe1\xc7I\x9a7\xe8J \x94\xc6\xc0zI\x84\xeef\x90\x92\xe5{\xb2\xc6\xcb[9\xe2\xc7\xe9%\x8d)\x9a\xbb\xc1:\xa6\xd7~\x94%\xc1\x1d\xcc\xa9\x17\x90\x98\xce!\xc9\x16\x0b\xff\x16\xa9b\xf71\xf4 \x86\x1e<\xee*\xc3x\xec\xf6\xe1\x9c\x0f92\x0fy\x1dS\xd6\x8c\x93P/\n\xe7-\xc6,\x07;\x8dg\xb6xr::\xfa\xd1b'\x89\xb7\x0cy>\xb5\xf2\xba\xa2f\x10^\xe8QA\x18\x93Ib+\xdcH\x11q\x8c\xd1\x81\xf1(\x89\xb8\x83\xad\x8fw\xbfB\xed\x06\x11\xbc\x00\x9f\xfd\xe9\xed\xc3\xc8\x15<\x83C\xb0\x8e'\x8e\xb4\x03\x06PW\xf0~/\xf6y|8\x82|\xcfh\xb4=\x1a\x8d\n`\xd3\xdb5\xf5\xd8\x9e\xb8&\x81?\x87\xbf\x9c\x1c\x1f\x15\x11\x0cuv\x8bhp\xb5\xe2\xab\x96)4\x84-E\x92\xc6\x94\xac\xd0\x16\x89\xf8a\x02a\x14n\xacc?\xe4[=o6\xd1\xb6+n=\xd8\xbc2\xd3\x9ai\x96\xecu\xb1d5\x87M\xbc\x7f\xe1\xeb\xd5\x87\xa0\xdc'B8\x1e\xf8 \x17\xfd\x9cP\xc1@\xa1\xaaY\xd1xIaE\xd6k?\\&\xcf\x11\xdb\xc4\xdd\xd6\x1c\x92(\x8b=*.9\xd8&P\xc9\x1aC\xc3\x8c\xaf\x1e\x13\x16\x1d\xc58\xf6\x8a\xdea\xa2\xb7|A3x\x01\x01\xfb\xc3\x17\x14\x9dd\xa6\xd9,\xdf{)\xda&`r!\x1e\x95 \x9c\x12\xb6\xeb\xf9\x0fU#\xae\x03\xcf;\x05\xa3\xd5t\xaa:P\x05}\xf0\xeax\xcd\xb0\x90\xb3MN\xa4\x9e2y\xc4\x11\xf8\x07\xe6\x83N\xc9r|GV\xc1 \x8a\x97\xfd\xcd\xe1ps\x8c\xf0\x13\xa6\xf3u4gm\xf3\xf4\xd2~\xc2\x99\"\xdf\x96\x958\xe0\xe0\xf4\xf0BL\xc2.\x80\x17\xe0\xb1?\x1cv\x12\x17\xfci0\xd3\x9b\xe4!\xf6\xe6\xd5\xeau\xf09\x1d\xfc\x91\xf0\xbb\x95$\x8f\x82\xcc T\xa7X\x13^\xe0p\xbe\x08\xd8\x1e\xc3\x0c_5\xd6i\x1f2\xfe\xa4`\xb0\xca|\x01\x9dK\x14\x83+z\x87!M\xd2i\x84\x17\x7f\xf9\xadM8\x8dfZ\x01(\xb5.\xfe\xa7V\xb2\x94\x102D\x8aMN\xa3\x14JR\x8c\x1c\xf32\x15?{=&Vl d\x98\x80\xa3>\xea\xe7\xa2\xa6\xb5E\xce\xcb\x15\xaf1\x1e\x9d\x83\x87\x00\x02\x16\x9d\x9e\xd8\xf6\x92\x84\x8aSx|\xd6\xc3\xe4C\ng\x8a\x13\x90\x8dY!\xf37\xd3\xd9]J\xc69\x94\x19\xfflSx.\xb2~GZchqyr\xe8D\xees\xd7\xd4Z\xaf\xa7\xb6\xa7\xdd)\xb8\xdb\xb6\xb8he\x08\xf0?\x8f,\x979mz\xd6\xbe\xfc\x19n.}\xc62\x8c\x86\x05#7\xda*\xbe\x8bb\xc3\xb8;7x\x14\xe12\xd6k t>a\xf2\x90f@\xf7!fx\xc5\xd7\xfbm8\xe7\xe6\xcd\xc3\xe7R\x90e\x0b\xa0>d\x95\x1f<\xed\xcf\xba]\xb6!8\xf4b\xba1G\\e$/\xf8c\xcel\xce\xe9\xc2\xf7|V\xec\xe3S\xe4\xfe\x91k\xb3b\xe5\x1b\xc3~\xed\x8bD\xb3r\xc8ZR\xd0q\xb6wpl\xa6\x8d,2\xe7n\xefr[\x01\x0c\xfd$\x84\x96z]\xe81\x82\xdaTe\x93\x13\xc1\x90m\xc5\xad\xbe\x80MC\xff\x9d['u\x1bd\xc8\xbfke\xc0QNjTf\x81\xeb.R\xcc\xda\xcfc\xce\x15\xcf\xe2AL\xd7\x94\xa4N\xf7\x0c\xcdd`\xa3\x94(K\xd7\xf5\x8f\xda\xae\xafE\\A\x89Q)\xd1X\xe2\xf9\xdck2\xf4.\xaby\xb3A\xa8\xa5u\x99Q2M\xae\x11\xeetQ\x08\x95\xbcM1=\xfe\x831\xb8\xf2;;,\x88\x90 \xda\x11+lk\x9b\x93\x13\xfc~\xebX_Dtp5\x97\xbe\x92\xb9\xed\x0c\xfbP\xa6\xffHbY\xf1\xc6\xc8\xad\xef\x96}\x06c\x99\xbb*\x0b\x82v\xa3\xafu\x9f{.\xf0\x0d\xc2O\xdf\xdf\x04q_\xf0<\x1e\x1d\xcc\xce\xc2\xbb\x92\xc8\xe1\x96\xc7\xd7\xa6\xf3~q\xd8#-\xc8\x8f{1\xa5\x97\"^\x8c\x00\xb0+\xce\xb1\x0b2W\x89\x00\x93Z\x08$\xf4o\x19\x0d=\n4Lcm\x94\x80|b\x15\"\x93ji\xa9$\x01\x9dL\xe0\x08\x13\x9c\xd0W'\xc7\x1dd'\xe8\xe0\xca\x0f\xd1\xaaG\x8e\xa0\xdb/6\xd3>\xe3\x0c\x9b\x18\xca_\xcd4*g1\xf95\xbev\x07T1\x9dMq\x8b\x9f&N\xf3\x11P\xd8\x0f\xe8\xdaQ6\x0c\x9b\xbfI\x03C\x84X\xc9\xafv\x18U\xde\x15\x1cP\x9b\xd3\x82\xf1@\xc8\xcfw\xcc\xdcA\xe5\x851lq.)b\xef\x12%\x01g\xb7\xd3\xe9\xb6o\x85\xbf\xd1\xedC\x99\xd11\x98<\x1b\xd9\x816\xdd\xd5^\xcc\xd9\x00\x85\x0b\xd8\xdd4\x1e\xfd\n\xe5(lF\xd8\xecc\x9d \\\xdaem\x86W\xb0\x89Y\x98K\xb04\x9cK\x9d\x80\x10Do\xfc\xf4\xd2\x0f\x81\xc05\x8d/H\xea\xaf\xd8\xcaW\x15<\xa6p \x82sS\xe6\xdb\xb9\xe5\\\\\xbe\x9al\xaf\x11\x98H \x98,\xa5\xceC\x08\x90B\x10\x06z\xeb\x05d\xc5\x11pE\xe2\xab\xa4\x9b\xa7k\xae\xc0\x82\x1dP%\xf1\xa1\x87\xc9\xed\x84bG\x95QCR\xd1\xe9T\xfaL2\xef\xb2$r\xcb\xcc\xe5U\xf4\xe1\xa4\xbd\x1d\xdc\xeb\x0b\xdd\xbc\x9ew\xb9R\xaa\xd0\x15\x18!\xb5\x08\xa2\x1bF.\xd9v\x8d\xe2\xd2\xf8\xcb\xab\xa6#\x7fx\x90u\xce\xf5\xfd1x5\xc0h\x8c\xf6\x1b\xb1\xcb\x03KH\"\x1a\xc3\xb8\xae\x06\x0b]\xa5F\xaep\ng\xa8\xe6\x1a\xb3]*N\x89\xa2\x16+\x93Ou\x8f\xeb\xf2\xb3\xac\xcf\xb5mY\x98k\xd6\x94UG\xcdZ\x88\x9a\xb5\xc7\x98\xda\xdeJ\xbc\x7f6\x13o\x0dY~\xca\xc9r\xf8\x15d\xd9\xcc\xc8\xe8Is\x08\xa2\x86J\x9e\x0d\x03(af\x15\xab\xe5\xc6\x0d\xc5\xc6\xe5\xa2f\xe7\xc4 \xd9\x0en\xd3\xa2\xf6\x84U\xb6M\xae\x03)\xf6cy\na4\xa7\xb0\xca\x92\x02\xdfH\n\x01%I\x8a\xaa{E\xcbV:\xa6\xed\xbb\xa9a\x81\x7fS\xb4a\x9as\x01\xddqQ\x1b\xb6\xea\xc3\xb2\x0fw}\xb8\xe8\xc3y\x1f\xae\xf8e\x94\xe6\xd0~o8\xcc\xff0\x1c\xe6\xcab\x07~\x92\xd2\x90\xe6\xb2\x12\xff\xe5t\xa35\x0d1\xbfx?\xc7~~}\xa3@A\x16\x08~E\xfe\xcc9\x15^\x80jO\xd8Gc\x88u\xc1\x97-\xf8W\x11q\xad\xca\x88:\xefs~\xb5\xcc\xbe\xc1\x84\x03\x01\xd3_\xa9B\xa6\x90:\xf0\xba\xae\xfa\xf0\x85P\x84\x9d\xa2\xf1\xa5\x8b\x17\x1e\xec\x85\xd3\xfa\x19*N\x14\xe4\xa0\xee\xefq3>w\xcb\xc3\x9b\x14\xa3[q~\xec\xbb\x0c\x12\xc6\xd8\xbcn\xfdV \x832\xbfg\x83\xf4\xf3\x1b\x9cS\xf6`-6\x15\x93\xfa\xce1\"w\x0et/'i\x98\n\x80\x1d+}\xb8*\x1f5\xa5{\xc4\x1cR0\x01\xde+\xca^W\x08\x9c\x87\xdc\xb1\xf4\x0b%ob\x96\xce@X\xee\x98%4\xf6YXBr\xcf-\xcf.%Nj\x9f^[\x9f\xae\xacO\x97\x86\x0d\x08\xc2\x8eF\x97\xa7\xf2\x0b\xe4\xc7\x85PY\xb7\x93\x1f3\xa3\xe7\xbf\xf4Vn\x16'\xfbB`\xe6B\x1b\xa9\xf0\xb4\xbb\\(@\x81f\xe7\xa9\xf8~\x7f\xcfhyl\xb5\x84F\xad\x13\xd2\xc1\xb0\x0f^.\x02\x1auP\xea{\x8a\x80\xd7\xe8F\x880n\x03\xb1C'c\xfb\xdcP\xb5\x81\xbfR?l\x84;\xdc\xde\"s\xe1\xd6\xd4y\x85S\xce9F\xc2X\xf8\x94&k\xe2)\xa7\x8f\xaa[\x05td@\x0e\xfa\x8a\xdemp\xd3\xea\x84\xae \xf7\xf0\xc8\xd9\xe9\x8b \xf2\xae\xa4\xd6\x9a\x1d_(l9x\xd7\xb0\xe8\xc3\xbc\x0f\x97}\xb8\xe6w\x05n\x1f\xf7\xc6\xb5\xa0\xd2\xa2\xe8N\x109\x81\xdc\xc8|\xb2\xbf\x97\xf9\xfe\xc57$\xc1\xb7\xc3\xa5e\xf2+\xa6\x04\x88\x97vF\xe9\xba\x91Q2\xe5'a\x80\x17\xe6\xa0\xce\xba\x19\x17\xf8\x9d\xd8\xb3\xad\xbe\xd0\x83sM\xac.P\xbd\x85\xf2\xb1>G\x9b\x9caX\x1beQ\xf9a\x1d\x8e6wD\x8fC\xde\xe3?\xda8\xf4|\x01[\x15\xbb}0\x80\xa1|\xf2\x0b\xfc_[\x19\xab|\xab\xb1\xbd\xda\x06\xbc\xe2\xbe\xb0.\xbe\xf2\x9b4\x8e\xbb\x97%\xdc\xbdVp\x97\xd1\xdb\x1c\x7falR\x1b\xc7\xe6\xc3d^\xf0\x1f\x9c>\x82\x17\xadV\x04.hzC\xa9P\xf8xQ\x10P.\xc0R\xeeD\xc8H\xa3\xc7\xb6\x95H~\xc9\xc5=\x1f\xef\xd99\x9a\x88\x13a\x0dm//@F*%\xf6\xeb\x8a\xd4\xcdU\x0e\xe5\xeb\x84@\xb9N\xf0\n>%Q(h\xa9\x19\xe3\xc2\x97\x05z\x02\xf9\xe5H!\\ \x8ew\x8d\xe4Xj\x9b\xdb\xe0Qe\x04\xba\xb1/\xca$\x9f\xad1\xd2\xb8\x18\xe9\xbc\x874d\xc1]\x81'\x10\xf3{\x13\xac\xc0\x17A\xa9\xc3*\x89\nI\xb5ga\x1e\xde\nI'\xe0\xcc\x1f0G\xd6-\xd6\x1f\xb5\xd8\xb3\x0fQ\x13W\x90\xb1\xaasd-\x9d\xb3\xd1\xa2\xee\x83 \xd9<\xfdn[R]\x15T\xe7f!\xd5$\xf0y\x96g\x0b\x0c\x8a\xab}\xb4\x86Z\xfe9\xf9\xd1\xe9\x01 \xa7\xa9b\x11I\xf3\"\xba\x82\x87\x7f0\xe1\x16\xb7\x08\xa4\x15\xddntP\x04I\xa6\x95\xab.\x8f\x04$.S\xacnW\x12\\b\xf0deC\xdb\xde\xb2N\xbf.h\x89\x1bU\xe22\xfc\xdcg\xe4k\x82+-\x1a\"\xc8\x7f\x8d1\x80\x17\xc7K~=\xcd\x99\x1b\xef2Z!w\xb3B\x86\x92q-\xfe\xc2\xd7[\xe1A\xb3\xd8\x83b\x80\x83\xc4\x83\xbbI\xa0\xbc\xc8\x93ne\xb9\xb3D&\x9d%6F\xbfF\xf1`\xdf\x18\x11\xbe\x8e5\x0c^\x87\x0e1\xea\x16\xac\xe65m0D?\x0ey\xaf\x86]\x9b\xf9\xfe-\x89Y\xc6!X\xc7\x07_3FP\xc7\xd9\xb9q\x88r\xcf\xad\x19\x90aC*\x1b\xce0P\xc5\x1a\xa8j\xe4\xd37\x8d\xbe\x9d\xf2\xc4\xe9x5Y\xe9\x05;\xe4\x1e=\x92\xd6CDc=\xd4\x06b\xe6%\xebxP5{x \x0bdC\x169{\xc1\x1f\xb8}\xb8A\xd4[\xf7z_\xbc\xd9\xeb\xb3\xb3\xe3C\x82\xf3\xbe\xae\x98\xd3TLf\x02\xf4A\xe9\xc1\x1a\xc6\x8c\xb5\x1e\x8b\xb70\xc4\x88\xcc\xf1\xa8\xd8\xe2\x9c\x85M)\x0f\xecA\xed\xcd\xaa\x0fa\x11=\x01\xb6Q\x18\xc7\xb0\xca\xd9\xb8\x96\x83\xe7Zo\xf9\xe6\xc8\xfa\xe6Z\xf0\x8ccA\xed\xd60\xd1M\x17\x90\xee\xd8\xdaix^\x1e!\xb7\x16\xee\x0c%\xe9\xea\x8b\x83\xbbj\xfe\x05\xd5M\xf8\xdc\xfd\n\\e\x9f\x8fB_\xaaj`;\xa3\xb6\xa4\xd3(@W\x8ek\xc9A=P\xbc\xd53'[\xcf\xbe\xfez\x12\xdar\x0bUi!\xc6\xec\xbd\xfb\x9a\x0b\xc76\xe3\xb1\xb0\x1c[\xdc\xa0\xdf\x9a\xf2\x82\xd5\xfb(8\xf6\xd2\x821\xee\xbe\x01,e\x9e\xa5\x00\x8cE\x17\x18\x97\xe6Y\x85D\x19\n\x863\x0e\xa9\xd7\x8d\x83\xb7\xe6\xf9\xd0#1b4\xf6\xe3\xb2\xc3H\x88_u\xf0\xf2}\x94Kt\xfb\xfb\xfb%\xc3\xdfG\x8f\xb8\xf1\xe4\xc4\xca\xefK\x1f\x9f\x82\xe3O\xfcp\x19P\xf8[\x16\xb1\xaab\xedEBJ\xf3,5\x1b\xe9!b\x86\xbe\xd3o\xb1ST\x01\xc3\xb0k\xb69z\xb4P\xd3}\xfb]\x13\xa29\x85v\xd7\xb4\x18\x8fU3\"|W\xb3|\xd0Z\x8a6t\xabC2!>\xaa\xb16e\x9b-\xf6\xa2\xae\xab\x9bvW4\xae\x8a\xfd\xe6}\x98\xeb53\xee/\xca\x90\xfex\x9a\xcd\xdc\xd2\x01\xf3\x01}G\xd4I\xb6h\x11%\x9c\xd1\xa60\x83\xc3`\x93l/m\xa2+\xf1^.\xcal\xc3\x18\x9e\xee\xe4?\x99\xd80t\xe1%\xfb\xaf\xc5]Y\xc4/\xb4}n\xb4\x1d\xb1\xf7\x9eC\xb4\xb1\xe1b\xef\xaf\xda\xc2\x8a )0\xc1f\x1c\x1f^\xbc\x80m\x17z@r\x91*\xdf\x81\x97\xf4\x96\xcc\xa9\xe7\xafH`wiR?*(\x0f\x1c\xbf\x82/f\xbe\x85\xc3RR\x81\xab0\xba \x81&\x1eY\xd3\xdc\xd8\xd3\xd6u}g\xd8)iVPR\xbe\xf5M\x94\xb4\xde\xf0w\xa2\xa4\xf3(\xbbhCI+\x83i\xc1K<\x84\xb4\xeaG\xa1%\xad\x8a\x1aG\xc95o\x0e\xbd\xc6!\xad\xa7\xaa\xdb\\\x87\xd1|\xf1\xdd\x86\xaa\x1a\x1aie\xee\xc4M\xe0n\x85\xf5[\xe7\xc4\x89\x19\xd9l\xd3b}0\x0f2y\n|\x92<\xc8\xe2Ic\xfc\xd8/\x9b:)*\xf5J8\x16\xd5\x10\xf2q\x16\xe6j\x80\xb9\x18G\xc5(N9\x93T5}8\xab\xde]\xd5\xd9U\x86&_j\x8a\x82ZWO\xea[\xd9IiV\xce\x99/\xba\x19z\xdd:^3b1\x88\x9c8\x1ew\xfb\xe4D\x1a\x85\xde\xad\xa7\xc5\xf7\xedM\xa5|\xab\xf8.\x15}\xf8cW\xad\xf4L\xf9\xae\xd4\xd9\xdaS\xea+\xe5\xcfx\xa8\x07\xcf\x8a\xe5x\xe2\xec*\xdd\x0b\xb5\x99\xc7u\xf4\xb7\xcd\xdbHHg\xf7\xf7\xdc\xbe\x8f\xa1y\x8b\x8d\xd5\xcc\xaeD\xe8K^fw\x85\xd5\xba\xd8`\x9e\x95\x0b\x11\xd6\x19\xd6Dp|A\xbfh\x8a\x16\xe1YI\xaf\xb8\xb5\xd3v\x10\xf6\x01\xa0\xafL\x8b>\x9b\xb4\x12\x8dGM1G\xafY\xfb\xc8\xda\xbc\xc1\x8a\xcdV\x10Y\xaef\x91\xd74\x8a\xf1Y\x90\x17p\x95\x89rrn\x8cjw\xd4\xfb\xf6\x04o\xf2C\x14\xf9\xfd\x8b\xb5U\xe2#S:X+\xda\x839\xab\xc0\xe7\xfe\x1f\xdcx\x80\xd1'u%\xc4\xfduI\xe7\x16|{=\x8e\xbe\x14/\xc08/\xc3\xe9gg$y\x191\xde\x0d\xc8\\\xdb\xe6t\xfbp((\x9fS\xae!\x0c\xcd\x0c\xcb\xd1\xe0\xf2`:\x11\xabC\xedtr2\xc2]\x82\x05\x99Y\x94\xe8\xcb\xba\xaeQ\xe1\xacH_ZQr\xf2\xf7\x87@\xa1\xdc\xd1:\xf7f\xc9\x8d\x0d\xba\x93.\xea\xa6,u\x95\x12q\xb3[\xd8\x81\x15gur\x19e\xc1\x1cmu.\xc95\x05\x12\xdeI\xcbk\xbc\x84\x95\xfe\xde\xad\xaf\xbb\xf3{\xc5Buv\x9a\xcf\n\x8d<\x85\x8dg\xa5i1\xean\xa7[\x14\xe8\x9d\xcd\xba\x93n1S\xab&y\xc9ugw|\xed\x85\x11\xd2\xe9\xdd:OZ\xf7\x1c\x96\xf0\x02\xee\xd8\x1f\xf4\x1f\xb7\xd2\x1c\xe7\xa2\xde\xcet9s\x072\xe0\xbb2u;\x9dPp\xe2b\x90'lW]\xd3\xe4:_\xf0\x1b\xe6/\\\x82o\xbb\x7f\x05\xb1/\xb1t\xe7\xb6`T\x0b\x86N\x19\x13\xbfw\x16\xc7\xdb\x91\xf0\xf0;\x9a\x863\xa9cc\xf4\xf4\x0f\xa1q\xe0\xf44W\x82\x15hZ\xd2<\xfc\xc9\xdcy\x99\x1e\x0c\x15\xd1H\xec\xf7\xc2=\xdfN(\xdaV\xe4\xf1\x1c\xdaW\xdet\xcb\x11]D\x84\x07u\xdc\x0c D\xb3W\x13T\xd0\xadH\\\x8b\xdb\xf2[\xc1\xd3\x8bi\xa2\x9d\xc6Z1N+\x03\xa6N\xa4\x1f=\x82%w\xf0,\xaf\xbd_^{\xc8Cq\x84Q\xb8qp\xf2\xea\xed[%\x9eL\x02$\xa6\xe0\x87)\x8d\xd71E\xc7\x87\x04\xc5\xad<\xe8\x9c\\\xda\xa4\x166\xa0\x85<;\x81\xed\xddf \xbb\x82\x15h\x80\xb0RA\xf1\xa4\xdeP\xa9d]\x1f\x1a\xc5\xa8\x0b\x15\xe8Yxp\x94\xd6\xc3z\x18\xff\xd5\xd1Fa,bAQqv\xa0\xcc\xc3\xce\xc8\xa1\xe4\x17\xf2\xb8v2d\x0c-\x03\xa0\x98\x02\x82@\xc4\x92\xb1Wrhn^\xd0\x87\xdd\x9d\xcd=\x11+U}i(k\xb2r\x8e\x15#\xb7J\xfb\xaeE\xde\xe9\x90\xde4\xdf\xaca\xe6 \\B\xc0DL\xf8[F\xcfds/~\x08\x96G\xd4Id\\\xf6T~\xbd\xbfg27>,\x02Y\xb2\xe7\xc5\xafr\x13\x9c\x13\xc1*\xe2\xeb\xfd=W\xeb\xb3\xa7\x18\xa0\x8a=\x93\x91\xaa\xf2'9\xbb\x86o\xca\x1f\xe5\xb6KB\x8cL\xc2\xcd\x07\x8a\x81\xc0\xfd\x80\xce\xdf\x8a:2\x97 \xe7\xdf\x0d\x95O\xf9\xd3|\xe8\xb8v\x052\x88rE\x171\xccG\x8b\xea\x08\xf5\xa7\xd4H\xa8e\xaa!\x10O\xf7,\xf7'\xf2\x17eB\xcb\x97S\xc3\x04\x86b-\x11\x93\x86\xdd\xaev\xe5\x97s\x93t\xf2\xdc$EZ\x12_3#%$V\x11\x82-\x86\x17\x10\xb1?<\x04[\xea\xf8\xd3xf\xa7-?i7\x9c\xdc\x99\x7f\xd5\xad\x1f\x1b\xb1p\xe8\x96\xd9P4\xfb\x95\xd5\x1a\x89%\x95\xb5$X\xa7C\x8dOA\x91\xc9!r\x8a\x8b\xc3\xfc\x86>\xa7\xa0~\xa8P\xd7>\\d),\xa2\x8c\x9drQL\x1f\x94\xc9\xa1He\xf0K\xbf\x9e\xfa\xe0\xa7\xbe1kA\xd3-D\x8b5E\x94\x89\x07\xf46\xa5\xe1\xdc\xa9\x83\x8fo\xea1\x90\xf2|Xg\x95\xe5\x90\xc8\xf7\x85\x8d\xfdI\xf9\xa9M\xe3`\xa5\xccb6?}\xe9l\xea\xf1\x81\xbf>c\x81.\x98h\xe4\x94B/V\xa7\x81tL\x1c$\xf2l\xb9\xc8\x16\x0bN\xba\xeb$3,\x93\xccX\xfc\xf4\xa2 [\x85\xa5@\xa7\x05\xde))\xd8\x07K\x9a\x9e\x84\xfezM\xd3&\x00\xd7\xcc\xd5\xeb{\xb1\xa3\x0c\xd7U\x95\x06:\xd9\x1bD\x00\xf8m\x85c\xd8\xdb\x11\x11p\xc4\xadKi\xb6\xc2:\x80\x1d\xe7\x1b|?w\xcf\x86g\xf1Y\xf8\x7f\xfe\xb7\x9aU\xa0;\xf0\xc39\xbd=^8\xcah\x90\x8a\x1f\xa4N\xc4\xef/\x0c!\xab\"\xd8@2^\x06\xf2\x06\xf6\x9b\xc2\x13\xd8\xe4\x9c\x87^X\xc3q\xc3`0\x00\x1c|o\x1fv\xf4RJ\x1bw3\x04\x91/ A\xea\x90 \xf0B\xc5\x0d\x85\xbd\xfab\xd0\x10#X\x1c\"\xc8\xf8F\x052-\xa0\xe2\xabP!\x0c\xbe_\x01\x15\x81Q\x99\x84\x87\x98\x00\xe7\xea\"\xee\x8aX\x98R\x02\xaa\xa1\x84\xe4\x95\xa1\x01x\x8f\x07\xcc\xefUkAO\xb3\xe6=\xe5\xbc\xe8A\xf7\xf7\xaeJ\xa0\xd4=\x94F\x9c\xfb\xb5\xe6\xe6UB\xf6u\xbb\xda3\xbe\xd8\xfa\x8caE\x0e\xe2\xb1\x1fr\xe1\xb1x\x86\xd1\x92\x1f\xe3U9\xe3XH\xca%\x186)\xa7\xa0\x04(\xd7\xf5\xd8\xdc\x04%(\x9e\x8b\x02~\x05\x82;\x10\x85r|VP\x03G\xa8\xa8x/c\x0e5\xd4]j\xc9tNi\xbe\x92h\x8ev\x953Em\x9d\x9d\xc6\xb1\xa3 \x87\x93\xa4q\xb7_\x81\xf5\x95\x1f\xce\xc7\xc5}n\xe9Y\xae\x90\x1d7\x98w\xd4t\x9e\x98D\xa2\x94\x8b\x00\xca\x07\xbb\xfb/\x82\x00\xfd\x9b\x11\x02\xb9c\xde\xb7\x85A\x95\xb9\xfe\x97\xc3`E\xd6&\x18\xe4\x8e\xb6\xdf\x16\x04\x15\xd7\xd0\x7f=\x08\xd8\x08\x1f\xb4\x13\xc4\xedA\x13\x00|\x19\xbe\x07Ek\xabm\xf0u\x9e\x8cR\xc8\x01&h\xca\x98\x9d\x8f\x1eA\xf7\x7f\xc4\xcd\x1d\xf2\x02E\xb9\xd3\xc5 \x15\xcf\xbaG\xd5\xdf\x9f\xde\xbd\x13\xbf+\xbcv\xf3R7\xac\xb4\xad\xb9uL1\x10Y#\xe0T\xcc\xc1Q\xdaZ\x8d\xe9:\xa6 \x0d\xd3\xb1\xa6%\x8f\x84Q\xe8{$h\x98\x01\x14\xbdv\xffG\x93J\xb3~5\x12D74\xf6HB\x1f\xd02\xaeK\x9b\xc6\xb3\xf5\xfa\xc1\x8d\xe3\xa2\xb6i\xdc#+\x1a<\xb4q\xfd\xc8m\xeb2\xa7\x0b\x92\x05\xe9Iz\x17\xd01tsxu\xff\xe5\xfb\xfd\"\x8a\xfe\xa9\xfb]c?\xd5z\xbf\x97\xf6u\x1agT\xdd\xc7\xa7\xd5\xdf\x1f?\x1d\xca}\xcd\nv\xd4\x97\x17$HJ\xb5\xdf\xd4\n\x0e\xde\x9d\x1c~)]\xb0m\xe4\x87\x0c\xfc[\x12\x90\xeeT\xa4\x13\xf81\x8a\x02J\xc2\x19\xef\xa3\x96\x9cN\xb2\xa12\x03\xed\x17\x93\x1b\x1dQ0&\xc8\x95\xf6\xa00\x91\x00\x1a\x83X\xa56\xdbXG#Z\xf5\xc5\x81=\x96\xeb\xdd\xa6/\x1d\xc9h\xd7\x97\x9c\xd7\x1b\xc3\xbc\xfe\x1d(\x88)C\xe2\xee\x03\x93\x9c\xd6\xb2\xa7\xed\x14\x03\xd54D\xda7\xb4\xa74$\xbfUI]\xa4#u~\x98\xfe;P:\xae\xb4Q5\xd8Z\xcc\x89\xccn\xf5\xba\xa8\xde \x95'q\xa3ylw\x83\x1bB\xf1[\xd4i4C\x19\xad\xdb\x13y\xdesY\x8eN{\xbdh\xe6\xf6\xa1;\x14\x99\xfe\x8d\xe29j=z\x82!\x8b\x1b=\xbfp\x14\x17\xbcQ\xb5+S\xfb\x90\xbby\xf4z\xa4\x9fb\xe6\xb7\x959\x8ev\xddA\x1a}b\x02\xe9+\x92PG@\xa2\xb1\x9a\x0526\x1c\xab\xc8\x85b*\x15I&aO\x0f\x02\x9f$4\xb1\xe1\xe2t\xb3\x0f\xdd\x0b?\xecjR \xe4\x98>\xedC7\xf2R]\x95\x1c\x8e\xd3\xd1\x10\x13Uy\xbaZ%\x88OG\xbb}\xe8^\xd2\xdb\xee\xf7\xbd\x0b0\x8b\xb5\xe5b_\x08\x90\x1f\xe9\xf2\xf0v\xedt\x7fw&\xe3\xe9Fo6q&\xe3\xe1\xfdt\xb4\xf1l\xc6\x8e\xd8\xf3\xd9\x0f\xae3\x19\x9f\x9d\x0d\xe4/VaJ\x0fgXY\xa4\xc4\x9d\xdc\xe7\x15z\xda\xc7\xc5/\xd1\x8c3\x19\x97\x0f\xf2\xa2\x07^\xf9\xecl\xe0L\xc6~\xb8\xb8\x7f\xcb\xfe\x1d\xbdq\xefyQH\xc2\xfb#rt\x7ftp\xe4\xba\x7fV-\xef1.?&\xedU:\xa7O\xcczB\xad\xf0\xbc\x08\"\xf2]\xc4gU\xbf\xcdoF\x18\xa5u:\xbe\xe0`\\\x95\xf9\xa1S\xd5zo\xf6\xcdy\x1am@\x189B\xd8\x07\xc9G\x08\x03\xe4\x1a;2H\xa3w\xd1\x8d\xdc\xd2\x8c\x97\x80 ;\xc8\xc7 b\x00Og}\xe8\xf66\x94+tdX^\x8a\x13\x86\xdf\xa1\x16\xccH\x1fX\xcdE\xc1{\x08\x0b$\x98\x88\xc3l\xf0\xe1\xf8\xe4\xed\xe9\xdb_\x0f\xcf\xdf\x1e\xbdy{\xf4\xf6\xf4\xaf0\x96\x8f\x8e\x0e\x7f:\xa8>\xea\x0eB\x12\x16\xcd\x1d\x91#\x18CZf1\x04is\xd2/\xe33\xa22\x9f\xf1\x86!\x8e\x95\xd3\x10\xb6w1\xe74\xa2\x07t\x95JN#f\xaf\x9b9\x8d\x10~`|\xf3\x18\xbf(\xa3J\xff\x9dx\x0d\x873\x1b\x9d}\xee\x8d\xa1\xe15\xda2\x1b%Bi\xc2\xf8P\xaf\x1c\xf2\x93#r\xc4\xfa\x82\xe4\xc6O\xbdKp\x8c\xca\x03\x8f$T\xd5D\x8e\xb5\xb5@\x01\x0e\"\x9f^<\xe2\x8d\xe5z\xdc6\x8d\x1d\x1d\x1cY\x1b\xcb\x15\xb5\xad\x1a#G\x1a\x8dl\xe1\xf8l\xdcnB\xeb\xf7=\xa0\xc5v\xfe7\x83\xd6\xdb\xa37\xdf\x0eZo\xc3E\x1bh\xd5)\xd0\xf7\x83\xd6\xc67\x05\xd7\xc67\x85\xd7F#\xc0t\xbb\xbdx}8\x18j\xc6\xa2\x9cKe\xbe\xb7\x0f$\xcf\xe95\x810?\xa6\xba\xb4\xcb\x0e\x14\x1e\x083\xb4\x11\x93\x7f\xd6mC\x8d\xff\x8aj\xfcW\xce\x1e)\xff\xb9\x1b\x8e\xe9\xc7\x9f\xbb\x8d\x1c]c\x8b\x93\xca/\xc6\xbb\x9d\xa6\xb3\xfb)\x9c\x9d\xa5\xb3\x9e[z8V{/\xfd\xe0\x0c\"/\xf9\xc1\xe5\x1c\"\xb6\xf0\x83\xf3\xdf\xf7\x0ec\xc6\xdcj7\xa5\xf7\xdd\x89\xebNJ\xac\\\xab\x1b\xdd\xd4_\xd1$%+\xa3)\xcb7\xe7\xd6\x8a\xb0\xe5\xd1\x80\xdeRO0my\xa9/K\xbf\x03\xbf\xa6\x89\x87b\xb85Y\x0b\xf7L\xfd\xb9\x97\xdf\xe0 \x0b\x96\xcf\xc3\xcd\xb9\xb2b\x12j\x9erW1\xf3>\x8c\xe3(v\xba\xafIJs\x9fZ\xca\xcat\xc1\x99|\x91W\xb4\x97NG3\xce\xfc\xf4\xd2\xe9\xe6\x8c{-\x11\xfesk\xd6\x87N:\xdd\x9e\x15f\xb0\xf4\x06X\x07\x0e\xfbo\xf0\xe9\xf4\x95#\xc0\xa0\xf3\xc3\xf3E\x98\x8a\x1ek\x82G\xa9\xe8\xa5\xd3\x9d\x19\x8fO\xd1K\xa7\xbb\xb3>\xa4\xd3\xbd\x99\x89\n\xa3\xca\x15\x03\xdfN\xf7f\x82+\x1d\xf6a\xcb}\x0e\x8b\xc2\xa7r\xeb\xb9\x0b\x0b4\xf0\xd3Q)l\x87u\xb7\xa8\xd3?\x13z\xa5\xd3g3\x04<[\xb3]\xba\x0d?\x80\xb3;\x84\x1f\x10Z\xc3\x19\xf4\xa0\xe7\xa4\xd3\xd1h\xc6\xd0l(\x95\x80\xb8 \xea\x9b\x1bkW\xc4g0\x82M\xc1\x9e\x85\x8bQ\xd5\x1f=\x02o\x90\xd0\xf4\xd4_Q\xc7\x1b,\xc57\x1760\x88\xa6gCa?LR\x12z\xf4x1\xc6\xeeZph\x96M\xc6\x88\xfa\xdb\x93cA\xd7\x8d\x8e\x00\xdf\x8a\x10?\x90\xcc\xf0\x04\xfc\xdf\x8f\xc4t_\xbcP\xac\"L\xe6O\xdf\x0e\x0c\xc5\xcf4\xbe\xab\x0c\x8b\xc3hg\xdb\x1d\xfc\x88\xb6\xc2E\xaf\xe0\x11dd\xd8L>\x97\x1a\xb4(\x18\xba\x07?\xbez}\xf8\xe6\xa7\x9f\xdf\xfe\xe5\x97w\xef\x8f\x8e?\xfc\xd7\xc7\x93\xd3O\xbf\xfe\xf6\xbf\xfe\xfa\xdf\xe4\xc2\x9b\xd3\xc5\xf2\xd2\xff\xe3*X\x85\xd1\xfaoq\x92f\xd77\xb7w\x7f\x1f\x8e6\xb7\xb6wv\xf7\x9e>\xeb=\xd9?\x0b\xcf\xe2\xee\x03%x\xae\xe4\xf9\x1e+\xf6\xc57\xe0\x06J\x1d5^\x8e3\xfa\xe8\x1b\xae\x88B\x1e\x030\xe4\xbeC\xa1\xed\x9e\xa8\xe3 i'\xb9\xfcK\xa5\x19;\x8f\x06\x08\xbb\xdb\x8d7G)\xbc\x80a\xab\xdb\x1f\xd4\x8b\xefj\x1f\x1b)a\x0c\xff\x01OQ\x01]\xc6\xfb\xaf>:\xa3\xb2\x02cz\x16\x9f\x85\xfb3\xa1\xc60\x03=\xb2.K\x86\x91\x80\xb4\x8f\x12\xf3r\x07\x86;\xa1\xdc\xd3{\xf8\x1c\x18\x94\xc9sH{=\x17R\xf8\x0f4\x05\xe3*\x13~\xa5\x13\x88L\x11\xf0\xf2%\x8cv\xe1\x11l\xee\xec\xb8}P\x8b\x9fVK7wv\xe0\x11$\x8c\xec'\x98\x0e\xe4\xc5\x0b\xd8\x85{\xc8rt\x88$:\xa4\xba\xe3U,\xd1\x10dH\\\x82\x03\xfb\x01v\xf1\x9a\xe6\xab\x86\x04c\x18=\xcdu=\xe5\xb6\x86\xda\xb66E)\xbe*|\x0f\x19h\xd4:\xdb\xf9\x9b1\xa6\xdfX\xc4\xd1*\xff\xe2\x04(\x16 \xbd\xc7\xaf\xdf\xd4~\x15C|0)\x87S\xd0\xf67'm\x11:\xe6n.F\x82b@>\xd2Hk2\x0b\xad1`\xe7V\x05;q\xe7g\xd3\x08\x97\x8f-\xfa\xee\x16\xf2|J\xe9\xa6\xaet\xb7R\xb8\xbb\x05\x8f\x00Mr\xd8\x8c\x9c\x88a\xecS\x17z@\xa7\xa9\xf9R\xb5\x8c\xa0[\xfc\x0e\xf1\x1b\x8f\x08\xc6\xb0Y\xa0k\xa9\x9d\xa1\xae\x9d\xedZ\xe1\x8b\x17P\xedqw\x1b\x1b\x1e\x15\xc8\\j\xb9>\xc0\x17/j\x0d\xefn\x97\xdb\xebC\\F\xbc\xfc\xd7Ws\x10f\x89\xb6\xa6\xff+\x87\x9c\xacs\x08F\x85\xe1\x03\x99\xb4\xc8\xe2\xd1`\xf0\xea\xf8\xca3\xdfd\xcf_\x91\xd7\xb8*\xdcx\x1cP\xdb~\xe3\x97\xd2A\xee%\xccv_\xf8\x9c+\x83\xcd\x1ed\"uh0MgE>\xb0\\]\xcb\x01>\xeb\ny\x15\xd5\xb2q\xb3Q\x87\x88\x89\xe3\x87\x10\xdb\xadx\"\xd1$Jj\x16\x8eB\xd6\xcf\x1a\xbb\x96\x9f/\xb2\xd6A\xe6\xa7\xb9\x0fVM\x98!$\xf9\xa1H\x9a\xc1\"\"[\xb4\xca\xdf\x91#Ny[~!\x83S\xd7O\xfc\xb3\\\x8dZ\xec\xfa/\xdc\xc4k\xe2\xc7\xc9\xbf\xd7.\x16\xbe\xbb\x96\x9dJ\xc4\x8c\x0e\xe2\x98\xdc9\x99t\x81\xcco{\xd8\x16\xce\xbel\x0bg\xb8\x85\xf5[7j\xbdu}\xf4\xe7G\xc3!\x85\xe2^\xd1\xbb\x84\xbd]u\xf17\xb5B\xa6\xe9\x8c\xd12\x7f:d\xe7\x0c\xfe\x9d\xcd\xfe\xe9hoXG\x1dW}]\x0d{&R\xd1\x18\xd6\xd1/\xad#\xd1\xae#1\xad#[-\x82\xab\x15\xd5@\xdc\x07_\xc0.\x12\xb0\x8b\x10vF6\xc6\xff7\xd8\xc1\xe5s\xfb\x81\xfb8\xa1\xc6\x0bt\xbdw\xe1\xf7\xdb\xc4\xd6#\xd6\x0f\xc1\x10\x08L9\xc9\xc2\xbe\xb0D\xccIm8Mg\xd6\xfd\xf2mQ\xdeD\xe9\xff\xed<*\xffH\x9ed\xe1\x9c.\xfc\x90\xce\xbfR\xfbb\x81\xc3\xc3\xa1\xea\xd6\xf2\xcd?T\xa6\xbb\x8e\xfc\xb9\x8c/f\xeb]'\xcd\xd94\x7f\xffn\xae\xd1\x7f$Ob\xba\xa4\xb7\xdf\xe5F\xe5\x01\xca3\x1f\x03\xd5`\xbd6\xe7S\xeeW\xa7\xe7\xb3\x19\x11xr\xf6\xc4\x99.\xfd\xd5\xec\x07\xf7\xcfO\xe4\x05\x87\xbez\xac 9\x00\xd2z\xfa\x89\xd4\xbe\x0f\x8dw \xfc\xc2C\x9a\xf2\x86\xd3\x11\xcab\xf2\x16\xe1%\x93K[\x9c\xd8\xac'4\xeb\x9d\xa6\x85!P\\\xb2 *\x9a\xa9\xb5\xf2\xbd\x8f\xe1\x7f\x0e\xc4\xe56Q\x80\xceo\xe1\xaa\xd0-\x19\x13\xf5\xc1\x001\xbc\xd0*.H\xd3~U\x96\xf9J*\x913j\xbc\x83\xb6&1\x0f%(\xd6\x05a\xb0\xea\x01\x1d$Q\x16{\x14z\xac\xc0\x08X:X\x06\xd1\x05 \xc4\xd5_o\x1f\xbaK\x1e\xb9\xaf\xc8D_\x11\xf5\x9fV\xca3\x9b\xd2\xaf\\5i\xd6.\x94_\x08`\x1f\x9eU\xc8 \xec\xc3\xa8r\xad\xb5\x80}\xd8\xda\xac`\x03+\xdb*\x97\xcdY\xd9v\xb9\xec\x92\x95\xed\x94\xcb\xaeY\xd9^\xb9l\xc5\xca\x9e\x96\xcb\x96\xac\xac2\xbe;\xd8\x87\xed\xcaX.XY\xa5\xdfsVV\xe9\xf7\x06\xf6a\xa7\xd2\xc7!\xec\xc3n\xa5\xbd[VV\x99\xdb +\xab\xf4\xf1\x8a\x81\xaf\xe2\x93x\xc5\xca*\xef\x1e\xb0\xb2\xddr\xd91\xe6/\xacT\xfc\x80\x85\x95^N\xb1\xb02\x95\xf7\xb0\xafA\xfa\xe1\x18\xbaggC\xcdQ\xb4\x87O\x88\xe6\xc9S|r\xa1y\xf2\x0c\x9f\xa4\x9a'#\xdeQ\xa8{4\xc2G\xd7\xbaG\x9b\xf8h\xa1{\xb4\x85\x8f\xaa\x0c\x1d\xfbl\xf2\xa1Wu\xd1\xec\xb3\xb5=\x86\xc7gg\xdd\xc7\x9a\xb1\xf3\xbe\xce\xce\xb4\x9d\xf1\xde\x8et\xcfv\xf9\xd4\xceu\x90\xda\xdc\xe2\xad\xbe\xd3?\xe4\xad~\xa8(\x1a\xcaU\xdf\xb2\xf3\xba{\xd7\xedC\xf7\xaf\xec\xbf;\x9a\xe0w\xf1\xe7\xf0\x84\xfdA\xb6\xb7{\xcc\xff?b\xff\xe3W\xfe-\xc2\xaf\xfc\xffc\xac\xbdX`E\xf1\xe7\xcd\x9b\xeeL\x17U\xe3\x8f:\x9d,\xb4\xb6\x95\xabhn\x82\xb2ou-\xeb\xf3\xc8\x19\x9b;;.\xe7\x85n\xbb<\x80\xeff\xb9\xad\xdc\x1a\x19\xab\xef\xee\xecl\xc9\x172\xf1\xc2\xb6\xe6\x05=\xd7\xde\xe1\x8dlo>\xdb~\xb6\xbb\xb7\xf9l\xc7u\xcb\x11q\xbdhNa\x1d\xf9\xa5\x8c\xb9<\x00\xe2\x8a\xdc\xc9L\x0c\xcb\x98\x92\x94\xc6<\x19\xc3\xf0\xf6\x8d\xf8\xe8X\x07\x1c\xe8'1\xd0\xa7\xe5\x95-\xfd\x92\x87\xde\xd9YW\x84u,\xe28\x0e\xf1\xfd\x8d\\Vv\xa1\xa7\x08p\xba\xc8%G\xf5\xc5R\xa2X\xf3x\xe1y\x98n_\x06\xc9\x961\xa7\xdf\x93\xf4r\xb0\"\xb7\x0e\xa6\x0c\x17\xc5\xf7\xf7\xb0\xe9\xcah\xdfW\xfe\xfamxM\x02\x7f\xce\xdbR~\xab\xa1\xb9\x17At\xf3\x8e^\xd3\x00\x99X?9\x8a\x18L\x97\x0e-\x9e\xb8\xd2\x17I)\x93\xbd\xa4w\x81\x08\xc1]:YMLu=%p\x93Ym\xe1\xdb\xff\x8f\xcf\x06\xcds(\x12\xa2pk\x0d\x9e\x845\xae\xdc\x1b\xa4\xf9\xd5\x0c\x8f\x04\xe0?\xe7ARG\x90\x89\x86X?\xac=\x91\xe4!\x18\xa8>\x97}\xc8xg\x19^\\\xab\x8f\xa6\x19\x1b_8%3\xd8\xaf\x06\xc3\x05E\xcd]\xc6gGA1\x868\xd8b\"\x0d%s\xdc\x89\xe2\xf4\x17z\xc7\xb3\xcf\xe4?\xca\x01\xddC\xfa\x9b?\x97\x01\xd5\xf3_\xf7\xf7\xf0T\x86C\x0f\xa3\x8ft\xc1\xdb\x10_\xd5\x16\xc2\xe8U\xb4Z\x93\xf4=\xdb\xce\xbc\x8eR\xa0\xd6\xf4\"\x86\xdd\xe8zu#@\xa9\x14\xa85\xbf \x84\xbcLOd{\xe5\xf0\xb6\x1cu\x1e\xd3`\x85E\xe4\xfaR\xb6F,\x99g\xec\x0d\x92Ra\xaf\xc0K\xb3\x84\xce_\xabOJ\xb1\xfet4\xe2\xa3v3!\xd2\x8b\xdd\x14\xc1~%\x9al\xea\x8at\xc6\xfc~nc\xc4\xf1\x9a\x8d-Q\x83\xa5\x81\x0f/ y\xeeb\xda\x064`\x97\xd9\xfa\x85K\x1f;\xfb\xc1w\xd1\xec\x87\xfb\x8a\x88\xac\x16\xa2\x83\x04\xb3\xbd\x95\x9e\xb0.ydW\x1f\xad\x86\xf8\xf7P\xd5C\x9c Q0\x14x\xdd\xdb\x87\xc8eC\xec\xedW]\xcb\x04\ngV\x10\xbd\xb6\x85\xe3\xd6\x87\xdb\x95\xe4\xf2\x07H]k\xdb\xef\xea$Z\xca\x1c\x08\xb1\x05\xc3>\xfe\xd5\xbe\x8e\x9f\x8c\x0dmm\x96\xa3T\x8d6wQ~\xdf\x1dU\xc3`m>\xdba\xbf\x18\x87RxP0\x96D\xfc\xba\xbf\x87\x9d\xbd\xad\xed\xed\xf2{\xec0\xdeb\xbfx~\x8a\xbc*+\xdf\xadt=\x1am\x8fF#\xebD\xfef\x9c\x08N\xb1\xd2\x0f\xb6\xcc\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebM\xf1\xf5\xd2:\xac7\xc6a=\xf9\xfd,\xfc\x01dT\x13u\xb9\xe57\xb6\x91\xfe^\x0f<\xf2#cs\xcaE\xbf2Y\xa5\\\xf43\xe3m\xcaE\xbf\x01\x06\x99\xae\x0f\xf2/\xf6\xd0\xebl\x1c\xbej\xe7\xd4\xd1\x84B \x0c\xe5\x0b\xdc\xe9<\xeeG\xfd\xe9{N\x07j\xe5\x8cS\xfd$\x12\x92\x96r\x96TV\x12\x83\xf3t\xde9\xfc0\xca\xb0\xec\xbc\xf8z[|\xbd)\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebe\xf1uU|\xbd+\xbe\xae\x8b\xaf\x1f\x8a\xaf\x87\xc5\xd7e\xf1u^|\xbd.\xbe\x9e\x14_\x0f\xc4\xcc\xcc\x89^49\x1f\xd2\xbaJ(7y\x18r\xba\xaaP\xd9^\xcfv\xb3\xd5\xf9$\xc8\xae\xd2\xbf\xafD\x05\xfaM\xaf\x04f+\xf7\x96\x8d\xfdoZc)\x13\x83\xfd\xc5\xc3\xd4\x0e\x12 \x9f\xe7rd\x1d\xf6a\x01hQ\xcdX\x15\xe4Ya\x03\xde\xe3\xe9\xf2\x92[\xf1vA$\xd2\x9c\xbeg'\xc3\xac\x8f\x88\xe9\x1b\xf4\xdc\xb9P\xc1@\xf4\xb5\x00\xd1n$\x1c%\x0e\xbaq\xa8\x7f2\xb7&\xc6\x85\xdcM\x00\x13\x08\xe1%<\x83\"\xed\xd2o0\xc6\xf2\x9fa\x0c\xbf\xc2\x98\x8f\xb2\x13\xf1\x87\x7f\x871\xfch%m\x7fU\xa8Fu\x85\xe8`\x9e\xadJ\xbc\xb7\xe9.\x84\xdf\xfe\xa6\xd5\xdb\xdf\xee\xe3\xc7\x86\x9b\xd9N\x85!\xe3\xa1\xfd\x19H\xde\x16!\x08\x14W\xd3\xc7\x18\xa0\x1dz\xec\x9b\xfeF\xd9\xcf\xb9\x0b;\xe9\x94\xfc\x17'\xed\xf3$\xc6\xbeH\xdeL\x14\x85\xa3\xd1eY\x80\xb0Q~\x92\x1f)G\xe97\x02\x94\xdcYd\xc0H}\xa6\xd9\x90\x87D\xe3\xd9\x82\xccv\xa8 p\xa2\x9ah6\x9c\xe5\x19H\x15T0\xc5n\x04\xeb\xbd\x0d@\x9e$\xa9\xbe{\x8d\x96\xaf\xe8Q\xfd\xf7F?jM\x06{\x90o\xff\xd8\xf8\xb6\xc0\xed\xc2\xe7\xe51z\xbb<~\xdcuM\xf8\x0e\xb2\xf5_\x9b[\xbfg\xad\xff\xc2\xf3\x04r\xbca\xcd\xfe\xe4|dE\xbe)M\"\xb6\xfess\xeb/\x8d\xad\xb7\xc67(\xcb\xee\xb0\x0fO\x9c\xb3\xb0\xe7:\xd3\xdf\xcf\xc2\xd9\x0f\xee\x93\xa5~W\xa9\x1f\x94\xc9\xb3\x9a|\xe1r\xd9DP\x96\x0c&\x90\xa1\x9aA\xb8U@4\x08H\x92\xbeeo\xf0\xfc\xe0\x7f\xce#\xd3\x0d\xfb\x98\x7f;u\x0d{Z\xfd\xa0\xa8~\x16\xcaP0Ct\xffd$^\xfe6c,\x88\xc9k$l\xf5#b\x0c\xc6\xaa\x0b\xb01\xc1\xa7\xfaam'\xc0\xc3\xbc5O\x04\xc4\xc9\x15O7\x1b\xc6\x0cyJ\x18>\xcb\x00o\x80|\xb6\xd3\x13\xe81Y\x0f\x13\xdc38\x88\n0a_\xc7<\x9f\x1d\xf4\xe0\xcfN\xc0\x85I\xbc\xb5\xb0vf\x8ey \x05*\xfa\xc6J\x9f\x19z\x12\xb7 \xdb\x7fk\xc4\xf6\xc7\x98\xac\xa4\xf9~O~rA\xba\xe0\xca\x85\xa4l\xe4\x91\x84\xce\xb4\xc2\x08\xbd\xe4\x02\xda.\xa0\xe7\x0e\x13\xd7v\xb7F\xc8\x04\xd4\x83\x95\xfa(\x15\xf3wv\xb76\x87PD.\xdd\xda\xdeb\xc26*\xa6\xfepF\xc3Mt`Na\x83\xb7\xce\x93\xc9l\x88\xd7z\\\x86c`c\xbc\xdb\x98\xeb\xbc\xde\x0b\xab\xd9\xde>t\x90\x93\xf9\xe4`Zh:\xf5g0\xe6\xa7\xdc\x1fz\xb74\xf5#\xafSmk\xe6\xf2\x8c\xa2\xfa\x86D \x08\xf3\x92\x95t\xba\xfej\x1d%\x89\x7f\x11\x08\xc7\xf71\xf8BU\xc9\x8d@x \xb2n\x13c\xf7\xd9\xb1\xcb\xf3\xbf\x983K\xc1\xbe\xe4\xd7\xa4\x02\x10\xe3\xafin\x01\xe221)\xc5\x95\xd2\xea/B\xb6\xdfx\x8em\xfd{\x9b\x9c\x1e\xe5\xcf\xd8(\xba\xbd..\x97\xdc\x94\x1b\xfc\xb09\x0b\xbb\xd6\x19\xfed\x14\x84MCf\xb8Q\x90\xd4\x8d\x11\xa6\xf7\xb4\xf6\xf1g-\x14\xd1\x1aAq\xbcV\xc9k\xce\x1bTl\x87UE\x96\xe2CY+:\xae2\x90\x85*\x9d\xc0\x0b\x08\xd8\x1f=\x07\x89\xa2\xa3\xe31)oJf\xee\xa0\x88s\xc0P\xc4\x1b\xe4\xf6\x06\\\xcb\xdd\xf1*5\xba\xdc\xbc\x80aR\x9e9\x90\xd3XY/Z\x80\xfaR\xdeN\xder\xa5#F\xfal\x82.\x95\xea]\x98\x80\x87\xdf\xc7\xd0\x9dt\xfb\xe0\x0dr\xbb\x04\xdb\xb1\xc2\xdaXp\x95\xa8\xb8\x1a\x99b33>\x0e5>N\xdfh>\x91\xf1\xbb\x00\xb5K\xee\x13\xa1\x94\xb03sa\xa1\xe2\x06\x0d\x80\xfaA9/\xa9\xf5\x85\x11-\xca\xf4\x99'\xe8\xf7D\x82\xfe\xc7/1k\xbf\xe0\xfdc \x9eG\xd7i\x82Wo\xfc\x04\xe6i\xc2\x10\x02\x8f\x9bN\x9a\xf2\xb4\xa6\x8b\x19\x9f\x99\xf9\xe41OY\x8a\xc3\xb1\xb6\x8a5\xfe\xb4\xc6&K+\xe6w\xec\xfa\xd1\xffU\xd2\xf1\xf1M_\x95\xd9\xd5\xfb\x83|\xc8a\x9fo\xe5\xb0\x0f\x9d\x11F\xc1\xc9\x7f\x0e5\xd9\x82\x13\xc8\xb1\x847Q\xcd\xdb\x9a\x13?U\xa4}\xc1#\xc4\x95\xa5\xdcjVS\xd6|\xd0\x87E\x1f\xed?\xea\xdeR\x0cAQ\xd9\x91?B\x17\x1f\xf9\xa4\xae.C\x85\x9d\xa3h(\xc5\x8dXqI\x92\xcb\x04\xa1\x8b7f\x85o\x06\x02\xeb\xd1#\xb6\x05\x95\x02T\xdb\xdc\xdf\x83P\x84K\xa5\x02\x12\x86\x97 R.\xfb\xa8*u\x85Z\x8aVn_\xa6\xc1\xcc-\xa0\xdf\xfd!\xa6\x8bs\x86\xe3\x15\xf1\xderQ\x8d\xd3\xc2\xb6;\x9a\xc6q\x08\xba\xf2}\x9eR\xdc\x00W\x97\xaf\x1c\xcf*\xab\xde_\x8aU\x96\xc7\xcd\x04\x9cN\xcd\x96I\xa3!\x92\x9f\xb2r\xb9\xaf.\xb0\xc5\xa2\x95\xdf\x1c\xa7\xc4\"\xe0]V\xeeYM\xb9\xf1\x91\xd6H\x1f\x04y\xa5\xe8\xc2%~w\x9aT\x80J\x0e\xd9\xe2$\xd0\xb4\xa3\x145\xb4\xa8\xbe\\\"u\xf9u\xe7*K\xd0\x92\x80\xc0\x05O|\xc3\x13\x98\xdb\x8c\x10\xa1\xa4b\xe5,\xc4e\xe9\xbe\x8d<\xe72\xd8\xc8E\x95=\x135\xc4\x823\xc8\xf8\x0c\xa9\x1d\x0c\x89$\xae\xb5D\x88\x89p\xca\x18\x9c\xcb\xa9?\x9b\xf5\x05\x8d\xe1\x96\x80\x19O\xcb\xce\xffq\xbc\xc7\xdd\xd5b\x07 \xe4\xc7\xbd\xc1\xbe\x15\x1e\x15L\xf0\x90\x89\xe0e\x1dO,\x1d\xd6,\xe77\x9f\x88 N\x13\xc6\xa8\x8a\xaf\xd0\xc5\x8d\xd7\x93\xaf0\x0e\x83S\x81\xd2\xdc\xd4\xa9$|\x1a\xc1\x17\xf4<.z\x1eC\x97\xe1uo_\xed\xdd$\xedHZk\xa2\xee\x89}&g\xe4K\xda\xe2\x14t\xe4QNG\x90\xc9\xe3\x9d3\xd9\xac\xbe[m[\xb5b#\x914\xec\xd3\xa0y\x9fz-\xf7i5\xa7\xb6\x97\xa3o%\xa7vV\xbf\x8a\x9f\xa0\x00\x8eR\x93\xa0`\xfc\x18\xc2\xbb\xddn\x1fq\x02\x95 S\xb6?\xbci\\`3N\xb63\xe2\x87_\x01\xd22N*\x8dq\x04\xcb\x8a%f2\x96q8\xc8x\xa3eF\xbd\x0e\x17\xaf\xb099\x14R\x1e\n\xb2\xe6Y{lR\x8f\xf5\xee?X\xaf \xeb\xbf\x11\xa3\x9a\xd0\xa9\x0b]\x05\xa9\xeac(\xa8\xa5\xf6`.\x1d-e\xf0~\xc9iRx\x00\xdb03\x93\x98i\xc16\xc5l'4\xd9\xe8\xa8\x84\"D[\x1d\x95\xe4)$4B\x12J\xcad\xa6%1\xc1\xb7\xba\x1b\x0c!\xc4W\x9e5\xb8Xy\xfb\xc2g\xca\xc2\x13\xce!\xcd\x9a\x16\xfd\x9fAF\x1a\xd6\x88\xb4X#\x85\"\x84&\x8a\x90\xf3\xbe\xd3xV\xdeA*1\xf091h\xd8\x8c\xae\xd0U\xb6\x82;Q7\xdc\xb4+S-7\xc2\xbe \xf0\xad6\x9cY\x94\xcc\xb7!\xd7(\x89@\x03I\x93\xf4X2\xd5k\xf4m\x84\xaa*-\x0b\xb98F.\x02\x8a\x9eT\x10-\x801/|,i\x048W$Kz!K/'\x95\xf9\x87G\x8f\xf8\xc5\xa4DbT\xe0\xd6\xc1]+i\xe2K\xca\xab\xc1\xc5N*\xc4\xce\xeeKu=\xfed\xee\xa8.\xd2\xe9D\xb5\xff2+\x03sm\x94.\xd4\x8c\xce\x1d\x87\xc7\xbb\x94-\xa3\xfb\x97\x89~*\xb4\xb3\xbe\xa2\xb9\xe5c'O \xa6\xd1\x80\x98}\xec7\x94\xc0\x14\xa1zO[Xy\x15ia|\xdc\x9c1\xf7ui\xbc\x85\x0fy\xbd\xd4\xed\xf3ce\xe0'<\xb4C\xaa\x89\xce.?Uf851\xc3\xd4I\xa7\xfeL@\xcd<\x12{G\xd5X\x11\x15K\xb8\xc8\xd6y\xc4y\xeb\xb0\xee\xc4\xca\xd0$\xe2dZ\xb9R\xf5\x0d\x97\xa8\x90\xaar-\x82,\x9a\xfa\xd3p6\xabL+\xd5\x98\x03\xe6\xe12b\xbb\xd2\x8fR\xab\"\x9b\xb5s\xc43\x02\xb0S\xe8\x1fUOB\xa9\x97V\xcc2q3\x84\xc8\x03\x85}6GZ\x9c\xb0\x13\x08%\x8b\x85\xda\xcbR\x0e\xf2b\xe7\xe5n\x9fr\xfbR\xaadh\x1f$dA_W\xac\x15,\x96{|\x8a\xf1\x80\xde\xa64\x9c;\xf5}\xc4m4\xc7@\xca\xab\x85'~et_\xe4\xf6\xa3z\xb1Z\x07,\x0d\xe9\xd5\xac\x07x\xd9\xd6q(\xecC\x8f\x9aC\xcaX\xa3\x99\xf3h\xe1\x97i\xba\xd6\x04\n\xe7\x0fo\x12C\x0cq\xd1\xdfS\xc1\xec\xd57T\xd1\xb8\xae \xd9zC\xf3\xdb\xdb[\xf6\xf6\x17\xda\xb1+-l\x8e\xec\x0d,\xa3\xf5%\x8d\xedm\xec5Lr\xe1\x07\xa6P\xebzs\x04\xeda\":\xf9\x16\x98%\x1d\xca\x1a\x83\xc4\xd47~d\xbc\xde\x99S/\x9a\xd3O\x1f\xdf\xbe\x8aV\xeb(\xa4a\xea(Q:\xcfzh\xb2\xc0\x18+\xcd\xceM\x07\xdc\x7f\xc2_\xdc5!{NT\xaa\xf1\x05$\xed\xd1\x9e\x8c\xdcQ\xdc\x0f\xa1\xcb;R\x9d\xcd\xf95\x0dZOO\xd0#\xde\x85X(6\xd1H\xf2\xd1#\x10G\x0f\x0dkS\x8cP\xb2\xdbG\xb6\xa0\xfe\x94'\xf03\xd0\xbe\\\xf4I\xd1O\xf2\x8f\xc8\x0f\x9d\xee\xa3\xae[!o}H\xb9go 2U\xb0\x94.\x92\xd1@b\xfa\xfb\xfe\xe4\xd1\xac\xe7\xeeO\x9c\xe9\xef\x8f\xb8\x95\x04\xae\xfa?>?G(\x86V3\x01i0\x159\xe8\xb4i6\x8fb\x156\xabg\x0b \x9b\xe2\x87\xfc\xba\xd7\x89\xa7\xfe\x8c\xb1\xc9-x\xa6\xf8a\x08^\xf8FnU}\x1a\xb9o\xe4\xde\xee\xb6\xd67rk\xb8\xa9\xf1\x8d\xec\x1e\xde\xae\xa9\x97\xd2\xb9\xaag+W\xcb\x14\xdf\x97\xf2\x93$\x7f\xe2\x87-\xc8\xb8\xe1\xcaL\xdc\x94\xf5a\xdd\x87y\x1f.\xfb\xe8\xc9\xa8\x89\x01\xba2X\xe2.\x0d\xe5w\xa8\xf9-\xafSE\xb5Yl\x8a\x92?\xf4\xe9\xdd\x9ar\x9fh\xa2\xe6R\x06\x950\\\xe8\xcf\x10\xb9+\x03=\x02\xe1\xddK\x1du\x04.\x04\xec)\xec\x8bh=\x1c\x10)W\x1a\xd3\x01Y\xaf\x83;'\xeeW#>}6\x0c\xf0\xdc\xech\x8f\x16\x12\xb0\x01\xe6\xfc\xedJ\xbc\xa0Kn\xb7\xf2R\x90\xa1P\xdei\xa0\xe8\xc0Z\xb9f\xcf\x16\xad\xc6t\xa35\x97dC\xa2\xb8\xb3t\xbbj\x01\xce\xb9\x9ac\xe3\x90\xed\xe0Z\xb59\xec\x83\x08\x05\x1fe\xa9s\xd3oa\x94\"A\x91\xc2\x068\x08\x0f{\x00\x88%L a\xdc\xdaB\xbep\xed\xd6\xf3s\x00ga\xabn\xdf\x06\x88\x1cZ\x1d\xad\xe7\n2\xa0Av\x00\x13\xb8`\xaf\x8c\xf9\x9d\x8e\x8a-5 M\xdf\xe3m\xd3\x1a\xe81\x97\x01\xea\\\x0bz\xb6Bl,$^f+\x1a\xa6 \x0f\xe4\x9f^\xfaI\x1fo+\xa8Ei\xc2^V\x90\xad\x10\xbf\x9b\x97\x0f\x14t\xe5\xbd\xd4\x91\x80 $\xab\x02fkmC\x9f\x1d\xd3\xc2\xb3\xd1-]u5\xea\xcd_8\x97m\xe4\xf0\xfa\xc6BSyG\xd7\xa8\xdb\xaf\x8cT{r`\xaa\x0bF\x85\xee\xefQFrB\xae\xfbA:\xd9a\xe7-\x99\xfb\xe1\x92g\xdap\x18\x95\xec\xae\xc8\xedo\xc4O\xbbty\xbb\xb5PS\xe5~p\xa2{#\x97u\xff@ *\xdd\xeb9\xe1-]B\x0f\xab\xac\x05\x82\xe43\xa1\xaf\x0f\x9d\xd8\xa9\xc4\xcd\xccs\x08\x15\x0c\":`\x8c\xc1#\xe1\xe3\x94\xcd\x0dH\x02\xb9|\xd9\xa9\xd8O~\xd6\xef\xd0\x1a\x80\xc6\xa0]\x14\x14-\xba\xe7\xe7\xd8\xfe\xf99R\xe4\x7f|\x86I\x15LZ-\xa89\xe8\x16\x8fC\xe7l?s\x1di\x15\x85\xe2`\x9f\x81vw\xe8\x0e\x16NUp\xee\x832\x0c\\\xbc>l\xba.\xeb\x7f*\xc3\xd9u\x1c\xaa\xda\x8c\xa1\x9aM\xe78\xd5\x14y*\xd5G\xcd6\x9e\xb0*0\x8cl\x87\xa8\xebK%\\\x8aFx\xf9\x9c\xd0\x1cM\xd0@\xf6\xb8\xae\x06\xad\x9a\xc1\xfe\xe33\xbf|\x19\x8b\x83\xa6\x82z\xde%\xf5\xae\xc6\x8aEv\xebM\xab\x92\xf5\x02\xe5\x8b\x8d\xdb\x82\xe8\x1b\x8f\x1d\x0fC6\xf0:\x0f\x1b\xd9\x97\xed}\xde\xdf\x18\xc7\xff\xcc}\xe0~oV\x1a2p\xed|E[\nx\xab2\xb4\x90\xad\xf7\xb4I\x88\x9d\xad\xbd-m\xdc\xa1\xa7\xba\xb0C\xa1\xb3]\xad\xcd\x07\xfft\xbbZ=\x10\xe5\xd5\x83\xc0\x13\xbdVG\xb9\xe0\xf5w\x86\xa5\xd3\xf0\x99\xf2+\x1a\xf8![\x1a\xa7\x82U\xeb\x1a\x19Z\xf8\xe1\xfc\xf5\xf1\xfb\xa3hN\xc7Ui6\xa6\xe1\x9c\xc6c\xf0\x07\xfc[e\x92\xe1*\xca\xc24\xd7\n\x1d\xa4\xbc\x11\x7f\xa0\x7fR~\xfb\x9a\xc6\x89\x1f\x85cH\xaa\xad&x\xc3v~\xc1\xe8\x05\x9d\x7fZ\xcfIJ\x931d\x83r\x89\xe15>\xd2\x93\xec\"\x8d)}\x1b\xa6\xd1\xab(L\x89\x1f\xb2y\x14\xc2\xabB\xa1\xf5\x91\x1a\xcf\xcf?\x1e\x1e\xbc:=\x7f}\xf8\xeb\xe9\xf1\xf1\xbb\x93\xf3\x9f\xde\x1d\xffx\xf0\xee\xfc\xe7\xe3\xe3_\xce\xd1CWk9e\x7fM,\n{\xbbU\xc5\x8ar>\x87\xe7iL\xa9.i\xf8\x92\xa6\xaf\x82(\xa1I\xfaV\x10\xe47q\xb4\xe2\xab\x12\x0f\xccO5\xba\x16\x8aK\xc6*\xc8\xcaM1\xc3@\xb9b\x18\x88e\xa0\xf3|\xcc\xfc\x02\x921\xfbR/\n=?`\xcb_\\h|\xaepH\xeboAL\xf6\xf6\xaa\xd1\xca$5\xa9\xeewNM\xf6\x9e\xea4u\xac\xbc\x1a\xdd,\x13\xe5U\xaa$\x88\xe1\xd3j\xbf\x81(\xaf\xf6\xcb\xe9\xc9\xde3==\xa9\x11\xc35'3\xa3*Y\x9a\xf3\xf2\xcd\xea\xe1w)\xcaG\x95\xf2kQ^\x9d\xeeJ\x94W\xc9\xe4R\x94W\xc1p'\xca\xab`\xb8\xe0\xe5[\xd5\xf6\xcfEy\xb5\xfd\x1bQ^\x9d\xef!*\x18\xdb\xf0n|{6\xc4\xce>D>\xeeP\xb8p/\x07\x87\xd74L\x0fW~\x9a\xd2Xl\xf0\x8f\x94x)\x96\xbf\xf3\x93\x94\x864vVn^\xf7C\x90-\xfd\xf0\xe7\xecB\xd4V\n\x8f\xe39\x8d\x1dR\xad\xfb)\xf5\x83D\xd4.Q\x0bga\xab\xcaj\x9c\xc6\x84\x91d\x12\xa0\x80\xde<\x82\xe4\xc7\xbb#\xb2\xa2\x9a\xfbC\xf69\xf1W\xeb\x80*\xd5\xc7pS\xa72\xecs\x18\xa64~G\xc9u\xb9v\xa6\xaf\xfd\xea\x92\x84\xcbrMCv\xb3\x13\x1a\x94\x07<\x86s}\xcd\x1f\xe9\"\x8a\xe9\xdbp\x9d\x95\xab\xd7]\xb4>#d~\x8e\x92\x02\xb8\x020?\xb1\xb5\xf3\xbd\xbc\xf8U@\x92\xc4\xf1\x8c\xf5O\xe9mZ\xa9|\x89\x95_\x1f\xbf\x97\xd7T\xa2\xaaR\xf2*\n\x17\xfe\x1235\xb4\xab\x99\xb4\xaey\xc1\x17}\xb5f%\xe5\xb1\x96\x0b\xdf\x10/\x8d\xe2\xbb\x16\xb1>\xa5\xc2\x81\xde\xc0\xba\x1a\x98\xb2\x80\xa68\xcd\xf3\x0d!\xc8\xf5iL\xc2\x84\xf0\x1e\xee4\x15\x7fd\xbc\x80\x1f.O\xd2\x98\xa4ty\xe7\\c\xa5\xda\xd8\xc3k?\x8e\xc2\x15\x0dS'0K\xf3\xf8\xed\x8b\xc8\xbf\x99F\x08\x00\xfb\x8cw\xa9\x03\xa8Kb\x9flxY\x1c\xd30\xed\x8eu\xf7 \xbc\xca\x9c\xa6\xc4\x0f\x12k\x15?a\xac\xcf\xdcV\xe7\xd2\x9f\xcfih\xab!\xfc\x02mU\xae\xe8]r\x19\xc5\xa9\x97\xa5\xd6\x01\x05\xe4\x82\x06\xb6\nq\x14\xd09M\xbc\xd8_#\x07e\xa9J\xb24\xf2\"FMRj\xab\x87\x92\x97\x1d\x06\xf4vM\xc2y\x03\x9cH\xb2\x8e\xd6\xd9\xda:=zm\x9f\xde*\x9a\x13{\x05\x19\xb5\xbc\xb1R\x82d\x8c-\xaf\xadj\x14\xfb4LI\x13,\xf1\xce\xfa2\n\xe64\xb6V\x8bi\x92\xd8\xc1\x14S2\x8f\xc2\xe0\xce^\xe7o\x99\x1f\xdb\xdb\xe1\xd3k\xa8\x13\xc5\xd6\x1drM\x82\x8c\xae\xc8ms\x1d\xdf\n\x1d\xac\x13F7\x8duRzk\x1d\x10I\xa3\x95\xef\xd9j\\d\x89\x15t\x81\x7fm]\xef\x98\x06\xf4\x9a4\x10\x0eF\x7f\x16\x0b&\x9f[j-crqa\x87?\xa3\xc2\xd7\xb8]i8o\xe8\xd4\x8b\x02\x8f\xf1\xe1\x0du\xd0P\xae\xa1N\xb2&\xd6\xe5\xf2\xa20\x8d\xa3\x06\xca\x884\xe6\x82\xce/\xac\xe0F\xcf\xe8\x15M\x12\xb2\xb4\x82}\x11D7id]8F\xf9\x82\xa6\xfe\xa2\x9b\xd0:\xecu\x94\xf8aB\xadP\x8c\xa3\x9bFH\xc7\xd1M#\xa4\xe3\xe8\xa6 \xd2 M\x13\xff\xef\x08\x99R\x8d\x8a\x00\xf6\xfa\xf8\xfdA\x9a\xc6\xfeE\x96R\xc6\x1a\xb2s\xaf^E\xf2\x1dy\x8d\xbc\xc2W\x9c\xc2\x8aFgX\x95V\xc4\xd5\x81^\xa3\xb3\xb7W\xad.e\xb0\xaap#e\xb0\xaap\x83q\x08\x9f\xf5a\xb4\xd5\x87\xcd\xbd>lmV,[\x990\xb6\xb9\xa9 \x14\x1d\x0d<\x12~J\xe8\xeb\xe3\xf7\xa8O@\xde%\xf1\xd9\xcc\x91\x0fE\xbd/O\x11Q~\x19\xc5\xb5R\xda\xfcjS\xf3\xc8\xc3+\xda\xf7\xd1\x9cb3\xb2\x00\xa4\xc3\xa0,\x18\xa8U\xab\xca\"~\xd3Zm\x9c\xf1\xae\xd5\x01\xb2\x07\x1d\xee\xb2\xe7\xd4\x0dk1\xf5\xbbHv\xc1V\x9f\xb8F\x05\xcaz \x14C\xac\x06\x9a\x07\xbd\x0dS'/u\xdc>\x8c\x86.\x8f\xe7\xa7\x11?+cu:\x1e\xc8HT\x0b\xc0\xec\xbe\xec\x0b\x86\xe4\xabL\xf6Z\x13\xa6{\x95G-\xc5t\xbc\xaf\x84W\x03\xe35K\xf5\x96\xdax\xd2\x17\x85\\\xa1\xe3\x00\xd9g}I\x12:\xffH\x97~\xc2\xf8X?\n\xe5\xb6\xd0Vg\x9f\x8b\xec\x82\xf1zc\xe8F\xa1\"\xb9X\xbc\x10<\xb2N\xb3\xb8\xfe\xca+^^\xb7\xe5\x87\xfa\xde\x96\x9f9]\xd3pNC\x0f\xd9\xdai7\x8d\xd6*\xda\x86\xf3n\x1fX\xe1/\xf4\xee\x03\xe3\"\xc4O\x862b\x98\xf8\xfb\x03IR\xda\xd5$\xe5\xab\xf7\xea\x95\x9a\xffN\x80\xac\xce\xa1\x1d,\xcbo}#p\xfe\x18d\xb1\x80\x92 \xb2\xaf\xa3\x9bP\x0f\xe7_\xe8\xdd\xa7\xb5\xf8\xfe>\xca\x12\x8aU\x1f\n\xe7\x93\x94\xc4\xdf\x0be_U\xba\xf9\x02X\xe3{\xdf\x15\xdabd\xff,xs\xc9\xf6\xfb\x03\x9c\xf7\xf3\x05\x10\xe7/~W\x90\xcb\xb1}C\x98\x97J*\xe3\xbb\x13\xaa\xbe\xbc07\x9b\xba\xd0^\xa5I{r\xad\xb2\x83[C\xe7C\xb3ZD\xd7r\xf7\xa2G\xc5\xab\xf2\xe1\xabk\x18gim:o {\xd0D\xd3S\x9b\xe3\x105\x19\xa8\x97@k\xa9\x84ki\xb7\x00\xd7\xc4\xac\xb3F0j\xb2\x1c\xd7ymhL \xafe\xde\xb7\x01W\xa0\x94G!:1\x05A\xe9\xceIJ\x90\xbbIa\x02\xe9\x80\xfd\xac\xdeI\x14#b]\xdd\xe4,Y}t\x87\x92\x8f5\x84\xa6\xcd\xfa\xba\xd8\x0e\x1e\x86l\xb3\x99FC\x13^\x82\xbaT5\xf2\xd6\x18\xf3k9\xa8\x9e z\xe39]\x17\xec\xbczX\x07\x87\xe1\xbc}\xf3\x82Z<\xac\x07\xfeR\x13\x9d\xe0\xd7O7\xdc\x96\x10\x85\x8fG\"J|u\xb8h=\xd7df\"1M\xd9\xc4\"\x92\xd3\xa3G\xca\x8e-\x07\xba\x16\x031\xf7\x8e\xab\xe1\xf6AI\x18^\x16\x08\x00\xf9a\xf6.\xc6q\x17\xe1{kMp\x1c\xab>:\x0c\xd1j\x8f\xe7\xa9c\xf2\xcd\xcd`I\xd3\xd7$%\x8e\xcb\x81\xb3\x0f>\xdawEQ@\xe7NTu\x05`X\xbd\xc0,\xc4E\xa5\xac\xd8\x03udO\\X\xf0]V\x8bsbp\x05\x95\x97\xd9\xe7Z\x7f\xfb\xdc\x92GDH\x91m\xb7qn\x8c\x07\xc4\xf3\xb2U\x16\x90\x94\x9e\xdeD\x1f\xd8\xf1\xfb\xdaO\xd6x\xf9\x9c\xe0E\xca\xc2J\x8dn\x1b\xf6;\xa9\xcf\xbf\x83\xd1\xa2\xe6U\x13\x9fo\xb6\xe3[m\xc7s\xa7\x1a\xb0F~\xda\x1c\x1c\xf2\x93\x1fF7\x97\xbew\x89\x8bp\x0d\x13\xbe\"cp\xee\xc4u\xd8\xaa\xa9\xabBd0\xf7\x95\x1bv\xe3\xfa\xea\x1b\x04\xe5&\x02Q\x1dc_\xdf\x15C\n\xf5\xef5\x86\xd9S\xf6]3M\xc1\xad\xdc\x82\\0d\xb81\xad,:5\xd4\x17\xb6\x88\x0c\xd7\xf1\xd8\xdc\x04\x07cj\x05\x14\xc0)\x1b\xbb\x11z\xfe \xa6\x01% un\xdc~~\xe0\xf5\x0d\x01,\xf5\xae\xce\xeda\x06\x0fBu.O\xb6Z\xabo\x8e\xe1\x8f\x1eA\xa7\x85iD\xe5m\x87\x0e\xbc4\x0e~\xa1w\xb8\x1ayJ~\xd8\xd0\xd1\xa2\xcf\xd1s\x80\xf2\x83\xf7\xba\xf9\xbe\xb9t<]XD\xa8\xb1\xa8\xf8*\x1b \xba1\x8b\xdcQ\x1a\xda\xd6HX\x01J\x810\xc1\xaa\xac\x96\xbc\x0d\x1d\x9c\xdf\xc4d\xbd\xa6\xf1I*\xb2~\xa4\xe5\"\xf3\xd5\x01gT0\xd0\x980\xd7\x0d8\xaf\xd3\x0d\xb3\xd5\x05\x8d\xf3\x95c\x0b`\x19\x0b(\xacw\x97\xe7\x8c\xc3\x03\xcc\xdc3`\xf4\xb5%Ms\x93TG\x9cyn\x112\x17\x1d\xefk\x15\xb4+\"?\xfa{\x8dz)\x9eB\x81\xd1\xe1D\xafp}\x8f\xa5_)*\xef=\xd595\xab)\xde#q\xa4\x8a$\xe2V\xb4i\x197\xd5@\xe0\xf8\xe5\\L\x17\xf5\x85\x928\x18\xd60\xd7\xe2\xce\xaf\xcfV\x00\x13\xa0\x0e\x0f8\x92]\x04\xbe\x97SMd\x02\xe2\x01\x99\x17n\xa8\x07\xc9G\xba8\x8d0m_\xbf\x1ab\x0bp\xe1B.\xc8\x0d\xce\xa3\x9b\x90Vc\x96\x16K\xc8\xc4\xb7\xe42\xca\x02!\x06\xb5\x81\xa6\x84I]r\x03\xa9\xae\xac]a\xe4\xd0\xa7\x06\xe8c\xb9\xc8\x86\x16\xd3\x85LL)\x86_\xbf\x0f\x89\x8c\x03\xf0\xb5\x03P.W\xecX\x90\x13\xcb\x94\x8f\xc3\xc7\xafb\x1c}\x08\xf1m\x0c#\x9eG+,\xde\x8e\x90\xc0\xf1\xbdY\x062g\x89\xdb\x80\xf7\xff5\xc8\x8a<;\xe2fLW\xd15-\xa3';\xf9\xbf \x82~\x075\\)\xe2\x80Q\x03iP\x8a\xfc\xe6\xc1^\x0b\x13G\xedR\xa7\x91Xh\xf3\xfb\x1e\xe6\\\x9a@d\x89\xfc\xe2\xac\x8d\xc1V\xd8\xe73_\x81 W8z\xe6!\x8b\xf0\xa0\xfb\xfb\xe0\xb5\xc4\x94\xb9h\x16D\x92\xe4\x04\xc6|\xb05\xf5G`\xb8\x96\x07\x19uD\xb4\xe2Y[\xf1,\xad\\WlZ\xc9\xa0 P\x88\xd0\xb8S\x0ds\xc9ov\xf0\x9d\x80S'V\xcc\x17\x0c\xd3`]WVq_\x17\x95\x17\x04dV\xfa\xd1 \x81\xc60\xca\x96\xd1\x08\xd0\xaf\xca\x83\xa2\x9c\xb6\xb3\xe2\xbc\x7f\xf6\xab:\xa8y\xd9\xce\xa98D\x95{\xa9\xeb>\xac\xf8&w\xfb0e\xbf\x1a \xa9\xfe\x8c\xcf\xb0\xf4+\x0f\xd2Z\xf4\x1bv\x8e\xca\x00+~\x14\x0e\xde\x7f:9=\xfftrx\xfe\xe1\xe3\xf1\x87\xc3\x8f\xa7\x7f\xad\x9f\xafj\xf5\x9f\x0fN\xce\x7f<>~wxpt\xfe\xeb\xc1\xbbO\x87\xf5c\xb7Z\xfd\xe8\xd3\xfb\xc3\x8fo_\xe9\xaag\x9a\xea\x1f\x8eO\xde\x9e\xbe\xfd\xf5\xd0\xf6^\xa2y\xef\xf8\xd7\xc3\x8f\xef\x8e\x0f^\x1f\xbe\xb6\x0d0\xd0\x9eR~\xf2*K\xd2h\x95k;\xc6\xf0\x91.\x0fo\xd7J\x94\xfc\x94&\xe9\xe0\xc2\x0f\xe7NHo\xc4c\xa7\xfb\xbb3')\xb9'\xb1O\xdc\x0d\xcc\x01\x14\x0f\x0eNO?\xbe\xfd\xf1\xd3\xe9\xe1\xf9\xd1\xc1\xfb\xc3\xf3W?\x1f|\xc4\xbc@?\xfc\xb9\xab\xcb\x1ao\x0f\x85\xc1><\xb3\x8e\xd6\x07\xb9x\xfc\xea\x92\xc4\x185\xd1R+I~\xa1w\x96\x1a)\xc6\x1c3=\x0e\x82\xe8\xe6M\x16\x04'^L\xa99\xb6\x0c\xd6\xc3\x08%xjx\x96\x0e\x03\xcbp\x13\xcb\xa3\xbb\xd03w\x9f\xa5\xd1+\x11\x12\xc3\xdcD\x96F\x1f\x02rglE\\\xec\x9b\x9f\xd3 \xf8@\xe6s?\\\x1a;auN\xd6\xc4\xb3\xd6\xb9$\xf1\x89e\xd5\xbcK\x12\x04\x14-\x1c\x8c50\xb4\xc7\x18\"\xb87\x8e\xd6\xb7\xc0\xc2\x0bH\x92\xbc}m\x7f\xceYLS\x8d(H\x8cA\x89\xbc\x88\x01\xc1\x8cV^\x14\xa64\xb4@\x80??\x9c\xfb\x18\xe8\xc3^\xef6}O\xc3\xccZ'\xc6\xc1\x9a\x00%*\xbc\xf3\x13\xdb\x88\xa2xnFO/\x8e\x92\xe48\xf61L\x92\xa1\x0e\xb7\x0c2?\xa4\xa7\xbe\x05\xdey|\\\xc3,\xe6t\x81\x81 \x0dO\xfd\xd8\xdc\xb2\x08\x96c~9\xba \x83\x88\xcck\x91 \xf3\n1Y.\xad\x0bEC\x8f \x04\xc6\xe7\x8b(^Y\x1f\x1e\xd8\xe9\x14\xabr\xd8\xa2\x8f\xf74\xbd\x8c\xe6\xd6*G\xd1\xaf$\xf0\xb9\xff\xa9\x01 \xac\x1a\xe7\x0f\xcc-\xc5dE\x7f\x8cb\x8c\x16i\xa8sI\xc9\x9c\xc6f\xa4\xba\xa4\xfe\xf2\xd2\xdc\x05\x0f`d\x1c\xe4\xa5\xbf\xbc4\xbf\x1b\xd3\x85\xf5\xe1;b!`\x97\xe9*x\x13Y&\x96\xa6\xeb\xc3\xbfe\xfe\xb5\xb1\x86\xefY\x16\xd37/\x10\xden\xbd\xc7\xf0\x8d\xc6\x1a)]\xc6~j>\x81|3\xc4\xaf\xe8\xdd\x07\x12\x93\x95\xb5\x86\x15\xc9\xae\xfc\xd0d\xeet83ov*nd\xd9$e\xba]D(4\x7f2\xec\"~]\x19\x95\xea3\x08a\x08|\xda\xd7\xed\xbe\xca>3$WK\xbe\x052\xd5\xd0C\xe4\x87xVE2\x11\x9b\xf4\x99>?\x84.\xd9L\xac\xac\xe8\xa40\x9d\xe7\x89x\x04\x85r\xbas\xff\xfa\xffa\xefM\xdb\xdb\xc6\x91E\xe1\xef\xf3+`\xde9ij,)\x96\x9d\xc5Q\xe2\xf6u;\xce\xe9\xdc\xc9\xf6\xc6N/\xa3\xf6\xf8\xc0$$\xf1\x84\"8\\d\xbb;\xf9\xef\xef\x83\x02@\x82d\x81\xa4lgf\xeey.?\xd8\"P\x00\xb1\x16\xaa\n\xb58\xfa\xbe\xb7\xb9\xf2\x1e\xfe\xfd\xb7\xf4//\xdc\xdf\xae\xb6\x07\x0f\xf1Q\xe8\xa5\xdbX\xbb\xca\xcf\xc5\x9a\xa2\xee\xd6\x04\xd1DL:\xfd[\x91\x8ab\xf8\x8af\xde\xd2M\xdb/>\x01Ug\xb3\xc9yU\x1f\xbc9\xf1\xa8yVH\x94np\xe0\xd6u'\xe1\x82\x1bkd4\x0e\xa2\x88%b\xbb\x08\x9c<\x9b\x9c\x93m\xc2\xc86 g\xbb\xc8\n/B\x1a{\x00\xbds\xfe\x9cx\xa3\xd1\xf3\x81\xd4\x0c\x1d\x874\xcd`\xe1V\x17\xa6\\\xda\xd5O\xb1\xe6\x90\xce\xb5B\x98\x9a\xf4\xf4\x87\x9b3\xba\x80H\x0d\x8e\xf4\xb7^?a\xe7:`\xb3\x8c\x16\xadgkH\xb8;\x1f\x8c\xe7<9\xa1\xde\xd2\xcd\xeaF\x80E/br \x83~\x81\xfa\x89\x1b\x8d=\xd1x\xb1m\xd3\xc1s\xb3?\xa2\x87Z\xdfQn\xe42\x0f7\x99,\xf1\xfc\xd7\xfb\xd8\x7f\xfb\x96\xcdm_\x82\xaa\x1d\xedkT+7nI\xcd\x1cTC\xb7\xaa\xd0x`\x86#~\xf0\x808r\x06\xc05\x03T\xb2\xe5:)\xcb^G\x19K\xd64\x94\xe9\x83\x8a\xde\xbc\xa9\x13)p\xb3 \xcd\xe1\xf3r*\x82\x14\xfe\x8b\x06\x8bO{4\x0c\x19S\xf5\x83\xa9G\xc6V\xaa\xda\xea2\x13%\x0eI\xa3\x12 \xa2\xc0\xf6\xbf\xdb\x98\xa3\xdc\xaf6\x7f b'\xe1\x0d\xd5c\xb7U\xd5n\xb6\x85r\x86\xc3\x08\x16+20\x99\x91\xad\x0c.\xc1x\x81\x8c\xc8\xa4\x18 ]\x1c\x9d\x9c\xb1\x1c7\xa3\x9ez(\xf9AK\xbc=\xb5.d?\xcb[v\x18F\x15\x87\x1d\xc1Jf\x9c\xbc&UX\xec\xbaH\xef:7\x13[U\xfa\x9e\xe0\xe4\x05\xc9\x9e\x13\xbe\xbd= \xd1\x8c\x9f\x8bI\x98q\x04\x05i\xf5\x9c\xe6\xdcO\xc9\x8c\x9d\xdf\xef\xb6\xb3\x1c{XP\xa4\xbb\x1ec\xa0\x13\x89h\xed\xcd&C\xf2\xdd\x0b\xc9\x1f\x16\x02\xec\x03'Kr\xe6|\xff\xdd\x908/\x1e\xca\xcc\xef\x9d\xf3\xe6\xc1(J;/\x80\xb1\xfc\xde\x01`\xf5\x1b\xf1\xf4=\xdb+a_d\x97\xdc\xbf\xf9\xfeE\x96\xe8b\xc9\xf7/\x1e\xaaDK\x1d^\xd9\xda\xf5\x82\\\xaf\xc2(=\x00\x8eo\xfa\xf0\xe1\xd5\xd5\xd5\xf8jo\xcc\x93\xc5\xc3\xdd\x9d\x9d\x9d\x87\xe9zQ\xb4~\xbdhT5G\xa9x\xe7/\xceT\xf6\xe8\xf0\x85\x1f\xacU\xcb\xe0\xd7y\xf38\xa4 \xa3\n\xfc\xc5\x8a\xc6\n\x1a~!\xd0\x1e\x0f\xa7d\xb6\xdb\x1c\x01\xddi\x8f\x87\x8b\x84\xe7\xba\x9e\xe2\xd56\x1a\xe2 \xd9\x82E\xben\xc4<`\xa1\x9f\xb2L\xd5P\xbe\"%c\x9a\xd0\x95.(1\x8b*\xa6_\x90BY\x82vAM`\xeb\xdc\x11y\xb7\xb0\x90\"wDn\xcacy\xad\x8bdyT\xe5!l\x92\x1e&4\x13\x9a\x84\xe7\xcc9\xcf\xf0\x9c%\xb3\xdcog~#\x08\xa0,0\xad\xbb\xa7,w\xfa\xcc\xf1\x82\xc4\x0b\x81\xc5\xf5\xc2 \xfe@\xb3\xa5\xf8\xed\xb39\xb8n`a\x18\xc4)d/\xc4\x9f`E\xa5\xaf\x07\x08\x80\xa2\xfe\xd3\xe4?\x13\xea\x07,\x02-\xdd\x15M\xc1\x03D\xac\xaaR72\xf0\x93\x877\x0b^\xfc\xd4u\x88\xc244\xebHddJ'\xcd\xb8\xf4\x0d\xc1\xae\xa5\x060\x84;8/(\x1b\xfba6\x07\x0f>\xc4\x1b\x12*\x7f\x99\xc1xk^N:i\x88@\x9c6\\\x9e\"\xf3\xda)\xa2N?p!\xe4\xfcEpV\xd4\x02\x11T\xe8?\xe7/\xa5m\xb5\xf3\"\x0c\xa2\xcf\xe4\xe1\xf7\x0e\x99\x12\xe7\x85\xa3HP\xe7\xfb\x17\x0f\xcb\xdfN\xd9\x95`<\x0f\x12M}\xa9\xe4C\xd9e\xd4\xd3\xed]\x0f\x01T\xc8`Qwoe~q\xe1BO\xeeW\x1f\x9d\xb8\x82(\xe6\x83\x99\x80\xab\n%\xfb\xd0\x0e/\xa2>\xac$Nl\xde\xc1<\xa2S,\xd1p@\xa3\x19\xc9z$=-\x97\xa8\xcfI\x8eK7R5\x85x\x9c\xc1\x86\x02\xa6\n[\xfa\xa4\xce\xbe\xaa0\x83\x0dW>\xb1\xaa\xbe\x9e.\xe3\x0cN\x1e\xd7;+\xe3\x0c\xee=\xae\xc3\xaf\xf1\x15\xa5\xc2\x0c\xee\xd4;\xab\xc2\x0c\xee\xd4 \x91\x1b\xd5\xfc\xfa`\xaa0\x83\x0d\xbb\x8d\x0b)\xb5\xd9{6\x18B\xb8\xc4\x9d\xba\n\xa4\x8a7\xd8\x18\xbe\x13U\xf0\x11\x14\x9c\xf8\xeb\xebB\xa2`r\x0b\xa2\x85\x16{\xf7\xa8\x10\xf9;\xe4l\x19\xa4D\xd0\xf6\x82c%W4%:L,\xb9\xbc!\xff%\xce\xa9H\x9cS\xff5Fn6\xfed\x7f\xd3\x1f(Ka./\xde\xa1'\x83\xb4Z\xfd?36\xbe\xc8\xe8\xe2\\\x1a\xd7(s\xcfl\xac\x97\x85\x1e)\x99jY\x0c\x8a\x1fu&{O\x1dA\x1d\x88\n\x87\xf6\xc1?$\x0e\x81\x0btA\x8f\xa9\x91P\xaa;\x84\xcf \x9c\xda\x96\xb2\xe5\xc0\x8b\xe1\x1a\xc3\x91\x0f\xf6\x89]M\xb4uO6\xfc\xc9\x0eHu\x11\x9b\xd9\xb6\xfa\xce\xc0\xa3\xa4\x15B\x8a\x94\x9fL\x9cA\xa5\x81p\xcf^1\xd158\xf72W\x14\xddu\x86\xb0\xec\x07\xed.M>\xb6x\xdc\x90N\xb6\x133P\xfd\x15\xea!\x19\xf1\x88\xa8m\xa6\xd9\xf8b \xa1!\xda[\xe4\x05\xac\xf2\x07\x0f\xf4\xcfRN#h\xb6\xd7`\x99#a\xa6\xe2W\x87 \xd3\x91\x9b\x0dI\x00>\xb2\x16L\x06\x8e\x85\x88\xc7\x1f\x19\xf5o\xdc\x81v\xa6\xe5\xbe\xc4\xee\x0e\xa0QQ\x9aM \x12\xeb\x99\xa0\xb6v\x16\x97\x9a\xa1:3\xa6\x88\xdf\xe7\xafVKQd\xb6^6\\ \xcd\xc7q^\xc6\xc1\x05\xe7\x92\xa2\xcd\xca\xcfd\xbd\x85*Y\xb7\xa7}i\xbci|l5\x8ey*G\xf0g\xe9\xca\x02\xbe\xd8^\xcd\xa7F5\x97\xb7\xa9\xe6\x1f\x8dj\x16\xdd\xd5\xe8_b5\xbej\x1ca\x19\x8f\x8f.y\x02w\xd3\xe2\x7f\xed\xcc\xcbx|L#i\x0e\xe0x4\x8aCzc\x05)\xfc\xe1h\xc8L&4\x0b\xbc\xcc\xe5|\x1c+\x0f\x85\x8e\xaf\x12<\xcc\xab`\xc6\xe3\x93U\x9c\x05\xe0K\x90\xc9_\x08H\xe4%7q&\x81\xf4o\x0c\xccW >\x9a\x9d$p\xa3\x0e\x91\xfd\x9a\xd9o8\xf5\x99/\xfd\xd6:!\xbc@\xc8\x0f\x0b\xe0[\x96Q\xdf\x04^\xa9\x04\xbc\x80\x8a\x9f\x04\xb0)\x12\xe4\x08\x1c\x96\xe7\xa9\x18\xb0X\xfcG\xb2\xe5L\xe1\xd3$2\x81\x88\x80\xfc Z _$\xa0X\xe6\xc4\xeag\x13\xe8#\xcdX1s \xcd\x98m\xd6N\x19\x03\xf3\x0b'\x85\x1f8\x80lQ*\x7f! \x19\x0d\xa5\xcf\xc9T\xfeB@\xf24\x06I\x8f\x93\xca_M\x90\xb3`\xc5t\xb4$'\x0bV,\xc7B\x1ae<\xfe\x89\x87\xf9\xaa\xec\xdd\x1a^m\xfd\xfb\x99\x06\x99l\xfe\x95\xfce\xd0\x11\x18 \xf6{c\xff^\x8f\xb3\x84z\x9f{\xec\xfd\x1f\x1aeK_\xcb\x82\xe0~\xfdR\x1f\x98{\xf5\x8b\x1a\xb1\xf3\x199 \xea3\xd5\xcc\xc2W\xbe.\xfe\xc8)<\xf4ft\x81\x1du\xd2\xd3{\x00\xba\xfb\xd6 ?\xeap\xc6\xdd\xb5\xcb\xeaMW@\x05>\x06\xb9\xa9/\x86%\xfeA\xba\x1bU\x0e\xdc\xd4\x1e\x01\xb9\x8f\xfc\xcf\x06\x96k\xe0\xcb\x84\xd1\xcf\xcd,\xd9\xb0u\xe03nm6\xcd\xfd\x00\xcb%\xa6\x0c=+]a\xdb\xfbp>$\xaf\x06\xe4U]\x1e\x93\x01\xb1\xd7Vx\x1c\xe7\xe9\xd2E\x86 \x1b\x92W\xb3\xec\\t\xdcB7\xb7v\\j\xac\xdd\xef\x8c\x9cH4Y\xe0\xcb[\xceI\xb0Z|\xf3v\x0d\xc9\xb7\\Us\x9e\xac\xee\xb7\x0b\x1f\x19h\x88\x11'Q?Z\xbap\x9a_\xae\x02)\xb4\xd4\xbfn\xd7\x8d\xc0\x128E\xad \xe9*\xce\x1a\xd7\x8b]g4a\xf4~\xc7\xe1\xb5\n/>\x14\xad\xd3?\x99=$\x01\x82;\x7fj\xe0\xce\x1b\xa0\x9b\xe4\x89\xd0\x87p\xfa\x11\xe5\xfd\xe5%\x07&k\xb8\xa4\xe2\x94Fs\x12<\x1d\xae@\xb0\x0c\xb6\xba\x14\xc7\x1f\x96\xb5\xb4\xd4\x15\xac,\"\x90@\xc6\x14\xc5\xb2>\xb3\x9b\x05\x8b\xf0\xbc0\x88>\xe39\x82\x9e\xc1s\xd4\x1d\n\x96\xa5Ug\xb1<8\x0e\xf1\xac\xab\xcbN\xe1\xcd\xcf\xe84\x89Uf\x95\n\xc5\x89\xad%j5w}\xf3\xff\x80\xff\xbe\xe6WW,\xca\x83\x8c\xad\x90\xf2\xe4\xc7\x9ap\xedW\xd0\xa2\x99\xd1\xd1\xefG\xa3\xbf\x9d\xab\xff\xd3\x8b\xdf\xc6\xbf\x8d~\xf3\xcf\xff\xf2\xe7\x87U\xf0\xbf\"\xb7\x95\xff i\xb5\xd3\x06#B\xfe\x8cJ3\n\xedJ\x1d^\xd0\x199\x03\xf2\xfd\x01\xd9\xa9J0\x02[\xa4\x92\xbfA\xb0\x01\xe4{\xbf\xb4\xc5\xd8\x13|{\x15\x17u\x85\xc4\xf9Oy\x03\xfeW\xf03\xfb\xe5\x0bq\x7f\x05\xf3su\xcf!\x08\x98\xc7\nW\xfeU\xdf\xbd4\xdc\xbc\x16\x04NUFb\x86\x03\xc9\xe8\x824\\C\xea\xcc\x88\xaeX\x1aS\x8f}\xfa\xf8\x9aT\xe3ph\xb9\x94\xbee\xa8e\xc7 [\x07r\x9e\xb9e\x9dRZ[\x1a\xa4\x05,u%\xa99\x17\xb4\xbe\xa5\x9d*\xbcv\xee\xc6\x16\x08\xd5s\x18\x92\xd7Q\x90\x054\xd4t\xbb\xa0%\xe7C\x92\x0c\xc9\xd5@\xfa\xd8o\xfa\xf4\xfb\xda\xe6fP|\xfd\xa4\\\x98\xf0\x8d\xf71\x8b\xce\xe8B\x9a\xdd\x1cE\xfe\x87\xf2\xda*\x85\x0f\xb6,\xf6\xebZ]JA@\xd6\xa5[k\xe9\xa7h\xfe\xd6\xb5@)?\xce\x8a]yN\x0e\xc9\x89X\xdeR\xf3\xebD\xaet\xb2M\xae\xc5/\xb9\xfc\xadKC\x02\xf7@\xe0\x1b\x92\xaf]\x14O\xc7\xc9\xf2\xa68\x82\xe6c\x9ag\x1c\xc2\x88H\xd3\xba\xd6r\xc1x. M\xfe\xe3\x9fr\x14w4\xeb\xd3\xbfSwZ\xa9\" r\x99gY+-\xf7o\xd0\x8dNz\xb3\xa3Q\xff\xe8O\xbc(\x99J\xab\xbeN\x0f\xcc\xd0CCQ+\xd6\xc8\x03l\x83\xb3\xb0\xb8\xd2H\xe0J\x03?\xc7@\xa7\xa7~\x8f\x91t\xc6\x89\x06/\xee\xb3\xa4\xc5T\xcf\x0c)\x11\xd8\xcfP\x0d\xfa\x1ek\x03x\xa7\xfe\xa8N\xa1\x04\xe2\xa2\xd8\x0e\x04\xfdt8\x87\xd5\x8f\x03\xba$\x92\x96\x01\xcb.7P\x7f5&\xc6$6\xdc\xfd\xe3\xebP+\xa2\x08\xa2-\x80x\xf6r\x9a\xe5\xfc\xbe\xe2 \x94H\xdd@-\xa6\x8e\x06\x135\xa29\xc1\xdc\xeccOA'\x9b\xf4\xe4\x9fK,\x0c\xeb\xe8\x90\xbcm\x8e(\xc8\xd4\xc4\x87\xbcz\x9bk~ ]1\xd8\x10(\x01\x85.\xab\x94\xda'\xb9\xd4 \"\xdb\x07\xc4\x01\x15\xa5\xbc}\xc2\xfb\xc6\xcb0\xcc\xc2#\x9f%g\\\xf0\xf9\x81'\xdbA\x0eID\xa6\xfa\xf4\xa9\xd2\x1cf[\x1a\xad\x07\xfa\x03\xf4\x8eZ\x80^\xbfT\x15\x83\xech\xd0\xea\xd3\x1d;\xb5\xfb\xf9s_\x17\xe1Kp\xe2\x80\x93\x16\xb5\xad\xe6J1\xf7\x1c\x1f\x14\x0b\x85\x8f\xa5\xce#\xccRB\xca\x04divP=b\xc1\x7f\x98\x15\x1aYZUL\xd0\x1b\x86\xe2\x98M\x01R?T\xadu\xc0\x0df\x84p]\x83\x9d_)Q\n\x0c\xdc\x89\x1b\xb4\xd1\xc5f \xda\x86\xd3\x12\xbd\xef\xa5\xfcQ\x13\x8aT\xc5[\x18\xff7\x0f\"\xd7qng\xa7O\xca\xa5\xfc\xb3I\xa3 \xce\xf37\x15\x02,\x19{K\x9a\x1ce\xee\x8e\xd8\xbb\x90\xbcM\x1225\xe2^\x10\xeb\xca\xab\xd1\xb7\xbd\xa5\xa6Z\x89\xed~\x97X>\x86\xd3T\x94\x17\x08\xe2\x7f\xc6bs\xa4\x83\x89\xc0\xe8 \x84\x86\x06\x0c\xd8{\x05Z\x1bY\x9c\xd5i\xfbB\x94\xec\xca\xces\x12\x92\x17$\xd5\xb6\x94$\xdc\xde\x1e\xe8fI\x0e6\x19\x92t\x16\x9ew\x912\x8d\xe8\x14\x1e\x0b\x8c\xf0\x14\x9ba1\x8c6i\x0e\x0d\x06e\xdc\xceHv\xb0h\x81\x9b\xc1\xc9\xdf\x8czR7\xe8\xab\x16\xbb\xc5\x16\x00\x19=\xbe\x8c\x82o+\xd7\xefb\x8c\xb8M\xdc\xcb\x15 \x82f\xda\x96%\xb9\x17J\x9a\xdb\xa4\xb3\xbaMh\xe6\x9d\xda\xd4)\xba\xe56\xf1\xacn\x13\x9ay\xa76\xf5\xe0\x03\xb9M\xec\xaa[\x85f\"$\xb3\x9d\x01\x7fW\x14j\x13\xaapE@7`\n,\xa3 \xc4V\x19v\x8b\xf8\xfa-\xde\x95\xda\xd1\x15M\x8c!\xb9\xc6\x83\xe3\xde\x95\x03\xec1\x1f\x97X\x83\xee\xf0\xc9\xcee\xd9\xc1t\xfe\xd4\x8f\xe9\xac\x9f\xfc\xc8\x0co\x80\xade\x8cI\x0b\xcf\x98 >\x00\xf4\x03:\xf3\x08\xc3(Y~4Y\x1f\x7fl\x96 \xe7\x91Yq\x85+\xeb#YN\xed\xecZ;\x1f\x05\xfd\x0cD?\xd3\x01I\xeb\xed\x0e\xa4\xec\x1fX%pU\xf2\xc7\xd7\xc1,8\x07B\xbd\x83\x9d\xb33\x8f\xedW\x8e\x92Z@\xb8`r\x08\x03G L\xad\xdc\xe6\x89`\xcc*\x0c\x1fka\xf8f\xd8A\xecB\x11\xd1\xed9\x90\x81q\xc5dfn\xaa\xd1\xc4\x83M\xd6x\xebZ\x12\xe0\x10\x98\xa6\x87Pb.\xa6\xb0}\xf1\x0dI\xdc\xb5\xa7Hek\xc4\x03\xb2\x15#{\xe3\xcb\x172\x87\xb1\xc0\xf3n\xb5o\xaa_\x9e\x0f\xd0\xca\x1f< \xb1\xa8OL\xc1\\\xfc\xb0\xecR\x91\xd7!\x81\x90\xfbM\x14E\"\xfb\xe9\xa7\xa0\xe0Q\xe9\x94\x98\x1aC85\x07|;\x95k\xa3\xdc\xaa=j\xaf\xc9n\x06\xf6\x9d\x9c\xb2\xacm\x1b\xb7\xdf\x8d\x17\xdf\xdb`\xa3w\xa3`\xdf\xa6|^\x7f\xca\xddrX\xedI\xd1K_u\x81L\xed\xd8\xc5\xdf0\x10k3\x05\x84U\xd4l\x80\x12\xd8\x15\xe3\x98c'\xb2\xf5\xfc\xbd5\xd7]\xb0\xb6\xac\xc2\xda\xb2~\xac\xed\xdd\x99c\nZz-6|\xd6L\xc5\xd1\xe3\xd5\xe6m\x02\x05\xd0\x8f\xbfU\xb5\xa9\xc1\xc6\xf3\x92\x8d/G\x0b/\x16vq\xffx1\xaf\xf25\x03\xbd[\xbc\x07\xcf+\x9f1\xe0\x11\x1aKg\xa5\x05q\xa4B]e\x06\xff\xabIr\x89\xb8#uF{\xa2\xc8\x16 _\x03\xf8\x8c]gJ\xf8\xe8V,>\x03PF(\xe4\x16\xd6\"d\x9b\x04\x03\xe3\x98\xcc\xc9!\xa1P.\xaf\x95SW\x92\x8e\x14\xf2\x1aE\xc2\x1a`\xd1\x81\x10\x0bg]\xdbL\x8a\xffy\x07\x0e\x85\x8b]\x84\xed\x1d%F\xab\x1b\xd5 u\xe6\x91]\x95\x10\xabyC\x9e\xfd\xff\xe9\xe2\x19\x8f\xd6\xf9\x95c\x87[\x01\xd8\x0f\x07iV\xdezvT<\\\xed<'\x11yA\xb2B\xfa\x15mo\x0fH6\x8b\xce\x95\x0e\x87\xcd\xf2\x9c\xf4a\xe7\xda\xf8\xd9\xde<\xe6\xf58\xcdx|\x96P\xefs\x10-\xbaN\xc7\xce6\x81\xc3\x82\xb6&-\x19\xf5\xdboo\xb9\x7f\xd3\xd2\xde\xc4u\x9e6\x1f\xe93\\\xf6\xd9i*C\xea\xa7\x8f&\x8bA6\xe0\x07\xa2\xf4h|\xc7\x03\xf1\xe9\xb3\xba\xcb2\x0e\x86\x87\xa3U:\xea\xf4\xdc]_\xeaj\xeb&n\xe1e\xdd\xe5C\xe2\xac\xd2\x913\xa8\xe3\xda;\xb5\xfb\xe1\xc8\x1d\x0f\x1e.n\xd9\xbe\xb2u\xc9\xb0\x1b\x85kW\xe0\xe3\x8c\x7f\x12\x14$\xe2\x02\xfc\xeb\xbdv\xceF\xa5(\xaa!\x19\x07\xe9\xa7(\xc8B\x96\xa6\xef\xc0\x7f\xd9\xa0k\x1cZ]\x19iQ\x02h@9\x97\x9c\x87\x8cV\\\x17\xcb\x0c\xa5\xc0_z\xe0\xaa\xed\x04\xady\x11\xa4\xef\xe8;7\xab\xa1\x07\xbd2DU \xe80\x9c(s\xc4?\xe5\x83\x07\x84K/\x922\xd2\x05\x99\x82\x08\xbc\x11!\x80HG\xe3`\x96\x99\x04+\xd0\xcf\xca\xc4y\x13_7N\xf7;N\xca\xfe\x0e6)\x0f\xff~\xb7\x8d2\xa8\xec\x94\x11l\x95\xfbl\xf7Cwv4\xfa\xdb\xf9=m\x16g\xf4\xe7\x893\xb08\xc3\xbfCk\xfb\xb5H\xcb\x0b\xfe\xf8\x8a.\xae\xa2 z\xe6\x17\xdb\xb8\xb6\xd8\"y\xf9\x90\xcd\"pq-M\x89\xa5\x14>\x82\xd54\x8b\xec~\x05\xc8m;lpg\x8fw:\xf7\xafej\xbes\xbe#\xdb\xb0\x88\xc8\xb6x\xb9\xe7\x86M\xcc\x86i\x92\xa9\xda\x10q\x08\x87\xecL\xd9\xfcb\xa2l\x8e\xcdE\x97A7\x01?\xa9\xea\xa6\x1b\xdc>\xa4 !(|\xa7B\xda\xff\x07\xf7\xe0[\x13\x84\x9ft\x931\xbb\xce\x12\xeae\xbat\xd9\x1e+s\x8e\xcf\xc2\xbd\x84~\xd9}2\xc0\xec\xe09z\xe8h\x9e\xc1\xb2\xcc\xa3\x19\xabn\xc0s\xcc*=\x9a9?\xb3\xcb\xcfA\x06\xae\xff\x80\x1c\xb9*\xde3\xc8\x7f\xcb\x7f/3W\xf2E\xe6\xac\xd22\xe3\xedi\x99\xfe\xbeL\xe6\x90\xda\xf8jm \x12\xe3`hN3\x8d\x82\x15\xb8\xf8\x02OM\xdcu\x8et\x823$\xe5\xcbI\xe4c|KQ:\xc8\x98\xf4\x14\xd6R\xc7k\x0d\xd3Z\x93\n\xf5g\xad\x05\x9cqa5d\x89\xa0?\xcd\xae\x9c\x15)\xa2\x86\xf2\x0d:S]\x81My\x02\xe6v\xde\\\x0d\xa6k{q\x00\xe6\xfd\x18\xf6\xca\xa0\x8a}\x01Q\x1b\xae\x82\xc8\xe7W\x80\x04\xa5\xa8\x8d\x04csf\xca\x97!i\x02\x14\x83\xdf\x0e\x06#[\xbe\x0e\xaac\x82\xb4\xa5\xa8\xa22\xb4\xc6[o\x9f\xd9\x82\xc6\xa13v^P.\xe2\xe5y\x03d+0a\x90h(\xe2\xe4 \x1aE\x0d\x113\xce)\xa2\\b$5\\D\x91\xbc\xd2.P`\x88\xce\xd1\x8d_qIJ\xee\x8e\x946s\xfc\xdct\xc1,%_\xbb\x93\xba\x0f\xe3\x1c\x97:J\xc7\xcf\x8f\xf6\x8cCE\xbb#~\x86b\xc7\xb0\xdb\xbd\x19h\x13 zY\xc6@5\xeb\xf5\xac\x07\xaa\xe3-\x99\xf7\xf9\x92_\xebHU:,\x1c\xb8\x84\xe7\x95\xd4\xc3R;d\x0c\xc5\x98oj\x8c\x8c!R\x9b\x05\x1d6\xa3)\x98\xaa|\x1b\x88\x95\xe8x\xa1$ nf\x11\xed$\x1a\xecX6\xb2A\x9a\x93\xb2\xff\x98\xcf\x1a\xf1\xc8\xb0\x9aR\xe8f\xb9f\x850\xa8m\x10\x10(\xba\x15\x80^k\x80F\xfeWX\xddx\xe3Tx\x7f\xd5\xbd\xf6o(\xd8\x9fd\xd8\xc16H\x15\x99P\xcfg\xa4\xccFX\xed\x9e*\x90*\xf4P!^\x91\xa7\xdb\xa5\xabJ\xc8!h\xe8[\xaaR\xfd\xc0++\xddc\xd6K\xeb\x9c\xe6\xd0\xb5\x9e6\xa6\xd9\xff\x06\xeb.\x1b\x9b#\xd9\\O\xac\xa7\x8b\x8dj\x9f\xcb1\xca\x8a-uh\xfc\x9e\x96\xdfm\x1d%sR\xcc:aN\xa1F\xf9kJl\xb7\xffU\x8f\x1f]s\xd1M\xcc\x92\xc6m'\xa6\x11\xde.\x9b\x95\xfb\x9d]3/\xcf\xd8{\xf5q7k\xb7mK\xc74\xa5\xb1\x1bv\x1aI\xae\x0b\x85\xf6\x88\xaeZ,\xe4Azh`Ce\xfbk\xe8k\xa2\x14\xbf\xf9\x14G\xa68Xr\xfb=\xd1\x10\xee0\x82\xe7\xc43\xc2\xf7=\x1f@j%\xa9\xdf\xd7\xe6P\xec\x1f9KnNA\xf7\x96'Ga\xe8\xca\x9b\xdb\x99\xe8\xf5\x81\xa0i\xff\xcf\xe9\xfbwc)i\x08\xe67Re\x01D\xd8\xdf\x9d\x83\xda\xcc\x81\xea\xfd\xf9w\x03\xe9\x02`\xe79\x89\xc9\x8b\"\xf4\xd9s\x12oow\x0d\x01Q#\xee\x83\xd6Y\xdc!\xb3$j\xdc\xfdR'\xc3\x1f\xcfy\xb2\x82\x19\x08\xe0g\x9f/\x12\xf5\xd5\xa5\x1ew=\xdeb\xec\xe1\xd2\xb5\x1e;\xcd\xf6,\x95c\xadg\xe0\xe4\xbb\\d\xcbn\xc9*.\xfa\xec\xce\xb5\xe7\xa0\x01\xa8\xf4\xf3u|\x19D>\x1a\x9eO<\x1e\x8f\xb2\x84Ko\xb2\x1e\xa6N\xd0\xaaM]\xa1<\xba\xf0\xc0\xda\xea@\xbfe\xf3Kd\xab\x10`sn\xca\xe3\xe9\xc1\x03\x12\xa0\xdaq\xf8\x06\x13\xdc\xb4\xa3\xaa\x85;\x1b\x88}\x8b\xcc\xbe&\x17\xad\xd5\xe0\xb8\xb1N\x9b4+\xaeZ\x84\xe1x|N\\)'\xe4pG\xa1M\xde\x00{\x0f\xf4\x0f\xc1\x8d\xeeX\xc4\xf2\xc5MD\x11\xd2\xad\xc4Y]\xb8\x1aD\xec4I\xe5]\xa1\xab\xbe6$\x93\x1d\x90\x18\xb5\xdc\xc9\xb8\\\xeai)\x8f1RcK\xb7VbH0\xa9,\xdb/\x91\x0c\xbe\x80e'\xca\xe2\x1a\x1c\xaf\x039\x8b!\xd6\xa3\x16\xf2*x\x03_W\xcfr\xd9\xd4JJ\xf1\xc9&\xa4[\x03E\x01\xb5f\xd9\x81y\xaec\x0d8.\xf3\xca\x8au\xe2\x01\xd9\xda\xaaC\xb6\x926u/\xe8\xdfl\x7f\xda\xb6Fs*\ne\xb1\xd6\x05\xa8\xf4\xab\xa4\xd7\xd66\xed\x1c\xe9\x05\xb6\xc5d\xa5KA\x08\x02\xbd\xb7~\x02\x9a\x06\x1a\x85\xdc\xa3\xed*I+\x1ee\xcbv=\xaa\xae\xaf]1f\xd3n#\x10a\xb5\xdc2C\xe3-\xea\xa0i\xf5\xd32\xaa\xaa\x82>\xdf\x8ej\x0c\xa2~\x9a\xc7\\\xc1\xb0[(3eb*\xdd\x11H \xa99?,\xbbdl\xa2zZ_(\xfc3u\x05\xcd\xe2\xcd\"M\x9dC\xea\xad\x04\x17f5\xce\xe9\xc9\xf1\xc7\x93\xb3\x8b\x97\xef/\xde\xbd?\xbb\xf8ptzzq\xf6\xe3\xeb\xd3\x8b\xf7\x1f/~}\xff\xe9\xe2\xe7\xd7o\xde\\\xfcpr\xf1\xea\xf5\xc7\x93\x97\xce\xed\xbfi\x08K\xeaR\x11\x15o\xb9\x1e\x0d+\xc0\x85\x1f\x94\xe0q\xa0\xf2\xf2^\x0f\x8e\xdf\"\xb3\x90V\xa4\xf6{\x90\xfa\x15\x9c\xe6\xe2\xc7Z\xad\xae\x88K\xc7\x86\x1d\xc8\xaf\x90[\x10\xe9\x9f\xacq\xd3&\xc5 \xe5)Z\xa6\x1f\x92\x8cl\x8b\x92SiN\x01\xd2\xc8\xad\x9d\xba\x9c}0$Y\xb9:*#\x1c\xe2\xee\xd9\xb8\xe9K\xc2\xd0\xa5\x96\x94\x8b2\xf6\xab\x17,d3\x92!\x01\xc4\x03\xea\xd5\xd7\x99[\xbf\xa8 V\xe4\x10\x0c[\xbc\x80\x98=\xb7X@\x08\x90\xc0PDo2\xca\xdbb\xf7OI\xea\x96\xfa\xef\x03\xf9\xd1\xad\xc9\xb0\x16\xe0\xb7]7\xa9\xe0\xc6\x0c{\xf4\xa4b\x8fn-J4\xf7 .\x0ef\xe1\xb9\xe4~\xfa0>rEv\xb36\x80\xda[\xa1,\x8a\x1b\xa5Y\x90l\x9dl\xda\xed\xe5\"r\xbd\x08\xa6$\xefX\x04\xdf\x96\xe8\xb1s\x1c\x06!\x19X\xe8\x9f\x8a\x037\xd7\x01xg\xa8K\xb6\xd2n\xb7\x14\x87&\x16\xf9e9\x9cm\"\xbf2l[\x8b\x14\x12\xa1\xeaJ\x99oU$\xa7\xbf\xaaN\xcc\xe2\xd5\x0ei\xe1\xbf\xc0\xe7\xa3\xb9\xf7\xec\x02\\\xf5-\xaft5\xcd+\xd7r\xa4\xcf!-U\xee\xeez`nt\xbb\xd0\xbcE\xa0\xf8A\x9aoz\x8b\x90\xf6\xbaE\x08;n\x11\xf4/\xfc\xb8\xdap\xb9j\x81E\xc9\xff\xd8\xad\x9e\x12\xd7y6q \x82\xfe\x1fmRp%\xaf\xbe\x1f\xe1w\xb9\x13\x1c\x159nC\xa1\xf7\xbf\x8b\x9c:\xe8\xbe\x1f\xb1\x9c\xf8\xa6fT+\xc5@\x1b\xe2p\xbb\x187$\x07\x9d\x0ed*\x96QnE\xd7V\xac\x85]\xb1\x16\xaa'n(\xc5 \xa1:F\xc9\x8b\x032\xd1\xf2\xb9=G\xf9~ g;\xe7\x03\xe9\xdc\x16\xe644\xb8r\xa9\xc8K5\xd7\x00\xc2\x9b\xe6\xfc4R\xfa\x1efUq\xbc\x94S\xfc_&w\x0f6\x95\xbb\xab-\x9eK\xc9hZ8m\xec\x10Rv\x8c\xfa\xbfD\xfcH7\x92\xfc%\xf5]\xd7E\x92v\x10\xe3\x92\x9e\xc2\x07Z\xda(F%%\xe2\x96\xfc5\xafH\x9d\x1ar\xab\xa8.\xb7B\xa4o\xcd\x15o\x17\x995+\xac\xc9\xc0\xda\xe6\xf1\xb6D\xdbf3#E\xc9Yi\xc1\x89P2\xea\x82\xdb\x8e\xee\xa1\xafY)\xc5\xd8\x90\xfd\xff\x96\x94\xc5\xee.f\xcf\xe4\n\xf8]\x19\xe4X\xda\xf2l\xaeg\xa3A\x9f*v\xc3\xa85\xfd\x90\xf0\xa1\x9dQ\x04Y\xbfv\x90\xd6\xd6\xec\x14\x1cGgC8;i\xdd`\x99\x0dE-\xc5\xe7\xa4\x06\xa9\xbd\x86\xf28B\x17V\xc7\xaa\xe0bU\xd0\x86\x05q\x04\x12T\xd8\x0fQ}M\xf0\"\x9a\xf6d\xdffg\xa5\x95\xbeg\xaer+h_DR\x1d\xca9;\xf9\xe5\xec\xe2\xf8\xfd\xbb\xb3\x93wg\x16G\xacD]1\xc3\xd0X\xa2 \x8bg\x0e\x07\xb8\xcf\xae\xbb\xbcR\xce\xd5M}\x17\\\xc6{UG\xe7\x19K\xca\xfaP\xb8\xaf\x03\xcc\x1d\xa4m14\xdd\xd8\xfe\x8f_\x07\xa7'g\x17o\x8f>\xfe\xf5\xd3\x87\xff\xb7\nH\xdeq\x1c\xdbVCf\xf8\x16\xbc\x1dIp\xdb/\xd7\xcf\xc9\xea\"\xb4\x8f\x1aG\x14\xb5\xcd\x87v\x9c\x809r6W\x89\x19Wz0\xa5\x92\xa0\xb0\x9f\xcf\xe2\x1c\x84\xab\x97V\xe7wp\x0c\x0d\x0b\x973\xed'\x1f(6\xb5\x83\xf8\xdd \xcbn\x90\xb5\xf5\xe6B?\xb0\xe1=\xa9*\xddZ\x15\x0cC}\xcb{\x9d\xe4\x00Qc\xb3\"\xeav3\x99y=\xe8\x02\xf1,\x04E8\xf3z\xa8oIU\xad\x059$\xee\x1c\xa4\xb9su\xe4\x97\xc1cVC\xb2\x1eB$\x9e\xc1@\x86\xe3yK\xb3\xe5xE\xaf\xdd\x95y\xc0\x0b\x80!Y\xd5\xce\xfc\x18|\xf1\xad\x80\xb1h/\xabB:\x95M\xb8(\x11\xe8\x91\x04s\x17CBg\xcbs\xdd\xa2L\xd9B-\xb7\xb7\x07C\x12\x0b\xf2b\xad\xf9|\xed\x81\xc7E\x9c\x7f\x98\x8f]\x7f\xab\x9c`>h\x1a\x03zR\xbaUk\xb2\x89\xf5]\x980\xc2g\xde\xf9\xa0\xcdm>\xf8?\xd2\xe8}^\xfa\x0fi\xd2\xb5\xcdK\x17\x82\xf6\x00\xc3\x7f\x91\x95\\o=\x087<\x05\x9b\xe7^f\xfah\xb5\x84\x9c\xec\xd3\x81bA\xf6vLF\n7\x05\xe6\x92|!\x80\xeb\x96y\x1d\xa8\x98\x94\xf4g\xfb\x9eU'\xef\xdb\xf7?\x9d\\\x9c\xfc\xf2\xfa\xf4\xec\xf5\xbb\xffl9|\x89y\x00w#?\xe3\x1c\xae\xf4\xa9\xbb\x94{\xcd\xae\x11\xaf\xac\xc7E\n\xb1L\xed}\xcd\xeb\xc7\x13\xd8\xc3\xef\xde\xbf<\xe9;\xab\xdd\xe3\x7f\xd7\xfd\xdbB\xa2\x93\xfeT5\xe9IY\x93\x8em\xdbkV\x9bg\xf8-$a\x85\xc5w\x95\xb4H\xd4\xa9b\xe0\x05Qe\xd4\xbbm\xe6Q\xd5s\xcd\xe9\x0b<\xf8\xb0\x19b\x8f\xe1w\xf0\xc4\xde\xfcH\xbaBl\xb6\xf4O\xf8\x9bEt\xedA\xea\xadD\xd7\xa5\x9b'\xd4\xd6W\xb9\x17\xa8\xfb\xe1 \x86\xa7\xae\xfa-8)\xa5\xdb\xbb\xbb{ \x97\xde\xdd\xdd\xad\x0b\xb4\x89\xa1x\xb6_\x1b\xb4\xdau91\x85\xccy\xc7\x81\xbfV\xb6\x1b\x86\x17&\xd60Z$\xe6} \xa8\x89H\xa1\xb7\xb4\xb3\xe7\x82^i*\x89U\xc7FV\xbfu\xa0*x\x0fN \x11\x15\x0f\x81=.N\xde\xfd4%N\x9cp?\x87^ \xe8\xe4\xe7\x93\x1f>\x1c\x1d\xff\xf5\xe2\xf5\xbb7\xaf\xdf\x9d\\\x9c\x9e\xfd\xfa\xe6\xe4tJ\xb6&\xd5F\xd4FJ\x8b\x0b\x9b\xdfE\xa4\xd8\x1b\x13M\xfa\x8e\x8a\x0dL\xb5\x80v\xb9j\xdd0\\?Z\xbc.>\x9d\xcb@\x01\x1b\x88\xf1\xda\xba@\xa1\xc2\x14\xa2U{\xe0k\xd7\xde#\xf0\xe9\xd1y#+\xf8\x9c\x0e\x9e/n\xf1\xbd\xa4\x1f\xd4\xba6\xee\xcd\xf3 \x06\x15\xd8%\xb8\xd8b\xb3\xf8\x1c\xb8\x0d\xbf~G\xda\x8f\x1d\\\x83\xf5n_k\x1e\xbd9@?(p\x97C\xb2\x1e\x0cH2\xae\x07Sq}`\xc3\xf2!\xf8b\xca\xa4\x1f\xa2\x96\xb1\xd3O\x0f\xbfJ\xfa\x91*JTV\x9dT\xa8W\x1f\xdc.\xd4\xbd\xa2\x8a6mM\xfa\xc4(#\x06w\xcd\xdd5l\xfa~\xa5TOW\xfd\xa0\xc57\x16\xd0\xfaZKW\xf5\xa5\xdb\xaf\xbeH\x8a\xcf;\x98Z\xd2\xca\xd8\xb6\xe7\x96k\x9c\x0d\xc8V\xc3\xc7[\x0cV&\x80\xf8\x90\x05.\xcd\xf5\xc1[[|.\x98\xf5\x8d\xa7\x0em\xd7]Y\xdc\x96\x13\xbdj(o\xf1vG\x88\xc5\xe3]\xd4\xb9\xa55r\xc4O\"\xf3A\xc6\x84\xa3\xb4\x8c~\x90Q\xa9\xa4\xd4\xd0\xb1I5\x94\x17|_\x07\xca\xb5\x8c8\xac\x1f?V\x13p+z\xa2\xf3*\xdc\xa2d\xd7PV\xa7\x96\x8bs\xa5dW\xf7\x89\x99*U\xbd\xba#\x80P\xb5\xa5\x9e\xeeU|h\xee=y\\'P\xe68\xe5\x13\xcb\xfa\x1a>9}Y\xdf\xbe\xa2w&\xf5\xea\x96\xaa;\xf5v\xacK%\xfbzO\x05Z\xaa9\xce\x14Xd\x17\xbb\xd2\x07\xc7T\x7f`\xb7\xf2\x97\xe8\xca/\x15H\xcb\xe5rS:\x7fU\xd1 M\xdf\x15\x18u\xc8\xc8\x01 \xc5\xbe\x96:\x89xX\xe8\xc6\x02\x85\xbb\x0b\xe9\x94Z\xaa\xf7(\x12^*\x97Wbf\xd5c\x0d(*B\xf5\xa9\xa2\xb5_]\x82\x17\xcd\xb1\xbbB\xe9$\x8fGi\x96\xe4^\xaf\xebALM\xcb\x88\xf3eq\xf7\xeb\x89\xad\x9c\x06\x19;\xbb\x89YA\xf4\xcb\xbc@i\xc6\xd4\x92\x8d\xd0\x8f\xcd\x8c\xca%l-_\x0e\xdb\x0f4\xf3\x96\xd2\xffZ-?f\x91\x1fD\x8b\xb2\xedH&h\xd6\x80\x03#<\xff\xa3\xf4\xb9\xa5\x15\xeb\xb6&\xb5\xfcW<\xf1\x98\xbc-\xa8dk\xc1\x9f\x18!d(\n\xb9\xa0\xc6|\xb5|\xb5>j\xa9\x80,\xdf'r\xb1\x16C\x9e)\xafOJi \xef\xc71\x0d\xc3K\xea}N\xeb\x1f\xa2ah4\xe3\xe7 \x0c?I\xa4\x0c\xddi\xac\x0c\xabZD[\xe46\xab%z\xbd\xb3\x1c\xed\xe9\xc5\xf66\xbaV\xb2\xd6\x85b'\xdd\xe9\xd0\xb8\xf3\xe9\xaf\x83G\x14\xe6U\xe3\xaa\x14}\n+\x11{!\xcf\xf61\x1ce\xe8g\x0eJ\x82\x0b\x96\xc9\xe5%\xbdl\xb5|\xc6o\xf5\xbeS\x7f\x14v\xd9r\xb7X\x89\n\xc1\xfa\xd8x\x1f\x07)\x04\xbe*f\xb7\xe5lv\xbd\x96\xb6-\xcb!\xd08\xa8B\x08I\xca\xd0F\x13\xfafD\x86%1LV\x97\x1ay\x1f\xf6\xf2eF6\xe8\xf8\x87\x9d\xe9\xb3tl\xb2\xeb\xb6N\x05\xd2\xb8!\x91\x1e\x06b\x1eD\x99-\xa0\x07\xee\xaa^?E\xd4Vl\xa5V\x9b\x83#f\xed\xda>o=\x0e\xc6 \x97\xa4\x91K\x07u\x1c\x86\xee=7o\xd9\xf9\xa0\x96]\xadC#\xa7\n\xdd\xf0\xc1(Y/`2\ne\xaa\xc2\xc2\x83\x016\xbeV\xba\xb2\xc9bo\xed\x808\xa2\xd2\xeb;\x0fu\xdbZ\x0dn\xb9\x1ao\xb5\xf8\x8aq\xd6\xe3f\xa7IZ4_\x83\x12\x83 \x8a\xb8@|.\x96\xe1v,\x87\xa0\xc7\n\x08\xf4\xa4\x07\xe5<\x0f\x86\x15\xc1~\xa1\xaan\xce4\x90\x0543&\xdc\xb5 \x03\xd7\xca\xe5\xbd'\x90\xb78\xecQ\xcf\x18\xa4\xa1flp0H0,b\x08\xe6\xcd\x81\x07a|\x95|\x02i8\xdc\"x\xe3\x93\xb7\x1f\xce~m\xbf>\xb2,hI\x85\xcc\x11\x15\xdeD/\x92*\x81\xbe\x0cB\xdf\xa0\xd2\xb1(\xde\xc8z\xec\x1f\xd2\x8a\x187\xb3\x15\xb1\x9f\xa5\x03\xbd>\xbfi\xf4+\xa2E\xf0\x96ov\\\x02d\x8dmc\x97\xdcII\xbf\x87q\x8c\x0f\x1e\x90\xad\xac\x8d\xa7\xecs\x87\xd0\xc1\x92\xee\x0c\xdb\xef4\xf4S\xb9\xb8, \xbam\xe2\xa0mw\x07\x1d\x01\x05\x08\xe8w\x07\xd1\x9a\x7ff\xff\x99\xd3\xc4g\xbe\xe6\xa9A\x05\x00\xadU\x9a\x93e-!E )\xac\xd6\xf1*\xda\x82a\xd9\xb6\x08\xe8i51\xbf\x05\x1c\xd3W\xba\xa5\xd8\xa2&\xe1\xf9\xf6\x14r%\xdb&\xe3h\x95\x03\xe1\x92\x16\\\xb8e\x93\xb4\x84:p\x99\x8dE\xec\xb3\xe5/V4\xfd\xac\x10U\x9f\xed\xben3\xa7\x04\x1eVuM\xcc\xa3%\xec\x07\xf8\xdb-C \xc4v\xfc\x8e\xf9\xc1\xd6O5~N6 \xd1,9o\x0d`c\xf5\x14\x87\x8dKU\xd2\xb2\xf9\xd0\x18\xe3j=\xf2\xf4\x99\xb3Q\x83\x8c\x93\xa5w\xabL=\xfb\x8d\xa4AM\xca\xc6>\xa5\x81t3[6\x8f\xe8\xe8\x0c\x8d\x1c\x19\xa8\xa1\x0d\xa1VC\xf0 \\\xb5\xf2rpl\xac\xb6\x82\xa5~\xba9K=\x90\x1f\xc2j\xd5B\x8f\xfd\xcdj\x15g\xbe\x1d\x89\x96.w\xbf\x02\xdf\xdb{\x0f\x13\x83\x1d\xeb\xb5n\x80`7;\xd4_\xab\x0f\xf3\x81\xd1H\xaa_X\xf7\xaf~]Q\xbd\xef{\xe5\xceM\xa1\x9e\xe8T\x1b9\xd9\x86\x84\x95\xdeCyP\x011\xc7@I\xaa\x9f\xaa\xa4b\x1f\xe4\xd9\xf0z\xfe\x8e\x89\x0dJ\x93\x9b>\xfb\xb2P\x8e\xc1\xdayH\xe6ME\x80\xcc\xb0\x14\xab\xc2\x0f\xcb\xfb\x11M\xc7\x97\xce\xa8\x0f\xac\xa7\xe1\x97/\xf6\x83\xee\x10\x1f\xa3\xf2;\xd5\xd9jO\xad\\;\x99M\x94 \xb6\x1b\x95>SPk z\x0f\xd0a\xfdI{\xe2\xb8\xc8\xf4\x97 0\xc2\xde\xa6\xa2\xbb\x16\x16i\x08\xbc\xcc\xd6\xa4m1\x17D\xc3\x81\x0c\xd2\x9b\x83\x11\xb8N\x9dJ\xd7[jF\xab\xf7\x04\xc1@\xd5o\xd3\xbeX+\xc7&\x9dW\x11\x10\xe2\xd8\xe6\x1d\x88\xc0\xd5#X\xe5\x03\xeeW\x9f\x1cJ\x17\x98\xb4Ji~\x94\xeb\x1b\xbc\xa6td\xbb\x9e=\xa6\xd9Z\x07\xfe7\xfb]\xe1r\xa1\xb0\xbdGq\x8bw(\xeb\xf6\x80\xf8h\xe3t\xc9\xf3\xb0$K\x8b\xad\x13\xc3\xc4\xa0\xb9\xa25\xf3\xa1\x8c\x82\xacg\xb5\"\n?8 \xd2\x8c\x03\xda\xe5\xbb\xe1\x90x\xb0\xac\xb6|\xf1E\xd1\xa3!\x99\x03\x9f\xde\xbe{\x86$&\x87\x9a7\xeb$e\x01\x91\xd5\xdb\x1aI\x9d\x19\xb8(ab\x17\x81\x95 \xb6\xd5\xc57\x9b\xb4m0$\xb4\x10\xea{\xe2E\xcb$\xe6Cc\xe5\x1e`\xa6=-$\x909\xbb=\xd5O*|Y\x0f)My,5\xd0f\x1fb \xe1,\xect\x93\xb5\x08\xc6m \xcc\xccVii\x11\xb5]dHGo\x0f\x1e\x90\x89r\xa4+\x1d\xc6\x14\x85\x93\xd9\x8e\x85p6\x88\xb1\x03E\xb2\x08\xfc#\n\x88sF~T\xb9\x84\x13\x19\x132%;\xcfI^\xf1\xee\x96\xb7\xfb\xc5^\x1bf\xd9v\xb2\x89\xbbtH\x1c=\xe5\xa6'\xc2\x94\x1c\x92T\xea\xd8H\x8dE\xb9\x1c\xa6$\xbd\x05e\x85\xf8\xbf\xc1\x96#\xbakn\xa1y\xad\xaf\x87\x87\xda\x13A\xdfe*\xb0\xf1\x0f2d\x9b\x1bV\xee?d[,8\xd3#\xda\xe3O\xa8%\x809\xbc(\xf4\x02\xbe:\n\x91\xe0\x90\x845\x19\x81D \xe07\x0b\xc9(\xee\x03p\xaa\xc0\xd4\xe6\xa8\xa0\x8a\xb0@\x15\xd9P\xb7E\xe2\x95\xd0@\x15I\x15\xef}\xac\xcb\x06\\\x18\xe8\xa1\xec#o\xbf2\xc2\x86L\nO\xc2B\xe9Ut\xbf\x1fv\xb24\xe8V\x18\xaa).iEU\xd1m\xc8g\xbb,\xb7\x1d\xc5\xd9\xa4\xd7s\xe2.]\x10\x95\x0f0\xf2URb\xacMP\x9a\xd9\xa4\xc8\x1d\xca\xac\x1a5U%\xa16{Y\xf1 r\xaah\x88\xbb@\xd7OS\x92\x8d\xb9\xdb\xd6Ou\x1a\xbb\xa5\xd9d\x03\x896\xef'\xd1&-\xb2\xba\xd6\x90\xac\x9a\x18\xc4\xc4\xdd\xc5\xfc\x95:1fJ\xcd{E\xdbT\x8bm\xda\xddp8\x0d\xc5\xf0\xfd\x1cdK\xe9]@\x1c\x01!\xca\xa2\x91\xdeR/\xb4\xe2\xfe\x9c+\x1d\xe3-c\x1b\xd8\xd9Y\xf7\x9fy\xb9\xfb>i\x8az\xda0\x08\xeb\xc9\xcb\x14\xc62\xb2\x11\xee\xddZ\xdc\xb7q]4P\x95\x14\x16+|\xd1F2\xe4c\x85\xf4T\xa7[VS\xeb\x95\xafx\xba\xaf\xb8\xd0iA\x06N?_\xc9<\x88h\x18v}\xd9\xec\x05\xca\xf5\xea\xa7\xd5\xf9\xec\xad\xdb\xdf.*\xd5\xdaA\xcc\xd0\x0eb\xa8v\x10+\xb5\x83\x9em\xc8\x16\x0f\xfbI\xb2h\x96Qo\xf9\x91\xcdos\xa2.X\xf6!\xbf\x0c\x03\xafp\x94f\xe9\xb9\xe6\xf2#\xcd\xe5Ov\xda\x18w\x194\xa7w\xedn\xa4\x14\x99\x0e\x0e\x80=\xd3\xaf\xe4\x8f\xaf@I\x8b\xb7\x81\x0c\x04\xd7\xcbv\xc7g\xc8\x98\xd8\x06D\x05\xd5\xb3\x8d\x07||\xc6\xce\xfb|W\xcdl\xdf\x8d\x7f;\xe1s\xf3~\x10\xcc!*)\xe3B9\x86[\xdcQ\x15\xa8\xae\xa6\xae\xa6l+j\xa9\xacPbS\xf9\xfa\xb5\xaf@\xaa1\xb0\x1b\x8fQ/\xcc\x8d!L\xedc\x02\x96\xf0\xb4\xdf\xa6\xb2\x93\x19\x88\xcd\xaa\xc56R*X\xdd\xc9\x96a\x82\xd7l\x1d9\xcd\xb2no\x17\xc9_\xef\xde\n\x94\xb1<\xbdY]rp\xc7*\x7f\x8d\x057\\ys\x9dD\x8c\xdc\x98\xc9U\xed\x00\xba{\xb23\xd9\xd9\xc3{\x95\xfc\xb3Z*\xa3s\xf2\xa4:\xed\xe0W\xf3\x7f\xffo\x9dy\xeb8\xcc*\x04\x0c\xa8\xe6\xcd\x92s\xd8=3~^\xc3|\xe0\xb3\x1dkmy\x01X\x0f\x0cp\xab\x91i\xb1\xb2\x95V\xb2\xcf\x1b\x9d\x90F4\x9b\x19\xc7\xf2\x0e%;0_\x12CR\\Y\x19\xc1\x12\xda\xf6?\x18/\xb53^\x86^\x0e\xb7\x9a9\xed\x0c\xa5\xa9md\x1a\xdf\xba\\\xda\xddvG\xb8\xaa\x0e\xd2\xbf\xca\x04\xd7\x16\xdc\xd5r\xda\xe3\x96\xb4\x08\x02m\xbbS\xd6(\xc5\xd57@-\x8e\xd3\xbf\x891\x17\x1eb\xe4I\xdd3\xba\x0e1\xf2\x14\xb1\xe6*\xcd\xad\xf6'\x0d\x07\xa79x\xa4\xaa~\xbai\xd9\xacd#\xd5S\xabb\x1e_\xfc.6E\xd8D\x12p>%L9\x8f\x0d~g\x10\xef\x97\xaa\x1a\x87:_\x90\xaag\xfc4\xa3Y\xe0I\x1e\xca\x10\x0f\xe5);6\xa3\x19\x9b\xf2\xd0\xbc\xb4NP\xea\xe5\xb4\xd5k{\xd3\xdd\xa9\xe0\xe2\xcb6)\xe5\x8a\xb4\xe3\xb4V\x8b\xa4\xea!\xa8v\xac6EN\xfd*M;*5\x0c2\xfaUX\x1f\xa8\xb6\xfa}\xa6\xa9\xa8\xda\xccW\xc1J\xed\xcfV0\xad\xe6\xd9\xb2\x8a\nP7,\x0d \xc03\xaa7\x18\x12>\xa6\xbe\xff\x81\xf30\x88\x16g\xdc\x0dk\x18\xe1^\x1c \xef\xee>2\x10\xbfD\xfa&\x14o#@\x8a\xb5\xcf\x9a\xe7\x0d\xa9\xc5\xb8o\xe1Q@\x15\xc6eD\xd3|p.\x0eH\xb6L\xf8\x15\xacjA\xd8I\xfd_\xe7\x98F\x11\xcf\x88\xc0<\x84\x12/\xa4iJhJh\xf1%\x07\xc1\xee\xea\xd6\xb8\xd0\xb5\xca\xca%/\xce\x83\xea\x92\xa8\xce\xa1\xa6\x9bM\xf3\x14X\xd3\xac\xdb\xe6G\x9b\xbb\xd4\x10\xfb\xb0R\x9dB5Z\x81\xaa\x8e\xe9-\xf2\x97z7\xc6A\xfa:\xaa`\x17\xe0\xdc\xea\xb5\xe3\xb2\x19\xbcE\xd5k\xb2\xf6\x9en\xd8\x1c\xa3\xea\xba\xc3w\xbc-\xb5\x0b\xa1\xceU\xb5a{\xcc\xea\xdd\xa6\x1e\n\xde\xa6S\x96}\xab\xf6\xe8\xaa-m)1\x88\xc9a\x9b\xa8\x81\xdf\x07j\xb0\x9c\xc5\xfb\xb6\xb3\x189\x8a{\xac\x1a\xe4\x0e\xb5f\x87\xfa\x8e\xfbu\xa5\xc5[\xdb\xad\xfa|%\xf5\n\xab\x83jbbjb\xe2j\xa3\xbb\xcd-\xad\xbeb\xa8\xbc\xa0\x08\xfcc@\x1e\xc9\xf6v\x93\xf8\xaa6\x91\xa2\x9d\xdd\xd4\xf0R\x0b\xec\x1d\x02\xec\xd9\x88\xad\xe2\xecfJ B\xa5\xf1\xb9m\xe2\x10D\x0bW\xfa!\xa8\x93 m\x14|*\xfb\xc9\xaf\"\x96\xbc\xe4^\x0e\x12\x0e\xe55\x89\xaf@HfSb\xd06\x0b\xe38a\x1e\xf5\x96\xacP\xe5\x967P\xdcEn1\x9b\xf2\xc0\x9aT\xb7FX\x1d\xca0^\xceo\xd7{\xde\xd6h$\xc6!\x17\xbd\x1f\x8d~\xbb\xdecNm\xaf\xd5\xce\x02\xab\x8eW\xf3\xf0\xef\xaf\xc4^t\xdb\x1a\x04\xba\xadQ-\xda\xea(\x930\xce\xa3\xea\xd8\xd6j/qK\x8d\xda\xa0\xf7\x82R&\x15b\x03\x0f\x1b\xc0Q4\xea\x14\xb8\xc0\x01\xe7\x19J\xd0\xba\x07\xd1]j\x99\x99\x91Y]k\x86\x07\x0eP.\x06\x86\xf39\xe1\xcfI3\x80\x1d\x89\xea\x9b\xb4\x12\xb5{G\x1a\x03e\xcf }\x0e\xbfh\xb5t\x80\x96~N\"2\"\x01\xf9\x9e\xec<\x1f\x80\xbc\x8bU\xaf\x91\xa2\xd1\x08-\x16\x90\x11\x89T1@\x04\xd5b\x01ZL\xef\xfe\xe89\xc9G\xa3\xe7v^\x1dB\x02\xb71\x8dHK\x1b\xad\xb0\xac$R\x15\xa5\xff\xa9 a\xae\xb3j\x0b\x83\xf4(\xf2XZ\xa5\xc8m\xa7\xacm\x89$\xc9lr\xbe\x89\x96W\xdb\xdc\xf5gIk\xea\n\x06\xea\xb5\x88\x08\xda8\x07i\xe8\x88\xec\x0e\xbcS\x05\xd1\x01*\xf1v\xa6x\x1c\xb1\xeb\xec4\xb8\x0c\x83h\xf1\xdcJ\xa7\x93\xda\xc5X\xa6\x14Z\x9e\x14\xd6q\x12\xe9\x0e\x86d_2A\xe3H\xab)>x@j\xf8\xcc\x80\x90\x11\x0d[\xbeJ\xcaE\\\xc7 \x16c-\xfd\xb4G\xe0\xb6;\xd3\x94\x04\x981,=\x17\x8d\x9e:A\xe1U\x0fx\x1c\xab\x9d[\xcedVWa\xba\x9b\xa8\xe2vD\x81\xc0\xd0\xb7\x15q\xdc\xcb\x85\x8aEj\xfa\x08'\x07\xf1\x1bL\x19h\xb1:x\x16\xef\xcb\xfafqJh\xf3\xb0\x15\x83\xd7\xb5\xd7 (\x02\x07)\xd8\xce\x04\xd1B\x85M\xb4\xb8\xa0k\x9b_Qfv\xdb6\xf2\xf1<\xcc\xd3%\xb4\x82)-\xf4T\xaa\xa1\xf3\x86\x04Gv%+\xbb!e0\xc9`\x08\x85A\x17m\xee\xd6<\x91}%W\xcb d\xc4\xadKT\x8cX\x82 \x97\xe1\xe4E\xa5n-b\xe1 \xa1\x81\xc5Qd\xce\xf8\xf9\x90,\xc7\xcaC\xd7\x99\x9a\x03\x97U\xa6C:\xb53\x87j\xd8\x18;\x1c\x17\xc7v.\xde\xa6\xa9\xd1\x18&lu\x18$Du\x81\x18\x19\xf5\x01h\xde\x19\x96M\x06n\xb1\xa2\xaa!\xf8\xc5qv\xc5\x8f\x92\x05\xf0\xb5\"\xa7\xe2dx\xad\x1c\xefW\x1b|\xc1\"z\x192\x7f*0d5\xa7:\xc4X\xdc\x95\x9f_\xbf{\xf9\xfe\xe7\x8b\x1f\x8f\xde\xbd|s2%\xc1\xd8\xa3\xd1\xa7\x94\xbd|\xff\x96\x1c\x92\xab \xf2\xf9\x15\xc1\xca\xa5,\xfb\xb1Vy\xbb\xe4\xa81\xe1bQT\xc7\xa6\xf1\x85\x13\xdd\xb1\xce\xaa\xd5\x10\x88Sb\xab\xb5\xd6 mV\xdar\xfc\x96U\xb7U\x9a%4\xfeAJ\x1faQ\xf4\x13V\xeb\xdb\x0drH\xf8X\x06\xf0W\xb1\x89\x96\xa0Z-\x0e@\xa8N\x124r\x99\xb1\x81\x16\xd7v5\xe8X\x892o\xdb\"%\n\xbd\xaf&\xadx\x14d<9\xf5\x12\x1e\xca\x88\xe8]\xd3\xaaQf;\x94x\x98\xeb\xb9r\xad\"\x8e\x9b\xbeV\xdb\xda$<\x8a\xc1\x97U\x0c\x89\x93B#\x1dD\x8d\xa2\x8aN\xcc\x11\xe9)\xd3(\x17T\x1b\xd1$0f\x0c\x86\x06\x02\x05\xb4\xc6\xeei\xb7\xcfI\xc7U\"\xce\xf5\xedr\x81\x1eF7\xf18a!\xa3)so+\\(\xde,$\xd7\x12RoEr\xf5S\xc1.\xc4`?K\xe4\x067\x1d\x86\x0eY\x91q\x88\x8c\x03\xc4\xc5\x8a\xe9\x82\xfd\xf2~>O\x99\x0c\xd82\xf6\xb5\xc6\x82\xfe\xa1m4\xe4:z\xc3\xe6\x88\x00\xf5FW\xf5\xeb\x06U\x9d\xf1\xaaX\xf0+\xc1\x82\xceC+;\xbfm\xa9\xf1O\xd5_\xb7\x9a\x89\x92\xf8\xdd\xaf3\xaa\xea\x9acb!~\x1b\xd7\"\xed\x81\x16\xf6\x9e\xe0\x91\x16&\x8f\xeb\xf5\x84\n\xbe\xde\x1e\x0f\xa7\x97q\xbe\xc9\x10B\xd0q\x10\xfd7\x83qi\x8e\xef\xcb\xf7ou\xfc\x8d)I\xda OVqvcT\x9b\xb7\x02\x0b<\xf3!\xcc\x17A\xf4c~)\xb8\xdf~\xc0\x9f\xb2 L\xc5\xd9\xde\x05~\xb2\n\xb2\x8c%S\xf0\x9bg\x05\xfd\x11t\x88\x8a&\x87m\xb0\x05\xef\xe8\x95P\xd5\xf5\xf6/\xe0\xbc\x1e\xd7\x99\xa6\x00g\xb1\xa8e-\xa9\xb5\xf7\xb4\x9e\x9eV\xd4\xc8'\x8f\x9e\xd6\xd5\xc8\x15\x17\xb6[\xff\xbe\xd7-\x03\x01\x8e\xe0\x94\x85r\x08_G\x82\xd9\xa5\xf8\x98+\xd9H>N\x80\x16eE\xa9\xea\xc0c\xf1\xb9\xcd/v\xca\x7f\xb4\xbc\x97\x8e\x0b\xa2\xaa\xc3&\x92\x8eK\xa2\xce\x85X\xe3\xbd\x0c\xad\xea\x02)+\x1dP\xa9\x1f \x94S\x17D\xddu\x04\x94\xa4\xa8\xa2\xb0.F\x9da\xc6\xad=:\xb6\xd1w\"\x9e\x05\xf3\x9b\xa30\xc4\xbeU\xed(*\xf8B\x98\xfbv\xc9W\xbb\xe5Aa^Pk'\xa8Q\x94\x94Ldx\x99D\x8c\x14\x0c-\xd5\xca\x86\x8e\xef\xd5\x06\xc1\xab\xad\x83z\xc5\xb7\xb2A\xc0:\xdf\xf1\x9d\x8d\xcd\x12Z)l\x9b\x81\xc1&\x0d\xae\xf8\xa8n\xfb\x18b\xa6`W\x18hl\x11\xed\xca\xba\xa1\xc6]y\xed\xcd\xae\xf3\x82,\xc5>7\xb0.\xcc&\xcfR.\xbf\x12\x91%\xee\xdc\x14)\xa4C\x12\x0f\x86$\xa8\xf2\xee\xf3\xba\xe1\x15\x14\xbf\xe3\x01\xd6\x90\x05*]\xea\xddz\xdc\xa7@\x1dl{\xa8\x18\x8f\xb6h)\x94\xd78\xdap[*\xa8%\x96\x8d\x98KO\xe6\x85\x90\xe0\xc1\x03\xe2\xa4\xfa\x80\x01\x85/M\xb9\x8a\xac-\xd71\x8f-\xc8W\x8cZ\xf3\xe8l\xce\xeb\x82e\x928N\xa7$'\x87=N\x00\xcd3\x16tt\xd16u}\xff\x91F\x8b\xd6\xa0,`\xdb1\xce\xd8u\xa6d8vP\xb8\xb3\x1d\xfby\x1c\x06\x1e\xcd\xac\xd7\xb5 \x84\xaa?\xe3\n\xcb\x9dI\xb7\xa6C\x92\xc8\xd3\xca\xff\x00\xbb\xcd9\x89|@\xaaI\xe6\xd8\xb9=-rK\xcc\x16\xb6\x9e\xb9-\xbc\xa1\xf8VC\xed\xcf|X\xe4OA\x03\xa5\xe9\xf7\x95\xe0\xcc\x1e\xe9\xc2\x07\xc4\x98$\xb9\x12*\x84\x8dX4H\xb2mh\xe5-\xb1`\x9dv\xd4-k\"\xe6\x174mz\x86\x05\x95\xf3M#o\xc9!\xdep\xd7tKH\xb9,\xed\xb0\xd2\xb7\xc1\x9c{y\xda^iP\x02v\xd5\x99k\x7f \xb0\x86\x8f2\xd7\xe6\x91\xb0]$\x90\x8fa\xe2\x0b+\x80\xe2\xeazH\xf21\x8b\xfcf\x06>\xf9:XC\x9f\xd8=\xa8\x07\x00\x82.!b\x98\x04P\xb723\xf5\xd1\xaf\x8cpu\x14\x07\xe4\x90\xec\x10A\x04g\xfc\x14\xd40\xdcA\xe7~\x0eA\xf2\xee\x85<\xd2h\x02\x1f\xdfPa\x15\xf1]p\x06\x12e)\xec\xe8P\xedh\xb7>\xc6C=\xea\xaau\xf6\xe5\xe8)\x0d\xa7z\xf9\xd0,/^\xcd\x99R\xef\xd5\xae\x87\x9bt]\xf0\xbb\x1e\xd9&-\xee+c\x13\xadV\x90)\xde\x9bX\x0c\x06\xe03W\xb94\x8b\xf5\xf0p\xbb\x03#\xad\xd2\x14\x8f=\x1e\x864N\x99%`k_\xf4\xe6\x8bs\x83L\x89\xd7\x81\xe6\x04\x9c'\xd0W\xcfu\x8a\x90\xf3\xa9\xf5\xb8\xear\xb52\xd4\n\xcb]\xe7V\xf7icX\xbagbQ\x90CIL\x00\xf2\x801!\xd3\xe2\xd7\xf7\x05\x8c+\x01X\xe4\x0f\x15\xa2\x03\x08\xf0Zi\x94\xd5\x99,\xf2\xc1\xd4\x14?\xd9d\xba\x9c{\xc7[\xd2\x84z\x19K\x1ci\x19\xce[\x8e=^\x14\x16\xcb\xa4R4!\xa3\xa2\xb8\x18\x1a\x8c\xeb!=\x84\xb0D\x1d\x1b\xc8)\xd3\x86\xc8\xf4Q\x81\x1eN\xf6\xa5E\xd4\xb9\xc1f\x81;8\xef\xdc\x86DI\x1d\xde\xd2l9^\x05\x91[\x0e{\xc7G\xf2\xaa\x93\x03=\xad\x94L\xcd\xca\xe4\xf4\xb6\xa9\x95\x89\x035\x1a\xb3\xebL\x94\x7f\xf0\x80P\xf2=i\x0d\xc7C\x0c|\xdd\xe2\xa0\x8d\xa86Ri\xff\x92Z\x01\xed\x9aJZ9\x15\xb4\xd6i\xc7xx\x1a\xd0f7FTo\xc1\xe9\x87\xd7\xa7\x87\xf3\x0d\x11\xa0~\xe6%\"\x0c\xe1L\x15\xe8\x9aK\\=\x04\xc7Eb\xc1\x1f\x85!\xd4\x96\xba\x10/\xe8{\xc0 n$\xb8\x0c\xf9\x959\x00\xcb\x99q=U\x91\xa7+\x82\x8d:\xd7\x08\xb6\x91-\x8a\x1a5\xe1\xc2{b\x1d\xfeN\xb1>.\xc5\x93\xb3\xbc\x11\x13T$\x17\xdcKbWB\x00\xe1\xfdx\x1e$\xa9t\x91_(\"\x18I\x95\x82\x9a\xdb)\x12\xb1\xdb{n\xff\xa0\xdd\x16\xca\xd4\xa0+\xf5\x1a+\xea\x86\x8d\x82\xb2\xad\xa5\xeaCuH\xff\xd4\xfc\xd5\xdb\xb3G\xc5`-\x01\x9cl\x18\x9f\xed<'\x91\xb5'{\x92\x13,\x88\xbf6\x1cJ\xc1i\xed6\x89\x80\x1bQ\xa4\x90Fr$ /\x94\xea$%\xdf\x9b\x86b\xf6\xad\x16\x81\x96)\"\xd3\xd4\x8f\\\xceS\x92\x91\x11\x12\xa6\x8a\x90FHi\xfd\x04\x851b\x05\xb8\x91\"\x07\x8c\xbb\xd1\xe0\x9b\x9a\x7f\xec\xef\xedX\x8c\xb0\x8be(\xd5\x9c,\xfc\xfa\x96b{\xb6\"\xb0\x01WVe\x11$%n&\x13\x137\x1a\x14\xfaR\xc6:\x13\xb8\xc2\xf1$\xf1\x98*\xbb\xb6C\x88f#\x93D\xb1)\xd9\xda\x92\xf1mhR(\xda\x7f\xe0i\xa0\xb9\xb4\xad-w\xf2\x84< V 1\x84\x0d\x15\x8d;\x0f\xdb\xa4c\xd8\xac\x17~\x80F\x1e< {\xe0\xe9\xa6\xc9\xdb\xdc\xa1}\xfd\xda\xa1\xb9^\x97\x899\x19W\xec+\xe0\xf2\x8fL\x8b\xe3e0\xf6\xd9\x9c\xe6a\xf6S\xc0\xaeD\xa6$;Pd\xb6\xe5nI\x17\x83\x16_Qc0\xba9\xac\xder\xaa\xd4)\xeak \x84:\x118D\xaf\xa4W\x95\x9c\xa5v{\x13\xe0\x1d]\xb1\xfb\x9dwg\x99e\xf1\xf4\xe1\xc3\xab\xab\xab\xf1\xd5\xde\x98'\x8b\x87\x93g\xcf\x9e=\xbc\x0e\x83\xe8\xb3\xd3\x94\x90!\xf0\xbf\xbc}#\xca\xec?\x8c\xe8\x8a\xa51\xf5\x98\xd3\x94\xa05\xf1\x12\xf5<\x16e?\xb2`\xb1\xcc\xa6\xc4\x91\xaf\xa3%\xbc#>\x9a\xa8\xe7\xe5\xab<\x04O\xd6;H\xb6\xef\x07Y\xb0\xb6d\x86\xc1\"\x12s\xff\x03MY\x18DL|O\xa7\x8d.U\"\xf6\xd10\xe4W\x1f\x19O|\x96@\x99\xf2\x15\x85\x8e\x97\xf4\x92e\x81\x87\xb7b\x15\x87A\x96\xfb\x966&\xf42\xf0^\xf1d%>\x04/\xa39OV\xd8wR\x0fn\x07\xb1Z\xb2, .\xf3\x8cI7\x88N\xe5\x1d\xabJ\xe7\x8b\xa5g\xc2\x8bw\x0c>\xcf\xf8G\x06\xc6\x92\x02\xba|\xc3`\x7f\x0fVy\xb6D\xdb)\xc6\xfcU\xc2\xfe\x91\xb3\xc8\xbb\x99\x12\xa7\xf2\x8e\xd4%\xf2?$|\x1e\x84LA\xab7\x0b\xac\x98\xcf\xd3e0\xcf\x14\xb4x\x1f\xa5\"\x01+p\xc9\xaf\xf1V\xb2E\x10\xe19\x01M\xf1\x8c\x1b4\xd9\xa3\xa1\xf7\x16\x0e`G\xffD\x1a\xe2\xd1\xb8\xd8\x0f\x1e\x8d\xed\x9b\xc1\x0b\x83\x18\xffN\x18\xc4\x1f\xa8\x18tG\xfc\x1c\xc54[Z\xca\x7f\xcca,\x01,\xc9\xd1\x91\xd4\xb5}\x8a\x02\xc1w;\x95w\x0c\x9e\x87\xb3#\x1b?\x98\xcf\xf3\x94\x1ds\xe9\xabsJ\x9cZ\n\xd2\x1b?H$go\xa9\x11\xbc\x9eZ\xf2\xd6\x81m |\xbe\n\"Z\xc1\xef:\xa9\x0d\xbd\xfb\xb9\xa5:|\\}\xbca\xcc_0\xb5\xb7\xf5O\xe4[,dkj\xed\xb8\xd4[\xfb\x81z\x9f\x17 \xcf#_\xd4\x05I\xa3\xcb\"\x0d\xab4\xc2'U\xd0L\x91m\xda\x04\x9b\x9bD4\xfc\xc8R\x9e'\x1eK?\xb2\x7f\xe4A\xc2\xe0\xa3\xb6<\xe4\xe3\xf3 \x0c\xd1\x0f\x88\x8c\xf71\xf5\x02\xf0k#\xdeF\\\xbeZjQ\xa8\x08 -\xa8H\xeew\xdb\xe72\x96|d\xa9\xacB\xfe\xb6V\xa1q\x99\xf1\x86\xc1\x86\x9c\xfb\xc7\x02\x13\x08P\xf12\x02\xbc`\x035\xba\x0b\xc0-\xfd\xe5^\x9e\x8a\x99\xc5\xfb\xc2\xa3\xec\x15]\x05!T\xc5\xa3l4\x877\xb4\xa2(;\x05]\n \x98\x06\xbf\xa3\x03\xa7\xc0\x8e\xfc\xff\xce\xd3\xcc\x04\x1eQH\xb2\x95\xc9\x12\x96y\xcb\xa2\x80|\xb5\x02\xdf\x84eC\xc4\x8b\x05\xf0'\x9a\x04\x12U\x00\xe8Z\xbeZ\x80\x7f\xd6g!\xc0^\xd9\x0eC\xa9\xae\x83\x0fg\xc2Wx\x06\xbe\xc3\xe7\xf8\x0e_L\xf0\xe4]<9\xbc\x89\x97\x8a\xfe\x82\xdf\xa3\x08'\xbe \xf3}\x12\xb0(\x03\xcc\xf0#O\x82\xdf\x05\x9f\x18\x16%y\x99;Z\x16\xd9=\xea\xfa\x89%Y\xe0YjZ\xabL[=\xe0\xb8\xdb\xd1?1\xa8\x84\xfa\xa2:\xd0\x12\x99K\x9a\xb5\x91\xd6RNo\xc2\xca;\x02\xbf\xa4\xd1\x02Ned\x98a8\x8e\xfc\xf5/S\xe2\xc0\xef\x11\xf5\xd7\xa3k\xac\x16\x91\xfb> \x16AT\x02sxG\xe1\x03\x9f\xf1EB\xe3\xa5\x85\x90\x0fVt\xc1L\x92\x01\x12ZI\x86 \"xU\x11\xbe\x86\x80\xd8\xf1X\x8c/\xeb\xcfx*\xbeJ?\xe3_\xf8\xbc\x87'?\xc2\x93Y\x12\xb1\xf0-\xcd\x92\xe0zJ\x1c\xf3\x15\xe9\xad\xcc\x16\x93\xfa\x06\xe4UE\x892\xc9R\xca6\xd9\x9f\xd9\x0d\xdci\xa4P\x95\xfa\x8d\xd6qs\x1a\x8b\xd3^\x01\xaa\x17\x1c\xf2,Xi8\xf8\x89@Iy[\x81;\xcdW\x14:\xcbXr*p?\xac\x0b\xf9>Je\x02V@\xa040\xa6\x95'\x8d~\xb7\x1e6`\x8f\x0e\x05\"v\x14-\x00\xe96\xd2\xb0r\x1cp\x012\xb2+\x9a|f\xc9 \x90\x1c\xf2\xf7\x88\xa1\xb4\x86\xcc|\x1b\x18\x80\xab\xc0\x0ex*\xaf\x085h*o\xa1,\xc0\x05\xd7c\xbeZ\xa15\xf60\xde\xac\xb0?\x07>\xac?\xe3\x0d\x85M\xf1=U\x84\xcb-qV=\xc9R\x9d n\x87\xcb\x96lE\x15\xa2\xc6>\xcf-\xd2\x82(_\xbd\xf72\xba\x86\xf5[\xbe \xdf\xd0R]\xa4\x12\xae\x89\x164O\xbaa\xc73\xa5<\x04\xcd ld\xa7q\x00\xd9\xf2m\xdc6_\xb3d\x1e\xf2+k\xa6\xd8\xe4Z6:%\x8eN\x1a\xc5*\x0d\x1b\x17\x05s\xb6\x0c\xbc\xcf\x11KS\xb3\\\xa6\x13\x91\x821\x0d\xa2\xec\xbd\x92\x08\xc1\xcb\xc8&\x10\x8ai\xc4S6\x018\xf1k4A\x81\xb2e\x81&\xcb\x17\x1cRP\xe7\xb5\xf5\x88\xa4\xda\xcb\x9a\x07v=\xc9^\xaa\xf6)\xeb78\x1c[\xa0\xee\x0e\xe0\xf2}\xc4 \xc1V\x00\x97\xa3\xc8\xac\xa3\xec\x17]\x8f\xf8m\xad\xe2(\xfb\xd5\x80\xfb\xb5\x05\xeeo\x06\xdc\xdf0\xb8\x84\xa5,Y\xb3\xa30^R\xf0\x1bo\xbc\xb7\xc1\xa71\xf3\xb2\x8fby\x9b\xa5\xcaT\xb4,`\xee5+\xc6\xb7\x92\x80\x94\xc07\x9d \xa2r|\x18\x136\x17#(\xfea\xd5\xb1\xf9\xaf2\x17\x1b\xb2\x82\x9ey\x0d+\x0b\x00U\n\x08cP\xba=a1\xa3\x19(\x89A\x81\xe2\xcd\n\xfbR0\xe1N\xf1\x1b\x85\x93<\xe8\xc9u\xc6\xa24\xe0Q\n\x05\xea\x89-%_1\x9a\xe5 3\xcb\xe9$\xb4\x94\xd2oA\x074\xcdCK\x16\xcflR\x94\x04g7\x12\x1c\xf7\xa6\x1e\xb5\xb0\x87)c8\xc3\x9f.i\\!I!\xa1\x95$MC\x1e[\xbe\xa2 \x184\x8fyyH\x13C\xe8SO\xc2\xbe\xa5@N\n\xb9\x84SO\xc2K\xd9\xba\x1b'\x8c\xfaoY\xb6\xe4>\xd4U\xbeb\xf5\x94\xda]\x02\xb8|Ca\xfd\x97l\x1dh\xe1\xa5\xf9\x8aB\xb3\x15.\xe0\x169kKN\x90y\xcb\xb3 \x84\xe5h\xbc\xa1\xf5\xf3X\xd3\x86\xe2\xb7\x95.\x14\x99\xa5\x0c\x02@\xed\"\x884K\x82\xcf,[&<_,\x8dc\xb3\x92\xdevvV\x00\xcd\x03\xb4ZC\xdb)*o\xb8,\x03\x94\xf0\xcf\x96\x95 Y/i\xba\xa4IBeWE\xca\xc8\xd7I\xf8\xa7T!^\xae\x81\xa2\x14\xb7\xaf\x04\x01\xf3&\x88\x98G\xe3\xb2L(\x13Z\x0b\xfc7\x0f\xa2j \x91b-\xf26\xc8\x04\xdd\xb1\n\x8c\xa6\xad\x8a4k1s\xbe\xa1L\xeb\x8c\xf3\xcfL\xd3\xc2\n\xfc\xcaB\x0c\xa7y2\xa7\x1e;\x95X\xc81_1\xe8\x1b\xb1\xd4\xdf\xd0h\x91\xd3\x05\xc0W\x12\x90\x12\x19\xbd\x0c\xa5\xb7&\xb1d\x8c7\x146Y0 \x02\xd4/+\xcc\xaf\x05\x0cv\x96e\xec:;\x02\xfdV\x01\xc6\xae\xb3\x91\xd4v\xb5\x80\xbed\x1eO4\x0e\x00p\xbfH\xb1\x141\x91/\x94h\xc3\xbd\x02\xa0\xa0\xf9\xca\x17\x0c\x92\xa3\x1b!+\xe98$7\xc7%\x019. \xc8E;k\x14t\x91\xd6\x86\x06\n \x13\x05\x94%\xdb\xb6\x7f\x1e\x05\x9e\x8d\xb7Qy?\x04~\x00\xf5\xc1\xdb\xe82\xf0\x03{E\xa0|e@\x83\xaa:\x0e\x9e\xa5\x1fXr\xb2\x92\xc0Y:\x8a\x05\x85\x8a\x11\xbf\xeb#\xe3>\xd7Y\x8f\xca\xeb]\x0c\xf8G-\xaar\xd6#%\xb6\xc2\xc0^\x9b\xb2%g=2dM\x18\xf8\xdb\n\x87\xe8\xacG&\xcb\x88\x15P\xdb\n\x19\xd65\xf32\x9e\x9c\xcc\xe7\xcc\x13xF\xbe\x8e\x18\xbcc5\xb1$\xb5\xb1jk\x96dG\xfe\xfaW\xa8&\xc9@\xf0\x86\xa1\x1d\x91Y\xca\xdd\x00\xb4E\xecVB\xffZ\x83F\xeb\x0e\xd8\xd5\x0f\xfcZ@\xca_\x16\x983\xc0 \nL\xbe\xa0\x90ip\x19\x846n\x18P%>\xacW<\xf1K\x89\x8fxk\x91\xf7\\% \xa9Q\xb7E\xeam\xb4\xc2o\x8cp\x9a\xf1\xba\x90\x95\\\xdb\xef\x87\xafq\x04p\x8d#\x80\xeb\xe3%\x8d\"\x16J\xad[@\x91\xf5$\xec\x1ba\x10}>\xf2\xb2\x1c\x88^\x07^\xa7T\xbe[\xc1\x13/\xe1\xa1\x01.\xdfm\xe0?& \x88\x96\xb0\xcb\x04\x15EC\xe6G\xb3\xd2\xb6\x1aO\x97\xfc\xaa\x00L\x97\xfc\xca\x06x\x16dF\x95\x99x\xb3\x82\xca\xab\\\x05\x89_\xe2^\xaf\xc2\x1f\xc0\xd3\xb6s\xbd\n\xa7\x97\x14U\x98\xb8^\x85\x11\xbe\xc8 \xe7\x17\xf8\x00\xd4\x10\xa5SLAG\x81\x8a\xb3W})\xa4\xe8:\xbc^\x85b\xcd\xea\xf6`J;D\xfa2@\x1as\x83/\xae\x1b|q\xdd4\x17W= \xf9\xf2\xefh]\xbfs\xbe:\x8a\xfc\x0fT\x1cQ\xe5K\xab\x7fT\x8a*\x1f)\x17\x02\x81\xc0\x95\xf5@\x11Dz\x1982Ug`\x84R\xcc!\x04il\x85\xa4Y\x1dil\x806 \xb9\xec\xdb >v\xd6!\x17z\x1b\x84Z\xe1\xad \xb0\xb2m\x10zI[\x8c\xdc\x8a\x85h\xcfWk\xb0WH\xd9\xc6\x8cL\xcd\xc8]\xa4\xaa\x9d*#\x02\x8e?\xb3\x9b\xd4\x0d\x06\xe39ON\xa8\xb7t\xed\n\x84t\\\xae\x08\x19\xe7vgH\x02\xf1\xeb\xc1\x03\xe2\xd2q\xe3\xeb\x12H@\x18\xeax\xdf$@\xc7N\xddu\x02\xc7\xedW[\x82\xfe`\x0e\x15\xa4\xa3\x85Guk\xd7T\x81\xef\xe2>>\x1e\xe3>>vw\xeb\xd5\xcf\xc16\xbdj\xcb\xaa50\xdf\xea\xf8\x05\xa69k\xc3;\x8b\x80\"/\x0e\xc8\xa4\xe6=\xb1i\xaeN@2\x12\x02]\x83o\xd0xIS\xe6\x7fd\x8b \xcd$\x15\xaf\x97\x10\n.\x1e\xe5\xf1~J\x1c\x1eID\x85\xa0)\xfdh\xd7\xf6\x06\xb4r\x11\xe5\xa0e\x90\xf5M@\xd9&\x16LC\xe4\x01^\x9a9\x19\x8f\x7f\x08\xf3\xc4\x19\x12\x07\x04\x01\x10\x1b\xfb-\x8br\x95\xf2\x8a{y\xaa~\xff\x95\xdd\xbc\xe4WQ\xf9\xf6)V\xbf\xdf\xf2\x06\xe8I\xe47'\xab\xa9\xa2\xbf\xa1EV\x8b\x05q\x87\x0b\x12\xfbf*\x0dM\xa7=\x0d\x82Mc\xd4io\xd3\xe0\xc2du\xda\xcfB\xd8\xb0j\x9dV\x8d\\\xf1m\xdb\xb17\x88\x1a\xed\xa6\xa5a\xab\x85b\x0f\xdb\xc4[\x8e\xbb\xb4KP&\x84\xd3\xc2PA\x07\xc7o\xb1\xf3\x92Q\x12\xa4\xf1I\x0b\x14\x8f\x05\xd0%\xcf#\x1f|5\xc4v\xd8\x90\xcd3\x13\xf8\x0d\x9b\xdfn\x94\xbf\xba~m<\xc0\xb2n\x0d\x8a\xfa\x9e\xbb\x16\x07,6\xde\x80~\x9a\x03\xa9\xcd\xfes\xc3\x93J\xac\xe6aH\x96Cbq\x10\xa7\x06\x9fC\xb4xr\xa0]58C\x91\x04|\xa6\x98\xd7!I\xc6\xa5\xea\xba\x8e\xb8\xf3Ry\xb7c\xa9\x0bf\x99\xd5\xfe\xfd \xf9\x8c%N\x93h\xfce3X\xee\x9aE\xa0\x84\x9aNImF\xd8u\x96P/\xd3wtu\xca\xa4%|\xf4\xd6\xa2\xc3\xea_\x0fdF\x0em\xb1\xd3\x06d\x8a\x9a[\x88'\xbd\n\xdam\xde=\x9a2\xe3\xd8\x9bZW\x9a\x1b\xba\x1c\x82\x9d;Y\x923\xe9#\x9e\x8f\x95\xaa\xed\x89\x1f\x80\xc8Q\x9a\xf1\xf82\xb6\xc7R\xfa\xa2\xd5\x07T\x8b\xd1!\xb8\x82\xc7\xb3\x8b\xf6\xc1\x99mo^qd\x96\xc7d\xf1\xe5\xbb}\xb8<\xe9\xed_\x87\xe3\xd6\x12\x17\x8b\xf4\xfc\x8eI\x89\xe0_\xaa6\xe9S\xdc\xd2 \xb5\xa6\x14\x19@n\xa4E{G\x0b\xeaT\x8b\xbdz\xb1t\xe7\x83^\xdd\xd2$TG\x97$m\xd5\xd9!\xd5\x91\x0edFZ\x1c94\\b\xfa\x1f\xf2\xec\x0d\xf8\xd3d\xf5\xe8k\x16\xaf\xa3%\xf1*M\x97a\xd1\x03u\xb5c\xb5\xc1\xc3\x8d\xaf.!\xf5\xae\xcc\x0c\x1e\x99\xc9\xe6\xaf\xbb\xc9\xfbP\x9c\xc9\xc9\x95\x05\xdbc\x94\x9b\xd9\xdf\xab\xf3J!\xce\xfc(\x8f\xdd{u&g\xae\xd2\xeb\xf0\xb1jM=\xdd\x97\xf0\x8f\xea\xbdZ\xaa\xf4\xfa(\xacUz\x9d\xe9Z\xa9A\xab\xc3/\x14|\xdd\x07\xdf\x8d\x1c\xcd\xfa\xe8\\*\x1e\xad>\n\x17e\x84\xaa?\xbe\xd6\xf2\xaej\xe1\xe8g\x0e\xbd\xe4\xe0G\xc0\xa1Q \xdd\xe3\x9dD~\xe5\xfdu\xc6\xf4\x15\x89\x91\xaa\xfd\x0f8\x97\x8a\x95\xf1h\xf4!\xa47\xc6\xcf3ya\x08)a\xe0}\x86\x1fUn\xc7\xe3\xb1,\x91C]>\xcf/Cv\xac\x81\xfd\x84.\xf4\x7f\xd5*\xf9S\xfa7\x90/\xd7A\xa6\x7fC\x8c7\xfd\xf2~]\x02\x15\x8d\xf5\x13\x0e\x1c\x92\x9f\xcb.)<3$\x0e[\xc5Y\x00Q\xcc\x1c\x16y\xc9M\x9c\xe9\x17_\xfdH\x12\x0e\x15\xce5{\x16D\xb1lv\x10\xadi\x18\x00\xd4\xe7\x92_\xfb\xccn>$pO\x02\xbf%k\x16r\xea\xeb\xff\xcc\x7fI3Z\xbe\xbde\x19\xf5\x8d\x94\xa2\xd5+\x93\xd5\x83\x97\xb7\\v\x14^\xde\xe7%\x94\xee\xf5\xaa\xe4\x06c\x9afL\xfe\xc8S\xf9C\xcd\x93\xf8\x0f\x12m\xe2\xc4 _\xe8\xc6&4c\xe5\xc0\x80s>\xc7t\xf1\xeb\xa4\x8c}\x96\x83\"~\xa9\x1a\xd2\x8c\x86\xa1J\xcd/WrV\xd2<\x8d\x99\x9c\xb9,X\xa9P\xd4\xf0\xc6soy,\xc8\x87\xb0xUS\x0c\xbfu\x07\xe1\xa5\x18\x08\xb8\x1f\x0b\x8cE\xba\xe6a\xbe2\x1a{EA\xf6\x0e?\x97\x8c\x85\xcey\x0f)\x91f\x8d\xd8l\xe7|\x9c\xf1Oq\xcc\x92c\x9a2w@\xb6\x05c\x16\x06\x1es\xeb\x9b\x95(\xcbg\x87G\x10\xe3\xb7\x99\x0bv\x98\x19\x8f-\xd9\x1c\x15x\x90;\x8a5Z\x0c\xc1KiFD\xb6\x89s\x0f\x92\x8c\x04\x91*T\x0f\xe3\x0b)P\xe3Cr5K\xce\x8b\x80\xd9\x00Y\xf3\xd2~\xa2PS\x91X\x08\x07\xae\xad\x16\xca\xce\x18\xe2P\x8d/\x12\xce\x81.}\xfd\xb2\xac\x1f\xa9\xe9\xd4^\xd3e\x9ee\xd2\x0c\xf8@\x06\xe0T\xdb\xdbHH\x8d#W\xa6\x08TF\x13FU\x9a\xf1m\xfdK\xf4\xec\xb8\x95\x92\xbf\xd8\x90\x92\xe7(\x13D\x13B\x87pR\\\xcd\xd89.-\xd8\xba\xe9 \xf5\xfb\xd3\xeaGpjtPT\xc7\xeaD\xe8\x07\xa6O\x8b\x0e\xe8\x97U\xcc\xdd\x01}\xa2\xb0z\x17X\x81\xf1;\x01\xfd\x1e@pRt\x00\xbd\x86\xd5\xd5 $\x0f\x96\x0e\xb07\xe2P\xe9\x01\xa3\x0e\x9c^\x90\xc5a\xd4\x03Z\xe2\xe7\x0e\xc0\x0fp\xfat\x01\xf5X/\x1f\xd4\xa9\xd5\x05\xa6O\xb4\x0e\xb8\x8f\xe5i\xd7\x05 'a\x07\xd0\xa9<\x1b{@\xf5\xe8\xc3\xa9:S\xbb\xc0\xe4y\xdb %\xcf\xe2\x0e\xb0\xb3\xf2\x9c\xee\x80\xfc\xc9<|;`\x7fV\x07\xb3\x9d\xbf\x12<\xc0\x1d\x19\xe5\xbfj\x8a\xab\x9do\x94\xfe\x9e.\xdd\xa8M\x82\xac\x9f\xfbf#!\xb8\xd3\xdd\xba\xd9\"\x88(`\xba\x84)\xa2\x19\xde\xdd\x9a!\xc9\xf4\xf6\xa1\xdeU\xaeq\xe4\xe9\xba\xc9p\xbf4X\x81\x8e\xbev\xc9G\xaa\x80@Y\xf6\x01\xb4Nc\x15\xec}7\x1a\x7f[P\xe6\x1d\x80\xdd\x12\x18\xa2\xe6.\xbe\xdb\xdc\xbd\x14\x9cUGc^*\xae\xab\x17X\xd6\xdd\xb9\x97\x9a[\xeb\x01'9\xb9\x1e\x80}F\xf5e\xc1\x01v\x02\xf2\xae\xadkq\xadHz\x8e\xfb\x99\xc1\xf6t\xe1a\xcd\x12\xf5\x81\xeb\xb3\xa8\xcfJV\xaa\xbd\x8f\x16\xef\xb8\xa4g\x1f\x8fLABG\x9b\x8e\x9aB\x86\xbe%\xfa\xf4\xa4\xc5\xbb^\x9f\x9e\x9cU\xd8\xcd\xf6O\xad\xef\xf6)\x19\xe4\xa7\xe3\x1b\xab\xbb}\xe3g\xe0\x88\xdb?\x81\xf8\\\xd3O\x9fO\x1c\xf3\xb8\x93~;\xeeF\x98\x1f@d\xd1\xde\xd2\xa6?\xc4\xa6\x08\x96\n.-q\x9d\xfd'\x0e\x1e\xc8H\xf0M\x17\x10\x90\xa1\xbc%\xba)9\xadf\x01u\x80\x05\xed\xb7?\x17\x83!\xb9\xa8\x94\xbd\x07\xa1/\xdcV\xf3H\x1e\x89\xa5\xdcw\xeb\xd4e\xe3\x8b\x8c.\xd0\xdb1b\x08j\x05\x1fm\x17\x0f\x04z\x18\x90`\x83\xf8\xac\x9f\x08\x96\xfe\xcb\x17\xe2\x9e(\xde^G\x85\n\x0c\x89\xdf\x0d\x16_\xaamh\xae\x820|\xc9B\x961\xcb\xf0\xdc\xfb\xd8Djll\xbd\x8c\xce\x95\xc3Iw0$>4\x0dR\xbb\xfaU\xbcYd\xef\xc7\x90zG\xd9\xfb\xa3}\xd4\x81=o\x11\x18h\xf7nc\x8f\x86\xa1\x8a\xacn@\x97\xcd.~%c\x9aC\xbc\xf8\xe3\x90\xa6\xa9\xcb\xeba@\n\xa9\xb0\xf4\x8f\xd0\xd4\x06a\xd2/\xb1\xe0-\xb0\xec8e\xb9\xcf\xcb\x0b\xed\xca\xadhM\xfd\x8a\xdf\xd3\xa85o,\x9a+\xc4\x0b\x83\xf8\x92\xd3\x04\xf8\xe6>~\xda\xb54\xa9RP\xe9\x94\x1c\x126\xae\xa4\x17\xb7\xa6\xd5\xe4\xaee\x85Mw\xf0-\xa7;\x90^\x86\xcdI\x08\xeec\x12&\x93\xc9\xbf\xc1\xdaM\x98@\xe2\xbeV(\xff\xf6k\xafy\xf1\xc3-79\xb8\x87\xbd\xcf\xecf\n\xf7V\xf5[4\xa2<\x02d\xa0\xe0\xdf\xdce\xe2\xf1\xb2$\xfc+T\x80f\x83/\xb5\x96|\x1a\xb6\xe5\xaeXF[\xb2\xa51\xa8-\x17|\x19\xa0\xd8\x81\xc8\xb8\x16o\xb9\x1f\xcc\x03pA\x90 8wwR\xbf\x18\x14\x8f\xb7\xa4\xc9q5\xf4~\xe7v\xfd\xccnb\x10\x1cH9\xae\xd4\xfd8\x94nm\xa7\xb5x\xa4\x04\x17\x8f\x7ff7\xb7\xf8\xaa/\xb8V\xf3\xa3_\xbe@z\x1e\xd7\x9a\xc2\xc6\xea\x03}\xdbs\xb5\x0c\xbc\xe5\x86\xadi\x19\x83\xfbll%\x05Eg\xf4[b\x00:$\xc1\xb7P\xe9m\xee_\xfcP9I\xbd)qNR\x8f\xa26\x05\xa0=}I\x93)q\x08\x92\xfd\x06\xf4\xad\x9c\xa3$\xe1W\xe27\x02\xf2)\xd6\x00\x9f0\x83\xc6\x8f\xca\xd0\x04 >ZLM^\xf2\xabH\xc3\xc8\x9b\xc7&\x08\x0b\xa7\xc4\x91\xa4\x1a\x92\xfd3\x18K\xbe?E\xb2\xde\xb2(\x9f\x12\xa7\xa2\xf9\xda\x00:\x8a\xe3\xb4\x13H\xb2MS\xe2\xc8\x1fo\xb8\x87\x19O\xbc\xe5\xbf\x7fH\x82\x08\x14\x84\x00?9\x9f\xa2\xc0gQ&\xf0\x89\xdfjg\x80\xa3\xe0\xfd)q~\xa0\xdeg\x9b\x85\xc5\xb3)q\xce\xe8%\x923\xd9\x15}\n\x19\xc5\xcc#&{ba\xc8\xdb\xedf\xe6\x13\xd1M\x8b\xaf\xcb\xc9S5T \xc7\xec\xc7&\xa2\xc1G!ZR\xb4U\xca\xe6\x9b\x99\xbb;S\xb8(L-\x03\xbb\xfb\xb4m%\xef\xedZ\xd6\xf0\xde\x1e|s\xc1\xd0\xf5\xb9\xf7H\xe5Z\xd6\xdd\xdec\x18%\xcc$|O\x8c\xd1\x8f\x1cu\xcb\xb5\xf7\xb4c\xdb\xec\xed\xb7n\x9b\xbdg]{\xe6\xd1N\xc7\x8ey$Z\xfe:J\x19\xea3\xe7\xd1\x93\xb6\xed4\x81\x95\xf3\ns52\x81u\xf3j\x17\xcd\x12\x83\xf9j\x0f\xcd\x12\xady\xf5\x08\xcd\x12My\xf5\x18\xcd\x12\xc3\xf8\xea \x9a%\x06\xf0\xd5S4K\x0c\xde\xab}tC\x88Q{\xf5\x0c\xcd\x9a@\x97w\xd0<9\x1c\xe8x\xec\xc2xL\xd0\x01y$\x06\xe4]\xbe\xb2\xac\xe8 \xccQ+6\xd9\xdd\x15U\xbce\x19\xada\x0e\x9c\xcb\xb3\x9f\xc0\xd2\x0b\xfegvc\xbb\xd1\xcd\x04\xc99\x03\x90s\x19\xec\xf63\xbbir\xa9\xc0\xfcV0\x1ah\xc8\x97\xde\xe3\xab\n\xb9_\x1b\x8d@\xcf~[\xa3\xb4\x7f|\xabld\xa2\xfc\xe1\x93C\x8d\xcc\xc8\x94\xc8\xb0:\xe3y\xc2W\xc7\x8a@\xab\x07DF\x15d7\xa2;\x82YAy\xc0x\xd5\x06eJ\x9cr\xc6\xee\xc1\xc9\xb6\xd4\x11\xfb\xd7s0>\xcd\xa8t\xf7\xc3\x92\x7f\x1d\x03\xd3\\-\xa0\xbb\xc3R\x1bI/\xb5\xa9\xcf\xda\x81<\xb8]\xf4;\xa0\xee\xc4\x96\xdc\x91%\xb2q&\xd5\xb5\xfd?\x86i\xff\xb7X\xf1\xb1\n\x15\xfd\x7f\x8b\xb8\xe9\xdf\x04O\xb00\xa3\xbft\xf1\x84\x1a\xf1JhCv%\x13\x04\x16\x05\xd5\xba\x97\xd5\xfc\x11\x1b\x1b\xc9\x0d\xc6\xaf\x11\xa74\xcc\xe8\xaf\x1b5\xe5\xd7zS~\xad6\xe5W\xbc)5(\x1c\xa8Ws\xff\x86-%\xc8\x91\x86\xff\xdfj\x19 \xce\xf2\xf1\xa0\xb9\xac\x9eu\xd1\x1b\x88\xac\\\x1f\xe0\xcd\xb1\xbe\xc8x\xfc\x86\xadY\xa8\xe2\x02O b`u\x11\xf8\xe0\xf5KdO\x90\xecJ\x84\x8e\xa9\x8a\x91R\x84\xc0\x80 \xa9\" \xc2\xa9U\xa3y\xd8\xb0\xeb\x85\x8co\x83\xe8O^dta~B\xe0\x82q\xc6\xdf\xf0\xabB{\xd3^\xa9\xb6\xfd\xfe\xf4\xf1uQ\x87\x91F\xa6\x88\xda\xfesl{F\xb5}x\xab\x196\xa7\xaf:3\xf5x\xcfS\xb2U3\xa0\xcfS\xf6*\xb8\x14\x13\xb25\xb9\x8f\xb6\x18\x91c\x1e\xd5\x15\xe6\xc51\xff\xf0\xb7\x87\x87\xdf?\xac\xa6\x0b&\xf9\xe1\xdf_\xfc\xb6\xf5\xdb\xe8\xb7Q-\x0f7\xd4?\xfe\xf1\xe4\xf8\xaf\xa7\x9f\xde^\x1c\x9d\x9d}\xbcxw\xf4\xf6dJ\x1cA\xc7\x8c \xe4\xf0\x08b*\xa79\x1a&\xc3\xf7\x8fU\xee\x19\x97\xb1\xb4\xbb\xf0\x081\xe8i\x9ct%\xe6\xd5^\xc6\xd2LTt\x08\x01f\xd88aqH=&\x10\xaaC\x1c\xb2M\xe8\xb8\xd9~\xb2M\xbe;p\xbe#\xdb$\x13?\x9d??\xf8\xae_@s\x1a}dy\xca\x9a=\xe9\x8a\x80\xa8c\x9b\x16\x16\xec.\xd6\xae\xf6\xce\x8aJ 6QL\x93\x94\xbd\x8e \xf0\xe4dg0\x94\xc1\x7f\x80\x8eo\xf6\xc2\xb6/\xeeY\xa4\xf6\xe4\xf1\xe3\xddI\x17\x92\xab\x0fQ\x11\xc7KL\xf6d\x08=\xdc\x91\x91\"wdH/V\x84\xdb\x12ks\xf4\x88< \xc1s\xc2\xc9\x0bB\xd1\x10_E\x8d\xb9\x19f\x90\x93m\xf2h\xe7\xd9\x93!\xa1\x03Y:\x17\xff\xb6\x0f\xc8\xa3\x01\x89\xc4\x7f7\x13\x7f\xd9X\x0b\xa4\x8f2\x97\x0f\x06d\x1b\xcd \xdbd\xd2\x96\xb9\xdb\x96\xb97@f9#\xffq@\x121\x00\xffa\xc6\xa6&\x8d T\x91\xdaD\x17\xc48lo\xab\xf6c\xcdGq\xa0+?5 _\x88\x1b\xa9\x9f/^\x90\xc9\x93\xfb\xc0G\xe6\xac;\x93\xc7\xe3'\xe3]\xe7\xf6\xb5u\xd8,\xb9\x91\xfb\xe8\xc9`(m\x91p\xdb\xa5I\xdd\x9aG{bx40\x8f\xec}\xa8\xe5\xd9\xc6\xa1\xb7\x04;\x1e)kw\xd6\xa2/'\xe0&\x8a\xfb-\xe3\xce)pV\x85\xd5\xbb\x01\xac7\x1b\xe8O\xd4T\x8a\n\xdcL\x06\x11\x1e\x08\xf4\xc7\xed\xe6\x9e\xcd\x16\xa1\xa1\xb4\x04\xf2\x8c|&N\xfd\xc4u\x1e=rDY\xf1\xeb\xb13\xac\xb8\xf3\xb8\xe7\xf8WbB\xf6,\x83\x9f\xa86\x9d\xe6\x97Y\xc2\x04\xd2\xe3EX\xe0\xdb\x7f9\x1b_\\\xb0\xf4-\xf7\xf3\x90\x81!\xdeP\x86\x87\x8b\x98\x97\x01\xa6\xfe\x90\xf0u \x86BG\x1dm\xb6:p#w\xff\xf1n}\xe5\xf1\"\xeb\xd1\x00e#\x02\xabY\x83\x8a\xf7h4M\x1ejM,\xa7\xa2\xa7MIwL\xc5J_\x12\x1dw\xad\xda_\xae\x93\xefyDU\xad-\x83\x18\xb9u\xfb<\x0eK:r'\xd8\x96\x16\x19{O\x1f\x9b\x18T&=\xc1\xc7\x9a\xfes\xc7Z\x9f;-\x07\x9en\x99\n\x1a\x8d|o\xab\x1fU\x016\"n5\xe8\xdd`@\xb2e\xc2\xafH\xc4\xae\x88@2`\xdc\xe0:\xc74\x8axF\x04oJ(\xf1\x04\xc3IhJh\xf1%\x07\xa1~\x14\x17\x8b\x99\xdd\xaf\x95\x95y\xff\x862\xb3e\x1f\xd9\x9c%,\xf2t\xf3\xc4\x87\xc8\x92\xa6\xd1w\x19\xb9d,\"A\x14d\x01\x0d\x83\x94\xf9dD\xd2\xd3\x05\x1b\x93O)+\xeb\x1b\x83\xb4\xa2xu\x07$\xe3\xf2d\xcc\x96l5&\x1f\x19\xf5\xc9J`m\x9a\x11\x15hu~9^\xb1\x87y\xca\xa4\xa8cT~\xc5\xa9\xdf\x8a\xe1\xa3\x91\xb5-~\x1b]A`\xd0\xcb\x95 \xb8\xe1&\xaf\x80\x0b\x08\x95kn\x04C^r\x1e\xa2\x19\xa2\xb1h\x86\x8c\x94\x8bf\xc9\xa3\x15\xcd\xd2\xce\xc5\xb1\xac\x9b\xd5\xa5\xa5\x114\xc2[\x0d\xfdy?Ge\x8bLK\xdb\x90r\x9a:\xb2\x14\x95\xf2Jk\xc7,\xa5xd\xab\x0fr\xa4\xc7F$\x17\xe2\x01\xe0]\xb8\xa6b\x18kW\xbf(\xff\x1e\xd5\x160\x91r\x83\xb1\x99 \x0e\xec\xa2\xec\x1d\xf0F\x83\xa8o\xa2\x14u\x82\xd14\x0d\x16\x10\x9e\xbb\xaf\xb0\xe79\xc9\xc8\x0bB\x93\x05\x88\x94S%\xe6yN\xb2\xedml\xaf\xe8\xa5^\x14\x98e\x88\xe1t\xf1\x89\x84\x04\x91\xe8\xa1j^y,-i\xfa\xfe*R\x8e&o$-')qqN3\xa9\x1b\x1f\xcd\x92\xf3\x1e\xd7\xdd\x86 9~\xe8\xb4\x8d8Q\x9d\xf2\xccN\xa9Q \xdf\x93=\xd1\x1e\xc95\x01\x8e,\xfb\xbdwN\x0e\xab\xaf\xb8\xfb\xd4\x159 ?p\x1e2\x1a\xa1\xa6\x04\x0b\xa2\x0c\xe3\xe7\xcd\xbc\x1b\x84e\xd3\xe9x\x14n}S@\x0e\x89\xbb#\x0e=5\n\x03)\x81\x88\x9b\x88\x0b<\xa2\x80\x8b\xc0\xe6\xf7\x05\xbd\xe3\x8d\xe3H\xf2z\x1dNb\xdc\x99^u\xcd]Y\x8a\xe6\xd58\x00\xe5\xdb\xbdp\xd4\xeeJ\xcb\xd3\xe8\xcb\x17\xb2%\xe8oZ\xd2\xdf\xba\xce\x12j e$\xf5\xb2\x07\x82\x0d\xa8\xbb\xb2\xd5\x0f: \x95\x11\xbd\x8f1\xa9N\xd1\x1d\x87\xc5\xaf\xe0\xad\x96\x91\xa9\x00\x9a\x83\xe3\xd70\xdf\xa6\xe3\xf3\x96%\x0b\xe6\xdfit\xba$OX9\xb1_/\x8b\x02\xed\xacf\x8b\xf3j\xd2\x85\xa1H\xc1N\x1a\xcb\x08\x1b\xd3\xcd\xa6oKV\xb9*\x07O\xcc\xc8)L\x0b>\x81\x06\xa89}f\x0d\x9bL^\x90\x9e\xe6\x97\xa9\x97\x04\x97\xfd\xe7K\xb5\x1d\x97\xa9\x89\xc6\xe4Q\xaa+\xed\xd3\x86,\xb9)\x1a\xd1\xb7\x0d+p\xbeQ\xffZ9\x1ef\xe2\x81q\x1f8.\x92%\xdc\x92F~\xa8\xa8\xe2\xf1e\x10\xf9\x90<\x18\x0cI#\xdbE\xfc\x8c\x10\xb47\x9f*\x1f\xef\xd5\x9f^=qu\xb3\xaa\xbd\x13\xecd\xaf\xa6\x15\x92\x83\x97\x81\xff\x96\xe7Q\xe7]\xab~\xe0\xa3\xe64\xb9\x9b}\xef\xe7 \x0c?2\x8f\x05k\x84\x93h\xfb\xf0U\xcbN\x90[\x0c\xdc\xc3\xa8\xb9j\xf2@M\x7f\xe5\xfaik\xea\xa7hu\x9b\xd1\xf9\x84\xcc\x94)\xb3\xe8\xd5\x8e\x02~\xa3\xaf\xd7\xb17h\xa5\xd7\xcf\xc2jz\x15c\x18\x19\xb6q,\xb2\x9b\xecd5\x7fm\x9c\xf7?0\x16}H\x98GC\x0f\\\x19\xf9\xca[\x7f\xadi\x06H\xc0#\x10\xa3T\x1b%o\xe6\x99\xaf\xb4\xd4\xab\x99v\xa2\x0b\x01\xaa\xf1%\x0d-|\xfd\xd4&\xc6\xc4\x04}\xa7\x06\x14\x1fk\xfb\xb5\xcf\xa1VCY}\xf9[\x02:\xb9\x07\xc6\xd8\x8eK\xe9Z\xfb\xd9\x07\xec\x8b\x14'\x00\xd1\xd9\xd9L]\xe8\xaa\xc4\xc3m\x1c]\x9f\xea\x08&\xcd\xef\xa2\xf2\xebO\x96\xdcl\x00M\xcc\xab \x1a\xc7\xe1\x8dk\x11\xe2`\xcfW\xe2\xd1vo\xc6\xb6G}s9\x06y\x9a<\xb0\x97\xbdk\xb0\xcb\xb3\xccGQ+6r^\xee\x8a\x0e\x8aI?\xb0<\n\xe7\x9a\xfd\xcaDp\xd3\xb5\xc4\xc8o|\xb7\xab\xd1\x18\xf4\xc7#\xedb?\xd2k\xa8z\xe1\xb4T\xef\xc0~\xd3l\xca\xb4q\n\xc8|\xbe\xb6\xaf\xb8\x16\xe9e\x1f\xbc\xb5`\x99\xb4\xb7\xf2\xb5zu_\xec\xa59\x8c\xea\x15\xc7\xf5\x908g\x9cP\xcfci\n\x97\x12W\xb2\xfa\xe2\xf6kHnxN\"\xc6|\x92q\x88\xe0\x1f\xcco\xc8\x1fD]kNI\x96\xe4\x8c|%T\x16\x9f\xf3<\xc9\x96\xc5\xe50\x01\"\x12\xeeF\xe0~q\x00\xf7HcgP\x1c\x04\xf3t|U\xedQ\x9fq\xe8\xa7\xda\xa5\x1f}\xcdi;\x10\xdb\x11qT\x96l\xae\xab\xf6\xa2\x81\xf9\xd1\x96\xe5\xdf^\x0b\xad\x9c\x02\xb6=\xd7^G\xae\xeb\xa8\x1d\xbd\xf6\xdd_\x1cw\x16\nb\xd2AAL\xfa\xef\xfc\xcd(\x08\xaa\xefih\xbb`-\x95{\xbeuX\xc2\x8e0Hp \xe6\x80\xf5R\xad, /e\xba\xce\xc8!\xd4m\xc2\xb6\n\x88:\x84\x84\x1e\x12\x1d\xb1\xfe\xccU\xb4D[~@\x0ee=;dJ\x803u=\xbd*l\xe7\x8a+x\xa7\x10`\xe7UXT\x82\xe2\xb6]\xc5\x16L\xf2\xd6\x96\xeb\x81\xd6\x07\x8c\xe6\xa0\x18\"\xab\xe8\xc1\x95\xbcqN\x0eIN\xa6jY6i\xc8k\xa5\xf9\xc1\xd5\xf5\x99\xca\x01\x1e#q\xff\xf8\xda$\x95\xbb\xee\xd3d\xe0\xe9\x1a~\xc2#`\x10\xc0\xfd\x03\xd1\x88TX\xc7j\xc5\xd5U\xb4l\xac^um^\xb5\xdf\xaf\x16Z\x93\x03\xe5!\xe0~\xb4\x1e\x87v\xa5\xbez'\xc1K\x90ti[\xdcR\xd5\x8f8\xcd\x98U-\xea\x9a\xc7KR\x83\xa9#\x19\xb0>\xd4\x1a\x83\x82\xd3L\xd4K\xf9\xe5\xda\x81T\xa8G\xf2\xb2j\x9bj\xa44\xbf\xddyN\x02\xf2\x82D\x85zf\xb0\xbd\xdd\xc4\x91\xc0\xd3p\xa5\x194$\xd1,8\x07a\x12\x9b\x89\x9f\xe7\xf2\xeeE\xfe\xb6\xb6\xad\x18\xac\xda\x0e\xf9\xb6Sh\xd9\xe7\x05\x00\xca0\x1b\xd4|\x02\x82\xce#\x00\x06\xdb\x7f\x9e\xa4\xf2\xbc\xe9\x89&\x957\xc2\xa7J\xb4\xd6\xd1[(QV\xd0J\x83\xe3#C\x0c\xb9\x08\x8e\x04\x1a\xd6\nv5\x12\xaf\x17\x94\x1aw8v[\xa0\xcaS\xd2\x0e\xb4`\xd9\xcb^\xb5\x01`\x12\xac\x99\x0fd\xd5\xab\x84\xaf:J\xac\x82\xeb j\xc9/\xceS;H\x06\x8a\xdf\x08+\x8dh\xe7f\xd6\xf1\x8fZG@\xee\xc3\xd6f\xca\xed\xdc2k4\x0c\xc1\x05E[~K\xf9B\xf7\xb8\x0d$\xc8n\xfa\x0e\x85\x81\x0b}6\x0f\"V\xa0\xa0\xe6\xce+A\x17,3\xb0\x15\xc4\\k\xc2s\x1b\xfc)\x98 %\x02[\x89\x97,\xf5\x92 \xce0^\x8fV\n\x19\xdaMMPA\xcaPAEP\xa5'\x85[\xe9\x17\xb4H\xea\x86C\xe2\x0d\xc9\x1cCD\xa0['\x0d-L\xcd:\xcf\xc6\x8e\x0bx\xd4\x0eG?\x023\xc4`g\xeb\xb5\xf0\x12\xb1h\x7f\x0cX\x1d\xb83hc,\xda\x88\x16\xc1e+\xe2S>\xb8\xf8\xb0}\x8a\x13\x1d\x1d\xd8\x17\x84\xb1G3\x97\xbb\xde\xc0\xc6\xe5\x14\x87\xdbR\x9e[K\xf2\x82\xf8\xc5\xb9\xb5\xbd\xbd\xec\xea\xb8 \x1b\xfc\xd9\x121+\xd0\x8fRN\x9e\xad\xc1a]\xa6\xfe\xcfE;\xe7\xb3\xf5\xb9\xd5o\xbd~\xc4WV`\x1f\xee\x0d\xc9\xbaC`\xd8O\xfc\x1a\x89\xb1_\x0f\xc9\xaaC\xf2e\xcaW7\x16\x83\xa1\xa9j\xa56%\xfeMp\x14\xd48\x12\xab\xde\x97\x12\xb7\xd7Y\xd8\xed\x81\xa2^\x1aL\xd1\xf8\x90\x04\xb8A\x9a\xd6\xdcn\x0e:\x084\x9a\xb3%\n\x18\x96\x08\xd9@\xc6\xbaeWD)\xaf\xbe\x0d\"\xf0fH\xd8\xb5\xc7b\xd8\xcf\xdc\xf3\xf2$a\xfes\"\x9a\x9f-\x19\x89x4Zi@\x9f\xad \x8b\xd6A\xc2#\xe0\xab\xc5\xa2\x06\xc9^\x1e\x86\x04\x82\x9a\x92\x15KS\xba`\x84F>\xa1\xbe\x0f\x11OhH\x96,\x8c\xe7yH\xaeh\x12\x05\xd1\"\x1dc\xda\xe2,L\x99eQ\x89>\n\xcehV\x1f\xa6s\xbb\xe0\xc3\x83\x9d\x86f\xbb\xd5\xa1\xc8\n\xbf<\x0f\xff#}\xb8\x18\xf6\x13\x1d\xeau3\xf3\xb6\xb7\x9b\x01\x1c\x88d\xfa\x07\xd2\xee\xe1\x808\xaf\xa35M\x02\x1ae\xe4\xa7\x80K\xe1\x15b\x00\xd1H\x91\xf2\xact\xd2\xec\xcc\x1f_\xf1\x1d\x828Hi\x02\xea\xd5\x87\x89\xd0\xa4#\xa8l\xd8A\x95\x13C}L\xbaE\x91\xf6\xd1!\\k\x83<\xb04\xaf\x9a\x0c\x86\x98\x8d\xff`Hr\xd1QO0d\xa0h,\xc5o\xa2\x7f\xdc\x8d\x86\xe4\xe9\x90\xa4\xd8\x01T\x1c>s\xe3;\xcf\xc9|4z> \x01\xa8\xfc\xcd\xe6\xe7-R\xa2\xeaR\xb3\x99\xdd\xa2\x0b\xcf\x1c\x8c\xde\xbe\xe5\x8a\x06\x8b\xae\x8d&C\xa2E\xbc0U\xe4\x90\xec\x80Nvy|F\xe4\x05I\xe0\x86R\xe9\xd2\xb9l\x16\x9dK.~\xf0\x1c\xa7b\xea1V{o\x99\xc6\x9a\x96;\xe6\xc9\xa3.{d\xac\xab\xa6\xec\x06\xd6\x11w\xb3AE\x90u?\xad\xdb{\xba\xffo\xd1\xbcF\x88t\xd9\xbcI#\x02\xbbB7O\xea\x88\x82vK\x07\xba\xfa\x89\x9e\xad\x89\xcb\xca \x8eA\xc3\xb7\x91\xbe(\xe2\xa84D\xac\xd3\xd9\xb9E\x9e\x91\x835\xd0\xc0u\x0c\x1b\x0c\xa0\x88sP\xe0\x83\x8b\x00*\xe5\x13L\x9c\xfc \xd1\x8e\xc6q\x9e.\xdd\x1c_\xbb]\x06\xb4\xdd\xbb\xae>\x06\xba\x7f\xf5^\x14Hr\xeb\xa0.]%\xd5\x9d\x1aDj^` 3\xd9\xfe\xba\xaa\x9e\xc6\x81\x9b-\x9f\x8e\x88\xdb\xdaM\x1321\x1c\xe2j+c\xb3\x83\xaay\x8f\x8c\xebdx\x95\x14i8\xd3\x05\xd4>R\x8f\x14\xb9B=\xacR\x0ff%N\x943\x81\xa0\x9c\x90\x03Q\xf5!I\xc6?\xe4\xf39K\xc8T\x99}\xdaX\xb3CB\xc74\x0c\xb9\xf7)J\xe9\x9c\x15\xf0\xd5A\xee\xbd\xbb \xa9;\xed\xd21\xca\x91\xc3`]h\xa4+e\xe4\x06\x04QL0\xdc\xc6\xb8\x11h\"\xb3+\x02z\xdez\xe1\xa3\xba\xe3\xc5\xc7=\x1e\xdf\xb8\xc9`h\xf52\xf7uP\n\xf2\xdc\xc9\xde\xa3A\xe1\xeek\xf3-\x80\x0c\x88q\xe64\x1bi\xf4\x1d\xd9\xe9\x99TP#\x07\xe4(I\xa8\xe8\xc5\xa08\x99\x9e\x0fH6\x8b\xce!0|t~\x1f;\xa2\x13\xdfO\xf6\xefr\x1c%\"\x13P\x9d)+\xbc\x9f\x96\xed=\xedt\xdcqO-\xab7+\xba\xff\xa3C\xa3M\xfb\xa6H\x14\xabQ\xdd\x05\x16\xc9\x8a4\x82\xd5B\x13\x03\xcf\xccv\xce\xe5\xa9\xa0\x8f '\x88|v\xedH\xcd\xe0d\x0co\xd0\x0e\xf85$\")\xce3\x95\x14\xe7YeSm8\x93\xbb\xbb8\x93\xb0\xff\xb4N\xae\xabS\xfb)\xee\xdap\xff\xe9\x1e\xca%\xec?\xad\x9f\xf2b\xd4\x9d\x99D\xb8\xdaQ\xc0\xb9\xd3d\x19\n\x98\x974cu\x00\xcf\x04xK\xe3z\xfe\xdc\xcc\x7f\x07\x8eD\xea \xb1 \xf2\x91-N\xae\x1b\xb5\xf8&\xc8)\xcb\xea\xf9\xcbJ>Lm\x1dd]\x01\x01\xe9_\x1dde\x82\x00\x86\x91GF\x1dnQ\x1b\x14\xfaS\xc0\xae\xea@7&\xd0\xab\x90\xd3lo\x17\xea\xac\x03^6\x00\x9f\x01\xd4\xb1\xbbA\x1d\xe2\xef\xc4Z\xd3\xde\xc65\x89\xbf\xbb\xbd\xbc\xe7j+a1\xd6\xb7]\xa9\xfb\xb6\x1b\x90G\xf8R\x9d<\xc3tk\x04\x1b\xdbzH\x90\x9aL\xcd\xc9\xb8\x143;-\x91\x0c*^\xf5\x9aHH<}<\xfb)\x83\x07\xc1~\xe0\x00\xa6\xbb\xbf\x06@\xcd\"V\xb0i\x01\xbe\xf3\xf0\x18`\xdd\xbb\xc5\xb2O[93\xbd\x04,\xab\xa4{\xe3j\xd6h\x7f\xa76\xb2bYL\x9e4\x97\xc4K\x9a\xb1q\xc4\xaf6\xc5:\x9a\xdeA&0hj\xbf\xf5\xe9\xfbZ;\x02\xb5\xf9 \xc8\x01{\x8e\x88K\xc9\x08\xf5O+\x98L\x88\x86#\x0e\xa7\xef\xc9\x0e\xf6\x15\x0d\xb7\xbd\x9d\x91\xef\x0fHapnx\x8e\xdei\xaa\xd4}\x95\x1a\x82\x19\xae\xd7W\xdb\xb8\x9a\xcd,j\xbc'\x89\xe1\xe4\x11.\xe3hluEn?\xc3\xc9\xed\x06S\x9a\x93\x03T\x0d&\x85\xf4\x86\x16L\xd8}\x95Y-\xe0\x011\xde\x89G@ \xdb\xcd\xe0\xf0\x92\xb1\xbb\x80\xc6L\x95\xd6Os\xd8\xc5\x94\xa0\xf3[\xd5\x0c\xc9\x06$,\xf1\xb1\xe6|\x80D\xcafQ\x1d#[\xa8+o\xb3\xa9\xda\x7f\x86\xc7\x93\xd8\xdb\xe9\xbe\x1a\xb7R\xbc\x05\x08v\n\x13\xe3\xfb\x18iG\xf4\xbahU\xa1\x90\xfc\xaf$\xbf\xa2YPeL\xec\xbbR\x14\xd9\x85\"\xbb\xe7\x16\xc5\x10\xa2\xe7\x85\x1aW\xd6\xda\x9f;\xea\xe6Ip\xdan0\x1a\x81mu\xd1\x06\xa9Y\xcf]\xf3`\xcd\xe5U\xb4l\xfc\x0b\xb2g2\x06T\xdak\x81^c\xb1p\x05\x95A\xb6\xb7\x13\x08\x16h\xc3\x12\x9aP\x8ef\x89E\xf5\x1d\xcc\x95\x81\xdcNe4\x8f\xa6\x92\x92U\xb8V\x0bip\xeb\x83\xbeyp\xab\x95fa\xc2\xf7\xf6m\x11\xe5\xfap\x83\x81\xab\x83='bS\x92m\xe28\x1b6\xbd+\x12\xcb\xfe3\x1c\xcb\xed?{j \x1bWo+\xd8/\x03j\xf2xH\xaa\x8e\x8aB\x9a.e(\x882\x91\xe6\xd9\xb2\x9a\xb2\xe4i\xcd\xfd\x8f\x18\xa4&\x8cR\xb0\xae86Jku\xa5\x8c&^-\xed\x1f9Knj\x1f\xa0\xd9\xb2Y\x9dH\xad} asRs)T.\xb2l\x0c!P\xc9\x01\xb9\x1c\x92l\x9c\xb0\x94\x87\xebN\x97\xaejr\xc1\xc7\xdd\xd6\x04\xfc\xba\xe9\xa2\xa6\xaf\x9a\xafF\x95r\x1f\xf5\xac\x98\x91C\xb4\xf2b3V<\xac\xc3g\xe6\x0eRIl*y\x16H}.\xad\xd7D\x15\xdf\xf9\x01D\xe0\x96_\x81\x18\xcb\xa6\x1f\x0f\x99\xac\xafZ\xaa\x0d\xfb\x94\x88%\x15TW.\x85\xd0\xc1\xee\x8c\x8e~\xdf\x19=\x1bo\x8f\xce\xb7\xa7\x83\x87A\xf3\x98}8\x9d\xed\x8c\x9e\x9d\xff\xe5\xcf\x0f\x9bG\xed\xc3\xbf\xbb\xbf=\xfc\xed\xe1\xa1{\xb8\xf5\xdb\xc3\xc1\xec\xef\xbf\x1d\xfe\x96\x9e\xffe\xe0\xfev8\xfb;\xfc:\xac\x97\x02\xb3\x04\xe7\x0fgH\x9c\xaf\xe2\xcf\x17\xf1\xe7\xb7\xdf\xc4\xdf\xbf\x8b?\xff\xe5\x9ck\x03\xa1\x99\xf3B\xa4|\xef\x0c\xc9w\xcew\x90\x07q\x80E\x81\x04\xfeF\xf07s\xce\x07\xcd\xd3{\xe6|WV\x15\xd6\x00\xe6\x00\xf0\x1f\xa2\xf8C\xf1\xe7P\xfcy.\xfe\xfc\xaf\xb2\x90W+\x14C\xa1\x12\xfe\x7f95s\n\x1fFd\xb6-\x87\xf4h\xf4\xb7\x8b\xd1\xf9\x1f;\xc3'{_\xeb\xa3\xb0T\x83\x8f\x80\x0e\xdc\xf1_\x06u\xf85ja\xf8\xdftM\xa5!\x1b\xce\x958\x06\x80\xd3\xe0(j\xd6{\xabo\xff\x89\x05\xfa \x88\xcb\x84V.r,\x86\x89s[\x99\x05\x8f\x976\x83\xc8y`\xe3\xdf\x1ch\x84\xd3\x92\x99Zs\xe7-%Uk\xacEE\x83:\x87\xedF\x9d%\xfb\xe8Yri\x93q\xfc\xff\xec\xbd\xeb~\xdbF\x928\xfa}\x9e\xa2\x84\xec8@\x08R\xa4\xe4+mZ\xeb\xc8\xcaF3\x89\xedc\xd93\xbb\x87V\xf4\x87\xc8&\x89\x18\x048\x00\xa8K\xc6\xdeg9\xcfr\x9e\xec\xff\xeb\xaa\xeeF\x03\xe8\x06@\xdb\xc9dv\x07\x1fl\x11\xe8{\xd7\xbd\xab\xab\xe8\xfa:\x17<\x06a\xa6\\\x8d\xc9\xbc\xa2S\x95\xa6\xe4\xb5\xd2\x1b/4R\xa7\x94(\xb7\x1a@\xdde\x0e\xc7\xa1Q)I\xe9\xdb\xec3\xe2\x12\xbaF,-)\x05^\x05i\xb0f9K\xe1\xebm\x1a}M\x19\x05.\x19\x04\"gU-\x81\x80\xc9Q=,<\x01_.\\\xe7\xc81(s[\x94Q\x8b\x14g\\h\xd3\xea|\xe5xp\xc4\xe9\x02\x8c9a\xa8\xd7\x8f(S\xc6&\n\xf3\x9a\x97z4\x1d\x9e\xc3\x04\xff+\xaeV\xbd{\xb7\xbfD\xf2d\x18\xf0%\xa6\xfb\x99@4\xf89 \xe3Z{|\xf5x\x91\xcbA\x9e\x86k\xd7\xf3a\x0fS\x8d\xcb\xb4\xc54\n>\xe6\x06\xf3\x17\xef\xe7\x02&\x90\x91#\xc3\xa5Ew\xbd(\x07\xf0\x16\xcc\xff\xb2\xcc\xf9/\xeb\x02\xc3\x05J\xc1\x17\\\xf8>\x92\x81\xd0\xa4\xd4\xc1\xdfV\xa4\x8e\x1c\x8e\xe0V\x80\x9bV\x18\xc3\x96\xe6\xa9;\xf2T\x10n\xe3\x07(\xa2\xad\xc9N\x1c\xa7\xd2\xc5\xdf?\x8a82e\\\xac-\xfe5\xd7\xd6\xcd\x8b\x82\x91\xffl\x8by\x02\x13py\xe5\xeb\xe9\xf0\xdc\x1b\xe4\xc9\x0f\xc95K\x8f\x83\xcc\xe8>^\x15\x08O|\xa0-\x15\x13\xbb\xaey\x1f@m\xb4x\x19\x81\xab\xa6\x18\xc1\xf0r\xb0\xc6H\xea\xfb?q\x96=\xfd\xe9\xdf\xdf\xed\x9f\xf7\xfe]\xfc\xbfo\xbc\xef\xca\x87\x8dn\x83\xfb\xfb\x0e\xc2\x8e\xea~\xe8\xc3\x81a\xd4{7\xd4\xdd\x9d;\xb0\x9e^\xe3\x8dZ\xb74\xec\x03\xaf&\xd5V#\x91\xd6\xe7\xb0\x87m\xf1-,\x9a\xdf[N\xaf\xcd\x97t\x95&}\xe6\xc3\xb1\x8f\x9e\x87\xfd\x91\x8f\xde\x82\xc3\xc7\xf0\x0c\x9e\xc0F]\x85zfNP\xc6\x1f\x81\xec\xeeK\x1c\xbeD\xf4\xcd\xf4\xd9\xb9\x88/\xdc'tz\xcf\x87\xf4\x12\x9e\xc0{z\xcd\xfb{iP\xaa\xb8^J-\x1e\x13)\xa1\xcaGpY8\xffpJ\xf2\xef\x98\xa9\xbb\xf6\xd2\x87\xf7\xa2\xdf3ZO\xbcw0\xf4\xe1\xd8S\x90\x81\xaf\x8e1\xa1}YM\x98\xb3Y2go_\x9f\xaa E\xee\x99\xe7\xc9\xb5\xb1(\xbd\xda\x82-\xba,\x18_\xf2\x97\x8f\x8bi\x96\x17n\xf1y\x0bG\x15d\xb1K \xfce\xddG[\x95\xf7\x95Uy\xef)\x12\x94f\xec\xfb$\xcb]\xaf\xae\x14\x95\x7f\x7f\xf8\x00\x8e%\xb3\xd6+<\xd7&\x9c(U\x12\x8e\xe7\xce\xb9\xe9[\xe9\x974'\xf4adP\xd5\x11\xec_\x99\xef\x81+\x00\x7fS\x1d\xb2\xa0\xec\xfb\xef\x06\xfb\x9e\x0f?r\x82\x83\xbb\xe8\xc3\x1b\xb9b\xb4\xa1?6\xee$\x88Y\x9e\xc2\x04\xdeL\x9f\xb5\\\xa2?Et<\x15\xd4e\xdezq^\x0d\xffgA\x85_\xd0\x10_\xc3\x04N\x15\xa0\xbd\x80'\xf0\xfa1\xbc\xe0\xa3<\x1d\xccVAz\x9c\xcc\xd9\xb3\xdc}\xe1\xc1S\x18\x1d<\x80#\xf8\x19z\x13pn8\xcf\xc5?O\xa7/\x1a\xc6\nrY\x7f\xee\x97\x8b~ \x19\xc2\x198\x1e\xf4\xe0\xd2\x80\x15\xcf\x8b\x12\xedc\xb9LY\xf0\xbe\xb1T\xdd\xbc\xd4\xfc\xa5\xfe\xd6\x88GO\xe1\xe0\xde=\x99\xeeA\x1b\xbd\xe3H\xc9\xc0\x86\xe8eV\xec\xc3+-vvQ%\x1d\xe4\xc9\xb3\xb3\xe3\xd3\xd3\xf2\x17\xd3\x05b\x0e2\x7f\x93\xbd\xa0\x15\xe6\x08\x9c1\n\xa1\xea\xcd\x98\x83\xbeq\xbe\xdfu%D:\xe9\xfb\x0ez\xf07]\xe8\xeai\x8d\xf0))\x01\xc8\xba\nRb\xf2\xcd\xeb\xdb\x07\xce\xbb9\xccp\xea~)\x08\x9d\x06H\x97^+\x1f\xbf\x9a\x9e\x9c[.E\n:\xc5i\xd6\xac\xe06\xad\xa4\x8a/\xf5/\xbc\x8e\x95L\xf1\x8e\x05//\xb8\xd1/\x8d\xa8\xcf\x1b\xfd\x96\x8b\xd8q\x8dm\xfe\xd2\x80\x02\xdf\"\xc9\xff\x05\x97\x05\xabg\xb3`\xc3x_\x8a\x17!y\xfe\xc5#\x84\xfa\xd6L\xde\xeb\xf0^\x97A\xffR\xe2\xad\\\x92/\x18\xef_\xb4\xbd&\xcb\x9e\x92\xbe\xfeR\xe1\x8aC\x1f\xfeR\x05`\xde\xfc\xf7\xe5\xe6\x8f\xaa\x88\xaf\xad\xe9\xf7u\xf1]u\xf7\xbdW\x11\xb1\x8b/RH)\xc6*\xcb\x94\xa4||\xe9\xd5G\xfd\xfd\x8eb\xfdeQR\xd3A8\xb1[NO\x10\x90\xcb\xb8\xa1\x82w\xab\xd2\xa6\xfa\\9\xabj62\xbb\x18\x0d\xc8\x04e\x05e\xd0\xea\xd8\x04\x8d\xbf\xaa\x88\xb54\xc1&R t\xaf\xbfA\x0f\xfe\xda\x80\x89\xba\xba&\xf43\xfc[\x1a\x16+JP%^p\xdd\xc8i:eU\xd4\x05\x05P\xc3\xa0\x992~\xe2?\x06Lc\x9e\xa7\xc5\x199|\xb6\x1f\xfa\x9c\x88\x92 \x7f\x02\\N\xae\x03\xae\x8aM\xac4'\xec\xbbNhc\xf3&\xd4\x0b\xa6Z\xcc\xe2\x95\xadPh *\x1b @\x96\x87YP\xed#2\xcb\xdd!\xf5\x14+\xe6\x18#\xc1*\x9c\xd1\xb0.\x86\xe0p\xberD\xc0\xc7r]\x0ex\xfc[\x0f\x8f\xad\xb6r\xe2\x18\xa8\xabR\x94/\x14-\xca\x16ij\x0fB>Ht7/phz\xf4\xd5y)ZOSLQ#B\x96\x89\x8a\xc7\xe5E\xec{\xab:q\xber|p\xfexp\xe8\xe0\xd7\xd4FEL\x87<\x96\x83\x18\xdc\xa2\xf2\xe1\x8b~.\xe3)\xba\xd5\xd2\x97\xe1\xf4\xc7du\xac\x18\x1d\xcd6\x91\xdcl\x16\x85\xe24K\x1b\xa1O\xd4\xb0\x81\"\x97\xe2\xb7`\xbb\x14\xc2\xa5\x8aQ\x9e\x8f\x14e\xf8\x18\x02x\xa2\"\x84>\x86\xc0\x9ef\x1d\xfdO\xa6\x81\xc9\x83q\xba=\x17\x086\xdd\x9e7\x8c\x8eB\x93\nQ\x02\xbd&V>\x97\xaa\xc9\x96\xc89H\x11\x0cH\x1d\xf5i\xdc$\xae\xcb\x0eL\xe1\x1c\x85\x82\x90\xd4\xba\xd1\x9c\x93\xd5\xc3\xac\xa2Uu\xf8\x18\"x\x02E\xd6\xf9\xa8Y\\\x9c\xc1\x04\xb2id\x11\x17\x1d9\x16B\xb5\x19\xe1\xf1tF\xd1\x08f\x06\xf1\xd5z\\\xbe\x9c\xc6jf\xe2:zI\xc0\x88\xcb\xd2E\xacNN\xeb2\x86ya[6\xadXW@g_\xf5\x8bHU\xd3\xa2\xa3\xb4\xbe\x9c\x16u\xcem+Z\n\x96T\xdd\x9e\x0dm\xcf\xa6dB\xda\xb4\x1b\x1e0\x04\xf1t\xd3\xa0\xcc\xc7\xd39\xed\xc8\xdc\x12K\xcc\xf8\xb6\x11L;l,\xa1\x82f\x95-\x16\xc8\xe7\xb8\xc09\xf8\x87\x0f\xb0./\\i?\x99\xfaQ\x9f\\CD\xb7R@D\x97U\xc4\x16O\x9a\xf4\xf7\xb9\"\xb0\xd2X\xee\x9e\xcb\xa4\x8a\xb8\x1a\x90=\xc0\xabEx\x92O1\x83\xa2\x162*V\xd2E]V\xd6\xaf=$\x07\x1c\xa8VB+\\)\xe3\x03~]\xe9\xfe\xf8\xf5\xcf\xa5\xf5Y c\xc3\xbe!\xdf\xbbmC\x94\xf0\xcf\xc4\x9f\xbcM)\xff3\xfa\xcb\x17\xd8G4LL\x93+\x0b\xb14\x922\xfc\xc3\xd7\xb1tR\x999\x13\xeat,}+\x18\xfeQ\x9a\xc2\x87\x0f\x107H\xff @\xfc\xaa\x8c\xe8\x16\xc1R>x\x04\xd8\xa2\x03\xf0G\xd1\x90+\xe8\xc1m\x87\x05T\x18\xa1y\x99\xe8\x02\x91\xa2\xd4\x9f@\x83\xe4IU\x99\xce9\xe2(\xa1x[H3\xf5\x05\xb8(\xed\x173\xb6\xc4:\xb5t\x0d\x13\xb8\xe0\x8d\\\xd2\x16a\x9bD\x17E\xedz\x9d\x13\x98\xc0u\xfd\xf5MmR\xdad\nL\xe4\xfdL\x0d\x11\x17\xcf8\n\xafJ\xb4\xa0<\x90z\x1b\x1a\xb9\x06:\xfc\xd0X\x8bA9?\x13\x1c\xa5\x84\xa7\x1a\xdc\x92sN\xb1\x08\xae\xe0\xe77\x1c\x81\x8f\xe8\xbf\x89\xfc>\x86\x1b\x85\xb0\xf4\xca\xf34t\xe2\x0d\x97YM\x99@P_\xac\xdc5\xabu\xbd\xa2\xaeW\xd45\x93]\x17\xb4\x82\xa9\xae\x15q\xc2\x0c\x7f>n\xedu\xad-D\x135+^\xef\xc23\x13\x01)\xca\x90R\xa6\xba\x8e\x15\xb6[ B\xa9.\xbe<\xd2\x7f\x8c\xb5\xba>t%T\x1c\xbc*WY\x903\xf0\x8d]\xa9\x13[<\nso\xe8*\x8b\x0f7\x83M\xb2\xe1\x18\xc9\xdf\xdcH\x17\x96\x95\xd7\xb5[K\x7fx\x08\xffb\x1bE/\xd3\xb71Et\x9e\xbb\xb2\x19\xa3|\x8c\xe0\xe7\x95\x17M\xad\xfa\x8d\xe4A>\xb8\xaf\xb8\xd2\xbc\xe7\x16@H\x7f\x15\n\xed\xbf;\x1eyD\x17\xdf\x04b\xfc\xbb#\x8e\x92\x14\xf1~U4\xac:+\x0d\xe1U\xc1\xfd\x1a\x88`\x87\x85\xf2A.\x89[`=\x8eF{/\xe9?\xdf\"E\x93\xb5\xf2p\xa4\x13\x901g\xa2\xa8\xb1\xc9\x11\x1c\x15\x83\xc1\x8f\x9f*\x02\xee\xdd(xQ\x93\xdcT\xbd\xf6J\xbd\x8a\xb1\n\xad\xb5\x18D!\x9dJ\xd2\xd1*\xe9+\x99\xe5\x98v\x1e\x8dw\xfd\x91\x87^\xb0\xefiA\n\xca.\xff\xba)\x0c\xfaB_w\x06\x84e\xc7\x88q\x03\xf9\xcb\xd3\x10\xf0X\x9c\xef\xfa\xf0\x12\xfb\x92\xb2\xe6Kx\x8a\x12\xe8\xcb~\xdf\x03\xd9\x0e\x1e\xc0\xdeL_\x9e{\x9c\xd4!L\xcd\x98\xfbR\xdc\x7f+:\xe0J\x7f\xf9\xb3O\xa6\xe81<\xc3\x81\xd5>\xf6\xfb\x06Z\xbcG\xe7\xd5'\x16\xc3\xf7c^\xed1<\xf34*\xcb\xc7Pi\x89\xb2\x10\xead\x9a\xaf\x95\xb8\xfb\xf0\xf0\xfe\xdd\x07fM\x8ck\xfc\x87\xf7\xcd\xdff\x18f\xdc\xf8\x89\x83\xf9\x81\xa5\xda\x867\xf9\xd0\xfcm\x0e\x13xP\xbd\x13'\x1f\x8ez\x0f\x0e\xcc\xdf\xb8n9:\xb0\xb4\x8a\x91\xf1\xfa\x16]s\x89~\xc97q\xbf\xbfo.\xc0\x05\xa1\xfd\xe9O\xefn\x0e\x86\xfdw7\x0fN\xce-\xe5.\xb1\xdc\xbb\x9b\x83\x93w\xdb\xc3\xe1\xf0\xe0\xdd\xf6\xbb\xef\x86'\xfc\xdf\xfb\xa3\xf3\xfd\xa5\xb9\xd2\x855\x8f\n\x7f\x92+\x96.\xa2\xe4z\x0c\xceK\xf5'Em\x8c\x19\x9bgp\x1d\xceY\na\x9c\xb3%K3\xc8\x13\xd8\xa4\xc9\x8ceY\x83b\xed\xc4I\xde\xbf\x0c\xb2p\xe6\x8c\xc19\x8d\"\xb6\x0c\"\xd1*\x17\x1dn\x1e\x0e\xc1\x8d\x93\x1c\x02\xc0R\x80h\xb4I\xc28\xf7\x9a\x9a\x0d\xe3\xab \n\xe7}l \x9b\xa6\x17\xd4\xb49\xf1\x9d!\x9d\n\x08\xc55\x82>\xcc\xcc\x9f\xb9\x8e\xfac\x90\xaf\x06\x8b(\xb1\xe5\xae\xe4:\x01\x19\xb5\x07\x8b4Y\x1f\x0bo\x1a\xcd\x9dX>\xca\xad\xf8\xcc|<\x00*\xc6\xfe\xeb ^\n/\xdc\x8b)3\xdaE\xed\xad\x1f[o\xd4A\xd5\x1e\xaeB\x85\xa2I|z\xfe\x18b\x0c\xc4\x9eR\x84X\n]n1hI?\xe5\x9d\xc6\xf6\xbeql\xc5\xb0\n\x89\xc2\x0e\x07\xa9\xe1\x00P}\x93\x02y!\xef\x82<\xf8\x89\xb98\xd5\x03\xf4\xfbC\xceON=)\xf4\xe0\xd8\xa5\x13Su\xe6r\xe9s\xc9\xd6S6@\xca \xeb\x15N;;\xcd\xfe\x99}\xdf\xd5\xb6P\xac\x06\xda\x0e\x1f\xaf:\x0d}\xe1D-\x05\xef\x84\xae\xa9\xb9\xa4jk\xee[I\xaf\xe7y\x1c\xb5\xee\xdd;xt\x9f8\xc7\x93 \xdc\xbb\x7f8z\x84R\x0b\xaf\x08G\xfc\xc5\xc1\x10\xe3\xa2\xdc\xbf{ot\x00\xe24\xad\xde\x96G\x01\xce\xb8\xbc\xea\xba\xa3\xe1\xc1!\xdc\xe1\xbb\xf7\xe4 \x8c\x86(\xc5\x88w1\xffq\xff\xde\xbd\xc3\xfb(X\x89*9\x17\xa0\xb8r0\x06\xf5\xe6\x0b\xc2\xd2K\xfbj\x8a\xf6\x10\x13\x9a\x8f\xe4\xe4#O\x9el\x00\x05\xfa\xbd\xa1\xa78\xd7{\xa0\x0e}\n\xa3!\xdc\x01\\\x9e\x0f\xb4\x1dB\xa0\xa1\xb5\xff\x00b\xe5\x18\x1d*\xf2&\x0c!\xcd\x01\xcf\x02\x05\xb4\xed\x08l\xaf\x1aQM\xcd\xa5\x07\x07\x07\xd0\x83\x07\xf7\xe0\x1bp\x19<\x81\x83\xfb\x1e\xf4\xc1u\x87\x18\xcd\x0c7\xfb\xden=\xbf\xb1\xdd<\x90\xcf\x95\xb8\xfd`I\x89\x82\xb8\x80\x98 Gp\xe22\xd8\x879\x06\x95\x03\xbe\xae\xc2G\x81\xde\xe7\xdec\xdc\x8fk\xf8\x06\x16\xf8\xf91G\xe4 D\x1e\xae6\x95\xban\x06\xbb\x13\x97\xe3\xbe{\x8d~3\xf0\x0d\xf0*._\x99\x8d\xb7\xdb\xc4\x7f\xb4\xc3\x98\x86\xdaz\xce\x18L\x075\xf7a\xe9\xc3-9\xe2\x98\x8c\x9a\xf2\xb9\xd0I\xb6\xb5\xd4\xb5\xf9\x16\xbe|8\xbf\xba\xb2\x7f>\xae\x1b\xc8\xe4\x83\xfb\"(\x85\xeeA\xbd\xf6f\x82\x82\xd0\xf3\xe1\xc4\xbdF<\x86\xa7\xc0'xc\xe8\xea\x86\xf0\x9d\xca\xf1\x89\xfe\x11\xb3\x03_J\x0b\xd1u\xaf\x87\xa1\xa7n\xba\xfa\xfcA\x81\xfb/\xdd\xcb\xddp\xfc\xf4sq\xdc\x87\x0b\x9fC\x9b\xb8>QMr!\x1f\x04\xccK\xe9\xc3\xf5\x0c]\xb6\xa4\xb0\x96#\n\xa3\xa8$\x84\x83U\xc9{\xe1\x92c\\\xe0\x11tN\x83s\x8e\x9e\x02\xd5\xde\x13j\xdd\xb85\xaf\xa0R\xc7)\x06{\x99\xc0{\xd5g\xa2\xd5^{\x84\xd9\x97\xed\xa8\xc5\x91)k\x19\xdcS\x91\x81\xfc\x16\x9e\x88,\xe6\xbc\xd6m\x837\xa8h\xba\x0fy\x81\x1a1G\x0d\xf7\x02c\x82pBn\x02\xda\x98C\x12U\xe4\x84\xfe\x82\x96rk\x1a\x9f\xb5o\x10\xa6\xc7\xd2\xea\xe2\xf8{\xbd\x18\xa1\xb8\xde\xef-P\xda3\xfbb\xc9\x07g\xc6IK\xec\xa3\x8e\x1a=\x96\xc8\xcc\xd1q\xce\x919\x14\xc8<\xe7\x0b\x17j\xc8<\xc70(\xdec\x98\x0bd\xe68\xb8\x81>\x87<\xa9\xe8,\xfd\x02\x04^\xb9K.\xf3\xc2\x1f98\x0e=O8\x15\x9c\xb8\xc7\x0dF(O\xf9\xb4\x13OAj\xafW\x97\xf0\xf4\xe7c\xaf\x17\xf3R\xf5\x84S\xd0\x86\xc7\xef\x9b\x84\xa4\xea\x9b\xadU\x17\xbebi\x16&\xf1\x18\x1c4\xe6X\xb4\xd0\xed,;0\xe5\xb2\x96\x0f] \x1a\xc33;\x9b%\x1f\xb01\xbc4O\xd5b\xb4\x10\xed\xfeh\xfe,\xdb<5\x7f\x16.\xf6\xe3\x8e\x12\xb1\\\xd8\xee2\xb4V\xebv\x90\xb3,\xa7\x98|\xceM\xdc\xef;\xd0#\xd2iJ\x99-\x9f\x8f\x16\x02n\x9b\xcf\xdb8\xa4\x19w\x1b\xdfg\xcdh\xa9\xcd\xe8GW\xe6\xa6\xb9[\xb9k\xf8i\xf3\xab\x83\xac\x0fZ\xbeD\x94n\xac\xa6Y\xf9\x88qn\xeb\x8d\x15\xc1nP,g\x14\x02\xd3\xd5c}$\x15\xffC\xdd\xe3\xcf\x90\xe6\x86\xffy8\xb2d\xbb\xe9\x14\xdfC\xef\xbc<\x1f\xe9\"\xd8\xb6\xabb\xbe\xa6\x0c%\xe5\xb9\xf8\x95\xe6\xc9\x91\xaak\xf3\x16K\xab\x88\xf58i\xeb\xec\xc56\x8a:v%\"\x85vjR;1\xde\xad\xf5\x1dC\x89u\xda\xcb|@\x84 \x0d\xf8\xf2\x16z\xec>|\xf4\x88+\xb7\x03\"Kd\xdd\x97\xde\xc9@q\xaa\xba%\xf3.\xf7\xaa^+\x91,m\x8a5\xd2\x12\x99J%\xb1\xa9e\xf0\x81\x96\xb0\x87>\xd4l\xf8x\x84\x81G\x89w\x1cbzxC\xd8\x99\x18\xf2\x8a\x07\x86L\x90\xa19M1zC\x0c\x853D\xe5\xc89\xa8\xb7\x8cqE\xde\xf5\xf6+\xc29\xd3\x0ckU;\x8ct\x01\x1d\xb1\xc3\xca\x888\xac;1\xe6\xa3\xd1q \x1c\xac\x83\x9b?\xb3[\x14v0\x85\xa9zch:\xd2\xcdW\xa5\xaf\x99\x0c\xf5\x19I\xc9 \x13PV\x1bQ\xd61J\xa4\n3\x8c,\n\xbd\x9e1\x833zLJ\xa9{\xe5\xa3\xc9\x9eMg\xc5\xfd\xff-\xfaQ\x0fm\xc6\xc55\x17\xaf\xd5\x81\xa7)5\xc6\x1a\xed\xd7p\x04\xee\x02\xcb\x16gTk!D\xa9wk!\x8c\x8eEY\xfa\x8c\xc7\x94s\xf3\xed\xe1\x85\xe7\x83\xe5b\xf1\x86k\xd6n\xe0\xc3\xdc\xa3\xb0\xd3\xd39\x1e\xb4\xf3\xffI\x16[a\x1cTr\xe0\x9c\xf2\xff}X\x9d\x17\xafV\x16\xec\x87\x02a\x82\x02\x0f\x8a\x89\xe3\xf9\x97\xcc'6\x083\xfc\x9f\x83e\xab\x8by9Q\x90\xb8\xba[CJ\x19&\xb2\x1ecgw\x02\xa1\x8f9m\xf4IWYld\xf8\n\x030atO\x89\x94\xcdA>\xebpB\x95/)gTKm.)\xe5\xe9\x96a\x94\x8bE\x10e\xcc`\x8a\xa4\x06\x05>6\xe7B\xc9\xbe\x0b\xe30g$\xb1\xd0\xc1s\xbd\xbd9[\x04\xdb(ol\xc9q,@\xf3\xd1\xcc\xce\xeb\x84\xb2\x16sX\xb4l\xa7\x97\xbe\xc6\x0dA\xdef\"\x91\xc8\xb3\x1c\x7f\x1eA\xe8\x06(\x9b\xa8\x01\x046\xea\xc0I\xa4\xe1\x16F\xea\x06x\xb5\xc2\x90wW\x8c8qI\xe3\xe3\x9d\xf1\xbf\xba\x08\x92R0\x83\x9e\xb9Of\xb22\n\xa3/\x86\xc2\xb2\xd7\xe4c\xa9\xde\x1c)U<2W\xdc\xd24\x1bF\x84\xf0\xf2\xfb\xa2\x04\xe6`o&\xd6O\x0e\xfa\xeb`\xa3\xe5\x92\\\x07\x9b\x1a\xdb+\x9d\x85M\xcfKV\xcb\xe2\xb8%\xed\xf5<\x99\x035w\xd94\xe5\x05-\xfe*\xd5d\xa8\xa0q{\xcd\x81\xbfy\xbd\xae,\xf9O\xcba,\x99\xd7Y\xb6\xa1 \x97\xbfR\x1a\xd4\xda\xea\xef5\xeb*fb-\x9fn!0\xe5#\xc6\xee\x96\x82.\xe5\x82\xde\xc5\xec\x1ar\xb7\x80(\x97S\x8e\xcb0\x0e\xd2[\xc7\xf3\x8a\xd7\xcee\x90\xb1\xfbw[-\x07V\xa5\xe8\xde]O$M\xed$\xce^iY)\xcdA\xdd\x0f, \xcf\x0f\x87\xe6\x84\xe7\xf7;\x05\xf47\x1c\xc8(\xde3\x01\"\x9d1\x14\x19\x0bb\x91\xb1 uC7\xf6\xd0\xc2\xaa\xc4O_$ \xc6P\xacB\x17\x8e\xd1\xbeV\xb8\xe6 un\x81*}@\x9f6p\xc9 \x84\xbe\x8c\xd7o\x14\xc7`\xf0\x84\xe6\x81\xf0\xe0)\xad\x1a\xaf.j\xa5\x9eN\x14\xd4\x90\x13\xf4n\xc8p\xa5%\xfe5E\x84\x1f\xd57\xf3n\xdb\x86YfL\xb9\x16\xe0\x03\x84m2\x92\xde\xc0^C\xc3\x16\xed\nt2\x9b\x9bQ\xd0\xaa\xaf\xc8\x95-.\xfb\xf9\xb0?\xfd\x89\x02\xf2\xbd\xeb\x7f\xf5o\x7f\xbc\xf3\xf57\xbd\xc1\xbb\x9f.\xfe\xcf\x87\xff>\xdf\x0f\xa5m\xc5\x12\x88L\xfaw\xccVA\x1a\xccrtD\x81\x15\x0b\xe6,\x85E\xc8\xa29\xc4\xc1\x9a\x99\"h(\xf2_\xb2\xd2\x94\xd1\xda2\xe7\x8ef\x87\xb6iW\xf5msg\xa9\xb93\xc9 \xcc\xd4/f7\xba\x19\xc3F$Ak\x88I\x7fK\xbbqWL\xd0\xde\x16\x7f\xe6I\xcc\xc6\xba\x8d\xca\xe0\x10\xa8?\"6\xbb\xd9\xb0\x0b5Rk\x7fkH'%\x06\xbc\x1a\x849\x85\x88\xa7s\xf9)%/\xa5\xb7y\x92\x9e\xef`D\xab\x8f\x13\xe3\x97u\xda\xca\xc4\xbc\x95\xe8\x9f\xb8\x0e6\xa8\xf6\xfb\xe50\x81\x89\x0c>z\x12\xccV\xed\x81\xb1Us\xc1f\xc3\xe29%\xbb\xa9\x8f\x98n`\xa3G\xb5.\xab \x85\xc0\xd0]\x97\xbe\x18:\x98\xb3\xe9\xc8\xe4\x94T\xf4\x88{ \xc4\x93%\xcb5\xa1\xe4E\xb0f\x99\xcb\xbcz\xff\x9d\xe7:\xcd\x1b:\xef\xb4G\xa1\x9d\x9e\xb1\xc1e2\xbf}\x9b\xb1\xb9\x12\x1e_\xa5\xc9:\xcc\xd8 exC\xbaB\x9c\x9eE)\x0b\xe6\xb7\xc0\xffuL\x87jE\x8b\x18\x90\xad\xd3\x00\x83f[\x1e\xbb\x96\x83j\x0f\x02\x0e8\x84$\x8e\x92`\xde\x05\x05\xf8\xc3\xc5\xa6\x94e\xdb(\xb7Y\xe4\xb1I\xc6W\xa0k\x9b\xb1\xcb\x06X\xa1\xb3\x11\xbc\xdb^n\x9bI'_\xab\xef\xc2\x88\xbdFva\xa6R1\xca?&\xe7$I\x0f\x06|w\x9feZ\xb2c\x12\x97:\x8d0k\x826\x94\x9dj9\xef\xabn\xfdP\x99Q\x91b\xd8-\xa5\xe9l\x98A\xc6\x08t\xf5\xaa\x18\x82B\xa4j\xec4\x95\xa8)K\x05\xe2\xa9\x0e\xeb2\xdc\xd1E\x18\x87\xf9\xb7\xc9\xfc\xb6\x93P\xcf\xd7\x85\xaa\xf1\xb6N\xe3\x10\x19\x97\x91\xc6\xe9UL\x07\x01\x1e\x14\x0d\xbda7\xd8\x90\x9d\xf3i\x17\xc1.\xa3\x04\xc3\xda|\x1b%\x97\x9a~\x15f\xaf\xe4\xdf/\x17B^\x91\xed\xf3\xa2\x9d\xdb_$\xe9\xfay\x90\xa3\xf3\xf4w\xe2\xef\x8e\xfd\xc8\xe2\x9d\xfb\xa2\xcb\x05\x18\xcc\x15-\xaco_\xffp\xa6\xbd\xea\xd8\xad\\>M\x9d\xea\xd4{P\xa0\x0c\xe0\xf5d\xb9\xb4\xebJ\x07\x1an\xc1\x84\xe3\x8cL'\xeaC\x0d\x1a8\x1c\xf3\xf5v\xa7\xc6\xfa6\x97Uh\xbe\x07.\x1f\xbcXT\x1e\xf9\x87\x0f\xb0\xa7u\xd0\xb0f\x80WH+\xb2\xac`\x15\xdb8\xdbn\xb8\xa8\xcf\xe6\xf0\xad\x9c\x0d\xaf\xd9\x16\xfc\xada\x95\xecH!s\x94T\xb7\xd0\xe6\xe2H7(\x90Lf\x9ci\xbb\xce,\x89s\x16\xe7}\x1a\"\x1e\x1a\x9a\xb0LE\xc6\x11u\xb3Z]\x1f\x9c\x9c\xdd\xe4\xfb\x9b(\x08\xe3\xc7\\\x8c\xcfX>y\xfb\xe6\xbb\xfeCG\x05\x97-\xb0H\x86\x8cRo\x06\xbc\x95.\xdd\x18\xaayx\xd1\xf5\xd3\x91@\x8d\xa6qz\xc1f\x13\x85\xb3\x80S\xb6\xfd\x9b\xfe\xf5\xf5u\x9f\xa3x\x7f\x9bFda\x9bWgm\x94`\n\xec \nxI4\xa5\x95\xbf\xca\xeb9!\x8521\xef/\xf2\x1b[@j\xbdPy\x11\x0db\x90\xc8\x04P.\xd6\xa5=\x0dz\xad\xcd\xb6\xe2v\xa7\x9e$\x954`\xe1,\xd9r\x8d1\xc9QdS\xe4\x17x5\x082\xe0\x8bnC\xc8\x1d\xc6\xcc\xb1\xadj\x9d\x85BP-\x91\x97\x0e[\xac\xf3\xd8\x1a%8\x92;\xcfq\xd4\xbeO\xa5\xe5\x17X\xc7g\xebz\x83|\xc5bwk2D\x8b\xe1\xe6D\xfeZh\xd2m \x8ak\x05\x06\xc1Q\xda\xfb\xd85i\x88n^\x98\xf74Kx^\xb1\x84OQ\x956\\yq\xf3i#\xeb\x95\xda\x8b\xddU\x0b*+\xa6/D\xa7\x95\xfb\x0c\xb4\xe7\x00\xbe#\xda\x97\x91\xddB\xd1uQ\x8fj,\n \xae\x15\x9dt\xb4\xe7#\x94\xa8\xbah@\xd5\x9f\xb3$\xfe\x9c\xb6\xfft\xf6\xf2\x05\xf9qX\xa9W\xe9\xbdMY\x98Y-\x18\xf2\xcc\xc5U'\x80\x7f\xff\xe8\xa1\xeaP_\x7f\xa4\x15\xba\xb5\xc4x\xe6\x0f\x06\xf5\xddhK,\xab\xeb\x0d\x92\xd06%\xb7\x85m*S\xed\xccR6gq\x1e\x06QFn\xdf\xc5o\xaeF \xf9\x00\x8a\x00\xb7\xe2\x05\xa1X\xe22\xf9FE\xfe[\xb3|\x95\xcc\xb11\xfaS\xbe'\x87\x19\x86\x7f\xf8t*\xaa\x1cx4I\x18\xef\x1cC\xe9\x9d_\xb57\x18\xf6P\x13\x0ci\x96\xca`i^~\xc3\xec\xf3\xd2o\x19\x98\xb3\xf2\xceI\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedfc\xcf\x04\x00\x05\x1a\xdc*\x8f\x0ftF\xef\x8f\xb8\xbcit\xe7\xfb\xe8\xe6r0r\xe2\xc5O\xe7?N\xde\xa8\xe8\x87k\xe9\xf8\x84\x7f\xa8\xc2\xe2\x87\x96\xc5)e\x0b\x96\xa6( \xd0[\x17\xdb)BRj\x1d|\x7f\xf2\xecy\xed\x0b]\xc7\xb7\xc0<\xaa\xdex\xd12\x8a\x92k6G\xb6\xf0\x1f'o I\x81\xb7\x06)\xfb\xdb\x96eyfB\x08\"rR\x83w\xe3nV\x99E\x07\xab\x8c \x83MV{L\xb1!/\xdf\xddq\x0cV\xc3F3B\xabxP\xbam8i\xbam\xc8\x9f\x94.\xdd\x93\x05]\xcb&\xd2\xc3l\"\xd0V\x1d\x0f\xf7\x04\xf3\x9b8\xc6\x06\xec\xcc3\x97\x16P\x83[\x10\xd7\x91\x0d\xaf\x13\x83\xf4 \x16S[W\xeb\xf6\xa6}_\x93\x86\x0d\x951\xf4\xd3\xa3w\xf1\xfe.\xbbY\xdb\xacq\xdb\xd5\xd0b\xa3\x08\x8a\xec\xe2C\xed\xb6\xbf\xfeH\x7f\x07\xb9qc\xa7\xb9A\xd0\xf7*\xf5\xab\x9e\xb5\xf2\xf9\x9c=\x98[\xf9*q\x84\\O\xb8B\xaa\xf3\x04\x1c\xe1\xea#\x95\xe4,\x0f\xf2-'\xb7\x0e\xfd\xe5`jLN\xf3\xe4\xa71\x1c\x0c\x87\xa2t\xf2^\xc5\x8b\xa5\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\xe8\x95\xb49\x14\xbfj\x1da\x9118/\xff,\xc7f\xe7\x05\xbe\xce\xb5r\xfc_\x84\x9a\xab\x90\xa9j@\xd5\xd2/4\xf0\xb0\xc1\x82\xe5\xe68rW\"\x16\xa0\x19*tS\xc2\x18\x9c\x8a%\x01\xa7g\x08w\xc6\x1fy@5\x06\x87\x0e\xa7\xa80\xfaX\xcac*|E_\xcd\x8dp\x85m\x0cN\xa1\xd0h\x8dp\x0d\xa3\xf8\xd9*\x00\xf2'Oo[\xcca\xda\xa1\x03o\xdf7eO\x96\xcfG\x98\x05\xe8R\xd7\xd5\xad~odo\xcb\x8c8\xb6l\xc0R\xaa\xe6k#\xfel\xda\x0bM\xfd\x1e\x83\xa3)\x1aT\xa9\x8e\x9ef\xd1\xa8d&\xf4\x10r\xae0\x95\x9dtv:\x95\xfa\xd6\xb9\xe3\x17.P\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83\xe5z\xea\xba\x93\\\x06\xba\xeb\xc6\x9d;\xc07\xe9/!\xbbn0\xbf\x99\x81\xc0<\x88\xa5\xf4K\x13V\xda0\xe3\x8d7;[\xe9\x8f>\xb4\xc2\x01\xb8\xd5E\x8d\xc4E\xf3@\xebP\x93h-\x11\x9b\xa8\xf8\xbbX\xd9\x11\xa3\x90\x0cB;\x8f\xdd\xd4\xc2\x82$\xcb\"\xf10\xd8L\x99\xe5\x8e\xa1V@$wO\xa0\x07\x8e\x8f\x81\xb1al\xba\x8f\xef\x97\xc6?g\x11\xcbY\xa7\xad\x17EU\x97|\"\x86\xbc\xda\xe5\xf6\x97,\xef\xd4\xb8\xda8\xb9@\xc4F\x82\x8c\x0e\xbb\xf5y\x8e\xcb\xa9R-\x1d\xaf\x82\x9d\x1c\xd0d\x07\x15\x07<77;w\x96\xfb\xca*\x93l\x80\x80\xf2\xea hk_\x08Ym\xb9Y\xe5SI\x96-z\xf4\xacs$\xe7B\xa6\xfc\xe1\xd4\x18\xe3s\xbaqT;\x957\x8c\x11\x9d\";\x98,\xa4u\xd1vkV\xdf\x8f\xba\x83A\xc3 9\xe0)\xb9p\x904\xa32\xfa\xde\x9bM\"\xfaT\xd0\xd5\xe57\x98L\x87\x99\xd8N\xef;\xce\x84\xc5y\x1a\xfe\x16S\xe9\xb6/S\x0eL\x06\xcf\x0fh\x99R\xc51H\x9b\xa1\xc9E\xc8\xb0\x00\x96\xb3\xf8[\xe4\xf3\xcfO~8ys\xc2\xf9%W\xd8}\xa1\x9e\xfb\xe0\xbc|\xf5\xe6\xf4\xe5\x8b3\xfe\xe7\xab\x97g\xf8\xe9\xd5\xdb7\x8ea\x81fZ\x97\xb3(\x89Y\x97\x15\xd7\xa4\xb2\x19ZP\xfc\x86\x15\xbcL\xe6\xb7\xfa)\xdbi\x1cZ\xee\xd8\x1aWP\xa4\xcb\xd7\xc6\xe9\xa9\x97\xf3\xd2\xcb\xf9gNe^9\xf9o\x9a\x14i\x0fc]\xdb\xb0k\x84\x85\xaa1\xae\xaa'\xf6JB\xeb\x18K5D\xd3M\x1a\x94\xcfm\x1a\x8d\x95\x9a\xb2\xc3*\xcf\x07\x9d\xfdi$\xba\xd1\x92\x91\xc5\xa8}\xa1\x1a\x82\x82\xe8\xcb\xe3X\"h5\x9b\xcf\x98R4q\x16N\xd5\xf3\x11\xcc\xd2\xd0\x95\x88c==\x1c\x8e|8\x1c\x1e\xf0\x7f\x0e\xf9?\x0f\xf8?\x0f\x0d\xe82\x1f\xa4l\x1e\xa6\x1d\xd2\x8d\xcb'\\\xa8\xfc.\x97\x9a\x95O\xb7\x96i\x11\xb7\x94\xbb\xa9Pjg\xc9\xdcz@_\x02\xdd\xae\xfb\xd0\x05\xe2\x9a\x95\xa7(\xa1\xa3\xe6\xc6\xcb\xc6;\x80\x1e\x1b|\xafT\xee\x84\xff|M\x06A\x98\xc0\x8c~f\x9b$\xc6{\x9ds\xfe\x1b5\xe7\xae\xab\xaf\xadQ\xcdi-n\x10v@\xb7\xbe \x99\xc3^\x9aml\xa1(\xfc\x9f?\xfe\xf0}\x9eo\xc4<\xec\xa6\x9apG\xcf8\xd0\xb0\xaf\xb9\x14h;\x1e\xb6\xd2\xa7r\x0dB\xc4\xb0\x13\x91\x92\x8f\x02\x9d\x8d\x1br\xc1\xf9Y\x14\xc9m\x13\x9b\xeb\x8a\xa8\xbev\x97\x110#\xa9\xfe0a|qR\xd1\xf8\xdb\xd7?\xa0\xca\x1c\xc2\x11\x84\x03\xed-\x8c\x81\x95\xfdI\xfe\xb3/\xf6\xa3\xcf+\xb5\xf8\xbcH\x93\xa2\xea\xc8\xd0\x0b\xe6\xe9\x97?\xf8257\x19\xbb\x82\xc7\xe0%x;\xe6\xf8\x08\x16\x9d\xa9\xb1|\xd2\xaak\xe8\x0b\x96_'\xe9{i^\x87E\x10Fln\xf2\xfd\x90\x8f\xe8:\x0f\xd7,\xd9v:o\x97\xcf\x17\xeb|\xc3b7Q\xc7Q \x9d\x7fa\xaa\x1d'\x8cg\xd1v\xce\xe8\xf0!)\x9d\xf6p\xc9*\x1c\\\x87\xf9\xea\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|\xb8$\xc9+\xc5sWsoO\xb4C\xb7#:\x8a\x1b\xeb/mR\xa9\x99\xd8\"\xf9\x1cl\x92\xe8v\x11F\x91\xc9+X\xfd\xe5:[y\xd1_\xbfk\x90\xb1h\x01G\xf4\xdfXS\xb1>\xeb\xa2l\xec>\x1a\x9a\xae\xaf\xf0\xf7\x0f\xcd\x17\x92\x1e>\xb2\xdc<*\xef\n\x85!\xe6\x84\xb0\xdc\n\x1e2\x8f!)\xbfUQ\x02\xc6\xb5\x9c\xf7\x9f9\xbf\xc3\x87\xd5y$j\x1e\xf5\xf9\xd5!\xeb2\x0df\xef\x19\x9fHg\xd3\x00f\x84\x9b\x9e\xd7e*\x83\x0d+\x8c\xe7\xe1\x8c\x95Zo\xe7\xab\xd4\x01f\x96\xa3\xe4s]zJ\xd9\x86\x05\xad10@\xeb\xa5\xdej\x19d\xeb\xf7\xd2\x9e\x079+Y\xcdN\xcf^\x92\xe1\xac\\\xd6\x1c\x8dg\xce\xa2p\xcd\x15\xb31\xde\x0e\xae}\x97\xc1n\xf6\x0cR-}K\xc7\x90\x8a\xe0\x13\xb6\"\x7fA]\xfde\x1c\xdd\x8e\x8d9\x063\x96\x86A\x14\xfe\xc2\xf8\\vX\xad\xa0v{U>\x86\xbd\xc8\xde\x87\x9b\x17\xdb(\xca,c@p\xe6\x05\xbe\x0f\xe2y\x84\x91Q*V\xf3J\xa3\xba\xc6\x0eL\x04~Q\xf1\xc82\x1f\"\x9f\x8buE\x88\x04\xd3l\xa4%\xdb\xc0R\xd1\xdbZv\xa0{\x82F\x1eV\x89\xb8Xwe\xba !\xdd\x82\xaft\x7f\x0e\xbe\xb6Tq\xe36\xd6RW\xc2\xaf\x9a\x04\xfdP\xb9LQ\x06\xb4\x15\xa7\x93|D[\x01\x0c\xe8\xfbf\xb8\xe2\xcd\x9f+\xf4\x8fm\x81u\xb0{\x9c_\xa1\x84U\x8f\x97A\xefe \x80\xea\x87t\x10f\xe2V\xc1\x95\xa7\x0d\xff\x08\xa6s\x17#\xc4\xc3\xb8:\x07\x8f#\xfb\x84\xa3\xfd\xdc\xcd\xdc\xab\xd2\xa7s\x18\xf3\x9a\xb1^F\xb8x\\y\x9eA\xa5\xe2\x9b\xbd\xf6\xd1~n\xb2\xe0\xe0\x96\x15\xcc\xf0J\x0d\xd1\x10\xff\x8f\x97-\xdf7\x8a<\x0f\x8f\x07\"\xcb\xd6\xdaU\xdc\xdbJ\xda3\x13t\x808|\x98\xc1\x11\xdc\x0e\xb2$\xcd\xdd\x19\xdf\xe0. \x9a\x94\xa9\xf3\x92\xbc\xdd.\xe1 \xac\x95\xb7[\xafw\xd9\xa4\x7f_\xc0\x04\xd6\xd3K\x8b\xc1\x0b\xdd\xbd\n\x80\x9d^`&\x07wY\xbd9\xef^yp\x04K\x99S\x86\xb9\xbc\xa8\x0f FP\xf3Z\xd0\x96\xcf\xb3V5\x86\x1e\xb8\\8p\x06|\xe7/T\x9e\xd2\x0b\x95\x9b\xb4\xb9Q\x03\xd1\xaa\xbd\x91\xfb_&CfQ\xa0\x91\x99\xa9s\xfd:\xe1\x0b\x80n\xe5\xa6\x83 \xcb\xc2e\xec\xfe\xfd#606\xc6\xcdQ\x01\x99\x02\x89\x07x\x8aS\xdc\xf7-\xbd\xd7\xc8W!T\x05\x05\x810\xba\xd1\x9c\x88\xfa\xab\x00\x03\xa0_2\x08\xd4\xe4j9E\xaeD\xdc\x1b\x0do\x82\x81bjp\x04[\xed\xd7X\xffV_\x89\x19\n\xc4u\xe2\x11\x0c\xea\xcc\x01\x8e\xcc\xaf\xc7\xb05\xbc\xae\xf7\xb5\xb0\xf7%\xf9\x14u\xa1~a\xcb\xf2W\xbd\xc1\x8d\xb5A\x11\x18\xea\xa8\xf8s\xac\xa8X\xbd\x1d\xae\xa2\x1b\xb9N\xb1\xb1G\xda\xdfES\x86\x05]\xd9\xdb\xca(\xa5\xbc\xf8\x83N\x8b\xea\x0d\\\x15;K\xb0\x85\x9eU\xcf\x93\x1cy\x8e\xf6\xb3^u\xdd\xd0\xb7.n\xd0 Jop\xa5\xf57\xf5\xd6\x97-\xab]H<\xdaji/\x8be+^\xd6\x91\xad\x04\xd4$\xdc{\xea/4\xa2\x0bo\x93r\xd5\"\xf3U\xa7\xc8\x15\x89h0gi\xe6\x17\x1dY\xb0\xf3m\xfc>N\xaec\xa1k@\xb2A\xf1g\x93&W\xe1\x9c\xcd\x8d\xf8)\xc2\xb1\xe2\x80\x8b\xae\xa6\xb2\xa7\ni\xb7l\xda\"\x8c\x08\xa1\xd1\xa1\x95s\x12\xf9\xces1/\\\xfd\x06\xae*\x80\xba/&o\xd7\xab\xd5\x07z\xedc*\x82*oF!D\xc6\xc2)\xe8\x98\xee.:\xe1\xfd\x0bj]\xbd\xf8s\x8d\x9d\xa2\xff\xc2w\xb4h\xc2\xc0R~9\xe6\x8a?*&\xa8\xba\x07X@\xbc\xe1lF}\x1csE\x9f\xeb\x15\x8e^\xa7>\x9b\x1b\x98@8\xbd\xaeL\x06\x83\xc8\xb8U\x96\x1f{\x18\x0d\xeb\xce\x1d\xc9\xdc\xabw\x1c\x15\x0f?#\x1e~\x06O\xe0V\xe3\xe1g6\xe1\xf6\x18&p;=3\xf0\xefE\x89w\xc7\xd3c\xe2\xdd|\x07N$\xb7\xcd\\\xfe\x1e\xa3\xf8\xde(\x0e\nG0\x97$\x83C\xd6\xca\x87+\x9f\x0bV\x17>,\xab\x8c\xf5cm]\xdec\x07\xe8f\x16\x19\xcc\x9c\xcf\xd0P \x90.\x98\xcf\xff\x9f-Ko_\xa5l\x11\xde\xf0m8r\x0c1\x9e\xc4\xce\xbf/\xf2 \x0c\xe1\x08\x9eA\x0f\xdeW\"\xfc\xe0_\xbf\x8az\xdd\x82\xeb]\xf4nEN\xcd*\x12~Vn#\xb6B\x1c\xa4\x7f\xe0,v\x0c\x07\x06\xa5\x91\x1c(Qi\xa4?ME\x9au\xd29\xdb\xe4\xab1\xdc30\xc1 \x0d\xd6,g\xa9\x18\xc0\x88\x1d\x1a\nEA\x18\xd3j}1]0\xe8\x10L\x05\xda\xbce\xd5\x0ekl\xeeH\xcb\x92\xb1\xffn\xe0N\x7f\x1aL\xcf{\x1e:\xb2N\xffmt\x8e\xf7\xfa,\xbeW 6z\xdf}7\x9d\xfe4}w~\xfe\xcd\xb9gK\\\x03b\x16\xe5\xc2\x94h*m:\x86\xe3\xd4\x0d\xc5Gq\xa5\xda'\xb2\xc5n0!\x85\xbdb\xd6p\x8e\xcd\x97\xa9\x16\xcd\xacZ`/\x1e\xe8[ \x98/\x0c9Z\x15\x1504\x1a\xa5\xab\xae\xc0\xb0$\xdav\x83vF\xa7\xe2\x86;[`=\xfdQ\xc4R\xe4\xf6VB\xb3\x1b`\x08G\xb1\xa88\xa6\x08\x9e@<@\x90n\x0c\xf3\xcdg\x1cA\x0fC\xe7\xef2\xf3`::\x17[3\xf2\xa1/\x02v\x7f\xc6J\x04\xc6\xa0\x14`]\x0ci\xab\xe1\xdd\x8a&HQ\x92\x10\xa3\xc0E\xe8M\xd6\x01tA\xb0Ry\xb9\x0d\x1c\xa9r\xca\xf2\xa2%7\x1b\x89\xe4\x03\xc3\xc7\xd0\xef'm\x8d\x81@\xd0\x90\xa2\x98\xb3i\xd2\x90\xda[>(9LE\x0c\xb6\xc0Cl\xc44\x08\xd3sO\xb28\x9b{\x99\xfet\xb8M-\x1f\xb4\x18\x97\xc1\xe3H\xf2\x86Y\xca\x82\x9c\xa1\x0eg\xd2\xefl\xcf\x95\x08\xe5\xc7\xb7\x8d\xd8b\x91\x9f\x91+y\xe7\x95\xd7\x81\xb6\xc6\x1e\xc9\xd7\x1a\xfcq-\xcc\xbe\xc7\xd5\x87S 4_\x9f\xc6\xb9\xbb\xf5ad\n\xd9`z\xf6\xc2\xecE\xf0\xc2\xcdp\x88\x01b\x1f\x06\xbd\x17\x06\x9a\xcc\xc31\xe3\xab\x8c\xc2\x8c\x8a\x1c\xc8i\xc6P|\xcc\xe8\xd3\x13\xa4\xc7\x8a\xa9\xc1\x91\xda\xc0iv\x8eQ\xf0\xc7\x10N\xb7\xf8g\xeb\xc0\xcc\x18\xa2?\x1cT\xc3\xc6R\xcdm\x08l\xb3\x0f\xe5\xa3\x9b \xec\xa9\x15\xa9\x98\x9a?\xc3\xcc\xf0 \xf6\x84X\x88\x03U{B\xe9\xbd\xd1\x9e\xa0JX4\x96\xe7l\x07{\x02\x8ei\x10.\xe3$e\xba\xe4\xa7dB\xc3G\x1f\x87 \x8d\x0c\x13S\xacl\xbd\x80\xb0D\xbef\xcb\x93\x9b\x8d\xab}\xf10I\xa5n\xae\x085s\x85\xe4\x12\xbc\x83\xba\xe5S~\xc3?eI\x8c\x83=\x11\x9eZ\xc1\xa0\xf8\xe9#f\xb1\xcd\xb1\xf0B\x0e\x06\x17\xea'f\xa5\xc8f\xc1\x86\xbd\n\xf2\x95\xba0\x8b\xa5\x0c\xefy\xf1ml\xab`\xfcR\x1e\xfe\xd6\x90\xd7\xaf\xd5\xad^\xc0c\xbb\xcf\x01]\xd0\xbc\xccXzE\x1e\x9c\xd3syk\xf3\xf2g\xa8f\xfc\x80\xba<]\xbdQ\x17\xed<\xb4\xb6@\x95\x9cv]\x06\xb3\xf7\x14\xc8\xad4`\x98\x98\xa2mV\x07h\x8a\xfd=\xab/I)\x8b*\xe5\x9cJ1-\xb9\xa471<\x81\xf41\xc4\xbd^]\xcb@\xdb\xce4>\xa7e\xc3H\x0bd[\xb7N\x0d\x19VlQ\xb7/S\x16\xbco\x99\xd9\xc2\xcd\xe9\xbe\x88\xaf:\xe3\x7fm8\x14s\x11\x0b\xd3D\xa8\xdfR{E\xabJ\x81\xaaz\x1b\xa2\xa4\xe1\x08\x81R\xc8\x8a\xefF#q\xa8\x1b\x891\x94\xad,.`\x8a\x15\xfb\xa8n\xfc\xf0_n\x88\x89\xbf4jY\xdf\xac\x85\xab\xb2\x01\xd4,\x1a\x18b\x82\x92\xe9\x98\x96\xda(\xa4\xe7\x83<\xf9\xd3\xd9\xcb\x17@9X'\xea\x85k\n\x14\xa3\xe0\"D\x9epAK\xfdg\xce\x9ar\x8f\x84\xa1\xf2[\xe6\x91\x98\xb37\"\xde\x17\x94\xac3\x99\xb0\xced\xfd~\xa3X\x83\xe6\x18\xe4T\xd3\xec\xbc\xc1\xa2\xb8\x97\xd6.\x8e\xf9\xb0\xf1*\xd2g>\xdd\x9cWt\xd0\x08Mf$\xc0\x94\x8f\x98rO\xc5\xac\xb7\x9bg\x92\x0d\x1e\xd9\xac\x93+\xd6\x90o{\x13\xe4\xab1\xdd\x0c\xdc'\xf3\x98\x81\xe0\xb9\x1b\xfb\xc5\x1c\\HK\xae\xd7\x16\x03\xd2\x95\xc8\xf9\xc2\xe7n7\xaf\x18\xf2ADP$i\xa2\x1f\x86B3\xbd\xd0\x8c\x0b\x89.\x89\xa2\x1cJ[\xe7\xcb\x85\x1d2\x11`;\xee\xde\xd0o_r(\x96\x1d\x05\xf3\x86u\x87\x1d\xd6\xbe\xb9\x15\x11}9\xd5X\xa0;kr\x81\xedjF5\xfbEm9\xe0*j\xb2W`\x8f\xb9YDNMm\x08\x15\xb5\xcez\xbd&\xeb'\x07\x8e\x0d\x9e%f\x0d\xc0Q\xc3-f\xc3-\xae\xfau\xde\xbf`>\xff\x87\xed\x1d\x1fm\xd3\xf6u\xd8=\xcd\xc5X\xfd\xc5\xa5\x1c\xc1\x96\xdb\xeciZQ=+\x02\x97\x94:\xb6\x80\n,\x99\xbe\x9bE\x9cR\x08\xb3!\xf1\xf5\x82\xa1\xe7\x94`871tPL=\xd7\x98\xba\xd2\xe1\xf9\xeb\xf2\x9a\xd4\x02 \xf1\xda\x898\xdao\x95vJz\xb9\x90?\xb9bq\xfeC\x98\xe5,F\xfb\xa3\xed\x93\xeb\xac\x93m\xc6\xb6\x1b\x87\xac.\xd6b\xef\xd9m{!lk\x9e\\\xc7m\x05\xdf\xb3\xdb.\xc5f\xab ^2,\x85\x807Of\xdb5\x8b\xf3\x81\xfc\xe3$b\xf8;\xc8\xf3`\xb6\xc2\xda\xae\x93\xc4\xe59u\xad\xa5O\xb1k\x9d\xea\x8c\xbb\xd6+/@\xd7Z\xfazt0A\xc4\x15\xb9;\x16\xaa\x01iO\xb1\x99J\x9b\x80z\x86y[\x8c m\x84\xddV\x12\xa7\n~!R'\x1f\x03\xc9+\xf4\xc3\x12\xc9C\x9e\xadw%r\x80\xc7>\x8c,\x08\xc9 _\x87\xaehH\x02\xb1\x0d\x13\x0d_-\xc8h,i\xc0G{\x8bu\\\xb3\xb5\xa9J6\xe3\xdb\x9c}\n\xbeUju\xc27SO]0\xa7\xdeW1\xb5\n\xeap\x8eT\xc0\x01\x85n`\xd7@I\x99\xbcRD\xd6\x8fd\xad\x8aYJ&\xa8\x19\xff\x8dv\xbe\xb4\x9b\xa0bp \x91F\x90B\xb1Em\xbd\x9a\x01\xac\xc9h\xa8\xb4\xe3\xcfI\x02\xd69\xadW)\xe1\xafQ\xa9\xd63\x94\x1d\x95~\x8d!\xf6\x06\xd9*\\s\xf6\xdd:/\xb9dZ\xc6\xb7%\xeer\x86'\xf2v\xa2%\x06\xdd\x12q'\x90\xadi\x92\xa7\xd9DdH\xab#}!-Ck\x0d\xf6\xa3mo\xbd?C\xee\x17uK\xcb\xac\x82\xd2\xfb\xfa\xb1\x19\xd3\x8c=\x9d\x9ce\x99\x0f\x0e\xff\x831\x87\x1cij\xb56\xa2\xfciv\x12o\xd7\x14\x11\xc3P\xf7\xc3\x07\xdd\xa5\xec\xa3Kq4\x0b\xc8\x89\xe1\x08}\x0b\x12oPD\xb3\x9f@JVR\xfdUb\x04\x94\x9d|\n\x8d`JQ;p\xe12\x11F\xad\xfaQ\x85\xf4(\x1d\xa8Y\xf6F.y1ih\xba\xebU\xda\xd1\xe6\xf1\xb1\xc1,\x89\xb3<\xdd\xce\xd0\xc0=\x99\xe8\xdf\xd0t \x86\xabv \x8e\x8aI\x8d\x0d#3A\xb9\x1d\xea\xb4\x93\xcc#\x0ee\x11\xb6\xaa\x9fh\xf2\xf7\x1a_\x1c\xeb0:)9z\xd7\x8bR\xa2\xc8#Sz!\x07\xcf\xe5K\xed\xb5\xf4\x9b\xb6\xe1\x96!g\x8f\xc4e}\xc8 \x0d\x00\xb3\xc2\x8c\xd58\xb4/\x81[\xc9Bo\xea\xcc\x90\x7fG\xe9\\\xeb`\xe3\x86\xcdc5\xe4\xa4\x91\xf4\xdcz$,\xe9y\x15\xbdE\x80%7\x9f\xc6\xe7\x18W\x9dM\xe3Z\x10\xfc:\xb57\x8c\xca\x90\x87\xa6\xa4\\+\xbaZ\x18\x82G\x15\x83\xa3*2\x1d\x9d\xf3\xb5\xd4\x7f\x8eIX5;\xf0bT6\xb6\n\xae\xc2d\x9b\x8e\xc15\xf4u`\xed\xeb\xa0\xdc\xd7\xc19\x1e3z\x83r\xabx\xc5N\x9a\xd5J#Pg\xe4|\xeb\x9a\xad\x0d\n\xb91&u\xb9\x15\xcf'+:}\xf3\xa5\x13e\xc4\x85\\%\xf2F&Y\xb7\x94\xbf:\x9dF\xe7t\xda\xad\x1f\x91\xceD\xe2\xe8\xe1c\xd8\xc0\x13X\xa8\x067v#\x18o\x11#WL7\x0d\xa7\xe6+.\xf0L\xe7\x0d%\xae0\x97\xe3\xaa\xc1\x12\xb5\xc6\x12\xe1tn\x8b\xef^\xba\x8a\x80W\xde\xec\x12?\x96- \xe3\x13X7\xa9\x1b \xe6\x8a\x0e z'k8\x02>\xa8\x0e>\x83!%\xc0\xce\xd0\xebk\xba\xf4a\xeb\xae\xbcs\xa3\xbb\x99|D\x9clQs[\xbbz \x1fu\xadE\xa76m\xf3\xd7\x8av\x9a\xfb-\x1ex\xdb\x86 \x1f1V\x07O\xbd\x1d\xe1\x17VA\x13Z2\xe9+pk\xbe,)\x9f\xf2\x1a\xd8\x07\xa0\x97Z\xd5J\x18\xd5\\\xfd\xc0H5\xd3)\x17f#\xd5\"\x12$NA\x90\x84\x1dA\x8en\x1ecL\x1e\xcd)\xc1Hd6(R\x1a\xf0\x02\xe7zk\xd3\xd4,\xefg\xe4\x16Q\x8c\xdd/\x06=\x88\x93\x1f\xb7y\x907*\xe6j\xf0\xcc8\xf8\\\x0d^\xe6g\x18\x92\x1e\xcdH\x8f\x06\xc1\x07\x8a\x81V\x0f \xd5@\xc9\xbf\xd1<\xd2\xeb0_\xbd\xc4+R5\xdfI{\xba\xd5L}\xafl]\x8b\x8cg\x0f\x0c!\xf3\x8fC\xec>\x1a\xdd\xab\x10\xa0\x8b\x0b\x96\xfd\x98\xcc\xb7\x11^\xf3\xdf\xad\xcb\xd8\x1d=x\xc0\x17\xd0}t@\xff\x8d\xee\x8b\x9f#\xf1\xff\xa1\xe7\x97\x05[wt\xcf\x1b\xfc\x95\x05\xef\x7f\x0c6]\xfah\x10]}\x99\xc9\xf7p\xe4\xb9U?\x8ePtV\xbd,C^\x0e\xa3\x83\xbb\x95\xf7[j\xea~5Y0\x0d\xfa\xd1\xa8\x1a\xbb\"\xa2\xf2\xd5\xe6g\xf8\xfa^\xd5{d!\xbcG\x0e*\xef\xf1\xdcr\xb0d9_\x91\xf2\xa7y\xc1\xbb\xc2\xec\xe4&gq\x16^F\x95\xcb\x1e\x9c\xedd\x83\xed\"\xcb\x93\xb4\xf2\xe9\x8a,\xca\xa5w\xed\x01d\xab^\x076\xaa)Y\xb8\x88\x8ag\x904\x86%qbx\xaed\xd3V\xd7\xe3\xf2\x98\x97FYg\xc9:\x05\xd6\xc0{\x13(A\xdb\x89\xbf\xa4q\x1bcj\x06\xf9\x88 \x0b?\xe0\x1c\x8e`\xe5.\xc4\xec\x1d\x01\xcf\x8e\xe7a\x0c&\x94}1\xfa\xb6HU\x14\x16\xb37v`8\xf4\xab\x8b Yy\xca\xedAK\xb2\xc1\x9c-\x0c\x83\xf4\xd1?d\xc7m\xb8\xadj\xa8\xee\xa3\x83\xa1\xe7\xaaV\xf1\n\xde\x12o\xbb\xef\x0d1\x96Q\xb1\x963\xb7\xcd\x18\xf1\x00\xf6&\x80\x96\xa5[\x0fs\x7f\xc9\xbb,\x8b\x94\xb1_P\x18\xa4\x17\x9e{\xe5\xf9\xf0\x80\xd6Yc\xff\x1fI~\xdf\xba.\xa6l\xe3\x9f\x8f\x0b\xad\xd0]\x977I\xbb!\xb3\xf4|\x08\x06/NN\x9e\xe3\x01\xba\x0f\x89;u(\x8e\xae\xe3\x83\xb3\n2\xfe\xdf\x92\xe5\xfc\xbf\x8c\xe5\xce\xb9\xdf\x00w\x12\x96n\xb5.j\xeb\x8c>\xf2\xb5x\xc1!\xc6L\xd2\x1a\xcf\x0d^\x1c\xa0`:'\x03\xc4\x1c\x9d\x10\xcc`@\xb0\xb7(\xd2\x7f\\,\xc4\xe1TSP\xe3P\x065\xbeXL\xd99\x8d\xc2\\Zj\x86|U@\xe8\x9b\xbc&\x8c\x0d\x97\x18\xec\x0e\x91\"\xa8-\x02i^\x8b\xe5\xffQ\xdfc\xfa\xbbs\xa2\xf0G\xa3\x87\x96\xc8I\x8dh$\x07\xc6\xae]\xd4\xbe\xf5\x10\xaf\x9d\xf8b1\x82\x1a\x7f\x10\x1c\xab\xc6\x96\x04\xbbz\xe4\xb9N\xb6a\xb3\x90\x95\xd2\x84t\x93\xd8\x10\xf8\x8cb\nj\xe5\x1c?LW(\x84\xf1I3\xa2\xa0}\x8a\x9c\x85PJBHK\\\xcd\xce\xe5\xa9\x1c\x08\x82\xa6\xfb\x90\n\x90T\xe6\x10\xf2\x18\x9a\x86\xe7\x9e\xf2\x1f\x12\x85\x8b\x1c\xf1\x92\x96R7\xe3\xd6T\xf6\xdd\x85\x03Z\xe7\xe1}\xe3\xfas\xf6o\xe6\xba\xc2\xcd\xb3Z-0\xef\xa6\x10\x1a\x86UaBH:w\xab\xef#%\xaf\x18\xa5\x86\xaat\xd0$5DnU\x92\x9b\xe3\xdb\xea\xc8WxxT\x86\x93\xaeR\x00\x1b\\`\xea\x07\x17\xff \xd2\xb1\xae\x1e\x10\x94~\xae\xdbN\xcb\x90\xb2\x04hrojg\xd9\x86\xa3P\x8cr\xe3\xb2A\xd0D\x94+\xe5\x19\x17F\x10\xf0j\xa5\xaa\xd9\x90\x0b\x98Zk\xd6\xc3\xaa<\xd2A\x16\x91|a)\xe8\x9c5 \x94:\x83\xcb\xa7\xa3\xc6\x15Z\x05\xad\x01\xd2\xa4\xc8\xb2W\xf4\xda\xd4b7\xf9B\x1e;4\xcd$F\xe7yT\xf5r\x99\x021\x10\xf1\xa5Y=\xbete\x1c\xc4|\xdb&'WT\x043\xd6\x01\xa0M.\xca%\x00\x18\x9cv\x0d\xb3\x11\xb5\xfe;\x07\x99\x88%\x90\x07\xa2\xb9\x8f\x97\x08\xf6\xf6\xfe\xbb\x9aTF\xfd\xe57(fe!e\\#u>\x84\xb6\xa9\xa3\xdbc)J\xa35\xc4\xeb\x96\x7f\x8d\xb0E\xe7\"$g\xd7\x8b\x9c\xdcE\xd8\xe0\x82S\xbcU\xaf\xe7\x83@r\xa2\xcc~a$\x04\xbc|\x97\xb9)\x8e\x88M\xc3ss*|\xfb\xd2\xa5n\xa4\x8b\\\xe6av\xdbv\xf9\xa0Gg\x80\x92\xbd\x04\xf3\x91]x\x97@\x9b\xec \xe2s \xbeR\xd2s\xeey\"\x11\x03I\xf71_\x93\x99\x1b\xab\x9c8\xc8\xe4D\xfe\x85X\x89\xfd\xc6\xbe,\xee3\x1d0Z>\xff\x88\xd9\x8bD\x0f\xa6\xa9\x9bgi\x80\x10\x1f\xa2f\xcc_\xd4\x91\xc0\x86\x01)YK\xd1\xb7x\xcft/\xb8<\xa1\x14'\xc4H\xbb\xc8\xc5\xa5\x9bt\xcaP9\x9b d7\x0dM\xa8\xd8c\xb8*P\xfb\x0f\xf0\x05$\x94\xaa( \x04D\x8b9\xa3f\xb6\x08\xcc\xf6\x06\x12L\xeeU[\xc9,RQd\x91Wf\x16\xf9fa\x16\x876$uW\xc3\x9b\xce\xf1\xf5\xdd\xa17X\xd4e\x13\x8b\xf9\xe6\x8a\xea\xdcm\x15\x82%\xa5$\xed\xf3\xd6$\x13_\xe2y\x003\xd8\xe6/`\x02\x97\xf5\xd7\xd7\x9c\xbf\xe1!!\xa30;f?\xd4\x13\x98\xc0\x05G\x86\x8b&m\xef\xc6p\x1e%@\xf3\xcaz\xba\x89\xcd\xba\x18\xad\xe7D\xe5\x16\xe1Rx`W\xa5\xf9\x83*\xf4\x85'\x93*\xb8\x1ez\"\xb9U\x95\xca\x83#p/0\x91\x8b\xaen\x1aqm\xc6\xbf\\\xa0j\xea\\\xcc0\xeb\xe2\xe0b&\xa4\xc1K\x9dO a\xc0\xebsK\x1f\xf2\xe9\xf5y\xcd\xca\xc0)\xc0\xca\xe5\xcb\xe9\xa3\xc3\x94O\x04\xd3\x173\xf4\x97,\xf7WA\xe6g,\xf7\xdf\xb3\xdb\xcc\xa7<\x1f\xbe\x98\x8eO\xb7\x0f\x1c\x99\x9e\xce\xe7\xa3\xe9&&\xe0\x16\x82\xbcnZ\xa8\xacu\xb2\xc1 \x8c\xe1\x84\x9c\xcdq\x03\x1c\x1c**L\xa4Em]}\xc3K:{S\xa8uN\xb4e\x16 \xbe\x9e\x9cn\xa1LA\xfa\xd5\xc2\x8d\x0br\x8e\x06\x07\x1a\xae:\xaf\xb3\xab\xec*\x0f\xd1\xc5\x8c\xab\xec\x05\x05\x1frr\xed[\xd5})\x0f\x15z{R+W\x15\x89=\x9f\x82H\xcd\xcb\x8b\xe0d\xe1/\xcc1\xf1\xf6\xb2t\xdc&\x9a\xd1,\x06\xbc\xb5\xfaPjP<&(^W\xcd=dIY\xfap\xed\xf9\x90\x95G\x1a\xe3\xadOe\xf0\xf1|\xd8\xb8b;n(G\xd3\x85\x0f\x89\x9b\x0c\xfe\x03z\x90\x0c\xfe\x8a\xff~\xe7\xc3\x8d\x9c\xf9\x9a\xb3\x90\xb3\xc9J\x98\xa4\xcd\xb0\x16\xa1\x1eTy\xaf\xec#\xe72=O\xb5\xe7\xc3\xfe\xf4\xa7\xa0\xff\xcb\xb0\xff\xe8]\xff\xab\x7f\xfb\xe3\x9d\xaf\xbf\xe9\x0d\xde\xfdt\xf1\x7f>\xfc\xf7\xf9~8\xc8Y\x86\xb9\xd7\xcc\x81Wd\x82\x97\xd9*H\x83Y\xceR\xceW)\xcd\x00,B\x16\xcd!\x0e\xd6\xc6\x9c/\xca\xfa\x94'?$\xd72\xaftyq-sn\xb6\x84t\x9e6\xeb\xd4\x99\xc1\xf1\x11t'$#p\xc5\x98u\xa4\x95\xac\x82\xd6\x10\x93Iv[\x957{[\xfc\x99'1+9\x88\xb5$<\x11\xb7\xa2\xccI\xac\xc0\xa8\xe2\x99\xdf\x1a\xbcF\xc4\x80+i\xc3rS\xb2\xb0\xd6\xb5\x92\xb2C\xbd\xdf\xce\xd9~\x0d\xde}\xa0\xa5\x02\x14\x97sJ\x19\xf2\x13\x0c\xfd\xb1S\xbe\x0c2\x1eQ\xd6bs\x82\x0c\x91\xf9\xbf\x1e\xcd\x14\xbd\xeaL\xddu\xe9\x8bM\x87\xe7>0c\xe86\xadG\xdc\x03q\xee\xb6d\xb9\xe6\x1e\xf7\"X3\xae\xfd\xef\x90!\xaf:\xd7\xa9)\xab\xdcGS\xe6B\xdb\x1e\x19|\x13A]k\x90\xd9\xf8\x95\x04-\xb2 \x0dR\xc6\xe7S\xcd\xdb\xf2,JY0\xbf\x05\xfe\xafc\xba\xcc\\\xc9\xef\xdfi\x80\x06\x7fF(K0\xb5\xd4LM\x81\xec\xd8\x8eY\x93r\x97\xcf6\xdbF\xb6D)x\xff}\xb7\x8c;\xb1\xcb(aZw\x1bO\xa7\xa52\xf8n\x82F\xf1\xf8Z\x15\xb9\x97\xcdT*FW\xa9\xdc\xce?\xf2\x01\xdf\xddg\x99\x96\xac\x96\xdc}:\x8d\xd0\xe0\xc7 \n\xda0\x86\x8cvCP\x04\x9f1\x8cE\x9fQ\x91\x8f\x98\x03\xecm\xce~\xa0\x0b\xbb\x0d3\xc8\x18\x81\xae^\xd5C\x15\xfc\x12'\xd4i*QS| \xc4S\x1d\xd6G\xd54\xdf\xad\xa7E \x0f/JY\x05\xe9\"UC\x12\xa0\xd0\x9c\xdd\x81yZ\x0eE\x91\xd9\xdc\xa0\xa6\xcbG\xf9\x05\x16\x89\x8e\xbe\x8d\x92K\xcd%\xbf\x9a\xecXo\x9f\x17\xed\xdc\xbeL~\xcd\xfb\x90\xe1g:\xf6#\x8bw\xeeK\xcf\x7f\xce\xfb\xab$@\xef\xd8\xad\\>u\xc1\xa2I\x86\xd0z\xd7\xd2mC)\x87\xd4\xba\xd2\x81\x86[\xe8\xf7\xc9\x04\\\xca\xec\xc0:4\xc4\"\xb7\xb9;5\xd6\xb79\xbdB{\x00\x03\x90&\xf1\xf2\xc8?|\x80==S\xb5}\xcd\xd0\x00\xb3\xac\xc8\xb2\x82U\xe8\xd7-\xbe\x95\xb3\xe15\xdbr\xab5\xac\x92\x1d)\x84+hm\x0b\xab1\xa7\xe5\x83\x05K\xf9\xdffI\x9c\xb38\xef\xd3\x10\xf1\xf8\xd6\x12\x04\xadT7\xab\xd5\xf5\xc1\xc9\xd9M\xbe\x8f\x01\xa9\x1es1>c\xf9\xe4\xed\x9b\xef\xfa\x0f1\x04W\x05\x8b\xe4\xe1\x98z3\x10W-Z\xbb1T\xe3\xed\x7f\x0e\x12\xa8\xd14N/\xd8l\xa2\x90\x92<\xee\xdf\xf4\xaf\xaf\xaf\xfb\x1c\xc5\xfb\xdb4\xa2\xe8\xfc\xf3\xea\xac\x8d\x12\x8c\x96a\x8d\x88)\xd1\x94V\xfe*\x8d&!i\xcc\xe6\xfd\x0d)d\xb4\xe44\xf6B\xe5E4\x88AY\x12]\xb1j\xb1.\xedi\xd0km\xb6\x15\xb7;\xf5$\xa9\xa4\x01\x0bg\xc9\x96k\x8cI\x8e\"\x9b\"\xbf\x98t\x17\x82\x0c(\x93]\xa3e\xa2\xcb\x989\xb6\x9d\x9b\xb7\x99\x04\xda\x12&\xb7nq\xc9\xaaY\xa5\x04Gr\xe79\x8e\xda\xf7\xa9\xb4\xfc\x02\xeb\xf8l]o\x90\xafXl\x8aM\xfdQ\x92\xdf\x9c\x88G\xeb8\x7f\x13Pl\x17\"`G\x11P>vQP>\x15\x91\x90o\xb3A\x16\x94\xcf\xc7_\x0bM\xba-A\xc9\xf3\xbe&\xfd\x91\xbfzaS\xcde\xdc\x17\xf2\xba\x1f\n\xaf{u\xb5E:\xdf\x9f+\x1b\xc7`\x91&\xeb\xe3U\x90\x1e's\xe6\xe6\xd3F\xd6+\xb5\x17J\x99`\xcbk\xfa\xd1\xb2\x10\x9dV\xee3\xd0\x9e\x03\xf8\x8eh_Fv\x0bE\xd7E=\xaa\xb1($\xb8Vt\xd2\xd1>\xc7\xf37B\xd5E\x03\xaa\xfe\x9c%\xf1\xe7\xb4\xfd\xa7\xb3\x97/(\x06\xaf\x95z\x95\xde\xdb\x94\x85Y\xab\xe7\x0f\xf9\xf5\xd1\xfd,\x0fU\x87\xfa\xfa#\xad\xd0\xad%\xc6\x08\x94`P\xdf\x8d\xb6\xc4\xb2\xba\xde Q\xda\\F\xf9T\xf1\xcd\xac\x94)\x95\xe9\xbf\xb9\x1a%\xe4\x83\xc2Gv\xa5r4\xc7\x98\x8f\\e\xd7\xf5\xe4NQ\xd6VlL&p\xa5\xf7\xc9\x9c\xd1\xdbd\xce\xfcR\x82\x18`\x9a$\xcc\xbb\xc2l\\z\x06\xf6\x8a\xbd\xc1\xb0\x87\x9a`H\xb3T\x06K\xf3\xf2\x1bf\x9f\x97~\x7f\xf8P_\xa1\x0f\x1f\xc0I\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedf#\xed\xbe\x8d\xc8}\xabe\x1a\x87\xa7\xd0\xa7{H\xa6\x8c\xdd\x1f\xdd\\\x0eFN\xbc\xf8\xe9\xfc\xc7\xc9\x1b\xc7+\xefcN\x7f\xa8\xc2\xe2\x07\xe5\x9d\xc1W)[\xb04EI\x80\xde\xba\xd8\x0e\x99V+\x1d|\x7f\xf2\xecy\xed\x0b\xf9\xcbZ`\x1eUoN\xf90&4\x9b#[\xf8\x8f\x937\x90\xa4\xc0[\x939\x873\x13B\x10\x91\x93\x1a|5\x8e\x8f\x0d\xf7\x17\x1d\xac2\x82\x0c6Y\xed\xd3p\xedz\xf2\x8c\xfe\x8ec\xb0\x1a6\x9a\x11Z\xc5\x03B\x1e\xd1~cxb\xfe\xe0\xf6H\x0b\xba\x96M\xa5\x87YT\xa0\xad:\x1e\xdc \xe67q\x8c\x0d\xd8\x99g.-\xa0\x14d\xf8\xed\xeb\xd3\"&\x19\xd7\x91\x0d\xaf\x93\xeeq\xe1:[\xb77\xed\xfb\x9a4l(\xad\xf4\xfe\xbb\xf4\xe8]\xbc\xbf\xcbn\xd66k\xdc\xb4\xda\xe5\x8d\"(\xb2\x8b\x0f\xdd2\xda\x8b\x8d\x1b;\xcd\x0d\x82\xbeWi\xed\x0e\x82|>g\x0f\xe6V\xbe\x9a+_\xfa\xbf\x17\x82\xbbH\xd0-\xae\xeeI%\x99R\xd5SXs\xfe\x17\xe6\nC\xf7\x0d\xf9i\x0c\x07\xc3\xa1\x8c\xfe\xfa^\xfa\x85\x88\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\x8a\\\xf8E'\xfcW\xad#,2\x06\xe7\xe5\x9f\xe5\xd8\xec\xbc\xc0\xd7\xb9V\x8e\xffc\x8a\xfc\xaa\xa1\xb1j\x17)/7\x1axDZo\x1b4\xaf\xac\xc7n\xba)a\x0cN\xc5\x92\x80\xd3\xb3\xe4Q\x92\x07Tcp\xceD\xcc\x88P\x06\xa6\x90\xc7T\xf8\x8a\xbe\x9a\x1b\xe1\n\xdb\x18\x9cB\xa1\xd1\x1a\xe1\x1aF\xf1\xb3U\x00\xe4O\x9e\xde\xb6\x98\xc3\xb4C\x07\xde\xbe_=\xc3\xd0\x9f\x8f0\xc3\xe0\xd4\xcd\x94\x174\x97\xca\x91\xbd-3\xe2T\xa3\x1f\xcbGJ\xd5|m\xc4\x9fM{\xa1\xa9\xdfcp4E\x83*\xd5\xd1\xd3,\x1a\x95\xcc\x84\x1eB\xce\x15L`\xaa\xe2\xd5\x9cJ}\xeb\xdc\xf1\x8b(6\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83%,A\xfbuP\xf9H^\xc0!\x94o\xd2_Bv\xdd`~3\x03\x81y\x10*[\xaf\xb0\xd2\x86\x19o\x9cS\x88\xdd\x87v\xa5\xc4\xc1\xd6\x10C$.\x9a\x07Z\x87\x9aDk\x89\xd8D\xc5 \xd5\xca\x8eP\x94D\xb5\x9d<\x83\x9a\xae\xde)?\xbeu\xb0\xb1:Di\x05`\x82\xa7\xd0\x18\xfd\xd4\xc7\xe8\xa706$\xff\xc1 ^\xc5\xf8\x85\x93z\x97\xad\x17EU\x97|\"u\x9f\xf6J\xfbK\x96wj\\m\x9c\\ b#\xe4f~T\x9a'\xa5{l\xebx\x154\xfbFU:\x96\x1d\xd4\xc2Bs\xe8h\xeb+\xabL\xb2\x01\x02\xca\xab'\x80\xa0\xad}\xe9\xf3\xdb\xe1\x1a\x14\xd4\x02\xdc\xc8\x1e=\xeb\x1c)\xdc\x8d\x88L\x95\xfb\xc5\x18\xe3st\xfc\xcak\xa7\xf2\x861b\xd0\xb2\x0e&\x0bi]\xb4\xe5\xfb\xd3\xf7\xa3\xee`\xd0\x92\xea\x8d\xc9\xc8lfT\xc6\x8b\x89f\x93\x88>\x15\xf23\xfe\xf5'\xd3a&\xb6\xd3\xfb\x8e3\x11\xae\xd2\xbf\xfeT\xba\xed\xcb4\xae\xdf\xf7\x92O\xd3\x94*\x8eA\xda\x0cM.B\x86\x05\xb0\x9c\xc5\xdf\"\x9f\x7f~\xf2\xc3\xc9\x9b\x13\xce/\xb9\xc2\xee\x0b\xf5\xdc\x07\xe7\xe5\xab7\xa7/_\x9c\xf1?_\xbd<\xc3O\xaf\xde\xbeq\x0c\x0b4\xd3\xba\x9c\x89\xf4\x17\xad+\xaeIe\xd2\x13\xdc\xbe\x82\x97\xc9\xfcV?e;\x8dC\xb3+\x96!\x16\xf5G\x1f\"Bnm\x9c\x9ez9/\xbd\x9c\x7f\xe6T\xe6\x95\x93\xff\xa6I\x91\xf60\xd6\xb5\x0d\xbbFX\xa8\x1a\xe3\xaazb\xaf$\xb4\x8e\xb1TC4\xdd\xa4A\xf9\xdc\xa6\xd1X\xa9);\xac\xf2|\xd0\xd9\x9fF\xa2\x1b-\x19Y\x8c\xda\x17\xca\x90D\xb7\\\x84\x96\xc7q,\x83nDm\xa6\x14M\x9c\x85S\xf5|\x04\xb34$/\xd5L\x0f\x87#\x1f\x0e\x87\x07\xfc\x9fC\xfe\xcf\x03\xfe\xcfC\x03\xba\xcc\x07)\x9b\x87)\x05\xd8\xed\xc4\xd2\xb8\xa0.RK]jV>\xddZ\xf6:\x88\x97UwS\xa1\xd4\xce\x92\xb9\xf5\x80\xbe\x04\xba]\xf7\xa1\x0b\xc45+OQBG\xcd&\xeb\xa4|,\xea\x93\x11\xf4\xd8\xe0{\xa5r'\xfc\xe7k2\x08\x02\x86^\xe5?\xb3M\x12g|{\xe7\xfc7j\xce]W_[\xa3\x9a\xd3Z\xd3%\x17\xd0\xad/H\xe6\xb0\x97f\x1b[(\n\xff\xe7\x8f?|\x9f\xe7\x1b1\x0f\xbb\xa9&\xdc\xd13\x0e4\xeck.\x05\xda\x8e\x87\xad\xf4\xa9\\\x83\x101\xecD\xa4\xe4\xa3@g\xe3bN\xa7gQ$\xb7Ml\xae\xeb\x91\xb1\xc4\xee2\x02f$\xd5\x1f&\x8c/N*\x1a\x7f\xfb\xfa\x07G&\xa2\x0f\x07\xda[\x18\x03+\xfb\x93\xfcg_\xecG\x9fWj\xf1y\x91&E\xd5\x91\xa1\x17L\x0f(\x7f\xf0ejn2v\x05\x8f\xf1\xc1$\x97\xcb\xe7\xa3\x8f`\xd1\x99\x1a\xcb'\xad\xba\x86\xbe`\xf9u\x92\xbe\x97\xe6uX\x04a\xc4\xe6&\xdf\x0f\xf9\x88\xaes\x8a\xfe\xfd\x0f\xe9|\xc3b7Q\xc7Q \x9d\x7f\xe1\xe5&'\x8cg\xd1v.\xe2\xd4%\xa5\xd3\x1e.Y\x85\x18\xa5\xec\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|0j\xe4 c\xf1\\\x0f\xa6\x9ah\x87n*}\xa0\xf6\xd2&\x95\x9a\x89-\x92\xcf\xc1&\x89n\x17a\x14\x99\xbc\x82\xd5_\xae\x9e\xc1\x163[\x90lQ\x8d\x85\xf6\x07\xd1xiqv\xbai\x94\x9bn\x19\xdd\xbb\xeb\x0d\xc8\x98b\nd\x1b\x1a\xb7\xc0lQ\x14\\\xc0pLQ5\xd5J\x13\xa2Q'\x10\xcd\xa4*\x8d\x9b\xf4\xc6\xe5\x03\xd1|\x13m\xeb\xa9\xfe\xaa\xb6\xd0\xc6\xcd\n\xb5\x18\xef2\x89\xec\xdd\xf2`W\xf9Ml\xe9\x9eQF\xffE*KN\x910\xdc\x9a&\xe7J\xc4\x1b\xcd\xe0I\x11N\xfa\x88k\xd6\xc2\xbf\xe2Y\xee\xa2s\xfd\x8b\xe0E\x9d\xcee\xd7!\xae\x9a5\xdb\xfd,\xc8\x18\x0c\xc7V\xc0\x97\x0dX\x8f\xd7\xe5\x83\x0d\x1d>\xb0\xb7$\x1f-\xd9\x80\xb8z\xd5\x10Y@>\x98\x86\xad\xb9\x18\x0e\xe0\xeea\xfb\x00\xf0J\xac\xcb\xd7\xf4\xf0\xa0\x85\xdb\xc8\xc0\x86\xadm\x06\xd3\xa8\xd73'\xea\x94\x8fY\xf2\x82\xe6\xc9\xe1\xa4F\xf6\xfe\xb9\x0c\x1b\x92<6\x83\xa7\x13\xb8\xfb\x90On\xc6!\xeb\xde\x03\x0f\xd7z\x06}\xb8\xfb\xd0>O\xe5\x95\x8b\x0d\xdc\xbf\xa7\x1ax0,\x1a\xb8\x7f\x0fz0\xb2\xdc\x10\x86\x1d\x1ch\xa9\x97G\x0fT/\xa3\xe1Ac\xf0<\xf9\xa8\x15>|\xe0k\xcb-p\xab#\x045\x96\xb2o\x10\x08\xb0\xe5+\xf1\xe8\x01\xae\xc4'l3\x1f\xe8\x81}\xa0mPp\xd0\x0c\x05\x82\xc4\x98\xa0 \xfd\\(H\x7f\xe7P\x10\xea\x10\xf1\xeb\x83B\xfa\xd9\xa0\xa0F;\xba\x0f\xdf@\x0c=\x93Q\xfd\x0f\xf6_\x82\xdf\x05ER\xe2\x08\xfaz\xea\x94\x8f\xbe\xc6\xca\xf8\n\x15\xab\xa2XVP\xf2\xf2;\xb8w_2\xaa\xc7\xb0\x85'pp\xef\xfec\xe8\xf5\xb6\x1e\x04\xd3-\x86#\xfe\xa3\x03=p]\xfeqt\x1f\x8e\xc0\x19:\"]r\x0f\xb6\x05\x97\x1d\xdd\xf7<\x9b\x87\x8d\xcc\x9e\xd6hFo\xb8E\xd9\x9b\xf0\xfe\xca[\\\xf2ft\x9cR\xceP\xe1\xac\xc8\xb4T\xc5F\xcdRj\x94%\xb6j:I!\xf0=<$\xf9\x8fkNw\xefi\x7f\xdf/\xfe~\xa4\xbd\x1f\x1dh\x1f\x12\x0e\xfb\x87\x8f\xf8\x8c\x12\x0e\xfbw\x0f\xd4[B\xdc\x84\x10W\xbd%l\xc4\xb7\x8f\x86\xea-a\x0f\xbe\x1d\x1d\x1cX\x04xtd\x80>\xc4*\x1dh\xce\xd7P^(BE\x9b\x8b\xd3|K\x0f\x1e\x12\xbdO9T\xfb\x80\x05\x83ib\xb1\xdd*\x82\xc1\xeb\x1e\x0c\xef\x1a+\x8f\x1e\x1d\x00\x0e\xf7)\xdc?\x87\x1e\x7fs\xf0\x10>\xc0\xfdC\xb8\x03\x9dZ\xbew\xef\xe0\xd1}5\xe7{\x0f\x0e\xef\xde5utppWv4:\xd0{\xa2\xbe\xe1\x0e\xdc?\xdcm\x00\xcd\xd6\x87\xb0\xc1v\x80\x10\xd2\xeb\xe9pW2*\xbd}}*\x94\xb1\xb7\xafOa\x1dD\x8b$]3\xab\xdb!\x08\xfb\xc5hx\xc0\x07]\x81P\xdf\xb4\x18w\x87\xf0\x81\x12\xc5\xdd\xbfw\xef\xf0>b\xad\xa8\x9ex\xf0\xe4 \x8cx\x81\xd0\xf3p\xbd\x1e\xd6\xd6ktP[\xb0\xe6u4\x0e\xbc\x03\x01+\x02\x890\x8c\xfbT\x12qs\xe8\x15\x80\xea\x95c7\x96\x15\x95\x96\x88\x05\xd4\x97\xe5\x8e\n\xef\xd8\x94\xb9\x85#K\x98}\x17\xc6!E\xe4:\x02\x87\x93?,~\x99$\x11\x0b\xe2zSG\xe0\xe4\xe9\x96!Y\\\x04QF\x7f9\xfa\xb8\x0b:,\xf5\xa5hw}\xc9\xae\x1e5\xc51,8\x02F\x1e\x18vQ\x87h\xd1\xc2\xc5-&\x0c\xa4[+U\xa5\xc8\x9c\x0fX9\xf1:w\x04MF\x87UgR\xb9ht\xa5\x12\xfa\xd2\xd8\xca_\x89\x0e\xd8\xa2\x18%bD\xba\xe6H\x96\x03<\xb3\xa9\x7f\xe4\xf8B\x99b'\xf6d>\xa6%,qM=\xe3\x83\xcc1\x1c\xa8\x88$\\\xbd\xdbrvL\xd9\xf29GZ\x10+Z\xc0\x13\xd8r\x1e\xb4h2\xe1S\xaa\xe1EC\xa6\x879\xa5$n\xc9\x16\x11\xba\x19\xe6\xb7\xedU\xd3A\xca\x87\xafm\xf9\x12\xf8\xbcQ\x08Skp\x05\x13\x98\xab\xf9\xaea\x02W4\xdf%\xcds O\xe0\x8a\xcfs\xe9\xc1\x8c\xd3\xa4\x15\xf4p8\xf3\xe9\xf2\x9c\xf3\x1b^`-\xd4\xb0\xde\x04\x9a.V`\x08+\xbep\x91^\xdeLp\x88r\x97{\xe4\xdd\xb5W\xaf\x8bj\x02gf\xedDL\xc7o.v\xa1\x8f<\x024\x995\xbe<\xba\x04\x86\x88_\xa1-\xea\xc6\x87\x0f2[\x8fdFJ|,\xb7`\xa8\x9d\x17\"CM\xec\xba\x12)\xf1c \x08\xb5%$\x8fp\xdbW\x8e\x1b#vXn\x94P\xbdN\x8e\x93\xc1:\xb8\xf93\xbb\xcd\x94\xee\xae\xde\x18\x86\xc5\xd1m\x04\xfbU\xb5p\xa6\x84 ^`f\xa8\xb8\xc1m\x93T\xd2443\x15\xaa\xdb\xaf\xb0\x9b\x0d\x8e\xb3\xfe\xd1&\xc0r\xbc\xde m\n}D\xe1\xe9\xb9\x8f\xc86$,\x1b\n\x0c\xf3\xf1\x94\x99\x13\x96K\xf1\xff\x05\x9d\xc1\\\xd3\x7f'T\xe8\x86\xb0\xf1\xa6\"\x00\xdf\xd8\x04\xe0\xb3\xaa\x00|c\x11\x80\xcfp\x8c\xb9^tm\xa5\x1c\xbc\x82\x18<:]\xb9\x87\x0f\x10\x1c\xcf\xe0\x08\x07:\x821\x9c\xa8\x9d9+\xc4\xe0\xb3B\x0c>+\xc4\xe03RJ\xd5[\x12\x83\xcf\xa4\x12 G\xc0es\xe8\xf5(\xc2\xda5Y\x9b\xb1\x8f \x86\x91\xe6\xb4\xc7j\x0e\x035CJ\xba\xa2\xcdp\xd9\xaa\xa0\xf2\x8a\xbd\xde\x12\xabn=\xb8\x82'\xe0\xbe\x87 \xdc@\x1f\x96\\B\xa38\xd5\xb7\xba\x04~\xe5\xc3{N\xa2\xc4\x96]a\xf1^\x9bIl\x96\xc4y\x18ow=\xe6\x03\xe1\x0d7\xe4\x00\xf3\x9bo\xc5Ee+\xcc4\xdc\xf8\xf6\xee\xa1\x18'o\x077\x10\x8e\xc0\xe5\xebz\xa5\x86[]\xd6\x1b\x0f\xe3\xa9q\xd2\xf5\xc7\x83\xa1\xc0\x11\xea\xbfR\xf3\xd2T\xf3R\xaby-\x8f,\xd4\xf6\x188H\xa1\xb7\xf4zk\x1cn\xd6\xc4\xe5\x8f}\x90\xb0\xb1\xb6o8oN\xce\x97\xc3\xd3{\x1b\x04\xc1X\xfb^\x9d\x10B\x98\x8c\xf88\x81\xc8\xbd\xf5a\xc3\xdf]\x8b\xe2\xfc\xdd\xa5x'\x8e\xc4W\xeaH\xfc\xd6\xf3 \x98\xde\x9ec(KXMW\x82\x96\xf0\x17\x86\x9bY 4(\xf7\x18\xe5\x98\xdbsO\xbf\xa6\x85r\x06\x1c\xc1\xf1\xf4Xk\xe6\x12\xc6\xb2\x8b\xe9\xb1\x0f\x97\x16\xc5\x8c\xaf\x06\x06\xf5\xea\xf7\x17^\x93\xc1\x8cou\x99\x16\xdeb/D,\xd5.\x12UE\x8c\xa8\xef\xe7\x1f\xec\xbf\x16\nt\xaet\x95\xe5\xc3\x07X\xf2/^\xfd\x93\x0e\xb7\xe5\xdd\xe3;\xb7\x86'\x90\x19v\xce\xfb\xcc}\xe3Hb\xdd9D\x84\xcf\xd9\xa3\ns\x90B\xc5\x1f\xcak\xd69\x93\xc1#K*\x83\xc3\x87#\xaf\xfdtO\xba\x13\xc8\xebpp\x04\x7f\xffH \x0dAB\x8b\x91\xeb\xc7e\x9d2]\xea\x03\xaeF\xd5\x13\x03\x1e\xb6GI\xb4'\x85HE\xa7\xad~p\xa2|\xe2\xb2Z\xfa\xb3\xd6\xc8p\xd69\x8d\x0e-s\xba[M[D\x81\x05\x1f<\xea2U\xc3\x0cJ\xfaT\x7fD:\x94\x12\x16Qt\xfc\xfbG.\xad\x04\xa83\xd9D\x16\xbc\xf01\x0d,\x9a\x10\xe6\xe9\xe3#\x88\x0c\x82L\xec\xce\xf8\x07\xa0\x98\x81>\x84nDA:g6\xbd\x18\x8aU\xcfv[`\xf3\x19\xeb\xfe7{E\xdb\xdf\xc0,I\xde\x87L\x7fs\x9cln\xd3p\xb9\xca\xdd\x99\x07\x07\xc3\xd1A\xff`8\xba\x0b\xaf\x93u\x10\xc3\xd9*\xbf\x8d\xd6A\xdcT\xe1\x1e\x1d\x9e#\x0f\x99\xa3*O\xfcf\xc4\x99H)w\n\xc4\xd3\x0d\x95\xc3?&\xb0u\xe7>d\xed\xa1)M8SI\xe4\x9d\xb14\x0c\xa2\xf0\x17\x93~\\],E\xa0\xc4v\xd7WZ7O}\xf8P\xbdm\x88pY\xa8n\x05d\x86\x16\xc8L0\xa9\x1e\x88\x06\xc3\x0cB\xf2\xfe\xab\xee2\xeep\xd0\x12\xa8R\x81y\x1c\xac\x9b\x1a\x93\x1auX\x8b4A\x07|\x18\x9e\x9b\xfa\xda\xb6\xf6u\x15D-]\xe1uu\xe8\x813q\xa0\x07\xdbz\x8f\xc2R\x06)W\xb5\x9f-\xadW<#(\xca@\xdft\x18\x8b\xc7\xd4\xd9\x8b\xe0\x85\x1b\x99\" \x89\xaa\xd9\n\x831 \x0dxA&\x00\x03\x14g(\x98?\x86\x1f\x83\x9b\xfe\xb3%\xc3\xc1\xff\x18\xe4\xab\xc1\"J\x92\xd4\x8d\x9a\xa87\x1e\x87\x0c\xe6\xc9:\x08\x8d=\xe8o\xb0\xd7\xe4\x15$'(\xfa\x98\x9cUe\x9b\xea\xd3\xe6\xdd\xe0D\xc1\x8d\xb3C\x87?\x047\x9f\xd3\x9b\x90\xc5v\xe8\xf0sf\xd8\xeaF\xd4\x04\xf4j\xbfu\xa8\xaf\xb5\xd4\x81\xffj2k1L\xc9Y\xebF\xca\xba\x1aP?N\xa9\xab\x04\xfb\x8f\xe1\x9b\xfd\xf2k.\x9a\xed\xff4}\xb7\x1d\x0e\x87\x8f\xf8\xbf\x07\xc3>\xff\xef\x01\xe3\xff>\xa4\x1f\x8b\xc5y\xef\xdf\xf6M\xc7c\xdb\xdf\xeax\xac\x1a\x93\xb9\xfc\xd7'I\xf8\x1dC\xaa\x8b\xfek\xcb\xeb2-\x1c\xc4t\xefk\xd7\xfb\xe6|\x7f\xd9\x16\x8b\\\x1eK\xa0\xbbF\xc9\x9e;\xf4J^\x1ae'\x8d\xf2\xec\xdb4H\xbd\xe3n\xb3,\xb9i\xc8\x1c\xf32+\xb2\x92\xc7c\xbb<\x9eV\xcd\xd3\xb1E\xe4N\xd1U\x00\x1d\x07\xee\xdc\x81\x14m\x97\xf7\x0fG\xe8q\x11C\x0fF\xfa\xc9|\x83X^s\x08\xc1\xca\x16\xc1\x9a\x0e*\x9fbW\x07h\x1c\x12n\x1c\\un0\x1c\xcb\xe3\xcf\xd1\xf0\xe0.|C\xde\x1a8v\x0fz\x90\xf0\x1f\xd8^\x8f\x8e\xf2\xed\xe4'\xa7\xebp\x07w\x87ey(\x84}\xb8\x7f\xb7\xf8\xc7\xf3at\xf0\xd0Z\xc6\x83?\xc2\xfd\xbb\xd62\xe5\xcf!\xfeB\x1f\x84^\xa3\x1bg\xa3\xbd\xban\xf25\x9c\xc6Qh\x89\xbb\x0f1B\x04\xcd\xf4\xe0ny\x84i\xf3$S\xc3\x04R\x9a\x00\xe7\x97\xbc\x03\xfeR\xb5?zt`l\xa0^WTH;\xd8\x0d\xda\xd2O\xea\x90\xb2gw\xf3\xe7@\xc3la\xf9\xedF\xb2J\x91\x86\x0b\x96(\\\xa6z\xfe/\xcb\x19\xb2\xc4\x93\x86[d\xa1\xddAs\x9e\xb4`F\x80V!v\xc3f\x8d\xa9\xc5\x94\xb62\x99L h4\x0d\x83\xd2\xcbCx\x02\\\xbao)\x9c\x90S\xcd\xf0\\\x19\xa7\xc2^\xcf\x0c\xc8p\xbd\n#\xa6\x14'>\x14s\xbb\xd2v\xc7\x81N\xf3x\xe9\x8f\xcc\x19r\xfe`\xdfIK\x8a\x00\xd0\x9d\x04\x85v\xbaS\xbb\xc2\xach\xa3\x8eZz\x8d;\"\xbd\xc1\xd4\x99\xfet\xee\x9c\x97\xcd\x07d;\xe0\xa2l\xcd\x9e\xa3\xda\x12\xa4\xbd\xed\x92\xf0\x0ea\x81\xb0\x1a!%\x1bd\xc96\x9d\xd9\"Fx\xbe,\x18\xca\x82\xe48\x98\x0efI<\x0bD\x10Gv\x0d\xaf\xd9\xf2\xe4f\xe3\xa6\"\xe0\xcf\x07\xc7\xab\x99]\xc1H\xba\xd8`\x11\xc6\xf3\xe3U\x90\x9e\xc6sv\xd3fB\x93\x0f\x87\xd1\\\x87\x0f\x85\x89\xfd\x86\xb3\xa22\xceZ.>\x95,i\x89\xeb\xf9\x02E\x0b\xd7\x98X\xa2\x1c\xda\x1c\xdcx\x10\x05YN\xc3\x7f\n\xb9\xf7\xd8\xe38\xd0\xb8]\x86\xfc\xcc\xbeX\x8aoos\xb6\xd3R\xc8\xd9\xf0\xd5\xc0\x1b\xb4\xb4 \xe4\x95\x858\x83\xf5q&\xe6x\x8b\xc4\xc5\x9fu\xbe\x1a*\x17\x87n\xa6\xebc\xa6j\xf6\x0d\xe0\xd2\x0c\x9e\x88\xc6\xc6\xbd\xb3EY.\xe4\x1b\xe5\x98\xc9\x85\x8d\xea\x89\x88\xfe$\xe8t\x84\xfb\xd4\x92~KQ\xc6\x84\xeb\x8c\x94)?\x99\x0e\x8dq6tyg\x97\xd5j\xbd)\xa3?r\\Hc\n\xdc\x92(\xe8#\xb50\xee%\x7f>\xb6\xedA\x8a\x06W\xd9\x8b\xf1^\x0c\xd8D\xbc\x96\xa5$\xa9\xf2\xc9\x84\xbcA\x92B\xb4+\xcd\x89\x8f\x15}?\x87\x9e\xafdN\xe95\xca<\xa7\xd0=\xa8\x07\xee\xa2Q\xe0\x10\xde$\x9c\xf4\xbdJ\xc2\xb8\xc5\xe6!\x9f.\xb6\x0f\\\xdb\x99lW\xae\xb1\xc6=DjIU\xc4\x13\xd6\x12\xa1~j\xef\x1b\xa7o\xe1\xfajBo\x84\x85\xe8\x8bM\xac?\xb9\xcf\xd7\xf2\xf9w\xdf\x9d\x1b_\xeek\xbb\xfeQ\x1c\x16t=\x13\xf8\xba\xdf\xef\xbf\x8b1\x00\x96\xb3\xca\xf3M6\xde\xdf\xdf\xb0\x1c\xf3\xdd\x0f\xb2\xeb`\xb9d\xe9 L\xf6\xaf\x0e\xf6\xe5\xaf\x9f\xb3$v\xde\xc5\xf3d}\x11\xce\xc7\xe0|%>\xf4\xb7\xa1\xf3\x8e\x0e\xc1\x82\xd2>\xab\xa60\xf2\xc15-\x07\xf4a\xe6\xc1>$\x1dg\xa5?ie{\xb4\xa3\xc0\x0cz\x10\xc17d\xee\x1d\xdc\x83#8\xc08\x0e\xdf`$&\xfe\xbf{\x17\xfa\xf4\xd2C\x95\xd2\xa6\xe0\xd8\x9e\x02Py\x17#\x0e\xac\x08\\\xdf3t\xef\xf5\xf0\x00\xf2 \x10`\x0f\x88L\xd37.\xb1\xa0\x0b\x90\xbe\xd2\x81\x0f\x8f\x1eiPo\xc7\xce\xea\xf3\xd1\x87G\x1d\x8b\x7ft\x9b\xcb\xd9/%5\x90\x84h\x07S\x85|2wK\xf1\x9e\x8dG4\xf2\xb1\x84\xb4\x93\x8c\xc8N\xa4X\xbe\xdd\x8c\xbb[\xbb\xa1h\xd4\x1571\x91*y\xeap\x8c\x8fU|B\x87\xe6\xdcS\xc6\x9d\xdck\x8a\x1d)\x1f\xe1`\xf4|\x9b\x8a\x00\x90q;\xb8\xb3\xf9\x92\xbd\\,2\x96\x9bBz\xeb\xcf'\xed[\x9e\x8c\xc1\x92\xab\x80>\xff\xd7\xb8\x89\xd6\x85q\x9e\xfc%d\xd7\xe5u6]\x9c\xad>\x92Wc\x9c\xf0o\x93m<\x0f\xe3\xe5q\x14\xb28\x7f\xcdf\xb9\xeb\x0dV\x88'\xed+\x14H\x8a\xae\xf8Z\x0f\xc2\xf6j3YM\xe2j{\x95\xc5N\xbcc\xc3Q\x02zm\xa1n0\x05\xf2\x13Xp\x88\n\x91^<\x85\x19\x1cQ\xbc\x01Z\xc91\x04\xe2\xc3\x06\x8e s\x03N/\xf9\x9b\xa2\x00\xb1\xd2\x06\xccn\x80\x81\x19\x8bs\x96\xd6\xb60\xed\xb0\x8b\x99\xdb$]\x94I\xe1>\x1c@\x8f\xa3\x0b\xc7\xaa\x96]\xe7\x85=OL\xefS\xe6\x94\xe5\xc9f\x0c\x81\xbd\xc0:\xb9\n\xe3e\xc7\x0c\xfcP\xd0\x86\xbd\xbd\xfa!\x90|\x1a\xc6\xc3\x81f,\x80\xa7\xb1\x14.\xdfX[Jca\x833N\xbdUN\xb3\xa4\x14?\x90\x7f\x9cDl]s \x04\xc1G[\x17C,\x82\xd0E\x88\x9f\xfd\x17\x1a\x91\xc5\x8f7\xc9\xa6\xcb\xd0\xd0j\xef\x9a\xfb\xa0x\xd7j\xe0\xd4n\x18/\xc5\xc8yo\xea#/k^N\xa4\\\xddd\xe5\xd2l\xde$\x1c\x92wL]\x81\x9bkIN\xa9P\xa0#\xac\x95\x978\x8cc\x96\n\x89\x01\x97y\x86\xc8Bov\x1c\xa3\x00\xadn\x8b\"\xf5T+\xa2\xe6\xc9\x86\x93 \x14\xde\xe2A\x82,\xca\xb4\xfb`\x06W\x83\xb75\x06%\x0drv\x86\x1bQ\x8b\xeah\xa3G\xd2N\xd5\x08N\x96D2e(i \xcb\xaf \x9c\x03\xef\x8ek\xff_\xbb\xed>k@'h\xec\xe8S`M\xc9\xe7\xac\x04^~' \xdc\x15S>\x0d\nw\x86/\x01/\x7f\xa8\xbct\x82\xf9\xfc\xe4\x8a\xc5\xf9\x0fa\x96\xb3Xd\x0c*L.{b\xcaq\xf2\xff\xb2\x98\xcc/\xf8\x9a\xb9%\x9ac\xbc'&E\x1ag\x15fy\x92\xdeV\xad9\x9bm\xb6:\xcb\x83\x9c\xcc<\xa2\x90y\x9d\xb8L\x13\x92 \x08\xe1\xe05\xe3\x85Qj\xd4+\xd7%\x0b\xcaT*>\x0fj\x95\xf9\xe8\x82m\x9e8\x9e\xda\xdc\xea\x82\xb8N\x94\x04s\xc7o\x87 \xeakWE\xb1ql\xeb \xde\x06\x91%\x86=Wq\x1a\x86\xbdI6\x19\xaen\x9b\xe7\xb5|\x18\x86\xe8&K\xdc/,\x16\xdc\x8cRH\x15\x9f\x12T\xf1\xc4\x8bAQ\xce\x06\xf7\xb0\x87\x97\xf3\xc40e\xb0\xf7\xc1*\xc8\x10\x92v].iUL\x06\xa8\xd0\xb8\xde\xa0\xd0\x08\x9aO\x0dZ\xedC\xd2h\xa7 {\xc9\xa4x\xf0\xed\xed\xe9\xdc\xadM!e\x0b\x99\xc1\xef+\xc7\x9b\x8e\x9a\xf2\x05\x83t\x8ek\x1b\x05\xd4\x0c\x05$L&\x850\x99s\x1e\xc3:\x88\xdc \xe4\x98D\x08\xe9\x9c5\xb5+\xf4Cx2\x81\x14\xc8 \x1d\xd0\xff\xdc \x124\xa8\xa8\xd0\xac}\xd9\xa1\xd9D\xb6\xf6L\xae\xebW2\x8aO\xe1\x86\xe5\xb8?}x\xf7.\xf34J\xe5\xbe{\x97}\xf87\xcf\xe4\xc2i\xc5\x9aY\x14\xce\xdewB\x99\xd2\xb1!\x1b\xe4A\xbad\xf9c:\x89q\x9e9\"\xd8L\x1e,_\x04k\xf6\xd8\x13G\x9f\x9b eq\xfe\"\x997$\n\xdfs\xf7\x90\xb1\x8c(\xe0\xd7\xe0z\x15\xceV\xa4&`\x1a\xc8?\xb3[\xfa\xb5fy\xa0~\xcc\xf24R?\x82\x88\x97j\x8c\xfd\x82\x16\xc86h\x94\x90\xa8\xa8\x94\xa2\x10\xf5\x08d\xe52G\x95\xdf\xe3\x9a\x91\xbc\xfa\xc4\x1a5\xd1\x80\xb6\xb9R{\xca?\xd0\x88\xac\xb8\x96\x82\\\xc7\x8d\xeb\xe7k\xd5\xa7\x94\x02pW\x90\x06\xdd\xc5\x0b\xb3\x18\xe4y\x1a^ns\xe6:\x9cv8\"\x85A3\xd9\x12\xc6\xfe\xe2\xce\xf6W\x0e\xf9\xb7n\xc9C:\x1f\xcc\xa2 \xcb8\x90\xb5\x86\xfa\x91\x06\xdf\x06\xb7w\xf9D\x0d\x840-\xdcZ\xdcQ\x9b\x89\x10\x8fW\xber\xc4\xd1j\x87\xbdB\x0c\x88\xe4\xd1J;\xb9\xca$\xac\x10q\x8c>\x95.\x01egJ\x19'\x08\xcf\xc94\xd5\x06}W\xe2\xcac'\xd6\xa5?\x15^\x02\x93\x16c\x164\xab\xd3\xf2Y\xec\xcc\x19\xa9\x16]\xff,3\x9c\x0c\xfa\xb0@/\xeb;\"x\xd9N\xb3\x94(\xa7\xa4<\xf7\xef\\\xdet\x8c>^\xfa\xf3\x11C\xbb\xa2\x94\x91\xf9\"\x83\xf4\xac\xc1\xe8af'\x16V\xf2\x07{!\xe9\x07\xa7^~t\xcb\xdea\x18\x9e\xd1\x18J-\xc5[\xad\xc1f\x13\xdd\x92\xa7 \x8c9\xac\x7f\xf8\x00\xae~\xa2\x1c\x9a\x0f\xa0;\xdd\xc9\x13\xc1\x1b\xe9\x94\xb2\xc8\xc9\xe7\x83sq\xc1\xb2\x1f\x93\xf96\xe2\x92^y_0}\xdbX\xcf\xc8\xa0\xeb\x99\x926m\xdc\xd8\xbd\xeb\x19\x02\xa8\xf0\x0f\x07\xd5\x0f\xa1\xf8pX\xfd\x10\x88\x0f\xf7\xaa\x1f\xb6\xe2\xc3\xfd\xea\x07L\xf6\xe0\x0e+o#,^MJ\x85'G\xbc\x15\x94&\xf1\x0f\xb2\x88\xb9\x87\x0f\x1fT\x1b^P\x94\x17\xcft1\xd3\x90\xf4Y?\x83f\x83b=E\x9c\xd5:\xac\xcb\x9b\xb1-\x97/A,2E\xbdX\xb1h\xc3\xd2l\x90lN\xe7\xe5\xe1\xb6;\x02\xaa\xd1\x0b\x7f:\x0b\xfe\x91\x9c(F\xe7\x89Lj6\xcf:\xa9\x9e\xf1JA\xb5\x92\x9b\x0f..0\xfd\xd9\x05\xc5\\\x1b\xfa\x18\x19R\x16\xf2<\x91#\x11K\x93{g\xe3\xc1D8\xc8\x93\xe52bg\xab\xe4:\xeeJK\xa4\xb0\x1f\x0e6i\xb2i9c\xcc\x85\xd3\xeem\xb2\xcd\x9fa\xdb-\x15b!\xb7-\x9b\x8b\x91\x97\x1cG8$\xd5\xd5\xcd\xab>\xc25;\xc3\x896\x17E\xad\x96s\xae\xd7,K\xa2+6?\xdb^\xe6)k<\x0f\xc53P\xcd?'@;\xf9@$\xc6\xa95\x84!KV\xc9\xb5;u\xd4\x0c2\x87\xec\xd9\xe7>\xec\xd9\x9c\x9a)u\xcfq\x10\xcfXt\xccE\xe2\xae[\x869j\x04\xbdo\xde\xae\xf4\xf64\x7f\xb9\xcdO\xe2\xe02b\xf31\xec\x85B\xa7\xac|\xb1\xb6b\xc8H\x03\xc5\xd8\xdf\xa4\x1c\x10v\x1a\xfb'\x80[\xb6a\xb3\x1d\x80m\x13\x98b\x8a\xea\x0fA\x1be,j\x10\x0c\x7f\xcbU\xe60\x84.\x1b\x7f!\xbf$F\xc9\xc11\x87ejs\xab\xa3M8\xb9a\xb3m\xde)q\"\xec2-F\xed\x9e\xc6\xaf\xd2d\x99\xb2,\x1b7&\xf2n\x18c\x1d\xfb\xba\x0e\xf6\x13\xa1\xe5\x8cEl\x96'\xe9\xaf\x00/]\x08\x13\x1f\xc2\xab _\xd9aK\xdd\x07\xc0\xac\xf6\x1b6\xab\x12\x15.\x9b\xfd\xe9\xcc\xf5\xe8\x12\xb1\xa9\xc4\xd4\xe1\x03Wt\xa6a\xf9\xcdt\xebW\xde\x82_\x0da\x7f\x85\x0d\xb0\x10\xf6\xf2\x1eX\nu\xdf\x06R\xd1\x9b\xb2\x00\xd6 \xc9\xc8>[\x13zZr\x8a\xfb\xa6;\x97\xb57\xca\x11\xc1\x87\xad&\x85\xf8\xc2\x07\x81OA\x7f;5\xcf\xe3=\xbb\x1d\x83\xb3\x0e6Hb\xde$\\\x8c\xce\x1c\xf34\x84\xe8\xdc\xd9]B\x1aJ\xf2A\xb2i\x07\x98\\\xc8)\x1d\x89A\"\xc4\xb4\x9c\xdc\x1d\xe3E\xb8\xcc\xbc\xb63w\n&?Of'7\x9b \xce\xc2\xa4\x834\xc2\x85G\xb6\xf9!\x8c\xdf\x87q\x8bX\xb4\xa5\xe2a\xb6\x89\x82\xdb\x97]\xa5\xa3L\xaf%R\xd9I\xff\x8f\xe6\x9a\x11\xa9\xb6\xdb\x0d\xd7\xa6\x10\xc6\xd7a\xfe#\xa2]\xcb\xeaa'OO\x16\x83\x1f\x83M\xab\xd2\xfe\xb3\xd0\xf4\x17x\x13\xfcOg^\x0b\x8b\x03T4\xc6p\xda\xdc,\x7f\xf2`\xd9\xe9\x86\x05\xa7\xdfV\xef]\xfd\xc9\xa4\xee\x91[\x14-\xfa.\xf4,\xc7\xc2\xdd\xf4g\xce6)\x9b\x059\x17\xf1OI\xf3-^9B]3\xf6\xa5\x15\xa3\xee\x9a\xccS\xf2!\x0e4\x86\xa4\xbdh\xa1\xa7t\xb8JQ\xd6UZTi\xa8\xaa\x8a-j\x19\x96\xaf\xdb \xc4\x82u\xb7X\xb4\xf7R\xd2/;\\\xf0SzU\x8b.\ne\x15\xaaE\xf6\x80\xbaN\xd9B\xf2AW\x81Z\xf4O\xb0\xe8\xc6-\xda(4\xe8\xc7-B\x12X\xd5\xfd\x16\xce\x0ff\x89\x96\x04b<\xd2\xa9}mo\xb0f\xd6\xd5\x9a\xebzB\x04P\xf7_\xd7\x1fa-\x89\xa4\x89V\xb8\xb5\x0b\x8f\"\xf7\xc7\xb6\xabb\n\x9c\xc7\xf0s\xf3\x8c\nm\xba\xcdh\xdf\x11<\xba\x82\xb4v\xb6-\x96P{\xd3\\\xb5tR)*\x97\xde\xb5U\xd7\x0eiUu\xed][uqD\xa7\xaa\x8a\xdf\xcd\xd5\xa4<5\x86\xcb\xf6\x82\x82\x95\x8f\xe1\xba\xbd\xac\xe2\xe3c\xb8h\x19y!$\x8c\xe1e{Y\xad\xe5W\xcd\xa5K\xf2\xd0\x18\x8e\xbb\x94\xd6Z?k.\xaf Och\xd9\x9d\x92\xe44\x86g\xcd\xa5u\xc1r\x0c'\x1d\n\xa3T9\x86\x9b\xe6\xa2\x8bx\x0co\xac%l\x87\xab\xb5\xb7\x1f\xcf=\xbfrO\xe4\xa3\x9b\x0d^mSfJ1\xb9\x92\xe4\x02-\x1d\xb5\xb3\xa9\x12s\xda\xab84\x16t\x00\xdd\xc7J\xdf*\xbc\xa4Z\xd5\xc4\x0c\xaa\xb2\x84\x8d\xf2k\xc6\x05\xcc\x15#&\x00\x13\xa0\\\x14\xbf7\xc7\xaf\xc8\xe6\xf8\x15\xd9\x1c\xbf\"\x9b\xe3Wds\xfc\x8al\x8e_\xfc\xc3Pw\x1a\x8a\xc8\xb9\xcb\x92k\xfa\xb7\xf6\xd9\x9a5\xfadi\xfeX&k\x8cv\\ip\xc7\xf2?\xd9\xe5Jx\x18bq\x992\xa7\x9a\xd6\xc8\xe8\xd4\xf8\x19\x07\xa7d\xa0Z\xb2\xfc\x07$t\x06)\xbe\xab}j\x17\xdbT\xbe\x83\xaa\x1c\x9b\x14\xdf\xc1l\x9b\xa6\\\xbch\x10t\xd1>\xe9\xc6\x98T\xbc\xd1y\x0d\xef\xe8\xb6\xceO\xab\x90Yd\x1dg5r\xa4O\xeb\xd7\xf0\"\x11\xdc\x03D\xf0\x19\xbcS\xe0|\x8d\xe7\xf5_;\xf0ug\xd2Z\x86\x00\x93@\xd5bg\xfc\xa4=T@a\xb3\xe6\xb6\xac\x06\xa3\xa50\\\xfb(\xcf\xa7\xcc88\xd3\x90\xed\x99\x18\x87Nwg>\xccj|\x84Z\xff\x171\x16\xcf\xfftb\x8c \x8b(\x15\xfa\xd5|a\xb0\x8b\xd3\xac\xba\xf0\xc3WL\x91_\x15_?\x82 \xe5 u3\x8fr\xe8\x0f\x1f\xc3\x0c\x9e@\xf6\x18f\xbd\x9e\x07\xd1tv\xae\xd7\x9c\xce\x0ca\x01\xc5R\xc6x\xe1\xd1\xe6\x9c\x8b\x18\xd8\xca-fA\x14 \x96\xc1|\x98\xf2\xba\xe72\xf4b\x84IZ\xc3\xc1,J\xb2N\xeeV\xc2\xc5J\xb7\xfd\xa11\xfc9G\x85\x10\x7f\xbbU\xffz 4\xc3\x8bZ5\xa6\xc77\xe3\xb7\xe0\\_\x96\xe4ub[\x1d\x0d\x9eqwcj\xba\x03;\xa4\xd3\x15\x96\xa6\x1d\x86\x10\xeeb\xf1\x0e\x84\xf1t\xf0\xec\xec\x8d\xbd\x14\xdfm\xed\x04-\x90)m\x1b\xcc`\x98\x0e\x15\xa1)\xd6\xc1\xa9\x81sS\x8aT\x87\xaf]f\xcb\xd0\xd0\xc6\x8a\xe7\xe1U\x8dT\xeb\x8f\xbaV5\x06g\x1e\x06Q\xb2\xecoo\xacWq\xbfH7\x97\xc1\xec\xfd\x1f\xea\xe57Z<9\xa5>^\xcf\xff\x8d\xfaZ\xb1`\xfe)\x9d\xad\x0e\x95\x1c\xe8<\xbb\n\xc2(\xb8\x8c\x18\xea\xfbI\x1a\xfe\"\\\xb8\x9a6\xfbr\x9b\xe7h\xe0\xb5\x0f8\xbf\xdd P\x89\x92\x9d&\x86\xfc\xa0\x8f\xd3k\xa8\x91\xc4\xba\xb9 \xeb\xec\xbc\x02\xd9\xd5\xb2q\xf4\xd7\xe1<_\x8d\xc19\x186\x0cd%\xa2;\xf0R;\x8f`\x9b\xd5e5\xfdY\xa5l1\x06\xe7+\x9c_\xc3 n\xa20~\xff}\xa9\xb0\x05y\x91\xe9~Y\x00\x9c%q\xce\xe2\xdc:\xfbh\x80|\xee\x8c\xfd\xcd\xf5\x06\xeb`S\xcaI\xdex\xfd\xb7\x85~\xce\xda\xcc\xb6\xc8~[\x0e?\x9e\x9d\xbdi=\xf0\x98\x17,\xc1\x1a\xb7D>e\x13X\xcb\x19\x96\xce\"[\x0f\x81*\xa6\xb8\x96\x93\xdb\x92\x91\xaf\xc5\x00\\1{\xd6\xdd\xa1\xe5c\xb3\xb4y\xf8\xd4\xbe}9%\n\xdf\xfeK_\x12\xcf\xbf\xf4\xa5\xff\xc5\xfa\x92\xe0|]4\xa6\xce\x97S\xf2\xeez@\\\xd7/\x06\x1a}|\x93\xa8\x83g\x9bI&\xafim\xe6\xd4\x15\xffR\xda\xccO,\x80\xac\xac\x8dy\xa4\x8b(\xd9\xedU\xb2\xd9n\x1c4,6+u{{\xbb)>\x89\xa8\x13\x14\xee\xce\xde \x0b\x7f\xb1D\x13\xf9\x92:\x10\xef\xb2\x7f\x9d\x06\x9b\xcd\xa7\x08\xbc\x1d\xe4U\xad\xb3\x04\x8e\xc0\xb9\xccc%\x113\x88\x92\xd9{6w`\\\xfd\xb0\x8d\xc5\xa7\xae\xf2\xaa\xf8\xb5\xf3\x14\xb2M\x10kR\xbb\x1c@\xa3\x98\xfe\xcf\"\xe5\xe2\x82\x7f\xa5\xad\xf1W\x1d\x96U\x13|\x1b\xea\x9bG\x8c\xf4\x14\xddkm#\x8f\x85u\xf8_\x92\x0d\xfcK\xb2\x81\x7fI6\xbf\xbddc\xbd7\xc0\x06Y\x9el8\xd4\x07\xcb\x80\xf8\xb0\x99\xff\xc8\xcb\x05\xd2z,:\xb1\x88&\xe8lop\xa9\xff\x9f(\x8e\x94\x1c\xd5?\x8dy\xef\xc6R9\n\x96\x85\x94\x8b\x0b\xceH5\x9am\xf8\xda\x81\x0b8A\x1a\x06\xfd(\xb8d\x91c\xea\x06h\x9c\xd6\x8e\xe4\xf7\x0e]}!>\xfeO\xc2\x93\xd9g\xf2\xe4\x86\xfa\xe6\x11\xff/\xb4\"\xcc8K\xad\xf1\xd4D|\xa9q\xe1PV11\xdb\x99\x89\x0bo\xc5\x87\x1a\x17\xce\xc4\x87\x1a\x17\x8e\xc4\x87\x12\x17\x9e\xc9\xc8G3\x11\xf9\xc8\xc4\x8fg\xbf=?^t\xe5\xc7\xb6\xb0EU*l\xe5\xb9W\"\xafz\x95\x98[}g\x92:\x0fl W$\x16+\x18$1\xa7\xcd\xc7\xab ^\xb6g0\x02\x8d\xcf\xb1A\x1c\xac-\xbaXP\\[\xab\xb0\xe8\xbf\x7fDL`&\xf4\xe3\xfc.\xc3\xbb\xee|H\x9d\x06S\x0fb\xc7\x1b\xa9\x1f\xdf*\x15\xca\x0d\xc8\xe3\xd7\xd2}\x94,M\x91tv\xe8\xbfY8\x08\xda\x14t\x8a\xab\xd0\xc9@B\xc1\x154\x93H\xcd\xe6\xdd\x1a\x80U@\x819\xa25 \x1d\x19\xe4 \xc9w\x96\x99\xc5b\xcd\\s:\xd3\xa0~\xec\xbe\xc3b\x9a7\xb3\xe3Y|P\x84\xfa\xe0\xbf,8\x0ee\xd9)3\xcaN\xc1?@vj6\xe2t1\xf6\xc4U\x00i\x83\xa5\xee\x87\xeeyW\x1bR\x88\x85\xbb\x9d\xd0\x07t\xd2\xcd\x91\xff4g\xeb\xa6\xabH[*Jy\xe0\xda\x8cO\x19\x15\xfe\x96d\xc8\x96\xa3\xf6\xa4do\xb2\x97\xa5\xc0\x19\x8b0\xcaY\xfaIH\xb7\xb77\xc3k?\x96(\xea\x80\xd8g\xef\x7fc\xee\xbfc\xe7r\xe5D\xd4]\xbc~\x94\xdfnXC\x8c\xd8\xa6\xc1\xcc\xbf\xcc`&;\x0c\xa6Q\x8f\xb0\xdd\xbf\xd8\xdd\x088K\xe2<\x08\x9b\x0e\xd9\xf7\xf66h\x95\xe4b\x87\xb5\xdfE\x92\xae\x1b;Nb\x8a\xf2\"o\xa5(6h\xebvS\xa6\xf6mI\x97Z\x16&\xe8t\xc2\xd9v\xba7[\xb1u\xd0z`\x18\xe3\xf2\xb6\xb4\xb5\xd3\xe9\xa6.\xc3\x8c\x81\x95d\x9a\xe6\x9a\x81vy\xad\xe5\xdeK\xf9\x08\xf5\x13\x8e.\x0bN\xea\x7fA\x00\xbd\xcc\xe3VK\xb5\x00P\x8e^\x0b\xfa\xf3\xc8:\x82\xack\xef\\e\xa6\xa3yi\xa3\xee\xac\xcdjR\x96m\xc8\xce\x0fX\xc6\xf1`\xfciC\x15\x1e!\x84H\x1d=B\xeaS*\x00\xc4\xba\xb8e\xeb\xf8'\x8d\xb5e\x0c|\x8b\xe7I\xdc\xe4\x97\xb1\x83\x97\x8as\x8cn\x1bh\n\x9bs\xa25o\x03 \x01\x94t\x18\xf0E 7\x9b%\x1b\xd6\x9f\xb3E\x83/\x87\xa5\x9bMq,q\xc6[\xc9 H\x19l36\x87<\x81e\x1a\xc49\x041\x04\x9bM\x14\x8a\x80\xd3\xf3p\xb1`)\x8bs\x88\xd8\x15\x8b2H\x16\x10\xccf,\xcbx\x95y\x90\x07\x90\xc4p\xc9VA\xb4\xe0\xdf\xf2\x15\x03\x16\xcfy\xa3\xe9\x00N\x82\xd9\n\x9e\xbd:\x85up\x0bs6\x8bx\x7fI\xcc Ia\x9d\xa4\x0cp2\xd9\xa0i\xf7\xf5Q\xf3\xa6R\xf6\xb7m\x98\xb2\x0c\xbbZ$Q\x94\\\x87\xf1R\xb6\x04Dg\x80b\xe1'1\xcb\xe06\xd9\xc25\x9f\x9a\x9ac\x9e\xc0\x19\xa5\xd1\x85\xb7\xa7\x03\x07\xe3\x03\xef\xc6\x81?\x8d\xfb~\xac\xbb\xd64J<\x9f\xcb\x91A2\x9f\x06%\xc5\xbe\xf0\xdb\xb6\xa6w`\x00\x92\xbd\xb5\x05\x8dA\x10oR\xa9\xda\x19\x04\xa7z\x9ft] \xeal\xa3\xa2\xe4b\xbf7\x1b\xd5\xef\xf2<\xc8\xa7?,\x96\xa8\x7f\xb6\x93\xa1\xffy\x17\xb6\xbe\xa8\xda\xdd\xa6T\x8b\xd0\xaaH\x0b\x9aUo2\x905\xeb\xdc\xbb9\xbaw\x93kC\xe5\xe3\xd1\x16\x1a(\xd8\xc1}^h\xdc\xc1&\xfc3\xbb\xe5\xc3hR\xa4#*|\x19d\xe1\xac\xad\xecL9\xd17+\xdb\xb9\xce\x9a\xcc\xda_v\x1db\x06\x93E\x13C\x9a\x05\x19\x031\x0fgl-\x06bh\xb6\x83\x8dV\xce\x02\x1d\xb5&\xe8\xae9AW\xed j\xfaJ\x87\xc8\x1c:+\xec\x10\xf9c'\x0d\x0dHF\x15\x1a\x9a=\x8d&4\xe8\xf6\xf2\xb9LY`9V\x05\xb5\xbf\x08z\x9f\xb1\xbd\xd1\xbf\xb6\xf7\xf7\xb9\xbd\x92U~\xf2\xcev\x928A\xedn\xf3\\|p\xde\xc6\xef\xe3\xe4:Vas4'nTB\xc1\xf1a\xd1\xf5v+t8\x0bo\x1b?\x8d\x1bz\xe0\xf4\x7f\xde\xae7V\x15\xcb\x90h\xe6\x7f\xf8 \xe8\xefR\xba\xfc\x97L\xf9\xbfD\xa6\xe4\x82V\xd2@HU\x1c\x00\xd7A;E\x93\xd0\x14\x17e\xd7,\xcb\x82%k*\x9d\x16\xa5\xb3d\x9b\xce\xac\x02\xd4\xe7\x92\x1e\xdd\xc6\x83\xb3\xb5\x85m\x05\xcc\xd3}\x1b1\x13\xe4\xea\xcfe0{\xbfL\x93m\xd4)\xd5\xe7\xfbm\x80\x1e\xf5\x07\x97\xe7\x1f\x16\x98\xbay\xa7\xa1t#\xaa\xc9\x95\x16t\x7f\xea;w\x8a\xd4\x10\x9c\xe0\xe14\x1c[z\x9c\xfa\x92\xdbX\xd8\xef\"\x94w\x1b\xdc\x83.(u0\xb2\x81\x12\x95\xba\x99\xc4@\x19\xe6\xda\xf7.\xc44\x8d\xcei\xbc\xd9\xe6m1v\x03*\xfb:\xb9n+\xb9\xa5\x92\xc7I\xa3\xb0\x08*\xff$\x1e\x19\x9fp\xc1\xac\xad\xfc\x8c\xca\xff\x18\xa4\xef\xe7\xc9ukX`\xcaB\xe9\xfc C\x9d\xbe\n\xf2U\x9bO\x0e\x08\x17\x96\\\x04W\x12\xa4\xa9\xb9\xc2\x1c Y\x10E8\x85\xcc\xf5v;\xf0\x92\x8fdo$\x11\xf3%9\x9d;\x1e\x9e\x7f}\xba\xe9\xa2\xdb9W\xcb\x19\xea\xean{\x99Y2g\xaaT\xa2\xe2\x04\xbb\x0e\x07B<\x07t\xfe\xff\xff\x0f\\2pz\x8e\xbd\xa5E\x9b\x11\x84\xa2#OU\x16\x19\xcd\xe7\xce\xf1!9\xb7V\xc6\xb4\xb6\x9bF\x87\x98\xd5}\xc3\xf5\xb2y\xd3\x19j\xd0\xb62\xad\xb7\xf4I\xf7\x19\xcb\xf5\x9a\xb3l\x96\x86\x9b\x1c\xa3^7\xcf\xe5\x93\xc7\xa4\x1f\xfc\n\xbd\xa8\xeb\xd6\x96w\xf5\x8b\x8d\xe24\xde}\x0ca\xfc\xd9#\xa0;\x13j\x14\x88\xeec\x07\xc1\xa4\xc1\xf1\xa04\x18\x07\xbe\xc1\x07\x1a\x9dB\xb6mC \xdb\xc0Dx\x8ep\xe5\xabE\xcd*L\x9e\xf2\x92\x06\xfel\x82%\xcf\x87yS\x98\x8a\xae\xde\x83\x9f\xe4g\"\x1fT\xcd[\x0f\xb2\xa1\xfd\xe4\x1d\xc0\xea\xefD\x9f:\x0b\x1a\xa6\x80\xa9\xa6\xc3\xec\xf2\x907m\x97\xd3u\xc1\xa2N\xbbK\xbb\xa67e\xdd\x85+\x91\xfa\x8e\x15\x97\xbcZN\xe3\xc8[6\x0f\xd2%\xcbi\xe3\xede\xe5\xdd\xb7\x8a\xbf<#\x91\xbcmg\x85\xc0ega6\xf6\xc5\no\xfd\x10\xd3L\x87\xadz\xfc\xbf|\n\x8a\xe7\x93\xac\xbe\xffd>\x05\xb0\x9bN\xde\xe9f)\x88\x9e\x7f\x83\xc4\xdc\x0b*\x186\x8cb\xdb%|\x05\xdf\xd1m\xab\xde\x11a\xa9f\x9d`&\xf3a\x0b\xc1w\xb0\xcdXj\xbfP#v\xbfK\xf6RR\xce\x1b4o\xa9\x9c7\xccS*\xe7p\xd4Bs\xe4\xa8m\x8a<\x7f>r\xf0\xb4\x9a\x19\x7f\xeb\x94\xa8\xffp=\xbf\x8bc\x06\x94\\HZ\x95\x0e\xbaM,\xf5\xfcX\xd3\xf39\xda\xd8\xd6\xbe\xbe\xf0\xffK\xb5\xfdv\xed}\x978\x93\xf0;\xd0\xf6\xa3O\xd3\xf6wS\xdf\x17\xbb\x99\x08\x0c\xda\xbe\"z\xedj\x7f\xf2\xab\xaa\xfduc\xa3\xfetP\xfb[N\xccH#\xb1GH,\xd4~\xe7\xdb \x0bg\xe5\xe8\x88\x8e\xbdj\xab\xce\xdb\xac\xc3\xa7]tx\xfb\xb0\xad:\xbc\xadJ\xd0\xb6\x14\xad6\x89O\xd7\xe1?yLU\xdd\xf5\xad\xe4yR}\xb5V\xac\xa8\xaf\x8e\x0f\x1b\xfc\x9f\xeb\xaf\x0d~e\xcd\xc3\xf9\x82\xfa\xabpC\x9f#q\xa7?[j\x10\xafw$\xde\xfe*\xfa\xf1\x17\xdb\xa8WA\x96]'\xe9|\xe7\x8d\xd2\xed\x0c\xbf\xde>\xed\xbe\xfa\xc16O8g\x8bX\xcew!f\xd7\xfd\x8d\x98c\xb7}\xebXZ@P\xc7\xd2\x9f\xb6\xcb_\xc4\n\xf2Y\xde{\xff$V\x10\xd3\x11yy\xc8\x8b\xdf\xbf\x15$\xd5\xac \xf6R \xda\xf7;\x18I\xd2\x16\x99\x8d\x1c\x9b)\xb5\x176gf\xe0\xc14<\xe7\xb2\x85\xaf\x9b@\x9a\xe4V\x94q\x03\xf3n\xa2\xe5\x84Y\xa3\x0b\x94w\xf5\x9f\xc9\xc7aa\x8d\x1b\xb2\xb0\xf98,l>\x0e\x0b\x9b\x8f\xc3\xc2\xe6\xe3\xb0\xb0\xf98,\xc8\xb2R\xfe\xc0\x05Yw!M,\xfc\x8fGw\x1fxf#\xcb\xe2\xb77\xb2l\xbe\xa4\x91\xe5\xf7\xe6\xf80\xff]:>\x04\x9d\x14\xee\x85*\xd9A\xc3\xe3\xbb8\xe3 B\x17\xf8\xb3\x06\xc5\x07\xa3\x98\x0c\x8a\x04d\xae\xd0\xc8\xed5\xae`Bb\xf7\x86$\\%j\xb5f\x16]Wj\xce\xa2\x90\xc5\xf9\xa9H&\xba\x1a\xc8\xdfm\xed,\x8d\xed\x9c\xb1Y\xca\xf2r[\xf4\xae\xad\xbd\xdbJ{R\xacx\x8379\xb0\xb6\xc8Q\xd8\xbfL\xe6\xb7\xceg\xbb\xa7\x04\x9b\x0d\x9d\xb5\xad\x06\xe2O\xfb\xe0\xbe\x84+\x0b]\xdb\x1c\xc3\xf4\xbc\x01\x14\xc5\xe27\xa6\xdb\xd4W\xb51\xb9favkH\xea(\xd7y\xdc\xb8;\xfan\x8c\xe1\xd6X\xee\x1f\xe0\x8e\xf3\xab\x18\x9b\x9a%\xbd\xaeaU@\x85Vi\xa3?\x00\xbbEV\x81]\xa3\xab\xc0\x8e\x11V@\xb0\xe1\xbc\x83\xcdkKS\xec\x96/\x05\x8a0+\x9d\x8c^\"\xa9I\x07\xa3\xd7\x82Jv0zm\xba\x86y\x01\xe9J\xb2\x83\x85lE\xe5w\xb3\x90]Q\xa5\xae\x16\xb25\x9e\x1b\x84\xd9\xcbgg\x87\xcd%9\x89^\xbb^-\xfe\xe01\xd7c1\xea ^o\xc7\x9f\xcd-\xdd\x16-\x11\xf59N\xd9\x9c\xc5y\x18D\x19\xb5T\\\xa4oi\xea\xff\xb2\xf7\xef\xebm\x1b\xc9\xa28\xfa\xffz\x8a\x12fN\x06\x1c\x93\xb0(\xdf\x99(>\x89-\xef8c\xc7\xde\x96\x9d\xcc\xda\x1ao} \xd0$\x11\x83\x00\x02\x80\x944\x89\xdfe?\xcbz\xb2\xdf\xd7\xd5\xdd\xb8\xf6\x0d\x94l\xcb\x19c\xd6r(\xa0\x80\xbeUW\xd7\xbd\xe6\x98\x04\x06I\xfc\"6/\xeci\x0d\x8eu*I\xc8\xe2\xf9\xd9\x91\xc0\x9f\x14\xfc\x96\xfeSg\x98)\xba\x9d\xb9\x07\xdf\xf7\x0d/\x1e\xa1\x15\xe6Cj\x16\xe5\xc2\x82\xb8t9u\x80W\xc5\xdf;\xbaT\xa7\x9c\xad\x1fG![\xbff\x88\xbf\x08\x040\xf4\x0fsC\xe8;y\\/dK\x1dgT\x9a^\x99\xaf\x94?\x06\x07\xdc\x17\xdfm\xca\xd5\xc1\x18\xe8\xed\x16\x1a\x823\xd2\xb9\xbc\xacL\xca\x02\xbd\x0e\xd57\xe8P\xcb\xba\xca4\xe7Ft\x1e/\xab;\x0d\x9dj\xbd\xf5\xd0g\xa7\xff\xa5J\x9b\xc8\xde8\xd6\xb9\\mM\xc3\x14\xaaU\xd9Zj\x868\x86\xb3\x1d=\xbd\\'Z\xd3\x11F%\xc3\xcc9\xdd\xf8s\xfc\xb9\x1ci\xbf\x99\xf5?\xc9R}\xbcy\xf5l\x80{SRo\xd8\xea\x13o\xf2\x98\xe5F\xa9\x19\xd5~\xef\xea\x9f\x17\xd6\x1d}\x9d\xbe#\xac\x83\xd6\xfds\x1a\xb8\\\xd2\xd7\xab\xcei\x1b\xd4/s3F\x077\x88zm\xc7\xe0<\x89\xd3\xb3\xe13\xca6\x1e\xfa\"\xd6\x93\xb8\x87\x93\xf8\x10!5\x0e\\\x81i\xe7\x1b\x01*=\xb0~\"V\xe5:~\x82AB\x98\x01\xe5\xb4\x92\xb4\xb4\x13\xb2ij\xff\xcf\x068\xaf\xb57pe\xf9\x12;X\xf5\x19\xa3E\xa4\xf4\xe71\x15\x17\xa6\x9a\xf8y@UE\xf1\xaeL3\n\xa8\x1b\xa0r8\x11\xf2u\xa6\xdeDa\x7f>\x0dl\xb7\xb5\xb9\xc2 \xfd\xd2\x9f\xe0'/a\x83@\xfe\xd4JE\xfd\xb1\x11\xb0\xda*Z\x04\xcc\x9aV\x8d!\x08h\xe3=\xf9\xf9b\x9b\xa5\xb1b\x98i\xa3\x8dq\x96/}\x16\x18'\xc6r\x8a\xf94\xb4\x08\x87S6\x14\xd9\xda\xd4\xae\xa9d\xf8|(^\x81r\xafqR\x11 \xdb\xf3\xb9\x0bV\xbd6\xbf\xb8\x1bfiF\x98f\xdc\xbf@?B\xaeoi\xab\xe9\xb48\xf3\x8aA\x02B\xea\xf8\x95\x81=`i=\xb4M\xd7\x0e\x14W\xd9\xf0o\x1b\x92\x1b\xc6\xfc\xbf)\x08d~\xee\xafII\xf2\x02}\xe6)#\xc99E\xd4t\xaa9^|\xdce9\xbf\xfaJ\x8c\x19\xd9'\xc5\x96B\x1e\xd4\xdd;\xa3\x9f@f\xbc\x01'\x14\x8fZ>\xf5\xea\xe9\x0bk\xf642\x1cf\x15\xd8`\x02\xf3g=\xcd\xea\x89\xb3:\xc8,\xd8\xa6\x86\x9fA\x07\xbd\x0c\xda+\x86\xfa\x12\\\x1aB\xde*+\xc4\x87 m\xbd\xfduE{\xe9\xa3\xef\x93\x82YWl\xf6\n\x03\xfd\xb2_\xda\xfb\x85O\xe0n\x18\xcd,.W\xb5\xdfd\xf8\x7fl\xd3\xbdK\xec\x81=$\xfb\xa7\xf8\x8fe:W{-\x01W\xc2\xee\xb4\x92\x98\x9d\x9d\xe3 \xd3\xef\"\xe6\x9e\x0e\xcb^\x0df\xa5\xa1\xd1\x13\x12\xacS:]j\xe2\xa03y\xc1\x8a\x04\xef\xe6\xa9\xa2 \xb8\xb84\xadZEt1\x9cc^\xdfV\xe9\xc3\xe8\xdea9\xa2\x1c\xb8\x01s\xfc%\xba\x8a\xb7\x84\xfb\x8c\xd9PD\xaf0*(i\x08gpf\x06\xe6[\xa9\x9a\x19\xf3\x1b\xf5\xce ^\x9a \x1e\x19\xb6\x05p\xdd\xe4% 54\x89\xb5\xf5|\xed\xba\xd4\"\x9d\x8a\xb9OM\x0c\x8bJ]~\x170M\xc4.H\x8dTp\xe7Q\x9au\x94\xd0iO\xaf\x96\x03\xd6^r9\xbd(t\xdal\xea\xbfMM\x97\xf2\xb2\xd4\x15\x84$\xb5\xef\x18\x8e\xae\xc2\x03R5\xe0\xd0f\xb8\x1f\xcf\x03\xf2\x92\xf87<\xeb=\xb0\x859G\xc9H\xc7'eC\xda\xd6&\x887\x1e\xee\xbd\x0c\xf8\xba\x9e\xdb$\xc0\xff4}\xaf\xde\xd2v\xbf\x91\x15_\xb3\xfa\x97\x1d\x81Ej|\x18\x90\x1e\x1fx\xe7\xab\x14\xf9R(K\xc7\xddz\xcc*\xc7\xdd\xf0\n\x1cw{\xe5\x95\x94\x94\xa3\x94\x94W\"\xbb\x97Wj\xe3\x82i$\xc0GS\xd6n\xc3\xea%\x1b\\\x04\x8b\xe4\xb9\x112\xad\x1dq\xd0\x15O\x0d\x19\x0dq\xc1\xf1\xe1\x10R]\xe2\x92\x8d\x88\xf4\xac\\\x00\x15\x0en^\x10\x13?\xd7\xf8\x1f3\xc7\x82\x19\xe8Y2\xce]\xf9\xfa\x82\x1c\xc2\xd8\xcb\xe0\xe4h\xce\xbd\xb6\x02\x81\xc7#C\xdffU\xa4\xba\x16\x8c\xaf\x94\x96M\xad\x17T\x9b{6`S\xaa\xcd\x7fK\x9b|$\xe06\x8a\x91*\x11\xbc\xc5mZm3\xe1\x1covw\xcf\xd1q\x02\xb9H\x9doj\x8a`\x94\xc1/D\n\x019\x06E\x0bp\xb1\xcc\xf4d\xca==\x18K\xca\xcbJDIH\xce_,\xdctd\xf2\x97\x8b\xa0\xf72\xaf\xa0{\x92\xbe\xd5\xf8uXy\xd1C\xc3crx\x15\x1d qA`/g\x1e\xda\x8a\xf1\xc1\xb7t\n\x18\x84\xb9C\xa23\x9d\xcf\x0dv\xba\xa9\x9c\xc7\xf7\xb4\x89\x84\x94\xf5\x8148\xd8P\x04\\1\x0e\xb6\x91KOY0\xaa\xd5\x14\x9e\xe1\xcbsX\xa4cPE\xdf7\x16\xc9WO\x02\xe3\x98\xacF\xdf?\xe8\xd4\x1e\xe9\x89\xcdy\xc46\xaa\xd5y\xc4\xe6\xd3\xe6_\xfb\xe7\xca\xbf\xbe\xf2\xb2M\xb1r\x9d\x9c\x14Y\x9a\x14\x04\xed\xca\x87\xa8\xd3WP3E\xde|\xd6^ev\x1c\xd2\x1a\xba\x9c\xed\xd4\\\xdf\x95\xf8C\xcca\xcf\xf3y\xc8\xe0\xd8T\xb6^hS0\x87R\xa0d\xe9\xc0\xe1!\x92\xd1t\xc1\xa2X\xc4\xe7*C\xdd!\xaa\xff\x12\xfa\xc17\xaf\x9eV\xb2\x9e\x9bu\x03\xa5(A\xd9b.\x03Vr\xeb\x15 \xa3\x9c\x04\xe5\x9bZ\x9f\xd1\x13\xe8t\x0c+\xfe\xd1\xaf\x9c\xd1[\xf6\x93\x8bS\xa7\x95\x84\xe1\x8b\"9\xa6@\xb09\x8b\xe5\xd4\x19\x89\xba\x06\xa2y\x99Lp\xee \xcd\xe6q\x1a\xbc\xc3\x12\xeey\x1a\x9f\x9e\xceK]\x08c\xdbF\xc4\xff\x92B3\x0b\x11\xf1sI\\\x94\xb1\xde\x89\xa9\xce\xc9\xf5\xcc\xa1\x8aD_\x9a\x03\xe4Z\xd69\x19\xb3\x1f\x07X\x15\xd9\xbd\xf7y\x9c\x05\xd0\xd29\xad\x88\x1f\x92\\b\xf53\xed\x19\xbb\xe0\xc9F\x98\xa1\xa0=\xc0\x9b\xd4\x17\xb2\xce\x1b\xd9\xc1\xbb\x12L{\x81\xcc\xc9N\xea\xd1\x86\\d\xfc(\xc3e\xae\xe9\xa2I\xfb\xe1\x8e\xc1\x81u\xe1\xe8G\x1d\x1aGm8\xf3\xa1M\xa0%Y^\xc6;gr\xb1\xa9\xa7\x06=*\x06W\x9c\xdb\xa1X\xa5\x9b8\xac\x08\xe1\x9b,\xf4K\xdb|\xac6\x15\xcd\xeb$\x0e\x9e\xd0\xf9\xa0tI\xea?\xff\xf8\xa3 E\x0fq\x0e\x81?\xdbO\xd9\xf1\xcd\x9f\xf3?\xda\x10aTd\xb1\x7f\xc11\xeb\xb1P\x7f\xb07\xe4\x0f\xa5c\xf8\xdcR\xb2\x8a\xe9\xd4\xc3\x0eM\xca\x9a\xd6\xf0\x06C=T\xd5\x8e\xe5\x93\xac\x7f\xd3\xafx=\x0b3?T\xcax=\xc7\x07\xfc\xc8\x12\x98\xa2\x87\x0c\x98\xf3\x00\xba\\<\xdfPi8\x14\xe4\xe9!\xf8\xde\xbau\xebI\x9a\xbb\x9b1\x14#\x98\x81\xef\xe5\x9d\x9b\xfa\x86B\xa8\n(S\xa1{cL\xa9\xb0\xa2\xa7+\xcf@$\xd7\x974\xafm\xfd\xf9\xea\x10\xf1\xca\xf4\xc7cSE\x97u\xfdb\x92\x96\x8f\xd3\x00I\x12\x86\x87k\xdf[\xd6\xef\x11\x9b\xf4\x1d\x175<\xfa.\x1a\xc0\xe75x\xe3\x98\xd0\xber\xda\xb7{n-\xd2VlO\x1c\xca\x9f\x92\xa4\x9c`\xe4\xd8[JZ\xb6'\xce#~\x13\xa3\xc24y\x85\x80\xeb\x94\x12\xd7 ,\x16\xea\x9c\x81\x8a\x8d\xfb=\x0b\xcf\xd2\xber\x0c\x87]wm\xa3)\x1c,\x0enk_W\xe8p\xf9\x0c\xc3\xe2\xc8\xe8\xf5%.\xa4\x95z\xa7\\\xe0l=8\x98\xe3\xcc\xc1\x90\xf7\xed y\xcb\xa2\x15\xb5\xef\x9a\x92x<\xa2\xe24\x1e\x06\xc7\\\xe0\x96\x8b\x82`1iMn'\xd0E\xaa\x1c\x99f\x96\xd3\x0fm\xe2\xf6\xd1\x18V\xda\xf4\x06v\xcc\xd7\xed>\xf3\xf5\xe6\xd53-\xdf5\xd4)TD&\xd2-\xa0\x1e\x8f%\xa3\xb7\xd2\xa7Xh\x8e\xe7\x98\xe4[\x92\x83\xd8O\xda1a\xf0\xcc\xc0Q\xb1\xcf\x16\x13\xf6\xeeN#+\xe9~1\xafR\x99\xef\xd85\xb6\x1dw\xec[8\xa8\xd1 \x8d!H\xe3S\xd6d5\xeb\x13z\x8f\x1fk\xban8h$\xd4.\xd1\xd5\xf5\xc7\xca}\x9cv\xea1)\xfd(.\x0cy=J\x8c\xa4\xfdP\xab\xf8\xd1Vo\xe8\x92\x85cX_e(S\xd5\xfe& kfc\xa7\xd1G\x8d\xe0\xba7\x8d\xaf\x81S\xf9\xf8_1\xaa\xed\x84_K\xdd\xf4\xb5\xca\xf7\xb6\n\x8e\xc1\x0d<\x04\xe1\x86\xb8]\x95\x99\xae\x03\x18.4\x9f>7\x0e\x8e183\xb80\xb0\xc8\x0c\x8e\xa5'4\x04\x17m\xf2x\x06\x06\xe6\x9c\xf3\xa7\xda\xcc\x89\xf4j\xca+\xba\x98\xb1\xf7\xf5|<\xd2\xcc\x871\xb4\xb2\xea\xd7\xb1MS\x11=\x96\xe7\x97 k\x10|\xed\x0c\xe6\xe6\x06\xd5\xe1-\x97\xf0\x85\x97\xeb?C\xbc{\xdd\xf4\x9f+\xa5\xfe\x13\x9f\xf4\xb4\x96\x91x\"S\x80\xaed\x9a\xd1\x0d\x7f\xd0\xd3\x8c\x16\xfcA\xaf\x8d\x98?\xe8iF\x03\xfe\xa0\x97\x1dy!\x1a\xdf\x7f\xd0}\x94Q\xf1e%\xb4\xa7h}\xec@\x84\xa2\x83\x8a\x9aU\xab\x8f\xafO\xdd\xda\xda\xd6T\xa9\x94\xa5&*\x99\xfd\xac\x99B\xb9\xb0Q\xbcEm\xc5\x9bE\ne\xac\xd0\\\xc7]\xbc\xc9\xe3!\x96-\x9eU\xb9\xad\xce\x90\xcb\x19\xc2LG\xce`!z\xe9\x12o\x93\xc7.\xe6\xe5\x17;5N\x99\xa3\x00\x95\xe4\x99;\x87+\xd1\x14\xca\xe7*\xe5s\xd5\xd4\xe3\x8c\xdc\x91\xc7\x1d\x8f\xd2\xbc\xe7\xf3\x04`\x9d\xe3\x17\xc9|\x7f\xbaT\xba\x86f\x9b\xb3\xa6\xabd\n\x0f\xc1Y\x95eV\xccn\xdeL\x13*Q\n\xbf\x06/JoV\xef9 \xab\xaa\xd7K\x8a\xab\xb4\xb1\xc5\x0d\\\xa8\x15\xa6m\xcb\x9b\xd2\xc6\x16\x08z\xf9K\x14\xc7\xafH@\xa2-\xd2\xb6\xc2\xc2\xec\xa6\x94\xd3\x85\xe2}\xf8\x12\x81\x88;\xb2p\xac\xc7uB`\xdb\xa5\x02\xddr\x95\x03\x96K\x1eZ'\xf3\xb1o/\xa1\xec\xd4\xbc\"[\xa7\xd8\xa9t\xce\x1b\xba\xe3\xf6\xe4\xd3\xed\xab\x9e\x1a\xb1d\x99W\xf8t.\xffM\xde\xe41\xa3Bu\xb1\x83j\xf2TqF^\xb0\xc9s\x92\x94OXj\x08s\x85\x93-%I{\xcc\xf9\x03\x7f\xbb\x1b,4\x97f\x05\xff\xc6f\x0c\x18\x9f\x88~\x16{Q\xf1\x93\xff\x93\xbbB\xfd\xca\x8a)0\xc4K\x1b\xaf\x88\xa3\x80\xd0M\xb2\xd2U\xc9m\xf9dlzy\xc5|\x13\x9fDw\xc3F \x87\xeb\xa4\xd5:\xea\n\xba@=dU\xbf\xac\x12\x92\xb1\x9d]\xb5\x89\x89\xf5\x0c\xf5\xb5\x00\xb5 \xcb\x17\xf3_\xad\x12\x99\x95\xfeR\x9b-F\\\x9d\xdd\xa7\xcdB\xd3~\xa7\xca[\x93\x9a\xdf\xa8\xf7\x9f6\x8bC\x0b\xdc\xc2& \x8c\xe7\xe8\xae\xbei\xe9\xa1!,\xf0\xe5\xcf|L\xa3m|\x0d*\xb2\xc5\x8d\xc5\xe5*5:\xf1\x89+\xc5@M\x816\xcf\xa2\x82\x9e\x8b\xb4ez\x98&c\xc8u9g\xc4\xc5\xd1\x8f\xc7j\xba%\xaf\xa3\x85\xa5\xad2\x98\xc1bTi \xf3Q\xad\x16\xdc\xb9\xb0\xba\xb8XJ\xd1*3\xa4\x05\x9a\xd0\x8b\x9e\x1e/\xb1\xac\x90\x05\x96\xd0+\xcd\xac\xd0\x1b\xaarE\x169@\x01\x83\xb9\xe9JY\xa17T\xdb\xc7\x08\xaa\x91\x8c\xd8\xe3F>D%d\x13\x8a\"3\xa6\xb5\xfd\x06\xa6\xbaB\xde\xab[\x0d\xaf\x8c\x9fR\xa8\xc9\x17p\x856D \xce\xfe^]8\xe9R\x96mYy\xe6\xcf\xc9\xb2-\xad\xe1\x9b\xaaj\xf8F\xaa\x1a\xbe\xbe\xaa\x86\xefFU\xc3\xb7P\xd5\xf0\x8d{5|Y \xcf\x82K\x05m\xe8@\x04\xcb~\x16%~\x0d\\\xfb\xa7\xe4\xd8\xafi\x88\xe0\x10\xee\x9cq\xe6\x8c\x1bPC%\x02J\x0d\xc2\x8e\xb2`\x15\xc5aN4\x944\x1d\xc6\xa9GC\xb8t\xdf\x9aC\xdf\x0c\x90/\xb0p\xb2\x8e%_\xb0\xc38\x0d\x8e\xce3?)\xb4Q\x14\x19?\xb8I\xf6,J\xdeE\x89fFCQ\x04\xd8Y\xf8qAX\n\xfeL\x0dO\xb9\xf4\x0d\x96\xfd\x8c\xfd\x0c\x1dk\x95\xa0[\x06jSes\xcd@\x1f\xf3\x1e\xeb@\x97\x0c\xd4\x04V\x05\x164\xa1\x1aJ1\x9cb\xab\xb7\x15\xb5r\xc8\xe7yz\xa6\x19\xdcY\x14R\xd2\xe0\x1c\xec\xeb\xbccH\xb4\\\x95\x0cjpo7\x85>\x14\x88\xed\x08\\\xab\xbf\xc4\x14\xcf&\xd8\xe7 r8t\xa9\x9aw5\x9d<\x8f\xa3\xe4\xdd\x0f\x83>\xa6\"6:\xad\xa3\xb6\x86rT\xbc\xc8HB \xf6\x91j\x9er\xa3\xf9@\x92JC'xg\xe2)\x1a\xe6{\xce'BcX\xab\x9d\x16y\xba\xfe\xf1\xd8\xfd\xbd\x1b\xcd\x87\x1a\x0f\xa7\x9e\x94\xf7\xe3k\x97\xd0\xb4/\xd4g*\xa1>S \xf5\x99J\xa8\xcfTB}6,GS\xe6vc\x94\xa9\xe4\xeef:\x97\xf3\x05~\xed^sY\xb96@&\xecg\x1f_\xd8\xd7\x9b\xe9\xbe\x08\xfb\xe2\xfap\xc2\xbeP\xa4\xaa\xe1r\xcbT\x05)\x87\xc3@R\x0dc\xc9\xb4\x07\xe9r\x19\x13d1\xd5\xa0L\x82O\x93\xd79\x15\xf8\xf1\xb8T\x03o8\xf0#? Hl\x00.8\xf0\xd19 6\xba|\xfb\x0b\xa3\xe1.\x1b\xa0<\x08\xadU\x12\xabjq\x8cz\x8e\xed\x10s\xea\x1a\x81\xad2q/+P\x8b\xef^\xb0 \xf5\x8b[\xc6\xef\xce+P\x8b\xef\x9e\xb6\xdd\xce*\xc6J\xc3z`\xb8\xbd)w\x02\x15\x9f\xcf\xbc\x90d9 \xfcRW=\xe0\x1c!\xb98\xa4\x06;F0}n\x8bG\x08c\xcak\xf1\x0e\xa1R\x8dn\xe7;\x84\xd0*\xe0^\xf0\x8f\xf0\xe9\xd2\x95\x9c|\x89\xa0~\x1c\xa7g\xaf\xf3\x8b\xa7\xe5\x8b\x8d\x06\x83_\xb3y\x1b\x98-\xe49\xeb0\xff\xfa\x11\x13?\xd5\xe0O\x11\x9c\xb0\xbd\xf94y\x99\xa7\xcb\x9c\x14\x1a,\xf9\x15\x0e\xe1\x9d\xd7P\xea\xa8A\x7fB\xd0\xa6\xeeF\x0d\xfb\na1\xdd\xb7,\xa3\xb7\xb8\x1e#\xc6 %Q\x9ai\xb5@\xcf\xe0\x10\x1e3#_\x15\x02\xae\xd3\x8f\xbd\xa9\xe1\xb3<\x0d7\x81\x1e\xfc7\xee\x8f\x8c\xa9G\x9eEE9r\x1f\x8f\xe1\xc4iT\xd5\xd5\xf5\xee \x1c\xc2\xb6F\x9bc\x1c\xba{<\x86G\x9a\x97\xfe\xddQl9c\xf8n\x0c/4\xca\xab\xef\x9b\xbd<:/ \xeaI\x8b\x91\xfbX\xd3\xcc\xcf\xc8\x04\xd9\xcd\xda\x0f\x0c\xb6YKX\x0d\xfc\x0b\x03\xe6\xf8\xa6\x83\xfc\x91A\x06,w\x9d\x1a\xee\xbf\x19\x9c\x8d\xf2\xf5\x1f\x0c\xd4F\xf9\xfa\xbf\x18(\xc7G\x1d\xe4_\x19d\xe5\xd5\xc1\xb2,h_\xf9?\x9dW\x8e\xf4I^\xfe\xd9ma\xb3^\xfb\xb96\x17\xca\xfff\xaf\x98\x14\xc2\x84\xf2/!\xcf\xe9S\xe3\x86\xda\xa5\xf7\x19f\x8fe)d\xd1\xc4\xf9-\xec\x9b\xdc\x95\xd0\x9d~\xef\x19\xee+\x1e\x9a\x97{\xad\xec>,F\x87\x838\x9c{\xd3\xb9p\xe4\xe8\xe0R\xf43\xf1\x8c\xa1$\xb6\x16R\x10\x1e\x04\xb4\x7f't\xdfI\xd2\x84\x02\xd8\xe69\xb1\x12\xe6\x9b\xaa\xdb*\xe7c}2R\xf9\xf6\\\x06\xe2\xc0\x0dx\x047\xc0\x91\xe9x\xdbP\xea\xd5\x8e\xc2\x99F\x03\xfe\xefZ\x01\xaa\xd4\x80\xaa\xa6\xe0\x9fZ-\xb1\xc0[\x94ngp\xaa\xeea\x83S\xd5\xfa\x98\xb4}K4\xa7w\xab\x84\xd3Z\x0f\xd7\xf0\x9f\xd1\x1c\xf6\xb53\x84\xca!W=M\xffm\xa7x8\x1f:\xfdC0\xb0R\x8d\xab\xeb\xe2\xbf\x1f\xc3c\xba!\x1f\xb3-\xfe\xc7\x1f\xcc\xff\xe4\xf0\xf0\x10\x1e\xd7\xce(\xea\\\x13\x06?\xe8J\x15u\xeb \xd3\xd5S\x15z-\x03\x18\xbaU'\xee\xed\xe9TC\xe8d\x13\x10\xa7~\x18%\xcb\x89\x9fDk_c\x1f\x19\x8d\xe1H\x9bX\xc8`%\x91\xb5\x8d\xea\xcd\xd3$\xcd\xd7\xbe\"\x07\x10&x\xfa\xc5\xcf\x93(Y\xce\xe0qM\"Fc\xf8\xd5\"\xcf\xd1\xb0\xfe4\xd89}\xa9\xca\xab\xc6Bcf\x10M\x83\xff\xb01G\xfc\xaaX\xd4\xd1h\x0c?\xd1y\xfc \xc3=/\x91\xb6E6,\xc1\xf3N\xc24(v\x9f\xd1\x0f\x86YO\xa2$\x84u\x9a\x13\x08EF\x9f+^\xd8\xd6\x0c\x0c\x1f\xb91\xd0\xd5\xd8\xe6\xa99\xeb\xcceq\xeb\xa7\xa6\x18\xa4\xc23u\x1b\xff[\xd7\x86}\xb0\xac\xc5L\xc4\x91\xf6\x0bJ\x8b\xd6O\xda\xe8X\xf6\xb4\x91c\xa7yj\xa87\xd4\x0f\xbaa\xd7R\xc4\x0c~\xb3:\x85yA\x10;\xf1\xa3\xe2Ef\xf0X\x03\xc5+x\xff\x03\xdd%uj\xb8\xa6\xbaL\xeb\xaa\xdb\xd2\x95I\xeb]\x89\xab#\xb9\xcf\xe0\xb9\x86mi*\x12f\xf0R\x0d\xb9H\xa4Ev\xc4e\xcdP5\xb4d\xda\xecE-\x15\x996\x7fQ\xe6\x97\xab\xe7\xdc\xb1\x93q\xe1\x86nr\x17\xe4P\xb1\xe1*l|\xae\xc1\xc1\xbf\xeap\xd0z2\x98M\xfeX\x0d \x1cV5Ly\xda\x91\x1bgB\x03Q\x98\xe5H\xda~\xf5\xda\x16\x15b\x85;\x12\xda\x91\xe31T\x1f\xd1\xe9!\x96\x84\xbb\x83\x91\x90}l\x06s\xafh\xdd\xd1\xacs\xff\xe5\x0b\xafw\xd3\xf0>\x05\xf9\xd9\xcf#\x8a\xf0?3\xed;\xffH\xef\x89a\x18Mx6\x8ca_8Z,HPF[\">\x85\x9d\x11\xdf\xa9\x9e\xe2}3\xfe}\xf5\x15\xbc\xa4\xff\xbc\xc2\x7fLtq\xa7cV((T4Z\xd5\xd8\xff\xd2\x9eo\xec\xa33x\xf5aq\xdf\x96\x98\xf0H\x16\xa6!\x9b\xc1\x13\xc5\xcc\xd7S\x7f\x15S\xfc\xbcRu\xbc\xa4\x12\xf9\xbcL&\xcb<\xddd(ys\xfd\x95\x91\xb3{.\xdeW\xf5\xe8\x17+\xc9Y{Z\xd9\xce\xe20\x92|\xd9\xb5\xad\xec=3(\xacvJn\x9a\xaa\x1f\xb5(k9 \xf6C\xd3wz4\x86\xa7W\xb5\x97\x85 \x1aT\xc1dCw\xf3.\xcd)]'\xaaey\xa6\x19\xe0\xcf\xba\xd6*\xb5\xf1\x0c\x9e\xa9g\xbaJ\xea\xab\x89*\x11\xcc\x90(\xfb\xa0\x8d\xfd\xb0>\xb7[l\xc4Ul\x98\x86-N\x9b#\xd2\x1aK\xb9\xf5a\x06o\xcc@\xfc\x90\xda\x8a\x80\xbf\x97\xfc\xfe\x934w\x19C\xa59\xfc\xfb\x8c\xb4\x95\xce\xdf~\x1b\xa9A\xe4\x86\xad\x19\xbcV\xbf\x82\\\xac\x89\x9a\x10\xf4\xa0\xf8\xdet\xdc\xfe\x1f\x1d\x06\x93J\x17>\x83\xef\xad1\xce@2vq\x1bz\xb9\xc9\x89\xcce\xa8\xca|'w\x19j\x9c\x1c8)\xad\x87y\xb5\x99d\xcf\xf8\xa6\xec?\xaaQ\x85J\x8a\x0b\x8fY\xbc\xba>5\xcc6\xa1\xf3B\xfa\x12Z\xd4\x9e1\xa5\x17\xd2B\xee\x85\xb4\xa8\xbd\x90\xee5S\x19-4\xeeF_b\x8b\xfe\x03\xdd\x8d\xac\xfc~\x86\xc4\xfb\xe7\xf6\x0e-\xe9\x10\x87\x16\xe6\xa6\xd4\xb6\x13\xa9\xa1}K_\xaa\x0d\xd6\xd039\xa7\x14,\\\x9d\x91-5X\x80`QQ\x95=\xd5\xf0\x0d\x0b\x845\xb9\x9ed\x08\xa5s= Y\xd7V\xe9\xd9\xb1\xa9{+\xfe1\x0b\x17\x94-\x03\xcd\xa3e\x94\xf8\xf1\x0b\x9bW0\x12I8\xa2X\xbd\xb1\x84C\xc8\xcc\xb3z\x81K\xc4\xd5\x1d\xc1&\x8fJ\xadU{\xce\x12(Tu`\xab\xae|_j\x8d\xf9\xa7\x9d\xc4\x0b|:\x9f\x1b\x03\xbf\xcf\xe4/\xbe4\x04\x9a\xf3\x1a'?n\xd6\xd9\xeb\x14\x811;\xc4\x07\xb7.\xd7Z\x01\xd6O\xe8\xfc\x8d\x06b\x8d\x16\xb0\xae*(\x05\xd1\x08 \xa7\xba\x1e\n^P\xc5\xb9\xa9?{f\xaf\xa6\xd3\x05>v\x0c\xd0\x1a\xc3r\xcd\xe3\xc8\xe3\xc6ig\xc3\xab\x92\xfb\xba\xabcc\xafX\xd2\x83\xad\xa8\x99],\x8a\xedn\xe9\xdd\xd5\xc8\"{\xfen=\xab\x93\\D\x8a\x02\x04\xef\xc7 :Qg\xdc\xff\xea+\xb8\xf0\x82t\x93\x94\xae\xaeos\xbdY\xbc&\xb93\xd0d\xcc\x1a\x1e\xe3!N\xd4\x941\x94\x98\xef\x97JMT\"\x89r\xec[\xe1^\x982\x89 \x81\xae\x13\x06\x17\xae\xc2\x01\x05z\xacEu\xd7\xac\xb8\xd2V\xc8\xc9\xb4\x08{\x85B\x87!N\xa1\xbb\xcfL\"D\xb0\xb3\x08q=\x03\x19>i\xa6\xb2\x01\xc5\xa6?\xa32\xa3_\xc4\x04q\xed.&hK:\x9b\xb8\x8fK\x1d\x1b<\xb3\x8e\xf4\xdd\xf7c\x94P\xded\x19\xc9\x1f\xf9\x05\x91%W\xd9\x99P-\x86\x13\xaa\xfa\xbb\xe3\xcf\xa0\xc4\xf1g\xaa\xad\x10\x91S_\x94\x16\xff\xb1\xd4H\xcd\xc0\x95\x034\x11\x89Dc`\x14\xf5\xe9\xc6I\xac\xe2PR\x844\xc6\xa1D\x08\xa6\x8fC\xf1\x11F\x1b?\x82u\xf1\xed\x84\xf7\x82w\xecq\x9d\xc6\xc4\x18\xe1AO\xd8\xb2\x99G\xe4\xc3\x9f\x04y3'\x838\x0d\xe8<\x9d\x9e\xb6\x9d\x9d\xa5@\x83\xcd_\xdazUU\x02\x06\x9d\x02J$`\xd0\x98\xa2\xb2\x06\xdf\xca\x9ao\xfbO\xfbXy\x80J\xd8\x1b\x0d\x0e\xb2,\x0d\x91|\x84Wy\x04^7v\x99\x9e\xaa\xcd\x80\x078\xe4\xe5R\xfa\x87[D\xcf\x84\xfb\xb2\xd3-\xea\x96\xd0\x8f\xd8\xe9\";=\xa2\x8f\x7fz\xf8\x98\xc1\xa63J\xf5q\xb2\xad*\xca\xd7\xe6\xa6>\xe6$\xed\xd27b\xa5\xdb\xe1#\xaf\xd2\xb3\xee\xbe\xe6\x83M\x87j*\xa4\x0c\x9d,\x81\xcc\xfb\xf1\x95~\\Z\x9bS\xd7F\xb3\xb4i\x1d\xbb\xe2P^\xe3R\xfd\xc2\xf2\xa5*c\xbc\xaeC\xa2f*\xeb\x93\x1a\xacU\xe3T\x0d\x96[\xc0\xc8\xeb2\xaa\xcb~\xf6\x06\xe3<\x89H\x8cN\xe5\x1f\xb2\x114Q\xb3\xa2\xa1\xeafZECK\x8f$e~qL~\xc3\xec\xb7\xa6\xcc\xa0\xdbF\x8d\xa8f\x9d\x9f1\x1c(\x881=\xbb\xcb\x93}\x85\xb3!\xee\xe4\x93\xa9$ \xc8\xb0\xad\x12\xd5Q\x84\x0cUT\xa5\xdeT\xb8\x8a\x9e\xa3\xcb\xa9BAy\xfe\xb3\x1f\xcb\xf4<\x9d\x04\x96\xef\xdb\x05\x10\xdf\xcb\xcf\x04\xf6\x99\xebu&\xbcJ\xcf\x0c\xc7\xc2\xed\xe9\x9f\xe2X`\x03\xb59\x19(B\xc8\xcf\x04\xe2Q|\xe8?C\xa6\x14\x1eR\xa63\xfd\xf1\xb8\xfa\xe1\xa2\x92\x91+\x1a\x87\x9d\x14\xd6\x94\x88o]#1ap\x9d\xbd\x1a}&H\xdbG\xcc?Q\x02\x13\n\xf0\xe0\xee\xfe\x9f#g \n\x9f\x98\x949\x1a\xc3\xa6O\xca\x15\x82z\x1fp\x91\xe6\xe0\xd2\xaf\xd1 \xaf$p^Bn\x8c\x13\xceR\xff\x16\xa31N\xf4\xfe\xd7\x10\xc07P|\x0d\xc1\x8d\x1b#\x88O\x82\xb7\xcd7O\x02\xf5\xc1B\xb7v\xc4O\xb2\xbe\xb2\x00ei\xa3\xc2 \xf0\xe3\x98k\x0d\xc8\x18N\xe8\xbboE\x11\x87\x18O\xe1\xc8Cs\x85\x1fG\xff\xae\xa5\x07c\x19\x07zE\x1e\xa1\xe3\xed{?\xbfG\xadBz\x865y^\x936\xef\xab\xfa\x1a\xf3$\xaai\x00\xd7X\xe2\xbe\xa3\xdfc\x7f.\xa2\x98PN\x03S-\n\xef%\xaf|\x0b)Z\x0dY E\xac\xce\x9c\xc07\xacVa\n7 \x82o\x0f\x99;n\xc2\xe2\xbbqs\xf39}\xcc\xd6JV]u\xcc4\x19=E\x17\xdd}\x1fC[u\x95\xb5\xcf\x98\x9c\xbf\x8a\x96\xab\x98\xce9\xaf[I$\xc1P\x1d ]\xc6\xff\xf5\xbb\xf7&\x0b\xfd\x92\\\xaf\xfe}\x02e\xdfV\x1f\x90\xc1vV%h\xe87\x14\xa9\x88\x0f\x15\xc3\xb4:.,0\x86\xc4\xc4\xb9\"\x9f\xeaj!&A\x1a\xaa\xca2\x8eQ/v%\xed\x89\xa1Nx\xc5yY57q\xd5^\x1dt]\x9a\x14Z\xd5M\xe71\x07r\xcc\x96i'\xcb\xf5\xc9\x01YYN\xda\xb4\xe4\xc8\xd1\xf5\xfa\x97\x15!qU\x04KG\xd0\xd5_i\xcc\x19\x96=\x80uD\xbf\xa0\xae{\xfa\x9er\x00\xc6M\xd4W\xc3\x99Tpr\xa7\xd7\xe6N\"\x1e9\xcf\xd2\xbc,Z\xc7S\x9f\xbd\x85\x06\xe7\x99\x903\xf8>N\xe7\xee y+[\x83\xf2\"\xc3\x91ST\xa7\xfc@\xc4\x8ad\xdfL\x83\x92\x94\x93\xa2\xcc\x89\xbf\xeeH\xeb\x1d\xf6'ZT\xf5v\xf7\x0e\x0f\xe1,J\xc2\xf4\xccK\xfcm\xb4\xf4\xcb4\xf7\xd6\xc5\xb1\xbf%\xb4\x0f#\xddC7\xefsV$.\x88\x82k\xa3\x87\x1e\xff\xda\x9bW\xcf8\xc61\x0e\xfe\xcd\xabgn\xae\x91\xe9C\x9e\x0c\xa4\x8b\xa6\xbeL\xef\x1dyX/W\xb8\xb6\xc1!8I\x9aP|\x8e\xbcUN(G\x9c\xd2\xdf\x05)\xbf+\xcb<\x9aoJ\xe2V\x9b\xcfa\xb2N\xa3\x1cq\xcd\x00\xd13\xb3\xfb\x1ec$\x9cq\x15\xd3;\x1a\xd7\xdd\x9d\xa7\xe1\x05\xe5\xd9H\x12>ZEq\xe8F\xc8\xa6\x05t\xeb\xba=\xc0\x9c\xac\xd3-\xa9\x01\x1b\x93\x95\x93m\xfa\xae1Y\xa9\xea\xe8}/E\xc9\xeb L\xc9\x95\xbfR1+R\x89Y\xbeJ\xcc\xda\xa8\xc4\xacB%f\xc5\xfcAOb\nx\xca\xc7\xbe\x1cUKZYU\x12B\x98>+\xe0?\x81`\x95\x8f\xc1\x97\x0bV\xd1u\x14\xacr.Xml\x05\xabt\xa8`\x95{\"x\\\x84\xe1\xfc\xc2B\x04\xad\x84\x0e\xde\xd5\\T\x88\xac\xc3\x85\xbc\xa0\xf5QT\xa8\xba'\x02\x10M\x90\xd5k\xcc\xed\xe2-\xe5\x9f{\xad\xbcg]\x14\xf1T\x8f\x18\xfb\xf0\xfa\"#\xac\xd7V\xdd\xace#\xca~\xe4i\\|\x17\x04$+\x7f@\xf5\xaf\x89\x9f30})\xe6v2\xb0\x8f\x11\xba\xedY\xa5@\xf4\x11To\xa4\xdd \x8c\xceO\xa6\xac\x08\xbad\xea4EZ9\xd1\xd3\xe5\xb4d\xde{j\x00\xe1>\xbb\x91BH\xaa\x17\xbd\x1f3\xabs\xafp4\xdd\xad\x96\x82X!\x15\xc4|;A\xacX\xa5\x9b8\xacX\"ka\xc7\xb4/\x1a>M\xdd\xc0@\xe4NH\xff\xb6(\xbf\xcf\xde\xaab\xdb8x\xfdw\x1bN\x84\xd6q\xb0\xeaO9\x14n\xc6\x0e(\xbb\xd7\x86\x97\x07\xbc\xf1\x17\x15\x0f;-\xfa\xe5J4D\x7f\xb6\x9f2D\xe1\xcf\xd9\x1f}\xdch/\xffG\x92\x06\xf5$\xc1F^d\x1e\x19\xd5z\xe9)C\xd2\xc3\x03=yH,\xbdN65\xac!\xa5,\xf3\xd3\xb0\xcc\x13\x8bl\x841\xefm\xd2\xc6-5p\xc8\xdc\\\x06\xa6\x0d]U=\xd6G\xd5l\xf9\x11Zi\xed\x8e1\x89\xdf\xa34$#7\xd5x>\xac\xb1\x98\x8f\x13\xd4d\xd3T\xd1\xc6w\x9d8\xda\x12\xb1\x86\xa6\xca6~\x1d\xbbj\n\"\x91m\xf5\xaf\xbe\x92\xdd\x16Q\xa4\xb27f\xb5\x84\xf7\xb2\xf5D\xdd\xf8)\x1cB\xd1\xac\xf6\xc7\xa6rIJv\x82>b\xe7)\x95p\xc5\xb0\xe9\xacJ\xcd6\xe229\xee\x0c\xd1+T\x1b\xcc\x98\xd9\xe0J\x9a\xb3q\x01\x10\x971O\x16w\x05x\xd5\x88_n\xcf\xb5)q]\xec\xcfI]3\xc4\xe4\x08\xd5i\x0e8b\xa3\xcc\xad\xcb\xa6\xa5\xad\x16\xc3\x89\xab&(L\xb0\x97\\1\xa2\xe065\xc4\xa6\xde\x7f\xc5\x0c\xe6\x1a\xc0\xc6:\x89t\x17\xfc\xe5 \x8eQ\xbeJ#]\xc6\xabA\xc8Q\xe3b\x94\xe8\x92\"Df\xa5\x9a~E\xb5\xd5^\xea`i\xeb|\x94\x1a^\xae\x99y@\x93\x03\xaa\x93y@CP\x18\xf7\xd8a\x11\xcc\xbcd\x8fk\xd0\x1c'\x8a0}U\xfe\xa5\xe1\xdb\xd4B\xc9(\\k\x86b\x0e{o0=i\xbb\xe8\xa8\xc1\xf2\x1d\xba\xb4+\x8dS\xb8\xe1\x88K\xed\x8eS\xa1\xf0\x84\xde\xe39wU\xcd;\xf4 \xd7&\x03\xbc\xa2~\xd8\x04\xbb9\x8f\x1b@]j\xfe\xa1;\x18G\xc9;\xcd<=\xc3\xc7un\x07\xdd\x8c\xb5<\x9bR\xa5gS\xa9b\xa5\x81\xb3\xd3I\xdf\xc3\xa9T{8\x89\x0bYg\xa5\xa7\x93\xb8\xb0|\xc9\xc9\xd4\x00\x15\x027\x18F\xed\x0c\xcepx\x08)<\xac\xf1\xfc\x94'#A'_G\xce\xb8\x80\x99y\xb9\xd0\xad$\x08a\xc5P\x96\xb8\x8e:[\xb1\x1c':6\x15\xd0\x1d\xf8\xb1\xd0\xa6mQ\xafkh`\x91h#\x13\xa1\x8du\x1aZ\x8b\x90iH\x8cC\xaaO%M8/\x0c:I\x803\x07]u\xce\x8c\xa2\xc6\xe1\xa1.m30\xbe\xa4\xabK\x9aa\xd9\x0f\xa5\xaa\xc9\xdc\x15\x0e\xae\xe5\x87\xc0\xfeT\x85\xfeI\xad\x84U\x14\x85n\x15\x83\xde!\xa1K\x8d\xe7;$u\xe9'C\xeaGX\xd6\x99\x83\x98\x85\x98U\x8a\x1a\xb9'-\xfb\xcf\xaf\x85\xa4\x16\xa7\xea\xa0\xdf\x9b\xd6\x03\xf8\x1c2\xb9\x84*w\xacP\xe5\x8e\x15\xaa\xdc\xb1B\x95;V\xa8r\xc7\n\xa5\xe6\x8b\x98?\x91Z\x10\xdcP\xd8\n\xc2\xcaV\x80\xbf\xa6\xb7z\x05\xa4\x17R\x8b\x03\xaa\x07Te\xa5\xc3\x8fo\\X\xd9\x1a\x17\x88\xc4\xb6 C<\xb3hkjo);O)\x0e\x8d}\x914\xc1'+\xf2N%$n\x90\xba<2)\xb9\x12\xe6\xeb\xd3oF\xfd\ns%\x92\xd1m\xf9\x99\x8b*\xec\xe3\xd2/uJ\xeb\xbcO\xb2\xbbK/\xae\xf7h\xd82\n\xb4\x9a\x11\xc8\xcf\x9c\\\xd1Z\xef6\xfa{Q6\x84\xf4\xe8\xa5\xb8\xa4\xc3q\xfa\xac\x1d\xfd\x94\x02\xbf\xe1\n\xdd\x94\xaeF\xb3\xca\x08-Z\xe0RK\x1d*3\x9aP\xfeB\x0d\xc3\xac%\xe6\x02d\xccbb\xe1\x9a\x13\"\xa0Y\xaf\xb8B8\x9d\x12t\x8b\x10v\x9a\xdau\x0dk\xd0\xd4.\xab\xfeYhj/\xf8\x0cVx\xa4\x06\x9dW\xa0\xf6\xf6\xb1S8\x84\x95\x17%\x0b\x92c\xaeS\x8d\"\xe1\x0c\x0ea\xc9\xc5!5\xd4\x11\x1c\x82\xcf8u&\xe2h\x93\xfa\x9d\xd7\xd0\xe4\xdc_g\xb1>\x07\xe0q\x0d\xced%\x0d\xec#8\x84\xadU'\xdeqH\xe1P\xc5\xe5Q%\xfcw\x0c~\x9d\x86$>b\xbd\xd6\x81\xbf`\xe06%\x80^2\xd0*.\xd3TL\xe75\x83\xb7Tp?\x17\x9b\x16i\x97'\xa1Q\xf4\xc8\xbaPP\xf1\x05\xb8g\xee\xc8$/>\x15+\x84\xc5\xb2x\xc7\x9c1<\x7f;\xe6\x8a\xe7\xe7~6r\x7f\x7f\xdfe3\xba\xd7\xafp\x08O\xb9\xc4\x87\x88\xe9\xf4>\xa0\x16\xf1\xeaP?4M=ma\x98#\x94\xe0\x99W`m\xa0hq1r\xbb0T\xccf@KR\x1e\xe3M\xb6AF\xee\xaf\"\xec\xd70\x9b&A2J\x82x\x13\x92W\xc4\x0f_$\xf1E\x8b\xcb\xec^\xf4\xd0\xa3\xc7\xcd\xaf\xf0\x10\xcaJy\x95\xf0;\xa7U\x9fj\xc5V\xce\x9f\xb9\x8d\xcc\x89\xcd\x151\xf5]L\xfb[\xfaI\x85\xe6\x8d9T\xd1^\x9c\xba\xbe\xe8\x01k\xda\xf7V~Q\xad\x1d\x9d\xf2\x90g\xfb\xacnQ\xb9\x14\x07\x95T\x0b\xd2\x9b\xebd\x0c\xcfu\xf3(\x99C\xcdi\xc4\x80\x7f\xc9\xa3\x92hg\xfc\xbd\xde\xfcq\x8e\xbe\xcc\x94v\x9d[\x04\x8a\x89K\xb0\xc0\x94\x1d\xa2l/+&\xf5\xd7\xbf\xe6d\xe1\x08\x97.\xda\xae\x8a\xebQ\xe0;\xddu?Y8\xf05/a\xdcF\x0bTeo\x1a\x16\xff\xd6\xbc\x9a\xb1p\x0d3\xbe&\x16\xaey\xe5\xda\xb8\xb8\xe6\x95\xf2\x1893\xa4\xe0\xd0[{<5%V\xba\xa4YK\\\xc8t\xc9\xd9IqiMKw*\xcd]\xaeQ\xf2)\xe3\xfe\x9aW\xdb\xa4\xc2h\x9by\xf68[(\x8f\x19\x17\x97,v\xbc~V+-(J_\xd6^b\x1c\xeb\xf0q\n1A3\x06A\x05\xe4\x1b\x92\xa2\xf7\xf9\x18\xde\xed\x98\xdc`\x07M>8p\x03\xdc\x0ds#\xd7l,'\xf4K\x9f\xb9\x85+\x03\xff\xafN\xdd>D\xd7\x1f]\xa1\x9a\x7f\xb0n\x7f\xe7}-[\x8bn\xab\xa7\xa7z\x93\xa1\xaa\xf1\x17\xba\x86E\xd5\x1f_\x94)l\xd8&T\xa7\xc4\x18\xce\xcc\xbb\xcdj\xacL\x9dWQ\xf3\xe6\xd0\x1b6Y\xd3\xcet\x84@2\xf1Q\"\x11\xd6\xa8\x19\xcc5[o\xe84\xbe\xb60q\x1b8\x1e\xf5\x94\xb4\xec\xd7|-\x04#E9\x9b\xee-\xef\x1da\xc7(\x88\xc4\xd5\xc7\xe4\xb7^\xd2\xb9\xe6\xd51\xb1\xcb\xf4>\x8a\xf5\x1e\xc3\\\x9b\x83q\xed\xc7\xb5\x83\x81\xc3\x9d=\n\xd0E\xa1 \xe1\xa8^ar\xa43\x1a\x83\x03l\xe9\xbc\xda\x06Uq\x9b?i:\xf1\x9d\x16\xc5+K\x89u\x9a}MV\xfc\xa6Z^S{\xb1c\xa2\xd0\xd5^D>T\x88\x02L\xb5\xfd\"\x0fIN\xc2\x91\x9bhV\x94\x1fB3\xf8I\xb1p\xd5\xd4\x1di\xa6\xee\x91n\xea\xb8h;\x83#\xeb\x99\xd3\xf7e4\xae\x04\xfc+\xb5w\x0e0r\x1e\xc3C8\xf6\xcaT\xc6\x85v\xa2W\xba\x97\xe1\xc0}i\"T\xc8\xb5i\x14<\xf4JpP\x06 :B\xad\xfe\x11,\x17\x064\xa4p\xa4\xad\x87Yo\xdf\x9fR\xe0\xaa\x92j\x95{\x1f\xbc\x94\x05i\xa5\xb7 \xd5fCF \x85u\xe8\xf7\xf7]s\x89\xcc\x9a\xd7TL6T\xffm\x9b\xd0\xea\xbf\xf8\xcdke\x13Z)sG\xacTQ%+UT\xc9J\x15U\xb2RE\x95\xacTQ%+\xa5Mh%lB+\x8c\xc8\xbf-\xb5\x04\xb1g\xbd/W\xe6\xa0\xf6\xedP\xf4]\x91no\xf5\xf1\x0dE[[C\xd1\x97(\x94\x8e\xd1\xca\x14\x85\xa2\xb7\x88d~^\x90\x90oq\x85X\x85\x91\"\x1bt\xdd\x7f\xd9\x04\x1fd\xf2\x12!)\x9c\x1bSk3\x99\xff|\xa9\x16b)\x10S\x91@\x94\x14\xa5\x9f\x04$]\x00\x0b<4\xebC\x12\x1e,\xf9$\x8aQ=\xa52\x8f\x89+\xf1R\x16\xc6g\x91\xc3\xa0y\xe56\xe6\xb5\xe6\xd5] \xca\x0cobydn\xf3R\x9cD\xd5\xe31~\xca\x0f\xbf+^\x93\xf3\xd2\xd5L,\xd7\x1bZ\xf7\xbc\xd3\xe3\x92\xf2\x07\xac\xaa\xbbN\x03!C\xafO\x1b\xa4r\x95\xd9\x02PN\x90\xec\x15\xd7\xea\x88W\x07a\xec\x942@\xb9)\x95\xbd$b\x7f^\xa2\xabWc\xd5\xb4\xb4d\xd6\xc1g\x16YB\xad\xccu\xac^\xc9&\x97$T\x12\x17\xabR\xc2\xf9|5\x98_\x9b;Xz\x8d\x87\xf0\xfb{\xd0\xba\x0fo\x06d>-\xdav\xa3\xd6nT\xbf\x85\xf5A\x06X\xd5\xe8\xc1\\\xfb\xf2\xa1\xa6\x8b\x92\xcf\xc7~I\xb0\xbe\xe8\xebhMt\"\xf4\xba\x9a\x04\x8d4$\xc9\xf5\xd5\xbc(\xc5\xa7\xcb\x92\x8aL\x0d7\xffo\xc3\x87\xe9_\xad \xf6\x9b\x91W\x92\xa2t\x93\x11\x05\xf6O\x1c>#\x93\xc7Q\x91\xa5\x05f\xe6w\xde\xd2\xe3\xe3\xa6_\x96~\xb0\xa2\x07\xb5xI\x05.\xbe%4,\xa1\xdd\xb7\xa4\xe0\xbd~5\xb4G\xec[\xf4h\x82\xd7\xb9\x9f\x14\x0b\x92\xcb\xba\xd6|\xa3\xd75\xeb\xcfI\xdf\xd0(\x8f\xe9*8\xf4\x98u Jx\x9c\xb9\xe9$\xa4[\xf9\xa2\xca\xb1Q\x92\xf3\xf2\xe6\xaa\\\xc7\x16\xban\x0c\xce\xe9\x1e\xf0\xc2\xcaV%;(\xa5\xc9\x0ed\x17K\x80pa\x84\xed\xca?\xb2\xebT\x9f\x94`n\xf1\x8938\x84\x93\x0b\xca\xd0\x15\x9byQ\xe6n\xea\xc5~Q>MBr\xfeb\xe1:7\x9d\x11\xdc\x80\xe9h\x0c\xa7o\xbd_\xd3(q\x9d\x99n\x9b\x8a\x0b\xed\xfc*D\xd5l\x08=\x13\xd4\xc9\xfdpdZv\xe0K\x7f^\x99{\xc8y\x99\xfbA\xf9\x84\xe7oz\x92\xa7k\xde\x8fF7\x98W\xc4\xc8=2\x18\x84\xe8\x85!<\xb43\xcc\xeaG\xe7\xf3\xdc\xc0 i\x9fR\x1aTy]\xd6\x99+\xe8\xc7%\xb7yB\x8b\x17\xf9\x8b\x8c$\x1c3/eIq|\xa3\xc6\x16\xaa\xfa\xec\x06\x07\\\xd8\xa9\x06\x8a\xb88We3hw>\x863\xfd\xa4\x83q\xe2\x9bYf`\x11 #\xff\xb5\x9aM\x91\xcbc\x06g\x83\xc7\xa2|\x81\xb3\xdb\x14\xf1\x94\xe3`)u\xb8\xce\xa8\xfa2\xe7< $%\x96\xd6\x86\xf9\xa6\x84\x8bt\x93\xc3\xd7r/\xda\x99f\x96k\xda\xe7\x06'\x84\xa2\x81\xdbN~\xc8x\xd7\x9b\x14\xe8_7\xb3\xd8\x8f\x92\x9b\x8d\xd9\xff\xc8\x036\xf0k\xc2\x88\xa7\x181\xcc\xe0\xe6\xff\x8d\xd6\xfe\x92\xfc\xebf\x0b\x87\x12\x8f\xbb\xfd\x14\xaeSl\x97\x8e\xd6\xb0\xd1\xa4\xf9\x0e8\xa8Fv\xc0\xd1+\xdb\xd7K\xed!\x80\xf9\x9ed\x9a\xcb\xe6\xb5\xf6\xcf\x7f\x89\xc2r5\x03g\xba\xbf\xff\xff\x93c\" \xe5W7\x94\x073\x1d\xbb\xa8\xd0\xc8\xf0\xb9\xf37a\x94v\xe6\xce\xea\xb8P\x9f\x8d\xf4\x8bzC\x117G\xaa\x1d\xb1tA\xd1h\x1c\xd7O=\x9d\x11]\xado\x96\xacL\xb5\x89\xe8\xc48\xcc\x7f\x88n\x1f\x04O\x17P~\xfc\xbdQ\x9e\xcbtE\xe22o\x0d\xee\xe4\xf5-\xec\xc3C(lw\x80z\xf9\xad\xcd\x7f\x91:\x9c\xf1M\x92\x93 ]&\xd1\xbfIX\x99\x89p\x8e\xbf\x16\x81A\x94\x89\x10A\xee~\x81\xd4\xdd\xd3E\x8a~\xca\xd9/4\xa4\xf8\xd3M\xe4\x06K\x91@\x99\x8a)\xad\x8d\xf7Z\xb7\xa5\xe5\xa5q\xa4\xe1\xc5Vg,\xc0\xb0Tz\x9e*]\xab\xacm\x916UH\x98Yu'\xcb`\x95\xef\xd0}p\xf7\x8e\xc4\x88\xa7\xd7}\xd6\xbe\x9eY\x1c\x95\xeeM\xf7\x9b\x7f\xdd|x\xf2\x7f\xbf}{\xe3\xdb\xd1\xcd\xe5\xc8[DqIr\x0b\x0fK\xfe!\xc7\xa9\xb2\x0dEkY\"\xdc\x8e\xfa\xba\xdd\xdf\xc8\xb6\xbf7\xbf\xf9\xd7\xcd\x1b\xac\x9b\x9c\x11 \xda\x0f\xfb\xf6\x1f\xc6\xaf\xfe\xeb\xa6\xddw7\xb6\xdf\xb5\x9e@\xec\xc0\x9er\\\x80\xc8E0\xef\xf0^$~\xf8\xbdn\xd6\xf8!\xcf\x9d\xd9\xed\x850JuM|\xf0-Li\x13\x0d]Gm\xcb\x9b\xbe\x85\x87\xed?g\xf0\xbb\xe4\xdcg\xb1[\x82\x83\xed?G\xbd\xad'a\x89\xfb\xa01\x1c\xca\xf4\xa6\x01\x1c\xc2IGeSg\xb2\xa5\x7fu\xe2\xac\xe9x\x17c4\x07\xbb\x0b8\x042\x86\xd4]\xd8\xb8\x13\xf3uR)\xeau!]\xec\x14wK\xd6^\xe4\x96\x94uq\x1e\xc5i\x11%\xcb\xd7\xfe\xd2\x81\x19l\xf8\xdd\x17\x19I\xea\xbb>\xbf{L\xe2E\x1b\xdeyM\xe4\xb9\xbe\xe5\x01\x81\xed\xa3\xf7\xfdH\xe2\xba2\x86TeR\x8eLI\xeaX\xfdq\xa4\xe8\xbd\xe7\xad\x81R\x1e\xdf\xa7\x88\x15O&\xf2\x9e\xd2\xad\x95\xbb\xc9\x18b\x85\x92\x0fK\x89\xc3\x0d\x88\xfa\xef\xa3b\xb69\x83us7n\x8c\xa1\xd0\xd9Y(J\xa4'%L@\xe7\xbe\x1dVP\x07\nM\xa1|\xb8l\xb9\xf0\xef\x0c\xe7 ov\xbb\x1aV\x8f\x109\x1d\xac\x9c\x057 ds\x0f7 \xab~ET\xe8\xc4\x80\x05\xec\xcd\x18\xb0\xeb\xc6\xf0kh\xd0\xa6\x0eN\xb4\xc7\xc3\x81\x02o\x91\xe6G~\xb0\xb2\xdb\x1e\xd9 yK\xf7_\xf7\xe4\xa42jfw\xaa\xf0/\xed\xedu\xfc%F\\\xfb\xfb\xaf\xa6o\xe9%\x12\xb6\xde\xfc\xfb^\xdd\xc0\xdf!'\x19\xf1\xd1vB\x99\xbaoVe\x99\x15\xb3\x9b7\x97Q\xb9\xda\xcc\xbd ]\xdf\xfc5M\x8a`\x15G\xc9;\x92\x977[\xf0\xdf6\xbe\xd4\xfc\xe8\xa34\xbb\xc8\xa3\xe5\xaa\x047\x18\xc1\xc1\xfe\xf4\xf6\xe4`\x7fzg\x0c?\xa6 \x1cW\x1f\xf3\x9a\xef<\x8b\x02\x92\x14$\x84M\x12\x92\x1c\xca\x15\x81\xe7O_\x8b\xdbM\xd0\x9b\xd5od\x06X\xd4c3\xb3\x842\x7frw\xdeq\xe3\x08Ab\xaf\x12$\xc8\x08\xcaU\x9e\x9e\xa1\x9d\xe1\xf5EF\x8e\xf2<\xcd]\x87\x9cgL\xdd\xe6\x03\x7fI\x92\"y\x8a(]\x8e*^\xa3\x0fr\xd0\x05\x81\x1b]0\xe1\xa9@\xc4\xc1\xf4w(\xfb\x1f\xca\x19\xf7A\xa9~\xc3\xce\x98\x8fX\x16\xf4\xfe\xc4@S\x9d\x97Vg\xde!\xc5\x1b\xde\x97\xca\x1e\xb1O\xb1\xa9\xfd*z\xc7|\x8d\xa5\x00\xaa\x97\xd1\x0d\xe3[\x98~=\xa2''\x0b]qS\xb8q\x88F\xf8\x12\xbe\xfd\xf6\x10\xa6c:\xc4\xc3\xee\x18E\x8b\xf4P\xe2o\xb4\x1a\x1f\x86\xed5cxw:2\xe1\x82\xc2\xbb)w\xc9\xc8+\xd3g\xe9\x99\xa8D;\xac\x0f\x1f\xdd\x99\xed3,\xfe\xba\xa82\x1b\xd0_\xf7F\x7f\x8e\x82\xaf\xdb/\x05f\xd4\x05f\x84\x17\xfd\x80h8\x81\xe0\xb9\xaa\x8a\xf6\xa8\xe2\xa8\x8e\xceKM1\xef\xb4[\xb2;U\x97\xecN?\xbeZ\x88 t\x9d\xb1\x98-\x8b\xe6z\xddReh>t\xb7Jy\xa7\xd3Sr^\x92\xa4\xe8\x1d\xf6\xef\x99\xe7\xd4\x0c\x9c1\xf0\xa3)1\xd7\xda\x8e\xae\x1bB=e\x9ecG\xeb\xac\xbc0\x94\x89\xef\xc5\xd4\x8a*\xf1\x98S\xb5~'\x12\xfa\xc9\x88\xeb'\xafU\xc5x\xd5\xc8m\xf0\x10\xb1B\x85\x88Q\xc1\xbf(9\xea\x98\xf9S}\x02\xfb\xfc\x0b\x8f\xa3\x02)\x9d\x14\xa1\xf9\xb9\x8f4\x0f{\x8d\xda-\xf4\xf6\xbb\x0c\xaew\xf4\xa9-\xd4\xa7\xad\x9c\"\x0e\x9d\x96\xe9r\xa9\x11>B\xdesY\xfa\xe7\x9e\xeb\x86\xba\xbfQ\x92mJi#\xcc\x04\xee\x04+\x12\xbc\x9b\xa7\xe7\x12MY\xa3\x0b\xfd\x87\xf8\x1e\x1e!\xa8t\x90(tj^\xc9\xac\x9c\x8c\\Q\xc1\xda\xe3\x1f6\x1e\xb7\xa318\xc7$ \x01'\x95mL\xa7\xe7#\xf4Y\x95\xe8\xff\xa49\xa1\xe5&\x93Pj2Q\x94\x93T\xa4\x88\xbeu\xd0\xcb\x0b\xf0%\x17\xb4\xdc\xb0ag\xd4\xb0\xcd\x05-v\xe0.f\x82\xa1\xeeG_}\xd5\xfa[-F$&\x1bD\xc3\x02\x90TC\x18\xb9\x89'$\xc618\xcc9\x03\xad\xcb\x88\x13\xcc\xbaLD^\xc2\x84\xd5PB\x91\xbfOG\x9a\x96\x14\xebCK\\\xdbai\xb2\xad\x94\xc8y\xad\xc2W\x03\xa5\xd6\x9af\x1fS\x1aX\xc9\xb4\x9b\x1a\x94\x8a\xc4\xda\x05IxT6\xce\x15.\x04N\x1e\xe5\xe4\xdct\x0c\xfe\x186*S\x10\xe6\xf3\xe6\xd5*X\xcdA\x8b\x8c\x05\xc2\x00c\x9ci\xc6KX\xea\xf6\x13\x10u M\xd3\xc8\xca\xb5WHg\\\x18\xb5r\"\x19C\xae\x98\xdbF\xf4\"\x96\xf0`k!\x0e\xb3\xaf\xbe\x02\x07\xb5Y\xb8\xdf\xd2z\xa1t\xfa$\xc1\x9a\xe9\xa2\x96\x01\xcf\xc3\xa88>\xf3\x97K\x92\x1f\xa0N\xd6\x87\xaa\x8d\xf3I\x9d\xf9\xf6\x8f?\xd8]L\xcf\xcbi\x11\x8f\xed\xad\xefW w\xabT\x8aj\x88\xc67f\xd8\x0b\x9e=\xea\xab\xaf\xc0m\xf4A\xd1\x83\xddZ\xaa+`\xef \x07\xb0\x1e}tY8h\xb2Y\xcfI\xfe\x9a\xeb\xc7F\xae\xaf\x88\x93\xeb{q\xc90\xdd\x1d}\x9c|\xedU\x12\x86_\xa28~E\x02\x12m\x91;\x91\xd5\xdc\xb7\xce\xc5Ps\xea\x9fxw\x99R\x88G\x97\xda\x83Hd\xa2\x02 \x1b\xee\x84\x1cf*3\x9a\xcd\xeeJ\xab\xed\xe4F\xad|\xd4#q\xa8\x07,%\xf5h\xc4Q=\xd9\xac\x91w\xf5\x81\xe5b\x88:\xf7u\xad \x17\xcd\xc6{53lJoP\x18\x86\xd2\xd84\x1b\x8c\x03\xa1\xff\x9d\x893#'\xbfm\xa2\x9c\x84\x8cT\xe1\xae\xf2\xd9\x19L\xf72\xba\x89x\x8b(/J\xb7\xb3\x01\xb1\x90e\xc1?+jZ\xdam\xc7bTe\xd1\xee\xee\xb4\xfe\x86lo<\x99\x18\xf4\x01\xbc\x05\xec\xce+\xc3q\x9fX\xee\x8f|@V\x8e\xb4\x865\x98\xcb#.?sm\xaf\x9e\xd7 Z{\xfe\xa6%\xaa\x0b\x95\xb7\x1e#\xad\xe9M`Mo\xc2\xea\xb3\xe6\n\x0f\x85\x91\xde`\x95\x07cj\x11\xafX\xa5gGB\xdde(\xef\xc0\xa0\x1f\xa5\xebu\x9a\xd8\xbcs\x81^\xd9\xce\x8fE\x9a\xb0\xcc\xe7O\xd2|m*)\x9b\xbb\xcc\x98\xfc=\x0b\xaaQ\xc2\x9e\n\xc7\n\xc6n\xa8\x01\xcf\xe0\xb0\xc9\xa2\x9c\x9a\x0b\x98\xceM\xf6\xac\xb6\xc1\xc9`\x15Y$Zk6\xd4\xf6#\x83\x95)\xa8\xec3\x85W\x15S\x10\xd8\xea\x06\x06\xbbP\xd0\xf4\x8f\xa2\x9fh\xa4\xf3\xc1{\xf4\x135\xcd$E\xd9\xc8\\hot\x92\x91I\xbbwk\xf3\x93\xa1\xf4X\xc3\xc2\xa3\xc9\x05\x04\x83\x8b\xb65\x8dL\x81\x12R\x97\xe1\xe4\x88\xe1\xafm\x0d\x8ds\x06nSC\xe3\xb8\xb13\xb8\"\xddT&\xa4 \xde\x94!MEC\n-\x93\x12P\x89^\xfd\x81\xef\xea]\xb9H\xf3\xb5\xaf\xed\xe5\x0b8\x04\xf4\x81^!7Rv\x18\x11\xed\x86x \x87\xf0\x82\xbdP\x1a\x10\xf45%\x00\xb47\x8f\xfd\xd2wL5\xf8\x9eS\xe8'\x15t\x94\xd4\xa1\xe5\xea\x97\x9e\xd6\xc3\xae\x19\x0e5\xf8\xaf\xa2\xf3(\x0cD%Y\x17T\x16\xc0\x81t\xab\xc95\xaf\x9f\xe0\x10\xde\xc1Cx\xd7\xe5\xa1\x1cM$\xe7+8\xc4\xc0GW\xd4\xa2\xe8\x12\xf0\x91[Vy{\x95_y\x0c\x87\xb0n~e\xe0\xfb\xcf,\x12Y\xbd\xb1\x80\xf9\xcd\x02\xe6 \x1c\xc2\xdeT\xab)h0z\xcc\xe9\xfeY\x8dOl=:\xec\xe03:v\xda\xc1gM\xbew\x8c\xfd\xe1\xb7\x84(\x87\x86\xe37\xf5\xf7\x04h\xe3koh\x9bo\xea\xf0e\xda\x03\xec\xf5~\x1b\x8e\xf5\xed\xb7\xfa[U\x1b\xe3f\xccB\xd9\x15G\xb1\x02FWL\xd6z\xa4\xe8\xf3\xf6\xb3\xdc\xfbH\x17&\xa8\xb0\x99\xd9\xba$4\xdf\x8c\x12\xa7\xe5\xde }\xe9\ns\xf8\x0fq&\xba\nC\xffSx\xd82#\xd2\x06\xa1\xa2\x070\xeb=T\xf6\xa6=\xb9\xf8au\xc6\x00VF]\xddC\xabT\x0dA\x1ac\xbe\x10\xdaS\xf5\xd9\xa7\xea\xaf\xf3?\xff\xef\xefN\xc3\x8f\xee*f\xb39Y\x9a:\xe9cx9\x86_Q\x0fu\xe2\xc0\x0d\xf8\x15n\x80\xf3\xd6\x19\xc3w\x18\xc2\xb7\xf3\xac\xb5z\x92\xa7\xd9\x84\x9fg\xca)p\xffJ\x1b\x1d\x833\xd2o\xb5\x1d\xa7 $YN\x02\xbfT\xad\xcf\xfbq}\x96\xd6\xdb\xbf\xf1\x16\xc6\x846\xfe\xfep\xab\x15i\x9c\xe4\\g\xdcb\xdbq\xba\xc6\xb0\xa4}~%\x94\xe3\xaf\xae4G\xfa\xb1\x89\x9dgnW\x14o&\x14\x83\x0c\xeeR\xe7\xff\xb0H\xa9~\xfe\xb3\x1f\xeb\xcb\xb0\xc8g\xa8N\xa0\xbf\xa63\xf2X\xcc\xc8\xe3\xff\xf8\x19\xb9\xc2\x1a+;8wV\xdb\xa9\xe1\xe2\xa9!\xca\xe7Zz\xcc\xeb\x9f\xc8\xbei\xc2\x8a\xbd3\xd4\x0b\xc3\x1f\x7f\xc0\xde\x13\xb3$\xab\xed\x87\xca\xf9\x85\xb2+\xea\xb5\x14\xbdw\xbe\x89\xbe\xfdn\xebG1\xa6\xe2@V\xb4\xf8\xe6f\xf4-=\xe6\xe0\x06\xbc\xb1\x88\x8eo^\xc2|\xaa\xc1\x8f\xda7\x8f\x07\xf5\x8eU\xc9\xcd\xde\x8fZ3\xd5\xe0\x94~\xfb0s&\xd82\xbbi\xe3*A6i\x8d9\xfbM9\x98\xd7t,{\xcf\xb5'Z+\xcb\x13\xc6\xdc\xce\x0cY\xed*)\x07\xcb\xebP\x94\x8a\xcc\xd3\xa3\xad$o\xd0uX\xebM\xb8N\xf3'5\x84`\xabf\xf0T\x0d\xd4\xd8Z\xf2\xedVK\x9d\x8c\xd5\xa2\x14\x0f&\xd0p\xb9m\x83\xcfXx\xbd%\xef\xbb\xabV\x84\xd0\xc5+fB\xccc\x7f\xea\x1a\x12\xf5\\^(\x11\x087\xc3\x0b\x0d\xc5:\xd2-\xab\xf5\xba\xd5\x0e\x96\xdd\xba\x88\x06\xa4\xe0\x0e\xd9\x9a\xacVvZ\x1f{\x8d\x8f\x98\xb3\x8e\xd6A\xb3*\xa2\xf6\x8d<\x89\xa5\x84H\xefX\x01G\x816M\x1d\x8en\x9a\x84K\xda\xac\xa9\xc9\xa9\xec\xe0\xc7\xa4,\xa3d\xf9$\xcd\xdd\xa0'g4\x183\xcdD\xd4>k3\xf8\x89\xb96PY\xf5'\xe4U\xd4\xaf %\xa7~\xf6\xae\xca\x89\xf9\xfa\x97R T\xaeT\x81\xca\x95*P\xb9R\x05*W\xaa`\x98+U\xe0\x16\x8d\x8e\x06jO\xe2\xe0\xe3\xfb?-l\xfd\x9f\xbe\x04\x98\x0b@\xfb\x00\xf38\n\xde}j\x87\x17k?$R[?4goevS\xc30\xcb\xe0\x1aU\xferma\xe2m\xfd8\xe2\x85\x1e\xfcu\xe1\x9e\xa4c\xf0\x91\x02UO\xbe'\x8b4'\xfcp\x12\x00\xa8\xb7\xe3\xb3\xe4\xa5 \x7f\xca|::7\xdd\xd1\x18\x12\x8f\xf0?4\xc7\x82\x18\xb4\xf6\x04\xce\xf0\xf4\xd5\x9c\xa3kn\xe1\xe8\xfb\xec\x02\x12*\x837\xda\xcb<\x0d7\xc1\xb0\xb8\xfe\xca\xdb\x8f\x8d\\\x92r\x80\x7f\x94\x19\xc9O\x04 \xae^\xf5\x1a\xeb\xf8\xdb?i,\xbf)\xf6y\xce\xa2\xabme\x93y\x99\x00G)\x10\xe1G\xfc\xd8f\xa9\xa6\xae\xdb\xb1\x8d\x19X\xee\xab\xb2\xc6H+\xa0I\xd3\xc9\xf8\xaat2\x1bU:\x99B\x95N&\xe6\x0f\xe4\x15\xd0Z\xb9c\xaeY\xc6\x98\xfeG\x84\x1e\xfa/\x0f\x1e<\x90 \xe9\"M\xcac\xa6\xcfv\xa2\xd2\x8f\xa3\xa0\x1b\xa2\xd3\xfa34\xd2'\x03\xe3\x00m\x1a!)\x83\xd6\xab\xbb\xa4\xf6\x93\xee\x94\x1fc\xc72\x03\xaf\x18\x02#\xff\xdb\xe9\xd1\x8e\xa5\x9b\xc0L\xb9`\x00\xf5\x82\x81\xfeEP\xb1\x08\xc62@\xc0\x19\x04:\xac\xb6\x17\xd1\xc8u\xc4\xd6V\xf9\x05C#\x94\x06\x9ae\xe1wVyC\x87\xd0\xf2\xfe\xeb\xe39\x01\xf46&C>\x06\x90\xb7yz\xaaI\xca\x00\x9c>\xff\xc0\xcb\xa9\xea\xe3\xe4\x8dI\x06@\xde\x85\xdd\x86;$\xd3\xc0\xd0.M\xf2\xf4l\xd7^\xed\xd2\\\x90\xc6\xfa\x05\xb8l\x92\x02\xd8\xb1\xddV6\x82\x8f\xdf<\xf3\x1a\x1a\x90\x05\xa1\xf4HR\xe6\x17\xb2\x12\xb9&\xdd\xb1\xf0\x01\xee\xc8?d\x0c\x07\x06\xbf%\x10\xee\xbb'\xfb\x9ax\x10q\xa1\x0b\xef\xc9\xd4\xa2\xda\xcf\x9e$\x1f\x83\x1b\x8d\xaa<\x81\xeaL\xd5\xe2\x12N\xbc\x91\xd7\xf1\x19\x7f;\x12N\xb4\x1dOr\xee=\x02\xb3\xc6S\xa3G\x89\xb86\xb2\xa6Z\x0e\xec\xfa\xee\x9a\xd8W\x8b\xbd\x0c\xe2HJ\xb5`\x97\xf0\x0f\x10\xd7P|\x06\xd6lz \x13\x94\xb8vl:\x92(\xa3?]o|^Fb\xa39H\x13\x9b\xf6)\x97\x80\xb6CGx\xcb\x991\x95\xbe\x83\xa6D\x83\x97\xa0\x80\xe5\xdcb\xa6\x1f\x94F\xfdX\xc3t\x93CHS\xbd\x83\x94c\xeb\x88?x\xcbP\x82\xba)\n\x85x\xf7\xba\x89B\x9fT\x83\x19\xc8\x04\x1e* \xb9\x81\x10xP\xdc\xf93\xa8/\x1b\xfc\xbeDK\xd9g\xf9m#5m$\x90k\xaa/\x19\"m0I\x83\x84Q\x99\xe6F\x0d#SF\x92<\xb7P\\2md\xec_\xa4\x9b\xd2\x02\xbf\xb3p\xb9#\xcc \x884\xdcH\x18\xe55\xf8\xf3\xd5\x07\x84\xcaL\x04\x82gv\x8a\x8c\x04\xe6\xe1\x84W9\x9c+\xeb<\xf3\x0b\x93#\xc8h\xa7tj\xb6\xfc\xfc\xa2\xcdL\xeb\x93\xa7C+\xcc\x19gA>\x05\x0c?u\xc7;\x9e\x95\xa5\xe1h\x14\xec}\xd9<\xa2\x94V\xea\x9d\xf6jo\x9f\xaa\x8f\x9f\xf7c,Mgh\x86\xe9\x90\xf4\xa7\x87\xd031\x7f\x1fVg\xaf\xe9+\xcd\x99\x0fx\x08+\xb7\x03\xc5\x1c\xc3\x1a\xae_\x02\x16Co\xc4\xcd\xcc/W\xf8\xbe\xb2\x1f\xc5\xda\x8f\xe3F-F\xbf\x84\xee\xeb\x0d\x7fW\xf5gt\xce\xebFw\xff\xb3UT\x92\xe3\xcc\x0f\x98k;\x99\xe0\n\xabw\x95U\x15Gi\xaa\x01>\xb05)\n\x7fI\xb4\x07\x8b\x16]\x8cC\xc2\x8a\xa0\x93\x90\x04)3\x91;3p\xb0\x12\x8aah\xc1&/\xd0\xdc\x94\xa5QR*\xb9\x1f\xd9\xd8\xb0\xb6\xb5\x8e\xe6i\xaa(W\x07\x7f\xe2\xcd\xa3$t\x19:\xe4R\xbb\xb6\xf3\xe3f\x9dA\x99\x02\x1d\n\xc5\x96\xbc\xd6U\x88\x1fm\xb24\xd4\x04\xb6\x13m\x91C\xe5\xbc\x8c\x8f\x92ZtwJ\x8e%h\x9fEE\xe9E\x05\xfd\x8f\xdb\xd9\x0c\xf6\x9bI\xb2\x97\xb8\x9f\xb0\xc7v\xd5%>\xc4\xd2\x804\xc8!\xfa\xe3&\xe8\xe5\x91c\xcc\xa4\xdd\xa7\xd3\xa4Z\xc6\xd6\xe7v\xde\x19\x9f\x90\x90Z\x13I\x0c\x0fB\xc4\xfd\xc8$\xcd~3\xff\x99 \xd5\x95\xd2\xa86\xd6Z\xd1\xab\xf6+\x06\xda%\xd3\xd6\xad\x94\xda:\x17\xd3k9\xce\x88W\xa4t\xc0\xb1\xb1\x1d \x11\xfcd\xff\xadW\xa6o\xe8va\xf5\x8a\xe0\x06\x10\xaf\x88\xa3\x80\xb8\xd3N\xc7\x04-\x81^\x1d10\xa7\xccm\xf2\xa4-\xa51\xfb\xc2\x17\xbd.\xbf,\xf5\xbaA\x95\xbb\xefO\xa3\xe1\xfd\xe2\xa0jQ\x01\xe9\x12>\x87\xe2\x13u\x12O\xdc\n\xd7\xd0\x93\xb0\xca\x92\xf58\n\x9f\xa7\x9bD\x16Td\xab$\xaf\x95\xe3\xcdl\x1fE\x95\xce\xa837\n\xf0*?R\x7f\xb2\xda\xf3!;J>`\xea/\xd2\x1bT\xfbN\x9d\xe6\xa9s\xbf*\x9d\xcf+)0\x9dH\x13G\xa4\xc3\xbf\xc4\xf8?\x81\xb9\xa39\x04\x93\xb5\xa3\xe2\"M\xa6\x0e\xec\xaeV%\xddv\xb3\xda\x89\x89\x82^\xc8&\x8edR^dD\xb0\xb7\xc8f\xba ?\xfe\xa5\x9f\xd1\xe9\x11\x0b4\xd6\xec\xd4\x03s\xcd\xf4\x9c\xf5J\xab\xf7\xd5\xc4\x85\xa9\x06SZp6\xe22\xe9fR\xe6C`\xa5\x953\xe8\xdb\xf8\xa05\x81\x9bR\x8fm\x80\xaeE}\xc7\xda\xe9z\xa5\xdbB\xcf\x98I\x12@\x8fzU\xa9\xf9\x08\x93^~\x93\xe6\x16cI\xb5co\x91\xa7\xeb\x1f\x8fG\xee\x89C\x0f\xb5(@.\xff\xe6\xafE\x9a8o\x1b\x9c\xe3\xf8\xday:\xd3\x1e\xbd\x10!\x06\xcf\xa2\xe4\x9d&5\xfcug\x10\x13\xf7\xb6* \xfdg\xc9\x18^\x05?\x98H\xf9\xc1\xa8\xe2\x07\x93\x11\xe3|\xf6\xbf\x86\x0d|\x03\xc9\xd7\xb0\xa1\xfc`t\xb2i\xf3\x83\x1b ?(\xf8\xcd\x0f\xc5\x08F#M\x12i\xcc\xb2\xf8\xda_\xa2\x05\x17u1\xa7\x8d\x1bLx\xa5\xccn\xa1X,\xb8B\xe6\xad\xd9\xb2\xc5i\xaf3:5\x98\xb1\x96\xc7\x003\xfd)\xf2F\xb7\x87\xa8\xe6G\xe87^d\xd7\xb9\x87\x9f\x80c\x1a\x14\xadf\xed\xf4\x91\x0fq\xfaH\x07\xa4\xcad eK\x7f\xb9$aE\xb8\x0b]\xc6G\xcc\\lv 11\x0f\xf6\x8aB;\xee*\xdd\x92|\x1b\x913S\x8d\xc1\x17\x1c\xceA\xa1p\xb0\xf56\xad\xad\xb7U(\x9d6\xaa\x1e\xf8$\x9f4z\xe8/\x0bg\x0c\xa5\xc1Y\x98y\xcf\x08\xa7\x92\x08\x1dI\x8c\xb6\xe2\x9dye\xa86M\xd5OT\xc2*_\xb8\x84\x9f\x05\xec\xe4\xb6\x00\xf5(sF\x1d\xe8\x9cl\xd4\xee\n\x00=;F\xf7jbPL\xd9\x95\xe6\"\xe9}\xd3\x85\xef\xaa3A\xa7\x87\x1b\x0e\xf3\xa2S\xcd\x89o\x9a\x90\xda\xef\xc1\xe0\x93j\xf4}\x00\xd6\xc3t\x00\xab\x0f-\x0bN\x992\x86PG\x06\xc4U\xa7\xeb7\xc32b\xb36d\xb0\x15\x17\xf33\x8b, \xe9N1$G\x05\xce\xde%\x0d/\xad\xc6\x06\x1e\xc3\xc6\xd29}g_\x0b\x10\x1b\xcc\xa2\xa7\xc6\xf8[q\x898\\C\nSzE\xe1\x0c\xd2*\x19\x93\xc5\x0bt\x8b%Z/\x9c&\xe4\x8b\xec\xa9\x19u\x9b\xc0/s\xb2\x88\xce\xb1\xb0]\xbd\x0c\xc6\xb7W9Y\xcc\xc0\xf9K\xf5\x12\x8e\xc6\xa2\xd9\x8a\xde0\xda\xa1'\x1a\xb6\xfe\xdbR\xb0&\x08&\xca\x8f\xfeM\xe0\x1bVUDM1o5\x0c\xfa?\xa5u\x9cv\x01L*\x0b!J01\xc9\x1eHm&\xad;\x03\xe5[\x83SI_\xa4\xb3\x12D\xa4\x04\xc7Z\xe4\x10\xd2\xc6\xae^\xc9\xcd\xfa1\x1a\xbe?i$.H\xbcS\xfe\x077VQ!\xb0=\xaf\xff%\xf9\xc4\xe5\xf9}\xde\xea\xc7\xe5S\xf964\xb1\xa8\xed\xed*'\x91\xcc\xc3\x98\x8fb\xe4\x9e$\xc8\xdc\xc0\x1e{[V\xe4\xbf=\xab\xd7\x8a\x81\xd7\x1d8I#\xd7\x83\x89Y\xc7\xa1\x9b\x98tJ\xcev\xe2\x9fc\x8fnE\xdd\x99\xc3(\xa5\xe6\x0c1\x9a\x99\x81\x87J\xffB\xa2\xe5\xaa\x9cAN\xb9\x9dy\x1a\xb3,\xa4I\x9a\xaf}m\xfc\x9ez\xec\xb2\xe4\x00j\xf0\x96wl\x9c\x06\xef\xaad\x04\x94e\x1b\xee\x05l%z\x08\x9f\x0b;\xe9\x83\xce\xca$\xf6\xe7$\xc6\xf3HQ#|\x0cI\xdbT\xbc\xb3/\x03(\xdbW'\x1f\xb4\xb0=\xd8\x1c\x1b\xff\x05\xd7B\xcb\xf84Y\xa4o\xf2\x18\x8f'\xfa\xfb{\xbf /\xfdr\xa5Q8JS+\xa4\xaa\xd4\n\x91*\xb5\x82\xafJ\xad\xb0Q\xa5V(T\xa9\x15\xe2Vj\x05\xb4C\xb7\x01\xea\xdc\x0b\xdcR=\xdd\xbf\x16\xa9\x17zsn\xc5\x11h\xdc(\xbeD%5\xe1\x86\x9eY\xab\xb4\xd0\xe8x\xd8\xa95\xe7\x8b\xb5\xd3q3(\x16\x84\xb64\xd9\xe4jR\xe4\x9c\x00E\x1dx\xf3\xea\x19\x96\xc1-\xd1g\xc1\x81\xb7\xbb$\x80\xd11\xb6vn\xd1\x06\x0c\x85O\x8c\xa5\xd0\x9b\x05\xb8\x12l\x053\xc6\xc2\x00\xac\x85\x81\x98\x0b\x15\xf6\x86~i\x90\x89\x93\x01\x1aM\x00h:\x9e\xf3\x94\x9c\x7f\xfc\x01N\xb9\"\x10\x92-\x89\xe9\xc9c\x905\xd3\xfa\x0b\x14\x93-\x14|\x1c\x9a\xac\xfd\xc8\x08\xefc\xf2<\x87\xb2p\x16\xf1\x1fV\x8cL\xaa\x15/mX\x1e\xa3\x86\x8aq\x94.\x96\xf5*\xfc$*\xa3\x7f\x937y\x99%r\x90\xfb\xbb\x9d8\xc5\x14\x9e\x945\xd4\xb1\xf3L\xb5\xb9\xc9c\x1d\x10\xb3\xd3\x08\xee\xc4\xe4\xe5^\xa2\x0c\xa9\x83bR[S\xca\xd3A\xc7\xcc\xea\x83L\xee\x15x\xcdc\xee\x98\xbc\xcaV\xa8\xa6\xe1\xb1\x8e\x86\xd3\xdeh\xf99\xe4\x984\x829c\x085\x06\xbc\x9a\x19\xd4\x9cZ\xcd9\xd4\xba\x91\xb6\xcfA\x85\xa3\x8d\xfa\xa4\xb8\x949\xb9y8\xb0\xda\xfe\xd7\xedp(T\x87C\xa1:\x1c\n\xd5\xe1P\xa8\x0e\x87\x82\x1d\x0e2\x92_||\x92\xaf\xd7\xa0\x7f!\xf9\xe2\xb2%\xf9\xc2/v\x97 Z\xc6\x1cXo\xa1\xf8Zn\xa1\xeb\xc1_\xf5\xf7\xd6\x17v\xea\xcf\xb2\xb7v\xd6/4u\x0b\x8b4Ugp\xfa\x8f;\xf7\xae\xc7\xa6\x157\xffDB\xd1\x97\x94B\xda\x94BO0\x9f9K\xff`4\xe5\x03\x9fO\x1ed\xd7\xc8 $\x17\x06\"i\\\xf4&\x0b\xfd\x92\xb0\x86e\xc6\xdbO\x9e{\xe8\xd2d\xf2\x03K\x9d\x83\x82\xae\xa5\x96\xfdG\xa9\xd6\x90B\xe9\x8e\x13\xa7~\x18%K\x96\xd5\xb8\xf4\xf8\x9f\xc7\xa5_n\xb4B\"\xc5[g\xe1G1 \x07\xbf\x8bn\x85^\xb0\xc9s\x92\x94\x1cC\x0c\xd2\xeb\xef\xef\xb5\x82(\xba\xde\xb9\x1b\x0f\x0b\xea\xd1\x9e\xe5$tF\xdc\xdb\xb0y\xff/\xbe\xefk\xb3\xa07%W\xfa/\x8e\x0dmw{S\xfe\xbb\xaa\x1a\x7f5\x07$\x8e\x1f\xebU\xfaQ\xb2CN\xfa|XK rf\xaa'|\x9d\xce\xa3\x98\xcc`z0\xb4/N\x94d\x1b\xfbTCut$\x9f\x05\xfe\xba\xf2\xe5,\xf6\x03\xb2J\xe3\x90\xe43p\x18\xea\xc0\xfc\x02J\x7f\xa9y\xab\xbc\xc8\xd0\xbeE\xceu\xdf\xee%*j\x12M\xf5k\xd5\xc1_c\x8aS\xe6\x1b\xe2T\xd8\xe28\xa0U<\x84U\x81qs\x14\x94\xdcn\xf6\x81\x13x_O^*S\xf1R\x99\x8a\x97\xcaT\xbcT\xa6\xe2\xa5\xb2a%\xc53\xca\x15\xb4\xeeb`L\xa6\x89\x9cY\xe0\xc7\xa6\xfbR.,\xfb\xf8\\X\x08\x87\xf0\x84\xb7\xef!\xebAwO\xbb\xcf\xfa@\x1a\xe8\x84\xd7v\xf0\xa4yYse\xc0{\xa7\xe6\x96\xec8%\x11iK\xfb\xa4Wmn\x19|\xc4B\xa3K\xbf$\xd2\n\xae\xe2\x8a\x8a\xa30*\xbfO\xcfg\xb075\x12\x0bGI\xe4#\xc3.\x86+a\x80`P\x02F\x18\xc0\x13\x81H\x95\xc3\xd8?\xacq]4\xa7\xbef\x96\xac\xcdc\xaa\xd3dx\xb6E\x90\x8cD\x9boB;\x14U\xa2\xb7\xa1#\xf8d\xfel\x8c\xcf\x14\xe7\xde\xa34)6k]\xfeD\xa8\x9c\xd62?\xf7\xd7z@\xe6\xb5\x16\x15\xbcf\xb6\x1e8\x1a\xc2\x1eC\xe5\xb7\x96\xf9\xe5\xea\xb9E\x9a\x8e\xcd\x003\x0ep\n\xbfq\x9d\xefYE\x1c\x0dk\n\x9c\x82o\\\xe759/\xbf\xcb\x89o\x02\xcf\x18\xf8*Z\xae\xe2h\xb9*\x1f\xa5\xa1\xd1\x81,d\xef4R\xf0\x99\xde@\xef\xed\x08\x8bg\xe2Z\x91\x92\xe4\xbfD8[\xfe\xf7\x17OC\x92\x94Qy\xe1\xfa\xdc\xe7<\x1fyu\xd9\x94\xc2\x19s\xd3\xf7\xb3\xa8(Gn\xf7\xc8\xea^[,\xa7\xd9\xe8\x1c\xdb*\xae\xcf?\x9a\x93\xdf6\xa4(\x1f\xd9\xf7~\xddBb\xfai\xc4\xccN*Wq[\xf8,\xc8\xde\x98\xd5\x8c\x0c%\n\xd5\x03}\xfbK\xd1>\x12~=\xec\x05\x1c\xc2\x92\x89\xc7z\xc09\x02V\x07\x85\xd1[\xed\xca\xaa6\xcf\xd3\xf0b\x82X`\xf0zpB\xbf\xf4\x19\xe4\x04c6f\x907#8\xec\xdf\x8e\x92\xfa\xdd(\xd1\xd5\xfc\x1a\xc3\x9c.k\xaa\xa9\xae\xb9\xd8m\xb0\xa7\xa7\xc8\xf0\xc3\x0dpW\x0d\xeb\xa3\x03Q\xb2\xf5\xe3\x88e\x070\x0d\x8a\x93\xdf\x0b\x03\xadk\x8b\x0e+? c\xf2\x82\xdfT\x8f\x9d\xee\xbc\x0b:z\xd5\xc8\x8d\xce@\xaa\x91\x13\xab\n\xa3bp\x9a\x1ej\xca\xae\xee\x8e\x86\x13\x96\x91U_P[\x87\x11\x97i\x9b\x84Q\xa9mX\xd5h1\xa0\xc19\xa6\xa0(\x13\x08\xfc$ 1H\xd6\x86u\x04D%\xb50*\xd5PF\xeck\xa4\xa9(\xd3\xe52&O\x05\x99\xd1\xef\xbc\x87\xe0<\xc2\x1ebG\xe8+u\xd5\x02\xcd\xd2\xb3\x0c\x0e\xa6\xf9X\x95\xeb\xf8 \xd6q\xd8i\xbe\xdb\xf1N\xceKq\x8c\x89L\xb4\xc0\xca\x92\xa9?`\xf4U\xe3\xf8\xbf\xd5Oo;\xf1\xad\x89\xeb\xa9(\x81\xc1\xf9Z\x81\x9d\xad\xe4\xcb\x9a}\xa9L\xea\xd4\xbb\xab\xf0.k\xc7\x9c\xd4\x87\xd1\xaay\\\xf6D\x1eq|\n\xdf8m\x02\xe0\xf6\x04\xe0\xf8\xba\xef\xfd\xfe\xbe+\xbfW\xf3\x17\xca\x1f<\xaaz\x10V\xcf\xdf\xb7\x95\x03\xdb\xa6x\xda\xe5\x97\x9b\x98y\x05\x89\xd9\xfdY\xcdLDU\xde\x10T/\xa5B\xbd\xa4\xd0\x1cQ6\xf9\xe6\xf9:\xbe\x19y%)J*\xceJ\xe1(\x83\x8c\xcbf\x02D\xab\x08<\x84\x84\xc7\x80\xd0\x9e\x9e\x9e\xafYu\xb0\xe6M\x99\xe7P\xb4\x00\x97w~\xef\xf0\x10\n\x9db=\x86C\xd8C\x8e\x0f\x93\x17\xfe\xfe\x9e\x8e\xb2\x903M\xc4+HyLY5W'\x1c\xe1fW\xd4\xb0\x1e\x8d\x9b9\xf1\xf5\x9eH\xc5?\xd7\xb1V\xa1\xd7P\x06(\x12\x9cK\x94u@\xe2\x82\xe0\xdc\xb6\x92\xf3\x17x\x0c\xb8\x0e\xce\xb1\xaa[\xfa.i\xbb\x83L\x88\xacEMc\xda\xcf\xb5)\x0d\x17\xf8\xd97\xad7\x14\xd1I\xafXvK\xb7\xe3R\xae$J\xbcE\xe2E\xc9\x82\xe4\xc7X\xe2\x7f\xe4\xe6<\xdaF\x9dg\x8d\xbe\xb7\xa0h|\x8c=\x16/\xa6\xa8\xefT\xcc\x07+\xb0\xf0K\x1e\x95\xe4E\x12_H\xf3]*\xe6EL{kf\x14\n3\xa1\xf7Lj\x19B=~\n\xf4\xcf\xb5\xa44\x99q\xaf\xf0}\xa2\x90\x90\x0d\x8bOw\xd1i]bc\x0c\xa9|\xdc\xa7C\x06\xee\x92N\xed\x0e\xf8\xe3\x0f\x08G\x0c^\xfa\xf96\x03>\x14\xedl\xe8p\xde%\x98\x89\x82`\xa6\x1d\n\xac\x82\xa3\x84=\xa7Bl\xcb\xe0\xea\x95y\xb4vYA6\xbd!\xb6\xb1\x85\x95ek9\x99\xe8\xc7\xba(\xb0\xb3\xc3J\xea\x8eUh\xa8\xa6k\x0c3+\xd9\xf8;v\x8aURc\xbe\x14^\xc2\xfc\xa8\x0c\xc9\xef\xe5\x96\x8e\xeb\xe9J\x7f\xdd+\x10\xd0\x1f\x0f\xee\xdf\x1a\xfd9\x8a\x10\xfc\xf9\x1c\xc2\x189|\x92\x06\x9bK\x96 \xe2$\x88\x15\x94\xa1\x1cB\x98\x068\x0e\x8f\x9c\x93\xe0Q\xba^\xfbI\xe8:A\x9a]\x98Sd\xc9\xa8\xd4\x07\xf3\xcc\xf0\xb8\x12R\xcd\xb4\x95\x9ck\x88\xeb9%W\xe0\xfd\xae\x0e\xce\xac\x8bK:\x8fX\xee&\xd3\x17\xd5T\xb2]\xbf'\xa3\xd2dQ\xaa\xb3\xcb+\xdb)\xc9y\xe9\xe7D](\x11P\x14CTj)\xbb\xf0\x8ezrs\xe2\x87\x8c7b\xb6q5dk$tZ\xd4\xa0V\x89A[\xc52/\x91\x0bT\xb0E\xf2)\xfd\xa0\xe6\xf7\xebP0\xa7\x7f(m\xe8\xa14\x95\x9dJ\xf4\xc9\xf4\xbe\xecX\xa2O\x1eLUqlj\n$\xbc\xd1N$\xa5\x08(\xe3&\xab?U\xd9|\\gE\xfc\x90\xe4EW$\xa5\xe2h\xe9e\x9bb\xe52T\xc3\x84\x9d\xec\xef\xc9?\x9d\xb1x\x9d\xe5\xd1\xc5\x18N\xfe\xf8o\xce\xdf\xb0zf\x9d\xa1\x08n\xc0\xdf\x9c\xbf\x8dx|\xf4\x06M\x12*V\x93\x9e\xaa{\xfbrTC\xb1Wa@\x0e$9C\xc5U\xe6\x17\x8a\x8dP94.\xc6h{\xea\x9c\x1b\xdd)\xf2HR\xe6\x11)\xa8\x90\x04{.\x16\xba\xa1\xc7i\xe6%\xe4\xbctG#/L\x132\xfa\x9a\x8f\xc2d\x8e\xc4L`6\xd6\x91\x15\xefZ\xe3\xc8\x0d\xc7p`R\xcfS\x9e\xedd\xdfP\xa1b\x8dPS\x89#\xa6\xb8(\x12\xad\x1b\xab\xff\x038\xdd\xd5\xde\xc2\x0dpf\x98?m\xcdW[N\x0b\xfa\x84\x00\x02\xbf\x0cV\xa0>Yc\x86\x11\xb8\xc2}{\xc1{XD\x89\x1f\xc7\xaa\x15V\xaf=\xbd\x98\x12%\xf3\xf8\xa1\xd5\xf8\xed*\x06`h\x0e\xf8\xd6\x89GP\xae\xf2\xf4\x8c\xbb\x07u/\xc9<\xfc\x97\xfa/\xfaA\x8e\x8a\xf34\xbc\x90\xa5\xd6\xa1 \xcez\x13\x97Q\xe6\xe7\xe5\xcdE\x9a\xaf'\xa1_\xfa\xcc\xd1\nG\xe6\xbc|q\xfc\x9a\xfd\xdd\xdd\xbb\x1aNa\xa9\xd9\x8f\xc0-|:\xa7\x8e\xb9f_\x82q}\xaa\xfdy:\xc6\x8c\x1c\xf2\xfd\xc9&\x057\xe7\xc51\xf9\x8d\xefN\xdas\xf7\x14\x0e\xe1\xac\xbb;\x97\xc6\xdd |\xf4G\xfd\x8dw\xca7\xacq\xfb\x01\xcf\xf5qd\xdc\x82\xc0\xb7\xe1\x91v\x1b\x02\x9e\x08|\x0f>q0h>J\x8a\xd2O\x02\x92.j\xae\xdb{\x12\xa1\xb0\xd0\xda\xa0\xe7t\x83\x1e\xfe\xffq\x83z\x89\xbf&\xf4\xef\xaf\xcb\x8b\x8c\x1c\xb2{\xf4'\xdf\xb9(P\xf7\xde5\xeem\x90\xe25X\xedq\x10\x98\xb4?F\x8c\x91\xdb\x05m6\x9f\x1e\x9f\xe8\xb5\x87\xc1\xfcg\x8d=\x7f\xa6\xdf\xf3`\xd94\xf0}x!\xf6\xfe|\xe8\xabe\x0f\x1b\x94\xb7#E\xb5 \x84\x97\x13t\x07uo\xfe\xeb_\xc9\xcd\xe5\x18\x1c\xa7\xab\xd8\xe3\xe3/e\xe5\xac\xdb\x1c\x8d\xcf\xb9\x93[\x8aJz\x9b\x8f'\xc4^7F\xefK\xcc\xca\x97\x98\x95O\x11\xb32 Z%B\x95c\xb0\"k\xab\x9a\xd7\x0dp\xab\xcf\x0b\xf1#29\xd5 c\xa0.K\x1b\xb3\x072\xbeD\xc1/\xa0#\\U_\xb0\x1e\x19\xe2J~\x0dCiZ>\x98\x97\xad\xe3-Q\xde\x148\x01\n\xeb\x1f305\xd6\xff\x9aV\xf0n\xba\xa7\xb1\xd0\x17\x8e\x82H\x9b\xf8\x10\xebr\xdd*p\xcc\xa3\xdb\x1b\xb3x\xfd\xf2c\xff\x00\xca7\xbd\xd2\xad\xea\xbc~_\x91\xf64\xec\xa6\x993;\xae\xd4N+\xbcW\xc3\x95h\xc6\x94\xa3M\x1d\x17o\xc5T\x0e\xf2\x98wF[\x89\xc5\\\xe7[Q\x8c\xdb\xa8\xf6R\x16\x8a\xe1d\x16E\x92\x01u\xfcL\xebdY\xb2\x9b\xf7\xce\xa0Z`\x85\xbd\x95 \xb6%\xbbM[jw\x05\xdf\xf5\x8c\xaf\xf9\xc2\xf7} \xbe\xef\xcfg`\xfa\x14gF\xcd\"\x99\xce\x0d\xcb\xb0\x82|@\x90\x00s\xb1\xa8\xc2\x17\xf91\xac\xd1\x96D\xf8\x02'\xf6\xe6\xd8\xd8\x82\x04\x9b<*/\x1e\xd3}\x1d\x95\xa6Z\xc7t+\xe5\xc6x\xdf\x98A\xf9\x9br\x95\xe6\xd1\xbf\xc9\xf7%\xa5\xb0{\xdd@\xb6\xe6\x15\xb0W\xc4Qx\x05\xf60\x8c\xd4\xe5\xc5&\xff\xf8\x03\xfd\x9d\xae\xc4\xea\xc5\xbax\x890\xda\xcd\xb0\x96\x8a+\x89\xa3m\xce\x86z\"\x02m\xd7\x9a\\\x91>\x84\x94u\\\x9b\xdf\xaa\xb1\xad\xd4\xc6\xae\xcaAX\xb7z<~\xbaJq\xf5\x1f\x9b\xeb\xea\x93zo\xc8\xe3T\x03\xb7ht4P\x1f\xad\xd7\xd9wC\x15Xj\xad6\xd9~\xf8\x80\xd2\x88\xfbP\x89*\xf4\xa1\xc9\x87\n\x1a\xf94\xd2\xe45\xbe\xcchD\xfb\x9e+n\xac\xd3\x90\xc4\x942\x8da\x8f\x07\xaaz\xe4<\xf3\x93\x90\x84#\xa1\xea0\xb8\xc6\n\xf8Y\xff\x13\n\n\xd0\xdf\xc3\xf2\xe9\xdd\x98\xb4&\x18iW\xb5&\x87\x89\x11&\x10S\xc8\xe3\xc8\x94\x1a*S\xb8n=ZE\x9f\xba-\xcd F\x99[\xac\xfeK\xee$\xd8\x86\xeaOI7\x9a\xf7\xc3\xf0^6\x11\xbc\x1f\x8e\x0d[E!9&\xf1\xe2Er\x84\xd3j\xe2\xc5\xf4+\x0d\x15\x1bV\xa1\xb5B\xe7C\xf7D\xd2\x89\x07\xac\xf6F\xdes\x0c\x85!\x1a\x90\x0f\xad\xfd\x11s\x80N\xf0\xf5\x94T\xa3\x19\xb4cw\xd8\xaa\xb6\xf3\xf0 \xb8z\xd4\x82\x98p\x08\x991\x956P\x98|\xaa\xe8\xcd\xfe\xfc\xb2U\xe8b\xae.\xdcl\x88F'\xc1\x0c \xea\xf2\xb6\x0d\xb5\xde*\x8a\xc3\x9c$\x943\xfa(M\xebB\x0d\xcd\x0d\xc9\xc2\xcc\xaasM\xc3Q\xdaxi\x05\x9b\xbc@\xa5[\x96F\x892_\x1c\xf4\xb0\xb7\xba\xcb$\xe7?\xed\xe0v\x1fX\xab\x92\x04%\xaa\x1368\x8c\x8b\x95\xed\x12\x1eP\xe4\xd4\xc7\xa0\"|\x17S\xf6\xcb\xbf Ar\x985a\xbb\x87\xa7\x91J\xf5\x85\x02\x990\xb0h\x1d\xd1\x92\xe8\xb5\xee\xc1\xee\xfc\xeey\xde\xfb\x0e\x89k\xb0C\x1d\xaf\x0f$O\\\xf8i=\x10GO\x9b(v\xdc \xbb\x14\x87~\xbf\x1e\xd2\xf83\xf0\xf9\xbb\x96*\xc11\xfb\xa10\xdc_g\xe5\xe0\xe7!\xc1\xf8A\x19m\xc9k\x7f>\xc8VZ\x99aC\xbf\xf4\x0bR\xa2G\x8e\xfc\xc8\xb6\x92Q\xaa^\xa8\xd5\x12\xbd\xdb\x97\x13JP\x13\x98,\xa2\xa5\x02\x8a\x89%\x86\xc0\xce\x00\x13QW\xb9\x86\x9fS\n\xfc\n\xf9\xaa(Y*E\x18G\xc4\xef#\x8b\x18\xa0k\x1b\x12\xef\xc6\x0d\x97~\xba\x02\xb4HS\xd4\x98\xc1\x98R\xf9\xaa\x8d\x99\xc4\x83\xefc\x0b/W\xc9j7\xb2\xce\xb0-^\xffIg\xafq8\xb5\xe0ly\xef\xc6XG\xee\xc4\xd1\x90\xefG%Y#\x9fY\xd3\x9a\xc3\xc3ff\x9d\xc6\xd9\xf2\x10\x1c\xbe\xb3x^\x96\xc1}\xd3\x07\xadt\xba\x16G\xc9;U\x860\xa8\x92\xd9\xf0$8\x8e9\x9dJ[~\xa8\x86\xa5\x1aDD\xc7{\x14F%`\x8c)\xcb\xbe\xc1\x1a\xe1wX\x154\x8dqd\xd7\xa5\xe0\xe7\xc8\xf5Z\x08\xda\xb3\x88'\xe7i5n\xbbBlTW\xb6>l\xc7\xd6\xb9P\xcc\xb1Y<\x92\xcb\x8c\xe8_}\x05\xe9\x18\x8c\xcb\xa0\xa9\x84\xa65\x071b\xab\xad\x94\xd2.M\xa2\xa1\xf55 \xd5\xa6;h\x1d\x06\xda\xc4'\xa4\xa6\x993\xd0\x14\xb3\x14\x14Y\x97\xef\xb4\xf7\xc0(1~\xdef\xa4\x05\x15\xb1z\x12S\xca\x9f\xf4\xa4\xb2H\xbc\"\x13\xbe\x162\xa9l\xc3\x1f\xf4\xda(\xf8\x83\x9eT\x16K\x0dL(\xfe\xb8qS,W\x1b\x98\x16\x1f_<\xcbl\xc53\xbd\xcfn>\x06\xbf\x7f\x92wy\xdfk\xe3\xb3+\x92\x84ozb\xa2\xc2g7\xed\x8b\x8az\x9f\xdd\xbc6X\x1d\xb6\xb7\x8e\x8aG\xcde\x89\xe3\x01\xabE\xc92\xca\x17\xab\xf4\xcc=a\x94\xb3p\xc6@\xde\xd2o\xf7\xe9\xc0\x989Q\x8c\xbb\xe3\xa5+f\xe9\x0dSH\x85\x1a\xdfN\xa8\xb9\xe6\xbc\xbb\x0dc\x9c6\xf8V\xdd!\x1c\x19B\x9f\x9a\xda\xf8\xe6\x92V\xc7\x05J\xb2Q\xdb\xdb\xb7\x03\xe2E\xc5\xf1*=K\x9aK\xdf\x80\xa6\x1c\xc0[\xccB\xa0?\xa0\xed8\x12\xa6\"\x9d\xa7\xe7J\xdeX\xd5L:\xeejX~o\xa9\xfbu=h\x1e\xb4\xc6\xe3\x93\x84Z\x0f\x8e\x90\x9d\xae\x9ax\xb5ZYY2'P\xf6\xa7\xa9]~l\x97]C\x16\xde\xa7T\xa3\x9f\xf5\x06v<\xabc\xe3\x19\x9d\xe1]\xc3\x19\xed\xea\x1e\x82\xf2\x10\x07\xbe\xad\xd0^\xe2\xf06)g\n%\xc6\x9c\x89^\xcc\xa0c\x84\x16G5\xe7\x02\xfc\xa2\x88\x96h\x931\xeb,\xaa\xe3\x806<\xfd\x1aJ\xf8\xa6w*|\x0d%\xa5\xfcj4\xda\xf2<6\xf5\xa1Pj\x82\xed\xaa&s:\xb4d$\xba]%\xfd\xf6V~\xf1\xe2,\x11l\x0c\xd3\x16b\x04\x02\xeeZr\x92\xd3\x13(9\xc9\xdf\xdaF\xc2B\xe3x\xef\xe3D\x1f\x01S\x1bw\x89\xea\xc4&\xda\xc3\x06\x9aCN\xd8\x81\x9a\xc07PV\xb3\x9b\xe8g\x17\x1a+\\\x9e$\x860\xc6\xdc#\xc9fMr\x7f\x8e\xe7a\xebO,&1\xc6\x9a\x88t\xd3o\x04\xd0\xde\xfe\x18x\xf64\xba$X8\xd1\xcd\xbd\xb3<*+\x88\xd1X\xc1d\x12\xfa\xc1w\xe4B\x1a!\".\xdb\xa0<\xa8\x17\xaa\x9a\xff\x92\x87\x9fh\xa6\xa8\xe27(\xeb\xe66P\x89\xee=^ \x12\xd3B\xe5\xbd\x9c\x84\xe2\xea\xf7\xe5\xbd;\xeao\xb3\xc8\xa8\x8c\xae\xd0\"2\xd5\xb9\xb2\xe2U\x80G>\xee\xb9\xa4\x19\x92Z\x8eD$dB\xce\xe0\xf5EF\x8e\xf2<\xcd]\xe7\x91\x9f$i t\xcf\x80\xcf\x8e\x18\xf0\x0b\xf0\xab\xd6T\x825g\xcbT \xf8\xa014c\x87At\x9a4{\xf9\x8a,HN\x92@t\x956\x08+\xbfH\xfeV\xc2\x9c\x90\x04\xd0\xe5\xd4\x8f\xa3\x82\x840\x81b\x93\x91\xdc\x1d\xb5 \xe8\xb0H\xa8+\xb9\x0f\xf5\xfc\xee\x95h\x97N\x11m\x1d\xd8;\xc4\xcc\x9dt\xf2\x90\xc0V\x13\xd2z\xc2\x98}9\x8e@c\x9e\xdc\xa8\xcd\xba\xf2\xcd\xb1$\xe5K\x81|/\x16nd\xe9\x1e\x0dR\x0c\x1c\x82'\x18\xa5.\x1f\xd2W_\xb1\xc21\xa8\x84V\xa0\xcd1\x9dlz\xe0\xe6\xa4((\xf6\xae7E $*W$\x879a\x1fH\xf3\x06\x1e\x8d\x81\xe2\x99\x037\xaa\x86\x14\xabB\xea\xedX\x9fQ\x8c\x87q\xb1s\xad\xfd\xaaa\x97\xd2\xa4(\xf3\x0d\xe5\xcdL\x96o\xbb\xf8\x8c\x9a2\xea\x8b'\xd0K\xd0\xc2\x996b\x1fX7+\xda*M\xc9'.\x05M\x1cq\x87 \x97\xcfT\xd1\xc2(x\x08\xd2\xfb\x1c7f(\xb9\n\xb4<\x94\x8a)n4\x86\xa62b\x0c)\xbd\xa5-\xd7P\xac\xd2M\x1cV\xef\xbc\xc1l\xa5\x96\x95\x03\xb4\x019\x82\xf5\xc0\xed\xa1\x9d\xd7T\"\xaf\xc2\xb70\xa5s\xd5H\xeeY\xf3 \xd3\xb7\xf0\xb0\xfd\xe7\xacg\x1a\xef^Q+\x01;\xdd\xd7\xaa\x02P\xd0\xa03\xcc\x9f\x81\xa5p}\x910\x1f\x80\x9a$\xbc#\x17\x85\x9b#WNZu(F#\x8flI~Q\xb3\x8b\xdaC\xae\xd1b\xe2E\x05\xf2Ac\xb6y\xb2B\xc9\x0c\x01\xe2\x14\x1e\xfd\xedn\xa2\xb9I\xd1\xcf\x94\x9e\x03\xfd\xeeiW\x12:\xddKO\xa8\x9c\x1c\x9d\x10m\xc7\xe4{\xa0\x8f\xb4\x94S\xef\x18\x06\xbb\xc73\xf1\x9e\xae\xd7\x1b\xdc\xa5\xad$\xc3p\x08\xd1\x18H\x83\x89\x8f4\xbc\x8cNa\x06R\xa5\x19\xb4\x07\xf2\x9e%\x88t\xf7E\xdd\x1d|r\xdd\xb4z\xa14WR\xca\x9f\xdc\xef)\xe9\"\xfe\xa4\xa7\xef\xf3\xf9\x83\x9e\xbeo\xc3\x1f\xf4>U\xf0\x07=}_\xcc\x1f\xf4\xf4}\x81T\xdf\xb7@\xf0\xa0s7\xe3\x1f\xb9\xd7t*\x08\xd5\x8a\xc0\xf0\xe3+\x02\xf5e\x8c\x86(\x02\x15\xc1\xfb=\x97\x0c\xad\"0\x96*\x02\x83J\x11\x18\x8f\xc68\xd7\xfb_\xc3\x02\xbe\x81\xf8kXP\x81%8Y\xb4\x15\x81\x0b;E`a\xab\x08\x8c\xec\x15\x81\x01W\x04.yd\xb2\xff=\xaf\xa9n#\xc7\xf1>\n\xdd_\xcb\xaa\xe0E\xc5\x8b\xef\x8eoa\x01\x87\x93\xdak\xa0p\xc6<\x1e\xc7/\x1cz\xae\x9c8a\x1d1\xe5\xbc\xed\xb5\xf3\x9e\xf7\xeeQ\xc7\x13l@\xff\x1c\xe8\xab\x86\xf0\xb3,\x11\xde\x15h@\x15\x8aN\xce\x8f4\xe7G\xbc\xc0\x93\x1b\xbe\"E\x1aoIx\xbc\x99\x979!\xeeI\xb50\x1d\x85\xaed\x85\\\xbar\xf4\x900\xa5\x17(Z\nU\xdb\xf4\x02\xb1T\xa1\xba\xf9\x04\nU\xbd*\xd5F\xe5\xca\xb2\x1d:\xfaa3<\xcf\xfd\x80\xa0\x8d\x18\xb8#\xb9\xaa=F\xb8,\xa9\x90\x1dE\xb4\xebb\x94$$\x9f\x18z\xa7l\n\x1d&\xad\xdb\xda\x0d\xe1\x9c\x12k' z}\xa4\x99#\xa7\xcc\xb5\x9d\xb1\xcb|\x96\xc6\x98\xf8\xec/w\xef\xde5h\\\x17iR\x1e\xb3o:Q\xe9\xc7Q\xb0C\x9a4\xf5`\xc2\xfa\x90jp\x893GG\x99\x1a/\xa9`^h\xa7(\xdd\xe4\x01\x99\xc1\x91\xbc\xbb\xa3Q\x8d\x80\xe7\x94H\x9f\x8b<\xd0\xe7J\xc3\xb4\x95\x0fw\xc7i\xcf\xa2\x8e\x1b\x0bi2\xd9\xae\xd1=\xe9dj\x80\xa2\xf2\xe4\xa9\x8b\xa7\x8e/\xd8\xf2,'\x81_\xea\x99X\xe0\x02\xe6\nm\xa9^T\xa0I\xf5\x1d~\xe8\x9d\xc7\xad&\x85\x9b\x1b>\x91)\xf3\x1f5\xaf-\xe5\xdc\x03?\xfe.\x8e\x96\xc9\x0c\x9c2\xcd\x0c\xf8I\xaf\x8cr\xff\xc9\xf2\x15\xf7\x9c\xd8\xf7\x0e\xc8\xda\xc03\x1amQ,\x026\xf3(\xfe\xff\x82>\x19p\x08\xce<\x8dC=n\xeaw'\x08\xad\x84&\x0d\x04\xb4I\xca\x86G;Vk\xa5\xde~\xa6=\xa3\xef\x17\xa7\x1c\x99\xee\xfb9\xe7dv'\xcc`K\xa3\xa0A\xa7r\xdd\xb0AIy\x80\x1f<\x7f\xd7s:\xf6sc\xee\xb1\x0c\x81w\xef\xb9\xaa\xcb/\xc7\xddT\x00\x16(\xc7\x03\xbd\xd0V\x99\xc0\x0dp\xf0WN\x7f\x9d\xd2_\xbe\xae'F7\x07!\x0f\x1b-\xf1m\xbf\x00\x83\xd5\xab!\x9b\xf1:\x84\x0d\xcd\x00\x86+\x9a\xdb\xe2\x0e\x02\x81\xa1%\xeeIa\xf0 \xe0Q\xdc\x0b\xb8\xa1\xb3\xa8\x8dd\xd62\xf6\xa46\xa8U\x87\xcc\x99\xf1\xb8\xe7'\xe4\xff\xfc?\xa7\xfdV\xf9\xb1\x0f\xa4\xc4\xea@J\xf9\x81\xa4&\xb2\x18\x8dw>\xe1%b\xbd\"\x8e\x02B{s\xa0,\x08+\xae-/\n\x99\xc2CH\xbd2\xfd\xf1\xb8\xfa\x81S\x9a\xf2 \xb2\x8a\x80\xbc\x0c\x19\x07\xb1\xaf,\x1cU\xac\xc9\x074\x99\xb3{\xf7\xee\xe9i\x07h\xe9\x07\xd8\x1c \x0c\x97\x92K\x92G\x18:\xc6\xc1d\x12l\x86\xda\xf1\xfc\xf3U\xbb\x10\xd4\xbc\xaal\x7f\x1e\xd3\x13\xefX0\x816;\xd5f\xce\x9do\xe0\xef\xf0\xed\xa59]\xc9Q`\"\xd75\xa9\xd6EuZ\xd3\xe9>\x8d\x1e\xaa\x8c\xb5$\xd3\x82D\x1f\xabA\x8c\xe4\x19Is\xb5\xb2\xbf^\xe5z\xa2\x0e\x0c&\xdf\xda\xae\xe8\xaf\x1d\x8am\x88\x197\x91,\x1b\x1f)\xa4W\x9a\xd8\xed+E3\xb0F5\x18\x82n G9T@\xa2\x89\xd2\xdc\x8c\x19\xd5\xa0\x81n\x06\xa7 #\xca\x01(\x92\xad@W\xda\xfc\xe9*\xd1\x11U\xaa\x03\xd0\xf1\xa7/\xe8\xd8\xb8.\x89\x8eL\x9f\xfd\x99\xa3\xe3\xab\xabD\xc7$-\x07 \xa3\x01\xad>\xbf#\x11\x0d\x14Wv\x02\xbe\xba\xec XW\xff\xba\x94 \xa0\xaf\x08\x0e\xe2\xb4\xd0\x94K}\xef\xec\xe0G\x98\x19\xfd\x08\x99\xe1\xee\xba9Pe\xca\xcc\x90\x99\xd4M*\xe2O\xa41\xe4\x99*\x86^z\x971\xa8\xdc\xbc\xac\xdc\xc6\xa0\xf2\xf42\xbbR\x01W\xe1G\x83E\xffd&\xf4\xb7^\x94\x84\xe4\xfc\xc5\xc2\x95\xa4\x12j^\xa6\xd8\xa0%\xcf\xeci\xe1\xfa\x03\xdci\xac\x1c\xe0\xd6\x03\xdcw\xcc&y(p\xe7\xb1\xd2u\xc4\x81h\x02?\x83C\xd8R\xd2~\xb98\x17\xd8\xc5\xbb\x02\xe0\n\"l`wg\x06`\xedo/\x13\xe0d\xd5GK;3\xe8\xe7C\x1b\x9d\x0b\xb5\xeb\x82!\xc4\xaf\xf6L\xf0\xe1\x9bC\xd8\x18\xc8L\xbf\xc2\xd3\x89\xe7yo\xb5#pN\x9c1\xac\x85\xdem\xbd\x9b\xae\x1b:\xfa\xeef\x90\xa9Y\xdf\x0d\xd6:o\xa8\xcc\xb5:\xbd7\x98q\xc1\x18\x97\x05\x95\xe2\xb96\xe2\x98\xfbF\x8f\xd0\x7fX\xaa\xab)\xec\xcf~l\xb4R\nX\xceB\xc9+\x1d\x8aK\x91\xcb\x8a=\xaad\xce\x0c\x1e\xee\x1ej+\x0c\xfb\x1a\x13&m\xa9B\xa9K\xc5\x1b\xb6v\xa3\xa0\xda6C4\x11\x01=\xd4\xfc\x12\xe9\x8c\xc1>\xa51\xb4\xa4\xd8\x80K\xb1V\x078\x0bvN\xb4\x9ex\xd0\x10f\x0d\\\x87\x9dh\x0e\xb5\xe8\xeb\x1bU\x1fcpZ\xf17\xad\xe7\xbd\xbb\x1dy\x14o}\xb6\xb1mr\xc93UI\x9e\x91J\xf2\xf4U\x92\xe7F%y\x16*\xc9S]\xad \xeb\xc5qRy\xd4\xcd\xea0\x9c\xe9\xfe\xe7\"\x80\xde\x9d\xd3\xff]?\x19TR\x14\xa1/\xf4)e\xd0\xf4\x03\xc8\xa0;\xe6\xf8\x87\xeb\"\x83\xdaH\x89\xc9@i5\xddAZ5\xcb\x8a\xfe0Yqc+\xda\x16\x18D\xdb\x0d\x15\xd1{\x03\xb0d\xc4{\xe8\x9f\\E\xa4\x18J\x07\xa0\x06S\x9f\x0d$n\xc4yP\x81\xce\xc2K\x8d\x83/\xd2|\xedk\x95\xb6\xc0\xb7#\x7f\xe1|m\x94\xaa\xb654F\xaa\x1a\xc0\xd7\xd2 \x15\x9f\xfec\xc8\xa7\xb1\x1c\x1c|\x03\\\xa8d\xe1vKR\xd6\x0bG\xf7\xb6\xfeE\x94,\xafL\xf2\xc6\xa9\x19C%\x81\xf3\x95\xb8\x02\x11\x9cw\xf1\xa7\xb4\xdc\xb9\x97\x17\xde\xca/\xcc-\xe9\xe7\xeb\x14\x8fe\x18\x83i.)Y<_\xc7\xe8\xfa\xb7\xfa\x0f\xd9\x13vS\x07;m\x0c\xe3\x84\x83\x81\xf1h\xae\xbd\xf3?\xff\x8f\xfe\xcf\xc1\x14\xe2\xce\x0c\x9c1\x1c\x97y\x94,\xddT\xe7M\xdaL\x94T!\xe8Vw\xe6\x9e\x99&\x83K\xaa[\x03\xa7\xdf\xf2II4=\xbc\x9c\xc2\xcb\\\xfa\xeb:(\xbc\xc6Pz\xe2}I <}\x86\xa7k\x91\xe0I\x14Qj\x8d\xc3&\xd3\x13?\x1e\xfa\xd8\x92T\x8f\x7f\xf6%*\xd9\xb4z\x8c\x87\xc0\x15ef\xe2{\xb2\x97\x0d\xc9*\x05S\xd9\xd9yI3W\x92\x1c\xf9\xa2k\x80|}<\x8be:\xd5\x94?\xe8\xe9T#\xfe\xa0\xa7S\xf5\xf9\x83\x9eNu\xc3\x1f\xf4t\xaa\x05\x7f\xd0B\xf2X\x8d\xe4\xf1\xc7G\xf2\xe0\x8a\xb2\x14\xa5*\x05f\xcf\xbbF\xa6\xc0\xcc\x87+0\x95Y\x8a6R\xc5edR\\~\xb2,Ei\xf2:\xbfH7%\xa6\xdfV\x03'\x1c\xf8\x91\x9f\x04$6\x00\xe7\xcc\xab%\xf1\xe71 \xb5\x01\xfe\x86\xba\xdd\xea\xb3\xb1U\xa8<\xbf\x98\xa4\x1buT\xb7\xb6R\xfb|S\x96\xf6Y\xd1\x9dy\x99\x00o\xef\xf4\x94\xfe\x11\xe0\x84\xd8\x147\x97\x1f\xcb\x94\x0fd\x93\x8aa]\x1f\xaa\x9f6\x1dT\xd4\xfc\x1b\x83\xf3:\xbf\x80\xa8\x84tS\x82\xccdfp\xdd\xd4\x17\xf7\xaeX#V\x12\xaak?i\xe1\xe7\x0c\x9e\xf0\x1d\xd0\xa8\x86\xd6\x01o`\xa8\x19\x9c\xe3\xe8\x0c\xf6jc!&\xc8\xa8\x0f\x95\xebYp\xfc\xcb\xa1\xf2\xe5P\xb9\xbe\x87\xca\xfc\"\xf3\x0bC\x91\x16\xe2E\xc5\xf1\x99\xbf\\\x92\xfc\xc0t\x94\xb0\\?\x1a\x12\x86P~\\\xa4\xc7\xab\xf4L{\xe2\x94\xba\xc3\xa0\x19XP\x8f\xd6\x0bVQ\x1c\xe6$A\xa1\x0e\xcb\xfc\x98?bG\xa6\xb7$/\xa24\x99d\xb9\xbf\\\xfb\xca\x13,\x1d\x7f\x88\xe6NO\xd7\xa4(\xfc%\x01\xc5\xfd\xc9\xc4_\xcf\xa3\xe5&\xdd\xa8\x0b~X\xcd\xa5\x12hu\xab\x0e\x0ey\x83\xb4\x18\xca\x14\x18\xc6\xe2\n@]\xea\x06\x13\xc7\xa8>\x94\x99\xdb\n\xd2\x90\xd4\xad\x15\x0c\xf5X\"V? \xa9\xa4a\xf9j\x9a\x91\xc4\xcf\"\xf6\xea\"\"qXP6 IK\x98\x13\xc8rR\x90\xa4\xc4\x8a\xd4+\x02\x85\xbf&\xc0\xf1\x1c\xd2\x1c^d$\xf9\xee\xe5\xd3\xc6\xb8\xeeY\x8e\xdc9\xdedY\x9a\x97$\x14\x0b*z\xe7\xe7d\xc0\xf8\xf8\xd4\xa0\xf0\xf57\xe7\xc0\xdbw\xfeV\xcdR\xb9J\x0b\x02\xe5\xca/a\xed\x97\xc1j\xc0g\xf9\xb4\xcd\xe0\x96\xb7\xef%l\xf6\xdcE\x9a\x039\xf7\xd7YL\xc6\xbb~k\x1f\xbf5\xf2\x1c\x11\xd3BI\xb0\xc5\x16\xd5\xee\xf3\x0f\xb0\xdf\xae\xdf\xf6^GE\x11%\xcb\xcfgs;\xafWt\x87\xa5\xdb($a\xe3u\x08SR`\xad\xdd\"#A\xb4\xb8\x00\x9f\x1eoQg'X\xef$\xbe#\xa3$\x8c\x02\xbf$\xd5\xd7$\x1b\xb9\xdd\x00|\xd9\x83\x97\x11\x10Z5I\xed\x85\x04q\xf2\xcb<\x0e\xc5\xa6\x96=c|\xca\xe7\xc7\xfd_c\xd5\xe5\xe0\xdc\xf4l\x97\x0c\xd48\xae\xfd8\xae0Q \x96\xe5\xf2\x9cm\x12\x9a\xd9u\xb7\x03\x07\x13\xb6\xe3\x7f\xafY\x92v\x8a\xa0\x8f \xc9\x9eE\xc9\xbb\xcf]\xbd\xdd\x18\x87\x0d\xb2pq]\xa9\xde\x96F/1\xe1\xa0$\xe7\xe50$\xf3\x8d\xb8\x93\xa4\xa8\xe1\x96\x88V\xb5N\x05\x1e\x1a<5\xa11\xd9^\x96\x93-I\xca\xc7\xacG\xae\x84\x92*\xf3\x9b\xae\xb0\xa2[\x89\x15\xddn\xb2\xf4N\x0c\xb4\x8b\xd9&=>\xdbT\xe9g\xa9n\x1f\xe3j\xf7\x1d\x89)\xb6\xb9\xb8+F\xacLk\x0b\xa1s=B\xe7\xed\x19\x94O\x86R\x8a\xe6k\x1b\xd9\xb0RJ UU\xc1\xf3u\x9c\x143pVe\x99\xcdn\xde<;;\xf3\xcenyi\xbe\xbcy\xb0\xbf\xbf\x7f\x13_\x93\xbf\xf4\xcf8J\xdeI\xdf\x9c>x\xf0\xe0&\x16 \x94\xbc\xabM\xf0\x93\xa5\x05rc3p\xfcy\x91\xc6\x1be\xf9{^\x05QQ\xbcF\x94?\xdc\xef\xa3\x7f\x17\x99\xd5\xd3J\x16\x85\xc5\xbc^\xac\xe7i,\x9d\xdamD\xce\xbeO\xcfg\xe0\xec\xc3>\x1c\xd0\xff\x93\x0c\x06\x0bNm\x928\x0d\xdeu\xd3\xd3\xe9z\x97\xb1<\xe0\x12\xa4\x9b\x81\xf3|z\xc7\xbb\x0f\xf7\x7f\x98\xde\xfe\xf9\x8ew\xf7\xd1\xf46\x1cx\xf7\xf6o\xc1\xf4\xc0\xbb{\xf7\x0eLa\xba\x0fS\xb8\xe7\xdd\xbau\x1b\xa6p\x97?\xbd\x0bw\xbc\xbb?\xdf]\x1dl'\xde\xfd\xfd\xe9\xa3\xfbp\xcb\xbbw\xe76\xdc\xf7\xee=\xb8\x07\xb7\xe8K\xb7\x82\xa9w\xb0\x7f\x8b\x0e\x07\xf0\xd9\x01\x1cx\xd3\x07\x0f~\xbe\xff\xc3\xed`\xe2\xdd\xb9s\x0b\xf6'S\xf0\xee\xde\xbe;\x99\xc2\x14\x1fM\xef\x05\xfb\xe0\xdd\xb9\xfd\xc0\xbb}p\x9f\xde\xbb\xf5\xc0{p\x87>\xbd\xb5\x7f/\xa60\xf7\xbc[\xf7\xef=\xba\xe3\xdd\xbdw\x00\xd3\xfb\xde\xfd\xbbS\xb8\xeb\xdd\xb9\x03\xd3\x07p\xcf\x9b\xc2\xf4\xc1\xea\x8ew?\xa0\x9f\x80}\x98\xd2\xcfL\xe8W`J\xbf3\xa9>swB\xbf\x13xw\x0enO\xbc\xe9\xdd{\xde\x83;\xb7&\xde\xbd;\xec\x07m\xee\xee\xcf\x0fh\x97\x1eM\xef\xc1}\xdaG\x98\xde\xf5n\xdd9\x80\xfb\xc0&\xec\xdf\x9d\xf9\x1f\x8d>\xf8\xca_\x9bu\xff\x93\xac\xe0\xf3\xe9\x01\xdc\xff\xe1\xfe\xcfw\x10l\x10\n\x7f\x82\xd5\x97\xe4\xb9\xb8\xc4\xe2\xdf\xf6n\xdd\xbe\x0f\xd3\xdb\xde\xfd\xdb\x0f\x82\x89w\xfb\xee\x03\xfa\xff\x93\xa9wp ~\xdd}p\x0f\xf6\x9fQ4\x98z\xf7\xa7\x0f\xe2\xc9\x81w\xf7\xce\x94\n`\x07\xdaW\xf0Q\xe3\x1f\x04\xa0\x98B\x1f\xc7\x07\xde\xbd;\xf7'\xb7\xbc\xe9\x9d \xfd\xf9\x00\x7f\x1e\x04\xb2\x97\xee\x8b\x97\xaa\xdb\x80\xb7\xc5\xcf\xaa\x83\xf7\xbd\xe9\xfd[1vor\xcb\xdb\xbf5\x0dto\x80\xe8z\xf5\x9ca\x1a\xed\x1d\xf6\x89b\xc2\xf4\x0e]k\xf1;P\xbe\xf2)0AY,\xf7\x12\xf8p\xcb;\xb8\x03\xd3\xfdgw\xbd\xe9\xfe\x038\xf0\xee\xdc\x0f&\xde\xc1\xdd\xfb\x13\xef\xe0\x1e\xffqo\x1f\x17\xf7\xc1\xbd\x07\xe2\x81wo\x7f\x8a\xff}p\xf7\x01\xec\xc7\xf7\xbc\xfb\xb7\xe0\x9e\xf7`\xff~@!\xbc\x83{S\xfc\xef\xbd}:[\xf4\xc5x\xd2\x80\x99\x08 \xfa\xe9)\xb6\x83\xdf\x11\xed\xd2\x15\xec4\xfcL\xf4\xf3\xd3\xce\xfa\xa4\x1fyy\x89\xa9\xbf\xe7\xdd\x9e\xde\x07\x9c\xf8\xc0;\xb8w0\x11\x93\xc6~<\xb8\xf7\x00\xf6\x0b\x9c\xcc{\xfbS\x9c\xc8\xbb8\x91\x0f\xf6\xef\x03\x9d\xce\x00\x97@\xcc\x14\xfb\x81/q\xa0I\x05\xd4XQ\xfc\x14N8[\x81~\x93\xb8\xf3\xe9t\xc7\xd8\xc1\xc9=oz{\xfa\x81\xe6\xfd6\x1c\xdcV\xcd;/\xcbqe\xd3\xfd\x00\xeemo\xffp\xc7\xbb\x7f+\xbe\xe5!)\xba\xf3\xe0\xd9}\xb8\x1bO\xee\x02\xfb\xdf\xd4\xbb=\x9d\xd0\x7f\x9eQ(\x98\xde\xfa\xe1`\xfa\xf3\xbdO0t\x16\xf1~e#\xdf\x87\xe9\xfd\xd5\xed\xed\xe4`5\xb9\xbd=\xf8\xf7\xf3[pw{\xb0\x9a\xde\xff\xf9\xee\x0f\xb7\xfe\xbd\xbe\x05\xf7V\xd3\x83\xed\xe4\xe0\x87\xbb\xdb\xff\x8f\xbdw[r\xe4F\x16\x04\xdf\xfb+\x90l\x9d*\xb2x\xc9d\xd6E\x123\xb3\xb2\xd5j\xe9\xb4\xd6T\xdd2\xa9\xfa\xcc\xce\x90\xacj0\x08\x92\xa1\x8c\x9b\x10\x08ff 5\xd6\x0fk\xfb\x03\xbb\x0f;f\xbb/\xfb0k\xf3\xb2f\xfb\x0b\xf3)\xfd%kp\x07\x107D0\x98U\xea\xd3\xe7LS\xb2\xca\x08\x04.\x0e\xc0\xe1\xeep8\xdc\xcf\xeb\x9d\x1d|\x1c\xc5\x84Q\x18D\xfd\xf3O\x07\x13\x9a\xa6\xfe6\xaa\x9f+G\xfd\xe9\xd9Y\xd5\xa6\xd47\x1f\x9e9\xce\x95\xd5\x87\xe9s\xc7\xb9\xb2\xfa\xf0\xb4\xbaCK\xf1\xc3\xf3j\x13\x81\xf3F\xa5\xdd\x9b\xa9\xba\x9e}\xee0u\xdddA\x80\x9f\x9f\xbb\x82\xedxq\x18\xc6QH\xf9\x8d\xce4\xad\x1c\xc5\xba\xd4$\x9ekP\xd5\x0f\xce\x10R\xee\x91+\xf5\x19\xdeX\x04\xd1\xbb\xf5[\x0c\xd7\x95\xd0}\x8b~\xd6_D|\xc3\xe0\xc3|\xa9S\xfc(\xf0#\xf6*^3rEN\xa6\xa5T<\x0d\x85G\x9d\xbeR\"(\x1e\xba\xaa'\x9d\x8aJv\x86\xa7\xa7\xe6\xc5\xb4x\x9f\xc4[N\x93\x9d\xfe\\x/\xa0S\xbd\xf7\x1b\xe7-\xa9^\n\xe6y=rrE\xc4}\xc2\xe2\x0d\xea\x8c\xfa\xa0\xb1\x19\xc1\xc1qOOWoP\xedL\xc4nIV\xe9\x89J\xa3:\xcd\x8b\xb9\xc9\xe6\xd7\xbb\xa6\x92c\x93\x9c\x056-\xad\x8d\xba\xbd\x1e\xef\xc1\xd5\xc9\x8c\xb3~0gK\x03O\xcaD\x1f\xae\x1e\xfe\xfc\xbe\xba\xa4`\x08r\xf3\x11\x95\xb5UY\xc5\xfb\xc5\xa6G\x84\x15*\x1c\x95j\xb2\xa0tR~\xa9Z\xcb\xfa+\xb80\xc9\x06D\xecx|\x0b\xfd\xfe\x8a\xf3\x98\xf7{\xff\x81\xc7\xd1\x96\xfc\x993\x85\xdet\x15\xb0?\xe3\xa1\xa4\x18\x11o\xc7\xbc\x1b\xb8\x9c\x7f\xea\xa1\x13\x8e\xea\xbd0\x8b\x9f\x18\xabF\x8d\x8cM\x1a\x8c\x88\x02[\xab\xe7!\x87V\xe4\xdc\xb0\xfb\xb4_\xfc6\x98lb\xfe\x15\xf5v\xb9-{m\xd5`sy\x99y\xb4\x84i\xc4\xa6\xcd\x1b\xd7Z\xbf\xbe3+\xc4\xd2\xaa\x10\xc6\xa6\x01W\xd4\xef\x8a\xb4\xde\xf93\x8a\xb8\x82\xc1\x87zj\xaa1\xa1\xfcp\x9dj\x06#\x8d\x99\x9e\xae\x18\xf29\xd5\x91\x16\xedU3\x1eK\xd3~4\x18\x91H\xd3\x89&@\xf4\xa1Z\xb7\xde\x01:!\xb6W\xd6\x94~@\x14\x86\xcea=\xe5\xf5\xa4RZG\xe4\x1b\xb3\xbc?\xe2\xb8D\x15\xbax6\xfa\xa0\xa1\xea\x06\xe2\x03\x06\x0c+\xee2l\xe0\xf7+\xe6B\xd1\xa7M\xe1u\x92 ?H\x0dC\xfe\x15\xf9(|\xbd\x81\xa1?u\x1e\x07\xf85%\xa6%\xb1)D\xfeE!\x01\x9c\x8e\xc4\xa6\x97[&~\xcb\x19U\x14<\xb6/\x0ebZ\xec\xb6\xaf$\xa7nS\xe3\xe0\xba\x9b\x98\x93\xbe\xe9e\x0e\xe1Hk\xfc\x03\x16m\xc5n\x04B\xca\xd9\x08D\x92^\xef\x82\xc4\xe3\xf1\xc5\x80P2\xbc\"|\xce\xe6\xfeR1@\xb6T\x8d\xf8\xc3!\xb6\x84]r#\"-\xcea\x1d\xfa\x8f\x0b\xf7x\x9a\x03>\x1c\xfa\xe4\x92\xc4\x17\x03\xd2\xc3\xa5\x80\x8e\xf3m\x17\xc85\xf6\xaa\x80\xa0\x06\x19U\x16s\x0ej`\x9a5\x8c\xc1Q#\xf0\x91\xb0s\xb2\xa3\xa9\x0bC\xd5\xa7,b\xa9G\x13\xf6j\xed\x92=U\x0e\xce\x92\x80z\xec\xabH\xf8\xc2g\xa9K\x12U\xd9\xb0\x9a\xdf\x8b0\xa8\x8b\xa4?\x17\xb4\xfa\x19J\"?e\xb1`o!\xa6\xd5a\xed~\xef2/\xf3rQ\xd8\x88\xbe\x1f\x95\xeb\x03\x95QG\xb2\xd3\xbb<-\xd4\xda#C\x92b\xf6r\xed\x1eR\xc4.5\xb2\xb9Xj9\xeb\x9a\xf4.\x13\xce^^\xaa\xe2P9\xed\xc3g-\x17\xc0u\xe6\xcbS\xf8zy\xaar\x16\x00 3\xd2\xebR\xb02\x0e\x1b\x16y\xae\x85=R2`\xe0\xe2\x0f\xdeH\x91F\x08\x1d;\x17\x8ekjkX\x1b\x8e\xc305\xeb\x93\x80F\xdb\xef8\xdb\xf8wu\xc9)Q\xe4\x9a\x86\xa9K(Q\xdf\xc1\xc9\x0c\xf8\x9f\xd1\x19'i\x12\xf8\xa2\x7f\xbaH\x87\xa7\xdb\xc1@\x87\xf2\x86H\xde\xbc\x1f\xe0\x12\xc6\x1e\xbe\xf5\xb2T\xc4\xe1\x88x\xf3\xb3\xe5\xc0\xfa\xb1p\xe5\x99\xab,\xcb\xca8\xd4\xed\x17U7\x1f\xe3\xd1\xe3U\xef1\x19\x92\x1d\x0c\xbb\xdf\x8f\xfb\x9b\xc1@\x8d\xf8\xe3\xde\xe3R)\xa7)ia\xc6\xd5\xbc\xad\xd5L\xc1\x0c\xf6\xa3\xc9\xce\xdf\xee\x02\x88p\xf4\xe8\x11)\xbcj\xc3\xd5B\xca\x88\xcc\x133\xd90\xeb\x1e\x15}o\x80n)\xfa\xf6\xd3\xa0\x15\x83\x1c\x88\xa1\x87DK\xeb\xd9d\xc7\xe8\xda\x8f\xb6\xb5%\xd8\xbabv\xaa\x0d@\xc7\xdd\xb7l\xcf\x02\xecb\xb95S\xf1\x91k\xd1Yum\xad\xef\xbap\x00c\xda\x1bM\xeev\"\x0c\xfe\x98\xc1\xb1\xed\xe5\x8e\x93\xd3\x97=X\\;\xfe\x12<\n8\x87k\x95\x05\x01\x13o\x03?\x15\xdd T\x168\x08S\xa1\xa2#G#\x0b\x9a\xa7\x13\xea\xf3\x05\x0b\xbbC\x17\xf8\xd5Y\xca+\xa9A\xd6\x0cU\xe0\xd7;\x19s%\xaa\xad\xdd\xc3\xd5&\x98\xaa\xb9v2\xc0\xdee\x1c\xe8e\x03\x95\x93\x97dJ\xae\xc9c\x92\n\xca\x05\xaeP\xf3 \x96&FTu#L \xbc#'!n\x99\x04E\xb5`[\xdf\xa9\xcfE\x06!\x80\x0c\\\x93\x1e\xa2bR\x9d\x99\xbc\xe6N\xe0\x9a\xe1<\xe9\x17jW;\xe659\x07\xe1\xf1%\x05\x1b\x10\x03\x07R*\xce6\x06\x06\x0c\xf3\x15\xbb(\"\x8c\xc1\x11\xcb\x8cV+\xf0C\xba\xed\"\xb2\x9b\x01|LR\xee\x95 M\xb9\xa7\x01\xad\x8fS\xf6\xd0!oX\xbd~\xb85Q\xcf\xfa\x8f \x0d\xf4hc-4P\xf3\x80\xcc\xd5$\xa0]1.\xe1\xc7\xbd\xc7\xeaO\x86\xeb\xbfH\xbf\xc9i\xaf\xb0\xd0+#\x04\x11D\xbb\xd3C\xc8^'\x16X\xcb\x113\xd5T\x8f\xe2\x81G@\xa3\xb27\xd5r\x0c4\x0d\xf5\xac\xe2\xf5\xfd\x11\xd0\xa8\xecM\xb5\x1c\x03MC=\xfc\x08Pxm\x9e\xf9Q p\xd7\xa8v\xa2\xd8\x1d\xb8\x94\xd8i.E\x03\x7f\x1bi\x0eu\xaf\xd6\x8d`wb\x0c\xa93\xa43\x98\xa3\xca\xac\xea\x90\x1d\xd3\xb7]\xad|\x1d\xe5\x1e\xda\xb3\xf5G\xee\xd9qh\xbc\xae\x96O\x05\x8f\x1d\xa2jc\x15\x98\xbf\xa1\x96# q\xd7s\x8c\xe0\xc5BG\xe9# \xa8\x97_\xb3\xa0{\xf3k\x16\xb8\xca\x1f\x01\x80\xa3\x06?J\xbbC\xe0G\xa9\xab\xfc\x11\x108j\x08)\xaf\x0b\x15\x8d5\xa8\xdc\xce\x1a\x8e\x00\xc2UG\x9a\xad\x0e\xad\xb5\x1c#\xb3U\xf3f\x1e>V\xebN\x8e\xa8;i\xab\xbb&`\xee(_\xaf\xb4.\xf1\x90D\xa1\x1b\xa9\xec\xa4Vj'\xb5\x88P\x12\\9\x88l\x1ao\xc4\xd1M@\x81\x94\\whM=\xd6);\xbb\x13\x1d\x07\xad2T\x95\xf1\x11a`N\xcb\xbaTV\xac\xaa^\x93\xa0\xdb\x0f\xae\x87\xaeVu\xae\xd9R\xd3\xe3KU\xe2\xa0\x14\xf7\xf2\xb1\xa3\x99#\x16\x85\xca_SB\xc5\xb1\x88b\xc1\xder\xb69\x04\xad\xe1D\x7f\xc8\xc2\x15\xe3\x08\x9f\xbf&C2\x1dLD\xac\x1d\x938N\x97\x95\x88\xdb\xdbD\x9cm\xc0\x10\xdb\xc9\xc4P\xea\xcdV\xdf\xac\xc9Kr\x06G\xa6\x9c\x0c\xafHof\xf5\x0c\xf0u0\"\x8f\xd5\n2\xea\x1f\x03\xffX\xd5\xfe\xd2\n\xfd\xbf\xdeD\x8fuL\xdf\xc7=\xe2\xaf\xaf\xac\xc4\xff\xb8\xf7rn>\xf5\x96Jxw.:;.\x80Y]wD\xba3eI\xf8\xf1\xe5\x8eW\xc1M\xc7)Kz\xb0N\x14\x1fn\xce\xa22\xc0\xec_\xa6\x0c\x9a\xaeeSY.\xe3\xa0^\\m\xa1\xa1|k\xcf\x8e\xc0\x9f8PM\x9dj@\xeaT\xc4\xd6|\x14\xea\x07>\xcc\x0fNX;j\xe1l\xd6\xa6\xde\x17,\xac-\x0e\x0b\xcc\x11\x1dt\xe9Kl=4\xf2v\xf1\xc1CE\xb3Fr|o\xefR\xd7\xc5\x105-\x06\x92\xe3|\x01\xe3\xabC\xb4\xa2\xde\x0d\xac\x90\xbf\xfe\xaf\xffM\xe1|e\xb0\xd6\xc7\xc8(\x0e\xcd\xd9\xfa\x08\xcd\xdbZ\xd4D\x9c#\xf6^\xeb\x9a\xb0\xb9>N>rC\x7fL\x0d\xc2Q\xc3Q\x02\xf3\xba\xb2\xe9+\x1f\x03\xa5\xe4\x8ad\xc5\xf3\xc3.\xcb\xa8_\xe4\xa4\x84\xf5]\xc4\xa9\x90}8\x8c\xc8\xcb+\"\xf4\xe9\x1a\x19\x93s\xc5\xc7\x15\x9b.+\xcaP\x13\x05\xd6\x07F\x0b\x85/FmU\xd2X\x89\xb9B\xbf\x82\xc6\xea\xac\x9c\xac\x99\xa5iU\x15\xafh\xcf\x8a\xf5\x9c\x97\xda\xd4 Z\xab\x85=Tip\xc5\xb9\xd4\xcf\xf78P\x03ri\x8f\x0f\xa1\xa9\x8a\n\xd5*\xd9\xecya\xaf.\xa7\xe4SS<\xa8\xcd \xf5\x03\x0f\xfa\xea\xc6]1\xb9\"\xf3\xda\x94\xcd{@\xa8{\xe8\xdb\xff\xec\xf9\xc0q\xf03\xef)\xden\xb2\xbcpg\xe1l\xc38\x8b<\x08\x13\x0f\x19?ug\xd4S\xaa3}\xe6\xced\xe9\xa2\xa0~`\xf2~\xde\x0c\xdc\xb9\xce3=k\x82\x0e\x8e-C\x16 \x03\xdft\xea\xce\x9a\x86\x94\x0b8\x06\xb49\xcf\xdd9\x03?\xba\xf17\xf7&\xd7\xd3\xc1\xb2\x94iy\xc4q\xbf\xc3z\xaahd\xc5\xcb\x84\xdc\x1ej+\x92pvA\x18\xb9$\xb1F\xc6\x0b\xc2\x86\xc3A\xa1\n\x8c$\x12\xcf\xd9r~\xb6\x1c\x11x\x98.]\xa6W\xc5\x03vm\xe5Q\"\x10.n\x84Gi.\xf8\x04\x9a\x02D\xe66X\x01\xa2-\x13\xdfg\x01K\xfb\xbd\xde``\xe1\x16\xe4\x92D\x17D(\xf0\xf9\\,\xfb\xac\xd1\x84\xe3\x03n\xc3\x95,A\x1a\xbb\xc6\x8a\x160\xd7\x84i;\x17\x1c\xcb:\xe1SC6\xb3\xd4\xcae\x01\xa9\x830\xb1I\xca=s\x88\xde?]D\xa7[\xbc\xf6:\x11\xdc\x0f]\xe2m\xc0\xf6,p\xde\xdeRm\xa532?\x1b\x91\xa9\x03?\xf3\xbb\xd8\xf32^\x82CWm\xc2h\x0c\x8f\x14X\xa3\xa2\xbd$\x9b\xb0h?\xb2\x1d\xff\xd8\xc6\xafO\xab\xb6\xaa\xdaJ\xe6y\x93\x91\x0c3\xa7\xb6\xbe\x0b\x0b)\x9c\xe6\xa6#\x12\x8c\xe0\x18\xbb~\x04\xfd\xec\x9c\x9c(\x82<\xf1v\x94\x7f\x19\xaf\xd9\x17\xa2\x7f\x96\x9f\x17\x8f\xa7\xf5\"\x9fO\xebE\xa6\xedE\xb4G}f\x1d\xe4\xf7\x96\xb3^{\x11j\x96x\xa1\x8b#2_\x0eF\xa4\x9f\xc1\xd5b:\"S\xe07gDJ\xf2\xfc\xb3:T\x19\xc8}\x8d\xcd\xc0r\x0c\xc8\x15\xa1\x93$N_\xd1\xbb\x11\x8a\x01\x8a\xc1]\x90\x94\\\x92@\xb1\xb0\xe9\x19\xd4L\x01E\x0b\xb5\xa7\x83\x0b\x92\x0e\x87naR\x873\x0c|\x8f\xf5\xcfG$\x1b\x8c4[\x86C}\xf3\x05\x9a\x1a\x91\xd4\xa0\xb9Y\xf4\xe4\x9a\x8c\xa7dF\xfa>l7\xd9\xde\xa7H\x07\xa5\xac\xa7)\xda8\x18\xe9;\xd8\xd0F%\xc7\x1c%Xo 2m\xe3\xc7+\xb2\x19(X\x1c\x14\xb0\x1bq(\xd0=\xf0'\x82Q=p\xa1\xb8\xccF\x0b\xb4\xa4~\xc9\xd8\xd2\xca)\xd2J\x9aKM\xd3\x12M\xac\x954\x0d8\x85*Z=\xde+\x89R\xd4\xca%\x8dR\x92\xaa\xc0J[.a\xcf\xfc\xa0\x03jY\xd3\x82\xc6\xe2\x82\xf0\x82pt\xd2\xef\xab\xf5\xed\xf7\xf9\xa8`R]\xa56\x88\xe3\x83\x8b\x01\x10 \xaeQ'68S\xb7\xd40\xbfb\xc3\xaa\xe4(o\\\xe1Q>\x14 \xde\xa1=c\xde=\x9bx\xc8[\xef/N\xf9\\6W\xcf\xa6U{B\xaa\xd3\xab\x86\xf8h\xed\xff\xec\xfc\xccIA\xd3\x9c\xbc\xd4\xccp\x14t\x9apB\xe4\x80\xf5\x88\xecFd?\"\xe1\x88l\xbb\xd1\xc5\x03\xa4\xf4\x01t1\xa8\xd3\xc5\xd4\xd0E\x0f\xe8b0\"g\xedt\xd1\xeb@\x17\x13rE\x02K\x17\x15\xd1\xf2\x90.n\xc8%\xc6p\xe8?=G\x8a\xb6\x86\xac\x15\xea\xb8Ac\x9c)R\xa4\xf5\xe0\x82lj\xb4\x12\xc8\x80\xaf\x00\xde\x1c\x80f\x0fM(\xc1R\xc7m\x1ca\xfc)\x03\xa4\x82px\xa5(\xc3G\x04\x0fZ\xb6\xf5\xed`\x1c7\xea\x91\"\xc8\xe4\x9a\xf4\xc3:`\x16(%O@\x86^\x0fSw\x83\x02|\x1a<\x07d\x17\x03\x05\x8c\x93\xad\xd8\xd2\x9a)9J[\xde\xb1U\xbc\xacoX\xcdtD\xbcA\x99M\xa4\x93|s2\xdf\"w\xa8\xa6\xb9.\xbe\xe8\xb8\x9c\xa1\xc3\xe4\x0d\xfc?\xecK\xe9\x8a7m>\x1eS\xf1[\x99\n\x10\xccB\x17\xb4\xc7\x8eR\x92\xb6\xa1>\x92\xff\xf8\xc7\xf3\x9f\"g\xf1\x1b8K\xce\x99\xfc\x1agr\xf2\x1f\xffh\xfe\xe3\x1f\xe2?\xe9/\xc4\x7f\xfcv\xfe\xe3\xbb\xf8\x8f\xff7\xe5?\x0fA\xc1F\xfc\x83\x01\x8fpw\x07n>\xec\x0e.\"\x97\x84_\x90H\xed\xe0JX\x01\x08\x16\xcf\xa3\xe5\xc0\xce\xba\x99\x07\xbd\x03\x11f\x00]\xbb\x10\x91{\x8b\xfb\xd7\x1a\x0d\x90\xcaK\xdb\x0c\x18\x80\xfar\xc2{d\xb5\xf4\xa4b\xf8LJ\x0b\xd9\xaa\xd5\x816\xb1\xfc\xa2\x9a\xddx\xd6B}\xb5\xe8\xdfz\xc5c\x17\xa4\x06\x85\xf5\xc7\x8cB\n$t\x85\x8b\xe6F\x1cF2\x0f\xe8\x8a\x05#r2\x053\x1cGUE\xfdV\xb9\xae\xe9\x88$Z\xce\x0e\x14IMM5}`'z\xfb\xcc\x06#r\xb2\xa9^$\xd2\x93\x9d\x0f\x05\x18%\x0e\\\xdd\x04\x04\xa4\x96\xe4\x95K\x8c\x0en\xd6I\xbeaw\x9c\xc348Q\xd1\xdbpo8\xac}\x06/Q\xb9\xb2\x83:\x15\x1an0\xa0']\xe0%\x0e\x98[\xa0%\xfa\nmK\x90\xc3\x96\x0e\x11\xdd)\xdc% *^\x93>lG\xe7\xcbAG8+\xb4\xbf\x19\x12\x81\x0eh\xda\x82\xcdv\x006\xeb\x08V\xa3\x8e\xc6\xfc\xac\xae\xc6eEh~\x06\xa0\x96j\xac\xfa\xa50\x8c\x1f\x0c}\x95U~\x8cQ\x1d\x8f\xbd\x06\xb8\xe0\xe2\x8a\x82\x1eh\x02\xd0&\x886\xab\xd7x\xfei9\xc8\x97]\x91ji\x83\xf5l\x80\xf2\x8c\x9b\xd3\x9b\xdcs[,\x97@\xac\xf6<_$q\xd2\xcf\x03\xbe\xc4\xf9\xbe3\x8b\x04\x9cg]\x17\x13fJ\xac\xe1\xa8%\xe5p\xa3\x87p\xb5\x1c\x1f\xba\xe6\xf0\x98\xee\xe1\xab\x0e\x0e\xd6Z\xc3|\x1b\xccj\x98\x12\xb7\x14\xe2#G-\xf6\xc9\x1ft\xa3\x84\xc4\xd1\xcbC\xb8u\x10q\xea4\xb2\x96\xd2\x0567\x95n\x83\xae\x05\xb2\nT\x1f$W\xd9d\xbb\xbf\xe6\xcd^\xfdruo\x7f>\xee\x0f\x16\xf3\xc5\xf2\xe7\xf7\xc3\xeb'\x93O\x16o\xe4h\xf6\xeb\xcb\x93\xc5b9\x00E\xf0b\xf1\xc9\xb4\xf71\xf6\x10\x0ey\xa5\xb8\xbb\xef\xb0\xb7()\xcf\x1a\xb6\x0dy\xce\xef\xd9\xf6\xab\xbb\x04\xc4]\xb8&\xd4\x7f#\xe7=\x08\xd2\xb8\x88\xfa\x83\xf9\xf2\xf1\xa27\x19\x9d\\\x8f{\xfafO\xaf\x87\xc1\xb7\xb8\xb9\xdb\x83\xa6\x82\xcbA_\x95*_t\xaeC\xd31n\x97\x9d\x804[\xa5\x82\xf7\xa7\x0e\xbc\x1cL\xd2\x98w\x0cN\xaa\xeb+\x9ck\x9a\x13@W\xbd\xa5\xeeI\xec\xdf\xa0\xff\xc9\x03\xc7\xa5g\xe4\xa3\xc2h\xa3\x82\x04_\xfa\xeb\x11\xe9m{j\xe7\xbb\xb1\x92Q\x9e\x17E\x933$\x98\xbb\x92\xc0\x1e\xa3\xc0\xee\xa6+\xd5\xed\xdd\xce\x9c\xd5\xba\xf3\x93\xe2\x86\xb2\xafH>\x14\xb0\xd2{eo\xf9\x12\xe8\xb2\x18\x8f\x9bk#\x06\n\xc1\xee\x84\xdeLP\xbd\xd9\x1b\x1c\xdc\x1b\x9a\x9f\xd5\x80\x9f\x8d@OF\xf3\xdd\xc6f\x12\xd0T|\x13\xad\xd9\x1d~\xf7\xb4\x0c\xb7g\x81\x11\x8d/@|\xdfL\xd8\x1d\xf3\xfa\x19\xe8-\n\xa5^\xa2\xfa\xfc \x95-\xfe4e\x83N5\xd3\xd9\xe2\xcf\x8a%\x99\xde\x98\x06#\x92\xa0>\x8d\x0cI2\x9f.\xf5\xe0v\x08EG\x0e\xf1\x99\xe2\xef=\xb8q>\xbeo\xd6L\xadc\x07\xb5\xb6\xc5\xb1\xde\xb5\xb8\x91\xcc\xcf\x97\x1d\xa2\xe7\x91\xc3\xf2b\xf1\xf7\xd0\xee=d\xeaT\x0f\xba\x15\xf9\xdb\xcc\xce!>_\xfc\x1d\xe0\xf9\xc5\x9f\x82)\x80\x05\x93/\x921I\xe6O\x0d\x8a6\xabR\xcc/-ho\xfa\x01\xb9$Y!\xe1!\xfd}\xc8t\xd9\x95\xf6K,\xa9\x12aT\x04\x0d(\x8d\x91\x98}\xdd\xf4\xd9\x08\\\x1b\xa4#bR\x04\xea\xb4\xdb)\xe6\x07 7&\xd5\x1cZ\x9c.\x86c\xb9\x98,&rq\x8d\xff\xc9\x93\x93\x93\x139\x1a\xc9\xf1\xf8\xb4~\x98q\xba\xe8\xf7=)B\xc9e2X\x0cN\xb7~\xfd`\xa3>w\xde\x8c\xf4\xfe\xfb\x7fsL\x11W\x1f\xfe_\xc7\x87D}\xf8\x7f\x1c\x1fD8#\xbd\xbf\xfe/\xffw\xaf\xf4\xa5\xc1\xda\xa6\x8b4\x95\xcbQ.iIk\xab\x8a\xbe}\x1a\xe4\xa5\xd2\xde\xa8\xc8\nS\xcd\n\xd3&VXc\xc4v\xd3\x94v\xe7\xc7\x19)\x97;\xcc\x96I\x91\xed*,\xcd,\xdb\x85\x95 gQ9/U\xafx\xd0<\xc8Oz\xfa=<\xa3\xb9&\x01\x99\x91\xc0J\xc3\xf1\xa8\xdd\xf6\xac\xfa\xd3\xd2\x97?\x17\x13\x11\x7f\x1b\xdf2\xfe%MY\xbfbtS\xfc\xa9e\xc6'\x82\xa5\xa2O\x07\x16^Z0\xbf\x18\x8eA\xec\xfe\xef\xff_oPH\x9d\xfc|>z\x0f\x1f\xfe\xfa\x97\xffZ\xfc\xd2\x9f_\x9f,\x07\x7f\xfd\xcb\x7f\x85\x8f\x9fL'\x93\xfa\xd7\x9f\x9f\xe9\xb2\x9fL\xd5\x7f\xc5\x0c#[\xef\xa8T\xee\x8d\x9c\xbf\x19/\x07\xe3\xf1\xb8\xaf\x1e\xe4'\x83\xd3m\x085\xfc\xf5/\xff\xfb'\xe7\x95\xbc\x8bt0\x1e\xf7\x17i)\xdb\xffV\xcb6\x7f3^\xa4\xaa\xd2>>\xd5\xb3\x83\xff\x96\\mM?\x8an\xd5\x12\x8d\xf9\xe3\xde\xd2E\x1c }[\xa7\x08\xa7\xf3\xf1\"\xc5\xdd\xd1\xf2\xd4\xb5\xc3\xa2m\x16\x8a'}a\x0e\x02\x01\x7f\x8d`\x0e\xd3~\xe2#\x120\x85\xbc\x85N\xd6\xdb\xc8\x0e\x98^\xdb\xad\x04\xd0em\x10k\x13\x914WF\x91<\x80\xde\xf8\xceM\x9b=\x92\x1d\x91\xfb\x11Y\x8d\xc8\xdb\x11\xb9\xfd0\x82t\xab5\xbf\xab&\xc2\xb4\xd2\xc4`u.\xc5\x9a\xccFaK\xaer\x88a\xe8\xb60tx\xfct;\xdf\xea\x9c\xe4\xf2\x8al\x06\x17d;\x1e\xb7\x9c(\x99_a\x0c\xb6\n\xb9P\xae\xd2\x9b\x14\xd8_\xd9\x15<\xe8,[\xb1\x19v\xe1\x82(\xc1\xca\x03\xc2\x18\x97vAz\xe3\x13\xe3\x86\xc7\x1f\x0c.\xda\x87\xd9\xfc\xc0\xd7\x07\xb9\"'\xb4\xafPX\xefN\xc6d\xaa\x05\xc2\xd4\xeeW\xa6#rO\xaeH\xef1NL\n\xa6\x89\xa0:\xc0\xb2\x01\x1e[']\xe6\xc3\xfcT\xeb{U\xc3zDB\xf57\xe9\x06\xb5\xf9\xc1\xa0\xb4\xcdc_\xcd\x83\x9a\xcaQeJ\xc9f\xa0\xa7\xf4\xa8\x06\x89\x06z7I\xfdh\x1b0\x18\x8a{\xd5R\xa1r\x95\xb69f\x18\x8a\xbf\x1c\xe0{rM\xfao\xe7;\\j\xc5\xe3\xca\xcc\x91<\";\xb46\xc8\x89 Z\xc4\xce\xcf\x97\x15\xb6\x91\xf5\x0b\x02\x80\x9e`G\xb9\xa7K\xd0&\x7f\x0c\x10\xce\x1e\x08\xc2t\xa9X^qI\x1d^+\xae\x9fj\xca\x8f2V \xbe\xd1\xe5WW\x836\xfd\xf6\xe4\x9a\xdc\x1e\xb3\xcf1?\x18\xc5V\x1d\xb4\xeb\x97\xc4\xe9\xcc\x0e\xddQ%\x11ug\xc4\x11\x07\xbb\xed\xa7\xf7J\x9b\xce\x85\xc0j5T\x8b\x03VH\xff0\x02\xf4\xfe\xfa\x97\xff\xe2\x8a\xa0\xea\xfa\xbd',H\xd9G\xad\xfa\xa3\xee\xc1\xc0\xc0\xbc\xea\xf8\x15\xe4\xa9\xdb\xdb[\xf9\x1b\xb9\x98-N\x17\xa7N\xb9\xc9o\xd4L\x9f\xbe\xb9\\\x9c\xd2E\xfa\xe4\xe5\xa9\x91\x90\xda\xc5#Z3^7F\xe8s\x87^CX\x0b.7\x06\xab\xce&\xe82\xaa\xf9\x9c*\xe3\xc1\x8c\x9c4\xc4\xae`!\xf5[>\x8b[_\x08\xc6\x9b+\xd7\xf2\xf2\xd7Q!0g\xd3\xdd\x16\xf3Ko}\xe1\xed\x14\x92l\x99x}\x9f\xb0\xfeA\xa1\xc1\xa3)#\xbd\x8c\x07\xbd\xd9Add\xc7\xacy%\xb2\xccH4\x81\xc8dl\xfd\x9a\xddu\\\xf60\xaa\xd0\x83?\xf1\xc0\x11\xf9\xa6\xfak:w*\xfe\xe0\xc2n{6\x1c\x08\x98\xb5\xbf\xaf\xa1\xe8)\x90D\x0cjF\x18\x96\xafTB\xbf\xb0\xa3z\xa3s\x9c\xfa\xa3\x92[\x9b\xa6\x9f\xe3\x0c\xcc~j\xfcb63Sg\x8ez\xb9\xea\xb4\xe8\xf2\xf5\x11\x0b\xfc\xe8&\x9d\x11V\x1f\x12\x9a\x89X}U\xcb\xa4\x1c\x93\xda\x15L\xea\xd8\x8d\x0co:\x80*\xeee\n;\x80:|jg\x12eA\xab\xe2E\xdf\xc3i\xd8\xe3\x14,\x95\xee]\x96J\xce\xb1\xaemk\xee;\x1e|\x14\xb6+\xa0o\xb9\xffX\xe7\x1f\xb9\xdb\xa0\x1eXD\x822);\xea\x14\x04\xea\xd1\xb7\xd0\xb5\xdc\x9d\xabr\xb6 \x9f[Vw\xfa\xe6\x92\xce_.\xd2\xa5a\x0d\xdb\x01\x1a\x87\xea+\xa3\xbb\xf1xD\xfc~\x9a;\x18P\x89\xc3\xe1@\xc9\xc6\x90\x0bR\n\x9b\xaf\xbc\xad\x18k\xcc\xcbv\x01\x9e\xe8\x0e\xac\xe0\x90Q\xc9\xf9}\x85\x1b\x14.\x13(\xf4F\xa1\x7f5\xc91\xda\xee:l\xaf\xf6\xa5=e\x08\x05\xfb\x81\x82yo\x15\x06F\xbc;L\xf1\x88\x99tOo\xa3\xd7\xd0\x9a\xde\x11np\xc7\xba!\x97\xb6Y4\xbe\xcdM\xdf \xce%\x15\xec[\x05\xc6~\xbeYN2\x1e\xa0\xa6J\xdb%\x1b-\x1a|\xd4;T\xf5Y\xb5\xb4\x1e\x11\xef\x18\x12I\x1e\xa4\x0d'E\x8dx\x90\xab\xa5\x93\x8eJq\x92\x0b{\xebN\x05 \xb2\xc0C;f\x1d\x8c\x1d\xd1;m\xcc\xab\x87\xbf{9}`\xd5f&T\xfd\x99\x81\xe8p.E\xb4\x02\xf3\xa1#\xf1\xd0)\xb6\x98\xd6\xbd\xec\x91\xd3\xfb\xf0>\x15h\xe0\xd1\xd0\x8d\xc7\xdd\xe1\x0b\xd0\x92\x1eP=!\xc3|L\x0c\x91\xe8 \x0e\xa9_P8\xb4zh\x9f\x1f:\x8fG \xf2\xd1\xf3w_9\xbb\xcaJgWY\xf9\xec\xca\x1b\xd9\x834}vu\xb0\x9d\xf6m2\xee\xd5\x0eV\x82\xe7\x1e\xe3\xf1\x05pI\xadM9\xb9\xb2\x14\x9a\xe0\xadmC/\xe0Sf\xac\xd7/\x06\x8a-\xdb6:\xed\xe0\xf6:(\xe2\x88\xf89z\xc4\xfa\xe6+\x1a\xc0\xd9\xe2U\x8ew\xfa\xe4\xa4\xdc\xa1'\xe4\x0b\xcb\xc7&?\xa6\xd5\x8fg\x93\xe9\xf3\xc9\xd3Jj5\xd3\x97qr\xcf\xfd\xedN\xf4\xbd\x019?\x9b>'\xff\xcc\xd96\xe6\xf7\xe4\x7f\xa2^\xbcJ\xc9\xe5\x96\xb3\xedo\xd4?\xe3\x1f!e\xe2\xc5\xe1\xcbj5\xaf\xbeyM\xbe\xf5=\x16\xa5l=!\x85\x18\x86j\xdc\xd28\xe3\x1e\x83X\x86\x01\xe6IOC_\x8c\xf5\xcb$\xd9%\x07\xa0T\x15\xa6\xb3\xd3\xd3\xad/v\xd9JAp\xaa B\x80N\xdbF\xe1\xb4\xf4\x0e[\xd1Q\xd9\x80\xbd\xddF(\x9e\xfcI\xf8\x81q\xb0\xae\x9d\xe2W\xac\xc4\x9c\x02v\x9c_\x94v\x9fe\xc6Q*x\xe6\x89\x98\xcfH\\_\x88\x19\x0fR\xf7\xb6\xb5eG\x9b\xeff\x1d\x1f#v\xfb\x1f\xfch\x1d\xdf\xba?\x97\xb7\xda\xae\xcay\xa6\xd6.\x9b\xe9{3\xf5\x1c\xc5X\xac.'\xd0\"\x0c\xbe\xa3\x14\x9d\xf8\xe9\x97A\x9c\xa2\x13\x9ck\x18\x89WT\xec&!\xbd\xebGj\xaf2R\xd2\xfc\x0cvK#\xa2\x1d\nT\xfd\xd5\x17\x7f\xa0KC0\"\xe1\x8b{\x0b\xc51e\xf1\xeeV\xab.\x86\x98\xcb\x8bfz\xf5N\xf0\x07\xc1[\xdbP?\x0dJ\xd0\xb2OGX,\xcc\xce\x8cnV\xa5\xe9\x04\xb7F|\xb5\\\xef\xddX\x8d\xc0w\xc1mc\x8c\xa8\xb1\xfaU\xbe\xb6\nj\x0bf\x02w@\xa0,\xc8\xf3=\x94\xfb\x17\x1a\xe8\xa8\x03] s\x15\xef\x02#,=\xf74\x14\xc1\xb7j8bb\x19\x95\x93'\x1e\x0d\x02\x13%FS\xe9\xc1(\x8f\x86te\xa3! rM\x04\x99\x91\x13\xbco\n\xbe\\\xec\xe8\xa0V\x08\x8c\xc7\x05\xf1\xa3T\xd0\xc8S\x85\xe2\x89\" \xaf\xe9V\x15.\xfa\x83\x9a\xd9\xd1}m\x89R\x7f0Y\xa9\xa7>+\xfaY\xea2\x88%\xd23k\x16\x05\xcc\xcf\xa8V\x01\x86\x9c\xbc\xb6\x0e'\x83\xcd\xb1\xa3\x94 \xe0TH\x9a\xe4\xd0\x0cF\x8e\xb3\x0cw\x17^\x15i\xf8q}(\x90\xffc:Q(f{QH\x9b\x141\xbf\x99T \xcb\x85\n\xd5c3\xa9\xd5\x1c\x18r\xc2ssV\xcb\x91!\xb3~k\xce^b\xc2P\xa4\x90\xe2&.\x83#f\xe6u\x81q\x1e719\xcb=f^\xf2RvZ\xbe\x80\xdb\x11\x85\xc5\xd2<\x1f\x05\x81\x05j\xb3\xef-\xc3me\x14l_\xbf6\x17(\x88,H\x05\xcd\xfbQ\x83]Jy?\"1p\x99C\x9e\xb3H>n06}\x81j\xaa~U\xc0\x1c\x19t\xd6\xbe\x7f\xe2\xf2\xaa\xfd9\xcfPIS\xb2\xabS\xfa\xa4\xabTp\xea\x89WL\xec\xe2u\x07d\xc0\xa0f=S\xae\xd7\x05\xe1Ph\x9e\x1d\x1e\x04R\x94\xc3\"\xe2G*\x9b\x98\xech\xfa\xc7\xdb\xc8F\xa3\x8fP\x14a\xf3hI\xd0#X\x03\xfb6\xb8\xd8\x05Fv'X\xb4\xee\x08#\x80\x87\xf2\x1f\xcb\xc5\xfbf\xe4\xaan\xe7\xde7\xdc\xcc)m\x15\x1a\x16\x98\x91\x18AW]\x1b\x9b^a;\xd1\x1b\x00\x93*\xa4\x90\x0e\x13L@\xde)\x14\xd2\x81F\x90\x99R\xbe\xcd\xc01V\x83\x843(u\x01\xc2\x03\xb6\xce\x0d-\x81\x07q\x19\xe9$\xcd\x12\xc6a\x01\xe2\x0d\xe95\x0b\x98`\xe5\xae\x8c*;2\x8a\n\x84\xa8\xd3\\\x07\x81\x9f\xa4~:k\xdd\xa2\x17\x7f\xd6\xa4K\xebh^b\x90\x04\x98\x83(\x0b\x02%VD\xe4\x9a\xf4&\x93\x9e\x12~1\xbc\xa21\xf6Rl\x1f\xf4\xfcc\x12Y\xd5\xf1\x90D] \xb6V\xecvDN%\x0f\x7f\xc19\xbd/x\xe8\xd25\x0c\xf2\x8e\x18eq5r\x83\xf9\x15\x96\xa1\xdd\xeb\xb0\xceG\"\xc4\x9c\xbb\xc0\x1aU\xd2\x95m:j\xc5\x87q\xfd8\xcb1 p\xff\xe5\x8bh\xfd%MD\xc6\xd9\x11\x03s\"&\xdb ^\xd1\xc0\x11\x9e\xf1\xcfP\xed\xf7l\xcb\xee\xfeL\xc2,\x15dG\xf7\x8c\x88\x1d#\x8f\xb7\x8f\xc9&\xa0[\x92\xb2Z`F\xf3\xcbG\xac\xb23\xbc \xb8T\xc1@\x8a\x81\xcf\x00}\xb9\xb9\x80\x1f\xf1\x08\"\xe9\xad\xd9\xdd \xdf7Eh\xbf\x82\xe1(\x8c9\x94Jl\xb5\xdf\xb2\x1b\x8az#Pw}\x84\xeb\\\xc6H\xb9Wf\x99!}\xec\xe3m+W\xdc\xdc\xdb\x9d/X\x9aP\x8f\xc1\x08\xce\x08\x04dr\xec\x0f\x8a\xfa\x8e\xc3\xdb\x02\xb7\xde\xc5\x86+\x8d\x18W\xa0\x1a9#O\x90\xb2\x98\xf2\xfa\xd5\xb7\x9d\xf0\xcanw\xbb\x80V\xdc\x96\x08,\x86\xa1UE12\xa5\xf95\nb\x95\xe6\x8eiMJ\xd2\xeb\xc4\x81S&\xbe\x10\xe5\xbdb\x87\xbbkzC\xa3J\xa6\xfd\xc1\x9c-\xf30\xba]\x1a\xdd\xd6\x1b=\xba\xc5.\xed\xe8\xce\xa5]\x1a\xaa*xtK\xad\x0b\xa9\x82\x829\xfeu\x01n[\x07\xae\xcb PU\x06d\xe8\xc2\xebU)\x0c\xae\xf9\xb9G\xe4K\xc5>\xbb\x8cH\xb1U=\x92\xfd\x1e0\xdf^M\xc3I\x1a\xe4\xbb\xf5\xbass\xb9\x9a\x0d\xd5hf\"\xa0\x82\xfe`\x94\xc7^\xac\x10\x14\xd4\xaf\xe9\xb9\xd0\xdc\x0bo\x11D\xe0\xf8\x1d\xefDr\xb5\x13W\x94\x17\xef/\x98\xc4\x0b\x98\xf4l\x92\xee\xfc\x8d\xe8+\x12<&\xb8\xed\xf7QrP\xdc\x9c\"\xc1l\xe2\x88n\x1c\x9d\x189\x85\x16\x03\xcfu\xc5\x0e\xce\xc2x\xcf\xfe\xee\x07\x8f\x16oX\x95FR\x0de\xbbv\x13\\p\xe2 _\xc0\xa8\xc3\xb1\n\x8e\xb7j\xc1c\xfdtD\x1c\xd7m\xc9!\x8d\xd9G\x9d\x89m}\xc9tY1\xb5\xe6;\x93\xe4\x1dM;\xcf\xbb\x15\x8e\xd0\x9a\xa3GzdX\x9d|\xb8(\xdc+\xdc\xa5\x81LL'w\x81(e\xe2\x1b\xc3?\x8f\x80\xaa\xc6\x89\x8f\xe3\x80\xae&\x8fk\xb1\xf3\x90\x1b\x1d\\\x87\x96J:\x8f\xa2\x16\xbcE\xe5`\xb2\x83\xce\x0f\xb0\xe2\x07\xc1\x0f\xf0\x96y\xef\xb2\x87\xd1\x95 \xaa \xf5\xdcb`2\xd2{\xd9\xcb\xa3\xf8\xda\x91R+\xbdwy\x8a\x05{/{\xcb\xa3T\xc7%\xf0:\x0c\x05\x8a\xcd\x96\x0bYA\xbe\x1a\xc5\xcb\xfc\xaaC\xa7\xd7G\xfb\xc0\xcd\x97\x87\x84j\xe2G\x84\x0d\x08sk\x03\x84\x16\x98\xc9\x90<\xc6\x08\x0b\xb0\xf5\xc0\xa8`\xed\xf4<\xa7\x16\xf5\xd1+\xa5\xbcW\xa2xMou\x84\x88\xfcQD\xdf\xceS\xdc\xa5\x89\xa2\xd6\xc9\xc8\xfcm\xbe?\x8c\xb4\xda\xa3-f\x06\x14\xe5\x1d\x98\x7f<\x0d@\x14`\x85\xd3+T\xb5\xe3X\xfe\x9e\xb3M\x7f\xd0\x82 ~N\"\xa0R\xedoZ\xcf\x04\xbb\x13\xfdBm\xa8\xb7oROt\x19\xbd\x02\xcc\x1d\x05f\xb3On\x1e9bm\x87Dc\x1e\x07(\xe6g\xf9:\xc2\xf6e\x8a\xbcC\xed&\xdb\xe6\x95\x1b\x13u\xa3K1\x1b'\xabA\xd5\x190\xb6!\xb9\"\xbd\xb7\xab\x80F7\xbd\xae\xaa\x942<]P\xae$\x81[-k\xfb\x12\x85\x93\x9a\xa1\xa5\x8dC\xd2\x1b#s\x9bu\xa4\xfc5\x8c\xe9\x02\xa9Uek`\xd7\xf1k\xadF\xae*f\x89\xbb\xd5\xbc\xc0\x11\xcd\x19b\xa2uT\xf6X\xce\xa8\xb0\x15\xbb\xc3@\x1e\x93\xef\xfe\xf8\xc37\xaf\xbf\xf9\x97\xaf\xde~\xf3\x87\xaf\xbf\xf9\xc37\xaf\xffc7\n\xe6<\xd69\x82\x8c\xa9\xf2z\x8f\x0f\x1a\xfe\xd3\xfe\xf5\xac7\x7f\xd3[>\xb9\xee\xc9\xc7\xf37\x8f\x97O\xae\x1f\xcb\xf9\x9b\xc7\xbd\xab\xcb\x97\x7f^\xa4\xcb\xe1\xe0\x14\x19\xdc\xe9\xfc\xcd\"]\x9c\xf5\x1e\xbf\\\x9c^-\xee\xce\xa6\xe3\xc5\xdd\xf4\xeb\xc5\xdd\xa7_/\x87\xa7\x134\x0fQ\xb3\xdb\xbf\x9e-\x16\xe9\x93+\xf5O\x0foM\xdao\x83\xeb\xde\xa8\xe8\xcbd\xaer+Vy\xd9?\xf9\xdd\x1f\xbf|\xfd\x1f\xbf\xfbj\xa0^u\xeab\x91\x0e\xf3W1\"= \xeeQ\n\x15\xaa\xcf\x83'\x86\xdb\xe2\xbb,Tq\xd9?\x85F{\xe0o\xe6t~6\xfe\x9c\x8e\xdf}1\xfeO\xcb\xfcq\xb6|rZ\xad\xb3\x0c\x81\xb0\xad\xa8^\x9d^\x17\xda\xcb\xf9\xf7\x88\xf4\xb6~\xcfE\x0b\xd5\xa0\x7f\xb9\xa3\x9cz\x82q\x13Q\xddhZ\xfa\x8f\xa2U\x9a\\\xc8G\xbf\x9e\xbe8\xbb\x90\x8f\x02\xa1\x9e\xe1q\x8b\x8f\xe7\x17\xf2\xd1OY\x0c/O\x9f\xc1\xbf\x9f_\xd4\xaf\xdb\xab\x1f\x989tA\xd8\xd2n\xa4\xb0\xf7\xb0\xf8Q\xb2\x8c\x98//PUzb|]\x82\xf2g\xfe\xf4@nE\x10ON\xc4A7\x1bAE\x93\x1b\x8f\x88\xd0\x9a\xbaf\xab\x81\xc0\xaa\x87\x91c\xa91Ut\xe7\x8bh\x0d\x93w\xff\x87x\xcdR0'\xf6At\xd1Zv\x7fD\xa2\x81M\xec\x17h\xfeWh\xa4\xa1\xca\xf5\xb5\x8f\x81\x81\xd6\x0d\n\xab\x1b\xa4M>\x86H\xe3fJ\x89wq!@\xc9\xa1\xa9\xf0\xaa\xc3\xd12\n^\xb7Q\xf0\xdc\xa3pD'4\xed\xf4\xbbP\xe5\x06(\x8e\xc3x\xad\xdf\x8dr\xb2Y\xd1I[\xba\xdd\xbcp\xf5~]\xaf\x8f\xc8*\xd79Z\x0eA\xd0\xb1\xf3C\xd3\x01{\xf89\xef\xb02\xa29\x07/\xb2\xcd\xd3E\x0b\x92t\x01\xf3\xd4X!\xda)\x84\xcb\xdc\x99\xf2\x91\xecg\x0f\x99\xba\xbaX\xd4(m\x14V\xc2\xd1'85\xc3\x86\xe2\xb2j\x11|Adh9\xe1\xb3\x92q\xc5\xe1Ds \x0f\xad\xa8\xaa!\x83\xcc\xef\x18Q5\x1f\xfb.H\xdc8\x12\xf9\x0c\x1e\x1c\x88\x0f\x06\xd9\xe0\xd4\x87\x00l\xf1\xf2\xe3\x81\xfb\xabr\x06\x87\xb4\xa4\x1a^\x9e\x8e\xb4S\xb0I\xffz\xe6G\x82\xf1\x08\xbc\xf4\xd1@Z\xf2\xe7\xc7\x91z\x01\x92\x14\xf3T2\x95-\xe1~\xcaR\x99\xecb\x81^i\xeee\xc2\xe35fO\xe5&\xce\xa25\xd4$\xfd0\x8cW~\xe0\xb3H\xfa\xd1:S}`\xa9\x0ciD\xb7\xb0VU\xb9\x84q%tI\xc1\xbc]\x14\x07\xf1\xf6^z;\xee\xa7\"\xa4\xa9\xf4\xe20\xcc\"_\xdc\xcb\xb5\xcf\x99\x82\xe1^\xb2u\xe6a\xf5\xec\xa7\xccO\xa0\x1e?J\x85/2\xc1dH\xf9\x0d\x13~\xb4\x95i\x1cd\x08\xd1\x9eb\x81T\xae(\xdfR_=\xc4\x99\xf0\x7f\xca\x98\\\xa1\xa20\x95j\xfb\xaedf\xe9\x05\x8cF\xf8\x10\x8b\x1d<\xc4a\x92 \xc6\xe5\x9a\x85\xb1\xc7\xa9\x90k\x9f\x86q\xb4N%\xf4\xdf\xf7R\xb9\x8b\x83\xb5\x1fmS\x19\xf8\xdb\x1d\xb4\x9fP.\"Us\x12d\xe1\n \xca\x92$\x80\xber\xeaC\x13{\x16)y4\x95\xd4\xa3k\x16\xdeK\x8fr\x06\xd0\xc4aB\xa3{\xe9\xf1\x0c\x06{\x1d\x87\x007\xbbK\xe2\x94\xad\xe5\x06\x9aI\xe5&\x88\xd5X\xc9-\x0d\x02\xc6\xef\xe56\xf3\x05\xe5\x00\x8e\xbf\xa6\xf7\xf2\xc6WX\x11\xc9\x88e\xa9\xa0\\\xc67~Do\xa9\xe4\xcc\xf3\x13\x96J\xce\"A\x03\xf5w\xef\xb3\xdbT\xa6;\xff&\xddQ\x89\xce R\x009\xe6B\xa6\xf7\xa9`a*\xe9\x96E\xde\xbd\\1\x1e\xf8\x91\xf4h\xc88\x95\x1e\xa0\x85\xf4\xe2\xcd\x861\x85/\xeb8\x95\n\x05\xa2\xadd\xa9\xa0\x82I\xa6z\n\xe03.\xe4&\x13\xab8\x9074\xdb\xb0H\x06\xd9]\xc6\xefeH\xfd4\x8ed\x18G4\xdd\xc90KY\x16\xca\x88n\xe3{\x8a\xb8\xa6\xa0L\xa8\xcf\xd5\x1f\x80)\xf6|\x1a\xe0\xa8\xdeKA\x85\x88c)|\x16\xad\xa9\x1a\xe1=\x0b\xe4\xde\xa7?\xb2T\xee\xfd \xa0\xeaO\xaa\xd0f\x1f\x03d\xfb\xf8\x9en\x99\x04\xccF4P\xa3\xbfN\xa5\xb7c4\x91\x9e\xdaw\xc85\x8d<&a\xd1\xcam@S5\xb2Y\xaa\xd0,\xda\xc62\xf2\xa3\x1f)L\xb4^\x0e2\xdd\xc5j\xd4\xe2\x80r)b5\x03\"\xbe\xb9\x8f\xa5\x88\xe3 \x95\xb7j\x8d\xca\xdb\x98\xdf\xa4\x922\x1eK\xca\x13*i\xeaS\xb9b\xa9\x90+\xff\x86\xc9U\x00h\xf9\xee\x9d\x1a\xdeDzA\xb6\x92^\x1c\xabU\x19'rCy(7~\xba\x93[\x7f#\xe46\xe3\x99\xf4\xa3M,\x7f\x8cW\xa9\xbc\xf1o}y\xc3\xd9Z\x064Z\xcb\xc0\x0fc\x19\xf8\xd1\x8d\x0cY\x94I\xb5\x18e\x18\xaf\xa9\x8ch\xc8d\xa2\xf06Q_\x938\x15\xf2\xa7$\x8e$\xf7\xbd\x9d\xe4\xd9\x8e\xcb\x94\xdd\xddK\xe1'\xa9\x1a/\xa6\xfe\x89\xe5-\x8d\xb6\xf2V-\xe7[\xff\xc6\x97\xef\xe2\x88\xa9%%W\xfeZ\xae|\x05\xf0J\xad#\xe9\xb1Xa\xb0Z\xaar\x1b\xef\xa5\x1f y\xe3\x872\xf4\x03\x191!\xe3(\x901\xdf\xaa\xe5/\x93l%\x15\xc0\x82\x052\x8bby\xcb\xd6\xf2\xee\xeeN\xde\xdd\xbf\x93\xd4\x93t-)\x93t#\xe9VR_\xd2@\xd2P\xd2H\xd2X\xd2\x9f$\xe5\x92\xa6\x92\nI3Io%\xbd\x93\xf4\x9d\\Q\xb9Z\xc9\xd5Z\xae\x98\\m\xe4j+W;\xb9\xf2\xe5\xeaG\xb9\n\xe5*\x92\xabX\xae\xb8\\\xa5r%\xe4j/W\xb7ru/W\n|\xe9y\xd2[Ko#\xbd\xad\xf4v\xd2\xf3\xa5w#\xbd@z\xa1\xf4\x14)\x94\x1e\x97^&\xbd\xbd\xf4n\xa5w'\xbd{\xe9\xbd\x93k&\xd7?\xca\xf5\x8d\\\x87r\x1d\xcb\xf5;\xc9<\xc9\x98d[\xc9\xb8d\xa9dB\xb2Ln|\xb9\xf9Qnn\xe4&\x94\x9bXn\xb8\xdcR\xb9]\xc9\xedZn\x99\xdcn\xe4v+\xb7jb\xe56\x90\xdbPn#\xb9M\xe4\xf6'\xb9\xe5r\x9b\xca\xad\x9an\xb9\xbd\x95\xdb{\xb9\xbb\x91\xbbP\xee\"\xb9\xe3r'\xe4.\x93\xfeZ\xfaL\xfa\x81\xf4C\xe9G\xd2\x8f\xa5\xff\x93\xf4\xb9\xf4S\xe9\x0b\xf9#\x93?\x86\xf2\xc7X\xfe\x98\xc8\x1b&o\xb6\xf2f'o|y\x13\xca\x9bH\xde$\xf2\x86\xcb\x9b[ys/o\xde\xc9\x80\xca`%\x03O\x06\xbe\x0cnd\xc0e\x90\xca@\xc8 \x93\xc1^\x06j\xa9\xca\xd0\x93\xe1Z\x86L\x86[\x19\xeedx#\xc3@\x86\xa1\x0c\xd5\n\x96a\"\xc3\x9fd\xc8e\x98\xcaP\xc80\x93\xe1^\x86\xb72\xbc\x93\xe1\xbd\x0c\xdf\xc9\x88\xca\xc8\x93\x11\x93\xd1FF[\x19\xf92\nd\x14\xcb(\x91\x11\x97Q&\xa3w2\x0eeBe\xc2d\xb2\x91\xc9V&;\x99\xdc\xc8$\x90I(\x93H&\\&\xa9L\x84Lner/\x7fR4M\xf2X\xf2T\xf2L\xf2[\x99R\x99\xaed\xea\xc9t-S&\xd3\xadLw2\xf5e\xfa\xa3Lod\x1a\xc84\x94i$\xd3X\xa6\\\xa6B\xa6\x99L\xf72\xbd\x93\xe9\xbdL\xdfI\xe1I\xb1\x96b#\xc5V\x8a\x9d\x14?Jq#E E(E$E,E\"\x05\x97BH\xb1\x97\xe2V\x8aw2\xa32\xdb\xca\xecFf\xa9\xcc\xeee\xf6N\xee\xa9\xdc{r\xcf\xe4~+\xf7\xbe\xdcGr\x9f\xc9\xdb\x8d\xbcM\xe5=\x93\xf7B\xbe\xa3\xf2](\xdf\xdd\x0e\x16\xab\xd3\xaa\xe6\xb47\"\xe8\xffoq\xbb\x1c\xfc\xa6\xbf\xb8\xfdy:\x9a>\x7f?0\xba\xcc\xb2:\x14r_\xcf\xe6\x8b\xf1\xc5\xec\xd1\xd5b\xb8\xf8d\xb4\xb8]L\x96\xc3\xdf\x14\nD\xf6\x897Ub4\xa3\xb6B\x94\x19\x96\xf3\xf1dh\xc5\x87\xe5p\xd6\xbf>i\xfa\xb48]\x9c\x0e\xfa\xd7'\x8b\xf5pqz=\xe8_c\xca\xb5\x13\x90\xbaJ\xb7?\xb9>E\xa5\xaej\xff\xf6\xf6v19\xbadsG\xad\xf6\x17\xd4\xc5\x8b\xb1\x05|\xf8\xe87\xbf^\x9c\xfe\xd3\xd5\x7f~\xdb\x1f\xc8\xc7\x9f\x80@Tg\xe1O\xbc\x0du\xc8\x11\xb3@\x8c\x0f\xaf\x03y\x12=\x1a\x7f\xe2\x81&-''Y\xb7\"\xdf\xb3\x80\n\x7f\xcfl\xb9\xcd\x81S\xc8\xa3/\xfa\x117\x99$\x87NX\x9a\x87\xd0\xd2\xf7\x19I\x9a\xa1\xb54\x7fF\x1cZc\xf3\x0b\xb1\xdf\x0d\xc1~\xba\x10\xf7vj\xd4E\x08\x81\xdb\xe4\x03\xe3bX!\xf9\x17\xa2_\"W\x87\xf8\xb4\x00$\xc6\x95r\xba\xe8\x9fn\x0f\xdc\xb7\x8fJ\xf9\x07\xa7\xdb\x03<\x1b\xb9\x80\x0d\x0e#%9\x1b\x90K\xd2\x07\xf2\x14\x95\x92-!?9\xeb8\xa6$\x9fs\x87w8\x976\xf2UU0\xeb\xaa\x84\xf4#pK\xd5(X\xce\x17\xb7\xcb\x06\xc1rG\xd3\xaf\xb3 \xc8\x8b\x9a\"-\x12\xbf\xa3\x9a\x8c\xfb?x;\x16\xb2\x83\x15\xb8a\xf8\x0f1_\x7f\xa90d#\x18\xaf\x023\x9b\xbfY\xa4\xcb'\xd7\xa6JG\x15E\xe6\xdb]\x1e5\xd3S\x94\x06tM\x7f2\x1dR\xec\xca\xdcb\xc94!\xfa]\xcc\xd2?\xc4\xe2\xf7to)\xf6\x1f\xf9\xefb\xa1\xad\xd3Z\xb2\x7f!\xbee4\x15\x7f\x8c\x98\xe9q\xa5\x8c\x9f~S\x9b\xcc\x9c\x92\xf5]\xe7\xf1\xce\x13\x89r'\xba,\xd7\xea\x82\xd3](\xce\xeb`~\xb6,\x1f\xac\xb6J\xf1\xbd\x1f\xe9\x9e\xa6\x1e\xf7\x131Cg=0\xce\xbd\xfd\xaa\x9c\xd8\xa5G\x87\x86\xbe\xa3\x89\xa0\x9d\xf1\x13\x86\x8e\xe7\xd5\xfa\x07\xfb\x00\xc7:@\x9fw89c\x13A\xdb\x1avO\\\xded\xbbA^\xc7\x82\x87\x81\x7f\x827&NL\x0f\x9aWQ\xcdW\xac\xf99\x91\xa7\x0d\x05\xbb\xa0\x92\x01\xf3\x84\xd9\xf1m#Q\xcd\xc09\x88$\n#P\xf8\x08\n\xf9Q\xf6\xcf]\x06\xef\x01\xc7\xbc\xaf\x8abS\xd7C\xae\xc2\xbe\x18Jv\x84-7\xf5=\x06\xc2\xa2\xc1\xa6\xb3T\xe3<\xc1\x8e\xc3q\xf6W\x98\xc5\x8fs\xe6\x87\x1ej;\x8e\xc2W\xb8\x7f\xe9Zy\xbe\x1f\xecX\x7fq\x94\xbb6R\xf4g\xfb\xc0\x06\x1f\x80A\x0d\x8d4\xce\xa7\xde\x8a\xfd-fT\xef\xd5\xba\xce\xe9\xeb\xf2\xd6\xaek3E\x0d\x00\x96\xed\xd8\xde\x83\xe6\xd88N\xd3\x0d\x82\xe74;\xe1\x0f\x87\xe2\xb8\x89\xef\xfd\xa6k\x93\x8dh\xf0'\xfe\x80E\x9d\xf1\x00\xf7S\xb9\xc2\x13\xc6\xc3(\x8d\xfb\xa8\x00\xbe>uY\xc3VX\x91\xad\xa2A\x1e5\xf9\xbf\xe3,a\xd1\x9a\xad?\x96\xedI\xc6;S\x99?\xf1.4\xa6tO'\xe3\x0dJ\xa2\"\xb6:\xf7\xb8V\x80\xacn\x9ak\x1f\xec\x90\x94}\xc3d0\xa5=\xed+\x10\xcc\xbdGM\x05!\xf4}G\xaf \x0f\\*\xd0\xb2qv\x9e\xfb\xf4~D\xc3\xe4\x02\xe21=\xeav\xcd\xea\xd85R\xbd6\x05\xed?tN\x8c\xbe\xae\xa8P(\xe7\xc3\x05\xd1\x07\xe7XU\xb5\x83\xa3\xf8\x9f\xcc\x12\xc2\x12\xf6#^`}\xcd\xa9\x1f\xf8\xd1\xf6\x87\x80B\xcc\xf6.\xe3S\xae\xb6\x8bl\xe4V\xd1\x97\x17\xb7\xdb\xe1zS\xf3\xeeAy8,Nb\xd1\x19$\xc7X\x1e\x01J\xef\xb4M\xe1Q\xd4\xe0\x1a\x87\xab\xe3i'/F\x8a\xfa\xda\x94\xf7#\xedh\x11c$\xf16?\xa5\x1a\xb0x\x92\xfb\xe5\x84\xbb\xc0\xf9`\xbc7\xbeeFd\xbe\xc4(>\xfd\xa2\xdbx\x1d\x8a\xeaC\xa3a\x1b\x8c\xc8<\x0fa\xde\x1b\x91\x1e\x04\xa4\x86\xf02\xea-\xf0S\xd1s\x85(\x9d\x973Bm\x9f\x7f@m;\xaek9?\xfb\x80Z\xe0\x93\xaeg\xdaZ\x8f\xbb\xbc \xcbm\xea8\xaf\xd4\xd1\x00;\xa3k?\xda\x9aBO\x1f\xd0pP\xa9\xe3\x99{\xf6v\"\x0c\xa0.\x93\xef\xf9\x03\xda\x12t\x15\xd8\x1e~\xda\xa9\x87k\xb6)\x0em\x15m\xdc\x85\x8aPA\xb1\xcf+\x81\x0d\x97\xee\x98x\xd5\x05\x8a\x14<\x0b\xacW\xb6\x8a\xcb){\xdd\x81\xa1\x1b\x1bF.\x89o\xaf)\xb0\xe1pP\xa8BG\x92\x9f\xb3%\xc4\xe7\x82\x87\xe9\xd2%\x8e\xd1@\xcc\x08\xe6<\x87\xf3\x85\xf9r\xa0\xa9\xd2\xa0BzrJa\x9fh\xc1\xad\x11\x04\x82\xf0\xdf\xb1\xaa\x835\x87\xe6\xcd\xf6E{\xfb-\x00\xbee\xe2\xfb,`)\x1e\xa3\xa3\xa3\x04\xec$\xbaH\x10\xe8\x10\xe1dzA(\xb9\xd4GHl\x12\xf8\x91j\x98\"Q\xbd\xf1\x93\xaf\xc2D\xdc\x7f\xebG,\xedS\x08m@\xc9\xcb+\x12\xa1\x17\xfe\x93>\x9b\x88\x1fv\xfeF\xcc\xe9\x12\xae\xdb\xac\x82\x9bo\xa25\x8b\x84\xfb\xfa\x13\x00\xccq\xe0\xe1F\x08\xd4\x12\xcf\xf9Ru\x91\xc2\xf1\xe6\xc9tpA\xf8p\xe8\x90\x130\xea\x85\xf0\xb7;\xa1`\xcfF\x84M\xfc\x14@4\xb0[\xbe\x90\x19\xb9\xaa\x8f\x9dQ_\x07\xa6\xa7y1\xda\xa86W\x8da%#2\x1c\xdaAB\xaa\xa1\xb9RB9\x8b@\xe8\xad\xd7\xda\x12\x0e&\x1f\xe7\xda\xe7\n\x9f\xcaq\xa5\xcc\x0420S]D\x0bQ\x8b%\x99\x82q*W\x1f\xb3\xb3\xb3\xcf\x9e/\xe5|\x91\x9d?;\x7f\xb6\xc8\xce\xcf\xce?\xd3\x89\xd5R\x01\x94\xca\xce\xce\xe8\xd9i!,X\x111\xe1\x8e\x91\x03+G\x84W\xc7P\x81\xe8#\xa2\xb9<)\x03\x02\x94\x92\xe1>>\xb3\xc7\x02\xd5\x9b\xf3\xc0\xe55\xab7\xc2I0\x02'\x10\xb98\x9b\x8eHo\x11\xa9\x14\xabU\\\x88\xde \x8f^W.\x9f\x15\x18p\x93Z\x1b\xd6V}\x0e5\x94\xd3\xb3\x82p\xf2e\xbcf_\x88~4 \xd7:,,F\xf9\xf3t<\x14\x08\xfe\xa6P\xbf\xa7j\xe8i\xda\x00\xee\x85)\x19\x13o@\xfe\x89<3\xc7\xb5\x90\x08\xc5y\x95z\xe8\xd5\x8c>\x15\x99\xf1\x07k\xe6\xc1\xdc\xab\xd54\xa4\xef\x8f\x14q\xf3#f\xfe\xbe\xa2w\x05\x024*\x05\xb4Al\x1fz\x1epZ\x86U?@e\x18kM\x9a\xeb\xae\xae\x96\xab\xdf\x8a\x00\x9c\x0dj\xa8X\xac;\xdf7\xfd\xaa\x0e\x08/\xbaUD\x1e\xd6\x1a<\xa0\xb8Y\xc7\xfa\xe7li\xd5`(\x11\xb0\xa5\xa2\xbc\x85.\x14=\x9f\xbd\x1f\x95\xda,K\x1a\xadM\xd7]\xda\xeb\xfe\xa2(\x87g\x8f\xfdC\x90]V\x00\x1b\xa0\xe8w\xe1\xea%k\x83\xfa\x87\x84zGC\x9cr/\x978\x0d\xd0z\x15\xd9\x0c\x85%\xc8\x1e\x0c\xde\x97;\xca\xd3C\xaezKn1\x9d\x00F\xf6\xe4\xa9\x06\x19\x02\xfdA\xf0\xfd\x96z5w\xc2\x0e\x86\x0c\xd2\x1f\xb9\x04\x97\xf8\xa6n\x07\xdfP\x10\xbf$\x91#b/Z\xaa\x9d4\x0c\xf2x\xccr\xbb\x04\xa6\x96\xedq\xdd\xd92Q\xc7\xdeV \xa9j\x19\xa98]],b\xb0\x8c\x1a=\x14\xa9,\x81\x82\xb6\xe2\x92\xd4/\xaf\xffy\xa0V\x01F5\xf0\xf1\x10\xce,\x87`9\x02\xb7\xad\x8acpr]Z\x19Pjj\x1c\xc1\xdb\xc4Q>\x82(\xc7\xa8~\x0c\x1c\x93\x91iQ\x05|\xb7\xf6\x05\x19\x83\xe1\xac\xf6 \x1a(\xd4\xbf \x81\xa2\xbc\xf1p8\x80\x88ne\xc8\x06j*Ax\x03&?\x18\x01\x07;\xb3)gZ\x1c\xaa\xf54\xc5\xfe\xe0\xc8\xa8\x15&e\xf7\xcee\xf3xY\\\n\x8d}\xd4c\x9d\xd5}UUD+\xb4\x8d;J\xb42\xa9\xee\x90\x83\xee%b\xf6\x82\x0e,2c*\x96j\x12\n\"\xcd%y\x96\x9b\xe3L\x1ds\x18\x03^\\\x81\x8f\x9a)\xee\xdb\x9aVW\xbe\x03\xe2j-\xb9x~\x8b\xdd\x1fl\x02rHy\x15\xd2\x97W\xe4Y\xfb\xc6J\x81:\x1c\x1er\x06k\xf5\x9cZ\x86\xe3\xa3<\xf6{C\x8c*\x1d\x8b\nUf\xb5\xaf6\xe6TN\x05\xd4\x96\"\x1e\x91g\xe0\xe8\xc5va\x04[\xd2ZyP\xc2\xb8\xaf'*\x10\xd3\x19\x99\x8b\x91\x86\xd7\xa1<\xd1\xe1\xab\x18\xca\x8c\xa5\xcf\xef\x95\xf0\x96\x8bI\xef\x7f\x194\xecN\xdf\\\xc7F\xe8|C/^\xb1\x84\x11\xb3\xc8Z\xcf\xbe\x81\xec\xccd\xaf\xa3\xbaG\x86\xe4)yI6\x8dh\xadrM\xcf_\xa0\xd7\x96\x18u\x1def\xe0\xa1\x82\xe3s\xcc\x13\xb7\xd6\x04\x92\xf7\x08%\xe7\xbeg5'\xc0\xda\xfa\x9e\xda\x03\x0d\xc8\x98\xa4\x03rI\x9e\xb6V\xa45\x159\xc5\x01C\xf9\x89\xe0~\xd8/\xeej\xff\xac7\xb5\xad\x95\xf1\x82\x8d]\x03a\x16\x17\xe4\xa4?\x1cf\xa8\xd1A\xc1 :\x90\x16g$+\xcdH\xb6\x04\x9b\xbe\xd2$\xa84P\x7f\xd8<5]P\x03\xb5\xa8\x8d:0\xb1\xb8\xa2[\xca\\\x84\x00\x04\xf8\xe6\xd1\x06\xe5R9\x0b\x8aj0\xb5\x10\xb0\xbe\x81\n\x01\x9a\x9e\xb9\xe9\x0b\x90\x9en\xd4\xc5\x87vs<\xce\xc9MF\x86\x8ae_\x03\xeb\x81\x93\xbfn\xc4\x07\x94\xf1\x0e\xea\x93PN\xc3tFhG\xc2\x84\x8a\x85\x0c\x16\xa7\x93\x1c\xfd{\xa29\xf5\xb0\xbb\xc7Q\x9b\xf0\x10\xb5\xd9\x93\x97$l]\x89/\xce\xb5\xb1[\x05\xdb\xf7\xc3\xe1\xa0\xb5\xa0\x1e\\\x85\xeey\xac\xdf\x90\xde\xfd\x81\xa5\xc2\x8f\xb6\x1f\xb2\xfc\xf5f\xa3\x0e\x13\xac\xe4\xbd\x92\xc84\x11\xc8Y\x17\xab\xeaA \xeaaa,\x01\xc9\xf3\x91\xbd\"{\x14\xce X\xed\x9e\\\x92\x10\xc2\x11\x15\xd6\xe2~@fd\x0f\xd4,D\x81m^\x98\x0d\xa8/\x17[T\x1d\xe3b\x0b#\xcd\x0bP-TS|\x17\x8e6\x8cO)\x94`b\xb3\xa39\xe9\xf7K\xe8\x10\x97\xd0!^\x02`\xfd\x12\n\xc4\xcb\xc1\x00\x03\xa09IZ\xfb\\7\x8b=~\xabXc\x03+\x9fLGpW\xe7\x0c\xaf\xa6l\xec&-!\x97d}A\x92C\xb1\x0b6\xf3d\xa9/eE\xb0\xfa\xdbt6\x04\xaeA4SC\xf3sSE\xf3k\xf6\xd0\xb5k\xedtf\\\xfd\xdb\xc9Q{\x14\x93\x98\xcf\xd1\xa88c\xa0A{\xfa\xf4\xd3:\x8dF\xc1\xb3\x03\xde;\xdb-\xa2\xc8\xf1x}\x18\xe8\x12f\xc7K\xc7\x8a\x0dH\xf9\xc0aT>~\xb8\xaa\x9c{v\xe4)y\x99\xa6\xa0\xc1\x9a\x19@\x84g1\".wue^P \xed\xfb~0\xca\x97\xa8\xd5K#\x11\x8f\xbb3\xbf\x02\xa0M\xf1om\x9c\xdb&\xa6T\x190\xc5\x1b\xe6\xd3\xa5=\x1d\xd2K\x0b\x17\x13\xcd\x97\x16F\xac\xd6s\x93\x90!\x01Z\x94\xcd\x93\"}\xb2\xe9t\x9e,\xdd\x8a\x83\x12\xf9L\xff.xd\x99\x17:\x0cJ\x0eq\xbf~F\x86%9Gm\xd8\xd3V\xce\xf4\xec\xbcE\xee\xce\x80N>zD\x9e=G\xc9\x1b\xa4\xf0\xe7\x07\xa4pX jEN/HF.I\xea<|\xac\x88\xd8\xb5Vm{O\x11B\xda\xd8\x1e\x01\xbfrVT\xf5\xab(\xef\x9a\xfe\x93\xbe\x8f\x1b\x80G\x8fH\xff\xe4\x84k\xbb\x10-\x13j\xa1\xac\xe3b\xd8\xf1\xe6\x85\xfaaR\xdb\xa0z:}\x14N\xda\xe4\xcai\x90\x0b \xf5\xf9\x90s\xa9\xf4y\x9b\x90\x86\\9.\xa3\xe6\x80\\\x93\xb1\x12\xa8\x0dzE\xae\x89\xe6\x15\xf4\x02)\xe0\xd9S\xfd\xack\xe0\xe4\xb2\x84\x07\xf5Zlc\xbc0Z\xf5\xce\xc7\xad\x9d?N\x0e\x8d\x0f\xadD\xf0\x83\xa8F&_&c\xd7\x1e\xb3e\\.\xc9\xb3\xcf\x14ZF\xe4%y\xfeic5\xa8em\\b\xbc\x1d\x08b\x15=m\xa0\xa8\x1d\xdegj\x0e\"ry\xa5\x80i\x13\x9e\x9e\xa1\xee3R\xb0?{a\xa2\xa6\xb6\x88\x16\x16\xb4\xda\xd7\xa6\xe3\xf7B\xa9\x07\xa2\x87yj\xa7\xd7\xb534p\x87\xd9\xb2\x9b\x19)\x01c;\"\xf7#\xb2\x1a\x91\xb7#r;\"_\x8d\xc8\xdd\x88\xfc0\"_\x8e\xc8\xcd\x88|\xe1\x10\xe1\x00\x15\x94\x08\xa9q\xd4(\x14\xb6\x8e\xbc\x0d\x1a;=\x89\xaa\x12^\xaa\xa4\x95lB\x03\xd3\x96Q\xfe\xd0\x8dO\xe8B\xaa\xb5\xbe\xcf\xed\xb7\xef\x8aV\xb8gG\x12l\xace\xb6\xe4\x1a\xef\x017\xafV\xd8T\xa2\xffj\xad\xd4\xd07\xca\xd5<\x911I\xf0~fg\xfa\x1e\xf35\xe3l\xfd6\xf0S\xd1$\x97A\x9e\x19\xd972\x82\xdb\x87KlJz\xed\x08\xea*\x0b\x02&Z!\xfdpx\xac\xc9\xd2[\xbd\x07\xbak\xdb\xf7\x81\x81\xce\xe0\x82\x9c\xf4O\xfa`\xb6\x836\x98\xb0\x81\xea\xdfW\xd5AkD[K[\xe9Rkf\xee\xc9\x98\xac\x958\xf3\x0cX\xb6*\xadPhG.\xc9\xb4\x94\xa2\xa4\xa8uQ~\xa7\n?v\x9dg\x1b\xc6\xce\x17,<0\x80_}\xc8\x00\x06\xd5\xdd<\xea\xc5\xc0H\xc1\xec\xf5\x0b\x08\xbdq\xec6\x8a;\xf1\xfb\xeaN\xbc,\xdd\x82e\x965\x808\xab\xefU\xb4}`\xd3\xc6\x00\xf7\xa6y%j\xaf\xfe\x16f\x11\x88\x99\x1a\xf5\xb7Vn'c\"\xc8K\x9c\x14\xa7=X\x15\xba\xa0\xda\x9b\xb4\x08\xaeW\x83v\xf3\x80\xa9|\xf0&\x050\xbd\xb0'\xf9\n\xb7(tD\xee+\xd2:\xd1\xa6xj\\\x8a\xa6g\xf8~\xbc]\xde\x8d^\\?\xa0\x82\xe1KrE\xee\xec.\xe8\x07rI\xbe\xbc ?4)\x18\x14\xe9\xbd\x9b\xffP\xb4\xe3kW.\xdc\x1cP,4+\x15\xea\n\x05\xd5\xf8M#\xc7W_\xb7m\xf2C\xce\x08)HAg\x83&Eo\xeev#\xe7{\xe52\xee\xe6C\xb7\xa4\xb0\xd6\xf7\xf6\xeb\xad5\x1cXuAB\xc5\xaf\xca\x1c\x04q\x91T\xa8\xf5\x831\xf4\xd6bdn\xc7\xa8\xa4\x8cG\x8f\xda\xcd\x0cHY\xf2G\x1c\x07>?$\xe7\xf5q\x03\x9c\x8c\xf4\xde\xe8\xdc\x08\xcc%\xe6L\xc6\xe4\xbc\x14\xb7\xd3f\x98GKcAevi\xb9\x851\xd2Y\xad\x08\xca\xf3\x0bm\xc6\xd9\xcf\x13U\xcb\xcb\n!+\x14(\xa4G\xe8\xd8\xbc1k\x97\x82\xa1\x7fO\x9b\x8bv$\x08\x99\xb6g\x1b\x92sT+\xf43\xb3\x0b\xf4\x14\x17x\xfe\x99{\x08\x87\xc3lPVDd\xc3\xa1\xc2m\x16\xed'\xe6VCjn\xae\x94\xd2 \\c-\xeb\x84\xb3\x8d3?~\xd0\x85R+\x9a\xe3\xf1f\x80\x0b;S\xcb\xb8\xa1\xcey\x0f\xae\xf0\xa6Km\x1a\xd9\x8d\x04\xda\x9b\x19o9\xdb0\xce\"\xafY\xbdIW\x8a\xda9\xe2\xe1\x1f\x14\xa9\xe2*?\xae\x1d\xf9\xd1\x03RTI\x10\xcd\x06d\x8c\x82S\xf1\x08%+\x0b/\xc3+\xf2\xac.M\x15.\xa2\x14\x1b(1~C\xd9\xec\xd7\xe1U\xedx\xc7\xb6;.}k\xd1\xe0\xe6\x82Z \"Z\x86z\xac\xa1.\xf6\xdd\xaf\xf64\xfe\x90\xd9}03SR\xca\x07\xe9\xbcL\xea\x07Q\xe7\xe3\xe8\xf2A\xad,\x9c\xe8\xb7ka\x9f>o\xd3\xc2\xe2\xb5\xb5\x03\xd5\xe4ZW\xb3\x16\x1cd\xe6\x82<}\x9e\xf3`P\xce\x82\xca\x94\\^\x91\x17\x17\x03\xe2\x83\xf1Wci\x17\xd5;\xe9\xfb\xe4%y\x81\x10\xea\xfa\xb4.&.S\xb5\xd4\xae1kg\xd8OG\xe4\xa9\":\xf9\xcd\x90\xfa\xf7\xe7\xea\xbb\xda\xfae$7\xcc\xac\x01H\xf3\xcb&`=?(\x08DG\xeas\xf1:W\x13\x8d\xda}\x8bX\xec\xb8\xc9\xfd\x11\x94\xbev\x0c;\x02\xebG\xaa\x9dv+\xa8\x9c\xc6CH\x1fm\xc2r\x084\x18\xb3\x07u\xd1\xdb\xf9\xc1\x1a\x1ci\xcd\x97\xb5\x0ev\xec\x97\x99\x84&R\xd26\x0b\xbf\xacZ\xdd\xa4>\xc4\x12pd\xee\xe1\x88F\x8bV{\xa7K\xcb\x10\xcd{GG\x86\x8aa\x8e=\xe0\xe8\xf7K\xec\x91\x96\x88\x1a\xd5:|\xbfH\xc8\xe8R\xcb$\xfdg\xcf\xf3\x8b\xb8\xb5U\x17#mz\x81:_\x8eE\xe2\xf2B\xee\xc7x\x17\xc6BQ`\xb31l\xd7\xfcb\xb9F\xb5^\xe1>\xdc/\xb0\x9cM\x17\xb4\xbe\xe9\xfca\xa8\x7f\x00\xf7:\x82|\xdc\xa2\x06V\x9d\x1f\xbd|\xdc\xe5\xad\xa8\xea\xbf\xf2\x12\xef03\x87W\xfc\xe0# \x16\x85;\xdfg\xe7\xd5\xbb\xdd\n\x81O\xdf\\\xf6\xe7:x\x9fvu=_\xa4\x8b\xd3\x97U\xd7n>f^\x9c:\xb2\xbf\\\x9ev#4#B]\xb4&?\xa0\xa8H\xc5\xb5\xa1\xab\xd8o\xd63$e1\xba.\xbbxJvMF\xe4$\xdf\xdc\xedD\x18\xb4\xca;\x89\xa2M\x8apx\xb0[zyu\xc0<\xf4\xc5\x99{\xeb\xe4\xb5\xef<\x9f\xe2\xa6\xae\x9f\xb9H\x97\xa7w\xae\x8a|a\xbe\xaci_Y8{._rz\xdfv\x1c\xf3\xecS\x00\x1a\xa4\x96\x93\x96\x1b)\xe6g.\xa5<='\xb2z\xf5\xc0\xfc4\x18`t\xf9\xf9\xa7\xaaf\xa1d\xb7\xe9\xf9y-\xfb\xfb.\xdb\xdeg\x9f6\xf7\x9c\xd8c\xa5\xeaV\x11-a\xd1\x95\x9e?(\xb6R\x87\"W\xd2\xb5\xd7\x13\x0f\x0eC{\x82h\xc0\xe7\xe9|Zq\xd6\xb7o\x0b\xd5m\xfcm\xc6\xa1U\xb5\xb3e\x1c\x9fx\xa8\xfe\xee\xa6\xf0\xef9\xfc\xfb\x14\xfe}\x06\xff>\x87\x7f_\xc0\xbf\x8c\xae\xb1\xd4\xce\xc2\x03\x1e2z\xfe\x86\xd3P\xbb\xc1P\xff\x86\x14>\xc6\xe0\xd9\x0f\x9e\x00\xd28\x13I\x06\xef\xf09A`\x12\x1eo9K\xa1\xf3\xe8b\x12\x9e\x98g\xe0N\xc5=\x8e\xa6\xf1\x11\xd1\x13f\xd8\x04tY\xb0;A9\xa3\xf0\xbc\xc1\x0b\xaf=\x01~'\x04\xc7gF!g\x06p\xec\xfd5\x8b{\xcb\xc9&\xe6_Qo\xd7o\xb9\x808g\xcb\xf2\x0dP\xad\x95\xfa\x90\x1b76\xb9\x8b\xf9\x8aCr\xcc\x95)\xb5u\xc0\xdb\xb6\xecv\xf9\x16N\x8e\xc1BdL\"\x97\xb7\x88v\xf6\xdc\xf5\xcau\xd1\x8a\xa0\xce\xc8\x04\xb2\xc9\xc2];\x17\xbb\x0bJ[]\xe4\xd8Am\xd7\xd0RA\xbf\xa4\xfa\x08J\x12x\xb0,\x9f\xcc\x06\xcd\x14\xd7\x87\x0b\x1d\xa80\xd6\xbb\n\x87J#\xb7\xfb\x81\x1b\xbfZ;\xea\xb7\xd6J\xady\x030\xef\x1199}3\x1f\xcf$Y\x0e?9EW\x9b\xb4]$\x80\x1b\x08\x14C\xa9\xf6{\xb2\xa7\xf6\x1f\x10\x03\xb5M\xad\x92\xe8\xeb\xe7)Z$\xa6\xe4\x92\xe472[no\x9f\xc0\xb9\x947O\x97\xe6\xdaH\x1b\x9fE\xff\x05\xa0\xb8M\xe1\xd1+\xb9W2\xd7\xb2[\x05\x83\x83\xde\x98\x89\x01\xed\xf4\xcd\xecz<\x9c]\x9bq[\xb7\xb3\xdf\xe7\x9f\x01H\xeb\xd2\x81Y \xbek\x92 {se=S\xdf{\x18b\x0b\xce\xbe\xb8\xbf\xdd\x89\xde\x80\xcc\x9c5\x9f\x15\xaa\xeb\x05l\x839MB\xaf\xed\x06\xb7\xea\xdc\x18w\x0c\x05tq\xdc\xdb\x81\xb9o\xc1\x14D\x14\xeb\x9d\xed\xcdB\xca\x85\xfc\x04\xfc\xb3\xf5\x06\x05\x04\x1a\x91\xc4\x8c\xc3Ia\xd2Z\xeb\x8e\xdb-_:\x8a\x0b@\xe8\x0f\x98)\xec>\xc4L\xa1+\x1c\x8ao\x1c\x80C\xc1\x00\x8b\xf6\x97\x84\x83\xff\x92@4/\xfe\xae\xe0\xed\x9a\xc0\xa3\x81\xbf\x8df$\x99\xa7.\xc0>\x02\xec\x1d!<\xacw(\xd0\xb2\x8f\x00\xe9/\xa3W\x10\xbb\x87\x1e@|\xc0R\xe4\x0fm\xf3\x88n\xa9U\xf6\x8b\xb7\xa2d\xc6\x03\xcbh\x0f4\x05\x8f\x0b\x1fDW\x8c\xa0r\x8e\xdb+}\xfb\xa7Efy\xf4\xc88)\xcfiz\xe0\xa6\xe9p\x83\xbd\xd1\xaa\xa6;Q?4^\xa4\x0b\xdd!\x87F\x83|0q!\x058\x1a\x8909DdHW@7F\xa0\xc9\xc3\xf3+Q\x0f\xc4\x15\x95\\e\xe2p\xabrD\x9a\xf2\xc0{Y\x8a\xa8$\x91Y1\xc5j7\x8f\x19\x97F\xb2F\x8a\xa4\xad!\x8a\xca!\x8aE\xda\xa8\x16\xe9\xb8\xf8Hi\x12\x9b\xd689\xb4\xce\x89\x83\x8a\x11\xd8\xa2to\xbe\x99\x90\x91n\xcd\x97W{\xe9\xcdn\xad\x8e E\xbf8\xc1\x03!\xea\xc1\xad\xec\xd0\xfcj\x8f\x7f\x82QI\xed\xf3a\xea\x13\x9b\xdce\x03\\\xb0\xe2\xea|r\xedw\xd8\x06\xc7j\xd3\xe7\x1b\x13z{M\xdf}\x18d\xees\xe8\xbd\x1c7\xc5b\x14\xc7#\xd7\xe9\x8f\xce\x12\x95\xda\x89*\xe3F~\x91}\xb6\xb5\xd6o\x15\xd0\xfb,\xf7\x08\x06\x96\x85\x8f\x1e\xd9\x89x\xe9t\x9d\xb7)\xee\xc3\x8d\xaep\x03\x05\x87\xc3\xcd\xc1m\xbc\x9d\xb3\xcdQ{w\xdf0\xc6\x8d1\x81lm\x03\xd0\xf9h\x9b,m\xa7\\4\xfb\xeb\xbc\xd2\xd6\xc1\x01\xb9\"\xf8\x90\xbdJ\x866\xe9J<\xa8\xf8\xafc\xb3\xb6K2\xf0\xe9^\xdb\x0dn\xb5\xd1\xed\xa1\x1e\x91B\xaf\x1a-\xedIA$\xceF$\xfb\x10\xb6{\x04@\xdd\xb8]A\x03\xac`3\xd8Z\xf4\x8d2m>J$\x1d\x8f\x13I\xb7!\xf8\x98\xfcs\xddlKK\x0e\x11t\x82\xfc\xd3\x89'$_\x9d\x07A!\x05pZe2\x92\x8f\x8f\"k\xf3\x8d\x1b\xf9m\xd6C\xa8B\xf4x\xe1\xb5\x1b}\x9d`\x0d/\x86\x86\x8d\xf4\x89^a\xa6\xf7\xc5#>\xba\x1c\x81\xd2\xa0j)W4\xd9gE\x1f\x89E\xfb\x03\xd8\x12\x14\x13\x14M/\xdd\xc5\x18\x91\xf6\xab\x08\xb9\xb7b\xa7\x91\x1bu\xdfF\xd8\x82\x81\xd1\xbd\xb9\x8d\xb0\x05\xb0\xf4\xf15=x\x1b\xa1\x08\xee\xbe\x08`X\x83oW\x1d\x8adT\x1e\x8du7d%%\x0ciCX\xd2\x05i\x89\xd9F\xa0\x18\xb2\xb1\xfdW\x02\xfb\xcb\xfc\x02^\xd3\xb1\xe2\x01\xb6s\xb0\xac\x83\xf9\xb4\\\xf8\x03\x1a]_x\xb5\x14\xe4\xa5/\xdb\xee\x0f\xfa\xda-\xf0\xa6\xc8j\xb3f\xb7T\xa5\x8e\xd6<\xe3\xb4\x95\x82\x8d'\xd0\xc9\xc1a\x90J\x17@\x1e=\"t8\xcc/\x88t\x01\xadn\xec\xd3\x06\x9a\xef\xbe\xfdP\xca\xfc!\x92\xf8:x\xb8\x80\x1ch\x94,H\xc6\x9b\x11\xb9\xff\xc7\xfd\x04\xe7\xfd\x04\xef\xa3\x1d\xba6\x8a\xcb-\xdb\x87\xe2\xfd\x04\xb7\x91\x9a\x0f\x1e\xb6.\x8d,\xaf\x8f\xc5\x07\x95s\xf1\xd4\x11=\xceZ\xf37\xde\x14\xcc}\xce\x0fP\x13\x12\xd5\xaaE\x9dH#\x19*\xe8\x90R\x971\\\xdb\x0d(\xeb\\O\xc9\x7f>^\xba\x82%o\xd51>\xb9$\xf4\x82\xf8m^]\x88\xa1Is\x1f._\xa5]._\x99_\xdc\xc1\xbb\x0b9\xe8\xe1\x858i\xa9\xf9\xe9\xcdM\xd7\xfb\\\x9aN\xe0j*\xda\x0c\xa4\xcd\xd2b\xbe\xd0\xd3\x11\xe1f\xf1\x15\x97\xca\x01rSYzu\xa2\x03K\xc9\x1d\xf5\xa8\x8b\x19DY\x8c\xaaQ\xac\x8eP\x1eV\x96\xf3CMw\xb4\xc1\xfb\x85\xec\xef\xf2an\"\xeem\xe3\xdc6\x86\x1f\x8d\x88\x1d\x8e\xb0r\xfe\xf4\xb9#\xc0J\xd4?\xff\xb4\x92L\x1b\xe2\xae\x08vgbc<\x9d\xba#wD\xec\x16\xa7\x1as\x9d\xbbs\xb1\xd4\xa3\x89\xcd\xf4\xd4\x9diE\xbd\x1b\xe1{7&\x8a\xcb\xd3\x86`!k\x16\x98\x1c\xcf\xdd9\xfc\xc8\xd6\xf1\xc2\x9d#\xa4\xdc\xc4\x1ay\xda\x10Q\x86\x85\xc9\x8e\xa6\xbe\xad\xe93w\xb64[\x99\x1c\x9f7\xe5Ht\x8egg\xee\x1c\x81\x1f\xd9^?k\x18h{\x95\xc4\xac-\xcc\xdd0\xe0\xc5\x8b'&k\xc3\xb0S\x1d\x1e\xc8dk \xd1\"\xa8 \xe4\xf2\xaca\\Y$|qo2}\xd6%0J\xf6Q\x02\xa3\xe4^\x90\x9c\x81Q\xa8 \x8cB10JE\x11\x0c\xd9\xf7\x18\x81\x99}\xebG7\x8a@\x17\x16i\x1d\xea\xb4n\xe9\xb3\xb7\x81t\x91\xd8\xb7E\xcc\xd5\xbc\xc3\x1c\xc6\xabb\xbe9z\xf9J\x8d\xa1\xafXI\xf1\xf8f\xd63\xf1hU\x89\xb9\x0d\xa6\xdb\x1b\x15\xe3\xed\xf6\xc0H\x0bM\x9c\xd6T\xd0\xde\xd2\xd6 \xcc\x11\xce\xac7\x98\x9f-]\xe6:Y\xc5\xe7\xf5kE*[=\x86C\x9fG\xc6KLa\xd4KQ]j\x88\x02\x8ez\x8d\x8e\xac\xf6\x15u\xafI\x9c:4y([y\xd4\xdb\xb1\x7ff\xa2\xef\xc3\xe5\x97\xb3\x01\xe6W\xe8R\xd1o\xb9MP1l\x03b\x8f \x97$\xbe \xa2Mx\xe2s\x01\"\xcbI\xc1g\x08\x04\xe2\xd2\xa0\xfc\xa0@\x19!\x10\xce3\x86$N\xf1\xdeb={)w>\x17\xefG\xa5\xe90\x1b\xfd\x8e\xfe\xdb\x0fNIy\n\xf2!G\xf7\xf40\x98\x97\xc4o\xd6\nF8x\x91q1s\x02\xc3\xc9\xe7\x11\x8e\xd3t0\xc0}\x84{W\xd6\x18\xe8\x187z\xaa\xf5\x97`\xef\xd4z\xbb\x9dM\x12\x16\xad\xfdh\x8b7\x04S\xee\xcd\xf5H/\x1b\x06\x95\xe0d\xe8R\xa0\xf7P\xe4\xe1;L\xe8\x0f\x9aF\xff\xd8\x802\xcdaO\x1ct\xc7\xeap\xfcF\xa7\xdc\xd9\xaf\xc8\xb1bB\x9dd\xf1:\xc2\xa4\xb7\xbe\xf0v\xc4mw\xed\xd1\x94\x91\xe9\xd9\xcc\xfd\xe1\xf3\xf3\xa6\x0f/\x1a>m\x1a\xad\xa7\x9f65\xdf4(\xd3\xf3\xc6\x91o\x82\xebE\xd38>w\x8c\n)\x98\xd29vbk\xb6\xa1Y \xda\xcb5\xf9S\xeap\x94\xd5H\xec\"\xcb.\x80\x1c\x192\x06T\x89\xd7]7G\x83\xc1\xc5@\xd1&'G\x8e\xf4e\nE\x82\xd4\xb6L\xe8\xbb\xe2UJ\xa3\xad\xf4!\xa3Z\x87\x83Q\xce\x82\xca\xf6\xe2\x1f \xe2w\x1e\x8b\xaa2\xc8\xc9;\xa7\x0d\x17E\xe2v[?=\xbc\xd8\xff\x82\xf1\x81\xd1#\xe1h\x8f\xc8\x89p;\x9a\x85\xd3\xcb\xb3\xd2\xf5TSYyV\x9c\x88ck\x98\x1e\xacA\xbb(9\xa0\xc6\xb0\xf4\x19U^>\x9eS\x12\x7f<>\xac\xb9\xb0~\xd4\x1c\xcd\xfb\x9d\xd4\x189\"\x15\xab\xc9\xedE\xce\x14+\x1e\x92iC\xe8\xd9\xe2\xefC4\x1d\xec\x90\xfe\x9d\xe4[\xe1\x1d\xe5kh\xabE O\xdaw\xbd\xc5\xdf{\xf70\xd7Xzi|\n1SG\x87\x81\xd7\x80\xa7\xf1F\x1c\x02\xbc\x03\xd0N\xa3\x11\x0d\xeb\xc1\x13\xb7C0\x1ch\xdfiv\x17\x0f\x87\xe8\x19\x9a\x93\x96;\xdf\xb1\xa2rq\xe3\xfd\x1b$U\xf1\xc7RF\xd8\xa5\xc5\xb59\xb8\x0e\x9c\xa2\xc0<\x7f\xfe\x02\xfdP\x13\xbd\x19;+\xf4\xaa\xb7X\x9c,z\xbf\xfe\xe4\x9f\x1e=\xee\x0f\x9e\x0cG\x93\xd3\xd9\xc5\xe5\xd5\xcb\xeb\xdf\xcc\x97o\xde\xfe\xf9g\xf9\xfe?\x8f{f\xe3\xd2\x1bt\xbboQ6\xb4Z\x92\xabb$\xa9\xca\xe5\x8b.d\xd5\xd2\xd4\x96\xad\x8a\x92\x9bk\xa4\xf3\xf3\x06\xbf\x8b\x07(\xeep\x18\xe3\xc5\xdf:j\xf9\x8d\x8e1\xf1\xb6\xf0\xf9\xf3\x17\n)\xcc]\xb0(\xbf\x88\xd0\xc4\xc8\x8c\x8fg\x85\x10\xc3+r>r2w\xcd?\xb4\xc3J7\xca\xebM\x15\xf8\xf4\xea\xb6B\xbb\x90\x96N+\x14\xa2\xf2 \xb6\xf9\xc7/\n\xf3k]\x1c\xb6\xb1_5\xbf5\x0fuo\xb1\xe8\x99aV\x1b\xc1\x8f\xb3\xea\x8eE\xe4\xd29F\xb3\xa0\xa0c\x89\x1c\xe3*\xc8\xee \xb3\x11\x01\x0f=\xbc\xb4\xa1\xcc\x0c\xb5\xfa\xfcE\x93+\xa1\x8b\x81*\xe8\"w\xa4,rE\xe8\x12\xc3\xd7\xc1_\xb3\x0b\xb0\x84\xac\xdc\xa7)D \x81\x93\xbf\xe6\x8d,\x85sx\xb8\xceH\x0fAIU=\xd4\x85>>\\\xc0\x19+\xa8\xae\xf2\x00\xb6\xe5\xc5\xd7\x85_4\x84\xed!\xa4\xd9i\x85_\x08\x93?'\x8bh9\x04\x93]\xd2k7Q1\x91|\x9a,S\x0e1\xa6\\\xde\xa5\xb5u\xd2uU\xc4E\xca\x93G\xfd\xfd;Z\x1cJ\xb2\xadu>m\x91\xb1\xcf\x1b\xd6N\xdaN\xf2\xdb\xed\xd7R\xf4^\x06w\x91[\xb257\xfe\xcb9\"\xf3u \xce\x94\xbc$g\x18\\\xa0\xda6\xd8.\xcf\xc0)\x96\xd3\xa7\xb9\x82\xee|0\x02\x03\xca\xab\x83\xd7\xdcL\xaef\x9f\xe7~\xee\xed\x8c*\x9c\xd3|\xab\xb9\x00\xd0\x01\xaeC`\x9ec\xdc0\xb8\x99n\xda\xaa\x81\xcc\x15!\xa8\x05\x0d\xf3\xd1\xa74T\x93\xc7O\xb2\x08\xce\xc9\x98\xa4\xa3FF\xacWt:\"\x1c\x0f\x89\x1c@\x9a%\x97\xe2A~\x8c\x8e\xe4u\x0b\x10>.k\xf4v\xdd\xd8\x19TC\xb6\xf6\xd7\xb6\x80\xceH\x9c\xf7\x161\x0f\xda\x0dY[Xj\x96\n\\\xd2T\xc3\xea@\x11\x9b\x01\xd1\xc4\x82b\xef?\x9a\x8d\x17\xbc\xd8P\xa8\xd7$\x1e\x8f\xc9\xcc:\xc1/|\x84\xe7\x18\x1d6]\x82\xa7\xe7&\xa1%\xfa\xc0\x18J\x04wSxjou\xe6}\xd6\xc1\xd4;\"\xd7zF1\x06\xaa\xd6%T\xe6\xd8\xa2K\xbb\x15\nk6 m3\x8c{\xef\xf6\x98\xd6\xb6\xcb*\xb4\xf8@\xc3\x97\x02\xef\xb0\xdd\xd7\xd6qv02P\xa2\x90Y\x01\xe7A\xad\xfco\x963h\xdf\xfd\xff*\x8c\xa1\xb1\xed\x7f\x13|\xe1\xd9\xd3\x0elAg\xfa[p\x85g\x0d\xee0\xdb\x98\xc2\xc9\x95\xae\xe7\xef\x8e-4\xf5&\xe7\n\xad9\x8e`\n\x1a\x0b\x1f\xce\x13t\x05\xff` \x9dX\x82\x1f\xa5\x7fc\x96\xa0Z\xfc\x07K\xa8\xfcZX\xc2\x8b\x06w\xc3\x7f\x0b\x96\xd0\xd8\xf6\xbf \x96\xa0\xdd\x9e\xb5\xb3\x04\x9d\xe9o\xc1\x12tS\xffNXBSor\x96\xd0\x9a\xe3\x08\x96\xf0b\xfa\x81,AW\xf0\x0f\x96\xd0\x89%\x84\x94\xdf\xfc\x8dy\x024\xf9o\x8c)\xd8\xe46\xd3 \xb3f\x89\x0d\x00\xc50\x00\x14\xa8\xfaT\xea\x8b\xe76\xf5\xf33\x9b\x8a\x9e\xe9X\xd53\xdd\xd1Q\xb9\n\xfeR\xeb\x03\x9b\xa1-}-=mH\x0fZY\x98\xe7Z\xc6\xc2u4\x85\x97\x0c\x1a\xc8\xbb\xc8\xc9;\xeaZ\x03\x18\x89j6\x8a\xa1\x95=\x97\xaaU\x0f:\xdc\x16\x81\xd2`5\x0f\xf7\x9a\xfa\xa8\x10\x1e\xeb\xab\xa7\xcf\xc85\x8c\x02\xf4x\xaa\xf0\xe3i!\x9a\x1f\xb6\xee\x80\x91\x16U\x10H%bt;o\xda\xd1\xd5D\x85\x1c\x91u\xe1\x0c9>G\xa7\xb0\x1e\xc0\xc7\xfb\xda[\xad\xad\x80\xf7\xe3\xdc\x15\xf3\xc9t\xa0\xd0\xbc\xbe|<\x1a\xc1J\x9d\x91\xcc1!4\xc25\xe5t\x07\xbff\x81\x1f\xa63\xe27\x10\x97\x07\xd8Z\xe4RO\xf5\xdap+\xe2l\x9a\x0f\xce\x12\x17Nm\x06uF\xa9C*&\xb0\x01\xc0\xb1O>@\\\xfb\xbb\xdcW>z\x84\xfd\xd3s\xa4\xbax]7\xb7\xb0\x01\x05\x90\xad\xa3C\xea\xd3\xfe\x1b9\x7f\xb3X,\x07\xfd\xc5b\xb1\x18\x00\x83>9\xcc\xf9U\xb6(?K\xd5\xb1\xf8\x80\xcc\x18s\x08\xe3\xdc\xd4\xde\x07}p\xfc\xe1\xc0O\x9du\xe0\x87+2_\x0e\xcc\xee\xac\xfe\xbd\xe0V\xd4E\x0e\xe2\xc3\xe8Xv\x0cR\xa7\xcb\xeb\x87\x84\x8d\xac\xac\x1b\xdc=\xd6\x1c\xa1\xba\x17S\xbd\x93s\x7f\xa9\x06\xaf\xde\x03\xa8p\x96W\x9d&\xb8\x9d\xa9H\xfe\x95%ZXCqm\x07\x90\xd9\x08x\x1fc1\x1d\xbbhJa/\x9b\x17M\xcbU\x1d\xc5\xba\x9e\x92\x97\x07\x8c\\N\x1c\xf8ZM\x83 \xd6\xad\xb54EGo\xb9\x16\xd4\xa60\xc8~9K#k\xa7\x93\xe5v:\xf4\x82\xf0\xe3\xa3\xa3\xf3\xc3\x81\xd7\xa6\x0d\x02}\x87\xa2M\x81\xd5y\xf7\xc0\xeahG\x04\xfd\xd4\xe4\x8e\xab\xe1B\xd7\x8a}\xae\x96cT\x11k2\xe3\x05\x10\x05#-\x12\xe1\x1c5\xc65\x8f\x96\xcd\xe4\xaf\x1bMk\xaf\xfc\x12D9\xad\xaah%|\x0e\x82\x11\xbb \x86\x8e\x98\x1e\xb9\xb4\x08Y$f\xe4\xacN8\xda`\x84\xa8\xcd3\xe2\x82\xb1\x94\xb1\x99~\xcf\xe3\xe5\x04\xdan\xec\x08~\xd6\xd2\xc7\x87R\xf2\xd8\xc1\x80\xb3\xd57\x0f\xa0\xf1\x05\"\xcaK\x04\x94~\xc4\xc0\xe4\x05Y\xe4\xecY\xd5u\x99\xd1\x99|\xe6\xd0\x99\x14\xe2\x8a\x9e\x8d?\x9f\x9c\x80\xf2\xf4\xc9pqzum\x15\xa6\xc3\xdf\xe49\x96\xfd\xebY\xfe6^\xfe|6z1}_\xf8>\xb8\xee_\xcf\x16\x93\xa3J\x0c\x9e\x0c^\x9e\xd6\xf56\x05\xd8&\x8b\xf1\xf2\xe7\xe9\xe8\xfc\xf9\xfb\xc1\xac?\x7fs\xf9rqwv6^\xdc\x9d\x9f-U\xd9\x87\xf3\x91\x92n\xa7U\xc2z\xd1\xa8}\xd0\xd4\xa3_\xa5\x16\x9b\xa2\x13\xaa\x97\xbd\x82(\x04\xaa\x90H\xab\x0f)\xb8\xab?\xe9s\x9b9\xab\xc5\xa1,\x94U\xbb\xa1l~\xb6\xd4\x8dL\xf5\xd5~\x0f\xac\x08\x02\xb5\xe7:\xb1\x02C\xd1/W?(\x8ba\x1dd\xef\xd6\xfd\xc3\xc1]Be\x1d\x1c^\x96\x02|\xe69(\x8e\xd6[\xba\xc2S\xb2\xaa\xe3\xc3\xa3[\xed\xb2\xcb8\xb0\xb2\x87zF\xf2[\x98\x03E\xedN04i\x94\x874\xb5\x13\x986M`/\xa4~ b \x87m\x93\xe9\xfdc2K\xbf\x8f:\x99iu2?\x0e\x91.\xd2\xa6y\xcf\x8b1N\xe7:\xf6\xeb\x8e\xe8(\xa5\xfa\x0fD\xe6\xa4\xab\x18CwR\x0f\x0b\x99?>\x04\xd6\xf48\xfe\x05\xb7u\xf0\x17#\x94\xfa\x18\xffs\x0d>\x1d\xads\xbb\x8d\x80\xb2[\x16\xc3\x1f\xfdo\xb2\xd3\xd1E\x9f\x9ec\x04R\x81\xd9\xd4_(\xee\xd3;\xf8\xa3\x9b\xf6C\xfcW\xbfE\x1b\xa8\xc7O\xf0\x95\xfb\xa9\xf9;Y1f\x13'w\x89W|\xces\x05\xb7\xef\xd4s\xb0\xc6\nq\x19\xc0\x13\xf6-Lyb\xfeB\xa9P\xfc\x84 Y\xa2V\x85z\x8c\xd8-|\x8a6\xf8\xc7\xc7\x7f!\x16i\x14a\x7f\xe2\x84\xfe\x94\xb1 \xf6n`+\xa4\x92\x92\xd8DD\x85b\\\xa4\xf0\x9e2\xbe\xf7=\x86\x8fij\xe2\xa1\x9a\x81I}\xb6\xc7\x8f\xbe~G\xb8\xd2\x10\xffD!&\xc74\xb1C`_ \x0b\xfa\x84\xec p\xca\xa9\xfeD\x188V\xe8\x19\x12;?\x0dY\x9a\x82\x06\x8a\xf4D\xf4\xf4\xfc\xd33x\xc2\x16\x05\xccr\xc6\x01\xae=\x0bC\xe8/\x0e\xc1-\x86t\xbd\xf3\x10j\xf5w\x9c\xa5L#\xca]\x18\xf0\xc4\xb3`\x15^\xb1T\x88\xd3\xf8\xee\xe9\xe7\x93\xe7g<\x7fDd\\\xfbYx'8b\xe8&\xc1?\xf8 \xb1\x82j$\x16\x82z\xbb\x90E\xf8v\xab\xfe]\xb1tG1\xf4\xec\xca\x17^\xeccX\xde8\x80\xb9\xf6h\xa0g\xdd\xdb\xf1\x18\x83\xda\xe2\xd3\x98\xdd \x16\xa566o8f{\x16\x89\x15\xf7\x05\x1bS!X\xb4f\x98\x1d \x0c<\xee\x01\xa8u\x10\xd1q\x12\xd0\xfb\xd4\x8f\xb6\xda\xbf\xa3IR\xb9\xa9\x1f!\xea\xaf\x05T\xbe\xde\xaf\xd4\x1f\xb6>\xbfQ\x7f7\xd4c\xc2GX6\xcc\x84\xf9\x8d\xb6:\x84\xaf\x9f\x02zma*\xb7\xbe\xc0?\xef\xc28\xe1\xb1 \xc0\xbb\x154\x80\xbav\x1e\xae\x04=+~\x82\x7f\xb8^\x13\xde\x0b\xfd\x17\x97\x85@L\xfa\x91BK?\xe2\xdb\x0d\xbbO(\x16\x08h*60\xe0j\xd5\xe0\xa2\xa0[\x8dD\xa1M\xe17:%G\xa5\x10\xeb\n\xd3\xf1\x8e\x05zYE8wa\x16\xea8\xbf\xe1\x1e\xa0\x03\x19[=\xc4\x88; \x0dB\xfc\x9bPN\xdf\xbd\x03\xa4K\x02*L4\xe3\x84\xc7w\x10\x1f8I\xef\x01\xce\x9f2\xc6!\xc1,0\x96\xc6\x19\xc7\x95\xc5\x11iyz\x1fA^.\xf4\xb2a^\x1c\xad\x03\x7f\x83KL\xaf\x88t\x8bk\xf0\xe6>\xc1\xf4\x10\xa6*\x8d\x835\xc5\xc0\xc5I,\xfc\x0d4\x96\xe2\xc4\xa4\x82Q\x00+\xc5\xee\xa8\xd74\x01\xc7)\xb0\xc2\xa2-\xc0\x94\xad\xa1\x81,\xe2\x8c\xc2r\xcc\xc4\xf9\xd9\x19DaVx\xc6}D\xd0\xbd\xcfn\xc79\xf4\xb7l\xe5a\xf6[Aq\xf5\xdd{\xfe\xed= \xc3\xdd\xc6GD\xbf\xe3\xf0\xe9>L\xb7\xbc\xb7|8\xff( \xf9\x9f\x0e&\xbf\x7f\xfd\xea\xdb\xb7\xaf\xbf\xf8\xe7\xb7\xdf\x7f\xf5p\x01\xb8\xa2Eq+\x17+A\xf8I~CE+^\xc8Ic0}\n\xc7\x1aE3\x05\x14\x97\x9f\xea;\x8dN\x97\x0e\x06\x17\xa7\x15\x8d\\\x8a\xe5@u\x04\x98\xac3?\x9d\xbeW\x99\x1f\xce*\x8b\x97v\x1c\x04\xab\xc0\x0f\xeb\xfa\xf8\xa7\x9f\xb9\xb9\xa3w(Z8\xde8\xdd\xb8/\xa9<}\xee\xd6Iy\x9a}\xbai\xa6\xbf1f(9\x93\xf1\x0c'+\x1cI\xa0rA\xf1\xe7\xde\x1dF\xaa \xe6\xd3\xa5b %\xdd\x14\xb9&\xa0\xa1\xf8&\x12}\x95\xc1\xe85\x06#2}\x01\x01\xd6\x8b_Gd\x8aa\xb6\n\x97\x81\xfc~\xa4j\xa1}\xa0\xcc\xb4\xff\xe2\xf9\xf3\xa7OK;\xf2\xa0\xcc\xb6\xea\xc4\x1am6\xc0p\xa8\xb1k)2\xe9X\xf1\x01\x05J\xb5\xa7%\x98\xf8\\eY\xb6\x00\xe1\x14\x95\\\x0e\xec\x1e\xfd\xc2\xfe\xeb\xca\xb3\xac\x05\xb5\x99c\xf2\x95\xe0\xe1\xf6[v\xa7>\xfd1k\x88\xca\x01\x07*iC\xc4\x0e\x1am\xbf\xe3l\xe3\xdf\xcd\xd4\x8e$\xdaft\xcb\xc6.\xed\x8b\x1f\xdd\xf8\x9b\xfb\xc6\xf8*7\xaf)\xdf21sJ\x03\xe2>\x89!\xa8\x08\xe3\xee\n\x809\xa63\xd2\xfb\xeb_\xfe\xcf\xbf\xfe\xe5\xff\xfa\xeb_\xfe\x8f\xbf\xfe\xe5\xbf\xb8\xd4]\xfev\x17`\xfc\x91(\x0b\x1cJ\xa8\xfc\x8clF\xce\xab\xa7\x1c\xa5W/\x0e\x938b\x91p\x8e\xb5\x17s\xe6JW?\x9e\x05\x10\x8a\xa5\x07\x9e\xe4z\xa3<\xea\x8b\xda\x1c\x19+\x19|\x03\xc9E1\"x\xd7\x83\x88{\x1f\xca\x05v\xbb^\x8e\xaeV\xfc\\=\xd8\xa3\x0eA\xfd\xa0\xe7\x08\x83\xe8\x98mto\xd7\x05th\xbe72\xce\xf7\xd4\x06\xd9@`\x1aV\xcf;F\xd7\xc8 {;T2\x890\xb0}\x0f\n\x9fu\x90\xbeB\xd0\xa6\x91\x8e\xa5\xdb\x0dv\x1c\xc7\x83\xc0\x17\x02w\x94b\xa7\xe8\x00)\xc5\x00&y\\\x8e<\x14K5FH!\xc2\x87\x0dHR\x08\xef\x82\xbaP\x07\xfc\xbfr\xbf\xfd\x83,\x14?\xfe\xbb$\x0b-\xcb\xae\x0d\xab\xff\xce0\xc6q\x1d\xbe\x801\x8e\xaf\xff\xc0\x18\xf8=\x04cj\xe9\xe4(F\x82\x0c\xa1\x13\x0d\xfd8\xf4\xffCh~'0?\x94\xd4\x1f\xa2\xf1\xff\n4\x1d\xb6]\xf9\xd2\xe4\xc5}IU\x98w\xaffS\x0b\x83#&jf\x1e\xfez<\x8e\xeeQ?\xbf^s\x86\x07\x04\x943\xcc\xc5\x85\xef\xa1\xde\x97\xa6>N&\xcd\xd6>h=A\xc9\xbaZ\xfb\xf8\x07\x93|\x18\x99\x95\x1d\xda\x12:\xac\xe25\x8c&\xb6\xbc\xca\x84\xd0z{\x1a\xed\xf1D\xcb\xa3\x890\xca|\x16 T\xa6{~\x19\x9b\xbc8\xd0\x7f\xb6<\xce\xf0\xc4+W\xef\xe7\xa7]\x82\x1a\x1cZ\xe39\x18\xf3bNE\x8cZ}d\xe9k\xa6$ d\xf2\x1b\xd4\xf3\xfb\xf8\xdd\xc7\xc32\xcc\x05\xb5\xb0\x80\x99S\x0b\x06\x03\xb6\xf1Y\xb0N\x99\x8e\x11\xb5-\x00\xbf\xf1\xb7\x19\xd72\x01\x96P\xb2\x81>\x1b\xd0\n\xf1\xdd\x14\xfe\x05yl\x87\x87k\xa0X\xde=\x87\x7fA\xe9\xaf\xd6\x83\xf9\xab\x0f\xe2l\x9f\xf3\xf5\xa3\xfe\xc2,\xf8!\x0c\xbf\x1f%x.\x88a\xdbz7+\xa8\x04\xacw\xe0\x81mY\x84IP,\xa4x\xde\x12\x9aC6\x08\xe5\xa6\xfe\xfe\x94\xe1\xf1I\xc8\xa2\xcc\xfc\xf5\x05\xf6>d\xbaC\x11\x9e+F1\xce+\xceN\x9c\x08\x0bil\xc7%\xce\x84\x06\xcd\x9c\xad\xe1\x9fxk0\xef'\xf5\x0f\x9e\xe9q\xc8\xc8\xb3\x15\n\xb6\xf0\x0f\xb5\xe7\x00\xa6\xca\x94\x05\xfa<%\xdd\xd1u\x0c\xc7IiH\x03\x80\"\xd7\xc9\xa7 \xf5\x10\xdc4\xa1XPp\xff\x86\xe9\xa7\x18\x89N*\xee\x11\xdb1\x08]/\xcd\xc2\x90\xe2)\x05\x06\x9d\xd3R\xa7z0\xd8,`$\x05\x0b\x93@\x1f8*\"`V\x90P\x13\x0f\x0f(\xb4\x9a\x195gG\x82\xe3\xbf\x14)\xa0\x80\xbc0\xd6\x19\xf4`\x8f\xc7<{\x7f\x8d\x07\xb3\xb7+\xdes\x04\x8a\x03\xa3\xb0^\xba\x87^\xe0\xd2\x0d\xc46\xb8GQ\xd9<\xafQ.5\xaff&i\xe4\x87T0/\x0epm\xe8\xf706c\xac\x13\x04\xa7Qj\xd0\xd7\x92\x81\xc2\xea\xf5\xb9&\x16^\xe0' \xc5.\xaf\xd9F\x0b\xd1)\x9c\xe5\xb0 \xf0\x93\x14\x17\x87\x1f\xd8E\x81\xcb\x04\xcf\xcb\x0c\xdc\xf0`\x84\xe9\x1b\x86G\x9a\xda\xf6\x1e\xe8\xaf\xfdK\xf9\x96\xd3\xb5\xaf\x97'\x9cnq|J\x11\x97\x99\xa0\x862\x84\x06\xb2\xc2_\xa1+O\xe2\xe0~\x1b\xdbG\xcb5\xe9\xda\xa7A\xb1 n\x90N\xe01q\x8e9\x10\x01\n\x9e\xee\xc3U\xac\x0fq\xef\x84\xf9k\x1a\x05\xabzx\xd0\x1d\x14\x061\xed\\\xef}\x06\xe8\xbc\x87\xae;f=\x82Y\xdf\xb0\xdf\x06z=o\xd8\x97j\x12_Q\xc1\xfd;\x93\xa0\xc5\x88\xd70{z\xb819\xd5\x94U\xbdF\xfb8\xd8\xb3b\xc9\xdf\xf9\x9bM\x96\xb2o\x958\xa3\x99\xb2JL\xed\xde\xf3\x15\xd2\x0bH\x144\x12\x90\x13S\xbe\x0e\xe2XC\xf4u\x16y_\xe4\x8f\xbf\xcd\x1f\xff9\x7f\xfc\x1e\x1f\xff\x99fi\xea\xd3\xe8\xb7A\xa6\xe1|\xc5\xf8\x96\x15\x1e\xff`E\x8aW1Ovq\x10o\xef\xf1\xfd\x8f\x9b\x8d\xa1\xc5\xa87,\x80\xf3C\xc2\xbc,\xa0\xbc\xdc\x97\x1f\x92\xb8\x98\xe9\xb5\xb1\x84`\xaf3\xbe\xca\x02%\xb4\xb8F\x1d\"r\xf4B=\x8f!\x8b\xb4e\x89z\xe6\x1c\x97P\x08\"\x0f\x9a(l8\x05\xc4\x0f-^\xe3\xe9f\x08\x04\x99\xad\x91\x04\x84a\x16\xf8h\xea\x81\xa7\xb0H\x92\xd1\xd8!\xdektN\xe8z\xad\xabMv4\x121\x92b\xae\x89L\xc8\x91\x00\xea\x83\xdc\x04\xa8\x1e&\xfc\x84\xe44\xbc\xb7\x98\x1aj\"\x17j\xd2\xa6\xde\xcd\xa3%s!\x92\xb7\xd0\xa0p\xa8\xa1\xcd\"\xcd\x90\xf0 \x00t\x8cU\x0cc\xf5k\x14\x8b\x1c\xd2\x1a\n$\x9e\xc7\xb4m\x80%\xeb4\xf0\xb7\xfa\x01\xbfd\"V\x12q\xc0\xb4,A\xbd\x1b\xc5`\x10\xefW[K\xbcV1\xd7\x90y,\x08\xd4x\xe9\xf9V\xafj<\xcc\xeb\x8ey78\x94V\xc0\x08(2!/`Hvm\xad^\x8cB\x82\xfa\xab\x97\xa9\x17\xc7|\x8d\x89\x9a:A3\x8a!\x8cW4e\x86g\xd2\xd436>\xe6L\xcf \x84M00\xd3w~\x98!`\xaa\x8a\x8d\x9a \x16y\xf7&A\xd59Nw\xfe\x06\xea[1\xbd\xd2V>\n\x1e(!\x16\x96/ZB\xa9\xbfc\xc3o\xe1E\xed\xffz\x95u\x1d\xf3\xb1Z <\x89\x03j7\x1f\xf5\xe41\n+i\xfe9\xe1\xb11\x9e\xc3\x04\xce\x14)4\xf4\x05f\x07\xbb\x80\x8b\x1d\x12Pf\\#k\xf5\xe2\x08\x18'&\xf1\\\xa8]\x03\x97\xd5Y\xf7~\xaa\xf7,\xc8\x14\xd9z\xcbB\xcd\x06Y\xc0\xf6\x16j#\x04\xf8(\xfc\xaa\xbf\xe3XQ<\\\xf9\xf0nF\xa0 z)V=\xb6#\x82\xaf\xc5bq$\xc6\x1b\x1a\xfaA\xfejP\xdb\xbe\x8c\xe9\xfa\xc7,\x15y\x9a\xe0L\x8bA\xfa]c1\xbc\xed)\xf7i\x94\xe7\xbe\xb5h\xb6A\xd9\x03Z\xda\xc2\x06i\x0b\x1b$`\x9dc\x83?E\xb9\xd0\x08eY\xe4#\xe34 %i\xb5@8u9M\x1a\x950Y\x9e8D-?\x82va\x99\xdf\x00 7\x98\x00;\xb5\x1b\xd8\xa9)\xb1L\x17\xbaa\xf7\x89\x929R\xfd\x92&\x10X]\xbf)n\x00\xcf\x96\xd4\x02%\xcd\xc7,`\x8a\xd6\x8d\x0b\xecI\xd5\xcd\x82\xd0\x8ac\xf8\xae:\x99S\xe1@K3\xf9\xe4\x05\xb16P\x1c\xb3\x84\xef\xbc\x1d\x8d\"\x16\xa0\x00\x84=\xbdw\xa4Asw\xd0\x8f;\xe8\x07\xca\x1f*7\xfc\x03_\xee\xe1\x0b\x18|\xbf\x8b\xe3\x90Fk%09d\x94\xac \xa3\xf4P8\x81U\xaa\x97\xb4\x15{Vl\xcf\x02-k\xdbM\x9a\x17\x07Y\x18\xa56\x13\xbe[r\xad?kQm\xcd\xa28\xb4Y\xd7,\xd1:\x0d+\xcb\xe7l\x1a\x1es>\x07\xbbG\xf5\xc05ykbA\x81\xc2\x1f-q\x17H{\xc4\xc4\xce\xf7n\"\xad\x17\x0b\xecV.\xb0\xfaT\xb5\x05-\xef\x83T\x8a]g\xea\xc50j\xf5\\\xe0\xba!\xbd\xb3_\xfc\xc8>\xc6{\xb55\x81U\x03\x8dFqNL\xa3,\x1f\x07#\xad\xf3\xf8\xd6\xa6\xf1\xf8\xd6\x8e!\n\xcc\x06w\n\xe23\xb7\xbd\xe0\xb6\x17\xb8\xe7\x05\x03\xc5\xfc\xb5\x00\x95\xde\x13\xfb\xef\x98\xde[\xf8Z\x8f\x07\xe8e\xb5\x80 \xb5L\xc2\xbeh\xe2\x03\xa2\x88V\xe2\xe9 \xffV\x96L\xb3\xa4\x9ar\x1f\x86Lp\x1f\xe4\xf1}N}\x0e\x8b\xcex\x83\xe3.\xf0\xa3\x9b\x99\x99\xe3\xbb0\x98i\xebzH\xb7\xe2\xba\xfa`G\x03\xaa\x9cA\x8e\xde\xb2`?I\x8a&\x8f\x81\xd3\n\x89T#7\x9b\xab\x9d\x17$\x1a\x8f/\x06\xa8\xe8\x8c\xb6=ru\x05\xa6\xa6\xf1\x86\x88\xb9\xb9}:\x87[\x98\xeaO\xe5f\xd9\x88\xb0\xb9J^6x\xdf2\xa6\x9b\x95\x83\x0d7\xe4^\xbb-\xae\xebp\x93h\xf5\x16^\xa6\xad\xb7\xaf\xbdc\xfb\x11a\x03\xf2\xc7\xd5\x8f\xcc\x13\x85\xf0\xf2;\x9a\xfe\xf16\xfa\x8e+\xd1A\xdcO<\x1a\xc0\xe0i\xcf\xd1\xba\xd7l\x1e-\x1d\x9eT\x8c\xc9N\xc3\x91\x0d\xd1\x80o\xc0\xbb\xdc\xcf\x8b\x9f\xe7\x8bt\xf1\xc3\xf2\x89\xd4\x7f\x17\xef\x17\xefO\xb7a\xbdG\x89*p\xf9O\x95\xec\xff\xf4\xd2\x99y\x0d\xd6jk*\xe8x\xbe\x18/n'\x8b\xec\xec\xec\xb7\x9f\x8e\x17\xd9\xd7_\x7f\xfd\xf5\xf2\xd4q\xf2\x08%\xd4\x12\xc7\x12\xcb\xe1'\x8e\\{\xc8\xd5\xbf\x9e\xe1\xff\x1b\xb9\x13\x03\x91\xa4\xd7\x12o\xd6H\xc1\x02\x89\xd7-\xa4\xe7\xaf\xe5]\x98$\x83\x99\x9c\xbf\xa1\xe3wK9\xa7\xe3w\xc3\xc9b\xbc\x1c\xf6\xafg\x90\xa6\xdefK\xf9\xc9`P5\xb7#\xda\xb3\x154\xb6\xb8\x1d\xe2\"\x93`\x829se\xde\xaa\xccs\xd5\xcd\xb3\xb3\xb1\xfas~\xa6\xfe\xfd\xe2l\x91M_|\xa6\xfe\xfd\xec\xec\xabEv\x8e\x9f\xcf\xcf\xce?W\xff>\xdf,\xb2\xa7ggg\xcb\xd3m\xbd\xca{rEz\x06 \x8b\xf8\xff\x03hf\x15.\x18%m\xed\xe3D\xc9\x0f\x8a\x86\x90\xeb\x03\x16\xe5\xa4\x803XC\xdd\xa9\xee{2\xeb^\x0b\x03\xc0\xda\xe1f\x13\x10\xd1x\xa6\x18,\x18\xe1\x15\xbe\x81M\xa1\xee\x86]\x13\xe4:\xef\xec\xac\x05\xd2&\xea\xb3r\xc3\xedoH\xff\x0b%\xb5M\xfc\x14\xfe\xf6Y\xa3\x85\xa1%Sj\xd1\x9f\xe1=z]\xc6\x98\xb0_\x10\x01\x11\xe7\x0d \x13\xc3\xe1\x80Ds\x81\xebU,\xeb\xcb\x95\x14\xdc\xf5\xd5{\xd3\xb4\xba\x11\xe4\x0d\x8f\xc3vG\x80\n\xda\xb7m\x07\xae\x85:{J\x00\xd9\xf8\x11[\x17\xe7\xec\xd6\x8f\xd6\xf1-\xb9\x06{\x002\xd3\xef\xe5&\x9d6\x83v\xe4o\x9d\x8d*\xc8\xbe\"W\x84\xf2m\x06\x86`&\x92\xfcK\x8c\x0d_\xf0B`\xb3\xcc\xcf\x96\xe4\xba\xfc:#o\x9b\x02\x9a\xde\x95\x0c`\x9b&\x95\xe4\x10\xdfV\xc7\xd2\xfc\xde\xbb\xbd5\xdcM\xf6\x8c\xa7\xaa\x8bW\xa47\x9d\x9cM\xd4\xae\xfan\xc2Y\x18\xef\xd9Z\xc7\xbd>\xf9\n\x9ck|5Y\xc7\x1e\x80\xad^?\x87~\xe5i\x93(^\xb3\xd7\xf7 \xb3\xb6\x9bw\x13?\xfd!K\x92\x98\x0b\xa8\xead:\"wu0\xd4(\xfe@\x8aU\xb9\xc7\xe2\xcb\x06\xbf~\xeaw\xd3\xf2\xed\x8b\x0eu\xff\x11\xf2\xfcN\xe7\xf9\x9a\xd3ms\xde\xef \xef\xef_\xbf\xfa\xf6\xb5>p\xfc\nO\xa5\xdd\xd9_C\xf6?\xd4,\xad\xcd\xef\x95\xfd\xfe5\xe8\x83\xdc\xb9\xbe\xc1\\4dk\x95\xf5\x15M\xdc\xf9~\xb4\xfc\x1a(\xd27\xe4\xbaRLM\xddW\x93W\xf1;H\xfcB\x08\xae\x12g\xe4\x1bw}\x7f\x80v_\xb3\xbb\x86\xde}\x0f\xdf\xbfD\x8b|w\x96\xdf\xe1\xd8\xfe\xf1\xd5wp[\xda\x9d\xe9[\xc8\xf4?\xbf\xfa\xf6\xf7B$\xdf\xb3\x9f2\x966T\xf7\xa7r\x0f\xbf\x85\x1e\x96\x0b\x92\x19\xf9\xd6]\xf8'h\x86Ej\xff\xf6\xa7\xef\x1b\xfa\xfcu\xb9\x85\x9f\xa0\x05[\x86\xcc\xc8O\xee\xb5\xe4\xe4\x17\xdf5-Z\x85\xf6\xef\x14\xf5\xfd\xff\xd9\xfb\xda\xae\xb8m%\xe0\xef\xf7W\x0c~zR\xfb\xe05\x90\xa4\xb7\xed\x06\xc2!\xb0ii\x03\xe4\x02i\xdaK\xf3p\xcc\xaev\xd7\xc1k\xed\xe3\x17^z\xcb\x7f\x7f\x8eF\x92-\xdb\x92\xec%iz?\\\x7fHXk$K\xa3\x91\xe6E\xa3\x99`\x9c\x92\x8a\x88\xdc\xea\x18\xdb\x10\xc4\xff\x8f@\x98D\xd8\x16S\xfe\x08\xe8mBRI\xc1(c1\xc27\x94\xdb.\xd5\xc8\x87u\xf0\x15\xeb\xa0\x1eK\xbf\xc0\x0e\xbc\n\xa2\xc5\x92\xf7\x1b\x95\x14=\xe4\x8f\x08\xc9G\xc9\xa8\xf0P\xb0u=\xf4{\x84\x9e\x91\\ ${u\x7f\x1e\xce\x18\xb5\xea\xe1\x7fRZ\xef\xb7\x80\x7f\x83\x1d8c=\xa7in^\x97?\xa3T\xdc\x9e\x82\xe6\xae\xf6Kc\xa7\xffE\xf4\x85m\x10\xeat\xf0\xfdr\xaf\xdc\x88\x8e\xe8Ds\xf7\x8d!\xfd\x07\x8c\x8c\xa6\xed\xd4W\xb0\x03\x86\x95\xffo\xd8\x81\x89\xbe\xe8W\xd8\x81\xb9\xbe\xe8_\x18wM[D\x08\xec\x80F\xa4cON0(\xa0\xb6,aez\xcf;@F\x05;\x10\xbb\xffy\xf0\xe1\xe2\x03\xa3\xceq\x98\xbbW\x188\xeb\xca\xcd\xf1\xdf\x04\xffM\xf1_\xeay\x06\xdeH\xed\xdf\x89\xf4\xdf\x89\xb0\xd5\x10\xff-\xf0\xdf\xcc\xf8\x85\xd0\xfe\x85\xc2^\x9c\x11Cb\"\xc0[\x81\x96\xc21\xb1\xb0\xb3\xa9\xadpi+\x9c\xd8\n\xe7\xb6\xc2\x1b[\xe1\xc2V8\xb3\x15\xde\xdb\n\xafl\x18\xba\xb4\x15\xde\x12\x8bB;R\xc8\xa2r\xa0\x91.A\xd2\xa3\xa0\x8a\xf7PZ\x93T\xef\"\xe1\xe4\xc3\xbdD>\x98d7\xed\x97J\xcf\x12\xe1(V\xb9Gq\xa7\x1aSkg\xb5\xd6\xb8a\xb99}uh\xf8\x98R\xc6*\xb1\x97\x85ZI\xfb)\xa5LVB\xfaw\xde\x9d\x8d.\xdf\x9e\x9e\xbc>|3\x92\x9fz\xf2\x04\xa6\x81\xfa\xde\x17\x9b\x14\x0f\x82'\xfa}\xb9wz\xb8\x87\x0d\xfab\x9b\xaa\x17\x1f\xec\x9d\xcbb\xdc\xa8\xe4\xfbw\xc7?\x1f\x9f\xbc?f\x8d\x9f\x9f\xec\x9f\xbc9C\xa5a\xcb\xe7;\xd648\xdb{=\xba|}rz\xf9\xd3\xbf\xde\x8dN\x7f\x93\xa5\xcbF\xe9\xf9\xe8\xe8\xed\x9b\xbd\xf3QY}\xc2\x01\xde\xffx\xf2ftyp\xb2\xff\xeeht|.\x0b\x17\xbc\xf0tt\xfe\xee\xf4\xf8\xf2\xe0\xe4H\x16\xcc\x9a\x05\x97\xafO\xf7~P\xab\xde\xb7 \x0e\x8f\xde\x9e\x9c\x96\xe57\xbc\xfc\xf5\xc9\xe9\xfe\xe8\xf2\xd5\xc9A\xd9\xe3\xab\x1aR\xce\xf6\x8e\x0f\xcf\x0f\xff\xcd\xbav\xe4\x8b\x8dI\x96\xfd<\x1a\xbd\xbd\xdc?9>\x1f\x1d\x9f\xfb\x9ciV\xc4\xf1\xee\xf4\xf0\xf2t\xf4\xc3\xe8\xd7\xb7\xac\xe1\x9c *0\x0c\x11\x91i\xd5f\xfc\x05\xdfa7=\x9cZ\x0c\xecI\xb4\xbc\x0dy%\xa7OT\xdb\xf8Z\xb8%Uh\x80\xd8M\x88\x0f\x8c\xd7\xc6.%>D<\xb3\x97\x84\xcbnf\nX^\x82\x85\xe5_Y\xab\x02\xd7Z2\xa5^\xd2]\x8f\xed\xb3Gj\x97\xd2\x12\xb2P\xebx\xb8\x9a\x0e\xf8\xa2(\x87\xbe\xb3\xc3\xa4\x88\x12\x11c7!\x1e\xd6b-U\xf0UmF\xad\x08Oy\xed\x88\x94\xbf`\xecRQ\x9b\x12\x15\xbe\xaa\xcd&\n\xc9S6\x13\xbbgD[\xe8!\x01\xf0\x8e\x95.Wr\xee\xb8\x85\x94\x1b\x96RB\xfe \xb8*\xab\xb7\xc2\x82\xca\xcb\xdc\xa9\xe7\xf3\xadu\xaa\xdd\xfd\x0c\xdc\xed\x84\xf46\x18\x94J\xbe)&\x82\xfa\x08\xbf\xeb\xa1\xc6Z%\x9f\x07K\xce\xb1<\xbd\xb7\xf4\x04dv\x08\x92\xa0<.:\xb6?\x8f\xe2\x89\xc9\x9c\x01h\xd1\x1b\x87\xf9x\x8ey8\xbaZ\xa7ENR&\x92c\xe8rs\x93\xab \xfb-\xe9\xba\x9e\xac>\xdd8XiF\xd8S\xfa\xf0\x0c!g\x1a\xd3\x9e\xfc\xcd\xb0\xc8$\xea\xce\x16\xa6)]\x0c\x1bv\xf6\xe6\xf3\xd0c\x06\xac\x94\x06\x9f86\xb3p\xa1>\x9f:\x14\xf3\xc4\x89\xae\x97\xd85\x9a\xd8\xf4\x9d<\xef\xbf&\xa5a\x96K2\xf61\xdbNf\xe4\x13M\xc1\xbd\xe1\x1b\x12\xca\x04\xdb|$/\xb77\xc4\x1f\x0e\xac#7\xb8\xee\x9a\xbfn\xeae\x0f\xfb\xc8k\xdb\x92\x85&\xd1\x98\xd1\x0ej\xb4\x03r\x0b\xef\xcc\xc3dO\x1a\xa4$[\xd2$C\x1b$\x1b\xacT\xb4\x1d\x1f\xd2\x80.I\xe2:?\x8c\xce\x1dq/e\xc86\xe7\x0d\xc6\x18_\x8c\xe7a\x9a\x91|\xa7\xc8\xa7\x83\xef|D\x89/\xd2\x9a\x06\x19I&.#@\x8fGE\xa9>\xf3\x08Jb\xd3\xb1\xef\xf5\xc0%\xfb\x92\xcb\x06}\xe0\xf1\x18\x83\xafS\xba8\xc33D\xb6\xcf8e\xdf\x9d\x9ek\xd3\xdc\xa7\xf2v\xfc\x93'\x90\x97\xc6 !\xa8\xe3\x95y\x9e^\x94uIg\xdap\x1d\xc7\xf3\x82+:\xb9\xf7L[x\xa2\x16L\xa34\x93\xcdc1\x13\xc4k\xdb3\xa3\xc7\xf7\xfc\xbc0G\xe9oW\\\xb1\x81\xa1\xb8\xbf\xe4]l\xb6\xefw\x81\xde\xc8]7\xd70 \xd8v\x8c\x00\xca-\xads\xe2~\xbd\x9d\xdd\xcc^n\xcf\x80\xa2\x8f\xf0\x0e\x06~k\x0f\xd3\xf5\x9c\x97\xdb\x1b\xb3\x97\xdb\x1b\x0c\xfck\x03#$\x01\x86\xdb:\x13.\x19.j\x91\x18\x82\xc9\xbd\xe62\x82\xbe\x9e\x9d\\\xdczW\x97/\xb7Qo{\xb9\x1d-f\x90\xa5\xe3\x1dg{\xa3\xf1\xe6\x0eh\x82^\xf2;aL\xd2\xdc\xdd\xf266\x9c\x97_{\x9e\xa6\x83\xc0\xd4T\xae7\xed\xf3N\xea\x11o'\xb6\x07W36\x86\xe7\xa3\xfe{\xa3 \xd4\x1f\xc5Ir\xc3\xde\xf9\xe7\x9fl\xd1\x12\x1f\x8e\x82\xb3\x1fO\xde_\x8e\xde\x8c\xb8\xac/_\xec\x9f\x1c\xd5_\x9c\x8f~=\xf7\xbb\xa9\xa1\xf1\xf9\xa3\xe0\xf5\xe1\x9b\xf3\xd1\xe9\xe5\xde\xfe\xfe\xe8\xed\xb9y\xf5\xd5s.\xd5\x8b\xb4\xaf\x0fWFE\xa9\xfd\xee4\xb4\xdfs\x8d\xf6{\x8e\xb1l D\xe8U6&t\n\xe70\x14\x07\x9d\xa6\x86\x88\xa6!\xc2\xd5h')\x16W$UM\xdd\xa4<\x02\xe2\xc7\xba-\x9f\x07\x0ep\x1c.\x0c)O\xf5\x88\xf9\xd8\x12\xb3\x1a\x973\x9b\xcf\xcf\x17\x04]+\xd8\xff\xc1\x94\xa6\xa3pN<\x95\x0c\x8eQ\xfdT\xdf\x9cb\xe8/\x8d\xcfJ9\x7f\x86 \xce\x03\xc6\x99\xf6\xab\xe3 \xed\x91H\xaer\x07\xcewJi/S\xfb\xf1\xb1\xb3\x89R&\xb3@f\x8a`\\\x05\x969\xe1\xb9\x1al\xf9\x7f\xa5\xf4Q\x91m\xddA\xa7{J\x8a%M\x1a\x13\xc2\xe7\xa3\x83\xfd\xf3\xf3\x8e!\x18\x8eH\xe4\x13\xc61\xbd%\x93\xf3p\x96\x0d!\xb1\xa9f>\xac%\xe4\"\xfd\x80\x01\xff\xd8\x1f]\x8b\x80\x8d\x80\xab\xb2k#\xach\xc2/ \xa2$#i\xbe7\xf9\x18\x8eI\x923&\xdeG\xc4\x01\\i\xed\xba\xae\xb37\xcdI:Bg:\x06\x90p\xc1\xe0\xb3\xc9\x94\xcd\xf97c\xadk\xff]\x9b\x12\x1eT\xb0%\xd3\xf0\xd7\xca1]\xf9C\x0f\xbb\xb6\xb1\xbd1\x0br\x92\xe5.Q\x97\x10\x97\x0eV\xd2\x9d*M=\x18\xc74\xe1\xaa\xa0m\x03\xaba\x99'9\xa9:P\x06\xe8c\x1d\xf4\xc1y\x12\xe7/\x1c\xcf\x93\xa6*\x99\xeaA\xdd\xf7\xb9\xb8X\xfeS\x1fO\xd9\xde\x0f>8\xc0$G\xf9\xe2+\xfe\xc2\xafW\xa8\x82J~\x01,\xa8\xdf\xdd\x81\x84\x0d\x93-\xe2\x90\xd1\xa3}[\xddZ\x85\x0b\x9c\xae\xc8\x05V\xd6\x07\xedpiO8\xda\x13.\xea \x17\xf6\x84+\x1e\xcd\xf2\xca]\xbe>;<\x82j\xc5a\xba\xb6>\x86\xf4v\xcc\x15\xdd\xc3\xda\xe4\x1b\xb5.\xa0\x89\x0e\xfa\x970.z\x82_\x13\xb2d#\xd2\xc7ki>\x82\x15T(\x18\x0253\x04\xd0\xebJ\xea\x83\x8ebl.\xc2\xd2\x11\xac@_\xd6n\xb4\xc8\xec\x92(k\x84\x17\xc5\x07/H\xc2\x05\xf1\x91\xf4\xf2\x00\x0f\x98\x82<\x8d\x16\xae\xe7\xf3\xa0\x85u\xbe\xeaC\x16H\xd4\xf2\x04P\xfc7\"\x8f'\xeb\xc8\x02\x89\x1e\x91J\xb3\xc9m\xf7\x94\x18\x96hJ\xe6_W\x1a\x92\x07d\xb8\x85Q\xe4o\x87G?8\xca\x8e&\x05\x9d0\x88&\x1e\xd29\xfb\x8b\x13\x14w^\xab\xbc]1\xa0]\x10.\x97\xf1=\x1e.\xbf%.?\x8e#\xfcG\xc2\xff\n\xcbL\x12\x91\x07/\xa1\xe0\xbcA\x95PD\xb5\x88\xa3\xc9\"c\xc8\xc7\x90\x12Q\xf7\xa0\x93\xca\xe1\xf1\xdbw\xe7\xbaa\xf2\xbb\x0e\n:\xf0f\x1d\xb7\xb6\x0bs\xf9\x05E b\xad`\x7fy\x1eF\xc5\x8d\x92B\xe3\xc7\xa0{\xd8\xc8\xb0\xb9D3\xec\xc4\x07\xc7Qp\xd5\xd9\xa2\x9d\xcb\x83\x18\xaeB(\x18)\xf8\nY6\xf6d\xad\x1c(\xa7\x03\xfe\x9b\x0d\xcfM!J`\x8f\xfd\x8d\x7f]\x13\xcf\xe8P\xd9|\xd8G\x05#d\x04\x87\xff\xa4\x9dl\xcf\xc3\xa3\xb6'O\xe0\xdf\\\n\xa0^\x8f\x99\x079\xfb8P\xac\xfe\xebc\xaa\xf7\x1b\x18\x88\xc1\xad\x95d\xc0\xa9`E\"\x00\xd1\xcc\x19V\xee_\xa7\x1chN\xf8\x18+\xa4\x12\x82\xb4\xd3w\xcc\xa0\xb6\x86\x97~\x15RPn\x0eT\x04\xc1\x1d{\xaa,0\xdc\x80\xc8m7kw\xe4\xc2\xa4 |\xe8\xa6b\xf5\xc1\xb0\xa2\\\xe6\xfe\xd7g\x18#\xa8\xe3L\xaby\xea\xd5@\xf7\xea\x82N\xd3T\xf3i\xaf\xf8\xd4\xf3\xd5\x93\x01\xba\xb4\xc8h\xea\xb3\x82\xb8\x0f\x9d\x83\xb1\x97\xb6$@\xad\x94alb\xa5\x03\xa5\x03U2\x04b?\xd7\x92wM\xfa\xc8Tl\x13:b\xed\x99\xa9\x07\xf9}[\xa6:\xc3\x80>\x07'G\x0e7\x87\xb0\xc1\xbe\xc0\xef\xa6AB\xeer.X\xbf\xf0Z\x0c\x98W\x14\xa1B\x92R\x18;&n\xc2\xb5\x9a\xa4\xd4\x8f\x14\x8d\xff\x049CU\xe6\xf9p\xcajX:\xde\x9a ]\x97\xf5\xb3`\xbcxr\x17d\xa2\xb1\xbe'|}g\xa3\x8f\xf4\xddG\xf2\xee#u\x87\x1d\x924f#\xe4Qqa\x07\x9c\xdf\xef\x9e\x8d\xd7\x06\x83\xdf\xef\x9e\x11\xc6\x88K\xf3\xceZ\xa5\xeb\xe3\xdetH,\xf7\x0b\xa0\xed\x0b\xab\xd4\x0fr\xcaO1<\xc8\xe7)\xbd\xc5\x83\x1d\xa68\x8e\xd2\x94\xa6\xae#\x8b!\xca \xa19\x84%\xf2M\xce\xb0\xe5\xf7Z\xbd\xc5AU_t\x19\x0b\xd7~t\x12\xa5\xf9}\xf5E\xde\x90\x0f\xe1\x15M1N\x8d\x81x\x8c(]\xab\x1d9t\"J\xb5\xbd\xde\xbb#\xecp\x98GcnHa\xc2\x8a\xce\xec\xd2\x84\xeb\xb6\xe6\xe8\xec\xb1\xa55\xac\xde\x9c\xdb%w\xb2\xf6\x04\x19\x18\x1a\xa8NtV\xdd\x1b\xc1t\xb3M>f\xcc\xcf\x91\x9a\xf7\x08\xba\x916/1\xd4M\xdf\x1e\xf0,\xbb\\HK\xf8\x19J} x\xf5#\x06\xc5a\x98\xed\x04k\x9b\x9eW\xb7w\xbf:9\xf8M\x88\xcb\x95\\\xbd\xcb\xf7J\x18B\xc2\xb4\x03\x92L\xf8\x99Xj:$\xb2\x0bdH_\\\\_\x9b\xe0\x7f\x03\x99-\xb8\x14N\xb6\x1d%\x7f\xb7}\xd5\xac\xc9\x91\xa3\x80+\xea\xf0^\xf3\x9b2\x06W \xfd\x14\xf0\x93\xe6\x13\xb6}\xa3\x95\x8b\x1f\xef\xe9{P\xdeC*8kJ\xbc\x17\xb8\xef\x15u\xae\xc2\x0dL\xb4\x86h\xca]x\xd8T\x1f\x13\x97rnB\x8d\xdc\xe4\x80T\x85\x9c\x9dP\x91\x8c\x98\x1a\xfa\xc60\xb3\xb0\xdae\x18\xc4\xacCG\xc1\x11\xb2-\xf8'~\x9e\x904<\xf0_\x80\x8a\xa6\x17\x1e\x845\x02\xe9\x81C\x90\xf4\x82A\xfb\xcd0b^\xef\xb9V\xc2\x80\x7f\xe3]:\xf3e\xaaK\x1f\xc2\x15&Z4\x88G\xb3\xea\xd9-#\xf2\xd2\x94\xd8\xaa\xf9\xc0\xd6dF\xf2}\x9aL\xa3Y/\x1b\xd8\x1e7\xd2r\xdfdMly\xd6\"\x06\x8aj\xb7ij\xb2rW\x95.\xcf\xfaf\xc3\xc9\xe4GJ\xaf\xfb\xf2\x7f\xfd\xd9\x03\"\x1c\x8f\xa3v\xf8\xa9\xd4\x9f\x7f\xe2^\x84'Sh\xc6\xcc=\xcdU\x8cj\xf3ju\xc1\xf4\xfd\xda\x99\x97^\x90n4\x9b\xad\xd4\xae\x1c\xc5\x85F\xa7Q\x1a\xde\x8b\xe3V\xdb\xc6\xa6\xd1\x0fW\xdbZ\xed\xe5\x832\x16\x9e\xce\xb6\x0c\x8b\x9c\x8a\xa2G\xc5W\x16\xfev\xfcpS\xdeSvs\x1f\x9c\xcbK\x92\x1d\xd1 \x0f\xd3S\xef\xfc\x0d7\xe0\xa9\xa9\x02\x94\xd5)O\x8cb7q\x9f7o\x15PQ\xf0\xb4Y\x10\x89\x82g\xcd\x82P\x14|\xd3,(D\xc1?\x9b\x05\x99(\xd8T%f\xf6b\x8b\xbd(\xdf\x94:F\xdc\x9ey\xf5\x06, *T\xe0\xe9\xb1.\xa8\xaf\x88\xaf\xd6\xf4\x0dlF\xd8\x05\x81\x9f\xb1\x95\xee\xca\x9e\xe5\xb6k\x9e\xee\xa6\x0f4\x10\x1f\xf6\xdc|\x1ee\xdc]\x95\x15\x84\xcd\x027\x0f./\xd1Twy\x89\xccb\xd3\x87T\x01\xf2;\xd3\x88P\xd0%\xbb>\xba\xaf\xab\xe0\xc5\x82\x93\xb4\xb4\x88\x99 \"[/\xaa\x8554]\xc3\xe4`lM\x0dM7<\x01\x0f\x0e3z6\xa7\xb7f\x92[Zmh\xe6\x01,;\x87\x18\xf7Et\x94Li\xba\xe01 ;\x88\xc2\xd2\xa1\xb1\xeds\x0bz\x15\xc5d\x08[OWm\x96\x8aqz\x96\x91N:q1\xed\x84\x98wB\xc4rg\xf8D\x0cXx\x08\xc9\xaes\xba|\x0c\x9a\xc2\x1eh\xfa\xaf\x1e@Q\x0e@\xa7\xb3\xd5\xde<|\xf0|\xe5*\xc2\x83[\xb5Y\nS\n\xa3\xcbe)\xec\xc0\x18\xdf\xfe\xbd\n\x8d\x0fy\xf0SF\x13\x14\x15\xc2Kn\xa1D&\xad\xbc\xbd\xa24&a\xd2|\x8d\xe1\x03\x9b/\xb9\xe9\xb1\xf1\xf65M\x17\x1a.-u\xa8{\xa6*\xb5T\"*KZ:Q$JZzW(\xab\xe8\xb4\xa8{\x9d\xde\x95\x89\x82\xd67bQ\xd0\xd2\xbb\xb8\x94\xd7\x14\x88\xa6\x08>n\xbc]\x8aF\xb6\x9a\x8dp\x01\xed\xdb\xc6\xdb\xb9\x04\xdfj\xf5\xf3F\x16\xb5\x86\xb6\x90%\x9b\xdf\xb4\x061\x13\x89\x8a\xb5\n\xe1\xfd\x97U\x08\x97\xe5\xba`=\x08\xa2\xecT\x84\x85\xf6\x95\xa20\xb9\xf7\x1b\x90\x96bN\xad\x86\xa6x\xa1\x0f7\xe5\x9b8\xcar\x15\x82\x91\xb5\xedw\x98\xdc\xd7i\xf5\xaa\xe5*t\xa3w\xf2\xa1\xc9\xfe\xf9\x86\xb6]\xcd:\xff\x1c:\x7fK\xb5\x97:\x7f\xd6,\xd0\xe9\xfc\xaaF\xfe\xa9:\x7f\xac\xb4U\xe9\xfcuK\x80Q\xe7/\xd3J\x1dD\x93#\x1eG\xb6\x05\xf9\xd7\xa9\xff\x93([\x86\xf9x~\xc8t\x860\xe6\xceP\xc6:\xdc\npc\x07\xe2^\xd2\x92\xc0\xf5\x1a\x17\x1aCS7\xe9\xe4\x9d:\x16\xff\xf7\xd9J\x90\x84\xbb\xd0\xc3\x97Z\x17~:\x90\x18\xd5\x90h\x91\xd8W\xb0\xcb\x14\x08;5\x1c\x0e\xe4AN\x7f\xe2\xd7\xaa9{g?]\xd3a\xbb\xf4\x8b\xb4|.F\x17\xbb\xfc~i\xe9\xfe\x18a\xb8\x9a\xbf\xe0\xa6\x80>*\xa9\x0f\xb4=\xe3\x06\xc6\xd3\x06\xac\x9di6c\x02\xfa\xb88x\xa8\xc5\xc2\xe3\xf9\xaa7_\xc0\x18\xb6\xa1x\x01\xe3\xf5u\x0f\xe2\x8b\xf1\x07\xb5\xe6\xc5X\x13kQ\xc6Y\xc4S\xe5\x1d\x03\xf3\xc3=\xae\x93\x01\x8e\xc38\x16\\\x90\xf8p\xc1\xea\x96\xc1$\xb8\x9e\x96\x96\xdbQ\xaf\xc3\"\xe9\xae\xaez\x8er\x92\x17\xfbh \xa2`\x92\x80G\xec\x0e\x18\xa0\x88\x81X\xbeC\xba4,<\xd1\x9a\xec\x15\xe3\xb2\xf2\x9d\x90\x90\xb4\xc7Sl\x1c\xa3\xa4X\xac0\x16\x81\xe7\xd6\x17\xf5\x1f@\x9bvK\x14a\xf4\xf4%\xe4\x89\xbf\x81/\xf6c?+\x08\x0f]\x8c\x96\xf6b\xb4\x9c\x87J\x99\xb8\x8b\x87N\x08\x8f\xf3d\x8c\\\x07\x82\x85\xa6\x01I\x8a\x85\xd92\xcd:G93\xdd\x15\x7f\xb8\x1e\x0c\xf1\xac\xb7\xe82U#Ou\x1d~\"c\xf3s\xea`;V\xbe\x02u\x8b\x1a\x95\x91Jw\xc1\x89\x12\xcc\x07\x84\xd7\xab;\xee%`\x90\xa8Zm\xda\xa3\x96\xb8\x9b\x80\x82ff\xe5]P\xd1\xaceF@\xb69Z,\xf3{q\xa5b\xcd\xc2\xa2\xa0\xc6\xcb\x90\xc8\xd5\xfd\xc0X\xcft\xbb\xd3\xb8\x86b\xdc\xfch\xba8\x08\xf3Pn\x80\x11\xba\xbb\xaf\xb9\xce\xeb\xb2 JD\x0c\xda\x8e\x83\xa3\xdcu\x0e1\x91\xa4]\x10\xa9\xed\xb7b\x8b5Q\x89\xd5\x82\xc6\xea\x0eEs\x96\x9e}\x12\x1d\xadNC\xad\xa9\xeb\x92\x90e~\xaf!\xc4\xfa dk\xd3\x84\xa0\x85|\xdf\x03Q\xcb0\xcbni:\x91\xb8\xe7R-CFU2\x94\xb9\x07\xffk\xf0\xd9\xbd\xc2\x16Q\xf2\x06[\x1b\xda\xfcK'\xe4\x8a\x16\xc9\x98\x9cG\x0bB\x8b|\x08\xcf\xbe\xb1@+\xa1\xe7\xacb\xe9_0\xdb\xad\xd7\x9fU\x02\x95\x16\xcf^\x02(1\xdc]\xef-dJ\xf3\xe8c\xad\x1e<\xae\x06Bc_\xcc\xd1\xf7\xf5\xc2\xdf\xaa\xf2R\x1ady\x98\x0b!\xc0(\x9c\x1d\xe6D'\x9cY\x1c\xae\xd2 #\xf9\x19k\xba\xba\xdao\x8d\n :hg\x91ri\x88Kj\x19\xc9\xb98f\xacd\xf2\xefW\xb0g\x184w\x98b\x03\xef'\x8fj\xc6k\xbd\x1f\xb0\xcax\xe5\xa5<\x11\xce\xe4/\x19o8\x994\x07\xbb\xcaX\xfb\x04\xc4\x10T\x06;p\xe9J\x8a\xeb\x12\x8a\x04\x06\x048w\xcaslau\x1e\x8d\x80\xd5U\x10\x0d\x1az`\xa1\xdfx\xff\x82\x01\xe2B7^\x9c\x15\x1f\xaefF\xdbH\xed\xe5_\xa3-\x95\xd6\xd7\xf7Q\x1c\x9f\x921\x89n\xf0\xb4,\xeb\xa1@\x19\xe7J\x92\xde\xda\x8e\xd0\xa2\x94]\x8f\x89\x7f\xfc\x9d\x9cN\x9bB\xa0\x92\xa3~*:\xf9\xd9\x17\xb2\xa0\xdau\xc4>\xba$?=\xec\xa7KR\x84\xedV\xed\"\x84\xebR'C\x84\xeaR'\x0b\x842\x99OC\xbc\x11,\xb4\xbeP\xd5\xfa\xec\x06\xd4\"\x88\x92)I\xb9\xf8\xe0FA\x94\x93E\xd6\xedhV?Q\xe9\xe1s\xf6\x8ag\xf7\xef\xf0\x1f\xcbP\xb7\xb5\x88W\xd0\xa6h\xb3&\xbc\xec\xd2v\xe7\xd2\xd3\xed\x13\xb5\xddy\xd7\xc6\xaeH\xd5\xe1\xeaR5T\x92\xb5R;\xecQKf\xdf\xed\xbe\xb7/\xd6\x9c\x85\x96\xa1\xad=\x1b\xa2\xbf\xd7\xa0kz1\xfd\x9b\xf5\xe2\x8ey\x14\x0eW\xdc\xedc\x8dGC\x99\x04\x98]\x91\xfd-\xfet=\xd8\x86\xad\xea^\xca$X\x84KE\x10\xf2\x81v\x11^$\x84\xe6\xb4n\x96\xcf:.\x96\xc9\xd9\xb75\x0f\xe2\x13K\xdc\x10xZ\xd7\x9e\x92\x8b|J \x06\xaf\xf1\xf0[/\xd6J\xb6p\xab\x80'\xeb\x82j\xe5\x9d\x8f\x8b\xe5\xc5\xe6\x07\xbe\xe3\xc1:P\xcb\xdd\xe4\xce{Y\x1dsi\x1f-2\xa2\x0e\xa2T}\xbf>f4\x19\xf0\xed|\xc0\xf4\xeb\x01\xdb.\xad\x0e\x81\xa6\xeeY\xdd\xcd\xa0\xfbd\x05Z\xa7+\x1dF*)]\xf7]\x81\xfd\x04{\xf9\x94$\xa3\xaaO|)\xd8)\xc7\xde\x1dy\x9e\x13Y\x96\xbf\x19\xc7V\xf3\x124\xa6\xf6*O\xe0*O\x06\xd9\x02\xb4\xb3<\xe0\xfaH\xc7\x86K\x93\xfd8\x1a_\xf7\x10^\xd4\xa7\xc4^\xa5\x87\xb9]\x88\xb3\x11\x9d\x03\x03pL\x9e\xa8^\x90S~\xf4\xf3X\xd4\xad\x84\xb6p2\x01\x07\xd6\xab\xcd\xab\xc1\xf8\xb8\x1b\xa1\xf1[%B\x91#\x08\xbdM?06\xee\xbd\xc9\x04\xd8g\xb5\xc3\xef\xb4\xb4\xbc-R\xb2\x8a\xb5\xa5r;\xebeo\xf9\xdf\x81\xdf\xca\x07~\xabj\xa9\xff;(\xd3?\x7f\xd1AY\x97\xceB{\x1d\xa7\xd5\x0f\xca\x0c\xa7\x0bx\xf2%\xf4\x9b\xb4\x9f~\x13\xf69\xcc\xea\x10#\xc2\x9e\x1ba\xba\xbaX/Dz\xa5f\xda\xcfX.\x82\x08$\xb6\xdbFuA\x9d\xbb\xc6MS\xba\xf8\xe9\xccs)jYx\xff\xd3\xc9S\x9e`e\x1a\xc6\x999\xe1\x0b\xe8\xa5\xf9\xb2\x1d\xdb\x81\xd7\xaaB}\xb7I\xe1\xd3L\xe4\xa5\x07\xf1\xa3\xf7\xec\xde{\xb2\\\xa1\x9fl\x1f\xb7X\xc6\xd9\xc2\xc9H\x8esrN\xcf\xc2\xc52\xeee#\xaf\xbc\xbb\\\xf6\xe5\x19\xdb\x1cxm\x8e'\xcf%5w \xfd\xdd`\xa2\xb5\xcb\x1bEF\xd2\xf2\x990\xb4:\x0f\x93ILNVi\xfb\xa6\xccw\xdc\xed\xbb\xa1\x0c^\xe7\x03\xe8\x1b\xbd\x85\xe132\x80\xcf\xe9y\xb9V1\x81\x86\x9dO\x9d\xc3\xf2e\x9bdtw\xb4\xeb8\xf8B\x86\xbc\xffbN\x96\xbb\xce9\xb9\xcb\xf7R\x12>\x92\x9b\xd4\x0c\x0c& \xda\x93\xe50R\x9b+\x06\x04c\x1d\xf6\x08\x9e\xc4\xd8M\x16\xfda\x0d\xcfkF\xbddX\xac\x05d\xc3\x1fi\x94\xb8\x8c}x\xfd8\x97EGm\xb0\x89\xfa\x06\xa0\xad\xf5(w\xbe.\x11\x1f\x81\x1fu\xe3E\x1e\x86\xe2E\x87\x7fz\xc1\x818\x91F\xa7\x89\n,\xad\x17\xf0\x10\x92\xb58\x02\x8f\xef\xc2g\xbdt\xd3\xec\xa6\xe9n\x8c\xf8h\x98e\xd1,a\x8c\xcc.\xa6\xd7\x92>o\xf1\xfc\xceMuE\xe4y\xb6\xef\xf3\x95\xa6bJ\x03]~\n\x03'&=\xf3\xc2c(8\xb4Ta\xac\xe9\x1dH.R]\xa0\x89\xd6\x1b\xc9\x90\xeb$X\xa7x\xda\xc5\x9aK\xd1\x83XO\x9ck\x19\xfe7_@\x02\xdbj\xa2\x7f3\xf6@\x99\xb9\xfc\"1`\x0e\x90P\x99tG\xd2\xf0\n\x05\x8a\xdaO\x91|,e\n\xdb4\x9a\x15\x12hm\xb3L\xda\xc7P\xce\xe3\\\xa6\xc1m\x1a\xe5%D\x99}\xaaI\xa7\x845xM\xee\x19\xfe\xf5\x0b\xbe\xff$\xa8\xd6X>\xa1V\x85\x91\x07\x01u\x15\xd2\xe0\x99\xc3R\xf1\x9eG\x07l{\x157\xb6\x9b\xe6\xc5r\xa6\xd8\x14<\x02F\xbd \x14\x05[\x9b\xdf|\xab\x0f\x86Q|\x91\xbbOn{\x99\xf7\x92\x8a\xb5+{\xad\x9f\xb3\x04\x8f\xf5T\x8b\x80\x95\x9b\xc2\xa1\xed\x87IBs`\xeb\x12B\xce\xfb \xccj\xa1\xd8\xdas\xd2!\x90'}\xbd:\xb0\xa3D\xed\xd9)\x99\x92\x94$\xe32D\xdc<\xca`\x1ef\xc9\xd79\\\x11\x92@\xc4\xaf\xb1D\x19\x99\xc0\x00\xb2bIR\xd7\xabA\xb0\xa1\x90I\x87\xf8\xb0\x86\xc7\x0dJB\xc9Z\x10\x1fm8\xbb\\P\x81\x86F\x0d\xfa\x86X\x843\xc2\x98\x1f'\xfa\x93i\xcb-\xc7\xa2y$\xab9d\x93`I\xd2,\xcarSX\x05\xc9\x14\x92\xee\xd3\xbdd\xa5\xe3kU\x1f\xd0o,=s\xaf\xb0\x1e\xd2~=dO\xe9\x06\xf7\x92U\xe1\x82x\xe9\xcd\x86\xe1\xaa\x12\x9aGS\xbc\xe68,\xb7oxYU|\xf2\xa4\x02J\xf1\x88\xa8G\xbe\x066\xd8!\x08p1\xf8\xaeZP\xe1\xcb\x92\x91\x0e\xf4\xeayUd29\xb7\x89\x12\x13-%?\x93\xfb\x03zk7\xa0\xca\xa7\"\x0f\xa9C\x8a\xda\xfa pFI\xceS\xc20\xf1\xfe\x9a\xdcsdNi:&\xc7\x12\xed\xbe\xc85e0\x10\xb2.\xbe\x8a\x8b\xf4\x91\xfdcUM\xf4\xbbb?\xb8\x86\x80\xf0\x11\xe9\xd7\x1f\x1eQs\x1b6\xbd\x92\x86\xba\x84\x0f\xf9\xc8\x05^\xc4\x06/F\x83V-\x03\xfc\x8a\x84=\xb5\x0f'\xc1\x84\xf2\xf1Z*\xdb\x97^.L)\x8a\xed\xa5\x1b\x0d\xf2I\x82(\x13\xbc\x8e\xdf\xd1a\x02L\xd5)\xab\x9f\x19\xdb\x07\xcd\xcb\\\x87\xddGtg\xd3\xd7\xcf\xbf|\x90\x0e\xa6q\x91\xcd\xfbN#TS\x99\xf3\x9a\xb6\xb4\x13Hf\x8c!\xc7\xab\xb4\xafEk.\x1a\xb2}NOXz\xea\x97\x93\xd4\xa7cI\xc3\xc4$\xce\x18D|Z\xe5r\xad\xfeS\xca\xba\xec5\x9f\x98_\xa0\x86\x03\x1b\xc6J\x0c\xe3^$\x91d&--K\xec8\x81\x04\x0d\xb31\x7f!Wx\x14E\x9e\xa4\xac\x08\x0c\xa2X\xfe\xfeR\x0c\xe8\xf1i3{\x07\xdf\xc1\xa9\xee\xe5\"(\xdd\xe6\x98<\xd6f\x8c\xd8\x8en_\xa9Aj\xcd\x87\x9d\"\xa81r1\xb2\n\xf4=A\x07?\x83\xe8|\xc6\x84O w\xcb\x94d\x19\x93\xda\x17E\x96\x03\x89\xf29I\xe1\x8a\xf0\x06h\xaa\xc8\xd2>\x06\x1dv`\xbd\xfc\x90\x862I\xa5\"U\xba?\xe7N\xae\xc8\xdb\xa8\xe8Pz\xd4\x8ei\x92\xe5i1\xcei\xaaS[\xe4#g\xc0L\xef\x95F\xda\x8e8\xa0>R\xff\xb4\xbbA\xa9\xba\xec\xd0\x94\x8cICK\x92{\xbb\x02\x1bYM\xa2\x86]\xd0\xbe\x17\xf3>DUN\x8a\xe5l:\xeb\xa4\xc3t\xcf\xf2T\xa0a\xbd\xf2\x81\xf630\xbf\x8f\xe2\xf8S-\xcch\x95\xab\x8b!\xaeb`n\xdc\xbf\xe8\xb2\x97X\xac\xc9\x7f\x89K\xac\xdcH;\xb7\xd0D\\\xc6\xab\x8dF\xbf}\xe2\xe8k\x8b\xff\xcf?\xcb\x8c\x85\xb84+g[\xc5\x01\xb7Q\xd2[\x8f1\xddi\xf6!\xa9<}\xb5\x93Q~\xac1}I\xb7\x01\xb5\xe74\xbdK\x16\x9f\x83\xbc\xb8t#{k\x92Xzw\xf1o8\x97\x10\xb9\xbe\xec\xf4\xe5*\x91\x15J\x8a\x04R\xb1k\xbfM\x82\xec\x95\"\x9b\xbc\xbaG\xf5\xc6\xe68\xc3\xa3-TUNP\x1f\xb1\x9c\xef\x8a\x90\x0fB\xab2\x03\x16\x02\xd0\xde\\\x86PQ\xb2,\xf2S25\xc3\xc5}\xcd1\xf2\x916\x9c\xff\xf4I\x1aUZ\x7f\x89\x07y\x19\x96<\xf5\x98\xb8\xb3\xa9XA\xec&aR\x9a\x84\x13n\x12\xc6\xac\x85\xf6\xcfK\x1d\xca\x08\xf4\x80~/\x8e\xa0\x18\xc7\x07G\x12\x85S\x1aQ}pJ\xa2\xc0d\xd1u\xa2\xc0\x83\xfb\x16Q4\xde\xf2y\xe7\xed\x8b\xb9\xe5?\xe4k9G\xd6\xd3\xffqG\x0cKt\xf3\x86]\xcb\xdc\x95_/\x1d\x01\xc4o\xfd\xbe\x06C\x08\xfb\xb6g\x88\x17\x0eC#\x910\xba\x98v\x0c\x89\x95\xd3\x8e.0\x1c\x96\xe3a?\x8c=)z\xb5T\xadB\x99\xba\xb4(r\xaeueb\xe8\xba\"\xf3=\xd8\xd6\xdd\xd7\xad\xcd\x06D{\x93h\x8b\xc2\xad-\xa3\x0d\"w\n\xd9\xc1\n\x97\xf8W\xc7\x99\xa5\xe5\xae\xa0\xdc\xd3\x9d\xd1\xdd\x92\x8cs2QM\xfcmBIa\x07\x8e\xc3\xe3v\x01cz\xce\x85\xf0\xf09\xbb_\\\xd1\xf8\x83\xa6~\x04;\xb0\xf1\x7f\x7f\xcf\xd6\xff\xfc=[\xffjc\xd6\x86\x08\x11\xe2b\xb0\xfea\xf3\xeebs\xf0}8\x98~X\xffjC\xe3\xe6T \xe4\xe6\xd5\xc5\xe6\x96\x01\"\xe3\x10\xf4bs\xf0\xad\x01\x841A\xcc\xad\x7f\xa8\x93\x1d\xd8\xde\xaa\xa4f\xa9\xe9\x81B\xe7:\x11NM;R'\xc3\xd7\xed\xa6\xa6\xfa\xa62\x12OY\x0d\xf5\x7f}\x9b\xac\xa4\xdd,\xdb\x80\xc6x\xf6\xcb\xfey-\xe7\xd9\x91\xd6\xa7y\x949\x9e.\xec\xf2\xa4R\"+\x16,\xd3\xe4\xb4\xc1\xe7\xb0\x03Ga>\x0f\x16\xe1\x9dF\xac+K#\x8d\xf8\xd2\xef\xb6'\xef\xf028`\xdbNBou\xf2\xa7r^\x07\xea\xb9\xd8L\xaf\x7fH\xddC&\xba1\x1e\xa8\xac\xad\xf1\xac\x18\xb5 \xd2d\xddiz\xa7\xea{\xa3\x89\x9e\x08\xd2\xac\xa0\xc9\x97nK\xd3\xc2\xeat\xebX\xa2\xbe\x93\xe1\xba\xab5\xde\xed\x16\xd0hD\xa0BC\xaa\x066\xc0Z}\xf2\x04&B`\xf3@{i\xe5AM\x13\xa4\xb1\xcdc.\x15KF\xa9\x9b2\xa8PmBdF)\xdc\xbdQ\xe5/\xffF'U\x93\x17\x1a\xec\xc0\x8cm\x86\xbb\x90\xc3:\x8f)\xd6u\xc6\x0c\xcd\x0cJk\x9a)\xac\x12\xe6\x13\x18\xc2\xba\xe6\xf3D\xb8\xdc\xf2\x84~\x11\xe6\xf33\x1f\x97\x16\"\x1d\xb4\xe5,\x90\xcdp&\xc1`\x17bW\xe4!u\x9f\xa2\x86\xba\x0bOa\x08\xdf1l\x84\nX\x8a\xfdk\xd0\xb3\xfaK\xf5\x8ci0\x17\xed\xa1>\x1e\xd1\xf9\x10a6\x99\xc2\x87\x0c\x85\x13\xf4w\xd7\x0b\x1cSn\xb2\xd3\x96--e\x13\xb4\xd9\xebIH\x9fpLo\xa8K\xbc\xc6v\x02\xea\"\xbe\xea\xf6w\xb4\\_b|2\xb2Jv\x8ca*\xe9\xdbx\xa0\x17_\xa8x\xdcr\x9e26\xae\xa1Js\xa75\x91;\xe5#;M`\x00\xb1\xb5gJ\xc0\xbd\x98\x11W\xc2T\xb6\x9c\xff\xb5\xcdu\xb7%zB\xc0\x00\xc6\xac\xac\xad\x04\xd8\xfax\xdb\xa9\xf4/l\xe1\xff/k\xf9\xc6\x8c9\xca\x18\xd5f$\x17\x82\x99{\xeb\xf7\xdc\x05K_V\x18\x80\x8b\xb8\xea\xbe\x9c\xba\x84]\xb8q\x13\x1fBYi\xec\xa1\x05\xdf\xb8a\xae6\xab\xa3\xce\x9d?S\x08i\x02\x98\x1dk\x17\xae\xf89\x82\xdb\xa4\xb4b\xb5\xaf\xdf\xf5\x99/\xf3JHx\x1c\x06\xcb\x8cR\xd5\xa5\x8c\xe7\xe4\xe2.\x10L63EJQ\x1bP\x086\xf3\xdaV\xfe.\xb3\x86\xa80\xe6_k\x13N\xee\xf90\xad\xf0\xa9W\x14\x01g\xd6F,\xe2^\xb42c\xed\xcf\\\xb9\xa6\x00\xfb=\x17l\x86b\x8c\xaeq\xcf\xd7\xf4\xdc\xe8\xc5\x95c\xe4\xe8\x1ccbn\xfa0s\x85\x15\x06\xf7\xec\xb54\x88 \xe6f\xe0Y\xb0]\xb6[;\x8b\xf0\xee}\x18\xe5\xdc\xfd\x8cq\x98\xb9{\xef\xa6\x81x-[B\xc3{\xe8\xe3&\xee\xe4i\x18\xc5\xc8K\xd1em\x17\x9b\x96/a\x08\x13L\xe0\xd7\xffhT\xb1\x00#\"0)\x98\xc4B&o_\xf1\xebG\xb1X\x15\xd5\xd2ic\x87}\xbd\xf7\xb9\xafn2v\xa1\x80!\x8c\xdc\x85kH\xf0U{\xa9\xb8\x87IW \x1f\x12\xf7\xd9\x96\xa8\xdc\xa1\xe5I\xe7\xc2z\xf7\x9c`#\x8c\xe3\xe0c\xe6\x0c\xe1\xf9\xf3\xe7~\xab\xb0\xc8\xe7\x1b!6\x9aq\xa8\xa7\xcf\x9e\xea\xa1\xd0\x88\xc7a\x9e}\xffL\x0f\x93\x92I1&i&\xc1\x0c\x1f\xccd\xe2! \xf7\x8d\x01nI\xc6\x83\xdb4\\\x0ej]|\xf6\xfd?[\xf0\xfc\x10)k\x8e\xa5\xdd\x01 8'\xf1\xb2\xec\xe9\xd3g\xed\x01I\xc0\xda\xb8\xbf7\x82\xd5\x87\xfe|\xb3\x8dE \xd9\x18\xfd\xf3\xcd-3(C@mH\xcf\x9b&\x06'\xd8\x98\x10\xb2\x1c\xc4Qr\x1d%\xb3\xfa\xb8\x9eo\xb61[\x83V\x06\xf7|\xb3\x8d\x83\x1al\x1c\xde\xd3\"\x97\xc0m\xcc\xd6\x80\xcb|K\x83<\x9c\xe1\x1c.I\x1a|\xcc\xee\xb0\xf2\xb7}+7+\xb6'~Bo\x93\x98\x86\x93A\x91\xc6r\x96\xbekA\x914\xad\x93\xc6\xd6\xd3v\x1f\x18\x10\xdeG\x18\xe4i\x98dS\x9a.H\x9am\xcc)\xbd\x16-?mO\x95\xa1R\xedGB\xf3\x01\x9d\x0eP\xc9\x16\x0d\xb5\xc9\xa3OC\xcb0\x0d\x17$'\xe9\x80&\x84Nec\xed\x89\xeb\xd3\x18\xd3d\x96\x03\xe9\x0e*\xdbj\xcf+kK]\x04[\xedE\xc0@\x1ak\xffi\x9bN\x19Ts\xe9?m\x13(\x8f\x9dP'\xcd\xf6\x8c\n(\xba\xccxV* \xd9\xee\x1c\xa7\xdb\xc6\xce\xa0YF\x02N\x1d\xea\xd36\xbd \xa8\xe6h\xdb\xd4$\x00[\x03n\x0f%\xa6\x8dm\xe6\xbb6Rh\x98=knn\xed\xceq\xa8\"\x9f\x0f\xc8]N\x92\x8cAo\xe0\x06\xda\xdct44\x83\x95\xcb\xe3\xc5l\x83\xf1\xa0\xabp|\x9d\xc9\xd5\xa7\xc1F\xb3\xce<\xcf\x97\x03\xd6\x01YG\xc3M\x9au\xd4\x89\xd6\x90C\x13\xbc\xda\x1c\xd8vQ\xf6\xad\x8dVs\xc5\x8c\xa7X+\xfb\xd8\x8d\x8b\x94\xfc\xbf\x82d\xf9\xe0\x8aN\xee\x07d\x12\xe5\xb4\xdc\x93\x9e\xb5\xf7\x04[\xed\xb2\xc3m\x8aiV\x13\xdd\xac\xb2\x1d\x95\x9fl\x13\xaf\xa1n\xf9\xb5\xf6\xb2\xc0\x1a5n\xf1\xcc\x80\xfc\xda\x04\x19F\xdb`\x7f\xcf\x0d(m\x92\xe1s\x03y \xe3Sh\xb8E\xbe\xedmJ[OO\xfb\x86\x8f\"\xb0\x82C\\HQN\x16%\xde\x0d\x0b\xa0YQE\x98F\x04\xd1\xd6Q\xa38p\x1b\x93D\x91\x01\xe3\xcd\x06\x16az\xcd\x98\xa1\xfc\xaea2[\xd5\xe8\x84\xc4r\x80\xcf\x0d\x84\xd5\xacD\x938J\xc8\x00\xaf\xb6\x859M\x07W\xe1dF\xe4\x97\x0d\xb4\xd6l\xa4df\xd5B4\xac\x89f\xcd\x1b\x9e\x02r\x90\xe5\xe1bYV\xd6\xec\x00 \xd6\x8aINjs\xb2\xd5\x1ef\x86\xb71\xb3\x8d\xa9\xc0\xdf\xd6\xf7m\"\x910\xb5\xad\xba=\xbd\x8c\x06\x9b\xdcF\xd3\x18\x83R[\xd2\xec\x94\x08\xd3\xe04\x9a\xcd\n\xc1\x1aD\xfeT#U\"\x9cF\x9c~\xde&k\x99\xd5\xeecc\xb4m\xc8\"\x8f\xe2\xba\x8c\xdc\x9e\xc4\x9b\x88\xdc\xd6`\x9e\x1b`RJ\xf3A\x94|$\xe3\xbc\xec\xdcw%\xa46]\x0d5^\xd8I\xdc\xa8fly\xd0\xd4\x8e\xda\xb5\xa5\xad9\xbd \x8d[Z\xfc\x06M\x0e\xeb\xb0U\xbb8S\xbf43\x8d\x92 ,\xf8\x0d\xa1\xaf\x1dX\x07\x02\xeb\xe0|\x1d4\x0d\xbdR\xd7V\xfa'\xff\xa2\xc15\xb9\xb7\xe6O\x16\x95\xc5\x11\x0e\x83v\x95\xcb[\x0f>\xd0 %\x19\x8do\x08St\xeb\x17\x1d)+\x8d\x98\n\xbe\xb5\xf9\x0d\xc7\xee\xc3\x07\xef\x1f\x0f\xde\x8b\x7fll\xfc\x1f\xc8h\x91\x8e\xc9Q\xb8\\F\xc9\xec\xdd\xe9\x9b\x9d*\xc3\xe1\xe0\xaaH&1[\xe7\xc1\"\\\xfe\xe3\xff\x07\x00\x00\xff\xffPK\x07\x08\x05\xef\x9fw>9\x05\x00\xf8\x0c\x1b\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8\xec\xbdys\xdc6\x9a0\xfe\xff|\x8aG|w\x152M\xd1\xdd\xad\xc3:,k\x1d\xc7\x9e\xf5\xbb\xf1Q\x963\xf3\x9b\xb7\xa3UQl\xb4\x9a1\x9b\xec\xe1!Y\x13i?\xfb\xaf\xf0\x00 \x01\x10 \xd9\xb2\xb33\xbb5\xacT\xac\x06A\xdcx\xeec\x0b\x16U\x1a\x95q\x96\xba\xa5\x0f\xc4\x83\xdf\xfe\x00\x00\xe0dW\xbf\x92\xa8t\xe0\xf4\x14\xca\xbb5\xc9\x16@\xbe\xac\xb3\xbc,`{\xdb\xf4v\x95\xcd\xab\x84\xc0\x19\xff#\x10\xb5O\x81\xb8\x1e\x1c\x83#\xba\x91?\x9a\x93E\x9c\x12\xda\"\xfb+\x08Ws8\xe3?\xdc\xd9\x05\x0e\xe8\xb8k0g\xe2\xaf\xe0\xfc6\xbc\xbe&\xf9\xcfo\xce\xcb0\x9d\x87I\x96\x92\x0f9)HY\x0f\xa1\xec\xab\xf3\x87\x07\xb7\\\xc6\x85\xdf,\x89X\x8e\x9c\x94U\x9eJK%^\xd0\xe7&\xcc\x81\xc0)\xfc\xf6p\xf2\x87\xbaPT\x85\xd4\xcd\xe5\xca\xf4\x89\x17\xe0\x92Y~\xe1\x89v\xe9\x0f\xb1b'JU\xdavLG7\xcb/h\x17\xcaKl\xeb\x18r\xbfU\x9a\x1c\xc3\xd6\xa4]\xcc\xbb8\x86\xdf\x1e\x94w\x0fj\xa7|T%\x1dU\x14&\x89\x1b\x8b\xc1\xf9\x10\xfb \xfdJ=\xfa3\x81S\xd8\x1aK/\xea\xd6\x9anx\x9bi\xb0\x82S(}H\x83\x88N\x8b\xfe1\x87S\xf5\x10\xfa\xd0Z\xb24\xc8\xf8\xf9\xbc\xbf\x87\xf7x\x1c\x02vL>\xe4\xd9\x9a\xe4\xe5\x1d\xff\xb2\xbdBQ\x96.\xe2\xeb*\x0f\xaf\x12bY\x96\xb4Z\x11\xf1~\xdc~\x7fM\xcac\xc8\xd5\x15\xf3\x9a9\xd29\xa4\xca\x1c\xf4\xd1\x8b\x13R\xd2\xa3^\x06\x97\x97\xa4x+\xeeK\xeb\xac\xc9\x8f\xd8 :\xd7\xb0JJu\x0cp<\xec\xeb\x01{\x9d\x06s\x97\xf8\xe0\x84\x0e]d\x1f\x88:\xbdL\xdf\"\xbd;\xde\x0c\xdf\x99u\x9e\x95\x19\xbd\xa9\xc12,\xde\xdf\xa6b\x8f\xd8i\xc2\xef\xd5\xf6\xd7p\n\xce\x93y\\\x94\x8e\x0f\xa9\x9b\x06\x14pL\xc7\x07\xac\xda\x83;\xd3\xceG*\xf7\xefT\x05\x81\xa2\xcc\xe3\xa8tN\x94[\x99\xc3)\xa4\xee\xfe\xd4S\xf7\x94^\xa8\x99\xf39N\xe7\x8e\x0fNN\x8a,\xb9!\xf4\xcf(K\x8b2\xaf\":\n'N\x8b2L#\xf2~A\x7f\xads2\x8f\xa3\xb0$\xec\x935\x05\x1b)\xd6\xe3[s^\xde%\xf8\xb2\xa0\x7f\xbcH\xe2\xb0 \x85s\xa1\xf6\x9ca\xcfE\x14&a\x8eu\xc9_+\x92F\xf8\xdd*\\\xaf\xe3\xf4\xda\xb9h\xe6PJ`\xb4s\xf9\xe9dS\x1f\xaa\x936\x9c\xa1\xb7\x8c^\x9a\xdf\x1e|\xb1=\x9f\xc9]\xe1\x12/Xd\xf9\xab0Z\xbau\xd3\xadvE+;\x138==\x858\x88\xd39\xf9\xf2~\xe1\x12\xcf\x83r\x99g\xb7\x90\x92[\xc8\xdd\xef~N?\xa7\xd9m\n\xd9\x1a\xa1\x9e\xf3\x1d\x8c\x80\xc0\x08\xbes .`EJ\x88S\x06\xd8c\xac\x90-X\x9d\x92\xd5\xf9\xcb\x8b\xb7?!l\x0f\xbe\xf3\xb4\x8b\xe6\x03\x05\xcaA\x19^3\xc8\x81\xbf\xe8\xe6\xd1\x99\xb1?\xee\xef!\xad\x92\x84\xbf\xe3\x1b\x8a\xaf\xc5\xdf\xf7\xf7\x83\xae\xca\xd6X\xed\x9c\xb7X\x9f\x0bl\xb3\xf9%\xb7\xda\xba\xf4`\xbd\x81\xbc\xd5\xe6\x80a\xb3\xd2Ou>\xf5\xd1\xc3j\xcd/}\xd6\xfcL\xf2y\x8b_j-\xf9\xb0bE\xa5@\xad+\x1fd8\x057\xc5\x0f\x94\xd2\xfa\x83\n\xf1\x9f\x8f\xbf`\xeb\xf4\x14R\n\xea\xe4\xf3\x96\x1a\xce\x9bq\xcd\xd2Yy1\xf0h\xd2\xa7\x9a\x9d\x97y\x9c^\xbb\xc4\xa3\x18\xb2lUzh\x1f\xa8\xca\xf3\x81\x1f\xe9\xac>\xd2\xf5\xb9\xb2\x1dm\xd0F%\x1e:\xba\xc8\x87\x85\x0f\x89\x0fk\x1f\x96\x8c\x06\x81\"x\xdd\xa6r\xe83\xaf+\xfc\xd1\\\xe1\xa6\xaepn\xaepWW\xf8`\xaep]W\xf8\xc1\\\x81\x12\x88\x94\x0b\xc8\xe1\x18n\xe8\xbf3\"N\x17A\x1a\xf8\x81\x12\xf3\xae(\xfe\xed\xc1k\xe8\x0ds\x8b\x97\xbc\xc5\x98\x9eB\xd1Z\\\xb7f\xfe\xe8\nN\xe1\xb2i\x19\xbf\x91\x7f\xe3\xa7'\xadO\xe9\xf5w#Dvx\x98\x10hz\xb8?\x94Lv]\n\xec\xb7\x96\xf4\xdd\x8a\xfe\xef&\x8b\xe70F\x90\xb9\x9aE\x17\x1e\xe5\xa0\xe0\x18Ro\x16]\xf8@\xe9\xa2kZm\x01g\x10\xba R\xc6\xc7p\x87L\x98\xe9\x0e'X\xef5\x7f\x83\xf4\x96\x0f \xfd&\xf1Y\x87\x95\xbb\xf2\xe9\xa1\xa0P\x1e\xb7\xe1g\xcf\x87\xcbYt\x01[\xa7\x90\xe0\xcdu/\xb1\xc6\xda\xf3YOW\xf2[\x17\x7f\x9dB\xa2\x81\xd5f)\xf2 bw9\xf6\xe9I\x83S\x98\xd0?\xfeHI:\xfa\xc79\x9c\xc2\x1e\xfd\xe3\x03\x9c\xc2!\xfd\xe3\x07Z\xe7\x80\xfe\xf5g8\x85]\xac\xf53\x9c\xc2\x01V\xfbH\xdfN\x0f}\xe5\xc6\x17\x9b\xdd\xce]\xe3\xed\xdc\xd3\x8b\xf9\xed\xd4\xef\x1b\xbd\x9dO\x9c'\xd7\xed\xcb\xa9\xf7n`]@b\xe38\xaa\xca\xdc\xd2\xb3\x1c;\xda\xa8\xf3\x8c\x02H\xd2>\\\x1c\xde:N\x83b\xdd\x10F\xa7\xe0\x00\xfd\"\xa5\x18\xe7\x14\x91\x0f\xef(\xf7(%\x90\x84\x11q+\x1f\x9c\xed\xbfVYy\xe2x\x88\x99\xbe\xf3|\x08a\x04\xces\xfamL\xffz\xf6\xc4\xe1d\x9b\xf3\xdc\xb1m\xeffD)\xe7\x8b\xe5\xf2\x94a \xe2\x86\x9e\x0f\xb9\x9b\x07\x1f`\x04y\xf0\x1a\xbe\x87\xd8\xed\xa4\xd2\x04\x1f\xe580+/\\:\x07\xeb\"\x11\\#\x12\x94\xd9O\xd9-\xc9_\x86\x05q\x91{$A\xb1N\xe2\x12\xbf\x0e\x12\x92^\x97Kx\x0e\xbb\xeat=\x1f\x1c\xb6\x86\x94!\xe9C\xdc}\xe8\xc9\xa9R\xc6\xac\xce\xe9\xce\x89\xbbz\x1b\xa7\xf3\xec\x96n\"\xfb+x\x1b\x96Kz\x97\xf1\xdf3\xf1\xfe\xd8\xf2yA\x92\x05\xfd\x98\xfe\xab\x7f\x8a\xef\x8eA\xc0\x01\xd7\x11\x84\xe82.\x1c\xcf\xf5z\xf0\xe05\xc7\x83\xd7\x8f\xc0\x83G\x9d\xa4\xca\xbe\x8e&\xd9\x8d;\xfa\xdfC\xaa\xd8\x89\xb8\x03\x9d\x16\xa0Kb\x90m\xc9\x1b[o0#\xa5\x91d\xe5\x7f\xf27\xed\xe5\xcc\xe9\\b\xfa\xbf\x01\xfb/\xaf^6\xf8p\xbf\xc8\xf3\xf0.\x88\x0b\xfc\xd7\xdcX:\xb8\xb1\xff\xe57E\x9e\xf2\xb0\xb3J9nN\x17\xd0\xbe\x04;\xf2\xe9nM^\xe5y\x96\xbb\xce\xcb0\xfd\xae\x04\x8a\xdd)k\xbd\xcc\xe6\x90\xa5\x00\xec\xac\x9aey\x9bB\xb0\xa6\x15E\xb4e\xb9Vt\xb5\x9a\x1e\x94\xf3\x95\xdfi\x9f\xd0\xf6\xd2\xce\xd3\x89wq\xec\x03\xb9 \x13\xcfuXq\xd3\xfee\xd9\xc7\xbf\xcc\xfb\xf8\x97\x9b>\xfe\xe5\xae\x8f\x7fi\x18\x9c?\xdb\x19\x9c\xe5\xa6\xec\x08\xe5aV}\x8c\xce\x15o\x99\xb2Ns\xc1:\xd9x\xa5.\xdee\xa9\xf1.\x8ckY#3\xa0q-W\xc8\xb5loC\x88\x8c\x05\xbb\xbc\x94\xd5\xa1,\x0b\xf2\n\xc7\x90\"3\xb3b\x8c\xc3Rc^\x9a\xd3\x8f\xb5\xcf\xb0\xb6`rh#Y\xcd\xf7\\\xd7\xdc\xc8\xe9)\xb2:\xdd\x92$\x90H\xc6F\x90d\xa7\xd2\xc5C\xaf'\x05: Dr\xecf\xda?\xa0Oq\x1b#T\n\xf3\xebjE\xd2\xb2\xe0\xb4e\xdfw\xf4\x89\xc2\x82\xc0\xf8\xb8\xb7\x1eH\x02{r\x0be{\x0b\xf5\x07[\x9el\xde\xb2K\x0c\x94\xb5\xfe`\xe3\xd3\xc74\xae\xd0\xd4\xa6\xe7\xa1\xf3m\xab1\xba\xa1\xd6/\xecm\xd5\xea\x95p\xbdN\xee\xb8\xf2\xaf\xde@s\x8b\x0f\xe6u\x11\\\x87\"!\x904!\xb2J\xa5n\xcaE\xce\xfc\xa6\x93\x9b\xcfl\xdc<~\xe6\xba\xab\xe0&\xce\xcb*L\xf0\xe25\xbf\x10\x96x\x9cW\x17\xbc\xfeG\xfa\xcd%\xfd\xdf\x16\xb2\xfc(\x0f`\xdc~\xe2yV\x8e\xfe\x1f\x85\x8b\x9f\xeab3.dk\x953\x1cu\xa8#4\x8a\xa2\x8c\xca\xc3f\xaa$X\xb06\xf7=83W\x96\xd5n\x16\xccE!H\xee\x96\x9e\x8f\xb0'\xa3gtk\x8c\xdc.jL=\x03Y\x04\xcd!\xaa\xeaf\xd5\x0d\x91 \x9f\x87V\x7f\xce5)\x1d\n\xbc\x91\xb8r\n\xf1\xcb@>\xbe\x88\"R\x14Y\xce\x08\x8a\xa2Z\xd3\xfd \xf3-\x0bA\xe1\xdc\x84IEx\xdb\xf4\xd0\x95\x0cY\xa5\x01\xbe\xf0\xfcMI\x0e\xf9\x08l\xa5\xee\xf4\xc8\xb3\xf3\xfd|\x0cO)\x9e0+~\x7f{\xe0\x8a\xcb\xf6\x82\xa2\xe6\xb6S\xa4 w\xd1\xbe\xa0\xea\xfa{A\xd8\xcc\xb3\x9f\xd8o\xe4\x1f\x9a\x1a\xb4\x8f\\\xb4\xebWS\xa3\x06u\xc8\x92K\x82j\xcb%\xda\xdd\xb3\xb0\x85\xa9\xbb7\xf5\x14dk>\xf4\x82\xc5\x0e\x16\xbcF\xecNh5\x99t\xef\xbf:\xb5\xf1\x01;b\x1b\x9f-I\xe67\xb1L\xa8\x9b0\xdf\xa2\x17\xb7}iT\x1a<\x05\xc6k\xd8\xaeL\xdf\xa0\xfb\xf8`uX\xff\x8d\n\x8dne\xba\xb2rCd\x82\x88\x9bc\x1f2\x1f*\x1fB\x1f\n3\xa8\xa4@d\xcbHc!\x03\xd0\xc6\xb9\n\x8fL\xc9T\x88\xe8\x1c\xc9-p\x18\xf76N\x99B\x8e|\x89\x08SJgQT\xe59\x99\x9f\x00\x9dd\xb9$\x90f\xe9\xceJT\x9c\x93\x1b \xe9M\x9cg)\xc5\xffH\x0e\xd3J\x8b*I\x80\xd0VaE\x8a\"\xbc&\x10\xa6s\x08\xe7sTe\x87 ,I\xb2^T \xdc\x86y\x1a\xa7\xd7E\xa0\x9f\n\xfa\x90\xa4 \x1dD*E;3}\xb1.\xcct>}(\x86\x1f\x9bi\x11W]\nR\xcb\x80\x9f\xfck\xf1\xe4\xda`\xdedz\xf8A^\xcc\x92\xd1\xe8\xc2X\xeb\xc1\xf3\xbc \x0dW(\x91}\x93\xde\x84y\x1c\xa6%\xfc)\xce\x92\x10)\x99\xd6WmJ\x8c\xdd\xb2(X\xe4\xe1\x8a\x14\x9f\xb2\x0f\xd9\x9aQ\x1a\xd1\x1f\xcc\x1f\x0e\x82\x01}\x16!OM\x9c\xae\xa4\xac\xeeW\xec\x0b\xb6bvaa\xa3\xd8\xa5\x8eS\xca8\x90`]\x15K7\xed\x10V\xab\xb35_\xacD\x9d\nW\xf2\xca@.\x0b\xe2tI\xf2\x98\x83\xed\xdd}O\xfd\x84\xb1\xe8\x93C\x1d\x03p\x1e}\xf2\xd4\xd8\x16e\xbf*\xe9M=?\xdaK\xec\x86\x0d\x91\xeb\xf9x\x0b\xc7'\x10\xc13\x10\x1c\xd0 D\xa3\x91\xbe\x88\xe2\xc8\x17\xb3H[\xc2\xa4io\xb6`\xcc\xb1Vt\n\xa1R \xa3\xc2f\x94|\xff \xb1\x80\xf9\x16\x8b\x97x\x9e\xccY\xd0\xef\xd4\x91U\x1c\xfb\"\x9b@\x89\xbbP/@\xa9\xec\x16\xb3,(\x83\x9c\x84\xf3\xf0*a@\x98\x1bi\xf0\x92S\xd8\x9a\xb4\xea\xdf\xe6q\xa9\xd6\xafKD}Z\x18&Iv\xfb\xefa\xb2x\xbf&)7\xbdS\x1bRk\xd4\xad\xb5>\xac\x9b\xcc\xd2\x88\xb8\x0eA\x83\xa8u\xf7r\xae[P\xc3\xd0\xf6\xfd=+\xbd\x14\x138/\xc3\x92\x04$\x9d\x13\xb4\xd6\xc9\x83\x94|)?\xc5\xd1gw\xc9\x86\xd0\xdd\xe9\xb2\xbd\x87%m\xcd5\x89\xf2\xccTb\"\xf3b\x8e\x18\xd7\xbf\xc7\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\x16 \xa9\x18\x06j\x86\x83\xfd\xa4\xa5$\xd5\xd4\x17b)w\xab\xde\xfdfB\x9e?h*sR\x94yvG\xe6\xad\xe1\x0f\x1e\xa2$\xcea\xa3\x15\xe7\x14G\xab |\x0c\xf3i\x8e\x98\xfaeP\x8f\x8d\xd60-D]Acu4a\xa12\x113@\xfe\xfd\xa7\xd0X\x9f\xd9&A\xabx\x1d\xdb)m\\p\xc9\xbf\xea\xa3\xfc\xb1C\x86?\xaa$\x11\x17\x16\xcf\xbe/\xdf#\xe2\xcb}\x7f\x13499\xda\xb3\xea\x8a\xec\xbb!\x8e=\xaetN\xd7\xb56\n\xeb\xa3\x8a7\x1c\xdf\xde\xc1\x9e\x01\x8f\xbf\x0d\xcbe\xb0\n\xbfv\xeds7\xde|\x02\xd2\x80\xcc\xe3\xd9\xb73\x88LZ2\x90\xb5\xfb\x87a\x10\xa7\x87\x1b/\xf0\xdf\x85A\x1c64!\xaci+\xc1J8\x93\xee\xa0\xcd\x19\xe3\xdb\x8f\xa8S\xc8\xb5\xb5U\xba\x1d\xf2-\xebg\x9a\x85\xeec\xf7\xdeb\xaeg\x16$\xee\xeb\x06\x96\x8c\x90>:\xf4\\\xa7\xc8#\xdd\xd4\x81\x92\xd3\xb5\xd0\xb6\xcc\x98\x1dI[\xfd\xe5:\x0e\x8c \xf4\xb8=\x8a#j\xca'\x06-\x08\x838-\xd6$*\xcf\xb3*\x8f\xc8\x90C \x08S\xe9f\xf96K \xc1\xa5\x87&\x12=\xb2Y`\xa4\xea\xa9\x8e\x10\x7ffn\xea\x83CYB\x07\xf5@q\xf3\x9b\x1e \x8a\xbc\xe8\xadm\x8c\x97\xa4\xcf\xaa\xe6\x8b\x8a\xd7;\x03\\\xa1\x92i\xb1\x8a\xe0\xd7,N\xdd\xda\xda\xd7\xc3\xf6\x90\xe2\xcd\xe1\xac\x86\x07p\x0c\xa1\xf8\xa9\x94\xc6\xcd\x818\x06wN\x12R\x12|\xefK\xaf\x14K\x8fF\xf2.\xd3[\xf56u0\xd2\xe2.\x1a\xef\x19e;894\xab\x90\xc1\x91\xf8\x08\xb9\xffot\x0d\x7fo\xc0\xb01\xd66_\xbd\x03\x93\xa2\xd9M\xdd\x83\x03\xcf\xc7\xf7\xe3\x86 \xb69\x98\x18\xaf\xe9\xe4@7\xf3\x0b\x8d\xaeT\x9f\xc9\x9d\xd9\xff''\x0b\xf3\x8b\xcb\xcb\x82$\xf6wx]\x8f[ \xcb\xe4%VX\xb7M&[\x83\x9c,\xa4\xcdh7\x13\x0dk\xe63\xb9\xd3\xf6\x14$\x96\xbc\x0d\x1ar!\x962\xc2\x88\xb6\xbc\x92>\xff\xf2/\xec\xf8\x1cC\xd5^\x1c\xfa\xea\x18\xca\xf6\x0b\xdc\x03\x83v\x1b\xb7 m\x97\xaf\xf3l]\x1cChX\xff\xec6%\xf917j\x12\x8f\xd9\xfbI\xb2]\x91\xc4\x1cA\x94\x93\xb0$\xaf\x12\xb2bn\x15}\x94 \x9e\xf1\xda\x17\xa25\xa2\x84\x9e\xc6*I\x0c\xb3\xe0o\xd4\xc1QZ\x83\xdfNY\xdc/\x1e\x14\xc3\xe4\x10\xd3\xc3CP\x03\xef\xae\xb9\xef\xc7\xc2\xf3!\x12\x85 3\x98\x1c\x01\xa1\xfb\xee\xf9 \x8bM\x03v\x84\x05\x1c8\xaeK\xda\xd5\x18\xf2Q+b\x19\x02\xa5\x8c\x810\xe6\xbb\xb7\xbd\x0d[\xa1v5]V\xeeV\xcc\x93\x11\xfd\x1fOZ\xcb\xb7\x84S\xd05\xe8\xb0\x03\xd3\xf6\xca0Y\xc7\xd2\x83*\x88\x96q2\xcfQ\xa4\xa1\xa1%\x94\xb9\xd2\xdaKx\x0e\x13\x13YQ\x0b\xb3\xe6\xc2\xac\xcd]\xd25bb\xac\x1bx\x06\xcb\x13\xb8\x19\x8d<\x98\xcfn.\xe4\xd1\xcdn`\x04S\x83\xfco\xec\xabc\x9a\xab'\xb05\x13\xee\x15\xc8=q\xe8z\xb5\x84\xe4\xc0\x97\x07\x8dO\x94\x9a\x16\xf1#\x9e\x8b;O\xdeD\\xi\x07\xee\xe8\x0et\x0cM\x08\x80\xe9ig\xee\x03c\xfc/\x0eP\x8a\x9e\x96\x14g7\x17\xc7\xaf/\xcc\xeb0*\xb3\xfcn\x90G\xa4v\xc9\x82\xab8\x9d\xbb\xdc\x07\xc9L8\x93@(\xd75/\xc5E\x10%YJ^\xa4\xf3\x8fL\xdc\xfd\x1f\xa4\x97\xb9n\xe6\x18p%\xbd\xcf\xa0,\xfd\x87\xdf\x03\xfa\x07?\xe7e\xc0\xa0\x8a\xcf4\xfb\xebB\x9f?\x1d\xc0f\xf0\xa2\xaa\x0d\x9brTd\x8a\x86\xdb@\x02m\x9b\xe8\x15n\xbfB\xc1\x03\x0e\xbb}j(\x12\xed\x9a\x8b\xb79\xd0\xa9\x14\xa03\x17@\x87\xdd\x9a\xfax\xc80h\xa9\xc3 \xb6\xde\xec\xe0#\x1e\x97\xcft\x0d\xb6\x0c\xef<\x0d\xdaT\x16h\xc3\xca\x15\x15\x11%\xb6T9P\x02g\xb0\xa6\xc5\xa7\x90\xd0\x7f\x8e\xc5/Z\xd7\x00\x9d\xee6\x84Nw\x1e\xac\x87@\xa7\xbb^\xe8t]C'\xbaz+\x06\x9dV\xf0\x0c\xeeN`E\xa1\xd3\xf5l\xa5B\xa7\x95\x05:)\x03\xba\x1et\xff\xf9\xddX\xfa0\x17@\xe0F\x95\x13\xd3\xc3\x1f\x17\x7f\n\x93xn:\xfe\x9bP\xa4\x8a\xbc\x88\x1d\x10AJ00&\xf7\xaa\x10\xc0\x7f\x80~\xe2T\xd2\x0e\x1f\x98Y\xc0\xdd\x83~\xa9@\x87\xb3\x03c%\xcc\xa0+wS\x8f\"P8\xe6\x87\xb0\x99\x8aq\xec\xfa\xc09%\xa6\xab\x8a\x8d\x04ef\x10\xd3\x0b\xc3R\xae!-H\xf9)^\x91\xac*a\x192\xb1\xc5\x15!\xdcK\x97\xcc\x9dn\x91|\xd5\xdfA\x94\x900\xff\x8a.B\xb3\xfc%\xc5s\xd0\x8c\xbe\xd6\xda4Et\xf9\xc6\x06\xc8\xc6\xbf\xcd(\xd3\xb5\x95\"\x880\xb4C\xf7\xb1)\xf6{\xda\xed\x94r\xa4\xec\x0b\xf5\x9a 9\x87\xd1\xa7\xd5\xdc\x1c\xb4l@8\x92l\xb5\x0e\xbd=\xb4\xdb\xe2\n,s[\x16\x10\xf1\xb0eg\x7f\xcdsHm\xb2\x04\xe9 \x9e\xc9?Z\xc4{\xa7\x80(\xad=\x18\xea\xfa\x03\x06\x95\xdb\x06\xa5\x1c\xde3\xf5\xe7\xb1\x04\x85\xa0w`\xb4\x8b\xca\xb6\x8a\xae\xa6\xa2-\x98\nu\xa6i\xfe\xd1\xfeV\xd3@Q\x0c\xb931]\xfe\xb6\x8e\x8e\xf9? J\xe4M\xd5\xeaY:9z\xe0\x83(K\xa3\xb0t#\xb4/\xc4\xb6}\x88D\xa5\xedmX\xba^\x9f\x96\xcet]\xb7\x166j\x96\"\x89\xd0]\x1b\xd4\xe28F\x83uC\x8d\x0f)\x01\x18\xd5\xfaerb;\xe7\xf8\x01\x85\x92\x91X\xd7\x13\x18\x8d\x12x\x86\xdf\xe0\x82\x14\xb3\xe4\"\xc8\xab\xd4\xb5X\xbc\x8a\xa5\x90\xbb\xec\xb9%\xc0%|\xec\x8e\x9a\xf6N\x865\xbc\x92\x0b[Jk\xbd\x1d\xdeP\x85 \x90\xf1d\xc6F\xe9\xa9\x95_\xf8\xc3\xbb\xb1\x830\xf1\xe4n\xd9\x864\xe2\xe9\x87^\xe2\xe9\xef\x08d\xb5\x83\x0c7\xed\xdd\xc3FC\x80V\x07\xc2\x1a\xa0\xbb\x03\xfb\xec\x8do\x1e\xf4\x05{\xe8\xbc\x89s\xbb*qQ\xa5\x92&3\xa44%%x;\x9b\xbbq\x15\x8b\xd3\xb8\xd6:\x0e\xe2\xf1(E\xc0hW\x03\xed<1`\xe9V5J\x1d\xdba\x01\x9d\xcf\xe4\x04Rx\xd6\"\xceO \xa5\xc41\x99\xa5\xb4+\x95@N5\xe28\xe2ZVr+\x96\xcf\xf3a\x82th\x0d\x05\xef\xef\x01\xa3s\x84\xeeR\xa1~\xe7\x92D2\xaf:=\xa6\xc4&p\x9bs)\xde\x06\xee\x85\xd2l\x1c\x94q\x89\xd6\x1f\xceU\x9e\xdd\x16$wh!\xff\xbb\x89\xba\x94\xde\xf0\xf0\x1bq\x10\xe6\xd77\x0c\x7f@\x1cp\xbbAd\xbe\xa4\xdfE]\x1b\xdf\xdd\xe0w\xf3\xf9OqQ\x92\x14\xdb\xbda/Q\xd9\xc0\xfe^,\xc4\x9f9Ye7D\xaf\xccJ_$\x89xQ\x887d\x15\x97\xe2\xefuN\xd6$m\xf5\xc4\x8b\xdf\xa7Q\xab\xddDj\xae\x97\xa1\x98]\xa8\xabw\x15\xa7\xf38\xbd\xeeVR\xe9T\xeb:\xcf\"R\x14\xf5\xc7\xb1f%\xedh[\x14\xdd\xce\x07x\xc89O\x1c\xed\xb3\xe5\x0f\x18\xd9&\\\x88\x91R\xe22y&\xc8\x81\xb3\xe1\xbd\xf9\xd3\xab\xcb7\xef^\xbfy\xf7\xe6\xd3_\xb0\xc6\x04\x9e\xd8V\x9a|)I\xda\x8a\x8bh[\x02\xa6\x9dk\xd3Q6\xf9-.\x0d[:7S-\x9f]\xe2y\x0d\xed\x04\xcf o\xd6\xae\x9c\xc5\x94\xc5\x9e\xa5\x17LD\x1a_|\xfb+$J%9\x9d\xd9]\xa5\x15\xd4\x8fYj\x8c=\xd35\xac:5v\x063n1\x95 N\xa3\xa4\x9a\x93\xa1\xa1\xcb(\xa7_\xf7\xa5\xbc~\xe0\xc6\x0fC[2D@3\x8c_<\x84\x85\xc7C\xe5.\xfdk{[\x84\xc6ce\xf8\xe7\xf66\xe4\xc2\x12\xbd\xd5\n\x1d_\xca\xde\xea\x9c\x06\xbeY\xc4IIr\xb7\xf3-IN(\x11\x17\xa2\x17\n\xfb\x06\xc11z\x0d, \xd4\xe3\xa740d\x0b\x08\xa1\x88\x96d\x15\x06\xf0F\xbcb\xf1\x0d)>\xc8\x16PT\xd1\x12[(Z\xc4a\xe0\x18\x8e\xe3\x12C\x1b\xae\xd6qB\xe6o\x9a\x95\xab8\x0b\xeb\x88\x018>\xcc.\xf4\x0f^}i\x7f \xd6\xd3\xf8\x01E\xcco\xc3u\x17E\nB0\xc4n\x90\xd1\xae\x80>l\xb1\x8e\x8dZv|\xcf\xc3j\xdak\xf0`\x9b\xf6\n\x8b0I\xae\xc2\xe8s+V.}d\x89{\xfdA\x07\xce\x17O:cW\xf1b\x86\xd7\x94\xf9P\x8a\x9e\x9a2C\x0c\xc3vw\x14\x90\x97\x0c\x90\x13\x83Z\xea\x04J\x86\xf9J\x0e\xbd\x1b\xc6W\n\xaf\xa8k\xff@\x12\x0d\xab\xe7\xc55\x9e\x16\xcb\x99\x90/\xb7\xf8+\x0c~|\xf5\xfa\xc5\xcf?}\xaa\xe5b\xa1`\x19:N\x848\x0d\xea07\xf1\xb5\xef\xf2\x80G\x01\xa4\x18\x97\xb6\x8e\xb3\xb1AyF\x9f\xab\x9c\x84\x9f\xdb\xaf\xba\x9c\xe1K\xada\xbd\xab\xc9f]q}\xa8\xa5/\x19\xc8\xfc9\xcf\xd2k`\x9e\x81\x08AD\x97x~\xce\x194\xe1\xbbP\xb3v]F\x01\xcc^\x81\x02vN\x0c\xd6N\xceM \xf3\xe5\x0b\xc8\x0d\xc9\xefz\x80\xa7\xc0\xb3\xb2\x1bN\xa8\x01*\x0dn\x9e\xd7\x916\x05XDn\x88\x83\xc6\x02\xdc,\xa7\x802N\xaf\x13\xc2g\xc8Mq=\xca\xa0\x95a\x9c\n\x98\xab\xbcm\xf9\xec!wA\x1e=\x8dl\xd3i\xd4\x81B\xb59P\xb8i\x9b\x81\xf4\xae5~q\x8f\xc9-\x84\xae\x01o1\xf4id\x89\x05\x1c?\xd6\x1d\xd3\x14\x11\x83\xcc\xa4\xb1M\x1bj\xab\xf8\xdb \xcaP2Ho\x05\xc6\xe4\x81Om\x16\xe9\x83}\xf9j\xcdl\xe9C\xac\x83\xad^},s\xee\x16\x06\xa1\x9b\xb2\xaf\x9a\x0e\xce\x0b\x8a$\x8e\x88{\xe8\xc3\xce\xa4o(\xdd\x0e\xf5{\xbb\xff+\x1d\xea\x87-\xeb?\x80\xd5\xf9\xb7:\xf7\xfb&?U\xe6\xdf\x12\xa7\x8f\xa3\xec\xb3\x9eC:@/+\xb7=\\7+\xf5\xf1\xa3&F\x1d4z\xfaQ\xcf\xd8\x91\x86\xda\xb8a\xfcJj\x19\xc3\xc1\xc8\xb21\xac`\xeaO8\xdc\x0e\xeeR\x81\x9e]G\xe6C\x1e\xaf\xe22\xbe\x19\xbcL*\xa1i\x04\x1d\xf8\xc2p\xbdX\xfc\xc5\xf6\x05a\xe5\xed#\xaeS\xb2FPW-\x16x\xe9\xcb\xfaG]\xed\xc1\xab\xddaR\xf7\xe0\xd0\x0b\xd8{\xb3@es\x0b\x06\x03\xe9\x8e\x1b(9-s=\x80\x08\x06\xf6\x97\x17o\x7fz%\xc2\xae9u\x82\xaa\xb0\xc8d\xdb\xc3U\x98\x7f\xe6\xa6?\xf8\x93\xc7V;mb%\xd1\xfat\xcd\xdc\x8a\xa7`be\x1ef\xb0p\x9bF\xcex\x02\x8c\xba\xa4\xc6b,\xf7\xa4\xe3\xf9\xf5\x90\xd7e\x95\x93\xf32\x8c>\x7f\xcaCth\xb4\xbc\x11\x86\x9cK9\x01X\x86q\x88\xb1\xac\xa05\xd1EYXhy\xbc\x8c\x0eY\xb2\xf6\xaa\xff\xca;,\x9c\xd8 \xe4HZ\xb9\xd5\xf2&W_\x8a\xb9\x0e\xa3U\xea}\x1a\x81s\x0c\x8e\x91f!h%\xd1\xb7 >l1\x07\x9dz\x1f(\x85C\x9a|$\xa6\xed\xd0s\x0b\xca\x94\xd6\xa0\x84\n\xbd\xf6\x026\xf7\x1d\x96\xcdK]\x95Z\x08>K\xdd\xe9x\xeaiV\xf7B\x01\x8a\xef\xf7w'\xe8\x88\xbe\xbf\xdb\xaa\xd7\xc8\xcb\xb1\xde.\xaf\xb7\xc7\xff\xdd\xe7\xff\x1ex\x92\xc5\xcbc\xc5\x9dv/\xc66(S\xcc\xda\xdc lCip,\xd4\xcc\xd6\xdc\xa9\xa5\x9ed\x00\xe7\xeeY\xeap3;Mm\xa0\xdd\x85!ru\xcd\xc4.\x17\x82\xcf\xb8\xa3Q\n#\xc8\xbd\xe6\x00\xef\x1e<>\xae\xce\xe3\x03\xfapV\xea\x11a\x89$%\x8a\x1e\xc4\x84\x87\xf7oE\x1f\xcax\xb9\xce\xb0n\x10=\x99\x05\x8c\xfdg\xf4\xe4\xea\x9bDO6\xdd\x8f\xbfOPa\xd3H\xf0ZF$N,7v\x91dY\xde7:\xcb\xd0\xe2\xe2]\xf8\x0e\x15\xce#\x14#\x8c\xe1\x18\\\xa1\xc1\xc81OZ\xbfD\xc1.\xaa\xe9\x0f\x10\xdcw@\xd5\x10\xb4|\xd4\x9a @X+\x18\xad\xb7\xba\xcc\x13xs\xf5h\xac\xe6_R\xe5\xb2!\x05\xdb\xf27\xfa\x18D\xd7]\xa6\x0b\xad1\xf4\xe4Nh\x0f\xc3\x1a\x9b\xdf6\x92\xdd\xe1#Ah\xb0\xe1`\x14E\xaf\xfc\x0c\x90N\xd6\x9dw0\x0e\"\x9b\x00\xb1\xa6\x12\xd8\x04\x1f\x0e\xbb.qoB\x99\xded2\x8f\x0dTf\x8f\xaefQ\xdaO\xc6\xbd\xb7\xce\x02\x0d\x1e\x15\xd6\xae\x8f^l\x85\xfc\xe2\xf2Z}\xf0\x0c+\xb62\x06VbNm\x19m\xea>\x16\xbe\xdc\xf0\xa8:\xa1k\xa4\xd7\xb0\xed\xca\x87\xc2\xe7\x99\xf0\x0c\x95(\x1e\x8efcC\x00\xe9\x04\xdf\xe8&G\xd9\xb0\xcc{\x1d\x9a/2+.\xba4\x9fZu\x83q\x80\xcf\x8c\x12xv\xbf\x96\xc5(\"\xcf\x98\x07\x00S\x1c\x17|X y\xc0\xe41\xf2\xab\xc2\x87)\x93\xb5\x9eu\xe3BhF\x96\xd4\xf8\x90q\x80\xfa@\xa0/\x16\xa9\xb1\x1d}6}\xc7Xn\x98\x91U\xbf=\x18\x15\xd0\x8f\xbf\x04\xc3.\x9f\xa2\xeb5y\xf01\xedo\x13p\xfd# \xa3\x92\x07L\xff?\x0e\xcf\x84\xec\x9c\xc0M\\\xc4%,\xcbr}\xfc\xe4\xc9\"\x8c\xc8U\x96}\x0e\xae\xe3rY]\x05q\xf6$\xa7\xdf=\x99gQ\xf1\x04?\xde\x99\x93(\x9b\x93>\x81\x9c\x999\xe6\xa3\x91\xc7,\xd5\x9d\xed0\xbf.f\x17X\x8f\xa4\xb4\x89\x9f?\xbey\x99\xad\xd6YJRY\xaf\x96\xc3\x08&\xba\xf2\x8c\xb5\xa1\x06\x7f\x17\xa2\x89,\x1f\x1e9\xbe\x89\x1a_\xf4\x87\x8b?i]\xff\x18\xe4\x10\xee\xba\xaa\x8e\xc1\xf4\xb83\xfa\xba\x0fq;\xacz\xdcs\xea\x06\x9d\x1b\x89\x82\xb2q4\x8f`\xe5\xebb\xf1I\x87\xf7\xcc <\xac^\xb8?\xb4\xff\x12\xeb,\xb7&\xc1\xb78(\x97a\xf9\x11[+\x98\xd8E)z\x1d&\x05Z>\xba\x18H[y\xf7)\xaf\xf8\xab\xb1\xfe\x8a+\x17r\x11\xcfW\xfdn\x19w\x9a\x8f\x88\xb9)\xf9\xf6\xb46^\xf0\x03>\x04\xa5\x9a\xfdO\xe0\x94\x1f\x94\x8d6P\x94v(\xa5\x9e|\xbf\xa5n\xd7\xf7\xf0iI\xe0\x8a 7W\xd9\xbcJ\x08,\xf2l\x05i6'\xc1\xaf\x85__D\xee\xf4\x1ah\xdf\xeb\xcd\xfd[X\x95\xcb,\x07\x80\xd7$\xcf\x8a\x02^\\e\xd5\xe7e8\x8f\x7f%Kx\xb6\xc0\xc2\x7fc\xff\x04Y~\xfd\x1c\x9e \x88\xd4\x94\xb5\x1a\x15\xf6H\x8aA\x12{\xf9\xa4uu\xb9\x1c\xaa\xc5?CC\\\xb4\xb2\xe4A\x93X\x0f\xef\x94\xf2\xb2\xbe\x10\xed\x98+\xd0le\x11|\xfa\xcb\x87W?^\xbe\xf8\xf8\xf1\xc5_.\xcf\x7f\xfe\xf0\xe1\xfd\xc7Op\x06\xd3\xc9\xde\xd3\xbd\xc3\xdd\x83\xbd\xa7p\x0c\x93\xf1\xd3\xdd\xa7{\x93\xc3\xa9\x96\xef\xd6\xd2ah\xc5\x95\x94\xe2\xa4\xc3yF_7\x86\x17\x1f\xc3\xf4Z\xf0\xc9\x14(%\xf1\x1cI\xd190Os\x865:\xcc+l\xb3p\x85\xbd\xd3\xcfqZ\x1e\nCc/\xb8\xbcDl\x7fy\x89!,\x1a\xf9\xea\xb1b*\x82l7o\x00}\x9c\xe8a\xe7\x18\x8c\xe5\xb8\xd3\xa1\x85y=\n\x1b\xc5\x06\xc2\x88\xcb5O\x80\x07\xc4\x97\x95 \x85\x9an\xa0i\xba\xbd6H\xde\x1b\x14\x0d6\x12\x0b\xeb\xb7\x15\x10\xcaN\x89MZ0\x1c\xc9=\x9d\x8b\xda,\xb9\\\x12\xe6\x86\xb2\x88\xf3\xa2\xac\x11?\xac\xaa\x02\xedgB(Z\xd1j\xe5G\x10A\xf6x\x08\x0f\xb63\x105\x01i\x0cr\x1c\xcb\xd6Db\xfd,\x0c\xaae\x0d\x89\xd9l\xe8;!\xb5Q\xe7\xcdm\x87BnR\xdf\x91~\xda\x9c\x89\x16\xcf-W\xe5lo\x03\x91\xcf\x83\xfc\xae\x1dK\xbb\x83\xedFW\xbf\xe0\xea\xae$?\xe1\x89\xf6\xd1\x0co\x0c\x98\xeb\xba)\x86g\x8d4K\xbf\xaa\xdfe\x8bEA\xca\xef\xe8\x11\xc8*4G\xbf\xca\xaat^\xd8vW\xef\x936\x0e#p1\xf7\xf0\xd8\xb3\xf6\xc3\xee\xdc\xf0~0\x00A#cI\xa5\x00n\xa7<\xf0o\x0b(\xd4F.\xd6*x\x81\x8fM\xc5t\x99\xcd#\xe9\x04L\xa4\x0b\x10\xd1\nk\x06H;\xaf\x8a\xc1\xd0O\xd9\xfdc\x93R\xb1\xc5\xd8tx \x1a>\xc7\x05\xad\xf3\xc9\xdf\xdf3\xe7P\xa7*\x17\x87][\xbfU\x04q\xf1\x8a\xc3\x0d7\xb58`\x7f\xe7\x08\xd0\xe2H`\x83!\x056\x94\x1a\xf6\x98n\x12H\xf8t\x0c\xf70g\x1bg\xf6\xd7\x02\x8e\\]\x16T\xa8d\x86\x8e\xb7y\\\x12\xd7\x02U\xd9'u\x96\x02\x97\xf9\x042#\xfc\xb1\x0f\xb1\xf7\xe8\xed\xf2\xfaL\x1f\xc5C\xd7\xb2\xa8\x15\xba\x141uH\xb3j\xd5\x08\xdc\xc3\xd2%\xc2\xe7\xc9\x166c\x08\x906\x9a]Iu\x82\xb8\xf8SLX\xda\xfdv\xb1\xc9\"L\xaa%\x8f\xb4!0\xdb\xa3\xad\xa9\x99-\xd5R\x0e\x11\x1dK\x1caX\xe2\x9b:\xd9f\xd7*pj\xb3\x1eIW(\xc2\x1c\xc3\xfb\x9d\x9cx\xb5\xa2\xcf\x8a Q\xbd\xe5\x84E\x14\xc7\x8eY\xc9\xc5j$a\x19\xa7\x93\xce*Wq\x1a\xe6w\x96* )w\xcd\xe8\x845\x82d^W/U\xb9\xd8\xe9\xac\xc1\x08\xed\xdeQ\xfc\xec\x96\x9eu\xc1\xa1\xe9.*\xa6\xdd\xe3\x89\x8a\x9d\x9e\x1a\xe5br\x90\x90\xbe:;\x1d\x95\xa0\x19\xf7\x14\xbe\xef^\xc1%\xf9\xd2\xdfJ\n\xcf\x9f?\x07\x83?\x114\xdb\x19\x16\xe4`\xaf\xbf\xa9\x1f\xfa\x16\xb2\xd37\x1c\xa0v\x0c\x19\xba1\xc0\x990\x96\xac\x86Ph\xf6SvK\xf2\x97aA0\x03\x19F\xa1k}\xaa\xebR\xcd\xe0\xeb\xa6\x8bc\x11w\xab\x9c\x11\x03\xec\xe7F\x14\x14\xfd\xf9\x02 \xe6\x83:\xbd\x93\x98*\x8b\xfe\xb8\x01\x01eM1\xf2\x05\xdb1l\xa3E\xdc\x92R\xee\x10\x85\x81\xdc?\x0eyNx.K\xe4\xce\xf0\x8d\"\xa2\xa3\xd8}\xa7.9D\x90F+Ie\x1ekp\x94\xfa\xdcB\x82\x852\xc6j1G\xce\xa5\x1ccQ\x88\x04D\xa5\xfa\xe5\x08i\xfd\x94\"\xc0\xb2#\x88\x82\x98e\xdc\xb9\x0e\xc0C\xe0\xc8]\xb7OF\x13\xf6h\\\x99\xc2J\x91\x86}\xda\x99\xc01\\k'\xcarB\x8c\xc2'\xde0\x81m\xa4u|\x8b\x9c\xc1\x86t\x1b\xf1\x85d\x10\xcac\xee\xc0\x19\x1e\x86\xae*\x8d\xe5\x0f\xe7Z\x8d\x95\x93\xb0(\xdfX>\xc0\xb9c\x12%\xfb\xec\x8d\xbc\xcbM\x98\xd4\x84\xbd`WD\xa0\x8a\x9c\x93W\xadP\x14\xe6\x1b\xad\xaf\xbf\x05\x98d,5\x8b%\xbc_(\x1d\\s\x8dB\xa2\x82\xcd[,\xa5\x16`\"\x05\x86\xd1\x18\xffM!\x01'\x04s\x0d\x8c\"=\xc4\x91\x1b\x17Za\x01\xc7ej\xd1\x8eTf\x95\x17\xc4,*\x91\xa0\xd8\xa7L\x18\xd8\xfc\xee\xbdWt\xa5\xa6>\x84\xf0\x04\xff-\xf8\xbf)\xfek\xb8o\xad\"M0k\x1b(\x1f\x06\x0b\x17U\x89\x8c]\xc7<{\xee\xcfo\xd2rr\xf0\xc3+\x97\xc0\xf7r\xb6\x11\xf1\x98\xef\xb9\xd5&H85\xda&\x8d4\x1d\xaaaN \x83g\x10\x9e@6\x1a\x99\x992\xe0\x9d\xe1\xf42\x0f\xc7\x1fQ\xf0\xc1C_-8\x1c\xce`\x07\x16\x9dr\x1d\xd1R\xfd\xa1\x88\xd2\x9dy>\xfb\x1cF|\x81\x8az\xdf\x16tA\xacMr \xbb\xc3\xc2\xd7\xb2\x163\xd89\xe5\xa3\xf1\xf9*X\x80\xb3}mR\x18A\x01\xcf!\xac1I\x08;P\xe08\xf9\xaa=Gf.\xdb\xd9\xe9\x9arM<'<\x88\xed\x9a\xf1\x80kx\x06\xc5 \xac\xbb\x16\x1d\x94\x85\x87\x11\xac=\x16\xa4\x97.\xfe\xbaw\xa5\x81\x9b\xc0\x98\xfc\xbb\xf5\x07\xe3\xeft\xd62\xcbq\x80\x0f1\xa9\xb7+3\xd6\xb3j@vt7k3\xe0[\xf5h\x07\xe8\x061o1J!\xdc\xdf\x9b\xf8\x18\xa1\x04\x97\x90\xb6\x81\xe2\xcd\x05-\xc3\x9b\xa3\x90\xe79\xc4x\x0chqLq\x01\xfea\xee!\xeb\x85\x9d\x19\xfc+L)/7\xb68r\x0bu\xe2\x92|\xe9P=\xe5\xf0\x1c2x\x02\xd3zh\xf8\xabK\xfeP\xb1\xb3W\xb1h\x87\xa3Q\xd5\x05>(\x9aX\x87yA\xde\xa4\xa5K\x82\xa2\xba*\xca\xdc\xa5|B\xe5\xc3\xd4\xf3ar\xd0!7g\xd4\x9a$(\xac\xccu\xcb\x19\xbdi\x98\x8a&\x1c\x00\xf4Dc\x83\x0e\xcde\xcf\xa1\xe1\x8d\xfd\xd5\xfd\x19s\nK\xc7\xc2C\x95\\\xdb\xa0\xd3\xd6\xd3\xd5\xd0\x9e\xec\x06\x03u\x9b\xb2\x11\xd2\xecB 8Q\xb3\xf2L\"\xc6\xb3\xed3\xc1Q\x19D<\xe4\xc4\x8b\xd2M{$\xfam\xc0\xf7\xc0dy\x9bL\xfav\xd8\xa4\x95\xb5\x19\xd4\xf0\x97a\x0d\xff\xd5\xfda\xf3A\x9f\x0fm{\x90VC\x0e\xec\xc0\x83\x93\xf2]\x93\xaeZ}\xb0\xb6\xb7a\xcbu \xc5NS\x0f9\x02~ \x19+!\xed_\xc5\xf9M\xcaO\xc3!\xcb\x84\x93R\xb0\xb1\x7f\xe0C\xc6\xb6=\xf6\xea?m\x9a<+H~\xf8\xda\x03\xff\xaa\x8b\x9fUY\x08\xf4\xe9TXL\xf4\xd5\xa7<\xc8\x0fw%\x91<\xa2[\x85\\E\x85\xfd\x0c\x1b\xd7\x8b\xaeq\xa5RL\xa1\x9af\x1c \xb2\xc5\x10\xf3\x18\x83\x1ab\x14\xddv\x81\xcd\x8c\x85\xf8\xf0E~\x93r\x16\x1bLS\xc5\x83N$\xc6L\x89\xe2A#V\xcaJ\xef\x1e\xc1\x19\xec\xc11\xfb5\xdd\x853\xd8\xe5\xbf&G\x138\x83)\x1c\xdbD/\x08\x91a\x04 \xad\x87[|\x83\xe1Z\x8c\xf8\xc5#\x8f\x8f\x81\x05\xf6kz\xe1kS\xc9p\xf4jY%\xcdh\xb2_\xcfh2\x85{p\xc5\x9c\xe4)Vt\x8a\xd3\xf1\xdeS\xfe\xdd3\xd8\xdf\x9f\x1e\x1dP\x92\x88\x92\xb3\xfbOw\xf7v\xbdo:\xff\xbd\xc7\xcf?\xac\x7f\xedn\xb0\x1ajYhY\xa1Cm\x85\xa4%\xab\xd4%\x0b\xe9\x92\x1d\xec\xef\xef\xee\x03\x06\xf4x\x06\x93\xc9do2\x99J\xcbd\x9c\xa2\x99$\xae\x8d\xb1(_\x84\x9f\xd3\xb6w}\xbc\xc9\x18tl!\xf7\xe7.(>\xa0?\x0f|\x11\xb5x\xc1\xc4\xa8c\xd8\x86\xc9x\xba\x0b\xf7l\x1397\xb3\x7f\xb0;\x1d\xc3={\xb5\xcd\x0c\xc2\xf9w\x1e\x05T\xa3SH\xda\x10\xdf\x06\xa5\xfb)\x12A\x8c\xd8\x15 \x14\xe3\x14\xbc\xbc\xafI>C8,\xee1\xc2\x13\x85\x1b\xf5\x16 \xe9.\x1c\xc7\x0e\x18s\xb32\x10\x04\xf4\x16\x06\xd3\xdcXz\xc0`8\xba\xc9}\xa6\x9a{\xdfCD\xa5\xedEv[\xe8S\xfeE\x82\xda\xb7\xbd\xf0\x81\x04\xe7Iv[\x97t\xef\xc3\xa8l\"\xab`,\xdc.\xbbBT\xdd\xb9#S\xa0\x837\xef\xce?\xbcz\xf9\xe9\xf2\xed\x8b\xff\xef\xf2\x87\xbf|zuN\xcf\xd3\xd8&\x8b;U\x93)\x9b\xcd\x82\xcc\xe5=\xb1\x13\xed\xf9\x8cn\xa4\x88o\x92\xc9\x92\x9e=G<\xb5\x02M\xb6J\xb2\xe3\xb4\xba\x96Y\x00\xd8\x81\xa8\xb3l@8H\xf1\xf0Q\xed\xb5\xe5G\xe21\xc3\x8e\x07\x1f\xf6\xa6\x9cVZd\x99\xebY\xc5\xa1%e\xc8\x98\xa5\xe9\xf6\xb6p\xeb\xad\xcb\xdc\x89\x0f\x13OR*\xb6\x8fjg\x0c4h\xe6\xb0e\x90\x9d\xa8\xe7\xca\xf5\xe8\xc9\xfa\xfc6\xfc\xc2-\xe4P\xc5L\xcf\xd4:\xcb\x92\xf3\xf8o\x14x\x1cN\x8e\xa6\xb4\xe82\xac\xae{M\xb6\xc1\xb6\xb1\x85\xe2\x0c\xa3\x1fo&\xd8\x1e\xe0u$\xb5\x1f5\xe9\x05\x0d\x16\x98\x1dBjW\x1a\x8b2F\xe3\xb9\xa237\xd6\xf1-\xf6\x93<\x9c\xcc\xf66\xff+@{U\xc2\xf3\xb8\xa9e\x17LbF_\x99\xc3\x9c\x16\xbe\xd6\x8a)\xe0)wh7S\xa3\x9d _\x1e\x98\x1a\x01\xc1\xcef\xab\xbf\x81\xed\xa7\xf8\x02Y>D4ca\xd6$\x1bB2\xf3\xbe3\x93\x05`\xde\xd4\x0f\x161\x0b\xea\x86\xc6\x86j\xa1Tb\x00\xf0}\xa7\x05\x17\xe1\xe7\xb4\x08\x17\x83\xe3\xafX2\xb5\xe9\xcdQl\xf1-\x9a\x94\"\xac\x0cjk\xcbmb\xa1\xdd\xdf\xc3V\x19\\\x8a&\x0c\xadG\xd9j\x1d\xe6\xa4\xcf!\x1bd\xf3\xca\xdar\x03\xdb\xd7\xf4QF \xd9\x8b:\xba\xb7P\xac\xb0/\x8c\xb6&\xcc\xf0Eu\\\xee2s\x90\x15{\x8c\x0d'\xf5\xaf\x98\xc5\xa1\xcfdN\x92\x99\xd2\"k\x98Q\x86\xde\xe2t\x8b\xc3\x98\xc5\x17xD\xc9,\xbe\xe8B\"\xa9\xe0\x1cY\xff\xad\x0c$\xf2c\x97\xddZ\x89>\xccw\"\x94zh\x8e\x04g0Q\xe2\xe1Bs^\x84\xf9k\xef\x89\x11l%W\xfe\x94-\xe5\x8fy\xc2}\x06\x06\xdf\xca\x84\xe3\xbf\xc1\x1ee\x80\x8d\xc3?\xa8\x01\x88) )\x0c1\xb3\x18L'\xf8u\xe6\xd5\xc1\xd0!\xb3\xa6\xbc\xfa\xceI\xe2\xa24\x99N\xf2\xe0{\x90-\x04P\xb0YQZ\x0c\x1f\x04\x01m\xa2\xb1\x11>\x98[S\x02$\x18W\x0b!\x0ca\x10\xa4C\xaa\x8b!\x89f\xe9\x85\x95\xdd\x12r)\x05=P\xbch\x86;f>IO\x1d\xa5\x8d\xc2N\x9cW\xdc\x18\xc5\xce\x06\xca \xbc\xfa\x9d\xf6\x8f>\x153\xe6FM8g|E\xf4\xd6\x9e\xb3\x08\xcd\xb9mEg+dg\x8fS\x98\xfb\xa0Pz\x12\xfa\xdc\x1a\xab\xef\x8a\xdbp=9\xe8\xf3\x0c\x17\x0c\x0e\xc6\x8c\xea\xd2\x13\x95F=\x91l\xae\xc9GRP\x12\xbb1\x1d^UI\x19\xaf\x13BWpr\xb0s\x15\x97F\xb4\xa8(\x1a\xc6'h\xbe[\x9e\xb0\xe37\xf5\xe0\x86\xbb&\x11Jm\x8dZ\xd9KA\"\xd1e\x17M\x10\x8b\xa8.\xcb\xee\xf4\x9b.\xcb\xdeW.\xcb\xee\xf4Q\xcb\xb2\xd7Z\x96]\xcfo\x8a\xe82\xb1\x7fLZ\xb8\x0dV\xeb`\xef\x9b\xae\xd6\xe1W\xae\xd6\xc1\xde\xa3V\xeb\xb0\xb5ZO\xcd\xabu\xa0\x15O\xd9?\xfbZ\xf1.\xfbg\xef\xf1kk\x8a\x1f\xd7\xb5\xbah\x9e\xdc\xb5\xc2\x8a\xa6\xa3\x8e\xaa\xc5~\xb6\x02\x08\x9c\xc1\x0b>\x9b1\xa5\xcc\x07\x84\x87\x92\xc7\x93wh\xf2\xe9F+\xf8\x07\x8d`\x98\xcd\x99\xb0\xfa\x1a#\xdb\xf4\\\x9eO\xe3Q\xe2\x0ck\x17\xfd\xa6R\xbd\x91\xda\xd4N*D3<\x8a7\xcda\xb69Y\xc1\x10j\x15\x06Q\xac\xe2\xe1\x9d\xbf\xd8\xa4\xf3.:W<\xbc\xdd_7i\xb7\x93:\x86a\x14\xb2xx\xff\x9f7\xe9\xbf\xd7v\x18\x9a\x86_m\xd2p\x075\x0e\x83(r\x18H\x95\xc3&\x9494\xb3y;l6\xbd\xc4:4v\xd1F\xc6\xfag\x1e\xf9Rx+\x1e\x83\xcd\xbd@~J\xe6\x8e8\x02\xc7\x19j6\x0dF\x9a\xec\x81\x8b\xe4\xd9dmA\xa5T\xa0N\xfeZ\x85Iw`\x170J\x1bzd\x0b\x122\x146\x9a\x9d\x88\x87\xe3\x80\xfb{\x0e,kY\x88\xd9/\\\x9bE\x9c\x16k-xr\x17f\xb2)F\x98\xffRK\xca\xdf9p\x81\x9f\x9es\xb3\xe9\x9a\xae\xa8\xddy\x10Fr\x7f\xc9`\x15\x96\xd1\xd2}\x12\xfc6}xr-2l\x80#\"\xe3\xd6\x8d\xf1\x10\x80,\xc8L\x10\x04\xe0x\x9e\x0f\xce3No\xd4\xe1r\x9e;]\xebb\x91'\xf5\x1a\xb5\x7f\xfb\xad\xd6y<\x05\xb3\xea\x9e\xdb\x0c!\xa2v\x84/\xc8\xb1^/\xaf\xed\xb6\xb4\x17\xcc\xd6,naT\"|\xdd\x11\x03\x8bv\xef\xefQ\x80\x83/b\x1d5\x9b)>\xee\x8f\x9e\xd3\"@\xfbh\xdb|sx\xce\xc7C\xe8_\x9dnBM\xfd^\x17\x02\xad1{-\xa4\x03|H\xeb\xbf\xf2\xfa\xaf\xb8\xfe\xab\xb9|\x83\xc4{\x19\xba\x0e\xec\xd0\xd3\x83!\xcd`\x87\x1e\xa7P\x96\xe8e>T\x1e7\xdf\xc0\x00\xc8B/\x18s\x15\xacb\x99\xc24\xbb\xe3\x13H\x98!\xedh\x94\xd8%\x80\xd1,a\x12\xc0\xc5,\xe9\x94\x00f\x18\xbc,\xe1:sZ\xdb\x0e\x83\x1f!\x01\xcc\xe0\x19\x1a!\xa3\x04\xb0\x82g\x90\xd9%\x802\x94\xc2(\xc2C\"\xbbI}q\xe3\\\\J\x91%\xd7.Ao[\xf7o\xd4\xd9\x9d\x1aR\x03\x03\xaavu\"\x99\xfc\x7fmG\x93\xce\x8e\xd0C\xdf\x0c\xc7l@L\x8b\xb9Y\x93\xb8L|$\xddt\x9f\xf3_\xadVj\x0f\x14\x1d@\x99\x83\xa6\xe4,J\xf9F\xad\x9b\x8f0\xc2\xe0\xb8x\x1d\xa7\x18\x97\xc03\x04d\xe1\xae\x92,r\x81p\x8c\x10\x84\x87\x0f,P\xc7\xcc\xe7\x91t.<\x16\xc9\x11\x92,\xbd\xa6\xfc\xaa\x88Fk\x0f\xa8q\xcf\x00\x85\x18D\xea\xc1\x19\x05\xcc\xac\xd8\x08\x899\x07Ay3\xd9\x9f\x89\xd5\x1db\x94_\xdb\x18K\xa8pGO\xea\n]\xacU,98\xc9\xc1{\x9e\xd7NM\"\xe2 \xe3\xef\xf0\xafA`_r\xeeeg1\xab\xca\"\x9e\xd7A\xa9\xec\xf1I\xf2:\xae\x805^\x86\x02^U'Q\xabJo\x08\xff\xc5/\xdbJ\x0b\x94c\xde\xf2^\xd6k\x18\xdb\xc5\xfb\xbc\xdc\xa0\xcf>\x8e\x8b7y\xb5A\x93_\xab\x8a\x80\xa6\xdb\xdb\x0d\xba\xed\xe5\xb1x\x9b_6h\xf3\x1fN\xd9q>h\xf0\xbd\xdc\x14Z\xf3o\xc4I\xd9,u\x01\x98A\x13s>\xd5\xbd\xa6\x98\xc2\xb1\xdf\xf9T\x97v\xfd\xdf\xf3\xf7\xef\xfa8\n\xbe\"\xe6\x1bJ\xdb9\x06\x11\x0c\xc4\xccr\xcc\xc32<\x06\xdd\x93\x0e\xe9\xa3&oFp\x19\xe6\xb9\x88\x0d\xe6\xf7\xc3R-\xf8*\x05,\xef\xe1\x14\xf6\xc6G\x07\xb6\x90q\xbfv\xe1l!A3I\x92\x1ec\x16\xac\x98\x03\xa3\xce\x97\xd9\x8c\x992@\xa2\xc1)js\xed\x0c\xe40\x87\xde\xcf\xff\xa8S\xfc\x16\x93{3drv\x1bDw\xcb&\xf5t\xb78r\x95\xd8\xa7\xbc\xc1\xb2\xa6+\xa9,\x82\xe3\xb0\xfbG\x98\xab\x1c.F\xe61}\xd3k\xb7\x9ce\x1dS\x8f\x07M\xfdm\xd7\xd4\x15St\x8d\xf1\x90\x877f\xc3\xcbk=^\xc659\xb1m\xd7\xf2Yv\x01#\x98\xee\x1f\xc0\xf7\x90\xcf2S\x90X\xd8t.\x9f\xba\xe6\"4\x12\x13\xd4H\xb0\xd8\x18\xf6H6\x0e#\x01E\x04\xef*NK\xbb}\xc7\x08\xc9 k\xdc\xb7O\xf9]\x9c^c`\x13Lj\x00W\xe4.K\xe7\x82\xf6ak6\xd0\x0b\xf7\xa5*\x82@\xa7\xc8\xc7K!\xbes\xd8\x18\x8ca\x80\xb8\xb0D\xc4\x0f\xb1i\xb2 \xba\xa8\xf1\xe3\x9fY\x03\x03\xe9\x91\xfe\xf4\xd8t\xb6\xe615\x88$t\xb0\xc7\xc1\x9c\x93/ \x8b\x17\x06\xae\xe8\x87\x1ef\x88\xd4>\xfd\x84\xdbS\xef\xe3\x86\x9b\xf5\x92\xca\xed\xd5\xadud\xaf\x17\x1f\xa6\xaa\xe1\x0ewG\x8b/\x00\xf5\x10\xdb\x18\x94\xe7\xd938\x84\xef)\xfd{\x061\x1c\xc3\x04v \xf6<\xb4\xd16\xbc\x184\xe1\x8f\x1bMxoz\xb4wt\xf0tz\xf4\x8df\xbdg\x9f5iOk\x17\xa7\xc5\x16c\xd0\xe4\xde\x0d\xbe\x1f_s\xb0lG\xb5\x03\x9e<\xfa|\xfe\xa4\xcc\xc88\x9dZ\xaer\x7f\xcf\x16`\xec\xb3\xa5\xf6!\xe6<\xae\xdc\xc6t\x97\xbd\xa3+\xb07h\x0c?>z\x0c\x87\x961\xecO\xd9;:\x86Cm\x0c\xf2\xafB\xa7\xeb\x86\xd8\xef\x08\xaf\xb8aJ\xeaS\xf8\xaf\xff*}=\x08&\xe1\xb9O\xfe\xeb\xbf\x88\xcf0\x05\x0bC9\xa2X\xbb\xbe!\xa5\x888RR\xc4^\x17\xe5^\x13\x92\x8c\xe5\xea\x92\xbe!\xe2\x1bR\x7fC\xa4o\xca\xba\x04\x93\x1d\x1b\x03\x985:\xcf\xda\xea\x1a\xd7\xc2\x1a s#\xf9IM\x81\xc1\x8e\x9eeE3\x86\x11\xec\xec\x101\xef\x13<\xda\xe3\x9e\xe9\xd2\x0f\xbe~\xc2\x87C\x00\x02o\x90\xd4s\x9c\xf8\x9a\x82\x83o\xdc\x90\x1e'\x07\xedc5\xa8\xd3\xa9\xa5Sn\xe9\x81\x8b2\xb9@\x9c?l\x1c\xed\xcd\xfe\xbaq \xb5\xa1\x0cf\xc88v\xa7\x8f\\\x8f=}\x1c\xae}A\xe4\xa2)\x16\xb18\x7f\x93\x83\xa7O\x9fN'\x94\x8b\xa8\xdf\xef\x0e\x1c\xf6#\x97\xaf5\xec\xd6\x18.D\xe2Li\x06\x93\x83\xf6\x14\x94Y\xed^t\x8a\xf0\xe9\xb0\xff\xd7A4x~\xca?\x9fL\x0f=.\n\xdf\xe1\xb4\xe3:\xbbu)\x95\x00\xdf\x03\x06\xf3\xec\x05\x07\x7f\x0f\xf0G\x94\x85\x91`[~q\x82\xe4e\x1b\nf\x1a\x14\xcc\xbb\x17)3,Rf]\xa4l\xc0\"}#\x90\x89\xbe\xd7\xf5\x89Gu\xde\xf7\x80\x11!v\xa4{0\x11\xa9\\\x07@\xd7\x0d\x80\xab\x15\x9a\xb5\xd7\xf1F\xf8UX\x81\x8bu\xedw\xa7O\x0f\xe8$S8c\x8c\xd0x\xf2\xf4`\x0c\xf7\x90\xc2q?\x05\xb2\x01\x8c~\xf4t\xd8$\xee\x15\x10\xfe\xfbM\xe7\xdb\x81\xfa\xcd \xbd\n'i\xd9to\xd0p\x87\xad\xfe\xf0\xe1b\xcf\xedA\x0f\x00\xee}\xc3}\x9dd\xa1\x01\xba?n\xb816\xd9(\x1a\xb6\xc6\x82\xeb\x1b4\x8co\xb5j\xadaL\x86\x0e\xe3\xc7\xac\xbaJ\xc8#\x97\xe3\xb0w\x1cc\xc1\x80\x0e\x1b\xc7#\xd7\xa3\x7f\x1c\x93!\xe3@\xe6\xd9\xca\xcdX\x848<\x9d\xa7\x82\xe0\x98\x15\x0b\xaam_\xea\x06\x04:2I=\x96t\xcc\xe6\x88\x12\xdbc\xfce\x1dN\x1fx!H\x13r\xba\x14\x94D\xdaB\x93\xac*#\"N\xa1\x84'\x1039\x90\x15\xbc\xd1\xca\x9dP\xac^I#\x99\xf0w\\\xc9\x14\xabXW\xd3`\xa4$\xad\xa6\x10\x9f\xd5+\xba\xb3\x13c\x808N*\x18\x964\x16K\x9a}\xb3%m\x11\x15\xdd\x16,\x86E\xd5\xd7\x92\x02\x8b\xfd}\x1f\xf5(\xd6|?\xb8;M\x06\\\xb7\xf4\x04\xb4\x96O\x197\xf9\x1f4\x11\x13\x05\xf2\xd5s\x99\xfaLr\xdc5\x9b3\xc3\xf5\xf0\x9b=\x9b\xb0=C\x11)\xa5\xa9>(\x1dl1\x1b\xfb\x91\x166\xd2>\xc9\xc1\x94\xf2\xef8I>\x1b}\x92|\xee\x86IN6\x9a\xa4\x89Z\xf9\xeaI\xee\xf9\x92H|\xd0L\x19\xcd\"f;\xdd\x93\xa6;m\xca'\x07\x96\xbd6\x1cg\xba2\x1f\xcd\xdb\xdfI\x16I+\xf3;l\xff\xe6+cY\x95\x89eU\xa6\xe63\xb3\xdb\xbd2\x93\xc1+\xb3!\x8a\x15\xd2cyY\xb6\xac\x06G\x02\xd4\xb7\xd0\x03\x86\x8e6\xcbN[\xb8%f\xa8d\xc7\xe0\xe6m\xb6\x07C\\lF,=Qz\x1f\x89\xc1+\x19\xdd\x08\x917wJb\x7f\nsL\x86\xdb\xe9\x84.\xf0\xcb\x10C\x14\xf9\x1a\xdew)\x96\xaa\xe0\xf9s\x18S<\x1a~\x13|\xb5!\x05\xf0?e\xa3;\xa8\x88\xaf\xdal\xb1\x17\x12\x81\x915\x04\xc6\xc6;>\xfa\xfb\xec\xf8\xefB\xa0L\xa6O}\xd8\x99L\x0f7\xa7Q\x14\x1d\x12]Z\xe6\x930\xf9\x1a\xfa\xe5w$_v\xa7O\x0f\xe8\\Q\x860\x0c\xb4\xff\x8e4\xcc\xefH\xc2\x04_K{0`\xca\xdd{;\x80\xc4QH\xa2\xaf\"h~Gz\xc6\xbeD\xea\xf5U\x8c$\xc4-\x1e\xb0\x8a\xff@\xc4\x8fE\xfe\xd4\xbd\x8a?i{\xd6\xe7U\xd1\xf4\xb4\xe9~i=M\x06\xf5d\x93\"uw\xf5\xe3c&e\x13\x14m\xd4U\xef\xac\xa2l}\xb7\x19\xdd\xd2\xa4\x9b\x1c\xa3Cd\xed\"\xd8\xd8\xd5\x97\x9a\xa7\x97\x94\xa5\xa41E\x90+\xd0\x0fI\xdd\"Wq\xe45 \x88\xce\x0b\xcc\xfb\xb2/\xbdS\xdc\x8a\x84\xd2\x0cP\x1eVO\x13\xa4\xcb\xf0\xa6\x0c\xf3kR\x9e\x97a^\xf6gC\xad\xcdx\x80\x19kj\xc30\xf7PdU\x1e\x91\x0dz\xc8\xbb\xc6\xcbZ{\x95\xce\xfb\xdb\xcaU\xe7\x8bz\xf5\xd5\x1d\x95\xec\xaf\x08\xc6^\xda\x916Jy92Z\xe5\"A\xcb\xf4[\xb99n=\x12\xc8\x8d\x1b*\x06]\xe6\xcaA\xec\xb1#$M\x0c,]\xc2\xe4\x04b\x9e\xd5`g\x07\xcd\xc2b\x18\x01\x03\x92\x14\xd6\xd1_\xa6\xb8/\xb5\x93\x11eA&d\x17X\x18\xaf\xcd\xb2\xfe\xb105\x9aY\xda\x06\xfd\x1b\xf3\xb9\x14\xa4\xac\xf3\xb8\x94\x8a\xa9N\xca\xcc\x9e2\xcf\x9c\x0bS\xe8\xfd\xba\x00\xc1\"\xc6\xf4\xf6\x1b\x00\x02\x83\xd3\xd5\xc6\x99\xadEz\x02\x0c\xa9\xc1\xd1\xa6vC\x8c\xe9s%\xb8\xd0\xfe\xc4\xe7Y7\xfa2#\x81\xec\xe2$\x07,\xb7Y\x1e\xd1\x87n\xe9t\xff\xa0F\xd4\x96\xf8h\xf6|\xabz\xb2\x19C><\x9b?{\x9d\xf1{h2o\xcb\xb2c\xbfj.\xe0\xdc\xe6Ul\xf3\xfch\xf5\xc7s\x97\x98\xf2\x9d\xf3\xc5b\xa9\x92\xacF\xbf\x1cF\xca\xe0\xe7\x19\xc3\x0dj\x91\xd5*\xfa\xfd`O`\x0c\xe7\xd1\xc4\xcf\xa3\xed\x9b\xa1Tf\x1bl\xe3\xcc\xab%\xba>SF{\xcc\x93\xc8\x8d}h\"{P,gL\x0bo\x87'\x06\x8b}\x04\"L\x93a\x01\"viB\x85\xb6|r\xacB\x96Q\xf8g7\x15)\xeds)\x01\xa6\xd7\x91\xbc\x99\xb2\xdc\"N\x95\xf9\x10\xd6\x13\xe0\xb6z\xe8\xa3\xacLB\xc0\xc5j\x96\xc1\xbfB\xb8\x81\xcd^\xd9\x8a\x91\xa3\x8e\x81N\xf6op\nOf\xff9\xfa\xe5\xc9x\xe7\xe8\xc5\xce\xff\x0bw\xfe\xb6sy\xf1\xe4\xda\xe6z\xf3\xba;\x84+\xa0r\xf6\x0c\x9c1:\xfd\xabiB\x8f\xb5\x02ul\x96\x0e\x7f\xb6*\x00o\xcc\x01\xda\x08\xf0\xa88\x13x\xd2\x9b\xe3\xb2q\x90\x89Ex~S^\x87\xee\x14*1\x0bl\xd3J\xec\xe0\xc1s\x8c\xe6\xbd/P\xf4\xfe\xd3\xdd\xbd\xbd.\x80\x1b\xf3\xfcp\xf6\x1aP_\xd2\xe7\xb0\x7f\xb0;9\xea\xabL\x1f\x96\x88b\x97\x8eggB\x07\xc3\x93ILw\x8f|\x98\x1cM|\x98\x1c\x1eu\x80u\xf1DYZ\xc6ie\xce\xa5$\x1e{\xf6 \xe0c\xaf@\xa4~\xb2J\xf5\xe4\xe7\x1fi\xf4\x98\x10\xaa\xb3Jo/\xdd\xd9\x95\xf0\x98\x1c\xecN\xad)\x04\xc53lU\xfc\xdfy\xc8)\xf7\xd18\x80\x11\xa5\xebvx\n\x82g\xcf`\xc2\x0c]v\xf8l\x8c-\x88\xb4\x89\x9c\xef\x190\x1f;&o\xeeo\xca\x12U\xf4\xdd3\xd6\xe1\x84eg\xe9K\x7f\xc0\x07\x93v\xcf\x83\xef\xdft\xbc7\xb0\xf7\xe9f\xbd\xc3\xf3\xe7\x98\xcb\x00\x03lcB\x83\x94\xfe\x9a\x1e\x0e\x1a\x16\xee\xd3\xb0q\xedn>.L\xba0\x9d\xee\xb1\x10\x1ep\x00\xdbt\x848\xba\x0d\xc6\xda\x03\x1aq\x1e(\x14!\x92\xb4&V\xd2\xdar\xf6\x99p\x86\x19X(i+\x93\xab\xfbu\xd6\x7fy\x8cw\xa6\xe3t'\x13>\xb5\x07\xbfS\xb8&h\xa8\xd4}\xea\x05,\xe8|\xd3q\x19\x90/\xeb,/\x8b:\x85\xf1\xe0\xd6\xf6\x0e5\x8a:f\xc5GZ1\xa5\xd3\x9cY\x86a\xf0y\xd0\xfb\x0b\xc7<\x02\xfb\x89\x15'\xa7\xc0\xefU\xc6\x8c\xae6\xfdb{\x1b\x90\x0d8=\x95\xee\xdd\xc3f\x93\xda\xdd\xf5\\\x16\xb1\xdf\x07'\xcaIX*~m_\xb1\\\xbbOw\x8d\xeb\xb5\xfbt\xcf\xb0`\xb4|_+\xafx\xf9\x81V\x1e\xf2\xf2\xa7\x9e\xc4\x0d\xd4\x07\xbbh/\xe6\x0d\x8f\x0e\xbac\xd0}\xa6\x1c?\x03\x0f\x9f)\xa7sV\xcfk\xad\n\x0d\xa2\x84\x84\xb9\x8b\x87\x9cX\xb3q\xddt\xa7\xd4FQ\x10)\xdd|6\xbe\xf0!\x9fMt\xbb\xff?\xb4\xffRd\xc0t\x0ctWT\x89\xd0\x9c$\x04c\xfc\xc4j\xf95\xa1\x102S\x0b\x97!\xdd\xd7J-,\xb0f\xe8+{_l\xb6\xf7O\xf7,gH\xf9\\_5c\xf8\xfb\x13HwvN\xda\xf0\x17\x05\xa8n9K/p\x01\xa5\xbc\xd1\x1aU\xc9K\xa5,\x9f\xe6+\"\x8ff\xf0\x90\x1b5\x92\x88y\xdad\xc9!\xf4/\xf2\xe8\x8b\xf9\xf4\xe81k\xd8,\xdf\xe5\xe5<,\xc3\xcbK\xe3j\xe4.\xf1\xe0\x0c\xd2\x99E\xbeW\x17\x1f\x83\xb3\x0c\x8b\xa5s\x01\xc7\x90\x06\xabp\xfd\xd8\xf9\xec\x8d-\xe0s\xa2_{\x06\x0e\xf0v\x8b\xa2\x8d`f\xc6D#9\xcb\xe8G!\xe5c\xc7<\xb1\x80\xb0\xc9d\xf7\xb1\x83CP#NH\xec6\xd2N\x8aY\xf3\xaf\x18\xeb\xd3\xb1a\xa8\x9a\xa8a\xd8Hmbbz\xbaY\x0c\x01q\xea\xdbb\x1bT\x12a\x14N\xe3\xb1s\xc6\xd8\"\xaa\x04\xe8\xd8\xe8\xbd\x81\x9d\x98\x1e\xb8\x9d1=l\x1b^\x17\xa7*XB\xf3\xa8\x94:lh\xc6\xd6\xf5\xd8\"\xc1\x0d\xc9\x0b\x8a'j\x0dS]TG\x86sn\xc6\x81\xe3u\xd7\x98\xd0\x1a\xb5]\x8b\xb9\xc6!\xads\xa6,{\x1bO\xa4\xe4K\xf9)\x8e>\xab\xb1\x98;bK\x82\xd8#Q_\x96B\x97\xb6\x08\x0f\x94\x8e\xba\n\xa3\xcf\xc6\x18\x0f\xa2%[\x98\xfb\x9b&\xab$\xb4\xc3J\x9b\xbf\x11\xb1\xb7\xc2.b\x1c\xa3&\x8d{\x02\xd5\xf6$\x80\x14\x16@\x81XI\xb7+X,\xb6\xd8\x93\xdf\xb1\xddb\xbd5}\xe2\x0f\xc0k\x86D+\xe7\xfa\xcd\xac\x83x\x1e\xfa\x86\xda\x93\xdb\xf1\x9b\x0e\xb5\x95{U\x7fzG\xdb\x93\x89\xf1[\x8f\xd6\xb7ir\xc4\xd35\xe0\xde\xd8Z \xcb\xc1\xe9}b\x1ci\x88\x16|\x8a\x1c6\x137\xc1\x83lV\x8dF\x17\xf2-\x99U\x1dq3\xe1[\xac\n\x8bX\xcc\xa5\xc4}\x0bb|\xdd\xc7\xe2? U\xdc\x801 N\xcb,\xda\xee\xde\xa6,\xda\x81\x89*\xc8y\x96B\x13y\x9f\xf5\x91\x8eqJ\x81 \x99q\xae3m\x14\x13\x0f\x86\xe6*\x9by\x86\xe0L\xeb\xf7R3\xe2\xaf\x98e{\xa3\x98\x9c\xa7\x1ek\xfe\xe4 \xb8\xf4\x02L\xa1\xa5\xa2\x84\x1c\x8e\xc1\xcd\xdc\x9cN\xcb\x9734V\x9e\x0f\x99\x1b\xb3H\xb0\xd5\xd0\xccr\x88\x1aL\x8a\xaa!\x01\x88\xd3\x8cc\x04\xde\x80gD\xe3\xa6E\xa1#\x1c\x9a~M\x19b/\xee2\xc5H6\x0fO\x1c\xab\xb8\x85\x01\xf8\xc0%5.1ghKYf\xe8\x98\x9fh\x9e\x13\x1a\x7fJ\x7f\x8f\x15?\xe4f\xee\x03\xb2\xae\xfd^so\xb6\xc6\xb4)\x03\xf3\xb7\xfd\xce\x83\xcb\xa5|\xa3\x1b\x93\xbafZO\xbeH\xa9\xbbwp\xe4\xb9\xce\"\xcb_\x85\x91\x08\xa5\xf5\xa8f%\x1e\xe0H\x17?p\x1e\xe0H\xe7\x0d2\xce\x1b\xe8\x10\x8d\x891\xf6\x9e\x1eJ\x8b\xe2n\xc6\xd0\xf9\x94\xfa\xe2 \xbd\x8d+\xdb\xca\xf4\xf1\x0c\xa6\x94~5\xd8)\x94p\xc6r\x15s\xf3\x8d\xd2g\xc9N\xab$\xa1'\xbcPP\xd7\xf4\xc2W\xa4#\xa8N\x0cy\xe2!\x16g\x15#\xd5\xa6\xa8P\x16v.N\xe4\xf0\x80\x91R\x19\xa1e\xa1Zv\x8b\x01\xd9##]\xcc\x93A\x1a\x12\xa2\xaa\x99 \xd3v\x05\x92V+\xc2_g\xed\xd7\xb7y\\\xb2\x97\xa1\xf2\xee\xc1\x87\x02\x19\xc7\xd8-\xe8\xb0\xe8\xcc\xa2\xe6\x90z\xc1\xf5\x90\xa8\xd3t\xc3\xf8V\xf9\xb00\xb3A\x96]\x89\x1a\xd3\x18\xf3\xe6D\xca\xe6\xecJ\x9bC\xc1\x99\x14\xba\xe8\x182\xce\xe1\xf3\xf7\x14\xae\xa5\xea\xfb\x149\x1c\xb9S\x1e\xc1\x87nh\xd4\x8cAz\xa3\x1d\x06q\x10\x8a\xe6 \x84\x86\x83P\xb4\x0e\x02\x8fa\xde\xde\xf4kR\x1a\xb7\xbc\xa0\xe5\x86\x9dV\x8fB\xd8}\x14Z\x89y\"\xbe\xdb\x11\x1d\x0ff\xc3\xf9\x16 I\x92\xe1\x1c\xdaD\xa9\xc1\x8f\xaf^\xbf\xf8\xf9\xa7O\x9c\xb0\xcc]\x0d\x0e\xb3 \xe7\xc70K\xdd\xfd]O\xcb\xdeO\xbe\xac\x938\x8aK\xfe\xfa)\xdd\x16w\x7f\xf7\x90\xff{\xe4I$\xcf \x18hgP\x05\x8d\x0c\xa9;m p./I\xf16\x9bWZ>\xd6AKG\xdb\x93\x05\\\x8a\xf5C\xea\xd6\x1abwz\xc0AI\xea\xee\x1eq\xaa;u\x0f<\xd7\x11&\x1b\x9f\xc2k\x01Z\x9c\x97\xe7\xe7\x1f\xab\x84\xfc\x14\x17\xa5\xff\xf2\xfc\xfc\xbc\xbcK\xc8\x8f$J\xc2<\xa4#\xa1e\x7f\xa2p\x85UHb\x92\x96\x1fIT\xe2\xcf\x1f\xdf\xbf\x95\xfff\x8d\x8b_\x9f\xb2\xcf$e?\xc22\xfc\x94\x87i\xb1 \xf9\x9b\x92\xac\xb0\xf0u\xcc;\xfd\xf7Oo\x7fz\x91$/\xb3$!8y,\xd1~\xbe\xce\xf2\xd5\xab\x84\xd0[\x8c\xbf\xcf }+J\xde\x92y\x1cbco\xe3\x15\xa1\xe8\x96\xa5\xe9}\x17\xae\xc8\xfc]6'o\xc3\xb5O\xff\xc5:\x1f\xc2\x98\xce\xe1\xaf\x15)\xd8\xd0?$\xd5u\x9c\xf2\x7f\xd8\x97\xe7\x7f\xfa#K&\x87\x15\xce\xff\xf4\xc7w\x88\xa5\xc5\xaf\x0fa\xb9<'\xd7\xf5\xcf,NK\xf1CZ\x85\xf3?\xfd\x91\xcd;\xcb\xd9\xa4\xcf\xd1D\x95\xa1sV@\x97\xfb|I\x08\xfb\xfc\x13eg\xf20\xfa\xfc\x92/x]\xc0~eU\x84#r\x82b\x9d\xc4\xa5\xeb\xf8\x02Z\x8cO0 ~X\xcb\x80\x8b\xd1\xc8\x04g\x11\x1e\xce\x8a\x8b\xf6\xbd\xa7\xe0%\x9fE\x867h0I\xe9\xf2E#\xf4V\xa14\xe6<\xdeJf\xd5\x05\x13\xd2%(\xf9\xa0@\"\x9bE\x94\xab\xc8\x02\\\xd7\x9e\x13\xaf3<\x14\x8e\xfe\xf6P[\x1am*\x96\x13\x02D\x0eH=\x1e\x86\xf5\xd0\x87\x9dI\x1f)e\xbb\xec\xdd\x94`m\"\xd7\x10\x80\x12\xf1\xf72L\xbf+\x81\x0e\x06V\xa4\\fs\xc8R0\xe6\xeaii+7\x1b$\x07-\x83Y\xca\xa9\x0d\xeav\xd2Y\xa8\xc7\xef\x13o\xa6\xbe\x1e\xa1\x87\x19\x16ZR\xa4s\xe3+\xb1\xe3B\xc8\x8b\x80Mlc\xd3\x9f\xa1\xe5\x8eF\x91\xbe\xff\xf4\xde1h\x1aeY\xcc\x83\xfa\xba\xd0^\xb7`\x0d\x1dl\xc9\xa9(w2=\xf4\\'^\xe4\xe1\x8a\xe8\x1d\x89'G\xe8b\x13\xab\"\x92$AA\xc1l0\x8f\x8bu\x12\xdeQ\xac\x97f)q|\x9c\xfb\xa1\x17\x84\xeb5I\xe7/\x97q2g\x99\xca\x83\"\xa7\x80\xd2\xf95\xbc \x8b(\x8f\xd7\xe5\xb1\xe33\xabV\x12DYZ\x92\xb4\xfcs\x9c\xce\xb3\xdb`\x9eEH\\zA\xb6&\xa9\x8bn\x03,j\xa7\xf3\x8c}\xfa\\T ^\x9f2\xc5\xf1\xb3_\x9e\xf0W\x98\x81)\x88\x92\x8cE\x8c/\xf08\xbd>\x81|g\xe7\xc4\x03\xae\x9a\x94t\x8d\xb3l\x96_\xd8\xad\x02\nWS\x89\x9a\xaf5O8\xcf\x94\xd7\x94\xa4\xed\xe7\xa7\x8c\xf0\x89\xabf\x04m\xdb\x0c\x93\xa2\x12\xb7\xf4\xfc:\xdce\xe8\x83\xfa\x9aK$)\xc68e\x0eX\xb4j\xe1\xaaY\x95\x08\xd2\xe0\xc7\x10\xbb\xa9/'\xe8\xed\x07\x87\x02}\xa0\xf7hDb-=~\xae8\x96\xf6\x01?\x9b\xa4\xabx\x17\xbe\xe3\x0e\xce\x1eW\x84\xbb%\xfa\xf5\xb0\x10\xa8\xa9\xb71\xcf.\x11t\xbb\x9e\xeb|&w\x85~\xf2\xd9\xa5U,\xcc7\x1av\x8e\xe1\xa3\xee\xc1\xc5?\x98\xec\xe7\xf1\xa34 #g\xce\xe5e\x94\xe5d\xe7\xd7\xe2\xb2X\x869\x99_^:\xa2O\xf3;\x8a\xe8\x1f;\xa1XL(f\x13\xfa\xed\xa1o:6\xc4\xe9DYZ\x94y\x15\x95Y\xee/\xc3\xe2\xfdm\xfa!\xcf\xd6$/\xef\xfc\xb8\xf8 \xce\xef\xfb\x85\xbf\xe6\xc5o\x8aW5\xbf\xe4\x97\xd9OY\x14&\x84a\x03_\xa0\x05\x9fc\x1e\x99j\xdbl\x95'{^\xb00\xcaTtQKf&\xf6\xfbV\xd6\xcc\x98\xa3\xcau+\xc6#\x9er\xdb\xf9\xb2\xb9\xc6\x18\xd0\x98\x99\xd4\xa0\xb8\xa5\x0d\xcdUfs\xcb\x10PA\xc8,\x94\x17\xbd\xfb\xb7!W9\x9d\x1cy\xee\x96\xec\xeeBq\xcb\xbe\xc7s\xde\xfb\xe0\xb0?\x1c\xbf\xe3\xb0\xa1\xfd\xc9%]\x8a:S>\xf7O\xbaD\x83\xaff\xc8\xbe\x1d\xc5I\xe8\x8d\xb7g\xb6\xaf\xe1\xed\x9a\xa1\xaebHvf\x17\x041@\xda\xee`\x9e\xa5*\xffI\x9f\x07\x06\xbc(\xe0\xc6\xe5m\xe66\x92\x8d\xeb\xad\x9d\x19&\xc2\xfb\x99X\xf7v\xc3[\xb071\xcb\x15[\x9cm\xebF\xd4r\xd7\x02\x89\xb7\xbc[]\xa4K\x08\xd5\xf1\xbb^\xefm2\xed:A\xfd[\xd5%d\xaf\xf3\x11\xff\x9c\xce\xc9\"N\xc9\xdc\xa1H\x84\xc9\x8f\xf8\xabwU\x928Fg1\xa4E;\x119\x0e8\xbf3\x94Jc)g\xc4\xe0\x98\x02QX\xa7\xe6\xd5\xf4\\\xe8\xd1\xca(\n\xbc\x12\xb1\xe7q\xac\x9d\xa1\xb0\x08\xb5\x00\x0e\xab\x80\xc3u+v\xca<\xcfFV\x03KBCP\xe3 m\xdd1T=\x80\xc1D\x02\x8c-\xa8?\x0f\xd3y\xb6r7\xdeM!\x92d\x86\x8a\xaeC \xc2(,]}\x17\xe9xK\x1f\x1c\xef\x92\xd2\x8e\xa3Q*\x92\x04q\xf8\xb1{\xf0x\xb4\xbbk\xbe\n\xfb^M\x8f\xb6/A\xee\xc6\x1c\\\xc7\x9c\xf4\xe3\xf2\x93\xc7\xae\x00\xdd_\xad)fA\xf4\x9bn\x8a7x^\x93\xddn\xaa\xe7\xa8\x9fS\xfd\xef\xa0z\xf6\x9fZ\xf0\xf1\xbe.\xf1\xcb\xcc \xaao\x12\xff\xbb\xf1\xf1\xc1\xc4\xb4\x00\xc1b\xc8>Rn\xc2^ $h\xdb\xe6\x92\x10\xa3\xad\xf3l\x15\x17\x843&\xa5+O\xc4\xea\xc5\xa4y\xb4\"\xd3$\xfdN\x0d\xd2\x9e\x1f\xc29|\xe0}Id\xa5=\xf3!\xea.\xd2\xdalX~\x1e\x04:\xceI\x91%7\x84\x03\xd0\xba\xf0W\x96\x858\xd7\xddZ\x1e\xbe\x82\xff\x98\xec\x99\xa5\x05\x93\xf1#O/\xb3?m\xb2JJk\xc5n\xc6\xffq\xd0L~\x04\x0e\xcc3R\xa4\xdf\x95\x98\xf7g]BN\xae\xc9\x97-\x8b\x8e\x94\x83\xd3\xaf\xba\xd0\xf4\x82b\x8e\xe4\xfe\xabiD\xeep\nO\x82'\x9a|\xc7\x88j\x9d'\xc1\x13\x07f\xe5\x85K\xb4\xbd\x128\xb6\xb5p0\x04o\x93Y~\x81J%\x1f\xb6\xac}@\x0f.7-\xef\xa6z\n\xf3\xe5'A\xa3\xfb@ e\x1b.Tn\xeaN\x0f\x0ft/\xdc\xb8~u\xa8\xbfB\xd2\xceD?\xc4\x01W\xc3 \x85\xd1\xf6\x08\xc8\xeb\xf7g=\xc0DPE\\\xe7\xa8\xed\xd8\xf1\xc0\xaf\xad\x84\x8e2\xd02\x90\xe0\x04\xcb*\xad\xbcFPS\x17I\xe2\x94\xb3f\x8e\xc7\x96\xa1\x9a\x0c\x83*+\x90\xe5\xc3\x91\xb6\x8c!\x9b\xf6\x0ckuWi9I\x0f\xd2\x11\x10\x93\xd9p\xd7N!s\xeb\x1d\xf3:\xb7\xccBPW2A\x9d)@\xb1s\x0f\xff\x1e\xfb\xb7\xc1\xd8\x87\\G\x82h5u\x0f6d\xb6L\x82\x9d\xd4\x9d\x1a\xc9\x9bC\xb3\x01\xc7dl\xf6CAi\xc6c\xc1l\xcc\x1d\x94\x98\xc0G\xfc8Eb\xf4\xb7\x0748j*\xfc\xa6[3:\x97l\xf7\xd0\xbd\x1bC`0\x0f\x84\x98\x87\x9f\x0e)\xf3[v\xb0\xb9U\xb0p\xb5\x08\x06\xbd\xd4Q{;\xb8\x00\xf6\x9a\x94\x92\x84\x89\x0d{C\xbf\x91\xdd\x03}K\x84\xcf\x90\x99\x12\xdd=\xd4\xad\xde\xb9\xcf\xd0\xa1\xceQp\x9f\xa1\xc3\xe9?}\x86\xfeA}\x86(\xaf\x94\xbaO=\x1f\x9c\xb7\xe1\xfa[9\xa1\x1d\xea\xde%\xdc\xebdj\xf6:\xd9\xdb\xd5\x0f ;P\xfa\xf1\x0by\xedG\xfb\x81\x18\xe1o\xc9\x11\x93|\xb628\x06'k\xe4\x0dR\xd5\x8a9\xba\xc4n\x89\xe7\xa1\xa4\xe7\x81\x82\x0c\xc6\xb6\x86\xfd\xc0U_3z\xae\x8f\xc6\xe3\xa7\x93\xa3\xa3\xe9\xfe\xde\xd3\xbd\xf1\xd1\xd1\xa4-nx\xf2\x9f\xee\xd9\xf1\xf8~6\xd99\xba\xf8e\xfe\xbd\xf7/O\xfa\xd6\xc0\xa2\x86\xc1\x10>|:FZxk\xcb%\xd2U\x13\xfa\x13\xc2\xb2\x9f\xc8F\xae13v\xe3hg\xeb\x94\xf9\xee\xe7AI\x8a\x12u\xba\x88\xb1\x84\x0b?\xcb\xffy\xcaC\x97\x96\xf0\xac\xd7\xefd\xc8J\xf5\xad\x82\xed$Xb\xeft\x0c\xf7T\nu:\x08m6\x17\xc2\xec\x84\xd5r\x1e\xa2\xb7\xe1\xc9/\xc1\xfd/3\xf7\xecx\xf6\x9f\xb3_..\xbe\xbfwg\xcew\x17\x9e{v\xec\x9em\xfd2\xf1f\xff\xf9\xcb/\x17\xf7\xbf\xfc\x12x\xdf\x9f\xfd2\xf1~\xb9x\xd2\xbe9O\xfe\xf3\x97\xdb\xef\x1fu@\xb8\x7f_\xa3o\xde\xd2\xc2\xdf\x8bm\xe8>A\x8a9k\xaa\x90bu\xc1U\x96%$L\x9b\x12\xc5Ik\x0bY1z\xbe*q\x9c0\xbaX&\xff\x12_\x10\xb6Cq*d\x88\x1b\xa9\xf9j|\xd4\x96\xe42\xf15\xb9!).\x9d\xf2\x13I\x03!\xe1^\x85_~\x8a\x8b\x92\xa4$o**\x855\xb3/\x8d\xac=\x84|C\xd0\xd5\xd9Xlo\xcc\x04\xda\x9a-8\xedi8\x1bD4k[\x00\xda9L}H\x83Wt-_\xad\xe2\xb2D\xdb{,k\x10\\\xb3\xf2\\\x0d\xa1\xbe\xd5\x16\xbd\xa9\xc3\xa9\xe3\xb7\xea\xfb\x89\xf6}A\xf4\x1av\xa8a3\xd1\x06\x91\xc9\x18\xdd\xc3\x99.\xd7$\x9cH%c\xeduV0K\x8cN\xabm\xf3\xb9\xf2\xd50N\x0f\xea\x8c\xc8*\xee\x8e\xc8 )\x11,\x96\xcd1\x8f&(\x1fsW\xbb\x06\xbf=Pr\x81\xd0\x999M\xd4AwK\xae\x16\xe0k\xee4\xdf*gF.\xedr\xe1\x97i\xa2\xd2x|\x0e\xd9\x14\x97b^\x91!9[\xb0\xb0\x1fb\xf1\x0dY7\xe9\xec\x17\\f\xc7\x1d\xf4~N\xa3\xb0\xba^\x96>Ti\xb1&Q\xbc\x88\xc9\xbc\x9e\x1b\x0e-\x00\xf7;\x9e}\xd7\xf1L\x927\xd6\xdf\x82\xd9t|)\x99 \xefB\xa9\xf6\xd0Z\xe3\xac\xc9\"\xcaW`V^\xd8\xc1.\x83\xcb\xa9\xe75\x0e~\x9a\xed\xb9i\xc9\xba\xfc\xf8\xd2&G\xbfE\x9ah \x7f\xd2\xe5\xca'5\xea\xab\xfb\xb4y\x17\x16\x17r\x82\xde\xb8\xaa}\x92\xb7,\"\xdcD4\xdb\xf6\x91\xed\x84\x92=\xa0J\x813)\xb9\xadG\xbf\xcd2\xe8!\xdct\x1d\xe9\x8d\x83\x0c|\xee\x92@\x0c\x89\x92\xfc\xcd/$\x87}\xfd\xfa2\xae@\xbb\xd2\"\xcaaS\xc4\xc2\x06\x11\x91\x9aOn\xe0\x14fZ\x91\x0f\xe4\xc2X\x91\xf8\xa6\xcet\xb0J\xbb\xbb\x0d\xf3\x94\xcc\x81\xa5\x0b8\xa5\xc8\xbb\x85ZP\xdbjD\x9b\xc7\x06D\x84\xddT\"\xf6\xb0\xde\x1d\xb7)x\x0e\x15vi\x19\x0dsa\x88\xb2\xb4\xc8\x12\xc2\x80\xbf\xeb\xb8i6'\x1e\xd0*\x18>s\x9d\x15E|\x95\x10P\xc8\x84\x15Ye\xf9\x1d$$\xfc\x0csR\x92\xa8$\xf3\x00\xfeu\x0eI=\xeap>\xa7e?\x17\x04\x08\xfbJ\xc7\xf6\xae\x07e\x06q\x1a\xe5\x84\x02\x9b$^\xc5e\xe0\xb4\xb6\xb4\x89\x93j\xa4\xbf\xc4\xf8\xcb<\x8c\x90\x08U\n\\\x91\x0e\xc9v\x932\x14i\x98\xaf\x96^\xb3?\xf9\xf67\xbaY\x82\xc2\xa7(Hy!\xd1\x95&dS25\xd2*\xbb!b\x0et\x98\xb1\xc7\xe3\xbb#\xc2\xa3\x9bNT\xf0#\xa0Y+\x82\x92\xfcKXi57\x10o\x00\xf6\xc9\x96#\xeeYkud}kyS\xfb\x7fQB\xe9w\x81`\xd8\x8c\x0e\xbf\xf4\xcb\xdb\x11w5^\xb0\xfbl$$j\x0c\x901a\x1a\xddQ\xa1s\xcc\xddT\x02k\x94\xea\x97V\xf5\x14\x83\xbdr\xd9T\x0b\x16)\x90T[Q\x15\x98\xaa/\x19<\xd5\xe3-\xab\xb8\xd0p\xa4jlX\x9d@\xb8\xb3C!\x8e!&\x0d\xf0\xc5Hg\xe1E3K\xfa\xab\x99\x17\x9d\xa5R\xc0'\xda\xeeS\xf5\xdf\xc4\xfe\xab\xf6\"I\x86\xf1Vf]{\xebz\xf4\\\x85\xad\x8e97!\xecYf\x1c\xddm\xf3Lg\xf4Q \xa0\xe3\xdc\xed\xed\xce{\xd1\x1e\x92\xb97\xebA'\xe8D\xaf\xccX\xdf\x1en8 \xb6\xb0\xbd\xd0nGLs\xdb'z'\xda\xf9\xc1\xe5\xd0`+\x18y\x9a\xdc\xc2\xd3X0\x83\x1e\xee\xbe Oi\xa1\x8bO\xea\xbbqbotV\xdf\x99\x1dh\xf1\x1d|%\xba\xb6\xd1v\xa8\x93Ag\xd9D\x96\xb6i$\x16'I\xbf\xc6g-\xe2\xcf@\xf9 \x1a\x1f\x8eav\xd17\xd6\x97Y\x95v\x0b\x04tv\xdf\xa6\x1e!\xed\x8dm\x9f\xb3\xc68\x83/\x83!u&z\xee\xd4\x15\x84\x05j?\xbc\xd1\xb8\x11\xfb\x0c;\xc2\x85\xa9_\xf5\x0b 5q.\xcf\xc5!{\xbeO\x0e\x9fz^p^\xe6$\\q\xd7\xdd\xe0# \xe7\xe1\x15Z(\xe0\xef?s\xbfg\xf6\xc1\xe4)\xfa\x86\xfcX\xad\x13\xf2\x85\xa9C1MLP;\xf9\xb1zGS,\xfd\x10\x16\xc5\xa7e\x9eU\xd7K\xa6\xfb\xd8?\x1c\xa4\x83\xed\x0d\xd1d\x0ett#\x92\x99\xb9\x18\x07MyW\x93\x7f\x06\x95?h\xc7\xc4$$\x89\x0b\x8c\xb4\x02\xc2o\x83!\xa1\xb4\xcc\xef\xd4\xa2E\x9c\xc6\xc5\xb2\xcf\xc7\x87>[\x9dK\xa0?\xb5\x96\x8fujG\xed\xa52*{=\x0e\x93r\xa3NQ~\x84\xd6%\x0fD8({\xa3\x80\xfa\xdd5I\xe7qz\x1d]\xed\xecP6\x8f't\x81\x1cW\xd0\xfam\x9b\xf2\x10\x0f \xa2,\xffL\xe6\xdcc\xb5x\x9d\xa3]\xac\xa9XlRIy\\\xd3g\xa7\x86\x00\xa8\xf4y@\xb5\xb7\xc1V\xa8\xe3r\xcb\xb7i\xd5fCB\xee\xe4N\x82\xab<\xbb-\x18\xf12sn\xc6\xc1d\xec\xf8@\xff8\n\x9c\x8b:\xfaW\x13\x0f\x8cA\xc9\xb1\x0f\xfb\x1e\x8f!\xcd\xbci\xb2:\xda\x8f\xda\xdb\xaa\xbe\xa6\xe7e\x88Z\xd9\xeb\xf6pP\xc8\xe2\xee\xeby\x04\xa3 N\x97$\x8f9L\xd8\xd5\xd36\x08\xb1\xa3\xf9\x90\xcc\xc9:'QX\x92c\xbc\xdeO\x0d\x0b\xd8V\x85'\x1c\xfa\xe8z%\xfa\xac\x99\xc6i\xec\xf1\x906\xed\x1aK4\x81h\xf2\xa6(\xde[\x1e\xfcfH\x0c0\xf7\xe1\x86\xf7i\x07\x0cw\xf8\xb1\xe5\xe5\xb5\x114\x03\x97\xaf\x85H\xb23X\xc8N\x1f\xaaW\xda\xf7D\xdcb\"\x0b~\x0dt:\x82\x12\xa6\xe5x\x9b\xcd\xd1\\l\xab\x94\n|\x16V\xd7m\xd7\xd3K(W\xb6\xc5\xfc\xf1\xe8\xf9x_\xbf1PZ\xb5~5X\xc6\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\xf6\x16\xd0'\xc2\x8a\xa2\xdd\x7f\xef\xff`a\x18\xdd\x19L\x0e\xe0\x18&\x07\xbb\x87{\x96UP\x86\x02\\k\xcbh\xd3\x18\xce \x86c\xbe\x16Q\xf3\"\xa2\xe4H\x04\xc7\xb0\xf0\xcd\x8d\xc8\x19\x15[\xef\xbd\x06\x94\x87\xc9\xcb0I\x98\xc0g\xe2\x0b4@\xe6?\xe6a\x9c\xca\x85\x0c\xe2i%\xeaw\x0c3\xa8esR\x94yv\xc7\x0b\xcd;\x92\xe0;\x9e\xe7fN\xa2l\xce\xbd\xablxJ\xa9C?N\xea\xdePB&R\xc1\x00kP-\xbb\xbf\x07\xa7*\x17\x87B\x98$spX@w\\\x9b*\x03\xb3R\x9d\xe2.\x8d\xb8\xb8\x04\x7f_\xe1U\xfe\x90g\x11)\n\xed\xe3,E_\xd1N:O<[\xdd\x94\x92\xfc\xdc41Moe\xd8h>\x9b\xe2\xc9\x99 \xfa.\x8d\xba\xeb1\xf7f\x1cxteG\x87\x94\\\xec\x9f\x95xJ}mE\x07\x0d\x85Q3\x07\xe2\xee\x91\x84\xa4\xbe\xf4\xb7\xe2\x86\xa5?\x0f\x88\x8a\x89g =\xba#G\x8aggGB\xee>\x1a\xe0\xbb\x0dNrc\x1fr\xcf\x97\xb0\x94\xfb\x8as\xe4~k\x1f\x98\xd0\x94 E\x85<\xb5\xe4\\=\xd3_\xd1\xc60f\xbfO\xc5\x1b\xcf\xf3!\x91T\xc5\x83\xf6\xf4R\x05\x8aL\x8en\xdae\"\x1f{\n>\xa4\xbbQ\x89\x9f\x1c\x9e\xa3\xe6@\xc2\x8b\xe8\xbc$V\x8aBN\"0!K*\xc1\xde\xb8\xac\xf7\xe6\x9d\xdc\xcad\xd0l\xae\xa4\xd9\x98&\x91B_\xf4\x03\xf1\x88\xb8\xc6\x1c\x07moc\xf4QA\x0ca\xda\x9b6q\xc4!\xf2\x9c\x969\x06(\xfc\xe0\x96\"\x86\xa5\xc26\xe6n\x03\xbb\x07\xcd\xf3\xd6:vb\xa4?\x0c\xd9\xb4\x04\xcd@t\xd0a\x16\x04\xd5\xdb\x87\xf2y\xa6\x8a\xa0\x98\xcf\xb6~5\xf1o\x84Lv\x82#\x069\x92ln\x89\x02\x02\\\xeao\xe2z\xcd\x98(k$\x05\xe6\nu|\xad\x90\x81\xcd\x82\xad\x1b\xda!\xc7\xa8\xae`&O\x98^\x0e\x95d\x05\x0b\xea\xc6\xa3^\xe0j\xf8\x10\xc2\xe8\xd4$L\xa3\x0f\xc69e\x88\x00\xcd\x7f\xfd\xfa\xf6\xb1\x1bSg4\xf3\xc1q(i\xc1\x10\x80z^F#\xac\xda\x81R\x18IB\xc9\x15\x8bP \xe3c\xcdd)\x8fg\x17\"0<\xc1\xce\xad\x0d\xcf\xb4\xcfz\x17\x05!d\xc4\x9d\xf2\x98\x9a\x8f\x0f\xa2e\x95Z\x18-\xf1\xa0\xb1P \xd29v\xd7M@\xc4\xeb\xe9\x16\xf0\xd0s_\xef\xd0\x04!\x93\xc2\xcd\xc11D\xf5\xa6E>e\xc0\x12\xed8\x98\x17\x8c\xde\xf9\x1a`z\x1b)\xa8\xe8S\xbb\x88\x0b@d?\x0d}2\x1e\x90@\x86\xf2\xado\x81$\xc3\xe0\xf0\x97n\xff(\xc1Abtx%\xab\xb10ld\x85\xfa\xb8\xd0d\xa2\xe1-\xd9O\xbe\x8c\x83\xc6un\x85\x9b%G\xa7\x0d\x0bc\x95Pj\xc0\x1b7A'\xc6SviU\x1aN\"\xda\xeb7\x8e\x05\xf2\xd3\xe7a\x182xe\x9d\x94\x80\xf1_\xbatM\xec\x10\x0d\xe46\xd59\xdd\xdf\x03Q$\x07\x14,Z\x88\x17N\xad T\xd2\x80\x99&{\x18+\\\xd59\xe7\xaa\x90;\x1a\xb8\xa4]\xa8W \xf6\x86\xe6fw\xc8\xd2j\xd3\xa4/\xd9\x94C\xeb\"5\x92EJ\xf2R0p\xad:\x8a\xd4A\xab;e\xe55\x16*\x85\x00I\xbb\x03,\x98\xc8\xec\xe2\x04\xca\x13\x8fN\xa3*\x96,4 \x12\x82t\xd9\xac;\xadyy\xb7\x81d\xaf\x18\xdf\xee\x96J\x1f\xee\xe6\xc4\xfc\xd7\x84\x9b\x93{-{\xac;l:\x8e\xc9\xe5J~0\xcc\xe9\"\xa8%\xae\x9b\x05|\x97U{\xf5\xd2\xbbv\xde\x10\x18\xc7\xe7hL7\x1b+\xc4E#\xf9\xe5\x96JZ\xc5f{)wC\xc2y\xe0\xf8\xe0\xfc\xf8\xea\xc3x<\xde\xb5\xa4F\x83\xf6\x05\xaf\x8b\xed.\xbb\xf8\xda\xb5\xb1\x08\xdc\x13n{\x9b\xff\x15,\xc3\xe2\x0d\xe7\xb7\xc0\xe6\xd3\xf8\x9a\x97IQ\xc7\xda__\xd0\x8bK\xef\xc6\xb0\xda\xbe\xe5,\xac|\xc3\xc8:\xdc\xef\xfa\xe5I\xb5#\xcc\\66-\x1b~\x93\xde\xf6\x15\xf0T\xcd\xdb-\xc9\x8a\xcc\x8f^\xf7a\xcb\x07\x84B\xf3^\xf1]\xedG*5^\xb6\x94\xf2>\xac$\x10\xb1\x8e\xd7\xa4\x0f:0 \x80\x8ah\x9a\x1c\x8a/\xc34\xcdJ\xa0\x0d\xf9\x18\xa7>\xe7\xeaM\x9d\x15\xd1zn\x8b$\xed\x1a:$\xebY\xe4Y\x03cn&\xbb*\xc6\x1e\x19\xdfa\x80\xe4X\xa6\xab\xea\x84\xfb>\xac\x9b\\\xce9nh./\xe8\xd2\x8e\xd2B$\x0d\xd6J*h\x91\xd9|\xf0\x91Zc>\x01\xdd\xfb\x13\x80\xe7\x10\xb4\\A6\x81T\n\x0eM\xa90\xca\x17\xb0\xf0\xd3\x02\x00Rj\x1b\xd1%sr\xd5$\xd3j\xeb[R\xf0}\xd1\xfa\x9d\xe7C\xcc\xe5\xeeg\xc3p\xb7\xa0\x06\xa4#\xc3\xb6>\\\x94$\x07\x92\xcem\xc1*L\xd4\x8d\x84\xa2\xf1\xb0\x98V \xefb\xca\xc3^\xeb\x9c\xb7\x9dK\x07I=c\nZ\"\x9e\xca\xa2H\x00\x89\xb8iH\xe53\xe6\xa9\xa8\x06\xe8\x7f\x1b\xde\xe1Ua\x0b\x81\xb5\x11\xf4\x14PfP\xa0\xb1\x80cM\xd6\xdf\x04\x05a= 9\xa4\xaa\xa3\\C\x9f\"\xd7i\x9a\xa5;\xac\xd9'\x1c\xd3 \x9f\x83\xc1\xbf\xb9A\xae\xb6\xee\x95\xba\xee9+\x89\x05\x1f\x1a[\xf7 f2S\xe6\xe6\xe7\xc6*\x01V\x19\xee~-\x0d\xb2\xed\x0f\xdaq\xf5*\xf1MM\xf7!\xf0R\xd7\xe8\x19\xd5A`\x8e\xdd\xdf\xdc)~}\xb1\xc7\x1e\xe9\xb4\x91<\x92\x9f\x87\xda\x08\xc3\xdeP\x8e\x06_U}A)\x11\x19K\x17\x9e\x99\x05T\x16\x8co\xbd\x03!J9Z|g\xde\x99Y\xaa\x16[\x8d\xac\x86\x91\xb4\xed\x02$ \xd73 \xaaf\xd0\xfc\x1d3\xdd\xd7d_c\xcb\xba\xa0\x05Q-\x18\xc4\xeb\xc1\x04\x0c}\xe7&b#k\xb3\xb5\x1d\xfa\n\x0b\x17\xdc}\xd8\xf0\xc6\x1d\x83A\xf3.?B\xacp\x0cq\x8f\xaa\x8c\"\x1cc\x1c~\xf9\x11\x92\x07c\xee\x05\xf9\xa17\x9d9;\xdb\x8f&\x0b\xd2\x1f Q\x8ey\x19\x8e\x8dL\xbe\xb1\xaeU\xc83:\x85\x89\xf9\xf02I\x8f,) \x1b\xf8\xd1 \x9e\x8b.\x88\x152\xce\x0f/\xb0/\x85\x82\x836 CO\xd5 \xe2I#\xdc\xd9i\x1c\x8d\xba\xda\xae\xd2!\xad+<\x9b\xda\x8bA\xa7!4a\x0c\xc8\xb3\x1f;;\xbe\xa4\x15\xa5\xe4\xab\xa4/\x93\xa4\x1e\xf8\xcb\xa8=k\x0bL\x98\xf6\x8c\x93\xc4\x9dD`A\xca\x1f[\x1a\xf3nZ)\xb6\xa5A\x14\xa4V\x19\x94\xd9O\xd9-\xc9_\x86\x05\xf3\xb0\xd8rg\xce\x92|\xa1\xdc\x11\xd7\xbb\xd3\x7fw\xf0\x8f\xb0\x88\xe2\x98\xfeq\x15\xa7a~\x87\x7f\x85\x059\xd8\xc3ZQ1\xe5\xff\xeeL\xf9g\x93\x83\x84\x88\x16\xc4\xdfyx+\x19\x19\xb9,\xd3\xa2\xa7\x8d\x03\xad\x8cp0\xb59\xe2\x90\xbbm\x8d[\xc1,\xae\x9bt5\x12{@ \xccM\x98 )\x10\xf7\xf6\xb6\x1c\x98\x8e\xb1\xb8\xb5\x8eZ\xc8\xbcr\x19\xde\xe4\x8d \x8bP\x1e3\x10\x8774\x17\xb2Y\xcan)@g\xc8J\x01\"\xe2\xc6>h\\\x0b7\xfdZX]\xb7y&\xd3\xb2)\xd3\x04fiDj\xa1[\x07\xe9F\x1a\x93\xa3\xb1/\x99f\xb5E\xd4 !\x95\xbc\xc5\xa8\x0c\xbc\x82\xb5\xe9\x92\xf1\xdamt\xad\xe4\xdd2\xa8\xb6k\x0bt\x1d\xa0\xf0\x01\xb4\xe7\xd6\xbe\xe6\x852\x1e+\x9fk\xe9\xde\xed\xec\x9f\x9e\xe1~1\x89z\xd3\x1a%\xf7\x8d\xf8[\xbb\xa6U*\xd7\xa9\x7fi\xb5\x9a:\xbd\xfc.\x93\x94\xa4s\xd7\xf3\x81\xb4\"8\xfd\xa1\x19\xa9\x9a\x9b\x11\xb3\xe8\x1f\x8d=\x8a\x0e\xdf\xacVd\x1e\x87%\xd9$\xb5~\x7f\x0e6\xfb\xbe\xf0\x03\xd2\x1b=\xe2\x9b\x0c#u\xf7\x0e\xf7<\xd7\x833\xee\xbf\x8c\xc9\x13\xd1\xb0\xf5p\xff+\xa6z\xd3\x84o>2\x87R\x99\x9a\xd3\xc2\xed\xea\xc1\xc3*\x83k5G\xec\xedPC\xfc\x1275\xb5h\xee\xca\x07\x850\x8a\x0c\xaf\n\xf5M\xf4Uy\x02n\xea\x90\x0d\x0b\x1f4k\xf4\xb8\x95=\xa5\xb2\xf8V\xaa\xdf\xa1B \xc5\x00\xb6\xcc\x1b\xd8k\xfc\\\x17Z\x84\x05\x86#h)\x0bo\xb1\x10Y\n\x16\xf0\xfc\x14\xb3\x14D\xee\x82\xa7\xfc^\xc6\x8d\x93\xd3\x0eDn\xe1.<\xef\x04X\xe4-\x18\x8d\x0c\xea(\xb4\xf3\x91\xa5\xac<\xccP\xc2Q\xe3\x8c\\\xf8\x90\xbb\x89\x94\x02E\xc3\x8f\xbc\xb47\xd3\xfc\xa0\x93\xa6xH\xb4\xb0\x91\x10Tj\x03\x18F\xd4\x9aDo\x96\x14\x8fHa\n\xc2\xc4\xeeA\n\x12]\xa5\xbcx`R\x82\xeeA5\x07\x8b\xd6\xad\xf3\x8b\xb0P\xcc\x9f\xc8\x97\xf2]6'\xaec\xcb\x99\x92ah\x01\xdbx\xb4\xb0\xb8]\x029\x0b\xfb\xcd\x1d\x858\x82g\xcau\x16#\x9bX\xf1w\xb7u\xa1\x90.\xb1!v0\xfdp\xaai\xe5\xc4c\x96\xa8\xa0\xcb\x9aJNY\xe4\xb8i\xe3\xc3\x08u\xfa?V\x1f1x\xe9Zf\x86\x176\x0e\xe6a\x19b\x98\xc2S\x18\x8d2\xf8W\x982s\x07l-(\x96\xf1\xa2t1\x04\x05\x17\xbf\x08\xafkN\xe1\x95\x06m\xd5\x83\x17dW\x05\xc9o\xd0R\xca\xbcx\xd12\xcc\xc3\xa8$\xf9\x8fa\x19\xb6\x82\xfe\xb3V,\x16\xeb\xbd\xf4\x02}X\x9a\x17\x0cai&X\x99\x94{F|(/P\xec\xc0\x15\x94\xa8\xbde\x04\xb0iq\x86\x88\xc5\x1e|3\x1c\xb6^\xe3v\xe4$$p\xec\xaa\xb0&\xc1\xb4\xe4\xf6f\xf6B\xe9\xe8D\xdcO\xdaM\x9d.\xa8C\x8cj\x1c\xca\xdb\xaa\xc4\x84|\xef\xd9\x8e7~\xb1\xb1\xdbze\xbf\x95\xc6\xa6\xffL\xae\xfe#.;:\xb0Th\x1f%\x1bH1\xdf\xa8\xde\xe0\xbb\x80\x8c_\xee\xea\xa2\n\x00\x16\xb8\xd5\xd8lA\xcaO\xf1\x8ad\x15J;\x0c\xdb!U\x182\x80\xa6\xba\xcb\x0e\xfb\xd8<\x98\x96T\xeeA\xba\xb2\x83\xe8\xcaoBeY3h\x9a\xb2f\xaay1\xa7l\\\xfb\xd3}\xfe\xef\xc1\xc6y1;F'\xd2S\x1e\x9a\x92\x8d\xa1\x86\x8f\xa7'P\xc3\x0e\xe7\xdda\x87\xd5X\xe9\x96|WV\xc8 \x84t\xed\x0e\x92,\xc2\xc3~\xdcJaF\x9fe\\\x94Y~g~\x99\xadI\xaa\xb2\x7f\x86J\x98\xf2\xab\xb7\xd6\xeb8\xd1+\xd9\xe6\x0b\xe2\x86K\xf1\x82\x9b3\x7f\x8b\xc9\xcal\x89\xfa\xccV\x1cta\xd8wmxr\xc3\x1dFm\xda\xb8\xb4C\xc5\x9b\xd7\xf1\xde\x0c\x82P\xab=Im\x08\x13\xf3\xb0Ih\x15$\x82B\xbb3\x87\xae\x95\xe3\x83\xf3C\x92]\xd1\x7f_g\xf9\x8a\"=\xe7\xc2;\x01\x16\x16\x13\x13\xf3U\x08\xc0]\xcf\x0b\xe6YJ\x90\xc4E\x8dE\x07\x92\x13z\x97\x98\xe5\x10\xb4\x93\x1f!\xc4)_3\xc693;QV2\x0b/\x86`5,\x91\x0d>\xec\x0b\x93;\x8c\xee\xe0P`\xe0\xd0k\xcb\x0b]=\xc9@\xaf;\xbb$\x1eW\xcf\\\x9f\xb8@h\xd6\xe7>\xdc\xf8p\xe7\xc3\xb5\xde|\x81y\x0f}\x98\x1b\xdc\x92W>\\\xfap\xe5\xc3m/\xbb\x08\x82\x83Z\x83\x08\xb6\xfa\xa2\xc6\x05/\x8c\xf1 \xe8#\xc2\x15v2\x00\x18\xef\x8fe\xec1\x87\xe0k*1C\x8a\x8ej\xd0\xacf/\xfbi\xf8\x86R8i\xad\xdd\xea\xfc\xca\xe2\xfce,\xdddD\xc3Gb\x00vmt\xf9\x05\xbd\xa5G\xe0\xc0\x1bq\xa0\xdb\x95\xce\xe1\xb4^[\n&n\xdaU^Y\xd0\xf1\x0bT\xca5\x82\xedV\x85\xf7p\n/f fNz1s\xfe\xed\xdf\xea\x8b\x85E\xe8\xfc\xf1bvcH\x1a\xfd+\x05\x86L\xdfxc\xe00?S\"\x00\xce\xe0\x1c\xce\xe0\xd6uHZ\xe61)\x10\xa2\xfd\n\xf6\xd4uoX2\xb7<\xbc\xc3\xa9\"\xa2z\x11\xf0\xafio\xef\xdb\x14\xd1\x1bD\xc5W\xf4\x96\xb8o\x18\x19\x8e\"\x0e\xcf\xf3P\xea\xae\x8b\ni\xf5+\xa6>G\xcfj\xf7\xca\x87/>%\x11(\xba\xa5<\x85\x89\xed\xb8\xe2\xabT\xd1\xea\x89\x0fK\xcf\xf3\xe1\x9c\xb6\xf0\x1e\xe1\x8c\xd8 \xec1H\xc3\x15\x93\xad\xbf\xe2x\xfc\xd7\x81P\xe6\xbd\xd5\x9f\xcb\xe3n\xf1[L\xf7\x8bW}\xeb\x15\xdb 1\xb4\x178\xb4_=\x1f\xc2\x19\xa1\x94\xc9\xaf\xf4\xaf/\xf4\xaf\xa5\x0f7f\x11\xdf\xcaj4\xc1\xe6t\x8c\x9bHw\xed\xd6\x15\xd3\xb4\xc8\x14(\x988\x86\xbb\xa6\xba)\xd3\x97x\xf8\xae\x1e\x83A\xb1\xe8\x9bl3A\x90\x89\x97\x14\xc2\xad<\xc0\x7f_\xd0\xa9gt\xea\x97>\xacf\x97\xa6\xf0\xa2,|\x91\x1b\x07\x1f`\x04q\xf0\x1a\xbe\x07wM\xbf{\xe5!\xfc]\x99c\x11\xad\xea\xc2A8\xf7FJH9\xb5\xd0\x0f]\xdfC\x1d\xa7\xa7\xd4\xd2\xe4\xda\x08{\x01\xc1\x8d\xba\xb9\xae\x08\xb3:\xcc\xeb4\xd2\x12}7,\xae\x05\xe4\xb5\x17\xbe+ mk\x0c\x1d\xd6\x81`\x1c\x06\xfd`\xa3\x91X\xe2\xd6\x9aF\xd2\xe30n\x1c\x8c\xd5\x1f\xb9+\xce\xca\x10\xf4S\xf7\xc64\x08DV\x1fX\x9a\x1etb\xe5\x93\xb9\x95\xba\x93}\x16\xa54u\xa7G\x9e]B\xccG\xf3\x14\xb6N-\xcaT\x91\xda{\x1e\xdf8\x9e\x0fN\xf8\xf5j\xd4\xa7m \xa1\xce\xdc\x0b\xc2f\xf2\x1b\x92\xfbS35|\xf4?3\xdd\xa2\xaa\xf6\x9bn\x9a\x19\xa8\x95s\x98\xab\xf1\xcc\xf9A\xa6\x93}\xcf\xdd\xd2)uc&\xf9\xbeu\xb1\xc7\xfa\x0cyB\xc76\")\xda @\x813\x163\x8d\xec\xe5\x9a\xb58\x85\xd0\x83\x94\x1e\xde\x8a\xed_\x88K\xb1\xbd\x0d\x11\x13^\xeb\xc1\x0d\xb8\xf3\"i\xc2\xe7\x16'\x1e\xff\x8e\x12p\xb3b4b\xf1}\xdd\xff\xca\xdc\x08[\xbb\xbfoZ3#\x97h\xb3M\xed\xdd\x9f}s\xaa\xe8\xcel\xfe\x95A\x93\xda\xc5\xf7\x06\xd7\xa4\x94\xb2d\xabV\"\x96c]\x8a\xbd\xe3y+\x91\xc5\x9de\x176\xf9\xae\x9ae\x8b\xf33\x8dW\x85\xf2\xf6L\xfd-\xd1x\xc7\xeag\x9c!?\x83J\x97\xe4n\xb8\xf8\x87\xe6\xc5o%\xe4no\xc5?s\x14\xd7\x03\xee\xcbu\xf8?;G\xb1\xf5\xec\x98\x12/\xfd\xcf\xcd\xa5\xdf\xb9\xcd\xbc\xb7\xf6.+\x16\x8b\xee\x04\xb6\xc1\x04\xd5\xb5<\xb6\xee\xd4RO\xd8,\xd1:{\x96:\xe6\x8c\xb7\x9b\xeda\x9f4m\xb2{\xd0N@\xbf\xfb\xf4\x9f \xe8\xa5\xe7\x7f@\x02\xfa}sR\xc4\x01\x19q-\xe7\xbf\xae`\xb3\x9f\xa4}\xf3@\xe6\xcd\xbe\xc7\x14.\x99y\xe6\x82g\x016\xbf\xa5TOhu\x14\xe1c*DJ\x9c\x82ns\x84 \xd6x6s\x8e\x03\x8e\xc1\xc5\x08\xdb\x98D\xf1e6'/J\xb7\xf0\xe4\xee\x9d\xe7\xc3\xdd\x1f\xa4\xa2e\xe7t\xa5\xdd\x91?r\xf8\x15\xc0!\xa4\xee\xde\xc4s\x13\x0f-i\xbb\x1aK\x1a\xd7\xcb\n\x83\xf4\xfa0\x91\xcc\xae\x1f(eI\xf7\xe1&H\xb3\xdb\xde\xd6\xb0\x96\xb5\xa19\x86\xce\x16\x06\x99\x94\xa2\x9c{\x01\x05zS\x1fb\xfcc\x12d\xe9\x8a]68\xa5\xd4\x07\xc6\xcap\xb3`\x9d\x15%\xbf\x85\x08h&\x18\x81i\x11\x84\xf39&\x1a\x94Se\x197Cj\x00\xc9\xbcE\x10\xafh\x8f\xe7Q\x1e\xaf\xcb\x82\x8e\xac{j\x0by\x0c\xdc\xa1\xdc\x07\xe7{)\xac\x17\x85\x94\xad\x11\xb9\x0e\x9f\x90\x83\xe4\xd4\x16\x1b9\xed\xcb\xc9\xd2\x9c\x84\xf3\xbb\xa2\x0cK\x12-\xc3\xf4\x9a [\x1d\xb9N\x81\xa3r\xbcNK\xf5\"\x08\xd7k\x92\xce_.\xe3d\xeeJ_yA\xbb\xe5\xbe3,\x123\xb1\xc6J\x16MY\xdcS\xab2\xb9\xd3\x94Q\xb2\xa0oN\x84bG\x8f\x99>%\xc4\xd7\xfa\xfe\x18\xd6\x1af\xa0\xb0\xfa\x18\x9a\xecC\x9b\xd1)\xf6\xc1\x9a\x95\x0fVy5},\xce\xf5\xf4\xb996{\xee\xa8\xeb\xd8i\xd7\xda\xdb\xb5\xc5\x04\x9bv\xdd\xd7q\xcf\xeamJ\xe9\xb4\x0c29\xa53\x1ed\xed\xa2O\xbe1u\x89]\xe6YH\x14\xe5\x1e\xea\x9bl\x9e\x857<\xb6U\x16,ZQ\xc4\x05!\x8c9\xc5sRd\xc9\x0d\xf10\x9c-F\xb1[\xc5\x05y\xec\xc2\xb4V\x80-\xcc\x9e\x9d\x04\\\xd1\xad\xef'\x00M\xd4\x9f\xd9\x99\xb2\x0en&9\x963O+N\xdemmQ\x02\xcf\xf9H\xae_}Y#h\x8c\x15\x0f\x9bAS\xb6\xdf\xd6\xda5#u\xa7\x87:A\xd7\xb8v(\xf2\xffA]\xca\x12V\xe3*\xeb\x9dq\x03\x84\xa3\xde\xc5\xb5Q\xd7\x88\xa1\x02\xae\x1b\xc6\xa46\x1eW\x8f\xb12J\x16\xb5\xaeX\x85\x84\x9d\xba5\x15\xcf\xfb\xcb\xb2A\xb9yp\x0e#\xc8\x91Y\xce\xba\xf5\xbc\xf4\x90(\x85\x98\xbf\x9dk*}9|\xd4\xa054\xcb\xae\x89\xecr#\xc2\xb5\xf3}\xec[(\x14\x8e\xba\x8a2\x9d\xd8B\xa9\xf0\x80\x84\x14\x97@\x08Q\x12\x16\x05\x84\x85\xe2%\xfb\xbbLG\x93\xd2\x0bO\xa4\xc9\xbe\xe9\xc4|{W$\xe3Z\xb6\xc8\n\xfe\x02J\xab^\xbc&oS\x96\x1a<\xc5\x18]\\\x9d\x03\xe9h\xd4E\xe8\xe7h\x89\x92Z\x08\xfd\"\xd2\x84\xac\xa0s\x01\x0f\xad\xaeB\xf6\x89\xe4\x95\xbd\x95\x07\x0b\xce\x97\xb1\x80J\xe5\x8c\\l\xb8_\x8f\x03%8WJY\x1d\xea\x1a\xdf\x98\xbf\xda\x1dO\xf5W\x19\x7fE\xe1\x8f\x9c\x86\xb0F|\x86\xdc\xa4\xb5\x89 \x0b\xd4,\x83\xa5\xb2\x1b,iA5\xfe\xd0\xfek#\xf8d\xb9\xea\";\xc1\x163\xc27\x12=\xe7\x14:\x01\xf9\xb2\xceIQ`\xd6\xa4\xaa(\x81\xc4\xe5\x92\xe4p\xc5c\xccf\xb9D\x05\xb1`\xcd\x0e\x8c6\x86J\x1a\xb8\x935s\xccc6\x96\xaa3\x8eJ\xc2\x8d\xed\xe5\x94\xd8-\xd3jC\xa7\xf5\x0d\x0c\x08@\x07\xaa\x91\x96\x85\x95\xd5\xcc\xbd\x0c1,\xd4\xdd\xc6\xfb\xc8\xa8\x11\xb1\xc7g8\xfd\\\xa1CD\xb2\xa1K\\\x83\xcbKJ!}\x93\xfb\xa3\x1aX\xef\x8e\xbfM\xfc\xa4\x03\x93}`\xea\xee\x99\xedz'-\xc5\x12zMS\xe09f\xe1\x07\x0e&\x9eb\x906e\xe5\xbb\xe3\x03\xe3\xf5\x0cMc\x06a\x97\xb6\xce\xb3u\xd1\x845\xa4\x98\xaa\xe4\x01HyIN\x16\x05K\x0d\xc5B\xcc\xad\xe7a\x89\xf9\x0f0Nr&\xad{\xbb\xef\xe2\xef\xd8w\xa4\xba\xdd\x87r\xf4\xa9\xe2# \xa3\xf2e\xb6Zg)\xc1\xbc7\xbf=\xf8J\x95\x82\x94\"EY'\x90\x91\x88\x11%n\xa69\xf4\x90\x04x\xd8\x8f\xdcu\x0e\xf7\xeb\xec\xef|~\x01I\xffZ\x91\x8a\x9c\xf31\xd4V\x15\xbe\x94\x87^\xab\xfb\x92\x87\xa2\x15\x11\x9d|p\xc4\x14T\x01\xa7<\xc9E\x96G\xe4gl\xa8[\xb6f\xe8\xf0u\xf3\xad\x906\x96\x03\x07W\xfa\xe0H]\xab\xe3\x8b\x14\xd8\x17\xcap\xaeP^Qp\x1d)\x85\xaa\x94 \n\x1fb\xb7\x90\x1b\x90Z\xf3\xd4/\xe3\xe2C\x95\x93\xd6\xa9\xe0 D,\x8cB]\xf3\x18B\xf5\xca\xd2\xc6\xa4\xb7\xc5\xb7\x00N\xa9{ ;\xaf\x0b\xf8\xa2\xe1\xbc\xe2mV\xa5%\x99\xf7\xc5\x0d\x14\x14\xb5fc\xa9NC\xdb\xbe6ae\xae/\x1d\x0dm\x18\xe6\xfa\x1f\xc9: #\x16\xa0ph\x1f\xe2n\x18\xea7\x8bm\x86\xec\xf9\xe3\xf7@,\xba\x1c\xac\xfe\x1b7\xfd\xdb\xb7\x1f\xb5\xfd\x04GU\x9e\xe3 \xdd\xdcu\xa2{\x16\xc3\xb2\x9a,\x98#H\xf3\xcburz\x05\x03\xc2\xd4\xf8\x0e\xfa\xdb\x1c\x8c'\xe3\xdd\xdfuQ\x9c\xf3W/?\xbe\xfat\xf9\xe3\xfb\xcbw\xef?]~xq~~\xf9\xe9\xdf\xdf\x9c_\xbe\xffx\xf9\x97\xf7?_\xfe\xf9\xcdO?]\xfe\xf0\xea\xf2\xf5\x9b\x8f\xaf~t\x86\xf4\xa9Q\x12\xd3\x897L*\xd1\x17!\xafu\x97\xcd~z\x14\xfc7T\xb7\xd1I\x8f\xd3\x7f\xba17\xa6\xbb\xba&\x14\n\xae\xb2\xf4\xd5\x97\x92\xa4\x94\xf8-0\xca\xf85)\xb5\x12RD\xe1\x9a\xfcH\xc8\xfa\xa78\xfd\xfc!\xc4\xa4\xcb\x84;\xbb\xb5\x8a\x8be\x98$\xd9\xed\xab\xbfVa\xf2\x1f\xe4\xae\xe0i\x05\xe3d.\x82\xbe\xb0jY^\xb2\xccz$\xb8*3^H\xf28L\xe2\xbf\x91s\x12\xe6\x11ko\x1d\xe6\x85\xfc\xfb\x9a\x94\xe7\xe1j\x9d\x90\xf3hIV\xec;L\xd1\x10\x96\xe4C\x98\x87+\xad\xa4,I\x9e*eo\xe3\xf4'\x91;Z*\x0d\xbf\x18J\xffX\xc5s\xa5\xe0\xc7\xb0$\x9f\xe2\x15Q\n\x99%\x8cR\xf4C\x96%$T;~\x1d'\xeawo\xd2\x92\\#\xad\xd3\x94\xbd\xabVWZ\xd1\xdb8\x8dW\xd5J\x1fn]Fi\xac\x97K\x12}\xe6\xdf\xad\xc8*\x8b\xff\xc6\xba\x8a\x8b7\xabU%\x84~\xa6\xd0>\xe2:_Q\xd6p\xfa\xd4d\xbd\x1e\xd7\xaf\x8fL\xaf3\xfe\xfap\xcf\xf4\xb6\x12\x1f\xef\xee\x9a^\x87\xf5kc\xd7\x05\x7f\xcd9S\xf9\x15\x9d\xdc\xff=\x7f\xff\x8e\xeb\x00\xfa\xec\x19\xec\x9eK\xc2*\x816\xc6\xce\x9b1\xb9-p~\x93\x85\xa4kb\x97\x0d\x11P\x15*+X+\xc6Z\x9d\xf4\xa4\x93\xb2\xa1\xf4:\xedD\xbc\xb8\xeb] \xde\xc8+\x17C\xd6|qy\xe4\x9a2\xfb\xbf\xe7.\xb2]\xaa\xdfj\xdd\xc3\xff\xcf\xde\x9fw\xb7\x8d#\x0f\xa3\xf0\xff\xcf\xa7(\xeb\xc9/C\xb6i\xc5r\x96N\x9c(\x9et\xe2\xa4\xdd\xd9z\xb2\xf42\x8a\xc6\x87\x96 \x8b\x1d\x89TH\xd0\xb62\xf2\xfb\xd9\xdf\x83\x02@\x82$\x00\x82\x8e\xbbg~\xf7^\x9e\xd3\x1d\x8b\x0b\x96B\xa1P{\x85i\x1a\xae;t@E\xb3\xe8\xd8\xaa\xfe\x8d\xbd\xbc\xf70@v4nv4K\x93\xe5O\xef\xdf\xa6S\x92\x125\xef7PO\xab|g\xabr\xe1\x11c*S(VN\xb1\x84,\xe5\x92\xf4\xd9\xbe\xb4}Z\xc0\x8b\x94\x19x\xa3\x8c\xcf\x04oM\x8a\xa6\xde\x93/\x1e\xf1\xfb\xcbp\xe5Q\xccd\x1fe\x14g[\xbe\"\xa6\xf5:\\\x95oB#\xc6 +;D\xf1\xf4C\xe2$\xa2\x80b\x16\xab\x1b\xb8\xa0jV\x0d\x159\xdb\xef\xcf\xa2\x05%J<\xa3\xb1 \x91hA\xefD\xa3\x8d\xf9\xf3\xd9i\x7f\x18N\xe6e\xeb\xc6\x1c\x01\xd2*0J\xc7h\x0dM\xc78{O\xe4^\xd7X#\x9a%\xfe\x18\xc8\xe2$]\xe2 \xc2qn\x08\xef\x03\xa4\x13\xcfcW\xa4m\xc9\xe8\\\xf4\x14e\x05\xdd9\x14}\xe4X\xfd\xf8\x9a{\x91\x13qj\xb6\x8a\x9bu\x97\x10A%^\x87+\x17t2\xa2LJ\xa6\xf9D)\xf2g\xcb\xfdP]W\xe2\xb1\x95\xe5\xa6\x9df&\xd8\xcb\xa0\x12\xd1\x08\xca\x90\xdfa\x97\x7f\xd9\xa8\xcfD=\xabr\xbc\x06\xcb\x9cP\xf7Z\x0f\x84\xa8\xed@\x88D\xa5\xa7\xdd\x00\xf2\xf2n\x1c@\xd4 L\xd9:\xa3d\xf9a\x9e\xc7\x9f_G\xd3\xe9\x82\x9c\x87\xa9]\xe4\x07\x9d\xe5\xce\x04\x13\xd2\x9fJ\xf7I\xc1\x85\xe9K*@\x97Fu/7\xf4H\x86\x0f\x8cyKc\x8fz\xe8\xbfE\x9c$\x8b\xe9\xc3\x1e/_\x8f\xff\xa9\xaf\xe2\xbd\xf1h\x05\x07\xb8v\xb7\xe1\x00\xf6`\x1f!|\x0f\x0e\xe0\x8e\xf8\x9b\xdd\xbf\x0d\xfb\xb0}\xeb_^\xe8\x9dd4\x0d't\xb3\x88\xc2l\x13O7\xd2y{\xc3\xf6\xec&\xf3\x96\x9b\x8c\xa4\xd4?\xd8\xe44\xf17'^\x98\x91\x0d9\x8d\xe2M\x92,<\x12\xc6\xfe\xc1&%\xe1\xe7\xcd\x9a\x12\x7f3\xc1\xc7\xec\xc0\xd9\xcc\xc3t\x83\xf2\xedt\xb3\x08\xb3l\xb3Hb\xb2I\x96\xab\xc5&\x893\xbaIb\x1a\xc59\xf17S\xe2\x9d\xe4\xa7\xa7$\xddL\xa2e\xb8\xd8L\x16aJ63\x8f\xed\xf1\x0dI\xfd\x83M\x14Gt\xb3\xf0\xc8iH\xc9\x86P\xe2\x1f\xf8\x9bi\xb2\x99&\xf9\xc9\x82l\x887\x99'\x9bEv\x10\xcd6\x8b\x8cx\xd1\xcc?`\xf3\x88\xb3<%\x9b8_n\xceHL7\x17\xde\x84\xac\xe8\x86L6+\x0fS4o\x92\x94\xfa\x1bJ\xbcx\x9amPs\xb2Ic\xdf\xf7Y\xd7\x8b\x05\x9d\xa7I~:\xdf\x84\x8b\x8cl\xb0l\xf9b\xcd\x86r\xc1\xa6\x93\x84\xeck\x8f\x84\x939\x9b}D\x18\xd8\x92\xe5&\x8f'\x1e\xdb\xbdl\x80\xa7\x8b\xe4$\\lN\x13\x9alN\xf30\x9dn\"o\xb6Y\xae<\x8e\x03\xd9F\x19D\xecEt3Y\xe4S\xe2\x1d'\xf1\x84\xf8\x07\x9bE\xc4\xa0\x95\xd3\x8d\x14}6\xd4#\xe9,\x9c\x90\x0dI\xe3p\xe1\x1f\xf8\x07\x9b\xcc\xdf,\xbcpy2\x0d7\x84n\x92\xc9\xe7M\x12\x9f\xfa\x9b\xa5\x17M\xd2\x04I\xe0\x06\xf5L\x1b\xaeK\xf07o\xc27\x9b\xd8\x0b\x97$[\xb1\x96B\x1a\x9d\x91\x0d\xb9\xa0\x1br\xbe\x89\x16\x9b\x84n\xf2\xc5\xc2\xdf$\x1e\xb2E\x9b\x15\x8f\xaf\xdc\xa4\x9b\x9cn\xceH\x9aFS\xe2oV^8\xf9\x1c\x9e\x92M\x98\x86\xcbl\x93Fgl]\xd2\x84\x92 %\x0c\x104\x99$\x8bM~\xb2\x88&\xfe&\xf5\xc2\x88a\x8c\x17N\x93x\xb1f\x0b7\xdb\x9cF\x19%\xe9fEB\xba\xf9\x92Gi9\xefl\x92\x93\x0d\xd7\xb3mh\xba\xde0\xaa\xe8\xfb\x9b\xcc;Y\xb3\xc5\x0f\x17d\xba!\x8b\xd9f\x9e\xa4t\x13\x9d\xc6d\xba\x89\xbe\"xB\x1aM6\xa8\xd3\xd9\xa0\xa9a\x93\x9fp\x97\x84M\xbe\"\xe9f\x1dO\xe6i\x12G_\xc9t\x83\xb1\xc4>\x83\xe8r\xb5`\x83\x9f\x93x3\x8f\xb2\xcd\xf7|L\xd1\xce\x06\x87\x11^\xf3z\x8a\xf6\xcc)E\xfb\x14\xab\xfc\xa2AB\xefGR\xbc\xdc\xf4\x86\x99\x06Pw\x06\xae_X\x8b\x8c1\xa6\xd6\xb7N\xf1\xadA\xcb[K\xc6\xd3z\xa7\x01\xc4\"\x83\xc9\x00K\xede\x84za\x00k[\x81\xe2&*H\xa1c\xc9\x84\x8e\\: .1\x19\n\x0fq[\xea\xb9A\x0d\xb1hMU\xdb(\x9a([0\x11\xa7\xc2\x9b\x8d{\x87\x95\x84\xbe$U\xa3\x81\x86\xb8H%\\\xa3\x08J\x80\xf6\xb5l\x12.\x9e\x86\x19\x1b\xd6\x93\xea\x9d\xe7b\x90\xad\xa0\x91\xeaG\x8f\xf6Sn\xe8\xf7n}\xea\x8f\xfe\xd5\xbf5\xfe\xee\xc6-&J4K\x7f\x92~\x16\xc6\x11\x8d\xbe\x92\x8f\xe9\xa2\xb5\x87H\xad_\xabz\xdb0a\xadW\x8b7\xd2\xc9\xd6\x8abp\xa6\xf6\xeck\x8f\xe0SB\x9fL\x18\x97\xcf\xb0%M\x16\x8b(>}G\xb2U\x12g\xed\xd0\xa8\x9dd\xa5\xc2\xbf\x1fe\x8a\xf6_Q\x87\xb0\xa51i\x0c\xaa\xc7\x9e\xfe\xcdR\xbf4\x8b\xe2\xa9\xd7\xaa\xac\x91Wq\xc2e4Li\xf6kD\xe7^o\xafW\xe8#U\x15*\x83\x89\xd7\x9b\xf0\xdd\xc3\xad\xf6\xff\xbe\xf4K,lz\xfe\x01\x98+X\x15\xaa\x1d\xaf'\xba\xe8\x89\xc4\x9b\x1a;\x89\xa1\x8d\x14\x9d\xe64\xe3\xd27\xe2\x17\xca7a\xea*\xb3\xa4\xc5\"O\xa2Y+\xc7\x9aM\x9bx2%d\xb5X\xbf\xa7i\xb4zI\xd65~\xcd\x927\xecZX\xaab\x99[\x94\x81:\xa7L=\xb6ut\xbb\xafZ51\x99N]K\xb7\xd9\xa8\xe4\x8f\xf1q\xb1\xcd\xd4&5\xef5e\xf8\xbf\x19\xb05d\xb1\x86\xa3\x91\xc6\xe4dVh\xe3\x98b\xee\xa1\x17a=D\xd4*\x8a\xc8mv\x87 5<\xa1\x0c\x15o\xe8\xd3V_\x9aU\x90\x91\x86\xec!\x15s\xb1\xa3F\x86\xa2\xdd\xa6\x94\xe2\x80^)\x0c\xb9A-\xeb\xcdp\xddp\xa6\x18\xad\x16\xb4m\xc1)\xb7Z\x94\xd5\x8dMn\xf5P%\xbeU7_n\xdf\xd3T\x94+\x98\x9d6\x83d\x91o\xb1\xd9\x84iM\x18L\xc4g\x1a\xd2\x1f\xa3\x03\xc6\x87\xa4p\xeapX#\xfe\x8da\x8d\x94\xde\x8chR3\xfdU\xdfc\x9bb\"\xfd \xee5\xfc\xfa\xa1\xc8\xbaq\xfbN=<\x05D\xee\x0d\xf4\xb0\xb83\xd0}\xba\x92-\x7f\xbf\xab{\xaa\x0f\x89\xaf\x16_e\x0f\xcf*\x07\x89\n-\xa3\x05\x19\xb3\x16\xf4\xa3\x18\xf5\xe3\x99\x17\x97\x0c\xb8N\xb7\x02\xaa'\x809:\xd7m\xa3\xc1\x01(\"A\x84A\x13\x11\x16Z5\xf2\\.hm\x8d\x95t\xf1<\xc0C\x9c\xe2\xa7Q\x93\x18p\xfe\xad\x9f%K\xd5s\xa2\x8d\xddd\xbd\xac\x95a\x8eb\xc6[\x8db\x8d\xdd\xeb\xb2\xbe%\x9a'\xdf[\x83\xdfc\xeb\xfe\x80\"\x10\xf01\x94\x02T\xef\x97p\x91\x13\x1e\xe8uB`A\xb2\x0c\xe8<\x8cA\xb4\xdck\x8e\xb1\xb9;\xfe0\xf8gv\x18\xd3#\xf3\x98NQ\xe5\x9e\x8aa\xf1\xc6\x9d\x86\xf5Y\xefI\xda~Z\xa0\xa4y\xeb_;\x07\x9f\xa6\xdb\xde\xa7>\xfb\xc7?\x90\xb6\x01EN\xad\x0d4\x04\xc1\xf8\xb8\x0c\xee\xc8\xe0\xfa\xdamt\x0e\x83\x8a!\xe2\x8d;\x0d\xeb\xb5\xceE\xd7mLx*\xd5\xf2+\xd4\xbc\n\xcd\x90\x9bE\x0b\xe24\xc0\x0f\x06\xbfb\xb71\xf6h\x9a\x13N\x1aD\xccR\xb8\xc8\xd4\x1b[\xbb\xca\xdf\x03\xc9\xca\x9bF}\xc2\xbbw\x1a\xf8S\xbd\x8f\xb4\xdb\xb8\xf9`5\n\x1f\xf3\xd8\xc4\xcb.C\xfb\xd9\xe4\xd3\xed68^\xb1\x9f}V\xb8\x0b[VZ6\xef4\xb2w:\xf7s\xb7QIqO\n\x1b}\x9a\xbcJ\xceI\xfa4\xcc\x88\xe7\x07\xb0u\xeb_\xa3\x7f{\xe3\x83\xd1\xee\xce\x83pg6\xfe\xf7\xfd\xcb\x9d\xe2\xef;\x0e\x7f\x0f\xf6.G\xfe\xe5\xd8\x890\xb0\x91;M\xf8\x8d\xd1\x0b\xdf\x9d\x98\x96\xbc\x89\x1b\x9d\xe7]8\x0d\xef\x951t\xa0\xfb\xf0:\x90\xfc\x0e#|f\x08xp\x1e\xdf\x16O\xebpzx\x81\x1e\xc9\xb6\xa5\x9d%\x8bEr\x0e+\xd1I\x0f\xb6u.\xec\xd53\xbc\x19\x9e\xd1:\xb2\xabr\xb67oV~\x9b\xb9Z\x13\xc7\x8b\xac\x1eR\x9e\x93d\xba\x16je\xae`\x8c\xe2\x1ew\x93\xc7_h\xc8:\xbeX.z\xc7\xd0\xf9LyS\xb0\x1e\x867\x17\xe5\x9b<\xc9\x85\xfe\xb5U\xf9\xda,I\x97!5\xbd8\xaf\x8cQ\xec\x00\xc3\xbb\xd3\xca(\xed\xef\x9e\x95\xef\n\xc4\xad\xa7\x1e\x01\x01G\xeet\x950\xa67\xb2f\xe6\\3\x91\xbdT\xcc\x0d\x01\xbf\x8c\xf4\xfd\x83Pe\xf4B\x99\xe0[\xbc_\x15\x9ay\x82\x97H\x16\xd306u\xackJot\x94MN\x92<\xa6&-:\xbbN0\x9c\x8fq$\xcal\xccl\x8d\xb9!\xd4eH&\xa1l\xcb\x8bx\xa6\".\x96X\x06r\xc1\xbe/\xb5i\x95\xcfw[\xbf\xc6\x94\xf1\x92\xf9\xeb\xfe\xf9\xa1\xc1\xc8\x0e\xd2\x00\xd7\xd0B,\xcc\x9e|V\xed\xaa\x9bdvhp\x08\x90\x17O\xef\xad\xd7\x11G6u\xac\xbc\x94\x80\xa7\xc8\x0fD\x7f\xc6/\xda\xed\xcf\xf2\x92\xb4\x88\x1b\xb8{H\xf7 ;\xde\xf88y\\bq\xf6\xe1\xf1\x80c\xe9\xf9\x81\xa1\xfc8h\xf5\xb9 \xb6\xe3\x13F\xd2\xd7\x01\x9c\x16\xb5#0\xb5\xfd\xfb\x00\x0e\xc75\xe1\xd5:\xf6R\xdf\xa4}E\xa7\xe6\x07\xb1\xd4 \xf2\xcfe\xf9 9\xf7w\x82\xd6\xc3,\"\x8b)D\x19\xe6\x0fY\xa5\xc9Y4\xc5\x13@G\xb1e\xa3g\xb6\xc1\xb2\x89\x7f\x85!<\xf3\xa2\x00\xce,N _\xd1\xc4\xc1\xc7\xf3\xd5\xd5\xd9\x00\xc4\x10\xe6\xe5\xd6\x99\xb7\x8d\xe69\x0c\xe1\x0d\x1b\xcd\xdc2\x9a\xe7\xcah\x9ew\x1d\xcd\xb4m\x08\x1fa\x08\xaf\xd8\x10\xea\xa5E\xd4\xeb\xa32\x84\x8f]\x87\x10\x96\x00 \xdbF\xf3\x03\x0c\xe1-\x1bMh\x19\xcd\x0f\xcah~\xe8:\x9aY9\x9aY\xdbh\xbe\xc0\x10\xfe`\xa3\x99YF\xf3E\x19\xcd\x97\xae\xa3\xa9\x1e\x89m\xe3\xf9\xdd\xe2\xb7$/\xe4n\xbc\xdfQC\x1eR\xb2C\x99\x1c\x85\xcd\xaf\xe0\x00~\xf6P\x85\xd6\xcb\x99\xb0Q\xdc}\xc7\xef>\xe5D\xd4\xcc\x17\xc9K\xcc\xf6w\x93\x1bKIf\xab\x07[\xdb\xfc~\x85!|\xf0\"\x0b\xb0qv\xbfv\x18\xe3\xaf\xedc\xac\x1c\x9emC\xfc\x05\x86\xf0\xb9}\x88\xbft\x18\xe2/\xedC\xac\x9e\xd0mc| C8j\x1f\xe3\xcb\x0ec|\xd9>F\x95\xc1j\x1b\xe1\x8b\x96\xa1\x1d#\xf3S\xb0a.\x03}!y\xd6\xa3\xd8\x1b\xf5\"J\x96Y/\x00\xceg\x8f\xfd\x00\xa2\xa6\xa1\xbb\xcd\xd7\x03\x14\xc1\xaam\xdb\xb1\xab\x82I/\xd0I\x82!\x0b\x06\xabV\x97P><\x12\x0fU*\xf0\x02\x190\xf6\xf4)\x13*\x03ap\xe7\xeb`\x1f,\xbb\xa2xJ.\xf6\xa1\xc5g\x90]$M\x93t_\x13/\xa7^\x97\x96x\xb0v\x9cP\x18\xe46\x94\xb8\x01Cx\xdd\x8e\xb47\\pA\x00\xeb\x86+56\xda\xbd5\xfe+\xcdl\nvNI:\x1a}\xbb\xbb\xb1\xc6\xd2 \xc2/\xa8\xab\xd8\xdf0h\xe9\"\xa0\x19\xbco],\x17BwE\x8c\xf2]\xc4\xbd\xae.\x96\x0b\xdc\xb6\xf8\x17\x166\xb2\xad9\xd7\xf3\xb0o\x98\x94/\xbe\xfd\xf7e\xc0\xbe\xbfq#%3\xd5\x1d`\xbdBO\x18\xda\xc7}\xcd\xff\x14%WD\xb9'\xda\x0f\xa7S\xf4M\x0c\x17?\x97O\x0e\xe0o\x8f\x0eX\xe3g$\xcd\xa2$\x1e\xf6\x06\xfd\xdd\x1e\x90x\x92L\xa3\xf8t\xd8\xfb\xf8\xe1\xf9\xce\xfd\xde\xc1\xe3O\xb1pl\x87\xdf^\xbf\x02r\x81K\x0c\x13\x9e\xe2\xf7\x84\xc0)\x89I\x1aR2\x05\x1e\xa4\xf47\xa3\xff\x93\xbc\xa4!LL\xa7\x8f\xa9\xb1\xbd[\x9f\xde\x7f\xf7\xe9\x96\xf7\xe9\xfd\xb6\x7f\xe3\x96\x05\xd9K \xc2\x10\xa2\xd1\xa0\x19\x8c\x08F\xc6B1\x16\x9eJK\xed\xf4)\xea\xcb~{\xfd\xea\x90\xcf\x8d;\x93\xb8\xf8\x80\xb0\x89$\xc2\xc3\xa8l\x8fo\x82\xe7i\xb2\xe4\x1bA\xb4\xd7\x9c\x91T\x8a\x99$\xbb\xa4M\xb2K\xb0\xbcm\xcd\x13&)=a`_\xc9y\x06Pxi\xaaYP\xac\x8e_g\xa2\x0eI=\xa9\x92\xbc\xd8\x12\x94\xe2\xfc\"\x99\x84\xac\xa9~\x86\x8d\x1b\xf4K\xa5\xde\xd2\xb4\xb5z\xa8\xa47\xee\x11y\xf0\x90~\x96\x9fd4\xf5\x06\xbe\xac\x17tS\xa7\x8d\x01\xd5C=\x85(\x86\xd8\x87\xb8^>%\xe5\x8e\x8a\x18g8J\xc7\xb2\xc5!&[\x1bM\xc9$\x99\x92\x8f\xef\x8e\x8a,]^:\xda\x1d\xfbc,\xdd;@u\xa1\xf6\x9d\xc1\x98\xdbU{.\xf8$\xb7us\xcd\x9a\xd9l\xec\xb4\xd5h\x15_\x86+\x07\x7f6\xf19\x12\x83\xea\x8c\x88\x0f\xdb\xd0\x1b\xa2\xb6\xb6\xf9\xb4\x9a\x99T^\x97~\xff\x8f$\x8aqy\x9aS\x13\x19{\xec\x83\x92\xf3\xa9d\xdd\xa0\"n\x17K\xd5yD1W\x04\xd0\xcb\xe9l\xe7~\xcf\xf7\xcb\xbb\xbd\x930#\xf7\xee\xe8\xc6Pf\x10jv\x9d`\xb8Y\x94\xc4\xd9{|\xcb\xe4\xb5\x13.V\xf3\xb0%\x97\xacz\x154\\j\x13\xe7=\x1f\xb7\xd0\x02S\xc1\x85)\xf1\x88\xfa\xccpd\xeb7\xe6\x92\xd0y2\xbd\xf2h\xf8\xe7\xa6\xf1\xc8\xa7\xceLDs\x8c4<\xfd\xb3\xc0Y\x1b\xb2\xf3 5\x98Y\xcb4\xe5\xc6\xce\xe8\x9cT\x94\x8c\xeeQ\x0cF\xbd\x91\xf4\xe6\xa5F\x0f\x11\x85m\xe1\xa5oz\xe5\xdf\xa2\xcc\xd1(\x0e\xd8\x06\x0dt\xfb3\xf5K\x9f\xfa\xff\xd9\xdb\xbdu\x1a@o\xbb\xe7\x8f\xc5\xfe\xd4-\xa9\x91J\x11\xdb\xa6\xd6d\xee\xaa\xac\xa4\xc1\xb1\xa6P\x9a1\xc25- W\xac8\xe5\xb4\xb9\x8ct\xf2\x18\xa9\x8e\xbc\ns\xa9\x143\xa4's\"\xc0:\x8f[d\xcaT:&\xcc\xd9\x98\xd4(\x8d\x96\x9e\xb2H\x9f2\\\xa3c\xb4\xd8\xf4z\xb6\xe1\x1a\x92\xab9\x0d\x93\xc1\xec\xb8\x84\xd9\xd7\xa6{Y\xa0I\xe7\xe6\xd44m\xe6\x9b\xb0\xecd\xf1\xd1\xad\x7f]\xec\x14\xccu\xeb\xb2\x05\xc6\x14t\x7f\xe6\x08\x85\xfdgS\xd8\x976\x85\xf5h#\xecb\x1ba\xf5r\x9f\xca\xff)\x1f\xf0\x94\xdfl\xa7x\xf7\xee\xfb\xfd\x1f\xf2\xd9\x8c\x08\x7fq[\xf5\xa3\xb3\"sSq\xf2\x95x\xa2\xa6\x19\xacX\x8c\xc0%S|o\xc49U\xfe\xe9\x18\x91:nT\x8cr\xca\x06\x89\x94\xae\x1cWjcD\xf59\x0eAaO\xf9T\x94d\xbc\x8bhBL^\x97\xc4\xb8\xbc<\xa4\xaa\x9aL[\xe4K\xe4\x14@-1\xe1c)+S.\xd9zZr\xfdP\xecx\x99\x97\xbe\xaf/\x9b%\xb9\xf4-\xa6\xd6\x16\xc3\xb2\xc5\x17\xae-F\xd6\x16\xb3\xb2\xc5\x1b\xae-&\xed\xb3\xbey\x13\xb6&e\xd3?\xba6\xadI-\xaf4\xbd\xe5mQ.\x87\x8f\x16c\xb7\x06C\xd7\x06\xeb\x898L\x0df\xae\x0d\xce\x1d\x1b\x9c\xb4\xaf\xf8f\x83\xdd:57s\x1d\xdf\xb41>\xf5\x17\xf1R^\x83\x85x\x91\xfc#\xe1\x7f\xc4\x8a3+\xcf\xd5\xcd\xee\xbc$kL\xcf\x17\x8a\x17\xe2)\xb9\xc0\x1b\x19\xbf\xf1$\xcb\x92I\x84\x99!\x00s\xb8\xc4e\x00\x1c`x~\xdc\x97m\xb0\xae\xfbe\x0bl\x00\xfd\xf7\x04k84\xe9\x07\xa6\x19\xf8\xfb\xdf\x8f\x8f\x8f^\xbf\xfe\xf8\xe1\xc9\x0f\xaf\x0e\x8f\x8f>\x1c\xbe\xc3?\x8e\xff\xfew\x8dji\xd5\xfc\xe2\xe5\xe1\xef\x87\xcf\x0c\xaf\xcf5\x1d\xbcyv\xf8\x9b\xf1\x83i\xf3\x83\xb7\xef\x9e\x1d\xbe3~p\x06C\xb8\xdb\xbc\xbd\x86!\x0c\xe0\xd1#]\xb5\xf3S\x18\xc2\x1av@\x93\xaa\x7fi\x90\xf7\x8f\xed5\xae\xf7\xeb\x89$A\xcf\xf9\x9f\\\xa5\x19\x13-?o9\xd8\xb9q\x18\x0b\xbb;\x92\xe4\x0b}\x8bT\x1c\x0dE\x83\xbbn\xdb\xe9=O*\xaf\x7fxh9\x89D\x84\x9bF\xaf^\xa9\x0e%\x0bH{\x98x\\\xa88w\xb0JH*r\x9e\xcb\x94\x05<\xd3\xc6\xeeCLw\x11?\x84h{\xdb\x87t\x14\xf1$\x89\x11\x13\xe8\xcd\xee\xf5\xa9\xd3l\xed\x01\x0d\xaa;:\x06\xa2\n\x98f<\\\x82\xf6\x8f\x8fy\xe9|\xe2\xfd\xc1OW\xf6\xc4\xa9\xe3\xb7\xd6Tb\x85\xf5A)\xe9a\x13\xc1P\xb9\x04\x8f\x1f?6\x995\x84\x92j\x1bb\x11C\xbd\xd9\xc0\x9d\xbd\x07w\x1e\xdc\xfb~\xef\xc1]\x9ca\x19\x99\xf8&|\xa3o\x85MZ\x93\x92\xcf\x04>\"\xcax#\x90\xb7Q\xf1\xe1\x06\x9c?l\xc5\xf2\xeb\xf9\x9c\x0dm|v\x90\xda<\x19jP\x16\x9d\xde\x92Q\x91\x14\x1e\x0da'\xae\x14,\x1cJ\xd0\xd5_&\xf0xXW\xc0\x9a\x06v\xd4\x96\xbd\xf1\x83\x18\xb9\xe3\x86}\xed\xda^\xbd\xaa\x8f\xa1\xbd\x0f\x0e\x80\xab\xc5i\xc4\x986\x97/\xb6\xba\xbf l\x03\x1a\xc5j\xb1\xb4\x8cC\x92\xe5\xe2\x99\xbc`\xac\xde\n\x02\xbf\x9f6\xabT\x83pd\xd6\x9c\x07\xef`\x08{\xcd\xdbo\x9c\xb3\xb6\xf3M\x9d\xa4\xcd6^\xf1\x93N\xbe\xa09\xda\x9e\xc1\x10\xde0\x1cye:\x02\xbe\x1a\x08\xf6<\xca0\xbb\x8833\xfe\\\xae\x94!\x99\xa7\xb4Z\x94\x0b\xc5\xb6\xe0\xa0\xb2l#\xf6\xbd\x85\x8a\xc2\x01\xa4\xc5\x19\x12\x89\xb2\xc0\xd6\xd3\xd0\xe0\x078Mb\xd3\x89\xebH\xab?\xda\xa8\x82uH\x1c\xfd\xac\xe3j\xad\xdcc\x18\xd4\x0fv\xees\xebWW6\xf6\x8b\x9d1\x00S\xd5h\x8a8\xe3\xd4\xc5\xefv5\xe0\xaf\xda\xf4\x1d\x05-\xe7Un\xb5\xc5\x96\xf5\xdd\xfdj\xef\x8e3(o\x90\xd6\x8e\xde`\xedR:ze\xcaM\xa4\x9d\xbb\x92\xb7\xdaiD\xbf8\xc0X\x13\xcc,\xb8\x14\xa7.^Z\xbb(\x92\x01\xa8G\x8e\xdc\x8e \xcf\x95-\x85\xe8>M0]\x83\xb5\x80\xb5\xbc$P\xd1y\xbd\x12\x167\xac\xd5\xe6!\xe7@\xa85\xc3\xfb\x96\xa9^\xd8\xe1\xc5\n3\xd3q\x06\x0d\x92\x14\")\x15 5K2\xe3[.\x0b\xd8\xd3\xcf(\xdd\xf0G\xfb\xe8.o\xeaV\xbb\x8a\xecj\xa6\x083\xc0\xfd\xc5\xb7\xc1\xbdO\x13\x94\xc5$\xc4\xc5\"\x84\xcd\xb5\xa0\x98\x9f\xfd0\xa6\xe9\xbax\x99\xba\x8e\xf2\xc6\xb7\x8dR30\xa2\x0e\x84\x8dSH\x91\xf2V\xe8<\xb6\x1f\xadc\xf3\xbe}pr4h\xe0\"\x14\xef\xd7F\xa6\xfe\xfa\xaa\xa8\xaa\xa8&\x1f\x81e\xb0\xbd\xd1\x918\xa0\xc75\x05t\x00_\xfb/\x0f\x7f\x7f\x0fCx\xca\xfe\xfe\xe5\xc9\xab\x8f\x87\xec\xd7\xcf\xec\xd7\xe1\x9b\x0f\xef\x8e\xf0\xe7\xbb\xa0\xd2\x7f\x14g+\x9e\xed\xbc6\xaa$O\xab\x99\xb9m\xf4\x85\x1d\xf0\xe6\xdc\x0bJ\xcb\xa3g\xe3\x0em\xd6\x1b\"\xdeK\xae\xb7x\xd9Of\x8e\xed\xbc\xf4\n'\x92\xc6\xc0^V\xa7L\xbe8\xb6\xa9\x1b\xdb\xcb\xab/*\x82\xef\xf8\xb84\x8e\xb2\x91\xfc\xbb\x17@\xef\xb2i\xcfQ\xfb\x99\x84\x939yG\xb2\x962\xc7JW[\xbc/\xfc\x10d\xc5\xafB\xd6\xfb\x18\xe3\x83)\x17\x06\x957\x87\xfc\xc5\x12\xeb\xcb\x8a\x0f\xa2\xfc\x99\x14\x1c\xcb\x8f\xc4\xd9\"^\xb0M\xa3\xe8\xdf%\x86HLdB\xcb\x82d\xbc\x02\xa8K\x0f\x89S\x00\xbe\xe8b\xd6\xda\x05\xf1^\x04\xf0\xd2\x0f\xe0Ee\xf1%\xbdu\\\x13=\xa6\xdf\xe0-\xdfp\xc7\xf4\x1b\x16L\xbfQ\x19`II\x1d\x9b\xd6\x0d\xf1\xc65#\xfc\x88!\xfc\xb8\x89\xf07\xae\x19S\xea\xb5\xdd\xf5=|\x13\xa64\xbb \xde\x8f|=\x7ft_\xcf\x1f-\xeb\xf9c\x8dr\xd1o[\xcb\x97\xfd(\xe3-D\x94\xfd\x92\xda[\x86\xdeB]\xcb\xc6\xaf(ro4\xb5\xb7?\x05\xf0\xcf\x00~\x0b\xe0\x1fM\xa5\xe9\xfb\xc3\x7f\xa0\xc2\xd4$9Rj\x11\x1d\x8fCQ+\x83\xd6\x88M\x17\xf6\x95\x18z\x90\xfc\xa50.}&\xebL\xcbC\xf2\x91$\xb26\x88\x1c\xca\xf1gQ\x0b\xab:4\xd2eh\xb1u\xf2Q\xa9\x9f7\xcc\x9f{\x16:+\xe8\xd2\xf6\xee\x84\xe1,\xa8\xdd{*\x0e\x83zm\x1fCG\x91\xa1#y\x16\x95\x06\x8c\x7f8\x1aX\x90\x1b36\xf8\x13k\xcd\xfbI\xe8Z)\xf5F\xe3Ff\x16}\xbby\x0brh\xd2\xe0\x88.\xa8\xdf\xe4\x9a\xbf\x94o\xa4\xfa7~(\xdf\x88\xf5oh\xa5\x9c\x83R\xc8)TOf\xcf\xbe\xabK:\xa3\xcf\x01\x9c\x8dAd\x8a\xed \xf1t\x92Y\xc3\x16\xa0gza\xee\xdb\xa7\xc7\x05\xb9k\x9aEfG\xf2_j\xd8\xa2A\x0f\x0d>\x14\xab\xeb4\x04v\xc29\xa9\xcb\xa8`\xcd\xf4@\x8dL\"xa\xe5H\xd8\x01QZ6\x06\x01\x864\xef>\x84\x1c\x1e\x0d!y\x08\xf9\xf6\xb6\xa9\x11\x10\xe3\x08\xd1S8f\xa2\x15\xec@\xced+\x83\x7f\x15\xc8\xc5\xe6z=\xe2\x85\xa3\xc18@\xc5]8\xda\x1d\xb3/\x03P\x02\xdas\xd8\x86\xa6\x12\x0e\x1a\xe2\x97\xbc\xe4g\x8d\x87\x96\x04s\x0dV\x99g\x83tZ\xa6\xd9\x9f\xbcL\xda\x152B\x96\xaf\x9c\x0d0\x0c\x1b\xbfzV\x96B^\xd2\xf9\xc3}a%\xf0\xb7\xb7\xe11:W\x9b\x1b\x077u\xa7\xbc\x8cjOy]\xc2>\xc7\xcc\xb9P\x1f\xa9i8s\xfbp\xa4E\xbe\xe2w5\x94r}\x8e\xf4z\xa8\xe9\x93j\xbe,\x03\xb8\x05\xbb\x85?\x8b\xf0{\xf1\x03\x89\xce\xf2C\xdb\xc1\xf6\xcfbh\xff\xd4#\xce?\x85\xcd\xa0e\xab\x99\xa0u\xda\x02-\xaa\xaa \xb8\x8a\xc0\xd1WhIm\xceB\xfa\xa5X\xd6\x96BiC\xbf\x1a\xa7\xd4\x13\xaeV\x01\xf4\x9e\xf2(\xde\x8c\x92\x15\x84\xf0.\x8cO \x9c\xaca\x17\x83\x1eAX'w\x83\xea*\xc9\xba#\xb8V~\xa0$\x01\xe0\x9eo\xa2\x1a#.ax\x92\xa1\xeb!\x81G\x82cco\xef\xc4\xd2\x84s\x8c\xc5\"T\xbd\x1f\x89\xa7\x8aj\xf3\x18\x87\x86\x83U\xb1FE\x0f\xfc{B\xa2\x85\xe7\x11\xd8a\x04\xf8\x16\xc4L\xb4\xf2\x99l\xde\x0dw~+`\xf9\x9b\x1ew~\xfb6\xdc9\xd6\xeb\x129\xbe(*\xa5'\xa2\xfaa\xdd2ah\xf6\x84\xda\xdcL\xcf\xadO/\xc4S\xf5\xa1b\xc6\x1a\xfdc,\n\x01\x11\x8f\xd2\x00n\xb0\x95S\xe3\x1eN\x89SIW\xc9\xb5\xb3U`\xe4\x91\xdb\xb4KM\xfb\xe8\xad4g\xf8c]\x05\xf3J\x9f\x9dL2\x15\x7fY\xa5G\xe1![Q-\x95\x1e\xb2CH\xb9\x8b\xac\x11W\x84\x8a\x88z\xf1\x88Q\xae\x14v\xd0\xa3+\x1a\xa3\xf0\xc7:*wf\xc4P\xd1H\xb5\x1bu\x1d\xb4\x93u\xb3\x0e\xe9&\xaa\x9dBc\xf2\xfa\x89\xea56\xdd\xb45\x05\x10\x1e\xa3\xfa\xc3\xc6\x819i\\\xac\xda\x16\xaei\xa1\\\x02/Wf{\x9b\xad\xcd\xf6\xb6C\x14 CuB\x03x\xc1\xe8\xd6\xd5Q\xbd\xee\xe5\xaaC}\xae\x1f\x1eQ-\xcaW\xfa\x9e\x87\xee\xf1lJ\xd3\xf5(wM}\xa2\xeb\xdcX\xbcS\xbe\xb3JSU \xd8ju\xa7%|\xa7%l\xa7E\x0f!1+q\xcfDY\xbc\x14\x173\x82\x1dH`\x1f\x12\x83\x9e\xaf\xb63\xf31V!\xae\xee\xc6D\xab\xb45\n\xa3\xcd\x14\n\xd7\xb5=\x05\xb8\x8c\xfbS\x01\xa1qw\xa6\xad{8\xb9\x8e=\xdcm\x15$\xe4P\xd3\x1a\xfdu{>g{>w\xdb\xe3\xca\"\x8e\xa6\xe5!\x17\x8bC.\xd6\xee\x8b\xc2[\xc5a\xad\x19*\x96\x121\xaeeEhR\x84\x0c\x03\xf7,\xb1\xe5w\xafj\x96\xb5\xd4\xb02\xe8$\xbex\xb1A\x06-vq\xf4\x10\xb6\xbc\x08O\x05\xb5*#(\xb9\xbc\xbdHT]\x84t{[\xec*]\xfdR1\xe5F\x8e -LK}\xf5\xb5\x025I;C\xd5\xa0\xce\xf9\xa2j\x89\xf9v\xf9hh\xd6\xb0\x02\xdd\xb7\x1aQ\xd6\xa1E\xcb\x81\x8b\xc4\x9d\xd1q\x0f\xe0\xd2\x08\x15\x9e\xd3F\xf0R\x81\xf2\xe9\x7f\x01\xcaW\xea\xc8\x17$\xb0\x08!\xe0\xb6\xaa\xa6\x83\x80z\xa0\x14\xc6\xa8\x87\x0e\xcc[4J\xc6\x01#T\x8dC\xc206\xb6KbEK\xc4w\x89\xb1\xf2\xbc\xa4\x9b\xb1M\x9b\x84&\xb6Q2\xe6\xe1\x90\xc5\xd8\xf2\xea\xc0NR\x12~n.\xa8 \xdb\x1a\xc7\x96vy\xffc\xbb\xaf\xb6\xb0F\x82\xa6[l=\x10\xafc\xef\xe1J\xc0\xe3\xf2XmS\x18\xb6oT\x90p\xe3En\x8b\x8dkQ,\xf2\xa0<\xb1\x87\xb5\xafY\xad\xcb\x92\xfdMG\xee\x0c\xefZ\xd0\x805\xbd\xba\x8b]M\xd0\x86\x03\xe8\xbd#+\x12R\x18\x8d{\xb0_\xfe\xe2^\x10\x8aZh\x1bz\xe5=\xfc\x96\xdd\xa1\xd1\x92d\xd0t:^_\x9d)\xd71\xe1|\x08\x1a\x06\xbc\xd2\x8f\xac\xf4\xe3\xca\x85O\xa9\xaa\xf8jFe\xd5\x9a\xc7\x94\x05.\x13\xa9\xec\x1f\x06*#\xca+1{|\xaa\"U\xd2\xba6\xb2\xd7\xa2\xba\xe4\x0e\x0f\xa6\xab3\n\xf5\x91\xa6\xe4\x8c\xa4Y\x177\xed\x16\xb8N\xc9\xc5\xdb\xd9\xd5\xc1\n\x07\xa81\xdc\x19X\xbbY\x84\x19=\xba\x86\xaeJ\x0cm\xed\xf2\xea\xc2\xd4\xeeC\x88\xe1\x91\xb2\xc4\x10;i\"*\xc3\x8d\xeb'ZlUB\xc4Ns\xe9.\xe5tbU\xbb\x11k\xc9f\xc2#\x88%\xc5)Y\xa0X@\xc27\xd6\xd9\x83\xeb\x12?\x1c(l\x05\x9a\xc2H\xe9\x88\x87\xb4\xaaz\x87\x83&f*S=k\xda\xfb\x19}_\n\xfa\xbe\xbcf\xfa\x8e*cI\xde\xf9\x0f\x85\xbas\xed\xee6\xf4\xfa\xfd~y\x97\xc4S\xd8\x06O\x08\x15\xf3B\xcd{\x00=8YW>'+\xcc{\x84I\xe74'\xc1\xf2zO\x029\xdcR\x17 \xdfU\x87\xd28#\x96W:#$\xe7\xe0Q\xd8Q\xfb\xf6\xe1\x96\xd2\x9fq\x7f`\x80\xf4.7\xc8+d\x82\xdf`k\x84:\xf1\xd9\"\xd1\xd8\x1ejCv>wj\x87J\xd1\xa9r\xb8\xa0K\x01\x9e!\xe5\xd3\x80\xdb\n\xf0\x8c)\xef\xfa\xf0hX\xf8\x96.\xa9\xb7\x1b\xc0\xae/\x8e\xa7\xa5@\xeeSB=\xd5* M\x06\xec>\xd1\xdcG\x905\xcf\xae\xe5U\x0e\x9b\xb3\"\xaa\xb2\xb2B\x0d\x85/\x18\x031.\xc3\x1c\xd4r\x07V\x87\x03\xe1Z\x89N\x96\xece\xeeSa\x19((x\xba\x0b\x1b\x93s\x14\x1e\xa1qY\x8d\xd3\x8b\xe1_C5G\xd1w@\xfd\x87\x0c1\x94\x9b\x0f}\xc0\xd7(\xdcR\xdf\xb5\x12\xdcC\xea9\xa5J\x8f\xea%]\x145b\x99\x9a\xffg\xaax\x99\xeb1\x0d\x94UxEG\xd4\x9e(\xb7\xea\xb1\xf2\x96ao\x00o8\xac\xdf\x89\x9c\x19\x14\xd3\xe1\xc0+\x9e\xe8\x1c\x9f3*\x8e\x8d\xb3\x83\xef*Y\x16`\x9fw\xd6 \xc7\xe7a6\x7f\x9aLU\xc8\xc8[:\xe5bT\xaf\nV~\xe8\x08B3\xe3\xf9\x9a\xd6\\M\x11~G\xdccM\xadPji\xa3\xfe5\x1d=\xa5c\xa7/\xb7>\x1b\xc7\x0d\xa6\xc6\xfb\xa2\xea\xc1\xfa(;\x8c\xf3\xa5\x08\xc0Bw8\xdd\x13\xa7\xb1\x98:k\x07\xaf\xfa\xb5p\x98\x8c\x93)\xf9\xb0^\x11@\xd2\x9e\x9dG\xbc\xfeYq\xbf\xad)vM\xc2\x8c\xc0`\xbf\xf5=Ph\x7f?\x8f\xa3/99zf\x9e\xa3\xbc\xb0\xf9\x07\x1d\x9b\x9f&\x13\x0c\x18>\\\x10\xf6\x0f\x9fl\xedf1\x06k\xd3z\xa56\x88-\xa5\xac\x96\xf6=\xfd\xd7l\xb9\xb6\xb7?\xd0@=\xfan\xc2\x07\xbe\xf7?\xe0\xde\xb7\x84\x88\xbc\xa6>\xc3\xfa\x8c\x18=\x1c\xc1\xc1\xd1\xb5\x8aB\x7f\xc8\xfa\xc8C\xfc\x81.\xcfu\x8f\xc1\xde\x9b$\xde!<\x95q\x19H\x98A\x98\x12,\xfa\x86\xd9\xb5\xc9\x14\xc2\x0c>\x93u\xd67\xd5=\x90\xdd\xb3\x0d%\xa2\x8dy9\x89\xd2#$\x80\xa7\xd4\x14W\"/R\xec\x9b}\xd8\xb2\x04x\xb1k\x92\xc4\xb3\xe84w|\xfb<\x8d\xa8\xdb\x9b\x82O\xd7/>\x80\xb9\xa4\x1e\xa8\xe5\x0d+N\xf5\xddH\x86`\x93\x95H\x12\x85\x83\xd7}\xe0\x1b\x1b\xb2\xab\xdb\xd4K\x95\xb5\xdd{\xee\x87\xab\xd5b-\xd8xCD\xbfz]\x06\x162\xc9\xce\xc0\x16\xc8\xb6\x13\xc1\x8aSzI\xf2\x1ax\xff1F\x08\xd1\x042B!\x84\x98\xed\x83\x12rr\x8c\x90\xc4bOXQ\x9f]T\xce\xc1<\xfb\x0e\xf4\xc4z\xeaw:\xed\xa5\xf2\xb5 k\x8caP2\xdah\xf3\x01\xd4\xa0\xc5\xcb)\xb3&y\xfddT\x93\x96\xa5y\x18\xf7@\xa6}G/\xd2\xb7\x06\xde\xbeP\xc7\x10\xce(\xa9\x16\niiG\x03\x05\xbep{\x00\xdf\xf1T\x85\xfd\xc9\x829\xf3Ld\x15\x16\xd6\x97)\xdc\xbdu\x9d\x11\xfcW6_r\x85\xa7\x92\x01\xeau\xb82\xa6<\xfb\xfa\x8d\x96\xc5\xe34IJ\xcd,\xfb\x81\xa2s\x11K\xc3\xf36\xf9:\x93b\xa5\xeb\xacS\xd7\xffP\x93B\xd9\xe7\x94\x11z\x14wh\x1a'\x92\xaf\xa6!%G\xf8\xf22h?c\xcd\xdc\x92}p)Y&g\xed\x92\xb6f\xd6K{\xc3S\xb2 l\x02\xaeM7f\xed:\xe5e\xd7)\xf3N\xea\x0bbO\x1c\xcdE\xc8F\x89\xcb\x03\xe1\n\xe2K\xe3L1\x81\x11\x1d\x8bF\x1d\xc6\xd2D\x0f\xc3h0\xd8\x15\x9d\"E,&Gq\x8b\x8flA\xa2]\x12I\x9c\x898P.\x80-\xcd:\xd1\xbc\xd5\x17\x8f\x91\xbb\\\xf8\xe1\x99\x89\xe2\x99H\x19\x93`\xf0Hk\xc5\xd8\x0c\x86\x10y\xb6\xb2\xdcb\xb92\xbe\\\xc2Y\xb7\x19C\x06F\xa9\xe3\x94z \x03\xb2\xc8\x1b\x9c\x11\x1a@/\x8ay\xb5\xfb\xcfd\xfd3V\x883Cf\x82%\x80-\x1e\xa8\xec\xa5\x99\x98\xf2\x92M\x19\xa9\xd5\x84\xed'\xf3\x07X\xa0\xd4\x9b\x95\x0bhU\x94r\xd6e&f\xcf\x7f-\xd9/\xb1\xdb\xbd \xc3W/)y\x19\xe2\xe3\xd91 `\xa1\xe1\x01\xc4\x9e\x8fc\xd4\xe9\x1a\"\x1eE\xdfi\xd1\x9b\xe0\x9a\xea\x96\xd9\xfa\x0e\x98,Hh-J\xa44\xdet\x8b\xa1\xdc\x1fB\x1c8\xc9yL\xd2\xa3gp BaE\x0c\xe3n\xa0\x9e\x14CQ\xb4S|\x83\xc1\xfb\xc3\xf2\xac\xe0w\xc3\x05\x15\xf5N\xb6\xc4M_pw\xd6\xc9,Iz\xda\xaat\x90\x90\"\x02\xae\xb2ks>\xc0f\x1f\xbfF\xd5\x92c\xb6\xf3\xa4\xe8\x08\xfd\x97\xea|\xd2\xa0\xe9\xc8\xd1\xec\xaeJ\xa0\xec\x86pM\x0fFl\xa9\xd2L\x12 \x84\x03\x07\xad\xaf\xf8\xde \xf0\xf3e8\x90\x7fI\x1d\x0d\x12\xd5}\x88Gj4^\xb3\xa8m\xcb\xf1\x81M>#\x18,\xdbi\x9d#\xd2m\x8dY\x1fN\xeb|%\xd0\x17\xc3J\x88\x87b\x85\xe3\x88\xfe7\xa2\x02\xae\xd6\x81\xfa\xebzQ\"KR\xea\xca\xe7\x1c\x11\xef\x17R\x98\xfd\xdb\xdb\xfda\xdd\x81uT\x1b'\xed\xedWd\xa0\xd6 \x14\xb2\x16[\xa90{\xcdu\x11:\x06@.)\"\x16\xe9\x9f\x87\xd9\x13NO=\x1f\x8f\xa1\xe3c\x12gyJ\xde2z\xedU\x89\xb7d\xa5\xac\x03/zw\xdc\x83\x8d\xf3\xa1zn\xa8\xa3a\xa2\xd8{;\xd8\xc2\xecHjb\xba\xf5\xaf\xf6\xd3\xb22\x05\xc8\xba\xf5 \xce-k\xdb\xdd\x1c\x9c\xa4F\x84\x9c\xc3\x0dw\x99\xa7\x93\x17\xda\xb7:1+\x87{\xe1m\x83r`3\xb3H\x0b\x11\xe1\xc1v\x1e\xc1\x043\x043\xca\xe8l\xee\x01/\xfb\xd4\x02\x01e\xb5[\xf7\x96\x9cI\xc9\xe0\xe8\xb0\x15\x0e\xe0\x9f\xb4dmT\xb6&(\xf3: K\x83\x1c^\xad!%\xf7\x83\xca\xe0\x0c\x04\x83\xa3\x99N\x941\xc9}\x08\xcf5\x9eC\x1fi\x00?\xd0f2\xe0\xd7O~6TO\xfb\xc2\xdeV\x81dR\x0f\xfenN\xfc\x81\xc3oNH$*j\x18\x1f\x8c5>\xac @\x0c\x9d\x9cDt\x89\xe0\x90\x90\x8f\x13\xee\x82\x1c;\xf5\xf9\xcbU\xfa\x9c$yL\xaf\xdc\xe5\xcb\xabt\xf9\x99\xac\x7f\xe4L1i@\xd7\xad\xdb\x17\xd7\xd7\xed\xda\xb9\xd3\x1b\xed\x9d\x1eS^j\xb4\xdc9E\x84M\\\xfa6\x87\x93\xcf\xc8\xbc\x14\x14\xe5'\xea\x89_n\xda\xd0\x1f[S<\xf2\nH\xa6}\xac\x0b\x025!\x0f\xad\xa9,$fGAA}\x10u\xa9FM\xd1\xd4Q\xf8X\xe4\x0c9\x84\x08w\x9bN_a\xc0G\x11%^\xe8\x97\xf8\x82\x06\x10Zy\x15&Qq\x89\xcd\xd3~\xba\xcf\x10Q\xac'e\xfc\xc8\x85\x17\xfa\x01\\x\x0cU\x18\xc4_\xc8\x1c\xae#\xf6\x99k:wB\xec;\xbeVy6\xf74\x9eEF\xf2\x92K\xa0En@\x8e\xac@.v=zm\x95j\x95\x9b7\x01\xb3\xb0V\xd4+<'c\x91\xd8\x97o\x7f7\xce<\xb1\xef\xeeR\x9433\x15\x002\\\x0cu\xf8Ue\x1a\x8e\xb7\x92\x8c\xba\xf2\x9c\xab\x84\xcc\x9ax<\xb9\x8a\xce\xadjx\x9e\x8d2\xf2\x85\x1e>jY9\x13@r\x97e\xe1\xdb\x1c-Cq\x7f\x16\xb1\x93\xc1\x01\xfd\x8a\x8f\xcb\xc4\xb9\xcdA\xfa\xbeb\xedb\x07\xb2\x9af\x17\xe9jy\x8am\x18\xa9\xc0\x94\x87\xca7W7\xb5\xa7\"\x1a\xaa\xf8\xc4\xb6\xe2\x80&pq\x1e\xa5U\xabi\xab\xf7pE\xfe^\x8a\x1a\xa3\x08x\xec\xd2\xf8\xad\xc6e\x02o\xabA0\xa6\xa5\x93\x17\x95n\x19\x86\xf4\xb1\x97\xd5z\xd2\x05A\xc3\xb2\xd2\xf1(\x1a\x17\x0e!\x9a\x81bf\xf2\xca\xd1\xe7\xc5\xa3]G\x89#l9iA\x84\x86x\xf7\xef\xde\x7f\xf0\xe0\xf6\x9d\xbb\x0fx,\xcf\xce\x10\x03ax\x1c\xcc\x9d\xdb\x83{w\xef~\x7f\xef\xae\xef3f\x0f\x1f\xec\xc1M(\xbeQ\xee\xdfa'\xd3\xde\xdd\xbd{w\xee\x0en\xdf\x0d\x80\xc2\xb6h\xea~\x00\x83\xbd\xefy\xf3\xf2\xde\xe0\x9e\xdb42\xe2(\x85\xa4\x02\xc5\x0fm\x15E\xa3\x11\x19\x0b\x01\xa3\xd6\xbb\xfa\xeb\x0b\xba\xba\x08\xde\xec\x0b\x15\xe6p\x18\xb2\xbf\xb9\x15.(\xffD\x9dz\xf1\xd2Q\x1c\xc0\xef-N\x11\xe6\xb9T\x0eCUz\x17\xc7\"g.\xa2\xf2X\x84G\x90\xf3\xd3\xd1HH\xa7\x88\x9e\xd1(\x193\xd4)s-\xb2\x1b\x03\xe7R\xe6\xb5Y\x19\xcd\xf0*\x1fi\x9d!\x16\x1b\xe1;6\xc0\xd3\xb9:\xdd \x9f\xee\x0c\xcfc9\xdd <\x02\x8cm\xda\x9abB\xe0l4\xc1I=\x84\xc9\xf6\xb6\x81![\xc0\x90\x7f\xa7\x17\xc8\x16p\xc0\x9b\x19\x8cq0\x11\xec3\xeeWQN\xea\xbf\xe3|\xb0\x17\xa2g\xd4\x02]\xc9.\xbc\x84IQaIH\xb3\x96\xec8\x18\xc4\x81\x0e~[!\xfb\x7f\xe1\x9a\xf0x\x08\x13]\x98\x8a\x15y\xe4\xc5\xa5Z\xe9\xb1\xf8\xdebp\xaf\xa0\x9b\xe0\xfah\x00\xe8\x88\x1a\xc0\x88u4\xf6+\x1c\x19q\xe1\xc8\xe4%\x9d\x0d\xc8\xc8\x94\x00O^\x11b\xb5 \xff\xb4\"\xa2\xe6\xa8h\xc9\x8d\xd5?@\xcbE\xc9K\"\xbb\x9e6\xb3\xae2\xabQ\x9eMa\x05\":LQ\xf0J9\xd3\xd81\x93\xf7V\x0c\xb7\x90\"em6\xff\x03\xe4\xaf'\xc2\xf6\xbf\x03\x038\x80y\x7f\x95\xf0J\x10\xf3\xd1\x84Q\xa3\xc6\x8d\x11\x1b9\xe3\xc7\xe7\x9c\xc1\xe4\xbf\xfd\x00{\xf6j\xda\xbfyi\n\x97\x02s\x00\xf36\x96\xf42\x80_\xafL\xce\xb4\xd1e\x88]\x86\xcd\x8aB=\x13W<\xafZ?\x9cG~R\x94}\x0c\x9a\x91D\xd2\x10\xae\xe95\x126\xd60\x93snr\xee\xae\x08\xcdF\xe5\xec($\xfc\x11fF\x1e\xf38..#\x11\x1d;Q\x07\xcf\x95\xe9b%3\xb4L\x00\xfd\x84z\xa9 T\x8a\x80H\x04\xcb\x13#\x90\x88E\xaa\xcc$|C\xfd\xf3I\x15\x86\xfa\x97f\x18S\xb95\x04o\x027A\x87\xdaH\xd7\x90PGue\x8e\x96\xa0J:\x1d\x12\xde$\x02_\xdf\xf9J\x8e\x10\x97K\xff\x0e\x1a\xdd\xe1\x00V\xa3\xc5\x18Z\n\xb1sE\xd9\x9c\x9b\xc5\xf8BW\xd7J?;\x1e%>w8(8\x1c0\x94|\xa5\x90\xf7\x99\x95\xbc[\xdc\xbc*\x15\xbf\x04C\xc0\xf63\xaf7\xb3\xf6\x03\xc4\x8c\xdd\x87\x82\xd5\x8f\x1fB\x88i~\x18n\x0ca\xe0C>\n\xc7\x88\x067Q\xb3@F\xc9\xf6\xf6\xd8R\xb3\x0e\x14\xa1t\x94\x8e\xb9\x8a\x8b\xf5\xc8M\"\x98\xe3A\x1f\xcc\xcf\x1e\xaf\x02\x98\x04\x10\x0605@R\x9c\xe7\xec\xffj\xb9z\xb5H\x7f\x93*\x11\xb4x\xb2\x04\xb6\"\x12\x0df\x81c\\\xeaWxS^q\x0eRQp.W\x88?{k\xe03V4\x1fc\x9ck\x0e\xdb\xc6\xd4\xb8\xd0~xs\xa8iA\xd6\xc2!\x15\x1c\xb6\x84\x9a1M \x14\nu\x84\xda\xb6@\xaa\xa8\x84\\!P\xb8\x80.\xa9\x80\x8e\xab\xd6\x10tb\xcf\x86\xf0\x08\"\xdc\xb1>\xbb%h\xbb\x97\xf0-\x1b\xf3\xd7w\x06\xa8\x9d\xe5\xf7\xe8(\x84m\x97rn\x86\xc2\x1f*\xee\x19\x8f\xcc\xe3\x82\x9d(\xac\xa8'5\x93\xe6y\x95\xbb\xe0&\xda\x93\x00\xce\x1b\xe7\xe5/\x7f-;aa$Z\xf8\x08\xce\x10Df\x11)\x81\x03Ht,\x82\xceo\xf2\x97\xffel\x82\x94\xcd\xb4/L\x1cNa\xc6&LF\xa1\x81Lg<\xf8\xc6\x911\xa0\xc4\x9bu=\xa2\x85#\xadC\x0f\x05O\x81\xf6z\xc3\xb1\xd2.\xc3\xed\xec\xac\xe0\x11,\xae,\xb7U\x08\xecn\xa0?\xe0cy\xc0s\xa1y\xc0%\xe5R,c\x14d\"\xce\xfc\x0c\x1e=\xc2#\xbf]L\x9b\xa1\x98\xa6[\xac\xca\x9beT0\x1e\xb3!\xfe\x89\xb4\xd1\x8b`3d\xc2T\xce\xf9 \x06yc[\xad\xf2ZIB\"-k\x01\x92\xbd\x98 \x87\x11\x1a\xcd\x8c\xab\xedm\xfd\x9a\xcf\xbb\x9e\xf2\x8cS\xcc\x88\xc7\x99\x99\x05\x93\x9c\x8cta^\x90K\xe9\x00\xb2\xaaQ\xcbi\x95ZrNj\xc5\x98\xa4:\xd9xyej\xf9\xdf\xacKz\xf9\x9f#\x86\x82\xae\xe9wy\\\xe6Z\x14\x86\xbab\x8e\xa1\x92\xc0\x8f+\x7f\xb8\xbe'&\x8a_\x1d\x0eZH\xe1\x9a1\x14K\xf2\xff }WXr\xee\xb3\x8a\xd5\xf4E\x99\x97P\xc0\x92M\x80\xb1\xee\x13\x93\xf1\xb4\xb3\xa6\xa5]\xcb\xf2\x1f\xd4\xb0\xbc\xd4\x00`\xde\xd8\xe0/\xae\xbc\xc1\xa5\x18\xc3\xa3B\x0b\x9f+\x86 2\xa2\x8e\xdf\x18\x8cu\x0c\xc9\x8b\xeb\xd9\x835U\xaev\x99\x90\xe4!\x06W\x87i\\./\xc3\xea\x19\x05\x12(\xf3\x08\xfd\xc6F\x0ce\xc0\n\xc3H\xd8\x87\x0c-\x01Z4\xaa\xac\x1a\xb68,\xca\x10\x89e\xd3\xe1\xadXv\xde\xa5f\xd7#\xd1)w~c\x91+\xba\xf3\xd2\xb9\xf6\xa5\xfeve\x0d\xac\xa4=n\xd0\x91\x94\xd3\x91\xa8V\xb6\xe8!\xa4\xa2\x84L\xea\x94\"9.\xea\x97\xa0\xe7\xc1X\xadwY\x9f\xdc\xaf\xfaY\xfcrm\x93\xe3L\xa6\xdb\xd4\x0c\xbcN!|\xd5\xe6\xa5\xe7w\x18(\x12(\xb3\xcf$\xfdJ9\x06\x13,@\xa7=}qE0H\x8a\xac\xa0k\x03\xad\x88w\x83\x06\xf0\xd5\x0f\xe0\x86\xdaKL.ZS;\x14P\xa6\x12\xca\xe8_\x19\x94A\x02\xdc\x99\xf2!\xd8\x8b6\x88\xfa\x13\x04\x17\xc9\xac\x0e\xc7\xd4\x98<\x0b\xaa\x8e#\x03)f\x8b\x89Z8\xd6\xa8\xa8\xadZ\n\xe1\xdcg3\xd5AI^\x97en\x9bT\xee\x96\xb6n\xb0\xbe\x99\xa8b!>Q\xf0\xce\xd7v\x1f\x91l\xc4\xc1'\xddS\x0f\xb0\xcc\x1e\xafy\xd6:6\xb5KD\xfbj\x87v\x95FR~f\x19\x83]\xd1\x91\xb4I\x0b\xf8\x92\\\xa6\n\x00\xe4]\xbb\x0cQ\xc3/\x18\xc2O\xd4K\x8c\xf6s\xb0\x8a\x0b\x93$\xa6Q\xdc\xa9\xf8C\xb3\x7f\xe5W\x9f\xfb\xcc\xb6\xecj(\xb7\xa7ic\xb4\xe6J5\xe6I\xad\x11\x90*0\xd9*c\x1e\xea5\xdc\x82;\xcd\x96g\xf2\xd9^\xf3\xd9\xa2\xf8\xce\xe4\xb9\xbf2x\x0c\x9c\x89\xd8\xa1\x0bc~=\x87<\x96\x9a\x88Z\xf6\xe5\x9cxJ\xcaI\x8d\xf0-O\x82\xc8\xa3\x96\x0c\xa3\xb1\xbd\xc6\x03\x1fL*t@\xde3~\\\xa7\xf0\x98g\x8dN\xe1\x11\xac\xe1\x00\xce\x89\xb7\x8b\x0c\xcfY \xe2L\xb1\x10\x04\xf1\xe2>M\xb8\xfc\xedcYZ\xd2\xd9-\x06\xfdD\xdeG_ \xf6\xacI\x03\xd2\xa6\xe9-4\xb5-\xfe&:/\x127O\x8b\xb9\xddaD\xc9\x032%-y@\xd8ArN\x19\x9bL\x1c\xf2\x80(\xc2\x87g\x8e\xb1\xe49\xbc\xc4\x11\xf7\xad9-^E\x19\x85Q/\x80\xde\xb8\x99\xd4\xa2\xd2\x93cR\x8bH\xd6\x8a/\x93\xe2\xfbEVrZ\xcdJn9M\x99\x00[\xb0\x96\xe8+\x83#O\xd2\xe842y\xb6I\x99\x8b\xf5\x14\xf7y\x99P\n7\xe1T\x13\ni\x02P#\xbbF\x05\x06\xdd\xb2k\xb8\xda/\x10d\x84\x83\x8c\xb3U\x95\xaa\xf9&\xbfo\xf4\x0d|\xac:\xb1\x11x\xa4d\x83\xed\xee\xb2\x06x,<\x82]8\x80\xb7\x82\xc7\xc3m\xb6+\"L\xdfJ\xa7\x04\xb4\x00\xf0gD\x1b]\x06`N\xb0Gp=\xe5b\xea\xdf)\xed9\xc74\x8c\x16v\x86J\xba\xf7\x1b_J\xac\x81\x02\x08\xc5\xcf\x18%0 W\xe1$\xa2kn\x10\x1f\xc2{t\xc2\xabG\x0dpy\x10E\xac\x88\xbf\x14\xd5^\xa2\xfd\xe3\x059#\x8b\xf2]\xf3\"n%\x8e\xe1\x06Q\xfa\xd0Z\xee\x00\xf8\xd8\xd6\xba\xd0\x13\x8e\xc6\xec$\xd3w\x13 \xbf\x0b\xae\x8a\xd4\xf7\"\xaa^\x98)y\x0e\xea(F6\x03\x16\x16\xa9\xcf\x19\xdd\xca+`F\xd8\xc2\x0e\xea8}\x1fG\x83o%\x15P5\xa9\xb2v\xc0\xdcJ\x169@9\x84!\x1c\x96\xb9\xb3\xf4\xf3\xdfJ\xf4*\x95\x8a\xe3\xc4\xeeC\xc8\xb8\x8bi\x86~\x92\x02\x16\xd9\xb8\x10\xbf\x8c\x049B7\x91\xb0\x80\x1e\xa3\xf1~\x00a\x9d\x82ip\xf4\xc9\x8c\x92\xc6\xf1\xde\x8a\xa2^\x15G1\xc8\xf8\x1b0UX?Q\xa8oA\xd8\xc8\x8e\xb0\xfaN\x9cp0\xa9\xe2\xa0\xc9\xa2\x848\x98b\xb2L\x86]*\x185(\x88/Ez\xc8\xa0\xf1\xab#r\xca\xcdbE9\xd1d.z\x13\xca\x8a\x08\x95|\x81\xf0k\xcb\x8bi2&\xca\x0f \xaf\"K\xf3x;%\x01,I\xc0\x98\x06[\x1a\xf5\x13\xf3iU\xf2\xea\xf2\x10\xd7BX(\n\x8b\x93]\xbf\x0c\x80J\xbe\xd4\x165\xc3\x0f}3|*\x89D\x04\xe3\xb0\xeb\xd7&\x06\x95\xb8g6\xb70\x00\xa3\x8d\xb5\xa2\xc7 +\xe5\xac\x0c\x9e&\xf2\x92\xc4$\x17\xfeK\x07\x12\xc1\xf8\xf1\xbe/\xa3\xdc\xf1\xa7\x99G\x05\xe1\x97\x92\x8b\xca\x87\xbb\xe8\x19\xbb\x03\xb9\xfd\x93 F\x9a\xee@n\xe0\x1b\xf1\x95\xc7\xb0F\xdca/\xdb\xec\xa1\x02\x08\xad<\xbc\xbc\"t\x9ce\xd3\x9e\x14\xfb\xe1\xd8Rt\x04\x14\xb5\x04V{\xdc\x99\xc0>\xa3\x9a\xf6OD\xcb\xe8\xd9\x15\x8e\xa8>W\nh\xb7\x1d\x80\x0c\xab\xab\xbb\xe5G\xa89nYV\x11 \xea\xbc\x80\x13$/\xd5\x05L\xe0\xf1c\x88\xec\xdf\xcd0\x00f\x9b\x1d\xeb\xf2\x03\xcb2\xcd\x8a\x05\x9d]\xf3\x82\xe2\xb9\xf6\xd0\xe8`\xa1^l\xed\xb5\x19]tW\xa1\x8b2 }\xf5+\x12E\xf6\x98\xa8\xd3\xa6\x90\xaf_\xa1P\x85\xb6\xbel\xb6\xe3\xcb\x8b\x0dcR\xf3%lCpP\x08&G\xf2\x19\xec\xc3\xa4\x0d\xc9A\x8c<\xe7\xae\xe8\x19f\xde\x8f\xf8\xa1\x940\xd4\x88\xd9\xa9\x1d\xf9f\xb7\x04\xb0N\xc9\xb27\x90.6\x1e\xbb%\x948\xd7&\xfb1\x1d\"a#;\xd7\x99E\xa3\x10J59;\x9b\xd98UU9\xfeTT\xe5\x04oH=y\x8c\xbf\xca\xacGa\xa1$\x8f\xf0\x87\"5&\xfc\x86\xd0\x97\xe7\xfcW5\xb9W\xe8\x04\x8a\x0bb\xd3\xa8\x9d\xa2i\xd0C\xc5\"\xb7\xeb3\xf1\xcd\xd1\x14\xfe\xbe e\x13\x88s\xee\x8f/\x92\xf3\xd8c*(w\x9a\x7f$\x89\x9bT\xcc6>@^\x18\xf1R\xf1\xa5\x88l\x1b\x93\xb3\x9c-\x9c\xdb\xa4F\\G\xa1%c\xce\x8c\x9b\xf8&\x1c\x0e|cHXX5I3~B\xc9\xbcQ\x9ed\xc3\xd0\xc6[t\xccXi}\xd8\xa0iE\xb3\xea\xc8\x8b\xe3\x9f\x96n\x99jWA\x05v\x1c\xf2(\xec4xK8(nJ\x13Y\xae\x8e\xb3\x19\x83`\xc2\x9bC3OW\xa8\xd9\xd0\x1f\xa0\x88\xc1\xa3\x8ag*\x15\x1e\xa8k\xe2\xf1\xfc\\\x82-E\xae\x94\x8d\x8a\x89\x97\x8d\x02P\xfa\x91<1\x8f\xa4\xb0\xa0\xd7l\xbf\xaaeU\xcf\x0f\xf2/\x1fq\x81F\xb2\x82\xb0\x0dg&\xa4\xab\xfarJ&R\xf0\xad\xf8\xf5C\xee\xb7\x80\xae8XXuX\xf80\xf0P\xad\x14=\x19\xd8G;C8\xb3\"^[\x99wcE/k\x92\x1e%\xe8EF\x9d\xf1r\xc7\xea\x13\x19\x7f`(o\xac\x98\xf5\xd5t;\x98\x9f\xc1\xcc\xb6\xb7\xb0\xff\x89\x0b\xfb\x8f1\x1e\xb0m*\xce\x10\x1623bc\x8c\xdc\xf4>\x9a\x8dv\xf1\xefm\x0c\x19c-h<\x16\x18>\xe4\xf5\xfd\x95\xb4\x91\xa9\x9c\xe1\x9e\x12s\xc0\x0d\xbf:N\xa5\x1a/Q\x88\x1e\x13\x15\x99f2\xe8t\x1bfl\xd4\x0f}|.\xf6\xd1\x84\x8dkR\xdd\xf1\x070\x92\xc6\xa3\xc9X\xec*&\xd8\xcd`[f\x1f\xc8\xd8\x9fg\xba\x11q\x99\x90=\x9e\x05\xbc\x8c\xfa\x8c\x1d\x00\xfc\xdf\x04\xff\xb5Md\xc1\xa5\xb1\x04#\x08\xf0\xcf\xd0\x7f\x08+\x06\x11\xec9c\xbb\xc9i\n\x95\xa1\xf3\xf1\xea\xf1n\xde\xe6N2\xc5 \x8aG\x18#\xc1\xc9F\xc8%\xee}60\xbc\xad\xa8\xb70\xba\xd1pda\x905\xff\xe6\xe6M\x8c\x03F\xd1l^SA\xb4\xd0\x8a5F\xb0 !\x9f\xf0\xe9-a\x08\xd9CX\xc2c8c\xff0J\xd0&K\x1c\xc3\x10\x16HA\x96z%\x89\xbcXwkAr\x8e\xc7\xbc\xdf\xf2\xb71\x81\x94\x9e\xbf\x93\x1f\xf2\x9e\xcf\x90v\xc1\x10\xe6-\x94 $\x83/A\xe6\xb1E\xc1(\xf6iEq\x92\"\x1b\x13\xfax\xd6=\x1e\xc2\xca\x87\x9c\x81c\x85\x8b\x86\xfff\xdcmaR8(4\x9a\x12z@\xde\x96.|\xb2pGf\xc2q\xc4(\x15\xe2\x87u\xe5\xc4>\x9cX\x85\x19\xb60'\\\xe8~\xfc\x98\x1d\xe8\xb6\x85a\x038A\xea\xba*_\xf7\xe1$%\xe1g\xf3W'BP\xdb\x1e\x82\xc7\xb7\x94\x0f\xdf\xc1 n\x92\x9d\x022b?\x8dN\xf4\xc2\xad~q'\x1c\xab\x1f\x0b5\"o\xa7\x0e\xd2\x8c\xad\xcc\x0e\xcc\xd8\x12M\xf8~x\xc4\xf7C\xe5\x83b93F \xc4\xfb\x92\xba\xec\x08\xaa\xb2\xa3\x8d\xa2\xec\x9c\x924D\xb5Fy\x9cp\xb6\x9bV\xd8\xf9\xb0\xd4\xed\x00\xc6q\x96\xeeU\x13\xd5\xbdj\xea\xea^\xc5\xc8\xc49\xf1r.\xee`\xa4f=\xba\xd1p\x1c\xff\xe1\x96/2U\xf3EV\"\xe8\xcb,k\xa1=\"\x04\x93b[\x99\xe0 Z\x01M\xe9{&\x1c\xc2\x8f\xc5\x9eMp}E\xa5\xbf\xdc\xcbxJI\xbe\xea\xd7\x9dR2\xe5\xf1h\x93\x0e\xe8\x91\xc0c\xe94y\xf3&O\x10Uz%'HR$\xe4\xebYn\x0c+\xf5\xb9-\xc5\x1cw\xab\xdeE\xa5\x9c\xd4Y\x9f\xb1My\xe6\xd4\xfe\x91\xbd}k\xa1\xc7\xa7\x9ce~M\xca\xfa\x8e\xecVg\xbf\x9b\xb3\xff\xf5\xf5\x1d_\xdb\xa1X\x94\xc2\x9c\xd5\x11\xce\xd4\xe0\x07\xd7\x94|U\xd5\xc3\x91bT1+!\xca\x14\xe1(\x02\xe1\x8f}\xb4\xdb\xf7\x8fy\xea \x9e;|\xc1\xed\xcb\x0e\xb9\xc3\x9d\xe6\xf4\xd4\xaaLXre\xc2\x92\x8d\xeb\x03\xf1xu\x9b\x0b\xe25B\xfd\x0c\xad\xffl\x970\x84i'\x90,\xbd1\xf5R.\xf8\xe0(3x\xfdb=6LIA\x0c\n\xff\xac\xe4\xf8\xd9\xd1\x1a\x9aT C\x9e\xb7I\x8f\xb7\\?\xd1\xa6(\xcc\x05y\x1cr\xedi\xf9s\x0f\xbe\x83D:n\xa2\x8d\x88\x1b+\x9b\xc9O\x0d\"\xac\xbcD\xff\xca|\x84\x8a\x05\xa55\xc3>\xf2\xfb4yI\xd6d\xfa\x9e|\xf1\xfc\xee\x94\x99\x8ev\x0d\\\x83\xdf\x9f-\xa2\x95\xc7:x\x1d\xf2|:\nn2\xa2\x9bVp\xb5\x8a\xb9\xaa\x933:\\\xa0\xf1L\x96}c\xd4%\xc2\xc3\x9c+1\x14\xe7\xde\\Q[0\"\x12J\xd1T\xa3\xbcTb\xcd\x8c\xb6\x99\x12\x01rD\xa5\xd0\x1f\x0d\xc6m\x8b\x9dr\xd5\x1e_G1\n\x9ej\xdd8\x08>?\xe1L\x9fK\x12Z\xb6\x90\x8bB)\xa2\x19#\xc90\xf1=\xa9,\xb4\")\x07\xf7\x0d\x17\x94#\xd2s2\x0c\x8c\x1f\x90\x93s\xcc\xbc\xfc\xae\xc5\xeb\x04\xdd\x95\x14\xaf\x93\xe3<#/\xc9:SJYH\x8a\xd7L\xe2k\xea\xf4\x8d\x81\xa6k{\xec\xde\xfc\xab?\xb7\xf9g\x7fn\xf3_[\xe2\xd8\xfeAl)b\x89:\x02R\xed\x9e\xdd`[\xbc\xcd\xabSi\x8e6\xb1?\xc0b\x8e\xb2xIkCgE\x99d\xf1\x91\xac\x7f\x86\xdeg\xb6\xbe\xdd\x07\x0b\xean\x12\xddx\x06F$\xd0U\x14as\x9a\x87Y\xab\x1b*\xa8\x1dE\xf1d\x91OIV\xafj_\xb4(_\xe8\xd6\xec<4\xb78 's\xf2\x8ed\xf9\x02\xf9\xdf8\x00\xc5\xa3\xf0c\x8c\x8f+e\xbbl\x11L\x85ZO\xebL\x01U\n\xd5\xa8g\xe5\xc8\x18\n\xafC\xf4\xb5\xa7fu\x84\xb1\xd8\x95\xe2\x9d\xdau~\\\xdf\xcb\x0e\x82wmR\xbd\xd4n\xca\xaex\xbbf1]\xb2\xf0nN\xac\xf2\x92v\xcd\xd4Z\xbeV^\xc8\xa5\xd0\xd6:\xb6\xf2*\xf7\x19\xba\xb9\x8ev[\xb2!\x01\x86u\xcaw\x95\x0f\x07\xe3@\xf9\xbb\xe1^X\xbf\xecfQ#\x19\x91\x97)\x8b\xb9\x1b>\xb2\x95\xc2\x15\xfe\x99\xc9L\xb0\x0f?\x1b\x11\xa9r\xd3D{\x9f\xb7s\xba\xad\x148\xad\x13\xdd\xb4;i1\xd3\x80\xb4\x1e\xd2\xe9RT\x99\x97%O\xcd\x85~\x0b\x19{(r\xd0G\x18&\x8c\xbe\xf6\xbc\xc4N\xaa\x15\xedp@V\x02\xe44\xbc\xab\x12\xa0\xa8\xc5\xd9\xa6J\x83R\xaf\x9c\x91\xfcXX\x04MD)j\x99\xb2\x9e(9\xcdY\xc5\xe1w\xe6\x14\xce\xdd)\x8d\x14_\x93V*\x83\x8ev\x82\xc0H\xf9\xd5\xfc\xf6\x99\xf0I\x8b8m\xb0\xbb\xa8\xa0o\x82\x95\x06I\xf9\x9dA+\x0c\x14d\xcb\x91\x02\x85\x0c\xdf\xb4\x0b\x00\x06uB\xa3*\xa2a\x8f\x7fl\xf7\\\xb3o\xf0Xe\xb1\xe2\xfan\x8f\xbb0G6.\x8br\xf6\x07-s\xce\x9c\x90<\x05\xbe\xeag\x00*w\xd5a\x9c\xa0\xeeE.%\x9a\xb6\x8c\xae\x8c\x07\x83J\x8dl\xd9\xd2 \x16=\xa1&@\xe4}\xdc\x19\xc0\x8e&\x855\x08\xee\xa1Nc\x8d\\A\x95\xc6V\x1a7\xb4|56\xae\x85;\x8c5\xbc\\\xac\x8f\x0e\xf9\x8f\xf3p-\xc5H.\x03\xd82\xc1N\x1f[d\x9b\x91\xf6\x8c7\xf7\xe0\xb4\xe5\x7fpU\xf9\xb5\x9c\xec\xb8\x19\xa3:\xaa\x19\xf1\xf8\xacH\xd4\xebv\xfcFxL-Y/[[%A\x8c,\xa7o\xf4\xe7\xb2\x03\xc5x\x9a\xbc\x80\xb0\xb5kJ\x0b\xf9\\\x87ia\nl\xde\x94gJ\x9c\x80\xf9\x8c \xf5Uy\xa1\x1d\xe1\x13\x8b[/H\xa9A\xe5\x13\xf0\x832\x91\xe2\xf6v\x00\x91\x87~ \x1c\x02hn6\xe7\xf9dS\xad\xfb\x84\x81\\<;\x1f\xe1\x04\xa6\x1a\x1f\x91X*/\xb6\x03\xad\x03\x9b\xe1\xe8\xfc)q.o\xe5F@\x06eT9\x92\xc4\xfe\x854\x84%.\\ \x08\x9bX6\xda\xb5X\xcd\xe4\x85\xd9,\xb5\x89A\xd5\xab\x8a/34\x15*9\x81\x9ecED\x91[\x1d\x91gfd8\xc1(\xf8\xe8\xf9\x1d7\xdb\xc0\x17W\xe2G\x0d\x11\xa7l\x86\x9d\xdc\x88\x98\x101\x80[\xe8\x83\x83\x81\x88\xe8\x93#\xde\xff,*\x98E\xady\x93\x18\xda\x1c\xf1:ff{\xc2k\xa4\x90\x86\x80\x1cF\xc0 \x81\xcd\x06r\xf6W^\xf4\xc8`\xd2\xa7 W\xa1+\x07\xb1\xe7\x97\x90\xd2\x0fJ8y\xe7\xb0\xa3\xc3\xcc\x0c\x86C\xee\xe9\xe7\xb1\xcd\x96 G\xa4]\xd8\xd7V\x9a8\x13^\x8d\xf6cg\"Y\xcc2\xdc \xc4\xcaZ\xd2\x18\x1a\x96\x06\xc4\x00\xb6\xf0\x94\x8a\xa4Y,,\xd2\xf8x\x93\xfaY\xe1p\x0c\xcb\x0c7\"\xdc\xb4L\nDDQE\xc9\xa4m3:\x89\xe9f4~l~\x00\x93o\xd3SEV\x1e'*\xb2\xea\x95\x8eY\x06B\x87\xd6\x81J8Nu\xfd\x95S\xc3\xa2\x03\x92\xd4\xd7\x12E\x9cqW\x02\xe3\xf3I+1\xbe\x12\xcb&|o7\x1b\xd8\xc2r\x90\xf9\xf66<\x82\xa4\xdcl\x13F\x83\n\xad\x9c8\xc7b,\xf8\x80\xe7X\x84h3\xe1\xe65\x031\n`\xa2\xa3G\x93oT\xd6 \x9b\x1e\xeb\xdfi\x89\xecz:\x896J\xabM\x15\x9fy}\x1c\x96\xf7\x9a\xcfR\xb9V\x0f}\x88ZOK\x06\xaf\xed\xed\x0c\x1e+(\xdfv\x12;E\xbfC[\x04<\xbb.\xedj\x024P\xb5N\xa1\xe0\xaa1 \x96\xd4\xe2Q\x0c\xb0'\x01\xaf\xa3\x13\x88'Oe\x92\\\xf4\xc6P5\x95]\x14\x04U\xac5\x1d\x98\xbf\xbb\x1e\x98v\xb2}M<\xb0\x99\x8c%.{\x84x\x16\x97\xf73\x11da\xa3S\xed\x88n\xe1\xb4'\xad\xa4\x8a\xa7\xe4\xc6\xd3\xb2\xceuO\xfc\x92je\x0d\xb6;\xb3\xb3\xdd~\x00\x9a@\xcbk\xe2\xb9\xbf}Y\x92\xd4e]\xba0\xf7\xdf~\xdet X\xb8\xc9q\x914\x89\xda\xe55MZ(R$\xb3\x0e\x86\x82V\xf8U\xd6\x1f)CT\xa3\x0cQ\xc0\x8f\xb0\xa8\x8d.\xb4\xcb\x0d\x8b\xd2\xeaa\x7f\x99q\xa2\x0b\xac\xe47\xc3\xbfX\x07\x9c\xcb\xcb*x;\x13\xf1L\x16\xf6\x1e\xce\xe7\xd1\x82\x80\xd1)\x0fTu\x00\xda\xae\xd4\x99'\xd8G'\x9a\xe7&$\xfcz-\x86\x8fo\xb6\x04X\xf0\x17\xe9\x94\xa1\xce\x91\x18@1\x1b\xeae-\xb4\xe7LT\x0d1oeve:\xca\x16\xb5(\x10@\xe1\x9e\xb7\xd0\xf3j\x02\x8f\xb0`\xcdM\xc8=\xac\xda\x87e\xf2'\x18\xa8\x0d\xfb2M7R\x84X\x94\x03HPR\xf4\x0bIbk\x17\x8bs\x9a\xf1\xca\xac*g\x0b\xcb\xben\x96P\xfa3L\x19\xa9Y\\\x03\xb1\x8a\xa3\x96B\xe7\xd7F\xa5\x04[\x958))\xa8\x93\xc9\x04\xe4\xb9%R\xcdw2\xcfN\\\xe9\x0d\x88^RA\x01\n\xf7\xeb\xd1`\xcc$T\xd4\x10z\xa1\x8c\xa7@\xecb\xc7h\xeeM\xca#3.\x08G\x1a\xf0\xf3s\xd2N\x16\xd9\x15r\xe7\xdcD\x94F\x9b4\x96\xd7\xda\x82\xf0\x8eJ\x90\xac\xa3g\x97\x19i\xdb(`\xdb\xaa]#C\xdb\x81\xa2\xba\x99\x99~\xb1RT\xee\x91\x89\xd1\xaa:\xf9E\x12\xdc\xd0\x986:2SK\xbe'\xa5v\xa3\xe2 HZ\x8a8 \xb8\x8fR\x1cy\xc4K/\x1e\x00\xffP\xb8\x97\x11\xa3\xfb`\x91e\xdaxD$\xfd,I\xa9\x9b4+>!\x1e\x1d\xdd\x1e\x07\x10\x8fn\x8f\x11\xcb\xe9ho\x0c;\x10\x8f\xf64\x19\x82\xfd\xb2 y-+\x83q\x97\x96;i\x08{\xcd6\xeb\x15\xfal\x0d1\xd0\x8f\x06\xba\x81q\xce\xf5\x85\xa8\xf1\xc1\xdd\xbao\xf0_?z5\x85\xa0 \xa7^Zq\x8a\xfb\xbb(x\xe5b7\xfa6\xed\x82,u\xe0\xdcRG\xe0\xcaK\x02\x99\xad\x0f;\x99\xe0w\x0fC\xd8K\x9fK\x86\xef\x96\x03\xff\xea\xfa6\x07\xf6\xbf\x03g\x88\xab\xd9*\x80\xa1n\x02\x973\xb9\"\xa0\x04\x16\xd8\x00\xc2\x13\x90\xf4\xb3dI\xae\xd2\x01C/K\xf3\xa2\xbe\xd4_\xc8H\xc9\xfc\x989\xe6\xc7\x14\xce\xbe\xa2\x1c\xc5U\xa1\x88\x03\xb4\xcd\xf2\xfa\x05\xe2\x1f[s!p\x13\x0b\xaf\xc9A\xfb\x93$\xceh\x9aOP\xb3\xecF\xdf\x7f28zGE6\x1b\x1e\x81\x84%F\xe8(6j\x0d\x810\x01\xc9\xcd\x818mI\x9c\xcc9\x88\x82\x04Zs\x8aq\x0bv\x14g4\x8c'$\x99)\x15\xcf-N\x11\x089D\x8f\xea\xa7\x95d\x9f\xa9gR=\x17MX9tv\xc5\xa8\x96j\xd7\xb2\xe6e(\xe5g\xb2\xce\x8c~\x89\xf2\xdar\xe3\xca\xd4\x8b\xa6k\x87\xb7\xd8E\xb4\x11\xaeN\x9d\xc8K\xcceJfQL~N\x93\x15I\xe9Zp\xbe\xee\xad\xb0\xeb\x94PE\xb4\xec2\x06y\xa9$\x88\x87Mvj\xe2\xb2\xdd F\xbd\xb2\xcax[\x8fo\xdduJk\x89\x98\x03\xe8=\x0d\xe38\xa1\xacuHb\x08c\x88\x8a\xf4\xbc)\x99$\xe9\xb4\xdf+H&\x8f\xb6\xb3\xb0\x98\xba\xab=s\x9b\xbc\x0c\xd1\x08\xf5\xeb\xb2\x7f\x12\xc5S\xaf\x8c\xbak\xff\xec\x12&!\x9d\xcc\x01\xc1f\x1f\xd0\xa5']\xd3\xe5\x11\x91\x0b\xfd\x04r\xfdq\x88\x81\xbcK\x93\xe5aL\xd35\xd7\x95*\xca\x9fv\\\xe9V(\x81\x0b\x7f\xc3F\x95\x04\x87\xfc\xda\xa4B\x14*\xdd\x1a\xcd\x08%!\x11KT\xfd\xc8\xbc\xacp\x00\x1f\x88p\xe5\xecPmA\x1e-D\xdd\xd9<\xef\x85F\xa2AHF\x99BH\x87\xf0\x9aT\xe1;\x9a\xca\xea\x06\x15\xa8\x17u\x0e4\xfb6\x00\xe2\xbd#\x01\xbc\xf0\x03xw\x05\n\xdc\x14\xfc\x90\x02\xeb0\xa1\xd2|-n\xa0\xb5\\\x1ao\x9b\x17M\xb36\x8c\xfa\x91\xf7\xe4K'\x9a\x81\x8d\xcb/\x9bt\xe1]\x15nN\xa1BgJEf=\xbe\xb1&>Jr\xb8\xa5K6X\x19\xa3L6\x80F\x0d\xe7i\xaa\xcd\x88yJ+\x8798\xfc\xd2o\x04\x89\xd6\x80\xc01\xb7\x15;T\xb2\xa8\x07\x02\xa3\x02\xcf+\x87M\x070\xa4W\x01C\\\x03\xc32\\i\xf0\x15\x04\x18\x1a\x85_\xde}\xdb\x19\x11XB\x94\x9a(Y\x1e\x13\xd5\xc9+\xe6<\x07\xc7e\xea\x11S\xcc\xd2%#P2\xdf\xf2?y7>\xcf\xd2S\xf4`T\x9d\x17\xcdG\x81\xc8\xd7\x1c\xc3>/\x06\xa4\xeb\xcao%\n\xdd\x8e&<\x1eT\xb0\xf8\x16\x08\xca\xe3I\x7f\\\xc4U\xddS\xc3\xa0aD\xdd:\xd8\x8c\x8b\xea\xa8\x90\x97\x96\xa1\xd8\xea}Q\x88 hP\xe1JCT4\xf3U\xc0\x82\xf8\xe8\x17V\x98Wt\xcba[\x8a\xf2$!\xde\x1b\x12\xc0\x0d?\x807\xeaR\xe9\x02\x01\x1d\x89x\x11\x0d\xd8\xa4\xe4o\xbems\xb5R\x1a\xf3\xfah7\x9d3o\x86;\x0cA\xee\xca\x92ig\xea\x86\xf7\xdf\x84\xb0\xd7\x82\xa1\xc4\x15C\x89\xc4P\"14\xe5\xa6\x10\x81\x97N5\xc3\x88\xf7\x8a\x04\xf0\xa3\x1f\xc0\xabo\xe7 ,\xc8\xf7\xeaZ\x90\xef\xcf\xc40\xe2\x8e_\xda\xc9\\\x1b~\xfd\x87\x91\xa8\xc4\x9f\x8e\x88\xf4Lp\xba\xcfT\xe8\x10!\xcc\xb4\xf1\x10\xcdu\x14,D\xbd\x9fg\xff\x95\x88\x84.1\xa6\x87\xec\xfa\x89x\xc6\"z\x8a\x93En}\xab@W,\xd1\x8f\xc2\x00:vr\xb1\xb5\xbc\xb9\xcbo\x1a\xa4Xv5\xf5rZD\xd7\x02\xfb\xbf\x06\xd1\x1d\"C\xdd\xf6\x02\x14\xe1\x95\x15\xb7p\x8b\xf3\xa4\\/\xd2\xe6e\x89\xde\x95\xb6\x11\x02G\x0e]\x18\xa0zI\xde%o}S\x0c\x1e\xf7r\x04\x07<\x91\x0bG\x89\x14Q\xa2\xbc9\xe07\x07\xcd|\xf9\xeaepYt\xa0 \x95s\xb8\x9a\x86\xe0\x9d\xf9\xd1+\xf3\xa3g\xe6G\x98\xa3\xcaK\xe3\x00N(\x13-b\xe5\xcdoT\xb0\x86\xb1\xe0A\xb7\xa1g\xd4\xb0V:\xec||V4\xea\xec\xf3\xb7\xe7qi\xf2\xb1w\xe6\xa8L\xe0i\x9e\xe6Eut\x1b\x9aW7oep#\xaa\x89S\xae\xcc\x85\x89\xaf\x07\xe5\xdfRg\xa1\x89\xd9\xac\xcf\xc4I\xf9[J&Z\x95\x15\xef\xff\xe6Me\x00\x15}\xae~\xb2R\x99\xa0\xda\x06\xcc\xd3\xec\x1f\x93\xe5\x8a\xaeQL.~\x0c!\x8f\x85\xa8\xfd\x1bm\xa6<\xadM\xd5Qc\xdc\\\xb4\xd2J\xcd-\xd4\x7fS\xacZy\xfc9N\xcec\xf8L\xd6\xd0\xfb\x1bl\x03\x85m\xf8[\x0f\x92\x18\xd8/\x89\xc7\x06#y\x05z[%\xf8D1\xfd\xb2\x16\x87\x16)\x1c\xf4\x86\x15cBu\x892\xa9\xd7j\xc1\xadJY\x08e4%\xce\xc1~\xb9\x0e\xcd:\xcc\x955pT\xae\x1b7\x8ey\xa6\xc48\xfb({\x8f\x9a\xf8I\xdcT\x01\xcd\xe2\x00\x16\x0c\xc7z\x7f\xff\xfb\xf1\xf1\xd1\xeb\xd7\x1f?<\xf9\xe1\xd5\xe1\xf1\xfb\xc3\x0f\xc7\xc7\x7f\xff{\xaf\xe9\x08\xb2bog\x0eJ\xa3y;\"\x18\xaa5\x91z\xb5& \x05Y([j\x88\x91\xcd\xe5\x87\xa6\xf4\x8eg\xa0^\xae\xe8\x9a\x87O\x17`tSDL\xdb\xf7bU\xc9\xb5\xb2\x04a\x94\xd9\xeck\xe5\xebb9-\xca\xb3z\x97kJ\\\x93p\x9fY\xe9\xd2\x0c\xf3\x0ex36\xdei\xec\xe9L5\x86v\xd7\xdf\xa0\xd2:\xe7*\xad\xd3\xb8\xd4d\x9d\xff\xbfM\x93uj\x87_\xa1\xee\xd3\x14XT\x7f\xad\xe2\xd1\"\x96\x0et+E\xa9\xb5*\x95Z\xab\xaa\x82I\xfe\xac>\x10\xac\xc1*VuV+\x17\x85\xcf\xca\xa6\xf0Y\xb5)|V\xb1\xdc\x870\x84\xb3X\xdc`[\x11Q2\x00\xe2\xadcF\x9c\xfc\x00\xd6\xd7\xa7\x11Z\xff)\x1a\xa1\xf5uj\x84\x84\xff\xbdM1\xb4\x8eK?}N\xb9O5\x94{\x19\x07p\xcc\xf6\xc9\xda\x81\x16\x9ft%l\xc7\xff!\xc2vn\x85\xe6\x92\x13\xb6%\x1b\xefI\xec=u/\xbby\xf1\x0d\x84\xed3'l\xef\x15\xc2\xc6n\xf5\xf38\x9bG3\xfad\xb1p\x8d\xe6\x7f\xef\xac\xe8~bWt\x1f\xc7\xa5\x83\xed\xb1\xba\xd7\xcecqC\xec\xb5\x13\xdck\x17q\x00\xe7\xd4\x0f\xe0\xe2\xfa\xf6\xda\xc5u\xee\x8a\xf74\x9c|\x86\x11\xdb\x10\xe3\xe6\x86\xb8\xb8\x82+H\xd5\x18?'\xe1\xb4\x89\xcf\xa8\xb7\xa2JRn\xea?\xe4\x89\xd7\xe9\xce\xceC\x1f\xbf\xe7^U\xe6\xbd\x00\x07 \x92\xd0\xe8\xe2\xfe*#_\x11\xf2\xb9\x13\x80\xd8\xa8K\xc3!\xfb\xa5\xc9\xde\xd1\xe8%\xcf\xe6m\xbd(9\xbe\xe5\xfa\xbai\x1d\nM_\xe1L\x82\xbb\x7f\xbb\xd1N\xa00\xc0l\xe0\x01\x02\xb3\xfe\x16\xec\xc0\x80A\xfc1W\x1b\xee\xec\xf8\xf8\x99\x89/\xc0\xcc*E\x1b\xa3\xd8\x90\xfb\x90-X}-\xd8\xa5I\xb4\\\xc5GC0e\xc1i\xe3z(\xf1V\x8d\x8a\xa1\xfcn\xad\xfc\xb9p\xed\xff#\xd6\x8b'\x8d\xc5{\xc2H\x91\x83`\"\xd4\xc9\x98\x1f\xda\xa3\xbe\xcf9\"\xfb\xfa\x959HZ\xa4\x16d\xc0\xf5\xd0m\xd9T\x05o_\x84\x07u\xe0\xd0\x08\xcf\x92gB\x01(\xd1\xc0P\xf5\x18\x8a\xf5o\xa6\xce\x87\x06\x19\xc5;E`\xaci\xfdIm\xfd\xe3\xab\xae\x7f\xd3\xfd\xba\xb1\xfeIke*\x15e\xb3E4!\xde\xc0\xde\xa68\xa6\xba\xb4\xcb\xd0\xd0Q\x1d\xa5\xeb\xca\x05\x83\xeb\xdd\xe9N\xd1Z\xeb\xdd\xa7\x91\xac\xae2\x8b.V\xa6o\x8d\xcf\x16(U\xc3\xa0.x\xc5X\x11;\xd8\x18\x92\xb8\x1c\x99\x8c\xa8|\x16\x8e\x1e\xc5`]\\\xc1b,.\xa2V\xe95h\xb8_{\x95\xa6\xab\x16\xaa\xa2\xa3sZ\x1f}\x99\xa6\xc7\x18\xe3W\x9cLi\xe5d\xc22gQ\x95d\xb1\x83\xe6\xa1\x8fw#\xfb\xe9n_\xc4\xb4\xb6\x88\xd1\x95\xd6\xef\x8fXWa\xba\xb6\x86\xdd\xd4V\x85.\xa9\xa9\xb9R\x10\x14\x0e\xf0L*\xa8\xbd2\x99\x8ea\xc8\xea\xcc\x06\x06=\xd4\xc5\x95\xb5\xa0\"\xee@]\x92\xf2hQ<\xbflH\x11\xf3=\x97\xd6\x10!\xad$\x13Le0H\xac$\x13\xc4o\xd2\x16&\xd0i\xb2n:R\xa7\xd9&z\x1db9S\xed\xd9\x97\xba\x9d\xdc\x8e\x91 \xad^\xff\x92\x9fH\xdb\xe2\x07D\xbf%\xa0\x03\xee\xd9\x8f\xcb`\xb2\xfa\xeag\xc8[je\x1e\xda\xb2\xf3Y3\xf3\xb9D\x05\\\xa0\xd6\x15\x85\x9a!\xbc\xd7H\xef\x87q\x00Otz\xd7\x0fO\x9e\xbe4h^\xdf\xb2\xf7/\x1c\xa4\xfd?\nw\xbd\x96\xfc\xa15\x8f=kF\x99\x92\x19\x8eTN8\xaa;\xeaE%\xfdK\xf9\xaf*upK\x19\xf8\xd9z\xea\x1er=\xc0!\x03\xc8\x1f\xb1\xd7pO14z\xd4..\x16ho4K*\x87\xd3\x08ut\xec\x9f&J\x18!\xa9\xa6\xef\"%o\x1c\xfb\x01\x94.\x93Jh\xc4\xfb\xf5\xf2$Y`\x85\x04\xdb\xf3z[\xb4\x06\x11\xf5\xd7\xdbx\xf4\xa4P/\xbeu\xd1\x06\xbe\xb5i\x03\xdf\xb6i\x03Y\x17\xaam\xed\x8b\x9aE%\x80\xb8\x7fT\x12\xc8\xaf\x01[\xa6X\x97\xfeK\xa4\xc4vH\xf3\xf5\x8cz6V\x04\xc4\x82S\x91\x1b\x97g\xda.\x8f\xf6\xcdFk\xa3\x87\x1acP\xe6{0\x98\xde\xac\xa6m*\xb0GOc\x1a+\x88w\x9b4\x81&G\xf1\x94\\\x90\xe9{\xf2\xc5\x010\n\x89\x7f#\xa2\xce\xddz\xf9\xe9\xbd{\xeb\x08\x1cm*l\x17\xcd\"W\x87pa\x84p\xefn\x1d{!\xa7,\xd2\x94]\xd2I!\x17;\xf6\xde\xa9\xdb\xec:\xbb\xed\xbcI^u\"\xa6\x9d\x9a\xcf\xaa\xb3R >\xce,\xac?/WY\xaa!\xe4\x9c\\ \x052\xae\xee#\xbc\xb86\xd0\xbf\x8a\xb2\x0eK\xbe\"\xd7\xd5/7C\xb8\xf7\xdc\x1b!\xc7r\xb2 \xe3\x9eK\x0f\xa5\xa9\xc3\xb1\xfc\x85Y\xbb\x04\xdb&\xc6\xf2\xba\x9f\xbe\xf2\x12\xc3\xcc\xb91\x8f\x97\xd9e\x94?\xc5\xb0\xc7}\xce\x14\xc2\x01\xe4\x98\x92|\x1fB\xea!\x7f\xd8\x8f2\xc1'J#\xe0\x88\x8e\xb5\x94[\xbd.}wOo\xf5*\x10\xc0\xe2\xf5\xad^\xa6\x8a\x1dP1\x16D\x0d+\x8f\xfd\xabA\xed+\xfb\xb8\xcfD%\x84h\xb4\xebP\xe79)\xed\xad\xb8\x08\xa1\x97\xa0\xc7\xae\x0c\xc4\xcd<\xa5\xd0j\xb3\xde\x96\xbc\xcc\xd9W\xcfD\x95(Q\xfdBW\xd7X^\x92\x92ci\xe9!L\xeaT\x14\xc7\xc4$N\xf9T\xd2S?\x90\xf7f\x8b\x90R\x12{[\xbb\xc2\x12\x83\xdaEM\xd1\x13\xebV\x00\x01\x1c%\xcd\xa8\x13\xba\xc8-\xc4\xfd\xa0\xec\xc0\x87f\x1fJ\x85X\xd86XN\xe4e\x06\xf8%\xaf\x8d\xd6,g\x8b\x0f\xa5\xfaV\xe3\x0e\xed\xc6\x8eH\x8f^\x97\xb4\xc9*\xbbV\xf5 v\x897\x98\xda\x12#k\x0b!4n\x91\x98\xa6Qe\xac.CU\xf4{\xef\xdc\xba9#\xe9\xda\xf1Lq\xe4\x82cK*\xf2\x16.8\x0d\xc0V\xf2\x13\x8a@s\x8e\x03\xbc\xd6\x11~\xa1\x14Z\xe3Z\xa2\xad\x81\x01\xf8uG\x12\xd0\x03\x86\x13]G\xc8\xd4O\xae\x1f\xd4|\x82\x9a\xf0'0\xf5\x19Ok=\xbaT\x8db\xc0d\x9fbNT\xcf`\xde\x00UOz\x80 M\xf4\xe5\xc15\xc3\xe2Z\xa1n\xb0\xa8 KP_q\xeei\x89y\xbb\x89\xaf/S\xa3\x19\x08\xe3@\\6o\xbd\xef\xc2\x92\xc2\xe9!\x1c@\x0f\x19\x1f\xd8\x87^\xd03c2#\xc1=\x8d\x1eU^\xdf\x82\xe96\x1c\x8fE\xa9\xfe\xad\x01\xba\xacn\xa3\xd2\x14\xffE7\xa3-YBJ\x99\x14\xaei\xe1E\x83gN\xaf\xc9Y\x82\xd8\x01N|\xdbg\xb2\xfe\x06\xf2\xf3\xd4iE\x97\x159\xd4\x01\xad\x8a-VM\xd9\xe9\xd4\x19?K;n\xb0\x00\"\xeb\x02\xd7p\xad\xe1\xa0\xf2\x08\xf60?\"\xc3\x14\xd8\xe7\xf9\x90\x1a\xdbAU\x03`\xcdZ\x1b\x01\x84\x03\xf0\"A\xe5\xb09_\xb4K\x8b\xd2\xb7\xbcb`b-\xc8\x9c\xba\x83\xec]t:\xa7\x1d\xe1& \x93\xca\x08\x95\x86(;}\x12\\\x8f0\xbd\xa7F\xbb;\x98\x06\x8d\xbd\xb8\xe3n\x81Tj2\\\xa7\xae\xd0\xb8|E\x0c\xfer\xb5C\x82q#\xddz\xe4yYx\xac\xdc\xbb\x18K\x85\xe9\xb2`\xe8\xbaJ\x9djL\xd4gf\x0c\xc8\x01}?(u\x7f\x03\xad\xf9\xd9\xa9\x97\x93\x9c\xbe\n\xbb\xa8\x07\xf8\xbeF\x0f\x99\xdd\x00v\x06N\xbdD\xd9\xe1rE]l\x0c\xa2\x17\xf5dR\xe4\xf4\xba\xe4\xbe/\x96\xb1\xca\x8c:\xf0\xa2&#\xa4\xd3l&I\x1e\xd7w~\xcb|\x9ex\xb4T%\xf1m/\x04X\xfeq\x07\xbd\n\xf6\xfe\x83+{*\xfaw\xa5R\xa0P\xaa\xaf\xd4\xf3K\x83\x94-\x03\x9eD\x0d\x1d\xf1nc]\xf1{\x917\xc1+\xeb\x94\xf3J\xe2lW\xaa9\x8f\x9d\xa46E\xe6\xd2\xb3\xbb\xf2\xb2\x94R\xc1\xb3@5\xb7\x19*\xe4]\xaa\xe7\xad\xcb\xea\x91/y\xb8\xe8\"l\x9d\xd1\x82l8\xb5/\xb2f:l5\xd5\xe1T\xbf\xb6\x18\xa8\xd5?\xc6ty\x95\xe2L\x94\x96\xf7\xed\x9cb\xb5z\xeb\xcf\xb1_S\xb5Z\xcf$\x0e\xc6A\x0b\x1d3\xc3@\xa2\xa0\x1b\x05\x8e\xaa\x94\xb7\xd5\xfc\xa4P\xb0\x00\x12OG\"\xe5e\x18\x7fgQc\x1ev\x913\x90\x0e\x89\x84\xcbK\x1eC\xb0t\xec\xe5\xa8\x0b\x0d\x97\xfdp\xaf\xd1.=E\xd9\xfb\xfc\xc4\xb1\xc0g!\x03\x0eM>aE\xa5\x14nu\xe6<\xba\xa2\x13r[\xda\xe2<.\x12\xe3t\xc8\xa7\xa5\x9f\xe2\x8a\xf1B]&\xe9\xd9f)`\xa6\xcc\xd2/n\xba\x9fj\x9f\xc9\xfa\xed\xac\xc3\x90\x8aC\x8d1s\x9d y\x0dFB\x1eq\xee~\xc4W\xb42lW?mH\xa9.\xdd.\xba\xab\xd1\x1a%\xbf\xfa\xc8\xcf\xba\xf7\xf7\xf2*\xebb\xe0\xbdq\x8d\xb5\xb9\xac\x9a}/\xc3\x8b\x0e\xbd\xbe$\x9dT\x18\xcb\xf0\xa2\xeb\x99\xfa\xb2\x92\x8f\xc8\xa9\x137\xa3Yc\x06p\x00ob\xee\xc2\xf2\xd5MPZF\xf1\xd5\xa7\xc3\xbb#\xbc;\xd7\xb9\xa5\xa43&jC\x1eA\xdf|\xf69Zu\x80\x9d\xd2\xfe\xeb\x90\xce\xfb\xcb\xf0\xc23T$6tV\x17\xbe]\xa5\x04\xc3\x1ecMzT\xb9\xe3<\x90_\xe7\xd1\xa2\xa3\x99\xa1\x18\xcc\xefW4l|\x8eV\x1fc\x1a-\xbau\xcb\x81.\x87\xdcM\x05\xc5\x13\x82u\xeb\xafi\xe5\xd0d\x06\x03}\x7f4\xfcL:,/\xad\x18 \xae\x80R\xac\xbfkF)\xd6dw\x94b_}\x0bJ]E\x92\xf8\x87\x13w\xab\x940\xfa\x18\xa3\x9a\xb7\x92\xbc\x0d#+[\x18^\xc9NS\xa3vY^L\xa4\x8b\xaa\xb1yJ\x81\x96J\x18\x08vlo\xedL\xd4\xf3o)\xfb_0n\x1a\xc1\x87\xa2J$l\x9b\xa1\xd2L)\xfd\x14\xdf\xde\xbc \xdb\xdb9\n\xa9\xa2AC\xa1ry]\xfa\x01\xe4\xc67.\x03P\xcb \xfd\x17\xadJ\x92vY\x16Z\xf1\xc6b\xdf\xd9\xe5Zv\x85\x16\x8f\x12y\x89q:FY\xaa\x17\xfaN\x85\xc5L\xdb?\x00\xf7\x88G\xf5\xb2F?\xaa\x97!VB\xbd\xa4\xe9&o-N%/\xae\xc3\xaf\x14\xa9\xb2x\xa9\xcaKF4R\x11\xc3\xdb\xfa\x01\xbb2\xe1\xac\xea\xf6\xf6\x04\xdf\x1e\xb4\xb8\xb6\x82n\xafM\x02\xc8P\xe3y\xc0H\xdbp\x08\xef\x84\x98\xf3\x9cad\x86/\xf04\x7f\xa1\xf0\x0c\xf9/X\xdc6\"`\xa5\x00\xda\x87\xdd5\xaf\xec\xe0\xb9*SQ\x1cZ\xdd\x98\n\x19C\xd0\x91/\xed.\x86\xcd\xc3l\xfe4\x99vpt\xa1\xf32\xbb\x00\xd6e\x9a\xab\xd9\x06\xday\x04(\xb6\x17wP\x1e\x0ea\x00\xb7`\xb7\xd8h\x16\xd2%\xcd\xa4\xb3V\x05\x9f\x9b+\x7f*\x8a\xdf\x0e\xf4Uo\x8b\xd7\xf8\xc0\x9c\x16\xbf\xf6\x0d\x1b\xed{\x14\xd2o\xdf\xb9\xbd\xf7`p\xff\xf6\xdd\xdb~P\xdc\x86G\x8f`p\x176@\xe0\xf1\xe3\xc7\xb03\xb8\x1b\xc0\x9d{\x83\xfbw\xee>\xd8\xfd\xbe\xfe\xdem\xe5\xbd\xdb\x01\xdc-\x9fc:w\x8f\xc06\xdc\xbe\x7f\xef\xce\xde\x83\xbd\xc1\x83{\xb0a0\xfd\x17\xdb\xd2\xff\x12\x9f\x0d\xee\x05\xb0\xb7w\xe7\xde\xfd\xbd\xbd\xbbE\xf3\x87\xe2s\xec\xa6x\xf3v\x00\xb7\xf7\xee\xdd\xbbs\xff\xc1\x83\xdd\x07\xbe\xda\x84e\xcby*\x7f\x10c\xad\xcb\x83\x8eP\x83!\xdc\x1e\xc0w\x90\xc26<\x8f\xbd'\x147\xcd\x13\xea\x11\xdfg32w\x0e\x8e\xbbS^\\+~\x85^\xaa\x93r\xe9\xa6\x98\x11v\xd4\xdaA\xb7\xc6\x1d\xdb\xf5\xb5\xe5\xac\xa1 \x88:RX\xb9SW\x06\xb3\xbd\xf8\x9a''Sr\x01\xa8o\xbc\x8eG\x0b\x19\xe0\xfd:\x1e=c\x7f\xbf\x16&\x8b\x8c\xdd\x12\xa1\xa3\xfc\xb6\x08\xac.\xee\xab\x81C0\x84W1>\x89\xe2l\xc5s\xe3\xe3'\xef\x93<\xad\xe6\x95\xd1\x81\xac\xa6D\x12\xee\xad\xd5\xd9a\xeb\x93y\x18\xc5\xbcma\xcb\xe4\xb7\x93\x98\x86\x11F\xa5\xe3\x10\xb8\xee\x12c\xc4S\xdd)9[D\x1dB#\x0b\x01\xe5+1\xae\x84N\xed\xb3:l\xb8\xf7\xbbZ\xff\xcdT15\xcb\x02V\xe1\xae\x93a\xb5\x90&\xa4\x93\xc4( \x1a\x9b\x8bO\x03p\xa3\xaab\x93t\x14\x1a\x97\xe1\xeae\xd5\x07\xd9\x15FW\x00\x02[\xf7:,\xda\xc4\x8c\x06,x4\x82\x05\x08\xd8\xc9Uv\xeb\x87\x18\x93\x9b\xb4f\xeexj\x06\x92<\xd5\xaa}\x19\xda\xf9\xb9\xb5\x9d\x11 \x80\x8e\x9d\x1a{g \x87\xf5\xb3\xb9e\xb3mQ\x97d\\\xd0\x84\xa7aXo\xaegX;\xd7<\xacW\xf6a\xf52\xa4\x81\x15\xe3\x07\x1c\xc0O\xef\xdf\xbe\xe9\xf3G\xd1l\xcd\xd5\xb6\x82Z:\xe6\x16}f%\xc0\x87\xc6L\x9e\x86\xe6\xbe\xb6b\x10\x85G\x05\x07G\xe11\xfe\xbd\x83\xec\x9cS\x07\xcf\x1d:`\xac\xcf6\xec\xdd\xbb{\xe7\xce\xed\xbb\xdf\xdf{\x00\xdb\xe0Q\xc6\x90\xdd\xf3\xf9\x9f\x8f\x1f\xc3^\xf3\xf4\xad.\x94h\xedCT\xaf\xc2h`\x95\xcb\xe5\x95|\xb3\xad\xaeu@J\x1b\xdeV\x82\xa5\x00\xf8\xba\xf2\xd0R&\xa2G\xbe\xaf$-\xc5f\xc5}k\xcb\x97\xac\xf7\xc0\x96GC\x85\xa8\xdel\xe7\x0c\xd2\x80[\xee*1~\xd8\x7f\xeb\xe4\xdd\xed\xa1W\xb0\x9f\x15\x90\x8d\x18ds\xf8\x1f&;\xb0\xad\xc7p \xa9\xb8\x00c\xcc\xef>\x7f\x07\x0e\xe09\x9b{\xce\xd3\x91\xa2\xd5F\xfe\x8cd\xca\xd86\xf0[\xad%\x86T\xe5%\x95p\xde\xc6\x0b\x12\x9e\xb9p^\xd2,7b]\x8c5\x87\xb2oY,\xb6/op\x02 \xf5/\x01\xdc\xe8'3t\xa65~\xc6\xf3\x93(\xde\xf9\xd6s\x96\x14\x1b\xdf+\x88\x81\xb8\xc7\xe8\x80\xc8H\x13\x94\x94\xc8\xcd\xc7\xa9\xab\xcb\xdd\x92z\xbbj\xcaj\x97>\xae\xe0_\xc7\x0e|\xc7\x08\xd5\xebv\xefq<\xf9\xbf^I\xafzC\xfe\xf1,\x0el\xc8\xe6<\x86_#:w9\xa7\xa4\xcc\xa3\xf6b\xc77\xc6\xd3\xc9\x00\x81\xe6\xf8M&\xcb\xca\x9dK\x9fQ\x842=\xec\\\xea\x1b\xd4\x9bE\xdd\x96#t\\o\x0e\xbf3\x8f\x85\x18\xc4kA\x0b\xb3\xb2\x93\x9cv\xd5|:\x9a\xaa\xd3p=\x9b\x0d\x9b/s\xb89@;Q\xf2l\xf3\x12\xda\x15+\x81\xfaX\xb1$\xa8\xb7+&\x85\x17\x81\xaa\xa4\xf5\xf1\xde\x8d\xca\xf2\xf1{?V\x9a\xe6\xf7N\xa8\xe6\xe3s\xaa\xf9\xfa\x82\xd6?oBE\xe6\x97\xdb\x87\xb8 W\x04\xea\xcb\xe6\xfd\xa7\xc9bA\x10\xd2\xfbp\xac)\x90\x81\x01b_5\x0f\xd4\xb4\x92G\x1a\xe7 \x9e\x97o\xa5y\"R\x05^hGI\xf7!\xd3\xe5{\xbb\xbb\xd3O\x9f\xf2\xe9\xfd\xdd\xdd\x1d\xf6\xefl6\xfb\xf4)\xdf\xbd\xcd\x7f\xee\xde\xbe\xc7~\xce\xc8\x1e\xfe\x9c\x91\xbd\x19~3\xc5\x9f{\xbb3\xfet\x97\xf0\x7ffc\xd3\xe0\xcc\x14\xad\x100(\xc9\xa8J\xc7.\xbb\xc1i\xb0\xfb\xa0\xc6\xeb0.\xb2wx\xb1\"\x13J\xa6\x10\x16\xed\xf4\x14c\x8f\xbc\x07\x89\x96\xb0G3\xf0\x94\xf8\x88-\xc5D\xb0\xd9\xc8\xecA\x1cE\xb4\xaf\x11\x1f\xe8\x9e\x864<>\x16\xd9F\x9bX\xa9h\xf1\x84\x14[\x83\x0c\xbb&\x9a\x1aTQP\xb9]\x14\x82M\xaa\xf7yQ\xc4\xbcz\x933\xc4a\xf5f\x86ofUB4\xe9\xb6:\xb7\x1f\xe8\x97\xe7\xce\x83\x96\xe3\x18\xa8\xc8\xcb\xc1Co\x1b\x8e\xeb\xca\xe6\x15\xc6\x0eOT\xe6\x04R\x9c\x80\xf2\xd1V\xc4\xb8\xab\x9b7\xd9\x1f\xb1\x8fJay8\xc6\xec\xaf\x98\x1dA\x95\xfe(\xeb\xf2\xca'\xfe\xed\x07\xb7\xb5\xb3\x1e|_G>\x81\x94\x0f\xeei\x90r\xd0\xc4\xc7\xbd6\xd2!k\xb9pG\xe1\x99\x0e\x15\x17\x98\xb5\xf8&\xe4\xcd\x03\x17\x0b\xb2\xca\xb2\x8c\x8d\xa7s\xc4H\x9dY\x8a\x11\xa8\x15\x03\xe4\x1c\x81\xec-\xd8?sx\x0c+;]F\x9d!\x0f\xd0\xf5\x9b-bAK\xfeX\xa9-6\xc5%n\xb6u\x06C\xd8\x194G\xbd\xe62t\xe3\xfe\xa9\x00C\x08\x07|'\x82\xf4\x8e\xae\xb6\x8dy\x01fx\xfc#\xa9\x0f\x80\xff \xbc\x06\xe8\xf6\xf6\x19<\x82\x956\x11\x00\x1b\xd6\x92\x81ttf\xe0n\x8e\xb1(\xcc\x99\xc6Q\x9c\x01 \xf3\xb1\x89\x13\x18\xc2\x02\x0e \xf3\x8e\x03X\x06p\xc6\x03\x91py\xf7!\xf3\x96\x01\x1c\xe3]\xbe\xfa3\x0d?SK\xe2{b\x92\xae\xd9{'>0\x018\x8aM)\x0b\x10\xa2\x03\xfd\xb3\x93\x94\x84\x9f\x1bO\x9a\xe7\n\xeb\xe8\xd46\n\xb6e;\xd8\x0c\xf0\x93\xc4;\xc5\xd7n\xde\x04oY\xe6\x8c\x9e0\x08Q\xb9-f~\x89K\xa7<\x16\xdf\x18\xdel\xeb\xd1\x06\x050B\x02\xb4\xd0\xb8\x04\xb2\xc8\x08Nb\x89\x0bt\x8c\xfbh\"\x96\xb6\x18\xb8a8\xdf\xba \xda\x13y&N\x10t\xba-~0\xfc_\xff\x9f\xea\x876n\xc8H\xa5\xeas\xa9\xd4_\xdb\x11 /%\x11\xa7\x98&o\xbf\xa0Ml\xdb\xc5\xf0\x08\xd2\x87\xcd\x95C\xd3\xb8GG\xf1\x18\x01\xa7r\x86\xbbZ\xfeOI\xef\xd4\x91\xcc\xdf\x19\xd4y\x83\xe2pkRyQ\x91\xa98^\x9b\xf4\x1e%\x19\xa5\\S\x93\xfc\xa3*\x08\x9f\x1de\x87q\xbe\xe4\x8a\x9f&{\x92\xda\xad\x1db\xe2\x85\xb8VE\x06\xcf\xf7\x85 \xde\xae\xec\x13\xad0\xe6\x9bak.X\xcc\x00z\xec\x0fBz\xfc\xc4\x0d\x9b\xf7\xab\xfd\xe9\x8f\xb4\xcce),\x99\xf2\x15\x06Qch\x10\xeb4\x18h\x9e%m*\x97-\xd2\x8f\x93)aB3\xdek6\x81\xab\x89\xa2w\xb3\x1d\xca\x8d\xd4\xac\x1dZiG\xa3sbk\x9es\xe0\x16\x90A\xc1\xe4\x00\xd2\xfe\x0f\xf9lF\xcaS\xab\xf95\x03\xa3\xc7\x8e\xb7\xb0\x1fe\xb5\xb7Q\x8a\x8d\xccJ\"E\xe2\xa9(\x89\xee\x0f\xfc\xc2X\xdc}\xdf\x1b\x988\xda?''\xabp\xf2\xf9\xe7d\xb1\x9eE\x8b\x05\x0fY\xe9O\xc9*%\x93Z\xedG&O0\x96t\x15\xd29k}4\xc6L\xf1\xf3h1MI,\xbe,~\xb2\xe7e\xb9\xb4)\x99E1\x91\xfb\x0bqr\x91\x84S2\xed\xe9\x14\xab\xa4\xd8a\xfbz\x0e\xa2K\xd1\x19\xda_4\x1e7\x95\xd4\xe6qF\x7f\xc9\x18#\x8716Wk\x08\x83J\x02\x9b\xced\xd4 #\x0c\xea\\t\"\xee\xdf\xd1p\xcb\xb8\xdf\x92~\x94\xb1\xfd4\xe5Q\n\x95\x97\xf8f:\x80\xc8\xcbQ\xe5\xa4\xa7;a\xb7\xb1\xdf\xdd\xbd\xaaZ\x91\xf2\x83\x8d\xd1\x81\xb4]\xb9\xd8\xbe\xb74g\xaa<\xc9\xe5;Z\x87\x17\xa9!\x10\xfa\x05\x91E\x90\x8e\x85;_\xcd\xdf\x84p\x8f\x92H\x16'\xf4\xe2\x9a\xa9\xeb\xf2\xaaX0\xb8_\x97\x818\x16|\x7f\xbf\x15\xc2m\xec\xc4.\xf72\xf0\xb8\x1a\x88\x07\xf1\x17\x9cD\xa1X\xe1\xd2\xe0#H\x1e\xfa<\x85\xe8(\xf2\xc8(\xde\xde\x1e\xfbc\xbdv\x8f\x7f!\x082-h\xebU!\xa0\xd7\xd9\x0d\x1a\xd8.v\xc1^\xfd`\xe3\x8a\x8c;\xdf_\x05^bJii\x18\x8c\xc4{\x07\xc0\x90a\x1f\x12/\xaf\xb8 9M\xae\x97g\x042\x9aF\x13\xaa\xa8\xf6*^X\x0d?\x11\xe9j\x13{\xdf?\xa8\xebF\x94\xe9\x1c7E@&\xbas\x98\xdd\xfb\xbe\xf6\xe5q\xff\x1d \xa7\x8cN\xbe\xa7\xfc@YV_`\x80\xbe\xeb\xf7\x0f\xcfHL\x0f\x97\x11\xa5$mv\x10\xb6\x81Q^%\xd1\x8f2Jb\x92b\xd1M\x8er\x8d\x0ft\x96{\xb1%\xea(\x01\"\xb88\xf6\xee\xef\xfa\x82\x03h\xbe1CA\xfdc\x14\xd3\xfbH\x07\xd9\x9e\xad\x9c\x9f\xcd\x99-85\x1b\xd4\xc0\xb6\xe8G\xf1\x9c\xa4\x11\x15J\xaf\xbb\x1a\xf3\xc0\x8a\xa3\xdd\xdd:\xb1\x06\xa12\xd0 \xd5\xec\xfe\x8am\x9fU\x7fJN\xf2\xd3Er\n\x07\xca\x0f\xaf\x97\xd1\x94\x84\xcb\x9e\x0f\xfbmC\x9f\x06(\xfb\xb3!\xd4w\n\x08\xe1\x88\x81\xb2\x8eK\xe5\xd4\x98X]7\xf9\xb3\x86O\x19\xf7\xd0#i\x9a\xa4=\xc6\xbd.\x92\x8c\xb0?\xa6$\xa3i\xb2f\x7f\xae\xc2\x9c\xdfKI\x96/Iol\x8a\xd6Y\x1a\xd1%\x01\xa1i\x8e\xbd\xbd\x81\xa8a\x81b\xab\xae\xbe\xa0$\x16\x04\xa28\xa3a\x94w\x86\xe5S\xdf\x0f \x13j\x85F\xb6?\x13 OJ\xe5\xb8)\xdaS\xe1!h\x0d\"M\xb0 \xdd\x147i{ym\x8f9q \xa8\xaa\xe2{X\xae\x93^\x89\xc7_\x14xfSJ\x9e\x15\xc5\xdd\xc4\xcb\xacu[*\x15\xce\xc3J\xaa\xc4\xa0N\x04\xdd\xe2\xaa\xd1\xd8\x0f\n\x9d?l\xb3\x86\xab\xd4\x17\xf6\x8b\xaf\x0dJT\xed]RR\xae\xdd\x00\x0e\xb5\x86I\x06\xba\x1c\xeb,zH\xb3\x11\xdf\x9d\xe0\x8aP\xd0\xcf9\xe5Uy&\x85F\xc4KQ\x15\x92\xaa\xdbf\x86\x94\xa6\x19}I\x94\xb8\x83a!\x0c\xd5NK\xcc\x12\\u\xaa\xe8\x1d\xc5g\xe1\"\x9aB\x9c\xc4;\xbc\xd9[\xe2p\x98\xcc\xf3\xf8s\xcf\xb7\xc5\xd3\x18&\"\xb6\xb5\x06n9: \x06\\*A\x02\xee\x15\\L\xc2\xe0\x99\xd7\x86,\x1c\x89\xc4*?\xc6\xc8\x1f\xcf4\xff\xfa\xc7e\xa5\xf9\x9f\xa5j\xf3\xed\xcc#<]\xb1bND\xd8\x10\xa7\xe4#bn\x13\x0c%\xd7\xe3\x06N0e\xa7\xb4z\xe45\xe7\xcb\x16B,\x02\xe7(\xfby\x9c\xcd\xa3\x19\xf5|\x08g\x94\xa4@\xe2)\x10\xc6\xf5\xf7\x10\xd7\xce\x11\xedd:;\x04\x16GU\x97\xb6q\xcb\xc8\x86\x0f\xdf>\xe7M6\x88C^\x1c\x19L\xfa\x8f\x19\xb4 &>\x92\x9b\xf6<\x8d\x84\xae\xbd\x0em!\x85\xcb\xb5:\xa8\x8cw\xc0z{[\xee\x9b\xea3\x9fW\x8fb\xcbP\x1d\x90\x0e\xfb\xea\xaa\x83\xb6\xb5\xda\xa2\x02LH\xb8\xab\xdc\x04n\x92\xa2HV\x8d9,\x99.j\xa4#\x97^\xeeF\xe3\xcf\x15\x1a\xaf\x1b0)\xb8\xa8\x9b7\xe5\x1eVh\xdf\x16\xe1l\xd1\x01\x9b\x02_\xebiHC\xb6\xd4\xa8\xf7b@\xf3v\xf9\x9a:\x12E\x8e\xa4\x05M\x95\xc8\x17\xb36t\x94\xb6\x02\xb8\xff?{\xff\xbe\xdc6\x924\n\xe2\xff\x7fO\x91\xc2o\xc6\x03|\x84h\x92\xba\xd8\xa6M\xeb\x93e\xb9\xc7\xd3\xed\xcbH\xb6\xbb{\xd8\xfa\xa9!\xb2H\xa2\x05\x02l\\(\xab\xc7:\xd1gw\xcf^#\xf6\x01\xf6\x9f=o\xb0O\xb0\xb1\x11\xe7MN\xef\x03\xec+lTV\x15P(T\x01\xa0,\xf7\xec9\xdf\x87\x88nS\xa8B]\xb2\xb2\xb22\xb3\xf2r\xef\x1e\x92F\xc7e\x8bJL\x9a\x16\xfa\xe85\x87\xe7\xd2}C.\xb8\x18\xd4\x9d\x1b\xa9\nU\x17$\x85\x7f\xb8wO\xf7\xba\xe0\xfc\xaaK\xac\x91\x81\xdb\x05\x0c6to\xd7\xf6OO\xf86F\xc3\xe7%\x83\n\xc1\x88\\\x8b\xdf\xe5\n\xe7Y(\xd7\xc9\xffRj\x15u\x1a\x0f3&\x0d vdA@\x11D\xe3\x06.7N\xeb\xb6ix]\x8es\xdf\xc8\xec\x08\xf5P\x19\xd1C\x91\xebN\x1b\xa9\x80.\x02\xd25f\xf1\xa6r\xf3,Hv\\f\xb8\xa9\xc0#\xc8>\xbbl'\x98\x99\xd1qyg\x8eK\x19\xb9\x92SB\xc5\x9fC\x81 \xdfs\x8d'\x0f\x9f\xa3\xd4<\x93 (\x87\xa2z\xc4+]\xf8\xc9[/K\xca.P5]l\xf5\x8b\x94_\n\x86r\xfaT\xd7YBd)\xa9\xd5\x9c\xda\xc91\x95\xcd\xa2\x885\x86z\xb2p\xc3j\x94G_U\xac|\x84\x11<\xdcy\xf8p\xbf\xf7\xd0\xa4/95\xa2n\xae>\x7f2b\xfe\x8dU:N\xf2#\xbb\x87d\xb6B\x9dS\xa6\xf0=(\x1f\x08\xd2\xa9\x9a\x93\xe6\x05\xf1\xa6]z\x08\x88\xb2aQm\x88a%\x80(\x07\x1ac\xa2U\x8dA3!\xcb'\xf6t\x04\x1fQ K\xff\xa5\x9dloSY\xeb\x13\x1d2F\xf7*\xfd5(\xfd\xb5[\xfa\xeba\xf9\xbb}\x17\xd2NG\x9bk\xe0\x86\x9d3\x08U \x0e\xe8!\x92CS\x9e9\xa9h\x0cz\x98\x9f\xb9\xd59}\xac\x87Bn(\xd7H\x8f\xaa\xbd\xf7\xe9\xe9\xa9*+(\xd6/l\x8b\xbe\x16\xef,\xb7XtG\xf7\x0d\x9bI\xce \xb0|\x1f\xef\xfc\xc9\xa5}\xc8#/\x1eV\xdceM\xf3<\xd4\xcf\x93\x0f \xc4$-\xe4.\x18\xc3!\xbf{\xd56\xa0\xcb\x1b\xe3n!%}\x08\xb2\xe0\xaa\x86\x04\x9d\x8e\xf2I\xfe\xa4u`2u\xfc\x93\xb1\xe3\xd2\x05Ln5FY,\xc1z2\x86K\xda\x7f[\xa4\xe0!I\xc10\xea\xf6\xd7\xc2\xb6\x96\xde\xf5\x05\xa1\xab\x86\xf3@\xf5B\xcf\x92\xd94\x17m\xfb\x8a\xce\x9d\xc7Ny0\x0d\xc0\x1a\xa9\x89\xbfL@\xb84\xaer\xae/\xa1\xe0M\xfd\xc9\xa5n\x9c\xad\xfax\xd9\xbc\xc2\x02\xdb\x99\xe6M\xd7\x13\xe2\xbb^1G\xaa\xca\xb4\x1c!Q\xb3\xcd\xd1\xd1\x05u\xc9\xa4\xe5\xdclJ\xaf>\x97\x08 \x8a-l\x8b\x8e\xa7\xb4\xad\x1f\x97\x07\x99\xa7R\xe6\xe3s\x1e+\x02\x8fi\x84\xef\x9a\x0e!\xe5\xe89`]!u\xac0J\xf9\x91\"\xc4\xcf!l\xa5\xec6\xf5i\xa9\x0d\xbb\xa4\xc0\x91\x0f\xa3\x9f\"?\xb4-\xbc\x13\xe9\xf3\x9eyI\xcd\xc1%\x0b\x1a\xdc\x9f\x92\x14>\xb1EQ@\xbc\xd8F\xd9&\xd4X\x94\xd6\xa9Z\x0c\x1a\x8a\x94\xed]\xf5\x00=\x00Lu$\x97H\x91B\\\xb9@[-u\xf2,\xc8\x1c\x06\x9a.\x88\x04\xe5p\x93\xf0\x96\x05\xc5\xa2\xad\xea/\"\xc4\x13Wmt\xd5\x07\xef1qlf\x15\\\n\xdb#\xf0\x8dDI<\x88\xed\x8f\x81\xc5r\xa4\xf4\xa46\xf7\x14\x08uf>\x80\xfa\x81\x82\xb8\x91\x81\xa7\x10\x15p\x8c\x8a\x13\xbf!\xb2\xb2?\x03;c\xd6I\xc5\xe7>\x95\x8e#\x18\xf2\x1f\xe5\x85f\x9b\xc7\xc6\xe9g\xb5\xa6\x96\xe2\xa9\xb4ow:\xb1\xcb\xc1\x81\xab\xbe`Zf\xfefX\xbc!\xdd\xd4\xf3\x03\xae\xe7\xe7\x02\xbc\xa8\xecr\x08A1\xc4\xcc\xa4\x91\x93\x1f\xb3\x85\xa7xn:\x1d}xc0jFA\xb2m\x17\x13\xddFw\xa0\xaam\x0e\x085)q6\x89\xab*p|\xd2\xf5\x82 \x9a\xbc\x0f\x13oF\xdaE\xe1m\xb1+(\xca\xd7\x98\xc5\xc6l\xa7N\xa2\xd55\xaa\xde\x04\xe7c\x97\x83\xe4\x8b\xe0\xbc\x1eSaS\x9c\xf7k\xc2]\xb8M\xc1\x974\xb9\xee\xf0+~\xde\xb9\xc5 K\x19E\xc3ev\xb9{\x13\x9bp\xf4\xb9\x8c\x0c\xbb\xde\xe1\x13\x7f\n=\xd95\x93)\x98\xffd\x910\x17Ql\xc7\x024\xa5\x9dB\x14\xe2\x9d\x02Y\xae\xd2k`J\xe8?i\xe6Bd%9\x13\x02\xe4\xfb\x17\x89\xfd\x7f\xabMrb\x8c\x1dj\xd6\\)=rU\xa1\x98$\xb3\xd2,_V\xf7\\\xce\xcbVD:\x9b\xce\xdej9\xa6\x93v\"I\x8fk\xbfr\xc9\x84\xd9\x93C\xd8\xe9\xe8/\xb20\x1a\xfa8\xe4vq\xc5\xbd\xaaQY\xb6\xadJ\x0f\xf2_\xb2B'f{\xb2^C\xc0\xa5 \x8b\x9d\x9d)\x8c`\xe5\xc5 y\x19\xa2[J_\x17\"e]\xf2;+\xe1\xa0\x9e\x12b\xa43=z\xf2\xf5\xe3\xca\x0d\x9dQ@N\xdd\x98\xffyE\x93-a\xf8\xa8\"\xd3}\xfa$\xd4\x0c\xc5\x8d5\x9f\xf1\x10*\xe2;k\xc7\xcd?qku@G\xec\x92\x18\x86pl\xf3\xcblJ\x10M\xf3\xe4\x04z$TP\x8e\xd4\x9ac`\xfc\xef\xdd\x13\xbd\x98\xdaF>\x99\xa5\x13-\x83\xc6\x88>\x0b\xdb\xa2\xf5\n%\x01\xe6\x15\x11#$\xd2N\"\xd2IS\x95\x97q\xfc\x0b\xdb\xe2u\x02\x92$\x90.\xbc\x10\xaeh\x8d\xa5\x17_Zl\\\xa8\\\x15`\xc3f\x85hw \xd6\x82\xfe\x11\xe1\x95\x19\xde!\xf8l\xe1\x91\xbf\xe3R\xf94\xc2\x01[\x8e+}_R\xa9pMQ\x05\x80:\x8dRI\xe3\xa8*\xd5\x1c\xb9\xc9\xbe\xab\x08\xc2l\x05C\\A\xbe*lic~\xc4\xf7\xe0 \x17\xf0\x86\xfc\x88<0\xe8\xb5\xd0\x0e\xc7\x91u\x7f\xdb\xa8\xec\xd4\xce\"\x07\xa0aFa\xb1\x95$\x85\x07\xc7\x1f1T\xd4\x8d\xe7\xd7(\xa5\xbb\xa8\xb8\x92w\\Q\x10\x9f\xb7\"(R\xc3\x9a\x0bM\x06q\x07\xfc\x04\xc2(\x05\x7f\xb9\n\xc8\x92\x84)\xa9\xd2a\xe5\x06\xc2_\x91\xd67\x10\xb5\x01\xd5\xa2\xb6\x97\x13\xc9\x95\x8f\xae\xc6\x91d8eb\xad&^B\xa07\xd4\x96\x01:\xe0\x0b{\xac\x1af\x0f\x99 }1\xb6\xdfo\xd3\xfe\x98\xfft!\xad\xc9\x13S\xd3\x15\xbfOi\xec\x8b] 5^wI_0\xd3\xb3\x0e\x95n\xe9\xce\xc7%\xc5 \xa0\xa3?N!Z\xa5\xc9\xe8\x8f?Yn\xa9\xb6\x9e\x1f\xa3\x8b\x8c^([\xcc\x90\xb0\xcf\x15r$\x9c\"YJ\xf9\x1dP\x92N\xa3,U\xde\x908\xa6\x92;\x0c\xe1\\\xb9%\x80\xb2\xc3\xb5\xce\x88X<\x0b\xdb\x8a\xc2,\xa4\x03\xb5\xd8m\x92\x08\x88\xca.\xdf\x99\x1e%\xee.\xbc\xe4=\xd6b7\xd8\xa5\x17\x8c\x06,lk\x12\x10/\xccVB\xa7\xb6\x8c\xd6\xdc\xf6\x8d\xc4vn\x1e:\xd7\x96\xce\xfc\xd0O\x16\x96\x0bKm\xf14\xf6\xfc\xd0r!\xd0\x96\x8a\xfdy\xad-\xe5\xb3saB\x89G\xf5\xe3\x90\x92\xeaYM\xd9\xb9\xb6\x8cS\x9b\xb5\xe3\xa2\x85/\xde\x82E\xb2\x96\x10\xaf\xf5\xcf\xafb?-]\xbcn\xa9/\x91\x08\xe6\x9f\x04\xfa\xa8\xf8\xe6\xf5\x9d\x19\xaf\xa2qm\x913d\x86{\xd3\xc68P\x808^2\x18\x91x_\xe4\x11\xc2n\x14N\x88\x00\x0dZ\xbeu\xa3\xb0\x04e=\x9e\x07\x8d\x14\x174v\x15Mrz;\x01B<|\xb3\xbe \x9fs|\x92\xd5\xba\x8e\xa2\xe5\xc5\xf3\xa7\xf8{{\xbb8\xcf\xca\xb58\xfc\x8c+\x8cQ1m\x886~(h\xc1\x7fc\xeb\x84-\x06\xe3b\x17\xe8A\x8cx\xa8\xd1-\xac\xb9+9-3#\xd2\xda\x9c\xab\x171\x89M\xd0\x05\xa1\x12\xe7\xd4*\xcd\xadq(\xfa\xb2\x83\xdd\xees\xa9\\\"\x97\xe8}\xc4\x89\xbb\xf0<.Ux\n}Z\x89\x87_=\xb1\x0b\xfa\xcf\xe3t\xae\x04\x135\xf3\x82\x84\x00v\x0b1IVQ\x98\x10\x17\x84\xady\xa8^\xc0\x96\x96\xb8\xa6\xb4\xd3\xe1\x93C.\xa4\x8b\xedm\xba\x1b\xaf\x1b\x80(H\x15q\\8\xb7\x1b\xa9\x19C8\x86`\xec=;\x17\x14\xc6D\x17L\xb1f\x90s\xe3\xb6j \xcc\xe7Z\nb\xeehYO\x9bx\xdb\x8d\xc7\xc5\xa6\xdd\x9e\xd7u[\x1cva\x97\xfdnw\xf6\x0by\x96\xed\xc4\x9c\xf8k\xbbi{;\x00P T%\x1b\xfb\xaeb\xb2\"\xe1T\x00\xa5\x08P\xae\x96\xb0h\xcd5*\xf4\xee9\x9a\xf0%\x0cy\xf8\x1fcr\x06\x07\x90\xd9\xf2\x0b\xf4n\x92\xfe.[d\x95>\x1d\xc18tK\xaf\xce\xb0\x8a\x08\x1e\xad'x\x12*\x8b\x03\x9b\x1d(e\xfe\x80\xbdS\xb8\x02\x86\xf4\xfc\x9c 1f\xa1 \xb4\xfcn\x0fY\xb1\xe2F.\xe4\xb7y\xb6S\xb9\xd4\xaf\x18\xc1T\x18\xf3Z\x9d\xd5&*\x03\xf3\xda\x17L\xd4P\xbdL\x15\x8f\xc6\xc9\xa5\x90\xc3I\x89\xa3\x17\xd8\xa1\x0d_O?\xea\xd7|T0\x97\xbc\x9c\x07\xccfV\x1cBb\xe4exT\x96\x1d3H\xc5+\xa3t\n\xf6\xb95\xbcX\xc4\x9c]Hy\xc4YnH\xaf\x1f\xf8Vmp\xd2\xb8\x18\x98Y\x83\xedCy\xe6\xfa\xcd\xb2\xe9\xac\xf4\xad\xe4\x8a4\x16\xe7\x1a\"x\x02\xfec\x88:\x1d\x07\xe2qtf\x82A\xad\xc2\xb6b8\x04Z2\xb5\xe61\xdcNlR\x9c\x9f5:8D\x89LZl\xfeY\x97eg\xb03\x17\x9d\x97K\x80\xd8F\xc9\xa7\x8aM\x9c\xf9\x11 \xe4\xbf\xc6\xbd3i\xf7\x9a\x16\xbensF\x95\x1b\xd7:\x899)}Y\xb8Ap\xc3\x0d=\x861\x8a\xce8\x13'gm\xcc\x06h\xb9\xeaA\x10\x18\x8dRY\x84,)lVD\xfb\xf5\xb8\xdcJ\xa8\x07\xbc+*+\x91c\x8d\xcb\x11\xdd\xb9\xba\xf7\xecB\xa4\xa2\xc9\x89\x0d\x0eM\xb1\xa4\xec\x8a%}\xceq\xae<\x94\x04\x85K\xbe\xa6\x9b\x1c\xabu\xeb\xefM\xf3\x93\x0eF\nf\xb8\x8a\xaa\x18m;Z\xc4cL\xdb\x02:?s\x95\xa3\xa68eR\x85\xddo\xc4T\xe0f)eC\x13a|T1?)\xdf@\xbc4GP.\xa2\x9c\xeb\xec\x0c\x15=\x14\xe5n\x9b\x00U\xa8Z\xe9.b\x1c6\xf0\xc92\x1dG\xcd\x16q\xdc\x96\xfb\x08\x0fnd\xde\x0d\x16\x94\xca9R(\xe6\xf8W-\xa6{\x15{\xab\x8dN\xf7\x9a\x1b\x80\xb6g\x7fl8\"\xf2\xe3\xc1\x07?\xe4\xa2\x1d\xd7B4\x89\xbd\x94\x9c,l\x8b\xcefE\xa6\xc0\x85\xfb\xb0\xec/!t\xf1\xf5\x92s\xca,\x1f\xda\xb9A\xf1\xb3[\xbe>0i\xcd\xc0x\x8dI$S\xed*\xf2\xe6\x9a\x04\xce[\xe7\xb00&\x1e\x94!!\x84\xd3\x12(l\xbf4G&\xa7\xfa\x14]\xb6B\xc5o$W*\xa3\xa6^\xb2\xde\xf7\x99Ho\xab\x1f`=a\x95\"\xc4~\x9c\x9f\xef0\xa2+t\xe3\xb9 \xa9\xdb\xb2\x0e\xdaLJ>S\x14\xbb\xc6\xfe\x19\x94\xe3\xd2JR\x01/\xb4EE \xa9\x9b\xdc\xed\x1b\xd1K\xaa\x9bR\xe6\x9f\x87\x81\xadM\xe5\x07\x065\x86\xaf\xbb.\xd7qF\xf3\xfc\x8a\x11\x19$D\x82\xf98:\x93vz\xf7\xc2\x0f\xa7\x9c\xba\xd1\xa2\x1a\x8f\x9cT\xf6\xa6l\x86\x8c\x84B\xe7\xfc\xfe\x908\xc2\xfb;\x16\x14\xa7\x10#\xaa\x13\xd5\xd3\x9e6\xee&\x82\x84\x94|\xbb\x9b\xa3\xd8hL\xaa6rM\xd1Q\xd8\xd2\xc5Qu\x8e\xe5\xd9\xa1\xdf\xc7\xf9,\x8e\x96\xf4T\x86\x11\xbc\xfb\xa7\xa2\xac\x1c1\xdb\xc50\xd8\xed\x02g\x97bpW\xa3M\xb4iB\x1fNc]\x84\xbaz\xa4\x8dI\xeakO\xea\x1a%\xcb\x8dv\xd0\xe5\xcf\xb9\x1bK\x0b\xbb\xa3[_\xf5@\x93\x1bQMd\x01\xfc\xac\xa2\x9c\xd6\xbc.Z3\xee9t\xb2\xce\x98\x9b\xde\x01\xfa\xe0\x14\xc6\x9b\xed\xfbA8\x97\xb8\xd9\x9c\xe7\xf1\x85\xb8 |,\xd0Z\xc7\x00\x91F\xcf&\xe9\xde\xb420\xbb\x16\x02\xe5\x8f\xf9k;\x8f(\xee\xb6Ppo\xf1$\\\x07\x94-\x97'\x18\xb2\xd9\x85\xbaA\xa9/\xcb\xb0\xc2A\xe1\xed+\x9e\xccZu\x96A\xcc*\xfd\x99;d5\xd0\x92[\xc3\xbd\xafg\xef\xe2j\xf4\x85\x8a\x0b\xcd\xb4\xb6\x05%\xaa\xc3\xe7,o_\xfb\xadf\x04\x95ru\n\xe5\nL\x95U\xdf\x86\xb2\xa8\xaaO\x95B~>?\xf6\x9f\xec\xa4\xc8\xb0\x12#H\x84\xec\xd4\x9a\xca\xe1\xf0\x13\x12\xcch\x15\xfc\xf7\xd3'\xb8\xf2\xc3itU\xa5/\xbe>\xb272\x12&_&}\x00\x7f\xc81\xcd\x9f\x16\xaeS\xdds4\xc4~\x816\xc8\x06\xf0\x00\xf2\x9a I\xdf\xf9K\x12eiK)'$W\x10\xd9>;\xc0\x8a\xaf1\x1cB\xc1\xff\xb8\x80\x03\xe0\x85\x15\xb5\x05\xf6\xfb2LI\xbc\xf6\x82[v,>\xd7\xf7,J5]\xcb#C\xfdK\xe9\x83F\xf1\x873\xf9\xa8\x88\xad&\x96\x8fJ\xda\xd2\x98\xcc\x94\xec/\xec\x8d<_\xe5#l\xb7 $\xa55f\x10\x89\xdd\x1c\x0f4s&a\x1c\x05A\x1b\xfd\x90\x0c\x1d;\xa5\xcd\x05\x84\xff\xf9r\x8a\xd2\x87\xfc\xaa\x8a_\xb4\xb7,\xd4\xf4w'\x9d\xa9\xd6p\xb4\xb7s\x84\xf3\xe1$\xf5\xd7\xe8'\xda\xf5\xc4\xcf\xcf\xe9\\\x7f?\xc8/R\xa5\xaa\x1a\x8dV\x91bQm\x15FPl\x99\xe6\\ri\xf7<\n\xc5\xe4\xd9\x9dD\xfe\xb7\xee\xb2G\xe3q\xe5bD\xab}G\xec\xb9\xe5\x92L}\x16\x9b\xa5\x99\x84\x95\xbfP\xb2e\xb2\x01\xa95(\x0e\xe6\xac\x8b\\\x98\xef\xbc\x0d\x87\xa0|\xa3\x1dD\xb5Ni\x18\xe5\xe2\xe2|\xb8M\xde\x9a&\xde\xd9\x14P\xcdGU\xa2\x9f\xc8Q\x88\xea\xd1S\xd8#\xe1\x8d\x82eA\x07R~\xab\x99F\xdfDW,W\x8em\xb4\xfeF\x13\"kA>Zz\xd3\x1eV\x8eq\x90\x1a*l\xd7\xd7\xf0\x92\x89\xef\xd7\xd6\xb8\xf0C/\xbe\xae\xaf\xe2%d\x7f\xb7~$\x93d\xd0Ta\xbb\xa1F:\xeb\xef\x07\xa4\xa9\xcevc\xa5\xd8\xbb2\x94\x83\xe4\x9fm\xc8+\xd9hq\x95\xfbwWwxys\x1b\xf2\xfc\xe8\x18\x19Ee+\x90\x0b\xf7\x07i\xeb\x07.(`3\xff.\xae\xa3\xf8T\x18\x9e5\\\x03\x91\xc7\x8f\x9db`u\xca\x97F\xdc\x85V\xf8+\x9e\x16\x83\x846h\x08\xadP\x11Z\xa2#\xb4EI\xf1H\xd3\xc0\xdaM3 \xbc\xd4\x0f\xfb\x8d\xbd\xd7\xee^\xf1\x88\xbey\x9bM]\xd7nwhEZ\xa0\x05\x8d\x13\x8fP\xe9\x98\x87\xd5\xb8'A8X\xd4\x87\xd8\x12\x0f\xa5\xd96'\xdaez\xcdbQl\xf5\xb4\x9f\xeb4\x84\xba{I\xbc/\x13\xd12\xb6\xca\xc1\xc5\xed\xd213\x1a\xf1X\x85,\xbdQ\xd5'\xc4z\x1f^\x86\xd1U\x08\x82\n\x0c\x81\x0d\xdb\xa8\xc7`\x07l\x99\x12\x15a\x1d\xf2\xb8t:\x8e\xab\x05\xdac#)\xf9(\x92\xc6\xb06)\xe74a\xa0\xd3Dh\x04\xb3\x89k#\xa9\xc0\x0ef~\x10|\xe3\xa1\x96\xce\xbb}/\xb5X-\xcfkV\x9aW\xc0z\xdc\xd9\xa8\xc7Z\x84\x95U\x98\xcc\xfek\x04+\x96f\xdc\x96:^\x98$g\x10\xe3\x0d\xbc$}MP\xce\x16\x81\x11\xe9\xabwQ\x8a\x82\x92\xfc\xeeh\xe11\x8f:\xd9\x1b\xb0\xa4\x0c\xcc\x7f\xe6gUV\x13\xd6\xfa\xc9\x08\xfa\x83\x07\"c\x03<}\n{0\x1a\xc1>\x1c\xc0@\xbc\xd9\xa5o\xfa\xbbp\x00;\xe2\xd5\x0e}\xb5\xd3\x83\x03\xd8\x15\xaf\xf6\xe9\xab\x01\x1c\xc0v\x1f\x86\xb0=\xa8\x1d\x92g8>\x852\xb0\x98\xfev\x19DU!\x7f\x13\x07h\xb4;\x19<\xa4{\xd9\xee?\x1a\xc0=L\x0f\xebH\xb6L\xe5\xa5\xb0\xfe\x9f\xff\xeb\xff4PY\xf40*\xaas{A\xc91\xac_w\xb4\xea\x06\xd27\x0d\xa4_;\x10\xd0\x0df\xa0\x0c\x06\xffV;\x1c\x98:\x1c\xf0\x0e\xdb\x13O\xae\x0f}\xacC2I\x90\x08\xd1\xbd~\xa8`\xfd\x13\xc9\xd7\x0c\xa3y\xa1Wf \xe5qY\xe5}@?t\x94}\x91\xa7l+\xf3[nuS\xb1\xa8`\xb5\x1d\x89\xcb4y?\xe7#\xde\x96\x02\xa0\xd5\xef\xbdD\xab\x01\xa0\xebe\xa7\x85'\x10q0!\xf9\x08\x1dWjt\xf2\xc5\x0cs\xf2n\xb6\"\xa9\x0f\x03\x80\x97\x91\x93\x85\x17\x1fESr\x98\xda\x92\x07\xac\x1aWZ<\xb4\xd1\x98J\xdd{{\x83G\xfb\x80f\xf9OF\xb0\xb7\xbf\xd3\x7fT2\xf8Rp\xa9B\xd0v\x95\x85\xe3)\x9a\xc7\x12D\x06gj\x9d~\xa5N\xff\xcc\x85\xb0pS\xd7\xe6\xd9\xae\xbc\xd1\x9bxh\x89\xa32\x93\xbef&\x83\xe6\x99\xf41\xe5\x85v\xe1\n4C\xa8\xd7\"R]\xaa:\x90\xef\xc3\x0f\xa4\x03\x89]~X\n\xe5@jQ\xdaH\x0d\xf7@fr\\\xc3\xbdtL\x9bS\x82@\xaf\x1a\x0eL\xb7\x12\xa4\x1623\xed\x16\x13\xe3\xafl\xb3\x1d-\x91\xeaq_\x93\x83\xd2ZqV\x83\xbb\x9d\xd9*F\xec\xc06\xde\x94\xa8X\xb1#\xec\xd1B\xb1\x1a\xb5\xf8Qj\xfa\xb3\xf6\x83\xe3\x1a\x86_\xc2\xb4\xb0\x81f\x05w\x87j\xda\xadtP\x8b\x1d\xf9\xa0{.\x02X\xc1\xd4a\x036\xac\xcc\xcc\x8e\xe1|\xa8\x07\xc6\xa2\x86yj\x82\x85\xd4\xb0\xf8E\xca\xd1\xdcX\xc6\xc7\xa8d\x1b\xe4\xa7\xf5\xc2\x7faq\x9b\x9fA\xb9`\xa8\x80\x1f\x97\xcdU\xdd\x9e[\xed\x7f\xbfHB\x87\x9e\x989k&\x98x&\xe7\x18:\x06\xd9\xba\xf12u\xbd\x84\x02>\x1e}\xae\x9a\xdeJ4\xb2\xbd\x8d\x83\xa1\xab\xb7=`bv\xdd\xc0\x90\xb1\x92F\xe6\xb4\x1e\xc3\xe0\xf7\x1f\x03o\x0bC\xef\x8cD\xca\xbc\xf2\xa8v\xf4\xa3\x12\x9d\x97\xb7\x8f\xd9\xb0\x98\xe9 \xcb[\xbeJ\x15E\xb8~\xf5\xeb\xca\xf9\x16V\xa9\x8c\x1c\x9e\x01\xb6\xc1\x0e+\x94[\xbf1\xb4,x\x8f\xf9M\xeb\x86FKL\x1bFR/\xd4S\xcf\xf2v|\xa2!\xa4\xfaq\xd5\xf3Bw*\xa0(+p\xeb\xe1\x14bLy\xd2\x92\x04\xa3\x9cR\xb7\xba\x99)e?/^\x17\x176\x035y\x1f\xcfq\xae\xcf\xcb\xac\xd1\xae#\n#\x04J\xd9T\xca9\x13\xa2j\xda\xf0\x92\xc9}n\x8b\x91\xc6^\x98\xcc\xa2x\xc9\x8c1tn1\x18\x17\xfc\x9d\xa8\xd7\xc2r\nT\xaeY\xe9E/T\x85\xdd\xbcV\xbd\x1fG!\xb5\xe1y3\xb90\x0bi[qY\x1c3\x06\x0e`\xcc\x06\x85\xd0\x857\xb9\x14qj\x96Y\x90\xfa\xab\x80@\xea/Ib\x8cw/\x06\xb2\xc8\xc2\xcb\xdcG%\x1f]\xf1\x86\xa7\xec*L\xadx\x1aWW\x93O[<\xe2\x80apl\xe1}\xe0+\x86;\xb6_ k.\xecc\xe1 \xf8\x9a\xa8\x1bEW\xb6Z\\\xe9\xf1\xa6\xb0\x01\xd58\xdd\xd1\x8e%\xc4\xd1\xd9H\xcak\xae\xaf\xc1\xc1\xc8\x82]\x98\x8a)\xe8kk\x14\xdafZ\xa9|\\\xe8\xad\x97t\x0154\xd5\xa4P\x1e\xb5\x89E\xf2\x89J\x06O\xc5\xbb\x91\\\xc3\x9cgd\x16d\xc9Bj\x80\xfd=\x12%\xc2\xe4\x1e\x0d\xb6W1\xc9\x1d\xf5\xb2&\xbd\xa8\x8e\x9d\x12\xbe\x18e<\xd3\x8fL\x1a\xcd\x81\xfcW)g\x9a\x96\x19\xf3r\xdaZ^\x14\xcaDz\x9c\\\x15\xfb\xa7~\x1e\x9e\x89\xeb+\xdd\xa4hLH\xabLB)\xb1`Z\xc4\xba\xaf\x84 \x10\xe7e\xe5\x9e\xe3\xc8\x0b\x02\xba\x0d\x8bE\x9eF!\x81\xab\x05 \xe1*\xcf\xa8\xb45\x82\x9e\xa5\xe9?U\x89f\x89:n\xd8]\x92\xfaAP\xdajj\x979d4\xbe\x00\x85\xcc\xe6W\xf2\xaa\xb9\xd2;;b\xdcJ\xb4adw\x99@\xab\x93.Q\x90\xdc\xe9\xa9\xdc~\xc5\x97\xac\x18yy0\xa5\xfd\xd6$(T\x00\\|m\x080c\xec\xb6*\xc9\xea\xbb,{\x9a\xd5\x9d\x99(\x9b\xc8\x07\x0c\x85J\xe9\x10J\xf37\xd2m;qa+V\x10I/\x1e\xb5>r\xecXY#<_\xbe\xd0\x89sc\x04\xb1\xeaYP\x7f\xa9R\x0b\xdb\xdc\xe7\x84\xc8\x10\xc5[\x04\x01p\x16B\xb8\xc4\xae`\x0c&\x95\x81\xe9U\xb8,[n\xd4\x15M\x16\xfc/\xe9\x96\xb9-f@\\\xdd\x06=#$Z\xe6i\x90\xf93\x95Q\xac\xb6\xa6l\xb1z{\x0c\x96{=\xe4D\x969\x90\xab\xc4]!.\xb7b\xb5%\x9eZ\x97\x89\x17sH\xcaBQ\x14\x1f{\x93E\xb9\xa2\x94\xe2|\x12\x93\x12.\xb4K\x8b+\xf0*bDSKU\xb9\x0din3\xda\x04@Lgz\xef\xde\x06\x8c\xb6\x9e\x15DK\x97\x10\xbd\xd9\x1c \x18\x04\x10\xd2qxV\xa9|c\xf3\xb4\xb8\x18\xc9X]+\xb7\xa4h\x84\xdb.\x97\x16\x9e\x0e\xfc\xfd3\x9a\x940`\xc7iZ93\xcd\xf5\xf5\xab\x96\xbc\xf6^\xdb\x98X\x16\x95\x18\x84\xa9/\xf0\xe2\xee\xde=\xae\xad\xd8\xc6\xc4\x0c>\x86\xb6\x1e\xe6\x8e\x95x#\xd4\x9c\x1d\xb9\xd5\x1c\xcb\xfe7\xbb\x0f\x06\x8eM\x87\xc4\x91\xd6K\x12\x7f\x1e\xc2\x10\x8bv>\xd7\xa2\xd0\x05\xdf\xc5Tr.x.\xcf\xe6:P\x13\xa4N\x9aH\x0b\xe8\xee+\xe8#\xe7\xcc\x8f\xaf\x95\xaf\xf4\xaeY\x13\x17x\x08@\xad\x07\xd6$\ng\xfe<\xab\xc9$.\x985\xbdl\xd1\xe4\xc1\xb5\xf6\x82\x8c\x0cA1\x02\x96\xd6\x15&^n>V\x9cN\xec\xcec\"]\xe5\xc6\x15\xc9\xba~\xe8\xe6a\x97\x87\\\x8c\x84\xc55\xd4B\xd1\xdd8\xa12\xa5h J\xa6\xb9*k\xc4s\x06\xa60\xa4\x87>B\x86\xb1\x14\xe8\xa7U\xacR,_\xaa\xe0m\x11\xcfn\xfc\xe8\xa1\xe3b:\xd4\xf1\x19\xcbl\xdd@U]\x9d\x02\x9cr>\xde8=\xcb\x99y\xfaG\xb9\n\x92=\x82\xfd<\x86t{\xfb\xb1#|\\-\xcf\x82\x0e\xd8\x9dN\xe8\x14\x1a\xa8\x9d}U\xae\x97\xf4(\xc2i\xc2\xb6f!K\x98\x8bE\xb9\xc4a\xd3\x06 \x0fq\xef\x82\xe5@\x87\xfe\xef\xef\xa2\x8dY(\xbc5\xf1\xec,\xdc\x06\x1e\xc3\xcd\xe32\xcb\xd8z\x8d4\x14\x1f\xe5\x1b\xc3\x9a\x15b\x8f\xc2\xe7\xe0\xa9E\x9c\x8a\xea\xa1\xba7\xe9\x93\xd9\xe8\nU\xde z\xf4\x07\xdd\xed\xf2\xcd\xe7\x12'&r\xe8\xb2\xad\xeb\x91\xbeTM:\xe7\xe7$}s\x15\x8aj\xcfI2\x89\xfdU\x1a)\xf6\xd3\x99\xe9\x83\xd7\xdeR\x0dh\xe2\x99\xea\x9e^//\xa2 iq2i\xd7\x98\x91`~4\xc76Q\xf1\x14\xe5D\xb9\x06\x86\x18\xc8\xec\xc4\x11\xccN!~kC\x0d\xeaW\x1a\x9b\xb6\x99\x87M\xc4\xc2\x14j\x14?\xf2\xd2k\x9b@\xee\xb2\xfa]\x19\x81L\xaa\x0e\x0f0\x82\xdb\x7fY3\x91\xed{r ]/g\xffS\xb9\x95\xcf\xdc\x15}\x1d\xff\x1b\xda\x0fUUs\xa4w\x03\xa3\xdc\xe9mq\x94\x9ek\x9a,xt\xfb\xe4\xc4n<8\xd3B!Fj\x85\x0b$w\xc4\xd8\x10O\xb7\x1a\xe18>C\x07'\xe1H\x91\xa1<\"\xbe\xa8\xacH\xd8\x00g\xb9\x8fv\xfc>\x1f\xfa\xd6\x16W\xf6\xb1\xf0\x03\xe5\x14r\x9f>\x19\xb4d\xc8\xd5\x9b\xf4\x83\x0b\xd24\xdaVX\xa1\xe7\xa3\x88\x0b\xd6\xf99I^E\xd3\x0c\x0dN\xd4\xa5D>G\x16+Yt!/N\xc8\xf7\xde28BnE\x93\x16\x7f]D\x88\x0e\xed\xbdAO\x83q\xc8\xfc\xb0\x80\x0dq\xb7\x18\x04\x1c@\x0cC\xcd\"\x0bSS5\\p\xd1\xa9n`\xb5\xa8\xaa'\x0f|-#\x91\xe3\xaf\x9bx3\xf2M\xe4M+ \xacjID\xce3\xb1\xd0\xc8q|\x88\x03I\xba!\xb9zG\x89@x\x1c\xc7v\xa1IB*\xad\x1c\x97\x1bz\x916\x11\x84\x9d\x87\x06q\x88\x8e\"\xb6\xcbs\xf0\xc3I\x90M\xc9\x10\xc6\xa1=\xe8\xed8g\x12\x12\xfcC\x07\xd3\x1f\x0c\x9c3\x85\xb0-W\x81?\xf1S,\xdf\x1b<\xc0P\x06{\x83\x87\xfc\xdfG\xec\xdf\x9d\xde\x1dM\xe2N7S\x10y\xcc[\x99t\xdf\xbd\xf9\xea\xabo\x8e\xcf\x8f\xde\xbc~\xf1\xf2\xabS|\xf5\xfe\xed\xf3\xc3w\xf2\xab\xda\x9d6\xe8\xed\xfdN;-[M\xbd\xaa\xf6\xd2@\x165\x07\xf3\xf5\x8a\x0c!\xab\x9e\x10+\xef\x9a\x02d\x08v\xcf-\xb6\xa0c\xff\xfdF\xd5\xe2\x02(\x9a?\xd2M\xa3\xf9<\xa87\x0ej\x18\x91&\xabJ>\xa2\xd4\xd4uy12\xfd\xbaYL\xb2K\xce\x19\xe4\xac*\xaf\xa8Y\xff\xfc#63K^\x81\x1cod\xad\x89n\xaeU\xad\n|\x1eA!2\x12\x8dJ\x0ef%l\xec\xef\xa9\x0c\xc8\x97\xc2F^\xa7\x85b'\xa7\xca~\xc8\xe2:\x94\xd1\x8c}U\x1d\x04\xdf\xbca\x83\xae@\xa3i\xd8H\x17\xa1\x18\xac\xa0\xa9\x16\x8b\xde\x19\xba\x9br\x87\x94\x1a\x10\xf9\x1c\x18\xdeQy\xa1\x8f\xb7\">\xdd\xd1\xd6%\xb9N\x90\x91&\xdc\xa3\xc2\xc2\x1d\\\xbc\xc3\xe47C\x16\x14w\x1c\x9e\x9d\x95t.\xa22\xdeZ\x1e\ny\x05%\x0c\x0e\xe9\xd8f]\xa0\x91\x86T\x1d\xc3\xd0\xa7\xb1O\xff\xd2\xe2O\xa3haT}7~\xb9\xd1\x01\xcc \x9a&\x18\xde4\n))\xda2\x1ew\xb7\x1c\x9d:4\xbf\x1cJyK\x96\x87\x98\x90\xfc\xeezE8o\x0c\x1d\xb0\xc4\xed\xaa\x977\xbae\xba\xafn\x18\xec\x86\x9b\xf8\x91~\x0f\xef\xedj\xb7\xf0#\x95\x05\xcbP\x18.\x1a\x0e\xed\xc1\xbecg\x94\xf2\xec;\xb6\xe5\xa7$\xf6\xd2(\xa6\xe8\xd3t\x94\xa7r\xf0\xb2\x1b\xa7F;\xa8\xbb\xba.h&\x8c \xa6#\xa8\xe2EH>\xa6t\x13i\x12\x91\xd3\xdd\x80m\xe3b\xbc\xcc\x87\xbd\x19\xb0%\xf5\x84\n?N\x1a\x1fh\xc1\xba\xdb3\x93\xc0=\xe9\xea\xa3\xc4\x94\xfb$i\xca%\xe8W\x14\x9dEf-\x17\xd7.B}\x04\xe5\xd02N\x81\x98\x06\xae\xf7\x18\x85\xbd\x07;\xbb;\xbc\x7fV\x1f;\xa2\xc8\x82\xce\xdf\xf4-\xf3\xc2L\\\xecd@\xcb2\xd8\xe6\xcdt\xe88\xb7\xf9\xa0\x9e<\x81~\xcf\x81\x0e\xec\xef\xed\xed\xec\xdf\xcd\xa6\xaf\x1c\xa9\xfc\xe0\x18\xf4\x8dg\xea\xc0\xe9\xceI*\x0e\xf9\xe6[Y\xa4\xf3\xeaIjd\xf1H\x03\x8b\x87<\xd1E@L\x0c^l\x13n{\xe4\xdcz'\xf6w\xf4\xd7#\nOV\xa10(\xa4\xb5\x03\xdb+\x92.\xa2z\x034\xc9\x8dl\x0b\xa3\xcd\x0b\x9a:\xf6\xcf0\xc0\xc5\xd8\xfa\x97\x7f\xc9\x87\x83\xaf\xa21\xa5Ng\x9b\xcd\x9b\xae\xf6\x0eJ\xbb\xfd\x1d&\xf5\x0evv\xf9\xbfLM:\xd8ej\xd2\xc1^\xaf\"\x0e\xf7\x1f9B\x14o\xd3Y#C\xad\xc3G\x99E\xf6\xc7\xa1\xddwlK\xdc\xc6\xbf\xf3\xe6\x96s\x06#\xb0~\xc1L\x8d\x1d\xba\xcf\xb7F`\x8d\xd9E\x0b\xfcrf1\x1d\xc1N\xcf\xe1VK\xa5\xe8\xbd\xa2\xa1\xba\xb0\xdd\x1c\xf2y\x9b\x16t\xe89\x80\x01L;`\x9d\x95\x9c\xe3\xb6\xda\xe9\x07d0n\x85\xf6\xee\x80%G\n\xed\xdd\x1d\xc7\x1cx\x8d\x8f\xe4\x01\x9d\xa2^\xd7\x1c\xda\x8f\x1e9\xb65\xf5\xd7Tl\xb0<\xad\x19\xccF\x81\x86\x1fT\n\xd5\x9b\xcc\xaeW\x00\xa0\xd5\xe4%]\xbf\x89\xd0\xd4\xb3\xe6\xe8\xaa\x81'\xb1\xdeV\x813\xe9~\x95\xea\x10\xd3\x95\x9a]\x8e\x13\xc0\x96#\xe6\xb1\xc7\x05I)|\xd1j\xe9\x99\xda(\xca\xd4of\x9b\xb7\xb9\xf5e\x86\xab\x92X\xeb\xc8\x0b\xff\x94\xc2$\n\xd7$N\x81\xa3y\x1a\xc1*\xf6\x97>\x06+\xc4)l*\xd25m\xf7\x81\xe1\xfc\xe9\xef\xe8%\xe8~O\xe5_\xaa\"t\xff\x01\x17\xa1\xfb\xff\xaaE\xe8\x87\x86\x83]}\xcf\x01\xbb\xab\x03,\x05x\xcf\xb1\xad\x97\xc7\xe7oO\xde\xbc{\xa3\x1ez\x9e\xaa\x9e*\x17\xab\xda\xab\n\x15U\xba/F\x8c>?\xf9\xe1>/b9FxXV&\x1e\xa7\xdd\x17\x8f!F\x8b\xb3) HJ\xe4\xac7\xe3h\x1c\x9fir\xa6\n.W\x8d\xed\xaa\xa7\xa3%c\xe5rP\xc7v\xa6b\xbc\xbb\xdc\xca\x1d\xefF<\x05\xdd\xd1\x80\x1b\xd8\x0d\xad\xe7B\xb9\x98{\xe3\x8c3\xb4'\xc6\xec\x93hzVX\xc0\x8c$}\xac\xcf\xb2\x19\xdf\x16\xf1\xf7\x0c\x14\xc5\x80\xf75\x1c\x1b=\x92\xff5(\x8f\xf6\xf4\xa4b_wEG\x99\xc2\xbeco\xb5\xa3\x16\xb78\xd99\x80<.5T\xe9\x00\x82\xa8\xfaz\xc2\xcc7\xab\x10Gsv\xcfaJ\xa2\x8c\x19Z{\x08\x8b{\xf7`\"\xfc\xb44\x1f>\x96\xa3@\xe1j\xe0w\x94,\xe0Z\xb0d!\xff.\xb2'\xd8\xda\xa7OEk\xfa\x05\x9a\xdcv\x81vM<\x12\xb7\xe3\xb3~\xb1\x1c\xba\xe1\x90\x01|\x99\x1c\xe7\xf7\x8ev\xaf\xc0\xe0\x12\xc2\x9a\x18\\\xce\nS.#f\x96\xec)&\x10Km\xcb\xa2\xfb6\xb7\xfa\xbf\xedT*H\xc5pmWg\x9c@ \xb6I\xb5\xdb8\x95\x92^\xe2\xdf\xf4\x94\xff\x15\xe9)\x0d\xe4j\xb0\xa3\xfa\x1dD-8\x18\xc9j7?\xb1j\xcf\xd19I\xdf\x8a\x8aof\xf5A\x92s\x90pZF\xf7\x94\x0b\x11n\xabqt\x06C\x93i\xdf$\n\x934\xce&i\xc4r\xe3\x83\xe4\xb7_.=(\xff-\x1d\xbb\xc3\xf2g\x9c\x08\x1c@\x06\x8aG\xf3\x86\xe0\xef\xdfzK\xcaV\xc7\x9b\xf5\x9e\x1f\x9d\xc2w\x07\xfdH\xf3\x03\xdc\x15\xda\x97\x9e\xe3\xf2\x93h\x8f\x1f\xad(\x0e\x08\xcf\x94\xdd]\xc7\xc5\xfdLe\x03\x177\xed\xa4,\"\x04\xecUI\xb9\xc0\xf2\x82'\xe2~wQq\xcc8:==\xc9XN\xbe\xaa\x19\xc7\xd1\xe9\xe9)eH\x9f\x93I\xe0\xc5\x1e\x9da\xd5E\xe3\xe8\xf4\xf4\x03\x15\xafx\x13ji\xe0\x930=!\x93T_\xfe\xfc\xcd\xab\xdaB6\x17c\xf1\xbb\xe8\x92\x84\xfa\xc1?\xf7R\x8fy\x11\x92\xf8eJ\x96\xfa6^\xf8\x81a\xe4\x7f~\xf7\xea\x9b\xc3 8\x8a\x82\x80L\xf4S\xa7U\x9a\xca_D\xf1\x92k\xbb\xf5\x15N \xfd\xdeX\xe5\x15\x99\xfa\x9e~\x86\xaf\xfc%\xa1b0.n\xf5\xcb\xd7\xde\x92L_GS\xf2\xca[iJ\xa3\xa9a\xd5\xdfz>]\xb1\x9f3\x92\x18\xd6\xe5m\x90\xcd}\xcd|\xd9{\xc3pN?|\xf5\x0d\x1eC\xfa6O?|\xf5:[^\x90\xd8X\xfc\xd6K\x17\xa7\xc4\x80\x0b\xb4<\xf2C\xc3\x80O?|U\x87H\xa7\x1f\xbe\xca\xfdM\x0d5\xa2,\x9e\x10\x16z\xdeP\x83n\x94\xd3\x05!\xa9\x1e\xaa\xef\xc8\xc7\xf4]\xecM.\x8fL[%\xafa(\x8e\xb2I\x0e\xbb\xbc\xe4\x86\xa5\x0b\xf7m\x0cY\xc98\xf05<\x81\xa9\x904a\xdd\xe9\xe8\xf8\xd4k\x17\xe60\x82\xe9x\xad\x18\x9d\xd2g #X\x8c\xe7\x9a\x92sd\xe7u%\x170\x82sJ\xf1\xcfu\xa7\x11\xf0c\x18\xdd\x89\xed\x0bz\xf6~\xfa\x04\x9e}\xe1\xc2\xcc\x85\x95\xe3\xc2\xc58(\xde\x05,\x07s2\x9e\x9f\xb1\xe8\xbaK\x8d/\x03R\xd6kz\xa2\xc7\x0e\\\x8c\xaf\x99\x1a\x99~~\xedB<\xbe>+\xf4\x99\xd0\x96Z7*}\xb4>9\xf4\xbd\xe1~_\xd5\x05e\x82\x954In\xfd\x9d\x07\xfff\xf9\xf4_\x8e\xe5\x93\x99\xd7pl+\x0b\x93I\xb4\xa2\xd2L\xa22o\x1a\xa7m \xdf\x84f\x01\xfcq|\xc6\xae\x00\xfa\x0f\x1c\xdbG\xef\x8f\xbf\x9b\xf5{\x15I~\x1c\x9f\x8d\xd33\xc5\x89^;\x11\x93~\xbf\x16\xf5\xf8\xa2\xea\xc4\x93\xbb5\xc4j\xbfMe\xb7^\xbe\xa1T\xa6;\x11lV\xe9-c\xae\xf6U\xab\xa8\x19\xbe\xae\xdc\xed\x04\x8ckS\xde\xae\xd8[U\xc3\xb0`M\xab\xaf\xa7\x9ct\xa8\xd6\x91k\xf6~W\x1d\xca5\x17,\xd5^\xe7\xfc\xfd\xae\xd3M\x88\xb2e\x97\xbc\xad=\xc7V\xbe:\xe7,\xb1*\xd5^\xf0\xd6T\xf8\\\xf1\xf7*\x01\xfc\x88\x1cf\xae\x8fW\x8eE\x91\x0c{B\x12\xc5\x91\xf0\x18\x8b\xf8\xfd[\xb9\xe8\x10F`\xf1\x8fp\x87\xcf\xecS\xa5\xd77\xf5\xea\xdb\x9f0\x92\xde\x08\xce\xbb\xb3r\x01\xa5\x84[[\xf5\xaa]\xb3\x7f\x9d\xa0\x8e\xc7\xdd\x98$Q\xb0&\xb6\xba\xa6\xf2CX ZY\xe6\x19\xd1\xdd\xcb\xaf\x01\x93\x15\x99 a9\xab\xdd\xc3\xea\x93\xdao\\xc\x96v5\xd9\xfaA\xb2\x0394zl\xf1\xa58!?1\x86\x163_\x8a\xac8\x0b\x12\xdao\x1cY*\xab\x8a\xe55\x1e\xb27*\xf6\xbdl\x9c\xf3\xba\x9aX\x05\xa4s\xc4\xde\xc2\x98\xaf\xe5\xc9\xe4w\xf1,p)\x0e\xdb\xc1)\xa8\x89\xb4J\x7f\xbej\xa2s \xae\xb4\xd2\xee\xb9Q B\xcb\x14\xc7\x01\xf9Y\xe7\xe1\xbc\xcf'\xfa\x1a\xcb\xe6\xa4U\xa0J\x94i\xf7|\xcd\xe4\xc9>.e\xf7\x1c\x00\xe9F\x97\x18\x94e\xe6\xf9\x9ahc\xea\x93\xe0\xc5\x03\xdf\x1b\xcd\xd5'\xbc:E\xb8\xe6\xda3\xac=\x8d\x96\x9e\xdf\x94 \xc4\xb8\x81\xe5\xc7c\xc1.>}b19)\xec0\xdc\xd8[\xc6E\xd1\xbfF\x18\xa4t\x8b)\xf9=d=Fh\xedoc\x0e\xadY\x97\x84)\x89m~\x81\xe0\xd91\x8a\xe6\x94\xc5\x9du\xc9G?\xb5\xb9P\xbf\xd5sX\x1d\x8c\xb4\xb3\xe2\xe6\xff\x070\xb1?\xda\x16\xdfw\xdb\x93\x85\xe7\x870\xb9\x9e\x04\xc4b\xa1\xea\xe9:\xbe\xb4)\x06\x1f\x087\xd0\xd0\x85\xc4\x85 -N\xb0d\x08\x13;6S\x03P\xf7e#Xp\xfc[\x19\x9f\x1f\x9f\xc4\xc4\x94f[<75\xf4\x08\xc2B\x19\x1d=v \xb3\xc3q\xd4\xe9\xe8\"\xc8\x8a\x87n\x12\x1e\xe1&p\xd4p\xad\x9a\xde\xde6\xf6\xb6)\xfe\xea\xb1QF\xac\x1c\xe8\x7ff\xaba \x9c\"\x1c\xa7\xf2\n|\xb9\xd8)\\\x83Rm\xd0I\xa0\x12\xddS\xad\xb7~\xedJ\x9d4\xc2n-\x05S\xab\xc2\x85t\xcf1S\xb4\x8d?X\x184\x84\x01\xe9\x9e_\xd1\x02\xe2t\xcf\xd7,F\x1d\xe9\x9e',{\x04\xe1+l\x13\x86y\xa4{>\xe1\xc6\x94\xf4\xa0xe\x13\xd4]\xd4\x8e\xfcu\xbb\x91\xbb\x86\xc8g X\x9a\xb0{\xae\x0d\x05\x0f\x18\xec5\x9f\x14\xde\x90\xf39\x19\x8e\xdf\xfac\x17\x03M\xb2\x00\xf6bc\x15\x87\x1fL\xd0\x88\xe7\x82\xeefd\x1e\xa6\xe0\xa7 f\xaa\xa9\xa4\xfc \x9c_\xa2%\xd5A[\xe6 $!\xbd\xf9,<\xbf\xd2zGV\xaaM\x87\xba\x84\x82\xf2c\xe0\xca\xc5\xd3\x8ec\x11\xe6\xa1\xf4<~\x8d\x07L\x1f\xcf\xe6\x13\xfe\xfb.\xd9\x80\x93\"\xf3\xed\xadO~g\x88y\xc39\xfa\x87\x0c\xfd\xfb\x14\xbfC\x17\xb6L\xe3m7N>\xbe\xfa\x89\xb4X\xbf\x86\xb5\xbb1\xce\xbf:o\x85\xc9(V\xfc\x12\xf7\xfaq\xed\x86\x9d\xf2\xa8I\xc7.\x88Ma\xb9`\x9d/,\xc7\xc5t\x14\xae\x1c\xd5\xbaU\x14\xa3\xd4F4a\xed\xe6\x98\"\xfeT\x88K-\xd0O\xca\xf1\xb4\xcb_\xe6\x7f\xdd\xb8\xec\x107O\x92\xa9\xf9r\xce\x0e\xff\x92O^\xf6&\x91U\x97\xe5l\xe5\xebJ\xe5\x85\\\x991\x8a\xc5\x80\x9c\xb2-\x8f=\xd8\xddw\xecc\xd9\x86V\x1d\x1f [\xc4\xfc\x16\xa2\xdcO\xb6\x88uu\xac\x0b\x97-\xac\x8f\xa8\x0c5\xd2\x8a\xa9\xec\xca\x19\xf7\x06\x15\xb0\xca\xb5F\xe5\xd4\x83\x94\x92s\xe9\x07\xd9\x18z\x16\xf3?\x87\nL&R\x08_\x0e\xe3<\xf0\xa8\xa7\x96a*\xdfW|\x1e\x98\xb8>\x14\x12Jy\x9d\xcb\xfb\x08\xd1\xa5\xce.\x03\xca\xd6\x89L\x85\x90\x8f\xd3\x88C\x8e\x12.\xcd\xa4\xa0\xc6x\x1a\x8f\xab\xd8%\xb8\xc2\"];?Q\xf0z\xf45\xc6[\xc8\xb3\xf33&\x05KNx\x89\x8c\xcd\xe7]*s\xfe\xd4\xe6\x828\xc5\x93\xed\x18\x97\x13\x7ff\x94\x83\xe6\xc1\xe9Q\x8d-\x1b\x9e8.\x04v\xd0\xfd\n:\x10t\xbf\xc5\xff\xbf\x80\x7f\x86\xadK\x15!\xdf\n\xa6\xe8\xb8\xf41\xb3&\xb5eZ\xc1\xad\xdd\x1f8\xb6\xfcJD\xa3\xcb\x0d\xddY\xc7\xa7\xa5.%z\xa3\xce\x8d\x82\xa7i\x91\x05\x83\xf4\x93\x8e2\x81\xa4z\xea\xb9\xb9\xb4\xef\xb0\xe8\x9bzD\xab\xc0\xa9\x18\xae\x8dl\xd3\xd6\xa5S;j\\\xef\xa6a\xf3Q]\xd9\xf9\xe6\xc8\xd7\xed\x98'\x93i\xc0S\x05\x92\xf6%\xd3\xd4\x0fv\x1fJV\xf0\x95\xbe\x8f\xbb\xcc\xc0\xb9\x8b;\xc8~#\xa3E\xdd\xb4\xbc h\x9a\x92\xcc\xaa\xeaO=F\xb5L\xf6BxsQ\xaf\xbe\xf1y\x15\xb3\xca&j/\xa9\n::\xd6\xdc'\xcaO\xa4\xb7\x9b\x93\x1f\x8a\xe8\x86\x14\n\xf4YSZN\x8f\x91\xf6zV\xb4\xb0\x82\x11D\x9dN3\x07\x98\xd4\xa4p\x10O\xc8(/#\x81tov:n\xa1-\xa3\x18\x81$\xb2\xfd\x08\x01;\xa6\xacE\"\x98\xf4\xb1w\xc6(\xdf\xf6vFKb;l\xe2\n\x8dB3p4\x97\x9a\xd2\xd6\xbb1o\xf9\xa8\x8bG\x97oG\xddu\xdb\x83%\xf6&\x8d{\xf7\xae\x10\xdd\x8c\xc5\xfe\x06X\xbc9nUW\xbd\xd8vP\xa3\xcd\xd3\x88\xb7P\xbf\x02>[\x81\xd8\xf6\xebV@\"A\xf8\xf3V\x97\x83L\xe9\xa5N\x9dgp)\xdd\x1c\xa0\xda^\n \xc84<S l\xc4\xe5\xb6\xa6m\xef\x97m\xe2\x81\x8d\x9fIN\xb38Z\xdaQ\x83\xad\x0c;7\x07F\x90\xe8ma[[\xd6\x17\x01T\xb6\x8a\xb4\xe3\xaa\x86Y\xe8\xcf\xd5\xf7z~A\x02\x9c\x9e\xd8\xa0g\xbf\x06\xa6\x90\x1f\xb9MP\x85:\x9f\x00\xf10\x0f\x80\xb0\xba\x00\xe2\xd1\x9cj.\x0el\x83\xee3]\x1b\xa9\x1d\xd5\xdczk\xe9\xfa\x9d\xa4\xa9\x90\xc8\xa5\x9e\xcbV=\x00\"-u\xe2\xf4\xa6\xa2.\xe4~\x0e\xbb\xfb\xd2\xba\xc5v\xdc}\x0b\x1d\x88\xbb'5wJ3?\xf4\x82\xe0\xba\xad\xba=\xe3\xb7\xc4~\x1e\xc1\x9aJ\xc2\xe2\x0f\x83\xae=4\xddjk\x98\xdd\xca}q(\xab&\x8d\x96\xd7\xfc3\x8fRGT\x84\x95/R\xea\xf8\xab\xca2\xcb\x8f\xce\x9a\x8c\x8al\x94\xad\xf8\xc2\xe3\xe2 u6\x1a\x96\xf9\xae\xf2\x0b\xa2n\xc5\x7fD\x84?\xd8S\xb0\xf1\xb4\x06\x0f\xd3\xb85\x0e\xd2C0\xd5g\xe0\x86<\xd1\x97\xce\x9eV\xdcB\x87]\x82\x86\xed\xfc\xee\x7fX\\\xc68v\x88\x97$\xcd\xd7\xd2m\xe0\x19\xda\x83\xbd\x01\x8f=\xb7\xc3\xff\xdd-\xc7\xaa\xdb{\xc0\xff\xe5\xb1\xea\xf6x\xac\xba\xfd\x1e\xff\x97\x7f\xbf\xcf\xbf\xdf\xe7\xb1\xed\xf6\xf9\xf7\xfb\xfb\xfc_\xde\xce>og\x9f\xb7\xf3\x80\xb7\xf3\xa0\xcf\xff\xe5\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=x\xa4\x8d\x9d\xc7|j\xdb\xc0\xa2\x11\x8b*\xbeNQ\x1ep\x13\x8f\xe3#\x1e\xae\xb2J\x10\xe5J\xd1\x94\xa0\x17\xb0\x82xH\x06\xd1z`\x8b\xd9\xb5\xf71\x9eJ\x1e\x16#\x8f\x1dR!\x8fr\xa3M\x08\x9a3\xb4\xdc\xe4r|\xe6\xe2\x9c\xf3\xccPy\xa4\x9c\x8c\xf9\xe9\xc6\xf0\x142\xb3v\x80g\xb9\xeb\x14\x99\xa52\x8c\xa2\xe3Sj\xd2\xef\xf7w\xfb\xfd\xbe\xc3r\xf7\x8a;\x91\x13/\x9c\xf3K\x11R\x8e-\xbe\xf6\x02\x7f\n\x93hJ`E'c2\xab\xe4w\xd4\x04\x9e\xb0H\x9dp\x80\xb1~0B,\x8b\xe4\xd9\x01\xdb&\xb0=b\xe5\x0e<}\n\xfd\x1e\xca\x14\x7f\x84~o\xb0\x0b\x1d\x16\xffS\x97|\xcc\xb4'C\x9eSP\xcd\x9c\xbb\xe1\x8ek\xc22CT -\xa52`D\xec]\xb5\xc7\x03\x16;\xa3\x1b{W\\\x10\x8d\num\x1dnP\xcc\xf1\x18\x8e\x84\xf0\x14\xbc\xc7\x0edl]x\x08Z2\xf6:\x9d3\x07\xe3D\xdc\x87\x9eF\x8a\xb0\x8e\xa2,L\x0b\xe7\xac\x90\xcc\xbd\xd4_\x13U|\xe0\xc1\xf8\"x\xaa\x1ar\xf1\xc7\x8e\xe0\xe9\xd3\xa7#\xe8;\xdc\x9b\xb53B\xc3#zb2\x07\xd7\x90\xbdz\xac\xac\xd3\xef\xa7\x84\xdb\x948\x17 \xda\x9a6aQ\xb3n\x1b\x16\xb5\x9a6\xa2\x8eD\x97\xfa\xd0\xad\x00\xe2\x88o\xe7\x84r\x93\x1d\xea\xe6\xe1DM\x99/\xe2[\x10\xd6\x18\x97\xad \xac!\x15\x92(\xec\x84E\x0b%\xac\xf1g\x11\x07\x93dBW\xc5\x0b'\x8b(\xdeH2\xa9\xe5\x06\xf9b`\xd4z+\xf4\x96\xc4\xaaK\xec\xf9\xd9\xc3\xbf\xf0\xe7\x1b\x8d\xbd\xcd\xd0Y\x9b\x16\xfe\xf7\x05G\x1e\xf8\xe1\xe5\xdd\x8f\x9d\xb7\xfa\xc5G\x1f\x05\xd3\xbb\x1f\xfc\xef0\xf0\x99\xff\x91\xdc\xfd\xc8\xd3\xf4\xf7\x18z\x14\xa6\x93(\xf8\x12\xbb\x956MG/\x9a\xff\x82;\x96v\x95\xf8\xbf\x90/7 \xde\xfa\x17\x9c\x83\x9fz\x81?I6\x9aB\x9b\x19\xf8\xbf\x03\x16mLvZ\xc1\x1e\xc9\xfd\"&\xb3/\x0b\xf8d\xe9\x05\xc1F\xa3o3x\xd1\xea\x97\x06=}}\xb9\x19\xe2\xb7\x1a\xbeh\xf6\x8b\x8f?\xbb\xb8\xfb\xc1g\xbf\x07\xd5O\xb2\xd5\x17\x18\xf9\xea\x8eF\x1e\xda\xfb;\x8em-\xbdt\xb2\xb0\\\xe8\xd7\xd7\x96\xc62\xce\xebi\x15\x9dz\x88\x88GH\x02i\xddE\xa2/+\x1aP\xcf\x90\xe7_\x0b\xc7\xc4\x9c\xdaB2\x9b\xf7\xe1@\xd8\xd81\xcf\xa8!\x9a\xb7q}n\xe8\x8c\xc9\x99P\xd8\xc7\x95X\x1f\x10n\x9a\xd5\x9f\x03\x93\xeb\x14-\x17\x06\xb7\x00g\xecV\xdd.\xa0\x15D\xa3&\x88f%\x88\xc62D\xe3\x96\x10\x95\x04\x88\x18C\x95\xf9\x08T\xf6\x86\x832rX\xe8\xa5;\x03hB\xbc\xf8\xdf\xd0\xf3\xce\xa0\xb9\n\xfcT\x8b\x9c\x15\xcbI3\x98\xc4EFh\xf7wUc=\x10z\x8f\xeakv\xb9\x867eU\x8d\x885A\xe3\x14\xcb\xbb\xb8\x98X\x92\x89mYt\x8e\x1a\xa4is\x1d\x02\x92%\x9a\xd0\x01\xe8\x03\x01@\xd9\xd7f$\\\x8bx\x12\x9d\xdc\xceMM\x86\"\x7f\xbb\xe5\xcb\xa9\xd3\x8a\xa8x8:\xfdgkf\xc2\x9f\xb80\xc1p\xd3\x01\x0b\x8b_\xe7u\xbe`\xa1;\xfdy\x18\xc5\xe4\xc8\xc3`}\x96o\xc1\x90\x1ey\xd0\xa1e\xcb,H\xfd\xc0\x0f\xb1hY*\xcaB\x1f\xaf\xda\x0f\xc0\xcaJ\x05I\xeaO.\xaf\xe9\xfbk\xfe\xde<\x84i\xbd\xd3\xfb\xba\xbc\x9a\xb4\xb3\xdd\xc1\xa3\xddG\xfb\x0f\x06\x8f\xf6\xd0\x8e\xff\xe9\xd3\xa7u\x0d`4\xd9b\xbf\xa7\xdd\x04\x83\x9c\xbb\xb0\x80\x0eXs\x93\x85\x00\xaa\xfaX\xf0\xaa\xb8\xdc\x02\xbb\xcb\xbc\xe6\xed\xd0F\xfe`\x1fl\xfd\xf0C\xe2X.,t\xd7\xd0\xf9\x83\x0e\xec\xd7\x0c\x17y\xc0\xce-\xdb\x9e`(1\xd4*C\x07\x92q\xef,\xc7\xf0\xa70E\xad\xe1\x8aG3\xe1*\xa4\xa9+>p\x1c\x17\xb6\xd0h\xbf\xa4\xe0\xc2\xc4\x1f\xbd\xb3\xfc\xe2-v\xebY\x9f\xd2\x83S\x0f0\xd0\x00\x04\xf0\xa4\xaa\xe4\xde\x86\xc1c\x08:\x1dG^\x99B\xa3\x16\xa0\x15\xaf\x8d?FZ\xe5w\xe9\xb9q\xdc\xea\xe098\x9e\x141\x15\xf1\xf2\x9f9\x00\xad\xe8\x07\x0c\x12}\x87g\x89\x90\xc0\xc6b\xc5O\\X\xe5\xad\x8e`\xed8\x8f\x1d\xb8\xee\x06^\x92\xbe\xc4\xb6\xf1>\x83\xf7s\xef\x9e\\\xa4\xc6\xf4\x16\x0f\xdf\x8cSv%S\x84\xf5\xde\x9a\xb1\x06(\xc9\xc4,<\x9f>\x01_1\x96\x93G]>:\xe8bp\xb0\x86\x03X\xf1\xb2\x9e\x0bk\xfc\xa42\x02\xc5,\x99\xb9*X=A\x1a\x85\n\xb3\xe7H\x10\xb3[Q\xb6\xf2\x99\xa9\x92+8\x80\xf1\x19\x0c\x05\x0d\xcau\xb1\xaa\x14\xa8\xd7iK,\x82\x81\xe5\xba\x05Su+>@b\xaa\xc2\x82\xa9\x8a+LU\xa8c\xaa\xe2M\xd9\x80z\xe5|f\x87\xf6\xe0a_U3\xfb\xbchg0P\x8b\"^\xb4\xd7\x7fHIL^&\xc6\x80A\xf1\xf5\\\x1a.f\xda=?'\xc9\xabh\x9a\x05\x18G\x1e\x86\x9a\xa5\x98\x92\x99\x97\x05\xe9P\xbd\x9f\xff\xa7\xea/q\xd2\x8e\xfd.\xff\xca\x85\xa8\xf8i\xa46|L\xd5\xbe'\xd1r\x15\x85\x94\x80\xe8F\x06\x98{B\xf8.}\xe3]GYJ\x17\x8fw\xd8\xb4Y\x8a H\xa8\"_Ny\xb7_S}\x8eW\xe2\x82U@\xbcr\x0b\xc2\x03\xc7\xcb\xe1\xea\x9d*\x9aLl\xca\xf9=\xd4\xa1 \x16\xed\xf5th\xc2\x8a*\xc8\x95\xe5E;j\x91\x97\x17\xed\xabEI^\xf4@>\xda\xf0\xd5\xfe\x9e\x1e\x15'\xbf?*\xcej/\x18\xf3\x91\x91:\xc1\x9f\xd2\xde\x1c\x9b\x1dN\xe8\x88\xe3bA\xa6\x16\xd8\xa4{~\x8e\xce\xe7\xe7\xe7\xc8&\xf4\xdc\x02\x1f\x1d\x9b8\x0e?\xadX\xf5\xfcxTE\x0c\x1d\x98h[\x9e\xd4\x96\x0b)\x1fFTz;\xae\xce\xe5\x92\\\x0f\xc1\x8aI8%\xb1\xe6\xa6\x94\xe3]#3\xb0\x96\xf3c\xac\xe2he\x88?\x03\"UFwN\xd2#\xb1\x85\xcduYd\xf0dE&,!P\x14\xd74\x1c\xb3\xd0\x1fq\xdc\xa2.\xdd\x13\xc4\xb6\x8e\xa20\xf5\xfc\x90T\x1cn\xe4'buO\xa2\xab\xbaZ\x99h1\xa8\xab\xe5\xb1Z\x18\xb57\xb10\x9c\xa9\xb9\xf2\x84U~\x17\xad.\xbc\xb8\xa9\xf2\x8cU~\xe6%\x9c\xde5}\x10\xb0\x0f\xa2\x90r\xeb\x1f\xbc\xc0\x9fzi\x14?\xf3\xa6s\xd2\xf4)&t\xe8\x06\x917\xf5\xc3\xf9i\xea\xa5Y\xa2F\xb2\x97\x9f\x05z/S~\x89\xdd\x9f7\xb0\xf7\x94GZP\x04\xb1\xad%I\x12oN\x90+\xb24J\x01(6A\"P\x9d;T\xf2\xdcQ\xb6o\xf2\x94\xa4\xcf$\xf0\x92\xe4\xb5\xb7$C\xb0\x92+o>'\xf1v\xe6[\xda\xfa7.L\xe0\xc0\xd8\xcf\xc4\xc5$l\x0eO\xc6\xe6\x82\xc5\xe1c!_\xb4b|\xaa\xfe[\xcc\xed\xddv\x9c~8\x8b\x8c#\xbc\x93\x1e\xf8\xc0\xb7'\xf9\xee\xf8=\xba3t\xe2`\xf8\xb7\x99\xe7\x07d\xfa\xaf\x12\x94\x8b\xdd\xd6\xbd\xa5~\x1a\x10c\x0f\xd6\x0b\x04\"\xa4\x11\xd0a\xc1\xe1\xdb\x97\x80l\x88Oi{\xd7r\xcc\x83\xf08rKkq\x84\xae\x95_dE\xcc\xe4\x013A\x9b\x18>\xf1,\xbd\x8f\xdf\xfa\xd3t1\x04\xeb\xe1\xc3\xde\xeacM{\xacz<\xf7\xc3o\xc8,\x1d\x82\xe5ei]\xffE\xfd\x13\x7f\xbeh\xf9AJ>\xa6\x87\x81?\x0f\x87`M\xd0\xdf_\xbfDP9\xdf\xf3\xb7\xff\n\xb01&\xcb(%\x85\xc7n#NZ+\xcb\xe5\xa4v\x8a\x88\xb9\xb5B\xe5_\x92MD,\x8c\x06\xcc\x9cq\xac6\xf7\x11\x89\x1eL\x15\xb2\xa6\nA\xbes\xaa:\x0dE\xea8+\x85H\xba\xb1\x8b&sNIb\xa9\x89(m\x1bl\x8a\x8a\x90;\x15\x8f\xa5\x81\xd3\xd5\xe6Am\xd3\xa2d\xdc\xa7\xcf\xff\xd6\xdf\x91\xad\x96\xa9p\xf2\xc8\xb1\xadrGV\xb3\xf4g\xe6\xd4\xa5J\xbe\x92\x86\x14\xe06\x17o\x83\x87{\x1a\xc1J\x02\x93^\x1ely\x01\x12\xabb\x9f\xa8^\x8c\xb3\xcd0\x8ba\xf5U\xeb\xce\xc2\xabk\x8b\na\x94\\\xb3qWvmy$C\\\x1d\xa7;\xdb\x10b2\x10*\xed3\x89\x8c\x02U\xbd\x8d($\xbaas\x0e\xb6\xca\"=b\x0ey\x0f\xf7\xaa\xfew\xbd}\xa7;\x93\xfd\xe8\xdb\xb4\xd8r\x12\xaa\x01\xeb\xe7Mb\xf0\x88\xbb!>\xe2n\x86|V\x83G\x0ft\x9b\xf4\xf4zy\x11\x05m\x9an\xb2\xf34\xd8\xe1\xaa;\x98\xdby\x1a\xbc\xad\x0d\xce\xd6\x03\xb5q>\xfeG}\xa7\xfb\xf5\xf1\xf7\xe5\xb2 /S>\xe1\xa9\xe5\xd4\x1eXj\xb9G\xeaxXn\xb9=\xf55\xcf-\xa7\xbc\x9d\xe6HR~\xbf\xe6\xefU4\xbd\xe6#T=\xe4\xe6\xfc\xbd:F\x9eV\xae\x82\xed\xec\xb5\x1a\xfe\x92\xa5\x94\x1b\xe83\xcaU\xb0\xed#\x9b\xa8\x1a\xfb\xee\x94\x81E\x95\xd6\x8e\xf9\x08\xd5\xea\x87|U\xd5N\xdf\xb0\xf7j\xf5\x9f\xf0u\xc5\x0d\xf5\x12Fp\xa8\xe6\x90{ #x\xa3\xbe|\x85i\xe1\x94\x97\xefP\x1ed\x18].9\xc2\x92\xbf\x9c\xbey]~\xff\x16FpD\x8f\xf2\xa3n\x82\xaaW\x7fv]\xaeqB\x05G\xdb:_\xf8\xd3) U\x11\xfc5+M\xa3\xb7\xb1\xbf\xf4\x99\xadv\xb9\xc67\xe8\x00\xa6\xcd\xb9_\xae\xf8\x9c\x92{\xdbJp\xf4\xdb1\x99\xfbI\x1a_\xab\xcd\xfd\"\xd7\xaa\xa4\xb9|\xc1J\xa3\xd5\xb6\xa1\xc2{M\x12\xf3r\x8dg\xa6\xf8\x01\xef\xca\xf5~F\x88\xfe\x955V.\xfa\x1eF\xb0\xf53F\x0e\xffY\xca\x08\xa0\xfc\xdd\x9d\xf9\xe1\xf4h\xe1\x07\xd3\xf2\xd7\xdf\x02\x8f\xf18\xa9w\x8d\xe3G\xdf\x03\xd8\x1a\xc1\xa9\xfd\xd2\xfe\xfb\x0d7\x0f\xd33\x91\xed\xe2\xb1@\xd1\xf0K\xd9\xe4\xac^0\xe0\xda\xac\x07\xc6J7N\xd7\xd3\x16V\xd9\xf2\x1bG\xad{\xe3\xc8\xd1\x0f\x0c\x8c\x00H\xa4\xf8\xd2~\xaf\xbf\x9dE\xd7\xd5) HJ\xe0\xfd\x98\x9c\xb9t\x92\xbc=\x1e8,\xc5;\x8a\xf7\xf4\xe7Kl\xa6\x12 \xf9\x06\x86\xf0\xb2\xbcd\x1fj\xb5\x9e \xd9\xd0\xff\xc2|\x0dO\xedw\x05\"\x98\x0d\xd8 K\xa5\x9bV\"|\x96\xbb\xff\x1aF\xf0\x8c\x8e\x98o\x8b\x12\xd6v\xc5\x91]\x02b\x0dBi\x1aI+\x00h\xd5R)\n\xf3\xbb\xba\x19|\xd5\x82\xd5+5<\x12\x8b\xf4\x95\xfd\"_\xc0%\x8b\xf2\x0f#\xb8\xe2\x19\x8d\xe8;Z\xe2\xdb\xbf\xe0\x9d\xdb\x01\xc6c\xc8 \x10f\xe4\xa3\xfd\x9d\xb0\xbc\x93\xe3\x93\xb31a\xb7\xa6\xe2\xf7\x88\xe7\xa8\xc0E\x0bM\x1b\xa1hr\x08\x1f\xed\x1e&\xb6\xd0a6\x0c\x8b\x0e?}b\xd8w\xe2\xc2G\xbb\x8fyv)\x7fR\xf4K\x87\xffm\x0e\x0d\xfa\xed\xcb*_\x0bU`\xfe\xa1\xcd]\xe3R\xeb8\x91;\x93\x87\xcca\xfc\x9a'\x82#th>K}\xc2\xa21\x8a|\xdf\x11<\x05\xff\xb1\x03_\xd9)\x83R<\xf61n\x00\x19\x87\xba\x10\x96b\x05\xeb&\xf0\xe7\xd6\xdb\xe9\x9b\xd2](.|\xcaRY\x19{\xde\xc2\xda\x05\x02!j\xb0\xbc\xa3[>E\xa6\x94\x19\x04\xd8[6#\xd9\x85\x0b'\xff\xf3\x17\xf1[\x94p\xecY\xf8 ]\xbc\xf4\x0c\x0b\xd5k\xd9\xf2\x14\xff\xd2f\x8d\xfc\x19s\xdc\xbd\xd0\xe0\xb5\xa0S\xf9\x90\x08\x1f\xd2\x0b\x16bY\x8f\xa7\xc2n\xe6\xd2\xae\xb1_\x11\x80\n\xab\x8dW\xb6\xca\xa7O\xca\x8e\xe2x[\x8d$sS\x07\x8e\xbf5\xae\xb8\x1a\xee\xe2\x95}\xc1\x9c\xa0c\x1e\xc1 \xe2\x11\x0c\xba\xa5\xdc\x8fl\xf4\x94\xd9b) qe(e;\xc9\x7f%,T#\x0bDa\xc6\x9b\xb8n\xfc\xdfm<~N\xc2\xd8\xf8_a\xe0\xa1\x170\x04>\xa9\x88OJ\x84\xee(&\x95=v\xc4\x9a\xe0f\xcb\xc4\xacB\x8e\xc1\xef\xc5jElJ\xbf\x8cI\xcd>\x8c\xca\xb3*\xea=\xc3\xa5\xf5l\xfb]]\x14,\xc4P\xba\x9ddB_\x0d\x99n1\x96\xb4\x88\x0f\"\xe5(\xaeDN\x17W^+\x9d\xcfX\xaf\xe43\xd6\x93\xbc:\xdd\xca\x14\x89\x94\xd3\x01\xc9\x19\xa9\xac4\xca=\x04\x9b\xf4E)K\xc4\xffOr\xd3\x87\x98\xb4\xe8/.\x15Q`\x04_a\xc4\xa1\xbd]\x07\xff:\xc6\xff\xff\x8d\xbe\xdb\xe7\xaf\xfe\x8c\x15z\x0f\xd9_\xdf\xf1\xf4\x97[\xa1\xfd\xf0!\x02\xd5\xa3\xb3\xb7t\xe2\x82\xe5\xd2\x8f\x91\xbcL\xbb\xf5\x17\xcd|\xbc\x1f\xecEIuE\xc7\x9b\xd9\x19&B\xca0\x11R\xc6T:\xcfTh3\x84\x1dJ\\\x8bl\x17\x90o\xe6\xbfRaa\xe1%/9\xfa\xbb~r\x14\x85\x13/=]\xc5\xc4\x9b\xa2\x90#\xf8/\x17\xcd\xce]n\n\xe623_\x97\x87rt\xd1x\xc8\x95\xe4(W\xac\xcb;o\xee\xca\x99\xfd\xb9\x9d\x91\xe5Z\xf4\x18H\x19\x85\xf8k\xb1E\xd2\xf4\xb1\x03\x0b\xfb\xaf\xe34-'\xbd-HP\x8a\xd9J\x16\xdd$\x8dbB\xa95o\x85\xa4E3!mfm\x93t\x1c*\xedP\x08\x9e\x96`\xc7\xf7w5\xa0Q\x14\xb7d\x15}\xfb9=\xd3:#4^<\x80\xe7tO\x0d\xd9?\xa3j\xea]\x85\xfc^\x92\xeb\x17\xcd]\xa19\xe7\xd7h\xceY\x9b\xd3\xc1\x03\xc6\x01W(\x13\x94\xc3\xed\xf8!<\xd7\xdb\xd3\xd1\x9e\x9e#\x177\x92\xe3\xbb\xd72\xf1YBNI\x9a\x92\xb8AJ\xfb^\x17I\xb2\xd2\x92\xbf\\\x05M\xf6\x05\xdf\x97\xb3\xd7\x01\x94\xf5\xba\xaen\xa1\x0d:O\xa6\x9ao\x91\xca\xaej\xe2F\x99\xf0S\x1b\x93\x96\xfd\xc1>e\x9cN\xedb\xab\xfa\xd5\xafj\x8a}\x92\x0c\xe1\x0f\xe5\ns\x92\xbe\xb9\n\xc5\xf7\xcfI2\x89\xfdUJ\xd1\xe7/u\x15_{K\xda\xd8\xdf\xea\xea\xb0m\x90\x0c\xe1\xbb\x12\x1cQ\xc1R\x06\xa6\xbd\x85\x07l\x8d\x88/\x8e\xc1wjxL!\xa6\x8d\xc3,\x08\xce0\xfe\xcd[[p\x9d\xd6\xdfo\xf8\x9b*\xec\xbd\x8a\x11\x8f\xf2 [\\\x85b:.X\x7f9}\xf3Z\xe3@\xce\xf5EM\xfb\xae\xc4\xfap\x86-=\xe3Y\xe4\x1f\xebb7P\x81\x82sd\xc5a\xef\xebSx\xf3<\xaf\x9c\x1d\xea\x9f\xb9`\x9f\xdb\x95\x94?\x9c\xc1\xffZ6\xe6\x9e\xf3j6i\xc3\x8c\x8b\xbe\xb4\xba!\x16\x1a\x08\xf9\xcc\x8au\xa6\xe3\xd2~\x89c \x03\xc0\x91\x84\x8e\x9dN\xc3\x85\xb7\xdc`\xe9\xa8\xaaz(\xa1\x95\xa4B\x18\xbfFV<\xb4\x07\xfb\x8e\xacZp\xe1u\xa9\x1eK\xc2\xf2f\x86\xd9\xe4\xde\x15\x84\x1b\xff~\xe5\xa5\x0b\x17,\xfa\x0f\xb7S\x81\xc0\xe6J\xc3\x1c\x07\xb6z\xad4\xff\xd2\x0d\xd6\x9ec[K\x92z\xba\xd0\xbb\x1a\xe5m\xa4\xd7\x9a\x8b`\xa4\x8e\xaa\xf3\xf4\xaav\xebI\xa1\xe4\xf3\x93\xe3\x8f) \x13\x9f\xca&\x9f>\xd5\x13D!\xf8\xd4R\xd7 \xa5\x9a\xa8]o\xa5\x9eK\xec\\\xddH\xd6$L\xf9p\xa20\xb1\xa9\xc0\xaf\xec\xc7rW\xf5<\x0e\xe0Q\x9c\xa2\xf7\x91I\xdaC\xb5\x9c\xbe\x90>\xfe\x10\xac7\x16t\xa0\xd3\xf1\xaa\xbc\xa4x\xae\x86j\xb0Z\xf1\xe8\xb4wu\xb0\x0b\x94\x1cR\xd5\x91}}\xfc\xbd68\xf9\xeb\xe3\xe3\xe7C\xd8\xeaWKf^\x92~M\xae[\x9c=\xa0u\xe9\xd0\xa9\xbb\xb85$s$e\x86Fr\x99u\x8a\xde\x14o\xd1\xcd\xc2\x90C\x81e\x01\xc0\xe51J\xe3y\xbd\xa44\xa0\x17\x06{\xac\xbcz\xe1\xb9b\x1d\xd7\xd4\x9d\xa9\\\x93x\xf4\x8b)x\xfcq|\xd6\xad\xe6\xce\xd7\x84p\x9b\x93\xf4[\xe2]n\x02\xf9[\x01dK\x1f\xe3\xa5\xa8M\x8c\x11\xab\xe5\xe73\xc0q\xd5\x06\x1cQ\xf8\"&\xe4\x97\xc6d\x82P4>\xa1\xc7F\xd0\xa5\xc8\x8d\xe6\x146?\xa68\x98\xe8\xef\x19rD\xed\x0c\xab[\xd3\xe4\xca\xbd\x93\x08\x19\xa4'\xc6\xfb\xa6\xe4G\xe6\x89\n\x05]\xac\xcd\xd4\x16\xb2\xc0\xba\xe5\xb5\xc2\x83\xbc\xbaB9\xf7\x90\xb9\xfc2\x94\x02\x84\xf6\x1eug,\xa1J\xef1x\x05\xf30y\xec@\x92g.\xa7\xe7\x867\x9e\xa0\x96\x04\xe5{\xe4*2=O%\x19\x89l\x06\xd0\x87\xfb\x06\x08\xb1\x08\xef~\xc2RY\xc9\x07\x90If\xb5\xb0*\x92\x9c\xd8\xbe}\xa6\xab\xca\xed'_\xe2\xbd\xea \x1a\xb1\x1b:!oV\xcf]+b\\\xbfD\x06\xaf\xfcp\x1a]Q\x88\x16\xbf\ns\x17\x95m\x86\x83\x9aB\x9b\xb5@\x05\x80\xb1\xce+\xa0\x9d\xa8\x8f\x81v\xad1\x1b)|\x8bM\x9e\xe1\x88\xf3Di\x8d\x17 \xe6\xbc7\xb9\x94\xaa!!\xcd\xf9\xe3\xc5\x10\xb9kQ\xa3\xbd\x92\xcdS8\x97\xedn\xf4\x08\xe0\xc0\xdf\x1b-\"\xfa\xbd\x07\x8emy\xc9u8y\xb9\x91\xfd\x86\xf8\x94%GA\x1dL\xab\xef\xda\xd9}<\xba[\xbb\x8f\x9d^\xaf\xc6\x08+\xf9\x0c#\xac\xaa1\x90Y\x12.\xf73\xc4q\xf51\xa7U1\x9fV0\x94\xb6\xb2J\x95}\xbd5D\xd4F\x8c\xa1T\xd6G\x12\xba\x15S\xf9\xe7\xde=4\xa3+\x07v.\x14#\x84eCe\x11\xd9\x12\x92\x82\x97@.Ml\xa9\xe1\x18\xf44\xb0\x02\xa0!h\x17\x05e1+w\xe6\xb0\xc0\x0f\xe1\xef7\xd5\xbb_m\xca\x1b\xf3\xde\xb5\xf9\"R\xd1\xe8\x05o I\x82\xcb\x0d6\xba3\xbbb\x12\x00\xd28XF2\x188\x0e\x1d\xc0\xf8\x8c\xdf\xc5(Yf\x91l\xdf\x86:\x10}f\x8a*W\xc2\xc9\x88\x0c\x0d\xa3V[(\x95Y%\x96\x0f5\x95\x1ceF\x10\xc2\x90\xe5\xc0 \xdb\xf0\x17h]\xb0\xd5wL\xfa\xf6\xc9\x82L.\x87\xd2uB\xabM\xdb\x8aN\xecT\"\xe2}.\x9d\xd8\xfdlKD\xc3!\x14s\x1bUVg\xb3\x81\xdd\x8e\xdc\x08\xc5\x1bZ*\x15\x1d\xb6\xa20M\xf6l\xbb\x06\xdb\xd3==\x97\xb8S\xb1\xf2b2\xfbN_\xb5\xf2bl\xdc\x8e\xfa:\xe1\xd5u\xe9\x89\xe9{\xb5\xf9\x19\x7f\xaf\x0e'\xe0\xcd\xab8\xba\xc2Li%+\xe2r\x85\x85T\xe1\x857I\xa3X\xb1\x85\x9a\xb2\nA\x14\xea\x1bXW\xe3@\\7\xca\xf0mn\xc4\xe7Za\x19\x8d\x87b\x12\x9aD\xfc\xa5\xb7\x1aB\xd4]z+\xbdp?\x8b\xe2co\xb2\xa0u\xf8O}\xbdI\x94\x85):\x1e\xd3\x1f\xfa:i\x84\x04\x90\xd6\xe2?\xf5\xf5\xa20\xb8\x1e\x82&\xe7Y\xb5zn\x9c=\x04\xbf[\xe3\xd3\xf66\x8bI\xa9n\xe9E\xb5~ \x03\x86\xa0\x01\x8e\xbc\xc2C\x98V+\xf8 \xfau\xe5U\xbcn\xf9\x8df\x90q\xb4\xa2\xc7j2\x04\x8d\xf7\x1c\x1b\xd2Q\xe0%\xc9\x10f\xa6r\x8e\x93C\xd0\xac\x13\xab\xf1\xca\xff\xe8\x87C\xd0\xc0\xfe\xf9\x9bWC\xc8\xaa\xef\xd7$N\xfc(\x1c\xc2\xa4Zv~\x9e\xe05\xd6\x10\xd6e\xe4\xd4S\xc8V\xa99\xea\x89\x8e\xacQ3\xf4\x12\x7f~/\x94V\xe9y\xaa\nM\xe2\x02\xb0\x81\xb2\xf5T\x0e\x96\xa5\x13M\xaf\xa2C\xae\xb6~\x1bE\x81\x9a\x8e\x14g\xd1\x9dEY\\W\x8bR\xbd\xfb?\xdc\xef\xdc\x9f\xeb\\{gFA\xc8\xb6,\xe8@\xea\x94\x82\xbd\xff\xe1\xde}K>\x8f\xaa\x0d\x06\xdas\x0d/|i\x1df\x85\x86\x7fN\xa20e\xb9\xb9H\xfe&c7\x88\xb5=\xact\x0b\x05\xd2\xb2\xa4\xd8\x93f\xb3a\x19\xefV\x91\xdb\x99l\xe7c\xc3)\x1b\x88\x9c?]7\x8e\x85\x18\x87\x86\x93\xc4\xe9\xc4$a\xde\x1fb\xc6\x97\xe4\xfamLf\xfeGi\xce\x1c(a\x05(\xf1F@\x996\x03\x85\x0d\xa7\n\x96\x0cK\xf3\xb1U+x50Md\x98j\xa8 ;\xe8(l\x13\x05\xb6\xe5\x05(\xe97\xec \x95\xb1\xd7\x14\xe3b\x84o\xd4M\x17^z\x82\x88\x99\x08d\x17\x8e\x9c\xb05b\n0\xdbW\xa8'm\x87\xbe\x9f\xa0\x9a\x08\x89\xf1a8=a\xf8\xfc5\xb9\xa6\x1dd\xd0\x01{kB\xe7\xcf,yP\xb9C\xff\xc2\xe4\xf2\xf8\xeb\x00,\x0b\x860\xb3\xf1O\x87\x8a2\xf7Qg\x1b\xa2\xe1\x10S\x05M\x9cztYK\xe8\xe2V#g\xacy\xd4\x0c\xd5\x89V\xcc\x90\xdd\x0c\xa1hf\x87b\x08U\x83\x17\xbaV\xe8\x9a\x8b\xa4`j\x13\x8c\x8c\x81\x1d\x96+\xa3\xc6\x7f\xea\x82\xe7\xb8\xb0\xe8\xc6$ ^Bl\xaf~\x0e\xd7&,\xe34\x83\x0eVj@\xfc\n\xa4\x8b\xa3)\x11\x06;u\xf6@\xa5\xad\x81\xee[\xca\xee(\xbd\xacl\x10\xba(\xdetJa\xe0\x87\xf3w\x91\x1d\x88\x89\xdej \xf9F\x96z\x95\xf7\xb2\xf4\xfa\x0e\xc7\xbcp!Q\x04\x8c*\xfb\x96\xb3^u\xa7\x98xP3J\xf1\xa9dM\xa0\xb9x\x10D#(c\x92.\xc9:\xe2\xd1\nS\x17@\x90\xe3\x91z\xdfX\xa6\x0c\xc8O~\x91\x01\xeb\"p S\x01\x9b]q\xb1U\x10\xa6\xda\x0d\xc3|\x19\xa6\xd1\xb7~\xba\xf8Z\xac\xf6\xcb0%q\xe8\x05CX+\xc7,\xe3m\x1b\xf5&B\x87G+\\s\xd7\xc3\xbaA\xe4\xfcp=\xf3/\xf4\xe4M\x00 \x02\x00z\x92Z1\x10/\xf0\xf3\x8b\xf1j\xa1\xbd\xaf\xd31\xdb\xa1M%\xaf\x86y\x0b\xc3\xc1\xae\xd0\xa0Pl\xad (\x07\x12\xac\xaa\xdf\xad\xa2\x95)\xf3\xb5\xc0=\xdc\xbd<\x12|\x15^P\xa7p \xc9\x15~_1B\xaa\xd5\xbfi\x95T\xb2\xc2\x08\x0d\x0f?}\x82\xd8\xb6\x06{h\xcb%\xd16\xdbq5\xf3\xe4w\x1cOx8\x90(\nN\xfd_\x880>V`B\x0f\xb7z\xb3\xa9\x0c\x934\x97^yZAS\xa6o-\xf6\nH\x96\xc6\x86\xebQ\x01\xda\xd2\x98\xb9\xd1kXP/\xb4\xeb\xf8\xf4 2\xfa6\x9f/3:\xce\xff\x1c\xb1\x8cp\xa1\xa0b0\xa2g\xa7\xc6\x02\xb9\xca\xe7P\xce\xa2\xc4\x83\x0fU\x80\xd0\xa7\xc2\xcf\xb7\x84\xc1m\x90\x1cd\xd8m\x82\xe8\xa0Cv\x11\xa8P\x07\x0e\xd0\xe2<\xe8\xf0\xbeb\x92\x05zp\xa6\x8b\x98T\x00\xda\xe6\xc0\x80\xcf\x84V|'\xd0\x8a\x19\xb4tG\x8cx\xda\x03\xac\xe2\xa5\x01z\x98U\xe5\xc0*\xc8\x0c:o\xf8L\xa8\xf9w\x025?\x87\x1a\xe3&\xaa\xb6\x03\xb0)\xe0*\x86O\xd5\x16\x0c\xe7\xdag\xc4\x0fk>\xd7\xfa\x05\x1f\x15?f${\x1f^\xd7\n\xb3\xe5\x05\x89\xe57\x05Ty\x17\xa4\xfb\x87?\xf0\x91\xd1wE\xfe\xf4\x99\xcd8V\xcb\xca\x93\x87y\xd0\x81 \x9dp\x0f\xc5`\xc7\x05\x8d\xc5\n\x9dqM8\xd65\x8a\x9bR\x93CLd\x93\xe8\xa1R\x96\xd0\x89\xc6\x1f\x01d+\x8bkfOq\x0dO\xf2$<\x8f\xe1\xba\xd3q`\n\x9d\x11\xa4\xf6\x8a\x9e\xc9\xe3\xeb3\x17\xd68\x97\x95\x0b\xd7\x0e_\xbd\xea\x0808\xa6\x99C\x98\xb3,\xa5\x06rC\x87?o\"bK\x17\xdd\xc0\xe7\x9c\xbb\xab\xa1\\\xd8\x1c\xbb\xe8\xec\x920\x8d}\x92\xe8\x81!\x9e\x1c(\x17\x0c([\xf6\x12Fp\x8e\xa9\xe9m\xc7\xe9N\xa3\x90<.\x01f\xc9\x0c,%\xd8\\t:f\xe8\x88\x87B\xa9y$\xc6\x01\x98\x01$\x1e:\x89\xabb|\xe6\x91\x88\x07\x0d:lifWhZ\xbbF\x03fN.\xae\xc6\xbd3\x87\"\x9e\x98kO\xcc\xb4\x1e\xac\x06[B\x86+\xb8\x91K[\xac \x01>\x1a\x92\x91\xc9\xcfi\x11+\xba\x0eCb\xdb\xda\xe9[naG\xc2n\xdd\xce\xd8HN\xe1@\xec~\xb8\xf2\xd3\x05\\\x92\xeb\x04\xfenAG\xdcg\xd3\x176qx\x9a[\x17P\xd9d\xddX0\x84S\x17>\xb65?3J\"\xd3R\xc1\x0d\xa5\xb8\x96\xa5\xf2\x1a\xadn\x1b\xeb\x8f@\xad\x8d3\xf7\xe1\xbaw\x8f\xff\xca\x1d\x8b\xabg\xa5\xf5/\xff\x92\x07\n\xd1\x9f\xd3f9)\x97\xf2\x80\xc5\xcdEg\xc3\x18\xcd\x9b\xd3\xb1\xafZ\x80\x1b-\xb2\x89\xc6\xdc\xfa\x0e S\x1e+\xdb\x08me|=\x1a[#k\x08\xd6\xa8g\xc0`k\x88\xc5\x83j\xb8\xa7\x1b\xa3\xc6\xc0\xfa\x03\xc5\xc9\xcaE\xc0\xfd\xf1hxv\x7f\xde$\x9aK\x0d\x91qzV\xed\xb7^\xa6\x0c\xef\x06(=\x9c\xb6 (\xa3\x01-\x1en\x02\x14\x06\x0e\xdb\xea\xb2\xcd\x9c\x8e{\xe8\xe8Ma\xc5\xfe\xee\x9f\xa1\x8dD\x92]0.\xc0\x1e\xd0#Z~\xd1w\x1c \x9a\xf6\xa8\xf7i4p\xee\x1e\xa0\x05\xbe\xea\xf7\xce\xdd\xdc\x80\x0d\x9c\xba\x9bn_\xaf\x07\x18R\x12Y\xb1\xe4\xc7\xa2\x8b\x8b\x98\x95^\\h\x83~z\xd3iL\x92\x84\xd5a\xbf\xb5\xd5b\xc2{\x89\x89\xbe\xa38\xf5'\x01\xe1u\xf0\xb7\xb6Z\xe2Oy%\xfaK[%\x9b\xfa\x11\xabB\x7f\xe9\xaa\\`\xf1\x85\xb6\xc8KX\xfb\xf4\x87\xb6\xc2\xd4g\xe5S__\x1c\xf1b}\xcf\xfe\x9c\x15\xfbsmq\x10M.\x7f\xce\xa2\x94\x8f!\xffS[9\x9a^\xb3j\xd1\xb4\x12P\x05+\xb0\xa5\xd3/\xdcE\x96\xa6Q\xc8*\xe0O]\xa5\x89\x17\xae=\xb6\xb8\xec\xa7\xbe\xd2*\xf5yS\xfc\xb7\xb6\x9a\xcfgE\x7fh+D|i\xe9\x0f}\x85\x80\x97kc\xc6N\xa2`\x1eG\xd9J\xd4\xc1?t\x15\xa7^\xca\x90\x91\xfe0U\x08\xfc$\xcd+\xd1?\xb4\x15\xa7\xac\xcaT[H\xd8p\xa7D;\xdc)I=?Hx\x15\xfc\xad\xad6c\x90\x9d\xce\xb4P\x9d\xfa^\x101\x9cb?\xf5\x95\xd6\xbc\xc6Z[\xcc\xc7\xa9\x1f&\x87\x82v\xfed\x89\x85d\xa9/\xbc S^~A\xb4 \x9a\xf9$\x98\xa2\xe9`l[\xe2\x0f}\xc5\xb9\x8cf\xc5\x9f\x86\xcaYLD\xc5,\xd6\"\xd3,\x8a\xd0+\x93V\xc2\x9f\xfaJ\xf1\x92W\x89\xb5s\\\xf4\xb1x\xd1\xd7\x16\x0eX\xe1@[\xb8\xc3\nw\xb4\x85\xbb\xacpW[\xb8\xc7\n\xf7\xb4\x85\xfb\xacp_[\x88V\x1f\xb4\x98x\xda\xf5\xa0\xef9P\xd8Om\xa5b\x97-\x8c{l\xc1[\xd1\xb7\x90.\x19\xca\xd1\x1f\xba\n\x8c\xc4j \xac?\x8b1\\&-\xc7\x9f\xdaJK\xb6%\xfc\xa5v?\xf8\xe1*c8\x87\xbf\xf4U\x12^A\xbb+//\x18 //\xb4p\xbc$\xd7s\xc2P\x95\xfd\xd4U\n\xbc\x0bN!\xf0\x97\xb6\n\x99\x93\x90\xf5\xc4~j+1h\x05Zp\x05~x\xc9\x8b\xc3K]\x85\xa5\xe7\xb3\x81\xd2\x1f\xfa\n+^\xae]\xe8\xa5\x17_\xf2\xf2X\xdf\x01 3V\x81\x84\x99\xa9\x82\x9frR\"\xfe\xd0W\xe4t[\xe7w\xc8+p\xec\xc5_\xba*\xa1\xc7Ha\xe8iIa\x181\xbfaV\x87\xff\xa1\xab\xc8\x04F\xac\xc6\xc5Z]%\xb6\xbc\xfa\xe3*Z\xa5\xc5F\x12\x7f\x18*\n\xba\x17\x19i^\x94\xa5\x02\xa7\xd9O]%\xd6\x97\xb6\x93\x95\x17{l\x05\xf0\x97\xb6\x8a?I\x05]\xe5\xbf\xb5\xd5D\x15Sq4\xcf9F\xf1\x87\xae\xe2\xcfX\xe3g]Q\xcc&\x12kg\x123(\xc4Z\x08\xc4\xd9\x05\xe3\x99\xe8\x0f]\x056.\xed\x80\x12o\xc9\xfa\xa5?\xb4\x15\n\xd41#NB&\xf9r\xf2\xdf\xfaj\x81\xc0/\xf6S[i\xe9\x05\x0c\xc5X\nN]\x15L\xa3\xc4\xea\xe0Om\xa5\x95\xc7\x07\xb4\xf2\xf4\xa3I\xe3(d$\x95\xfd\xd4W\xba\xe6\x0c<\xfe\xd2V\xc9\x18\xeb\x9ddZ\xe6;\xc9\x96K/\xbe\xe6U\xf0\xb7\xbe\x1a_\x07\xfd~IY\x1c\x95\xd8\xb6R\xe6\xdb\xa2\xa9\x92\xf3\xce\xa9\x89yN\x19\xd9M\xb5$7%\x1f\xd3\\\xa4\x11\x7fh+R\xde\x82\xd5\xa2\xbf\xb4U\x16\xac\\\x9br=\xcd\x8f\xec\xd4tf\xa7>?\x0e\xe9\x0f}\x85T\xc0\x03#L\xeb\xaa0\xaa\x99jIf\x1a{\x93K^\xeeM\xb44\x9e\x11x-u\xcf\x18\x82fZ\xec\\{\xac\xe3\xb5\xa7\xedy\xedO \x13\xa7\xf0\x97\xae\xca\x15\x17r\xae\xf4R\xce\xc4\x8f\x85T\xc9~j+\x05\xfe\xea\xad\xc7\xd7A\xfc\xa1\xab8%3\xc1\xaf\xcf\xb4$\x82\x04\x81\xbf\xe2\x02$\xff\xad\xab\xc6v\x92\x9e5Yzs\xce\xdd,1\x93C\xb5J\xe0\x87\xac\x06\xfda\xaa\xe0\xc5_\xc5\xde\xd4G3f^\xb5x\xa5\xfbh\xe9%\xe2\x1cO\xb4k\xbc\x12\x10Z\x19\xa0\xb3\xf2\xd2\x94\xc4\xa1\xa8C\x7fk\xabE\xc1\xf5\x9c\x13@\xfe\xdbT-\x9f\xa9\xf8CW\x91\xce\xc9\x0bJ\xb3-\xbf\xd2~$\x88kl\"\xadi\xc4\x89L\x1a\xe9\x89\xfd\x9a\xd3\xc3\xb5v\x1d)Q\xc8\xa9\x83\xb6BNtSFuK5\x0c:\"v {\x07:\xa2:\xbbvn3\xdd7\xb9\x07\xfb\xc2\x9e\xecs\xc7\xd1\xdf\xdb\xd8\x01Yx\xe4\xd0\xfe\xe4`\x8cw\xa0\x03\xd6\xd8\x83s\x8f<\xf5\xf6\x97[\x8f\xebcYT\xdckx\xa8\xe7}5V\xb0\xf0\x8b1\xf9\x18\xd7\xda\xa2\x08[\x92\xcfQ\xe9\x03\xb7\x08\xd6\xab\xf5E/3Z\xe3\xc9\x13/\x8c\xc2\xebe\x94%O\x9fj\xb4\xb7\x81Q\xe5\xeb1s\xb9\xb5m\xe1/\xddN\x00\xd4eQ^ym\xe7\xf7\xba\x86zt\xbaX/\x9f\xb7\xa1\"\xbb\xe0\xc5\xaa\xfc\xae\xd7PQ0\xf2\xeb:F\x1e\xf2\xc08X\x91\xdf'\x9b*\xf2 ck\x11\xcf\xd8T\xd1\x0b\xaf\x870\xb5c\xd9\xf6\xef5^`\x9bA\xf9f\xd6\xa4\x82\x17\x8f\xb8\\*\xe2\x99\x14\xe6\xce.DM\xf7\x8b\xca\x15\xccVal\xe0\xc8\xf6\x1d\x0b\xdb\x12n\xdf\xf0\xa3\x05\x1d\x88\xa0\x03\xd6\x8f\x10\xcd\x8a\x94s\xac f\x05\x0b/\x01?\\S\xea\x93{\xcf@\x18\xa5\x98\xc0\x82\x8a\xdd\xfe\x94\x88\xa9vM\xe9C\xc5C\x11\x14\x13I\x8dCC\xb2W\xf1`D\x89\xf2\xa5yV\x1b\xb0B<\xb4\x0b4\xad\xacD\x17\xd0=e\xc8\xbc\xe4\xf3\xa4\xd3\xf71\x16\x99\x02\"\x0c \x8d\xef\x12\xf6.\xc9V\xab\xc0gi>$\xa8\xb9@>\xae\xc8$%S\xf0B\x06\x9d\xaeu\x9b\xebX\xf1\xe4w\xe0<\xd0\xc2\x04\x9e@\x96\x1b\x06L:\x9d\xb6\xa0\x99aj\xc9\x0c\x93\xe2r\xcc\xa2#\x1e\xd3\xb1O\xe8\xaf3\xcb\x05\xaf\x05\xe4\xe8\x02\xcddCJ\xf4T.\x8c.>c\xb2:sx\xf5\xb91\xdc\xe2\xea\xb7\"\x11\x1eb\xf9\xde\xfa\x82;qC$O7@l\xef\xcb#\xb6\xd7\x1a\xb1!\xf1\xc3y@\xe0\x84x\x93\x94s&\x9f\x87\xe5\x9f\xb3\xf0\xa6\xack\x02C\x7fWB\xbce\xd3\xc5/\x99\x19\xb7^c\xe6P\x14zK\x16)K?+\xf5\xf1\x1a\x8d\x9eM\x0f\xc3\xc1\xae\x14\n\x16\xe3\x0d\x97\xde\xe0h\x8a\xad\xdd\x8c}\xe2\x11vp\x95\xc6Z\xb5pc\x1b\xa2W\xab\xcf\x97Gv\xb1\x92\xf4s\xac\x91a\x8d\x7f\x1c\xba\x1b\xb8(\xbc\x92\xbb%\x91\xabu\xb0R\x1fD\x9bk;\x1d\x933Ge0\xe4\x05\x88\x8b\x05\xf0\x0d\xc0\x0e\xab\x94\x05I\xca\xebhJ\x1a9\x8a\xcf\x81\xa1\x89d0\xbe\xf2w%\x18\xff0\xceM\xcc\xb5\x11\xd0\xf2\xa9\xd6L\x93\xdaq`%+\xb3\xad\xd1\x08\x92:T\xbaC\x8e\x8c\xf5\xd98g\x89\xeb\xf2C\xc8\xea\xf7:\xf0 e\xdd\x85\x97H\xd1\x95\xecI+\xd2\x0f\xf5\x0cZ\x17\x19\xb4v\xac\x19|.{\x06\xff\x00\xd2\x15\x85\x1b\x1c\xd1\x1a\xe9@\x8aTW\x11\xd0jL\x0d?o\xeb\x16Q\xd1\xc4\xce`\x810\x1f\x83\x07O \xcd\x19tO\xf6\x866=tR+\xba\xf2\xe9\xd8\x93\x89j\xed\x04@\x12y\xfer\xfa\xe6u\x91?H\x9bYB~6\xdcih\xb2*\x1f~-\xb6Z\x14\xe2\x89\x99o\xcf\xba\xf3\xf2\x16\xe8B)\xda\xef\x8e2R\xe8i\x16\xad\xbb\xb4\xd2\xa4Y\x14\x13\xba\xa0T\x9b\xa9_~\x8c'C\x98\x0f<\xb2\xb7\xfa.\xe4\xab'\xe2\xf4\x96\xd6&\x87U\x17\x8eU\xb1\x14\x8f\x8f\x05\x99\\\xe6`L\\\xb8\xc8R\x88\xc9\x84\xf8k2\x85?&\xe0\xa5\xe0\x87S\xf2\x11\xfe\x98t-\x17\xce1\x99\x0bA\xe7m\x05l\xe6\xd5\xfd]\xb6`\xef1d\xa5\xe5\xc8\x9a\x97\x03\xa4\x1d\x94\x8e\xb3\x86%\x01(\xfb\xd5&\xe5\xd1R\x02\xed\xb4\xa2\x8e\xd0\x9a\xc6\xb6\xd9\x9f\x86\xadxw\xfb-Y\xb4\xb0&\x15\xcfg.\xe9\x7f=\xac\xc6\x8f\xac\xc7\x1f7\xe44Z p9\xb30\x9e\xb4\xc4\xd9Y\x9bf\x817\x1d`\xac\x84;\xe1C\x82\x1c\xd4\xf5\xdb\x01\x1a\xb7D\xbb\x0dswL \xf9\xe8M\xd2\xdf\x11\xeb\x93\xd6X?A\xacO6\xc5\xfa\xc9g`\xfd\xe4\xce\xb1^\xa0p\x86q\xed\x18\xff\xd4\xc4\xb5\xe4;%\xa0;\xa5\x15J\xd3\xda+\xdc)A\xcb\x9d\xb2\xb5\xda\x0cN\x97\x84\xcbdA=9\xfe!|\xe6M\xf3+\x0cZ\xa0\xf0l\x0c\x06,\xc6\x80\x05\xdcs\xe5\x87\x10/\xff\xd0\xd1E\xfb\x95\xec\xf7\x92:\xa5\xef[l\xd35\xf7s[\xd9\x89\x0bAu\xb7\x07\xedv;\x85\xdb4\x07\xdb\xf4\x1f\xb4\x8f+oo$\xafM\xa8\x06B\xd2\xe1\x8f\xd0Z\xe5\x891x\xf2\x02\xf8\xf4 \xfap\x1f\x0b\xf0\x07\x81!f\x00c^2\x84\xfeR\x03@\xe8\xfb^\x18\x02\x13,\xfc\xa4\xbb$I\xe2\xcd\x89\x14\xf8(I\xbd\xc9%\xbaW\xb5j|j\xc8\xff \xcaC\x9b\x11\xa5\xc8\x85\xcc\x85\x04)\xbc\xd6\xe5\x93>6=\x883\xa6\x89D\xa23\xc1\xa4V.\xb0X\xa5\x9e\xc3S.`b&dE\x8f\xbc \xf0\xc3y\x11j\x0dp\xe7xi\x14'0\xf5c2I\x83k\x91\xe4\x85n\x94(\xa6D\xe3\xe2\x1a\xd2\x05\x81\x1fWq\xb4\xda\xa6D'\xf9\x11V\xde\xe4\xd2\x9b\x93.\xbcO\x08\xfc\x987\xd8E\x865\xff\xd3v~\xa4\xfbl\xe2\x05\x01mb\xd9\x85\x13\xe2Ma\x19\xc5\x84r\xae\x8b4]\x0d\xef\xdf\x9f]t\x97\xe4~\x96\x90m\xfcz\xbb\xe8\xc7\xb8I$<\xc48\xd0\xe3\xe8\x0c\x0e\xd0\xd93\xf7W\x15\xef\x18\x91x\xb7 \x85\xacS\"\x9a~\x82\x86\x97\x94\xf1N &?g~\x8cZEY\x9eb|\xb7\x9f&\\\xd4\xf2\x13\xf8\x91vD\xe9(\x0c\xbf\\\x1f\xb9\xbf\xae\xe8\x88Nn\x08\xa9]\xc2\x91&Op\x90\xaf\xe6\xbb\x17~8\xb5\x19\x19\xda\xeak\xc0\x9b\x8b]~r\"F\xaa~\xd7\xabF\x981`\xfc\xba6\xa4\xa3\xe9@v!3a\xbd\xb8k1_\xe1\xf0\xb6\xe7\xb6\xe7p\xe2p\xd0\xee\xa8(\x1d\xa9K\xfay\xdbS\x95\xbeM\x05[\xcf\xd7\xa9\xba(\xaa\x17\x93\x1eb\xd7\xb6\x96\xf2%W>\x8b\x92\x9b{\xef\xe9\xe13\xf1\x12\x92;e\x0fk\xaa\xf0\x9b\xf7\xba*\x85\xbb\xb8\xbe\x16\x14\xd06\xa5 `\x0d S\x84\xe6f\x0c\x9e\xb7\xac\x19\xce.\x99[\xd1\xbas\x8b\xb6I\x97\xacI|m_7x@\x97=\xdeS\xb9\x89\xbaD\x0bk5Bc\xa3\xa8\xb0.9r\x86\xcc\x913\xe4\x8e\x9c\x93\xa6\xdb\x95\x8d\x1c;\xd5\xe7\xa6\xd1\x0f|+n\x953\x82\xce\xc1\x17)O[9\x98\xc7\x8a\x83y\x1b%\xc2c\xd8\xb2}LhPv\xec\xae\xfd\x12\x8a\xbb\x10\x9fyuK\x0b\xd97\x83f\x03gs\xdd\x98Zr\xbd\x18Z\xa8\xad\xb39*\xaf1\xf1\xc5\xb5\x9d\x8d\xfbg\xad&\x02mt;&\x8c\x16\xe1\xa5\x1b\xbf\xaf\xf6\x7f\xd3\x8a\xcc\xcd\xeb\xbd^\xc5=\x8b\xf1|R\xf5\x85p\x00\xdc.\n9?I\xbd~B\xe6\xc7\x1fW\x85k\xba\x05-\xa3\x13\xf1\x9e\xa4\xfc7\x9c\xd3\x14I\xa1\x18\x95\x18[\xff\xf2/R*B\x0b7p\x835\x19\x91\x07\xc8^W\xe1\xc8\"q\xd1\x81\x8b\x11T2W\x1a\x80\xbb4\xc7\x14\x93\x12\xcb\xe1\\rjW\\i1\xb7\xe8*\xe4\xc5\xda\xcc\xb5\xfa\xebJ\\\x82\xfa\xa8O2\x00\x9e{\xa9\x94\xb1g\xea\xa5\xc4\x90\xb4\xa7\xf2%[\xdb\xe2\xdb\x98\xcc\xc9\xc7\x95\xc6\xeb\xd9\x84F\xed\xe0y^\x8f\xac\xfaT\xd1\xe2\xc4n8\xaa\x19\xd2\xd6\x1d\xc3\x8d\xc7\x9e\x98\xbd\x17\"gS{\x86\xd6\x1f\xc5\xac\x0e\xae@]\x05\x0e\xe6\x16#\xaa\x1bP[\x1a\xd3\x14\x89\xae\xfc\x17\xffH\x8a\x88 #v\xc5&g/\x08\x14I\x05F\x94\x95\x0e\xba\xf2\x8b\xc0\x055\xe8\xe7\xad\xccb\xebb\x01\xe5W\xfaw\xd4\xbe\xd5\xdf\xeb\xeewy0\x84[\xb5\xb6.\xc2\xec\xef=tLa\xc5\xfdV\xf6\xcf>\x7fu\xf8\xfa{C\xbc\x87$\xf5R\x7f\xd2\xae\xee\xaa\x08\xb4\xde\xa26\x8f\xf2\xba\xc1\x07\x0b?\x98\x1em\xfa\xd5\x9c\xa4\xcf\x199\xa0;P\xf9\xe6\xfc\xd5\xf1\xc9W\xc7\xcf\xcd\x9f\xbe\x0c\xfd\xd4\xf7\x82\xd3\x14S=l\xf4\xe9\x914\xdcM>\x8dI\x88\xfe\xbd\xe2\x8b7\xaf\x8f\x8e\x8d \xe4[\xe8[?\x08^\xb1p\xaa-@\x92\x7f\xf6\xdc\x9f\xde\xe2+\xda\xd9 \xbb)\xd4\x80\xd4\x84G\x8b(\xa3\xe0\xe0m\xbc_MK\x10m;I\xf5\xbb6\xe3}\xeeOo\xf3\x19v\x17.[\xc3\xe7\xfd\xeb\xd3\xc3\x17\xc7\xe7\xb7\\\x13\xdd\xd7\x1b\x03Y\xd7\xc8\x06S\xcf\xb0\xaa\x94\xcf\xc1z\xf3\xe1\xf8\xe4\xe4\xe5\xf3\xe3\xf3g\x87\xa7\xc7\x1a\xe6\xa7\xda\xce\xc4Htp#\xc6\xfe\x9aLq7\xbd\x88\xa3e\xcd\x8el\xd3\xd7\xcc\xd8\xd7\xd4OV\x81\x87I\xceZ\xb2\xe4\x80\x84W\xfa\x0eT\xbd\xaex\x0c\xd7F\x82\xa6\xb6\xee\x8d\xb2\x9c\x9a\xd8\x9e\xf2\x93\xdf{\x84\xec\x9e;,\x85\x86\x0b;\x1d\x87k\xb4\xc7\xe1\xd9Fw\\\x1aR\xdaz\xdci\xb7\xf25f\x1b\xfc\xfb\x8d\xab+\xd3\x060\x85\x9a\xa1\xddzT\x86\x01}\xc6X*g\xc7\x06\xc3Q\xbe\xc5\x00G\xea\xbb\x11L\xed\xca[ly\xa8\xad\xbd\x11BJ\xa7\xf1\x06\xc3^Il\xaa\x00a\xfenS\xf8\xe5\xccC\xeb\x01l\xb5\xaf\n\xed\xf6\x10\x94\xf7\x91\x1f6\xb7*\x1e\xc1\xe85\x1b\xf5\x8b\x07\xc7\xa3\xda\x02\x86\xadm\x01A\xe8\xbd(\xbb\x88W\x9d\xed\xba\xa5Odo\xf9.\xfc \xadhy6\x9b\xef\xa3\x0c<\xbc\x10I\xc9r\x95\xfa\xe1\x1c\xd2\x88gi\x07\x0fb\x92\x90xM\xa6\x88)t\xa4.\xfc\xf8\xc7\xe4G\x17\xd2\x85\x97\xf2\x03;\xfc\xe1O)\\\x10\x88B\xbc\xa9\xb1\xf8\x8aZpI\xae\xbb\xf0\x9c5\xe5cn:/,,\xa6E\x8b\xf8\x86x\xd3\xc7\xb4\xce\x95\x1f\x04\x90\xa4\xf4\xff\x17\x04\xbc\xc9\x84$,94o\\\xb6\x17\xff\x93>t\xbe\xe9\x11z/\x04\x9a!\xee\xb5\xeeA\xf5\xd7&\xab\x03\x12\xcf=\xa9.4\x1c\xc0d\x1c\x9eqE}\xfbq@!^F\xb6\xee8D\xbd\x87\xe7\x82\xd5z}\xe9RR\xc8^GY,\x19\x0b\xe3\x0dY\xba\xf0B\x88\xc2 \xe9\xc2\xbb\x85\x9fP\xc8\xcf\x02\x7f\x92\xc2\xd2\xbb\xa6k3\xcd\x08m\xc9c\x87Z\xd7ba\x99\xd7\x91?\xb5Q\x8f\x8ct\x0bo\xad\xe3\x86\x80\x93\xf2S\x7f\x01,?\xbc\x13}\x1ch\xf5in\xd6\\\xe3\x86Q\x99Mh\x9a\x97\xa5\xd1\x85\x1fN\xcb&\xf7\x1b\xdcA\xeb\xd3\xfd\x80d$\x98\xa8\x88E(b%cbF\xacs\xcd'\xf7\xeeQd*\xb3p,tm \x8f0?\xc3\xcc\x9b\x10\x13BEk\x12\xc7\xfe\x94\xa3\xd4,\x8e\x96\x1c\xa9\xe8\xd7\x90\xac\xc8\xc4\x9f\xf9\x13\xb40\xef\xc2q\x98d\x0c\xc3RVkI\xd2E4\x85\x10\x93\xd1N#\xbc\x01\xa6-\x06\xde\x8a\x85\xf2\xc4\x91\xf0jhjH\x1c\x97\xdd\\\x94\xb7\x82\x08\xbb\xfb\xe9\x93\x96a\xbc\xcd\xcc\xbe\xc8V!\xedn\xe3\x90q3\xa7\xf00\x11\xa5\xc8`\x1cZ%\x0d\x7f\xaaL7K(\xd9/&\xc8\x160\x8a\x8bAQ2\xceg\x02/\x19\xe9v\xe1\xa7,I\xf9\xb71\x99g\x81\x17\x17\xb6\xf4.=w\x08\xda\x86n\xde\xff\xc6\xbd\xe9 \xea:\xcf\xd7T\xa8\xe1\x8c;\xde\xc7\xfb\xa4\xf3\xf3\x98\x0e\xf60K\xa3g~8}\xeb\xf9\xb1&\x863\xc8\xac\x83G\x8f\x96P\xddf\x19\xcb\x14\xdee\xdc?.)\xff\xedh\xa3\xd0\x8b\x07\xd7Xm\x8c\x19Vxx\x8d\xd5x*\xad\xb9ch8\xf6Z\x98\x8e\xadp\xda\x95\xfe\x9a/\x02\x03{\xc5\x12\x01\xcd\xaa_;0\x1b{gt\xd2\x93\x86\x96jbQ\xcb\x0f\x9d\xd3BG\x00\x9bF\nu\x86\xd3h\xbd\x82\x01\xc4W\xe8\xe6\xd6g\xa4\xa2+(y\xbb\x13\x0c-\xf5\x9b\x16E~\xd6<\xa4w2\xf6Zr\x8f\x80\xfb\x1b\x03\x9b\x9b\x99\x80k\x95\x00\xf2\xd7\xea\x0e|\x1f\xe6V\x04\x94D\xc3*\n\xfc\xc95\xfc1A\x94\xbe$\xf8\xf3jAB\xb6\x03\xe7\x14\xbd\x8b\xadI?Ab|\xcdV\xbff8\x07\x10\x8f=\xc6\x13\xd0\x1f\x14\x19`\xa8\x1b!\x8b*\xcc\xea\xae\xf3\xba\xed\xa0\xcfCT\xf3\xaf'\xcd\xf0d\x11\xadY*\x16\x8f\xf6\xe3\xe6\x1f\xd7~[\xc3+T\x8f\xf8V\x84~a<\xef\xcbbIds\x8b\xb2\x9a\xfc\x01\x9a\xf7\xc4\x05kI\xe29\x11\x89\x97^G\xcf\xb3U@\x0fd\xf25\xb9Nlg\x08G^H\x8f]\xac\x06a\x14n\xb3f\x12$\xe0\xc4\x01\x8d\xc8\xc2r\xa7\x95.\xf5\x90\xe1k\xec\xeb]\xcc-ZXo\xe9U\xc4\xe9w\xc2\x8e{\xca\xe9'\xde\x92P\x14\x1c\xe2\xd1\xdb\xead}LA\xb4\xc2\xa8\xb3\xf4L`Vr\xa2\xea\xc4\xcb\x12nNv\x15\xa9j[\xdb\xa1G\x9c\"L\xdb\x8e\xe088\xdfMw@i\x9c\xf4p\\\xd0\xb7\x97\xe4:\x11,0gL\x0d.\xaa\xc2\x86\xb0\x15ZL\x9bL\x11e\xf6\xd2x\xee\xa1OI\xd7[\xad\x82k\xccE\xe2\xe6\xde \x89\xc1\xd1\x91>(\xd4\x1a\xbe2\xdf\x8f\n\x9b\xb8\xc2\x11%n\xae\\\x18{\x84\xe6\xd3\x1bC\x1ek\xe2G\x83t\xebf\xfbl \xf0\x87>\xd9I\xbb\xfd\xb8\xfel\xc0\x1b\x01n\x04\xea-\x87z\xdd(*\x10f=\xa7\xbb%\x16`WzR[\xd1\xe77\x06\xfd5A#h@X\xb4\x9e\x9f\xfb ~\x84F~\x9a$\xeb\xa0'\xa9U\xa4]6\x0f\xb0\xa4\xaa\xbf\xf5\x18\xf5\x06/\xad\xc6xn\x1c#\x8fY\xce/\x90Z+\xb7p|L\x1f\x1fwI\xf8sF2r\"5\xc51lc\xe95\x9fpK8 c\x9c-\x15`\xb7\x87\xd5\x859\xd90HV\xa2\xf6\x85|\xab.\xf3\xf6p\xae!m\x05d\xeb\xc8%Q\xaeT\xe3\x1a{P(\xd0\xa4*,\x88|p\x94\xf9o\xecY<%/\xc2T\xdb\xaekP\xf5Cg\x04\x83\xa6\xf6A\xd1Y6\x8b\x05\xc0%\"2\x0e\xa1\x03\xfd\x16|*&\x84\x181\xca\xe4\xdf6\x10\xc2\x0d\xa2\xaf\xc8\xb3\xb7\xe2\xda\xedj\x96c\x91\xd07&3\x0cj\xe6\x96\xf6\x850R\x0f\x0b\x93\xf9T\xe4\x172ODh\xef\xf0\x13\x85U\x80\x03\xedk\xdbiT\xe8E\xb6\x865\xf3\xd0\xb0\xaelO\x86\xcc\xf4\x1f5]\x0caI%_\x8e\xfe\xb9\xbf:\xe5]h\xd7\x16=\\\xe4\xeb)*\x050~\x9fR\xc1\xc4\x97.\xee,G\x81\x88\xa7\xdf\xad\x0d\x12o\x8c\xca\xf2\x92\xb5KH\xae\xe0\xc2\x95_\x96\x82\x88`\x8ef\xb9P\x87\xe2<\xd5\xa0'\x12\xdf\xdb+\xd9\x02\x9c8\x8e\x0b+\x9b\xb80\x17?R\xf1c\x89'\xacz-\x82\xbe\x08\xdd\xa9rS\xa2V\xb3\x1d\xd4U\xc8\x83c\x17\xed.XR\nx\xbb\xdb\xedR\x86\xb9\xaa\xdab\xcb\xe3/W\xcc\x1c\x05<\xf8\x915\xf0#\xe7$\x91\x99N\x1cy\xfe\xd3E\xa64'\x13\x8fJ\xb4\xfc\x83A\x14\x92\xffJ\xcb~ \xca\xad\x8d`p5\x80e\xd1\n5\xa9\xd3Y\x80BM\xc1\x0c#\x12j\nD\x04BM\x91p\xd8\xd3\x14\x89(\x83\xba\"\x1eWPS\x84\x91\x04u\xefE\xc8@\x8d\xd62\x8fa\xa6\xf9N\x0er\xa5\xf9\x94\x85\x052N\xcc\xf0\x15\x8f\xc8a*a\xc1\x174\xa5\xdcU\\7\x05\xe6N\xab\x98\xc3jy\xbe\xb0j:\x19\xbb\x10\x96L'C9\x9f\xeag\x10\x0e\xee>\xc9n\x00\x8a[\x13\x17\xac\xf3s\x92\xbc\x8a\xa6Y@,WA?4\xaa\x1f\xca\xd2\xcc\x0d\x1eI\xfc\xf0\xa9\xa3\x1e|\x8aUt\xce\x85\x98dh`\xef\xdeE\xab\x0b/\x1eB$\xfa\xa9\xd42Y\xad\xde(\x84\xd2\xcd\x89\xfc\x8e\x86*\xda\x94\x90\xfa\xa8\xf9\x89\xbb\x05\x14\xe0\x00b\xd0\x8dMX\xd9V\x1c\xb6\xe0\x1f\xbe(\xd5\x03be\x87v\x7f\xf7\xa1\x9a\x03\xd4\x17E{=]^QVT\xc9\x1c\x9a\xe5E\x95l\xa4^^\xb4\xaf\x16%\xdcfU=\xa8&\xcc\x0fWy;\xa3+\x82-\xed\xef1\x9e\x88\xae\xdb\xae\xa3\xb6\x1a\xf0\xf3l\xdf\xd1\xa5*]\x19\xcfg\xd4'\xa6\xe5uN\xeb\xd7\xd9D\xcdoJ\xd0^\xd4r\x07\xd2\xb9a\xba\xff\xb2{.\xf8\x02\xd7\x1d.\xe9\xea\x9c\x7fho\x88\xb8=\x172\xf5\x03\x9br\x9f\xc8v\x9d\x9f#\x13\xd6s!.*\x11\xc7a^E\xb9 \x1d\xea\\B\xc5\xa5|7\n\xdf\xc7\xc1\xd1\xc2\x0b\xe7\xa4\x95+V!\xe6\xa5^<'i\x9dCN\xd4MH\xca\xc4\x00\xb3\x80\x97\xc5\x81JE\xc5\xa3\xf1\x8b\xbeq!\xea\x06\x917=]\x91I\xab\x01GL\x0e\xebR\xa6\xf7\x10\xeb\nA\xeb}\x1c\xa0\x87\xb9\xae\xc64\xba\ni7j\xba\xf3|\x0c\x08\xb7S\xcc\x8e\xd0j\x18z\xb8\xa1\xe7\x9ax\xb3\x88\x89\xc1.\xa6\x98\xb2Mp\xc0\x14\xae\xd87\x99\xd2Y\xe0\xcdrw\x15\x935 \x85t`\x1b\x06.f\xf6>\x0eZ\x0d\\\xea;b\x82W7\x8b\x83\x0d:\xc4\xb1z\xf1\xa4~\xff\x88G\xc0\x89\xa2u\xd0]yqB\xd8\xd7\x8e)\x834\x19[Y\x1cPq\xdb_z1\n\x91\xd6Y\x1ew\xd2\xac\x9c\xa5\\\xd8\x95\x1fN\xa3\xabn\x10\xf1k~\xdcW\x93\x08#\x1f\xdc\xbfoA\xa7Rc\x11%\xa9\xe6\xf5\xcaK\x17\xe6\xeeXmJ\x98\xf8w\x0b?I\xa3\xf8\xba\xfa\x06/v\x98\xcc^-\x93un\\\xac\xb4,\x97\xc5\x1c<\xa0\x83e@KH\xec{\x81\xffK\x0e8]\x86\xde\x9b*\x1am\xb4>b\xd3\xccIz\x14\x853\x7f\x9e\xd8\x0eE\x8c\x84\xa2\xf4\xd8\xa0p\xc1I\x11I\xc7\xc4n\x86r\x899\xef^\xe7\x12Pj\x88v\xc5]\xb2\xf0B\xa7\x0d\xa5\x81<\xb5 \x99\xbe\x0c\xa7\xe4\xe3\xd0\x90\xc2\x1e8\x03$\xe1\xae1\xcb\xb1\x89FE\xe1\x0b?HI\xfc\xc5H+\x03\x7f\xe0]GYZ\xa6k\xacc\x9d\xfd [t\xae<\xd1\x0f\x02\xc9q\x8a\xb4\x90\xa1F\x14'\x14\xd8\xa6\xf8\x92\n@\xab\xfap\xdag\xe9\xa5\xd6\xf9\x88b\xae'\x9dbL;B\xdfF\xa5\xb7\xe3\xea\xa8\xf1\xbe\xcd2\x1a\x98kl\xc29g\xd5\xbc\"L\xd9\xd4\x8cYf\xa0\xb5\xc6\x992\x88T^\x10\xf4\xf3D\x9du\x8b \xd6a\\\xcau\x86f\xa5*\x11Z\xc5\xea\x8e7\x7f\xc4.q\x9a\x08\x02\xde\xa8\xd1\x1d\x1cr\xa2P\xb7\xe9\x0b\x15\xb0\x86\xe0\x9bU\x981k\x7fc\x1a\x03Hg0v1F\xc7`|e\x0bl\x10OkZ\x03z\x9ch(j\xbc\xb7o\x81D\xe2\x06\xec\x8ep\xe86g\x02\xe7\xd7\xa53\x816\x94\xf3\x1c\xe9\xb8\xd0\xf8vK\x10=C>\xe4\xf6@`Z\xce;\x9dy\xc3\x1eb\x80\xd1z\x07\xca\x0f\xbb\xfb.\x11\x13s\xe5\xb8h\x18!n\xae\x89\xf7!\xb6\xf5\xcc\x98pU<\x11\xab\xf8\x8d!i\x9fx\xd0\xc9\x8f\xae\x93\x1f\xce\xb9\x95b\x97\xffIwHVK\x1e\xbc\x9a\x9bqk\xe6\xf9\x01\x99\x1a\xda\xc4\xf3\xde\xebN\xa2\x00\x15\xf3V\x8c\xd9=!S\xdf\xff\xff<\xcf\xab\xb3\xac\x0b\xd0\x11\x80\xe1\xa7y\x9c+\x83\x0f\xa2x\x16\xb5\xf72<`\\=I\x9bb\x17f\xfa\x15TIW\xd3-+}\xa6\xccFh\"\x8eO\x9e\x9aYh\xadE:?\xdd\xfeP\x1f\xdc/5\xb6\x87\xe2\xe1\x1b'\xa50\xad'v.\xe7\xcek\xac\xa4(\x03\xb6j\x98\x03\xcb]\xd94\x054\x07e.S<\x9f\xdd6\xff\xb0\xf6\xb3E\xba\x0c^Dq\xfeQ\xd5uK<7.\x18\x87\x88\xf9\x95\xf2(f\\`\xf4\xf0\n\x86\xa2\xad\xf9;\xd6g\xd3\xdc\xfci1\xbe\xfa\xe9L\xfd\xc4\xbb\x08\xc8t\x08Y}\xc5(d<\xeb\x90\x116I\xd0\xad\xff\x8e\xaf~PO\xb0\xeb\x808uLL63{[\x08b+\xc9\xb0\xcdH\xc2\xd2\xac\xd6\x01RF\x10\xd1\xf4v\x16\x07\xdb\xfcS\xe3\x87)\xaa\x8dY\x9a\xad\x1az\xaa\x01({c\xfeFl\xa5\x02\x94Y\x1c\x98\xab\xb7Z\\\x9e#\xd1pi\xea4\xef7\xffV@\xe4\x19\xbek\xe1\x13\xf8\x93\xcbaem\xf5\x03u\xc1:\xfe\xb8\n\xa2\x984\x05;3\xa2\xc4\xd4_\xb7F\x88\x14\xb5\xd4\xfa\xcd_\xb7\xf17\xe9\xe3*\xf6V+\xf2\x85;a\x13\xd9\xbem_\x91 b\xe6\x8d\xb6\x9c\xd7\x0efA\xfc\xf9\"\x1d\x82\xb5\xd3\xab\xc1\x86+\x7f\x9a.\x9a*%\xf1d\x0831\x90\x1a6#\xa0\xfd\x9d^y\xf39\x89\xe1\xfdK\xc3\xack q\x89\x80'\xac)\xcb\xa9\xfb\x04\x13v\xb7]\x96\xd2^\x11\x8bS\xb7YN\xb3\x8b\xa5\x9f\x0eaaZ\xc1Uw\xe9\xad\xda3\x0b\x92\x04\x9et'A\x14\x8a\x898\xf4\xd3\xfa\xe3\x87q\x06f\x9an\x92\x7f\x1d\x1d\xa5W8\xf73\xc7\x95\x9a\xbe\x91\xa8R\xceCK\xdb_\xbe\xacb\x90Qojd\x18\x94\x02\x80`J~\xccxy\x7f\x15\xce\x1f_x \xd9\xdfu\xfd\x0f\xcf\xde\x9c\\\xf5\xbe\xfej\x1e\x1d\x1e\x1e\x1e\xbe>}\xbf8~??<<|\xb6K\xff&G\x87\xaf\xe8\xbf\xaf\x1e\x04\xfb\x7f\xa5?\xbe\x7f\xf1\xec\xd5\x87\xe3\xf7\xb4\xc2\xfb\xd9\xd5\xad\xfe\xeb\x05\xbf<\xbb\x1f\xf6\x9e\xcd\x16\x1f\x9f\xad~\xba>\xea}\xdc\xbd\x7f\xff\xfe\xfd\xce\xcf\xeb\xdd\xa3\xbf\xac\xfa\xcf{\x8f:\x9dY\xbast\xff\x97\xbd\xfb_\xf7\xf7\xef\xbf\xdfy\xf0\xe8\xfd\xec\xea\xf9l\xef\xe1\xfd\x9f\x1f<\xea\xbc\x8f\x07\xcf\x07'G\x97\x8f\xe8x\xfe\xfc\xdd\xc9\xe9\xbb\xe0\xd5\xe1\xf1\xf1\xe1U\xf8\xe8\xfe\xfd_v\x0e\xe7\xeb\xdd\xfb\xeb\xef_>\xbf\xaf>\xef_\x91\x9f\xfc\xfe\xe5\xe1\xe1\xe1\xf3\x87\xa7\xefO\x9e}\xf8\xf3\xfcY\xf0\xb7W/\x0e\xa3\xbf^=?|w\xf2\xf1\xe2\xbbg\x0ff\x9d\xf5\xdb\xaf\xc3\xe0\xbb\xc3\xbf\x85\xfb\x97\x83\xc9l\xe7\xf0\xd1/\xf7\xdf\xce\xde\x1c=|\xf9\xf2\xfb\xd0\xdf{\xb1\\\x1e>{\xf5\xf0\xc5\xab\xc5\xd5\xbb\xfe\x83\xc9\xa3E\xb8\xf0\xff\xf6M\xff\xe8j}\xfcM?]\xbe}\xde\xfb\xf9\xf4\xeb\x9f\xf7\xe7\xdei\xfa\xed\xfd\xcbW\xdfy\xe1\x87\xe5\xe1\x87\x93\xe7\xef\x83?\xf7\xdf\xac\xb3\xec\xdd\xcb\xd7\xd1\xfe\xe5\xa3\xde\xe9\xc7\xd9\xc3\x9f\x937\xe9\x8b\xfd\xf9\xeel\xd6\x8f\x92\xb7;o\xc2W\x93\x0f\x0f\xa6\xbb\xab_\xa6/\xdf\xa7Y?:\xdc\xfd\xd0{\xfe\xb7\xe8\xeb\xe5\xc7ep\xfc\xfd:}\xfe\xfe\xa7\x9fNw\xd2\xe5\xd7\xcb\x9f\x9fuV\xdf_?\\=\xef\x7fx;{\xf0\xd3\xdb\xe3\xde\xcb\xdd\xde\x9f\xff<\xf1\x9e]\x85\x19\xd9\x9f}\xf5\xcb\xfc\xfat/\xfd\xee\xe5\xfbG\xfbo?<\x88/\x9f\x7f\xfb\xe7\xd7\xdf|\xe8=\xffz\xf7\xc5e\xf4\xf5\xf2\xc5\xea\xf5^\xf4>\\\xfb\x0f\xbf\x8e\xc8\xe1\xe0\xfe_\xbeK\x96\xdf\xfd5\x8b.?\xf6\x12\xff\xa4\xff\xd5\xc3\xf4\x9b\xcb\xd7\xfb\xe4\xd9\xa3\xe4\x9b\xab\xbf\xac\xee__/'\xd7\xde\xdb\xfb\xef\xe2\xb7\x9d\x93\xb7\xcb\x8bW\xaf\xfc\x8f\x93\xbf|\x98\xbf;\xe9{\xef\xff\xf6h'\xfa\xea\xbbd\xfe\xdd_\x0f\xbd\xaf\xf6\x8f\xaf\xe8\xb2\x1c\x9e\xbe\xff\xf0\xe6\xe4\xeb\xbd\xa3\xef_\xbe\x1c}F\xd0\x19\xd2\xbd\xb8N\xc97Lj\xae\xd3.\n\xad\xe2\xc4N5\xf2\x18\xaai\xc6=\x8d\x84\xc34-\xaa\xe9\x1c'\x16;\xf0\xcf`\x87\xd0\x81\xd8\x81\xfb\xb0\x0b\xdb\xd2]\xe9\x8d\x0b\xa4\x9bF\xcf\xaeS\x82\xa6a\xf5\xd7f\xb9\xe9 \xb3\x10\xc4Q2\xcb\x17:*\xe6\xfc:\xee\xf3\\\x14!\xb9\x82\xa8\x92\xe4\xa7\xc6N\x03\xc7I\xa0C+\xb1q*f\xc3x{\xe6BF\xe99%\x06=\x97\x05q\x86\xa7\xd0\xc3\x0b\xe2m\xd8\x85!\xad\x120\xfb\xc5\x00\x9e\xc0\x8c\xfe\xd3\x19\xc1\xae\x83\x90\xf5\xc7iw\xb2\xf0\xe2\xa3hJ\x0eS;p\xce\xe0\xc9\x13\xe8?\x84O\x95\"\xe8@\x9f\x17\x0f\xf4\xc5\x03V\xbc\xaf/\xddq($\xc6I\xa7\x83\xe6\xfa\xf0\xf4)\xf4\xf7\xe1\x1e\x0c\xf6\xf6\xd4\xf7\x0f+\xaf\x07{{pO\x0d-5@)\x9bI\xcf\xe6\xc9\x18\x06K\xe7\xf2\xf4)\xecV;Q\x18\xb3~\xab^\xfa\xbdZ\x90\xed\x9a!\xf6\xf4)\x0cZ\x03\xc0\xd1\xa2\xb4WF\xe0Y\x1c-o\x87\xc2B\x97\xc5\x8d\x12\xe0\x8f\xb0\xc3\xc2=\x8e9>\xf782\xc36\xf8,\xc7\x83G\xff\xe9\x8c\xa0\xbf\xbf\xf3p\xc7\x81\x88\xb1\xe13\x8a\xe0\x99\x8b\xd1n\xb1\x04\x9e\x82\x07\x07\xe0\xc1\xb0x\xa7\xb2\xc0\x0c\xd2>\x1c0@\xa7c\xda\x0d\xdd?\xbc\xd1x\x8c\xc0\x19\x9c\xd1\xcd;&\x0c\xae\xf7`\x7f\x87\xbe\xb0F#\xcbq`\xc8\xb1\xc2\xcf\xd7\xcbf\xed\x0cp\x1d\x1e:\xd016\xdc\xef\x89\x96)b\xe4-\xf3\xae\x06RW\x15\xee=\xbf\x93\xfe)\xf2C\xdb\x92\xec\xb4$E\x91d\xc5\xc9 \xea\xf3\x7f)\x84\xa5\xf8\xab\x92\x9f\xdc{?L\x1f\xb2u<\x90\xff\x18\xb2\x90\x88lQ\xac\xc3gG\xcf\x8f_|\xf5\xe7\x97\x7f\xf9\xfa\x9bW\xaf\xdf\xbc\xfd\xeb\xc9\xe9\xbb\xf7\x1f\xbe\xfd\xee\xfb\xbfy\x17\x93)\x99\xcd\x17\xfeO\x97\xc12\x8cV?\xc7I\x9a\xad\xaf\xfe_\xea\xde\xb4\xc9\x91d9\x0c\xb4\xdd/k\xf6\xfe\xc2~q\xa4\x86\xdd\x99\x83\x04\n@\xdd\xa8F\xd7\xeb\xd7\xd3#55\xd3\xfdl\xaa\x1f\x9fH\x00S\xcaJ\x04\n9\x0dd\x82yTW\xcdT\xafQ\xd2R\xa2H]\xdc\x95(R\x07\x0f\x1d\xe4.IQ\xa4\xb4\x07wy\x99\xed\x9b\xf9#\xfa\x03\xfb\x17\xd6\xc2#\"32#\"\x13\xa8\xaay\xd4\xc2\xac\xbb\x00\xcf\xc88=\xdc=\xdc=\xdc\xafo\xbe\xec\xf5\x07\xbb{\xfb\x07\x87G\xc7\xed\x1d\x8b\xa7\xcbat\xa4\xc8g\xe9\xc1\x13HN\xa0\xdd\xf6\x1cqS+\xc3+b\xc18\x93Q\xd9s\xe8#O\xe7\xec\xe0\x9b\xa9z\x9e\x1d\xa4\xf4\x14\xc35\xc0O\xc0\x1e%c\x0e\xa4\x8b8z\x87\xc4\x13\xa3\xba\x15Q}\x99\xc3W\x178\x1bAO\xd0\x0b\x02\x1e\xac\xb2e\x1a\xac\x97\x98\xf0f\xaf\xaaE\xbb\xca\xef\xe7`\"\x95\xd7s\x9b.\xa6v-;\xfcN\"\xb0x\xad#\xbc\x03=\x0eq\xa3\xe4\xf1\xc8\x87\x8c0\xd3\xfeN\x8b%\xd7\xcc\xc3\xdcD\xf1s\xa4\xe0\xa1\x90\x85+.m\x90\xad@H\xff\xb4G\xb0\xeb \xc2\xd8)] Jr(\xf5\xec\x1f\x1c\xf6\xfb\x07G=\x8a\xd7\xf4 \xba\x8c#\xa6St\xdd\x1f\xf0'\x8c|\xb0\xe7\x03*\x9df\x02\xf3\xed\x88y\x18Q\xfc?\x92p>B\xc8\xa0\n9\x90\x00\x07\xbb\xf0\x08\xa2\xea\xad+>}\x99f+\xe4\xdf\x82\xb1\xd5\xb1d\x0c\xea!\x06\x1d\x0c(jY\xe7\xbaG\xbbZyC\x9eM\xd2\x8d\x897\xab\x0b\xbb\xa7\xa0\x02\x0b\xabM\xe7\xfa\x08>\x84\x80\xca\x02\x942\xa8\x12\x05\xdd\x17v\x9f\xce\xab\xe7\xe8K\xf80\x82\x04\xe7L}F\xd9r\xe7P\x85\xa3\x9f\x10\x9cb\xc3}\x18BO-\xb2\xe6E:\xf4\xb9\xa6\xea\x05K`\x04m\xa8\xe6T@\xc4B^\xbff\x14f\x01\x8f\xf8\x18:s6\x08X\xc0\xd3\xa7#\xe8\xcc\xa9\xe4\xd0\xa6;\x18\xe6t\xdb\x9d`\xf9\xc1\xfe\x01|\x88\xe1\xb2E\x03.\x88\xfa\xe6\xd0\x19\xc1\x91\xa3i\x91\"p\xa4\xb6\x14\x95[\x8a\xf3\x96\xb2\xbc\xa5l\xf3\x96(\x91`7 #\x07\xfb\xda\x87N\xf5\x06\xaa\xe1~3}5\xc2W\x8b\xcc3\x19\x9c\xc2+\xef\x15\x9da\xd8\x81\x1e\x15\xbc\x16\xf9\x9ck\xf44\xc8\xf0>\xf5\xd2Ew\x1d\xbd\xb3\x07\xec\xee[D;Z\xbe\xc8\xaa7\x17KU\xe3\xa8?,U\x15Q$\x94\xf6\x0ce\xe8\xef\xe2 \xad^\x93\xa9\xcdiBq\x9b\"6\x0b\x19\xcf\xd1\x9b\xd6\x1c\xe8\x91w\x9e\xa3\xb7o@o\xf4\xb00\xa07\xc5\xd1\xc1n\xce\xbc\xe5\xd1t\x06{\xb4\xc2\x12\xe8\xf0\xd0\xd1\xe3:\xc5\xe5\x98\x93\xd5H\xdf\x8d\x19/B\xa7\xaf\xa3y~\x85\x12\xd4\x13\xe8\xc1\xed-\xbf#\x8b\x8e\x1b,K\xc4\x13\x14\x8cq\xa7i0\x97\xce0v\xd4\xbbH\xd0-)H^y\xafl\x82>\xf2\xcc\x90\xca\xd0\xe3\x14lJ2\xf2\xc7\xbcJF\xbc\xe7tp\xb8\x0b\xb0\xae\xf92\x8ab\x1b\xbf.\xa3KZz\x87=\xf8\xe4\xd5\xc0q\x81P\\K\xa0\x8cM\x9d\xccq\xe0 \xf4\x91\xf3d\x9d\x0ee\xcb\x1f\x8e\x80\x96\xa7\x07\x82\x11\xee\x94%<\xa5\xfd9\x855\xec@\x02CXW\x10\x89n\x89\xa5CQ,\xa1E\x07\xac\xb6v\x9b\xd6\xb6\xc3j\xcb\xeb\x99\x8b1\xc9\x83(\xb5\x82Om\x82\xb5u\x18\xe6\xca\x8d\x05\xac\xb6\x11,q\xf8\xc8\xbd*E\x96\xe6\xf7F\xd0s\x9c\x13\x08hcG'(\x9f\xb5aQ\x88\xbd\x1e\xa5T\xed\x11\xcc(\xad\xdeAzA\x85\xa7:\x12\x94Qd\x0e\xe0\x96\xbe\xeb\xd3w\x83\x13\xf0\x19\xc5Q\xaa\xcf\x8a\xea\xb3\xbcz_W=\x7f\x15:0\x9b\xc2\xed\x08\xfa\x03\xba\xb1\xae*\x1c\xae\xe1P,+p\xca\xdb6\xf7\xea\x0c\xed\xdd\xc1Q\xe5\xc8[x\x85\x96\x1dk7i\xb2\xb8\x921\xd08\xdb\xc6\xdd\x9f<{\xfd\n\x1d2\xf9W\x9d\x87M\x9e\xe6fXI{S&yMW8\xccwS\xf2\n\xf9\x85\xdd@{[w\xa3\xf1\x9a\xf4\x0e\x92g\xed\xa8\x14\x0d]LPd\x87\xf6\xee\xae\xe2w\x1c\xf0GG{\x8e\xd6\xa57\xfa\xf1\xba\xf4n\xe3\xdd\xde\xa8KU\xd3(H\xf9\x185q\xbbh\xf9\x8a\xe3.\xf3\x11\xa7\xef9\x1b7\x0b\x924^g\xa5\x8eq\xa5j\x94\xcaxM\xd8\xfc\x9c\x12\x03\x161\xc1\xe0\xc3\x11\xdf\xd4(\x8a\x8bP3\xeclT\xf5\x83vN\xa0\x85>\xfaH\xf2\x92Rv\x00f\xee\x0fy\xbc\x0b\x9e\x94\xc0\x85\x16z\xce\n\xa7!\x96\x1f\xc19\xe1\xe34\x18\x85\xde\x83\xef\xb1\x84 u\xda\xf0\x88M\x15\xcb\\n\xa8g\x1e\x84\xderY7\xe4\xfa \xa1\x9f\x16\xfa\x13%]\xbe\xd4\xd2w\x83\xd3\x18l\xd84\x08\xf9L\x9c\xfb2su\xfa\xf1i\xa1\xda[\xf7X\x9ca\xa7:\xe7\xc5\xa9\xf3\xcd\xcd\x9aTN\x9e<\x80\x12\x0bV\xc5\xeeYf1\x8b\xe1\x11\xa4$\xf6.\x96E\xc0\x7f\xe5\xc2V\xd14{\xf2 \xbcb\xb7\x1a\xdb\xfa>\xbc\"\xb4\x8f\xf6\x1d\x17B\xfb\xf8\x00=\xa5\x8b\x0e\xd0\x96\x06\x1bu\xbb\xe07\xfd]\x1d\xc7 \xed\x03\xc7\xb6p\xb6\xd2(\xaez\xea\xb0\xeb\x80\xbb\xa6x\xe1\x94\x89u\x83\xe4\xa5\x98\xebM4\xc89\x85\xd2\x9eUyD\x15\xdc\x8a\xe3\x80\xa5t\xf8\xeew\xf3\xee\xe1\x9d[L\xb7U\x8d\xc9\x12\x97|k7\x9a\xde\x0dWt\xefAWtww_Y\xcb\x81\xd3\xe5w{\xbc$ .\xc3Mj\x92\xd7U\x9a\xca\xd8\x8e\xbbg\xd0\x86\xb8\xfb\xb1\x0b\x16\xabU1\"\xb2V\xd8\xe8\x0e\xa4I\xdb\x08\xa1\x9an\x9a\xeeU\xaf\x94\xf2\xa8\xef\xbd\xaa\x14\xc5p\xeb\xa0:\xbd,F\xfd~5v\xbc\xc7j\x19T\x8b'9J\xf1\xc9\xd3cj\x0b\xbd\x07C{p\xec\xd8F>-\\\xf1\xbe\xd2\xc4e \x068e\x9a,\x91\x88\xceQ\x0d}\xc8t\x9a?K\x8b\xfd<\x80\xce!e\xe9\xc9z\x19\xa4\xb6e9\x1a\xc7-\x1d\xeb!\xe3t\xaap\x9b\xf7\x8e\x0b\x87\xd0\x1aA\xc2\x82\xd5:<\xcf\x91\x9c\x1e\x91=\"\x8e\x93\xab\x89\xe8\x0b\x92%\x86\x1e\xabj\x85\x88R \xe6\x0cm/t\xces\x911We\xd3\xf3o\x9f\xd9F\x82\xee\x9cYC\xa2\xee\xfc\x84\x9e\x8b\xc0\xd7\xe4\x15\xcak^\xbbx&\xf5\xec\xbc\xd2\xb1\xdfnO\x1d\x17\xcf\xa1\xf4\xd0\x14\xdb\x0b\xa7\xebG\xa1\xef\xa5\xf6\xdc^\xa0\x02\x9a\xc2\\<\x89\xce\xf2>\xdc0\x0b\xcc\x15<\x85\x9b\x13\x07\x96\xec\x9e\xd3\xc2\xc5\xb3\xf3l|Cke\xe2\xc2xM't1^\x1b\xf4j\xd2MK\x18B\xb2\xc9\xe6\xd9\x90\xe4<\xe4\x81\x83\xd6w\\Cr(\x0elRO\xb1\xc3\x95\xbd\x19\x88\x8d\x7f\"\xb5\xda\xdf;vl\x8b\xd6n\xb9[\x88\xc65f\xb8\xc0\x8e\xa9`[Fp M7\x19E=\xf5\xda\xf9\xdc\xfe\x89A\xefv\x928\x1f\xda_xW^\xe2\xc7\xc1:\xbd\x9dy\xa9\xe7\xec\x04+u\xd4;\xe3\xcf'\xd7\x83^gr}\xf8b\xbasY-\x12\xb1:\xc7\x9f\x0f\xa7mg\xb8s\xb9RI\xdd\xd8\xeaZ.X;\xb2\xef\xb9\x19K\x12/\x0c\xd2\xe0K\xf2\x83x\xd9t\xf3@\xd8\x92\x98R5\x15\xd7~\xe8Y\xce\xd2y\xb4n\xb4\x12 k\x95\x85\xde>\x1d\xf7\xa6\x0e<\x85\x8e&'\x95\xed9\xdc\xd6\x84\x8a{\xaf\xbb\xa2\xd2\xb3\x1d9\x8e\xb0-1\x0bm\xdcMI\x922\x15\x8e\xe5]DY:\xbcXz\xe1[\x0b\x86\xe0a\xc4<\x19hB\x81M0\xa0\xc0\xe3\xdd=\xbd@\xb4\xbb\xbf\xeblc\x1e\xc6`\xf8\xdd4\xfa$zG\xe2\xe7^Bl\x0c\xd1\xda\xa6C\xa6t \x03\x96W\xe3\x9e\x1a$\xaa`\xbb!\xec\xe9\xc3:\xf4\x0f\xef\x1e\x98\x027Yy4[\xcaUE\xf7\x0e\xaa h\xf8\x04\xefU\xb98\x93\x05\xaad\x8f\x89\x02\x87U\x81\xc2\x03\xae\xfeS%\x81\x98N\xb8\x14\x93e\xc8\x05\xcarIf 8\x85\xa4+\xf2\x87\xe5\x05\xebg\x0d\xb3\x12V\xe6\x0d\x03k\xf2\xa4\x8e\xfal\x80\xaa\xc2<\x92\x93\x1b\x06<\xdfX\x1b,K-\x9a\xc9E}8\x05_\xa4\xfb\xa3\x9b\xa2\xf2\x82\xe0\xc1DS\x19\xaf\xc2\xeaa/\xc3B\x15;\x1aA\xc7\xa3\xdb\xae\xd3\xa3\xbb\xad)~\x80\x89\x9dm.!t\xfa\xdc7\x83\x07\xc1K\xb9\xa2\xb9l\xf2f\n\x90\xd89\x81v;\x84'\x10\x9f8\x10\xf0\x00\x83<\xbcv\xa8\xe6\xc6\x16s\xfa\xa0\x18\xcb9\xa5!~.Z\xed*\xc7\x11\x15\x8f\x83\x1c\xd7TdfX+\xe5\xb2\xdb\x10\x1d\xcd\x87\xac\x88\xdf\xde\xc6\xf0\xa4\xa5\x12 \xae\x86(qW\xf5\xda\x86\x94G$5\xe8m\xc4\xccUB\xd8\x95\xb4$\xef\x95.\x06h\xdbf]\xd4/`\xcc\x9d\x06NE\x07B\x18\xc2\x8c,IJ\x10R\x8ap\xd8\x8c\xa8\x02\xf5\xaa+\x99O\xfa\xb6\x13-D@1\x88\xbb\xe2\xdb\xee^\x95\xe8 \n\xaeO\x92\xb5\xbb\xaf\xcb\x92\x85\x8c\xe0\x8eC\xc8\x0bhu\x83\x04%zSx\x01:\xa5\x01c\xda\x11\xa3H:r+>\xcc]\xe5\x149>\xe5\x88hZF\xb3\xb2\xbe|\xc2\xcb\xc7v\xe8B_:\x9e\xd0w\x93e\xe0\x13\xbb&\x91\xb27N\xa76\xa5\xaaI\x193\xef\xbeR&-H\x93\xa8 0^\xefe!0)\xdfd\xdc\xd7\xe1\x14\x02J\x8dQK\xf9\xe8\x11\x84\xf0\x94\xd9\xf4R<\xd7\x88\xa6\xb6\xd8\x03\xdbv9f\xa4Z\x99_\xf3P\x98YOx\xfbt\x08<\xc5\x1eS\xda\x1e@\x1b\xbd6P\n\x0c\xf9\x03\x1c\xa0\x93\xbf\x84a\xfc\x02\x87\x91\x7f\xfar\xc8_\x0e\xa1\x83\xceXO\xa1\xe7\xb2/#\xad\xd9\xf0\x8aG\xbc`\xac#@\xd6\x11\xc3\x13\x08N\x1c\x88Xh\xb1t\x1c\xd3\x9e\xe8\xfd\x11\xa3;\xe3\xc6~u\xb76\xed\xe2A#.\x19\xe5\xb3\x94m\xb7\x94\x1dp\x1bIO3\n\x18ZJ\x0b\x15\xc4\x16M\x08\xb2`\x8d'\x93lv\xd4\xebu\xe8\xdf\xf9|>\xad\xb8\xa3\xc7\xa2Po\x97\x15\xea\xed\x1e\xcc'\x93lN\x06\xf8sN\x06\xf4\xe7\xa07\xc3\x9f\x83\x9eZ\x05\x9dd\x0b\x9b\xd9\xf5\xc7\xac\x99\x0bSs\xe8\xd85\xfe\xbc\xa1S\xe8\xc3e\x9f\x0e\xe5Jg\xe4\x00\x8b\xcf\xe6\xf3\xa9\xf3\xd5\xe0\xbd\xa52\xf0\xf2`/\xe6\xf3)\x02|sC o(\xcfk~\x9b\xe7Fw,\x16\x89A\x95Y\xb1\x999\xe9\x11\xf6g>=\x15i\xefm\xde\xe9A\xaf7\xe3\xb5\x8e\xb9G\xcd\x94\xd3\xcd[\x0bEL\xc7X\x87\xe5|XU\xff\xce\xa5^\x8e#\xd1\xd5S+\x0f\xed\xe6BX\xad\xbf\xd2\xef%\x8cx\xb6X\x1bGg\x9f\x8e\x8a\x91\xe2\xa0\xe7\xd0\x06\xdf\x05\xeb\xd2\xba\xeb\x9eH\xf9\xa9r\xe9\xb0+\xc2w\xdf\xc6\xd5s\x898\x10V\xa3\x01\x8am\xac;\xb1\xf0\xd1Z\xe3\xc7\xff\xe5\xe7~mj\xddkd\xf5\xccY\xc8JvdS.\x9c\x1f\xf13<\xe2;\x18\xb7\xc72\xdb=\x1a\xf7rC\x02U\x13\x9f\xd31\x8d\xa8F\xde\xd7Pr\x14\xff\xa2\xdc\xdf/\x1d\xb7\xdb\xc1\x14\xe9y\x00O :q\xd81\x87\n\x06\xe98\x98\xa2\xeb\x8dA\x92l:\xcf\xd4`\x83A\xcfU=s\xa3\x96g<\xb9\xf6{\x9d\xc9\xf5\xec`r=;\xeaL\xae\xe7\x07\x93\xeb9~\x99O\xb2^\x9f\x92\x82\xac\xd7?\x9cOw.kpf[zx\x1f\xe4\xb2S\x14\xdfR\xc7a\x96q\x81>\x11]\xdb\n2\xdd}\x12\x0f\x9dJ\x90\x03\xebG?g\x0d\xc1zV!\x14\xd6\x8f\xfe\x96\x1e\xfc\xb7\xf5\xe0\xbf\xa3\x07\xff\x8fz\xf0\xcf\xeb\xc1\xbfI\xc1\x9e\x02\xfe-=\xf8\xdf\xe8\xc1\xffV\x0f\xfewz\xf0\xbf\xd7\x83\xff\x1e\x05?W\xc0\xbfC\xc1\xbe\x02\xfe'\x14\\M\x91j\xfd\xe8\x0f)x\xa6\x80\x7f\x81\x82\xab D\xad\x1f\xfd}=\xf8\x17\xf5\xe0_\xd2\x83\xff\x17\n&\n\xf8\x7f\xd5\x83\x7fW\x0f\xfe==\xf8\x1fP\xf0K\x05\xfc\x0f\xf5\xe0\x7f\xa4\x07\xffc=\xf8\xf7)8P\xc0\xffA\x0f\xfe\x03=\xf8?\xea\xc1\xbfL\xc1\xaf\x14\xf0\x1fQp\xf5\n\xab\xf5\xa3\xff\x89\x82_+\xe0\xffY\x0f\xfe\xa7z\xf0?\xd3\x83\x7fE\x0f\xfeU=\xf8?Qp\xa4\x80\xff\xb3\x1e\xfc\xbf\xe9\xc1\xff\xbb\x1e\xfc\x7f\xe8\xc1\x7f\xac\x07\xff\x1a\x05\xff@\x01\xff\x0b=\xf8_\xea\xc1\xffJ\x0f\xfe\xbf(8S\xc0\xff\xb7\x1e\xfc'z\xf0\x9f\xea\xc1\xff\x9a\x82\xab d\xad\x1f\xfd\x19\x05\xdf(\xe0\xbf\xd0\x83\xff.\x05?S\xb7\xc3oS\xb8\xa7\xc2\x7f\x9d\xc2\xdf,\x14\xf8\x9fSx\xaa\xc2\x7f\x83\xc2\x93jH#\xebk=Y\xfeZO\x7f\xbf\xd6\x13\xda\xaf\x91\x88+\xe4\xed\xeb\xbf\xa3\x07\xff\xbc\x1e\x8c3\xa0\x10\xc3\xaf\x7fA\x0f\xfeE=\xf8\x1f\xe8\xc1Hh\x15\x8a\xfa\xf5\xdf\xd7\x83\x7fI\x0f\xfe\x87z0\x92 \x85,\x7f\xad\xa7\xd6_#eR\xa8\xf5\xd7\xbf\xac\x07#\x99P\xe8\xef\xd7\xffT\x0f\xfe\x15=\xf8W\xf5\xe0\x7f\xa1\x07# R\xf0\xed\xeb\x7f\xa6\x07\xffs=\xf8\xd7\xf4\xe0\x7f\xa9\x07\xe3\x9e\xfd\xab\n\xf8\xd7\xf5\xe0\xdf\xd4\x83\xff\x8d\x1e\x8c\x9b\xf3R\x01\xff\x86\x1e\xfc[z\xf0\xbf\xd5\x83\x91\xd9\xff5\x05\xfc\xdbz0\xca\x00\xca\xc6\xfc\xfaw\xf4`d\xb1\n\x07\xfb\xfaw\xf5\xe0\xdf\xd7\x83\xff@\x0f\xfeC=\x18\xd9\xb7\xc2\xd8\xbe\xfe==X\xcf4\xbf\xd6s\xc7\xaf\xffH\x0fFv\xf2\x93\n\x18\xd9\xc9\x17\n\x18\xd9\xc9_W\xc0\xff'\x05\xbfU\xc0\x7f\xac\x07#'\xf8D\x01\xff\x89\x1e\xfcgz\xf0_h\xc1\xdf\xfc-}i\xe42\xd5\x981\xd6\xd7\x7f\xaa\x07\xff\xb9\x16\xfc\xcd\xcf\xe9\xc1\x7f[\x0fF\xd2\xabH#\xdf\xfc\xbc\x1e\xfc\xf7\xf4\xe0_\xd4\x83\x91 (\"\xcd7\x7fW\x0f\xfe\x05=\xf8\x97\xf4`\xa4\xdf\x8a\x90\xf2\xcd?\xd2\x83\xff\x89\x1e\x8c\x84Z\x91/\xbe\xf9\xc7z\xf0/\xeb\xc1Hc?S\xc0\xbf\xa2\x07\xff\xaa\x1e\x8cT\xb3\x1a\x93\xc1\xfa\xe6\x9f\xeb\xc1\xbf\xa6\x07#\xa1>S\xc0\xffJ\x0f\xfeu=\xf87\xf5`\xa4\xc8\x8aT\xf0\xcd\xbf\xd6\x83\x7fC\x0f\xfe-=\x18)\xf2\x1b\x05\xfc\xef\xf4\xe0\xdf\xd6\x83\x91\xf4VC\xe4X\xdf\xfc{=\xf8w\xf4`$\xa6\x8aP\xf8\xcd\xef\xea\xc1\xbf\xaf\x07\xff\x81\x1e\xfc\x87z\xf0\x7f\xd2\x83\x91\xc6*\"\xe47\xbf\xa7\x07\xff\x07=\xf8?\xea\xc1\x7f\xa4\x07\xffg=\x18I\xef\x0f\x150\x92\xdew\n\x18I\xaf\"\xe3~\x83\xa4W\x11f\xbf\xf9c}i$\xbd?\xa3\x80\xffD\x0f\xfe3=\x18\x89\xe9\x97\n\xf8O\xf5\xe0?\xd7\x82\xbf\xc6\xd5y\xa92\x1e\x9c\xab@\xe1<\xdf\xb0\xe3\x9a\"\xb9|\x83\xc2R\xa4\xc2Q\xb0|\xac\x927\xe4\x1bI\xe1\xcab\xf2\x08a\x8ex\xdb\xab\xe9\xee\xa3Q\x945u\xdc(5\x84tL\xa6\xa5\x17\x9aT\x895J!\x83_\xc8\x81>\x1d\x89\xa2q\xcbx\xf1~\xa3\xeaKo\xde\x12zc\xbcK\x92\xf2\xe4\xdd\xdc\xf2\xc6\x9c\x92\xe4\x81\xa3}\x93\xdb]\xb2\xc2\xee\x82\x1aL\xa6x&\x9b)\x9euv\x12\xf4 \xeb\xf5:\x93\xeb\xc1|r\xbd\xebu&\xd7{\xbd\xc9\xf5\xfeEgr}\xd0\x9b\\\x1f\xd2/\x87\xf3i{\xe7\xae6j\xd1\xc9\xf0>\x9d\xf4:_N\xc7\xcf:?3\xbd\xc5\xff\xbf\x1a\xb8\xef\x11v;\xeeu\x8e\xa7\xf4+{\xc8\xbf \xf4v\xfc9\xfb\xd9\xeb\x1c\xc3t\xe7\x8e\xdd\x0f\x99g\xd8Vv\xae\xdc\x085\x99\\{\xfedr}\xd1\x9fL\xaeg\x87\x93\xc9\xf5\x9c\xfe\x87\nV:\xe1l\xc6q\xca\xd9\x9c\xe3\xa4\xb3Y\x9f\\_0\x85k\x8f+\\\x0f\xe60\x99\xa4\xf4\xf5\x8b\xc9\x84\xbe\xeb\xf5P/;\x9fO&\xe1d\x12c\xa1\xc1\x11\xfbs<\x99d\xfd\x83#Z\xa2\x7f\x84\xd6\x16Z\x11\xfb\xd3g\x7f\x06\xec\xcf.\xfb\xb3\xc7\xfe\xec\xb3?\x07\xec\xcf!\xfb\xc3\xea\xec\x1d\xb3?\x1ek\x81un\x9f\xfe\xd9\xed\xf5\xaaq\xae\x98y\xcd\x826\x0b\xecm0\x9d\xcd\xda\x96\xba\xe1P\x0b=8\xe4\xc3>\xbc\xd0[\xc9\xe8R\xd3I\x9d\xd3\x99\x9a\x1fL\x98\xb6{r\xad\xda\xba<\xad\xe9Mt\x0d-A\x95\x06\x8dU?\xeb\xfc\xcc\x84)\xdaQ\xd3\xceT\xed\x93\xeb\x191\xd9\xd7\xb60\xe4\xf9w2\xe4\xa1\x89l\xbcq\xbf\x96\x92E-\xcb\xed~\x9e\xcer\xb6\x96\x8a\xce\xeb\x8b.x\xd1-\xcd\x07\xb7&\xdb\xa9S\xb5>\xce\x8c\xd6\xc7\x85\xc1\xfa\xa8\xb5\xb5\xe2\x1d\xe8\x8d\x0c\x92\x0b\xbdA\xf2\xaad\x90\xd4\xd7G\x9f\xcd\xca\xaf\xdd\x14&\x96\xf1<\x8fs\x8f\xf3\xdf\xa6\xd3\x86\x96:\xfbt8\xbb].oW\xb71\xb9Mn\xd3\xdb+\xe28\xa7\xdc^9\x8e]\x98\xbb`}`\xa9\xf6NX+\x15}t\xfb\xc9'\xb7\x9f\xde~\xf6\xe2\xf6\xec\xf6\xcd\xedO\xbd\xa8T\x04mX\x9a*+\xfa\xb7\xdc\xa4\x7f\xe2\x8d\xa6\xe6-\x17\xf7\xfb\x87\xf6\xe9\xb0\x7f\xf6\xe6v\xf0\xea\xa3\xdb\xdd\xcf>\xba\xb5O[\xe3\xfe`w\xeaL&\xb37\x7f\xcd\xb1OG\x93\xc9\x05\x92\xf1\xf3\xa9#\xbf\x93\xa4\xb7\x83pv\xbb\x1b\xcfJ\xef\xa4\x8b\xfc\x9dg\x9d\x9fa\xef\x04.\\I\x03\xbb\x97\x8dJ0\xaf\x9b\xcd\x98\x97Y\xe48\xa8\xe6\xf4a\"\xc7a\xd5\x05\x98'@\xeb7:\xd0V;\xcc\x82l\x06_\x12vw\x9b\xe7\xc6\x9cy\xa9w\xae\xcf\x7f\xba\xf0\x92\xc5\x10o\xb6\xc5\xae\xf2p\xe5\xad\xf1\x99\x1d\xd1q\x07\x1a\x0f)\x91f\x0b+(=\xbd\xbb\\\xa6\\\xc6\x11rYU^\xe3\xf6o\xc55\x97\x0bf\x8a\xdb\x8b\xc7\xe1\x03\xed\x9d\xdd\xc4\xec\xc8\xa8\xb3%\x87\xdb\xd9\x92Y\xd6\xcc%\xf1b\x1b-\xc8\x04\x03\xb9\xe8\xa4_1\x13T\xd2U\xfd\xcaD\x18\x7f;f\x1e\xeb\xe3\xfe\xb4\xde\xb4N?\x89\x9c\x0b\x92\xf6\x81e\xed\x92\xc1\xdc\xab\x11\x13x\xca\xf0K\x82\xf2i\x19\xb8\xf0(\x12fe`\x82%\xbd\xf2\x1d\x8f-/u\x1c6\xca\xd2Z\x84\x970\xb5\x9d\xf1d\xfa\xd5\xfb\xdb\xe9\xce%\xd2\xf1\x0f\x1eYR\xb1r3\xb7\xf9}\x07\xa7\xfb\xe1)R\xf4\x89\xed\xdc\xe2\x06\xea\xb69`\xea`M\x1f\xf4\xbb\x1f\x9e2~\xf5\xc1\x9d\xe9z\xcbn\xa1\x0b\x1b%n\xc2\x03\x01o\x1e`\x18\x8d!x\x0e\x13\xfb\xb3\xd2\x8d\x9f\xcdQ'\xcf\xe5\xa6$\xbe\xccs\xb9\xed\x8c?\xefN\xdb\x1f\xect\xc95\xf1m\x8cR\x16\xe0m\xa8\xe2[\xf7\xe5\x8b\xf3\xef\x7f\xf6\xfa\xcdk\xbc\x87j\xe1\xa5\x15\x8b\xdf\xf6Kb\xdf9\xefw\x99\x03W\xd9\x15\x7f\xbb\x99hE\xcc\xd9%\x08\xb7M\xfa)\xed^gl\x9d\x9f\xfbQL:_$\xe7\xc9\xc2\x8b\xc9\xec\xfc\xdct\xa7\xe8\xae*\x05\x8dc\xff\xc6\n\x83\xe6C\xdbf\xb3&\x18\x03\xd2\x96\x85\x87\xac\xe3\xd1\xa3\xdc5\\\xa6I\xe3T\xef\xe6Y\x90\xa5\x0e\x0b\x1e\xc6c\xc6\x90;\xcf\xbe\xce\xfb\xd3:?_F3/Y\x9cSF\x7f\x9e\xc7\x94;?\xd7\x1c\xb9\x14\xbf\xf4\xf2\xf6\xdc\x16\xb5J\x93$\xa6\xa3<\x17\xc1\x1cl\xc5\x83\x0b\xa4\xb33Q\xa6\x0fJ\xde\xca<\xc4P\xbe\xdau\x99\xf4\x85\x7f-\xbf\xba\x82\xd7]N\xd9\x8dU\xe12\xfe\xa0s\xff\xe3\x9f\xce\xfc\xda\xc2i\xf9\n;\x8e0\x90\xc6\xfd\xa0\xe3\xac\xc1\xb1\xa61j\xf6\xb2X\xf9\xe6a\x16;\xa8]\xde\x89L\x18\xeb\xbb\x10\xb2\xdb\xc8\xe8\xc7')\xd7\x08\xf7\xfa&L8\xb8/uh\x12I\xc6\xd3\x07\x12B\xb42\x08\x0b\xd5\"\x89a\xebe\xe0\x93\xa6\x89\xdf\x08\xb9\xf4Bo\xccPH\xbb$-;\x14\xc1\xb6l\xba;\x8b\x04i\x1d\x8c\x1aE\xba\xebh\x8d\xa9\xda\x0bl\xc4k\x15.t:\xf9\x1c\xb9\xd0\xbb\x13\xbb\x15\x93\xf4\x974\xf8\x90\xc7\x13+T\xb6\xe3p:\xee7q\x9f\x87\x1cI\xee\x8b[\x1e\n\xa5t\xa5\x9b\xb1\x0f\xdf\x93Mw\xb2:\xad\x18q\xca\xae\xb9E\xc7\xa7\xd5n\xb7%\x0c\xe1at\xc6\xb4\xe1)^\xb3\x0f\xc7\x01\x9dm\x96\xe0~\x83}m\x1e\xed~\xe3hM\x18\x14\x8bT\xa5\x0e?P\x99n\x96\xdd\x95\xfb7\x12#3r\xb3\x1b\xa1\xa9\xb6;\xf2\xd5Q\x8clb\xb1\xac\xdb\x12\x80e\xcd\x96\x00\x17Q\xb4$^\xc8!\xa7\x94\x0d\xf0T\xae\x16\xb2\x9d\x94\xae \x93\xc8F\xf7\x90)\xb7_\x8c\xd2&\xc0\xb5\xb8$\x1b\xa8\xee\xbf\xdd.0\xd6\xf4-v\xa1f\x03\x16\xdd\xd0\xef\xbe\x101QO\xd3P\xd7\x80\x95\xbbe\x86\x1brv6\xcaoW\xf5\xef\xb7\xedv\x8f\xf6\x1c;\xb4\xf7v\x0f\x9c\xad\x8c\x90\xe63{_\x7f\x1f\xeaPw\x18\x0b\xed\xc3\x83\xc696,s^\x80q\xb3\xcc$\xd0zE\xe0!\xdd]F*\x0c\xb7\x02\xbci\xad\xbe/\xeaH\x04\xb5\xdc\xd5\xd4\x00\xfc\xaed\x84\xe1*\xc3\xda\xbe\xcb\x1f>\x8e\xc4\xf6\xc6\xe9\x14/lx\x86l\x17\nT\x85\xd0^\xfa\x94\xe0\xe4\xd3a\x14\xe0}\xe4Jp\n\xde8AQ\xdc\xa7\x82\xaa\xaf\x91\xc7\x01\xee\xa3Q<2\xdc\xa1P\xe2\xf8p\xbd\xeb\xd1\xde\xd6\xa8 \xc8l`\xa2\xf8\xfd\x928\xf4\xe8\x11\xa6*\x18\x0f\xa6\xec\xd6*\xfd\xde\x9b\xba\x0c\xd8\x9fR~\x96\xb7\xa5\x18\x8e\xa1z\x04J)Af<\xd4Ub<\xdcu\xd6\xfa\x87\xd5\xfbF\xe2:\xa1N\xe5\xd5W\xd5]\x83\xa69\x14wx<\xddd&H\x98\xf8]|e\xf8\x18\xba+`i3b=\xe5\xa3\x0d{\x0e\x96\xbc\xc1(M\x0b\x17f.\xac\xd9\xaep\xe1\xca@1\x91\xee\xca]\xbeAO\x8b\x99\x0b\x0b\x17\"\xb8\xe5w\x0c\xaf\xe8\xa6\xbc\xa9\x1fA\xcd\n\x8a\xb7\xee~\xfak\xbc\xad[]\x91\xeaA\x94Yy\xb6:\x8b\xdeC\xdel>L\x91\x8d\x85dZ\x96\xcb\xfd\x0f\xdea\xb91\xd1\xdf\xcd$\xc6\x07j\xeb\x9e\xa2\xa1>|P\xbf\xaf\xf7b\xea\xf7\xaaV4$\xd5\xbd\xc6 \x1f\x9b\x1e\xf04\xc4\x17D\xf4\xcbh\xae\xde\xd7\x04I8\n\x0d\xb5@.\x1dQF\xe7 &\xfa\x042\x16C\x9aO\xabW:\x13\x96\x11\xbd\xdd\x0e9\x06Q\xa8Z\xbd2\x0e\x10)z<\x13?\x85F1YH\xc9\xf7\x13\x8c\xcd\x8cX/\xc8\xee\x1e\xeb=\xd5\xf6zz\x83\xe8^\xbf\x8a\x12\xc8{\x95@H>\x17\x8e\xaa\x885\xe7\xf0*\".U\xb1\x00\xbdI\x84\xad\xeb\x99\x08\xa2WuOY\x94K\xc5\xdeM\xb5\xc4L.\xc18v\xb5\xc8\xd5\xfd5\xb0B>\xb9q\xe1\xd2\x85\x95\x0e\xfd)\x9a$\xdalT\x17\xf8\x84h\x9e\xbc\x83\x11\x9c\xc3),`\x08\x9e\xf6\xddk\x18\xc1E^BW\xc7\x19e\xf4\xb4\xa2wT\xacY\xc3)\xcc`\x08\xef\x1c\xfak\xa6\x16\x7fA\x8b\xd3Z\xaf\xe5\xe2\xd7\xa6\xe2\xcfD\xc5\xd7\xean~F\xf9\xb9\x8f\xd62u#\xe3&\xf5\xe5`Q\xad\xbe\xba\xd7\xcey\\\xe23\x0c\xd5\\\xb3\xbb\xf2\xf6Zgy\x85+T.\xae\x04;s\\8\xa7\x909S\xfc\x06\x9aU\x1bB\xc4\xa1\xefJ\x0f\xd4\xb1\xb5\xec\x10\x1ea\x90|=\x8dz\x0d#8Cer\x1e\xd9\xc8:?g\x89\x0eg\xe7\xe7\xa6\x0c\xd3_\xc0\x08^H\xaf\x91\xeakzj\x87\xf6\xbe/\xea\x0e\x83o)\x8e\xc3)\xa4,\x984*Vk2H\xbe\x84\x11|\x81Z\xd8\xa28\xd1\xcbD\xc6\xc9\xbe\xb4\xdf\xba\xf0R\xcc\xe3J=&n\"\x03\xb5pQm\xb5\xf6L]\xbe;3F\x95\xd3qc\xec\xb1\xfe\xd4\xb7{\xbc\xaf\xf5\x0b\xc9\xbe}\xbf\x90\xaa\x8c&;\x88`\x01o6\xb3\xd31\x99V'\x83~2\x89\xbey\xb3\x19\x06\xb5* \x94#2\xaf\x8eLq\xe0\x88\xca\xbe\x1a\x99v~\xab\x93\x1b\xde\xcf\xe2\xb3\x91D\xc4\x99i\xe8l\xc48\x7f\x9cbXs[f\xf3t\x8aM\x90\xa6&\x8c\x08m\x8acx\xac\x8fi\xac\xb8\x9ad\x06\xa9\x81\xbbE\x1d\xeb\xa5\x80\xbd^\x95\xdf\xfb*_\xa7\"\xc0@\xe5\xfe9\x8b\xfe\x1e\xd3\x15WytI\x1c\xf8\xc8K\x15G\xd5\x92$\x80a\xd7k%\x81O\xbd\xb5N\x0c\xc8\x9f\xbfB\xa5v\xb5\xc8\x8d\\\x849\xb6T\x8b\\\xcaE\xce\x88\"l\xacJ\xcfQ\x97^-r^*\x82\xca\xf4j\x91\x0bE\xee\xf9^6\x9f\xab\x1d~W\x996\xef\xa7\x02\xf2\xaeZ\xe8z\xe3@\x94g(\x17\x9c\xc25c\x0b\xaf\xe7\x1b\x07\xfe\x13\xb4:v\xe1\xda\x85\x17.<\xab\xa2~\xf2.\xc0\x08|Z\x1d\x96\xef%\x04\xde\x0d\x158p\x06\x98\xcayA[\xa3r\x9e\xd0\xdb[`\xcf_\xcf\xe7 I\x8b\xe7\xecw\xad\x00B?)\x06\x10\xbb\xc0 vy\xf4T\xf6K-\x8f\x1d\xbd\xd0w4\xb7|6\xf5\xb6\xf5\xc2\xa6\xc4=\xc0\xab\x1e\xec\x1bqtY\xbf\xb1\xb5\xa5\xda\x1a\xc2\xd7\x06\xf8Um\xef\"\xbb\x9d\xba\xd0\xd6i\x9d\xf1\xedE\xed\xdbi7\xf4V\x84\xe9/\xf1\x1b\x06jY\x91$\xf1.9\x98\xff0T\x7fc\xe8\xf4\xaa\xbeYfYR\x83\x88\xe6\xef\xcf\xf4\xef\x0bQ\xcd3\xbcvi~\xed\x0b\xe6.P\xcd\x1d&>\xb9Xf\xd3\xfa\x13\x0ch\x8d'\xbd\x96\xd0P\xa0\xb4\xfaE#\xf6 \xe9\xed\x19\xd74\x98\x9b{\x9b\xd7\xf5\x16\xe7\xc3 \xaf\xc1\xed\x08\xe6.<+\x0e\xa2\xe6\x86_b8\xc5\xd7\x88\x88\xaf\xd1T m\xe0Zy\xf0Y\xa1\xb1q\xe1\xa5az\xcf\xcd;\xba\x10\xe3\xcfD\xccJ:\xa83\x11M\xb6\xf4\xa2^v\xbc\xbb\x11\xdb\xe9\x16 3\xf5\x94\xed\xae.i\xdb\xca\x87<\xad\x0e\"\x8cA\xf5\xa5\x89\xb7\xaf v\x85\x15\x8e\xdbm2\x85\x11:\xf5\xa7\x95\xcbq\xce\xb7\xa11\xfbv\x86W;65\xa1@\xd3\xb0\x8cx\xb0\xd7\xd3i\xcc\xfa\xaa\x08\xf5@\xda\x03\x9ewO7\x89\xa8Q\x81G\x10\xa8\xf38gv[\xcd\x89\x123\xef\x19S\xa5.1m\x82M\x1c\xc9\xd2\xd4\xf2\x8d\xf4\xa8Hm\x00#X\x9e\xc0\xba\xc6\xe4\x81\xb9\xb9\xc7k\x83]\xa0e\xfb\xa8\xb1\xc0\xdc(C\xc9\xcbn\xe1lh\xe3\xa0m\xcc\xd03YG\x13i\x1b3\x96[\x88>\x96T\x0c3\x0d]\x14\xe6\x82V%Bg\"+\xea\xd8\x0f\x8dCO>+T4\xf4\xe9il\x0dO`i\x9c\x99K\xb4\xa7\x88\xf91\x98UV\xe8\xce\xb80L_\xe6\xe4\xfa$\x1fox\xae\xf0\xfc\xbb@,J\x11\x7f\x86\x90\xd9\xf4H\x8cP\x86^\x89\xc9\x8c,\x9b3\xce\xe1\x94\xf6p4b\xc7y\x8fW\xc2P\x13\xeb=7\x9b\x9cQE\xa3\xe7 \x171\xf1\xde*OT\x83\xf0\x0d2L\x94\xb2\xfd\xc2\xb7\x1d\xfdF\x16u\x14\x1f\x0dI\x88\xbf7\xa6\x89\xbf@!N\xaaU?\xf5\xefP\xba\x93\x8a\xa9\x03\xba\xa0\xfb\xe6\x1dm\xad\xdc\xc9\x80\xa7lS\xa0\x8c\xd3\xdb\x96\xd8\xf0r\xd8\xf5\x0b\xfa\xecBV{#D[\x16\xdb|'\x97}\xc7\xfc\xd0\xd9\xd4o\xc0\x12\x13\x99)\xe7?(\x82o\x99\x88P\xa6\x91\xfa\xeb\x0e{=}\x0c\xca\xbb\xfbN`\x10\xe1\xc8\x85\xe0\xce\xc7\xe2\xbd\x9e\xfe\xbe\xd0Qc\x97\xd4ZE\xcd\x11\x8b\xefnpHc\xaa\xc6\x08o`G.\x84\x1b\xdc\x0ehf\xb2\x1a\xbd\x816^=)\xc5\xa7\xcf5KR|\xfat\x1c@\x1bX\x8c\xfaqh\xf0>\xbf\xfbl\x9b\xf2\xae\xe8\x8c\x11\n\x0b]s\xe6\xf92y\x11f+\x96\xb0K\xd5R\xf0\xd7.I*\xf1[vfNT\xddEV\xca\x0c\xa4#\x15\xc2J#\xa9\xe5\xc6S\x18V\x0c\xfe.\xc46\xcb\x1b\x94\xd7\xa6\x0dO \xd5XD\xb8'\x1aMh5K\x0c\x0c!\xd0\xe3\xa4\xf7-#M}\x92\x83\x9e\xc8\xe9/c\x91\x9e\xe0f,\x0f\xbf\x86\x89a\x8cN\xf4\xe2D\xea\x15\x8d\x83v\x1b\x13\xc4o@\xc1\x9aB^7N\x84\x81\xb8\xdc\xfd\xa6\xe6\x9eAy\xdc?\xd4_B\xd4'\x0dQme<\x81X\xbf*\x82&\x06\x1b\x9a\xee.\xd7\xf6r\xa8\x8e\xc4\x85\"\xec\x84\xb2\x92\xe8D\x83\xa99\x02\xa3\x00\xca\x9e\xb7\xd0\x19$\xd3\x96ZWJ\xb5\x96(\xbci\xcb.P\x0e\xbe\xbd\x859\xfdoI\xff[\xab\xa5f\x98\xb3\xfc\x94\xb2\x8c\x1c}\x99\xae\x8d\xca0\xba\x9c\xa1r\xce-\xa3\x84\x87~)<\xbe}\xcb\xcf74\xbb\xeb\x8b\xf2\xb3m\xb1*\x90m\xdf\xb0.\"8BUS\x01\xb6\xd6^LB\x0e\xc0\xf7\xd7\xac S,I\x05\x0b\xd5P\x05\xf8Z\xaa\xd2a\xe2\xda\x8d\x0bW\x0e~\x9f1\x03\xf7\x8d\x9e/\xcd\xee\xbb\x8b6&'\"-\xac\xa0\x17\xe9\x89\x03\xb1\xc8\x8a\x12\xea{\x17\xdfy+\xeasS\xec\xe96\xa2\xce\xb6\xdc\xb4?\x0c\xb4#\xe0w\xbab\xae\xa3\xf8\xb6h\xd4\xdd\x15\x1a\xa6\xa4\x1d\xfd\xaa\xec\x16\xe9',\xc3d\x82\xc5\xf4d\xe3|\xfa>^F^\xba;\xe0\xb6w$\xe3\x95\x87\x07{\xfa\x87/\x85\x86E\xf7\xa4\x7f`|dj\xacP\xd9\xe8\x1f=_z\xab5\x99\x99K\x98\xda\xa4\xcfJ\x8db\xa6\xdc\xb1\x0e\x83*o\xea\xeb+\xe9\xeb+\xcfr\xf3G\x05^\xe8\xee\xd5\x07D\x01r\xfbGu58\xae(\x0f\xd0\x18R\x81 \x03H\x05,<(*`a\x0b\xa9\x80\xd1\xfeQ\x85q\x9bG\x05\xfcC\xe2\xbd\xcd\xfb\xd1\xea\xbb\xdbm\xc1\x88o\xc1 '\xf8\xf8\xb3\xd5\xca\xc6tW61\xf7\xc6\x1d\xd9\xec\xcf]#L\xa6fu\xe5F\xfb\xb8F\xf3Ul\xf1\xbeb\xf3\x03\xbe\xcf-6\xc3\xa5d_tr\x18\x1b#\xdd0\x9a\x9177k\x06S\xab\xc0tQx&U\xeba)\xca\xb1\x9e\xb4T\x8f\xc6\xb5\x80\xd2\x10vs\xb8\x98\xe0\x11\xaf\x1a-O>I4~\xba^\x1da\x14\x9f\xfa\xc4\xd3W\xb6+\\Q\x95\xfe\xb1\x98S\\\x8b\xb3\xfbG}'?Zn\xce\x15\xfa\x86\x03Z\x7f\xa3\x03\xdav\xb2eu\xe9P\xf7\x14\xcb \xe3U\x7fx\xa1=\x1eO\x0d\"YHE\xb2\"\x85\xbct\xc8\nq\xff\x97U1-\x9eF\x8e\xb9:\x98\xa4\x8fm\xeeU]\x19\xd2tm;\x19b\xa0<\xe5\xbfQ\xfd$\x99\xbbF\xa0W(\x11>\xc2\xdc\x92{{\xdb\x9cv\xa9\x06E\x8eD\x8e~\x0c0\xe0\xf2\xa1nu\xed\xa6\x99\xba\x9a=!\xf22uW\x1bR\x9b\xca\x92\xf7\xa2\xb1\xd2\x90\x07\x86\x84\xd0\x067\xd9\xbdA\xd5W\x92\xfbP\x0e\xaa'4\xeeC9\xa8\n]\x89^F\xe3N\x94\x8as\x06=t\xf9v\\\x81b0\x0e\xbb\x1axg\x8d\xd0\xa8\x02] 4\xab@g\x08\xad\xe6\xdf\xa3\x07#\x89 \xb2L'\x1a\xb1\x84\xee\xae+4[\xc7\xf8\xbf$\xe4\xd8}\x87\x1dJ\x82\xd2\xbb\xc8\xed\x8b\xd7\x02,\x12\x95\x8a|?\x8eVABD1J\xae\x93hyElV_V*\x8c\xc2FQ_\xc6\xceD\xa5\"\xb9\x90Q\x14\xf3\x9cB\x87\xda\xbcA\xf5\x87\xd2P\xe7c*.;\x96\xb6sM\xc69\xc4>8\x05\x9f\xa2\xba\x9a*\x93\xc7?\x10^\x12Z\xfb\x1e\xdaT\xe7\xb5\x96r\xcd\xca\xa9\xdc\xce\xe4V\xa0\xab\x07\xa7\xd3P\x85\xc6\x03AWE\xbe\xca\x86j\xea]\x0e\xca\xebo\xa8\xc2`\xfe\xafV\x91\xe3\x87\x81\x94\x80\x96MT\x92U_mGovw\x1d;\xb4\x0f\x1d\x17,\xb1&\xa6(5[\xdej\x94j\xe6S\xfc\xf0\x15\x9f\x91\xf4\xe1+\xe5\xcb\xf0@\x15\xf7\x8f\x0c\xa1\xd4\xb6\xb7D\xe4\x82\x87\xb8\xbf\xe7\xf2\xdb)B\xb5\x1e\xd6\x18E#\xaeeW\xb7>p\xa6\x91\x8e#\x9d\xba\x94\xa9Kx~\xb4\xd8\xce\x1cSX[\xd8\\\x8a\xa9\xb9B`\xba\x01\xa9\x0f_\xb57\xd0)\x0b(\xbb\xd4\xc5\xaf\xd2\xad\x86PhV\xcb3\xfewXe\x8bs\xd5\x04\xbf\xdc\xf0\n\xa1A\xc6\xc8\xf8\xe1\xd1c\x99A\x13\xdb\xc7\x95%\xcdW+\x85\x9e;\xd0\x05%\x90Z\x90L\xac\xec\xd4\x90\x07\x17\x89\xd8\x9bh \"\xb8\xc0s\xb8\x85\xe5\x03\xc92\xfd\xa3\x8dn\x83\x1bL[\xb8\xf0\xba@I,\x9d\xa7^|\x96\x86\x1a\xc0)\xa6\xc1mJ|k\xe8\xfe\xce\xf8\xf3\xeex2\x9d\xb6o'c\xfbthwN'\xb3\xb6}:\x9ct'\xb3\xb6s\xea\xdc\xdac\xeb\xf1\xd4\xb1\xe9\xb3\xd3\xd6d\xe0\x8c?\x9fL\xa6\xb7\x93I\xd7\xf9\xf0\xd4\x99\x0c\x9c\xc9\xf4\xd6>\x1d\xe1\x1b\xb7\x93\xf1d\xea\x14_o?p\x9cj^3:\xdc\x9d\xc9\xc4\x9eL\x9c\xd3\xea3\x81\xebGN\x83\x1b\x8a\xe9\xc8\x02\xc5\x0c\xed\x1d\xb0\x9b\xb8\x98N\xf6y4#\x98RV:\x98X\x16r\x14\x11\xfa,.O\x17s\xa2\x8cLGa^GLq\xab\x94C\xff\x83>f\xa2E\xe5y\xaa3A\xc9!%\x18D\x8f:\xd16\x8bH \x8a\xce\x89f\xbf\xf9\x1a\x99I\x06C\xec\xab_\x05\x90,y\"\xf8\x00W5\x84\"\xb4\xa2[\xf1\x14\x026 \n\x8c\x11x\xdf\xf3\x17\xfa\xb8\x07w\xa6\xb4{\xbb\xfa\x83\xc6\xdench\xc3\x1ab\x86\x1b\xb6\xc5\x8f\x92\xe2\x8eK\xdct\x00\xbc\xcf\x11\xad\xd4\")\x9d\xc8\xef:5}\xc35\xfc-mj\x8a\xedL\xd8\xd4\xf4,\xe8\xf0\xae~\x00\xb9X\xe0s\xcb\x07\xe5Q6)\x82\x009\xb9\x15j\xc9\xbcd\xa0\xdd\xf6\xe1 \xcck\xafg'6\x19\xfbS\xa3\xdf\xceR\x90g1\xf7\xd8\xbf5=k\xa1\xbf\x8d\xfa^\xca/s\x97\x1eh\xc5\x074\xac\xd1>\xb6F0\x87SX\xc2\x10Z-{\x0ef\x031g\xa1s\xfc\x9b\xd9k\x17\xe6\xdc\xbekKq\x13\xef\x8d\x87\x06$\xbc\xbb\x97\xc2\xae\xde'doW\xef\xbf\xa2\xca5\xd9\xa6\xc8c\xe8z\xc4\x9cD\x98G\x01\x06\xbcj\xde9w\x9e\xa7\xbc@\x9d\xc2Z,1)\x87\xa8\xaaz\x8c\xdeu\xca7\x91J\xee\xd3\xfd\xb8\x12\xb9\x0e\xee\xd3\xd9\xbd\xdd\xaa2T\xa8\x83\xf4\xa9\xb2\xf7vu\xc4\xe8S/]tW\xdeu\xd3\xb0\xcd\xc2\x98W\xb3\xf5TMA\xcb\xcb\xd5\xaa\x9d\x8aO\xde\x95\x88\x98\xc1+\x13I\xcb#\x93B4\xc9\x13\x9e'\xe8\x0d\xeeA\x1b\x12\x0c\xbc\xe62^\x1c\xd0\xf9\xdeu\\H\xee\x8f\xb6\xc2\x15V\xd1o\xe44V\xf6eb\xde(!\xb4\x01\x05\x9e>\x0c\xa1\xd3wN\xf06K\xd4\xe9\xc0\x10\xda\xed\x88%TW\x90\x85N\x13\xb1\xe9\x91\x0b\xbd\xca$Et\xa4\x9d\x86\xbb\xc7D\xdb\xdbm\xce\xc4_#\xec\x98d\x12\xf8 \xe8\xeb%\x12\xb1w\xe9\xd2\x12\xe8\xa0\x10N`\xd8\x18\xc2\xc1<\x82=\x9d\xa8\xd2\x87\x9d\xaa\"\x0b\xe3\xbbt\x0f\x8f\x0f\x0f\x8ew\xfb\xbb{G\x07\x83\xdd\xfe\xfe!\xd9\xed\x1dm;\x01\xb9\xaa\xfb\x94\xf9^1S\x01\x13\xe3\xa8\x04\x8b_;\x01{\xcc\xc2\xbeu\xe8\xfa\xf7\x1d\xf8\x10\x1d\xeeR\xb1SR:r\xfc7\x92!w\x9d\x0b%^3\xd7&\xe8\xb4\xc3\xaf\xbcW*-\xd8\xf9|\x92\xb4o'I\xfb\x83\xea)\x83Ex\x1ew\xda\xd3\xde\xf5\xb8\xd79\xf6:\xf3i\xfb\x83\x9d@\x15Vv>\xef]\x8c{}\xcdS\x9f=\x8d\xc6\xbd\xce\xa1\xe61\xe5\xe0k/N\xc8\xcb0\xddvI\xe8\x8e\x91\xa3\xbd #`\xbeqR\x95\x10\x05\xb6yc\xa1J\xd3p=\\\xe0\xbf\xd6\xc6\x91\xe6\xd7\xcfN\x8b\xef\xecJ\xb3^\xe8\x89\xd9\xc9\x9e\xdd\x10\xa2\x9b\xa1T\xea\xbd:J\x11\xe4\xae\xa5\x19e\x19\x8f\xda\x95&\xd9e\xb1r2j\x95\x00\x87,\xac6K\x14\xa3\xdd\xc4xN\xf3E\x118\x85\xb9\x9dv\x93e\xe0\x13{\x80j\xa7S\x18\xc0\x10\x8e\xe8\xa8=\xa9X\x84}\xba+r\xf7\x15uK\x03\xb7\xdb\xab\x8a\xd8\x99V \xe7\xa6\x8f\xbdf!\xc9\xcc\x01\x19\xf7a\xb2\x12\xe5W\x86iC)4\xaf\x86\xb2-\x8aGL\x8c\xa1VE\xf1\xfcc\xd3\x172.\xdaf\xf0\x04\"\xe6\xe8\xd4\xc7\xb8q\x81\xed\x8d\xb3)\xbbH\xe6\x9c\x98\xf5\xd1\xa6\xd8\xe7\xdb\xae\x84\x9eN\x18\x82\x0d\xa9\xea\x98L\x08T\x1b\xac\xa7\x86)\xe0\nd\xf2\nT\xef\x1f\x89\x83\x93\xf0\x8d\xd0\xd2\xdeV\xab$\xd5x\x18\x1b\x86\xb1\x8e\x08\xf7e\xae\xe0\x18\x96\xa2\xdfz\xb9\xbe+\xe4\xee\x9f\xe1\x98L\xb7\x8f\x99ne \xc1\xec8~*\x99/\xb9\xd3\x05\x0b\x97!\x9clx<\x18\x92|\x1a\xcd\xb2%\xb1\\\x85\xc1,32,E\x8es\\\xbcs\xbd\x8a\x82/\xc9\xec\xcc[\xad\x97\xe4\xe38Z\x9d\xf9\x0b\xb2\xf2`$=|\x1e\x13/%\x7f\xe3\xd3O^\\c1\x16J\x0d\xbf\xfe\x8d\xd5\xb2\xf2R\x10\xceI,\xfdN\xd4\x9a\xb9\xa1\x1bH\xd7Wk^\x9eh\xf0\xa9\xaf\xa4H \x90\xe7\x87\xf6\xde>=n*H\x85\x8f\x0ev\x9dM\xa3\xb1\xc8|\"\xed\x16\x13\xc9e9\x95\x1a\xcc\xc8\xdc\xcb\x96\xe9\xb0z\xab\xf4;\xea7\x81kj%\"\xf3Q\x8e\x04&\xaa\xcc\xbb'\x90L)\xf3^= \xb2\xa2\xe7d\xe5\x05\xcb-Z\xc8\x12\x12\x7f\x97\xb0\xd5\xe8\xfa\xd1j\xa3\xb6x\xbf\xceg^J:i\xb0\"\xd6\xe6-\xa2\xaf\xc5G^J\x9cn\x1a\xbd<{\xcd\xbc@m\x8d\x1dBs\xda\xc5\xcd\xb9y[\xbd\xcd+=\x9f/#/}\xe0\xaa\x830%\x97\x0f\xdea\x1eD{X#T\x88\x8fX\xe5<\xee\xb6t\x8c\xe9r\x94fQ1\xf8\x0f\xb5\xfd2\xba\xab\x07\xd0\xfaN\\\xe5\xfel#\xb0{.\xc4]\xe6`\x11\xcco\x1c\xadB\x03rC\x8b\x9a\x82H|\x02|>\x8f\xe2\x95g\x88\\EI\x827\xc6\xfc\x91\xe7\x16\xb4!\x98\xa2\x0b\x90\xf6\x12\x92\xc0K\xec]\x90|\x9c\x85\xbecGx\x82\xb2\xd1\x1ek\xfd |\x1bF\xefBxs\xb3&C\xa0\xf5\xa5\xd8\xbb\xba\xa9\xf1M\xc40\xa7J\xa9^u)\x0e\x85\x9e\xf0%\x17\x97\xb2\x9fB\x1f\x8a\x9c\x14\x94\xc9\xe7E\xc6\xfd)\x15\xde\xe4\x9f\x98\xc7\xca8{\xcaR\xe8\xe2\xc5\x81\xf0\xf9\xadY\n\xb4yw9\xfd\xd0\x17\xf1\xb0\x08\xbf\xc4\x17\x10\x8dg/\xf0\xf9\n\xba\xdel\x16\xd0\xc9\xf1\x96\xdfo(?\xc7\xf2AJV\x86\x02h\x14\xe9\x06\xa1\xbf\xccf\xe43\xe2\xcd^\x87\xcb\x1b}\xd1\xb5\\\xf4\x87q\x90\x12ZV/\xe8I\xd3\x9f9e\xdc\x99\x11\xb2^\xdePz\xb6\xfe\xeb\xe4\xc6\xc1#\xff\x07\x1f\xc4dnma\xa5\x94\xe5\x8a\x92ou7\x08g\xe4\xfa\xf5\xdc\xb6\xfe\x8aU\xc9\xcc >\xefM\x16\xa2H\xef\x7f\x1c\xb0\xe0\xb7\x91\xe4\x1a\xae\x176kb\xec\x82hc.f\xc3 \xaf\x8a\xdb6^\x1c{7*\x97\x01\xedy\x01U0\x85\xb7\xf9\xc8l\xed\xbe\xe2\xc1\x06\x14\xcc\xae\xba1\xca\x9fY\xe56\x8b\xfc\xc9E\xf5+*\xd8-\x1cX\x8c\xaf\xa6t%\xe8\xdf\xee\x8c\xacc\xe2{)\x99\xe1\x8d/\xf9Q\xccq\x0d\xd8\x05\xb6\xea\xe3w\x02\xbf\xf0\xf9\x1a\xef\xb9\xcfh\x81\x11\xa46-A\x85B\x83\xd0\x8f\x13\xcd\xb4N\xbe\x03\xb3\xcav\xe9\xd7\x8c\x06W\x90\xbe\xee\xebQ\x01\xaa\x11\x0c\x94y\xf4\x1d\x97\xc5,\xb0o\\\x8c\xb2\xb6\x82\x11\xf4O`\x05O`\xef\x04V\xed\xb6\x03\xb3\xb1U\xee\x12\xa5\x95+:\xb4K}\xb78\xd2\xcfTT6\x91i\x8e?\x0c\x19\xe0\x94\xa7\xb2 \x12v\xbdl\xde\xf5\xc2\x9b\xd7s\xd4\x92\xb1\xaf\xdd\x95\xb7.<5\x9a\xee\xe6\xb2\xf8\xf3:\x9f\x08\x18*ME!\x11M\xe1\xd7\x07lj\x9c\xdas\xfa\x94\xd2q\xd2%a\xb6\xc2\x10\x8c\x82c\xcb\xdf\x87|\xa9B\xca\x0e\x97\xc1\x97\x04\xbb\xe7\xd8\xec5g\xdc\xa3uX\xf3`IX\x8a\x8d\x08\x1d\x9b\xd0\xa5I\x17/_U\x12\xdbU\x19\xbf\x9e\x96\x89\xe1u\x13V\xfe\xd1#\xa6\xb6\x17\x00\xf4h)\xb8\x01{\x8e\x1cF\"C\x8aO\xc6{\xd7x\x04\xd9\x88\xa1\xb2K\xcb\xdf\x1aO\x8d\xb6\xe1\xa9x\xff\xa5\x86\xa7z\xf8|\x13\x86\x19m\xc90\xa3&\x86\x19\xd5\xb3\xf25c\xba\x9b\xf0\xd4\x85\\4\xe7\xa9\xfa\xb23l\x99#\xb4\xbe\xc8\x15\xd26\xfd\xb3\x9b\x9ag\x97(\x86]\xaf\x96\xfa\xc7\x94\x86]b|2\xfd\xf3s|\xbe\x8e\xc9<\xb8\xd6\x97\xb8\xc8kH\xd6\x9eo\xa8\xe6\x1d\x9b\xda0[\xe9\x9f_\xe7\x87d\x03\x03\xcfj\x188\x9a\x07\x1c\x96\xda\xfc\xc7\xc1\xc5\xb3&.\x8e\xd1Y1l\x8c\x15F\xa9wI'\xc7b\xfe\xb1\xf69\x9c\xc29\x15\xcb\x87\x16\xba\xb6;\x94A\xb8p\xc1\xf4\xf37c\xfa\xdc\xba^-\xc3\x043e\x9f\xd3B\xf8\x13o\x03^\x18\x04\x1c\x99)\xa0[\xe5\xdcD|i\xe99\xc5\x07J8\xf0\xef\xed-\\\xd2\xff\xbez\xef2\x08\x0f\\'\xff\xa0e\x18\x96\xc0e\x97\xc7\xe0\xcd\x85\xbf+\xee\x95;u+\x1cbIy\xc3R\x8dZe\xe4\x0c\xf43\x17;\x90\xe5\xa4\xa2\x953?>\xe4\x08U\xfd\xbe\xf8h\xf8\xd3\x8c\xb6>\xdb\xbau\xc1V\xb6n]L\x03/9u\x01%\x9c\xa2\ns\xab\xe7^\x9a\xc6C\xb81T\xee\xc2\x95\x1e\x1b)e?3\xb8XB\xc1\x8a4\xabb\xdfsY\xce6\x9a\x15\x17\xce\x0c\xebb\xdfsa\xb6j\x9f\x97R\nm nk\xd3\x12\x01\x9f\xfa\x17zq\xbbA\x9c~F\xc5ii\xcf\xd0\x9d\xb8\x14\x1b\xf0\x85Y:\xa5}{Q\xb9jh?ct\xa3\xf5b\xfcL\x12\xbcooa-?(Dn*\x8c\x1b\xa6\xab\xd4\x0e}\x8b\x11\x89\xfc\xab\xe8!\xff\xdd\xa58\x1b\\di\xed\xb2\x89\xcf\x15\x8f.YF\x05\xac\x0b\xa54\xda\xd9\xfc\x971\x05K\xf5\xf3\x85\xe8_-\xd3\xae~\xde\x8a\xb78F\x99)\xbd\xf8\xdc\x8c\xf3Q\x0br\xf8l\x9a\xb3,\x14\x9b\xbe\xa0#\xf8\x82>\x91\x80\xcb\xf13<\xf7\xe0\xdf\xf2\xa3\xb7\x14\xfe\x96\x0214f\x82sQ\xbf0\xb5\xa9^\xe4O\xb9\xb3#P;\xef\xca\xce\xe9\xf2\x0cV\x84A1\x00\xbbT\x86\xc1Mv\x19\xe9s\xc5\xe3f\xa6lt\xcd/\x94\xd1\xe3%\xa5\x14|\xa7 \x19\xf5\xa3\xd0\xf7R\n\x1fJt\xf5e\xc3\xb4\xd5\x91Fq\x98\xe4\x0d5\x11\xea\xb2\xb49\x04\xebYx\x93.\x82\xf0\x12|/\x84\x0b\x02\x0b\x12\x13\x83T@;\xedo\xca\x11\xaa\x0d%\xa6s+%r\x0f\xc8g6\xa0\x91|\xe6\xae\xcb\xf8\xbf\xe4\xae\xb1\x12h\xc63&\x94\x17\xf5\x1d]\xd4w\xecT\x96\xb0\x80kl\x85o\xe0\x14\xc6\xfa\xbe\x1b\xfb\xfd\xde\x85kZ\xd1u\xb5\xeb\xef\xb5v\x90\xa5\xd9\x17\x81\xca;\xeci\x19K\xd1\x08Z\xd2s\x05\x82n8vX\xb5:\x01\x1aJ\xfc\xa5\x17{\xb4\xc1!\xb44\xd7\x1b\x83pF\xc2t\x08\xd6$\xad\xdc\xae\xab\x9a\xcb\x00o1\xd4X\xa5h\x7f\xa2\xa2?\xcb&\x13W\xa5<\xc7\xa9\x06\xab\\\x0d\x87\x96<\x05\xf6\xabn1PxK\xec\x0f\x9c\xeeY\x1a\x13O#\xfe\xa3N\x8c~\xb1\xa4\x15\x83\x8a\xf5Jo\xf5\x04\x919\x80\xd24\xcd\xc9\x01=\x05\xd0\xa5\x11\xc7\x1e0\xd1!\xbf\x92k\xb3\xf7\x9c\xee\x17Q\x10\xda\xe8KgYU\xdb\x9a\xf8$\x94\x8c\x19\x84oC4\x08\x1b\xbdD\xd3\xb1\x142\xe0-\xb9I\xec\xd4\x19\xf7\xa6SdyI\xf7\x9c,\xc9\xaa0\xdbr\x80\xa0\xdc\x91\x9bC\x02?\xcaB*\xfd\x84\x12\x0c1\x89\x0d\xab\x0c\xa3-{20%q\x9c\xadS\xcc\x00'\xc0\xfa\x19\xf3\x99\xd3\xbe.4\x14\xf0S2\x957\x95\x87\xf9z\xad\xcd:\xde\xf24l-\x02\"y\xab\xf5m\xa8~r3g\x1b\x1e\x8f\xac\xc7\xd0f\x0epmxl=6\xbe\xf8\x1e\xbd\xa6\xc7dj\x14,7 \x93\xe2z2\xc7\x08%\x94\xad\xf8\xe0\xa5\\\x81B\xfa\xbb\xb9Pv\xc6\x18\xd1\xca\x0c\xf7\x1a\xc4'\xe9\"\xcd\xa48\xb6\xb6\xf9\x0f\x0cty\xee\xcf\xbc\x14\x95RK6\x9d\xb6\xf5\xa45~\xfe\xd1\xb37\xcf\xc6\xf4\xc0)J8\xb9\xe3\xde\xced:\x99>\xdd\xb9t\xc1\x9aN\xa7\xd3\xa7y\xf1\xa7xx\xb5\xa6\xd3\xa7\x16V\xcdW\x13Q\xdf\xe7\xa1k\x96\xd2=\xaed\xc3\xf8\xc5\xf2G\xbb\xb7N\xc1\xc2\x01!T\xd9YpJ1\x90\x0f\x19\x86\xa2\x0b9\x15\x816\xf4\xf1r\x81\xbdd\x89\xb5]T%\xb5zyo\xd1\x13\xd3,T\xbc\xc77no\xa5\xc1\xd5\x8865\x0b%L\xea\xc6w\xf3\xfe$\x9a\xee\x189\xb3~F)E\x19B\xa4\xdf\xd49}\x18\xd2U\xd3\x16\xc9\xc5\xfdd\x08s\x83F.\nS\xe4l\x06e\x13#aC\x08M\x9d@\xca5\x04\xaf\xeey\xd5e\x15\x94\xa9xo\xe0#^\x1d\x1f)\x11\xf2\xc2HL$\x97&\x8a\xcf\xba\x08\xf1\x82 \x12\x89\xcc2\x0f|\x0c\x9fK\xa7$\xbf\x9d`\xa6\x9a\x81\xd14\xce\xd3X*\x95\xd5\xed\x1d\xe1$W\xbc\x94,\x82yZ\x0d\xa8#\x7f*\xc6=\xadKX\xb5|d\x07N\xb3\xc2\x8c~p\xf25gp\xf1\xd1K\xe9z([\n;F\xed\xf5)\xce;\xe3yB\xa1f\xf3\x94\x0b\xa7`=\xd9\xa1T\x8d\xffn\x83\xf5\xd4\x92Kq\x06\xfa\xe8\x11\xb4BZz\x12\xf2\xc7\xe8W\x8c\x17\xc9t\x1b\xcf\xbc\x8aQ\xa3\xd9\xa3\xd5\x92\xf1\x04\x9dr\x8b\xdf]o\xbd&\xe1\x8c\x8a\x0d\xae\x8cO]\x06\x0cJ@\x11\x1d\xccn\xf5\x1c\x17Z\xbdMH\x04]4\x8e\xc9\xf9\xac\x95\xe7K\x9a.i\xa2\x8a\xdd/,\x07\xa7`\x01++=CI\xca\x02\xcb)\xde\x8dq\x85D\xf5|\xfaqo\x08\xd8\x8eiM\xc4\x02\x97\x96\xa5\x15W\xb7\xa4xC.\xa8\"#\xae\x0c\xde\xbd3]\x87\x82\x1a\xa7;-\xcd\xd0\xd0\x0bD\x1a\xf4H6\xa8_9\x0d\x0b\xd5\xb52Q\x16\xf41\xc5\x08\x00\xdd\x04eh8e\x99Px\xaax\xb3\xb5\xc3\xb2\xcc\"\x9c\x89\xcc\x0bW\x00>\xa3\xfc|,A\"\xda\xac\xf894\xb6\xb1\xe0q\xe4\xcd[ef\xe6\xfe\x0b\x863\xe4:}\x13\xf8o\x99\x13J\xba\xe5N\xbc\xaa\x95\x0f+\xc4\x0e\xf5\x1e\xf6\x1c\xda#\x96\x8c\x12\xf2\xd8\xab(\xc9 \xb7\xc79\xe7\xd7V{\xa2\xd0\xb2\x89\x08\xe3\xc1\xd2L\x1agv\xa3g\x94\xf8\xf8]\xb2\nR\xdb\xa2\xd2\x99\xa5\xb5\x9c\x8a\x0f\x15P\xd8\xfaoHT\xeb\xe6\xf1\xa6v\x1e=\xfb\x8a'\xa0[\xbb\x98\"\x91\xb2\xbd\x9e\xa3\x0f\xed\\\xd3\xca\xa5q\xf8\xccf\xdf0\xcb\xe9\xb75\xcb)\x95\xf58\x88\x843\x0b\x7f\xc6\xc4\x9by\x17x\x00\xa7\x04H<\xf7\x97QB\x0c\x91\xee@\x7fl\x00\xc3rT!\xc2M\xa0y\x1c\x0b5=$p\x94\x08\xbb\x92j\x02q\x1b\x8f\xee2\xd4\xc5s\xae\xbe\xe6+\x12'\xa8\xd3\xb0\xfa\xdd\x9ea\xd7\x93\xd0\x8ff\xe8\xe1\x19w\xc5wFr)\xbd\xfa^\x8a\xd9\xd4%K\xb2b*\x85\x02\xf6\"\x87\xd5b\x9f\xd8\x87\xfa\xe1\xa2\xc2a\x08\x99\xcd\xb4\x81E\xecD\xbc\xc8\xc5\x82\x15\xe6\xbe\x06&%\x0c=\x0dm\xe2\xf5 \xc2\x9a\xcb\xf2@\xa2L\xe5@\xba\x88\xa3wH\xc61(\xacm\x85Q\n^\x92\x04\x97!\x99A\x1a\x81\x07,\x14uK'?\x88\xcf\x95\x94\xaa\xbb\xde\xdePdG\x96\x143\xe6\x8a=[\xea-'\xaa\xa1[\xaa\x81\xa9\x80\xdaT\xc0\x10\x94V\x0e\xbc\xdfD\xdb\x08\xaf\xdc\xd6\xc9\x8a\xe2c\xa2R\x86#\x1f\xa5y\x9b.\x89\xc4p\xd9\xee\xa1Ccv<\x91\x01\x9a\xca\xb9\xe2 \xed\xe9\xc6$S\x9dW!$\x96\x91=\xffU\x8a\x1a\xba\xbbg\x88\x18*\x0fG\xb0\xf3\xf2\x00\xadG\xd6\x10\xacG\xdej}R!\x8a\x8f\xad\xc7\xf4\xc9\xcffQZ}d=f/\xad\xa3Dy\xf4\x04\x1f-\xd5w\x9e\xe2\x83\xcb\xf4\xa4\xa0\xa3\xd2\xb0\xb7\xbal\xc5\x89\x17\xa7lH\xbcru\x8f=~d=y\xfax\xea\xec\\\xd6LF\xa5\xc2pL\xaaI\xb4`\xb8m(\x8a\xd2%\xba\x93\xd2\xbc\xf3[\x11\xfd}\xa7\xfb\xe2\x8a\x84\xe9\x8bU\x90\xa6$\xd6)\xf9\xd5\x83t\xccc\xa1.\x02\xe5Z>\xfd\x84\xf6\xee\xbec\x07.&\xd3\x0d\xba\x9f\x15\x14\x93\xb6x\x80\xc0\x1f\xc6A\x9a\x03\xf7\xf6\x8f\x11\xf8Q\xb6^\x92k\x06:\xe8!\xe8M\xec\x85\xc9<\x8aW\x1c\xdaG\xe8\xf7\xbd$y\xb3\x88\xa3\xecr\xc1\xe1\x03\x843\x9d8;\xd8\x05r\xc2\x8f\x00\x9d\xc1j'\xffJ\xca#o\xd2\x9c\x07\xfa\xd3h\x8a\x06a\x1c\x0e\xbb0\xc5X\x0dZ\x89\xe9\x1b\x18\x1bh\xede \x91\xbe*\xc7&}\x93\x91\x96\n\x85\x05\x1f\xc2\x1ac\x92d\xab\xd2\xf7\xdaSY\xd8\x8d\xc2\\$\x0b\xd0\x81\x0e\x01\xb1\x17\x84\x96\x0b\x11B\xce\x83\xe4,\x9d\x05\x11\x957\xe4\x81\x11$*\xb7\xb7`\xb3j\xa8\x18\xe7\x82\x87\x02\x11\xfd\xcd\xc46\x17\x92\xaa\x16\xef\x8a\x874k\xf5M\xf3\xebi\x07\x9bac\x19\xe7\xb8)\xa3c\x9b\xcd^\xb2A\x85\x86{\xe03\x92\xa4qt\xc366\xff\xb1i\xb3\xbe\x9en\xa3\xaf\x90\xed\xb8\xdcN\x1cw\x97A\x92\x92\x90\xc4\xcf)\x1f\xc2\xfd\xe4\x82E(3\xb5\x1c\xc1_\xab\xf4V\xdf\xe2\xdc\x88&\xab\xe8\x8a|\xc2\xdb\xa9\xac\xb9\xf2PZ\x7f\xf5Uy\x9d\xab\xcf\x8a5\xd7\xbe\x89#\xa2\xc2\x92\xaeU\xf9\xa9\xa9\xd5ym\xabsm\xbd\xc5\xd3\x9a\x9d \xc8-\xc3\xe4R?\xab\x10\x19\xdb\xe7\n\xb6\xcf\xf3w\xca\x10v\x94\xa1\x04\xc8b^\xceM4\xdca\x8ec5d]\x7f\xab\xaf\xa0\xeaG=\xa7\xcb\xc2\xe3\x96\x19\x9e0\x1e6\x86\xc8\xa9\xa2R\x8ee\xa9\x16\xcbZ\xcd\\\x0d\x84\x00i\xa7 %\x19#\x8e,E\xbe\xb9Y\x13.I>\xf7B*LR6\x03\x1e\xf8K/I\xc0K\xc0\xcb[\xd2\x1c\x0b\xdf\xf3\x0d\x94\xcb>\x0b\xe2\xcd\x80E\xa3\xe1\x90\xd4\x0b\x96e\x08?\x0e\x8c\xaa^\xcb:$I\xd5\x8c\xe6\xf5r\x9a\x10m\xf5\xf3A\xb7\xa21S~H\xaeS\xa6\x8eR\xc7\xa9\x8af\xf2P\x9eb\xc0\x92|\xb8\xa8\xf5\xc1\xdb\xc0\xc3\xd2\xac\x90\xf2\x94\x10\x17\xdam\xa9\x9a\xf2l\xb8\xa5\xb1g!\xea\xbe\xbf\xfd\xe1\xe7\xfd\xddd\x0ex\xec\x0ci&\xd0\x11\\\x1ec\x051\xb6\x19\xb32b\x13}\xe7\xe2xQk\xddy5\x15'\x1a\xda\xa3.\x9d\x91Z\xbf\xc3\xbe2\xc4\xd3\xd2\x80\xaa8^Y\xf2\xa2%:\xbd.t:RU\xda\x98\x85u3\x82\xb1\x0e\x9bf\xa4\xaew\x0d;\xb0\xdc\xda\x17Q\x106\"\x1c\x9b\xffQu\xfe\xc5E\x0f\x8d\x17s)\xean\xdeY\xe6Zl1m<\xae\nO\xcdM\xe7\xed\xc4\x81\x10\xda#4\x81\x13\xc3\x9a \xaeR;\x7f\xe8{u\xcf1\xc5]o\xb9\x8c|\xbbg\xf0cV0\xa6\xd0\xf57\xa0]13xj\x0eXl\x08\xde\xde\x0f\xc2\xc4\x9b\x13;\x85\xa7O\x9f\xa2v2+O\x9fG\x97\xf3\x04\xb2\x13\x07'.\xc36\xd8\xacF\xfc\xe2\x04^\xde\x8e\xd67,\xb0\x01}\xa5-\n\x96\xa2\x18dl\xd2MS\x1c)S\x9c\x03\xdeSI\x0b\x03s\x06\xdd L\xd6\xc4OK?\xba~\x96\xa4\xd1\x8a\x91\x89\\9\x93/\xd0\xb8ZpZ\x87\xecb7\xe7/i\xd4jlXC0\x92\x1c}\xb8\x1e,.\x05z\xcfMo\xec\xe2h1^\xe3\x89{c\x7f$\x1d\xfb.sw\xbd\xddF+\x90\x88\x0fS\x1cu\x13\x92\xbe\\\xad\xc8,\xf0\xcc\x1e\xae\xdc>\xc3|\x8cx\xcab5&\xb3\xfc\xf1k\xaej\x007\xdb\x98L3\xc0M7iw\x16\xf9\xa8(3\x97[\x97\x12B~_ \xc9k\xcc*\xa7}`\xcc\xa7N\xab\xc2\x8clk:'o\x82\x15\x89\xb2\x14NaM\xc9\xb5[D\x8c\xe7yk\xa6\xccq\xfa\xab\xf7\xdd4bW\xdb\xf9\xe9[$\xb6aQ\x8b\x9a\xe8\x88\xf8Hf\xa0Z\xca-\x7ff\xb6&\xaa\xaf\xf8\x98\xf4[0\x94Q\xa7\xae \xb4\xa1v\xd7Q\x92~\xca\xb3\xf9\xb3\xac?\xc1\x8an\xc93?\x0e\xd6\xa9\xd1\xddG|\x04\x11\xd79\x08V?x\xcc\xefF\xe1\x8a5Woh\xcf\x85\xbf\xbc|\x13\xd3\xab~\x88\xde\x84 \x7f\x18o(f\xc0\xb6,\x17\xac\x0f-~\xa8(\x1a\x0e\xab\xa1\x94K\xb5\xe8W\xc2vP!\xc5\xab~\xbe\xf0\xc2\x90,\xe1\x14l\x1b\xa3\xa7\x90wP~\xe4t\xe9\xbc\xf7\xf5\x03\xaeE\xae\x99\x9d\"\x057\xa9<\xb7\xc0\xd3\x08;1(M\x8a\x01\x0bQ5\x86\xc6E+\nc\xe2\xcdn\x92\xd4K\x89\xbf\xf0\xc2K\x82i\x92\x97\xa3\xddvD\xbe\x8b\xe2\x0e.Z\x06\x0d\x97\xbd@r\xfb\xaa\xdf\x85\x94\x1f_x\xfe[\xe3qV|\xbc\xf82\xd1\xf9\xdb\x89\x8f\xe1\xae=\x14l\xc8\x1f'S\xa6\xdf\x8e\xed\xc4q!i\xb7M\x08\xb7fG4y\xed\x16J\xd9:\x1f\x82\x85y\x89Yzw\xf0\xab\x81\x9b\xa1\xa1\xca\x1a\x1f\x15T\x8e::\"\xa1\x9f\x94\x86\xbb;\x02[h\x17\xeb}\xf4\x1a}\x9e\xe7\xdc\xf5\xa6\xaeL}\x9a@\xf1im\xb8{\xe4O~:\xed\n4k\x16p\xc4'\xc6\xf7(\xd6\xd5\xf7^|\xf2\x14P\x0d\xba\x0b\xdd\x07\xfd\xae{f\xdf[\xdd\x87\xd4\xf9O\xea>\x0d^\xda\xd5\x0f\xf6\xa9\xbfm\x9f\xe2qo\x93\xbbU\xf2\xe7.\xfd\x1a\xdc\xa5_.\xc4\xe3\xfe\x8f\xa3w\xbbw\xef\x1d\xfd\x7f\xf0-\xf7\xb1\xd1\xd5[\xf7A{\xfd\x12U\x0e\x1aw\x0f\xddG/Q\x97J\x98\x84\xa3\xbc\x00\xcc\x83\xd0[.7\xa1\x0f\xccp?\xdf\xe0\xbc`|\xba\xa9\xdfoE\xb7g[Y\xc8\x02\x02\xcedY(!\xcby\x11\xa9?\x0fN\xbc\x08\x12\x0c\x83=\xc4\x02\x92\x0d\xb8\x949\x14y\xb1\xd9\x15`\xf3[Q9\xfb0\x90M3\xf1E\xdd\x03\xe9.#\xdf[\x9e\xa5Q\xec]\x12)\xa2\xa3:)r\xfeTm\x855\xef*\x10aQ.\xb7\xaf\xe5GBa\xc8sn\xa07\x99\x95\xc6\x19a\x87\x7f\x1e\xd2.t\xbai\xf4I\xf4\x8e\xc4\xcf=\x8d\x01Y\xfe\xb5q\xf0R\x10wal+\x8c>\xe2A\x88\xd0\xc0b\x8a\xbd\x0d\x92\xb1\xa9\x1a\x15\x13\x8a\xb14\x9eapm\xb4ai\xe5\x12\xa1m\xa1\x85\xa8\xd2\xb5\xaa\xef\x91\xee\x1e\x81\xf8\xd0*b\xcf'\xa5*\xe0\x14\xfc(L\xa2%\xe9\xe2C\x16\xc0F\x80\xdeyq\x88g%\x1c\xa4\x1aD\x0f\x8c;-W\x170R\x93\xa2I\xaap\xc4j\xda\x87\xc6\xad\xb4\xd1\x1e\xd2+\xe2J\x19\x96\n\xb0\xe4\x06r\xac\xcb\xa3\x14\xda\xfb}\xed\xad\xcfH\xdd\x1e\xdc\xb6G\xe9\x82d\xde\x8b\n\x1c\xa2+\x15\xa9\x01\xc9\x0bG\x12MpS\xac\xb8\x1b\x84\x0b\x12\x07\xd8yt,q%\x98\x1d1'\x93H\xd2\xab\x9f\xa7\x92\xcbH\xddd\x01\xa2\x06\xb7DT\xdb\xde\xc2\xb3\x86.\xcf\xe1F\xcbS~k\xd0\xbf\xc3K\xfd\xfe\x81S8\xc5\xdc\xf1}\xc9}f\x93\x1a\x9a\xec\xcd\xfdc}\x16\xc4\xfe\xb1>\xcf\xcd\xdeAs\xac\xf6\xeaBqK\x04\x0bH-\xc7P\xd2\xeb\xcc\xb3\"zU\x8c\x97R\xd1*g\x13)\x8a5\xe6\xd6\xcb\n\xebWau\xe8z\xc9M\xe8\xf3\xe4\xadYw\x1d\x07\xab \x0d\xae\x08\x9c\xe6.0pZn\x02\x87u\xbc\xef`6\x0c\x1e\x03\xca\xd6\x948pl\x82w\xe5*\xcf\xa4zi\xb1C\x07S\x0e\xc8\xc0\xfd^\x9f\x01\xe9\xd7\x01V\x93w\x15\xfd~\xec\xfd\xde.\x82\xd6,!\xa7\x00\xee!p\x16$\xeb(\x07\xf6\xd1f\xd3]y\xd7\xcf.sX_\xc0\x04\x80\xbd\x19\x939\xba\xa7\x90X\xc0\x0f\xe8\x8e\xa3\x88\x92m\xb9k\x9a\x10i\xef@\x17\xb9\x1du>\xdeE\xa2\xa2\x12>\x99/#9\x97\xf5f\xe8\xc4\xd1$H^y\xafl\x8c\xfb\xcf\xd2x \x96\xa40\x82W\x18\xc3\x153H\x0d\xd8\x9e\x92\x07\xc6\xcb\xc9l\xfd\xe4\xe8\x02\xd9]\xb1 v\x89\x0b~y\x81\x03L\x9dBe\x1f\xbb\xc8?_&\xb9\x8eDv\x04\xb9\xd1\xb8\x83\xbf^\xd3\xc6\x13x\x8c\xa5\x1f\x83\x17\xce\xe01/\xfe\x18|\xe6\xe2sA K\xd0]\xfc\x92\xa4\x0b\x12W\xb5\xe5|\x19\xcbazr\xd1\xc8:?\x17\xd1\x19\xce\xcf-\x16\xaf>\xec\xce\xa3\x18\x9dp \x0cYf)\xcf.B\xe3\x93\xfc[X\x0c#\xe24\x9f]\x0c\xcbh\xd5 s\xd7\n\xa8\x8c\xd1(A\x87c\x82q]R\x1e\xa8\xddW\xee\x13\xb1T\xce\xe7\xe7\xeb8\x9a\x07K\x12\x9f\x9f\x03\x8f\x14^@0$\xa6\xdf\xcd\xd63/%/\xc2+\xbcJ\x9d\x87\x9fx\x90\xbd\xd3\x88\x93\xbb\xba\\\xbcBU+\x89Y\x17A8S\xb1TS\x90.\x95\x8a\xb6r\xe2\xff\xd2\xc3\xa4x(y[\xf1u\x7f\x99\xbc\x08\xb3\x15\x89\xbd\x8b%i\xa2\x07\x9b%j\xd0\xde\x84\xa2\x934g7\xd3\n\xbc\x1f\x18\xe27\xacK\xa5vk\x0ew\xc5n\n\xec\x90\xa58\xf3\xf9q\xdf\xb3)\xae\xa1Ux\xdeM\xa28\xb5\xb5\x04v\x8d\xa9W\x11\xf9\xd7\xb8\xdc\xc3\"\xfbL\x83\xc6}>N\xa7\xc8\xcf\x99\xc4\xed\xd2\x01\xca\x93e<\x88\xf1\xde'\xecE\x96R\xf8T\xd4\xe3\xbb\xb0t!\x1c\xa7S\x17R\x91gD{\xa3\xdctX}\x10\\\xde;\xacRR!\x81\xea\xf3E\x1c\xe9\xd3E\xec\x1d\xf5\x9d\xee\x8a\xa4\x8bh\x96\xe8(\xed\x9e\xf2\x1eg\xd6\xc7\xba\x04\xd3\x9a\xbd\x80g\xc2r\xc9\xf9\xa6\xbbfYl\x0cff,?\x96\x1c\x14J\x89\x1d\x94\xf0\x9d\x0b\x94\x81\xa3J\xcc\x80\x19B\xc9*hL\xdd\xa5?H\xa1o\xb7\x0bW.\xdc\xb8p\xe9\xc2\xca\x85s\x17.\\x\xe7\xc2\xb5\x0bg.\xbcp\xe1\x99\x0b\xaf]\xf8\xc2\x85\xb7.\x86\xb1Z\xe2\xe9KO\xf0\xaf\x98T\xdc\xe2\x020%\xe5\x9cw\xe7\xbai\xc6\xabS\x89\x9eK25\xc5\xfb3\xcct*\x831\xb8\xd3\x08\xce\xba\x97$e\xd1\x87\xcf\xba \xfd\xba\xc2\xaf\xcc\xac\xe1b\x94\xce3f>q\xdcB+\xd3\x8dI\x12-\xafH\xcc\x82\xcc\xbe\xe5\x9c%\x87\xd2=\xfd\x05\x8f\xbc\x144\x04a\xe1\xfc\x97\xfbU\xe5\x04D\xa5\x1e\x94\x1fcp3\xb4\xd6\xbf\xb5#\xa7\xe8\xd2\x88\xf1\xe8\x1b\n\xa4Et\\\xf2%]\xad\xfc\x1c\xfe\x82\x16\xcb\xb8W\xf2%I-\xdc\xb4\x11\xf3\xc5s\\x\xa9\x8dhO\xfb\xc0\xd2\xf2a\x94\xe4\xc2\xfbp\x9e\x93\x13v\x86\x8f\xc6\xbd)\xeaQ\xaap\xd1\xe7\x11\xcb}c\xd6\x08iF&D\x8b\xd8\xb6\x9e\x07\xb1\x9f-\xbd\x18\x82\xf0*\xe2\xaa\x1c\x17\xac\xe7/?{\xfe\x83O\x9e}v\xfe\xf2\xd5O\xbd~\xfe\xec\xcd\xcb\xd7\xafLVwZ\xeb\xa5\xad\x89_\xfe\xbe\x08i]3\x8d\x0f\xd4\x13\xbe\x1a/\x99=2p\xe1\x99\xbc.\x89X\x17n\xc1\xa7bH\x99|\xbap\xe5\xe4y\x07\xe9\xfe\xa8\xd5\xb6\xe1\xe1Y\xbf\xaa\x86\xa1\xb2{\x02\xb5h#\xae\x12\xe4\xa8[\xe0\x90\xc1\xa5\x10\x8dm\xba\xa0\xc9\xa7\n\xbe\x14\n3\x18V\x90\xccqMh\x9ew\xfa\x81\x17\x89\xf9\x03\xa0\xbf\xb0f\x99\xf2\xfb\xe3\xb8VD\xcdu.\xa7\xfa\x7fXR \xdf\xefD\x8e\xc7\xf5\xc4\xb8\x0b\x8d\xd3\x14\xd4.kP\xa6\x06\xba\xcc]\xb8M\xefK\x0dj:\xf7\xc0\xcb7\x0e\xe8\x1e\x0b\xb5\x8b\x17\x88u\xa3\xe2\x97\xe2\xae\x9bi-\xffQ\x1c\\\x06\xa1\xb7\xd4Z\xfb\x85\xb0>\x84/\xd4\x87\\\xd2\x7f\x85\x91\x83\x90\xdb\x8b\x9fj\xd9K\x92nr\x0d\x94\x0f\xf2m.\xe7\xbd\xb5S\x07\xb9\xdc)\xdc\xb0@\x0f\x1c)R\xba\x18*\xd5S[^x\xc9\x16-\x1b\xd6Q\xe3\xda\xa3i\x8a\xf1\xdbMZ3\x900`\xfd\xd5\xf7\x00\xe7\x04\xfd{W\xccM\nF\xf0\x12EU\xee\xbe\xc0~\xbc\x96\xd1\x82=\xb1P\x9a%\xba Q\xea PL\xd8 #\x8fP\xac\xbc\xd4\x0f\x03\xcf\x83\xe7\xf4\xc8'\x89Fn\xde1l\xc5\xdatb\xa3R2\x9f\x9aK9B\x9dC7\x7f\xae\x0ey\x81F\x0f\xccI&\x83\x9f\xe5`>K\x85\x1b\x95\xfdZD\xf1X\x94T\xfa\xfa\xb8\x15j\x7f\xe9\x18\x870S\x1f\xe4g\xe1\x0d&8e\x92-\xdf\x9ej\xb3\xd5\xed}\xa1\x8aj\xe6{,n9\x87\x8e\xba\x86l\x0b\x86\xb8\x05\xc3\xb2\x8cFP\x92 \x99\x8c\x96q)\xb3j7\xde\x92\xa7\xe7\x8an^\x1bg~\xe5*\xa1iki\xc8G\xc1T\x18\x17\xc9[\xa8\xa6=w1\n}P\xefF\x8cH\xdf8w\xbc\x1b\xc5\xd09\xcf\x1d\n~'Mk\xcaW\x8dNhA\xddB\xd6Y\xba\xa3U\xbd\xcb\xf5\xb7\xd6\xcf\xac\xbb\xf0\x121\xf7\xda\xee\x16XP\xd3q\x8e\x18\xb4\xaeT\x93pum\x7f\xa1\x0b\x8c*\xeb\xbe\x86\x10a\xd8*#\x89\x8d\xec\x0b\xcdSN\xbb\";\x13\xa7\x1d\xb5\x15\xe4D\x91\xfdN\xf7\x0cyEd_\xab}\xcer\xc8\x83\x9c\xf0\xfb\xc7\xba\xfc}\xf4\xe4\xaf?\xe1\x0ft'|\xd4Kv}o\x9df19K=\xff\xed\x9b\xd8\xf3%\xb6B\xe48\x1d\x8d\xf6\xa8\x90;#2u\xa7.\xf7\x98\x07\xe5\xfc\x1fj\x89\xa4\xa2c\xd2\x9e\x85#;\xe1\xa1\xb6<\xc6\xd4x4R\x91\xb8\x1f\xed1\x89\xc8\x14\xc9n\xe1F\xa2l\xd8\xf5\xa3\x19\x8a\xddxO\x87\"\x1a-CJ\x02\xcf=\xd6hs\xa3\x02\xe3\xc0\\I\xc1\xe2\x84ln[`\xb1l\x88\xad\x8f\x882\x8f\xa2!X\xb1\xf7\xa5U\xa5Qj\xd9\x0b\x8a\xf1\xd6\xec\x9d\xb7A\xd94\xfe\xf2f\x08\x16\xfdS\x0d-\xecb\x80\x9a\x08s\xb7]x1\xcb\xe1\x16\x7fy\x83\xb4\x81ve\xf6\xce\xc3\xf7\x1eXo\xbbgH\x8d\xaaU\xdc\xa2\x11g\xe5]o\xa0\xd41\x18\x08\x8a[8\x91\xe2o\xeb\xc2\xa0\"w\xa3\xa3n*+:Q\x1a-yhk5\x8df\x17\x9et\x1cS\xf9\x9d\x8cc\x8d\xabi\xa3\xbfN\xc8\x02\x15\xd0}\xdd\xe8{\xc1\x04\xfe\xfe d\xf0\x04\x92\x13h\xb73v\x7f\xad\xd8\xa0\xd9\xd4\xc5\x80\xb7yh\xa2jv\x82J\x1c\xb407\x8bh1\xfd\xdb0\x1c\x1e\xee3\xc3\xa1\xa4ag\xa6\xc3\xc3\x83o\xdbt\xa8_D>V9\xae\xac\x95\xdb\xd4-\x8c\xb4X^\x87\xdaE\xd5;`=\xb0>Y\xe1\x1eA\xd9d\xd1\xb4\x9d\xaa\x1d\x17\xe6f\x8c\x84\x9b\xaf\x0d;\x9em\xebzr\xa7\xbek(&oB\x1fR\x9d]A\x1b*Ks\xc7\x81\xe3\xb0\x1f=\x82`,\xec\x12\x98\xbe\xa1\xf5 f\xd6*\xfe\x1f3\xfc\xe7w\xe5J\x17nS/\x08\xf9n8\xea\xddc7\x88\xd9\x96\xc9\xfc\x96{\xa5\x8e\xd7\xc5E_1\xe7\x88\x08\x17\"\xa06r/\x91\x9d\xbb\xfal\x1eE\xd6\xc3\x18\xda\xc50\x95\xa9\xe4wa\xee\x8a\x0d\x95#b\xc9\xb6\\NDy\xdf\xceW\xee\x92\xba\"\x18\xbb\xc6\x04\xb4\xd4[E\xd7\x1b[r\x16\x9bZrf\xf5\x96\x9c+\x83%\xa7\xd2\xdc\xcd\xa6\x06\x9fK\x9dE\xb5\xac4)\xbf\xb0\xd2\x12\x0c?\n\xe7\xc1e\x86\xb6W=\xd1 \xb9mV\x1f\xf5Z\x04I\xaa#+j\x9akJ\xa2\xe2&a\x05\x84\xc0b<\xb3-\xd1\xa5\xe1RF=\xeb\xfc\x9c\x10t\x1b8\x95b\xcb!\x8c\x1e\xe5(h\xd5\xc5\xbc\xe70\x82\x99P\xc8\\U\xdeva\xe5\xb8RA^,\x1c\xa7S8\xd5\xc5[\xe7O\xe8\x1f\x16\xac\x0d=O\x11:\x821\xb3\xa5\x92i\x01\xe2\x91:\xca3V\x11\xf5B\x9f\x0c\x91\xd0o6K\xae\x1c\x0eL|J\x13\x15\x88\x88|\xcan\x0d7\xb9\x9f\xc8\x8d\xd4\x01{\x03\xaf\x91 \x97\x8df\x8fX\x8c\xadCg\xf7u\xe8\xe7\xf1|\xce\xcf7\x9c\x8a\xf9|\x88\xa2\xef\xa63\xc1i\x84^\xcd\xcd&\xa3\xa5G\x9bR,\x05\xfd\xfb-\xbb\x82X\xce8\x9dn\xf0\x9e\x8a6,\xb6(}[\x9d1\x10\x92w\xc4n\xbe\xd1\xc5\x8b\xc7\xd1\x94\x8a\xb0\x91\x03A\x11\x927\xd0\xcd+{J\xe5\xe4\x81\x88K%4\xfa\x1c\x05\xe3q\xc4]\xe40ie\xdcM\xd6x\xeb1r\xa1\xaf\xbb\xb7\x87\x96\xb4\xb8h6\xaem\x96kc\xc3:\xcf\xf8\xa6eg\n\xc4\xac\xf1~\xe2U\x1e\xd1\xa2v\xdd\x0dt\x82r\xe3\xa0\xbc\xa0\xe6\x15\xd1\xafc}\x1cx\\\xc5Pc#c\xb6!9\xd5\n\xbb\xebH\xd8\x89\x85\xc0\x13\x08\xe9r\x13\x07\xa21\xa1\x0f\xcb\x17\x1dI\xcd%8l4\xc0\xe0\x15\xec2+\xaf\xb7w\x82\x847\xa0/\xb3\xaa\xf9.\x8e\x0bC\x8e\xb6RnJ\x15\xb7\xc9\xaac\xa9\x9b\x80Mnl-\n\xe2\xb2\x08\x92\x86{F\x0d\xf7\x8a6\xb9\x89Un\xaf\"\xaf\xdc\xbf\xf5\x86\x9bVu\xad\xbb%\xdd\xd1\xfd\xfa\xb2\xd1\x8d\xaa\xbf\x14\xfc\xa4\x9fue\x16L\x98\xf7\x1d\xfd\xaf\xf7\xba@\xcch$\xb1\xab:O\xc6K\xe7vP\x85S\xc62\xb7#GGx\xe6\xb6\xec\x0b\xcd\xbc\x08o\xec\xaf\xde3]\x9c,\x1d\xd7_\xa1\x16\xaeb\xccU\x02\xad.3\xdbgq\x88\xf3C#\xadTn\x8c\x08\x9f%:\xa3\xdf\x81\xfb\n\xcc\xdc\xd5\xa9\xea\xd3_\xa3W\xd5\x88\xcd^\x9e\x9b\xb0\x12\x99\xb8h\xaf>p\x80D\xf7+i\xb05\xdeG\xd2\x0b\xe8,d\xa7\xe3\x10-\xcf\xf4o\x19%\x1c\x91\xf4\xce+\x19\xa5\xd5\xeb\xfb\xef\xdd\xedN5\xa8\xf6B}\xd7\x86iy\"~(\xce\x14\xcb\x8aC\xa5\xae\x8b ,\xc5]\xb9\xefQ\x88\xadS\xffX\xa3\x1d(%\x94\xbb\xe3\xa1.`\x9a\x8d\x94\x8a\x07\x0f\xd4\xed\x8d\xce\xd1B\xb3\xcc\x04S6\x92y\x1cUrq\xd5\x9d\xb6Y\xe8v\x14\xddq\x0d\xc7\xa8Gv\x99\x8ax\xea\xb8\xf0\xbd(Z\x12/\xb4Q\x94!E\xb8e,\xc0LA\xe8\x15\xfd\x10c\x96\xf4\xbcG\x07N7HI\xec\xa5\x91>\x90\xe3\xb1\xde}|O\xb9\xcd\xc5\xf6\xe8\xa0\xba\xa3=\xfd\xd6M\xf4\xead_\xbf\xff\xe7\xbc\xcdj\xe5\xcb*^mt\xacV\x0f\xcb\x8b\x878\x8cj\x9e\xcb\x87Q\xf5)\x1e\xe64\xf1\x17\xdf\x1bO\xf2\xe5\xa3\xfa\xb6\x9b\xa8\x10K\x8d\x1e\x94\x8d\xa6\xa4\x17\xb5\xa6$\x0c\xb2T(\xe6\x13\xa6\x98\xf7\xed3\xa4A\x9e}\xc6\x83#\x02\x8f\x16\x8eh\x8e\x0bG!\x11\x0b\xf6\xec\xe4q\xf2\xca\x95\x1bb1\xe0 \xe8\xcc$\xee\xa1S!\xde\xa0\xe1\xbb\x93y{\xda\x97P\xc4\xe9\xa7$\x85a\x11\xbf\xb9\xcdo\xeb\xd1\xf3\xb9}S\x928\xfa\x0e&+\x1bA\x8a\x17\xd1o\x0c\xd2\x10;\xd5\xd1V\x1b\xa4\xf0r\xed\xa5N\x95B\x8c\\R\xb1&t\xe0\x86\xf9\xf2\xa5Z\x07J\xf1\xe1#5$\x0cU\xa0*\xe4\x06\xb3\x05~\xc7\\\x08\xe7|\xa9\x98\x91A\xb5M\xd8\xef\xb0\xbb\xf1\xd48\x178\x0f\xe7\xe8\xe5\xfa\x8e_Ge~4\x94`\x8a\xf9\xa1\x07\xe4\x0b\x18\xc19\x06\x16\xb3\x8b\xc9i]tgQHN\x1c\xb4\xbf\x9f\xc1\xa9\x10\xe2\x983\xf0\x05\xd3\x98p7\xf6\xfc\x17\xe5\xdf\xf6\"\xd7\xa6\\\xbb0\xb3opg,\xf0\xae\x15\x9f\xe6\xebj\xa3\xed\xb6!a\x16]9Mv\xa0\xc2\xdbs^\x83\x0d8\x03\xf2\xda\xebF\x8f\xe3uQoW\xc1\x89k\x8e\x10\xbfz7\xa4\x82]#\x05\xbb*\xc7\x92\x1c\xa9\xb6\xc0\xa2\xd8vx0\xdb:\x9bt\xd5\xd8\x0c| f\x8c\x07\xd8\xb3\xa2\xfbn\x8d\xccW\x89\xb0\x1b3\n8\x1b\xa7,\xcb\x1f\xcb\x9e<=q\xa0\xdd\x8e\xb5\xd4\x0b\x8b\x8e\x80\x17\x9d\x8a\x9c\xab\xf6\x9a\xa9]\xac\xef~\x17\x03\xab\xb9\xe0u/\x13.:\xd5\x1fI\x0bo V\x13\xd3\xb5\x10\x17<&.\xe2\x93~\xf5\xb4Zry\x97\x83\xd8F\xb52/J\xa4J\xc4\x08}y\xfa\xf9\xf9\x8c\xb00\x94A\x14\x9e\x9f\x0f\xc1\xc3\xd0\xa2D\xe7\xccw\x1ez+R\x94\xb9\xb2\xab\x0e\xd0\xef\xcb\xea\x91\xb9\x1dT\x9b\x9cG1}\xbd\x1e\xcb\xf8\xa0\x17\xcc\x0e\x86\x7f\x86\xec\xcf\x08\x02;'\xe8\x8aR\xa4\xf4\xfb-\xb9\xf9x\x93\xc6\x0c\x8e\xe3\xb8\xf9\x08\x04!$(\xd3.\xcc:\xfc\xc5\x98L\x99\xa7s\xce\xc1Hm\xd7\x16^\xf2\x92c\x89\x98\xcb\x98YA\xa4'\xcc\x9f\xcf\x92 J\xaa\xf4 y\x8e\xaa\xaa\xb3\xb5H\xf6R\xa9N-\xc0kU\x1f\xa8\x95s6V\xad\x92\x83EE\xfc\xa7\xf2\xfa\x8a\x92\xc3\xca\xbb\x08\xe3/\xe2w\xe5-\x9e\x13\xa9\xf2\x9e\xc8\x9a\xc4\xde\xe4\xbf\x94w\x13\xe2\xc5J\x93\x0c\xc8\xdfd?\xd4\x17\xd7\xc4\x0fHR}\x93A\xc5\xab\xec\x97\xe6\xdde\x90*o.\x834\x7fo\x19\xa4\xca[\x92\x08PyWz\xc2k\x90 \x9azrAA\xa9'\x7f\x92\xd7\x93C\x94z\xb20\xf1\xa35E\x83\xea,HOx=\x12\xa4\xe4E\x82$F\xa2J\xd5\x9d/\x119\xdaFU{.\xba'\xda\xaf\xb5 \xcb\xba_A\x95*;\xae\xd2\xb1\xc0\xdc1\xb9\xe5MZ\x15\xe4\xdb\xc6\xec\xedL\xef\xd1\xad\x90Qh\x83\xe5(\x0e\xa1\xa5\xdfx\xa4x=\xdf\xb4\xd5\xa4\x92M\x0b\xd4Q.\xcb\xa3\x0cddr\x9b\xa6U\\>\xe1\xed\xe8\xb5\xa3\\\xee\xae\xe4\x86\xc7\xe0\x189\xc6\xd9r\xa7\xf4\xbd\xca\x11\x11{\xe5[\xae\x98S\x8b\xbd\x105\xbf\x10\x94\xe2\xf0\x97\x04f}\x15\xe5\x99\xd0UQH\xe5\xf7\x89\xa5%\xe9g\x8f{[G1b!\xcfP\xdf\xa0\x93\x1cR\x8c\xea\x9f\xcb\x0d\xfac\x90\xd8\x1c\xc52\xdc}4\x9b\xf5:?\n\xb1\xab>Z4\xb9\xbd\xa5\xcf\xe54\x05\xac\xecY^\x16#\x98V\xb3\x18\x9e\xf2\x8b{\xb4\x1d~'\x8ecj\x87\x87\xfe\xb0\xa3b\xd1=\\\xf4\x80\xa2=\xf3\x93\xc5X&\xe3\x1e\xf7q\xc7\x07\xf4E\x17\xbcq\x9f\x03\xbf\xc5\xae\xe7}\xefO\xc7\x11\xe2xvr\xaf~;\xae\xa8\x8c-\xe0\x1d\xf0\x97k8\xb5\x99\x16\xd5\xa1n\x17\x1b\x83\x07\x8f\xa9\xc1\xe4\xac\x1e\x93=\xee^^\x8f\xebyn>c)\x1f\xd9\xc1\x06{\x81\x0b[\x19\xc5.\xf3f\xa0\xaf`\x1a\xc0q\xb2 =\x8d$,\xdd\x9c\x9eJ\xd2\x7f\x86\xe8\xe0\x8d#\x89\x9e\xd6\x93R\x9f!J\xc6\xe24\xb1\xbe\xf6\xa7\xe3\x00\x91.\xba\x03a}\x90\x9e\xe5\x17q\xf3\xce\xd0\xf7\x85\xdf~\xe0\"B\xd3g%\xd0 \xb4\xb0\x18\xb7\x7f?z\x04\xbe n\x0e2\\\xbf\xbb\x8e\xd6\xb6\xe3\xb2E\xe1\xbf\x9c\x0dj\xdeb\xbbH\xd7\x016\xd9'\x9b\x86_\xe1r\x8a,\x97\xa8\xd5\x7fG\xff\xeb\x1eRY\xc5\xf0\x7f\xcco'\xb2\x90\xb4]\x0ci\xc7\x83:\xdf\xe7B\xe2VB\x9c\xdc\xf66G9\xb4w\xa7\xf6W\xef\x91P\xa6\xf6+\xef\x15\xbb\x83\x98\x16I\x1e\xe0\xe1fk\x03\xa9\xbf5z\x18=XYt\xbe\xe3\xb4n)\x1bW\x89\xe4C\x88\xc5\x12\xb9 .:\xc2\x19\xbc\xe0\xca\xc2[PHi\xe18\xd8h\xd7\x95\x85\xac\xa6\xe0\xa1,_6K\xac\xe3B\xc8~\xb5\xdb\xa9\xf3\xed\xf0BIc\x85\xf9\xa3\x90\xf1\xb7p\xa0\xec\x0c_&Va\xe9\xb7\x86*<\x0c\xd1\xd1\xc8+\xdf\x02\xbdy\xc8S\xa0^\xc9\xa0G\xf5\xd0(\x8a\x9a\xe48\xcd|hJF\xf7\n\xc7\x15\xcd\xe09\x82\xb8\x10\xa1\x7f\x01ECM\xd8\xe4\x0dh\xe1F\x18\xce\x8e\xb9L\xcag\x83\xa5d\xc9G5\x00\xe1\xc7\xbb;\xe3<;C\xf9x\x86j\x16M\x136#\x9e\xcb\xf3~\xf3S\x1aC\xfel\x0b\xe4\xe7\xbdi\xd5\xf6\xa6\xe1\xc8@\xe4\xe6=U\x90\xf54\"\xb2W\x16\x91\x93\xb2\x88\x9c\xe4\"\xb2W\xfc\xd2\x88\xc8j\xcd\xc6\x9er\x89\x98\xae\xd4\x86\xd3s\x0f\x96e&\xe4p\xc7\xed\xe5\xcaD\\\xed\xeaw\xf4\xbf\x1e\x86\x07j\xef;\x85v\xff\xb8\n\x8f8\xfcH\x7f\xbfM $..\xcfT\xef\xe0$\xa6\x8bo\xe5b\xdb\x05\x0870mL\x15\xc1\x93\x184\\x\xe7J\xd3\xa5\x0bk\x17\xfd+\xe7\xdcAQ\xa5/u\x0f\xaf\xd0\xba!\xc2\xce\xa9\xcfo\xf0\xb9\x08\xc1X\xc6\xe8\xe2=\xf4\x08\xaf\x97\xe5\x84\xa4QD\x17\xd6\xe2V\x8c\x91\xa1DJ\x07\xbcVj\xd4\xd4\xebC\xad\x80\x88\xd7\x1737\xbb$\x17\x9f{.t\xfa\x945\\\xf1\xcb'\xcb<&\xc2\x9a6\xab\xda\x9c6rX\x8eli\x02\xe1\xaa\xc6o\xf9}e\xfa\xa2P\x04\xe9m\x9e\xbb\xda\xdb\xed\xda\xfb\x93\x90\xbb\xbbI\x11\n\xb4s&;\xee\x8d`\xbc\xc0\x88\x15\xa1p\xe2c\xd4=t\x98\x0d\x0e\xa7V#\xbd\x89O\xcc\x18\x12\xdd\x95KF'\xd6LZ^b\x96|\xe1\x92\xdf\xe0D#>(\x7f\x98\xe9\xa8.R\xec\x8c'4@~=c\xc17\x8a\x80\xc8\xb8\xb7X4\xd8\x88\xf1+\x1e\xcb8\xc6T\nQ\x98\x92\xeb\x14\xf30\xc5\x97\x89\x93\xfbo\xc6,yD\xc00%*P\x88\xae\x89)Et#id\x99\xbe\xf9\xdej\x8a\xc2q\xc5\xeeEr\x9fp\xe3\xa6\x08\xe9\xd0\xd3rV-\x1e\xfeCT\x0f\xa9\x19a\x84\xfc\xccD\x8a\xb4\x1b\xcc\xcc\x9a?\x1e \x13jS\xf9\xd3\x82\x9c\xdd\xd1\xdaXO\x16\xe3\xa4\x08\xda\xcb~\x04\x85MF\xe9>\xbf3\x86X\xa1\xf4\x8a\xffX\xe2\x8f\x9cq\xc5\xdb\xf5e\x81\x0eZZ\x94\xc6\x1b 6-\xc0\x88\x8e\xc3\xa9\x0es*^8\x90u\xe9\xcf\x0dD\xa1\xc4\x9esa\x85\x8b\x14Z \xa5qJ\x12{\xad\xe3\x0fj\xefs\x1a\xc2\xa8\xa2\xe8\xaf\xf9x\xa6\xbd`\x9b\xe1M\xfb\x0d6\xc5g$\x8d\x03rE\n\x8a3\x8b\x08#D\xc1j\xbd$T(\x12h(\x90\xf8\xb1\x96*\x89\x0fk\xda\x9e\xbb\xa0\x1bqe|9\xb5\xff\xafq\x9c\xe5\xcdj\x1aoM\xdf\xf8\xfb\x0f\xd6\xbd\xbc?\xdb\xf5P\xac\x08\xe6n\xe0oh\xd1\xb1\x04)\x04\xaf\xaa\x8a\x81\x85\xca3q\x1a\x93\x8a\x01\xf9`\xbb\xad\x0f\xeaW\xe3\xe7D\x19\xc0R\xfb\x12\x88\x03\xfe\xa64I\x7f\x8e\xc7\xc1\xe8\xe9\x8e\xbeM\xcf\x8e\x1c\x93\x8c\x1f\xe1\\cVF\x9ct\x84x\xb3\x03I\x1elH\xf2\x7f\xd5\xefa\xe9\"\x1asj*\xee\x84y\xccO\xb1\xd5\xe9x\xe2\xe4R:\xac\xb4z\x98\x9fP{]L\xc3\xbf.I\xfa\x19G\xd0\x1f\xd38z\xc5 <\x16LV\xb3\xfd\xef\xa7\xd4\x92\xd2\x0f\xe96X\xe8B%DsXD\xecm\xf1\x88\xbd\x04\x86\"\xa5b#s@\xaf\xb2\xee\xf3\xb33\xba\x1c\xf8\xa5K\x12\xdf[\x17\xfaT\x19\xa8N\x95`,\xcd,H\xc4dP2z\x19\xbc\xd8\xfef\xd1\xec\xdf\x84\x98\xfcl\x16\xc4$\x01\xaf\x08}g\xf4X*\xc5\xbb\x96\x82L\xf1\x10La\x9ea\x81\x12\xcfN\x9f\x1d\x83)ya\xa2t)[\xc2 \xb4\xdb\x01<\x81\xf8\xc4\xc1\x19\xe6\xf9{\xe4B\x01\xde{\x8c\xa0Mg\xff\xe9\x08\xfa(\x05S\x01d\xb7\x8ftgp\x08\"\x03!N@\xc0\n<\x1d\xc1\xdeQ^v\xff\x10\xcb\xd6=\x7f\xf4\x08\xf6\xf6i\x81\x8c\x12\xc6\xc9\x04\x83F\x15\x96\x89\xfe\x01Zr\x80\x12K\x1b\xfb\x1a\xb0*[\xfdJ\xd8\x01\x82uup\xc4\x1f\x88\x0e\x1e\x17_\xf5=D\xe8\xc1~\x0e=\xee\xe5\xd0\xe3\xc3\x1c\xda\x1f\x0c\xf02(\xce\x13\xce\x11\xa5\xe0\xac\xcbe \xce\x9b\xf5\xff\xfe\xc5\x9fY\xb5\xfbPuz\xd78Q\xc8\x18\x8b\x1a\x18\xf6\x0dO\xdan \x91Y\x8a\xcfJt\xe5r\xec\xeeX\xd6\x1b\xbew\xf2\xdb:\xa1\xdd\xef\xdf'\xb0\xa76p=\xad\xd8:?'\xc9\xa7\xd1,[\x12\xabJ\xb5y\x9a 9\x8d\x82\xc3T=\x98K\xaf\xceQ\xc5x}9I\xbd\x94|\x7f\x99]\x06a24l\xdadM|\xd33\xfa\xf1\xb0\xcdd\x08\x99Y\xc8O\xc8\x92\xf8i\x14'C0\x04c\xd2\xbf\xcbR/\x19\xbb\x068\xb6Y\xe6\x13Zs\"\xa6\xc2\xdc\x8f\xbc\xaf\xd1F}\xf5\xf4}U\xf1\xf0;\xfa_\xefU\xf9mn\x87\xf6~\xffX\x89\x90\xcd\xed\x0c:\xbb\x84o\xd3'{J\xa0e\xfeh\x7f\xaf_}\xe4\xe5\x8f\x06J\x90i\xd1\x87\xbd]\xc79\xf9N\xfeL\xe0\x0e\xf8z\xc5O\xca\x98C\x81\x9f\x05s8\xa9\xa0)\xe3\x06_U6\xa7|+G\xa3\x10\x93b\xe6\x05!=\xb65\x1c\xac\x0bC\x1d\xa7eEF$\x93\x19\xbc\xd8(i\xd9\x8fC\x9d\x84\xb9\xd1\xbdB\x99\x07\x1e\xb4X'a\xb1\x1c\x97\xd5 \x93\xdfQ\xbf\xd1q/\x95[B\x97$\xfd$\xf2\xbd\xe5s\xdc\x04\x9b\xc5\xfa\xb3{\x18\x8c\xd8\x8b\x13\xf2\xd3\xde\x8a\xbf\xea\xd8\xb1\x18\xfcv^\x0erC2]|\xdc\xe9t&a\x16/\x87`-\xd2t\x9d\x0cwv\xd6$M\xd2(&\xdd\xe4\x9dwyI\xe2n\x10\xed\\\x0dv\xc4\xaf/\x92(\xb4&\xe1,Z\x9d\x07\xb3!X\x7f\x85?\xe8d\x815 \xd11\xddK\xa3\xf8\x07\xa5:\xa3p\x19\x84\xe5\x1aEAk\x12F^\x96.\x06\x9f\x91Y\x10\x13?-\xde\x1c\xee\xec,\xe9\xbc-\xa2$\x1d\xee\x0ez\xbd\x1dV\xb2\x13\xf3\xa2\xddE\xbaZZ\x93\xf0\xb1v\xd0\x1bQp\xc9\xb5c\xd07hR\xe3\x87\xa9^\x7f\xdc\xdb\xdf\xebi\xb7od\xc4\xdcZ\xf4Q\xbcH\x85\xb5\x120\xfe\xa6\x88\x15=#\xeb\x98\xf8^Jf\xe0\x853\xc9\x91&K\xc8\xac\xdb\xe0C\x03\xf2\xfct\xa9\x98\x87#\xe9\xc9IK\xbbg\xfe\x82\xac\x98uu\xf7\xa8\xf4\xe4\xe3g/?9{\xf6\xf1\x8b\xf3\xb3\xe7\x7f\xed\xc5\xa7\xcf\xb8\xc1vP*\xf3\x93g\xaf_\xc9\xcf\x07\xbd\xdd\xd2\xf3\xe7\xaf?{Q~^~\xff\xa3\x17\x1f?\xfb\xc1'o\xce\xab\xed\xec\xefj\x8b}\xfc\x83O>\x91\x8b\x1d\x95\x8b-#o\x86\xa1\x02\xe8\x97\xea\x83g\xf4P\xc1\x9f=c\x17\xce\xc4\xe3\xc4\x9b\x93O\xc4\xbb\xe2\x87\xae\x80\xa8C\xfa-\x17\x9be\xab5\xc6\x0c\xa4_\xaa\xef\x7f$\x1e\x8a\x1fr\x81\x9f~\xf6\xe9'/\xae}\x82!\xe89\x1e\x96\x86\xf6\xe9\xcbW/?}\xf6I\xddZl8\x87\xe6\xe9K|/D\xd5\x81E\xbfY\xa5gH\xe1\xd8C\xfcZ~\xeaG+\xee{\x12\xd9\x16\xffQ.\xe1\xcdf\xcf\xa5\xf0\xe1X\xb0\x0c\xb3\xee!\xdfI\xfe}\xd5\xab\xfcA>\x9b%0\xbfD\xa5h\xa0\xb3|\xeaJ`/\x9f\xaf\x128iVH\x97_\xf0U\x85\xf2\x1cF0(\x83(\x92\xed\x96A\x14u\xf6\xca\xa0\x85Z\xd7L\xad\xebJ\xad\xeb\x86\xb9\xc2]\xf7z\x9d\xc9u\xefhr\xdd\xfb\xde\xe4\xba\xf7|r\xdd{\xd1\x99\\\xf7?\x9e\\\x1f~\xdc\x99\\\x1f\xedM\xae\x8f\x0e:\x93\xeb\xe3\x8f'\xd9\xc7\x1f\x7f\xfc\x02\xff\xffxz;\x9ed\x1f\x1d\xd1\x97\xb3\x8f\xbe\xf7\xf1\xc7S\xfb\xb4E!\xcf\x19\x84\x96pn\xed\xd3\xe1\xf8\xf3r\xb1\xdb\xcf\x9dJ\xb1\x9dr\xb7.y\xb7\x8e\xf6\xcb\x1ez\xe5R+,\xe5N\xc6\x93\xe9\xe4\xab\xc9\xfb\xea\xe3s\xfa\xf8s\xfbt\xd8\xbam\xb5n[c\xaf\xf3\xe5\xa43m\xb7\x9c\x0fv\x82r\xc9\x8b\xa2\xe4\xf8\xf3\xa2>\xc7>\x1d\xfe\xc4\xb8\xd79\xf6:\xf3\xe9W\x83\xf7\xb7\xec\xfb\x97\x93\xce_9\x99\xecLN\x87\xdf}4\x9a\xb4'\x1f\xb8\xe7\x93n\xeb\x7f\x98|\xf8xbO\x1c\xfa\xf6\xd4\xf9\xf0\x83\x9d@\xc7\"\xde\x19YD\x9f_B\xc33\xe3.\xfb.\x11q\xb5\xaakcU\xc7EM\xbb\x83\x0dj:\xdb\xa6&\xec\xdf\xb6}}alao\xaf\xa8\xea\xb8/}\xdf\x95\x9a\x18\x94~\xeco\xd0\xe03\x83yG+\x9e\xee\x1d\xa1\xb9\x02\xa5K~\xd2>\xc5 9{G0\xa4\xc7\xea'\\\xef\xb0;\x80[`\xc9\x9c\xd91\xbb7@}O\x87\x16j\xd3i\x19B\xa7_\xdb\xb1\xd7\xe6\x998\xca\x15]\xd6\xa4g\xb1\x96s\xc8\x7f\x87\x00\xb9\xc8\x05\x85\xf4\xfb\x07\x12(\xc5BU@?_.\n\n\x19H\xae\xe9\nA\xbd\x81\x04\x9a\xb3R{\x12(f\xa5\xfa\x05\xe8\xbf\xa7\x90]\xe95\xd4}\xec\x16/=\xb6\x1e\xc3\x10\xf6\xa4a\xec`\x0f\xe5\x96&\x14r(u\xe7\xff\xf9y,\xb3/A~\x13\xcb\xc8#E\xaa@\xa1G\xbd\n\xf4\x98)\xabk\x17\xe1\x8b\x9a#\xc6\x93\x11\x1c\xec\xef\xef\xee\xc3)W\\a\x96\xe9\xe7\\\xdfd\xa7\x85\x03j\xf9\x01K\xe9\xd9\xa6\xa7\xb5\x0e\xd6p\x00O\x9fB\x9fJX\xfb\x07\xbb\x83^\xf9\xd1#:\xdf\xbb\x8a\x11\x15\xe4\xd3\xd8[\x90\x13\xd3\x0e\xf6\x0f\x1c\x17^j`\x9f\xb2\x84r\x9f\xc2\x13\x18\xec\x1f\x9c\xc0\xa7\xed\xb6\x03o\xc7\x9f\xd23\xd9k\xfbS\x87\xc7\x19\xe8\xb9\xf0\xb2\x00\xea\x88\xd3\x1b\xad\x1e_hb\xc9;\x08P\x01C\xdeQI\xb7;\x0f\x96$\xf4V\x84\xb2\xf6 \\g)\xde\xdb\x8f\x92 \xc5;\x96i\x97\x9e\x1fd\x18t8\xf0,\xf5\xe2\xb2\x9b\xbc\xda\x97\xe7\xda\xbe0Q\x99\xf7\xb3\xf6\xfd\xef\xeb\xdf\xefF\xe1\x0f\xbd8\x0c\xc2Kv\x96\xcc\x7f\xf2\xeb\xea\xe8y\xca\xeb\xd7-\x0e]\x97\xcf\x94\xd3\"\x15\xd9\x86\x8d\x16\x1a\xf1\xbe1d\x0b?\xa2\x8f \xed^\x918\xa1\xc3x\xf4\x88\xcd\x845\xcb\xd6\xcb\xc0\xf7R~3\xf5'h\x93\xc0\x8eT\x98Q\xca\xe5\x91\x0fC)`\x15{\xb3\\\x12<\x9f\x8a\x96 \x90k\xcfO\xf1b*\xc9U\xba\xb4\x9a\\\xe3n\xc7\x8c+R\xa67m;\x93\xae\xf8\xf6\xc1N\x97\\\x13\xdf\x0e\xc7=\x1e\x03\x8d5\x14,\x97\x9dy\x14\xafdw\xffh\x0e\xe9\x82\x80\xda[*\x8b\xa1\xf4\xf82L\xedx\xdc\x9f\xbal\xafDe\xf8@\xc0\xa5\xb8\x8e\xac\xb5,d#\xc1lhX\xbf\x983\xde\xe6,\xf2\xf3A\x15\x13:\x82\x90E-\xef\xfa\x0b\xe2\xbf\xfd$\x08\xc9\xf7b\xe2\xbd\xa5\xe2[Dw\x90h\n\xef\xdc\x0e\x8a\xaf\xdf\xe7\xad&\xd9\x9a\x8a\xb1d\xd6\xd0hiu+*\xb67\xcf\xfe\xeav\xe8\xa2\xe2\xca\xc0\xb0\xdao\x9e\xfd\xd5\x9a\xc5N\xdfE\x85\xfe\xdf\x12\ny\x16\xd1\x0e\xbf\xd1u8\xef\xa6$I\xed\x18\x03@(K\x9bz\x97\xb0\xf0\xc2\xd9\x92\x80=\x0f\xe2$\xcd+t\xc4$\x94\xfa@[\xc9C*\xa4\xde\xe5\xa7\xde\xda\x85\xb8@\x9b\xc7\xe9\x82\xc4\x84\x1ep=X\xc7\xe4*\x88\xb2dy\x033\xe2/\xbd\x98\xcc \xc9\xe6\xf3\xe0\x1a\xa9\xa2\xf5\x18\xda\x10C\x1b\x1e[R7\x1e;.\\\xb0.\x07\xe6.\xafcB\xab\xb1\x13\xe2G\xe1l\x83>\x8b\xce2\xbf\x87r\xe0\xfc\x92\x96Q\xa5=\xaf\xc4\x92\xe2@U)\xa4\xc8\xdf\xaa\xaa\xe9\x08<\xd1\xa3\x02\xbac\xb0\xd8;\x94\xd8\xf2+\x1e\x888\xb4\x19\xa5<\x08V\x120sz$E\xf5f\xf9\x08\"\xfa\xa7=\x82\xbe\xc3e\x06t\x0e\xf0\xaa\xb6\x15&\xfb=\x19AF\xd7,C\xb9\xa7\xdf\xdf\xeb\xf7\xfb\xc5d\x93\xeb5\xbb\x83\xcf\xa2\x1c\xfc\xe4\xd9\xebW@\xab\xf1\xfc\x94(\xb90A\xdc4\xbca\xab\xe6I4\x84.E\x92\xc6\xc4[\xa1\xc3\x81\x17\x84 \x84Q\xd8Y\xc7A\xc8\xb6z^m\xa2\xab7\xed\xc6$\xc9\x96\x98/\xd53\xad\x99f\xc9>)\x96Lqo\xb9\xe2 \x04\xd0-\xac\xe2,\x833\x1cw\x83\x84\xa7\xdb\x0f%\x0c\xe4\x1a\x9a\x15\x89/ \xac\xbc\xf5:\x08/\x93\x13\xc4\xb6u\x1c]\x053\x8a\xddQ\x16\xfb\x84\xe7o\xa6\x9b@&k\x96\x93\x87\xd8\xa4\x87E[\xf2*xKn\x12;t\x9c|A=x\x02>\xfd\xc3\x164\xc3\x80\x8f\xde\xd4\x95\xe2\x9ce\xd87\x9b\xb0\x90\x94!\xfa\xdb\x04\xecG\xabW\xcfM?\x920Z\xce?\xac\x9b*\xdf\x85\xb9\x8a\xd7Aa\x08\x0cd.\xc3S\xf2\x08#\x91\x95z\x97\xc3\x1bo\xb5\xecF\xf1\xa5;\xe8\xf5\x06C\x9c?\xe6q\xabAsZ7\xbb\xeb\x18$L(2E>\xc0\xa5\xe2\xae0\xf4\xa0\x1d\xe5s\xe7\xc3\x13\x98\xd3?l\xee\x04.Dc\x1fS\x90\x1b\xb07/\xa6\x96\xc1\xe7)\xea]\xe9\x94'y\x8cb\x9e\xde\xa9X\x13\x06\xb0\x99\\\x04t\x8f\xdd\xde\xeaD\xa7\x11x\xecI!`\x95\xe5\x022\x13(\x06o\xc9\x0d&\xe0#\xe3`\xcaB$\xe5\x97~\x83\xe6D>\xea\xe2\x7f\xb9\xd1Y\x8a\x1f2p)\x05\x8d\x92(I\xd1s\x87\xdd\xe8\x12?\xdbmz\xac\xd8\xe5\xc8p\n\xb6\xfc\xc8\xcd\x8f\x9a\xb552Y\xaex\x8d\xca\xe8lz<\xc0\x89\xbd\xa0,\x9en/A\xa8\x18\x85\xc7gmt3\x92$S\x1c\x80\xa8\xacvf>6\xf1\xee\\\x86\x97s\x0e\xd5\x0e\xe1\x84;\x10\x04\xda\xb8\xac\xdc+\xeb\xda\x0e\x1c\x1e}TS[\xbb-\xd7\xa7\xdd)\xb8\xdbv\xd9\xd1\xca\xe0!7\x8bj\x0c~\x9b\xb4\xac}\xf9=\xbc[\x04Td\xe8\xf7\nA\xae\xbf[|\xe7`C\xbf[\xef\x90\x15\xe12\xaa%pv\xbeD\x07\x83\xe6\x89v!\xa6x\xc5\xd6\xfbe8\xa3R*\x9e\x9f\xf8A\x96.\x80\xfc\x90\x16\xdez\xd8\xefu\xbb\x8c\x87\xb0\x0d\x8b\xe1\xc6\x0cq\xa5\x9e\xcd\x0c\x99\x06\x8f{\xc16\x08\xe3\xbe?\xc5\x89\xfb\xd2\x85V\x1f\xbd\xe3\\\xd1\x94@\x0e\xa7\xdc\xbfM\x1aw\x0bf\x8f\xb4 g\xf7|HO\xb9\x83\x10\x9f`\x87\xf3\xb1\x0bo&\x13\x01zj\xf1 !?\x9b\x91\xd0'@\xc24\xbe1\x8a\xd9\xcc\xc7\xacDd\x88\x96\x96\n\x12\xd0\xf28\x8e\xd0\x83\x13Kd$p\x07\xc5\x89\xb4\xfb6\x08g0\x02K\xf4\xc0r\x8b\xcd\x841\xc6\x9a\x04\xca\x9f6\xd3\xa8\\\xc4D\x8c\xd6\xef\x80*\xa6\xd3!\xee\xee\x16\x11\xc2\x1b\x04\x90\xdc\x7fBW\x8f\xb4a\xe8\xf8M\x1a\x18\x8f\x1f+\x99i\x87R\xe5\x03.\x01m\xc2-0\x12m\xc41~\xb3\x17\x86\xb0\xcb\xa4\xa4@D\xb1\xc58\\t\x19Z-k\xf3Z\xd8\x1b\x16\x0b6 \x0b\x94\x91N\xf20\x8a\x03\x9b4\xa7\xbc\x98\x8b\x01\x92\x14p00\xb2~\x89r<\xc9\xb3\xf8\xd1\xd1\xc7\xba\x83pi\x97m\xd2\xbdBL\xcc\xc2\xfc\x04K\xc2\x99\xd0 \xf0\x83\xe8\xbb ]\x04!xpE\xe2\x0b/\x0dVt\xe5\xab\n\x1eS\xa8#.\xb9I\xe3m\x9d1)._M\x96D\xe0T\x9c\x80\xbdK\xa1\xf3\xe0\x07H~\x10\x06r\xed/\xbd\x15C\xc0\x95\x17\xbfM\xac<\x0eqe.X\x16\x85\n\xdd\xcd\x15;\xf2\x195\xf4*:\x9dJ\x9bI\xe6/JGn\xe6\xa5I1\xaf\x8c>\x8c\xb4o6\xef\xeaB7\xaf\xe7*WJ\x15\xba\x02\xe3L\xcd\x97\xd1;J.\xe9v\x8d\xe2R\xff\xcb\xab\xa6#\x7f\xc8\xc8Z\x17\xfa\xf60\x99u\xfd\x1c\x0d\xd1m#F]\xe6)\x08\"\x1a\xc3PU\x83\x85\x8eT\"W8\x85STs\x0d\xe9.\xe5\\\xa2(Ea\xe2\xa9\xee\xb1z~\x16\xe5\x99\xb6-\x0bs\xcd\x9a\xb4\xea\xa8Y\x0bQ\xb3\xf6\x18=\xc1k\x89\xf7\x0f\xcd\xc4[C\x96\x8f\x18Y\x0e\xefA\x96\xcd\x82\x8c\x9e4\x87\xc0K\xc8\xe4\xd9\xd0\x81\x12fV\xb1Zl\xdc\x90o\\v\xd4l\xbd\xb0C\x07\x93\xc76\xd7\xa8\xe5\xb0\xd2\xb6\xc9u \xc5~,\x0f!\x8cf\x04VYR\xe0\x9b\x97\xc2\x92xI\x8a\xaa{I\xcbVb\xd3\xf5\xbb\xa9a\x81\x7fJ\xd2\x86i\xf8\xc2U~I\xf2\xc6\x85K\x17V.\x9c\xbbp\xe1\xc2kf\x8c\xd20\xed7\x06f\xfe}\x033\x97\x16{\x19$) I~Vb\xbfl+Zc\xd4\xd9T\xe8j\xa1\x88\x1e\x9d\xcf\x82\x00pyE\xfc\xcc%\x15\x06@\xb5'\x8c\xd0\x19b]\xc8eLA\x85A\xeb\x1f=R\x04Q\xfbM.\xaf\x96\xc578e\x93\x00\xc3\xca!\x93\x9f:\xd0\\W}\xf8\x84+\xc2>E\x97x\x07\x0d\x1e\xf4\x85O\x0d\xde\x9a'L\x82\xba\xbd\xc5\xcdx\xe2\x94\xbbwZ\xf4\xee\x86\xc9c\xdfJ'a\x88\xd5\xeb\xd6\x8f\x07j\x80\x11\xbc\xa1\x9d\x8cr\x0b\xce\xa7\xf4\xc1\x9ao*z\xea\xbb\x80\x11\xf8\xc5\xa4\xcfs\x92F\xf0<\xd6\xa6\x9c\xecu\x99\xd5\x94\xec\x88\xf9L\xc1)\xbf:\x8eg\xaf\xd789\xdb\xd8X\xdcB\xc9\x9b\x98Og\xc0=w\xcc'4\xe0^;_\xd5\x8475=\xcb\x91T\xfb\xf4\xaa\xf6\xe9M\xed\xd3K\xc3\x06\x04\xeeG\xa3\x0b\"|\x87\xf3\xe3\x92\xab\xac7;?z\xc6$D\x18\x84\xa8\xa9\x1e.\xd6D\xd2\xa1-\xab\xc8\xb4\x07\xecP\x80\x07\x9a\xfd#\xfe\xfd\xf6\x96\xd2\xf2\xb8\xf9\n%\xd2\xc1\xd0\xc5[\xaf\xec\x08h\xd4A\xc9\xefI\x07<\xadL-\x7fX\xaa\xdf\xa6\x91:'pm{t\x9f\x1b\x8a6\xc8W\xf2\x87\xf6p\x9f\xf9[x\x0e\x9c\x99\x1a\xafH\xca\xb9\xc4\xe8Q\x11\xfe\xffc\xee[\xbb\xdb\xb6\x95E\xbf\xf7W\x8cx{\x1c2\x92\x15I~$Qlk\xa5i\xd2z7ur\x9a\xa4\xfbt\xcbj\x16-A6\x1b\x89T\xf9\x88\xed\xbd\xdd\xf3\xed\xfe\xb1\xfb\xcb\xee\xc2\x0c\x00\x82$@\xd2N\xd2\xd6k\xb5\xa1@\x10\xcf\xc1`\xde\x93\xb2d\xe3\xcf\xb5\xdbG\x97\xad\x82\xbf\xe4%\x9c\x82\xfe\xc0\xae\xb7\xd1w\x02\x12\xb6\xf1c\xa4\xc6\x149}\xb6\x8a\xe6\x1f\xa4\xd4\x9a__\xc8l\xb9\xa8kX\xf5\xf2\xa88Z\xc4\x9b\x8f\x02K\x8b\xa2\xb5@r\x02\xb8\x91\xf8\xe4\xff.\xd4\xf9\xc5/$\xc2\xaf_\x97\x86\x9c\xcc\xf2\x0f\x01c\xad\xb9g\xd1\xd5\x93\x14\xee\x9d9\x07\x96\xfa\xee\xf8\x9f\xd2\x13aD\xd8\x98\xf9\x0b~\xf1\x07kN\xcd\x04\xa9\x12\xe8o\xfc ~\x02>\xcc\xa3U\x14\xf2\x95^\x07IR \x9bW\xfe3\xbbKC\x1d\xb3\xa2\xff}\xaey\x9a\xe6X\xdcz\x12_\xf0 \xae\xb3U\x1a\xe0\xd9\xf9\xc0\xaea\xed_\x830q\xd6W\x05\xd5\x1b\xf6\xb9\x19\xdf\x88\x19\xef\x13\xcb\xe5\xf3\x0b\xf2\xd3\x80Mp\xed\xe42yN\xedi08\xc8Y\xcb \x9cG\xeb\x0d\xea_\xd8\x95ec\xf9l\x91\xceS{\xfb\x04\xa2\x18\x96\xd1j\x15]\xb2\x05\x9c]\x83\x8fj\xd0\xd4?\xcbV\xa8\xeca\xebMz\x8d\xca\x0d\"\xfcr\x9c\xa8\xbc\xa6c\xf3\xc6P(\x11\x0dEYeP\xae\xa4\x037DZ\x04T\xca\xa7\xab\x1f+A\x06hB\xb1s\xbc\xd9+k{-b\xd9\x1b\x97\xb7(Hk\xc6\x88\x9e\x81\xa8Qr3\xbfVnV\x80;\x9b\x17c\x93\xe8\xac\xf2Q\x15\xf2\xc4\xd1AH\xb3\x01\xda\xba j\xab\x9c\xae\\\xd4&\xf1d\x81~\xc5\x16\n\xfd\xfe\x81\xc4O\x0f\xce\xbc*\x01d\xa3~\xcaZ]\xccY\xb3\xd4\x93\x88u,\xf9\xc6\x17\xf5\x84\xd2\xc7FB\xe9\xda\xe0\xad\x04\x02H\x859\xa8\xbbi\x86\x05\xd2\x89=\xde\xe9 98IbM\xe9\xc9k0\x1f\xefs8\"\x82ac\xe5EUmN>\x8f\xf6D\x8f\x03\xea\xf1?M\xfeip7\xb2*\xf6(\xc3T\xd3=- \xabM-a\xa5\x8e\x1a\xf3z\xad\x96W\xe8\x0b\xab\xec+i\xd2\x08v\x17\x05\xd8\xfd\xa8\xc1.\xc7\xb7\n~al\x13\x1b\xc7\xf6\xcb\xe4\"\xa7?\x08?\xc2>9\xc5\x9f\x04\xe1\xf9\x8a\xc1\xefY\xc4\xab\x8a\xbdGZ\xa2n\x96\x86\x83t\x1b6\xc3\xdc\xe9\xe78):\x83a95\xbb\x04\x1e-\xc4t\x9f\xff\xd4`\xe2m\xf3\xa9i1\x9eZ\xc9\x88\xf0]\xf5\xd5\xa0\x8d\x18m\xe0\x95\x87d\x03|\x14c\x8dd\x9b-\xce\xa2\xa9\xab\xcbv*\x1aO\x87~\xfb9TrM\x9f\xfcE9\xd0\x7f\x98\xfa3\xafp\xc1\x1c\xa3\xef\x88>\xc9\x16-Rp\xd1\x910\x83\xe3\x1c\x8b\xcf\xcf\xd2\x08]\x89\x1f*Vf\x17\xc6\xf0hO\xfd\xe4l\xc3\xc0\x83#\xfe\xbf\x16\xba\xb2\x80\x14\xda\x11\x19m\x07\xfc\xbb'\x10lo{\xd8\xfb\xd3\xb6k\xc5\x99\x14\x0c\x1b\x87~5\x07\x07\xb0\xebA\x172\xc5R\xa9\x13x\xc1\xae\xfc\x05\x9b\x07k\x7fU\xef\xd2\xa4\xff\xe9K\xf9\x9b\x1b\x95\xe0\xc5N\xb7\xd0ZJ,\xf0!\x8c.C\x10\x11\xd3\x94\xcc\xac\xa6\xeb\xea\xc9\xa8\xc7\xa4~\x8eI\xe9\xe8\xdb0i\xb5\xe1/\x84I\x17Qv\xd6\x06\x93\x96\x06\xd3\x82\x96\xb8\x0dj5\x8f\xc2\x88Z51NGC\xb26\x0c+\x0c\\\xcdXu\x97d\x18\xcd\x8a\xef6X\xd5\xd2H+s'2\x81{#\xac\xdf:\xcf\xdd\x98\xa3\xcd6-V\x07s+\x93\xa7U\xe0'\xb7\xb2x2\x18?\xf6\x8a\xa6N\x9aH\xbd\x14\x8eE7\x84\xbc\x97\x85J\x0c\xb0\x10\xe3(\x19\xc5iw\x92.\xa6\x0fge\xddU\x95\\\xe5`rWS\x14\x94\xba.\xa5\xbc\x95\xdf\x94v\xe1\x9c]\xd1\xcd\xc1\xeb\x8d\xbbl\x06,\xbe\"\xcf\xdd%\xb9}\x12\x92F\xa6w\xe7Q\xfe\xbc;\xd2\xcaw\xf2g)\xe8\xc3\x1f\xfbz\xa5\xc7\xda\xb3Vg\xe7\xa1V_+\x7fL\xa1\x1e\x96\xb5P\x8e7\xce\xbe\xd6\xbd\x10\x9b-IF\xff\xa6\xf9\x18 \xee\xec\xe6\x86\xec\xfb8\x98\xb78X\xcd\xe4J\x80\xbe\xe4ErWX\xad\x8b\x03\xb6\xac\xa5B\x84u\xc6\xb2\x89b\xb8\xe3\x14k\x98g-\x8f\xef\xce^\xdbA\xd4\x0f\x00}eZ\xf4\xd9$\x95h\xbcj\xf29.\x9b\xa5\x8f\xbc\xcdK\xac\xd8l\x05\xe1+1\x8bT\xd3h\xc6gsU@\"\x13\xed\xe6DdP\x14\xdc\x1c\xda\xb3t\xe9\x7f\x99\xc6\xbf\xdfYZ%\xfej\xe3\xb6\xcb?\xbb\xc0\x04\x8af\xf8\xc2\xff\x83\x8c\x078~\xd2wB\xe8\xaf\x0b27Kr\x01\xf9w\x179\x8e\xb9\x14\x15`D\xcb\x10\xfe\xec\x0c%-#\xc6\xbb\x0d\xbeWw8\xbd\x1e\\ \xcc\xe7\x16k\x08C3\xcbv4\xb8<\xd8n\xc4\xf2P;\x1d\x85F\xc8%X\xa0\x99\xa2\xc5\xea\xa6*Q!R\xa4'\xad( \xfd\xbd\x16 \x94\x07\xd0\x96\xde,\xca\xd8\xc0\x998(\x9b\xaa\xa9\xab\x95\x08\xcdnn\x07\x96\xdf\xd5\xc9E\x94\xad\x16h\xabs\xe1\x7fd\xe0\x87\xd7\xd2\xf2\x1a\x95\xb0\xd2\xdf\xbb\xb5\xba[\xe9\x15s\xd1\xd9\x8fjVh\xe4)l\xe1h\xf5\x91\xb9\xda\xd4\xeb\xf1\x84\x06\x13\xef\xfbs\x19;OwM\x93\xfb\xfc\x9e4\xccw\xdc\x82\xcf{~\x05\xb2\xcf=!\xae7\x8c\xbaFh\xbf\xb9\x01g\xe9\xafVg\xfe\xfc\x833\xeb\xc9\xed\x99\x80X\xb7\xda\xeaS\xac=+\xccT\xac\xd1\xd6\x16\xbc\xa7O\xa8\x18\x1f\xcd\xa1d\x10\xa2\xf1=\xdf\xfe\xce\x01\xc6\xe0\xc4\x95\xec\xc2\xbd#H\xfds\xd4< \x98?\x13\xbe\x13\xa2uN+\xf6\xf0 `i\x9a\x97\xdeC\xff\x9b\xca.\x93\xc3{\xd3N\xdeq\xebr#4\xa1'\x13\xdd\xa31\xd9\x82!\xbfS\x9a\xa1s\x94+\xe1\xd0\xcbI\xf7\x91\"~\x94W,\x7fdI(\xd5\xc2\x8a\x7f\xbe\x8a\x12&\xcc\xf8K'\x99_\xe8\x95\x89\xdf\xdc\xc0\xeb\xafr\xf8R\x8f\xcaw\xe1\x87v\x9e\x85\x1a\xfa\xaf\x00\xa9\xc9\xc3P\x90~Z\x18!\xe1KP\x0d#\x94\xf6W\xec\xdc\x9f_\xf7\x94K\x8f\xc8l\xa6m\x18\x99=I\xb1U\x0b\x97E\xdc\xf1\"\x9f\xd1\xfcU\x0f:nIs4\x10tw\x07-z\xcc\xd20\x9ck\x06\xed\x9d\x13m|d\xc1\xdf\xadMC5\xbc\xect\xd63\xfa\xba\x15\xd8=\x19\x0f\x05\x0e\xc8\x8d[\xb8\x07\xa9xH\xc8k\"kiR\x1b\xeb\xe6\xcc!PKNCd\x06\xf8L\xd1\x19\xa0\xa8\xa1\xad\xcd\xb1\xd4\xa8\xa3m3\x04;\xd26\xf8hR\xfc\x05\xfbUPC\xdd[gZ\x1b\xd2\x01\xe4\xb2~1\xc0\xe2\x7f\xb1t\xe7\xae\x81\xa8\x16\x04\x9d6&\xd2;\x8b\xeb\xed'\xe1\xe1\xf7\xd34\x9cI\x19\x1b\xc7\xa7\xaf\x85\xc4\x81\xf0\xa9\x12\x82\xe5`Z\x90<|e\xef\xbc\x88\x0f\x06\x1ak$\xce{\xee\x9e_\x8f(\xdaV\xa4x\x0e\xed+\x8f\xbcbD\x17\x11\xe1A\x1f7_\x90\xccpV\x13\x14\xd0\xad\xfd\xb8\x12\xb7\xe5\xe7\x9c\xa6\x17\xd3D;\x8d\x8df\x9cV\\\x98*\x92\xde\xda\x82sr\xf0,\xee}T\xdc{P\xa18\xc2(\xdc~\xfa\xe6\xd9\xf1\xb1\x16O&\x01?f\x10\x84)\x8b71C\xc7\x87\x04\xd9-\x15tNnmR \x1b\xd0\x82\x9f\x9d\xc0\xee~\xf3\"{\x82\x14hXa\xad\x82\xe6I\xbd\xadc\xc9\xaa<4\x8aQ\x16*\xc03\xf7\xe0(\xecG\xede\xfc\x9dk\x8c\xc2XL\n\xc3d\x86(~G\x0e$\xbd\xa0\xe2\xda\xc9\x901\xa5\x05\xc8\xa7\x80K b\xc9\xd4Wrs\xf3\x82\x1e\xec\xef\x8d\x1e\x8aX\xa9\xfaG\x03Y\x93\x97\x8b<\xfa^\x19\xf7Q\xb2\x04\n\xc5\xd9\xa8YK/\x82\x84\xb6\x100\xfd\x01\xfe\x96\xd131!\x92\xfa!H\x1eQ'\x91\xf1\xd8\x99|\xbc\xb9A\x9e\x9b\xbf\xcc\x03Y\x1eb\xda*\xf9\xab\xd8\x04Q\"XE<\xde\xdc\x90\xd5\x02\x7f\x8b\x01\xaa\xf8;\x19\xa9J\xbdQ\xe4\x1a~)\x7f\x14\xdb.01|j\xf9\x981\nx\xb0b\x8bcQG|\"\xe8wK\xe5\xb7\xf4V\x0d\x1d\xf7.\x07\x06Q\xae\xc9\"\x06j\xb4(\x8e\xd0\x7fJ\x89\x84^\xa6\x1b\x02a\xa1:\x9fH_\x14\x11-m\xa7\x81\x08\x0c\xc5^\"$\x0d\x1c\x158(\xac\x1e\xd3P\xbb\x80<\x08\xf5A\x90\x9bFX8\xb7&\x92\xf3\x89^\xe7 \x0f\xf8\xb8\x0d\xc3'\x1e\xfc\xe0Z<\x8c\xc3|n\xb5\x07\xf4k\x9b8Z\x13E\xc3!\x9d\xe3rW\xc8G\xcb\x96\x1c\xcc-B\xf9\x88\xf3\xfc$\x91aFZH\xac<\x04[\x0c\x07\x10\xf0\x7f(\x04\x1bs\xa3i<\xab\xc7-\xdf\x1b\x0f\x9c<\x99\xdf\x99\xf6/XJ\xaa&T\xc9\xaf\xaa\xe7\x95\xd7\x1a\x8a-\x95\xb5\xe4\xb2N\x07\x06\x9f\x82<\x81C\xe0\xe6\x8aC\xa5\xa1W\x184\x085\xec\xda\x83\xb3,\x85e\x94\xf1[.\x8a\xd9\xad\x128\xe4I\x0c\xbe\xeeU\x93\x1e|\xdf\xb3\xe6+h\xd2B\xb4\xd8S\x04\x99\xb8\xcf\xaeR\x16.\xdc\xea\xf2\xd1\xa1\x1eCV\x9c\x0f\xef\xac\xb4\x1d\x12\xf8\xee\xd8\xd8W\xdaOc\x02\x87Z\xcc,f\xf3\xfd]gS\x8d\x0f\xfc\xe9\xe9\nL\xc1D\x03\xb7\x10z\xb1r\x97r<&.\x12\x89e\xcf\xb2\xe5\x92Pw\x15e\x86E\x94\x19\x8b\x9f\xf3h\x95\xad\xc3B\xa0\xd3\x1c\xee\x02-\xa3\xc19K\xdf\x84\xc1f\xc3\xd2\xa6\x05\xae\x98\xabW\xcfbG\x1b\xae\xa7\x0b\x0dL\xbc7\x88\x00\xf0\xbb\x1a\xc5\xf0pOD\xc0\x91\xf1o\xf4\xd9\n\xeb\x00~\x9do\xd3yvN\x07\xa7\xf1i\xf8\xff\xfe\xaf\x9eU\xc0\xe9\x07\xe1\x82]\xbdZ\xba\xdah\x10\x8b?M\xdd\x80\xf4\x17\x96\x90U\x01lS\xf0\xc0\xc2\"oc\xbf\x0c\x1e\xc0\x88(\x0f3\xb3\x86\xe3\x86~\xbf\x0f8\xf8\xee!\xec\x99\xb9\x946\xeef\xb8Dz\x1e\xbd\xd2Jd\x9c\xec\xd3\xa6\x97\x93Ww^\x9a\xcc\xba,n&\xd0\xf8vieZ\xacJ\xa4\xafJ\xc6\xd7\xf7\x13VE@\x94/\xd7CL\x80\xa8\xba\x80\\\x11sSJ@1\x94\xe0\xbc|4\x00\xefR\xc0\xfcn\xb9\x16t\x0d{\xde\xd5\xee\x8b.8\xbf::\x82\xd2\xcf\x90L\x19\xd86\x1b\xb5\xe3\x18\xef\xf8\xfc\xe8s\x82\x15)\x88{A($\x8f\xea\x1dFK\xbe\x87\xaarN\xb1\xf8)q0\x0e\xc6\xa3W\x98\x00\xf9\xba.\x9f\x9b\xc0\x04\xf9{Q@*\x10\xd2M0\xb9\xa096p\x85\x88\x8az\x19\xd3\xaa1\xde\xad\x11M+L\xf3\x89Hs\xa0])z\xe3\xfc2\x8e]C4\x9c$\x8d+\xd9\xfd>\x04\xe1b\x9c\xabs\x0b\xef\x94\xf7\xd7lu\xdb\xc6\xcd#\xaf\xdb\x17\x91\xe7\xf1Mz\xbdbcp\xd4z9\x7f\xf5q?\x8b\xa2?\xf5\xb8\x1bL\xa7Z\x1f\xf7\xc2\xb1N\xe3\x8c\xe9\xc7\xf8m\xf9\xf7O\xef\x9e\xcbc\xcd\x0b\xf6\xf4\x8f\x97\xfe*)\xd4~Q)x\xfa\xf2\xcd\xf3\xbb\xa2\x85\xbas|\x9b\x81\x7fN\xfc\xe1LE&\x81o\xa2h\xc5\xfcpF}T\xf2\xd2I\nT\xa8\xe1k\xe7^\x8bmL8\xc1\x9a\x82\\\xd2\xad0\x91\x0b4\x06\xb1KmN\xb1 E\xb4\xea\x8b\x16{,\xf7\xbbM_&\x8c\xd1\xae/9\xaf\x17\x96y\xfd\x1d\x10\x88%3\xe2m\xb3\x9aV\xf2\xa6\xed\xe5\xe344\x94\xb5o\xe8\xa1\xd6\x90|*c\xba\xc0\x84\xe9\x820\xfd; :\x12\xd7\xe8\xb2k#\xe0\x04v\x87zS\xc3\xca\"\x17\xee\xe4FU\xe8\x1a_\xe7\xbfD3\xeed\\\xbc\xc7\xf3\x1e\xa8\xf2\xe9i\xdf\x9d\x8c\x83pys\xcc\xff;y\xe1\xddPQ\xe8\x877'\xfe\xc9\xcd\xc9\xd3\x13\xcf\xfbZ7\xb9\xc7\x80\xfc\x98\xadW\xeb\x9c=\xb0K \x8d\xbc\xf3r\x15\xf9_\x84{\xd6\x85\xdb\xa4\x15\xe1\x88\xd6\xedD\x82\x80\xf1t\xda'\x9d\xeaf{\xb3\xcfN\xd2\x18#\xc1\xc8\x11\xc2!H2BX\x1eW\xa8\x91~\x1a\xbd\x8c.\xe5\x89\xe6\xa4\x04L\xf8=>\x06\x11\xfcw:\xeb\x81\xd3\xdd\xceu\xe7\x0c\xe9\x95#q\xc1\xb8d\xf2\xa7h\x91\x1e\xf0\x9a\xcb\x9c\xf4\x10\xa6G0\x11wY\xff\xf5\xab7\xc7o\x8f\x7f~\xfe\xfe\xf8\xe4\xc5\xf1\xc9\xf1\xdb_`,_\x9d<\xff\xeei\xf9\x95\xd3\x0f\xfd0o\xee\xc4?\x811\xb0\"\x85!0\x9b\xcb\xeeFf\x04E2\xe3\x05\x07\x9cZBCX\xe7\xc5Dh\x04\xb7\xe8\x8aIB#\xe6\x9f\xdb \x8d\x10\xees\xb2y\x8c\x0f\xda\xa8\xd8\xdf\x89\xd4p\x89\xd6\xe8\x1c\x92\x1b\x86\x81\xd4hKk\x14\xf0\xa4\x0d\xe2C\xb3l(HN\xfc\x13\xde\x17$\x97A:\xbf\x00\xd7*;\x98\xfb \xd3\xe5\x90cc-\xd0\x16\x07\x81\xcf\xcc\x1dQcJ\x8a\xdb\xa6\xb1\x93\xa7'\xb5\x8d)1m\xab\xc6\xfc\x13\x83<6\xf7x\xb6\x1e7!\xf4\xfb\x12\xab\xc5O\xfeg[\xad\xe3\x93\x17\x9fo\xb5\x8e\xc3e\x9b\xd5\xaab\xa0/\xb7Z\xdb\x9fu\xb9\xb6?\xebzm7.\x98\xe9\xb4\xe7\x9f\x0f\xfa\x03\xc3X\xb4{\xa9H\xf6\xf6 S\xc9\xbc&\x10\xaak\xcaa\x0e\xbfP(\x02fX\x87L\xfe,]C\x99\xfc\n*\xe4\x97\xa2\x8e\xb4\xffy\xdb\xae\xed\xc7\xd7N#A\xd7\xd8\xe2\xa4\xf4\x8b\x93no\xd3\xd9\xcd\x14NO\xd3Y\xd7+\xbc\x1c\xeb\xbd\x17~\x10}H%\xf7=\"\x10\xb1\x85\xfb\xee\xbfn\\N\x8by\xe5n\n\xdf{\x13\xcf\x9b\x14(\xb9V\xea\xdc4X\xb3$\xf5\xd7V+\x96\xcfN\xac\xe5\xe1\xca\x83>\xbbbsA\xb3\xa9\xd2H\x96~\x01r\xcd\x10\x07\xc5\xa23\xd9\x08\xb7L\xf3\xb5\xa7\xf47H\x81\xa9yx\x8a(\xcb'\xa1\xe7'\xf74\xf3\xee\xe7q\x1c\xc5\xae\xf3\xad\x9f2\xe5K\xcbx\x99)(S \xf2\x89v\xd9t8#\xda\xa7\xcb\xa6\xa3\x19y+e\xf4sg\xd6\x83\x0e\x9b\xee\xcer\xf3Wv \xbc\x03\x97\xff\xaf\xff\xee\xed3W,\x83\xc9\xff.\x10\xe1)\xba\xbc \x8aN\xd1e\xd3\xbd\x19\xc5\xa5\xe8\xb2\xe9\xfe\xac\x07l\xfapfC\xc2(p\xc5\x80\xb7\xd3\x873A\x94\x0ez\xb0\xe3=\x81U\xeeK\xb9\xf3\xc4\x83\x15\x1a\xf6\x99\x90\x14\x88\xa8\xd1\xddU\x15\xfd\xd9\xc0\x8bM\x1f\xcfp\xe1\xf9\x9e\xed\xb3]\xb8\x0f\xee\xfe\x00\xee\xe3j\x0df\xd0\x85\xae\xcb\xa6\xc3\xe1\x8c\x83\xd9@\x8a\x00qC\xf4/\xb77\x9e\x88\xcb`]6\x0dzV\x1eFS\xdf\xda\x82e?a\xe9\xdb`\xcd\xdce\xff\\\x93?\n\x0d\xda\xa5\x0b\xce\xd3o\x9e}\xfb\xfc\xc5w\xdf\x1f\xff\xe3\x87\x97?\x9e\xbcz\xfd\xdf?\xbdy\xfb\xee\xe7\x7f\xfe\xcf/\xff\xf2\xcf\xe6\x0b\xb6<\xbf\x08~\xfb\xb0Z\x87\xd1\xe6\xf78I\xb3\x8f\x97W\xd7\xff\x1e\x0cG;\xbb{\xfb\x0f\x1f=\xee>8<\x0dOc\xe7\x96\xec; x\xbe\xc4\x86\xddY\xfbm\xc1\xd3A\xa3b\x9cc\xc7\xc8\xa2\x1e\n)\xf2_H\x1eCa\x9d\x8e\xa8\xe3\"b\xcfr3vi\xbcN1\x00a\x7f\xb7Qk\xc4\xe0\x00\x06\xad4?(\x13\xdf7\xbe\xb6\xe2\xc1\x18\xfe\x0b\x1e\xa1\xf0\xb9\x08\xf6\x9f|q\x06E\xe9\xc5\xf44>\x0d\x0fgB\x86a_\xf4\xa0v[|\x8c\xffc|\x95\xd8\xb7{n\xd1\x07)\xff\xee\xc1\x13\xe0\xab\x9c=\x01\xd6\xedz\xc0\xe0\xbf\xd0\n\x8c\xe4%\xa4\xce\x99\x8b\xfc\x10pt\x04\xc3}\xd8\x82\xd1\xde\x9e\xd7\x03\xbd\xf8Q\xb9t\xb4\xb7\x07[\x90p\xa4\x9f`\x12\x90\x83\x03\xd8\x87\x1b\xf0\x158\x04\x12\x1c\x98\xe9r\x15[4\x00\x19\x087\xc3\x81\xdd\x87}T\xd1|\xd2\x90`\x0c\xc3GJ\xd0Slk`lk$J\xf1S\xe1q\xc8\x97F\xaf\xb3\xab\xbe\x8c1\xe9\xc62\x8e\xd6\xea\xc1\x9d#O\x80\xe8\x1e\x1f\xe7u w[\xa9\x08\x06\xf6\xe0,\x0e!\xd0\xf6Z\x93\xb6\x00\x1d\x93s\x8b\x15\xa1X\x80/k\xc45~\x0d\xae\xb1@\xe7N :\xf1\xe4\xfb\xd3\x00\xb7\x8fo\xfa\xfe\x0eR|Z\xe9\xc8T\xba_*\xdc\xdf\x81-@s\x1c>#7\xe0\x10\xfb\xc8\x83.\xa4SfW\xa8\x16\x01t\x87\xf4\x87\x9fyD0\x86Q\x0e\xae\x85v\x06\xa6vv+\x85\x07\x07P\xeeq\x7f\x17\x1b\x1e\xe6\xc0\\h\xb9:\xc0\x83\x83J\xc3\xfb\xbb\xc5\xf6z\x10\x17\x01O\xfd\xfad\x02\xc2\xca\xceVd\x7f\xc58\x93U\x02\xc1*,\xbc%\x89\x16\xd5x2X\x9c9>\xf1\xca\xb7\x19\xf2\x97\x985\x12\x83[o\x03C\x80\xca\xfc\xb8\x91>z\xae\\\x83\xf9\xe1\x0b\x9f\x90 \xd8\xea6\x16\x88|\xa1\xf3)\x9b\xe5I\xc0\x94\xa8\x96\x16|\xe6\x08f\x15E\xb2q\xb3=\x87\x08\x84\x13\x84\x10\xd7\x1b\xf0\x04\xa2Id\xd3j\x08\nY\xdfo\xecZ\xfe\xdd\xc9P\x07i\x9f\xe6>x5a\x81\x90\xa8;1k^\x16\x11\xce\xa2U\xd2\x0e\x058\xc5SyG\xfa\xa6*\x9c\xf8\x93<\x8cZ\x1c\xfa;\x9e\xe1\x8d\x1f\xc4\xc9\xdf\xeb\x10\x0b\x7f\xdd\x9a\x83\x9a\x89\x19=\x8dc\xff\xda\xf5\xa5\xdb\xa3R\xf4\xf0\x13\xec\xdf\xed\x04\xfbx\x82\xcd'7h}r\x03\xf4\xe1G\x93!\x0d\xe1~`\xd7 \xff\xba\xec\xd6ok%\x9b\xb2\x19Ge\xd1t\xc0o\x19\xfcw6\xfb\xd3\xa1\xde\xb2\x8f&\x9a\xfac9\xd4\x99\xf0\x06\xb6\xeccT\xd8\xc7\xcc\xb8\x8f\x99m\x1f\xf9ne\xb8[Ae\x89{\x10\x89\xb5\x0b\xc4\xda\x05\xb8vV\"&\xfa\xeb\x0fp\xf1\xd6\xbe\xe51N\x98Uun\xf6)\xfcrg\xb8\xf6\x82\x0dB\xb0\xc4\xfe\xd2\xee\xb1\xb0'L\x10\x15\xa2\x0d\xa7lV{\\>/\xc4\xdb\xf0\xfc\xdf\xcd\x8f\xf2\xb7\xe4A\x16.\xd82\x08\xd9\xe2\x13%/5\xcbp\xfbE\xf5*\x19\xe6o\xcb\xcf}\x8c\x82\x85\x8c(V\xd7\xbb\x89\x93\xab\x13\xfa\xfd\xcd\xbc\xa1\x7fK\x1e\xc4\xec\x9c]}\x11U\xca-\xe4f\x01F\xa6\xc1zm.'\xe5Mg\xa6\xb19\nxp\xfa\xc0\x9d\x9e\x07\xeb\xd9}\xef\xeb\x07R\xb3a\xae\x1e\x1bb\x0c\x80\x18\x94\xf3@\x8a\xdd\x07V%\x02i:\xa4\x05o8\x1d\"\x1b&\xd5\x07G\x9c%mq]\xf3\x9e\xd0\x9aw\xcar\x03\xa0\xb8`\x0b\x947Si\xe5K\xdf\xc1\x7f\xce\x8a\xcbS\xa2-:\xa9\xdf\xca\xab[0\"\xea\x81e\xc5P\x93\x95kFY\xaf\xcc\xc7|\"\x92PT\x1au\xd0\xd6\x14\xe6\xb6\xf8\xa4vC\xf8Zu!\xed'Q\x16\xcf\x19ty\x81ua\xd3\xfe\xf9*:\xf3WB\xe7\xd7=\x04\xe7\x9cB\xf5\xe5\xa9\xe7\xf3Wkz\x15\x9c\x87Q\xcc\x9e\xf9\x89\xfe.\xe0\xef\xd8\x97BfO\xb4J\xea~\xd1\xa21]\x06\xe1\"\xbaT@A?\xfb,\xd9\xc4\xc1\xda/\x19\x06\x06\x8d\x98\xd1\xa8N\xf8-y \x07\xff\x17\xe3\xc6\xaa\xbaF\xfe)\x18p\x11\x06\xf8\xe6{\x16\x11!\xc8\xf48}4\x0e\xe3g\xa1\x9eM\x8f\xfd\xf0\x9c\x8dkyo[TQq8^\xc7\xd1y\xec\xaf\xe9P\x84\x18\xfb\x8e\xef\x98\x0c-v\x16-\xae\xb58<\xce\xf3+\x0e\xf9I\x10\x85oR?ek\x16\xa6\x8eVu:\x98\xa9&\\\xe7i\x1cG\x97/\xc4\n\xe7_\x96?`\xea\x0d}\x8bN\xcf\xb7\xfd\xca\xc0\xe6\xebZ\xb1\xba5hD\xd4\x9f\x84\x8eEt\x9c\xe6\xcd\x0f\xb4\x8d\x0f\xeb6\xbe~\xd3\xff\xb0`s\x9b\xc3\x0b\xdej\n\n\x88\x81\x95\xdb0\x14\xbfu(\xe0\xbbc\x84\x82\xbc\xaa\x82\x02^\xd7\n\x04\xc5\xfae \xe0\xc0v\xeb\xaf\x0cf\x10/\xfc`\xc5\x16\x90F\xca\x16B!\x0c\xbb6\xc5\xd8\xc1\xc6\x8f\xfdur\x0b\xab\xd0H\x06T\x0d\xfd\xb5 >\xc5\x0di\xec\x0cW\x1c7\xba\x07\xce7\xabh\xfe\xa1t\xde\xec_\xe1\xf2Mp\x0d\xe4\x02\xbaQ\x0fB\x199x\x8a\x96\x0b\xfc>\x9e\x0egt\x01\x0b\x95\x8b^\xdd\x91\x08\x02#F\xe5\x9f\xd2g\xf5&4w\xbe\xa1\xe5\x00\xfe\xd4;Z\xdd\xba\xcat\xed\xcb\xda8X<\x00\xf6F&\x8b1\xf7\xd1N\xa98\xa3\xda\xe5b\xbfN\xdaW\xac\x9a4\xcb\x15J\x08\x0f\x0e\xe1q\xb1h \x870,i\xb3Vp\x08;\xa3\x12(\xf0\xb2\x9db\xd9\x05/\xdb-\x96-x\xd9^\xb1\xec#/{X,\xbb\xe6e\x8f\x8ae\xe7\xbc\xac4\xbe5\x1c\xc2ni,\xefyY\xa9\xdf3^V\xea\xf7\x12\x0ea\xaf\xd4\xc7\x15\x1c\xc2~\xa9\xbd7\xbc\xac4\xb7\xe7\xbc\xac\xd4\xc7S\xbe|%7\xc4W\xbc\xac\xf4\xedo\xbcl\xbfX\xf6\x01\x93\x15\x96*\x1eca\xa9\x97\x1f\xb1\xb04\x95\xb7ph\x80\xf8\xc1\x18\x9c\xd3\xd3\x81\xe1\x1ez\x88o|\xc3\x9bG\xf8\xe6\xcc\xf0\xe61\xbeI\x0do\x86\xd4Qhz5\xc4W\x1fM\xafF\xf8jiz\xb5\x83\xaf\xca\xd4\x1c\xff\x1b\xd1\xd0\xcbBh\xfe\xb7\xb3;\x86{\xa7\xa7\xce=\xc3\xd8\xa9\xaf\xd3Scg\xd4\xdb\x89\xe9\xdd>M\xed\xbdi\xa5F;\xd4\xeaK\xf3Kj\xf5uI\xc6P\xac\xfa\x8c_\xd6\xce\xb5\xd3\x03\xe7\x17\xfe\xbfk\x96\xe0\xb3\xf8\xe7\xf9\x1b\xfe\x0f\xd2\xbc\xce+\xfa\xff \xff?>\xd2S\x84\x8f\xf4\xffWX{\xb9\xc4\x8a\xe2\x9f\x17/\x9c\x99)\x90\xc6\xeb*\x92\xcc\xc5\xb5%\x0d4Y\x9e\x1c\xd6z\x93\xf5(X\xc6ho\xcf#B\xe8\xca\xa1h\xbd\xa3b[\xca\x02\x19\xab\xef\xef\xed\xed\xc8\x0f2\xf1\xc1\xae\xe1\x033\xc9\xde\xa1FvG\x8fw\x1f\xef?\x1c=\xde\xf3\xbcb\xf8\xdby\xb4`\xb0\x89\x82Bz\\\x8av\xb8\xf6\xafe\xda\x85\xf3\x98\xf9)\x8b)\xf3\xc2\xe0\xea\x85\xf83\xd1\x0d8\xd0wb\xa0\x8f\x8a;[\xf8%o\xbc\xd3SG\xc4p\xcc\x836\x0e\xf0\xfbm\xc5'{\xd0\xd5\x987S\xb0\x92\x9f\xaa\x9b\xa5\x85\xac\xc6\x9d\xc9crG2\"\xb6\x0c0\xfd\xa3\x9f^\xf4\xd7\xfe\x95\x8b\xf9\xc1E\xf1\xcd\x0d\x8c<\x19\xda\xfbC\xb09\x0e?\xfa\xab`Ami\xbf\xf58\xdc\xcbUt\xf9\x92}d+\xa4`\x83\xe4$\xe2kz\xee\xa6\xf9\x1bO\xfa\x1fie\xb2\x97\xf4z%\xe2m\x17\xaeU\x1bE]\xcd\xffkH\xdfU\xe0\xdcrw\xfe\xff\xfca\x919\x87\"\xfb \x19iP\xc6\xd5\xb8\xa40`J'C\xce\xff\xd1\x13\x8a\x88:\xa4\x8c\xe4\xf14\x10Z]q\x16\xd84C\x0f\xeeN\x87\xc8\x99,7]\x1d\x91A/\xff\xcc\xc0\xd5r\xd0\xc8\x94\xff\xb6\xd7\x03\x97\x12\xb8\x95B\x90\xf7eV!\xde\x0foOdt\x98\xf7u7\xcb\x1e\xf8\xd4\x99\x8f\nk\xfd\xd5\xd4\xe7\xe3\x0b\xa7\xd9\x0c\x0e\xcb\x91oA\x13p\x17\xe1\xd9\xd5@\x8c\x03\x0e\xb6\x98H\xf3H\x05;Q\x9c\xfe\xc0\xae)\xd5\x8c\xfaQ\x8c\xde\x1e\xb2\x7f\x06\x0b\x19=]\xfd\xba\xb9\x81G2\xf6y\x18\xfd\xc4\x96\xd4\x86x\xd4[\x08\xa3g\xd1z\xe3\xa7?\xf2\xe3Lu\xb4\x02\xbd\xe6<\xe2\xd0\x8d\xeeV\x97b)\xb5\x02\xbd\xe6\x1d\xe2\xc5\xcb\\Du\x9f<\xbf*\x86\x98\xc7\x9cWa\x1e\xa6\xbe\x98I\x9a\x97,2\xfe\x85\x9f2a\xa7@\xa5Y\xc2\x16\xdf\xeao\n\xc1\xfdL8\xe2\xc4x\x98\x10\xe8\xc5i\n\xe0\xb0\x14:\x96y\"w1)\xe6\xb6\x87\x04\xd7|l\x89f\xaa\xf4\x04\"8\x80\xe4\x89\x879\x1a\xd0j]\xa6\xe6\x17n|\x98\xf8?\xf2\xd0\xda\x87\xfcCD\n\x0b\xd1A\x82\xa9\xdd\nox\x97\x14\xc65Bc!z\x0eu!\xc4\xa9\xe0\x03C\x01\xd7\xddC\x08<>\xc4\xeea\xd9\x9dL\x80\xb0_\xbbD/\xebbo\x9bc\xebJty\x1f4\xce\xce\xd4\xf6\xb7U\x14-\x19\x0e\\\xb1\x15\x87>z\x9c\xd76\xf4okC;\xa3b`\xaa\xe1h\x1f\x99\xf7\xfda9\xf2\xd5\xe8\xf1\x1e\xff\xc5)\x94\xdcm\x82\x93$\xe2\xd7\xcd\x0d\xec=\xdc\xd9\xdd-~\xc7/\xe3\x1d\xfe\x8b\x92Q\xa8\xaa\xbc|\xbf\xd4\xf5p\xb8;\x1c\x0ek'\xf2\xc2:\x11\x9cb\xa9\x1fl\x99?\xbe\xcf\x1f\x9f\xe6\x8f\xaf\xf2\xc7\x0f\xf9\xe3\x8f\xf9\xe3e\xfe\xb8\xa8\x1d\xd6;\xeb\xb0\x1e\xfcz\x1a\xde\x07\x19\xc8D\xdfn\xf9\xc4\x0f\xd27\xd5X#\xbfs2\xa7X\xf4\x0b\xe7U\x8aE\xff\xe4\xb4M\xb1\xe8g\xc0\x88\xd2\xd5A\xfeP\x1fg\x9d\x8f#\xd2\xed\x9b:\x86\xe8'sK\xf9\nO:\x85\xfa\xa8\xbe}Kx\xa0R\xce)\xd5\x7f\x8b\xec\xa3\x85\x04%\xa5\x9d\xc4x<\x9do]\xba\x8c|,;\xcb\x1f\xdf\xe4\x8f\x97\xf9\xe3\xfb\xfc\xf1i\xfe\xf8*\x7f\xfc\x90?\xfe\x98?.\xf2\xc7\xeb\xfcq\x9d?n\xf2\xc7\xe3\xfc\xf1*\x7f<\xcf\x1f/\xf2\xc7\x8f\xf9\xe3\xf3\xfc\xf1713{V\x17C\x82\x07\x839\x8a\x97\xbf\xed\x10\x0bb\xf2\x06\x0e[\xff\x13a\x05c\xdd\xef\xd7\x9a\xcdS\xff\xe3m'@\x91\xdd\x9a'\x02\xe2\xe6\x8a\xa7\xa3\x861\x83\xca\xffB\xb3\x9c\xa3\xfa'\xe2'=\x81.\xe7\xf50\x9b=_\x07Q\x01&\xfcqL\xc9\xeb\xa0\x0b\xffp\xe7\xc4L\xa2\xd2\xa2\xb63{\x98K\xc8A1\xb2V\xfa\x83\x83g\xe65A\xfb\xcf\x8d\xd0~\x0f3\x934+\xf7\xe4\x9fb\xa4s\xaa\\p\xcaV\x1aI\xc8LK\x84\xd0\x111h\xfb\x80\x0e;\x9c]\xdb\xdf\x19\"\x11P\x8dO\x1a!WL\xdf\xec\xef\x8c\x06\x90\x07+\xdd\xd9\xdd\xe1\xcc6\n\xa6^\xbb\xc3\xc1\x08\xbd\x96\x19lS\xeb\x949f[|\xd6%\x1e\x8e/\x1b\xa7\xdd\xc6$\xf3z+\xcce\xbb\x87\xd0AJ\xe6\xdf\xfc\xe2\x99@:\x8df0\xa6[\xee\xb5\xd9\x1bM\xff\x93\xba\xd4\xba=\xf3(}\xa8\xb9!\x11\xfc\xc1\xbee\x05\x99n\xb0\xdeDI\x12\x9c\xad\x84\xb7\xfb\x18\x02!\xaa$\x0b\x10\x8a=\xe64\x11v\x7f\xb8\xf5\xfc\xfc\xd7\xf64Rp(\xe95)\x00\xc4\x90k\x06-@\\D&\x85XRF\xf9E\xc8\xcf\x1b%\xd46\x7f7\"|\xa4\xde\xf1Q8]\x07\xb7K\x1e\xcam\xbalNC\xa7v\x86\xdf[\x19a\xdb\x909l\xe4(u{\x88\xb9/\xa9\xf4\x85a,\x8a\xf8\x99\xb2\xf1/E6\xfe{G\x98\xa2_\xd0\xfe1\xf8\xf39\xdb\xa4 \xaa\xde\xf0\x06^QN0\\\x81{M7MqZ\xd3\xd5\x8cff\xbfy\xecW\x8ad\x87cc\x95\xda\x90\xd3\x06\x83,#\x9b\xdf\xa9\x97\x8f\xfeOA\xc6G\x87\xbe\xcc\xb3\x17\xf4\x07r\xc8a\x8f\x8er\xd8\x83\xce\x10C\xdf\xa8\x9f\x03Cj\xe0\x04\x14\x94P\x13\xe5$\xad\n\xf9\xe9,\xed\x01E\x85+r\xb9\xe5\x14\xa6\xbc\xf9y\x0fV=\xb4\xff\xa8\xbaIq\x00Ea\x87z\x85\xbe=\xf2MU\\\x86\x02;W\x93P\n\x8dX\xae$Q\xbbM\"@-al~\x13\x18\xda\xd1\x8a\x1aZ\xd4?.\xa0:\xa5\xee\\g Z\x12\xf8pF\xa9n([y\x9d\x05\"\x14D\xacDB,\n\xfa\xb6\xec \xf1`C\x0fE\xf6\x9c\xd5\x10\x1b\xceW&\xe2@\xedb\x1c$\xa1\xd6\x12\x91%\xc2)'p\x16\xd3h6\xeb \x1cCf\x80>\xe5`\xa7\xff\x08\xee\xf1t\xb58A\x02\xf8\xf1l\xf0\xa7\xdc\x9b\x823\x1e2\xeb\xbb\xac\xb3\x14[\x875\x8b\xc9\xcc'\"r\xd3\x84\x13\xaa\xe2\x11\x1c\xe5\xf1MS-\x1d{?\xf1\x97\xec\xdb\x92\xb5B\x8d\xe5\x1eM1\xee\xb3\xab\x94\x85\x0b\xb7z\x8e\xc8Fs\x0cYq\xb7\xf0\xc6/\x8d\xeeN>?\x02\x90\xc85V\xba\xd6\xf0\x83\xed\xbc\x7f\xcf\x92\x1f\xa3E\xb6\xaa\xc6.\xfd\xe8\xaf\xb2\xa2w\x1f:\x8a\xf5\xcfY\xfa,\n\x97\xc1\xf97\xd7\xefb\x0c\x86\xdb_D\x97\xe1*\xf2\x17T\x0e\x87\"\x1eB>\x80\xdc\xe9h4\x18j;h\xf8\xd4\xae\xf1*\xdb\x16\x18\x15\xbd\xa2\x92;\xe0C]\x86\xfd%K\xe7\x17^\xc5E+\x9f\x93qJmvU\xd51\x92-\xca\x97\xb8\x9fl\xd8\xfc)\xd6L\xccH2\xf7\xe7\x0dJ\xcb\xe1\xa6^?\xbd`\xe8\x07\x17\xe9\xe9F\xe5\x9f:E\x91y\x14\x80\x9aSM\xbe\x8c\xce\x88\xa8.\xed'\xa9\x9ff \x1c\x1d\xc2\xee\x00\xd3[\x04\xfdl\xb3\xf0S\xf62\xf2\x17Ax\xfe\x06\xdf\xbb\xce\x12\x1d\x17i@\x9c\xb3\xb8e\xb5w\xf1\xcaux\xc1<\n\x93h\xc5\xfa\xa8\x14se\xffo\xd9U\xaa\x91'Y\xbc\xe2@\x86\x17\x07R\x89\xcc\xe5[)\xdcQ\x7f\xf1\xd7+\xea\xc1s\xc3~\xca\xae\xca!\xb4\xa1\xaaF\xfb[\x9d\x1f\x1d\xf2\xcfY\xda\x12\xd2R^\xf78t\xcbw\x15L\x80\xc1\x18\xa6l\xf6\xf7\xc2\x12\xa5s\xaf\x08w~\xfa\xf7\x0c^\x84H\x91\xcb\x1b<\xef\x0b&\x10\x83)9\x93\xd4\xc7\x96\x83\x17\x16[F5\x9a;\xdc\x7fT\xea1\x11#\xd9-\xe2!j\x93\x02I\x92\x0b\x06\x07\xbcL\xbe\xf0\xdc\xa0\x07I\xff\xdd\xebo\x9f\xbe}\xfe\xfe\xd9\xab\x93\x17\xc7\xdf\xbd\xe9\xb5\xdc>\x0c\x0e\x8d\x80\xeccp\xd1\x7f\xbc\xf1\\\xd6\xdf\xf8\xd7\xfc\xa8\xeb(\xde3\xf7\xfa\xf6\xd5w\xdf\xbdl\xdb\xab\xbc9U\x07f\xb5/\x02UEt\xa2\x86\x9c\xf0\x97=\xe8\xc4\xc5\xd1\x05\xc2\xf3t\xe6}\xc5\xf7\xf9\xc1\x83\xff\x03\x14J\xe2G\n\xdb\xf4\xee\xa7\x97\x87\xc9\xa5\x7f~\xce\xe2\xed,\xd8\xe6xg\xe1\xaf\xa2\x90m\xa3N$\xed\xff\x96\xf4\xd7\xfe\xe6\xff\x07\x00\x00\xff\xffPK\x07\x08v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00swagger-ui.cssUT\x05\x00\x01\x80Cm8\xec\xfd{s\xdb8\xb27\x8e\xff\xff\xbc\n=\xbb\x95\x9a\x99\x1dS!EQ\x17\xabf\xeb\xc8\xb1\x93q6r\xc6\xcem\x92\xad\xad)\x8a\x84$\xda\xe0\xe5\x90\xd4\xcdz\xf6\xbd\xff\x8aw\\\x1a $;s\xf6\xf7\xad\xb3\xd9dl\xe2\xd3\x8dFw\x03h4\x00\xb2\x9bl\xed\xe5\x12\xc5\xda\xda;\xfc\x9fN\xe7\xe5\xdf\xfeo'\x08c\xdf\xc6\xde#\xea:I\xd2\xd9\x0c\xbbzW\xef\xfc\xbf\xce\xec\xfac\xe7\x9d\xe7\xa0 A\x9d\xff\xd7Yz\xe9j=\xef:\xa1\xff2@N\x88\xed\xe4%M\xf7\xb7\x97\x8b0H\xb5\x85\xed{x\x7f\x9e\xd8A\xa2%(\xf6\x16\x13'\xc4a|\xfeWs\xde7,\xe3\xdfD\xfd\x9dU\xea\xe3\x03\xf6\x02\xa4\xad\x90\xb7\\\xa5\xe7F\xd7\xb0&\x9a\x9fh)\xda\xa5Z\xe2=\"\xcdv\xef\xd7Izn\xe8\xfa\x8b\x89\xb6E\xf3\x07/\x85K)\xce\xf3\xd0\xdd\x1f|;^z\xc1\xb9N\x95\xd8q\xea9\x18\x9dQ\xcf\x12\xcf\xa5\x9f,\xc20E1\xf5h\x85l\x97y\x14\xd8\x1b\xea\xf7\x049\xa9\x17\x06\x07\xd7K\"l\xef\xcf\xe78t\x1e\xe8\x16\x1b\x87\\K\x99\xf0\xe7=\xe4OJ\x19\xbb\x83!\xf2;\xb4\xa4\x0bo\xe9\xd8Q\xc6\xf0\x8cy\xbc\x8eii}\xdb\x93UZPT\xea0\x90\xdf\xe9\xeb\xd1\x8e\x96+>T\xca\x9d\x87\xbbL\xe4\xdd2\x1f:\x16a\xec\xf3\xca\xfbg\xba\x8f\xd0/1JP\xfa\xaf3\xbe Y\xcf}\x8f)\x01*\xcbf\xb5\x92\xa2(\xfdW=\xb6\xdaQ\x84\xec\xd8\x0e\x1ct^\x14\x01\xd5\x974\xe7\xe7\x9a\x1f>j\x8b\xd0Y'\x9a\x17\x04\xcc\xd4C\x8a\xaa\x04-\x85o\xc1\x16\x95\xf3 \xde\xeb&\x91\xed\xba\xd9l\xa0K\xda\xd0\xb0\x89\xbd`)n@+\xae\x92^\x02,E\xa7\x11\x87p\x9df\xbevnD\xbbr\xec\xed\\\xe4\xc0\x8fh\x972\xb3$\xc2n\x82\xd2C\xd5\xb0\xaei!\xbf\xd3\x1d\xe6\xff\x0e\xb8a\x01\xa3%\n\\h\xda\xac\xe7\x14j\xd6$\x9e\x16\x83a5\xacW\xdd>\xb5\xe7\x18M|{\xa7m=7]\x15\x1d\xa5\xd6\xf2d\xbb\xf2R\xa4\xe5\x83\xf4y\x11y1Sl\xb8\x8cQ\x92\x80\x83\x8f\xd2(Xw\xe1\xbaw\xd9\xeb4\x04\xac\xeb\xac\x90\xf30\x0fwP\x1f\x89m\xd7\x0b\xffu\x92Vd\x0e\x15\xac\xfd9\x8a3\xef-\x19\xe7^\xa9%\x91\x17h@\x17\x14\x10\x85\xeb\x94&:\x94C\x90\xa0\xa1 \xb2cg\x05v\xdfLY\xb9\xc7LJ\x0f\xd3\xc2\xc5\"A\xe9\xb9\xd6cB+\x8aU#K\xf1@s2nX\xdc\x06\x11]\x13\\@\xd2q#[C\xbf\xf00\xd2\xd6\x11\x0em\xb7R\x82pt\xcaG\xed\xcaO\xe9X\x00\xa5\xb6\x87\x13:\nE\xc1Z\x12\x85&k\xdf\xb7\xe3}\x8d\xc0^\x92j^\xca\xf4*\xc7\x0e66\xec\xc4\xb4V\x8b \xed_\xcc$\xe4G\xd8N\x115\x93Rd]\x17\xcd\xd7\xcb\xce\xdf\xa8q! \xb1\xe7v\x96!v\x01\xac\x96\xf7;\x90\xe2\xaf\x8b\xc5\x02\xa2\x98c\xdby\x80)\xd8\xf8\xa7\xa4X\xc6\x9eK\x04Ndx\xdbY\xc7\xf8G\xd7N\xeds\xcf\xb7\x97\xe8e\x14,'Y\xf7\x1d\xf4\xcf\xbc\xcf\x17\xef\xef\xb6\xfa?\xde,\xc3\xe9t:\xbd\xf9\xf0iu\xf5i\x99\xfd\x98\xffs\xfdj\xfau:\x9d^^]\x0e\x07\xef\xb2\x07o~\xbf{\xfd\xe5\xd7\xbb\x8f\xf3\xde7\xdd\xed\xbd\xde\x7f\xbb\xbd\xb8\xf8\xf6f\xec}\xfbp\xf1v\xfe\xe5u\xf0\xed\xf3[\xfc\xf5\xcb\x9d\xe58\x18\xff\x96\x11\xecW\xd1\xe7\xd7+\xfd\xcb\x951{\xef\xdfl\xe6\x1f\xacU\x81\xb7\xfa\xf3\xdf\xa7\xc5\xff.\xb7/\xd1\xaf\x17\xab\xaf\xbd\x14\xbb\xaf.\xbco_\xdch~\xaf{\xc3\xe1\xfa\xe5\xb5w\x11}\xbb\xd4\xbd\xcf\x8f\x9fofW\xc6\xf6\xb6\xf79\xb4?\xad\x06\x8e\xff\xf9#z\xb0>}5\xa3\xf8\xeb#~\xb8\xbe\x1f\xfd|}\xb9\xeb\xbf\x0fV\xa9\xf3\xc6\xc0\xee\x9b\xab%zc$\xf3`6@\x97\xba\xf7\xf5\xcb\xdd\xe6\xab\xffi\x90\xfd>\xff\xf2Y\xff\xfaa\xe4]\xff\xba\x1c\xa07\xc6\xd6}\x93\x8c\xaf\x1f^?\xcc{o\xf1\xf5\xeb\xd5\xcd\xa7W\x17\x97s\xf3-\xbe\xbe\xfc\xb4\xbe\xf1\x8c\xfb\xd9\xc7\xab\xdd\xf5\xa5c\xbd\xbb\xbf2\xde_\xce\xf67\x1f\xb6\xcb\xd9\xfdtw\xf3a\xb4}\xffa\xb4\x9b\xbd\xd2\xb7\xb3\x8f\xe1nv\x19\xeeg\xaf\xa6\xcb\xeb\xea\xef}\x7f\xf9\xdb\xafo\x1f\xbe\xddG\x1f\xee\xae\xbe\xd6\xf28\xfe\x9d\xff\xdb\x87\xb7\xa1\xfb\xeb\xdd\xf6\xbd7\xda\xb8\xa6k\xbe\x0b\x9c\xc7w\xfex\xffm?\xda\xbd\xff\xf8`\xbd{\x9c\xee\xdf=^\xef\xdf\xfd\xfe\xf6\xe1\x9bg<\xa2/\x96\xfe\xf5\xf7e:\x0ff\xf7\x04\xdf\xabo\xbf\xdf\xdc;>\xde\xbao\xf0f\xee]\xec\xbf\xbd\xf9:\xf8\xfa\xe5\xed\xc6\xfd\xfdv|\xed]7:xcl?~\xd2\xc7\xd7\xfeJw\x7f\x9d\x0e\xde\xed\xc7kg_\xdb\xe2~\xde\xd37\xe8\xcd\xeb\xed\xbb\xc7\xab\xf5\xec\xd58\x9d\xe7\xfaY\xa5\xf37\xd6\xe3\xfb\xe0F\xff\xe4\x7f\xa6d\x9e\x07\xb3u\xa9\xd3\xf5\xd7\xde8}g\xaeV\xce\xab\xd1\xee\xdd\xfdt\xe3\x18w\x96\xf3\xe6\xd3\xe6\x93\xff\xf9qn~\xde\x7f\xed}\xfe\xf0\xed\xcb\xd7\xfbk\xef\xa2?\xff\xb2[;\x8fQf{EY\n9\x9c+\xe3\xe6\xfd\xc3\xdd\xe6\xab\xf99\xfd\xf6\xc5\xd2?|\xba\x1d_g\xb6~e=\xd8_n\x07\xb3\x8fw\x97\xef?~\xed\xdf\xe8\x9fz7\xfa\xe7\xd7\xb3\x8f\xaf_\xdf\xdc/{\xb3\xc7o\x97\xb7\xf7\x0f\xdb\x9b\x87\xdb\xfe\xec~\xb9\x9d]]\x13\xfc\xf0\xda1\xefVs\xff\x06\x13\xfc\"\x9a\xdf\xad\x1a\xbf\xcb\xe8\xd2\xf1?\xaf\xdc7\xe3\xfd\xe77\xe3\xcd\xfcR\xf7n\x0b\xfd,?\xbdYm\xdc7\xe3G\xfb\xcdx{}usy}y\xbd\x9d}\xfc\xb4\xfc\xc7\x95\xb1\xfa\xda\xc3\xeb\xbc\xec\xd5\x83\xf7\x9b7\x1d\x95v\x1a\xdc\xbd\xf9\xbc\xb7\x7f\xff\x86\xbf]}\xdb\xcf{\xfa\xd21\xef2\x1d\x0e\xec/\xd6\xa3\xfb\xe6\xf5\xfak\xef\xf3\xdb\xbbK\xdd\xcb\xf0\xef|\x1c}\xbb\x0c\xcd\x9b{g\x7f\xfbpk\xde\xdc\x7f5o\x1f?\xedf\x9f>\xf5n\xef\xdf\xbe\xba\xd5?\xedo.\xa7\xfd\xd9\xc7\xe9vv\x7fe\xce>\\\xd7\xfc\xbe\xbd\x19\xdf\xbb_\x0c<\x0f\xee\x08~w4\xbf\xc7V~\x9bL\xf6w&\xe0\x93\x99\xaf\xbe\x1a\xe7~\xf9\xe9\xe1\xeeM\x81+\xfa]\xde\x0f?\xf6\x97\xbf]\x8e\xfb\xce\x9b\xd7\xf7v\xef\xb3~\xfd\xe6\xf3:\xeb\xef\x8ew\xfd\xf2\xb7\xe4\xe2\xc3\xcfof\xd9\x08q\xff\xe1\xd3\xdd\xc5\xe7_\xef\xed\xaf\x9b\xc7\x97/\x1fG\x97\xef\x92\xcb\xfe\xd2y\xf3\xbb\xf7\xf5j\xfa\xe6\xe2\xfa\x1fo.\x02\xf4\xf2\xe5\xe2u\xb4\x9d.\xb7\xd3\x8b\xf1hj\xbf\xeeE\xf7\xf8\xd3mF~\xf1\xf6\xee\x93u\x15?\xbc].\x97\xbf\xfc\xf2S'F\x11\xb2\xd3\x8e\xde\x11\x8e\xa4\x9a1x\xc6\xc1\xf4\"\x1f\xe6n\x8b\xc1t\xba\x18\xbd\x1c\xaf\xfew0\xfd\xdf\xc1\xf4?u0}\x7f\xf9u\x7fw\xbf\xba\xba\xbb\xcc\x06\xd3\xaf\xfb\xd6\xc1\xafe0m\xf8\xdd\xaa\xf1\xfb\x0f\x1aLo?\xb6\x0e~G\x0d\xa6\xb7\xed\x83\xf3\xf7\x19L7\xaf>\xe8\xc6u6\x18\xcd\xea\xc1\xd4\xbf\xeb\xbf\xb4~\xbex\xfd\xdb\xc5b:{\xed\xbf\x9c],w\xa3\xbb\xe9\x9b/\xaf\x02c:\xf5?,\xcd\xfe\xed\xe0\xe1\xe2\xf2\x1f\xb37\xb3\xcbW\xdb\xebWhv\x8d\xfc\xd7/\xad[{{\xe5E\xd3/\xdbO\xab\xed\xd5\xfd\xecr3\x9f~\xc1_\x1e6\x9f/\xb6\xeb\xd1\xe6\xf6zz1\xbd\xda^\xbc\x8aV\xa3O\x03G\xcf\xc7\xa5+\xfc\xfa\xe3\xc3\x87\xf5\xad\xff\xea\x95\xd2\x00<\xd2\xf2x\x97\x1c\x85\xb3`\x99\x1d~\xef#T\x8f\xbf/\xc7\xf7/\xfb\xb7\xd3\xafw\xbf\xaf\xa2o\xcb\xe9\xf4\xc3\xa7\x87\xff.\x03\xd9\xe6\x7f\xbf\xbdL\xa6\x17\xaf\xaf\xdc/71\xba\xcdF\xe6\xdbj\xe0|\xd9\xbf\x9d\xed\xec_\xeft\xe72\xdc\xbc\xebY\x8f\xef\xfcb\x1c{\x97\x8f\xb5\xe3\xfe\xd7\xdf\xa7\x9b\xd9\x87\xfe\xf6\xddv:\xfa\xcd\\m\xbf~\xb9\x89\xbf\xfd~\xbb\xfc\xea\x7f\x0e\xec/\xfd\xf1\xf5\xfa\xe7\xe1f\x7f\xbd\xb4\xbf\xdc\x8e\xaf\xb1c|\xfcxq\xe3\\\xdd`\xfb\x0d\xbeF\xc1[\xfc\xc9\x8c\xde\x7f~s3\xb0{3\xeb\xdb\xab\xeb\x97\xb9\x8f^f\xfd\xf7\"\xfd\xf6\xfb\xdd\xaa\x19#\x96\xe3\xeb\xb2\xee\xf7\xbe\xf5\xf8\xde\xcf\xc7\xe0M\xd6\xe7\xf31\xf9\xd7\xbb\xf8\xb7\x0fo\xab\xb9\xe2\xeb\xc7\xcf\xd3\xe5mo\xbc\xff\xf6aj\xbc\xbb\xff\x9a~}\xbc\xda\xcd>L\xcd\xf7\x1f\xfa\xbb\x9b\x8f\xcb\xc7\xd9\xfd\xa7\xa4\xec'\x9b\xd9\xe5\xc3f\xf6q\x9a\xce.\xaf\x06\xb3\x8f\xd3\xc1\xec\x9e\x18c_]g\xe3~\xed_\x8d<\x99/\xea^\xad\x1b\xd35\xdd\xbde\xce\xf6\xd6\xc6\xf1\x9d\xcd\xec\xe3\x83\xf5\xfe\xc3h;\xf3F\xfb\x99gd\xf4\xa9cf}\xf1u\xff\xdd\x17\xeb\xf1z\xdf\xf0\xbd{\xf3\xf9\xf1\xab\xf96r~\xbd\x8b\xe6\xbd\xfe2\x1b\xbf\xdf\xfb\xaf\xbd\xb9\xf9Y\xff\xed\xc351Nf\xe3\x00Q\xa7\xcc\x1e\xfb\xff\xc0\xb1\xf9\xf7\xe9\xe0\xd6|\x8b\xbf\xfe~\xb7q\xf0\xddf\xde\xdb\x12\xf3\xe2E87\xef6No\xb5q^]\\\xde\xee\xa7\xfb\xd9\xe5\x95q\xfdju\xf3\xf5\xcbM4\x0f\xb2\xb2eT\xf0\xb9\xb8\xf9\xf81z;\x0fn\xf4\xaf_\xac\xfbo\x9f\xf0\xd5o\x1f\xdef\xfc\xd7\xf6\x17\xfc\xf0\xfe\xe1z7\xbb\xbf\xd6\xdf\x7ft\x1eo\xee\xddW\xb3\xc7\xab\xdd\xdd\xc7o\xaff\x0fo/\xef>^\xeb\xb3\xcb\xe5nv9\xdd\xcf>:;\x82\xdf\xd5\xbcwc\xcc\xbf|^\xbbW\x0d\xbfoo(~z+\xbf|\xee\xac\xe7\x13\xec\xf8\xb8\xf7\xed\xcb\xdd\x1b\xc7\x1f\xa7\xd7\xbf\x16\xba|\xef\x8b\xe7\x85\xdb\xfb\xab\xfd\xec\xfe\xd6\xbay\xbc\xea\xdd\xe8\xd7\x8f\xf9\xbc\xf0p\xbd\xbf}\xb8y=\xbb\xbf\xdd\xbe\xbf\xbc\xda\xce.\xafw7\x8fW^\xc3O\xde\xfa7\x97\xa3\xf0\x1f\x97\xe3_\x7f{\xfc\xf4\xb2\x8d\xa6\xfd\xef\xe2\xe5v:\xbd{5\x9d^O\xa7\xcb\xcb\xe9\x87\xeb\xe9tuu1\xdd]]\xbc\x1c\xddN\xbfd\xe3\xe6\xed\x14\xf8\xdf\xd7\x8b\xe9\xed\x15\xf0\xfc\xfa\xeajzu1\x9d\xce.\x98\x82\x8b\xe9\xe5\xd5\xab\xa9~u7\x9d^]^\xf0<\xef\xae?\xbe\xbe\xf8\xf4\xe5\xea\xc3\xf5\xe6\xa5=\x9dn/\xa7\xb7\xd3WW\xb7\xb3\xbb\xe9\xe5h\x1a\xbe\x0f>~6n?^\x0e\xdf\xbeMV\xbf\x99\x9b\x0f3\xf3\xb7\x97/\xbf)\xcd/\xc6@m\x829*\xbe\xcf\xe6\xd7W\xb7\x0f_\x96\xbd\xe9\xff\xc6\xf7\xff\x7f\x1d\xdf\xab\xce\x01t\x1c\x9e\x8d\xad\x8asV\xcfH\xc9y\xab\x8c!U\xe7\xad\xc7\xcf\xbf\xe2\xed\xb7\x0f\xe3\x0f\xdf~\xbf\xd9\xb8\xbf\xbf\xbd\xcf|\xe9\x9b7{\xb6\xf8Y%\xae\xbfy\xfcj\xce\x1e\xde^\x15I\x97\x99!\x1f\xbf\xdb\xd7\x1d\x0d\xbf\xaf\xad\xfc\x9e-\xbeoOn\x1c\x15\xdf\xdf]\xb6\xf2\xfbN\xf1=\x1a\xbc5\x1f\xb2\x11\xe2\x91M\x96\xe8\x9f.\x93\xd9vv\xff\xe1.\xfc\xfa\x9b\xf5\xe6\xbf\xfb\x1f~\xbb\x99\xdf\xdd\x7f\x9e]\xdd\x1a\x8bWw\x97\xcb\x9f\xbd\xe0\xe5\xe0\xe7\xb7\xc6\xf4\xed\xa7]\xb2\x9c^\xbd\x99NM\xe3b\xfav\xf6A\x7f\xf3\xb5\x18\xcf?|\xfa\xfc\xfe\xee\x1f\xd6\xab\xaf\xd7\xd7\x92\x04J\xb3\x15C\x1f\x8e\xa1\x7f\x03\x8e\xcf\xccCwO=\xe0N\"\xb8\xf4A\x04\xd7\xa3\xcf\xcd\xb8\x98\xfe\x95\xdeZ\xae6\xe6\xe8\x87\xfc\x01\x9dE\x18\xfb\xf4F\xacA\xff\xda\xa3\x7f5\xe9_\xfb\xf4\xaf\x16\xfd\xeb\x80\xfe\x95?\x0b\xb4J}\xba\x15\xf9Nu\xb1\x89\x83|\xdb\xc3\xff\x12\x95\x96\xdbT\xa2\xe2\xc8N\x92m\x18\xbbB@\x8a\xc4\xbcS\xb4K\x85\x85\xeb\x98!,\xb64\xe9G\x1e\xbd\xc7c{\xf4.UH7\x9a>'\x101\xe7\x94\xca\xf3Q\xd4\xb3|\xd7\x93~BKPmK\xd2\x0fW\xf4\xaf\xb4-\xd6\xf8\x94\x0dH\xba7\xd8I\x84\x9cT\xcb\xf7\xd8\x0e\xe2\xf3%b\"M3\x06\xbbq\xb5\x9b\\\x9d0\xb2\x06\xdd\x9e\xf5BF5\xde\x19\x03\x96\xca\x18\x0e\xbb\xc3\xa1\x94\xac\xbf3Y\xaa\xa1\xbc\"s\xd7\xe7\xea1\xcd\xaeiJ\xa9\x06<\xd5`\xd0\x1d\xb4\xc8\xc6\xb7\xc8\xd2\xa5$\xa3\x9d\xc5U\xd3\xeb\xca\x1bd\xedF\\5\x03y5C\xbe\x9a\xa1\xd1\xed\xf7Z\xea\x19r\xf5\xf4\xe5\xf5\x18;\x83#a\xcf,2$\xc5\xc9\xb5C\xedq\xf6< \xf1:E\x934\x8c\xce\xf5I\\zd\xc9M\x9f`\xb4\xc8~'\xce\x0eT\xe7k\xb2\x9f\x1f5/p\xd1.\xfb\xe5\xdf\xff\xe5#\xd7\xb3;\x89\x13#\x14t\xec\xc0\xed\xfc\xe8{Ay\xea\xc0\xd4\x91\xff\xd3A,W\x90<\xa17d\xd4'u\x08\x80P\xadO\x00\x84\xed\xdd\x02\xaaM\xa9g\x00\x84*\x9d\x03\xaa\xaf\xbd\x7f@\x95)t\x11\xa8\xb2\xf6^\x02\xe9Q\xa5\xa3@\xb5\xb5\xf7\x15\x88J\xa9\xbb\xe4\x84\xcf\xdfc\x14\xbaL\xf9\xb0>\xbd3h\xe9G\xfeS\xba\x91\x7fb/\xe2\xe8\x14;\x11G\xa7\xd0\x87\xf8\xba\xd4\xba\x10G\xa7\xd4\x83\xf8\xda\x14:\x10_\x95J\xff\xe1\xabR\xe8>\xbc\x06\x95z\x0f_\x97B\xe7\xe1\x89\xd4\xfa\x8e\xff\xe7w\x9d\xb6^\x82\x9f\xd2K\xf0\x89\xbd\x84\xa3S\xec%\x1c\x9dB/\xe1\xebR\xeb%\x1c\x9dR/\xe1kS\xe8%|U*\xbd\x84\xafJ\xa1\x97\xf0\x1aT\xea%|]\n\xbd\x84'R\xeb%\xf8\xbb\xf4\x12\xb2^\xcf_\x1e\xe8c\xa0\xb4XN\xb8A1y\xce>?W\x9d?\xfd\xbf\x9e\x1f\x85qj\x07)K\x12\xa4\xb6\x17\x00D\xf9s\x82\xac}\xa6;\xf0\xc2d\xd3\xee)\xf2\xc0t\xacH\n2)\xcc\xbe\x85\xa0\xfeirBd\xc7\x89)\x94\x08\x9f&\x11D\xc6IDQ\xce\x97\x9a\x83\x82\x94v\x9d\"\x19t\x1e\x84\xe5O\x13\xa2\xac\xf6sn\x90\x98/\xb54\x8c\x8e\xe6\x93\x86\x11\xc7'\xef4Gs\xe2;\xc5\xbc\xea\xc7G\xf3*\xc88nY\xe7=\x9a\xd7\xf1\x8b\xab\xda*L_P\xaaN`\x98SX ms\n3\x89yNa'\xb1\xd0)\xec\xda\x82\x12\xd5\x11\xa51\xdd\xf1N'\xb2\xdc\xf1\x9c\xc4\x86;\x9e\x97\xccn\xc7s\x93\x99\xedxnmV\x93\x1a\x08\x1f]\x9d\xc8@\xc7s\x12\x1b\xe8x^2\x03\x1d\xcfMf\xa0\xe3\xb91QL\xb7<\xfe\xce\x1f\x83\x07a\x1aqL\x1389O\x94\xc2\xe4zMt\xfc\x18\\\xf1\x08\x92\x13\x84\x05\xa9\x14\xe4%\xe9\xda|[uD\xaa\x98\xfb\xa7\xb4\x03 Ri\x86\xaf\xdc\n\x89\xc0\xf8\x14\x81\x01\"\x15\x811)0\xed\xfb6}\xcf-g9)\x1f\x95\xd18s\xbb\xa7;O+\x9alt\x00\xe8\xb2\xc7\"\xda\xfa^]1\x1e\x00\xd4E\x81\x88~N\xdf_\x86\x18\x94%\"\x0e\xb8\xe2\x90wz\x80>\x7f.\xa2\x0e\x80{\x81\x94\xba\x8e\xef\x8bs;\x9f\xd2\x8f7\x03Av\x8a%\x08\xf2S\x8dA\xb08\xdd\x1e\x04\x93\xd3L\xc2\xa9\x0f\xb2\x8a\x82Y\x14\x86\x9b\xb9\x9d\xcd\xe3'\x98\xca\x7f\x92\xa5\xfc'\x1b\xca\x7f\x06;\xf9O4\x93\xffT+\xc1\x06\xc1'\x19\x04?\xc9 \xf8\xc9\x06\xc1\xcf`\x90'\x0ee\xac\xe6@\x83\xd04Zq\xd5\xaf\xa2\x13\xbc\xe3 \xc3\x05\xc8\x8eA\xb0a\x18\x1c\xd8\xb5\xe3\x07m\x19\xdb{\x06k\x9a&\x87\xf5=\x17\x82Z\x96\xc5A\x01\xd8p8\xe4`\x89\x877\xcd\x85\xef\x128\x1e\x8f9 .\x8c\x0d\xc1m\xdb\xe6%\x0d\xc3\x00\x92\xc1q\x1c\x01k\x00\x8c\x10\x82u\x9b\xdf\xd2d\xc0\x8b~\xf6\x87\xc3\x83P\xf6&g\x85\xd3\xc6:\x0d]%\xd8\xfeQ?\xd3_\x9ce\xb1\xf8Yw\xfc\x93\x80p\xd4B8\x12\x11\x0e[\x08\x87\"\xc2A\x0b\xe1@Dh\xb5\x10Z\"\xc2~\x0ba_Dh\xb6\x10\x9a\"\xc2^\x0baODh\xb4\x10\x1a\"B\xdd\x92\x13\xeaB\xed\xe8\xbd6\xd2\x9e\x98\xd6h%6 \xea|\x8c\xe1\x9c6^\xces\xda3\x1dt\xd8\x82\x88uX\x92\x08p\xd6\x82\x88uV\x92\x08p\xd4\x82\x88uT\x92\x08p\xd2\x82\x88uR\x92H\xa8\x08\xd6AI\"\xc09\x0b\"\xd69I\"\xc01\x0b\"\xd61I\"\xc0)\x0b\"\xd6)I\"\xc0!\x0b\"\xd6!I\"\xc8\x19K*\xd6\x9f(2\xb1+\xf1\x8eH\x11\x82N\x98O`1r\xd9\xc1{\xa8\xf7u~\x9c\xe5\x81\x8bE\xdf0\x07\x82Y\x01\x82\x0f{\x16?\x89\x84\xb1\x1d,\xf9\x81~`\x02\xf3\xf32\xc4<\xd7\xf9\x10@\xee\x11\xc6\xe1\x96\xc6\xf2\xaf\x0e\xa8\xa5\x85\xe0\x7f]\xcc\x17\x86\xcdO\xa8\xd1:\x8e0+\xb0\x85z\x8e\xcdO\xe6\x05w\x90\xc2\xee\x0f\xccE\x0f6J\xe4\x05l\x04\xe2Z\xba>\xe2\xad\xb2\nS\x08\x9d\x99f\xce\xcf\xa9 r\xa4\x0b\xa7v\x10o\x9b.\x1f\x8e\x94\xc1\x10B\x01\x837\xcc\xe1\xd0\xe2\x9b B\xc7\xf6x\xc8\x0b]E\x19<\xc1\x18\xa1\xb9\xc3\xeb$\xb07l@\xa2\xeb\xc6\xbc\xcf\xb3\xce\xa5\x9e\xe35k\x1b]\xef\xf7\xc7|\x08\x03 Mk\x88\\\x91W\x01\xf8\xf1\xc0q\x80 &\xc7\xa3\x04$q\\\x04\x91l\xedd\x85\\\x88`1X,\x16\xbc\xf4%\x01\xa4H4Z\xb8\x0b\xde{K\n\xb8s,\x16\x0e\x9a\x8bH\xa0\xde\xef.\\\xbe\x15d:\x91\"\x10f\x88\xe6\x9aV\xbe\xea\x84&\x80\xde\x7f\xd2\x9d\xc7\xf5\xd0\x1d\xdb\xae\xb7N\xce\xd9\xa1\"6\x18@\xd7\xe8Y1b\xd3\xadq\x8f\x85\x81(\x93EA\xa0>\x032\x00\x8cf\xe8\xac\xe4@R9\xd6\"\x0fc\x067\x1e\x8f\xc7\xc0\xea\xaf\xdew+\xc0y\x92<[iUz!\xd7\x90\xc5:P\xa41\xad\xd8U,\xe0UV\x1bbU\x96\xb5q+\xf7\x16[\xe4\x82*\xe2y\x15\xdb\x81\xa2\x96\xc8\x05kO\xb6\x1cX\xe7\"\xd3Q\"\xff\xe21\"\x17\x03\x90\xb0\x97\x01@\xd0\xd1x\x9c\xc8\xd7\x00\xa4\xc8\xddx\xa8\xdc\xe3\x98\x8c\xdfS\x9c\x8eO\xdd=\xd9\xefT\xa4Sw=\x86\xdb1\xde\xa7\xe0~*\xb9\xbeX'\x12oB\x97d!B\x8f\xe4\x80\x02\x87\xe4p\xb0?\xb20\xa1;r@\xa17\xb2\xc8\x16g|\xb6\x01\x90\xcbN>\xdd\x15\xdbe;\xc2\x13\xfd\xef\xe3\x88\x02\x9fc'!\xc0\xe7X\x88\xd0\xe78\xa0\xc0\xe78\x1c\xecs,L\xe8s\x1cP\xe8s\xc7M\xb9,\xbc6oc \xa2\xa0<\x9e\x06\xfb\x1c\x9b\x80}\xba\xcf\xe1\xe7\xf49|\xb2\xcf\xd1\xfc4\xadx d\xc5\xaeH\xf5\x02/\xe5-\x82\xf8,\xe4d\xa0\xf93\x0eZ\xdeF&\x91\xc0&f\xb6\x84\x08\x03D\xe3\xf2w\xd4\xb5\x0f\xd1\x07\xb8!\xdcn\x8f\xb4-\xd8\x92a\xb5\xc8(\x1cDd\x17\x1e\x08\x9b\x86\xc7\x81\xd6\xe1`\xa0\x818\x14l#&\xee\x15\x9a\x89\xdb\xbe\x17Z\x8a\x0f\xf5\x85\xc6b\xf7\xe2\xebm\xc0v\x83\xa9\x0cl[\"\x1a\x15\x1a\xd1W\xb4!\x8b\x13\x98\x90\x85\xc1\x16\xf4U\x0c\xe8+\xd9\xcfW3\x9f\xafj=68\x16\x1b\xcf?\xc1v\x023\xe1V3aE3\xb18\x81\x99X\x18l&\xacb&\xacd&\xacf&\xacj&6\x9e\x14\x9b \xc3f\xa2\x80\xc9\xcav\xc3\xadf\xd0\xd7\xba\xf3\x87\xe7zG\xef\xf4\xa3]\xa7\x17\xed:\xf4\xa6\xcbD \x05\xd6\xd4\x13\xd54R\xaa F\x815\x99PM\xbd\x92\xbe\xbd]r$Xc_Vc&\xb9\xaeP\x1f\x84\x03k\xb3\xa0\xda\xfa\xa5\xc4m\xb5\xc9p\n\x83\xf0\x01t\xa2lT\xff\xd3\xfcHR\xd9\xf3\xbb\x92\xa0\xb2\xef\xebM-\x95\xb6\x99\xf8x\x87\x12T\xf8,>\xa5\xe0T\n3{\xedi\xfe\x9f\xe8h\xc2\xba\xbe\x83\x9f\x81u}g7\x93\xd6\xd9f\xf4\x13\xbc\x0c\xac\xefOp2\x99?\xe1?\xd1\x9f\x84u}\x07\x7f\x02\xeb\xfa\xce\xfe$\xad\xb3\xcd\xbe'\xf8\x13X\xdf\xf3\xf8\x13Ua\x14\xa3\xfa\x0b\x1e\xda.\xff\xb4E\xfdq.m_~\x08\xa8\xf9\\W\xe2\xc4!\xa6?%\xd2\xcdb@=\xff\xe6\x11\x13\xb0\x15Q\x9f~\x80S\x89E\xa4\xa7W\x9fRb\x8a\xf3\xf0N?\x14\xe9I\xbe>#\xaf\x8f\x0fa\x8b*\x8d\xb2J \xc4-j5\xaaZyD^\xb1QT\xcc\x97fu\xf7\xf2\xba\xf9\xc8\xb8\xa8\xbbW\xd6\x0dD\xceE\xdd\xbd\xaan\x1e\x91\xd7\xdd+\xea\xe6K\xb3\xba\xcb\x86k\xa2\x96\xd7M\x07\x10e\xfdM\xe3\x01L.A\xd5|\xa0<\x97\xa1P\x80&\xd2@\xad\x02\x00Q\xc9P+\x01\xc0\x142\x94j\x00\xca\xab{\xd4\x9a\xb6\xf00>HoS+\xcc\xd0\x07\xde\x99\xb3\x98\x01\xf0\xe7\xc2'\xb3B\xc8-Ko\xcf\x8a\xa5\x0e_\xa4 \x9f\xcf\x1d\xbb\xaa[\xe4\x99u\xf5B\xe7o$\x10\xfb?!\x84\xc0\xc9+9D^Z\xcb!\xec\x08\x8d\x1c\xe2\xbe@\xc8!r\xf8J\x10\x89\xcf75\xc9\xdc\x9e\xa8K\xec\xf9u\xb3\x84\xce_\xcb#\xf6\x7fB\x1eI\x17 \xe5\x11\xf6\x82F\x9e\xb6\x8eP;\xad\xb0/(t\x06\x85p\xb5\xe8!\xbe\xa4\x83\xf8\xd2\xfe\xe1\xb7t\x0f_\xda;|y\xe7\xf0\xdb\xfa\x86\xdf\xde5\xfc\xb6\x9e\xe1\xcb;\x86\xdf\xd6/\xfc\xf6n\xe1\xb7\xf6\n\xbf\xb5S\xf8*}\xc2W\xe8\x12~[\x8f\xf0[;\x84\xaf\xd2\x1f|\x85\xee\xe0\xab\xf6\x06\xffI\x9dA\xe8\xf7X\xe2\xf7X\xea\xf7\xb8\xc5\xef\xb1\xd4\xef\xb1\xdc\xefq\x9b\xdf\xe3v\xbf\xc7m~\x8f\xe5~\x8f\xdb\xfc\x1e\xb7\xfb=n\xf5{\xdc\xea\xf7X\xc5\xef\xb1\x82\xdf\xe36\xbf\xc7\xad~\x8fU\xfc\x1e+\xf8=V\xf5\xfb\xb6\x80\x88&v\x16\xe7\xf6\x82}5j\xf6t\x8e\x16a\x8c\x0e\xe5\xc7{\xcf\xff\xd2\xf9\x0b\xfd\xe5A\x98\xcd\xc1\xc1\xc8\x8e\xcf\xe7a\xbab\x01\x87\xbf=\x86\x99o1\xcfqI\x92I\xc7\x14U\xdc\xf2\x960esqMAYt\xd2N\xb9\x93O\xa3b\x91\x9aRP\xaa\xa6\x18\x12\xac)U\xd8 V\x9d\x8e\x9dl\xa8\x93\x08\xecK\xe5\xf5e\xe2\xfa\xea\xd2\xc2\x82\xc9\x8c[\x17\xc2\x82a\x99`\x98\x12\x8c*u\x03\xd9\xe7\xfc<\xe6S\x81L\xf1\\\xf2A\xc2\xae\xeb\xcd\xdb?4\xd8u\xbd\x94E\x01\xfd\xc5m@`\xa9C\x17k\x0eb\x17\xddn\xaa\xc5\xe1\x96\x81\xc5\xe1\x16Bi\xcb8\\G<\xb6x\xceQ8!^\xfb\x01+A\xfeP\x80\x05+ \x8b8:m\xe1\xed\x90{(\x90\xd8\xde\x87\xeb\xf4<\x7fD\xbc\xfeJ\xa1\x7f\x1c\x18\xdbg=Lf~\xb2\x1c\xf6\x00\x12\x01;\x01\xcfC\xe0\x07\x00\x1046\x89\x83\xbd\x81C\x08\x1d\x82GJ}\x02\x84K\xdd\x02\x10\xa5\xdd3DDR\xe7\xc8\xd73R\xffPp\x10\x85\x01\xd4\xcd\x06:\xa9\xd3\xf8m>\xe3\xb7\xb9\x0c\xcbA\xe41\x1c\x0ev\x18\xbf\xcd_|Uwa\x81ro\x01\xd0rg\xe1\xe4P\xf0\x15\x98F\xee*\xfe\x93<\x05v\n,w\n\xdc\xe6\x14\xb8\xcd)X\x0e\"\xa7\xe0p\xb0S\xe06\xa7\xc0\xaaN\xc1\x02\xe5N\x01\xa0\xe5N\xc1\xc9\xa1\xe0\x140\x8d\xdc)p\x9bSPt\x0b\x8cvu%D\xee\xbd\x0e{5?\xd12\x10\xf9,\xfb\x9dfS\x9a\x08\xe4V\x99\x99aJ\x90\x90E\xc4c^R\xcd^\xa7!\xb5E\x90==7&\x95\x94\xe7F\xc7\xe8\xe4\xd9|\xfa\xb7\xc6\xeb\xf5\xfc\xe7\xea\x85\xa9@\x15\xf9\xe1S\xae\n\xbd\xa9\"\x7f\xe7A\xfd\x13\xc0\xa1\x8c$H\x1ea\xece\xeb\x89\xea\x0b\xe3\x13\xb2\xcc\xf5\xe2\xe2\x95\xff\xe5\x17\xcb\xeb\x9a\x88\x92\x82\xe5\x04|\nH\x90\xc5H@\xf5\xab0\xf6\x1e\xc3 =A\x808\xdc\xb2\xb5s\xfd#/\xdf\xc6vt\xa8\x19d\xbf\x9dg\xffL\xe8_A\xbd\x03\xa4\xc5\xc3 \xfb@P\xaf\x16\xa3\x0d\x8a\x13\x04\xd4_\x15M\xe0\xc7B+6,\x8f\xb6fU\xa3\xd0\x9c\xb4L\xa2R\xd8\xbc2\xb9Z\xcd,\x91\x8c`\x0d\xd8\x1b\x96\xc9K\x91\x9fhIj\xc7)%N\xf1\x19\xfd\xfcyS\x15\xf90\xff9\xff\xbcy\x92\x8f)\x05\x0f\x889\n\\\x805\n\\\x96q\xf6\x88c\x8b\x02\x17bZ\xbe\xe8\x93\xe7[\x14\xb0\xac\xcb\xa7$\xf7\xe2\x11\xc4{n'(\x1b\xc8\x00\xeeU\x11\xcb\xbf~N\xd6P=\x845\x1e\xa3\xd4Y\x81:\xcfKx\xad\x17\x8f\xc9\n\xcag4\xff\x04\xe1Ee\xd0\x8aE\x06\x07\xac\x97A\x85\xc6\xcb\xf9\xe4\xb6\x03\xb84\xa6jxp\x96\xca9T\x86\x02\x98PF\xc9\xf9@6\xc9\xb94&\x01\xf80\xca\xcf9\xc1\xba/uS\xaa\x1e\xd4\x0e\xa9\xe5\x9c\x13\xa8\xe4\xfbu\x92z\x8b=\xd0q\"\xdby`\xfb\x0d\xf1\xac\"\xac\xb2T\"\xedW8\xb6\xf3\xe4\xac\xa8\xbeS?\x01YsF\xa9Q|\x07\xca9\xb1\xfd\x87|\xc8\xd6\x00\x99\xab\xc2\xccQ\xbaE(\xe0+(\x01L\x0d\xd5S\xb6\x8a$\xb2\x1dT1\x83k\xb2\xf3\xd74\x1eh~\xae\x97\xa4\xb17_\xa7H\xc0\xb2\xa0\xa29\x96\x08\xb6\xf7\xe4A\x0da\xc3\xc29\xda,X1\xa3\xbaP\xc3\xaa\xe9Ar{Ul\xd8~\xd4p\xa2\xba\x91\xcc4\x15\xab\xda4<\xaf\xca\x0c43\x89\x11*\x9e\xac\x11\x1a\x96\x84% \xaer;0=\x95\xb4\x04\xd9Qk\x96P_-\x0e\xdf\xea\xccl\xebz\x81\x8d\x8bh\x9c\x88A\xb5\x1c|\xaeO\xca\xffB\x9c\x0c \xa7\x1e\xcb\xc9(9\x19\x10\xa7\x9e\x84\x93\xc9r\xea\x95\x9cz\x10'S\xc2\xa9\xcfr2KN&\xc4\xa9/\xe1d\xb1\x9c\xfa%\xa7>\xc4\xc9\x92p\x1a\xb0\x9c\xac\x92\x93\x05q\x1aH8\x0dYN\x83\x92\xd3\x00\xe24\x94p\x1a\xb1\x9c\x86%\xa7!\xc4i$\xe14f9\x8dJN#\x88\x13\xb6\x93T\xe6\x9cz\xf6?\x96\xe38\xfb\xdf\x84\xf8\x19\x085\x97Y\xd4\xa7\xcb\xd6C\xe5\xbbm7\xe8\\\x9f\xd4$\xe0\xca*\xe7e\xc8\x96o\x0d/\x83\xe0e\x00\xbc\x92U\xec\x05\x0f\x99d\x15i\x80\x966)F\x81\x00\x05)\x89\x0d\x80\xd8\xa0\x88\x0d\x85\\\xdb\x81\xe7O\xe4\xfd\x88\xc6\x9e\xbe\xa4\x86\x18>\xf7\xaaZc\x0e\x0c/\xbe\xcb\xc2\x1a\xac\xe5\xf8\xb55\xcbFmA\xf6\x9c\xcbk\x81\x04\xadK\xafgZa\xe7\xd5W<\x8e^d\xf3\xd4\xa7\xad\xb3a)\x9e\xba\xd4>\xcd\xb8\x7f\xcaj\xfbT\xab\x7f\xbf\x057+\xd1\xf3\xae\xb9a\xee\xcf\xb2\xec\x86Y?\xe3\xca\x1b\xae\xe0\xb9\x17\xdf\"\xfd?\xd7\xfa\x9b\xeabOY\x82\x8b\x18\x1d\xbb\n\x17\xf19a!.bu\xdaZ\\\xac\xa9\x13\x96\xe3\xacY\x9f\x7fE\x0e\xd6\xf0|\x8br\x90\xfd3\xaf\xcb\xc1:\xbe\xd3\xd2\x9c\xb2\xee3\xad\xce)\x9eO^\xa0\x0b\xb8\x9d\xb6F\x170;u\x99.`\xf7\xc4\x95\xba\x80\xeb\xd3\x17\xebB\xc3\x1c\xbb^\xe7\xe7\xeb',\xd9\xe5\xcc\x8e\\\xb5\xcb\x99\x1d\xb9p\x973;r\xed.gv\xe4\xf2]\xce\xec\xc8\x15\xbc\x9c\xd9\x91\x8bx9\xb3#\xd7\xf1rf\xc7/\xe5[\xfc\xf6\x89\xaby\x96\xfb\xe2i\x0bz\x90\xddS\xd6\xf4T\xf7?aY\x0f\xd3\xb3+{\x85\xa5\xbd\xc21\x9a\x9c\xa7\xff\xcc\xcb}\x9e\xdf\xb3\xaf\xf6\xfd?c\xb1\x0fTr\xc2Z\xdf?a5\xf8\xacK}P\x80\xd65\xdfs\xad\xf4\xfd\xa7,\xf4Y\xe2\x13\xd7\xf9\x90\x0cO^\xe6\x9fb\xd7?g\x95\x7f\x9a\xc1\xbf\xe3\"\xdf\xff\x9ek|\x88\xf9\xf3,\xf1!\xce\xcf\xb9\xc2\x87\xf8?\xfb\x02\x1f\xd6\xfd\xb3\xad\xef\xfdgZ\xde\xc3|\x8e^\xdd\xc3lNY\xdc\xc3\x9cN\\\xdb\x8b\xb4t\xca\xd2\xde\xff\xde+{\xa0\x82g\\\xd8\x03\xdc\x9f{]\x0fT\xf1\xbd\x96\xf5\xfe\xf3\xaf\xea\xfd\xe7\\\xd4\x83\xccN\\\xd3\x83\xbcN^\xd2\x83\xdc\x9e\xba\xa2\x07\x99>\xc3\x82^`\x93\xa3\xd7\xf3\xec\xcc\xfc\x94\xe5\xbc\x8c\xd7\xb1\xaby\x19\xafc\x17\xf32^\xc7\xae\xe5e\xbc\x8e]\xca\xcbx\x1d\xbb\x92\x97\xf1:v!/\xe3u\xec:^\xc6\xeb\x84e\xbc\xd4]\x9f\xba\x8a\x97\xae\xae\x8e^\xc4K\x17\x84'\xac\xe1\xfd\xa7-\xe1!\xf2\xe3V\xf0\xa2\xc5:~\xe6\xc5:\xcf\xef\xd9\x17\xeb\xf8\xcfX\xac\x03\x95\x9c\xb0X\xc7',\xea\x9eu\xb1\x0e\n\xd0\xbav{\xae\xc5:~\xcab\x9d%>q\xb1\x0e\xc9\xf0\xe4\xc5\xfa)v\xfds\x16\xeb\xa7\x19\xfc;.\xd6\xf1\xf7\\\xacC\xcc\x9fg\xb1\x0eq~\xce\xc5:\xc4\xff\xd9\x17\xeb\xb0\xee\x9fm\xb1\x8e\x9fi\xb1\x0e\xf39z\xb1\x0e\xb39e\xb1\x0es:q\xb1.\xd2\xd2)\x8bu\xfc\xbd\x17\xeb@\x05\xcf\xb8X\x07\xb8?\xf7b\x1d\xa8\xe2{-\xd6\xf1\xf3/\xd6\xf1s.\xd6Af'.\xd6A^'/\xd6AnO]\xac\x83L\x9fa\xb1.\xb0\xc9\xd1\x8buvf~\xcab]\xc6\xeb\xd8\xc5\xba\x8c\xd7\xb1\x8bu\x19\xafc\x17\xeb2^\xc7.\xd6e\xbc\x8e]\xac\xcbx\x1d\xbbX\x97\xf1:v\xb1.\xe3u\xc2b]\xea\xaeO]\xacKWWG/\xd6\xa5\x0b\xc2\x13\x16\xeb\xf8i\x8bu\x88\x9c[\xac3\xf4\x87\x05\x0e\xed4\x7fG\xce\xe4\x0fz-\xcc@\xe3\x12\x9a\xbf1\xa7\x05\x1b\x94\xd8\x93\xde\x82\xb4\xc8\xdf\x82\xa4.W\x83V\x12\xad\x81+\xbcYH\xfd\xfc\x81\xe6\x1f#\xb2\x7f\x94\xc4\xbe\xba\xc0\xb0l\xc7\x98\xb9\x06\xab\xc9\x86)\xd9\xa8\xd2\xc4\x0e\x12-A\xb1\xb78,\xc2 \xd5\x16\xb6\xef\xe1\xfd\xb9fG\x11FZ\xb2OR\xe4\x9f]`/x\x98\xd9\xce\x87\xfc\xd7\xd7a\x90\x9e\xd9\x1b\x14xq'@\xbb\xea\xe7\xb3\x15\xc2\x1b\x94-r\x9b\x9f:\x01Z\xa3\xb3\xf5|\x1d\xa4\xeb\xb38\x9c\x87ix\x16d\xff$h\x19\xa2\xce\xda;\xb3c\xcf\xc6g\x8d\x14\x8ct\x9c`K\x14\xc6K\xcf>\x83\xc0\xb9t\x9a\xa0E\xc2*J*\x9e\x80\xc7:\xa1\x8b\xa8\xf7\xa0e\x0f(\xa2Wa\x90\x84\xd8N\xce\xfc0\xb0\x9d0\xfbO\x98G\x13,\xa3u\xec\xa1\x98!\xcd\x9fun2\x95\x96\x00\x11}\xad`\x8a\x03\xa3\xf6\xc6\x1e\xa2\xb6\x17\x86\xa3x\x00v\x15R\xa7+\x84\xed\x84&/\x9e\x9dI\xccT\x16\xa9Z5\xf5|D\xd7\x91?\x81\xa0\xf3\xd0\x0d\x03\x8f\xc2^\xe4\x8f:\xb3\x8f\x10\xde\xb1\xb1\x97\xa4!m\x85\xe2\x99\x80bi\xc7\xb6\x1f\x06.-|\xf9\x10\x14\xc9N\x1eP\xbc\xf10\xa6\xfd\x84x\x0e\x91\x95\x8d(>\xa1\xe5\xa56\xf6\x98\x0f_/\x12\xad\xc8\xc3\x91\xc0\xe2\x89\xc2`I\x8f=\xf9;\xafT\xebc\xb0e\x95\nu*\x0c\xd0^6\x88\xaa\xca\xe1\x1f-\x06X#V\xaf\x11\xd25\x8d%M\xb2-r\xc8}\xee\x93\xefT1\xf7E\xf8\xc5\xd6\xa0\x00\x06\x0f\xe8Q\x80\x1e\x0f0)\x00\xf7y\xfa\xc5\xb6/\x17q\xb1\xb5(\x80\xc5\x03\x06\x14`\xc0\x03\x86m\xcd\x1cQ\x80\x11\x0f\x18S\x80\xb1~\xfc\x9b\xba\x19\x8f\x15Z\x84E@Fa1\x90]X\x0cd\x1a\x16\x03Y\xa7U\xe2E\xf1\xb9\xb36\x1b\xb1\x18\xc8L\nm\x1f\xb1\x18\xc8X,&\xb3\x97\x82\xc1\x14F\x05\xba\xbf\x8b\x8d\xe8\xb7\xb5\xc3` \xa0 \xfdv\x0b\xfa\xed\x06l\x11v\x91\x7f\xed\xac\xd5|~\xbb\xf5Z\x1b=b \xa0\xed\xfc#M'\xb6R\xdb\xe0\xc7\x00@+\xe1v+\xe1v+\xe1v+\xb5\x08\xbb\xc8?v\xd6j%\xdcn\xa5\xd6F\x8f\x18\x08h%\xcc[\x89\xc2xA\xb4N\xb5\x18%\xa8\xb9\xdfnG\x11\xb2c;p\x8a/qN4?|d\x1f2&Z\xa7i\x18\x14l\xce\xcfs\xfc\"t\xd6\x89\xe6\x05\x01\xfb\x16`\xa2F\x1eZ~\x86\xed\\\x9fD\xb6\xebz\xc1\x92]\x18\xaf\x8cC\xb9\xd1\xca\xbf>y\xd5\xab\xca\xf8\xd7\x19\xaf\xcc\xaa\xac\xcf\x97\xf5\xab\xb2\x11_f\xd5\xf5\x0d\xf8B\xadW\x17\xf7\xac\x17l\xa1\xa5W\x85\x16\xfb\xa9\xe5\x956\xac)\x87<\xa5\xa1\xd7\xa4\xfcg\x9a\xf3\xcd\xe6\x1cBl;\xf3\xb0\x0d-\xddf\xc5\x15\x93\xf2\x01\xc5\xa4\x84@1-#\x0b\xc8D\xdb@R\xb2\xc0U\xf1\xce\xb9\x12\x90\xfd\xcc\x96{\xc1\n\xc5^ZA\xca_\x15\xe6\x89\x03\xe39\xd9t#q\x1e\xa2\x18\xf2\x1f\xa2\x18r!\xa2\x18\xf2\"\xb2n\xd8\x91\xc8\xea!_\"\xcaAw\"\xcaa\x8f\"E\x10;U\x86j\xf7+JX\xd0\xb5(qA\xef\xa2\x04\x86\x1d\x8c\x16Y\xecc\xbc\xd0\xb0\x9b\x11\xfc$\x9eF\xa0*gS\xf06\x85\xa8d\x95E\x132\x0f\xf4\xa5\x0e\xe8K\xfd\xcf\x97\xba\x9f\xdf\xe6}\xbe\xdc\xf9|\xb9\xef\xf9-\xae\xe7\xabx\x9e\xaf\xe2x~\x9b\xdf\xf9mn\xe7\xb7z\x9d\xaf\xe6t\xac\xbc\x02\x9f\xf3U\\\xce?\xce\xe3`\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2m\xce\x85\xe5\xce\x85\xe5\xce\x85[\x9c\x0b\xab8\x17Vq.\xdc\xe6\\\xb8\xcd\xb9p\xabsa5\xe7b\xe5\x158\x17Vq.\xcc9\x17\x05Lc\xdby@\xee\x01\xa34E\xb1\x96D\xb6\x93E^]\x83\xfb>E\x01\xd4\xd2\x8c\x19\x0b\xd7\xba\xba%\"\xf0\xd1\xd2\xe6\xd8\xf72x\xfb\xb8z\x009\xe6\xdf/:F\\\x80\xa2Mb\xa8\x92\\h\x05\xa9\x15f\x83\xba\xaac[\xc2\x11\xb46\x84\xafB\xa1\x1d\x12\x91\xf1\xb1\"s\x04\xad\"\xf3U\x14\"S\x14x\xa5%!\xf6\xdcC\xbe\x8f^u\x16\x0e\x93z)F4\xa6\xdb\xb38\x98\x13F{\x06e)\x98\xfa\x00\x8a\x94;O\xbbT\x1cL$\x18\x0f\xb4\x9e\xc9\x0fk\x89}%\x81}EyY\\\x9b\xb82\xc9\xb0\x92dXQ2\x16g\xb1^\xe5\x05\x0f\x87\x14\xedR\xcdEN\x18\xdb\xe5 Vv\xd1\x9b\xc1\xce\xb8'\xe7\xb6\x93z\x1b\x04\x14\xe4\xcb\\\xe0\xf9*\xdc\xb0k\xe4\xfc\xb9\x80\xff\xc6K\xbc\x145o\x1cMc;H\xbc\xea\\g\x18w\xba\x86\x95t\x90\x9d \xcd\x0b&\xd2R\xbe=\x85\x90\x87p\x9df*:7\xa2]\xc7\x0d\xd3\x14\xb9\x1dg\x1d\xc7(H_eLX\xba$=d\xff\x14Yn-\xddGP\x8e\xc0\xdf\x16\xab\xc1\xda\x15\x81\xd9zk\x90\xe5\\,\xe1o{D9\x1f\xc6\xf8[\x93(\xe7\x03\x19\x7f\xdb'\xca\xf9P\xc6\xdfZd\xfd|0\xe3o\x07\x04\xc0\x84$\x18\x92\x12@U\x8c\x08\xc0\x00\x92qL\x00\xc6\x90\x0c\xc5+\xd4\x1b\xd0I\x9b\xf1\x859\xf2\x85\x93\xdc\"\x0c\x042\n\x0d\x01\xedBC@\xd3\xd0\x10\xd0:\x8c,\xa0\x81h\x0cl#F\x1a\xd0L4\x06\xb6\x14\x8d\x11\x1b\x8b\xc6)\xec\xf6\xab\x8e\xdd\xa5\x15\xfdV#\xfa\xad6\xf4[M\xe8\xb7Z\xd0o5\xa0\xdfn?\xbf\xdd|~\xbb\xf5\xfcv\xe3\xf9j\xb6\xf3\x8f3\x9d\xd8J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xddJ\xb8\xddJ\xb8\xddJ\xb8\xddJX\xcdJ\x98\xb3\x12\x05\xdb\x1a\x07\x91Z\xb7\xbd\x83H\x9f[\xf3 R\xe4\xb6\x7f\x10ipk\x1d\x84\xaa\xcb<\xa1*e=`\xab\xf5\xaa\xb2\x1ePVq\xe5\xd6\xd0[\xcd\xac\xe8L\x9e\xce\xac\xda`\x9a|Y\xd5\x08\xb3\xcf\x95\xf5+\x9e}\x9e\xa7U\x95q\x0b\xf6\xad6\xa8\xca\x06|\xd9\xb0*\x1b\x02eU\xfb\xb8U\xfeV\x1bUt#\x9en\\\x95\x8d\xf9\xb2,\xe0\x10\xf5\xb7\xad\x96\xae\xbc\xd8\xad\x95\xd35\xb3\xff\xf1\xa0mX\x00\x93\xaaY\x83\xee`0\x18\x0c9d\x9e\xc7.0\xf9b\xbc}\x80?0.\x9aM\x13b/mJ!GmJ!_mJ!w%\xea\x85=\x96\x00@NKH\x06\xf9-Q\x0c\xb9nS\x0cz/Q\x0c90Q\x0c\xf90\xa1\x16\xc8\x8d\x9bb\xd0\x93\x9bb\xd0\x99\x9bb\xd0\x9f\x89b\xc8\xa5 \x9b@^\xdd\x14\xc3\x8eM\xdaD\xe0\xdb\xa4\xeaZ\xdd\x9bh\xab\xcc\xc3\x1bX\xee\xe4\n^\xae\x10\xc6\xe4\x01\x8a\xc4\xf3}\x99\xe3\xfb2\xbf\xf7en\xef\xb7x\xbd/uz_\xea\xf3\xbe\xd4\xe5}\xa9\xc7\xfbR\x87\xf7\xa5\xfe\xeeK\xdd\xdd\x97z\xbb/uv_\xea\xeb\xbe\xd4\xd5}\xa9\xa7\xfbrG\xf7[\xfd\xdc?\xc2\xcd}%/\xf7\xd5\x9d\x1c\xf6g,\xf3g,\xf3g,\xf3g,\xf3g\xdc\xe2\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xee\xcf\xb8\xd5\x9f\xf1\x11\xfe\x8c\x95\xfc\x19S\xfeL!\xc2\x0d\x8a\x178\xdcj\x1b/\xf1\xe6\x18\x1d\xaa\x07\xe7\xe5\x03\x01|\xe5\xb9.\n\x1at\xf1\xbb\x00\x9c8q\x88q\x03.~\x17\x80\xf3H\xaa\x86\xf2;\x1b5p\xc7\xc9\xac\xedZ\xa4\xde\xb1rk;\xb9\xe4;Vvm'\x97~G\xcb\xaf\xedd-\xd8\xf3-\xd8\xb7\xb4`\xcf\xb5`/o\xc1\x9ek\xc1^\xde\x82=\xd3\x82\xfdi\x01-\xebXY\xe8p\x94oQ\x04\n\xeeE\xe1[=\x8cB\xab8\x19I\xa0\xecg\x0c\x91\x92\xab14\n\xde\xc6P\xa88\x1cE\xa2\xeas\x0c\x91\x92\xdb14\n\x9e\xc7P(\xcc\xc1\xaa\x81&\xe7\x92\xfe\x91\x1e\xe9\x1f\xe7\x90\xfe1\xfe\xe8\x1f\xe9\x8e\xfe \xde\xe8\x1f\xef\x8c\xfe\xb1\xbe\xe8\x1f\xed\x8a\xfe \x9e\xe8\x1f\xef\x88\xfe\xb1~\xe8\x1f\xe9\x86*\x1e\x87\x8f\xf48|\x9c\xc7\x1d3\xc7\x92`%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dki\x02%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dsi\x02 XKR;\xf5\x9cCq\x055\xcc\xdf\x8d\x91\xb2\xb7Ob\x84\xf3;\xa2\x0d\xaazB\xe3\xecy\x12\xe2uJ\xe0\xaa'4\xae\xf8\xa8~\x0d\xca\x7fU\x18\x8e\x0f\x80\xe0\xd9\xc8\xae$;\x05\x94\x8bOA%-\xa0pE#\x14Z\xa10\xa9\x94M\xf3\x15[\xe6+7\xccWk\x97\x7f\\\xb3\xc4-\xc0\x8a-\xc0\xca-\xc0j-\xc0\\\x0b\xe8N\x92'r\xc3\xc8v\xbct\xcf\xbdu@\x1b7e\xdd1[8\"\n\xd9\xbb\xe9\xda\x90(d/\xc1k\x03\xa2\x90\xbdm\xafYD!{\xad_\xeb\x13\x85\xec\xfb\x034\x93(d_T\xa0\xf5\x88B\xf6\x8d\x08\x9aA\x14rJ\xd0\xad\xa6P\xe7$\xd2{d1{0\"\xd4\x1a\xce\xccy\xfb8L\xed\x14i}\x8b>o\xb0\x08c\xff\xbc(\xfb\xb1o\xb9h\xf9\xd3D\xf0\x1cd7\xd6\xc5\xec\xc6:\xcc\xaex\x0e\xb23L\x89x\x86)\x90\xaf,\x809\x8e$\x12\x1a#\x81\x88e\x01\xc8\xb1\xd7\x93\xc8\xd8\xeb d,\x0b`\x8eC\x89\x8c\xbd\xa1@\xc6\xb2\x00\xe4h\x1a\x12\x19MC cY\xa00\x96\x1e`\xd7\xd2\x88\x0f\x1c<\x8fwI9\x9e\xe6`R\x96\xa7\xfa\x98\x9c\xe9\x89n&ez\xaa\xa7\xc9\x99\x9e\xe8lR\xa6\xad\xfe\xa6\xe0p\n\x93w\xe3\x85\xfes;\xa1\x84\xe1\x89>(\xe1x\xb2\x0b\xcax\x9e\xea\x81\x12\x9e';\xa0\x8c\xe7\xa9\xfe'\xe1\xf9D\xf7\x93z\x1a~nO\x930<\xd1\xd3$\x1cO\xf64\x19\xcfS=M\xc2\xf3dO\x93\xf1<\xd5\xd3$<\xdb=\x8db:\xc7\xb6\xf3\x90EP\xf9y\xce\xf3x9\xb7\x7f\xd4\xcf\xb2?\xdd\xf1O\x10t\x04AG t\x08A\x87 t\x00A\x07 \xd4\x82\xa0\x16\x08\xedC\xd0>\x085!\xa8 B{\x10\xb4\x07B\x0d\x08j\x80P\xdd\x02\xa0:\xdb\xae\xed\xca+\x02\xde\x02\xbbJp\x8e}qf\xe8\xfa\x0b\xded\x05|$\x82\xb3f+\xe0C\x11\x9c5]\x01\x1f\x88\xe0\xac\xf9\n\xb8%\x82\xc3M\xed\x8b\xe0\xac\x19\x0b\xb8)\x82\xb3\xa6,\xe0=\x11\x9c5g\x017Dp\xd0\xa4%\xf6\xaf:{\x93:@v\xacQ\x10\xc3`V`\xae\x1d?h\xcb\xd8\xdeW\x08\xd3dVw\xbe\xe7R\x00\xcbb\x96ad\xe1p\xc8\xacG\x13\x0foP\\\x15s\xefB\xc3\xf95\x0b\x1ad\xdb6#A\x18\x06\x94\x08\x8e\xe3@lH\x08B\x08\xd0E\xae\xdd\n\xb2\xe8g\x7f\x00\xf5\xd7\x80\xc5\x02PV\x8c\xdc\xba\x92\xa1\xde\xd7\x19\x0cQ\xbcX\xf4\x0ds\x00IJ\x81\x86=\x8biN\x18\xdb\xc1\x92\x10c\xc0]\xe9_\x86\x98\xe00\xe7\xae\xd9\xef\x11\xc6\xe1\xb6Dd`H\n\n\xf4\xd7\xc5|a\xd8\x8cy\xa2u\x1c\xe1Z\x10\x0b\xf5\x1c\x9b\xbd\x9c\x90s\xa2qv\x7f`.z\x80\xea\"/\xa8=\xd1\xb5t}\xc4\xe8n\x15\xa6\x14&S\xe0\x9c\xb1\x10]>\xd2aW\xa0Q\xb6\xe9\x0eA\xb7G(\xa8{\x869\x1cZ=\xd6\xb3I\xc0\xd8\x1e\x0f\xfb\xb0\xdf\x11\xb01Bs\x87iW`o\xf6M'5\xe6\xfd> \xcd\x1c\xafQ\x03\xea\xf7\xc7\xec\xcb\n\x88r\xd3\x1a\"\x17\xb4)\x89\x1a\x0f\x1c\x87u\xe1\x1c\x85\x12\x1a\xe8\xb8\x88\x03n\xedd\x85\\\n\xb6\x18,\x16\x0b\x04\xc2(\x15\xa0\xd1\xc2]X \x8eq\xb9\xc5\xc2As\x10H\xf5\x10w\xe1ro'\xc3a\\_\xb1/\x80\xd5-AZkK\xad\x8e<\xe6\xb6\xf3\xb0,\xde\x91ZPH\x83\x90\x8ap\xd4B\xc8\x85$\x15\xe1\xb0\x85\x90\x0bP*\xc2A\x0b!\x17\xaeT\x84V\x0b!\x17\xbcT\x84\xfd\x16B.\x94\xa9\x08\xcd\x16B.\xb0\xa9\x08{-\x84\\\x98S\x11\x1a-\x84\xdc\x0cY\x11\xea\x96\x9c\x90\x0b\x81\xe6K\xad\x8e\x828\xca\xb6\x80\xa8&\x86\xdc\xa7-<\xaa\x89!\x17j\x0b\x96jb\xc8\x8d\xdaB\xa7\x9a\x18r\xa5\xb6@\xaa&\x86\xdc\xa9-\xac\xaa\x89!\x97j\x0b\xb2jb\xc8\xad\xdaB\xae\x9a\x18r\xad\xd6\x00\xact/\x9e\x92\x0f\xc7\xe6K\x8d\x88\xc8x\x02.8\x9b/\xb5&>\xe3\xf1\\\xa86_ju\xb4\xc6\xc3\xb9\xc0m\xbe\x14A\xb90n\xbe\xac\x824\x1e\xcc\x05u\xf3\xa5F\xc5u< \x17\xe2e\x92\xd7Q\x1e\x8f\xe7\x02\xbe\xba\n\x01\x01\x17\xfeU\xba/\x02<\x9e\x00\n\x06+\xc7\x80\xe0\xect9_\x16+\xe4\xc8\x8eQ\x90\xf2\x14D!l\xe3l\xc2\x03\xda\x01D\x98\xf3\xa5\x00\x0c\xc5\x9b\xb5\xa2D$|\xf49_je\x00\n\xe1\xf9X4s\xa3,\x1c\x85\xd0|d:_VA\x00\x87\xe7\xe3\xd4Zz\x11 \x18\xb5\xce\x97U@\nt\x02 \x86\xadk\x11RA\x11me\xb8<\xd4\xe4I\xa0\xf8v\xbe\xd4\xea\x10\x176\x1f\x1b\xedfM\x11\xa1\xf9\xd8\xb7i\x88\x88\x86\x8f\x84\x9b1&\x8b\xe0\x80A \x88\x8b\xf3\x81C\x00\x07\xa2d\xa2\xb3\xc2DP\xcc\x9cu\xd8,l\x86\xc6U>\x82\xaeZ\x91\x87\xab\x10 \x10O/Eh(\xba\xae\xdb \xa0\x81b\xed\x8a\xa6\x0e\xb7\x81\x81\x0d\x88\xbc\xb3a\x87\x08\xbe\x013\x02qxC$R2\x14\x957T\xe2\x0e\x06\xc4\xe8\x0d\x99hT\xe1#\xf6\xf9\xb2\x0e\xd79\x020r\xcf\xef\x97\x17s%t\x07\x9d,\xce\x7fn\xd6N\xec\xbb\xd7rd3\xf3\x8a\xb9\x11\x18\x8a%71\x17\xf0zn\x16sl \x14Cn\xe6.\xd0\xd5\xe4-\xe6W#(v\xdc\xcc^\x80\xe5\xacx6\xdc\xac_\x00\x8bY\\\xcc\xa8,\xa7Xq1A\x01%\xc3\x021C\nE\xb1\xe5\xe2\x86R+U\xe8 Q\\\x0d\xa1\x18r\x81\x05)\x81\x9c#\x81\xa1Xr\xa1\x07\xe1[y8\xd1\xe2\x7f\x05\x86b \x05'\x05E\x0bC\x88\x17;\xdc\x10\x1dI\x1b\xeb-]-C\x90\xecd+h\x92l\xd4\xcax$f\xcc.\x8fH\xb2a+\xe3\xa1\x981\xbbt\"\xc9\x06\xad\x8c\x07b\xc6\xec\xb2\x8a$\xb3Z\x19[b\xc6\xec\x92\x8b$\xeb\xb72\xee\x8b\x19\xb3\xcb1\x92\xcclel\x8a\x19\xb3K5\x92\xac\xd7\xca\xb8'f\xcc.\xe3H2\xa3\x95\xb1!f\xcc.\xf1\x88\xae$\xed 5\x82d\xdc\x96' Ie\x9d\xa4F\xc8\x98\xc3\x1d\xa5J%\xb41\x1f\xca\x99\xc3\x9d\xa5J5\xb41\x1f\xc8\x99\xc3\x1d\xa6JE\xb41\xb7\xe4\xcc\xe1NS\xa5*\xda\x98\xf7\xe5\xcc\xe1\x8eS\xa52\xda\x98\x9br\xe6p\xe7\xa9R\x1dm\xcc{r\xe6p\x07\xaaR!m\xcc\x0d9s\xb8\x13\x95\x81\x9e\x98w\x05 Y\xcb\xa2\xc3e[HW#\n\x8e\xd0\xd2\x00\x0c\x17\xa9\\\x8d\x94=\x174\x02\x8b\"8~$\xd3;\xd2*\xd8(\x12X\xb2\xc0\x01%\x91\x10\x92V\xc0\x84\x95\xc0\xb2\x19\x8e0\xcb\x0c\x92\x94\xb7\x94\xaf \xe4\xac\xd3MR\xceT\x84\x08,\xc9\xe0\x18\x94\xc9NIk\x00\"Q 9\x00\x07\xa5dJK\xae|&4\x05V\x89p\x94J%\xc1\x14\xda!\xadC\x10\xb6Ry\xb3\xf6~@\x06\x9c\xc0\xbaP\x18\xc7V\xa96i\x0d-\xcc\x05\x81-\x95\x98\x93\xf2'q\x82Z\x84i\xbc\x9a\x89B \xbddci\xae\x1a\x85\xb0z\xa9\x12Y/\xd9\xe0ZZ\x93 \xce^\xaa\x84\xdaK6\xda\x96\xd6$\x08\xbc\x97*\xb1\xf7\x92\x0d\xbf\xa55 \"\xf1\xa5J0\xbed\xe3qiM\x82\xd0|\xa9\x12\x9d/\xd9\x00]Z\x93 V_\xaa\x84\xebK6b\x97\xd6$\x08\xde\x97*\xf1\xfb\x92\x0d\xe1\xa55 \xa2\xf9\xa5J@\xbfdcziMpdBl\xf6\xb5\x8fA\x92\x9e\xab\x16\xef\x13\xbb\x83\n\xb5\x89{\xaf\xda\x02\x80\xd8NT\xa8M\xdc\x83\xd5V\x04\xc4\xfe\xa3Bm\xe2^\xac\xb6D 6,\x15j\x13\xf7d\xb55\x03\xb1\xc3\xa9P\x9b\xb87\xab-\"\x88-Q\x85\xda\xc4=ZmUA\xec\xa1*\xd4&\xee\xd5j\xcb\x0cb\xd3U\xa16q\xcfV[wT;l\xe2\xaajDQO\x15\x14\x01\xdbo\x05^\xca\x8c\xe3\x03\xed\xcc\x15\xd0zsN\xcc\xad\x810<\xf9\xad\xbb\x82\xa0\xd8\xbd\x133,\xcb\x19n\xfc\xc6^\x81^\x86X\"\\^\xcap\xe27\xfd\nl\xb1\xc7 \xe6U\x96\x93\xdc\xf8-AR'm\x0c)\x14-$\xb0mX\xd0\x14{\x80b\x9ee9\xc5\x0d\xdaT$%h\xe3I\xa1(\xce\xd0\xc6#\xe1\xb0\x91\xe0\x05\xbd,\x84\xe2 \x9f\xbc\xcb\x08\xaa\xcdI1\xcb\x1a\xc1\xb97\xbbsYjK\xca\x0d\xe2\xc4\xefjR:\x92\xf2#0\x0cW~\xdf\x93PQ\xbec\xd6\xa2\xc6\x02Cq\x85vF\xcbN!g\x08\xf1\x02\xb6M\xc96\xb5p$A\x14_hg\xb5 \xec\x8dd\xcd\x98\x97R\x9c\xa0]WB?s\xbc\x968x\x03ax\xf2\xdb\xb2\x05\x81\x9c\x1d\xcf \xda\xb2%U#\xe7G`h\xed\x01\x9b\xba\x04E\xb5\xaf\xdb\xc2\xb8\x86Q\xbc\xa1\x9d\xdf\x82\x88\xd8\xfc\x15s&A\xb4\xaf\x03\x9b\xc3\x14I\x8b+Q(\x8a3\xb4\x81L\xd1\xb4\x0d\xc74\x8c\x96\x1a\xd8e\xa6\x88\xa43$\x81a\xb8\xf2\xfb\xd0\xa5\x07-\x15b\x02\x12T\xf0\x05\xd2&\xc2\x08\xa18\xa6#\xe5.c,\x0e\x19\xc8#=R\xf6l\xe0\x00U\"\x8a!\xeaC@\xd2\x1a\xa8H\x02b/\n*\xca3CR\xe6Dh\x01\xb1\x16E\x19\xf5\x01#)s\xca 9\xf6\xa2\xb0\x839\x8f\xa4\xa0}y=\x928\xa4>\xc4$\xad\x84\x8a\x19x\xf6\xe2\xc0\x849\xf3\xa4\xd0\x92\x96\xaa\xc4\x91\nyP\xaa\xbd\xb3\x11\xb37_\x898t!\x8eVI\xeb`\x02\x18\xb8\xdf\xc1\xb1Ly\x16Kn\x0f9kQpC\x1d\xdcR\xb1\x85\xbc\x1aQ\xb4C\x9d\xf5j7\x059\x07\xf0\xd5\x88\xc3\x9f\xeax\x98\xbcw\xcb\x99\x0b\xe3!\xfa0\x99\x82\xae\xe4\x15\x89\x03\xa4\xf2\x00\x9a\xb4\x06\"L\xe2Y\x8b#&\xf2\xb4Z\xbb\x19\x889\x1e\xaaD\x18B-\xdb\xf9KY\x8bc*\xea0\x9c\x82 \xa4\xd5\x88\x83,\xf6\xfc\\{ML\xa8\xc5W&\x8e\xba\xe8Sw\xd2\xaa\xf8\xd8\x0b\xe8\x84\xc20\x8c9\xa9\xa7R\x93\xdc\x85\xc5q\x19{\xbcO\xa5\xae\xb6 K\x18\xa8Q\x87\x02Uj\x92\x07&\x92\xc8\xadu\x17\x99\xc0\x08*\x00\xf7\x94#[?\x08\xbe\xdf\x1a\xd9F]\xd4\xedY\xdc{j#\xbb\xd7\x94C\xc5f]\xcc\xbfY7\xb2\xfbu)\xffj\xdd\xc8\xb6\xeaR\xfe\xdd\xba\x91=\xa8K\xf9\x97\xebF\xf6\xb0\xa9\x97\x7f\xbbn\x84\xeb\x06k\x18-R\xae\xd5\xd8\xa0\xcb\xc1\xa6\xe3\x1e\x03\x820&\x8d\x01\x94\x80\xfb4\x04\xd0\x04\xb6h\x08\xa0\x0e<\xa0!\x80N\xf0\x90\x91\x05PL\xdc(&\xce\x06\x16N3\xb1\xc1\x00@\xd5\xc4=\x16\x05\x81L\x06\x04('\xee3\x18@;\xb1\xc5`\x00\xf5\xc4\x03\x06\x03\xe8'\x1e\xb2\xf2\x00\n\x9a7\n\x9a\x87i\x1a\xfa\x9c\x86\xe6\x06\x8b\x00U4\xefq0\x08e\xb2(@I\xf3>\x0b\x02\xb44\xb7X\x10\xa0\xa6\xf9\x80\x05\x01z\x9a\x0f9\x99\x00E\xa5\x8d\xa2\xd20\xe2\xb4\x94\x1aT1\xa8\xa2\xb4Gc \x88IA\x00\xe5\xa4}\n\x01h&\xb5(\x04\xa0\x96t@!\x00\x9d\xa4CZ\x0e@!\x1bF!\x93\x16?\xda@\x1ab\x89@\xbdm\x00\xbdq\x84\x10\x1d\xafL\x96\x0cP\xf0\x86W0K\x05(}\xc3+\x9d\xa5\x02\x0c\xb1\xe1\x0d\xc1R\x01\xc6\xd9\x00\xc6\xe1\x1a\x06Xl\xc5\xce\x125\x11<6\xae\xc0Y\x83!\x02-\xb6\x82\xa6\x12\x96\x10\xa2\x03\xa6\x17\x86\x0c\xb0\xd8\n\x98q\x18*\xc0b+`\x12b\xa8\x00\x8b\xad\x80y\x89\xa1\x02,\xb6\x82\xa6*\xb6a\xc0\xc7\x85l\xfd\xe0\xdb\xf1\xd2\x0bX\xdb\xf8\xb6Q\x95@\x06\xf0\xed^]\x0c\x95\x9aU)\xf0\x95'\xbb_\x15\x02\x9fU\xb2\xad\xaa\x10\xf8Z\x92=\xa8\n\x81\xaf-\xd9\xc3\xbaN\xa0\xa1\xb8j(\x18\xbf\xf8\xd8\xa0\x8a\xc1&\xe3\x1e\x8d\x81 &\x05\x01\x1a\x8f\xfb\x14\x02\xd0\x00\xb6(\x04\xa0\x06<\xa0\x10\x80.\xf0\x90\x96\x03PH\\+\x04\xec\x9b~l\xd0\xe5\xa0J\xe2\x1e\x03\x820&\x8d\x01\x94\x12\xf7i\x08\xa0\x95\xd8\xa2!\x80Z\xe2\x01\x0d\x01\xf4\x12\x0f\x19Y\x00\xc5\xcck\xc5\xc0\xf3\x8c?7\x18\x00\xa8\x9ay\x8fEA \x93\x01\x01\xca\x99\xf7\x19\x0c\xa0\x9d\xb9\xc5`\x00\xf5\xcc\x07\x0c\x06\xd0\xcf|\xc8\xca\x03((\xad\x15\x04\xc4)~j\x90\xa5\xa0j\xd2\x1e\x05\x81\x10&\x89\x00\x94\x92\xf6I\x00\xa0\x91\xd4\"\x01\x80:\xd2\x01 \x00t\x91\x0e)\x19\x00ElhEL\xe4n\xb3\x01\x143Qp\xa4\x0d\xaf-\x96\x0c\xa2\xe248i\xf5\xb4\x0d\xa7\xd4I\xab\xe7m8=OZ=q\xc3\xa9~\xd2\xea\x99\x1b\xde\x1al\x83\x00\x0b\xad\x98Q\xbf\"\x81\x87\xbc\x154 \xd0$\xa0\x85V\xc0\xc4\xc0\x90AT\xfc\\A\x13\x01\x16Z\xf1\xb3\x07M\x03Xh\xc5\xcf'4\x0d`\xa1\x15?\xc3\xd04\x80\x85V\xc0\x9c\xc34(\xb7P\xfb[-\xe9\xd7\nFv\xfer\xce2\x96\x01\xf2-d\xa9 \xe5BA \x84I\"\xc0\xc4\x0b \x00s/$\x00L\xbf\x90\x000\x03C\xc9\x00&a\x08\x84(\x0f\xc3A\x04\xa9\x18\x1e\x07\xc1L\x0e\x06&d8\x14\x98\x93\xe1P`Z\x86C\x81\x99\x19^.09C\xc2D\xf9\x19\x1e#H\xd1\x00@\x08g\xf280Q\xc3\xc3\xc0\\\x0d\x0f\x03\xd35<\x0c\xcc\xd8\x00\xb2\x81I\x1b\x12'\xcc\xdb\x00 A\xea\x06BB@\x13\x00\x82 \x1c\x00\x07\xe6p\x00\x1c\x98\xc6\x01p`&\x07\x92\x0fL\xe6\x90@8\x9f\xc3\"\x04)\x1d\x0e\x06\xa1L\x16\x05&vX\x10\x98\xdbaA`z\x87\x05\x81\x19\x1eN&0\xc9\xc3)\xaa=\xcf\x03kN1\xd5\x03\xeaS-\xdb\x03)Y)\xe1\x03)^)\xe7\x03\x19C)\xed\x03\x19H)\xf3\x03\x1aM-\xf9C\x92*\xe6\x7f8\x92cR@<1D\x0b\x91\xc2\xd3\x9aJ\"\x88#T\xcd\x05q\x84\xaa\xe9 \x8eP5#\xc4\xb7Q9)\xa4\xe5\xdfs\x8f\xe1\xbc\x10Q(H\x0d\x91\x08\x08`\x12\x000AD\x94\x839\"\xa2\x1cL\x13\x11\xe5`\xa6\x88\xac\x1fL\x165\x00Q\xbe\x88E\x08RF\x1c\x0cB\x99,\nL\x1c\xb1 0w\xc4\x82\xc0\xf4\x11\x0b\x023H\x9cL`\x12\x89@\x89\xf2H\x1cD\x90J\xe2q\x10\xcc\xe4``B\x89C\x819%\x0e\x05\xa6\x958\x14\x98Y\xe2\xe5\x02\x93K\x04L\x98_\xe21\x82\x14\x13\x00\x84p&\x8f\x03\x13M<\x0c\xcc5\xf100\xdd\xc4\xc3\xc0\x8c\x13 \x1b\x98t\"pp\xde\x89\x01\x08RO,\n\x02\x99\x0c\x08L@1\x180\x07\xc5`\xc04\x14\x83\x013Q\xac<`2\x8aUPk>\nT\x98ZJ\n\xd2\xa2RV\n\xd0\xacJb\nP\xb6Jn\n\xd0\xbfJz\n0\x89J\x86\n\xb2\x92R\x92\x8a T\xcbS\xb1\x04G\xa4\xaa8R\x80\x12\"\x04\xe7(\x85\x84\x15K\xa6\x98\xb3b\xc9\x14\xd3V,\x99b\xe6\x8ak\x9b(y\xa5\x90\xbdR\xf8&Kd\xeb\x9a_\xc5fPF\xab)\x14%\xb4\x08\x04\x040 \x00\x9c\xcej\xca\xe1lVS\x0e'\xb3\x9ar8\x97E\xd4\x0f\xa7\xb2|f\xad\xc0\"\x0c\x16!Jd\xb10\x08e\xb2(8\x8d\xe5\xf3\xb1=\x0b\xb2X\x10\x9c\xc4\xf2\xf9\x98\x9d\x05\x0d9\x99\xe0\x14V\x83\x12f\xb0X\x88(\x81\xc5\xe1 \x98\xc9\xc1\xe0\xf4\x15\x8b\x82\xb3W,\nN^\xb1(8w\xc5\xc9\x05\xa7\xae\x1a\x988s\xc5aD\x89+\x1e\x08\xe1L\x1e\x07\xa7\xad8\x18\x9c\xb5\xe2`p\xd2\x8a\x83\xc19+^68e\xd5\xe0\x04\x19+\x1a JX1(\x08d2 8]Ec\xe0l\x15\x8d\x81\x93U4\x06\xceU1\xf2\xc0\xa9*FA\n\x99*Hc\xaa\x89*@\x8f\x8ay*^\xb9ji*^\xe1jY*\xde\x08jI*\xde0j9*\xc0X\x8a)\xaa\x86R5C\xc5P\x1c\x95\xa0bi!R\x88\x12\x9c\xae\x94\xd2S\x0c\x9drv\x8a\xa1SNN1t\xca\xb9)\xb6}\xea\xa9)\xbf\x8c\xd4\xa0\xccT]&JL5\x00\xa8\xdcl\xca\xe1\xb4T]\x0cg\xa5\xeab8)U\x17\xc39\xa9\xa6n8%\xe5\xd3k\x04\x16`0\x00QB\xca\xe7\xc3\x7f\x16d2 8\x1d\xe5sq=\x8b\xb1\x18\x0c\x9c\x8c\xf2\xb9\x88\x9d\xc5\x0cYy\xe0TT\x0d\x12f\xa2\x18\x84(\x11\xc5\xc2 \x94\xc9\xa2\xe04\x14\x03\x82\xb3P\x0c\x08NB1 8\x07\xc5\xca\x04\xa7\xa0j\x948\x03\xc5BD (\x0e\x07\xc1L\x0e\x06\xa7\x9fX\x14\x9c}bQp\xf2\x89E\xc1\xb9'N.8\xf5T\xc3\x04\x99'\xaa\\\x94x\xa2A\x10\xc6\xa41p\xda\x89\x82\xc0Y'\n\x02'\x9d(\x08\x9cs\xa2e\x81SN\xb4b\xda3N\x80\xa2\x14\x13N\xbc\xf6\xd4\xf2M\x9cF\x95\xd2M\x9c\x92\x95\xb2M\x9c\xde\x95\x92M\x9c)\x94rM\xbcu\xd4RM5\x9db\xa6\x89\xc6\x1f\x93hb(\x01B\x88\x0e\x9a{T\xd2L4\x95j\x96\x89\xa6RM2\xd1T\xaa9&\xa6]\xa7\xa5\x98\x04\xd9$\\\x85SP6\xa9)\x14e\x93\x08\x04\x040 \x00\x9cMj\xca\xe1lRS\x0eg\x93\x9ar8\x9bD\xd4\x0fg\x930\x13\xd7\xb3\x08\x83E\x88\xb2I,\x0cB\x99,\n\xce&a>\x16gA\x16\x0b\x82\xb3I\x98\x8f\xb2Y\xd0\x90\x93 \xce&5(a6\x89\x85\x88\xb2I\x1c\x0e\x82\x99\x1c\x0c\xce&\xb1(8\x9b\xc4\xa2\xe0l\x12\x8b\x82\xb3I\x9c\\p6\xa9\x81\x89\xb3I\x1cF\x94M\xe2\x81\x10\xce\xe4qp6\x89\x83\xc1\xd9$\x0e\x06g\x938\x18\x9cM\xe2e\x83\xb3I\x0dN\x90M\xa2\x01\xa2l\x12\x83\x82@&\x03\x82\xb3I4\x06\xce&\xd1\x188\x9bDc\xe0l\x12#\x0f\x9cMb\x14\xa4\x90M\x824\xa6\x9aM\x02\xf4\xa8\x98M\xe2\x95\xab\x96M\xe2\x15\xae\x96M\xe2\x8d\xa0\x96M\xe2\x0d\xa3\x96M\x02\x8c\xa5\x98Mj(U\xb3I\x0c\xc5Q\xd9$\x96\x16\"\x85(\xc1\xe9J)\x9b\xc4\xd0)g\x93\x18:\xe5l\x12C\xa7\x9cMb\xdb\xa7\x9eM\xc2eP\x06e\x93\xea2Q6\xa9\x01@\xe5fS\x0eg\x93\xeab8\x9bT\x17\xc3\xd9\xa4\xba\x18\xce&5u\xc3\xd9$L\xaf\x03X\x80\xc1\x00D\xd9$\xcc\x07\xf9,\xc8d@p6 s\xf1;\x8b\xb1\x18\x0c\x9cM\xc2\\l\xceb\x86\xac{U\x1fl?w\x15\x1fV\x00w\x17\x1f\xd4\x00w\x19\x1fR\x01w\x1b\x1f\xd2\x01w\x1d\x1fR\x02w\x1f\x1f\xd2\x02w!\x1fT\x03}\xe7\x1e\xd6\x01}\xe9\x1eT\x00}\xeb\x1ej=}\xed\x1ej:}\xef\x1ej7}\xf1\x1ej4}\xf3\xbelq\xfb\xc1\xcb\x033f\x90\x17UD\xa3\x1d\x05\x01\x07<\x12\x01\x8ey$\x00\x1c\xf6H\x008\xf2\x91\x00p\xf0\xa3d\x00\xc7?\xf6\x00\xabh\x08\xe4q\xe0(\xc8\xc1\xc0\x81\x90C\x81c!\x87\x02\x87C\x0e\x05\x8e\x88\xbc\\\xe0\xa0H\xc0\xe4\xe3\"\x00\x04\x87F\x1e\x07\x8e\x8e<\x0c\x1c y\x188F\xf20p\x98\x04d\x03GJ\x02\xd72XBHp\xbc\x04\x80\xe0\x90 \xe0\xc0Q\x13\xc0\x81\x03'\x80\x03\xc7NH>p\xf8$\x80\xb2\x11\x94\x83\x81\x83(\x8b\x02\xc7Q\x16\x04\x0e\xa5,\x08\x1cMY\x108\xa0r2)l5\xaa\x9ef\x0f\xc8\x83W\xc2\x81\x96@\xc0\xe3l\x03\x80\x87\xd9\xa6\x1c\x1ee\x9brx\x90m\xca\xe11\x96\xa8\x1f\x1eb\xe9\xfd[\xe1\x08\xcb\xc2\xe0\x01\x96A\xc1\xe3+\x03\x82\x87W\x06\x04\x8f\xae\x0c\x08\x1e\\Y\x99\xe0\xb1\xd5gF\x1b\xd1\xd0\xca\xe1\xe0\x91\x95\x85\xc1\x03+\x8b\x82\xc7U\x16\x05\x0f\xab,\n\x1eU9\xb9\xe0A\xd5g\x07\x18\xd1\x98\xca\x03\xe1!\x95\xc3\xc1#*\x07\x83\x07T\x0e\x06\x8f\xa7\x1c\x0c\x1eNy\xd9\xe0\xd1\xd4\xa7\xc6\x1a\xd1`\xca\xa0\xe0\xb1\x94\x06\xc1C)\x8d\x81GR\x1a\x03\x0f\xa44\x06\x1eG\x19y\x14\x86Q\xc1\x88\x89\xeb\xe1F4b\x12\x08x\xc4l\x00\xf0\x88\xd9\x94\xc3#fS\x0e\x8f\x98M9\x96\xdc\xca\x05\xfajr\xc1\xa8\x10\xa6\x95C\xdb7\x12Kf\xae\x1d?\xb4\xf2\x92}I5\xe3\xf3\x80\x0e)\xda\xa5\x9a\x8b\x9c0\xb6S/\x0c\xce\xb1\x17 -]\xc5\xe1z\xb9\xa2 \xd6\x81\x8b\xe2\xac\x98\xa3\xa9K\x18\xc7\x0b51M\x10\x06Ha\xe9s\x00d\xce\xd6Q'\x88\x0d\x91)H\x0e\x91\xe5\xc2+H\xaf\xb0p+\x9b\xe4\x9f\xd4\"\x9eJ\xa5A<\x95B{\xc4\xa2\xe3\x93D\xe7\xa9TD\xe7\xa9\n\xd1)\x8a\xb4D\xd9\xd8[\x06\xe7YT\xc0\x94\xc7dy>Q2\x00\x87\x048(HQ\xac`\xed\x03#E\xed9bA\x18\x08(\x0b\x83)\xc5Q\x90G\xc1\xfbR\\y\x83DF\xbf]D\xffh aaZ-G#`a0$\x0c\x0d\xaa,\x9c\x7f!~\x11\xc6\xfe\xb9cG^jc\xef\x11\xb1P\xccBq\xb8E\xb1c'\x1cr\xcd\"\xd7Q\x04#\x03\x16y\xd2p\x98\x12\xce\xa1\xd4\x12\x00-n\x0c\x00\x16\xb7\x07\x00+\x0c*\xcan\xda\xb8\x98Z;9\xb0\xa4\x99\x1cV\xd2J\x0e\xab\xd0HA{8\xb7\x92\xb5\xe7\x08\x1f\xe4\xb1\x92\xf6pX`8]h\x833\xe6\xc1\n\xd9n>\xab/\xc2 \x8b\xf5\x1e\xd19\x1fR/4\x8b\xa5K\xd6s\x80\x94\x0f\xa1\x17\x06Ql\xf2\xc5=\xa2\xb8\x07\x05\xea\x0b\x93@\x18@\x90\xbe\xe8S\x00\x88\x85E\"\xf8\xe2\x01Q\xdc\x1d\x0d\x01\x06C\x12Q\x00\xda{\xc3\x81\xd5\xbd\x16$\"\xf5g\x9d\xae\xc5\x02\x005a\x04\x9a\x01d\x07\x1a\x01\x99\x82F\x08\xacA\x83`\x83\xb0\x18\xd0&\x0c\x080\x0b\x8d\x10X\x86\x01\x15\x18\x05\xeb(\x8cU\x99\xc9|\xa1\xc5\xfcV\x83q\xb4\xa4\xbd\xfc6s\xf9m\xd6\xf2\x15\x8c\xe5\xb7\xdb\xcaW0\x95\xdff)_\xc1P\xfe\xb1v\x12\x98\x04\x0bM\x82[M\xc2\xd1\x92&\xc1m&\xc1m&\xc1\n&\xc1\xed&\xc1\n&\xc1m&\xc1\n&\xc1\x80I(\x8c\x8f\xecd\x1d\xa3C\xd3O\xb2\xce\x03b\xb2r\n\xd8\x17\x01\x03;\x8e\xc3-\x01\xedq<\xbd\xc0EAZLi\xc5\xcf\xe7Fs\"+m?\xcf\x98\xf86\xc6\x9acG\xe5\xe8\xb0\xb1c\xcf\x0e\xd2\xf3\xe69\x8dO\xe3u\xe0\xd8):\xe4\xc9\x81<5\x82\xce\x83p\x1b\xdb\xd1$\xdc\xa0x\x91\x7f\x9c\xcfs]\x14Lr\xa9\xea\x87\x08c/J\xbcDa\xcc9\xc0\xeaH\x94\xd5\xcb`[4L\xa3EJ\xae\xe3\xbd'\xea\xb9\x1e\x88UU\x9d\x11\x9c\xaem\x05u+\x0c\xf1\x95\xc2|u\x13\xf8\xc7X\xc0W1\x80\xff<\xfa\xf7\x8fT\xbf\xff\xdd\xb4/Q4VW4>F\xd1XE\xd1\xf8y\x14\x8d\x8fT4~\x8a\xa2)\x96U\xb9\xe6\x84Aj{\x01\x8a\x0f\xf5\xa3\xfdy\xe2\xc4!\xc64E\xb1h\xa6\xb7\x12\xecu\x1aN\xc8\x9d\x96\xec\x01\xa3\xddX\xcb\x1e\xf2t\x0c\x0cS\xb0\x86Y{\xe7<\x00bj\xec\xd9\x1buIARPX\x8d9\xf4\x94\x03\x15\x04V\x18M\xcaV\xf8'7\x02\xa0\x84\xdb\xe0\x1f\xdb\x04\xb1\xb4\xf8di\x01JXZ\x0cHK\x8b\x82\xbd\xe8\x10\x85\x89\x97'\x02\x17\xde\x0e\xb9\xff\xd7\xf3\xa30N\xed \x9d\xfcQ\x97\xd8\xf3$\xc4\xeb\x14\x11\x85\x19\xe9y\x8c\x9c\xf4G#\xdau\x88\xbf?\xd1Eg\xc4\xdf\x9f\x14\xcc}\xe0\x04\xcc\x1c\xe7\xcf\x94QAH\x15\x9f\xcc$\xf7\xff\x83\x04\x17\xc9\x88\xff\\\x19)\x01\xb6\x89\x16\x84\xb1o\xb3#u\xf6\x88F\x16\xa370\xa0\xd3\xb0(\xa6#\xc9(>>'X\x0b\xc5\x07J\"\xb9\xe0\x90\x8a\x13\x8d\x85e\xd2)\x88\xa7\xe0m\x8d\xcclt!\x14\x19\nCx\x89\xfd#\x05\x96\xca\xa6jfp\xe6\xe6e\xc3\xbcl\x14f\xa3\xcd\xed\x04\x1d6(N=\xc7\xc6e:;{\xc6\xef\x91l4\xdfsY\xa8\xef\xb9.\xe6\x80i\x18\xb1\xc04\x8c\xb8\xaaS\x9f\xab9\x0fp\x14\x0c~\x00\x9a\x91\xf9\x8ezK\x00\xb4\xb01\x00\x16n\x0f$B\xd1$\x856)8q\xd9P^o\x92vr`q39\xa8\xa0\x95\"\xbb\x1d\xed\xf8e{\xf01\xed\xe1\xc0\xe2\xf6pPA{\xf8\xfa\xcb\xf6PX\xd7\xf3\x0fad;^\xba?7\xb8\xa23\xf6\x01\xf41\xfa\xecq\xf1\xfdym\x8b\xe6\x0f^\x99\x15/f\x90\x92w\xa7kXI\x07ez\xf1\x82IK9'\x86\xbc\xd6J\xfc\xae\xc5\x13\xdaN\xeamP\x03\x19M\x94d\x0c\xd7\xa9\\\xc8p\xcd\xec\x9e-q\xb8=\xe3\x9e@\x82\xe7\xcf\xbf\xa3\xbe\x14\xea\x15\x18|\x95-\x03\xf3S\x11\x9dn\xfe\x9f\x1a\xa8\xab\xa9\xedXQ\x9b\nKC\x95\xf5\x9e\x89Py\xb3\xda@y\x1b\xd9\x16\x18\xdf\xa7\x05\xcd\x06{^+\xa4w\x16R\x98 _\x7f\xb6\xef\xe1/\xe3p{\xd0\xfc\xf0Q\x0b\x93\x9dVd\x0f\xfd0LW^\xb0<_\xc6\xf6>ql\x8c\xea\xb6\xcdm\xe7aa;H\xdbx\x897\xf7p\xd6\xf2r\xc1+)\xa24\x93of\xe5?a;E\xdf~\xd4\x7f\x9a\x88\x9e\x03\x1a\xe5Xu\xba=A\xa7:\x02z:\xe4\xac\xa5\x16^\xdb`\xd7\x89\xe1.\x9b\xeb$\xb7\xc0\x8fFW\xb7HM\x11O\x81:\xcaaI\xc4\xac;\xe6Yu\xc7\x00#\x0d\xdb\xf1\x12\xfd\x7f\xc5A\xbc\xe0\x18\x1f\xe1\xd1OEI\x9d\xa5\x80\x88L \xf2\x9a\xb2\xb4\xcdwz\x90\xeb\xf4\x84\x06o\xf7\x1f\xc0\x17\xb3\x87L0\x1dzAZ\x8fH\xce:N\xc2\xf8\xbc|H#\x93\x95\xed\x86[\x0d\x02N\xea\xc5b\x8c\xb0\x9d\x89\x05\x99\xdd\xc6\xb8\xd3\xb5\x92\x8e\xb3\x9e{\x8e6G\x8f\x1e\x8a\x7f\xec\x1a\x03\xeb\xac;\xea\x9fu\xfb\xfd3\xe3\xa7\xc9\x91x\xb1\x88\xe7\xf6\"\xcd\x04\x0d\x83\x14\x05\xe9\xf9_\xfe\xd2\xf8\x7f\xb8\xd3\n\xe4\xb9\xde\xd1;\xc6 \xdauz\xd1\xaeC\x9e\xf7\xeb\xfd4Q\x86\xe5\x07;c\xdb\xf5\xd6\xc9\xb9\x17\xacP\xec\xa5\x93f\xd2\xe4\xd6\xd1\x93\"\xf3\x99\xe7e\xf4I\x11A\x1a\xba\xfeb\xb2ByN'\xff\xf91\xcf\x98\xee\xce5\xf9\x9cu\x846Ui$\x1a\xcd\xfd\xbb\xd0\xeb\x99\x18Ej_\x10d\xcc\x97\x9a\x1dx\xbe\x9d\xa23\xc1s\xa8/\x11\xa5\xc2\xd0\x89=\xc4IM\xdb\xec(\xd0\n\xa6\xa5~\xd4\xf4Ce\x17\x9d-2\xea\"\x83-\xea\xd5E=\xb6\xc8\xac\x8bL\xb6\xa8_\x17\xf5\xd9\"\xab.\xb2\xd8\xa2\xf1x\\\x17\x8e\xc7c\xa0\x98*\xe7\x00\xbe\xbdk\xa45\xfa\xc3\xfe\xc8\x1c\xf4\x87,\xaa\xf4\xf2\x1aY\xfe\xce\xc3\xbc\xd4\xb3q\x0d\xe3\xb3\x95\x8f\xda:HP\xc3(\xff\x8d\x86\x04(IQf\xa0h\xaf\x15\x11T\xdeM:!\xb3\xaf,\xc2Ej\xb05>\x10\xbf\x9e\x1b\xecB\xa2\xa4k6\xae \xda\x95\x01\xd6\x01c{G`\xcd#\xb0\xfd#\xb0\xd6\x11\xd8\x01\xa3\x17\xe8`\x7fA\x8f\xbd$\xd5b\x94 \xa1q\x08\xc4\x9a{\xf1\x1c\x99\xaf\xd6'94I\xf7\x18i\xe9>B\xc5\xd1*\xa1%\x8b\xed\xa5N\xf4sDm7u\x8f\xdbo\"9&(B\xb1\x9d\x86q\xce\x94\xe0at-A\xfb=\x7f\xd9\xf1\xfc\xe5\x81\x18\xd2\x9b\x9cG\xfe\xab\xeb%\x11\xb6\xf7\xe7s\x1c:\x0f\x02\x1d\x06\x0fI\xc7>\x94\xe7\xe1Mk\x88\\\x17\x9a\x02\xf8\x01k\"-\x95\xd5\x06\x0d\xb6\x0c\xa2\x9c\xf5\x0b\xa9\xc6\x03\xc7Y,\x9e_\xaamlG\x11\x8a\x05\n\xec\x0f\xf4hW\x1a\xf0\\\xef\xe4\x9b&\xa5\x0b\x9d\xeb\x9d^VH\xcd\xf0\xdecVRN\xcf\xf3p7\x01\x9f\xd2\x12\x84Qn\x1a-\xb5\x97Z\x82\x9cL\xeaCe4\x82ymH\xcdO\xb4\x05F;\xf2Y\xf6;%I\x18{\x993V\x99\x18\xaa\xcc\xf5\xe2\xa2\x9a2%:\xa98\x12%N\x88\xd7~0\x01\x9f\n\xc5\x7f\xba\xd8\xe4 \xe0F,\xeai\xfe\x8b\xe6\xa5\xc8O\xaaG\x95E\x0c=\x0b\x97\xb2\x7f\x8c\xea\x9f \x134\x8aB\xc4^\xc2E\x81\xbddR\x9b,\xef\xb9F\xb4\xeb$!\xf6\xdc\"\x1c\xb3\xc6g\x03\xebld\x9cu\xcd\x9f\x84*)\x9d\xb8\x99\xf5\xa9\x1b\x1e:\x1bj\x93\xca$\x8e\x18\xf5I'\xd4;V\xb4\x9b\xe4\xa5\x0b\xdb\xf7\xf0\xfe<\xb1\x83DKP\xec-&U\x1f\x9e\xf7\x0d\xcb\x10\xf2\xee\x06\xa1\xe6\xa2\xc4\xe9$\x91\x1d\x1cH\x03d\xfa>7j\xd5\x9f\x1b\x93\xe2?BV\x9dd\xb3\x84\x82\xa2\\\x85}^\xab\xfdD\xc2\xca\xb71u\xde\xa9_5t[\xcc\x04}]\x9f\xa8HK\xf4\xd1\xdc \x8eWVd\xc7\xb6\x8fR\x14\xff\xf1G6\x15\x90B\xf5\xa2]\xcd\xdf\x8av\x1d\x9db\xef\x87A\x98o\x10P\x82\x0ft]V\xdb\xc6C[\xad\x9a\x06\x1f\x0e\xfc\xca&\x9b\x04\xcch7\xa9\x0e>\x90\xfe`\xa9{\xb9\xc5\xdb\xc3\x82\xedq \xdc\xcd\xc8j(\xba\x02\xd1\x07\xfe\xaa\xeb:\xb3\x10\xe9\xb3\xc3a\xb3\x921\x99E\x8c1\xe6\x16;\x00\x04\x14\xad\xd3M\xedy\x1e8\xa0\xf8\xe9#\xceQ\x0eOV]\xfc\x9c\x8dC\x87\xc6\xdb\xfa\xfc\x90s\x04\xa3\xf3\x85\x17'\xa9\x16.\xf2\xf0\x83a\xdb\xd1;\xfa\x11\xbc\xbaebs\xd5/:9\xe7S\xa7\xf3*\xd7Y\xfc\"\xb3\xbe\xad\x999L\x1eSY\xfa\x8bj\xb5\xd9kV\x9b\x99\x9f\x00kd \x9b\xf3\xfb\x8f\x9a\xa5\xbf\x00\x13=U\x111\xb4.c{\x0f6\xab\xeb%Z\x18\xa1\xa0\x19n\x92\xb5\xef\xdb\xf1\xfe \x1a\xe13\xef\x16h\xa8fQL\x8a\x95'V\xd6\x1a\x95s\xd0\xc4\xf7\x82*\x82\xb5\xb2\xdf A\xd9\x1b\x83\xa3\x9f\xe0~c\x00\xcb\x7f\x83\xe980\xe6(\xd9\xcf\x8e\x01w\xb0=G\xf8\xe9\x1d\xef\xa4\xa9\xfe\xa8f\x95\x922C79,\x0fu\xbd\x1eG\xb9\xc30'\xcc\x1aJ\x02\x95\xfd\x91\x9a\xa1$\x9d[\xc0j\xd5g'J\x95Q\xadi\xeds4\xae\xe8C\x9a\x8f\xd2U\xe8\xca\xe6\xed\\\xcf\xf5\xd6\xe5H'f\xd0A\x16\xa8e\xe3\x05w\x03\x8c\x99\\L\xba\x0b\xe5\xd3ONC\xf5\x04\x9d\xed+\xf2v.\x16\x0b\xc5F\x86\xf9\xd2,3\x80\xe7\xb6\xf5\x97\x92$\xb2\xd3\xd5\x11\xd0?\xfepQ\x14#\xc7N\x11\xa5\xccAD\xf4\xacS{[n~\xbdq\x08\xbdc\x16\xab\x19\xfa\xb7'w\xd0\xc96\x8c]m\x1e#\xfb\xe1<\xffW\xb31\x96\x85c\xaa\xf1R\xb9\x19N\xec\xe8\x0f\x07\xa3h\xc7l\x81\xff\x07\x9a\xaf\x17\xed\xd8\xd3\x9d\xcal\xd8\xcd:,\xbc\xa6\xab\xd4p\xa6\x8b*r\xc8\x16\n\xb1\x17\xe5\xebR\x82\x81\xa9:\xe4<\xdfH\xf3?4\xe9\x90\xd1\xbeZp\xc7\xc8\xad\x18\xe0\xf7\xea\x00\x9f\x98\x95\x9e=\xb2\xe7\xa4\xab\xf6\xad\x19\x19\xcb\xb0m\xc4,5\xe0\xf8\xaab\x19\x85IJ\xbc\x8f\"3p\x7f\xec8c}\xc2\xae\x80\x87\xe6YO\xef\x9f\x19\xfd\xbe0\\\xa1\xb8\n\xa7\x1drN(\xea:\x81\x19(\xb3\n\x1f\xf5p\xf9h9\xd7\xac&\x17\x8em\x98\xbc&{V\xef\xcc\x18\x18g\xfd\x91\x82&\xd7j\x8a,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2\xdeE\x18\xa5\x88\x95kl\"\x13\xf1\x9a\xec\x8f\xcf\x06\xbd\xec\xff\xad\x8a,\xd8\xaa\xe92\xaf\xec$v\xa0\xd8j\x9cN\xd4\xa8B\x0dK\xc4:\xe6\xc0\xb0\x17\x0b^\x9d\xe3\xe1\x991\xb4\xcez\x96B\x17_\"5\xc7,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2>\xb2Sg\xc5\x88e\xe9\xc8tz\x9c\"G\xfaY\xaf7<3\xc6\n\x8a\xcc\xd9*\xa9\xb2\xa8\xec\x14n\xa0\xd4J\x8cNS\xa7J\x05\x19WF\xae\xb1n\xf4\x00\xb7\xcc\xa6\x1cc\xa4\xe6\x96\x19W%e\x16u\x9d\xc0\x0c\x94Y\x85\xcfi\xaaT\xe1\x1f\xe6\xb1^\xc2H\xa6\xbb\x96m\x0fym\x9agc\xfd\xcc\x18\x0c\xdb\x95Y\xf2U\xd2gQ\xdbi\xfc@\xc1\x15Y\x9d\xa6U\x95*\x88\xb0\xbe>\x15:\x98\xd0\xa2\xa2y\xf6\x07\xce\x14\x8d{\xc0\xab\xa5\xc4\x95(i\xb9\xa8\xefd\x96\x07Hzun\xa7\xe9ZR\x0b!\xa0\xb3B>J\xb8\xa4\x9c\x1aY\xa7[\xfe\xa0\xa5^\x8aQk\xaef\xe1\xe14kD\xb3\xd6*\x9eh^\x90Eq\xd4\xd6b\x1eI\xe7{T:\xb5oU%\xd8{M\n\xd2\x1d\xb9.b\xbc*\xb5\xe7\xa7\xad\x82\xa8\x9a\x8bex\xdd,b\xe3\x1b\xd8\xf3N\xedy\x07{l\x1a\x8d<\x89N\xf1b\x16,\xc7\xaf\xfe\x8a\xfa\xd8\\8\xb7bbv\xf2\x99\xcf\x96\xf5X[C\\\x85\x89\xecb\xdf\xbe`5\xa8WeF\xb4\xa3\xceK\x11)l\xc1\xfe\x1e\xbb\xbdW\x08Q\xfa\xf8\x81\xc9\x90\x81\xbeI\xae\xbe\xb5r\xaf\x1aLJhh\x97\xa28\xb0\xb1\xe6\x86N\"\x87\xe6^\xfdGy\x13\x8a\xb5+\xbd\xcdX\xbb\xa8U\xa5\xb5\x8f7\xa8\xa4)\xdc\x11\x12ik\x84h\xb2ALf\x14h\xd3\xf3\xb6 :\xa6\x01\x020%\x7f\xc4fR\x9f\x9e\xb3\x15\xaa\x939\x0fC\x13\xa3\x1dr\xd6)\xaa\xe0\xf50\x98\xbb\x81\xfc\x9d^\x0ci\xa7;O\x03r\x1c$\xc7\xe5>7.\xcfCw\xaf\xe5;\xb0u,r\xd2\x98\xf7?s \x82\x97\x9ez\x86\\/=P'\x16\xf4V\xfab#\x83T\x9a\"M'A\x189i\xb5\x9bkB\xb3W\x8c\x92(\x0c\x12\x94h^\x100f\x96\"\xb9\xee\xc8\x95[\x82\x9eXN\xa3\xa7u\xc6\xaa\x96,\xec\xf8#I\xedt\x9d\x80{\x0fOeJ<\\\x07n\xe8\xac}\x140\xb9]\xe3\xd8d\xf6X\xcf\xfeH\xaa\xce\xcf>1\x9f\x0f\xcd\xcf\x93UY\xef\xbe\x8e\xfc\xc9\xf36\xb78o\xf5?\xd1Zb<\xfd\xe3\x8f\xc2g\\o\xd3\xf5\xed\xf8\xc1\x0d\xb7\x01\xec]2\xca\x18\x05.\x8a\x91;+9\x80\x9b\x7fE\xa0\x93\xbf\xb9\xcd\xa1\x8f\xc75C-\x10\x9a\x91\xa7\x1c\xa8d\x9e\xd1\xef\xf7\xd1q\x9a\xe1\xf6\x9dT\x1aW\xa9\x85\x9dEThY\xc5t\xa2\x038\xad|g\xc9\xedg\x90\xdc>\x1c%\xf0h<_\xe8\xfd\x89\xe2\xbd'\x15\x89\x9a\xd6\x14\xa9\xf3\xe7h\x13}\xd8qd\xcc\x0d\xddy\x82d\xec\xce\x95\n1'T\xba:N\xd3\x8b\xc5BxbN\xb8\xd3\xaaeSW\xf3\x1b\x0e\xed|\xe4+\x0e\xdd\x93G!\xa9\x0ej6gl\x9b\xfd\xfa\x96\xb7TP\x15F1w\xa6\x0b\xee\xfb\xcc\x95\xef<\xa2)69\xb3\x9f\xca=\xce\xecwx\xe7\x93{\x98C\xab\xe0c\xb5\x8fV(H\n\xf1\xb3\xa0\x83z@\xfd\xa24\x06\xd5/\x89ae;\xd6\x8er\xcd\x15'\x18\x1at\xf3\x96\x86\x16\xban\xb1\xdc\xcf\xba\xddAr.y\xe5-W\xc5{\xc0\x9d\xd0\x05\xd6~2\xf4\xdf\xbb\xbe\xe7\xc4a\xfe\x80|iN\xe9!\xbb\xeaHN_g\xce\xe8\x0c\xd8\x13\xd6Y\x1f\xc8\xdcQ+\xd7y\x89\xf8\xc4S\xee)\xe5\xca\x138tJZj\xe8\x8ezc\x138\xed@n2\xf2\xc6&\x0d\xf8\xd1K=\x8c\xbd\xb5\xdf\xf9\x82\xe6g\xc4\x84/\xe9\x97L\xc4P\xb6\xd9\xd4\xeb\xc5\xed\x90\xdb\xdb+r \xc4+\x88\x88eT\x8f\\\xf3\x9bE6\x83\xdaG \x8ej\x83\xa7\x95\x98s\x1a\x96\xe0P\x13\x07\x93\x8bX'n\x9e\xbe^8i\xa7XQ\xba\xbf+\x1dLzr\x13\xbe\xe7\x92\xa7\x1a-\xb5\xe2\xb8\xb5U,,N\x88D[\x94T/`\xeat\x93a\xd6\xcb\xcf\xe6T\xa0\xe0\x85\xb9\xd5l\xd2\xf8p\xe5\xb3\xe5\x89J\xe2x\x7fq\xd1\"\x9bW\x9a1\xc1x\x8e\xa37\x91\xed\xbc_'\xa9\xb7\xd8W\xe3L\x8d}\xaa7\xfei\xce\xd0\xa2\xf4\xfaQ\xdbH.\xa6,3uD\x8f\xd1\x81\x1e\x03'\xf2,\xfdEs\x18\xb5\xce\xd9\x95\x8c\xa5\xa7O\xf3\x13\xa6g\xc2\x13\xa8T\xb1\xc0\x1fO\xe8\x11\x12-\xcc\xd1\"\x8c\x91 aI\xb5\x93\x8e\x9a\x88Dm5\xdb\x11G\xc8\xb5\xbcG\x01\x07r\xeb \xec<\x0e\xd3\xfc\x87\x8e\x91t\xbc`\xe1\x05^\x8a:\xd94n\xc7g\xc4%\xcf\xc9\xf1\x14\xcd{\x12\xb8\x04x\xb1\xf7i\x9d\x15\xff/\x0e\xbe\xe6\xf3b\x1aF\xe5\x9e\x039;\x0c\xd8{\xb1y\xa6\xa9\xf6\xf3S.\xa0\xff\xfb\xbf*\xf2\x07\xb4_\xc4\xb6\x8f\x92N\xd5\xb0C\x1a\x02\xf7\xa0\xf3R\xf4\xa3\x91\xae\xe3\x80t\x1a\xea\xf9\xbf\xff\xfd_\xcf\xccO\x14\xec\xe7&\xa5N\x93W\xc3\x9c\x02I7\xfb%\x0eq\xa2\xd9\x8e\x83\xa2\xb4\xda\xac)\x87dj\xf3g\x19#\x14<\x85g~\xf5\x83\xe0ED,\xdd!\xf2!K\xcc\xb1\x17<\xa0\xf8`\xe9/\x9a\x17\x86P\xba\x15 H1\xcbc\xb5\x9d\x95y8\xba\xab\xda\xdd \xcc\x93 u\xb8\xe1\x05\xdc\x92\xb2\x06\x9d\x81O\xcf3\xa7\x83\xce\xfaU\xb7\xba\x8b\xea\xeb\xdf$\xc7\xcf6(N\xbc0\xd0\xa2\xd8^\xfa\xf6\x81\xdc\xaa\xa8\x83K\xe4\xb3\xe9?\x9a\xea\x8f?|\x94$\xf6\x12==\x82:u\xde#\xe5&\x06\xfcn\x0f\xf9@\xd8\xcc\\\xa0E>q\xd8\xb4\xcb\xc5\xf4\x82\xc6\xfe\xdd\xf56\xc4\x8bE-\xcbY)\x9dmTb\xde\xc9\x171Mt\\m\x97\xba(\xfbS\x8b\xdb\x8fv\x9d~\x11\xf6\xb2\x8bN\xba\x9ay\x1a\xb4\x9d\xb5&\xaf'\xf5\xc8\x83\x9a\xec\x19A\x93?6h&\xfcH\xbc\x8c\xed\xbd|\x05\x9as\x89\xec\x18\x05\xe9s_e8a\n\x9d\xa7A\xf6WK|\xd1\xc5\xad~\xa9\x19\x8e\xee\x9f\xae\x97\xd8s\x8c\xdc\x7fU\xef\x9b\x08\xc2\xcc\xe5p\xb8En=[uM\x8e\x90y?\x00s\xb9\xc9b\x9aer\xd7\x9fx\x04\xdf&\xc7\x0e\x1c\x84\xd9Sa\x8b\x81> \x97_e\x01i\x12\xb9\n\x0b\x0e|u\xf6:]\x85\xb1\xf7\x88\xe8\xeb\xd8\x13z\xb4\xab\xb8T\x07=\xe5\xa7?y\xe1$\xf5\x16\x89\x86\x05\x0e\xed4\xff\xb6\x0cm>p/\x9e\xa1\xdf,\x0f\x0b\x0fc\xf8\xc8e\x86-w\xaa\x80\xfe\xd9\x1f\x8fu\xd4\x03\x92[T9\xc7Q\xcb\xb8D\xa7\x0d\x9f\xe4\x8aZ\xc0\xb8\xe8\xff\xc7\x0fN4\x83r\x1f\xbcxU\x15\xd7\xb13\xadv\xb8\x03\xe2\x0c\x07l\x0b\x18\xe4\xa4\xf9_F\xdd\x95Y\xec\"\xf3\x98\xb5\x83\xb9\x18P\x0e\x0e\xca\xa2\xd3\\3\x0f\x95s\xce}\x98\xb8\xf7Y\xf6B~w\x8ef\xcc\xa8V\x06-\x0f\x80\x13}E\xcf\xfe\xb4\x89-\xbc\xf5\x0bO*\x05\xeb\xa1\x9e\xfd\xa1X\xcf\xd7i\x1a\x06\xec\xdb}\xc2u\x9a\x0d.\xbc\x02\x0bx\xd7\x0b66\xf6\xdc\x03\xbfVIV\xf6\x03\xeat\xfbI\xc7\x98\xc0O\xdb\x0e\x03\xffu\x81\xb83Fe\xd0{\xc4\xc4\x9b\xa7\x18\xac\xea\x1e:\x7f\xbc\xa7\xcc\xd9\xca\x13\xbb\x8ba\xf6\xa7\xb3\x8e\xf1\x8f\xae\x9d\xda\xe7\x9eo/\xd1\xcbd\xb3\xfcy\xe7\xe3\xc9\xdcN\xd0\xa0\x7f\xf6\xdb\xaf7\xbdo\xfb\x8b\xfe\xfc\xcbn\xed<\xea\x9e\xfd\xeb\x9d\xee\\\x86\x9bw\xa6k\xba{\xcb\x9c\xed\xad\x8d\xe3;\x9b\xd9\xfdt;{5~t}\xc7\xbb\xfe\xf5[\xf4\xedw\xf7\xd5\xdc\\\x8e\xaf\xef\xa7\xcb\xd9\xab\xe9\xbe\xf8{\xfd\xf3\xf5\xab\xe9\xf2\xfar\xb7\xfd\xfa\xfb]x\xfd\xe6v|\xfd\xa0\xeff\xfb\xbe>\xfb\xb8\\\xde\xec\xfb\xfd\x9b\x8f\xf8\xfe\xdd\xfd\xb59\xfb\xa0\xafg\xf7_\xfb\xef\xee\x9d\xed\xfb\xfa\xe7\x07\xf3\xfd\xab\xe9\xf6\xfaU\x7f\x7f\xb3\xef\xefo\xee\x97\xeb\xd9\xbd\xb3\xcf0\xb3\x0f\xf9s\xeb\xe6\x1e'\xef>\xce\xd6\xef?N\xfb\xd7\x97\xb3\xf5\xfb\xcb\x9b\xfbw\x1fj|\x9aa\x9b\x9f\x1f\xcc\xf7\x1f\xa6\xdb\xf9+\xfd\xf1\xdd\xfd\xc3\xf6}\xfe\xdf\xe5\xe3\xd7}V\x9f\x93\xbe\xbb\xbf\xee\xdd\xd4?\x17u\xbc\xfb\x90\xd5\xf1\x90=\xdb\xe5|\xef\x97\xeb\x9b\xc7\xa9U\xfd\xfc\xfe\xa3\xd3\xbf\xbe\xbc\x98\xcd>N\x97\xb3\x8f\xaf\x93\xb2m\xe9l\xdf\xdf\xdd\\\xbe\x1e\\{\xa3\x9f\x7f+\xf4\xf4\xf3O\x9d<\xaf[\x9c\xfc*b\xceN\x10j1\x8a\x90\x9d\x92\xf3ZqS\x9f{#\x84<\xa3\xd9SK|f0\x95(\xa8Y\xb9G\x11\xb2\xe3,Z(F\xa4\xfcEm\xecC\xe6w\xc0\xdd\xff\xe9\xafq\xeaE\x18\xfd\xabJ\xfeZ\xd4\xc15\x0b\xf4V\x80\xd1\x9f\xde]\xe9\xbd\x07.\x89\xd8\xcbg\xd8\xa3\xee\x94 8\x19#\x9d\xbd\xe0\xa5\x94\xdd}\xea\x99\xa4\xfch\xe1?\xb3%\xf5/\xc8\xb7=\xfc\xaf3A\xe9\xc2\xc3HX\x18\xd9I\xb2\x0dcW\x08H\x90\x1d;+aq\xb6\x1e\xa3\x0b\xb3'v\x8clRE:\x91l\xa2\x1dh\xc4\x0c\x8f\xc4\x86\xa1;\xce\xfe\xb4\x0d\x8f\x8b\x85\x9a\x15\xff\xf3\xd5\xd5\xbct&\xdf\x8a\x91\x1b\xbb\xeaO\xd2V\xb4\x81\xea\xd6\xb4\x01\xcbV\xb5\xc1\xf2\xd6\x81\xa0\xaa\x95\x7f\xca0\x00d\x8ar6\x07C\x7fq6\xd6_\x00Y\xb6:\xa5k\xba?jF\xb4\xcbF]0\xe5K\x96\xff\xbb\xa7\xbf8\x1b\xb5\xf2\xeb\xc9\xd9U\xc5\xff6\xf5\x17g\x96\xfe\xe2l\xd8\xcaQ\xeb\xb7HX\x95\xff\xbb\xaf\xbf8\x1b\xb4\xf2kaWs#3k\xff\xab\xd1g\xd1(8\x1403\x07y|\xbc\xd9\x9a\xeaQ\xb7\xe8\xf9\xd5\x137l\x92\x01u\xcb\xbb(\x8e:-\x00\xccMUK\x8aw|\x1d\xf8\xd0\x17\xb8\x1fU\x0f\x11\xce:\xe6\x0f%\x13[r\xe4d\xc2\x9c\xd5\x88QN\"P\xc0\xb3\x9f\xd9rV\xc8y\x98\x87\xbb\x03\x19\xf5\x97+Y`mD\xeez\x08\x1eW*\xd5\xb3?peOx\xfd\x86\x80aD\x1dD\xef\xeb:\xf1\xd1\x8d\xc2\x0e\xe4y\xb9J\xf3,HU\x8bP\xba\xae\x16\x85\x98L\xaag\xff\xaa\x9b\xca/\xa5\xa5t?\xe7\x8a\xfa{\xb7xC\x8f\xf0\x8dJt.K#\xf7\xcb\xf27/Tn7 \xcf\x91\x8f\xca\xedn2\x0ef\xcf|\xd0[Q\x8c\xff\xa1Q\xf6G\xf4\xb2$=_\x02T i!\x97\x08\"\xde\xf1\x90\xf7\x83\xfa\xa7\x13U\xd7\xfe\xca_\x85WFKk;\xcf\x7fB.e0^Y\xf9\x1a\xf8/\xc0\"\xd8Y\xd9q\x82\xd2_\xd6\xe9B\x1b\x9d\xbd0_%\x9be'\xb7\xe0/?\x18\xfa\x0f\x9d\xc2\x82\xbf\xfc0\xfa\xa1\xb3\xf1\xd0\xf6\"\xdc\xfd\xf2\x83\xd9\x19v\x0c\xbd3\xfa\xa1\xb3\xf3q\x90\xfc\xf2\xc3*M\xa3\xf3\x97/\xb7\xdbmwkv\xc3x\xf9\xb2\xa7\xebzV\xc7\x0f/\xcc\xab\x17\xe6\xab\xc8NW\x9d\x85\x87\xf1/?\xbc\xe8\x99}\xa3?\xec_\xfd\x90?\xd0\xe25F\xbf\xfc\x806(\x08]\xf7\x87\x8e\xfb\xcb\x0f\xb3A\xd74\xcd\x8ea\xbd3;\x86\xd1\x1d\x0c\x86\xd8\xc8\x9eh\xd9\xbf\xfdN\xaf\xd3{W<\xce\xc40;\xa3\xac\xec\xf1\x87\x97EMY\xa5/\xcc\xab\xbf\xfc\xd4\xb1\xf4\x17\xcdZ\x93\xd6\xa8\xeb\xd98\\j\xeb\x1d\xf35\x9d \xf9\xa2U\xea\x1e\x8b^\x1dV\xaa^\x03,`\xd8\xe9f\xbaw\xe30\x02\xb8K\x19\x8an\xc1\x8c~\x12V\xe5\x87\xae\x8d\xa9z\xea-m\xae!\xd4\xfe63)\x16\xbf\x9a\xe5\xdcP\x7f\xf3\xc3\xe2\x86\xe2\x937\xf8\xf9\x05JuY\xafm\x81\"\xc8\x07\xe8\xd1\xaeS\x9c\x9c\x92\xbe\x04Z\x8ckUj\xb5\xb1&;\x06g\xf5\xc90\x82O*J\xd8\xd2\x17U\x80{6U\x9e\x9c\x9fk\x95V\xb8\xd2\xba\xe9K>#f\x81=h\x16\xd8O8\x9a\x04\xd5\xff\x94\xd7\xce\xd5\xb1J\xaf8/':*[:\x16\xe96'\x9d\xffQmM\xa7\xeb\xe00AZ\xfe\xf8\x88\x94\xfc\xf3e\x9bd\xc2\xad\xc8\x0f\x83\xf7\xd8c?\x03\xf2\x0d^\x8d\xe8\\\x1eN\xb4Ir\x82[\xf8\xa1+O\xef\x98\xfa\x91g\xea\x85\xb5t\xba\xc4}\xd9$\xb2\x99\x1b\x11<&u\xabc\xb9\xb6\x9e\xfd\x11\x9d\xcc\xe5(\xff\x9e\xba\xcc\x8dK\xf5w\x0f\xe5\xcc\xb44\\.1b\x8fh\xc1\x81\xd7@\x14x\x95\xa6\xccF\xa9N\xd7D\xbe\xc2\xebo\xb8\xe1]\xf8*`u\xe4\xa9\x08\xe8C\x0e$\x03~**\xcf\xf1\x8cu\x17-\x81\xf3=\xe5s\x8eN\x0bc/\xcf\xa6\xe9/\xb2(a\"*\x10\x1b\xaa\xeb\x84\x18\xdbQ\x82\\\xf1\xa9#\x81P\xf9c1\xe7\xf2\xac\x1et\x02\x8d\xdd\xc0\x12\\\xa1=*\xd2k\x0f\xe0\xaa`\xb0\xd7o\x82\xc1\xec\xe7:\x1a\xcc\x83\xea~\xa7\xd7'c\xbd,\x8c3\xf4\xce\xe0\xdd\xa8k\x8d;\xc3n\xdf\xe8\x18f\xd7\x18v\x8c\x1e\xd6\xfa]k\xd4\xe9w\xad\xf1;C\xef\x18#<\xd0\x06m\xf1\x1b\xb7W\x90\x05/\x90\x16\xef\xd7~\xa4\xa5a\xfe60`\xe1\";\x01\xc43\x10\xbfz\x8a:;\xa8u\xfb\\g\x03-\\\xdc\x87\x97\x1f\xe3$\xa0\xd5\xbb\xa5\x8aG+/H\x0f\xc4!\xbb\xfcG\xf6cc\x04T \xab\xd1\x1d!\x7f\xc2\x9f\xe3\xab\x86\xff\xae\x81\xfcN~\x14\x08\xf8\x1eo9<\xaa\x04od\xb85\x84\x1c\x9e\xb8D\x95\xad\xfb\x99\xc3F\xe5\xc9\xb2\x02\x9a\xd4W0ub\xf2\x97\xbdR\x9a\x97M\xc2\xbdz\xc1)1{\xeb\xfc\x0b\x0f`\x9a,\x96b\"7Qh\"\x7f\xef5\xcd\x9e \xd1\x9e\xe5-\x86'\x85Ap\xb2\xe8Y\xdf\x13.\x0f\"\x06:w\xbc\x86S\xd5\x13_\xa3\x0d\xf0;\xe9\xcd\xde\x1c\x9f\xe3\xde_\xce\x92[\xac\x07\x90\xddEo\xdd\xf6\x02\x0e\x0b05\xa8\x0d\x99\xf9\xeaQ\xda\x17*F\xc0e\x97\xfa\x82\xc3Q\x1f\x1c\x02\xde\xc6\xa7>\xd8\xb0\xdf\xeej\x91\xb5\xc5F\xc3\xe3\x98\xd1Q \xf1\xda\x90\xa3\xb8\xe4\xa7\x83\x18&\xad#\x12\xc7\xa6|\x90\x08\x0cLM\x0b\xa3\xfa\nVf\xab\xe6\x15;\x96B\x85\xf3pw\x90\x1e\xdai`T\xc2\x19\x8ca\x95\xcd\xcc\xbe\xcc\xa7\xae\xe4\x08\xb7\xe6Ni\xd5L\xba\xd0\x0b\x87,\xf1\xa4\xce\xf4Ty\xcf\xb4\xf4\xec\x0f\xc4\xac\xa9U\xdb\xdaq\xe0\x05K\x903\xb7|\xab^\xdcR\xddn\x17\x1fV\xe4_Q\x97\x8du\x7f\xcf\xfe)\xa7\xe5\xee<\xb6\x1d\xa4\xe5\xabZjF\x84\xceBEq\x18i\x81\xed\xb3\x87\xb8\xa9\x15I#\x1d@\x9c\xfbx\xa5\x18\xcb\x06\x10(X\xfb\xb2\x0b\x8f9(\x0b\xb1\xed\xf4 \x9e4\xba \x8a7(\x16\\\x1f{\xb6\x0bYd%\xa2\xebW\xf47f@\x06\x9dU\xbf[\x9d%\xaf\xee\x1e\x94\x01E\x8fUcE\x92\xdas\x8c:i\xf55\x16So\x01\xba\"\x9b\xd5\xd2eQ \xf8\x85\xdb u\x1f\x82H\x82i\xc4\x9dNy\xe5\xf0\xeb\xfaKWik\xa3\xdb\xe1^\x0eE\x1c|\x87I\xbbN\xe8G\xeb\xack\xadc\\\x0f\xcd\xfc\x91~\x10_\x1cC\x07\xf5E\x9c\xaa\x9d\x88&l\xce\xf5\x978\x9c\xdbX+\xea\xfa\x8f\xbe%*\x90\xb4\xd6S9\x00\x92g\x9c{\xd50$~=S\xf5\xaa/\xc0\xdd\xcb1C\xe0\xed\xb9\x03@/\xc3\xa12nZ\xb5>?\xaf~\xe0\x99\x94\xc3]\x9a\x9fLJ\xe3\xac?\xd4\xbcX\xafg?\xd6,`\xc0\xf8tu\"\xa5O\xbe\xe2\xab\xd8\x84\x82ZU\xde\xefN2IZ\x12dp\xa7|j\xda\xac\xec\\\x80B\xaa7\xb7)\xe9E\xa2\x91fl\xe9Q{\x0f\x03\xe2\xe6 \xf0V\x9f\x92m\xfe\xea\xc6\x9c\xed\x99\xact\xd5vz\x8cI%\x13\xd7b\xf2c\xf2\x8a\xeb\xb7\x9e\xda\xa9Bf\xae\xaa\xbe\x8c\x93\xb0/\x93\xe0\xce\x02\xc1\x1f\xd52\xf9\x17>Ix\xd2\x97\xcdJ\x86B\xfa?\xfe\xc8grI\xc4\xd1\xd7O\x99\x14\x99\n\xba1\xfa\xef\xb5\x17W\xaf\xc7\x11\x0d\x12\"*\xf86+\x1c\xe0i\x03\xfasCM\xca\xac\xe2\xf6\x97R\xf0\xf2e\xd0V1\n\x0e\xd8o\xae6\xb2\xa0]\x8a\x82\xc4\x0b\x99l2\x81\xf0\x14^\x9csLW\xe5?\xccBT&|m\xfe\x13+\x8d\x91+V\x81\x1f\xa5\xfb?66^\xa3?\xf8\xc4\xb5ID\x03\xe5\xda\x91\x8b\x0e\xb8\x17\x0cJ\xb9\x97\x93=\x15L\x0e\x8f\xe2\xd0\xad\xee%5\xc1<\xffjH\x8c\x80\xab\xee\xfc\xa6^\x1aFs\x9b\xfeb\x0dpE\xa7|s\x0eDZ\xfd\x17~\xcd`\x89\xb1O\xdb%{r\xbe\x07\x14\x98:U\x95\xe7\x06\xd9!U%WB\x8eb\xf9^3\xbbIR\x1c\xb9\x90\xaf_\xd8cD\x95\x84E\xca\x06\xd8\xcc\xe2#\xd1\xca\n\xf5+J\xd61\xae_\xd3\xf7d\xad\xe7m5\x9b\xd6\x9b\x93\xea \x01\xca/r\xa2\xc0e\xaevfO\xd8{\x9dy)\n\\\xf56\xb4\xcc$\xa5\x86\xf8seV\x7f\xb8\x80\xbeJV]h\x12\xdf*\x91\x8b\xd3-f!\xed\xf4\xb3WOw\xeb 8\x99\x0e\xa8\xe3p\xa76\xa9\xbcgG\xcf\x9aJ\x1d\x82\xf6\xd2<\xc0\x92\xbf\x19\xf2\x18\xa1\x8a\xa9\x9f\x93\xa3\xd7\xc8\xd1\x9b\x94\xff!\x94#t\x0b\xea\x04$\xb0\xee(\xcf\x0dR\xbf\x1f#<\xf5\xb4\xbc\xd5$\x89D\xc88\xae_\x1e\xf2\x90\x9c\xe1$\xae\xd5Q\x8b\xa8\xb2qG\x0e:^\xb0\x08\xeb;\x1d\xc0K(\xb3\xf2\xce*\xbf\xee\xd7\xf5m/`\x97urt\x87=\xc4\n\xc0\xb1w\xc6?\x8c\x80g\xc5z\x89\xe0w\xda+\x0f\x0b\x19\x0d\xa0\x02\xf6\xf3\xc8\xc5C\x13z\xd8\x87\x1eZ\xc7\xbf9\xa0\xa0,\xdenU\xad\x8f\x8b\xdbb\xea\xe9C\xdd:\xf2\xa4.\xf4\xee\xf7\\\x0e\x9b\xd5\xeeQ\x1b\x11-\xb6\x80\xae\xc9\x16\xb5\xd2\xef\xbc3\x16\x83\xb1\x03xay7\x9f\xdc\x9f\x02\x98u\xe7v\x824\xe0\xe80\xa9\x0b\x93:\xdbZ\xcf#G)Qh\xcc.\x9bF5\x07O{w/\xc1\x95\xff2\xaad\xc1`\xb5\x1c\xae(\xd6\xef\xe4\xcb\x9d{\xc5\xc0\xc2.\x8d\x93u\xc4\x1dd\xb5\x86\xcc\x01\xb7\xa1;\xea\x8f!\xf3\x92\x92\xe7\xaf\xdbST\x057T\xd9\xebt\xa5\xcd\xd3\xe0i\x01\x0e\xbd6\x7f\x8e\x17U\xc8\xa5,\xeeK\xbba\x80\x0e\xf2\x14rN\xf8\xa4\xa6)M\xd4\xcf\x1a\xbb\x912w\x88\xd7\x040)\xd0&4\xd1\x9a\x97\xe3\x01\x9c\xc0\xe4\xa1\xc1\xdeo(\xd2\x89-\xa7\xe6d\xdc\xe1M)a\x1dl8E3#v\xcd\xcbc\xffV\xb4\x13\x1d\xb7bH\xeb\x8f\x8e\xf3\xc1\xbe\x94\xae\xf5&\x9a\x84\xa0\x08\xa3\xd9\x1b\x90R)Q\x1c\x87q\xc2\x0e\xa8\xd4\x06\x18?Y=y0M\x9c0BIg\xd5{\xfa\x94\x9f\xb3\xd2\\\xb4\x90\x1f\x8b(\x1b\xaa1V\xe9\xc1\x0eXu$\xe2\x92\x9acc\xf4)b^\x80E>\xe5C\xd2\xea\xfaZ\xebd/\xf9&\x15-v\xf9;\xdb\nx\xd3\x0b$e\x8fl\x08\xdf=\x7f\x92]\x05U&\xc4\x8b\x9f\xc0M/\x86\xae\x882\x9f>P\x9e\xb4\x06S\x90\x8c\xd6a\x8f\xba\xac\xa44P+\xb99t\xc7\xb1\xf0\xb7\x03x9\xad\xbc\x971\x02\xeej\x8c~\x9a4\xaf\xc6\x02\xdfAV\x00\x0d\x9e\xd6hH\x0d\xfav\xe0\xff\xb4,\x94\x9d\xee\xf2kaq\xb7\no\x9aTZ\xe5\x1d\xf9J\xef\xff\xbc\xfc\xdb_;I\xb8\x8e\x1d4\xb3\xa3\xc8\x0b\x96\x9f\xee\xde\xfd\xd20\xea:I\xd2\xf5\xed\xe8o/\xff\x7f\x01\x00\x00\xff\xffPK\x07\x08_;\x94/\xe8Y\x00\x00\xa8X\x02\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00 \x00swagger.yamlUT\x05\x00\x01\x80Cm8\xec\xfd{{\xdb8\xb2/\n\xff\x9fOQ\xdb\xef\xfb\x8c\x93\x19G\xbe\xe4\xd2\x89\xf7\xca:\xcb\xb9ug\xba\xd3\x9dI\x9c\x9e\xb5f\xcel\x05\"!\x89m\x8aP\x08\xd0\xb6\xd2{\xbe\xfby\x00\x90\x14)\xe1J\xd2\x97tP{?k\xd2\x16\x01\x14\n@U\xa1~\x05\x80^\xa0\xd9\x0c\xe7\xc7\xb0{4:\xd8\xbd\x93dSr|\x07\x80%,\xc5\xc7\xf0\x82\xd0\x05\xa1\xf0\xe1\xe5\x8fp\x1f~\xc23\x14\xad\xe0\xfd\xab\x0f\xa7\x80\xb2\x18f\xef\xdf\xbd\x80\xef\x11\xc3\x17h\x051\x89\xe8\x1d\x80\x18\xd3(O\x96,!\xd91\xec\x9e\xc8\x8f\x93\x8c\xe1|\x8a\"\x0cS\x92\x03e\x88a\xf8\\\xe0<\xc1t\x0fRY+\xcbQFQ\xc4\x0b\xd2\xdd;\x00\xe78\xa7\xa2\x92\xc3\xd1\xc1\xe8\xe0\xce\x12\xb19\xe5\x8c\xedg$\xc6\xe3\x8aM\x80\x19f\xf2\x1f\x1bm\xbf\xc9\xa6$_ \xfe\x1f\x80&\xa4`\xc0\xe6\x18\"\x92e8b8\x06^MY\x8e\x16\x8b\x05\xcaW\xc7p:\xc7\xb0\xcc\xc9\x12\xe7,\xc1\x14\xc8T_\x86\xa1\x19\xad\xda\x05\xb8\x0f\xdf\xa3\x04\x89\xce\x96\x7f[\xe6$.\"\xdc\xfa\x06-\x97i\x12 \x96\xf6\x7f\xa3$+\x7f\xca1]\x92\x8c6\xbf\xdd=:8\xd8]\xff\xe7F\xd7~&1\x16R,h\xe3\x13\x1a\xcd\xf1\x025\x0b\x01\xb0\xd5\x12\x1f\x03\x99\xfc\x86#\xd6\xfaa\xdd\xcbv\x01h29\xae\xc6`\xe3\x13SqN\x93\"I\xe3q[BM\x92\\Q\x96'\xd9L\xf1A\x94&8c\xe3\x0c-p\xb7\xf2d\xb1HX\xa7\xa23\xd2\xa9XgV)\xce\xcfq\xde\xbd\xab\xda\x01\xb2\x94\xddXCM2\x0fm\x12{\xb7\xc5iA\xb2\xe4\x0c\xe7\x9d\xca\x02\xe0K\xb4Xrmt\x8e\xd2$F\x8c\xe4\xf7\xb9\xc4\x14\xdf.s\xc2HDR\xfd\xcc\x05k\x17\xc5\x17GK\xddO\x0e\xfcB\x93\xe7\xef4\x9fLR\x12\x9d\x0d\xd5\xc8\xe1\x81\xe6\x1b\xb4\x1c\xac#\xaa&2\xcc.H\xae\xe9\x86\xb5\xfa\xba\xea\x19J\xd0\xfd#\xd5R\x9e\xa3,\xc3i7=\x92&\x94\xe1l\x8c\xe2\xb8\xf7\xc4\xdb=|z4:|\xfcd\xf4\xe8\xf1\xe8\xf0\xf8\xe8\xf1\xe3G\x8fw}\x97cK\x85\x9f\xe2,\xc6\xf9\"\xc9XU\xa8'\x87\x07\xa3\xc3G#\xd5\x10\x116\xd7-\xbc\x16G\x0b\x92cH\x1aF\x93d\x15k\xd4\xc0\x9b\xc2\xb6Td_f\xecr\x9cd1\xbe\x1cj\x8a\xee\x92L5,\x9c\xf2e$f\x02\xa6Zn\xbc[c\xd1\xf2x\x7f\xff`$\xfe\x9f\x98\x15\xdf\xad\x9b\xdf}d2\xe1\xafQ\x92\xe2\x18\x18\x11~\xd0J(\xe4\xb5I\xdf\xa7\xab,J\xb2\x99,\xde\xf0pjO\xe5\x83\xfc\xa0\xf4\xa5\xc8\xd4\xe4\x9a4\xe6\xda\xfbw/\xee(\xb8\xf9\x1e3H\xa4\xb3#\x18I(DE\x9e\xe3\x8c\xa5+\xa0\xab\x8c\xb7t\x91\xb0\xb9\x9cL\xe2\x9bjN\\\xbd\x9f\xd3\xe8\xe9U\xf8;-Q7I\xd65!$\xc5(s\x1c\xd6\x0f\xc2\x9eK\x7f7C)\xe0<'9\x1fP\xa1\xed\xe9~\x8a\x18\xa6L?\xac| \xf8(\xc8\xef\xa4\x8d\xf0\x19\xd5\xab\x1c\x8dS5_0\xdcP\x88j\xc7\x0b\xcc\x90n44\xda\xc6\xaci\xe6\x18\xc5f\xdf\xa3\x97\x12\x8b\xe6(\xc9\xc6:\xc7\x08\xbc\xd5J$\xb6\\\xf3b\xa2\xb4\x88 :\x94\xcc\xe6\x1a\x07\x17\xea\xf6\xb2b1\xc1\xb9\xbd\xbdC\xcd',\xd19\xa5\xe0\xdd\xa7\xdd\xa3\x83\xc3\xef\xee\x1f\x1e\xdd\x7fppz\xf0\xe8\xf8\xd1\x83\xe3\x83\xa7\xa3\xa3'\xdf\xfd\xe5\xe0\xf0\xf8\xe0@\xa7\xb3\xb3b1f\x97V}\xed\xdaQ\x9d\x8f\x94\"\xca\xc6r\xee\xd9G\xd10U\xc0i\xbap\x9a#:7\xfd\xee(^h\xf6\xee\xd5\xabG\xaf\x1f<>z\xfa\xfc\xd1\xe3W\x07/_\xbex\xf0\xe4\xf5\xc9\xcb\xc7\x8f\x0e_\x1f<4\xd4\xb6D93\xf2\xed(\x04p\x16\x04'F\x18Jm\x1f9\x8euE\xd61\xaf\xc8>\x12\xe03\x1a0\xe0\x88\x08\xb9\x0c9\xff\x1f<2-\x00\xb9k\x1e\x9b\x05\xe2$\x88\xa1\x04\x10#\x86n\x13?\xf5\xee\x93\xde&\xae2|\xc9\xc6\xb7\x93\xb5\x88\xfb\x17\x19-n\x15Sh\xb9\xbcM\xec\x88\xb5\x97cZ\xa4\xecV\x89 \x9f'1\xce\"|\x9bx\xe26\x85P\x9c\x0f\xb4\x91ky\xb7\x13\x1c\xcd\x1f\x1c\x01\xce\"\x12\xe3\x18\xca\x16\xec]\x92\xde\xdaa\x8c\x97g\x8f\x1eF\x05\xfamv\xf6\x05\xa3\xc7_\x96\xb3\xb3\xcf\x0f\x1e\xb3\xec\xb7\x8b\xf8\xcb\xf9C4\x8d\x1e\xc4G\xbaP\x901l\x00\xaeV\xd7\xcd\xe2Z\xc2N\xe0*\xb1\x96\x9cG\x03\x06\x1c\x91e\x8e\xe5\x9c\xb7\xea~\xfd\xaa\x03\xdb\xca\x03\xf71u\x1d\xd1\xda?\xb3\x9b.I\xce\xd2]\xd7l\xc9\xe9\x91\xe4\\os\xf3\xae\xdd\xa0K\xb2\x85#$]E\xcb9)2\x83v\x92t\x15\x0d\xb3d\x81)C\x0b\x8be\xeb\xd8x\xa7`\x89$\xd1\x9e\x13ONZ\xa3\xe6I\x17\x8a\x92d7\x14\x92\x1cW\x15x\xac,pVX^\x83\x01\x03*\xad59\x18\x13I\x1e\x82\x02Oa\x81\xbby\x91\xe41]*r65\x92\\\xc7\x0f\xfc\xc7\x10\x06\x1fG\x9a\xcc2\xc4\x8a\xdcq\x999qZs\xf8\x9f\xf7\xad}\xfa\xae8}\xf1\xdd\xc3\xbf\xa5g\xd9\xe7\xff\xf9\xfb\xab\x8b\xd9w\xbff\x8f\xdf>\xf9e\xf1\xdd\xeb\xe2\x1f\x07\xaf~y8\xf9\xed\xbc\xf8\xedq~\xf1\xc3\xe1\xe2\xf4\xe3_\xf3\xf7\xc5\xdb\xb7\xff8?9\xf9|\xfa\xf4\xd7\xdf~\x9e\xbd;x\x7f\xb2\x7f\xfar\xf9\xb8\xd8\x7fzt\xf29\xff\xc7\xf4\xbf\xff\xfaa\xf9\xfco\xcf\x9e\xd5\x0dw\xcb\xb5\xdb\xad\x92\xed~\x97\xe6\xe0\xdfe\x15\xba|;$\x95\x06 \xfe\xcf\x08\xe7\x0c%YiI\xca\xcf\x87N\xbd[\xa2\x1c-0\xc3y\xeb\xe3$;\x86%b\xf3F\x7f\xc5\xb9\x806/\xb0)\x86\xe7\x82\xf9\xador\xfc\xb9Hr\x1c\x1f\x03\xcb\x8bf\xaa\xbcf\x11]\xde\xdf\n\xc3w\xca\x10l\xc8\x92.q\x94L\x93h\x9b\xb9\x90/\xe8\xb0\x97q\x0de\x04\x94\xa6\xa2\x80\xd2\xd8U\xfc\x90&\xc8\xc1\x89p\x12\x028\x0b\x02\xdc]\x06\xa7\xb1\xae\xc8:\xe6\x15\xd9G\x02|F\x03\x06\x1c\x91\x80\xd2\x04\x94\xc6\x81\xab\x80\xd2\xf81\x15P\x1a'\xbe\x02J\x13P\x1a\x9b\xb5\xbbU(M\xc8\x17\xac\xc9\xdaYp\xea0\xb89gNN\x08\xd8\x86\xaf\"\xbbC\xe6$i\x18@\xda!_0\xecDK\n;\xd1\xab_nM\n;Q\x0d9\x8f\x06\x0c8\"a'\x1av\xa2\x0e\\\x85\x9d\xa8\x1fSa'\xea\xc4W\xd8\x89\x86\x9d\xa8\xcd\xda\xdd\xaa\x9dh\xc8\x17T\x95\xef\xb5\xb3 >q\xf0\x89\xb5\xe4<\x1a0\xe0\x88\x84|\xc1M\n\xf9\x827\xd1p\xc8\x17l\x91\xddPHr\\U\xe0\xb1\xb2\xc0Yay\x0d\x06\x0c\xa8\xb4\xd6\xe4`L$y\x08\n<\x85\x05\xee\xe6E\x92\xc7t\xa9\xc8\xd9\xd4Hr\x1d?\xf0\x1fC\x18|\x1c\xff\xf8\xf9\x82\x0fM\xd9io2aq6\xb3\xd1v\x1f\x1e<\xd4\x17z\x8f?\x17\xf5\x8dweQ\x88 \xa6\xd9n\xa3\x8aNy\x8a\xfb\xeb\xd0\x07f\xdeW\x03\xd6\x85\x81\xe2\xab\xcaS\xec\x94\x00\xd8b\x0c\xd0\xf6}\x86W\x9c\x0e\xa8\xb3\xe9\x86I\xbd\x1e\x08]1\xb5O\xa6\xf5\xc5,J\xd0\xa6\xf4,\x9e\x96\xc3\xf2\xec\x1e \xd8\x08\x0f\x9c\xa3\x94\xf3z\xf8\xf8r\x85\x17K\xbcX.\x9f\x1e]>\x9d\xaf\xbe|yz\x91\xcf\xa6O\x1f\xe6\x8f\x7f{:\x7f4=\xbax\x98\x1d\xa5\xca:\x97\xc5d|\x86W=z\xe3\xa2djv#\x92\xd1e19\xfc\x12\xfd\x16\x17x\xf9\xf9\xe0\xbc8\xfa2;\x9b\x9d=|\x8a\xa7\xe8 \xfb|\xf1%\x8bQ\xf6\xf9\xd1\xe2a\xf4\xdd\x12=(\x1e\xa2\xe5\x97\x87\xb3\xa3\xfc\xe9\x8c.?\xcf\x1e\xcf\x9eF\xf4\xc1\xd9\xd3\xa8\x98*\xdb:',\xc9f\xe3%\xb9\xd0\x01s~]\xda=<\xd0yFu\xd0h\x99'$O\xd8 \"\xdch\xaf\x9b\xea\xda\xdd\xd0]\xce\x99\xd6\x1b\xca!\xa4[\xf7M\xb7\xde\xd2\xb6\x8d\xb4\xeb\xa0m\x05\x05m\xdb \x87\xde\x04m{u\xdavx\x07\xf5yu\xde\xa2\xf4M3\xc2\x00\x9d\xa3$E\x93t\xad\x87\xba\xaayv\xc9\x95;\xa2s\x85j\x8f\xf12\xc7\x11b\x1bJoC\xe3\x9f^\xc2d%\xf6I\xe5\xef[\xda\xbd\xf1\xee\xcb\x1d\x05s\xef1\xcb\x13|\x8e\x015\x9f\x88\x81\x82&\xd9\x0c\x12FE\xdd\xa3\xb2\xe4\x15Z\x85u\x0f\xb6X<\xbd\xdc\xfc\xd9b\x0e\xb6&O\xc3\x1c<\x7f\xf1\xfc\xd5\xd1\xc1\xab'/\x1f>\xfe\xee\xd1\x93\xe7O\x1f\x9f\xbcz\xf4\xe4\xc9\x83\xe7\xdf==z\xf2\xe8\xc9\xd1\xd3\xc7'/\x0e\x1e\xbfzt\xf8\xf0\xc1\xa3\x87O\x0f^?\x7f\xf9\xe2\xe4\xd5\xd1\xa3\x93\xef\x8e\x9e?x\xf1\xe2\xbb\xc7\xcf\xefT\x1c\xf8Z\x93Ky\xc5;\x93o\xe0\x9c'\\{mtk \xd3\xa1\xde5\x1b\xd7\x95Y)\xbd}\xfc\xe8\xf1&\x7fF\xa3\xa6\x89'\xd4\x1c>x\xfcd\xe3G\xa6\x08n\x1a\x8d\x96\xd9d-\xa8\xe2\xb6z\xb0XO\xb0\xc5\x8e-jm\x8a\x8dPQ/\xb4f\x86\xacAq\xa3\xbaE\x0bRd\xd6\xac\xb2\xdb\x15W\x8fqF\x16\xe6O\x9c\xfa^Q=\xfd(Cg\xaa\xa7q\xd6d\x13\x97\xa4.m\xef>R\x1a\xd7\x05^t{W\xc9\x12\xb1\xb2\x0e\x89}0\x1cbbN\x82pq\x9a\x00\xde\xbe\xfa\xf8\xe2\xcd\xdf^\x1e\x1cM\xe9\xcbw9z\xf2\x96M\xde\xd3\xd5\xf3\xc3\x8b\xef&\x9fO\xdf>z\xf4\xf7\xe2\xf0\xc1\x93/\x7f\x9b\xbc\x8e\xfe~\xf9\xf0//^\xafN\xde\xcc\xf0\xa3\xbf\xff\xfcn\xfa\xe3\x9b\xe2\xfc\xcb\xf3\x7f<~\xfav\xf5\xf9\x07\xfa\xf9\xe5\x93\x0f\x87o.\x92W\xcb\xbf$\x1f'\x8f\x7f\xfd\x10\xb3t9\xfb\x9fg\x9a\xa6->\xa1\x83 \xc1I\x98PUe\x9c/N\xf2\x84\xa6LY\xbd\x01\xdc\x7fWL~\xc4\xab\x0f8Z\x1e=z|\xa6K\x00\x05\xb9\xcd(\x06\xe7\xe4\xe4\xfc\xcb\xc1\xc3_\xe7\xec\xc7\xbf\xce\x9f\x9c\xbcx\xf1\xeb\x97\xf4\xcd\x13tJ\xe8\xf7\xab\x83\xe4\xec\xf5\x7f\xff\xf8\xe6\xd7\x1f\xfe\xf6\xe0\xb7\x1f\xdf\xe6\x84\xfe\xa0SVQ\xc4\x97\xdfX\x9a\x11\xdb\x98\xb8N;=\xecC\xf1\xe7B\x0f\xf4\xc3\x00-\xc9\x1c\xa0AM\\J\x8c&N\xcb\xeb\x0c\xd1\xf1\x05\xca\xb8O\xda\xa5x\x1b\xca:\xd0\xec\x15x#\x05\x1d\xa2\x89\xc7\x0f\x1e=T\xb5`{:\xaf\x97\x897.s\x97EnT%\x0e\x02\x00\x97\xd5\xa9\xac\xc5\xbc\x8dyS\xed_\xca\xfd\xcc\xab*\xd0^'\xd8\xd8w/\x0e\xbb\x93\xf5\x1bL\x18\xe5\xd1\xbc\xf5p\xe5\x1d\x05[\x8a\xcf\xf8\x96\x08\x9f\xe3\x8c\xd1A7.\xe2)\xa9\x86t\xe4\xcee\x81)E3<\x92m7~\xd6\x0cT\x8b\xf9\x0ds\xd6\xdc\x81\xc9\x0e\x00-\xa29 \n\xbb\xedv\x9eQ\x9c\xc5\xbbp1O\xa2y\xa9!($\xedW\xce\xf8VcJ\xd2\x94\\\xf0\xbd\x1c\xce\xe2%I2v\x0c\xbb\xdf\xbf:\x15\xa3\xf6\xff\xa8\xea\x1c\xf1=.\x066G\xedI\x8cQ4\x87\x05\x89\x8b\x14CL\xa2b!\xd8\xe3;DrQq;\x82\x94\x903\xf1\n\xe9\xe5\xe5\xb8\xfc\xdb\"\x86$\xe3\xcc\xb4\xaa\x8bH.\xf7P1gN\x06<\xee\xd3\xf8l?&\x11\xdd\xa7K\x1cA\x9c\xe48b\xa4%\xf3\xc6N\x8es\xeb5BT\xd8\xb9\xe1F\x88Of\xb9\xa9\x935K\xc9\xb6>\xd7I\xf9Om\x9e\x9e\x95\xc9\x82\xb6\xc0\xd4\xc1\xe3\xe5\xe5\xe5\xf9\xaeZ\x1e^u8 n\x89fX'\x97wh\x86\xb7\xf7jR\xa2I\xc6\xf0\xcc\x16\x8f57\x9d&\x8bD\x1b$~\x8b.\x93E\xb1(\x9b\x072\x95z\x19\x968\xdf\xe4y0\x86\xd8\xe5h\x91d[\xd1^]\xfd-~[\xba\x89d2\xb6[N\x9d2\xae4\xcb1b\xbc/9\xe0\xcf\x05J\x81\xcd\x13*\xd5\xb8\x9a\xeb\xa3G\xcel\xa3\xcb+c;\xc5\x94rU\x91\xb93\xfeD\x18\xfe\xf27\xef0\xcaI\x9a\x02\xbb\xa4\xb0@,\x9as\xcd\xd1\x8a\xa7H\x8d\xd3(;PDE\x9e\xb8\x88\xd4\xfb:Y\x9f-l\xb1\xe9K\x0fZ\x19\x9f\xf4Z\x8f\xb7G\x95\x9at\x90n5\x8a%\xdd\xb5\xb2\x07\x9b #\xca\xac^\x93\xfbvU\xf8\x86)G\xc5\xc1Ws\xd9\xd9\x0e\x1c\x7f\x93dN\xce3\x8e\x8a$CDN\x92*.W\xfe2\x8c\xa7\xac\x8d\xd4I\xb29\xf3\x92\xac\x91)\xa7a4D\xf0$9t\x19\x1c\xbb\x0d\xb6\x98\x9e$'\xae\xc13`e\x93&\xb8I\x14\xdc%\"\xc9U.\x92\x9c\xa2\x7f\x92\x9c\xa5T\x91G$P\x92\x9bx%u\xe7F\x13\x1b\x94\xa4\x8f\x10Jrj\xd6!\x96\xe7<\xa8\xae\xc3\xe9\xd4&\xb8\xf6@\x92\x8b\xc6\xad\xe8\x86\"\x8a\x92\xacqEI\x8e\"\x07\x0f\xb1CU\xad\xf5+/\xc9C\xef\xa8\xa3$kt\xa3\xa2\xae\xbc\x0d\x10\x87\x94\xe4\x1a\x8d\x94\xe4\xc1\xaeCdR\x92=>)i\xc8\xb6u\x11KI\x0e\xf3\xd5e\x9ejc\x98\x92\x9c\xfac\x8bgJr\xaa\xca-\xb6)\xc9\x1c\xe1\x94\xe4\xdf\xa8.\xda)I\x1f\xf3,\x7fw2\xef\x0e\xa6\xddax%\xb9\x0c\xb2$\x07%\xe8,0p\xd7\x1f\xca\x1a\xddrL\xa8\x0cJn\xecG\xbb\xc5W\x01\x96\x84\xd6\xab\xc9'\x82\xfa<'(\x8e\x10\x15ik\xc9,\xc31\xb0\xcb;\x8a\xa6\x95\x1f\x02#\x80`Z\xa4i\xf3\xf5\xfb\x88d\xb4X\xb8ER\x87\x08\xbaNH\xac\x8aj\xd4\x1c\xeb\xa4\xb9a\xdcO\xe7\x98wiQP\x06\x13\xbc\xee\xe6\x07\x16\x9f^\x8e\xc4\xcf\xb4X.I\xcep\x0c\x93Z\x1c\x0b\x12c\nI\x16\xa5E\xdc\xf6\xf5>\xed\x88p\xc8\xce\xa7\xbb9fE\x9e\x01\x9a2\x9c\xf36\xe49\xb4{{\xf0i\x87\xae\xb2\xa8\xf9\x05\xce\xe1\xc5\x1cGg\xa7\x97\xf7\x005\xa2\x97\xb2B\xd4\xfe<\x17\x11\x16t\x81V\xf7F\x8d/\xb5\xb9/C\xc5:B6E\xc8\xa6\xd8 \xa7\xfd\x94C\xdf+\xf2\xd8C\xd9\xc4%\xa9K\xdb!\x9b\"dSX\xf78N\xf2\x84\xde\xfb\x1a\x07\x8f\xc4\x9f\x93\x01v1\xae\xfb\x17'\xe6\xac\xfb\x06\x97\xddJ\xdf\x96\xb8E\xd7\x198M\xadum\xc2\xe2\x97?{#\x17\xa7\x97k\xbf\"\xc9f\xe5&\xa9\xf1\xf5@\xf6;\xe2\x0e\xc6x`+\x1e)\xa5\x06u\xa5\xdbHRE1R]\x1f\x0f6\x99\xdb6I\xb6fm;;[\xf9$\x9bv\xb3\x02]SkBN\x8a\xdb\"\xd4MN\xe5\x89b1\xf9\xc4\xff\xd5\x8c\x14\xff?\xa6\xb9\x07\x8f\xd6X\xe5\xe6\x07\xe5\xf4\x82\xc3\x03\xf57b\n\x89\xff\xeb5\xdc\xf7aW\xad\x1d\x15?\xc48M\xceq\x1eV{X\xed\x82\xbe\x9d\xd5\xae\xba\xb1\xf0\x8f\xbe\xda\xfb\x9c\xeb\xe8x\xc3\x81\xf9\xa8\xc6\xe6\x82\xea\x16\xdf\x12\xa7\xa0\xe4y>Y\xb6\x19\xee\x1a \x8f\xf0\x95\xa8z\xe3|\x13#\"s\xe4d\x91d\x04.\x92\x1c\xc3\x94\xe4\x8b:\xe5M\x17JR\xd6u\xb7\x8c%\x91\x1c2\xc2\xee\xc14'\x0b\xf8\xeb\x87_~\xe6\xadL\x10\xc5\x8f\x1f\xde\xaf\xce+\x8a\x06\xeb\xea(\xce\x13\x94&_p\x0c\x93\x15\xc3\x15\xeb\xb7$\xc6\xa6\x1b\xc82\x96\xc6Hy\n\xb3\xf1]\x08N\xd9Vd\x08N\xd9\xfb)\xc9\xde[I!8\x05\xf6FBp*\x04\xa7Bp\xcae\xda\xddDp\xca?\x98$M\xf0\x05\x12\x07\x04\"L\xe9\xb4H\xd3\x15\xc4\xb8\xbc\x16!\x8b!\xc7\x95\xd7\xd1\xa8\xe5\xca\xed\xb0\xcd\x11\x15\x8f\x18*|\xa2\xfb\x9b\xbe\x908E\xc0D?\xd7B1\x82\xaf\x0d\xa1,P*\xfd\xb9F\xe7\xbb\x1d\xd2\x17\xde\xa9\x14\xab,;\xb0w\xfa\x12+GY\x85#\x0f\xe2 )\xafT\xda\xbe\x1a\xad}\x92\xaed\xb5\xfc\x0c\x92\xac\xba[\xad\xe5iB\xbf\xeb\x96\xba\x1cJ\xf5vEN\xd4R\x07\xa3\xef\xb1m?\x95v\xd3\xa0\x9bLZIk\x1f-\xab\xc3b\x0f\xf5v\xd0\xb5\xde\x96\xdd\xeb\xb5n\xca\xb9\xdeX7\xfbb\xd71\xc59-+u\xd8+5\x8e\xfbg1D$\xc9\xa8\xdc\xa0\x90l\xbd\x9c\x18\x01\x94\x116\xaf\xb1\x06\xc3\x82\xba\xee\xcd\xc9\x1fk\xed\x19\xb7\\\xe5p\xe8z%\x92h\x85\xed\x14\xf1\x07v)\xe00\xde\x9f\xf6\xc5\x08W\xbd \xe3[\xdbq\x8e?\x0f\xea\x95\xf19\xd9\xd7\xde\x95\x032{\x8a\xe6\xf9\xe3\xcb\xf9\x9c=\xca\x17\x9f\xcfq\xf6\xf8\xe8Iv\x96^\xa6\xc5\x97\xd5\xf9\x93/O\x7f\xfb\xfc[\xb4\x88\x94Um\xacO)\xebr\x12\x91\x1c~\xc4+\xdey1\\|\xd1\xccp\x86s\xc46\x02\x02\x8a\x9a;\xc7\x95\x1b\xbd\xdb\xf9\x803\x06\xe7 \x82\x17\xa2\x9f\xf0+Y\xa1\x19\xce\xe1\xff\xfdxppp\xf8\xfa\xf1\x93\x83\x1dE\x0d\xe6W2\xdd\x9b\x97\x8d\xde\xff\xa1\x98(\xbe\xfb\xaa\xbc\xa2C\xcd\xad8\xbd+6\xdf\xb9\x83\xe2\xdf\n\xca\x16\xd8\xbc\xedtj\xe8pt\xa4je\x8au\x0e|\xd8\xd2*\xc8\xb7=\x0dfD\x93E\x91\"f\x9c\x97\x13BR\x8cT\x9a\xa1Q\xff\x14\xa5T\xdd\x1f\xd3\xc9\x8b5\xbd\xa2,\xe1~;\x9fm\"\xba\xbb\x11\xa6\x8cP\x96\x11q<\xa3\xa08\xde\xbc\xcdfM\x11\xc9~+2YH\\FQ\xa9\xb91\xc9\xd2\xd5\xbd\x8dR:9\x9b\xa6\xdcU\xdd\x15`\x9cb\x0e\xc3\xed0\xb5\xcc\xd3\xca\xa7\x8d\xc6tR{\xe4G\xe6\xdbN\xeb\xc0\xa0\x8c\x0bV\x83\x14\"\x83\x92\xba*\xf4\x10\x19l\xd1\xb5\xa8\xd1\x10\x19\x0c\x91\xc1\x10\x19\x84k\xf4\x81\x9d#\x83\xd5\x89\xdb\\\xbe$\xb4.\xd5)\xb4!#\x1b\x8dk\x8f\x91P\xc1\xe6\x8apn\x7f\x18\xa4\x11\xff\xab/\x104i\xc2\x93\xa2\x9e\x99\x1e\xa3s\xa5\xb1\xd9\xc6g\xd7\x1c\x8a\xf5\x9et'j\xa9\xf3\x01\xd9\x92<\x0ch\x03\x94\x0e\x96A\x1bj\x9c\xa2\x1e\x8e\xf1\x00\xae\x8d\xf1\x99\"KYa\xe9M%\xc3\x8e\xacE\xbe\xedi\x02[\xcbb\x92&\xd1\xed\xde<\xf4\xdb\x00tp\xa7=\xfc^\xdc\x84\xf4\xf8\x98&\xd9l?\xc6)\x9e\x89\xe7\xc3\xb8SQ\xfe\xfb$\x8e\xf3\x7fW?%$\xa3]<\x8cF]w\x94\xcc=\x97\x10\xd8I\x14\x9dT\x90\xc6\x14^V\xe5\xea2\x1d\xfd\x0d\x7f\x15\xeci\xf8P\x9aBCD\x12\xc5D\xeb\x8e\x97e6m\xde\x07)\xf8\xf2/\x1ef\xcf\xdb>8:%W\x0d\x8c\x97\xe20=xn\\\x16NO\xa6\x1bk\xa0s\x94\xab\xd7\xbc\xb1X\x99^\xa0/\xf7-\xee]\x9c\\\xd6z\xd0\xb7\xbc\xaan.k;\xb3`\x9dBPL\x16 k,\xc2jQ\xf9e\x16o\x95\xdf\xe2\xebt\x8ea\x89(\xbd y\xcc\x95T\xd3\xe7e\x04r\xbc \xe7x\x9d\xc5\xfd\xe3\xdb\x0fN+\xcf\xcb\xed\n(w@\xb97h\x007\xf8\xfa\"|\x01\xe5\xde\xa2\xb0\x19\xd0\x7ff\xdb\x0c|\xe3(\xb7\x83[e\x14t\xabo\x8eo\xecnj\xfc\x18/\xcf\x1e=\x8c\n\xf4\xdb\xec\xec\x0bF\x8f\xbf,gg\x9f\x1f*\x1d0\xb3.[\x97~\x1b\x1f\x9c\xb1\\i~z8\xa0\x9d\xcfv8\x04\xbf\xc1E\x11\xf7-\xef\x10\x0c\x07\x97zL1q\xd0U\xd0\xc3`\x0c\x0d\xc7\xd5:>\xc7\x8duj\xd6\xd7\x9f\xf9\"\xaf\x1b\xdaP\xd8w\x94\xcd{+\xebvhE!E=+\xeb\xd52\xcd\xcb3\xf5:~~Ei\x83\x9f\x0fy\xf4\xabA\xd9\x0e\xc2\x12#\x1e\x0c\xbd\xa4\xac\x07C\x1ej\xbb\x82\x12[\x93\x00\xeeN\x93\x94\xe1\x1c&+\xd9)9!h\x15O\xba\xf5\x1a\xda\x11J\xfc\xff\xe7xz\x0c\xbb\xff\xbf\xfd\x18O\x85z\xe2\x1e\xcc\xfb\x86$\xfa\xdfM\xe1\xe0H\xb5D\xffM8L^\xd7\xe1\x96\xd8]k\x86V3\xed* ;\x97\x9b[\x07\xf2\x0d\x02:\x17\xd0\xb9\x0d\xfa\xaav\xc2\x01\x9d\xdb\xa2\x10\xc6\xd4\x7f\x16\xd0\xb9?::G\xf3\xa8b\xfeF\xf8\xef\x10\xe2X\xd3\xba\x1b1e77\x06\xbd\xfa\xd0!K\xb6\xa5\x0e\x0f\xbe.\xc4.\\\xafk5\x19]\xcdq\xc0\xf2Zt-F0`y\x01\xcb\x0bX\x1e\\\xe3\x0ef\x18,\xaf\x19\x9a\xd0`xW\x17>\xaa\xdd\x96o#v\xe4\x11\xdb\\CRk\x19\x01\x9b#\xd6\x02\xa1\x12\n\x13\x92q\x07\xad\x8e\xd0\xfeQ\xe2\x9b\x06\xb5fRh\xa4\x84\";\x9fs\xe8\xe6\x07\x0f\xeb K\x1f\x15g\xb4\xa0\xe3e1\xd1\xa8~K7l\x96\xac\xe6\x93\xb7\xb5,&\x87_\xa2\xdf\xe2\x02/?\x1f\x9c\x17G_fg\xb3\xb3\x87O\xf1\x14\x1dd\x9f/\xbed1\xca>?Z<\x8c\xbe[\xa2\x07\xc5C\xb4\xfc\xf2pv\x94?\x9d\xd1\xe5\xe7\xd9\xe3\xd9\xd3\x88>8{\x1a\x15\xd3\xadv~CI\x8a\x95A'\xf3\x8e\x982\xc4\n\xc3\xd8\xe9\xcez0r\x86\xd5\xb7xZv>\xd5\xee\xb5\xe3\xadr\xcd)\xa3-\xdb\x19}\\\x90,9\xd3?\x8en5\x19I\x8c3\x960\xed\x8b\xf7\xd6\n.\xf0\x84&\xba\xd8\x86Cy\x8a\xa3\"O\xd8j\x1c\x91\x8c\xa1\xa8;n\x19c\x86\x92\xd4\xe2\x9ek\xcasEi=Dd_M*\xa3.\xaaN2\x96\xa31\xbb\x1c\x0b\x17B=\\\xe6\xd9\xdbh\xe5`\xeb\xc7u\xca\xca\xd5\xf4a]\xbf\xf9\xb0\x94C\xed\x87O\xbf;\xb8\x7fpx\xff\xe0\xf0\xf4\xe0\xe0X\xfc\xff\x7fl7\x18\x91\xc5\"\xa1\xf4j\x96L\xae\x0d\xc5Y\xfb\x016IqZ\xa0\xcb\xf1u\xb4\x11\xcdQ6\xc3W\xdeT\xb1\x8c\x11\xc3\xfe)\x01mr\x9d\x01\x1d\xbd\xd5u\xf9\xebpHC6X\xbb7\xee\xd9`\x1b\xd9\x00\xc3\xf6\xa6\x83_\xe5\xefu7\xf2U\xfe\x10.\xb7W\xa4\xd0\xeeA;\xbbB\xae\xdes\xff1^\x93\xdds6\xb2o\xf6\x9a\xaf\xdeg\xd6y\xcc&\x7fY\xe7-\x9b\xbc\x0d\x9d\xa7l\x10\x8e\xddK6\x166x\xc8Fco6\xf5F\xdf\xd8b\xb3\xcc~\xb1\xa5\xb0\xd1'\xb6\x94u\xf3\x87-\x95\x18}aCY\xa3\x1f\xec\xb66\xb6\xdd\x08G\x0f\xd8\xec\xffj\xbd_\xbb\xef\xdb\x95o\x9b\xd7\xebX\xaf\x8b\xc7k\xf2w{,\x00\xbdkh\xf5\xd8,n\xa1\xd9\xc7\x1d\xa2v\xabw\xdb\xb7\x11\xab_\xeb\xd1\xc0\x95\xf8\xb4W\x9a\x1c\xbbvfe\x1d\x1e\x8eP\xc3\xfaUY\x96k6#\x94\xc5\xfc\x9f\x98\x8e\xe0\xf9\nb\x89Y\xde\x13Y>\x1c\xfb\xc7>\x85k\xf7\x0d\x9d\x8d\xbc\xab_\xd8\x7f\x84\xd7d\xf7 \x8d\xec\x87\x80\xacV8v\xff\xcfX8\x04dkr\xf3\xf4,\x95\x84\x80l\x83\xba\xf2m\xf3\xe7\x1c\xebu\xf1\xe5B@\xb6EN~[\xdfF\xac\x1e\x9bG\x03C\x04d\x87\x8e\xbd\xbax{\xfd\xde\xe0\xfdv?\xd6\xb5\x86\x8e!(\xeb\xb5\xe0\x9d\xf7\xd2\x7f\xf4\xf7xoDu\x0d\xf4\xc4\xc1\xb7\xa7\xc4\x8c\xcf\x1d\x04u\xd6\xa2\xdb\xa0\xce\x1cn|\xea\xa7\xd74\xe5\x1cnx2\xfb\xecW\xf5\xdc\xc1\xd0\xea\xa6\xd66KB\xd2\xe3.\xcb\xaa\xf9\xc0\x11\xdfq\xe3\xea\xa1\x82\xb2f\xe05\x7f-+\xca+\"\x94\x12B\xf1\xb8C\xc4@\x02\xd1]J&\xd94\x95\x133E\x94\xf9o\xce\xea\xf2^\xa5\xc4fA4\xb8\xde\xac\x8dsL\xb1\xdf\x8es\x99\xe3\xf3q\xd9w\xafPI\xdf\xa9\xbda\x11\xfbMp9\xa7\xab*\xe5\xb9I\xfa\x87\x9c\xdf\xeb\xc9&.\x9c\x90\xbbR\xc7!\xdb\xaca\x81.\xbb\x96L\xfc&\xeb\x8cp{!f\x99W\xb9\x1e!\x0f\xbeg\xdf\xcc\xf2\xd9.\xaa6\x15\"B\xa4q\x92\x07]\x0c)\xa2s\xbe\x1ah2\xcbx/\x93lJ\xba-\x08^\x83\x80\x01\xb8\xa6\x9f%\xe78\xdb8!yG\xc1\xd8VAe\x11\x8f%\xb2\xb5\xd6\xca\x0eV5yf4\x992~\xde\xad\xb3}\x1a\x9f\xe8FU\xe3\x06\x0f\x9d\x05\xf4\xd6\x92\x014\x10\xa7\x8f\xeeT\xdf^\x8dF\xbaZ\x1f\x962\x943\xab\x13\xa7u>c|9&\xd3\xa9\xd2\xd0Y\nK\xc0b\\d,I\xbd\x0bs\x1b\x8b\xe3\xf1$%\xd1\x19\xb5\xa7\x04lj\x08OOrYL\xd2$\x823\xbc\x127\x15\x91\xacv\xe1\xb6\xd6gW\xf5\xb3\xbb\xd6?\xc6m-\x97Z\xb5\x8f\xf5\xb8\xe1\xef\xa3(\x08\xa8\x94\xfa\xd6N\xae\xc5\xe3\x07\x9c\xc5\xad\xcb\x98\x18\x81\xc2R\xc1\xd5\\V\xd3I\x85i&\x8ej\xd7\xae\xdf,\x98#\x00\x06\xcd\xb0\x15I\x18f+\x0f\xa2k\xad>\xec\xeenq+\x87\xf9y\xfbbF\xc5]\x8dZ\xee\x07r\x8c\xae\xe4\xd2E\xe5\xd5@`\xd0\x93\x159\\\x95\xa3\x8d~)\xaf\n\x02[G\xc0\xda\x190]\x1b\x04v\xbe\xc0\x1a\xd6\xb3\xcb\x05l\xb2\x01\x97~J\xb2\xf7V\x92\xf5J!p\xeb{E\x0e\xd1\xcf\x8al\xe2\x92\xd4\xa5m\xcd]{\x9d\xef\xd1t\xba\x1c\xa8\xd7\xd4\xb3\xb4\x00v\x1e%\xd9\xb2`%\xdd\xc0UD\x9c\x8c\xd7\x11\x81\x8b \xc1I\x98PUe\x9c/N\xf2\x84\xa6L\xbd\xaf(\x02\xfb5E\xd0\x89\x93\x9e\xd7\x15\x81\xe3\x95E\xe0\xca\x9c5\xe3\xcev}\x11\xf4h\xc9\xdb\xd1\x0f7\xe8Y\x97Q\xb8A\xcf\xbc\x16\x1d\xcd\x9c\xdd\xc4\xf9\xb6\x17n\xd0\xbbV\xb3\x15n\xd0sm\xbd\xa7Ir1GV\x86,f\xe8\xe6n\xd0\xdb\xda\xd7\x02\xc9\xebM\xd9\xba\xaeN\x91\x8au\xa0bH\xd8\xa0\xac\xb3\xb1\xa5/\x8b\xd8B\x00\xee\xc1\x04o\xbb\xed\x18\xa0\xf3\xb3\xce\xe8r\x8c\xcf\x93\x98\xcf\x8b1\xf2D\x0b\xb8\xca[G\xbd.\x92,&\x17^\x15,\x92l\\V\xb2\xc4y\x97\x1abRLR,*\x19K@`\x1c\x17y\x07\xc4\x8c\\d,Y\xe0^\x95\x88Y3\x9e\xe62L5n\xf0\xd6\xaf\x1a\xc9\x9ac\x1d\x1dW\xd1\x8c\x9c\xef\xf3iB(\xaarq}\x02z\xf5\x93\x1dU\x1dw\x14\x8d\xab\x02yTS\xf0z\x02x\xdf\x93s.\x8e,\xaa:\xa4\x0e\xe1\xb5z\xb1a\x9fk\x0dW\x88(\xec\xa7\x9d\xaa#c><;\x9f B\x19L0|\xdaa\xf8\x92\xed|\xdak\x95\xfe\xb4S\xb7X\xe2u;\x9f\xf6\xe0\xd3\x0e%Sv\x81r<.\x96\xb3\x1c\xc5x\xe7S\xa3X \x80\x10\xca\xc6uc\x93?f\x90-\xbclb\xeb]x\xd9d \xaf&\xbcl\xb2EaK\xaa\xff\xcc\x16m\xfd\xc6_6a K]]\x16I\xcd\xde\xf8\x94k\xd9[\xaf\x92\xad\xcd\xe3\xa5\n\xa6&T\x7f\xfaGSekP\xba\x9d\x1b\xec\xf6$K\x95z\x19\xe3%\xa1\x896\xd3\xabS\xd0\xed\x9b\xcb&\xf7\xde\x9c\x85\xa0\xaau6t\xb5\xa5!\xa8\xda\xa2k\xb1`!\xa8\x1a\x82\xaa!\xa8\n\xd7\xb8\xfd\xf0\x0e\xaaV~\xcf\x10\xaf\x8ex\x05K\xe5\xd5\x12u\xb0\xe8\x8e\xa2\x9d\x8dOZwP\x08\xdfq+\xaez\xf5A\x1c}\xda\xe69a\xad4\xcbVW\xc4\x8f\n\xcfM\xf72\xb3f\xd0M\xed\x97\x1e\x1b\xd1\xf2P\x7fp\xb5|l]\x12h\xda`\xd4\xd3O\x96\xdak\x06\xc2\xe8:\xe8U\xb2>^\xe2\x07mR\x82\x0b\xdd\xae8c\x84\x996\xd3\xb6\xad\x87a\xe31\x80\x8bg\xd9r8\x88\xd0q\xbba\xdbl\xf8\xb5\xa4\x0c\x95\x95\x16@f\xfe{\x0d\x95\x9b\x9f\xf4Y\xfa\"\x9b\x0eGWW\xa9\x8d\x94I\xd0\xb9u\xd4\xca\x076\xfb~\x8d\x14\xacO\xa8\xc9\xca\xd6\x06v\x1bC\xd88\xa8\xd3\xa9\x8e\xaf\x06`\x93\xd7\xf7\xea:\xc6\x9d_\xf9\xda\x84\xb8\x83(\xc9\xa88\xa8T\x17hU%\x0bog\xd5\x07(\xcdq\x97\x12\xa04E\x0d\x01J\x93\x14\xa0\xb4\x10\x88l\x93o{\x01J\x1b\x06Jk\x9d\xf1z\xc7M!\xbc\x10\xa6o\xe3\xbb\xae\x90[\xab\xfe\x8f\xe2n2X\xa0K\xd5\xf1CI_\x1b\x96\x160\xb4&\xf9\xb4\xa1X\xc2\xa5\xd7u;\x84I\x8b ]\"\xfd9\x0c\x9f\xbe\xd2\xd6m\x11m2\x1c\xf4\xf1i\xe2-\xba\xfcU\xb7\xa8$Q\xed\x8b\x01\xe0\xd9\xd6\xaez\xcfn\x8c\xe8+F\xc3;\xe0\xc6\x1d\xfc\xa6\x1a\x0d8j\xc0Q\x95\xe40\x99\xc1M\xdf\x81U\xe7A\x87\xf6\x02\x8e\x1ap\xd46\x05\x1c\xb5\xa6\xeb\xda{\xde(\x8e\xba\xbb\x11\x1d\xfc\xbd\xfa\xe7\x9b\xb8z\xad\xc0\x1bh5\xe7\xd4o~\x03\x93\x15$\x95\xa5\xbc\xfa\xc0\x9df`\xca\xb0Z\xdd\xf9\xc6O\x1do\xaa\xd8=\xea\x9c\x9a\xe5\x08\xe8y9\x0eF|N\xd6\xa7\xb9\xaf\xde{3\xd7u\x93f\xc1\xe4\\J\x9a/\xe4W;;\x0eX\\\x0fCf@\xe1|t\x84\x05\xa82\xe2o\x03\xb6\xa3G\xde\x06l\xc4\x8e\xb9\x0d\xd4\x98\x11m3\xb4aA\xdaz\xb8\xe3\x9dw\x89_\xfb\x96\xdb\x01SS\xb6\xe0i1\x93\xb5b\x1f\xde^\xeeWA\xa3\xae\x86\xb3*\x7fG\xc1\x90\xfcb*^*\xc7\xf5\x97eLo\xc3\xde\x06+z\x83V\xd4`\xa0\x9c\xc3\x89\x9e\xb3\xfa\xcd\xcb\xab\x9c\xd5\xa5\x9e\xa3]guU^?\xab\xab/\xb8+\xb85y\xc2d\xf6\x98\xccW\x9b\xe3\xa5\xb7\x00&\x93\x17RK\x9ad\x86\x8e\x8c\xfa\xc5\xd2B\x9ds\xe9]\xb2\x1bn\xd1\x1b\xb9\xf0\xd4r\xbdm\xb7_\xa6\xcbK)\xce\xf2\xbd.`\xa4\xc3Y\xf1\xd8^\xc7\xad\xcaj\xd1\xcc\x92V?U\xe3q\xb5\xca\xd2\xe5\xe2F\x91\x10S\xa5\xee\x86|\x98M\xda\\\xa9!\x1f\xa6I!\x1fF\xd2\xba\xf6\x90\x0f\xb3E\x01P\xd2\x7ffvj\xbe\xf9|\x18\x83gf\xf1\xe8\xfc\xbd\xb2\x9e>\x99$\xdd\xc40\xad\x91\x10\xd9\xc2\xfd\x0ed;n\xf1\xbc\xfc\x90\x90.\xd0*\x1a\xb4{\x8b|\xdb\x0b\xe9\x02!]\xa0M\xb5\xdcB\xba\xc0u\xb9\xe6\xdd\xd3\x05\x92\x98o\xd0\xaa\x98D;y\xe0\xe1\xc1\xa1\xbe\xa6\x1f\xf1\n\x96\x88\xd2\x0b\x92\xc7\x90P\xb8\xc8I3X\xdd)\x18\xe3\x14r\xde\xff\xbdv\x9d:g#\x945\xdcQ\xf0\xd6\xfa\xa0\x1d|\x06\x94\xc5\xdaS\xcc\xdfr \xc6\x85\xb3\xf2q\x10\xd3!p\xfd9\xf2\x8e\\\x96\xee\xae\xedE\x90\x83\xc7\xcb\xcb\xcb\xf3;US\xb7\xc0E\x0b\xbev\x83|\xdaPx#]\xe1\xb9\xafh\x83\xd6G\xe7\xab\xde\xf4|x\xf0P_\xddkRd1ddC\x89^\x89\xce?'\x0cw\xc6\x18\xc5\x9d\x17\x06\x84Q\xfe\xde\xba\xd5\xe3Va\x8d7\xac\xd8\xafJ\x11^-\x1c)\x06U\xaf\x055k\xb6\x0f\xc4Fd\xf7]\x8by\xaf\xd5u\xc9N\xeb\xcb\x0f\xe0\xfa\x95\xc8#\xd8\x9e\x98\xd6\xb9\xb2X\x80\xb1\xac\x8b\xccvl|\xeb^f9\xdbv>\xc14\xc1i\xbc\xbe\xa1f\x85\xa9\xbck&#\xdb\xd7\xd34\x93\xe7v> 7\xf6\xd3N\x99\x1e\xa8\xbb\x93\x99\x0fi\x00\xcd\xb6(\x80f\x014\xf3\xa9=\x80f[\x14\xc2\xaa\xfa\xcf\x02hf\x04\xcd4\xde\xddm\xdb\x8fI\xd29\x86Fn\xd7\x13`\x85\xe9U\xbb\xe2^\x968\xc0F\xad\xa2A\xbf\xb5\xc8\xb7\xbd\x00\x1b\x05\xd8\xa8M\x016\xaa\xe9\xba\x9c\xd3\xbe\xb0\x91\xd8\xf6\xdff\xccH\xc4\x0f\xf7\x7f\x17nCg\xb0\x88\x97\xbe\xa3\xe0h\xfd\xab.\x86\x08%f\xa4\xba}\xf7[\x8ex\xb8pV=&\xaf\xb9\xb8X}\xe7qG\xee\xbeZ\x9c\xc8\xdf\x1f\xee\x8a\x86x9\xb3\x9d\x95Ig,\xa2\xb1F\xafD\x91\x88\xa3\xc0\xfe\n\xe4{\xdc|\x14n\x97\x82\xa8\x07\xe4\x91b@\xed\xe7\x1aY\xb2P\xea\x99\x86\x1f\xf1=f\xd4\xaf\xc2\x11\xbc\x996N\x07ri\xaf\x85\xbb\xc4Y\x9cd\xb3\xf5Y\xab\xbb\xc9\x08\x97Wa\xc3ny\xd8\xe1\x9d\xb8\xd4z\xf7\x1e$\x0cr\xcc\x8a<\xa3\x802\xc0\x8b%[\xc9\xe6\xeb\xfa$\x1b\xa3\xf2\xbf\xbfU\x0dw+4\x83\xe6\x88\xb9\xe3\xde\xd3t@Z{\xac|\x80\xba\xd5G\xc9\x07\xa8\xd8||\xbcW\x03\xde\xcan]\xb2\x93\x9e\x92j\xaa^\x0f\xfb\xad#\xe7\x1e\xbaI\xba/\xb3z\xa5\xd59/[\x97\x0c\xeb\xb4\x91C\x0d#q/\xd6\x02]\x8e\xdbW\xe8C\x91q}\x83r\xdc\x0c\x0de(#\x14G$\x8b\xe9U(\x91[\xb10\x17I\x16n h\x91O\x1b\xaa\xb0\xc1\xd6\xe4\xea\xbc\xc0\x9f<~x\xe0\xbf\xc4\xff\x83\xb09\xce\xc7\xdc4\xfc'\xdf\xd5d\x84\x1b\xfd\xe6\xb5\xdd\xdc\xa4`\xca\xda\xd6\xc3\xd1\xb3\xd1\xae\xca\xa1\xf4\x870\xe1I6\xeb\xaf@\xa4/bV\x1f\xae\x85n\xf7\xba\xd7\xaf)6\xcf1\x9d\x93\xb4\xfb$<\x18=\xd2\x9a\xb1\x9e\xe6\xeb\xc1\x83\x87\x9a\x8a\xd7\x032^\xe2\x0c\xa5L\x11*rn\xe6\xe0\xf0v-!\xcd\x14\x1bj\x01\xc9kJ\xfa/\x1fYO\x0f\xf3\xbbU\x81\xb4\xbe\xad\xc7i\xfe \x86\xd7\xb8%^\xf7\xb6\xf3$\xbe\x8d\x96@7?\xba\xce\xe3\xdd\xfd8\xe1\x82\x98\x14b@c\x9c\xe2\x99\xb8\xa1u\xff\xf7\xfa\xdf'q\x9c\xff{?\xc7\x17(\x8f\xab\xfd\xc4\xf6^\xec\xfe\xf6\xde\xa8\xca\xe5m\xd4sG\xc9]\x19\xe79\x89\xa2\x93*\xf7a\n/\xabru\x19\xcd\xaeL9\x9c\x8a\xc8\xcew\x17O\x1f\xb3\xf8|\x81\xbe\xa0\xb3\xf8\xe2\xec\xe2\xa8x\xf4\xdd\xd1\xd1w8.\x8a\x14\x1dE\xabG\xdf\x1d\xa5S\xdf\x15\xccw\xf7|k-\xae~\x82RH0A\xa9X\x8d\xd3\x9c,\xc4+\x0e\xa5\x10\x12\x92\xd9VtU!-\x16\\\n\xbc0\xff\xcf\xaaf\x8c\xf2\x0c\xc70Y5\xab\xe4\xff\x89\x80&\xd9,\xc5\x0d \xb7\xe5\xd7c1\xbfl\xcc\x91+[\xce\x1d\xfc\xe8R(\xba\x15~\xbd>t}e\xf8\xb8\x8c_\xa9?s\xf2u[\x92s\xc4\xab%m\xcc\xfas\x94r\xae\xada\xcd\xa3\x8b\x87\xd9Q\xaa\xacS\n\xd9\xdc\x19=\x10j\x81B\xadR\x97d\x93\xbd$+ \xea${I\x0e;\x1aIvX\xb4K\xab\x8a=\x8e\xd00\xb7c\xaa\x7f\xbd\xdbE\xb7@M\xad:\xb7C\xd1\x9d\xac\xac_\xfe\xed\xdf\x136\x8fstQ\xeb\xfe\x9a\x9d]\xda\xd0\xfa\x95\xf6\xbb\xa3`\xa4[\x15\x0ej\xffj\xd2y\xf5\xcf\\nd\x9aJ\x8f\xa2\xee]\xe5Im|\xe5\xe7,\x86\xec\xd2\x90]\xbaA_\x15\x80\x1f\xb2K\xb7(d_\xe9?3\xb9\x18\xf0\xedf\x97\xde\x8a\xfdLH\xafl\x15\x0d\x0b\xbcE\xbe\xed\x85\xf4\xca\x90^\xd9\xa6Zn!\xbd\xf2\xba\xbc\xb3!\xf7\x9e7\x90W\xe9\x19\x1d\xde\xff\xbd\x8e\x82\x89\xbf\x7f\xc5\xd1b\x9f<@moZ\xd20\xf6\xe6\x97%\xf7I\xe4\x87e\x97\xea\xc2\xc3t\xa9C(\xd0#\x0e^\xbd\xde\xb2\x15\\\xb8\xa3\xe8s\xf5\xb1\x8cXo\x97\x91\xf1\xec\xcd\x00\xb6Cx\xc2#\xe6pU\x0e\xdf\xd5\x1em\xd7:%\x16\x95dqBl\x17x\xdb\xeb\xfd\x8a\xa3lnsv\xfb{u0\xad\xc4|\xaa\xb9\xbd\xb9\x88\x1d&q\x88\xb1U\x14bl\xaa\x16\xdd{\x17bl\x03yq!\xc6\xb6Ea\x0b\xae\xff,\xc4\xd8B\x8cmPS\xd7U\xd5\x84\x18[\x8b\xaee\x81\x87\x18[\x88\xb1\x85\x18\x1b\\\xa3w\xd6\x7f\xe7)\xefB\xacwr7~\x9c\xd95\xecvQ\xee\xd2\xaa\x94\xb3\xaf8\xdev\xa5\xd9\x99U\xf6d%/\x94n\xc4\x1cl\xd9\x98\x8dd\xcb]Cm#8\x9d'\x94\xcf\x03^\xa8\x9a\\\x0d\xb7\xe9b\x9eDs\xf1cAq\x0e\x17I\x9aB\x8e#\x9c\x9c\xe3\x06\xa30-2\x9f\xac\x1c\x8f0\xc0U\xf9`\x8a\x85\xea\x9b\xc4\xb89%O\xcf\xd8\x8d(\x0e\xbc\xf8\xf6E\xd0\x0e\xbbO\x8f-\xe5U\xd9\xf3\x10F\x19f2mn\xf9=&\xd2V\xc8\xfd\xab\x9bJ\x96`\xc4R\xbc\xb6\x83\xf3\xb1\xee\x02c\xc3\xa8MHV\xd0^5\xac\x979C\x97\x8e%;\xce\x08n\x80\x92l\xd6g2\xbc\x95U\xc0\x82\xc4E\x8a\xedS\x81\x7f\x7f\xbb\xa7\x00\x17\xcaX\xa3\x08\x0c\xc3\x96d\xd3T\xf0=\x16{\x9bh\x8e\xb2\x99\xd6k\xb3\xd4\xb0p\x1e\xf8\xad\x92\x9eOk\xcd\x08J\x05d\x8f\xfd&\xe9$%\xd1\x19\x1d/q>^a\xe4\xfa\x88`\xcfiZw\xd3{\x96\xbe(\xdf\xb4+kZ\x0bL:V\xb7|\xae^\x85,Q\x96\x15(\xbd\xbf\xcc\xc9y\"b\xd1\xbde*k\x84u\x8d\xdf\x9cle\xc0x\x1f\x15l\xbe\x7f~8\xc1\x0c\x1d\xee\x97\xce\xf2\xb6xk9\x9e\x94_\xac\x9fG,o\xde\xc6\x97 \x95\x92-\xbf(K\xca,\xa1\x84do\xe2u\xe9\xae\x02:\x01ZD\x11\xa6tZ\xa4u\xe9\x91\x93\xd0\xbcB+m94\xc9\x14k\xb8\xaa\xf4\x15^|\\\xe4\x8aK\xf1\xd7\xbf[\xf6SnA4\x80\x13\xf8\xf8\xfe\xa7\xfd\x1cSR\xe4Q\x15\xeb\x9f#\x06E\x96|.p\xba\x82$\xc6\x19K\xa6 \x96g?y\xdb@\xa6\xda\n\xc5\x13\x1f8OP\x9a|\xc1\xf1\x1d\xedw\xcb\x9c0\x12\x91\x14&\xc5t\x8asX`J\xd1\x0c\x97\x07Me\xdf`QP\x06\x11\xc9\x18J2@\xbap\x0d@\x8a\x11e\xfa\xb6H\x86ag\x7f\x07\xa29\xcaQ\xc4p.\xdf\x0eJ\x11e@\xf1l\xc1\xf5\x04\x91/\x89~|\xff\xd3.\xdd| \xb3M\x82\xa9\x9c\xab\x1f\x8a3C\xab\xbc\xbai\x91\xa6+\xf8\\\xa0\x94K0\x96\xf2-\x9b\x12\x92\xbc\x8b\xc41Zm%\x9f8+\xfb3Bf)\x1e \x99M\x8a\xe9\xe8e!\x97\xd8\xa7{\xb2'\xa2Z:'E\x1a\xc3\x04\xeb\x83\x9d\x00\x08\"\x94\x91,\x89P\nS\x92/\xf4-\xdf\xc5\xa3\xd9h\x8f\x8bV$\xab\xec\x8cv\xeaW\x7f\xa2\x08/\x19\x8e\xef\x8d\xee\xe8\x8b\xbf\xc9`\xc9\x85\x9dDx\x0f\x18F\x0b\n\x05-\xe4cU9\x8e\xc8b\x99\xa4\x9cSF\x840&I\x86r\xddK\x1b \x95\xcej)\xe6\xa0|\x08v\xa5o\x1a_.q\xc4 a\xc0\x08\x14\x94\xb7R\x82\xf8\x19\xc3\x97b\xa8O\xb2\xd5\x08~ \x17\xf8\x1c\xe7{\\\x10\xda\xca>\xbe\xff\x89\x96\xa7\x9byUl\x8e\xf5\x0d\x0be\x84\xe1\xd3\x9c\xb1\xe5\xa7=\xf9\xbf\xf4\xd3\x1e\x90\x1c2R\xfe\xba'fc\x84\xb2\xf2\xd5\xe1\xd6\x1b\xb3[\x15b\x06\xc5\x12\x90\xe8\xbb\xa1]\xa9\xe9\x85h\x16hI\xe5\xd4\x12\x9c3R\xad,\x88\xf14\xc9\x12&l\x1f\xd2=\xb3\x020%iJ.\xe8\xb1al\xff\x0co\xa6\xeb\x1e\xf1i!\xccj\x8c\xe3\xba\xd3\xfc\x8f\x88\xd2b\x81\xe3\x91\xa9\xa2\x93\x0c~8=}\x07\xdf\xbf:\x05\x92UKP\xae\xb1U\x82\xd3\x18\x90\xb6\xf4?7\x97\xc5\xe9j\x89\xff\xf5\xcf\x7fi\x0b\x94\x81*>\x1f\xe4|\x03\x99\xcb+F\xa8\xb4\xf3\xe2\xf9_n2G&\xae\xd7\xf6_>7\x86\xb8\xccp\xcc\xc5\x1d\xa1\x88\xeb\x16B\xce\x8ae\xf9h0\x15\x1b\xb7\xd8\xa0\x9f\xc4\xbc\xd2\xfd,&\xa1\xe0q\x8e\xe4q\xfaEc\x0d\xc5r\x11\xa1\xaaK\xfc\xdf\xe7$\x89\x01e\xfa\x89\x05%\x83B}\xe4xJr\xbcWU\xc0\xebE,\x99$i\xc2V\x90a\x1c\x8bi4\xc1 T^~n\xe8\x89\xe8\x8b\xdc\\\x88Bb\xcd\x8e\xe0\xeeG\x8a\xe1\x1c\xe7\xdc\xf1\xe2R\xe2\xd3\x93\xeb,9?Q\x86f\xa6\xdeOr\x8c\xce\xb8\x0e*+\x1e\xdd\xd3\xcf\xa8\x9f \xc3\xc7\xc0\xb8\x0d\x99\x96\xb0\x0e\x12\xfd(uW\xf9|t\xba\x02t\x8e\x92\x14MR\xa3\xba\xe4\xf3\x91L\xa7I\x94\xa0\xd4b\xcb&\xc5\x14r\xcc-\x11\xde\x13\x99\x14 \xab\x1a\x15\x08\xd4\x94\xe4\xebu\xa9\xadj\x82gI\x96\xf1\xce^$ln0.\xab%\x1e\xc9\xf9\x8f\x96 \x1dEda\xd2\xc6\x1f\xc4J\xa5 \xdep\xe3\x8a\"\xdb\xd4Rp\x97\xf3\xc7\xfd:\xf1\xf6\xb5\\\xda\x9b\xe7\x0b\xd7\xb4Hfs\x06\x13\x83R\x12\x9d\x16\x88Z\xb2X\xa6\x98\x1bY\xb9\xa7\xa1K\x1c%\xd3$\x02\x8a\x17(cID\xd5K\xcd\x18Tvp\x81\xe4\xda>\x86\xc9\x8a\xe9f\x97\xab\x97\xf4\x96\xab\xa3 \xae\xdf\xbb[;8[~Li\xdc\xd1\x84\x9c\xeb\xe7t)\x82r)\xa8\xba\xef\xc2\xd9\xa7\x93l\xf5\xa9r\x8f\xc4\xbb\xe5(\x9f$,\xe7\x8bX\xcf\xa1\xb2\xaa\xcaF\xa0\x94\x94S\x0f\x90zh\xb9v\x16\x86Fr8i\xbb\x85\x1b\xee_\xed\xd5i\xa6\xe6\xbbj\xe1\xa4\xc9D\xb0]\xda\x11\n\xb4X.I.,\xf8\x12Eg\xfbE\xc6\xff\x87\xdbm9/\xd4+\xa84\xf4z\xc7\x86L\xa1`R\xb1U\xeaA\xe4\xba\xa08N\xa4\xae\xa8\xf1_\xce<\x9b\x13\x99\xd5\xa7S\xcf\x9c\x1f9\x84\xea\xf6^\xc9\x08)\x1c\x1e\xc3;\xce?\xd7\x0beWP-\xf4$\x83\x17\x7f\xf9\x8b\xc1L\xbe&\x04\xa6\x84\xc03\x18\x8dF\xff[\xfb\x19g\x06e+\xfd\x07([\x8d8\x1b\xafs\xb2\xb8;%\xe4\x9e\xfe\xd3\xd1Ho\xff\x92)\xdc\xe5U}\x14\x1d9%w\xff\xc4\xeb\xba\x07\xbf\x1bt\xb8\xa9\xbe\x7f\x9bewd\x91\xdd_\xd19\x1aLx\xf0L\xf8\x86\xbc\x95\x01$\x94\xd0\xbb\xaf \x19E)\xa2\xd4\" \xc9\"/$\xfb\xd8(\xa8\xe7A#\xb9Zt\x0f,\xa2{\xb7bs\x92\x19\x84'\xb9zM\xc8\xdd\xd1h\xa4\xb7\x06\xb5\xe0\xee\x1a\xbf\x11\x93O\x88\xb5\xabTy%o\xa4P_\xbe\xfa\xf0\xe2\xfd\x9bw\xa7\xbf\xbc\xbf\xa73\x12P6+'\xaa\xb9a\xd9\xb4Y\x9c\x0f-\xe2\xfc\x9e\xe8%)Dy\xfc\x0c\xfe\xb4\x9c\x8c^\x13\xf2\xfbh4\xfa\xb7\xfec\x94\xad\xf6\xb8\x1b\xcaK,\xa5\x13\xf5\x16\xe5t\x8eR.dsGL\"\xdc\xe4\xc2\xc0B2\xdd`\xe0c\xb6X\xb3 \x18\x14\x0bD|\xf5\xbf\x9eA\x96\xa4\xc6 n\xe6K3\x93\xf9\xe6V\xc8\xb9\xd2\xc5\xd5F\x03&\xab\xb5\xdbUY\x0fq\xeb\xd4D\xed\xf5\xc6x\x8a\x8aT\xf8b\xea\xa6v\x15.\xd5>\xdf\xbf\x8f\xc4\x0f\xdc]\xdd\x05\xd4\xb0v\xdc\x12\xf2\x99\xa0\xb3\x0dr\x86\xa8\x1b\xabMK\x96\xae\xaa}\xe5V\xb0\xa0v\x93\x01M\x99p\xdb\xd4\x0d\x898\xc6\xee\xfe\xae\xba\xa9\xd2&V,\x8b\xddn\x85\xdc\xc1\xce\x94\x90\xd1\x04\xe5\xa2\xb3\x97\xfb\xab\xd1\x97\x1d)E\xb1\xf7R\xd6\xa7\xdf\x8a\nVwx\x1d\xdc\x1c*?\xf9\xeb\x87_~V\xff\xf2\xec\xd9\xb3g\xfa9\xc0\xcb\xadc.\xeb\xd3\x97Y\xe9\x04\xc9}]AKo$\xc7\xb3\"E\xb9\xba\xbe\xedj\xe4\xddSk\xb7e\x0f\xf0b\x82\xe3x\xed\xc0\xecIw\\U\x1d\xd2Do\x1a.\xc5Tld?\xfd\x17\x17\xdd\xa72\x98P\xbbm\xcd\xc1Q/\x90R\xfd\x1c\x1b6 (:\xe3:h\xbd!\x9e&)\xd6\xdb\x8dJg\xbd\xc39%\x99q\xd9\x96\x91\xb8i\x92S6\x16#\xfc\x0c\x0e\xf55\xd7\x05\xf8\xa4\xac\xbe?\xf2\xb7`\x00F\xaev\x84,w\x8eaG\xb5j\xdbb\x18\xc9^\xee\xec\x99\xea\x13\xfd\xfb\x19-x\x9d\xff!\xbb\xf0\x9f\xc6\x02\xbc\x7f\x1b\xdf\xfbv\xf2\xcd\xb4\xdcp\xb5\xe7\x9a\x9c\x0d \x85\x0b\x9c\xa6\xf7\xcf2r!3\xf3\xe7\x88\x02\x82\xa8\xa0\x8c,<\x17W{\xca\xefI\x07~c\x1dH\xe5\xd9`\x87O`\xcd\xe6\n\xc9)\xadn\xec\x93X\x8c\xd5<\x9f\x934\x96\x93\\r.\x97r\x92\xd5\xeb\x03d\x04P]\x95\\2\xeav\x04\x0b\xa3\xda8\xdf\xe5z\xad\x12\xe1Vh\xa8\x8a\x98\xfe\xeb\x9f\xff\xbagXHC\xcc\xb9v\x83\xe6i'D\xc5\xab<\x1c\x1d\x1d\x1eQUJ\xbb\xa4mC\xcd\x12\x96\xe2\xe3\x1a\xb9\x10a(\x13.S\xd1\x12\xcd\x92\xac\x81S6\xa9\xb5\xeb\\\x7f(C\x87\xa5nm\xfc\xb94-*\\F\x92\x11\x071\xa3 \x19\xbed\xb6\x849\xc3\xf6\xdf\xba\xf9/\xc5\xf7\x7fu\x9b\xfe\xaa\xfd\xea\xceK\xfe\xcf2\n\x86(\x95\xa1\xbewh\x86\xdf\xcbC&#\xf9\xbb\xa6\xb2\xcf\xf5A2^-\x17!\x86\x05\xa1\x0c\xb0\x88-\x89\x80\x94\xa2\xa8\xe6ed\xf0\x11@\x91d\xec\xf1C\x93\x08\xb4q\x0f\xf9\xf4;\xef\xbf\xf8\x87L\xa8\xe3\xf6\xb2\x8aj6Bh:@\xa8)\"\x99\x97'*\xd3\xad\xc0\x0bD\x81b\xb6\x07 \xa3U\xb0\x96B\x91\xc9 \x18\xcb\xf8\xd5E\xb2\x919n\x0e\x96\x88S|\x15\x10\xf9\xbe\x9c\xab\xd5\xb0VsW*\xddi\x19\x03\x13E\xf6O\xd4\x0b\xe8\xfd\xbb\x17\xa5\xff\xb6\x9e\xf0\xa5w\xa9\xc713(2\x89K\xe0X\xc6\x97\xeb\xa6\x1be\x06B3E\xfd:(S\x93\xd5\x13k\xb3A\x92\x8c\xe1\x99\"nT\xcd\xb0$c\x0f\x8e6~-\xf5\xb0\x17\x0f1f(I\x03\x04\x1b \xd8\x00\xc1J\n\x10\xac\xa0\x00\xc1nS\x80`\x03\x04\xab\xa3\x00\xc1\x06\x08VP\x80`\x03\x04\x1b \xd8\x00\xc1J\n\x10l\x80`\x03\x04\x1b X\x1d\x05\x086@\xb0\x01\x82\x0d\x10l\x83\x86\x80\xc3\x02\x04+(@\xb0\x7f\x14\x08V}\x1d\x9d\xbc{n\x0d\x8e\x8e\xcep\xd3\n\xb66\x93\x1b\xa0c\x892\xa2R\x85\xca\x83v\"\xa8T\xc2g%\xbcZC\x92\"\x144\xdb\x88\x99\x08\x8c\x91/{3\xcc8\x82_\xb8\xc1#\x99\xd8+\x92\xe9\x94b\xc6\xb7_mv\xa1\x11\xca\xa6\x985\x95b\x92\x1d\xcb\xb6\x1a\x7f[\xdf \xbay\xad\x93&H\xa0\x0c\x0c(\x84(\xf9\xd3\xc9qcS^vF\x882+\x168O\xa2\xeaob\xb5E(\xab\xef\x83\xba\x98\xe3\xac\x12|\x91\xd5\x81\xa8\x0d\xf7\xf3\x8d\xa8-\xc5\x94\xaeE(C7\x05\xe5\xa2>\xc3\x9e\xf2lW\x7f\xc5\xc2\xdd\x80~\x15\xe2M\x93E\xe2*]\xf1m\x05\x9b\xea\x10a\x19\xa4l\xce\xe0\x12d-\xd2\x0d\xf0R\x86$\x9a\x7fz3\x85\x14OY\x19\xfdJ\x98T\x87\x95\xd3(\xe2\xabr\x81\xc8F\xb8\x9c'+\xc0(\x9a\x03Z.oP\x8aM\\{]\xde$\xcbF .Q1C\x898\xc6\x0c\xfc\x1fI\x16'\x11b\xb8FZJ \x8a\x0f\xcb\x89\xd4\xac.\xc9\xa2\xb4\x887\\B$[\xa9\xa1\xae\x8d\x11\x13\xc0i#\x02\xcbUw+\xb5\xa3U\xd9\xc77tc\xb46\xba \xbc\xe8\x1c\xd3\x12\xe1\x16\xcbk\xbd\x1e\xf9\x92\x1b\x95\xab)\x99e$\xdf\x88_W\xab\xb1\xdd\x84\x94L\xdf\x81\xdd\xbe\x8d\xaeV>\x1b\xbf(\x866\xc7\xe78oUj\x1a\xd6\xf2\xeb\xcd!\xad\x86S\"g{\xe6\xc5\xd2\xaa\x907\x86\xe5e\xe1$\x8fq~]\xb2\xd8<\x0e\xff\xb7\xb2\x9d]\xe3!\xf2\xfd\xdf\xcb;\x89\xaa\xd7H\x0c\xe7\xc9\xd7\xc7\xc9\xcb\xff.\xd1\x7f \xd3\x00\xc9\xea\x97g\xcb\nT\xc7\xca\xefT\xbd\xbe\xdd\xa7\xcau\x19\x0d\x9d\xf2\xa2\x9811\xc1\x1a\x93w j\x0f\x9e\x92\xe0\x96\x90\xd0%\x1d\xc1\x9cv\xd0)\xe9@4\xa1\xa9\xd0\x9ar0@\xc2A\xc7t\x03-H\xeb\x96l\xd0+\xd5\xa0S\xa2\x01\xa0t\xf3\x85\x96\x8a\x98[\x9aA\x97$\x03\x13\xf4\xe7\x94b0p\x82\x81Sz\xc1\x80\xc9\x05\xd6\xd4\x82\x81\x12\x0b\xfa\xa4\x15x'\x15\x0c\x90R0pB\x81%\x9d`\xf0d\x82\xabI%\x18<\x91\xc0=\x8d\xa0[\x12\x81A\xe8\xb6\x14\x82\xc1\x12\x08\xdc\xd2\x07\x14\xf1\x0b\xbd~\x1d8u\xc0\x968\xd03m\xc0\x904`uO\xac \x03n\xfe\xcb\xb0\xc9\x02\xb6T\x01;O\xdd\xd2\x04*\xcd\xae\xa8\xd0\x96$0`\x8a@\x8f\x04\x01uZ\x8f)=`\xd8\xe4\x00sj\xc0\x10\x89\x01N\xc8\xb6%)\xc09%@\x8f\xde\xf9\xa7\x03\xe8\xebRF\xca\x07I\x04\xf0\x11\x96k\x12\x80]&\xce \x00\x1d\xe0\x7f5\xaa0\x10\xf4\xef\x04\xfc\xdba\x7f\x17\xd0\xdf(E_\xc0\xdf\x15\xee\xd7\x81\xfd\x03@\xfd\x1e@\x7fw\x98\xdf\x00\xa6\xbbB\xfc\x03\x03\xfc\x06\x8e\x943\xb5\x13\xb4_Ed\x15\xf5i\x80\xfd\x81a}=\xa8\xdf\x15\xd2\x17\x11\x01\x15\xe3j@\x7fX8_\xb7\xf1\xb3B\xf9:\xacQ\x07\xe3\x0f\x0b\xe2w\x87\xf05p}'\xb0\xde\n\xcc\xfb\xc1\xf2\xce\xa0\xbc'$\xef\x03\xc8k\xe1x=7\xae\xb0\xa8\x1b\x14\xef \xc4{\xc0\xf0\xca\xae\x0d\x0b\xc1\xeb\x16E\x0f\xf8]\x19\xa7\xd0\x82\xef\xdd\xa0w\x13\xcc><\xc8\xde\x7f&9\x03\xec\xae\xf0z\xdbD\xba\x1f\xe8\xecp\x9es\xa3\xb6p\x9cSR8\xce\x19\x8es\xae)\x1c\xe7\x0c\xc79\xd7\xd4\x05i\xd1V\x16\x8esn\xd3@\xa8K?\xdc\xa5\x03\xf22\x08\xf628\xfab\xc5_\xae\x00\x81\xb9*\x0c\xe6\nP\x18\x1f\x1c\xa6+\x12c\xd4\xe16,f@4\xc6\x15\x8f\xf1Dd\x06\xc7d\xec\xa8Lo\\&\x1c\xe7\xb4r\xd6\x0d\xa7QV\x15\x8esvAll\x98\xcd0\xa8\x8d#\x14aEn<\xb0\x1b\xeb\xb1:O\xfc&\x1c\xe7\x0c\xc79]\x90\x1d\xabT}\xd1\x1dw|'\x1c\xe7\xdc\xa0\x81\xd1\x9ep\x9c\xb3I]\xb1\x1fee\xe18\xa7\x07\x12\xd4\x07\x0bRV\x17\x8es*\x0b8\xa1G\xe18\xe7pXR8\xce\xd9\x1bi\x1af\xce9\xa3M\xeex\x93\xdbq\xce\xcd7\xb67v\x91\xe5\xcf\xad\xebq\xab\xbf1R\xde\xfa:%[\xc7v6\x82\xe1\xebS;\x8d\x87,A\xbd%\xd7\x9d\xccQ\x1e\xcc\x11]3\xbc\xed\xf8N\xfc.\xf8L\xb0|\xd9q-\x0d\xf5\xc1\x9b\x82\xcde\xa9;\x15\xef\xb7\xf4\xecM\xb3\xefM\xda\xb8\xe2X\x08\xa0}\xbdq%\x80\xca\x8e\xc9\xb7\x83\xb7\x0d\x89\x96)\xb0\x02R\x0bt9^\xe0\x05\x19\xd7\xf8\x89\x01\xb7rJ\x98\xd5^\xf3\xcb.\xc74\x99\x8d\xc5\xe9\xcd+m\xe3\x0b\x1eG\x842\xf1\xe8\xeed\xc5\xfaf\x00k\x1b\xe3\xbd9\xc7y2]\xc9\xf6p|\xf4\xe8\xd1\xe1\xd3\xebj\x8e\xe2hy\xf4\xe8\xf1\xd9\xe1\xf0\x0d:\xa0\xder\xed\xf9\x80\xde\xe5\x1a\x7f\xff\xee\xc5F}\x01\xf3\x0e\x98\xb7-\xe0\xeb\x123\x85\x80y\x07\xcc[\xfbe\xc0\xbc\x05\x05\xcc{\x9b\x02\xe6\x1d0o\x1d\x05\xcc;`\xde\x82\x02\xe6\x1d0\xef\x80y\x07\xcc[R\xc0\xbc\x03\xe6\x1d0\xef\x80y\xeb(`\xde\x01\xf3\x0e\x98w\xc0\xbc\x1b4\x04\xfe\x180oA\x01\xf3\xfe\xa3`\xde\xd6\xfb\x1d'(;\xaba\xe4 JQ\x16a\xc7\xfb\x1d\xd3\xf4y\xf9}\x0d,\x8b\xd0\xac\xfc\xa3\xd0\xb7i\n\x11\xe1Z\x8b\x0b\x1b\x01M\xb2Y\x8a\xab\xdb\x12\xd5\xb8\xf3\xba\xd6\xf2\xe7[\x0bZ\xae\xbaPV2\xa4\xdb'\xff\xfc\xcb\xe9\xabca\xe9\xe5w\xa5\xc9LDT\xe3M\xc6JeRG\x92\x9a\x1aEY\xa1t\xb0\xd4\x8d\xd1d\x96!V\xe4\x98\xd6 \x1c\xdc\x9d\x9c\x91\x19\x11\xcbw\xdb\n\xb7\x04RM\x93\n\xc3\xad\xff\xbb\\ \x12jH\xb2\xad\xb0Vx\xbfYPx\xbf\xf9\xdbz\xbfym\x00\x9c\xb2\x1fTu\xec7m\xd3\xfbw/6;\x112\"BF\x84yQ^\x01\x980@\xfaa\xa2\xc9<\xacM\xca\xb0)\x88\xe1\xb5\x8b\xaeO \x84\xd7.\xaeP\xb8\xf6w\x1a\xc2k\x17CH1\xbcv\x11^\xbb\x10\xf4\xed\xbcva\x89\x86\xec\xff.\xb6\xaa\xa6\xa8H\xe9vj#\"U\x0c\x84\xef\xf8\xdc\xa3\"e\xadw*q\xdc\xee\x88\x88\xce\x07\xec\xb4\xbd4\xc45,~\x98)\xa6a,j\x8fg\x0c\x19\xcd\x188\x96\xa1\x8fd\xf8\xc51\x1c\xb6j\xe5\xact\xda\xa6\x95\x1b\xb3\xf6D\xae(\xdc\xcc&)\xec\xc9\xc2\x9e\xcc\xea\xc2U\xfc =\xa3\xe3N\xfcX\xf1\x16Im\xc5\xffr\xd5\xec\xd9N\xad\xb5\x0c\xac`\x89\x8e\x17\x98\xa1\x181\xa47\xaa\x0d\xf5\xf3R\x94y[\x16i\x99\xd9(\x15\xfb\x98\xaa:i]Sn\xf0f e\x98\xab;.\x89\xba\xaa\xa6\xa2V\x1f\x82k7V~qk\xado\xd5\xf1[\xb2\xde\x9b2\xe8\xbah\xc5\x10\x8d\x8b,a\x9aF\xcc]\x93\xa4\xed\xa0$K7%\xd9:+\xc9\x88\xc1H\xb2v\xb9\"\xbb\x13\xd2\xfe\x9a\xaf\xee\x1a\xa2\x95\x0b\xa2\x84\xd6\x9b\x87Hf\xc99\xd6\xa7/7\xeb\xe2B\x17\x87:\xa0@\x8c,\xee\x19\x92q\x00_.I\x86\xf5\xf8\x91$\xb3\xedkR\xc3\x0ej\"\xe2\x15\xf9I\xa9\xe2\xb3)\xa8%\xb9\x90;\xc7\xc3\x83\xf5\xefb\x83J2S\x969\x94G{\x0c)\\\x82r\x94\x94\xf9:\x13D\xf1\xb8V\xc3I&\xf7>\xfc\xdf\xf8s\x81R-\xaa\xbd&1zR+}\xcc\x12\xb6KK#`)vX\x0e\xe938\xfc?u\x17\xd7\xdc\xd8\x8a\x8b\x93=\x95w\xdb\xe8\x04\x99\xca\xa9\xb1>\xa2\x12\xe5\x98o\xef\xf5G0$\xd5\x1d\xe0U\xec\xf2*v-\xe9\xeb\x92j\xde\x9f\xc1\xe3=`\xf3\x82\x1e\xc3!\xf0\xf2\xbck\x07\xff\xe7\xb1\xc3TEi\x82\xa8y\x15\xbb\xe8\x14I\x16\xcd\"\xc9y\xc5\xdbP\x9e\x8a\xca.\xc8\x80`\x9aP!\xc6r\xadW\xbfU^\xb7\xf3z\xd7~c\x8a\xf0\xb6i=\xac\x8d\xe5\x858gE\xc46\x93\xc0\x91\x95\xb5\xd6>\xaa(gK\xb9\x8e\x92H\xee\xb9t#m\x17e\xc3\xb2l\xaa\xcdJ\xa4\xcdu&| mer]\xb6\x9c\x8b&\xf15\xd3\xd9\x04\xba\xeb8\xde\xccf_\xc4\xdf\xe4r\xbd\xbb\x0e}\xf3_\xea\xdei\xeb\x13+\xbe\xb1\xe6\x0e4\x0b+N\xe82E\x1a\xfc\xd7\xbf\x87\x869V\xb6\xd4\x8aj\x19S\x89\xb7\xc8\xd2\xd0\xbc\xd8IiJ\x1b\xb5\x94?\xdfZ[\xda\x14H\x93nbv\x19\xcd\x9bur\x84\x1b\x9c\x86\xbc\xc1\xa9\x0c\x12\x943>\xa9\"\x1a\xd5\xfc\xaf\xfc\xe5\xb6\xfd\xd5\x05\xb2\xc4\x92l,\x8a\xee\xfb\xcf\xe6\xfa\xd4\xee>\xeb?\x06\xfb\xd3\xfe-\xd8\x9fk\xb4?j\xccV.!\x07\xa8VN\xf2_\xa6-3\xd40@\xcds\xf4J#TUP\xfevk-\x90No\xf7\xd8\xcd\x19\x0c\x89e\x8c\xc3\x91\xf96\xf9\x19\x0e\x87\x1dG5+\x9dL@\xa9\xf47frE\xe1\xd0\xbc\xa4\xa0\xf1\xafY\xe3_]\xb8\xf1\xc6\x0e\xa5S\xbc\xcfp\x16\xe3|\x91dl}\x01LJ\xa23\xba\x9f\"\x86)\xd3\x9b\xaa\xef1\xfbI|\xf2\x9c\x7f_&V\x95\xf8\xa4\xf8;\x88\x8a\x94V\xaa]\xf6N\xd5\xb7[j\xabD?\xc6\x89\"\xfc\xd3\xc3Z\xcd\x11\x9dw\xb08`?\x01\xb2D9\x1bS\xcc\xc6s\x8cb\xac\xd0D`\xe3\x1c\xac\xdcs2\x9c\xe1\x00\xab\xfa\xaa\xc8%=F/*p\x11\x17\xd8EVob\xde\xa1\x9cQ\xcc~\x10\x92\xdb\x1en\xf9\x91\x98\xb4o^\xaaf\xc9\xb0S\xe4j\xc7\xaf|i\xcc Y\xd9] @\x8a\xee\x89\xd3Z\x96\x91\x18\xe4J\x01\x8d0\x9b\xe44\xee\xe0v^H\x12Z.\xaf\xb7I\xbb\xfbX\xd1\x0b\xae\xda2ZP\x88\xd0Rzf\xd2\x9aT\x7f\xce\x8b\xb4L\xa4Y\xe6\x84\xebD3\x8b\xa8\x1eO\x19M\xe6\xff\x11\xcdQ\x92\xed\x99\x12\x99d\xe6\xb3\xcc\xdcI\x1b\x85@\x00e\x12\xd2\x16\xbc\x95\x0f~H\xae\x0c\x15V\xcf\x80\xad\x1f\n\xdc5\xc4\xebAD\xff1\xb0\x1ceT\xde\x8a\xbf@\xd1<\xc9\x94o\x83q\x12\xdc)5wE\x0eC:\xc7\xc9ln\xc8\x05t\xa8\xc2M\xcf\xb1D\x7f\xe0\xd6\xb3\x99\x181|\x9f\xd7\xa7\xf9R\xbc;\xa1\xb7k\x15\x0d\xb8\xa2\xcdJ\x1c\\;\x08.\xca\\\x92\x93\x1d\xac\xc8\xa9\xa7\xe0\xdc[\xb0\xdb\xc7\xfa3';Y\x91\xcb<\xaa\xc8.r\xf0\x11;\xb8\x8b\xde\xd1\x9en|\xac\xb6\xab\x15\x89)\x1b\x91\xc5\"a\xe3\xeb\xf0\x08`\xcd\x18oN\xde\\/\xb5f#\xd7}\x93\xf8O\xd7\xc4\x9exd\x111\x92\xd3kjpS\x1e\xd3\\\xe4\xb7 \xfd\x0d\xa4`\xcb\x82\xad\xff\xb6\xcc\xf1\xb9\x14\x97\xa66q\xbe\xee\xda\xfbP[\xcckj\x0f-\x97\xd7\xd4\x92X\x1f\xe5a\x97kj\x12\x9f'1\xce\"|M\xcd\xd5\xf3o\xed\xf6\x18\xfcQ\xae\xa8 \xc5\xf9\xb8\xbc\xa3\xee\xaa\xf9k9sR\xe1\xb5\xa0\xf5\xda=\x92\x81\xdc\xd3z\xe3]*\x15i\xa5\x94\xa9\x08\xca#+\xe0b\xb5\xec\xd6\x8a]Z%cN)\xb7&\x94;\x89\xd7A\xc0\xe0\xe51\x9f^\xca\x03`\xf5\x8bM\xc2\xc9\x94\x81L\xe9F\xfeW\x19\xa0\xf8A\xf8w\x7f94<\xf0X\xc5^\xc5\xbb\xeei\xca\x85\x06s\x9cc\xf1\xaa\xb5\xd0a#\x80\xbf\xe3\xdd\x1c\xc3o\x05e\x80f9\xc6\xe6\xee\x96\xcfx\xcb\xf3\x13\xe2]-c\xfb\"gu\x81QV\xf6J\xb2~\xb2\\\xfe\x80\xe8\x1cb\x82\xe5\x1b\xca\xe5\xd9D^55\x19hv\xa9\xb9\xf4\xc5\x96\x95\xfe\x92{\xfa\xadW\xd9\xc4 \xdc\xa9t\xcaQ\xf9Vl\xc9\x86\xf2\x94\xdd\x9at\xd6\xa1R+W6\xe7\xcd\x0d\xc0\xb0\x13\xdf\xeaS\xda\xf9\x95\x14\x17r\x97\x84\xc7\xe7\x84\xe1\xb1\xbd\x13\x92\x1c\xb9\x00\x0fN8 \x1e4j\xa9M\x1e\x0c\x80'\x13PU\xef\xf4\xa5\xb3*j\x12\xce\n\xcb\xdd\x86M\xba\x0f\x1f\xde|\xff\xf3\xab\x97\xe3\xb7\x1f\xbe\x1f\x9f\xfe\xcf\xbbW\xe3\x8f?\xff\xf8\xf3/\x7f\xff\xb9G\x0d\xef\xde\xbf\xfa\xf5\x97\xd3W\xfdjx\xf1\xcb\xdb\xb7oN{\xd5\xf1\xcb\xbb_>\x9c\xfc\xe4XE\x05\xc9\xf4\x94\x87\xbb\xbeo\xd3\x87d\x96\xe1\xf8-\x9d\x9d\x96\xaf\x04\"1\xf6\xe2\xec\x91\xf8\xc9\xb9\xa6\xc6\xc3\xb5\xad\x88\x8bQ[o\x90vL\x8f\xe1W\xc2\x8c\x11\x92\x0d\xd2\x8f\xcb1\xbc\x13\x0e\x0fJ\xdd\xaa\xb3E5\xda\xd4a\xe1\xf8\xecT%\xe5\xa4\xc8\x8c \xaaM\xf2\xdb6K2afj\xb2\x87H\xda\xe4\xa9\xeb\xa0\x83\xbe\x03\xe7\x9d\xfd\x9a:\x0c\x1f\xb8\xbad\x9b\xe4\x15viS\x07\xe9AG rr\x0c\xd0\xb4\xa9\xcb\xbc\xab\xc8\x7fIT\xe4;\xe0\xd0}\xd0\xa1\xeb\xc0{\x06}\xda\xe4\x14\x02j\x13K\x16\x982\xb4\xb0\x04\xed\xd7\xd4A \xae\xf1\xd46\xd5Q\x0d\xfb\xde\xb3M=8t\x1e\xaa5sI\x16\xe3K?\xd6\xfc\xe6\xbd\xbf\xbe\xad3O\xfc\xd8\xbaJ\x89uqA\xb8Uo\x9f\xf7[\xe6\x98{\xcc{\xfc\x1f2\x88\xb9\x07\xc4M\x8e\xf2s\xe1p\xcb\xf0\xda:h\xc6\xfb\xe2\xe6\x874\xfc\x16\xdb\xa7\xc2\xb3\x9f\xb8\x0c\x80\xa7\xbe\xf6\xd5\xd3\xc1\xb3\xdf\xa0\xe0\xd9\x07\xcf\xdeN\xc1\xb3\xb7}\x0d\xc1\xb3\xf7\xb3\x81\x92\x82g\xaf%\xff%Q\x91\xef\x80C\xf7A\x87\xae\x03\x1f<\xfb\x8a\x82g/\xc9_\xdf\x06\xcf~\x9b\xae\xdb\xb3\x17jq|NX\x92\xcd\xc6\xe2\xc20\x97\xc1\xf0\x1c\x08?U\xb8\x9e\xb3\xb7\x83\x1f/\xd5\xd3\x91\x13W\x95\xe3;?_V\x00\x11\x9f\xa4\xafJxh\x0d\xd6U\x80\x91\xc0\x9f\xad\x95\xd5\x03S\xba\xd5\xc0.\x08\xafk\x9a&\x11\x9f?b\xd6Z\xe6\\\xca\x1d\xd1\xb1\xbc\xcfh\x8c\x18C\xd1\xd9M\xc3V\x8d\x1e\x8c\x1d\x92L%y\xf0\x02\x9e\xfc@\xa9\x1aq\xec\xe9Uy\xf2\x04\x1d\xf8\x02K\x0e\xb2\x9a:0\x06\x1d\x99\x03\x97,f5\xf9\xe66\xab\xa9c_\xa1G\x7f\xc1-;ZM\x9e\nk\x93*\x05f\xcd\xa4V\x935\xbfZM7\xc9\xb4\xaf\n\xde$\xb7\xbcm\xefj\xdby\xde\x1b\xd9\xdc\xde\xb59f\x7f\xab\xc95'\xdc\xbb\xe2V\x0e\xb9w\xa6\xb8\x9a|\xf3\xc7\xd5d\xcf*WS\xe7\x89\xec\x17\xd1\xa9\xa8ss\xbe>S\x93\xcc9\xecj\x1a\x80QW\x97\xaaM\x8eY\xf0j\xba!\xdd\xdf%^\x00\xfd\x84\x0c\xfe[\xab6\xf5\x08\x1cU\xd4C\xda\xd0S\xe2\xd05\xa0TQ\x97\x0d\xf6&u_\x91\x15u\x9d8\xd0\x7f\xf2@\xdf \xd4+\x00UQ\x87@TE\xeeg\x0f\xd4\xd4C~=\xe4\xe6\x7fzAM\x0eg\x1a\xd4t\x13\xddv>Q\xa0\xa6\x9b`\xd9\x9eu\xab'\xd7\xa3\x18\xde\x15\x9b\x8en\xa8\xc9\xef@\x87\x9anB\xfc\xaeGB\xd4t\x13\x1c\xdb\x0f\x95\xa8\xe9&x\xf58\x96\xa2\xa6\x9b`\xda\xf1`\x8b\x9an\x82a\xbf\xa31jr?0\xa3\xa6\xeb\xefw\x9f\xdd\xb9\xf5\x84\x8eWm\x1e\xa7y\xd4$\x9d\x0b\x1f\x91w\xf4\x88\xbbz\xc2_\xd1\x1e\xd4+\xdb\xa0\xa2>N\xba?\x1aVQ\xd8|\xbaPG\xf5 )l>;\xcf\xeb\x8a\xba/\xc5\x8a\xbaN\x1c\xe8?y\xa0\xef\x04\xba\xe9\xcd\xe7\xfa66_ J\xc9\xd9^\x04R\x91\xf5l\x9b\x9az-\x94~\xcb\xa4\xd2\xa4\xe3i\x8a\xb4\x0f\x80\x98\xa9\xf7D\xf3\xcb(m\xd3}x\xfe\xd3//~\x1c\xbfy9~\xfd\xd3\xc9\xf7\x9e\xd9\x94\x9b\xb4Y\xdb\xc9\xf3\x0f\xaf~vO\x12m\xd3fe\x9e\x19\xa7m\xda\xac\xec\xe77\xae\x89\xa7m\xaa\xd3P\x87\x13[\xf7]\xb8$\xb9\xb8\xe3\xd7)\x9a5\x1e\x05\x92\x972>O#r\xf6\xe6e'|FR\xad\x06 \x91\xd9!\xb6\x02\xdb\xd49{\xa9M\xbd\xd7Iou\xec\x91\xa3\xb1M\x83\xb1\xdf\x0dt\x90\xe4\x9d\x0e\xd5\xa6\xc1\xfa\xd0i\x08\xfa\xec\xbb$\xbd\x10\xdb\x9d\x0f\xc9Lfps\x1f\xad\x82\xf8D\x86Uu\x80\xbcC\xd5I\x06\xa8\xac\xdf}\xfb\xd5\xafO\xb2\xb5\xf6\xb1\xf8:\xdbF\x1c\xd7\xf7\xdbM\xca-\xe4\x05\xa2\xe5\xb6\x90\xc9k\x0bPy\xd6\xde\xab\xaeu@\xceM\x1ak\x1dA\xb1\xf3v\xaf\x83\xdd\xefb\xed\xd7}q/\xd3\xd5\x0b\xea\xe0\x01u\x90\x82\xa4.\xb2\x90\xd4Y\x8f\xf7R =\x94\xc7\xb2\x98\x8c\xcf\xb0\xe6a>\x13u\x16.\xf4\x120'\x1c\x1f=zt\xf8\xb4K\xd1\x9e\x82\x86~\xc2\x06\xf1\xc8M\xb4y\xa6Q\x15Qm\xa4\n\xab\x05oU\xf4v\xa5ns8\x9cF\xd3e6Y2id;\xb6\xdc\x02\x97\xec\x18{\x1e\x8cUl\xe0$:pJFp\x12 \xb8[\x0d\xaf\x1c\x14\xa7\x9e\x82so\x01\x9c3H\xdc\xc6\xb3\"\x97 T\x91]\xe4\xe0#vp\x17\xbdg\xf6\x86S\x9e\x86KF\x86\x8bWm\xf5\xa5\x1d\xa7\x82\xebD\xf0\xca\x91\xf0\x18\x0c\xb7\xbc\x87\xae\x19\x0e\x1ds\x19:f-\xf8\xe7'\xf4\xcaDp\xf7 \x87\xca.\xf0\xca#\xf0\xce\x18\xf0\x985\x1e\x0b\xd8\xd1\x93\xe9\xd0\xb8\x9b\x07\xe3\x8c\xd6w\xe0\xc0\xd2}\x1f\xd7\xc9\x15U\x17\x18\xb9CMj\xc4\xd8\x8d#\x07L\\\x83sk*D\xdbN\x9c\xcf;\xa4\xed'\x07\x9d\x1e\"\xdd\xa8A>K\xba\xf1\xecax\x8eT\x92\xc9\xc9\x0b\xcf\x91\xda&\xe7\x9aN\xe0\xe3\xfb\x9f\xf6sLI\x91GX\x1f\xe4|+\xcd\x88\x18\xa1eN\xe2\"\xc2\x802i\xc2\xf4\xe9k\x7f\x86\x93\xf5\xb5 T\xbc\xc9\x83\xb8\xccp\xcc\xc5\x1d\xa1\x88\xeb\x16B\xce\x8a%\x94\x07\x11a\x82\xa8!\xb9\x90\xd8\xeeQ\xf9\xf8\xfe'\xc1\xe3\x1c\x9d\x8b)\xb8h\xac\xa1X.\"Tu\x89\xff\xfb\x9c$1\xa0\xcc\x84\xafH\x06\x85\xfa\xc8\xf1\x94\xe4x\xaf\xaa\x80\xd7\x8bX2I\xd2\x84\xad \xc38\x16\xd3h\"\x0e\xf6\x8a\xa9fJ\x93$\x19W\xb3\xd9\x0c\x8bBb\xcd\x8e\xe0\xeeG\x8a\xab\x9b\x93\xb8\x94\xf8\xf4\xe4:K\xceO\x94\xa1\x99\xa9\xf7\x93\x1c\xa33\xae\x83\xca\x8aG\xf7\xf43\xeag\xc2\xf010nC\xa6E\x16\xc9\x15\xc6\xfbQ\xea\xae\xa8\xc8s\x9c\xb1t\xd5\x08~\x1b\xd4\xa5x2i:M\xa2\x04\xa5\x16[6)\xa6\x90cn\x89\xf0\x9e\xb8L&aU\xa3\x05\xc5\xb1t\xf2\xaau\xa9\xadj\x82gI\x96\xf1\xce^$ln0.\xab%\x1e\xc9\xf9\x8f\x96 \x1dEda\xd2\xc6\x1f\xc4J\xa5@\xd8\\*\x8alSK\xc1\xdd\xf25|\xbcX\xb2U\xb9\xb4\xef\xe9\x8d`2\x9b3\x98\x18\x94\x92\xe8\xb4@ \x92\xc52\xc5\xdc\xc8\x8a\x05\x03t\x89\xa3d\x9aD@\xf1\x02e,\x894\x99\xa2W\xf0\xa6\xfa&\xb9zIo\xb9:\x9a`@r3\xd0pp\xb6\xfc\x98\xea\xe2\xa0 9\xd7\xcf\xe9R\x04\xe5RP\xbe\xc1\xe6\xc0\xd9\xa7\x93l\xf5i\xbd\xe3A\x19\xa0|\x92\xb0\x9c/b=\x87\xca\xaa*\x1b\x81RRN=@\xea\xa1\xe5\xdaY\x18\x1a\xc9\xe1\xa4\xed\x16n\xb8\x7f\xb5W\xa7\x99\x9a\xef\xaa\x85\x93&\x13\xc1viG(\xd0b\xb9$\xb9\xb0\xe0K\x14\x9d\xed\x17\x19\xff\x1fn\xb7\xe5\xbcP\xaf\xa0\xd2\xd0\xeb\x1d\x1b2\x85\x82I\xc5V\xa9\x07\xca\x15+\x8a\xe3D\xea\n\x98\xe1\x0c\xe7\x88 \xe6\xf9>\xab\xba\x0bJY\x1f\xe7G\x0e\xa1\xba\xbdW\x97\x88O~8<\x86w\x9c\x7f\xae\x17\xca\xae\xa0\xe6\x85\xe0/\xfe\xf2\x17\x83\x99|M\x08L \x81g0\x1a\x8d\xfe\xb7\xf63\xce\x0c\xcaV\xfa\x0fP\xb6\x1aq6^\xe7dqwJ\xc8=\xfd\xa7\xa3\x91\xde\xfe%S\xb8\xcb\xab\xfa(:rJ\xee\xfe\x89\xd7u\x0f~7\xe8pS}\xff6\xcb\xee\xc8\"\xbb\xbf\xa2s4\x98\xf0\xe0\x99\xf0\x0dy+\x03H(\xa1w_\x132\x8aRD\xa9E@\x92E^H\xf6\xb1QP\xcf\x83Fr\xb5\xe8\x1eXD\xf7n\xc5\xe6$3\x08Or\xf5\x9a\x90\xbb\xa3\xd1Ho\x0dj\xc1\xdd5~#&\x9f\x10kW\xa9\xf2J\xdeH\xa1\xbe|\xf5\xe1\xc5\xfb7\xefN\x7fy\x7f\xcf\x14%[OTs\xc3\xb2i\xb38\x1fZ\xc4\xf9=1\\\x11\xc7Ey\xfc\x0c\xfe\xb4\x9c\x8c^\x13\xf2\xfbh4\xfa\xb7\xfec\x94\xad\xf6\xb8\x1b\xcaK,\xa5\x13\xf5\x16\xe5t\x8eR.dsGL\"\xdc\xe4\xc2\xc0B2\xdd`\xe0c\xb6X\xb3 \x18\x14\x0bD|\xf5\xbf\x9eA\x96\xa4\xc6 n\xe6K3\x93\xf9\xe6V\xc8\xb9\xd2\xc5\xd5F\x03&\xab\xb5\xdbUY\x0f\xf9\xf4\xa6\xda\xeb-\x83d\xdc-Q7\xb5\xabp\xa9\xf6\xf9\xfe}$~\xe0\xee\xea.\xa0\x86\xb5\xe3\x96\xb0\xbc4PY\xa1\x9c!\xea\xc6j\xd3\x92\xa5\xabj_\xb9\x15,\xa8\xddd@S\x86U!BI\"\x8e\xb1\xbb\xbf\xabn\xaa\xb4\x89\x15\xcbb\xb7\x0b\xb8\x9c\xd1;SBF\x13\x94\x8b\xce^\xee\xafF_v\xa4\x14\xc5\xdeKY\x9f~+*X\xdd\xe1ups\xa8\xfc\xe4\xaf\x1f~\xf9Y\xfd\xcb\xb3g\xcf\x9e\xe9\xe7\x00/\xb7\x8e\xb9H?\x92puP:Ar_WP\\\x85WgE\x8a4\x17ZoW\xc3\x8b\xc4x\xed\xb6\xec\x01^Lp\x1c\xaf\x1d\x98=\xe9\x8e\xab\xaaC\x9a\xe8M\xc3\xa5\x98\x8a\x8d\xec\xa7\xff\xe2\xa2\xfbT\x06\x13Z\x81\xeajp\xd4\x0b\xa4T?\xc7\x86\x0d\x08\x8a\xce\xb8\x0eZo\x88\xa7I\x8a\xf5v\xa3\xd2Y\xefpNIf\\\xb6e$N\xbc\x18;\x16#\xfc\x0c\x0e\xf55\xd7\x05D^B\xf9\xfd\x91\xbf\x05\x030r\xb5#d\xb9s\x0c;\xaaU\xdb\x16\xc3H\xf6rg\xcfT\x9f\xe8\xdf\xcfh\xc1\xeb\xfc\x0f\xd9\x85\xff4\x16\xe0\xfd\xdb\xf8\xde\xb7\x93o\xa6\xe5\x86\xab=\xd7\xe4lH(\\\xe04\xbd\x7f\x96\x91\x8bL\xe8\x999\xa2\x80 *(#\x0b\xcf\xc5\xd5\x9e\xf2{\xd2\x81\xdfX\x07\xd5\xbb\xc55;|\x02k6WHNiuc\x9f\xc4b\xac\xe6\xf9\x9c\xa4qy \xad\xe0\\.\xe5$\xab\xd7\x07\xc8\x08\xa0\xba*\xb9d\xd4\xed\x08\x16F\xb5q\xbe\xcb\xf5Z%\xc2\xad\xd0P\x151\xfd\xd7?\xffu\xcf\xb0\x90\x86\x98s\xed\x06\xcd\xd3N\x88\x8aWy8::<\xa2;\x86)$\xff\x97\xa1Y\x034\xb8\x0f\x1fp~\x9eD\\z\xbb\xfb\x11\xa1\x0bB\xf7'\x88\xe2}Vg\xe6\xed\x9f\x1fN0C\x87\xfb\x02\xfd\xa2\xfb\xbf\xcbl\x9e\x7f\xef\xcajf\xebS\x97\xb4X,P\xbe:\x86\xef\xb1D\x9b\x9e\xaf\xe4[\xd5\xf0\xb9\xc0y\x82i \xa0qA\xcf\x92s\x9c\x95\x89A\x95\xd6\"K,\xfb\xfc&\xde\xae\xa3\xfc\xa6\xc2\x9d\x1a\x9d\xd8=:8\xd8\xd5\x83W@\x8b(\xc2\x94N\x8b\xb4.\xdd\xd4\x93\x03\xc1V\xfa\x8c me`\x85r\xf4i(\xd6\xf8\x85%z\xe1\x94\xe1c\xe4\x1c\xac\xdc\x83=\x93\x879e\xf0\xb8d\xee\\\xc7\x93\xfeN\x999\xc6\x8c\x1c\xcdM\xe8}\xa6\xc8\xd5\x8e\x9f\xf5\xbaz\xdf\x8b\xe9\xad<\x81\x13_\xe0v\xad\xbc\xd3\xb8Cc\xec\xad\xb7\xae[/\x85\x1f\xbaI\x97H\x9e$\xb7\xcb\xdb\xa1}\x19\xbb\xa9\xab\xadk\xda]\xaf]w\xbd`\xbd}a\xba\xa1B\xdf\xab\xd4}/M\xb7_\x8f\xee0\xa4\xd7\x96\xe2j\xbe\xb0\xdc\xab\x19[.\x91\xe3u\xe3\x03\xaeh{\xda\xa5S\x07\xc1E\x99Kr\xb2\x83\x159\xf5\x14\x9c{\x0bv\xfbX\x7f\xe6d'+r\x99G\x15\xd9E\x0e>b\x07w\xd1;\xda\xd3\x8d\x8f\xcd\x99\xae\xee\x17_;\xf5\xc8\xa9'\xfe\xd7V;\\P=\x14{\xce\xf7\x1b\x0f\xd5\xe0\xa6<\xac7=\x1bon\xf6\xbb\xa3y\xa8>\xb8\xde\xb0.0\xca\xca^I\xd6O\x96\xcb\x1f\x10\x9dCL\xb0L')\xd3\xaay\xd5\xd4d\xa0\xd9\xa5&\xb3\xc2v\x08\xe0%\xf7\xf4[\x01\xea2\x15Z8\xe5\xa8\x84\xcd\x9b\xd9\xdd\xfa\xf4%\x9du0\xbf\xcb7\xc0\x9c\xb7?\xfc7\xe0\xc4\xb7\xfa\x94v~%\xc5\xd5\x9b\x8ac\xf1,\xbe\xbd\x13\x92\x1c\xb9\x00\x0fN\xa0z\x9a_\xa3\x96\xda\xe4\xc1\x00x2\x01U\xf5N_:\xab\xa2&\xb9\x1d(\xaa(<\xcd_Rx\x9a\xbfA\xe1i~\xa72^\x97\xe5\xfbm\x9b%\x99N\x86\xa8\xc9\x1e\"i\x93\xa7\xae\x83\x0e\xfa\x0e\x9cw\xf6k\xea0|\xe0\xea\x92m\x92W\xd8\xa5M\x1d\xa4\x07\x1d%\xc8\xc91@\xd3\xa6.\xf3\xae\"\xff%Q\x91\xef\x80C\xf7A\x87\xae\x03\xef\x19\xf4i\x93S\x08\xa8M\xceg1+\xea \x10\xd7xj\x9b\xbc\x8f\xaaV\xd4\x83C\xe7\xa1\nO\xf3\xfbI\xac\x8b\x0b\xf2U?\xcd/<\xfb\x89\xcb\x00x\xeak_=\x1d<\xfb\x0d\n\x9e}\xf0\xec\xed\x14<{\xdb\xd7\x10<{?\x1b()x\xf6Z\xf2_\x12\x15\xf9\x0e8t\x1ft\xe8:\xf0\xc1\xb3\xaf(x\xf6\x92\xfc\xf5m\xf0\xec\xb7\xe9\xba=\xfb[x5\xe4z\xce\xde\x0e~\xbcTOGN\\U\x8e\xef\xfc|Y\x01D|\x92\xf6\xbd\xa6\xb2\x1e\x98\xd2\xad\x06vAx]\xd34\x89\xc4U\x95|\xd6Z\xe6\x9c\xb8\xc0r,/\xa8\x1c#q{\xe6M\xc3V\x8d\x1e\x8c\x1d\x92L%y\xf0\x02\x9e\xfc@\xa9\x1aq\xec\xe9Uy\xf2\x04\x1d\xf8\x02K\x0e\xb2\x9a:0\x06\x1d\x99\x03\x97,f5\xf9\xe66\xab\xa9c_\xa1G\x7f\xc1-;ZM\x9e\nk\x93*\x05f\xcd\xa4V\x935\xbfZM7\xc9\xb4\xaf\n\xde$\xb7\xbcm\xefj\xdby\xde\x1b\xd9\xdc\xde\xb59f\x7f\xab\xc95'\xdc\xbb\xe2V\x0e\xb9w\xa6\xb8\x9a|\xf3\xc7\xd5d\xcf*WS\xe7\x89\xec\x17\xd1\xa9\xa8ss\xbe>S\x93\xcc9\xecj\x1a\x80QW\x97\xaaM\x8eY\xf0j\xba!\xdd\xdf%^\x00\xfd\x84\x0c\xfe[\xab6\xf5\x08\x1cU\xd4C\xda\xd0S\xe2\xd05\xa0TQ\x97\x0d\xf6&u_\x91\x15u\x9d8\xd0\x7f\xf2@\xdf \xd4+\x00UQ\x87@TE\xeeg\x0f\xd4\xd4C~=\xe4\xe6\x7fzAM\x0eg\x1a\xd4t\x13\xddv>Q\xa0\xa6\x9b`\xd9\x9eu\xab'\xd7\xa3\x18\xde\x15\x9b\x8en\xa8\xc9\xef@\x87\x9anB\xfc\xaeGB\xd4t\x13\x1c\xdb\x0f\x95\xa8\xe9&x\xf58\x96\xa2\xa6\x9b`\xda\xf1`\x8b\x9an\x82a\xbf\xa31jr?0\xa3\xa6\xeb\xefw\x9f\xdd\xb9\xf5\x84\x8eWm\x1e\xa7y\xd4dz\xa6GM\x1d=\xe2\xae\x9e\xf0W\xb4\x07\xf5\xca6\xa8\xa8\x8f\x93\xee\x8f\x86U\x146\x9f.\xd4Q=H\n\x9b\xcf\xce\xf3\xba\xa2\xeeK\xb1\xa2\xae\x13\x07\xfaO\x1e\xe8;\x81nz\xf3\xe9\xf2\xc4\x93\x9a\xa4\xe4\\\x9fSm\x92\xf5l\x9b\x9az-\x94~\xcb\xc4\xebA)5\xf5\x9eh~\x19\xa5m\xea\xfa$\x95\x9a:>T\xa5\xa6\x8e\xcfW\xa9\xc9\xffQ+5\xf5z\xeaJM\xddw\xe1\x92\x86z\x16KM^\x8fe\xa9\xa9s\xf6R\x9bz\xaf\x93\xde\xea\xd8#Gc\x9b\x06c\xbf\x1b\xe8 \xc9;\x1d\xaaM\x83\xf5\xa1\xd3\x10\xf4\xd9wIr}\x1e\xacC\xd5I\x06\xc8\xf8h\x98\x9a\xfa\xf4\xc9\xe1\x811\xaf\xfa\xd4\x8f\x91\xd5\xcf\x8ey\xd5\xa5{\xa2LGk\x1dA\xb1\xf3v\xaf\x83\xdd\xefb\xed\xfd\x1e\x93\xaf\xa8\x9b\x17\xd4\xc1\x03\xea \x05I]d!\xa9\xb3\x1e\xef\xa5@z(\x0f\xef\xc7\xe8+\xea,\\\xe8%`\xe8\xfaH}E\xbd\x04\x0d\xfd\x84\x0d\xdd\x1f\xaf\xaf\xe8f\xd9\xef\xe3\x91]\xc1\xe3\xf6\x15\x0d\xf2\xc8}E\xbe\xe9\xb8m\xea5>}\xf6\xf5\xcb\xee\x8f\xe0Wt\x03\xbcW\\\xfb0\xdbQ\xf3t\xd59\x1dUz\x0fav^\xa2\x1d\x95yG\x81B\x0f\xa1B?5\xdeC\xb8\xd0G\xc0\xd0W\x81\xdf\x1c\xe3\xddU\xf7\x95)\xee\x01\xd5v\x1f\xa5\xddcL\xba)=\x18B]_;\xd7\xdd\x0e\xabT\xd4\x91]?V\xf9\xee\x8cdcwh\xcc\x93+?n&\xab/(cI\x86\xc7~\xfb$\xbf\xfd\x91\xc7\xbe\xc8[\xcf\xfbkwoc\xe99\x02\x92:\xa8Ao\xd3\xe8-,\xe8$0\xe8j\x08; \x0e\xba \x0f\xba\x9b\xbd\xebe\xb3\x8b\x91\xbb\x02\xf36\x88a\xeb\xa6i;\xc9\xdbO\xafI\xeaa\xc0\xae\x81\xc7n\xc6\xca\x931O\x96|\xa2\xe4\x1d9q\x8d\x80\xfb\x06W\x7f\xe2\x06\xf5\x8587x\"\x8e\x0d\xf6=\xcdX^R\xda8_\x8b\x18\xc3\x8b\xa58\xc9\xc8\x08,\x12\x9ab\x14\x03\x92g\x16\xad\xf5\xc93\x8d\xaa\x88j#UX-x\xab\xa2\xb7+u\x9b\xc3\xe14\x9a.\xb3\xc9\x92I#\xdb\xb1\xe5\x16\xb8d\xc7\xd8\xf3`\xacb\x03'\xd1\x81S2\x82\x93\x00\xc1\xddjx\xe5\xa08\xf5\x14\x9c{\x0b\xe0\x9cA\xe26\x9e\x15\xb9L\xa0\x8a\xec\"\x07\x1f\xb1\x83\xbb\xe8=\xb37\x9c\xf24\\22\\\xbcj\xab/\xed8\x15\\'\x82W\x8e\x84\xc7`\xb8\xe5=t\xcdp\xe8\x98\xcb\xd01k\xc1??\xa1W&\x82\xbb\x079Tv\x81W\x1e\x81w\xc6\x80\xc7\xac\xf1X\xc0\x8e\x9eL\x87\xc6\xdd<\x18g\xb4\xbe\x03\x07\x96\xee\xfb\xb8N\xae\xa8\xba\xc0\xc8\x1djR#\xc6n\x1c9`\xe2\x1a\x9c[S!\xdav\xe2\xda\xfc\x99\xf9\xda|t\xf0}\xf9^ \x17\x95|\x17\xb6\xfco>\x84|p\x14\x8b\xe9o\x05\xceW\xfb[O \xbe\x7f\xf7\xa2|\xb5w\xcdP\xa5\x14\x1a5\xb4\xdf.\xcc\xa0\xc8\xf0\xe5\x12G\xbc\xd38\xcfI^\xb3\xd0(3\xd0\x0b\x86\xa2\xfe\xed\xb9k\x98\xad\x11\x89\x15\x93\xdd\xec \x98\x1c\xbd\xf2\xb2=/\x1eb\xccP\x92*T\x8e\xc9\xb6jm\xaa\xc5\x96\xdal(/>.r\xad\x17\xe5\xb0\xf2\xdd\x16\x0e\xc0 ||\xff\xd3~\x8e))\xf2\xa8|\xe6Y\xac\x98\"K>\x178]\x01_F,\x99&\xb8\xf1F\xb0!/D>\xd3P=alx+8'\x8cD$\x85I1\x9d\xe2\xfa\xc9\xd4\x91|}B\xf6\x0d\x16\x05\xad\x975 \xbd[\x92bD\x99\xbe-\x92a\xd8\xd9\xdf\x81h\x8er\x141\x9c\x8f\xc4k\xce\xe2\xc1j\x8ag\x0b\x9c\xd5\xba\xeb\xe3\xfb\x9fv),\x91\xe6\x95eN\x82\xa9\xfaZ&}\xabL\xf1\x96\xb6\x90o\xd9\x94\x90\xe4]D!\xd1<\xdd\xcc\xe9\x13gE\xfb\xaa\xeb\xa7{\xb2'\xa2Z:'E\x1a\xc3\x84\xeb^m}\x08\"\x94\x91,\x89P*\xd6\x90\xbe\xe5\xbbx4\x1b\xedq\xd1\x8a\x1b\x17vF;\\}\x89WI\xa2\x08/\x19\x8e\xef\x8d4\xcfksz\x93\xc1\x92\x0b;\x89\xf0\x1e0\x8c\x16\x14\nZ .\x0eyy\xd52I9\xa7\xf2\xd9]\x98$\x19\xca\xf5\xde\xabx\x08e\xb5\xc4\xe5\x8b$l\x8eW\xfa\xa6\xa5\xaa\x83\x84\xf1\xddvA\x9b\xb7n2|)\x86\xfa$[\x8d\xe0\x07r\x81\xcfq\xbeg\xf4L>\xbe\xff\xa9\xf2|\xca\x07\xd2\xf5\x0d\x0b\x0d\x8a\xe1\xd3\x9c\xb1\xe5\xa7=\xf9\xbf\xf4\xd3\x1e\x90\x1c2R\xfe\xba'fc\x842 K\xf9.w\xaa\xef6\xb7B\xc5\xb2\xbcv\xd4\xd0.\xce\xcf\xc5\xc3\xec\x88\xc1\x02-\xa9\x9cZ\x82sF\xea\xbbGE\x080\x91\x8f\xa6 }\xa4nJ\xd2\x94\\\xd0c\xc3\xd8\xfe\x19\xdeL\xd7=\xe2\xd3\xa2z\x0f\xbf\xee\xb4\xf0\n(-\x1686\xdcq\xfagn\x9b~8=}\x07\xdf\xbf:\xad\x9e\xa9\xf9\xf8\xfe'\xb9\xc6\xc4\xe3\xef\x06\xe7a\xebu\xe5\xd3\xd5\x12\xff\xeb\x9f\xff\xd2\x16\x80\xf2\x81\xf6$+\xe7[iF\xc4\x08-s\x12\x17\x11\x06\x94I\x13\xa6Oa\xfb3\x9c\xac\xaf\x06\xa1\xe2]\x1e\xc4e\x86c.\xee\x08E\\\xb7\x10rV,\xa1<\x8c\x08\x13D\x0d \x86\xc4v\x97\x8ax0\x9f\xe40G\xe7b\n.\x1ak(\x96\x8b\x08U]\xe2\xff>'I\x0c(3a,\x92A\xa1>r<%9\xde\xab*\xe0\xf5\"\x96L\x924a+\xc80\x8e\xc54\x9a\x88\xc3\xbdb\xaa\x99R%I\xc6\xd5l6\xc3\xa2\x90X\xb3#\xb8\xfb\x91\xe2\xea\xf6$.%>=\xb9\xce\x92\xf3\x13ehf\xea\xfd$\xc7\xe8\x8c\xeb\xa0\xb2\xe2\xd1=\xfd\x8c\xfa\x990|\x0c\x8c\xdb\x90i\x91Er\x85\xf1~\x94\xba+*\xf2\x1cg,]5\x02\xe0\x06u)\x9eM\x9aN\x93(A\xa9\xc5\x96M\x8a)\xe4\x98[\"\xbc'.\x94IX\xd5hAq,\xdd\xbcj]j\xab\x9a\xe0Y\x92e\xbc\xb3\xe2\xa5\x7f=c\xdbo\x7f\x9b\xb4\xf1\x07\xb1R)\x106\x97\x8a\"\xdb\xd4RpW\xba\xa1\x80\x17K\xb6*\x97\xf6=\xbd\x11\x14\xde\xe8\xc4\xa0\x94D\xa7\x05R\x90,\x96)^\xd4/\xbc\xd3%\x8e\x92i\x12\x01\xc5\x0b\x94\xb1$\xd2d\x8b\x8a\xb5\xda\xc3\x05r\xd8\xf4\xb8zIo\xb9:\x9a`@rC\xd0pp\xb6\xfc\x98\xea\xf2\xa0 9\xd7\xcf\xe9R\x04\xe5RP\xbe\xc3\xe6\xc0\xd9\xa7\x93l\xf5i\xbd\xebA\x19\xa0|\x92\xb0\x9c/b=\x87\xca\xaa*\x1b\x81RRN=@\xea\xa1\xe5\xdaY\x18\x1a\xc9\xe1\xa4\xed\x16n\xb8\x7f\xb5W\xa7\x99\x9a\xef\xaa\x85\x93&\x13\xc1viG(\xd0b\xb9$\xb9\xb0\xe0K\x14\x9d\xed\x17\x19\xff\x1fn\xb7\xe5\xbcP\xaf\xa0\xd2\xd0\xeb\x1d\x1b2\x85\x82I\xc5V\xa9\x07\xca\x15+\x8a\xe3D\xea\n\x98\xe1\x0c\xe7\x88 \xe6\xf9>\xab\xba\x0fJY\x1f\xe7G\x0e\xa1\xba\xbdW\x97\x88O~8<\x86w\x9c\x7f\xae\x17\xca\xae\xa0\xe6\xa5\xe0/\xfe\xf2\x17\x83\x99|M\x08L \x81g0\x1a\x8d\xfe\xb7\xf63\xce\x0c\xcaV\xfa\x0fP\xb6\x1aq6^\xe7dqwJ\xc8=\xfd\xa7\xa3\x91\xde\xfe%S\xb8\xcb\xab\xfa(:rJ\xee\xfe\x89\xd7u\x0f~7\xe8pS}\xff6\xcb\xee\xc8\"\xbb\xbf\xa2s4\x98\xf0\xe0\x99\xf0\x0dy+\x03H(\xa1w_\x132\x8aRD\xa9E@\x92E^H\xf6\xb1QP\xcf\x83Fr\xb5\xe8\x1eXD\xf7n\xc5\xe6$3\x08Or\xf5\x9a\x90\xbb\xa3\xd1Ho\x0dj\xc1\xdd5~#&\x9f\x10kW\xa9\xf2J\xdeH\xa1\xbe|\xf5\xe1\xc5\xfb7\xefN\x7fy\x7f\xcf\x14)[OTs\xc3\xb2i\xb38\x1fZ\xc4\xf9=1\\\x13\xc7Ey\xfc\x0c\xfe\xb4\x9c\x8c^\x13\xf2\xfbh4\xfa\xb7\xfec\x94\xad\xf6\xb8\x1b\xcaK,\xa5\x13\xf5\x16\xe5t\x8eR.dsGL\"\xdc\xe4\xc2\xc0B2\xdd`\xe0c\xb6X\xb3 \x18\x14\x0bD|\xf5\xbf\x9eA\x96\xa4\xc6 n\xe6K3\x93\xf9\xe6V\xc8\xb9\xd2\xc5\xd5F\x03&\xab\xb5\xdbUY\x0f\xf9\xfc\xa6\xda\xeb-\x83d\xdc-Q7\xb5\xabp\xa9\xf6\xf9\xfe}$~\xe0\xee\xea.\xa0\x86\xb5\xe3\x96\xb0\xbc8PY\xa1\x9c!\xea\xc6j\xd3\x92\xa5\xabj_\xb9\x15,\xa8\xddd@S\x86UABI\"\x8e\xb1\xbb\xbf\xabn\xaa\xb4\x89\x15\xcbb\xb7\x0b\xb8\x9c\xd1;SBF\x13\x94\x8b\xce^\xee\xafF_v\xa4\x14\xc5\xdeKY\x9f~+*X\xdd\xe1ups\xa8\xfc\xe4\xaf\x1f~\xf9Y\xfd\xcb\xb3g\xcf\x9e\xe9\xe7\x00/\xb7\x8e\xb9H?\x92puP:Ar_WP\\\x05XgE\x8a4\x97ZoW\xc3\x8b\xc4x\xed\xb6\xec\x01^Lp\x1c\xaf\x1d\x98=\xe9\x8e\xab\xaaC\x9a\xe8M\xc3\xa5\x98\x8a\x8d\xec\xa7\xff\xe2\xa2\xfbT\x06\x13Z\xc1\xeajp\xd4\x0b\xa4T?\xc7\x86\x0d\x08\x8a\xce\xb8\x0eZo\x88\xa7I\x8a\xf5v\xa3\xd2Y\xefpNIf\\\xb6e$N\xbc\x1a;\x16#\xfc\x0c\x0e\xf55\xd7\x05DnB\xf9\xfd\x91\xbf\x05\x030r\xb5#d\xb9s\x0c;\xaaU\xdb\x16\xc3H\xf6rg\xcfT\x9f\xe8\xdf\xcfh\xc1\xeb\xfc\x0f\xd9\x85\xff4\x16\xe0\xfd\xdb\xf8\xde\xb7\x93o\xa6\xe5\x86\xab=\xd7\xe4lH(\\\xe04\xbd\x7f\x96\x91\x8bL\xe8\x999\xa2\x80 *(#\x0b\xcf\xc5\xd5\x9e\xf2{\xd2\x81\xdfX\x07\xd5\xdb\xc55;|\x02k6WHNiuc\x9f\xc4b\xac\xe6\xf9\x9c\xa4qy\x11\xad\xe0\\.\xe5$\xab\xd7\x07\xc8\x08\xa0\xba*\xb9d\xd4\xed\x08\x16F\xb5q\xbe\xcb\xf5Z%\xc2\xad\xd0P\x151\xfd\xd7?\xffu\xcf\xb0\x90\x86\x98s\xed\x06\xcd\xd3N\x88\x8aWy8::<\xa2;\x86)$\xffw\x89r\xb4\xc0\x0c7\xd3m\xef\x0b\xcd{\\\xa6\xea4\xaaH\xb2\xe3\xcdPv\x8e?\x17I\x8e\xe3c`y\xd1\x14\xbafC\xadJ\xbc`h\xd6j\xfd\x03\xce\xcf\x93\x88W\xb6\x1f\x11\xba t\x7f\x82(\xdegu\x82\xe0\xfe\xf9\xe1\x043t\xb8\x9f\x91\x18\x8f\x93lJd\xf1\xd9\xfa\xd4'-\x16\x0b\x94\xaf\x8e\xe1{\xcc~&1~\x93M |.p^\xe1\x0ee\xe0\x06x\x15\xe2\xbe\xa9JW\x92%\x96\x92~\x13\xb7J\xdf\xa9:,\x81\xae\x06\xc7\xbbG\x07\x07\xbbz\xb4\x0ch\x11E\x98\xd2i\x91\xd6\xa5\x9b\x8ay \x9c\xactH\xc6\x1b2i\x92\xb6V\xb0\x82H\xd5\xa6\x7fl\xbc\xb3\xdc\xd8\x00X\x1b\x11_\x1c\x19\xa0r\x87(\xcdz\x8a\x19/\xcd\xb6\\C>\\C\xc6\x8b\xc3\x87i\xa6=\xf2\x9a<4KSiB\x19\xceD\xa6D\xa7\xf2\x19f\x17$\xd7H\xd4R\xd6aJi\xcbFs\x94eX\x05\xb4:\x14\xb6\xc6\xf4\x16$K\xcet9o\x96\xcaEh\xf4\xca\x96 \xbb\xb4=\x94c\xed;@\xbe\x8c\xec\xb91\x86z\x1a7\x9a\xeb\xf5B\x0f\xa5#\xccP\x17\xd9\xa3\xe5r\xdc\xb9p\x9f\xe98K\\\x12h\xb5\xc5'E\x92\xc6\xe3\xb6=\xf4(>#.\xea\xd9\xd2z\x8c\x97\xc6\xd6\xf5i\x81\xc6\x94@\xeb\x9c\xb7M\x86\xf2\x1b\xc4\x8c\xc9\x98\x0e\xb3^|&S\xd7\x16$.Rl\x86\xe7\x1d\x1e\xe9\xe8\xd4hY\xaf\xf6{j\xceK\xf4k3\x9a\xe3\xe8\x8c\x16j?\xbf\xfe\xea\xad\xe4,il\"\xb9\x13\xfc\xab\xe4\xf4\x8d\xeaR\xce\x96\xab\xd3\xf8p\xab\x12\xfe\x1f\x0do\xaaNa\xaa#\xfa\xdaZU\xe9P[\xd5\xd4\x99P\x9f\x0bLY\xbb\xd9:\xf5i\xc3\x95\xab(\xe4@I\n9P!\x07jM!\x07*\xe4@\xad)\xe4@\xb1\x90\x03\xa5\xa6\x90\x03UQ\xc8\x81\n9P!\x07\xca\xd1K\n9P5\x85\x1c\xa8&\x85\x1c\xa8\x90\x03\xa5\xa0\x90\x03\xa5\xfc&\xe4@\x85\x1c(\x0d\x85\x1c\xa8\x90\x03\x15r\xa0B\x0eT\x83\x86\xc8G 9P\x82B\x0e\xd4\x1f%\x07\xaa{\xfe\x11]eQ\x92\x95\xd7\x91h\xb2\x8f>\xc8o\xea\xe4#\x91pT\x16\xd4\xe5\x1c\x95e\xca_om\xcaQ\xab\xfbM\x92uM\x08I1\xda\x0e\x04\xd5A\x04\xc5\xefV\xe4\xaf\x14\x8d\xd3\x15\x08k\xe4\xaf-\xd0\x8a\x02\xf0')\x00\x7f\x01\xf8[S\x00\xfe\x02\xf0\xb7\xa6\x00\xfc\xb1\x00\xfc\xa9)\x00\x7f\x15\x05\xe0/\x00\x7f\x01\xf8s\xf4\x92\x02\xf0WS\x00\xfe\x9a\x14\x80\xbf\x00\xfc)(\x00\x7f\xcao\x02\xf0\x17\x80?\x0d\x05\xe0/\x00\x7f\x01\xf8\x0b\xc0_\x83\x86\x00a\x02\xf0'(\x00\x7f\x01\xf8[\xdf\xe3\x8d\x19\xddO\x11\xc3\x94\x19Q\xc0\x9f\xc4'\xf5\xc3F\x1f0\xab\x01AYz}5\xf8}\x8a\x99\x0e\x18\xdc\xae\xa6\xfc\xf0\xd6b\x84\xf2\xdd\x06\xdd\xab/\xc6\xc8\x83\xe9\x91\x0e\xd33q\xec\x06P&\xcb1jk\x84\xc5\xf2\xe6\x9a\x85;p\xe0\x10\x1c\xb00paU\x92KL\xa5\"_\\\xccX\x99:8b\xd8+\x0c\x8d\x8f\x81\x1d#\x83\x0e8\x99\xb9\x03\x88\xcd\x9d\xb12\x18\n/\x83\x8e\x98\x99\xb1B.\\g\xdc\x0c\xfacg\xe0\x8d\x9f\x19\xab*\xe3\xfa^\x18\x1a\x0c\x8d\xa3\x81'\x96\x06\xbex\x9ayf\xd7X\x9b+\xa6\x06C\xe3j\xe0\x86\xad\xc1\x90\xf8\x1a\xf4\xc6\xd8\xa0\x1b\xce\x06Cam\xd0 o3/\x07Dql\xc7\xdc\xe0jp7\xb8B\xec\x0d\xae\x06\x7f\x03O\x0c\x0e\xba\xe1p6\x15\xec\x86\xc5\xc1\xb0x\x1cx`r\xe0\x8f\xcbA\x07l\xceAe\xdes\xc0\xe7`\x08\x8c\x0el8\x1d\xb8\xbbg\x0ex\x1dxzq\xde\xb8\x9d\xb16\x81\xe99`w\xe0\xc1\xe5\x80\x18\x1ex\xe1x04\x96\x07\x1d\xf1<\xf3\xbc\xa2vL\x0f\xba\xe3z\xda\xfax\x8b6l\x0f\x06\xc3\xf7\xc0\x1d\xa6\x02\x17\x9c\x0f\xfc\xb0>\xb0\x05\xe7;b~\xe0P\xaf!\xfe7\x10\xfe\x07\x9d\x84\xeb\x8e\x03\x82C/;\xe0\x81\xd0\x15\x13\x04\xb3T\x87\xc3\x06\xc1\x1d\x1f\x04G\x8c\x10\x9cqBp\x93\xba?^\x08^\x98!\x18qC\x18\n;\x04_\xfc\x10zb\x88\xe0 ^\x0f,\x11\xae\x02O\x04\x17\x1e\x0d+a8l\x11\\\xf0E\xe8\x811j+\xe4\x1f\x9apF\x18\x1ak\x04+\xde\x08]1Gmmr\x8fj\xde\xae;`\x8f`\x84H\xc0\x88AB'\x1cR[\x95\x11\x9f\x84\xae\x18\xa5\xb66\xe9\x07\x1a\xa2f\xc3a\x95\xe0\x84WB\x07\xcc\x12\xfcpK\xe8\x82]\x827~ \x16kk\xc1\x94\xc0\x03Wr\xc52\xa1\x0b\x9e \xbe\x98&\x98;\xde\x05\xdb\xd4V\xd6@\x0e]\x97\x8c\x1b\xc6i\\\x10\xd9\xcc\x8cs\xc2\xb0X'\xd8\xf0N0c\x9e\xda2]\xb1P\x18p\xeez`\xa2\xe0\x85\x8bB\x03\x1bm\xd39aI6\x1b/\xc9\x85\xeeNc\xa7\xc8\x84\xfd\xd9\xfdeN\x96\x84\xe2|\xbc\xcc\x13\x92'\xcc\x82\x88\xf5j\xad}wi\x05(*o.U\x82\xb3\x15-\xd1,\xc9\xc4Xl3\xdbjc\xfd\xa1\x8csc\x11\xa3h\xfc\xb5jN\x85\xc7Jb& \xd0r\x9b2\xbedz\x8c\xd1*OkD\xa9\xbcD\xf6\xff\xea\xe23U\xfb\x95\x80\xf9?\xcbp,\xa2T\xc6\x9f\xdf\xa1\x19~/\xefp\x1d\xc9\xdf5\x95}.p\xbe\x12\xd5\xf0j\xb9\x0c1,\x08e\x80EPSDC\x15E\x19aH\x83~:\x0b\xc0p\xdd\xbb\xedE|\xd1\xbc\xe8\xbf\xf8GV,&2ZV\x1dmi\x9c\xa3\xd0\xc5S\x9a\"\x8aH\x91\xb1\xb1\xa8L\xa7z.\x10\x05\x8a\xd9\x1e$\x8cV(\x02\x85\"\x93\x130\x96\x81\xd2\x8b\x84\xb6\xc7\xd4z\x0cw;\x11\xc1\xe9D\xeeFE\xf5\xf9\xdcfE\xe1m\xf2\x0d\n\xc7s\xc3\xf1\xdc5\x85\xe3\xb9\xe1x\xee\x9a\x06M+\xf0I)\xf0J'\x08\xc7s\xfb\xa6\x0etH\x1b\x18$e\xc0?] \x1c\xcf\xed\x93\x1e\xe0\x93\x1a\xd0!- \x1c\xcf\x0d\xc7s\xc3\xf1\\WX\x7fPH\xbf\x0b\x9c\x1f\x8e\xe7\xea>\xb3\xc2\xf6\x1e\x90\xbd\xcb\xe1S\x1f\xa8>\x1c\xcf\x0d\xc7s]`\xf7p\xe0z\xb8\x96BU\x93\x15@\xef\n\x9ekmC\xb8\x96b\x9b\xc2\xb5\x14\x1d@o;\xe0\xed\x0bv{\x00\xdd\xde \xb7\x1f\xc0\x1d\xae\xa5\xf0\x03\xb1\xc3\xb5\x145\x85k)J\xaaN4\xcf\x9b\xc7_\xa1<<\xbc\x11\xca^\x9f\x1dfy\xe1q>\xdez<>\\\x84\x01v)\x86\x8b0\xaeP\xb8\xf6+\x1c\xc2E\x18CH1\\\x84\x11.\xc2\x10\xf4\x0d\\\x84Q\xdd\x83\x11'|\xc1L\n.\x87\xfa\x12\x8c\x88,\x16E\x96\xb0\xd5xIH \xd6\xab\xee\xbexQ}\xf7\x8e\x90\xb4\xbe\xf1B\"\x92\xe5/\xc0k\x80\x88$\x19U^u\xd1\xaa\xe2N\xd5\xfb[z\xc1\xc5Z\x1aMb7\x90H\x11\xe3\x8c,:C\x08h\xc1\x17}\xc7\xe2.\x01\xfc\x978zA\x92F\xea\x1b0r\x86\xb32\xfe.\xb9\xaf4\x14\xdf\x01\xf0?E\xc9\x02\xa9s\xd9%\xbb\xbaH\xf4\xcf\xbf\x9c\xbe:\x16{i\xf9]\xb9)M\x04n\xf0\x12G\xa5\xbb^c5M\x9f]Y\xa1\x0ca\xa8\x1b\xa3\xc9,C\xac\xc81\xad\xd7(7^32#\xc2A\xde\xde\xe7\xb6\xb3\x02\xf9b\xa8\x84\xd2^#\xbb\xb4\xbdJ\x14\xa5\xb7\x84-2\xaaZ+\xa8{^V{!n|\x12\x92\xb3$\x85\xe4,\x93Z\xb9\x02\\s\xd3\x80\xfd\xad4\x94\xbbF\xfb\x15\xe3\x14\xcf\xc4\x8dA\xfb\xbf\xd7\xff\x1e\x97\xf7\xf6\xfc{?\xc7\x17(\x8f\xa9\xe1R\xa7\xc6\xc6\xee\xa5,\x9f\x90\xec\x94\xfbH\xefe\xd9\x96\xb1\x93\xceSY+\xa0(\xca\x0b\xa9\x16\x90\xf0j\xeb\xaa\xea\xccb\xa5%T7T~ykMb\xd9\xeb\xdb1\x83k\x01\x8f\xfb\xde\xd1$\xbbe.\xae\xee\x9b$m\x0f%Y\xfa)\xc9\xd6[IFO@\x92\xb5\xb7\x92\xcc^\x81$\xa7\xaa\\<\x04I\xde~\x82Q\x10\xc2\x83\xb0x\x0b\x92:\xf9\x0c\x86\xfa\xca\x08\xa0\xc9s\x90\xe4\xef?\x98CN\x15\xad\x95\xc7\xcbJ\xdfI\x05\xb2\x0e\x86J]\xb5\x9eT\xcaz\xc8T\x88\xbd\xacb\x97V\xff\xe6\x03 \xd7\x84\xc5\xc3\xa9\xb4`=\xa2iZ:\"\n\xedX7\xb4Y\xa9&w\xff&tJ\xf0\xb4k\xea\xb4jn\xc8\xd3\x966\xb9\x92\x8aH\xde)\xfe?\xf6\xde\xfd9ncI\x13\xfd]\x7fE^\xef\xc6\xca\xbe#Q/\xcb\xb6\xb4\xe1\x1bW\xa2\xa4c\xce\xb1-\x1e\x91\xb2\xf7\xcc\xde\x8d6\xd8]\xec\x86\x89\x06\xda@5\x1f\xf6\xcc\xff~\x03\xf5\xc0\xb3\x1eY\xe8j\x9aG\xce\x8c\x899\x16\x1bHT%\xea\x85\xfa\xbe\xfcj-\x9aw\xbfA:\x96\xdb\xa3\xae&\x16\x1f\xe6iz\xfa\xba\xdb\xb2\xbe\xa0\x15\xb74Zq\xdf\xf2\x8a\xdb\x85\xc3\x8c\xd6\xd2\xb6\x968\xba\xb0\xd7\x13\x9b_\xb5\x94&\xf0Bey\x9d\x17\xa3\xbd\xb1\x1dp\x9e}~=<\xfac\xb4\xdat\xc9\xc4\xb6\xdd<\xf0\x0b\xa2\x9d\x80=_\x0d\xf4\xc10\xfe\xd9\xd7\x01iro\xec_ir\x1f.2}\x0b\xccN\xe71x\xf3\xcd\xf3\x11\xa7x\x9a\xdd\xfbF\xb3;\xcd\xee\x93g\xf7\xb6\xe4\xa3\x99\xd8V\xf2\xd1\x85\xbd\x927\xbf\xee\xbb\xe4{Y\x97\xb42\xe9\xfeeHQ69\xd8\xfd\x85H\xebd\xb0\x15\xe0Z|t\xbd\xdd\xd3\xd1\xb8\xa3\xcb\x8f=\x89\xc9[\xfa\xa1\x7f\x92\xef\x04\xdc\xd8\x14\xab\xeeK\xa8\xa7\x1e=\xa5\x19{\xfd\xf9p+\x05?\xd1u_\xe2\xceS]\xaf}\xd1d'\x8d&;\x9a\xec\xee\xd6\x94Q\x7fG,\xca\xe4J\xff\x053q\xfc\xac\xeey\xa5\xea\xabg\x0f\xed\xab D\xc8\xfc1pzO\xc7\xe7\x8eN\"\xc3\xb8\x05u\xdbA\x99\x87\xbe&6\xaf\xb0\xa1~\x10\xef\x08\xe3\xfd\xb0Y\xd0\xa0/\x8d\x06}\x1a\xf4\xa3\x0f\xfa\xce1_\x04\xa0\xb2\x8f\xe4\xc7\xe2\xf7f\xdc\x96\x977\xc9B\x1d\x8f\xb0.\x16\xdb\x8c\x99G\xee\xceu\xd2\xe1=]\xbd;:fw\xc3\xd2\xb5>?K\x06\xa3\xfb\xb2\xdb\xf6\xa4c\xd4\x0fKk\xce\xce\xe4\xeeJ-\xe1\x92'\xd7\xf6\xbe\xe6\xe8\x0dgI\xc5f\x8d\xa2\x9e\x8b\xbe\xe0sT\xe4\xdb*\x8a\xa7\xde\xcc6cyr\x961\xa7\xa71\xab\xb55\x1b\xd7\x17\xbc_Y\xa2\xd3\xc86\x8a\x9a\xe7\xd4\xcc\xa6\xba\xc9\x87\xe3\xc3\x81?\x9a\xd5hV\xbb\xf5Ym\xda\xf2\xbf\xddE0\xa1U\x82\xe6\x9dVUZ\xe4\x8e\x85\x7f\xf3!\x7f\xd8\\\xdd\xcc\x1d\xc9|\xbe]o3\xa1)\xd1:\x13\x1d)\xf1\xb0\xdd\x0cn\xd5ewv\x06ik\xe8\x99E:\xa1\xe8\xce$\xf2\xcf\xa9\x12\x83j7\x1bK6g\xe9%30kv\x9cQl\x85\x05O\xc7\x02\x1f\x81\xcd\xd3\xc1\xc0[:i^\xea\x1a\xa2\xaf\xf8P8\xc0\xb9\xf1o\xd4I\x0bF\xe4\x1c\x95\xc7\xd2\xd5&!sVoH\xaaZ\x18Bg\xd3\x83\x15\xc3\x94\xa1\xab\xa3&b\x93\xafG\xa6\xe1\xa8\xfd\xd6ln\xa1I\xb9\xff\x1bM\xca{\x98\x94]\x9f\x9a\x7f\x0dH\xca\xb3\xc0(\xb6\xbc\xe2\x89\xc8{\x9b\xf9I\xf7M\xd7~\xdf\xde6$\xcbh\x94_l.\x8e\x82\xe3^i\x8c\xdd\xde\xd3q\xba\xa3+\x0e\x0fWf\xd2\xc2\xc0\xea\x13hU04Z\x15\x0c,lU\x00\xa8\x10\xba\xba}\x87\xbc\xde\x19K\x0cN>\xdf\xe6\x0f\xf5~C\xfe\x85n\xe4\xa6\x8a\x0c>N \x15\xcb\x80\xbcJ/\x99L\xf4M\xe6\x17\x0f\xa4\xeci\x05\x95\x08\x1fTI\x9e\x1a\xcf\x8f\x9c\xaf\xd8\xfc\"i-,\x8d\xd6\xc2\xb4\x16\x9e\xbc\x16nK^\xf1\xa4\x14\xe7\x07\x8e4\x1b]\xab\x90\xc1]\xbdJ\xe8\xd3d\x9a\x8b\x94\x1cd[\x97a_W3\xea\xb0~\xa1\"[\xa3\x17\xeaQ\x92\x93\x02_{\xa8\xb8R\x0e\xbb\xa3\xd5&\xa9\xcc\xb00\x92T\xe6\x1e\x83\xebo\xa3$\x95\x19#\x8a$\x95IR\x99\xc2>Y\xa9\xcc!\xd7\x96]\xa6\x0b\x96\xcfY\xb3\x93\xa7\xff`\xdf\xaa{\x95eo\xd5E-e*\xcb@\xdfi\xdc\x96\xeb\xdctO\xd7\xf0\x8e\xee\xc8\xf5#\xd0\xb5\x7f\xc95>n\xa3\x0c\xe8lQ:[\xd4z%\x9d-*\x8c\xce\x16\x1d\x1b\x9d-Jg\x8b\xda\x8c\xce\x16\xa5\xb3E\x85\xd1\xd9\xa2\xf66Mg\x8bJ\xa3\xb3E\xe9lQ:[T\x18\x9d-*\x8c\xce\x16\x15Fg\x8bJ\xa3\xb3E\xe9lQ:[\x94\xce\x16\x1d\x1a\xf6\x9cG:[T\x18\x9d-\xfa\xa9\x9c-\xdaZ\xef\x0bQ\xe3\x17\n\x8f\xea\x833\xa3\xefkb^\n#\xe6\xe5'\xca\xbc4\xa7*u\xe0\xc8\xe9\xa4\xcb.\x10\xfa\xe1\xf8pX \xa2_\x12\xfd\xd2\xb7/\x89\xd9\xda\x03\x82f \x9a\xb5^I\xd0\xac0\x82f\xc7F\xd0,A\xb36#h\x96\xa0Ya\x04\xcd\x124K\xd0,A\xb3\xd2\x08\x9a%h\x96\xa0Y\x82fmF\xd0,A\xb3\x04\xcd\x124\xdb\xb1\x180\x19A\xb3\xc2\x08\x9a\xfdT\xa0Y\x97\n\x02e\x96\x87\xa5\xedRf\xf9\x1e\x83\xeb\xcf\x89\xa6\xcc\xf2\x18Q\xa4\xccr\xca,\x17\xf6\xc9g\x96\xdf\xf7\xa6\x96?\xfaC\xff\xd7l\x95T+\xd7\xf9\xf2\xa3D\xf3\x86\xdc$\xa0\x1a(\xf2\xf6/\xb5/c\xee\xf9\xa7\x92x>\x898\xc5\x9d$\x05\xef\xfe:A42\x01\x8eJ`\xd8\xcb\xb0\x8f\xaf\x91i\x04>\x12\xc1\x8e\x14\x02\x07\x81\xc0\xbb<\xf1\x92\x07p\xeb\x97\xb8\xc4\x01\x1fm\xc0_\xa6i\x94\x01=\xb2\x1b\x1c\xfa\x08\x03\x11\xe9\x02;\x90\x05\xcc\x14\x1f\x17U .Q\xc0M\x13\x88A\x12@\xa1\xdc\x1e\x82\x00\x9a\x1e`G\xf2\xc2\xa9\x01v_\xc6]\xf3(\xa4\x80\x90`a \x01\xfe\x98\xa0\xc9\x00\x13\xa8\x00f\x84!\x12\x0d\x00E\x02\xf0S\x000\x04\x00g\x14C\xc1\x7f,\xf4o\x03\xfe#\xc0\xfe\x01\xa0\xfft\xc8\xdf\x01\xacc\xe1\xfe\xc8`\xbf\xa3D\xc6\x96: \xe6\xd7\xbb\xb3\x06\x7f\x16\x90?2\xc4o\x07\xf8\xa7\xc2\xfbbG\xc0Tp3\xb8\x1f\x17\xda\xb7}\xf8ya}\x1b\xeeh\x83\xf4\xe3\x02\xfa\xd3\xe1|\x0bt? \xb8\xf7\x82\xf4a\x10=\x1a\xa0\x0f\x84\xe7C\xc0y+4o/\x0d\x16\"\xc5\xc1\xf2\x81\xa0|\x00$o\xacZ\\8\xde\xd6)v\x80\xe2\x8d\xfb\x14V ~\x1a\x0c\xef\x82\xdc\xe3\x03\xee\xbb\xb7$4\xd8\x8e\x85\xda\xfbS$\"\xe33(\xddS%x\x0e\x80\x03mt\xb6\x864J\xee\xa4\xe4\xce\xd6(\xb9\x93\x92;[\x9b\x82\xb5X\x9dQr\xe7\xd8\"\xe1.\xbb!/\x13\xb0\x97(\xe8Kt\xfc\xc5\x8b\xc0\xec\x01\x83\xd9\x17\n\xb3\x07\x1c&\x04\x89\x99\x8a\xc58\xc7p\x1f\x1a\x13\x11\x8f\xc1\"2\x81\x98LtT\xc6\x8f\xcb\xec\x8c\xccPr\xa7\xb7d\xd3\x90\x1a\xa3+J\xee\x9c\x82\xd9\xf8P\x9b8\xb8\x0d\x12\x8c\xf0b7\x01\xe8\x8d7\xc9.\x10\xc1\xa1\xe4NJ\xee\xc4`;\xde\xa8\x86\xe2;x\x84\x87\x92;\x07\x16\x19\xef\xa1\xe4\xce\xaeME\x7f\x8c\xce(\xb93\x00\x0b\xda\x05\x0d2\xba\xa3\xe4N\xe3\x0d(\xfc\x88\x92;\xe3\xa1I\x94\xdc\xb93\xd6\x14\xa7\xcd\xa1\xf1&<\xe2\x84K\xee\xece\xb6t<\x19\x15z\xc5E=5]\xf1\x075\x1a\x96R'\x95-F'+\x02D8\xda\xda\xf8q\xeeM\xeaY\x16\x97M>\x8f\x88C\xf5\xe8\x0f\xf9\xbf\xb3\xfa)\xae\\\x9ecqY\xef\xc8\xc86\x92\xba\xd2\xcb\xe2\x12\xd6\xc5b\x9b\x99\x8f\x91\xfc[q)\xdd\xdc\xd3U\xbe\xa3\xb9<\x97\x858\xffZ\x86f\xbc6\xee\x95\xa9w\xed@\\\xb9\x89O\xc92\xf1A\xcc\x0bu\xfdx\x9e\xb2\x16\x13\xbcx\x97.\x02+\xd3b1\x91\x8b\xdb\xab\xd3\xf7,_\xf2\xa6)K\xf7 \xdd\x0f\x0b\xbe`\x9b\xa2J9.V\xfd\x8b\x11\xc1R7D\x8d\xd6:\xcdg\xca\xaf+Vf\xc0\x12\\\xa0%\xf8\n&\xcd\x07^\x82\x08[^\xac]_e\xde\x17Z[\xb2.\xb6\xb9\xa5\x96\xd2\x10n0\x1bi\xb5\x1d\x16i+.\x9e\x00/.X\xae6\xc2dut\xe6i=\x15'\xb9*\x9ck\xcf\xf5\xc7\xf7\xa7o_\x8a\xd5\xab\xbcV-\x03S\xb1Sw\x94s5A6\xbb\xa3\x95\x13\x04P\xb3\xa7\xfcp\xb0?\xb4J\x97y\xc2\xb7%\xab\x9a!\xb9\xfeTZ\x16\xcbBLM\xe6\x15f/H?\xa4y\xba\xde\xaeu\xeb\x15\xdf\x0b\x89x\xedE\x95du\xbbfy\xfd9\xe2\xecX\xb5\xad\x93\xebY\xd3g\xa2\xf5n;\x91>\xb9\x16\xe5\x96\x8f\x12\xc5~U\x87\xac^\x81\xd4\x1d\xb3\xed\x90P\xbf\xc6\xa6F\xb6\xaf\xd2\xa3<\xe5i\x92\xa9}h\x18\xb2\x0c\x1a[\x179_\x8d\xf6\xb0y\x92e7\xb8q\xa5{)bT\x11\x97G\x1dS~\xdb\x16\xe5\xd6\xd2_\xbd/\xc7\xbb\xcd\x8e|{\xaa\xd5mX9\xaf\xd7\xa4K\xb9\x9d,\xf2\xb7+\x9e\\0\x81D5\x93\x90\xa4\xcb\xd8\xb0A\x95\x8e.P+\xdbk\x9b\x17y\x95.X\xddA\xc4\xa6\xbe\xa9\x19\xf0U\xc9\xaa\xba\xfd\xdc\x91\xd8\xd4-\xb6\xd4_\xad\xffd\x95\x88D%s\xe3;\xfds\x93T\x16L\x05\xe0\x8d\xda\x07Q\xad\xfa\xf1\xc1sc^\x0c\xe3\xc5\xec\x8e\xd5^~\xe3\x17\xe7\xf0\x13Sm@\xf4\xe9S\xd1D\xe4?\xc5b\xcd\x01\xe7w\x83do\x19u\xe5\xd9\xe2`\x18\xaa'\x8f\x9e\xf5C\x85`~\xc9\x05c\x08\xefK\xadT?\x1c\x1f\x0e\xfc\x11\xeb\x8bX_\xb1\x16;\xc4\xfa\"\xd6\x97\xd9\x88\xf5%\x8cX_c#\xd6\x17\xb1\xbelF\xac/b} #\xd6\x17\xb1\xbe\x88\xf5E\xac/i\xc4\xfa\"\xd6\x17\xb1\xbe\x88\xf5e3b}\x11\xeb\x8bX_\xc4\xfa\xeaX\x0c\x06\x0e\xb1\xbe\x84\x11\xeb\xeb\xaf\xc0\xfa\xea0\xa0:~\\_\x92\x9d;\x1a\xb8Y\xbe\xb6\x0e\xe0\xcc\x0buT\xf8yQ>\xd0\xd2\xf4RE\xbe\xe7\xec3I\x03\xf8\xecA?\xbc\x9f \x94\xba\xfe\xa1\xfe\xd0\xfaL\x01\xef\x9fEd\x91\xd9\xe8bF\xb6\x98\xc2\xfd\xd4\x0dF\x86\x98\xbe\xa4O\x12k\xfe\xdah>/\xd3K\x96C\xc5\x13\xbe\xad\x8c<\xb1\xc6\xd3=]\xa9;\xca\x13\x1bD\xa5k\xfcO\xc0\xbftqf\xa9\x05p\x0e\xda\xffq\x1c6/\x10\x01;\xb1\xc8S\x0d@T\x05\x10p\x1e\xe0\xea\x03\xc8m!m\xa1\xd0\x9e\xd3\x99y\x7f\xc7\xf1\xb9\x13\x1b\xe2\x03?\xcc\x07\x13\xa0>w\x05\xb4\xb06\x06\xee\x83X\x90\x1fL\x84\xfd\x9c\x0e\xeb\xe0\xa2\xa1?\xd8\x1d\xfe\x83`\x08\xd0\xe9\xaa\x15\xdf\xc6\xc3\x80\x10\x1b\n\x84@8\x10B!Aw\xcbn\xe0B,,\x08\xb1\xa1A\xc0\xc1\x83\x10\x13\"\x84\x9daB\x98\x06\x15B,\xb8\x10&A\x86\xee\xee\xa0\x97 \xbe~\xb3\x17\xe8\x10\xf6\x08\x1f\xc2~ D\x08\x84\x11a\x1a\x94\xe8\x1b\x82qp\"\xc4\x85\x14!\x00V\x84ph\x11&\xc0\x8b\x88!\xf3\x0b\x04\xc4\x081`F\xf0A\x8d\x80_\x9e! G\x08\\\xc5\x05C\x8fNo\x02\x96D\xc0\x8f\x10P\xca\x880$\x04A\x91\x10\x1b\x8e\x84\x89\x90\xa4\xbb]U~X\x12\xa6C\x93V\x7f\xf5\x13}\xf0$D\x83(\x01\x8f\xb4\x01\x06\xaa\x840\xb8\x12|\xf8\xc2D\xd8\x12\x10~\x1d[\x98\x91 L\x98\x14\\<\x94 \x88ZN\x804a*\xac \xee\xa8\xc6\x837\x01\x0fq\x02\x12\xe6\x044\xd4 \xb8\xa8\x87C\x9e\x10\x04{\x82\x13\xfa\x84X\xf0'\x84B\xa0\xb0#\x0c\n\x88\xf0\x06\xc0\xa1\xb0\x0fH\x140et\xf4\x84x\xf0(` R\xd8\x01&\xb5:\xac/tA\xa5\x10\x1b.\x05/d\nSaS\xab7\xf9\x8d\xea\xfe\\G\xc0\xa7\xe0Dy\xc0 \xa3\xc2$(\xd5\xea\xca \xb1\xc2T\x98\xd5\xea\xcd!\xc1.-\x1e\xdc\n(\xc8\x15&\xc0\xae\x10\x06\xbd\xc2\x14\xf8\x15\x82!X\xf0\xcc\xb6\x1eX\x0c\x02\xa01,\x1c\x0bS Y\x08\x85e\xc1]\xf1)\xf0\xac\xd5Y\x07\xfc\xc4v\x19\x1cL\xeb\xec\x10\xf9\xd2\x0d\xd5B\\\xb8\x16|\x90-\xb8a[\xeb=S\xe1\\\x88\xd8v\x03`]\x08\x82va$$\xafMB\x8b;@`,\xb7%\xf3\x82@J\x8f?\xbc?~\x7f\xf2\xea\xfb\xd9\xc9\xe9\xab\xd3\x8f'\xb3\x8f?\x9e\x1c\xbf=\xe4\xc6\x9f\xde\x9f\x1e\xfd\xf8\xb7\xf0\xfb\x8e_\x9d\x9c\x04\x95\xf0\xc3\xdb\x7f\x7f{x\x1at\xcb\xbbWG\xdf[o\xd0\x19\x97\x13\x02\x88\xddU\xd1\xd8\xf1\x89h\x03\xe2M\x8ao\x7f\xd99\xd5^\x90\xf8\x8d \x86\xb2}\xe8i\xb3\xdb\xedM\xd0\xd9\x14\x9c\xd5\xec\xa5\xa9\xeb\x15\xa4x\xe4\xa6\x92 \xdb\x1dt\x1c\xf5\xe4~s\x1a?\xbc\xff{G\xa9\xa1\xc9\xe9\x95\x0f\x85\xc5VL\x83\xb2`\x82u`\x8f\x91U\xba@\x99\xa7\xf1\x8eK\xd9\xfb\x19WH\xc9\x9f\x88YF\xd9Q\xc6\x85\x93\x7fw\x94\xaanP\x9d\x1c\xe9z\x1eZ9\x80\xa1MRU,\xach\xbaK\x8e\x0b\xa7\x7f\x89W\xbc3\xc6r(\xd9\xaf\"\x01:\xa8\x94r\x14\x18\x97Q\xfe=^ \xcf\x934\xb3\x15\xed<\xcd\x93l&\xa5!$8\xb4g\xbe\xc2\xfd\x1bV\xdd\xdfy7<9\xab\xea\x15\xf7\xce~\xee\xe7\xc5\xee\x85\xc9\x8bY\xfd\x051\xbbd\xbc\xd8\xd1\x19v\x00?\xad_\xd8\x07)?\xd1\xb6\x93\x8a'\xf9\")\x17R\xbbC)\xaa,\x8bKV\xe6\xc9\xf8\x8c\x97\xd6\xdc\x12%\xd5\xf6l\x9d\xf2\x19O\xd71\x12\xa6\x16 g\x0fk_\xc6\xeb\xb4\x8e\x0b\xcb\x17\xb7\xf3@\xa1\xfc\xe1V:r\xd3\x94\xa49\xd5\x8eP}\x07\xd7{\x10\x9aG\xa8\xe0\x00J\xf7\x08\xe9\n\xdbh\x83\xd5\x8f\xbc\x85wC\x87q\x95\x91\xd0\xdaHS\xd5\x91\x94@Y\xc5\x93\xf2\x96z\x9bz\xe2-u6L3\xd1\x0b\xd3\xde\xc2o^\x94L\xbd\xbbu\xfdeZ\xaa\x19\xd03\xb2\xd9F\xb5M\xb2T\xcdl\\\xe1^\x19\xdb\x0b\x07rI\xcd\x9f\xd5>\x9e\x89<)\xcd\xd9\xf1\xdd\x1d>g\xd7|v\xc1n\xcco\xc5\xfbN\xbch1Oy\xc6^\xc2\x7f\xda\xfa\xab~\xbeV\x93\xa9\xffSQ-\xc4\x82\xac\xfe\xc7q\xb2d\x1f\xa4\x86\xe3\x81\xfc\xdd\xe2Lr|k7\xb5\xdb:\x84\x0c\xd6E\xc5\x81 \xc2\x82`:\x18n\x15\xa3\xf3\x8e\x01p04U\x08\xacC\x96\x94\x85\xaa\xeb/\xfe#\xdf\xd6\x8d\xafn{:\xf3\xbe\x93\xe6m\xc3J\xbb!\x9a\xd7\xa3\xd0L8\xb3\x0d\x1fWI\x05\x15\xe3\x0f \xe5\x95f\x08U\xb0\xcde\x03\\H\x12\xc4UZ\xf5\xdf)F\"H\xb3pQ*A&\x0f\x8fZ\xee\xf2\x87\xe3\xc3a\x05H7\x88t\x83|\xb3\x04f\x06\x80 \xe4b\xd9tI7\x08A$\x8eB\"\x9eB &\xdd\xa0\x88d\xe1\x10\xa2p\x10I\x98t\x83v%\x04O \x03G!\x02\x87\x93\x80I7h\x17\xd2o\x08\xe1w\x02\xd9\x97t\x83H7\x88t\x83\xb0d\xdd\xa8D\xdd)$]\xd2\x0d\xb2]\xe6%\xe3\x06\x10q1\xaa8!\x04\\\xd2\x0d\"\xdd \x0c\x99\x96t\x83\x84\xedB\x98%\xdd \x93'/)v*!\xd6:7\x90n\xd0\xd8H7h\x02\x91\xd5Ob\x0d%\xb0\x06\x90W\x83\x89\xaba\xa4U\xd2\x0d\n#\xa6\x92nPc\xa4\x1b\xa4\xac\xd1\x0d\xd2r+\x92\xbb\xd4\xf1\xd5\xfb\x9a\x1c\xc0\x90\x83\xbbz\xf8kK\x82\x12h\xac\x06\xa5\x86\xeb\xa4\xdbe\x1d\xfeyLC#s\xefOb\x14\"\xcb\xb2O\xe6\xa0\x91-x\xfb\x0cA\x17+\xf0v\x99\x80c\xf6_\x9a\xbf\x94\x80|\xe7o\xad$\xd6y\x92U\x1eM,0\xb2\xbf\xf1\x8co_g\xf1\\lgv\xa3\xd8\xdcH\x067\x82\xb5\x1d\xc4\xd4\xd6\x03\xe2e\xc1{\xfb]\xbdaP\xfc\xd8\x1b\x7f\xe4_\x92\xc5\xa2dU\xa5\xe1\xf9\xee\xa8\xd7z\x8a\xf0^u!\xd5@SX\x0b\xda\\0\x18,\xe5\xc1g\xaa\xb8\xac\x82\xf3\xb2X\xdfJ\x89[V\xce\xc1\x05\xbb\xb1\x15{0\xcd(zK\xa2\x96\xea%\xe3\xdb2\x17\xe0\x85\xe2m(^O\xc3\x85\x11\x90\xc3r\xb07/j\xa0GE;\xbf\xe5\x00\xde\xd7\x1fVR\xdc\x0e\x8a\xf3\xf3\x8aq(J\xe8\x17\x17:\x90i\xc5x\xe4hY6\xa0\x0dA\x94\xe5\xb3\xc5q\xb0\xf9\xab*#B)r \xd2\xb9\xfe\x9b\x18\x93\x94\xae\x9f\xdc}_\xb1\\\x07~\x9b7\x80\xc7`\xfa>\x12\xde\xb2\xba\xcd7!\x94\x10\xc1\xb6\xaaC}\xc1\x02\xe3\xd9w\xbf\xe7\xe0\x0e8G\x86\xf0f\xe9:\xc5FW\\\xab\xe9:6*\x92\x04\xc3\xba-X~s\xd6\xbf\xf6\xbcm\xe4\xd6w\xf7OG\xe7\x90\xb1s\xaeP\x96\x94\xcbe\xb7^\xf9\x08\x1cOv\x10\xf9\x90:\xceg7\xc0\x92\xf9\n\x92\xcd\xe6O\x8cb\x97P\xd5\xde\xef\x8ae\xe7\x8e:\xa2\xa2\x85\x16B\x06\x12\xea\xffH\xf3E:O8k\x10}}\xc6_}\xa1jH]wi>\xcf\xb6\x8b\xc1\xd6C\"\x9f\xd2P*\x06oL\x10t:H\x9f8V\xaf\xcb)\xec9\xfbx4\\\xd9\x0e\xaa vkJV)&\x95\xe8^m\x7f\xac\xbb\xdc\x81\xeaM\xe92/\xca\x01N\xaa{c\xff\x1122\xbb\xbe\xd8\xb3\xa2\xc8X\x8f\xc5\xdb\x0c>\x83_\x0c\xaf\xb6d\x97\xac\xec9u\xbdVu\xf5\xf0\x95\xea\xd7)\x19\x1a\x0f\xdc\x9d\xa5\xe7\xb0~\x18\xcb\x05\xc9\xa4(\x17\xac\xbc\xadX\x84\x9dN\xad\xe7\xd4G\x7flZUK\xe7\x01\xd5z\xa9\xa8\xd5G7-\xe1Vp\xcdZ\xf5/}\xe9\xd1\x1b]w\xa3\xfc\xe8=]\xff;\xae>j#\xd1M\xe2\xe6v\xa2\xbd/v\xaaS=\xd4Yt\xf0\x16\x1f\x10t>D\x1d\x00 K\x9bD\xeas\xf8\x0b\xd2\x0b\x8dJ\xec\xf3R\xfbb\x93\xfb\xf0\xf4\xbeH\x04\xbfi\x14?\x87\xbb@\x85\xd0\x1di~\xb1\x89~\x81T\xbf\xc8d\xbf0\xba_ \xe1\xcf\xd5\x86'h\x82F%\xfd\xa1h\x7f\x11\x89\x7f\xbbR\xff&\x91\xff\"\xd1\xff\xa6\x10\x00\x1d\xce\xd0\x1a\xa0{ \x01\xee\x8f\x06\xb8\x17\"`\x18\x150:\x19\x10K\x07\x8cJ\x08\xc4S\x02\x83I\x81\xe1\xb4@\xefP\x88S\xfb\xdc\x99\x1a\xe8U\xfaD-\xa8\x10\x04\xc1\x90UW0I\xd05 \xa2\xf5=q\xe5\x8bH\x15\x0c!\x0bF\xa6\x0bN#\x0c\xbaZ\x10J\xd3s\"i\xd0\xe2\x8d\xa3\xf4<\xe3\x10\x07\xd1\xec7\x04y0\x88>\xe8\x93\xc3\x9bB!\xf4\xf9\xb4R \"\x11 \xc3\x83\x89'\x13\xfa\xea6\x81P8\x91R\xe8\xa2dD\xa3\x15\xa2\x89\x858j!\x96\\\x88\x88r8\xc10\x84b\xe8V\xe8\x8cB3\x0c$\x1a\xeeF5\xf4\x054\x80n\xb8\x07\xc2\xa1\xb7t\xd6\x96\x1e\x8fv\x88 \x1eN\xa7\x1eZ\xdcq\xaf\x12gT\xfa\xa1\x8f\x808\x91\x82h\xf1\xe5W\xe0D\xd0\x10\xdd\xea\x9b.\xed\xcd\xd8d\xc4\xe8tD;!1&%\x11CJ\x0c\xa7%\x06\x11\x13'P\x13C\xc9\x89\x1e=Mw\xe9\xb0t1,Eq\x02I1\x90\xa6\xe8\xa8\xee\x14\xaa\xa2\xc5\x15BAs\n]\xd1\xd1\xe4\xfd\xea\x99\x11)\x8b^\xe5\xcc}\xd0\x16c\xb5\xc5\x00\xeab\x08y\xd1\xac\x8b\xe9R\xc5\xf4~\xbf\xbb\x141\xf1\xec(\xd7=(5\xcc\x10\xc6\x94\xeb.\xa7\x12&\x92E\xe5\xba\xc1\xa1\x82\x19\xc4\xac\xea\xdf\x88\xd9y\x88\xa5\x7f\xd9J\x0c\xd9\x9a\xd9\xed\xb2P]\xcf\xbd->jk\x1eM\xc9?\x89\xa3\xdaZp\xf9\xf6\xc9[m\xcd\xa3wy\xfb\\\xd6\xd6pZ\x97\xb7\xcbom\xcd\xaes\x89U\xb9\xe4\xbb\xe3\xea\x1e}K\xef\x1c\x82\xd0\xb6D\xf8p\xebZ\"\x1c\xe04-\xbd\x8ep\xc3qL5\xcbvH\x1e\xff\xe6U\xb2\xf4\xd6\x07\xa7s\x87\xd1\xb0\x8c\xf2(\x84z%wHE\x81O\xb9\xd2\xdb\x1f0=\x02\xa1Y\xe9\x0d\x06\xa0\xf4*\x11np\x0d2X\xa9\x12\xa1G\x19W\x8d\x12\xa9E9E\x89\x12\xa9C\xe9\x0d6\xa6\x01\xa3\x14(#<\xc9\xff\xda\xe3)O\x9aG\xa0\x00\x89>\x94B\xdf@\x93o\xe0\xef\xc3\xf1!)\xf2\x91\"_\xb4\xe1p\x12y\x8f\x14\xf90\x94\xbd(\x84\xbd)t=R\xe4\x8bH\xd2\x0b\xa1\xe8\x05\x11\xf4H\x91oWZ\xde\x04R^\x14J^8!\x8f\x14\xf9v!\xe2\x85\xd0\xf0\"\x93\xf0p\x14\xbc\x88\x04<,\xfd\xce\xb0\xb3N\x8a|}C\x10\xee\xb0\xab\xa4`\xb2\x1d)\xf2\xa1(vS\x08v\xa4\xc8g\xbb\xccK\xaa\x0b\xa0\xd4a\xf4\xe6B\xe8t\xa4\xc8G\x8a|\x18\xd2\x1c)\xf2 \xdb\x85&G\x8a|&O^b\xdcTZ\x9cun E\xbe\xb1\x91\"\xdf\x04\xfa\x9b\x9f\xfc\x16J}\x0b \xbe\x05\xd3\xde\xc2Ho\xa4\xc8\x17Fs#E\xbe\xc6\xf6Am\x8b\xd1\xe6\x02hmxR[\x90\"_\xda\x1d\xb9\xfb'\x9f\xb5\x97\xf4\xf00\xb9\xdb\x0f\xe9b\xa8\xbe\xd7\x1d\x91RqtZo\x18l\xd51x\xb9\x9d\xaa\x00\x13C\x1a\xe3\x91\xc2\xca+\x87F\xc6\x1buI\xa3\x91\x91\x081\x1c\xf5G\x81\xffUi\xbe\xcc\xc6\xb5\xef dh7\xf7t\x08\xee\xa8@\x86\xae\xd9\xdd\xc0\xb4\xbc\xca\x1aA{:\x8e\xb3\xdf\x1a\x01\xb3\xc9\x8fq\xf3\x11\\\xc1\x93Fg\x86\"\xb6\xd4\x82\x99\x18\xde\xc2\x7fBg\x86b\x82\xa8\x86\xa16\x82\x9a\xab\xa2{\x80|N\xfd\xe7\xb9\x94\xad\xd2Z\x83\x96\xc3\x1c\xc5\x95<\xbd\xb4\xac\xeem\xfc+:uS\x18\x9d\xba\xf9\x97:uS/\x02B(=\x83\x85\x836\xa2\xf4H#J\x0fQzZ#J\x0fQzZ#J\x0f'J\x8f\xd9\x88\xd2\xa3\x8d(=D\xe9!J\x0fr\x95D\x94\x9e\xc6\x88\xd2\xd35\xa2\xf4\x10\xa5\xc7`D\xe91^C\x94\x1e\xa2\xf4X\x8c(=D\xe9!J\x0fQz:\x16\x83^A\x94\x1eaD\xe9!J\xcf\xdd\xa5\xf4\xd0\xf1mS\xcf\xc6\xa2\xe3\xdb\xf6\x18\\\x7f\x1b\xa5\xe3\xdbbD\x91\x8eo\xa3\xe3\xdb\x84\xd1\xf1m&\x8e\xea\xa3?\x1a\x92\xa2\xebL\xb7N,5\xd9K3W\x15OU\x1f\xcb\x9a\xe6\xb2\xd4u;\x95'\xbc\xe8\xe7\x1e\xbdi\x17+\xea\xeaW\x8bE\xe9\"\xb6\xaa\x9f\xee:\xaf\xd5\xc6\xeb\x98\xc4\x0e\xf3\xb2S\xbd\xe0\x84\x97\x1f\xe5\xe1\xa5z\x1e\xe0\xe2gr\x0f#\x95\x94\xc0\x8c\x16\xc8?E\xb0L\xe3rL\x91\x0c\xd3p~\xa9?@\x91\xb9\xa5Nf\xe9dU-U\xc8 \x0c\xbc\x817\"\xe0I#\x02^\x9c\x81\x85\x08xD\xc03\x1b\x11\xf0\x84\x11\x01olD\xc0#\x02\x9e\xcd\x88\x80G\x04\xf0\xf2\xd9\xcfG\xa7\xdf\xcd~z{\xea\xbeQ\xa3\xea\x93\x02\x81\xd9\xbek\xad\x1e_\xde\x8b\x8b\xc5\xbb\x11\xdbOr\\\x96\xdb\x8du3\xd7\xadU\x12\x15<\x0e\xe5\x98\xd6\x92\x19:\xc3\x9b\xe7N\xfb\x8b\xb7\x06\xa2\xc3\xa9\xc8\x8b\x87\xc5\xa6[\\\xc7\x17\x9f\xe9q\xff|{\xf2r\xf8\x87\x8e\xfb\x1b5rOs\xae\x1a\xd7K\xd3\x1f{\"\n\x92\x04\xb3\xc3\x93~|\xffr\xf0\xef^\x8cv\xf2\xdc\xb6\xde\xe13\xda_\xfaO\x13\xbb\xb9\x97\x8c\xa3\x9f{\xc5\xd2\xe5\xeav\x8f\x99\xfbY<\x92-:=\xa1\xad\xc36O\x05h+\x8a_7\xff\xfa?\x1c\xce\xaaM\x96\xf2\xa9\x9b\xeau :\xcf\x96!\xcb{\xd4 _oz%\x9d\xcc\x8b\xbcJ+}he\xc3Y;z\xf3@v\xedz\xeez\xa0w\x10\xecu\xb2\xbd\xaf^e\xe4zF\x1f\x98U\xfb\x93+\x1d9v\x8c\x00\x16:\x0eN\x18\x1d\x07\xf7\x97:\x0eN,\xe3C\x08x\xf2\x9b\xe1\xc3\xf1\xe1\xc0\x1b\x11\xf0\x88\x80\xe7\x9b\xfd0s\x0d\x10\x01\x8f\x08x\xd6+\x89\x80'\x8c\x08xc#\x02\x1e\x11\xf0lF\x04<\"\xe0 #\x02\x1e\x11\xf0\x88\x80G\x04\x99\x0c~\xabT\xf0=\x12\xc1o\x8b\x06\xbe/\x12\xf8\xadS\xc0\xfd\x04p\xc4P\x82m\xf1\x11\xa9\xdf\x16\xe2\xb7\xbf$;\x93\xbecR\xbeMo\x07\xc9n\x0d%\xb7\x12\xb7\x95\xb8\xad\xa6\xdf#um\xe2\xb6\x12\xb7\xd5l\xc4m\x15F\xdc\xd6\xb1\x11\xb7\x95\xb8\xad6#n+q[\x85\x11\xb7\x95\xb8\xad\xc4m%n\xab4\xe2\xb6\x12\xb7\x95\xb8\xad\xc4m\xb5\x19q[\x89\xdbJ\xdcV\xe2\xb6v,\x06\xcf\x90\xb8\xad\xc2\x88\xdbJ\xdc\xd6\x7f\x05n\xab@\xdfle\x17?\xf6J-\xfe\xa0\x0eVl\xa0\xb3\xcd-\x9e\xaa\xa8\xb9J\xeb4\xe7\x0dY)\xc9\xf3m\x92\xcd\xc4J\xa5j )&^\xd2+q\xe9qs\xa5\xde\x9e\x82\xdaa=\x8aJ_\xd0\xfa\x923\x9d\xaeZ\x8f\x904tvO\xd7\xf3\x8e\x92\x93,q\xea\x9as\x97\xc7\xb9\xbf\xe3\xdf?\x19=^#\xb2\xdew`p\xd6{+\xd2\\tl\x85\x07\x0f\xdf\x18\n\x1b69z4jHt\x08\xa14\xc2\x89]\x9b\xa4{\xd8b\x0d\x1a*\xd3\xfc<\xebh\xdc\x99\x86\xc8#}\x89b\xb1\x9a\xfbh\xe3\xc81>6\x9e\xd4\x8fwv`\x1cD\xa5k\x91\x06\xc46\\\x961\xcf\x12O\x83+3\xd3\xa5\x89\xf5\xf4!\xad}\xf1\x1f\x8e\x0f\x87+6\x1a\xdahh\xbb\xdbC\x9bX\xa8;\x96~\xc7\xe2\xf7\xde\xa0&3\x1dDr\xcfy\xd3\x13\xdb\x05\xbfqP\xfb!\xcd\xb9t\xa5~\xbd\xb3\xa3Z7 ]\xeb\x7f\x9f\xc8\xa8\xf4\x85Lu\x00\xf4\xb7\xc9\xbaXl\xb3\xb82\xa6u\xb4g\x0b\x96\x17\x16Z\xb5\xb7\xc1(\x91N\xae`\xb0y\x91\x8a\x0f\xdb\xda\xaf\xe1\xfafx\x9d\x95 g3\x89k\xef\xf6\xe4ur\x9d\xae\xb7k\xbdZ\x95.\xeb\xef\xecv(\xaf\x9f\xe5,\xcc:\xb9\x8eS\x88\x90gN<\xcb\xbc}f\x9a\xe3\x9e\xb9,\x92lvV\xe4\x0b65wA=\xb1vT\xbf\xe4\x0d+\xe7\xf5\xa4)}B\xc2\x8b\xb5\xe9\xdb\xe0,+\xe6\x17\xd5l\xc3\xca\xd9\x0dK\xa6\xa56 \xf2&\x9a\xe25\xd3\x9c|p]L\xa8\x1f\xdc\xbb\x071\x87\xcba\x055\x81\xab)[\x8di\xc4X\xa5\xe9\xda\xf0\xfb]\x9b\xae\xe5\\\x83\x9e\xb0;\x9f\xf0\xaa\x9d\xeb|\xb2\xa4\xa5\xb44\x93\x95\xa4\x97\xcb\x99\xea\x81\xca\xefHy\x05\xd5\xf6\xac\xda$\x82f\xd6n\xd2]\xb0\x1b\xe3\xe4\xfe/4\xb1c\xe6\xf5\xde\xb4\xae\xc5\xcf\x9b\x88E\x9d\xcfu\x98'\x0d\xb6S\xe5\xcc\x1dm\xd8z\xe7\xa4q\x98\xc6`\x1a\x83\xcd\xb7\xdf\xb51\xd8\x05W\xe8>jk\x80\xcdP\xd9\x1d6\xe4\x88Z/\xad[\xb1\xfev\xd0=/v\xce_\x1e\xd4P\x17\xd6!\xb5q\xc1nzE\xac\xff\xadQ\x94\xa6d\n\x85\xd7u\x8aZJ\xdfDWeI\xb5J\xf3%z\xaa\x1b\xcco\xe3\xaf0\xedQ\xbd\x0eu\x7fo\xee:Q\x97\xfc\x0b\xcda\xd6~/E\xfeO=\xe4'\xf3\x15\xd4\xd3\xae\x98\\\xfaq\xb1g\xfa\xc9\x1dV;m+\xcd!\x91\xdb\x9d\x82)\xb7Nnd\xaa\x9c\\\x14\x89<\x7f6/\xd6\xeb\x94\xcb\\L.\xd3\x8c]\xfe\xe6E\xfe\xab\xca\x10\x91\xf90\xce\xcc\xcf_N\xc4\x93^\x8b\xc5\xde\xcfb\xfd\xf5K\xb3\xe9\xc1Y\xb9n\xbe\x0e\xc4\xeb2 1\xf5\xdc\xfd\x90V\x95v\xf7:\xe5\xaf\xea\xae\xf8\x8b\x99\x1d*\x9b\xc6l\x9b\xf3t\xfa(\xdd\xbe\xff\xba=>\xac_\xd0\x8em\xe04]\xb3\x8a'\xeb\x0d\x88\x92\xa9\xd6\xd0\x7f\xe9i\xa5J\x0f\x0b\xa1\xb9fu\x96\xa5\x97,gU\xd5,?\xcd\xa1\xe0\xc5\xfa\xac\xe2En\xdb\xe0\xd7\x81\x18kTu\xcd\xa6\xdd\xd57l\x1c~^1\x91s'\xdb\x9d\xce \x13\xd5_%\x15\x9c1\x96w\xca\x0d\x9f_\xa4\x99\xad\xa7\xd7Vl\xa5\xd8F\xe3\xa4b\xfc\x0b\xad{V1g\x12\xfc|\xd8\xe9d\x97\x10\x99c\xf5\xf7\xe6e1W\x94h\xf1%\xedL \x95\x89\x84\xf3\"?O\x97\xdb\x92-`\x9dVgl\x95&\x97\xb6\xcc\xd9\xb5h\xd2\xfa{Hh\xbb\xedp0p\xdc\x01\xeb\x15\xa8\xf2\xc0\x05\xdb\xf06\x85v\x9b\xe7\xac\x9e\x85\x93\xf2FN\x87P\xb2d1\xd4\xa9\xeb\xda\x8f\x85\xd6\xd4\xfb\xe5d\xbb\xfe\xdc\xd4\x8f\xbf\xf8\x05\x92\xec*\xb9\xa9\xea\xa0'\x99}\x14\xea\x8d\x02\x87\xb2\x80\xc6A\x00S\xcd\x9f\xf4[\xef,O\xbaz*\xfa\xe7\xfb\x83%\x8f-7~]\xe4)/J\xa5\x06\x9aZ\xb8\xf6M\xc7\xad?\xba.S~c\xd8\xe5\x95\xb3\xa9x\x98\xfa\x12A\xad\xb9\xb4\xb9\x0e\xca\xdc\xe1\xf3\x9d\x0e\xb6\xa4\x83-\xb1\x07[\x02\xaa\x0fv\x85ve\xadzt\xfb4\x87\xe5\x87\xe3\xc3\xf63\\};Vp\xb5b\xa5\xa9\x11YV$\xf3\xa2\x94>\x84nG)+\xdf0\xe3\xebIG\xec-t#c\x0c\x87\xbe\xe3\xa4X\xb7\xe5vfx\x95l\xc3D\xe6\xec\xeb\xa4l^\x92']\xa4\x1f\x16\xd12m #\xfdd5\xe7\x0eG\xf7\x1b\x0c\xb5\xcfar\xf2\xa8\xf7Ahe\xa15\x7f\xa4\xcd\x8f\xfeo\xb4\xf9\xb1\x87\xcd\x0fg\x02\x08I\x83{\xc1\x12\xb0M\xce$\x0d\x1e7\xb8~Qk\x92\x06\x8f\x11E\x92\x06'ipa\x7f\x1dip\xf7\x06\xff\xa3?\xe6E^\xcd\xd4\x86\xb2K\x16\xbc\xfb)\xdc\x05\xbb\x87\xdf\x9f\x92\xbe5\x17b_\xd2\xab\xf2d\xdb\xf7\xbf\xa7\x03rG\xb7\xfd/\x93l\xd6\x8dY\xd4/g\xe7N\xbeg\xd1\xe3\xdf\xc5\xf7\xae\x9a\xfc\xfbB\xbe\xef\xce\xa8\xbb\xf7\x8e\xbd{\xff\xce}\x84\xca\xfa\xbfK!\xfe\x8e\xbd{\xbf~\xe2n\xbd]\xa7-d\xaf>\xeaN=~\x9f\xde\xbfK\x8f~\xd7\xee\x1dz\xdc\xfb\x8e\xb8;\x8f\xda\x9b\xf7\xed\xcc\xfb\xf7\xe51\xbb\xf2\xb8\xbaO\xdb\x91\x87bk\xd3\x9f\x9d\xb2\x1f\x1fs7>t/>`'\x1e\xdd(w\x1d\x80b\xed\xc0\xc7\xdb\x7fG\xee\xbe\xfb\xab\x17w\xe7\x1d\xb3\xef\x8e\xdfu7\x16x\xb8:\xb0m\xcb\xab\xadE\xb6\xa8\xef\x10\xcb#\x83\xb3\xfe\x82I\x1av\xeb.\xca\xce\x1dm\xdc\xd1\xc6\x9d\xe9\xf7\xbb\xb4q\xd7\xfd`\xb15\xc2\xee5\xbaC\xe8\x7f6t\xe8A\x17\xed\xf8\xda\x93\x02J\xfbM\xc6\x85Fo\xf3I\xb6`\x19[\x8a \xacz\xf4\x87\xfaGQ\x8a\n\xb8>\xc9:c\xc2\x1b}\xd3\x9b\xd6U\x9bs#6g\xda\xbf\x8bt\x1b\xf9\x9d\xd6<\xac\xf1\xa4\xc2d\xcc\xb11=E]wg?\xdf\xda\x8a\xcf\x0ce\xd4\xf6gt\xbc\xb6d\xee\xce\xb3\xf3QJ\xfd\x16\xe5 \x8e\x01\xae\xc3\x02j*\xef^;x\xbe\xee\x92gl\xbez\xf6\xf4!\xcb\xebAy\xd1\xf4P\xc7\x89 \xb5q\xa1@\xa7\\\x9a\xf9\x13 \x87%\xb9V\xf8\x93j=z\xfe\xee\xb5n\\\xdak]\xad\x92\xd2\xd5\x16\xa6UUzU\x0b\xb1\xee\x0b\x10\x1a\xd5\xf2\xc7\x92\xcdYz\xc9\x16\xb6\xb2ac\xd7\x8e-C\xc6w\xfd\xa5\xac\xbeY\x8b\x0b\x96W\xb0b\x99\x10\xb7\xb4\x12\x8f\x00\x92\xb9X+\xab\xaf\x0c\x07\xe3\xe7*\x97B\x99E\xdei]J\x9c\\\xc8\xf3\x17\xf3T\xe0\xb5\xcdG\xb3\xcd\xd5e!\xc5\x08\x8a+\xb9\x7f[\xe4\x0e2\x9c\xe7\x95\x9e%Y\x92\xdbR\xf3\"\x0e\x10\xd6d~i\xa86\x93\xac\xebH\xef\xe8\x06\xdbJ\x0e\x8b\xb4{\xb0\x92h\x10J\x8b[VGo\x8f\xd7/0\xc9U\xe1\\\xb2\xef?\xbe?}\xfbR\x08h\xcak\x95\x12e*>2\x8fr\xbd\xc1\xd5\x08\xb4W\xceF\xa0\x04\xfc\xd4\xe2\xd9\xde]\xd3e\x9e\xf0\xad\xec?r\x85Q7\xc2e\xb1,\x84:\xdeT\x06S\xdb\x89\xba\xdf\x05\xe2S9\xc9\x044Vt;\x1a\xbb\x9e\x8b/\xc9\x95\xe5\xdc\x98\x94w\xa4\xd3\xcd\xb5Q-u\xa8V\xa8\x06\x87f\xb7l]\x94\x0c\xaam\xca5\x94at6\xcf\x04~\xd7L\xd8S>%M\x13\x7f\xdbb\xb2l0\x8cU\xf7[\n\x95\xdd\xdb\xb8\x9f\xba(U\xbdBv \x9b\xbedG\x07\xc9\xe9}\xb9EM\xf2%\x8a\x16Q\xb4\xb0\x14-\x17\x03Bm\x1a\x98\xbe\x04\xbcY\xd7&G\x8f\x8c_.\xa4\xc8'\x8d6\x1e\xe2\xac\x16\xe8\xe46:\xb9\xcdltr\x9b0:\xb9mltr\x1b\x9d\xdcf3:\xb9\x8dNn\x13F'\xb7\xd9\xdb4\x9d\xdc&\x8dNn\xa3\x93\xdb\xe8\xe46atr\x9b0:\xb9M\x18\x9d\xdc&\x8dNn\xa3\x93\xdb\xe8\xe46:\xb9mh\xd8S\xb4\xe8\xe46atr\xdb_\xe1\xe4\xb6>\x9b\xa8\xe3\xaa\xf71\xd9\xbf\xaa\x87\xb86?\x8d)\x81\x06M\xd4\x1dx\x80\x94l<5\x93\x93\x92\x8d\xf7\x18\\\x7f\x9a,%\x1b\xc7\x88\"%\x1bS\xb2\xb1\xb0\xbfP\xb2\xb1\x99\xd8^\x94c^\xfb\xa3\x92u\xc9W\xd2\xad\x89\xe5\xfe\xa1{]\xc3i\xef\xdd\xdd\xe6\x1d\xbb\xd8\xeb=G\xf7td\xee(m\xbd[\xbf\xbbF\\\xef\x96m\xef\xcc\xd4\xbf6u\xbd*\xe7\x7fR\xcd\x8de\xd0\xb5o\x131\xbbm\xc1\xe9OQod\x8fl\xd7\xde\x98 ,*\xfe\xa7\x07\xa1S\x86\x1d\x82\xb0`\x15\xd7S)>\x12,\xe7\xa5\xb3\x97\xb8\x87\x81\xd6\xac\x03Bk\x88~+\x0d\xd3{\xa5\xcdK&\x871\xb7\xe0pk\xc8\x17)\xcd\x9fT\xdb\xb5\x90\x97/mPz\xe8}J\xaa\xbf5\x19\xe1\x08\x7f\xdd\xb6\x02\xbc(.`\x93\x0d\x0e\xb90\xd9\xbc\xa8?\xffEA\xecB\xfc]\x9b\x14D\x9f\xa0mk\x13\x02\xd9\xaf\xc1\xf0H\xf5k\xa9\x9b\xe0\")\xb5\xd6\x0bb\xeb\xd8\x17DA\x0cJ\xb2\x99'\xab\xa2\xb5\xa0 \x86\x87dP\x9e^H\xd4o\x9aW\x8f\xf0&V\xcb\xbd\xc8\x08\x85\x10{F\x8e6\xc9\xd1\xaf\x87\xb8?=\"mQ\x9a\xb4\xcd\xb5\xfa\\A\xdc\xdd\x19^\x1fvT\x06d\n\x82\xe8\xc8\x02RAx\xea\x86\xd1\x1d\xbe\xb0:v\xd7\xa0os^\xdetrXz\xaf\x0e1\x02\x83d0\x94,c\x97I\xcea\xcdx\xb2Hx\xe2*o\xaf\xb4j^\x11\xd4.\xf95\xd9)\x80\xfa\xd1\xe6\x0c[\xed\x0f\xfd\x9e\xdaA\x03\xb2\xb4\xe22\x15u\x93\x94<\x9d\x0b(\xc3\xe6\xa6Y4\xdd\xef.\xfd\xf3\xa5\xc8 s\xa4u\x9d\x97\xc5\xba\xf7\x04\xbd\x0ei\x9b\x87\xd8A@\x15\xa1\x9d\xbb=Y[\x9e \xdb?Y{&j\xd4$\x8d\x9b\xa0{\xdf\x19u\xb9-\x19\"\xdaP\x8f\x06\xf4\xe3\x81\xd6\x07\x03\xa3\xf5\x01\xad\x0f\xb4\xd1\xfa`h\xb4>\xd8\xef\xfa\x00\xd5\xf2Q\xef\x18_\xefQ\x9d\xed)\xabv\x96\xbc)t\x9d\xacVH\x1d),\xd0a\x97\x86\xe4\xb1:\x1cv3\\U6\xab\xeb\xf9\x8eDW\\$\xbb\x95w\x84\xaf\xbf\x1c\x93\xf11\xfaS1\xab\xf4R\xc6\\\xf8&gi\xe7\xa8\xa1\xe3e\x8d\x14\xe5\xe1\n\xa3<\xdcO4\x0fwT\x1d\x01\xbe\xf40\x8di \xb8}|\xc5*\x00\xd6\xf6\x02J\xc4\xed\xffF\x89\xb8\xbe\xa6\xda\x1a%\xe2R\"\xae\xd9(\x11W\x18%\xe2\x8e\x8d\x12q)\x11\xd7f\x94\x88K\x89\xb8\xc2(\x11\x97\x12q)\x11\x97\x12q\xa5Q\".%\xe2R\".%\xe2\xda\x8c\x12q)\x11\x97\x12q\xcd\x1d\x84\x12qG\x86M\x8a\xa4D\\a\x94\x88K\x89\xb8w3\x11\xb7*\xe7\xb3\xbe0\xbf\xad\xdc\xe3+{eo\xe9#\x9d\xb27\x0c\x0d&\x88\x87\xc3\x1a\x84\xa6eY\xea\xb0\xa88\xb2\x0e\xe3+\x83\xea\xd0\x97\x1f\x8fX\x03J\x87\xc6D\x8b\xd2\xa1\xa5\xed9\xb8\xfeD^J\x87\x8e\x11EJ\x87\xa6tha\x94\x0emJ\x87\xde\xe6g\x85\xa8\xc3\xacC\xc7 <\xfc\xeb\xa3\xf6a;\x05\xacy\xc8\xe8<\xb0\xc6\x99\xc8\xa3n[\xd8h\x8d\xa6\x83k>\x18\xccT\x02u\xc3\x9dM\xb5n#\x7f\xc7\xf2\xac\xfbM\xc4\x91\xfd\x8a@\xa70\x00\x0f\x98\x9e\xe9Mxvb\xc9\x8d;+\xe6\x86;\xa3+b\x0d\xc3\xcf\xe5r\xd6\xf0\xd3I\x05\n\xca\xc3A\xbc\x10i\xf8\xfc\x1b\xec\x0b\x946\xcc\xbbI\xcd\x197m\xef\xf6\xf8\xc3\xa5\xda\x04\xa5\xd9\x04\xc7\x08\x97^\x13\x18\xa7AZMj\xc8\xa8A\x07 \x97J\x13\x94F\x83\x0eRX\xb5]\xa93\xea\xf48u\x89\x83\x0f%\xad\x9ef\x16\xdbL\xd2m\xd4)wP\x7f(\xa1\x82\xf1g\x06\xc1Q\xf9^U\"\xbcu|\xc1\x0cK\x84A\xa6K\xdeY\xa7x\xf3\\\xc2r\\\x16\xae\xfcW\xd3\xea\xc8\x95\x07\x8b\xa9\xb3\xa1\xb6P\xf1\xa2T\x0b2\x91\xffZ\x7f$g\xac\x9b\xe3jt\xd5\x16\xcf\x91\xf4*N?\xd7\x1e;\xe7\xd3\xe7uX\xeb\xfe\xfeP,\x91\xd9B\xe4\xdfR\x12\x85\xd9\x03%Q\xfc\x95\x92(\x86}\x14\x7f\xaa\xd9\xc0\x9b\x81\xad+s,\x9cO\xa0\xd3\xce\x94Q\x92E\x9c/\x1cJ\xb2\xa0$\x0b\xb3Q\x92\x850J\xb2\x18\x1b%YP\x92\x85\xcd(\xc9\x82\x92,\x84Q\x92\x05%YP\x92\x05%YH\xa3$\x0bJ\xb2\xa0$\x0bJ\xb2\xb0\x19%YP\x92\x05%YP\x92E\xc7b\x10\xde)\xc9B\x18%YP\x92\xc5\xddL\xb2 z\x7f\x18w\x9a\xe8\xfd{\x0c\xae\x9f\x98N\xf4\xfe\x18Q$z?\xd1\xfb\x85\x11\xbd\xdfD\xefo\xa8X.N\xff\x7f\x1a8\xfd?57\xf6\x98\xfc\xad?H\xf3\xf3B\xb4\\y\x04Z\xf3\xe4\xc6\x97\x8b\xb9\x7f\"\xeb`x\x9c\xba\xfa\xce\xd2\xf6\xdb\x08\xdc\x0d\x82\x87>\xcf\xe9\x16\xa9\xec\xc3G\xf6\x16~}\xfez\x1b.\x0b\xbb\xb0\xeb\xef\x7f\n.\xaa\x14\xb8\xe9R\xd6{\xa6\xd2\xa8 b\xdb\x0d\xa0SA\x10\xa5\n:\xb4\xaa\xbe\xfd\x9a\xa4\x19[\xb8\xd1\xa912\xdc5\x1bb\xde7\xec\x97\xbe,\x0fh\xf1\x82\xab\x15S\xfb=]\xd9\x8e\xba\x1f\x9c1\x96\xab\xab\xed\xed\xa0,\xd6B\x15\x84-\xa0\xe2 \xdfVrS|$\xf0!M^b\x8b\x05\xb6\x06\xeaA\xe9PLV\xfe\xddz\xdb\xe7\xb2\x9c\xad\xe0\x9d\xfa/\x01\x82\x98oA\xec\x18\xb1|\xbb\xb6\xaf\xe3\x1f\xc2\xeb\xf7?\xbe\x99\x9d\x9c\xbe:\xfdx2\xfb\xf8\xe3\xc9\xf1\xdb\xc3\xa3wGo\xdf\xa0\xef\xa8\xff\x15x\xf9\xd1\x8f\x7fC^\xeft\xae\x95 \x82\xaa U~n\x01\x82VrB\xb2\x19C\x87p(v\xe3\xc5o\x9f\xa7\xf9<\xb3/\x0f*\x96\x9d?l\x05w,\x8d\xa0\xe5S\xc83\x85o\xa1j\xc3G\xb6\xe8\xba\xe0\xfb\xa8?\xa6U\xb5\x95\xc8\x83}\xc2\xea`\xef\xadW\xcb\x16j\xb7t;W\xa0\xb9n@ m\xff\xceY\xb9\xae\xc0|>lk\x1e\x91\xbbh\xe8\xfe\xba\xc8\xd3\x0bfP\x1ai\x0d\xf1\x82! F\xd0>\xb5s\xd4\xfaj\xbbN\xf2\x87%K\x16\x82\x16&\xd6}\xbe\x18\x01&N\xa0x\x02\xdc\xca\x93\x80\xfd\xd4Q?\xb6\xab\xb2\xa5Q\xd0\xf6\xc7*]\xe6 \xdf\x96\x0c>g\xd7\xeeO\xe9\x8f\xc7E)8\x9e\x7fg7gI\xc5\xac\x837\xc0\x15;\xabR\x1e\x03\x13\xe8\xd5X\xb95\xd6H\xff\x96\xa5\xf9\x85k\xe8\x99o\xcb\x94\xdf\xcc\xc4\x17\xcd\xdc)y8\xa5\x84\x9ew2|\xbc\xb1&l\x9d\xa4\x99\x17J\xd7\xae@\xb9\xb2\xd7\xd9*\x8a\xd3\xda\x94\xaa*\xb7z\"\x90\xe0US \xf5\xab\xb9T\xad\xfe\xaa[x\x12Q,\x8c\xd8$\xf6\x0d\x0d\x8b\xa5\xdf\xce\x03H\xcf\xdb\x1f\x1f\x88\x11U]\xe1\xa0\xdchA\xca\xb4\x1a-\xef\x96\xdb\x8e\xe0\x9d/D.\xd5\xc9\x80\x00\xf9\x94&\xc3\x83$\xb4$\x1d!Z\xa7y\xa37iu\xd7_H\xf2BK\x0f2_\x80\xe6\xc5z\x9dV\x15v\xd2l/\xefM\x8c\x9d?\xb79\x19{\x9e\xf2\xdag\xce\xca\x84\xbb\x15Z\xb1o\x05\x0c~{\x15U\xd2\x97\x9d\x8b\x9c\xbe\xa4\x03I\x1d\xd1)\xd4\x1b\x9fO+\x04\x17\x89\x80\x13\x1eL< \xc7W\xb7 D\x9c\x89T\x1c\x17\x94\x19\x8d\x8e\x83&\xe4\xe0(9XR\x0e\"\xca\xe1\xc4\x9c\x10j\x8e[\xda8\n='\x90\xa0\xb3\x1bE\xc7\x17\xd0\x00\x9a\xce\x1e\x88:\xde\xd2Y[z<\xba\x0e\x82\xb03\x9d\xb2cq\xc7\xbd\x12\xc6Qi;>\xe2\xceD\xea\x8e\xc5\x97_\xba\x18A\xdfq\xcb\x16\xbbD\x8bc\x93x\xa2\xd3x\xecD\x9e\x98T\x1e\x0c\x99'\x9c\xce\x13D\xe8\x99@\xe9 %\xf5x\x84\x88\xdd\xa5\xc3\xd2,\xb0\xd4\x9e \xe4\x9e@z\x8f\xa3\xbaS(>\x16W\x08\xe9\xe1)4\x1fG\x93\xf7\xcb\x0eG\xa4\xfax%\x87\xf7A\xf7\x89\xd5\x16\x03(?!\xa4\x1f\xb3\xa0\xb0KN\x98[P\xcb\xd60R\xc2\xb8o\xe3)2\xc2B.\xd8\xe2\x0f+\"\xec\x92\x10\xc6\x95|\x92|p\xb0x\xb0w/\xc5%\x1c\x1c*\x1b\x1c$\x1a\x1c&\x19\x8c\x16\x0c\x9e \x17\xec\x12\x0b\xf6\xc6\x0f\xf7\xb2w\x95 \xc6\x88\x04\xe3$\x82#U(\x9680^\x1a\xb8[\xae\x9d\n\xde\\\xb5\x93(\xb0G\xf3\x8f\xef\x8e<{\xc5\x80\xbd\xaf\x12\xd0Q\x81\x882\xc0^5D\xbf\x04p\xe4\x9aE\x16\xffEK\xffz\x85\x7f\xc3\xeb\xb9\xab\xe8/^\xf27\xbcl\xcew\x10M\xec\x17+\xf5\xeb\x15\xfa\x0d\xaf\xe0d\x91_\x9c\xc4\xaf\xb7@~y_\xdc\xfb\x88)\xed\xbb\x8b\xb0/F\xd6\x17\x1d\x14\xb7\xc0bh`B\xe4|1\xb3\x04\xa0\xc5|}R\xbe\xbd\x9a\xec.\xe4\x1ba\xa2\xc2K\xf8\xe2\xde\x02\x84\xcb\xf7J\x89^\x87\xbf\x9d\xc4{\xbd1\x02T\x9c\x00%\xdb\xebm\xf0\xda\xf0\xc1\x04\xb4`oW\x90\xd7\xe3\x10-I\x8b\x13\xeb\xddS\xb5Cdz\xe5X\xe6q\x18C\xa47H\xa2w\x8f\x81A\x89\xf36\xe2\xbb\x1e\x87^i^ohP\xc2\xbb\xa8p\xe0\xe6\x04\x08\nZd\xc1]\x87\xdc.Zl\xd7\x1b\x0b\\\xed\"\xca\xec\xa2Ev\xfd%\x9b&\xb0\xab\xa4S\x0d\xfe\xec\xf2\xba1\xc5u\x91\xd2\xba\xc1\xc2\xba]\x11]s\xe5\xec\xb2\xbaqEu1\x92\xbaq\x05u\x11r\xba\x93\xc4t\xb5p\xae\xc9\x9fWJw\x9a\x90\xae\xda_4\xf8\xb3\xcb\xe8\xa2Et{}\n+\x0b\xda\xa8\x82\xee\" J\x8a\xa0\xcaH\x11\x94\x14A[#EPR\x04m-j\xb2CH\xaaCP\xa2\x03)\x82\xee\x9a\xde0!\xb9!JjCxb\x03)\x82\xee\x92\xd0\x10\x92\xce\x109\x99\x81\xa3R\x19\"&2`\xd3\x18xX\x12Ch\n\x03)\x82\xf6,8i\x81\x14AQ\xa9\nS\x12\x15H\x11\xd4v\x9979! 5\x01\xa3w\x19\x92\x96@\x8a\xa0\xa4\x08\x8aI> EPa\xbb\xa4\x1b\x90\"\xa8\xc9\x937\xc1`jz\x81un E\xd0\xb1\x91\"\xe8\x844\x02\x7f\x12Ah\nA@\x02Ap\xfa@X\xf2\x00)\x82\x86\xa5\x0b\x90\"hc\xfbH\x11\x88\xd1\xe6\x02\xd2\x03\xf0\xc9\x01\x9f\xaa\"h_\xf1\xccV\xe6\xfeU\xbd2\xb7h\xe7\x9e\xcb\x1c,\xfa\xb6J+^\x94\xe9<\xc9fi~^<\xfaC2\xfc\\\xaan\xdf5\xb7\x1c\xe5\xe7E#\xe3V\xd7\xb3\xf56\xd4r\x93nuM{\x8am}\x7f\xf7t\xa5\xef\xa8Z[]\xc5\xf1R\xbeW\x94\xfa\x92\xde\xeb\x1f\x86E B\x9a\"\xd3\x9a\xb5l\xe0\x05\xe7V,Y\xd8\xa8\xe2N\xb7\xe0u]\x9b\xda#u\xb0r\xe4\xd1\x9agI\x95\xce\xe1,+\xe6\x17\xa2\xde\xf6\xeb}e\x02T\xb9j\x13O\x8bD\xa1B\x1c\"*-\xd9ln\xf7\x91\x98\x8d7i\x87Z\x19\x0f\xe6\xc9\x86o\xcb\x86\x12\xaa\xff\\n3&\xb3\x1b6eQ\xf7\x1fw\x11\x93\xe6}JD\xac\xfe\xc7|\x95\xa4\xf9\x03\xc7W\xa5\x127\x15\x0c\xcbz\xfenn\x82E\xc2\x93:.\xdb\xb9,\x9b\xfaT\x91\xa5r8\xd4\x1b\x98-\xc4q\xdfr\xe8\xb0\xb4\x8a\x0b\xaaN\x99\xe4\x95\x9c\xcf\xd7\xc9|\x95\xe6\x16!\x16\xc1\xc7J\xf3YjIi\x03\xdc+u\xb1\xbb\x01\xe7\x02\xc3\xf2\x06\xb8E\x8e\x9cX1\x8b\x17\xe8\x8e\x8e\x1c\x016%\xbb\xbc\xe5\x01`\x95T\xab\xc8\x9d\xd1)*$V;|V1>s\x0d\xbb\xdaP5\x05tmks\x9c\x93\xdc\xbb\xccIU\x19\x1a\xa6\xd9i\xf3\x87\x1cB\xc2\x0e\xf8\xd07\x0d\xed8)y\xc5\xf8w\xe2\x0d\xb8\x9a\xae\xa0e\xf2\x99\xbb\xc8\xa8\xa2\xa2\x8a\xa8\x8aW?\x8e\x89=z\xd9\x19\xeaa\xcfrO\xfd\xd3-\x15\xaf\x15\xf5\xbd\xa5\x07\x0e\xe3q^\x16k=\x8eC\xb1\xe5\x9b-o\xff\xd6\x8e\x1d\x16oBJ\xff\xd6\xeb\xd0J\xcd\xde\xce\xf3\x92\xcd\xe6\x96\x9e$\xfa\x87\xe2\x02\xdc\xd2#\xd9e\xba`\xf9\x9c\xdd\xd2\xe3\x9a\xf6\xd7.\x7f\x1c\xd3R=\x02\x17\x15\xf3((C\xb4\xf2\xf5\x16ur$\xeb}E4\xcb$\xb1\xa9\x08\xa7,_\xb0r\x9d\xe6\\\x0d*r\xfa1\xadh.\x93\xacb\xce433u\x10\\\xf4A\xc0\xcdf\x98y\x0c\xa7U-\x0d\x15\xeb\x90\x15r\x90z\xb5\xd3O\x97\x1e\x1f\xaea-\x0d\xa7d-\x0d\x11}@\xbe\x01P\xee\xdc\xda\xd6\xedu\x887\x00\x81o\x01&\x10=\xbd\x0e\xb9\x11\xf1w})HC\x13?\xbd\x9eZb\xa8\x8f\x00\xaa-\x94\x08\xeau\xb8I\xf8\nM\x08\xd5f\xc2z\x82\x89\xa1\xda\xf0\x04Q\xaf\xab.\x814\x80(\xaamG\xc2\xa8\xb60\xe2\xa8\xd7\x9d\"\xb5\x05\x11H\xb5\x85\x12I\xbd\x0e\xcf\x8b2\x88P\xaa-\x88X\xea\xf56EG[\x1a\x8ah\xea\xf5\xd2'\xa2b\x08\xa7\xda\xa2\x11O[\x87\xbb\x10P\xb5M \xa2j\x8bBH\xd5\x86'\xa6z]\xf5\x88\xab~\x82\xaa\xb6=\x10U\xb5\xed\x8b\xb0\xaam\x0f\xc4Um!\x04Vmh\"\xab\xd7S\x97\xe8*\xda\xb7\x9f\xd0\xaa-\x88\xd8\xea\xf5&\xc6 ,\xc1U\x1b\x0f#\xbaj\x0b%\xbcz\x1d\x86(wK\xdb\x99\x00\xab\xcd\xa3\xe2--`\xe9\xe8\xfd\\j-t\x95\x19D\x94\xf5z\xe3\x9aH\x8b \xccj\x0b)q \x81\xd6\xe9\xab\xbf\x9aE\x10i\xb5\x05\x11j\x9d\x9e\xbc\xba\xe0\xd2\xa6\x90n\xfd\xed\x13\xa5\x11.m\n \xd7\xe7\x90\xa3\xf4\xc2\xa5\xc5!\xe5jC\xf2K\xb5yI\xba\xda\x02\xc8\xba\xda\x9e\xe4\xab\x0dS\xf3 \xa4_m\x93\xc8\xbf\xda<\x11\x8fG\x06\xd6\x86$\x05k\xc3\x90\x83;\xd7\"H\xc2\xda\x90o%\x9c4\xac\x0dO\x1e\xd6\xe6R.\x97\x16\x85L\xac-\x88T\xacm\x17r\xb16L\xe8\x03\xc8\xc6\xda\xa2\x93\x8e\xb5\xa1\xca\xeb\xe9I\xe1dd\xa7\xbb\xb3\x1b?)Y\xdb\x14r\xb2\xd3\xa1f\x03\xb8\x15\xd0\xa5M!+;\x1dr\xbd\xe7\xe6PC\x97\x16J^v:k\x89\xcd\x88\xed\x0e\x04\x99Y\x9bK\xa4Y\x9aK+]Z8\xc9\xd9\xe9\xce\xa3\xa6\xae/\xc2\x13\xa1=\x0d\xa4C\x92v(\xabK\x0b%F;\x9d}\xfc\xf0\xfd\x01\x82 \xad-\x94(\xad-\x800\xad-\x988=\xb8\x11I\xa0\xd6\xe6[\x05x\xd5\xaf\xa5aI\xaeXbu\xe37\x94`\xdd\xdc\x18B\xb4\xd6\xe6 \xc6\x14\xe2\xb5\xd3!B\xad]\xda\x14\"\xb6\xb73\xf9\x95\xdb\xa5\x85\x13\xb3\xbd\xa3\xb8G\xc5]\x9a\x83\xa8\xed\xbco*\x89[[\xcc\xf6\x1e@\xean\\\xa3\xc9\xdd\xda\xec\x0b$\x97\n\xbc4\xb9\xff\xe3\xd2y\x97\x86Q\x84\x97\x16\xb2\x932E\x1d\xde\xddf\xcab\x8d\xd6\x88\x97\xe6R\x8a\x97\x16R\xa3I\xaa\xf1\xd2\x82\xb5\xe3\xa5!w\xf0\\:\xf2\xd2B\xd5\xe4\xcdw95\xe5\xed\xb7\xd8\x95\xe5M\xf7x\x1f2Ae^\x9aKk^]\x81\x8bwH\xa3\xd9U}^\x1aF\x83^\x1aN\x89^\xda\x1e\xaa\x1bK\x9b^\x1a^\xa1^Z\xb7\xa4\xd1*\xd4\\\xbb\x93f\xbd4\x84\xe0mt\xa6\x89W\xcb^\x1a\xb21@`\xfc \xa2\xba\xbd4T\x0c1J\xf7\xd2\xf6X\xef@\xed{\xaf\xbf\xcf\xd9\xf5\x01^\x01_\x9aW\x07_\xda\xd4(\xec\xaa\x89/\x0d\xaf\x8c/mji\x11\xef,\x9aV~\xdf\x9dO1_\x9aW7_\xda\xd4\xeaO\xd6\xd0\x97\x86S\xd2\x97\x86,\"\x96\xf8\x1e\xf2\x16\xc3\x15\xf6\x9d\xee\x12\xbe\x93\xce\xbe4\x8c\xda\xbe\xb4\xc0\xc0\xf92H`r\xf0\xb0*\xfcNwjL\x9f\xa2\xc5/\xcd\xa7\xc8/\xadW\xc3\xddu\xf9\xa5E\x9e\x86\xf1J\xfd\xd2B\xde\x1a\x84\xab\xf6{\xfdI';i\xf7KC\xc6\x11\x02b (5\x7fi\xc8\x0e\xa5-4\xec\x80\xd6\xf7G\xb9\xea\x9c\x01\x80\x13no\x0d\xa7\xf5/\xed\x16\x82\x12\xa2\xfe\x8fr(Ga\xcf\x19\x00(Oa1E\x9f\x16 \xed\x96B\x8b:?\x00\xe5M\x9f1\xe0?E\x00\xe5\x0e\x17\\\xd4y\x03\xd2\x02\x02\x1a2+\xc2\x84\xe0\x87\x9eC\xe0ux\x95T\xae\xd3\x08\xa4\xa1\xcf$\x90\x86\x8cWH\xdd#\x9eR\xd08\xc4\x9dU \x0d[\xd6i\xe7\x16X\xdd\xb5\xea\xf5NJv\xf8\x19\x06VW\xf2l\x03\xecI\x06\xd2\x82\xcf3\xb0?\xbd=\xe7\xc0}\xaa\x81\xb4Ig\x1b\xb8\xab\x8e:\xe1@Z\xe89\x07VG\x9d6\x8d8\xed@\xda\xa43\x0f\xec%H\x1b\xce\x00w\x9f| m\xda\xf9\x07\xce\xca\xb0\x85\xe3\x14\x04i\xd3\xceB\x18\xf5V!\x92\xd2W!i\xceAH\xab\x80\xa3\x10\x06\xca(\x1f\x8e\x0f\x87e\xa7S\x11z_\xd8t*\x82\xc1\xb0SKh\xb2\x9cl\xbft*\x02\" \xce\xc4\x1d\nN~\xc3'\xbd\xd1\xa9\x08\xdaB\x93\xd9\x9c;\x9e!IlA\xc9kt*\xc2\xaeIi\x13\x92\xd1\xa2$\xa1\xe1\x93\xcf\xe8T\x84\x18\xc9e!Ie\xe8d2:\x15\x81NE@\xaf\x92\x82\x92\xbd\xe4\xd7\x12\x9d\x8a@\xa7\"\xc4J\xc0B\xe6\xfex\x13\xae\x02\x12\xad0\x9a\xff!\x89Ut*\x02\x9d\x8a\x80Ix\xa2S\x11\x84\xed\x92\xb8D\xa7\"\x98a`\xc4\xfa\xeb}D\xf6Y\x83-\xffO\xfd\xbd9K\x80\x8f\xb5\xe6\xbb\xc3\xd2\x8eg\x0b\x80\x85\x1di;s\xc0v\xe4\x80\x88\x83\xba\xc3t\xbe\xc0q\x13\xa7\xde\xd9\x02\xca\x8d\x81*\xd7;M\xe0D^&\x9c\xe8/\xcd;{\x98@7\x14]\xeb\x15F^\xa4z\xa3\xd8M\x17+\xab&Hb\x16L+X\x17\x8bmf\xd8\x91\xb0\x96\x0b\xbcx\x16\x86$\xea\xdd\xce\xe9Uf\xc0\xe6T\x94\x0d\xc9\xecT\xddN|\xe9\xbbx\x98\xeb\xe4\xba\xa3\xfb\xeb*\x95K\xed\xdaO\xf4\xed\x15\xbc\xffP]p\xcdmj\x00\xfa\x8e\xa8\xb2\xad\xe8,\xe7\xa5\x13?\x8cZn\xeb\xd6X\xa7(\x9d\xda\x80\xfe\x93Xj\xa6b\xcf\xb1y\x19\x16O\x1d\xba\x82\x80\xa7;\xff\xfe|\xc3J\xd8$i\xf9\x88\x97iaL\x0c\xe8\x9c@rG\"3.\x91\x0eP\xfb\x9a;\xc3\xac\xbe\xc6J\xa2\xd9\xb0\xb2J+#\xd1\xbe\x0e\xecl\xc1\xf2\xc2\x92-\x17\xd6\xb9Zo=\x02^\xfdg\xb1[>/\xd2\x1c\xc4\xefin`7!X\x12rX\xf5\xb2#\x14\x1fB^-x\x10}_D\x83 \x1a\x84\x0f\x03\xc0\xf5U\xa2A\x10\x0d\xc2v%\xd1 \x84\x11\x0dblD\x83 \x1a\x84\xcd\x88\x06A4\x08aD\x83 \x1a\x04\xd1 \x88\x06!\x8dh\x10D\x83 \x1a\x04\xd1 lF4\x08\xa2A\x10\x0d\x82h\x10\x1d\x8b\x01I\x13\x0dB\x18\xd1 >\x15\x1aD0]\xa0(2\x07Y\xa0(\xb2\x1eM\xa0\xbe\xbc\xc7|\xe8\xb1\x03\xea\xcb\xd5\xdf\xef.)\xa0\xa9p\xd7\xfa\x94\x80\xba\x92]\x88oT\xeb\xd6\xac%\x00/\x8e\x93\x17|&S\xb1g.\xa5J\xcfN\xc6.\x1ez\xb5\x96\x08dQdh\xfc\xb1\x8e\xca\x87\xe3C\xc2\x1b o\xf4n\xb6a\xf6\xab\x80\xf0F\xc2\x1b\xadW\x12\xde(\x8c\xf0\xc6\xb1\x11\xdeHx\xa3\xcd\x08o$\xbcQ\x18\xe1\x8d\x847\x12\xdeHx\xa34\xc2\x1b o$\xbc\x91\xf0F\x9b\x11\xdeHx#\xe1\x8d\x847v,\x06\xf6Cx\xa30\xc2\x1b\xff\xaax\xe30\xb3\xd4\x84:\xfe\xd4&\x82j\xec1\xc9\xb2N\xee\xa7\xdeY\xe4s\xa9\xea\xbdL/Y\xae\x8e\xe53\x02\x93\xadG\xf5\xeb\x9d\x85']\x99\xb7\xfcO\x80~d\x18\x8br\x96,\x16%\xab,W\xa1\xf6?0[\x08`xd\x0f\x8a\xd5\x7f\x1b\x9e]p\xdf\xbe\x85\xa5\xfd\xfdO8c\xf3\x15\xb0|^,\xc4.\xa5\xe8\xfa\xe6)n^\xbf\xde\xbc\xdaV\xb3\xcd\xf6\xec\x82Y\x0f\x15\xf3D\x17\x10\x11\x06\x04\xc0\x06\xb8\x08C@\x94a\x02\xd8\xe6tf\xdeqq|\x80\xc4\x06\xdd\xc0\x0f\xbc\xc1\x04\xf0\xcd]\x81\x84\xaf\xd0\x00\x1c\xc4\x02\xe1`\"\x10\xe7tX\x07\x17\x0d\xc6\xc1\xee\x80\x1c\x04\x83rNW\n,\x08\x02\xe6 68\x07\x81\x00\x1d\x84\x82t\xee\x96\xdd\x00xX\xa0\x0eb\x83u\x80\x03\xec &h\x07;\x03w0\x0d\xbc\x83X\x00\x1eL\x02\xf1\xdc\xdd\xa1sB\x85\xa7\n{\x00\xf3`\x8f\x80\x1e\xec\x07\xd4\x83@`\x0f\xa6\x81{\xbe!\x18\x07\xf0A\\\x90\x0f\x02\x80>\x08\x07\xfb`\x02\xe0\x87\x182\xbf@\x80~\x10\x03\xf8\x03\x1f\xf8\x07\xf8\xe5\x19\x02\x04\x84\xc0U\\0\x18\xe8\xf4&\x80B\x04 \x08\x01\xa5\x8c\x08\x0cB\x108\x08\xb1\x01B\x98\x08\x12\xba\xdbU\xe5\x07\na:Xh\xf5W?\xd1\x07\x18B4\xd0\x10\xf0\xd8\x17`\xc0C\x08\x03\x10\xc1\xb7\xe3?\x11H\x04\x84_\xc7\xa6b$P\x11&\x05\x17\x0f.\x02\xa2\x96\x13@F\x98\n4\x82;\xaa\xf1\x00G\xc0\x83\x8e\x80\x04\x1e\x01\x0d>\x02.\xea\xe1 $\x04\x01\x91\xe0\x04#!\x16 \xa1\xa0$\xec\x08L\x02\"\xbc\x01\x00%\xec\x03\xa4\x04L\x19\x1d=!\x1e` \x18\xd0\x12v\x00.\xad\x0e\xeb\x0b]\xe0%\xc4\x060\xc1\x0bb\xc2T \xd3\xeaM~\xa3\xba?\xd7\x11\x80&8q\x17p\x02\x9b0 \xdc\xb4\xbar\x82\x9e0\x15\xf8\xb4z\x93\xeb@\xd7\xb9\x99\xd1\x00P@\x81\xa00\x01\x08\x8500\x14\xa6\x00\xa2\x10\x0c\x8a\x82g\xb6\xf5\x00U\x10\x00Va\x01R\x98\x02\x92B(P\n\xee\x8aO\x01L\xad\xce:p$\xb6\xcb\xe0\x80Sg\x87\x10G\xbb;\xc0S\x88\x0b\xa0\x82\x0fD\x057\x90j\xbdg*\xc0\n\x11\xdbn\x00\xd0\nA`+t\x00\xd7\xbe\xfd\x9a\xa4\x19[\xb8\xd1\xa9\xb3\xa2\xc8\x98u\xeb\xb8\xd9\x90p^\x85\xfd\xd2\x97\xe5Q\x88\xdd\x02\xaeVL\xed\xf7t\xcf\xea\xad\xfb\xc1\x19c\xb9\xba\xda\xde\x0e\xcab\xdd\x1cV+\xc0]\xb9)n\xd4z\x05u\x89-\x16\xd8\x1a\xa8\x07\x0dO\xbbV\x7f\xb7\xde\xf6\xb9,\xe7\xa3F\xcdW\xfd\x97\x00A\xcc\xb7 v\x8cX\xbe\xb5H\xd7\x82@\xdb_\xbf\xff\xf1\xcd\xec\xe4\xf4\xd5\xe9\xc7\x93\xd9\xc7\x1fO\x8e\xdf\x1e\x1e\xbd;z\xfb\x06}G\xfd\xaf\xc0\xcb\x8f~\xfc\x1b\xf2z\xa7s\x9d#\x1aT\x05W\x92+*\x9e\xd8F \x1f\xa4\x9a\xb1Z\x95\xb4gT\x8b\xdf>O\xf3yf_\x1eT,;\x7f\xd8J5[\x1aAsX\xf6L\x1e\xc6|\x0bU\x1b>\xb2E\xd7\x0b\x9ed\xed\x19\xd7\xd5V\"\x0f\xf6 \xab{\x12|\xe7\xd4oKE\xdb\xd2\xed\\\x81\xe6\xba\x1e3\xa0\xfbw\xce\xcau\x05\xe6\xc3\xa4[k*\xe0\xee\xa1;\xa3\xfb\xeb\"O/\x98!\x07\xb95\xc4\x0b\x86\x80\x18A\xfb\xd4\xce\xc9\xf8\xab\xed:\xc9\x1f\x96,\x91z\xd5b\xdd\xe7\x8b\x11`\xe2\x04\x8a'\xc0\xad< \xd8O\x1d\xf5c\xdbJ\xb6i\x8c\xed\x8fU\xba\xcc\x13\xbe-\x19|\xce\xae\xdd\x9f\xd2\x1f\x8f\x8b\x92\xd7\xb3\xcc\xdf\xd9\xcdYR1\xeb\xe0\x0dp\xc5\xce\xaa\x94\xc7\xc0\x04z5Vn\x8d5\xd2\xbfei~\xe1\x1az\xe6\xdb2\xe573\xf1E3\xe7\xb1K\xe8y'\xc3\xc7\x1bk\xc2\xd6I\x9ay\xa1t\xed\n\x94+{\x9d\xad\xe9\xf2\xadM\xa9\xaar\xab'\x02 ^5\x95P\xbf\x9aK\xd5\x1e\xc5 \xcf\x11\xd9ah\xf7+\xef\xe3\xdf\xd0\xb0X\xfa\xed<\x80\xb4s.\xc4\x031\xa2\xaa+\x1c\x94\x1b\xf9E \x00\xd0\xe1\xf2n\xb9\xcd\xdd\xe7L\xe0\xce\xbf\x08\n\xd0\"\xe1\xeca\xed+Z\x90\xe4\xd1\x19\xf6\x10\xad\xd3\\\x1e\xaf\xe1j\xca\xfd\x85$/\x04\x00\x9e1\xce|\x01\x9a\x17\xebuZU\xd8I\xb3\xbd\xbc71v\xfe<>\xddeh\x91\xa6\xbc\xf6\x99\xb32\xe1\xae+\xf1o\x05\x0c~{\x15\x15\x04\x95$\xeb\\\xe4\xf4%\x1dH\xe2A\x03\x96\xcfK\x96p\xf9U\x8c\x98\xf6P\xf1\x02d\xcc@\x95\xc9\xb7q\x8f\xe8\x10\xdaBB\x0b\xea\xf1\xfa\xd3\xa7\xd3l\xc4\x9f\xe7\xab\xa4\\\x8a5\xa1\xd7M\xbb\x1a|\x00\x82$~.H^\xc3\x131\x86\xb6N\xaeg\x7fr\x00t\x11z\xedJ\x1fC3\x88\x88\xd7\x99\x1c\x1d\xdb\x9e?Or`\x97\xacT\xa1\x0c\x8e\x8dd\xa5\xdc\x85\x10uJb\x8c\xd4\"I\x1d*\x12\xda\xd2\xbc\xeem\x15\x1b\x91y;\x91\x96A\xf2\xba\xf2\x07q\xbb\xa9g\x07\xe7L\x03\xf8\xc0ag\x1c\x08\x0cp\xa7\x94\xba\x1f\n B\xfc\xc1\xd4+\xaf\x9b\x9aB=\x00^,\xe5\x86\x96\x00\x1b\x04PP\x7f5\x1b]%\xebb\xdbP\x89\xcd\x1b\x90?u\xaa[/\x04\xf4\xd7\xb7\xca\xe2KK`\xd7\xf2\x05\xca\x97-\x96\x0ein\x99\xbcO\xb2\xa4Z\xd5S\x97\x16\xf9\xb0Q\x88\x93:\xac\xb2\xe7)\x0c\xab\xf7\x98\x07\x92x(xpEY\xda&\xb6y\x92\xcd\xb7Y\x03\xdd\x9co\xeb/,\xf3\x03\xb7y\xfbf*\x11\xbbb\xcb!\xe5\"C#_Bq)\xbeS\x9b-\x04\xf8y\xc5rYUs\x05\xca\xfe\x86\x8c\xf9\xa9\xfde\xe9\x83\xc1\x10\x93V\xf5\\\xbfH\xb9&\x87%\x9d\xe6c\xf4w\xb5*\xaa\xce!U\xe6\x87v_cZ\xf5h\x98\x9d\x16!\xaa\xd6V\xc1\xe8i\x9168\xad\xdcl\x17\\B\xf3S{\xef\xef\x00~*DX7\xc5\x15\x93\xb3\xce\x19k^\x17[\x08\xec\xd5\xdal\xf5~\xab\xa8\x82\xf9i\xebm\xc6\xd3M\x96\xca\xc2\xf5\x9f=\xba\xa1\xd7\xeb:)<-\x1bL\x9d\xb9'S}\x16`?\xe2m\x93,\xd5\xc9Z\xe3Q\xaa\xf7\x98\xf6\xc2\xbe\x8c_\xfbg\xd5\xeeM\x89<\xd2\x9c\xeb:\x8f\xae\x1f\xbb\xe63k\xce\x88w \xf52\x04y\xca3\xf6\x12\xfe\xd36\xc2\xea\xe7\xebA\xb5\xfeOE\xafM\xaaJ\xee\xea\x1d'K\xf6\x81\xfd\xb6e\x15?\x90\xbf[\x9c\xb5G_\xd6n\xeb\x102X\x17\x15\x07&H\xaa\x82\xddj\xb8U\xb4\xaf\x1d\x03\xb0\xb5\x7f\xef\xaa\x10X'\x19\xb9\x97\x99\xeaM\xcd\xf6`9=4v\xc4vl\xfc\xb8n\x88\xe6u\xc7\x9d\xc9Nc\xb9\xfc*\xa9\xa7,\xfe\x00R^iVx%F> \x85\x88\xbd\x83\xab\xb4\xea\xbfS[ED\x1a]\x9b\xb5\x86\x95d\x1c\xe5\xb9ike\x1a\x9b\x1fH\xa5\xb1\xff\x1b\xa94\xe2\x96H0!qL\xb6URiD$\x89\xf1\x18 bS\x92\xc3H\xa51b\"XH\x12XP\x02\x18\xa94\xee\x9a\xec5!\xd1+J\x92Wx\x82\x17\xa94\xee\x92\xd0\x15\x92\xcc5!\x91\x8bT\x1aI\xa5\x91T\x1a\xb1\x89XQ\x93\xb0\xa6$`\x91J\xa3\xed2o\xa2U@\x92\x15F\x830$\xb9\x8aT\x1aI\xa5\x11\x93(E*\x8d\xc2vI\x86\"\x95F\x93'o\xc2\xd3\xd4d'\xeb\xdc@*\x8dc#\x95\xc6 IJ\xfe\x04\xa5\xd0\xe4\xa4\x80\xc4\xa4\xe0\xa4\xa4\xb0\x84$Ri\x0cK:\"\x95\xc6\xc6\xfe\x92*\x8d-\xef\xaf]\xf4<\x14#\xef\xcbqbK\xef#R\xa5\xc3\xb0<9\xcb\xe4\xc6\x8bD\x04\xeb\xb0u\xd0\\\xa1\xc5(is&1F\x103\xcaKyo\xe7o%\xfbm\x9b\x96l\xf1\x12\xce\x93\xac\x07+\x19\xbf\xd4u\x91[ \xf7\xe0\x82\xdd\xd8\x8a>\x00H\x15\"\x9a\xa8Q\xbfd|[\xe6R\x07PB}\n\nn\xe0S\xb1{\xb5\x1cl\xf3\x88\x1a\xd4\x15uC\xa2\x07\xf0\xbe\x9e\xa3\x8b\\|\xde\x16\xe7\xe7\x15\x13\xb4\xf2~q\xa1\xb3\xfb^1\x1e9Z\x96\xbd\x0cC\x10e\xf9lq\x1c\xec#\xa8\xca\x88P\xe6\xdb5+\xd3\xb9\xfe\x9b\x18 \x14\xdf@n\xe4\xacX\xae\x03\xbf\xcd\x9b\xbd\xb3\xc1\x8a\xf9Hx\xcbXU\xb5!\x94\xbbM\xdb\xaa\x0e\xf5\x05\x0b\x8cg\xdf\xfd\x9e\x83;\x80\xa9\x0d\xe1\xcd\xd2u\x8a\x8d\xae\xb8V#\xf76\xf4Z\xee\xabv[\xb0\"3l\xb3\x01\xde*wQ\xba\x7f::\x87\x8c\x9ds\xb5a\x97r9\x82\xebu\xae\xd8\x12\x96\x1dD>\xa4\x8e\xf3\xd9\x0d\xb0d\xbe\x82d\xb3\xf9\x13\xa3\xd8\xc5\xe0\xdb\xfb]\xb1\xec\xdcQGT\xb4\xd0\x02x\xb9eP\xffG\x9a/\xd2\xb9\xa0U)pHEP\\\xa8\x1aR\xd7]\x9a\xcf\xb3\xedb\xb0\x8aM\xe4S\x1atn\xf0\xc6\x04\xd6\xdb\xd94\xae\x87\xcd\x1e\x0d\xa5\xe7\xec\xe3Q5x[\x83*\x88\x85\x7f\xc9*\x05\xca\x8b\xee\xd5\xf6\xc7\xba\xcb\x1d\xa8\xde\x94.\xf3b\xc8\x9b\xd3\xbd\xb1\xff\x08\x19\x99]_\xec8}\xd4\x96Xjx\xb5%\xbbde\xcf\xa9\xeb\xb5\xaa\xab\x87\xafT\xbfN \xf6=pw\x96\x9e\xc3\xfaaL\xd0\xf0\xa1(\x17\xac\xbc\xadX\xd8\xb4\x93\xef\xfb\xc5\x93\x1f\xfd\xd1\xfc\xb7\x90\xc9\xfd/\xa5\\\xec\x94Sn\xd4\x94;,\xb8\xfc\xbc\x10\x8dR\xce\xda\xed\x0fJgW\x87\xc2\xac\xa6|O\xc7\xe3\xae\x8b)\xdb\x08\x1a\x93\xf8]8Id/\xd4\x80\xd9\xab\x8f-\x86\x1c.\x85\x8c\x13BvF\x13\xbc\x11\x05\x04{\x05\x11Q@G\x15\xa6rX\x1c\xfe\x82\xa4\x8f\xa3\xf2X\xbcL\x96\xd8\\\x16<\x9b%\x12\x9fe\x1a\xa3\xc5\xe1.P\xecxGVKl^K \xb3%2\xb7%\x8c\xdd\x12\xc8oq\xb5\xe1\x86\xf9\x82e\xb8D\xe6\xb8\xa0X.\x11y.\xbb2]&q]\"\xb1]\xa6\xf0]\x1c\xce\xd0r\xc6{\xe0\xbc\xec\x8f\xf5\xb2\x17\xdeK\x18\xf3%:\xf7\x05\xcb~\x89\xca\x7f\xc13`\x8290\xe1,\x18\xefP\x88\x13.\xde\x99 \xe3\x15-F-\xa8\x10|\x98\x90UW0'\xc65 \xa2\xa5\x8aq\xe5\x8b\xc8\x8c \xe1\xc6Df\xc7L\xe3\xc7\xb8Z\x10J\x9ex\"G\xc6\xe2\x8d\xa3\xa4\x89\xe3\xf0d\xd0d\x0f\x04W&\x88-\xe3S\xf6\x9c\xc2\x98\xf1\xf9\xb4\"g\x91x3\xe1\xc1\xc4sg|u\x9b\xc0\x9f\x99\xc8\xa0q!\x90\xd1X4h\x1e\x0d\x8eI\x83\xe5\xd2 \xa2\x1c\xce\xa7 a\xd4\xb8\xc5\x86\xa3\xb0j\x02y5\xbb1k|\x01\x0d`\xd7\xec\x81_\xe3-\x9d\xb5\xa5\xc7c\xd9 x6\xd3\x996\x16w\xdc+*\x1c\x95m\xe3\xe3\xdbLd\xdcX|\xf9\xc5\x84\x11\xac\x1b\xb7\x90\xb0KF86\xf7&:\xfb\xc6\xce\xbf\x89\xc9\xc0\xc1pp\xc2Y8A<\x9c L\x9cP.\x8eG\x1a\xd8]:,;\x02\xcb\xc8\x99\xc0\xc9 d\xe58\xaa;\x85\x99cq\x85\x10\x03\x9e\xc2\xceq4y\xbf\x10pD\x86\x8eW\x04x\x1f,\x9dXm1\x80\xa9\x13\xc2\xd51K\xfc\xba\x04~\xb9\x05ml\x0d#\xee\x8b\xfb6\x9e\"\xec+\x04|-\xfe\xb0\xb2\xbe.Q_\\\xc9' \xfa\x06\xcb\xf9z\xf7R\\R\xbe\xa1B\xbeA2\xbea\"\xbeh \xdf \x02\xbe.\xf9^o\xfcp/{W\xe1^\x8cl/N\xb47R\x85b\xc9\xf5\xe2\xc5z\xbb\xe5\xda\xa9\xe0\xcdU;\xc9\xf4zT\xf8\xf8\xee\xc8\xb3W\x9e\xd7\xfb*\x01\x1d\x15\x88(\xcc\xeb\xd5'\xf4\x8b\xf2F\xaeYd9^\xb4\x18\xafW\x8a7\xbc\x9e\xbb\xca\xf0\xe2Ex\xc3\xcb\xe6|\x07\xd1\xe4w\xb1\xe2\xbb^\xe9\xdd\xf0\nN\x96\xdd\xc5\x89\xeez\x0b\xe4\x17\xdc\xc5\xbd\x8f\x98b\xbb\xbbH\xedb\x84v\xd1AqK\x1e\x86\x06&D`\x173K\x00Z^\xd7'\xae\xdb\xab\xc9\xee\xd2\xba\x11&*\xbc\xa8.\xee-@\xb8\xa0\xae\x14\xcdu\xf8\xdbIN\xd7\x1b#@\xc5 PB\xba\xde\x06\xaf\x0d\x1fL@K\xe8v%r=\x0e\xd1\"\xb18\xf9\xdc=U;D8W\x8ee\x1e\x871ds\x83Ds\xf7\x18\x18\x94\\n#\x87\xebq\xe8\x15\xcb\xf5\x86\x06%\x85\x8b\n\x07nN\x80\xa0\xa0E\x96\xc0u\x08\xe0\xa2\xe5o\xbd\xb1\xc0\xd5.\xa2\xf0-Z\xf6\xd6_\xb2i\x92\xb7J\xcc\xd4\xe0\xcf.x\x1bS\xee\x16)v\x1b,u\xdb\x95\xb55W\xce.t\x1bW\xe6\x16#r\x1bW\xe2\x16!p;I\xdeVK\xd9\x9a\xfcy\xc5m\xa7I\xdb\xaa\xfdE\x83?\xbb\xb0-Z\xd6\x16'\xda\x19\xac\xd99\xf0F\x92\x9d\xc2H\xb2\x93$;[#\xc9N\x92\xecl-jZCHRCPJ\x03Iv\xee\x9a\xc80!\x8d!J\x12Cx\n\x03Iv\xee\x92\xba\x10\x92\xb8\x109m\x81\xa3\x92\x16\"\xa6,`\x13\x16xX\xbaBh\xb2\x02Iv\xf6,8=\x81$;QI SR\x12H\xb2\xd3v\x997\x0d! \x01#H\x19\x92\x80@\x92\x9d$\xd9\x89I3 \xc9Na\xbb$\x16\x90d\xa7\xc9\x937\x95`j\"\x81un \xc9\xce\xb1\x91d\xe7\x84\x84\x01\x7f\xba@h\xb2@@\xaa@p\xa2@X\x9a\x00Iv\x86%\x06\x90dgc\xfbH\x06\x88\xd1\xe6\x02\x12\x01\xf0i\x00\x18\xc9\xce\xbe\xf6W\xc7U\xefc\xb2\x7fU\x8fx0\xd2\xf9\xea {v\x07\xa7T\x1c\xa7\xd8\x1b\x11[\xf93^n=\x12\x7f1%\xce\x1eu\xd0U\x8c\xdc\xd9\x9b\x0e\x18\xab\x95\xcf4&i\x15>\xd3u7\x0b\x9eu\\\xde\xd3\xc1\xb8\xa3\xdagm\xb0f\x862j\xe3\x7f\x02\xe8\xe5c[x\x1f\x00\x88\x87@\x8f;\xefTj\x93\x86\xd8g\x02\xe4vM{\xed\xe0\xf9\x9a\xedq\xc6\xe6\xabgO\x1fj\xf9\xb5V\xc6\xcd\xe9\x8e\xb7\xd9\x0c\xaeM\xeb~\xaf\xb9\xfdZ\x8f\x9e\xbf{\xad\x11\xd4AW*\x86\xb4)U\xed\xe5\\t_\x80\xd8\xde\x94?\x96l\xce\xd2K\xdb\x11\xdb\xf8\xd8\xb5cK;a\xab\xc0\x15\xb9\xda`U),\xabz\x95yv\x03\x0e\xb9\xacd.\xa4C\x95\x1c\xa8}\x1e,\xaer\xf9\x8dU\xe4\x9d\xd6\xa5\xf6\xb5\x05\xb2S\xcc\xd3\xa4a\x90\xb8\xd0\x8b\xcb.5\xa38\xaf=\xda\x9f\xeby\xa5gI\x96\xe4s\xcf\xa6p\x84\x01\"/\xacIY\x80m3\x92\xf7\xb2\xa3\x1bl+9,\xd2\xbcC\xd4\x12\x0d\xa2%\xf7\xe4\xc5ZK\xcb\xd6/0\xc9U\xe1\\\x88\xc1\x8f\xefO\xdf\xbe\x14\xdf^\x8a\xc0#?bR\xb1\xcf|\x94s\xb5\xbck\xf6\xf6+g#Pk?E\\\xb1wW\x9d\x03R5\xcb\x89\xba\x11.\x8be!\x16VSw\xca\xdbN\xd4\xa5\xdf\xd4O\xb8L2!+]t;\x1a\xbb\x9e\xb3\x8d\xd4\xad6\xbaKyg\xd7\xdd\\\x1b\xd5R\x87\x0b]58\xa8\xd8U\xb0.J\x06\xd56\xe5Z\x06\xd8\xe8l\x9e \xed\xebf\xc2\x1e\x86\x81N\xc0\x16F'`\x7fr'`\x8f\xdee\x9fL\xd7Yx{yu&G\x8f\x8c_\x05D\xb1\x13F\x14\xbb8s3Q\xec\x88bg6\xa2\xd8 #\x8a\xdd\xd8\x88bG\x14;\x9b\x11\xc5\x8e(v\xc2\x88bG\x14;\xa2\xd8\x11\xc5N\x1aQ\xec\x88bG\x14;\xa2\xd8\xd9\x8c(vD\xb1#\x8a\x1dQ\xec:\x16\x83\xeeD\x14;aD\xb1#\x8a\xdd]\xa2\xd8\xd1\xb1\xd8S\xcf\x1c\xa6c\xb1\xf7\x18\\\xff\x81\xcet,v\x8c(\xd2\xb1\xd8t,\xb60:\x16\xdb\xc3\x19\x7f\xf4G\x9f\x90\xeb:3\xbb\xc3\x11CS\xc7[\xfa$l\x92\xd4\xcc$\x7f3\xd4\xdf\xfaW\xe2\x8f\xdb\x08\x1d\x938b>\x16\xb8\xd35x\xddC\x18\xff\x1b\x01\x81\xe0p\x04}e(\xf3\xdb\x03Zzy\xdf\x01\xac\xef\xc8u\x0d\xe7{{\xea\xeae{\xfb\xb8\xde\xe1\x15\xdc\x9d\xe7\x8d\x8bWL\x8e7\x8e\xe1\x1d\xce\xefV\x9b\xc2V\xe6\xfeU\x833\xe751g\xcfe\xbe5\xea\xd2\xa3\xf6\x04\xe5\xf62\x07\x9f\xa9C\xfa\xfd\xa8\xef4p\x9c\x1a\xaf\x08\x92S\xe3\xd0Jv2&\x15j\x7f\x0b\xb3E\x04\xb1yb\x1e\xa6\x18\x9a+\x16\xb5\x86Q\x19c^U*'k\xcc\xcf\x1b\xf30\xc7\xbcML\x9a\xaf\xa1I\x0b\xe0\x8f\xa1^\x884,\x87\x0c\xff\x02\xa5\xc5\xe5\x91\xe1\x98dA\\\xb2 1\xc2\xf0\xc9\x82\xe3\x14\x8fS\x86c\x95\x05\xf1\xca\x02\x82\x14V\xedh\xec\xb2\xdd\xf8e(\x86\xd9\xde\x82\x80\xa5\xa1Ex\xeb\xf8\x82E%\xaa\x85Q\xd5\xa2\x92\xd5pu\x8eFXCQ\xd6v#\xad\xd19\xd2\xca\xe8\x1c\xe9O\xe7\x1ci\x04\x13\xd3\xf9\xf5\xe2$g\x0e\xbc\x19\xf8>\x83S\xa6\x8d\x9faD\xd3\x94F4\xcd8_8D\xd3$\x9a\xa6\xd9\x88\xa6)\x8ch\x9ac#\x9a&\xd14mF4M\xa2i\n#\x9a&\xd14\x89\xa6I4MiD\xd3$\x9a&\xd14\x89\xa6i3\xa2i\x12M\x93h\x9aD\xd3\xecX\x0c\xca\x1c\xd14\x85\x11M\x93h\x9aw\x93\xa6I\xe7M\x87\x1d\xe6K\xe7M\xef1\xb8\xfe\x93\x92\xe9\xbc\xe9\x18Q\xa4\xf3\xa6\xe9\xbcia\x9f\xfcy\xd3\x9a\xc2\xcf\xaf\x1b\xf6~\x95\xae\xb7Y\xc2\xd5\xce\xf6\xa6\xa8\xc6\xa4\xfc\x13u \xe8k+`\xd7l\xbe\xe5u\xbd\x12\xe0e\x92W\x89\xd8\xbc\x94\x1fs\x15O\xd7\x89\xf8q\x99\xd4mG\x0c\x15\xd2g\x8fz\xaf\xfd\xde\xd3\x95\xbf\xa3,\xfbeR\xcd\xd2\xfc\xbc\xf0\x10\xcb\xf4ez\x8c\xad\xff\xbb~9\xe2\xc0\xd3\xb3b\xcbU8\xdaqU\xc5\xd3\xc8N\xb4\x96\x13\xbc\x8c\x8d\xba WI\xce\x99A\xf5\x150`\x05\x82d\x85\x01\x04\x00\xfe\x96T?\x8b\x82\xe8\x98\xac\x93\xebt\xbd]\xc36O\xb9\xd8\xc9\xbe*\xca\x0b\xb8R\x88\xa5\x04\xca\xf8\xb5\x9dq\xb6ae]8\xd3\x87i]\xeb:\xb8\xb7T\xe7\xbf%\xd5\xc7\xaa\xad\x98:\xa0\xb68\x17/9\x99s\xc9-\x98\x17\xb9\x02\x9d\xfb\xae\xe4\x10\xe2iPj\xd2H\xab\xee\x14\xa2A\x8d\xfd4\x9dE\xc2\x93\x1d\x03h\xc5\xb7pM\xe6M\xc2\x13\xb1\x04\xccoDi\xda\xf1\xf5\xbc\x14\xe7\xf7\xca/*\x81>\xe7\x8b\xcc\x02 \x81\x1e\xa1\x8a\\\xcc[?|<9u\xc0\x83\x19\xcb\x97|\x05\x9b\x92\x9d\xa7\xd7\xb2\x7f\x8a\xf1\xba\x1e\xe2+V\x7f\xe4p&K#\x0b\xb1\xcdx\xba\xc9l\x80\x9a.cS\x04#\xa2\x98\x15\xcb\x89\x91\xc6\x05\xf2\xfbb\xd9\xdf\xd5\xc9\x8aeoL\x9a\x1aO\xc3\x05\xec\x92\xe5\xfc\x0e\xeb\x0b\x0b7\xd6_\x11!\xaf-\xe1\xbcL\xcf\xb6\xdc\x9d\x8a\xe2\xab\xae4Oj\x0c\xe0\xaa.\x0d\x13\x00iV\xdeq\xd7P\xb1\xd0\xe6\xed\xf4\xad9\xf1\xf3\xd6\xf6\xf5\xf84_\xb0k\xec\xe3\xc7\xeb,\xb3\xd9V\xa2&\xc3\xf5Zmo\xeb\x1e\xf5J\xb78\xf9M\xac\xa8\xf1\x17\xec\xe6\xa1\xfc\x90\xda$i\xe9\xda\xcf\xa9mx\x1c~\x92\xcb\xde\x8aJ3p\x14S\x14PN\xdbU\xfd\xfd\xa6 H\xb0`\x97,\xab[\xa4X '\x9c\x8b\x0f\xbcf\xf7\xd9\xea\xb0;4q\x07r\xa5\xb7S^\xb3e\x9a\xbf\xce\x8a\xf9\xc5\x83\xe6oo\xf3\xc5\xe0/\x87+6\xbf8\xbd\xb6w\xea|\xd1\\\xfb\x86e\xe9%+O\xaf\x1d\x08\xe4\xf7 g\xe5\x83\xee\x9a\xb7\x82urS\x7f\x08\xc8\x94\xd4\x85\xdaZ\xe0+V150\x9ac\x8d\x8b\xb4\x88s\xd5\xa1U@\x95\xa5s\xb1Y!_\x81\x1c!\x14\x13\xf1\x8a\x95\x0c\xd8:\xe5\xdc\xca\x8aZl%\x97U\x0e\xfc\xb6\x9a\xb6\xf3\x81m\xe4w\xed\x88A\xb3\xb6\xefr\xd0y'\xa9B\xee*\x9bE\x82OXy\x99\xce\xd9A\xe3\x83H\xe6\xc2\x88dN$\xf3\xd6\x88dN$\xf3\xd6\x88d\xce\x89dn6\"\x99k#\x929\x91\xcc\x89d\x8e\\%\x11\xc9\xbc1\"\x99w\x8dH\xe6D27\x18\x91\xcc\x8d\xd7\x10\xc9\x9cH\xe6\x16#\x929\x91\xcc\x89dN$\xf3\x8e\xc5 \xfc\x12\xc9\\\x18\x91\xcc\xff\n$\xf3\xb3b\xd1\x9d\xfb\xd2|\xf4'+\x11\xdc\x84\xfd\xfc\xf7\x92\x9d\xbf\x84\xfb\xff\xedQg\xe3P\x91\xe6\x0e\xf8\xf5\x81\"\xcd\xb5\xc8\x94\x94\x80\xba\xaf|\x0ciw\n\xc82\x13\xef\xf8\xb5\xba\xd6\xa4\x83\xfb7\xc6O\xaf+ \xf0\x9d3>_\xd5\x83\xfcu%\x08\xb3]\xf4\xb6\xc7\xa7\xeb\xdc\xa4~\xbe\x1dJ\x1d2h\x9d\xe2iT\xf0\xfe\xbd\xb6\x14\x84\xe7u\x8d\xf0<\xdc~\x10\x10\x9eGx\x9e\xf5J\xc2\xf3\x84\x11\x9e76\xc2\xf3\x08\xcf\xb3\x19\xe1y\x84\xe7 #<\x8f\xf0<\xc2\xf3\x08\xcf\x93Fx\x1e\xe1y\x84\xe7\x11\x9eg3\xc2\xf3\x08\xcf#<\x8f\xf0\xbc\x8e\xc5\xc0V\x08\xcf\x13Fx\xde_\x01\xcf\x93 s\x1d\x17\xbd\x8fH\xf9\xab\xce%\xcb\xd2J\xeeyw\x04(\xc4\x15\xa3/\xd2)Z\x1bC\xb8\xc4\x00\x94X>\xe0\xe7E\x961Q\x9cw\xea\xf3]$\x8c\x8f\xeaJbSaJ>$6\xb5\xc7\xe0\xfae\x92Hl*F\x14Il\x8a\xc4\xa6\x84}2bSm,\xc4cgg\xf6\xa9\xec\xe9`\x13\xf8!\xbc\xff\xf0\xe6\xed\x87\xd9\xeb\x7f\xce>\xfexr\xfc\xf6\xf0\xe8\xdd\xd1\xdb7/\x8d\x7fm6\x9c\xd51\x8brQY\x15%ok\x0c\xef\xeb\xffy}\xa3\x07\x02\x11\xb6W'\x872\\i\x05\xf3dt\x9e_\xa7\x0c\xafN\x0e_\xf6\xfe\xd5\x1e\xec\xd8\x0f\xad\xd5\xc3\x9b\xb7=\x17\xf5?\x1b\x1f\xc3\xf7\xb3\xe3\xeb\x19\x8dA,\xdf\xae\xfb\xeb\x13sxm\x97\xbc:9\xb4\xfdT\xd7\xa3\xf7R%Y\xc7\xe5\xde\xce\x85\xb2\xa8\x8c\xbd.\x8bd1O*~z\x0dg\xfa\xbf\xbbk;#\xe7\xa9s\x97\xfa\xf9v8O<\x9cT\xc4\xaf\x9bS\xbam\x14\x9dI\x9aM\xae3\x8f\xbd\x10S\x87\x83\xe4>N\xf1t\xc5\xe0,+\xe6\x17\xeay\x86k\xf9\xf5*\xa9V\x13\x0b\xd2{%\xf5\xc3\xba\xab\xfa\xda\xafi_a^,X\xb5Ilg\xd4z\x1f\xaa\xeaV\x7f\x03\x0b7Z\x90\x02\x0e\x8b\x85\xe9k\xce\xcc\xf2\x02/\xd3\x0bP\x91\xee\x85\xa0\x11\xce\xa8\x9fiD\xd7\xa6Ku\xf5\x1et\xff\x83\\.\xd4\xab\xeb\xea\x81\xde\xa0\xbeo\xb8\xb1L\xaef\xfbV\xad\xaa\xdf|\xb1\xe5\x9bm\xb3\x16\xe9h\xcb\xdc\xaf +\x96KV\xc2\xe7er\xa5\x1e\xf6\xc5\x01\xfc`\xd5Z\xb2\x83\xbby\x91?\\\xd4\x1f\xa1\xeb4O+\x9e\xceM1\xce\x8a\xe5\x1dV\xb5ZW\xcb\x99W\xd6\xc8\xdf0\xa5\xf9\x9b'\xb8D\xcb\xa4y\x1b\x01x\xc4\xc2\xa4\xf9\x82+\xed\xcf\xd1\xd0\x12.=\xd7 \x03!\x0d'+&\x0d\x17\x18i\x88\xf0HC\x07I\x1a>T\xd2P\xa2c\xd2\x02\xa2&\x0d\xa9)&-\xd0;n\xbc\xea[+\xd8\xd59\x11\xbcy\xc1pU&\x9b\x0d+\xeb/\x98\xd2E\x16j\x8d\xab#\x9a\x93|\xa1\xbeg\x93\xd2\xc1W\xe9\x9a\xach\x05i^q\x96,\xc4\x87wr%\x87y\x07\x88\x15\\\xf3\x13\xf1\x1cIU\xd7\xb5fyO\x9c*\xa8\xe2\x82&Y\x8f\xfbM\xbf\xf0\xd5W\x93k/\xd8\xcd\xa3V\x18M\xd1,\x93\x92\x0dC\xe1q\x87\x0dTH\x98\xc2U\xbc\x9c\xee\x94\xc2\x97\xd6\xf2\xaa\x8a\xb5\xa7Q8\xd5\x1b!\xa02\xaf^\x1f\x1e\xfd 7\xa8\xbf/\x96m3\xafc\xbc\x9d\xf3m\xc9t%\x858p.u\xf7\x1cl;~-|6\xbb\xdeY\xb14\x97\x11WB\xec\xfa\xa1\x1e\x0c\x16r\xe9P/\x12\xcc\xcePk\x04\xb320`\x06\x9c\xfegH\x8b\xb5u\xa4\xf0t qe\x89\xa6\xfc\x8b\\\xa4\xbe\xea\xa9\xde\x962\x1bF\xf3\x04\xc7_n]\x8b$\xd7;\xa9\xa0Z\x95\x17\xcen|\xc5\xe4\x96\xc5\x95w\xd2\xf4O\x93\xdc\x93!\x81\x88\x02\xa0\xfb\x05L\xcd\x93p\xf8\x1bQ\xe5\\cP\xd4\\ o\xb6D\xec| |\xc6D\xa4\x9c\x89iY\x13\x0ewu@\xd1y\x13;gN\xc4\xce\x9d\x08\xcc\x9e\x88\x9c?\x11\x96A\x11\x98C\xe1j\xc3Mv\x056\x8b\"r\x1e\x05*\x93\"b.\xc5\xae\xd9\x14\x93\xf2)\"eTL\xc9\xa9p8\x13\xd9\x16\xfe\xac\x8a\xbd\xe4U\xec/\xb3b/\xb9\x15a\xd9\x15\xd1\xf3+\xb0\x19\x16Qs,\xf0Y\x16r\x19\x11\x90g\x11\x9ei\xe1\x1d\n\xbf@\xe4ZD\xc8\xb6\xf0n\x08\xa0\x16T\x88\x9c\x8b\x90UWp\xde\x85k\x12<+.\x19\"\xf3\x02[\xbe\x88\xd9\x17!\xf9\x17\x9130\xa6\xe5`\xb8ZP\xe5\xcf\xc2\x98\x9c\x87a\xf1V?\xcd\x97\x89\x11+\x17\x03\x9dP\x80\xc8\xc7\x08\xca\xc8\xf0\x10\xa8'ee\xf8|Z\xd9\x99\x91r3\xc2\x83\x89\xcf\xcf\xf0\xd5mB\x8e\xc6\xc4,\x0d\x17\xcb5Z\xa6\x06:W\x03\x97\xad\x81\xcd\xd7@D9%Q;\x12\xb5\xc3\x80\xa4$j'l\x17X\x94D\xedL\x9e\xbc@\xe8T\x18\xd4:7\x90\xa8\xdd\xd8H\xd4n\x02\xdc\xe9\x07;C\xa1\xce\x00\xa03\x18\xe6\x0c\x039I\xd4.\x0c\xd6$Q\xbb\xc6\xf6\x01e\xc6hs\x010&\x1e\xc4\xc4\x88\xdaE>\xa4\xca\nb8\xb5GD>k\x10\xf4\xe2\xf9R\xef} \xeb\x074\x10Zr\xe5J\xad[;\x90%KY\xc6\xf26\xd2\x1e\xc2\xeb\x0f\xef_\xbd9|ur:\xfb\xe1\xfd\x9b\xb7V\xa9\x1b\xcb\xe5\xaf\xbf\x7f\x7f\xf8w\xcc\x85'\xff\xfc\xf1\x10s\xdd+\xe3\x85\x8dfN@i\xfd{\x0d\x0d\xfa\xf8C\xb1`\x1d\x85$\xb1s\xdf\x88\xe8\xd4\xd1\xb6\xc0\x98 p\xdd\x11\x98 \x1f\x8e\x0f\x1b8\xd3\xd4\xf6]Q\x7f \xbf\xb3\xb2x(\xa7~\xd1\xf9\xeb\xe7\x0b\xcd#\xcb\xb8e~)\xa3X\x89\xbfv\xb0g\xdeQ\n\x12\xa8T\xfd\x1c\x91c.\xc7\xb5LH\xf0]%)\xaf,P\x82\x98\xe3\xaf\xd5.\xea\\@\xed\\\xaak%\n\x83G\x15\xb7~\xe3\xa3\xd2\xd6\x7f\x8cZ\xd8\x04\x0eWl~qz\xdd\xa6s\xb7Pu\xbd`\xc5\x15\xf6\x95\xb1\xb4\xaf&\x15W\xea\x91\x99\xf6|\xd2\xb5`Cp6,\x96\xbbQ\xf7Py\x91N\xdc\x82\xf2\xf2\x9f<\x08\x93WN\x86m\xb8\xdb\xbc\xe5_\xeczU\xf7\xcd\x87\xf7=\xfac\x95T\xab\xffR\xbaR\xd6C\xfc\x9a\xe3\xfbdHozZF\xe3\xe3\xfb\xd4\x0f\xb7#b\x15rp\x1f\x1d\xd9\x07\xc4n\xf0n\xed\xfbg,i\xc4n v\x83\xd9\x88\xdd \x8c\xd8\x0dc#v\x03\xb1\x1blF\xec\x06b7\x08#v\x03\xb1\x1b\x88\xdd@\xec\x06i\xc4n v\x03\xb1\x1b\x88\xdd`3b7\x10\xbb\x81\xd8\x0d\xc4n\xe8X\x0c\xa4\x99\xd8\x0d\xc2\x88\xdd\xf0W`7\xac\x92\xaa;D\xf5\x0f\x97\xa8\x7fl\xce\x01\xbb\x16\xd7\xd6\x91\x17\x87\xdd<\x00\x96\x8b\xb4]\x10Ml\xc5\xaeU/\xee\x9d>\x91\xe6/\x87\x1b\xe3V\xb6\x84\xe1\xf3\x1c\x81\xebm7\xcb2Y\xb0\x06\xdc\x13\x8a\xd1l1\xdbdI\xfe\xe8\x8f\xba\x92.\x94\xef\x95\xbc\xfa8KrQ-q@Q\x93S\x9e\xdd\x80r\x07\xea1P\xbb\xad\xd7\x11)\xafD\x04\x8d``\xc7\xeb=]\xe9\xdb\x80\x04y8\xf2f;\x7f\xc6\xb9W\xe2\xd6o\xee\xd5C\xe5\xb7\xabF\xd4=\x83\x06\x12\xae\xba{\xfd\x8b\x88\xebU\xdd\x94d\xecB`\xe7\x7f\xd4\xed\xb1\x13\xf3)\x19\xe1\xc2\xc7\xa3ns\xf8p|8\xec\xf3\x94\"N \xaao\x07\x11\xb3 \x07\x04\xa2\x12\x88j\xbd\x92@Ta\x04\xa2\x8e\x8d@T\x02QmF *\x81\xa8\xc2\x08D%\x10\x95@T\x02Q\xa5\x11\x88J *\x81\xa8\x04\xa2\xda\x8c@T\x02Q D%\x10\xb5c1\x00-\x02Q\x85\x11\x88\xfaW\x00Q\xeb\xff\xdfq\xd0\xfb\x84\x14=Q\x01Q\xddMh\x0d,\n\xe0K\x83\xaa\xf5\x14\xd1\x1d\x8a\"\xc3\xa7\x02\xe4\xba\x07`\xc3N\xd5\x1e\x8d\xc0N\xed\x98\xe9\xa1\xbc\xaa\x87\x99\xcaf&\xfe\xde\x83J\x8d\xf0h\xc7\xc1=]\xa9;\n\x8f\xb6\x91\xe8Z\xaf(\xe2\x05\xa6\xfe\x10\xb4f-\x07x!*\xd1\xde\x8cm\xd5\xbb/\x83\xd9\xd8\x008a\xbc\xd3V\x15V\xaa\xeb\xa30\"\xf1\x93\x1e\xb5\xc4n\x93\xf5\xa0\x94\xce\xcdVHJmO6\x9b\x18\xc59\xbfJJ&\xf6S7\x9bL~\x1d\x8a\x8d\x9b$\x83\xcf\x8a\xfc\xa1rh\xeb\xb3\xf3b\xbdN\xf2E\xa5\x0e\xd8\xb6=V,\xfc\xea\xc9\x07^\xb3e\x9a\xbf\x16\xd0\xb4\\\x1e\xb6\xcb\xbe\xe6M\xa6\x16d\xba\xb5#\xb1\xeb\x98d\x95\x80>\xac\xb5\xe5\x05,\x18\x17\xe7\xaa\xaf\x98\xd8\x14L\xda*\xebP\xcc\x93\x1cVI\xbe\xc8\x18$\xb0L/\x99m\xa3\xb4y1\x02\x17\xb0=SWB\xba,\xe56\x13o^e=\xbf\x9d1\x96\x0b\xd4#um\x89\xeb\x82>\x80\x94\xeb\x16`{\xa8\x02!\x1a\xdc\xa8\xadeZA\xb1\xe5\x0f\x8b\xf3\x87\x8b\x84\x8b\xbc\xf4\xbc\x1bk\x8b?)\xfa_\xc2w\x9aQ`{p\xc9\x92\xf9\xaa#5\xdf\x7f\xf7\xec\xcb\xc7_>~\xf6\xe5\xe1\xf3\xa7_>\x7f\xfc\xe4\xab\xa7/^?\xff\xea\xed\xe37o\x0e\x9f}\xf3\xee\xd5\x9b\xaf\x9e?y\xf7X\xaf\xac\x87\x1a4\xc3\x06\xf7\xf7\x9f\x8e\x93T\xb1\xc1\x10sL\xef\x85\x1a\n:xy\x83+~\xa8\x96\xdd'5\x7f\x7f\xb5X\x94\xac\xaa\x8c\xbf\x0d\xbe\x8f\xce\xd8|\xf5\xeci{P\x94\xbc\xf3^/D\x92\xb0\xfad\xc16\x17\xcf\xbf\x9co\x93_\x97\x17\xbf\xb3\xe4\xab\xdf7\xcb\x8b\xdf\x9e}\xc5\xf3_\xaf\x16\xbf_~\x99\x9c\xcf\x9f-\x9e~}\x0f\xe0'\xad\xe4\xb2\x97r\\&Y\x1d\xc5'_]\xdf\xb0\xf5\x86\xad7\x9b\x17O\xaf_\xacn~\xff\xfd\xc5U\xb9<\x7f\xf1e\xf9\xd5\xaf/V\xcf\xcf\x9f^}\x99?\xcd\xeao\x97\"\xcd\xd1/d\xc1\xf2b\x8dm;\x15O.\xf4\x17Z\xb2.\xb6\xf9\xa8a\xd8\xee\xbc\xff\xfcq\xddX\xbfk\x1a\xea\xe8\xf2)\x0d\xf4\xf4Z|\xe8\xa1+\x1b\xd2O\xde<\xfe\xe6\xf9\x93g\xdf\xbcy\xf1\xe4\xd9\x8b\x17\xcf^|\xfb\xf8\xab/\xbf|\xfb\xf4\xc9\xd7\x87\xaf\xdf={\xfd\xe5\x8b\xaf\x9e\x7f\xe5\xec?\xf9v}\xd6\x19\xaf\x9b\x87>\xfb\xea\x1b\xf5\xc7\x89\x13\xd5\xba2\x0e\xe7A\xa3\xf4\xf8\x8b\x9b\x19'\xbf \xd2\xe821\x0c\xef\x8eq|\xd8\xbc\xfa\xb7\xdc.[s\xd0G\xfa\xe6\x9c\x8b\xa4\x19\xfbO\xdfl\xd5\x95\x16\xf2\x0c\xd5\xd3\xb4\xad\xd9\x1a\xb7\x10\xa8\xd2e\x9e\xf0m\xb9\xfb\xab\xb6x\x02_E\x9a*\x18\xd9\x10?\xbc\xfdxx\xf4\x8f7\x8f\x9f\x9eWo\x8e\xcb\xe4\x9b\x1f\xf8\xd9\x87\xea\xe6\xf5\x93\xab\xaf\xcf~;\xfd\xe1\xf9\xf3\x9f\xb7O\x9e}\xf3\xfb?\xce\xde\xcd\x7f\xbe\xfe\xf2\xdf\x0e\xdf\xdd\xbc:Z\xb2\xe7?\xffx|\xfe\xf7\xa3\xed\xe5\xef\xaf\xff\xe3\xab\x17?\xdc\xfc\xf6]\xf5\xdb\x9boN\x9e\x1c]\xa5o7\xff\x96~<\xfb\xea\xa7\x93\x05\xcf6\xcb\x7f~;x\xe4f{63\xae>\x9cm\xc9\xdd\x92\xc4\xad\xc6\xf7\xe7}\xc1Ml8\xcb\x17\xac\\\xa79\x7ft\xbc=\xfb;\xbb9a\xf3\xcd\xd3\xe7_]<1\xdc\xe7 \xd8\xe0\x9f\xf8\xea\xf2\xf7\xc7_\xfe\xb4\xe2\x7f\xff\xf7\xd57\xaf\x0e\x0f\x7f\xfa=;\xfa&9-\xaa\xbf\xdd\xbd\x05\xfdq\xb2L\xf3\x84\xb3\x85X\x14\x9c^\xf7\xd6a\xceu\x01/x\x92\xcd\xe6\xa6\x05\x8dm\xa6\xd6\xddm\xd2M\x9bd\xc9F\x9d\"\xe0VQ\xde\xd0;\xb3t\x9d\xa2K\xfaL\x7f\xa1\xf0\xebjxO\xdcM\xba\xfe\x9a\xac\xeb\xc2\xd8\x1cb\xaf\xcfT!\x8cJ\x9b\xc6\x10\x0d\x8a\xd1\xae\xd8\xd4=\xd7\x91z\xcah%'\xcd\xb5\xfeq\xac\x80<]l\xb4\xc2\xeb\xde3y\xddd\\\xf3I\xf3N?\x98\x05\x91-\n\xe0\x8e\x04\xf8k&\xcdW?i\xce\xd5\xa14om\xb5!V\x8a\xd2\xdc\xe1\x91\x16\xfe\xd4\xc1\xdaQ\xdax\x05)\xcd\xe9\xde\xb1\x06\xf4\x06\xdf\x17v\xa7o\xf0\x95L\x9a{\xad)\xed\x96W\x9c\xd2\xac\xebNi\x88v\xeb\x0b\x1fh7\x8e6\x81\x88 L^\x95Js\x92\xbfaB\x19vX\xa7J\xf3\xadV\xa5!\x8a\xe5X\xb9J\xb3\xaf_\xa5\xed\xf2\x8c\xe1\x8aV\xda\xc4Ih\xb4\xc6\x95\xe6,\x9fm\xbd\x8b\xb8\xd5\xbd\xf6\x95f^\x01K\xc3;\x1f\xae\x86\xd5\xfd\xa35\xb1\xfa\xfbn\x93\xae\xa3\xbb\xfa;\xabc0\xf0V\x18\xfc\xfdl\xe0\xe1\x84/N\xd5\nfT\xf6qY{k\x94\x80\xc5a\xaf\xc0\xbd\xb5\x871`\xe6 \x8dV\x17\x96`\x98\xa6J\xdb\x0b\x8d\xf9\xa9c]\x17`[\xa9y\x0d`\x9f\xf9\xb1~;\xb3|\x7fn780\xcc\xb5\x01\xaf\xc8\xb9\xa7\xe4\xfa\\\x1d\xcd\xca\xb78\x17\x1bg`k\x1b\xb0\xb7\x00\xf3\x1c\xeb|I\xe1\xf3\xa9\xa5w\xe3\x9e2q\xc6t\xcd\x93\xfeW\xdb\x9f\xaf\xcc3!\xd6\x8b8 \xe3\xe8\x0dz\xbc\n\xd9\xff\x0f\x80!\xc4\xc3\x92\x92\x8f\xbe\x94Q\x9dd\xf01\xdf\xdem\xf8\xf0lJ\xd7\x05\x10\xc7_\xd0\xfe\xf8\x05\xd4N\x04\xf9;\x96,\x18\x1e\xe6\x13\xdc\xc2Y:\xa2\x10\xd9\x82-\x91\xae\xd5\xf6\xec\xa1\x96z\x08\xc3Mt\xb7\xe0\xe9\xda\x8e\x1e\x0e\xee\xb9\xff\xf4\xf1\x93\xaf\x1f>y\xfa\xf0\xd9\xe3\xd3\xc7\xcf_>\x7f\xf6\xf2\xf1\x8b\x83\xa7\xdf|\xfdo\x8f\x9f\xbcl\x17\x1e\xf9v=3\xec\x81\xd8\n\xa2\xdf\x8b`j\x9e\xd5\x913D\x01\xd5,\xf6\xfcV\x9b\x87\xf7\xdb-L\x1c\xeb\xc6m\x18\\\xed\x18lm\x19,;B\xd8!-0\x02r\xe3/\xe0\xfd>{\xde}\xc1\xf28\x8f\xd9\x1e\x87\x95E\xc2\x93}\xfao\x0f!\xd9\xe7Srv\xcdg\xb7\xf3\xa8V.h\x8f\x0fI6\x9b}\xba\x17mK \x0b\xec\xf39\xec2]\xd4\x93\xef>\x9fQ\x8f\x19E\xc5\xcaY\xd2\xe5FX\x1f\x83\xe5H\xc0x\xf6\xc0\xf35\xa0=\xe1k\xd2\xd0,\xc6\xf5\xd0\xb1\xf9Iw\x98K6\x9b\xd0\xfb\x1f\xeb\xa9\x18= \xaf:s\xb6\xf1\x16\xf3m`\x9c\xbf\x01U\xc8\xf1<\x0e\x96\xddu\xff\x12\xa7\xbb\xd4\xed\xcf\xeb\x80*\x0bn~\x07\xd3\x1c\x0f\xa8\x02v_\xa8e\xbe\x87i\xd3\xe9-\xce\x80\xda\x0c\xeb\x00p\x15\x1e\xbc\x9b\x17\x965\x01\xf8\xd6\x05\xe0Z\x1b\x805:\xe0\x8b\x10\xec\x18%\xc3Z\x01P\xed\xe4\xd9\xf3aC1\xae\x1b\x00\xd5\xaa'\x14\xdc\xb0\x86\xd8\xdb\xb3\xac\x93\xfc\xde\x9e\xe8^[\xec\xed\xb1\xb6u\xc6\xde\x1e8^s\xec\xedQ\x8e\xf5\xc7\xde\x9eiY\x8b\xec\xedy\xf6u\x89\xf3\x91!\xeb\x13\xd8u\x8d\x02\xa6u\nL\x9bR\x0ck\x16\xf0\x8d\x98\xe6\xb5\x0b\x98\xd6/\x80\xf6\xb5+\xb1\xa0\xe7_\xb7\x9a\xdd=u\xc6\xe5I+&\x9a\xfb{\xb7~bs\xbf\x14c[\xa7\xe6\xad\x92\xfd\xe2\x08\xcd\xdcf\x1e\xaa\xa49k\xdfzH\xf3\x05\x1b\x11T\xbc\xf7w\x17\xd5\x06D\xce~L\xed.^\xcbb\x9b\xef\x06\xf2\x19\x9c\xd6\xdf\x11\x15O\xd6\x86\xd1+\xc01\xfa\xc3\xa2\xe3\xd7\xfa\xf6\x10-\xfe\xb7%)\xb1G\x90\xfc\x0d\xcb\xd8R\x1cH\xa0\xfe\xab(?\xb0\xab\xa4T\xfdh\xd4x\xc6\xcd\xc41\x9e\x1b\xab\x1f\xb24\x1e,\x89\x835-A(\x1b\xb4\xd5\x01\xcb\x9c\xb7\xab\x1a\x911i\xdb\xf1\xf2\x9bz\x8d\x93\xb5\xcd\x89\xda\x18_*A\xbby\x8f\xa7uO\x95/\xb3B\xbf\xcd\xb2{=\xec)\\\xde%\x80\xa3\xba!\xcdGZ\x8cF$m\xd8\x94\xa4\xd9\x17Q\x96%\xd4\x0e+r\x87j\x90w\xacq4:i.u \xbc\xf7\x8eN\x80Qp,nS\xbaK=\xefuR\xb1\x0f\xec7t_;/\xf1\x1a\xbajO`\xf9\"Y\x95_]\xafV\xfcy\xb9\xfe\xed\x92\xe5_=\xfd&\xbf\xc8\xae\xb3\xed\xef7\x97\xdf\xfc\xfe\xe2\xd7\xdf~\x9d\xaf\xe7\xcd\xed\xbd\xcer\xc2\xc4\xd1+\xaa\x93@Q\xc2\xdf\xd9\xcdYR\xe9\xd3\xdd\x8b\xe6\x88KH\x80\x97I^%\xe2 L\xe5\xcd+\xfa\xd0)\xedg',\xe7p\x99&p(\xca\x0d?\x157\xc9\x92\x95\xf0\xff}|\xfc\xf8\xf1\x93w_}\xf3X\x1fi\x13\x9a\xf3-\x1d>\xfcn{\xa6~\xb3\xe9\n\xb8\xdd\xb4\xab\xe4\xb1\x96\x80\xe7\xce'\xfa\xce\x9e\x94\x88\xe7\xa6\xbe.\xcd2\xa9f\xc9\xe2\xd7m\xc5\xd7,@\x0d\xf9\xc9\xc1S\xed\xe1\x9c\xb1=\x0f\xd2w\xa9g\x81Xl\xad\xb7Y\xc2G\xef\xe9\xac(2\x96\xe4\xe3p\x9d'Y\xd5\x96\xc3~p\xee\xdb\x8a\xa7\xeb\xba\xd5/\x93J\x1c\x82\xd7k\xfd\xf0\xf9<\xc9\xf3B\x9cv$\x8e.Ns\x98\x17\xf9\xaf\xea\x94\xd8\xe1\xf9o\xba\x0b\xcd\x8a<\xbb\xf9\xe2\x1e\xc0)\xd3\x9a\x1c\x8d\xde7z\x80\xb8\x9b\x0b\xaa\x91\xd2\x89\xbb\xd5\xf6b\xdd\x03Oy\xe6\x1e\xa4\xba\x0d\xc1u]\xf3H\xf1\x1b\xea\xca\x8a'|\xebn\x88\xe7i^\xbbL\xb2\xecf\xb6\x83>\xee\xfd\x1bV\xdd\xef\x0f\x15\xd6\x81\xa23\x9a\x1f\x12^|\xfd\xf8\xe1\xe3'\x0f\x1f?9}\xfc\xf8\xa5\xf8\xbf\xff\xd0.\x05>[M\x16\x16*\x13\xe4\xcb5\xd6\xa7\xb6ur=\x8b\xe3e\xbeJ\xf2%\x8b\xe0l\xbbY\xd4\x9f\xd1\x93$|l\xb1n\xc1\x0e\xf4P\xdd\x8e\x00\x98\xb1:\x0c\x0bA\x8c)gI\x96\x18\xf6\xa6P\xcd\xc2\xb0d\xf3\xc7n\xb8\\\xb3\xcb\xe0:\xdf\x80\\\xa8}\xd4]\xa5\x0d|\xd0\xb1o\xfb\x0c>\xcby\x99\xee{\x0fM\x9c\x0d\x9dd\xb3\xd1k\xecz3\x042\xf4\xfay\xc9DtG\x83\x9a\xe7\xbeu\x9a\x1bz\xd8\xe8\x86\xe6=\xbe\xed\x06\x0d\xf1\x02\xad\xf5\xc77\xf5\xde5\xd6z\x1a\xae\x1d\xd7\xcdV\xab;6,\xdcr\xd0\xfaS\xa6-j\xedU\x1f\xd8\xe2\xf6\xe2U\x95\xf3\xc0;\x16\x15G\xdd1\xad\xff\xff\xf7\x92\x9d\xbf\x84\xfb\xff\xedQ\xe7\xe0\xf7G\xdd\x88\xdc\x1fD\xa8\xee1\xf8\xfc\xf3\xa0W5/\xea\xc1V\\\xee~c\xb0\x87F%\xe7\xae:\xda\xd6\xcb\x9a\xef\xa27i\xc5\x8f\x9a\x93e\x11a\xb8\xe3\xdfG\x15\xcb\xcegb9z+h\xf9]\xdbz\xb9\x14\x87^\xd9V\xac\x9fn\xdd\x8f\xb7gY:\xff;\xc3wgq\x89\xab\xed\xba\xcf\xad=I\x97y\x9a/\x83\xba\x8e\xdc\xd8DL\x8f\x8229+\xce\xcf+\xe6\xbeP~n\xcf\xb69OGX\xfe`\xc2\xad*\xb6\x90\xd9X\x95\xed\x03\xad\xb9\xe38)\x93\xf5\xa1\xf8Z\xc0Wn{Vm\x12H\xb6|up\xf9\xe4\x8c\xf1\xe4\xc9\x81h7\xf8\x15h\xfdM\xbaf\xeb\xa2\xfe0-\x939g\xa5o69/\xcau\xc2_\xc26\xcd\xf9W\x9a\xf7\xc7\xafgU\xba\x9c\x19\x0f\xc3C\xdf\xff;\x9b\xcd\x8b\x8a\xcf6\xac\x9c\x9d\xdd\x8c\x91l\x8c\xa3\xba\x14\x97\xacL\xcfo\xa4/\xb6x\xfa\xfc\xf9\x93\x171\\U\xfa\xb0\x88pg\xbd\x19X\xbe!\x10+$V\x01_1\xd8\xd4\x7fbu\xf0\x05\xc0^\xff\xa9~\xa9\xb0.\x16\xdb\x8c\x1dX^\xb5\xd0\xeey%\xe9\x1d\x1fX\xb5)\xf2\n?V(Z\x88\xb5\xe7I3\xf7\x82\xfa\xd2\xd9\xb64\x1e\xaf`h\xb8vjAm\xaf\xe0\xe3\x87\xef\x1f\x95\xac*\xb6\xe5\\smV \x87m\x9e\xfe\xb6e\xd9\x8d\xda\xcc;OU\xb0\xea\xe7@q^\xff\xf7\xc0U\xc5\xca4\xc9\xd2\xdf\xd9\xe2\xde\xe0\x97MY\xf0b^dp\xb6=?g%\xacYU%Kv\x00\xa7\xab\xb4Re\x86\xf5\xb6\xe2 \xf6\xf5\xd2\x1c\x12\x0e\x19K*>\xf4T\xe4\x0c>{\xf4\x194\xfd\xa5\xf6\xc1D\xae\nTl\xb9f9W\x85\xab\xebu\xbf\x82MR\xbf\xc8m\xc5\x07\x8eJ\xb6)Y\xc5\xf2\xd1\x13\xea[\xcf\xb7Yv\x03\xbfm\x93\xac\xae\xf7BFE\xb9\x15\xf5\xff<\xa9 \xcd\x87\xb7\xfeR?\xec\xd1\xb2(\x96\x19;\x10u>\xdb\x9e\x1f\xbc\xd9\x96b\xd5\xfc\xcb\x17\xb2\xac\xc2Y\xb5*\xb6\xd9\x02\xce\x18\xd4\x95\x1d\xf8\x99'y\x91\xa7\xf3$\x13\x8dy\xf8\x94\xcf\xd9\xc1\xf2\xe0A\x1d\x9e\xfa\xeb\x10>;\xf8\x0c\xd2\n\xf2\x82\xd7\x8d\x8am8[|qpox\xd3Q\x0e\x9b:`\xe9\x9c=\x00\xce\xea\x1e\xb0\xad\xb6I]M\x99\x81\xb1I\xb3\xba,\xbc\x10\x95\xb3@\xb0s\xb2\xac\xb8\xaa^\x8e\xa2\xff\x7f\xc3\xd1y[\xb6\xfaum\xca\xe22]\xb0ES\xfc\xfa\x8fIUm\xd7lq0\xbe\xfdU\x0e\xdf\x9d\x9e\x1e\xc3\xdf\xde\x9eB\x91\xeb\xe6-\xbb\xccM\xca\xb2\x05$\xf0\xbf\x87\x0d\xef\xf4f\xc3\xfe\xcf\xff\xfe?\x03gj\xce\xaf\xdf\x8cz\xcbr\xc4\x14\xf1\xdb\x94\xc5b;g\x90\xe4\xc0\xca\xb2(\x0f\xc6%\xd9l\xb2t\x9e\xa8:\x97\xacn#\xc5\x15[\xd4a\x99'\xf3\xba/\x16\xc5\xc5v\xa3\x8el\xab\xe0,\xa9\xd8B\x15zT\x94\x8f\x1f\xbe\x17\xcf]%\x97\xe2U\xaf;\xadq!\x9bc\xa2\x8bY\xff\xf7e\x91. \xc9\xc7\xe9@\xf2\xa1\xa2\x83\x95\xec\xbc(\xd9\x03}[\xed-\xe1\xe9Y\x9a\xa5\xfc\x06r\xc6\x16\xe2\x15\x9e1\x10\x03@y\xc9\x16#oE\x0er\xa7X\\*z\xc0\x01|\xfe\xb1b:\x81\xb1\xaeo\xdd \xea\xbe,[D\x92'\xcbq\xfd\xceJ&\xd6x\xda\xdd\xc1\x17\xc3w\xfbc\xc1\xd9K\xe0\xf58x\xaeH[\x89(\xa9\xea\xd3\xf3mY\xb2\x9cg7\x90\\&i\x96\x9ce\xbaS\x0dG\xc6\xf3\xf3t\x9e&\x99q\xec=\xdb\x9eC\xc9\xea\x11\x95=\x80$_\xd4=T=@\xb0\xc6\xc4\xb4\xd7\xb4\xf03\xb6L\xf3zm/\xe9c\x86\xeer \xdbZ\xb2I\xab\x83y\xb1\x1e\x8f7'\xa2\xa5WP\xf0\x95\xecF\xf9\xb0\xbf\xc2\xe7j\xaae\xeb\x0d\xbfQ]\xe3\x0bX\xd7\x9f\x06p6\xea\x90\xa2\x98uq \xad\x17a\xf5@/\x1a!T\x1b6O\xcf\xd39Tl\x9d\xe4<\x9dW\xddFk8J\xca:Q\xea\x95C\xbd\xf8\xe9\xfd\xe0\x9eA\x7f\xa8;\xe1\x19\x83Dn\xf3t\xa6\xc1\xd1\xbc\xa7\xa6\x90\xe4\xac\xb8d\xba\xe0\xa3\xe6'\xe2{\xcf\xff\xec_^\xe57\xbf\xe8 \xb3\xaa\xbblR\x9e\xa5\xbc\xac\x1b\xbd\xa3\x0cz\xecJ\xb2\xa2W\x7f\x11\xdb\xa4\x1b\xf6z\x84\x11\x03\xa0,\xc3\xd9x\x01\xd0}\x8e\x9e\xd3{M\xe1X7\xbe,=\x13\x05S\xe3^\x05\xd5v\xb3)J1Ol\x92\xf9\xc5\xa3m^\xffO=;\xc8wV\x8d[\xf9p2,\xcea\xcbe\xb7\xd6]G\xf0\x86\x93\xc5\"\x95\xfd\xa8\xe1;\xd6\xc5\xe3\xabbQ\xe9\x82\xd7\xcf\x91\x81\xeez|+W\xf8\xf0\xa4^&\xce/DOQ\x05K\x9a\xc0\xa59\x1c\xfe\xdb\xbf\x8d\x06\xe9wE\x01\xe7E\x01\xdf\xc2\xc1\xc1\xc1\xff\x1c\xfcX?.\xc9o\x86\x7fN\xf2\x9b\x83\xfaA\xef\xcab\xfd\xf9yQ|1\xbc\xe0\xe0`8\x02\xa7\xe7\xf0y}\xdbGQ\xac\xd3\xe2\xf3\xffQ\xdf\xf7\x05\xfc1\x1a{\xc6\xf7\xfe\x97\xa9\xaeO=u\xfd\xf7\xe42\x99TY\xf8V\xcc\xf5\xb5\xc7\xc0\xba\xa5\xd5\xe7\xef\x8a\xe2`\x9e%Ue\xac\x9a|t}\xa9,q\xe7\xf2\xe1Szun*\xfd\xccS\xe9\xe3\x1b\xbe*\xf2Q\xb5\xe5s\xdf\x15\xc5\xe7\x07\x07\x07_\x8c_\xa6\xac\xf2\xe7\x86_\xc4k\x16a\xc0D\xa1\xbe\xe1H\x06\xe1\xcd\xdb\x93\xc3\x0fG\xc7\xa7\xef?|1\xfcT\x95\x8eeC0\xb9\x96\xceM\xd5\xff\xd2S\xfd\xbf\x15\xc3\x9a\x8b\xaa\xbf\xfc\x16\xfe\xc7\xe6\xec\xe0]Q\xfcqpp\xf0_\xc3K\x92\xfc\xe6A\xbdl\xa8\xaf\xdb\xc8I\xf3\x87\xa4\xacVIV\x07\xc5T\xc0q\xe5\x87\xcf\x19=$=\x1f<\xe2c\xben\x1f\"\x8a \x1a\x9b\xb8\xea\xff\xfa\x16\xf2434 \xd3\x93{-\xe5T|\x16\xce/\x9aqC/\xd8\xe0\xec\xa6\x9dR\xf5\xa8v\x95fY\xfd\xc3\x82\x9d'\xdbL\xcc\xa9]g\xf7\x0dS\xe6\xa3\xfa\x1b\xe3@\xfcP/\"\xee\xd7\xeb\xc7ft\xadG\xde\xfa\xdd\xd4\x7f\x90\xef\xa7\xeb\xae\x19\xca\xf2\xecF\xaf\x91G\x9f,\xcd\xf2\x04\x92s\xce\xe4L+\xbe\x92\xee?\xba\xdfu\xa6\x16\xe8\xfa\xb1rE\xaev:\xe0\xb3\xf3\xa288KJQ\xe0\xebG7\x07\xbf\x7f&\xeb*\xd7\x9c\xc3\x85\xb3x\xdcg\xf5U\xf5\xb0\xda\xf9\xe1\xdfO\xde\xff\xd8\xfd\xf7\xb7\xdf~\xfb\xed0\xda\xf55\xedW\x99\x9c\xdb\x8b\xba+\xa8\x89N\xaeZ\xb7\x95\x9a\x85J\xb6\xdcfI\xd9\xf52\xbe\xb9\xbep\xc1\xdaI\xea\x01\xb0\xf5\x19[,\xda\xe9\xea\x81\x9a\xf7z\xdfr\x9d \xe4\\T\xf4\x97\xff\xb7\xae\xea/\xf2#\xa5\x9dr\xbb\x81;\xd0\x9d\xeb\xe5h\x01\x96\xcc/\xea~\xd5.\xcf\xcf\xd3\x8c\x0d\xc7)\xdd\xfb\x8eYY\x15\xf9\xff\xcf\xde\xbf5\xc9q\x1b\x89\xe2\xf8\xbb?E\x1e>,I{\xd4\\j\xcf\x9e\x88?\xcfrc)\x92\x92\xc6+\x91\xf3'\x87rl8t\x9a\x98n\xf4L\x99\xd5U\xad\xaa\xea!\xc7k\x7f\xf7_\xe0V7$\x80D\x15z,{+_DM\x17\x12@\"\x91H\xe4\x0d\x08\xcb\xea[\xf2.\xab\xeaf-)\xfd\x1c\x9e\x8e\xb1\xb4\x9f\xc9\xe2\x0d\xfa\xab\xaf\xfd2\x11\x00\xe9\xed\x81\x9c\xf1\x83g\xf0\x00\xe3\xdd\xe1TVj\xcc\x0f\xcel,r\xb4o\xd8^`\xfa75\xb4\x7fG>\x13\xa3\x1d}\xe5\x1b\xf2\xf9N+\x8e\xc3\xb5Tk\x91\xd5\xf0\x99\xe7\xf9W\x9f\x8a\xf2s!w\xd1\x0d\xab\x81\xc1\xe6X7\xe5\xdeb\xc5!\xd3\x9c)\x85g\xc4Ij{\xf7:\x14\x0cR\\\x03S\xec\xd1G\xf7Q\xb2\xa9\xe1\x94\x9b2\xdf*6\xe9\xf5.o\xfc\x9a\xc3@\xdf\xb75\x83\xf51I\xd4-W\xc1#\xb1/\xcdD\xad\xab\x9e\xb11\xfc\xfc\xc7\x9f\x1f[\x0c8}u\x87\xc8\xb1\x05\x96\xd3\x15\x88\x9e\xae\xbe~\xfau\xfd\xc0Z6p\xea\xb0\x98\xfdL\xac\x9e\xda\xdd\xfa\xff\xe5\x92\x1a\xa3\x9cl\xf1D7\x81w\x17/5&%\x10)v\xbaz\xaa\xa1\xee\xc4N\xb4\x065\xe9y\xad\xd1\xfeKIR\xc3\x9e\xcf\xb4\x97\xd2\xb8\x97\xd0\xbc\xe71\xf0\xcd2\xf1\xa53\xf2\x85\xcc|\x13\x0d}\xa9M}\x1ec_js\x9f\xd3\xe07\xdb\xe4g\xe1c\xa8\xd1/\xb5\xd9o\xb6\xe1/\xb9\xe9o\x96\xf1/\xbd\xf9/\xa1\x010\xb5 0\xa1\x11\x90b\x06Lh\x08t\x9b\x02\xe7\x19\x03-d\x98q\x90h\x1e\x9ck \xb4\xd0\xd9\x06\xc3\xc9&C\xc7\xfb\xf3\x9e\xa3\xd8i8\x0c\x9f\xd2\x13\x8d\x87\xb6\xe02\xc6\xc4\x91\xf9\xd0?\x82\xc4&D\xcc\x88\x98\xc4\x8c\x98\xd8\x90h\x9b\x12g\x1b\x13\x07\xb8\x1a\xcb\xb08\xcf\xb4\x18\xb0\xb79\xcd\x8b\x04\x03#j \x8902\xe2\xed\xff\x8a\xcf}\x92\xa9\x91:\xf9\x90\xb9\xd1?\xd3\xa0\xc91\xca\xe8h_\xb1g\x1a\x1e\x03\xa6G\x9f\xf1\xd1o~tR\x85j\x82\x0c\x1b!m3\xe4,C$\xc9\x149\xc5\x18\x89\x93\"h\x90Lf\x92t\xf4?\xe2\xa4\xa4\x86\xc9\xe4\xa6\xc9\xc4\xc6\xc9\xb4\xe6I\x8f\x81\xd26Q\xdaF\xcaTf\xca\x84\x86\xca\xd4\xa6J\xaa\xb1\x92`\xae$\x1b,i&K\xc4h\x89\x19\xb6\xe8\xa6-\xbf\xe1\x92l\xba$\x19/\xad\xc1\xa74`&7a\xa64b\xa64c\xce[\xef\xa0)3l\xcc4\xe6L\x01\xaa\xdcIk\x1d\x94Wa\xb9\xa4_\xb2\xba\x91\x84\xd5\xbf\xe8\x16\x07v\x9d\x15\xbd\xa4\x14\x18k\xe6\xdd\x07\xa3\xd0\xc4\xf6\xcfZ\x14\x1a\xd3hwT\xa0\xb6F\xdc\xd2(K\xd5\x0f\xe2\x84aRD\x84\x9e\xfe_\xc6\x17\x1a\x83\xdf\xd8q\xc5?\xf5M\x9b\xd5\xb52\x13\\\xb0k\xfe\x8e\xffr\xe4u\xb3R\xbf\x8f\x90\xfcr\xe4\xe2\xb6\x7f\xc3%:A\x02\x0e\xfb\xb2n\x80\xcb{\xad\xbc\x04\xf7\x9a %?\x83\x13\x1aD\x9a\xea6jJ\xd6\x1dM\xa2\x97\xf3\x91\xffPu\xbc\x84\xcc6\x16\x8e\xdeu|7j\xdb\x9f\xaa*\x03&\x91\x8c9\xfb3\xab\xa1\xe6\xcd\x19dMm\x0c45\x1c\x0b\xc5\x08[uG\xfe\x9c\xe9rQakz=\xc1\x9c^\xc7\xd9\xd3U\x14m\xb45\xfd\xd0\x0b\x8f\xb6\xe6r\xf0F\xe6\xeaS\xb2\x8b\xc9U\x10\xc1\xfc\xde`k\x98\xc46x\xe0\xf5\x0c\\\xee \xeciH\x83\x01\xd9i\xd0\"\xc1\xd9\xb1\x88\x07\xac\x80\xf0\x18\x85\x97ul\xf7\xbb\x8b\x976\x0f_\xb1\x9a\xaf\xa4h\xe9\x05\xeb\xb7\xdb\x93\xcc\xc1\xe1$\x0bTh\x0ef7\x10\x9bZZ2\xbd\xf1+\xde\x1c\xabB\x15rS\xe3\xd3\xe2\xbe\x15\xadR\x9e^gE\x0f\x87\x9c\x968u\xfcBs\x05o\x85V^\x16\xd2\xf0\xa2\x92k\xa0\xac\xc4\x10\xfak\xdbz-j\xde\x98\x8dF\xc8\xc4q\xb2\x8b{\xeez\x08r\xfa\xc5q\xcf\xablc\xfe&\x95\x98\x0d+\xda\xc2v\x9foxa\x88u,Z\xa1\xdb\xbf1\x9dKT9\xaf\xebn\xd6\xca\xb4x\xac\x05u>q\x1f \xf4\xc4{\xf8\x86$\x98\x9c]\xe1\xb3\xc1I\xa4\x86\xb5]'\x8c:A\xfb\x9c\xa17\xc21\x1f8\x17\x95\xdd\xac\xfb\xc3\xf9\x0er\xbek\xb4\x015k\x94\x16h\xee\x82\xd2\xb4\xae\x98Nu Hqu\x07\x9cmn\x80\x1d\x0ef\xde\xbd\x83k<\xfbqq\xc3\x96\xf5G\x7fw\xcf\xbf\x87\\PA\xae| Mu\xe4\xe2\xe4\x85\xac\xd8f\x1b\xd6\xf0\xd6 \xa5g-?\xb4\xd6++6\xf9q;\xb8\xf31\xd5C\xeb\xd9\x1bQX\xfam{'\xb8\x10'C\x95\xeb\xc3y=\xa0\xe9h\xc0\xf2\x9a+D\x12\xdf4\x86I;\xae\x16\x8c\xbb\xd2l\x99]\x17e\xa5?\x19n\xff>zE\x03C\xfa\x8a\xdf\xf2\xaa\xb62q\xe6\x93]#\x1e\x93\xdc\x90\xbb\x96\xf4>\xf33`\x0f\x9d\xe8\x88\xcbdu(\xab-\xd7\x9e#|\xdf\x1b\xe5\xff}\xb97\xc2\xd7V\xec\xbf-K\xa8\xcb=_\xb7\n\x00zy\xec \xf0\xfe\xb2\xf5\xef\x8fJe\x1f\xeb\xab\xfd\x86\x99\x99_{7\xca\n\xb8\x16gH\xa5\xbf\xd0CV\xd9A\xadd\xd1\xa8\xba~\xc5-\xe7\x19\xe1\xc0\x89\xd4\x99l\x9d\x9d~\xec`z\xfal\x1d=B?G\xa5\x06MfbC\x8f\xd2\xc5\x9d\xdaw\x87o\x86\xe2=b\xa5\x9e\x8e\xe2\xe2%\xfdI\xcbL\x9fo\xb8\xba4jD\x9b\xb2R\x1f\xc9}4\xe2=i\x03\x90\xe7`\x7fV\xad\xe4\x18n*\xdd\x93\xb5\xab*~\xe0\xd2\x7f\xf1\x0d\xabZ\x92\xb9\xf7\x95F#\xd7w\xbc\xa5\xc6\x17\xeb\x17ED\x9a\xa8\x15\xec\x82\xf2\x85[z%\nm\xc1\x83Z\xd2\x84\xb3L\x0fd\xc1\x03W0+*!der\xb0J\x17\x9c\xd2\xc36\xf6\x9aE\x07\xa8\xcc\x0cM\x91\x8b<$\xca\xe0\xf0\x9c\x19\x8e\"\xad\xe3\x03\xec}\xe43CP\x90\x90\x93t\xc1&3\xc2L\x12\x06\x98L\x0c-I\x19T\x92$\x9c$] I\x92\x10\x12\x7f\xf0\xc8\xf4\xb0\x114LdN\x80\x88\x15\x10\xd2\x84BA\xe6\x05\x81\x8c\x82>&\x84{\xf8\x0b\\(\x08_\xe7\x07g\xd3\xc4\x80\x0e4\x1b\xaciC9\xf0\xfeR\x84o\xf4+\xc4\xb7\x81\x1b3C6\xe6\x07k\x0c\x8e\x9a\x99\xa1\x19\x9a\x8c\x06\xdb\xd4@\x0cg\x14\x02\x12|\xe1\x0d\xbb\x18zyi\xa1\x16\xc36\x7f\x1d\xcf%:\xb0\"4\x19_0\x05>~o\x00\x051t\xa2\xf3\x92\xcd\x08\x97p\x06J\xe0!\x12\xae\xe0\x08k\x96\x94\x80\x08_(D?\x08bb\xf8C \xf0!.\xe4a\x14l\xe0\x0bsH\x10\xe00\xea\xad]\xe9d\xe1\x0c \x03\x19\x92\x850\xa4\n^@\xc3\x16\xfa~\xe0~\xa8\xc2\xfc \x85$\xe1 \xe9\x02\x13\xc2! \xde`\x04B\x18B(\x00a\xe0\xbd\x1fb\xa7\xba\x9f\xdd\xe1\x06\x84@\x83@\x88A;\xbcTa\x05 \x03\n\xd2\x84\x12\xa4 \"\x98\xb6r\xde\xc0\x01_\xc8\x804\x93T\x87\xcd\xea\x9a5\xfc3\xbb[U\xc7\xa2\xc9\xf6|\xf5Z\xdc\x80\xc8\xd6\x12\xde}\x0d\xb8\x8e\xba)\xb7\x81\xf2\x8a\x9d\x16\x9b\x15\xcd\xbf\x98\x17J5\x05\xbd\xb8\xad\xda\xd9\xcd\x92\xfd4\x804\xe6\"\x05\xd3\x8dF\x16\xaa%\xfbi\x9a\x89 \xa1\xd6\x92\xfd\x14o\x90R0\xc3,e\x10$3N)\x98h\xa2jG\x93\xccP\xa5 \x89\xb9JA:\xa3\x95\x82$\xa6+\x05K\xf6\x93\xf5\xd9<\xc3\x97\x85n\xc9~Z\xb2\x9f\x96\xec'\xeb\x87%\xfb\xa9\x07K\xf6\xd3\x92\xfd\xb4d?\x0d\xd1%4\x1a*Hf:T\x90\xca\x80\xa8`\xc9~Z\xb2\x9f\x82\xe6H\x8d'l\x94\xd4\x1f.\xd9O\xff\xa3\xb2\x9f\xda\xd8\xc6\xe2S\x1b\xd5\xf8\x8a\x17\xe5\xfeC\x11\xf1$\xec\xa8\x8a?z\xb9\x18h\xee\xb2A\xb7\xaajA\xf4\xce\xe9\xdb\xae\xae\xb3[^\xe8\xaf\x8fE\xd6H+\x12\x1cYS\xee\x1f\x9b\xd3\x85\x7f9\x94\x05\xf2\xae\xbd\xc7dJ\n\xe56x\xfb\xc3\x94\xef}\x8b\xc1=\xfd\xe7\xeew\xc9\xa1e\xc1\x95\xe5\xae\x87\xa1b\x99>2\xaeX\xcd\xd7j\x1a2\nZ\x85\xd5\x8a\x7fsq\x8e\xf4\xe6\xda\x12\xffa\xad\xe6\xddG\xf8T\x93\xe29<\xfd\x7fm\xf7\x1d\xee\xfe\xa7\xd2\xdc\xa6\xa5l\xbf{q3\x10\xe4\xeb\xecK\xf2\x15 qgk\xbb\x16\x1f=\x14\x1f\xf5_\xc8\x1b\xdf\x8f\xdb\xfe\x9f\xc3\xff\x11\xbb\xf4X?\x83\xa7 Z\x89\xe1\xfd\xf3\xff\xfb?\xa3eby\xc6\xea\xe8\x17\x90PV2\xc9z\n\xa3J7\xc8\xb3Z\x8e[3\x91\xf9\xcd\xe4\x93\xf4\x18I\xa2\xc1c?;\n\xf4\xd6\x9c \x9c\xc7M3\xbe\xf61\x85\xb4\xbf\x05L\xc0\xf2Q\x13Q/}\xb6Q\xaf\x7f\xae\x1c\x1b\xeeG\xde\xb0-kX\xc4~\xab]o\x7f\x8e\xac\xfdE\xb9_\x8b\xd1L\xa3;\xd9\xe2\x1f\xfb\x8cG\xc8\x900K>X\xd8\x86\x8c\xa8\xc0\x96\x1a\np\xd9\xa1\x00\x97 \nB3\x9a/M\x14\xcc\x94)8\xa5\xedn\"$\x8d\x82\x99\xf2\xc6\xc2\xa7\xe4\x8f\xc3*\x17+{\x14X\x12H\x01\xb6\x1f\x14X\xbb\xa2\xdf\x00\xe5\xeb\x04\x92I\x81;\xc7j\x96\x942\xc8i\xb2j0\xa7\x9e,\x19oL3\xc3\xfe\xd9%\xa6\xa7\xfb\x86M\xd9f\xb7 \xce\x88Q\x14\x06;I4\x1e\xf7-\xff\xa6X\xedQ\xe7\xa1\x12\xbftd\x92\\ix\xa6\x87\xef9\xfcs\xcb\"\xdb\xac>\xe4,\x94\x94\xe1^\x16\xdd~\x90r\xc3\xa1>^_\xf3\xba\xe1[=D\xb98\xed0\xed\xe6*\xc1`\x93gb~fhB\xe0\x05\xc6\xa5\xd7\xe8\xa1\x94\x8d\xfd\x84\xdb\x81#P\xac+<\xe2\xd7\xcf\xe0\xa5<\x85\xe0\x85\xd8$\xe6\x90\xaf\xef\xf6W\xe5\xf4 ~\xd5\xbcK}\x13}\xe9\xbf\x19\x87]}#\xee\x00e\x01\xfc\x8b\xf1E\xc8\xd1\xbc\xb8|\xfb\xe3c\xe5a\xed!\xdc\x0co\x87zUky\xcfV\x9dh\xa2\xf9\xb2\x93\xcc\xd1J\xd8*\xba\x05\xa3\x9c\xd8\x91\xcfB\xd5\xbc\xd8\xaey\xc1\xae\x90\x97\xc4\xff\x96\x07\xb15\xa4\xfe\xf7\xe3\xec3\x05\xae\xdc4\xff \xf8\x9e\x17\xdb\xd7\xaa3\xe5\x89\x14\"\x01\xda\xc3\x8b\x0d(\xa4\x9fS\x87G\x9fo\xb8\xf4\xa70{\x8a\x90\xd5\xc3cA \x10\xad\xbb\x0d\xad\x8cDk\x1f\xed)\x89w\x83i\xd1\x1e\x9b\x12\xacb?65` \x95\xde\x9f\xe7\xdf\xa8\xc7/\xe33\xef\xf5\xab\x99\xbf*\xbd.\xea\x056\xdf \xf7Rq\x87\"2\xd3\xc2D\xeb\x16\x83\xc3\x8b\x15[\xe9\xe7\x91\x1d\x8f\xcc\xb2o\xde^\xbe~&\xad\\\xeagmR\xca\xa4g\xe8\xbch\xb4A\xa0\xf5\xa6\x0d\xac\x02\xca\xf07\xe4\xb0\xec\xba`\xcd\xb1\xe2\xb5L\xe6\xca*e\xa5\xbc.\xafKy#\xefN\xcc\xc1\xd4\xccB\x19\xc9\xd8\xfe\x7f\xb9Sa\n2\xc2 +Zq\xbf\x94\x16\xa1\xa5-B\\\xea\xa2\x1c\xcbRZ\xa4--b\xcb\x1e Td\xe85\xd3\xd8\xde]\xbc4\x83Dj4\x8cE\x9en\xa9\x9e\xee$\xd7\xbew7\x8d\xea\x9d\xf71\xdb[\xa9-\xf9\xfd\xe5\x89\xe8\xa3\xcf\xdcr\x04\xde\xad\xf3\xfex8\xe4wow\xd1\x9bg\xec\xf0\x8a\xd8<\x08\xfb:\x18w\xf1\xaa!\xcc1^2\x8a[\xcd\xb4\x89\xf3\xab]\x8a#C5\x8d\xe6\x8fZ6\x1b\xf3Gj\xeb\xcf\x12cb\x80\xc6V\xd0\xa9\x1bj\x81\x0c\xf3\xe8\xff\xd3\x17\x03\x19U\xf2\x1b@t\x13\x17_P\x98\xb0\xd7Lc\xb3\x8ey\x077\xf6N\x102\x03\x8e\x98\x03Y\xe7T\xa1]-{\xcc<\xe8\xba\x83M\xa1\x1b\x9cf\xfdR\xe2\x86,\x82\x11\x13\xd2\x03\x17\xe9\xbdO\xf0Y\xcf\xdc\x0e\xe96\x02e\x0b\xf4\xc9\xd8\xf0b\xcb\xab}V4-E\xbf\xe3\xcd7y\xb9\xf9\xf4\xcd\xdd\xf7<\xbb\xbei\xa2\xc5\xde\x95h\xbd\xce,\xa6\"\x1d\x8c7\xac\xbe!\x1cv\xe0\xbe\x1f\x1dX%\x14\x9df}\xc3\xd9\x96W\x182D\xdbt\xeb\x9a\xc8\x0d\x07ZL.k\xae\xcf\x92kO\x11Bzk\xe8*x\xc1\xaa\xa6\xe6\xcd\xf7r\xc6\xbf\x19\xfd(W\xf3\xfcU\x7fu\xa6-M\x1az\xea\x8a)\x08\x05\xd4p\x95\xcd@\x0eS\xde\xf9\x1d\x94\x9a\xe4p\x18M\xbe\x0f^\xfa\x83\xff\xf6\xaa\x80\x1d\x0e\xa7A\x1d\xba\\\x08\xf9S\xd4\xbc\xa8\x8f5l\xd8Am\x7fu\x86\x99?W\xc7\\\x1bq\x0fU\xb9\xe1u\xad\x12>%=\x10|\xda\xda!\x7f\xde\xdc\xb0\xac8\xc3,\xdf\xea\x85\x11e%\xce{\x1f\x83\xbc\xbf*\xfb\x90\x1c\x8bI\x12W\xa3p\xd4\x15\x93\x044\x05\x85\x1e\"7\x1b\x90\xc7\x05\x87\xa6bE\xad\xb2M\xf7ls\x93\x15|l\xeb\x95\xa3\x18H \x03\x9e\xa5\xb8\x91\xd2.\xaa\x89\x7f\x9f7\x99m\xae$\xa2\xdb\xb2\x86\x7f%\xda\x8f\xbe\x90y\xd1\xb6|50cg\xe0B B\x03\x06\x7f\xc9\x1b\x08\xc9c\x03\xde\x91Cp\xf4\xe0\x96\xd3\xed\xcf^ym \xec\x81\xf3\x91\n(\xe4\x820\xc9\x02r}\xf4\xd1P\xbe\x1b\x90\xac\xb2)\xf7\xfb\xacY\xa7\xfd\x96\x9b\xe8\xa4\xe6bXh`\xe2+\xa1v\x0d\xca\xbc\xc8\xf7\xfcvJcb\xba\x90\x99\xc6\xb5\x1d(y=tfw\xcdf\x1d\x1c\x11\xcc\xe3\x1f\xa7\x8a\x10R\x10\xb6G\xa5\\\xf2\xf5m\xd9\xf0\xb5{p\n\x82\nI\xb8G\x01\xb2/\xe6\xfe\x9d\xd4\x11\x10;\x03\x83\xce\xfbEp'\xf6\x81\x17GG\x80V\x1f\xbe\x82\xf7\xe7\xdf\xbdy\xfdj\xfd\xe3\xfb\xef\xd6\x97\xffu\xf1z\xfd\xe1\xcd\x7f\xbey\xfb\x877\x13Z^\xbc{\xfd\xd3\xdb\xcb\xd7\xd3Z\xbe|\xfb\xe3\x8f\xe7\x97\x93\xda\xbe\xbdx\xfb\xfe\xc5\x0f\x81\xa6\xda\x8f\xf2l\xe2|\xc3bl\x08\xef\xb3\xeb\x82o\x7f\xac\xaf/u1 U\xabV\x86\xe7\xc8\x9f\xfa\xf5\xd1p\x014\x84\xf6dC\x85\xd1\x08\x9ck\xf3\x0c~*\x1b\xa4:0 \x83\xa2\xf33\xb8\x90\xe7(\xcb\xfdh\\\x97\xb2!D04E\xc1WP\x95\xc7\x02\xf5\x14\xf6\x81v\xabP\xd0\xeb\xba\xad:\x8e\x83\xfb\x867\x04\xa2\xec\x80\x08\xf9\x01\xc1\x0bN\x07\x11d\x87\xd0 ?\x06\xd2\xadq\x08\x11\xd4\x80H\x8a\x08\x08\xdc/\x87\x10\xc3\x17\x06\xe8\xaci\x80\xbaP\x10\xbfX\x10\xbb`\xc4;+\xda\x04\xbf\xc1\x8e?\xdd\xf3\xbaa{\x87\x8d\xad\xf7!}\xa2!s\xcb\x10\xdaK\x9d[\xf5\x1f\xc2\x84\x91\x04I\xdd\x0d\"+\xb6\xfc\x0bm\x084>\xa4\xcb\xa7\xd6\xcb@\xeb>%\x05b\x8ePqJ\x0dc\xd6\xc4\xad\xbdl\xf8\x99\xae,\xbe\xcfT\xb5s\xf5O\xa9\xacy\x11\xca\xdb\x7fw\xb7\x17c\xf6\x9f\xa3\xbd\xf3\xd6\xf5\x89\xd4\x10\xaf\x16\x0d\x91\xder\xd1\x10;X4\xc4EC\xf4\x01Qv@\x84\xfc\x80\x08\xc5#\x82\xec@?\x03\x14,\x1a\"\x01\xa8\x0b\x05\xf1\x8b\x05\xb1\x0b\xb6h\x88\x16L\x18I\x90\xd4\x8b\x86H?B\xff.4D)V\xd6\xb7e\x93\x15\xd7k\x99\xc6\x17\xd6\x16\x83\x04\xa5\x89\x92\x8e\x97\xee\xb7_\xd2V\x8e\xec1\xb4\x85\xa9|\xf3\xca\x18\x92\x05\xf3\xbc\xd6f\xe4\xce\xecn\x0c\xcb\xcaM\xd3\x12\xd0\x89N\xabi\xcd\xe7R\xe0\xd8\xe5\xd9F\xac\xb3\xe44\x07O\xe4B\xe1Y\xab\xac\x985k\x1a\xb6\xf9t_\xe6\xec\xde\x08\xd7\x9eX\x1a\x05\x84>\x81\xd8/\xb4\x84\"\x9e\xf6\xc4\xbe!\xa2\x7fp\x84@\xe1\x101\x00\x88\x1c\x04\xf8\x82\xa8p\xa0\x86V\xe1\x109\x17\x980\x1f\xf0\x07g\xe1@\x14\x00c0\x02\xc1\x19\xc8\x85\x833\xbc\x0b\x87\xfb\x1c\x1cUt\x8d\x81\x166FF\x87\x87\x97Qo\x9e\x06\x02Ag8PC\xd1\xc8\x08\xb1\x90\xb5p\x80\x1a\x0e\xd4\xb05\x1c\xdc\xc1l8D3\x1e\xed\x06m \x1a=\xf5\xcc\xef\x03\x1e:\x87\xc3\x8c\x01\x85T\x82!\x04\x82\xefp\xb8'\xd9\x19s\xcf\x83iD\x03\xba\n>\x84 \x17u\x03\x13\xa8\x07\x13)\x08\xb1\x17x\x031\x17\xa81\xc4\xef\x0c\x03\xb1\x0b\x0e\xd3\x17\x1d\xa6.\xfc\xa4\x0b\xbf\x81\x88\x8b\xbf\x81p\xc8#\x0e\x13\xe82\x81\x1e\xf4\xa0I\x1c<\xa1\x948\xdc\xc7\xb4\x82\x81\x8e8\xdc\xc7\xd0\xdcI\xe5n\xa0F~\x92\x11\xba\x02\xecm\xa0\xc5\x8d\xe2p\x1f\xe4\x0cE\x9e\xe2p\x1f#s\xc7\xae\xe2p\x1fc\"D\xbf\xe2p\x1f\x83\x0b\xc4\xcf\xe2p\x1f\x03\xa3E\xe0\xe2\x10\x8e\xcb\xc5\xe1\xf4\xf3\x9ar;\x8b \x00&!t\x05 \xe3\xa0\x0eQ\n #5\xb4X\xcd\xecWx7!y\xfd\x0cLQ\x0e\xe9Vv\x03\xcb\xa5$b;*X.%N\x88\xdf\x12\x06b\x17\x1c\xa6/:L]\xf8\xfb\xbe\x94t\x19\xd9T\xca(\x8a\xb8k\xfe\xd9\xe0\xad\x02h\xc3$\x06\x9e\xc6\xbeF2\xadw9C\x8a\x97\xf9`2c\xd0\"\x92\x86\xf0\x15|\xf3\xc3\xdb\x97\xff\xb9>\x7f\xb5\xfe\xf6\x87\x17\xdf\x11\xa3u\xc60\xc6\xf2\xe2\x9b\xf7\xaf\xdf\x84\x83\x8d\x860FB\x8cX\x1a\xc2\x18\xc9\x9b\xf3P\xe0\xd2\x10\xda0\xa6\xf9d\x89\xbf\x85)P\x9bl\xfbm\xce\xae{\x85\x05U%\x84o\xf2M\xf9\xe9\xfcU\x94]YA\xbb\x1d!S\xde\xd9P\x83\x0e\xa2\xbd\xfbC\x98\xcc\xcf\x93\xc5\x1c\xc1\x87j\xc3\xeca\xc6\x19U\x15\x90\xc3\x05\x860{\xacQ$\x9d\xa2\xc7+x)\xd5\xe9\xf7\xd9\xb5\x8a\xd0\x13\xba\x87q)\xc8\x08\x04\x93\x92\x15\x812+\x80i\xbcau~\xda\xd8\x15\xf6aBY\xeb\xdd\x96\xd9j\xee\x02\x026|f\xb5\xbeV4*+\x8f\x99\xec\xb4\xce\xe8\xe1\x9fI\xb7\xffj\x1e\xbc\x12D\x9cq1'[7\xd6\xf0\xb7\xb1'y\xc4)\x1e1;\x051sT\x10-\xe3&m\xc6 \x1b\xf1p\xbc\xb2\x8bh\xfa \x9aX0\x89`\x02\xf8\xf6\xeb\x7f\xfd\xd7\xa7\xff\xbf\x98&\x13 \x07\xd3\x88\x07\xb2\xa6\xd2\xe6\xf0\xf5\xbf\xfe\x9fOO\x7f\xcd\xc3\x9c\xa25\\\x1c\xaf\xf2l\xf3\x9f\xfcn`7\xf9\xc4\xef\xfaeB\xe3\xce\xfcc\xcdU\x1d\xa7^\xbe\xf5O\xad\x0c \"\xa2\x86n\x0da\x12\xbd\xa7\xdc\xddZ\xcb\xd9\xa1\xca\xca*k\xa2\xb7\xd6I\xc7hFG\x19T\xe4N\x8f\xdd\xe3\x91\"q\x02q\xa2\xb7J\xa40\x8c$\x10L \x12L\x13\x83\x13\x88\x05S\x08\x06S\x05\xe0\xfd\x0d0^\xf4%\x17| \xc4\xde\x14\xa17\x81\xc6q\xc2\x04\xe6\x88\xbb\x93\x8f..\xd0\xb7m\x157,\xda\x90\x84\xa6^\x16\xeb\xb0\xf9\x9d\xd8;\xad\xd7\xab\xbb?\xb3\xa2\xc9\n\xbe\xa6\xe9\xd94\xfd\x9a\xa0W\x93\xe5\"]\x1a\x92\x0f\x0b\"\x05\x15D\x88\x13\xf2\xd1@\x9e\xb6\xe5\xa2!v\xb0h\x88\x8b\x86\xe8\x03\xa2\xec\x80\x08\xf9\x01\x11\x8aG\x04\xd9\x81~\x06(X4D\x02P\x17\n\xe2\x17\x0bb\x17l\xd1\x10-\x980\x92 \xa9\x17\x0d\x91~\x84\xfe]h\x88\x7f\xc3\xd0\xe8\xa5`\xbd\x0dK\xc1\xfa\xa5`}\xe8\xbb\xa8\x01@\xe4 `)X\xaf\x80(\x00\xc6`\x04BTM\xf8\xa5`}\x18\x96\x82\xf58P\xc3\xd6pX\n\xd6\xfba\xc6\x80B*\xc1\x10\x96\x82\xf5T\x15|\x08\x13.\xea\x06&P\x0f&R\x10b/\xf0\x06b.Pc\x88\xdf\x19\x06b\x17\x1c\xa6/:L]\xf8I\x17~\x03\x11\x17\x7f\x03\xe1\x90G\x1c&\xd0e\x02=\xe8A\x938,\x05\xeb\xe3\x86\x16\x97\x14\xae\x80\x1a\xf9IFH/\xe8F\x8b\x1b\xc5\xe1>\xc8\x19\x8a<\xc5\xe1>F\xb6\x14\xac\x8f\x1c\xdcR\xb0\xbe\x07\xa7\x9f\xd7\x94\xdbYL\x000 \xe1R\xb0\x1e\x87x\x0d\x8c\xe4\xf530E9\xa4[\xd9\x0d,\x97\x92\x88\xed\xa8`\xb9\x948!~K\x18\x88]p\x98\xbe\xe80u\xe1\xef\xfbR\xb2\x14\xac\x0f\x16\xa4\xc0a2c\xd0\"\x92\x86\x10[\xd2\x02\x87\xc8B\x178D\x96\xbf\xc0\x81^\x14\x03\x87I\xa52p\x88\xbf\x85) V\xdc\x88\xc4J\xaa\xcf\x81C\xb4w\x7f\x08\x93\xf9y\xb2\x98#\xf8Pm\x98=\xcc8\xa3\xaa\x02r\xb8\xc0\x10f\x8f5\x8a\xa4S\xf4x\x05\xd4B%\x11(\x97\x82\xf5\xbeO\xa3\xce\xb8\x98\x93\x8dVH\xd3@\xdcI\x1eq\x8aG\xccNA\xcc\x1c\x15D\xcb\xb8I\x9bq\xc2F$\x17\xe24\x10M,\x98D0\x88-\xd0i`\x12\xe1`\x1a\xf1 \xbep\xa7\x81\xfb\x1d\xe6\x14\xad\x81Z\xd83\x02e\x82\xca\xcd\xb1\x85>\x0dL\xa2\xf7\x94\xbb\xdb!\xbe\x00\xa8\x81{\x18\xe3a)X\xef\x81Ha\x18I \x98@$\x98&\x06'\x10\x0b\xa6\x10\x0c\xa6\n\xc0\xfb\x1b`\xbc\xe8K.\xf8\x12\x88\xbd)Bo\x02\x8d\xe3\x84 \xcc\x11w'\x1f]\\\xa0o\xdb*nX\xb4!-\x05\xebq\xa0KC\xf2aA\xa4\xa0\x82\x08qB>\x1a\xc8\x93\x87(\x02@\xecA\x10E\x08\x88#\x06\xc4\x8b\xfd\xd3\x0e'F\xc8S\xc5\xfbR\xb0\xde\x05'\x18K\x9c\xb0&\x0e\x80\xd85\xc5\xb2\x17\xd9c\xc8jG5 -\x05\xeb;\xf0\xad\xe6R\xb0\x9e\xe0\x7f\x0d\x9eL\xe1\xd3h\xa9\n9\x80\xa5`=\xc5\x8d\x15\xe9\xc7\x8b\xf4\xd8\xd1}s\x93\xbcpK\xc1z\xf2\xb1\x16t;E\xf4\xe4\x98\xce\xff\x94\x82\xf5H\xc1xo\xb9\xfa\xaeP}\xaf\xa5F7\xadX}\xab\xc6\xbe\xe7\xcd\xc4\x9a\xf5\xe3S\x1e]~\xec0\xc0\xae\xab\x988\xb5\xc4\xa8C|\xba\xc4\xa6c\xf79\xd9\xd4q\x07\xf4\x08m\x9f\xc0\x16\xcd\xd6\xc7\n=N\x03;\x85\xa2>\xbe\x80\x0f\xef~xR\xf1\xba\x8a\xee\x9f\\\x97\xe5u\xceW\x92\x16W\xc7\xdd\xea\xd5\xb1\x92 n\x1f\x1f\xab\xd1K\x94\xf5My\xcc\xb7p%\xcb\xc8\xe0\xe9\n\x1bV\x94E\xb6a\xb9\xdc x\x8f\x8f\xf8\xeazu&H(S\xf5\x1e\xac\x1e\x08\x19!\xab\xadn6\xfc\xd0\xf0\xed\xe3\xd5o\xf0\xa6\xe7\x05\x1c\x04Q\xb3\x0d?\x83\x86\xb3}\x0d\xc7\xfa\xc8\xc4\xf4U\xa6\xfe!\xcb\xc5\xe8\x9aR\x155\xcd\nV\xdd\x01\xcbs\x9cvw\x07\xae+\xae67\xfc\x0e\xef\x92\x7f9\xf0M\x03Y#\xae\x1b\xc7\xda\x94\xd0\x91\xcc\xc0\xbf\xc8\xa5|Q\xdc\xad\xe0\xfb\xf23\xbf\xe5\xd5\x99\x14m\x1f\xde\xfd\x80_\xa3\xd5\xc9+\xd0\x08v\xc5\xf9us\xc3\xf7\x1c>\xde4\xcd\xe1\xe3\x99\xfao\xfdQ\xd6\x1f(J\xfd\xeb\x99\xe4\xb2\x0d+\xa0\x94\xbbIR\xc0\x16\xdd\n\x8e\x07]\x0f\xc8\xd1\x1f\xafny\xa5\xc8\xb0g\x87Z\xb1\x8c\x98\x81\xbc`\xe9\xc2A\xd2\xe6\x90\xa9\xda\xb1\x0c\x9f\xdb\xae\xcc\xf3\xf2s\xfd\xcc\xb1v\xbf\x85\xf3]7\x03\xb1\xe4\x87\xaa\x14\xc7\xd2\xb6\x9d\xa4<\x10\xeb\xfa\xb8\xe7[G\xe1\xa1\xdf\xc2\x8b\x02\xbe\xbf\xbc\xbc\x80\xef^_\xea\x82\xbbb\xacj\x83\xdee<\xdf:8\xf3\x8fc\x16\xbf\xbc;\xf0\x9f\xff\xf83\xfa\xb1\x94\xe5G\xb9\xd6\x9a\x87\x94\xbc\x97\xabp\xa8\xca\xedq\xc3\x81\x15\xc0\xab\xaat\x04R\xff\x16^ty\xa2\xb5\xac\x1d\xcc\x04}\xf8V\x90u\xc36B&\x94\xe5\xa7\xe3\x01t\x86\x00\x88\xc3m\x0be\xe1\xda\xe8\x8e\xa1~x\xf7\x83\x1c\xd7\x0d\xbb\x95l\xb5\xef\xed\x85\xad\xda\x0c\xccLC\xfc\xfb\xb6\xcc\xb6\xc0\n\x97\xb1T\x0dJn\xfb\x8a\xef\xca\x8a\x9f\x99\xc6\x02'k\xb2\xab,\xcf\x9a;(8\xdfJ\x16\xb9\x92\xd90\x92\x8d\\\xb1+e!\xc4aq\xcde\x03\xb9\xefV\xf0\xe8C\xcdM\n\xbb\xa0\x8a`;!g\x14\xdf\xb1\x82]\xbbf|Uq\xf6I\xc8\x0e\x8dt\xf5\x18\xe7\x967e\xc3\x9fA#\xe4\xf8\xeeXl\xd4N\x11c\xd7\xf2fs\xac*^4\xf9]\xcf\x82\xe6\xc9S.w\xbbl\x93\xb1\xdcs\x8e\\\x1dwPqq:\xf03\x99=\x9c5\xa6\xb3\xa3X\\\xa9\xf7\xb4\xfb\xeb\x8a_gE\xe1\xd2*?g\xcd\x8dC\xe8\xdf\x1d\xf8J\xf13;d\xf5jS\xee]\x12\xf3\xbd\xdcm5\x94\xcd\x8d\xda\xe4\xc5X\xb2\xc0#\xad\x8b\xf1\xfd\xa1\xb9\xd3\xdb\xf31\x8al/m,W\x0eA\"'(\xcd\x8a\xd9\xfe\x90sq\xd0I\xe6\x87\xfa\xc07\xd9.\xdb@\xcd\xf7\xach\xb2\x0d\x12\xe2#\xf7\xdb\x04\x95\"F\xf1vh\x1c?\n\xd1q\xc5M\x9d\x8e\x9e\xc2`\xe9\x06&\x03\xfc\xaa\xbcu(\x1bj\xaa\x9a\x9d\xc7\xd3\x0c\x8d\xe6\xe3\x8b\xe2\xeec\xa7\xb8\xb3\x02Xu\x955\x95\xd8|\x9eQi\x19m\xa1cyY\\\xab\x15a\xf6\x92 \xa9)\x85\xbe\x1a\xd5\x95\xadN\xf5\xfb4Z\x11\xc2f\x17\x86\xf1\xf3\xecJ\x0eU\xcb\xf5\x1a\xea\xe3\xe1PV\xf2\xe4<\xb0\xcd\xa7'\xc7B\xfcG\x9c\x97j\xbd\xa5V2F'5\x1aTy(wpl\x94\xf01\xdb\xb9\x16\x82\x8fm\xb7\x99\xda\xdbp\xcd\x0b^\xb1F\x0eX\\\x1d\xda\xa4\xfd\x17\x88\xbcSKd\xf7\xf3\xfa\x0b\x13\x0c\x0cO\x9f\xc1\x85\x18\xaf\xd8\xc7z\xe8\xac_I\xef\xe5\xef~\xe78\xa6\xbe-K\xd8\x95%<\x87\xd5j\xf5\x7f\xd1O\x04\x11Xq\x87\xff\xc8\x8a\xbb\x95\xe8\xfa\xdb\xaa\xdc?\xda\x95\xe5c\xfc\xb3\xd5\n?{\xb2\x1d<\x12(>\xc8A_\x96\x8f\xfeI\xe0x\x0c\xff\xed\x90\xa7.<\x7fu\xd3\xe6\xeb\x00m~\xcfn\xd9l\xe2\xc0s\xa9[ \xec3\xa8\x90\xd5\x8f\xbe-\xcb\xd5&gu\xed!\x82\x1a\x92h\xa0\xe6\xd3k\x84\xf7\x8bP\xa7%\xcf\xbf\x04\xc8sq\xd7\xdc\x94\x85\x83@j$\xdf\x96\xe5\xa3\xd5j\x85K\xe2\x968\x8f\x9c\xbfK\x06\x92d\x8b\xa5\x9ah|\xae\x88\xf6\xea\xf5\xfb\x97\xef\xce/.\xdf\xbe{\xec\xb2\x8et\x8c\xe6\xeeLu\xe7&\xd7\xff\x0e\x90\xeb\xbb\xd2QgC\x90\xea\xd9s\xf8\xa7\xc3\xd5\xea\xdb\xb2\xfc\xef\xd5j\xf5W\xfcCV\xdc\x9d uM|}P\n\xc8\x8f\xac\xaaoX.\x88\xe8\x1e\xb8\x8bL\xe3\x9e\x1d\xddf\xbbQ\xa7\x1f\x8a}\xd7\xad\x1c\x94dl\xf9\xd5\xffz\x0eE\x96;\x19\xd4=\x16\x84\x13\xc5\xa5M\xd2\xd1\xc8A\xa3l\xc3\xd5]\xa7\xaa\x18\x89\xad\x9e\xcb\xb83\xa6F\x0b\xdb\xb1F\xce\xfc\x87\x88\x1a\xf2D\xdcEW\xf2\x07\xa1\xca=\x04\xd6;U\xc4\x89\xa3+\xaa\xd8=\xc8U\xb7;i\xc5x\x91\xdf\x99{\x93u\xe1mUG`\xbb\x86+mF\xdc\xb7\xed!?yhw\xa1/tf\x88\xea\x06\xc75g>\xd8\x95\xe5\xea\x8aUrr_\x9e\xdc\xad\xfe\xfc@QK\xdd5\xf0k\x95\x1c\xca\x03\xf1\xad8^\xac\x9f\x7f\xff\xfe\xed\x1b\xfb\xaf\xcf\x9f?\x7f\x8e\xaf\xa3\xf8\xbe\xb3\x03(\x9d\xaa\x14\xdbT+\x0c\xea\xaer\xac\xb9\xb1\xb4]\x1fs\x86T\x9c\xb3Q\x88\xcf\xb7\xbc;\xe6\xcf\x80\xef\xaf\xf8v\xdb\x1d\xf8gZ\x7f@\xac\x07\xbdcw'\x89\xf1\xf1?\x049>\xeaK\xee\xc0\xfeh\x88\xbb2[\xfe\x99C\x89f\x9bOb\xcfw\x97\xb5]\x96s\\\xfe\x1a\xf9p\xc1\xab\xba,\x9c\xdbF[p\xe4\xeb+k\xb92\xcf\xe1)\x8e\xb1\xfdX:\x0d\xf5\xb7_\xd3\xa5?\x80s\x14\x0f$m\x1e<\x83\x07\xd8\xae\x19Nw\xa5f\xf4\xe0\xcc\x85K\xce\xe5\x0d\xdb\x0b|\xff\xa6\x86\xfc\xef\xce\x8f\xc5\\F\xdfR't\xbe\xd3\x17\x83!O\xa8\xd5\xccj\xf8\xcc\xf3\xfc\xabOE\xf9\xb9\x90\xfb\xfa\x86\xd5\xc0`s\xac\x9br\xef`\xf2!\x0b\x9e)\x05t\xc4\x97\xe6\xed\x9e\xb6[\xc1h\xc55r\xaf\x97lgw\xf2Qn\x08\xc3\x877e\xbe\xd5\x15\xad\xba\x91I\x0b\x96\xe6_\xd0\xd6\"\xcd\xbe6>\xd9M\xcb\xb9\xf0H\xc8\x07C\n\xcb\xac`,g?\xff\xf1\xe7\xc7\x0e&\x9f\xcb#\xc3\x8e\xdcl\"\xc9 \xd0=]}\xfd\xf4\xeb\xfa\x81c\xd9\xfb\xff\xe7\x8bd\xf0\xdc\xc4\xdc\xae\xd1`\x98\xc6\x04\xac\x83kTk\xf07\x8e\x86\xa6\xef_hM\xf2_\xd5\xbcu\xa2\x1c\xd8uVH\xdau\x83\x19\xe0\xec>hcmX\xd1\xff\xabAo\x9c\x1a\xdd\xb9\xdc`Fu\xdc\x9c.k}Xvy'=\x9c7]\xed\xea\xfb\xcb\xf8>i\xf0\x1b\xc2\x88\x7fjs\x0d\xabke\x87\xba`\xd7\xfc\x1d\xff\xe5\xc8\xebf\xa5~\x1f!\xf9\xe5\xc8\xab;\xd9\\\xa0\x134\xe0\xb0/\xeb\x06\xb84\x86H\xebI\xaf \xe2h\x0fN\x08)r\xe7\x8a\x8b\x92\xe8\xe5|\xe4?\x8a\xe3\xfeJ\xdd\xca\x8d \xadg\xc7\x19G\x93\xf4\xa7\xba)\x8fE\xb3\x96H\xc6[\xf43\xab\xa1\xe6\xcd\x19dMm\xac\x805\x1c\x0b\xc5\x08[eH\xf9\x9c\xe9\x18\xab\x80'\x0c\xf3FE\xbd\xdf\xdcG0\xf9\x19\xe77\xe5\x96\x9f\x17\xbb2\xda\x1f\xa6\xd5\xc1uQn\xf9:+v\xe5\xd8\xafE\xe2sc\xaeX\xa3\xe5*QDnd\xf2\x97\xaf\x11\xa7\xb0\xd7N\xe4a5g\xc5\xc9\xe9\x08\xd1\x1a\x91\xd3\xd0\x0dW`K\xdaYyV7\xbc\x90>z\xd2\xf7\x05o>\x97\xd5'\xd2\xb7\x9e%\xb4\xbe\xdd\xdc\xb0\xa2\xe0yM\xfa\xd8)\xdf\xf6e\x91}\xc2\x1f\x9c\xb6\x90\xc8\xbd9\x9b\xbd\x9a/\xae\x9a\xd9\x9e5\xac\x0e\x9b\xa8\xb7 {E$\xed}\x11\xb1\xb9\x84\x92J\xa2\x0d;\x1c\xd6\xe4\x8fc\x96\xf9:\xf3\xc5\xd7Y\x9f_\x1d\xb3|\xbbn\xd85\x8d/\xaeK\x9f\xd8p`\xdf\xf2\x03\x8a\xdd\x8ejB#\x9a\x9c<\xe3w\x8c\x1fX\x83\xc6~yw~{\xd6\xed\xcb\xed1\xe7\x12\x89\xf5\x91\xa7\xceo\x14r\x8d\xc7\xfa\xae\xc6\xc3\xa6h\xb877|\xf3\xa9>\xee\xc7DT\xbf\xfe\xa8z\x1e\xebf?\xa9\x91\x9c\xf7\xeb:\x0d\xd5\xba\xee\x03T\xb1C\x8e\xb5\xceFma\xeb\x9f\xccV\xb3\xf6<\x96\xba\xc1\xb0\x9b\xf6 6\xad\xc4\xe1\xabq\xc5\x1c\xc1\xef\xef\x8aMV\\G\x9f\xc0\xb5j7\x16\x0dWe\x99s\xd6-d+;{\x7fw\x12`4\x16b\x80\x8en5q\xfe\x98\x1e\x13M\x8c%<\xc7\xb4X\xc2s\x96\xf0\x9c%<\x07\x96\xf0\x9c%\x7f\xd4=\x07\xe8(\x0f)\xd9 6\xf42\xa3\xab\xe7\xf4(;\x02\x0d\xbc\xcd\x87>\xe3\x81\xa7\xd8\xd7z\xe0\x15\x8e\xf1\x05\x07\xa8\xd5\xd2\x9bL0\xcb\x1d\x87\x0c\xdbr\xc1E\xc8\x89\x06u\xb89\xb7\x95\xdf\xc8\x94\xd0\xbd\xe6v\xac\xa5s\xa9%s\xa69\xddh3\x1ch\xa9\\g~\xa7\xd9$w\xd9dG\x99\x9c\xef\xd8\xbf\xe3t\x91Mv\x8e)\xddl\x84\xcd\xe1\x16\x9b\xe3\x10\x93\xce\xaf\xf1l\x90;\xdb\x14'\x98\xdb\xe15\xd3\xd5Err\xd1\x1dZ3\\Y3\x9cX\xe8-?\x99\xab*\xad\x93*\x99{*\xec\x98J\xe6\x92r9\xa3\xe6\xb8\xa1P\x97\x13r\x17\xb1\xe5\xcdT7\x93\xd3\xa54\xd1\x99\x84\xb8\x91\x82\xfa\xa7\xa5P\xfbO\xd0\x89\xee\xa2\xce5\x84\xd1\xf77\xe1\xbe\xe79\x87\x943\xa8\x87\xcev\x0b%p\x08\xcds\x05\x8d\xb8||\x18\xcet\xffhB\xf71\xceq\xf4x\xbd\x18\x0e\xe7N\xd0\xadc[x\xe9\xae\x1c\xbb\xed_\xb1\xb9Nr\xdcP&\x1br\xd6\xb8\xe7\x16t\xd0D\xb8f\x86V\xac\x99\xee\x18\xaf#\xc6\xed\x82\xf19_P*P\x1d.!W\xcb\xd8\xc92\xc3\xbdBp\xac\xc4\xbbT\x10\x07F\xc8\x8d\x92\xc8\x81\x82\xf4<\xe0\x94Y\xee\x92\xb1{d\x8ec\x04q\x84\xccr\x81\x8c]\x1e)\x9d\x1dN7\xc7\xd8\xf6;vm\xa4qj$sg\xa4ud\xd0\\\x18A\xe7\x05\xd1mAqXX\x96}\xbb7\xaa\xe9\xd9\xef\x9e :&\x08.\x89\xc1\x90S\xba!f9 l\x87C:WC:'\xc3\xf4\xd5\x0d:\x16B.\x05#\xbeq7\x02\xaa\x89cF~\x8f\xd3\x80\x88c\x8e\x8b d \xec\xac\x89d\x13\xe10/\n\x99\x83\x9d<\x85|D\xb1\xaeb\xc9R\xc8gX\x92\x14\x86\x0dI\x8erb\x1b&E5'H|\xc0\x12\xa0\x9c\xd7\xbb`\xe2\x93#\xe9\x89\x8c\x10KvB\x12\x9d\xc2\xf8\x90\x04\xa7\x18\x83v\xda\xc4\xa6\x1e\xc7o\xaa\xbbCS\xae\xdaw!\xc8\xecn\xbd\xc7\xe1\xdd\xb5\xbd\xfb7\xf2v\x06\xad\xa9)Y\x1e\xf1\x84\x85\xff\x81\x8a\x1e\x19\x0e_\x1fV\xaf\x94\xa6g\x08G\xa6\x84;5\x19e}G~\xf38\x1d\xd9\xc9RN\x97\x1a\x92\x82\x1c\x8f\xc4J;\x8eC\xe1L5F\xd0\xa0)\xc6\xc8wVj\xf1D\xb1i\xa7\x12\xd3\xf8\x0eI\x1fF\x1a\x8e\xd2\x86#\x96\x1eO\x15v\xd0\xdd\x99\"<\xfa\xde\xcf\xdbo\xbb\xc1\x12\x18\xdc\x1e 28t`\xfeA]\xe8\x8d\xf3S\x7f\xf1\x08\xe3\x19\xec\x15\xef\x1a\x0exs\xb4?\xe8\x0d\x07{\x82\xd6\xac7\xd3F^i\xbf\xe9:'\xccp\xfc\x92E\x047\xe1\x89\xceJt^\xb1:\xdb\xe8\xb2\xedY?k\xd6\xd9\x07x\x13\x05\x7f\xcdE\x0fB&\xde\x97\xe2P,\xeac\x0d\x1bv\x90\xcfX\x18\xaf\x96\xfesu\xcc\xb9|\xc6@\x10`\xc3\xebZ]\x0f\x0c\xf5F\xe8\xa4\x87M\xfc\xb4\xb9aYq6\xd6\xd7U9~\x89A\xdc;\xda\x0fa\xcb\x1a&\xe6v\xdc\xa81\x18\x13\x81\xea\x1d\xf5\xc9\xf6\xb2\xfe\x1f\xd6\xe3\x8e\xea\x865\x1c\x9a\x8a\x15\xb5\xba\x8d\xec\xd9\xe6&+\x06i\n\xb2gj!\x08\xec\xb9\x9a\xe0\x990^\x8c&#\xd6\x0c\xe8P`\xcf-\xc8\x1b0\xfe:\xcd\x04\xee\xc5\x9fY!q\x1b\x92\x89\x12|\x85\xc69B\xf0\x8eR6u\xbf<\xa3\xb0\xfa^\x9dq\xc7m)8\xd5\xc3<\xc1\x17f\x9c\xaf\xcb\xf4^[Z\xdb\xa3\x0br\x8e+xJ\xa0R{J\xeda\xb1\xf5z\xdf\x89\xffM\xd0]\x97 \x9e\x00\xd9x\xec\xbb\xaa\xdc+w\xd3\xe1\x00\xe5\xb19\x1c\x9b\xeeo\x87\x8a\xdfZ\xaf\xa8\xcb\x00\xb1\xa4cj%d\x02\\\xe2J<\x1f\x8b\xe4\x17\xed\x9bN\x80\xce* \x92\x14B\xcay\x1f\x827\x97!\x10g\x0d\x113\x17\x10x]\xb3\xf7!q\x9d\x0d\xd0\xd8\xcb\x00e\x11 n! f1\x08\xf7&\xf4s\xfc\x8d\xce\xfeg\xf7\xff\xd0o\xc4c\x8f\x10\xdf\xbb\x97\x94]\xc7\x8e*\x8c\x1dP\xf9\x89&7\x82\xef@B\xd2\x99R\x8f(\xf9&d\xeb\x02\x97/FV\\h0g::w\x9f\xa9ha\xf5O\xa9\xdc8\x91\xc9\x1bg\xef5\xe5]\x89$\xa2\x1a\xe8\x9de\xd8\xcfR\x8b\xbaZ\xb4\xa8E\x8b\x82E\x8bB\x81\xc8\x9c\xb4cn\xd1\xa2\x80NR\xa0\xc9`\x05\x8b\x16\xd5\x03\xca\"@\xdcB@\xccb,Z\x14\xb5\xf7E\x8b\x8a8\xa2~\xb5Z\x94\xdc\xf6k_b\x7f\xfbe\x98h\xe1\xad\xde\xf1\xc5\xe9\xfb\nn\xb9\x88^|[\x8d\xc2\x03\xaf\x8cqQ0\xc2k\xf3\x1azkfm\xdfG\x97\xe6\xf5\x96H(*\xad\xce4\x9fK\xd1~\x97g\x1b\xb1v\x92c\x905\xce\x85\x92\xb0\xde\xe4\x19/\x9a5k\x1a\xb6\xf9tJ\xd3foDk\x87\x8f_\x01\xe1\x14\x0b\xf5\x05-1\x08''\xa1? \xf6 H\x98\x05\x0e\xc4N!\xa2c\xf0\xd7c\x1f\x03%|\xc3\xd1\x92>v\x88\x1c?\xb8\x03@p l\xd41\x98\x8d\x8b\x06\x8b\xe0\x80\x86\x90\xe0p\x1f\x03\xa2\x88\x951\xd0CSH\xe8\xd0\xf0\x95P\xc0\n\x0e1a,$\x84\xe3P\x17\x7fp\x0b\x0e\x94\x90\x17\x1c\xf0@\x18\x1c\xa2\x98%|\xab3\x10\x85\x96r^\xf6\xc1\x0e\xbbq|7m\x10a\xcd\xd5\x80'p\x07\x87\x13\xcb-\xea\xfd\x04\xe2\x89\x034\x95s\x08\x91\x97G\x03\x91T\x82 \x94\x82\x98K\xa5\x01\xeae`\x0cq\xdcm f1a\xda\x82\xc2\x94E\x8d\xbe\x84\x8e\x9a\x85.\xa3\x06\xfc\xe1R8D\xd2 r\xee\xb4\x80+\x1c\x1caX8\x9cr\x1a\xde\xa0)\x1cN9\x1cw\xc1,\x1c\xa8\x11c$d\xe3\xa82\x1c\xc2\xb1f8\x9c\x92l\xbeh5\x1cN9\x1a<\xde\x0d\x87S\x8e#\x101\x87\xc3)\x07\xe4\x89\xb9\xc3\xe1\x94\x83 G\xed\xe1\xe0\x8f\xe5\xc3\xe1t\xf3\x88\xbdy\xc4\x04\x0c\x06\x91a\x01\x858`\xaf\xbe\xd9\x10\xa1\xe9\xc4h8\xbf\x12==\xe8\x951\x10\xabX\xd1\xac\xad\x06\x16\x05\xdd \x8b\x82.!\x8e\xad\x0d\xc4,&L[P\x98\xb2\xa8\xf7\xa5\xa0\xb7\xde\x0c\xd2\x92\xaa\xd9\xdbQ\xb28\xa0\xb1\xb38D3c<+\x1a \xb2\xde\xe5\xec\x9a\xdah\xe2\x82\x87\xa31\x86\xf0\x15|\xf3\xc3\xdb\x97\xff\xb9>\x7f\xb5\xfe\xf6\x87\x17\xdf\x11\"\x17\xc60\xc6\xf0\xe2\x9b\xf7\xaf\xdf\xf8\x83-\x860F@\x88\xd6\x18\xc2\x18\xc1\x9bs_\xd0\xc6\x10\xda\x10\x8eyd\x88\xbb\x85(P\x1be\xfbm\xce\xae!+\xb6\xd2\x85\xa2\xcbC\xc27\xf9\xa6\xfct\xfe*\x18\xcd1\x84vKAF7\x84FzR\x870\x89G'\x89$\x82\x8by\x08\xb3\x86F7\xe2) \xb9f\x870k|d\xd2\xc5\xea\xbb\n^J\xf5\xf3}v\xad\xa2\x8d\xc49oL\xd1\xd2\xdbkR$\x88\xe8\xb2\x02\x98\xc6\x192?\xc7\x8eWa\x1d&u\xb4\x1eG\x99-b\xac\xfb\x9f\x1d\x8f\xf7\xf5A\xa9\xdd\x8d\xca\x82a&;\xa4\xbb\xbc\xbbG\xdf\xed\xa1\x9a\x13\x02\x97\x82\xe7\x0d\xf5\x94\xc1^^\xc5\x81~\x82\x12OO\xe2L\x14P\xe7\xa3 J\x0eEo\xa4\xc8M\xe4xD\x16\x87(\xa2@4a\x00\xab1\x12\x82h\x02A<\x91\x00\xafa\x12\x82\xfb\x19Z\xec\xe9L-\xa6BD\x17,\xb9\x12\x02J\x08\xcb\x10\xa2\xe9\x1a{\x879\x84^\xb7\xc0\xe1\x84\xe32#\n\x0d$b\x87\xc6\xec\xcd\x08\x91\x15I\x84(v\x8f\x10V\x11\x84\x80Hb@\xbc\x98\x8a$\n\xc4\x12\x06\xa6\x08\xa8\xd3\x0f*N4Q\x05\x93\xac\xcb\x19B\x06\xa6\x16\xf24\xb1\x14+\x94\"iI\xdf\xf80U\x1c\x9dlD\xf4\xc0\xc3\xb6\x05}(\xe1a\x08-\xb6,\xac\xd7\xfc\x87@\xe81\xdc\xd3\xd5\xdd\x9fY\xd1d\x05_\x87\xf5\xd0\xb0\xfe\x19\xd0;I\xf2\x8a&\xa5H\xc2\x9a@!\x05\xc4-O\x12\xcd\xa4I\x02y\xa2\x10#\x88\xc9\x13\x06\xfa\xa4!N\xec\x9ef\x08T!\x1b#^\xd5\x8b\x01~|\xb1\x82\x95.1\xc8t\n\xefa\x05\x91\x024a\xfftaI\xe8\x94\xd0]\xc8\x92\x14\xd1\xcb\xdc\xc8\xe9\x1f\x84\x80~)\xe3\x97_\xc8\xf0\xe5P\xf4\xb42H\xa0\xb8z1\xf3\xaci\xf8\xfe #\xa7\x9b\x12\xf6Y\x9ds\xb6\x05\xa6\xe2\xa5A\xc5K\x1bKF/L\xa6#\x08*\x84p\x81\x93\xa4\x10\x16\xe2\xe9S80_\x8c\xcb{\xb7T\xc0\x1a\xfc\xe4\xa4\x9f\x81\xd0V\xf9\xf5U\xc0ry\x8c\\\x9aE\xca\n*A\x1fN\x80(n\xbfL\x8c\x07&\xc2\xd7\x12\xe1U\xa1\xf9O\xa2=%\x83u\x0c\xba9\xc2\xce\x0b\x92\x9b\"\xb0\x0c\x01\xce\xf4\x1c\x0dD\xc4\xee#\xc1\xeb\" bG\x86=8g\xa8v{\xdc\"\xef>\xb2\xa2\xac\xec\xb8\x15\xbd\x87mhO\xc7\x8b\x82\x9e\xbfRd\xb2\xb6\xab\xbdI\x87\x82\n\xa5$B?\xa7\\\x8e8\xfc\x9co~\x06\xce\xad\x81\xbc\x9d[O\xcd)O-9\xea\xa2\xf4\xb7\xadH\xb3z\xee\x8b-\xbf\xa0\xf2\x8a&\xaf0r\x89\x1f\x82\xc0\x99/b,\x9a\xbc\xec\xe9B\x14\xe6\x1b\xe9?^\xf6\xeb/\xfdH\xe7\xc1\xf9\x06\xd3ul='\x82c\xe72[@\x9f\x99\xa0m9t\x18\xbf\xfe\xe2\xd3]R\xaao^]\x05\xd5S0\x1d\x05\xd3O\xe6>\x08\xe0\xd5G<\x93\xc5\xf5\x10\xaa\x0eB\xd4?\x88\xbaGX\xef \x88\x80\x0e\xe6\x0b\x83\x0e\x82\xba\x86\x87\xc4\x1enr\xe8\x17\x04d\xb8^\xe1\xd4)\x08\x18G\xc3K\xa4G\xe0:D:\xfd!\xa0;\xb4\x03'\x0bp\xc7FB 8\xde<\xe1m\x13\xdc0\xc1\xad\xe2\xdb$\xc4\xed\x91fcx\xb6\x04U\xe7B6\x80\xb7\xe9\x98\xe9\x11v\xa7u\x9d\x84\xb9-f{\xd5\xd6\x82%\xf0\xd9\xa0\xfek\xc4\x89`\xedat\xff\xbaU\xf7\x99u^g\xd5xUU]\xfbc\xb7\xea\xbb\xce\xab\xed\x9a\xa4\xae\xab\xbd\xaeXA\x03\xf2B\x8f+\xa0Fhg\xf2\xd3\xde\xff{\xe48v\x8eS\xeblQ+k\xd1ki\x11\xabgE\xd5\xcb\x1a\xb0\xf4_\xc6\x06\xd4\xd8\x9aX\xbe\xbaW\xb1\x95\xaebk[-6RXl\xa4c8\x91\x8d\xd4\xa1c\x06\xd9\x0d\xd3/\x03Jp\x10\xe7hj\xde\xbaI\xf1\xbc\xec\xd0}\xe3F\xe5>7\xa7T5\x1aW/\xfa\xcd\xa0\xf7\xbe\xdc\xb1*\x17\x8d+>.\xa7F\x0b\xcb\xa9\xd1B\x90\xb9\x97S\xa3\x07\xcb\xa9\xb1\x9c\x1a\xff\xd8\xa7\x86/v\x02\x9d\x12\xc6\x9a\xce\xbaud\x0c\xf3\xee\xf585\xa7U\x95\xebU\x90\xd3x\xd0:r\xd6\x95/\xfa\x96\x17|Q#\xe2\x00\xc7\xde\xcc\x98 ]m=\x00B\"\xc5\xe5\x95\xa6\xea\x04\xf8\xb7\xee:\xcdt\xdd\xc0\xf5\xb5\xa3&s\x94\x8e\x00A=\x01\x12\xeb\n0A_p\xb5 UQv\x85}\x92\x8eY\xec\xc8r\xe4\xe1\xfb\xcfA\x97\xfc\x05oF\xfd\x8c\x13\xfbT\x87*A\xcf\x80\xd0\xc8!8z\xf0\xeb\x1c\x10\xa4\xb7\x81\x90\xee\x01\xc1\xcc\xf3 \xb9 L2\x82.\x02>}\x04\xdc: \x84\x86\xe8\xf7\x98\x04\xf5\x13\xa0\xe2G\xa6\x1e\xac\x0c<}\xcfx\xa2G\xa6\x8d\xd6\x1f\x9f8Y\x8fqQ\x04\xad\xe4\x8b\xd6\xef\xc5\xde>XN\xc3\xe54\\N\xc3\x01,\xa7\xe1r\x1a\xea\x9f(\xac\xbc\x9c\x86\xf0wy\x1a\x86\xb2#\x9c\xd3u\xb1\xbe\xb7J}4\xb6t\x86#\xf7\n\xa4\xb2\x04\x80\xcb\x1a\x00\xe4Z\xf2\xa8\\\xc3eY\xa0Z\xfc\x04\x8d&P\x0f~\xceiqZQ\x1e\xac\xe9\x1e[\xc9=8& \x8d\x0bh\xb5\xdaIG\x00\xf4\x98;X\x06=X\x8d=u\x97\x94d$\x05\xf4*\xeb\xd0UP\xf7MuRm\xf5\x98\x8a\xea]\xb5t\x0f\xc2\xd8:\xea\xb1\xd5\xd3\xc35\xd3 K\x1a\xaa\xbbH@ASy\xfc\x15\xd0\xa3\xba \x15J\"\xd68O\xb8\xa3\xc3u\xf5H\x13\x04\x8a^\xa7\x80\xa4\x12\x1b \xcd\x14\xc8\xb3\x85\xb0\xaa\xdc~FR\x99\x0dP\xf8\xc8@\x98\xe4\x10Cv\xa0\x93\x9e\xa8Z\x8f>\xf6\x97(\xa4W\x0e'\xcd\x884\x93\xf8\xda\xe0\x84\x8a\xe0\xa9\x86G.^\x9d\xaa\xc31=\x82\xe5\xba\xbd\xa5\xb8\xe3\np\xa7\x9a\x03\xb5\xc4v\xaa\xfe\xc2E\xb4S\xf5\x14Q&;U\x97\xc4B\xd8\xa9\xba\x83\xa8R\xd7\x07r\x81\xeb4\xe3\x1b(s1\xf5\xa9\x83\xf5\xa7}U\xa7\x83\xe7V\xf8\xbc\xba7\xfd&P9\x9av\x10\xfa\xec\x0f\x06\x16\xc5\xa6\x03\xd2L\x81<[X\x14\x9b\xd4\x8a\x0d\xa5\xe2\xb2\x9a\x8b\xbfJd\xb0>$\x91\x15\xa8\x8c\x10\xcc\xbd\xefC\xc4b\xd0j%\x87\x93|h\xed\x88u\x91\x83 A\xb4f\xe1\x1a\xc8\xc4\xe4!\x1c\xa8\x15u\xd2U9\x0e\x96\x07\xe8\x03\xc1\xaa=\x84\x08\xae\x89\xd8\xc0\xc4\x9a\xc5\x13:\x0f]\xbb\x15\x90\xab\x12O\x18A`\xfat\x8b\x13\xbd\xda\xb0L\xc8\"`\xc2\x8d4\xb4\x11M\xcfTt $T\x11\x0e\xd4\x0e\xf6JS\xbf\x0c\x0dUe\x0bI{\xaf\xa4'Hy\x8a\x84'\xecP\"{\x92X\x93P\x85\x8d01 N\x0e\xe8\xf5\xd7\x88\x93\x04\xeaD!\xa6\xf2Z\xfa\xcei'D\xda\x8akq\xf5\xd6(%\xc7\x80N\x1b\x9a^y\x88\xa9\xb3\x96\xa8\xe7\x83\xb72n\x90\xdb\xc3|\x1e\xdc\xc2\xa4\x89\x10\x18+\xb8y\x83\x93\x01\xd2\x84\x80\xbamI\x13\x03\xda\xe4\x80\xbea\xd3vK\xd9\xaa)7j\xcc6\xa5mR\x12=B\xdb\x04\xe2\xb6g\x92>C\xee\xfd\xf6\xbbPg\xae\x8e<\xb5`\x9d8]\xb8\xc2\xd5^]\xfaD\xcaJl'*\xfe\xe5\x11-\x01\xa1\x12\x12'\x01A\x12\\Z\xc2.\x0e\x8a\x8d\xf9\x9d\xf8\x85D\xb4x\x08=\x9b\xe7\x11\x0c\xa1\xddB\xe4\x02|K\x92\x04\xc0\xc4\x1eB\x9b=zC\xdeG\xf4\xcd\xc4\x1a\xa5x>\x13\xb52\xa93?\xe7\x87\xac\xa6\xd7\xe6r\xc5\xef\x90\xeanX\xdb\xdd\xb5\xcd\x83\x99@^\xac>\xcc\xe0\xc8\x0dR0S*\xc9\xe6\xe8/A\xfe\x86\xa0\xed,&b\xda\xd5\xc2\x1d7\xedi\x11\xb0\x96E\xc4P+\x88\x8e\xa46\xcd(\xb6\x86\xf8\xa8j'\xa2@\xb4\xb5\x82)1\xd7\xee\x96\xa1\xc8k\x05\xf7P\x84\xde\xeb\xedQ=\xf8}\x03aOO\xc8\xcfC\xb8v\x84\xf6#\x10\xdc\x0d\x04rA\xf8$W\x10\xe1\xdb!\xcc\x0e\x883\x04 zu(\xebf \xcc\"\x06B\x04\x06:\x91\x81J\xe8(O\x0e\xc1\x8f\x134+\x13&@3#\x93\xad\xe8\x11=:I\x15\x8c\x16W@\xe1\x8b\xf0~\x0e\x1a\xc6\xe7\xcf\x88r\x04\xa4\x8a+W\xe0\x8f.W\x80\xc6\x98\xeb\xe6H\xde\x95\x82E\xd3X4\x8d\x16\x16M\xe3\xa4\x92i\xd14\x16M\x03\x01\x12\xa1\x17M\x03(\xa4Z4\x8d\xbf\xb1\xa6\x11\xb2\x83\xe9\xaf\xfc\x84\xf1oEo\x96\x9b\x82Y\xf8O\xf9TDh=g\xe5\xc3\xd93\xf9\\\xba\xb3\xe2\x14\xd0r\xe34\xbaif\xae@\xb6\x9c\x82\x99zh \x7fN\x01\xe1\x04\n\xf5\x03\x81\x8c:\x05\x84\x8e\x80\xd8\x19Pr\xec\x14\xc4f\xda\xe9V\xb4\xb1B\xc4x\x81\x96{\xa7\x80 #\xfb`6X0\x0fOA0\x1bO\xc1)\x07\x11\xda\xf2c\xa0g\xe9\x05QuY|\x13s\xf5\x14\xc4f\xec\x05\x11\xaa\x19\xc4\xe6\xed)\x88\xcd\xdeS\x10\xce\xe1S@f\x84P\xbc\xbb\x022\xba\xd0\x99\xd4\x07\x7f\x86\x9f\xfe&\xbecZ\xd8!1\xe7O\xc1\x89d\x0bE\xff\x868\"@X\x15\x1bB\xc4\xc5\xc7@\x045 \x92\"@\xbd\x10\x19\xa0(\xc0c\xa0s\xa8\x01\xeaBA\xfcbA\xec\x82E]\xa0FM\xfc\xa1\xf7\n\xe8\x99\x85\n\"\xe6\x1b1\xcf\xf8\\C\x05\x84\x8cC\x05\xa7\x1869\x7fO\xc1)\x86\x906+QA\\n\xa2\x82S\xcc\x8d\x9a\xad\xa8\xe0\x14#\x08\xe7/*8E\xdf\x11\x19\x8d\nN1\x08b\x8e\xa3\x82S\x0c .\xebQ\x01=\xf7QA\xfaq\xc7\xa8\xce\xf1\xc9\x92^t\xeeDJ\x05\xbetJ\x05\xc4\xe3\x9ez\xcc\xff\x0d\x15\xce@\xca\xa5\x82\x18\x8d\"lZ3\xb0h\x9a\x8b\xa6\x19\xfa\x1a\"\x16\n\xe2\x17\x0bb\x17\xec\xd4\x9a&%\xd5S\x81\x9a\xa9?\xe1SA0\xedSA\x14c\xc5\xb1UT\"\xa8\x82\xe8\x85\xa4%\x85*\x98\x9a\x1a\xaa`b\x82\xa8\x82\x89i\xa2\n\xe2\x93E\x15\xccJ\x19U@\xc95\xe8C\xaa\xf4Q\x05QI\xa4\n\xc8\xae\xa9!D\xf3]\xb4\xf8 &\x97*\x98<\x1c\x9a\xc5GA\xd0\xd75\x84\xc9c\"\x91(F/T\x90.\x0d\xb5\x8f\xcfgs\x8c\x19cTb\xaa\x17\x13\xfa\xbc\xa6'=UA IU\x01A\xf6S$~(mU\x01\xed\xe4\"\x9cZ\x84Q+\xa0\x8c]\x01YVDm\x82\x88\x0d\x10\xcc\x943@\x9eT\xf0\xe0\x82\xc6\xf9>\x01\xb2\x10f \xac\xb86+\x8c.\x16\x85{\xf1\xe8\xe1n\xeep\xb6\xf1t\xed\xc0\xb6\xd8\x10\xb6A\x88\xda\x90\x10x\xb0Z(,\xcd\x0e@CH8>\xd4Q*c;p\x18\x12\xe6m6\xde^\x8e\x80\xae\x08\x0e\xb3}/A\xee\x18\x1d\x10^\x17\x97\xf3\x90u\x1f\xac\x0e\x07\x95\xc2Dx\x93\xc6\x92l\xb8{\xc9+\xd0\x9cg\xa1\xd79\x84\xba\x81\xdc\xa1E\xde\x95\x1e\xf4\x1c\x0e\x0cBB\x80\xa8\xe8\x9dA.T\x04\xf3\xc2o\xfc\x816\xd41\xb8\xc2d\xa8\xed\xed \x17jKO\x88\n\x15\x85#\xc0\x84\xda\xdc\x1f\x1e\xe2\x0e\x04\xa1\xe1OW\xd4\xda:h\xe5\xa1\xffMw\xc4\x11\x0e[G\xaa@\x8c\xc0K#\xa5\x9ca\xfd\xa1\xd3\xbf\xfd\xcew\xfd\xf0_;<\xe1\xf9AM\xd3\xa9%\x18p\x06\xdd\xcfE\x1dR\x01gj\x13\x08>Z\xb8|*\xed\xc2@0$>\xa4m\x18p\x87\xbd{\x96\"\xf9\xb3\x8ax\xc0: \x9d\xeb2\x10\x08C\x9f\xb13\xdc\x91$d\xeeu\\\x7fI\x81<\xde\x91Cp\xf4\xe0\xd6~\xda\x9f\xbdZ\x90\x01\xca\xf5\xd5\x1ft\x13$\x17\x84IF\x0c\xa5\xf1\x06\xcf\x84\x03\xb3I\x9c\x88\x8e0\xacU\x19\xf0\x04XO\xed>\x18Z<\x15\xf1(\xb8=\xa1\xc7\x19\xde*=\xde\x02A\xa9\xba \xfb-`\xba\xef\xc2GA\xa2\xff\x02\xe6\xf80\x1c\xf8\xc8\xa5}\xa8\xbe\x0c\x08\x96\xf1 ,\x97/\x83:\xb8\xd2\xa1\xeb\xbf\xbb(\x0f\x19\xb5\xef\x9eB(\xbb\x93`\xd7\xdd\x87\xfd\x82h\xf6\x01\xca\x8c\x804+\x80\xa0 \x08\xda\xdeBf \xf0\x82\x81pR2\x89\xa4@#+\xd14\xd4\xfb\xd0\x9dl\x1c\xf6\x99\x00e\xf4\xc1Q\xd3\xfd'\x10.R3w8A_\x05$\xe8$\x8do\x05\xc8\xfe\x15H0\xe6\x90\xaf\x05\x12\xf4\xe1/\x113\x17;\xc1\x07\x03 \xba!\x94y\x99\xdb\x05\xcd7\x03$\xff\x0c\xcc\x1eOz_\x0dxj\xab\xcc\xd0mOz\xf6{\xea\xa3\x84\x0f\x95\x1er\xb4\x16\xcar\xe8#\x10\x9e\x15,\x87>\xfe\xa1\xfb\xd0\x0f\xd5\x15Q\xe3\xfe\xdb>(L\xf2\x15\x01\x9d\xc8\xe1\x8a \xb1~#\xac\x0d\xa1\xfaG\xa4\xff\x08k\xe2\xaf\xf41\xc9\x8f\x04\x10\xb2\xdb\x1a\x98\xe2j\n1\xbf\xf3w\xb2+\n\xe8\x9c@\xdcl\x84\xca\x1c\x91\x1d\x86\xabp\x04]T\x10\xdf\xabg\x9a\x89\xddU\x16JG\xe0\xe6t\xb7\x15&\xcb|\x85/<\xe5.\x9cb\xca\x13\xc6\xebI\x94\xf4\x89\xccS\xb9\xd3\xef\xc1;\x1bH\x8cL$\xeb )\x91\x84\xc9\x00eB\x10\xf6\x08(H\xd7aX\xacF{\n\x9c\x98\xe8)\x90!\xd7\x18\xd0h\x10V\x96\x08~\x04\x053{\xc3]f\x10\xe2R?\x7f\x9e\xf0F\x15\xd8\\\xc1\xad\x15\xdeX\xc1m\x15\x9c\x00\x84'\x01\xb4\x0d\x95\xa6\xab\xd0VJ\xb5\x91\xa8\xdb(\xbc\x89\x82\xf3\x0em \xe2\xf6\x99\xd5O\xc8%\x07\xa1\x0e0\xe4\x8e\xf2\x05(\x1e\xac\xbd\xbf@\x01v\xde\xce}\x03\xd8\xb1\xd9=3w2\xacck{6\xb5o;{6r`\xdd\xbd;\xca\xbbm\xa7#vo\xd2\xe8\xed\xe9\xf3\x88;6\xa6\x8f\x8b +io\x8f\xe0\x06\x8c\xc4\xea\xdbl\xe4\xcd\x81\\O\xbcm\x87W\x0f\\I\x9f\x98Vo\xbf\x11>9\x99\xfe\x82U\xcd{c\xdaP3\xb3v\x8c\xbdOF\xc6\x1f\xd5bl\xe8\xc1\x888%\x83\xd2a\x84\xb1\xa6\xa2^j\x8c\x9c\xc9\x8cLE\xd4\xa5Oq\xe5;E\x92[ 9\\\xf6\xa4\x13\x02\xf5\x9d\xa3\xae\xf9\xa9\xe8BA\x93\x93\\\xef-\xf5F\xe8\x02.\xf7\xc9\xaev$]\xd0\xebb\xa7\xb8\xd6q\x97\xba\x83\xcc\x98\x19\xdd\xb9\".\xe1i\xbb\xca\x83(0\x1b\x89\xc7%>\x81{\xd3f\xd0\x04\xad\xdd^=\xde\xaf\xc3{\xac\xda\xb8\x90\xebCH\xaf<\x95\xe3*h\xa5vZ\xa7\xfd\xae\xe8 \xe7X\xa3\xa1\xb9\x9c\x1d\xae\xe6\xd8\xee\xbc\xee\xd9Xd\xf3]\xc7a\x97q\xec\x98|\xae\xe1X\\\xb8\x0b8\x16K\xc0\xd5\x1b\x8b\xce\xe3\xd2\x8dE\x15v\xdd\xfa]\xb6q\xfd\xa5u\xcd\x8e]\xb2\x11:H\x923\x03q\xb1\xba\x85]\x0f\xc9\xc0\xa5\xba\x1c\x16\x83\x9f\xfe\x01\x0f\x0b\x97\x0bS\x8d\xe7\xb4i%A\xd7d\x80(nWd\x8c\x0b2\xc2\xf5\x18\xe1r\xa4\xb9\x1a\xa3]\x8c\xa9\xd3\xd4H~\xc1\xc02\x048\xf3t\xf5\xed\x02~>\"vd\xd8 \xfdy\xbe2n\xd3\xfdwv%\xbe\xa1\xdf\xceq\x8d\xfe\xb1\xbe\xbe\x14D\x91\xed,\xf2\xf4\xb7\xd3W\xf0\xfe\xfc\xbb7\xaf_\xad\x7f|\xff\xdd\xfa\xf2\xbf.^\x8fx\xd2\xfe\xfd\xe2\xdd\xeb\x9f\xde^\xbe\xf6\xfd>\xd8.\xd8\x17o/\xde\xbe\x7f\xa1\xf6I\xbb3|\xe3\x18\x10\xf7/\x86\xb8\x83\xb9\xaaE\x13s\x15\x8b\xa62\n`\xcf\xeb\x9a]s\x13\xeb\xdd{\x9c\xdbP\xd49\xbfgr\xd1k\xdfwj\x16\xcf\xe0B\xea&,\xaf\xb1\x05i\xadn\xfd\xc5\xf0\x1a5&\x96\x92Bl\xa8\x11\xaa\x08j1\x0d\xea\"\xa3-\xe5\xb0\x8f\xc6\xa1\xc1\xac\xa1\x89\xec\xa0\x96\x05t\xa6Q\xd1c\xe9$\xe1p3\xcb{\xe3}'\xf0\xcbb\xed_\xac\xfd\xff8\xd6~\xdb+\x1d!\xc5\x12\\\x0e\x1d\xcc\xe9dM7c:\xd9\xd2\xcb;\x1e\xce\xf10\xe44\x94.VL\xc8\x88(\x1b&\xc8e4\x10`\xbf(|\xb3\x1dM\xb6\x90\xb2\xda\x89\xbbj\x9d\x94da\x8b&\xab;\xe6:\xf0\x99\x0e\\\xaf\xd4f\xb8q`\xaek\xb5\xe7\xf3D\\\xaa)\\\xa9\xa8\x0b\xd5\xe7:\xdd\x94\xf5\xbe\xacWW\xac\xe6\xab\xdb\xa7W\xbcaOW\xaf\xf8\xe6e\x99\x15\xe4\xa5\xd9\xf2\xa2\xdc{i\xcc\xf6\xe5\xb1\xf0\x89`\x9c)\xf5@Z\xa5\x86AS~\xe2\x85\xd2`\x98\xea7+\xe4l%Q\xc4\x9f6\xd9\x9e\xe5\xba\xc3\xf6|y#O\x91\xcb\x1b\xae\x7f\x80]\xc6\xf3\xad<\xab\n\xd1\x8b6\xd0e\xfbC\xce\xf7\x92\xff\xe5\xba\x1e\xeb\xa6\xdc\xc3\x9e77\xe5v\xbc\xedj\xa8\xf8/\xc7\xacRv\x9f\xeb\xf2\xba\xd4\x9c=\x8c\xa0\xd3\xbb\x1a\xf9\x85\xbe\xb9C2l\xaa\x12\xaa\x80\xa6\x8a\"\xe3\x18\x8b\x19q\x1d\x0e\x89\x9a\xb2\x0d\xefB\x83\xe5\xd3\xee\xc1\xe5PIx\xa8\xc8\xf5\x1a*\xd7\xc7\xbdd\x9e\xe1\xb2G\x9c$\x98\x14\x9fr\x9c\xf4\xf1L\xa9\"\x04\x9fx1\xb4\xbe\x08^\xeb\x0c.&\x05\x85U\xbcU#\x0b`\xbb\x86\x8b\xcd\xa2\xa6q\xc3j(7\x9bcUq\x97eF3\xc8\xf0\xb6\xae\xff\xe6W\xa6\x0e\xecZ\x0b8\xa7\x86o>\x18i\xf9\xed\x9f5\xf1\xcc\x96\x9a\xa4\xa6\xc9\xacQG\xac(\xc2r\xa1\xa8\xa3\xbf\x8cE\xbd\xc1o\xf6\xbf\xf8gS\xc2\x95\x98H]\xabE\xbd`\xd7\xfc\x1d\xff\xe5\xc8\xebf\xa5~\x1f!Q\x97?\xd1\\\xa0\x13$\x10w\x9b\xba\x01\xbe\xdbe\x9b\x8c\x17M\xde?5\x91\xf0\xac\xe0\x84\x90\x1d\xe4\x8a-U\xf6\x191\x1f\xf9\x8f\xe2\xb8\xbf\xe2\x95`>\x9d\x98\xda\x8b0\xcd\xc6\x05\xf4\xfbS\xdd\x08\xa6]K$\xe3s\xe53\xab\xa1\xe6\xcd\x19dM-x\xe8(\xe5\xe7\xb1P\x8c\xb0\x85\xb2\xb9\xe1\xd5\xe7\xac\xf6\x050y%\xe0\x04q\xac1D\xc8\xe0\xb6\xe9\x8b\xcd\xe6\xb8\x97\xdbv\xfbrt- \x08a\xec&q\n9\xbc\x181\xa7\x1b1\xf1 \xfa\x19`p\x86t\x1f\xf4\xd6[\xa3\x19+0\x9f\xf8\xa1\x01&(U\x1d\x8bB\x88{\xb9\x95xu\x06\x1bV\x08\xd9\xd2\x93\xe8\x0d\xb0\xe2N\x868\x92\xd9\xd5V<\xc8\xbcz/\x1e\xaf\x85QO\xc6\xa8S5\xf0V\xefV\xf8\x92\xa8\xdc$^\xedT22\x8f\xba\xf5M\x94)\xd0\x13\xd2\xd6.\xad\xb6\x01JO\xd2%#\xb5\xc8D\xfa\xe3|\xcdQ/\xa5\xc9\x99\x1d\xdeR^\xe4\xb9)\xbb\x15}A\xe1\x83\xe7\xae\xe0D\x02G|\xbe>VV\xc8\xfb\x0c\x03\xc0\x0b\xf8\xf0\xee\x87'\x15\xaf\xcbc\xb5\xe1P\xb0\xbd\xce$>\x16\xd9/G\x9e\xdf\x81\x98X\x93\xed2\xad\xf76:\x83\xc2\xae\x92\x04P\xf3*cy\xf6g\x8e\\\xd0\xe5\xde\xdf\x949\\\x1dw;^\x99\xd4\x8b\x95b\x0e5v\xd8\x1f\xeb6\xbdY\x1c\x199guc\xe3*\x0b\x0e\x0f\x9e<\x80\xcd\x0d\x13\xfc\xcf\xab\x95\x94_9\xab\x1b\xa8\xf9\xb5\x90R\xc6\xf4\xfe\xe1\xdd\x0f\x0fk8\xb0\xe6F\"\xb7P\xb5\xde4\xcd\xe1\xe3\x99\xfao\xfdQf\x07\x14\xa5\xfe\xf5Lr\x8f\xd0OJ\xb9+\xe4\x8ck\xde\xc0\xf1`\xe1S\xf9BH?\xbc\xba\xe5\x95\x9a\xf2\x9e\x1dj\xc5\nr\xc4M\xd9&\x15\xc9\xf39S\xa2\x8a\xd5\xb0+\xe51\xf3\x0cY\x8b\xdf\xc2\xf9\xae\x1b\xa1X\xbeCU\n\x89\xb2m'!\xcf\xe3\xba>\xee\x85 C\x10\xbc(\xe0\xfb\xcb\xcb\x0b\xf8\xee\xf5%h\x07\xc2\x87w?\xa8\x0du'\x0ft\x06\x7f\x1c\xb3\xe3\xe5\xdd\x81\xff\xfc\xc7\x9f-t`\xee:\x85Ywu\xf8HJ\x1e\xaar{\xdcp\xa1\x1d\xf0\xaa*\xad\x87E\xe4h\xbah\xffZ\nhy\xc4\x1a\xd1\xbf\x11{\xb5,?\x1d\x0f\xedU\xed\x8a\x89;hY\xa0b\x05\xc4Td\xdf7\xecV.\xfd\xbe\xc7\xa3[\xc5\xa4\xcc\x0cU\xfc\xfb\xb6\xcc\xb6B\xdfDP\xa9\x8e\xe5\xf6\xab\xf8\xae\xac\xf8\x99i(\xf0\xb1&\xbb\xcarq\xfe\x8b\xb3\xaa6Wd!\"\xaa[\xbeE\xf0\x95\x85\x10C\xc55\x97\x1f\xcb\xbd\xb1\x82G\x1fjn\xea\x02\x8aY\x0b\xf6\x10{]\xf1\x07+\xd856\xcb\xab\x8a\xabSO#\\=F\x8c\xa9e\xc3\x9fA#d\xe6\xeeXl\x14\x07\x8b\xf1\xea=/\xcf:q\x0f\xef\xdf~q\xb2\x96\xf2\xd2n_z\xb5\xac\xbe:\x8a\x9b\xb4\x90\xc0\xfcL\xea\x91Yc:9\x8a\xc5\x92\x97\xd4\x96\xef\xaf\xf8u&\xef\x00\x162Y\xeb\xd7\x16\x17w\x07\xbeR\xfc\xc8\x0eY\xbd\xda\x94{LJ\xbd\x97;\xa2V\x17m\xb1\xe1\x8a\xf1\xee\x86G\xda\xeb\xc7\xf7\x87\xe6No\xa1\xc7\xb0\x17\n\x8a\x85\xee\n\xd9\xccr2RwnU\\\xa5;\xd7\x07\xbe\xc9v\xd9\x06j\xbegE\x93m\xea!\xab\xcb=\x12q\x14{\xf2qC\xa7\xf4\x8fb\x1b_q\xa3\x96\xf5\x0eZ\xeb\\\xd5\x87\x13\xbb*o\x91\x03ZMI\xb3\xa43\xdej4\x82\x8f/\x8a\xbb\x8f]\xd5\x11V\x00\xab\xae\xb2\xa6\x12\x9b\xc63\x12#\x07Y^\x8eh\xa1n*\xc3\xa5\x10\xd2J\nT5\x92+[\xdd\xe8\xf7e\xb4\x87\x11\xcb\\\x18\xc6\xcd\xb3+9<-Gk\xa8\x8f\x87CY\xc9\x13\xe8\xc06\x9f\x9e\x1c\x0b\xf1\x1fq\xee\xa8u\xac\xb1]b\x1f\xb8\xe5\x0e\x8e\x8d\x12\x10f\xfb\xd5\xa0\"$2\xb5\x17\xe1\x9a\x17\xbc\x92\x17hu9j\xa3\x00^\x8c\xe4\x91Z\x82!\xfe\xd7_\x98\xbc\x84<}\x06\x17b|b\xdf\xe9\xa1\xb2~\xb6\xea\xcb\xdf\xfd\x0e9\x06\xbe-K\xd8\x95%<\x87\xd5j\xf5\x7f\xad\x9f\xc5dYqg\xff\xc0\x8a\xbb\x95\xe8\xee\xdb\xaa\xdc?\xda\x95\xe5c\xfb\x93\xd5\xca\x96\xf3\xd9\x0e\x1e\x89\xa6\x1f\xe4\x00/\xcbG\xff$\xda>\x86\xffFd\x1b\xd6\xfe\xaf\xf8\xdc\xbf\x0e\xcc\xfd\xf7\xec\x96M\x9e<<\x97\xba\x86\xc0:a\xa6Y\xfd\xe8\xdb\xb2\\mrV\xd7\x8e\x89\xaa!\x88\x8f\xd5\xd8{\x0d\xec\xbeF\x14hI\xf0/\x01\x12\\\xdc57e\x81\x10A\xf5\xfemY>Z\xadV\x8f\xb1\x85V\x04x\x84\xfe&\x99@\x92\x85J\x15\xd1\xe8\\\x11\xe5\xd5\xeb\xf7/\xdf\x9d_\\\xbe}\xf7x,\x14A\xa3W\x8c\x82w\xa0\xba\xc0\xc9\xf1\xbf\x03\xe4\xf8\xae\xb4)!I\xf1\xec9\xfc\xd3\xe1j\xf5mY\xfe\xf7j\xb5\xfa\xab\xfd\x11+\xee\xce\x84\x1a#\xbe<\xa8\xc3\xfbGV\xd57,\x17D\xc2\x07\x8a\x91b\xdc\x1b\xd2U\xb6\x1bu\xf4\xa1\xd8w]\xc9\x81H\x86\x94_\xfd\xaf\xe7Pd9\xca`x\xff#N\xba\x94\x1e\x8a\xcd\xa7V\x06\x19\x85\x12\xae\xee\xba\xe3\xddH\xc9\xcfY\x9e\x8b\x1ft\xce\xbd8\x12\x87\xe8\x1e\"\xc7\xf5\x13q7\x92\xd5 VB\xb5y(t\xdcVb\x0bin\xb2A\xd5\x8a\x0d\x11\xb6\xa2\xb1\xc8\xef\x8c>o]\xb6Z\xb5I\xdf\xea\x1bs\xc7{\xf8\xe4\xe1\x10\x9d\xbeP\x98\xae\xd5\x0d\x82k\xeey\xb0+\xcb\xd5\x15\xab\xe4\xa0\xbf<\xb9[\xfd\xf9\x81\x9a\xb1\xd2\x8bm\x15_v\xf9@|'\xc4\xf3\xe0\xa7\xdf\xbf\x7f\xfbf\xf8\x97\xe7\xcf\x9f?\xb7i/\xbe\xeb\xee\x96J\x9f(\xc5v\xd1\x87\xa9\xd2\xaf\x8fu\x9b\xabp}\xccY5\xc4c7odT^w\x0c\x9e\x01\xdf_\xf1\xed\xb6;\x10\xcf\xf4\xd9:\xba\x91\xf6\x8e'e\xdd\xfb\xf8\x1fb\xda\x1f\xb5 ePP\xcc\x10qe\xb6\xdf3DAd\x9bOb\xefu\x17\x8a]\x96s[\xbe\x99=z\xc1\xab\xba,Pv\xd67\xff]V\xd5\xcdZR\xfe9<\xb51\xb5\x1f\xca\xd2\xa3\xfa\xbb\xaf\xc3\x12\x15\x00\xed\xf5\x81\x9c\xff\x83g\xf0\x00\xe3\xec\xe1\xb4Vj\xf4\x0f\xce0\xc7\x00\x8e\x1a\xbc\x11\xad\xd3\x967\xf3\x8c\xdd\x88q{\xa2i\x1b1l\x07\xf5$K\xf1\xf3\x9f\xa0\x13\x0d\xda\x9d\x01\x1b\xa3\xefo\xc2}'6e\xdb\x86\xec\x04f\xec\xa4F\xec\xf1a8\xd3\x80\x8d\x18\xad\xe7\x98\xac\xbd6[\x87\xb9:h\xac\xb6\xedctC\xb5\xdd\xf6\xaf\xd8\\'\x99\xa8)\x93\x0d\x99\xa7\xdds\x0b\x9a\xa6#\x0c\xd3C;\xc4L\xa3\xb4\xd7$\xed6H\xfb\xcc\xd1(\x15\xa8\xa6\xe8\x90!zl\x86\x9ea\x84&\x98\xa0\xe3\x0d\xd0\x88\xf97d|NdzFz\x1epJR\xa3sb\x93sR\x83sJs\xb3\xd3\xd8<\xb6\xe0\x8d\x0d\xcdi\xcc\xcc\xc9\x8c\xcciM\xcc4\x03s\xd0\xbcL4.SL\xcb\x96a\xd9\xee\x8djd\xf4\x1b\x95\x89&e\x82Ay0\xe4\x94\xc6\xe4\xc4\xa6\xe4t\x86\xe4tf\xe4\xe9\xab\x1b4!\x87\x0c\xc8J|{\x8cwS,w\xed\x83\xb2\xef.^j\\\x96\xbd\xee\xba\xbc\xed\x95\x03=\x94uF\x0ff>\xe8\xda\xd6HqiJ\x1c\xf3VuW\xfa\xeb\xa4\xe3\xd5Z\xd3\xc6\xd8\xfe\x8d\x82\xfa\xe3\"\xfa\x8b \xa1\xfc\xe7Es\x9f\xa1\xfc\x9a\x81\xba9\x991\x9b\xb5\xd6u\x92\n`\x1bi\xf1\xeeW\x19\x91\x7fm\xb26\x82\xc9p\x97\x9fY#\x0b\x84\xee\xb3b\xbd\xed\xb39,,\xf5+a)kj?fE\xb6?\xee\x0d\xef\xe8\xc4\x0e\xc3\x16\x82ex!46\xf5\xe4\x02\xa8\x84\n\x83k\xcf\xbe\x98\x85\xa6\xa5Z\xb8\xed\x07?\xb2/r\x1c\n\x8d\x1c\xc6\x0b1Sqh\xf1J\xf2\xae\x19\xa2 l\xc7\xb8p^dM6\xc8\x9bWf\x16\xe8?\x9e\x08\xfb\xb2hn\xb0D\xef\x01\x8b\xdb\xe5fj\x9d\x1c!?\x12Z.\\\x97\xb7\xbc*\x98\x10\xf9f\x10\xb5c\xfb\x98w \xc8;g\xa6\xb0\x97\x86m[\x8e/~\x9c\xc5\x8f\xb3\xf8q\x0c,~\x1c\xd3|\xf1\xe3,~\x9c\xc5\x8f\xb3\xf8q\x16?N\xef\xff\x17?\xce\xe2\xc7Y\xfc8\x8b\x1fg\xf1\xe3,~\x1cX\xfc8\x83\xcf\x16?\xce\xe2\xc710}u\x13\xf9q\xd4+wG\xab\x14\xd1H\x07\xb7\xdfc5\xef\x86\xae\xdf_\xbe\xb8\xfc\xf0~\xfd\xe1\xcd\xfb\x8b\xd7/\xcf\xbf=\x7f\xfd\xca\xfb\xdd\xab\xd7\x17o\xdf\x9f_\xae/^\xbf;\x7f\xeb\xff\xf4\xa7\xb7\x97\xe7o\xbe\xa3|y\xf1\xe2\xfd\xfb@\xbf\xef^\xff\xfe\xf5\xcb\xcb\xc0G\xdf\xbe8\xff\xa1\xf7I\xfb\xc8+e\xb2n[\xb71`\xbe\x97\x94\x96\xb4\x94\x9ao\xaf\xf6\x9f^\x05\xf5,a\xcf.;\xe0\x17/\xd1\xbd\x83\x1c\x15NW'\xa1\xec\xe4P\xb3\\w\xbe\xf2\xf75\\8\xbb\xbb\xe1\xef=\xcb~k\x06W\xdd\xc0\xf6(\xc5\xa0\x1a\x8a\xb4\x08\xf7:\x1e\x9a\xc6\x1dC\x190\x86=\x92\xc1\xcf\xb4\x81(\xc3|\xec8\x14\xdb\xd9\x03P\x7f\xf7\xf4A\xfa\xf6aQ\xd2\x90\x16\xe5Zh6\xeb[\xde\x94\x84\x06\x03\xc1r)\xe6\xfeNN\xbdGVYn\x8cU[\x90\xa4\xd1\xee'\xc4\xc5b\xe8X\x1f\xaf\xf6Y\xb3n\xb2=\xf5\x19\xdc\xf1c\xbe\xc6a\xc5\x8b\xed\x1c4\xea%\xf2\xc5\xcd\xf9+us\xea'\xe2\xeb\x86U\xb3\xb8E\xe3\x99\xc5,\x03\"\x9b#u\xf4@A\xc55e\xf6B\x1f\xac\xb4|ql\x04\x8f\xab\xf1}O'\xb2\x86\xd8\xd7\x85(zP\xe8(u~\x86\xe9?\x01\xdd'\xa8\xf7xu\x1e\xb2\xbe\x833|\n=\xe7\xbet\x9c\xd0\xa2\x9cB\xbf\x19\xe9\x14\xf7\xae\xd7\x04\xfb?\x8d>3\xd2e\xeeS\x8f\xc1u\x98\xfb\xd2_:\xdd\x05\x115\xfa\x11\"\xc9\x1f\xd1\xb9\xa6\x8e\x03\x93\xa4\xfc\xa0\xd1\x11@q\xbeX\xc9\xc6HX\x9c\x07\x11v\xa4b\x07=\xfc\xe3\xbeg1Y\x19\xb00\xcd\xd1\x08,d\x89_\xad\x18h@I\x03\xed`\x14l\xe7\x8dC\x1dm.J\x18\xaa\x19,1\n\xb5\xdfM\xfc\xab\x11&\x1c\xea\xb4j\xafs\xbb{\x19\xd9S_\xc0\xb1\xe9'\xea\xd2\xff\x83\x1fPM'\x0c\x12\x8b\x83\x93<\x92\xfa\xef\xe3*\xe1\xb3\xc4\xc2\x90\xbf\x94\x88@\xd9\xde\x8cg\xa9[\xb2\xd4-9}\xdd\x92\xf1Y\x10q\xe6\xd4Q\x87\xce\xc4\xb7\"\xf5=\xdc\xfbd\xe4\xe0\x1b;\x94W\xbd\x1cYq\xf5\x08CS\xea\xef'\xed\x06\xd3\x15\xf2\x9e\x91\x93\x83\x06c\xfd\x81\x17\xd7\xcd\x8dq'\xa2\xd1\xd5md\xb5o\xce\xc3\x8f\x08\x93\xd6\x0d&\xcd\x1a\x0d\xec\x87E\x19\x9e~\xfe%=\xfd\xe6+\xc30\x9e\xfc\xac\xf4\x00\x08\xa4\x08\x00y\xb7\xd8\xe1q \xd3\x05\xc0\x912\x00\xa3\xb4\x01P6j\xff~\xec\x7fB\xd8\x8d\xf2\xf3I{\xf1\x97cY\x1d\xf7DbN\x0dB\xd4\xab\x7f\xe0\xd5\x86\x17\x8d8M\x85\xc0\x92\xa7Y\xdd\xb0O\xbc\xf7\xf0\xc4m\xd9p\xcd\x1e\xeax\xb3O\xe7++\x94uS\x16u\xb6\xe5\x82!\xa5\x91\xad\xcf9\xcdM\xc5k\xb1\x9e\xf74G\xc1!U\xa3c<\xfe\x8b\xd7rF*\xfd\xa3\xcf\xefB\x1bY\xc1+e\xa5\xc3\xb9\xe8\x9fW\xff\xda\x9f\xc8-o\xca\xf5=\xcfF\xa9\x02\xe5\x0e~\xe2zm\xe4\x9e\x90\xcf\x9b\xeb\xff\x95\xee\xf7\xf1\xf4\x82+&&\xc3\xb7-\x01\xcc\x94\x9f>\xf9\x17\xe4V{\x82\x07\x86m}B\x0f>Z\xa30\xb3~6e\xf7%4EYI;\xe0;#\xdd'd\xe3x\xc3d\xf6m/a2\x0f\x04\xde2I\x99\xd4\x03)\x13{\xc0\xff\xa2\xc9\xac\x04\x1fH\x98\xe4\x03\xc1D\x1f\x98\x9a\xec\x03s\x12~0\x8a\xdd\x1d$\xbf8_7\x99\x91\xf8\x83\xe0R\x8a\x94\xf3\x85\x93y @\x08\xba\xe3\xc1\xf9\xcaI\xeaD \x98\x9f\x0c\x04\xe9\x13\x82`^R\x10\xccK\x0c\xc2\xb7(:\xc8d\xe9B\x90+\xb9 \"\x88\x11Jr\x82\xe0\xbc\x83\xc9N\x10\x97\xf0\x04\xe8\x0b\x003\x13\x9f \x94\xfc\x04\x81\x97UBo\xabx\xa8DM\x86\x02BB\x14\xa0o\xac\xccJ\x8c\x02Zr\x14LJ\x90\x02'a\x82\x89R\x90.Y\n\xdc\xa3\xb08-i\xe2\x14\xccL\x9e\x1a\xa1\xc2^aI\x9cN\x05\x89S\xaa\xc0\xff\x16\x0b\xf6\x1a\x0b\xf6\x1eK\xaa\x14+H\x99f\x05\xc9S\xad\x00\xa8\xe9V@I\xb9\x02z\xda\x15\x10S\xaf\x00\x7f\x9f\x05\x7f\xb1\x83\x9e\xa8\x13z\xa3\x85\x9c\x8a\x05\xb4t,\xc0\xa6\x912-\x0b\xe6\xa6f\x8dp!o\xb7\xa4L\xd6\x82\xa4 [0\x9b\x1f\x82\x89[@H\xde\x82\xc1;.v\x12\x17\xf8n3\xe3d.\x08\xc5\xdd\x06\xbfu&u\xe1\x9f\xbb\x12\xbb\xf0\xaf\xad\xe4.\xfc3$\xc1\x0b\xffp\x94\xe4\x051\x81\xcf]\x03w\xc4\x7f\x9a@h\x03\xf7\x15\x10\xed\xee\xef\xf4\x81\xd1\x06\x90\xa4+|H'\x0e\x94\x8e\x1e\xcfi\x02\xa7\xdba\xd8\xc9`\xf80N\x17Hm\xc0\x9d\x14\x86\x8f\xe84\x81\xd5\x06\x86\xc9a\x10H\x10\x83i\x1e!$Y\x0c\xfc\xb6\"4i,\xd0\xc6N\x1e\x0b4p'\x91y\x1b\x9e \x99\x0c\\ e\xde\x81\xb8R\x85\xa0\x17\xb3c\xe7\x0bMF\xe9H4\x83%\xec\xe6\x1f(\xec\xc6\x93\xae\x06S\x19\xc7\x99\xba6\x11c\xf246o@\xe2\xd8\xafO\n\x1c0\x83\x8a\nH\xd4\x8d\xe2c\x12\xcd\\\xfe\xde\xe2\xe0\xd1\x88\x83\xc9\x02\xa2q\xc6\x1d\x04dDx\xc3C\xfa\xe8\x83P\xfcA\xea\x08\x84\xc41\x08\x81(\x84\xd9q\x08i#\x11(\xb1\x083\xa2\x11\xd2\xc6#\x90\"\x12\xd2\xc6$\x10\xa2\x12\x92\xc7%\x04\"\x13\xa6\xc5&\xa0\x88\xbc\xf1\nI\"\x16\x881\x0bh\xcb\xa88\x86\xd9\x91\x0c\xa9c\x19\xdc\xd1\x0c\x89\xe3\x19N\x11\xd1\x908\xa6\x81\x1a\xd5\x908\xae\xc1\x1f\xd9\x90<\xb6\xc1\x1d\xdd\x10\x11\xdf0=\xc2\x01E\xe6*\xab\xaa`F\x94\x833\xce!\xa8Rxc\x1dh\x1aG\xbax\x07\x7f\xc4Cx4I\xa3\x1e\xfcq\x0f\xc9\"\x1f\xe6\xc6>X\xe8\xa4F\x83*\x0fi\xe3\x1f\\\x11\x10\xf3c \x08\x8e\x7fo\x1c\x041\x12\xc2\xe9N\x8d\x8c\x86p\xe3A|L\xb3c\"b\x88C\x89\x8b\x08S\x81\x14\x1b\x11\x1d\x1d\x81{\xe0\x12DH\x10b$BQ\x12\xe18 /\xd5bb%h\xd1\x12x\xbc\xc4\xec\x88 r\xcc\xc4\xd4\xa8 7\x99H\x91\x13Ic'o\x0d\x0e\xf4i\x85\xb50\x00\xc1\xd5o\xe5'\xb8\xc1A\x96\x03\xd3|aviv\xcb\xf1X\x16t\xff\xa2_\xba\x97\xc79\xc1\x9e\xf0(\xca\xaf\xcaC\x7fh\x0e\xff\x89\xb5\xaa\xcf\xc6\x7f\xe8!\xbd\xd3\xa5\xc2bPj\x16x\x86\xfd\x11\xfa\x85e\x95P\x8e\xc6\xff\xe6\xed\xb3\xd1\xff\x0f\xa80\x01_\xc7cc\xcc\xdd/\xc3>\xa4R~k\xca\x8f9{\xfb\xcc\xb3\xeb\x9bY\x91\xe2\x7f\x90\x18\xf8\xb6\xc7\x92\xdd@\x8eE&c\xf2\xda\xf2t\xf2\x1f\xf5!\xef\x97\xc0t[\xd8\x04\xce\x1e65\x93\"|\xe7|\xa1Z\xca\xfavu36&\x9c\xbf:3\xe6\x1f^\x9d\xb5\x8f2Zt\xb2\x8b\xa9\xf5\x0f\x13\xcaq%GA+\xa4&>\x8d\x8f\x81\x96\xb5\xe4\xc6'\xd5\xaf<\xfe\x199\xd8<\xa8\xd0\xc3\xed~o\xe2\xeec.x\xd0\xf9\x8e\xba\xd8\xc3\x8ez\xdc\xc5\x1cx\xc4#/\xf2\xd0\x9bt\xec\xd1\x0e\xbe\xc4G_\xd4\xe1wO\xc7\xdfI\x0e\xc0\xd3\x1f\x81\xe9\x0f\xc1{<\x06}\x07a\x8c\xd1l\xfea\xe8w8M=\x10S\x1d\x89\xd6\xf8T1Ss\xf3\x17m~9\xf2*\xe3j\x1f\xd6\x8b=i\xb1'\x9d\xd2\x9ed\xabNT\xb5\x8cP\xe0V\x1a\x1b.z\xe5\xa6 \n\xd9\xb8.4\xba\x14(_\xb9\xb7\xfd\xdc:\xd0\xa3:\xc2\xae\xaa\xcfH\x8d\xe4\x89\xa3\xffwt\xf4\xd5\x84\n\xcfv z\xaf\xba\xb3\xab\xb2\xf3\xc4Q\xa34\x9fZ\xc9yD\xf3\xa8\xba\xcd=\xbe\x1b C\xf9'\xc9\xcb\xe2\x93\xac\xb8\xd6C(\x0b\xec\x00\xa8}|\xfd\xae\xe7\x18%\xf0\xf5\xc8\x92\x89\x90\xd8\xb2`\"\xdf\x0c-\x97\xc8\x07\xb8\xc5\xd2\xfa0\xb1\xa5\x12\xa1\xd1O\xad\x05\x90@\x1c\xf4\x8a\xe4\xe5\xc2\x81\x00\x1e]\x88\x90\x86\xd6%\xe8\x14\x17=\xfc\x9a\xe3\xd1}\\\xc1FT\x9d?|\x9d\xa1]d\x82W\x18\xf2\xe5%\xf2\xda\x12\xba\xb0$\xbb\xaa\x10/)'\xbf\x9e$\xbe\x98\x9c\xf2J\x92\xf22r/\xd7\x10\xfc\x02\xe2\xd8~ /\x1d\xf8\x198\xe5\xa2\x91\xe2\x8a\xe1\x10\xc5o{\xb2\xc9\"\xc9\xf0]]\xff\xd6u\xc9\x1c\x9f\xa4q\xc8\x97\x80T!\xc9\x12\\\x82L\x95\x1b\xbd\xc5\xd1\x98liq\"\x19\x91D2\xa4\x97\x07\xf3\xa5\xc0 \xf7>\xce\xebYq\x1dy\xe7p\xbc\x86\x85\x8a\x8e\x01\xc3\x05_\xc1\x1a|\xdd\x1f\x9bK/\xd58\xe2\xf4Q[~\x91\xa7>\xd6Y\xd09\x8f\xf5\x94\x90\x8c\xb0\xbf\x19\xea&!\xad\xc4\xa3\x8f\x104\x11\x92\xdc\x00\xaf\xf6\x91N~\x00*C\xec\x99$\x93#6\xea\x89\xb2\xc4F\x94B\x9e\xd8X\xa7\xc8\x14\x0cKB\xb9\x02\x886\xe1\xbfB\xcd\xd1 \xf4\x86\xdegE\xd3=\x87\x1f'\xc0D\xdb\xf5(\xde\x11\xdd\xc9\xdaH\xd5\xe8\xa4\xe8M\x99\xc9t\x11\xd1^\x7f\x93\x15\xbb\\\xda\x0b\xd7\x82\xe9\xd7\xaa\"\x01\x0d\xeb^?j\xc6\x8a\xe2\xc8r]\xcc\x00\xb2\xa2\xc3)\xae\xfa\xdc\xeah\xcf\xbe\xc4u\x10\xc2\x17\xb8Dw\xf8\xb4\x8d\x02\xc5w]\xb2|}U\x16[\x1e:\x1246\xd1@\x10U[\x99@\xb5\x05\xd6\x94\xfb\xf6U\xf4\xbc\xdc|\xaa\xc5A\xb3\xbe\xe3\xcc\x7feu\x1a\x1buw\xaa\x90\x0b\xdfj\xa4\xa2[\x10Hm\xe6\xd4'\x8eJ\x1c\xea\xbd\xe3flzb\xf5a_n\x8f9w\xf1\xa34\xfb\xbd\x90\xebzQ\x95\xb7Y-$_\xb4\xb3U1\xc6\xfa\xd0b \x12\xc0mr\xb20\x1a\xeb\xa5.\x85!\xe7&\x93\xad\x14O\xf6>\x94\x16$\x9f\x06\xef\x9d\xb3\xd7J\xdaG\xf0d\x8c\x017\x9b\xda\xd4>7L\x19M\xe6\x96\x9dg\x93\xb7\xdb\x18\x0e\xb2v\x1f\xd0\xc8iM\x8abmn\x1b\xf9\xe2\xd6l\x02N|\xa9\xd5\xfb<\xa2\xffaD\xad\x07v[\xa9O|\x92\x7f\x05\x13\xe4\xe03\"\x85\x05:\x84\x85:\xa5\x878\xe1\x0en\x01\x1f\xd3\x19\x0571\xe6\x93&\xf4\xc1%\xf8)\x98 \x07\x00\xf8\x0e\x01o\x1fa\xcf\x13\xfd@8\xc1\xbb\x86jc\x0c\xf5\x97\x97=6#l\xbc\xfaxU\x1f\xd8\xc6\xafl\x0c|\x8f\xc8\xef#w\x87_kkeSo\xb8}E6+\xb6\xd9m\xb6\x95\x07\x87\xd9\xe5\x9a\xf3UV\xbd\xaa\x9f\xd6G\">Q\x88.l\xfb\xf8\x88H\xb3\xa5\x94OH\x0dd\x94q)\xb7\xb3\x98$\x9c\xec\x05\x027\xc7R\x9d\xc4H\xa1$\xff\xa29\x98w*\xe3\xd69\xabo\xb2\xe2z\xaa\xea]g\xd7\x05\xdf\xae\xf5\xa6\xfe\x9c\x15\xdb\xf23\xf1\xdc\xed\xef\xe4}V\xac5*!\x18\xa2\xf0\xf4\xce\xefm\xf9\xb9h\xb2=_\xff\x89e\xf9z\xabS\xaa\xbdx$\x01\xd6;Y\x86\xb0,\xd6\xdb\xf2x\x95s9\x96\xf8\xee-\\j4\xb1\x880\xed\xb5M\xaa\xb7N\xdd\xb6\x10\xd9\x95r\xfa\x9b\x15\xb5UZk\xadg\xef@K\x83\xa5m$/\xcf\x00\xe5\x18\x18\x9f\x02\x01\xfe!\xe1\x1cEi\x84x\xc9\x83\x93\xc2S\x9e\xe6\xce!\x05\xf9+\x1eir^k\xcf\xe3T\xa7l\x88\x7f\xdfg\xd7EV\\\x9f\x17\xbb2\x9a\x89oY.\x97%+\xae\xd7Y\xb1\xb3\\\xc6$vf\xdbm\xc5\xeb\x9a\xb4\n*+\xf0\x06q\xd5\x04\x97\xcd\xa9\xf8|/\xb1\x013 \x8f\xd2F\xc7\x9a\xb2\x92\x811\xb2\xb8\x080\xd8\xb0b+\xfe\xcc\xe1\xed;\xf9\xc3\xb1\xf8\x93\xcc}\xee\xa1\xcc\x8a-\xff\xb2.w\xbb\x9a\xcf\x1f\x9d\xdf\xb9y.\xba2\x19\x9a5d\xc5\xa6\x92I\x9a|\x0b\x9cmn@\xf0ugqlg\xc3\xb4V9v^f\x85\xf8I\x08\x14\xe9\x18\xda\xb3;U?S\xc9\x04\xe9F\xe2\x9br\xbf\xcf\x1aUx\xb5\xd1\xf5\x80\xad\x82u\x9b\xb2\xf8\x93.F\xa7\x0cdHa\xd7\x8f\xef%\xd6o\xa4\xfc\xfa\x83\x145\x1f[\xad\xa3\xe1\xd5\xbeU<$A\xf1\x1a\x94\x1f\x7f\xcc\xea\xda \xf9&k^T\x15\xbb\xfb\xd87\xf1\xa9\xe5Y\x1f\x8b&\x8b\x8d\xcfr\xe5<\xfbV\xe42\xdb\xf3\xbaa\xfb\x03\xc8\x1e\xf5\xda\x0c\x97 \xab\xf5\xa8`{\xe4\xe2\xa2\x95g\xb7\xbc\xe0\xf58\xaf\xdeH\xa6\xfet\x9ar\x7fU7e\x81_+\xae\xca2\xe7\xac\xc0\x05\x16\xf2\x9b\x7f.\x7f\xb8\xe1\xb2@\xa6ZkS\nRN\xe1\x86\xd5\xaa\x0cA7\x1ex\xf4)\xcb\xc5\xa4\xcac\x03\xe58\x08\xadkZ\xf3\xe6\xf1\n\xcee \xd0\x9a[\x15\x9c\xcbb3fY\xc5rR\x99\xe6\xbf\x1c\xb3\xdbR\xd5\x8f\x15\xe3\x92f\xf3\xe2N\x05\xa7\xd9L\xb8\xcb\xae\x8f\x15\xdf\xc2>\xab\xaf\xf8M\xc6n\x87\x85i\xf7\x92y\xcc\x11*c\xe3\x88\xc9^\xd3\xb7\xec\x0b\xd0\xfd\xc0'~h\xba\xda\xb3\xc7\xa2\xe0\x1b^\xd7\xb26\xb3\xe0b\xa88\xdb\xd6V\x80\xc1\x9b\xb2\xd1\xb5\xbf?\xbe?\xee\x1fa\xfc\xff\xf8#\xb0\xfc3\xbb\xab\x05\xb9X>f\xab\xc1\x9ey\xa9\x06\xd3\xdb2\x1e\x87\x86Y\x91\xdea\xd1\xf7N\x9b\x9f\x1f\xd6\xa0\x8f\x03q?VAY\xfb\xb2\xc8\x9arD\xc5\xe6\x86g\x83\x92\\f#\x808\x9bo\xb3\xe6\xaew\xc5Pbz|\xd6\x98\xc3p\xd0\xa1\x8c\x8b\x94\x01\x8f*\xbaN:\xc3\xcd \xf3\x9b\x1e\xb6a6$r\x06R\xce\xda^3\x8d\xed\xdd\xc5K3\xab\xe8\xd37^\x87\xc4\x8e\xdc\xb41I\xe8\xd9\xec\xd9\x1b\xee\xf3\xd9\xd3\xc8\xb7\xa5NpJ\xfb\xce\xe9\xc9\xa3\x0c\x85\"%>\xaf\x13\x9e\xd8\xb43;\xd1\xa9M9\xb7}'7i}\xf0\xd3;\xbcF)Op\xfc\x0cw\x9f\xe2\xbes\xdc\x7f\x92\x87\xe7\x95\xf24\x8f8\xcfS\x9e\xe8\x943\x9dt\xaa\x93\x18h\xca\x06\x9f{\xba\xa78\xdf\x83'\xbc\x7f\x1a3N\xf9\xe12\xb4'\xbe}\xceSNz\xdf\xe9\xce\xf2\xbc\x1b\x88\x99?\x96\xdd\x12q\x01]\xb2RB\xbe\x81_\x7fV\n8\x8d\xf3]\x97=\xc5\xae\x1cT\xd4\xcc\n\xb8~w\xf1\xb2\xd3\xf5t9\xcb\x1a>\xdf\xf0\x8a\x8f\xce\xb1MY\xa9\x0fe\x19N\xadn\xb6\x150\x85D\x95f\x9e\xfe4\x07s3_\xbe/\xf7\xdd\xa0\xd0\"\x98\x15?pY\xbd\xfd\x1bV\xb5\x94u\x95k\x1d\xccQ\xb2\xc7\xb8X\xab\xaam\x19R\x80\xa3\xacM\xfdv\x1a_\x8c\n\x8cI\x1c\xb2\xf6k\xe9\xa6\x08\x83\xe3\xfa(\xba\x13p\xe1\x9fT\xfb\xc4\xf5\xce\x88\xd1\xb8\xa5wB\xfdr\xbaf\xd9\xd7${\x081\x9dr\xb66\xe9\xd7#q\x0d\xd2K\xe9\xc0\x8bo\x03j\xa7\xd2\x14\xc7:\"\xa6\x1d\xe2z\xa1K#t\x8f9\x95\x16H\xd2\xfffh~=M\xaf\x87\x10\xd1\xf9\x02\xda^\x92m5G\xab\x9b\xa7\xcfy49G\xd0\xfat\xed\xcd\xb6\xd1\x0c\xf46Tc3\"\xbd\x91O\x0f\xb5\x12\xfd\x9b\xb2\xd8\xbe\xefUM\xb6Va\x98,\xf0\xcd\xdb7\xaf\xdc%\x80\xc7\xbf\x8b\xff\xf3\xfex\xfe\xe6;\xf4\xd7^\xc36\xbc\xd7\xd75\xaeItsk\xb5\xd3^\xad\xd4\x96\xc0\xfdX\x7fG'\xcf\x00\x8d\xcb- +t\x84p\xb7\xc9\x86u\x83q\x92\x08|\xea_\xd8\x8a+&\xd4\xef.)y\xef\xc5w\xfe\xe6;\x83\xf0\xfc\xcdw^\x8c\xc7\xe2J\xe9A\x0e\x84fx\x84\xc1u\x03s\xb0\xd6K!@\xea:\x8b\x08O\xdf\xb4Md\xe4\x92+,k\xb0\xe5\xc7m\x06\x91\x10\xf2\xd17iV4\x1f\x81\x0e\xee\x96\x1a\xa5\xf1\xef\xf5\xd1U\x9c\xa9\x10\xc2>\x87\x98_#n)\xa2\x1f\xa2B?\x98\xdcC\xd1\xb0\x0d\xc0\x1b\x8e[\xbe\xcax\xadn [\x9e\xf3ky\xab:\x03\xa9\x19\x18?\xe9\xeaa\x0f\xf9\x9e}YO\x1d\x89u\x8b0\xc8\x06$6\xc1[\xe3\xa1\x8e\x15\xaf\x8de\x07\xe1\xb7*\xbe\xa6\xba\xe6\xe3)\x8cf\xa0\x82p\xd2N\xa4\x87\x13\x9d\xcf\x96e\xf9\x9dR\xccX\xedx\xa2stZ\xca\xd9;\xa6r<\x08\x85e\x1d\x11\x1f\x11Ppz\x08\x0d\xb7\xc8G:[\xcd\xd1Z\x10V\xebh&,[\xa5\xdb\xad-5z\x08Fa\xc4&\xffa\xb0C\x82b\xe0]\xb7\xa5 \xb2`\xb8\xd8(\xa5N\xb4o\xec=\x13\xee}\xc0bs\xf7\x89\xdc\x19}\x99\xe4\xdb#\xce\xfd1a\xd0\x93\xf6D7\xf6\xfe\x90\xfd\xfb\x01\x1f\xc6\x88UfHr#\xc5\xcd\xd1\xceH\xac\xfaJ\xb1F\xcc\x89\xd5r\xd3\x9ar\xbd\x1dM\xdcjl\xd8\xf7\x8aon\xfe\xe5\xeb\xafx\xb1)\xf5\x0b\"\xf2\xd7\xf6\xc9\x1b\xdd\xcc\xb0@;\xb7)\x83\xb0\x1a\xd3\x06a\x1d\x8e\xf5\x0d\xabxT\xcf\xaa\x85^\xe5\xfe\xcc\xc4\xe2\xea\x1f+\xbe\xe1\xd9-*\xb1Z=\xaf[\xb6q(\x8f\xd0Q\xf4u\xb6\xfc\xc4\x8b\x1anx.kY\xb3\x02\xd8F^\x15\xf4\x85H\xa3*?\x17\xaa\xd8uY\xf4\xe8\xac\xdf\xc2\x94\xaf\xbb\x96\x9bL\x9ax\xcc%\xb9M\x03,?+\x8bZY\xf0\xf1\xb2\x90X.\xda\x9b\xb8\x1dq+\xda\x0co\n~\xce\x05\xf2\xc9\x9a\x8a\x83\xc1\xcf\xc5\xf4\x01\xa5\xe2f@8\x9a>\x8ax\xce\xb6P\x0c\xec\xa1 9\x1cRs9\x8c9]\xc0\x15\xcbYa\xc7v\x13\x19\x93\x9a\x0f\x81\x15\xee\x0f\x8b\x9d\x01e'\x17\xe8\xefP\xcc\xa9\xcb\xdf\x99\\\x0d\xd0\x8a\xef\x87\x04a\xdf&,\xad7,\xe7\x85\xb4\x87\xf4X\x89\x7f\xd9H#\x89\xbc\xd05\xbd\x87[\x87\x8b8~lJ\xf3ok\xbb\xdc\x97\x15\x87\xfa\x985\xd2} \xd4\xc3M\x9e\x89\xde\x8c!\xba\xf6\xca\xbfn\"T\xc1\xb7/\x8b\xecS\xd0t4 \x91n\xd2[\xe9\x9b\xe3\x9e\x15_U\x9cm\xe5\xb0\xe5ki\xc6Vn\xb1\xb3z\xb3\xbf\xf1\xe7\"\xf8\x94+\x83\xa0o;P \xa8,\xef~l\xd7\x1e\x1e\xf1/+\xf8pQV\x0d\x0c\xd4\xaa\xff\xe4wW\xac\xe6\x8f\xcd\xc0>\xf3\xab:\x8bS\xfat\x13t$\xe6\xb7<+>\xb5\xe7:\xdf\x1c\xab\xac\xb9[K\xfe\xd8\x84\x0c\xe1C98j\x8bv\xca\xf7,\x93\x0f\xee\xb7\x9f\x83\xfe\xdc\x0ca\xcb\x1b\x96\xe5Q\xba\x85nbD\xb0\xb2T\xb6]\xea_\x91\xbd\xd4\xe3G\xd4\xfa\xd1\xfb\xd8\xc3\xd5\xdfguSV\xd9\x86\xe5Q\x1e\x92\x1b\xce\xb66_\x93\x84\xa6~\x88|$\x0b\x953\xe4\x8a\xd5\xd9F{\x08\xb2.\xf6\xc9\xd3\x07xB\x8ctF\xd3\xf8\x8f\x9e\xa3\x11\xfc>K\x00v8\xa4C\xe7\xbf\xf9\xbf\x14\x02\xa9\xa8\x8f5l\xd8A\x89Yu_4\x7f\xae\x8eyWdm\xc3\xebZ\xd9\x82\x0c\xf5F\xe8\xe4\x11,~\xda\xdc\xb0\xac8\xb3\xc3t7\xf9Q\xbd\xfe\x97\xe7\xbd\x0f\xc5\xdd\x9e\x89\xb9\x1d7j\x0c\xa6\xba\x8b\xea\x1d\xb55\xb0\xee=\xfe\x87\xf5\xb8\xa3\xba\x11W\xb6\xa6bE\xad\xe4\xf4\x9emn\xb2b\x10\xcc\"{\xa6\x16\xdaO\x12\xb2=!X\x1f\x8b\x05\x92\xcfYJ\xe2\xd9\xa3W\x1c~\xa8\xf8mB\x06\xbfa\xf5\xcdD\x86D\x1er?\xb0\xaaY\xd7\xbcY\x8f\xb7\xb7\x01\xe7\x08\xc1;J\xd9\xd4\x0e\x1bh\x7f\x92X\xb3\xa2\xe1\xd7Hx\x0e\x04\xc2g\xc0I\x06\x08\x91\x02\xfc\xe4h\x17\xed\x82UM\xcd\x9b\xef%U\xc6\xcb\xad\xbcOk{\x08A\x0er\x85y\x08Tjo)F\x11[\xb0\xf7\x9d\xf8\xdf\x04\xddu\xb1. \x90\x8d\xc7\xbe\xab\xca\xbd\x14\x13\xecp\x80\xf2\xd8\x1c\x8eM\xf7\xb7n\x0f\xf40\xc8P\x96\xa4cj%e\x02\\\xecpH\x80E\xf2\x8b\x8e\xb6H\x80\x8e\xdf\n}l\xc3\x13\xa0j\xd7\xaf;^F\xe2I\xd5\x8a\xe1\xb1W^\xb4\xbf\xc1\xc1\xa7v\xd5\xc0\x82\xd6\x1e7\xca\xffu\xc9\x8b\xad\xf4\xdb7zC(\xf1\xd4\xb3)!Q\x0fi\xa3\x95\xc5_\xdd\x17~\xaf\x98\xf1\x9f\xf26\xea\x01%\\\x97\xff\x87v\x80\x9e\xc1\xf3\x7f\xa5\x01\x01\x8c\xf9 +\xe4;\xbc\xab\xc1\xf7\xdd\xd68\x1c\xaf\xac\xf84\xaf\x9c\xf7Iy\xd1l}\xacP1\x1f\x10\xc4!*\x81t\x99\x7fx\xf7\xc3\x93\x8a\xd7\xe5\xb1\xda\xe8\x8b\x90\xbc\x12\x1e\x8b\xec\x97#\xcf\xef\xf4\xfdd\x97i\xea5\xba\xf8\x00\x16-\x0c\xf2\xc2`\xde\xe3\xc6\xeb\xc2\xcbk\xec\xa6\xcc\xe1\xea\xb8\xdb\xf1\xf6-b\x1d\x95\xa2\xe6\x02\xfbc\xdd\xdeH\x815\x90sV#\x11\xab c\x178V\xa3\x97(\xeb\x9b\xf2\x98o\xe1J]\xc1Ql\x1bV\x94\x85\xb8hH\x99\x80\xf7\xf8\x88\xaf\xaeWg\x82\x84R\x15}\xb0z`\x9c\xcbl\xb3\xe1\x87\x86o\x1f\xe3/\x0e\x02\x9c\x17p\x90\x86\xfb\x0d?\x83\x86\xb3}\x0d\xc7\xfa(+\xc4\xaa\xa8\xa2C\x96\x8b\xd1\xe97\xa8\xaf\xb2BFX\xe49N\xbb\xbb\x83\xe4!\xd6\x88\xaf\xef\xf0.U]\x03\xc8\xa4\xb5Be\xdc\x1b\x15\xbd\xe1_\xe4R\xbe(\xeeV\xf0}\xf9\x99\xdf\xf2Je\xe6\x7fx\xf7\x83\xbdw\x05(+\x85~y\x1f\xef\xb0\xde\xdc\xf0=\x87\x8f7Ms\xf8x\xa6\xfe[\x7f\xcf\xa8\xb4\x8c\xb6\xd0\xb1\xbc,\xae\xb5\xf7\xc0^2!5\xa5\xd0W\xa3\xba\xb2\xd5\xa9~\x9fF+B\xd8\xec\xc20~\x9e]\xc9\xa1j\xb9^C}<\x1c\xca\xaaQ\xcf\x02l>=9\x16\xe2?\xe2\xbcT\xeb\x8d\xa6\x93I\x8d\x06U\x1e\xca\x1d\x1c\x1b%|\xccv\xae\x85\xe03\xde\x00\x96\xc35/d\xb9\xcc\xad\xf6d\xb4J\xf5\x0bD\xde\xa9%\xb2\xfby\xfd\x85 \x06\x86\xa7\xcf\xe0\x82\xe90f=t\xd6\x1e\x88Y\x01/\x7f\xf7;\xc71\xf5mY\xc2\xae,\xe19\xacV+4\xdc^\x12\x81\x15w\xf8\x8f\xac\xb8[\x89\xae\xbf\xad\xca\xfd\xa3]Y>\xc6?[\xad\xf0\xb3'\xdb\xc1#\x81\xe2\x83\x1c\xf4e\xf9\xe8\x9f\x04\x8e\xc7x\x96\x80\x07\xcf_\xdd\xb4\xf9:@\x9b\xdf\xb3[6\x9b8\xf0\\\xeaV\x02\xfb\x0c*d\xf5\xa3o\xcbr\xb5\xc9Y]{\x88\xa0\x86$\x1a\xa8\xf9\xf4\x1a\xe1\xfd\"\xd4i\xc9\xf3/\x01\xf2\\\xdc57e\xe1 \x90\x1a\xc9\xb7e\xf9h\xb5Z\xe1\x92\xb8%\xce#\xe7\xef\x92\x81$\xd9b\xa9&\x1a\x9f+\xa2\xbdz\xfd\xfe\xe5\xbb\xf3\x8b\xcb\xb7\xef\x1e\xe3\xa67\xd5\x95b4wg\xaa;7\xb9\xfew\x80\\\xdf\x958\xa5$\xa9\x9e=\x87\x7f:\\\xad\xbe-\xcb\xff^\xadV\x7f\xc5?d\xc5\xdd\x99P\xd7\xc4\xd7\x07\xa5\x80\xfc\xc8\xaa\xfa\x86\xe5\x82\x88\xee\x81\xbb\xc84\xee\xd9\xd1m\xb6\x1bu\xfa\xa1\xd8w\xdd\xcaAI\xc6\x96_\xfd\xaf\xe7Pd\xb9\x93A\xddcA8\xf1RV\x8e\xd9|j\xe5\xa0Q\xb6\xe1\xea\xaeSU\x8c\xc4\xfe\x9c\xe5\xb9\xf8AG7[\xd8\x8e5r\xe6?D\xd4\x90'\xe2.\xba\x92?\x08U\xee!\xb0\xde\xa9\"N\x1c\xedY\xb0{\x90\xabnw\xd2\x8a\xf1\"\xbf3\xf7&\xeb\xc2\xdb\xaa\x8e\xc0v\x0dW\xda\x8c\xb8o\xdbC~\xf2\xd0\xeeB_\xe8\xcc\x10\xd5\x0d\x8ek\xce|\xb0+\xcb\xd5\x15\xab\xe4\xe4\xbe<\xb9[\xfd\xf9\x81\xa2\x96\xbak\xe0\xd7*9\x94\x07\xe2[q\xbcX?\xff\xfe\xfd\xdb7\xf6_\x9f?\x7f\xfe\x1c_G\xf1}g\x07\xd0i\x0fb\x9bj\x85A\xddU\x8e57 W\xd7\xc7\x9cU6.\x1b\x85\n\x05\xe9\x8e\xf9\xb3.\xa9M\xef\xbe3\xad? \xd6\x83\xde\xb1\xabB\x0c>\xfe\x87 \xc7G}\xc9m\xd5\x98>qWf\xcb?s(\xd1l\xf3I\xec\xf9\xee\xb2\xb6\xcbr\x8e\xcb_#\x1f.xU\x97\x85s\xdbh\x0b\x8eL\xb2Z\xcb\x95q\xa5\xc0u\x1fK\x1b\xae\xfe\xf6k\xba\xf4\x07p\x8e\xe2\x81\xa4\xcd\x83g\xf0\x00\xdb5\xc3\xe9\xae\xd4\x8c\x1e\x9c\xb9p\xc9\xb9\xbca{\x81\xef\xdf\xd4\x90\xff\xdd\xf9\xb1\x98\xcb\xe8[\xea\x84\xcew\xfab0\xe4 \xb5\x9aY\x0d\x9fy\x9e\x7f\xf5\xa9(?\xabx\x94\x1b\x19Y\xa9#Ip&\x1f\xb2\xe0\x99R@G|\xa9\x84Q\xaf[\xc1h\xc55r\xaf\x97lgw\xf2Qn\x08\xc3\x87\xaa\xf0p/\xc6En\xa7Q\x10\x89\xf8Y\xb3\xaf\x8dOv\xd3r.<\x92)/\x9a\x14\x96Y\xc1X\xce~\xfe\xe3\xcf\x8f\x1dL>\x97G\x86\x1d\xb9\xd9D\x92A\xa0{\xba\xfa\xfa\xe9\xd7\xf5\x03\xc7\xb2\xf7\xffO\xa5\x9c\xe1\x96\xe2S\xd4\x160)n:)\xf7\xb3N2\x1b\x98\xc1\xbb\xfc2\xfd\xb5\xf41\xe9\xc4\xc31B\x93R\xa32\x0b\x7fc\xff4\x9e\xdb0@d\x90\x9e3\xce\xa2\x81G\xaa\xd3'm\xda\x8a\xfe\x97\xb4X\x0e\xd1z\xae\xad\xaew\x97\xfc\xc9L\xee/\x07iM\xfe\xcf\xba\x04'\xd7w(2R\xd2\x93\x01\x15\xf87\xd5w\xa2\xc3\x06\xedPEi7\x93\xbf=\xca\x8aM\xbe\x82\x9a\xe7\xbb\xaf\xba8\xc6\xd1\nt\x91\xa0X\xd8$}<\x08\xcf\x8eQw\xbe\x1c\xf5\xa4\x9f\xfacV\xd7Ge\xebc^\x8fN\x17\xff?\x9e\xc0(\x12\x0d\xff\xa9\xff?\x03\xa7R\xff\xef\x0d\xaf\xf6\xb5;\x9c\xcc@3\xcd!d\xc5\xbfu\x100\xde\xa4\x8c\x89\xeb\xc0\x8e\x8e\xeb f@\x0es\xd1\xf4\xd89\x14]YYQt\x1dX\xf1t\x1d\xc4\xcc$6\xc6\xae\x03w\xb4]\x071#q\xd0tb`\x1e\x8ak\x14\xac\xd7\x81\x15\xb6\xd7A\xcc\x14\"B\xf9\x0c\xb4\xe7E\xda\xd2V!\xca\x8e\xbb5T=\x13\xd7\xd2\xf6G\xf5\xac\xd7\xcd\xa8BAs\x93\xd9\xb2j|$_\x1f\x0b$\x85s\xd4\xb7\x1duE\x9a\xf0\xd4*L\xc3\x8e}S\xde\x8b\x8b_\xb6\x1f\xbf\x9b\xd0A/\xc9\xb4\x94\xce\x8f\x9c7\xdc5\xe1\xcd(\xbd\xb4\x83\xc1\x887v^[3L\x10\xebr\xdb\x12\xc9gw\x16k\x07!\xaabx\"\xf2\xa1P|\xadC\xc4\x9f\xe3\xda\x81g\xfe\x10\x0cM\xc32`;\x08\x08\x00 \x92\x08t7\x13\xf2\xfe\x9c\xf8\xb0$\xd4\x0e\xf0\x84\xda\x0e\x12N,>\x8d\xd0\x89\xcaN/\x0c'\xdev\xe0M\xc1\xed \xf1\xd4\xa3\x93\x11\x9d\xd8\x1a\xbb\xb0\x85/U\xb7\x034i\xb7\x87\x97\xe6*sIV \x12dJ\xa6/.P\xfa\xd9\xbf\x06d)q\x9e\xef\xd6X>\x99\x82\x19\xba;\x82\xdd\xba\xed=\xac\xe5\xdd\x02\xb6|\x933U0N\xbe\xd9a!\xd3_\x19D\xfdy\xf8\xc6\xf1S/z\xdf\x8a\xe8?\x83\xa6\xbcV\x97\xe06\xd9I\xdd+t6\x8f\x8et\xc21>Tu\x10\xcc\x1dD\x1b[\xb3\n\xf8\x17\xfdd\x8a\\\x10y\x9ceE\xbd\x82\xf7\xba\xa4\xd2\x00\x9d\x89G\x18\x87\x0c1A\x12\xc5\xdd\xdaf:@{\xa6\x02\x1c\xa4_\xbe\xac*\xbei`\xc3\xf2\xcd17\xe6\xc6\x01\xb2\xddQh\xc7\xc3\x0e\x8eEG\xcdZ\xce\xbf<6\x905\xbcR\xe7Cy+\xef\x07\xed\x85 \xfep\xc3\x0b5\x15`\xd5\x90\x9f\xfb\xd7\xc6a/2\x1e\xa0O\xf0q\xdd\x9dM\xc5\xb7Y\x9bn\xc6\xfa\xc9r\x9fo\xcaz\xd8O[\xbbl\xd8I\x7f\x19\xb2.\xb4Cv\xd6\xad\xa4\x1az7T\xf9\xe0\x87\xb2\xd1\x8f\x15\x12\x1d\xab0\xece@\xff\x95~\xe2Qg\xc5 \xc9z\xc5\xdb%\xe0[i\x8b\x97\xac\xa4\x9f\x86Q\xc3\x1bb\xdc\x1f\xf3&;\xe4\x99\x1a\xc4\x10\xbf\xfc\x10\xe7\xeca\x86Ig\xfbUA\x9c\x92\x11{$.\x94(\x12\x04\xddY\xcf&\xcb\xe8\xcf\xd6Y\xa4\xcb\x035e\xa5&p`\x95\xa4\x9cNq\xd1\x95\xef\xc5\x96m$\x0b*\xed\xf5\xc0+\xf5\x86\xad\xb4\xeb\x15\x1f5\xb2}\xd9\x05\xedU\\\xbe\x923\x1c\xb8\xf9\xf1\xd1\xc7\xe2\xa3.K\xd4\xd6\xda\x1fw\xf8\xf1\xa6m\xba\xe6ESe\xbc\xfe\xd8)o\xf22\xe7H\xc7\x89|\xe3\xc3\xa5J\xa3bp\xb0<#]X\x0b:\xa5\x17k\xeb\xa1t\xaa\x8f5Zq\xdcuQ\xda\xe3\x1e\xc71\xfc\x84\xa2HC\x84f \xe6\xe4\xec\xca\xffu\xdf\xf4\x87\xa2\xa9\x9b`\x1cV\x85\x01\x8d\xba7\"0\x7f\x92^\xa0LJ\xe2\x96@}Q0H\x84\xabx\xef\x97G\x07^\xc1\x81e\xd5\x93\xa6\xca\xca\xf6Zo\xf3K\x82\x19\xd9H\xcd\\:\xaav\xdf\xb4\x93kJ\xb3G\xcc\xe8\xc4\x0cI\x8f8\x0e\xba\xefZ\x0dt\"\xf1gi\xb3\xd9\xa8|\xd6.q\x15\x11\"\xae'i\x87\xcf\x04\x0e\xf7\x9fow\x95eN\xde[E\xd9\xe8\xc7\xb6\xd6c\xbb%2y\xea\x97\x83\xe9u\xcf<\x95\xa5\xacy\xd9\x05\x82Ul#\xe7\xa4\xe5\xb1\x90\x92E\xd9|\xa5\xffW%\xff\xd6\xc7\xc3!\xbf3\xce:\xf1\xd3o\xcc:\x0c\xa9\xea \x87,\xb4\x98$\xad\x7f]\x0d\xda\xa2\xedq\x1cC\xda\xf1\xf5\xc3\xee\xe5\n[\x92b\xefV\x0cz\xeb>\x18\xdd\xae\xdb?k;\xae\x19K7\xea\x88\x9b\xdd\xf2\xfe\x85\xb3\xf8\xcb\xdf\xcd\xfb\x17\xb8\xc5\"\xa8{9U\xbe>\x82'\x18\x06\xc7s\xc8^\xe5\xef\x831\xc3\xcd\xd1\x02;\x9b\xe8\xfd*\x81')+\x90HOs\xe8hA\xfdl\xc6\xc8\x13\xe9d\x0e%\xc0\xb2\xaf*\xc0\x96W\x81\xb5\xc8\xfd\x06\x93J\xe1\xa8\xa0\x86\xb2p\x84\xfc(\x08h\x86\xe1\x9a8aB+\x18\x8d\xc6\x10[\xff_\xf7\xfeDg\xe9n\xca\xf2\x93\x03\xd9!g\x1b+\xb7\x11T\x84\xc8!\xe7\xb2\x1f\x97\x8f:b\xce>?\xf5h\xde\xa3\x8e\xcd\xf4\x8eE\xf6\xa5\x0b\xf1\xe9\xe6\xd6}\x8e\xcdB\x87\xb2\xac\x1d\x9a\xa4\x82\xe0,h\xeb2\xeakpLkU\\\x7f\x92\xab\xd4\xdc\xed1W.M\x1c\x9f\xbe\x1a\x00k\x02\x93L89\xcf\xd8\x9b\x92:\xa20\xb9\x10\xe9\xff\xbah\xaaA\x1cf\xb7\xc4j\xcf\xaa\x1cj\x1bW\xc5s~\xcb\x8aF\x9c@l\xcb\x1a\xe6\xbdJ\x19\xa7 So9\xe1\xee \xfd\x11U\xadEf\xa3\xbc\x9cJ\x0d\x94\n_\x9d\x15\xd7y\xef\x0e\xf5\xb0\xf7`\xc2\x00\x99\xf8\xdb\xe8&\xa6\xfc\xaa\x1aC\xdf\xf5*\xc8$\xb6\xc4We\xb5\xe5B)\xcf{N\xa0E\xa3\\4\xca\xd3k\x94c\xde\x9f\xa1ZzQM\xd11\xdbP\x9ah\xc5\xb2\xdddcu\x92\xc4\xf2\xfe\xdaSNN\xf1\x8b\xcd\x89U\xa7\xba*S#|\xa1\x9aS\xfe\x8aSNe\xca\xadH5\xceZS\xde#*|\x98$\xae2\xe5\xaf1\x95\xb6\xc2T\xd2\xfaR\xde\xeaR\xcd\xbc\xdaR)+K\x85\xebJM\xae*\x95\xb2\xa6T\x13\xae(\x95\xb2\x9eT\xb0\x9aT\xe2ZR\xdeJRS\xeaH\xf9kF%\xa8\x18E\xaa\x17\x15W\x1bjfe\xa8\xb4u\xa1\\~\x8a\xa45\xa1\xd2W\x84JZ\x0f\x8aV\x0d*i-(_%\xa8\xc4u\xa0\\U\xa0\x1aj\x0d\xa8\xa9\x15\xa0T\xb5'\x04!^\xffiF\xf5'G\xed'\xef\x11\xef\xad\xfb\x14>\xff\xd3\xd5|\xf2U|\xf2\x8f#i\xb5'_\xad\xa7D\x95\x9e\xe6\xd5yBv\x12v\x94\xa7\xad\xf1\xd4\xa0\x15\x9e\xe6\xd6w\n\x160\xf2\xd4v\"Uv\xc2\x0b\xbd\xc4Uu\xc2qXE\x1ef\xd7s\xa2\x12\x83R\xcb\xc9?oR\x1d\xa7\xc8*Nv\xd1\x8b\x04\x15\x9c\x82\xf5\x9b\xfc\xd5\x9bB\xb5\x9b\x9cT\x8a\xa9\xdbD\xa9\xda\x84\xd5l\x9aY\xb1\x89X\xafiZ\xb5&G}$J\xa5\xa6\x84u\x9a\x1c\xa3\xb08mV\x85&\xac\"S\xc2zLx5\xa6Y\xb5\x98\xb0\xdaK\xa9+/y\xeb.a\x05i\xb0\x9aK\xe9*.%\xad\xb7\x94\xbe\xda\x12\xbd\xd6\x12\xa9\xd2\x92\xb6cP\xea,\xe9O\x83U\x96\xd0\x1aKx\xef\xd4\xda9\xe1\xfaJ\x11\xd5\x95\x88\xb5\x95\xaci\xa4\xae\xab\x94\xb2\xaa\x12ZS)mE\xa5\xb4\xf5\x94\xe6\xf1\x03\xa9\x96\x12\xa5\x92R\xffX\xc1\xaa(\xa9\xdb\x0cV%\xc9WA\xc9\x7f\x83H\\=\xc9Y; \xab\x9c4\x8cZLT7\xc9y\xe5\xc3j&Q+&\x91\xea%\xd1\xaa%\x05k%ETJ\xc2\xea$9\xe7?\xa0v\x9a\x1aI]\xec\xc8\xf4\x87%-\x8e\x1c#\x9dS\x1b\xa9\x97\xe8\xfb\x1b\xbc\x7f\xf7\xc0z\xff3\xaf*R\x13\xef\xb4p\xd6C\xf2\x1a4\x06\xc3OT\x0b\xc9] \x89>\x14\xd4|2\xbd\x06\xd20m\xd2\x80\xab\x02\x92\xb3\xfe\x11}\xfcSk\x1f\x85+\x1f\xd1\xc7\x80\xd20]\xcd#G\xc5#g\xbd#\xfa\xc0\xa3k\x1d\xf9+\x1d9;v\x879\xf9\xe9\x98\xb8\xc2\xd1\xf8\xd0t\xd67\xf2U7\nN\x12\x8fk\xa2NtfU\xa3n\x8a\xc1\x9aF\xae\x8aF\xe3\xc8\xab\x89\xf5\x8c&H\xd6p%\xa3\xf0\xb6K[\xc5(\xb2\x86\x91s\xce\x10\x8c'tW\xba\xf1ng \x11\x05\x92\xd7-\xf2U\xf0\xf1\xd7,J4\x9dd\xd5\x8az\x95z\xa2k\x15\x91*\x15%\x9cpt\x8d\"\x87\x7f\x0b\x86\xf3\xa6V(\xf2\xd6'\xf2N3\x1c\xf39 \xc2\x94\x12DX\xb9\xa1`\xb1\xa1\x89j\xf0\x9c2C\xf2\xaf#|\x1d\x9an\xf4\xee\x11\xa4.0\x94\xb4\xbc\x10^\\(ai!\xbb\xb0P\xba\xb2B\xfd{V\xbf\x87\x94%\x85\xd0\x82BWi\xcb !\xc5\x84R\x97\x12\x8a+$\xe4H9\xb1\xa2\xf1\x08!\x81v\xe3Yq\x80\xf1\x19&\xee\xca9i\x13K\xfc!\x83^\x81\x1b:Q&\x06\x0e:\xf1,\xcfU\x12\x03 C\xa1\x84\xa9\x83 \x13\x87\x13.\xcfU\x0e eh!)\xb80mx!!\xc00y\x88\xe1\xf2\\\xa5\x82\xa8\x90\xc4\xd9A\x89\xa9\xc3\x12\x97\xe7*\xfb@\x0bPL\x1c\xa2\xb8\xfb\xcc \xd2\xb6\xa4cR\x12\x1a\x9f\x9d\xdaA\x87\x8a\xdf\x9eh\x03\xdd\xb0\xfaf&\x93;B\xf5\x0e\xacj\xd65o\xd6\x98\x981\x100\x01\x84F\x0f\xf8\x918\xf8Y\xf60~8x\x0c\xe1g$|\xa4\x02\n\xb9 L\xb2v\xc1/X\xd5\xd4\xbc\xf9^R\x0ec\x19y%k\xd6\xf8\x90H\x1c\x89\x0eAw/\xd0\xaa}\xad\x98Nl\xff\xd1\xb7\xe2O\x89\xbb\xef\xb4\xcb\xc4\x88\xc7\xf3\x92\xfeF\xa9\xb8\x1f\x0eP\x1e\x9b\xc3\xb1\xe9\xfe\xd6\xed\xb5\x11\x16\xa9\xee\x9dl\x8c]rOZ\xbc\xecpH\x8cQ\xf2\x9f\xd6\x07\x13\xa3\xe6\xe2\xb6Tlxb\xb4\xed\xfaw\xc7'\"F\x85\xa4)k>\xf5\xbdag\xff\x83\xc3^\xed\xe8\x81v\xd7\x1e\xab\xea6u\xc9\x8b-\xaf\xf6Y\xd1\xe8\xcd\xa7\xc4g\xffD\xbcey\xcd\xd1\xca\x11\xf6\xcb=\xe8\xbb=\x1e\xa9\xeb\x93\xb7\xa1|:\x08\xd1\x88\xa2\xf9\xa4\xcb\xad\x83\xc8\xfc: \xe4\xd8A\xf8\xcc\n\x9dX\x8d'\xdf\x0e\xc2\x14\x04\"\x15aF\xee\x9d\x13a3\x08\"w\xc4pN\xcc\xc1s\"\xf3\xe4\xe6\xc1\xd4\xfc<'69\xa8@\x8e\x1e@\x82<=\x98\x9e\xab\xe7\xc4\xc7H\xf9z0/g\x0f\xa6\xe6\xed\xb9\x87\x9d\xe7\x92^\xc1\xdc=\x98\x98\xbf\xe7D&\xb3v\x089|0#\x8f\xcf\x8d\x907\xa1\\>H\x99\xcf\x07\xe1\x9c>H\x95\xd7\x07\xb3r\xfb >\xbf\x0fR\xe4\xf8\xc1\x8c\xc2+q\xfe\xa0>\xef\x0f\x16p\xff \x9b\xff\x17\xf4\xe1a\x0e d\xf1\x00\xbd\xa6&~`\x80\x0b\x082\x04H\xe4\x03BmN Dy\x81P\xca\x0d\x84\x10?\x10\xd2B\xa0\xe8I:=J\xaa\xc9\x17\x84(g\x10\x12{\x96\xc7\x1dDM\xa9=\"\xcc\x1f\x84\x9a\x1cB(\xe3\x11\xa2\xf6\xe4F\xef\x0fl\n\xf8\x84\xa8=\xd6\x1f\xea\xe1\x14B\x15^!\xa4\xd1\xe7 \xc6/\x84t\x8e!\x84\x88G\x19\\C\x88\xd8\xf3\xf0**\xf0\x0ea\xf1\xe4\xa5\xf1\x0f!q\x86\x12y\x88\x90\xc3E\x04\xff\xcc\xd5\xe1$B\x1a/\x11\x12\xb8\x89\x90\xc4O\x84\xf8\xac.\xe3)B2W\x11\xbc|E\xa8\xc1Y\x84%\xbcE(\xe0.Bd\n\x139\x8cP\x9b\xc7\x08\xb1~yVr\x16\xa7\x11\xb5\x14x\xe9\x83\xb8\xaar\x1b!\xc0o\x84\x02\x8e#j\xcc\xf7j\x08q\x15p\x1dQ{\xfe\xa3h\x84\x03 ^\x1e$x\xb9\x90P\x99\x0f E\x9cH\xd4\x1c\xc6\x93\x84\\\xae$j)\xcc\x9f\x84\x85\x1cJH\xe7Q\xc2R.%,\xe2SB`\x07\x0bp\xe6`\x01o.\x85[ K\xf9\x95\xb0\x84c \xfeA\x16p-\x17>\\\x05\x1cL|m\xfbx\x98\x90\xcd\xc5\xc4M\xf9\xf8\x99\xf0\n\x1cM\xa8\xb4\xe6\x12\xb9\x9a\x90\xcc\xd7\x04\x87\xb3 ^\xde&Lgc\x9c\x9d Q\xfe&$\x9e>\xb3y\x9c\xa85\x85O\xf4s9\xc1\xcb\xe7\x04\xbb\xc7\xf58\x9d\x10O5\xe0\xdcNX\xc4\xef\xc4\xbe\xed\xe1x\xfa\xbe\x8a\xf1<\xdd\xefz\x8d.\xe2{\x02\xf88\x9f\x10\x9f/\xe3N\xd5\xe2~\xc2\x0c\xff\xf5\xf2?aa\xdf<\xeb~1\x17\x14\xb5\xa2C\xcf}|P\xb0z\x14\xeb\xb0\xf6\x8fR^(\x94\x171\x03\x1cQ\x88\xdf \xb0\x07W\x8d+\n\x11\xbe(dt.\x90:\xcc\xe1\x8ez\x8d\xa9\xd7\x7f\x04\xf8\xa3\x10\xe6\x90B\xc6\xe8\xf2\xb9\xa4\x90\xc8'\x85\x8c^\x05\xe6\xbc&\xb7\x14B\xfcR\xe0\x9d\xf2rL!cX\x19\\SH\xe0\x9bB\xbc+1\x80[\xca\xec\x17\xf0OQ{&q#\xceA\x85(\x0f\x15\xd2'\xa2\x985U\x8f\x97\nK\xb9\xa9\x10\xe4\xa7\x82=\x82\x12\x8e*\x94\xef\x15)|UH\x9cu(\xe0\xadz\x0d\xde\x91\xe5\xdcU\x88\xcf\x0b$\xcc\x0dDx\xac\x10_\xd0\xeaJ\x9d>\xc8\xe2\xb4\x06\xcd%pF\\\xe2\x18\xc7\\\xc4\x0f\x1b\xe9\xdc\xa4\xf2F\xf3YWs6\xef&\x1f\x15\xb5%\x1e q\xe2\xd4\x07\xef+s\xa5\xf4T\x0f->ut\xd0\xca\x9e\xe6\x04\x0b\xbf\xe4G\xd5\x01\xe7\xd2<5\x1deAM\xb3mh\x83\xf5\xcb\xe8\x95\x8a\xa4\x9bA$\xe0\x8c\x06\xe5\x1f\x97f\xa1\xf4\x01\x99\x98\xbd};R\xc1\xc7>6\x03m7\xa7}\xa3eT\x10\xb2\xf1\xdc\x9dn\xc7\x83C$\x0f\xc05\x06\x0c\x8b\x92\x98k\x14\x7f\xac&\xa7\x9b\xef\x98\xf3\xd4'<\xdb\x8d\x7f\xab\xf1l3\xc1-&\xf2*v=\x8ad\xfd\xf1\xd4\xe2\xd7]l\xdd\xc5\xd6]l\xdd\xc5\xbc\xb6\xfe\x7f\xd8\xc5\x82\xeb-x\x07\xe2\xe3q\xc6\xa2'\x15\xc8\x1f\xa7\xf6\xa9\xd9\x93\x8e\x8am\xc5\xf92b\x90\x9c7\xe4H\x05\x84\xb8E\xe5\x0ffF\xe2\xb4j-\xbc\xaf\xbcu\xc2\xaf\xf1T.Rr\x00\x18O-\xe5y*^v\xdf\xb7\x1e\xb9\x85) \xa1\xcfmhf\xf4a\xa6N\x875ns{\x97\x1b\xaa\xd9\xb9I\xb7b\xc9,\x18c6\xcd!\xe3wF\xbe\xeaj\xae\xba\x9a\xaf\xa1\xabiV\x9c\xd0\x04b,Ii\xfcHZ[\\o\xfa]\x95\x02>:\x0fpr\x02S\x94\x13\xec\x84e\xd2\xba\x8e$\x0d\xbd\xeb\xc1\x98\xcb\xbc\x8c \x9e\xfd\x9b\xd3U\x05\x1dr\x8c\xa4u\x08=\\\x04J*\xaf\xab<\x95\x10\xe5Gb\x99Xd\x1f\xdfk\xddh\xbe\xc5\xe3x\xad\xa4\xc5Bu\xd4\x94'|O\x08\xdc\x13\xc7\x99\x0co\xb4#\xf1\x16\x0b\xc2\xe7\x11\x85\xe2\xec\x84\x08\xbbB\x127\x14OKV\x81\xfc\xca^(]lO{\x81l\xc0\xac\x0ddC\xda'\x02\x0d\x0d\x0e\xad\xd2\x90\x02}\xa6}Z_bS\x848Q+\xca\xd5\xf0\xb5\xc1\x187\x12\xdd\x06\xb33hU\xd7\xc9\xd2\x18&\x8c8\x00\x19\x05\x8c\xb4\xe7\xe0\x9d\xfd^df\xc6\xb6\xdb\xed\x89\x9e\x95\xd1\x9a\x15)\x98\xd9 [\xc5\xd3O4\xf4J\xc7\xe6\x83\xad\xf3w\xfd\xb0%\x03\xd9\xf2\xd4\x0fR)E\xc5\xa7\xad\x103\xb4C\"#\x92\x06\xd3k{\x13\x9e\xe9\xa3\xbbC'\xef\x90Zj\xe6\x07\x15\xf8\xe6\x16+m\x07\x91\x9dZ\\K\x9e\xcb\x7f\x92\xf2\xde\xeb\x8e&/\x12\x08\x9brGS\x87\x9ee\xb0\xaf\xf4\x08\x89;\x02\xf1G\xe9.\x9d\x0cHl.4\x972\xb1dg\xe8\x84\x84\x1c\n\xa7\xfc@\xf6<\xf7\xd0t\xd0lx\xf0\xed\x0e\xf83\x0b\x04\xdc\x93k\xff\xdc\x89\xbcE\xdfi>J\x8aGq\xf9\xb4~\xd36\x13j\x8e\xc7\\\x1a\xfc\xcc\xb5w\xcf,\xb9\xedxn\x85g\xc3\xca~\xe0\x1c\x8c\x8a\xb8\x82\xf7Td\x7f\x16\xfe,v\xf7\xae\x04\xaaEel\xf8\x8d\x9a\x81\x873\xd8\x85O4\xbbo\x07q\xdb\xdc\x99\xfb\xf5\xdb\xcd\xa7\xf7\\\x80@\xa6\xa9\x04\x93\xbf\xe5[\xe4\xe7NEt\x93\xc0\x95X#\x1e\x8a\xb6p\xe0n#\x13\xf7\x8b\xad\xd7?N\xed \x16\xc5\xae\xdf\xf5\x9cM\x9c\x9a\xd5\xc07\x1c3\xa7\xf1\xd1\x97\xcf0,\xcd \x9c\x9f\xb0\x15\xb38\x8f\xa1r\x17\xba\xad5u\xb1\xa6.^+ua\xcf}42\xf2Ff\xba\x81\x9f1\x0bZh\x96\x1a\x99-\x0e\xc7&\xff\x9d\x95\xb3\x08K2\xa7e\x08\x9c[\x9e)\xc0<\x8b-[\xf6b\xd2\xcba\xd1e\xef\x96\xe5\xdf\xb0\xa8Wb9\xb8_\xc5\xb6\x9e|AeO \x15\x96R\xce\x11Q\xf6\x8b%g\xc9$s\xf3\x88\xb1\xa0@2-\x93F\xce\x14EF\xa9\xf8q9\xe4l!\xe4, dvx\xc5f,.~\x9c#{\xec\x13#\x8d\n\x1e\xe7J\x1d3/\x8e\x98\x0b\x8a\x1c\xe7\xc8\x1b\x87e\x8c+\x08\x18'I\x17/\x93(.\x14'\xce\x95%\x06\xb4j\xee;\xd7U\x95\"\xae/B\\U~8Mx8Or\xd83\xc1!\xb1\xe1\xe52\xc3\xb3\x9c0\xf6\x8c{\x04\x86i\xaa\xb4p\xae\xa8\xb0\x10\x0fF\x0c\xe2r\xc2\x05B\xc2\x1e \xe1\xe0\x16\x1f\x94\x0d\x8e\xef\xff\xf5\xa4\x82C\"\xc1\xe1~\xe4 \x03+Oj\x19\x0bI\x02W\x12\x03.\x90\x01\xc6\x9f$l+/\x90\xfee\xedY\xd6(*\xfa[*\xf7\x1b\xd5\xaa\x0dH\xfc&\x89\xfb\xe2\x1a\x9c\xcb\x04}q\x1b\x8e\xc6]\xb1|o\xead\xa4H\xf6\x86\xc7\x9d$\xd3\xbbP\xa0\xd7\xd5\xfc\xab \xca\x1b\x95\xe3\x0d\x0b\xf1\xc6$x\xbd\xb3\xb4Dv7Ep\x17\x93\xda-\x14\xd9M\x94\xd7\xcd\x13\xd6\xf5H\xd7\xa6\x88\xe9V\x94\xd1\xf5\xf4\xc2YiY\xa2\xb9\x10\x10\xc8\xad(\x8d\x8b\x8b\xe2\xe6\xca\xe1z\xa5o\x0bDo\xd1cHP\xda\x16\xd3\xdd\xc4\xe4l\xeb \xd9\xe6K\xd8\"r\xb5YB\xb5AQ\xdat9\xda$!Z\x99\xc7H\x91\xa0\x95_\x8d\x8a\xcf\xa2\x8a\xacx\xeb\xa9\xb2\x9fq\xa9\xd9\x05\"\xb3\x89\xf2\xb2\xce0\n$e\xd1e\\ \x1c\xebd[P\xc9\xd8<\xb1X\x9f0l]I\xd8\xb2\xf5\x90$\x03\x9b\"\x00\xabo+\x98\xe8\xab8\xcd`b\xae!\xa1\xd7\xf0 \"[\xdc\x15\x17\xbe\xf0\xca\xbab\x82\xaef\x95\xb7\x92\x94\xab\xf7\xc8\x87\xc9\xb7\xa6\n\xb7&I\xb6\xa6\x89\xb5FeZ\x17\x08\xb4&1\xceg\xbb\xdal\xd7\x91c\x9d1\x11X\xa5?\xad'\xce\x8a\xb4\x8d\xce\x15\x07\x8f\xec\xaa^w\xf0\xd9\xf2\"\xc6\xfd\x1d\xd3\xfea\x14=\xf4\xcf\x93DV\xe9\xf2\xa2\x85WR5\x98\xd00\xba_IF\xd5/\xa0\x9a\xde\x154}\x92#\x97:\xc9\xa2\"\xf6|B\xa9^\x89\xd4\xf4\xfe\xe7\xca\xa2\xc6\x05Q\xd3\xfb\x80\xcea=\xf9S\x8f\xf0\xa9W\xf24\xbd\xe3\x8beN\xc3\x02\xa7\xde\x86\x135[\x9cy,\x102\xe5\xa9`\xcb\x9c\xbdiz%L\xb3\x94\x89\xc2\x88\xde\xd4\x81\x16\x8a\x94\x1a\x0c\xd5\xb0<\xa9O\x98\xd4\xe8i\xbe$i\x86g\x8d\x0b\x90\xc6\x1f\xbb\\\xd1Q\xb8\xc3\xb2\xbf\x0b\xe5F\xbdc\x86(G\xd6/0\x19|\x9c!iR KJ\x94\xabBz\xac\x85\xb4\"\xc3\xf2\xa1\x95\x86SM,t~b\x96\xcb\x84& \x84V\x1c\xf029\xd0\x100\xd4\x18w\xaa\x10hP\x0248\xcc8\xdb\xc1\x98\x84\x1cUOL\xbd3\xaa\xdb\x99\x19\x06\x97\xa8t\xf2O-{\x18a\xd6\xdf\x83RMN\xb0\xd0\x04\xd9\xea\x9b\x93\xda\xa6f\x0c\xd7\xdd\xac\xa8\xb8\xe9jm\xd6S\xd9\xd4\xcfYz\x0b\x05\xca\x9aBIS\xb3\x85jj\xde\x15\xaaiZ\xcf9\xa2\xa3Y\xa4\xa0\x89(fF\xb52%\xfc\x0fG\xdd\xc5H\x18\xf3\x12_\x8e\xecC\x08\x1c\xcb\xc9\x17s@\xf6c\xb9\x17~rA\xc0\xbf\xc66\x90J\xf4\x08\x0f5\"J\x8b(\xe8y%*\xc4?\xaf\x02K\x02a2!\x82\x88\x91&\xe3\x13-\xae\x8a\xc4I/u2\x89<\xb9`\xcca\xb5\x13\xfb0S\x8bB\x99D\xa2L\x18E\xda}\xa9K\xa5L&S\x96hb\x80=\xb8\n\x94\xca\x94\xe9\xaaG\xab\x8c\x12+\xad\xee\xe4Q+\xc3c*\xa5W\x1a\xc6\xee\\\xb5\xab\x1c\xb6\xe5J\x12YI\"\xd5H\"\xc6\xb2\xf1PEB\x01\xddB\xce\x08f*\x87\xd7\xbb<\xa0\x9c\x1e\xaeW\x8e#\xc3D\x93\xa2`,\x93n\xe2\xb5\xf3?\xa2\x84\x93\x18\xe5$\x18z\x85\x02/\xea%\x9eD\xb7\xb5\xd8,A}\xfaI\x8c\x80R\x9b\x82R\x99\x84\x12\xa1\xa1\x14\x13Q\xeaRQR\xc8(\x05t\x94\xba\x84\x14\xb1Z#\x94\x94\xba\xa4\x94\x04ZJubJ\x84\x9a\x92GNA\x0d\x05 +U(+\x89\xa4\x15\xf4\x97\x8b\x88,\xc5T\x96\xdad\x16?\x9d\xa52\xa1\xe55(-\x95I-\xa9\xb4\x96\xca\xc4\x960\xb5\xa5:\xb9\xc5Oo\x11\x8e+\x89\xe0\x92OqA\x8dq\xda\x8b\x87\xe4RDs\xf1\x12]\xa2!E\x90\xec\x92\x16q\xd4#\xbc\x84)/\xf1\xdeT\xa5\xbd\x84\x89/\xd5\xa8/\xa5\xe4\x17\xc7\x1c\x8fh\xd0\xe0\xa1.\x01F>F\xc8\x80JI0 \xcc\x8f \x11&\x91\n\xe3\xc5\xd3/\xa4\xc3\xf8\xed \xe3bR\xcc\x92\xc9I!\xc6\xc4g!\x89\x1c\xb3\x98\x1e\x83C\xb0+Pd\x12H21\x9aL\x9c(\x13\x9c\xb5%d\x994\xba\x0cN\x98)\xa6\xcc$\x93fri3\xfeiJ\xa2\xceT%\xcf\x04\xfa\x82\xac\xc4\"\n\x8dc\x0d\xa1\xd4T%\xd5\xf8h5\x85\xc4\x1a\xb7\xcb.\xd1\xa6>\xd5&B\xb6\xc1\xe968\xe1\xa6&\xe5\xa62\xe9\xe65h7K\x887\x89\xd4\x9bE\xe4\x9bt\xfa\x8d\x87\x80\xe3\xa3\\\xa4\x93.\xe2$\x9cE4\x9cd\"\x0e:\xa0\xdad\x9c\xbat\x1c\x0f!\xa76%\xa76)\xa7|\x8d$\x11s\xd2\xa89&9\x07\xa7\xe7\xa83\x18F\xc2 St\xe2'\x9e\xca4\x9d\x00Q\x07\xa7\xeaX=\xacE\xd6 \x1e[1\xc2N:e'\x91\xb4\x93J\xdbI \xee\xc0\x12\xea\x0eN\xdeI\xaf\x9d\xd4!\xf0@\x84\xc2\x93\xde\x9f $\xa8\x9c\xc8\x03\x1e*\x8f\xd9\x8bP\x07\xb5\x7f\x94\xd1y\xb2\x0bB^RO4yc\x0c\xa4\x12\xb1'D\xedY\xd6!O\xba(\x9f\xe0\x83\x9a\xeb\x07/\xc5'@\xf2Y6\x92\\\xa2O\n\xd5gYO\x97\xc9{&\xc8h\x16v\xbfP\xc634\x15\xd9\xd2\x9dU\xca\x98q\xb9N\xe3\xe7\x13J\xaaT\xa2S\x9eM\x84\xb1\xe5z\x9c\xe9.\xfb\x9f\xacZ\x99\xe36~H\x91r\xb1{((N\x1aO@\xb6\x1b\xd0\x1e;\xcd\x9c\xdb\xd1\x8c\x0ef?\xee\xf80\xab=\xe2 \xa5\xc9 \x0b\x97\xbc\xe6\xfd\xda\x99\xf1\x892\xeed\xa6R\xa6\xa5\x8c\xa9\x19\xf4hd\xfa\xd51\xd1\xb0\x01\x0f\x1a(\xaa\x85\xe9\xdd\x0f\xc2\x9bYE\xe5K\xbf\xe6e=\xb5\xcbj:\x97^\x85K\x9a\xafmYK\xd52\xacg\x99\xa5d\x99\xada\xc9\xc7k\x9f\x8c\xbd\xea\x95\xd9\xba\x95(u\xc7\xa3XY\xa2U\xc9u)\xed\xd1 r*9\xfa\x94~-\xcaB\x15\xca$\xfd\xc9t\xad\xc9\x02\x95\xc9\x02}I\xc4aTT\x91\xac\xab\x1fYM92\xae\x19YM-\xd2\xa7\x13Y\xa2\x10\x89\xaaA\xd2\x14\x1d\xc8\\\x05H\xaf\xdac\xa6\xce#\xa2\xf0\xe8\xdd(\xbd<\x8b\xf0\x0e\x9a\xa9\xe48\xab6b\xf3\xfbS\xbc\xed2\xddF\xa1\xd3\xa8\x99s\x15\x1b+h5\x96\xa94Z\xab\xdc\xde\x0c\x0b\x95\x19\xe5D\xeb\x16K4\x18\x83\x02\x83\x1e\xdd\xc5\xa8\xe2\xa2+\xbe\x96\xae\xb2\xe8\xfe\xf6\xef\xd8X\xb34\x15S\x06\x1b\xd3Q\xf4\x8f-\xaa\x9d\xb8@5\xd1\x14\x98*TJ\x0cj$\xfa\xd5\x11C\xba\x88\xe8,\xa4j!\xc6T\x10m\xfd\xc3\x02\xe5\xc3\x04\xcd\xc3\xe5j\x87\x88\xb6`L\xe1\xb0\x92\xb6!\xd2\xb2\xb1R\x8a\x94\x0cm\xe5\xc2\x12\xcdBD\xa3\xb0H\x9d\xd0V#\xac\xa9C\xe8U \xb4e\xd9l\xd5\xc1:z\x83\xd5\x94\x06\xebj\x0c\xa6\xa9\x0bFu\x05\xe5)9\xa6((\xbf\x16\xd4\x12tD\xf7\xdc\xd6RU\xe1\xc2\xca\x81\x89\x9a\x81 j\x81F\x97k*\x04\x16i\x03\xbaZ\x80\xf5T\x00\xeb\xe9\xff\xe5\xdf\xdd\xa8\xe6_L\xedO\xb9o[\xe1O\xc4\xe0\xb6z\x9fO\xd5\xcf\x1f\x03WT\xf2C5\xfcl\xf5>\xa3'5t\xfb\xd0\xc3\x88\xad\xd5\x97\xa2\xd2\x17\xd5\xe7\x8b+\xf3\x055\xf9\x12\xd5\xf8l\x1d>t|\xc6,\x96k\xef\xcdEg[u/\xde\xba\xb1\x9alCs\x16x\x99\xc6\xdedg\x9c\xbb8\xb7\x89wD\xfb\x87\x91|\xd6?\x8f*\xea\xd1\xf4$2\xaa\x9f\xe7=\x1a\x1b]\xad\xa0\x99\x87\xab\xe5\xa55\xefRO\xb2\xb5\xf1\xc0\xc2\x8f\x00\xaa\x8a\x87\xea\xe1\xa5\xf55G\x03/\xac~\x97\xd6n\xa6\xb4\x1d&c\x87\n\xd8\xa5\xf5b\x91h\x9d_\xae\x0em,\xa1\x80i,\x94\x8a\xb2t\xf6\xce\x82\n\xd2\xf9\xa4\xe8\x82\x83\x89\x14>=\x03\xa2\x0b%\xe7\xe6\x01h\xe6\x82bs\x98\xcc\x9c]\x92\xcd\x10\x98[\xe0\xae\xc2rra\xffP !\xc7\xd3\x8e\x96\xb94\xf18tl\x10\x04\xf3\xe2\xb2b\xdeG\x0d\xa2\x03\x87\xaa\xc2p>\x850\xbf\x18\\a\xd7\x97K\xbf\xa1+[]\xcbD\xdf\xa2ro\x15\x06\xb7X\xdc\x0dI\xfb\x83)l\x97\"\xeb\xe6\x15t\xf3\x0ei\x01Q5G\xa7\xcd\xd6d\x0b\xaa\xb1-\x8c\xe9J\xb4\xd7\xf8\xa7?\xe9\x035\xd1\xbfx\xab5\x95\xd6\xca5\xd6T\x1dN\x1a\x9c\x8b\xd6\xa5\xbaj\x96\x96Z\xa1\x8a\xda\x1c\xf4K{\xb3~Z\xb1r\x9aT\x8b\x92\xe6f\xcd\xb4\x8aji\x96NZM\x85\xb4\x886\x9a\xc4\xd2\xb0\xce_4w\x9bvB\xd3\\~\xb8\xfa\xfcU\xe4\x0b\xbe\xf4\xbbdH\xcda\xdc\xdd\xb6\xdd\x96\x9c\xed'\xae\xed(\xd9\x91\xd9\xb1b\x01\xd8^5\x04\xf8\x83J\x9eHG_\x19P\xcb\xbfn|\x12pl\x0d\xa5C{wB\xf4i\xb1\xae\x89\x0bef\x066|\x88\xaa\xc4\"\xaf\xc9\x15Wp\x8f \xbc\xbe\xaexw\xbaT\xf3\xa2\x1f\x1c\xa6\xc9\x82\xe7\xa19\x1e\x99\x0b{ \x12\x8c\xf9H^\xb8S\xe2]B\x0c6\xb6\xea\xa2\xb8D\x07Gh\xbb\x91\x92f\xcb\x05\xdd\x9ag^\x81\xf5\xbfr\xdd\xd2\xbb\xbb\xe66>\xb1\xa55\xf5\x96t >P\x884\xa3\xc3J(p\xbe\xfb\x86A\x85Az$/?\x8b\xac\xf3\xb1i\x87Q\xa4\x06\x99\xafJ\xeb\xb5\xbf\xcf\xbck\xbap!\x8c\xfbv\xc37x\xbd\xd7\xb2\xc5g\xd6erh)\xf7>'\x9e\xde\x1d{c\x0f&g\xb29\xc5\xf6%\xd3!h\x9b\xd3H\x87\xd3\x86\x9f\x8ce\x8fx`\xdb\x01w\x03,<<\xf3\xdfZo\xfc\xdb\xf7\xbb\xb0\xffQ\x93\x9b\xecz\x10<\x9c\xb1\x80\xad\xe5\xee|\x03\x9f\xf0\xb2\xa5\xac)|\xc6\xee\xbaw&~i\xc6\xcf\xdd}\x9f<\x0f\xbbf\xbc}n:\xea&L=\xc7\xc6S\xe0\x10\xfcK3\xfe;\xb7\xa5\xa2\x1e\x15\\\x9e\xba\x96\xf2r\xf8s?<\xc2\xb3\x04\xf2\x88\xdd\x96\x9ey]\x9e\x0c\xac\x85\x0b\xad[\xec8T\xa7S\xbf\x8fs\x97\xe6\xcdv\xc7\x82\xd6\x0d\x15\xc0\xb4M\xdfI<\x14f\x80M\xe9\x1c5\x9f\xe7g\x80\x1b\x91\x80\xb3\xe0}\xf9\xce#\xa3\xe4\xdb\xb2mh\x938v\x03A\xe2\xf7\xb6\x1f\x1b\xdap\xdcW\xf7\xc2\xad\xc3@\xe8i\xe8T\x8aZe\xfby\xa2\xa1\xdb\xee\xc9\xa0=\xe8\xf0\x99\xc2\xd7\xdf\xafo4s&hfO\xba\x1d}\x80\xe3@\xee\xdb\xb3@\xa2r(:\xe7\x1c\x11v\x1cg\x8f\x04kU4&\xc2\x0c\x82\xbc\xd7sjtrn\xb1}\xde\x1a4s7&+\xb8\xdfA\xdb\x89\xd9bw,>\\ix\x0d \xaa\x06\x10\x01\xcc\x93\xb8r#\x0c\x88\x9b\xb6\x82L\xfd\x12\xa6\xf1w;\x89\xcbW)RW<\xc2\xe1\x1b\xed\xbc7\xb4\xe3L\xe3x$/\xef\xe6}\x9f\x1d\xa4\xc7~\xd36\xf3 \x03\xeb\x13_\x99\xde\x90\xc5\xea\x82\xd8\xe5\xb9\xc3\x1d\xa1\x99\x11\x95\xb0%Od\xcf\xee4\xcf?5\x946\x9b\x07\xbd\x1c\xad=4\xe6\x9a\xb5\x00\x1b\x8a\xc3\xf8\x81\xec\xda\xee\xc3\xbe\xdf<\xbe\x9d>\xfb\xd4m\xadO\xae\x1e\xc8\xe6\xf1\xe6\xccv=\xd4\xcaG\xb2o\x9f\xc8ps\xb6\x001_\x1aJ\xd8\x81lh\xba\xb1\x91\xf8\xabC\xf3\xc2\x8e7J\x83\xf94rb\xc8\x03\x19\x89|x=\x01\xd2\xbfU\x0d\x904[\xca\x9fi\x1f\xf9=\x8c\xd1%\xb1?h\xf4\x12q\xeeU\xb3\xf2W~'x\xa8`Mdp\xd7\xd1\xc2\xd5\xe4\xad\xc7tI\xc8\x93\x87\xb9\xa1\xd7\xf0\x8d\x88\xa3\xf1\xfa\x01\xd4wx\xbe\x1d\x8a\xef\xab\x05p\xe2\x8a\x85q\xbe\xceT\x15=\xae\xfa-\xd1l\xd9fR\xd2\x1b\xae\x7f\x10DqfO\xf53!J4\x8c\xbc\x91^\x86\xdf\xdb\xb7\n\xfc\xf7F~yh\x9eo\x17\x06[\xc6\xd3\xc3f\xb4?\xd1\xe3ib\xc5h\xbb\xcd\x9b\x91\x05b;2\xc0\x9f\xd9\xea\x12F\xffr\x01_\xb9\x0b\xd7\xact}\xf7nK(\x19\x0em\xd7\x8e\xb4\xddhq\xe0+;\x1c$\x1d\xa5\xdb\xb1\xef\x9a\xb8\xfc\xcchc.uC\x88\xff\xb2cM\xfd\xeb?&\xac\xc3\"Q\xed/\xfe\xe0\xcb\x1f\x95\xea?\xc7\x06!.\xcfP\xc4\x15\x1c\x90\xb8\xc2\xc3\x12\x977f\x15W4\xbc\x84px*\xae\x04+\xf1PQ\\\xb9\xdbP\xb8\xf3\xbe\xc4\x98\xb8b\xfb\x80y\xc5G\x92\xbd\x87\x05\xd7\x18\xd6\xff\xe2-N\xbfb\x03\xcb\x0f\x17\xdd9\xd4\xf2k\xce\x1f\x9d\xe3(D:W\x9ar3\x8c\xa9\xf3\xb1L\xbc\xc5ZOu\xfd\xec\x11\xd9.\xf2\xfa\xed\x94\xcc\x82\xa4\x1d\xe9\x12=\xbf\xa8\x16\xfd\xed,\xce\x83\x05\xf6\xeaK#\xc54\x88\xb7\x81(\xe2\xd3\x1crd\xe6\xba\x92\x1bV)-^=r\x9b\xa5N\xd5% \x1cAWB\xf0J\x08^ \xc1+!x%\x04O\xd7J\x08^ \xc1+!x%\x04\xaf\x84\xe0\xc0oWB\xf0J\x08^ \xc1\xfcZ \xc1\xf2Z \xc1+!\xb8\x802\xba\x12\x82WBp\x02!\x98\xb6\x072\xd2\xe6p\\\x92H4\xf3\x9b\xed\x9c\x829\x0e\xe4\xa9\xedO\xa3(C^\xc0_\xd9 \x92\xd7\"G\xf87\xf8\xd7\xb7\xd0\xd27b\xda\x9f\xf9\xa7|\x89l[\x03\xc8a\xbeK\x9b?\xc9\xaa\x8f\xa6\xf4\x1f<\xf5t\x8aFe\x8b_\x9a\x91^\xf5\x87CK\xf5\xc6\x0d\x07\x07\xff\xfa\xd6p\xb3\xacG,\x1a\x1d\xdb\x91\xb7\x84\xd4\x9e\xa7\xf1\xcee\xddX\x0eyz\x15\x1c=\x0bp\x17[\xef\xb6\xb0\xea\xcd\x9cB\xa1\xcdn\xd4\xf2\xf1b\xabP;\x0d\x87i\x8db\x8dn \x174\xd4\xaa\xcf\x9b\xe1\xe5H\xfb\x0b\x0e\x1c\x1b\xdbY\xc6\xf1\x8a\x9d\xaf7\xf4CK/\x87\xa1I\x170%g:4\xb7w-\x1do\xb9\x1c\xab\x93|M)\xd1\x92\xbdQ\xb9B\xd7\x94s\xac\xc3\xeb\xa7\xd68\x04P\xcf>l\n\xb5X^h\x9e\xde\x8b\x0dw-\x15\xc5\xb5y\xc2[N18\xc9\x97o\x93n<\x0dd\xca\x9aMj\x91\xfc\x8e\xd1\xe6\x91\x8c<-%x(:7D\xda\x13\x0d\x8a}\x9c?\x95\xc2\x82\xc6\xd8S-\xb2\x03=}\x18H\xb3\x85\xb1\xb9Wg}\xf19\x9bON8\xe0\x8c\xb6\xbe\x93\x99\x058\x89c\xdbt\xa3\xe9\xf9blw\x9d.\xd5y\xdd\xee\xba\xafS\x15\xdd\x99e\x9d\xc0\xfe\x0e\xae?\xff\xf2\xeb\xed\xd7o\x1f?!|q\xfd\xaf\x1f?\x7f\xfftu\x83\xfc\xe1\xe6\xd3\x7f\xdc\xfc~\xf9\x05\xf9\xcb\x97O\xbf\\^\xfd\xed\xf6\xf2\xeb\xe7_\xbf\xddrO.\xee\xa7\xe4\xaa\xfb[\xf6`<\xe4\xb0f\x7f/`a|\xf0p`\x7f\x11\x19\x07:\x02\xdbL&\xde\xec\xee\xd4\x0cMG \x19\xe7\xf8\xda3pO\xaf\xa6\xd3\xbf\x94T\x15\xbb\x95\xd14\xbbur\xe7\x91m\x0c\x84=Os\xc2\xc0\x9d\xcd\xf7\xce'zC\xd6\xd0\xf8\x16\xc5\x03*6\x11\x1f\xfb\x8d\\-\xd2\xfc\x13\x19\x84\x7f\x10\xfa\xae\xaaZ'\xd0\x9b7g\xac\x13\xf2\xce\xbdw?\x12\x88;\xc1!2\xbb!\x8atl\x9c\xbc\xc1\x17\x1d\xecn\x11\xcd)9\xd3S\xb3w\x02=\xb6\x8d\x1e\x95\xd3\x96i?\xf3;\xd2\x1e\xef\xbag\x05\x86\x17\xda\xfb\xd0\x1f\xc5\xe0\xee\x9a\xcd\xe3s3lG+\xe5hM\xb6l\xee\xf2\xd0v\xbd\xf0\xb5\xda\x8d\x86\x81\x1c\xfa'\x01\xdf\x15Q=']\xe9\xcf\xe6\x04\xb8?\xd1\x87E8s6\xebd\xb8m\xbb\xfb~)\x8a\xe3\xbf\x0e\xe4\xfe=\xbc\xf9/?k \xef\x9f\xdd.]\xf3\x16X\xa7\xdeL?\xf6\xef\xe8z\x7f\x0c>\xa4\xbewJ\xa3a\x8a\xcb\xf4\xe3\x95cLdJ\xdcF\xa6e4\xce\xcf\xff\xdd\x0b\xec\xfa]\xcf\xe3+37$1\x86\xb2?\x0e\x89E0FeI\xa9i\xb7\x92\x89M\xf45\xbak\xc6[\xfeP.,E\x9c\\L\x9b\xec\x8ds\x13\xa6&l\xe6\xcf\xae\x91H\x18\xb9\xf6yT\xca\xf6\x97\xd9-\xb8\x8bnC\xc6\x91u\xca\xfa\xcb\x1d\xaf\xadqY\x91\xd3\x84r\xe0\x95B\xe87\x9b\xd3\xa0\xfb\x92c\xf3\x92'8\xe3*\xbe\xdc\xc3\xa9\x1b \x15d`\xcb{\xa97\xe4\xb4\xea\xfd9\xc7\xe6Ey\x8f{\x16\xa1\xc1\xe7{\xcb\xdedj*\xc1@\xb3\xd9\xf0\xfb\xc9w\x87c\xf32\xff\xdc\x9e\x02\xee\xe7\xd9\xd0\xc4wy\xe1\x87\x9eU\x7f\xfe,|\xe4i\x14%M\xfe\xe9V\x96\xf7\xc4B\xb6'[\xed\xe3\x7fqZ\x1a \x95~p\xfa\xf5\xb6'#\xfcK\xd7\xd3\x7f\x91\x15E\xe1\x8f\xd9&\xc7Y\xd7\xf7\xd3z\xb6m\xa9\xdd\x0d\xd9\x13\xf4u\xca\xc3\xda\xd4\xbb\xe6[\x8a\xed\xfd<\xc5l?\x10\xd3\xf5g\xd2Nj]\xc6=\x9ceJN\x1ed\x890\xc0\xa7\xe0/\n\xcd\xa3\xc0e\xbc\x05\xde\xefI\xc0\xc3\xbec=\xbf\xa3\xecn\xea\xf83\xa3woD\x84/_\xa7\xc0\xd6\x8c\xcb\xfbh\x8e\xcc\x1b\x0fmC\x89\xd6(\xbf%\xec|E\xce\xfc\xe5\x87b0\x9b\x87\x86y\xc4\xde\x85\xcb\xb1\xaf\xaa\xe2\xd7de|+\xee2\x0f\x03\xef\x9bv\xcf\x7f\x84\x9fZ\xd4\x82\xb1\xcarj\xff\x97\x93*\x82\xa7 \x7f\xa7\x8e\xa2\xec\xaf\x13\x00\xc2Z\x04n@\xf7a\xe8\x9b\xed\xa6\x19i\xf2\xe1\xef\xc3\xf7o\x97\x1f\xaf.\xafo\xfc'@\xeb+\x1f\xbe|\xbb\xfa_\xbe?^\xff\xed\xd7+\xdf\xdf.\xa7?\xcebe\xe1\xd6q/c\x0cR;.\xf1\xf0C\xfdMD\xf1\xea\xe9\xb99_\x93\xe1\xa9\xdd\x90y\x86\xe0\xfbo\xaa\xa7b\xd7\xd1O\x86\xfe~\xbd\x87\xff$C/\xf9G<\xc5\xc7\xdaQ\x0f\xb4\xd7\x02\x9f4g\xbc\xfcS}\x1b>\xcf\x03\xd0\x8e}\n\xb1*^4\x06\xcf\x0d;\xdf\xce\xefV\xe3\xee\xe1,74.\xdf\xc1\x996\xe2\xa5f<\xfd\xe5\xed\x16\xbb#N\xaf\xd8\x87\xc5\x9dj@\xb1\x96f\xe2\xa7z5\x1a/\x84\xf8;u\x89\xf6\xea2\xab[\x82\xb19\xa9k\x1cxZ\x91\x12\xd1|\xe0\xf9\xb99\x7f\x17^+\xf9\xb4F\xcf\xb7\xfc\xb8\xbd4\xb9\x05\xf6:W\x86TX\xc0\x0e\xf2\x88\xf7? $ \xab-D\xa70\xf6\xb4\xa3_\xd3\x9fx\xf4\x0b\xdaS\x8f\xfe\xfd\xd2\xf8B\xea\xd3\x0f\xc18\xa3\x8e\x17\xd0\x0c~\xff\xed\xca\xf1\x04\xe8h\x16{\x03\xd4\xcakz\x04X\xe4\x15\xd0\xee\xbd\x9eg\x80E\xde\x01\xed\xdc\xeby\x08p\xbd\x84o[w}\xc5\xf4\xc4\xca\x7f\xf2\xf2\x96Z|\xce\xd2\x9b~&\xed\xe9\xeb/\xea\x9b\x16\x92\xc8\xe8\xf9v0~\x83\xfe\x0e\xff- $4H9\x0by\x8fB\x1eB\x1a\xff\x86EJ\x83\xe4CH\x88\x9c\x068A-h<\x85\xa8\x06\x0eYm6\x89Q\x9f\xfc3\x13'\xae\xf1o\x19\xe45H\x9e\x9b0\x89\x0d0\"[\xba\xf1\xffc\x9f&R\x99\x0daR\x1b\x84(\x0e\xe0\x90\xdb\xe0\x07\xe5p\xbc\x847\x08\xdeyq\xf9\xef?\xe0\xe47\x08gy|$8\x08L\x86\xb8\x02\x0c\xb2\xc0\xc4\x88+\xce\x1e\xe3&<\x7f\x8b\x0cH\\1\x82\x1cD\x07(\xae Q\x0eR\x06+\xae\xf8\x90\xc5\x15!\xcdA\xda\xe8\xc5\x15%\xcfA\xba\xb5X:P\xbfr\x88tA\x83&\xd7;\xf8U1\x904\xd6\x99\xb8\xd2FV\x93X\x07Qr\x1d\xe4\x13\xec<\xd6\xbc\x13\x902\xfc\x8a\xf4;v\x05)x\xe0\xa3\xe1ABg\xab\xd2\xf1\xc0G\xc9\x83hOR70\xea\xa3\xe6Al\xef2)z\x90\xbc\xd7\xe6R\xf5\xc0C\xd7\x0b\xb6\x9c\x18\xad,\xa4\xee\x01J\xdf\x83\xea\x1d\x89P\xf9x\x83g\xac\x03\xc8f\xe0w\xff\x14\xa5\xf6A\xcc3\xc7\x9e\x84\xaa4?\x08R\xfd\xa0*\xdd\x0fjR\xfe D\xfb\x03(\xa2\xfeAE\xfa\x1fD)\x80\x90K\x03\x84\x12* 6c/G\x95e\xc6\xe8\x80PB DlM(\n@h\x81PH\x0dD\xccqT\x16E\xe8\x81P\x9d\"\x08\xe54A\xa8O\x15\x842\xba \x94Q\x06\xf1G\x14\xedd5\"!T'\x13BMB!$\x91\n\xa1&\xb1\x10\x02\xe4B($\x18b\xcf8B9\x04\xe9j\xa2\xb4C(\xa6\x1e\"\x06]2\"\xe4\x13\x12\xc1\x7f\x14\x0bn\xf1Ay\xbd\xf8\xfe\x9fIR\xc4\xdc\xdeT3\xa7\x06Q\x11\xa2\xfd(#,Z\xc68}\x11!-B\x1d\xe2\"\xd4&/\x02B`\x84r\x12\xa3e\x8d:\x94F(\xa45B\x8c\xed\x07~z#\xa4P\x1c\x01gc-\xa2:\xfam8T\x97\"\xda#,\x98\x8c\x18\xfd\x11\xa2\xe3\x8e\xd2 a\x19\x15\x12\xb0\xf9(\xa5DB\x8c\x16 Aj$D\xe8\x91\x10\x9a\xa5T\x9a$$P%\x01\xa1KB\x19e\x12\xd2h\x93\x90E\x9d\x04\xef\xc4D)\x94P\x8fF \xfe^8+\xad*\xa5\x12\ni\x95\x96)\x97d \xb5\x89\x96P\x99l !\xc2% \xa4K@\x88\x97P\x8d| 5 \x98P\x9d\x84 \xc9DLH!cB:!\x13\x12I\x99\x80zg\x94\xbe\x07\x0b(|a\x82&\xa4\x934!\x8d\xa8 \xd80j\x126\xa1\x94\xb4i\xd9r(\x9cP\x95\xc6 U\xa9\x9cP\xbc\x1e\xa2\x94NH\xa0u\x82F\xed\x04@\xe9\x9d\x90\x9c\x84v\xf3\xe5ETO\xc7\x87o\xdb\xa6\xb3\xe8\x9e\x00\xa5\x94O\xcb\x98|\x85\xa0C\xfb\x04?\xf5\x13\x82\xf3PF\x01\xd5\x0cMdP\x9d\x06\xca\xae\x1c*h\x12vEv{\x02\xaf\xc8\x7fS\x0d\xbd\"\x7f\x86`X\x82\x90\x95\xbf\x92t\x88\x8aMs\xa0Hy\xb7T\xad\x16%`x\x17=N\xbc\xf0|=\x04\x89\xc8&[\xe8FJx\x16\x8aW\xa1\xdbK\xa7T\x14\xd2)\x10*\x05:\x87\x1e\n\x05\x86Y\xafD\x9d\xc0i\x13\xa9\x94 \x8b.\x81\x8e\xc9\xef/\x8a)\x12\x1c\xb1\xaf?\n\x19\xf4\x08Z\x91\x1a\x81\xd3\"\x8a(\x11\x8a\x04\xa1\xd9\x0b\xd0!\x1c*\x04zG\xb0\xe5T\x95\xfe\x90O}\xc8\xa6=\xe8D\x07}\xae\xb2(\x0f\xb9t\x87iF9\xc5\xb1\xdb\xecO[\x82\xbb\n\xee$\xdaN\x0cTm`\xf2 \xfeiv\x1a\xda\x1b^\xe5\xcb\xfc\\b$\x0f\x8a\x80\x9f\xb7\xb8\x1f$\xf7\xf7dC\xdb)\x0f\xfa\xa7]3\x1e\x87vC\xfe4\xed\xf2\xc2MN\xeb](\xbb\xf5\x07\x02\x87\xf6\xd0v\xa7\x83lV\xd5\xe4\xe6\xba\xda\x81\x1c\x8e}\xbf\xc7w\xba_H\x1e,s^\xad\x89\x94\xdd\x9b\xb3\x87\xaaK\xcf\xca\x19\xaaw{\xa0\x02\xb6+\x0et\xc5\x81\xae8P\xe7\xcb+\x0e\xd4\xf3\xc3\x15\x07\xaa_\xf1!\x8bk\xc5\x81\"\xd7\x8a\x03]q\xa0\xe2\x97+\x0eT^+\x0et\xc5\x81\xae8\xd0\x15\x07\xba\xe2@W\x1c\xe8\x8a\x03\x85\x15\x07\xba\xe2@W\x1c\xa8q\xad8\xd0\x15\x07\xba\xe2@W\x1c\xe8\x8a\x03]q\xa0\xb0\xe2@W\x1c\xe8|\xad8P\xe7Zq\xa0+\x0et\xc5\x81\xae8P\x91\x00[q\xa0\x1e\x1c\xa8\x81V \x02?'\xc8'\xffI\x10\xec\xc9\xbf1\xf2JR\x06\x10\xc6\xd1M\xac\xa3p\x1f\x80\xcbL\x02\x8b\xfbV(\x99#\xb8\x99\x11\x01\xce,\xed\xa93|q\xf9j \x18\xc4&\x98b\x08\x81 \xe2@\x1b\x1cj\x13lp\x11\xdc\xc6\x0b\xb8 6\x91\n\xba\xc1`7a\xf8Eh\xb6\xd2\xc07\x18\xfc&}\xbe\xe2\x10\x1c\x0f\x08'\xbd \x07\x88\x93^\xc9\x8cCq\"\x05M\x0c\x8e\x13\x82hx\x80\x19\x9egF\\a\x10F\x10\x98\x13^\x1b\xe2\n\x83s\xbc\xf0\x9c(\x06\xc3\x0f\xd1\x89cX\x82\xf8\x95\xe0d\x89+\x05\xb7B\x83`\x9d\xe8\xf0\xc4\x95\x02\xd8\x89\x0fW\\Q\xd0N\xd2\xd0\xc5\x952\x01\xe2J\x80\xee$\xce\x85\xb8\x92\xe0;\x0b,\xc6S\xc6\xfa\xb5\x14\xc4\x13\x9f\x9c%0\x9e\x1c O\xfa\x08\xb3\xc0<\xfe\xe5\xcf\x9cb\x14\xceS\x1b\xd0\x13\x80\xf4\xa4NDeXO\x02\xb0'\x00\xedI\xe9t\x01\xbc\xc7c\xcd\x0f\xf0\x89\xf7'uk\xa4!\x90OtWt\x81>\xe9\xfby \xd8\xc7\x0f\xf7\xc9\x8e)KA?>\xd8\xcfkt(\x01\xfc\xe3\xc2\x7f\x82\xdbJh#\xa1^\x10P\xd4\xbf\xc7V)\xd4\x87\x02\xc5\xc0@\xb5\xe1@\x95\x01A\x11HP1(\xa8.,(\x05\x18T\x00\x0d\xaa\x0b\x0e\x12\xab5\x02\x0f\xaa\x0b\x10J\x80\x08U\x07 E`By@!\xd4P\x10\x94\x08 B\x7f\xb9\x08TT\x0c+\xaa\x0d,\xf2C\x8b*\x83\x8b^\x03^T\x19`\x94\n1\xaa\x0c2\n\xc3\x8c\xaa\x03\x8d\xfcP#\x89\xa3H\x01\x1b\xe5\xc3\x8dPc\x1c\x82\xe4\x01\x1c\x15A\x8e\x02\x07\xc8HH\x11\x04\x1e\xa5E\x1c\xf5\xc0Ga\xf8Q\xbc7U!Ha\x10R5\x18R)\x10\xc91\xc7#\x1a4x\xa8\x0bF\x92\x8f\x112\xa0R@R\x02\n'\x08JJ\x84%y\xb1\x0d\x0b\xa1I~;H\xc1\xb7\x18\xa0\xb4drR@J\xf1YH\x02*-\x86*\xe1\xe5\xf0\np\xa5\x04\xc0R\x0c\xb2\x14\x07-\x05gm p)\x0d\xba\x84\x83\x97\x8a\xe1K\xc9\x00\xa6\\\x08\x93\x7f\x9a\x92`LU\x81L\x81\xbe +\xb1\x08\xce\xe4XC\xe0MU\x01N>\x88S!\xc8\xc9\xed\xb2\x0bz\xaa\x0f{\x8a\x00\x9fp\xe8\x13\x0e~\xaa \x7f\xaa\x0c\x80z\x0d\x08\xd4\x12\x10T\"\x0cj\x11\x10*\x1d\n\xe5\x01C\xf9\xe0/\xe9\x00\x988 j\x11$*\x19\x14\x85\x0e\xa860\xaa.4\xca\x03\x8e\xaa\x0d\x8f\xaa\x0d\x90*_#I \xa94\x98\x94 \x94\xf2B\xa5\xd2\xd3\xf8X\xed\xa1&`*\x00\x99\xaa\x0c\x9a\n\xc3\xa6\x82\xc0\xa9\xf0\x9cT\x03O\x05\xe0S\xb9\x00*\xa7\xef:$\xc8\x07%\x9a\x074\x1d\xec\x8f\xcdN\xca\xbd\xbd\xc7\xed\xce_\xd0k\xb6\xda\xa7\n\x0b\xa3Z\x9f\xfbG\xb1\xba\x05^\xb1\xe8\xc8\x99\xde:\xb5n\xefZ\xf6&\x13$R\xc7\xc1\x9c(\xfbjf\xd8\xff*\x8d\xb8Q\xbe\xdb\xfa\xb7fG\xe4;0/\xc4\xdf-#l\x1a\x85\xf2\x133\xc7\xe6\x80\xc0\xa1\x1f)\x10\x9eo\xe2 *\xed'\xb4\xa7\xcd~\xe1\x80\x16\xbc\xb0\x9f\x9b\xe7\xe3\xe1\xff\xd3\x9d\x0ew\"\xf1\xa1\xb2\x94Z\xaa\xcc~\x83\xbd>T\xae\xccv\xcb\x8d\xd8\xcf\xces3\n)\xb2\x96\x8e*\xd1:\xc2\xa9\x13\x0ba+rU\xcf\xed(\xee\x01\x8e\xfaApxi\x10\xbf\x9b\xf3\xf8\xe1ET\x93\xa5\xa9\xd8+I\xbf\xf6[\xf2y\xaal:\xab\xcf]yc\xdb\xed\xf6\xba\xf4\x94\x98j\xf1\xf1\xbc\xdb\xf1\xc7^|&4\xd7rV\xf8\xc1\x15d\x12\xad\xf1\xf7\xc0\xca\x19a\xe6\xa7w\xc3\xaa\xdc\x0e\xda2\x84V\x93\xfd\xa6e\xe0o\xad\xbd\xfe\xfc\xcb\xaf\xa1\x17-\xdb\xdf\xfa\xf8\xf9\xfb\xa7\xab\x9b\xc0\x17n>\xfd\xc7\xcd\xef\x97_\x02\xdf\xf8\xf2\xe9\x97\xcb\xab\xbf\xdd^~\xfd\xfc\xeb\xb7[\x1eh\xe8\xdf\x9c\xde\xbc\x1c\xefYx\xeb\xbanw\x1d\x7f\xef\xb2u\xcb\xb4\xc9\x14\xe9N*\xf4\xeaF\xb29\x0d-\xb5\x13M\xbbS\xc3E\xfb,YB~y\xa6\xcf\xd3w\xed\x05\xd0M\x07\xa7N\x84^F\x87\x98\x87\x97\x01\x94\xd5\xd6@\xd8rr7T\xf7\xde\xbcw>\xd1\x1b\xb6&@\x15\xb4F>]\x1f\xfb\x8dH{\xdb%\xa2'2\x88-\x88\xcf\xd8\x84\x8e\x81\xfb\xa1?\xc0\x8d\x03y@\xd6\xc3{\xf7#^/\x82\xfb\x13\xdf7\x8dn \x14\x08\x9b\x07\xde\xf0\x8b\xc0y\x98m<\x9c\x0eM\xf7n \xcd\x96{3J\xce\xf4\xd4\xec\x9dS\x12\x8b\x0f\x8f\xea\xa9\x11\x95\x10gf\xf5_\xd8\xf7\x98\x8f0\xb2\xfc\xc3\xab\xfb}\xe8\x8fb\x0e\xee\x9a\xcd\xe3s3lG\xabHc\xdd#\xab\xd9\xcbC\xdb\xf5\"\n\xd0\xd6\x0d\x0c\xe4\xd0?IIQ~\xdaf\xf3+\x7fz8\xedi;;\x81D\x80\xb2r\xa1\x17_\xd9\xcf\xdf\xd8\x9e\x91\x1b5\x9f\xb2N ,\xf8_\xc6v\xa7\xfb)\xe3\xa1U\x96\xad,\xb6\xed\xf1&O\xdb\x0f\x1e\xd3a\xdf/:\x9e\xbc\x03\xdc\xb5\x94\xe3\xfe\x9c=@\xfdA{\xa0\xc4\xddy$/#\x7f6\xe4\xa4O\xbdk\xe6\xa5\x9d\xb3?\x903\x1d\x9a\xdb\xbb\x96\x8e\xb7#\xed\x07\\\x85f\x89z\x1f\xd9;x\xc5h\xf8a\xc4S~\x9f{\xc5\x96\xee\x86~h\xe9%\x9f#\xa1\xfak\x15\x93\xc4\xcd\xe4p\xe9)>b\xd3*\x80\x96\x86\x87\xe5\x00\x0e\x16Y\xc8H\x8ct\xe3i S\xc1\x1fH\xc7#_\x11Z\xd3\xe6\x91\x8c\xa2V~h\xbb\xf6\xd0\xec\xa5\xa4\xa7f\xd0:Z\x1c\x19\xd9\xaeR\xb8\x13\x9fw2\x1c\xc1\x05|\xe6 \x91)\x05$\xc5\xe5\xcd\xb2>\xed\x05~HX\xb0R\xef#\x8c\xa7\xcd\x03\xfb\xd9\xbe\xdf4RN\x1dY\x00:\xb6e\xea\xbc\xeb\x14\xbf\x0d[2|x\xd1\x9d\xa1\xe6\x9et\xd7\xf4\x0e\xbe}\xff\xf8\xe9\xfb\xed\x87\xbf!\xbe@\xfb\xe3\xe5\xf5\x95\xfb\xe1\xc7O\xf2\xd3\xc9\xb3x\x8d\xe1N\x05o\x1d7\xe3y\xfa\xfb\x81\xcen\x97\xcb\xb3_\x80\x1c\xbe\xea\x16\xc7#]^_\x89\xe9kG\xd84Z:\xca\x1c\xe4{\xe3_sjk\xdc\x10\xb1=\xf2&\x90\xdf\xb2\xb9xo\xfes\xfa5\x1b\xbc\xf3s\xb9\xe0\xb4\xbe\xf2\xef\xf2mF\x0cj\xfa\xb2{\x83\xaf\xf9\xc2[\x94\xd4\x10\xfb\xa8\x99L[\x10\x87R\x145\xec\x8d\x1c\xc3\x07\xf3\x8a\x18a?:\xb8\x1e.\xb8\x1a\"\xd8\x8b\x05\xa6\xf9(\xe0Z\xf8\xdf0\xf27\x0b\xf3\x9b\x8d\xf6\xe5\xe3\xb5\x0f\xbb^\x9co6\xc2Wl\x1d\x965\x0f\xb6\xb7\x04\xd5\xcbO%\xf6h\x90\xc2s\x0e\x92\xd7\x8f\xda-\xc4\xeb&!u\xd3Q\xb9\x05x\xdc\x02$.\xe20*\xe2m\xeb\"m\xabal\xe3\xe8\xdaj\xb8Z\x1f\xa2\xb6\x04K\x8b\xe2f\x91\x82\xaa\xebor\xb1\xb2^\\l&\"\x16\xc1\xc2.K\xb1@t\x07\xcd\xc4\xbc\xce\xf8Vl~\x7f\x8a\xb7]\x86p\x15\x88V\xcd\x9c\x8bm\xad\x80j-\xc3\xb3Z\xab\xdc\xde\x0c\x0b1\xacr\xa2u\x8b%h\xd5 \x14\xd3\x83P\x8dbS]\x98Z:\x1e\xd5\xfd\xed\xdf\xb1\xb1f\xa1OS\x06\x1bC\x9c\xfa\xc7\x16E\x99.\xc0\x97\x9aP\x9cBLi\x10M\xea\xc7\x91\x86\x10\xa4\xe8,\xa4\xa2FcxQ\x1b)Z\x80\x11M@\x87.\xc7\x85\"(\xcc\x18\x16\xb4\x12\n\x14i\xf9\xeff\xa6\xb9\x00\xf3icb\xf2{\x14\xc1\x08A\xe5\x17\x85\xf2\x9f9\x9f\x06\xa6\x08\xa22z\xben\x8b\xebu_\xe9\x19\x94\xc7\x0b\x8eI]\x11]\x02\x88k\xe6\xd5j& \x13 S3w}\xbf'\x0d\xae<\x02z;\x81\xef\xc5x@\xe2\xe2\xd0\xfbY\xb0\xaf\xd5P\xf0\x8f\xe4\xe5\xdd,6\xf7\x16\x9aq\xec7-\xcf\x84\xf2\x9c>n\xb0\xe9\xc4\xaa\xb7s#\xb1\xee\x08\xd98\xbem\x8c\xba\x1a\x1alY\x80\xc6V\x08/\x92\xf0\xb0\xf0A;^;\x86\xf4G\x97\"De\xb5\xdd\x7f \xbb\xb6\xfb\xb0\xef7\x8fo\xa7\xcf>u[\xeb\x93\xab\x07\xb2y\xbc9;T\x1f\xdd\xd2G\xb2o\x9f\xc8psF8\xd5_\x1aJ\x86\xb7f\x9c|\x10\x1aj\x8a\xafsb\xf3\xcd<\xcfH\xa4\xc30\xe7.5\x9b\x87\xa9\xe7\x13,K\xa0\x8e\\\xd5\x9b\x86/R\xb7(D'\x155\xb6w\xf1}\x8b=\xb7\\F\x0eZ\n\xfdfs\x1a\\\xb9\xa0\x0f\x1c7\xf0D:\xf5\x84\xa8\xdf\xd9\x1d\xfa\xb3J}\xf2R\xf2\xf4=\xfc\x9e9\x19\x92v\x84\x81\xdc\x93a\x10 \xa9F\x15,\xda\x03?m\xceP\xb4c\xf3\"\xfevO\xec\xd2\x8a\xba\x9e\x1f\xfa\xbd\xb3\x0dz^Rs \x87>\xb1\xc2\xae\x04\x17\xc8\xa1W\xd8\x82\xf9<\xcc?\x15+N\xdd\x00\xf0\xd5U\x84\xe4a\x7f\xa2\xb7\xd8+)\xbd\xed\xc7\x85\xd5\x1c\xad8\xd9\x8e\xaa\xff\xe8o\xaa\x94\xb1\x89pJ\x02\xc2\xa2!\xac\xb8\xd3\xebz\xfb\xee\xddM5$\xf9\xe6\x1f\xe6\xc6\x1e\x1ac\xb5\x933%\xdd\xd8\xf6\xdd\xadH\x06\xaf5\xa1\xb5&\xb4\xd6\x84\xd6\x9a\xd0Z\x13ZkBkM\xc8\xbe\xd6\x9a\xd0Z\x13\x8aG\x1ckMh\xad \x19\xd7Z\x13\x92\xd7Z\x13ZkBkM(\xa9\xe5\xb5&\xb4\xd6\x84\xa6k\xad \xad5!\xebJ\xcd\xf7\xaf5\xa1\xb5&\x14[#\xafX\x13\xf2\xbdD\xc4\xc9;\x8b\x84\xc6t:Q\x9f\xf2\xfb\xb9i\xba9U\x7fg\xc7\xdd<\xa5\xed\x14j\x9e\x1fH'\xdd\x91P/\xd1\xdba\xc7\xe7\xf1\xa4\xc4\xc1/\xd8\xbad\x01\x90p`#\xeb\x87\xbd\xa1xr\xa6l\x95n\x9a\xee\x0d?\xef 6\xcaV\x94]\x9c\xfc<\x17\x87ud\x82\xbb\xbe\xbb\xdd\x0c-m7\xcd\xfevM\xc6\xaf\xc9x\xe3Z\x93\xf1k2~M\xc6\xaf\xc9\xf85\x19\x8f\\k2~M\xc6\xc7#\x8e5\x19\xbf&\xe3\x8dkM\xc6\xcbkM\xc6\xaf\xc9\xf85\x19\x9f\xd4\xf2\x9a\x8c_\x93\xf1\xd3\xb5&\xe3\xd7d\xbcu\xa5&Z\xd7d\xfc\x9a\x8c\x8f\xad\x915\x19_=\x19\xff2\xad\xbav\xd7\xf5:\xa1\xc48\xb3\xdd\x9c?h\xf2K\\\x8a\x89K\xb3\x1ab\xa6l\n\x9a\xfd~\"\xa1\xb0\xffB\xffD\xa6DQs\xa2\x0fy\xa2\xc0\x13\xebd\xfa!\xc6\x14\x98\xecO\xfa\x91'\xfa\xd0\x0f\xed\x7f\x8a\xe7k {~B\xf2+F\xe9+J\x1d\xdeE\x8aQ\x0c\xe9\xad\"p\x88\x17u\xb2Y\xbd'\xea\x16\xccL\x17M\xbb\n\xa9K85 \xcfI?\xae\xd8j\n\x07O\xcd\x9b4\"\xeds\x99\xea\xa4\x9b\x07\xb9cJ\xe5\xc7\x89\xfa\xa3\x993\x19D3\xeb\xc7\xa4\xf9L/\xd6\xdb\xf4]G6\xfc\xcdiS\x83,\x86it\x857\xcd\xe0\xbe}4\xe2\x87\x90\x080\x0b\xdc\xfa\x91\xaf\x0dD\xc4\xecfR\xd6\x1di\xd3m\x9bA\x86ES\xda\xe9n\xe8\x9b\xed\xa6\x19y\xe7ti7\x9fN\xd9\x87I~\x8c\xc6\xb5\xca\x14\xa3*\xeb\x9e;5(_\xfd\x89zjO\x81$Q,\xe7R\xb5\xde\x14\xaa5\xd5\xac3U\xac1\x05\xeaKE\xb5\xa5zu\xa5XM)\xb3\x9e\x94]K\x12\xf9kd\xb6\xbcu\xa4\xec\x1a\x12\xfav8\xef\xfb\xe1\xcajGp::\xf6|u\xa3\x9c\x9aQ\xa8>T\\\x1bJ\xaa\x0b-\xa9\x01\x15\xd5\x7f\nj?\xa8[\xa9Z\xe3\xa9]\xdf\xa9X\xdbI\xa9\xebT\xac\xe9\xf8\xeb9Uk9x\x1d\x07 \xf11/\x95[\xbf\x11\xb5\x1a\xc7\x1cV\xbb\xc9\xae\xdb\xa05\x9b\xc0V\x1c\xa8\xd5\xc4v\xe9Z5\x1a\x7f}&\xd4\x83\xb2\xba\x8c\xf3\xc69\xec\x9ds\x95\xea1e\xb5\x18\xe7)q7\xdc\x9a5\x18\x8a\xd4_\xcaj/\x91\xd2\x82\xb7\xe6\x92Po\xc1\x92\xafK\xea,\xd8\xef\xff\x8e\x8f=\xb3\xb6\x926\xf8xM%4\xd2\x84Z\xca\xa2:\x8a\x9dt*\xae\x9fDj'\xa1\xbaI\xb8f\xe2\x99\x95\xf4ZI\xbcN\xe2\xd6H\x8a\xea#I\xb5\x91\x9c\xba\x08Z\x87\x88\xd7C\xaa\xd5B\xd0\xf6\xad\x95TT\xffp\xeb\x1d%\xb5\x0e\xb4\xb6QT\xd7p\xeb\x18uk\x18\x81\xfa\x85\x9b\xd6u\xeb\x16\xb5j\x16\x15\xeb\x15\xb5k\x15\xa9u\x8a\x84\x1aEr}\"\xad6\x81\xa4\xf1\xb1VSs\xcd\xb1zDr-\"\xa9\x0eau\xben\xfd\xa1\xa8\xf6\x80\xd5\x1aj\xd6\x19j\xd6\x18J\xeewBm!^W\x98\x9d\xbf?\xaaV \xc3\x0c\xb1'\x8f\xbcS\xb6\xb4\x93G\xcc\xa9\xa6\x90\x13&\xe2D\x97\x0b8U\x14o\x92\xf3d\x9c\xb0\x8bD\x9b\\\x99&D\xa2\xc9\x94gB\xcf\x8a\x95d\x99|\x92Lh\x9b\x1e)&\xac\xb8RS\x82)&\xbf\x14`{\xd05\xbbn\\kv}\xcd\xae\xaf\xd9\xf55\xbb\xbef\xd7\xd7\xec\xfa\x9a]\xb7\xfe\x14\xdb\xa5\xd7\xec:]\xb3\xebkv}\xcd\xae\xaf\xd9\xf55\xbb\xbef\xd7\xd7\xec\xfa\x9a]_\xb3\xebkv\xfd\x9f>\xbb\x8e\xa1\xf5K\x90\xfa\x086\xbf\".\x1f\xc9\x84\x15\x89\xe3\xa4\n\xe3\xd05Uj\\k\xaatM\x95\xae\xa9\xd25U\xba\xa6J\xd7T\xe9\x9a*\xb5\xfe\x14\xdb\xa5\xd7T\xe9\x9a*]S\xa5k\xaatM\x95\xae\xa9\xd25U\xba\xa6J\xd7T\xe9\x9a*]S\xa5k\xaa\xf4\x1f\x97*\xc5\xa5K*\xcb\x96P\xd2m\xc9ph;z\xd1\xdcm\xda\x8bOO\xa4\xa3\xc9\xe2\x10\xfc+\xf3\xadp\x0ff\x0d\xa5C{w\xa2\xaf\xad\x1f\xf1H^j\x1c\x13\xab\x9d7\xdbnK\xce\xb8\xa1\xbb\xbe\xdf\x93\xc6\x96\xc7\x9c,9\x7f5n\xf8\x1b~\x7f.\xd5\xac\n\x8c\xfd\xd8v\xbb=as\xf0Nlz\xc7\xa6\x1d\xdeB3\x8e\xfd\xa6\xe5\xc7$\xb9c\x01a\xbf\xbex\xe3.\xa4\xe9\xf1\xe1\xf6E.i\x84f\xce1\xc1\x96<\x91=\x9b|!\x8aBi\xb3y\xd07=M\x06E\x83\xc3\x7f'\xe3\xb1\xefF\xf2\x81\xec\xda\xee\xc3\xbe\xdf<\xbe\x9d>\xfb\xd4m\xadO\xae\x1e\xc8\xe6\xf1\xe6\xcc\x1e \xeb\xf7\x1f\xc9\xbe}\"\xc3\xcdy\x8a_\xbf4\x94\x0co\x0d\xbd\x1384/\xecq\xf9\xe3D\x06\x16\xdc\x9cF\xae\x88\xc2\x1fC>\xf2\xd1\xbb\xe2\xa7\x19M^\xfa\xc6\x8aC\x97\x08\xb28\xac\xf5\x95\xfa3k5\xe1\xeb\x08[A\xaf\xb3v\xa4\xa6\xcc\xe9\xb8\x1b\x9a-\x99\x84e~\xdb7]\xf2\xf4\xb1\xf0\"2\x11\xfe|\xc95\xa1\xc2\xe3\xf1 E\xd1\x14T\x7fD\xb9\x83\xffI9\xd0\x93\x86\xc3\x97_3vZ\x99G\x9c\xb2 \xfd=}f\xce\x9d\xad\xf4\xe3q/\x8e\xb5<\xb3\xd3\xec\xe1O}\xf7N\x1a\xf9\x13l\xfa\xc3\xa1\xe9\xb6z\xb2~{\xe2\xc3\xd0>\xa1\x13\x9dc~\x0ed|\xae\xc5\xdd\xd2&\xbf/\xec\xc1#\xdb\x0b\xf8\xcc\x13\x82\xcd~\xec5sl4\x86\xf9\x1e\xb6\x84\x92\x0de;\x15\xcf\xe25\xf3\x10\xd4\xd0\xd86'v\x17h`\xd7>\x91n\x9e0\x9e\x1a\xd7-\xaa\xae\x88\x1f\x0cb\x0d\xd0iZY\x18tGH\xc7\x93\xfa\xf2`\xa0\x1a|\x0b-\xe5\xf3\xae\x993s\x822\xc7>\x953\xe6\xbe\xb6#\xf4'\xfa\xae\xbf\x7f\xb7m(\x99\xf7]\xd5\x9d\x9b\xf6@\xa0\xd7U\x9c\xfe\xa7\xe0g\xb4\xc6~=\x90f\xf3\xc0\"+y:\x9a\xec\xf3\xe5@\xce-U\xa7\xbbE\x1c\x12\x93Bb\xacN\x83H\xc2\x0e'\x8a7B'\xd2\xc8<\x8a\x83\xcc4\x1e\xc9\xc0L\x93\xad~\xd6\xfc&Ok[v\x98\xe5\xe3\x95 \xe1\x91L\xbd6\x15\xc6\xd0>c\x04\x17\x9e\x9e\xd1<\xfa\x94|\x9d\x16^w\xaf88m\xb7\xd9\x9f\xb6\xbc^\xf0\xce~o\xf8xb\x9e\x7f\xe4\xcb\x88\xf2\x07\xa0\xa5\xe2f\xf2\xcciC\xfba\x84\x0d/\xb85'\xda\xb3=A\x88\x8c\xa9vh\xef:\xa7\xe9\xf9f^dJ\xa2\x8e\xc6\xb6\xd2\xdc\xf5'\n\x0d\x1c\xf7M\xd71\xff.\xed\xb1\xfb\xcc\xd7JKe\xa5O\xda\xe2D\xab\x0b\xbf\xc7\xfa\xdf'2\xbc\\\x8ag\x8d\xb5\xabv\x9bd/Vk\xfd<\xa8e,\xe28\x83z\xa4/!6rxnf\x07\x11\x98H\xdf\xe0T+\x83\xfa7?\xeb('\xca\x7f\xf5\xb3\xf63i\xed\xfboW\xea\x11\x13\x8e+6\xadW\xa2J\x925\xad\xc7i'q\x86\xc7g\xa0U\xa7 \xde\xc4\xb4\x0e\xd8\xdf.\xac{aE\x90x\xfchnF\x10\x8a\xf9\xc2e\x84\x92\x8d\xc92\x85mSP{\xab\x02t\xbb\x82\xf2-\xcb\xb2\xc660g\xdb\x82\n[\x97e\xce\xd9\xc8\xa0|3\xb3\xac\xc9;g7RsS\x03\xdf\xc6\x06\x8b67@\x1c\x14\x84\xd66\xee\xa8 \xb8\xd9A\xe9\x86\x07\x89\x9b\x1e8\x1b\x1f\x84\xc6\xe2\xd3;-\xdb\x04-c\xa5;\xa1\xf30\xf0\x0f\x02\xee\x1cq\xaa)\xee\\\xfb\x99\xb4\xb6\xd8\x9d\xff.\xdd\xd1\x15k\xa3\x1bO\xe35m(Y\xec\xd9\x95W\xbb\xdd(;\xb7#3\x94\xb8\x85Ng\xa2\xc0\x1c\x85{\x1a\x9c.\xdd\xcc\xcf\xb8\x9d\x9f\xe6 \x9cf\xee\xff\x06\x00\x00\xff\xffPK\x07\x08\xa5\x14\xc7\xdfk\x84\x01\x00\xaa\xbe\x13\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x00\x00\x00\x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(6B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x0f\x02\x00\x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb9\xb1\xf1mT\x02\x00\x008\x05\x00\x00\n\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\xd6\x06\x00\x00index.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(]\x12r 9\x03\x00\x00T \x00\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81k \x00\x00oauth2-redirect.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\x05\xef\x9fw>9\x05\x00\xf8\x0c\x1b\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\xef\x0c\x00\x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81xF\x05\x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(_;\x94/\xe8Y\x00\x00\xa8X\x02\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81T\x01\x07\x00swagger-ui.cssUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xa5\x14\xc7\xdfk\x84\x01\x00\xaa\xbe\x13\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\x81\x81[\x07\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00E\x02\x00\x00/\xe0\x08\x00\x00\x00" + fs.Register(data) +} diff --git a/client/lcd/swagger-ui/favicon-16x16.png b/client/docs/swagger-ui/favicon-16x16.png similarity index 100% rename from client/lcd/swagger-ui/favicon-16x16.png rename to client/docs/swagger-ui/favicon-16x16.png diff --git a/client/lcd/swagger-ui/favicon-32x32.png b/client/docs/swagger-ui/favicon-32x32.png similarity index 100% rename from client/lcd/swagger-ui/favicon-32x32.png rename to client/docs/swagger-ui/favicon-32x32.png diff --git a/client/lcd/swagger-ui/index.html b/client/docs/swagger-ui/index.html similarity index 100% rename from client/lcd/swagger-ui/index.html rename to client/docs/swagger-ui/index.html diff --git a/client/lcd/swagger-ui/oauth2-redirect.html b/client/docs/swagger-ui/oauth2-redirect.html similarity index 100% rename from client/lcd/swagger-ui/oauth2-redirect.html rename to client/docs/swagger-ui/oauth2-redirect.html diff --git a/client/lcd/swagger-ui/swagger-ui-bundle.js b/client/docs/swagger-ui/swagger-ui-bundle.js similarity index 99% rename from client/lcd/swagger-ui/swagger-ui-bundle.js rename to client/docs/swagger-ui/swagger-ui-bundle.js index 4491b4b28445..07eceace9377 100644 --- a/client/lcd/swagger-ui/swagger-ui-bundle.js +++ b/client/docs/swagger-ui/swagger-ui-bundle.js @@ -41773,4 +41773,4 @@ }, o.resolve = i, e.exports = o, o.id = 1058 }]) }); -//# sourceMappingURL=swagger-ui-bundle.js.map \ No newline at end of file +//# sourceMappingURL=swagger-ui-bundle.js.map diff --git a/client/lcd/swagger-ui/swagger-ui-standalone-preset.js b/client/docs/swagger-ui/swagger-ui-standalone-preset.js similarity index 100% rename from client/lcd/swagger-ui/swagger-ui-standalone-preset.js rename to client/docs/swagger-ui/swagger-ui-standalone-preset.js diff --git a/client/lcd/swagger-ui/swagger-ui.css b/client/docs/swagger-ui/swagger-ui.css similarity index 100% rename from client/lcd/swagger-ui/swagger-ui.css rename to client/docs/swagger-ui/swagger-ui.css diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml new file mode 100644 index 000000000000..0a96bc1d791e --- /dev/null +++ b/client/docs/swagger-ui/swagger.yaml @@ -0,0 +1,36858 @@ +swagger: '2.0' +info: + title: Cosmos SDK - Legacy REST and gRPC Gateway docs + description: 'A REST interface for state queries, legacy transactions' + version: 1.0.0 +paths: + /node_info: + get: + description: Information about the connected node + summary: The properties of the connected node + tags: + - Gaia REST + produces: + - application/json + responses: + '200': + description: Node status + schema: + type: object + properties: + application_version: + properties: + build_tags: + type: string + client_name: + type: string + commit: + type: string + go: + type: string + name: + type: string + server_name: + type: string + version: + type: string + node_info: + properties: + id: + type: string + moniker: + type: string + example: validator-name + protocol_version: + properties: + p2p: + type: string + example: 7 + block: + type: string + example: 10 + app: + type: string + example: 0 + network: + type: string + example: gaia-2 + channels: + type: string + listen_addr: + type: string + example: '192.168.56.1:26656' + version: + description: Tendermint version + type: string + example: 0.15.0 + other: + description: more information on versions + type: object + properties: + tx_index: + type: string + example: 'on' + rpc_address: + type: string + example: 'tcp://0.0.0.0:26657' + '500': + description: Failed to query node status + /syncing: + get: + summary: Syncing state of node + tags: + - Tendermint RPC + description: Get if the node is currently syning with other nodes + produces: + - application/json + responses: + '200': + description: Node syncing status + schema: + type: object + properties: + syncing: + type: boolean + '500': + description: Server internal error + /blocks/latest: + get: + summary: Get the latest block + tags: + - Tendermint RPC + produces: + - application/json + responses: + '200': + description: The latest block + schema: + type: object + properties: + block_meta: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + block: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + txs: + type: array + items: + type: string + evidence: + type: array + items: + type: string + last_commit: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + precommits: + type: array + items: + type: object + properties: + validator_address: + type: string + validator_index: + type: string + example: '0' + height: + type: string + example: '0' + round: + type: string + example: '0' + timestamp: + type: string + example: '2017-12-30T05:53:09.287+01:00' + type: + type: number + example: 2 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + signature: + type: string + example: >- + 7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ== + '500': + description: Server internal error + '/blocks/{height}': + get: + summary: Get a block at a certain height + tags: + - Tendermint RPC + produces: + - application/json + parameters: + - in: path + name: height + description: Block height + required: true + type: number + x-example: 1 + responses: + '200': + description: The block at a specific height + schema: + type: object + properties: + block_meta: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + block: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + txs: + type: array + items: + type: string + evidence: + type: array + items: + type: string + last_commit: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + precommits: + type: array + items: + type: object + properties: + validator_address: + type: string + validator_index: + type: string + example: '0' + height: + type: string + example: '0' + round: + type: string + example: '0' + timestamp: + type: string + example: '2017-12-30T05:53:09.287+01:00' + type: + type: number + example: 2 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + signature: + type: string + example: >- + 7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ== + '400': + description: Invalid height + '404': + description: Request block height doesn't + '500': + description: Server internal error + /validatorsets/latest: + get: + summary: Get the latest validator set + tags: + - Tendermint RPC + produces: + - application/json + responses: + '200': + description: The validator set at the latest block height + schema: + type: object + properties: + block_height: + type: string + validators: + type: array + items: + type: object + properties: + address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + pub_key: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + voting_power: + type: string + example: '1000' + proposer_priority: + type: string + example: '1000' + '500': + description: Server internal error + '/validatorsets/{height}': + get: + summary: Get a validator set a certain height + tags: + - Tendermint RPC + produces: + - application/json + parameters: + - in: path + name: height + description: Block height + required: true + type: number + x-example: 1 + responses: + '200': + description: The validator set at a specific block height + schema: + type: object + properties: + block_height: + type: string + validators: + type: array + items: + type: object + properties: + address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + pub_key: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + voting_power: + type: string + example: '1000' + proposer_priority: + type: string + example: '1000' + '400': + description: Invalid height + '404': + description: Block at height not available + '500': + description: Server internal error + '/txs/{hash}': + get: + deprecated: true + summary: Get a Tx by hash + tags: + - Transactions + description: Retrieve a transaction using its hash. + produces: + - application/json + parameters: + - in: path + name: hash + description: Tx hash + required: true + type: string + x-example: BCBE20E8D46758B96AE5883B792858296AC06E51435490FBDCAE25A72B3CC76B + responses: + '200': + description: Tx with the provided hash + schema: + type: object + properties: + hash: + type: string + example: >- + D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656 + height: + type: number + example: 368 + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + result: + type: object + properties: + log: + type: string + gas_wanted: + type: string + example: '200000' + gas_used: + type: string + example: '26354' + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + '500': + description: Internal Server Error + /txs: + get: + deprecated: true + tags: + - Transactions + summary: Search transactions + description: Search transactions by events. + produces: + - application/json + parameters: + - in: query + name: message.action + type: string + description: >- + transaction events such as 'message.action=send' which results in + the following endpoint: 'GET /txs?message.action=send'. note that + each module documents its own events. look for xx_events.md in the + corresponding cosmos-sdk/docs/spec directory + x-example: send + - in: query + name: message.sender + type: string + description: >- + transaction tags with sender: 'GET + /txs?message.action=send&message.sender=cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv' + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: query + name: page + description: Page number + type: integer + x-example: 1 + - in: query + name: limit + description: Maximum number of items per page + type: integer + x-example: 1 + - in: query + name: tx.minheight + type: integer + description: transactions on blocks with height greater or equal this value + x-example: 25 + - in: query + name: tx.maxheight + type: integer + description: transactions on blocks with height less than or equal this value + x-example: 800000 + responses: + '200': + description: All txs matching the provided events + schema: + type: object + properties: + total_count: + type: number + example: 1 + count: + type: number + example: 1 + page_number: + type: number + example: 1 + page_total: + type: number + example: 1 + limit: + type: number + example: 30 + txs: + type: array + items: + type: object + properties: + hash: + type: string + example: >- + D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656 + height: + type: number + example: 368 + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + result: + type: object + properties: + log: + type: string + gas_wanted: + type: string + example: '200000' + gas_used: + type: string + example: '26354' + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + '400': + description: Invalid search events + '500': + description: Internal Server Error + post: + tags: + - Transactions + summary: Broadcast a signed tx + description: Broadcast a signed tx to a full node + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: txBroadcast + description: >- + The tx must be a signed StdTx. The supported broadcast modes include + `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and + `"async"`(return right away). + required: true + schema: + type: object + properties: + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + mode: + type: string + example: block + responses: + '200': + description: Tx broadcasting result + schema: + type: object + properties: + check_tx: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 0 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + deliver_tx: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 5 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + height: + type: integer + '500': + description: Internal Server Error + /txs/encode: + post: + deprecated: true + tags: + - Transactions + summary: Encode a transaction to the Amino wire format + description: >- + Encode a transaction (signed or not) from JSON to base64-encoded Amino + serialized bytes + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: tx + description: The tx to encode + required: true + schema: + type: object + properties: + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + responses: + '200': + description: The tx was successfully decoded and re-encoded + schema: + type: object + properties: + tx: + type: string + example: The base64-encoded Amino-serialized bytes for the tx + '400': + description: The tx was malformated + '500': + description: Server internal error + /txs/decode: + post: + deprecated: true + tags: + - Transactions + summary: Decode a transaction from the Amino wire format + description: >- + Decode a transaction (signed or not) from base64-encoded Amino + serialized bytes to JSON + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: tx + description: The tx to decode + required: true + schema: + type: object + properties: + tx: + type: string + example: >- + SvBiXe4KPqijYZoKFFHEzJ8c2HPAfv2EFUcIhx0yPagwEhTy0vPA+GGhCEslKXa4Af0uB+mfShoMCgVzdGFrZRIDMTAwEgQQwJoM + responses: + '200': + description: The tx was successfully decoded + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: The tx was malformated + '500': + description: Server internal error + '/bank/balances/{address}': + get: + deprecated: true + summary: Get the account balances + tags: + - Bank + produces: + - application/json + parameters: + - in: path + name: address + description: Account address in bech32 format + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + '200': + description: Account balances + schema: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '500': + description: Server internal error + '/bank/accounts/{address}/transfers': + post: + deprecated: true + summary: Send coins from one account to another + tags: + - Bank + consumes: + - application/json + produces: + - application/json + parameters: + - in: path + name: address + description: Account address in bech32 format + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: body + name: account + description: The sender and tx information + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + responses: + '202': + description: Tx was succesfully generated + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid request + '500': + description: Server internal error + /bank/total: + get: + deprecated: true + summary: Total supply of coins in the chain + tags: + - Bank + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + total: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '500': + description: Internal Server Error + '/bank/total/{denomination}': + parameters: + - in: path + name: denomination + description: Coin denomination + required: true + type: string + x-example: uatom + get: + deprecated: true + summary: Total supply of a single coin denomination + tags: + - Bank + produces: + - application/json + responses: + '200': + description: OK + schema: + type: string + '400': + description: Invalid coin denomination + '500': + description: Internal Server Error + '/auth/accounts/{address}': + get: + deprecated: true + summary: Get the account information on blockchain + tags: + - Auth + produces: + - application/json + parameters: + - in: path + name: address + description: Account address + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + '200': + description: Account information on the blockchain + schema: + type: object + properties: + type: + type: string + value: + type: object + properties: + account_number: + type: string + address: + type: string + coins: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + public_key: + type: object + properties: + type: + type: string + value: + type: string + sequence: + type: string + '500': + description: Server internel error + '/staking/delegators/{delegatorAddr}/delegations': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Get all delegations from a delegator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + shares: + type: string + balance: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + post: + summary: Submit delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + delegator_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + validator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + amount: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator address or delegation request body + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/delegations/{validatorAddr}': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query the current delegation between a delegator and a validator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + shares: + type: string + balance: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid delegator address or validator address + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/unbonding_delegations': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Get all unbonding delegations from a delegator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + initial_balance: + type: string + balance: + type: string + creation_height: + type: integer + min_time: + type: integer + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + post: + summary: Submit an unbonding delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + delegator_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + validator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + amount: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator address or unbonding delegation request body + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query all unbonding delegations between a delegator and a validator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + entries: + type: array + items: + type: object + properties: + initial_balance: + type: string + balance: + type: string + creation_height: + type: string + min_time: + type: string + '400': + description: Invalid delegator address or validator address + '500': + description: Internal Server Error + /staking/redelegations: + parameters: + - in: query + name: delegator + description: Bech32 AccAddress of Delegator + required: false + type: string + - in: query + name: validator_from + description: Bech32 ValAddress of SrcValidator + required: false + type: string + - in: query + name: validator_to + description: Bech32 ValAddress of DstValidator + required: false + type: string + get: + deprecated: true + summary: Get all redelegations (filter by query params) + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + $ref: '#/definitions/Redelegation' + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/redelegations': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + post: + deprecated: true + summary: Submit a redelegation + parameters: + - in: body + name: delegation + description: The sender and tx information + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + delegator_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + validator_src_addressess: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + validator_dst_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + shares: + type: string + example: '100' + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + '200': + description: Tx was succesfully generated + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator address or redelegation request body + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/validators': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Query all validators that a delegator is bonded to + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + consensus_pubkey: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: '0' + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: '0' + unbonding_time: + type: string + example: '1970-01-01T00:00:00Z' + commission: + type: object + properties: + rate: + type: string + example: '0' + max_rate: + type: string + example: '0' + max_change_rate: + type: string + example: '0' + update_time: + type: string + example: '1970-01-01T00:00:00Z' + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + '/staking/delegators/{delegatorAddr}/validators/{validatorAddr}': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query a validator that a delegator is bonded to + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + consensus_pubkey: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: '0' + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: '0' + unbonding_time: + type: string + example: '1970-01-01T00:00:00Z' + commission: + type: object + properties: + rate: + type: string + example: '0' + max_rate: + type: string + example: '0' + max_change_rate: + type: string + example: '0' + update_time: + type: string + example: '1970-01-01T00:00:00Z' + '400': + description: Invalid delegator address or validator address + '500': + description: Internal Server Error + /staking/validators: + get: + deprecated: true + summary: >- + Get all validator candidates. By default it returns only the bonded + validators. + parameters: + - in: query + name: status + type: string + description: >- + The validator bond status. Must be either 'bonded', 'unbonded', or + 'unbonding'. + x-example: bonded + - in: query + name: page + description: The page number. + type: integer + x-example: 1 + - in: query + name: limit + description: The maximum number of items per page. + type: integer + x-example: 1 + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + consensus_pubkey: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: '0' + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: '0' + unbonding_time: + type: string + example: '1970-01-01T00:00:00Z' + commission: + type: object + properties: + rate: + type: string + example: '0' + max_rate: + type: string + example: '0' + max_change_rate: + type: string + example: '0' + update_time: + type: string + example: '1970-01-01T00:00:00Z' + '500': + description: Internal Server Error + '/staking/validators/{validatorAddr}': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query the information from a single validator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + consensus_pubkey: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: '0' + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: '0' + unbonding_time: + type: string + example: '1970-01-01T00:00:00Z' + commission: + type: object + properties: + rate: + type: string + example: '0' + max_rate: + type: string + example: '0' + max_change_rate: + type: string + example: '0' + update_time: + type: string + example: '1970-01-01T00:00:00Z' + '400': + description: Invalid validator address + '500': + description: Internal Server Error + '/staking/validators/{validatorAddr}/delegations': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Get all delegations from a validator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + shares: + type: string + balance: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid validator address + '500': + description: Internal Server Error + '/staking/validators/{validatorAddr}/unbonding_delegations': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Get all unbonding delegations from a validator + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + initial_balance: + type: string + balance: + type: string + creation_height: + type: integer + min_time: + type: integer + '400': + description: Invalid validator address + '500': + description: Internal Server Error + /staking/pool: + get: + deprecated: true + summary: Get the current state of the staking pool + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + loose_tokens: + type: string + bonded_tokens: + type: string + inflation_last_time: + type: string + inflation: + type: string + date_last_commission_reset: + type: string + prev_bonded_shares: + type: string + '500': + description: Internal Server Error + /staking/parameters: + get: + deprecated: true + summary: Get the current staking parameter values + tags: + - Staking + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + inflation_rate_change: + type: string + inflation_max: + type: string + inflation_min: + type: string + goal_bonded: + type: string + unbonding_time: + type: string + max_validators: + type: integer + bond_denom: + type: string + '500': + description: Internal Server Error + /slashing/signing_infos: + get: + deprecated: true + summary: Get sign info of given all validators + description: Get sign info of all validators + produces: + - application/json + tags: + - Slashing + parameters: + - in: query + name: page + description: Page number + type: integer + required: true + x-example: 1 + - in: query + name: limit + description: Maximum number of items per page + type: integer + required: true + x-example: 5 + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + start_height: + type: string + index_offset: + type: string + jailed_until: + type: string + missed_blocks_counter: + type: string + '400': + description: Invalid validator public key for one of the validators + '500': + description: Internal Server Error + '/slashing/validators/{validatorAddr}/unjail': + post: + deprecated: true + summary: Unjail a jailed validator + description: Send transaction to unjail a jailed validator + consumes: + - application/json + produces: + - application/json + tags: + - Slashing + parameters: + - type: string + description: Bech32 validator address + name: validatorAddr + required: true + in: path + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + - description: '' + name: UnjailBody + in: body + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + responses: + '200': + description: Tx was succesfully generated + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid validator address or base_req + '500': + description: Internal Server Error + /slashing/parameters: + get: + deprecated: true + summary: Get the current slashing parameters + tags: + - Slashing + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + max_evidence_age: + type: string + signed_blocks_window: + type: string + min_signed_per_window: + type: string + double_sign_unbond_duration: + type: string + downtime_unbond_duration: + type: string + slash_fraction_double_sign: + type: string + slash_fraction_downtime: + type: string + '500': + description: Internal Server Error + /gov/proposals: + post: + deprecated: true + summary: Submit a proposal + description: Send transaction to submit a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - description: >- + valid value of `"proposal_type"` can be `"text"`, + `"parameter_change"`, `"software_upgrade"` + name: post_proposal_body + in: body + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + title: + type: string + description: + type: string + proposal_type: + type: string + example: text + proposer: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + initial_deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + responses: + '200': + description: Tx was succesfully generated + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid proposal body + '500': + description: Internal Server Error + get: + deprecated: true + summary: Query proposals + description: Query proposals information with parameters + produces: + - application/json + tags: + - Governance + parameters: + - in: query + name: voter + description: voter address + required: false + type: string + - in: query + name: depositor + description: depositor address + required: false + type: string + - in: query + name: status + description: >- + proposal status, valid values can be `"deposit_period"`, + `"voting_period"`, `"passed"`, `"rejected"` + required: false + type: string + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + proposal_id: + type: integer + title: + type: string + description: + type: string + proposal_type: + type: string + proposal_status: + type: string + final_tally_result: + type: object + properties: + 'yes': + type: string + example: '0.0000000000' + abstain: + type: string + example: '0.0000000000' + 'no': + type: string + example: '0.0000000000' + no_with_veto: + type: string + example: '0.0000000000' + submit_time: + type: string + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + voting_start_time: + type: string + '400': + description: Invalid query parameters + '500': + description: Internal Server Error + /gov/proposals/param_change: + post: + deprecated: true + summary: Generate a parameter change proposal transaction + description: Generate a parameter change proposal transaction + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - description: >- + The parameter change proposal body that contains all parameter + changes + name: post_proposal_body + in: body + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + title: + type: string + x-example: Param Change + description: + type: string + x-example: Update max validators + proposer: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + changes: + type: array + items: + type: object + properties: + subspace: + type: string + example: staking + key: + type: string + example: MaxValidators + subkey: + type: string + example: '' + value: + type: object + responses: + '200': + description: The transaction was succesfully generated + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid proposal body + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}': + get: + deprecated: true + summary: Query a proposal + description: Query a proposal by id + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: '2' + responses: + '200': + description: OK + schema: + type: object + properties: + proposal_id: + type: integer + title: + type: string + description: + type: string + proposal_type: + type: string + proposal_status: + type: string + final_tally_result: + type: object + properties: + 'yes': + type: string + example: '0.0000000000' + abstain: + type: string + example: '0.0000000000' + 'no': + type: string + example: '0.0000000000' + no_with_veto: + type: string + example: '0.0000000000' + submit_time: + type: string + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + voting_start_time: + type: string + '400': + description: Invalid proposal id + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/proposer': + get: + deprecated: true + summary: Query proposer + description: Query for the proposer for a proposal + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: '2' + responses: + '200': + description: OK + schema: + type: object + properties: + proposal_id: + type: string + proposer: + type: string + '400': + description: Invalid proposal ID + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/deposits': + get: + deprecated: true + summary: Query deposits + description: Query deposits by proposalId + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: '2' + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + proposal_id: + type: string + depositor: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + '400': + description: Invalid proposal id + '500': + description: Internal Server Error + post: + deprecated: true + summary: Deposit tokens to a proposal + description: Send transaction to deposit tokens to a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + - description: '' + name: post_deposit_body + in: body + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + depositor: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid proposal id or deposit body + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/deposits/{depositor}': + get: + deprecated: true + summary: Query deposit + description: Query deposit by proposalId and depositor address + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + - type: string + description: Bech32 depositor address + name: depositor + required: true + in: path + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + '200': + description: OK + schema: + type: object + properties: + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + proposal_id: + type: string + depositor: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + '400': + description: Invalid proposal id or depositor address + '404': + description: Found no deposit + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/votes': + get: + deprecated: true + summary: Query voters + description: Query voters information by proposalId + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + voter: + type: string + proposal_id: + type: string + option: + type: string + '400': + description: Invalid proposal id + '500': + description: Internal Server Error + post: + deprecated: true + summary: Vote a proposal + description: Send transaction to vote a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + - description: >- + valid value of `"option"` field can be `"yes"`, `"no"`, + `"no_with_veto"` and `"abstain"` + name: post_vote_body + in: body + required: true + schema: + type: object + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + voter: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + option: + type: string + example: 'yes' + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid proposal id or vote body + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/votes/{voter}': + get: + deprecated: true + summary: Query vote + description: Query vote information by proposal Id and voter address + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + - type: string + description: Bech32 voter address + name: voter + required: true + in: path + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + '200': + description: OK + schema: + type: object + properties: + voter: + type: string + proposal_id: + type: string + option: + type: string + '400': + description: Invalid proposal id or voter address + '404': + description: Found no vote + '500': + description: Internal Server Error + '/gov/proposals/{proposalId}/tally': + get: + deprecated: true + summary: Get a proposal's tally result at the current time + description: >- + Gets a proposal's tally result at the current time. If the proposal is + pending deposits (i.e status 'DepositPeriod') it returns an empty tally + result. + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: '2' + responses: + '200': + description: OK + schema: + type: object + properties: + 'yes': + type: string + example: '0.0000000000' + abstain: + type: string + example: '0.0000000000' + 'no': + type: string + example: '0.0000000000' + no_with_veto: + type: string + example: '0.0000000000' + '400': + description: Invalid proposal id + '500': + description: Internal Server Error + /gov/parameters/deposit: + get: + deprecated: true + summary: Query governance deposit parameters + description: >- + Query governance deposit parameters. The max_deposit_period units are in + nanoseconds. + produces: + - application/json + tags: + - Governance + responses: + '200': + description: OK + schema: + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + max_deposit_period: + type: string + example: '86400000000000' + '400': + description: is not a valid query request path + '404': + description: Found no deposit parameters + '500': + description: Internal Server Error + /gov/parameters/tallying: + get: + deprecated: true + summary: Query governance tally parameters + description: Query governance tally parameters + produces: + - application/json + tags: + - Governance + responses: + '200': + description: OK + schema: + properties: + threshold: + type: string + example: '0.5000000000' + veto: + type: string + example: '0.3340000000' + governance_penalty: + type: string + example: '0.0100000000' + '400': + description: is not a valid query request path + '404': + description: Found no tally parameters + '500': + description: Internal Server Error + /gov/parameters/voting: + get: + deprecated: true + summary: Query governance voting parameters + description: >- + Query governance voting parameters. The voting_period units are in + nanoseconds. + produces: + - application/json + tags: + - Governance + responses: + '200': + description: OK + schema: + properties: + voting_period: + type: string + example: '86400000000000' + '400': + description: is not a valid query request path + '404': + description: Found no voting parameters + '500': + description: Internal Server Error + '/distribution/delegators/{delegatorAddr}/rewards': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf + get: + deprecated: true + summary: Get the total rewards balance from all delegations + description: >- + Get the sum of all the rewards earned by delegations by a single + delegator + produces: + - application/json + tags: + - Distribution + responses: + '200': + description: OK + schema: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + validator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + reward: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + total: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + post: + deprecated: true + summary: Withdraw all the delegator's delegation rewards + description: Withdraw all the delegator's delegation rewards + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator address + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query a delegation reward + description: Query a single delegation reward by a delegator + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + post: + deprecated: true + summary: Withdraw a delegation reward + description: Withdraw a delegator's delegation reward from a single validator + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator address or delegation body + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/distribution/delegators/{delegatorAddr}/withdraw_address': + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf + get: + deprecated: true + summary: Get the rewards withdrawal address + description: >- + Get the delegations' rewards withdrawal address. This is the address in + which the user will receive the reward funds + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + '400': + description: Invalid delegator address + '500': + description: Internal Server Error + post: + deprecated: true + summary: Replace the rewards withdrawal address + description: Replace the delegations' rewards withdrawal address for a new one. + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + withdraw_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid delegator or withdraw address + '401': + description: Key password is wrong + '500': + description: Internal Server Error + '/distribution/validators/{validatorAddr}': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Validator distribution information + description: Query the distribution information of a single validator + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + self_bond_rewards: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + val_commission: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid validator address + '500': + description: Internal Server Error + '/distribution/validators/{validatorAddr}/outstanding_rewards': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Fee distribution outstanding rewards of a single validator + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '500': + description: Internal Server Error + '/distribution/validators/{validatorAddr}/rewards': + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Commission and self-delegation rewards of a single validator + description: Query the commission and self-delegation rewards of validator. + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '400': + description: Invalid validator address + '500': + description: Internal Server Error + post: + deprecated: true + summary: Withdraw the validator's rewards + description: Withdraw the validator's self-delegation and commissions rewards + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in + conjunction with generate_only) + responses: + '200': + description: OK + schema: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + '400': + description: Invalid validator address + '401': + description: Key password is wrong + '500': + description: Internal Server Error + /distribution/community_pool: + get: + deprecated: true + summary: Community pool parameters + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + '500': + description: Internal Server Error + /distribution/parameters: + get: + deprecated: true + summary: Fee distribution parameters + tags: + - Distribution + produces: + - application/json + responses: + '200': + description: OK + schema: + properties: + base_proposer_reward: + type: string + bonus_proposer_reward: + type: string + community_tax: + type: string + '500': + description: Internal Server Error + /minting/parameters: + get: + deprecated: true + summary: Minting module parameters + tags: + - Mint + produces: + - application/json + responses: + '200': + description: OK + schema: + properties: + mint_denom: + type: string + inflation_rate_change: + type: string + inflation_max: + type: string + inflation_min: + type: string + goal_bonded: + type: string + blocks_per_year: + type: string + '500': + description: Internal Server Error + /minting/inflation: + get: + deprecated: true + summary: Current minting inflation value + tags: + - Mint + produces: + - application/json + responses: + '200': + description: OK + schema: + type: string + '500': + description: Internal Server Error + /minting/annual-provisions: + get: + deprecated: true + summary: Current minting annual provisions value + tags: + - Mint + produces: + - application/json + responses: + '200': + description: OK + schema: + type: string + '500': + description: Internal Server Error + /cosmos/auth/v1beta1/accounts: + get: + summary: Accounts returns all the existing accounts + operationId: Accounts + responses: + '200': + description: A successful response. + schema: + type: object + properties: + accounts: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: accounts are the existing accounts + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAccountsResponse is the response type for the Query/Accounts + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/auth/v1beta1/accounts/{address}': + get: + summary: Account returns account details based on address. + operationId: Account + responses: + '200': + description: A successful response. + schema: + type: object + properties: + account: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryAccountResponse is the response type for the Query/Account + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: address + description: address defines the address to query for. + in: path + required: true + type: string + tags: + - Query + /cosmos/auth/v1beta1/params: + get: + summary: Params queries all parameters. + operationId: AuthParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + max_memo_characters: + type: string + format: uint64 + tx_sig_limit: + type: string + format: uint64 + tx_size_cost_per_byte: + type: string + format: uint64 + sig_verify_cost_ed25519: + type: string + format: uint64 + sig_verify_cost_secp256k1: + type: string + format: uint64 + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + '/cosmos/bank/v1beta1/balances/{address}': + get: + summary: AllBalances queries the balance of all coins for a single account. + operationId: AllBalances + responses: + '200': + description: A successful response. + schema: + type: object + properties: + balances: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: balances is the balances of all the coins. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAllBalancesResponse is the response type for the + Query/AllBalances RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: address + description: address is the address to query balances for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/bank/v1beta1/balances/{address}/{denom}': + get: + summary: Balance queries the balance of a single coin for a single account. + operationId: Balance + responses: + '200': + description: A successful response. + schema: + type: object + properties: + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + QueryBalanceResponse is the response type for the Query/Balance + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: address + description: address is the address to query balances for. + in: path + required: true + type: string + - name: denom + description: denom is the coin denom to query balances for. + in: path + required: true + type: string + tags: + - Query + /cosmos/bank/v1beta1/denoms_metadata: + get: + summary: >- + DenomsMetadata queries the client metadata for all registered coin + denominations. + operationId: DenomsMetadata + responses: + '200': + description: A successful response. + schema: + type: object + properties: + metadatas: + type: array + items: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given + denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: >- + aliases is a list of string aliases for the given + denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: >- + denom_units represents the list of DenomUnit's for a + given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit + with exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges + (eg: ATOM). This can + + be the same as the display. + description: |- + Metadata represents a struct that describes + a basic token. + description: >- + metadata provides the client information for all the + registered tokens. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryDenomsMetadataResponse is the response type for the + Query/DenomsMetadata RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/bank/v1beta1/denoms_metadata/{denom}': + get: + summary: DenomsMetadata queries the client metadata of a given coin denomination. + operationId: DenomMetadata + responses: + '200': + description: A successful response. + schema: + type: object + properties: + metadata: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom + unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: >- + aliases is a list of string aliases for the given + denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: >- + denom_units represents the list of DenomUnit's for a given + coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit + with exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: + ATOM). This can + + be the same as the display. + description: |- + Metadata represents a struct that describes + a basic token. + description: >- + QueryDenomMetadataResponse is the response type for the + Query/DenomMetadata RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: denom + description: denom is the coin denom to query the metadata for. + in: path + required: true + type: string + tags: + - Query + /cosmos/bank/v1beta1/params: + get: + summary: Params queries the parameters of x/bank module. + operationId: BankParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + format: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status + (whether a denom is + + sendable). + default_send_enabled: + type: boolean + format: boolean + description: Params defines the parameters for the bank module. + description: >- + QueryParamsResponse defines the response type for querying x/bank + parameters. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/bank/v1beta1/supply: + get: + summary: TotalSupply queries the total supply of all coins. + operationId: TotalSupply + responses: + '200': + description: A successful response. + schema: + type: object + properties: + supply: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: supply is the supply of the coins + title: >- + QueryTotalSupplyResponse is the response type for the + Query/TotalSupply RPC + + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + '/cosmos/bank/v1beta1/supply/{denom}': + get: + summary: SupplyOf queries the supply of a single coin. + operationId: SupplyOf + responses: + '200': + description: A successful response. + schema: + type: object + properties: + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + QuerySupplyOfResponse is the response type for the Query/SupplyOf + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: denom + description: denom is the coin denom to query balances for. + in: path + required: true + type: string + tags: + - Query + /cosmos/base/tendermint/v1beta1/blocks/latest: + get: + summary: GetLatestBlock returns the latest block. + operationId: GetLatestBlock + responses: + '200': + description: A successful response. + schema: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + block: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing + a block in the blockchain, + + including all blockchain data structures and the rules + of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing + on the order first. + + This means that block.AppHash does not include these + txs. + title: >- + Data contains the set of transactions included in the + block + evidence: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed + message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or + commit vote from validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed + message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or + commit vote from validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a + validator signed two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules + for processing a block in the + blockchain, + + including all blockchain data structures + and the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: >- + hashes from the app output from the prev + block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a + Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included + in a Commit. + description: >- + Commit contains the evidence that a + block was committed by a set of + validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a + set of validators attempting to mislead a light + client. + last_commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included in a + Commit. + description: >- + Commit contains the evidence that a block was committed by + a set of validators. + description: >- + GetLatestBlockResponse is the response type for the + Query/GetLatestBlock RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Service + '/cosmos/base/tendermint/v1beta1/blocks/{height}': + get: + summary: GetBlockByHeight queries block for given height. + operationId: GetBlockByHeight + responses: + '200': + description: A successful response. + schema: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + block: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing + a block in the blockchain, + + including all blockchain data structures and the rules + of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing + on the order first. + + This means that block.AppHash does not include these + txs. + title: >- + Data contains the set of transactions included in the + block + evidence: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed + message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or + commit vote from validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed + message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or + commit vote from validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a + validator signed two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules + for processing a block in the + blockchain, + + including all blockchain data structures + and the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: >- + hashes from the app output from the prev + block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a + Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included + in a Commit. + description: >- + Commit contains the evidence that a + block was committed by a set of + validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a + set of validators attempting to mislead a light + client. + last_commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included in a + Commit. + description: >- + Commit contains the evidence that a block was committed by + a set of validators. + description: >- + GetBlockByHeightResponse is the response type for the + Query/GetBlockByHeight RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: height + in: path + required: true + type: string + format: int64 + tags: + - Service + /cosmos/base/tendermint/v1beta1/node_info: + get: + summary: GetNodeInfo queries the current node info. + operationId: GetNodeInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + default_node_info: + type: object + properties: + protocol_version: + type: object + properties: + p2p: + type: string + format: uint64 + block: + type: string + format: uint64 + app: + type: string + format: uint64 + default_node_id: + type: string + listen_addr: + type: string + network: + type: string + version: + type: string + channels: + type: string + format: byte + moniker: + type: string + other: + type: object + properties: + tx_index: + type: string + rpc_address: + type: string + application_version: + type: object + properties: + name: + type: string + app_name: + type: string + version: + type: string + git_commit: + type: string + build_tags: + type: string + go_version: + type: string + build_deps: + type: array + items: + type: object + properties: + path: + type: string + title: module path + version: + type: string + title: module version + sum: + type: string + title: checksum + title: Module is the type for VersionInfo + description: VersionInfo is the type for the GetNodeInfoResponse message. + description: >- + GetNodeInfoResponse is the request type for the Query/GetNodeInfo + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Service + /cosmos/base/tendermint/v1beta1/syncing: + get: + summary: GetSyncing queries node syncing. + operationId: GetSyncing + responses: + '200': + description: A successful response. + schema: + type: object + properties: + syncing: + type: boolean + format: boolean + description: >- + GetSyncingResponse is the response type for the Query/GetSyncing + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Service + /cosmos/base/tendermint/v1beta1/validatorsets/latest: + get: + summary: GetLatestValidatorSet queries latest validator-set. + operationId: GetLatestValidatorSet + responses: + '200': + description: A successful response. + schema: + type: object + properties: + block_height: + type: string + format: int64 + validators: + type: array + items: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + GetLatestValidatorSetResponse is the response type for the + Query/GetValidatorSetByHeight RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Service + '/cosmos/base/tendermint/v1beta1/validatorsets/{height}': + get: + summary: GetValidatorSetByHeight queries validator-set at a given height. + operationId: GetValidatorSetByHeight + responses: + '200': + description: A successful response. + schema: + type: object + properties: + block_height: + type: string + format: int64 + validators: + type: array + items: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + GetValidatorSetByHeightResponse is the response type for the + Query/GetValidatorSetByHeight RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: height + in: path + required: true + type: string + format: int64 + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Service + /cosmos/distribution/v1beta1/community_pool: + get: + summary: CommunityPool queries the community pool coins. + operationId: CommunityPool + responses: + '200': + description: A successful response. + schema: + type: object + properties: + pool: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: pool defines community pool's coins. + description: >- + QueryCommunityPoolResponse is the response type for the + Query/CommunityPool + + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + '/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards': + get: + summary: |- + DelegationTotalRewards queries the total rewards accrued by a each + validator. + operationId: DelegationTotalRewards + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + validator_address: + type: string + reward: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a + decimal amount. + + + NOTE: The amount field is an Dec which implements the + custom method + + signatures required by gogoproto. + description: |- + DelegationDelegatorReward represents the properties + of a delegator's delegation reward. + description: rewards defines all the rewards accrued by a delegator. + total: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: total defines the sum of all the rewards. + description: |- + QueryDelegationTotalRewardsResponse is the response type for the + Query/DelegationTotalRewards RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: delegator_address + description: delegator_address defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards/{validator_address}': + get: + summary: DelegationRewards queries the total rewards accrued by a delegation. + operationId: DelegationRewards + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: rewards defines the rewards accrued by a delegation. + description: |- + QueryDelegationRewardsResponse is the response type for the + Query/DelegationRewards RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: delegator_address + description: delegator_address defines the delegator address to query for. + in: path + required: true + type: string + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/distribution/v1beta1/delegators/{delegator_address}/validators': + get: + summary: DelegatorValidators queries the validators of a delegator. + operationId: DelegatorValidators + responses: + '200': + description: A successful response. + schema: + type: object + properties: + validators: + type: array + items: + type: string + description: >- + validators defines the validators a delegator is delegating + for. + description: |- + QueryDelegatorValidatorsResponse is the response type for the + Query/DelegatorValidators RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: delegator_address + description: delegator_address defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/distribution/v1beta1/delegators/{delegator_address}/withdraw_address': + get: + summary: DelegatorWithdrawAddress queries withdraw address of a delegator. + operationId: DelegatorWithdrawAddress + responses: + '200': + description: A successful response. + schema: + type: object + properties: + withdraw_address: + type: string + description: withdraw_address defines the delegator address to query for. + description: |- + QueryDelegatorWithdrawAddressResponse is the response type for the + Query/DelegatorWithdrawAddress RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: delegator_address + description: delegator_address defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + /cosmos/distribution/v1beta1/params: + get: + summary: Params queries params of the distribution module. + operationId: DistributionParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + community_tax: + type: string + base_proposer_reward: + type: string + bonus_proposer_reward: + type: string + withdraw_addr_enabled: + type: boolean + format: boolean + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + '/cosmos/distribution/v1beta1/validators/{validator_address}/commission': + get: + summary: ValidatorCommission queries accumulated commission for a validator. + operationId: ValidatorCommission + responses: + '200': + description: A successful response. + schema: + type: object + properties: + commission: + description: commission defines the commision the validator received. + type: object + properties: + commission: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a + decimal amount. + + + NOTE: The amount field is an Dec which implements the + custom method + + signatures required by gogoproto. + title: |- + QueryValidatorCommissionResponse is the response type for the + Query/ValidatorCommission RPC method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/distribution/v1beta1/validators/{validator_address}/outstanding_rewards': + get: + summary: ValidatorOutstandingRewards queries rewards of a validator address. + operationId: ValidatorOutstandingRewards + responses: + '200': + description: A successful response. + schema: + type: object + properties: + rewards: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a + decimal amount. + + + NOTE: The amount field is an Dec which implements the + custom method + + signatures required by gogoproto. + description: >- + ValidatorOutstandingRewards represents outstanding + (un-withdrawn) rewards + + for a validator inexpensive to track, allows simple sanity + checks. + description: >- + QueryValidatorOutstandingRewardsResponse is the response type for + the + + Query/ValidatorOutstandingRewards RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/distribution/v1beta1/validators/{validator_address}/slashes': + get: + summary: ValidatorSlashes queries slash events of a validator. + operationId: ValidatorSlashes + responses: + '200': + description: A successful response. + schema: + type: object + properties: + slashes: + type: array + items: + type: object + properties: + validator_period: + type: string + format: uint64 + fraction: + type: string + description: >- + 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. + description: slashes defines the slashes the validator received. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryValidatorSlashesResponse is the response type for the + Query/ValidatorSlashes RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: validator_address + description: validator_address defines the validator address to query for. + in: path + required: true + type: string + - name: starting_height + description: >- + starting_height defines the optional starting height to query the + slashes. + in: query + required: false + type: string + format: uint64 + - name: ending_height + description: >- + starting_height defines the optional ending height to query the + slashes. + in: query + required: false + type: string + format: uint64 + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + /cosmos/evidence/v1beta1/evidence: + get: + summary: AllEvidence queries all evidence. + operationId: AllEvidence + responses: + '200': + description: A successful response. + schema: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: evidence returns all evidences. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAllEvidenceResponse is the response type for the + Query/AllEvidence RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/evidence/v1beta1/evidence/{evidence_hash}': + get: + summary: Evidence queries evidence based on evidence hash. + operationId: Evidence + responses: + '200': + description: A successful response. + schema: + type: object + properties: + evidence: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryEvidenceResponse is the response type for the Query/Evidence + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: evidence_hash + description: evidence_hash defines the hash of the requested evidence. + in: path + required: true + type: string + format: byte + tags: + - Query + '/cosmos/gov/v1beta1/params/{params_type}': + get: + summary: Params queries all parameters of the gov module. + operationId: GovParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + voting_params: + description: voting_params defines the parameters related to voting. + type: object + properties: + voting_period: + type: string + description: Length of the voting period. + deposit_params: + description: deposit_params defines the parameters related to deposit. + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. + Initial value: 2 + months. + tally_params: + description: tally_params defines the parameters related to tally. + type: object + properties: + quorum: + type: string + format: byte + description: >- + Minimum percentage of total stake needed to vote for a + result to be + considered valid. + threshold: + type: string + format: byte + description: >- + Minimum proportion of Yes votes for proposal to pass. + Default value: 0.5. + veto_threshold: + type: string + format: byte + description: >- + Minimum value of Veto votes to Total votes ratio for + proposal to be + vetoed. Default value: 1/3. + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: params_type + description: >- + params_type defines which parameters to query for, can be one of + "voting", + + "tallying" or "deposit". + in: path + required: true + type: string + tags: + - Query + /cosmos/gov/v1beta1/proposals: + get: + summary: Proposals queries all proposals based on given status. + operationId: Proposals + responses: + '200': + description: A successful response. + schema: + type: object + properties: + proposals: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: >- + ProposalStatus enumerates the valid statuses of a + proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + final_tally_result: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: >- + TallyResult defines a standard tally for a governance + proposal. + submit_time: + type: string + format: date-time + deposit_end_time: + type: string + format: date-time + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + voting_start_time: + type: string + format: date-time + voting_end_time: + type: string + format: date-time + description: >- + Proposal defines the core field members of a governance + proposal. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryProposalsResponse is the response type for the + Query/Proposals RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_status + description: |- + proposal_status defines the status of the proposals. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + in: query + required: false + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + - name: voter + description: voter defines the voter address for the proposals. + in: query + required: false + type: string + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: query + required: false + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}': + get: + summary: Proposal queries proposal details based on ProposalID. + operationId: Proposal + responses: + '200': + description: A successful response. + schema: + type: object + properties: + proposal: + type: object + properties: + proposal_id: + type: string + format: uint64 + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: >- + ProposalStatus enumerates the valid statuses of a + proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + final_tally_result: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: >- + TallyResult defines a standard tally for a governance + proposal. + submit_time: + type: string + format: date-time + deposit_end_time: + type: string + format: date-time + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + voting_start_time: + type: string + format: date-time + voting_end_time: + type: string + format: date-time + description: >- + Proposal defines the core field members of a governance + proposal. + description: >- + QueryProposalResponse is the response type for the Query/Proposal + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits': + get: + summary: Deposits queries all deposits of a single proposal. + operationId: Deposits + responses: + '200': + description: A successful response. + schema: + type: object + properties: + deposits: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + depositor: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an + amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: >- + Deposit defines an amount deposited by an account address to + an active + + proposal. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryDepositsResponse is the response type for the Query/Deposits + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}': + get: + summary: >- + Deposit queries single deposit information based proposalID, + depositAddr. + operationId: Deposit + responses: + '200': + description: A successful response. + schema: + type: object + properties: + deposit: + type: object + properties: + proposal_id: + type: string + format: uint64 + depositor: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: >- + Deposit defines an amount deposited by an account address to + an active + + proposal. + description: >- + QueryDepositResponse is the response type for the Query/Deposit + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: path + required: true + type: string + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}/tally': + get: + summary: TallyResult queries the tally of a proposal vote. + operationId: TallyResult + responses: + '200': + description: A successful response. + schema: + type: object + properties: + tally: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: >- + TallyResult defines a standard tally for a governance + proposal. + description: >- + QueryTallyResultResponse is the response type for the Query/Tally + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}/votes': + get: + summary: Votes queries votes of a given proposal. + operationId: Votes + responses: + '200': + description: A successful response. + schema: + type: object + properties: + votes: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + voter: + type: string + options: + type: array + items: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a + given governance proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: >- + WeightedVoteOption defines a unit of vote for vote + split. + description: >- + Vote defines a vote on a governance proposal. + + A Vote consists of a proposal ID, the voter, and the vote + option. + description: votes defined the queried votes. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryVotesResponse is the response type for the Query/Votes RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}': + get: + summary: 'Vote queries voted information based on proposalID, voterAddr.' + operationId: Vote + responses: + '200': + description: A successful response. + schema: + type: object + properties: + vote: + type: object + properties: + proposal_id: + type: string + format: uint64 + voter: + type: string + options: + type: array + items: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a + given governance proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: >- + WeightedVoteOption defines a unit of vote for vote + split. + description: >- + Vote defines a vote on a governance proposal. + + A Vote consists of a proposal ID, the voter, and the vote + option. + description: >- + QueryVoteResponse is the response type for the Query/Vote RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + - name: voter + description: voter defines the oter address for the proposals. + in: path + required: true + type: string + tags: + - Query + /cosmos/mint/v1beta1/annual_provisions: + get: + summary: AnnualProvisions current minting annual provisions value. + operationId: AnnualProvisions + responses: + '200': + description: A successful response. + schema: + type: object + properties: + annual_provisions: + type: string + format: byte + description: >- + annual_provisions is the current minting annual provisions + value. + description: |- + QueryAnnualProvisionsResponse is the response type for the + Query/AnnualProvisions RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/mint/v1beta1/inflation: + get: + summary: Inflation returns the current minting inflation value. + operationId: Inflation + responses: + '200': + description: A successful response. + schema: + type: object + properties: + inflation: + type: string + format: byte + description: inflation is the current minting inflation value. + description: >- + QueryInflationResponse is the response type for the + Query/Inflation RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/mint/v1beta1/params: + get: + summary: Params returns the total set of minting parameters. + operationId: MintParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + mint_denom: + type: string + title: type of coin to mint + inflation_rate_change: + type: string + title: maximum annual change in inflation rate + inflation_max: + type: string + title: maximum inflation rate + inflation_min: + type: string + title: minimum inflation rate + goal_bonded: + type: string + title: goal of percent bonded atoms + blocks_per_year: + type: string + format: uint64 + title: expected blocks per year + description: >- + QueryParamsResponse is the response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/params/v1beta1/params: + get: + summary: |- + Params queries a specific parameter of a module, given its subspace and + key. + operationId: Params + responses: + '200': + description: A successful response. + schema: + type: object + properties: + param: + description: param defines the queried parameter. + type: object + properties: + subspace: + type: string + key: + type: string + value: + type: string + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: subspace + description: subspace defines the module to query the parameter for. + in: query + required: false + type: string + - name: key + description: key defines the key of the parameter in the subspace. + in: query + required: false + type: string + tags: + - Query + /cosmos/slashing/v1beta1/params: + get: + summary: Params queries the parameters of slashing module + operationId: SlashingParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + type: object + properties: + signed_blocks_window: + type: string + format: int64 + min_signed_per_window: + type: string + format: byte + downtime_jail_duration: + type: string + slash_fraction_double_sign: + type: string + format: byte + slash_fraction_downtime: + type: string + format: byte + description: >- + Params represents the parameters used for by the slashing + module. + title: >- + QueryParamsResponse is the response type for the Query/Params RPC + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/slashing/v1beta1/signing_infos: + get: + summary: SigningInfos queries signing info of all validators + operationId: SigningInfos + responses: + '200': + description: A successful response. + schema: + type: object + properties: + info: + type: array + items: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was + unjailed + index_offset: + type: string + format: int64 + description: >- + 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: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to + liveness downtime. + tombstoned: + type: boolean + format: boolean + description: >- + 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: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. + + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for + monitoring their + + liveness activity. + title: info is the signing info of all validators + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + 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; + } + title: >- + QuerySigningInfosResponse is the response type for the + Query/SigningInfos RPC + + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/slashing/v1beta1/signing_infos/{cons_address}': + get: + summary: SigningInfo queries the signing info of given cons address + operationId: SigningInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + val_signing_info: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was + unjailed + index_offset: + type: string + format: int64 + description: >- + 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: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to + liveness downtime. + tombstoned: + type: boolean + format: boolean + description: >- + 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: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. + + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for + monitoring their + + liveness activity. + title: >- + val_signing_info is the signing info of requested val cons + address + title: >- + QuerySigningInfoResponse is the response type for the + Query/SigningInfo RPC + + method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: cons_address + description: cons_address is the address to query signing info of + in: path + required: true + type: string + tags: + - Query + '/cosmos/staking/v1beta1/delegations/{delegator_addr}': + get: + summary: >- + DelegatorDelegations queries all delegations of a given delegator + address. + operationId: DelegatorDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of + the delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of + the validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an + account. It is + + owned by one delegator, and is associated with the + voting power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that + it contains a + + balance in addition to shares which is more suitable for + client responses. + description: >- + delegation_responses defines all the delegations' info of a + delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorDelegationsResponse is response type for the + Query/DelegatorDelegations RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations': + get: + summary: Redelegations queries redelegations of given address. + operationId: Redelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + redelegation_responses: + type: array + items: + type: object + properties: + redelegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of + the delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation + source operator address. + validator_dst_address: + type: string + description: >- + validator_dst_address is the validator redelegation + destination operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for + redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance + when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of + destination-validator shares created by + redelegation. + description: >- + RedelegationEntry defines a redelegation object + with relevant metadata. + description: entries are the redelegation entries. + description: >- + Redelegation contains the list of a particular + delegator's redelegating bonds + + from a particular source validator to a particular + destination validator. + entries: + type: array + items: + type: object + properties: + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for + redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance + when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of + destination-validator shares created by + redelegation. + description: >- + RedelegationEntry defines a redelegation object + with relevant metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a + RedelegationEntry except that it + + contains a balance in addition to shares which is more + suitable for client + + responses. + description: >- + RedelegationResponse is equivalent to a Redelegation except + that its entries + + contain a balance in addition to shares which is more + suitable for client + + responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryRedelegationsResponse is response type for the + Query/Redelegations RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: src_validator_addr + description: src_validator_addr defines the validator address to redelegate from. + in: query + required: false + type: string + - name: dst_validator_addr + description: dst_validator_addr defines the validator address to redelegate to. + in: query + required: false + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations': + get: + summary: >- + DelegatorUnbondingDelegations queries all unbonding delegations of a + given + + delegator address. + operationId: DelegatorUnbondingDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + unbonding_responses: + type: array + items: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at + completion. + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's + unbonding bonds + + for a single validator in an time-ordered list. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryUnbondingDelegatorDelegationsResponse is response type for + the + + Query/UnbondingDelegatorDelegations RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators': + get: + summary: |- + DelegatorValidators queries all validators info for given delegator + address. + operationId: StakingDelegatorValidators + responses: + '200': + description: A successful response. + schema: + type: object + properties: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed + from bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for + the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission + rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to + delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate + which validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + description: >- + Validator defines a validator, together with the total + amount of the + + Validator's bond shares and their exchange rate to coins. + Slashing results in + + a decrease in the exchange rate, allowing correct + calculation of future + + undelegations without iterating over delegators. When coins + are delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated + divided by the current + + exchange rate. Voting power can be calculated as total + bonded shares + + multiplied by exchange rate. + description: validators defines the the validators' info of a delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorValidatorsResponse is response type for the + Query/DelegatorValidators RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}': + get: + summary: |- + DelegatorValidator queries validator info for given delegator validator + pair. + operationId: DelegatorValidator + responses: + '200': + description: A successful response. + schema: + type: object + properties: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from + bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates + to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, + as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase + of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + description: >- + Validator defines a validator, together with the total amount + of the + + Validator's bond shares and their exchange rate to coins. + Slashing results in + + a decrease in the exchange rate, allowing correct calculation + of future + + undelegations without iterating over delegators. When coins + are delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated divided + by the current + + exchange rate. Voting power can be calculated as total bonded + shares + + multiplied by exchange rate. + description: |- + QueryDelegatorValidatorResponse response type for the + Query/DelegatorValidator RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/staking/v1beta1/historical_info/{height}': + get: + summary: HistoricalInfo queries the historical info for given height. + operationId: HistoricalInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + hist: + description: hist defines the historical info at the given height. + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing + a block in the blockchain, + + including all blockchain data structures and the rules + of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + title: prev block info + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + valset: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the + validator's operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must + contain at least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name + should be in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, + for URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message + definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup + results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently + available in the official + + protobuf release, and it is not used for type + URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of + the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol + buffer message along with a + + URL that describes the type of the serialized + message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods + of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will + by default use + + 'type.googleapis.com/full.type.name' as the type URL + and the unpack + + methods only use the fully qualified type name after + the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" + will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded + message, with an + + additional field `@type` which contains the type + URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to + the `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed + from bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature + (ex. UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height + at which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time + for the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission + rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to + delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate + which validator can ever charge, as a + fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate + was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + description: >- + Validator defines a validator, together with the total + amount of the + + Validator's bond shares and their exchange rate to + coins. Slashing results in + + a decrease in the exchange rate, allowing correct + calculation of future + + undelegations without iterating over delegators. When + coins are delegated to + + this validator, the validator is credited with a + delegation whose number of + + bond shares is based on the amount of coins delegated + divided by the current + + exchange rate. Voting power can be calculated as total + bonded shares + + multiplied by exchange rate. + description: >- + QueryHistoricalInfoResponse is response type for the + Query/HistoricalInfo RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: height + description: height defines at which height to query the historical info. + in: path + required: true + type: string + format: int64 + tags: + - Query + /cosmos/staking/v1beta1/params: + get: + summary: Parameters queries the staking parameters. + operationId: StakingParams + responses: + '200': + description: A successful response. + schema: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + unbonding_time: + type: string + description: unbonding_time is the time duration of unbonding. + max_validators: + type: integer + format: int64 + description: max_validators is the maximum number of validators. + max_entries: + type: integer + format: int64 + description: >- + max_entries is the max entries for either unbonding + delegation or redelegation (per pair/trio). + historical_entries: + type: integer + format: int64 + description: >- + historical_entries is the number of historical entries to + persist. + bond_denom: + type: string + description: bond_denom defines the bondable coin denomination. + description: >- + QueryParamsResponse is response type for the Query/Params RPC + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + /cosmos/staking/v1beta1/pool: + get: + summary: Pool queries the pool info. + operationId: Pool + responses: + '200': + description: A successful response. + schema: + type: object + properties: + pool: + description: pool defines the pool info. + type: object + properties: + not_bonded_tokens: + type: string + bonded_tokens: + type: string + description: QueryPoolResponse is response type for the Query/Pool RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + /cosmos/staking/v1beta1/validators: + get: + summary: Validators queries all validators that match the given status. + operationId: Validators + responses: + '200': + description: A successful response. + schema: + type: object + properties: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the + type of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's + path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the + binary all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available + in the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the + regular + + representation of the deserialized, embedded message, + with an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message + [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed + from bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for + the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission + rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to + delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate + which validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily + increase of the validator commission, as a + fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + description: >- + Validator defines a validator, together with the total + amount of the + + Validator's bond shares and their exchange rate to coins. + Slashing results in + + a decrease in the exchange rate, allowing correct + calculation of future + + undelegations without iterating over delegators. When coins + are delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated + divided by the current + + exchange rate. Voting power can be calculated as total + bonded shares + + multiplied by exchange rate. + description: validators contains all the queried validators. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + title: >- + QueryValidatorsResponse is response type for the Query/Validators + RPC method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: status + description: status enables to query for validators matching a given status. + in: query + required: false + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/validators/{validator_addr}': + get: + summary: Validator queries validator info for given validator address. + operationId: Validator + responses: + '200': + description: A successful response. + schema: + type: object + properties: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from + bonded status or not. + status: + description: >- + status is the validator status + (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. + self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: >- + description defines the description terms for the + validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the + validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for + security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at + which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates + to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, + as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase + of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared + minimum self delegation. + description: >- + Validator defines a validator, together with the total amount + of the + + Validator's bond shares and their exchange rate to coins. + Slashing results in + + a decrease in the exchange rate, allowing correct calculation + of future + + undelegations without iterating over delegators. When coins + are delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated divided + by the current + + exchange rate. Voting power can be calculated as total bonded + shares + + multiplied by exchange rate. + title: >- + QueryValidatorResponse is response type for the Query/Validator + RPC method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations': + get: + summary: ValidatorDelegations queries delegate info for given validator. + operationId: ValidatorDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of + the delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of + the validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an + account. It is + + owned by one delegator, and is associated with the + voting power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that + it contains a + + balance in addition to shares which is more suitable for + client responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + title: |- + QueryValidatorDelegationsResponse is response type for the + Query/ValidatorDelegations RPC method + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}': + get: + summary: Delegation queries delegate info for given validator delegator pair. + operationId: Delegation + responses: + '200': + description: A successful response. + schema: + type: object + properties: + delegation_response: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an + account. It is + + owned by one delegator, and is associated with the voting + power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it + contains a + + balance in addition to shares which is more suitable for + client responses. + description: >- + QueryDelegationResponse is response type for the Query/Delegation + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation': + get: + summary: |- + UnbondingDelegation queries unbonding info for given validator delegator + pair. + operationId: UnbondingDelegation + responses: + '200': + description: A successful response. + schema: + type: object + properties: + unbond: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's + unbonding bonds + + for a single validator in an time-ordered list. + description: >- + QueryDelegationResponse is response type for the + Query/UnbondingDelegation + + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + tags: + - Query + '/cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations': + get: + summary: >- + ValidatorUnbondingDelegations queries unbonding delegations of a + validator. + operationId: ValidatorUnbondingDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + unbonding_responses: + type: array + items: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding + completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially + scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at + completion. + description: >- + UnbondingDelegationEntry defines an unbonding object + with relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's + unbonding bonds + + for a single validator in an time-ordered list. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryValidatorUnbondingDelegationsResponse is response type for + the + + Query/ValidatorUnbondingDelegations RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + tags: + - Query + /cosmos/tx/v1beta1/simulate: + post: + summary: Simulate simulates executing a transaction for estimating gas usage. + operationId: Simulate + responses: + '200': + description: A successful response. + schema: + type: object + properties: + gas_info: + description: gas_info is the information about gas used in the simulation. + type: object + properties: + gas_wanted: + type: string + format: uint64 + description: >- + GasWanted is the maximum units of work we allow this tx to + perform. + gas_used: + type: string + format: uint64 + description: GasUsed is the amount of gas actually consumed. + result: + description: result is the result of the simulation. + type: object + properties: + data: + type: string + format: byte + description: >- + 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: + type: string + description: >- + Log contains the log information from message or handler + execution. + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + format: byte + value: + type: string + format: byte + index: + type: boolean + format: boolean + description: >- + EventAttribute is a single key-value pair, + associated with an event. + description: >- + Event allows application developers to attach additional + information to + + ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx + and ResponseDeliverTx. + + Later, transactions may be queried using these events. + description: >- + Events contains a slice of Event objects that were emitted + during message + + or handler execution. + description: |- + SimulateResponse is the response type for the + Service.SimulateRPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.SimulateRequest' + tags: + - Service + /cosmos/tx/v1beta1/txs: + get: + summary: GetTxsEvent fetches txs by event. + operationId: GetTxsEvent + responses: + '200': + description: A successful response. + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.GetTxsEventResponse' + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: events + description: events is the list of transaction event type. + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + - name: pagination.key + description: |- + 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. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + 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. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + 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. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean + - name: order_by + description: |2- + - ORDER_BY_UNSPECIFIED: ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults to ASC in this case. + - ORDER_BY_ASC: ORDER_BY_ASC defines ascending order + - ORDER_BY_DESC: ORDER_BY_DESC defines descending order + in: query + required: false + type: string + enum: + - ORDER_BY_UNSPECIFIED + - ORDER_BY_ASC + - ORDER_BY_DESC + default: ORDER_BY_UNSPECIFIED + tags: + - Service + post: + summary: BroadcastTx broadcast transaction. + operationId: BroadcastTx + responses: + '200': + description: A successful response. + schema: + type: object + properties: + tx_response: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: 'Result bytes, if any.' + raw_log: + type: string + description: >- + The output of the application's logger (raw string). May + be + + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where + the key and value are + + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where + all the attributes + + contain key/value pairs that are strings instead + of raw bytes. + description: >- + Events contains a slice of Event objects that were + emitted during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed + tx ABCI message log. + description: >- + The output of the application's logger (typed). May be + non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type + of the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be + in a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can + optionally set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results + based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty + scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the + above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any + values in the form + + of utility functions or additional generated methods of + the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and + the unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will + yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a + custom JSON + + representation, that representation will be embedded + adding a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + 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. + description: >- + TxResponse defines a structure containing relevant tx data and + metadata. The + + tags are stringified and the log is JSON decoded. + description: |- + BroadcastTxResponse is the response type for the + Service.BroadcastTx method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: body + in: body + required: true + schema: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + mode: + type: string + enum: + - BROADCAST_MODE_UNSPECIFIED + - BROADCAST_MODE_BLOCK + - BROADCAST_MODE_SYNC + - BROADCAST_MODE_ASYNC + default: BROADCAST_MODE_UNSPECIFIED + description: >- + BroadcastMode specifies the broadcast mode for the + TxService.Broadcast RPC method. + + - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering + - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + the tx to be committed in a block. + - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + a CheckTx execution response only. + - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + immediately. + description: >- + BroadcastTxRequest is the request type for the + Service.BroadcastTxRequest + + RPC method. + tags: + - Service + '/cosmos/tx/v1beta1/txs/{hash}': + get: + summary: GetTx fetches a tx by hash. + operationId: GetTx + responses: + '200': + description: A successful response. + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.GetTxResponse' + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: hash + description: 'hash is the tx hash to query, encoded as a hex string.' + in: path + required: true + type: string + tags: + - Service + '/cosmos/upgrade/v1beta1/applied_plan/{name}': + get: + summary: AppliedPlan queries a previously applied upgrade plan by its name. + operationId: AppliedPlan + responses: + '200': + description: A successful response. + schema: + type: object + properties: + height: + type: string + format: int64 + description: height is the block height at which the plan was applied. + description: >- + QueryAppliedPlanResponse is the response type for the + Query/AppliedPlan RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: name + description: name is the name of the applied plan to query for. + in: path + required: true + type: string + tags: + - Query + /cosmos/upgrade/v1beta1/current_plan: + get: + summary: CurrentPlan queries the current upgrade plan. + operationId: CurrentPlan + responses: + '200': + description: A successful response. + schema: + type: object + properties: + plan: + description: plan is the current upgrade plan. + type: object + properties: + name: + type: string + description: >- + Sets the name for the upgrade. This name will be used by + the upgraded + + version of the software to apply any special "on-upgrade" + commands during + + the first BeginBlock method after the upgrade is applied. + It is also used + + to detect whether a software version can handle a given + upgrade. If no + + upgrade handler with this name has been set in the + software, it will be + + assumed that the software is out-of-date when the upgrade + Time or Height is + + reached and the software will exit. + height: + type: string + format: int64 + description: |- + The height at which the upgrade must be performed. + Only used if Time is not set. + info: + type: string + title: >- + Any application specific upgrade info to be included + on-chain + + such as a git commit that validators could automatically + upgrade to + description: >- + QueryCurrentPlanResponse is the response type for the + Query/CurrentPlan RPC + + method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + tags: + - Query + '/cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}': + get: + summary: |- + UpgradedConsensusState queries the consensus state that will serve + as a trusted kernel for the next version of this chain. It will only be + stored at the last height of this chain. + UpgradedConsensusState RPC not supported with legacy querier + operationId: UpgradedConsensusState + responses: + '200': + description: A successful response. + schema: + type: object + properties: + upgraded_consensus_state: + type: string + format: byte + description: >- + QueryUpgradedConsensusStateResponse is the response type for the + Query/UpgradedConsensusState + + RPC method. + default: + description: An unexpected error response + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: last_height + description: |- + last height of the current chain must be sent in request + as this is the height under which next consensus state is stored + in: path + required: true + type: string + format: int64 + tags: + - Query +securityDefinitions: + kms: + type: basic +definitions: + CheckTxResult: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 0 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + DeliverTxResult: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 5 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + BroadcastTxCommitResult: + type: object + properties: + check_tx: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 0 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + deliver_tx: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + example: + code: 5 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - '' + - '' + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + height: + type: integer + KVPair: + type: object + properties: + key: + type: string + value: + type: string + Msg: + type: string + Address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + ValidatorAddress: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + Coin: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + Hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + TxQuery: + type: object + properties: + hash: + type: string + example: D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656 + height: + type: number + example: 368 + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + result: + type: object + properties: + log: + type: string + gas_wanted: + type: string + example: '200000' + gas_used: + type: string + example: '26354' + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + PaginatedQueryTxs: + type: object + properties: + total_count: + type: number + example: 1 + count: + type: number + example: 1 + page_number: + type: number + example: 1 + page_total: + type: number + example: 1 + limit: + type: number + example: 30 + txs: + type: array + items: + type: object + properties: + hash: + type: string + example: D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656 + height: + type: number + example: 368 + tx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + result: + type: object + properties: + log: + type: string + gas_wanted: + type: string + example: '200000' + gas_used: + type: string + example: '26354' + tags: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + StdTx: + type: object + properties: + msg: + type: array + items: + type: string + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: >- + MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: tendermint/PubKeySecp256k1 + value: + type: string + example: Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH + account_number: + type: string + example: '0' + sequence: + type: string + example: '0' + BlockID: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + BlockHeader: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + Block: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + txs: + type: array + items: + type: string + evidence: + type: array + items: + type: string + last_commit: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + precommits: + type: array + items: + type: object + properties: + validator_address: + type: string + validator_index: + type: string + example: '0' + height: + type: string + example: '0' + round: + type: string + example: '0' + timestamp: + type: string + example: '2017-12-30T05:53:09.287+01:00' + type: + type: number + example: 2 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + signature: + type: string + example: >- + 7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ== + BlockQuery: + type: object + properties: + block_meta: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + block: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + total_txs: + type: number + example: 35 + last_commit_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + data_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + next_validators_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + consensus_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + app_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + last_results_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + evidence_hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + proposer_address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + txs: + type: array + items: + type: string + evidence: + type: array + items: + type: string + last_commit: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + precommits: + type: array + items: + type: object + properties: + validator_address: + type: string + validator_index: + type: string + example: '0' + height: + type: string + example: '0' + round: + type: string + example: '0' + timestamp: + type: string + example: '2017-12-30T05:53:09.287+01:00' + type: + type: number + example: 2 + block_id: + type: object + properties: + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + signature: + type: string + example: >- + 7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ== + DelegationDelegatorReward: + type: object + properties: + validator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + reward: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + DelegatorTotalRewards: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + validator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + reward: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + total: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + BaseReq: + type: object + properties: + from: + type: string + example: cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager \U0001F680" + chain_id: + type: string + example: Cosmos-Hub + account_number: + type: string + example: '0' + sequence: + type: string + example: '1' + gas: + type: string + example: '200000' + gas_adjustment: + type: string + example: '1.2' + fees: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + simulate: + type: boolean + example: false + description: >- + Estimate gas for a transaction (cannot be used in conjunction with + generate_only) + TendermintValidator: + type: object + properties: + address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + pub_key: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + voting_power: + type: string + example: '1000' + proposer_priority: + type: string + example: '1000' + TextProposal: + type: object + properties: + proposal_id: + type: integer + title: + type: string + description: + type: string + proposal_type: + type: string + proposal_status: + type: string + final_tally_result: + type: object + properties: + 'yes': + type: string + example: '0.0000000000' + abstain: + type: string + example: '0.0000000000' + 'no': + type: string + example: '0.0000000000' + no_with_veto: + type: string + example: '0.0000000000' + submit_time: + type: string + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + voting_start_time: + type: string + Proposer: + type: object + properties: + proposal_id: + type: string + proposer: + type: string + Deposit: + type: object + properties: + amount: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + proposal_id: + type: string + depositor: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + TallyResult: + type: object + properties: + 'yes': + type: string + example: '0.0000000000' + abstain: + type: string + example: '0.0000000000' + 'no': + type: string + example: '0.0000000000' + no_with_veto: + type: string + example: '0.0000000000' + Vote: + type: object + properties: + voter: + type: string + proposal_id: + type: string + option: + type: string + Validator: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + consensus_pubkey: + type: string + example: >- + cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: '0' + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: '0' + unbonding_time: + type: string + example: '1970-01-01T00:00:00Z' + commission: + type: object + properties: + rate: + type: string + example: '0' + max_rate: + type: string + example: '0' + max_change_rate: + type: string + example: '0' + update_time: + type: string + example: '1970-01-01T00:00:00Z' + Delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + shares: + type: string + balance: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + UnbondingDelegationPair: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + entries: + type: array + items: + type: object + properties: + initial_balance: + type: string + balance: + type: string + creation_height: + type: string + min_time: + type: string + UnbondingEntries: + type: object + properties: + initial_balance: + type: string + balance: + type: string + creation_height: + type: string + min_time: + type: string + UnbondingDelegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + initial_balance: + type: string + balance: + type: string + creation_height: + type: integer + min_time: + type: integer + Redelegation: + type: object + properties: + delegator_address: + type: string + validator_src_address: + type: string + validator_dst_address: + type: string + entries: + type: array + items: + $ref: '#/definitions/Redelegation' + RedelegationEntry: + type: object + properties: + creation_height: + type: integer + completion_time: + type: integer + initial_balance: + type: string + balance: + type: string + shares_dst: + type: string + ValidatorDistInfo: + type: object + properties: + operator_address: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + self_bond_rewards: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + val_commission: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + PublicKey: + type: object + properties: + type: + type: string + value: + type: string + SigningInfo: + type: object + properties: + start_height: + type: string + index_offset: + type: string + jailed_until: + type: string + missed_blocks_counter: + type: string + ParamChange: + type: object + properties: + subspace: + type: string + example: staking + key: + type: string + example: MaxValidators + subkey: + type: string + example: '' + value: + type: object + Supply: + type: object + properties: + total: + type: array + items: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: '50' + cosmos.auth.v1beta1.Params: + type: object + properties: + max_memo_characters: + type: string + format: uint64 + tx_sig_limit: + type: string + format: uint64 + tx_size_cost_per_byte: + type: string + format: uint64 + sig_verify_cost_ed25519: + type: string + format: uint64 + sig_verify_cost_secp256k1: + type: string + format: uint64 + description: Params defines the parameters for the auth module. + cosmos.auth.v1beta1.QueryAccountResponse: + type: object + properties: + account: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryAccountResponse is the response type for the Query/Account RPC + method. + cosmos.auth.v1beta1.QueryAccountsResponse: + type: object + properties: + accounts: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: accounts are the existing accounts + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAccountsResponse is the response type for the Query/Accounts RPC + method. + cosmos.auth.v1beta1.QueryParamsResponse: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + max_memo_characters: + type: string + format: uint64 + tx_sig_limit: + type: string + format: uint64 + tx_size_cost_per_byte: + type: string + format: uint64 + sig_verify_cost_ed25519: + type: string + format: uint64 + sig_verify_cost_secp256k1: + type: string + format: uint64 + description: QueryParamsResponse is the response type for the Query/Params RPC method. + cosmos.base.query.v1beta1.PageRequest: + type: object + properties: + key: + type: string + format: byte + description: |- + 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: + type: string + format: uint64 + description: |- + 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: + type: string + format: uint64 + description: >- + 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: + type: boolean + format: boolean + description: >- + 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. + reverse: + type: boolean + format: boolean + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + description: |- + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + title: |- + PageRequest is to be embedded in gRPC request messages for efficient + pagination. Ex: + cosmos.base.query.v1beta1.PageResponse: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: |- + total is total number of results available if PageRequest.count_total + was set, its value is undefined otherwise + description: |- + 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; + } + google.protobuf.Any: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical + form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types that + they + + expect it to use in the context of Any. However, for URLs which use + the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with + a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + grpc.gateway.runtime.Error: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + cosmos.bank.v1beta1.DenomUnit: + type: object + properties: + denom: + type: string + description: denom represents the string name of the given denom unit (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + cosmos.bank.v1beta1.Metadata: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit (e.g + uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: denom_units represents the list of DenomUnit's for a given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit with exponent + = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: ATOM). This + can + + be the same as the display. + description: |- + Metadata represents a struct that describes + a basic token. + cosmos.bank.v1beta1.Params: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + format: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status (whether a + denom is + + sendable). + default_send_enabled: + type: boolean + format: boolean + description: Params defines the parameters for the bank module. + cosmos.bank.v1beta1.QueryAllBalancesResponse: + type: object + properties: + balances: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: balances is the balances of all the coins. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAllBalancesResponse is the response type for the Query/AllBalances + RPC + + method. + cosmos.bank.v1beta1.QueryBalanceResponse: + type: object + properties: + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + QueryBalanceResponse is the response type for the Query/Balance RPC + method. + cosmos.bank.v1beta1.QueryDenomMetadataResponse: + type: object + properties: + metadata: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit + (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: denom_units represents the list of DenomUnit's for a given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit with + exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: ATOM). + This can + + be the same as the display. + description: |- + Metadata represents a struct that describes + a basic token. + description: >- + QueryDenomMetadataResponse is the response type for the + Query/DenomMetadata RPC + + method. + cosmos.bank.v1beta1.QueryDenomsMetadataResponse: + type: object + properties: + metadatas: + type: array + items: + type: object + properties: + description: + type: string + denom_units: + type: array + items: + type: object + properties: + denom: + type: string + description: >- + denom represents the string name of the given denom unit + (e.g uatom). + exponent: + type: integer + format: int64 + description: >- + 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: + type: array + items: + type: string + title: aliases is a list of string aliases for the given denom + description: |- + DenomUnit represents a struct that describes a given + denomination unit of the basic token. + title: denom_units represents the list of DenomUnit's for a given coin + base: + type: string + description: >- + base represents the base denom (should be the DenomUnit with + exponent = 0). + display: + type: string + description: |- + display indicates the suggested denom that should be + displayed in clients. + name: + type: string + title: 'name defines the name of the token (eg: Cosmos Atom)' + symbol: + type: string + description: >- + symbol is the token symbol usually shown on exchanges (eg: + ATOM). This can + + be the same as the display. + description: |- + Metadata represents a struct that describes + a basic token. + description: >- + metadata provides the client information for all the registered + tokens. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryDenomsMetadataResponse is the response type for the + Query/DenomsMetadata RPC + + method. + cosmos.bank.v1beta1.QueryParamsResponse: + type: object + properties: + params: + type: object + properties: + send_enabled: + type: array + items: + type: object + properties: + denom: + type: string + enabled: + type: boolean + format: boolean + description: >- + SendEnabled maps coin denom to a send_enabled status (whether a + denom is + + sendable). + default_send_enabled: + type: boolean + format: boolean + description: Params defines the parameters for the bank module. + description: >- + QueryParamsResponse defines the response type for querying x/bank + parameters. + cosmos.bank.v1beta1.QuerySupplyOfResponse: + type: object + properties: + amount: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: >- + QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC + method. + cosmos.bank.v1beta1.QueryTotalSupplyResponse: + type: object + properties: + supply: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + title: supply is the supply of the coins + title: >- + QueryTotalSupplyResponse is the response type for the Query/TotalSupply + RPC + + method + cosmos.bank.v1beta1.SendEnabled: + type: object + properties: + denom: + type: string + enabled: + type: boolean + format: boolean + description: |- + SendEnabled maps coin denom to a send_enabled status (whether a denom is + sendable). + cosmos.base.v1beta1.Coin: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + cosmos.base.tendermint.v1beta1.GetBlockByHeightResponse: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + block: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block + in the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing on the + order first. + + This means that block.AppHash does not include these txs. + title: Data contains the set of transactions included in the block + evidence: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator + signed two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules + for processing a block in the + blockchain, + + including all blockchain data structures + and the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: >- + hashes from the app output from the prev + block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a Tendermint + block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included + in a Commit. + description: >- + Commit contains the evidence that a block + was committed by a set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use + with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of + validators attempting to mislead a light client. + last_commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set + of validators. + description: >- + GetBlockByHeightResponse is the response type for the + Query/GetBlockByHeight RPC method. + cosmos.base.tendermint.v1beta1.GetLatestBlockResponse: + type: object + properties: + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + block: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block + in the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing on the + order first. + + This means that block.AppHash does not include these txs. + title: Data contains the set of transactions included in the block + evidence: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator + signed two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules + for processing a block in the + blockchain, + + including all blockchain data structures + and the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: >- + hashes from the app output from the prev + block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a Tendermint + block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included + in a Commit. + description: >- + Commit contains the evidence that a block + was committed by a set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use + with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of + validators attempting to mislead a light client. + last_commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set + of validators. + description: >- + GetLatestBlockResponse is the response type for the Query/GetLatestBlock + RPC method. + cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse: + type: object + properties: + block_height: + type: string + format: int64 + validators: + type: array + items: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + GetLatestValidatorSetResponse is the response type for the + Query/GetValidatorSetByHeight RPC method. + cosmos.base.tendermint.v1beta1.GetNodeInfoResponse: + type: object + properties: + default_node_info: + type: object + properties: + protocol_version: + type: object + properties: + p2p: + type: string + format: uint64 + block: + type: string + format: uint64 + app: + type: string + format: uint64 + default_node_id: + type: string + listen_addr: + type: string + network: + type: string + version: + type: string + channels: + type: string + format: byte + moniker: + type: string + other: + type: object + properties: + tx_index: + type: string + rpc_address: + type: string + application_version: + type: object + properties: + name: + type: string + app_name: + type: string + version: + type: string + git_commit: + type: string + build_tags: + type: string + go_version: + type: string + build_deps: + type: array + items: + type: object + properties: + path: + type: string + title: module path + version: + type: string + title: module version + sum: + type: string + title: checksum + title: Module is the type for VersionInfo + description: VersionInfo is the type for the GetNodeInfoResponse message. + description: >- + GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC + method. + cosmos.base.tendermint.v1beta1.GetSyncingResponse: + type: object + properties: + syncing: + type: boolean + format: boolean + description: >- + GetSyncingResponse is the response type for the Query/GetSyncing RPC + method. + cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightResponse: + type: object + properties: + block_height: + type: string + format: int64 + validators: + type: array + items: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + GetValidatorSetByHeightResponse is the response type for the + Query/GetValidatorSetByHeight RPC method. + cosmos.base.tendermint.v1beta1.Module: + type: object + properties: + path: + type: string + title: module path + version: + type: string + title: module version + sum: + type: string + title: checksum + title: Module is the type for VersionInfo + cosmos.base.tendermint.v1beta1.Validator: + type: object + properties: + address: + type: string + pub_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + description: Validator is the type for the validator-set. + cosmos.base.tendermint.v1beta1.VersionInfo: + type: object + properties: + name: + type: string + app_name: + type: string + version: + type: string + git_commit: + type: string + build_tags: + type: string + go_version: + type: string + build_deps: + type: array + items: + type: object + properties: + path: + type: string + title: module path + version: + type: string + title: module version + sum: + type: string + title: checksum + title: Module is the type for VersionInfo + description: VersionInfo is the type for the GetNodeInfoResponse message. + tendermint.crypto.PublicKey: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: PublicKey defines the keys available for use with Tendermint Validators + tendermint.p2p.DefaultNodeInfo: + type: object + properties: + protocol_version: + type: object + properties: + p2p: + type: string + format: uint64 + block: + type: string + format: uint64 + app: + type: string + format: uint64 + default_node_id: + type: string + listen_addr: + type: string + network: + type: string + version: + type: string + channels: + type: string + format: byte + moniker: + type: string + other: + type: object + properties: + tx_index: + type: string + rpc_address: + type: string + tendermint.p2p.DefaultNodeInfoOther: + type: object + properties: + tx_index: + type: string + rpc_address: + type: string + tendermint.p2p.ProtocolVersion: + type: object + properties: + p2p: + type: string + format: uint64 + block: + type: string + format: uint64 + app: + type: string + format: uint64 + tendermint.types.Block: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in + the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing on the + order first. + + This means that block.AppHash does not include these txs. + title: Data contains the set of transactions included in the block + evidence: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote + from validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator + signed two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for + processing a block in the blockchain, + + including all blockchain data structures and + the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: >- + hashes from the app output from the prev + block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a Tendermint + block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included + in a Commit. + description: >- + Commit contains the evidence that a block was + committed by a set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for + use with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use + with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of + validators attempting to mislead a light client. + last_commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set of + validators. + tendermint.types.BlockID: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + tendermint.types.BlockIDFlag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + tendermint.types.Commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set of + validators. + tendermint.types.CommitSig: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + tendermint.types.Data: + type: object + properties: + txs: + type: array + items: + type: string + format: byte + description: >- + Txs that will be applied by state @ block.Height+1. + + NOTE: not all txs here are valid. We're just agreeing on the order + first. + + This means that block.AppHash does not include these txs. + title: Data contains the set of transactions included in the block + tendermint.types.DuplicateVoteEvidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from validators + for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from validators + for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator signed two + conflicting votes. + tendermint.types.Evidence: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from + validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from + validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator signed two + conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing + a block in the blockchain, + + including all blockchain data structures and the rules + of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included in a + Commit. + description: >- + Commit contains the evidence that a block was committed by + a set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of validators + attempting to mislead a light client. + tendermint.types.EvidenceList: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + duplicate_vote_evidence: + type: object + properties: + vote_a: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from + validators for + + consensus. + vote_b: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: >- + SignedMsgType is a type of signed message in the + consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: >- + Vote represents a prevote, precommit, or commit vote from + validators for + + consensus. + total_voting_power: + type: string + format: int64 + validator_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + DuplicateVoteEvidence contains evidence of a validator signed + two conflicting votes. + light_client_attack_evidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for + processing a block in the blockchain, + + including all blockchain data structures and the + rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: >- + Header defines the structure of a Tendermint block + header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the + signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: >- + CommitSig is a part of the Vote included in a + Commit. + description: >- + Commit contains the evidence that a block was + committed by a set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use + with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use + with Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of + validators attempting to mislead a light client. + tendermint.types.Header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in the + blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + tendermint.types.LightBlock: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block + in the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set + of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + tendermint.types.LightClientAttackEvidence: + type: object + properties: + conflicting_block: + type: object + properties: + signed_header: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a + block in the blockchain, + + including all blockchain data structures and the rules of + the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: >- + BlockIdFlag indicates which BlcokID the signature is + for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a + set of validators. + validator_set: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with + Tendermint Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + common_height: + type: string + format: int64 + byzantine_validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + timestamp: + type: string + format: date-time + description: >- + LightClientAttackEvidence contains evidence of a set of validators + attempting to mislead a light client. + tendermint.types.PartSetHeader: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + tendermint.types.SignedHeader: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in + the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + commit: + type: object + properties: + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + signatures: + type: array + items: + type: object + properties: + block_id_flag: + type: string + enum: + - BLOCK_ID_FLAG_UNKNOWN + - BLOCK_ID_FLAG_ABSENT + - BLOCK_ID_FLAG_COMMIT + - BLOCK_ID_FLAG_NIL + default: BLOCK_ID_FLAG_UNKNOWN + title: BlockIdFlag indicates which BlcokID the signature is for + validator_address: + type: string + format: byte + timestamp: + type: string + format: date-time + signature: + type: string + format: byte + description: CommitSig is a part of the Vote included in a Commit. + description: >- + Commit contains the evidence that a block was committed by a set of + validators. + tendermint.types.SignedMsgType: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + tendermint.types.Validator: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + tendermint.types.ValidatorSet: + type: object + properties: + validators: + type: array + items: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + proposer: + type: object + properties: + address: + type: string + format: byte + pub_key: + type: object + properties: + ed25519: + type: string + format: byte + secp256k1: + type: string + format: byte + title: >- + PublicKey defines the keys available for use with Tendermint + Validators + voting_power: + type: string + format: int64 + proposer_priority: + type: string + format: int64 + total_voting_power: + type: string + format: int64 + tendermint.types.Vote: + type: object + properties: + type: + type: string + enum: + - SIGNED_MSG_TYPE_UNKNOWN + - SIGNED_MSG_TYPE_PREVOTE + - SIGNED_MSG_TYPE_PRECOMMIT + - SIGNED_MSG_TYPE_PROPOSAL + default: SIGNED_MSG_TYPE_UNKNOWN + description: |- + SignedMsgType is a type of signed message in the consensus. + + - SIGNED_MSG_TYPE_PREVOTE: Votes + - SIGNED_MSG_TYPE_PROPOSAL: Proposals + height: + type: string + format: int64 + round: + type: integer + format: int32 + block_id: + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + title: BlockID + timestamp: + type: string + format: date-time + validator_address: + type: string + format: byte + validator_index: + type: integer + format: int32 + signature: + type: string + format: byte + description: |- + Vote represents a prevote, precommit, or commit vote from validators for + consensus. + tendermint.version.Consensus: + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in the + blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + cosmos.base.v1beta1.DecCoin: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + cosmos.distribution.v1beta1.DelegationDelegatorReward: + type: object + properties: + validator_address: + type: string + reward: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: |- + DelegationDelegatorReward represents the properties + of a delegator's delegation reward. + cosmos.distribution.v1beta1.Params: + type: object + properties: + community_tax: + type: string + base_proposer_reward: + type: string + bonus_proposer_reward: + type: string + withdraw_addr_enabled: + type: boolean + format: boolean + description: Params defines the set of params for the distribution module. + cosmos.distribution.v1beta1.QueryCommunityPoolResponse: + type: object + properties: + pool: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: pool defines community pool's coins. + description: >- + QueryCommunityPoolResponse is the response type for the + Query/CommunityPool + + RPC method. + cosmos.distribution.v1beta1.QueryDelegationRewardsResponse: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: rewards defines the rewards accrued by a delegation. + description: |- + QueryDelegationRewardsResponse is the response type for the + Query/DelegationRewards RPC method. + cosmos.distribution.v1beta1.QueryDelegationTotalRewardsResponse: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + validator_address: + type: string + reward: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: |- + DelegationDelegatorReward represents the properties + of a delegator's delegation reward. + description: rewards defines all the rewards accrued by a delegator. + total: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: total defines the sum of all the rewards. + description: |- + QueryDelegationTotalRewardsResponse is the response type for the + Query/DelegationTotalRewards RPC method. + cosmos.distribution.v1beta1.QueryDelegatorValidatorsResponse: + type: object + properties: + validators: + type: array + items: + type: string + description: validators defines the validators a delegator is delegating for. + description: |- + QueryDelegatorValidatorsResponse is the response type for the + Query/DelegatorValidators RPC method. + cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressResponse: + type: object + properties: + withdraw_address: + type: string + description: withdraw_address defines the delegator address to query for. + description: |- + QueryDelegatorWithdrawAddressResponse is the response type for the + Query/DelegatorWithdrawAddress RPC method. + cosmos.distribution.v1beta1.QueryParamsResponse: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + community_tax: + type: string + base_proposer_reward: + type: string + bonus_proposer_reward: + type: string + withdraw_addr_enabled: + type: boolean + format: boolean + description: QueryParamsResponse is the response type for the Query/Params RPC method. + cosmos.distribution.v1beta1.QueryValidatorCommissionResponse: + type: object + properties: + commission: + description: commission defines the commision the validator received. + type: object + properties: + commission: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + title: |- + QueryValidatorCommissionResponse is the response type for the + Query/ValidatorCommission RPC method + cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse: + type: object + properties: + rewards: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + DecCoin defines a token with a denomination and a decimal + amount. + + + NOTE: The amount field is an Dec which implements the custom + method + + signatures required by gogoproto. + description: >- + ValidatorOutstandingRewards represents outstanding (un-withdrawn) + rewards + + for a validator inexpensive to track, allows simple sanity checks. + description: |- + QueryValidatorOutstandingRewardsResponse is the response type for the + Query/ValidatorOutstandingRewards RPC method. + cosmos.distribution.v1beta1.QueryValidatorSlashesResponse: + type: object + properties: + slashes: + type: array + items: + type: object + properties: + validator_period: + type: string + format: uint64 + fraction: + type: string + description: |- + 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. + description: slashes defines the slashes the validator received. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryValidatorSlashesResponse is the response type for the + Query/ValidatorSlashes RPC method. + cosmos.distribution.v1beta1.ValidatorAccumulatedCommission: + type: object + properties: + commission: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: |- + ValidatorAccumulatedCommission represents accumulated commission + for a validator kept as a running counter, can be withdrawn at any time. + cosmos.distribution.v1beta1.ValidatorOutstandingRewards: + type: object + properties: + rewards: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + DecCoin defines a token with a denomination and a decimal amount. + + NOTE: The amount field is an Dec which implements the custom method + signatures required by gogoproto. + description: |- + ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards + for a validator inexpensive to track, allows simple sanity checks. + cosmos.distribution.v1beta1.ValidatorSlashEvent: + type: object + properties: + validator_period: + type: string + format: uint64 + fraction: + type: string + description: |- + 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. + cosmos.evidence.v1beta1.QueryAllEvidenceResponse: + type: object + properties: + evidence: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: evidence returns all evidences. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryAllEvidenceResponse is the response type for the Query/AllEvidence + RPC + + method. + cosmos.evidence.v1beta1.QueryEvidenceResponse: + type: object + properties: + evidence: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + QueryEvidenceResponse is the response type for the Query/Evidence RPC + method. + cosmos.gov.v1beta1.Deposit: + type: object + properties: + proposal_id: + type: string + format: uint64 + depositor: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: |- + Deposit defines an amount deposited by an account address to an active + proposal. + cosmos.gov.v1beta1.DepositParams: + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial + value: 2 + months. + description: DepositParams defines the params for deposits on governance proposals. + cosmos.gov.v1beta1.Proposal: + type: object + properties: + proposal_id: + type: string + format: uint64 + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: |- + ProposalStatus enumerates the valid statuses of a proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + final_tally_result: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: TallyResult defines a standard tally for a governance proposal. + submit_time: + type: string + format: date-time + deposit_end_time: + type: string + format: date-time + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + voting_start_time: + type: string + format: date-time + voting_end_time: + type: string + format: date-time + description: Proposal defines the core field members of a governance proposal. + cosmos.gov.v1beta1.ProposalStatus: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: |- + ProposalStatus enumerates the valid statuses of a proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + cosmos.gov.v1beta1.QueryDepositResponse: + type: object + properties: + deposit: + type: object + properties: + proposal_id: + type: string + format: uint64 + depositor: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: |- + Deposit defines an amount deposited by an account address to an active + proposal. + description: >- + QueryDepositResponse is the response type for the Query/Deposit RPC + method. + cosmos.gov.v1beta1.QueryDepositsResponse: + type: object + properties: + deposits: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + depositor: + type: string + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + Deposit defines an amount deposited by an account address to an + active + + proposal. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryDepositsResponse is the response type for the Query/Deposits RPC + method. + cosmos.gov.v1beta1.QueryParamsResponse: + type: object + properties: + voting_params: + description: voting_params defines the parameters related to voting. + type: object + properties: + voting_period: + type: string + description: Length of the voting period. + deposit_params: + description: deposit_params defines the parameters related to deposit. + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial + value: 2 + months. + tally_params: + description: tally_params defines the parameters related to tally. + type: object + properties: + quorum: + type: string + format: byte + description: >- + Minimum percentage of total stake needed to vote for a result to + be + considered valid. + threshold: + type: string + format: byte + description: >- + Minimum proportion of Yes votes for proposal to pass. Default + value: 0.5. + veto_threshold: + type: string + format: byte + description: >- + Minimum value of Veto votes to Total votes ratio for proposal to + be + vetoed. Default value: 1/3. + description: QueryParamsResponse is the response type for the Query/Params RPC method. + cosmos.gov.v1beta1.QueryProposalResponse: + type: object + properties: + proposal: + type: object + properties: + proposal_id: + type: string + format: uint64 + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: |- + ProposalStatus enumerates the valid statuses of a proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + final_tally_result: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: TallyResult defines a standard tally for a governance proposal. + submit_time: + type: string + format: date-time + deposit_end_time: + type: string + format: date-time + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + voting_start_time: + type: string + format: date-time + voting_end_time: + type: string + format: date-time + description: Proposal defines the core field members of a governance proposal. + description: >- + QueryProposalResponse is the response type for the Query/Proposal RPC + method. + cosmos.gov.v1beta1.QueryProposalsResponse: + type: object + properties: + proposals: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + content: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + status: + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + description: |- + ProposalStatus enumerates the valid statuses of a proposal. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + final_tally_result: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: TallyResult defines a standard tally for a governance proposal. + submit_time: + type: string + format: date-time + deposit_end_time: + type: string + format: date-time + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + voting_start_time: + type: string + format: date-time + voting_end_time: + type: string + format: date-time + description: Proposal defines the core field members of a governance proposal. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryProposalsResponse is the response type for the Query/Proposals RPC + method. + cosmos.gov.v1beta1.QueryTallyResultResponse: + type: object + properties: + tally: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: TallyResult defines a standard tally for a governance proposal. + description: >- + QueryTallyResultResponse is the response type for the Query/Tally RPC + method. + cosmos.gov.v1beta1.QueryVoteResponse: + type: object + properties: + vote: + type: object + properties: + proposal_id: + type: string + format: uint64 + voter: + type: string + options: + type: array + items: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a given + governance proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: WeightedVoteOption defines a unit of vote for vote split. + description: |- + Vote defines a vote on a governance proposal. + A Vote consists of a proposal ID, the voter, and the vote option. + description: QueryVoteResponse is the response type for the Query/Vote RPC method. + cosmos.gov.v1beta1.QueryVotesResponse: + type: object + properties: + votes: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + voter: + type: string + options: + type: array + items: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a given + governance proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: WeightedVoteOption defines a unit of vote for vote split. + description: |- + Vote defines a vote on a governance proposal. + A Vote consists of a proposal ID, the voter, and the vote option. + description: votes defined the queried votes. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: QueryVotesResponse is the response type for the Query/Votes RPC method. + cosmos.gov.v1beta1.TallyParams: + type: object + properties: + quorum: + type: string + format: byte + description: |- + Minimum percentage of total stake needed to vote for a result to be + considered valid. + threshold: + type: string + format: byte + description: >- + Minimum proportion of Yes votes for proposal to pass. Default value: + 0.5. + veto_threshold: + type: string + format: byte + description: |- + Minimum value of Veto votes to Total votes ratio for proposal to be + vetoed. Default value: 1/3. + description: TallyParams defines the params for tallying votes on governance proposals. + cosmos.gov.v1beta1.TallyResult: + type: object + properties: + 'yes': + type: string + abstain: + type: string + 'no': + type: string + no_with_veto: + type: string + description: TallyResult defines a standard tally for a governance proposal. + cosmos.gov.v1beta1.Vote: + type: object + properties: + proposal_id: + type: string + format: uint64 + voter: + type: string + options: + type: array + items: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a given + governance proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: WeightedVoteOption defines a unit of vote for vote split. + description: |- + Vote defines a vote on a governance proposal. + A Vote consists of a proposal ID, the voter, and the vote option. + cosmos.gov.v1beta1.VoteOption: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a given governance + proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + cosmos.gov.v1beta1.VotingParams: + type: object + properties: + voting_period: + type: string + description: Length of the voting period. + description: VotingParams defines the params for voting on governance proposals. + cosmos.gov.v1beta1.WeightedVoteOption: + type: object + properties: + option: + type: string + enum: + - VOTE_OPTION_UNSPECIFIED + - VOTE_OPTION_YES + - VOTE_OPTION_ABSTAIN + - VOTE_OPTION_NO + - VOTE_OPTION_NO_WITH_VETO + default: VOTE_OPTION_UNSPECIFIED + description: >- + VoteOption enumerates the valid vote options for a given governance + proposal. + + - VOTE_OPTION_UNSPECIFIED: VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + - VOTE_OPTION_YES: VOTE_OPTION_YES defines a yes vote option. + - VOTE_OPTION_ABSTAIN: VOTE_OPTION_ABSTAIN defines an abstain vote option. + - VOTE_OPTION_NO: VOTE_OPTION_NO defines a no vote option. + - VOTE_OPTION_NO_WITH_VETO: VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + weight: + type: string + description: WeightedVoteOption defines a unit of vote for vote split. + cosmos.mint.v1beta1.Params: + type: object + properties: + mint_denom: + type: string + title: type of coin to mint + inflation_rate_change: + type: string + title: maximum annual change in inflation rate + inflation_max: + type: string + title: maximum inflation rate + inflation_min: + type: string + title: minimum inflation rate + goal_bonded: + type: string + title: goal of percent bonded atoms + blocks_per_year: + type: string + format: uint64 + title: expected blocks per year + description: Params holds parameters for the mint module. + cosmos.mint.v1beta1.QueryAnnualProvisionsResponse: + type: object + properties: + annual_provisions: + type: string + format: byte + description: annual_provisions is the current minting annual provisions value. + description: |- + QueryAnnualProvisionsResponse is the response type for the + Query/AnnualProvisions RPC method. + cosmos.mint.v1beta1.QueryInflationResponse: + type: object + properties: + inflation: + type: string + format: byte + description: inflation is the current minting inflation value. + description: |- + QueryInflationResponse is the response type for the Query/Inflation RPC + method. + cosmos.mint.v1beta1.QueryParamsResponse: + type: object + properties: + params: + description: params defines the parameters of the module. + type: object + properties: + mint_denom: + type: string + title: type of coin to mint + inflation_rate_change: + type: string + title: maximum annual change in inflation rate + inflation_max: + type: string + title: maximum inflation rate + inflation_min: + type: string + title: minimum inflation rate + goal_bonded: + type: string + title: goal of percent bonded atoms + blocks_per_year: + type: string + format: uint64 + title: expected blocks per year + description: QueryParamsResponse is the response type for the Query/Params RPC method. + cosmos.params.v1beta1.ParamChange: + type: object + properties: + subspace: + type: string + key: + type: string + value: + type: string + description: |- + ParamChange defines an individual parameter change, for use in + ParameterChangeProposal. + cosmos.params.v1beta1.QueryParamsResponse: + type: object + properties: + param: + description: param defines the queried parameter. + type: object + properties: + subspace: + type: string + key: + type: string + value: + type: string + description: QueryParamsResponse is response type for the Query/Params RPC method. + cosmos.slashing.v1beta1.Params: + type: object + properties: + signed_blocks_window: + type: string + format: int64 + min_signed_per_window: + type: string + format: byte + downtime_jail_duration: + type: string + slash_fraction_double_sign: + type: string + format: byte + slash_fraction_downtime: + type: string + format: byte + description: Params represents the parameters used for by the slashing module. + cosmos.slashing.v1beta1.QueryParamsResponse: + type: object + properties: + params: + type: object + properties: + signed_blocks_window: + type: string + format: int64 + min_signed_per_window: + type: string + format: byte + downtime_jail_duration: + type: string + slash_fraction_double_sign: + type: string + format: byte + slash_fraction_downtime: + type: string + format: byte + description: Params represents the parameters used for by the slashing module. + title: QueryParamsResponse is the response type for the Query/Params RPC method + cosmos.slashing.v1beta1.QuerySigningInfoResponse: + type: object + properties: + val_signing_info: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: Height at which validator was first a candidate OR was unjailed + index_offset: + type: string + format: int64 + description: >- + 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: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to liveness + downtime. + tombstoned: + type: boolean + format: boolean + description: >- + 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: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. + + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for monitoring + their + + liveness activity. + title: val_signing_info is the signing info of requested val cons address + title: >- + QuerySigningInfoResponse is the response type for the Query/SigningInfo + RPC + + method + cosmos.slashing.v1beta1.QuerySigningInfosResponse: + type: object + properties: + info: + type: array + items: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: Height at which validator was first a candidate OR was unjailed + index_offset: + type: string + format: int64 + description: >- + 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: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to liveness + downtime. + tombstoned: + type: boolean + format: boolean + description: >- + 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: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. + + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for + monitoring their + + liveness activity. + title: info is the signing info of all validators + pagination: + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + 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; + } + title: >- + QuerySigningInfosResponse is the response type for the Query/SigningInfos + RPC + + method + cosmos.slashing.v1beta1.ValidatorSigningInfo: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: Height at which validator was first a candidate OR was unjailed + index_offset: + type: string + format: int64 + description: >- + 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: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to liveness + downtime. + tombstoned: + type: boolean + format: boolean + description: >- + 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: + type: string + format: int64 + description: >- + A counter kept to avoid unnecessary array reads. + + Note that `Sum(MissedBlocksBitArray)` always equals + `MissedBlocksCounter`. + description: >- + ValidatorSigningInfo defines a validator's signing info for monitoring + their + + liveness activity. + cosmos.staking.v1beta1.BondStatus: + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + description: |- + BondStatus is the status of a validator. + + - BOND_STATUS_UNSPECIFIED: UNSPECIFIED defines an invalid validator status. + - BOND_STATUS_UNBONDED: UNBONDED defines a validator that is not bonded. + - BOND_STATUS_UNBONDING: UNBONDING defines a validator that is unbonding. + - BOND_STATUS_BONDED: BONDED defines a validator that is bonded. + cosmos.staking.v1beta1.Commission: + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be used for + creating a validator. + type: object + properties: + rate: + type: string + description: 'rate is the commission rate charged to delegators, as a fraction.' + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator can + ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the + validator commission, as a fraction. + update_time: + type: string + format: date-time + description: update_time is the last time the commission rate was changed. + description: Commission defines commission parameters for a given validator. + cosmos.staking.v1beta1.CommissionRates: + type: object + properties: + rate: + type: string + description: 'rate is the commission rate charged to delegators, as a fraction.' + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator can ever + charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the validator + commission, as a fraction. + description: >- + CommissionRates defines the initial commission rates to be used for + creating + + a validator. + cosmos.staking.v1beta1.Delegation: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_address: + type: string + description: validator_address is the bech32-encoded address of the validator. + shares: + type: string + description: shares define the delegation shares received. + description: |- + Delegation represents the bond with tokens held by an account. It is + owned by one delegator, and is associated with the voting power of one + validator. + cosmos.staking.v1beta1.DelegationResponse: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_address: + type: string + description: validator_address is the bech32-encoded address of the validator. + shares: + type: string + description: shares define the delegation shares received. + description: |- + Delegation represents the bond with tokens held by an account. It is + owned by one delegator, and is associated with the voting power of one + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + description: |- + DelegationResponse is equivalent to Delegation except that it contains a + balance in addition to shares which is more suitable for client responses. + cosmos.staking.v1beta1.Description: + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or + Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: security_contact defines an optional email for security contact. + details: + type: string + description: details define other optional details. + description: Description defines a validator description. + cosmos.staking.v1beta1.HistoricalInfo: + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in + the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + title: prev block info + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + valset: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort + or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be + used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of + the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum + self delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing + results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation whose + number of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: >- + HistoricalInfo contains header and validator information for a given + block. + + It is stored as part of staking module's state, which persists the `n` + most + + recent HistoricalInfo + + (`n` is set by the staking module's `historical_entries` parameter). + cosmos.staking.v1beta1.Params: + type: object + properties: + unbonding_time: + type: string + description: unbonding_time is the time duration of unbonding. + max_validators: + type: integer + format: int64 + description: max_validators is the maximum number of validators. + max_entries: + type: integer + format: int64 + description: >- + max_entries is the max entries for either unbonding delegation or + redelegation (per pair/trio). + historical_entries: + type: integer + format: int64 + description: historical_entries is the number of historical entries to persist. + bond_denom: + type: string + description: bond_denom defines the bondable coin denomination. + description: Params defines the parameters for the staking module. + cosmos.staking.v1beta1.Pool: + type: object + properties: + not_bonded_tokens: + type: string + bonded_tokens: + type: string + description: |- + Pool is used for tracking bonded and not-bonded token supply of the bond + denomination. + cosmos.staking.v1beta1.QueryDelegationResponse: + type: object + properties: + delegation_response: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an account. It + is + + owned by one delegator, and is associated with the voting power of + one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it contains + a + + balance in addition to shares which is more suitable for client + responses. + description: >- + QueryDelegationResponse is response type for the Query/Delegation RPC + method. + cosmos.staking.v1beta1.QueryDelegatorDelegationsResponse: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an account. + It is + + owned by one delegator, and is associated with the voting power + of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it + contains a + + balance in addition to shares which is more suitable for client + responses. + description: delegation_responses defines all the delegations' info of a delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorDelegationsResponse is response type for the + Query/DelegatorDelegations RPC method. + cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsResponse: + type: object + properties: + unbonding_responses: + type: array + items: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + completion_time: + type: string + format: date-time + description: completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to + receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object with + relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's unbonding + bonds + + for a single validator in an time-ordered list. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryUnbondingDelegatorDelegationsResponse is response type for the + Query/UnbondingDelegatorDelegations RPC method. + cosmos.staking.v1beta1.QueryDelegatorValidatorResponse: + type: object + properties: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's operator; + bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or + Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be + used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the + validator commission, as a fraction. + update_time: + type: string + format: date-time + description: update_time is the last time the commission rate was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum self + delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing + results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation whose + number of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: |- + QueryDelegatorValidatorResponse response type for the + Query/DelegatorValidator RPC method. + cosmos.staking.v1beta1.QueryDelegatorValidatorsResponse: + type: object + properties: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort + or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be + used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of + the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum + self delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing + results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation whose + number of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: validators defines the the validators' info of a delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorValidatorsResponse is response type for the + Query/DelegatorValidators RPC method. + cosmos.staking.v1beta1.QueryHistoricalInfoResponse: + type: object + properties: + hist: + description: hist defines the historical info at the given height. + type: object + properties: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block + in the blockchain, + + including all blockchain data structures and the rules of the + application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + title: prev block info + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a Tendermint block header. + valset: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of + the serialized + + protocol buffer message. This string must contain at + least + + one "/" character. The last segment of the URL's path + must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in + a canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary + all types that they + + expect it to use in the context of Any. However, for + URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally + set up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based + on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in + the official + + protobuf release, and it is not used for type URLs + beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer + message along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values + in the form + + of utility functions or additional generated methods of the + Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by + default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the + last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield + type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with + an + + additional field `@type` which contains the type URL. + Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom + JSON + + representation, that representation will be embedded adding + a field + + `value` which holds the custom JSON in addition to the + `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from + bonded status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a + validator's delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. + UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which + this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to + be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, + as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase + of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum + self delegation. + description: >- + Validator defines a validator, together with the total amount of + the + + Validator's bond shares and their exchange rate to coins. + Slashing results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation + whose number of + + bond shares is based on the amount of coins delegated divided by + the current + + exchange rate. Voting power can be calculated as total bonded + shares + + multiplied by exchange rate. + description: >- + QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo + RPC + + method. + cosmos.staking.v1beta1.QueryParamsResponse: + type: object + properties: + params: + description: params holds all the parameters of this module. + type: object + properties: + unbonding_time: + type: string + description: unbonding_time is the time duration of unbonding. + max_validators: + type: integer + format: int64 + description: max_validators is the maximum number of validators. + max_entries: + type: integer + format: int64 + description: >- + max_entries is the max entries for either unbonding delegation or + redelegation (per pair/trio). + historical_entries: + type: integer + format: int64 + description: historical_entries is the number of historical entries to persist. + bond_denom: + type: string + description: bond_denom defines the bondable coin denomination. + description: QueryParamsResponse is response type for the Query/Params RPC method. + cosmos.staking.v1beta1.QueryPoolResponse: + type: object + properties: + pool: + description: pool defines the pool info. + type: object + properties: + not_bonded_tokens: + type: string + bonded_tokens: + type: string + description: QueryPoolResponse is response type for the Query/Pool RPC method. + cosmos.staking.v1beta1.QueryRedelegationsResponse: + type: object + properties: + redelegation_responses: + type: array + items: + type: object + properties: + redelegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation source + operator address. + validator_dst_address: + type: string + description: >- + validator_dst_address is the validator redelegation + destination operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation + completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when + redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator + shares created by redelegation. + description: >- + RedelegationEntry defines a redelegation object with + relevant metadata. + description: entries are the redelegation entries. + description: >- + Redelegation contains the list of a particular delegator's + redelegating bonds + + from a particular source validator to a particular destination + validator. + entries: + type: array + items: + type: object + properties: + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the + redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation + completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when + redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator + shares created by redelegation. + description: >- + RedelegationEntry defines a redelegation object with + relevant metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a RedelegationEntry + except that it + + contains a balance in addition to shares which is more + suitable for client + + responses. + description: >- + RedelegationResponse is equivalent to a Redelegation except that its + entries + + contain a balance in addition to shares which is more suitable for + client + + responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: >- + QueryRedelegationsResponse is response type for the Query/Redelegations + RPC + + method. + cosmos.staking.v1beta1.QueryUnbondingDelegationResponse: + type: object + properties: + unbond: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_address: + type: string + description: validator_address is the bech32-encoded address of the validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + completion_time: + type: string + format: date-time + description: completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to + receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object with + relevant metadata. + description: entries are the unbonding delegation entries. + description: |- + UnbondingDelegation stores all of a single delegator's unbonding bonds + for a single validator in an time-ordered list. + description: |- + QueryDelegationResponse is response type for the Query/UnbondingDelegation + RPC method. + cosmos.staking.v1beta1.QueryValidatorDelegationsResponse: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an account. + It is + + owned by one delegator, and is associated with the voting power + of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it + contains a + + balance in addition to shares which is more suitable for client + responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + title: |- + QueryValidatorDelegationsResponse is response type for the + Query/ValidatorDelegations RPC method + cosmos.staking.v1beta1.QueryValidatorResponse: + type: object + properties: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's operator; + bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or + Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be + used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the + validator commission, as a fraction. + update_time: + type: string + format: date-time + description: update_time is the last time the commission rate was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum self + delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing + results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation whose + number of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + title: QueryValidatorResponse is response type for the Query/Validator RPC method + cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsResponse: + type: object + properties: + unbonding_responses: + type: array + items: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the + delegator. + validator_address: + type: string + description: >- + validator_address is the bech32-encoded address of the + validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took + place. + completion_time: + type: string + format: date-time + description: completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to + receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object with + relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's unbonding + bonds + + for a single validator in an time-ordered list. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryValidatorUnbondingDelegationsResponse is response type for the + Query/ValidatorUnbondingDelegations RPC method. + cosmos.staking.v1beta1.QueryValidatorsResponse: + type: object + properties: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's + operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort + or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security + contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the + validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be + used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which + validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of + the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was + changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum + self delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing + results in + + a decrease in the exchange rate, allowing correct calculation of + future + + undelegations without iterating over delegators. When coins are + delegated to + + this validator, the validator is credited with a delegation whose + number of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: validators contains all the queried validators. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + title: >- + QueryValidatorsResponse is response type for the Query/Validators RPC + method + cosmos.staking.v1beta1.Redelegation: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation source operator + address. + validator_dst_address: + type: string + description: >- + validator_dst_address is the validator redelegation destination + operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation took + place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation + completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when redelegation + started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares created + by redelegation. + description: >- + RedelegationEntry defines a redelegation object with relevant + metadata. + description: entries are the redelegation entries. + description: >- + Redelegation contains the list of a particular delegator's redelegating + bonds + + from a particular source validator to a particular destination validator. + cosmos.staking.v1beta1.RedelegationEntry: + type: object + properties: + creation_height: + type: string + format: int64 + description: creation_height defines the height which the redelegation took place. + completion_time: + type: string + format: date-time + description: completion_time defines the unix time for redelegation completion. + initial_balance: + type: string + description: initial_balance defines the initial balance when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares created by + redelegation. + description: RedelegationEntry defines a redelegation object with relevant metadata. + cosmos.staking.v1beta1.RedelegationEntryResponse: + type: object + properties: + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation took + place. + completion_time: + type: string + format: date-time + description: completion_time defines the unix time for redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when redelegation + started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares created + by redelegation. + description: >- + RedelegationEntry defines a redelegation object with relevant + metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a RedelegationEntry except that + it + + contains a balance in addition to shares which is more suitable for client + + responses. + cosmos.staking.v1beta1.RedelegationResponse: + type: object + properties: + redelegation: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation source + operator address. + validator_dst_address: + type: string + description: >- + validator_dst_address is the validator redelegation destination + operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation + completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when + redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares + created by redelegation. + description: >- + RedelegationEntry defines a redelegation object with relevant + metadata. + description: entries are the redelegation entries. + description: >- + Redelegation contains the list of a particular delegator's + redelegating bonds + + from a particular source validator to a particular destination + validator. + entries: + type: array + items: + type: object + properties: + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation + took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation + completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when + redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares + created by redelegation. + description: >- + RedelegationEntry defines a redelegation object with relevant + metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a RedelegationEntry + except that it + + contains a balance in addition to shares which is more suitable for + client + + responses. + description: >- + RedelegationResponse is equivalent to a Redelegation except that its + entries + + contain a balance in addition to shares which is more suitable for client + + responses. + cosmos.staking.v1beta1.UnbondingDelegation: + type: object + properties: + delegator_address: + type: string + description: delegator_address is the bech32-encoded address of the delegator. + validator_address: + type: string + description: validator_address is the bech32-encoded address of the validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: creation_height is the height which the unbonding took place. + completion_time: + type: string + format: date-time + description: completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to + receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object with relevant + metadata. + description: entries are the unbonding delegation entries. + description: |- + UnbondingDelegation stores all of a single delegator's unbonding bonds + for a single validator in an time-ordered list. + cosmos.staking.v1beta1.UnbondingDelegationEntry: + type: object + properties: + creation_height: + type: string + format: int64 + description: creation_height is the height which the unbonding took place. + completion_time: + type: string + format: date-time + description: completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to receive at + completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + description: >- + UnbondingDelegationEntry defines an unbonding object with relevant + metadata. + cosmos.staking.v1beta1.Validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's operator; bech + encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + format: boolean + description: >- + jailed defined whether the validator has been jailed from bonded + status or not. + status: + description: status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's + delegators. + description: + description: description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or + Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: security_contact defines an optional email for security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this + validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the validator + to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be used + for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a + fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator + can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the + validator commission, as a fraction. + update_time: + type: string + format: date-time + description: update_time is the last time the commission rate was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum self + delegation. + description: >- + Validator defines a validator, together with the total amount of the + + Validator's bond shares and their exchange rate to coins. Slashing results + in + + a decrease in the exchange rate, allowing correct calculation of future + + undelegations without iterating over delegators. When coins are delegated + to + + this validator, the validator is credited with a delegation whose number + of + + bond shares is based on the amount of coins delegated divided by the + current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + cosmos.base.abci.v1beta1.ABCIMessageLog: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the key and value + are + + strings instead of raw bytes. + description: |- + StringEvent defines en Event object wrapper where all the attributes + contain key/value pairs that are strings instead of raw bytes. + description: |- + Events contains a slice of Event objects that were emitted during some + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx ABCI message + log. + cosmos.base.abci.v1beta1.Attribute: + type: object + properties: + key: + type: string + value: + type: string + description: |- + Attribute defines an attribute wrapper where the key and value are + strings instead of raw bytes. + cosmos.base.abci.v1beta1.GasInfo: + type: object + properties: + gas_wanted: + type: string + format: uint64 + description: GasWanted is the maximum units of work we allow this tx to perform. + gas_used: + type: string + format: uint64 + description: GasUsed is the amount of gas actually consumed. + description: GasInfo defines tx execution gas context. + cosmos.base.abci.v1beta1.Result: + type: object + properties: + data: + type: string + format: byte + description: >- + 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: + type: string + description: Log contains the log information from message or handler execution. + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + format: byte + value: + type: string + format: byte + index: + type: boolean + format: boolean + description: >- + EventAttribute is a single key-value pair, associated with an + event. + description: >- + Event allows application developers to attach additional information + to + + ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and + ResponseDeliverTx. + + Later, transactions may be queried using these events. + description: >- + Events contains a slice of Event objects that were emitted during + message + + or handler execution. + description: Result is the union of ResponseFormat and ResponseCheckTx. + cosmos.base.abci.v1beta1.StringEvent: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: |- + Attribute defines an attribute wrapper where the key and value are + strings instead of raw bytes. + description: |- + StringEvent defines en Event object wrapper where all the attributes + contain key/value pairs that are strings instead of raw bytes. + cosmos.base.abci.v1beta1.TxResponse: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: 'Result bytes, if any.' + raw_log: + type: string + description: |- + The output of the application's logger (raw string). May be + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the key and + value are + + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where all the + attributes + + contain key/value pairs that are strings instead of raw bytes. + description: >- + Events contains a slice of Event objects that were emitted + during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx ABCI + message log. + description: >- + The output of the application's logger (typed). May be + non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + 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. + description: >- + TxResponse defines a structure containing relevant tx data and metadata. + The + + tags are stringified and the log is JSON decoded. + cosmos.crypto.multisig.v1beta1.CompactBitArray: + type: object + properties: + extra_bits_stored: + type: integer + format: int64 + elems: + type: string + format: byte + description: |- + 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. + cosmos.tx.signing.v1beta1.SignMode: + type: string + enum: + - SIGN_MODE_UNSPECIFIED + - SIGN_MODE_DIRECT + - SIGN_MODE_TEXTUAL + - SIGN_MODE_LEGACY_AMINO_JSON + default: SIGN_MODE_UNSPECIFIED + description: |- + SignMode represents a signing mode with its own security guarantees. + + - SIGN_MODE_UNSPECIFIED: SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + rejected + - SIGN_MODE_DIRECT: SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + verified with raw bytes from Tx + - SIGN_MODE_TEXTUAL: SIGN_MODE_TEXTUAL is a future signing mode that will verify some + human-readable textual representation on top of the binary representation + from SIGN_MODE_DIRECT + - SIGN_MODE_LEGACY_AMINO_JSON: SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + Amino JSON and will be removed in the future + cosmos.tx.v1beta1.AuthInfo: + type: object + properties: + signer_infos: + type: array + items: + $ref: '#/definitions/cosmos.tx.v1beta1.SignerInfo' + description: >- + signer_infos defines the signing modes for the required signers. The + number + + and order of elements must match the required signers from TxBody's + + messages. The first element is the primary signer and the one which + pays + + the fee. + fee: + description: >- + Fee is the fee and gas limit for the transaction. The first signer is + the + + primary signer and the one which pays the fee. The fee can be + calculated + + based on the cost of evaluating the body and doing signature + verification + + of the signers. This can be estimated via simulation. + type: object + properties: + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. + title: amount is the amount of coins to be paid as a fee + gas_limit: + type: string + format: uint64 + title: >- + gas_limit is the maximum gas that can be used in transaction + processing + + before an out of gas error occurs + payer: + type: string + description: >- + if unset, the first signer is responsible for paying the fees. If + set, the specified account must pay the fees. + + the payer must be a tx signer (and thus have signed this field in + AuthInfo). + + setting this field does *not* change the ordering of required + signers for the transaction. + granter: + type: string + title: >- + if set, the fee payer (either the first signer or the value of the + payer field) requests that a fee grant be used + + to pay fees instead of the fee payer's own balance. If an + appropriate fee grant does not exist or the chain does + + not support fee grants, this will fail + description: |- + AuthInfo describes the fee and signer modes that are used to sign a + transaction. + cosmos.tx.v1beta1.BroadcastMode: + type: string + enum: + - BROADCAST_MODE_UNSPECIFIED + - BROADCAST_MODE_BLOCK + - BROADCAST_MODE_SYNC + - BROADCAST_MODE_ASYNC + default: BROADCAST_MODE_UNSPECIFIED + description: >- + BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC + method. + + - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering + - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + the tx to be committed in a block. + - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + a CheckTx execution response only. + - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + immediately. + cosmos.tx.v1beta1.BroadcastTxRequest: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + mode: + type: string + enum: + - BROADCAST_MODE_UNSPECIFIED + - BROADCAST_MODE_BLOCK + - BROADCAST_MODE_SYNC + - BROADCAST_MODE_ASYNC + default: BROADCAST_MODE_UNSPECIFIED + description: >- + BroadcastMode specifies the broadcast mode for the TxService.Broadcast + RPC method. + + - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering + - BROADCAST_MODE_BLOCK: BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + the tx to be committed in a block. + - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + a CheckTx execution response only. + - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + immediately. + description: |- + BroadcastTxRequest is the request type for the Service.BroadcastTxRequest + RPC method. + cosmos.tx.v1beta1.BroadcastTxResponse: + type: object + properties: + tx_response: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: 'Result bytes, if any.' + raw_log: + type: string + description: |- + The output of the application's logger (raw string). May be + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the key + and value are + + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where all the + attributes + + contain key/value pairs that are strings instead of raw + bytes. + description: >- + Events contains a slice of Event objects that were emitted + during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx ABCI + message log. + description: >- + The output of the application's logger (typed). May be + non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + 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. + description: >- + TxResponse defines a structure containing relevant tx data and + metadata. The + + tags are stringified and the log is JSON decoded. + description: |- + BroadcastTxResponse is the response type for the + Service.BroadcastTx method. + cosmos.tx.v1beta1.Fee: + type: object + properties: + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. + title: amount is the amount of coins to be paid as a fee + gas_limit: + type: string + format: uint64 + title: >- + gas_limit is the maximum gas that can be used in transaction + processing + + before an out of gas error occurs + payer: + type: string + description: >- + if unset, the first signer is responsible for paying the fees. If set, + the specified account must pay the fees. + + the payer must be a tx signer (and thus have signed this field in + AuthInfo). + + setting this field does *not* change the ordering of required signers + for the transaction. + granter: + type: string + title: >- + if set, the fee payer (either the first signer or the value of the + payer field) requests that a fee grant be used + + to pay fees instead of the fee payer's own balance. If an appropriate + fee grant does not exist or the chain does + + not support fee grants, this will fail + description: >- + Fee includes the amount of coins paid in fees and the maximum + + gas to be used by the transaction. The ratio yields an effective + "gasprice", + + which must be above some miminum to be accepted into the mempool. + cosmos.tx.v1beta1.GetTxResponse: + type: object + properties: + tx: + $ref: '#/definitions/cosmos.tx.v1beta1.Tx' + description: tx is the queried transaction. + tx_response: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: 'Result bytes, if any.' + raw_log: + type: string + description: |- + The output of the application's logger (raw string). May be + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the key + and value are + + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where all the + attributes + + contain key/value pairs that are strings instead of raw + bytes. + description: >- + Events contains a slice of Event objects that were emitted + during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx ABCI + message log. + description: >- + The output of the application's logger (typed). May be + non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + 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. + description: >- + TxResponse defines a structure containing relevant tx data and + metadata. The + + tags are stringified and the log is JSON decoded. + description: GetTxResponse is the response type for the Service.GetTx method. + cosmos.tx.v1beta1.GetTxsEventResponse: + type: object + properties: + txs: + type: array + items: + $ref: '#/definitions/cosmos.tx.v1beta1.Tx' + description: txs is the list of queried transactions. + tx_responses: + type: array + items: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: 'Result bytes, if any.' + raw_log: + type: string + description: |- + The output of the application's logger (raw string). May be + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the + key and value are + + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where all + the attributes + + contain key/value pairs that are strings instead of raw + bytes. + description: >- + Events contains a slice of Event objects that were emitted + during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx + ABCI message log. + description: >- + The output of the application's logger (typed). May be + non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + 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. + description: >- + TxResponse defines a structure containing relevant tx data and + metadata. The + + tags are stringified and the log is JSON decoded. + description: tx_responses is the list of queried TxResponses. + pagination: + description: pagination defines an pagination for the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + GetTxsEventResponse is the response type for the Service.TxsByEvents + RPC method. + cosmos.tx.v1beta1.ModeInfo: + type: object + properties: + single: + title: single represents a single signer + type: object + properties: + mode: + title: mode is the signing mode of the single signer + type: string + enum: + - SIGN_MODE_UNSPECIFIED + - SIGN_MODE_DIRECT + - SIGN_MODE_TEXTUAL + - SIGN_MODE_LEGACY_AMINO_JSON + default: SIGN_MODE_UNSPECIFIED + description: >- + SignMode represents a signing mode with its own security + guarantees. + + - SIGN_MODE_UNSPECIFIED: SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + rejected + - SIGN_MODE_DIRECT: SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + verified with raw bytes from Tx + - SIGN_MODE_TEXTUAL: SIGN_MODE_TEXTUAL is a future signing mode that will verify some + human-readable textual representation on top of the binary + representation + + from SIGN_MODE_DIRECT + - SIGN_MODE_LEGACY_AMINO_JSON: SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + Amino JSON and will be removed in the future + multi: + $ref: '#/definitions/cosmos.tx.v1beta1.ModeInfo.Multi' + title: multi represents a nested multisig signer + description: ModeInfo describes the signing mode of a single or nested multisig signer. + cosmos.tx.v1beta1.ModeInfo.Multi: + type: object + properties: + bitarray: + title: bitarray specifies which keys within the multisig are signing + type: object + properties: + extra_bits_stored: + type: integer + format: int64 + elems: + type: string + format: byte + description: >- + 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. + mode_infos: + type: array + items: + $ref: '#/definitions/cosmos.tx.v1beta1.ModeInfo' + title: |- + mode_infos is the corresponding modes of the signers of the multisig + which could include nested multisig public keys + title: Multi is the mode info for a multisig public key + cosmos.tx.v1beta1.ModeInfo.Single: + type: object + properties: + mode: + title: mode is the signing mode of the single signer + type: string + enum: + - SIGN_MODE_UNSPECIFIED + - SIGN_MODE_DIRECT + - SIGN_MODE_TEXTUAL + - SIGN_MODE_LEGACY_AMINO_JSON + default: SIGN_MODE_UNSPECIFIED + description: >- + SignMode represents a signing mode with its own security guarantees. + + - SIGN_MODE_UNSPECIFIED: SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + rejected + - SIGN_MODE_DIRECT: SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + verified with raw bytes from Tx + - SIGN_MODE_TEXTUAL: SIGN_MODE_TEXTUAL is a future signing mode that will verify some + human-readable textual representation on top of the binary + representation + + from SIGN_MODE_DIRECT + - SIGN_MODE_LEGACY_AMINO_JSON: SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + Amino JSON and will be removed in the future + title: |- + Single is the mode info for a single signer. It is structured as a message + to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the + future + cosmos.tx.v1beta1.OrderBy: + type: string + enum: + - ORDER_BY_UNSPECIFIED + - ORDER_BY_ASC + - ORDER_BY_DESC + default: ORDER_BY_UNSPECIFIED + description: >- + - ORDER_BY_UNSPECIFIED: ORDER_BY_UNSPECIFIED specifies an unknown sorting + order. OrderBy defaults to ASC in this case. + - ORDER_BY_ASC: ORDER_BY_ASC defines ascending order + - ORDER_BY_DESC: ORDER_BY_DESC defines descending order + title: OrderBy defines the sorting order + cosmos.tx.v1beta1.SignerInfo: + type: object + properties: + public_key: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up a + type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified + type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + mode_info: + $ref: '#/definitions/cosmos.tx.v1beta1.ModeInfo' + title: |- + mode_info describes the signing mode of the signer and is a nested + structure to support nested multisig pubkey's + sequence: + type: string + format: uint64 + description: >- + sequence is the sequence of the account, which describes the + + number of committed transactions signed by a given address. It is used + to + + prevent replay attacks. + description: |- + SignerInfo describes the public key and signing mode of a single top-level + signer. + cosmos.tx.v1beta1.SimulateRequest: + type: object + properties: + tx: + $ref: '#/definitions/cosmos.tx.v1beta1.Tx' + description: |- + tx is the transaction to simulate. + Deprecated. Send raw tx bytes instead. + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + description: |- + SimulateRequest is the request type for the Service.Simulate + RPC method. + cosmos.tx.v1beta1.SimulateResponse: + type: object + properties: + gas_info: + description: gas_info is the information about gas used in the simulation. + type: object + properties: + gas_wanted: + type: string + format: uint64 + description: >- + GasWanted is the maximum units of work we allow this tx to + perform. + gas_used: + type: string + format: uint64 + description: GasUsed is the amount of gas actually consumed. + result: + description: result is the result of the simulation. + type: object + properties: + data: + type: string + format: byte + description: >- + 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: + type: string + description: >- + Log contains the log information from message or handler + execution. + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + format: byte + value: + type: string + format: byte + index: + type: boolean + format: boolean + description: >- + EventAttribute is a single key-value pair, associated with + an event. + description: >- + Event allows application developers to attach additional + information to + + ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and + ResponseDeliverTx. + + Later, transactions may be queried using these events. + description: >- + Events contains a slice of Event objects that were emitted during + message + + or handler execution. + description: |- + SimulateResponse is the response type for the + Service.SimulateRPC method. + cosmos.tx.v1beta1.Tx: + type: object + properties: + body: + title: body is the processable content of the transaction + type: object + properties: + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages is a list of messages to be executed. The required + signers of + + those messages define the number and order of elements in + AuthInfo's + + signer_infos and Tx's signatures. Each required signer address is + added to + + the list only the first time it occurs. + + By convention, the first required signer (usually from the first + message) + + is referred to as the primary signer and pays the fee for the + whole + + transaction. + memo: + type: string + title: memo is any arbitrary memo to be added to the transaction + timeout_height: + type: string + format: uint64 + title: |- + timeout is the block height after which this transaction will not + be processed by the chain + extension_options: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + extension_options are arbitrary options that can be added by + chains + + when the default options are not sufficient. If any of these are + present + + and can't be handled, the transaction will be rejected + non_critical_extension_options: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all + types that they + + expect it to use in the context of Any. However, for URLs + which use the + + scheme `http`, `https`, or no scheme, one can optionally set + up a type + + server that maps type URLs to message definitions as + follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a + [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on + the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) + might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message + along with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in + the form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default + use + + 'type.googleapis.com/full.type.name' as the type URL and the + unpack + + methods only use the fully qualified type name after the last + '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a + field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + extension_options are arbitrary options that can be added by + chains + + when the default options are not sufficient. If any of these are + present + + and can't be handled, they will be ignored + description: TxBody is the body of a transaction that all signers sign over. + auth_info: + $ref: '#/definitions/cosmos.tx.v1beta1.AuthInfo' + title: |- + auth_info is the authorization related content of the transaction, + specifically signers, signer modes and fee + signatures: + type: array + items: + type: string + format: byte + description: >- + signatures is a list of signatures that matches the length and order + of + + AuthInfo's signer_infos to allow connecting signature meta information + like + + public key and signing mode by position. + description: Tx is the standard type used for broadcasting transactions. + cosmos.tx.v1beta1.TxBody: + type: object + properties: + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages is a list of messages to be executed. The required signers of + + those messages define the number and order of elements in AuthInfo's + + signer_infos and Tx's signatures. Each required signer address is + added to + + the list only the first time it occurs. + + By convention, the first required signer (usually from the first + message) + + is referred to as the primary signer and pays the fee for the whole + + transaction. + memo: + type: string + title: memo is any arbitrary memo to be added to the transaction + timeout_height: + type: string + format: uint64 + title: |- + timeout is the block height after which this transaction will not + be processed by the chain + extension_options: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + extension_options are arbitrary options that can be added by chains + + when the default options are not sufficient. If any of these are + present + + and can't be handled, the transaction will be rejected + non_critical_extension_options: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the + serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must + represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a + canonical form + + (e.g., leading "." is not accepted). + + + In practice, teams usually precompile into the binary all types + that they + + expect it to use in the context of Any. However, for URLs which + use the + + scheme `http`, `https`, or no scheme, one can optionally set up + a type + + server that maps type URLs to message definitions as follows: + + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the + official + + protobuf release, and it is not used for type URLs beginning + with + + type.googleapis.com. + + + Schemes other than `http`, `https` (or the empty scheme) might + be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above + specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along + with a + + URL that describes the type of the serialized message. + + + Protobuf library provides support to pack/unpack Any values in the + form + + of utility functions or additional generated methods of the Any + type. + + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := ptypes.MarshalAny(foo) + ... + foo := &pb.Foo{} + if err := ptypes.UnmarshalAny(any, foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + + + JSON + + ==== + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + title: >- + extension_options are arbitrary options that can be added by chains + + when the default options are not sufficient. If any of these are + present + + and can't be handled, they will be ignored + description: TxBody is the body of a transaction that all signers sign over. + tendermint.abci.Event: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + format: byte + value: + type: string + format: byte + index: + type: boolean + format: boolean + description: 'EventAttribute is a single key-value pair, associated with an event.' + description: >- + Event allows application developers to attach additional information to + + ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and + ResponseDeliverTx. + + Later, transactions may be queried using these events. + tendermint.abci.EventAttribute: + type: object + properties: + key: + type: string + format: byte + value: + type: string + format: byte + index: + type: boolean + format: boolean + description: 'EventAttribute is a single key-value pair, associated with an event.' + cosmos.upgrade.v1beta1.Plan: + type: object + properties: + name: + type: string + description: >- + Sets the name for the upgrade. This name will be used by the upgraded + + version of the software to apply any special "on-upgrade" commands + during + + the first BeginBlock method after the upgrade is applied. It is also + used + + to detect whether a software version can handle a given upgrade. If no + + upgrade handler with this name has been set in the software, it will + be + + assumed that the software is out-of-date when the upgrade Time or + Height is + + reached and the software will exit. + height: + type: string + format: int64 + description: |- + The height at which the upgrade must be performed. + Only used if Time is not set. + info: + type: string + title: |- + Any application specific upgrade info to be included on-chain + such as a git commit that validators could automatically upgrade to + description: >- + Plan specifies information about a planned upgrade and when it should + occur. + cosmos.upgrade.v1beta1.QueryAppliedPlanResponse: + type: object + properties: + height: + type: string + format: int64 + description: height is the block height at which the plan was applied. + description: >- + QueryAppliedPlanResponse is the response type for the Query/AppliedPlan + RPC + + method. + cosmos.upgrade.v1beta1.QueryCurrentPlanResponse: + type: object + properties: + plan: + description: plan is the current upgrade plan. + type: object + properties: + name: + type: string + description: >- + Sets the name for the upgrade. This name will be used by the + upgraded + + version of the software to apply any special "on-upgrade" commands + during + + the first BeginBlock method after the upgrade is applied. It is + also used + + to detect whether a software version can handle a given upgrade. + If no + + upgrade handler with this name has been set in the software, it + will be + + assumed that the software is out-of-date when the upgrade Time or + Height is + + reached and the software will exit. + height: + type: string + format: int64 + description: |- + The height at which the upgrade must be performed. + Only used if Time is not set. + info: + type: string + title: >- + Any application specific upgrade info to be included on-chain + + such as a git commit that validators could automatically upgrade + to + description: >- + QueryCurrentPlanResponse is the response type for the Query/CurrentPlan + RPC + + method. + cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateResponse: + type: object + properties: + upgraded_consensus_state: + type: string + format: byte + description: >- + QueryUpgradedConsensusStateResponse is the response type for the + Query/UpgradedConsensusState + + RPC method. diff --git a/client/docs/swagger_legacy.yaml b/client/docs/swagger_legacy.yaml new file mode 100644 index 000000000000..45973eeb6cec --- /dev/null +++ b/client/docs/swagger_legacy.yaml @@ -0,0 +1,2597 @@ +--- +swagger: "2.0" +info: + version: "3.0" + title: Gaia-Lite for Cosmos + description: A REST interface for state queries, transaction generation and broadcasting. +tags: + - name: Transactions + description: Search, encode, or broadcast transactions. + - name: Tendermint RPC + description: Tendermint APIs, such as query blocks, transactions and validatorset + - name: Auth + description: Authenticate accounts + - name: Bank + description: Create and broadcast transactions + - name: Staking + description: Stake module APIs + - name: Governance + description: Governance module APIs + - name: Slashing + description: Slashing module APIs + - name: Distribution + description: Fee distribution module APIs + - name: Supply + description: Supply module APIs + - name: version + - name: Mint + description: Minting module APIs + - name: Misc + description: Query app version +schemes: + - https +host: api.cosmos.network +securityDefinitions: + kms: + type: basic +paths: + /node_info: + get: + description: Information about the connected node + summary: The properties of the connected node + tags: + - Gaia REST + produces: + - application/json + responses: + 200: + description: Node status + schema: + type: object + properties: + application_version: + properties: + build_tags: + type: string + client_name: + type: string + commit: + type: string + go: + type: string + name: + type: string + server_name: + type: string + version: + type: string + node_info: + properties: + id: + type: string + moniker: + type: string + example: validator-name + protocol_version: + properties: + p2p: + type: string + example: 7 + block: + type: string + example: 10 + app: + type: string + example: 0 + network: + type: string + example: gaia-2 + channels: + type: string + listen_addr: + type: string + example: 192.168.56.1:26656 + version: + description: Tendermint version + type: string + example: 0.15.0 + other: + description: more information on versions + type: object + properties: + tx_index: + type: string + example: on + rpc_address: + type: string + example: tcp://0.0.0.0:26657 + 500: + description: Failed to query node status + /syncing: + get: + summary: Syncing state of node + tags: + - Tendermint RPC + description: Get if the node is currently syning with other nodes + produces: + - application/json + responses: + 200: + description: Node syncing status + schema: + type: object + properties: + syncing: + type: boolean + 500: + description: Server internal error + /blocks/latest: + get: + summary: Get the latest block + tags: + - Tendermint RPC + produces: + - application/json + responses: + 200: + description: The latest block + schema: + $ref: "#/definitions/BlockQuery" + 500: + description: Server internal error + /blocks/{height}: + get: + summary: Get a block at a certain height + tags: + - Tendermint RPC + produces: + - application/json + parameters: + - in: path + name: height + description: Block height + required: true + type: number + x-example: 1 + responses: + 200: + description: The block at a specific height + schema: + $ref: "#/definitions/BlockQuery" + 404: + description: Request block height doesn't + 400: + description: Invalid height + 500: + description: Server internal error + /validatorsets/latest: + get: + summary: Get the latest validator set + tags: + - Tendermint RPC + produces: + - application/json + responses: + 200: + description: The validator set at the latest block height + schema: + type: object + properties: + block_height: + type: string + validators: + type: array + items: + $ref: "#/definitions/TendermintValidator" + 500: + description: Server internal error + /validatorsets/{height}: + get: + summary: Get a validator set a certain height + tags: + - Tendermint RPC + produces: + - application/json + parameters: + - in: path + name: height + description: Block height + required: true + type: number + x-example: 1 + responses: + 200: + description: The validator set at a specific block height + schema: + type: object + properties: + block_height: + type: string + validators: + type: array + items: + $ref: "#/definitions/TendermintValidator" + 404: + description: Block at height not available + 400: + description: Invalid height + 500: + description: Server internal error + /txs/{hash}: + get: + deprecated: true + summary: Get a Tx by hash + tags: + - Transactions + description: Retrieve a transaction using its hash. + produces: + - application/json + parameters: + - in: path + name: hash + description: Tx hash + required: true + type: string + x-example: BCBE20E8D46758B96AE5883B792858296AC06E51435490FBDCAE25A72B3CC76B + responses: + 200: + description: Tx with the provided hash + schema: + $ref: "#/definitions/TxQuery" + 500: + description: Internal Server Error + /txs: + get: + deprecated: true + tags: + - Transactions + summary: Search transactions + description: Search transactions by events. + produces: + - application/json + parameters: + - in: query + name: message.action + type: string + description: "transaction events such as 'message.action=send' which results in the following endpoint: 'GET /txs?message.action=send'. note that each module documents its own events. look for xx_events.md in the corresponding cosmos-sdk/docs/spec directory" + x-example: "send" + - in: query + name: message.sender + type: string + description: "transaction tags with sender: 'GET /txs?message.action=send&message.sender=cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv'" + x-example: "cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv" + - in: query + name: page + description: Page number + type: integer + x-example: 1 + - in: query + name: limit + description: Maximum number of items per page + type: integer + x-example: 1 + - in: query + name: tx.minheight + type: integer + description: "transactions on blocks with height greater or equal this value" + x-example: 25 + - in: query + name: tx.maxheight + type: integer + description: "transactions on blocks with height less than or equal this value" + x-example: 800000 + responses: + 200: + description: All txs matching the provided events + schema: + $ref: "#/definitions/PaginatedQueryTxs" + 400: + description: Invalid search events + 500: + description: Internal Server Error + post: + tags: + - Transactions + summary: Broadcast a signed tx + description: Broadcast a signed tx to a full node + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: txBroadcast + description: The tx must be a signed StdTx. The supported broadcast modes include `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away). + required: true + schema: + type: object + properties: + tx: + $ref: "#/definitions/StdTx" + mode: + type: string + example: block + responses: + 200: + description: Tx broadcasting result + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 500: + description: Internal Server Error + /txs/encode: + post: + deprecated: true + tags: + - Transactions + summary: Encode a transaction to the Amino wire format + description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: tx + description: The tx to encode + required: true + schema: + type: object + properties: + tx: + $ref: "#/definitions/StdTx" + responses: + 200: + description: The tx was successfully decoded and re-encoded + schema: + type: object + properties: + tx: + type: string + example: The base64-encoded Amino-serialized bytes for the tx + 400: + description: The tx was malformated + 500: + description: Server internal error + /txs/decode: + post: + deprecated: true + tags: + - Transactions + summary: Decode a transaction from the Amino wire format + description: Decode a transaction (signed or not) from base64-encoded Amino serialized bytes to JSON + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: tx + description: The tx to decode + required: true + schema: + type: object + properties: + tx: + type: string + example: SvBiXe4KPqijYZoKFFHEzJ8c2HPAfv2EFUcIhx0yPagwEhTy0vPA+GGhCEslKXa4Af0uB+mfShoMCgVzdGFrZRIDMTAwEgQQwJoM + responses: + 200: + description: The tx was successfully decoded + schema: + $ref: "#/definitions/StdTx" + 400: + description: The tx was malformated + 500: + description: Server internal error + /bank/balances/{address}: + get: + deprecated: true + summary: Get the account balances + tags: + - Bank + produces: + - application/json + parameters: + - in: path + name: address + description: Account address in bech32 format + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + 200: + description: Account balances + schema: + type: array + items: + $ref: "#/definitions/Coin" + 500: + description: Server internal error + /bank/accounts/{address}/transfers: + post: + deprecated: true + summary: Send coins from one account to another + tags: + - Bank + consumes: + - application/json + produces: + - application/json + parameters: + - in: path + name: address + description: Account address in bech32 format + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: body + name: account + description: The sender and tx information + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + amount: + type: array + items: + $ref: "#/definitions/Coin" + responses: + 202: + description: Tx was succesfully generated + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid request + 500: + description: Server internal error + /auth/accounts/{address}: + get: + deprecated: true + summary: Get the account information on blockchain + tags: + - Auth + produces: + - application/json + parameters: + - in: path + name: address + description: Account address + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + 200: + description: Account information on the blockchain + schema: + type: object + properties: + type: + type: string + value: + type: object + properties: + account_number: + type: string + address: + type: string + coins: + type: array + items: + $ref: "#/definitions/Coin" + public_key: + $ref: "#/definitions/PublicKey" + sequence: + type: string + 500: + description: Server internel error + /staking/delegators/{delegatorAddr}/delegations: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Get all delegations from a delegator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Delegation" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + post: + summary: Submit delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_address: + $ref: "#/definitions/Address" + validator_address: + $ref: "#/definitions/ValidatorAddress" + amount: + $ref: "#/definitions/Coin" + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator address or delegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query the current delegation between a delegator and a validator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Delegation" + 400: + description: Invalid delegator address or validator address + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/unbonding_delegations: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Get all unbonding delegations from a delegator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/UnbondingDelegation" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + post: + summary: Submit an unbonding delegation + parameters: + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_address: + $ref: "#/definitions/Address" + validator_address: + $ref: "#/definitions/ValidatorAddress" + amount: + $ref: "#/definitions/Coin" + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator address or unbonding delegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query all unbonding delegations between a delegator and a validator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/UnbondingDelegationPair" + 400: + description: Invalid delegator address or validator address + 500: + description: Internal Server Error + /staking/redelegations: + parameters: + - in: query + name: delegator + description: Bech32 AccAddress of Delegator + required: false + type: string + - in: query + name: validator_from + description: Bech32 ValAddress of SrcValidator + required: false + type: string + - in: query + name: validator_to + description: Bech32 ValAddress of DstValidator + required: false + type: string + get: + deprecated: true + summary: Get all redelegations (filter by query params) + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Redelegation" + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/redelegations: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + post: + deprecated: true + summary: Submit a redelegation + parameters: + - in: body + name: delegation + description: The sender and tx information + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_address: + $ref: "#/definitions/Address" + validator_src_addressess: + $ref: "#/definitions/ValidatorAddress" + validator_dst_address: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" + tags: + - Staking + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: Tx was succesfully generated + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator address or redelegation request body + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/validators: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + get: + deprecated: true + summary: Query all validators that a delegator is bonded to + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Validator" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + /staking/delegators/{delegatorAddr}/validators/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query a validator that a delegator is bonded to + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Validator" + 400: + description: Invalid delegator address or validator address + 500: + description: Internal Server Error + /staking/validators: + get: + deprecated: true + summary: Get all validator candidates. By default it returns only the bonded validators. + parameters: + - in: query + name: status + type: string + description: The validator bond status. Must be either 'bonded', 'unbonded', or 'unbonding'. + x-example: bonded + - in: query + name: page + description: The page number. + type: integer + x-example: 1 + - in: query + name: limit + description: The maximum number of items per page. + type: integer + x-example: 1 + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Validator" + 500: + description: Internal Server Error + /staking/validators/{validatorAddr}: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query the information from a single validator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Validator" + 400: + description: Invalid validator address + 500: + description: Internal Server Error + /staking/validators/{validatorAddr}/delegations: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Get all delegations from a validator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Delegation" + 400: + description: Invalid validator address + 500: + description: Internal Server Error + /staking/validators/{validatorAddr}/unbonding_delegations: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Get all unbonding delegations from a validator + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/UnbondingDelegation" + 400: + description: Invalid validator address + 500: + description: Internal Server Error + /staking/pool: + get: + deprecated: true + summary: Get the current state of the staking pool + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: object + properties: + loose_tokens: + type: string + bonded_tokens: + type: string + inflation_last_time: + type: string + inflation: + type: string + date_last_commission_reset: + type: string + prev_bonded_shares: + type: string + 500: + description: Internal Server Error + /staking/parameters: + get: + deprecated: true + summary: Get the current staking parameter values + tags: + - Staking + produces: + - application/json + responses: + 200: + description: OK + schema: + type: object + properties: + inflation_rate_change: + type: string + inflation_max: + type: string + inflation_min: + type: string + goal_bonded: + type: string + unbonding_time: + type: string + max_validators: + type: integer + bond_denom: + type: string + 500: + description: Internal Server Error + # TODO: We need to either fix this route when the validator is not found or add a slashed validator in the contract tests + # /slashing/validators/{validatorPubKey}/signing_info: + # get: + # summary: Get sign info of given validator + # description: Get sign info of given validator + # produces: + # - application/json + # tags: + # - Slashing + # parameters: + # - type: string + # description: Bech32 validator public key + # name: validatorPubKey + # required: true + # in: path + # x-example: cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + # responses: + # 200: + # description: OK + # schema: + # $ref: "#/definitions/SigningInfo" + # 400: + # description: Invalid validator public key + # 500: + # description: Internal Server Error + /slashing/signing_infos: + get: + deprecated: true + summary: Get sign info of given all validators + description: Get sign info of all validators + produces: + - application/json + tags: + - Slashing + parameters: + - in: query + name: page + description: Page number + type: integer + required: true + x-example: 1 + - in: query + name: limit + description: Maximum number of items per page + type: integer + required: true + x-example: 5 + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/SigningInfo" + 400: + description: Invalid validator public key for one of the validators + 500: + description: Internal Server Error + /slashing/validators/{validatorAddr}/unjail: + post: + deprecated: true + summary: Unjail a jailed validator + description: Send transaction to unjail a jailed validator + consumes: + - application/json + produces: + - application/json + tags: + - Slashing + parameters: + - type: string + description: Bech32 validator address + name: validatorAddr + required: true + in: path + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + - description: "" + name: UnjailBody + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/StdTx" + responses: + 200: + description: Tx was succesfully generated + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid validator address or base_req + 500: + description: Internal Server Error + /slashing/parameters: + get: + deprecated: true + summary: Get the current slashing parameters + tags: + - Slashing + produces: + - application/json + responses: + 200: + description: OK + schema: + type: object + properties: + max_evidence_age: + type: string + signed_blocks_window: + type: string + min_signed_per_window: + type: string + double_sign_unbond_duration: + type: string + downtime_unbond_duration: + type: string + slash_fraction_double_sign: + type: string + slash_fraction_downtime: + type: string + 500: + description: Internal Server Error + /gov/proposals: + post: + deprecated: true + summary: Submit a proposal + description: Send transaction to submit a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - description: valid value of `"proposal_type"` can be `"text"`, `"parameter_change"`, `"software_upgrade"` + name: post_proposal_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + title: + type: string + description: + type: string + proposal_type: + type: string + example: "text" + proposer: + $ref: "#/definitions/Address" + initial_deposit: + type: array + items: + $ref: "#/definitions/Coin" + responses: + 200: + description: Tx was succesfully generated + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid proposal body + 500: + description: Internal Server Error + get: + deprecated: true + summary: Query proposals + description: Query proposals information with parameters + produces: + - application/json + tags: + - Governance + parameters: + - in: query + name: voter + description: voter address + required: false + type: string + - in: query + name: depositor + description: depositor address + required: false + type: string + - in: query + name: status + description: proposal status, valid values can be `"deposit_period"`, `"voting_period"`, `"passed"`, `"rejected"` + required: false + type: string + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/TextProposal" + 400: + description: Invalid query parameters + 500: + description: Internal Server Error + /gov/proposals/param_change: + post: + deprecated: true + summary: Generate a parameter change proposal transaction + description: Generate a parameter change proposal transaction + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - description: The parameter change proposal body that contains all parameter changes + name: post_proposal_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + title: + type: string + x-example: "Param Change" + description: + type: string + x-example: "Update max validators" + proposer: + $ref: "#/definitions/Address" + deposit: + type: array + items: + $ref: "#/definitions/Coin" + changes: + type: array + items: + $ref: "#/definitions/ParamChange" + responses: + 200: + description: The transaction was succesfully generated + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid proposal body + 500: + description: Internal Server Error + /gov/proposals/{proposalId}: + get: + deprecated: true + summary: Query a proposal + description: Query a proposal by id + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: "2" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TextProposal" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/proposer: + get: + deprecated: true + summary: Query proposer + description: Query for the proposer for a proposal + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: "2" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Proposer" + 400: + description: Invalid proposal ID + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/deposits: + get: + deprecated: true + summary: Query deposits + description: Query deposits by proposalId + produces: + - application/json + tags: + - Governance + parameters: + - type: string + name: proposalId + required: true + in: path + x-example: "2" + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Deposit" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error + post: + deprecated: true + summary: Deposit tokens to a proposal + description: Send transaction to deposit tokens to a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + - description: "" + name: post_deposit_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + depositor: + $ref: "#/definitions/Address" + amount: + type: array + items: + $ref: "#/definitions/Coin" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid proposal id or deposit body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/deposits/{depositor}: + get: + deprecated: true + summary: Query deposit + description: Query deposit by proposalId and depositor address + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + - type: string + description: Bech32 depositor address + name: depositor + required: true + in: path + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Deposit" + 400: + description: Invalid proposal id or depositor address + 404: + description: Found no deposit + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/votes: + get: + deprecated: true + summary: Query voters + description: Query voters information by proposalId + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Vote" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error + post: + deprecated: true + summary: Vote a proposal + description: Send transaction to vote a proposal + consumes: + - application/json + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` + name: post_vote_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + voter: + $ref: "#/definitions/Address" + option: + type: string + example: "yes" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid proposal id or vote body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/votes/{voter}: + get: + deprecated: true + summary: Query vote + description: Query vote information by proposal Id and voter address + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + - type: string + description: Bech32 voter address + name: voter + required: true + in: path + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Vote" + 400: + description: Invalid proposal id or voter address + 404: + description: Found no vote + 500: + description: Internal Server Error + /gov/proposals/{proposalId}/tally: + get: + deprecated: true + summary: Get a proposal's tally result at the current time + description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. + produces: + - application/json + tags: + - Governance + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + x-example: "2" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TallyResult" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error + /gov/parameters/deposit: + get: + deprecated: true + summary: Query governance deposit parameters + description: Query governance deposit parameters. The max_deposit_period units are in nanoseconds. + produces: + - application/json + tags: + - Governance + responses: + 200: + description: OK + schema: + type: object + properties: + min_deposit: + type: array + items: + $ref: "#/definitions/Coin" + max_deposit_period: + type: string + example: "86400000000000" + 400: + description: is not a valid query request path + 404: + description: Found no deposit parameters + 500: + description: Internal Server Error + /gov/parameters/tallying: + get: + deprecated: true + summary: Query governance tally parameters + description: Query governance tally parameters + produces: + - application/json + tags: + - Governance + responses: + 200: + description: OK + schema: + properties: + threshold: + type: string + example: "0.5000000000" + veto: + type: string + example: "0.3340000000" + governance_penalty: + type: string + example: "0.0100000000" + 400: + description: is not a valid query request path + 404: + description: Found no tally parameters + 500: + description: Internal Server Error + /gov/parameters/voting: + get: + deprecated: true + summary: Query governance voting parameters + description: Query governance voting parameters. The voting_period units are in nanoseconds. + produces: + - application/json + tags: + - Governance + responses: + 200: + description: OK + schema: + properties: + voting_period: + type: string + example: "86400000000000" + 400: + description: is not a valid query request path + 404: + description: Found no voting parameters + 500: + description: Internal Server Error + /distribution/delegators/{delegatorAddr}/rewards: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf + get: + deprecated: true + summary: Get the total rewards balance from all delegations + description: Get the sum of all the rewards earned by delegations by a single delegator + produces: + - application/json + tags: + - Distribution + responses: + 200: + description: OK + schema: + $ref: "#/definitions/DelegatorTotalRewards" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + post: + deprecated: true + summary: Withdraw all the delegator's delegation rewards + description: Withdraw all the delegator's delegation rewards + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator address + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos16xyempempp92x9hyzz9wrgf94r6j9h5f06pxxv + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Query a delegation reward + description: Query a single delegation reward by a delegator + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Coin" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + post: + deprecated: true + summary: Withdraw a delegation reward + description: Withdraw a delegator's delegation reward from a single validator + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator address or delegation body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /distribution/delegators/{delegatorAddr}/withdraw_address: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf + get: + deprecated: true + summary: Get the rewards withdrawal address + description: Get the delegations' rewards withdrawal address. This is the address in which the user will receive the reward funds + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Address" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error + post: + deprecated: true + summary: Replace the rewards withdrawal address + description: Replace the delegations' rewards withdrawal address for a new one. + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" + withdraw_address: + $ref: "#/definitions/Address" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid delegator or withdraw address + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /distribution/validators/{validatorAddr}: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Validator distribution information + description: Query the distribution information of a single validator + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/ValidatorDistInfo" + 400: + description: Invalid validator address + 500: + description: Internal Server Error + /distribution/validators/{validatorAddr}/outstanding_rewards: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Fee distribution outstanding rewards of a single validator + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Coin" + 500: + description: Internal Server Error + /distribution/validators/{validatorAddr}/rewards: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + x-example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + get: + deprecated: true + summary: Commission and self-delegation rewards of a single validator + description: Query the commission and self-delegation rewards of validator. + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Coin" + 400: + description: Invalid validator address + 500: + description: Internal Server Error + post: + deprecated: true + summary: Withdraw the validator's rewards + description: Withdraw the validator's self-delegation and commissions rewards + tags: + - Distribution + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" + responses: + 200: + description: OK + schema: + $ref: "#/definitions/StdTx" + 400: + description: Invalid validator address + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /distribution/community_pool: + get: + deprecated: true + summary: Community pool parameters + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Coin" + 500: + description: Internal Server Error + /distribution/parameters: + get: + deprecated: true + summary: Fee distribution parameters + tags: + - Distribution + produces: + - application/json + responses: + 200: + description: OK + schema: + properties: + base_proposer_reward: + type: string + bonus_proposer_reward: + type: string + community_tax: + type: string + 500: + description: Internal Server Error + /minting/parameters: + get: + deprecated: true + summary: Minting module parameters + tags: + - Mint + produces: + - application/json + responses: + 200: + description: OK + schema: + properties: + mint_denom: + type: string + inflation_rate_change: + type: string + inflation_max: + type: string + inflation_min: + type: string + goal_bonded: + type: string + blocks_per_year: + type: string + 500: + description: Internal Server Error + /minting/inflation: + get: + deprecated: true + summary: Current minting inflation value + tags: + - Mint + produces: + - application/json + responses: + 200: + description: OK + schema: + type: string + 500: + description: Internal Server Error + /minting/annual-provisions: + get: + deprecated: true + summary: Current minting annual provisions value + tags: + - Mint + produces: + - application/json + responses: + 200: + description: OK + schema: + type: string + 500: + description: Internal Server Error + /supply/total: + get: + deprecated: true + summary: Total supply of coins in the chain + tags: + - Supply + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/Supply" + 500: + description: Internal Server Error + /supply/total/{denomination}: + parameters: + - in: path + name: denomination + description: Coin denomination + required: true + type: string + x-example: uatom + get: + deprecated: true + summary: Total supply of a single coin denomination + tags: + - Supply + produces: + - application/json + responses: + 200: + description: OK + schema: + type: string + 400: + description: Invalid coin denomination + 500: + description: Internal Server Error +definitions: + CheckTxResult: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + $ref: "#/definitions/KVPair" + example: + code: 0 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - "" + - "" + DeliverTxResult: + type: object + properties: + code: + type: integer + data: + type: string + gas_used: + type: integer + gas_wanted: + type: integer + info: + type: string + log: + type: string + tags: + type: array + items: + $ref: "#/definitions/KVPair" + example: + code: 5 + data: data + log: log + gas_used: 5000 + gas_wanted: 10000 + info: info + tags: + - "" + - "" + BroadcastTxCommitResult: + type: object + properties: + check_tx: + $ref: "#/definitions/CheckTxResult" + deliver_tx: + $ref: "#/definitions/DeliverTxResult" + hash: + $ref: "#/definitions/Hash" + height: + type: integer + KVPair: + type: object + properties: + key: + type: string + value: + type: string + Msg: + type: string + Address: + type: string + description: bech32 encoded address + example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 + ValidatorAddress: + type: string + description: bech32 encoded address + example: cosmosvaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l + Coin: + type: object + properties: + denom: + type: string + example: stake + amount: + type: string + example: "50" + Hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + TxQuery: + type: object + properties: + hash: + type: string + example: "D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656" + height: + type: number + example: 368 + tx: + $ref: "#/definitions/StdTx" + result: + type: object + properties: + log: + type: string + gas_wanted: + type: string + example: "200000" + gas_used: + type: string + example: "26354" + tags: + type: array + items: + $ref: "#/definitions/KVPair" + PaginatedQueryTxs: + type: object + properties: + total_count: + type: number + example: 1 + count: + type: number + example: 1 + page_number: + type: number + example: 1 + page_total: + type: number + example: 1 + limit: + type: number + example: 30 + txs: + type: array + items: + $ref: "#/definitions/TxQuery" + StdTx: + type: object + properties: + msg: + type: array + items: + $ref: "#/definitions/Msg" + fee: + type: object + properties: + gas: + type: string + amount: + type: array + items: + $ref: "#/definitions/Coin" + memo: + type: string + signature: + type: object + properties: + signature: + type: string + example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= + pub_key: + type: object + properties: + type: + type: string + example: "tendermint/PubKeySecp256k1" + value: + type: string + example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH" + account_number: + type: string + example: "0" + sequence: + type: string + example: "0" + BlockID: + type: object + properties: + hash: + $ref: "#/definitions/Hash" + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + $ref: "#/definitions/Hash" + BlockHeader: + type: object + properties: + chain_id: + type: string + example: cosmoshub-2 + height: + type: number + example: 1 + time: + type: string + example: "2017-12-30T05:53:09.287+01:00" + num_txs: + type: number + example: 0 + last_block_id: + $ref: "#/definitions/BlockID" + total_txs: + type: number + example: 35 + last_commit_hash: + $ref: "#/definitions/Hash" + data_hash: + $ref: "#/definitions/Hash" + validators_hash: + $ref: "#/definitions/Hash" + next_validators_hash: + $ref: "#/definitions/Hash" + consensus_hash: + $ref: "#/definitions/Hash" + app_hash: + $ref: "#/definitions/Hash" + last_results_hash: + $ref: "#/definitions/Hash" + evidence_hash: + $ref: "#/definitions/Hash" + proposer_address: + $ref: "#/definitions/Address" + version: + type: object + properties: + block: + type: string + example: 10 + app: + type: string + example: 0 + Block: + type: object + properties: + header: + $ref: "#/definitions/BlockHeader" + txs: + type: array + items: + type: string + evidence: + type: array + items: + type: string + last_commit: + type: object + properties: + block_id: + $ref: "#/definitions/BlockID" + precommits: + type: array + items: + type: object + properties: + validator_address: + type: string + validator_index: + type: string + example: "0" + height: + type: string + example: "0" + round: + type: string + example: "0" + timestamp: + type: string + example: "2017-12-30T05:53:09.287+01:00" + type: + type: number + example: 2 + block_id: + $ref: "#/definitions/BlockID" + signature: + type: string + example: "7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ==" + BlockQuery: + type: object + properties: + block_meta: + type: object + properties: + header: + $ref: "#/definitions/BlockHeader" + block_id: + $ref: "#/definitions/BlockID" + block: + $ref: "#/definitions/Block" + DelegationDelegatorReward: + type: object + properties: + validator_address: + $ref: "#/definitions/ValidatorAddress" + reward: + type: array + items: + $ref: "#/definitions/Coin" + DelegatorTotalRewards: + type: object + properties: + rewards: + type: array + items: + $ref: "#/definitions/DelegationDelegatorReward" + total: + type: array + items: + $ref: "#/definitions/Coin" + BaseReq: + type: object + properties: + from: + type: string + example: "cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc" + description: Sender address or Keybase name to generate a transaction + memo: + type: string + example: "Sent via Cosmos Voyager 🚀" + chain_id: + type: string + example: "Cosmos-Hub" + account_number: + type: string + example: "0" + sequence: + type: string + example: "1" + gas: + type: string + example: "200000" + gas_adjustment: + type: string + example: "1.2" + fees: + type: array + items: + $ref: "#/definitions/Coin" + simulate: + type: boolean + example: false + description: Estimate gas for a transaction (cannot be used in conjunction with generate_only) + TendermintValidator: + type: object + properties: + address: + $ref: "#/definitions/ValidatorAddress" + pub_key: + type: string + example: cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + voting_power: + type: string + example: "1000" + proposer_priority: + type: string + example: "1000" + TextProposal: + type: object + properties: + proposal_id: + type: integer + title: + type: string + description: + type: string + proposal_type: + type: string + proposal_status: + type: string + final_tally_result: + $ref: "#/definitions/TallyResult" + submit_time: + type: string + total_deposit: + type: array + items: + $ref: "#/definitions/Coin" + voting_start_time: + type: string + Proposer: + type: object + properties: + proposal_id: + type: string + proposer: + type: string + Deposit: + type: object + properties: + amount: + type: array + items: + $ref: "#/definitions/Coin" + proposal_id: + type: string + depositor: + $ref: "#/definitions/Address" + TallyResult: + type: object + properties: + yes: + type: string + example: "0.0000000000" + abstain: + type: string + example: "0.0000000000" + no: + type: string + example: "0.0000000000" + no_with_veto: + type: string + example: "0.0000000000" + Vote: + type: object + properties: + voter: + type: string + proposal_id: + type: string + option: + type: string + Validator: + type: object + properties: + operator_address: + $ref: "#/definitions/ValidatorAddress" + consensus_pubkey: + type: string + example: cosmosvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cs3k9cuf + jailed: + type: boolean + status: + type: integer + tokens: + type: string + delegator_shares: + type: string + description: + type: object + properties: + moniker: + type: string + identity: + type: string + website: + type: string + security_contact: + type: string + details: + type: string + bond_height: + type: string + example: "0" + bond_intra_tx_counter: + type: integer + example: 0 + unbonding_height: + type: string + example: "0" + unbonding_time: + type: string + example: "1970-01-01T00:00:00Z" + commission: + type: object + properties: + rate: + type: string + example: "0" + max_rate: + type: string + example: "0" + max_change_rate: + type: string + example: "0" + update_time: + type: string + example: "1970-01-01T00:00:00Z" + Delegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + shares: + type: string + balance: + $ref: "#/definitions/Coin" + UnbondingDelegationPair: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + entries: + type: array + items: + $ref: "#/definitions/UnbondingEntries" + UnbondingEntries: + type: object + properties: + initial_balance: + type: string + balance: + type: string + creation_height: + type: string + min_time: + type: string + UnbondingDelegation: + type: object + properties: + delegator_address: + type: string + validator_address: + type: string + initial_balance: + type: string + balance: + type: string + creation_height: + type: integer + min_time: + type: integer + Redelegation: + type: object + properties: + delegator_address: + type: string + validator_src_address: + type: string + validator_dst_address: + type: string + entries: + type: array + items: + $ref: "#/definitions/Redelegation" + RedelegationEntry: + type: object + properties: + creation_height: + type: integer + completion_time: + type: integer + initial_balance: + type: string + balance: + type: string + shares_dst: + type: string + ValidatorDistInfo: + type: object + properties: + operator_address: + $ref: "#/definitions/ValidatorAddress" + self_bond_rewards: + type: array + items: + $ref: "#/definitions/Coin" + val_commission: + type: array + items: + $ref: "#/definitions/Coin" + PublicKey: + type: object + properties: + type: + type: string + value: + type: string + SigningInfo: + type: object + properties: + start_height: + type: string + index_offset: + type: string + jailed_until: + type: string + missed_blocks_counter: + type: string + ParamChange: + type: object + properties: + subspace: + type: string + example: "staking" + key: + type: string + example: "MaxValidators" + subkey: + type: string + example: "" + value: + type: object + Supply: + type: object + properties: + total: + type: array + items: + $ref: "#/definitions/Coin" diff --git a/client/flags/flags.go b/client/flags/flags.go index 2cdaa5f2137f..b305412d4667 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -2,18 +2,14 @@ package flags import ( "fmt" - "os" "strconv" "github.com/spf13/cobra" - "github.com/spf13/viper" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" ) -// nolint const ( // DefaultGasAdjustment is applied to gas estimates to avoid tx execution // failures due to state changes that might occur between the tx simulation @@ -23,7 +19,7 @@ const ( GasFlagAuto = "auto" // DefaultKeyringBackend - DefaultKeyringBackend = keys.BackendOS + DefaultKeyringBackend = keyring.BackendOS // BroadcastBlock defines a tx broadcasting mode where the client waits for // the tx to be committed in a block. @@ -35,179 +31,138 @@ const ( // immediately. BroadcastAsync = "async" - FlagHome = tmcli.HomeFlag - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagHeight = "height" - FlagGasAdjustment = "gas-adjustment" - FlagTrustNode = "trust-node" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagMemo = "memo" - FlagFees = "fees" - FlagGasPrices = "gas-prices" - FlagBroadcastMode = "broadcast-mode" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" - FlagIndentResponse = "indent" - FlagListenAddr = "laddr" - FlagMaxOpenConnections = "max-open" - FlagRPCReadTimeout = "read-timeout" - FlagRPCWriteTimeout = "write-timeout" - FlagOutputDocument = "output-document" // inspired by wget -O - FlagSkipConfirmation = "yes" - FlagKeyringBackend = "keyring-backend" - FlagPage = "page" - FlagLimit = "limit" + // SignModeDirect is the value of the --sign-mode flag for SIGN_MODE_DIRECT + SignModeDirect = "direct" + // SignModeLegacyAminoJSON is the value of the --sign-mode flag for SIGN_MODE_LEGACY_AMINO_JSON + SignModeLegacyAminoJSON = "amino-json" ) -// LineBreak can be included in a command list to provide a blank line -// to help with readability -var ( - LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} - GasFlagVar = GasSetting{Gas: DefaultGasLimit} +// List of CLI flags +const ( + FlagHome = tmcli.HomeFlag + FlagKeyringDir = "keyring-dir" + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagGasAdjustment = "gas-adjustment" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagMemo = "memo" + FlagFees = "fees" + FlagGas = "gas" + FlagGasPrices = "gas-prices" + FlagBroadcastMode = "broadcast-mode" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagOffline = "offline" + FlagOutputDocument = "output-document" // inspired by wget -O + FlagSkipConfirmation = "yes" + FlagProve = "prove" + FlagKeyringBackend = "keyring-backend" + FlagPage = "page" + FlagLimit = "limit" + FlagSignMode = "sign-mode" + FlagPageKey = "page-key" + FlagOffset = "offset" + FlagCountTotal = "count-total" + FlagTimeoutHeight = "timeout-height" + FlagKeyAlgorithm = "algo" + + // Tendermint logging flags + FlagLogLevel = "log_level" + FlagLogFormat = "log_format" ) -// GetCommands adds common flags to query commands -func GetCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") - c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") - c.Flags().String(FlagNode, "tcp://localhost:26657", ": to Tendermint RPC interface for this chain") - c.Flags().Int64(FlagHeight, 0, "Use a specific height to query state at (this can error if the node is pruning state)") +// LineBreak can be included in a command list to provide a blank line +// to help with readability +var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} - viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) - viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) - viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) +// AddQueryFlagsToCmd adds common flags to a module query command. +func AddQueryFlagsToCmd(cmd *cobra.Command) { + cmd.Flags().String(FlagNode, "tcp://localhost:26657", ": to Tendermint RPC interface for this chain") + cmd.Flags().Int64(FlagHeight, 0, "Use a specific height to query state at (this can error if the node is pruning state)") + cmd.Flags().StringP(tmcli.OutputFlag, "o", "text", "Output format (text|json)") - c.MarkFlagRequired(FlagChainID) + cmd.MarkFlagRequired(FlagChainID) - c.SetErr(c.ErrOrStderr()) - } - return cmds + cmd.SetErr(cmd.ErrOrStderr()) + cmd.SetOut(cmd.OutOrStdout()) } -// PostCommands adds common flags for commands to post tx -func PostCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") - c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") - c.Flags().Uint64P(FlagAccountNumber, "a", 0, "The account number of the signing account (offline mode only)") - c.Flags().Uint64P(FlagSequence, "s", 0, "The sequence number of the signing account (offline mode only)") - c.Flags().String(FlagMemo, "", "Memo to send along with transaction") - c.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 10uatom") - c.Flags().String(FlagGasPrices, "", "Gas prices to determine the transaction fee (e.g. 10uatom)") - c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") - c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") - c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ") - c.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)") - c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)") - c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it") - c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible and the node operates offline)") - c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation") - c.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|test)") - - // --gas can accept integers and "simulate" - c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf( - "gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", - GasFlagAuto, DefaultGasLimit, - )) - viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) - viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) - viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) - viper.BindPFlag(FlagKeyringBackend, c.Flags().Lookup(FlagKeyringBackend)) - - c.MarkFlagRequired(FlagChainID) - - c.SetErr(c.ErrOrStderr()) - } - return cmds +// AddTxFlagsToCmd adds common flags to a module tx command. +func AddTxFlagsToCmd(cmd *cobra.Command) { + cmd.Flags().String(FlagKeyringDir, "", "The client Keyring directory; if omitted, the default 'home' directory will be used") + cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") + cmd.Flags().Uint64P(FlagAccountNumber, "a", 0, "The account number of the signing account (offline mode only)") + cmd.Flags().Uint64P(FlagSequence, "s", 0, "The sequence number of the signing account (offline mode only)") + cmd.Flags().String(FlagMemo, "", "Memo to send along with transaction") + cmd.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 10uatom") + cmd.Flags().String(FlagGasPrices, "", "Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)") + cmd.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") + cmd.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") + cmd.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ") + cmd.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)") + cmd.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it") + cmd.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)") + cmd.Flags().Bool(FlagOffline, false, "Offline mode (does not allow any online functionality") + cmd.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation") + cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test|memory)") + 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") + + // --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)) + + cmd.MarkFlagRequired(FlagChainID) + + cmd.SetErr(cmd.ErrOrStderr()) + cmd.SetOut(cmd.OutOrStdout()) } -// RegisterRestServerFlags registers the flags required for rest server -func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { - cmd = GetCommands(cmd)[0] - cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Uint(FlagMaxOpenConnections, 1000, "The number of maximum open connections") - cmd.Flags().Uint(FlagRPCReadTimeout, 10, "The RPC read timeout (in seconds)") - cmd.Flags().Uint(FlagRPCWriteTimeout, 10, "The RPC write timeout (in seconds)") - - return cmd +// AddPaginationFlagsToCmd adds common pagination flags to cmd +func AddPaginationFlagsToCmd(cmd *cobra.Command, query string) { + cmd.Flags().Uint64(FlagPage, 1, fmt.Sprintf("pagination page of %s to query for. This sets offset to a multiple of limit", query)) + cmd.Flags().String(FlagPageKey, "", fmt.Sprintf("pagination page-key of %s to query for", query)) + cmd.Flags().Uint64(FlagOffset, 0, fmt.Sprintf("pagination offset of %s to query for", query)) + cmd.Flags().Uint64(FlagLimit, 100, fmt.Sprintf("pagination limit of %s to query for", query)) + cmd.Flags().Bool(FlagCountTotal, false, fmt.Sprintf("count total number of records in %s to query for", query)) } -// Gas flag parsing functions - // GasSetting encapsulates the possible values passed through the --gas flag. type GasSetting struct { Simulate bool Gas uint64 } -// Type returns the flag's value type. -func (v *GasSetting) Type() string { return "string" } - -// Set parses and sets the value of the --gas flag. -func (v *GasSetting) Set(s string) (err error) { - v.Simulate, v.Gas, err = ParseGas(s) - return -} - func (v *GasSetting) String() string { if v.Simulate { return GasFlagAuto } + return strconv.FormatUint(v.Gas, 10) } -// ParseGas parses the value of the gas option. -func ParseGas(gasStr string) (simulateAndExecute bool, gas uint64, err error) { +// ParseGasSetting parses a string gas value. The value may either be 'auto', +// which indicates a transaction should be executed in simulate mode to +// automatically find a sufficient gas value, or a string integer. It returns an +// error if a string integer is provided which cannot be parsed. +func ParseGasSetting(gasStr string) (GasSetting, error) { switch gasStr { case "": - gas = DefaultGasLimit + return GasSetting{false, DefaultGasLimit}, nil + case GasFlagAuto: - simulateAndExecute = true + return GasSetting{true, 0}, nil + default: - gas, err = strconv.ParseUint(gasStr, 10, 64) + gas, err := strconv.ParseUint(gasStr, 10, 64) if err != nil { - err = fmt.Errorf("gas must be either integer or %q", GasFlagAuto) - return + return GasSetting{}, fmt.Errorf("gas must be either integer or %s", GasFlagAuto) } - } - return -} -// NewCompletionCmd builds a cobra.Command that generate bash completion -// scripts for the given root command. If hidden is true, the command -// will not show up in the root command's list of available commands. -func NewCompletionCmd(rootCmd *cobra.Command, hidden bool) *cobra.Command { - flagZsh := "zsh" - cmd := &cobra.Command{ - Use: "completion", - Short: "Generate Bash/Zsh completion script to STDOUT", - Long: `To load completion script run - -. <(completion_script) - -To configure your bash shell to load completions for each session add to your bashrc - -# ~/.bashrc or ~/.profile -. <(completion_script) -`, - RunE: func(_ *cobra.Command, _ []string) error { - if viper.GetBool(flagZsh) { - return rootCmd.GenZshCompletion(os.Stdout) - } - return rootCmd.GenBashCompletion(os.Stdout) - }, - Hidden: hidden, - Args: cobra.NoArgs, + return GasSetting{false, gas}, nil } - - cmd.Flags().Bool(flagZsh, false, "Generate Zsh completion script") - - return cmd } diff --git a/client/flags/flags_test.go b/client/flags/flags_test.go new file mode 100644 index 000000000000..5cc591bebbe7 --- /dev/null +++ b/client/flags/flags_test.go @@ -0,0 +1,38 @@ +package flags_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client/flags" +) + +func TestParseGasSetting(t *testing.T) { + testCases := []struct { + name string + input string + expected flags.GasSetting + expectErr bool + }{ + {"empty input", "", flags.GasSetting{false, flags.DefaultGasLimit}, false}, + {"auto", flags.GasFlagAuto, flags.GasSetting{true, 0}, false}, + {"valid custom gas", "73800", flags.GasSetting{false, 73800}, false}, + {"invalid custom gas", "-73800", flags.GasSetting{false, 0}, true}, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + gs, err := flags.ParseGasSetting(tc.input) + + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expected, gs) + } + }) + } +} diff --git a/client/grpc/reflection/reflection.go b/client/grpc/reflection/reflection.go new file mode 100644 index 000000000000..eb07ea86bd73 --- /dev/null +++ b/client/grpc/reflection/reflection.go @@ -0,0 +1,45 @@ +package reflection + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +type reflectionServiceServer struct { + interfaceRegistry types.InterfaceRegistry +} + +// NewReflectionServiceServer creates a new reflectionServiceServer. +func NewReflectionServiceServer(interfaceRegistry types.InterfaceRegistry) ReflectionServiceServer { + return &reflectionServiceServer{interfaceRegistry: interfaceRegistry} +} + +var _ ReflectionServiceServer = (*reflectionServiceServer)(nil) + +// ListAllInterfaces implements the ListAllInterfaces method of the +// ReflectionServiceServer interface. +func (r reflectionServiceServer) ListAllInterfaces(_ context.Context, _ *ListAllInterfacesRequest) (*ListAllInterfacesResponse, error) { + ifaces := r.interfaceRegistry.ListAllInterfaces() + + return &ListAllInterfacesResponse{InterfaceNames: ifaces}, nil +} + +// ListImplementations implements the ListImplementations method of the +// ReflectionServiceServer interface. +func (r reflectionServiceServer) ListImplementations(_ context.Context, req *ListImplementationsRequest) (*ListImplementationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.InterfaceName == "" { + return nil, status.Error(codes.InvalidArgument, "invalid interface name") + } + + impls := r.interfaceRegistry.ListImplementations(req.InterfaceName) + + return &ListImplementationsResponse{ImplementationMessageNames: impls}, nil +} diff --git a/client/grpc/reflection/reflection.pb.go b/client/grpc/reflection/reflection.pb.go new file mode 100644 index 000000000000..66dbef0c7105 --- /dev/null +++ b/client/grpc/reflection/reflection.pb.go @@ -0,0 +1,936 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/reflection/v1beta1/reflection.proto + +package reflection + +import ( + context "context" + fmt "fmt" + 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 + +// ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. +type ListAllInterfacesRequest struct { +} + +func (m *ListAllInterfacesRequest) Reset() { *m = ListAllInterfacesRequest{} } +func (m *ListAllInterfacesRequest) String() string { return proto.CompactTextString(m) } +func (*ListAllInterfacesRequest) ProtoMessage() {} +func (*ListAllInterfacesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d48c054165687f5c, []int{0} +} +func (m *ListAllInterfacesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListAllInterfacesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListAllInterfacesRequest.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 *ListAllInterfacesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListAllInterfacesRequest.Merge(m, src) +} +func (m *ListAllInterfacesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListAllInterfacesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListAllInterfacesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListAllInterfacesRequest proto.InternalMessageInfo + +// ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. +type ListAllInterfacesResponse struct { + // interface_names is an array of all the registered interfaces. + InterfaceNames []string `protobuf:"bytes,1,rep,name=interface_names,json=interfaceNames,proto3" json:"interface_names,omitempty"` +} + +func (m *ListAllInterfacesResponse) Reset() { *m = ListAllInterfacesResponse{} } +func (m *ListAllInterfacesResponse) String() string { return proto.CompactTextString(m) } +func (*ListAllInterfacesResponse) ProtoMessage() {} +func (*ListAllInterfacesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d48c054165687f5c, []int{1} +} +func (m *ListAllInterfacesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListAllInterfacesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListAllInterfacesResponse.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 *ListAllInterfacesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListAllInterfacesResponse.Merge(m, src) +} +func (m *ListAllInterfacesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListAllInterfacesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListAllInterfacesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListAllInterfacesResponse proto.InternalMessageInfo + +func (m *ListAllInterfacesResponse) GetInterfaceNames() []string { + if m != nil { + return m.InterfaceNames + } + return nil +} + +// ListImplementationsRequest is the request type of the ListImplementations +// RPC. +type ListImplementationsRequest struct { + // interface_name defines the interface to query the implementations for. + InterfaceName string `protobuf:"bytes,1,opt,name=interface_name,json=interfaceName,proto3" json:"interface_name,omitempty"` +} + +func (m *ListImplementationsRequest) Reset() { *m = ListImplementationsRequest{} } +func (m *ListImplementationsRequest) String() string { return proto.CompactTextString(m) } +func (*ListImplementationsRequest) ProtoMessage() {} +func (*ListImplementationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d48c054165687f5c, []int{2} +} +func (m *ListImplementationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListImplementationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListImplementationsRequest.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 *ListImplementationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListImplementationsRequest.Merge(m, src) +} +func (m *ListImplementationsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListImplementationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListImplementationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListImplementationsRequest proto.InternalMessageInfo + +func (m *ListImplementationsRequest) GetInterfaceName() string { + if m != nil { + return m.InterfaceName + } + return "" +} + +// ListImplementationsResponse is the response type of the ListImplementations +// RPC. +type ListImplementationsResponse struct { + ImplementationMessageNames []string `protobuf:"bytes,1,rep,name=implementation_message_names,json=implementationMessageNames,proto3" json:"implementation_message_names,omitempty"` +} + +func (m *ListImplementationsResponse) Reset() { *m = ListImplementationsResponse{} } +func (m *ListImplementationsResponse) String() string { return proto.CompactTextString(m) } +func (*ListImplementationsResponse) ProtoMessage() {} +func (*ListImplementationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d48c054165687f5c, []int{3} +} +func (m *ListImplementationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListImplementationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListImplementationsResponse.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 *ListImplementationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListImplementationsResponse.Merge(m, src) +} +func (m *ListImplementationsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListImplementationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListImplementationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListImplementationsResponse proto.InternalMessageInfo + +func (m *ListImplementationsResponse) GetImplementationMessageNames() []string { + if m != nil { + return m.ImplementationMessageNames + } + return nil +} + +func init() { + proto.RegisterType((*ListAllInterfacesRequest)(nil), "cosmos.base.reflection.v1beta1.ListAllInterfacesRequest") + proto.RegisterType((*ListAllInterfacesResponse)(nil), "cosmos.base.reflection.v1beta1.ListAllInterfacesResponse") + proto.RegisterType((*ListImplementationsRequest)(nil), "cosmos.base.reflection.v1beta1.ListImplementationsRequest") + proto.RegisterType((*ListImplementationsResponse)(nil), "cosmos.base.reflection.v1beta1.ListImplementationsResponse") +} + +func init() { + proto.RegisterFile("cosmos/base/reflection/v1beta1/reflection.proto", fileDescriptor_d48c054165687f5c) +} + +var fileDescriptor_d48c054165687f5c = []byte{ + // 395 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0x2f, 0x4a, 0x4d, 0xcb, 0x49, 0x4d, 0x2e, 0xc9, + 0xcc, 0xcf, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0x44, 0x12, 0xd2, 0x2b, 0x28, 0xca, + 0x2f, 0xc9, 0x17, 0x92, 0x83, 0x68, 0xd0, 0x03, 0x69, 0xd0, 0x43, 0x92, 0x85, 0x6a, 0x90, 0x92, + 0x49, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0xcc, 0xcb, 0xcb, 0x2f, + 0x49, 0x04, 0x49, 0x17, 0x43, 0x74, 0x2b, 0x49, 0x71, 0x49, 0xf8, 0x64, 0x16, 0x97, 0x38, 0xe6, + 0xe4, 0x78, 0xe6, 0x95, 0xa4, 0x16, 0xa5, 0x25, 0x26, 0xa7, 0x16, 0x07, 0xa5, 0x16, 0x96, 0xa6, + 0x16, 0x97, 0x28, 0xb9, 0x70, 0x49, 0x62, 0x91, 0x2b, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x52, + 0xe7, 0xe2, 0xcf, 0x84, 0x89, 0xc6, 0xe7, 0x25, 0xe6, 0xa6, 0x16, 0x4b, 0x30, 0x2a, 0x30, 0x6b, + 0x70, 0x06, 0xf1, 0xc1, 0x85, 0xfd, 0x40, 0xa2, 0x4a, 0xce, 0x5c, 0x52, 0x20, 0x53, 0x3c, 0x73, + 0x0b, 0x72, 0x52, 0x73, 0x53, 0xf3, 0xa0, 0xd6, 0x43, 0xed, 0x10, 0x52, 0xe5, 0xe2, 0x43, 0x35, + 0x46, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x88, 0x17, 0xc5, 0x14, 0xa5, 0x78, 0x2e, 0x69, 0xac, + 0x86, 0x40, 0x1d, 0xe3, 0xc0, 0x25, 0x93, 0x89, 0x22, 0x15, 0x9f, 0x9b, 0x5a, 0x5c, 0x9c, 0x98, + 0x8e, 0xea, 0x32, 0x29, 0x54, 0x35, 0xbe, 0x10, 0x25, 0x60, 0x57, 0x1a, 0xed, 0x60, 0xe6, 0x12, + 0x0c, 0x82, 0x07, 0x5e, 0x70, 0x6a, 0x51, 0x59, 0x66, 0x72, 0xaa, 0xd0, 0x1e, 0x46, 0x2e, 0x41, + 0x8c, 0x20, 0x10, 0xb2, 0xd0, 0xc3, 0x1f, 0xe4, 0x7a, 0xb8, 0x42, 0x54, 0xca, 0x92, 0x0c, 0x9d, + 0x10, 0x2f, 0x2a, 0x19, 0x35, 0x5d, 0x7e, 0x32, 0x99, 0x49, 0x47, 0x48, 0x8b, 0x50, 0x02, 0xc9, + 0x44, 0x38, 0xf4, 0x31, 0x23, 0x97, 0x30, 0x96, 0x60, 0x13, 0xb2, 0x22, 0xc6, 0x19, 0xd8, 0x23, + 0x4c, 0xca, 0x9a, 0x2c, 0xbd, 0x50, 0x4f, 0x04, 0x83, 0x3d, 0xe1, 0x2b, 0xe4, 0x4d, 0xbc, 0x27, + 0xf4, 0xab, 0x51, 0xd3, 0x47, 0xad, 0x3e, 0x6a, 0x2c, 0x16, 0x3b, 0xf9, 0x9e, 0x78, 0x24, 0xc7, + 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, + 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x71, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, + 0x7e, 0x2e, 0xcc, 0x42, 0x08, 0xa5, 0x5b, 0x9c, 0x92, 0xad, 0x9f, 0x9c, 0x93, 0x99, 0x9a, 0x57, + 0xa2, 0x9f, 0x5e, 0x54, 0x90, 0x8c, 0xe4, 0x84, 0x24, 0x36, 0x70, 0xc6, 0x30, 0x06, 0x04, 0x00, + 0x00, 0xff, 0xff, 0x32, 0x5b, 0x2b, 0x51, 0x89, 0x03, 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 + +// ReflectionServiceClient is the client API for ReflectionService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ReflectionServiceClient interface { + // ListAllInterfaces lists all the interfaces registered in the interface + // registry. + ListAllInterfaces(ctx context.Context, in *ListAllInterfacesRequest, opts ...grpc.CallOption) (*ListAllInterfacesResponse, error) + // ListImplementations list all the concrete types that implement a given + // interface. + ListImplementations(ctx context.Context, in *ListImplementationsRequest, opts ...grpc.CallOption) (*ListImplementationsResponse, error) +} + +type reflectionServiceClient struct { + cc grpc1.ClientConn +} + +func NewReflectionServiceClient(cc grpc1.ClientConn) ReflectionServiceClient { + return &reflectionServiceClient{cc} +} + +func (c *reflectionServiceClient) ListAllInterfaces(ctx context.Context, in *ListAllInterfacesRequest, opts ...grpc.CallOption) (*ListAllInterfacesResponse, error) { + out := new(ListAllInterfacesResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.reflection.v1beta1.ReflectionService/ListAllInterfaces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reflectionServiceClient) ListImplementations(ctx context.Context, in *ListImplementationsRequest, opts ...grpc.CallOption) (*ListImplementationsResponse, error) { + out := new(ListImplementationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.reflection.v1beta1.ReflectionService/ListImplementations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReflectionServiceServer is the server API for ReflectionService service. +type ReflectionServiceServer interface { + // ListAllInterfaces lists all the interfaces registered in the interface + // registry. + ListAllInterfaces(context.Context, *ListAllInterfacesRequest) (*ListAllInterfacesResponse, error) + // ListImplementations list all the concrete types that implement a given + // interface. + ListImplementations(context.Context, *ListImplementationsRequest) (*ListImplementationsResponse, error) +} + +// UnimplementedReflectionServiceServer can be embedded to have forward compatible implementations. +type UnimplementedReflectionServiceServer struct { +} + +func (*UnimplementedReflectionServiceServer) ListAllInterfaces(ctx context.Context, req *ListAllInterfacesRequest) (*ListAllInterfacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListAllInterfaces not implemented") +} +func (*UnimplementedReflectionServiceServer) ListImplementations(ctx context.Context, req *ListImplementationsRequest) (*ListImplementationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListImplementations not implemented") +} + +func RegisterReflectionServiceServer(s grpc1.Server, srv ReflectionServiceServer) { + s.RegisterService(&_ReflectionService_serviceDesc, srv) +} + +func _ReflectionService_ListAllInterfaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAllInterfacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReflectionServiceServer).ListAllInterfaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.reflection.v1beta1.ReflectionService/ListAllInterfaces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReflectionServiceServer).ListAllInterfaces(ctx, req.(*ListAllInterfacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReflectionService_ListImplementations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListImplementationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReflectionServiceServer).ListImplementations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.reflection.v1beta1.ReflectionService/ListImplementations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReflectionServiceServer).ListImplementations(ctx, req.(*ListImplementationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _ReflectionService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.base.reflection.v1beta1.ReflectionService", + HandlerType: (*ReflectionServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ListAllInterfaces", + Handler: _ReflectionService_ListAllInterfaces_Handler, + }, + { + MethodName: "ListImplementations", + Handler: _ReflectionService_ListImplementations_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/base/reflection/v1beta1/reflection.proto", +} + +func (m *ListAllInterfacesRequest) 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 *ListAllInterfacesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListAllInterfacesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *ListAllInterfacesResponse) 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 *ListAllInterfacesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListAllInterfacesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.InterfaceNames) > 0 { + for iNdEx := len(m.InterfaceNames) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.InterfaceNames[iNdEx]) + copy(dAtA[i:], m.InterfaceNames[iNdEx]) + i = encodeVarintReflection(dAtA, i, uint64(len(m.InterfaceNames[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ListImplementationsRequest) 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 *ListImplementationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListImplementationsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.InterfaceName) > 0 { + i -= len(m.InterfaceName) + copy(dAtA[i:], m.InterfaceName) + i = encodeVarintReflection(dAtA, i, uint64(len(m.InterfaceName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ListImplementationsResponse) 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 *ListImplementationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListImplementationsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ImplementationMessageNames) > 0 { + for iNdEx := len(m.ImplementationMessageNames) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ImplementationMessageNames[iNdEx]) + copy(dAtA[i:], m.ImplementationMessageNames[iNdEx]) + i = encodeVarintReflection(dAtA, i, uint64(len(m.ImplementationMessageNames[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintReflection(dAtA []byte, offset int, v uint64) int { + offset -= sovReflection(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ListAllInterfacesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ListAllInterfacesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.InterfaceNames) > 0 { + for _, s := range m.InterfaceNames { + l = len(s) + n += 1 + l + sovReflection(uint64(l)) + } + } + return n +} + +func (m *ListImplementationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.InterfaceName) + if l > 0 { + n += 1 + l + sovReflection(uint64(l)) + } + return n +} + +func (m *ListImplementationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ImplementationMessageNames) > 0 { + for _, s := range m.ImplementationMessageNames { + l = len(s) + n += 1 + l + sovReflection(uint64(l)) + } + } + return n +} + +func sovReflection(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozReflection(x uint64) (n int) { + return sovReflection(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ListAllInterfacesRequest) 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 ErrIntOverflowReflection + } + 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: ListAllInterfacesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListAllInterfacesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipReflection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReflection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListAllInterfacesResponse) 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 ErrIntOverflowReflection + } + 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: ListAllInterfacesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListAllInterfacesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceNames", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReflection + } + 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 ErrInvalidLengthReflection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReflection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InterfaceNames = append(m.InterfaceNames, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReflection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReflection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListImplementationsRequest) 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 ErrIntOverflowReflection + } + 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: ListImplementationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListImplementationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReflection + } + 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 ErrInvalidLengthReflection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReflection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.InterfaceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReflection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReflection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListImplementationsResponse) 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 ErrIntOverflowReflection + } + 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: ListImplementationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListImplementationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImplementationMessageNames", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowReflection + } + 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 ErrInvalidLengthReflection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthReflection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImplementationMessageNames = append(m.ImplementationMessageNames, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipReflection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthReflection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipReflection(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, ErrIntOverflowReflection + } + 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, ErrIntOverflowReflection + } + 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, ErrIntOverflowReflection + } + 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, ErrInvalidLengthReflection + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupReflection + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthReflection + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthReflection = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowReflection = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupReflection = fmt.Errorf("proto: unexpected end of group") +) diff --git a/client/grpc/reflection/reflection.pb.gw.go b/client/grpc/reflection/reflection.pb.gw.go new file mode 100644 index 000000000000..ab486750e860 --- /dev/null +++ b/client/grpc/reflection/reflection.pb.gw.go @@ -0,0 +1,246 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/base/reflection/v1beta1/reflection.proto + +/* +Package reflection is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package reflection + +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_ReflectionService_ListAllInterfaces_0(ctx context.Context, marshaler runtime.Marshaler, client ReflectionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListAllInterfacesRequest + var metadata runtime.ServerMetadata + + msg, err := client.ListAllInterfaces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReflectionService_ListAllInterfaces_0(ctx context.Context, marshaler runtime.Marshaler, server ReflectionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListAllInterfacesRequest + var metadata runtime.ServerMetadata + + msg, err := server.ListAllInterfaces(ctx, &protoReq) + return msg, metadata, err + +} + +func request_ReflectionService_ListImplementations_0(ctx context.Context, marshaler runtime.Marshaler, client ReflectionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListImplementationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["interface_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "interface_name") + } + + protoReq.InterfaceName, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "interface_name", err) + } + + msg, err := client.ListImplementations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ReflectionService_ListImplementations_0(ctx context.Context, marshaler runtime.Marshaler, server ReflectionServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListImplementationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["interface_name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "interface_name") + } + + protoReq.InterfaceName, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "interface_name", err) + } + + msg, err := server.ListImplementations(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterReflectionServiceHandlerServer registers the http handlers for service ReflectionService to "mux". +// UnaryRPC :call ReflectionServiceServer 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 RegisterReflectionServiceHandlerFromEndpoint instead. +func RegisterReflectionServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ReflectionServiceServer) error { + + mux.Handle("GET", pattern_ReflectionService_ListAllInterfaces_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_ReflectionService_ListAllInterfaces_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReflectionService_ListAllInterfaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReflectionService_ListImplementations_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_ReflectionService_ListImplementations_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReflectionService_ListImplementations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterReflectionServiceHandlerFromEndpoint is same as RegisterReflectionServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterReflectionServiceHandlerFromEndpoint(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 RegisterReflectionServiceHandler(ctx, mux, conn) +} + +// RegisterReflectionServiceHandler registers the http handlers for service ReflectionService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterReflectionServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterReflectionServiceHandlerClient(ctx, mux, NewReflectionServiceClient(conn)) +} + +// RegisterReflectionServiceHandlerClient registers the http handlers for service ReflectionService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ReflectionServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ReflectionServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ReflectionServiceClient" to call the correct interceptors. +func RegisterReflectionServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ReflectionServiceClient) error { + + mux.Handle("GET", pattern_ReflectionService_ListAllInterfaces_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_ReflectionService_ListAllInterfaces_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReflectionService_ListAllInterfaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ReflectionService_ListImplementations_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_ReflectionService_ListImplementations_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ReflectionService_ListImplementations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_ReflectionService_ListAllInterfaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_ReflectionService_ListImplementations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"cosmos", "base", "reflection", "v1beta1", "interfaces", "interface_name", "implementations"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_ReflectionService_ListAllInterfaces_0 = runtime.ForwardResponseMessage + + forward_ReflectionService_ListImplementations_0 = runtime.ForwardResponseMessage +) diff --git a/client/grpc/reflection/reflection_test.go b/client/grpc/reflection/reflection_test.go new file mode 100644 index 000000000000..211fc397ad3e --- /dev/null +++ b/client/grpc/reflection/reflection_test.go @@ -0,0 +1,55 @@ +package reflection_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/grpc/reflection" + "github.com/cosmos/cosmos-sdk/simapp" +) + +type IntegrationTestSuite struct { + suite.Suite + + queryClient reflection.ReflectionServiceClient +} + +func (s *IntegrationTestSuite) SetupSuite() { + app := simapp.Setup(false) + + sdkCtx := app.BaseApp.NewContext(false, tmproto.Header{}) + queryHelper := baseapp.NewQueryServerTestHelper(sdkCtx, app.InterfaceRegistry()) + queryClient := reflection.NewReflectionServiceClient(queryHelper) + s.queryClient = queryClient +} + +func (s IntegrationTestSuite) TestSimulateService() { + // We will test the following interface for testing. + var iface = "cosmos.evidence.v1beta1.Evidence" + + // Test that "cosmos.evidence.v1beta1.Evidence" is included in the + // interfaces. + resIface, err := s.queryClient.ListAllInterfaces( + context.Background(), + &reflection.ListAllInterfacesRequest{}, + ) + s.Require().NoError(err) + s.Require().Contains(resIface.GetInterfaceNames(), iface) + + // Test that "cosmos.evidence.v1beta1.Evidence" has at least the + // Equivocation implementations. + resImpl, err := s.queryClient.ListImplementations( + context.Background(), + &reflection.ListImplementationsRequest{InterfaceName: iface}, + ) + s.Require().NoError(err) + s.Require().Contains(resImpl.GetImplementationMessageNames(), "/cosmos.evidence.v1beta1.Equivocation") +} + +func TestSimulateTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/client/grpc/tmservice/block.go b/client/grpc/tmservice/block.go new file mode 100644 index 000000000000..e92fbec72bd2 --- /dev/null +++ b/client/grpc/tmservice/block.go @@ -0,0 +1,19 @@ +package tmservice + +import ( + "context" + + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/client" +) + +func getBlock(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) +} diff --git a/client/grpc/tmservice/query.pb.go b/client/grpc/tmservice/query.pb.go new file mode 100644 index 000000000000..8055c8c162aa --- /dev/null +++ b/client/grpc/tmservice/query.pb.go @@ -0,0 +1,3900 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/tendermint/v1beta1/query.proto + +package tmservice + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + p2p "github.com/tendermint/tendermint/proto/tendermint/p2p" + types1 "github.com/tendermint/tendermint/proto/tendermint/types" + _ "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 + +// GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +type GetValidatorSetByHeightRequest struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // pagination defines an pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *GetValidatorSetByHeightRequest) Reset() { *m = GetValidatorSetByHeightRequest{} } +func (m *GetValidatorSetByHeightRequest) String() string { return proto.CompactTextString(m) } +func (*GetValidatorSetByHeightRequest) ProtoMessage() {} +func (*GetValidatorSetByHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{0} +} +func (m *GetValidatorSetByHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetValidatorSetByHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetValidatorSetByHeightRequest.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 *GetValidatorSetByHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetValidatorSetByHeightRequest.Merge(m, src) +} +func (m *GetValidatorSetByHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *GetValidatorSetByHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetValidatorSetByHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetValidatorSetByHeightRequest proto.InternalMessageInfo + +func (m *GetValidatorSetByHeightRequest) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *GetValidatorSetByHeightRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +type GetValidatorSetByHeightResponse struct { + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Validators []*Validator `protobuf:"bytes,2,rep,name=validators,proto3" json:"validators,omitempty"` + // pagination defines an pagination for the response. + Pagination *query.PageResponse `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *GetValidatorSetByHeightResponse) Reset() { *m = GetValidatorSetByHeightResponse{} } +func (m *GetValidatorSetByHeightResponse) String() string { return proto.CompactTextString(m) } +func (*GetValidatorSetByHeightResponse) ProtoMessage() {} +func (*GetValidatorSetByHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{1} +} +func (m *GetValidatorSetByHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetValidatorSetByHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetValidatorSetByHeightResponse.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 *GetValidatorSetByHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetValidatorSetByHeightResponse.Merge(m, src) +} +func (m *GetValidatorSetByHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *GetValidatorSetByHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetValidatorSetByHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetValidatorSetByHeightResponse proto.InternalMessageInfo + +func (m *GetValidatorSetByHeightResponse) GetBlockHeight() int64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *GetValidatorSetByHeightResponse) GetValidators() []*Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *GetValidatorSetByHeightResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +type GetLatestValidatorSetRequest struct { + // pagination defines an pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *GetLatestValidatorSetRequest) Reset() { *m = GetLatestValidatorSetRequest{} } +func (m *GetLatestValidatorSetRequest) String() string { return proto.CompactTextString(m) } +func (*GetLatestValidatorSetRequest) ProtoMessage() {} +func (*GetLatestValidatorSetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{2} +} +func (m *GetLatestValidatorSetRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLatestValidatorSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLatestValidatorSetRequest.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 *GetLatestValidatorSetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLatestValidatorSetRequest.Merge(m, src) +} +func (m *GetLatestValidatorSetRequest) XXX_Size() int { + return m.Size() +} +func (m *GetLatestValidatorSetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetLatestValidatorSetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLatestValidatorSetRequest proto.InternalMessageInfo + +func (m *GetLatestValidatorSetRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +type GetLatestValidatorSetResponse struct { + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Validators []*Validator `protobuf:"bytes,2,rep,name=validators,proto3" json:"validators,omitempty"` + // pagination defines an pagination for the response. + Pagination *query.PageResponse `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *GetLatestValidatorSetResponse) Reset() { *m = GetLatestValidatorSetResponse{} } +func (m *GetLatestValidatorSetResponse) String() string { return proto.CompactTextString(m) } +func (*GetLatestValidatorSetResponse) ProtoMessage() {} +func (*GetLatestValidatorSetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{3} +} +func (m *GetLatestValidatorSetResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLatestValidatorSetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLatestValidatorSetResponse.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 *GetLatestValidatorSetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLatestValidatorSetResponse.Merge(m, src) +} +func (m *GetLatestValidatorSetResponse) XXX_Size() int { + return m.Size() +} +func (m *GetLatestValidatorSetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetLatestValidatorSetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLatestValidatorSetResponse proto.InternalMessageInfo + +func (m *GetLatestValidatorSetResponse) GetBlockHeight() int64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *GetLatestValidatorSetResponse) GetValidators() []*Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *GetLatestValidatorSetResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// Validator is the type for the validator-set. +type Validator struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + PubKey *types.Any `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` + VotingPower int64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` + ProposerPriority int64 `protobuf:"varint,4,opt,name=proposer_priority,json=proposerPriority,proto3" json:"proposer_priority,omitempty"` +} + +func (m *Validator) Reset() { *m = Validator{} } +func (m *Validator) String() string { return proto.CompactTextString(m) } +func (*Validator) ProtoMessage() {} +func (*Validator) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{4} +} +func (m *Validator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Validator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Validator.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 *Validator) XXX_Merge(src proto.Message) { + xxx_messageInfo_Validator.Merge(m, src) +} +func (m *Validator) XXX_Size() int { + return m.Size() +} +func (m *Validator) XXX_DiscardUnknown() { + xxx_messageInfo_Validator.DiscardUnknown(m) +} + +var xxx_messageInfo_Validator proto.InternalMessageInfo + +func (m *Validator) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *Validator) GetPubKey() *types.Any { + if m != nil { + return m.PubKey + } + return nil +} + +func (m *Validator) GetVotingPower() int64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +func (m *Validator) GetProposerPriority() int64 { + if m != nil { + return m.ProposerPriority + } + return 0 +} + +// GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. +type GetBlockByHeightRequest struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *GetBlockByHeightRequest) Reset() { *m = GetBlockByHeightRequest{} } +func (m *GetBlockByHeightRequest) String() string { return proto.CompactTextString(m) } +func (*GetBlockByHeightRequest) ProtoMessage() {} +func (*GetBlockByHeightRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{5} +} +func (m *GetBlockByHeightRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetBlockByHeightRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetBlockByHeightRequest.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 *GetBlockByHeightRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetBlockByHeightRequest.Merge(m, src) +} +func (m *GetBlockByHeightRequest) XXX_Size() int { + return m.Size() +} +func (m *GetBlockByHeightRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetBlockByHeightRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetBlockByHeightRequest proto.InternalMessageInfo + +func (m *GetBlockByHeightRequest) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +// GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. +type GetBlockByHeightResponse struct { + BlockId *types1.BlockID `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + Block *types1.Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (m *GetBlockByHeightResponse) Reset() { *m = GetBlockByHeightResponse{} } +func (m *GetBlockByHeightResponse) String() string { return proto.CompactTextString(m) } +func (*GetBlockByHeightResponse) ProtoMessage() {} +func (*GetBlockByHeightResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{6} +} +func (m *GetBlockByHeightResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetBlockByHeightResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetBlockByHeightResponse.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 *GetBlockByHeightResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetBlockByHeightResponse.Merge(m, src) +} +func (m *GetBlockByHeightResponse) XXX_Size() int { + return m.Size() +} +func (m *GetBlockByHeightResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetBlockByHeightResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetBlockByHeightResponse proto.InternalMessageInfo + +func (m *GetBlockByHeightResponse) GetBlockId() *types1.BlockID { + if m != nil { + return m.BlockId + } + return nil +} + +func (m *GetBlockByHeightResponse) GetBlock() *types1.Block { + if m != nil { + return m.Block + } + return nil +} + +// GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method. +type GetLatestBlockRequest struct { +} + +func (m *GetLatestBlockRequest) Reset() { *m = GetLatestBlockRequest{} } +func (m *GetLatestBlockRequest) String() string { return proto.CompactTextString(m) } +func (*GetLatestBlockRequest) ProtoMessage() {} +func (*GetLatestBlockRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{7} +} +func (m *GetLatestBlockRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLatestBlockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLatestBlockRequest.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 *GetLatestBlockRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLatestBlockRequest.Merge(m, src) +} +func (m *GetLatestBlockRequest) XXX_Size() int { + return m.Size() +} +func (m *GetLatestBlockRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetLatestBlockRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLatestBlockRequest proto.InternalMessageInfo + +// GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method. +type GetLatestBlockResponse struct { + BlockId *types1.BlockID `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + Block *types1.Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (m *GetLatestBlockResponse) Reset() { *m = GetLatestBlockResponse{} } +func (m *GetLatestBlockResponse) String() string { return proto.CompactTextString(m) } +func (*GetLatestBlockResponse) ProtoMessage() {} +func (*GetLatestBlockResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{8} +} +func (m *GetLatestBlockResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLatestBlockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLatestBlockResponse.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 *GetLatestBlockResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLatestBlockResponse.Merge(m, src) +} +func (m *GetLatestBlockResponse) XXX_Size() int { + return m.Size() +} +func (m *GetLatestBlockResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetLatestBlockResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLatestBlockResponse proto.InternalMessageInfo + +func (m *GetLatestBlockResponse) GetBlockId() *types1.BlockID { + if m != nil { + return m.BlockId + } + return nil +} + +func (m *GetLatestBlockResponse) GetBlock() *types1.Block { + if m != nil { + return m.Block + } + return nil +} + +// GetSyncingRequest is the request type for the Query/GetSyncing RPC method. +type GetSyncingRequest struct { +} + +func (m *GetSyncingRequest) Reset() { *m = GetSyncingRequest{} } +func (m *GetSyncingRequest) String() string { return proto.CompactTextString(m) } +func (*GetSyncingRequest) ProtoMessage() {} +func (*GetSyncingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{9} +} +func (m *GetSyncingRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSyncingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSyncingRequest.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 *GetSyncingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSyncingRequest.Merge(m, src) +} +func (m *GetSyncingRequest) XXX_Size() int { + return m.Size() +} +func (m *GetSyncingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSyncingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSyncingRequest proto.InternalMessageInfo + +// GetSyncingResponse is the response type for the Query/GetSyncing RPC method. +type GetSyncingResponse struct { + Syncing bool `protobuf:"varint,1,opt,name=syncing,proto3" json:"syncing,omitempty"` +} + +func (m *GetSyncingResponse) Reset() { *m = GetSyncingResponse{} } +func (m *GetSyncingResponse) String() string { return proto.CompactTextString(m) } +func (*GetSyncingResponse) ProtoMessage() {} +func (*GetSyncingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{10} +} +func (m *GetSyncingResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSyncingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSyncingResponse.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 *GetSyncingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSyncingResponse.Merge(m, src) +} +func (m *GetSyncingResponse) XXX_Size() int { + return m.Size() +} +func (m *GetSyncingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSyncingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSyncingResponse proto.InternalMessageInfo + +func (m *GetSyncingResponse) GetSyncing() bool { + if m != nil { + return m.Syncing + } + return false +} + +// GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. +type GetNodeInfoRequest struct { +} + +func (m *GetNodeInfoRequest) Reset() { *m = GetNodeInfoRequest{} } +func (m *GetNodeInfoRequest) String() string { return proto.CompactTextString(m) } +func (*GetNodeInfoRequest) ProtoMessage() {} +func (*GetNodeInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{11} +} +func (m *GetNodeInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNodeInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNodeInfoRequest.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 *GetNodeInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNodeInfoRequest.Merge(m, src) +} +func (m *GetNodeInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *GetNodeInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetNodeInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNodeInfoRequest proto.InternalMessageInfo + +// GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. +type GetNodeInfoResponse struct { + DefaultNodeInfo *p2p.DefaultNodeInfo `protobuf:"bytes,1,opt,name=default_node_info,json=defaultNodeInfo,proto3" json:"default_node_info,omitempty"` + ApplicationVersion *VersionInfo `protobuf:"bytes,2,opt,name=application_version,json=applicationVersion,proto3" json:"application_version,omitempty"` +} + +func (m *GetNodeInfoResponse) Reset() { *m = GetNodeInfoResponse{} } +func (m *GetNodeInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetNodeInfoResponse) ProtoMessage() {} +func (*GetNodeInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{12} +} +func (m *GetNodeInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNodeInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNodeInfoResponse.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 *GetNodeInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNodeInfoResponse.Merge(m, src) +} +func (m *GetNodeInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *GetNodeInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetNodeInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNodeInfoResponse proto.InternalMessageInfo + +func (m *GetNodeInfoResponse) GetDefaultNodeInfo() *p2p.DefaultNodeInfo { + if m != nil { + return m.DefaultNodeInfo + } + return nil +} + +func (m *GetNodeInfoResponse) GetApplicationVersion() *VersionInfo { + if m != nil { + return m.ApplicationVersion + } + return nil +} + +// VersionInfo is the type for the GetNodeInfoResponse message. +type VersionInfo struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + AppName string `protobuf:"bytes,2,opt,name=app_name,json=appName,proto3" json:"app_name,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + GitCommit string `protobuf:"bytes,4,opt,name=git_commit,json=gitCommit,proto3" json:"git_commit,omitempty"` + BuildTags string `protobuf:"bytes,5,opt,name=build_tags,json=buildTags,proto3" json:"build_tags,omitempty"` + GoVersion string `protobuf:"bytes,6,opt,name=go_version,json=goVersion,proto3" json:"go_version,omitempty"` + BuildDeps []*Module `protobuf:"bytes,7,rep,name=build_deps,json=buildDeps,proto3" json:"build_deps,omitempty"` +} + +func (m *VersionInfo) Reset() { *m = VersionInfo{} } +func (m *VersionInfo) String() string { return proto.CompactTextString(m) } +func (*VersionInfo) ProtoMessage() {} +func (*VersionInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{13} +} +func (m *VersionInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VersionInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VersionInfo.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 *VersionInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_VersionInfo.Merge(m, src) +} +func (m *VersionInfo) XXX_Size() int { + return m.Size() +} +func (m *VersionInfo) XXX_DiscardUnknown() { + xxx_messageInfo_VersionInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_VersionInfo proto.InternalMessageInfo + +func (m *VersionInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *VersionInfo) GetAppName() string { + if m != nil { + return m.AppName + } + return "" +} + +func (m *VersionInfo) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *VersionInfo) GetGitCommit() string { + if m != nil { + return m.GitCommit + } + return "" +} + +func (m *VersionInfo) GetBuildTags() string { + if m != nil { + return m.BuildTags + } + return "" +} + +func (m *VersionInfo) GetGoVersion() string { + if m != nil { + return m.GoVersion + } + return "" +} + +func (m *VersionInfo) GetBuildDeps() []*Module { + if m != nil { + return m.BuildDeps + } + return nil +} + +// Module is the type for VersionInfo +type Module struct { + // module path + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // module version + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + // checksum + Sum string `protobuf:"bytes,3,opt,name=sum,proto3" json:"sum,omitempty"` +} + +func (m *Module) Reset() { *m = Module{} } +func (m *Module) String() string { return proto.CompactTextString(m) } +func (*Module) ProtoMessage() {} +func (*Module) Descriptor() ([]byte, []int) { + return fileDescriptor_40c93fb3ef485c5d, []int{14} +} +func (m *Module) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Module) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Module.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 *Module) XXX_Merge(src proto.Message) { + xxx_messageInfo_Module.Merge(m, src) +} +func (m *Module) XXX_Size() int { + return m.Size() +} +func (m *Module) XXX_DiscardUnknown() { + xxx_messageInfo_Module.DiscardUnknown(m) +} + +var xxx_messageInfo_Module proto.InternalMessageInfo + +func (m *Module) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *Module) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *Module) GetSum() string { + if m != nil { + return m.Sum + } + return "" +} + +func init() { + proto.RegisterType((*GetValidatorSetByHeightRequest)(nil), "cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightRequest") + proto.RegisterType((*GetValidatorSetByHeightResponse)(nil), "cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightResponse") + proto.RegisterType((*GetLatestValidatorSetRequest)(nil), "cosmos.base.tendermint.v1beta1.GetLatestValidatorSetRequest") + proto.RegisterType((*GetLatestValidatorSetResponse)(nil), "cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse") + proto.RegisterType((*Validator)(nil), "cosmos.base.tendermint.v1beta1.Validator") + proto.RegisterType((*GetBlockByHeightRequest)(nil), "cosmos.base.tendermint.v1beta1.GetBlockByHeightRequest") + proto.RegisterType((*GetBlockByHeightResponse)(nil), "cosmos.base.tendermint.v1beta1.GetBlockByHeightResponse") + proto.RegisterType((*GetLatestBlockRequest)(nil), "cosmos.base.tendermint.v1beta1.GetLatestBlockRequest") + proto.RegisterType((*GetLatestBlockResponse)(nil), "cosmos.base.tendermint.v1beta1.GetLatestBlockResponse") + proto.RegisterType((*GetSyncingRequest)(nil), "cosmos.base.tendermint.v1beta1.GetSyncingRequest") + proto.RegisterType((*GetSyncingResponse)(nil), "cosmos.base.tendermint.v1beta1.GetSyncingResponse") + proto.RegisterType((*GetNodeInfoRequest)(nil), "cosmos.base.tendermint.v1beta1.GetNodeInfoRequest") + proto.RegisterType((*GetNodeInfoResponse)(nil), "cosmos.base.tendermint.v1beta1.GetNodeInfoResponse") + proto.RegisterType((*VersionInfo)(nil), "cosmos.base.tendermint.v1beta1.VersionInfo") + proto.RegisterType((*Module)(nil), "cosmos.base.tendermint.v1beta1.Module") +} + +func init() { + proto.RegisterFile("cosmos/base/tendermint/v1beta1/query.proto", fileDescriptor_40c93fb3ef485c5d) +} + +var fileDescriptor_40c93fb3ef485c5d = []byte{ + // 1060 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xdc, 0x44, + 0x14, 0xaf, 0xb3, 0x6d, 0x36, 0x79, 0x8b, 0x20, 0x99, 0x84, 0xc6, 0xb1, 0xd2, 0x6d, 0xf0, 0xa1, + 0x4d, 0x88, 0x62, 0x6b, 0xb7, 0x6d, 0xda, 0x43, 0x29, 0x22, 0x04, 0xd2, 0xa8, 0xa5, 0x8a, 0x1c, + 0xc4, 0x01, 0x21, 0x59, 0xde, 0xf5, 0xc4, 0x19, 0x65, 0xd7, 0x33, 0xf5, 0x8c, 0x83, 0x56, 0xa8, + 0x02, 0x71, 0xe2, 0x88, 0xc4, 0x57, 0xe8, 0x85, 0x2f, 0xc0, 0x11, 0x71, 0xe4, 0x58, 0x81, 0x84, + 0x2a, 0x4e, 0x28, 0xe1, 0x53, 0x70, 0x42, 0x9e, 0x19, 0xef, 0xda, 0x4d, 0xd2, 0xdd, 0xcd, 0x01, + 0x89, 0x93, 0x67, 0xde, 0xbf, 0xf9, 0xfd, 0xde, 0xbc, 0xf7, 0x3c, 0xf0, 0x6e, 0x9b, 0xf2, 0x2e, + 0xe5, 0x6e, 0x2b, 0xe0, 0xd8, 0x15, 0x38, 0x0e, 0x71, 0xd2, 0x25, 0xb1, 0x70, 0x8f, 0x1a, 0x2d, + 0x2c, 0x82, 0x86, 0xfb, 0x34, 0xc5, 0x49, 0xcf, 0x61, 0x09, 0x15, 0x14, 0xd5, 0x95, 0xad, 0x93, + 0xd9, 0x3a, 0x03, 0x5b, 0x47, 0xdb, 0x5a, 0xf3, 0x11, 0x8d, 0xa8, 0x34, 0x75, 0xb3, 0x95, 0xf2, + 0xb2, 0x16, 0x23, 0x4a, 0xa3, 0x0e, 0x76, 0xe5, 0xae, 0x95, 0xee, 0xbb, 0x41, 0xac, 0x03, 0x5a, + 0x4b, 0x5a, 0x15, 0x30, 0xe2, 0x06, 0x71, 0x4c, 0x45, 0x20, 0x08, 0x8d, 0xb9, 0xd6, 0x5a, 0x05, + 0x38, 0xac, 0xc9, 0x5c, 0xd1, 0x63, 0x38, 0xd7, 0x2d, 0x15, 0x74, 0x52, 0xee, 0xb6, 0x3a, 0xb4, + 0x7d, 0x78, 0xae, 0xb6, 0xe8, 0x5b, 0xa2, 0x2c, 0xf9, 0xf5, 0xd9, 0xb2, 0x20, 0x22, 0xb1, 0x04, + 0xa1, 0x6c, 0xed, 0x6f, 0x0c, 0xa8, 0x6f, 0x63, 0xf1, 0x59, 0xd0, 0x21, 0x61, 0x20, 0x68, 0xb2, + 0x87, 0xc5, 0x66, 0xef, 0x21, 0x26, 0xd1, 0x81, 0xf0, 0xf0, 0xd3, 0x14, 0x73, 0x81, 0xae, 0xc2, + 0xe4, 0x81, 0x14, 0x98, 0xc6, 0xb2, 0xb1, 0x52, 0xf1, 0xf4, 0x0e, 0x7d, 0x0c, 0x30, 0x08, 0x67, + 0x4e, 0x2c, 0x1b, 0x2b, 0xb5, 0xe6, 0x0d, 0xa7, 0x98, 0x42, 0x95, 0x5b, 0x7d, 0xb6, 0xb3, 0x1b, + 0x44, 0x58, 0xc7, 0xf4, 0x0a, 0x9e, 0xf6, 0x4b, 0x03, 0xae, 0x9f, 0x0b, 0x81, 0x33, 0x1a, 0x73, + 0x8c, 0xde, 0x81, 0x37, 0x24, 0x7f, 0xbf, 0x84, 0xa4, 0x26, 0x65, 0xca, 0x14, 0xed, 0x00, 0x1c, + 0xe5, 0x21, 0xb8, 0x39, 0xb1, 0x5c, 0x59, 0xa9, 0x35, 0x57, 0x9d, 0xd7, 0xdf, 0xa8, 0xd3, 0x3f, + 0xd4, 0x2b, 0x38, 0xa3, 0xed, 0x12, 0xb3, 0x8a, 0x64, 0x76, 0x73, 0x28, 0x33, 0x05, 0xb5, 0x44, + 0x6d, 0x1f, 0x96, 0xb6, 0xb1, 0x78, 0x1c, 0x08, 0xcc, 0x4b, 0xfc, 0xf2, 0xd4, 0x96, 0x53, 0x68, + 0x5c, 0x38, 0x85, 0x7f, 0x18, 0x70, 0xed, 0x9c, 0x83, 0xfe, 0xdf, 0x09, 0x7c, 0x6e, 0xc0, 0x74, + 0xff, 0x08, 0x64, 0x42, 0x35, 0x08, 0xc3, 0x04, 0x73, 0x2e, 0xf1, 0x4f, 0x7b, 0xf9, 0x16, 0xad, + 0x43, 0x95, 0xa5, 0x2d, 0xff, 0x10, 0xf7, 0x74, 0x21, 0xce, 0x3b, 0xaa, 0xf5, 0x9c, 0xbc, 0x2b, + 0x9d, 0x0f, 0xe2, 0x9e, 0x37, 0xc9, 0xd2, 0xd6, 0x23, 0xdc, 0xcb, 0xb2, 0x71, 0x44, 0x05, 0x89, + 0x23, 0x9f, 0xd1, 0x2f, 0x71, 0x22, 0x11, 0x56, 0xbc, 0x9a, 0x92, 0xed, 0x66, 0x22, 0xb4, 0x06, + 0xb3, 0x2c, 0xa1, 0x8c, 0x72, 0x9c, 0xf8, 0x2c, 0x21, 0x34, 0x21, 0xa2, 0x67, 0x5e, 0x96, 0x76, + 0x33, 0xb9, 0x62, 0x57, 0xcb, 0xed, 0x06, 0x2c, 0x6c, 0x63, 0xb1, 0x99, 0x25, 0x73, 0xc4, 0xee, + 0xb1, 0xbf, 0x06, 0xf3, 0xb4, 0x8b, 0xbe, 0xac, 0xdb, 0x30, 0xa5, 0x2e, 0x8b, 0x84, 0xba, 0x28, + 0x16, 0x8b, 0xb9, 0x57, 0xbd, 0x2e, 0x5d, 0x77, 0xb6, 0xbc, 0xaa, 0x34, 0xdd, 0x09, 0xd1, 0x3a, + 0x5c, 0x91, 0x4b, 0x9d, 0x81, 0x85, 0x73, 0x5c, 0x3c, 0x65, 0x65, 0x2f, 0xc0, 0xdb, 0xfd, 0x92, + 0x51, 0x0a, 0x85, 0xd8, 0x7e, 0x06, 0x57, 0x5f, 0x55, 0xfc, 0x97, 0xb8, 0xe6, 0x60, 0x76, 0x1b, + 0x8b, 0xbd, 0x5e, 0xdc, 0x26, 0x71, 0x94, 0x63, 0x72, 0x00, 0x15, 0x85, 0x1a, 0x8f, 0x09, 0x55, + 0xae, 0x44, 0x12, 0xce, 0x94, 0x97, 0x6f, 0xed, 0x79, 0x69, 0xff, 0x84, 0x86, 0x78, 0x27, 0xde, + 0xa7, 0x79, 0x94, 0x5f, 0x0c, 0x98, 0x2b, 0x89, 0x75, 0x9c, 0x47, 0x30, 0x1b, 0xe2, 0xfd, 0x20, + 0xed, 0x08, 0x3f, 0xa6, 0x21, 0xf6, 0x49, 0xbc, 0x4f, 0x35, 0xc1, 0xeb, 0x45, 0xb4, 0xac, 0xc9, + 0x9c, 0x2d, 0x65, 0xd8, 0x8f, 0xf1, 0x56, 0x58, 0x16, 0xa0, 0x2f, 0x60, 0x2e, 0x60, 0xac, 0x43, + 0xda, 0xb2, 0x82, 0xfd, 0x23, 0x9c, 0xf0, 0xc1, 0x7c, 0x5c, 0x1b, 0xda, 0x4f, 0xca, 0x5c, 0x86, + 0x46, 0x85, 0x38, 0x5a, 0x6e, 0xff, 0x63, 0x40, 0xad, 0x60, 0x83, 0x10, 0x5c, 0x8e, 0x83, 0x2e, + 0xd6, 0xfd, 0x20, 0xd7, 0x68, 0x11, 0xa6, 0x02, 0xc6, 0x7c, 0x29, 0x9f, 0xd0, 0x7d, 0xc2, 0xd8, + 0x93, 0x4c, 0x65, 0x42, 0x35, 0x07, 0x54, 0x51, 0x1a, 0xbd, 0x45, 0xd7, 0x00, 0x22, 0x22, 0xfc, + 0x36, 0xed, 0x76, 0x89, 0x90, 0x85, 0x3e, 0xed, 0x4d, 0x47, 0x44, 0x7c, 0x28, 0x05, 0x99, 0xba, + 0x95, 0x92, 0x4e, 0xe8, 0x8b, 0x20, 0xe2, 0xe6, 0x15, 0xa5, 0x96, 0x92, 0x4f, 0x83, 0x88, 0x4b, + 0x6f, 0xda, 0xe7, 0x3a, 0xa9, 0xbd, 0xa9, 0x46, 0x8a, 0x3e, 0xca, 0xbd, 0x43, 0xcc, 0xb8, 0x59, + 0x95, 0xa3, 0xe5, 0xc6, 0xb0, 0x54, 0x7c, 0x42, 0xc3, 0xb4, 0x83, 0xf5, 0x29, 0x5b, 0x98, 0x71, + 0xfb, 0x21, 0x4c, 0x2a, 0x61, 0x46, 0x9b, 0x05, 0xe2, 0x20, 0xa7, 0x9d, 0xad, 0x8b, 0xdc, 0x26, + 0xca, 0xdc, 0x66, 0xa0, 0xc2, 0xd3, 0xae, 0x66, 0x9c, 0x2d, 0x9b, 0xdf, 0x4d, 0x43, 0x75, 0x0f, + 0x27, 0x47, 0xa4, 0x8d, 0xd1, 0x8f, 0x06, 0xd4, 0x0a, 0x55, 0x81, 0x9a, 0xc3, 0x80, 0x9d, 0xae, + 0x2c, 0xeb, 0xd6, 0x58, 0x3e, 0xaa, 0xec, 0xec, 0xc6, 0xb7, 0xbf, 0xff, 0xfd, 0xc3, 0xc4, 0x1a, + 0x5a, 0x75, 0x87, 0xbc, 0x51, 0xfa, 0x45, 0x89, 0x9e, 0x1b, 0x00, 0x83, 0x46, 0x40, 0x8d, 0x11, + 0x8e, 0x2d, 0x77, 0x92, 0xd5, 0x1c, 0xc7, 0x45, 0x03, 0x75, 0x25, 0xd0, 0x55, 0x74, 0x73, 0x18, + 0x50, 0xdd, 0x7e, 0xe8, 0x27, 0x03, 0xde, 0x2c, 0xcf, 0x10, 0x74, 0x67, 0x84, 0x73, 0x4f, 0x0f, + 0x23, 0x6b, 0x63, 0x5c, 0x37, 0x0d, 0xf9, 0x8e, 0x84, 0xec, 0xa2, 0xf5, 0x61, 0x90, 0xe5, 0xd0, + 0xe1, 0x6e, 0x47, 0xc6, 0x40, 0x3f, 0x1b, 0x30, 0xf3, 0xea, 0x58, 0x46, 0x77, 0x47, 0xc0, 0x70, + 0xd6, 0xec, 0xb7, 0xee, 0x8d, 0xef, 0xa8, 0xe1, 0xdf, 0x95, 0xf0, 0x1b, 0xc8, 0x1d, 0x11, 0xfe, + 0x57, 0xea, 0xaf, 0xf2, 0x0c, 0xfd, 0x66, 0x14, 0xc6, 0x7a, 0xf1, 0x25, 0x80, 0xee, 0x8f, 0x9c, + 0xc9, 0x33, 0x5e, 0x2a, 0xd6, 0x7b, 0x17, 0xf4, 0xd6, 0x7c, 0xee, 0x4b, 0x3e, 0x1b, 0xe8, 0xf6, + 0x30, 0x3e, 0x83, 0x47, 0x04, 0x16, 0xfd, 0x5b, 0xf9, 0xd3, 0x90, 0xff, 0xd7, 0xb3, 0x5e, 0x88, + 0xe8, 0xc1, 0x08, 0xc0, 0x5e, 0xf3, 0xba, 0xb5, 0xde, 0xbf, 0xb0, 0xbf, 0xa6, 0xf6, 0x40, 0x52, + 0xbb, 0x87, 0x36, 0xc6, 0xa3, 0x96, 0xdf, 0xd8, 0xe6, 0xe3, 0x5f, 0x8f, 0xeb, 0xc6, 0x8b, 0xe3, + 0xba, 0xf1, 0xd7, 0x71, 0xdd, 0xf8, 0xfe, 0xa4, 0x7e, 0xe9, 0xc5, 0x49, 0xfd, 0xd2, 0xcb, 0x93, + 0xfa, 0xa5, 0xcf, 0x9b, 0x11, 0x11, 0x07, 0x69, 0xcb, 0x69, 0xd3, 0x6e, 0x1e, 0x5b, 0x7d, 0xd6, + 0x79, 0x78, 0xe8, 0xb6, 0x3b, 0x04, 0xc7, 0xc2, 0x8d, 0x12, 0xd6, 0x76, 0x45, 0x97, 0xab, 0x61, + 0xd6, 0x9a, 0x94, 0xef, 0x9d, 0x5b, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x08, 0x32, 0x9c, 0x36, + 0xf7, 0x0c, 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 + +// ServiceClient is the client API for Service service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ServiceClient interface { + // GetNodeInfo queries the current node info. + GetNodeInfo(ctx context.Context, in *GetNodeInfoRequest, opts ...grpc.CallOption) (*GetNodeInfoResponse, error) + // GetSyncing queries node syncing. + GetSyncing(ctx context.Context, in *GetSyncingRequest, opts ...grpc.CallOption) (*GetSyncingResponse, error) + // GetLatestBlock returns the latest block. + GetLatestBlock(ctx context.Context, in *GetLatestBlockRequest, opts ...grpc.CallOption) (*GetLatestBlockResponse, error) + // GetBlockByHeight queries block for given height. + GetBlockByHeight(ctx context.Context, in *GetBlockByHeightRequest, opts ...grpc.CallOption) (*GetBlockByHeightResponse, error) + // GetLatestValidatorSet queries latest validator-set. + GetLatestValidatorSet(ctx context.Context, in *GetLatestValidatorSetRequest, opts ...grpc.CallOption) (*GetLatestValidatorSetResponse, error) + // GetValidatorSetByHeight queries validator-set at a given height. + GetValidatorSetByHeight(ctx context.Context, in *GetValidatorSetByHeightRequest, opts ...grpc.CallOption) (*GetValidatorSetByHeightResponse, error) +} + +type serviceClient struct { + cc grpc1.ClientConn +} + +func NewServiceClient(cc grpc1.ClientConn) ServiceClient { + return &serviceClient{cc} +} + +func (c *serviceClient) GetNodeInfo(ctx context.Context, in *GetNodeInfoRequest, opts ...grpc.CallOption) (*GetNodeInfoResponse, error) { + out := new(GetNodeInfoResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetSyncing(ctx context.Context, in *GetSyncingRequest, opts ...grpc.CallOption) (*GetSyncingResponse, error) { + out := new(GetSyncingResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetSyncing", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetLatestBlock(ctx context.Context, in *GetLatestBlockRequest, opts ...grpc.CallOption) (*GetLatestBlockResponse, error) { + out := new(GetLatestBlockResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetBlockByHeight(ctx context.Context, in *GetBlockByHeightRequest, opts ...grpc.CallOption) (*GetBlockByHeightResponse, error) { + out := new(GetBlockByHeightResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetBlockByHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetLatestValidatorSet(ctx context.Context, in *GetLatestValidatorSetRequest, opts ...grpc.CallOption) (*GetLatestValidatorSetResponse, error) { + out := new(GetLatestValidatorSetResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetLatestValidatorSet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetValidatorSetByHeight(ctx context.Context, in *GetValidatorSetByHeightRequest, opts ...grpc.CallOption) (*GetValidatorSetByHeightResponse, error) { + out := new(GetValidatorSetByHeightResponse) + err := c.cc.Invoke(ctx, "/cosmos.base.tendermint.v1beta1.Service/GetValidatorSetByHeight", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ServiceServer is the server API for Service service. +type ServiceServer interface { + // GetNodeInfo queries the current node info. + GetNodeInfo(context.Context, *GetNodeInfoRequest) (*GetNodeInfoResponse, error) + // GetSyncing queries node syncing. + GetSyncing(context.Context, *GetSyncingRequest) (*GetSyncingResponse, error) + // GetLatestBlock returns the latest block. + GetLatestBlock(context.Context, *GetLatestBlockRequest) (*GetLatestBlockResponse, error) + // GetBlockByHeight queries block for given height. + GetBlockByHeight(context.Context, *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) + // GetLatestValidatorSet queries latest validator-set. + GetLatestValidatorSet(context.Context, *GetLatestValidatorSetRequest) (*GetLatestValidatorSetResponse, error) + // GetValidatorSetByHeight queries validator-set at a given height. + GetValidatorSetByHeight(context.Context, *GetValidatorSetByHeightRequest) (*GetValidatorSetByHeightResponse, error) +} + +// UnimplementedServiceServer can be embedded to have forward compatible implementations. +type UnimplementedServiceServer struct { +} + +func (*UnimplementedServiceServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNodeInfo not implemented") +} +func (*UnimplementedServiceServer) GetSyncing(ctx context.Context, req *GetSyncingRequest) (*GetSyncingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSyncing not implemented") +} +func (*UnimplementedServiceServer) GetLatestBlock(ctx context.Context, req *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLatestBlock not implemented") +} +func (*UnimplementedServiceServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBlockByHeight not implemented") +} +func (*UnimplementedServiceServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestValidatorSetRequest) (*GetLatestValidatorSetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLatestValidatorSet not implemented") +} +func (*UnimplementedServiceServer) GetValidatorSetByHeight(ctx context.Context, req *GetValidatorSetByHeightRequest) (*GetValidatorSetByHeightResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetValidatorSetByHeight not implemented") +} + +func RegisterServiceServer(s grpc1.Server, srv ServiceServer) { + s.RegisterService(&_Service_serviceDesc, srv) +} + +func _Service_GetNodeInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNodeInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetNodeInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetNodeInfo(ctx, req.(*GetNodeInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetSyncing_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSyncingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetSyncing(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetSyncing", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetSyncing(ctx, req.(*GetSyncingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetLatestBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLatestBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetLatestBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetLatestBlock(ctx, req.(*GetLatestBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetBlockByHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBlockByHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetBlockByHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetBlockByHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetBlockByHeight(ctx, req.(*GetBlockByHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetLatestValidatorSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLatestValidatorSetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetLatestValidatorSet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetLatestValidatorSet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetLatestValidatorSet(ctx, req.(*GetLatestValidatorSetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetValidatorSetByHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetValidatorSetByHeightRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetValidatorSetByHeight(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.base.tendermint.v1beta1.Service/GetValidatorSetByHeight", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetValidatorSetByHeight(ctx, req.(*GetValidatorSetByHeightRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Service_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.base.tendermint.v1beta1.Service", + HandlerType: (*ServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetNodeInfo", + Handler: _Service_GetNodeInfo_Handler, + }, + { + MethodName: "GetSyncing", + Handler: _Service_GetSyncing_Handler, + }, + { + MethodName: "GetLatestBlock", + Handler: _Service_GetLatestBlock_Handler, + }, + { + MethodName: "GetBlockByHeight", + Handler: _Service_GetBlockByHeight_Handler, + }, + { + MethodName: "GetLatestValidatorSet", + Handler: _Service_GetLatestValidatorSet_Handler, + }, + { + MethodName: "GetValidatorSetByHeight", + Handler: _Service_GetValidatorSetByHeight_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/base/tendermint/v1beta1/query.proto", +} + +func (m *GetValidatorSetByHeightRequest) 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 *GetValidatorSetByHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetValidatorSetByHeightRequest) 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 m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GetValidatorSetByHeightResponse) 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 *GetValidatorSetByHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetValidatorSetByHeightResponse) 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.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GetLatestValidatorSetRequest) 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 *GetLatestValidatorSetRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLatestValidatorSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetLatestValidatorSetResponse) 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 *GetLatestValidatorSetResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLatestValidatorSetResponse) 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.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Validator) 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 *Validator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProposerPriority != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposerPriority)) + i-- + dAtA[i] = 0x20 + } + if m.VotingPower != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x18 + } + if m.PubKey != nil { + { + size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetBlockByHeightRequest) 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 *GetBlockByHeightRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetBlockByHeightRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GetBlockByHeightResponse) 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 *GetBlockByHeightResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetBlockByHeightResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Block != nil { + { + size, err := m.Block.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.BlockId != nil { + { + size, err := m.BlockId.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 *GetLatestBlockRequest) 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 *GetLatestBlockRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLatestBlockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetLatestBlockResponse) 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 *GetLatestBlockResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLatestBlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Block != nil { + { + size, err := m.Block.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.BlockId != nil { + { + size, err := m.BlockId.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 *GetSyncingRequest) 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 *GetSyncingRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetSyncingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetSyncingResponse) 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 *GetSyncingResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetSyncingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Syncing { + i-- + if m.Syncing { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GetNodeInfoRequest) 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 *GetNodeInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNodeInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetNodeInfoResponse) 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 *GetNodeInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNodeInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ApplicationVersion != nil { + { + size, err := m.ApplicationVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.DefaultNodeInfo != nil { + { + size, err := m.DefaultNodeInfo.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 *VersionInfo) 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 *VersionInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VersionInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BuildDeps) > 0 { + for iNdEx := len(m.BuildDeps) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.BuildDeps[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.GoVersion) > 0 { + i -= len(m.GoVersion) + copy(dAtA[i:], m.GoVersion) + i = encodeVarintQuery(dAtA, i, uint64(len(m.GoVersion))) + i-- + dAtA[i] = 0x32 + } + if len(m.BuildTags) > 0 { + i -= len(m.BuildTags) + copy(dAtA[i:], m.BuildTags) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BuildTags))) + i-- + dAtA[i] = 0x2a + } + if len(m.GitCommit) > 0 { + i -= len(m.GitCommit) + copy(dAtA[i:], m.GitCommit) + i = encodeVarintQuery(dAtA, i, uint64(len(m.GitCommit))) + i-- + dAtA[i] = 0x22 + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x1a + } + if len(m.AppName) > 0 { + i -= len(m.AppName) + copy(dAtA[i:], m.AppName) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AppName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Module) 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 *Module) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Module) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Sum) > 0 { + i -= len(m.Sum) + copy(dAtA[i:], m.Sum) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Sum))) + i-- + dAtA[i] = 0x1a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Path))) + 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 *GetValidatorSetByHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *GetValidatorSetByHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + if len(m.Validators) > 0 { + for _, e := range m.Validators { + 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 *GetLatestValidatorSetRequest) 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 *GetLatestValidatorSetResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + if len(m.Validators) > 0 { + for _, e := range m.Validators { + 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 *Validator) 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.PubKey != nil { + l = m.PubKey.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.VotingPower != 0 { + n += 1 + sovQuery(uint64(m.VotingPower)) + } + if m.ProposerPriority != 0 { + n += 1 + sovQuery(uint64(m.ProposerPriority)) + } + return n +} + +func (m *GetBlockByHeightRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *GetBlockByHeightResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockId != nil { + l = m.BlockId.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.Block != nil { + l = m.Block.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *GetLatestBlockRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetLatestBlockResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockId != nil { + l = m.BlockId.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.Block != nil { + l = m.Block.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *GetSyncingRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetSyncingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Syncing { + n += 2 + } + return n +} + +func (m *GetNodeInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetNodeInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DefaultNodeInfo != nil { + l = m.DefaultNodeInfo.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.ApplicationVersion != nil { + l = m.ApplicationVersion.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *VersionInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.AppName) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.GitCommit) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.BuildTags) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.GoVersion) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.BuildDeps) > 0 { + for _, e := range m.BuildDeps { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *Module) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Sum) + if l > 0 { + 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 *GetValidatorSetByHeightRequest) 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: GetValidatorSetByHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetValidatorSetByHeightRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *GetValidatorSetByHeightResponse) 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: GetValidatorSetByHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetValidatorSetByHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", 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.Validators = append(m.Validators, &Validator{}) + if err := m.Validators[len(m.Validators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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.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 *GetLatestValidatorSetRequest) 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: GetLatestValidatorSetRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLatestValidatorSetRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetLatestValidatorSetResponse) 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: GetLatestValidatorSetResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLatestValidatorSetResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", 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.Validators = append(m.Validators, &Validator{}) + if err := m.Validators[len(m.Validators)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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.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 *Validator) 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: Validator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Validator: 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 PubKey", 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.PubKey == nil { + m.PubKey = &types.Any{} + } + if err := m.PubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposerPriority", wireType) + } + m.ProposerPriority = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposerPriority |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *GetBlockByHeightRequest) 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: GetBlockByHeightRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetBlockByHeightRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *GetBlockByHeightResponse) 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: GetBlockByHeightResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetBlockByHeightResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", 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.BlockId == nil { + m.BlockId = &types1.BlockID{} + } + if err := m.BlockId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Block", 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.Block == nil { + m.Block = &types1.Block{} + } + if err := m.Block.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 *GetLatestBlockRequest) 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: GetLatestBlockRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLatestBlockRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetLatestBlockResponse) 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: GetLatestBlockResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLatestBlockResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockId", 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.BlockId == nil { + m.BlockId = &types1.BlockID{} + } + if err := m.BlockId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Block", 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.Block == nil { + m.Block = &types1.Block{} + } + if err := m.Block.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 *GetSyncingRequest) 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: GetSyncingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSyncingRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSyncingResponse) 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: GetSyncingResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSyncingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Syncing", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Syncing = bool(v != 0) + 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 *GetNodeInfoRequest) 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: GetNodeInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNodeInfoRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetNodeInfoResponse) 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: GetNodeInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNodeInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultNodeInfo", 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.DefaultNodeInfo == nil { + m.DefaultNodeInfo = &p2p.DefaultNodeInfo{} + } + if err := m.DefaultNodeInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ApplicationVersion", 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.ApplicationVersion == nil { + m.ApplicationVersion = &VersionInfo{} + } + if err := m.ApplicationVersion.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 *VersionInfo) 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: VersionInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VersionInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppName", 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.AppName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GitCommit", 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.GitCommit = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuildTags", 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.BuildTags = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GoVersion", 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.GoVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BuildDeps", 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.BuildDeps = append(m.BuildDeps, &Module{}) + if err := m.BuildDeps[len(m.BuildDeps)-1].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 *Module) 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: Module: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Module: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", 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.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sum", 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.Sum = 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 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/client/grpc/tmservice/query.pb.gw.go b/client/grpc/tmservice/query.pb.gw.go new file mode 100644 index 000000000000..90135d79b937 --- /dev/null +++ b/client/grpc/tmservice/query.pb.gw.go @@ -0,0 +1,566 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/base/tendermint/v1beta1/query.proto + +/* +Package tmservice is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package tmservice + +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_Service_GetNodeInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetNodeInfoRequest + var metadata runtime.ServerMetadata + + msg, err := client.GetNodeInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetNodeInfo_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetNodeInfoRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetNodeInfo(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Service_GetSyncing_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetSyncingRequest + var metadata runtime.ServerMetadata + + msg, err := client.GetSyncing(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetSyncing_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetSyncingRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetSyncing(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Service_GetLatestBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLatestBlockRequest + var metadata runtime.ServerMetadata + + msg, err := client.GetLatestBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetLatestBlock_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLatestBlockRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetLatestBlock(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Service_GetBlockByHeight_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockByHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.GetBlockByHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetBlockByHeight_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockByHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.GetBlockByHeight(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Service_GetLatestValidatorSet_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Service_GetLatestValidatorSet_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLatestValidatorSetRequest + 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_Service_GetLatestValidatorSet_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetLatestValidatorSet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetLatestValidatorSet_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetLatestValidatorSetRequest + 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_Service_GetLatestValidatorSet_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetLatestValidatorSet(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Service_GetValidatorSetByHeight_0 = &utilities.DoubleArray{Encoding: map[string]int{"height": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Service_GetValidatorSetByHeight_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetValidatorSetByHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetValidatorSetByHeight_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetValidatorSetByHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetValidatorSetByHeight_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetValidatorSetByHeightRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetValidatorSetByHeight_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetValidatorSetByHeight(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterServiceHandlerServer registers the http handlers for service Service to "mux". +// UnaryRPC :call ServiceServer 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 RegisterServiceHandlerFromEndpoint instead. +func RegisterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ServiceServer) error { + + mux.Handle("GET", pattern_Service_GetNodeInfo_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_Service_GetNodeInfo_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetNodeInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetSyncing_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_Service_GetSyncing_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetSyncing_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetLatestBlock_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_Service_GetLatestBlock_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetLatestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetBlockByHeight_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_Service_GetBlockByHeight_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetBlockByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetLatestValidatorSet_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_Service_GetLatestValidatorSet_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetLatestValidatorSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetValidatorSetByHeight_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_Service_GetValidatorSetByHeight_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetValidatorSetByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterServiceHandlerFromEndpoint is same as RegisterServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterServiceHandlerFromEndpoint(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 RegisterServiceHandler(ctx, mux, conn) +} + +// RegisterServiceHandler registers the http handlers for service Service to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterServiceHandlerClient(ctx, mux, NewServiceClient(conn)) +} + +// RegisterServiceHandlerClient registers the http handlers for service Service +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ServiceClient" to call the correct interceptors. +func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ServiceClient) error { + + mux.Handle("GET", pattern_Service_GetNodeInfo_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_Service_GetNodeInfo_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetNodeInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetSyncing_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_Service_GetSyncing_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetSyncing_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetLatestBlock_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_Service_GetLatestBlock_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetLatestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetBlockByHeight_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_Service_GetBlockByHeight_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetBlockByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetLatestValidatorSet_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_Service_GetLatestValidatorSet_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetLatestValidatorSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetValidatorSetByHeight_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_Service_GetValidatorSetByHeight_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetValidatorSetByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Service_GetNodeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "node_info"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetSyncing_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "syncing"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetLatestBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "latest"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetBlockByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "height"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetLatestValidatorSet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "latest"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetValidatorSetByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validatorsets", "height"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Service_GetNodeInfo_0 = runtime.ForwardResponseMessage + + forward_Service_GetSyncing_0 = runtime.ForwardResponseMessage + + forward_Service_GetLatestBlock_0 = runtime.ForwardResponseMessage + + forward_Service_GetBlockByHeight_0 = runtime.ForwardResponseMessage + + forward_Service_GetLatestValidatorSet_0 = runtime.ForwardResponseMessage + + forward_Service_GetValidatorSetByHeight_0 = runtime.ForwardResponseMessage +) diff --git a/client/grpc/tmservice/service.go b/client/grpc/tmservice/service.go new file mode 100644 index 000000000000..58d44af579cc --- /dev/null +++ b/client/grpc/tmservice/service.go @@ -0,0 +1,227 @@ +package tmservice + +import ( + "context" + + gogogrpc "github.com/gogo/protobuf/grpc" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rpc" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + qtypes "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/version" +) + +// This is the struct that we will implement all the handlers on. +type queryServer struct { + clientCtx client.Context + interfaceRegistry codectypes.InterfaceRegistry +} + +var _ ServiceServer = queryServer{} +var _ codectypes.UnpackInterfacesMessage = &GetLatestValidatorSetResponse{} + +// NewQueryServer creates a new tendermint query server. +func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { + return queryServer{ + clientCtx: clientCtx, + interfaceRegistry: interfaceRegistry, + } +} + +// GetSyncing implements ServiceServer.GetSyncing +func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) { + status, err := getNodeStatus(s.clientCtx) + if err != nil { + return nil, err + } + return &GetSyncingResponse{ + Syncing: status.SyncInfo.CatchingUp, + }, nil +} + +// GetLatestBlock implements ServiceServer.GetLatestBlock +func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { + status, err := getBlock(s.clientCtx, nil) + if err != nil { + return nil, err + } + + protoBlockID := status.BlockID.ToProto() + protoBlock, err := status.Block.ToProto() + if err != nil { + return nil, err + } + + return &GetLatestBlockResponse{ + BlockId: &protoBlockID, + Block: protoBlock, + }, nil +} + +// GetBlockByHeight implements ServiceServer.GetBlockByHeight +func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) { + chainHeight, err := rpc.GetChainHeight(s.clientCtx) + if err != nil { + return nil, err + } + + if req.Height > chainHeight { + return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length") + } + + res, err := getBlock(s.clientCtx, &req.Height) + if err != nil { + return nil, err + } + protoBlockID := res.BlockID.ToProto() + protoBlock, err := res.Block.ToProto() + if err != nil { + return nil, err + } + return &GetBlockByHeightResponse{ + BlockId: &protoBlockID, + Block: protoBlock, + }, nil +} + +// GetLatestValidatorSet implements ServiceServer.GetLatestValidatorSet +func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestValidatorSetRequest) (*GetLatestValidatorSetResponse, error) { + page, limit, err := qtypes.ParsePagination(req.Pagination) + if err != nil { + return nil, err + } + + validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit) + if err != nil { + return nil, err + } + + outputValidatorsRes := &GetLatestValidatorSetResponse{ + BlockHeight: validatorsRes.BlockHeight, + Validators: make([]*Validator, len(validatorsRes.Validators)), + } + + for i, validator := range validatorsRes.Validators { + anyPub, err := codectypes.NewAnyWithValue(validator.PubKey) + if err != nil { + return nil, err + } + outputValidatorsRes.Validators[i] = &Validator{ + Address: validator.Address.String(), + ProposerPriority: validator.ProposerPriority, + PubKey: anyPub, + VotingPower: validator.VotingPower, + } + } + return outputValidatorsRes, nil +} + +func (m *GetLatestValidatorSetResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var pubKey cryptotypes.PubKey + for _, val := range m.Validators { + err := unpacker.UnpackAny(val.PubKey, &pubKey) + if err != nil { + return err + } + } + return nil +} + +// GetValidatorSetByHeight implements ServiceServer.GetValidatorSetByHeight +func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValidatorSetByHeightRequest) (*GetValidatorSetByHeightResponse, error) { + page, limit, err := qtypes.ParsePagination(req.Pagination) + if err != nil { + return nil, err + } + + chainHeight, err := rpc.GetChainHeight(s.clientCtx) + if err != nil { + return nil, status.Error(codes.Internal, "failed to parse chain height") + } + if req.Height > chainHeight { + 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) + + if err != nil { + return nil, err + } + + outputValidatorsRes := &GetValidatorSetByHeightResponse{ + BlockHeight: validatorsRes.BlockHeight, + Validators: make([]*Validator, len(validatorsRes.Validators)), + } + + for i, validator := range validatorsRes.Validators { + anyPub, err := codectypes.NewAnyWithValue(validator.PubKey) + if err != nil { + return nil, err + } + outputValidatorsRes.Validators[i] = &Validator{ + Address: validator.Address.String(), + ProposerPriority: validator.ProposerPriority, + PubKey: anyPub, + VotingPower: validator.VotingPower, + } + } + return outputValidatorsRes, nil +} + +// GetNodeInfo implements ServiceServer.GetNodeInfo +func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) { + status, err := getNodeStatus(s.clientCtx) + if err != nil { + return nil, err + } + + protoNodeInfo := status.NodeInfo.ToProto() + nodeInfo := version.NewInfo() + + deps := make([]*Module, len(nodeInfo.BuildDeps)) + + for i, dep := range nodeInfo.BuildDeps { + deps[i] = &Module{ + Path: dep.Path, + Sum: dep.Sum, + Version: dep.Version, + } + } + + resp := GetNodeInfoResponse{ + DefaultNodeInfo: protoNodeInfo, + ApplicationVersion: &VersionInfo{ + AppName: nodeInfo.AppName, + Name: nodeInfo.Name, + GitCommit: nodeInfo.GitCommit, + GoVersion: nodeInfo.GoVersion, + Version: nodeInfo.Version, + BuildTags: nodeInfo.BuildTags, + BuildDeps: deps, + }, + } + return &resp, nil +} + +// RegisterTendermintService registers the tendermint queries on the gRPC router. +func RegisterTendermintService( + qrt gogogrpc.Server, + clientCtx client.Context, + interfaceRegistry codectypes.InterfaceRegistry, +) { + RegisterServiceServer( + qrt, + NewQueryServer(clientCtx, interfaceRegistry), + ) +} + +// RegisterGRPCGatewayRoutes mounts the tendermint service's GRPC-gateway routes on the +// given Mux. +func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) { + RegisterServiceHandlerClient(context.Background(), mux, NewServiceClient(clientConn)) +} diff --git a/client/grpc/tmservice/service_test.go b/client/grpc/tmservice/service_test.go new file mode 100644 index 000000000000..dd8f0dd50ee1 --- /dev/null +++ b/client/grpc/tmservice/service_test.go @@ -0,0 +1,164 @@ +package tmservice_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/network" + qtypes "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/version" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + + queryClient tmservice.ServiceClient +} + +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) + + s.Require().NotNil(s.network) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + + s.queryClient = tmservice.NewServiceClient(s.network.Validators[0].ClientCtx) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s IntegrationTestSuite) TestQueryNodeInfo() { + val := s.network.Validators[0] + + res, err := s.queryClient.GetNodeInfo(context.Background(), &tmservice.GetNodeInfoRequest{}) + s.Require().NoError(err) + s.Require().Equal(res.ApplicationVersion.AppName, version.NewInfo().AppName) + + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/node_info", val.APIAddress)) + s.Require().NoError(err) + var getInfoRes tmservice.GetNodeInfoResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &getInfoRes)) + s.Require().Equal(getInfoRes.ApplicationVersion.AppName, version.NewInfo().AppName) +} + +func (s IntegrationTestSuite) TestQuerySyncing() { + val := s.network.Validators[0] + + _, err := s.queryClient.GetSyncing(context.Background(), &tmservice.GetSyncingRequest{}) + s.Require().NoError(err) + + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/syncing", val.APIAddress)) + s.Require().NoError(err) + var syncingRes tmservice.GetSyncingResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &syncingRes)) +} + +func (s IntegrationTestSuite) TestQueryLatestBlock() { + val := s.network.Validators[0] + + _, err := s.queryClient.GetLatestBlock(context.Background(), &tmservice.GetLatestBlockRequest{}) + s.Require().NoError(err) + + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/latest", val.APIAddress)) + s.Require().NoError(err) + var blockInfoRes tmservice.GetLatestBlockResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &blockInfoRes)) +} + +func (s IntegrationTestSuite) TestQueryBlockByHeight() { + val := s.network.Validators[0] + _, err := s.queryClient.GetBlockByHeight(context.Background(), &tmservice.GetBlockByHeightRequest{Height: 1}) + s.Require().NoError(err) + + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/%d", val.APIAddress, 1)) + s.Require().NoError(err) + var blockInfoRes tmservice.GetBlockByHeightResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &blockInfoRes)) +} + +func (s IntegrationTestSuite) TestQueryLatestValidatorSet() { + val := s.network.Validators[0] + + // nil pagination + res, err := s.queryClient.GetLatestValidatorSet(context.Background(), &tmservice.GetLatestValidatorSetRequest{ + Pagination: nil, + }) + s.Require().NoError(err) + s.Require().Equal(1, len(res.Validators)) + content, ok := res.Validators[0].PubKey.GetCachedValue().(cryptotypes.PubKey) + s.Require().Equal(true, ok) + s.Require().Equal(content, val.PubKey) + + //with pagination + _, err = s.queryClient.GetLatestValidatorSet(context.Background(), &tmservice.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{ + Offset: 0, + Limit: 10, + }}) + s.Require().NoError(err) + + // rest request without pagination + _, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest", val.APIAddress)) + s.Require().NoError(err) + + // rest request with pagination + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/latest?pagination.offset=%d&pagination.limit=%d", val.APIAddress, 0, 1)) + s.Require().NoError(err) + var validatorSetRes tmservice.GetLatestValidatorSetResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &validatorSetRes)) + s.Require().Equal(1, len(validatorSetRes.Validators)) + anyPub, err := codectypes.NewAnyWithValue(val.PubKey) + s.Require().NoError(err) + 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) + + _, err = s.queryClient.GetValidatorSetByHeight(context.Background(), &tmservice.GetValidatorSetByHeightRequest{ + Height: 1, + Pagination: &qtypes.PageRequest{ + Offset: 0, + Limit: 10, + }}) + s.Require().NoError(err) + + // no pagination rest + _, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d", val.APIAddress, 1)) + s.Require().NoError(err) + + // rest query with pagination + restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validatorsets/%d?pagination.offset=%d&pagination.limit=%d", val.APIAddress, 1, 0, 1)) + var validatorSetRes tmservice.GetValidatorSetByHeightResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &validatorSetRes)) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/client/grpc/tmservice/status.go b/client/grpc/tmservice/status.go new file mode 100644 index 000000000000..f1a8da8e8362 --- /dev/null +++ b/client/grpc/tmservice/status.go @@ -0,0 +1,17 @@ +package tmservice + +import ( + "context" + + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/client" +) + +func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) { + node, err := clientCtx.GetNode() + if err != nil { + return &ctypes.ResultStatus{}, err + } + return node.Status(context.Background()) +} diff --git a/client/grpc_query.go b/client/grpc_query.go new file mode 100644 index 000000000000..011523944c54 --- /dev/null +++ b/client/grpc_query.go @@ -0,0 +1,132 @@ +package client + +import ( + gocontext "context" + "fmt" + "reflect" + "strconv" + + gogogrpc "github.com/gogo/protobuf/grpc" + abci "github.com/tendermint/tendermint/abci/types" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding" + "google.golang.org/grpc/encoding/proto" + "google.golang.org/grpc/metadata" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +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, 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 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 reqProto, ok := req.(*tx.BroadcastTxRequest); ok { + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req) + } + resProto, ok := reply.(*tx.BroadcastTxResponse) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req) + } + + broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto) + if err != nil { + return err + } + *resProto = *broadcastRes + + return err + } + + // Case 2. Querying state. + 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 + if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 { + height, err := strconv.ParseInt(heights[0], 10, 64) + if err != nil { + return abci.ResponseQuery{}, nil, err + } + if height < 0 { + return abci.ResponseQuery{}, nil, sdkerrors.Wrapf( + sdkerrors.ErrInvalidRequest, + "client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) + } + + ctx = ctx.WithHeight(height) + } + + abciReq := abci.RequestQuery{ + Path: method, + Data: reqBz, + } + + abciRes, err := ctx.QueryABCI(abciReq) + if err != nil { + return abci.ResponseQuery{}, nil, err + } + + // Create header metadata. For now the headers contain: + // - block height + // We then parse all the call options, if the call option is a + // HeaderCallOption, then we manually set the value of that header to the + // metadata. + md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10)) + + return abciRes, md, nil +} diff --git a/client/grpc_query_test.go b/client/grpc_query_test.go new file mode 100644 index 000000000000..c4d2f024a07b --- /dev/null +++ b/client/grpc_query_test.go @@ -0,0 +1,82 @@ +// +build norace + +package client_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.network = network.New(s.T(), network.DefaultConfig()) + s.Require().NotNil(s.network) + + _, err := s.network.WaitForHeight(2) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGRPCQuery() { + val0 := s.network.Validators[0] + + // gRPC query to test service should work + testClient := testdata.NewQueryClient(val0.ClientCtx) + testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + s.Require().NoError(err) + s.Require().Equal("hello", testRes.Message) + + // gRPC query to bank service should work + denom := fmt.Sprintf("%stoken", val0.Moniker) + bankClient := banktypes.NewQueryClient(val0.ClientCtx) + var header metadata.MD + bankRes, err := bankClient.Balance( + context.Background(), + &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, + grpc.Header(&header), // Also fetch grpc header + ) + s.Require().NoError(err) + s.Require().Equal( + sdk.NewCoin(denom, s.network.Config.AccountTokens), + *bankRes.GetBalance(), + ) + blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) + s.Require().NotEmpty(blockHeight[0]) // Should contain the block height + + // Request metadata should work + val0.ClientCtx = val0.ClientCtx.WithHeight(1) // We set clientCtx to height 1 + bankClient = banktypes.NewQueryClient(val0.ClientCtx) + bankRes, err = bankClient.Balance( + context.Background(), + &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, + grpc.Header(&header), + ) + blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) + s.Require().Equal([]string{"1"}, blockHeight) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/client/input/input.go b/client/input/input.go index 95f32281edd5..57b04b40186a 100644 --- a/client/input/input.go +++ b/client/input/input.go @@ -2,8 +2,8 @@ package input import ( "bufio" - "errors" "fmt" + "io" "os" "strings" @@ -36,40 +36,15 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) { return pass, nil } -// GetCheckPassword will prompt for a password twice to verify they -// match (for creating a new password). -// It enforces the password length. Only parses password once if -// input is piped in. -func GetCheckPassword(prompt, prompt2 string, buf *bufio.Reader) (string, error) { - // simple read on no-tty - if !inputIsTty() { - return GetPassword(prompt, buf) - } - - // TODO: own function??? - pass, err := GetPassword(prompt, buf) - if err != nil { - return "", err - } - pass2, err := GetPassword(prompt2, buf) - if err != nil { - return "", err - } - if pass != pass2 { - return "", errors.New("passphrases don't match") - } - return pass, nil -} - // GetConfirmation will request user give the confirmation from stdin. // "y", "Y", "yes", "YES", and "Yes" all count as confirmations. // If the input is not recognized, it returns false and a nil error. -func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) { +func GetConfirmation(prompt string, r *bufio.Reader, w io.Writer) (bool, error) { if inputIsTty() { - fmt.Print(fmt.Sprintf("%s [y/N]: ", prompt)) + fmt.Fprintf(w, "%s [y/N]: ", prompt) } - response, err := readLineFromBuf(buf) + response, err := readLineFromBuf(r) if err != nil { return false, err } @@ -90,13 +65,14 @@ func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) { // GetString simply returns the trimmed string output of a given reader. func GetString(prompt string, buf *bufio.Reader) (string, error) { if inputIsTty() && prompt != "" { - PrintPrefixed(prompt) + fmt.Fprintf(os.Stderr, "> %s\n", prompt) } out, err := readLineFromBuf(buf) if err != nil { return "", err } + return strings.TrimSpace(out), nil } @@ -115,11 +91,6 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) { if err != nil { return "", err } - return strings.TrimSpace(pass), nil -} -// PrintPrefixed prints a string with > prefixed for use in prompts. -func PrintPrefixed(msg string) { - msg = fmt.Sprintf("> %s\n", msg) - fmt.Fprint(os.Stderr, msg) + return strings.TrimSpace(pass), nil } diff --git a/client/keys/add.go b/client/keys/add.go index 4f5fda67d731..979983b9bdfe 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -5,35 +5,31 @@ import ( "bytes" "errors" "fmt" - "io" "sort" - bip39 "github.com/bartekn/go-bip39" + bip39 "github.com/cosmos/go-bip39" + "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/libs/cli" ) const ( flagInteractive = "interactive" flagRecover = "recover" flagNoBackup = "no-backup" - flagDryRun = "dry-run" + flagCoinType = "coin-type" flagAccount = "account" flagIndex = "index" flagMultisig = "multisig" flagNoSort = "nosort" flagHDPath = "hd-path" - flagKeyAlgo = "algo" // DefaultKeyPass contains the default key password for genesis transactions DefaultKeyPass = "12345678" @@ -62,41 +58,38 @@ required through --multisig-threshold. The keys are sorted by address, unless the flag --nosort is set. `, Args: cobra.ExactArgs(1), - RunE: runAddCmd, + RunE: runAddCmdPrepare, } + cmd.Flags().StringSlice(flagMultisig, nil, "Construct and store a multisig public key (implies --pubkey)") - cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures. For use in conjunction with --multisig") + cmd.Flags().Int(flagMultiSigThreshold, 1, "K out of N required signatures. For use in conjunction with --multisig") cmd.Flags().Bool(flagNoSort, false, "Keys passed to --multisig are taken in the order they're supplied") cmd.Flags().String(FlagPublicKey, "", "Parse a public key in bech32 format and save it to disk") cmd.Flags().BoolP(flagInteractive, "i", false, "Interactively prompt user for BIP39 passphrase and mnemonic") cmd.Flags().Bool(flags.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device") cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") - cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") + cmd.Flags().Bool(flags.FlagDryRun, false, "Perform action, but don't add key to local keystore") cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)") + cmd.Flags().Uint32(flagCoinType, sdk.GetConfig().GetCoinType(), "coin type number for HD derivation") cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation") cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") - cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") - cmd.Flags().String(flagKeyAlgo, string(keys.Secp256k1), "Key signing algorithm to generate keys for") - return cmd -} + cmd.Flags().String(flags.FlagKeyAlgorithm, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for") -func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) { - if transient { - return keys.NewInMemory(), nil - } + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) - return keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) + return cmd } -func runAddCmd(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf) +func runAddCmdPrepare(cmd *cobra.Command, args []string) error { + buf := bufio.NewReader(cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - return RunAddCmd(cmd, args, kb, inBuf) + return RunAddCmd(clientCtx, cmd, args, buf) } /* @@ -108,61 +101,68 @@ input output - armor encrypted private key (saved to file) */ -func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.Reader) error { +func RunAddCmd(ctx client.Context, cmd *cobra.Command, args []string, inBuf *bufio.Reader) error { var err error name := args[0] - - interactive := viper.GetBool(flagInteractive) - showMnemonic := !viper.GetBool(flagNoBackup) - - algo := keys.SigningAlgo(viper.GetString(flagKeyAlgo)) - if algo == keys.SigningAlgo("") { - algo = keys.Secp256k1 - } - if !keys.IsSupportedAlgorithm(kb.SupportedAlgos(), algo) { - return keys.ErrUnsupportedSigningAlgo + interactive, _ := cmd.Flags().GetBool(flagInteractive) + noBackup, _ := cmd.Flags().GetBool(flagNoBackup) + showMnemonic := !noBackup + kb := ctx.Keyring + outputFormat := ctx.OutputFormat + + keyringAlgos, _ := kb.SupportedAlgorithms() + algoStr, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm) + algo, err := keyring.NewSigningAlgoFromString(algoStr, keyringAlgos) + if err != nil { + return err } - if !viper.GetBool(flagDryRun) { - _, err = kb.Get(name) + if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); !dryRun { + _, err = kb.Key(name) if err == nil { // account exists, ask for user confirmation - response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf) + response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf, cmd.ErrOrStderr()) if err2 != nil { return err2 } + if !response { return errors.New("aborted") } + + err2 = kb.Delete(name) + if err2 != nil { + return err2 + } } - multisigKeys := viper.GetStringSlice(flagMultisig) + multisigKeys, _ := cmd.Flags().GetStringSlice(flagMultisig) if len(multisigKeys) != 0 { - var pks []crypto.PubKey + var pks []cryptotypes.PubKey - multisigThreshold := viper.GetInt(flagMultiSigThreshold) + multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold) if err := validateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { return err } for _, keyname := range multisigKeys { - k, err := kb.Get(keyname) + k, err := kb.Key(keyname) if err != nil { return err } + pks = append(pks, k.GetPubKey()) } - // Handle --nosort - if !viper.GetBool(flagNoSort) { + if noSort, _ := cmd.Flags().GetBool(flagNoSort); !noSort { sort.Slice(pks, func(i, j int) bool { return bytes.Compare(pks[i].Address(), pks[j].Address()) < 0 }) } - pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) - if _, err := kb.CreateMulti(name, pk); err != nil { + pk := multisig.NewLegacyAminoPubKey(multisigThreshold, pks) + if _, err := kb.SaveMultisig(name, pk); err != nil { return err } @@ -171,72 +171,70 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio. } } - if viper.GetString(FlagPublicKey) != "" { - pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, viper.GetString(FlagPublicKey)) + pubKey, _ := cmd.Flags().GetString(FlagPublicKey) + if pubKey != "" { + pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, pubKey) if err != nil { return err } - _, err = kb.CreateOffline(name, pk, algo) - if err != nil { + + if _, err := kb.SavePubKey(name, pk, algo.Name()); err != nil { return err } + return nil } - account := uint32(viper.GetInt(flagAccount)) - index := uint32(viper.GetInt(flagIndex)) - - useBIP44 := !viper.IsSet(flagHDPath) - var hdPath string + coinType, _ := cmd.Flags().GetUint32(flagCoinType) + account, _ := cmd.Flags().GetUint32(flagAccount) + index, _ := cmd.Flags().GetUint32(flagIndex) + hdPath, _ := cmd.Flags().GetString(flagHDPath) + useLedger, _ := cmd.Flags().GetBool(flags.FlagUseLedger) - if useBIP44 { - hdPath = keys.CreateHDPath(account, index).String() - } else { - hdPath = viper.GetString(flagHDPath) + if len(hdPath) == 0 { + hdPath = hd.CreateHDPath(coinType, account, index).String() + } else if useLedger { + return errors.New("cannot set custom bip32 path with ledger") } // If we're using ledger, only thing we need is the path and the bech32 prefix. - if viper.GetBool(flags.FlagUseLedger) { - - if !useBIP44 { - return errors.New("cannot set custom bip32 path with ledger") - } - - if !keys.IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) { - return keys.ErrUnsupportedSigningAlgo - } - + if useLedger { bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() - info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index) + info, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index) + if err != nil { return err } - return printCreate(cmd, info, false, "") + return printCreate(cmd, info, false, "", outputFormat) } // Get bip39 mnemonic - var mnemonic string - var bip39Passphrase string + var mnemonic, bip39Passphrase string - if interactive || viper.GetBool(flagRecover) { - bip39Message := "Enter your bip39 mnemonic" - if !viper.GetBool(flagRecover) { - bip39Message = "Enter your bip39 mnemonic, or hit enter to generate one." + recover, _ := cmd.Flags().GetBool(flagRecover) + if recover { + mnemonic, err = input.GetString("Enter your bip39 mnemonic", inBuf) + if err != nil { + return err } - mnemonic, err = input.GetString(bip39Message, inBuf) + if !bip39.IsMnemonicValid(mnemonic) { + return errors.New("invalid mnemonic") + } + } else if interactive { + mnemonic, err = input.GetString("Enter your bip39 mnemonic, or hit enter to generate one.", inBuf) if err != nil { return err } - if !bip39.IsMnemonicValid(mnemonic) { + if !bip39.IsMnemonicValid(mnemonic) && mnemonic != "" { return errors.New("invalid mnemonic") } } if len(mnemonic) == 0 { - // read entropy seed straight from crypto.Rand and convert to mnemonic + // read entropy seed straight from tmcrypto.Rand and convert to mnemonic entropySeed, err := bip39.NewEntropy(mnemonicEntropySize) if err != nil { return err @@ -270,38 +268,36 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio. } } - info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, hdPath, algo) + info, err := kb.NewAccount(name, mnemonic, bip39Passphrase, hdPath, algo) if err != nil { return err } // Recover key from seed passphrase - if viper.GetBool(flagRecover) { + if recover { // Hide mnemonic from output showMnemonic = false mnemonic = "" } - return printCreate(cmd, info, showMnemonic, mnemonic) + return printCreate(cmd, info, showMnemonic, mnemonic, outputFormat) } -func printCreate(cmd *cobra.Command, info keys.Info, showMnemonic bool, mnemonic string) error { - output := viper.Get(cli.OutputFlag) - - switch output { +func printCreate(cmd *cobra.Command, info keyring.Info, showMnemonic bool, mnemonic string, outputFormat string) error { + switch outputFormat { case OutputFormatText: cmd.PrintErrln() - printKeyInfo(info, keys.Bech32KeyOutput) + printKeyInfo(cmd.OutOrStdout(), info, keyring.Bech32KeyOutput, outputFormat) // print mnemonic unless requested not to. if showMnemonic { - cmd.PrintErrln("\n**Important** write this mnemonic phrase in a safe place.") - cmd.PrintErrln("It is the only way to recover your account if you ever forget your password.") - cmd.PrintErrln("") - cmd.PrintErrln(mnemonic) + fmt.Fprintln(cmd.ErrOrStderr(), "\n**Important** write this mnemonic phrase in a safe place.") + fmt.Fprintln(cmd.ErrOrStderr(), "It is the only way to recover your account if you ever forget your password.") + fmt.Fprintln(cmd.ErrOrStderr(), "") + fmt.Fprintln(cmd.ErrOrStderr(), mnemonic) } case OutputFormatJSON: - out, err := keys.Bech32KeyOutput(info) + out, err := keyring.Bech32KeyOutput(info) if err != nil { return err } @@ -310,19 +306,15 @@ func printCreate(cmd *cobra.Command, info keys.Info, showMnemonic bool, mnemonic out.Mnemonic = mnemonic } - var jsonString []byte - if viper.GetBool(flags.FlagIndentResponse) { - jsonString, err = KeysCdc.MarshalJSONIndent(out, "", " ") - } else { - jsonString, err = KeysCdc.MarshalJSON(out) - } - + jsonString, err := KeysCdc.MarshalJSON(out) if err != nil { return err } - cmd.PrintErrln(string(jsonString)) + + cmd.Println(string(jsonString)) + default: - return fmt.Errorf("invalid output format %s", output) + return fmt.Errorf("invalid output format %s", outputFormat) } return nil diff --git a/client/keys/add_ledger_test.go b/client/keys/add_ledger_test.go index e7678ce2f8a5..b8ba1ec7f59e 100644 --- a/client/keys/add_ledger_test.go +++ b/client/keys/add_ledger_test.go @@ -3,21 +3,23 @@ package keys import ( + "context" + "fmt" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { - runningUnattended := isRunningUnattended() config := sdk.GetConfig() bech32PrefixAccAddr := "terra" @@ -34,39 +36,43 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { config.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub) cmd := AddKeyCommand() - require.NotNil(t, cmd) + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - require.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(flags.FlagHome, kbHome) - viper.Set(flags.FlagUseLedger, true) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password - mockIn, _, _ := tests.ApplyMockIO(cmd) + kbHome := t.TempDir() + + clientCtx := client.Context{}.WithKeyringDir(kbHome) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + cmd.SetArgs([]string{ + "keyname1", + fmt.Sprintf("--%s=true", flags.FlagUseLedger), + fmt.Sprintf("--%s=0", flagAccount), + fmt.Sprintf("--%s=0", flagIndex), + fmt.Sprintf("--%s=330", flagCoinType), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) mockIn.Reset("test1234\ntest1234\n") - require.NoError(t, runAddCmd(cmd, []string{"keyname1"})) + require.NoError(t, cmd.ExecuteContext(ctx)) // Now check that it has been stored properly - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) require.NotNil(t, kb) - defer func() { - kb.Delete("keyname1", "", false) - }() + t.Cleanup(func() { + _ = kb.Delete("keyname1") + }) mockIn.Reset("test1234\n") - if runningUnattended { - mockIn.Reset("test1234\ntest1234\n") - } - key1, err := kb.Get("keyname1") + key1, err := kb.Key("keyname1") require.NoError(t, err) require.NotNil(t, key1) require.Equal(t, "keyname1", key1.GetName()) - require.Equal(t, keys.TypeLedger, key1.GetType()) + require.Equal(t, keyring.TypeLedger, key1.GetType()) require.Equal(t, "terrapub1addwnpepqvpg7r26nl2pvqqern00m6s9uaax3hauu2rzg8qpjzq9hy6xve7sw0d84m6", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, key1.GetPubKey())) @@ -79,42 +85,43 @@ func Test_runAddCmdLedgerWithCustomCoinType(t *testing.T) { } func Test_runAddCmdLedger(t *testing.T) { - runningUnattended := isRunningUnattended() cmd := AddKeyCommand() - require.NotNil(t, cmd) - mockIn, _, _ := tests.ApplyMockIO(cmd) - - // Prepare a keybase - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - require.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(flags.FlagHome, kbHome) - viper.Set(flags.FlagUseLedger, true) - - /// Test Text - viper.Set(cli.OutputFlag, OutputFormatText) - // Now enter password + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + kbHome := t.TempDir() + + clientCtx := client.Context{}.WithKeyringDir(kbHome) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + cmd.SetArgs([]string{ + "keyname1", + fmt.Sprintf("--%s=true", flags.FlagUseLedger), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%d", flagCoinType, sdk.CoinType), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) mockIn.Reset("test1234\ntest1234\n") - require.NoError(t, runAddCmd(cmd, []string{"keyname1"})) + + require.NoError(t, cmd.ExecuteContext(ctx)) // Now check that it has been stored properly - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) require.NotNil(t, kb) - defer func() { - kb.Delete("keyname1", "", false) - }() + t.Cleanup(func() { + _ = kb.Delete("keyname1") + }) + mockIn.Reset("test1234\n") - if runningUnattended { - mockIn.Reset("test1234\ntest1234\n") - } - key1, err := kb.Get("keyname1") + key1, err := kb.Key("keyname1") require.NoError(t, err) require.NotNil(t, key1) require.Equal(t, "keyname1", key1.GetName()) - require.Equal(t, keys.TypeLedger, key1.GetType()) + require.Equal(t, keyring.TypeLedger, key1.GetType()) require.Equal(t, - "fetchpub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw2k3x89", + "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, key1.GetPubKey())) } diff --git a/client/keys/add_test.go b/client/keys/add_test.go index b1ebee331684..aa6f68876a2e 100644 --- a/client/keys/add_test.go +++ b/client/keys/add_test.go @@ -1,57 +1,116 @@ package keys import ( + "context" + "fmt" "testing" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runAddCmdBasic(t *testing.T) { - runningUnattended := isRunningUnattended() cmd := AddKeyCommand() - assert.NotNil(t, cmd) - mockIn, _, _ := tests.ApplyMockIO(cmd) - - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(flags.FlagHome, kbHome) - viper.Set(cli.OutputFlag, OutputFormatText) - - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } else { - mockIn.Reset("y\n") - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn) - require.NoError(t, err) - defer func() { - kb.Delete("keyname1", "", false) - kb.Delete("keyname2", "", false) - }() - } - assert.NoError(t, runAddCmd(cmd, []string{"keyname1"})) - - if runningUnattended { - mockIn.Reset("testpass1\nN\n") - } else { - mockIn.Reset("N\n") - } - assert.Error(t, runAddCmd(cmd, []string{"keyname1"})) - - if runningUnattended { - mockIn.Reset("testpass1\nN\n") - } else { - mockIn.Reset("y\n") - } - err := runAddCmd(cmd, []string{"keyname2"}) - assert.NoError(t, err) + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + kbHome := t.TempDir() + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) + require.NoError(t, err) + + clientCtx := client.Context{}.WithKeyringDir(kbHome) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + t.Cleanup(func() { + _ = kb.Delete("keyname1") + _ = kb.Delete("keyname2") + }) + + cmd.SetArgs([]string{ + "keyname1", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + mockIn.Reset("y\n") + require.NoError(t, cmd.ExecuteContext(ctx)) + + mockIn.Reset("N\n") + require.Error(t, cmd.ExecuteContext(ctx)) + + cmd.SetArgs([]string{ + "keyname2", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + require.NoError(t, cmd.ExecuteContext(ctx)) + require.Error(t, cmd.ExecuteContext(ctx)) + + mockIn.Reset("y\n") + require.NoError(t, cmd.ExecuteContext(ctx)) + + cmd.SetArgs([]string{ + "keyname4", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + require.NoError(t, cmd.ExecuteContext(ctx)) + require.Error(t, cmd.ExecuteContext(ctx)) + + cmd.SetArgs([]string{ + "keyname5", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flags.FlagDryRun), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + }) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + // In recovery mode + cmd.SetArgs([]string{ + "keyname6", + fmt.Sprintf("--%s=true", flagRecover), + }) + + // use valid mnemonic and complete recovery key generation successfully + mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n") + require.NoError(t, cmd.ExecuteContext(ctx)) + + // use invalid mnemonic and fail recovery key generation + mockIn.Reset("invalid mnemonic\n") + require.Error(t, cmd.ExecuteContext(ctx)) + + // In interactive mode + cmd.SetArgs([]string{ + "keyname7", + "-i", + fmt.Sprintf("--%s=false", flagRecover), + }) + + const password = "password1!" + + // set password and complete interactive key generation successfully + mockIn.Reset("\n" + password + "\n" + password + "\n") + require.NoError(t, cmd.ExecuteContext(ctx)) + + // passwords don't match and fail interactive key generation + mockIn.Reset("\n" + password + "\n" + "fail" + "\n") + require.Error(t, cmd.ExecuteContext(ctx)) } diff --git a/client/keys/attestation.go b/client/keys/attestation.go deleted file mode 100644 index 1fe48009eaa6..000000000000 --- a/client/keys/attestation.go +++ /dev/null @@ -1,95 +0,0 @@ -package keys - -import ( - "bufio" - "errors" - "fmt" - "github.com/cosmos/cosmos-sdk/client/attestation" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/bech32" -) - -func AttestationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "attestation", - Short: "Create or verify attestations", - Long: `Create or verify offline proofs to demonstrate ownership of keys`, - } - - cmd.AddCommand( - &cobra.Command{ - Use: "create [name]", - Short: "Create an attestation", - Long: "Create and attestation for one of the keys present in the store", - Args: cobra.ExactArgs(1), - RunE: runAttestationCreate, - }, - &cobra.Command{ - Use: "verify [address] [attestation]", - Short: "Verify an attestation", - Long: "Given an attestation and address, verify that one proves ownership of the other", - Args: cobra.ExactArgs(2), - RunE: runAttestationVerify, - }, - ) - - return cmd -} - -func runAttestationCreate(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) - if err != nil { - return err - } - - decryptPassword, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) - if err != nil { - return err - } - - privKey, err := kb.ExportPrivateKeyObject(args[0], decryptPassword) - if err != nil { - return err - } - - att, err := attestation.NewAttestation(privKey) - if err != nil { - return err - } - - cmd.Println(att.String()) - - return nil -} - -func runAttestationVerify(cmd *cobra.Command, args []string) error { - _, bz, err := bech32.DecodeAndConvert(args[0]) - if err != nil { - return err - } - - var address crypto.Address = bz - - // create the attestation - att, err := attestation.NewAttestationFromString(args[1]) - if err != nil { - return err - } - - // verification check - verified := att.Verify(address) - if !verified { - return errors.New("verification failed") - } - - fmt.Println("verification successful") - - return nil -} \ No newline at end of file diff --git a/client/keys/codec.go b/client/keys/codec.go index e47d3b231afc..2fb7bd94397e 100644 --- a/client/keys/codec.go +++ b/client/keys/codec.go @@ -2,14 +2,15 @@ package keys import ( "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" ) // KeysCdc defines codec to be used with key operations -var KeysCdc *codec.Codec +var KeysCdc *codec.LegacyAmino func init() { - KeysCdc = codec.New() - codec.RegisterCrypto(KeysCdc) + KeysCdc = codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(KeysCdc) KeysCdc.Seal() } diff --git a/client/keys/codec_test.go b/client/keys/codec_test.go index 6bc6bd49e365..33f6103d1888 100644 --- a/client/keys/codec_test.go +++ b/client/keys/codec_test.go @@ -1,31 +1,31 @@ -package keys +package keys_test import ( - "fmt" - "reflect" + "bytes" "testing" "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" ) type testCases struct { - Keys []keys.KeyOutput - Answers []keys.KeyOutput + Keys []keyring.KeyOutput + Answers []keyring.KeyOutput JSON [][]byte } func getTestCases() testCases { return testCases{ // nolint:govet - []keys.KeyOutput{ + []keyring.KeyOutput{ {"A", "B", "C", "D", "E", 0, nil}, {"A", "B", "C", "D", "", 0, nil}, {"", "B", "C", "D", "", 0, nil}, {"", "", "", "", "", 0, nil}, }, - make([]keys.KeyOutput, 4), + make([]keyring.KeyOutput, 4), [][]byte{ []byte(`{"name":"A","type":"B","address":"C","pubkey":"D","mnemonic":"E"}`), []byte(`{"name":"A","type":"B","address":"C","pubkey":"D"}`), @@ -37,7 +37,7 @@ func getTestCases() testCases { func TestMarshalJSON(t *testing.T) { type args struct { - o keys.KeyOutput + o keyring.KeyOutput } data := getTestCases() @@ -58,15 +58,9 @@ func TestMarshalJSON(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - got, err := MarshalJSON(tt.args.o) - if (err != nil) != tt.wantErr { - t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - fmt.Printf("%s\n", got) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MarshalJSON() = %v, want %v", got, tt.want) - } + got, err := keys.MarshalJSON(tt.args.o) + require.Equal(t, tt.wantErr, err != nil) + require.True(t, bytes.Equal(got, tt.want)) }) } } @@ -94,10 +88,8 @@ func TestUnmarshalJSON(t *testing.T) { for idx, tt := range tests { idx, tt := idx, tt t.Run(tt.name, func(t *testing.T) { - if err := UnmarshalJSON(tt.args.bz, tt.args.ptr); (err != nil) != tt.wantErr { - t.Errorf("unmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - + err := keys.UnmarshalJSON(tt.args.bz, tt.args.ptr) + require.Equal(t, tt.wantErr, err != nil) // Confirm deserialized objects are the same require.Equal(t, data.Keys[idx], data.Answers[idx]) }) diff --git a/client/keys/delete.go b/client/keys/delete.go index 3559a228082a..e0921710141f 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -2,15 +2,12 @@ package keys import ( "bufio" - "errors" - "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/spf13/cobra" - "github.com/spf13/viper" ) const ( @@ -29,63 +26,46 @@ Note that removing offline or ledger keys will remove only the public key references stored locally, i.e. private keys stored in a ledger device cannot be deleted with the CLI. `, - RunE: runDeleteCmd, Args: cobra.MinimumNArgs(1), - } - - cmd.Flags().BoolP(flagYes, "y", false, - "Skip confirmation prompt when deleting offline or ledger key references") - cmd.Flags().BoolP(flagForce, "f", false, - "Remove the key unconditionally without asking for the passphrase. Deprecated.") - return cmd -} - -func runDeleteCmd(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) + RunE: func(cmd *cobra.Command, args []string) error { + buf := bufio.NewReader(cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) - if err != nil { - return err - } + for _, name := range args { + info, err := clientCtx.Keyring.Key(name) + if err != nil { + return err + } - for _, name := range args { - info, err := kb.Get(name) - if err != nil { - return err - } + // confirm deletion, unless -y is passed + if skip, _ := cmd.Flags().GetBool(flagYes); !skip { + if yes, err := input.GetConfirmation("Key reference will be deleted. Continue?", buf, cmd.ErrOrStderr()); err != nil { + return err + } else if !yes { + continue + } + } - if info.GetType() == keys.TypeLedger || info.GetType() == keys.TypeOffline { - // confirm deletion, unless -y is passed - if !viper.GetBool(flagYes) { - if err := confirmDeletion(buf); err != nil { + if err := clientCtx.Keyring.Delete(name); err != nil { return err } - } - if err := kb.Delete(name, "", true); err != nil { - return err + if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline { + cmd.PrintErrln("Public key reference deleted") + continue + } + cmd.PrintErrln("Key deleted forever (uh oh!)") } - cmd.PrintErrln("Public key reference deleted") - return nil - } - // old password and skip flag arguments are ignored - if err := kb.Delete(name, "", true); err != nil { - return err - } - cmd.PrintErrln("Key deleted forever (uh oh!)") + return nil + }, } - return nil -} + cmd.Flags().BoolP(flagYes, "y", false, "Skip confirmation prompt when deleting offline or ledger key references") + cmd.Flags().BoolP(flagForce, "f", false, "Remove the key unconditionally without asking for the passphrase. Deprecated.") -func confirmDeletion(buf *bufio.Reader) error { - answer, err := input.GetConfirmation("Key reference will be deleted. Continue?", buf) - if err != nil { - return err - } - if !answer { - return errors.New("aborted") - } - return nil + return cmd } diff --git a/client/keys/delete_test.go b/client/keys/delete_test.go index b2df8684dffe..af2c926158dd 100644 --- a/client/keys/delete_test.go +++ b/client/keys/delete_test.go @@ -1,135 +1,94 @@ package keys import ( - "bufio" - "strings" + "context" + "fmt" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runDeleteCmd(t *testing.T) { - runningUnattended := isRunningUnattended() - deleteKeyCommand := DeleteKeyCommand() - mockIn, _, _ := tests.ApplyMockIO(deleteKeyCommand) + // Now add a temporary keybase + kbHome := t.TempDir() + cmd := DeleteKeyCommand() + cmd.Flags().AddFlagSet(Commands(kbHome).PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + yesF, _ := cmd.Flags().GetBool(flagYes) + forceF, _ := cmd.Flags().GetBool(flagForce) - yesF, _ := deleteKeyCommand.Flags().GetBool(flagYes) - forceF, _ := deleteKeyCommand.Flags().GetBool(flagForce) require.False(t, yesF) require.False(t, forceF) fakeKeyName1 := "runDeleteCmd_Key1" fakeKeyName2 := "runDeleteCmd_Key2" - if !runningUnattended { - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) - require.NoError(t, err) - defer func() { - kb.Delete("runDeleteCmd_Key1", "", false) - kb.Delete("runDeleteCmd_Key2", "", false) - - }() - } - // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(flags.FlagHome, kbHome) - // Now - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), kbHome, mockIn) + path := sdk.GetConfig().GetFullFundraiserPath() + + cmd.SetArgs([]string{"blah", fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome)}) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1) + + _, err = kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1) + _, _, err = kb.NewMnemonic(fakeKeyName2, keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - err = runDeleteCmd(deleteKeyCommand, []string{"blah"}) + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb) + + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + err = cmd.ExecuteContext(ctx) require.Error(t, err) require.Equal(t, "The specified item could not be found in the keyring", err.Error()) // User confirmation missing - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1}) + cmd.SetArgs([]string{ + fakeKeyName1, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + err = cmd.Execute() require.Error(t, err) - if runningUnattended { - require.Equal(t, "aborted", err.Error()) - } else { - require.Equal(t, "EOF", err.Error()) - } - - { - if runningUnattended { - mockIn.Reset("testpass1\n") - } - _, err = kb.Get(fakeKeyName1) - require.NoError(t, err) - - // Now there is a confirmation - viper.Set(flagYes, true) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - require.NoError(t, runDeleteCmd(deleteKeyCommand, []string{fakeKeyName1})) - - _, err = kb.Get(fakeKeyName1) - require.Error(t, err) // Key1 is gone - } - - viper.Set(flagYes, true) - if runningUnattended { - mockIn.Reset("testpass1\n") - } - _, err = kb.Get(fakeKeyName2) + require.Equal(t, "EOF", err.Error()) + + _, err = kb.Key(fakeKeyName1) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\ny\ntestpass1\n") - } - err = runDeleteCmd(deleteKeyCommand, []string{fakeKeyName2}) + + // Now there is a confirmation + cmd.SetArgs([]string{ + fakeKeyName1, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flagYes), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + require.NoError(t, cmd.Execute()) + + _, err = kb.Key(fakeKeyName1) + require.Error(t, err) // Key1 is gone + + _, err = kb.Key(fakeKeyName2) require.NoError(t, err) - _, err = kb.Get(fakeKeyName2) - require.Error(t, err) // Key2 is gone -} -func Test_confirmDeletion(t *testing.T) { - type args struct { - buf *bufio.Reader - } - - answerYes := bufio.NewReader(strings.NewReader("y\n")) - answerYes2 := bufio.NewReader(strings.NewReader("Y\n")) - answerNo := bufio.NewReader(strings.NewReader("n\n")) - answerInvalid := bufio.NewReader(strings.NewReader("245\n")) - - tests := []struct { - name string - args args - wantErr bool - }{ - {"Y", args{answerYes}, false}, - {"y", args{answerYes2}, false}, - {"N", args{answerNo}, true}, - {"BAD", args{answerInvalid}, true}, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - if err := confirmDeletion(tt.args.buf); (err != nil) != tt.wantErr { - t.Errorf("confirmDeletion() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } + cmd.SetArgs([]string{ + fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flagYes), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + require.NoError(t, cmd.Execute()) + + _, err = kb.Key(fakeKeyName2) + require.Error(t, err) // Key2 is gone } diff --git a/client/keys/export.go b/client/keys/export.go index fb12eb508d5e..7e9cb88ed633 100644 --- a/client/keys/export.go +++ b/client/keys/export.go @@ -2,48 +2,85 @@ package keys import ( "bufio" + "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" +) + +const ( + flagUnarmoredHex = "unarmored-hex" + flagUnsafe = "unsafe" ) // ExportKeyCommand exports private keys from the key store. func ExportKeyCommand() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "export ", Short: "Export private keys", - Long: `Export a private key from the local keybase in ASCII-armored encrypted format.`, - Args: cobra.ExactArgs(1), - RunE: runExportCmd, + Long: `Export a private key from the local keyring in ASCII-armored encrypted format. + +When both the --unarmored-hex and --unsafe flags are selected, cryptographic +private key material is exported in an INSECURE fashion that is designed to +allow users to import their keys in hot wallets. This feature is for advanced +users only that are confident about how to handle private keys work and are +FULLY AWARE OF THE RISKS. If you are unsure, you may want to do some research +and export your keys in ASCII-armored encrypted format.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + buf := bufio.NewReader(cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + unarmored, _ := cmd.Flags().GetBool(flagUnarmoredHex) + unsafe, _ := cmd.Flags().GetBool(flagUnsafe) + + if unarmored && unsafe { + return exportUnsafeUnarmored(cmd, args[0], buf, clientCtx.Keyring) + } else if unarmored || unsafe { + return fmt.Errorf("the flags %s and %s must be used together", flagUnsafe, flagUnarmoredHex) + } + + encryptPassword, err := input.GetPassword("Enter passphrase to encrypt the exported key:", buf) + if err != nil { + return err + } + + armored, err := clientCtx.Keyring.ExportPrivKeyArmor(args[0], encryptPassword) + if err != nil { + return err + } + + cmd.Println(armored) + + return nil + }, } + + cmd.Flags().Bool(flagUnarmoredHex, false, "Export unarmored hex privkey. Requires --unsafe.") + cmd.Flags().Bool(flagUnsafe, false, "Enable unsafe operations. This flag must be switched on along with all unsafe operation-specific options.") + + return cmd } -func runExportCmd(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) - if err != nil { +func exportUnsafeUnarmored(cmd *cobra.Command, uid string, buf *bufio.Reader, kr keyring.Keyring) error { + // confirm deletion, unless -y is passed + if yes, err := input.GetConfirmation("WARNING: The private key will be exported as an unarmored hexadecimal string. USE AT YOUR OWN RISK. Continue?", buf, cmd.ErrOrStderr()); err != nil { return err + } else if !yes { + return nil } - decryptPassword, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) - if err != nil { - return err - } - encryptPassword, err := input.GetPassword("Enter passphrase to encrypt the exported key:", buf) + hexPrivKey, err := keyring.NewUnsafe(kr).UnsafeExportPrivKeyHex(uid) if err != nil { return err } - armored, err := kb.ExportPrivKey(args[0], decryptPassword, encryptPassword) - if err != nil { - return err - } + cmd.Println(hexPrivKey) - cmd.Println(armored) return nil } diff --git a/client/keys/export_test.go b/client/keys/export_test.go index 4d0236cdd05a..b207a71bb159 100644 --- a/client/keys/export_test.go +++ b/client/keys/export_test.go @@ -1,47 +1,71 @@ package keys import ( + "context" + "fmt" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runExportCmd(t *testing.T) { - runningUnattended := isRunningUnattended() - exportKeyCommand := ExportKeyCommand() - mockIn, _, _ := tests.ApplyMockIO(exportKeyCommand) + cmd := ExportKeyCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(flags.FlagHome, kbHome) + kbHome := t.TempDir() // create a key - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) - if !runningUnattended { - defer func() { - kb.Delete("keyname1", "", false) - }() - } + t.Cleanup(func() { + kb.Delete("keyname1") // nolint:errcheck + }) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - _, err = kb.CreateAccount("keyname1", tests.TestMnemonic, "", "123456789", "", keys.Secp256k1) + path := sdk.GetConfig().GetFullFundraiserPath() + _, err = kb.NewAccount("keyname1", testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) // Now enter password - if runningUnattended { - mockIn.Reset("123456789\n123456789\ntestpass1\n") - } else { - mockIn.Reset("123456789\n123456789\n") + args := []string{ + "keyname1", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), } - require.NoError(t, runExportCmd(exportKeyCommand, []string{"keyname1"})) + + mockIn.Reset("123456789\n123456789\n") + cmd.SetArgs(args) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + require.NoError(t, cmd.ExecuteContext(ctx)) + + argsUnsafeOnly := append(args, "--unsafe") + cmd.SetArgs(argsUnsafeOnly) + require.Error(t, cmd.ExecuteContext(ctx)) + + argsUnarmoredHexOnly := append(args, "--unarmored-hex") + cmd.SetArgs(argsUnarmoredHexOnly) + require.Error(t, cmd.ExecuteContext(ctx)) + + argsUnsafeUnarmoredHex := append(args, "--unsafe", "--unarmored-hex") + cmd.SetArgs(argsUnsafeUnarmoredHex) + require.Error(t, cmd.ExecuteContext(ctx)) + + mockIn, mockOut := testutil.ApplyMockIO(cmd) + mockIn.Reset("y\n") + require.NoError(t, cmd.ExecuteContext(ctx)) + require.Equal(t, "2485e33678db4175dc0ecef2d6e1fc493d4a0d7f7ce83324b6ed70afe77f3485\n", mockOut.String()) } diff --git a/client/keys/import.go b/client/keys/import.go index cbc1b83e85d3..2b7e230654ce 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -2,79 +2,39 @@ package keys import ( "bufio" - "encoding/hex" - "errors" "io/ioutil" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" -) - -const ( - flagAgentRawKey = "agent-raw-key" ) // ImportKeyCommand imports private keys from a keyfile. func ImportKeyCommand() *cobra.Command { - cmd := &cobra.Command{ + return &cobra.Command{ Use: "import ", Short: "Import private keys into the local keybase", Long: "Import a ASCII armored private key into the local keybase.", Args: cobra.ExactArgs(2), - RunE: runImportCmd, + RunE: func(cmd *cobra.Command, args []string) error { + buf := bufio.NewReader(cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + bz, err := ioutil.ReadFile(args[1]) + if err != nil { + return err + } + + passphrase, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) + if err != nil { + return err + } + + return clientCtx.Keyring.ImportPrivKey(args[0], string(bz), passphrase) + }, } - cmd.Flags().Bool(flagAgentRawKey, false, "Signal that you want to import an key from the agent framework") - - return cmd -} - -func runImportCmd(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) - if err != nil { - return err - } - - bz, err := ioutil.ReadFile(args[1]) - if err != nil { - return err - } - - // if the user has requested that a raw agent key is imported then we must read the file as such - if viper.GetBool(flagAgentRawKey) { - // hex decode the input string - rawPrivKey, err := hex.DecodeString(string(bz)) - if err != nil { - return err - } - - // check the size of the binary data - if len(rawPrivKey) != 32 { - return errors.New("Incorrect raw key size. Please check input path") - } - - // create the underlying private key - var priv secp256k1.PrivKeySecp256k1 - copy(priv[:], rawPrivKey) - - // armor and encrypt the key with a dummy password (to reuse existing private key import path) - passPhrase := "dummy-not-really-used" - armored := mintkey.EncryptArmorPrivKey(priv, passPhrase, "secp256k1") - - return kb.ImportPrivKey(args[0], armored, passPhrase) - } - - passphrase, err := input.GetPassword("Enter passphrase to decrypt your key:", buf) - if err != nil { - return err - } - - return kb.ImportPrivKey(args[0], string(bz), passphrase) } diff --git a/client/keys/import_test.go b/client/keys/import_test.go index faac935179bb..ea84408c2df5 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -1,36 +1,39 @@ package keys import ( + "context" + "fmt" "io/ioutil" "path/filepath" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runImportCmd(t *testing.T) { - runningUnattended := isRunningUnattended() - importKeyCommand := ImportKeyCommand() - mockIn, _, _ := tests.ApplyMockIO(importKeyCommand) + cmd := ImportKeyCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(flags.FlagHome, kbHome) - - if !runningUnattended { - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) - require.NoError(t, err) - defer func() { - kb.Delete("keyname1", "", false) - }() - } + kbHome := t.TempDir() + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + require.NoError(t, err) + t.Cleanup(func() { + kb.Delete("keyname1") // nolint:errcheck + }) keyfile := filepath.Join(kbHome, "key.asc") armoredKey := `-----BEGIN TENDERMINT PRIVATE KEY----- @@ -44,11 +47,10 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO ` require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644)) - // Now enter password - if runningUnattended { - mockIn.Reset("123456789\n12345678\n12345678\n") - } else { - mockIn.Reset("123456789\n") - } - require.NoError(t, runImportCmd(importKeyCommand, []string{"keyname1", keyfile})) + mockIn.Reset("123456789\n") + cmd.SetArgs([]string{ + "keyname1", keyfile, + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + require.NoError(t, cmd.ExecuteContext(ctx)) } diff --git a/client/keys/list.go b/client/keys/list.go index dbac54502d98..d6c2511b6484 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -2,11 +2,8 @@ package keys import ( "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" ) const flagListNames = "list-names" @@ -20,28 +17,29 @@ func ListKeysCmd() *cobra.Command { along with their associated name and address.`, RunE: runListCmd, } - cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") + cmd.Flags().BoolP(flagListNames, "n", false, "List names only") return cmd } func runListCmd(cmd *cobra.Command, _ []string) error { - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - infos, err := kb.List() + infos, err := clientCtx.Keyring.List() if err != nil { return err } - if !viper.GetBool(flagListNames) { - printInfos(infos) + cmd.SetOut(cmd.OutOrStdout()) + + if ok, _ := cmd.Flags().GetBool(flagListNames); !ok { + printInfos(cmd.OutOrStdout(), infos, clientCtx.OutputFormat) return nil } - cmd.SetOut(cmd.OutOrStdout()) for _, info := range infos { cmd.Println(info.GetName()) } diff --git a/client/keys/list_test.go b/client/keys/list_test.go index ac8ca97d0fb5..5904f74bfdd2 100644 --- a/client/keys/list_test.go +++ b/client/keys/list_test.go @@ -1,75 +1,76 @@ package keys import ( + "context" + "fmt" "testing" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) func Test_runListCmd(t *testing.T) { - runningUnattended := isRunningUnattended() - type args struct { - cmd *cobra.Command - args []string - } + cmd := ListKeysCmd() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) - cmdBasic := ListKeysCmd() + kbHome1 := t.TempDir() + kbHome2 := t.TempDir() - // Prepare some keybases - kbHome1, cleanUp1 := tests.NewTestCaseDir(t) - defer cleanUp1() - // Do nothing, leave home1 empty + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome2, mockIn) + require.NoError(t, err) - kbHome2, cleanUp2 := tests.NewTestCaseDir(t) - defer cleanUp2() - viper.Set(flags.FlagHome, kbHome2) + clientCtx := client.Context{}.WithKeyring(kb) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) - mockIn, _, _ := tests.ApplyMockIO(cmdBasic) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) + path := "" //sdk.GetConfig().GetFullFundraiserPath() + _, err = kb.NewAccount("something", testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - _, err = kb.CreateAccount("something", tests.TestMnemonic, "", "", "", keys.Secp256k1) - require.NoError(t, err) + t.Cleanup(func() { + kb.Delete("something") // nolint:errcheck + }) + + type args struct { + cmd *cobra.Command + args []string + } - defer func() { - kb.Delete("something", "", false) - }() testData := []struct { name string kbDir string - args args wantErr bool }{ - {"keybase: empty", kbHome1, args{cmdBasic, []string{}}, false}, - {"keybase: w/key", kbHome2, args{cmdBasic, []string{}}, false}, + {"keybase: empty", kbHome1, false}, + {"keybase: w/key", kbHome2, false}, } for _, tt := range testData { tt := tt t.Run(tt.name, func(t *testing.T) { - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - viper.Set(flagListNames, false) - viper.Set(flags.FlagHome, tt.kbDir) - if err := runListCmd(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr { + cmd.SetArgs([]string{ + fmt.Sprintf("--%s=%s", flags.FlagHome, tt.kbDir), + fmt.Sprintf("--%s=false", flagListNames), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + if err := cmd.ExecuteContext(ctx); (err != nil) != tt.wantErr { t.Errorf("runListCmd() error = %v, wantErr %v", err, tt.wantErr) } - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - viper.Set(flagListNames, true) - if err := runListCmd(tt.args.cmd, tt.args.args); (err != nil) != tt.wantErr { + cmd.SetArgs([]string{ + fmt.Sprintf("--%s=%s", flags.FlagHome, tt.kbDir), + fmt.Sprintf("--%s=true", flagListNames), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + if err := cmd.ExecuteContext(ctx); (err != nil) != tt.wantErr { t.Errorf("runListCmd() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/client/keys/migrate.go b/client/keys/migrate.go index ef1d832ac113..4816381e8c6f 100644 --- a/client/keys/migrate.go +++ b/client/keys/migrate.go @@ -6,14 +6,13 @@ import ( "io/ioutil" "os" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) // migratePassphrase is used as a no-op migration key passphrase as a passphrase @@ -23,16 +22,18 @@ const migratePassphrase = "NOOP_PASSPHRASE" // MigrateCommand migrates key information from legacy keybase to OS secret store. func MigrateCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "migrate", + Use: "migrate ", Short: "Migrate keys from the legacy (db-based) Keybase", - Long: `Migrate key information from the legacy (db-based) Keybase to the new keyring-based Keybase. + Long: `Migrate key information from the legacy (db-based) Keybase to the new keyring-based Keyring. +The legacy Keybase used to persist keys in a LevelDB database stored in a 'keys' sub-directory of +the old client application's home directory, e.g. $HOME/.gaiacli/keys/. For each key material entry, the command will prompt if the key should be skipped or not. If the key is not to be skipped, the passphrase must be entered. The key will only be migrated if the passphrase is correct. Otherwise, the command will exit and migration must be repeated. It is recommended to run in 'dry-run' mode first to verify all key migration material. `, - Args: cobra.ExactArgs(0), + Args: cobra.ExactArgs(1), RunE: runMigrateCmd, } @@ -41,13 +42,17 @@ It is recommended to run in 'dry-run' mode first to verify all key migration mat } func runMigrateCmd(cmd *cobra.Command, args []string) error { + rootDir, _ := cmd.Flags().GetString(flags.FlagHome) + // instantiate legacy keybase - rootDir := viper.GetString(flags.FlagHome) - legacyKb, err := NewKeyBaseFromDir(rootDir) + var legacyKb keyring.LegacyKeybase + legacyKb, err := NewLegacyKeyBaseFromDir(args[0]) if err != nil { return err } + defer func() { _ = legacyKb.Close() }() + // fetch list of keys from legacy keybase oldKeys, err := legacyKb.List() if err != nil { @@ -58,22 +63,24 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { keyringServiceName := sdk.KeyringServiceName() var ( - tmpDir string - keybase keys.Keybase + tmpDir string + migrator keyring.Importer ) - if viper.GetBool(flags.FlagDryRun) { - tmpDir, err = ioutil.TempDir("", "keybase-migrate-dryrun") + if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun { + tmpDir, err = ioutil.TempDir("", "migrator-migrate-dryrun") if err != nil { return errors.Wrap(err, "failed to create temporary directory for dryrun migration") } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() - keybase, err = keys.NewKeyring(keyringServiceName, "test", tmpDir, buf) + migrator, err = keyring.New(keyringServiceName, keyring.BackendTest, tmpDir, buf) } else { - keybase, err = keys.NewKeyring(keyringServiceName, viper.GetString(flags.FlagKeyringBackend), rootDir, buf) + backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + migrator, err = keyring.New(keyringServiceName, backend, rootDir, buf) } + if err != nil { return errors.Wrap(err, fmt.Sprintf( "failed to initialize keybase for service %s at directory %s", @@ -81,25 +88,19 @@ 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 + } - keyName := key.GetName() - keyType := key.GetType() + for _, oldInfo := range oldKeys { + keyName := oldInfo.GetName() + keyType := oldInfo.GetType() - // skip key if already migrated - if _, err := keybase.Get(keyName); err == nil { - cmd.PrintErrf("Key '%s (%s)' already exists; skipping ...\n", key.GetName(), keyType) - continue - } - - cmd.PrintErrf("Migrating key: '%s (%s)' ...\n", key.GetName(), keyType) + cmd.PrintErrf("Migrating key: '%s (%s)' ...\n", keyName, keyType) // allow user to skip migrating specific keys - ok, err := input.GetConfirmation("Skip key migration?", buf) + ok, err := input.GetConfirmation("Skip key migration?", buf, cmd.ErrOrStderr()) if err != nil { return err } @@ -107,8 +108,15 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { continue } - if keyType != keys.TypeLocal { - if err := keybase.Import(keyName, legKeyInfo); err != nil { + // TypeLocal needs an additional step to ask password. + // The other keyring types are handled by ImportInfo. + if keyType != keyring.TypeLocal { + infoImporter, ok := migrator.(keyring.LegacyInfoImporter) + if !ok { + return fmt.Errorf("the Keyring implementation does not support import operations of Info types") + } + + if err = infoImporter.ImportInfo(oldInfo); err != nil { return err } @@ -128,10 +136,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error { return err } - if err := keybase.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil { + if err := migrator.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil { return err } + } + cmd.PrintErrln("Migration complete.") return err } diff --git a/client/keys/migrate_test.go b/client/keys/migrate_test.go index 0ad9226d309d..32746291c882 100644 --- a/client/keys/migrate_test.go +++ b/client/keys/migrate_test.go @@ -1,36 +1,42 @@ package keys import ( + "context" + "fmt" "testing" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/client" + + "github.com/stretchr/testify/require" - "github.com/spf13/viper" + "github.com/otiai10/copy" "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" ) func Test_runMigrateCmd(t *testing.T) { - cmd := AddKeyCommand() - assert.NotNil(t, cmd) - mockIn, _, _ := tests.ApplyMockIO(cmd) - - kbHome, kbCleanUp := tests.NewTestCaseDir(t) - assert.NotNil(t, kbHome) - defer kbCleanUp() - viper.Set(flags.FlagHome, kbHome) - - viper.Set(cli.OutputFlag, OutputFormatText) - - mockIn.Reset("test1234\ntest1234\n") - err := runAddCmd(cmd, []string{"keyname1"}) - assert.NoError(t, err) - - viper.Set(flags.FlagDryRun, true) - cmd = MigrateCommand() - mockIn, _, _ = tests.ApplyMockIO(cmd) - mockIn.Reset("test1234\n") - assert.NoError(t, runMigrateCmd(cmd, []string{})) + kbHome := t.TempDir() + clientCtx := client.Context{}.WithKeyringDir(kbHome) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + require.NoError(t, copy.Copy("testdata", kbHome)) + + cmd := MigrateCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + //mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + mockIn, mockOut := testutil.ApplyMockIO(cmd) + + cmd.SetArgs([]string{ + kbHome, + //fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flags.FlagDryRun), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + + mockIn.Reset("\n12345678\n\n\n\n\n") + t.Log(mockOut.String()) + assert.NoError(t, cmd.ExecuteContext(ctx)) } diff --git a/client/keys/mnemonic.go b/client/keys/mnemonic.go index 01d7416f0536..c411612782e6 100644 --- a/client/keys/mnemonic.go +++ b/client/keys/mnemonic.go @@ -5,7 +5,7 @@ import ( "crypto/sha256" "fmt" - bip39 "github.com/bartekn/go-bip39" + bip39 "github.com/cosmos/go-bip39" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/input" @@ -23,54 +23,53 @@ func MnemonicKeyCommand() *cobra.Command { Use: "mnemonic", Short: "Compute the bip39 mnemonic for some input entropy", Long: "Create a bip39 mnemonic, sometimes called a seed phrase, by reading from the system entropy. To pass your own entropy, use --unsafe-entropy", - RunE: runMnemonicCmd, - } - cmd.Flags().Bool(flagUserEntropy, false, "Prompt the user to supply their own entropy, instead of relying on the system") - return cmd -} + RunE: func(cmd *cobra.Command, args []string) error { + var entropySeed []byte -func runMnemonicCmd(cmd *cobra.Command, args []string) error { - flags := cmd.Flags() + if userEntropy, _ := cmd.Flags().GetBool(flagUserEntropy); userEntropy { + // prompt the user to enter some entropy + buf := bufio.NewReader(cmd.InOrStdin()) - userEntropy, _ := flags.GetBool(flagUserEntropy) + inputEntropy, err := input.GetString("> WARNING: Generate at least 256-bits of entropy and enter the results here:", buf) + if err != nil { + return err + } - var entropySeed []byte + if len(inputEntropy) < 43 { + return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy)) + } - if userEntropy { - // prompt the user to enter some entropy - buf := bufio.NewReader(cmd.InOrStdin()) - inputEntropy, err := input.GetString("> WARNING: Generate at least 256-bits of entropy and enter the results here:", buf) - if err != nil { - return err - } - if len(inputEntropy) < 43 { - return fmt.Errorf("256-bits is 43 characters in Base-64, and 100 in Base-6. You entered %v, and probably want more", len(inputEntropy)) - } - conf, err := input.GetConfirmation(fmt.Sprintf("> Input length: %d", len(inputEntropy)), buf) - if err != nil { - return err - } - if !conf { - return nil - } + conf, err := input.GetConfirmation(fmt.Sprintf("> Input length: %d", len(inputEntropy)), buf, cmd.ErrOrStderr()) + if err != nil { + return err + } - // hash input entropy to get entropy seed - hashedEntropy := sha256.Sum256([]byte(inputEntropy)) - entropySeed = hashedEntropy[:] - } else { - // read entropy seed straight from crypto.Rand - var err error - entropySeed, err = bip39.NewEntropy(mnemonicEntropySize) - if err != nil { - return err - } - } + if !conf { + return nil + } - mnemonic, err := bip39.NewMnemonic(entropySeed) - if err != nil { - return err + // hash input entropy to get entropy seed + hashedEntropy := sha256.Sum256([]byte(inputEntropy)) + entropySeed = hashedEntropy[:] + } else { + // read entropy seed straight from crypto.Rand + var err error + entropySeed, err = bip39.NewEntropy(mnemonicEntropySize) + if err != nil { + return err + } + } + + mnemonic, err := bip39.NewMnemonic(entropySeed) + if err != nil { + return err + } + + cmd.Println(mnemonic) + return nil + }, } - cmd.Println(mnemonic) - return nil + cmd.Flags().Bool(flagUserEntropy, false, "Prompt the user to supply their own entropy, instead of relying on the system") + return cmd } diff --git a/client/keys/mnemonic_test.go b/client/keys/mnemonic_test.go index 7097ef0a3ca5..c986e6230b5d 100644 --- a/client/keys/mnemonic_test.go +++ b/client/keys/mnemonic_test.go @@ -1,34 +1,35 @@ package keys import ( + "fmt" "strings" "testing" - "github.com/cosmos/cosmos-sdk/tests" - - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" ) func Test_RunMnemonicCmdNormal(t *testing.T) { - cmdBasic := MnemonicKeyCommand() - err := runMnemonicCmd(cmdBasic, []string{}) - require.NoError(t, err) + cmd := MnemonicKeyCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + cmd.SetArgs([]string{}) + require.NoError(t, cmd.Execute()) } func Test_RunMnemonicCmdUser(t *testing.T) { - cmdUser := MnemonicKeyCommand() - err := cmdUser.Flags().Set(flagUserEntropy, "1") - assert.NoError(t, err) + cmd := MnemonicKeyCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) - err = runMnemonicCmd(cmdUser, []string{}) + cmd.SetArgs([]string{fmt.Sprintf("--%s=1", flagUserEntropy)}) + err := cmd.Execute() require.Error(t, err) require.Equal(t, "EOF", err.Error()) // Try again - mockIn, _, _ := tests.ApplyMockIO(cmdUser) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) mockIn.Reset("Hi!\n") - err = runMnemonicCmd(cmdUser, []string{}) + err = cmd.Execute() require.Error(t, err) require.Equal(t, "256-bits is 43 characters in Base-64, and 100 in Base-6. You entered 3, and probably want more", @@ -37,18 +38,15 @@ func Test_RunMnemonicCmdUser(t *testing.T) { // Now provide "good" entropy :) fakeEntropy := strings.Repeat(":)", 40) + "\ny\n" // entropy + accept count mockIn.Reset(fakeEntropy) - err = runMnemonicCmd(cmdUser, []string{}) - require.NoError(t, err) + require.NoError(t, cmd.Execute()) // Now provide "good" entropy but no answer fakeEntropy = strings.Repeat(":)", 40) + "\n" // entropy + accept count mockIn.Reset(fakeEntropy) - err = runMnemonicCmd(cmdUser, []string{}) - require.Error(t, err) + require.Error(t, cmd.Execute()) // Now provide "good" entropy but say no fakeEntropy = strings.Repeat(":)", 40) + "\nn\n" // entropy + accept count mockIn.Reset(fakeEntropy) - err = runMnemonicCmd(cmdUser, []string{}) - require.NoError(t, err) + require.NoError(t, cmd.Execute()) } diff --git a/client/keys/parse.go b/client/keys/parse.go index 79ccca739f0a..2af792c03c8e 100644 --- a/client/keys/parse.go +++ b/client/keys/parse.go @@ -1,30 +1,30 @@ package keys import ( + "context" "encoding/hex" "errors" "fmt" + "io" "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/libs/bech32" "github.com/tendermint/tendermint/libs/cli" + yaml "gopkg.in/yaml.v2" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" ) -var config = sdk.GetConfig() -var bech32Prefixes = []string{ - config.GetBech32AccountAddrPrefix(), - config.GetBech32AccountPubPrefix(), - config.GetBech32ValidatorAddrPrefix(), - config.GetBech32ValidatorPubPrefix(), - config.GetBech32ConsensusAddrPrefix(), - config.GetBech32ConsensusPubPrefix(), +func bech32Prefixes(config *sdk.Config) []string { + return []string{ + config.GetBech32AccountAddrPrefix(), + config.GetBech32AccountPubPrefix(), + config.GetBech32ValidatorAddrPrefix(), + config.GetBech32ValidatorPubPrefix(), + config.GetBech32ConsensusAddrPrefix(), + config.GetBech32ConsensusPubPrefix(), + } } type hexOutput struct { @@ -44,13 +44,16 @@ type bech32Output struct { Formats []string `json:"formats"` } -func newBech32Output(bs []byte) bech32Output { +func newBech32Output(config *sdk.Config, bs []byte) bech32Output { + bech32Prefixes := bech32Prefixes(config) out := bech32Output{Formats: make([]string, len(bech32Prefixes))} + for i, prefix := range bech32Prefixes { bech32Addr, err := bech32.ConvertAndEncode(prefix, bs) if err != nil { panic(err) } + out.Formats[i] = bech32Addr } @@ -78,63 +81,72 @@ hexadecimal into bech32 cosmos prefixed format and vice versa. Args: cobra.ExactArgs(1), RunE: parseKey, } - cmd.Flags().Bool(flags.FlagIndentResponse, false, "Indent JSON output") return cmd } -func parseKey(_ *cobra.Command, args []string) error { +func parseKey(cmd *cobra.Command, args []string) error { + config, _ := sdk.GetSealedConfig(context.Background()) + return doParseKey(cmd, config, args) +} + +func doParseKey(cmd *cobra.Command, config *sdk.Config, args []string) error { addr := strings.TrimSpace(args[0]) + outstream := cmd.OutOrStdout() + if len(addr) == 0 { return errors.New("couldn't parse empty input") } - if !(runFromBech32(addr) || runFromHex(addr)) { + + output, _ := cmd.Flags().GetString(cli.OutputFlag) + if !(runFromBech32(outstream, addr, output) || runFromHex(config, outstream, addr, output)) { return errors.New("couldn't find valid bech32 nor hex data") } + return nil } // print info from bech32 -func runFromBech32(bech32str string) bool { +func runFromBech32(w io.Writer, bech32str, output string) bool { hrp, bz, err := bech32.DecodeAndConvert(bech32str) if err != nil { return false } - displayParseKeyInfo(newHexOutput(hrp, bz)) + + displayParseKeyInfo(w, newHexOutput(hrp, bz), output) + return true } // print info from hex -func runFromHex(hexstr string) bool { +func runFromHex(config *sdk.Config, w io.Writer, hexstr, output string) bool { bz, err := hex.DecodeString(hexstr) if err != nil { return false } - displayParseKeyInfo(newBech32Output(bz)) + + displayParseKeyInfo(w, newBech32Output(config, bz), output) + return true } -func displayParseKeyInfo(stringer fmt.Stringer) { - var out []byte - var err error +func displayParseKeyInfo(w io.Writer, stringer fmt.Stringer, output string) { + var ( + err error + out []byte + ) - switch viper.Get(cli.OutputFlag) { + switch output { case OutputFormatText: out, err = yaml.Marshal(&stringer) case OutputFormatJSON: - - if viper.GetBool(flags.FlagIndentResponse) { - out, err = KeysCdc.MarshalJSONIndent(stringer, "", " ") - } else { - out = KeysCdc.MustMarshalJSON(stringer) - } - + out, err = KeysCdc.MarshalJSON(stringer) } if err != nil { panic(err) } - fmt.Println(string(out)) + _, _ = fmt.Fprintln(w, string(out)) } diff --git a/client/keys/parse_test.go b/client/keys/parse_test.go index f6080e795fe3..687922db3c6c 100644 --- a/client/keys/parse_test.go +++ b/client/keys/parse_test.go @@ -4,12 +4,16 @@ import ( "testing" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestParseKey(t *testing.T) { - bech32str := "fetch104ytdpvrx9284zd50v9ep8c6j7pua7dk9j04mp" + bech32str := "cosmos104ytdpvrx9284zd50v9ep8c6j7pua7dkk0x3ek" hexstr := "EB5AE9872103497EC092EF901027049E4F39200C60040D3562CD7F104A39F62E6E5A39A818F4" + config := sdk.NewConfig() + tests := []struct { name string args []string @@ -23,7 +27,7 @@ func TestParseKey(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.wantErr, parseKey(nil, tt.args) != nil) + require.Equal(t, tt.wantErr, doParseKey(ParseKeyStringCommand(), config, tt.args) != nil) }) } } diff --git a/client/keys/root.go b/client/keys/root.go index b0121eb2c07c..92c78c3abeab 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -2,23 +2,41 @@ package keys import ( "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client/flags" ) // Commands registers a sub-tree of commands to interact with // local private key storage. -func Commands() *cobra.Command { +func Commands(defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "keys", - Short: "Add or view local private keys", - Long: `Keys allows you to manage your local keystore for tendermint. + Short: "Manage your application's keys", + Long: `Keyring management commands. These keys may be in any format supported by the +Tendermint crypto library and can be used by light-clients, full nodes, or any other application +that needs to sign with a private key. - These keys may be in any format supported by go-crypto and can be - used by light-clients, full nodes, or any other application that - needs to sign with a private key.`, +The keyring supports the following backends: + + os Uses the operating system's default credentials store. + file Uses encrypted file-based keystore within the app's configuration directory. + This keyring will request a password each time it is accessed, which may occur + multiple times in a single command resulting in repeated password prompts. + kwallet Uses KDE Wallet Manager as a credentials management application. + pass Uses the pass command line utility to store and retrieve keys. + test Stores keys insecurely to disk. It does not prompt for a password to be unlocked + and it should be use only for testing purposes. + +kwallet and pass backends depend on external tools. Refer to their respective documentation for more +information: + KWallet https://github.com/KDE/kwallet + pass https://www.passwordstore.org/ + +The pass backend requires GnuPG: https://gnupg.org/ +`, } + cmd.AddCommand( MnemonicKeyCommand(), AddKeyCommand(), @@ -28,13 +46,14 @@ func Commands() *cobra.Command { ShowKeysCmd(), flags.LineBreak, DeleteKeyCommand(), - UpdateKeyCommand(), ParseKeyStringCommand(), MigrateCommand(), - flags.LineBreak, - AttestationCmd(), ) + + cmd.PersistentFlags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.PersistentFlags().String(flags.FlagKeyringDir, "", "The client Keyring directory; if omitted, the default 'home' directory will be used") cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") - viper.BindPFlag(flags.FlagKeyringBackend, cmd.Flags().Lookup(flags.FlagKeyringBackend)) + cmd.PersistentFlags().String(cli.OutputFlag, "text", "Output format (text|json)") + return cmd } diff --git a/client/keys/root_test.go b/client/keys/root_test.go index 2071ff8d5eba..f66ae9265de6 100644 --- a/client/keys/root_test.go +++ b/client/keys/root_test.go @@ -1,25 +1,15 @@ package keys import ( - "os" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/assert" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" ) func TestCommands(t *testing.T) { - rootCommands := Commands() + rootCommands := Commands("home") assert.NotNil(t, rootCommands) // Commands are registered - assert.Equal(t, 13, len(rootCommands.Commands())) -} - -func TestMain(m *testing.M) { - viper.Set(flags.FlagKeyringBackend, keys.BackendTest) - os.Exit(m.Run()) + assert.Equal(t, 10, len(rootCommands.Commands())) } diff --git a/client/keys/show.go b/client/keys/show.go index 539fb111e55f..614a99b9da42 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -5,15 +5,13 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" - - tmcrypto "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/ledger" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -35,10 +33,10 @@ const ( // ShowKeysCmd shows key information for a given key name. func ShowKeysCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "show [name [name...]]", - Short: "Show key info for the given name", - Long: `Return public details of a single local key. If multiple names are -provided, then an ephemeral multisig key will be created under the name "multi" + Use: "show [name_or_address [name_or_address...]]", + Short: "Retrieve key information by name or address", + Long: `Display keys details. If multiple names or addresses are provided, +then an ephemeral multisig key will be created under the name "multi" consisting of all the keys provided by name and multisig threshold.`, Args: cobra.MinimumNArgs(1), RunE: runShowCmd, @@ -48,48 +46,50 @@ consisting of all the keys provided by name and multisig threshold.`, cmd.Flags().BoolP(FlagAddress, "a", false, "Output the address only (overrides --output)") cmd.Flags().BoolP(FlagPublicKey, "p", false, "Output the public key only (overrides --output)") cmd.Flags().BoolP(FlagDevice, "d", false, "Output the address in a ledger device") - cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures") - cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") + cmd.Flags().Int(flagMultiSigThreshold, 1, "K out of N required signatures") return cmd } func runShowCmd(cmd *cobra.Command, args []string) (err error) { - var info keys.Info - - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), cmd.InOrStdin()) + var info keyring.Info + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + if len(args) == 1 { - info, err = kb.Get(args[0]) + info, err = fetchKey(clientCtx.Keyring, args[0]) if err != nil { - return err + return fmt.Errorf("%s is not a valid name or address: %v", args[0], err) + } + if info.GetType() == keyring.TypeMulti { + info = keyring.NewMultiInfo(info.GetName(), info.GetPubKey()) } } else { - pks := make([]tmcrypto.PubKey, len(args)) - for i, keyName := range args { - info, err := kb.Get(keyName) + pks := make([]cryptotypes.PubKey, len(args)) + for i, keyref := range args { + info, err := fetchKey(clientCtx.Keyring, keyref) if err != nil { - return err + return fmt.Errorf("%s is not a valid name or address: %v", keyref, err) } pks[i] = info.GetPubKey() } - multisigThreshold := viper.GetInt(flagMultiSigThreshold) + multisigThreshold, _ := cmd.Flags().GetInt(flagMultiSigThreshold) err = validateMultisigThreshold(multisigThreshold, len(args)) if err != nil { return err } - multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) - info = keys.NewMultiInfo(defaultMultiSigKeyName, multikey) + multikey := multisig.NewLegacyAminoPubKey(multisigThreshold, pks) + info = keyring.NewMultiInfo(defaultMultiSigKeyName, multikey) } - isShowAddr := viper.GetBool(FlagAddress) - isShowPubKey := viper.GetBool(FlagPublicKey) - isShowDevice := viper.GetBool(FlagDevice) + isShowAddr, _ := cmd.Flags().GetBool(FlagAddress) + isShowPubKey, _ := cmd.Flags().GetBool(FlagPublicKey) + isShowDevice, _ := cmd.Flags().GetBool(FlagDevice) isOutputSet := false tmp := cmd.Flag(cli.OutputFlag) @@ -105,29 +105,35 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) { return errors.New("cannot use --output with --address or --pubkey") } - bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix)) + bechPrefix, _ := cmd.Flags().GetString(FlagBechPrefix) + bechKeyOut, err := getBechKeyOut(bechPrefix) if err != nil { return err } + if isOutputSet { + clientCtx.OutputFormat, _ = cmd.Flags().GetString(cli.OutputFlag) + } + switch { case isShowAddr: - printKeyAddress(info, bechKeyOut) + printKeyAddress(cmd.OutOrStdout(), info, bechKeyOut) case isShowPubKey: - printPubKey(info, bechKeyOut) + printPubKey(cmd.OutOrStdout(), info, bechKeyOut) default: - printKeyInfo(info, bechKeyOut) + printKeyInfo(cmd.OutOrStdout(), info, bechKeyOut, clientCtx.OutputFormat) } if isShowDevice { if isShowPubKey { return fmt.Errorf("the device flag (-d) can only be used for addresses not pubkeys") } - if viper.GetString(FlagBechPrefix) != "acc" { + if bechPrefix != "acc" { return fmt.Errorf("the device flag (-d) can only be used for accounts") } + // Override and show in the device - if info.GetType() != keys.TypeLedger { + if info.GetType() != keyring.TypeLedger { return fmt.Errorf("the device flag (-d) can only be used for accounts stored in devices") } @@ -136,12 +142,28 @@ func runShowCmd(cmd *cobra.Command, args []string) (err error) { return nil } - return crypto.LedgerShowAddress(*hdpath, info.GetPubKey()) + return ledger.ShowAddress(*hdpath, info.GetPubKey(), sdk.GetConfig().GetBech32AccountAddrPrefix()) } return nil } +func fetchKey(kb keyring.Keyring, keyref string) (keyring.Info, error) { + info, err := kb.Key(keyref) + if err != nil { + accAddr, err := sdk.AccAddressFromBech32(keyref) + if err != nil { + return info, err + } + + info, err = kb.KeyByAddress(accAddr) + if err != nil { + return info, errors.New("key not found") + } + } + return info, nil +} + func validateMultisigThreshold(k, nKeys int) error { if k <= 0 { return fmt.Errorf("threshold must be a positive integer") @@ -156,11 +178,11 @@ func validateMultisigThreshold(k, nKeys int) error { func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) { switch bechPrefix { case sdk.PrefixAccount: - return keys.Bech32KeyOutput, nil + return keyring.Bech32KeyOutput, nil case sdk.PrefixValidator: - return keys.Bech32ValKeyOutput, nil + return keyring.Bech32ValKeyOutput, nil case sdk.PrefixConsensus: - return keys.Bech32ConsKeyOutput, nil + return keyring.Bech32ConsKeyOutput, nil } return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix) diff --git a/client/keys/show_test.go b/client/keys/show_test.go index f0685eeef10e..7f43450b81c2 100644 --- a/client/keys/show_test.go +++ b/client/keys/show_test.go @@ -1,30 +1,38 @@ package keys import ( + "context" + "fmt" + "strings" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" - + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" sdk "github.com/cosmos/cosmos-sdk/types" + tmcli "github.com/tendermint/tendermint/libs/cli" ) func Test_multiSigKey_Properties(t *testing.T) { - tmpKey1 := secp256k1.GenPrivKeySecp256k1([]byte("mySecret")) - pk := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey1.PubKey()}) - tmp := keys.NewMultiInfo("myMultisig", pk) + tmpKey1 := secp256k1.GenPrivKeyFromSecret([]byte("mySecret")) + pk := multisig.NewLegacyAminoPubKey( + 1, + []cryptotypes.PubKey{tmpKey1.PubKey()}, + ) + tmp := keyring.NewMultiInfo("myMultisig", pk) require.Equal(t, "myMultisig", tmp.GetName()) - require.Equal(t, keys.TypeMulti, tmp.GetType()) + require.Equal(t, keyring.TypeMulti, tmp.GetType()) require.Equal(t, "D3923267FA8A3DD367BB768FA8BDC8FF7F89DA3F", tmp.GetPubKey().Address().String()) - require.Equal(t, "fetch16wfryel63g7axeamw68630wglalcnk3lul4e00", tmp.GetAddress().String()) + require.Equal(t, "cosmos16wfryel63g7axeamw68630wglalcnk3l0zuadc", sdk.MustBech32ifyAddressBytes("cosmos", tmp.GetAddress())) } func Test_showKeysCmd(t *testing.T) { @@ -34,93 +42,184 @@ func Test_showKeysCmd(t *testing.T) { require.Equal(t, "false", cmd.Flag(FlagPublicKey).DefValue) } -func Test_runShowCmd(t *testing.T) { - runningUnattended := isRunningUnattended() +func TestShowCmdWithMultisigAccount(t *testing.T) { cmd := ShowKeysCmd() - mockIn, _, _ := tests.ApplyMockIO(cmd) - require.EqualError(t, runShowCmd(cmd, []string{"invalid"}), "The specified item could not be found in the keyring") - require.EqualError(t, runShowCmd(cmd, []string{"invalid1", "invalid2"}), "The specified item could not be found in the keyring") + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + kbHome := t.TempDir() + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) + require.NoError(t, err) - // Prepare a key base - // Now add a temporary keybase - kbHome, cleanUp := tests.NewTestCaseDir(t) - defer cleanUp() - viper.Set(flags.FlagHome, kbHome) + clientCtx := client.Context{}.WithKeyring(kb) fakeKeyName1 := "runShowCmd_Key1" fakeKeyName2 := "runShowCmd_Key2" - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), mockIn) + myMultiSig := "mymulti" + threshold := 2 + + t.Cleanup(func() { + kb.Delete(fakeKeyName1) + kb.Delete(fakeKeyName2) + kb.Delete(myMultiSig) + }) + + path := hd.NewFundraiserParams(1, sdk.CoinType, 0).String() + acc1, err := kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1) require.NoError(t, err) - defer func() { - kb.Delete("runShowCmd_Key1", "", false) - kb.Delete("runShowCmd_Key2", "", false) - }() - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1) + + path2 := hd.NewFundraiserParams(1, sdk.CoinType, 1).String() + acc2, err := kb.NewAccount(fakeKeyName2, testutil.TestMnemonic, "", path2, hd.Secp256k1) + require.NoError(t, err) + + var pks []cryptotypes.PubKey + pks = append(pks, acc1.GetPubKey(), acc2.GetPubKey()) + + pk := multisig.NewLegacyAminoPubKey(threshold, pks) + multiSig, err := kb.SaveMultisig(myMultiSig, pk) + require.NoError(t, err) + + multiSigInfo, err := keyring.Bech32KeyOutput(multiSig) + require.NoError(t, err) + + multiSigInfoBytes, err := KeysCdc.Amino.MarshalJSON(multiSigInfo) require.NoError(t, err) - if runningUnattended { - mockIn.Reset("testpass1\n") + args := []string{ + myMultiSig, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", FlagBechPrefix, sdk.PrefixAccount), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), } - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1) + + var res keyring.KeyOutput + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + require.NoError(t, err) + + KeysCdc.Amino.UnmarshalJSON(out.Bytes(), &res) + require.Equal(t, res.Threshold, uint(threshold)) + require.Len(t, res.PubKeys, 2) + require.Equal(t, strings.TrimSpace(out.String()), string(multiSigInfoBytes)) +} + +func Test_runShowCmd(t *testing.T) { + cmd := ShowKeysCmd() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + kbHome := t.TempDir() + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) + require.NoError(t, err) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + cmd.SetArgs([]string{"invalid"}) + require.EqualError(t, cmd.ExecuteContext(ctx), "invalid is not a valid name or address: decoding bech32 failed: invalid bech32 string length 7") + + cmd.SetArgs([]string{"invalid1", "invalid2"}) + require.EqualError(t, cmd.ExecuteContext(ctx), "invalid1 is not a valid name or address: decoding bech32 failed: invalid index of 1") + + fakeKeyName1 := "runShowCmd_Key1" + fakeKeyName2 := "runShowCmd_Key2" + + t.Cleanup(func() { + kb.Delete("runShowCmd_Key1") + kb.Delete("runShowCmd_Key2") + }) + + path := hd.NewFundraiserParams(1, sdk.CoinType, 0).String() + _, err = kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1) + require.NoError(t, err) + + path2 := hd.NewFundraiserParams(1, sdk.CoinType, 1).String() + _, err = kb.NewAccount(fakeKeyName2, testutil.TestMnemonic, "", path2, hd.Secp256k1) require.NoError(t, err) // Now try single key - if runningUnattended { - mockIn.Reset("testpass1\n") - } - require.EqualError(t, runShowCmd(cmd, []string{fakeKeyName1}), "invalid Bech32 prefix encoding provided: ") + cmd.SetArgs([]string{ + fakeKeyName1, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=", FlagBechPrefix), + }) + require.EqualError(t, cmd.ExecuteContext(ctx), "invalid Bech32 prefix encoding provided: ") - // Now try single key - set bech to acc - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - if runningUnattended { - mockIn.Reset("testpass1\n") - } - require.NoError(t, runShowCmd(cmd, []string{fakeKeyName1})) + cmd.SetArgs([]string{ + fakeKeyName1, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", FlagBechPrefix, sdk.PrefixAccount), + }) - // Now try multisig key - set bech to acc - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - require.EqualError(t, runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}), "threshold must be a positive integer") + // try fetch by name + require.NoError(t, cmd.ExecuteContext(ctx)) + + // try fetch by addr + info, err := kb.Key(fakeKeyName1) + cmd.SetArgs([]string{ + info.GetAddress().String(), + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", FlagBechPrefix, sdk.PrefixAccount), + }) - // Now try multisig key - set bech to acc + threshold=2 - viper.Set(FlagBechPrefix, sdk.PrefixAccount) - viper.Set(flagMultiSigThreshold, 2) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) require.NoError(t, err) + require.NoError(t, cmd.ExecuteContext(ctx)) - // Now try multisig key - set bech to acc + threshold=2 - viper.Set(FlagBechPrefix, "acc") - viper.Set(FlagDevice, true) - viper.Set(flagMultiSigThreshold, 2) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - require.EqualError(t, err, "the device flag (-d) can only be used for accounts stored in devices") + // Now try multisig key - set bech to acc + cmd.SetArgs([]string{ + fakeKeyName1, fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", FlagBechPrefix, sdk.PrefixAccount), + fmt.Sprintf("--%s=0", flagMultiSigThreshold), + }) + require.EqualError(t, cmd.ExecuteContext(ctx), "threshold must be a positive integer") - viper.Set(FlagBechPrefix, "val") - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - require.EqualError(t, err, "the device flag (-d) can only be used for accounts") + cmd.SetArgs([]string{ + fakeKeyName1, fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", FlagBechPrefix, sdk.PrefixAccount), + fmt.Sprintf("--%s=2", flagMultiSigThreshold), + }) + require.NoError(t, cmd.ExecuteContext(ctx)) - viper.Set(FlagPublicKey, true) - if runningUnattended { - mockIn.Reset("testpass1\ntestpass1\n") - } - err = runShowCmd(cmd, []string{fakeKeyName1, fakeKeyName2}) - require.EqualError(t, err, "the device flag (-d) can only be used for addresses not pubkeys") + // Now try multisig key - set bech to acc + threshold=2 + cmd.SetArgs([]string{ + fakeKeyName1, fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=acc", FlagBechPrefix), + fmt.Sprintf("--%s=true", FlagDevice), + fmt.Sprintf("--%s=2", flagMultiSigThreshold), + }) + require.EqualError(t, cmd.ExecuteContext(ctx), "the device flag (-d) can only be used for accounts stored in devices") + + cmd.SetArgs([]string{ + fakeKeyName1, fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=val", FlagBechPrefix), + fmt.Sprintf("--%s=true", FlagDevice), + fmt.Sprintf("--%s=2", flagMultiSigThreshold), + }) + require.EqualError(t, cmd.ExecuteContext(ctx), "the device flag (-d) can only be used for accounts") - // TODO: Capture stdout and compare + cmd.SetArgs([]string{ + fakeKeyName1, fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=val", FlagBechPrefix), + fmt.Sprintf("--%s=true", FlagDevice), + fmt.Sprintf("--%s=2", flagMultiSigThreshold), + fmt.Sprintf("--%s=true", FlagPublicKey), + }) + require.EqualError(t, cmd.ExecuteContext(ctx), "the device flag (-d) can only be used for addresses not pubkeys") } func Test_validateMultisigThreshold(t *testing.T) { @@ -161,9 +260,9 @@ func Test_getBechKeyOut(t *testing.T) { }{ {"empty", args{""}, nil, true}, {"wrong", args{"???"}, nil, true}, - {"acc", args{sdk.PrefixAccount}, keys.Bech32KeyOutput, false}, - {"val", args{sdk.PrefixValidator}, keys.Bech32ValKeyOutput, false}, - {"cons", args{sdk.PrefixConsensus}, keys.Bech32ConsKeyOutput, false}, + {"acc", args{sdk.PrefixAccount}, keyring.Bech32KeyOutput, false}, + {"val", args{sdk.PrefixValidator}, keyring.Bech32ValKeyOutput, false}, + {"cons", args{sdk.PrefixConsensus}, keyring.Bech32ConsKeyOutput, false}, } for _, tt := range tests { tt := tt diff --git a/client/keys/testdata/keys/keys.db/000136.ldb b/client/keys/testdata/keys/keys.db/000136.ldb new file mode 100644 index 000000000000..58165a9ab136 Binary files /dev/null and b/client/keys/testdata/keys/keys.db/000136.ldb differ diff --git a/client/keys/testdata/keys/keys.db/000137.ldb b/client/keys/testdata/keys/keys.db/000137.ldb new file mode 100644 index 000000000000..340e51356e83 Binary files /dev/null and b/client/keys/testdata/keys/keys.db/000137.ldb differ diff --git a/client/keys/testdata/keys/keys.db/CURRENT b/client/keys/testdata/keys/keys.db/CURRENT new file mode 100644 index 000000000000..c5ea6dc2c1e0 --- /dev/null +++ b/client/keys/testdata/keys/keys.db/CURRENT @@ -0,0 +1 @@ +MANIFEST-000167 diff --git a/client/keys/testdata/keys/keys.db/CURRENT.bak b/client/keys/testdata/keys/keys.db/CURRENT.bak new file mode 100644 index 000000000000..6da0b2c731cc --- /dev/null +++ b/client/keys/testdata/keys/keys.db/CURRENT.bak @@ -0,0 +1 @@ +MANIFEST-000165 diff --git a/client/keys/testdata/keys/keys.db/LOCK b/client/keys/testdata/keys/keys.db/LOCK new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/client/keys/testdata/keys/keys.db/LOG b/client/keys/testdata/keys/keys.db/LOG new file mode 100644 index 000000000000..c42df220de73 --- /dev/null +++ b/client/keys/testdata/keys/keys.db/LOG @@ -0,0 +1,876 @@ +=============== Sep 12, 2020 (BST) =============== +14:56:38.444867 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:56:38.447630 db@open opening +14:56:38.447826 version@stat F·[] S·0B[] Sc·[] +14:56:38.449162 db@janitor F·2 G·0 +14:56:38.449180 db@open done T·1.537964ms +14:56:38.449193 db@close closing +14:56:38.449264 db@close done T·69.313µs +=============== Sep 12, 2020 (BST) =============== +14:56:49.081871 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:56:49.081975 version@stat F·[] S·0B[] Sc·[] +14:56:49.081994 db@open opening +14:56:49.082040 journal@recovery F·1 +14:56:49.082399 journal@recovery recovering @1 +14:56:49.083134 version@stat F·[] S·0B[] Sc·[] +14:56:49.088411 db@janitor F·2 G·0 +14:56:49.088430 db@open done T·6.428462ms +14:56:49.088440 db@close closing +14:56:49.088491 db@close done T·48.589µs +=============== Sep 12, 2020 (BST) =============== +14:56:55.214003 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:56:55.214144 version@stat F·[] S·0B[] Sc·[] +14:56:55.214165 db@open opening +14:56:55.214215 journal@recovery F·1 +14:56:55.214329 journal@recovery recovering @2 +14:56:55.214750 version@stat F·[] S·0B[] Sc·[] +14:56:55.221347 db@janitor F·2 G·0 +14:56:55.221365 db@open done T·7.194565ms +14:56:55.608587 db@close closing +14:56:55.608644 db@close done T·54.685µs +=============== Sep 12, 2020 (BST) =============== +14:57:07.211101 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:07.211224 version@stat F·[] S·0B[] Sc·[] +14:57:07.211243 db@open opening +14:57:07.211287 journal@recovery F·1 +14:57:07.211388 journal@recovery recovering @4 +14:57:07.213734 memdb@flush created L0@6 N·2 S·470B "cos..ess,v2":"val..nfo,v1" +14:57:07.214142 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:07.218723 db@janitor F·3 G·0 +14:57:07.218743 db@open done T·7.488657ms +14:57:07.218804 db@close closing +14:57:07.218842 db@close done T·36.603µs +=============== Sep 12, 2020 (BST) =============== +14:57:16.418006 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:16.418133 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.418153 db@open opening +14:57:16.418199 journal@recovery F·1 +14:57:16.418508 journal@recovery recovering @7 +14:57:16.418891 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.425395 db@janitor F·3 G·0 +14:57:16.425423 db@open done T·7.257565ms +14:57:16.425482 db@close closing +14:57:16.425522 db@close done T·38.172µs +=============== Sep 12, 2020 (BST) =============== +14:57:16.425854 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:16.425965 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.425983 db@open opening +14:57:16.426027 journal@recovery F·1 +14:57:16.426133 journal@recovery recovering @9 +14:57:16.426324 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.431088 db@janitor F·3 G·0 +14:57:16.431103 db@open done T·5.115335ms +14:57:16.431142 db@close closing +14:57:16.431179 db@close done T·35.705µs +=============== Sep 12, 2020 (BST) =============== +14:57:16.431287 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:16.431376 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.431394 db@open opening +14:57:16.431437 journal@recovery F·1 +14:57:16.431721 journal@recovery recovering @11 +14:57:16.432205 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.437468 db@janitor F·3 G·0 +14:57:16.437486 db@open done T·6.087128ms +14:57:16.437529 db@close closing +14:57:16.437571 db@close done T·40.188µs +=============== Sep 12, 2020 (BST) =============== +14:57:16.437907 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:16.438006 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.438024 db@open opening +14:57:16.438067 journal@recovery F·1 +14:57:16.438573 journal@recovery recovering @13 +14:57:16.439155 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.443451 db@janitor F·3 G·0 +14:57:16.443466 db@open done T·5.437579ms +14:57:16.443511 db@close closing +14:57:16.443634 db@close done T·118.642µs +=============== Sep 12, 2020 (BST) =============== +14:57:16.443733 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:16.443847 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.443864 db@open opening +14:57:16.443915 journal@recovery F·1 +14:57:16.444629 journal@recovery recovering @15 +14:57:16.445570 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:16.450978 db@janitor F·3 G·0 +14:57:16.451001 db@open done T·7.132193ms +14:57:16.451050 db@close closing +14:57:16.451089 db@close done T·37.371µs +=============== Sep 12, 2020 (BST) =============== +14:57:19.439656 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:57:19.439775 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:19.439793 db@open opening +14:57:19.439845 journal@recovery F·1 +14:57:19.440199 journal@recovery recovering @17 +14:57:19.440624 version@stat F·[1] S·470B[470B] Sc·[0.25] +14:57:19.445819 db@janitor F·3 G·0 +14:57:19.445837 db@open done T·6.03822ms +14:57:19.828985 db@close closing +14:57:19.829058 db@close done T·71.028µs +=============== Sep 12, 2020 (BST) =============== +15:07:04.002859 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:04.002990 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:04.003010 db@open opening +15:07:04.003081 journal@recovery F·1 +15:07:04.003191 journal@recovery recovering @19 +15:07:04.003591 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:04.008917 db@janitor F·3 G·0 +15:07:04.008942 db@open done T·5.916433ms +15:07:04.009005 db@close closing +15:07:04.009050 db@close done T·42.762µs +=============== Sep 12, 2020 (BST) =============== +15:07:15.240666 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:15.240802 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.240825 db@open opening +15:07:15.240871 journal@recovery F·1 +15:07:15.241288 journal@recovery recovering @21 +15:07:15.241702 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.249270 db@janitor F·3 G·0 +15:07:15.249299 db@open done T·8.459432ms +15:07:15.249363 db@close closing +15:07:15.249404 db@close done T·39.294µs +=============== Sep 12, 2020 (BST) =============== +15:07:15.249761 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:15.249850 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.249868 db@open opening +15:07:15.249911 journal@recovery F·1 +15:07:15.250026 journal@recovery recovering @23 +15:07:15.250195 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.254923 db@janitor F·3 G·0 +15:07:15.254943 db@open done T·5.069716ms +15:07:15.254987 db@close closing +15:07:15.255026 db@close done T·37.365µs +=============== Sep 12, 2020 (BST) =============== +15:07:15.255136 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:15.255218 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.255235 db@open opening +15:07:15.255277 journal@recovery F·1 +15:07:15.255617 journal@recovery recovering @25 +15:07:15.256091 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.262240 db@janitor F·3 G·0 +15:07:15.262260 db@open done T·7.018813ms +15:07:15.262310 db@close closing +15:07:15.262353 db@close done T·41.276µs +=============== Sep 12, 2020 (BST) =============== +15:07:15.262707 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:15.262808 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.262829 db@open opening +15:07:15.262874 journal@recovery F·1 +15:07:15.263408 journal@recovery recovering @27 +15:07:15.263994 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.268793 db@janitor F·3 G·0 +15:07:15.268810 db@open done T·5.975152ms +15:07:15.268861 db@close closing +15:07:15.268900 db@close done T·37.419µs +=============== Sep 12, 2020 (BST) =============== +15:07:15.268989 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:15.269096 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.269117 db@open opening +15:07:15.269165 journal@recovery F·1 +15:07:15.269858 journal@recovery recovering @29 +15:07:15.270587 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:15.275935 db@janitor F·3 G·0 +15:07:15.275951 db@open done T·6.828156ms +15:07:15.275999 db@close closing +15:07:15.276033 db@close done T·32.757µs +=============== Sep 12, 2020 (BST) =============== +15:07:21.660414 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:21.660547 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.660568 db@open opening +15:07:21.660655 journal@recovery F·1 +15:07:21.660960 journal@recovery recovering @31 +15:07:21.661682 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.667796 db@janitor F·3 G·0 +15:07:21.667813 db@open done T·7.237366ms +15:07:21.667869 db@close closing +15:07:21.667914 db@close done T·43.496µs +=============== Sep 12, 2020 (BST) =============== +15:07:21.668253 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:21.668354 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.668372 db@open opening +15:07:21.668418 journal@recovery F·1 +15:07:21.668529 journal@recovery recovering @33 +15:07:21.668930 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.674796 db@janitor F·3 G·0 +15:07:21.674817 db@open done T·6.440491ms +15:07:21.674861 db@close closing +15:07:21.674898 db@close done T·35.584µs +=============== Sep 12, 2020 (BST) =============== +15:07:21.675013 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:21.675115 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.675131 db@open opening +15:07:21.675179 journal@recovery F·1 +15:07:21.675707 journal@recovery recovering @35 +15:07:21.676833 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.681212 db@janitor F·3 G·0 +15:07:21.681226 db@open done T·6.089677ms +15:07:21.681270 db@close closing +15:07:21.681299 db@close done T·27.867µs +=============== Sep 12, 2020 (BST) =============== +15:07:21.681691 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:21.681799 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.681817 db@open opening +15:07:21.681882 journal@recovery F·1 +15:07:21.683119 journal@recovery recovering @37 +15:07:21.684000 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.689926 db@janitor F·3 G·0 +15:07:21.689940 db@open done T·8.117662ms +15:07:21.689984 db@close closing +15:07:21.690027 db@close done T·42.379µs +=============== Sep 12, 2020 (BST) =============== +15:07:21.690104 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:21.690189 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.690205 db@open opening +15:07:21.690247 journal@recovery F·1 +15:07:21.690536 journal@recovery recovering @39 +15:07:21.690899 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:21.695207 db@janitor F·3 G·0 +15:07:21.695223 db@open done T·5.013121ms +15:07:21.695265 db@close closing +15:07:21.695320 db@close done T·53.965µs +=============== Sep 12, 2020 (BST) =============== +15:07:24.335083 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:07:24.335214 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:24.335233 db@open opening +15:07:24.335282 journal@recovery F·1 +15:07:24.336367 journal@recovery recovering @41 +15:07:24.336786 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:07:24.342965 db@janitor F·3 G·0 +15:07:24.342984 db@open done T·7.745647ms +15:07:24.725175 db@close closing +15:07:24.725234 db@close done T·57.895µs +=============== Nov 2, 2020 (GMT) =============== +00:08:43.299526 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +00:08:43.299860 version@stat F·[1] S·470B[470B] Sc·[0.25] +00:08:43.299875 db@open opening +00:08:43.299900 journal@recovery F·1 +00:08:43.300467 journal@recovery recovering @43 +00:08:43.301378 version@stat F·[1] S·470B[470B] Sc·[0.25] +00:08:43.307882 db@janitor F·3 G·0 +00:08:43.307911 db@open done T·8.03178ms +00:08:43.308144 db@close closing +00:08:43.308231 db@close done T·85.824µs +=============== Nov 2, 2020 (GMT) =============== +00:09:14.493119 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +00:09:14.493237 version@stat F·[1] S·470B[470B] Sc·[0.25] +00:09:14.493272 db@open opening +00:09:14.493296 journal@recovery F·1 +00:09:14.493370 journal@recovery recovering @45 +00:09:14.493648 version@stat F·[1] S·470B[470B] Sc·[0.25] +00:09:14.499436 db@janitor F·3 G·0 +00:09:14.499452 db@open done T·6.170984ms +00:09:14.499537 db@close closing +00:09:14.499592 db@close done T·52.707µs +=============== Jan 22, 2021 (GMT) =============== +12:47:15.935887 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:47:15.937333 version@stat F·[1] S·470B[470B] Sc·[0.25] +12:47:15.937343 db@open opening +12:47:15.937370 journal@recovery F·1 +12:47:15.937642 journal@recovery recovering @47 +12:47:15.937942 version@stat F·[1] S·470B[470B] Sc·[0.25] +12:47:15.944262 db@janitor F·3 G·0 +12:47:15.944270 db@open done T·6.922789ms +12:47:15.944460 db@close closing +12:47:15.944492 db@close done T·30.723µs +=============== Jan 22, 2021 (GMT) =============== +15:23:04.060521 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:23:04.060694 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:04.060708 db@open opening +15:23:04.060734 journal@recovery F·1 +15:23:04.061045 journal@recovery recovering @49 +15:23:04.061463 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:04.067352 db@janitor F·3 G·0 +15:23:04.067386 db@open done T·6.675171ms +15:23:11.819265 db@close closing +15:23:11.819317 db@close done T·51.057µs +=============== Jan 22, 2021 (GMT) =============== +15:23:14.037455 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:23:14.037524 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:14.037535 db@open opening +15:23:14.037560 journal@recovery F·1 +15:23:14.037629 journal@recovery recovering @51 +15:23:14.037951 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:14.045002 db@janitor F·3 G·0 +15:23:14.045020 db@open done T·7.475686ms +15:23:22.065063 db@close closing +15:23:22.065111 db@close done T·47.074µs +=============== Jan 22, 2021 (GMT) =============== +15:23:43.145956 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:23:43.146094 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:43.146107 db@open opening +15:23:43.146132 journal@recovery F·1 +15:23:43.146447 journal@recovery recovering @53 +15:23:43.146912 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:23:43.153059 db@janitor F·3 G·0 +15:23:43.153108 db@open done T·6.977141ms +15:23:43.153245 db@close closing +15:23:43.153290 db@close done T·43.663µs +=============== Jan 22, 2021 (GMT) =============== +15:25:14.027169 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:25:14.027240 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:14.027250 db@open opening +15:25:14.027274 journal@recovery F·1 +15:25:14.027627 journal@recovery recovering @55 +15:25:14.028059 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:14.033292 db@janitor F·3 G·0 +15:25:14.033304 db@open done T·6.047911ms +15:25:19.981971 db@close closing +15:25:19.982011 db@close done T·39.165µs +=============== Jan 22, 2021 (GMT) =============== +15:25:51.137523 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:25:51.138542 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:51.138553 db@open opening +15:25:51.138579 journal@recovery F·1 +15:25:51.138632 journal@recovery recovering @57 +15:25:51.138981 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:51.144970 db@janitor F·3 G·0 +15:25:51.144983 db@open done T·6.422769ms +15:25:51.145031 db@close closing +15:25:51.145071 db@close done T·39.108µs +=============== Jan 22, 2021 (GMT) =============== +15:25:56.504732 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:25:56.504809 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:56.504824 db@open opening +15:25:56.504872 journal@recovery F·1 +15:25:56.505474 journal@recovery recovering @59 +15:25:56.505571 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:25:56.512054 db@janitor F·3 G·0 +15:25:56.512061 db@open done T·7.232269ms +15:25:56.710823 db@close closing +15:25:56.710860 db@close done T·36.326µs +=============== Jan 22, 2021 (GMT) =============== +15:26:02.847640 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +15:26:02.847733 version@stat F·[1] S·470B[470B] Sc·[0.25] +15:26:02.847745 db@open opening +15:26:02.847771 journal@recovery F·1 +15:26:02.848002 journal@recovery recovering @61 +15:26:02.850382 memdb@flush created L0@63 N·2 S·472B "cos..ess,v5":"tes..nfo,v4" +15:26:02.850491 version@stat F·[2] S·942B[942B] Sc·[0.50] +15:26:02.854544 db@janitor F·4 G·0 +15:26:02.854552 db@open done T·6.802972ms +15:26:09.729296 db@close closing +15:26:09.729392 db@close done T·95.18µs +=============== Feb 6, 2021 (GMT) =============== +12:21:53.904083 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:21:53.904380 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:21:53.904391 db@open opening +12:21:53.904417 journal@recovery F·1 +12:21:53.905225 journal@recovery recovering @64 +12:21:53.905589 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:21:53.910965 db@janitor F·4 G·0 +12:21:53.910976 db@open done T·6.578518ms +12:21:53.911304 db@close closing +12:21:53.911387 db@close done T·82.205µs +=============== Feb 6, 2021 (GMT) =============== +12:22:02.353974 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:22:02.354077 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:22:02.354089 db@open opening +12:22:02.354116 journal@recovery F·1 +12:22:02.354419 journal@recovery recovering @66 +12:22:02.354608 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:22:02.359491 db@janitor F·4 G·0 +12:22:02.359504 db@open done T·5.408186ms +12:22:02.359514 db@close closing +12:22:02.359542 db@close done T·27.662µs +=============== Feb 6, 2021 (GMT) =============== +12:22:07.888198 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:22:07.888300 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:22:07.888310 db@open opening +12:22:07.888338 journal@recovery F·1 +12:22:07.888397 journal@recovery recovering @68 +12:22:07.888494 version@stat F·[2] S·942B[942B] Sc·[0.50] +12:22:07.895048 db@janitor F·4 G·0 +12:22:07.895060 db@open done T·6.746979ms +12:22:08.093013 db@close closing +12:22:08.093057 db@close done T·43.222µs +=============== Feb 18, 2021 (GMT) =============== +07:32:13.660053 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +07:32:13.661098 version@stat F·[2] S·942B[942B] Sc·[0.50] +07:32:13.661111 db@open opening +07:32:13.661140 journal@recovery F·1 +07:32:13.661439 journal@recovery recovering @70 +07:32:13.663498 memdb@flush created L0@72 N·2 S·465B "cia..nfo,v7":"cos..ess,v8" +07:32:13.663598 version@stat F·[3] S·1KiB[1KiB] Sc·[0.75] +07:32:13.668369 db@janitor F·5 G·0 +07:32:13.668400 db@open done T·7.285777ms +07:32:13.668491 db@close closing +07:32:13.668557 db@close done T·65.011µs +=============== Feb 18, 2021 (GMT) =============== +07:32:20.349460 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +07:32:20.349568 version@stat F·[3] S·1KiB[1KiB] Sc·[0.75] +07:32:20.349618 db@open opening +07:32:20.349691 journal@recovery F·1 +07:32:20.349769 journal@recovery recovering @73 +07:32:20.349867 version@stat F·[3] S·1KiB[1KiB] Sc·[0.75] +07:32:20.355997 db@janitor F·5 G·0 +07:32:20.356005 db@open done T·6.383828ms +07:32:20.553221 db@close closing +07:32:20.553251 db@close done T·28.713µs +=============== Feb 18, 2021 (GMT) =============== +07:32:30.022753 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +07:32:30.022830 version@stat F·[3] S·1KiB[1KiB] Sc·[0.75] +07:32:30.022842 db@open opening +07:32:30.022870 journal@recovery F·1 +07:32:30.023106 journal@recovery recovering @75 +07:32:30.025727 memdb@flush created L0@77 N·2 S·462B "cos..ess,v11":"foo.info,v10" +07:32:30.025896 version@stat F·[4] S·1KiB[1KiB] Sc·[1.00] +07:32:30.031203 db@janitor F·6 G·0 +07:32:30.031214 db@open done T·8.368455ms +07:32:30.031222 db@close closing +07:32:30.031249 db@close done T·26.625µs +=============== Feb 18, 2021 (GMT) =============== +07:32:36.137856 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +07:32:36.137945 version@stat F·[4] S·1KiB[1KiB] Sc·[1.00] +07:32:36.137955 db@open opening +07:32:36.137988 journal@recovery F·1 +07:32:36.138053 journal@recovery recovering @78 +07:32:36.138160 version@stat F·[4] S·1KiB[1KiB] Sc·[1.00] +07:32:36.144271 db@janitor F·6 G·0 +07:32:36.144281 db@open done T·6.322633ms +07:32:36.144342 table@compaction L0·4 -> L1·0 S·1KiB Q·12 +07:32:36.145937 table@build created L1@82 N·8 S·1KiB "cia..nfo,v7":"val..nfo,v1" +07:32:36.145957 version@stat F·[0 1] S·1KiB[0B 1KiB] Sc·[0.00 0.00] +07:32:36.147223 table@compaction committed F-3 S-606B Ke·0 D·0 T·2.864358ms +07:32:36.147251 table@remove removed @77 +07:32:36.147265 table@remove removed @72 +07:32:36.147280 table@remove removed @63 +07:32:36.147394 table@remove removed @6 +07:32:36.341754 db@close closing +07:32:36.341789 db@close done T·34.217µs +=============== Feb 23, 2021 (GMT) =============== +11:59:56.652297 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:59:56.653267 version@stat F·[0 1] S·1KiB[0B 1KiB] Sc·[0.00 0.00] +11:59:56.653279 db@open opening +11:59:56.653333 journal@recovery F·1 +11:59:56.653684 journal@recovery recovering @80 +11:59:56.655439 memdb@flush created L0@83 N·2 S·491B "bar.info,v13":"cos..ess,v14" +11:59:56.655563 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +11:59:56.659803 db@janitor F·4 G·0 +11:59:56.659812 db@open done T·6.529102ms +11:59:56.659952 db@close closing +11:59:56.660013 db@close done T·59.126µs +=============== Feb 23, 2021 (GMT) =============== +12:01:34.578182 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:01:34.578308 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +12:01:34.578348 db@open opening +12:01:34.578422 journal@recovery F·1 +12:01:34.578796 journal@recovery recovering @84 +12:01:34.579157 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +12:01:34.583888 db@janitor F·4 G·0 +12:01:34.583925 db@open done T·5.547338ms +12:01:34.583962 db@close closing +12:01:34.584011 db@close done T·46.636µs +=============== Feb 23, 2021 (GMT) =============== +12:01:34.584060 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:01:34.584136 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +12:01:34.584166 db@open opening +12:01:34.584195 journal@recovery F·1 +12:01:34.584799 journal@recovery recovering @86 +12:01:34.584896 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +12:01:34.590435 db@janitor F·4 G·0 +12:01:34.590445 db@open done T·6.275747ms +12:01:44.922399 db@close closing +12:01:44.922453 db@close done T·53.361µs +=============== Feb 23, 2021 (GMT) =============== +12:01:53.346191 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:01:53.346299 version@stat F·[1 1] S·1KiB[491B 1KiB] Sc·[0.25 0.00] +12:01:53.346310 db@open opening +12:01:53.346427 journal@recovery F·1 +12:01:53.346591 journal@recovery recovering @88 +12:01:53.350436 memdb@flush created L0@90 N·2 S·259B "cos..ess,v17":"led..nfo,v16" +12:01:53.350863 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:01:53.356998 db@janitor F·5 G·0 +12:01:53.357009 db@open done T·10.694071ms +12:01:53.357177 db@close closing +12:01:53.357258 db@close done T·79.894µs +=============== Feb 23, 2021 (GMT) =============== +12:01:57.771688 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:01:57.771807 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:01:57.771818 db@open opening +12:01:57.771844 journal@recovery F·1 +12:01:57.771911 journal@recovery recovering @91 +12:01:57.772211 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:01:57.777712 db@janitor F·5 G·0 +12:01:57.777726 db@open done T·5.899191ms +12:01:57.777794 db@close closing +12:01:57.777821 db@close done T·26.301µs +=============== Feb 23, 2021 (GMT) =============== +12:02:01.179234 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:01.179444 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:02:01.179471 db@open opening +12:02:01.179568 journal@recovery F·1 +12:02:01.180395 journal@recovery recovering @93 +12:02:01.180499 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:02:01.186898 db@janitor F·5 G·0 +12:02:01.186908 db@open done T·7.433758ms +12:02:01.376649 db@close closing +12:02:01.376744 db@close done T·94.311µs +=============== Feb 23, 2021 (GMT) =============== +12:02:08.325782 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:08.325880 version@stat F·[2 1] S·1KiB[750B 1KiB] Sc·[0.50 0.00] +12:02:08.325892 db@open opening +12:02:08.325919 journal@recovery F·1 +12:02:08.326096 journal@recovery recovering @95 +12:02:08.328874 memdb@flush created L0@97 N·2 S·189B "cos..ess,d19":"tes..nfo,d20" +12:02:08.329781 version@stat F·[3 1] S·2KiB[939B 1KiB] Sc·[0.75 0.00] +12:02:08.335685 db@janitor F·6 G·0 +12:02:08.335726 db@open done T·9.800531ms +12:02:08.335812 db@close closing +12:02:08.335913 db@close done T·98.185µs +=============== Feb 23, 2021 (GMT) =============== +12:02:10.989199 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:10.989372 version@stat F·[3 1] S·2KiB[939B 1KiB] Sc·[0.75 0.00] +12:02:10.989381 db@open opening +12:02:10.989413 journal@recovery F·1 +12:02:10.989493 journal@recovery recovering @98 +12:02:10.989823 version@stat F·[3 1] S·2KiB[939B 1KiB] Sc·[0.75 0.00] +12:02:10.997764 db@janitor F·6 G·0 +12:02:10.997775 db@open done T·8.391051ms +12:02:11.186825 db@close closing +12:02:11.186873 db@close done T·46.355µs +=============== Feb 23, 2021 (GMT) =============== +12:02:13.779564 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:13.779705 version@stat F·[3 1] S·2KiB[939B 1KiB] Sc·[0.75 0.00] +12:02:13.779716 db@open opening +12:02:13.779766 journal@recovery F·1 +12:02:13.780050 journal@recovery recovering @100 +12:02:13.782794 memdb@flush created L0@102 N·2 S·186B "cia..nfo,d23":"cos..ess,d22" +12:02:13.782888 version@stat F·[4 1] S·2KiB[1KiB 1KiB] Sc·[1.00 0.00] +12:02:13.787114 db@janitor F·7 G·0 +12:02:13.787129 db@open done T·7.382544ms +12:02:13.787201 table@compaction L0·4 -> L1·1 S·2KiB Q·24 +12:02:13.787271 db@close closing +12:02:13.789006 table@build created L1@105 N·8 S·1KiB "bar.info,v13":"val..nfo,v1" +12:02:13.789011 table@build exiting +12:02:13.789013 table@build revert @105 +12:02:13.789055 db@close done T·1.783005ms +=============== Feb 23, 2021 (GMT) =============== +12:02:19.245131 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:19.245285 version@stat F·[4 1] S·2KiB[1KiB 1KiB] Sc·[1.00 0.00] +12:02:19.245315 db@open opening +12:02:19.245368 journal@recovery F·1 +12:02:19.245465 journal@recovery recovering @103 +12:02:19.245858 version@stat F·[4 1] S·2KiB[1KiB 1KiB] Sc·[1.00 0.00] +12:02:19.251449 db@janitor F·7 G·0 +12:02:19.251465 db@open done T·6.140479ms +12:02:19.251485 table@compaction L0·4 -> L1·1 S·2KiB Q·24 +12:02:19.251521 db@close closing +12:02:19.251592 db@close done T·70.226µs +=============== Feb 23, 2021 (GMT) =============== +12:02:21.580113 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:21.580210 version@stat F·[4 1] S·2KiB[1KiB 1KiB] Sc·[1.00 0.00] +12:02:21.580222 db@open opening +12:02:21.580272 journal@recovery F·1 +12:02:21.580647 journal@recovery recovering @105 +12:02:21.580747 version@stat F·[4 1] S·2KiB[1KiB 1KiB] Sc·[1.00 0.00] +12:02:21.587123 db@janitor F·7 G·0 +12:02:21.587130 db@open done T·6.905846ms +12:02:21.587221 table@compaction L0·4 -> L1·1 S·2KiB Q·24 +12:02:21.589889 table@build created L1@109 N·8 S·1KiB "bar.info,v13":"val..nfo,v1" +12:02:21.589929 version@stat F·[0 1] S·1KiB[0B 1KiB] Sc·[0.00 0.00] +12:02:21.591275 table@compaction committed F-4 S-1KiB Ke·0 D·8 T·4.039289ms +12:02:21.591357 table@remove removed @102 +12:02:21.591414 table@remove removed @97 +12:02:21.591428 table@remove removed @90 +12:02:21.591440 table@remove removed @83 +12:02:21.591472 table@remove removed @82 +12:02:21.777758 db@close closing +12:02:21.777800 db@close done T·40.787µs +=============== Feb 23, 2021 (GMT) =============== +12:02:22.900722 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:22.900859 version@stat F·[0 1] S·1KiB[0B 1KiB] Sc·[0.00 0.00] +12:02:22.900892 db@open opening +12:02:22.900963 journal@recovery F·1 +12:02:22.901083 journal@recovery recovering @107 +12:02:22.904868 memdb@flush created L0@110 N·2 S·193B "cos..ess,d25":"val..nfo,d26" +12:02:22.905267 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:02:22.909786 db@janitor F·4 G·0 +12:02:22.909799 db@open done T·8.899965ms +12:02:22.909931 db@close closing +12:02:22.910008 db@close done T·74.647µs +=============== Feb 23, 2021 (GMT) =============== +12:02:53.139966 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:53.140102 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:02:53.140135 db@open opening +12:02:53.140206 journal@recovery F·1 +12:02:53.140586 journal@recovery recovering @111 +12:02:53.141053 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:02:53.147675 db@janitor F·4 G·0 +12:02:53.147687 db@open done T·7.546001ms +12:02:53.147750 db@close closing +12:02:53.147818 db@close done T·67.754µs +=============== Feb 23, 2021 (GMT) =============== +12:02:53.147913 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:02:53.147982 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:02:53.147993 db@open opening +12:02:53.148043 journal@recovery F·1 +12:02:53.148101 journal@recovery recovering @113 +12:02:53.148192 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:02:53.152906 db@janitor F·4 G·0 +12:02:53.152912 db@open done T·4.91707ms +12:02:53.156922 db@close closing +12:02:53.156949 db@close done T·25.968µs +=============== Feb 23, 2021 (GMT) =============== +12:03:24.147022 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:24.147113 version@stat F·[1 1] S·1KiB[193B 1KiB] Sc·[0.25 0.00] +12:03:24.147123 db@open opening +12:03:24.147195 journal@recovery F·1 +12:03:24.147542 journal@recovery recovering @115 +12:03:24.150459 memdb@flush created L0@117 N·2 S·244B "cos..ess,v29":"pub..nfo,v28" +12:03:24.150556 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:24.156079 db@janitor F·5 G·0 +12:03:24.156116 db@open done T·8.964543ms +12:03:24.156215 db@close closing +12:03:24.156330 db@close done T·113.154µs +=============== Feb 23, 2021 (GMT) =============== +12:03:33.230269 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:33.230428 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:33.230456 db@open opening +12:03:33.230505 journal@recovery F·1 +12:03:33.230859 journal@recovery recovering @118 +12:03:33.231123 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:33.237886 db@janitor F·5 G·0 +12:03:33.237932 db@open done T·7.464889ms +12:03:33.238009 db@close closing +12:03:33.238077 db@close done T·67.991µs +=============== Feb 23, 2021 (GMT) =============== +12:03:33.238135 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:33.238190 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:33.238200 db@open opening +12:03:33.238226 journal@recovery F·1 +12:03:33.238295 journal@recovery recovering @120 +12:03:33.238459 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:33.242714 db@janitor F·5 G·0 +12:03:33.242723 db@open done T·4.520893ms +12:03:33.246526 db@close closing +12:03:33.246576 db@close done T·49.286µs +=============== Feb 23, 2021 (GMT) =============== +12:03:36.732039 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:36.732132 version@stat F·[2 1] S·1KiB[437B 1KiB] Sc·[0.50 0.00] +12:03:36.732143 db@open opening +12:03:36.732193 journal@recovery F·1 +12:03:36.732321 journal@recovery recovering @122 +12:03:36.734960 memdb@flush created L0@124 N·2 S·244B "cos..ess,v32":"pub..nfo,v31" +12:03:36.735282 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:03:36.740852 db@janitor F·6 G·0 +12:03:36.740890 db@open done T·8.717358ms +12:03:36.741044 db@close closing +12:03:36.741134 db@close done T·87.869µs +=============== Feb 23, 2021 (GMT) =============== +12:03:56.009876 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:56.009989 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:03:56.010002 db@open opening +12:03:56.010034 journal@recovery F·1 +12:03:56.010178 journal@recovery recovering @125 +12:03:56.011128 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:03:56.018052 db@janitor F·6 G·0 +12:03:56.018064 db@open done T·8.05417ms +12:03:56.018173 db@close closing +12:03:56.018224 db@close done T·49.879µs +=============== Feb 23, 2021 (GMT) =============== +12:03:58.983153 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:03:58.983257 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:03:58.983268 db@open opening +12:03:58.983297 journal@recovery F·1 +12:03:58.983885 journal@recovery recovering @127 +12:03:58.983986 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:03:58.991844 db@janitor F·6 G·0 +12:03:58.991851 db@open done T·8.580014ms +12:03:59.181560 db@close closing +12:03:59.181637 db@close done T·76.045µs +=============== Feb 23, 2021 (GMT) =============== +12:04:10.259722 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:04:10.259852 version@stat F·[3 1] S·1KiB[681B 1KiB] Sc·[0.75 0.00] +12:04:10.259869 db@open opening +12:04:10.259919 journal@recovery F·1 +12:04:10.260104 journal@recovery recovering @129 +12:04:10.264224 memdb@flush created L0@131 N·2 S·187B "cos..ess,d34":"foo.info,d35" +12:04:10.264492 version@stat F·[4 1] S·1KiB[868B 1KiB] Sc·[1.00 0.00] +12:04:10.268582 db@janitor F·7 G·0 +12:04:10.268595 db@open done T·8.720601ms +12:04:10.268655 table@compaction L0·4 -> L1·1 S·1KiB Q·36 +12:04:10.268669 db@close closing +12:04:10.268830 db@close done T·159.948µs +=============== Feb 23, 2021 (GMT) =============== +12:04:10.268891 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:04:10.269025 version@stat F·[4 1] S·1KiB[868B 1KiB] Sc·[1.00 0.00] +12:04:10.269034 db@open opening +12:04:10.269089 journal@recovery F·1 +12:04:10.269152 journal@recovery recovering @132 +12:04:10.269259 version@stat F·[4 1] S·1KiB[868B 1KiB] Sc·[1.00 0.00] +12:04:10.274436 db@janitor F·7 G·0 +12:04:10.274466 db@open done T·5.404186ms +12:04:10.274543 table@compaction L0·4 -> L1·1 S·1KiB Q·36 +12:04:10.277245 table@build created L1@136 N·8 S·825B "bar.info,v13":"pub..nfo,v31" +12:04:10.277287 version@stat F·[0 1] S·825B[0B 825B] Sc·[0.00 0.00] +12:04:10.278388 db@close closing +12:04:10.280880 table@commit exiting +12:04:10.280907 db@close done T·2.542424ms +=============== Feb 23, 2021 (GMT) =============== +12:04:12.868499 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:04:12.868628 version@stat F·[0 1] S·825B[0B 825B] Sc·[0.00 0.00] +12:04:12.868640 db@open opening +12:04:12.868670 journal@recovery F·1 +12:04:12.868785 journal@recovery recovering @134 +12:04:12.870434 memdb@flush created L0@137 N·2 S·244B "cos..ess,v38":"pub..nfo,v37" +12:04:12.871017 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:04:12.876243 db@janitor F·9 G·5 +12:04:12.876251 db@janitor removing table-124 +12:04:12.876290 db@janitor removing table-110 +12:04:12.876302 db@janitor removing table-109 +12:04:12.876330 db@janitor removing table-117 +12:04:12.876340 db@janitor removing table-131 +12:04:12.876381 db@open done T·7.712682ms +12:04:12.876440 db@close closing +12:04:12.876498 db@close done T·55.873µs +=============== Feb 23, 2021 (GMT) =============== +12:09:38.966259 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:09:38.966450 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:09:38.966463 db@open opening +12:09:38.966490 journal@recovery F·1 +12:09:38.966746 journal@recovery recovering @138 +12:09:38.967252 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:09:38.974464 db@janitor F·4 G·0 +12:09:38.974477 db@open done T·8.005768ms +12:09:56.196454 db@close closing +12:09:56.196575 db@close done T·142.606µs +=============== Feb 23, 2021 (GMT) =============== +12:10:09.568902 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:10:09.568981 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:10:09.568993 db@open opening +12:10:09.569022 journal@recovery F·1 +12:10:09.569291 journal@recovery recovering @140 +12:10:09.569781 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:10:09.575840 db@janitor F·4 G·0 +12:10:09.575848 db@open done T·6.851269ms +12:10:23.290522 db@close closing +12:10:23.290590 db@close done T·66.518µs +=============== Feb 23, 2021 (GMT) =============== +12:11:01.674005 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:11:01.674086 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:11:01.674098 db@open opening +12:11:01.674128 journal@recovery F·1 +12:11:01.674359 journal@recovery recovering @142 +12:11:01.674814 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:11:01.680965 db@janitor F·4 G·0 +12:11:01.680980 db@open done T·6.874747ms +12:11:06.655715 db@close closing +12:11:06.655759 db@close done T·43.852µs +=============== Feb 23, 2021 (GMT) =============== +12:19:52.269690 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:19:52.269780 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:19:52.269792 db@open opening +12:19:52.269826 journal@recovery F·1 +12:19:52.270051 journal@recovery recovering @144 +12:19:52.270585 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:19:52.276899 db@janitor F·4 G·0 +12:19:52.276939 db@open done T·7.116495ms +12:19:59.249868 db@close closing +12:19:59.249968 db@close done T·99.117µs +=============== Feb 23, 2021 (GMT) =============== +12:20:30.569407 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:20:30.569504 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:20:30.569516 db@open opening +12:20:30.569545 journal@recovery F·1 +12:20:30.569730 journal@recovery recovering @146 +12:20:30.570245 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:20:30.577100 db@janitor F·4 G·0 +12:20:30.577111 db@open done T·7.591098ms +=============== Feb 23, 2021 (GMT) =============== +12:20:35.223490 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:20:35.223588 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:20:35.223601 db@open opening +12:20:35.223630 journal@recovery F·1 +12:20:35.223986 journal@recovery recovering @148 +12:20:35.224401 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:20:35.229848 db@janitor F·4 G·0 +12:20:35.229856 db@open done T·6.250812ms +12:20:41.049391 db@close closing +12:20:41.049441 db@close done T·49.18µs +=============== Feb 23, 2021 (GMT) =============== +12:21:45.804793 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +12:21:45.804915 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:21:45.804928 db@open opening +12:21:45.804961 journal@recovery F·1 +12:21:45.805201 journal@recovery recovering @150 +12:21:45.805681 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +12:21:45.810888 db@janitor F·4 G·0 +12:21:45.810920 db@open done T·5.985873ms +12:21:49.489917 db@close closing +12:21:49.490008 db@close done T·89.528µs +=============== Feb 26, 2021 (GMT) =============== +11:30:44.083018 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:30:44.084062 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:30:44.084075 db@open opening +11:30:44.084102 journal@recovery F·1 +11:30:44.084383 journal@recovery recovering @152 +11:30:44.084768 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:30:44.090432 db@janitor F·4 G·0 +11:30:44.090476 db@open done T·6.381184ms +11:30:44.090566 db@close closing +11:30:44.090613 db@close done T·44.34µs +=============== Feb 26, 2021 (GMT) =============== +11:32:36.352559 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:32:36.352641 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:32:36.352653 db@open opening +11:32:36.352681 journal@recovery F·1 +11:32:36.352756 journal@recovery recovering @154 +11:32:36.353034 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:32:36.360804 db@janitor F·4 G·0 +11:32:36.360816 db@open done T·8.15837ms +11:32:36.360904 db@close closing +11:32:36.360960 db@close done T·54.048µs +=============== Feb 26, 2021 (GMT) =============== +11:32:48.449675 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:32:48.449787 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:32:48.449820 db@open opening +11:32:48.449847 journal@recovery F·1 +11:32:48.449955 journal@recovery recovering @156 +11:32:48.450282 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:32:48.456194 db@janitor F·4 G·0 +11:32:48.456235 db@open done T·6.384513ms +11:32:48.456367 db@close closing +11:32:48.456478 db@close done T·109.034µs +=============== Feb 26, 2021 (GMT) =============== +11:34:15.269223 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:34:15.269382 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:34:15.269414 db@open opening +11:34:15.269464 journal@recovery F·1 +11:34:15.269563 journal@recovery recovering @158 +11:34:15.269872 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:34:15.275610 db@janitor F·4 G·0 +11:34:15.275622 db@open done T·6.200818ms +11:34:15.275707 db@close closing +11:34:15.275752 db@close done T·44.471µs +=============== Feb 26, 2021 (GMT) =============== +11:34:32.038701 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +11:34:32.038798 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:34:32.038810 db@open opening +11:34:32.038837 journal@recovery F·1 +11:34:32.039081 journal@recovery recovering @160 +11:34:32.039560 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +11:34:32.045125 db@janitor F·4 G·0 +11:34:32.045132 db@open done T·6.318174ms +11:34:52.928799 db@close closing +11:34:52.928908 db@close done T·94.101µs +=============== Feb 26, 2021 (GMT) =============== +19:42:33.585125 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +19:42:33.585220 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +19:42:33.585232 db@open opening +19:42:33.585283 journal@recovery F·1 +19:42:33.585544 journal@recovery recovering @162 +19:42:33.585964 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +19:42:33.592890 db@janitor F·4 G·0 +19:42:33.592928 db@open done T·7.666705ms +19:42:33.592996 db@close closing +19:42:33.593063 db@close done T·63.906µs +=============== Feb 27, 2021 (GMT) =============== +17:05:01.817733 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +17:05:01.817819 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +17:05:01.817830 db@open opening +17:05:01.817855 journal@recovery F·1 +17:05:01.818108 journal@recovery recovering @164 +17:05:01.818567 version@stat F·[1 1] S·1KiB[244B 825B] Sc·[0.25 0.00] +17:05:01.824986 db@janitor F·4 G·0 +17:05:01.825024 db@open done T·7.162696ms +17:05:01.825107 db@close closing +17:05:01.825221 db@close done T·111.618µs diff --git a/client/keys/testdata/keys/keys.db/MANIFEST-000167 b/client/keys/testdata/keys/keys.db/MANIFEST-000167 new file mode 100644 index 000000000000..410bbeb71a22 Binary files /dev/null and b/client/keys/testdata/keys/keys.db/MANIFEST-000167 differ diff --git a/client/keys/types.go b/client/keys/types.go index 079ef4962d24..129d26e29823 100644 --- a/client/keys/types.go +++ b/client/keys/types.go @@ -1,6 +1,6 @@ package keys -// used for outputting keys.Info over REST +// used for outputting keyring.Info over REST // AddNewKey request a new key type AddNewKey struct { diff --git a/client/keys/types_test.go b/client/keys/types_test.go new file mode 100644 index 000000000000..24428a5b9261 --- /dev/null +++ b/client/keys/types_test.go @@ -0,0 +1,29 @@ +package keys_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client/keys" +) + +func TestConstructors(t *testing.T) { + require.Equal(t, keys.AddNewKey{ + Name: "name", + Password: "password", + Mnemonic: "mnemonic", + Account: 1, + Index: 1, + }, keys.NewAddNewKey("name", "password", "mnemonic", 1, 1)) + + require.Equal(t, keys.RecoverKey{ + Password: "password", + Mnemonic: "mnemonic", + Account: 1, + Index: 1, + }, keys.NewRecoverKey("password", "mnemonic", 1, 1)) + + require.Equal(t, keys.UpdateKeyReq{OldPassword: "old", NewPassword: "new"}, keys.NewUpdateKeyReq("old", "new")) + require.Equal(t, keys.DeleteKeyReq{Password: "password"}, keys.NewDeleteKeyReq("password")) +} diff --git a/client/keys/update.go b/client/keys/update.go deleted file mode 100644 index 2ca46d43289b..000000000000 --- a/client/keys/update.go +++ /dev/null @@ -1,55 +0,0 @@ -package keys - -import ( - "bufio" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/input" -) - -// UpdateKeyCommand changes the password of a key in the keybase. -// It takes no effect on keys managed by new the keyring-based keybase implementation. -func UpdateKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "update ", - Short: "Change the password used to protect private key", - Deprecated: `it takes no effect with the new keyring -based backend and is provided only for backward compatibility with the -legacy LevelDB based backend. -Refer to your operating system's manual to learn how to change your -keyring's password. -`, - RunE: runUpdateCmd, - Args: cobra.ExactArgs(1), - } - return cmd -} - -func runUpdateCmd(cmd *cobra.Command, args []string) error { - name := args[0] - - buf := bufio.NewReader(cmd.InOrStdin()) - kb, err := NewKeyBaseFromDir(viper.GetString(flags.FlagHome)) - if err != nil { - return err - } - oldpass, err := input.GetPassword("Enter the current passphrase:", buf) - if err != nil { - return err - } - - getNewpass := func() (string, error) { - return input.GetCheckPassword( - "Enter the new passphrase:", - "Repeat the new passphrase:", buf) - } - if err := kb.Update(name, oldpass, getNewpass); err != nil { - return err - } - - cmd.PrintErrln("Password successfully updated!") - return nil -} diff --git a/client/keys/update_test.go b/client/keys/update_test.go deleted file mode 100644 index 351cedd578ce..000000000000 --- a/client/keys/update_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package keys - -import ( - "testing" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/tests" -) - -func Test_updateKeyCommand(t *testing.T) { - cmd := UpdateKeyCommand() - assert.NotNil(t, cmd) - // No flags or defaults to validate -} - -func Test_runUpdateCmd(t *testing.T) { - fakeKeyName1 := "runUpdateCmd_Key1" - fakeKeyName2 := "runUpdateCmd_Key2" - - cmd := UpdateKeyCommand() - - // fails because it requests a password - assert.EqualError(t, runUpdateCmd(cmd, []string{fakeKeyName1}), "EOF") - - // try again - mockIn, _, _ := tests.ApplyMockIO(cmd) - mockIn.Reset("pass1234\n") - assert.EqualError(t, runUpdateCmd(cmd, []string{fakeKeyName1}), "Key runUpdateCmd_Key1 not found") - - // Prepare a key base - // Now add a temporary keybase - kbHome, cleanUp1 := tests.NewTestCaseDir(t) - defer cleanUp1() - viper.Set(flags.FlagHome, kbHome) - - kb, err := NewKeyBaseFromDir(viper.GetString(flags.FlagHome)) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName1, tests.TestMnemonic, "", "", "0", keys.Secp256k1) - assert.NoError(t, err) - _, err = kb.CreateAccount(fakeKeyName2, tests.TestMnemonic, "", "", "1", keys.Secp256k1) - assert.NoError(t, err) - - // Try again now that we have keys - // Incorrect key type - mockIn.Reset("pass1234\nNew1234\nNew1234") - err = runUpdateCmd(cmd, []string{fakeKeyName1}) - assert.EqualError(t, err, "locally stored key required. Received: keys.offlineInfo") - - // TODO: Check for other type types? -} diff --git a/client/keys/utils.go b/client/keys/utils.go index 008d3ccf7822..7490914d1108 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -2,15 +2,12 @@ package keys import ( "fmt" + "io" "path/filepath" - "github.com/99designs/keyring" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" + cryptokeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" ) // available output formats. @@ -22,101 +19,80 @@ const ( defaultKeyDBName = "keys" ) -type bechKeyOutFn func(keyInfo keys.Info) (keys.KeyOutput, error) +type bechKeyOutFn func(keyInfo cryptokeyring.Info) (cryptokeyring.KeyOutput, error) -// NewKeyBaseFromDir initializes a keybase at the rootDir directory. Keybase +// NewLegacyKeyBaseFromDir initializes a legacy keybase at the rootDir directory. Keybase // options can be applied when generating this new Keybase. -func NewKeyBaseFromDir(rootDir string, opts ...keys.KeybaseOption) (keys.Keybase, error) { - return getLazyKeyBaseFromDir(rootDir, opts...) +func NewLegacyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.LegacyKeybase, error) { + return getLegacyKeyBaseFromDir(rootDir, opts...) } -// NewInMemoryKeyBase returns a storage-less keybase. -func NewInMemoryKeyBase() keys.Keybase { return keys.NewInMemory() } - -func getLazyKeyBaseFromDir(rootDir string, opts ...keys.KeybaseOption) (keys.Keybase, error) { - return keys.New(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...), nil +func getLegacyKeyBaseFromDir(rootDir string, opts ...cryptokeyring.KeybaseOption) (cryptokeyring.LegacyKeybase, error) { + return cryptokeyring.NewLegacy(defaultKeyDBName, filepath.Join(rootDir, "keys"), opts...) } -func printKeyInfo(keyInfo keys.Info, bechKeyOut bechKeyOutFn) { +func printKeyInfo(w io.Writer, keyInfo cryptokeyring.Info, bechKeyOut bechKeyOutFn, output string) { ko, err := bechKeyOut(keyInfo) if err != nil { panic(err) } - switch viper.Get(cli.OutputFlag) { + switch output { case OutputFormatText: - printTextInfos([]keys.KeyOutput{ko}) + printTextInfos(w, []cryptokeyring.KeyOutput{ko}) case OutputFormatJSON: - var out []byte - var err error - if viper.GetBool(flags.FlagIndentResponse) { - out, err = KeysCdc.MarshalJSONIndent(ko, "", " ") - } else { - out, err = KeysCdc.MarshalJSON(ko) - } + out, err := KeysCdc.MarshalJSON(ko) if err != nil { panic(err) } - fmt.Println(string(out)) + fmt.Fprintln(w, string(out)) } } -func printInfos(infos []keys.Info) { - kos, err := keys.Bech32KeysOutput(infos) +func printInfos(w io.Writer, infos []cryptokeyring.Info, output string) { + kos, err := cryptokeyring.Bech32KeysOutput(infos) if err != nil { panic(err) } - switch viper.Get(cli.OutputFlag) { + switch output { case OutputFormatText: - printTextInfos(kos) + printTextInfos(w, kos) case OutputFormatJSON: - var out []byte - var err error - - if viper.GetBool(flags.FlagIndentResponse) { - out, err = KeysCdc.MarshalJSONIndent(kos, "", " ") - } else { - out, err = KeysCdc.MarshalJSON(kos) - } - + out, err := KeysCdc.MarshalJSON(kos) if err != nil { panic(err) } - fmt.Printf("%s", out) + + fmt.Fprintf(w, "%s", out) } } -func printTextInfos(kos []keys.KeyOutput) { +func printTextInfos(w io.Writer, kos []cryptokeyring.KeyOutput) { out, err := yaml.Marshal(&kos) if err != nil { panic(err) } - fmt.Println(string(out)) + fmt.Fprintln(w, string(out)) } -func printKeyAddress(info keys.Info, bechKeyOut bechKeyOutFn) { +func printKeyAddress(w io.Writer, info cryptokeyring.Info, bechKeyOut bechKeyOutFn) { ko, err := bechKeyOut(info) if err != nil { panic(err) } - fmt.Println(ko.Address) + fmt.Fprintln(w, ko.Address) } -func printPubKey(info keys.Info, bechKeyOut bechKeyOutFn) { +func printPubKey(w io.Writer, info cryptokeyring.Info, bechKeyOut bechKeyOutFn) { ko, err := bechKeyOut(info) if err != nil { panic(err) } - fmt.Println(ko.PubKey) -} - -func isRunningUnattended() bool { - backends := keyring.AvailableBackends() - return len(backends) == 2 && backends[1] == keyring.BackendType("file") + fmt.Fprintln(w, ko.PubKey) } diff --git a/client/lcd/root.go b/client/lcd/root.go deleted file mode 100644 index c55f949bb14e..000000000000 --- a/client/lcd/root.go +++ /dev/null @@ -1,122 +0,0 @@ -package lcd - -import ( - "fmt" - "net" - "net/http" - "os" - "time" - - "github.com/gorilla/handlers" - "github.com/gorilla/mux" - "github.com/rakyll/statik/fs" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/log" - rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - - // unnamed import of statik for swagger UI support - _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" -) - -const FlagAllowCORS = "cors" - -// RestServer represents the Light Client Rest server -type RestServer struct { - Mux *mux.Router - CliCtx context.CLIContext - - log log.Logger - listener net.Listener -} - -// NewRestServer creates a new rest server instance -func NewRestServer(cdc *codec.Codec) *RestServer { - r := mux.NewRouter() - cliCtx := context.NewCLIContext().WithCodec(cdc) - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - - return &RestServer{ - Mux: r, - CliCtx: cliCtx, - log: logger, - } -} - -// Start starts the rest server -func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTimeout uint, cors bool) (err error) { - server.TrapSignal(func() { - err := rs.listener.Close() - rs.log.Error("error closing listener", "err", err) - }) - - cfg := rpcserver.DefaultConfig() - cfg.MaxOpenConnections = maxOpen - cfg.ReadTimeout = time.Duration(readTimeout) * time.Second - cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second - - rs.listener, err = rpcserver.Listen(listenAddr, cfg) - if err != nil { - return - } - rs.log.Info( - fmt.Sprintf( - "Starting application REST service (chain-id: %q)...", - viper.GetString(flags.FlagChainID), - ), - ) - - var h http.Handler = rs.Mux - if cors { - h = handlers.CORS( - handlers.AllowedHeaders([]string{"Content-Type"}), - handlers.AllowedOrigins([]string{"*"}), - )(h) - } - - return rpcserver.Serve(rs.listener, h, rs.log, cfg) -} - -// ServeCommand will start the application REST service as a blocking process. It -// takes a codec to create a RestServer object and a function to register all -// necessary routes. -func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.Command { - cmd := &cobra.Command{ - Use: "rest-server", - Short: "Start LCD (light-client daemon), a local REST server", - RunE: func(cmd *cobra.Command, args []string) (err error) { - rs := NewRestServer(cdc) - - registerRoutesFn(rs) - rs.registerSwaggerUI() - - // Start the rest server and return error if one exists - err = rs.Start( - viper.GetString(flags.FlagListenAddr), - viper.GetInt(flags.FlagMaxOpenConnections), - uint(viper.GetInt(flags.FlagRPCReadTimeout)), - uint(viper.GetInt(flags.FlagRPCWriteTimeout)), - viper.GetBool(FlagAllowCORS), - ) - - return err - }, - } - - cmd.Flags().Bool(FlagAllowCORS, false, "Allows CORS requests from all domains") - return flags.RegisterRestServerFlags(cmd) -} - -func (rs *RestServer) registerSwaggerUI() { - statikFS, err := fs.New() - if err != nil { - panic(err) - } - staticServer := http.FileServer(statikFS) - rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) -} diff --git a/client/lcd/statik/init.go b/client/lcd/statik/init.go deleted file mode 100644 index 9633aeb291c7..000000000000 --- a/client/lcd/statik/init.go +++ /dev/null @@ -1,3 +0,0 @@ -package statik - -//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/lcd/statik diff --git a/client/lcd/statik/statik.go b/client/lcd/statik/statik.go deleted file mode 100644 index 48a9278324ee..000000000000 --- a/client/lcd/statik/statik.go +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by statik. DO NOT EDIT. - -// Package statik contains static assets. -package statik - -import ( - "github.com/rakyll/statik/fs" -) - -func init() { - data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8\x00\xbd\x01B\xfe\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\x00\x00\x01\x84IDATx\x01\x95S\x03Luq\x1c\xfd\x8c\xf1\xc3\xec0\xa7)\xcda\xb6k6\xb2\x9b\xf9\xb2k\xc85/\xdb\x8dqx\xc6\x94m\xcc{\xef\x7fO\xff\xf3l\xdc\xed\xf2\xe0\xfe\xf8\xc9\xffP\x14\x11/\x14[\xa3P\xc4\xa1\xbc?\xf1t>7\x12s\x13\x03\x85\xca7IR a\xb5j\x8f\xa71\xbe]\x88\xf6\xb9L\xf0\x1c\x93\xcf\xda\xe3)\x10\x93f\x8d\xe4\x06\x13\xcf\xde<\x9b\xd14\x95\x8a\x92\x81OA\xcfF\x89\xdd<\x9b M\xe6}L\xe4\x07\x15\xc5\xf5\xe3\xffI\x0c{\xd6\x8d\xffs\x994\xbasfh\xae?\xafk\x1aprw\x10 <\xb9\xdb\xc7\x86\xa6\xd1\x19I\n\xa8\xb1\xd7\x84y3g\x171T$\xb5c\x7fq\xfbbq\xbfk\x8e'\x1dQ\xb0\xc2,\x92\x0bx|;F\xe5\xf0\xef\x00\x83\xf2\xa1\x1fx|?q\xbd\xcb\xc2\x16\x80ZF\xf0\xc4J\xf3\xe3\xe4n1\xcc\x17k`:}\xcby\xe8\x98\xcbB\xc7|6z\x97r\xd14\x9d\x06\xd3\xf9\x8a\xe4\x94\x90\x8b\xb6\xd9\x0cP\xebc@\xd0|\xbe*\xc94\xc8\xa7\x98'\xcdh\x00\xe3\xd92\xa6vK}\x0cB\xa4\xf0+D\n\xc7\x81)\xb0\x10\x9a\xe3\xa9\xd8\x8bx\xe4(\xa2\xbb\x8dl\x0d\x01\xb6\x8a-\xf378\xbe\xdd\xc7\xa6\xb6\xc9\xd9\xc6d\xd8\\m\xf4\x0c\x92 uQ\x0e\xd2\xf5\xb3\xd1\xf1w\xdfQ\x16\xb34a$\xa1\xc4\xc4(V\xbcF\xd9\xdf\xa4\x91\xe9\xb0&,\x12+\xcd\x93\xcf\x1c\x1cb\xdc\xca\x00qt\xeb\xcc-\x14\x89\xfe\xfc\x0fm2j\x88\xec\xccs\x18\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x08\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00 \x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8\x00u\x04\x8a\xfb\x89PNG\x0d\n\x1a\n\x00\x00\x00\x0dIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\x00\x00\x04|ID\xc4\xcf\xd0@\x04&%\xad\x1e\x16\x0f\xf7\x8d\x97AR\xfa\xca\xe7l\x87\x05\xf8\xd2\xfb\x0c\x84\x1d\x0dLVY\xdc/ju\x13\x1a\x88\xd2\xa0\xaaa\x82|nzp_\xf4\x03\xc8 \xd4;^\x8a9}\xeeu\x9a\x91 `\x04\x14s\xec\xe1\x0c\xc6]\xa3\x05``\xd1w\x12*~ \x00\xf3\xae\xd3\xa0\x9cb\x82\xa2bx(\xb3n\x1fqx\xd2\xf2\xda4\x1d\x8a}\x1ck\xd4>\x9cI+\xeb\xb3\xf4k\xc8u`L\x93\xf3]4\xb5\xd0\xc3\xe33\xd9\xee\xd7\xf2\xd9\x19\xea\x18\xc9\xc1Y:\x18\xfb(-\xadN\x82\x06e\xd5\x1f0\xa2\x1dV\xf8\xbe0\xc1\x985\x01\xf8\xd2~\\\xa6\xa5\xb5)&\xf6\x98V\x80l\xe4\x03\xf8\x03\x04\x00s\x9a^\xec\x85\x00\xf4+\x0b\x00\xe1:G\xf2p\x96\x0e\xc4,\xe46\x1e5\xbbP\xdd\x15J\x80}\xce\xa4\xe2\xc8{m\xa4\xe2\xc3\xc2\x01\x07\xc0\xdb\xa4\x18-\xa1\x931\xba\x10S\xfa%\xb6P`\x10\x19v\x99#|Gg\x9b \x10W\xf6\x8dI1\xba\x92\xd66\x17E\x12\xfa\xd9\xa8\xf3UTe\n\x1b\x95\x9d\x81f\xe5\x18\xa5umc\x81\x86\xa6\xeb\xec \x804\xcbg\x17\xa19\xfa\xc6\xf7<\xa3\xbd\xf2\x0e\x7f\x02\x80\x97Y\xc7\xac\x184$h\xa3v\xba! \xcc{\xcd\xb4!\xb1\xd8\x92%h\xe3\x93\xdc\xd3_\xda1\xe6\xaei\xcf\x83\xa6p\xbc$\xf0\xb2\xda\x94\xa2q\x14B@\x13\xdb\xff\xf3\xd7\x0d\xfaA\xb9\xc5n{\x8e\xd6Y\x08\x01u\xc1'~\x16\x8e\xe9\x04\xa2\xfbA+\xc74\x0c\x98\xab\xd7:\xfc0\xd1v\xaf$\xa2#\xb7\xf1\x08\xfdm!OXh8\x10j|g\xd1\xe0a\xb2\x99\x04\x9a[y\x9a\xbdk\xf24C$\xa0\x9e#\x9f\xa3\xa8\x001\xc6\x1a\"\xc0\xe4i\xa6\xcc0\xf3\xf7\xb7\xf5XE\xb8\xe0\xa1\xc9\xc2\x0c\x90\x83\x80$\x838\xdf\xd6\xe3\xd4\x82FNG\x0f\x876\x8a\xbf1\xa8d(\xa7@\x8cQX\x90\xdb\x19\x9f\xc5YG\xe9\x9e\x00\xa5y3]\x9aJ\xe1\"\x00\x00\x00\x00IEND\xaeB`\x82\x01\x00\x00\xff\xffPK\x07\x086B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00 \x00index.htmlUT\x05\x00\x01\x80Cm8\x9cT]k\xdc:\x10}\xdf_1Q\x1e\x92\\\"\xfb&\x81p\xf1\xb5\xfd\x90\xa6\xa5\x81\x94\x06\x92}(\xa5\x14\xd9\x1a{\xa7\x91\xa5E\x92\xf7#!\xff\xbdX\xf6\xae\xb7\xdd\x90BYX\x8f\xe7\x9c9\x1a\x1d\x8d\x9c\x1ep\x0e\x1f\x1f>\xddBe,8/<\x95 \xc9yKE\xeb\xc9h(Z-\x15B\xd1\x92\x92\xc0y>I\x0f\xae?\xbf{\xf8r\xf7\x1ef\xbeQ\xf9$\xed\x1e\xa0\x84\xae3\x86\x9a\xe5\x13\x80t\x86Bv\x01@\xda\xa0\x17P\xce\x84u\xe836}\xf8\xc0\xffc\x03\xe4\xc9+\xcc\xef\x97\xa2\xae\xd1\xc2\xf4&\x8d\xfbL\x8f*\xd2\x8f`Qe\xcc\xf9\xb5B7C\xf4\x0c\xfcz\x8e\x19\xf3\xb8\xf2q\xe9\x1c\x83\x99\xc5*c\xae\xd7\xe0-E!\xbb'A\xa5\xd1\x9bbjD\x8d\xf1\\\xd7\x9b\xeaJ,:\x9c_\x9c\xaf.\xce\xa3\x008zB\x97\xb1\x90a\x10\xff\x9d\xde\xd9\xe5\xea\xec\xf2\x17\xbd\x90\x19\xf5\xc2\xc6\xfa\x18\x82\x9bC\xf8<<\x01\n\xb3\xe2\x8e\x9eH\xd7 \x14\xc6J\xb4\xbc0\xab\xff\xb7\xb8Y\xa0\xad\x94Y&\xc0\x1b\xf3\xc4]i\x8dR\x85\xb0\x8e/\xd0z*\x85\xda\xe7\xf2u\x02=q\x83\xbdL\x86\xe0\x9f\xd3M\x90\x14X\x19\x8b\xe3\xbb\xa8<\xda7\xfb#=CK~O\xb40r\xbdW\xd8\x08[\x93N\xfe\x1d\xdb+D\xf9X[\xd3j\x99\xc0a%\xba\xdf(\xd5\xfd\xa7\xf1\xd6\xaf4\xee'\xac\x0b;\xf9\xc1OI\x0b \xb9;\x0e,OcI\x8b|2\x18^Z\x9a{p\xb6\xdc%\xf1~\xc6\xa3\x1f\x8e\xe5\xdd*\x81\x94\xbfY\xe1\xbc\xd0R(\xa3\x91\xcf-:\xf4o\x14\xf7/K\xd2\xd2,#\xa3\x95\x11\x122\xa8Z]v\x17\xec\xf8\x04\x9e7N\xc51\\\x85{&\xc0\xad\x9d\xc7f\xc8\x97F;\x0f-A\x06\xc3m\x99\xde\\\x85\x9e\x8fGG[\xab\x12`Q\xeb\x8c\xd8v\xfb_}K7\xd3F\xfe]\xb1\xa1\x82h%q{\x8b\x9b6\x88/\xc4i }\xc07u~}\xe5\xad\xfd\xc9\x98\xe7q\xd8_}o\xf1\x92%\x9dx\x15\x9f\xd3yO\xbdX]\x1aA\xc9>t\xd6o\x93\xd3\x92\xf2\x04l\xc5\x8d\x92jz\xc1jN\xd6\xf2\xa9\x87\xfa\xb5]\x05\xcc\xf9\x1acB\xa9,\x9f\xd0\x08\x05\xb7\x962\xec\xdb\xb6\xe2\x16b\xc6\xd5\x942H\x05KfI\x06\x7f\x9c\x98\xa8\xc0\xd5\x9c\xa2\x0c\x13\xa3\xe7U\x8e\xb55;'Nk\xe6\xd0\x9d;\xd4%^\x14\xbd\xd5\xf7\x92QN\x8e.\x1c`\x079m\xe3\x9e\x8a\xfe\xed\xa2\xad\xe0y>\xe6\xe23\xdc\xf8u\xa7=\xa3\xf6\xa1\x98\xb4\x17g\xa9\xf4\x1dA\xa8Z\xe4\xf6\x88_\xfc)\xf8\xd5N\xcf,\xea\xb4\xabS\xf2\xd2\xe0v\x10\x90\x82\xbd\xb3\xe1\xc1g\xc8>\x120\x0c{\x1d\xbd\x1c\xd1\x7fd\xb4\xbf\x82|\xf7\x9f\xd0\xa7\x1e\x82\xc5`H\xc0\x94F3p0$H.\x0f]v3\xaa\x9b\x1c\x83EW}\xba4\x12O`_\xb5!H5\xd1 \x9a\x0c\xaa\xcd\x04\x8cE\xe7M:\xe1\x08\xfe\xefQ\xab\x02\xfe\xb7A\xeb\xb6k\xbb\x05{\xef\x8e\xde\x84\xcb\x9c\xb2\x8f\x04\xd7U\xf9\x9aQ:\xbe\xf51\xf1\x1a\xaaW\x97uR\xdd\xe7\xf59\x974\xb7\xfc5s\xd0\xc4P\xdf\xdd\"\xd7\x96\xc2\xdab7x\xb8;\xfc\x01\xfa'\x00\x00\xff\xffPK\x07\x08]\x12r 9\x03\x00\x00T \x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00 \x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8\xec\xfdyw\xdb6\xf68\x8c\xff\xffy\x15\xd7\xfa\xf6\x9b!kZ\xb1\x9d\xa5\xad\x13\xc5\x93\xc5m\xb3g\xe2\xa4\xcb\xa8\x1a\x1fZ\x82,6\x14\xa8\x90\x90m\xb5\xf2\xef\xb5\xff\x0e.\x00\x12$\x01\x10r\xdc\x99\xf9<\xcf\xc3s\xdaX\\\xb0\\\\\\\xdc\xfdn\xc1tI\xc7,\xc9h@\"`!\xfc\xf9?\x00\x00\xbd\xec\xf4w2f=\x18\x0c\x80\xad\x16$\x9b\x02\xb9\\d9+\xe0\xd6-\xd3\xd3y6Y\xa6\x04\x0e\xe5\x1f}\xf5\xf6\x00X\x10\xc2\x01\xf4T7\xfaG\x132M(\xe1-\x8a\xbf\xfa\xf1|\x02\x87\xf2G0\x1c\xe1\x80\x0e\\\x839T\x7f\xf5\x8f/\xe2\xb33\x92\x7f|\xfedI'));&\xe6'\xffs\x15\xb0YRD\xd5\xf4\xd5\xd4s\xc2\x969\xd5\xc0\xa2\x1e\xf0\xeb<\xce\x81\xc1\x00\xfe\xbcz\xf0?\xe5M\xf5*\xd0 \xd7_\xe6W2\x85\x80\x0d\xf3Q\xa8\xda\xe5?\x14t\x1e\xd4^\xe5mg|t\xc3|\xc4\xbb\xa8=\xc4\xb6\x0e \x8fZw\xd3\x03\xd8\xdak\xdf\x96]\x1c\xc0\x9fW\xb5gW\xf5N\xe5\xa8\x08\x1f\xd58N\xd3 S\x83\x8b \x8b@\xfbEC\xfe3\x85\x01l\xedj\x0f\xca\xd6\xaand\x9b\xb4?\x87\x01\x90\x08h\x7f\xcc\xa7\xc5\xff\x98\xc0\xa0\x8ep\x11\xb4@F\xfb\x99\xc4\xc5\xf5\x1a\xde\xe2\xd2\xf7\x05J\xbc\xcb\xb3\x05\xc9\xd9J~\xd9\x86\xd08\xa3\xd3\xe4l\x99\xc7\xa7)\xb1\x80\x85.\xe7D=\xdfm??#\xec\x00\xf2:\xc4\xc2j\x8e|\x0e\xb46\x87\xe6\xe8\x15\x86 Z\x93\xfe\xc9 )^\xab\xbd\xd1\xc25\xfdR+\xc1\xe7\x1a/SV\x1f\x03\x1c\xf8}\xed\xb1\xd6\xb4? X\x04\xbd\xb8\xc7\x81\x1c\x01\xabO/k.Q\xb3;\xd9\x8c\\\x99E\x9e\xb1\x8c\xef\xca\xfe,.\xde^P\xb5F\x02\x9b\xf0\xfbz\xfb\x0b\x18@\xef\xf6$)X/\x02\x1a\xd0>'\x12w\xef\xde\x13\xaf]\x05\xc3\x06~P\xbd\xff\xde\xb2 P\xb0<\x19\xb3^59\x9d\xdc\xd0\xe0\x1b\xd5T\xd4D\xb5ZS\xf5\x8f\xbe\xbdw'\x0c\xbc\xbe3\x0f\x81\xe9+-\xb6\x08S+\xd9\x05PN#\xb6\x02\x02 -XL\xc7\x9c\xbe\xb10\x046\xcb\xb3\x0b\xa0\xe4\x02>\xac\x16\xe4(\xcf\xb3<\xe8=\x8d)\xcd\x18p\xe0B\x0c\xe34.\n\x88\x0b\x88\xcb\x1ezacG\xde\xcct\xaaG\x1c\xc1\xf3\x08)\x15\x0d\xf6\xef\xef\x87\xf5M\x94\xc0\x00\x82\x1c\x06\x90\x85|\x07\xe4\xf5\x1d\x90\xc3\x81\x01y%\x9cZ\x1bO\x1f\x8f\x01\x96M8\x96t\x98\x18\xc1\x8c\xafd9\x04|\x06|\x13\xef>\x00\n\x0f\x81\xf5SB\xcf\xd8\xec\x01\xd0\xedm\xd3G\xa0f\x8d\xc4\x99\x8e\x1e\x18\xdf\xc8\xfb\x15m\x81A\xfd\xe7z\xcd\x89\x11\xe4}\x9d@I4\xe9\x9d\xc7\xe9\x92\xf4 \xa1\x90s\x88\x05y\xff\"OX\xf9F\x18A\xb0\x1bA\xa2 \x10\xf2\xc9\xe5\xfdOd\xc5igk(\x0djo\xda\xb9%\x009.\x18\x08\xb0\xf6*E*\x16h\xdb\\\x1c\x04\xb9\xbc\xcf\xbf\xd6)H\xbd\xcf+\xbf\x1d\xa5\xef\xc4\xfaHJ\xc4\xa0\xc17\xf7\xef70\xadB,N\xca\xff\x9dX\x7f\xf7\xde\x7f\x0e\xe9\xad\x04\x84\xe8\x14\xe3=\x99\x92\x9c\xd0\xb1\"\x1b\x9c\xd7\x81Y\\\xd0\xbf18%\x84BB\x13\x96\xc4iR\x90 \xec@\xb1\\\x90<\x08kop\x12C&\xbd\xd0x\x86l1\x8e\xd3%c\xb65\x18@p\x9e%\x13\xd8\x85\x01\xe7\xd2\xe0\x10zK*N\xedI\x0f\x0e\x9a(\xcc\xe9\x1bg$+\xaep\xab\xe4\xed\xf8\xc7\x04\x0e\xf4s\xe9\xaf[R\x18@\x1cp\xec\xfa6l\xaci&\x1f\xdd\xb9\xfb]\xf3Q\"\x1f\xdd\xbd\x17\x86&>0n\xb3\x05\xea|6p\x05\xc4\x8d\x1e\xc4\xb6\xb9\xae\x87'\x16\x90\xdf\xba\x05t\x99\xa6\xb8\x92\xccr\xf6\x1cs,\xe1\x8ceN\x8a\x82\xcfs\xbe,\x18\x90\x84\xcdH\x0e\xa7D4\x90\xe5\xdaa\x14\x01?\xacz\xb0\xbd1v4\xd0\x8eT\x04\x88o5d@\xab\xd7\xf9\xe8k$\xca\xc8\x19\x16,_\x8eY\x96\x9b\xa0\x0d\x88\x0f\xe9\x92\x1c\x00i3\x85\xd0d\x1c\x0d\x8c%\xbf\x14\xdd6\xb3\x96\xd0fPw[/5\xc87'\xae\xf2PPk|\x88\xd3\xcfk\xc7\x01\x13\x92\xce\xc9 \xc2\xe0\xe4\x84\x1fT\x1b\xf2\x01\xb8\x1b*\xa0\xe7\xae\x83\xd6\xbc\xd5T+|\x85\x1e\xe7y\xbc\xd2x\xc3\"M\xc6D\xdb*\xa0o\x17f=\xae\xc5\xdc\xeb\x8b/\xf9\xceqNbV;\x99\xc20\xd2\xf1\xa4\xaf-9\xe7\xc7\x1b\xdb\xc8<\x14\x03C\x0f\xd5\xee\xc5}-6\xec\x8b\x80\x84^-\xe6\xce\x16\x97U\x8b\xbf\xfa\xb6\x989[,\xaa\x16_\xfa\xb6\x98t\xcf\xfa\xd6-\xd8J\xab\xa6\x7f\xf0m\xda@\n\xb5\xa6\xb7\x82-\xc1\x1c\x91\xe1t\xe4\xd7\xe0\xd2\xb7\xc1\x85g\x83\x85o\x83\x13\xcf\x06\xd3\xee\x15_\xaf\xb1[\xaf\xe6\xc6\xbe\xe3\x9b\xb5\xc6\xa7\xffbA.X7\x16d\xea\x8fD\xfcA\xfbI\xf1\x9c\x95\x9ck,\xee\xbc$+\xc2\xc5\xf5\xa5|\x81N\xc8%\xde(\xc4\x8d\xc7E\x91\x8d\x93\x98%\xe7\xfc\xa3T\xdc|\x9bOH\x8eo\x8d\xf9\x0d\xd5\x06\xef\xba_\xb5\xc0\x07\xd0?&\xfc\xbcJ\xda\xf4c\xca\x05\xc4\xbf\xff\xfd\xe4\xe4\xf9\xeb\xd7\x1f?<~\xf2\xea\xe8\xe4\xf9\x87\xa3\xf7\xf8\xc7\xc9\xdf\xff\xdekS\xd6E\xfb\x8b\x97G\xbf\x1e=\xb3\xbc>1t\xf0\xe6\xd9\xd1/\xd6\x0ff\xed\x0f\xde\xbe\x7fv\xf4\xde\xfa\xc19\x0c\xe0^\xfb\xf6\x1c\x06\xb0\x07\x0f\x1f\xc2\xb9A\xf1\x00\x03\x98\xc3\x0e\x18\x8e\x96\x15*\x9c\xda\xf7O\x8dZ\"\xa8\x8e\xb2\xad\xbd\xd6SC3'\xd7i\xc6F\xcb/\x9c\xd8J\xfa\xd8$g\xc4\xf6\"O\x92|dn\x91\xc8\xa3\xa1lp\xd7o;]\xf2\xd3\xcc\xf6\xf0\xd8q\x12q\xbee\xbd\x86\xdd\xb6\xf4W\x13*_\xc7l\xd6\x9f\xc7\x97\xfc\x90&R\xb2\x84\x1dT\xb4\xf0c\x88\xb3Tx8\x06\xa8O\x13Rh\x06\x0f\x81>\x80\x8c\x8b\x9f\xf90\x1b\xf1\xe3j\x98\xc160\x83\xac)A\x99{\xcd\xf6\xa9s94\x9e\x8c\xf4\x8b\xe4\x0f\x05S\xfcs\x80\x0cE\xc2\xe9\x02#\xc1cq\xba\xf2'^\x1d\x7f\xb2B\x12\x99P\xba\x9c\x9f\x92\xbc\xc6\x82\xba$o\x8a\xd0\x7f\xf4\xe8\x91 \xfc\xa0\x1a\xe5|&\x15\x1c,_\xa9\xbb\xfb\xdf\xdd\xfd\xee\xfe7\xfb\xdf\xdd\xc3\x19\xd2R\x05\xfb&~cn\x85/2m\xe3\xba\x0d|\x0c\x1e\xc2.\x1c\n o\x03\xab\xc9,\xe0\x00\xcec\x97\n\xaf\xc1\x14\xda\xdaxkb\xe2\x1aM\x05rm94\xe4Zs\xe8\x08\xa1\x1e\x1e\x0e`\x87\xe2\xc9^g\xce\x0d/3x\xc4\x01\xe85\xb0w\xd6\x95\x97\xa3z-G\xee\xb9a?\xf8\xb6\xc7\xfc\xda{\xed\x018}c\xc0!P\xce]\xcb\xc5\xd6\xf77\x83m \x9c\xf5n\x087\x9cC\x12\xef%\xa8di\x9d\xf4\xfa/\x8e\xdf\xcf9\x1dhS\xe6\xdf\xf9y\xd1\xbe\xfd\x06\x06\xb0\xdf\xbe\xfd\x9e\x9fR\x95tW\x19K\x8eW\xf3\xd3,\xe5\xeb(\xfe\xea\x8bM\x9d\x19\x8c \xcf\xc4I\xa7^0\x1cm\xaf`\x00\xef9\x8e<\xb3\x1d\x01\x1f\xcd4\x87\xcd\x92\xa2O\xc9%\xf3f\xc6?\xab\x95\xb2\xe8\xa8\x94\xc1\xa4Z(\xbe\x05\xf7j\xcb6\xe4\xdf;\xa8(\x1cB^\x9e!\x19\x1c \x91v\x9e\x86\x99Y\xb2\x9bd\xd4v\xe2z\xd2\xea\xef]T\xc19$\x81~\xcequJ\x9a\x96A\xfd\xe1\xe6>\xb7~\xf4ec\x9f\xb8\x19\x83\x866H\xb3\xf4!\xcexu\xf1\x93\xb9\x0be\x91\xe1C\xb5\"\x82\xd4!\x08\xa3\x85\xdf\x8c~tw'\x0e\xd3\xf7Hk\x87\xefG|\xcb\x90\xe1\xb3\x91a\x08\x0d\xb5\xcc@?\x13\xd5\xf0\xbcF\xf4\xb3\x07\x8c\xd5\xc9\xabCXp)^]\xbcpv\x81\x1a\xa0\xe6\x91\xa3\xb6cB\xd0 \xab\x84\xe8>\xcb\x8e\xc9g\xbc\xa5Z7\xb7\x0d\x1aP\x0b\"\xc5'\x93M\x18\x95X\xe4\x02\x181\xae4(M\xa9M\xbfut\xb9 cF&\x82A\x83,\x87DIE\xa27\xc8\xa6b\xcb\x15\x11\x7f\xfa \xa5\x1b\xf1\xe8\x00\xb5\\\xb6n\x8d\xab\xc8\xaf+_d\xfb\xf5\xcb\xe0\xdeg\x19\xcab\n\xe2r\x11\x96\xed\xb5 \xfdi\x9e\xcd\x8f(\xcbW\xe5\xcb\xc4w\x94/\xbfl\x94\x86\x81\x11} |\x9cR\x8aT\xb7\x96\xdec\xfb\xc19\xb6\xe0\xcb\x07\xa7F\x13\"4\x19\xdeo\x8cL\xff\xf5QSU\xb1\xec\x98\xe5 =s)\xdd\xb4\xc1\xf6\x86\xcf\xe5\x01=\xea\xd5{\x88\xe0c\xff\xe5\xd1\xaf\xc70\x80\xe7\xfc\xef\x9f\x1e\xbf\xfax\xc4\x7f\xfd\xce\x7f\x1d\xbd\xf9\xf0\xfe9\xfe|\x13\xd5\xfaOh\xc1Q\x1f\x06\xcdQe\xcb|Le\xf2\xd9\xb3M\xd3\xd8^\\\x7fQ\x11|''%\x00{|$\x7f\xf6\"\xe8]\xf5\x9cc\x1e\xc7\xe3\x19yO\x8a\x0e\xeb\xa8\xd6\xd5\x96\xe8\x0b?\xc4sOt-e\xbd\x8f\x14\x1fL\xf0\xfc\xd2\xdf\x1c\x88\x17+\xac\xef\xb3L\xc8\xb2a$\x1eI\xc1Q\xfbH\x9e-\xf2\x05\xd74\xca\xfe\xbb\xac\x18\xdaDR\"\xbdx\x04\xa3\xd8\xd2\x01\x98{\xc8\xf2\x0d\xba\x18wv\xc1\x82_#x\x11F\xf0km\xf1\x15\xbd\xf5\\\x133\xa6\xbf\x14-\xbf\xf4\xc7\xf4\x97\x0eL\x7fY\x1b`EI=\x9b6\x0d\xf1\xe5\x0d#\xfc\x90#\xfc\xa8\x8d\xf0/o\x18S\xf6\xbcz\xf8\"Liw\xc1\x82\x1f\xc4z\xfe\xe0\xbf\x9e?8\xd6\xf3\x87\x06\xe5b_\xb6\x96/\xfaI!Z\xc8\x08\xff\xa5\xb4\xb7\x1c\xbd\xa5\xba\x96\x8f_S\xe4\xbelko\xbf\x8a\xe0\x9f\x11\xfc\x12\xc1?\xdaJ\xd3\xe3\xa3\x7f\xa0\xc2\xd4&9\x12\xe2\x10\x1dOb\xe4\xca\xd0\xa3L'6\x1b\xb1\xaf\xcc\xd2\x83\xe2/\xa5q\xe9\x13Y\x15F\x1eR\x8cDr\x83\xd5PN\xf8\x07\xc2\xc7\xadF\x077\x19\x1auN>\xa9\xf4\xf3\x96\xf9\xa3\x80\xe1\xaf\xa0\xcb\xbb\xbb\x93\x86\xb3\xa8q\xef\xa9<\x0c\x86#\xaf\x8e2KG\xea,\xaa\x0c\x18\xff\xf04\xb0 7fm\xf0+\xdeZ\xf0\x95\xd4\xb5\x12\x12\x0cG\xa1_\xbbq\x07r\x08\xa3fR\x883\x0fy@\xd9\x05 \xdb\\\xf3\x93\xea\x8d\xdc\xfc\xc6\x1f\xd5\x1b\xd4\xfc\x86Q\xca9\xac\x84\x9cR\xf5d\x16*\xbfL\xd2\x19~\x8a\xe0|\x04\xfc\xb8O6\x92x6\x92Y\x97\x1d@/\xcc\xc2\xdc\x97OO\x08r74\x8b\xc2\x8d\xe4?7\xb0\xc5\x80\x1e\x06|(W\xd7k\x08)\xf1T\x97\x11\xc9\x9a\x99\x81\x9a\xd9D\xf0\xd2\xca\x91\xf0\x03\xa2\xb2l\xecE\x10\x0b3F\x0c\x0f\x07\x90<\x80\xd8\xeeF\x07r\x1cK\xde\xc6\x90r\xd1\nv \xe6\xb2\x95\xc5\xad\x0e\xd4b\x0b\xbd\x1e\x0b\x96\xc3\xbdQ\x84\x8a\xbb\xe5pw\xc4\xbf\x8c\x80\x84\xa5\xa6$\x86mh+\xe1\xa0%~\xa9K}\xd6zhU\xfb\x936\xab\x8c\x9et~Df\xfc\x17/\x93q\x85\xac\x90\x15+\xe7\x02\x0c\xc7\xc6\x8f\x81\x93\xa5P\x97r\xfe\xf0_X\x05\xfc\xedmx\x04 \x1c:\x1a\x07?u\xa7\xba\xacjOu]\xc1\x01|F\x07F.\xcaKL\x12\xe8L\x86{\x8d\x93\xa8\xfc\xa8}\xdb\x03M\xb2\xfc\x1ax2\xb5;\xb1*\xca\xa4y\x94\x0b_L\x8eR\x11XQ\x83\xe3M\xfd\x0c\xa3\xd5\xbe\x91\xba\xcf\x0c\x9bx\x19\xd0\xb0?\x8f\x17\xd5\xba\xbb\xda\x05m\xd2\x08Q\x0c\x1d\xa06\x10:Ts\x13b\x1d\xd2\xaf\xff\x81!\xa9-\xd0^t\xb4\xeaD\xd0\xeb\x99|\xcd\xf8\xd5\xeb5=\xf7\xf0;N\xd3\x17\xde*\xab\x85\xfbT1\xf0#/9\x1b\xc1\xa1\xb4 \\:\x7f\x95\x14\"\nfB\xc4\xf3_\xeb\xcf_\xc7\x0b\xa1\xbb\xf2\x1a\xce\xc4=\x1ce=\xae\xf9]\x0d\x14O\xdd\xd4\xaa\xe9\xaf\xf9Acf\xdf\x11\x1cwHe\xbe$\xb0%\xf5\xef\x0c-\xcc%Fm\xd9\x18%\xc1\x82j/\xeem\xa0\xa6\x97N\x08o\xa7V#\x06So\xb8\xb6f \xb8y\xf9f\x10\x868\xa1\x00=\x0f\xf4\xbb\x9bN\x10\xec\x93\xf4\xa7f[f\xc7Q\xd2'\x9f\x97qZ\xa0J\xde\xf4\x02\xd3^\xd8Ro\x07\xcc\x93#?\xf7Z\xf2\xee\xe5\x8d\x03\x11M\xa4\xd9\xb5+\x87\x07\xed&+o\xca\xc7\xda\xcd\xe6\xe7''\xb3\xb8\x98\xb5\x1a\xa8n\x97\xaf\xd4\x1e\xac\xd7B\x7f\xcco.\xe5\xb0\nu\xa3\x907\xc6\xea\xc6\x18=\xa5;\x90\xb2\xe9\xc1!\x0d\xd1\xf8\xdb \x1b\xe5Z\x81\x9e}\xe6\xb6\xf9H\\\xac\x06J\x88})#\x04\x1d\xe6\x8f>9'\xf9*\xe8T\xa8\xa8K\xb1B9\xda\x00\x83P\xec\x82Nv\"\xe3@\x98\x91 CNQ8/\x06\x94\xc3\x15o\xeeb\\\xa1\xed(\x00\xf4\xdf\x97\xfdq.\xc2c\x8f\xa8q\xda\x16\xa8\xe5gc\xee\xbc\xf1\xaaZ@\x0b\xcd\xd1\xd5\xbe\x88m\xda\x0d\xdbB\x90\xb4 \x0exg\x0d\x0f\xf9\xe6\xa5xK\xc7\x12\x10\xa9\x05\x81\x01$f\x08\x1b\xa17\x15\xc10\xc6/\x16 \xb6\x8frE*\xd1\xc7\x14<\xa8_\x1c\x9e\x9c\x13\xdd\xc2\xd8\xb4\x00\x9d\xa43\xfe{\x86<\x01\xe9\x9f\x11\xf4\x8a\\\x85\xfc \xbf\xab\xddB\x1cQ\x185\x95\x1ek\x06\x8a \x885V\xf1q\xaa\x11\x13\xbe\xa8\x0b/\xba7w\xd3\xbd-T4\xea\xf1bsM\x02\xe2\x1c\xbbj\xc0\x8c\x8fB\x9f\xa3\xbc\x1e\x1a\xfa\xa4\x86/\xcb\x1e\xdc\x86\xdd\xd2\x9fE\xfa\xbd\x84\x91zC}\xe8:\xd8\xfeY\x0e\xed\x9ff\xc4\xf9\xa7\xb4\x19tl5\x1b\xb4\xce:\xa0U\x8b\x8c\x11*\x02O_\xa1\x15q9\x0b\x99\x97b\xd5X\n\xad\x0d\xf3j\x9c\x91@\xbaZE\xa0\xe2\xfb\nF\x16\x10\xc3\xfb\x98\x9e\x118]\xc1n/\x8cpo\xe19\xb4\x1b\xd5W \x0d5\xe8[z\x1bv\xc3\x08i\xba\xf6\x02\xc5e\x94K\x18\x9f\x16\xe8z\xc8\xe0\xa1\xe4\xd8\xf8\xdb;T\x99pN\n\x16\xe75\xdd&\xa1\x13M\xb5y\x82C\xc3\xc1\xeaX\xa3\xa3\x07\xfe=&I\x1a\x04\x0cv8\x01\xbe\x0d\x94\x8bV!\x97\xcd7\xc3\x9d_JX\xfeb\xc6\x9d_\xbe\x0cwN\xcd\xbaD\x81/\x9aJ\xe9\xf1i\xc1\xf2x\xcc\x9a\x96 K\xb3'\xc4\xe5fz\xe1|z$\x9f\xea\x0f53\xd6\xf0\x1f#\x15`\x1a\x10\x12\xc1K\x8e\x19z\xdc\xc3\x19\xe9\x0c\x04\x82\x86\x15\x86\x93G\x94\x0f4M\xfb\xf0\x932g\x84\xa3\xb6gc\xa3\xcf\x8dL25\x7fY\xadG\xe9![S-U\x1e\xb2\x03\xc8\x85\x8b\xac\x15W\xa4\x8a\x88\x04t\xc80\xecn\x07=\xba\xb2\x11\n\x7f\xbc\xa3jgf\x1c\x15\xadT;\xf3\x9a\xac\x9fu\xc84Q\xe3\x14Z\x937\xbe\x95\x9956\x9bikJ \xaa7\xbd\\M\xa8/\xf4\xc3CbD\xf9Z\xdf\xb3\xb8p&\x02\x80\xa6\xa5S4\xdd\x08\x93o\xa9\x02\x1a\xbd|\xe9\xc6\x12\x9d\x8a\x9dU\x99\xaa\"\xc9V\xeb;-\x11;-\xe1;-{\x00\x89;\x16:\xe6\xdf\xe3bf\xb0\x03 \x1c@b\xd1\xf35vf<\x8a n\xee\xc6\xc4\xa8\xb4\xb5\n\xa3\x89\x17\xc8\xae\xb3=%\xb8\xac\xfbS\x03\xa1uw\xe6\x9d{8\xb9\x89=\xbc\xd9*(\xc8\xa1\xa65\xfb\xf7\xed\xf9\x98\xef\xf9\xd8o\x8fk\x8b8\x9cU\x87\x1c\x95\x87\x1c5\xee\x8b\xd2[\xc5c\xad\x91\xf7\x0dk\xbb\xb2&4iB\x86\x85{V\xd8\xf2SP7\xcb\x86v\x94\xb1\xe8$\x9e\x04\xd4\"\x83\x96\xbb8{\x00[\x01F\x9cKyT\x08\xa4\x18\x8b\xb7'\xb4\x10A&d\xe2\x08\xf2\xedm\xb9\xab\x1e\xd8\xa5\x91\xbc s#L+}\xf5\x8d\x025\xcb7\x86\xaaE\x9d\xf3D\xd7\x12\x8b\xed\xf2\xbd\xa5Y\xcb\nl\xbe\xd5\x98\xb6\x0e\x1dZ\x0e\\$\xe1\x8c\x8e{@,\x8dX(\xaf\x8d\x10\xe4\x12\xe5\xf3\xff\x02\x94\xaf\x0e\x15\xfd\x14)C\x08D\xca\xa2\xb6\x83\x80~\xa0\x94\xc6\xa8\x07\x1e\xcc[6LF\x11'T\xadC\xc226\xbeK\xa8\xa6%\x12\xbb\xe4A\x17\xdd\xa4.m\x12\x9a\xd8\x86\xc9H\x84C\x96c\x8b\xeb\x03;\xcdI\xfc\xa9\xbd\xa06lk\x1d[\xc6\xe5\xfd\x8f\xed\xbe\xc6\xc2Z \x9ai\xb1\x8d/\xdf\x08\xab\x8a+\x01\x8f\xaac\xb5Ka\xd8\xbdQA\xc1\x0d\x11\xa5\x02\x9eC\xb1(\x82\xf2\xe4\x1e6\xbe\xe6\xb4.+\xf67\x1f\xfa3\xbcsI\x03\xe6\xe4\xfa.v\x0dA\x1b\x0e\xa1\xf7\x9e,H\xcc`8\xea\xc1A\xf5\x0b\xbd \x98\xa6\x16\xda\x86^u\x0f\xbf\xe5wX2'\x05\xb4\x9d\x8e\xe7\xd7g\xcaML\xb8\x18\x82\x81\x01\xaf\xf5\x93\xd0q\xba\x9c\x10o.|Ft\xc5W;*\xab\xd1<\xa6,\xf0\x99Hm\xffpPYQ^\x8b\xd9\x13S\x85\x03\xa5\xad\xab\x8d\xec\x83\xb0\x13\xc3\x8e\x08\xa6k2\n\xcd\x91\xe6\xe4\x9c\xe4\xc5&n\xda\x1dp\x9d\x90\xcb\xb7\xd3\xeb\x83\x15\x0eQc\xb8\xb3\xe7\xec&\x8d\x0b\xf6\xfc\x06\xba\xaa0\xb4\xb3\xcb\xeb\x0bS*UT\xb9\xc4\x98+\xcaJ\xb0\xca\x03\xa36\\\xda<\xd1\xa8S A\xbd\xe6\xb2\xb9\x94\xb3\x11\xab\xba\x19\xb1Vl&<\x04\xaa(N\xc5\x02Q \x89\xd0\x98\xf0F]7\"~xP\xd8\x1a4\xa5\x91\xd2\x13\x0fI]\xf5\x0e\x87m\xcc\xd4\xa6z\xde\xb6\xf7s\xfa\xbe\x92\xf4}u\xc3\xf4\x1dU\xc6\x8a\xbc\x8b\x1f\x1au\x17\xda\xddm\xe8\xf5\xfb\xfd\xea.\xa1\x13\xd8\x86@\x08\x15\xeaE\xb2\xe0\xed\xc1\xe9\xaa\xf69Y\xf0\x86{!\x9e\x07\xed\x93`u\xb3'\x81\x1an\xa5\x8b\x84\xaf\xebCi\x9d\x11\xabk\x9d\x11\x8as\x08\x08\xec\xe8}\x87p[\xeb\xcf\xba?0@zW\x18\xe452!n\xf05B\x9d\xf84\xcd\x0c\xb6\x87\xc6\x90\xbd\xcf\x9d\xc6\xa1Rv\xaa\x1d.\xe8R \x02\xb2\xcb\xa7\x91\xb0\x15\xe0\x19S\xdd\x0d\xe1\xe1\xa0\xf4-]\x91`7\x82\xddP\x1eO+\x89\xdcg\x84\x05\xbaU@\x99\x0c\xf8}f\xb8\x8f k\x9f]\xab\xeb\x1c6\xe7eTemy,\xf6-\xf8\xbf:\x92\x0c\x06|.vi@d\x17p\xaf3\x94\xf6D\xb5\xd0\xb5\xf3 4\x13mp\x89\x03\xed\xc3j\xf5\x85\xe7#\x0eGB\xd4@sV7s\x16V\xd8\x8dz\xc3J$\xe0\x90\x93\xf2`k\x03S\xf8\x1a\xf3\xe0iw\xeb*G\xeaT9\xd6%\xc4\x08\x12\xa3\x06\xd1\xbcl\x19l\x8b\x11\xed\xf0\x01\xe4\xfe\x0b\xd4\x92\xd7\x8c\x00\xdc\xfc\x00\xae\x80g\x1co\x03\xa0\x969\xf9\x02\xd9\x0c\xce\x9b8\xec\x95 \x9d9\xd5!\x0d\xe8\xf3E\x7f\x84\x16\xc9\xbf\x98\x03P\xca\x17\x94\xd7c\x1f\x91kuC\x0c\xc1\x8a4\x16F\xf8}\xc8\x1fe\xb8\x1d\x9aU\xc5\x13\xfegy_\x92,\xf9 \x9eq\xe7ed\x91\x81\x8f8%*\x9d\xd3 \x89\xe0\x94\xe0\x9f\x17\xd5\x9fG\xea\xcfSRF\xf4\x887\xb5@\x1e\xf1\xbe\x0c\xf29jH0|\xa1/\x89-\xbb\x04\x9el\xc9|\x89 &v\xf6\xab\xd3\x8e\xdf\x0b\xaa$,\x11\xec\x87*\x7f\x06\xbe~\xe0\xbfk\xee\xdf\xbbw\xe7\x1e\xdc\xe2\xe7\xd9\x9a\x13s\xfb\xc6)\xdfd\xe2M;\x92\xe3^\xd9F\xb7\xbbG\x8f\x1e\xc1\xde\xfdP\xde\xe1O\x02V\xde|\xf8\x10\xf6\xee\x8b\xdc3!\xac\x9b\xce\xf8\xb6P\xa6\xe3._Il\x1en\xc1\xde\xee7w\xbe\xb9\xbb\xf7\xed\xfe]X\xc3\x9d\xfd\xfd\xbd\xfd\xfd{w\xbf\xe1O\xfc\x9c2\x9fZ:\xd2)&\xac\xd7\x8e\xe0\xeb\x92\x86Z4\xd5\xdd>\x8f\xaa\xa3\xb6\x07\xa3\xbb\xe3\xae\x9e\xb7\x9a#4Px\xc5\x18\xa8qY\xe6P\xa5=\x18\xd8}\xce\x12\xf4)\xdc\x92C\x15\x0e;\xc2\xa7\xc21P\xd0\xf0t\x17\xd66\xe7(q\xec\x8d\xe0\xbd\x80\xf5\x1b\x993\x83`:\x1cxF0\xf1\x19>\xe7T\x1c\x1b\xe7K}\x9d,\x0bp :\xdb\x08\xc7gq1{\x9aM\x88\x06\x19u\xcb\xa4\\\xc4\x96\xaa\x90-\x1d\xa4\x9e \xb43\x9e\x1f\x9a\xbe\xaa\x08\xbfw\xc2c\x8d\x84a\x97\x1a3\xa9\x9c\x0b\xcb\xaf\xc9\xf09\x19y}\xb9\xf5\xd6:n\xb05\xceOS\xb4q?/\x8e\xaaT\xd8\xe8\x0egz\xe25\x16[g\xdd\xe0\xd5\xbf\x96\xa3\xa0\xd9\x84|X-\xf8\x96\xdb\x0d\xa1\xb8H\xd8x\x06Au\xbf\xab)~\x8d\xe3\x82\xc0\xdeA\xe7{\xa0\xd1\xfe\xfe\x92&\x9f\x97\xe4\xf93\xfb\x1c\xd5\x85\xcd\x7f\xb7a\xf3\x93l\x8c\x01\xc3G)\xe1\xff\x88\xc96n\x96cp6mVj\x83\xdcR\xdaj\x19\xdf3\x7f\xcd\x97k{\xfb5\x89\xf4\xa3\xef\x16\xbc\x16{\xff5\xee}G\x88\xc8\x07\x12r\xac/\xa4,z=G\xd7\x06\n=V6\xd5\x01\xfe@\x97\xe7\xa6\xc7`\xefMFw\xc8%#\xb4H\xaa@\xc2\x02\xe2\x9c`\x92\xe38M\xb3\x0b2\x81\xb8\x80OdU\xf4\x9b\x89\xb3\x9b\xdd\xf3\x0de-n\xf1\xdc\x98\xc3X\xbf|\xd2\x11\xab\xab\xbb*\x86~iI\x8c;\xde\x94|\xbay\xf1\x01\xcc~\xb1\xea\xc2\x15j\xac\xc3\xa6$C\xb2\xc9Z$\x89\xc6\xc1\x9b>\x08\xad\x0d\xb9\xd5m\xfa\xa5\xcb\xda\xfe=\xf7\xe3\xc5\"]I6\xde\x12\xd1\xaf_W\x91\x83L\xf23\xb0\x03\xb2\xddD\xb0\xe6\x94^\x91\xbc\x16\xde\x7f\xa4\x08!\x96AA\x18\xc4@\xf9>\xa8 \xa7\xc6\x08\x19\x95{\xc2\x89\xfa\xfc*\xe7`\x9f\xfd\x06\xf4\xc4y\xeaot\xda+\xe5kI\xd68\xc3\xa0e\xb41\xe6\x03h@\xeb'4]\xf1&\x85\xd6\x14\xd5\xa4c\xe1\xd4{J\x80s\x0fd\xd2\xf7\xf4\"\xfdd\xe1\xedKu\x0c\x13\x8c\x92f\xa1 \xf5b\x16\xfc\x85;{\xf0\xb5HU\xd8\x1f\xcf\xe2\x9c3/\x8fY@Q\x98\xb1\x8aG\xc7\xa4\xed#\xad\xff\xe2\xbd?&U\xc6\x84\xa48*ic\x9bj\xbc\xf5\xdaa,_9\xf0V\xa9;\x8d4\xf3\xcf\xab\x08z\x7f\xefE\x82]\xb4\xea\x04\xc6\xb18\xe2]{\\\xf6cs\xf57\xa0Y\xd8\x16\x97\xdf\x91\x08>XE\xe6\x9fI\xfc\xe9u\xdc\xd02\n\x06/xGd\xe6\x02\xf9\x92\xa1qqF\xb6\xa1\xfc\x1c;<9I\xe6\xf3%\x92p\x8em''\x8d\x14\xed\x1d)\"\x03lE\xfc\x0e\x9e\x93&\xd2\xf3\xfe\x7f\xe7o\xec\xdd7$\xa6\xe4\x0f\xf6\xef\x192\x1f\xbf\xb7\x0cY\xb2\xf86)\xfa\x95e\x03\x9c\x91@\xc4f\xa1tV\xb9\xcd/H>\xcd\xf2\xb9P\x7f\xc7\xa2\x8d\x8b\x84\xcd \xa6\x90\xd0iB\x13F\xa0H\xfe \xbe;\xf0\xa3[\x8cw&\x0d\xfbE$\x0d\xfb\x8cMp\xfeb\x1c\x94\xf9\xd3\xf9\xb3>\x1f\xd9\xeb%\x8byO\x85\x16\xd6\xd2\xa5\xab\xce\xad\xe9\xed^\x91\x80*-?\xedO\xb3\xfc(\x1e\xcfj\xf1V\xc6@\x06u)R\x8a\xdc\x15m\xa9\x9b\xd4e\x8a\x82\xf6\x03\xe7g\xef\\ \x7f\x90\x8el\xe6\x1fI\x04'|\x9e\x1f\x89G2\x9d\xd2| B\x8a\xcb\x038r\xa9\x88\\\x8bd%!\x1d\x15\x86`{\x00\xfb]\xa2\x14\xda\x85\xe1Q\x95@\xc6p,\xbfN\x8a\"\xa1g\x82 \xc3^?\x91\x95\xc8f\xc1\x86\xd4\x94fR]\x82y\xe6/E\xfcU\xde\x97-\xdc\xbds\x9d\x11\xfc\xd76_\n\x85\xa7\x96\x01\xeau\xbc\xb0\xa6<\xfb\xf8\x85\x96\xc5\x93<\xcb*\x959\xff\x81\xa2s\x19K#\xf26\x85&\x93b\xad\xebb\xa3\xae\xff\xa1'\x85r\xcf\xa9 \xec9\xdd\xa0i\x9c\xc8r1\x89\x19y\x8e/\xaf\x0c\xd5\x0cm\xdfn\xba\xb29\x99g\xe7\xa4S\xd26\xccz\xe5nxBR\xc2'\xe0\xdbtk\xd6\xbeS^m:e\xd1IsA\xdc\x89\xa3\x85\x08Y\x92\x17\xa5G;\x94\xae \xa12\xce\x94\x13\x18\x92\x91l\xd4c,m\xf4\xb0\x8c\x06\x83]\xd1)R\xc6b\n\x14w\xf8\xc8\x96$\xda'\x91\xc4\xb9\x8c\x03\x15\xa6\x8d\x95]'\x1aw\xfa\xe2qr\x17K?<;Q<\x97)c\x12YM\xcbb\xd6RW\x01\x03\xc8\x82\xa5\x83\x06\xca\xe5*p\x02K\xe9\xac\xdb\x8e!\x03\xab\xd4qF\x82\x04cH\xd0p\xc3\xf7n\x04\xbd\x84\x9e\xc7i2\xe1\x94\xf8]\xccf69\x88\xcf&\x85\x01\xc4.\x0fT\xfe\xd2XNy\xc5\xa7\x8c\xd4*\xe5\xfb\xc9\xfe\x01?\x07I0\xae\x16\xd0\xa9(\x9d\xe2\xec\xc7r\xf6\xe2\xd7\x8a\xff\x92\xbb=H9\xbe\x06I\xc5\xcb\xb0\x10\xcf\x8e4\x82\xa9\x81\x07\x90{\x9eR\xd4\xe9Z\"\x1ee\xdfy\xd9\x9b\xe4\x9aZu\xd0\x1a;`\x9c\x92\xd8Y\x94Hk\xbc\xed\x16\xc3\x84?\x84Ym\xc0:\xea\x8d\xb3\xee\xf6k2P\xe7\x04J\x8b,_\xa9\xb8x-t\x11&\x06@\x8e\x86 b\xb1\xfeE\\<\x16\xf44@\x1f\xb6\xfe\xc9 \xa1\xc52'o9\xbd\x0e\xea\xc4[\xb1R\xce\x81\x97\xbd{\xee\xc1\xd6\xf9P?7\xf4\xd1pQ\xec\xd2\x0d\xb6\xb8x\xae41\x9b\xf5\xaf\xf7\xd3\xb12%\xc86\xebA\x9e[\xce\xb67spR\x1a\x11r\x01/\xfde\x9e\x8d\xbc\xd0\xbe\xd4\x89Y;\xdcKo\x1b\x94\x03\xdb\x99E:\x88\x08\xba3\x93\x80a\x82\x19\x86\x19eL6\xf7H\x94}\xea\x80\x80\xb6\xda\x9d{K\xed\x98\x8a\xc11`+?\xd2\xfeI*\xd6Fgk\xa2*\xaf\x03\xb24\xc8\xe15\x1a\xd2r?\xe8\x0c\xce\x9edp\x0c\xd3I\n.\xb9\x0f\xe0\xb3\xc1s\xe8{\x12\x01\xb2W\x8dd\xc0\xaf\x1f\xbf\xb3TO{\xc2\xdf\xd6\x81dS\x0f\xfedO\xfc\x81\xc3oOH&*j\x19\x1f\xac5>\x9c @,\x9d\x9c&l\x8e\xe0PN\xb14\x13.\xc8\xd4\xab\xcf\x9f\xaf\xd3\xe78[Rv\xed._\\\xa7\xcbOd\xf5\xa3`\x8aY\x0b\xba~\xdd\xfezs\xdd\xae\xbc;}\xd9\xdd\xe9 \x13\xa5FK\xa7\xe6*\xc2\x86V\xbe\xcd\xf1\xf8\x93H\xd3\xa9(\xcaW$\x90\xbf\xfc\xb4\xa1?t\xa6x\x14\x15\x90D\xc6\xaaVRJ[\xb3_u6k\xa6m\x1ce\xac\xe5o\xd1\xab\xf8\xc0\xe6\x8eyr\xb2\xc8\xc9\xb9\xc9\x14\xec\x97\x85\xe5\x9f\xbeIQ\xeb\xc5_\x9f8\xf2\xf6fJ\xaa#\x11d\xa5H\xc7\xf0\x87F\xe9\xa8\xb8!\xa5\xbb\\\xfc\xaa\x13\xbd\xcck\n\xbf8\x93R\x7f\x8fz\xed\xe0{>\xa0\x7f\x92`\xd73\xff\xdd?\x9c\xb8z.k\x92\x9b\x8d\x9c\n\x15-\xab\xadt8\x17\xc1\xa9\xc5\x9d\x12d~\xd8\x8b\xe0\xc4\xa1\xbc\xc1\x04pL\xf5\x86\x91/\n\xbc\x11h\xcaU\xb1\xb8I\x04q\x18\xc1\x96T}T~U\xe6\x0eD\x1e\\\x19~\x18$\xb2P\xd7!\xe7\x02\xa4\xf6`g\x0fK~\x1d4\xab\xc9\xf1\xeb\xcae\n\x17zvl\xc6g\x14{U\xf9\xc6\x9fp\x9bW\x93\x1cZ\xa1'\x8a\x8f\x19\x1f\x9b\x82@m\xc8C\xea*\x8b\xb2>c\x16\x95\xd4\x07Q\x97\xb4\xd5\x14\xa4\xa5\xa3@O\xb8\\p\x08\x19\xee6\x93\xbe\xc2\x82\x8f\xd2\xe9\xa6\xd4/\x89\x05\x8d`\xe9\xe4U\xb8D%$\xb6\xc0\xf8\xe9\x01GD\xb9\x9e\x84\xf3#G\xc12\x8c\xe0(\x881\xeb\xc3\x05?'D\x0e\xd7!\xff\xcc7\x9d;cn\x1e\xaa\x95\xa8\xf4W\xe1\xf6\xd9\xba\xff\xc2\xcf\x13\x976\x80c\xea[l\xcc\xf2\x08\x1b\x0c\xf8\x02h\xac\xf3\x8br\xa6\xb2\xbaP\x04\x99\xc9\x96\x83\xbbW$\xde\x0e\xaa$_U\xcb\x07\xda\xdf\x8f\x1e=\xe2\xf4\xe3\x16\x9c\x99\xf7\xf9\xb2\xde\x08\xba\xe9k\x1fY),\x1f\xef\x8f8^\xaci\x1b\xc3Z\xfc\xb1\xc4qI\xbd\xea\xb0\x82\nl\xc3\xb9\x84\xccH\xe8\x15\x07\xf5\xd5\xcdB\xfe\xe5C\xf1\x1d\xe1+\x0d\x070L\" \xbeK\x9e3\x17\xbd\xac\x12k`\xf5\x82Z\x86\x02Z\x9a\xe8:\x12\xdfph\xd1a2\xb2\xd3\xcc\x02M\xb46\xeds\x1c,\xd1-:\xe0\xaf\x15\xf5\x8c\xc6>~ \xd3V4\xa1\xba\xae\xc2\x90\x1f_\x8be1\x0b\x0c\x9eEV\xf2\x12+\xa0e~@\xce\x9c@.w=zmUj\x95[\xb7\x00\xb3\xb0\xd6\xd4+\"'c\x99\xd8Wl\x7f?\xce\x12\xc1S\x82\xc9h\x87\xbc\xa3QX\xe3\xc8\x98\x0fG\xa6.\xe5l\xc0\x86\xb6\x04x\xea\xca\x10\xab%\xf9'5\x115FEKl\xad\xfe\x01F.J]\n\xd9\xcd\xb4\x99wU8\x8d\xf2|\n\x0b\x90\xd1a\x9a\x82W\xc9\x99\xd6\x8e\xb9d\xb7\xe0\xb8\x85\x14\xa9\xe8\xb2\xf9\x1f\"\x7f\x9dJ\xdb\xff\x0e\xec\xc1!L\xfa\x8bLT\x82\x98\x0cSN\x8dZ7\x86|\xe4\x9c\x1f\x9f\x08\x06S\xfc\x0e#\xec9hh\xff&\x95)\\ \xcc\x11L\xbaX\xd2\xab\x08~\xbc693F\x97!vY6+\n\xf5\\\\ \x82z\xfdp\x11\xf9IP\xf6\xb1hF\x12EC\x84\xa6\xd7J\xd8x\xc3\\\xce\xb9%\xb8\xbb24\x1b\x95\xb3\xc3%\x13\x8f03\xf2H\xc4q \x19\x89\x99\xd8\x89&x\xaeM\x17k\x99\xa1U\x02\xe8\xa7$\xc8m\xa0\xd2\x04D&Y\x1e\x8a@b\x0e\xa9\xb2P\xf0]\x9a\x9f\xa7u\x18\x9a_\x1acL\xe5\xd6\x00\x82\x14n\x81 \xb5\x91\xae!\xa1\xce\x1a\xca\x1c3AUtz\xc9D\x93\x08|s\xe7\x0b5B\\.\xf3;|\xef\x8d\xe1\x10\x16\xc3\xe9\x08\xdc!\xeb3\xa1(\x9b\x08\x0b\x8cX\xe8\xfaZ\x99g'\xd4\x04\x13\x8f\x83B\xc0\x01E\x97\x85F\xde\xc7N\xf2\xeep\xf3\xaaU\xfc\x92\x0c\x01\xdf\xcf\xa2\xde\xcc<\x8c\x103v\x1fHV\x9f>\x80%\xa6\xf9\xe1\xb81\x80\xbd\x10\xe2\xe1r\x84hp\x0b5\x0bl\x98lo\x8f\x1c5\xeb@\x13J\x87\xf9H\xa8\xb8\x84/|\x80 \x05\xb7\xb1\xda\x98\x81\x90\xf0\xc7\x8b\x08\xd2\x08\x96\x11\xcc,\x90\x94\xe79\xff\xbf\x08S/\xa1\xc4\xe5?\x16,\x86{\xf0/\x98j\x9c\x8b\xba\xe3h\x0f?\xde357\xab\xda\x99\x99\x11\xf1tSr\x7f\"\xd1m\x86\x14\xfc\x00R\xf8\x17\x92\xfd\x14\xd6`\xc1\xd0\x0b\xed\x93\x82\x05\x8b\x08\xa6\x11\xcc\"8\x0d\x9b\x01\xf8\x1d\xe2\xc7yY\xed\xa3\xf2\x80\xb0\x1f\xb5B\xbdZ\xa6\xbf\xc9\xb5\x08Z!\xc5P\x80O\xb9\xa7\x1eb\x99=Q\xf3\xacslz\x97\x88\xf6\xf5\x0e\xdd*\x8d\xa4\xfa\xcc1\x06\xb7\xa2#\xe9\x92\x16\xf0%\xb5L5\x00\xa8\xbbn\x19\xa2\x81_0\x80\xafH\x90X\xed\xe7\xe0\x14\x17\xc6\x19e \xdd\xa8\xf8C\xbb\x7f\xedW_\xf8\xccv\xecj\xa8\xb6\xa7mct\xe6J\xb5\xe6Im\x10\x90:0\xf9*\xa7|\x06s\xb8\x0dw\xdb-\x8f\xd5\xb3\xfd\xf6\xb3i\xf9\x9d\xcds\x7fa\xf1\x188\x97\xb1CG\xc6\x80a\xe4\x9b\xbb\xf3XZ\xe4\xea \xe6\xc9+\xa9\x9d\x99/\xa4\x18:\xec\xaa\xe7D\xdd5\x1e\xc4`r\xa9\x03\n^\x89\xe3:\x87G\"kt\x0e\x0fa\x0e\x87p\x81\x99\x07\xf2\x08U\x0c\x18g\x8a\x85 X@\xfb,\x13\xf2w\x88ei\xd9\xc6n1\xe8'r\x9c\xfc!z6\xa4\x01\xe9\xd2\xf4\x96\x9a\xda\x0e\x7f\x13\x93\x17\x89\x9f\xa7\xc5\xc4\xed0\xa2\xe5\x01\x99\xb1\x8e< \x0b\x16\xc1\x05\xe1l2\xf3\xc8\x03\xa2 \x1f\x81=\xc6r\xc1\xb4#\xeeKsZ\xbcJ\n\x06\xc3^\x04\xbdQ;\xa9E\xad'\xcf\xa4\x16\x89\xaa\x15_%\xc5\x0f\xcb\xac\xe4\xa4\x9e\x95\xdcq\x9ar\x01\xb6d-1I3\x8e<\xcb\x93\xb3\xc4\xe6\xd9\xa6d.\xde\x13\xed\x8b2\xa1\x04n\xc1\x99!\x14\xd2\n '\x0c6\xcb\xae\xe1k\xbf@\x901\x04\x99d\xabjU\xf3\x1dE\xa00\xb1\x7f\xe5\xc4\xc6\xe0\xa1\x96\x0dvs\x975\xc0c\xe1!\xec\xc2!|\x92\x19\x0cq\x9b\xed\xca\x08SqsW\xa8\x1f\xf7\xc43f\x8c.\x03\xb0'\xd8c\xe8\xfb\xa4\x16\xd3\xfcNe\xcf9aq\x92\xba\x19*\xe5\xdeo})q\x06\n \x14\xdfb\x94\xc08^\xc4\xe3\x84\xad\x84A|\x00\x97Xo\xbb\x195 \xe4A\x14\xb12\xf1R\xd6x\x89\xf4ORrN\xd2\xea]\xfb\"n%~\xe1\x06\x89\x08\x9b\xa8BL\xcbuV^\xf6b\x14\x1c^\x9b\xb8\xdc;7\xd3\x05\x82E\xac\x14~\xad \xa4\xcf13z\x17^\xb9\xe2,k\xdbj\xb3\xf4-H \xcaJ\x1c\x9aU\x03 \xcb,\x992T\\h2\xaf\xcah\xaf^R\xba\x0d\xf1p\x91&c\xe4\xdb\xf6lQ\xbb\xb5\xc1&\xb4 \xf9&d\xa0\xd1\xcbn'8\xfe\x0d\xc9$tjZ\xfeTK\xab'\x9b\xc0\x15\xe6\xf8\xd3\xc8>!%%\x81j\xd7NE\xc1\x19)'(\x16\xcbb\xd6\x05 %\xbcU\x11\xfa\x96]\xae\xc1\xc9\xca \xe1\x1b\x16\xbai%\xe0\x9f\x90\x11\x91dQ\xd9R-;\xbe\xe6\x16\xbc\x8b2\xbb\x96\x16\x11%w*\xe8*l\xe3\x1e\x1e\xe6^%\xd9\xea`\xcb|\xf3:|R\x87\xecn\x04;{\xeeV\x97\x14wWW\xcb\xad\xf5\xb8\x16\xb0\xad\xa1a\x9f\xf0\xc8\xd9\xf1\x05\xb3#\xfbd\x99HnH7\x07\xb1\x17(\x9a@\xee\x00\xf0&\x89W\x1e\xfb'^i\xf7\xe1\x95\x90\xa3\xd9\x91o\xe2\x95vw\x1b\xe4\x19y\xec\x97g\xc4\xdc\x87\xd7\xb4\xce\xaf\x93\xd7\xe3qg\x9e\x91&\x9fx,\x08\xad\xd7\x89\xa6o\xc2v\x11\x8dz\xcb\xbe\xf5\x97\xce\xbf\xa8\xee_9\"Y\xe2\xaf\xac\xfa\xe7\x1e\xddfI\x19\xca\xedi\x17gOJ\xe4\xb3\xaf\xcd\x06\x05a0\x14\xb1\xabB.\x9e\xa8\xa7\xec\xdfW\x04\x86b\xd1\xd6\x8d)\xd0F\xd9)\x9aur\xa5\xfe\xd8 _\xbc\x02\xa1s@\xa1\x04\xc1\xa2\xd7w\xa6\xd7\xad\xec\xdc\x98\xc8_\x92d\xe2\x82\x05:\x9b\x135\xb8\x9c\x1a\x87\xa3s7\x91\xc6\xdcl\x94\x90\xc2\xb4\\I\x81\x12\xf6\x00&\xac\xad\xc1\x9a\xb1v\xe2\x89W\xcf\x8f?X2O\x9c\xa3\x05]\x83\x9cM\x7f5gV<\xc0\xb1\xa3h\xac%-\xa8f\xd2\x8cn\xd3\x7f\x9d\xb3\xe1\x8c\xa9`\x90sV\x05\x83\x9c\xb32\x18\xe4\x9c\x95\x89\"\x9f\xc8\x9c\x91\xda\xbbx\xbf|[\xbd\xa5~\xe1\x8b\xa5\xfd\xed\x89\xb2\xc5i\xb7\xd5\x17\xea\x17>\xaaR{=)\xf3|U\x0f\xcadOOj\xd9\x9f\xf0\x85f\xe2\xa0'\x0d\x89\x19_\xd2\x93\xf4<\xd1r\xf6\xc8\x87z\x0e\x9d'\xb5\xa4:\xa2\x0b=\x03\xce\x13=#N\x04\xf3\xb6\x08\xf4\x84L\xb3\xdcd}\xb4iZh\xe9\xd0\x84\xde\xcc\x0c#\xdb\xca\x8d\x81\xeb\\\x86^hL\x97Y\xbb\x88\xfaC\xe1\x13e\x0e\xad\x15\x0e\x80\x8f\\\xadK=\xe1p\xc4O2s7\x99\xf4\xbb\x10\xaaHs/LT\xbd\xb0S\xf2\x18\xf4Q\x0c]\x06,,R\x1fs\xba\x15\xd7\xc0\x8c\xb0\x85\x1d\xd4q\x86!\x8e\x06\xdfJj\xa0jSe\xe3\x80\x85\x95,\xf3\x80\xf2\x12\x06p\\\xe5\xce2\xcf\x7f+1\xabTj\x8e\x13\xbb\x0f\xa0\x10.\xa6\x05\xfaIJX\x14\xa3R\xfc\xb2\x12\xe4\x0c\xddD\x96%\xf48\x8d\x0f#X6)\x98\x01G\x1fO\x19i\x1d\xef\x9d(\x1a\xd4q\x14\x83\x8c\xbf\x00S\xa5\xf5\x13\x85\xfa\x0e\x84\xcd\xdc\x08k\xee\xc4\x0b\x07\x93:\x0e\xda,J\x88\x839&\xcb\xe4\xd8\xa5\x83\xd1\x80\x82\xf8Rf\x86\x0c\x1a\xbf6DN\xb5Y\x9c('\x9b\x8ceoRY\x91\xa1\x92/\x92~mq9M\xceD\x85\x11\xc4udi\x1fog,\x82\x15\x8b8\xd3\xe0J\xa3~b?\xad*^]\x1d\xe2F\x08KEay\xb2\x1b_\xc2\x04-,\xc8\x1dQ3Ryf\x87O-\x91\x88d\x1cv\xc3\xc6\xc4\xa0\x16\xf7\xcc\xe7\xb6\x8c\xc0jc\xad\xe9q\x96\xb5rV\x16O\x13u)b\x12K\xff\xa5C\x85`\xe2x?PQ\xee\xf8\xd3\xce\xa3\x82\xf4K\x89e\xe5\xc3]\xf4\x8c\xdd\x81\xd8\xfd \xaa\x18\xf9k\x16\xbe\x11_y\x04s\xc4\x1d\xfe\xf2\xdca\x0f\x95@\xe8\xe4\xe1\xd5\x95\xa0\xe3,\x9fvZ\xee\x87SG\xd1\x11\xd0\xd4\x12X\xedq'\x85\x03N5\xdd\x9f\xc8\x96\xd1\xb3k9$\xe6\\)`\xdcvx\x97/a\xd1t\xcb\xcfPs\xdc\xb1\xac\xc2\xa9\xd5\x7f\x01S$/\xf5\x05L\xe0\xd1#\xc8\xdc\xdf\x8d1\x00f\x9b\x1f\xeb\xea\x03\xc72\x8d\xcb\x05\x1d\xdf\xf0\x82\xe2\xb9\xf6\xc0\xea`\xa1_|\xed\x8d\x19]L\x97Z\xf4\xa5M\xe8k^\x89,\xb2\xc7E\x9d.\x85|\xf3ZJUh\xe7\xcbv;\xbe\xba\xf80\xd2\x86/a\x17\x82\x83.\xf5#\x92\x8f\xe1\x00\xd2.$\x079\xf2X\xb8\xa2\x17\x98y?\x13\x87R\xc2Q\x83\xf2S;\x0b\xedn \xe0\x9c\x92co ]l=\xf6K(qaL\xf6c;D\x96\xad\xec\\\xe7\x0e\x8d\xc2\xb2T\x93\xc3\x0e\x17\x92\x96\x9a\xaa\\\xfc\xd4T\xe5\x0co(=9\xc5_U\xd6\xa3e\xa9$\xcf\xf0\x87&5&\xe2\x86\xd4\x97\xc7\xe2W=\xb9\xd7\xd2\x0b\x14G\xcc\xa5Q;c\x18\x06}\xc6\x07$\xec\xfa\\|\xf34\x85_\xb6\xa1l\x03q,\xfc\xf1er\x1ewL\x05\x11N\xf3\x0f\x15qS\x8a\xd9\xd6\x07\xc8\x0b#^j\xbe\x14\x99kc\n\x96\xb3\x83sK\x1b\xc4u\xb8td\xcc\x19\x0b\x13\x9f\xb4\xe5\x89\x8d\xa1`\xe1\xd4$\x8d\xc5 \xa5\xf2F\x05\x92\x0d\x136\xde\xb2c\x18\xc0\xd8\x1c6h[\xd1\xa2>\xf2\xf2\xf8'\x95[\xa6\xdeUT\x83\x9d\x80<\n;-\xde\x12\x0e\xcb\x9b\xcaD\x16\xeb\xe3l\xc7 \xd8\xf0\xe6\xd8\xce\xd3\x95j6\xf4\x07(c\xf0\x88\xe6\x99J\xa4\x07\xea\x9c\x05\"?\x97dK\x91+\xe5\xa3\xe2\xe2\xa5g\x1a\xc3\xa7\xf6\x91\x94\x16\xf4\x86\xedW\xb7\xac\x9a\xf9A\xf1\xe5C!\xd0(V\x10\xb6\xe1\xdc\x86t5sD\xc9DJ\xbe\x15\xbf~ \xfc\x16\xd0\x15\x07\x0b\xab\x0eJ\x1f\x06\x11\xaa\x95\xa3'\x03\xffhg\x00\xe7N\xc4\xeb*\xf3n\xad\xe8\xe5L\xd2\xa3\x05\xbd\xa8\xa83Q\xeeX\x7f\xa2\xe2\x0f,\xe5\x8d5\xb3\xbe\x9en\x07\xf33\xd8\xd9\xf6\x0e\xf6?\xf1a\xff1\xc6\x03\xb6m\xc5\x19\x96\xa5\xcc\x8c\xd8H\x91\x9b>@\xb3\xd1.\xfe\xbd\x8d!c\xbc\x05\x83\xc7\x02\xc7\x87\xb8\xb9\xbf\x92.2\x15s\xdc[j\xd8\x86\x86_\x13\xa7R\x13\xfb+\xd1#\xd5\x91i\xac\x82N\xb7a\xccG\xfd \xc4\xe7r\x1fa\xf5\xac\xb4\xbe\xe3\x0fa\xa8\x8cG\xe9H\xee*.\xd8\x8da[e\x1f(\xf8\x9f\xe7\x86\x11\x8d\x85L\xc8\x1f\x8f#QF}\xcc\x0f\x00\xf1o\x82\xff\xba&2\x15\xd2X\x82\x11\x04\xf8\xe72|\x00\x0b\x0e\x11\xec\xb9\xe0\xbb\xc9k\n\xb5\xa1\x8b\xf1\x9a\xf1n\xd2\xe5N2\xc3 \x8a\x87\x18#!\xc8\xc6RH\xdc\x07|`x[Soat\xe3\xc4\xbc\xb2X0]|s\xeb\x16\xc6\x01\xa3h6i\xa8 :h\xc5\x1c#X\x90\x90\xa7bz\x9c\xdf(\x1e\xc0\n\x1e\xc19\xff\x87S\x82.Y\xe2\x14\x060E\n\xb22+I\xd4\xc5\xbb\x9bK\x92s:\x12\xfdV\xbf\xad \xa4\xcc\xfc\x9d\xfaP\xf4|\x8e\xb4\x0b\x060\xe9\xa0L\xa0\x18|\x05\xb2\x80/\n\xc6\xac\xcfj\x8a\x93\x1c\xd9\x98e\x88g\xdd\xa3\x01,B\x8898\x16\xb8h\xf8o!\xdc\x16*\x07\x85VSR\x0f(\xda2\x85O\x96\xee\xc8\\8\xce8\xa5B\xfcp\xae\x9c\xdc\x87\xa9S\x98\xe1\x0bs\"\x84\xeeG\x8f\xf8\x81\xeeZ\x18>\x80\x13\xa4\xae\x8b\xea\xf5\x10Ns\x12\x7f\xb2\x7fu\"\x05\xb5\xed\x01\x04bK\x85\xf05\x9c\xe0&\xd9)!#\xf7\xd3\xf0\xc4,\xdc\x9a\x177\x15X\xfdH\xaa\x11E;M\x90\x16|ev`\xcc\x97(\x15\xfb\xe1\xa1\xd8\x0f\xb5\x0f\xca\xe5,8%\x90\xef+\xea\xb2#\xa9\xca\x8e1\x8ar\xe3\x94\xa4KTkT\xc7\x89`\xbbI\x8d\x9d_V\xba\x1d\xc08\xce\xca\xbd*\xd5\xdd\xabf\xbe\xeeU\x9cL\\\xb0 \x16\xe2\x0eFj6\xa3\x1b-\xc7\xf1c\xbf|\x91\xb9\x9e/\xb2\x16A_eY[\xba#B0)\xb6\x93 F \xc6\x9a\xbe'\x15\x10~$\xf7l\x82\xeb++\xfd\xc5A!RJ\x8aU\xbf\xe9\x94\x92\xb9\x88GK7@\x8f\x04\x1e)\xa7\xc9[\xb7D\x82\xa8\xca+9A\x92\xa2 \xdf\xccrcY\xa9\xb7])\xe6\x84[\xf5.*\xe5\x94\xce\xfa\x9co\xcas\xaf\xf6\xdf\xb9\xdbw\x16z|.\xdc\xe1>\xb0\xaa\xbe#\xbf\xb5\xb1\xdf\xcd\xf9\xff\xfa\xfa\x8e\x1f\xdcP,Ka\x8e\x9b\x08gk\xf0\xb5oJ\xbe\xba\xea\xe1\x9dfT\xb1+!\xaa\x14\xe1(\x02\xe1\x8f\x03\xb4\xdb\xf7OD\xea \x91;<\x15\xf6e\x8f\xdc\xe1^sz\xeeT&\xac\x842a\xc5{|\xcd\x02Q\xdd\xe6\x88\x05\xadP?K\xeb\xbf\xbb%\x0ci\xda\x89\x14KoM\xbd\x14K>8)\x1c\xfc\xbcHI\xc1,\n\xff\xa2\xe2\xf8\xf9\xd1\xba\xb4\xa9\x12\x06\"o\x93\x19o\x85~\xa2KQ\x18K\xf28\x10\xda\xd3\xea\xe7>|\x0d\x89r\xdcD\x1b\x910V\xb6\x93\x9fZDXu\xc9\xfe\xb5\xf9H\x15\x0bJk\x96}\x14\xf6Y\xf6\x92\xac\xc8\xe4\x98|\x0e\xc2\xcd)3\x19\xeeZ\xb8\x86\xb0?M\x93E\xc0;x\x1d\x8b|:\x1anr\xa2\x9b\xd7p\xb5\x8e\xb9\xba\x933:\\\xa0\xf1L\x95}c\xa10\xfe)%\x86\xe6\xdc\x1bkj\x0bND\x96J45(/\xb5X3\xabm\xa6B\x80\x18Qi\x19\x0e\xf7F]\x8b\x9d\x0b\xd5\x9eXG9\n\x91j\xdd:\x081?\xe9L\x1f+\x12Z\xb5\x10\xcbB)\xb2\x19+\xc9\xb0\xf1=\xb9\xfc\x9e(\xca!|\xc3%\xe5\xc8\xcc\x9c\x0c\x07\xe3kt\x7f\xf7\xcc\xbc\xfc\xa6\xc3\xeb\x04\xdd\x954\xaf\x93\x93eA^\x92U\x01U)\x0bE\xf1\xdaI|m\x9d\xbe\xb7\xd0tc\x8f\x9b7\xff\xec\xafm\xfe\xd5_\xdb\xfc\xc7\x8e8\xb6\x7f0W\x8aXV\x1bA\xbd{~\x83o\xf1.\xafN\xad9CR\xe6\x08\x8b9\xaa\xe2%\x9d\x0d\x9d\x97e\x92\xe5G\xb2\xfe\x19\xfa^9\x15b\xfe\x83\x05}7\xc9n\x02\x0b#\x12\x99*\x8a\xf09\xcd\xe2\xa2\xd3\x0d\x15\xf4\x8e\x12:N\x97\x13R4\xab\xda\x97-\xaa\x176kv\x16\xdb[\x1c\xc7\xe3\x19yO\x8a%\x86Q\x12\x1aaE3\xe9Q\xf8\x91\xe2\xe3Z\xd9.W\x04\x93\x12C\xcc\xce\x14P\xa7P\xadzV\x9e\x8c\xa1\xf4:\x14\xbc\xa1]\x1da-v\xa5y\xa7n:?\xa1\xef\xe5\x07\xc1\x9b.\xa9^i7UW\xa2]\xbb\x98\xaeXx?'Vu)\xbbf\xee,_\xab.\xe4RHg\x1d[uU\xfb\x0c\xdd\\\x87\xbb\x1d\xd9\x90\x00\xc3:\xd5\xbb\xda\x87{\xa3H\xfb\xbb\xe5^\xd8\xbc\xdcfQ+\x19Q\x97-\x8b\xb9\x1f>\xf2\x95\xc2\x15\xfe\x9d\xcbLp\x00\xbf[\x11\xa9v\xd3F{?ws\xba\x9d\x148o\x12\xdd|s\xd2b\xa7\x01y3\xa4\xd3\xa7\xa82\xc6\x81bbz7\xc5\xadj\xa6d\x18&\x8c\xbe\xf6\xa2\xc4Nn\x14\xedp@N\x02\xe43\xbck\x13\xa0\xac\xc3\xd9\xa6N\x83\xf2\xa0\x9a\x91\xfaXZ\x04mD)\xeb\x98\xb2\x99(\xf9\xcc\xb9\x86\xc3o:\xeb*o@i\x94\xf8\x9atR\x19t\xb4\x93\x04F\xc9\xaf\xf6\xb7\xcf\xa5OZ&h\x83\xdbE\x05}\x13\x9c4H\xc9\xef\x1cZ\xcbHC\xb6\x18)\xd0\x92\xe3\x9bq\x01\xc0\xa2NhUE\xb4\xec\xf1\xef\xbb=\xd7\xdc\x1b\x9c\xea,\x16m\xeev\xba s\xe4\xe2\xb2\x88`\x7f\xd02\xe7\xcd \xa9S\xe0\xa3y\x06\xa0sW\x1b\x8c\x13\xf4\xbd(\xa4D\xdb\x961pW\xa8Yj\x90-W:\xc1\xb2'\xd4\x04\xc8\xbc\x8f;{\xb0cHa\x0d\x92{h\xd2X+WP\xa7\xb1\xb5\xc6--_\x8f\x8d\xeb\xe0\x0e\xa9\x81\x97\xa3\xe6\xe8\x90\xff8\x0f\xd7Q\x8c\xe4*\x82-\x1b\xec\xcc\xb1E\xae\x19\x19\xcfx{\x0f^[\xfe\x0f_\x95_\xc7\xc9\x8e\x9b1k\xa2\x9a\x15\x8f\xcf\xcbD\xbd~\xc7o\x86\xc7\xd4\x8a\xf7\xb2\xb5U\x11\xc4\xccq\xfaf\x7f-;P\x8e\xa7\xcd\x0bH[\xbb\xa1\xb4P(t\x98\x0e\xa6\xc0\xe5My\xae\xc5 \xd8\xcf\x98\xa5\xb9*/t#|\xe2p\xeb\x05%5\xe8|\x02~P%R\xdc\xde\x8e \xe3\x0d\xe5\x12\x02hn\xb6\xe7\xf9\xe4Sm\xfa\x84\x81Z<7\x1f\xe1\x03\xa6&\x1f\x918*/v\x03m\x036\xc3\xd3\xf9S\xe1\\\xdc\xc9\x8d\x80\n\xca\xa8s$\x89\xfb\x0be\x08K|\xb8\x12\x906\xb1b\xb8\xeb\xb0\x9a\xa9\x0b\xb3Y\x1a\x13\x83\xeaW\x1d_\xc6h*\xd4r\x02}\xc6\x8a\x882\xb7:\"\xcf\xd8\xcap\x82U\xf01\xf3;~\xb6\x81'\xbe\xc4\x8fX\"N\xf9\x0c7r#\xe2B\xc4\x1e\xdcF\x1f\x1c\x0cDD\x9f\x1c\xf9\xfe[Y\xc1,\xeb\xcc\x9b\xc4\xd1\xe6\x9d\xa8cf\xb7'|@\ni \xc8\xe1\x04\x0c\x12X\xaf!\xe6\x7f\xc5e\x8f\x1c&}\x96 \x15\xbav\x10\x07a\x05)\xf3\xa0\xa4\x93w\x0c;&\xcc,`0\x10\x9e~\x01\xdfl\x85tD\xda\x85\x03c\xa5\x89s\xe9\xd5\xe8>vR\xc5bV\xe1\x06K\xac\xac\xa5\x8c\xa1\xcb\xca\x80\x18\xc1\x16\x9eR\x992\x8b-\xcb4>A\xda<+<\x8ea\x99\xe1\x86\xc9p\xd3*)\x10\x93E\x15\x15\x93\xb6\xcd\xe9$\xa6\x9b1\xf8\xb1\x85\x11\xa4_\xa6\xa7\xca\x9c\xe09\x96!\xda\xa4\xc2\xbcf!F\x11\xb4\xdd\xe5\xaf\xf45\xbe\x9e\xb2N\xda\xf4x\xff^K\xe4\xd6\xd3)\xb4\xd1Zm\xab\xf8\xec\xeb\xe3\xb1\xbc7|\x96\xaa\xb5z\x10B\xd6yZrxmo\x17\xf0HC\xf9\xae\x93\xd8+\xfa\x1d\xba\"\xe0\xf9u\xe5V\x13\x10T\x13tM\xa1\xe4\xaa1 \x96\xd2\xe2\x11\x0c\xb0g\x91\xa8\xa3\x13\xc9'\xcfU\x92\\\xf4\xc6\xd05\x95\x9b(\x08\xeaXk;0\x7f\xf2=0\xddd\xfb\x86x`;\x19K|\xf6\x08 \x1c.\xef\xe72\xc8\xc2E\xa7\xba\x11\xdd\xc1i\xa7\x9d\xa4J\xa4\xe4\xc6\xd3\xb2\xc9u\xa7aE\xb5\x8a\x16\xdb]\xb8\xd9\xee0\x02C\xa0\xe5\xcd\xf0\xdc7\xb0,Y\xee\xb3.\x9b0\xf7_~\xdel@\xb0p\x93\xe3\"\x19\x12\xb5\xabk\x92uP\xa4De\x1d\\JZ\x11\xd6Y\x7f\xa4\x0cY\x832d\x918\xc2\xb2.\xba\xd0-7L+\xabG\x07\x8f\xcf1\x04+\xf9\x8d\xf1/\xde\x81\xe0\xf2\x8a\x1a\xde\x8ee<\x93\x83\xbd\x87\x8bY\x92\x12\xb0:\xe5\x81\xae\x0e@\xdb\x95>\xf3\x04\xfb\xd8\x88\xe6\xf9 ?\xde\x88\xe1\xe3\x8b-\x01\x0e\xfcE:e\xa9s$\x07P\xce\x86\x04E\x07\xed9WUC\xac[\x99_\x85\x89\xb2e\x1d\n\x04\xd0\xb8\xe7-\xf4\xbcJ\xe1!\x16\xac\xb9\x05q\x80U\xfb\x90(\xa7\x18\xa8\x0d\x07*M7R\x04*\xcb\x01$()\x86\xa5$\xb1\xb5\x8b\xc59\xedxeW\x95\xf3\x85\xe5_\xb7K(\xfd\x15\xa6\x8c\xdc.\xae\x81\\\xc5aG\xa1\xf3\x1b\xa3R\x92\xadJ\xbc\x94\x14\xc4\xcbd\x02\xea\xdc\x92\xa9\xe672\xcf\xa6\xbe\xf4\x06d/\xb9\xa4\x00\xa5\xfb\xf5po\xc4%T\xd4\x10\x06K\x15O\x81\xd8\xc5\x8f\xd18H\xab#\x93\x96\x84#\x8f\xc4\xf9\x99v\x93E~-\x85sn\"K\xa3\xa5\xad\xe5u\xb6\xa0\\\xb4\x90\xac\xa3g\x97\x1di\xbb(`\xd7\xaa\xdd C\xbb\x01E\xf533\xfd\xec\xa4\xa8\xc2#\x13]@M\xf2\x8b\"\xb8Kk\xda\xe8\xccN-\xc5\x9eT\xda\x8d\x9a\x83 \xeb(\xe2$\xe1>\xccq\xe4\x99(\xbdx\x08\xe2C\xe9^\xc6\xac\xee\x83e\x96i\xeb\x11\x91\xf4\x8b,g~\xd2\xacb\xa2\x022\xbc3\x8a\x80\x0e\xef\x8c\x10\xcb\xc9p\x7f\x04;@\x87\xfb\x86\x0c\xc1aU\x90\xbc\x91\x95\xc1j\xb1I\x86l\xa4v\xd2\x00\xf6\xdbm6+\xf4\xb9\x1a\xe2\xa0\x1f\xee\x99\x06&8\xd7_e\x8d\x0f\xe1\xd6\xfdR\xfc\xfa!h(\x04m8\xf5\xc2\x89S\xc2\xdfE\xc3+\x0f\xbb\xd1\x17\xe2 \x1fJ\x89\x1bV\xbc\xc8\xc9d9\xde@\x87![\xff\x15=+\x05;G\xd1\x87S(*,\xf9\xf2\xdd\xb6\x0c\xd4\x8a\xe5&\xdfWG@\xca&\x03\xaf\x0f:\x12\x89\xf9\xcc\xc3\xf5\xf4|\xff\xd5\x8b'\x13\xf5s\xec[N%\x8f\xbfu\x0b\xa8\xa6\xbf\xad\x85M\xae\xd7U4\x82\xf8\x05[\x03\xde\xedz-b[\xbd\xc6\xfb\xb2\x8a\xbf\xf8\x02\xa1Y\xea:\xf91OH\x90\xfbz8\x97k\xd6\xf2\xb3\x04\x81\x84\xf3\x84\x06u\xcb\x14\x0c\xfc\xf6u3\x0b\x9f\xf0\xf3\xac\xce\xc4\xdfE\xbcv&Bx\xb6T\xfd\x0bM\xa2\x81Z\xfa=i\xa9\x10\xe4\x95\xd9\x92\xf0\x81\x06\x94\xf6|\xba\x05Y\xe2\xc1\xb9\xe5\x9e\xc0U\x97\x022_\x1f~2\xc1O\x01\x86\xb0W>\x97\x1c\xdf\x1d\x07\xfe\xf5\xf5m\x1e\xec\xff\x06\x9c!\xaef\xa7\x00\x86\xba \\\xce\xe4\x9a\x80\x92X\xe0\x02\x88H@\xd2/\xb29\xb9N\x07\x1c\xbd\x1c\xcd\xcb\xfaR\xffFFJ\xe5\xc7\x8c\x11\xbb\xa5\xb3\xaf,Gq](\xe2\x00]\xb3\xbcy\x81\xf8\x87\xce\\\x08\xc2\xc4\"jr\x90\xfe8\xa3\x05\xcb\x97c\xd4,\xfb\xd1\xf7\xaf,\x8e\xdeI\x99\xcdFD a\x89\x116\xcb\xb3\x0bD\xf1\x0f\xab\x059\xca\xf3,\x0fzG\x97\x0b2fd\x02\xc3\x97\x11\xfc4\x02\xb6\\\xa4\xe4\x00z\xb0\xdd\xcaHk\x19\xc3?\xdd\xd1U\xaf\x88\x8cG\x08#x\xea\x1b`\xf5\x8b\xbb\xcd\xa5\x00[^\xb1A\x19\x17x\xbd\x9a\xfe\x87\xbb\xe9z\xc4V {\xfaUc\xb88\xb7\x15j\x81\\^\xbd\x12\x8f\xea\x1c\x9c\x14\xd7\\zT\xee\xf6\xd6\x13\xb41\xce\x9aY\xdd\xf1-\xe9\xa4/\xf3\xac\xbf\xd0\xb3\xcbW\xdf\x0bm\x13k\xa7.\xb5\x8c\x9eu\xe6\xba'\xf0Hf\xa3<\x10\xc5>\xe0\x10v\xf8\x0f\xbfs\x9fZ\xb6\xf2\xb9\xf4E\xfb\xc9x\xe0\xa3\x14m\xe7\xa5\xf9\xd3\x9f=0\x1f\x8f\xc0\xd3\x94@\x96\x03\x06E\xef\xa4\xc9\xa7r\x0f\x98I\xbc\x18\x14\x1f\xb5\x81@X\x97\xd9\x0b\x16yG\xe2d\xc1A\x94$\xd0\x99SLX\xb0\x13Z\xb0\x98\x8eI6\xd5*\x9e;\x9c\"\x10r\x88\x1e\xf5Ok\xc9>\xf3\xc0\xa6z.\x9bpr\xe8\xfc\xa2\xa8\x96\xea\xd6\xb2\xc6U(\xe5'\xb2*\xac~\x89\xea\xda\xf2\xe3\xca\xf4\x8b\xe5+\x8f\xb7\xf8\xc5\x8c\x11\xae^\x9d\xa8K\xceeB\xa6 %\xef\xf2lAr\xb6\x92\x9c\xaf\x7f+\xfc:#L\x13-7\x19\x83\xbat\x12$\xc2&7j\xe2\xaa\xdb F\xbf\x8a\xdax;\x8fo\xd3uF\x1a\x89\x98#\xe8=\x8d)\xcd\x18o\x1d2\n1\x85\xa4L\xcf\x9b\x93q\x96O\xfa\xbd\x92d\x8ah;\x07\x8bi\xba\xba3\xb7\xa9\xcb\x12\x8d\xd0\xbc\xae\xfa\xa7 \x9d\x04U\xd4]\xf7gW0\x8e\xd9x\x06\x086\xf7\x80\xae\x02\xe5\x9a\xae\x8e\x88X\xea'\x90\xeb\xa7\xf1\x9c\x94\xa1\xc3\x9fD(^\x8c?&d\x1a/S\xf6\x13\xe7\x960\xe7\x8c\xb5\x1b\xfb\x00\xc4\xea\x88\x80\xc3\x8f\xa4\xa9\x98P\x97\x05q2\x94)\xcaS\xab\x15C\x9d\x99t]\xa5\xe4\xa7\xb1P\"\xda\xb1\xa9h\xd3\x7f\xb1\xe0\x1d\x8b\xe0#gL\xde\xdd\\\x95\xaew7Y\xa5\xebm>!9\x99\xbc\x8e\x17\xf0g/\x82\xdeU\xbbV\xd7\xbbk\xd4\xea:\xd7k\x04\xf0\x95\x125\xfc\xed\x90\xadyh\xc9b:\x18F\x8a\x1f\xd2PT\xa6m\xd5\xd0z\xf7o\xaenS\x96\x9d\xe1S\x92I\x95\"}\xb4\xb5{\xa1\xcc\x88\xe0\x1c\xf5f\x95\xbf~g\xae\xdaG\xef\xae_\xfbHo\xb8]\x06\xb5\xd6p-\xf5\xb8\x0f\xb0+\x90U\x9f\x06\xa8\xb8\xd1 \xa7?rv\xbf\x91nDGD+\xf2i\xa30\xd8\xd2\xba\xdc\xe8E\xbe\xb9\x80\xa1\x0e\x90\xa1\x05\xd6\x12\xde\xe57/\xbf\x12\x17\xed\xa1O\xf3l~DY\xbe\x12\xbaRM\xf9\xd3\x8d+\x9b\x15J\x10\xc2\xdf\xa0U%\xc1#\xbf6\xab\x11\x85Z\xb7V3BEH\xe4\x12\xd5?\xb2.+\xdf\xd5\xaf\x99t\xe5$\xfe\xd5\x16\xd4\xd1\xc2\xf4\x9d-\xf2^\x18$\x1a\x84dRh\x84t\x00\x1fX\x1d\xbe\xc3\x99\xaanP\x83zY\xe7\xc0\xb0o#`\xc1\x1b\x16\xc1\xafa\x04o\xaeA\x81\xdb\x82\x1fR`\x13&\xd4\x9ao\xc4\x0dt\x96K\x13m\x8b\xa2i\xce\x86Q?rL>oD3\xb0q\xf5e\x9b.\xbc\xa9\xc3\xcd+T\xe8\\\xab\xc8l\xc67\x0e\xdf\xef\x159\xdc2%\x1b\xac\x8dQ%\x1b@\xa3\x86\xf74A\xd7\x1d\x89y*+\x87=8\xfc*l\x05\x896\x80 0\xb7\x13;t\xb2h\x06\x02\xa7\x02\x9fk\x87\xcd\x06`\xc8\xaf\x03\x06\xda\x00\xc3<^\x18\xf0\x15$\x18Z\x85_\xde|\xd9\x19\x119B\x94\xda(\xa99\xe0\xd6&\xaf\x99\xf3<\x1c\x97I\xc0l1KW\x9c@\xa9|\xcb\xff\x14\xeb\x10\x8a,=e\x0fV\xd5y\xd9|\x16\xc9|\xcd\x14\x0eD1 SWa'Q\xd8\xechB\x1b\x9f\x0e\x96\xd0\x01Au<\x99\x8f\x0bZ\xd7=\xb5\x0c\x1aV\xd4m\x82\xcd\xba\xa8\x9e\nye\x19\xa2N\xef\x8bRL@\x83\x8aP\x1a\xa2\xa2Y\xac\x02\x16\xc4G\xbf\xb0\xd2\xbcbZ\x0e\xd7RT' \x0b\xde\xb3\x08^\x86\x11\xbc\xd7\x97\xca\x14\x08\xe8I\xc4\xcbh\xc06%\x7f\xffe\x9b\xab\x93\xd2\xd8\xd7\xc7\xb8\xe9\xbcy3\xdca\x08r_\x96\xcc8S?\xbc\xff\"\x84\xbd\x11\x0ce\xbe\x18\xca\x14\x862\x85\xa1\xa2\xda\x96\xc2K\xaf\x9aa,x\xc6\"\xf8!\x8c\xe0\xd9\x97s\x10\x0e\xe4{v#\xc8\xf7Wb\x18\xf3\xc7/\xe3dn\x0c\xbf\xfe\xc3HT\xe1\xcf\x86\x88\xf4Jr\xba\xaft\xe8\x10)\xcct\xf1\x10\xedu\x94,D\xb3\x9fW\xff\x95\x88\x84\xc7\xa5\xed!\xbf\xbeb\x81\xb5\x88\x9e\xe6d\x11;\xdf*\xd1\x15K\xf4\xa30 \xaa\x12\xa3\xd8Z\xdd\xdc\x157-R,\xbf\xdaz9#\xa2\x1b\x81\xfd_\x83\xe8\x1e\x91\xa1~{\x01\xca\xf0\xca\x9a[\xb8\xa3\xa2\x86Z/\xd6\xe5e\x89\xde\x95\xae\x11\x82@\x0eS\x18\xa0~)\xde%\xee|S\x0e\x1e\xf7r\x06\x87\"\x91\x8b@\x89\x1cQ\xa2\xba\xb9'n\xee\xb5\xf3\xe5\xeb\x97\xc5e\xd1\x83&\xd4\xce\xe1z\x1a\x827\xf6G\xcf\xec\x8f^\xd9\x1fa\x8e\xaa \xa7\x11\x9c\x10.ZP\xed\xcd/T\xb0.\xa9\xe4A\xb7\xa1g\xd5\xb0\xd6:\xdc\xf8\xf8\xaci\xd4\xf9\xe7o/he\xf2qw\xe6\xa9L\x10v\xd0YY\x1d\xdd\x85\xe6\xf5\xcd[\x1b\xdc\x90\x18\xe2\x94ks\xe1\xe2\xeba\xf5\xb7\xd2Y\x18b6\x9b3\xf1R\xfeV\x92\x89Qe%\xfa\xbfuK\x1b@M\x9fk\x9eli\x1f\xd7l\x03v\x9dT\xff\x84\xcc\x17l\x85br\xf9c\x001\x95\xa2\xf6/\xa4\x9d\xf2\xb41UO\x8dq{\xd1*+\xb5\xb0P\xffM\xb3j-\xe9'\x9a]P\xf8DV\xd0\xfb\x1bl\x03\x81m\xf8[\x0f2\n\xfc\x97\xc2c\x8b\x91\xbc\x06\xbd\xad\n|\xb2\x98~Y\x8b\xc3\x8c\x14\x1ez\xc3\x9a1\xa1\xbeD\x85\xd2ku\xe0V\xad,\x846\x9a\n\xe7\xe0\xa0Z\x87v\x1d\xe6\xda\x1ax*\xd7\xed\x1b\xc7OCZ\x9f\xa9\xccS\xea\xca\xac\xd8\x9a)\xeb\x9ci\xfb\xe8\xae\xcd\xf4\x86\xb4\xfd\xce>\xae\xcf\x1eX!\x91\x07\x06\\k:jZ:\x00])e1Y_uk\xd8\x8dS\xbc9v\xf3\xdf8C\xe25\xc1\xff\x84 \xa1\xbeA62\x0dT\x1b@\x06\x0d\xf8\x1a\x04\x1ap\xa8w\x82\xcc\x16z\xd7j\xc0\xb1\x15\xa8\x8c\xc5\nuxO\xd7\xed\xd3\xf2\xd7\x19a\xefT\xf3o\xa7\x9c\xb4\xd8\x11E\x1b\x7f\xde\xcc\xe4\xed\x17(\xb2\xec(\x99--\xfe\xebu\xdd\xcb\xb0\xaf\xee\xf6\xde\xa3\x93D\xcf\xab\xb3\xc2\xdd\x993'\xfd9E\xff\xde\x94\xcacgk\x1c\x94\xc9\xe9\xf9\xb3k'\xa7O\xae\x9d\x9c\xde\xc5\xc1\x97\x92t<\x99\xd8\x8b\x11\x18\xb6\xa6\x17 S7 \xb7\x82-\x04\xe1\x16\x19N\x9b9\xa4\xeb,zF+[UFK\x0bUy\x1b\xeb`\x97\x0f\xda\xe5\xb73*Jdk\xd5\xb2\xab\x9b?'\x18\xd4\xa2\x1e\xf0\x9f\xd5\xc3V\xf9m\xf5\xe0\x19!\x8bF\xf1\xed\xfa\xc3F\xb3\xeaV\xfd%c\x01\xef\x8c\x1aJ\x8dg\xd4XA\xbc\xbc\xdd\xae \x9eQ\x8f:\xe0\x19\xed\xdb\xeb\x80\xe3CW\x1dp\x16\x144\x82#\x8ey\x05\xbd1\x07\x93\x82\xa2-Yf\xd0\xf6\x96D\x02Nq\xfb\x9f\x88\xb0?\x9bZ\xbd1\xa9\xaawL\x98U\x9a*\xbeH\x9a\xaa\xb8Vg\xbb\xf1d\xe2\xdb\xee\xa4\xc0\x9aq\xac\xac\xbcC\xb7\xb7CH\x026\xa4\xa3\xb0}\xec85\x8a\xe5\xb1\xcd\x8f\x1d\x8b\xfa\xc6x\xec(\x07\xa9Z$\xc1p\xb7yx4\x96>\xa1\x8c\xe4\x05\x19\xb3\x9b]\xfe*\xa3\x12\xf3\xab\xbd.0\xc4/\xbeC6\x94\x98NeS\x18\x9f\x17\xcb~-,0\xf0\x14N\xbfg\xd6'\xe7$_y\xb4\xac\xae\x12\x1dJ#\x8cE\xf5\x0b\x02 \x90\xcd\x93\xa4\xc5\xa6$\xeefZ\x1aHR,OY\x1e\xff\x7f8\xf2o\xc2\x91\xeb\xc6ry\xa2\x08&\xb2\xbai\x14Q<\xa4\xcf1\x85`\xc43G\xab\xe5\x10\x81\x93\xebi\xf4$9H7I=/K\xaf6\xd1q\xafCM\xd3\x1e\\[\xe7T\xdf!Y\xce|y\x819\x0d~.\xbdw:Nf\xde\xee\x93\x95\x8f^\xc2\xd08\xebn\xff/\xd2 \x15\x7f\xadz\x85iZ\x85\xb61\xcf#3t\x90c\xcc\xb9\xafa\xd88\x1d?\x85Xk\xc4\x9b\xea\x80L\xf9\xb0;\xd5[\xc5\x7f^\xfb\xb3\x99\xc2G\xf65\x8f?\x91\xe0\x0bu>8\xfb\xa48FM|J\xdb*\xa01\x8d`\xcaq\xac\xf7\xf7\xbf\x9f\x9c<\x7f\xfd\xfa\xe3\x87\xc7O^\x1d\x9d\x1c\x1f}89\xf9\xfb\xdf{mG\x90\x05\x7f\xbb\xf0P\x1aM:\x11\x81X\xaa5\xb1f\xb5&\x05\x05U([j\x88\xb1\x1c\x9c<4\xa5w<\xae\xf0|\xc1V\"|\xba\x04\xa3\x9f\"b\xd6\xbd\x17\xebJ\xae\x85#\x08\xa3\xcaf\xdf(_G\xd5\xb4\x88\xc8\xea]\xad)\xf3M\xc2}\xee\xa4Kc\xcc;\x10\x8c\xf9xg40\x99j,\xed\xce\xbf@\xa5u!TZg\xb4\xd2d]\xfc\xbfM\x93u\xe6\x86_\xa9\xee3\x14X\xd4\x7f-\xe8pJ\x95\x03\xddBSj-*\xa5\xd6\xa2\xae`R?\xeb\x0f$k\xb0\xa0\xba\xcej\xe1\xa3\xf0Y\xb8\x14>\x8b.\x85\xcf\x82\xaa}\x08\x038\xa7\xf2\x06\xdf\x8a\x88\x92\x11\xb0`N9q\n#\x98\xdf\x9cFh\xfe\x97h\x84\xe67\xa9\x11\x92\xfe\xf7.\xc5\xd0\x9cV~\xfa\x82r\x9f\x19(\xf7\x8aFp\xca\xf7\xc9\xdc\x83\x16\x9flJ\xd8N\xffC\x84\xed\xc2 \xcd\x95 l+>\xde\x13\x1a<\xf7/\xbby\xf4\x05\x84\xed\xad l\x97\x1aa\xe3\xb7\xfaKZ\xcc\x92){\x9c\xa6\xbe\xd1\xfc\x97\xde\x8a\xee\xa7nE\xf7)\xad\x1clO\xf5\xbdvA\xe5\x0d\xb9\xd7Np\xaf\x1d\xd1\x08.8\xb5<\xba\xb9\xbdvt\x93\xbb\xe2\x98\xc5\xe3O0\xe4\x1bb\xd4\xde\x10G\xd7p\x05\xa9\x1b\xe3g$6\x14\xaaG\xbd\x15\xd1\x92r\x93\xf0\x81H\xbcNvv\x1e\x84\xf8\xbd\xf0\xaa\xb2\xef\x058\x04\x99\x84\xc6\x14\xf7W\x1b\xf9\x82\x90O\x1b\x01\x88\x8f\xba2\x1c\xf2_\x86\xec\x1d\xad^\x96\xc5\xac\xab\x97J\xdbP\xae\xaf\x9f\xd6\xa1\xd4\xf4\x95\xce$\xb8\xfb\xb7[\xedD\x1a\x03\xcc\x07\x1e!0\x9bo\xc1\x0e\xecq\x88?\x12j\xc3\x9d\x9d\x10?\xb3\xf1\x05\x98Y\xa5lcH-\xb9\x0f\xf9\x825\xd7\x82_\x86D\xcbu|\xb4\x04S\x96\x9c6\xae\x87\x16o\xd5\xac\x18*\xef\xd6\xcb\x9f3\xe9\xda\xff\x98\x9a\xc5\x93\xd6\xe2=\xe6\xa4\xc8C0\x91\xead\xb4u\x05$\x0c\x05G\xe4^\xbf*\x07I\x87\xd4\x82\x0c\xb8\x19\xba\x1d\x9b\xaa\xe4\xed\xcb\xf0\xa0\x0d84&\xb2\xe4\xd9P\x00*4pT\xa7\x10\xeb\xdfN\x9d\x0f-2\x8aw\xca\xc0X\xdb\xfa\xb3\xc6\xfa\xd3\xeb\xae\x7f\xdb\xfd\xba\xb5\xfeYge*\x1de\x8b4\x19\x93`\xcf\xdd\xa6<\xa66i\x97\xa3\xa1\xa7:\xca\xd4\x95\x0f\x067\xbb3\x9d\xa2\x8d\xd67\x9fF\xb6\xb8\xce,6\xb12}i|\xb6D\xa9\x06\x06m\x82W\x9c\x15q\x83\x8d#\x89\xcf\x91\xc9\x89\xca[\xe9\xe8Q\x0e\xd6\xc7\x15\x8cbq\x11\xa2\x7fe\xd6p\x7f\x08jM\xd7-TeG\x17\xa49\xfa*M\x8f5\xc6\xaf<\x99\xf2\xda\xc9\x84e\xce\xb2:\xc9\xe2\x07\xcd\x83\x10\xeff\xee\xd3\xdd\xbd\x88yc\x11\xb3k\xad\xdfcj\xaa0\xddX\xc3\xcd\xd4V\xa5.\xa9\xad\xb9\xaa\x10\x94\xe3\xeacZMH\x9f\xcc\x86a\xc8\xfa\xcc\xf6,z\xa8\xa3kkAe\xdc\x81\xbe$\xd5\xd1\xa2y~\xb9\x90\x82\x8a=\x977\x10!\xaf%\x13\xccU0\x08\xd5\x92 \xe27y\x07\x13\xe85Y?\x1d\xa9\xd7l3\xb3\x0e\xb1\x9a\xa9\xf1\xec\xcb\xfdNn\xcf\xc8\x84N\xaf\x7f\xc5O\xe4]\xf1\x03\xb2\xdf\n\xd0\x91\xf0\xec\x17\xcb`Q\xd1\x98g(Z\xead\x1e\xba\xb2\xf393\xf3\xf9D\x05\x1c\xa1\xd6\x15\x85\x9a\x01\\\x1a\xa4\xf7c\x1a\xc1S\x93\xde\xf5\xc3\xe3\xa7/-\x9a\xd7O\xfc\xfd#\x0fi\xffq\xe9\xae\xd7\x91?\xb4.\xf3\x7frf\x94\xa9\x98\xe1L\xe7\x84\xb3\xa6\xa3^V\xd1\xbf\\\xfc\xaaS\x07\xbf\x94\x81o\x9d\xa7\xee\xb1\xd0\x03\x1cs\x80<\xa6A\xcb=\xc5\xd2\xe8\xbbnq\xb1D{\xabYR;\x9c\x86\xa8\xa3cCjH\x84k\x85\xa4\x9e\xbe\x8bU\xbc1\x0d#\xa8\\&\xb5\xd0\x88\xe3\xd5\xfc4K\xb1B\x82\xeby\xb3\xadf}|\xfd\xd7':|Z\xaa\x17?\xf9h\x03?\xb9\xb4\x81\x9f\xba\xb4\x81\xbc\x0b\xdd\xb6\xf6D\xb7\xb5E@\xfb\xcf+\x02\xf91\xe2\xcbDM\xe9\xbfdJl\x8f4_\xafH\xe0bE@.8\x91\xb9qE\xa6\xed\xeah_\xaf\x8d6zh0\x06U\xbe\x07\x8b\xe9\xcdi\xdaV\xd8c\xa61\xad\x15\xc4\xbbm\x9a\xc0\xb2\xe7tB.\xc9\xe4\x98|\xf6\x00\x8cF\xe2\xdf\xcb\xa8s\xbf^^\x1c\xfb\xb7\x8e\xc01\xa6\xc2\xf6\xd1\xccc\x82\xdf\x9e\xfa\xa4\x07\x9c\x85Y-H6\xc5\xfc\xda/\x8eQ\xe7\xc8\xff\x10\x16\x1e\x0b\xf8P\xbb\xc4\xdf\xf1\x9d\xde\xdb7\xff-\x13|\xfb\xa6\x9c\xe2\xdb779\xc9\x97du\x0dAC\xf8\x13\xd8\xfa\xa4\x93F\x8f\x1eU\xa3\x10\x98\xfcS\xcc\x89\x1aX\xcc\x1b\xa0\xebI\x0f1\xa1\x89\xb9<\xb8aXB+\xb4\x19,j\xc8\x125W\x9c\xa1\x84\x8ay\xbbYh.Sc\x18\x08\xe7@|6o\xa3oRZR\x04=\x84C\xe8aE\x028\x80^\xd4\xb3c2\x83\x01\xf4\x0czTu} \xa6\xbbp\x9c\xcaR\xfd[{\xe8\xb2\xba-,%\xfc_t3\xdaR%\xa4\xb4I\xe1\x9a\x96^4x\xe6\xf4\xda\x9c%\xc8\x1d\xe0\xc5\xb7}\"\xab/ ?\xcf\xbdVt^\x93C=\xd0\xaa\xdcb\xf5\x94\x9d^\x9d\x89\xb3t\xc3\x0d\x16A\xe6\\\xe0\x06\xae\xb5\x1cT\x1e\xc2>\xe6G\xe4\x98\x02\x07b\xc3\xb6\xb6\x83\xae\x06\xc0\x9a\xb5\x0e\xe4\xc8\xe0\x10\x82LR9l.\x94\xed\x92\xb2\xf4\xad\xa8\x18\x988\x0b2\xe7\xfe {\x9f\x9c\xcd\xd8\x86pS\x84Ig\x84*C\x94\x9b>I\xaeG\x9a\xdes\xab\xdd\x1dl\x83\xc6^\xfcq\xb7D*=\x19\xaeWWh\\\xbe&\x06?\xb9\xde!\xc1\xb9\x91\xcdz\x14yYD\xac\xdc\x1b\x8a\xa5\xc2LY0L]\xe5^5&\x9a3\xb3\x06\xe4\x80\xb9\x1f\x94\xba\xbf\x80\xd6\xfc\xee\xd5\xcb\xe9\x92\xbd\x8a7Q\x0f\x88}\x8d\x1e2\xbb\x11\xec\xecy\xf5\x92\x14G\xf3\x05\xf3\xb11\xc8^4\"\xae\xcb\xe9M\xc9\xfd@.c\x9d\x19\xf5\xe0EmFH\xaf\xd9\x8c\xb3%m\xee\xfc\x8e\xf9<\x0dH\xa5J\x12\xdb^\n\xb0\xe2\xe3\x0d\xf4*\xd8\xfb\x13_\xf6T\xf6\xefK\xa5@\xa3T\x1fI\x10V\x06)W\x06<%\xe5\x98\x88w\x17\xeb\x8a\xdf\xcb\xbc AU\xa7\\T\x12\xe7\xbbR\xcfy\xec%\xb5i2\x97\x99\xddU\x97\xa3\x94\n\x9e\x05\xba\xb9\xcdR!\xefJ?o}V\x8f|^\xc6\xe9&\xc2\xd69)\xc9\x86W\xfb2k\xa6\xc7V\xd3\x1dN\xcdk\x8b\x81Z\xfd\x13L\x97W+\xceDHu\xdf\xcd)\xd6\xab\xb7\xfeN\xc3\x86\xaa\xd5\xcd'\xd6\xaa\x1at\xf9\x8e5>&\xc6<\xa0\xea\xba\xf2\xe4\xf7\xc4.}\x93m\xb8\xdf\xa5\xf8\x81;|\xa3\xd3\xa5\x14Y6\xe7,,\xd5\";xn\xea']V\xc2%m\n\x97\xbc\xefa\x16\x01\x1d9\x05L/\xd6\x8aO\xff%\xf1%n5o\xf4M\x84=T\x8dQc\xa9]\xf3\x98\x1agd\xc7\x8a\xe8 7\xb3z8\xda\xb2\x99MF\xb1!rx\x0e\xa5\x02\xdc\xa6\xe3\xf1_-\xcf\xa1\xbc$r\x05\xfdF\x91o\xcc\xbc \xe8\x1f\xfb5\x9f\xc6\xec\xf5\xb5\xa51\xdf5\x02m\x13\xffb\xae\x93\xa4\xae&m\xabk\xea\xbb6\xb2\xd6Bn8k]\xc7\xa1\xae\x895o\xf1\x8d%O\xd9\xe2\x06ga \xd9\x1f5)\xc1WD\xd0\x8f\x12\x7f\x8c\xe1\xa7\xdd\xab\x0d\xcc\x90\xf5\x82y\x1e\xd8R\xa1\xa4.\xef\xfa\x14\x1f\x9fa]m\x9b>5\xaa\xfcd}\x07\xfe\x9cz\x0e\xddTnZ\xf8\x03c\xa1MUa:\xabU\x98\xee\xcc\xb6\x9c`\\\x90GV\xe4\x00}\x1a\xb1Z:\xc6-\xa9\xa4\xc4I\x04+\xceJ\xafB\x14\x13V\x95\xbf\xa7\x19D\xaee\xf1:\xad\xce\xf2l\xb9\xf8w\xb0\xe2~6\xbc@f\xbb{\xc7P\xd5\xc5\xf9wO\x06\xde\xc8\xb9w\xe9\\\xf8\x95\xb59w\xfe\x99\xe0\xdc\xbb\xf7\xb5~I\xf0\x04\"\x04r\xbd\x86\xe1(\xc4\x18\x06\xccY>\x8c#HFp\x00\x89\x87q\xd0A\xc7\xec0P(\xe8G\x81\xb3:\xe5\xed4?U\x14\x8cD\x90\x04&\x12\xa9.\xcb\xf87\x165f\xf1&r\x06\xd2!\x99py%b\x08V\x9e\xbd<\xdf\x84\x86\xab~\x9e\xd3M{J\x8a\xe3\xe5\xa9g\x81\xcfR\x06\x1c\xd8|\xc2\xcaJ)\xc2\xea,y\xf4J'\xe4\xb7\xb4\xe5y\\&\xc6\xd9 \x9f\x96y\x8a\x0b\xce\x0bm2\xc9\xc05K 3m\x96ay\xd3\xffT\xfbDVo\xa7\x1b\x0c\xa9<\xd483\xb7\x11$o\xc0H(\"\xce\xfd\x8f\xf8\x9aV\x86\xef\xea\xe7-)\xd5\xa7\xdbts5Z\xab\xe4W\x1f\xf9Y\xff\xfe^^g],\xbc7\xae\xb11\x97U\xbb\xefy|\xb9A\xaf/\xd8F*\x8cy|\xb9\xe9\x99\xfa\xa2\x96\x8f\xc8\xab\x13?\xa3Yk\x06p\x08\xef\xa9pa\xf9\xe8'(\xcd\x13z\xfd\xe9\x88\xee\x98\xe8\xcewn9\xd9\x18\x13\x8d!\x8f`n\xbe\xf8\x94,6\x80\x9d\xd6\xfe\xeb\x98\xcd\xfa\xf3\xf82\xb0T$\xb6t\xd6\x14\xbe}\xa5\x04\xcb\x1e\xe3M\x06D\xbb\xe3=\x90\x9fgI\xba\xa1\x99\xa1\x1c\xccO\xd74l|J\x16\x1f)K\xd2\xcd\xba\x15@WC\xdeL\x05%\x12\x82m\xd6_\xdb\xcaa\xc8\x0c\x06\xe6\xfeX\xfc\x89l\xb0\xbc\xacf\x80\xb8\x06J\xf1\xfen\x18\xa5x\x93\x9b\xa3\x14\xff\xeaKP\xea:\x92\xc4?\xbc\xb8[\xad\x84\xd1G\x8aj\xdeZ\xf26\x8c\xac\xec`x\x15;\xcd\xac\xdaeuq\x91.\xab\xc7\xe6i\x05Zja \xd8\xb1\xbb\xb5sY\xcf\xbf\xa3\xec\x7f\xc9\xb8\x19\x04\x1f\x82*\x91e\xd7\x0c\xb5f*\xe9\xa7\xfc\xf6\xd6-\xd8\xde\x8eQH\x95\x0dZ\n\x95\xab\xeb*\x8c \xb6\xbeq\x15\x81^\x06\xe9\xbfhU\xb2|\x93e!5o,\xfe\x9d[\xae\xe5\xd7\xd2\xe1Q\xa2.9N\xcf(K\xfdB\xdf\xa9e9\xd3\xee\x0f\xc0?\xe2Q\xbf\x9c\xd1\x8f\xfae\x89\x95\xd0/e\xba\x89;\x8bS\xa9K\xe8\xf0kE\xaa<\x1c\x1aUD\xa3\xac\xdf\xeb7\xd1B:\xab\xfa\xbd\x9d\xe2\xdb{\x1d\xae\xad`\xdaki\x04\x05j<\x0f9i\x1b\x0c\xe0\x8d\x14s>s\x8c,\xf0\x05\x91\xe6o)=C\xfe\x0b\x16\xb7\x8b\x088)\x80\xf1\xe1\xe6\x9aW~\xf0\\\x97\xa9(\x0f\xad\xcd\x98\n\x15C\xb0!_\xba\xb9\x186\x8b\x8b\xd9\xd3l\xb2\x81\xa3\x0b\x9bU\xd9\x05\xb0\x8a\xf3L\xcf6\xd0\xcd#@\xb9\xbd\x84\x83\xf2`\x00{p\x1bv\xcb\x8d\xe6 ]\xcaL:\xeeT\xf0\xf9\xb9\xf2\xa36\x16\x0ea\xcf\\\xf5\xb6|M\x0c\xcck\xf1\x1b\xdf\xf0\xd1^\xa2\x90~\xe7\xee\x9d\xfd\xef\xf6\xbe\xbds\xefN\x18\x95\xb7\xe1\xe1C\xd8\xbb\x07k`\xf0\xe8\xd1#\xd8\xd9\xbb\x17\xc1\xdd\xfb{\xdf\xde\xbd\xf7\xdd\xee7\xcd\xf7\xeeh\xef\xdd\x89\xe0^\xf5\x1c\xd3\xb9\x07\x0c\xb6\xe1\xce\xb7\xf7\xef\xee\x7f\xb7\xbf\xf7\xdd}Xs\x98\xfe\x8bo\xe9\x7f\xc9\xcf\xf6\xeeG\xb0\xbf\x7f\xf7\xfe\xb7\xfb\xfb\xf7\xca\xe6\x8f\xe5\xe7\xd8M\xf9\xe6\x9d\x08\xee\xec\xdf\xbf\x7f\xf7\xdb\xef\xbe\xdb\xfd.\xd4\x9bpl\xb9@\xe7\x0f(\xd6\xba<\xdc\x10j0\x80;{\xf05\xe4\xb0\x0d\x9fi\xf0\x94\xe0\xa6yJ\x02\x16\x86|F\xf6\xce\xc1sw\xaaKh\xc5\xaf\xd1K}R>\xdd\x943\xc2\x8e:;\xd8\xacq\xcfvCc9k( \xa2\x89\x14\xd6\xee4\x95\xc1|/~\x10\xc9\xc9\xb4\\\x00\xfa\x1b\x1f\xe8p\xaa\x02\xbc?\xd0\xe1+\xfe\xf7\x07i\xb2(\xf8-\x19:*n\xcb\xc0\xea\xf2\xbe\x1e8\x04\x03xF\xf1IB\x8b\x85\xc8\x8d\x8f\x9f\x1cg\xcb\xbc\x9eW\xc6\x04\xb2\x86\x12I\xba\xb7\xd6g\x87\xad\x8fgqBE\xdb\xd2\x96)ng\x94\xc5 F\xa5\xe3\x10\x84\xee\x12c\xc4s\xd3)9M\x93\x0dB#K\x01\xe5#\xb3\xae\x84I\xed\xb38j\xb9\xf7\xfbZ\xff\xedT1\xb7\xcb\x02N\xe1n#\xc3j)M('\x89a\x12A6\xb2\x17\x9f\x06\x10FU\xcd&\xe9)4\xce\xe3\xc5\xcb\xba\x0f\xb2/\x8c\xae\x01\x04\xbe\xeeMXt\x89\x19-X\x88h\x04\x07\x10\xb0\x93\xeb\xec\xd6\xd7\x14\x93\x9btf\xeexn\x07\x92\xdaI\xf5\xbe,\xed\xfc\xde\xd9\xce\x90E@F^\x8d\xbd\xb1\x90\xc3\xe6\xd9\xdc\xb1\xd9\xb6\x88O2.h\xc3\xd32\xac\xf773\xac\x9d\x1b\x1e\xd63\xf7\xb0z\x05\xd2\xc0\x9a\xf1\x03\x0e\xe1\xc5\xf1\xdb7}\xf1(\x99\xae\x84\xdaVRK\xcf\xdc\xa2\xaf\x9c\x04\xf8\xd8\x9a\xc9\xd3\xd2\xdc\xc7N\x0c\"\xf0\xb0\xe4\xe0\x08<\xc2\xbfw\x90\x9d\xf3\xea\xe0\xb3G\x07\x9c\xf5\xd9\x86\xfd\xfb\xf7\xee\xde\xbds\xef\x9b\xfb\xdf\xc16\x04\x843d\xf7C\xf1\xe7\xa3G\xb0\xdf>}\xeb\x0b%[{M\x87\x0bu$\xbe\xae\x8eD\x19\xa8\xc5\xef5\xceD\x91^\xa0|\xd08\x14;\x89\x9a\xec\xb6\xb1\xb0\x0c\xa3o\x0f0\xfc\x161\xa5>p<\xd82s\xf2\x93/M\xdf\xe0\xa73\xbf\xd1\xc0\xa9=\xbf\x93b\x9a\xd0 JO\x9e\xdd~\x817\xdd!:\xd3\xc1\x01\xec\xb4\xfd\xffLfN>*?\xc3\xd5\xb9\x9e>S\x99\xa8\x9c\xa3\xd1\xd2\x0c\x97{\xc7\xcb\xd53\x8d\x0b\xf6\xfc\x9a#+\x8dq\x7f\xd9\xe8n\"~\xc3\x13qn2~\xc3\xb7\xcb\xc5\x06}*Dm\x86\x15\xd9\x9d\x98\xf9:U\x96\x02.u\x8a\xa0Z\xb1\x10\x98\xf6j_\xfe\x89\x15\x8c;\xb23\xf2\x8b\xa8\xec\x8c\x9c`\xef*\xe7~t\xce\xafRDt\x04\x85VI\x15\x959\xa3\x03{J0\xef\xc9\xd1\x1eB\x0e\x07\x90\xab\xd0\xfdc=\x02x_94\x88\xd61\xc7\x81gP\xb0r\xee\xfc\"\xf2Qz\xab\xfe\x15$\xe4:\x8e\x9f\xa2\x9a\xbdW\xeb7\xe4\x9a\xe8\x89\xfd\x1b;\x0d6\xd2k\x87\x88\x82\xaa\x14]]\x0b\xa5e^\xafG\xd3\xdc\xba%\xf8\x8b\x99\x96dU\xe1\xed\xb5\xfc\x11EUmKV\xa5M\xdd\x117s^j\xc1\xe3\xd1\x00v1\x07\x85%\x90\xc8\x02(d\xbefUt\xd1\xce^\xf5\xa5<\xb4Z\xd5\x14\xc1v\xc61\x92/\xb2b\x13\xd3\xe6\xf5\x93|\xf8\x99\xf5\xaa\x12\x03%\n\xec\xc3\xd7\xea\xd7\x0e\xec\x89\x02\x03\x0e\xcb\x9f-\xf5\xa1~)\xa3\x01s\xca\xe5\xeaJ\xbe\xd8V\xd79 \xad\x8d`+\xc1R\x00b]Eh)\x17\xd1\xb30\xd4\x92\x96b\xb3\xf2\xbe\xb3\xe5+\xde{\xe4\xca\xa3\xa1C\xd4l\xb6\xf3\x06i\x84\xb0\xaa\x19\xd0~\xc7\xfe;'\xefo\x0f\xbd\x86\xfd\xac\x84l\xc6!\x1b\xc3\xff\xe5\xb2\x03\xdfz\x1c\x07\x92\x9a\x0b0\xc6\xfc\x1e\x88w\xe0\x10>\xf3\xb9\xc7\"\x1d)Zm\xd4\xcfL\xa5\x8c\xed\x02\xbf\xd3ZbIU^Q \xefm\x9c\x92\xf8\xdc\x87\xf3Rf\xb9!\xefbd8\x94C\xc7bq\x1e\xe5\xa5 \x00J\xff\x12\xc1\xcb~6EgZ\xebg\"?\x89\xe6\x9d\xef}\\\xc3\xbf\x8e\x1f\xf8\x9e\x11\xaa7\xed\xde\xe3y\xf2\xffq-\xbd\xeaK\xf5\xc7+\x1a\xb9\x90\xcd{\x0c?'l\xe6sN)\x99G\xef\xc5\x8do\x9c\xa7S\x01\x02\xed\xf1\xdbL\x96\xb5;W!\xa7\x08Uz\xd8\x89\xd27\xe87\xcb\xba-\xef\xd0q\xbd=\xfc\x8dy,\xc4 Q\x0bZ\x9a\x95\xbd\xe4\xb4\xeb\xe6\xd31T\x9d\x86\x9b\xd9l\xd8|\x95\xc3\xcd\x03\xda\x89\x96g[\x94\xd0\xaeY \xf4\xc7\x9a%A\xbf]3)\xfc\x1a\xe9J\xda\x10\xef\xbd\xac-\x9f\xb8\xf7C\xadiq\xef\x84\x18>\xbe \x86\xaf\x8fH\xf3\xf36TT~\xb9\x03\xa0m\xb8\"P_\xb4\xef?\xcd\xd2\x94 \xa4\x0f\xe0\xd4\xe0\x03\x81\x01b\x1f\x0d\x0f\xf4\xb4\x92\xefX\xfb\xb9\xc8\xcb\xb70<\x91\xa9\x02\x8f\x8c\xa3d\x07P\x18\x1e\xe8Y%\xe7\x86\xe7\xef\xc98\xcb'\x07\x90\x9b\x9e\xc5\xf4\x8c\x1c\xc0\xca0\x89\xf7dAb\xde\xa4\xe1YR\x1c\xc0\xccp\x7f\x9agsLmkK\x97|\x15\x01\xe9\x93\xcbE\x96\xb3\x02\x93\xc4 \xac\xbcr\xfb\xb4\xf5\x96\x05\x81\x82\xe5\xc9\x98i\xf9i\x94 ]\xdbn\x9a\x0f\x8d\xdeQ\xb3u\x15\xfb\x16G\xb0\x8c\xa0hn$L\xc6\x1e\xb00\x82-\xe3\x1e\xe6]\xa7m\xfa\xa7\xa5\x01C=OX&L;\xca\xf3,\x0fz\xaf\x13\x9aL\x132\x01r9&\x0b> \xc8\xc6\xe3e\x9e\x93\xc9\x03\xe0\x93d3\x024\xa3;s\xf5\xe2\x84\x9c\x03\xa1\xe7I\x9eQNu1\x02\x8b\xbf4]\xa6)\x10\xde*\xccIQ\xc4g\x04b:\x81x2Ix\xb3q\n3\x92.\xa6\xcb\x14.\xe2\x9c&\xf4\xac\xe8\xf7\x0c\x14\x9b\xa4\x05q\x90\xfc1\xe7i\x9a\xc0r\xf8\xf7L\xed\xfcfP\x07\x05\xeb\xe7d\x91\xc6c\x12\xdc\xfe\xbf\xc5\xed\xb3\xa8\x9b\xa8AE\xd8\xc6\xc3\xe9\xf6v;\x84\x17\x90\x8a\x85a\x9f\xc6s\x0c\x8dxN\xcf\xe3<\x89)\x83\x9f\x92,\xc5\xe4\xdb\x86\xfc\x92\xad;l\x96g\x17\x90\xf6\xa7y<'\xc5\x87\xec\x1dV\x91\xd9k\xa6b\xd3\xb0\xfa\xcb\x91\x98\x06w\xee\x86f\xdc\xcd\xaf\xdf\xba#K\xa2L~>!\xd3\x84\x12\x95\xfc\x9c\x8bE\xbd\x93\x13R\xbc\xce&\xcb\x94\xf4L\xa4T:I5\\\x9e0\x8f\x12\xe7\xbb\x9ef\xf3yF\x8f.\x19\xa1\x85\xcc\x7f\x8e\xf7\x1bwH1\x8e\x17XS\xf1UB?\xbd\x8b\xb1\xae\xa2J\x9d\xdf\xba]\xcc\xe24\xcd.\x8e>/\xe3TV#d\xfd\xd3e\x92N\xbe\xcf\xf2\xf9\xb3\x98\xc5\xe2\xb5,g$\x97OY&o\x92<\x89\xd3\xe4\x0frL\xe2|,\xda[\xc4y\xa1\xff>#\xec8\x9e/Rr<\x9e\x91\xb9\xf8\xee\xaf\x17\xc7o\xdf\x88\x9d\xd1\xe9\x01\xc6\xf2U\x07\xb3\x8c\xb6*D5\xab\x8eF\xe8\xa8o\xdd\x82^\x86\xbd\xf6D\x11\xb2\x86\xb1\xa0\xb7\xa4b\x9fNzp\x00\\\x82*\xf8\xc6\x8d\x97)\x0b\x03\x16\x86\x8ex\xd7+\x18\xc7l<\x03q8\xb6\x1e\xcb\xef\x1a\xd9\x1b\xae\xf8^\x16\x03J\xa6\xabNH\xc8F\x8e\x05\xc3|$\xf9f-\xa9<\x1c4\xfb\xc6\x1e\xe2<\x8fW\x1bt@d\xb3\xe8]\xa3\xff-\xeaI\n+\xefp\xd4\xeeH\xb0%\x92O\xd2z\x03b\x0eM\xe3\xabr\x84\x1eT\n\xae\xe6\xb3\x9eAB\x0b\x16\xd31\xc9\xa6\xb0RK\xd2\xe7[\xd2\xf5i /\xc6\x01U\xcf\x86\x8b\xb7\xd2\xb2)\xce\xb8\xcb\xb4\xbc$\xec\x8b\x8c\xce8\xdb\xea\x95\x8a\xd9\xac\xde4\xd5Nd\x98`\xf0Cv\xcc<\x0b\x05)\x15\xa3)\x87\xbb\xd2\xfd\xecF\xb0\xacP\x91\xb4\xb3\xf3v [\xe6\xf0\xc5!3$\xe80\x14\xbe\xeb*\xc6N\x879\x17\x0f\xc90\x1f\x89\xf4\x8at\x99\xa6fMt+\x13&\x82\x8cf\xf9\x1c\x0f\x0f\x81s\x03\xb8\x8c\x90N|O}\x91\xd6<\xc1vOIQ\xd2\x9dc\xd9\xc7\x92\x8eo\xbe\x175\x11\xaff\x9b\x99\x9a\x8dT\xe2u\xbc\xf0A'+\xca4\x93\xfa\xba\xf4\xa2\xf5ue\x01_Y\xa1\x8a5\xe5\xee\x84?\xdb\xa5\x84p\xc8\xef\xb1\xcb\x7f\xdb\xa8K\xc5x9^\xa7\xee$s\x1e\x08Y\xd7\x81 U\xda\xfcn\\\xdd\xa5\x18r\xb1\x01\x98\x8aU\xc1\xc8\xfc\xc3lI?\xbdN&\x93\x94\\\xc49\xf1E\x9c\xee\xfd\xcf\xfa\x93\xa4X\xf0\xb3I2\x8eH\x97\x9cp\xe9n\xd4\xf4\xb2\xd3\x82\x05\x1d[\x08\xcd\x93\x01 0\x959\x0b,\xbel`\x14#\xccw\x0d\xe7\xa0\\#\x0e\x80e\xf14\x9btC\xf9\xbcL\xb2\xa5\xaal[I4+55\xc1\x05?[.\xf8D\xfc\x93\xa8+\xe0\xec\xf7Ty\xd4m\xe8\xf5Bc\x06\xa5\x10\x19pK0\xf3\x95\\f~\x82\xf9l<\x8c\xce\xa9N9\xa5\xc0\xe1\xbc\xa7\xfc3\xd0\x8a)V/\x8a\x13\xb2\x0d\x0eu\x9a\x11\x99\x83\xc0p\xec2\xce>\xb0\x91\x1d\x96\xf5^\xfaI\x81\x9dQ\x91\xf8\xfe\xa05\x88\xf6\xfcg\xc9\xd9,M\xcef\xdd\xdc\xa5Z\xe1I6Fu\xab\x99\x01\xd9\xaa\xf8\x8c\x9e!s\xaf\x08N`\xe4\x92=\xcd(#\x94\xa94\xac\x8f\xe0\x1e\xb9S\xc5\x03\xe9\xafX'\xdf\x8d+\xb5\xec0\xba\xd2@\xa4\x83\xab\xfa\x88\x90\x0b\xdf\x8dP=\xb2\x1c\xee\x8e\"\xd44\xecE\xa8@ \xfd\x84R\x92\xff\xf8\xe1\xf5+\x91q\x18\x16\xa8V\x10r\xb2\xa8g\xbb\x80\x87\xf0\x0d\x92\xc9\xdf~\xc3\xfdJ\xa5\xe7\xdc\xd8\x99m\x86\x03\x84\xf7\x94\xaa\xae\xb7\xb7\x8b\x910\xafM+\xd8\xecE\xb05\x86\xf5\x1a\x16\xf0\x08\xbe\x15\xbd\x08\xaa\x80w\x87\xb7\x7f;\xbe\xddg\xa4`\xc18\x8c\xf8\xdb\xfc\x83\xdb\xc3\xaf~\xbb\x18i\xf7\x83\xdem9\xb2\xf5\xbal\x80\"iN\"\xf8[\xefo\xa0\xdcN\x92\x08z\x7f\xeb\xe9?\x97\xc3\x02v\xe0\xee\x08\xb6\xd1)\x9e\xf2g\xbd\x9d\x9d\xdf.\xefp\x99\xbc\xba\xf5\xf5\xed\xdeh\xb8\x18\xb9\x8de\xb8,SQ\x98\xa1\x1f/\x16\x84N\x9e\xce\x92t\x12\xc4\x9a\xc8}\x94\x12\x8efA\xafX\xc4\xb4\x17\x86\xfd\x82\xb0\xc7\x8c\xe5\xc9\xe9\x92\x91\xa0W\xb0\x15\xaa\x03\x86\xbdq\x96f\xf9\x01\xfc\x9f{\xf7\xee=\x80iF\xd9\xce\x05\x11 qO\xb3t\xf2\xa0\x17\xe1\x8a\xe1\x7f\xfa\xabxo4\\\xc0!\xae\xdd\x1d8\x84}8@\x08\xdf\x87C\xb8+\xff\xe6\xf7\xef\xc0\x01l\xdf\xfeW\x10\x07\xa7\x05\xcb\xe31[\xa7I\\\xac\xe9d\xadL\x0fk\xbeg\xd7E0_\x17$g\xe1\xe1z\xc9\xb2p}\x1a\xc4\x05Y\x93\xb3\x84\xae\xb3,\x0dHL\xc3\xc3uN\xe2O\xeb\x15#\xe1z\x8c\x8f\xf9\x81\xb3\x9e\xc5\xf9\x1aE\xdb\xc9:\x8d\x8bb\x9df\x94\xac\xb3\xf9\"]g\xb4`\xeb\x8c\xb2\x84.I\xb8\x9e\x90\xe0tyvF\xf2\xf58\x99\xc7\xe9z\x9c\xc69YO\x03\xbe\xc7\xd7$\x0f\x0f\xd7 M\xd8:\x0d\xc8Y\xcc\xc8\x9a0\x12\x1e\x86\xebI\xb6\x9ed\xcb\xd3\x94\xacI0\x9ee\xeb\xb48L\xa6\xeb\xb4 A2\x0d\x0f\xf9<\xb0\xf6\xe8\x9a.\xe7\xebsB\xd9\xfa2\x18\x93\x05[\x93\xf1z\x11\xa4\xc98a\xeb,g\xe1\x9a\x91\x80N\x8a5*M\xd69\x0d\xc3\x90w\x9d\xa6l\x96g\xcb\xb3\xd9:N\x0b\xb2Nh\x9c\x06\xe9\x8a\x0f\xe5\x92O'\x8b\xf9\xd7\x01\x89\xc73>\xfb\x84p\xb0e\xf3\xf5\x92\x8e\x03\xbe{\xf9\x00\xcf\xd2\xec4N\xd7g\x19\xcb\xd6g\xcb8\x9f\xac\x93`\xba\x9e/\x02\x81\x03\xc5Z\x1b\x04\x0d\x12\xb6F\x95~p\x92\xd11 \x0f\xd7i\xc2\xa1\xb5dk%\xfa\xacY@\xf2i<&k\x92\xd38\x0d\x0f\xc3\xc3u\x11\xae\xd3 \x9e\x9fN\xe25a\xebl\xfci\x9d\xd1\xb3p=\x0f\x92q\x9e! \\\xa3\x8ai-\xd4\x08\xe1\xfaM\xfcfM\x83xN\x8a\x05o)f\xc99Y\x93K\xb6&\x17\xeb$]gl\xbdL\xd3p\x9d\x05\xc8\x16\xad\x17\xc2\x10\xbe\xce\xd7K\xb6>'y\x9eLH\xb8^\x04\xf1\xf8S|F\xd6q\x1e\xcf\x8bu\x9e\x9c\xf3u\xc93F\xc6\x8cp@\xb0l\x9c\xa5\xeb\xe5i\x9a\x8c\xc3u\x1e\xc4 \xc7\x98 \x9ed4]\xf1\x85\x9b\xae\xcf\x92\x82\x91|\xbd 1[\x7f^&y5\xefb\xbc$k\xa1b[\xb3|\xb5\xe6T1\x0c\xd7Ep\xba\xe2\x8b\x1f\xa7d\xb2&\xe9t=\xcbr\xb6N\xce(\x99\xac\x93?\x10<1K\xc6kT\xe7\xacY\xbe\x1c\xb3\xf5\xf2\xb4\x18\xe7\xc9\x82\xad\x97\x0b\x92\xafWt<\xcb3\x9a\xfcA&\xeb\x8b\x84\x8dg!\x87\xe8|\x91\xf2\xc1\xcf\x08]\xcf\x92b=\xcb\xb3\x8b\xe2p\x9d\xc7\xb4H8\xd2\xe4K\xb2\xceW\xeb\xd5\x82\x041\xee\x8f \x99\xae\x93\xc9\x9a\xc6s\xb2\xce\xa6a\xb8^\x064\x18K4\x9f\x90i\xc0\xd9E\x8e'\x19]\xa7\xa4(\xd6\x85\x18#K\xd2p]\x90u\x91\xf0\x05:\x0f\xe2|\x9d\xe4l\x19\xa7\xeb,\x99\xacQm\xca\xd7\xe7\"\x18\xcf\xe2\xfc\x84\x89\x01\x91\x9c\xacgIJ\xd6 \x9b\x85\xeb\xcb,_\xaf\x12\x92N\xc2\xaf$\x01\x9cr~iw\x14r\x16T'9\x8a\xdc| \x97\xecM6!\xc14\x0cC\x91Al\xc1)\x94\xa0\xeb\x9cF\x1c\xf0\xf3c\xaa\x1d\x00{{\x0f`k\xb8\x17\xc1\xed\xe1o\xb7\xff\xbc\x1a\x06\xbf\xedl\x7f=x\xf8\xe8\xe0\xc1\xfa\xb7\xdf\xfa\xd1\xe1\xd6\xad\xbf\xff\xfft\xfa{{\xf8\xdb(\xac\xdfhPhI\xa0\xc7\xbc\xe3\x0cS\x93sR\xff\xb0\x07[x\xceH\x12=.\xa9\xf3\x98\x1fS\xdb\x90\xc26\x12\xe8m\xd8\x1b\x95\x7f\xee\x8f\x90 \xffvyg\xbc\xb5\xb3\xd3So\xf2{\xb7\xbf\xae\xff\xbc\xcdi\xe1\xff\x11-\x8e\x86;;\x8b\xd1\x03\x87\x07\xcf\x14\xb6\x070\xf6e.\x8d2\xda<^|\xc8\x1a|\x97M\xf5as\xb1\xe4\xc7b#\xc9~\xf9\xcapo\x04\x87\xf5\x9f\x07\xd0\xfbDV\x06\x96D)\x06\x0d\xed\xef[\xdb\xdf\xaf\xb7\xbf?\xaa1[\xaf\xe3\x85\x89\xe1k0\x90\xaf\xe3E?)\x84\x96\x04=\x81\x84\xf7\xc3\x06\x1cd\x9dc\xa4\xa2\x82\x0dE\x0b\x89\x89g\xe4\xfd\xd3*\xef\xfd^\xa5\x11\xea\xcfI~F\x02\x93\x14x.\xa3\xe5\xbbG\xc3\xdf\xe4\x8c\x155V\x07\xe2O\x0bK\xf4\xbc2\xecl\xed\x99\x9fM-:]p*=K\xe6o\x11\xc1\x04\x06(~&\x9a\x96RE\x06\x04!\xa6 \xe4\x83\x0b\xf8\xb6\x9e\xd4\x1c\x85\xc2\x07r\xd8..\x8e\xf72\xe3\x14\xc3'8\xfd\\\x8e%\xab\xc62C\x17Y\xe7Ws\x0e\x83\xceP\xf63|k\xaf\xe3\xad\x15\xe7i\x83\xb3\x08h\x99m'\x82\x9c3X\xc12\x82yS\x0d\xad_mTPB\xc7\x8a\x0b\x1d\xb1r\xfe\xc0\xec\x87\xb1H\x9a\xb72s\x83\x06b\xa1\xab\x86\x8d\xdf\x8c\xa5k\x05r\xe5\x86\xef\xa7\x9c\xfbHm\x18a\xc7\x15~ma \xdeI_n\n\xedo[\xe2\xe6\x8e\xee@\xf1\xf7\xa14\xe0M}\xe1\xd0\xba#\xc7\x14\xb7I)\xb9D\x8e\xf4\xfb$%o\xe29\xf9>\xcf\xe6R\xa6y\x96\x14\x8b\xac@\xe3\xeb\x8f$\x9ex\x94\x95W\"\xde\xedi\x92\x12~l\x0fz\xc1\xf0_\x0fF_\x87\x0f\x0e{\xb7\x93>\xb9$c\xa3\xe1\x00\xcb\x9e\x08\xdb\x00g\xea\xebm\x94MT-\xd8\x88\x93\xaa\x9e\x82\xcdh\xb2\xa1F\xaa\x8c\xf9\x19\x94\x12n\x99\xa6m\x08-\xe2b\x1c\xa7O\xe3\x82\xc0\x00\x9e\xd6\xef|/\x07\xd9 \x1a\xd9\xc3\xd3\x80Tf\xe2\xdf\xfa\xc3\x7f\xf5o\x8f\xbe\xfe\xea6\x17%B\x93\xc6*\xa6 K\xfe \x1f\xf3\xb4\xb3\x07\x0e\x802vlK\x8b\x1d\xe3\xc2\x9a\xd0u\xb8ekM18\xd6{\x0e\x8dG\xf0\x19a\x8f\xc7\x9c\xcb\xe7\xd8\x92gi\x9a\xd0\xb3\xf7\xa4Xd\xb4\xe8\x86F\xe3$\xab\x14\xfe\xfd\xa4\xd0\xb4\xff\x9a:\x84/\x8dMcP?\xf6\xccoV\xfa\xa5\xbaCx\x97Wry\xc2\x15,\xceY\xf1s\xc2fAo\xbfW\xea#u\x15*:\xe9\xf5\xc6b\xf7\xf4\xf04\xfd\xf3*\xac\xb0\xd0V\xa8\xc1LlK\xd5N\xd0\x93]\x88&\x8dv\x12K\x1b|\xcb\x06\xd40.s#a\xa9|\x93\xa6.5v\xa1\x0d2CVA\x887\x9b\xb7\xf1dB\xc8\"]\x1d\xb3\x8e\xbaLmJ\xf3\xdeP\x86\xffye\x0eLi\xe0hf09\xd9\x15\xdaU\x1cQ\x1edC6\xc2\xbdr\x08\x13\x92\x12F\x80\xdf\xe1B\x0d\xff\x87\xf3\x03\xe2\x0dj\xcce`\xcaV\xabl\x03\x06\xb2\xa7\xa2!\xbd\x08\x89)`\xd6\x95\x19HV We=\x95Y\xd7r\xa6X\xad\x16\xa4k\xc1\x89\xb0Z\x94\x87\x12 \x1d\x0c\x84F|s\xad\x89\x08\x84}o\xdf\x00R\xc5\xect\x19$\xcdQ\xc2\xe0\xe2\x13\x88#\x15\x03\xebS\xf4\xbd\xf8\x90\x95\xfe\x1c\x1ek$\xbe\xb1\xac\x91\xd6\x9b\x15M\x1a\xa6\xbf\xfa{\xe7\xb2\x92\xe7I@\x83oL>\x12ctH\xba\xf7\xcd\x9e\xe1\xd9T~x\xef\x1b\xa3{\xc5B\xb9f|\xbbkz<)\x1f\xdf5=\x9e\x95\x8f\x8d\xe3:\x97\x8f\xef\xdf36>W.%\xbb\xf7L\x8f\xcfpV{\xdf\x99x\xff\x95\xfc\xf4\x8eqR\xa7\nX\xfbw8\xe2\xd7\x9e\x97\x04\xfa\xa4\xc3w\xe1\xd6-\x0c\xe1P\xbeU\xd2\xb5\xd8\x8c\x8b\x12\xa5M\xa5\xea\x9bQ\xf3\xfa/\xbe\xb0\x170\x80\xf2\x08lO\xe5\xc8\xe0\xc0\xd3\xad\xd9o\xc9\xc8fsL{\xb06`]ndv\xae\n\x047&on\xfc\xd8\xd9\xf8\xd6\x16q\xdaW}(\x95c\x0dtO\xa9\x89\xfa\xc8\x06\x86\xa7\xce\x91\xf2~\x17U\xbf\xfc\xe7\xd4\x7f\x18u\x07\xaeN\x16\xce\xa1\xf8\xd9\x8c\x8b\x18Z\xc4a\x0b\x8br\xc7\xda\xf8\x9dz\xe3wD\xe3NN\xbcn\xa2\x97} \xefQ\x7f\xc8\xca\x87\xeb5 `\xcfk\xc7\x88\x0e-\xab\xfd\x18\x9d\x84\xab\xfc\xdf\xb4b\xbfM\x9a\x15\xd0\xfd\x00\x86\xd4\x92\xf6\xces\xa3\xc1!h\x02AR\x04\x182\xc5Q\xd5\xcaq\xf9\xa05\n?\xb6\x06|\xfc\x0e\xf0\x08'\xf8i\xd6&\x06\x82{k\xd4l\xeb*`\xb3\xc5{\x99k\xc3\x1cR\xceY\x0d\xa9\xc1\xeau\xd5\xdc\x12\xeds\xef\x93\xc5\xe1\xb1s\x7f\x80\xb2\xa7\xc2#\xa8\xc2\xc4{?\xc5\xe9\x92\xc0|Y08%\x90\x92\xa2\x006\x8b)\xc8\x96\xbd\xca\xd9?\xb68fn0\xa6\x87\xf61\x9d\xa1\xc2=\x97\xc3\x12\x8d{\x0d\xeb\xad\xd9\x85\xb4\xfb\xb4@9\xf3\xf6\xbfv\x0e\x7f\x9bl\x07\xbf\xf5\xf9?\xe1\xa1\xb2\x0chRjc\xa01H\xb6\xc7gp\xef,>\xaf\x9b\x8d\xcecP\x14#\x01\xcf<\x87\xf5\xc1\xe4\x9b\xeb7&<\x95\xb6\x02\xe2\xf0)\xb4Cn\x9a\xa4\xc4k\x80\xaf-\x0e\xc5~c\xec\xb1|Iz\xb2n0?D\xa7qZ\xe87\xb6v\xb5\xbf\xf7\x14#o\x1b\xf5\xa9\xe8\xdek\xe0\xcf\xcd\xce\xd1~\xe3\x16\x835\xa8{\xecc\x93/\xfb\x0c\xedw\x9b3\xb7\xdf\xe0\x92\xe2M\xfc&\xe0\x9f\x95\xce\xc2\x8e\x95V\xcd{\x8d\xec\x8d\xc9\xef\xdcoTJ\xd8S\xa2F\x9fe\xaf\xb2\x0b\x92?\x8d\x0b\x12\x84\x11l\xdd\xfe\xd7\xf0\xcf`t8\xdc\xdd\xf9.\xde\x99\x8e\xfe\xfc\xf6j\xa7\xfc\xfb\xae\xc7\xdf{\xfbW\xc3\xf0j\xe4E\x18\xf8\xc8\xbd&\xfc\xde\xea~\xefOL+\xde\xc4\x8f\xce\x8b.\xbc\x86\xf7\xcc\x1a3\xb0\xf9\xf06 \xf9\x1b\x8c\xf0\x95%\xd2\xc1{|[\x94\\\xc0{rvt\x89\xfe\xc8\xae\xa5\x9dfi\x9a]\xc0Bv\xd2\x83m\x93\x03{\xfd\x0co\xc7et\x8e\xec\xba\x9c\xed\xad[\xb5\xdfv\xae\xd6\xc6\xf1\"\xab\x87\x94\xe74\x9b\xac\xa4RY\xa8\x17\x13\xda\x13N\xf2\xf8\x0b\xcdX'\x97\xf3\xb4\x87\xee\xf2\xda\xcd\x9eEU\x99T\xea\xce\x9c\xa0\x9b\xc2\xc4\xf6j\x0c\xc2;J\xbe^`\x84\x8b\xe8\xc8\xa2\"\x8e\xcb\xd5\xca\xedv\xc7X47\x97|\x8e\xa5\xf3\xb1\xf6\xa6d=,oN\xab79q\xb6\xbd\xb6\xa8^\x9bf\xf9\x8f\xe0,\x82\xd3\x08N\"\xb8\x88\xe0(\x82\xcb\x08\x8eG\x0d\xe1\xd59\xf6J\xdfd|\xc5V\x92\x0eYB\xe4\x9f\x9f\x86\xcd\xb9\xbf\x97\xb4\x1e\xa6 I'\x90\x14@3\x06\x8b<;O&x\x02\x98(\xb6j\xf4\xdc5X>\xf1\x8f0\x80WA\x16\xc1\xb9\xc3%\xe1#\x1a8\xc4x>\xfa\xba\x1a\x80\x1c\xc2\xa4\xda:\x93\xae\xd1|\x86\x01\xbc\xe7\xa3\x998F\xf3Y\x1b\xcd\xe7MG3\xeb\x1a\xc2\xf70\x80g|\x083\xc7\x10\xbe\xd7\x86\xf0\xfd\xa6CXV\x00q\x96\x1d\xe1\xa3\xf9\x03S]a\x91\x11\xfbh\xfe\xd0F\xf3\xc7\xa6\xa3\x19W\xa3\x19w\x8d\xe6 \x0c\xe01\x1f\xcd\xd81\x9a'\xdah\x9el:\x9a\xfa\x91\xd85\x9e\x9f\x1c^K\xeaB\xee&\xf8 5\xe41#;\x8c\xcbQ\xd8\xfc\x02\x0e\xe1\xf7\x00Uh\xbd%\x176\xca\xbbo\xc4\xdd\xe7\x82\x88\xda\xf9\"u\xc9\xd9\xfedsb\xa9\xc8l\xfd`\xeb\x9a\xdf\x8f0\x80\xd7\x81\xab\xda\n\xce\xee\xc7\x0d\xc6\xf8c\xf7\x18k\x87g\xd7\x10\x7f\x86\x01\xbc\xed\x1e\xe2\xcf\x1b\x0c\xf1\xe7\xee!\xd6O\xe8\xae1\xbe\xc0\xec\x8d\x9dc|\xb1\xc1\x18_t\x8fQg\xb0\xbaF\xf8k\xc7\xd0N\x91\xf9)\xd90\x9f\x81\xfe\xaax\xd6\xe74\x18\xf6\x12F\xe6E/\x02\xc1g\x8f0\xc9N\xcb\xcc\xdd\xe5\xe9\x01\x9a`\xd5\xb5\xed\xf8U\xc3\xa4_\xd1E\x82#\x0b\x86\xaa\xd6\x97P=|'\x1f\xeaT\xe0Wd\xc0\xf8\xd3\xe7\\\xa8\x8c\xa4\xb9]\xac\x83{\xb0\xfcJDVKC\xde\x95\xe6\x85\x995\x0e,\x99\xc4\xd4\xe5\xac7\xdb\x89\x13\x1a\x83\xdc\x85\x12/a\x00\x1f\xba\x91\xf6\xa5\x0f.H`\xbd\xf4\xa5\xc6V\xab\xb7\xc1{\xa5\x9dF\xc1\xcd))7\xa3/w66X:Az\x05m*\xf6\xb7\x0cZ\xa6\xf8g\x0e\xef\xdb\x97\xf3T\xea\xae\x98U\xbeK\x84\xcf\xd5\xe5<\xc5m\x8b\x7fa~\x12\xd7\x9a\x0b=\x0f\xff\x86K\xf9\xf2\xdb?\xaf\"\xfe\xfdW_\xe5d\xaa;\x03\xac\x16\xe8\xb4F\xfa\xb8\xaf\xc5\x9f\x0b\x91\xcf#!\xf2w\x95\x16\xe6]\xf5\xe4\x10\xfe\xf6\xf0\x907~N\xf2\"\xc9\xe8\xa0\xb7\xd7\xdf\xed\x01\xa1\xe3l\x92\xd0\xb3A\xef\xe3\x87\xefw\xbe\xed\x1d>\xfa\x8dJ\xb7v\xf8\xe5\xf5+ \x97\xb8\xc40\x8e)g>O \x9c\x11\x8a\xc9\x19' B\x94\xfef\xf5~R\xd7yY^\n\xa7\xd3\x9fsQ \xb8\xfd\xdb\xf1\xd7\xbf\xdd\x0e~;\xde\x0e\xbf\xba\xed@\xf6\n\x88\xb2\x84\x94'*C\xddXx\xa6,\xb5\x93\xa7\xa8/\xfb\xe5\xf5\xab#17\xe1J\xe2\xe3\x01r.\xcb\xaa\xd5\xdb\x13\x9b\xe0\xfb<\x9b\x8b\x8d \xdbk\xcfH)\xc5l\x92]\xd2%\xd9%a\x08\x87M?\x98\xa4\xf2\x83\x81\x83F\x8eJ\xe9\xa3\xa9\xa7?q\xba}\x9d\xcb\xcc\x86\x7f\x1at\x85 \x93\x17V\xe2|\x9a\x8d1\xcbN\xbf\xc0\xc6-\xfa\xa5Joi\xdbZ=\xa1\xa4w)MD\x16\x94byZ\xb0<\xd8\x0b\xfb\xc5\"MX\xd0\xbbe\xd2\xc6\x80\xee\x9f\x9eCB\x81\x86@\xfb\xb3\xb8x{A\xcb\xdc7\xb9pS\xc4(\xc3a>R-\x0e\xb8XE\x86\x132\xce&\xe4\xe3\xfb\xe7O\xb3\xf9\"\xa3\x84\xb2 \x1f\xee\x8e\xc2\x11\x0c \xe7T\xe8\xd6-0\xbe\xb37\x12v\xd5\x9e\x0f>\xa9m\xdd^\xb3v\x1a\x1b7m\xb5Z\xc5\xfd\xca\x97\xab\x81\xd0\xd6\x8cD\xca\xfdA\x0f\xb6MO\xc9\x90\x19\x0d\xb3\xfd\xdf\xb3\x84\xe2\xf2\xb4\xa7&S\xf5\xb8\x07\xa5\xe6S\xcb\xb9\xa1r\x17Sr\x01$`\x9a\xb9\"\x82\xde\x92Mw\xbe\xed\x85au\xb7w\x1a\x17\xe4\xfe]\xd3\x18\xaa\xd4A\xed\xae3\x0c6K2Z\x1c\xe3[6\xaf\x9d8]\xccb\xcf\\\x83\xa0\xbb\x8f)m\xe2\xac\x17\xe2\x16J \x07h\x9c\xf3)i\xcf,G\xb6yc\xce \x9be\x93k\x8fF|n\x1b\x8fz\xea\xcdD\xb4\xc7\xc8\xe2\xb3\xbf\n\x9c\x8d!{\x0f\xd2\x80\x99\x8d\x14S~\xec\x8c\xc9I\xa5\x8a\x8d\xe6\xe4\xc7z\xfa+_^b\xf5\x10\xd1\xd8\x96\x1c5\x88\xbd\xeao&x\xbb!\x8d\xf8\x06\x8dL\xfb3\x0f\xb5\xc4k\xfb\xbb\xb7\xcf\"\xe8m\xf7\xc2\x91\xdc\x9f\xa6%\xb5R)\xe6\xda\xd4\x86\x94]\xb5\x95\xb48\xd6\x94J3N\xb8f\x15\xe1\xa2\x9aSN\x97\xcb\xc8F\x1e#\xf5\x91\xd7a\xae\x94b\x96\xbcd^\x04\xd8X\xa0\x063\x8ektL\x9a\xb31\xa5Q\x9e\xcc\x03m\x91~\xc3\xecx\xbd\x13\xb4\xd8\xf4z\xae\xe1Z\xb2\xaay\x0d\x93\xc3\xec\xb4\x82\xd9\xc7\xb6{Yd\xc8\xe3\xe6\xd54ig\x9b\xe8N\xc2z\xfb_\x97;%s\xdd\xb9l\x915\xf7\xdc_9Bi\xffY\x97\xf6\xa5ui=ZK\xbb\xd8ZZ\xbd\xfc\xa7\xf2?\xd5\x83\xb2\x90\x16\x0d\xee\xdd\x0d\xfbO\x96\xd3)\x91\xde\xe2\xd7\xca\x06hN\x88\xd9\x9cfI\xa9\x8c\x92\x99\xc8\x15\x0f\xff\x7f\xf2\xde\xbc\xbbm\x1cK\x14\xff\xbf?\xc55\xa7_\x8a,\xd3\xb4$\xaf\x91\xedx\xb28\xdd\x99\xc9\xf6b\xa7\xea\xd7\xa3\xf2xh\n\x92\xd8\xa1H\x15\x17;\xae\xb2\xe7\xb3\xff\x0e.\x00\x12\x04\x01\x92rR\xd3\xfd\xde\xe3\xc9\x89E\x12\xc4r\x01\\\xdc\xfd\x9e@\x15\xcb\xf2\x13\xf1\x83\x9c\xc7\xa2\xfc\x17$\x0b(\x81p\x047a\x16\xe6\xb0\xc8\xf3\xd5x{{\xe6\x07\xe4:I\xbex\xf30_\x14\xd7^\x98l\xa7\xf4\xbb\xedi\x12d\xdb\xf8\xf1\x16#\x9fRo\x91/\xa3\xd3P\xc4nd\x94\x86\xcb\xf3\xb9A\n\xc7\x90\x1fA\xba\xb9\xe9@\x0c\x9b'`=\xf1\xd3y6\xb94Q$\x157\x97\xa2\xcb\xaeB\x1f\xb2:\xeaq5ED\xcd$\xed\x1f\x94\xb3\n\xc8\x99uG\xe2l\xa2\x99\xa4\x16\x1dS\xe5\x15\x98C[\xd2\x1a\xd8\x12\xc58j\xc4\xca\xca\n\xef\xbb\xc4\xa8'\x14\xd8\xe7\xa4\x1f\xac\x932\x1a\xf1#\x9a\xacB\x19\xcbcf\x1d\xa8nz\xf5#\xcb\xfd\xe0\xcb#\xba\x80\x11\x98\xd9\xb8\xe9/:r\xfa\xb7W\x9b!\xb7\xd0}D\xb3\xc2\xb8\x17[\xd6\x18\xfd\xf6j?\xc5H\xcfk\xb5^\xd4\xb3\xbd\x88\xa8=\xad\xca\xa8\xf2\x84\xc84'\x04\x8b\xac\xc3\x8c\x102x\x06{p\n\x19l\xc1\x1e\x8c1\xf3R\x00'\xb0w\x04\x01\x1cCv\x04\x01E\xe3\xd1$\xa0\x05.\xe5\xda&AKb\xf0\x1b\xee\xa5n\xb6\xa3\x86R\xdb3\x93\xe9\xac\xd4c\xc1\xb0\x8d\xe2:q\xd1\x16\xd0\xd4\xc4\x9eux\x8a\x03\xb75 \xdb\xe5\xdf\x1c\xdcR,h\x8a\xc3\xa3p\x8afOSzb\xc2\x7f\xd1\x9f\x05\xfd\xf9_\x90\xcc\x90Zd\xcfV\xecYV\xacV\x11=\x7f\xf2\x84=O\xf0\xb9\x0b\xe4\xeb\n\x03\x9c\x80\x1fC\xe9\xd8\xe1\xfd=\xe3\xa1\xbf=\x8d\xe8A\\z)\x19\xc8\xb3\xbch\xe5X\xc4EK\xde \xe7\xb2\xe8H\xe9\xde\xa9\x8b\x16\x97\xb0\x8d\x99\x95\xd9\x03\xdb\xacN\xe4\x0b\x1d\xf3y\x1eJ\x91~h\xb2taQ\xaeo\n9\x8f\xc2pQfP\x88\xda<\xf1\xc5E;?/\xe5W\xf3\xd6\xf2f\xd8\x1a\x82\xc5\xf5\xda\xe4\xd9\xc2_\x911\xac\x9aoD\xa07\xed\xcb\xa5\xbfzY\xbe\xef\x8d\x1ef\x88\x9c\x1ew\x06F\x18\xe5>\xb3\xf5\xe7\xb6\xb6\x87X\xbc\xd9Z\xdb\xf9\x8a\x9f\xf4<+\xb5'#V\xd0<\xeb\xdaN6\xb9\xcd\xae\xb3\xcap2\xb1V\x0dg\x8d\xae\x9f\xbf\xf2~\xfe\xca\xfb\xf9+\xf6\xf3WM\xd9\x94\xc7\xfb\xcfl\x8b\xed\x7f\xcb\xed?\xe1D\x87.\x9b\xb3\xadi6,S,d\xf6\x9a\xc7\x99\xec&&z\n~\xb3\xaf\x82+\x11|t}\xbb\xf2\x11h\x9c\xc7\x84\xfeu\\\x1f\x1e\xb3R\xa5\xef\x85\xfc}\xac\x8e_\xf4\x97\x16\xaa0+r\x1ae\xcen\xbb\x14>\x03\x06F\xac\x05\xdf}\xd0\x8c\xac\xd00]\xe2]\xce\x8f\xe1\xb4\x0c\x9e\xa7\x9b\xb0\xb5N\xe0}~\x02\xefK'\xf0\xbe\xee\x04\xde\xef>\x81\x05\xd5\x00'\x80\xa6+)\x0b\x9e\xc7\x8c\x1c]\xe1\xbd\xcb\xe2\xb3\x9e\x02QQpm`2\xe2\xe5\xc9\xe8\xa5\xe3\xb14u\xa2\xc0\xf6\x1b\xe7\xe3\xad\xcfl\x9f\xb2\x15 \x18S\x16\xc6\xac@\x88\x05<\x94\x97\xb0\x86\xebk\xad\xb1\xa2\x98&A\n\x0f\xbc1t\xb4++\xf6\xc2\xac\xec\x96\xfa\xcd\xa0\x16\\U7\xed\x99\x96\xfco\xd2ar\xf4D\xed\xec\x8b\x89\xa7P6\xa9X\xec\xac\xd5\xe44B\xda\xa6#\x87\x8f\x81X \xdb\x89\x95\xa8/\xb1\xf2_\xa5\xac\xe0\xbft\x14\x8aQ\xec\xd8\x8c;\xe2\xb4\xc2=2\xc9\x1b\x9b\xa0\xaf\xe0\xaeI\n\x02\xf2\xc6\x8b\xb4\x1b/(7^\xc4I\xdfH\"}g\x8c\xf4\x9d\xc11DG0\xa3\x1b/\x98\xcc\x9a\xa4\xef\xcc\x10\xd0i\x85\xaa\xa6\xc44\xe7\xb1\xbdj\x9ds\xbaf\x0b3\xfd\x84F\xd0\xf6\xeaQKB\xa2_3\xcd\x92X\x18\x96D\xd8E\xbf\xa2K\x00#\xd5\xfa,\x10fW\xc1'S\xef\xe7\xa3\x19\x00-#\x1ce\x0d]\xc4y_\xa5\xc9\xea\xa2\x1cS\xd6\xe8{\xb9\xe2\xb4\x99V\xca\x95s\x83\x91\xab\xca\xc8\xf5.\x92\xb8\x03\x97\xd3\xac<\xa1-,\xe1\x18\xe6G\xb0\xa4\x8b\xc4<\xa5\x18ZJE\xb27.,\xcbEL{9\xa1\xfd]\xd2_\x97V\x89t\x03\x13\xb5K\x81x'\x9f\x82\x08\xae\x12\x80w\x1d\xf3\xd0\xb1\x19\x85xC\x17.\xbb\xb9\x1f[\xb7`\xa2\xdd\x82a\xb9\x05\x13\xc7\xe5 \x10\xc1\x87cH\x8e\xc0\xa7\xd0\x0c'~}\xbb\xf9\xe6s\x0eQ\x07vU\x01r\x88:]\x16\x7f \xf3\x8d\xb8r\xb7\xab!\xa2[\xae~\xfe\xcaq\x84\xdaq\xf8\xe58B\x8eJB \x95\x14\x0c\x95\x14p\x0c\xe1\x11\x14t\\\xfe\xa4h\xa2\x92\xc2\xa4E\xe2(\x8cLrC \xe3^\xca\xda\xf6\xd2\x17r\x97]H\xfb\xc9NV\\\x08\x9a\x91 \x89\xa7e\xd7\x9c\xe6V\x8bM[\xad\xc9\xe6\xb6o5\x90\xa1\x8b\xe1~\xe5H=\xe5\xbe\x9b\xb1}G\xb1jP\xee;\x8a\x9cW\x1c9\x9b9T\x81N3u\xef\x05.\xcc\xca\x99G\xa4\xb8\xf5\x8c\x02\xc5\xa6\xe3\x08&\xb3K\xfa\xcc\xa9v\xa1\xdf\xc6s2\x8bi\xe3Nl\x92\xe5\xa0\xc5\x8a\x0fNs\xf5\xea\x0f\x98l\x9d\x9d<3\xd3\xe7\x92\x05\x8bb\xb7U1\x060\xae\xbdk\x9eK\xb1\xa9\"\xb4\xd1\xd2r\x15\xb5:G\x97Z\"\xee\xff\xa5\xd3\xfe\xb1\xc7y\xd1~\x9cO\xff\x87\x8e\xf3\x9b2\xcec%\xffi=X\xbb4\xebK\xc4x7-\x18o\xd9\xb5\xeb\xe9)\xbdTw\xfd\xc2\x85\x9b\xda\x89\x8b\x1c\xe2M\xf7Y\x0b=%J\x9d\xc6\n\xed[u\xd5\xdc\xaa\x95|G\xfeT\xfc\x925\x85\xcc~\xecQ\x8a\xa3\xed\x1f\xcb\x9f\x8c\xc3\xde\xf2\xb3,\x9cWl\x92\x1d8p\x1e\xc6\xd3\x94\xc0y\x92.\x8a\n\x01\xfdk\x14\x06$\xce\x08\xbc{sQ>\xfcq\xbb\xfc)tR<\x8d\xd9\x9c\xe4\x92)\xd7\xf9\xdd\xf2:\x89\xb2\xa6\xae\x8a\x97\xae%\xb9\x94\xbek\xea\xae\x1a\x1fp\xcb\xca\xbb7\xd9Y\\,\x19\xda9\xd2\xc2\xcdH\xc4\xe8=\xa9pS\xf3\xe6\x18\x94Z\xc3\x89\xdcp\xbb<\xba\x83\x85u\x93\x7f\x1d\x98|\x11\xc9\x04\xb1\x8e5%\x96\x0b\xd6\x1e\xb34\xd4\xc2\xee\xbd\xbf$\x99M\x9c\xc9\xe0\xb2\xb5\x0355\xf1\xef\x0fL)<8\x82\x18\x8eaH\xffR\x84\x97O\xac+\xba\x15X\x0f1\x0f\xd3\xcb\x85\x9f\xbeL\xa6\xc4\x8e\xd1t.\xd6\xf7\xd7\x1a\x0cG;\xbb{\xfb\x07\x87O\x99}KK_s\xc5\xa6\xadK\xc4\x95\xabq\x84\x00$\x0b5\xab=\x8c\x8bXw-I\x91\xe8\xc9p3\xb4\xb6\xb2\xd2\xb6\xc2\x94\xd7\xc4\xbb\x9aE\xfe<\x83'PPZ\xe5\xa5\x1f,\x08K\xa5@[\xd1\xcbxo\xcaLG\x154\xe8\x17)\xd1$\x80\x06\x11\xa7\x82%m\xc2\x82M\x9c@\xc6\xb2\xb8\x02\xed\xe7\xb55!zV\xed\xea\xc3Vm\xfb\x0d\x8fx\x1fO\xc2\x8e8\xea\x19\x02\xddw\xbc\xabi\xb2|\xf3\xaa\x9d\xa2f\x16\xb2Z\xaeN\xbepTGU\xd4\xd1\xe4\x08\xa1\x91`P\xfa\xf3\xf0:\n\xe3\xb9Yy..\xda`d'\x94\x8b\xecjP\\3\xdbw\xa1\xcd\xa3K\xbe\x02\x9e\x91FC\x08\xa8\x97Y\xe7L\xaf\xd4\xb6vF\x16\xed\xa7\xb1\x98A5\xdd\\\x12bi\xde\x9f\xe8\xd7\xe6\x9f\xf4\xdf\xeb\xb6\xc0\xb4\xb9\xb5\x19\xd1\x9aU4(\xbd92\xec~&qa\x96\xd7\xb0\x81%M\xc4\x03w\x7f#\x98\xda\xdb[\xf9)\x89q\xc3:\xb2vA\xb3\x01p?U\xc5\x0d\x83\x83jI\x91\xd2U\x11\x87q\x84U\xa4\xde*Y\xd9\x8e\x83\xd8\x8a\xf6Y\x98U>y\x02+z\x96\xaa(E\x90\xac\x7fj\xb6%\xb8\xe3\xfa8\xe7$\x7f\x19%\x19\xc9rq\xc6\xbcN\x93%\xed\xf2\x18\xa6\xaeZ\xb4Y\xa6\x9d\xfc\x12\xf4\xfeT\x1b\x97^\x82 \xca\x0b\x99I\xba\x84\x13y\x18\xc2\x9c\xfb\x87\xd5\x81\xd8\xe8\x1c\xfd\x86vLt\xb2\xabsa=\xfb:\x91Z\xc6\x98\xcc\xd6\xce\x0e\xba\xf2T\xcf%7\xba\xf2Y\x07\xa7\xc3V\x98T\xdc\x11V\xf7\xa4\xaa\xfb#\xae\x13\xd4\x8f\xda\xd6\xce.\xb6\n'\xf5\xb7\x86v\x8e\xca@\xfcl\xc5\xe4b\xc5\xe01!\xf7\xdd\x08\x7f\xa9P\x1b\x84W) \xe8\x96\xadvl\xc3nD\x14\xe1KC!ub\xf9]\xafe\xd3\nf&L\xe7\xd1\xb2\xe9\xc9Y\x1b.\xdd/E\x14\x19\x8d\xa5\xf5<\xf8\x02\x9f\xaa\x04\xa4\xdc\xc5\xea\xb0\xac\xbeR\xce{\xe6\x1d9\x06k\xe4\xedy{\x96\xaeMM\xc0\xe6\xab+\x86\x01\xe8\xdf\x13q^~+);\xd0\x19\xe0N\xac/a<\xa5|}J\xb2$\xba!,\xf7Z\x9ca\xae)z#D\xc8\x1ff\xf4n\x95\x92i\x18\xf89a\x9f\xacR\x92\x91\x18\xcbq\xf3\xffs\x9e\xec\x8de}{\x1e\x85~F2\xeb\xb2I.O\xac,\xf0#?\xc5\xb2\xe4\xd7\x82\xc4\x01~\xb7\xf4W\xab0\x9e[\x97\x1d\x92\x11#y\xe5\x82__ \xe1\x8c\xe5\xb9\xc8\x85'\xac\xcc\xe1\xe6}\xc3\xb4\xd3Z\xb6x\xd8 \x0f\x9d\xc1?\xcc\xd0w\xb7b\x1bS\xfb\x87\xcf\xf1\x978\xb9\x8d\x81\xa9.\xc0\xfa\x81\x13\xa8?X\x10f\xb0$9%\x80\x90KD\x03oHf\xac\x0cae\xfe\xf6\xfc\xdd[\\\x04\xde\x0f\xcaju\\\xc8\x17a\xe6\xe5\xfe\x9c\xae8~G'\x0f7:\xfe\xe0\xf1\xed\xf9;>\xa1\xf8Z\xfc\xbe\xbf7\x8b\x96@b\xd3\x15\xb3\x07^c\xb9.\x98[Ky'\xd7\xda\xea*\xa1\xad\xb5Z`,\xbctu[\x1fO\xb9\xf4\x18f+\xef\xd4Q\xf35\xc9\xc7-\xee\xea\xa5\xe4\xc5\x8a\x05k\x0f\xeae\xe5\x85\x8c\xec\x1cs\x1e\x95\x9f\x96\x1f\xf8B\x9e%hB\x8c1 \xaf\xb7\xb8\xaf\x08'\x9e\x90\xcb\x9eK\x93^\xfe\xa4d\xc6LR\x9f\xc6\x82\xf2\x1d\x17\xf8\x92\x0e\xab%-\xd6\x95ii\xe3Rc\x0b\xbb\\\x82b\x81W\x165\xf4@\xea\\\xd9\xbdx\xf4\n\x85\x8dvG\x8em\xdd~\xc9\xd4\xf8j\x8c+\x1f\xee\x1b\xd8\xf2\x1d\xc7cR\xdd&s\xaeM\xdc+\x99\xe3\xda\xfd\xfc^\xf8\x02G\x91\xdb\xfd=\xd8\\\xf6\xe6\xd3\xd9\x0f\xc5C\x1f\xf5\xb0cH\x1c\xdbb\xfda\xc6`\x92\xb3\xd4\x83\xe3ey\x82\xa9\x92\xd3>\xb0\xd1#\xfd\\\x0e\x15_\x0f\xdc%\x80\x19\xda\xb1\xbd\xb7\x7f\xa8\x06\xacO\xf8\xab\xa7CG+7\x08\x8dC\xef\x1f\xa3\xde\x10\x9f\xfe\xe1O\xcd_\xe5\xbel\x13\x89\x0bmD\xdb\xc1\x00\x1c\x81\xab\xf6}\x15\x11\xa7\x17\x81)\xce\xf1\xa5\xf0\xae\xfa\xb0\xb3Y\x90\x08\x05S\xb0Gz\xa5,_\x96\xf1}\x88!\xe1\xcc\xef\xfd\x8e`*\xed1\xd8J:\xb5`bH%\xeb\x19\xc1\xbck\x98\xe3\xa6@\xd5u-\xef\x1a\xe3V\x18%[\xb0\xbcj\x94EbHW\x8e\xa4\x9e;G|\x9c\x06\xe6\xb5_`\xb7\x90\xa7\x16\xf3\xb5\x88\x0e\xa0_\xbe\xaf\xee\xa0t\x1b\xe8\x18\x9bIi\xc6\xb2\xf64c\xd0\xb3i\xe0\xcb+\x14(\xd67W\xa7\x1f\x9f\xf6\xa9\xe0\xa1\x1a/\x1f\xd8\xea\xd4\xd0\xcd:\x91\xb7\xd0\xe6\xfayN\x96\xab\x1c\xf2\x04\xa6\x84\x1d\xf5E\xca\xbc\xd9\x84\xbdni`\xa0*\x03\xaa\xcdl\xf7\xa2^%:u\xbf\x1d\xc9\x0f\xf7\xb5H~4\xfc\xbf\x16\xc9K\x07\xa0^\x1c=\xdc\xd3\x82d\xf7\xa9F\x1a\x1d\xdb\x0d!u\xc1\x1e\xab\xa9M\xfaz]\xa3\xf2\xc1\x05f\xbd\xb2\x02\x0c\xe0\x0d\x99\xf7Z\x8f\xaa\xa6e\x81\xbf\xe8\x0b,\xca\x02\xe7\xfa\x027e\x81\x8f\xfa\x02\xcb\xb2\xc0\x0b}\x81yY\xe0g}\x81;8\x81)\x9cB\"\x92.\xd1\x99\xe5\xd9\x97~7e\x11\xbb\xc6h&\xa5\xb6W_\xe8\x8a\xd7\x9c\xc2\x18\x16\xf4/\xcb\xecd\xa7\xbc\x95\xdf\x1f\x9c\xaa\n\x03\x9b\x8f\x9a\x9ei)\"\xca\x1d:1\x98\x9a|\x03\xf3\xe0^)\x11\x8a\xae&\x11\xd3\xb1\x14\xf6\x1d\xaa\x7f\xe8h(\xb1\x1d\xc0)\xbe\x841\xaa\x81\\\xb8c:!\xac[k\xbf\x85\xa5O\xb14\x8caI\xcb\xd1JB{\x86&yc\x98c\x07\xb0\x9a\x13\x98\xc1i\x07c\x00\x12\x83_\xd1\xb8z\x0b?\xf9B\x96n\x11f\xb5x\x1e]\xe2\xd3\x0c\xf3#\x83\xad\xea\xd6\xba\xbe\xa3W\xe0g\x04\x06\xe3\xcerP\xb7\x8f\xd1L\xa1za\xcd\xc3\xf5k\xb6u\xf8\\\xbd\xb0\xf2\xd1c*\xd7\xc60\x92\xaf\x0ea\xb1Z\x996W\x99\xb8\xccu\x95b)f5C\xe7\xdc\xad\x94\xa3\xfa\x1a5\xdau\x90\xc4\xa1\xd5\xfebr\xd9r\xc3\xea\x02\x88\xb3d\xd47\xca\x86\xa8N\x91\x19\xae\xfe\xd7\xfc\x0d\xaa5]\xc0of.\xfb\xcc\xb6\xef\xbc\x1b\x96\x14\x1b7^u\x87\xb8\xc4a[n\xe6r\x8c\xf4\x89~sM\xff\xdb\xb8\xa6\xaf\x9e<\x01\xdf\xbev\x01\xab5\xa7(\xc9\xbc\xd7\xcci;\xf3\xfe\x02'0\xa2?\xce\xe1\x04v\xe9\x8f\x8fp\x02\x87\xf4\xc7\x0bZf\x9f\xfe\xfa\x19N`\x07K}\x86\x13\xd8\xc7b\x9f\xe8\xdb\xd1\xa1[\x93\xb70Q\xfc\xbaR09\xeeT\x85=n\xc3x\x9a\xdc\xd2!\xb1_\xde;\x0c2q\x82ZL8\x15\xef\xc7\x86\xcf3\x12a\x10e\xfaW\xfd\x14\xdf\x8dAL\x84m\x89\xd9^\x84\x99\xe5\xc8\xa6_Zq\xdb\x9c\x8b\xdb\xe6\xdf(n\xeb\xe2\xbc\\~b\x8f\xf6\xd5\xd3\x16\x03\x81\xd1S\x9eE\xcaN\xeb\x9cT\xda\xceI\xa5\xa6e\xa1e\xa0\xda=\x1aPBEx`\xb0\xb0\x96\xd9(w\xb5\xc7\x7fT\x901h\xd4\x83\xa44r\x1ak9\x9b \x89g\xe1\xbch)q\x9b\x86\xb9x[\x1f\"\x86\xa0g\x07r\xec\xd6T\xb1\xd0=wfym \xd1\xd8\xde\xdb\xd9Q\xa6\xa8\x9a\x91Z\x7f\xf4M\xeavH\x8d\xfb\xd4\x8b7\xe3>\xfd\xff\xc6\xb5\xa7\x8e\xeb\x8f_z\xe52j\x17\x15\xd6\x94%\xc3#\xc8\xb5\x860\xb9\xde\x10\xe6F\xcd\xd4\xa0\xb5NoDr\xeb\xb0\xea+\x0dUx\x8072I/\xb9\xf7\x94\x89\xe3\x01\xbd\x89\x00=\xa8\xde\xef\xef\x0d\x06\x07\xec\xfd\xfe\xde\xde\xce\x1e]I\xfc\xd7\x13`\xf2&z\xb7\xaby.*\x1c\x94\x95\x1d\xb2\xe7\xc3a\x95]J\x14\x1a\xee\x96\xa5v\x86\xb5\xcf\x87\xa3\x83\xf2\xd5p\xef\xa9\x03<\xbf\xd63\x18\x0e\x87\xbb\xc3\xe1\xd0a\x97\x04\xd3&T4\xbe\xba!\xcf\x02\x87\x9d6\xa11\x8a\xfe\x18\xc06\xc1\xb6 l\x9d`\xf9}\x07\x9e=\x83\xa1\xca\xbe\x8b\x8b\"\xbf\xbd\xfd\x9d\xd1\x80~5\x1c\x8cv\x10&FM\xaf\xce\xac\xb6I\xf5k\xd1\x9a\xeeS\xad)\xf8\x0dw6\xdd~bO\xfc\xad\xdf\xfe\xe5\x92\xfe?\xd8zz\xf9\xfb\xd0\xdd\x19>8G\xdbs\xc5\xe0\x8dR\xc5\xdb\xff\xf9/\xb6}:\xfe:\xf1\xb7f\xbc\xf0\xe1\xc3\xfd\xa4\xfc\xe98\xdb\xcaW,\xe7\xec\xeep_+\xb4n7\xc5R\xc4\xa5|\x88\x89\x1d\xf0\x14\xcc\x01\xe3\xd0w\xf6PO\x92{\x01\x1f\xf1\xf3\xdc\x1e\xe0\xb2\x88Dx.F\xabc|\xab\xaf\xcc\x946\x9f\x0c/\xeb\xb9\xaf\xe0\x140\x80\xea\x9b8\xb7\xf3\xd2D\xcf\x85\xe1>\xa5h\x1a\xaf\x86\xf4\xd5\x00\xe3\xb4\x16v\x8cD\x8f\x01\xcc+\n\xb8\xc9\x93\xe3g\xd6\xe5v\x1d8S\xe9\xcd\xbc\xfe\xaai\x02B/\xeb\x895\x06\xeb\x89\xbf\\\x1diB#[\xc7\xf86\xca\xb5/\x9f\xe1\xcb\xb9\xf6\xe5\x0f\xd6\x0f\xf4\xe5\xafE\x92\x1f5b\xd15\xa7\xed\xc6\x88S\x16\xb2\x11\xb6\xac-\xe0V\xba=\x84x\x93K\x06a\x86\x1eK\x9a\xc1\x85\xe1:\xfa\xe0\xd6dVR2Lq\x0c\xe6z#c\xb4`\x149H\xf8W\x06\xe6\xbeKum\x0coH/2\x89/y\xe4\x1bm\x19]\x0c\x91\xfa<95Z\xdb\xc5l\xc0=\xd2\xe9q\xa0[\x1368\x8e@.y\x04\xf3V \x11\xff\xb4q<\nSW~\xbe5\xcd\xa9\xeb\xdd\\\xf8xN\xd3\x9fE\xcc\"\x1d\xbek\xcfgWJ\x1e\x84b\xd4\xfa\xe5\x17\xcb\x81c\x18p\xcd\x16)\xe3,\x86.X\x7f\x1eZ\x8e\n\x99\x9f\xfc(\x9c\x9e\xc5y\x98\xdf\xbddf(>}\x81x3\x99\x92\x8fI\x88j\xea\xc2e\x9ajZ\x17\x96\x0eI/A\xb4\xd4\xb5'\x86\x9ee\xae\x9c\x18\x08\xbb\xc5\x06\xff\xd7\x1c\x03\x84w\xb6\xb1\x12I\xd80\"\x83\xa8v\xea\xc2\x8d\x0e\x19\xb51Ak\xc9\xd8\xa5\xa0\xd6U\xe0\xcbS)\xc1;\x8c\xf5\xf2\x98\xae\x1e\x19E\xeb\x0dn\x8f1K\xfb\xeai\xcbD\xeb{\x87Z\xd1\xfa\x81Z \x13\xad\x0fGj-\x8f\x93\xad\xbb\x92\xf4\xdc ^_t\x89\xd7o\xba\xc4\xeb\xcb.\xf1\xfa\xbcK\xbc~\x07'L\xb6\x8d\x923.\xe3f\n\x13!A7\x8a\xbc\xcd\xa2\xf5\xc5\xba\xf2\xf8+8\x81kI\xd8G\xbf\xb9\xae \xff~\xd7\xa5Q\xaaD\xechY)\x89\xd8\xd1+\xd3f\x82v\x14\x91\xdfA]\xd0~\x87\x82\xf6S\xb8\x831\xc4\x0eJ\xd4\xe9\xb1\x8c\xc2\xa5\x00\x8fp!&G\xc9\xb9Q\xa0X\x98\x04\x8aw\x8c\xc4\xb8c\xe2@!2\xfc\xec\xb8\x80\xb2\xc2\x0d\x9ee,\xe4\x02\xc3\x15\x06\x08\x10\x02y\xf1\xd6\xbe\xe2\"G\xa301\xf5\x02\xa6\x9eJ\xdc\xffi\xc1\xa2Y\xf5\xa5*\xb3\xb8\xeak\xa0\xaa\xc4\xf8\x06Uw\"\xdd\xa0\xdb\x96J\x00\x15\x9a}hP=\xdc\xf0\xa8\x01\xdc\xcc&\xc4\x1c\"\xda\x85W``KtM0R\xdf<\xf22*\x95\xed\x82\x85\x11\x15~\xec?\x9c\xa0\xe1\x0coH\n\xba\xec\xbb%\xf9\xe4\xa0U\xcd\x0f\x0e\x8fF\xf6\xactu?\xde.}\"\x9e\x19\x03\xfe\xaegP\xa7\xf1X\x8b\x99\xea3\xb7\x0b\xc7\x85\xd4N\xbd\x8f\xb0 \xa9\xf7\x1a~\x84\xa4=\x02\x83\xe0o,\x0b&\xe4\xd2\xa6c0\x02)gF\x03\n\x05}\x7f\x0f9w\x88\xa3_K\xd9\xe0\xeb\xc3u0 #\xc6O\xae\xb15\xddG\x15\x8e\xba\xeaU\xdc\xc3\xfa$_\x84\x95\xd1\xfa\x83,on\x9a\x19\xd0\xfab:\x0c\xa3\xb4\x1aq\xd5\xc0\x05r\xe3G\x8em\xb1\xc7U\xf5F# \xcd\xb1Y\xc9\xdc\x11\x93\xb1[\x1d\xaf\xf6\x9d\xa4\x905Q\xe3S\xdd\xe6\xfc\xfe\xa2\xc6^\x9e\xb37\"\x19E\xa3\x01\x91xb\xacMT\xb1\x08\xb3SV\x160\xf1\xf0j\xb9\xd0\x84\xe7C\x91\xd89\xf6\xb2\x15 \xceIDh/2\xcd#\xbc\xfb\xb7,i\x15\xf7\x89\xa3\xcc\xf4\xad. \x8e\xb8x\xa7}\xbb\xa0\x0cmi \\\xd7\x1e\xd25\xa8XH\xff\xfe\x80\xb1lb\x9d\xa5\x80|}H\xc3\xb1\xc6\xdeF\\\x0f\x18\xd5\xd3\xd4l\xeeB\xd8\xf7x\x85j0\xe2\xd4\xb8\xf5\xd3\xd8\xb6p\x95\xde\xa6\xfejE\xd21\x04I\x11M\xe3\x1fr\x98\x13\x16\x17\xd4r\xdc\xa6\x9fa\xb3 \xad\x17\x99@dt{\x0c\xfe\xa1\x86\xf4\xcd\x86[\"\xe3\xf2\xcdGiZ\x7f\x15\xaa\x9bO0\xae\xcd\x944\xcc\xf9\xae\xbe\xc9v\xbc\x81g!\x8d\x9fW\x0c\xdan\x17\x13f\xe6\xfe\x0f\x9d.\xeeU\x1d\x15:\xc1\xa7h\xe3\xcf\x08\x91J\xde\x8eqCE\x02l?\xe6\"\xf7\x0d\xc3\x88\x1f-R\x1c\x1d\xa8RBLy\xd1\xe4\xd1d*\xa0\xa4\x06\x18\xda\x96\"\xb2\x887M\x8e*\xa5\xfcb\xd2\xcaQ\xea\xa1\xa7\x0f\xcf$\x8f\xa6\x1f\xaco\xfa\xc4V\x16\xae\xbdL\x03[\x03\x03\xed\xba\"\x0d[s\xa9tx?\xd6\xfc\xb2\xdb\xcc\x7f\xae\x8b\xf9E\x92D2\xb3\xd9\xab}I\x90\xac\xda\xa7\x0b\xab\x1bu1\x84\xdcv[uZ\xf2+k\x80\xfa\x99-\x9f\xb23\xa6\xf1\xdc\x95\xa2\xe6\xd4\x0b\xab\xd1s4\x87\x13\xba\xb4\xa3\xeb1\xda\xe8P\xb4\x8a\xe4Qj\xc7\x8ekN\xdb_\x1e\x0d\xa2\xdaZ\x89\x1a\xe1\xfe\xd0h\xcf\x9a\x93\xdcb\x91j\xe8\x9cg\xe2\xae\xb9I\xad\xe7A@\xb2\x8c\x9e\x7f\x18\xab\xb9X\xd19#S\xd36\xb5\x90d\xe1u3\x86\x8c\x99\x87\x95\x0e)kn\xe4~Vb\x0dw\x84\xb5\xac\xc4\x1e\xd7\xa4\xbab\xbe\xa5\xc9N\xb7a\x83\xcb\x81\xce\x88,\xb6w\xf6v\xb5\x8a\x91}Uz[\xf0\xe2\xaa\xe7\x02J\x9f\xecCu\xafD\xac\xd1]u\xe4L\xf1\xaf\x96\x9ei\\\xadV\x18\xb0\xb3\x0eS\xb4L\x9b\x93\xfcc\x92Dd\xaa\xe6\x87Xh\xe4\x1a7%2)\x1f\x97'\xeb\xb2\xc1\x1d\x9cy\x98\xde\xea\x13 \x928\x08#r\x91\xfaq\xe6\xb3\xd2O\x9e\xc0\x0d0'\xff\xe1h\xc72YOP\xeem\xa2l\xdb8\xccY6\xcfq;\xe3\xc5<]\xc34\xbf+i\xdb\x8ce\x18\xc3\xbc\x18\xecX\xae}\xa5\x88\xa54\x82\xabu\x1a\xd98\xa9\x9a\x81S\xb0g(\xb5\x0d\x08%\x19\xcd\x9f9.\xdc\xdaH\xfe\x95\xdf\x9e\x18\xc3\xb0?\xa8t\xe6z\xc0 \xfc(\xba\xf6\x83/\xff\xbb \x05\xf1R\x92\x91\\\x11{<\x16\"\xf5\x9a\xe3$\x0fgw\xcf\xa3H\xad\xbd\x1a\xc8\xa5nI\xdd5\xe3\xff1\x1f\xe7j\x98\xd2\x9a\xb2\x9d6\xb8\xf2\x95\xebj\xfa\xd7\xd8\x07\xa2\x19\xcd\xba=i[\xd5R%\x1b\x83v\xdb\xa8\xeb6\xe35\xe2]-\x93\"\xce1\x15\x06lA.\xdf\xb7V{\xd5F\xdej\xe1\xa2\x88G\xeb\xab\x96\xc5\xfe\x18\x8ev-\xc4\x9c\xe2\xb9C\x7ffI\x9a\xdb\xd7\x8e\x0b\xab\xcd\xcdz%Ud\xba*\xaca\xce\xa3\x1a6\xd7\x0b\x17tR\x04:\x9b\xc4\x06\x0fQ\x1f\xe7\xe8jE\xe2i\x18\xcf_\xf2\xd9\xcb\x9a\x0c\x1c\xba\x156\x0b\x96\xb3_xQ2\xbfHVo\xc9\x0d\x89>a\x88'c\xa0\xa3\x1b\x1e\xbd\xd6\x90\x9e(\xf4\xae\x82\"MI\x9cs\xc6\x0c\xf3\x89c\x9e\x03?\xc8E\x1b?3\x16\x0b\x8f\xe4\x88\x8d\xa2\x11g\xcba\n\x03\x8be\x03,VS?',\xb8WD\x97\xd4{\x7fI\xe8\xaa\x14\x0c\\\x1e.\x89\x9dt\x19\xab\x00\x87F\xe6\xadH:K\xd2\xe5g\xac\xf7\xcd\xec=\xa1\x84\x85\x9f\xde\xd9\xa1\x8bF\x0d\xcd\x85\xcct\xa7 *n\xa5F\xcf\xe2)\x8b\x0c\xae\xe7>{D\xbe#\nf \xf1\xaf\xf4\xaf\xedO\x82K\x97\xef\xc2\xe2:\n\x03\x11\xb8\xc6V}>\xfe\xd4\xfc\x95\xd8\xb2\xdf\x19D*R\x9c\x93\\\x1a\x1b\x9f\x90\xac\x03\x8d\xf1\xad8oC\x87\xc2-4I\xfb\xe0\xc4v\xb4\x14z)\x89\x88\x9f\x11\xbb\x89\xa0\x1c\x03\xd6b_\xb6!\xa4Z\x9d\xba\x99\xee@v]\xa1\x86\xf8\xd2\xea&\xb6\xa1\x02i$\x16$\xcf\xd1\x89>M\xc6N\x88\xc2-E\\\xd0\x93\xe2\xd5R\xa1k\xd6\xf3\xa7S\x8a\x9c\xc3x~\x91\xd8w\x8a8\xef\xb6M\xcc\xc9\xa3\x0b\x95h\xf1\xfe\x1e\x16\xc6(Y\xb3\x0e\xb7:\xa1\x88\xbb\x93\x8f\x1c=\x86!b\xf0\xf6\x95HKO\xd7\xc2]9\xad\xba\xd4v\xdaN\x19{\xc3\xa8<}\xf3\xe2\xe4\xd0\x04\xb5\x03-\xfd\x08\xb9|\xd4\xd7\xd6tWG\x8d\x82\xa4\xb3\x06/`\\\xed,2V}\x81^Sn\x8cL\x19\xee\xcb\x9a\xeb\xb4\xcc\x17\xd3\xb2`\x97t,7^\xbd\xaaf\x05m\xfb\x84\xe3\xb9\xcf\x1c\xb5\x97\xe75\xd1\xdbP\xf2\x16\xc3\xec\x05m3\x8c\xe7\xbcQFFb\xa0\x81\x9c\x0b\xe8PZ\xe0]\xb1C\x03\x8b\xbfGm\x08\x17Ji^\x9c`N\xbc!\xd2\x98\xdaQ\xb5\x8ed\x16\x15\xd9\xe2\x85\x02\xd5[\x85\x19\x8a)G\xceT\xca\xcd\xe5\x88/\xf5\xf3g\x16\xb1\x88\x8b\x94L\xc3\xbe\xe5\xb4\xe2>\xbd\xb6\xb0I^\xb0\xfe\x08@\x9f\xe7\xa9\x9f\x93\xf9\xddz}9\xa0}\xd1gOQ\x00\\\x92T\x87\xf8\xc95\xdd:\xbe\xf2Es\xda\xc5GO\xe9G7\xfa\x91\xb5M\x9a\x9f\xf9\xab\x1e\xa9T\x03[\xb3\xe6\\N\x97\xf0[\x8f\xd5\xf5\xd2\x8f\x7f\xc8\xc5\xb2\x06?\xc6&@\x1cP\x10\xc6\xe0c\xe8E\xf25\x87\xdb\x05II\xc1\x87\xe2c\x08\x85\x1c\xaeI\x18\xcf\xc5\xf6\xf4\xe8\xb8\xa6%5\x80\xfds\x19n2\xb2>z\x81\xd6\x19>]C\xce\xb0\x11\xdb{C\xc7l\xb4\xc3q\xc0\x01\x9d!\xbd*\xe9\xf7\x07\x17,\xbf\xa1B\x02FytP\x06r\x13]s\xeaxU\x9c\x8c\x87G\xa84\xc5\xd3.O9\xcc~@\xc1\xf2T\x17\x1f\x07_\x8d\x86\xea\xab\xd0\x14h\xa2\xd4b\xa0\xcd_\x861!\xe4\xf7\xa5\xf6\xa4\xd3[^\xc8tUSWz=@\xd7\x8e\x95\xf5\x0b\xdd\x1d%U|\xaf$\xe5Q\xcf\xe4\xd7,\xe2i\xa9\xa0\xa9\xcc*O\xab1\x8e\x0d]]\xcf\x83\xe8\xbb*D\xc4/\xd9;\xb1\x1b\x18\xd2\xac\x9d@hW\xfa\xae\xd6)\xe3\xfd\x97\xc3JR\xe8H\x86\x00c\xd4\x03U\xddk\x9d\xc3\x7f\xc4\xfc\xad\xd1\xf7\xc7oG\xb3\xd4\x93\xb3\x97J\xc4O}S&\xfc\xd6 \xd0\x9a^Bgx\xfe=\xc6( T\x0d\x86\xe6\xaa\x84\x94\x0bTu\xf2T;\xb6\x9f:.L\xaci\x98\xad\xe8\x01\xf2\x12=\xa9-\x17\xac\xab\xdcOylVz\x1b\xfbyx\xc3\xfc+1\x96c\xf6\x8a\xcd\xf7\xc7\x94\xd0gd\xca\x9eRT\xee\xcf\xd1\x08\xee\xa5\xa94B\x1f\xca\xdd%j\xd8p\xdf\x18K\xdb\x10\x1d\xad4\xfb\xd3ft\x03\\\xd4\xa7\xd8i\x96\x01\x8e{\xe3Y\x0c\x00\xec`\xf0y \x8f=D\xc5\xecX\xfa&\x9e\xf8\x9a\xdc!\x0d\xe8\x08Y\x1d\xe6B\xf5\xd4Y\x87S\xdd\xc31l\xb08\x8e1\xb7\xde\xfb\xa9i\xbc(i\x84\xbd&\"\x80\x13\xa0\xdcU\xd8\xb0\x9aR\xf6\x1bZY\x89\xc8\x9d\x1a\xc4\x81<\xb1\xbe\xfc\x9f\x9acN\xedL\x96\\\xd5\xa7l\xc5\xfa\xf6J\x9c\xea=$L\xcdAmh&\\H \xd4\xd5\xda,\xc9t\xd5\xc4\xabw\x05}\xa1\xea\x8fl\x87\xd9\xf8a\x88\xcc:7#M\x08\xafM~r\x02h\xadf\x9e\x95\xc6\x8c\xb4r\xa7Y\x9e\xac\xa4I\xe9\x00\xda\xfa\x80P\xeaGH(\xcfZ@\xc1\xb0\xea\x0bD\xbd\xbc\xc2\xda\xa3\x13\xa6\x80\xee\xbd\xb8:\xc1\xb1\"i\x86\x99\xc4\xbb\xd7N\x98}d\x85\x19\xdaj\xb4\xd3\xd6\x8c\xfc\xadv\xbf\xd4J\xf7\x96\x9a\xd6\xa6\xa7\x07\xae\x84z\x0c\x0d\x96\xd1\x0c\xf1\x0f\xd3\x84k\xa3\xd3\xeb\x94\x15\x95\xd0\x9aebB\x146\x89//\xb5\x12\xd1j_;.dU\xe7\x98kc\xe6\xf9\xc5|I\xe2\xfce\xe4g\xbd\x1dNd\xb8\xa8\xbe'5\x1f.\x84\x8d!b\xda\x0d\x8fn\x10\x93[\xf5\x18J\x99\xec\xbf\xfc\xd0\xa9\xdda\"\x16\xf9A\x9d\x98\x06\x8c\xa6.\x8f3E&\x18\xfbR>f<\x9e\x8b\x98\xa4\x19\x908H\xa6a<\xafgD\xc8\x17$\xc6\x8d\x87\xc9\xd2\xca\xc3\x0fD\xe0\x17\x1fx\x03\x06e\xb88c\xb9\xc1@/\xd57\xffF\x18\x19\x18\xcc\x04\xf4S\x13\xb5\x88\x85\xc0\x0cCC\x8c\x9b\x1f\x84}n}\xdc<\x9b\xa6\x0f\xac\xa2\x16gp\xbd\x03\x1d\xae\xdb\x17\x0c\xdb=y\x82LO\xb9\x1e\xe4w\xcdC\xbe\x85P\xc3\xd0>\xde\xf5]N\xde\xf2l\xdd1FWA\xcf\xf3\xea1\x1cWv\xcb\xeaV\xfd!\x99\xcd2\x92\xff@\x97@R\xe4\x90\xcc\xe0:)\xe2if\x9a]\xb5MZ9l\x82\x8d\xb6\xfd\x03\xc7\xd8\x0e\xdbs\xfd\xdb\xc9\xeb\x99\xd1\x99!juO!\xd5@\nuE\x80\xae\x08n\xe0\xb1\xee1\x05\xb3\xbe'\xad\x88)oCD\xb4\x00\xcf|\xd8\xbaU4J\xe2\xda\xec\x8f\xf5\xde,\xdd\x04\xa1\xb84\x9f#@\xcb\xe8\x0e\xf7\xf7\xcc\xed\xde*\xf2\xd9a\xdb\xd4od^\x98\x9dq\xbca\xc7\x8ei\x13 \xd4bIh\x83\x1d\n\xac+%\xee\xd1\xed$\x90\xce\xd3\x01\xdc\xc3\x82M\x9c\xde\xe2\x10\xf8\xe1\x8a\xd3\x81\xc7V\xea8\xdem\x1a\xe63/HX\xa7\xdcL\x8d\xe1\x98\x11\x91\x84rZ$\xb9)\x1bUJi\x08\xfag\xf3\x04\x86t`\x18\xbax\xb4\xb7\x07O \x9f\xa4\x1a=\xd7Z#\xd4$^\x85r\xdd<;\xa1\xbc\x95\x89jy^e\x96\xf1#\x0c\xbfB\xf8\xce\x82\xc8O\xe7\x842\xa8~\x0cK\xffk\xb8,\x96\x90\xa1;\xc7\xe0+\xe5\xb3}9\xcd\xf5p\xdfAWNJ6i)\x9e\x12a\xdf\xf7\x1c\xd4\xa2u%J'\x8b\x9c;JH\xcb\xf5\xdb\xb4\x0f\x92\xd6\xdasHe\xbc0\xfb)$,\xd0H\xf31\x9d\x88\xfb{ \x06\x14/\xf7\xb4\"0\x9b\xbd\xd5\xb8\xd6W\x8c\x9e\xa5\x13r\x80\xb4\x9c\xdb\xa1\xc0\xa9\xcd\xb2'\x9a\xedU[\xbe\x1b\xc3\xa3#\xa7\x14\x0d\x1bOB\x14\x88Z~\x16\x84\xa1\xa5\x17\x8b\xb2\x12\x91\x9f\x87\xf1\xb0\xb5\xc8u\x18\xfb\xe9\x9d\xa1\x08H\x12(\xfdq\xc2*A2\xaf\xad\x95\"\x9fm\xb5\x96`\x84vg/^\xdb\xc41\x02\x1c\xaa\xe6\x82l\xd4\xde\x9f \xdb\xea(\x91\xcf\x86\xfb\x11\xe9*\xb3\xd5R\x08\xaa~\x8f\xe0\xc7v\x08.\xc8\xd7\xeeZbx\xf6\xec\x19\x18\xac\xb6\xf9t\xfa\x19\xd9\xdf\xed\xae\xea\xb7.@\n\xa32cE\xa8\xedpzO\x0cp&\xcc\xc6\x1d\x95;\xf5\xe8f.\xcf\x8f\xd6\xf8T\x95\xbe\xeb\xd1\xd7M\x1b\xc7\"\xf6\x16\xd1F\xc6\xe7riz\xfc\xb9\xe2\x10L{5\xba\x94\x98*\x83\xc6\xa1B\x01\xa4\xa4\x189\xc0\xb64\xd3h\x10\xb7\xc4\x94;L\x99\xf0\x1cOn\xe49\xe1\x99,\x91;\xc575\x11\x1d=\xdd\xb7\xca'\x87 b\xa1I\xcf\x1cV\xe1f\xecB\x98\xbd\xf7\xdf\xdb\xb1S\x16K\xf8\xe1\\\xca\xb7\xb6`\xe8\x08\x91\x80(T\xbe\xdcDZ?\xa6\x07 \xe9p\x84@\xcb\x95V8\x00\x8f\xfe$7\xdd\\\x19@\xa2\x8c`m1\xa3\xd7\xcc\xcdm\xf4k\xafk\xf9A\x8bH\x8c\xd9\xdd#\xcf>K\x93%\xe5\x15S\x07\x15\xc35\xae\xac\xc6J\xe5\x15\xfb\xb45\x841\xcc\x95\x15eX!Z\xe1\x13\xaf8\x87'H\xeb\xb8\x069\x83\xe9\xd0\xad\xc4\x17\x92\xf6\x97\xc7\xd9\xc5\x08\xa4\xa7\xadE*\xf5\x04\xe7Z\xb5\x85#?\xcb\xdf\x18>\xc0\xb1O\xf2\xcb\xb6\xd1ky\x97\x1b?* {\xc1\xae0\x08Q\xce\x843Z\xfd\xe8q\x15\xfe\x06d\x12\xb2\xf0l\x86\xd8o\x85\xb4p\xf5%2\x89\n\xd6O\xb1\x14\\\x95\x89\x14\xd8\x89\xc6\xf8\xef\xb4\x8a\xc6\x99*h\x14\xe9!~\xb8q\xa1\x15>\xe0gY\xfd\xd1\x96\xf4\xcc(/@\xb2\xb6\xa2\xd8GL\x18X\xddw\xee+\x9fEO-`\x9bEQ\xe5\x7fc\xfc\xab\xd9o\x8dG\x8a`\xd6\xd4Q\xde\x8dai\x92FX\x00{\xe2\xa5\xc4\x9f~~\x13\xe7\xc3\xfd\x17gv\x0e?\xea\xdc\x18\xf5\xfb\xdc\xa8E\x16\xce\x8e\xa6A#M\x87j\x98#\x08\xe1\x18\x8a#\x0877\xf5L\x19\xf0\xc6px\xa1\x83\xfdG\xad4OQ\x1cp<\x1c\xc2\x16\x04\xadr\x1dQS\xf9!]9\xb4\x9b\xa1\xe3\xb2\xcfa\x93\x03(+\xe7-\xa0\x001V\xc9\x91\xec\x16K\"\xc1j\x0ca\xeb\x84\xf7\xc6\xe5P0 g3lb\xd8\x84\x0c\x9eAQ\x9e$\x05lA\xe60\x7f`\x84\xda3d\xe6\xc2\xad\xad\xb6!\x97\xc4\xf3\x8c\x07\x0b\\1\x1ep\x05\xc7\x90\x1d\xc1\xaa\x0d\xe8P\x03[{>\x1cCz\x04\x9b\x9b~\x1b\xfa\xa0\xc7\x84\x9c\xf7\xa2\xb8\xce\xf2\xd4\xa6|\x82\xef\x02O\x8d\xa1_X8H\xa4\xd6\x8a\x8a\xa0\xf0\xf5e\xc9\x84\xee4f\xba\xdb\x03\xe9\x89\xcaz-\x9a\xeb\x8eE\xc3+{a\xbf\xa6\x1bJ^\x16\x0e\xaa\xe4\x9a&@\xa6\x96\xae\xfa\xb6d6\x18(\xeb\x94smM.]Y\x14V\xb2\xf2L\"\x963\x87K&8\"r\x02\x94\xb8C\xa2\xafK\xa8\x98\xaf;\xe8\xdb~\x83\xae\xc1\xa6W\xc5g\xfd*~a\xff\xb6~\xa7\xbf\xf6\xad\xbb\x97V\xa3\x92W\x96\xde\xb6|\xd6\xa4\xadF\xa4\xa0\x15\x1b\xb6\x9d\xd3\xd3i\x84i!\x1c\xbe \x19+!\xcd\x9f\xcf\xf9M\xcaO\xc3!\x8f\xdaL\xd1\xc6\xde\xbe\x0b!\x9b\xf6\xc4)\x7f\x9a4yF\x94\xfc\xf0\xad\x0b\xfe\xbc\x8d\x9f\xad\xb3\x10t\xd8q\x8d\xc5\x84SH\x91\x07yq\x97\x13\x91\xf1\x9dbU\xf5!WQ\xe5u\x9b\xae\xb6~\xbdl\xeb\x17\x05\xf3;?_x\xcb0.i\xc6\x1e\"[:\x9f\xe8\x1aq\x04 \x8an\xdb\xd0&\xa5\xbd]\xb4\xafu1F\x07\x99$-\xc9\xe5\x03\x11,\xc1X\x82\x9e\xe0\x11e\xa5w\x9e\xc2)\xec\xc2\x98\xdd\x8dv\xe0\x14v\xf8\xdd\xf0\xe9\x10Na\x04c\x93\xe8\x05iE\xd8\x84\x19\x1c\xa3\xb0O\xc8\xeffm4D\x9f\x04\xb8\x11\x1c\xc3ptX\x12rQ\x8b^ \x04\x9da.\xd2'-.m\x8er\x19\xc3\xa7#x\xc2\x88X2\xa1\x83\x1b^:L8@\xd9\x17{g\x08O r\xe0\xf8\x18\xf6\xe1\x1e\xf6w\xe0 %^\x9f\x89\x0cb\xd8\xdd\xec;t\xd7`\xf6).\xb9\x7f<3>\xde\x8d.]e(!\xf6\xbe\xfe\xcc\x97F4\xdc+G4\x1c\xc1=\xd8bL\xf2\x10}:\xc4\xd1`\xf7\x80\x7fw\xcc\x13\x96\xdd\xdf#9+%x\xfb^\xe3\xdf}\xfc\xf8\x8b\xf2ng\x0dh\xd4\x9f\x15\x06\x08\x1d*\x10\x92@\xe6\xd7AV8\"\xef\x1b\xad\x89\x82\x8c\xa5\x92\x1bI`\xd2\x0eQO\x12\x97\xc6X\x94/\xc2\xcfi\xdd;.\xee\xe4!\xc5s\x81\xdc\x9e\x1d\x94i\xe4\\H\x19>\x0f\x98\x18u\x00O\x00\xf3\xc5\xdd\xb3I\xe4\xdc\x0c\xcb%w\x0f<\x95\x1cer\xc4w\x18\x1bg\xf3\x04fM\x8co\xc2\xd2\xdd\x14\xc9M\x19\xa7\xa9M|\x8a\x8aq\x8a^\xbe\x94$\x9f&\x1d\x1d\xb71>\xe7b\x10\x9d\xde\x02$\xdd\x85\xa5\xc9V&\xaeT\xaf\x0c\x04(\xc3\xa2\xa4\xa8=\xa4\xc7\xeb\xe6I\x9f\xce\xf0\xe3&u\x99j\xeeK\x07\x11\x157\x81l7\x8eO\xf9.\xf7\xb8b\xe9\x84\x1e\x0e\xb9w\x1e%\xb7\xe5\x93\xf6y\xd8$U\x84N\x82\x12V\x0dC\xc0\xba\x95y\xa8\xba\xb37\x1b\x1e8\x90{o\xde\x9f\x7f<{yq\xf5\xee\xf9\xffw\xf5\xe2o\x17g\xe7t=\x0dL\xb2\xb8\x139\x89\x0e1\x98\x05\xe9\x9fwy\xf6\x18\x83\xdf\x0b\xdf\x1a\xc5di\xd8a\xa2R\xb3J2\x9fie)\xbd\x00\xb0\xe5\x18N\x92\x1e\x01\x13\xc4\xc5{\xb5\xdb\x94\x1f\x89K\x8f;\x1e\\\xd8\x1dqZi\x96$\xb6c\x14\x87\x12\xca\x901K\xd3'O\x84'x\xf9\xcc\x1eb\xc2\xbcJ\xa9\xd8\\\xaa\x9d\xd9\x0d\xf8\x1864\xb2\x93\xfa\xbab\xf1u\xbe\xbc\xf3\xbf\x96\x91\xa3|\x1b\x05\xcb\xab$\x89\xce\xc3\xdf\xe8t\x1e\x0e\x9fb\xf2\xa1+\xeea\xd3\xb9\xe2\xb5\x13[sJT=\xbf\xb8`\xbb\x87\x1f\x8cT\x7fd\xf3\xf0EZ\x0b\xcc\x16!\xb5\xec Y\xeb\xa3v]\xd1\x91k\xcb\xb8\x06\xfb\xc9st\xf5\xa7\x0d\xb1_\x18\x1cJ+!\x13\xdetY\xa9Xa_hmM\x98\xe1K\xdd\xd5\xad\xcd\xccAV\xec16\x08\x02ZGc\xdf\xd43\xd0\xc9\xb5\xd5\\j\xb5\xd0B\x0c\x933\x0c\xd2\"\xd5\xa5\xbc\x07\x99\xc4\x97FvK\xc8\xa5j\xc7\x83\xad\xcb\xb3\x0f\xdcV\xdc\x84\xee\xcc\xbd0\x13\xe7>7F1\xb3\x812\n\xf7\xff\xa0\xf9\xa3\x97\xcf\x8c\xb9Q\x13\xce\x19_\xe1 \xdf\xb1\x16\xa1Z\xb7is\x91J\xce\x1e'\xb0p\xa1F\xe9I\xc7\xe7\xc6\xa0\xfe.\xbb\xf5W\xc3\xfd\xb6x\x9d\xa0\x06\x0fh\xd3\x13\x11\xad\x9eH6\xd7\xe4=\xc9(\x89]\x99\x0e/\x8b(\x0fW\x11\xa1\x10\x1c\xeeo]\x87\xb9\xf6X\xac)\x1a\x06Gh\xbeK\x8e\xd8\xf2\x1b9p#\xe2\x9f\xba\x98\xb4R\xc7\x7f e\x82\x1cB\x04\x04\x10\xeb`\xd9\x19}W\xb0\xec~#XvF\x8f\x02\xcbn\x03,;\x8e[=\xa2`b\x7ftZ\xb85\xa0\xb5\xbf\xfb]\xa1u\xf8\x8d\xd0\xda\xdf}\x14\xb4\x0e\x1b\xd0:\xd0Ck_y\x9d\xe8\xda\xf9\x83F0\xcc\xe6LX}a\xfc\x16x&\x8f\xa7\xf2(\xb1\xfa\xd5\x8b~S\xb1Z\x890\x90\x90\x1f\xa2\x19\x1e.\xba>M\xa0\xd9(\x96>>\xa1\xbd\xe5w\x9d\x1f\xe3\xeac \xa4\x89\xe4\xcc%\x19(\x1b\xa5\x1b\xd0\x83\xee\x14\x17\xef\xc5\xc7j1\x9b\x9c\xac\xa0\x0f\xb5\n\xbd(Vq\xf1\xc6_\xae\xd3x\x1b\x9d+.^\xef\xf3u\xeam\xa5\x8e\xa1\x1f\x85,.\xde\xfe\x87u\xda\xef\xb4\x1d\x86\xaa\xe2\xf3u*n\xa1\xc6\xa1\x17E\x0e=\xa9rX\x872\x87j4\x17\xfdF\xd3I\xac\x03\x94v\xd1Z\xc6\xfa3\x8b\x0eUz+\x8e\xb51\x14\xd4\x8b0w\xc4M\xb0\xac\xbef\xd3\xa0\xa5\xc9\x1eD\x0c\x12\x1c\xac)\x0cI\x1d\xa9\x93_\x0b?j\x8f\x1f\x01ZiC\x87lA:\x0c\x85\x8df\xeb\xc1\xc3\xcf\x80\xfb{\x8e,KY\x88\xde/\\\x19E\x18g+L+\xd6\xefd2)F\x98\xffRC\xca\xdf\xdaqq>=\xe3f\xd3%]Q\xba\xf3 \x8e\xe4\xfe\x92\xde\xd2\xcf\x83\x85\xbd\xed\xfd>z\xd8\x9e;\xde\xdf\x930\xb6-\xb0Dx\xb0\xb22\x9e\xec\x89\xa5P\xf7<\x0f,\xc7q\xc1:\xe6\xf4\x06\xae+]6\xf4:\\\x0c\xf2\xa4N\xa3\xf6\xef?\xd5*\x8fW;YU\xcfmf{\x8e\xda\x11\x0e\x90\xb1Z.-\xed\xb6\x94\x17\xcc\xd6,i\x9c\xa8\xb9\xf0u\xa7'pY\xef\xfd=\np\x06,\xd5\x9cr4\xeb)>\xee\x8f\x9e\xd2G\x80\xf6\xd1\xa6\xf1\xa6\xf0\x8c\xf7'\xa7\xbfZ\xdd\x84\xaa\xf2\x9d.\x04Je\xe6RH\x07\xb8\x10\x97\xbf\xd2\xf2WR\xfe\xaa6_/\xf1^\x88\xae\x03[t\xf5`\x0e,\xd8\xa2\xcb\xa9\x90%z\xa1\x0b\xbe\xc3\xcc7\x10\x9c\xa5^0\xe1*\xd8\x9ae\n\xd3\xec\x0e\x8e`\xc6\x0ci77gf `4\x991 `0\x99\xb5J\x00i7ia\xd6KZ\xda\x8c\x83\x1f!\x01\x0c\xe1\x18\x8d\x90Q\x02\xe8\xc31\x84f \xa0\x8c\xa5\x82\xa8\x98\x92>\xb1\xc6\xa4\xb6\xb8q.\x82\x92\x9b\xe3\xdbf z\xd3\xba\x7f\xad\xc6\x96\xf5\x90\x1a\x98:\xaf\xad\x11\xc9\xe4\xff[\x1b\x1a\xb66\x84\x1e\xfaz\x0cf=\xbdp\xdf\xd4E\x10\x86\x1cm}\xa5\x10?X\xac\x0f\xda0@\\X\"\xe2\x87\x984\xd99\xba\xa8\xf1\xe5\x1f\x1a\x03\x03\xa9\x91\xfe\xd4\xd8t\xa6\xeacz&IB\x07s\x1c\xcc)\xf9\n\xb2x\xa1'D\xff\xde\xc1\x0c\xe5\xa5O\x7f\xce\xed\xa9\xf7p\xc2\xf5z\xc9\xda\xeeU\xadud\xaf\x17\x17Fu\xc3\x1d\xee\x8e\x96\\\x02\xea!\x9e`P\x9e\xe3c8\x84\x1f)\xfd{\n \x8ca\x08[\x908\x0e\xdahk^\xf4\x1a\xf0\xfb\xb5\x06\xbc;z\xba\xfbt\xff`\xf4\xf4;\x8dz\xd7<\xea\xbc9\xac\x1d\x1c\x16\x03F\xaf\xc1}\xea\xbd?\xbeea\x99\x96j\x0b>y\xf4\xfa|U\x1bQ[J\xc6\x90\xeeB\x04\xc0\xc0e\xa0v!\xe1<\xae\\\xc7h\x87\xbd\xa3\x10\xd8\xed\xd5\x87\xb7\x8f\xee\xc3\xa1\xa1\x0f{#\xf6\x8e\xf6\xe1P\xe9\x83|\x97\xa9t]\x1f\xfb\x1d\xe1\x15\xd7OI}\x02\xff\xfd\xdf\xc4U\x83`\xe6p\x8a\xa9Z\xfe\xfb\xbfs\x97\x9d\x14,\x0c\xe5&=\xb5\xcb\x1dBD\xc4\x11B\x0f\xf6\xf2Q\xeaT!\xc9\xec\\\xf9&\x17\xdf\xe4\xe57\xb9\xf4\x0d)\x9f\x10\xc7`\x03\xecT:\xcf\xd2\xea\x1aaa\x0c\x90\xb9\x96\xfc\xa4\xa4\xc0`K\x8d\xcb/\xae\xb8\x0c\xf3\x9b\x08q\x86\x81\xbb\xa81\xe7\x9cNH8\x19\x13S\"\x80\x0d\x04)\x00\xd2\x95\n\x07\xaa\x85V\xf7\x80P\xd8\x0f\x11\xd5\xe0\xedYO\xb9\x1a\xe1\x92\x19!\xb8A\xaaM\x90\x13\xb2|\xa3\x05\xf7\x89\xe56!\xdcgoX\x12G\x9b\x9bt\xd89\x17\xae\xffxB\xe9\x1e\xe7\x88\x13\xb5\xec\x1b\xd8\x84\xf0\x12~\xd4\xb9v\xebIY\xfd\x88_\xfccF\x0c\x9b\xb0\xb5\x95\x8bq\x1f\xe1\xd2\x1et\x0c\x97~\xf0\xed\x03>\xec\x83\x10\x84\xc6\xa9\x1c\xe3\xd0U\x15\x1cl\xe2\xfa\xb48\xdco.\xab^\x8d\x8e\x0c\x8drK\x0f\x04\xca\xf0\x12\xcf\xfc~\xfdhN\xf6\xb7\xf5\x03\xa9\x8dZg\xfa\xf4cg\xf4Hx\xec\xaa\xfd\xb0\xcd\x00\x91\x1f\x8d\xf0\x11\x8b\xf37\xdc?88\x18\x0d)\x17Q\xbe\xdf\xe9\xd9\xedG\x82\xaf\xd1\xedF\x1f(gc+#\x18\xee7\x87P\x1b\xd5\xcee\xab\x08\x9fv\xfb\xff:\x8c\x06\xcfN\xf8\xe7\xc3\xd1\xa1\xc3E\xe1[\x9cv\\%\xb76\xa5\x12(X\x1d\xc7\xedF\x07\xff\x10\xf4W\x03\x8c\x84\xdb\xd2\xcb#$/\x9bX0T\xb0`\xda\x0e\xa4P\x03\xa4\xd0\x08\xa4\xb0\x07\x90\xbe\x13\xcaD\xdf\xebr\xc5\xa3:\xefG\xc0\x88\x10[\xd2>@\xaf\xd3\x9e\xd8u\x0d\xe4j\xc4fM8\xde\x88\xd8\xaaF\xe4b\x84\xfd\xce\xe8`\x9f\x0e2\x86S\xc6\x08\x0d\x86\x07\xfb\x03\xb8\x87\x18\xc6\xdd\x14\xc8\x1a8\xfa\xd1\xc3a\x83\xb8\xaf\xa1\xf0?n8\xdf\x0f\xd5\xaf\x87\xe9\xebx\x92>\x1b\xed\xf6\xean?\xe8\xf7\xef.\xb6\xdc\xect\x0f\xe4\xde\xd5\xdd\xd7Q\xe2k\xb0\xfb\xe3\xba\x9b`\x95\x95\xa2ac \xb8\xbe^\xdd\xf8^Pktc\xd8\xb7\x1b\xaf\x92\xe2:\"\x8f\x04\xc7ag?\x06\x82\x01\xed\xd7\x8fG\xc2\xa3\xbb\x1f\xc3>\xfd@\xe6\xd9\xc8\xcd\x18\x848\xc8\x86n\x92\xda\x01\xc7\xacXPm\xfbF5 P\x0f\x93\xd8\x81-\x8a\xf2M\x8e(\x899\xc6_\xd8\xe2\xf4\x81\x1b\"\xafBN\x13AI\xc4\x8dc\x92\x15eD\xc4 \x10\xd8\x86\x84\xc9\x81\x8c\xe8\x8d\x16n\xc5b%$\xb5d\xc2?\x10\x921\x161BSc\xa4$AS\x88\xcfJ\x88nm%\x18 \x8e\x93\n\x1a\x90&\x02\xa4\xe1w\x03i\x83\xa8h\xb7`\xd1\x00U\x85%E\x16{{.\xeaQ\x8c\xf9~pv\x10\xe4\xb3(IP\xd2\xcd\xb1\xb5\xbc\xca\xb8\xc9\x7f\xaf\x81\xe8(\x90o\x1e\xcb\xc8e\x92\xe3\xb6\xd1\x9cj\xb6\x87[\xcd\xd9\x90\xcd\x19\x8aH)M\xf5\xf7Z\x03,G*=|z\x0e\xb27\xa5\xfc\x07\x0e\x92\x8fF\x1d$\x1f\xbbf\x90\xc3\xb5\x06\xa9\xa3V\xbey\x90\xbb\xae$\x12\xef5RF\xb3\x88\xd1\x8ev\xa5\xe1\x8e\xaa\xe7\xc3}\xc3\\k\x963\x85\xcc{\xfd\xf4\xb7\x92E\x12d\xfe\x80\xe9_\x1f2\x06\xa8\x0c\x0dP\x19\xe9\xd7\xccN;d\x86\xbd!\xb3\xe6\x11+\xa4\xc72X6\x8c\x06G\x02\xd57\x8e\x07\x0c\x1d\xad\x97\x9d6\xce\x96\x84\x1d%[\x1a7o\xbd=\x18\x9e\xc5\xfa\x83\xa5#J\xef#Op_:n\x88\x10y3\x89z\xc1~\nsLv\xb6\xd3\x01]\xe2\x97\x05\x86(r\x95s\xdf\xa6\xa7\x94\x0f\xcf\x9e\xc1\x80\x9e\xa3\xc5w9\xaf\xd6\xa4\x00\xfeO\x99\xe8\x16*\xe2\x9b&[\xcc\x85D`\x84\x15\x81\xb1\xf6\x8co\xfecf\xfc\x0f!P\x86\xa3\x03\x17\xb6\x86\xa3\xc3\xb5i\x14R\xd3!Q\xd02\x9f\x84\xe1\xb7\xd0/\x7f \xf9\xb23:\xd8\xa7cE\x19B?\xd4\xfe\x07\xd20\x7f \xf3\x88\x81\xfe\x81t\xcc\x1fH\xc6T\xf9\x10\\%\xedA\x8f!\xb7\xcfm\x0f\x12\xa7F\x12}\x13A\xf3\x07\xd23f\x10\xd5\xb7o\xcdHB\xec\xe2\x1eP\xfc'\"~\x0c\xf2\xa7v(\xbeR\xe6\xac\xcb\xab\xa2ji\xdd\xf9RZ\x1a\xf6j\xc9$Ejo\xea\xedc\x06e\x12\x14\xad\xd5T\xe7\xa8\x82du\xb7\x1e\xddR\xa5\x9b\x1c\xa0Cd\xe9\"X\xd9\xd5\xe7\x8a\xa7\x97\x94\xa5\xa42E\x90\x0b\xd0\x0f\xf3\xb2F\xae\xe2HK\x12\x10\x9d\x17\x98\xf7eWz\xa7\xb0\x11 \xa5\xea\xa0\xdc\xad\x8e*\xf26\xc3\x9b\xdcO\xe7$?\xcf\xfd4\xef\xce\x86Z\x9a\xf1\x003\xd6T\xba\xa1o!K\x8a4 k\xb4\x90\xb6\xf5\x97\xd5v\x16O\xbb\xebJ\xeb\xce\x17%\xf4\xeb3*\xd9_\xe5\x18{iK\x9a\xa8\xda\xcbM\xadU.\x12\xb4L\xbf\x95\xea\xe3\xd6\xe3\x1cTn\xa8\x18t\x99+\x07\xb1\xc5\x96\x904 \xb0t \xc3#HxV\x83\xad-4\x0bK`\x13\x10I\"\xae\xa3w\xba\xb8/\xa5\x93\x11eA\x86d\x07X\x18\xaf\xf5\xb2\xfe\xb105\x8aY\xda\x1a\xedk\xf3\xb9d$\xaf\xf2\xb8\xd4Lubf\xf6\x14:\xfa\\\x98B\xef\xd7\x86\x08fa\x14\xad\x87\x084NWkg\xb6\x16\xe9 0\xa4\x06?6\x95\x1d\xa2M\x9f+\xe1\x85\xe6'.\xcf\xba\xd1\x95\x19 $\xde\xaa\x16\xb0\xdcdy\x04\x18\x80\xe8\x18m\x8c\xc5Am\x88\x8ff\xce\xb7\xaa&\x9b\xd1\xe4\xc33\xf9\xb3\x97\x19\xbf\xfb&\xf36\x80\x1d\xdb\xad\xe7\x02NM^\xc5&\xcf\x8fF{\x95\x12`:-\xc9\x9b)\xcb-\xe2T\xe9\x17a9\x00n\xab\x87>\xca\xb5A\x08\xbc\xe8OB\xf8_P\xaca\xb3\x977b\xe4\xd4\xfb@\x07\xfb\x19N`{\xf2\x9f\x9b\xbfl\x0f\xb6\x9e>\xdf\xfa\x0f\x7f\xeb\xb7\xad\xab\xcb\xed\xb9\xc9\xf5\xe6\xd7\xf6\x10\xae\x80\xca\xd9S\xb0\x06\xe8\xf4_O\x13:V\x1e\xd4\xfbfh\xf0\xb5Q\x01x\xa3\x0f\xd0\x96\x03\x8f\x8a3\x84\xed\xce\x1c\x97\x95\x83L\"\xc2\xf3\xeb\xf2:\xb4\xa7P Y`\x9bFb\x07\x07\x9ea4\xef=qD\xef\x1d\xec\xec\xee\xb6!\xdc\x90\xe7\x873\x97\x80r\x93>\x83\xbd\xfd\x9d\xe1\xd3\xae\xc2\xf4b\x89(vh\x7f\xb6\x86\xb43<\x99\xc4h\xe7\xa9\x0b\xc3\xa7C\x17\x86\x87O[\xd0\xba\xb8\x82$\xce\xc3\xb8\xd0\xe7R\x12\x979{\x10\xf0\xbe\xfb R?\x19\xa5z\xf2\xf5O\xd4{\\$\xed-u\xb6\xd2\x9e] \x97\xc9\xfe\xce\xc8\x98BP\\\xfd\xa0\xe2\xfe\xc1]\x8e\xb9\x8f\xc6>lR\xban\x8b\xa7 8>\x86!3t\xd9\xe2\xa3\xd1\xd6\xc0O\xc5\x84\xf3==\xc6c>\xc9\xab\xfd\x1b\xb3D\x15]\xfb\x8c58d\xd9Y\xba\xd2\x1f\xf0\xce\xc4\xad\xe3\x10\xf37\x1a\xec\xf6l}\xb4^\xeb\xf0\xec\x19\xe62\xc0\x00\xdb\x98\xd0 \xa6w\xa3\xc3^\xdd\xc2y\xea\xd7\xaf\x9d\xf5\xfb\x85I\x17F\xa3]\x16\xc2\x03\xf6\xe1 \xed!\xf6n\x8d\xbev\xa0F\x1c\x07O\xd9\xa0\x8b3 \xd2i\x05\xc9\x94\xc0*1x\x91\xc9U\xb2\xf1\xee>b\xbc\x87t\xbc\xbb\xe4\xeb*I\xf3\x0cN\xe0\xf7\x07\x89v,\xc1\x106<\xd2\x1b\x9b7#\xf9E\xb8$I\x91\xc3\xc2g~\xa0\xd7\x84\xc4 B\xe6W\xf0~\xd04\xe0w7\x10D\xc4O\xbf\xa1\x89\xa2\xb9\xe0\x19n\xc5\x18`e\xef\xab\xe8\xc2\xe5#\n>\x95o\x16T\xe3\xc9 \xf3\xe2\xda`\xf9\x8e5\xf5\xd0C\xb6z\xecv\xd4\xab\xcf\xb7!\xaab_\xd4\x97\x81\xc8\x0f\xa17\x955\xa6\xef\x10U\xb2\xa5SF\xcb\xd79\xfc\xb7\xb6\xd0\xac\xab\x94\xd2v\x07\x0f\xa8&l\xa3Z\xac\x8d\x95\xa0\x1d\x03f\x9d\x11\xdf\xc8\xbc\xa6\xb4\x10O\xe5\x9b\xb1\x8av[\x13k\xd0\xeaU4-\xdf\x19\xe6\xc9\xd4\xa9\xda\xe2=\xad\xdf\x8e\xd5,\x89\xad\x1d\xa3M\xa8Y\x15\xcb_\xb6\xb4\x9a\xe8\x1e\xe7\xa9\xcd&Jb\xb3\x00C\xbf\xd4\x9f\xcdx\x12\xda\xe6\xc6Y5f\x04\xb3\xb7b\x1a\x0b\x9bW\x05\xa5X\xe0\x14[\x14\x01\xc4\xed\x08\xc3\xa7b\xdd.D\x92\xecuj;\xed\xfbu\xdah\x16\x89\x88\xc0\xc4L\xd2\xb3\xad\xb0W\x1a\x8a\x01\xfb\xd8\xc6KR\xa6S\xf4\xed\x083\x11\xe9\xd79~@\xb1d$\xe0\x8aA\xc4x\xf6\"\x9e\xf2cv\xe9\xa5El\x9b<\xfc8(\xe4&;v \xf0D\xcfl\x8f\xea\xe6N\\\xfd\x8ev&T\xa7\x98K^\x86U\x1a_\xe9\xa1\xdd\x16P\x12Q \xab\xc8G\x14\xc8b5h+\xa5\xabV~\xe1\xf6o\xc6\x8c\xc2\xc4\x95\xda\x06\xf9\x12\xf4\xc2^\xe2\xean\x08d\xf2K\xc6\x9b\xe6\xe6a\xad.@\xa3\x01\x8eL;\x1a0\x8f^\xfb\xe6A\x05\xd8C\xebN\\h\x858(\x0b\x9c\x15(9\xe1B{\x96\xe6\xe8D\xcaZ\xaa\xab\xee\x86n\xec\xaa\xc5\xc4\x8b\xc9\xd7\xfc\"\x0c\xbe\xb4\x12\xa7b\x9fR\x8a\x80\xd1\xbc\x8d\xb8\xcdM\x93!\x94W\xa8\xc5\x9e\xc1\xb0 \xce\x12\x17\xc4\xcc'\x93\xb2*\xea\x97G\x10onRr-f\x86XR\xe8\xe8F\x98\xfd\x883\x1b\xe4V\x80\x0fe\xf7\x98\x15Z\xa2\x07\x03\xfa_aO%T\xe8\xc2B\xb6\xabG\x00\x9b\xcfF> <\x1c+[\x8e\xd5\\\xd4\xaaM\xbc<\xcc#\x0cJz\x9d&\xb7\x19I-\xfa\x90\xff\xe6a\xf2\x13\x8f\xc47H\x07\xd2\xdf~:\xbf\x11y5\xbd\x1b\x92ft\xfeX$\x93\xf2>+K\xe3\xbb\x1b\xfcn:}\x1bf9\x89\xb1\xde\x1b\xf6\x12\xdd\xd1\xd9\xef\xd9L\xfcL\xc92\xb9!ja\xf6\xf4y\x14\x89\x17\x99xC\x96a.~\xafR\xb2\"q\xa3%\xfe\xf8C\x1c4\xea\x8d\xa4\xea\xccK\x8d\xef\xc0\xc9e\x1dz\xd7a\xdc\x99\\\xa5A\xb5\xae\xd2$ YV~\xccC\xa4HA\xf1\xea\x8d\x04\xb7\xd3\xb6\xf9\x16\xac\xd2\xb6\xa5|\xb6\x98\x86\xe9\xe3z\xc6>\xed\xeaW\xb1\xf4\xb3/=z6\x90\xb6>h\xb8\x10E\xc5o\x15\x19AEO\x90KL\x9c\xcc\x90\x98G\x84\x1a\xa0\x8a\xd8\xda\x90Uu:}\x0f\x06\xb1\x15\x03\xf5\xcb\x8aU\x19C\x83k|\xc4@\x9aH/\xd5\xe2\xd0\xca\xbe\xe6\xa4\x0bk&f\x94\xd8\xc0p\xc7'0\xa4\x88E\xd2\xdeT\x98jx\xc9\x835\xc8\x8f\x9a\xf4DlLx+duZ\xb0\x19\xd7\x07\xa8\xc2{\xb5\xd7Lt\xcfP{\xea\xa8\x02|\x9fb\xdep\xe2\xd7\xb1\xaeof\x961\x17\xd6\x86\x88\xa2\x19\x0b\xd0 \xc3&\x91\xa1\xa1GnHzW\xcb\"\xdd\x95\xda\x0c\x19\xb7x\x92^j\xf8\x1bts\xb1\x19W\xcdp2\x9b\x04\x17B\xc7a:\xb5\xd05s\xf2Z\xde\xbb1\xf15\xc2\xb5 \xc7\xb8\x84cN\x0f;8\xc5\xe0\x14C\x1e\xd98e\x07\x1c\xcb\xb9 )\x85k3\xa9\x9d\xe4-\xa0\x16\x97\x00]\xfb\xa6\xef\x03}6\xc4Y\x9a,[Yv;4\xcc\xc3\x83\xf1\xb8\x8f\xbc\x94dE\x94\xbf.\xe2\x80\xae%\x17\x9f\x04\xc9rU\xe4~\xce\xd9\x94\xce\xcd&6Z\xe3\xe5\x03\xab/#\xf9\xa7GWJgH[q\xed\xa1L\x0c\x88_\xb9wuE\xb2w\xc9\xb4@\xf6\x8d\xf2i\x98:\xd6/\xa2\xfc\x1dY&,soB\x9f\"\xda$\x02\x8b\xbedH\x94\x11\x1d\xe5\xcb<-\x82\xbcH\xc9\xb4D\xb6}\x18\xefGP\x99\xbeBe6\x99s+\xc1<\xb8F\xea]\xc8\xfeM\x1dg\x87C\x06\xb30\xcd\xf2*^\";\x18\xfc\x18X\xf5p\xbb )\x01\xe2\x07\x0bX\xf1\\\xbb\x94\x11\xf0A\x9c%\x9a\xa3\xc3Gk\xb0\xb2SG\x0d\xa0\xd0\xbd\xc6\xd3\xf8~!wYC\x88UR\x8bq\x1dU\xb5\xf9\xc3\xd3\x0dY_\x0e\x8e\xdb\x93\xe4\"Z\x84\x9cW\x08\x81\xd3~\x03F\xfb\x11N\xfb\xe5\x93\xb4\x9d\xee\x03i(^J\xa6E@l\x85\x13\xea\"\x98\xc9\x84R\xcb\x97\xcc\x18R\xa3\x8es\xe1\xf7\x07E %\xb1\x9fu\x91\xb6\x8f\x04L}\x99\xd3\xf5m'z\xb5\x97\xc2\xa7 \xee#\xb6\x87\xc3\x03\xe5@D\xc6\xc6\x1e\xed\xee8zV4\xb6\x87\x83\x01\xa5\xfc\xda\x1a\x00Y\x84'\xd2'$6Z\xabK\x83\xea\x91TLZ\x12\xcc\x18tM\x96\xb4\x1a\xea\xc1\xaeaD\xed\xcc\xf5\x86\x1c\x0b\xd5\xc4G\x8b=\xb6\xf1H>Z\xedq\xac*$\xeb\xfb\x8e\xc9\x9c\xc6`\x8d\xbc=o\xcf\xd2\xad\x12\x8d\xfd\xe1\xd5\x153\xd4\xa4\x7fO\x84\xdb@o\xf0\x8d\x0e\x0e\xd6\x86\x9f\xcc\x85\xca)\xe7j\xb2\xeau\xa7Q\xbf`\xf7\x0ev\x95\xe7!\x7f\xbe\xa7<\xa7{\xc7\x9ap\x9c\xf8\xbe\x88\xa2K%Tx!\x17\xf8,\xd2\x9d\xab\xa524n?E\x13\x04f\x0fx\xe1\xcf\xcb\xcc\xde\xdf\x01R\xd2\x89Bo\x0b\xcc|2\xe6\n\x16\x08c\x8ev\x99q'\nF\xc6\xc8&?\x16\xb0{OGz\xc8>\xdd\xeb\x9cx\x0d\xbd,\x96q\xc2\xdej\xb7E\xca\xb2\\\xc4%\xd8\x1e\xdb\xf7\xd1Su\x96Y\xdf\xf7w\xd41\xb1Uqp\xd89$\xc3\x0c\x85\x0c\xde)\x83w\xb26\xbc\xf5\xb2> !\xef\x0e4#\x91NXJl\xb4\x93\xd4\x82V\x99h\xce0\x89s c\xa42\x84U\x98\xf9\xbc\xab\xbdx0\xc0\xad>\x96\x90\x1f\x14\xfbR\xb5\xa1\x17\xc6\x0b\x92\x86\xfc\x149\x1c:\xcd3-\xb6w\x06\xeaL\x16\xac\xae\xda*\xac\xea\xb2g.\xf8\xd2\x9br\x80\x19\xae\xbd\xa2\xd2\"\xf0\x14I\x83#\x88\xe0\x18*uFD \x80\xe6\xda\xa5\x04t6\x89\x14\x18\xce\xaa\xfa&\xc1%\x8a\xb9\x94G\x94)\x93\x1f\xb4\xebwg\x86C\x879\xc7\x88@\xda\xc9\x0cfU~IJ\x12\xce\x1a\x84\x96_W\x95\xb9P\xa8\x0f\x10\xfbo\x08\xd7\x89\x94\xf8S\xff:\xe2\xb1c\x17aV=9a^\x80\xf5\xf2\xb7i\x98\xd7\xcb\x97Oxy\xa6q\x89\xa2\xe4\xf6\xaf~4\xfb\xb0\"1'\xd3\xeb\x15\xd5K\x94\xb55>,\xabL\xe2\x80\xd8\x16\x89\xa7\x96\x0b\xabvp6\xb5\xf4\x9a\xba\x85\xc3\xc1\x95\x18\xc0y\xee\xe7\xc4#\xf1\x94L\xe9\xcb\xb4\xd4\xc5\xd9S\xd6\x85.\x1d}c\x0e\xb16[E\x0d\xf4\xe2;\x99\x1d*\x1f9\x19.\xaf!\x17,\xd1\xaf\xbf\x86\xf3\xc5\xcf~N\xd2w~\xfa\xc5r\xd56\xe2bIRZn\xdc\xd0\x85\xcfI>n\xa7\x98\xc5\xe6\xd6\x00b!7[\xdf\xfc\xd5\x80\x1c\xb7\xd7P\xa6$\xcb\xd3\xe4\x8eL\x1b\xdd\xef\xddE\xc9\x9f\x86\xf5V\xacS\xec-]@\x8d\x12\xb5\xf1TK\xac\xfe\xa5W\xf6\x0d\xbd\xce4\x80(\x0b(d\xb9B\x08\xd4\x06\xa2\xc7\xc8\x7f\xfc\x10*\xfd\xb3i\x10\xb4\x88Q\xe1M\x19,I\xe1z\xc5\xbf\xea:\xe4\xb1Av\x80\x14Q$6,\xae}W\xdeGyM{\xff]\x0e\xca\x9d\xe1\xc8\xb1\x1f{\x8a\x93\xca=\xabT\x91t\xd1\xe8k\xf6o\xff@w\x90\xb3\x10\xf7\xfe\xd7G\xf6;\xb1\x07.\xd2\x1e\xdf\x00\xccu\xcbk\xa9\x94\xa1flvl\x1f:]\xf2\xbe\x90;~z\xe2l\xfb\x98$\xc2\x16\xc0\xc4@\x0b\x82\xa6\xf9\x1d*8\xf4\xb2;\x19\xc1 \xc3Pz\n6\x05\xd6F\x0bez\xd0\xd2\xef\x1b\x86\"\x1a\x9a\xb2}\xd4D>\xca\xf1h\xa7\xe7\x8cm\x8d\xf6,t\xb7\xc5\xedVP.\xde\x16\x9bH\x03\x1f8\xe6\x1b.I\xa2\xf3\xf07R\xe2\xad:L\xe8vl\xa4o\xad\xdd\xfa((\xab=*\x1a\\&\x16\x9cNi\x9d\x94\xb9I\xc6\xed\xa8@\\%\xfb\xda:-q\xad\xcf\xdc\xba\"\xf6\xe6$\xa7\xf7\x88\xac\xd0\x01\xca\xa7O\xcb\xf1\xa2czu{\x02\xc3\x81C\x0b\xa4$\"~F\x98\x84\xaf)\xa1}\xd0\xa8oc\"\xd2\xa9b\x83\xe9X\x05\x08\xbd\xf2\xdbD-\xd5\x0b\x06\x8fY\xe4 \xeb\xa6\xd6Y\xe8\xa0[\xec1\x8b\x10\xe0\xe8\xc0\x01\xda5\x0f\xbauO\xab\xe8\x03\xce|\x91\x92\x06@\xbbD;\xe2\xfa\x16h\xa5\xdf\x05Zi\x19G\xa9\x114Z\\\xfd\x01\xd6\x88\xc8\x00z\x98\xcd\x92\"\xed\x02Y\x8bT\xf1[\xa0\x96|\x17\xa8%R\xf4\xa9\xd4Q\xf5\xf9\xe2Z\x0bp\xae\xd6\xf1\xb8\x8e\xca\xf4Gg\x81O\xdb\xe4ju\x03\x7fmq\xb3\x98tO\x95.%\xfcy\xb7l\xc4p\x94\xa7v\xb2\xfe9.\xf7\xe8\xd1-s\xb9\xd1#\xc8\x08\x89\xfa\xda\xd1\xcb\x8a\x0e\xb5\xe2\x96\xe1P}\xce\x98\xfd\xe1\xfe\x81c[Y\x1aX\x1a\x9e\xff5\xefH)_k\xca\xdfX\xfe\xc1\xc2\xf1\xb2U\x14\xe6\xb6%J\xcaR\xd8\xd8\xde\x1f8\"a\xf99F\xca\xe8\x03$\xce=\x93\x9a\x05\x98m\x94~\xe1\xda-tr\x84\xc8d\x0d\xafx4FH\xe4\x87\x14s[\xb1\xbf$\x16\x1a\xd1$\xd5=7\x9fDIxi\xd2cK\x9f\xf9\xd5\x17>/\x87\xf2\xd6M\xf6{\x0c\x19\xb3H\xe0\xde\xcb\xb9\xe3\xb0\xa8b,\xb6\xcbi)c\x871\x14\xe2\xb6\xf64\xa9\xd6\xc4\x18\xec)\x89HN\xf0\xbd+\xbd\x92\xd7\x94c\x97\x93(3\x85\xe54\xb5hu\xf84h!\x87\x04\x14\xa7}&>Ja$a\x87\xdc\xfeZH\xa1sM\x94z:9\xf4\xc1\xa9\xc4A\xc0\xb8\xcb^\xa5\xd76\xeb\xa4\xbe\xf5\x9bo\xb4o\x10\x81\xef\xeckw\xdf\xde\xaeJ\xc53Q\xdb\x81Z<\xe3\xc5UYj\xc4\x9f\xab\x12\xbb\x80?W\xeb\x99\xf1\xe7*2X\xa1\xd0\x8ci\xb3\xce\"B\x0f\xc4z\x81\xa9T\xe0\xb5O\xc9\xe4\xbbz\x81\x05+\x10%\xb1\xbe\x82\x1b8\x81\xb4\xfeh\xd9I\xb47t7\xd0<\xc8\xe7Z\xb2\xf9\xe5\"\x8c\xa6)\x89\xc7\x86sx\xe9\xaf\xc6\x10zK\x7f\xd5$\x0b\x80 1\xcf\xfc`A\xcb\xf0\x9f\xfarAR\xc49-\x85?\xf4e\xf2\x045\x9f\xb4\x14\xff\xa9/\x97\xc4\xd1\xdd\x18f\x8dw\x1a\xca\xe5e\xb2\\%1\xa1M'^y\xd3,\xf7\xb1HI\xadl\xedA\xb3|m\x05\x8cA\x03\x1cy\x86\xc7\xa0\x81J\x98\xfd\xe4G\xe1\xb4,Rx\xf5'\x9aN\xa6\xc9\xea\x82\x99De\xa6.\xbd\x8c\xfc,\x1bC`z\xcf\xd7\xe4\x18\xa6\xa6\x12\xef\xc2\xafa<\x86e\xf3\xfd\xab\x0f\xef\xc6\xe07\x9f\x97J>\x8d\xf1\xe9\xd5U\xb6J\x89?\x1d\xc3M}q\xea)\x829>\xfdc\x90Nc\x93\x87L\x12\xf0\x94\xb2\x1e\xf6h\x7f\xbf\x12\x14V\xe2\xa5\x85\x9f}\xb8\x8d\x85\xc8P\x8b\x9cF\xfb\xaa\x9eO\xcf\xa1~!wc\xd8\xd0XA\xa6d\xa6\x7fqu\x95\x91\xc8\xfc\x0e)\x84\xb1\x9a\xbeX\xeb\x10\x9a\x19O\nI\x9cG\xbc\x94T\xbbJ'?\x8e\xfaU\xf3\x85\xdcI\xd5\x88_BU\xa1\xe1\x1cX2C\x03Y\xd2\xd4*\xd3\xeb\xcf\x7ff'\x96vE\xe6\x98^\x994_\xe0\x1ch\xb6\x16NA\xdc|\xbeJ\x93U6\x86B\x03\xff\xe46\xa6|PhZ\xd6P\x01\xa7\x8a\x0b#\xbd\x0f\xea\xc7\x88\x060:`\xa4\xcc\xd0\xfaw\x1d\x97\x06&\x0b\xf0\x15\xe8,\xc0\xd1\x9b\x96\x11\x04:\xde\x19\xd5S)\x84t\xf1\xe4,3\xcf\nm9R2s\\\x88\xc4\xc3\x19:\x98\xc0&\xa0\xd2\xcfqky\x06=\xb6\x84\x05\xe91.\x9f4\x8b1z\xb7^\x10\x9f!\x1d\x14\x96\x921\xe6\xb5\xb6Q([\xd3\xe6\x99\x87}f\x1f\x93OR5\xe3.\x05\xdfTg\x18\xb5\x05\xa3&d\x98\x0eh\xea\x80\xef\x05\xfc\x8c\x84Fl\x8f2\xe2\xc3\x14\xbd\x944\xcb\xb4T\xf2-J\xc3\x9e)\x85\x11S\xef\xdd\xc01L\x8f\xe0fs\xd3\x81\xc5\xe4\xa6n\xd8s\x83\x811\x9b\\\xee\xc0\xad\xf7\xa9\xee\x8f\xf8\xd0\x18 \n\xdf\x88\xb0?\xa3\xf0\xcat=\xa5\x9d\\\xa21\x87\\\xb2\xd9|\xb5.\x96N\xcd\x96\x8c\x02^\x9a\x81e\xc3\xe0\xfeA\xb77\x02\xba\xdag.\xac0\xa9&z4\x05E\x9a\xd2\x03\x10\xfc\x1aK\x13\xd4\xc9\xaa^Fp\xca&C\xb7\x9e\xd2 P\xbbWs\x8f\"\x0f\xae\xa4P\x9a\xa7G\xfa\xf3x\xfa\x89\xc5F\xf8w\xd2\xa9t\xa8\xc6\xe81\x86\"w\x19\x96\xa5\x7f\xf8>\xa0?\xf8:'\x1e\xc3*\xf4\x17b\x1eu\xfc\x12M\xd1\x13_\xf8\x0c\xb8\x94\xa8\xb4\x7f\x7f\xa8*n\" \xd4\xba\xd0-\xdc|\xb5\x00~8h\xce~\x0cj\xdd2\x16\x8d\x87_\x17\xd2\xf1kHg!\x90\x0e\xdb5\xe5\xf2\x90q\xd0T\xc5A\x0c\xdel\xe1\xe39.\xaf\xe9\x12mi\xde9\n\xb6\xf1\x0d\xd8\x86=\xb7e$F\xf9\xbb\xba~\x8c\xe2\xbd\x15\xf3\x81\x99\xd1?cqG\xcbj\xb0\xd3rM\xec\xb4t`\xd5\x07;-;\xb1\xd3\xbc\xc4NK\xc7\x85;\x86\x9d\xee\xe0\x18\x96GpG\xb1\xd3|rW\xc7Nw\x06\xecT\xeb\xd0\xbc\xd7\xfe\xe7{c\xea\xc2B \x81\x9b\xba\xfe\x9c.\xfe:u\xfch&\xb8\xa6Gc\x0bD\x90\x12\x0c\x8d\xc9\xad\xca\xa4i\xf0'\xe8&M%\xb1\xd3\x81\xe3\x9d\xdf-\xaf\x93HO\xe9\xa6\xebU7:\xd4\x9b\x0d\x0d\x0f\xbf\xcd\xd6m\x83C!\xa9\x0c\xd0q\xc1\x7f\x8b\xdd\xdb\xc8 \x81|\xaa\xaa\x19\x19\xd3\xbf\xdf\xb0#bt\xf5\xfe\xb0sdf\x94+E\x12\xe4f]p\n\x13r\x89\x96g\xfe\xb7\xc8\x131\x1e~cxJ\xf8\xbb~\x13\x11\x1aB\x972\x95\x1b\xa9\xechH\x13W`\xe0b\xd8lD\xe1\x11k\x7f\xc0j\xa4\x93I\xfbF\xe8\xddV\x02\xa7`m\x0d,J_u\x8c\xbf\xc6p\xe9$E\x9cUb\xe7+F\x1c\xea9C\xc4\xcb\x8a\x15I\xaf\xb8yq\xc5lU\xd6c\xacR;\x97eqM\xec\x15$\xb1\xd0E\x9a\xc4\x17\x98\x98_\xcb @\x87]\x8a\xb8\x84\x89\x82\x9e\x0b\x03\xd6\x8dY8/D=\x1a\x9f\x81\xda\x93\x87\xbaU\xf1\xa3\xc0\xd6\\\x0e\xaa\xd7\xb9\xc2\x88\xc45(\xd7\xe0Z\x9f\x80\x98\xdc\xa2\xe9r-.w f\xf8\xfe\xb6\x07\xfb\x9d\x9b\\\xb7kj\xa6\xceJ\x98\xd8\x97~\x1c'9\xd0\x86\x11\xc5%)\x14q\x19sH\xbb[\xbe\xcb\xa0\x1a^\x1f\xcaxyt@\xfb\xa0\x81@P\x10\x91b\x04_\xba_S\xb9\"\xe6\xfb\xdb\\\xdd\x9ch\x19\xab\x99c\xe5\xfe\xf02\x9d\xd0\xec\xe3\xc9\xf4\x87x.\x89\x93\xa8>\x04\xdd\x0c\xd9\x03\x17B1 g\xed\xc3\xa9\xe7\x8c\xb9\x06\xa0\xb5\x18\x0d\xab;M\xf2\x99\x16f\xab\x18\xff\xf7\xc3\x8cr\xa8\x98X\xe6\xfe\xbeK\xceT\xc6\xd6\xe6Lm\xccX*\xd2dj\x1b\x10|\x048\xca\xc7\xa5\x9c'\xed\x92\xf30S\xef\xfb{a\x06\xde\xc4\x0b \xefg/\xcc\xde'\xf9\x82EcH\xdd\xda\x0b\x06\x8a>\x04K7=W\xf5An\x83\x0b\x93\xfb4\xa1\xee\x04NBpjbB\xc9\x079\xd5o\xad\x99\x94\xac\x88\xdfo\xdd0\xcf\x1e\xf5\xe8\xc6\xa5\x133\xda;f^\xd61lb\xd4L\xccP\x85\xc5\\\xefL\xcf\xc1\xe6F\xf4[e\x81\x1a\xcby1\x18/\x8c\x83\xa8\x98\x12\xa1\x95\xe9p\x1fG\xef\xe0\xb2\xad\xda\xeb\x07\xae\xc9\xed[S\xb3\\\x9bEM\xee\xe5\xfe\x9c\x9b[\xd3_O\x9eP\x1e>\xa4\x8b\x88\x89\x92\xe9O<\x13M!a\x1f\xd0\xaeJkJ\x86ofa\x94\x93\xd4n]\x91PAn\x8b\xc7J.\xb1v\xaeV*\xad\x93\xe6\x84i\xa2\x16r\xf3\x15\x9c\x0e\x14:\x88\xdf\xf7\xf7hK\xc6\xde/WQ\x18\x84,\x1dIy#\x97 _\xa5\x12\xe5\x8d\xae\x8e\x9e3\x85\xb2A/J\xfc\xe9\xbfs [Y\xe0G~jq1\xbex%\xd3Y\x89m]\xa0s&\xbac\xc6I\xbc\xc5\xbeA\x84LO\xbc|A\xa0\xec\x7f\x14f\x18\x07\xdf\x87,X\x90\xa5\xef\xc1\x1b\xf1*%Y\x12\xdd\xd0\x13!\x99AV\x04\x0b\xe6\xed\xdf\x08l\xe3Y\xcdIe\x86=\xc9r\x15Fd\xfa\xa6\x82\x9c\xcf]\x08,\xd1\x01\xcb\x85\xc9\xa5\xfa\xc1\xd9\xd7\xe6\x07\x02\x9e\xda\x0f(m\xf9\xce_)\x14v\x03\x9etK\xf2\x1d\xa4\xd5X\xd0\x8b\x01k\xac\x95\xdf\xe3{\xf2kA\xe2\x80\x98K,\xfd\xd5\ns\x1f\x98\n\xcc\xfc(\xba\xf6\x83/c9h\x97\xb8\x1e\x94H\xf3\xd0q\xea\x8b+\x9e\xb0\xadx9\xc1m\x8af\x16\x9eh\xa9z\xa6\xf1\x15m6GQ9a\xa8\\\xe7\xa7|\x84q\xed\xf3#\x16,v\xe8H2'R!!U\xae\x08Fj\xd2\xd6\xae\x16\xc3\x9aP\xc9Jz\x15\xde\xab\xb3\xd7\xcf?\xbf\xbd\x10\xfa\x95R\xc1\xdf\xb6\"\xc4j\xa8w3\xbb\x0d1\xb2\x9c:h\x1d\xdc\x03?#0\x1ck\xe7\x03\x83'\x8a~)p\x9c\x0c\x0c1\x02\x0c\xf1\x96\xb1\x9d\x91\xb9\x1d\xb9b\xb5)\xd5G\\\\\x86\xa6\x04\xd3\xa2\xfd\xa6\x86d~N\x93x\x0e\xcc3\x141\x88h\x12\xd7\xcf9\xc3&|\x16J\xe9D\x9b\xba!\xe4y.SA\x0e\xa2\x83u^{\x92;.l\x90^\xf1_\xc49+[K\x17\n\xa2R\xf0\xe6\xf9\x8a\x04\xe1,$\xd3\x12-\"C\xcfQc\x06v\x92RD\x19\xc6\xf3\x88\xf0\x11r_]\x07\x83\xc6\xfba,pn\xed\xad\xa72\xb5k\x84\xb1\xd1\x0d#\\w\x18\x7f{\xfe\xee-\xc7\xde\xb51P\xbci\x1a\x81\xf4\xae\xd1\x7f\xb1\x8f\xc9-\x14\xb6\xe6\xdcb\xc7\xa7V\xaa#\xf0\xf8X\xf5\x05\xac \x93\xbb\xad1\xd7$\xf6\x86\xc3\x9a\x19\xdf\xa1\x96\x96K\xda\xe4\x956\x81'\xf4\xa5\x1aXLn+\xd4\x1e+\xef>\x9f_\\}>?\xbb\xfa\xf8\xe9\xc3\xc7\xb3O\x17\x7f\x1b\xeb\x92\xa1\xfe\xf5\xf9\xf9\xd5\x8b\x0f\x1f\xde\x9e=\x7f\x7f\xf5\xd3\xf3\xb7\x9f\xcf\xc6\xb0\xab/\xf5\xfe\xf3\xbb\xb3Oo^\x8aR\x87\xfaR\x1f?\x9c\xbfA\xd6@)>2\xd4\xfa\xe1\xa7\xb3Oo?<\x7fu\xf6J\xed\xc6\xce\xa8\xf9E\x18\xd3\x85\xf1\xea\xc3;\xc1\x10\xbfD\x19[\x97\xf3\x12H\xb2\xd1P\x7f:\x02'v\x89\xc7\xab\x0e z8\x98NS\xe0\xe2h\xe2\xbd\xfa\xf0\xeey\x9e\xa7\xe1u\x91\x93\xf7\xfe\x92d+?\xe8\xfe6\xd3\x7f\xdb\xf5Y$>\x13\x00\xe8\xf5U \xbez\xc7\xe3\x9d\xbc#\xf9\"\x99\xf2\xef\xf4\x98\xba\x94W\xccP^\xe1\x85\xd9\xcb\"\xcb\x93e\xd9_J\x18\x16\xdeU\xe3\xb9\xb0\x97\xe4^U\x9a/\x9d\x16\xba\x1f\xf0`]\x95s\xa0\xea\xd7fL\x12f[\xbb\x87\x96\x0b\xb3\x16co\xdaw\xa4\xcd\xbc&Y\x98\x877\xc4X\xa7\x1e\xcb\xf5\xab\xfc\xc3\x0dI)\x07E\xa6\xc6\xe1\x9b\x90b\x93\xc9\x95/\xc3F\x06~\xf2/<\x05\xe2\xb0 \xf8L\x1e\xa5x\xa6\xefd\x19*(\xb5\xad\xbd\x01\xee?\x174[\xb4ms\x03\xdf\x9a7\xe8\x9c>\xeb\x08[\xb5\xf0j{\x02N\x14sA\xf9\xd2\xbbi\x00:\x96k\xb1\x88\xad\xd4\x8e;\x0es|\xcd(\xaf\x17\x19\xbf\x92w\x1b\x9c@\xc4\xca\x07\xc6\xf2\xf5\xcd\x06'\x10\xb0/dD7\x99]6lv\xc4\xa5\xe1\xd7jO4\xbeq\xd6\xf8\xf9\xd6\x7f\\\xf9[\xbf\xfd\xf2K1\x18\xbc\x1cl\xe1\xdfW\xfb\xec\xcf!\xbb}\xcdn_\xb3\xdb\xd1\xeb\xd7\xf4\xcf\xce\x01+\xbcs\xf0\x8a\xfdyMo\x87\xaf\xf1\xedh0x\xb9\xc5\xfe\xbe\xc2?\xac\xf0hx\x88o_\x0e\xd8\xed\xeb3z\xbb3\x18\x0c\xe9\xed\xab\x03\xfc\xf6\xf5S\xf6\xf6\xf5\xab\x97x\xfb\xea5\xbb}\xfd\xfa\x95&|Is\x05\xbdyu\xf5\xfc\xe2\xe2\xd3\x9b\x17\x9f/\xce\xae\xde?\x7fw6\x06k\xea\xe7\xfeVJ\xfc \x0f\xa7Vs\xfb}\xfa\xf0\xe1\xa2\xed\xa34Ir\xcdg\xf5/\xae\xce/\x9e\x7f\xba\xb8z\xf9\xd7\xe7\x9f\xb4F\x85Ji^\x0e6\xc1\xfa\xe5\x97-o\xb0\xf5\x14\x81\xfc\xe2\x00\xa19\xe0\xc0\xddg\xd0\xdcy\xcd\xa0\xb9;\xd0t\xa3Z\x1cz\xae\x1e]\x0d\xb3,d\x8e\xd2\xf1\xd4O\xa7\x0c\xff\xeb\x91y\xcbQ=n\xa4\x16\x00\xb4DV\xca\xf7\xa1\xb3\xea\xfa \xa6\xfai'\x13jj!3\xe2\xc00\xf5\x03\xb7\xbd\xb2I~\xe9\xc8\nr\x8d\xd6\x15\x8c\xa8B|3ln7\x13)\x8a\xe6\xcdFS\xcf\xef\xceO\x1c\x1c\xee\xd4\x18\x8a\x1df\xa3\xfc\xd4\xc0W4x\n\x8a\xef\xfc`\xf1\x89\xcc2.\xe1Bi\xc7\x157\x9d\xe264:a\x87\x9e\xcfX&E\x9cK\xf6\xf1\xea\xd8P\x98\x1f\xa2\xb5\x94^.V eZ\xaf\xc6\xae\x7fi\x94\xe7\x10\xb5\xdf\x92\xce\xa7\xf9\xd2K\xc9\x8cI\x91\xe7$\xffD7\xff;\xda\xea'\xe2O\xefl\xc7#\xf1\xaf\x05)\x08z\x04R\xcc\xdc\x86_\xe7$\xffk\x92\xe5\xef\x93i\xe7\x8e(\xbb*}c\xb7:6\x17q+P\xb5\x8dxSRN+3\xb1S&\x94>S+n\x08\xb0\xeb\xfd\xe0\xf1\xf3Z'74M+\xe3\x8c\x94^4'\x12\x95:(T\xc6\xc4\x13!\x97/_\x05I\x9c\x93\xafF\xdfdM\n\x10\x90\xd6S\xeae\x8b\xa4\x88\xa6\x9fWS?'\x08\x14_\x9ft\x18\xf0\xacA-B\x1d\x82\xbe\xc3\xec1\xeb \xb0\xc5\xa8]\xf6\xd5\xe3\x16`\xdcc\x016\x11P\xdbT\xadH:K\xd2%\x1b\xef\x9b\xd9{\x12\x90,\xf3\xd3\xbb~\xfe\xcb\xc4\xbb*\xf0\xcb\x17~\x1e,\x98\x86\x8f'\x8a\xc51\x9ajo\xac\x9f\nk\xe81`\xf8=0\xe0\xc8\x10\xedo\xb8\xfbT\xab?\x1b\x19\xfc6w\xf6\xd4\xf2\x183\xad2\x08\x91\"YN\x93\xa0\x10\xd3\xab J'^{\xe2\xc7\xbb\x84)q\xf4\xb5\xc5\xfeM8\xc7h\x9erf\xe5\x93\xe6{\xaf\xc8H\xfa|\xce\x1b\xde\xfe\xe5\xfal:'\xbfl\xff2\xdd\xf6r\x92\xe5\xb6\xa6\xa0\xf6\x1c\xd0\xf8x\xd0\x8d\xd7\xf0\xa9\x00\xd9\x82\xcc\x8b\x93\xa9\xc1:*\xe69V\x995\xa7~W\x8b8\xedz\x8e\xa5\x16?\x9e\xc7\xb1\x8cK:\x00\xc3Y\xb2,h\x93\xf4\xd2\xc5\x1d\xa5\xd9\xbch\xc5Z\xed\xb6E\xbe\x8c0\x8a\x1c\xda\x8e\xd1;\x07\xc6\xd2{\x8aP(\x1c}V\x00\xf1\x8bi\xfd\xd6\xd6]\x84Q)\xbbv\xd2p\xc8=\x16(\xdc\xf0?\x94db\x02\\\xdd\x0b:\xf7\x95\xd9B\xed=\xa5\xe1\xea2\x0bf\xeb\xc1\x03\xeb\x89\x92\x82a\xf9\xfc\xe9\x0d\xc6\x83\xd2C\xe1\x1c+\x10\x85\x84\xd2\x94A\x8e\xb7\xaf>\xbc\x93\x7f\xb3\xca\xc5\xddE\xf2\x85\xc4\xec\xc6\xcf\xfd\x8b\xd4\x8f\xb3\x19I\xdf\xe4d\x89\x0f_\x87\xbcQ\xba\x9d\x9fG\xd1\xcb$\x8a\x18\xc7\x8bO\x94\xdb\xd7I\xba\x14\x0e\xca\xf4\x9e\x85t\x16O\xde\x91i\xe8ce\xef\xc2%\x1e\x80\xcc\x8d\x9b\x9e\x03S\x8a\xce\xde\xf9+\x97\xfe\xc52\x1f\xfd\x90\x8e\xe1\xd7\x82d\xac\xeb\x1f\xa3b\x1e\xc6\xfc\x0f\xfb\xf2\xfc\xa7\xbf\xbc\xc5\xb5\x8e\x05\xce\x7f\xfa\x0b#\\\xc5\xddG?_\x9c\x93yy\x9b\x84q.n$(\x9c\xff\xf4\x176\xee$e\x83f\xd15^\x14\xb3\x99\xa8\x8b\x82\xfb|A\x08\xfb\x9c\xa2\xa1\x8b\xd4\x0f\xbe\xbc\xe4\x00/\x1f\xb0\xbb\xa4\x08\xb0G\x96\x88\xe7\xe1\xd2y\xcc\x18\x99\x93\xa1(Dl\xd1L\x1f\xb4\x93\xee\xccb\x92iv&\xddK)\xdd\x89\x8d73\xe0\xfb-\xa8,G\x15t\x81\xce\x1b3\xee\x8a\x94`\xc8Q\x17\"\xba\x10'\xd1%\xdd\xee\x1e\xc2\xb5c\xcd\xab8\x91\xa1\xa62\xbcI\x17\x024\x1c\xe9\xb1\x08T\xe2eQ\x18\x10\xfb\xd0\x85\xada\x97!\xafi\xbb\x9b[\xeb\xce3\xd5\x99c\xea{\x04\xc7\xeem\xd8o$xj\xee \xf6\x10\x9e\xd0s\xbf\xb9\\\xea\xee\x07\xf6\xc8PNrd\xb0w\x0de\xb8\xbb\x84\xa2;_\x0fAJ\xb8pG\xe5\xbd8\x0f\xb7o\x8a\xd8\xde;xp\xe5\xe5\xe3B\xd2\xb5\x84\x8c\x1d\xdc\x1d8\xdeL\xd7\xc3=},\xe6&\xee\xee\xda z&\x82E\x99M\xd0\x1e%\xe6&\xc6D\xf6\xc9\x08\xb9\xf6\x93\xa0l\xac\xb92T\x97\x93\xbe3\xb9&\xa4\xba\x98\xf4\xdd\xbd=\xc7\xde\x18\xd4D\x95\xa3\x9d\x03\x87\xc7\xedq\xc1jF\xcf\xd1\x9bG^QR\x8eG\xfb!\xc2\xfe\xee\xaa\x9e\x82\xe3\xa1%\x06\x8f\xb0\xb6\x12\xd1\xc2\xae4>\xfee\xb8\xba\xabPooRK\xfe}\xaa\xa5\xa8\x10\xa8<]L\xe3\xf54\x895\xe1\x18\x90\xdbB\xff\xdb\x9c\xf1Wbl\x9b'\xa5\xaf\x84n\x8e\xcd\xaeK\xbc\x9d\xa1qn\x1d\xed\xe4\xfe\x13!\xf5\x162n#\xb6\x87\x83\xa1c\x1b\xa7\x9a\xb7{@\x11\xbb>\xae\xef\xef\x0f.X~#\x8c/\xf4\n\xe5+7\xd1x\xa9\x88\xe7\x1c\xcf_\x07\xe8\xfd\xe0\xda\x9aQ|c\xa3!Vn\xcf>\xadU\x8ftat#\x89\xddk6e\xb3(\xdd\x01\xc0\x02\xcb\x86\xf1#\x17\x1c\x81g0@\x1e#ET\xf1t08\x18>}:\xda\xdb=\xd8\x1d<}:\xa4,\xc7\x9a4\xfd\xb7d\xb5lM\xa1\x07[0d\xe6\xc0\xd6\xbb0fVs(\x12\x06B\xc9\x0f\xf8\x17\x0cyFi\x90#\xb8 \xb30\x87E\x9e\xaf\xc6\xdb\xdb3? \xd7I\xf2\xc5\x9b\x87\xf9\xa2\xb8\xf6\xc2d\x1b\x15\x99\xdb\xd3$\xc8\xb6\xf1\xe3\xad) \x92)ar\x9f\xd30\xbe\xf1\xd3\xd0\x8f\xf3\x13\xac\xb2\x96:\xa6L\x1bHQ\x8e\xf5\xc4O\xe7\xd9\xe4\x92\x95\x8bi\x15\x9f?\xbd\xa9d\xdfRb\x19\xd8\x84\xa1\xeao\xc4\xea\xc0Qc\xae\xb6\"\x8a`I\xb2\xcc\x9f\x13t\xb4\xcb\x08>\x8f\x93xk)F<%7@\xe2\x9b0Mb\x14\xaf\xd2\x8f\xf1C\x1cG\x06~<\x05\x7f:\x0d)\x80\xfd\x08\x16$Z\xcd\x8a\x08n\xfd4\x0e\xe3y\xe6)n27<,d\x95oHM \xc0\xa8\xbc\x04\x85d\x14\xf6o\x04p\xe0\xa70\x89\x90\x9d\xc2\x8c\xb8\xb3\xd4_\x92\xec\"\xf9\x98\xac\xe0\x84\xceT\xf2\xc8\x8d\xd1\x87\xbe\xe3IC)]CJ\xb7\xeb\x1c\xc9\xd3\xf5Vk\x8bI\xa7x\x03\xedj\xaa\x86\xf7\x998\x03\x1a\x91\x04\xa1\x81\xf4r\xe1\x1d\xd5\xba+\xa4\xc6j.Up\xdat\xb1\x1aW)L\xf0\xd9%\x93\x94\xc6\xcd\xc8\xc0\xd887T\xe9\xdb\xbcu\xcd\xca\x9b\x932\xf2z\xdf\xa3\xdc\xb5_\xa5\x1a\xaf7\xa5\xa6\x0fi\x99\x8ee\xcdJMu2}M\xbf\xaa4\xda\x0bm\xadl\xd6{\xd7\xaaqU\xd7\xd6\x8aa\x0f\xfa\xd7\x8a\xc5;k]\x1b\x9e\xb2\xab\xa2\xae\xc2Od~\xf6u\xd5\xb7\xb6r\x8d\xb2\xcf:\x16i\x0f\xa7F\xb9\xee\xfe\x8e\x8dR\x1b\xaf\x14\x0f\x84^\xbd\xa7\x1fu\xf4\x1dq\xea\xda\x15\xe3WR\xcd\x0c\xcfIf\xe5X@\xd7\x9e0\xea\xe8\xdd\xa4(\xd5\xb9d>\xa6\xe1\x12\x0d\xfc\xfaV]\xedk\xd4\xeb\xe9P\x07\xbe\xd0l/|n\x88\xe5\xa0[\xe2P\xcf\xc4\xa7\xed?\x93O1\x970~S\x16{p\xca\x185\xb1\xbd\xb7\xebx\xec\xbd\x9e\n]\xdf\xfdWs\x8e\xe1\x04J\xc1K9'#\x0e\xd9\xbf=\x7f\xf7\xf6\xeck@V\xfcx\xc5\x97)\xf13\x9cY\xc2\x1f,\xfd\xf4\x0b\x0b\xfc\xc0n9\xe9pR%v\xa1\xe5)\xcc\xec\"\xfe\x12'\xb71\xb0g\x8e\xe5\xc0&/\x85\x95\x9c\x82\xc52\xfe\x89'\xe5)f\xe3\x99b9n\xd9\xe5U^\xa4\xe4<\xf7\x83/\x17\xa9\x8fQ\xc6\x0codk\x19)\xee\x01\xad\x10\x9fe\xb4$\x86\x0d\x14\xc4\x87\xc3\x9f\xd1.K\xe9\xcd\xca_iK|\x0b\xd6 9\xedOj\x8c\xbb\x90\xd6_\x8a\xb1\xb6\xae\xec\x1b9\x1b\x01\xce\xd3&Xc\xd0G\x0c\xc9)e\xd79 .lT\xc1\xfcq\x1e0\xe1\x07\xa3\nM\xd3\xe1(\xa1\xb4\xd6\x8e\x83\xd3%\x8884E\x91\xa0\xd3\x94*>$\xa5\xff\xc8$\xb6wv\x07\x8e\"h\x15\xbe\x83\xf8\xfe`o\x88\x96W\x07{#\xb5\\\xe5j\x82\xe5vx\xb9]\xfew\x8f\xff\xddw$w\xf1G\xecN\xf1T\xe6\xaat\xe9:b{\xd4Hu\x11r\x13\x08\xf5\xb90\x8dP\xa5\\E\x15\x103\xf5\xe6L\x14NX\x0c\xaf&\x92\xc8L\xd2-\xd1\xd3\xb61\xaaeso\x1af+\xca\xc82O\x0fo\xb5\xf032\xfdD\xe6a\x963\x05\x08Z\xeeNbs\x14\x89\xc2&\x8d\xa0\xec\x0f\xf4Y\xdc\xb4\nJ\x99\xaa\xdd\xbb\x12\xcd\x8a\xa1\xa2\x01\x8b\xf6\x05\x8b\x1c/\xbdy\xc3\xcf\xb6\xc6'\xe5\x0b\x17\xeaq\x86\x9a@\xd4\x04\xd4\x14\xe1\xfaz\xc1\x03\xa5\xfc^\x9e\xfa7$\xcd\xc8\xc5m\xf2\x91\x96\xb3\x89w\x95\xfb\xe9\x9c\xe4\xb4+.dJN\x9bf?\x02\xbd\x18}\xad\xbe\x98\xe6\x97\xd9\x99\xc8\x1dj\x14\x03!\x9e\xa3|=\xa6\xd6@\x05\xb8\x00$\xd3M7#X\xd2K3\xfaX\x1d1@]\xe6\xd1\x1c\xff\xcc\xb4H\xd1\xc8\x85\x99s)PH\x95\xf1\xb7-\xef\xce\x8f\xf5 \xa1\xfb\x9a\xafj\xcd\xc0\x1f\xb3\x84\x93o[\xc2\xd0 \xc8U\xdf\x05\xadB\x80\x16\x9a\xa9\x0bw\xa0I\xc6\x04\x1c\xae\xd3\x86\xce\xd7\x0f\x82bYD~^.\x85W\xbcM\x92u\x19pb\xf0\x83\xa8\xd5R\xb2\xad\xfa\xf3/\xe1\xea\x02;\xde\xab!U\x15nj\xe8U\x98\x92 _s\x14\xab\x9e\x95\x9f\xc59I\xdf\x12\xff\xc6\x00\xa6\xd2\xb4W\xd7R\xb5\xed\xaajlf\xcd;\xe3 ]L\xabF\x7fRO\xf1\xe97\x1f\x8d\x86\x93Q\x1fy\xaeyb\xf2\x88\xceC\xdd\xc9\xa8;I3\xc3I\x1aUI\xa6~Ws0a\xcc\xf9\x86\xc9\xd1\xacK\x8c\x04b+\xd9\xa1G\xbe\x92\xa0\xc8\xa5y{\x13\x7fH\xa7\x84\xd3\xedh\xfb\x95}$i\x86\x1b?\xb7\x193&\x13\x94\"\x0f\x91\xdd\xd8\xdd\xf5^\xf5f\x8f\x11\x81n\x0cZ+\xeb\xcd\xb9\xb3\xca\x86\xad\x95-\xfaVfy(\xe9\xf4\xae\xd2$A\x93\xaa7\xaf\xea\xf5\xd6\x17\xd2M\x03\xadH\x1e\x00\xcdF\xd8\xcb\xb3\x1b\x12\xe7\xccl\x01\xe7a\x0c\x89\xa7\x7f\xd3D\xf4\x8dr\xd9\x0b\xee\xde\xa7\xa9\x83\xbfk\x9d\xb2\xa2\xa4\xdb\xfa\x19\x06ku\xe51S@ZOw-\xfcR<\xd6\x1cD7\xdce`\xd1H\xf4I/;\x9a\xe4,\xfbh\xc4\"\x81\xfd\xfe\xe08\x93\x10#H\xe8\xeb\xc2\x94_\x8d\xf3\x81\xd9\xebd\xda0b>\x1a|z\xd3p\xfa\xb1\x1a\xbc\xeeY \x866\x00J\x84o\x0f\xa3|\xa1I\x8b\xb4=\xa3\xe4C\x9f9\x00)6\x84v1\x8b\x0b\x835XI\xfc2\n\x83/\x96>\x90B\xa3\xdcK\xc6\xe6\xf6(\xfe*)\xae#\xd2\xb7r\xa9t\xff&\xde%EF^%\xb7\xf1:e\xd7\xac\xfe]r\xb3V\xd95\xab\xff\xbc\xea_\xb2\xbbj\x90\xf4t\xf6\x06\x92\x8a\xfeu\xc4\x12\xbcbT\xc0\xdc\x05\xeb\xba\xc8s\xb6Cy2H+\x8cWE.?\xc8\xd0\x14K~\x92\x93\xaf\xb9\x9f\x12\x9f?sZ\xbc\xa8[#s\x88K\xf4\xb2\xe98\x05\xa0\xea \xc4\x85\x87s\xe3\xcd\x03\xb3\xceV]'DDJ\xf59\x8bY\xed\xc8b:=\xeeH\x8dx\xa8T\xf2SZ~\x92^\xb6a\x00\x96/\xe8\x11H`=\xb4\xc5\xf9\x8a\xdb0\x8a^\xd5Z4=g\xed\x9bG\xae\xc7AX\x1dO\x81\x94N(tz\x0c\xfey\x14\x95lC\x17\xd5)\x98<=\xe0\xeby\xbc\x15\x12[\\\x14O6\xfcpc\xb4\x82\x89&\xf1\xe5$\xbflC\x8ab\xfcf\xf0\xeb\xc4\x06\xe2B\xf8\xa4\x86i\xd0=\xb7\xb9\xa1<\x87)\xef`\x8f=\xf1\xa0J\x90\xf2\xd4\xe7\xc7{\x7f\xca\xbb\x84g\xe8\xf2\xa3r\xc5H\x83\x9a\xfd\xa1\xdff\x7f(.a\x87\xe8O2\x03|p^\xba@O \xda\xc8\xab\x8dF\x1e\x83\x19\xf2\xccv8D.7\xa4\\\x91~q4\x11K\xf3 \xdf\xdea+\xbc\x99\xebU\x13\xdefR;\xc0\xbe\x05\x1a.X!\xba\xd2$ Y\x86U\xffo\xdaHW\xf5b\xcf\x04M\xe8\x94\xfc\x01d\x88%\xe1\x14V0\x86\xa9\xe32\x80Q\xaa\x0c\x93\xb1\xfa^JP\xd5\xfd\xd2/\xe6\x8b\x9c\xe9\xc2[\xbbyu\xb5*\xd29\xe90\x81\x89*S\x0fc=\x12\x91\xf4\xc2\x8f\xbf\xf4\xcb\x8f\x1d\xd5\xeb,\xef\x0c,!\x0b\x01\xf0\x8d,a#\x85\x97` \xd5$A\xfa\xe8:7!\xb9\xed\x9aK(\x83\xe9\xd1\xd2U\xd0n\xbc\xd5\xaf~1\xfd\x89\x16e\x82\xf0\x99\xf4n\xc3x\x9a\xdc2\xcb\x81\xb2b\x8d\x87%H\x87P\xeea\xe2\x85W\xdcKM_\xb8<\x0eO!\x16!o\x7f\n\xc9-\xc6t\xe5\xfe'?\xb3\xc6\xc7\xc0z\xd1\xdc\x85MffJr?\x8c\xfa\x00\xac\x04\x12\xfb\x84\xb6\xdb\x199\xbb5B\xa6\x0b\x89\xda\x16oCRZIy@\x1bf\xa3\xf8\x85\xe7\x17s\n5\xcc\xa3e\xfb\xcc\x0bT^\x94\xfe\xb7/J\xb5\x93\xcb\xe4\xa6\x13_\x10\xcc\xa7\x1e\xe4o\xe2\x9c\xa4\xb1\x1f \x01\x1d\xdd&\xa8El\xdb\xae=\xc4R\xe5t\xe8\x9bi\xab}\xe1w\"\xd3\xbaF\x9e{\xff\xae\xdd\x90\x92\xbe\xde$#1C\xcah\xd7\xac\xc7?\xbdTS8\xa9\xd5\xf7\xdb?nH\x8d\xbcLVwi8_\xe4`\x07\x0e\x8c\x06\xc3}\xf872\x85\x9f\xfd\xdcT\xec\xefdz\xcb\xea\xabl\xc5\x02\xbaz\xd1E\xb0,\xff\xe3\xf6\xffQ}\xdc0\x1f(\xfa\xcd\x05u\xab\xd6:)\xa9D\xbd,\x91G3t\x02\xc8\x14\x16\xe1\xd9\xbe\xa5\x10\x17\xcdh\x95-\xe1,\xc4\x86\xafl\xeat\xf49plo\xcc\x9f\x0c\x92\x90\x85\xcbaR3Q\xa5$\x958\x81P1Y8\x81\xd0\x01\xc2\x9c\xfe\xda\xa8\xb32}L\xddb+u\xca\xaf\x13\xcf_\xad\xa2;\x9eP\xa9\x95\xbf,+\xaby\xc3\x86z\x82O\\\xe5D`F\xa0\xd4\x11\xc6\xc6\xa9\xc8\xcb\x93rG\x17\xde\x1f\xff\x9b\xe9G\xc2\xf2\xceZ\xd0\x1aKR\xc6c\xacy\x814\xeai0\x92\xd2\x85\x0eGk\xd7\xb4\xa2-x\xb2\x9e\x9e\xfa\x81C9\xc7\xd8\xb4(\xcb\xade\xf7\x95T\x9e\x0f\xf6zV\xc8\xdc.\xb8\x0f\x8a\xe3\x9e\x1b:\xd5\xf3?\x81A\xaf\xda]\x16*\xbc\xde\x9a\xe8i\xea\xc7\xd3diw\xfan\x18\xbak1\xf36\xdb\xf2\x82$\x0e\xfc\xdc\xae\x85\xc4\xc74\xc6cJeX\xce\x95\xe5\x82\xbd\xb9\x19\xc3&\xa4Ne\x0e\xb1\xb3\xff\xf8\xe43\x8dh\x06<\xb5e\xe39Sp\xec6\xe6\xcb\x07\x83\xd5|\x05\x8d\xdcc\xd9o\x87\x83\x81\x03\xa7\xfa\xd2\xd0-ZF\x94V\x06Y\x0d\xe9\xf2\xdd\x188.\xa46\xe5\x9d\x13\xa7\xdd\xd0\xdd\x14\x8c\\\xb6v\x7fh\xb4g\xcdInQ\\\xc1\xacW2q\xd7t\xfc\xb2\x9e\x07\x94aKR%\xdc\xb4\xc9\xf3\xcbBw\x0c^7\xe5\x0cE\xb2i\x0f_P\"\xf1\x11KTsP\x89\"\xeb\x9a\x17\xc7e\xce\x88F\\\x9f>=\xc1\x9d\x11\x9002l\x9aY\x94$iW\xef\x0c]\x0b\xb3\xf7\xfe{\xf4\x81\xd9\xc44\n\x03\xe6\x12\xc3v}\nc\x88\xd7O\xe8!\xe1\xa4Q\xaf\x87J\xe3>\xc3\x99\xa6\x91\x1b\xb4\xc4qn\xf4\xc1 \\R\xcaK\xddh\x98\xd6\x88\xcb\xd4\x93\x9d\xfe=\xd1\xb0n\x9aO\xea\x9d\xa91p\xf2\xa5\xf0\x8c\xba\x05\xd9\xe7\x0c&\xd5\xa9[\x92ofC\x08X\xe3\xd05\xef\x97\x7f\xa0\xe7\xaa\xd9Gr_\x9f\xc8b\xcf\xe4\xc3\xd9\x89\x0eR;Y?\xffZ\x97\x98gO/\xe69\xd0Iy\x98\x87Y\xf3\\\xc4A\xd5\x1f3\xbd\xff\xb0;\xc7\x9e\xd9\x14.cF<\x1ao[\x96\x94\xdeGk%\xcb\x82 \xb9\xd4\xb9\xf7\xa2\\\x7f`\xf0\x06\x8f\x1a\x11\xd8C\xb3\xe7\x1cH\x82']8`!^\x9ad\x97]\x84\xaaT\\\xe3%\xe72\xef<6\xa6f\x02\x0ds\xc21X\x1f,\xd8\x84\xcdMM\xf2oq\xddj\x93l@\xe3\xdc\xc1'\xad\x92\xf9\x99H\xeb\xa2\x8dfB\xaf\x7f?\xfb\xdb\x184\xf6#\xef\xcf\xce^\xe9\xd3\x17\xce\xfc,\xffw\xa2\x86\x873mg\xcc\x1a\x90\xc8A5\xb5n\x0b\xcc[]\x9f\xb6\xf2\x14\xacs\xca\xfdX\x1f\xd1X\x9f\x98e\x1d\x1b!NOk\x04a,\x97\xd5:\xf4\xdaj\x97{lT\xd4\x9bu\xd6R6P]_\xc4\xa5\x9fLq\x86N\xd2K/lNl\x13\xf2s\x92\xffL\xfc/\xeb@\xfeQ\x00\xd90\x84H\x84&<6\x86\x7f\x088zi\x05\x92\xf8uJ\xc8o\x9dBn\xa8*\x8f\xd0\x1e\xd4\xa3\x8b\x9b\xfe\xc2\xd8vO\x9e\x80\x00\x13\xfd\x1d\xd8u\xb6K\\:\x02\xb0\x8d6c\xfc\xee\xef\x0fe\xb8\xe77\xd9Y\x19yC\xfb\xf5Z\xb4\xc9\xef\xdf\"]\xd6W\xadw{\xcf]\xb0\xaa\xc8F\x0d\xf7w\x8e\xf2\xe4xG\x947\xf7^\xbe={\xfe\xe9\xea\xc5\xdfPs\x847\xf8\xeb\xfd\xd9\xcfW\xcf?_\xfc\xf5\xea\xecS\xf5\xe0\xfc\xe3\xd9K\xfa\xe0\xea\xc5\xf3\x8b\x97\x7fm<.\x1f\\\xfc\xf5\xd3\x87\x9f\xdfkJV/J\xc5\x05\xedCLn/(}\x1b\x9f\xa5\xed\x9eg|u4\x97\x0e\xc5A\xda\xa8\xcd+\xff.J\xfc\xe9\xb8%\x83$\xd4\x89y\xb5C\x18/\xf3[z\xa59@\xca^\x91\x8e^\x9c\xafH\xf0\x8d@\xc9\xbe\xbd\xf9o\x06\x81&\xbe^\xef>\xbf\xba\xa6;\xd7j2\x01\x0d\xc4]~\x9c\xadH\xa0i92\x1f\x02\x8dO\xb5\xad\x06\xbac\xa5\xfc\xd4/\xf2\x85\xa6\xd5Y\xedT\xc2\xd2\xb8\x80\x95b\xab\xaa\x18;\xc9\xaa\x92W\xd7w\xcc-\xb37_\xb6\xaf2X\\\xc6\xaeK\xdcY\xba?3\xa5\xc0\xe5\xda\xe1C\xdaH\xed\xfb{\xb4\x0fa6?\xc4\xa1\xef*\xeasMfs\x7f\xc7\xe1\xec\x96\x0b\x16s?5E\xaf\xeaE\x98H5\x0f\xf4\xee\x88\xfb\x0d\x19\x0bO\xf7?\xd03\xb0\xfb\x03\xbd\xf0e\x7f\xb0\xdb7\xdc\xb1\x10nli\x98\xa1\x98[U\x01W\xd3\x0c0\xe6\x16W\xe2\xd6\xd7\\\x92r?c\\@\xb6s\x04\x9b\x9b9\x1cCl\x0c\xb3\x99\x1a3\\3\xafa\x92\xdb)f\xcfK'\xc3\xcbv)\"\xbd2\xd9\x0b\x98\x9f@\xa9[{\xccm\x0fO \xa9?\x9f\x13\x96\xfc\xaa\xf6p\xe1\xa3\xe5J\xfda\x86%\x8b\xbauK\xb6\xde\xdc\x0f\x07{}$c*\xd8$\x93\xd0\x13)_x\xbc\xb5u\xd4\xe4C\xb8\x94~\x12_\xb2\xfc\x83\x92\x19\xb0\xf6\xac\xd8\x1a>z\x8f\x0c\xba\x93\xd1kFS\x0d\xe4\xeaj\xea\xe7\xfe\xd5\x95\xb6_\xa9\x9d;p\n\xf1D\xc3:\xe7\x94u\x16\x8f\xc7`-\xfcla\xd1\x134\xf6\x96\xfe\xea\xd1\xe31\xb8C\xed7\xe2\xf2\x89\xf0v\x06w\xa8]\xfd\xc6\xec\x11\n\xd7\x84\xeeD \x9dlA\xde\xa5!\x85\x86.:\xc6)\xf86*\x93\x12\x9b\xe0\xba tg\x89T\xddc\x94\xb8v\xc0M\xee\xdbZ\xbd'\xde-\xb9^\xf9\xc1\x97\x8fIt7\x0b\xa3\x88\xab\xe4\xa7d\x95\x92\xa0\x99\x17\x14=\xdeW~\xbe\xc8\xb8=I\x15z\x99\x7fY\xde\x9e\xb0\xf4\xb3z\x06\x8f\xb8`\xb1dM\xda\xd8f\xb5p\x91\x9a\xf0tk\xc5>#^\xd4x\xad0\xd6\xad\xfd\x0c\xffG\xfa\xa8\x11\xc64\xfa\xd8\x9c\xad\x13\x18>R_\xab\x9a&\xd4\x07@w\xdd\xf6\x7f\xda\xa7\xe3\xc1\xfdd\xb8\xf5\xf4\xf2\x97\xe9\x8f\xce\x9f\xb7\xbb\xb6\x88\x01\xa3$\x95\xb1\x8f>\xef\xfb\xc6\x86\xfd\xff\xb3\xf7\xef}q\xe3\xc8\xe20\xfe\xff\xbe\x8a\xc2\xe7\x9c\xac=\x18\x03I&\x97\xce\xb0,\x03\x9d\x1d\xce\x06\xc8\x0f\xc8\xcc\xce\xaf\xc3\x971\xb6\xba\xdb\x1b\xb7\xddk\xab\x9b\xb0\x9b<\xaf\xfd\xf9\xa8$\xd9\xb2,\xd9\x86\xb0{.\xcf\xd7\x7f@[\xd6]\xa5RU\xa9.T9\xd3\x18\n\xc9`\xc4*{\xf2\x04\\\xd5EI\xde\xf0A\xb2\xb1\xc7M\x87\x0b\x1e]\x80xX\x80\xc0\x1f`k\x97\xff\xfa\x0f\xf4e\xcfi}\x8c\xc5\xfb\x80\x99\xd2]L\xf5\xcd\x82\xed(\x17\xfa5\x8a\xe9\xa2\xf9z\x8b+\xd8\x18\xf1\n\x86\x03P\xba\x82*\xae}\xc8\xa1\x83\x90\xd2\xb1\xa1`\x1f^Y\xc8\x9dg\xfa\xfd\x99 w\x9e\xe9\x0e\xc6\x05V}\xa6\xd3\x99\xa5\x99*M\xc5%\x81^\x0d^\x18\xb9\x85\xd7&\xa4S7\xf7\xdats\xea&Zj\x8c\xa9\xa1\x96:\xc7\xd4\x95\x96\x8a\xe1\xdd\xea%q\xb9\xe1\x91\xe2m(\xfc9!\xb7W\x08vk\x97\xbb\xe3`\x7fQ\x97\x8c\xbb\xacqw=\xae\xd5\x947\xca\x9e\x84K\xb5X\xee\xf1\xd01j\x96\xf7E\xbeHJ\"\xb3%\x01\x0f*N\\^_\xd8\xc8|A\xa8Z_\x88YV\x8d,\xbf\x90\xf0\x93\xd6\xec\x8ao\x0fw=\x08ZK\xe3=_\xa62\n|c\\9r\xcf6\xfd\xbc\xd8\x9d\x8b\"\xf4\xc1>\xa4n\xc6\xdd\xdbh\xd7~\\\x81P*)\x18/\xf7\xf1Z>\xea\xbc\x967\xac\\\x9b\xa6\xc5z\xa6\xc3\xea\xc1\xe9\xb4T\xb1\x1cVE\xb5\xca\x96j\xe2a\xd5\xe0\xfa[\xaa\x98\x0f\xab\xa2\x82\x8fFn\xa3\x8a\x81\x8235\x05\xf2AV\x0d\n\x89\xfd\xecu/\x95e\xbf|\xce5\xaeG\x88nF`\xb4%\x13}W\xb4arq\xaa\xf49F\xb4v\xbf%T\xe1\xd8\xf2\xd5\xce\x90Au\xf2\x0d;\xdc\xb9>\x1e\x82\xe8[\x97x^\xcdJ\xc8x0l\xf3f\xf0\x03$o<\x94i\x91I\xee\xd2I\xb6\xb9y\xe5]\x19\x07\xcf\x8d\xf2\x90\xd7\x16\xf4\xa8\xa6_?h\x02\xccr\xfb\xfaZ\xb45\xb4\x0d\x1a\xacIQ&\xdc\xef\x92PE\x92IA\x92\xc5\xe4\xf3\xd9\xd4u\xd6;\x81\xe3u\xe7\xd8e9\x9e<\x11\x02:s\x8eW,\xcf~\xcf\x85cF>\xd3\xcb$\xd2n\xb1z\xf4u\xfaUX\x18V\xad\xd5X~\xefDa\x9a\xde\x84\xd1'\xa7\x92\x1eb\xf8Y\xb8!\x8aZ\xcb\xef-\xaa\xc5ka\x07\xc7c(\xb4\x94\xb3\x8de$\x8e4\x06F\x92\x0f\xa2\x85\x9d\x1e+_\x8b\xc2\x97|$*\x08\xe4LZ\x8d}\xa0G}K>\xed\x1a{ie\xf5\x11\x1aT\\]\xdb\xa2X&\x1f=\x10\x89\xfat\xe9w\xc9\xe7Q\xbbjU>\x93Ooo\x9f\xffk{k\xd5N\x93OW\x87\x07\xd9b#.D\x12SRS\xee\n\xb6\x90\xb3 \xb9\xb9B\xc8\xd0\x9e\xdc \x1e$\x93ps\xf3\xaaa\x8d\x10\xf6D\xe5\xfd\xe6YQ\xcd\x03zt\xfd\xbf\x0e\xbd\x81\xd68<\x14\xe3\xd5hL=wU\x07\x89\xdf{f\xcdx\xbb\xa6\xb5\x89\xcc/\x84\x97E\x93<2\xe9;\xb2\x92\x0c\x91\xe0$\xbb\xc2s(S\xfc\xc2u\xd9\xb5Y\x84\x10y\xf5]\xa9F\xfe\xca\x83i\x91/\x00\x9d\x83\x85i\x9aG\xca\xcf\x0fY\x19NI+\xe1\"\xcdo\xb5#\x81\x91\xa3n\xe2\x16\xdc\xa7\x0c\x0d*w\x94\xa1\xe7C\xe2\xe6<~b\xc8\xdb\xea\xa7G\xf0h0x\xce4\x1f\x0c\xceA\xe34\xc8rq\"\x88\n\xcc\x94\x8biRX\x0f\xf9\x1c\xdc\xb3\x8b\xbdg\x97\xd6\xc5\x8e\xeeI\xb0j\x9b{6I\xae\x0d\xc1\x14\x98\xc2\x05\xc2>\x14\xc14\x91Z\xc1\x8c\x86\x13\xaf\xcaoT\xb07\x8c],z\xaf\xf2\xe9?a\xec\xf5\xd2\x98\x16E\x01\xbe\xff\xc2\xce\x15\x01\xeb\x81`G{\x05\x87\x83h=u#e\xee\x8b\x97\xdf{\xae3\xcd\x8bq\x18\xcd\x9dA\xa8\xa8O\xe3\xf5\xd9\xaeY\x10\xf1\xcc\xe2\x06r\xf7\xb5.)\x10\x82\x88W\xaa\x18\xd7\x1dL\x8c#R\xc3\xf8$+T\xcfL\x8d3\xdb\xbaC\xfe\x01\x9e6\\\xe5n4\x84\xban)\x9c\xc3r\x97\xb1D\xb0/\x0c\xc2\xcb\xc6\xd1\xf5T\x04\x8c\x94\x8c\x0dFO[\xa1I\x13\xe7\x0b6\xd0n\x08\x93\xc3J\x7f\xd3\x89\x1c\x11\x93KI#2\x04\x97\x92v\xebx\x9e\xcf\x0d\xe1\x1b\xa3\x82Z\x91\xc6\xe0\xc6\xb0\x19\x96%kgP\xc5\x9fI\xfbs\x1d\xa2G\x8fK\x0c%\xdb\xfen\xee\x96\xac[ld\xb5x\xf6\xab\x17\xcc\x86\xf2\x83b\xa9|\xdd\xef@u\x0di^\x15\x945\xf1@\x06\xe6\xc5I\x1b\x8b\xf3LY\x1c\x86\xceh\xa5\xec\x03#H\xc4=\x88\xf8\x8e\x16\xe8\xcd\xef\x19\xb7qS\x1a\xe5\x1fqA\xd3\xba\x0f\xca\x17\x0d\x18$ \x945 \xac\x0c\x80P\xb6\x00\x01},\x98\x16\x1d\x05\xd3\x86%G\x9bd\xc3J7A\xc1\xa0\x01\xa4\x82B\xa9\xafv*V;\xf5D\x0c\xbd\xe8~(\xa9\xc6\x12\xadp\xb9\x02I<5_\x01={f2\x18\xcb\\\x8b\xb0rwW\x17nrt\xb7\xfbB\xc7M\xdc\xa7D[R\xa9\xaa\xbd\xb8TS\x82\xd5\x87\x88\xbe\x05\x97&\xb8\x8e}\x98\xfb\xb0\xf6a\xe1\xc3\x0c\xf6`\xa9\xaa\x89\xdbhU);n}dD\xa5Y\x94w\x87\xc2\x06\xde\x11\x06\xd9Oa\x04:\xbae\xcf\x0d\x92\xe0\xcd \xb6q\xc6\xb3\x1e\xe3\x8e\x84r8i\x99v\xb0\x1a\x13wf\xd4\x19E\xba3\xe6\xa6\x072F\xef\x1b\x88\xe1\x0fp\xf3\x06n67\xcd\xd46\xab\xd1]\x08G\xacwn\xe8\xce\x91T\xbd\xb9\xf2\xf0\x8em.\xee\xd8\xee\\L\xf3P\x06\x81\xb7_\x0b\x1e\x0b\xb2\xba\x9a]4!\x1a\xcd\x7f\xcd}\\\xc3\x1eTq'\xde\xc0\x066\xb9F\x8e\xc3\xf5\xbc \xce3b\xb8\x14\x06\xb5\xb3\xb9\xbb\xf6\xe1\xce\x879\xb7\xc5\xe3w\xc4\x03\xba\xf6\xd5\x0b~<\x1f\x1f\xfc\x99\xc7j\xa5\xc1\xf9\xf8\xf2\xc3\xf9)\xec\x89\xdd\xf6\x8d\xe7\xb3\xd5'u\x11\x1c\x8d\xdf\x1e|xw \xfd\xfe\xa9ww^\xf5\xf8\x9d~)\xfcL\xbf\x12\xff_\xdf\xdb\xdf\xb4BR<\xb7\xdcm\xec\xe8\xdb<1\\\xf1\xdc\xdf\x94\xd1rH\x85Fm\x8aD1pD\xee\xc5\x0d\xb1\x18\xddd\x83\x00\xad6a&\x1f\xec\x96\xd6+W\xa8\x869O_\xeaGCU\xcchc]}\xb5-\xdc\x0e\xa7}\xd9\x7f\xdep\x05\xa7\x07\x82\xc9\x8cxp\xf8\xda \xb39FQ\xde\xe2(\x10\xa6I\x16\xa6ig\xd7:;\x0eP\xb9&\xeb\xcf\x08r\xa4Q\x9a\x97b\x00\x9d\x05\x9aF\xe6\xdcu\xc5\xe0\n\x86\x0c\x0e\xba\xe6\xde\x93\xa8\x15{\x1a@\xba\xd2\xb0\xd9)\x81d-\xb0\x11s\x03a\xdbu\x8b|V\xed\xab\x05\x90\xd8\x81\xfb\x83GM?\xae\xff\x93U\xbcNh\xe7u*\xcffA$\xa0\xf8\x80\xbaa\xa7+\n\xae\x01\xd6\xa3T\xc5\x88,\xe7\xc9\xdfV9}\xd3\xe1\x8b\x83=7\x05 ?\xd9\xb3\xf0\xd6^\x0di-\\,\x1f\xa5\xb1\xd7C\x1a\xfb\xb7\xcfO_>Fk/:\x14\x0d\xa1j-}\x94i|\xd1\xa3b\xc8\xdb\x9a}k[\x83t\xd8\xa2<\xa3I\xb6j\xdf\x0c\x81\x95\xc5\xe3|0j\xf6\xbb l2\xfcX\xaen\xf8\xb5\xb5\xbb\xf2!\xf4\xe4e>\xe3@\x19+\xbc\xa9#:s\xe5b\xaf\xca\xfa\xf7Y\xc9v\xe50\xd2C\x0c<\x92\xbaH\x83\xea2\xfa\xa67\x851\x0b\x852\xb5\xd9@\xaf\xcd\\\x96\"\xbf\xce@ [\x92\x96FId\xb8\xb5\x9d\xa2p\xa1\x99\xb6l\xa3\xabvx>\xf6\xd0|yp\x93\x17t\x04N\xc8\xfe\x1b\xd0\x1f\xcb\x92%\x0b\x0c\xe11\xce\xe2\x11\x94\xae\x13\xca\x04\x92\xc5\\\xff\xb9\x99\xd4]\xcb1%<\"H\xb3\xaeD&\xeb5\xd6\x1f\xba\xeb\xbd\xa0!\x1b\x89Zg\xc9\x92\xf4\xfax\xa2\xb1\xae\x1f\xd3U1\x02\xe7&]\xe9&\xed\"\xc3a\x98\xbdO\xc3\xbb\x118Q\x98-\xd3\xf0\xae3\xdb\xe5\xbc\xc8W\xb3y\x9d\x9b\xf2\x04K\xa1y\x98\xcd\x08\xcb\x8c?,\x99RT\x01w\"\x8c e\xce\x92/\x96y\x99T\x0b\xe6Du\x82uu\x94Bb\x1e\xd5b\x1dS\xa6\x14\xfc\xb0\x8cQ&\xa0\x96\\a\x9a\xadhF\xc9gzB\xb2\x15\x16\xc2\xb7\x05\xc9V\xb6\xecK\x9c\xf8|i\x9b\xf5\x15v{e\xe9\xa9\x12\x1ek\x04N|\x93v\xcc\xe1Q\x11\xceX\xa6\"\x9c\xd93\xf0\xd9ey\xac\xd3\xca\xb3QRT\x19)\xb1\x80\x16f\xfd\x9cP\x99\xf3sb\x1bG\x11\xce0\xc0\xa3\xc8\x99\xb2\xdf\xf6\xacg\xeb\xaa\xf5|\xdd\xd5\xb8\\w\x96\xb3c\xc1\x8f\x8a|\x89\xb9\xf2\xa5%\xc3\x8ao\xd7\n\x9ec\x91\xd0\x05\xd7\xe3\xc5\x92&\x84\xcd'\xe1\xbf,\xd9\xb2\xa8\xb8[R\x9eQ\xfe\xb6e\x8dE\xb6\xd8\x9a\xa5(r67\x84\xfd7gy\x9bG\xabr\x04\xce\x94\xfd7g9\xce\x96\x08x<\x02\x981\xcb\x9f\xc9\xddQ~\x9b\x8d\xc0\xf9D\xee\xe2\xfc\xd6\x82\xca\xfeL\xee\xde\x17\xa4,y\xbe%\xfbi\xcd\xf8a\xc9s\xad,\xab\xf0\x0e-\x93\x19\x0f2\x92f\xca\x8cs\xe9\xca|Bh\x18\xab\x05\x16\"\xc1^H\xc2\x0c\xcb\xdf\x013U\xe0\xb8\x118\x0b\xf6\xdb>\x07U\x108\x99\x95qW\x1dY\xcfp\xee1gn\x9b~\x9e\x91\xef\x03\x9e\xd3\xba\x11D\x988\x99\xd16\xbb\xef\xc3\x121\xdd\x92\xfd\xb7eY\x95<\xcb\xaa\xb4e\xe1G\x89\xfd\x1ca\x19\x92l&\xf2$\x99\x05\x19\xbd/\xf2\x99\x80\x9b\xa5\xf8i\xcex\x1eRRm\xcb\"\xa4\xa4kKr \xdb\x08\x9c\x12\x7fX2\x11\xf2 \xb7Y\x89?\xec\x99\xf80J\xfe\xcb\x96-\xe5\x91=\xab.\x962\xa5\xb3\x9f4LS\xde\x07\xfe\xcb\x92mU. b\xec\x92\xff2g\xbb$\x9f\xa9\xdc\xd1T\xfe\xb6dM\x16\xa4:\xf3h\xb2 ]\x87\xdde\xbe\x8a\xe6\x87a\x16\x116\xa5\x94\xbdE\xf8\xd6\x91\x9d\x1f0\x98\xd7\xde_\xf6U\xec\x17\xcci\xdf/\x98U\xeeX\xcc\xdb\xb1e\xf1\xda/Q\xa9>Z\xa5\xd4d_3\xcdX\xd1\xcfy\xbaZ\xd4P\xb7\xc6\xd7\xae\xf5\xfc%L(\x87\x96[\xfe\xcb\x92mNp*o\xd9\x7f\xcd\x04\xb4Y`\xcex(\x1e\x85\xa6\n\xa2w|\xe4\xc0\xa6\x90\x18\xb9\x8d8\x04^P\xa6ID\xdc\xa7^\x93\x1dX\xa3j\xdb?\xbe\xa2VE\x93\x94>'2\xd2Z\x1d\xa4\xb0}\x990 p\xad\xa9\xa2~\xf99:\x8f\xf9)\xcc\xe2\x94\\\xe6\xcbwdMRw\x1d\xcc\x1b \x9e\x0f\xeb\xa0]=\xec\xf5{ll\x8e\xa2$t\x9ca@\xcc\xbe\xae\x19\xdb{\xf2\xc4\x98\x1e\xd4\xd5\xb6\\\x01j\xb3X\xb6\x9b7\xb5.5\x88\xdc\x0dc?\xbe|\x01\xe3\x87\xa0\xaa\xdf\xed\x0e1\x97b\x81\xcb|\x80S\xd1\x86\xa4\x98\xfa\xd0\xed;O>b\x00=j}\x95\x16\xde\\D\"\x99\xcc\xaf`\x0f\x96\x9b\x9b>D\x13\xf6&\x82\xfcV\xaf\xed\xe5\xe6\x11 `\x0f\x92V\xc0\xc6#\xc20%\xc9\xa2\x84\x94\x13r\xd50f\xcb\x87\x08\xb3P\xcb\x9d\xed\x1c\xabu[\xa1\xc7\x99\\\x89X2+\x1e\xa7\xd8\x91{\x9d\xcb\x86Wht/v\xbd\x07\xfbfp\xa2E\xb8\xfcqu\xc3\xd6\x11?(\xb5\xf8\x12e\x08\xb3\x9d\xd4\xe5G\xfd7\xd5\xa8\xd4 \xaa}@%Gg'H~\\\x88\xf3\x96W\xe4TGqc\x02\xe4\xa1\x0c\x1b;\x9d}\x16\x01o\x95\xf6\xaa\xea\xeb:\xee\xd9cC\x0d\xc6\xc2\xbf\x1c\x9f\x1e\x9d\xfdr\xfd\xd3\xc1\xe9\xd1\xbb\xb1\x1c\x0bR\xd4r(x\x86p\xbe\xbb\x1e\x9d\x9b\xba\x92\xde\x16\xa3s\xef1\xbc\xb7\xa2dUEf\xc1}\x96\xf2\xd8\x17_\n\x01 \xf3\x04\x90`uI\xe6\x08\x15\xd7\xc1\x93\xd5\xecO\x92\xf5\xf5\xa8U\x81\xec\x10\x96G\x1a\x97u\xca\x87\"\x10\x1f\x85N\n\xbeck\x98\xc0\xba\x1d\x9b\xf7\xd6\xb0\xb6W>\xc4\x93\xd5\x15\xef.n\xc7\xbdVHy\xe8;.\xf4Z\xfb\x03\xd5\x80b\x867\xa8\x9f-\x85bK7\x1aK\xfd8\xfdhB\xcf\x90\x8e\x88\xc86<4\xe9\xfbpF\xfe\xf2k\xcfA\x86\xb7\x17\xfa\xad\x1e+\xdd\xe9Kz-\x9c\x86\x9a\n\xba\x0e\xa2\x19\xfcm\xd2\xe3\x92\xf7$\xaa\xd3\x06UQ\xa0k|$+W\x85\xc0`?\x87\xe9\x8a\x9c\xe4YB\xf3\x02 \xba\xdeq*\xae.\x90T\xc0K\xdcu`\x984\x97\xed\x80\x0d\xcc\xb41\xed:|\xd8$\xac\x82\x82L\x0bR\xce\x95~\x95\x96\xfb@\xd3R/\xf8\x18\x94\xd2\xe8\xebzZ\x87\xecR\x1fm?To_-\x06\x08\x83<\x904\xc5\xd4Ur\xa5\xd1P\xb4\xe6\x94k\xb4^\x17\xab\x94\x94\xd7\xd7\x0d\xdd\xf0\xeb(\x8c\xe6\x04\x13-\xd7\x8b\x85Bp\\_O\x93,\xc6\xdcv\xaa\xa5\xad\xf7W5-\xc8\x04~\x8d\xb7\xb5\xfb\x06\xa8\xd5\xb1`\xb3\xe0ds3\xbbB\x85\x01\xae*s\x0fO\x83\xbe6\x82(_,\x93\x944\x07a\xbaB'\xa2\xfb\x06\x96\x83M\xa1\xe3hT\x0cQ\xc6)\xecI\xddn\xda\x8e\x04\x84\x13\x98\xfc~\xe3\xf5\x18\x07\xa8\x95\xa2\xae\xfe?\xd0\x07q\xaby[ OY\x92\xc7\xda\xe2\xae\xf3:\x86oD\xa9\xec\xc9\xd4)p\xd1!X\x86\x13!\x07G\xf9\xe0\xbe|\xd1Z\xe5#\xcd\x82if\x88M\xdd\x1a\xad\x0d\x1cB:\xd0\xf2\xa5\xa8a\x99o\x01\xa3\x11\x1a^\x12\xb1\xbe\xea>\xa3\x19Doq\xb5\x81B\xb5\x8c\x16V\xd1\xef\xc3\xa2$\x05\xb0\xe9C\xc3\xb2i\xbeB~\x1f6A7K\xd7\xf6Eq\x15L\xa5\xf1g\xebK\x98b$c\xfc\xff\xe5\xcb\x90]\xdf\x9c\x9d\x1b2\xcd\x0bb4\xf7k\xb9\xb1ZK\xcfx\xbd\x93\x94Hm\x9c\x8eI\xca\x1fs\x92\x82r\x89l|\xee\xc3\x8e\xc9\xf5!C+F\x13R\"\xd9K\x93C\xc4if4/\x0dS:\x82\xa4\x9e\xf2\xd6\xb6\xbb\xd7\n\x84SJ\x8a\xff=\x0b\xc0o~\xff\xa7-\x02\xc34\xf7@\x13F\x04\xa0M\x08\"/\xdb$\x18T[z'\xc10q8 \xc5cM\x02\xefA\x9f\xf2\x17\xcb\xd0\x0cJ\x8b\xae` \x8c\x00e\x06\xdc\xe3cs.\x86\x1dy\xf5Y\xd9\xd2\xa0\xe7\x87\xd9\xb0j4\xba\xa4\xda%fU!\xca\xce\x1e\xc3N8g]\x87E\x98\x853R\x8c \xc9\xd6a\x9a\xc4bg0\"\xc5\xb4'\xa0\x8d\xbd\xe9\x95:*=\x84\x13\xe6\xbe\xef:\xc5I\xd9Z(}\"\xdc\xeee\xf2\xfe\x17\xcc\xe5\xeec\xcc\xe5\x8cP\xde\xbb\x01jo\xc2\xcb\xc1\x9e\xdeB\x0d\xef\x15\xe1\xe9\xb6\xfa1!W\xda\x1e\xfd\xea\xdf\xdf\xf3{\xbf\xbb\x93\xce\xbd\xbb\xe6nC\nn1hq\xd6\x8e\x16\xc0\xc12/O\xc2\xcf\xed\xaf+\xf9\xb5\xfd\xa9\xc4OIy\x9c\xbd\x0boH\xda>x\x94\x8f^M\xc7\x9b\xf2\xa5,\xcf\x87l\x11\xd2hN\xe2\x8b(_\x92\xb2\x8e\x0dj\xfc\xbc\xb5\xe5\xb7*C>\x05{\x8bf\xf5x4)\x9d\x10\xa2\x14F\\\xed\xbe\xe1\xa3\x82\x1f 4z\x9ag\xfdz\xcd\x0fN7\x07\xa1\xca\xaf\xea\xecaq\xcf\xf3 \xdb\xdclCr\x15\x82\xfb\xf53\xe1\xdb\x11\xbd\x04\xb2\x9f[[V\xd2\x99\x0b{\xcc\xbc+\xea\x80\xb5\xbe\xb4u\xabP)\xb7$EP~J\x96\x97\xf9'\x92\xd9\xc3\xef\x80\xa2\x11\x0f\xfb\xdc\xc5\x19_l\xcb\xa4\xc3\x1e\xf7\x0cb\xfd\x9a\xc1\x16\x9ft\xbe\x06+}\xfeK\xff\xe1a\x15^\xdb\xa2`r)\xba\xeb\xfc\xdd\xf1\x8cq\xa5\\%\xb6r\xa7V\xaa\xd4w\xbd\xa8=B\x15\x02\x8f\"\xc1C]\xc7a\xc3\x17\x0d\xf6j\xa3\xa9\xf5\x0f\xd3\xb8m\xc8IL\xa1H\x9d\xc30\xfb=\x85(LSX\x10:\xcfc\xc830b\xd4\x96\xcb\x8d{\xcew+&\xa20S\xd8\xf5\x02)x\xd2no\xd0a\x87\x08\xe0\xe2\xe6M%\xf5^\x1f\xa4\x96\xc5H`\x1f\xb4\xaa\\\xf4:\xaf\xd8\xb1\xdd\x7f`}\x9d1 S\x14\xd5\x15jD8\xcdW\xb8\xc0\xb6y\x1b\xc1!\x8dd\xf2\x97\xedr\xedt\x19\xae\x9c\x87]+\x10\xe1\xc8\x18\xd3^\xdd\x9e\xa1\xe6\x8eJ\xd1?\xc7\xd9\xf4\xfeun\xfcs\xbak\x83\xe4<[\x93\x82\x82p\xfbKsX\x16\xc9\"\xa1\xc9\x9ap\xefON\xdf.\xd3\xd6\xb9\xe9\x0c\xec\xfb\x9d\xfb\xfa\xe5\xd0\xadpd\xd4w\xdd'\xb8\xf0\xf4\xf5B\xd7\x1f\x0dE\xfa\xae\xe7:\xc7\xe3\xeb\xf7\xe7g\x97gz\xd0\xd1U+jA\xe3s\xd9%\xc8\x02)\xcc\x12\x8e\x99\xdc\xdd\xef_x\xae\x93L\x8bpA\xf4\x86\xe4S\xe0\x05\xa0\xcdS+\x8f\xc2\x12\xa0I\x10#7\x97ix\x07{\xe0dyF\x1c\x1f\xa3R\xecx\x0d;\x17\xee\xa4\xb0,\"\x96\xed\xaf\xe1:\xe4VE#\xc7\xe7\xa4(\x0dP\xe3/\xa3\xbf$Y\x9c\xdfV\x08\xc3\x0b\xf2%\xc9\\\x1e*\xa0H(q\x9d\x1fx\xd1?T\xc2\xec\xb7{\x1c\xbf\xfe\xf0q[|r0?\x1a\xbc\xba\xc2\x95\x14 \xde\xbe\x81bk\xeb\x8d\x07\"<\x8b\x12oe\x92L\x8a+\xc3\x8d\xa4\x00\xcc\xd2\xd5\x0e\xc4\xaecE\xa0\x1eP\xa3\xb6Zi-#\x02\x16\xa2v\xe9.Kq\x8e\xcf\x8f\x17N\x91\xa0\x03t\x1f\x9a\x9f\x85\x93\xd3I\x88n,\xd1\xfe\x04=\x9fka\xd4\xa5\xe3h7\xfb\xff^D\xfa\x17O=\xd7\xf9D\xeeJs`\xdf\xdd\xdd\xfe83\x96\x8e\x17\x82\x86w\xf1\x07w(\xf9\xe0~>5\xd9$\x17\x13\x871\x11\x05\xd9\xfaky]\xce\xc3\x82\xc4\xd7\xd7\x8el\xd4\xfc\x0d\xef\xfb\x1f8\xa2\\\x8e(\xe7#\xfa\xc7\xd7\xbe\xf1\xd8\x10\xab\xa38\xd2\xf7\x9b\xd7\x90~R\xbe\x97 |6\xf5M\x04\x99O\xf3wy\x14\xa6\x84\x9f#\xbe\xe4\x9e'\xb0u\x82~\x07\xd1\xa1\xacsVG]B\xbb\xb2\x02\xcd\"-T\x18;\\\xc34%8be\xe9F\xc2\x12\x19\x1e\x008\xde5#8773\xd8\x84\xc2\xab\x18\x13F\xc4\xf7\x9dl\xd6\xbd\xf0\xd2\xe2\xea\xf7\xd9\xffx\xb6\xf7y\x0f\xa9\xf4\xe2\xe5C{\xfb\xa8\xa4\xd2\xee\xeeK/\x98\x9a\x899\x93\x07\x17\x13\x9e\xea\x1b\x87\xf9\xbe\x07\x95a6r$V3!='5A\xeeC\"\x03\x84\xa2\x03\xb6\xf6foz\xa25\xdd\xecH\x87\xc6\xcd\x8d~\xcf\xb9\xea\xf5\x80\xf3t\xd74\x03\x18{\xbdw-\x19#b\xcf\x04\n\xcem3X(\x03_\xf2\x18B\x82\xa7!\x0d\xdf\x11\xc6XI\xa0\x13L\x8c\xa5\xf9\xf2Eu\xd4\x9e\x19$a?\x86\xb1\x8cW\x04\n9ju\xcf\xc7=)g\x95\xec]}\xaa\xcb3\x11\xd5J\xa0\xd1*\x11e\x13\xe8\x8eVc\x1d\xbf\x81uy\xfa\xbdY\xd4\xf0\xbdM\xce\xd9\x07\xbe F\xefd\xc8\xbf5W|k\xfc\x9b\x03\x9b\x90\xa1\xbf\xdb8'e\xf6{\na\x14\x91%\x85\x82\xcc\xc8\xe7\x96\xd3[\x01\x11\x02\xa9~\xdb\xa6f[\x14\xa5\xc5\xfd\x9b\xd3x\xc6\xc3\x1el\x07\xdb\x9aH\xc9x\xe2:\xdb\xc1\xb6\x03\x13r\xe5jnu\xaa\xa3\xd6(\x80\xef=\xbe\xe9\xa4\xb8\xe2\xf6\xb8\xb0am\x03z\x8et\xd3\xfcn\xdc3\xe0\x11\xc5\x8d\x8c\xb4\xfd\x90\xec=L(\xb27F\xac\xda2Q\x16\xa2\xad\xd6 \xc9M\xa0\x9f\xefx\xc1\xf4\xa1k\x9b\x07\xfc\xcc\xe7\xec\xa9|\xe1\x81\xa1\xfe\xf1\x15\x83.\xd4\x19\xfe\xa1Gtq\xae\x91\xc4!xAs@\xdd\x1d\xd4\x97'\x90d\x1c\x93\xac0f\x95 c\x0b|\x1c\x06\xd3\xd65I\x1f\xac\xb7\x97DH\x8cf\x84*\xfc0\xef\xb6\xd9\x8d\x07\x0fXz\x7fT\xdf\xa1\xcd\xb5\xfd\xddFs\x90\xdf\xc1\x1fc\xc2\x05iI\x9e\xc19\x89VE\x99\xac\x89\x94\xb8\x92\xcf\x94dq\x92\xcdZ\xc5\xc2\x15\x9d\xe7\x05\xfc\x9c\x84\xd1\x9c\x94i\xb8\x86w9-\x17a\x96\xaf\xe1\x87T\xfe|\xf5\xfa\x8f\xb3E\x98\xa4A\x94/\xfe\xd0\xaa#M\"\x92\x95\x04N\x8e/\xb5oz\xd6\xcb9\xe6\x82w\xa2\x84{r|\xe9\xf5\x949\xcc\x97wE2\x9bSp#\x0f\x9e\xee\xec>\xdbz\xba\xb3\xfb\xca\xd8\xe5\x9e\xaa\xde\x93b\x91\x94\x18\x14,)aN\nrs\x07\xb3\"\xcc(\x89}\x98\x16\x84@>\x05\x06_3\xb6L9\x84\xd9\x1d,IQ\xe6\x19\xe474L\xb2$\x9bA\x08Q\xbe\xbc\x83|\xaaW\xcf\xce\x11(\xf3)\xbd\x0d\x0b\x02a\x16CX\x96y\x94\x84\x94\xc4\x95\x1e/Zf\xc04II .\x9d\x13p.D \xc7\xc36c\x12\xa6\x90d\xed\xca \xc8\x9cp\x9b\xd0y\xbeb(\x9d\x83M\x92g\xbe\xf0s\xcdz(?\xa7\xc9\"\x11\x0d\xb2\xe28\x8b%\xd0\\\xaf{U\x12\x1f\x07\xe5\xc3\"\x8f\x93)\xfbOp\x0e\x96\xab\x9b4)\xe7>\xc4 k\xe9fE\x89\x0f%K\xc4\x05\xf4\xd9(\xb7\xf3\x02J\x92\xa6\xac\x86\x84\x94\xc6\x89\xa9\xfb\x8eE\xf0\n\x80-\x06\x15\xd3\xcbz\x05\xb7\xf3|\xd1\x1cgR\xc2tUdI9'X&\xce\xa1\xcc}\xbd\xfarU\xdd+\xb0\xd2\xd3>\x1a\x1f\x81sp\x01\xc7\x17\x8e\x0f\xbf\x1c_\xfet\xf6\xe1\x12~98??8\xbd\xfc\x15\xce\xde\xc2\xc1\xe9\xaf\xf0\xe7\xe3\xd3#\x1f\xc6\x7fy\x7f>\xbe\xb8\x80\xb3s\xbd\xe6\xe3\x93\xf7\xef\x8e\xc7G>\x1c\x9f\x1e\xbe\xfbpt|\xfa'\xf8\xf1\xc3%\x9c\x9e]\xc2\xbb\xe3\x93\xe3\xcb\xf1\x11\\\x9ea\xfb\xa2\xe6\xe3\xf1\x05\xab\xfbd|~\xf8\xd3\xc1\xe9\xe5\xc1\x8f\xc7\xef\x8e/\x7f\xf5\xe1\xed\xf1\xe5\xe9\xf8\xe2B\xaf\xff\xed\xd99\x1c\xc0\xfb\x83\xf3\xcb\xe3\xc3\x0f\xef\x0e\xce\xe1\xfd\x87\xf3\xf7g\x17c88=\x82\xd3\xb3\xd3\xe3\xd3\xb7\xe7\xc7\xa7\x7f\x1a\x9f\x8cO/\x038>\x85\xd33\x18\xff<>\xbd\x84\x8b\x9f\x0e\xde\xbd\xc3\x96\x0f>\\\xfetvn\xea\xfd\xe1\xd9\xfb_\xcf\x8f\xff\xf4\xd3%\xfct\xf6\xeeh|~\x01?\x8e\xe1\xdd\xf1\xc1\x8f\xef\xc6\xbc\xe5\xd3_\xe1\xf0\xdd\xc1\xf1\x89\x0fG\x07'\x07\x7fb}?\x87\xb3\xcb\x9f\xc6\xe7\x98M\xf4\xfd\x97\x9f\xc6,\xa957\xa7pp\n\x07\x87\x97\xc7g\xa7l\xcc\x87g\xa7\x97\xe7\x07\x87\x97>\\\x9e\x9d_V5\xfdr|1\xf6\xe1\xe0\xfc\xf8\x82\xcd\xde\xdb\xf3\xb3\x13\x1f\xd8R\x9c\xbdeY\x8eO\xdb\x9d>=\x1d\xf3J\xd9\xaa5\x17\xf7\xec\x1c\xdf?\\\x8c\xeb\x9e\x1e\x8d\x0f\xde\x1d\x9f\xfe\xe9\x82uH\xcd\xacC\xcdv\xe3]\x9e%`!\xf7\xa5\xf4\x02\x92\x8c\xc1g\xc4\xe3\xfc\x8a\xf3\xb5J9\x12\x97$\x8d\xc4s2\x1b\x7fn:\xf1S\xe2oAS\xc7\xdd\xd88\xea\x874Z\xb6q\x10R&AE\x04\xaa}\xf9\xab\x0e\xca\x00#dI\xa8\x12\xa6\xc1XU\xa5x\xc26<\x1a\xd0\x19\xbc\x92\xf7w\x95M\x89\xa7\xb2U,\xc1E%\xa4\xcbdA\x1a\xd2.k%|\n\x1b\xd5\xf0$\xa3ZVK\x17\xebCF>/I\xc4N\x992\xa1+\xe1\x83e\xd0\x8a\xe4VI\x97\x14\xd3\\_#o|}\xedT\xf7PUh\x99\x96\xb0\xab9ak\xe1\x94\xcbH%\xda\x00\xc1\x10\xe0h\x17\xad\xccd\xd4\xfa:\xd0G\x1d g\xe7\xaa\xd3\x96\xc6R\xefS\xaf%\xab\x9c\xec\x18\xae\x14\xe5M,7\x9e\xec\xce+*\xe4jz\xb5N\x1aZ$\xf3\xeb\xf3\xaa\xbc\x0f\xbb\x06\x9d=k\x14M\xc3\x04\xa0\xf9]%\xe0\xc4\xb7\xa6~\xe0\nidA\xb2~\"w\xa5\xbb24iu\xa1\x0f\nc\x84\x12\x9f\x90\xfb\xa2G\xe1I\xee\xa2gz\x1e\x19$T\xc1\xc2\xd0S\xd2\xe8\xa9\x8c\x9c\xeb\x86\x93\xb2\xba\xf54h6\xaay*\x90%f\xeb\x06\xf5Y\x0b\xa5\xea\xc9\xd0x\x8cm\x03\ntN\xd5\xdd\n\xa8\x8b\xa2\x85G\xaf\xee\x83\xd9~i\x8e\x0c\xa35\xe5\xe2\xba\x97\x8bw\xb3F\xa2\x90\xf9\x8a\xb7\x04-\xd6\xd5\x94\xb6\xf7-\xf5\xf9\xea\xf9\x90[s|E\xdd\x96\x11?\x06\x9a\x13\\\x88O\x86\xd5\xa3\x8d\xd5\xa3m8\xa3ze\xbc\xd7\xbc\xc2f:\x0f,l\xec\xa0!d%\x1bMhA1\xcd\x80\x94\xcf=\x11Oq\x10\xbf|\x1f\xa5K\x9b\x00\xbb\xbd\xf4D\x89\x92\xc4\xd6\xd6b\x94\x88\xcc\xba\x01u\xb4\xd4{qZ'W(\x11n\xe7\xcf\xb8>\xba\x1et\x9a=\xea\x8e\xa7\x86\x1do\x0d7,Q6\x9d\xe4\x96\xbdc\x0c\xb9\x94\x08\xffqO\x9e\x98\xa6\x85\xf1\xf7[\xbb\\\xc6W[\x08M\xf2+6\xbcb\x92_a<\xf7\xc3\xa4\x88ViX\\90\x92\xa9\x04\xb3\xf9\x90 \x97\x0e;\x08P\xe2\xa3!\x00\xaa)\n\xac!\xf6#\xe56ih\x9f(\xcc\xd3D\xda\xd0\xf2\x0bR\x96\xe1LV!\xdf\xf6\xea/C+*i\x18}\x12\xd5\xf0\xdf{2\xd5P\x85\x14\xc57w\x04\x03\xf0 \x06\x922\xde\x06\xe1m\xca\xe4\xad\xf8\xc2-?\x84\x1f_\xe0~\xd5\xf2\xecn\x91\xafJ\xc7\x83Mpp\xfe\x1f\xacP\xf8\xfd+\xf35\xe3\x0bc\xc8#\x96n\xf2|\xcc\xd2\xf5k\x80\x95H\x7f\xed\x99\xcc'K\xbb\xd8\xc9\xa4\x10\x8d\xda8J\x84\xbb\x1d\xae\xf0j\xd0\x9d\xe2zS\xdc\x19? \x0b\xd7{\x03\x9b\x9b\x14~\x80\xcc\xa8S,g\xa2\x1do \xa4\xec\xbc$\xd4-0\xfeW1\xd9\xbd\xb2\xe9\xed\xd6\xbf\x14\xa5'\xde\x07\x86\xac\xfdF\xb2P\x8f\xc2`\x1ceS\x15\x9em\x94f\xe2{\xe9\xf9\xe0\x9c\x84K\x9b\x10x\x90V\xbc\"Un\x85\xd0\x13\x10e\xf1\xea\xf8\xc2\"\xd2|\xd1\x12\x81\n\x88\xda\xd5E\xf4\xa5H\x7fi\x84\xb4\xd4\x0ei\xc2< \x0ei\xc8\xad\x140\x1a\x99\xd1\xca\xaaL\xfe\xce\xf1\x05\xfbaX\xf4\xd4\xb0\xe8\xb9\xdfH\xae\x16=i\xa6\xf3E\x0f\x9b\x89|\xd1W\xcdD\xbe\xe8es\xd1S\xe3\xf2\xa8C\x1e\xacN\xdb\xf0\x9b\xb2\xb5\xcb\x1d\xa7\xd0\xca\x9c\x98\xeb\xdcK\x1f$\x9b\x9b\x19\xfc\x00\xc5\x1b\x0f\xc8$\x87M\xc0\xf81\xed\xb05\x92o\xd3\xe6l08\xbdx\xaa#\x1c\xa1\xf2\xfcZ\x07\x1bcL6\xa3\xaaS\x0b\xda\xba\x84\xc4m\x18\x0c\xd5\xe0\x8a]\xec\xb9\x8a\xb1\x90,@B\\Q\x1e(\xdc\x90\x1b\xb6[E\xc7Z\x8dj\x10\xb8V\xbe\xaf\xba\x03\x1dF\x83\x9a\xf7\xf4\xea\xbe\x8b`>%\x9e\xebkcZ\x83\xf6t'\x9a\x97\x8c\xf6\x14'\x03\x16\x0eq\xd37\xaa\xb6\x08u\xc7A\xab\x99\xb3\xaf<\xe8L\x15E\x15\xd56\xb8\x87\x92\x8dU;\xbd\xd9\x9ey)\x06!\xed\x0e\x1b\xb1z\x95\x9e\xe9\xab\x015\xf2m!e\x90\xbaB\x16\x8e\x08\xffl\xd0 \xcbcry\xb7D\xd2\xc9d\xfe\x88\xf7Af:\x92;\xa4\xc7zH\xa3\x1e\x83\xe9%\xdfW8\xbb\xd5\xd4\xec\xf1\xab&\x19t^\xb0&&\xbf\xe0l\x1e\xdd\x15\xec\xc3*HJ-7\xb2\xd4\x9a\xde{{\xfeAgPv\x9f=\xf7\xaa\xcb\xd5!z7\xafwv^\xee\xbe~\xfd\xf4\xfb\xe7/\x9f\xef\xbc~\xbd\xfbP6\xc5\xe4\xbf\x1d\xe7\xf1\x0f\x8c(\xc7_\xff\x81\xbe\xf1\xb93\x02\x02?\xec)\xa2\xb0\xfek\xb1{\xf5\xa6\x1b1I\xdc\xde\xba\xd4\xed\xe9\xceC\x80\xfb\xe9K\x9d\xc0\x04\x01\xdd\xdf\x08\xc1l\x13\xe4\x8f\x00\xc1\xd5NH\x1a\x10\x8cU\xa3\xb9cDJ\x83\xc5\x9env\xd0\xca\x00\x9d\xf7\xe0 \xe5]u\xeb\x05\xf9\xdb*)H\xe3\xc5uV4I\x1d/`\x03\xb3xb\x01U\xae\xfc\xe5\x8b\xdc\x8e7 \xdeD6^du\xc6zz\x02[}u=\xfbf\\=`3v(W\x99\xaf\xd6[FT\x0c\x04\xb6?\x06_>N\xdc\xfd\xd1\xe4\xffL>^]}\xf7\xc5\x9d8\xbf\xbf\xf2\xdc\xfd\x91\xbb\xbf\xf1q\xd7\x9b\xfc\x9f\x8f\x1f\xaf\xbe|\xfc\x18x\xdf\xed\x7f\xdc\xf5>\xea\x81Yx\x00\x98\x8f\xb7\xdf\xfd{oH\x07\x8b!S\xc3\x8eI\x17\x8bV\x92t\x01\x98F\"k\xc3\xad\xb0\xc7\xc6\x1ed\x08\xd4%R1JB\x158B\xa64\xdc\x0em\xa0F .?\x8f\x05\xc2\xa3\xc8n$\xea\x9b,A\xf9\xf6H\xa4\xd3<\xf7^\x86\x0e\xf7BD\xf7\xa4\x1f\xcd\xf2\"A\x99pm\xd3\xcaE\x17\xf5\xc1\xb9\xbe&\xe5I\x1e\xafR\xe2\xe8\x1a B\x1bAU\x08AC\x9b\x05Y\xe4\xc9\xdfI|\x11.\x96)y[\xe4\x8b\x8bhN\x16\xa1\x90*\xf0\x8f\x87\xa8,\xf8\x97\x93w\xe3\xcf\x98\x8d\xb3\x10\xf8\xf3/\x8bT+\x94dSR(\xefe\xbbfq\x00\x824\x81i\xd4\xac(z(\xec\x98\x89\x1b\x0b\xdd\xcc}\xf1\xfd\x0b\xcf\xb0\x0f\xf0\xd3\x8b\xd7\x9e\x91\x97\n\xed\xeb\x83\xa0\x10\xd4\xf3(T\xf5\xdaXKFF\xd0\xddZ\xfd\xae\xfdk-|\x19\xb6+\xe1\xa2\x99\xe1qm\xa5,\xa7\x95\xc7\x10F\x8bg\xbd&\x8b0I\xef\xd1\xc2\xaa$\xc5\x1f _\x8c \xca\x17\x83\xda\x12\xfdb,(\xd9\xa2\xc9\x828\xc3[t\xe5\xf5\x95\x17\xd0\xfc\xf8\xe2L\xa8\x84\x19\xf8\x02\x83<\x05\xd1\xc4\xf0\xb6\x06\xc5u\xe3\x95^O\xd3<\xa4\x8f\\u\x92Q2{\xf4\x0e\x0bT\xd8G\xff\x83\xb2\xca*\xf6\x94\xb88\x10 \x8dW\xad\xf2\xa5\xdd~\x13\xdc\xdb\xbcLw'\xa4\xcc\x82mt\x17\x9d\x0frr%\x99\xdeyF\xff3 \xc4f4h3a\xf2AO6\xc14/\x16\xa1\x812\x02\x81\x12V\x13\xd4O\xbcv`\x13\xb8\xa9\xcc\xca\x18\xd5S\xc2%\xf6.)\xdf\xae\xb2\xc8s\x13\xc6c%\\O\xda\xf9\x90}\xca\xf2\xdb\x0c\xb5 \x85K\x1b\xec]\xd7\xd4\xa46\\Xa%\xcb\x0d\x93<2[7\x89\x7f\x00\xa4\xa3\x15U\xd6\xfa\x8ep\xf7\n\xf6\x9b\xaf\xa3\x96)\xa8|r\xd3RP\xcbR \x99\xd9\xb1\x14\xca\x97\"P\xe1\x8035V\xb3Vg\xaa9\xef\x1c[\x16\x00m\xce\xb26\x844\x93\xcf\xa2\xe3\xdb\x0c\xc9\xb0\xcf\x0bC\xc0f\xf60\x1c6\xc3;j\xf3\xf7\x1b\xfc\xbe,\xc841x\xb4b\xcfuU\x03F\xab5g\xba\xe5S\x9b\xad\x16\xe6\xef\xe3\x8aG\xb6\x1c\xe0a\xc7\x01\xceN\x90\xd4C\xa8\xfa\x97\x9c\xe2a\xdf)\xee\xb2Y\xbd\xc3K\xff,\xa7\xe1\x8cM\x8e\xc3\xcd\xa5\xdc\x1b\xd8\x87\x1bF\x96\x8f\xd0>\x16u\x01\xee|\xb8\xe6\xde\xd2\x17\x13\xf6\xdd\xf9\xbcH\xb3r\xc4\xce\x8e\x1b\x96 _\xd1_\xc1\xb5\x85\xc0Q\x0f\x05\xc48\x91\x0d\xf9\xb2\xdc\x11\x83\x07\xd8\x03\xfe\xff\xcb\x17\x98qK\x10\x9f\xa7HU\x0d\xe5\x85\xe5\xe1P\x023\x11\xa9>\xae\x88\xbf\xf5$\x93nn\x9b'\x04\x9e\x0d\xd3\x81ns\xe5\x13\xc9\x1d\xc8\xfd\xb6\xb2\xca\x85\xdf^v\"\xe4V\x9d\xa6\xd6\xf94g\xad\xcf\xef\xdd\xba|\xb6\xac\x8b\xfb\x8d\x0bs\xaf\xf6E\xaeV\xa6\x01\xe4\xb6U;\x91M\xfd\x85\x99\xdc\xee!\xa7\x0f\x199\xad\xec\x19\xb4$\x95\x1b\xf0\xc2N\x9d\xb2\xbe]\xe8q\n\x0e9\xde\xd8\xb8\x98\x1c*\x84\xf7\x97/\xb0T?\xd4$7#\xc6-\xd3\xd5h\x87\x95\xe2H\xa2\xfa){(\xde\x03\x06\xb3h\xa9\xd2\xb5l\xf2a\x03\xff\xd4R\xbc\xc3\xba\x90Jc\x9d\xad\xde&;Wv\x96E}\x0ed\xff:\x0fm\xfd9\x93\xa5\x04D\xd91\xbd|\x16\x93j\xd4\x12\x1d\x1e^UG\x16\x92M\x07l\x04\x07\xd04\xb5\x9dN\x0e\x91\xef\xc1\xff\xcdOg,\xfd\x8c%~b\x7fJ\x9c\x8b\xee\x85\xf9\xdaw\x80\xc9\xa7\xd9\xd9=hw\xbe\xe1\xf3H\x9dA\x8d\x18\x94\x03p\x1byx\xba\x05\xce\xd5\x87\xad\xfa{d\x99.\x86\x15h\x82\xc7{Tw\xe5;\x05\xd1\xa8pa\xf0^\xa2[\x8e\x04\xde\xf7L[\x17j\x94\xcc\xa4h\xa8\x0fQ7\xa9\xcd\x118\x07\xd9\x1d\x9d\xa3\x0dT\x98\xc1\x0dAc7\x0bU\x80\xe1Q\x86\x9e\x08zC\xa5\x8doeH\xee\x11\xcf\x99\x018R\xcc\xdc\xb8 \xffSv\xd4W,\x15&\xcd\xd9\xf9\xdbB\xff\xb7lQo9WV\xa2]\xb8Xa\xc6\xe1M\xcc}\xb7\xf6\xfb\xab\x0fcV\xd1X\xef\xfaW\xe3=\xc8\xd4x\x89'\x05\x8e\x11\xff\xda\x84R\x86\x0d\xb3\x86\x9c+\x97x\xc3s3\x93\x19lL\xa24\x94\x81{M~\x0b\x92,\xc6\xc0*\xceG\xaa\x85c\xd3\xaf\xe1\x00\xcda;.\xa5X\x7f\x92\xba?\xd3\xbe\x1b.-\x7f\xda\xaf&Q\xcd][t\xcf\xd5\xf0\xc8\x9aq\x87\x95V\x9ex\x15\x87\x05O[\x84\x9f\xabxrU\xc6Fb\x85\x1b\x95 hw\xc1`\xd7$\x85\"2OCl\xd8YY~?\x8ds\xd5\xd8\xa0\xbb\xe2\xc4Z\xb1\xeaz\xc5\xb0\xd2\x0dGY>d\x01\x06W\x19/\x12\xca\xdd\xdcc\x9a\x12\xac\xa3\x9ayy\xbb\xd8\xf8\xaaMz\x9dG\xac\xfeI\xf3\xfb\xaeV\xbe$z\x0e\xbb\xd4\x03\xa9&\xe5\x06\x9b*\xc6(D\x06\xa8\x10\xbe\xebL\x1e\x152X\xacJ\xca\xd0g\x08<\x1e\xf2\x9a\x88[)\x8b\x1b\x05#\\\x11\x0eo\xf5\xcc6GD\x16 \xed\xb7\x9f\xe7\xfe\x8f|X\xf9P\xfa`\xf0\xc4\xac\x83\xb9\xabm\x03\x0c!'\"\xe5\n+\x1c$\xc4\xd4l\x01~F\x05'\xb7\x9d\xce\xd5\xd2\xda\xe9\xd2\xd0\xceDo\xb1\x9e\xa1\x8b#U^\xe3\xa9\xc6oc^5\x9f|\x03\xcd\xc3F\x1f eZ\xbe.\xbf\xff\x90E\xe1j6\xa7>\xac\xb2rI\xa2d\x9a\x90\xb8\x1a\x1bv-\x00\xf7\xf7\xb0\x89\x0e\xa2\x1d\xcf\xe4.\x84\xb7\x17\x05\"j5\xa7\xde\xa3&\xdak\xcdq\x82^\xa2\xd4\x19\x98\x90+\xbb\x92\x05\xd7\xc2\xc8<\x0f\xca\xdb\x04UXt9\x97i\xca\xa2\xb0$\xb0k\x8e\xf4/\\\xb0\xa2[t3\xd5\x82>\xa4\xdb\x9f\xb0\xd2\xa7\xbd\x95\xfa\xcdu\xba\x7f\x13\xcf\xee\xd9\x84\xfa\xf6\xf4\x9e\x0d\xca\x9b\x7fc\x99UE\xd4\xf7[\xe1\xb1\xfd\x18.\x97\xe9\x9d\xe8\xe0J\xd7{\xad\x84\xf4\xb9k\n\\\x83,\xd4\xfd\x1a\xc4C/\xc5\xeb-n\xda\xe2y\x95^t\xc9C4r\xc7\xe5Pnnz\x90N\xca+\xad\x8bF\xfc\xa3j\x954\xb1L\x18\xc7J\xcc\xd0N\xe5!\xb6\xe3\xc26$oX\xfc\xce\xa4\xb2\xda\x1aYV\xa7^\x17\x96\xecAU\x0d<\x93\x91[5\x02)~cx\xd3u\x94/\x0e\xfa\xff(\\\x1a\xc8.y(\x90\xaf:8\x02\xaaU\x94\x04\x08/\xa5\x9f\xf6\xae\x074\x87$\x8b\n\xc2\x90\x0d\xfa\xb7\x08\x9c\xd6\x92J\xe4\xea\x9b\xe9/\xd9\x7fZ\x84\x11\x1e\x82\x8d\x04\x0cL\xd7u^\xe7h\xe6\x00\x1b`\x15\xb9&<\xfa\x8du5\xd9\xc3\x03\x88d\x12\x83\xee\x83[\xfd\xdec\x8c\x8dyU\xd0\x08[F\xd8J8M\xf0\xad\xeb\xd4\xbf\x13\xfb\xb7\xdaA\x9a\x0e\xe3\xad\xd6F\x07\x81\xad\xed\xd1\xb3\x156:\xc6\\\x15\xe5\x9ci\xeb\x8ax_g\xf4\xd1\x87\x98~\xe6>y\xd2\xb9/\xda]2\xb7f\x05t\x8a\x0e\xc8\x1a#\xd6\x97G8\x02\x90K\xd8\x9eh\xa3\x0d\xb7J+\x19\x8a\xe8\x8dh\xf0#cC\xaa\x0b\x0eF\x9e\xa6\xb0\xf04\x96\x93!\xb3\xa1\x03\x83\xc6\x04N\xd0\x9bjo\xbc\xb1W:\xa9\xf6\xcc\x16\xb4\xf8\x0e1\x13]\xcbh\x03\xeat\x10,\x9b\xc8\xd26\x8d\xc4\xdd\xf1\xea\xdbx\xbfE\xfc\x19(?I\xe3\xc3H\x8b\x16e\xea\xeba\xbe\xca\xba\x05\x02:\xbboS\xae\xa0\xed\x85m\xc3YRy\x94\x14\xd3`q\xa0R\x87+\x96\x16\x9c\xfd\xf8F\xe3F\xec#4\x1c\xe6\x95\xbaJ\xa3T\xbfI\x80n\x0cD5\x0f4\x99\xfbl\xe7{\xcf\x0b.hA\xc2\x85\xa0H\x82s\x12\xc6\"\x02\x1b\xbe\xffR$T\xbcg\xee\xee\xeb\xefQ\x80y\xb4Z\xa6\xe437\x80\xe3)\x97E\x98\x95\xd3\xbcX\xf0\x8aww0\xf5}X\x96\x97\xf3\"_\xcd\xe6<\xf3\x8b\xe7\x83LMz\x1d\x01\xf28_&T,\xdc9>\xdf\xf1l\xf4\x9fA\xd7\x1e481II\x12\xc6|\xa1|\x84\x07\xaa\xe0\xa7PF\x8b\xbbf\xd24\xc9\x92f\xc0E\xdb9\xbd\xd19\x07\xfa#-\x0f\x08o\xd4~\xb6\x93F\xaf\xec\xf9\x04R*\x8c\xe6\xfb\xea\xb3\x16^d\nd\xe0o\xc2\xc8 \x82P\x1f\x1a,\xb9\x93\xc5\xe8fk\x8b\xf1y\x18v\x1d+`3h-k\xbe\x07\x02\xac1\xca\x8bO$>'\x7f[\x91\x92\x96o\x0b\xf4\xe9mJ\x96\x8bDP/\xcdPlO\xd3\xdb\x92\xcfW\xee\x91\xa5\xf5\xedk\xc7\xeeV\xb7\xd3]\x9b\x0fYq\x11\xc6\x06\x0dn\x8a\xfc\xb6\xe4\xd4\xcb\xc4Y\xef\x04\xbb;\x8e\x0f\xec\xc7\xeb\xc0\xb9\xaa]\x81\x04kR\x94I^y\xf9\xf0\xe1{\x8fk\xd2\n{\xda\x04\x87w\x99\xe8KpW\xed\xd3\x0b\x1a\xa2-\xfc\xac\xdd\x9dT\xd8\xad\xbc\xd0\x8e\x954H\xb29)\x12\x81\x15^\xed\x1aX\xaa\xc8h-\x02(|\x12z\xa6#\xdc\xe0\xcf\x06\x99IL\x05\xfe\xd1=\x0e\x80\xd4uvw\x9f\xefJG6\xed,\\u\xebC\x92\xd1W(i\x025`\x8d\xd7R1e\x03\x98\xfb\xa8\xa1\xc5\x1a}iE\x0d\x0b,l\xf983bg\x10\"6\xee\x82\x8a\xa3C\x0420\x84Q\x05e\x1fSU\xf6k \xd5\x11\x99\xf0\x8b\x8e\x93\xd9\x15\xfc\xeaz\x7f\xea/\x10\x19z\xb7\x0f\xbb/`\x04\xbb/\x9e\xbdzn\x99\x85FW\xd0\xaa\xf4\xcb\x17A\x0c\xe7\xb0\x0f9\x8c\xc4\\\xa4\xf5\x87\x94Q$)\x8c \xf2\xcd\x95\xd4\xb1~\xdc\xf6w\xafF\xe6az\x18\xa62,\xa7/\x0f\x02\x12\x1f\x15a\x92\xa9\x89\x1c\xe7i)\xcdr\xfclh\xa6\xc5\xa4\xa4E~'\x12\xcd+\x82\xf1\xf99\x7fE\x82\x98Dy,\xa2\xc9\xd8N\xaaF\x1eVxZ\xb5\x86B\xb2q\x16\xe5\xa2\xb7\xa4\x95\xf6\xe5\x0b8+:}%\xe5I*\x13\x87 l\xc5\xb5\xa1rD\xab\xe4)\xef\xb2HJL\xd8\xfb\x0dn\xe5\xf7\xdcZW+\x9cg\xa8\xff\xd2\xab\xb8\x0b\xedC\xb3\xef\xc4\xe4A\xdc\xaeoU\xec\xd8\xad\x84RpY\xf4]\x16u\xe7\xe3\x81\xe0\xb0\xe3\xd1\x8d\xfd@d\x14c\xff\xa8\xe4C\xb4\xb9%\xb2\x81\x8a\xc6 \x15\x7f \xf7\x1eII\xe6+\xbf\xd9\"X\x1b\xf9\x8a\x871\xf5\x0c\xc4\x87\x99\xa6\xd2\x9f\xad-\xe5x\xf71r\x80[\x9fJn\xeeC\xe1\xf9\xca9\xe5^\x08\xa6\xdco\xad\x03\x97\x9br\xb9\xa8\x14\xa9\x12\xc1\xd8\xf3+,V\x19\xe3\x15\xdc\xdc-\x1e\\\x81\x0f\x17\x1cT\xecZ(\xe89\x8aO\x00es\xd0A\\\xf5+\xf8\xe0\xad\x01\xec\xc1\xd8\xd5YD\xfd \xf1\xcc\x90{\x07\x7f\xb7\xb6 C\xde2\xb9\xa2dX\xea-gB}\x8cfZ\xba\xd78\xcd\xfcj4gsv\xed*\xef\xf6\x91\x1b\xbfXi!\x05\x01\xa8@Y'\n\xf8kl\xfa\xba\xdb\x8d\xfciX\xd2\x1f\xbb2T`\xa6\xd4\x88\x8a\xcem$\xaa\x03\xc2\xae\xb9\x03\x92\xdf\xdai`-\x8d<\xcc\xc8-\x84\xfcf\xb11\x016\xba\xe0\xce\xbc\xad\xb9\xe6s\x930\xd8p\xe7\xfc\x12\xec\x8ew\x00\x8d\xbe\xd9\x8f\x06-\xe05\x1c\xa0\xdeY|\x9f2n\xf6V#\xfaX~N\xa6(\xe1\xa2ok\x0e\x0e7\x08\x9e\x94f}\x0c\xbe\x86\xca\xc5\x87\xc4\xcb\xe2\x8b\xed\"A|^\xeb%\xd7u\xd1\xb5\xbd\xac8\x01\x95\xc22e\xaf\xfej/\x8eg\xb4R\x98\xbf\xef\xc9/\x9e\xe7\xc3T\xb9-\x1e\xb4\xa67M\xa4\xc8E\xe9\xc6k\x03\x15\xec\x19\xfaP\xf6F(_\x05>\xc7\xcb\x03\xe5\\\xc4\xa8+r\xa6\x18\xe6\xa4\xf2$\xe4a\x87\xf9\x17\x97\xb7^\x7fSk\xd9\x1d4\x9ake4\xa6Ad\xd0\x17\xf0Q>\"\x06\xa3<\x83\x9e<\x01\xaa\x10C\xb8\x06-\xe2Hb\xe4\x98\xa59\x06,\xfc\xd5\x15\x07\x84\xc68\x16n\x8d\xbb\x07\x8d\xf3\xd6\xdawj\xa4?\x0c\xb6\x0c\xeb\xca\xb1\xb2\x86:\xcc\xb2\xa0j\xf9PD\xcfo#\xd8\xc9g\x9b\xbf\x8a\xf87b&;\xc1\x91\x8b\xcd\xcd5\xf4\x8a\x0e\x83AtZi@l\xe6\x93(\xa9e\x05\xe6\x0c\x95R\xf4\x8a\xa3\xcd\x92\xcf\x1b:\xfd\xcb\xf1\xc6\x82k=\xa1w \xbc'\xc3\x1c\xbb2\xd0'\xce\x86\x0f+\xd8\xdc3\xc9\xd3\xd8\x93\x07a\x9a\xf2\x83\xa0\xe4^\xd8\xe4\xee\xe3;\xa6\xf2\x92\xe6\x83\xe30\xd2\x82\x1f\x00Mx\xd9\xdc\xc4\xac\x1dG\n'I\x18\xb9b\x11\x0b$\xa2\xaf\x89*\xe7\xf1\xecb\x04qN`?l\xe7L\x1b\xd6\xbb(\x08)&\xee\x94\xc8T\x9c|\x10\xcdW\x99\x85\xd1\x92\x0f\xea\x0b\x05DP\xf6\xddy\xb99r\xbf\x88\x87\xc1}\xb5B\xbb\x88\x99\x1a\xdc\x1c\x8c \xad\x16-\xf5\x19\x036\xd5\xc0\xc1\x0b\xae\n\xb9\xa3\x81S\xdau\xf4\xca\x83\xbd\xa6\xb9\xf9\x1e\xb2\xd4ZW\xa9\x87\x0bhn\xa4Z\xb4\xc8H^\x86\x06fM\x07\x9d\xc2\xa7\\\x8f\xb4\xbc:\x85*\xf1\x96\xb6\x07xx\xf0\xc9\xd5\x1b o<6\x0c\xb4=\x92\xa28\x9c6\xebJk\xe1\xe9\x0c\xc2\xca>A~\xb7\x171\xb3s$e\x1e|p\xf8pZ.\x92\xf4gF\xe8\x08\x0d\xad\x84\xc8\xb5\xdbI\xa3\xfe\xa8\xb7{\xd5\xd4\x1b\xdc\xda\xa8\xcfW\x1f\x1c\x8d\xe9\xe6}\x85\xa4\xacE\xbfBYI\xcbX//\xe3nH\x18\x07\x8e\x0f\xce\xd1\xf8\xfd\xce\xce\xce3\x8b\x8f3ho\xf0*\xb9\xd7\xfd\x99\x85E\x10\xb1\xb4\x9e<\x11\xbf\x82yX\x1e\x0b~\x0bl\xa1C\xa5\x9b\xe8z\x99&\xed\xd2Wh(\x07{\x03s\xfb\x16X\xb8\xf3\x0d=\xeb\x08\xe0\xd5/O\x92Z\x90\x1bsU\xdf\x94\xd4\xfc&\xdb\xed\x9c\xe3\x92\x0e\xa6\x9a\xbc\xa4\xc2\x8f\xce\xfaN\xcb\xaf\x88\x85\xe6\xbd\xe2;y\xce5\"\x9c\xb4\xee\xe5}P\x15G\x97\xc9\x92\xf4a\x07.\x01h\x1e4uP\x90\xc30\xcbr\n\xac\"\x1f\xd8\xafB\xdcp\xea\xac\x88\xd6r[$i\xbf\xa3C\xb2\x9e\x1b\xf0\x1b\x18s\xbb\x8d\xfd\x86\xc1#7\x88\x0b\x85\x8d\\\xa5\xab\xd01:W\xa1_V\xae8\xdd\x02\x17\xb4P'4\xb6\x1fi+$\x0d\x94\xe2\xdc\xed\xaa;L\xf0**Y\x06\xd3\"_\xe8\xf1\xe3\x00DH\x05\xcb\x16D\"\x85\xebWpT\x8dT\x18\xe3\x0b\xf6\xf1U\"@FmsEX\xbc\xe1\xd1$\xd3\xcd\xdak;\x86\xac\xaa}\xe1\xf9\x90\x0b\xb9\xfb\xfe\xb0\xb3[R\x03\n\xc8\xf0\xa5\x0f\xa7\x94\x14@\xb2\xd8\x16d\xd3D\xdd(G\xb4\xc5y\x86\xd8\x8b\x19\x9e\xdc\xab\x16\xe7m\xe7\xd2A\xb9\x9e1Y-\xc9'\xb4\\$\x80B\xdc\xd4\xa4\xf2>\xf7\nN\x1az\x80'\xe1\x1dn\x15>\x11\x98\x1bQ\x0fF'+Q_\xc0\xf1\x8c\xd1\xa3\xb9,A\xb1\xa3\xc989\xd4\xbc\x8er\x0dm\x1eg\xeb0Mb\xc8\xf2l\x8bW\xbb-N\x1a\xe4s\x1c\x0f\x95\xc5\xb9/\x8e\xe6\xbc\x87\xcdy/xJ.\xf9\xd0v\x10\x10\xb9\x069\x97\x99\xf2\x00\xd2n\xde$\xc0B\xc3\xde\xaf\xa4A\xb6\xf5AU\xae\xdek|S\xd5}\x078\xd1o\xf4\x8c\xd7Axw#\x17E\x8b[\x82{Jl_\xda\xe1\xc2G>F\xf2H}\xbeVz\x18\xf6\x8a\n\xee\xb2\xa4\xda\xa0\x8c\x88\xcc\x95\x0d\xcf\x15\x03,\xce#\xcc|\x9e\x94F\x18\xf8\xce\xc2\x18\xb9@>\x95\xd8j\xd3\xaa\x1b\xc9\xeaF\x0b\xb8:8\x12m\xde\x0c\x9a\xcb \xed\xfd\xa6\xeck\xa7\xc3GR-\x18\xc4\xed\xc1\x05\x0c}p\xc3=\xb6\x19\xd8Z\xfb\xfc\xdb\xb8\xe0n`\xc3\x1d7\x02\xc3\xcd\xbb\xfaH\xb1\xc2\x08\xf4P\x84\xda\x83\x07\xce\x08\xb2\x1eY\x85\x90<\x8c \xe9\xce\xc8v:\x8fgo\x07M\x1f-\x86S)\xca1O\xc3\xc8\xc8\xe4\x1b\xf3Z\x85<\x9b{\xd0vs\x06\xb5\xa4G\x95\x94\xacj\xfc\xd1\x89\x9e\xcb.\x8c\xb5\xf2A\xa2\x8cvL\xa0& \xc3\xa0j\x10\xf1\xa4\x11\xee\x1c\x1a77\xbb\xea^eCjo\xf0l\xcdV\xda3 \x1b\x16H\x9e\xbflm\xf9\xca\xad(:\x82\xac\xef\xcb\x14\xa9\x07\xbe\x19o\xcf\xda\x02\x13\xbc=\x93$q'\x11X\x12z\xd4\xba1\xef\xa6\x95\xd0\xd6\xd2\xe2\"O\xb8\x99\xa2\xf9\xbb\xfc\x96\x14\x87a\xc9\x8d,6\xdc\x893'\x9f\x19w$\xee\xdd\xd9\xff-\xfc\x11\x96Q\x92\xb0\x1f7I\x16\x16w\xf8+,\xc9\x8b\xe7\x98+*\x9f\x8a\xff[OE\xb1\xdd\x17\xe8k\x17k\x90\xbf\x8b\xf0VQ3r l\x82\xe3xZ?P\xcf\xa8\xb2\n\xd0Ng\xe9`\xb2\xde\xf3\xe8d\xb2G]W\x83+\x83\xf2\x81I3\xd7\xca&5X\xe6[\x93\xda\x89\x91\x83&U\x9c\x83\x91\x91\xe2F\xae\xba\x97\x93\xee\x18W\xe3\x80h\xef\xdd\xe6\xe8\xbc&\x84]\xdf\x87\xcf\xc8\\\x85J\x15\xd7C\x1e\xe3\xc4\x19\xb1\x96,\x96)Y\x90\x8c\x92\xb8\x87\xb5\xa9/\xe7\xb8h\\\xfdF\xb2x`g\xaa\xbb\x8c!{\xdb\x1a\x90 \xa9\x02\xc2\x055\xe2\xeeW\x11\xbd\xdf\x8b\x99\xa8\xcd\xbf\xa1\xe9$\x83{\xa8\xaf\xee\xa8\xa5\xcc\xabP\xf1MQ\xab\xb0\xc8\xcbc\x8e\xe2p\x87\x16R6\xcb\xd8\xad\x06\xd2\x192S\x80\x07q\xad\x1f\xb4S 7\xfdJX]\xd5\xb9\xaf\xd2\xb2\x19\xbf \xcc\xb3\x88TB\xb7\x0e\xd2\x8d\xd6*G;\xbe\xa2\x9a\xd5\x16Q\x83r\xa8\x14-Fe\xe0\x16\xacT\x97\x8c\xdb\xee^\xdbJY-\xd3\xd5v\xa5\x84\xae#\x14\xd1\x81\xf6\xd8\xda\xdb\xbcl\xf4\xc7\xca\xe7Z\x9aw;\xdb\xc7\xd8\x8d\xf7\xdc\xf9\xf5%\xf7Z\xfe\xd6\xb6\xe9*S\xf3ToZ\xae:O/\xbf\xcb%%Y\xecz>\xd0V\x0c\xf8\xdf\xd5=U\x03\n~\xcf\xa0\xd4}\xb6\xf3\xcac\xc7\xe1\xf1bA\xe2$\xa4\x04\x13w\x87\x85\x0ex\x8c(\x83F\x04\xf2\xbbf\xe7\xbf\xb9\x1b\x99\xfb\xe2\xf5\x8e\xe7z\x95\xdbN\xc6-a\x98\xc8\x17\xafw\xbfa\xa8\xeb\xcam\xfc\xcb\x1ds\xf0\x84\x17\xa6\x88?\x99\xfb\xea\xa9!\x86\x97n]-\x0e\xf6f\xc6\x95)jSWx\xa0R*E\x867\x9a\xff\xc5\xb4\xa1.y\xdf\x05\\W^\x1b\"_u\xa5\x0f\xb51\xa2\x12\x9f!\xb4\x98W6\xcb\xe1\x85@\x86\xc1W\xb9A\xb0W\x9b\xbaF\x9a\x93\x05~F\xa0sI\xf4p\x11y\"\xce]\x04\x7f\xd8\x83\x1d\xc6&\xb0\xb4\x914H\x96vN[\x90\xba\xa5\x1by\xde\x1b\xe0a\xee`s\xd3p\x1d\x85z>\xaa\x94\x95rq\xc2T\x1c\x8d\x13z\xe5C\xe1N\xbdz\x8c\x1a\xbf&R\x15w\xc9\xdf\x00\xcd\x0d#\x89\xd6i$\x05\x95Z\x07\x86\x11\xb5&\xd1\x1b1\xd3\x8bHaJ\xc2\xc4nD\n\x8aT\xb8\xf1\xe1+\x97\x12tw\xaa\x06,\x967\xce#\\r\x11\xc0\xe1\x92|\xa6\xa7yL\\\xc7\xe9p\x1cn\xd0\x00QT\xaf\x06\xdc\xaf \x83\xd3\xc1\xe6{\xf2\x80\xe7\x97\xeb\xdc=\x16\xb5\x9d\xdfC\xfc_f\xfd\xfe/\xb11\xe3W\xb3D\x05\xad\xd6\x9a\xe4\x94E\x8e[;Z\"B\xf3\xa3\xca\x8f'8\xd1c\xd0\xc8\x077l\x1e\xc4!\xe5\xe1|\xf6`s3\x81\xff\x80\xa7\\\xdd\x01k\x0b\xcay2\xa5.z\xa1\x10\xe2\x17ix-(\\6\x82 \xad\x96qH\xc9\xbb\xf0\x8e\xcd\xf3\x00*\xd7@\xb2cD\x0f\x83\x80u\x19\xde\xa5y\x18w\x84\xfb\xa9;\xf06I)\xe9>\xe5{:`\x10\xc9\x0e\xeb@9\xcfo\xfb\xc9C\xc6\xa0\xb6|B\xf5\xf8>\xe7\xc1\xb4\x94\x04#UE*\x17\xb0\xba\xfby\x06\xc5\xb6\xe1\xae:\x86ke\x1b\xb3\xd9\xc8\x14\xbf\x8e=l\x16\xb2\x91\xe1.\xc5f]\x88s\x17\xcd\xc3lF\x84UW\xff\x0c\xdes\xfe\xda\xbe\xe3\x1d\xe7\x11\xa70|\xe4)\\\xe41\xb9\xd7\x0c\x9a\xb8/c\xd0\xae\xf6\x06vR\xdc\xb1\xd7|\xf7\\\xf37\xa7\xcd\x9f\xb5\x91\x81Vr\x8a\x1b\xcfi\xb3p:Z\xd1\xca\xb1\xc1:m~\xae\xc2J2;\x83+\xee\xa2\xf2\xbf\x1ea\xe2\xf5mH\xc9\x8fd\x9a\x17d\xfc\x99D+\x14l\xd2 \n3\xf1\x8a~.y\"k\x0cOR%m\x1e\x96?\xe5\xe2\x12\xa6\xfa\xfeKB\xe7'\x84\xf2Y[\x86E\xb8 \x94\x14\xe6\xd4\xe3,JW%\xab\x94P\x9ad\xb3\xb7ya.\xf6\xe3\xddqL2\x9a\xd0;\xfc\x1e\xa6i~{Y\xdc\x1d\xd3\xb3\x15\x95\x85\x16\xec\xa8\xafn\x0ddj\xa1\xbf\x96\xcb<+\x89\xb9P\xa9\x16)\x1b\x05\xf8\x1b\x0dg3\x12\x9f\xc9\xb1\x96\xcd\xa1\x97\xac\xbb\x97\xe1\xac\xca{Dh\x98\xa4\xd5\xab)\xfby\x9e\xd3c\xaet\x87r)\xca\xa3Z\x88\xf6\xe6rzo\xc2\x92\xbc\x0f\xd1\xacO\x00@Rw`\x9ad\xf1Q\x95\xc6+!\xd1\xaaH\xe8\xdd\x91\x96U\xa6\xf3i.\xf2x\x15\x89\xa6\xa2<+W\xb2\xdd\xbc9\xc2eH\xe7\xb2\xfcb\xcd\xfd!I\xe3g\xfcM>SRdaz\x94G<_\x92M\xf9^M\xca\xb3\x83\x8bg\xbc\xec\x92D\xd5\x8f\xff,9\xa8\x9c\x932O\xd7$\xbeX\xdd\xd0\x82\x88\xe6Y\x06\xedC+\xbdQS\xf5r\x91\xaf\x8a\xa8\xce|Ay_WE}\x19\x8b,\xaf!>\x82\xa2\x15\x94\xb9\xafLA\xdaQ\xa5'GyA\xd1\x0c\xf1Wt\x87\xf8+\x9aH\xafn\x13cm\xbf\x97\xd0nVa\xb0\x1c\xfd\x08\x17\xecL\x9d\\1\x96bF\xe8q\xe6N\x9c\x05\xa1\xa1\xe3\x83\x83K\xe6T.\x9e5G\xb5\xd4\xf3a\xe2T\xdb\xact\xae<\x1f\x0f\x8d\x12Eh\xffy\xe1\xb9\x93+\xcfC\xc8\xea\xb1\x87\x94\x97\xa0\xc1I\xb8\x0c\x92\xf2$\\\nE%\xec\x93\xeb`\xb0\x06\xaf\xd6\xf4\x16\xc9I&\x12\xb5\xb9A2\x81\xf7\xe4$\\z*9\xea\xab\x98\xe1g\xae\xe0\xd2\x7f\xf7a\x9a\xae\xf7Bj%)\xbf \xb1O\x94\xe7\xf1\x0e+\x93%\xa7\xea]RR\xcf\xf5\xbc\xa0 l\x1f\xb9\x8d\xaet\xdd\xc1\xc8\x08\xa4\xb1\x081A\x959\xd9\x97o\x88\xb8\xaf?/R\x87[5\xd4\x89]r\x19F\x9c\xbbj}\x9b\xe0\x04\x0el\xca\n\xf8r0\xb0j\xce\xbb\xbe\xfc\xffP\xa3\xa87\xa7\xbe<\xe6AX\x8e\xb3\xff\x1a:\x87\xf1\x84|\xf2\x83\xa4d\xffT\x81$ \xca|A\xbe\x11f+\xe0\xd4\x94\x8d\xfbf\xe4\x92\x07\x1d\xba\xf49>\xa5$\xa3,\xc9\x0c\xabz\xc7\x14\x08}\xd3\x9aH6\xd5\xb1K\xbcj\x9f\xf7\xed\xef\xd6~f\x0b\xda&\xd5\xb8\x8b\x92\xfb\"\x8f\x81\x953Tz\"n\xceZ\x1fQ\xa7\xac\xb5\xb5x\\]r+vW\xbb\xd8\n\x1d\x93`1yb]\x8bM\x811\xd2\xcd_Fp\x89\xd1\xf30j\x15\xcb\xe8,V)M\x96aA\xb7\xa7y\xb1\xd8\x8aC\x1a:u\xb6\xbcX\x1c\xb1\x14\xcc\xcapE\x12\xe1q\xb8\xfdy\xeb\xf6\xf6v\x0b\x8b\xac\x8a\x14\xaf\xd7I\xecT~\xda\x8d\x04\xb96U\x06h\x14\n*\x15\xc0\x189\x1aI\x894\xf2\xe5\x9d\x00Z\x1d\xe3\x87\xf5\xe1\xde \x83&dy/\xb0c\xc7\x8a\x9c}\xc3\xa1\xd2\xc6*\xd1\xaa(HF\xdf\x0bR\x84\xd3e'\xcdS\x19A\xc5\xfd^\xbfrY\x99y\x04~1\xf4\xd2k\xd6\xc1\xce\xff\x893#\x14\xe1{\xc5\xff\xe5%\xfe\xe7\x1e\xba\xd8\xaf|\x89D\x0f\xfb9'a,\xf6B4g?\xd0\xcb\xa6\xa3E\xd2\x88z\xc5\xde\x15Wf;\xd7\x00Z\xf7\x9fS\x1e%M\xa5VX\xd1P\x08\xcb/HJ\"\x9a\x17\x9e\x1b\xf5\x05\x82\xac\xb0\"\xee\x8b\xaaBM\x9d\x9fs\x04\x9cHz\x94\x86V\x85\x1e\x15\x9d7Q\xd3d\x8f\xd2\x0c\xab\x8e\xa3\x0cG\xf7\xfc\xef\xeb\x04\xe1\xa35\xc8k\x14\xcdf9\xdd\"qB\xf3\xc2\xd6\x01A\x9e>J\xf3\x7f-\xf3\xac\xa2>8\x18\xe9\xb3\xacm\x86%\x87$\x8dp~\x94\xce\x14\xa2\xbe\x9e\x0e\xf9Vz\xbe\x97\\R\xdbC\xecSh\xccB\xf7\x11\xc5Qr\x8b\xce\x91\xcd\xca\x80\x89\xc3\xe8\x03~M\xa8\xa6d\xdc\x8f1\xce\x05\x8f\xca\x8a \"~b\x19\x9c\x151)H\xccg%X\x90bF\x18\xc3S\xd3\xa9#\xdd\x16K[\xbbx\x08\xb3\xf4mK\xd9\xdd\xd3\xa5\xdf\x00<\xcf\xd7\x97\xbeZ\x87\xf6\xaa7\xde\xe7*\xff7\xa8c\xd3\x96\xbaC\xb3\xc6\xb5\x88#)\xb9K\xf34\xcc\xfd\xee\x0b\x16\xd1\x98n\x0f\x8a0+8\xd8\xfe\x8a\xbb\x86\xf1Wi\xaf#\xc8\xcai\xde\x9e*m\xae\x16|d\x1aG\xfd\x98\xddP\xab6\xac\\\x83\xb57\xb7\xbb\x1e\xd8\xae\xda\xaa\xa8\xb3u,h\xc3\x9f \x84%\xe5\x0c\xe6\x0e,\x06v`{\xbd\xefNv\xb6^_}\xe7}\x0c\xda\xbf\xb6\x93\x80|&\x11#p\xb8\x0b\xb7]\xd3lH\xe9\x87\xb9+\xf1\xc0\xae\x10I\xeb2\x02\xaag\x12\xee\xdaB\x18s\xe3\xb3\xbe\xc6\xf1\x0e\x9a\x07\x0e \xca\xe4\xef\x04~\x80]\xaf\xb9\xfb\x05\x17\xdbf)%\x03\xd7\x93\xad\xb9\xd6\"\n\x1d\xec\x83K\xda!\xe9H\x87\xca]\xdd\xd5\x8d\xaad\xd5Uk\x18bc\x1bV\x83\x1c\x10F\xae\\\xb3\xb6\xf0d0\x15\x97K\xd9\xf0\x9a\xb7\x8f\\W\x1f\xb6\x9a\xbd\x9a\xf2\x0bB\xe7y\xdc\xab\x9f_-\xb7U\xa6.\x9f\x84U\xc6\x18\xfb-\xc6\xd8\x9bU\x07\x80\xc3\x95\xe5J\xdat/\x8f\x87\xf0\xa8\xb9\xda\xfanh\xbc\xdf\xe8r\xc3oCR\xbc\xe1\x0bB=\x974\xd9\xb8\xbe\xe3\xe5Z\x97f>vGd\xd5}\x1d\xb9\x95\xc8\xab\x12\xb2~[O$\xd5)\xeak \x9e\x0c\xc8\xca,\xf8}\xd4n(U\x1b\x89\xfc\x968\xba\x97\xd0\xab]\xbfY)=d\xd3\xeav}\xa0W\xbe\xd031\x82xS\xb0!\x08g[\x15v\xb5\"\xd4 F\x99D\xeb\xa6\xdcoI\xe2\x1fe\x96\xd5.\xda\x85\xa1P\xcd\xb6r3\xf0(\xed\xcb\xfa\x8cK+\xee#\x1e\xa5!V\x97\x99I\xac.@\x1e\xa5\x1dQ\xdd\x006\xa5\xfbf\xc6\xdc\x99;\x1fn|\xb8\xee\xbe\xceku\xac\x11\xd8\xdd\xaa\xc5Qe\xe7\xd7\x8c\xaeSu\xd0\xe9\x9b\x02\xf9\xa0\xd7\xa3\xae\x0c2\xd3FS\x18\xda\xaf\xb5\x06j\x07o\x13:\x97\xaa6\xe5\x80\x91\x19+\xd1p>'Z\xe4\xd0\xab\xf4\xa1#W\x1f\x03b\x17|\x8ekP\x11\xd5\x9f\xaf5\xe3S\x1f\x04\xcd\xdeU\xe9\x8f\xdc;\x83E\xb2\xfe|m\x85\xb6o\xe7\xb0~\xb6\xfbpnt\xca\x80|\xe4c$%\xb4\xbd\xa5\xa1h\xae\x97#\xeeC\x1fe\x8b\xb3\xbaz\x0f\xc7\xc6\xfbg\xd9\x87\xfa\x8a\xb6\xf7\x94\x92S\x82~\x81*\xc4\\]\x02q\xe5\x01W\xd9G\x83\xee\xcf\xa05\x1a\xe5\xc6\xcc\xa0?\xd1\x89\xc6\x9a\x83\xbc\xd0\xd8\x08\xe5z\xda<\xed\xb7>\x8c\xfd\xc1\x13A\x06\xdf{\x81r\xc6+`N\xab\xf3YEl|5\xaflJ\xb7\xf2d\x0e\"\xf4\xab\xcfH\xf8]\xf4\xcc'\xf7\xa2\x10\x02\xe9\xf0\xd0\x07QZ\xfdD\x06\xce\xb2@=\xc6A1\x8c\xbf\xd32\\G\xe8\xd9\x03\xfb\x08C\xfb \xf6\xed\xff\xd5\xea2\xf4^\xcbZuC\xb9w\x94w\x8c\x1d\xfb\x11TPn\xc8\x9fz6\xee!'\xb1\x0d\x8a\x18\x83\x10F\x95i\x10\x9c\xe2x\x0e\xf3l\x9a\xccJ\xb6<\xf6\x85\xc5\xcb,\x06\xb8\x17yAM>\xd0\xe5\xc3\xfd\x10\xd7{\x92\xe7\xef\x04\xf5\x0b\x94O\xe4\x05\xfd\xf1n\xd8\x9a(e\xcd\xee\x00\xba\x02\xd4\xea\x8f\x9c\x0f\xa3\xdej!t\x1fV\xd8?R\x94\xca\x1cL\nK\x14}P\xe9\xeb}\x90]\xe8\xb0\x11\xff\xea5)\xa6>\x0f\x0c\xf2\x9e\xdd\xd8g\xe9\x83\xbc\xee\xb3\xbe\x1a\x93\xbc'^z\x02{8t\x8aU\xb8\x05^\xd0\xf7\x0eV\xc1\xdb\xdd[\xbb>\x96F\xdc\xd9[\xd6\x01z\xa0\x8a\x0e\xca\x11$\xf7F\x04\x86\x9d\xd9\xdc\x82\xbe\xa6\x07e><\x86\xca\x9ck\x192\xaf\xf0~\x17\x1a\x9f\xf0LST\xb4\x1e\xa93\xbc\xbe>&\xa1\xf1~\x80]ik\x90=J\x8f\xb4j\xef\xd5\xb13\x8e#\x9b\xban\xf7\xe0O\x0e\x95\x1b_\x96U\xb2\xc9&\xa8P\xb4\xeb\xee\xd1\xc2\xa7\xc1-\x98\xb4\xfa\xee\xd1\xd0\xc1\xe0\x86\x0c:\x85U;\x1d\x0dh\xc6)M\xbd\x10\xa3\xfa\xe2\x90\xdeK\x04v\xef\xbbw\xa3JW\xf3|5\xa3\x92\xfcA\x8a \x03\x9b\xb4\xcaW\x8a\x81\x9c\xb0\x14E\xe7\xb89\xb2\x06\x9d,\x15\x9c2y\xc9\xe2\xd8\xc6\x08\xe2\xa4\x1eX\x0b\xa6\xcd\xc3r\xce\xc5\xac\xf8\xf30\x8f\x89q@\xa0\xe3y\xc3\xa5\x9aXq\x93\x11\xca\x03Y\x85JQI\xed\xb6Y\xf7NMi\xb7o^\xb7N,\xf3\x9ec\x99\x1ee^\x1d\xda-\xc2y\xe9)+\xab\x16\xc2@\x13\xa9c\x7f8\x98^'\xb2\xa3\x0c\xab\xe6\x0cf7\xf4{\x1f\xe3.\xbe\xffh\xfe\x19\xdb\xf7\x1b\x01\xa5\xb0\x80\xc7P\x90\xb0\xae\xca\x99\x98\x93\xdc0\x95&\xe5\xf0oD\x83\xbc\xd0\xd5c\xa1\xb8\x07T\x97\xd4\x9ah]\xba\xa1\x0d\x04\xd7y1\xa5N\xa4<\xac\x0c\xb8\x02p/Z\xd7\xc1\x8e}\xd0\xf7\x17\xf2i\xcd\x0e'\xfa>W\xf5\x93k\x1d\xff\x07Hj$\xdanH|\x8d:r\x06\x17<\xdc\xcc\xb1V\x1a\xc5\xf8\xcf\xce\xb6\x08K9\xd9Q\x02\x12\xaa\x11\xa2do\xe0\xd2\xde\x9f\xff\x81*\xa9lRz\x95R\x0d\xb3p\xf2\xaf\xd155\\\xa3\xa0\x99\xb2\xf4\xf1\xd2\xb9\xbd\x1f\x88\xd0\x85\xccU(y^y\x9d\xf7A\xb9T7\xe5#\xaa\xe5\xb5;\xbd\x97@x\xff\x83A\xac\x1a\xaa\xa0x\xa7\xd4\\\x8a\xdf\xb5\x7f\xb11\x1e7\xe5p\x95\x05M\x1f\nl\xcc\x8fP\xaa\x0b\x16!\x8d\xe6\xee\xf6\xffq'\xe1\xd6\xdf\xaf\xd8\x9f\x9d\xad\xd7\x9b\x1f\xb7\x82\xab\xef\xbc\xd1\xb6E\x0b\x97\xbb\xa0HJ\x19\x90\x80\xb1\xed\x1c\x92\xb3V\xd0\xc1\xd6)\xcb/P$\x8a\x14\x92\xef\xd6G\xe7Z\xac\x0f\x1f\x9e\xc33\xe6\x9ar^\xc3\xf6\xc1`h\xd47%\xa2s\x13gN\xe9\x12\xd54)]\x96\x8a\xb7\xac\xe3\xaa$\xf7\x90U\xb7\xdce\xf4\xd4)\x0d\xe9\xdd,zd\x8a\xc7\xa1S\xecF\x19-\x8d\x07\xdb\xe6Rp/z\xdf,M\x96\x03\x02\xcfJqj\xe5\xfa\xd1\xa0\x0b\x93\xa9\xeb\xd8\xc65\x7fm\xf7\xc4\x8c\xd6\xf61\xde#W\xf3> \x97\xda\xb6\xf9\xaf\xb7\x8d#\x8a5\x9c\xf8\xddp8\x98\xcf\xd4\xd7\x92p3\xf3\xa6W\xc2\x92\xd0\xd6+\xe7\xc7\xb9E\x12J\x80\xc7\x8b%\xbdC\xfb\x9f\x8az\xc6\xaf\x12N\xf1\x93\xb4\xa8\x92\x89\x9a\x16\xe0a\x18\xcd\xd5:M\x86S\x82O7\x7f\xc2\xb4\x0bi\x9c\xb5\x0c\x8b\x92\\\xe6\x95U\xd5\xc5\xf8\xf2\xfa\xe2\xf0\xa7\xf1I\xc3\x9c\xfa||q\xf6\xee\xe7\xf1\xd1\xf5\xc5\x87\x1f/\xcf\xc7\xc6oj\xda\xd9\xfb\xf1\xf9\xc1\xe5\xf1\xd9\xe9\xf5\xc9\xf8\xf2\xe0\xfa\xe7\x83w\x1fx\x99\xc3w\xe3\x83s\xf6~\x8c\xf9\xde\x1f\x9c\x1f\x9c\\(_\xce\xc7\xff\xbf\x0f\xe3\x8b\xcbF\xca\xc5\xfb\xb3\xd3\x0b^\xfc\xdd\xd9\x9f\x1aYXoO>\\\x1e\\\x8e\x8fZ\xe9\xedw\xa5\"S\x0fD\xdf\xc7'\xef/\x7f\xe5\xe9\xd7\xc7\xa7\x87\xef>\\\x1c\x9f\x9d\xaa\x19\xf0\x93\x9a\xf0\x9f\x17\xcd\x0c\x1f\xce\xdf\xa9\xaf\x17\xef\xc7\x876\x034\xd8\x83\x1b7s\x9f~\xaf\x93\x9d\xb9\xf8\xf2\xea\xb9\xfe%\x91e\x9e\xe9_B\xf1\xe5\xf9S\xfd\xcbJ\x96\xd9i\x15*\xc5\xa7g\xcf^\xe9\x9f\xd2\xea\xd3k\xfdS$\x9b\xfa\xdek\xd0\x8f\x1c&/\xfaT?%\xb6z\xc7\xe8\x8e\x82,\xd30\"\xee\xf6G\xba=\xf3\xc1\x01\xd0\xf1\x96\xcdkc\xad/\xd6Fsh/q\xdd>\x1f+3g\x8d\xaej\x9e\x1c\xcd\xbd\xf5-\xb6\xf9\xa7\x1d]\x18\xe0\x1c\xe0\x03j\xe9?\xb8\xf5\xdbok\x9d\xa1\x85\xde\xc5\xec\xe9\xc2\xf8\xa1]\xe0\x06\xf6\x88\x13\xcd\xbc\xb8! bO_>w\xf4\xc5\xcc\xa9q\x95?\x8b\x86\x9e8P,\xf7?x\xb4\x9f\x86\x0b2\x02K\xf0\xa8%?\n\xac*\x85I\xf9\x97E\xaa[\xfd\x00\x0crL\x80\xf3\xd6)\x89\xb4\x1b\x9b\xfe\x8b\xa6\x0f\x87o\x9d\x1c1\xb9\xddSS\xdcsjR\x12\x16?\xeb\xa7\xed\x83A\xfb\xf8A\xf3q\"\x14D\xdbj\x1c\x03\x96U\x9av\xa1\x91a\x1f)\xdb\xd3\xfd\xbf>\xa8\xfb}\xbb\xc1\xb2\x9c\x9f\xc8\xdd\x08tS\xbd\x87\xcc\x80\xb4\x1d\xfb\x1f:\x03\x1a\x1f{\xcf\x19`\xf0\xab\x10\x96\xdf2\xf6\xcb\xc7\x1d\xbbT{\xbe\x87\x0f\x10eD\x92r\xfe\x96\x01\x9d\xfc\xb7\x18PI\xe8}\xd9[\xdb\x80\x8e\xee= \xce\x9ew \\6^\x0bx\xca\xf1\x1ad\xc3\xb6\xf16\x89\xd9iEd\xbe4\xd9\xa5e\xaen\xd1\x19W\x05Z\xf4\xe5\\|\xda}\xd9\xfa\xb4\x96Ti\x9b\xcc]\x88O/_\xb4\xc8\xdcY\xf5\xa9Ej\xdfI\xc3R\x13\x93{c=\x14dh\x1e\xd51\x04\xe9v\x0ca%w\x1a\xf3xm`\x1e\xd0\x14Q\xfa\x9fA;\xc8\xe6\x18n\xdb\xfcG\xa3\xc8\xaaH\xb5\x12c\x03\x07\xd3(\xc2\x95\xa8\x1be>\x9b\xd8\xa0F!<\xd2\xb5R\x83\xb8\xabF-\x84\xf1\xc9\xbc\xae\xfa\xfaF\xab\xf5\xd0\xc2\xc7\xf1\x8a$\xf3l\xec\xd0'\x13O\xc8\xcb\x95\x84^\xcb\x8bt\xad\xd4\x81\x81\xb3T\x0b!\n\xd3\xca\x9cup\xa9uYq\xe9m\xa9\xe3\xbd\x81\xf3\xe5e\xd3|f)ca\xa0y1D\xb9\xb6Q\x9e\x18\x99\xf1fAS\x8b\xc7\x9d\xec\xbdZ\xbesi\xfe:@\x8a\xd0\x00\x95J\xccz\xbd 4\x14\x87j\xb3\xceS\x8b\xb4\xa2QOm\xde\xda({\xde#\x051\xd6q]r\x81\x8bV\xd7Q\x05\x0c\x95\x80\xc5a\xcb/e\xaa\x8d\xcc\xef\x86\xaa\xb8\xb9;>\xba\xa8\x16R\xc5J\xdc\xa6\x9bH\xab\\zS\xe8\xd3K\xfeV\x19:\xad9\xb8\xc5\xe7\x01\xe6,\xcdGLQe\x937J\x96\x8c\xdc\x99\x10)\x8a\xce\xea\xf8\x95\x9c027g \x85{R\x83\x1c\xd4\x1a\x16\x10\xc3@\xc0\x97/\x90\xb8\x18\xb0\n\xc1\xb6C\x87\xabD\x0bqF\xda\xb1i-\xda$\x1d{\xbez\"h\x91\\\xaa\xa0\x0c\xa7\xe4]\x1e\xc6\xc6h]j4=\xf3T\xf2\xa5a\xf4t\x9e\x8aX\xfb\xe8\xf1-\x0f2r\xcbx\xf6qq\x9fN\x9b\xa7\x8f=)Y\x93t\x042\xa0\x935\xdf\x82\x94e8c\xc4GP\x90\xb0\xcc;\xcc\xe4\xd2$\xc3|\x8b\xb0\xf8\xc4OQ\xf6+`\xc9\xa8\xdb[\xbfmb\xe4 .:\xb3\xcck{\xf2l[\x05\x03\x1d)\xde6\xf7\xc0Uba\x85\xb0\x0f\xce*\xe3\"et\xf2\xc1\xb6VTo\xad\xd0\xe3&\xe0M\xd1\x88\x1bz\xec\xd0\x1fH#}0\xc4\x95\xfb[\xa5\xbf\xa5Hf; a0\xecM\xab\x86d\xe5\x85\xa8\x7f\x7fBus6`\x8f\x82t\x83\xde\xbbO\xa1\xf2\xff2\xed\x00\x8a\x15\xecA\x18L \x8d\xe6\xf6L%f\x12S\xd5\x01`\x98\xed\xe0\xc2\xc0\xe3\xc8'\xaaD\xb2\xb8\xfa)\xec\xc3?\xbe\xc2\x08R{\x91\xa9\xbcT\x14:\xc2f\xb5\xa0\x0fh, 7\xe6mXd\xdc\x91\x84\x98\xa2\xc6:7\xc2tB\x99d\x11\x81\xf5\xb3`w'\xd8\x810\x8b\xe16IS\xb8!P\x90E\xbe&1$\x19\xac\x9f\x07;\xc1\xce\x1bX\x95\x04,r~\x11\xd0s\xc3\xf1|\x0ep\xb6XW\x0c4\x18i>\xedRv\x8e10\xd9\"\x8fI*/ZN\xc2\xa8\xe8\x88*5\xc7\x12\xd5\xcdVO\xee5\xe6\x16C9\xce()\"\xb2\xa4y\x87R\xf5B\x94\xe0\x04\x8cR\xc42\xcaz\x95\xeb8?y\xe5i\xc1\xad\x9dG\xf0\xfb\xf6\xca%x\x1e\xac\x8a\xd4\xaa\xfe\xc5&\x8fq\x15\x11\x83\x88wIFNW\x8b\x1bR\xbc\xcd\x0b\xb4\xcf\xdb\xb7}h\x86\xdd0\x84\xc2\x90\xcf]\xd5\xcd\x0bZ\xd8\\w\xcb\x1b\xb7\x0eT\x8f[\xca\xe8cH>\xac\x8dN3\xe4\x9b\xb0$Gyd\xe5\x1dA\xb8\x00mB\xc8\x08b{\xf6&x\x8c\xa0c\xd3\xb7ac\x04\xeb\xae\xec-\xc0\x18\xc1\xc2\x98\xfd\xab\x17\xd09\xc9\x06\xe8WA\xe3\x8e\x95M\x98\xbd\x03\xec\xe1\xf6\xad\xfc\x1a\xd6\xae*\x9eL\xc1Mz \x0c\xa8$\x02\x0e\xba\xf3\xcf\xcc$\x06\x082\xa3y\xfb\x9f\xe1\x1do\xa6(\xd6t\x0d\x11T\xe5\xbc\x81\xda\x9a\xeac%K\x08?\xcf\xd9\xa4LWi*\xb6\xc8\xcc\xbd\xf3\x95\x14i\x15\xc0\xd2\x96\xdc\xc8\xb5\x91\xbd~ \xfe\x9a'\x99\xeb\x04\x8eZ\x04)\x15FU\xcb\xd8\x93$\xa0\xdcE\x9b\x9c7\x1f\xb5s\x84\x8b iu\xccr\x9a\xef\x93\x89\x0f\x8e kz\xa3?\xcb\xa7\x11\xcf\xaa#\x10\xa8\xfa\x08\xb9! Dc\xbd\x85\x86X\x01\xda\xa1\x8e= #\x13/qV\xc6E\xf1#j\x99\xe4\xdf`9XhWfvS\xaaVr\xcb\xfc`r\xa5\x1dGo\x85>\xda\xa2&\xc6\xd8kZ\xbf\x96\x15Y\xcdh\xc7\nh\x81X\x03\xdfQ5b\xa8\x0f!\x0f\x80\xe2C\xec\xc3\xdc\x87\xb5\x0f\x0b\x1f*k\xdf[\x1f\xc6V\x85\xa1\xba\xed\xdbb\xd0\x86\xc1p\x0bo\xdexP\xde&\x9c\xca\x0f\x96\x05F\xfc\xe2\xc1\xd0\xbb6Z\x14\x96\x04vF\xddk;\xe5\xe7\xd7\xdf\x82\xf2\xae\xa4d1d\xe3\x12\x19\x8c\xf1y7\xdc\xb0\xe7\xa6 a;\x92\x9a\xfa\xd8\xc1\x05lH\xc2\x89\xc9\x8d\x00\x1e\xe9\x05`\x04q\x9e\xfd\x9e\xc2<\\\x13\x08\x81\x0f\x06h.\x0c`\x08\xe4\x99\x0f\xe1M^\xd0$\x9b\x05\xdcaQxS\xac\x96h\xe2\xc1\xda\xb0\x05\x07\x069\x93\xcf\xfbg2\xd3yQ\xc1\xc6\x92\xa2\xa8)d\xc1\xb1N3\x1fi\xe2\xbc\xa2\xf2\xf8P8\xef\x97#E\xaaS\x9e\xa1\xa4\xfc\xade\xee9\x04\x94\xd6\"R\xe8`\xacK\x0dw\xf3\xb6\x87U\x1eb\xe8\xd4\x14\x91\xf0\x12\x91\xf0\xa2\x1fh\xe1\x1bp\xb0\xe9\xf9\x16\xbclz\x86\xe0j\xd3S)\x14\x8au{\xeaw\x99\x1b\x9a\x1el\xf9\xe9\x83[\x0e9\x91K2\xea\x0b\xb6\xbc \xe5*\xa5'\xe1\xd2\x17\xbc5\x83\xf2_\x12:?\xe4\x0e=%\xcaV\xa0\xed\xa5\x0f\x89\x9b\xe2\xf9z\xbfi\x93O\xc5tL9\x1f6\x8c\x96\xd2\x1f\x13[r\xf7\xb0\xaat\x96\xe5\xe6a\xd5\x98\xd8\x19\x83\xa2\xd2\x90\xc7\xc8\xea\xdc\xde\xbb\xaa>bQ\x7f\x10\xbc^>\x18\xbc\"\x05\xbc\x96\x88x9\x9f\xc4\x8f\xba\x88sWP\x04a\x9a\xe2 R\xba\x1e\xf7f\x86\x8c\xcc\x10n\xc9\xf6\x0c\xe4\xa2lO\x9b\xbbZ\"w\xb5\xd4\xcc\x16\\.\xa1\xb8?\xfbdz*l`b\xa0\xe6\xee\xfa\x7f\x1b\x03ez\x1e\xc2T\x99\x9e{3Z\xa6\xa7\x9f\xf92=\xa8Pm`\xba\x16\xd2\xbd\xf6\xac>WW\x885\xe3\xf6\x87\xb4\xfa\xd0\xa2\x83\x1e:\xbd\x15f\xef\x94\x10u=\x96\xa3`\x04\xf6\x08\xf0\xb6\xe7A\x88h\xf7\xfb\xfba\",\xe4\x90,v\xeeW\x0e\xd4\xcdX\xd2|i\xf1\x91cz\xba\xa9g\xf9|\xc5\xe8\xf1&G\xb6\xc6\xdc6\xc9\xa4\xfa\xb4\xae\xf0z|)\xa8O5Xs\xd0\xcf\xde:\xba\x07\xfd\x95Q\xc3\xab\x8an\x13\xb8d\x00bW \xd6\x9d\x9a\x9c\x0d\xbb\x93\xab\xcac\xcfR\x9a\xd0\x074\xff\xcf\x8b!D\x84\x15\x9c\xa7\x8a\xc8X\xd4\xd6=\xc0\xae\xf5\xe1\x90\xdb\xc3~\x8e\x95\x83\x92{-\xafxz\x1f\xaf\x8dx0\x10I&>\xed\x06\x07\xe4\xf1\xfaz\xf4\xba\xbbG5c\xf1\x1aO\x87\x1d\xec!^V\xba\xbb\xbb\x9e\xafK\xfe\x02j\xbb{\x80\x8aL\xed\xa1Sc\xb3\xa1\x83\xcb\xc6>\xae \xd3\xdef\x9e\xd9\x9b\x19\x8a\x11\x86\xec\xfe6\xd0\xab\xbb\xda\x87\x89\xb1\xd4\x841j\xbb\xaf\xafZ\x1f\xaf\xda\x0e2\xe0\xd9\xf7\x0d\x9d{\xab\xb5\xc77^\xec\xffM\xc6\xc1\xf4+\xa8\x03\x0cC\xfaV\xf7LX\xbd}m\xdb\x02\xdc\xd3\x11x\x8fJ\xdcy{\xff~\x8b\x8e\x9fT\xd8l\xaf\x99m\x80\xfe\x10\xdb\x1c+o\xfdO\x1a\xdd\xc4\xe2\xc0F\x0cO\xc5\x83\xf7\x1bi\xcb0\xe9[\xd6\xee\xf0A\xa3\xab\xb4\xa5\xcdC\xe4.\xc1\xef\xbd\x84]\xf6X\xdf\xae'\x7f\xf1\xcf\x18\xe9#\x98\x13\xf0\xb058\xea\x9f\x85\xe9\xc2\xf0iS\xb7v\xd3\xbc\xed\xc1j\xae\x03&\xa5_=\xd7\xfc\xb9`'\xb6\xc9\xcd\x81e\xc9>uAK\xc3\xb8\xef\xbf\xe7h\xffv\xaf\xd1\x1e\xf4\x8c\xb6e\xe0\xf8\xbfa\xd0g]\x83n\x18y\xf6\x1e\x9c\x1d\xe34\x8c\x857\xff\xbe\xab\xf9\x96\xd9io\x17\x86*\xe5\xd9Tn\x8aa*{\xf9P\x95\xbd\x95&\xeb6\xe7\x12\xf1\x06\xc3\xf2YOu)\x12\x96\x0c<\x18\xca3\xe7\xe1r$qW`\xcc1\xc5\x1c\x95\x8e\xa8\x05m\xc2\x1e\xacl\x9c\xc1\xfd\xb4S\xac\x9a)\xe6\xec3\xbc0\xe0\xacD\x9b|M\xa6\xe0\xce\xe0\xc9\x13\x98)\xa1\xc7\xf4w)y\xd2\x93\x85{\xd2~\xf1\x93\xa4iY\x0d\x1bBK\x86{\xc7\xaa\xcf\x89\xf6\x1e3\x98\xa5w\xc6\x0b\xcf;\x1d\x07\xb9\x93\xd4\x87\xe8\x8am\x84\x8c\xad6\xd2X^\x17\x9bJ\xd4)\xd9k\xbe~\xf9b\x8d\x1f\x00\xca\xd6P\xcbLx\xc3\x1d\x1e\x0c\xdd\x0dt\x0e\x8e\xa1\xfcv\x84\x8b\xa52\xf9;w\xda\xe1\x9a\xea\x82=p\x0c\xbe\x97\xc0\xcc#\xa0H\x07\x83\xc8}\xa6\x1f\xaa\xc8Lq-\xfa\x91\xcaH\x01\xcd/\xd0\x12\x96\xb1\xcf\x02<*\x00?\x8eQ\xc8\xa7\xbe\xefi\xdfG\xbcP\xca\xfeD\xa2\xf3\xcd\xfcY\x90/\x8fcw\xc6\xefc<\xd4)\xe5d\x96k]\x136\xa97\xb0\x07)l\x823r`\x13\"\xf3\\2v\xb6\xe0\xb1>\xca\xa0D\x1c@\xe2\x0bLro\x90ko%w\xe8_]\x8bjX\xbe\x9f\xc3\" oR\xd2\xa5\n\x05\x18,\x9d\xe5\x1eU=\xe9\x96\x08\xb0\xa5,\x97aDFpc\xcd\xf8\xb5_\xbap\xfb\x08=\xedo\xbf{\xce\xabv+\xf7>\x15t]{\x12\x91\xec\xc35\x8c\xe0\xd6G5^=R\x1d\x0e\xa2\x9d\xec\"\xa0\xf0\"\xad\xa8u\xa2L+\x9d\x17B\x87!\xdfm\x7f\xe7\xd8\x17y\xac\xb6\xfac\x1es\x9c\xc4\x8b\x9bK\xb1\xc1\xdd\x05I\xf9\x9f\x17g\xa7\\0\xed\xb9cT\x8cW\xab\x81=`\x19\xb86\xbc;\xf6F0f\xfba\x8csi\xc8<\x16\x93\x0c\xa3\xf6\xa7\xf6\x86n\xa5\xb0\xa1|\x163\xaf\xb8\x01\xf9\x07z\xe6m\x8f\xe33\xee\xc4\x9bU\x92J2\xcc\xfd\xec\xf9P(\xc4\xa8\xab\x1c\x90\xf5A\x08\x9f\x0d\xb5\x11\xc3\x11\xa6R\x19\xbd\xfeq\xd7\x0d!\xe0\x84\xea*:\xea\x93\x9bG\x99u\xab0\x16m\xc2\xd32\xc0\xbc\xe1\x9bD>_U\xf8k\x0e\xd3p\x97\xcc\xc6u\x01{p\x14R\x12d\xf9mG\xa8\x9bLRg.\xd1\xd5\x05\xad\xd3F\x83x\xc5Qj\xa3\x0d\xd8\x82\x8bj\x0dyO-c4\xa8O}\xf5\x84\xa0\xad\xbfyuJ{\x1a\xea8c\xb9\xf6F\xd7}\x0b)\n.^\x98\xab~m\xccg\x9ei@\x8d$\x0b\xafI\xdan{\xf4aK\xf5\x04\x83\xa3\xaf\x1d\xab\xa3\xaf\x9d\xa6\xa3\xaf\x9d+T\xe37P\xef\x15%\xda\xfe\x96uR\xa0\x89\xd8\x07\xb9b\x9e\xc3}\xfeP\x0c1\xc9\xcb9Wf\x1fi\xdd\xa4\x9bT\xd2$\xc14\xebR\x9a\x0f+}\xd5\x01\xf4;\xe9\xe7\x07\xca\xea\xf6\xdf\x16\xa5\xce\xed>\x0c\xb9\xfa\x80\xe6\x1d\x8b_K\xd8\xa9\xfc\xb0\x1d_W8x\xednl\x8a\xf7\xc9\xed\x03\xcb\xce\x08D\xa6\xa3\xca\x9c\x9d\xd1J\xdb\x9f\x17\xe9v\x12P\x86\xac\xa6\x96N\xccq\x00\x15\x81\xd8\xe8\xbe\x0f\xb1\xfd\xec\x16\x80\xb0\xd2\xb8C\xd4},\x9a\xb85\xb1md\xa1\xfcm\xd1\xbf\xe7\x8a\xdf\x96\xa5\x96\xd8\xa2\xdfb\xd8V^\x92\xc4V\xednS,\xdc\xa9\xa5\xab\xc2\xb4\xd9b\x9fa\x0c\x97\xbb4\xa0\x1c+\xce\xc1_=\xce\xa8H@>/\xf3\x02\xfd>7\xe7\xbb\xb2\xf1\xcd\xdc\x97\xcf\x9ej\x90P\xdb\x087\xbdO\x19\x9b\xb4\xb57@,\x89\x91]\\n\x00\x12f\x11\xbaUD\nKA\x80\xe8\x11\xb4\x80$\x03\xe2\x01\xde\xea\x03\x9b,T\xb4p\xd1\x1f\xeb\x08\x92,\xca\x8b\x82D\x14\x92l\x9ds\x07x\x1b\x16W\x8e\xe4~3hv\xe7U\xd9(\xb9\xaf\x9f+\xcdT\xc3\x0f\xa6CD\"\x19\xb9\x1d\x805Y\x8f\xda{\x8d\xd15\xc1\xb2\xc8\x17 \x8a4YUdX\x9096\xe9\xca\xfcRm\xbe\xb3\xf6,;?\x861\xbc\x17mEyV\xd2b\xc50\xb3M\x97\x11O \x1f\x0f\x1b\x83\xbc\xd6\xf3y\xe7\xc5\x05*\xcb\x84\xbe\xe5D\"\xa3~1M\x0b.\xf3U\xb5;\x1c\xb4t\xf5\"}\xbfcZ\xa4\x01bB\xd4\xb0\xe3GW\x921\xd8D~\x9aLrv\x16\xe3\xbf=\xa0\xec\xdf\x08\nVG\xee\xe3\xeb\xbf\x04\xf2^>\xdf\xb5\x8c\xaax\x8c\xea_\xbd\xb0\xd4\xce@M\xd7g\"\x9f\x97i\x12%t\x04\x13\xd6\xb1\xe7\x8c\xe0u_>\xff^\xfc\x7f\xe1\xa9\xdeP\x1f\xde\xbb\x0eJR\x99\x97\x17\xbb\x167\x93\xec\x9b\x8e\xea@\xd0=\x9a\xc7\xca`s\xeb\xea\xbb\x91\xb7\xef~\xdc\xfe\xb8\xed\xed\xbb\x93\x8f\x17\x1fK\x0c\xc9\xd9.\x1eb\xf1\xc9\xc1\xd6\xff\x1f+\xe0\xffw\xb6^on\x05W\xdf\x8dX\x05\xdb\xedB\x8c|\xb1\\\xad:\xff\x86\x9e#\xc3r\xae\x87\xf3\xae\xb3\xec\xb3,\x7f[\x91\xe2\xce\x9eg[\xfatDG\xca\xd6l\x7fd\xd9\xc2\x15\x92x\xbb\xb6\\\xa7\xe1)\xeb\x13\x8fH.\xaf\x86w;\nl\x8f\xdc\x8f\xf1\xa6\xf7\xef\xdb\x18\xc8\xbch\x14\xebo\x04{\xac5\xd4*c\xa8\xa6}\xce\xc9\x87M\xe7\x08v\xcd-\xe3D\x8e`\xb7\xf5Q\xf5# \xaa\x9b\x8d\xd4\x8e\xaf3\xaepo\xb3\x94C\x015\xfa\x83s+\xc3m\x1a\xa4\xe2\xd4\xe2\xc2@\x8bp\xd5\xb9I\xf3\x9b\x91#d\x9e\xcb\"\xa7y\x94\xa7\x1e\x87{v\x96\xb8\xab\x8c\x94Q\xb8\x94\xbc\x13\x9bF\xcf7WH\xd2\x92\xe8\x8e\xea\xf6t\xf7\xd8\xf2A<\x981\x1cX\xb7E\xb0b\x1fJO\xeaz\x14\x93\xcc \x91\xac\x1bR-\x99\xad\xda\xd6uS\x84\xa1\xdb$\x03\x94\x90\xba\xacr6_\x93LG\xaf\xf2Ql\x14\x8a\xa0L\xc3rNP\xfc\xec\xd6o\x8c\xb0\xa5\x9cQ\x9f\x17dj\x8a\xfa\xd3J\x91\xbc\xe9\xef\x9a\xd9\xccp\x11u{;\xad\x02\xfaZ\x89g\xf3\xa4\xc8\xb5\x1e\x01\xe5\x0e\x9f\xd9\xbf\x80\xe6\xef\xf2[R\x1c\x86%A)\x8fc\xb1v\x17\xa3\x1f\xc1\xc6\x06\x9d<\xb5\xec\xbe\x82\x94\x94U\xff\xac\xbd\xd1\xf4+V\xf3\xd0\xa7\xb6C\x14*J\x8f\x1d\xf1*\xb17\xad\xbdPW0E\xcd\x82\x176\x83\xdc\xec\xa9\x94\x1a\xf7sn\xc1\xb0\x12\xc1\x91-\xdc\xcc\x02j\x97\xdd\xe6\x1c3\x96c\x9eX\xb8\x8a;\xd8\x83\x9dv\x7f\x10L+\x88f\x84\xd3\x02\xad\xf5\xe5f\xaaR\xb8=\x8e\x8f\xcb\xcf\x1d@s\"B \xfe\xb3Q\xf50\xabJ\xe4\\\xcc\xe7\xf1\x82)RH\xec\x9c\xdap\xd9q\x13\xb9\x84{.\xf6\xbc\n\x0f\xe0\x85H(A\xdd\x87Y\x03\xea\xe5\xef/_ \xe1\x1eu\x95\x8cU\x15\xc8\xf8\xc9\x17DL\xea\x9b\xe3\xf8\\l\xc1h7\xea7ku\xd7\x93\xa7l\x83N\xb6\xdd\xe0;o\xbbq\xf4xo\xe0\x0e~\x80\xb5\x10s\xbc\x81\xbb\xcdM\x0f\x91\xb5\xcbx\xd8\xf5\xe4\xee\xca\x9b\xec\\\xf9\xdc\x12{\xb2{\xe5C\xc9f\xa5\x84}\x98M\xe6\xb8\xef\x19|\xb7]j\xb2\x1c\xff\x8f\x1b\xa3,@\xfaX.=~\xc9\xe1dh\xfe\xa2f_\xb2>\xee\x83++\x15\xa0\xb3#tT\x95\xa4\x1861\xb7\x87A\x87\xb5\xfczf,\xcfs\xc6(\xfc\x15\xbb\x9c\xf7C\x14\x8eq\\z1\xdek\xcf\xf3\xe5@\xf1\x9f\\\xa5\xe5\xe4\xd9\x15\xae\x96Hd+\xb0\x9c<\xbfR\xebe\xff\x9a\xa8\xc0\xb0}8`\xcd\x02<\xe9\x90\x14\x12\xbf=\x84+\x15 @\xf1c?\xab\x8e\x91 \x9a\x87\xc5\x01uw\xc4\xdc\xea\xdfy\xef8GQ\x9f=\xa2\xd5*\xd3\x00?\x11\xa0\x92\xdd\x18\xe9\x0c9\x14g\xdb\xf1\x82r\x99&\xd4\xe5?\xe5\x0cn\xedz\xd2a5Q2x\xbep\"\xc1A\x8e\x1b\xbce\x93\x02\xb6\x18\xfd\xc1\xb7\xd2.7s\xdby\x03\xc5\xd6\xd6\x1b\x0f#{\xe0M\xd9\xa4\xb8B\xcf\x19\xac\xba\x08#\x13\xec\"~\x0d\x9a\x19\xdcf\x0e\x1fB\x06\xd6#\xee\xb7\xc3\xdd\xa9\x03Z\xb8 \xf7j\xe0C\xab\xc4\xd6V\xb7\x94\x19\xd7&\x0bVY9O\xa6\xd4u\x1c\xcf\xc7~\xb2\x89\xceq\xa9\x82\xea\xed\xcb\x17\xc8\xb8\x0e\x1cf\xcb\x84\xce\xfc\xb6)\xa2\x8a\xb2*\xbe\xbabl\xde\xd8\xb7\xbc\xa0*f\xe0\xfa\xa93\x19a\x97\xff\xe0\x85yf~{\xc8\xdeV%)\xc4b\xb36\xca\xf26/b\xfc\xcc\xbe2B\x13\xa7d\x89\xdf\xd9\xab\\\xb5Q\xab\xfcr\xb2S\x81}\xa3.\x86#\x04\x02d_\xf2\"\x99%\x19oP\xc1\x86\xa2\xbb\x88l\x93\x94\x8c*\x98\x95y\xf6\xd5\x97Mp\xb6\xb7\x1d\xd8\x94\xc5F\xe00|\x8dM3b\x01\xab\xaf/3\xb53Q}\x9b\xf2J\x85)B\x1b\xc4KBG\xbd\xac\xa7|\xf0\xe0\x13'\x94\x19R*\xeb\xaf\xae\x0bh\xae2\xca9\x86n\xa5\xd1\xdeX\x17\xd2\xdd\x84\x8b\xd4\xaa<\xa8x\xa0\x85d\x82\x17\xc9=\xe6_C4{9\xd7\xd0c\xee*Zc0K}H\x14p\xdd\x17~1\x12 \xb2I\x05\xb2\xd5\x95/\x0f(o\xc8Q\x8d\xc3\xe92\xd7\x84\xa1#\xa98\x9a\xa1\xa3I\xf8\x96\xe2\x13\xbd\xb9'\xba\xcbS\xd9$\xcb\x1e?\xc64#O7\xb4c\xdb\xa3\x8f\xf1\xe6\xbfos\x1a\x9a\xb2Yv\x85\xffxe\x0b'\x12!\xd0`\x99/\xdd\xaa\xc3bSS\x81\x96F\x8e\xa7\xcc\xbf\xfc\xa8\x14\x7f\x9c\xc9\x97 \xd17F\x95\x08\xa2\xcd\xf3\x94\xf5\xa9\xa6\xa56z\xa2N\x0f\xeb\x95\xa4\x8d\xfa\x94\xbcQ\x0c\xd0o\xf4=\xc8\xd6\x13\x0dW\xd9\xc4V\xad\x0b'3\xfbx\xe0\x8f\xc0\xf97\xcb\xb5\xb6\xfaHhP(\x82\x0da\x16\x1e\xb2M\x05&\xe5V\xf5\xf9*X\xc2\xc7@\x15R\x8c=\x08~\x8d\x99\xccF\x1f\x15\x05Rr\x02\xa1\x84\x1f`U\x91\xaf%;\xe7\xed\xf3\xcd\xca10ZM\xca\x0e\x0d\x9dT\xd2q\xc9$\x9d\xec^\xb1\x1e\x8a_\x1a5w\x8fnK\xa2\xa1>\x11\x93\xc6\x89\x98\x18O\xc4D=\x11\x13\xc3\x89\x98\xe8'b\"O\xc4\xa4\xa1\xde\xd3\x0e\xeei\xba\x9f\x14\x05F=\xb2o@\xd7vMNI\xf1\xa5\x8f\x04\x89\xf0\x8c\x84\xf5%\xd3\xbb\x0e\xcd\x1b\xca\xe5\xd1v>\x0f@\xc6\xc9\x95\xe3\xb7\xd0e\xd8%1s\x85\xdc\x04\x85<\x1c\xb7\x18\xa9\x88B\x07\x81\xb8;\xfa\xc4\xe3\xb4n\"\x1d)\xd0\xcb>\x9f\xf2\x91\x1d\xf9U\x97\xfc\x15\x9d\xc4 \xcc\xcd=%\x8d\x11\x7f\x15\xb9T}\xe7\xc7H\xfd\x05I\x7f\x96\xfeGG\xfe\xcc\xf8J\xf3\\\x92\x10\xcf\x87\x8d4X\xa6\xabY\x92\x95\x93\xec\xaa\x0biR\xb9\x86\xe35\xc9h)\xeby)\xeaQ\xab\xe9>5\xe4)G\x03\xb2\x167\xab\x1d\x1e\xad\x14D\x9fd\x10z\xb0r\xc3Iy\x85\xeb\\z\xb2\x17\xaf\x1c\x94;\x19<_\x82\x11\x17\xab\xd7\xb4\xed\x95\\\xd9h\xfe\x94w\xf94\\\x90\xa3\xa4\\\x864\x9a\x0b\xedd\xb6\x19\xcen\xb3\xcaP\x99{\xc9b]{\xed\xa0*BGY!8m\xceA\xad\x8f\xb1\x9c\x87%\x89\xcf\xc9,))\xd7q`uhS\xc6A\xcd\xb0|\xd5\xfc%l\xfe\xacR]\xaeS\xab\x0d\"\xf1<(\xdd|\x92\\\x89\xe9\xe8\xd9\xe9P\xa3?=\xae\xed\xefLy6HPh\xc3B\xfcR\xba\xed\x0f\xa2\x07>c\xd3;\x17\xaf\xb4/\x9e^'\xbfB/\x19\xf5\xc1\x17kwg\xa7\x02\xe7\x8e\xccH\x06\xb7s\x1c\x91%\xc9b\x92EI\x95M\x01\xf1Iv\x15\xc4J\x0ee\x10\xf2\x97\xa4K\x9a\xfd\x16\xfb\xaam\x95e\x83\xa7\xb6\xda\x91e,\xfd\x19\xd5!\xb5s/\xf3\xb2LnR\xd2\x82M\xe1\x01\xa0 \xa1\x19;\x9e\x10y\xbc\xc7\x11a\x8c\xc9>\"#\xafVf\x97\x9d\x81u0\xba\x8a\x83\xe7\x92&~0\xb0\x95\x0bu\xd6\xbf\xa7\x1b\xe5\x8fw\\)e\xc0M?\n\xa5,\xb2f.\x0e\xc3k\x11\xeb\x0e#m4\xd1G\xa7\xe6\xe2N\xc5\x8e!\x133\xeeI\x10\xadH\xb9\x93\x8b\xafr.\x9f\n\x9c\xc4\xf3\xe0\xad8\x17\x80\x0dD\x9fH\xa1\xf6L\xf4\x8c\x88 \xe6\xc0\xf66/p\xd2\x87\xce3 \xe2\x06T\xb7\xc7\x8flUk\x13V\x17\x16\xf6\x1d\xdc.\x84\xb2*\xb3[g]\x1b\xc3\x86\x8e\xbbNqn83\x08\x8f\xcb\xa7\x02)\xd4\xac1`^\xf9\xe0\xc9\xaeC@\xd1 V\xa0\x80\x96}\x96\xb2Iq\xd5\x01uP\x1f:b\xc2\xdbQ\x85\xe4\xd3u\xfe\xcaG\x92\xcd\xab4\xed\x82\xaa\xeb\x82\x94\xa4\xb1}Gv5Nh\x11[\xb9\xb8\xe4A\x8fg\xad\x8d\xc3\xe5\xe1\xe2\xb2\x94\x91]\xed\xe1Wd\x8e\xe4'\x8c\x97O\x12\x88\xedg~\x1f\x12\xa1\x1e\x0f\x9e\xdb\xde\xd7\xa2{\xd4\x88\x13$Yk]\xd6\x8evC\xbc>\xf6\xa0\xd0\xdb\x0d\xd5v\x8bI\xd8\xbc\x804j\xd9\xaa\xf4;_\xcf\x87S\xe9\xdc\xa3\xa2\x99VG/\xd0\xee\xd3\xdd\xa7\n\xdd+Hw\xf7\xb51\xfe\xc6\xaaC\xdd\xad\xa6\xb9P4\xfc\xe5\x0b8\xab\xecS\x96\xdff[\xb8\x8e\x9a\xf0\x85\x04\x11w\xe9p\x19\x163B\xf1biF\xe8i\x1e\x93\xb7E\xbe8\x16\xf7\xa8n\x81\x97\x84\xfb\x10\x06I\xb6\xce?\x91?\xad\xc2\"&\xf1a\x98\xa67a\xf4 }Cp\x7f\x99\xd8-\x82W\x14\xe6\xbcU\x16\xdf\xd0zc\xef4\xa9\x8a\xb6\xdeER\x8e\xb38)\xe7}\xf8X\xecK\x87\xe6\xcb\x93|U\x92\x0fK)\x94b\xd3C\xf3\xe5e\xbe\x8a\xe6\xe3,6%\x1f\xb2\xf1\xa7\xe2K\xd7\xb6N\xca\x93|M\x1e\xd0\x1dV\xcc\xd4\xb2\x92\xde\xdd\xee\x05\x0d\x0b\xfa\x80\x86\x8f\xf2\xdb\xcc\xd40\xd67\xa0e\xa1\x82{\x94\x14$\xa2\x129\xf4u\xa2>\x1c\xaf\xe5\xe9\xf8.))\xc9\x88M\x0b;k\xe6\x960i\xc0\x03M?T\x94\xd3\x10\x8cXx\xe6\x18\xa1\x8dA\xb4\x19\xde3\xcf\x18\x18\x18\x14\xfc\xc4\nS\x97\xd83J\x95<#\x90\xfb\xc6 0}\xac\xc6[},\x06-\n/M\xca\xe36\x95j\xb9\x16]WV\x80C\x97\xa6\x18\xbc4\xec\x9c\xd5\x9d0w\xe8\x01I4\xb6\xf3\x06r\xf8\xa1v\xd5\xfc\xe4 l\x90 )\x19b\x0fg\\[\x9e\xe6\xcb%\x89]\xef\x0d\xe4\x9b\x9b^\x8d\x1d'\xf9\x95\x0fE[U\x12\xa4\xc2\x10^X7\x90\xa9!\xe3\x03W\xe9!K\xc4Fr@/\x8b\xd5`J\xbe_\xbay\xff\xed\x06\xf7\xdar`\\[\xdaI\xbc)\x84!\xbf\x19\x87\x1f\x1a7\x7f\x1d+\\lnv;\x18B\x8azR\\\xb1Ue\xe4\x9f\xa2\xfd3)\xdajG\xa0\xdc\x15\xa0\x87\xe0'O\xd8\xa6\xe6\xc1\xb3e\xc1n!\xa9\xbe\xd8Xe\x97\xfaU\xe7\xde\xee\x847\xda\x05U\xf3\xb0\xac!\xaa\x0f\x80\x14\xf1E\xbb\xbd\xaeV0\x9e7\xef4C\x98\x0cq\x0el\xab\x08\x0ce\xf5@/\xed\xd6t\xd4|\x9f\xd6Zh\xbd\xbb\xb5\xa4<`k\x81\x0e#{\x91\xa5\xe4\x18\x82\xba\x14\xcf\xdb3\x9ew\xf9-Zw,\x16y\xf6\x90\xe6,U\x0cj\xfb}\xc8\xce\xa1{\xce$6\xd9,\xd93\x8f\xb4\x08\xd7\xa4(\xc9\xe5m\xfe\x9e1\x8c\xc3\x14\x11\xaa\xe6\xf4\xe2U\xa1!m\x8e3J\x8aw$\\\x1bZE\xd7\xe6FYu\xab\xed\xba\x1a\xadp'\xfc\xa0\\&\xc93\x93g\x0f\xfe\xf10_,\xf3\x8c\x11\x03\x05\xe9]\x00\x90'l\x1b\xbf\xb4Q7\xaf\x9fU{\xc9\xc7\x10\xa6C\xea\xcf\xcd\xf5\xff\xce\xfcfa\x8f8\xc6x8{\x042 U\x95\\\xf1:\xb9\x0dd\xcc\xb1\xaah\xcb\xa4\xa33j\x14kUQ\xa1\xc2\xc9\xee6\x86\x02\xe5^M\xe3FL\xccN\xcb\xca\xac\x9b}je/\x08\x1a\xca\x1c\x86\xab\xd9\x9c\n\xd7\xe1\x9d\xb2\x02v\x8aY\xcdr\xd6\xc2&\xd4\x12\x14\x86\xdb\xe4\x14\xf5Y\xf4\xadp\x91<\x1c.\xcc\x164&n\x97S7\x94\x13\xd7_\xbe\x00 \xca\"\x1a\xa7dA2|\xbfM\xb28\xbf}\xa3O+\xdb\xef4@\x9b\xaer\x99gq\x92\xcd>\x94D\x96\x93\xfaG\xd6\x1c\x9e\x0f\xcfxh\x9c \xcbc\x82F\xfd\xfb<\x8c\x1c\xc9\xf0\xe0i\xe8(|\xab5\x8e\xd0-t\x9f\xaa\x163y\x10\x85\xd9\x87\x92\x1c\x9d\x9dT\xe0\x1b\xe7\x11\x1a\xef\x06\xc9b\xc9{\xca/'\x9f<\xb1}\n\xe6a\xf9\x96\x84tUH\x7f'\x1b{\xd6z\x94\xcc\xae\xe3\xf8\xa8\x1d\xdc\x98\xd9\xed\xef\xbekB\xcdwp8'\xd1\xa7\x92Af\x98q\x81?$%\x94\xab%[_\x1e\xc0\x89\xce \x08.IP\xc7\xe82=['E\x9ea7\xb4J\xf56N\xcf.\xc7#\xb8\x9c'%\x8f\x0f\x95\xe5\x14n\xf3\xe2\x13\x08\xa3\xbd\xf4\x0e\xa9\xce,\xcf\xb6f\x8c\xc6I\"\xde\x13\xd6\x8fh\x0ea \xbf\xf1H\xca\xbf\xf9z\xd5\xbf\xa1\xb8\xee7\x1f~K\xf30f\xff\xd1\x08\xfc7\x1f\xa3Q\xfd\xc6\x1ds\xfc\xd6\xd7\xc1\x1f\xf3\xa2\xc8oK\x98\x16\xf9\x02N\xf2\x98\x14Y\xf2\xf7\xa2\xaf\xd4\x1f\xd1^\x14\xfe\xc1\xb5\x0f\xbe\xd6\xd7%\x17\xab\xe94\xf9\x0c(D\x84L\x98\xaf\xcf\x02p\xa24\x89>9z\xbdUE\xfb7y\x9e\x920chq\x89K\x8e\xab\xc3\x16\x07\xd7@$\xa2\x9c\xb7\xb1J\xed\x1a\xa51AU#c\\dE\xedenW\x90\xb036\x0b\xd3\xd6\x874\x89HV\x92z\x9a\xe0Y\xb0\x13\xec,\x0b\x02\xee\xe1\xaa\xa4\xf9\x02~\\%i\xec\xc1\x1789\xbe\xd4\xcao7\xde}\xbb-\x9e\x8eL\xd0~@\xddS_\xbe\xf0[\x82\x0d\xd7 \xe3\x18\xe7Z\xd2\xc8\x0e\x83Z\xb9GjVA\xbfY\x91\x1c\xb5\x93g\x0el\x9a\xfc`\xa1PP\xad\xecM\xbbOF\x92e-\xae\xa0\xab\x8d\x1a\x15$\xa4\x12=\xb9N\x9c\xacM\xea\x1daP\x12z@i\x91\xdc\xac(q3\x1f\x84\xb3\xe47\x8e\xd0\xfe7\xaa\xc2\x84\x93\xcc&2\x05\x85\x9d@Mb\xae\xbdr;'\x95\xd8\x0c\xa4~\xf2\x10\xac\xc2\xef\xe6\x03^\xde\x07\xe7Y\xb0\x83\xaa\xd6\xc9\xa3!\xd3\xd6\xd1}\x90\xd2\x118aJ\xffL\xee\xf4\x90\xbayF\x8b<\x1d\x81\x13\xd1\"m\x7f?!4\x1c\xa1\xdb\x82\xb0\xfd\xf1b\x9eLY\xcd\xa8W\xcd>\xd7C\xb0\xd0:\xb6\x03\x0e\x0dW\xb3\x90&k\x82\xf3\xd3\x86\x12\xf43v\x92\xc7\xc94!\xc5\x05\x0di}\x8d\xd4\xfe\xd4bO%\xa0\x16\xad\x1b\x83\x8aS\xc43dc\x83\xaa\x90PC\xc1\xb0\xf3\xbau\xcd\xf2\x08K\x99\xb9\xaf^\x1b\xd4_2\xf7e+=\xe1j1\xbb\xdcv\xf4\xd9k\xfc\xf7t\xf7\x95\x1e\xfd\x9a\x8b\xe4w\x9f\xeb\xe5W\x98\xfe\xec{\xb3X\xbe4b\x151d\x93h\x92S\x18\x93\xdd+!\\\xa7\xe8\xb5\xf8\"\xb9I\x93l\x86\x1eu\xa6IQ\xd2\xc3y\x92\xc6\x86)_\x8b\xab\xf6\xc4\xedc\xafH\x90d%)\xe8\x8fd\x9a\x17\xc2\xb1D]\xa1q0\x91\xad\xaeB\xd4\xc58\x0dQ_\x8b?3\xe94XM\xb7Z3\xb3ob\xdcl(07+\xeaTaK\xec\x840\x8fI\xa4\xcc\xb8]\xb8\x95\xba\xdc\xee\xba\xe0\xd7\xf7\xdc\x82\xbdCk4\xafh_\xf5\xd1\x88g\x1c\x1cZ$Q\xb4\xdaA\x91s:l2\x97\xd6\x03l\x88\x1c\xae\xba\xcf\x9d\xec\x1a\xee\xdfb\xac\x1b?\xef\\\xf1;v\x12\xf0`\x9b\x08\x89-\x0eK\x0355+\xed\x1eFl\x83\x89\x8e\xe5\xab\xc4\xef\xddK\x87|P\xcfR5\xfbZ\x0cc\xfc\xe6\x0861\xa3\x15\x8b|U\xa6w\xe7d\x99\x86\x11a$?\xe3\xe3N\xc2\xe2\xd3j\xd9DS\xeb\xb6k\x8c\x9e\xf2-\xef \x05\xcfuD\xd2d\x91P\x12_\x92\xcf\x03\x0d<\xe4\x84\x11\x8571K~\xf9\xbda\xe7\xb4\xe6\"\x1c\xe8>\x17\x9e\xa7n\xe1\xeb\x14\x08\xeb\x19\x8a\xf6\x18\xe4\xe4x=\x02\xfb\xe0\xae\xf0\xde\xcf\xf3!v\xf9u(E\xd5||\xeb\x95]-\x8b<\"e\xf9\x01=\x14\x97\x03\xc4e\x0d\xeb\xae\x9d7\x90)\"\xe67\x90\xd9u\xab+\xf0\xb2\xea\xabHS\x98\x02oXm\xf5@\xa5]\x7f|z1>\xbf\xbc>98\xff\xf3\x87\xf7=j\xf6\x88u\x0b\xe9\xd8\xc7\xe7GJ\x11\x84SJ\n6\xa7}\xd1\x0d\x06\xd9\x05\x9c\x9c\xfd<\xbe\x1e\xff\xe5\xf8\xe2\xf2\xf8\xf4O=\x1d\x9a\xf2\x0eL\x85\xb8\xf6\x9f\xd4\xa3\x8b\xf1\xc0\xf9 \x1b\xf3\xf3\x18M_\x8e\xffry}xvz9>\xbd\xeci|\xf5\xe8\x8d\x9f\x8fq-N\xcf\x8e\xc6=m/\x9b\xeb0T\xc9\xe9\x9e\xf2\x9a5\xa6>\x88\x1a\xb3{\x01\x9a\xd3\x05#\x9f\xe7\x94.G\xdb\xdb\xb7\xb7\xb7\xc1\xed\xb3 /f\xdb\xbb\xaf_\xbf\xde\xfe\xcc>kd\xf3\"\xa4s{\x99W\xdb'!\x9d\xe3\x9f\x93wZ\xc9r=3\x16{\xba\xb3\xb3\xb3]\xaeg\n\x01\xfe8C\xed%u\xd5\xe8\xe9\xb5\x0d\xf6\xc9\xc5\xc1r\xc9\x10(\xfe@S\xde\x0f\x19\x0f~\x1f\x85\xe9[y>*\x94P%\x826\xaa\xbfvV\xd3\x1f\xd6N^L\xa9\xad\xb4aI\x17\xac\x8e\x1e\xdb\xdb\x8cQ\x8d=s_\xed\xbc4\xd0\xf1\x99\xfb\xf4\xc5+\xcf\xcd\xdc\x97\xdf{AR\xfe\x1c\xa6I\\\xc9\xe6\x1a\xb9CE\x19\xdee4\x7f{\x12nV\x94\xe6\x99\xd9\xaf_4'\xd1\xa7\x9b\xfc\xb3\xf9k\xb2\xc0\xf8\xfe\xa6O\xf3$\x8e\x89\xa5\xd2\"\x8c\x93\xdc\xf2\x89\xa0\xed\xa6\xe9S\xb9\xbaY$t\xd4\xd2L\xb6i \xe9\xeb\x8d\xe2\xee\x0dv\xc8\xe3\xa0H\xfc.\xc9>10\xac?`x\x04\x99\\\xb8\xce\xab\x97N\xaf\xae\xb2\xde\xcc\n\x95X]\xadR\xa9\x9f\xc8\x93\xf2\xec\x10\xe5mR\xc7\xfc\xd5\xab\x9ev\x0c\xdePZ\xed\x88Q\xf5\xb4\xf4\xba\xd1\x92\xfc\xc5\xc002\x9a\xd2\x8a\x88\x11Ch-P\x18f2\xa1\xa8\x93\x19N\xb8.\xd6\x15\x17N\xcb\xee\xf0\xb7\x82\x84\xf1Y\x96\xde\xf1\xb78)\xc3\x9b\x94\xc4\x8c\xbcb\xfd\x1f\xa1\xcb\n\xe1 \xeb\xd7|%\xc3\x83\xc6\x10\xc2o\xd8\xad\xdfX\xd2\x12h\x0e!\xa3y\x160MH\x1a\xc3mB\xe7\xf9\x8aB\x98\xc1o\xb2\xc1\xdf`\x1efqJ\x8a@\x91\x93\x16$\x8bI\x01!\xb0\x8el\xe5\xac'XC\x00\xc7\\\x90\xc7\xeb+\xe7\xf9*\x8d\xe1\x86\xc0bEY\x171\xd4\xfeo\xc22\x0e\xbd\xf7\xfd\x16\xc0\x19\x9d\x93\xe26)\x19\x99@(\x90\x84\xbd\xab\x1d\xc8\x0b\xf8M\x8e\xf8\xb7\xc0d2n\xd9~$~\xf8\xfc?\xe2\x94\x8b\xbe\xfc\xb7\x98\xf4C\xd1\x97\x7f\xd2\xb4\xcb\xd2#H\x026\xf3\xbf\xeb\xc8?\xb5\xda\x13-\xdb\x9b\x16u\xc8m|\n\xbf\xcb\x99\x11\x94q\xdb\xfc\xbf\xd3J\xb0\xe5\x08\xe95\x9b31\xa9\xdc\xff\"\xe4S\xf8\x8d[~m\x82\xf3[\xd0\x0ckh\x94]::m\x00\xa2Oq\x0b) \x18\xbc/\xf2%\x1aE\x0c\x83\xcc\xa62td\x03^6\xbe\xc8\xa4\n-%\x16\xd1\xa4\xb8b\xc74\xe7\x9a\x1c\x06\x88\x8e/\xee\xeb\xf2\x0e\xcb\xa9D\xf5\x89\x83\xe0\xcd%\xdb\x89\x0c\xfb\xc7\xba5\xedV\xdb\x99T\x99\xafP\xd5\xdeN\xde.u!\x81|zI\xd4&d\xcd\x08\xfdY\xc7\xbe\xa6.V\x9a5\xf5\xf1\xb5\x8f68(\xbc\xa8\x12\xff_\xf6\xfew\xbdm\x1cY\x18\xc4\xbf\xf7U\x94\xf9;\xa7\x0f9\xa6\x15\xc9v\x9cD\x89\xe3\xe3v\xdc\xd3\x997\x89sbg\xfa\x9d\x9f\xc6G\x0f-A\x16'\x12\xa9CRv<\x93\x9c\xeb\xd8o{\x0d{\x01\xfb\xec%\xed^\xc2>(\x00$\x08\x14H\xcaq\xf7\xf4\xec;\xfc\x90X\x04\x88?\x85B\xa1\xaaP\x7f\xc4_\"X\xf5\x8d\x15\xc4\xdf\xee\xfb\xc4\xa6=\x8d\xbd\xeb\xa7\xea\x11\xaa\x8d\x84\xd9a\xf5Z\x1f\x81|\xdd4\x06i)vVn\xc6V\xc1\xb7+$T\x94Ql\xd7/\xe4\xfd\xa9\x1c^m|M\xb3q\xb4\"\xab\xc8vJ\xf2{\xa4\xfd\x10\xce.*\xf8\x1aFI\x10?\x1c;\xd5!\xb1\x08\xe8\xfd\x12|\xa7\xe4\x18\xb7\xcc2\xfb\xe2\x1f*\xf5\x8c\xa9\xc4\xb1]\x88\xa0\xd2f\xa0\xda)cI\xa9\xd5\xa0k7Z\x95T\x15N\xab\xcb\xd26|UO\xe5\x98\xb4/b*\x90\xb3@\x92L\x96\xc8h\x18\xc4\\@\x06\x8f#\x8a\xc4M\xb6\xc1\xc1\xaa\xa7\x95<\xd0X\xf0\x0dv\x06\n\x0bd\xae\xd6\xca%\xabN\x83\xdd\xa6)\x0e\xb9\x8f\x95\x8a2q\x9f\x8e\xcc\x87\x16\x0du\x00\x8f\xb0\x0e\xfeQ\xf0}\x82\xdc*\xda\x1f\xa2\xa0Xa>9\xe5FB\x80N-\xa2\xa4\xba\x9a\xec\xdbwFZl\xb1\x9a\xcf{i\x16#\xec\xc2\xedZE\xadV\xd1z\xff)\xa1\xfb\x89\xdd!%\xb2q\xdc\xa8cjW\x84\x87\x90\xb4\x10\x15\xe1\x04\xc4\x0fg\xcf\x9aK\x08*\x00#\xcd\x8a\xf89\x06Q\xb2\x071\x03\x7f+\xab\xdc\xb3G\x91H\x99\xb9\x95\xfal\xc4\x7f\xa1\xaa\x1e\xffp\xdf\xf8\x96\xd06\xd6\xef^\xc8\xd9y\xc1\x15\x9c\xeb\x0b\xb75\x10\x7f\x132\xa6^\xb7\xd0\xea\x12\x17\x8b\x18\x81'\xab\xaca\x85\xbd\x94\xbd\xceU\xd0I\xd7=\xb7B\x1e\x12b\xf5\x10\x91\x88wUl5\xfe\xe6\xa8^%\xb6\xaa\xc40\x84Z\xfcG\xbc\x8dV\xe9\x9a\xd1T\x07\xff\xc4\x97\x9f\xd8\x9d|\xf7\x89\xdd=\xc4Z\xd17\xcb\"Tf\x1bAV\xac/M\xaa\xbdCo\x08\xdea\xdf\x11y\xd1\x1bb\xf1\xae\x9d\xba\x9bH\xf8\xa3\x80\xfd/\x9c9\xf6=4J\x08\x14u\xf7\x1f\x8d\x0e\x87\x97\x8f\xae\xc3\x0e\xe7\x87\xbaZ\x1e1\"\x96c\xa3._\xc5\x0f\xfdV\xa0\xf4q\xda.\xa0\x1c\xee\xf2\xe2\xe1&@\x11\xe0\xf0U\x8466\xea\xa3\xb7)\x87\x95\xf8\x8dQ1Y/__ D\xf4w\x05\x83S\xbd\x18\x04\x81\x06M\xff\xb0\xff\xe5p7xx\x80V\xf8J\xd3\x8a\x07 \xce\xec\xe2\x8a\xf6\x0fP\x916\x18\xec\x9a\xd7\xe6\xf2z]\xde\xab\xef\xef\x05\x9d=\xda\"BN\xec\xb1\xe4\xbf\xd6l\xcd\x04\xdfP\x8f\xccm\xb7@h\xbbJ\xdb I\x94\x1a\xcf?\xfd\x14+\xe8C\x0csQ\xa9\xb8\xe4\x82\x8ah/z*B!\x11\x014\xb3\x8e@\x92\x04fF\x8a\x8e\xf2\xf7\x0b\xd8\xed\xe3\x95\xdb6x\xe0\xf3&\x86\xc0q5\x93a\xaeB\xf0\x02^\x16x\xa0g\xffs\x87\x16p\x9d\x1fh\xeb\xed\x1a^\xa2\x0e}\xad\x03\xbd\x01\xdb\xed?\xce\xdf\xa6\xeb\xa4h\x97\xa0\xd4R\xd1\xfd\x83n\x86RH3\x94\xdeXH\xfclZ\xdaT\xd77\x89!I d\xaa\xecr\xbb\x08\xed\x8b2\xd9k\xe9\xbc\x88U\xed\xe1\xa9mc\xaf-\x94\x9cEu\x84\xd2\xeeb\xbd\xf1\x8a\xa1\x95\xa9\xea,\x87#\xea\xad\x08\xbf\x88\"\x13\xf5\xcd!\x8c\x8a\xcb\x10\"\xebB\xbb\x11 \xaf\xa51^\x07\x11\x93\x91\x03%\xdej\x03\xa5\xbe)\x07\xda\xecM \x07\xfac\x9aM$-\xe8\x8aM\xf4bH\xe3\xder@Z\xc3(\x98\xf0\x11\x15fJ\x0crH\xf2\xe6\x1e-\xaa\xba!T3\x9aH#\xf4rd\xd8\xf0\x7f\xf0\x9e\x14\xac\xaa2\xbdo9l=\xc1\x82\xa6\xd4\x97\xbf|\x02\x99\x85\xf5_\xd5\x90\x17\x84\x9b\xa2a\xd2\x80\x86\xc9e \xf0\xb0\x0b0\xcfYA\x01\xd2\x05\xc5\xc4 E1[?\xa1\xc0\xf8\xe5\x0b\xd0\x05\x870\xba\x0c\x02\x85\xb0|\xd4\xa6{\"=jy\xe3\xe4\xd8=\x0e,\xa86\x8327\xc7h,\xac7\x96\xc9\x0e\xf9\xf9\xdb\xbe1\xcc\xe5\xec\x0093\xd6\x99.\xf7I]\xc0\xee\xae\x87#\xe7\x07\xea\x86l\xc77x\xc9'\xfe`/\xa0\xb8\x90\xbd}\x9a\x0b\xe1<\x86\xee\xaf\xa9\x8f#\xbd\xff8\xba\xdd\xed\xdeT\xc1\xdeP\x928I\xa7\x8c\x16j&\xf3(\xe3\xa5h/\xccP\x1b\xc0yI_(\xbaU)^M\x0d\x84?ARZ\x06\x0e\xf6\xf8\xde\x92\xc8P\xc0\xcbC\xd8\xdbE\xd5\xc1^\xa9[(`\x08\x1bJ\x9a\x15h\xad<\x15\xd2\xc5`\xf7)y\xdd\xbao\xde\xc2b\x98\xc7\x91`\xa1${si\xb0\xe3k8\x04u\x0d]\xe9V\xeaurB\xfbR\xaf\x81q\x0e\xcb \x80\xf5\xb2 \x86,\xa8+k\xec\xdb\x89\x85\x90\xeae\xde\xc3M\x97[\x18a\xf3\xf7\x18\xaa\x8b\x05|\xdfD\x8dJ\x0fdf,\xf2\x84\xe24\xa15\xe9\xd3\x0c\xe7\xa4\xd4Ex\xb5\x8c8\xa8$\xd2yO\x1a\xf7\xaam~X\x0f\xfe\x9e\xe8w\x01\xc2\x8eK\xf4\x94\x04\xbc\xea\xec\xbe\x08\xb5\xfb\xecI a\x8c>\x83j5\xcff!4\x82\xbe\x93\xbc\xa2\xf7\xe3\xcaJ\xd3\xb2eA&1\xd2a\xe7\xb3\xde\xd5]\xc1\xde\x08u\x12\xcd\xf8b6\x9a\"\xe8\xe5\xac\xf0\xc5\x0f\x0cb\xdd\xe6\xdec\x8e^\x05\x87\xc4\xf5\x9b\xc7yo*\xe6\xa5R \x0e!\xe2EJmm\x16\xba\xc1\xa0\x00\xaam\xfc\x01n\xf2G\xfa\xc6\xff\xef\xbe\xd8\xf8\xfa\xbeG\x94\xc4\xa8\x0b\xc5\xfc\x03\x9b\xac\xb3<\xc6$\x86\xebP\xf8r\xf1\xf7mWB\xb8w\x8d\x8dk\xedX\xc5\x95H\xaabs\xab\x9e\xa7|(\x84s\xb8f\x1c%\xe84z\xda\xce\xd2u\x82~\xbcY\x9a\x16\x8e\x9c\x98\xe6~\xc6I\xce\xa3\xfc\xa3BhmB\xc0\xec`\xf3q\x15\xc4\xb0\x99{\x16&B$fuq\x8e\x01\xcb{ \x94\xfe&u\xec\xc5c\x90\xfc\x1a\x14\xf4}\xe4\xc0\x02\x02\xd9\xd4\xf3\x95\xcc\\V^\x94\xb9\xc6\xa7\xae\xdbb\xdf\xb4u\xd5\x9f\x08\x15\xaar\xd4\xeeyjg|\xd4qV\xe9(\xb9l\x99\x18\xb9\xdb\xaa\xe4w_\xeb\xb2~3\xef^\xa2E\xa1\x19(;\"yH\xc3\x12\x91\x92\xbdL\xf9\xa9l\x9cD\x96,\xe1K\x89\xb9 \x12\xf9\x13\x0fl.\x89\xc8\xdfe.fyh\xf0wE\xc6\x98\xe5\xd8EN\x14\xcd\xb5Y]B\xf0q\xdbh{\xa3\xe8!w)l\xb1:\xc6\xd0\xa8d \xcb7Q\x08\xef\x83\xc7\xa6\xbeD\x08\xefOLY_\xba8\x0e\x1e\x93.\x8e\xcf\x06OZ%\xac\x86k\x04\xce\x06Q\x97\xc0\xbc\x81]G\x19\x17\xf2\xf7\x1ce\\\xc8\xdfw\x94q\xf1\xfe\xc0Q\xb6\x82Cx\x0c\xea:\x9cH\xa2<\x05y\xfd\xbd&iV9\xd9\"\xe4\xb4w\xde\xc8D\xdf\x84\xb0\x0c1\xd1\x1bnKL\xea\x96\xfa\xd7A\x08W\x98kv\x8d\xd9\xe4\xf6\x82\x10\xc6\xfcL\xf1\xef*6\xfbV\x90\x99S\xf4\x05?\x82)\xefo\xccE\xa4\\\xfd\xeaW\x06R\xcfa\x0c/\xe1\xf69\xdc\xba\xb6*\xdf\xa6\xfe\nc_p\xa2,\xa3\xe4/\xe1\x10\xae\xfc\x1b8\x84\xbb\xd1\xede\x08\xb7!\xf0\xc1\x99Z>\xb3\xa1$\x80\xd3\xd1-\xe7\xf5\x974\x11\xe1OI\xc5\x96A\xb7TA\xa0\x18\x9a\xbdf\xbf\x17\xd0\xcfjw\xff\xa0\x9a{\xdc\xb9\xb9\x9b\x0e\xad\x1dtn\xed\xb6Ck\xbb\xed\xad\x9d\ny\xe5\xc6\xbd$\xda\x891i\xe4\x7f\x14\n\xc3\x11\x17K\x86\x80\xd9\xf5&p\x04\x13\x18\xc2i\xad\xba\xe9\xeax/\xcd\xa9\x14\xdb\xc4a^j$\x8a\x10\xbc*\xd3\xb7g\xfa^H\xd3z\x9d\x0d\xe3T\x13Sv\xa5Y\xfcW\x95\xde\x1d\xcf\xdf\xf2\xe5\xf1\x04\xed\xca\xa4-\xda\x0fQ\x1eO\x8e\xd7\xc5\x9c%E\\\xa6bpV\xff1\xcd\x96\xef\xa3,Z\xe6F\xad\xd5jA~\xfe\xbeJ V\xf4V\x19;V\x05\xaf\x97\"!1\x16\x9c\x9c\xbd\xfb\xf1\xf5\xef?~8\x1d\x1f\x7f\xbc\xf8 _\xfd\xf1\xf8\xcd\xebW\xc7\x17\xa7\xf8\x83\xbf=\xfb\xf0\xfa\xff\x7f:>\xe3\x7f\xee\xe2\xcb\xf7\xb2\xbaU\xf0\xe6\xec\xf7g\x1f/\xea\x1f\xe2\xaf\xf3\x9f\xce~\xc6O\xc6\xef\xcf\xde\x7f|\x0f\x87\x8a(|W\x81T\x86\xcf\xf5\x13\x7f\xff\xb1yE\x9f\xca\x92\xdd=\xea\xf2\x1e\xbf\x19\x04\xb5C*\x9f\xa7\xb7\xaf\xf8\xa2\xc6\x1c4\x9d|\x9e\xecm_`\xea\xf9 A\xa1\xa3\xbbE\x1aM\x87\xcdbG\xb9\x16\xdf\xd2;A\xfe\xbb\xf5\xbeH\xaf\xd3u'V\xdf\xd5\xf5\xea\xbe]\x97\x13?\xe3\x7f\xed~\xcb\x18\xa6\xf7\x1d\xc3\x04\xa3=\xaf\x05\xe2\x7f\xcb\x08\xe6\xf7\x19A\x1d\xb1#\x85\xbe\xfdg&\xfe\xaee\xd1\x9ee\x96\x92\x0bV\xa7OZ\x9e\x10nEJn\x13&\x1e\x15\xf5\x92\x8a\x1c{zJ\xacv\xcf\xa26\x89\x89c'{|\xab\x8dW\xe9j\xbd\xf2\xec+\x8c:%\xf0J\xcc0\xaa\xae\xea\xf4\xc3\x13\xc8kT\x9ab\xcaK\x17\xf9\xf1V\x19\x1b\x97\xed\x8fSD=/\xa4\x89\x98gU4\xa0?\x17}i\xc4\xd0S\x17\x97\xd8\xa6E8\xbd\x12\xe1p\x10^\x8d\x1a9\xe8o+NV\x9c\x1c\xc5\x95\x94\xcay\xdcp\xc7X\xb3!\xe2m\xd1cY\xd6XKx\xd2\xf3\xc6\xe8\xf2H\xc4,K?\xb1\x84\xae ,\xa8\xa5[#]e!\xf2RM\xe6l\x19\xd15&\"\xc2E\xb4t\xf8\xfb\x8b\x9b\xb1kV\xf8\xdel\x91\xdeR\xe1\x82d\xc4\xf4uO\xe2x/\xbf\x8d\xae\xafY\xf6\xf1\xf5\x076\xc5\xb8\xcf\x822\x85\xe0E\xe51+t\x063\xcep\x88\x1c;\xbd\x84\xdd\xf2e;\xcd\xcc\xa4\xfe\xea\xe1\x8d\xbc\x9e\x92G\x04\x7f\xf2t\x9dM\xd8P\xe5\x90\xa7\xe1\xc1n\xd8b\x08\xdem\x94%qr\xed\xa8%%\xc1!x\n\x8f\xc4\x91\xbf\x8c\xee\xe0\x8a\xc1\x1a\xddgCXEy\xce\xa6\x90\xa3y\xc5m\x94\x83\x88\x0e\x86J\x8e\x9ce7,\x83\xf7F\x95\xe4\xdf\n\x89ml*\xc2|a\x1eRQ\x9b\xb0C\x0cB\x88z\x18J\x0c\xed+~M\x10a\xafm\x00\xf2\xfb!\xc4j\xdd\x03?\xa2<\x821\x13\x97qH5\x0c\xdf\no\xa8\x1e\xdc C\x88\x88.\\$U\xa7\n\x14\xaf\xf6\xeb\x92\x04\xd6\xb8\x11c\x11X\xc3\xb9\x11\x059(\x13\xab\x91u\xd62\x84\x87\x98\xa0\x9b$Tu.\xac\x8bt\xf5L\x84zu\x11\xb3\xa4x\xedhk\xa6\xd59g\x93\x8c92\x9b\xaf\x9c&\xba\xfc\xb9\xce\xa2\xa4\x18\x8b\xf3\xdfS\x03s`\x1e\x7f\xf2I\xca\xabrp\xa6+\x96K\xfbF |\x16\x01\xac+A\xf5\xa0\xc7\x9e\xa3l.}\x15\xcd\xf7JKy\xc5\xa5 A\xc0\x16p\x04\xf3^\x9dL\x1c\x82\x87\xf2\x06\x9a_\xf2\x1d\x92\xf7\xae\x8a4\n\xfc\xa8\xcc\xf8\xba\xc6\xbbM^\x96V\xbbgEy\x9d\xf3G-:\x89\xfc\xae\x8f\x14 \x87\xb0&\xe9\x8a\xcc\xc1[\xce\xc2\x9f\xa0\x06`*\x97s\x1cs\x08M\x82\x10f\xf5\xf79\xae3\xdf<\xe8\xba\xd5y\xf2\x93r\xf2\xb3\x00\xd3\xec\x99\xf2\x9b\x83&\\\xa5\xd3\xbb\xa1ji\x1d/\xa6\\8{\x15\x15Q\xe0\xaf\x1c\x8a\xcdu\xb6\x18\x8a\xe0\xce\xbe\x87T\xe3c\xb60Y\x0e\xf5\x08\xb8\xc6\x0eD`\xd1\x94e9\xc9\x96\xf2\x07AH\xb2\xcdPR3\xe2N\xdcI\xafB\xb7\xb0\xf9[\"U\xa9\xac\xc1w\xdf\xb7\x10\xb3f\xe2\xb2\xeeH\\l\x93b\xfd\xa9a\xe7\xb0\xcb\xce\xdc\x84\x8a\xd0\xc1\x00\xd4S#lr\xfbL26eI\x11G\x8b\xbc\x9d\xc4\xa5m\xb4\xcdI\xa3\x1eb{M\xee\xb3e6\xd9{r\x83\xb4\xec=\"r~\xc7\x0d\xe4\xd6\xe9\xb4\xdb\x00\xb98\xf3D\xba:\n\xc6\xf6c\xb6hV\n;m\x8f\xb3\xb2\x8fV!\xa1h\xe5\x1b\x8a\x96\xadVt\xd8j\xc57o\xb5\x1a\xbaG\xfa\xbe\x1bO8\xc7\xefF\xf7 f\x08(z\x13g\xd81\xac\xa5\x0e\xa6!8`\xa1\xd5\x12\xc7\xd4\x10\xd6\xee\x9aj\x11\xc7\xeb,\x1e\x12V\x04\xd0\xb8\xc3\xb2\x07\xd8af\xd2U\xf5\xb4\xef\xb0t\x93\x1df'\x9c\xbe\xd7\x0e\xa2\x95\xa8\xff\xdcJ\xb5\xe7a\xb6\xd2o\xe6\xd4\xfa\xbbm\xe3\xbf\xff\xe6\xbc\xff\xf1\xb7\xd9\xe6\xfc\xa5\x8e\xbf\xeaZ\xe4\xc1x\xc7\x99C\x13%\x90\xfe\x9a\x152\xeb\x1f]+\xef\xc6\x7f.:i\xcf\x84\x824\x8d\xf2\xbds\x0c\xae\x9e\xbaR\x15 \xbdh\xbeb\x93\x96\x8a\xabrx-\x15\xa7Ho8\xe68\x96\x0e\xcbQ6\xa0+\xdc\x94W2(}\xcd\xe1\x08\xfe\xf6\x15\x9cR\xc6\x12\xdb\x93\x08AW\xb9\xae\xb7\xb8T-.\xe9\xeaw-\xec\xf9\x95\xd05dD\xa4 \xfe\x8c[4\x97\xb7p\x08\xfeJ\xc3\x07\x1f\xad\xe2\xff\xf65\xe8E\xd3)\xde\x11E\x8b\xff\xe0\xf0\x11\xd6\xfa\x82-\xa3\xdb:%\xae\xaf\xf4\xb2Y/\xce\xcf\x8e\xcf\xf7\xfc\x80\xcb\xb0\xfd\x10\xa2J\xa0\xbe\na\xd2\x13\xb1\xf7\xd9\xf4\x1cul\xbe\xc8\xac\x0cC\xa2\xee\x8c\xcfXV\x08\xeb^\xe2\xbaU\xd1-\x1c\xd5\"\xf6\x89\xa6\xb2\xaa\xa9\xdb@\\\xa6\x9f\xca\xb4\xf4\x87`\x08\xfa\x7f\xfb\x1a\x82,\x0c\xe1\x96\xb2\xe3\xe3[\xee3\x1c\xc2i\xe9\xd1\xe0;\x88\xc89\xd1\xbc\x93\xa8\xf2\xf3|\x85a\xcc+\xd9\xf2\xd1_\xf24 \xa1`\x9f\x8bG\xabE\x14'!\xfc\xee\xd1\xef\x1a\xa8\xbcw\"\x82[\xee\\\xdc\xad\x98g4\xf6y\xe7\xf6\xf6vg\x96f\xcb\x9du\xb6` ?\n\xa6\xb6b\x13\x04\xb5\xba\xa6\\\xb3z3VL\xe6\x8eY }\xfd\xec\xd8'\x18\xd6i\x08\xde*\xcd\xcd\xdb\x0c\xf5\x94d\xf5\x9c.\x97\x12\xfd\x8dc_\xe0i\xe18\xf9e\x9c\x1bt\xf3\xe2`N\xb3!\xac\xfd\xa0g\xbfw}\x9f\xaf\xd2$gD\x03V\x81\xd5\xc0\xd7\xa0\xc7\xf92\xbf\x99[\x02\x8d+\xd3,KYo\xcaO<\xf7\x92#\xf5\x97.\x91B\x1b\xfd\xe5\x0bx\xaes\x0d\xd4\x15\x88\xfc\x02;9\xd5>\xa3\xed X/\xfd\x84\x0e\xcc_\xbe@\x06G\xb0hWw\x83\xa6\xf2v\xd0Z\xe8\xa8\xd2\x86\x8e\xeaqhP\x7f\x13\x16\x85\xa0T\xe0yG\x158\x94\x8c\xc1\xd8=\x00\xa9\n\xb7\xf9zP\xdd\xfd\x03\x00\x8f\xf5\xf2\"*\xd6\xf9\x05\xfb\xec\x9a\x08\x85\xe6\x98\xaai\x03<\xaf\xacQY\xa0l\xfch\x04D\xcb\xc5r\xb7\x89\x9b]\xf5K\xec\x90\x06\xae\xf9\xa6\x0c\x00P\xfb\xc4m\xf2C\xe7\xa6\xd2\x1f%\xdbh!M*\x17\xad#}\x03\x8bL\xa4\xcd\xe6E\x99\xdc\xb9\xc2sp\xfb\x10\xbc\x10\x98H\x16%\xc2\x04\xe0\x0ft\xee\xc5\xbf\xc6S\x96O\xb2x\x85b\x9e\xfe\x91\xf6\xbe\xf6\xa9\xfeA\x93m\x92\x96k\xcb\xf6\x0e\x02\xa0|\x86\x00\xfd\xec\x7f\xf3\x18\xbd\x01\x1a\xd7^\xfd\xf6l\xab\x10\xad\xfe\x14-\x17\x82\x81s\x99\x10\x95\x19\xa7\xc8\xe8\xbb\x98k*\x15!U\xeb&\x12Y\xb3\x89\x84\x91\xbb\xb6v\xb7o\x0d\xac\xd1\xd8\x94\xdedR\xea\x89\xab\x0bk\x0c\x87\x1cM-g\xea\xc6\xc4p\xb2\x19\x91\x0fT\x13X8\xa2^\xcc\xb3\xf46\xe1\xa8\xaa\xd3\x9f 4q\xfe\xb7\xb7\xf4\x8b4\x9a2a\xc8vq\xf6\xfb\xdf\xbf9\x1d\x0b\xeb\x8bs|\xf5\xf1\xfd\xab\xe3\x0b\xfdU3^\x98\x16\xc5\xbf\x14Z\xacUh\x86Flh\xb1=\"\xb4\x11\xa5\xed\x91q\xd2s\x0e\x9e\xd9 *PrH\x16\xe9\xf5\xf5\xe2\x9b\xcc\xd1\x08\xe5\xe5}\xac\xa1\x88e\x93\x064\xf9X@\x8ep\xc9&\x96\xbf\xfcH\xcc\xcc\xd3W\xa0D\x9br\xb2m\xba\x86\x1a\xfd\xbf\x07\xf6\x97\xafK;\xadL}D\x07AG\x03\xfd<\xc3\x8bmi\xae\xcf\x92\x9b\x9aA\x7f!\xcd\x17\x95\xc9?\x92\x1b\xe4e\x95}?\xe7\xbcr\xcd\xe0\x7f\x95\xe6\xc20[\xfdz\x1bq\xc1M\xf5%\xed\xb7e1\x9e\x9e\xd6Z\x90j\xe3\xf1U:\xbd\x1b#\xf6y\xb6,e5&\xb3T\x8d/\xfe\xf4\x9enN2Vx\xbfk4\x18\xd5\x1b<\x7f\x7f\xf6\xee\xfc\xb4\xa9E\xb1\xd3\x9b\x9a\\\xd7\xe1\xc5\xc14\xfe\xe3\xf1\x87\xd7\xc7?\xbc9%\xe6,\xa06\xbe\x91\x08/\xa7\x8d-\xde\xeb\xd8\xbf\xd1\x02\x95R1\xc2\x12\x7f\xb7O\xba\xc2\x0e\x1e\x9b\xf1\xad\x84/\xecc\xb3\xbap\x85}b\xbe\x16\xee$\xfb\x8f\xcd\xf0\xa8\x0b\xe19kjK&b,\xfbf\xf5\x99\x18\xcc\xb3\xc0\xf7\xe2\x82e\x11Fv\xaaWYq\xfe\xdf\x1f]b,\x14\x8c\x9c\x91p\x8e\x1a\xe2\x04\xe4K\xdf\xf4ui\x94\xd2@Sl\xcc\xe3\xbc\xbe-*\xc8:\xdd}Q\xfa\x9a\x87\xca\xd3\xd5l>\xf7\x13\xacdFQ\xe2+u\x17\xc2U\x08c\xe1\xea\xda\xae\xe0\xc50\x10\x98 \x0b\xf3R\x9c\x94\x9e\x8e'V~Z\xf5tr;\x15148\xe4\x1a\xf2\xad\x89J\x88\x9fM\xd5\x80\x96{\x1b\xebk\xdf$\xec\x16\x12\xe9\xa7\xee\xc8\xe7\xa6\x9eMT\xa9\x9b\x8c\xa8\xfbH\xec\xbe\x08\xf3\x13\xf4P\xc4\x10\xb5\xaf\x15B\xdb\x95>K\x07 \x0e[8<\xa4n\xe3\xce\x85\xd8k\xbd?\x11\xdc\x02\x1d#\x8e?\x9f\xe0\x10NF3\xcc\xfas2\xf2\xfe\xfd\xdf\xcb\x8d\x85\xafn8>\x9d\x8cn.\xed/\x8f\xe1\x10>\xa1\xc3\xb4\x7fC\xdc|\x9d\xc1!\xdc\xc0\x11|\x86#\xb8\xf5=\x96\x14Y\xccr/\x80!\x1c\x97~\xd9\xf6g\xe8\xd4\x85\xb1&\x84~\x1f\xfb\xef\xc9\xafyoF\x82@\x8e\xf5\xefQ\x1f?\x86C\x98\xf8\xefeT6v\x0b,\x08\x02\x8c\xe5i\x86\xbc\xe2\xd5\xc7\x98\xb3\x13?\\\xf8\xe3\x10N\xe55\xb7\xb8\x93S\xa8\xa0\xdf1\x8c%\x94\"^}\x16\xc24\x08B\xf8\xcc[\xc0\xbc_\xe5\x02\xf1\x1e?\x89X \xbc\xf5s\x19i\xf4\xb8#\x95\xf9T\x05c0\xb4i8\xba\xef\xbf\x87\xadk\x0c>\x8f[}\xeb\\,\x90\x1a\xda \x0e\xed8\x08a=*\xb8\xa8z\xcc\xff:\xe5\x7fMC |\xa49\xfc\xee\x9c\xf6ObNC\\D\xbej\xb7\xbe\x9a\xa6\xe3\xaeS\xc4Y^V\xd5\x91n8*\xcbU\x1d\xc2\x19\xb1U\xe0\x9a\xdeV(\xd8_I\x1f}\xfc\xff\x84O=\xe6S\xbf\n\xe1ntuI\\\xa8\xa2\x03x\xea\xa7\xbd\xf7\xb0\x0di\xefG\xf8\x1d\x08o\xff\xf3\x00\xe9\xef\x1d\x1d\x80e\xc3(\xf7\xfa)\xb0\x95\xf8\xfb\xfb\xa8\xd5\xddJ\xfc\xc7\x83\xc0\x9dQP\xf6\xf5\x04\xb6\x0e\x1d\x829?\x80\x0f\x02\x99\x9f>\x04/\xb2ds\x10\xc9w\x86\xedDL\xf5f\x83\xdc\xc0\xb6^\xe5\\!\xefg:\x07\xdaxLG\xc9|B\xe5\x85\xe1l\xc1^\xe0[9cd\xb0\x8d\x83A\xe0{\xafO\xc7\xef?\x9c]\x9cy\xf7\x0e\xb0\x11\"g\x92\x92\x894\x84\xc2\xd2z\xbdp\xc5M\xc3P\x82\xeb\x00\x12\x0ci\x89z{\x7f\x8d\xb0\xc0\xa8\x902\xc4/\xf1\xe1\xf32 \x0e\xbc\x84\xfcy \xbf\xe3G\xc0(\xdf\xde\xbe\x14f2\xff\x1d\xfb\x0bl\xed\xcb\x97\xaa5\x1a=\xcd\xa8\xe2\x9d\x17hw\x10\xf4T\nb\x1a\xa4\x99\xb8\x8fP\x95d\xd0\xdd\xcdzq\xa1\x01u\x0bb/\xb5\x8d\x0e&\x1d\xa7GN\x06\xd3\xac\x07\x8btj\xe4$\x8a\x08\xcdy\x8ca\xe8F\xf1%\x0c\xe9\x13\xc1\x0en\xaf\x07 \xad\x97\x1e\x19\x91\xef\xab\xc3hX\xffL\x86\x88:\x82\x08\x86T\xe4\xf8\xce\xd0\xdf\xdb#\xa0\x9f\x8d\xbc\xf1x\x92fl\xe7/\xf98\x9fG\x19\x9b\x8e\xc7\xe2\xa8\xf7]e\x87\xf0\xb7\xaf\xad\x1b\xcf\x01\xd2t$r8\xfa\xa9\xd0\x9c\xfe\xedk\xd02\x1f\x17=\xbd\x9fF\x91%\xeb%\xcb\xb8\xf04\x84-\x7f\x00\xdf\x03E\x01\x94\xf7\xb4\xaa\xb7\xeb\xa8w\x9b\xc5\x85\xaa\xb3\xef\xa8\xa3\x14#\xb5\x82o\xba\xd8\xa9Z.\xb7\xef\xfe\xe3\xc0\xdf\xd2\xb5\xd4\xfc\xddA\xe0\xcbh\xbf\xe0\x89?\xbc\xa6$\x1a\xa8g\x1e\x17p\x08\xd2\xa2\xaeT\xca\x8f\xe3\xfa\xcdG\xe8>U\xf8\x98\x98L}/\xda\xb3!Rj\xe0\xc71I\xc5\x12xyXQ\xc6#b\x15%L]<\xe34M\x98\x9d\xe0\x15\x86\x18\xcc\x0d2\x91\x7f\xa0\x9a\xdb\xf6a\x19V\x8f:Feg\x04\xaf,\xfb\x19\xd4\xfb\xd1\x10z\xc3cr0\xa0\x03R=\xde\xbb\xefv++4\x05\xd3\x8fC\x88\xc4y(\x17>\xf5\x0bS&V\x0f\x1e\x05~\xe2(\x15A\xa6]\xd1\xd2\xe4\x98rx\x01}\xe1\xd7\xfeR\xb8V28\x02\xcf+\x85\x00\xbeP1\xb6\xa4\x05/\xcc\x83\x00^\xc0\xe3\xc7\xbb\xcf\x0e\x90\xbd\x83\x97\xf0\xf8`o\xf0L4\xb4\x0d\x03\xe9\xa8\xc9iKd}\xcc+\x88\x06\x0e\xf6v\xb1\xf3\x887\xf0do\x7fO\xf6/\xeacG0\xc44H\xe2m\xbe\x88'\xcc\xcfC\xec\x04s\xd5D\xb0#\x9b\xd9\xe6\xe3\xdc\x91\x83z\xf1\x02\x06\xfd\x00\xb6\xe1\xe0\xf1\xe3\xbd\x83_v\xb7\x9b\xfa\x11\xa9\xab1\xb1G\x86-3\xe9\xbeT\xd5\x98\x1a\x9c\xb5\x0c\xf1a\x9e\xc6RWs@\xebj\x06\x96ng\"\xeb\x9b\x83\x94\xca\x9a'\xffT\xd6\x10\xcf?\x955\xfa\xf3Oe\x0d>\xffT\xd6\xfcSY\xf3Oe\xcd/\xa6\xacqjj\x06duw\x18\xd1\x03\xc7\xdd\xc9\xe3\xbe\x83o\xd3\xc2\xb3w\x12DQ\xfcL\xdb$\xa5\x0d\xf9\xca\xb7Q1\xef-\xa3\xcf6\xcf J\xe2\xa4\xc3 \xe9\x18\xb0d\xb4\x19\xf2\\}8\xe2b4l\x83\n\xc2\x19\xfb\xcc\x88\xc9\x0f\x1b\xac\x8f\x9e\xc8#4\xb2\x96\xc4\xb9\x9e1c%_\xbf\xceOK\xb9/,\xd27\xe9$Z0)\x1b\x95)Qpo\x9c\xcd\xbc^\xbeZ\xc4\x85\xef\x85\xde\x86\xec\xfb\xde\xde\xaf\xa2Dq\x04\xad\xdd\xa5\x95i\xc8o\xe5+6A\xfa}\x8f\x15\x95\xea\xb2H.hk\xca\x14\xcd\x13,\xc2CH\xfd\x16Q\x923?\nF\xf1e \x13\xef\xa4z\x92\xf3\xeeh-b\x17\x87J)h\xddR\n^v\xff\x89 \xab\\nL\x07/{`\xf2\xc4\x13Zs\xc2Y\xd9\x89\xca\xcdl\xb3\xb0\x93^\xce\x8a\xd7\xcb%\x9b\xc6Q\xc1l~u\xd2\x9b,X\x949j\xcc\xb1\xc6[a4\x7f2\x8f\x92\x84\x19~\x867X\xe3U\x9c\xaf\xa2bb\x98},m\xe5\xe55\x11\xca\xe7\xae\xed@CA\x1e\x0ea\x9b\x9fe6I\xe6'\xcf\xb5\x99:\x85\xce\x90\x01\x9a\xe1\xc5\xb5\x93\x9b\x95A\xd2x\x85\x10\n\x9f\xf0 \xa8\xbd1\xa6s\xd5\xcad\xdf\xc9\\ \xc2Q\xa5\xdeV5\"<\x96\xa7(D\xae\x1a\x9b\xac\xa5\xfd\x18]\n\xad\xed\xe09D\xd95n\xed\xbcR\xec&\xcf\x03\x95C\xa3,\x1d%\xdb\xdb\xe6I'\xf7\xcf\xf5h{{y\xd9\xb6\xd0\x02(\x7f\xe5\x0c&_\x87\x9b^\x92\xde\xb6\xb6\x86\xb5\x9c\x0d\xcd\xe1H(\x13|$\x93\xec\x16\xe6A\x8f\xd3\xbd\xdd\x10R\xfcc\xd0K\x93*\xb4\xf9\x95\x08T\x1f\xf9qo\x95\xe6\x85\xdc\x85Hk\x06\x18\xcfi\xd2\x8b\xa6\xd3\xd3\x1b\x96\x14o\xe2\xbc` C\x9aN.\x86\xd6\x00r{\x93^\xbc\xe4=\x9e\xa3\x17P\xceG\xd6<\xb5\x89>\x06<@=/\x04\xefw\xf54\x07\xf6\x88|ON\xc8C\xaejK\x8c\x1c]\xa5\xd2$c\xd1\xf4\x0e\x03\xee\x89p|(]/|O\xf8&a\xaa\x15\xf7\x88\xf2^\xb4Z\xb1d\x8a\xf9\xe8}\xed\xab\xa0g\xb7\xdc\x86\xc3y/c\xcb\xf4\x86\x89\xc6\x90g\x0e\xcb}\xea\xf4\x1c\x80\xa6\xcc\x959+.\xe2%K\xd7\x85\x86\x11\x9c\xe9\xa8\xbe\x0f\xeaF\xb3\xd6\xf7V\xa4Y\xa4\xd5C\x98VM\xe0_]\xb9\x15\xf7`\x1b\x9doh:\x8a\xeaF\x9a\x1f\xbf\x19\x02k'\x9b]\x1cv\xdc]\x13\"\x1f\xc8\xae\xdb:n\x81\xde\xa6\xec\xce\x13:D\xff\xe0I{V3G\x9e\x8f\x0cie\xea\x17vj8\x91\x90\xa8-\xb5q\xdc\x9b\xb9\xb2\xfe\xfa\xfd\x10\x92^\xc6\xf2tq\xc3\x02\x8cl\x8f\xa9\xfc\x96\xb1\x96\xdfjC\xc0X\x10\x10\x80yF+\x01\x91\x0dDg\x86v&\x90\xe2\x00\xe9|\xf3\x98\xc7\x8f\xcb\xc9Z\xdaT\x91wF\xb2x[[\x9c\xc9\xf3>\xb0\xeb\xd3\xcf+\xa4\x8di-%\xe6\x86s\xb6\xf8<\x95\xb0\x81\x9c\xf3\xe3{\xe1\x82ZN?\xed\xc9\xab7\x11\x9aA^\\\x89w\x9cK\xb10>\"\xc2\"F\xd2A\xc0O\xf0\x161\xeb\x9d\xa3C(\x17ac\xb7\x05\x00\x88l\x9e\xb6\nA&\x8c\xf1B\x88\xee\x0d\xc4g\xae\xdb\x84Zf\x97Nr\xa9\xa6\xeb\xc9\xea\xc9\xc57\x1a\xd1\xee\x9eC\xa69\xd8Cyc\x12\x15\xbe'\xf8)O0\x1dB\xc2\xab\x875\x9e\xd5\xeez5\xbe\xf4]\xb4d\xbf\x8e\x9c\xbdk\"\xa2\xdc\x934~Z\xe6\x0fR\x9aylj\xce\x854c\xdd\x9eKaf\xcf\x14Z\x16.@\xbc\x92\x0e\xc8\xba\xe4&\xe0&lS\x8e`\x01- peF$\xcc\x98'\xae\xf9\"\xbf\x90\xda\xb7\xd2\xccL|`\x1eH_\xad\xaedN\xa5\x92\xf4\xa6\xfeV\xd6\x9bii\xfdB`\xa3\xe2\xb2m\xc5\xcc\xe5Jp\xa7\x96\xb1C\x1el;\xa8D\xae\xf8\xc9\xa5\xe0\x8a-~\xa6\x13R\xb9Y\x94\xd2\xdd3\xf1\x1f\xef\x99\x18Ty\xeb\xd4\xfdr\xbat\xd9v\xed\xf4\xec\x80\xde\xa4O\xcc\xf7\xb1c3\x08\xf4\xb6\xac=\xe4\xbd\x93\x95tGS\x94Ey\x1e_;\xd4Q[\xb8\xb5[L\xaa\x944KE\xb4-\x1c\xef9\x92\x9c\xdf-\xaf\xd2\x05\x15[\x06\xb9\xe9\xe8j2e\xb3\xeby\xfc\x97O\x8be\x92\xae\xfe+\xcb\x0b\x8f<)e:\xd1'!dJ\xbf\xe4\x05\xbdY\x9a\x9dF\xad\xd1\x1a\nq\x86\x18\x0e\xadA(,\xc4r\xe1l\x1b\xf0\x0e\xca\xf3I\xdc\x95\x89\xa2\"\x08d\x98L\x0f\x93\xeeVn\x16_\xeb\xcc~\x9b\xd7\\\x84{\x9e\xc3\xdc\x94rC\xa49\x83PFK\x9f\x85\xa8!\x89{\xb3\xe7\x90\xc3KX<\xb7\xf9\xd2\xb2\xe5\x95\x90=\xd7\x9ap\xbc\xe0\xc2q(\x14!\\\xfe\xf3\xa7\xe510\xf1\xa7B\x98\xf1\xa7A\x88\x8a\x90y9\x86\xa5H\xc2u\x03/a\xf9<\x00I&\xa6!\xead\xe6\xa3eiQ\x95\x8cV\xa8S\x1f\xad\x1c2\xb8\x96a\x0d\x86\xdd\xb2J\xb5\xed\x9eA\x9f\xe6\xd7\x06\xa6nI\xec\x9e\xdd\x03j\xf7\xf8\xbc\xe0\x80s\x8f\xfe`\xf7 \xa8\xd9{<\xc5\xd7\x8f\xf7\x1e\x93)\x1a\xd6\xd4\x98\xa1t\xd7\xcc\xd2U\xae\xb9\xfdV)\xd4\x95_o\xc6f\xb9\xcc\xe2\xc7\x7f\n\xafh\x9c\x19\xea\xef5Jc\xf7\x9d\xff\x1d\xfb^\xd4\xdd\xa8\xd7\x9aof\x9c\x7f`\xd1\xa4\xd0\xf3\x10\xf2\xed\xa2W\xc9e>\xfd6\x9e\xb1\x8c\x85e\xe4\x82wg\x89\xc7\xbc\xbe[\x87e\xca\xf8\xa7\x8f\xbd\xa0>\xbf\x9e\x91\xd3\xbf\xbc\xaf\x0ceD\x05\xa2\xae\xcab\xafR\xb7\x85\xe0\xa9)\xd4u\x06\xfa$gi6a\x1f\xed\x00\x01\xe4j\x19\x1d\xfeX}\xab\x04x\xd6qp,\x04O\xeb\xba>\xbeE-\xab\xf1Z\xcfj\x9c\xd7\xf3#\xb3[X\xd4^\x1a)\x97s.\xd3\xe5z\x03ZkA\xfd\xcb8\x7f\xbf\xce\x98\x85\x15[\xfd&\x95AY\xd3r\xe5\xe2\x8di\xa5\xb9\x86\xa8p_\x82\x92\xf8\xcf\x02\x9b\xbc\x18\x0bc\xf5l\xfe\x90\xae\xafa\x861\x0c\xba\xfe\x07\x91\xcb\x13q\xb5k\x1fjk\x10\xf5+X;nb\xee\xbf\x04\n\xe8z\xc2\xb0\x07n\x9aT'\n^\x84\xef.\xf1\x17\xdf\xb8\xf5_\xbe\x97q\xdc\xed1q\xaf\xe4\xa1\xc9\xf0A\x7f\xd0\xdf\xfb\xc5F\x9a\xf8\x8f\xf7\xefm\x9d\x86\xe2\xd6\xd6`C\xd6\x98\x1eP\xed\x82\xf0\xfc\xf4\xe4\xc3\xe9\xc5\xf8\xd5\xd9\xf8\xdd\xd9\xc5\xf8\xfd\xf1\xf9\xf9\xf8\xe2\xa7\xd7\xe7\xe3\xb3\x0f\xe3?\x9d}\x1c\xff\xfc\xfa\xcd\x9b\xf1\x0f\xa7\xe3\x1f_\x7f8}\xf5\x0d\xees\x0f\xe65O\xc1u\xd7\x12\x0f\xa51\xe0\x01\xed\x92\xf7\xd82\xd0\x92v^\x074\xc3\xbd\xfb\xe4q\xdd^\xf4\xc9\xbe\xfe\xbb\x87)\x13=\x91k\xfe\xbcH3\xe65\x98}\xaa\x05\xed]i\xb3\n\xabV\xd2\xe5U\x9c\xb0\x0fl\xba\x9e\xa0\xd7gkKi\xcd\xdb\xa0j\xe9*N\xa6\"\x8c\xd0 \x1fY\xda\xa9\xb1\xd8\xd1X\xb4Z-\xee\xde\xc6\xd3\xe9\x82\xddF\x9d&\x189Z\x9ap2\x9fwia\xbd\xb1\x1b\x85\xe3 Ps\xe8\xd0g\\\x1bs\xd1\xd3o\xcb\x80\xc9|\xb0V\xf46\x8e\x8aFJO\x92.a\xf4\xb3\xda\xad/\xe7\xb1\x11\xf9\xc4\xb5\x98(38m-\x15\xf1\x16\xff\x88:\x9f0\xa5/\xc5BED*\xe5\xd3\xcf+\x8c\xf9\x00\xc5\x9c\x01K\xe6Q2a\x19\x14)\\1\x88\xca\xe9\xf6\xa8\xe8\x8ajq}\x16\x08C\xd9Z\x0d[+A\x8e\xa9h\x1bS&\xb0\xbf}H72\x99/\xa1g\xc6{j\xfb\xf5\x84pM\xe1\xef\xf1\x9e\xda~\xbd\x92\xa7W\xad\xa0D\x88)\xa9\x8e\x9c\xe1\xda\x8a\x1c(\xe2\xfa[X\xc6\x06&\xb0\xe8F\xe7MVS\x8bNM\xdc\xd0L\x8csAX\xd3\x82,\xd4\xe5]\xebj\x80v}M\xa5O\x95s\x98\xfaA\x08\xb32\x9a\x8dU\x0d\xb4\xa94\xda(\x8a\xd4\xdb\x0d\x15@\xea,\xb6\x06!\xef\xd5\x1e\x91\xfe(\xd9}&\xb23\x9f\xd9W\x14\xe63C\xfd\xc4\x84\xf9I\x08\x03\xda\x8a\x0b\xac]A\xbfu\xad\xe4\xd2\xbd\x92[Y/B;\x02k\xe9d\xf08X\xae\xf3\x82/\x19\xc6\xe2\x05!x\xe5=\xf8\x983\x98\xac\xf3\"]\xc2\xb2\xa4\xe8\xa8e\x88\xf2\xbbd\x02\x91\xf8\x9c\\^#-:\xeb\xa1l`\x0d\xe1\xdf\xca!Dw\x98\xb2}\x1e\xdd0\x88\x12(\x83\x1d\x83\x87jiPvG=\xf8\x89W\xb9K\xd7\xb0\x8c\xf3|\xc5\x16\x0b6\x85\x08PD\x89\x92\xe2\xe8\xdf\x1c\xa3Y\x11\x00P\xa7g\xd9\xfdT\x1a\x804\xce\xcd\x1dFs%E\x1bNSr\x7fA\x9a\xc2~\x85Y\x9cD\x8bEc\x1b\x03\xfb3\x9b|\xe8\xf6\x12\x9c\\\xcd\xc4\xd9 \x93\xa6k\x89\xe1\xb7\xb7]\xc8\x7f#3\xb6\x17\xa3\xc4aD\x92\xb6^\x80\x82\xa6\x92\xfb\xce]m\xe9\x0c\xc8\x15\xf7^\xbf{}Q\xff\x94V\"\xadI\xc3L\xb5hd\xec\xf1|}\x95O\xb2\xf8\x8a\x91\x11\x96\xafKq\x87\n\xf5\"\xe4'\x89$m\x92\x1f\xdc\x9bp\xf2\x93,a\x9f\x8b\x0f]O3\xf5H\x1d\x0f\x05Y\xf58!\xac\x1e*Th})BX\x8f\xd2^\xd4j?sS\xf9)\x11I\xacu+Fz\xb8\xdaJ\xb5C\x1a\x14\xb4 5\x91\x0e\xeb\x8b\xbb\x15\xa3\xe0\x9d^\xc9t\x89\x12\xd8\x8a\xec!\xac\x9d=\x96\xe4\xb6\xddJ\x9f\x95\xf6\xd4\xe2/\x7fn\x9e\xeb\xfaC\x93~@)\xa2\xe1pQ\xa2Ma9\xc3\xeaO\xa3\x0d\x82z\xd6\x89\x06\x7f;l\x90z\xba\x9cQ\xf8&\xe8\x843P\x0d\xcf\xf2&\x01\x81|\xcc\xc2\xc6\xf2\x05\x11)\x87\x0b]\xb4K\xecc\xeb\x0e0&Q\x91\xef\x94!x\xff\xfe\xef\x9c\xb9\xfc\xfc\x88\xff\xac\x07\x93\xff\x06\x89Z\x17\xf1\x1d~i\xd6\x9d\x8d\x14E\x1f\x9bWB\\\x1a(o\xc7\x84\xd8|I\x84\xc2Qfk.\x9f\x87\x9cp\xfa\xad\xd7\x10\x1eh\xa5Mo\xad\x8c\x1f;\xb9a\xb3X\xaf!\x92\xb9\xe2\xb5\x81\xe8\xa6v\xc1\x1c5\xea4\x90{\x89\x91{\x01\xcc\xd7\x8a\x7fm\xa1hS*\xdal^\xbc\xc0\x1b\x93\xc8b\xcbxs\xa8$\xe6\x1cIQ5\xd1\xb7\x9bH\x90\x1d\x17\x8e\x07a\xcd:\xda\xb3mY\xc8\xa3\xca-\xd7%\xba+2\xbe\x91\xf0I\x02^uV\xa1\xf7\x83 \xda\xe3~\xd0\x8bzB\xa3e\x82~cm\xd5\xa6\xf5\x9dkm.u\xc9\xcc0\xf2.\xacP\x97\xc7x_\xa6q9exIq\x19\xa8Y\x83^\xda\x8b/xQ\xc5\x18\x95\x08\xd0|\xda\xd0\xac\x8d\xdd\xf8\x80n\xbc\x18\xf5/I\x04)zBz\xf5k\xb0l\x18AWB\xca\xfc\xa2\x87j\x18\xc9\x80\x87\x15T\x88\x13\xc88\xec\x1fDq\xf8`J\xbc\x10\n\x15\x00\xb9\x8b\xf2S\\\x10\xd5(\xb7&}\xc0\x11xq\x12\x17q\xb4\x107P\n,*\xabr\x91\x82\xae\x9b\x83!\xa6\x1c\xbf\x89\xd3u.\xd3)gl\xc2\xe2\x1b6\x85\xab;]\xffP\x8b\xec\xaakM\xcb\xd1w\x81e\xb5g\x9f8\x9cQ-\xdb{y\xb1i\x1e\x19\xca\x84\x9frG\x1d\xc0#\xd3\x98]\xb8Q\x1cA=b\x02\xe5\x90\x86r\x0d\x1cA^\x1e\x07e\xc5j\xf5)}5GJ\x8a\xba\x13y\x06\n\x97Q \xaf\x1f\xfb5\xcb\x95\x82KXh\xc3kW\x8d\xf4\xaa\x0bL\xee!\xe8y\xc0\x17\xd6\xa3i~A4\xa6\x08z_\x18\x9fp\x1c\xe3@,\xf8\xaf\x9d5\xc7\xaa\x9d>G\x96d\xb3\xadS\xed{\xa7\xbd\x9c\x96\x0f\xa8\x84\x0e\x9e>\xe2\x08\x92\xb6t\x87\xa5G\x1f\xbe\xae\x0f^_\x0cm\x80Ay\xb6%\xfe\x9e2\xf0\xde\xdc\xfc\xb6\xcd\xbcag l\xbf\xe5\xa9\x8b\xb6\xf4}\x18j\xb1\x01\xd2\x92\xb0g\xc1s\xd8\xde\xe64={\x1e@*\xe8y\xe1\xb3Qr\x89\xcaT\x87\x1dh\xba\x19\xd4\xb5\x83\xf1\xc9A\xe0{E\xfaq\xb5b\xd9I\x943\x97\x15'}Hv\x02\x0eqA\xaf\x06\xb0C\xd8\x1c\x8bh\x97\x94\xaf\x7f\x81>_\"%\xc6!\xec\x14\xf0\x12R \xcb\x14\xb6\xd1h\x0b]\x81\x12Y\x90r|\x0c\xca\x8f\x12\xd8>\x844\x10\xe0\xe6\x1f'\xf2\xe3\x04v\xf8\xef\x97/1v7\xff\xe3\xd0\xcczU.h\\.U\x8aK\x95\xc1\x0bH\x9f\x07\x10\x8f2\xb4\xa5\x19e|$\xf4a\x17\xb7\xac\x92\xb9D|.\xc2\xc2\xd5\xf7F\x7f\xfe\xf3z\xb7\xdf\x9f\xfe\xf9\xcf\xeb\xe9\xd3~\x7f\x87\xff?\x9b\xcd\xfe\xfc\xe7u\x7fO\xfc\xec\xef\x1d\xf0\x9f3\xb6\x8b?glw\x86\xdfL\xf1\xe7n\x7f&J\xfbL\xfc7\xbb\xdc\xdc`W\xce#\xe9\x15,/\xdaM\xcf\xbabG\x08\x19\x85 \xa9\x03A\xe2\x86\xbdD\xac\x1a\xdee\xc6\x12\x03\xf8\nmo\xa7\x97\xb8v)\xbc\x80\xf8y h\x9e\xcfw\xd7(\xbdD\x0f0\xc76\xdb\x90\xb8U\xdbl\xf0\x9420\xae\x84\xf1J\xcdA\xc6\xd7\x8fI\"\xe3\xd6\xb3\xa0\xe1\x9a4\x04)\x9c\xf6\"\x05\xad\"H\x89[\x83\xa4M\x84US-\x99,ZQ-v\xde\x11(\xdeLXldhx5\xea\x13\xa6\xcf\xa0\xd6[\x04*\xb7\xc5{<\x0f\xb9\xec\xe5\xa7\xd5A\x17c\x1eHs\" \xc7)r`\xd7\x07`\xd7,q]e\x00\x88{9o\x14/\xb4\xbe|A'\xc1\xdaG_i\x94)\xbfO\xd8\xad\x1f\xf7N\xf0\x17\x97\xe38\x0bo\xe0\x13\x7fT\x15\xcc\x8e\xa0\xef\x9ax3\x94\xb3ng\x05\xfbd\x19\xf5\xc6\xba\x04}\x9c\xdf%\x13%,\x9b\x82tM\xd6vUZ\xeb\x95~\xcf\x12\x116\xc0U;\xd7k\xbf\xcf\xd2\xcfw\x97\x8e\xab\xf7\x16\xf9\x18\xad\xff\xdb\xc4\xe1\xcc\xe5F\x81\\\x0c:\x95\xe2_\xeb\xf2\xaf\xb8\xfc\xab\xcd\xc8\x86\xa2\xdd\xb6\xd6\xa1\xc52\xb8y\x92\xa5i\x17\xb5\x01\xdd\xeax\x0d\x11m\xff'\xfe\xb4d\x86jmY\xf8\x8fm\xd2\xecWj\x11\xf4\xd4\x10\x1b\xa2\xfa\xa0\x1f\xf8\x89\x7f\xb0\xff$\xd8\x88{ih\xd0\xdc%b\xf3\xec?i92\xcbKo\x19\xfa\xc8q\x80\nv\x15\xad\x0c\x95.\x06\x8a\x92h\xab\xa2-\xe53\xb4\x95\xfa\x89\xf0kV\xf4\x1c#\x02&h\xae\xaa\xf7\xc7x\x97m\xa7r\xc3\xacim\xdc\xee3\xda0\xe4\xc0\xca2\x14\xa1\xb1n\xed\x15\xa7\x07\xbbm\xd8\xae\xd8\x80<\x84E\x08\x13\x8a\x19@g\x02\xf8\x9e\x0c \xaf1\x8cv\xa9\xc8\xa8Dq\x07x\x1f\xc6\x019E \xfb3@\x1f\xdd\x97\xb0j&%\xc2\x8f\x9a\x9f0\x94nm\xce[\x11\xc5\x9a\xe85\xc7%\xb6\xdb\xbaq\xf08Kq\x87f\xbd\xbf\x96`\xe0\x12\x17?\xb63B\xf4\x04\xc5\xf9\xa0\xbb\xb8\xa0N\"!k!dE\xce\xfb\xdc\xc0\x0bX=w\x1d\xe5\x98\xa7\x96\x8c\xef\x02\xd2)\xba\x18\xdd\x10we\x1c\x00y\x80M\x8c\xf9\ns)\xd9\xbf\n\xe1\x0eC\x1d\x15\x88\xa1\x13\xcc\xca\xe8\x8b8F7\"\x9d\x13\x7fK\xb7\xa6\x99r\x8c]*\x1f^o\x1c`\xea\x9a8Y;\x92\x0c.\x0d\xcb:\xfd\xb9\xcaX\xf4\xc9*\xb1I!:\xa77\x8db\x0b\xa5\xf1V]V\xed\x93\xd8\xbf\xc6j\x9cA\xbd\x13\x9a\x1a\xbe\xfb\x17\xd2\xcdTl\x8bIP\xe1\xd2\xb50\x06p&\xbdl\xea\xb1 \n\xe0\x84\x04\x90\xd0\xf8*\xe2\xa7\xc4\x18+\x86/\xd0\x15\xee\xa3\x85\\\xdar\xe0\x8e\xe1|\xeb\x82\x90\x87\xc8\xa4'<\xcaQCZ\xfe(\xeaN\xe9\xf8\xd7\xbd\x84\x95o\x92\xf35\xc9\x9e\xc4\xac\x9a\x98\xefT\xcc\x97\x84\xa9e>N2\xbf\xf7$\xe8}\x8c\x93\xe2)\x8a\xb1\x0fr^\xee>\xa3B\x80r\xb1\x87\xbe\xc79\xd8\xbf\xaf\xe8)\xe2\xa5~\x93/\xddSz\xac\xbb\xedcr\xeb2b\xa1\xa5q(g\xf8l\x8e0\xf4_\xe6\xc7!$\x1dp\xa4D8x\xfc8\xf03\xc7\xd6M7\xebc\xd0\xa7\xa3RqN\xcd\xbf\n!'&v\x0d\x870\xf2X\x96\xa5\x99\x17\x827Y\x08\x7f5o\xca\xf2\"K\xef0\xb0N\xb4\x16\xef2\x96\xaf\x97\xcc\xbbt\xb9\x08\xdd9\x11&\x06y\x1b\xc3a\x88\xde\xe0ROf\xce\x154\x1aU\xe8F\x86\xb1]\x0f\xbd\xc9\xc5\xed\xd3\xdbt\xca\x9b\xdc\xdab\xda\x0b\x19Z\xd9\xb7\xeb\x99o\xbe|\xc1O3\xb9\x7f\xce\xca\x12\xc7\x1d\xa40r\x98\xc7\xd7\xf3\x9f\xa3\x82eo\xa3\xec\x93\xbd& id\xd5\xeeO\xed\x1f\xac\x89\xd1\x1d\xc1\xe0\x00\x8608\xd8{\xba\xef\x80Bm(\xfc,\xe0S\x12'\xa42\xa5\x10\xb0\x88\xaa\x82(\x90\xd9c\xd6!\xdd\x08\xc6\xfb\x9d-\xd24\xf3\xedr\x15\x96@\x08\x8a \\\xeeo\xca\x84\xed\x18\xe4R\xcb\xd8\x1e\x8b<\xe9\x9c\x8f\xd5_\x9d\xa4k\xf4\xa5W\xf5f\x8b\xf4V\xa4\x1a\xd7j\xb2D\xa4\xc8/\xf3\xb5\xb3d*\xe8W\xed-\x87\xb2\xf8\xb6|\x85.>\xc2\x9d\x05\x7f'\x8cM\x15\x91\xac5(Z\xa3\x8a\xd4\xda\x89 \x8aF\xfbbC\x9cO\xe6l\xba^\xd4G#\xf7\x8f\xf9\x12-\xe9N\x93I*\x87\xca\xacw\\\xae^\x17\xb3\xa7*\xe3|t\x1b\xc5\xc5\xab,\x8a\x13\x0dNr\xaeo\xd3\x8c\xd5\xdb\x9f\xa4S\x96\x99\xe0+{\x13oY\xf5\x8a\xa3\xc4\x1c/\xb2\xe6\x92\x82<\x0bzBE\xf1J\xb4\x15\xd8M\xb3[\x98\xfbU#\x81\xdd\x8fVX\xc3W\x97\xe7\xd7\x95\xdb\xf3\xcb\xa4\x1c[\x88\x8b:e\xb8\xaa8\x08>\xb4+\xd2\x95\x0dG8\xce\x8c\x03\x92\xd7\x17DK\x04\xa9\xa8\xad\xb8\n\xf1 \x14\"4\x03\xcc\xebV4\x06\xdb/w|\x10\xba\xd8f\x89\x1b\xda\x87\xea\xcdaU\x1a`\x14\nW\xdcx\x07 \xc7\xd5m\\\x16B\xeab\xe9%\x17\xc1\x0c\x88\xd8`\xabL\xcd\xe1\x08\xfc\xc8\xd8c\x9d\xf8\x04\xd4\x8d\x8b=\xac\xd6\xc9\xee\xa7\xaa(\xf1\xcc\xd5\x1ah\x9c{Y\x99\xb7\xde\xe4b\"\x94\x01\x8a*!\xd4%\xddRy\xd3\xc2*\xb1\xd06o\xb8N}aX\xb1\x91d'\xf6\xed\n\xa0\xb9xI\xb9\xfa!\x9c\x93\x97\xf7\x1ct\x11\x86.\xf2\x91f#\xbew\x82+B\x81\x9es&\xa2\xe4,zq.\xd8'?\x13\xce\x07\xfa\xb6A\xcd%e\xbb\nztn\xa5*1NKa\xa8W\xf7Mz\x9d\xdcD\x8bx\nI\x9a\xec\x88f\x1f\xc9\xc3a2_'\x9f<39\x9dz\xf0\xb8wLDnk\x02n\x11F\xb0\n!F\xe1\x93\x13p\xbf\xe4bb\xcc\xc7c\x0cY\x1a\x9c\x96\xf1\x97\xfb\x1c\xa3]\xf37?&\x93\xc5qi\x16\xb3\x0bi6\xc7\x1c6\xcdv\xde\xc6\xdc\x16\xbdY\x96.i\xdc\xc0 f\xfc\x94\xd6\x8f<{\xbe\x9aC\x9e\xe0({\xeb$\x9f\xc7\xb3\xc2\x0f \x9a\x15,\x03\x96L\x81\xdd`\xf0\x8f\x00s80\xb48\x10!\xfa\x10X\x02U\xbb\xb4\x8d[F5|z\xf6\xa3h\xd2\"\x0eQyd`nK\x0em\x8c\x0bXn\xda\xdb,\x96\x97{&\xb4\xa5\x8e\xaeJ\xf5\xa5\x8fw\xc0{\xfbT\xed\x9bz\x99\x0ci\x8c\xe9\x9ej\x03\xa2\xb0\xcfT,\xb6\xad\xd5\x16\x93`\xe2$\x84\xd5\xb9 \xdc$r\xc0/L\xe6\xb0b\xba\x98\x93\x8e|\xf5\xcd\xf8\xe3\x0e\x1a\x7f\xab\xd1xj\xc0E\xc9E}\xff=\xd4\xddEp)\n\xc1\x16\x1d\xf1)\x88\xb5\x9eFE\xc4\x97\x1ac s\xa0\xf9}\xb1\xa6\x1d\x89\xa2@\xd2\x92\xa6*\xe4Kx\x1b\x14\xa5\xad\x01\xee\xfb\xef\x914\x06\xa1XT3\x10d\xed\x17\xed\x94q\xa5\x87q\xf2J\xc6\xeb\xdb\x93\x9f\xea\nc\x82\x7fP\x01\xad\xea\xaf+\xce\xcf^bB\n\xae\x8d\xc7\x89\x80\x8e\xee\xfd\xc6\xfe\xf9 \xdf\xee,\x13\x82\x06\xbf^\xc5\x88,\xd5\xdf\xf5\n\xe3u\xa2\xd7)\x7f\x19\xb5\xaa:\xad\x87\x99\x90\x06\x10;\xd6\x8b\x05G\x10+\xccw\xbdq^\xb7K\xc37\"EE\x06\xe4\xf29\xc9AVG\xf4\x04\xcfoC{Th1\xdb|\xa4kxld&7/r\x15eu\x86\x9b\xa1;\xa1 \xfb\xc2\xba\x07U\xac\x9e\xf4\n\xc3\xa0\xa9\xe3*\x1c\x1a\x126;\xfcH\x1d&r\xcf\xb5\x9e\xe4\x97/_\xc2\xa0\xf6k\xb7\xf6k\xbf\xf6\xebi\xfd\xbb\x83\x10\xd8\xf6v`:]\x83\xe0\xb6\x03T>\xbd\xa8q\x17\x0c\xe7\xab\xa0\xa9\xcf\xbc\xb04\x06\xfd\x10\xfa\x1dc\xdb\x9c\xd3PPW*\xed\xc2\x97\xdd;\x97\xf3-e\x05\xc7\xfa\xa9\xef\xf1\xd7\xea\x9d\x17V\x8b\x1eP\xdfH\x9d\x88\xe2\x04\xd2*\xf5\xc6 \xba\xa3\x0d\xe1\xa4f\xe6\x02\x0d\xf3<\xa1\xe7)\x87\x04j\x92\x9e\xc8\xb0\x80\x0c\x87\xfe\xee\xc2N\xea@\xf7\xf3\xc9}\x82\xd4\xf4!\xc8\x82\x9b\x1a\x92~\xa8O\xf2X\x10\xd6\x8e\x13\xbb\xca!\x864\"\x01\x0bXV\x9c\x16\x17\x10\xce\x9c\xab\\\xeaK8x\x8bx\xf2\x89\x1ag\xa7>\xde\xb7\xaf\xb0\xc2v\xa1y\xa3zB|w(\xe6,eZ\x85\x90\xa8\xd9\x96\xe8\x18\x82\xb9d\xdarn6\xa5\x8bo%\x02\x88bS\xdf\xe3\xe3\xa9m\xeb\xe7\xf5AJ\x0b\x01\xa5|\xf2\x83\xe7\x86\xc0\xe3\x1a\xe1\xdb\xb6C\xc88z\x8eDWH\x1d-F\xa9{\xaf\xe3\x98\xdeu\x13I\xfaB\xfbU\xb9\xb0\x08\x07\x16\x0c7D\xe2\x15_$\x91\x93\xa4\x16^\x8a\xb8g\x92%;\xa6\xf4\xa0\xff\xd2\x15:\x99\xd8\x93\xcd\x1a\x02)Mx\xe2\xecd\x9a\x91$\x9f\xef\xc0\xb4\x95\x02\x0d\x01 \xa5\x0dM 1\x8a\x00\x8d\x9er\xfd\xa4r\x832\n(\xa9\x9b\xd0\xfeZ\x9al\x0d\xc3\x0f-\x99\xee\xcb\x17\xa5f\xa8n\xac\xe5\x8c\x87`\x89\xef\xa2\x9d\xb0\xfc$l\xd4\x01\xbd\x16\x97\xc40\x84s\x95q\x81\x13D\xd7<%\x81>T*\xa8@k-p0\xfe\xdf\x7f\xafzq\xb5\x8d|\xb2\x0c\xd0Q\x03\x8d\x13}\xa6\xbe\xc7\xebUJ\x82\x10C|\x18Q\xae\x04\xe4\xaa\x93\xc6\x96\x97q\xfcS\xe5\xf6\x00\x0b\x96\xe7P\xcc\xa3\x04ny\x8de\x94}\xf2\xc4\xb8P\xb9\xaa\xc0\x86\xcd*\xd1\xeeH\xad\x05\xff\x91\xe2\x95\x19\xde!\xa4b\xe1\x91\xbf\x93R\xf94\xc5\x01{A\xa8}_S\xa9HM\x91\x05@J\xa3T\xd38\x9aJ\xb5@or\x10\x1a\x82\xb0X\xc1\x04WP\xae\x8aX\xdaL\x1e\xf1}8*\x05\xbc\xa1<\"\x8f\x1cz-\xfe\x7f?\xd0u\x7f;\xa8\xec$gQ\x02\xd01\xa3\xa4\xdaJ\x9a\xc2C\xe2\x8f\x1a*\xea\xc6\xcbk\x94\xda]\x14?\xb0\xea\xa7\x9b\xa1 \x1ew\"(Z\xc3\xc4\x85\xa6\x80x\x00q\x8e\x81s\xe3\xe5JdH`6\x1d6n b\xcc2\xd2\xca\x8c\x96\x82\xd6\xf7B\xb8#\x8b\xa7Y\x14'^\x083\xb2T\xed\xcf%Y*g\x17\xc2\"\x109S\x8d\x8f\x13N\xaa'\x0deWd\x99\xa467AX\xc6\xbd\xde\x8au-!^\xeb\x8fo\xb3\xb8\xa8]\xbcn\x99/\x91\x08\x96\x9f\xcc\xa88\xb9_\x1b\xd6w\xe2\xbc\x8a\xc6\xb5E\xceP\x18\xeeM;\xc5\xb2\x8e\xeb\x06#\x1a\xef\x8b\x04\xf2\x8c\xab\x8cQ9^\\X\x17\"\xea!|\xeb\xc9X\xc6\x02\xc6\xd5.\xa0A\xac\xb20Pes 24\x00\xd4\xb2!8O\x05\xc4$1\xc1P\xb6\x14*j\xc5Jk\x1c\x8e\xbeBt\x91\xd1@k\xe4\x12\x1d&%qW\xa1\x0ej\x15^\xc2\x80W\xda\x11\xcd\xbe\xf3+\xfa/x\xcc\xad\x95b\xa2f\xd1\"g\x80\xddB\xc6\xf2U\x9a\xe4,\x04ek\x9e\x98\x17\xb0\xb5%n(\xdd\xde\x96\x93\xeb\x8bl\xca\xbc\xbdMw\xe3\xb2\x05\x88\x8aT\x15A\x08W~+5\x13\x08'\x10L\xbc\x17\xe7\x82\xc1\x98\x10\x11!\x9a\x06y\xed\xdcV-\x84\xf9\x8a\xa4 \xee\x8e\xee\x9ai\x93l\xbb\xf5\xb8\xd8\xb4\xdb\xab\xa6n\xab\xc3.\xe9\x89\xbf\xbb\x9d\xfdJ\x9e\x15;\xb1$\xfed7]o\x07\x00\xac`n\xba\xb1\xef*c+\x96L\x15P*/=\xb3D\xe4\x98iP\xa1\xf7\xc6h\xc2\x97\x0b\xe4\x91?F\xc5%\x1cA\xe4\xeb/\x02\xb4\xe3\xab~\xd7-\xb2j\x9f\x1e\xc2( k\xaf.\xb1\x8a\xf0\\J\x1c\x04OCeu`\x8b\x03\xa5\xce\x1f\x88w\x06W \x90^\x9e3|3\xc7%\xa1\x95w{\xc8\x8aU7r\x89\xbc\xcd\xf3\x03\xebR\xdf2\x82\xb1\x18\xf3&\x9d\xd5F*\x03\xf7\xdaWL\xd4\x90Jz\xc1\x1f\xc2\xc9%\xd6b9\xeb\x1c\xbdR\x11\xce\xe3\x9c\xfeh\xe0\xfe\x88U\xcc\xa5,\x87#lIXq(\x89Q\x96\xe1Qi8f\xd8^\x19\xfa)8\x90\xd6\xf0j\x11KvA\x18\x13%R\x92%p\x18\x9d\xfd\x9c\xfcB\xe9\xf0#\x0f\x0b'\xa8S\xa8\xcf\x9c\xde,\x9b\xce\x8an\xa5\x163\xb4\xff\x1cb\x0c\x15\n\xf1\xf6v\x00\xd9(\xbet\xc1\xa0Qak\x19\x0e\x01I\xa6nd\x9c\xc3w~Q\x9d\x9f\x0d:8D\x89H[l\xf9\x99\xca\xd9\x13\x850\x08\x0c@\xec\xa0\xe4cc\x93d~\x14\x08\xe5_\xa3\xfe\xa5\xb6{]\x0b\xdf\xb49S\xeb\xc6\xb5Ib\xcek_Vn\x10\xd2p\x83\xc60A\xd1\x05g\x12\x94\x82\x98\xdb\x00\xadT=(\x02C\xf0l*FRe\xb3\xa2\xdao\xc1\xe5.B=\xe0]Q]\x89\x9c\x11.G|\xe7R\xef\xc5\x85\x88\xa5\xc9\xc9\x1c\x0eM\x99\xa6\xec\xca4}\xcey\xa9<\xd4\x04\x853\xb9\xa6\x9b\x1c\xabM\xeb\x1fM\xcb\x93\x0e\x0e\x0d\xcc\x08\x0dU1\xdav\xb4\x98\x19\xde\xc8@\xfb\x9d\x00]\x9e\xb9\xc6QS\x9d2\xcc`\xf7[1\x15\xa4YJ\xdd\xd0D\x19\x1fY\xe6'\xf5\x1b\x88\xf7\xa4\x01\x12\xe0\xd9*\xd1<\x08(;CC\x0f\xc5\xb9\xdb6@U\xaaV\xbe\x8b\x04\x87\x0dr\xb2B\xc7\xd1\xb0E\x82\xb0\xe3>\xc2\x83\x1b\x99w\x87\x05e\xfd\x1c\xd1\x14s\xf2\xab\x0e\xd3\xbd\xcd\xa2\xd5F\xa7\xbb\xfb8\xef|\xf6g\x8e#\xa2<\x1eR\x8c\xc7\x83\x0c\xa5\x10\xa7[\xc5^NN\xa6\xbe\xc7g\xb3bS\x90\xc2}R\xf7\x97P\xba\xf8f\xc9\x99 \xcb\x87nnP\xf2\xec\xd6\xaf\x0f\\Z3p^c\x16\x9a\xa9\xb6\x8d\xbc\xa5&A\xf2\xd6%,HW4\xfe\xe8\x90P\xc2i\x0d\x14~Z\x9b\xa3\x90SS\x8e.[\x89\xe17R*\x95QS\xafY\xef\xa7B\xa4\xf7\xcd\x0f\xb0\x9e\xb2JQb?\xce/\x0d\x04\xd1U\xba\xf1R\x90\xa4\xb6l\x806\x93\xba\xcf\xd4<\xceG\xe9%\xd4c7kR\x81,\xf4UE\x0d\xa9\xdb\x1c\xee[\xd1K\xab\xcb8\xf3/B%3=\x85F\xc7\xf5\xfe\xca\xe1\xdc\x80\xfa\x1agt]^1\"\x83\x84Hp=\x8a/\xb5\x9d\xde\xbb\x8a\x93\xa9\xa4n\xbc\xa8\xc1#\xa7\xd0\xbd)\xdb!\xa3\xa1\xd0X\xde\x1f\x16\x81\xf2\xfe\xce\x14\xe7Z\x89\x11\xf6Di\xda\xd3\xc5\xddD\x91\x90\x9ao7\xe9z\xc2\x92\xf5\x92e\xbc.\x97\x13lj\xb3\x91k\nEak\x17G\xf6\x1c\xeb\xb3C\xbf\x8f\xf1,K\x97\xfcT\x86Cx\xfb]UV\xcf\xac\x10b\n\x1eG\x82\x05C0\xae\xe5j\xb0\xe3Mti\xa2-\x1b\x90\x88\x99Q\x16\x94\n\x83\x94<\xaa\x1b\xb4,_\xc9Q\xd7?\x97~,\x1d\x0c\x8f\xee}\xd7\x03m~D\xee\xd0\x02\xe23K;M\xbc\xaeZsn:\xf4\xb2\x8e\x84\x9f\xde\x11:\xe1\x94\xd6\x9b\x1b\xf4\x83p\xae\xb1\xb3%\xd3\x93*yA9Y\x08s\x9d{\xba6i\x17\xa7\xd6\xc0\xfcF\x08\xd4?\x96\xaf\xfd\xf2\x04 ;h\xb8\xb7\xe4=\xce\x11\xe7\xcb\xf5 &bv 5(\xf3e\x1dV8(\xbc~E\xd0\x92\xfa,\x87\x9cU\xfbYzd\xb5\x10\x93{\xc3}@\xf3w\x99\x1d~\xc1\xf2\xa1\x996\xb6`\x84u\xf8\x96\xe5\x1d\x90\xdf\x12#\xb0\xca\xcd)\xd4+\x08]Vs\x1b\xc6\xa2\x9aNU\x06\xf9\xe9\x9ca\x87\x0c\xc8\x96\x95\xa1g\xaa\xfbvDd\xafL>\xabG\xcf\xca\xd9B\x04\xb5\xe4\xff\x7f\xf9\x02\xb7q2Mom\xfa\x92\xd2\xe1\xef\x91\x93p93\xd1Y.\xa0\xc4\xb4xZ\xf9N\xf5\xc6h\x89\xfd#\xd2K\x07x\xf0\xcb^\xce\x8a\x8bx\xc9\xd2u\xd1Q\xccI\xd8-\xc4~*N\xb0\xeak\x8c\x87P1@!\xe0\x00d\xa1\xa5\xb7\xc0~_'\x05\xcbn\xa2\xc5=;V\x9f\xd3=\xabR\xa2k}d\xa8\x80\xa9}\xd0*\xffH.\x1f5\xb1\xbe\xd5|\\S\x97fl\x86\xb6\x91\xba\xec=3\xe6k|\x84\xed\xb6\x81\xa4\xb6\xc6\x02\"YX\xe2\x011g\x96d\xe9b\xd1EA\xa4C\xc7g\xbc\xb9\x05\x93?_OQ\xfc\xd0_\xd9\xf8\xc5{['D\x7f\x0f\xd2\x99i\x0e\xc7{\x1b#\x9c\x8f'E|#\xb4\xaf\x91\xfa\xf3[:\xa7/\x08\xe5M\xaaV\xd5\xaeW\xc0\xcbC\x99S\xc9l\x15\x0e\xa1\xda2~+/\xcaz\xe34Q\x93\x17\x97\x12\xe5o\xea\xb6\x87p\xb9\n1\xa4\xd5n\xa0\xf6\xdcr\xc9\xa6\xb1\x08\xce\xd2N\xc2\xea_Ta+*Rh\xd5\xe08X\xb2.za\xb9\xf36\x1c\x82\xf1\x0d9\x08\xbbNm\x18\xf5\xe2\xea|\xe8\x94\xe0lc\xe6\xd9\x11S-Eeb\x9c\xebq\x88\x9a\xf1SY$\xe1\x9d\x82\xe7\xc16\x17\x82q\xbeE\xfa&\xbd\x15 \xc9|\xa7\xfd7\x1a\x11ys\xf6\xd9\xa3\x8d{D9FBj\xa9\xb0\xd3\\#\xca'q\xdcX\xe3*N\xa2\xec\xae\xb9J\x94\xb3\x83\xfd\xe6\x91L\xf2\xdd\xb6\n;-5\x8a\xd9\xe0`\xc1\xda\xea\xec\xb4V\xca\xa2[G9h\x1e\xda\xfd{\xda\\\x95\x1e\xde\xf6\x16\xaf\xefnG6,\x8a\x931\x08\x95B.\xdc \xac\xab'\xb8\"\x81\xed\x0c\xbc\xba\x90\x92S\x11x\xd6r\x11T<\x7f\x1e\x94\x03s\xb6\x0c]p\x17:\xe1\xafz:\x0c\x12\xba\xa0!tBE\xe8\x88\x8e\xd0\x15%\xd5\xa3M\x03k\xb7\xcdd\x11\x15q2h\xed\xbdq\xf7\xaaG\xf5-\xdbl\xeb\xbaq\xbbC'\xd2\x02\x1dh\x9cz\x94\xba\xae\xc1\xe8\xa9mO\x82r\xb1h\x0e\xb2\xa5\x1eN\xb3}I\xb4\xeb\xf4ZD\xa3\xd0R\xd8\xea\x0f\xa5#\xa4n&\x1d\xd1{\xc5\xe5b\xed\x989<\x94\xd1\nE\x120\xdb+\xc4\xfb\x98|J\xd2\xdb\x04\x14\x15\x18\x82\x18\xb6[{\x88V{uJT\x05v(#\xd3Q,W\x07\xb4\xc7F\n\xf6\x99C)/\xdb\xe4\xac\xd3B\x80\x8e\x88\xd1\x08n#\xd7VR\x81\x1d\xcc\xe2\xc5\xe2M\x84z\xba\xf5\xfd{i\xc4j}^\x93\xda\xbcf\xa2\xc7\xbd\x8dzlDX]\x89),\xc0\x0ea\x15\"\xe7\xe4k\x1d\x9b\x92B\xed\x17\xd6[Dy\xf1\x8e\xa1\xa0\xadB#\xf2W\x17i\x81\x92\x92\xfe\xeed\x1e \x9f:\xdd\x1f\xb0\xa6\x0d,\xff,\xcf\xaa\xc8&\xf3\xa5\xa9\xc5\x8bC\x18\xec>QIb\xe0\xe5Kx\x0c\x87\x87p #B\xe3\x9b}\xfef\xb0\x0fG\xb0\xa7^\xed\xf1W{}8\x82}\xf5\xea\x80\xbf\xda\x85#\xd8\x19\xc0\x10vv\x1b\x87\xb4v\x1c\x9fJ\x1bXM\x7f\xa7\x0e\"[\xca\xdf\xc4\x05\x1a-Ov\x9f\xf2\xbd\xec\x0f\x9e\xed\xc2\xf7\x98\x14<\xd0\xac\x99\xeaK\xe1\xfd\xdf\xff\xd7\xff\xe9\xa0\xb2\xe8cTU\x97\x16\x83\x9ak\xd8\xa0\xe9h\xa5\x062p\x0dd\xd08\x10\xa0\x06\xb3k\x0c\x06\x7f\x9b\x1d\xee\xba:\xdc\x95\x1dv&\x9e\x85T\x88>\xa7\x90L\x93$\x12t\xb0\x1f\x1aX\xffB\xf36\xc3x^\xe8\x97YCy\\V}\x1f\xf0\x0f\x03c_\x94\x89\x0d\xeb\xfcVho*\x11\x17\xac\xa9\xa32\xc2\x99\xbe\x9f\xcb\x11\xefh!\xd0\x9a\xf7^N\xaa\x00\xf8z\x95\xd9T8\x8a\x07\xf0\xaf\xb0\xcb7P\xbfI)_\xa5n\xf4K\xf2\xee\xb6#i\x0e\x04\x80\xd7\x91\x93y\x94\x9d\xa4Sv\\\xf8\x9a\x0f\xac\x199Z=\x18b\x9f\x8b\xdd\x8f\x1f\xef>;\x004\xcc\x7fq\x08\x8f\x0f\xf6\x06\xcfj&_\x06.Y\x04m\xdfX\xb8Q_\xa4-\xd6 \xb2{i\xd6\x19Xu\x06\x97!$\x95\xa3\xfa\xce\xe0\xfeF\x1e\x14\xde\x9a3\x19\x103\xd9m\x9f \x1f\xa5c\xe1*4C\xa87\"\xd2\xc2M1\xeb7\xe2G\xda\x81$n?\xa8\x9c\xec\xf5\x8d\xd4r\x11\xe4&\xc7\x0d\xdc\xcb\xb6ksj\x10\xe8\xdb\x01\xc1\xc8\x95h\x84\xcc\x84\xdcbj\xfc\xd66\xdb#\x89T_z\x9b\x1c\xd5\xd6J\xb2\x1a\xd2\xf1\xcc71b\x0fv !\xb0bOY\xa4%j5\x1a\xf1\xa3\xd6\xf47\xed\x87 t\x0c\xbf\x86iI\x0b\xcd\x9a=\x1c\xaa\x91[\xe9\xa8\x11;\xcaA\xf7C\x04\xb0\x81\xa9\xc3\x16lX\xb9\x99\x1d\xc7\xf9\xd0\x0c\x8ci\x03\xf3\xd4\x06\x0b\xada\xf5WQ\x8f\xe7\x06\x87\x10\xd75\xd3\x8a\x91t\x0b\xff\x95\xcdmy\x06\x95\x82\xa1\x01~\\\xb6\xd0t|\xee\xb4\xff\xe3*\xef%\xfab\x96\xac\x99b\xe2\x85\x9c\xe3\xe8\x18t\x03%\xd5Mhs\xbb\xf5\xbd/\xec\x14\xd1\xe5\x9bD\xa3\x04c\x92V\x00\xd71\x89\xf3\xfc\x9c\x10$\x81\xe2/\xeao\xf0:I[\x91:\xd4\xa5\x88\xd0xK\xf5\xc0\xf8\x8f\x1cV\x1d\x9d\xebc\x92RL\xe3]\xc2\x8d\x99\x17\xbd\x81\x01\xae\xec\x93+\x8aAs\x0e\x19\xbc\xe0M(\xd2hW\xba\x91\xd9\x03\"\xbf\x18e\x97\x0e\xfe#E\x0d}\xd9L\x8a\x8e\xbcB_\xaf\xa1@\x8aG_\x08)\xdd\xc8\xce\x0e\x0e\x86\xaf\xde\xce\xae\x10\xb3\x9b\x06\x86\x8c\x956\xb2\xa0\xf3\x18v\x7f\xfd1\xc8\xb60\xf8\xce\xa1\xca\xd2Y\x1f\xd5\x1e=*\xd5y}\xfb\xb8M\x8bQOhly\x9b*\x96\x01\xfb\x8d\xaf\xad\xf3-\xb1\xa9\x8c\x1e\xa0\x01v\xc0O,\xcaMn\x0c\x9a\x05\xef\x0b\xcfijh\xf5|a\xf5\x0d\xa3\xa9\x17\x9a\xa9g};\xbe \x08\xa9C4h\xe4\x85\x1eT@\xa9C\xeb\xde\xc3\xd1\xc4\x98\xfa\xa45 \xc68\xa5\xeeu5\xa3\x9b\x1ei9Nn\xb4\\Pt\xa63LcS\x164\xa9\xd7\x11\x87\x11\x04\xb5\x84*\xf5\xb4 \xb1\x9d\x01\xabfu_Zc\x14Y\x94\xe4\xb34[\ns\x0c\xca3\x06C\x83_\xa8z\x1dl\xa7\xc0d\x9b\x8d^h\xa9*\xe9\x95\xb5\x9a]9*\xb1\x0d\x0f\x9c\xc9\x95[J\xdb\xca\xea\xf2\x983v\x80\xe068\x84\xae\xa2\xc9'\x15\xaaf\xb9^\x14\xf1j\xc1\xa0\x88\x97,w\x86\xbcW\x03\x99\xaf\x93O\xa5\x9bJ9\xba\xea\x8d\xcc\xfaW\x94W\x852ut\x88Y\xf8\xdc\x93M\xbb\xda\xc5\xf3'5Lw\xfc\xd4\x8al\xaeLd\xe1\x05\xa4D\xe0\x8d\xaa+\xdf,\xb6z\xfcZ\x99\x81Ri\x04\x19\x9bj\x88C\x99I\xeakN\xd7\x90`\x14\xf1.\\\xc5\x1c\xf4\x8d5*u3\xafT?/h\xfb%\xc2\x13\x83\xaa\xa6E\xf3h\xcc-RNT3y\xaa\xde\x1d\xea5\xdc\xa9Ff\x8bu>\xd7\x1a\x10\xbf\x0fU\x89\xb2\xbaG\x9b\xedU\xc6J_\xbd\xa8M1J\xf1S\xca\x1d\xa3\x8eg\xe4\xc8\xf4\xd1\x1c\xe9\xbfj\x99\xd3Hnl]\x12\xd7\xfa\xa2p.r-\xc9U\xb5\x7f\x9a\xe7\xb1v\xb1}\xb5\xab\x14\xc2\x88\xd4\xe6\x12j\x99GY\x15\xee\xde\x8a\x14\xa0\x0eL\xeb\xa2\xe3$Z,\xf86\xac\x16y\x9a&\x0cn\xe7,\x81\xdb2\xa9\xd2\xd6!\xf4\xcd\\\x86B\x8bi\x10\xcd\x1au\xdc\xb0\xbb\xbc\x88\x17\x8b\xdaV3\xbb,!C\xb8\x03TB[j\xa5V\x0b\xb5w~,\xd8\x95x\xc3\xe0\xee:\x816']\xa3 \xa5\xdfS\xbd}\xcb\x9d\xac\x1ay}0\xb5\xfd\xd6&)X\x00\xae\xbev\xc4\x98qvk\x8b\xb2t\x97ug\xb3\xa63\x13\x85\x13\xfd\x80\xe1P\xa9\x1dB\xac|\xa3]\xb7\x17!le\x06\"\xd1\xf2Q\xe7#\xc7\xcf\x8c5\xc2\xf3\xe5\x17:q\xbe:Al:\x174\xdf\xaa4\xc2\xb6t;)t\x88\xe25\x82\x02\xb8\x88\"\\cW0\x0c\x93\xc9\xc0\xf4-.\xcb\xd7\x1b\x0dU\x93\x15\x03\\\xf4\xea\xdc\x960!\xb6\xb7A\xdf \x89\x8e\xa9\x1at\xfe\xccd\x14\xed\xd6\x8c-\xd6l\x90Q\xf8\xc2fZ\x10Y\xe1Cn\x12w\x83\xb8\xdc\x8b\xd7\xd6\x98j3\xeb$G_\xcc#\xa9KEiv\x1aM\xe6\xf5\x8aq\x95\xdf~\x92\xb1\x1a.tK\xdf\xab\xf0*\x16D\x93\xa4\xaa\xd2\x8a\xb4\xb4\x1am\x03 \xe7\x069\x8eug\xb4iV\x10M]\x12\x99`\xbe\xc08\x80\xc0F\xc9\xa5U\xf9\xab/\xf3f\xa3\\`\xaeUX\xd34\xc2}\x97\x8b\x84g\x00\x7f\xfb\x86&5\x0c\xd0Sen\x92\xb7\x16\x89\x1d\xb9jq\xfe.z\xe7c\xfa_\xd4b\x14B\x7f\x817w\xdf\x7f/\xd5\x15;\x98\x9b!\xc5\xe8\xd6\xc32\xfc\n^ \xb5\xa7O\xef4\xc7\xba\x0b\xce\xc1\x93\xa7\x81\xcf\x87$\x916\xca\xf3\xf8:\x81!\x16=\xfbV\x9b\xc2\x10\xd2\x10\xb3\xc9\x85\xb0\x0eA\xf5h\xec\xadNv\xbd\xd6\x85\x05\x7f\xb4\xb8 Evg|E{g-B\x90Q\x00I'\xacI\x9a\xcc\xe2\xeb\xb5r\xc3\xea\xd3\xcc\x7f\xe4t\xd2js\xe2\xc2,\xd8C0\xcc\x80\xb5u\x85IT\xda\x8fU\xa7\x93\xb8\xf4Xhw\xb9\x99%Y7\x0f\xdd=\xec\xfa\x90\xab\x91\x88\xd0\x86$\x14\xc3\x8d\x13\xd4\xa35\x0cJ\xa6\xa5.\x0b\x1d!ez\x0d?\x13\xf9\xc1\x05K\x81\x9eZ\xd5*e\xfa\xad\n^\x17\xc9\xd4\xd2\x83\x83 \xc4\x8c\xa8\xa3\xcb\x10\xe2v\xaa\x1aR\x1ap\xce\xf9\xacG\xec\xb2d\xe6\xf9\x8fz\x15${\x05\xf6\xf3\x1c\xd8\xce\xce\xf3@\xb9\xb9z\x91\x07\xdb\xe0oo'A\xa5\x82\xda;0\xe5zM\x8f\xa2\xdc&|o\x96\x88\x9c\xb9XTJ\x1c>o\xb0\x90Q\xeeC\xf0\x02\xd8\xe6\xff\xfcM\xb51K\xa4\xc3\xa68;+\xc7\x81\xe7\xf0\xf5y\x9de\xec\xbcF\x04\xc5G\xf9\xc6\xb1f\xaeD\xf2 \x9eZE`\xa9\x1e\xec\xbd\xc9\x9f\xc8OB3\x01\x95\x03\xfd\x81\xba^\xfe\xfa\xad\xc4I\x88\x1cT&u\x1a\xe9\xeb\x00\xaa\xaa]\xb3\xe2\xec6Q\xd5^\xb1|\x92\xc5\xab\"5\x0c\xa8#\xd7\x07\xef\xa2\xa5\x19\xd3d\xed\xaa{~\xb7\xbcJ\x17y\x87\x93\x89\\cA\x82\xe5\xd1\x9c\xf9\x85\x89\xa7('\xea50\xca@\xe4\xe7\x81bv*\xf1\x9b\xce-G\xae4\x7fpOg\xa1H\xba\x9eQ>\xb6\xfa\xd2\x93M\xa0\xa1\x86\xfd]\x1d\x81\\\xaa\x0e\xcc\xe7\xbe\xfe\x07\x9b\x89n\xe0SJ\xe8\xb4\x9c\xfd]\xbd\x95o\xdc\x15\x8f)\xfe7\xf1\x07\xfb\xe6n\x89iO0\xce\x9e\xde\x17I\xf9\xc1Fd\xc2\xe3\xfb\xa7\xa4v\xa3\xddK\x12\x0c\x19\x92+\\!\xbd#\xc1\x87\xac\xa9\xe5HF\xd9%\xfa8)_\x8a\x08\x05\x12\xf5\x85\xb5$I\x0b\xa0\xf5>\xba1\xfcr\xe8[[R\xdb'B\x10\xd4\xd3\xc8}\xf9\xe2P\xe0![\xefR\x10\xceY\xdbh;\xa1\x05\xcdH\x15!x\xe31\xcb\xdf\xa6\xd35\x9a\x9c\x98K\x89\x8c\x8e.W\x06\"\xde<\xda}v\x81\x88\xbdX9\x17\xae\xdf/\xd6\xd7q\x92\x0f\x1d{\x8be\x99\xab\x08\xb0\xed\xe9z\xc2\xb2|\x08~\x9f\x0b\xbar\xe9\xcd\xe2E\xc1\xb2\xee\xc4\x80\xf5>\xb1\xbbs\xf6_~\xd0c7,\xd3\xc8\xb4\x13\xb4`u_\xb4d\x0bD\xa9mT4d6Q\xb2?z\xb8f\"\x16aw\xb2\xefDg\xd6[\xb2\xec\x9a\xf9N \x19\xc5T\";\xdc\x06X0\xfe\xe1O\x0f\x8d\x08\x9a\x1e\xa3\xf2 N~\x0dtH\xe8pZ\xbf\x06\x805)\xb2.\xc2\xc5B\xe5\xb6k^\x97\x89\xcb\x0f\xf3m%\x94\x0f:\x0b\xe5j2\xa6\\./e\xec\xc9\x95\xaa\x03\xc3{\xfa;\xfb/>\x83\x85uG\xc5\x19\x9b!\x18WS\x0bv\xc3\x16\xc32`|\xadl\xc9\xf2<\xba\xe6Go\xe9\xe6\x8d\xb5\x8c\x1e\xff\xbe\xa2\xb7K\xaf\xd5\xa4\xe1\xb4`\xfb\x97\xfc|\xc5&C(z\x9c\xc98W\xda$\xfc\xf5\x87\x04\xd6\x91\xb28f\xf35\xe8\xc0\xb1\xaaok\xa2\x80\xd8\xa1\xf8b\x15 \xbe\xc4l\xba\xc2G\x87\xf6\xf0\xc9\xae\xa9\xd4\x7fH\xed!Er\x08\xf7\xf8\xff\x15\xf4\x80 \x87\x8e7\xd3\x11\xd2\xe4]q\x8f\xc6\xff\xdc\xab\xfe\xdc\x0f\x02a:\xf3\xf7'_\xb4!\xa3\xeb\xc0\xe8\x80\xc67e\xb41\xc4ZI\xc7\xbd\xa0\x17'S\xf6\xf9l\xe6{\xd2\xe21\x9dA\x84g\xbd\x9f\x07\xa6\x11)\x947\xd1/a\xc7\xe9\xf6\x7fS:q\x1b] \x07ft \xa3:S\x96\xb6\x98\x05\xa1\xf0\xbd\x90\xea\x1e\xf4i\xe7z\xfb\xa1\xab\xc3>\x92\xd8\xed\x0ebB\xadqq3\xe1\x9b\x88\xd0\x90\xd7\xcdh\"\x91i\xdc*'4\xb1\xab\xe5\xef\x970\xc0\x83}\x1b\xbc4\xc3\x18)\x05\x0c!\x1b%\xb0\x0d\x83K\xa3\xea\xae\xac\x8a\xc0\x0b\xc1\xd3kj%X\x80\xbf\x9c\x03\xfc\x1a\x82\x97\xcf\xd3\xf5b\nW\x0c\"\x97Z\xc3O6\xc9$\xe0&~\xbf\xe9\xfdD\x9c\xbdEO\x1c\xfc$\xa1\xd1nu\x1dD}\xb0\xf7TCZ\x071\x0f\x91_\xfcMC\xe6\x1b(\x8dkw\xfa\x14\xf9\x11&@\x9e\xf2s\xeay\"e\xeaj\x11M\x98\x9f\xb0[\xf8\xc0\xaeO?\xaf\xfc$\x04\xef\x9aW\xf7\xbc\x80\xd2\x1b({\xa2\xdf:\x1e.\xa2\xbc@ss\x11Yr\xb1\xc0\x1fy\x19\x16\xd6@+R\xb4\x10\x98\xf6\xd8|\x1d[M\n\xa5\x8b0{U\x0cl\xd0q\xf5\xea\x80l\xd3\xb1\x94k\xae\x8b}JXU\x9a\x16cm\xaa\xa9\xd6\xc1B\x8f:n\x1aB\xd9=oG\xe3\xc8\xbf\xc5$\xe9A\x97\x9d\x90F\x1cs\xb0a\xdb\xe5\x92}\x11\xdd\xa5\xeb\xa2\xdb={)\x88\xfc\x03\xdc\xafS8\xfeP\x1c2}\xbf\xbe\xdb\xef\xbb\xef\xd7\x9fv\x16\xe5\xffW\xe0\xab\xff\xbe\xdb\xca\xc6\x99P\xaahvM\xa3\xa8HaM\xfc\xd0X\xb3& \xb4\xb0\xab\xe6\x98\xa4\xd3\xb8\n\x96hm\xaen\xe7\xa3J/\x90\x86\x90\xf7>\xbe\x7fu|q:~s\xfc\xa7\xb3\x8f\x17-\x8a\x82\xfaQ+\x88\x00\x9e\xa0R\xb9\xa7S\xc2\xc6\xde~|\xfd\xe6\xe2\xb4M\x91\\\xefM\x08\xde\x9b\xf5v\xfe\xd3\xd9\xcf-\x9dX\n\xca^>Oo\x13\x9b\x0e\xa9\xa3b]j\xed\xabO\x8ay\x9c\\\xbb\x1c\xe0\x94\x16\x1f\xdb\x95\x87T\xd5\xc8\xdf\xf8\xd8;\x1ev\x1c\x0e\x19\xe1\xd8\xd8\n\x07 \xf5\xb7g\xafN7\x06\x07\xce\x8d\x06GUi\x99N\x99c\xfa\x18\xea\xdc\x1fy\xbcJ\xee]\xaa\xfb\xab\x84\x0f5\x13\xb1C\xd0\xc6\xd9\xabO#\xfd\xad\x1c\xa5|\xd9\xce\xd7\xcbe\x94\xdd\xe1\x94o\xe7\x91\xc8\x0f\xc4\x7f\xc4\xf99_U\x11\x86}\x9de,)~D<\xd5\xdf\xb8\x98-u\xec<\xdd\xfbUO\x1d\x82\x95\x13de`Z\x97\xe5\x92\xda\xe8T\xa5\x9aS\x07\xf6\xe8Z#\x13\xda\xf2\x86\x04\xb4\xba\xb6&\xc9\x80S\xdd\xb50\xd6\xa5 {\xb4\xd6\x8brw'i\xb6\x8c\x16\xf1_\x19\xba{\x05\xd2\xfe\x1d\xfb\xd6wp\xae\xef\xe0\x00\xcb\xeb\xaf\xf9w 9\xcc\x1a\x0eu\xda\x8d\xa5\xdd\xab.\xa0\xd7SX\xe9\xa6\xb1pT\xff\xe9\x8e\x9e\xd3>kj\xef\x1a\xea\xe5\"0\xa6jo\x1bA\x94\xbaK\x06\xb6\xfc\xdb\x81\x1d\xdfBf\xc3c\xd3\xb8Hk\x18\xd2\x89\x94T\xf2\xcf\xdeAG\xd7/N\xa5\x8c\xa1\xd0jt9\xc0\x14\xf3\xe6d~\x12\x8c\xfa\x97!$\xa3\xc1%zc\xfa&EoTm\xab\xbb!\xd6\x13\xcd\xda\xc2\xa90\x14\xd7\x90#\x16\xfec\xd2\xc8Y\xa4\x0e\xac\xf7\xf8]\xfd\xaf\xce\xb0zb\xd2\x0c\xa9\x96x\x16\xf8^\\\xb0,\xc2\xa5\xb0\xc9\x9b\xe1K\xd9\x06o\xc7\x8a\x9b\xa1\xf4\xfd\xac\x87\x0dk\xc9\xc71{\xdaa\x8d\x9f\xddp\x8a\x8dsI\x8d\xb0\"\xf6\xfa\xab\xe5\x1a=\xb9\x1ce\x97f\xfe\xbdX.b\x93\xa4\x06\xaa\x1f#*Q(\xa1\xc8)NM^\xa5\x1a\x108\xb1[oA\x83 \xedx\xd3\xd9r_\xc4AB?\xe6*\x84\x93\x19oE\x913\xf3=\xbdi4\xc0\xd1R!?\xccb\x02\xa6X\x86Y\x97\xda\xa0\nMr\xb0z\xa6i\xc2\x86b\xdc\x9d\x83^\x878\xb0\x0d\xba\x8f\xa86\x98\x1f;\x08\x03\xeb\xe0\x1e\xd5\x05\xcb\x7f\x05\xfe\xe9\x97VE\xe4xk\xea^\xbe\xdb,Z\x1d+\xfdBC\xee\xe8\x7fH\x85\xc5\xde\xaf\xcb:.Paa\x99\x94\xaf\xcb\xa2\x81Y\x94\xcb\xa2\xbd\xfd\x03Z\x97AD_\xfd\xa7.\xe3\x97\xde\x97$:\xadHw\x81X\x95\xec\x99%\x91,yj\x954i),!c!\x9b\xd9\xb3\xba\x9eH\xb5\xc6\xc0x?\x93\xefwI\x84j\x08S\xfaK\xd8\xb9\xd4\xf4,\x99\xa6g\xd1\xac\x0f\xb3\x10fJ\x06?\x7f\x7fz\xd2M\xefQ\xe6G\xd0\xa2\")\x81\x1b\xa3\xe9\xa2Z\x04-Ru\xa5\x08\xe8\xa3V\n\x01\xc7`>~x\xd3m,\xb2\xb3u\xb6\xd0\xfb\"\xc4\xf6\x86\xce\xfep~\xf6n\xa3\xde\xfe\x92\xa7\xa6\xb4u\x96MY\xc6\xa6\x9a\xee%\xe8\xdc\xff\x87\xd3\xf3\xb37\x7f<}\xb5\xc1\x18P\xf8\xc9X\x9e.n\xd8\xd4\xbb|\xf8\xb1\x8c\xcf?\xfep\xf1\xe1tc\xad\x0c\xad\x8fI\x84\x13\xbd]\x98J\x13\xdab\xde\xa2\xa4Qs=__\x15\x193e>]\xad\x14\x04\x0ehd\xdd\xa1\xf0\xfe\xf8\xc3\xf1\xdb\x87\x9a:\x9f\x9d{\xe6Y\xb4|\x17- \xd0\xc4U\x85\xd7\x84\xd6o]\x15\xdb\x85y\x13\xcc1\x9cg/\xce\xff\xe7\x92\x88 7!tB\xea\xbd\xf0T\xe6\xe7\xcf\xfc$\x9d\"\xd1\xda\x8a\x05g\x0dG\xb0\x16\xaa\x88$Z2\xa17\xeby\xb0\xad\xde\xc6\x89|\xc7?\xde\x11\x05\xaa\x1d\x1f\xf3\xf7\x97_\xc4\xf61\xca\xe9\xea\x02\x8e\xc0\xc3\x19\x8d?/\x17\x1e\x0c\xe5/Z\x7f\xa0i\xf7\x18\xe6\xf3F\xeb$7\xd6dA\x08#\x0f\xa1\xc9\n\x86Wv\x93\x10f\x97A\x08yg\xac9}\xfb\xfe\xe2O\x02w\xc6\xaf\xdf\x9d\xbc\xf9x\xfe\xba\x95\xb0l\x84EoY1O\x89\x1a\x0f\x83Kq2Y\xac\xa7\xect\xb9*\xee\xfe\xc8Ak\xf3-\xc2\x1cx+.y\x1ee\xc2v\x1be\x89\xef\xfd\x1ce \x06\x1el\x02\x08L\xd0\xe4\"I\x0b\xb8f \x17^\x19D\x80c\xfb\x1f\xec\xae\x87\x16d6\n\xe4\x18\x1d\xd7\x81#\x0f\xb3\xe8c\x04@\xce\xd9g/\x84\x9c\xaf\xfd\xba}\xed\xffx\xfc\xe6uE3\xce\x7f\xbd\xe5\x8e\xf3\xb3\xe3\xf3=z\xad5\x05YGH\x04\x84\xfa\x9f0\"\xe7\xb4\xe3\xd1\xe7\xe5\xe2Q\xdc+X^\xf8\xb1\xd8\xde\x1c\x0d\xd6K\x96\x8f\xc5\x96\xa4\xbe\xe4{x\xd2\xe3\x9ca\xc4\xa1\xf3s\x8c\xf3\x8bd\xcc\x10ArB\x18\xb1\x86!6\xdfcl4]c\xb7_R\xd3\xefx\xfb1S\xd6\x8f\x1a\xed\x10m\x95\x8e\x15\x94\x01\x95K\xecV\x18\"\x8e\xb0\x9bh\x11\xf3\xc9\xbd\xe7\xad\xa3\x91\xfb\"\x84\xb4\x835\x18\x87FAR\xe4\xa2\xa2\xc8!(\x0b\x85Ks\xfe\xa4\xd1\x93\x1d\x15\xa5}\x7f\x08\x93\xfco\xdc%\xdavx(\x1cH\xdaq`t\xd9\x15\x07\xbaX\x03\x81\xc5F\xd6\xacCj\xdd\x12\xb0\xdf\x18\xf0\xe7\xa7\x17\x9c\x9b{\x7f\xf6\xee\xfc\xc1\xb8\xb8\xcc\x8c\x07\x035\x1e\xce.\xc3k\x9d\xde\xd2A\xc8\xd6\x0ef\xc3_\xa3\x13\x1d\xc2\x07\x8e\xc0\xd0\xea\xdb\xa0\x15\xd6\xd2dP,\x8e\xfcC\xd1V/!\xcf\xc6\xd2\x90_T\x92? \x9e\xaa\x88\x8au\xce\x19\x16U\xb5zS_\x9bP\x96g,_\xa5I\x8eY\x02\xb2\xa07g\xd1\x94\xa19\xd2\xba\xfc\xfb\xcb\x17K?\xc0\x17c\x824\\\xe3}\xb1\x1d\x8e*i\x08\x91\x8b\xdd_;(\xe4B\xc1\xae\xf7\xc3\"\xbd\x12\xda\x97iTDzPm\xbb\x8e?A\x8a\xed\x1aD\x08^\xc1>\x17\x9cr\x88\xd6\xf8\x112\xe9\x88\x95\xff\xf1\xf1\xf4\xbc\xedJ\x7f\x03\xa4\xfc\xaf\xcd\x902\xd6\x90\xb2U\xec\xf8\xaf5\xcb\x0b9\xe9\xd8\x05\xf9.\xa2\x05\x9f\xf9\xdb\x8f\x17\xc7\x17\xa7\xaf\xfe\x91 \xb0\\\x17Q\xc1\xa6\x1f\x1e\x0e\x10\x929<{\x7f\xfa\xe1\xf8\xe2\xf5\xd9\xbb\xf1\xdb\xd3\x8bc~B||0:\xd5$r9\xa4\"\x01\x92O\xec\x8e\x96\xa6F\xad,\x85\x83[\xeaz\x1eYN\xa0\xe5J(V\x0e\xb5\x0e\xae\xcf\xf3 \x080{dY\xbd\xd2\x0el\xfcI\xab\x90\x8d\x9f\x1eUX\xe2\xaa\xb7\xe0\x87ll\x9f\xaci\xd0M\x1b$\x98\x87\x87>\xc5\x9a\xb0\xa3qOL\xd9\x82I&C'\x87Y\x08\xe9e;\xde\xab\xc9<\xe8\xd6\x7f\x98\xb9\x94{\xbb\xe3T8-;?\xf9\xe9\xf4\xed\x83\xadI>\x993\xeat\xfe&*\x96\xf2s,\xd6\x11\xd5\x13\xfdTT,\x13\xca\x87/_\xb0\x9e\xbc\xb6\x1dR\x1fxc \x83s\xf1\xe6\xb2\x9e\x97$(\x7fv\xbe\xbf\xdd\xa3c\x99=\xdb'4\xdd\xf2\xb67_\xb1I\xccr\xaf\x8b\x1d\x00\xb9\x16!\xb2d\x99\xcf\xd0_?/\xb2\xf5\xa4H3\x12zZ*\xa8HK\x0f\x7fx\x08~\x82mD\x01\xdf\xdb\x98\xdbh\x08\xa9n+\xd0\xe9*\xe1\xa6\x16\x87\x15\xe7\xb8\xff\x8cV\xd8\xef\x99 \x91\x86\x85\xfb\x94\xce>\xf1\x07V\x948\xa9\xb1\xa7\x14\xf6\x93\xde*K',78\xdbU\xc9\xfd\x94\x89\xf6k\xe5S,\xafg\xc0\xaf\xd7\x98c\x8d\xb7\x82\x9f<\x99GI\xc2\x0c\x85\xdb\x0d\xd6x\x15\xe7\xab\xa80\xc35/1\x1di\xed\xd55\x11\x80\xee\xae\xed*\xf7F\xa67\xd8\xb6\xc3_\x83\xd4\xea\\\x1bWJ>s\xe6\xbeW\x97Z\xd7V(R\xf5\x08\xba\x82\x15B(|B\x92\xa9\xbd1\xa6s\xd5h\\\xc1\x1fu\xe1%x\xcez[\xd5\x88V|\xe7O1\xc6\xc1\xaa\xb1\xc9*G\xba\x8c\xd6\xcaQ{\xf0\x9c2lJ\xaa\xe8\xaa\x95\x11S\xb2\xbd\xed\xb8g\xbb\x1emo/[o\xda\xd7\x8e$\x1a\xf2\x06\xe8\xc7j\xe0\xa1\x15\xae:\x84\xcc_\x06!,\xbf\xd3^5\xc7\x86\xd7VG\xff\xc8\x93[\x00\x87\x90\xf8\xcf\xf6\x02\x7f\x16\xe0\xb5l#\xec\xd0\x94\xe1\"\x9e|\xf2#\xff\x0e\xe3\x94\x0ct\xfe\x0f\x86p\x83\xc6`\xbd$\xbdmm\x0dk9\x1b\xc2\xd0\xc2\xb12\x19N\xd8-\xcc\x83\x1e'{\xbb\xfct\xe2\x7f\x0czi\"\x8578\x84\xab\x10\xbb\x8b\xfc\xb8\xb7J\xf3B\xeeB$5\x03d>&\xbdh:=\xbdaI\xf1&\xce\x0b\x96\xb0\x0c\\\x01\x0b\xb5\x06P\xdb=\xe9\xc5K\xde\xe39\x86S\xcdU\xd0c\xf7\xd4&\xfa\x18|tt\xe3\x07\xca\xef\xea\xa6\x87\xf6\x88t\xa7\xa1\xab\x10\xb6\xc4\xc8y_^\x9ad,\x9a\xde\xa1\x1d\xc2d\x1e%\xd7\xcc\x838\x81\x85\xef\x89 \xaf\x1e_>\xf7\x88\xf2^\xb4Z\xb1dz2\x8f\x17S_\xfb*\xe8\xd9-\xb7\xe1p\xde\xcb\xd82\xbda\xa21\x91 \xa7\xdc\xa7\x06\xce\xd6\x16\xb5a|\xac\xb8\x88\x97,]\x17\x1aF\x84\xd0\xaf\x1f\xb8\xfa\xd1g}?\x84\x95q\x06pZ=\x84i\xd5\x04\xfe\xf5\xedq2\x1bM\xebh:\xea\x08\xc2\xcd\x9f\x9b!\xb0v\xb2\xd9\x18\xc9\xb5\xb5kBQ\x02\xb2\xeb\xb6\x8e[\xa0\xb7)\xb3\xb3\xfb\x94dvv\xfb\x8f\xef\xc3\xe2`\xb2\x10\xa4\x95\xa9_\x88|\x1b:\x9b#\xed\xedJK\x08[\xf1\x82\x91\xa2{3;\xa5\x98\xf8\x82\xf3\xc2\xa8\x05\xe3b\x92\xb4\xa4\xe5\xec\xc32\xce7\x8cs[\x8fu\xffd\xef[\x02\xda\x17\xba\xe5\xc0!l\xb9\xcc\xb9w\xfb\xbf\xa4Q\x8e>\x1eY\xa7\x8b\xa5d+\xf3\"\x9c%\x1d\xa1\xc5]\xa8\x8f\x89\xe1\xd40j\x8aw2\x9a\x13\xd8\xe3\x81\xccOC\x88\\\xb5\xa112\x85zn\xa4\xb3}1J/\xfd\x88\xd0\x10\x98\x8f\xd0\x0e\xa2\x8a\xc2Y\xb7=\x8a\xb3ztF\x9e\x0c$\xa3\x1e\xdb\xe0K=x\xeb\xb7\xeeM\xd3\xa4\xda7%`\xd5N\xf0\xf3\x00c\xfav\xd0\x80\xab'\xf3=\xce\x15\xcb\xc8\x1b\x89\x88\xd7 \xd2'\\\xb6exq\x918\xc2^\nM\xc0\xb7R_\x84\xc9\x8e\xe5\xff\x98\x0d\x87\x8b\xdb\x9b\xa1Q5\xe9\xc1>}\xca>1\xe5j\xa9R\xd83St\xca\xfc\x15\xe6\xa1,\xc4\xf0\xa7\xfd.g2\xba\x1f\xe4\xd4\xc9\xbc\x15\xa1d\xa9TP\xf5\x8dX\nb\\\x84\xdf\x19\x84(\xb2\xa3\xa7|\x8aQ\xe2\x82@Jb\xa1\x90\xdaa\x07\x06!J\xe9\xecy\x99o\x12\xc5\xbe\xed\xed\x05\xbc\x80\xc9s\xd7\x81\xc2%\xa4\xb5_\x8c\x16\x97\x0e\x82\xcc\x05w\xc2y\x81O\x01{\x995I\xc7\\\xa6_\x8d\xa6\x0e\xe9XO\xaf\xcd\xbb\xe1\xc2C\xee\xdf\x840\x0da\xc5\x99{QA\x98r\xceQ\x80\xb9\xe1\x9c\xfc\x0d\x0c!\xe6c\xc6@\x17\xfc\xcd\xe8\x92\x9f\xceT\xf8!\xebM\xe6\xaf\xb0\x83y \x00\xc6\x87\xf7\x9d\xfb\x13\xb5>\xf7E\xc2\xbd\xfdN\xbc\x1bq\x14{\xe31\x9a\xb9\x8e\xc7b\xaf\xe0\x9e\xe0\x8c\x88\xfc\xc0\x86z{V\x9cZ\x12\x19\xa2\\Z\xa1\x12V1Zb\x1a\xc3\xbf\x01\x95\xd7\xa3\x82\x0b\xf7\x1b\x9a\xb5k\xf4\xc9\xe4\xc5\xd261\xab9\x10\x16C\x95\x9c0\xc4\x0d\xc1\xab\x9b\xe2\xb6\xc5\x8f\xc10\x94\\&E\xb3\x07B\x06p\x9b\xf7\x7f\xf5\x1d\x8b\x9dv\x81\xc7/lN\x1cBQ7\xa1\xc8Q\x17\xcd>\xb3\xc9\xba`\xf2N\x0b_ \xfb\x81?\xe4ir\xbeb\x13\xed\x95\xfc\xe9\nJ\x11\xfb\x89\xbfO\x862\xe7%\x83=\x87\xa3<\x91\xecX\xad\xc5/c\x0b\\\x9bL\xa3\x0cU\xa9\xec\xf3\x15\x9bH\x07\x05R\x1aj\xc4VfX\xf6TL{(L\xd1rv\x91rx\xcbz\x89^\xc55\xa1\x90Z\xa9_c655\xa1\xa9\x1b\x0c+\xc71\x14 #\xcc\xe5\x04\x11\xbc\x80\xe29D\xdb\xdb\x01\xc4\xa3\xe8\xb2\x96&$\"\x0e\x08\x13d1\x82*N\x14\x06\x7f\xa8_\xcf\x9dD\x939\xa3\\\x8c\x94\xd4\x11\x8f\xfa\x0e\x07\xa5\xdc\x0eP\xbf\x0e\xab;\xce\x80\xb2K\xe0\x8f_\x8f\xb9I\xe5\xacq\xf2\xe9F\x7f9\x1a{\x05\xbd\x7f\xc9\xd8\x8c\xa3<\xdeb\xf3\xedh\xcc\xd2W\xa3\n\x81]n\xc2\x80\x87\xd4F\x7fh\\!\xcd\xb8\x94\x0c\xda[\xa4\xd7\xb2k\xe1\xb6\xea\x9b\x1a\xdc\xfah-J\xb5\xc1h\xcb\xb0\x8c\xf7\x1f/\xc3`\xc7\xd2\xae\xd0\x8aRcP\x95\xbf?]\xef\xa2c\xb8\xd1c\xbd\x9d\xa4\xcbU\x9a`VJ\x0b\x04e\x94\xb6\xf3\"\xcd\x1c\xd6\x01Z\xa0b\xbb\x02\xde\xaa\xd5z\xb1\xeb\x08\xab\xa6\x8c%S\x96\xd9\xa5\xb9\x0c\x1c\xfe\x89\xbd\x8dV+6=I\x93\"\x8a\x13\xaa\xea\xa2\xdc\xbeK\xb6L\xe3\xbf\xb2\xc0\x8fDvr\x91>:F\x1e\xdcJ\xa2\xe5T\x0bfiZ\xbcN\xf8\xda8\x9d\xd9\xf4\x99\x0d\x810\x1c\xe7\x0f1\xf8\xa19\xd0\xdc\x1e\xe8\x02\xc7J7)\xa05\x84\xb5\xfdYd\xdd\x88\x80\xc5\xcb\xba=\xd5Z/\x9a6r\xf6\x02\x0d\xd9(\xc2\xd9\xe2\xf4\x05\xbf\xa8\xe3\x17Tk\xeft\xfe\x02d\xe58\xf3\xfe\x94bf\xd0=\xea7\xb2\xf1uTD\xfa'p\x04\xff$0\xb0\x81y\xbb\xe6\xcc\xdbcj\xbe\xd7$[\x17\xcb\x12\xda\xe5\x0cK\xac\xd6\xd6\xaa5\xca\x01\x11?1\x0b\x16\xb2\xc0\xead\"\x0b\xac>f\xb2\xe0\xc0,X\xe1\xd2\x99\x97\xe4S\xac\xbe2\xde\xcee#O\x9eXC\xbd\x11\xe2\xffc\xf3\xfa|)?y\xfa\xf8\x19\xcd\xe6^\xff\xbal._W+\x1d\xb4C\xe5k\x13\x81\x06\xa3l \x8eR\xa7\"Y=\x9a&\xb9\xad*\xd4\xaf\x18\xf2\x8aM\x12\x1a\xefL\xda\xe1L\xcc\x02?\xeb\x952\xb3\x8a\xe8\xbf\xae\x19\x9594\xe7n\x0d)\x90:\x04\xfd\xd1F:\xab\x19\x06%r\x98\x8b\xda\xdbQ\xfb\xdc?\xb1\xbb!xb\x1f{\xf4A\xa0?\x9224r\xec\xd4#\x07>-\xf5\xd7\"\xee\xc7\xa9Hl\xcf\xe9\x91a\xbf\xf67\xf4u\x0fdn\xf3U\x96\xaer\xf9\xf7$M\n\xf6\xb9h\x81#\xb4\xc2\xf2\xebe\x10\x12\xe1\xd8\xcbb\x7f\xd5+\x89\x9dK9\x8d\x98KC-\x95\x9c\xc2\x0d\x1fp\xc2&\x85\x16\xdb\xa4-\x80\xeb\x8dL\x8eo\x9a_\x7fE31\xe6S\xd1'\xd5\xa3PD?\xbe\x96\xd1\ns\xd0_\xa4\xfc\x04@\xdb\xe7v\xa9\xc1h\xb0}\x9d\xf1\xde\x9a\xba\xc7\xd4\x1f\xf7\x9a|\x0d\xfc\xa4\x8c\xf1D\x146d\xf6Ij7\xee\x0d\xd4d#J\xb2\x01\x15\xf9\xadP\x107t\x1f\x96rl@5\xeeC1Z\xa8\xc5M\xef}\x96\xde\xc4\x9c\x97\xef\xd0\x18 j\xa6Y+j\x82\xe0\xb16\xa3Qn\xf2t_:\xdf@\x97Zh\xd2W\xb1\x81`h$\x0ci\xb4\xf4j\x8c(]r\xc6)\xe7\x8c\x1b=\xa7by\xd9JS&\xd2\xba'\x1670\xc9(\xbd\x0c!\xc3\x7f\x19\x99\x88\xa6i6c\xbc\xacp\xb0\x9f\xc44\x85\xcdc\x830\xde,\xb1C\x9d0\xb8x\x1c\xf58(\x82\x9b|\xeb\xa4\xff>\x14C\xa4\xac\xc5\xda8\xb6\xf6\x93\xe2\x8a\x03'\x12Z~\x8c\xb2G\xa3^\x13=\xb5\xa9J\xb1)U\x11\x14e\xa2\x90\xfb\xe7x\xb1\xf8\xc0&,\xbeA\xa1%o 2&\x81id%\xf9\xa3M\xb8\xda\xbd\x9b\xd2\xd4\xafM\xa4\xa7#y\xdc\x944\xaa\xcb\x06\x0e\xd8e\x1d7\x14 \x8a\xa4\xd3\x96\xa6\xee\x8b8A\x18\xb9n\xdc\xf4\xa7@a#\x0e\xc1\xcb\xd2\xb4p\xdd\\\xa8\xa7\x9d\xa5\xdb\xd8\xec\xc1A\xfa\x1a\xc8\xde\xd7P\x97B\xc9\xedn\xc5c\x03\x8db\xa9\xaaY\x08\xde\xf1j\xe55\xcc}\xde\xabl/x\x7f\xbek\xe6q\x88\xb7\xa2\x81\xc5\xcc\xb4\x1aUTJ\xb3$Z\x12z\x8e\x16\x90{\xd3\xf8\xc6\x92\xe5\xd5\x93\x17w\x0b\xd6\x14\x14i\x15M\xa7\xe8B\xee\x0d\xd8\xb2\x01k'\xe9\"\xcd\x86\xe0\xfd\xff\xa2(r\xe4\xbd\xb3W0\x04\xef\xff\xf9\xdf\xff\xb7\xff\x03<\xf7\xf9\xea\xc5\x9e\x00\\\x08\xdeI\xe9\xa8.\xd7\x96/\x0c\xe6\xbf>\x84\x02\x8e\xc0\xe38\x0f%\xb5\xf0`\xc8\x17\xd1\x0b!g\x0c\x8a9+\xbd\xe3=+\xe4w}b\xb7\xad\xca(\xb5&\xdd\x18f\xb9B[>\xab\xd8o!oW\xdcx\x9c\x7f`\xd1\xa4h\x17.\x9a\x0dI\xf5\xa7\xf3\xd1\xa5\x9e\xf2\x08k\xa7:\xd0\xc2\xdf&N\xfe6i<\xad\x92{\xf0\xb7\xd0*\xd5\xd1'RB\x9eHI+\x9f\x0b\xdd\x89\xb9z6%\xea\xea\xa9\xae\x02:\x9cI\xea\xe9 \xe1&n\x1a\xdcI\xc2\xc5\x1bwz\xda\xd2\xbd\xa8Dl\x01\xa3\x06\x0d\xa8Y\xb5\xed\xde\x1dZM\xfdJ\x06\x95\x91\xb7\x83Yy;\x88\x96\xa9\xe2v0\x85\x17\xc0\x9eC\xba\xbd\x1d \xd7Y\xbb\x1dt1\xb0\xa0\xdf.\xe9h\x9b9 \xd7\xc9TP\xb6XOG\xc5\x87\xea\"\x92\xe36\x89G:d;VL=\xc27\xbb\xc0c\xc6\x8d\x1f\x8e\x99Q\xd4\xddPgW0\xb4\x94\xc6\xf6\x19\x9d\x86\x10\x9b@\x8ag\xe0\x97\xc6[U\xe2\xbf4\x90A+\x13v\x0b\x17w+v*\x12x\xbdcl\n\x11\x88\x0fB(R\x981\x0e\xfd\xa8:#z\xf0s\x94\xc3u|\xc3\x12\x880\xd5\x8d\xaf\x99\x04\xa5\xfcPY'BM>\xe5\xe7\x89q\xe1\x9aZA08\xd6 \xa3-3*\x84\\U\xce\x8b\xc5\xbc]\xe4(\xb0\x1b\xfe\xf3N\xb1\x9f>\xfa\x14\xe0\xcf[?\xc2\x1f\xb7\x82[\xf3\x99\x1f\xf4\x16\xe9\xb5\x0c\xeeR\x9d\x86\xb38\x99j\xc7\x1e\xe70$\xb3Q\x0e\xa0\xd3%\xa1\xdb|_Nx\x08\x89\xff\xe4\x89i\xc8W\xe9\x8c\xeb\x97\x03]\xba\xa4\xaf'\xdc\x03\x99G9^\xb3\x0bG\x89w\xe9\x94\xe5C\x18\xddX\x12\xc2:\x04\xe1V\xa4\x90\xd5w\x10T4\xdb\x16\xb1\x93\x1c'\x838\x94\xd7x\n$x\np\xc4Jz\xf2,\x80\xa1\x8a_\x87\xb1\x89\x9d:\xee\x05\xca\x11\x92\xfd\xec)\xa4\xc6hl[\xfd\xc6\x03\xd0\x81\x8e\x8dwR4,\x0b\xa1U\xd1\x1b4\xb8@\xd26[g\xd0\x84\x1b\xec7\xf1\\\xf5Q\xcbKC\x93\xceO\xd1b\x8cz[\xc4K\xa2\xc4SE;\x8bt\x12-<\xbb\x06[F\xf1\xc2~\xbdL\x93bn\xbfN\xd6\xcb+F\x8ck\x15\xe5\xf9m\x9aM\xed\x92\x8c\xef\x07\xfbu\xce\xa2lBtP0b0\x9c\xef'\xde\x923^gD\x03\xb7\x8c}\xaak`\xdb\x94tN.W\\N*v\xb6\xfe\xab\xce\xb5\x92\xac\xae\xce\xe5\x16p\x04[[\xd9Hp\xce\x98b\x8e\xcf4\xcaX$+T\xe3}p\xfc\x12\xa9\x03\xcf'\\\x8c|\xc3f\xc5\xd0\x0c\xe1U\xabq\x91\xae\xac\n\x19\x9be,\x9f\x8b\n\xb8m\xf3\xb6}\x98\xf5\xac~Q:\xf8\x1c\x9aE\x17)\xfaK\xf7\xeejm\xb4\xee\xc3\xec\xdb\xe1\xe4R\x83\xfa\x83\xc7\xa6u\xbatM\xb7B\xc1E]\xd4W\x9c\x82\xb7\x86\xd6f\xbdY\x9c\xe5\x05\xaa\xf4\xddZ\x1b\x94\x9f\x12\x112\x06\xd3ic}\xferO\x8aS\x1cC/\xeeV\xd5\x89s\x93\xc6S_\xbc\xc7\xa5\x83\xc3v\x0f\x15@`k\xeaX\x8bU\xd2V\xc5T\xfbvW\xf9r\xae\xba\x15\x82{\"a]918\xe2\xc4]\x04\xd3AMy}j\x15\xde\x04F0\xa6o\xa0\xdc\xdd(\x07}\x1f\xcbz\xb3t\xb2\xce\xcds\x86v^~\xf0\xdd\x1f%\xf1\x12c\xdb\xbf.d\x90\xfb\x93t\x9d\x104\xf6*\xcd\xa6,{\xbd\x8c\xae\xd9\xd9\xba@\x06\xbf\xa1\xca\xf9\"\x9e\x10$Y\xab\xf1s<\xa5\x8e\x95\xab\xf4\xf3\x8f\x0b\xf6\xd9Y\xf0\xfb,]\xaf\xc8\xd2\xb3l\x1a'\xd1\xc2Qa\x92.\xd6K\xd7\xdcDan\x17\xcc\xc8\xa1\xcc\xc48n\xe9\x92\xf7i\x1e\x17\xf1\x0d1{^z>\xcf\xe2\xe4\x13]\xf6\x8e]G\xee/1\\\xb1]t\x9d\xc5\xd3\x0f\xd4Xd\xc1iB\x1c\xc5\xb2\xec|\x15%\xee\xc2\"\xca\x08X\xf1\xd2\x13\x84WS\x99\xb3WQ\xec\xeeX\x96\xd3}\xcf\xd2\xa4\xf8\x99\xc5\xd7s\xa2l\x11'\xecd\x11-\x89\xb5\xe7E?9>KW\xd1$.\xee\x88\x02\x1a\xdci\xb6\x9aG\x14\xaa\x14\xd1\xd5y\xfcWb\xedn\xe3izK|\xf0\xd7\xd7\xc9\x94\xc2\xae\xbf\xa6\xe9\x92\x98z\xbcX\x9c\xb9\xc6:[\xa4\xe9\xd4Y\xca\xb9\xd9\x86\xc2,\xfd\xc4^E\xf9<\xca\xb2\xa8\xb1B:\x9b\x91\xdb^\xd4x\x1b\x17,[\xc4\xcb\xd8Y\xa3e\x0c%A(\xcb\xbe\xda\x17p#\xefgv\xf5).\xbc\x10\xbce\xce\xff}\x9b\xfe\x95\xffw\xe6i\x9a\x1e\xa9\x89\xf9\xc4\xeer?\xeb\xe2\xee\x9d\xdauh\xa7\xe3Q\xeba\x0e\x9a:\x11\x13WL\xe6Qv\\\xf8\xfd\xa0W\xa4\x1f\xb90+5\x99\xbc,__ \xc3\x0b\x7f@\xd9\xa4\xa3!\xe8%gf\xf4\xd0\x97X\xa6\xa98\x8d{\xca\xd8\xa2\xf1q\xfe1\x89\x8b\x05\xcb\xf3w\x92i7\xdcs\xf3y\x9a\x15\xf3(\x99*\xad\xd5\xe9\xe7U\x94\xe4\"'\xa3=\xc5\xabh\xf2\xe9:K\xd7|\x8f\xd3\x00\xa8j\x1c\x17E4\x99/\x19Ev\xed\xda'\xb4\xaccW\xc4#\xa4KEA\x8d\xd3\xe4\x7fnR\xf9O]*\x7f`+\x16\x15C*\x8d)\xa1:\xb1;i\x87\xdd\xfd\xc7\xdeiD\x92\xc29F\x81\xa5\x8eC\xba^\xe9\\\x98\xc76W*W\xb6\xfb\xd0~H\x8b\x82\x93\xc2\xa6\x01\x8a:\x9d\x86)\xaav\x1a\xac\xa8z\x8f!\x0b\xf1\xa9i\xc0\xbcF\xa7\xe1\xf2\x8a\x9d\x06\xcb+\xdec\xa8\x1f\xc4y\xd84V\xac\xd2i\xb0X\xb3\xd3h\xb1\xe6=\x86\x8bbg\xd3`/\xd2U\xa7\xa1^\xa4\xabN\x03\xbdHW\x1b\x0d\x93\xf3&\xae\x11\xf2\xb2\x96Ny\x95?FY\x1c5\x11\xca&\xfeG\xafC3\"\xeaib\x87\xd4\xc3[\xf91Z\xc6\x8b\xbb\xae\xf3O\xd7\x05o\xd8\x05\x02Y\xdc\xb2D\xb2V\x0b\xacd\xad\x86\xe5\xf9\x8e\xfe\xe5P\x15\xc4\xf8\xf6\x9b\x84\xaa\xc4\x7fj\x06\xe3K\x85a\xd0`\x1f\xe3\x02\xee\x89\xf0\x80O\xfb\x96\x83\xbc4 \xc2rv\x0b\x1f\xd8\xf5\xe9\xe7\x95\xef\xfd\xe7\xc8\x83m\xc8z\xc7\x17\x17\x1f^\xff\xf0\xf1\xe2t\xfc\xee\xf8\xed\xe9\xf8\xfc\xe2\xf8\xc3\xc5\xf8\xe4\xa7\xe3\x0f\xb0\x0d\xde%]\xa9,\xfe\xdd\xbfXi\xcd\"\"\x1e\xfbZ\x06\x80(_\x96w\xa5\xb9\xf3\xaetkkmG`\xc7\x00\x81\x11\xf1\x9e\xcb\xfd2\xfb\x1a\x1a\xb4\xf9\xeb\x11\xbb\xc4\xb0\xaf\xa8\xdd\x85!\xf8\x91\xf6\xa6\x16H\x9bNs\xdc\xc5\x9e\x10\xf3\x84\xcc\xa3\xfc\x874]\xb0(\x11:\x80\xef\xbf\x87\xad\xaa\xe8\xddz\xc9\xb2xR\x16\xc5\xf9\xbb\xe8\x1dg\xfeT\x05%\xce\x99\x15\x0bx\x01\x83\xb2\xd6\xd9\x0d\xcb\x16i4eS\xab\xaf\x01\xa9\xc0\x03\x89<\x13[\x1f\x87V\xcbo\xa3\xec\xd3z\xf5c\x9a\xbd~\xd5\xaaJ\x13\xd3\xcez\xaf_\x8d\xeb\x88\xc0q\xe0\x90cHj\x85\xb4\xae#@\xce\x8a\xe3\xa2\xc8\xe2\xabu\xc1\xac>\x1d\x8c.f\x9b(\xbf\xf2\x89\xee\x89\xe0\xefM3\xfd\x90\xa6m\xd7\x95\xe5T?\x9c\x9d]\xd8\x93\xfd\xb7C\xcf\xfb\xb7\x0d\xe6i\xf4HB\xd7\x9a&\xd1uXK\xdcK\xf4k\xccT\xed\x8c\x0ePV\xea?\xbc\xfc\xe6\x1f\xc5,'\xf6\xd7Q\xad\xc2\x08U\xc8\xb4Q\x15j ]\x82\x0bF\x8b\x14.\x1f\xa5~\xd0\xf3huc\xe9\x07\xd6\x8b\x14tl\xb3\x0e\xf5\x94\xf6\xff\xe6n\xfc\xf2E\xbcl\xd8@\xfdRE\x1e\xab5\x86!\xfe\xad\x90\xbb\x93\xbe\xb2\xc4\x9d8?Y\xe7E\xba\xac\x16\x15\x01X\x91\x0d\xbc\xc1\x1a\xa2\xf8V\xf5 \x01\xba\xc1*\x1b\xbdtXl9\xc4\\RL\x15{\xa7\xc00#\xc6`<\xaf\x05\xd1\x11\x80ndk\x880\x92\xb6\xe0[a\xe1[\xd1\x8co\xa4\x1f!h8\x94\xf60cW\x9c&T\xbeD\xf5\xf0\xa6\xe2@hw]\x06~l\x913GgP\"x\x8a\xee\xbd\xba\x02\\\x98}\x89\xabb\x13pb\xb9\xe8\xeeT\x9b|\x02y\xf11/\xed>\xd0$Q\x81\xe8\x8eo\x8cK:@\xabzZ\x06\x0e\x9a\xbdQZ\xdfq4\x93\xa4?k\xfb\xa3|\x15M\x1c{\xb5\xfa\xea\xc8\xa0~\xef\xce\xfd\xb5\xc8\xa2\x877\xbc\xe8.O\xed\xe8\xb4\xd3\x8eN\xac\xf6}l:P\xa9\x8c\x8c\xf7\xd8\xa5s\xc4\x8e+|\x9b0\x08Hc\xd0}\x82\x14\x14\x06^Lz\xdaV\xd2(\x86\xdcA\x1d\xf7\xa0\x8b\x0886a.\xf3\x00\xf8\x8a& P\x89\x84\x15\xfaXmH\x15%\xa4\x1a\xc7V\xc7\xf4Mh\x145\x8c\xee==\xf0\xc9\xb71%r\x9e|\xa5\x85\x7fgJ\x94\x06\x9c\xad\nU\xf0\xe3\x06r\x84\x1d\xdb\x04\xc2\xbd\xd9\xab\xa3U' \xee\xddj\x1f\xabG\xc0F1\xb2\xd3\x03\x0c\xfb\x8b\x7f{\x0e\x9fc1J{a\x8d\x93\x9d8d\xc5\x97\xf4>\x12\x17\xe2m\xc8R\xfer\xc8f\"9\xe77\xcaf\x03*lq\xe2\xef\x0e\x1c\x11\xc6\xcdp\xeb2\xcf\x97\xd9\xca\xba\x92\xdc\xb6\x06\xa4\x91lnq\xb1x\xd7\x8bV\xccY\x9a\xa25\xcd\xebW\x95\x0dv\xcd\xdci\xc5\x92i\x9c\\\x7fD\xa3\"\n]\xda\xbe\xc1\xe5\xb7\xb1\xc6\xf0.\x10w\xed\xf2\xcaU\x06C \xf1\x04\xc3\x9aW\xf6B\x94\xfdL\xc5\xb1|\xff=(\x03>\x89\x98>\xeb-\xd7\x8b\"^-\xa8\xb4P\x15\x1e8\xc5=\x82X\xde\x94\xd9\xd8\"\xcc\x81B\x1b(\xf5\xd2UaGEu\xde\xba\xa3\xbbA&\xc4d\xdd\xe5 \xa9\xbb\x1cd#AhG\xe9\xe5\xff\xcb\xde\xbbv\xc7\x8d\x1b\x0d\xc2\xdf\xf3+J\xcc\xacCF4\xad\x8b\xc7c\xb7G\xd1\xeb\xb1\xe5\x8d\xb3\xe3\xcbZ\x9e\xe4\xeci+Z\xaa\x1b\xdd\xcd\x11\x9bdH\xb6de\xac\xe7\xb7\xbf\x07\x85\x0bA\x12 \xc0\xb6<\x93d\x1f|\xb0\xd5$\x88K\xa1P\xa8*\xd4\xe5\xac\x93\xc0\xa4\xd5\x92\xd2B\xdcn\xc1L\x89X\xd0\xcd\x0e\xb1\x8b\xa7\xf9\x197\xa4\xd2\x93\x02\xacPaLU2\xc7[\xf1\x0d\x9e\"\xed\xe7Gj\x82xQ:\x1a\x13\x137\"A\xc3\xa6\xde\x02O{r\xda\x01R\x907\xb3@&\xa0l\xdb!t\x87\xba\xa3#\xac\xb1\xe2k\xe2\xc7\xd3\xbd\xee\x17F\xcc\x12\x7f\xe9\x05\xef%\xa9\xff\x9cW5\x06Mq8\x9f\x84<\xc1b\x19\x99\xecA\xf3\x8c\xd9\x01Nz\xd6\x8c\xe2\x8d~\xb3q_xv\xb8\xf4\x97k\xf0\xc8]\xe7\x9b\xac\xfe\x1b\xeb\xcba\"\xe2\xa0U\xf6\xb6\x8e\xdd\xed\x8c\xbf\x07>QZ$\xc8\x9c1*\xc9\x92:\x89Sn\xb9*\x08\x07et2\x984!?\xf1\xbdI\x8f\xc9\x12\x8eU\xecs\x83\xaeP\xc2\x7fX\xcc\x17EXw\x8d%\x8e\xa20@\xf2\x10\xceoy\xe7\xec\"\xcf|~\xeb\x0e\x04\xdf\x85\xba\x9b\xd8\x0eP\xcd\xb9\xe3*.|\x1ec\xcb\x18\xd5\xe0\x96\x85\xaa5\xd9\xf9_\xc7\xd5kN\xbc'\x92\xa0\xd7\x0dA\xefch\xa8\xa6\x8d\xa8\xf9\x8eW\x13r\x1eu\x16\x99\xbe\xdc\xa0\xc9\xcfF\xb7\x8d\xc3\xee^e\xc1\xa3\xf1\xd3\xe7\xcc!\xc8\xb6\xc6\x06/\x0f\x15\x13\x87\xfa,\xf2\xaaf\xa0\xd7\xec-\xd3\xc6bVmZD\xb2n\xb1\xd6\xc8\x0cY\xe7\xa1e\"\xd6\xfe\\Y4{_Je8\xd2-\xb1\xbe\xdf\xd2N8\xc4\xde.\x99\x7f\xb6\x8da \xd9q\xaf\x19A\x08%Ztex\xb6i*42\xd3N\x0f\xbb\x8e\x07\x9amW\xa5]\x0c\xd5\x15?D>\x13\xaf\x17)G\xfe\xfa\xaaLm7\xb0m\xae\xe7u\x19O\xfbx\xbf\x1b\x91\x80g\xcdy\xd45q\xdc\xf0\xe7\xdd\xfb\x8c\x8a;:\xd3\x0e\x809<3\xdewx\x13 \x19\x93N<==\xb4\x96m\xd6\xab\xf7\x11\xcd\xfb<\x1c\x97\x91\x8fxz\xa2}\x91/\x8f\xee\x88\x98\xc7\x00\xf1\xd3\x0e^J\xb9\xccc\xd9\x92Zi\x8e\x86\xf4b\x86\xb3\x88)\xb1h\x03z\xb9S\xeb:\x84A\xfc4\xa1:z!=D\x11|\x8bI%\xbb\x17\xc2\x0cv]\xbc@Ax\xf9\x0eU\x80\x16\x0d\xa3\xbcu\xbc\xd6\xe6nP\x0bg\xab\x85\xf2\x18\x9e\xaf\xc8\xec\x12\x03K\xf1\xc05,\xf55\xe4\x0b\xf8\xbf\xe8\xa3\x05\xbb\xe0\xfd\xdfH/\x9a\x82Q\xb1\x03\x8a!\xb5A\xac\xf5\xf3\xe8<\xbf\xceHI \x87\xef\xed\x1f\xeeyMX\x89\x04\xd5\xc9\x13 \xf2\x10f6\xae\x98\x16MV,\xb6\xec\xc8\xb7\x1c\xc1\x86#\xdc\xab\xac&e\x16\xa72|\x8b\x8f\xc1%]<`\xc4\xac\x1a\x8cQ3p\xdd\xbb'NPf\xf5\xda\n\x95\xa5\xffF\x8dfK9\xc3&\xa4\x8c\xcb'%\x0b%(?\xea\x03\xc9g\x10\x088\x082r\x0d\x15\x9b\xae/~\xb3\x1a~\x1e\x04\x11\xe7\xb2)\xa3\x83\x87}\xd6zr\x04\x19C4\xbcr\xcb\xe7]r\xc16\xae)7\x99\xc7\x9c\x12\xba9\x89\xdb\x0b\xc3\x9d+s\x0c\x1c\xe1#\xb5G\xec\xd8\xf7\xc2\x86\x02\xb4q\\\xde^\x9c#\x00\xd1p\x8fy\x8f\xcbGk\x96\xc1\x97\xb9)w\xf3+\xd1\x92\xfb\x95\xea\xbf\x98t\x05\x86s\x16\xc9\xa1N0g\x8a\x1a\xe4l\x02\xcd\xadC7\x81,{\xf3uN\x92\xef\xbay\xd6\x94P\x17}\xd4\xfd\xf3\xdb\xd3\x0f=\xc7\x00Z\x9e\xbf}\xfd\xee\xed\xe9\xab\x0f'\x13\xd0\x88\x02'\xaf\xdf}\xf8?\x138\xe8\xbfY\x92\xfa\xc3M\xe1\xc4\xb8\xb7/~;'\x01\xdd\xe8\x11v\x83\xea\xea\xa4\xfak\x9c&s\x11\x15\n\xd1\xd6\xb0 \xf8\xbeN\"9\x05\x98@\x12\xd1\x99\x8a\xa4g\xa5\xef\x1d<\xd2'o\xec\x88\xd4\x067\xf1/\xb5=`\"x\x1f, f\xc68Y\x17\xf5\x8dD\xa4\x97\xf1\xac\xce\xcb\x1b'\x88R\x92o\x9bR\x1f;\xfa\x8d\xb1]\xe7\xd4\xa5\x90\xa7\xed\xb0l`\x90Dl\xa2\x94k8\x82<\xbcS\xd8\x9a7\x07\xdf\x05,Ve\x0f\nm\xf5\xf3\x95\xd6K\xdcpL\xd8\x00\xc5\x81\x94S\x04\xa7Tk\x9fR-\x86\xa9\xdc~\xc4v\xd5\xaf%\x83\x8e\xddb\x82ZK\xfbI\xf5\x01\xdd;\xc6M\xa8\x15\xc8&\x19l_\xac\xb7\xce\xd2\x88\xbd\xfc\x9f$#e2\x93cx\x9e\xc6\x95\xd5! \xf8\xd2j\xb0\xbeO\x9bX?\xad\x89:w\x92\xb8l-\xf9\xeb\xeby\x19\x9aQ\xfb\xe1#\xc6\xe1\xef\xf7rj\x08YB\x97\x81S\xec \xff\xa0\x9fiD\xd1\x94{\x91\xa7\x11,\xbc\x89\xe7.\x08H\x9c\xa1\xfc\x8b\x86\x7fW\xef\xceItIn\xe0\x18\xe2\x88T\xb3\xb8 >>\x08P\xc5T\xe7,G\xaa\x7f\xf8H57\x12\x7f\x8d\x89\xd9\xd51=\xa2\xc7\xc6\x9e\x92+\x9e\xa7\xa9\na\x16\xea\x13q\xd2E)BLr\xc2gQ\x1b\x04 %\xd2\x1e\xe5\x00\xd1\xb7\xcb\xbb`\x92\xaaxD\xf9\xaa\x9a\x13\xa2&\x94\x9a\x88\x94\xd10O\xbc\xae\xc26\x89'\x0dTy\x17u\xf4\xcd7|d\x18\xf4Or\xf83\x7f\x81 \xf1\x85p\xa2\x07\x8b\xc6\x0e\xa3\xf7\x84\x13\x94U\xeb\x05\x86\xda\xf0\xbc\xae\xb9\xc5\x97\xfaA\xb2\xd0\xa9h\xcb\xb2 \xa1\xc2tn3v(\xeeuo\x7f\x17\xec\xf6\xf7Q'\xe0%S\x7f\xe9N\xad\xc2\xec4\xfe\x92\xd7Q\x04lq\n\xf5\x177k\x02\xe4\x98\xf2\xa9\xf5?\xa2G\xbb\xb4!\xf6\x98\x07\x12\x06\x89\x0c\xa2\x92\x14i<#\xfe\x83\xe9\xc7\x8f\x7f\xff&\xfa\xe3\xee\xb1\x1fL?\x9e\xfdr\xfb\xf9\xec\xc12\x04\xef\xe3\xc7o\xeeyJ\xb5vW\x9f\xa5oT\x10\xfd\xf1\xd8?>\xfa\xf8\xf1\xa3\x1f|\xc6m\x1b\xed\xf2\x07g\x01\xb6\xf4\xcd~\xf4\xc7c\x86\x18\xdft\x03\xc2\xeb\xbd`\x85~\x8d\x8fV\xa7n\x96\x06|hF\xdc\x0d\x10?\x184X\xd8,\xef\xb7\xbf\xf9]\xff\xaf\x8e\xb2\xae\xe1*\xd8\x11\xb3(\xf3\xb5Qm\xf2:\xc6T\xde\x85\xff:.Z\x06|\xaf\xe3\xc2AQ\xd3\xaa\x85\xdbL\xb6\xd6y\x1e\x18\xdb8%5\xfb\xe8\x94\xd4\xad!\x9c\x92\xdaa\x08\xadZ\xca\x10\xfa\xcf{q\xa4\xaex\x92r*#\xbc\x8e\x8b>b\xae\xf8\xcbS\xd2am\x9c\x12\x9a\xcd\xa3\x8a\xd4\xecm{\x0d\xc3v\x0e\xea\xa1\xe5\x9fGK\xd2\xd7@\xb3D\xb8\xc3\x0d\xcc\xb9i\xa0\xe6\xe3\xd8\x16T\x8ew\xde\xe0\x8f?g4\xb4g\xa1\x85l\xf2\xf0@VQ<\x9fkF1\xecx\x0e<\x07\x83a\n\xd6\x98\x94\xfd)\xac\xf4Sh6\x94\x8e)\xba\xe2\x99\xe6\xbb\xee\x07\xc0\xb3\xf2\xe9\x9e/\xad\x13\x03Eg\x1a\xe9C\x1ai\xda\xbd\x19\xd3.&~~\x95\xd5>\xe1\x1e\x9b\xfe>ej\xf74\x8a\x8a-P[\\\xdf-\xb5T\xef\x8ae\xc8\xac\xc7c\xbd8s\xf4\xed\n\xab\x8bi}6~?\x0c7\xcd#.\xe9\x9av\xdd-*\xafq\x15D\xeb\xb8\xf0o\xb6\xd8.\xc3\xe3\\\xb3l\xf8\xddD\xf9.\xbb\xc9 \x00k\x0d\x00\\\xf7\x9a\n\x80\xb5\x1e\x00\xbf\xeb\xffE\x87E\x05\x85\xe9\x99\x8e/97\xf3%yo\x1eF\xf3\xa8+\x99\xc2y\xb6J\xd2\xf9\xab\x17:\x99\x0c\xc3Oe\xd2\xab\xfa|\x8c\xb5\xd7\xb5E\xc8\xf6>f\xd8G\xc6B\xd13\xcd\xffO\xd9e\x96_g\xc8s\xf8h\xc2\x0f~\\\x03c\x80\x16I\xca\xa2\xf2H\xd6\xe6\xef\xd1\x1f\xa7\x1f?~|p\xf6\x80Y\x1c\xef\x827au\xd3$#\xccM\x9a>\x0c<\x14<\xb19\xa69\x9b\xc3\xc5\x0d6\x9b\xc9\xf7\xaa\xf3\x87nB'}\xb8k\xf4\x05\xde\xef\xc9\xba\xa8o\xb0\xc1q\xf7\x1b\xde\xefk\xf2\xa96}(\xd4\xd8\xfc\x8f \xff#\x9a'U\x91\xc6hY\xca\xdc\x98\xf0i\xc6\x7fJ\x80\x0e\xce\xec\x93\x01\xa3B\xc4\x90Sz\xde\xbeh\xba\xd1Z\x97\x94\xa2b\xa3\x91\xefW\xcaE\xa5\xb7\xd7\x19)_\xbd\xe8a\xab\xd4\x8b\xa2\xe5\x8c\xae\xef<\x08B\xb8\xc6\xfc\x91\x80\xb1\xc8\xcf\xab|S\xce\xda\x1cE{'\x9d\xf6\xb4\xb6yvJXH\x9d\x92dcL\xab\xf4\xd6\x92\x14\xd03\xdf\xdb\x7f\x88\xd1\x923\xb9\xa1\xe8\xee\xeaW\x97\x92z\xc9$\xf5\xb2\xa5\xbe(\x87-\nY\x8e\xb9\xd2\x90Z\x1f\xb8\x0e/\xf7\x13\x93m\xa1\x1ck+:\x7f\xdc\x8cY\xaf\x8c\x8b#\xc2\x83\xf9(\xcch\xeb!6\xbaO\x1b\x8d\xa3\xa4z\x9do2\xba\xc9Xo\xdf\xed\xb7;+\xe2\x92d57\x90R~\x1ea\x8cr\xe5\x01^\x8e\xca\xd6\x0f<&\xec\xc9\xf7.\x176\x1d\xd5h\xf6\x03Y\xe4%y\xdd\xbaAu3\xe7/}c\xb8H\x0e\x87 h2\xaf\x03FSc\x03\x9e@\xa6\xaf\xc0\xec\x9e\xcc\xf6oby&05\xac\xbd\x84\xb9\xd9V\x8f\xc55\xe4\xc1s\xc6Z#\n\xc8\xfd\xc4\x1b\xd1\x83n\x9b\xddC1JA\x194\xfe\x91\x98\xd5\x8bb\xd5\x1b\x96y)\x87N|\xfd`\xea\xf6V\xae\x95a1\x97Va\xf1\xa6b\xf0\xc6r\x95\x92g\x030\xdbf\x8c\xa8\xc7m\x01\xac\x8e\x94\xb5\xdd\xdd\xb5\x8c&[\xdf)\xc8X\xa4\xc7\x16\xa4\xf6\xf5\x90\xaa|\xa2K\xc7x!\x82\xf7\x0f\x8d\xbb\xd8\x94K\xc2\x87N\xe6r\xf0\x95\xc5\xd5\x14\xc3j\x9eF\xe7EI\xaeHV\xbf\xdb\x94\xcb$3*j[\xc9\x94\xf6\x9e\x02\x81\xef\xe1B\xd2fb\xa6\xcd\xb4\x9c\xfb\x17Sr\xe6\xaa8\x03\x9c\xf8@\xd0\xfa\xe1[\xdaf\xb7\x7f\xc9\xe2 \x85\xcaN\x17\xa9\x86\xfa^\x92\xfa9\x8f\xecW\xc7\xb3\xcbg\xf39\xc9\xe6\x9b\xb5\xebHtVO\x836L\x82~\x9c\x0c\x86\xaf.\x99\xe5$Z\n\xe9\xcf\xbe\x1av\x8f\x18\xeb@\x1a\xae\x81s\x11\xd2*\xcav\x9e\x80\xa2\xe4Z\x88\x08\x87\x06\x8aL\xc1N\x9b\xcf\xa3\xf39\xb9\xd8,_\xbd0\xae\x00\x8e\x0d\x99\x9d\x16L\x7f\xb8y\xf5B\xc4\x9c\x17EcB\xdb\xfd\xc4\xb6\x14\x12\xcd\xf9z\x00y\x1a\xb0!|B\x8e\x9f\x08\xce\xeb\x1d\xdf\xbcC\xc8\xd3\x15i{\xb8\"\x8f.7\xfc\x18\xc4T*\x124\x12\x0b\xa6\xf5\xb4t\xaf0\x8f\xae#\xe8\xf0\xb1\x83\x839q\xf3)n\x1at\x1d\x84\x03\x18\xc4\x19\xe9\xd4=g\xb9]\xbbw\x87\x01\x12\x0e\xb6\xefpT\xecO\x89\xf2n\xa3{'\x19$\xb7\xe19@G\x1e\xcfk$Gi\xff\x15Y&UMJ\xc2\xe8U\xdc\xe5@\xaa\xd5\x9b<;\xad\xe3l\x1e\x97\xf3\xbf\xc5e\x96dK$\xbe\x0e\\\xb0\xf1FB\xa4>,I(\xf2\xc2N\xaat\xd8\xecH\xa2N2\x94;\xb5/\xc6\x86\xda?\xc5\xa7\xdb\x1b\x010G\x97\xeeu\xbf\xde\x9e\x969\x1b\xba\xe9{\xa09gH\x14\xcf\xe7'T\x80\xfc\x91{+2'\xa8\xeeSn\x1e\xb6\xb3\xaf\xb5\xadn\x1a]\xe7Wc\xd2\x8a\x08\xff{C_c1\x90\xc5\x9b\x881\xa4'6\xc9'\xd3<\xf0=\x8a\x00\xbb\x0c4w<\x959\xd1w\xb3\xcd,L~\xb5\xfd\xed?\x8b\x8bzS:\x06\xee\x80\xedW~\xef\xae\xc15\xb0\xf2\x9a\x8bKQ\x06`f\x1f]\xa9\xff\xd8\x05\xcc%\xe7\xa0^\x88$\xba\xeaL\x8d\xe6\xdf\xad\x84kwA\x0d\x1e\x1f\xe8\xc2\xf8\xd1\xe7\xfaP\x11\x87\x8f\xba\x99\x00\xb8[\xddw\x07A\xbb\xfd\x8d.M/\xf3aM\xf2\xecy\\\xc4\x17I\x9a\xd4\x89=u\xc2\xd5\x97&\xa0\x80\x8e\x14\xe6\xb7SQ\xdc\xbb\xc7\xb2Ox<\x8d\x00^\x1b}\xfe\xdcKI\xc1\x9e\x95\x1b\"*\xceXL\xff\x93yR\xc7\x17]\xa7`\x93\x03o\x92g\xaf\xb2E^\xb2(\xf4\x16\x0c\x17\x1a\xb6x`Jz4\xc5\x18\xfb\x04\xdd>\x8c)\xbe+1\xa0\xf7\xccc\x1c\x03\x1cj\x97\xc8G\xb7\x91M\xa4\xce\xc2'Zy\x1el'nI\xaa:/\x89l\xc7i\xf9\xd9\x05[lJ\xda\xc3tZ\xca\x9c\x0d\x13\xc6j\xedi\xeb\x14\xed;G\x9c\xe9\xc7\xab\xb52\x84\xdc7\xe5l`\xa1\xe30!\x90\x19z%\xd6\xd8D\x95\n\xbe2\x84*\x08!\xf1\xcb\xe1\xd0E*\xcc\x9d`\xa5\xd7\x1azr\xda\x18l\x1e\x13Q\x90\x007\x96\x1e\x83*\x16\x93^\x81\x17~\xa8\x87,\xc9\xe6\xad\xaa'\xd9\xbc\x8f\x15\xfd\x81I\xebP ^\xd9B\x7f\xb3\xab\xbb\xd6\xb4\xf1m\x12a\xbf\x1f\xee'\x87\xb8`\xf2\xf5\xcc\xb8\x8eD\x08*\x01\xf7\xb4\x12\x18b>)8\x10\xefg\x11=1\x10\x80\xbe7[\xc5e<\xabI\xe9\x85p\x9f\xa7\xf9\xe2\n\xee\x01\xb1\x04A\xcc\x1b\xa2\xcc\xe3`3\xdaV4Y\xfa\xb9\xddR-\xd2]\xbd\xc5\x98\xf7\xd5\xb0*\xe1\xf3\xe7a\x941\x98\xb8\xe3\x04F\xaa\xef+\x03\xf2[n\xd0\xea\xa82\xe3*3\xbb$\x99&\xd6\x15E\xc5V\xaa\x7f\x91\xb6\x9b2w\x86\x1d\xd4\xdd \xb4v\xd8\xd9\x0bp\x04\xaf\xe3z\x15\xad\x93\xccG\xa7\xad\xd6b\xfd\xc6\xfb\x02\x1dt\xf86\xf8@>\xd5\x83[!\x89fy\x9a\xc6EE|d\xe1\x12\x13bg\xf2e\x0fYs\xb8\xcf_\xb3Y\xe9\x12\xcf\x8aH[\x95\x82\x93CQ\x94\xf4<\x12\xcb/\xb8\x15\x8f\xe4\x96\xe2\xa6\x830>\x01\xee\x8d\xd9q\\\x11\x02\xa2XO8n\xfe\x14\xdcy\xd0\x84\xe2\xeb+B\xf5\xea\xa5\x86\xf7\x9e\xd5\xc9\x15Q\xf2\x08\x91\xe8\"\x9fwRH \x81z(\xbc\x8f\xee\xbb\xdf\xb5\xff\xda\n\x9cW6\xef\xdb\xc7z\x86\xb3\x17f:\xd6\xfb\xea\xb2(\x0e\xfb\xdfv\x1b\xafZ.^}\x0f\xaf\x94\xf5\xf2\xb0+\x15\xcf\xf8\xf3n?\xcc8\xfe\xf0\xdb\xee\xf3\x82\xcf\xad\x1bub\xce\xfa\x17\xe1\xb0\x1f>\xea\x0e`\xc5:z\xdcy|\x85\x8f\x0f\x0e\xba\xe3Z\x8364\xdb\x92u\xdf\xcb\xdfu\xc3\xb9\xf6n3\x17\xaa\x03\xdb\xfe\xc3'\xddQ\x9d\xf3\xee\xbb\xd3\xb9n\x1c\xdb\x92~\x00\xe4N\xe5\x13\x8cQ\xa6\x8b\x1f\xdc\xaa\xf6 \x8e\xba\x9e\xd2\xa7p\x04O\xda\x8f\x9e\xd3Z\x9dj\x97\xc68\xde\xcf\x8c&h\xcc4L&\xcf\xa2\xbb\xf6\x14\x1fu\x93qMZ)\xc8\xba\xac\xae\xce:\xec\xad\xb9Sz\xb6\xca\xa0\x80\x8c\x84\xabO\xfck\x96\x8ew\xd8\xfa\xec\x9d\xd8n!\xf2\xa4\xdd\xbe\x90\x96\xb7\xa9\x06%O\x8b\xa8\x9f5\xdbtv\xc6\xe6\xe8=\xec.\xd1\x14\xf2\x03\x8e\xc0C/~\x16\x8ck\xc2L\x155w$1\x1cC\x0c\x13\x88\xbb\xf6x1\x9a\xe2\x05\xa1T\x95\xd5\xc9\x9a\xf4\xaet{\x13\xa6\xfb~\xd5\x89\xf3@\xc1\x94\x85<6\x01w\xa9D\x07\x98n\xf8\xa8DU\xcd\xd1\xfe\xe8Q\x95`\xc8\x81s\x16\xbdC1\xa0\x88\xcek\x0eD\x1e\x0e\x89e\x87\xffQ\x8d\x88\xf0*\xabsLa\xbd\xc1\x85\"\xb8P\xd9\xb0\xb5\xe4\x07eUuKJ\xc9\xe3:B\xe0\xbe'\xb3<\x9b%)\xf9P\xc6Y\x153\xfeuI\xeawy\x9e\x92\xb9\xbf\x83\xcc\xc1,\xdaT\xe49\x9e\xe6|\x01;\xb3\xce\xa3\x82\x94T\x02\xf5\xdf \xb1\x11\xe4|\x10\xe1`\x7f%I \xe5)\xf2\xe1i\xbd6\xe9\x8d\xf0*d/\x84U\xb4\xc94\xeb\x86\xd6D\x9d\xed)\xf8\xec\x9e\xf4\x15<\x85\xbaI\xfb\xf74\x80\x9a\xab\x81\xf0\xb7\xaf\xbc\x1b\x1e\xec+\xb3\xa5\xf0\xb3\xf1\x96\xc2U\xa4\xcbj\xae\xf3Q\x13f%t\xe9>\x7f\x86\x9d,:_\xe5\x15\xbf\xdb\x18cC\xfc\xb3\x91\xf4\xec\xf8;\xdc\xdeU\x02u\x07\xfd\xde$\x1f)\x9f\x9dj\x9e=\x1f\x06\xdc\x1b3\xe0\x1c$U\x0e^=\x9b\xce.\x88\xef\xdd\x1b\x0fN\xdc\x06mX\xf20{\xfd\x9bW\x93e-\xbb\xf6\xc2\x16\x9e\xe7Y\x1d'\x19)_e\x8b\xbcO\x05z\x07\x83\xf8\x8bN\xf1}\xffl{a\xb3\x88\xc7\x08R%^\xbe\xc2\x11\xbc\xefZ\xa95\xc3}\xa1\xf8(%U;\x88\n\x0f\xe7\xf9\xa2\x15\xd9\x06\xe3\x11\x0d\xf4.\xe6N\x07\xa0\x10\xfdfn\xb4A\xde\xd3\x87\x1e1T#\x82\xd2\xb9\xff\xd8\x93\x8c;\xdfL\xe0E\x87\xeb\x10A\x11\xaa\x1fn\x18\x01B(L\xe0\xb2\xc3\xd4a\xa2\xd4\xd7y\x96\xd4\xb9K\xc4\xc7\xae\x84\xd1\x112\xcf\xd9\xbd8\xedl\xc0\xd2U\x7f\xe8B\x03\xb6\x1f\xa3\xd6\xb8\xfc2\xb4\xab\xaf\xaf\"\x92\xfdcC6\x82T\x8b\x00\x19\x92x\x86L\x08\x95\xf5\x9e\xc7iz\x11\xcf.\xd5\x8a\xb9F~\xa2\x87\xd8\xe0\x9c\x196\xbc!\xd7\xd6ik\xe7\xfc3\xcf\x19R\xfa\xde\xe1w^\x10\xc2&\"Y\xb5)\x89\x92\x14\x97\x03\x02\x93J\xf77\xab\x10=1\xde<\xc6\x13\xee\xd6XG\x17T`!sf\x0dQ\xf9\x1f\xd0\xacY\x8cJ\xdf$\x0b\x8c+1\x89o$#\xad\xb8\x9c\xc6g\xf4\x8bp8\n\x07\x83\xd6\xe9\xe6\xa2. \x9e\xf2\x92(8C\xacc\xc6\x82\\`\x11\xadbT\xaerH>\xa6\x90\xfcQ0\x1f\xba\xee\xd4N\x1c\xd6\xf7\x8bF|\x15]\xc5i\x82&#\x1c\xeb\xfc<\xe4|\xde\x8b\xb7\xaf9A\x11\x96\xec\xad0C\x0dr<\xf1B\x93\xad\x8c\x07\x94\xaa\x93\x18\x83\xa3\x15qU%\xd9\x12b`\x95!M. \xfca\x9e\\\xfd!\xc4\x97\x80\xfdr=\x85\xe8\x07\xdf\x07\x90\x97\xf0\xfd<\xb9\x82\x07\x7f\x8a\xd0-DL\xd0\xb1\xc7YJ\xdb\xc7\x0e_\xe6\xf9@w/\xf3\x9cu\xf62\xcfEg\x99\x1a\x03Z\x89U\xc6\xf9f\xec\xf5\xc3*\xa9`\x1d\xdf\xc0\x05\x81Y\xbc\xa9\x98W\xcd&K\xf0\x02!\xc9\xb38Mo \xcd\xe39\x1dP}\x9dC\x92\xcdIA\xe1\x9b\xd50\xcb\x8b\x84Tt\xc8lL\xdc\x07\xc7\xb0\xa5\x98\x9fX\xdc\x19\xf9\x0b\xd3m\x1bR\xf8 h\xe2\x9ci:\xb0\x9a\x9fRq\xbb\xe0n\xa7\x06\x05\x122H\xe7E\x99\xcfHU!o\xc6\xc3\x99\xfaUt>c\x7f\x1a\x15B\xf4\xeb\xa5~\xe2T\x92\x7f\xe3\xeb\xf2d`\x12\x8c\xa1QSa?\x1d\x12{\x0cSY\x80\x7f\xee\xcf\xd8\x15\x80Y\x07L{X\xb0\x1e\xfaB\x05\xe5\xde7\x17i2\x93\xf1\xbb-\x96)sa,k=[\xd4\x9237\xf3\x85\xf9\"\x14@\xab\xa1\x17E\x9eq\xba\xc3\xd2O1\xac@\x82\xa4d\x1e\x84\xb0\xd0\xb6\xa3\xbfk\xfd\xb1'\x07<\xc3\xd8xvS\x0e\xe0\xc0]!\x1f\x99\x19\x00\xb7\xa6\x12\"r\x84;o}\x93\x82\xfd\x06\x8e\xe0\x95\xb1\x89\x0b*\x82a\x13)\xfe\xab C\x00\\9\"\x89w\xf7d\xa5\"a\x16\xc2E\x08I\xe0\x88\x08\xc6C\x8b\x1bK\xe3\x92^\x07!\\\xdb\x8f.\xb7\xfb\xfcf\x95\x07N Ud\x1c\xce\x08\xa2_X\xdb%\xd6\xcf\xcd\x81\xf8p\xcfD\xe6j\xdc\xed:\"\x83\x8e\x0c\xc6T\xb5\xaf\xd0n{_Q\x96\x7f\xe0\x01\x020\xd4D\xa3\x9191\xd0/!V\xed; '\xaf\xcb\xddc/\xa7u\x8f/9\x0b\xfb\\\xcek\xa1;@\xeb\x98\x9e\xb7n\xeb\xa7F\xf7\xa0;\xde\x93\x10b\x1dD(\xac\x14N\x8e\xb9\xa5\x0d\x86c\xdd\xe0^\x1b\n\xee3\x8ffq\xf6\x9el*\x9e\x19\x8a\x8eb\xd3\xc92C\xc5\x0b2\x8bg+\xc2v:\xad\xa1oQP\xf6M[_6\x8f\x9e\xff\xf9\xe4\xf9\xff:\xfd\xe95\xaa\x16\x99\xf6Q\xdf\xc2\xa6\x97\x93c\xc4\xc7\xe2t\xd8D\xf9\xa6&\xe5\x9f?\xbc\xfe\xd1\xd4Ke\x1b_\x08\xdd\xa8\xbc\xa2\x88\x13b \xb5Q\xe1\xe2Y\xaf\x16\xe9\xba\x90\xa9\x97O\xe2\xce)\x94\x9e\x94A\xa8\xfaWf\xcc\xb1r\xb0e\x10\x8c\x80H\xf5\\\x06\x9c\xe1\x91\xbf\xe5j\x1b\x1c\xec\x85P\xc0.\x1c\xec\xa1S\xf4\xc7\x0c\xfc\x8a\x94W\xa4d\xd5g\xe6\xea\xfa\x99\xe9tWtg\x1dx!h\xaee\xfb4\x03\xb5K\x86F\x0e\x19\xaf\xdd\xd3\xef\x19P\x81\x07\x98r\xd5\x90\xe9'\x94GIV\x91\xb2\xfeP\x12\xc2\x1c\x1b}F\x9d\xe81`\xe4\xd3.X\n\x80P\xb3\xd3kE\xab>\xf2:\xefG|\xfa\x85\xf7O\x87\x8f\xbe\x0d\xf4\xcd\x9b\x8f\xa5\xc6\x0fH\x03$TM*\x1a\xe37|\xed\x98\x95@\xd9DS}\x1a\xa01\x8fN\xb9l\xd0A\xb1\x060\x00\xeb\xb1\xf6;\x98\xc8Z,\xe4+\xcf\xeb\xd7\xb3\xf8\xfb\x82\xab\xbb::?'\xd5\xeb|\xbeI\x89F\xcd\xc3C\xb2f~\xf7\xea\x0d\xc3\xe7b\xbc|4\x7f)\xd5f\x8e\xa1\xd4Z\xd8\xcd\x859\\\xdb\xb4\xeeV\x1d\x0d\xaf\x83r>\xff;\xaaVqA:f\xd3t\xe7\xce\xca\xe4\x82L\x94\x8at\xfa\xa8\xc2\xfa\xc7&)\xc9\xbc=\xe2yR\x15\xf4,v\xfe\x80\xf9\x94\xd5C=4+\x10\xdc\xe1\x12\x84-8\x98\x11W\x7f\x0b\xcd\xaf<\xc0\x14\x16I\\\x89\x90\xb2\xccK\xf5\x8e\x04\x1f\xf4\xb8.\xfd\xddt\xbd*\xf3k\x8c\x80t\xc2\xbfj/\xa9\xde\xbc\xdb O\x95\xcb\xe4\xc7\xdd\x1bJ~\x9b\xdc\xb3S\x14\xa9\xae\xba7\xa41\xaf\xdf\xc5\xde\x0d\x7f\xdem\xbf\xe2\xcf\xbb\x17\xc0\xfc\"\xb9\x97^\x80_$\xf7\xd2\x0b,\xf8\xf3\xee\xc5/\xbbH>x\xa2\xbbH\xce\xfc\xc3\xc7\xddy\xb1\xfb\xe3\xfd\xc3n\xfbW\xbc\xfd\xee\xb5\xfa\x9a_\xabw\xdbY\xf2\xe7\xddy\xb1\x1b\xe4\xde=\xf4\x05\x07\x7fw\xba\xe7\xbc\x99\xeep\xae\xf9\xf05W\xc4\xb4zw\x94\x9f\xf0y\xef\xda\xfa\xb4\xafN\x7f\x0eG\xddh\xda\x97p\x04\x0f\xdb\x8f\x9eQN@\x04\x00|V.\xf1\x12\xa9:\xebD\x18|\xab\xd6\x12\xa1\xeb\xba\x95\xde\xa9\x950\xf4n\\\xe7\xa5\xa9\xf6\x07\xb5\xb6\x88<\xd8\xae\xf2\x9a\xdfb\xcb\xdf\xd3gg\x94g\x9b*\x03.\xe3\x9b3O\xf7\xf4\x87\xcdbA\xca\xde\xbb\x17q\x1d\xff5!\xd7\xbd\x17<\xc7\x87\xee\x03\xd2{\xf82\xcd\xe3\xfa\xf0@\xdf=\xbe|\xf4P\xff\xf2UV?6\xbe\xd9\x7fd|e\xea\xecu\\\xf4\x9e1\x17\x14\xf1\xf8C\xe7-\x8b \xd8\xfb\xe8\x94\xd4\xfdg\xc8\xdf\xf5\x1f\xdf\xac/\xf2\xb4\xf7\xf8\xa7\xc487|\xf5<\x8d\xd7\x05\x99\x9bk\x98\xa6O\xdf\xb5\xe6O\xc9\xbc\xf2\x1e\xc9\xa8\xf8\xeam\xe7\xe3\xbf\x91\xf8R\x02ig?\xd4262,\xef\xab\x10~\x0e\xe1M\x08\xefu\xb7w/B\xbc\xbb\xc9\xe0\x1e\x9c\xf6\x99\xeb\x9f\xf8\xab\xe7\xfdW\xff\xe0\xaf.\xdb\xe7\x03ei_\xe1%\xee\x0b*\xb5\xc31\xbc\xa2\xe3\x90#\x98\xd0\xdfA\x10\xaa\xda\xd3\x17R\x84x\xd1ol\xe7Z\xcd[\xdaa\x9e\xe8\x0c^\xe2\xbdBWJ\xa5\x9f\xbe4\x89\xc1thW~M%\xee\x1fe\xd3\x18\xd5\xf7E\xf7\xe02\xc4\xbf\xa5\x1d\xff\x13\x8e`E[\xe9\xbd\xa5\xe5\x078\xa25\x8e\xe0-\x15\xb8\xf1\xafwz\x05\xc6\x85:\xc1\x8a\x8e\xe2G\x83\xaa\x03[\xf9 \xdb{F\xff\xfa\x01\xb5ToLr\x81\x98\xeeO\xac\xee1\xfcr\x0b\x13Xv'\xff\x13\x1c\xc3\x82v\xbd\xf1_0\x1d\xe7\x04f\xf4w\xcc\x7f\xf7\x1a7\x82F\xf4\xba\xf3z\xfa\xcf3\xd9\xc1\x1b\xee/\xfb\x8bA\xefH\xc7\xb8\xa6\x1d\xfe\x93N\xbf\xdf\xdb\xef\xcc\xbf\xde\xa3\x0d\xde{`!\x18\xcb\xa0\x8f\"\x7f\x85#x\x8f\x9aj\x1d\x9a\xfcU\x0e\xf2\xaf\xfd\x97\xef16#bF\x88~\xed\x0d*\xca\x08`\x92}\xe9\xd9t\x00\xde\xdcbXC\xbf\x14\xbb\xb1D&\xe7}\xd7\x12<\x08u\xe8\x7fn\xeb\xd2p\x9f\xf3\x02\xc7\x9d\x87\xa0t\x9c\xbbvLa\xf6g8\x82\x7f\xc01b\xc6\x1c&P\xc0\x04\xff\xbe$7\xd5\xab\x0c\x03\xe2\xf6:\xfd\x1b\x1c\xc1K8\x16{{\x02\x7f\xee\x01\\h5\xfd\xbf\xd1U\xab\x15\xde\xcf4\x93\xbf!5)1\xc6\x13z\xe8\x9e\xa1%\xfd\x0b\x9c\x8f\xdb\xec\xe4\x93\x91\x1c\xe7\xc1\x93.\x87$8N}\"\xaa\xef\x1e\x8f\x9669<\x12\xe6u\x81W~;\x18Z\xbc\x95\xeb`\xe4\xb8\xf7\x1f\x1b\x92\xc2\x1ety2\xce)?\xd6g\x85=x\xd2}\xbei\xc2\xf62\x0f[\x11A\x97\x1d\xa0\x15%#\x83\n\xdfV\x94\x8d\xe9\x19\x8b\xb2\x81\xce[\x14\x04<\xcc\xc6\xb0{{{}a\x02\xb1\x1e\xe8N\x06\xc1\xeab\xeb\x81v\xd8cX\xb9{\xd4\xf6\xab\x8d\xcb\x9c\xb4\xaeuG\xae\xf0\xe3\xc7z\xcc<\xec\xc9H|\xb0\x8f\x0f\xb7\x1dl\xe2+\xa9\xa0\x99\xc9\x18&\xec\xf7\xbe`\xf0]4\xcc\xa5\xde2\xfed\x1b\xa6\xfeF\xa3Q\xa3@\xaeZi\xd7\xa8L\xe1Z\xc6\xfb\xb0\x0f\x13\xc0\xe0\xfd}\xe2e\xbdc\x93\xa8KA\x1a\x0b\xb9\x82\xc5\xfd\xbc\xbf\xcf\xaebs?i:c\x1d\xa1\x14\xc9\x82\xf7o\x82\xa7\xb0\xbb\x1b\xc3\xf7\xb0y\x1a@\xc5\xcd\x11\xa65\xecB|\xa6?\x17Y\xe3\xfawr@\xa9\xec\x816\xb5/{\xa9\x9f\x06\x90\x8a^L=\x08\xf6\x87\x05\x0c\xcd\xfc\nS\x8a\x11\x96S3\x04\x9d\xdeo\xfb\x85\xefn%a\x0f\xbe\x1f\xf8\xa5\x01A\xbf\xc0\xf7\x91S*\xa6\x15i\x12\xab\x87\xe05*\x16\xaf{Y\xce\xb3\xd3*w1\xb7\x81A\x05c@B\x0d\xd5\xcbzZ\xae\xa6\xf5\xa7=H\x99\xf7$\xea\xe2\xd9\x0dV3\x05\xc9\x1f\x90\xfe1^w\x04N\xd1\x884M\xe9/\xafr\x9b\xc0\xbc^,q\xdayTs\\\x11\xb4\xdedQ}\xc94;3\xd8\xdb)\xb0\xa4k\xd9\x80\xc2\xcf\xfc\xfd'\x07\xc1\x17h\xcf\xbe\xf6\x92\x1bM \xf54\x03\xc3\x88\x18\xbd\xa4\x92l\x91k3\x87\xd1\x92\xe6Km\xee0\xc0\x94\xb5e6\x81C\xfdKT\xdcM\xe0a\xef\xa5\xc659\xb3\x1ao\x82\xb2nSrF\xb9\xb6\xfb\x9a\xfb\xd0~\xd3\xccOs\x96g\x8bdYEi\xbeDs\xc0~=F\x02J5\xdb\x00\xa8f\xa7\x89\x8d\x91`\x97Z\x92 \xcb[\xafDR\xc5\x12\xfe\x04\xfb\xa8\x87f'\x00\xa5\xca\x94\xb0\xee?\x05J&\xcb\xa7\x10\xef\xee\x06\x94F\xd2\ngjkZ\xb2\x89\xa0\xfa\xd3\x91\x12\x92\x95+M\x83)9\x8b\xe2\xa2H\x11\xe5\x06\x0d\xda\xc5\xe9\x1a\xd1\xb5D\xfd6&)f\x17\xee\x1e}\x88\xf7\xb3\\/\xdb}\x8fOY\x05\x8aD\xbd\xf7\xf4!{\x8d\x18\xd8{\x8fO=\xad[>^Vc\x0e\xa8\xca\xe4\x17\x8f\xa8\x99\xf4\x91\xc00]\xa7S\xc2\x9a\x07\x8e21]M\xe3\xd7\xb9vpc\x8f\xc4\xc6\x978\xae\xa5u\xfa\xb3\xc0\xc0`\x90\xce}\xc4:\xbe$\x7f\xae\xeb\xc2\xa7\xc4\x97\xbc\xa4\xaf)Y*\xf2\xaa\xc6\x1f\x06\xd5\xc3\xc5&I\xe7\xef\xc9?6\xa4\xaa\xd5\xe6\xd4\xe7\x06\xd2\xc1r{\xab\x1f\xf1G\xfa\xfa%\xa9\xf2\xf4\xaaU\x9f?\x1a\xac\xcfMM4\x9f\xf17\xfa\xaf+R&q\x9a\xfc\x93\xbc'\x95\xfa\xad\xfa\\\xffe^\xbc\x9a\xab_\xacHZ\x90\xb2\x8a\xe8\xf3\xbbEc7\xdc\x91\xc4\xad\xd6\xeb\x0c\xf0\x84\x9e\x96\x8d\xfa\x84\xfe\x10-\xf7\xe9\xd1\x15w\x1d\xa1\xb5\x8cGQ2\x81\xd2p\xd2\x98\xa3\xe3\xf2.'\xba\xa8<\x1aM\x8e\xe0C\xe8h\x91+\xc8\xc5\xa0Q>W~\xa1\x97N\x94r\xcd\xa7|a\x00=\xf0If\x1anF2\x15k\xceNDx\x0d\x83\xe7wGp\xd0\xb9\xdd\x00^\xb9\xe5\x9c\x7f\xf9\xfc\xd9\xc0A\xb0\xaf\xf5\x90e\xfb\x7fS\xc6\x17)\x19\x00e\xb6Y\x13Q\xc7\xc0\x10,I\x8f.\x01h\x82\x10C\x1d\xd9On\x01\xb0\x1e\xbf\xa8\n\xe9\x96#\x9f\x88-\xd3\x1f\x138Dl\x11\xad\x8c\xc0\x9d:\x9a\xfbY\x08^\xcc\xfd\x8a\xb3\xfe\xd4s\x17\xfb\x18\xde\x9c+\xef\xdaO\xbdRG\x05KL\x05\xb5_Gt?\x1f\x1c*\"\xaf?\x1d\x1c\x82J\x072\xff\xe1\x81\xf2e8<\xf8\xce\x97\xdfn\xfbek\xb4\xe3\xbe\xdc\xba\xcf\xc3\xc3\xc7\xe6O5R{\xfb\xd0o\xbd\x92$\xb2\xd4c\xb7@-\x0dr\x13c@\x1fy\xf6\xdb\x93T\xea\x07\x93\x1b\xf1M\xec\xb6.\x1f\n\x7f\x82\x83\x8e\xb5x\xc3\\\x1e\x9c\xc1q\xfb\xe7\xc4\x98\n\x8d\xb29\xbe\xa6\xf5Cc\xeb\x87\xed\xd6\x0f\xcfP\xff\x1eDW\x07o\x0bRbL\x9aWh^\x12\xd7 \xc6/\xb9y\x9d\xcf5\x1e\x9f*\xa8[\xa9\xddTE\x0b&kP,\x10&\xe8\xf87\x13\xf4#\xf0I\x10\xb0(Qy\xd39s\x84U\xd2r}\xac0\xc7\x96\x174\x86a\xab\xf6'\x01L \xe1W[\xfaE\x1e\x9e\x9e\x9e\xbej\xfd\xc5\xcc\x02\xc9@8K\xdd\x12\x8dC\x00\xfb\x12\x99\xc8\xad\xc0A\xbfnG\x84\x80]\xf0\xce1}P+QZ\xb5\xf3\xff\xfd\xfe\x9b\xff\xf1\xf7{\x7f\xf4\x83\xf3\xdd\xa3\xe9/\x1f\xcfn\x9fN\xbe\xff\xd3\xe7\xe8\xe3\x83\xe3\xf0\xe3\xc7?x\xde}\x96<\xed\\g\x99\x0b\x0df\xb0\\\xe8\xcc\xf3\xb0\xb1\xa1\xdbo\xfa\xad\x95~}\xff<\xf8\xe5 \xbc\x0dD\xd3J\xe6\x12\xff<\xf8\xa3@\x80\xe6\x83\xe9\xf9Y\xf0\xc7o\xf8s\xcb\xc6UF\x851X\xe7~M\x87\xd1\x0f\xa4nX\xdc\xd8v\xa0\xf0\x06\xbd\xfb\xfdtL\xa667\xb66+N\x1fw\xf6\x90\x03q\xc6\xc4\xcaDWA\xdc\xc1\xb1\xe0Vb\xcf\xeel\xb3g?\x7f\x86\x1d\x12\x15q\xbd\xaa\xfa\x8du\xaa\xb3jC\xb1-@Qs\xf1\xea\xfd\nR\xb6\xcf!\xc9\xa0\xd4\x9b\xa8*\xeaXZi\x9a\x1b\xa2\xcc\x03\x87\x85\xf7\xee\xd9\xfbg\xafO>\x9c\xbc?e\x83O\xa2:\xff\xa9(laSD\xb9\xe2\x0eg\xb4\xa7ibP\xa6\x8aB;\x8c\x07\xe9el\x83}\x1cX\x87\x04\xd0\x18j\xdbk\x8aR\x15df\x8c\x13\xa6+t\x95XX\xd1\xdc\xfd\xa35\xa9W9\n]-(\xbb7 i\xfed \x9c\xa8Z4:(]\xc1\x0c4\xbe\xc9\x06]-(\x85\xa1W\xb2D\xe8\xcd\xe0Gz\xa7\x97\xfe\x9b\xf6\xaf\xadT\x96\xa0U[b\xe3\x9a\x0bp*g\x95~\xe6\xef?\xee\x06\xff\x00n\xb6\x86o\xbby(\xea(\xa9\xde>;=t\x125\x98.$/H\x16\x17\x89\x91\x89\xe0Y\x15(\xae\x17\x0d\xae\xd3\xc9\x1ez\x1a\x16<\xa9N\xaf\xe3\xe5\x92\x94\x07#\xc6P\xb1O\xb6\x18\xc3\x81n\x0cy\xf1j\xce\x12\xf0\xd7Q2\x7fY\xe6\xebwq\xbdz\x8d\xf8\xcd\xdcI\xeb(%\xcbxv\xf3\xaa\xff6\xa6o\x97\xa4\x96\xc7\xf9\xfb\xf8z\x84\xf8\xc2\xd9[F}\x8f\xd9Ib\xd7\xd7J\xc9/\x12[\xd7\xbc5\x18!f\xbb\xd5\\+\x11\x8b\xcb&\xa1\xdf;x\xe2$\x83'Nb\xa3z\x89\x12\x19i\xc7p\xef%H^\xa2\xf2\x85\x83\x0c\xca4\xf7\x13\x19\xf0\"\xf6\xf9\x1f\x9b\xb3\xa8\xca\xd7\xc4\xb7\x03\x14\xba+\xc2\xee\x16\xb5uu\x91\xd7\x0c\xd9\x10\xd0>>\x9bK\xdc\x80#\xd8\xd0\x87$\x9e\xad\xd4\x87\x15\x8b\x93Q\xaeQ\xcb\xc5w\xc4\x98\x0dQ\x90\x99~mY\x005D/\xb3\xd4\xa1\xb3\xd9\xc1\xb5F\x96\xaf\x8e\xbe\xf9F\x8emn\xba\x8b\x82\xde\x89m\x0c2+\x0e\xda\xccx\xca\"\x9f\xbd\x17\xc2\xa2uZ\x0e\xac\x9d\xc0\x18\xcc\x92\x15\xafIMJ\x0d\xdb!\x8a\x1cgE\xc7\x19\x07\xb0\xe3\xb0\xe7D\x91r\xe0\x948\xf0\x08;\x9did\x0d\xf6{\xb3<\xab\x93lC4\xa9a\xd4r\xc5]qs\x9f9\x7f\x99\x9cqE\xa1\xddj\x83\x02uK9\xad\xa8tB\xffc\x91\xca3\x8a\xc6\xf8\xf4\x08\xa6\x99ev\xc0\x87\x86\x87\xcb\xb4r\xa8M\x076k\x84\xa6\xfd\x00f}{'\x13\xbd\xd4\x15\x12\x9d\x9f\xe7e\xb2L\xb28U\xc4)\xe6\x96\xa1}\x83\x12\x8cBT\xc2\xf6O\x96\xb7\x9f%L\xe7W\xed\xd6\x81\xe8\\\xab\xbbE\x86\x00Td\xc4\xac-\xf4\xba\xcd\x98\x02\xbc\x80#\x98M\xf7\x1c\x00NKa\x84\x91\xe9\x0d\x15P\xda0*:0\xaa\xac=\x9b\x19%\xfb[\xe4\xe5\x9bm\xcc\xce\x18\xeb\xb6\x04\x0e\x9d\xb9%U\x84ZV\x06\xda\xd7-\x92^\\QzQ\x07\xe0\x15e>\xdf\xcc\x08\x1f\xdc\x15\n\x02\xb3<\xab6\xeb\xf6\xb3\x8a\xcc6eR\xdf\x88g\x9f?\x83\xbf\x9a^\x9d\xa1\xb1\xdb\xd5Y\x08s\xb6\xf3V\xba\x0ca\xddB\x01\xb3A\xc6f\xa5\x909v\xa64\xed\xd0\xbf\xb97\xa0\x03\xc8\x80\x83m\xcd\x14\xf5N\xf5\x81{\x18\x98\x14\xe1\xbar\x03G\\Ab\x9f'X3pt\x8b\\\xa0\x8b\x10\x9d\x16(\xd1M\x1b\xa2;\x0f\x9e\xc2\x8eO\xa7\xe8_\xc0\x11\x9cG\x19\xf9T\xfbA\x10\xcd\xf3\x8c\x04O\xf9\xe4]\xc1%\n\xed\x8f\xb2z\x17,\x00\xa8\xdb\xbcD\x91#>\xa1(um'3\xdd\xc2n\x90N\xce\xc6\x8eZ\x94\xde.\xa3\x0c\xcf\xc9\xb6\xad\x01\x87\xc7\xa7\x91h\xa4+\xa7#QKW\x9e\x8fD7]\x19\x87\x82\xba\"\x17\xf92D\xa7\x95\x0eZ^\xd3\xe5\xa3\x98I\xa1\xe6_\xc2\x11<\xebb\xe6'\x8e\x99;\xf6\xab\x981\xe5\x8a\x87\"\xbf\xdc\x06uu\x85bb\x87\xd7v>\xc5mE\xde\x1be\x1e\x81\xb7\x19*p\xc4\\\n\xc4\xbcq\xfe\xd4q\x9d\xac\xb5\xb6\x150n\xfdJ\x0f\x1b\x8d\xf9K\xef\x89<\x89T\x85\x08G\x8e\xceMQ_E\xbb\xe0J\xd8\x87\xdf\xe9T\xb4\x85P\xd1\xf6\x82Z\x03\xf7\x17\xb6k(\xf8\xf0\x98\x07\xa4b\x11\xa1\\\x15rs\x08\x8d\x06\xab\xdf\xe9jL\xa7D\xb9w\xfc\xfb\xc7\xeb\xb3\x07\xcb\x84]\xfe\x0d\x80u\x9c\xe9\xc1\xe3'\x036\x16\xffo\x98\x1e\xdc\xcd\xd5s\x9a\xc7\xf3S\xa3\xc2\xb0\x94\x9c3\xd3R\xd0\xe6\x0d\xe9\xdb\xf5\xc9\xc6\xe4\xdb\xcb \x90(\xbf43\xf2\x9b2\xa5U6e\xca\\\xc5\x8c\x15\xab:\xae7\x15\xe6$\xc1\xbfl5Y\x8aPQ\x9b\xfe2\x7f\xb1\"\xf1\x9c\x94\xd5\x04\x12\x9fD\xfc\x87\x81B\xe8\x1b\x89\xe1\x08r\xf1\xe5\xd4\xe3y\x84\xee\xd3\x9d\xe7\x19\xf4\x10\x1b\xccC\xf9\xf93\x9c\xfb\xb1\xd9\x0f\xca\xdf\xa0kKM>\xb1\xf8\xe5\x17i~\xc1\x14X\x17\xe8'\x1e\x88\xcd\x1c\xd5+\x929(\xb9)\xc9\xceY{hH\x97G\xf3\xb8\x8e\xd9\xdf\x9b\xc0r\x00]\xf5\"\x01;(\xea\x84\xa63.\x8a4\x99\xa1\x02\xe9\xc1\xcf\x15\x8bO\xc1\\w\xfer\xfa\xf6MT\xc4eE|LA\xb4l\x8c>\xe3\x05\xf91\x8f\xe7C\x0c\xf4-\x1d\x85\x0e\x84\xa2\xe4\x98\x01\x01\x8e(\x85\xc8\xa3\xfc\xe2g0j\xf5\x9dX\x83\x9c\x8d\xf5\x84\xdbl\xeb\xb9\x01\xfd\xe9\xc3a\x91\xf7\xa9\x83\x9b\xe1B2\x9cT\xaaO\x19\xf6\x8c\x94a\xafM\x19\xf6\x18e\xd0\xe3\xaa\xce\xbf\x04\x94\xa5\x15\xe3SC\x8e\x10\xa1\xd6e\xf6@:\x1d\xaf\xf9r@ \xba9\xcd\xe8@\x85\xbf \x9a\xfaGI\xc5\x1d\xa1\xa6\xd9Y\x00\xc7\xac\xd2\x04\xa6\xf4\xff\xb3\x10\x7f\n\xb9\x8b\xe2\x93\xf0U\xd1@\x1d\xf1\xb7\x1b,s\xc0ld\xe0\xa4\xd0Gfy\x99\xf0#C\xc4\x89\x13\xcfd\x9c\xd1\xa3\xadl\xaeVm\xfb\x0dS\xe0\x17\x12\x15I\xf1\xa5\x06,\xcdM\xe3,Oy\xd6\x9a\x97\x98\xf0\xcc||\x90(N\xd3\xfc\xfad]\xd47\x18;\xd8|||\xd9\xcc\x8fE\xf2\x1dJ\x1f\xf5WX\xdd\x04@es\xfdb\xc8\xc8\x1f\xfb9\xcb\xdfp\xc1\xa2k\xa8 \xcd\xe5\xd7y\xff\xe3+\x91~'\x9b\xe5s\xf2\xd3\xfbW\x86\x80P\xa0p\x92\xa8\xcdM\xb8j\xe8\xa6\x99]\x1eX\x1dma\xd0\xfc\x16l\x81\x19\x95\xcf;\xf7\xe4:\xee0\x08\xcdW\xbe\xb9m\xa9rfd\xd4\xde\xbf8C\x97G\x18\xfe\x1d\x8e!\x8f\xd6q\xe1'A\xf4s\x9ed\xbe\x17zt\xf3z\xebMZ'\x0c}\xd4J0\xe9\xd4\xd7\x03`V]M\xc0\x0b\x0d\x06\x99\x15\xbe\xfd\x1f\x07{\x86\xf75{\xbf\xf7\xc4\xf0\x9en\xbfj\x02\xdeg\xaf\x0fP\xa4^\x94\xe9\xc0\x14\xd0\x9e\xe7\xb4M\xab\xe1{\xe0\xceU#\xda\x02\xce73U'7Dx\x85\xd1\xd64\x1b\xb8>\xa1\x9bvg\xa7\x8c\xaa\xcb\xa48\xa1\x88\x9ed\xcba\xab\x82\x9c\x87\xeb\xefo\x0bc\x88V\xe0l\x95\x1d\x83EQ9\xf6/\xa2)\xc6^ny\xe2\xbf\x9d6\x82v\xa3Q\x88\"6\xf84\xa1\xc7\xcf\xc6\x8f\x8d\xeeJ\xa2pc\x1fC\x1a\xd2\x10\xf2 \xd4\x05v\x0e)Oo$0\xeb\x86\x9dB\xa90Y\xa0\xe1\x91~\x14l\x85\xcc\x0e\x0eI6Of\x14\xa3u\xf1R\xbb9o`\x00\x8f\xd3\xdf\x8e\x95Aq\xc3*\xf9\x08\xee\xd4\xf3\xd0\x9d\\[=\xc7\xd6\xfe\xb1!\xa5!\x8203\xa9Y\xe4\xe5Z\x7f\xd0\x0c\x86fM\xfb\xfb9 \xc6X\xb3@\x83\x04\xb1\x9fL\xc9\x19;)\x07\x10|`3\x168\x15\x83\x8c\xc3d\x12\xf9\xf29\x7f\xf9\x01_\x9a\xed;P\xe8{\x80\xf4\xbb\x88\xcb\xfa\xe3\x03\n\xa9\xfbT\"y\x90D5\xa9j\xbf\xb0\x9a|\xf08j\xa6\xf8\x9d\x80J\x04.\x01d\xe4\x1a\xe6\xa1\x06\xa8=\xf6\xd4*\xd6\xb06\xa3\xb8(H6gAu\x92i}\x86\xf6\xbdC\x00\xd6om\xa6\xf4\x94\xe3\xac\xfc\xc40\x1d\x1ez\x98\xe1T\x7f\x07j\x91L\x1bq\x058\xf8V\x98)\xb2*\xd2\xa4\xf6\xbdco\x00\x01\xae\xa0g\x0b\xbc\n\xa1\x1b\x8aB-K\xba\x9b\xa6{\x03G ^ O\xf7\x07j\\\xa0=\x86\x19\x85nl\xf8q\x8e\xe9\x96\x04 db\xe6\xcd\x00\xb2t\x90#\xd7 \x87\xeb\xa6\xe3\x8bu>%f%6e\xab.ZCl\xa8\xf4\xf9PFmP\xa9u?\x0b\xa7(&\x8c3\"\xc4\xb5-\x9d\x8d(\xf2fSG\xb0C\x96\x0c\x08\xcfG\x12\xb0l\xbf{O!\x83\xef\x81<\x85lw7\x10bYC\xb8\x87\xac\x8d\x04gRG\x8b$\xadI9~1\xccZ\xfb[\xc1O\xde3\xb9@@\xd3LI\x8f\x84c\x0fv\xf1(\xf7\xfal\x1d \xa3p\x11BE\x99^}{L\xe1u\x04K\xd8\x85\xeb\xb0\xd9\xd4x\x928\xecj\xed\x94\xbe\xb2\xc1q\x08uT\xad\xf2M:\x7f\x91_gi\x1e\xcf\x9f\xa1Z\x8deg%\xe9\xc2p\xdd.\xed\xc3\xfc\xcc?\xe8eK\xa4Eh\xc5\xf7\x86\x94\xe2Z\xa3\xe6\xb9\xd0\xa7\xeb^\xae\x1a\x8b\xe7\xfe\xcb+\xf1Rc\x0f\xad\xba\x1a\x0b\x9b`\xf9\xec\xcf\xec\x8c\x136\xc1l\x07Ri\xf8m\xf9\xbf\xe9\xea K\xce5)\x97\xe4U\x86\xcf\xde\x96\xb4\x02\x1cA\x8ao\xb8\xc3\xb7C\xc0\x1bh\xd6Zz\xdf\xd8\x11\xdf,\x11\xb2]Y\x7fq3\xda\xfa\xb2E\xad\xfb\xad(B\xf2\xeeg\x90a \xbaK\xab\x9b\x03\xaa\x8c\xf5,2\x08\x82\xaa\x01\xbf_\xf2\xc8\xe85\xfe\x95\xf9\xa4\x97\xa8[6\xd1F}Z\xf9\xe0;\x8d\xc5\xfdZ\xa0\xb5\x169\x97\x02\xc5\xbe\xd5\xbd\xbd\x11\xdf\xf6Ru\x02?\xf5\xe4\xae\xd2\x83\xa3\xed(op\xda\xe8\x83a\x02\x9a\xf4\xee\xdd\x1d\xc0\x8f\"\xdbI \x88?=2\xaf\x14S+y\x94\xad\xe3\xf2RRj f\xae\nUL,!\x17Kn\xa0\x97\x01\xf6\x8d2\xc0~[\x06\xd8?\x1b\x08C(Ng9\xcc\xeb2.\x1c\x0f\x14\x16\x82\xfdi\x00\xd5u\xc2T\xc5QQ\x92+\xe4\x8d3\xf2\xc9\xca6\xce\xe2\x8a\xc0\xded\xb0\x0e\x08\xd3,\x93\x10[\xdb\x84X\x91\xc2\x1e5\x02\x14\x96u@O\x1c\x0c6\xbf\x92\x04\xac\xf9\xfb\xf3gL.\xa7\xdd6q\x10\xc2N\x1c\x95,\xa4\x04\xa6)\x9b\x91\xa2\xce\x07w\xb9Z\x18`\xe0\x08\xf6\x1d\x0d\xb1.J\x12_Zk\xda\xef\x87\xe5\xb5$\xef\xff\x11\x9d~\x7f\x1e\xda\xfb\x17\xb5\xe0\x9a=r[3\x12\xd5{\xcc\x1c\x9fdu\x08\xf4\xe7h8=\xf9u\xc1\xc4\x87\x1c;\x00\xe1\x89\x1d\x08,\xe3lmYjlm\xdfa\x1f(\xa7_<$|\xc6&\xe13\x1c\x96/y8+\xce\x81\x19\xbb\x90<\x9a\xb1\x1f~\xb8\x88\x08z\x92,\xec\x1f\x86\xca\x0ex\x14\x82\x8f\xf9\x1eJ\x8c\xed\x82\x071\x06y\xa1O\xcbt\xf8\"\x0b$\xe0\x1c\x90Q\xb2\xab*2\x8aa<\xa1{]=@|\x16\xaf\xd4\xadw\x07,\xa0[A\xed\x1a HU\xe4YE\xbe\x84\x82\x1c|\xf7\xebn\x8d.\x0598d$\xa47\x13\xa3\x0eP\x14\x84\xdc\xc1\xa1\x1b\xe4HT\xef\xb7\x89\xc8\xfexP=\xfauA\xc5\xc7l\xc9\x0f\xc3\xc0\xe0\x82\xbe\x8c\x8c\x18\x9c\xc3Da\xcd}goN\x82\xe5\xd0\x01\x83\x10$.\x1d;n\x04I\x0b\x0e\x9e\xe0b\x1e\xb0\xbb\xb4\xb8\x9e\xad\xfc\xfd\xc3\xc0\x10\xafFW\x9ai\x1c\xda\xa7\x01w\xb8\xba\xcc\xc4\x8b\x8e\xdd\x01.\x87\x0eh\xce\x1a\xf4s\xae\x94c\x19%J\xc5Z#\x08\xf8\x8f\xe7\xf9\x1c\xc3\xc5\xf2\x9fL]\xc5L@ \x97{Q\xde\xc6G\xf5A\xa8\xbb\x99S\x0b\x1b\xa5\x03\xda \x19\x8b\xf2\xcb\xd1\xeb\xf3\xd0\x02'Q\xeev}\xf0\x16\xd1\x0d\x9c\x89\x0e\x9c\x89\x04'}\x1cv\x93\xcfw\x0b\x82\xf1\xe1\x81\x1d\x8c\x92\x8c\xc6\x17\xe5\xa6\xa8}\x8f=\xf0\xc2^ \xefna]X\xf0 +y$\x9b{#\x86R\xd5y1`\"\xa9\x07\xf9-K\x93\x871S\xa7\xc6o\xa7\xf4\xcc?x\xa2\xd7\xf9i\x02\x18\xdc\xea\xd4D|\xa0v\x85t\x03\\\x16\x92\x10\x07'%![(\x8d\xdbnVB\xa125*{\x06%B>\x98\x07\xfe\xcfU\x9e}\xfe\xb4N?\xdf\xc4\xeb\xf43\xa6\x00\xfdx\xf1\x80\xf1\\_|\xb9\xd3\x8d\x10\xb2\xad9\xe1\xc3\xfd\xffxk\xc2\x81\xc1\xb4/1I\xa0\x06Q\xfe\x1eCi\xe2\xd5\x97\xf7\x00\x83\xa0\xe0M\xba]F\x16\xe6\x04\x99`\x02\xddkTS\xe3\xb3\x01\x13)#\xa3\x85\xbaR\xba9\xd8\xbc\x9b\x00\xcfti\xce\x95\xa5\x19GZ5S\x991+g\x9d9\xaa#i]\x0c3\x19\xeeW\xa4\xfc\x0b\x85\xf1\xd2\x8d\xcaiL\x85\x9d\xf1\x19i\x94ua6\xca2\x0db\xee0\x08Q\xb9e&\xeb\xd4\xfaJ\xdf:zAY\xf6\xb8\x88\x9b4x!\xe1\xc5\xf3\xb9\xb0\x8a\xff\xfc\x99\xb2#\xeb\xfc\x8a\xb4\x9f0\x06\xc5\x10\x99\xc6\xb8/;\xc6Z\xa6 ^\x0d\x82\x0f\xa7\xff\xf93\xd0\xb9\"$\xd7\x9b:\x16\x90D\xc9\xfb\xc6\xd1\xd4x=\xd8\xcf\x15o\xdfo\xe0AA\xd7\x07\x80|\x8a\xb7\x16\xbag/\x08)\x9a\xe7n8\xb4t\xc0\xa1\xaf\x8e\xc87Fcl\xb3\x87\x06\x1f\xe1\xa9\xbc\xd6Z\x92\x1aM\xaf\x7f\xb8y\x97'\x19\xa5\x08\xfd\x18\xb8\x00.n\x0f\x82\xbcw\xb2\x86\x86\xda\x88\xd1\xbf3\xff\xbas\xa3\x84\xbe\xecz1t\xeb\x7f\xce_\x1ej\x0d\x06\xae\x87\xec\x10N\xc4\xa7\xda\xdb\xdcO\xe26W\xf7\xf2T|\xaa\xb5~x>d\xc3p)>\xd5:\x0c>\x13o\x1f\xf7\x8d\x18\x9a+\xdc>4\xe3\xf9|2,'\x8b2(3\x81\x90\x9b\xe8>\x1d0\x1c\x1c\x92\x9b@\x91\x9d\xb4\x154\x08\xd6o\x89\x93\x85 $\xbaw\x94\x8a\xde\xe9|9a\xb6Ny\xfb !\xf5\xba\xab1S\xba\xe8\x1a'\x8a8\x899\x19\xca\x86\xa3\xe5\xdc\x06\xdd %\xad\xb7!L\x87\xb6\xa3\x89\x9a\x9b\x0e\x1ae=\xdb\x8a\x0b\xdd\x9a\xdaV\xf1\xaa!\xb6\xe6\x11f\xcc\xeb\xf85\xa9c\x1c\x1d\xa9\x00\x83}\xadI\x8d\xaa\xcd\xb5_3\xd5B\xc7\x8f\\\xd0\xfc\xcf\x9f[xEk^\xe9)\xd7U\xc8\x9b\x15\xe9l\xafl00\x9e\x85\xf5Y\x10\xde\xf1\xc8m\xc0\\v\x0e\xc7a<\xbb\xd0\x83`)A0\x1ee\x14\x06\xe0\xc2\xc8\x00h\x9f\x8a\xdd\xd7{\xa9a\xcf\x8a\xb8$Y\x8d\xa1\xba5<\xda\x10\x83\xd6\xf1\xf0\xac\xed\xf1\xaa\x95\x84\x9aG\x98B\x17\xf1\x95]\x9b0\xbf\x97\x92\xf9\xbd\x18aE\xfbE\x9f\x18\xd4\xc3\xa2s\xb0\xa5O\xf1\xba\xef\xfd\xa3\x01\xc6\"\x8d\xeb\x9ad\x13\xd0\x04}Yl\xd2\xf4\xe6\x8d\x08g\x84s\x1e\xe1;\xbe\xf0g~\xea\x93\xae\xf6\x1a\xf4\xe3\xc8:\xddh<1\x93\xea]\x99\xaf\x93\x8a\x8c\x18D\xc1\xb5\x86s\x9f`,\x14\xa7\xb1p\xcf\xae7\xe4\xda\x117\x86\xe3\xa3\xf0\xa1\xe0}m\xa5U\xb5\x01\xb8\xa8\xdb`\x08\xcf\xc1U\xc4j&\xf7\xaeL\xd6I\x9d8kA\xdcg\xb9\xf9\xcdg\x99T\x7f\xa9\xf2\x8c\xcb`+\xdd\xfb\xe7L\xde\xed\x89i\x16\x84\x92jn!/\x9b\xb4\xdc`\x1a\x18\xefQ\xe3\x1b\x9fT\xaf\xb9&b\x02W\xba\xd7\xcf\xe6s\\\xb0\xa6\xdaZW\xed\x7f\x92\x8c\x94q\x9d\x97#\xe6\xf5\\\x92d\xe5\xfb\x97\xcd\xd7ns\x13\x1fL@\x93P \xa9\x18\xdb=\x81B\xf7\xf2\x84\xe5\xaeu\x1eq+x\n~\xdc\x1fc\xeb \x95\xdf\x15C\x1f\xa9\x0c\xfd\x9dRap#t\xa3\x8e}A\xae\xb4'\xdb~\xba?\x94fm\xf8\xd3'{\x03\x86M\xb6O\xb7\xcebw\xb0\xf7\x9d\xf9\xd3\xff`s*q\xbfw\x07\xfeJz>\x8c\xe5o\xe8;\xae\xe8k\x97\xbcv\xcfF]_\x9d\x850\xb8N\xea\xd5\xf3\x92\xccIV'qZ\xc11xI6K7s\x82&`U\xbc&\xf7Y\x9cx\x8d+\xb6`\x03\xc4z\xdb\x14yd@hB\xe7\xbe\x81Pm\"p\x9d9\xbd&`G]XML\x01\xecX\xf5\x1e\xb0\x8cyTA\x8d\x177,\xfc=\x9b\xd1\xb6&\x9a\xd0g\xc6\xcf\x06\xd2\x1b\xcd\x9a\xe5\x99h\"\x88\x01\x8aw\xaea\xe0@\x95c/\xf2\xb9>x\xa7.\xcb\xc9\xef\xcc\xbf~\x85\xdb\xbdd\xe8\xb2,\x1e\xf0\xe9]\xc7\x97,\xb7\xf2_N\xdf\xbe\x11N\xbd\xb3\x94\xc4\xe5\xf3x\xb6\"6\xbb\xd6**\xd2\xcd2\xc9\xaa\xa8$\x8bJ\xf9\xb0cB|\xeb\x9aQ\x1eT\xc2R\x9b\x17J\x10\x97z\x95\x18\x92\x99\x9c\xa0X\xd8\x19\xe0<\x9f\xe1\xf0X\x14]\x12\x84\xdd\x19,TX\xf8\xd7C\xeae\xddf2\x84;\x01\xd3f\xba0\xe0\x97~JB\x8c\x9a\xb6\x07m\xd0i\n\xeb \x01N\xd5\xb0cI\x81\x931MM\xd3X\x13\xf2>\x08\xf5\xdf\xad\xf5\xdf1\x9cN\x08~\xc7\x8f.$\xec\x85\xb6~\x9c\xa6o\x17A\xd8\x8d\xf9n\x06\xb55k\x9b\xbc\x11\x1a\xa6<\x17qE^\xe4\xb3 \x9clCi\xf8\xf0\x07IfW[\xa1\xe5\xbdE\xa1\x82\xfe\x8b\xa4\x9aQ1$c\xec\xaa\x86\xebmj\xf3\xd5y\x1d\xcf\xca\\\xcb?\x8b\xb2\xce\xe7$\x15\x94\x86W\xefGE\x01\x854\x9e\xbb\xe4E\x86\x8eos\xdc\xac]b\xf4mv\xd5\x1b&\xdb\xb8\x1d\x8b\xf2\xa5\xee\xc7\xa2\xb8\xba!\x8b\"\xcf\x8a\x9e\x07\x87\xc9\x16\xb4[\x98\xeb\xa0[\x8fc\x1c:D\x91#\xb48v\x882\xac\xf2\xe6\x8e\x1e\xe6f\xb4>\x1b\xa283D\x9d\x0f\x9c}8D1(\xd2\xfd\x00&0\xeb%\x13\xb3\x9d\xe6\xa0\x90^\xc2N\x083\x8b9\x94pl1\x1cd\x8bE\x92\xa2{W\xff~\xde\xc4\x8fT(\x8c\xbe\xee\xaa\x1d\xb0\x0b3\x17\x19R\xdc\xb1]\xd2\xa3E\xfa\xcak9\xc66}\xd1\xd7^\xf2\xa6U\xc2\xa5\xaf\x89\xf1\xe3\x9dy\xf9\x0b^\xdb\x91\x97?g\xebr\x99\x14B\x97\x87<\xa7\xbe\xf25\x8b\xe7U\xd7\x1a\x19\x1d\xb8\xc1\x13\x89\xf8Ibd\xfai\xad\x13tc\x0e\xb1E\xbc\xd5\xbe\xa6\xffl\x04\x9d\x0b1fN\xed\x97\x18\x91\xd1\xcck\x8c\xe03\x1cy\x8c\xdb\xc0?\xe1t\xbf\x9b\xfa\xbd\xcfZn8\xf7\xa8\xb5\xb4\xe2\xd2\xfc\xbe\xe6\x15K\xbbY\x19Rnf\xfe\xd6\xba\x83\x83\xbd\xad\x93\xbb?\xd9Z\xfe\xdfZ\xfa\x1f\x18\xabU\xf6W\xdf\xdc\xb9\x10a\xe2\xc8\x0d\xfaOy\xa2\x9b\xd9\x03TAE\xb3\xb8\xa87%9\xad\xe3\xd9\xe5\x872\x9e\x1186\xbd\xe1\x04\x9d\xfe\x1b\xcd\xf2\xac\xaa\xcb\xcd\x0c\xdd\xdf'\xecYEkR^C\xfan\x06\xec\x99\xe5\xaaA\x1fx+k\x05\xde*Y\xe0\xad\x92\x05\xde*ww\x03\xc8\xa6e;\xf0Vi\xe0\xacqpkRU\xf1\x92`\xae\xc6\xbd\xb3\x90\x99\xd0\xd4\xad\x93J\xa7l7\x11\x8c\xac\xb9\x8bW\x9dUC\xf5\x05\xcf\xedC\x8f`\xf5\xa9\x02:\xfai\xd8q\xa8\x1a\xad\xf5\xfb\xed\xf12\xa9^\x96\x84\xa47o\xe25\xb1\xe7w\x90\x86\xe4S\xd2\xf2\xc7\xd1\xae\x1d;\xc4\xa5\x0b\x9d\x91\x80\x97Q\x92\xcd\xc9\xa7\xb7\x0b\xca\xa5\xfc \xee\xefS\xda\x9d\xcb\x87Y\xf30q\x0d=)WZ4BX#}$\xb1\x12e\xf4i\xf2\x1a\xb9K\x17M?\xc7:\xb80 \x1dX\xe5\x85\xa0f5\x0b\xc1\x13\xe7\x05\xfe\x10\xf9\xf8^\xb4\xbf\x98\x89\x90\xb4\xd5\x83j\xb6\"\xeb\xb8\xfb\xb4\xd5\x88\xf2\xbc\xdd\x95\xda\x0c\xef\xe8\x946\xa7\x1f{\x82cg\xfd= \x9f\xe2u\x91\x12\xefl\x0c\xc6v\xc8\xf7\xc3/ \xc3\xadW\xff\x96*X$G\xc6\xedp\x07\n\xda\xfe6B\xf3\x86~03\n\x87\x8cG\xf9\xc3`\xef\x8c\x9c\xed \xc5T\xef3r%\x91>\xb9F\xab\x8f~'\x1d!TP\xdd~E\xb1g\x90r\x97\xa4\xca\xd3+\xe2w\xb5\x82\x96}[G\xf3\xa4\x8a/R\xc6]-\xe2\x19\xc1\x00Q\xdd1\x84\x18]\xfb\x92<+\x92\xeaC\xbc\x94\xd9C\xfd:\xd0G)\x1e\xa2A\xb34!\x99\\\xc1Nt\xb7\xdfL\xcbxh\xd62\xfah\xed\xffm\x80\x91\xe4\x1e\x05\xba\x8a\x82\xa1\xd4\xa7\xf3\xa9\xc4[\xad\xb7A\x8a\xbb\xf9;\x03SY\xfa\xa9!\x8cb\xe6\xef?2\x06Q\\\x0cEP\xd4\x86\xb0[17\xf9'\x86\x00\x8a\x99\xff\xad\x8e#^s\xbe\xb7\x0d\xd8\x1ce\x0d48\x94\x82A\xae\x06CL\xe5\x8f\xe8\"\xc9\xe6~\xb6I\xd3\x90\x7f\x16\xf0X\x1f\x14\x9f1m\xad\xd2\x04\x7f|\xba\xb9\xa8KB\xdf\xce\xd5\xb7\xe4\x13\x99mj\xb4\xd0\x11\x7f\xd3\xc7\x9d\x18\x8fi\xebA\xabB\x13\xf01\xed=\xa4\x15\xdbJd\xe5g\xc82\x85\xb0\xb3\xe1\x87M\x92\xf2f\xae\xa2w\xcf\xde?{}\xf2\xe1\xe4\xfd\xf9\x0f?\xbd\xfa\xf1\xc5\xc9\xfbS\xd3f\x82#Xi_\xd0\x0f.h\x9b\xef\x99\xd4\x84\xed\xaa\x0f\x10r$-X\x9f\xfd\xdd\x90\x17\xaf\xe6\x13Xc\xe2\xfb\xf6\x86\xc0q+-\xc8\xac\xd1\xe2\xf1\xffY\xd8\x17\xfe\x00\x9d\xfc\x98 \xc5\xfe4\x99\x8e\xdao [\x14\xa5\xbd\xcbm\x17o*n\x0d \x84`\x1d(.\xe8y4\x96fe/l\xf4R\xc8\xc3xt\xef{\x83\xbe\xbb\x94\x08WRi\xcf\x02\x88\xd7\x06\xed/\x89Vy\x85\xbe\xba>\xff\xf3\x082\xfc#@ 3I\x80\xbf\x17\xbf\x8e`\xca\xc5\xdcY\x9e\xca\xe8(\xde\x84\x8a\x13^p\x86_^\xc4\x15y\x17\xd7+\xfe\xa9\xfcy\x04T\xba\xb3/\x80\xaa\x03\xc9\xc7\n\xca\x16e\xd3\xde\x80\xd01\xfc\xe9\xfe\x17\x98\xb8l\xadW{\xb2\xf7h\xdbO\x0f\x1fn\xad\x1f{\xb27` \xf4\xef%\x9a\xa9\xbf\xee\x9c\x1bG\x9bdv\x01\x89\xb8I \xd5\xeb\xb8\x18\x08.\x9e\xc3@\x84\xf0d\xc8\x1dX\x1a\x0chu\xbe\x9b![\x83j\xc8W8\x15\xedj\x87$\x82\xa1\x1fj\x9d\x85\x17C\x9e\xc42C\xa86h\xb4\xe0\xe5\x0f\xf6\x86\xdc\x81\x87Y2E\x14\xbd\xf6I@E\xc1\x02\x8d\xb6\xad\xaa\x1a\x11n\xfdP+5\x89x\xeb\xda\x81\x8b8\xda\x87\xda\xb7\"\x8e\xf6Cm\xc3\"\x8e\xf6C\xed2 o\xf0\x87Z\xafm\xe1\x0e\xfeP\xeb\x98\xed\x94\x08A\xb9\x00\x1e<\x80;\xf9\xb5\x98\x98K\x82^.\x12\xf6b\x98\xcdd,\x92g\xf1'\x99\x93\x8b\xcd\xf2GrE(\xe7\x98d\x8b\xdcR_\xde\xfaO-\xael\xac\xe2\x9f\x93\xaa\xce\xcb\x1b\xb3\xd5\x9a(\x8cy\xb07+|s\x1d\xaa\x16\xcc:|.Y:\xdb\x07U\x1dSi\xc46\xd4\xc2\xb5\xbd\xc6\x0c\xc3\xd2\"\xaf\xf8\xa1$d\x82\x9b\xea\xdc,4\xa9\xa5Z\xe5\xd7/\xe8\x02\x9a31\x89\x12\xa7\xa93\x1c\xd8\xd2Q2M\xa5 FY-h\x91&\x17\xafI\xbd\xca\xe7\xd5\xa4\x8b\xab\x9dd0\x14u\x035\x10\xbcu\xdc\x1d\xc6\\\x93RJ\x14\xca\xc1\x04\xfc\x06eI$\xb7w\xbe$5S\x16\xf0\xceE\x05n\xf3\xad\xd6\xe3\x8f\xfa\xd5Wq\xf5~\x93\xc9\xaa\xecg\xbf\xdau\x19\x17\x05\x99\xbfk\xce&\xfaT\x98\xfa\xac\xe3\xc2\x97\xd5X\x1d\xa5\x89@\x84\xe4\x91\xc0\x89\x1a\x13j\xd1\x01\xc7>fD\xd4T\x8c\xe7s\x7fz\x166\x1cp`\xf9\x80\xe3\\\xf3\x11\x7f \xbf\xdb\x14\xf3\xb8&\x1c\xec\xbe\xda\x94\xde\xd2`\xd0\x11\x87\"\xc1\xbcA\x02\x12\xc2\xd4L\xbd.\xc9\xcd\x04<\xa4L\x03h\xc7Y\x03\xbb\xee@\x14\xe4\xef\xe94\x1a\x9a\xc7\x8c\xf5m\x1f\x82z\x9bV\x87Z-1\xbbBc\x17j\x19\xaa\x8c\x8f!\x83\xfb\xb0\x0f\x13\xd8\x0bBd?\xf6\x9fB\x0e\xdfC\xf6\x14\xf2\xdd\xdd\x00\xcai\x8e73\xadK\xb6\xdc\xc1%\x17\xdd\xbfy\x94\x95 J\xf3e\x13\x86Jc\xbd\xa1\x16\xb39\x8b\xc1Fd\xe8\x90a\xcbtE\xca\x8b\xbc\x1a\x8a\x04\xb1\xd5B\xc9v\x99\xf3_{\xd9l\x0d\xc0\xbf\xcf\x82M\xbd)\x06\xce\x84]\xf0\xce(C\x7ff\x8b\xca&\xcaWX\xcb\x86*\x8dYNKx\x05P\x04dAE\\lk\xd4\x827\xb9\x83*\x13Qr\x83\x08\xd0-B\xfa\x99*\xf4\x99\x9ex\x98F\xb8d\xd70h\xf4\xde\xab\x10\xc0\x04t\x04\xda\xc7\xb0m9\xbf\xc9Qk0\xe9G\xc4\xab\xca\xad\xdcu\xb7\\m\x93P[\x14>\xd1\x9d^\x889\xcc\xc5G\xaeHy3\xce\xb1Y-R\x86<\xe2I\x98\x9d\xbe4$\x1bkU\xb1o*\xde\xb7T\xd4tL-K?\x0f\xc1\x988\xb1[0\x16D\x08\xb3\x10\x16!\x14\xe8\x14\xbf\na\x8d\xee\xab7\xf6\xb1\x80n\x85p\x1a\xc2\xf3\x10.Cx\x16\xc2\xdb\x10\xde\xb9A\xbe[,+\x11o;~\xd0\xadL,V&\xdeje\xbae\xdb\x95\xea\x16\xcch\xdd\xa7A\xf9\xa8\x00\x16C%\x96\xf9r\xb6[\xa4nq\x0fk1T\xec!*l\x85\xa5b\xb8$7x\xd3\xbf\x98.T#\x9a;\x07\xde\xc3\xff,\xe0\xf1\x9d\xd7L\x0f\xe3D\xe3\xd9\xe9\xa3>\xf9\x92\xdc \x0d1%.u-,\xe2\xff\x97o\x93f\xa4\x8f\xbfl@\xe0\x96\x11\xc4V\\\x93H\xd9\n\x9a\x89)\x98\x1b\xa2\xe2m1\x9d\x9f\x85\xa8G[H\xab+\xd5l*\x08Q\x8d\xa6>\xc2\x93\x1dC\xa9\xcc\xf1\xcfu\x88\x87B\xa2\x0dD1\x9b\xe6\xd17\xdf\x94dq\xc6\xb2\x95\xee\xec\x85\xa8=\xdb\xd9gf\xbf\"\xed\x91\xa4\x99\xfb\x0fC\xb4\x0d\xee\xb8\xbe\xd0\x9fU\xf3\xd3\x98 \xd3\xb58\xa7C\xb2\x15J\x1c0\xce\xc5'8\x82\x13\xc4\x1d?\x08\xa2y\x9e91r.Eb\xe4\xe1\x7f\x18m\xc0\xe8&p\x04\x9fD\x10\xf9\xe7p\x04\xf9\xf4\xf4,\xc4\xf8\x95\x0b!\xf7\x9c\x06!\x86\xac\xd4\x9c^\xcf\x83\x10\xdeb\x96\x17\xc4\xb2\x10\x06\xd3\xfa\x8e)\xf1\xd8\x84H\xb6\xf2\xaf\x04\xf5\x9dg\xff\x0d&K\x91^W:\xb2\xf6\x16\xe5\xb6\xd9\xf4\xed\x19\xd2\xb4\x80Y\xb8\xa5d\x19\xd7\xe4\xff$$\x9d\xfb\xa5\xcf\xd8\xd6\"\x08\xc1\xab\xf7\xbc\x10\x0e\x1e\xdd\x05\xcdr\xc9\x81e+\x18x\x9aJ{\xa7,d\x0c=\x83\xef\x1c\x1f\x0e-)\xb8\\\xcb\xbf\n>P\xa0\xbd\xc3\xcc\x06\x19\x8b\xd0\x96a$\xbbw\xff\x0d8K\xe9r\x80\x87\xfb\n\x0b\xf8\x1c%\xbcK\xcc\xddZ\xdc\xc5\xfe8tt\x15\x1c*\x82Q\x89\x9b\xf4\x8b_62\xb8CV\xf0\xf0Ny\\\xc7\xcc\xaaC\xe5\xce&v\x07\x94M\xb2\x91\x87\x98\xb3\x153\x0b\xc6\"c\xde\xc3\x80\xf3\x9e{\x8c\xf7\x8c\xadi\x02m\x85\xc9\x1cw \x9b\xcbq?Ty\xe1\x87\xfb!\xec\\P2s\x12\xf1]\xa4\xfc\xddM\xc05\xb68\xa5Hs)\x9426c>\x0ca\xe7\xfc\xce\x89\xe2\xc3;\xd8\x81\xf0/D\x14Y\xde\xbd\xeb/\x9b\x14[\xc1;\xd86\x92D/\x92,\xa9V\xfe\xc3\xc3;\xc1-\x87D\x89\xb6\xd2\x1b\xd9\xde\x9d\x8c\xec\xf1\x97\x8dl\x1b?sS\x913t\xf4?7\x95\xedp\xf26\x84\xd8\x9e\x98\xd0V\xa6Tj\xa7$\x97\x92\xaf\x87\x8f\x1dB\x1a\x9b\xca\x94\xd2\xbc\x10\xa9\xc8\xc3\xef\xdc\xee\x0e\xba\xc5\x10\x15r\xa8\xdc\xb2\xc4\xf1\x9d\x8b\x83\x9b D\x9b+\x0c\xc9\xcb\xcf\x8d\x82\xeb.\xe6\x8a\xeeBj\xe2\x1f\x852f\xac\xa2\xba\xc8uw\xf8\xdd8mc\xf5\x19\x88\x81[`1\xa5\xd5\x18\x84x\x8d\x1e\x02w\xa1\xae(%\x97\xb4\xa5zb;\x9a<\x1e\xdf\xf9N[\xc2\x11\xac\x85\xc6\xa1\xec\x88m7\xfeR\xbcZ\xf28\xa3K)\xc1\xed\xefo\xb3J\xfb[p\xa4\x02\xdd$l\xb7\xd0En\xc1\x97\xb1\xf1n\xc1`\xcaq\x1el\xc1Pn=\xd0-N>\xb9W\xf7\x1fQ\xe8\xb2\xd4\xd3\x9cA|\x14\xf0\xfd\xbd\xc7\xf6w9\x9a?d\x12\xfa\x16\xfc\xa0\x1c\xd6\x81JO\x0e(\xff\xb7\xa0<\xdfJ\xe1\xffV[\xf2\x7f\xce\x99\xc4\xbb\x85%3\x16c\xa2\xfc\xdd\xd6\xf7}\xe5\x97j\x8b~-Z\xc1\xf8\xb3\xf9\xb8An\xad\xa0\x91\xee\x8c\x9c\xcb9\x18\xcb\x7f9\xe73\xef\x96^\xcfc\xf9+\xd6\xf3\xc8\x93\xe8K\xf8'9\xe2\x91\xfc\x92\x1b\x0e\xdc\x86P\x8e\xe7\x87\xa6\x8fB$(t\xf7\x1e\x8ca\x7f\xa6\x07\xc8\xee\xd0Mu\xe0\xc8\xee8\xb07\x16k\x8a[\x9f\x04}\x03\xe2\x9c\x99\x1d\x96\x81\xcd\x8a\x18\xa4=\xe8\x9bxM&\xc0\xa3.|\xfe<\x14~Q\x94V\xe8Y\x95!\x92\x8f\xfd\xdc2\xfa\xd1Q\x8d\xecVN\x94(\x8d\xb6r\xb2\xd1@\xbbw\x9b(\x8aE\xe4\xaam\x16\xdb1\x1eU\xbc?\x9c\xcc\n\xa4\xf7\xd6\x92\xd4\x82\xd3\xac^\xe6%k\xce\xaf\xd5\x8c\xae\xbf\x0d\xd0U\x83\xec;\x84\xbd4\xec\xecX|\xb72\xd8J\xc9K`\xa1\x0c\xb9\xd2\xfb\xcc-u\xa7Z$\xe8q\xe8\x16\xe0~\x05\xe8. \xc7hno?\x02\xb8\xd6\xf9\xa9Q\x13\"\xd9\x11\xa5\x06>\xb1\x1c\x1f\xaa\xd7n\xcb\x1f`Z\xf3\xfc3_\x11\x14\xef7\xd9\xf3|\x93\x0de\xb0\x1a\x0d\x0buB]\x98\xfbDl\xb0\xaf8)\xde\xd7\x87d\xc8 \x7f\xf4\xb4\xf4K\xdc\xcc\xcbm\x951\xe2\xcf\xb4V\xedeX\xf2\xaa\xaf\x08\x0fA\xe7^es\xf2\xe9W\x03\xc9\x87\xa4\xc0\xe4\xcbj\xe7N0\xf2\xb2\xcd\xfa\x82\x94\x1e\xec4\xbe\xd9p\x0c\xf7\xf7\xc1\x94&\x0d\xee\x04Lt\xb7\xde%t$\xbdkX\x83\xbb\x1f=w@\xd8\x96\xae9\xd8\xc8\xb6\xcc\x92\xc7\x916_C\xd4\xb2\xb3\xb6\xbf\x87\xf2\x9c\xa7TG\x1f\x8c\xa1x\x91_\x08+v\x80}E(\x0d\x03\xa5a\xf1\xda\xe9;\xe8f\xe1y&F\x1e\xach\x8d\xd7\x0b\xec\x1f@\xc6\xbd\xcd\x19Dm\x8bE\x0bf\xd8\x19NY\xa1\x16\xb4\x9b\xd0\x1aqKV\x025\x82\x19sK\xf0\xbb+\x00\xde\xff\xcck\x88!\xcb\xb3\xfb,\x0f0\xf3\x1b\xf3Bp\x19-\xf0!d\x91\xf4\xf1b\xb1\x83\x1b?.1\xf5\xb0\xc5Ys\x1e\xcb'2=\x91\xf0\xd5\xec\xb19\xcd\xf7l\"\xad\xf7\x1fV$s\x82+h\x8cM\xd5\\\x1a\x1a\x88U\xd2\xcd\xca'\\\xed&\x86\xbb]\x7f\xe2\x14\xd0\xf4\xc5\x96E\xb2\xc3\xba\xcc\x15\xdd\xe2\x96\x93D-\xfd\x8c\xc7]\xfc\xb463,\xb0~\x0d\x8e\xbc\x03\x991D\xc3\x06\x97v\xe6\xebvL\x16\xb1\xd2hO\xd1qJP^!\x19\xd5\x19\xe3\x88Z\\\xf5\xae\xc8\xb4\xbf\xdc6xdA$q\xba+\xfesM\xe2)\xe6BW\xc75\xc1\xf0\xbev\x14p\x0c\x1ebY\xe1\xe1\x11\xb3\xc0\x14\xd8\xaet\x81mvp3dJ\xa7\xbf\x02\xb2\xb0\\\xc6\xdb\npV\x84iq[]:\xd5\xc4\x07\xb4\x81\xe8{\xd8\x13!n8U\xfeP&d\x0eu\xce\xf3;C\xdc\xf6\n\x86z\x15\xd7\x90T\xd9\x1fj\xa8W\xa4$;\x9e\x0c\xb7\xd9\x1dFU\xa4 \x95\x18C\xd8\xff\n\x00\xee\x11\xdf\xaf\x05^'>\xb5\xd9c\xfc\xafN\x14\x19''!\x11eN\xb7M]\xb6\x154S\xcd\xac\x95m\xfb\x070\xbe\x81\x06\x8d\xd9\xfe\xe9x\xbb\xda\xdc(\x03~\x890\x0e \xee\xfdkB\xa5\xaa\xe5k\x1c\x07\xaa\xd2h\x0c\xee90\x90\x8d\x97\x18\xa0\xe6p/\xd4\x0bBH\xe1\x04\x15h\xa8\x1c\x93'\x05\x95k\x9eW\xb8\x1f-\x01\xd8\xbf\x00\x1c\xcf7eI\xb2\xad\xa0\xe2\x08\x11!w\xe8\xb4u\xfc\x15\x1f\x04\x7f\xfa\x95tG\xfd\xfeG\xccu\x14\xf5\x89\xf4\x92\xbb\x95\xb6\x9b\x00\xe6\xd7\xb0\xfbU\xe8q\x17\xf4#\x00b\x83\x87:\x97\x99\xda\xc7W\x99\x05')o\x17\x1fn\x8aQ:\x80\x11\x1b[\xd8<|\xa5\x8d\xf8cr1b\xe0\x8e\x83F\xf07a+\xee~\xe0\xe7K\xf25t\x8f\x0d\xcb\x8a\xc9\xf1\xdb\xdc\xeaW\x80\xbf\x12\x14\xe3+\xcc\x86m\x82&\xfc \x9d\xd4\x90\xb8\xb4\xf54\xaa\xadf\xe1\xbe\x07z\x13\xa9\xe8D\xbe\xce\xd9\xc4\x83\x8f\x8c\x99\xc8\x98Y\xf44\xe8\xc6\xc3\x08\xfe\x04>;\xd1\xbf\xc6,gi\x9e\x8d\xa2X\x8e\x93\xfc\xcb\xe9\xdb7<@\x1feMsE6\xfd\x1a\xe7\xab\x88\x8d5b&\xb6\x89H\x97lb\x9f4-\x84 \xce-\x81W\x93\xcc\x97k.\xda\xac( a\xfbH\x14\xd09\xfe\xedW\xc6\x99sM\x19\xc0\xba\xb9\xcf\xb5\x19\xc9\xa0R\xcf\xc9\x11_D\x8ck:h\xf1\xec\x0e\xc2\x06\xed+\x97\xda\xa8\xdc1\xb8v\xb7\x88}i\x8a\xb0\xa6+}\xe9\xe4\xeb\xf6f\x87\x85\x88\x96\xed6\n5\xb6+\x9ekN_\x89\x00b\xf8\x1d\xfba\xfd\xce=\xca\x04\x1b\x8d\xaa\x8a\xf5\x13\x11\x0eI\xa0I\xa3\x9a\x0dB\xf5\x9e\x99\x07\xb3M\xbed\x131]0\xbbV@\x9a\x8c\x11C\xd5\xdfx\xd3\x16\xb6\x1f\xb2\x0c\x1e~\xef\x19Rl\xca8k\xea\xff \xf6\xf7\xb4\xd7\xe5\xd6\x98\xbc\xa2\xb0\xf5\xcb\\\x17O,\x9cT\x99r?P\x99\xf4\xc3\xf7\xfeF\xfepE\xa0$\xf1lE\xe6\x10\xc3*.\xe7\x90&\xeb\xa4\x86|A\xc7\xcbMT\xa0\xdcd\x95g\xa3V\x0eD\xa2DW\xb9>\x87.5\x93zK\x03\x97}&\x92\x08i\x9b\x19oy\x00\xe3\xac\x0f\xc0\x01\x00\x00\xd0_\xfe8M\xfd\xcd\x97\x8e\x0fi\xa0\x88\x97\x13\x82\x0cmfm\xe56p\xcdN\xd0-\xdb\x91\xb4/\xd8\xa9\xbc\xc3Q\x03\xcd:Xv\x04\xa5}\x89\xc4\xb9\x9aE\x1a]\x85o \xab'J\x8e\x0dtu-p\x1f\x1cla\xc7]\xa6\x95\xaa\xd9\x97\x0bPD\x11\x87\xc7P&_]\x89\x99\xf1\xfe\xa8o6\x8e\xd1\xa3\xd4\xe2\x0e\x06Qdh\xb2\x8a\x99 w\\\x08J\xbf\x0e\xd9\xaa\xfe\x98\\\xf8A\x10<\x85\x1d\x9fB\xc0\xaf0\xa9A\xcb\x8c\xff)\x87M\x00\xc4\xaf\xf8\xe5\x87\xf3`\xc6\xdft\x89\x12s\xcbi\n0;\xc5\x11\xe5\x16\x16I\x16\xa7\xe9X\x80\x8d\x071-; %\xd7\x85bL]Hc\xeaQ\x8dm;l\x10\xeer\x01\xb70\xde\x8c\xfa\xdc\xcd\x86\x15\x9ck\xde\xb2;p\xd2G0\xeb\xe7\x12Q\xac\xe2\xb0(\xed+Q\x8ck\xeeO-\x91A\x9d\x8cQEa'\xfe\x04\xfaY\xfeu\xe56p\xb1\xa4\x1d\xb9\xceRTj\x99K\x95cf\xd12!2%\xec\xee\x16\x97\xf8i\xd6\x1a\xd2,\xc0\xf1`\xbc\x1dxo\x90\x8d1&}\xef\xd5\xad\xeel:1J\x07%YT\x13X\x0b4\xd1\xd3sL\xa1<\x81\xe5p\xad&\x05\xd7\x04n,Ue\x04\x9c \\\x88\xaa\xfd\xa9\xb4O 5\x0c\xf9u;By\x93ay\\<\xf8\xc3\x87\x03\xf1\xe0\x87?=x\xfc\xdd\xb6\x9f>\xde:\xa5\xe4\xc1\xf6\x91\xef\xf7\xf7\xb6\xfdt\xff\xbb\xed\x13\x04\xec\x7fIF\xca\xd6+\xa9\x94\xf9\x8d\xe2\xed\xeb\x07\x93\x1b\x95\x98,2LT\x93\x8aY5\xe9\x07\x80\xb5jq\x80Q\x99\xecm\xebV\x9d\xe5Z\x8a\xa1$i\\'W\x04~z\xffc\x08\xd7I\xbd\xca75\xac\xe2\xab$[B\x0c\"\x13E\x84Y\xbe'\xf0\x07\x19\xf4\xf4\x0f\xf2\x1d\x7fZ\xe3S].Bh\xa0\xf8\xa9'\x97\xd6Z\xf5w\x9f2\x89ep\x82^b\x84\x9e \x9f\x0c \xcf\xf3M:\x87,\xaf%DJ\xb2 %\xc9f\x04.\xc8,\xa6X\x93/&\x80\xb3\x16\xb92\x11\xc3:c6\x0d$\x1e\xc4)\x1f!\xe9\x05h\xa3P\xfb\xde\xef=\xb7V7\xc6\xe9 \x9b\xbfwS\xa2\x89o\x8b\xda\x084\xe09\xd5\x98\x9eeA0\xc0\xb1 \xab\x80\x14\x99\x90\xe1U\xa6\x0c\xc2E\xc3 ,{\x8b>\xec\xbfr~\xce\x15\xabz\x1eA\x97\x91\xc6\xca\x10\xf3\x91\xa9C\xe1v\x81\xee\xb8W\xf9\xa4+\xce\xda\xfaKM\xf8\xed\xb6\xd0\x95\xbe\x03!B\xeaWY\x88\xcep\x0c\xbae\xae\x038\x86\x1a&\xd0_\x96:\x80 \xf8\xb4U8\x82W,G\xf8_N\xdf\xbe\xe9\xcf\xdb\xc8O\xf2\xcey\x1b\xb5>U`\x88\xef\xdd@\x90Zq}\xa6\xbd\x85f\x9a7.\x17\x7f\x0f\xfbR5V\xf7\xeb\n\xdc>\xed\xde\xd1\xe91\x1d\xcd\x18\x9b\xac\xe4e\x87\xca\xf6\x89J\x91'YMJNG\xe8\x9e\x87yN*\xacC>%U\x0dI\x06\xf3|\x86\xa1\xa9\xb5\xf9Th\x91\xadh\xce\x14\xcd(\xf9t\xbb\xc9\x16\xf5P\x9e\xe9\x11\xad\x95\xfe\xb21\xf9 \xea\x8c?\xdc\x14\x84\xeb\xfbN>\x15dV\xa3\xaa\x8f}\x14\xc2\x12\xadi\xe9\xbcU\x90\xd1\xc3\xd3\xdbd,\xaf\xcc\xdc\x03\x96|\xe0\xaau\xa3c\x9e\x92\xf7\x80Y(\x92\xe9\xde\x99\xbc!!Q\xb5\xb9\xa8\xea\x12s\xc1\x80\xe7\xc9~\xa6g0\xc1\x0cXHb\x1fx\x01\xd3\x86\xb9a\xdfb\x90~\xeb@\xc3\xd9\x82\x13\x89J\x9b\x8cT\xb3\xb8 >\x91\xc9\x9f\x1e\xfc\xd7\xfe\x83e\x88\xb9\x9d\x94g{\xf8\xec\xbf\xbazP\xd3\xd0\x8a\xc1\xa15\xfdkzg\x1d\xed\xa9\xbd\x7f|\xc0\x1e\xee\xbbv?\x1fdP~\xf6\xeb\xc6\xa4wG\xa3\x95\x11\x9b\x97D\xb3U\\>\xab\xfdZ\xda\x0b\xe9\xe9\n\xcb^\x86\xa6C\xf7u\x1e\xfe\xbc/\x8e_j\xdac\x8a!;\x98\xb9^ \x0e\xfb\xf1{\xfe\x03k\xd0_;t3;M~%\xf8\xcc\x10\xb4:1q\x0d\xf5\x01\xef\xc5K\xcdpsL\xf5\x95\xf3\xc0\x15\x1f\xf0\xda\xb9\x0cA\x1b2Sh\xd2\xec\xa7\x0e\xf4\x01\xc1)\xe01\xdd\x12\x13\x84\x00\xb22q\xe1\x17A\x93@Z\xdb\xda\xad\x9f\x19V#\x86#\xf0\xf1\xee\xc2\xfb\xbe*\xc8l\x1d\x17\xf7);\xf8'/\xa0\xd4\xed\xf7\xd8\x89\x9ep\xd6p\x84\xce\xfc\x1d\xdb\x81\xe9Y\x80i\xcf^\xe43\x0cZ\xea'\x98\xca\xd0\x86B\x1b8\x02\xcf3Q\xffq\x19\xadi[\x1b:|\x84Q\x81\xb7\xaa\xf9t\x83$\x86\xfe\xef\xda\x9c\xd2$n\x92\x18c\xb6\xcf\xfd\xd8h\xe8\xa1\xe3h\x86\xe7\x9eO\x13\xbc\"\xc2\xff\xb9\x93\n\xbf\x7f\x89\xbb\xfbW\xfdu\xe7 \xbd\xdaC\xa3Kr5\x94\x93k=\x94Xk9\x98\xb0K\xa6\x82\xd2~{1\x94X\xeb\x9c%\xba\xd5e\xb3\xbd\x16}jSH\x9d\x88>\xb5\xcd~\x1aL\xf2{:\x94\x13\xeb\xb9\x18\xae\x16J\x97B&\xef\xbfz\xc6\xd3\xea\xbf'\xcb\x93O\x85\xef\xfd\xdd\x9f\xc6\xf7\xffy\xb6;y\xf0\xe0\xf3\x83\x07\x81\x17\x82\x97x\x9a\xef\xder}\xf5\xf3\xe6\x8c\xf5(k\xf7\x9e,\xf0\xf0\xf6\xec2\xb4(x\x03&2M\xe2\xc7,_\x7f\x87\xebGk\x00\xe0\x17\x9c:\x04\xef\x0f\xf2\x1d#\x87\xbd\xe7\x1f\xf8\xa4\x07\x94?\xaf\x8d\x8a(f\xcd\xf1MI\x16\x06K\x0e\xa1\x91\xec\xce\xdf@\xdbE\xc1\x8b\x00\xbc\x86a\xa7\xd2^\x08\xda\x83I\x14\x94\xc8i\xad\xcb(\xa9^\x96\x84\xa47o\xe25\x99\x07~e\x0d\xeeN\xfb\xc2\xb4sJ\xf6#?\x93\x14\xd3~1\xaag\xe2\xda\xc20\x05\xd1\x04\xd6\x9b\xaa\x86\x0b\"Y8\xf0)\x9a\xdc\x7fO\x16\x81\x913U\x0bk\xc5\xe1\xfe\x98\x8f}\x02\x0e\xd9A\x16\x1b\xbc\xa3_\xd9,\xcamW\xa4\x14\x8e\x0b8B\xb1\xdc\xdek\x81\xa1\xb7\xf7\x1c\"E`\xd8\xee)\xf3\x9b\xb5en\xa3\xe5\xca\xf1\xbe\xca\xed\x02\x85\xb6\x96\xd2\xae\x0b8\x86\xdc/BH\xa9 gL.+\xca\xb8\xdb\x01\x8e, =-\xec\xb5A\x15X\xe6v\x88\xc0\x18\xd4\x01\x8e>\x0c%\xae\xdc>p\xc5!\xd0\x1f\xc8\xad\xd7V$[6\x91\xc7\xac\x9d\xdd8\"\x03\x12\x90\x95?\x0f\xe1*\x84\n\xcd\xbb\x1c\x16\x029\xa1M\x9aR\xb6\xeb\n\x8e\xc1\xbfA\x91y.\xfc\x07\x19\x9f\xe8/\x05u\xf1o\x02\xc62/9\xd1\x1dV\x93q\x99\xf6_\x06%\\)\n\x8c\xc6\x88\x80\xee\xa9%OhD\xe9(Bh\xe3_\x850\x0f\x82\x88+\xad\xe0\x18\x96\xf2\xef ,\xbb&]N[\x0ddl\xa3\x11\xbb\x0d\xb6\x00/\x8c\x051l\x01f\x18 j\xb0o@\xe0j\xa4\xa5\xc6\xc5\x98\xd3\xa9\xe9\xa9\xa2\xdeZ\xe7W\x84\n3\xb0t\xc8\xfaE\xf7\xefEK\x1b$\xa4\xe4\n\xd3\xdf\xb8-\xc77\x1c\xae\xd6\xca\xb63\x0b\x84\xc6\x89\xee\xca+\x14R\xd3f\x96\x17\xa12N\x91\x1b\xd0\x9acT\x14\xb9\x94W\xd6\xea\xb7\x81\x03\xe8\xdc\xce+\x10\xc4l\x9c\xc5\xb6Z\x84\xfa@\xab\x005\x15iST\xc4\xf5**\xc9|3#\xfe\xd6C\x00\xf52\x96ytNk\xbc:\x9d\xd6nA\xa2h\xc1\x8c\xfd\xee\xfb\x08F$\xa55\x15>hU7\xcc\x9d\xe4\xb9\xb2$S\xb5'\x7f:\x82=\xd4U\xec\x85\xcdmn\xe0\xd7AG\x1cv\xf2\xa4\xd3\x15q\xb1\xe3\xd7\xd3\xcc\xe1\xb2\xbf[\x86\xe2\xf2\xe8\xca\xad_\x8f1\xb7\xb9\xf5K\xe1\xa5q\xd1\x88\xe4\x17\xd6o\xed7\x12\xdd\"p\xc9\xc6\xb5\x81\x95\x011\xbf5\\\xf8\xf7\x9ejd\xb0W\\\x80T$\xbc\xd7&23\xcfg\xcf\xe3\xd9\x8aL\xe0\x9d\x1e\xb5\xe3\x8b*O75I\x167\x13\xc8\xf5uf)\x89K\xde\x8c\x9b\xd2\x85\xf33;\\\xf1;')\xa9 \xbb\x8a\x98t\xf1\xf7\xdd6\x91-\x94\x16\xcd 6\xa8x\xf4\x93TE\xf0 \xbc\xd5W\xba.\xe3\x82\xd7H\xf45\x96\xa4F2n0\xbfG\xdd\xf7\x04b\xfd[\xf2\xa9.\xe3Y\xfd\xb2\xcc\xd7\xd8\xc8F_M\xde\x06\xb9.\x87r\x19x\xce\xee\x920\x81\xec0\x88W$\x9e\xa3\xa1\x87}\xd3<\x9b\xcdHQO\xc0\x8b\x8b\"Mfh\x8f\xf3\xe0\xe7*\xcfBP\x9f\xdc\xc4\xeb\xd4\x1b\xde/\xc3\xf47\xcd\xe3\xf9)\xdaF\xef\x98\xe3\xaf\xdd:\xdf\x0c\x8a\"\xe8^\x84G\xf6\x80\x91\xce\xb6-_K\x02_\xc5\x0b\xf2c\x1e\xcf\x07=\xb4F\xe1-\xc7\x19#\x0fH\x97\xe1\x1dcF?\xe4\xe8\xa42\x81\x99\xbe\xaa\xb8\x1f\xf9\x8b\xfa\xc9%\xc9&\xb0\xe8\xd3\xa5\xa0k\xb9\xc3\xa7\x08G\xf0\xaa\xaf\x8a\xfc\xd9\xaa4\x17*V\xa2^\x0f\x10\xf5z\xa0cp\xd0\xeeD5J\xa9{\xe6FcMZ\x1enm\x0ds\xf0\xed\xf6\x9f>\xfa\x02C\x1a\xf5\xcd\xaf\xa0Z.\xad\xeb \xdb\x1a\xec\xc0\xb0\xd1\x0e\xe8\x8fI\x93\xc29\x17\n\\3\xba\xf6\x87\xc1\x14\x95h\x12\xa7Q!\x99\xb5\x94 ^1\xe8\xa7\x85lv\x1c\xadI\x1dS\xa4\xe6\x7f\xb24\\6\xe5\xe6f\x1b\xe5f\xdeUnn\xacZ\nf\xd0\xd4Isk\xfb\x08T\x0dl\xfb\x16\x1a!\xd8\xe813\x88i\x9b&\xc3$\xb5\x08;\x8fH\x88\xabL\xb1m\x89\x003\xf8Vhn],\xdag\x98\xee\x04\xb7\xc3\xf0X7[\xf0.\x80\x1d`B,8\x82Y\xcf\xfe\xa2[\xa8x\xcd\xf8\x1d\xfc\xc0\xdfca\xd89\xfb\xf4\xcbm\x08\xb3 \x88\x10\xd6n:\xd7i\"\xe5\xe8M\x08\xbf\xdc\x062c6\xe9\xf8\xa78\nb\x887I;\xc4\x97\xfd+\xe0_624\xe5\xb8\xed\xb8A\x0b.\xa4\xa3\x8b\x81\xa0W]\x13\x89\x94`\xfeqH2h#*\x8b\xbdT\xb9\xe0)(\xe6\x1d\x1d\\\xb5\x9bU;\x9b\x18'\xd1\x9a\x94K\xf2\x82\x90\x82\xae\x98E`\xba\xb5\xc5n\xe2\xad.\x98\xac\xdci|\x16\x04!\xcc\x18]\xa2\x84J\xd6\xe2\xba\x9b\xa9D\x96M\x08\x1eV\xf3\x02\xfaM\x9fG\x10\xc5Y\xd6i=\xc1XTc\x0eu\xeb\x19\xd9z%e\xf7\xdf\xc8\xd8T\xfd\xf5+\x1c\xd8\xf9\xd0\xadl\xd2\\\x90\x8e?&\x1b\x9b\xf0Qgei9+{\xd9\xd6q\x1d\xec^\x82\xe2\xbc\xec8\xa6O\xcf\xec\xea\x9d\xfe\x1d\xa2E\x1c\xe9wC\xa9q\xd2\xb1]+\xa3\xaa \xb3\x10\xaa\xa1})e\x90\xfey\xe2@\x84\xdd\xb4}\x9bi}\xa6,h\x19\xc9\xa5{\x1d\xcf\xca\xdcO\xed\xa4e\x94.E\xe0]\xe3\x87j\x0bR\x03\x0d$\xf2\x0e9\x1dv\xec\x18P\xb4\x04\xea\x8a\x88s/\x0bac\x10\xb3\xb4O%!\xd64d5\\\xfdoJ\xf6oB\xc9\x9a\xa4\xcd\xa3(\x99i/\xd0\xd1\xc6z\x1aa\xda\x08\xd2\xb1qC\xd9\x122d\x06NK<\xdd\xb4w\xf4:\x9f\x93T\xc0\x9d\xedjZ\xc7\x80\xeaN\xbbY\xe5\xed\xed\xbbx\x14\xe3>~\xaf\xc5\xff\x8f\xef5\xfd`\xcc.*\xd2T@\xdf\xf3l\x95\xa4\xf3\x92d\x13]\x8cq\x16e\xb0v3BM\x86l\x95\xe4\xe1&b\"\xca`\x0b$*\xca\xbc\xce\xff\xca\x9fgp\x8c\xbbe\xd3\xde-\x99R\xab\x89P\x8a\xc6\xc4W\xec\x99\xbf\xa7\x04\x8c\x08|\x12\x89\x99i\x94\xcb\xc6\xd3T\xb5\x84e_Ok\xc3\xa5V\xab\n\x1cAB\x913\x13\xa3\xd1\xba\x19t=\xf9~u\xc2\x19\x0fY\xfcm\xf8\xcbC\xdd\xcbJ\x98\xd7i-\xe8RA\x90\xb5\x0d\xcfTM\x91 \xf2\xae\x17i\x9d\xb4\xf6\xcc\xb0M\x86o-\xf3\x9cR\xc1\xdc7\x9a\xba\x81\x8d\xe8t\x1c\xc9I\x08S\xf3hd\\\xac\x11\x81\x89\\\xb8\xb9\xabnP\xf5\xb8$\x19\xc6\xc2\xda\xb1\xa5\x1bB\x1b\x13[\xfb\xa0\x08\xc5dJ\xd4t\x03v\xd5\x08p\xa3\xe3L\xee\x00;K\x17O\xcb38\x86\xc4\xa7\x7f\x0821a\x8fq\xbd\xe8\x83\xc1V\xb8\xe7u\xe2\xcb\x85f\xcdl\xd2t@\x91\xae_\x7f{\xc0\xa9;\x8e;G\x17\xc5\x97\xb1;\xa7g\x81\xd6\x19FL\xccE\xed$\xd9\x04\x19\x15\x92\x81$S\xd3,*\x7fS\x9ei\xef)\xe4\xf0}c\x87~\xef\x1e\xf8\x0c\x03\xf2\xb3\x10|D\xb8\x86lN\xcb\xb3\xe0)\xe4\xbb\xbb\x01\x0b\x911--\xd7\xfbb\x1a\x18\xe0E\xa1\xd7_eu\xd8\x8e\x18\xb3F\x0e\xdb\xaeu\x03A\x945\x82cfi4Q\x9f\x1e\x888\xc9Hu\xd0\xafE\x11\x1cu6\x0dN\xfb\x12Ui\x8dA\xa8\x05\x0f@\xdd\xc9#6\xa4\x98j9\xcd\xd0\xa8\x9eE\x8e-Y\xfe\x85\x1c\xad\xd4\xd0\xe8?\x04\xfalxg*\xc4w\xf4V4\xfa\xb7\x9b\x99\xf7\xd9X\x06o\xf8\xd6\xe5p\xc0\xf1\xf9\xdf\x8b5T\x7f\xfd\n\xdc\x84\x10\xc3\x1e\x0e\x89aZnB\xf0!\xfbZ\x8b{\xc1\x88\xeck\xe5;\xc9\x89<2q\"\x99\xff\xed\x00\xf6\x0cr\"W<\x03Y\x87\x99\x94\xa2\x1bKs\xab\xf2*\x03\x9b\x1a\xb7%f\x0b\x9e\x85\xb0\x08\xa1\x08a\x1e\xc2\nMF\xd7h\xbdv\x03G\x10\x97Kt5T2m\x1d\xa0uYc@!\xabL\x0f\xe8!\xda\xfaI\xf9v\xfdn\x97Z\x141\xf6\xeb\xd29\xf2\x14\x9e.O\x9f\x06P]'L>\x14\xd9, \x86\xce\xb1\xd11LW\xe8\x90\xd5S(\xce\xe1\x08nx\\\x99\x93\xacNJ\xf2\xa1$\x84\xa5\x18\xbe\x11\x86\xf5,\xb50\xad\xf6\x8f\x0d\xa9\xeaWYM\xca\x19)\xea\xbcd\xc9\x86\xe9\x9b\xaa\xc8\xb3\x8a\xb4^\x15\xf8\xaa\xad\xe7b\xd9Jo4\xb22\xcbGl'\xd2\x80\xa10\xea\xd5\x8b\xa4\x9a\x95\xc9:\xc9X~\xbe\xcc\x8d{\x92\xa6~\x06+\x90n\xe9O\xd9x\x83\xdf-\x1a\x98L`\xe1\xf6m\x1bh\x13(\xdc>\xebCu\x02s\xeb\x97\xb7!\xda\xce3\xf6[\xa6\xbe9\xbd\x8e\x97KR\x06\x0e!\xf3\xa0 {h\xadKe\xb15\x86\xf2d\x8aY\"\xb2\xac~\x1bv%\x8cN\xea\x0d*\x8c\xael\x863\xa2\xb0\xe1\xac\xdd\xc0\xd6\xcf\x80\xe1\x1a\xad\xab\xbaL\n\x11\x85\x14\xedl\x06\xadcD\xb1^\x12\xe1&\xfe\xd6y\x13/\x99\xe3/\xc9\xea\x10vJJ\xc2\xda\n|\xe6\xdb\x99\xa9\xcc\xe7\x12\xc1\xcfW]\x91\xf8\x97|Y2\xf4\xd6C\x16\x9f\xaeQ|Qn\x8a\xda\xf7X\x87^\x08K\x97\x19X2\xad\x8e\xc9\xac*\xb5\x18\x96L\xaaF\xc6\x960VI\xebb\xd8\x9f\x8a\xb8\xa5\x93j\x8b\x81\xc3F\x0e\x0d\x93\xb0p\xb9X\x9e\x14V\x9d\x99\x1f\x8ce\xaa\xfe\xbdX#\xfd`\xf2A&@s2\xef\x19O\xe6\xbd\xf6\xc9\xbcg:\x99{kjSE1\x0b\xe97\xf1z\xc0+\x809d\xaf1\n\xbb\xb9\x16\xc6\xe2\x8d(Yf\xe1\xb2\x0c\xb9\x9a\x9dG\x08|\x94\x89\x1eV\xfbFX\xed\xb7a\xb5?\xc4\xc5\x80\x8a\xdb\xe4\x13\x99mj\x16rZa\xcf\x86\x891#\xc2\x04I\x8ay\xc7\x86]\x1aDB\xf0\xfa\xe7\xae\x87O{G*}\xbc\xa9H\xf9\x92\xd4\xb3\x95g\x8d\xc1&V\xd4\xca0\xb0%\x9d@9\\M\x0d\xcaeI)\xac,\xffP\xa8\xb4\xdb\x10\x12\x831\xb7\xf5\xd6\xde\xac\x1f6\xed\xb6\x9a\x1d\x1d\x94\xe6k\xbb\xe4*\xd9\x0b\xfd\xdbF\xcd\xc1\x03\n\x1c\x03\x95\xd4\x0d\xa0\xcd\xb1-\xbe\xcc\x1f\xe2\xa5\xbeV\xd2n3\x87c\xf0\xf87\x1e\x18\xcd\xa4c\x96\xec\xe7\xe0m\x03\xe4\xe7\xf9\xba\x88\xeb\xe4\"I\x93\xfa\xe6u>7\xec\xe2\x8d\xc1\xdb\x96\x96\x05\xbe3\x92\x12\xc6\xaf\x90x\xb6\x92\xdd\x06\xf4\xa8\xb0s\xfa\x8d\xb6\xdbNb\x18\xd8l$&\xc5Z\x12\xc7\xf4[\xdaO\xa3:^Vp\x0c3\xfeg\x00\x13\x98&gc\xcd\xc0[\xce\xb4G\xaa3\xad]\xbb\x8a1\x1cX`\x1c\xfc\x8f\xddF\x0c~\x06\\\x97\xcd\x00\x9e\x17\xaf\xe6\x81\x9f\xe2\xfd_n\xdb\xf0\xa2\x0c\xa3\xc6\x04bk+:W\xedn)PDv\x1b\x11\xe7\x98\xed\x8d\xc2\x18\xba%\x8a\xa0_\x86\xfd\xd2-\x12q\x9c\xfd\xd9Z\xe4\xccL\xdeE\xb1\xf9wQ\x8c\xdaLgg\x01\xd0\x7fwwCH\xa6\x9e\x07\xbb0\x83]|D\xf1\xa5\x18n\x83\xa9\xa9\x9b\xb0D\xf4\xecK\xb0M\xfb\x8aP\xcc\xa4\xa2)\xed\x8a\xa2\xa4C\x04a\xacz\x04s\x16\x8a|\xfcp\x81wK\xe5^:L{m\xeeyA+\xb7:\x9c\xd3\xde\xcc\x89\x9bAQ\xe2\xb31\x17\xc6\xba\x06\x06Z\x7f\xa9\xd66;\xfb\xcaj\xb0\x10\xea\xa8\"\xe9\xc2\xe0'\xac\xde\xb2\x1d\xf6-\x10\xd6\xf1%9aL\x0c\x1cQ\xb2\xc1\x1e=+\x92\xeaC\xbc\x94\xb4\xa1\x92\x7f5\x95\x9d\xf4Vw\xc0\xb2\xea\xf7\x1dj\xce\xd4\xe1\x1b\x9d\xf63^\xb3hMh\x80\x1a\xd9h\xe2v\x07*t8?s\xad\xd9\x85Ic`\xa2\xb5\xa5\xe1@\x96w29$\x99\xe9>KVJh\xa5r\x9a\x9f\x0d*\x9c$\x81\xab\xb47\xf4\xc0x\xb5l\x9a\x9f\x05\xd8Xs\xf8V,,\x8d\xb9i\xceMO\xf0\xebi\xa2W\xf2\x9b\xf9\x0e}\xc3q\x91T\xba`\x81=\x1b\x0d=\xe6\xffK\"\xfaV \xf8\x8f\xd9\x03nK\xd9\x9e*=K\xfa\x84Q(\xf6\xbf\xd5\x9a T\\u\xdf\x7f\x93\xda\xb0\x02\x9a%\xd1\xbalj\xd6z6\xc6}\xa5g\x89\xca\xb4\x12:\xd7CMW\x0b\x16.\x8d\x1d\x1a\xfa~\xba\xf03:\x17*\x88\xa9\x13\xdf\x9a\xa5\x19w\x07\xf6\xe4` \xce\xf1\x7f\x86\xa6\xe7\x0b\x85O\x85\xd14\x1f\n>\x89*2\xdb\x94I\x9d\x90*\x04\"\xee*0JPV\x7f\xb8)\x08{\xca\x14\x08\xcac\xc3I\xc3\xa4\xaej\xb6\"&\xd9\x8c\x89\x9c\x9a;\x11m\xed\x8a\xd7\xee\xdf\x93h\xab\xcf\x98\xdc\xcd\"\x19\xfcT\x1ax\xf2\x05\xd6\x92\xea\x0f}\xa5\x82\x81\x87\x0f\xf4\x87|~\x13\xa2\xb6\xb8\xbc\"\xa5a\xf2s\xaeP\xa6U\xfe\x1a\x97I|\x91\x12\x83S\xed\n\xab\xae\xea\xdapE\xb1\xe4R\xaeP\x93\xe8k\xdd\xb4k\xfd\xb0I\xd2\xb9\xb1\xb2\x08\xe2\xf5)J\xaa\xb7\xcfN\x0f\x03\xbf\xd6\x1c\x147\xe8\xaeO\x1b~\x0b\xc7p.\xef!\x95\x88\xe8\x86 \x83\xef\x8c\xc4bS\xa6\x13cd\xa3YI\xe6$\xab\x938\xad&\x80Z\xf6Ut\x9d\xd4\xab\xe7\xcds8\x06/\xc9f\xe9fN0\x0ca\x15\xaf\xc9}\x16C\xcc\xd0h\xe3\x08l85gy~\x89q\xdeuF\x84\xfd\xf9\xc5\xa8\xfd\x7f\xa7A[z\xb4\x07!T\xb2B\x0fS\xe1\x08*\xca\xf4\xf3\x1a\x12\xed(=7\x80\xf2\x83\\\xaa%\xa9%\x91}\x1f_\x07CQew>\xa8\x91U\x9f\xfb^\xc3\xa4P\x89'\xc3\xd0\xb1Y^\xc3\"\xdfds\x9d\xab\x10\xed\xfb5F\x9e\x94\xd4C\x0f\xbeWmm\xd3k8\x86_na\x02\xaf\xf5\xd5\x7f\xc66\x87t1o\xb0\x86\x10\xd7\xf5\xf3{\x17m\xca\x14v\x8f\x8c\xa6\xa1\x83\xaa\x01F\x93\xcc\x01\x03$\xcd0\xdeT\xb2\x8dm\xbcU\xec\xec{c\x18\x9dF'\xf1\xc6pdr\x1d\xc4\xcf}\xcc\x0cB\xd8\xc9\xa4\xa5\x8d\x88(\x10ql\x0e\xe1]\x1fr\x12joBx\xc7\xd7\x80\xa2\x17J\xc1?\x07Q\x9d\xffT\x14\xa4|\x1eW\xc4\xc7\xa08G\xb0d\xca%=~\xbc\x97*\xfej\xfa\xe6\xccT\xb3\xe4\xd8\xce7b\x14\xa3\xbb=e\xa7\x0ch\xf7\x02\x8e\xe0\x99\xe2\xa9u\xea\xbfR\xc8_\x104\xcf\xdf\xb7\x9ek\x9a{1B+'4\x8a7S\x12%\xd9\x80-ai\x89\xb3\x85\xaa\xbd\x8b|~\xe3\xc9\x18\xb2\x8ca@\xbc\x8b\xd5\xbf\xa3\xc6h_Z\xb4-;\x11\xb5\xd0:\x8a}\x94\xc5k\xfck9e\x7f\x9fQn\xce\xf0>\xc1M\x1e\xb10\xadX\x19&p\xe9\xb3\xbfCx\x11tn;D\xc2\x96\xeb\xb8\xcc|\xef\x9d\x80+\x8f\xd4\xcf\x9a\xc6p\xfdI\x05\xf1\xfa\"Yn\xf2M%\x83\xdb\xd7+\x02<\n3\xee=X\xc5\x15\xac\xf3\x92\xbe\x893\xc83\xd2(\xfa1;\x00~\x91!\xee\xf7z\x88\xb39\xbe.\xe2\xaa\"\xf3\xfbI\xa6|\x8b\xba\x8d\n\xe6 \x8b#\xc6\xfa\x848\x83?$\xd9\x1f\xd8\xdb\xc8\x0bB\x11\\\xebh8\xf6bG\xd5%u\xeb\x8a8\x86\x91\xb9\x1bsCy\xf2\x85\xbd\n\x8cCHJ2\xa7\xbfvH\x84\xb7\xe2'\xeb\xa2\xbe\xf9+3\xf9nH2\xf7\xe2|/>h&\xd8\x06\x06\x856\x9dgQ\xe6W\xc9\x9chI\xb5:\x99\xb7]L\xf3\x98;\xa8@E\x8ev\xf5M\x81\x88\xa2\xd1@\x976\xaf\x0d\xe0[@I\xa3:\x90.\xdf\xcdK\x03d\xa02\x058M\xb48\xec\x85;\xb6vqA\x84\x97\x8c+\x1c\x91!\x041\x18\x15s\x80l\xf2\xbd{\x90Y\xb4\xce%\xf9\x871\x0e\x8d(rl\xd6@h\"3\xc1p-E\xa9\xfcj\xb8\xa6\xcdz\xc4\xd9\x9c\\\xa7f\xa6\xa4\xf1\xc7\xbe\xa9\xc3/\xcc*@\x0f6u\xe8N\x9d\xa0\x9d\xf1;\xcem\xd2\x9e\xae\x9b\x9e~\x0c\xe1]\xc0\x83\xef\x9ct\x1e\x07\xe2\xcc\xc3M\xda\xb6\x80\x97\xe7a`\xf1\xbd\xa43\xfc\xa9\x9f\x8aM\xf9~l\x98/q\x9c\xc8&\x8c\xde\x18\xa0J\x96\xbb\xe0cP\xfb{\xc8\xdeb\x18\xec&goE\xca\x04M\x8b\x06l\xceoC\xfa\x99\xbe\xa7\xe6\x10~\x8ec\x82#\xf8\xa9\xbf6\xfd\x13\x9c\x0d\xee\x9d\n\xe8>\xc3\xc1\x02#\xa17\xf6\xab\xec\x7foHy\xf3\xb6|\x99\x97\xeb\xc0\x7f\x17\x84\xf0\xeew\xed>Z?m\xf7\xac\xcama#\xb20\xb9\x97\x9e\x80ng\xbbMV\x06)/\xdbo\x14K\xa7\x1b\xc5\\\x11\x02\xcd\xb5\x12'A\x15\xa4\xbc\xec$TB+\x99!\x12\xffXp\xe6\x03\x86{\x15\xdf\x02J\x92\xb6:\x84\xa9\x87<\x9e\x87\xf7\x85~\xc9\x82\xd3Rv\xf1\xc7\xfc\xbaa\x17=6\xb0\xca;\x0bD\x9c\xb7\x81f\x1cj75\xcc\x03N1n\xbb\xf9\xfd\x8c\xc7\xd94sj9\xc5fDi\x97,\xae\x14\x91\n*\xc6\x8dL\x85*\xcd@6\xa59*\xdb\xd0\x0d_!c\xe9\xe5\x01\xfc \xee#\xcf\xe6\xa7\xec&\x86\xce\xb2\x9a\xaaUL>\x93;io\xba\xb2\xa1j\xbawF\xc7'\xda\xdb;\x0b(1\x14\x8dz\xbfxM\xcfn3o9zL\xcf\x98\x87\xc7\x83_\xfc\xe9\xdfo\xcfv\x83\xdb\x07K\xd5\xcf\xe3)\x0bs\x81\x862> \x9e\x06T\xb6\xd8T+\xbf\x9c\xee\x9f\xd9}6\x0d*`?\xdd\xe6f~\x16]\x89\xfd\x85\xbcq\xf3sJ\xac\x97\xa1b\xc2\xed\xaf\x86\x8fo\xe0\xc4g\xc3\xef\xf3\xa5\x0d\x9b\xfd\xb3\xb2\x13\xc9\xfd\x17\x99\x1c\xe6\xd6\x0b\xc1[\xda\x02\x81\xd0\xa5O\xa5\x97j9\xe8\xccd\xba\xdb\xd4\xf7\xd0\xb5\xc6\xb2m\xac;\xb9\x1c\xb1\x85\xcd\xae\xef\xc2\xe2\xcb\xd6 ]\xca\x95<\xb6\x19\x93l\x8b\xdfPj\xbe\xa9-\xdf\xd0\x13\xe6\x9d\xcf\x1dLgy\x8a\xb4\xf4\x9d_\xb6\x1f\xd8F\x9b\xe0\xbe[\xe5\x15z\x1e\x96\xf8\xd7\xf0\x17\xcc\x85\x8e\x92s\x14T\x1c\xfap\xc9\xac\xcb\xf1E\x84O\xf3\xe97H\x9e\x138\x86\x9cb\xf4\xe4\x01\xe6\xd4\xf0\x13\xd8\x85\x18\x9d\xf0\x82\xe9F\xf5\x00\x84c\xd8\xb4\\\x99`b\xc8\xbaz\xeb\xa7!hr\xb2\xdf\xfa\xe8\x9bk\xa7\x15\xe3x\x8a!=8H\x8e\xc2\x85\x0b\xc8\xdb\xc7z)R\xb2XX\x8c.j\xe5\x03\xa8E\x97\xb7}oT\xf3 T\x98\xf4K\xfc`;\x0e\xfd\xad\x8cma\xf4/\x8a!1\xc3\xcd\xa4\x83\x9b\xab\xba.\x06p\x87\x19\xf4\n\xdcL\xe4_C\xf8\x96\xe27\"\xb0\xbb\xad\xf6\xcc\x82\x99]\xac\x9caz\x17>\xc9\xae\x99+\x96\xf6\x89\xf0\x1b\x17&\xc6\xf2\xbfy\xf80E\xdd\xc4n\x98e\x8di&i\xa2\xe6nU\x03\x82\x7flH\xf9\x95V\xc86{ &\xb3\x8e\xbd\x8ep|\x08\x03\xf6\x17\x87\xc0\xce>w{\xbbw\x0f\xbc\x8b'?\xbd\x7f\xf5<_\x17yF\xb2\xda\xcf4\xbe\xa7:\xcb\xea\xbc\\\xbf\x88\xeb\xf8_\x12\x00~\xc64\xc1=\x0b\x16F\xa5\xe8\xd8\x11<\xf8\x87D\x13\xfa\xcbiC\x89-a\x1ee\xa7\xe3I\x7f,\xe6o]\xb6\xab\x1ei\x1d\xfc\x05\xfe\x93\x03\x0d\xa8\xbf\xee\x9c\xc5\xe8\xcb\xf9\xf9\x90\x12P\xc4`\xd2\x8a\xc8B-\xf9\xed\xe3q\x81r\xff\x05\x08\x8e\xb9bC\xa9\xcdu\x10*QU\xdf\xa4\x03\x95P/K\xd14\x1d\xf6\xae\xe9\xabr\x86%\x18\x8c_g\x1b!8moZp\x16\x13HP?_%\xeb\x82\"\xd4\xe0\x17|J\x13\xd8\xd0ol\x990X6\xa0 \xec\xec\x1b\xab\x99$\xcb!\xfa\x9f\x0b\xd2\xaf\x0bL\xf2\x1f\xc9\x98\x99\x19\xb06K5\xcc\x88l\xfa\x91\x0e\xbcM\xc6mF=n\xdb\xa5\x04+\xd2\x99\xb6\x8b\xe2\xcd )\xde*\x86\x8d|Op\xc3\xb1\\me\xa4\xb4\x0f\nq\xca\xacY!\xdb\\$\xc5\x8c\xa9\xbc}?\xf3\x86\x0fAQ\xf8n\x19\xb5\x15E\xc1-\xe9\x98r\x95\xf7\xe3\xe8\xce\xcew\xa7\ni\xb7\x0f\xc5\xb6\xe3\x07\xf6{\x82f\xb4\xf0\xd0IP\xcd\xc6\x1dJ\xee;e\xf4\xa1\xd0\xdf\x1e\xad'\xb7}U\x0b]\xdf\xa9\xc7S(K\xe6\x8c\x12\x9e\x9a\xbf\xec\x9ad\x11\x14\xbb\xa6g\xae\xdd\x81\xeat!\xc1\xb0\xff\xa8\xe3\xe5\xac\xdf`[t\xe2\xfd\x0f\x14\xfcM\xed\xfd\x9c'\x99\xefi\x9c\x13\x95w\xd0E\xd8_]#\x9b\x0cid\xe3F#\xdb\xd5\xb9\xb2[\x90\x17I\x85\\!\x99S\xfc\x88g5;\x01\xf3P\x1f\xc3\xdeb\xb8i8_\xb5VF\xf5X/\xb0Krcc\x04\x9cTl\x16M,3\xfd\xb42D\xcc\xafk\x88\x1e\x00W\xeb\xda\xe7(\n\x87\x13\xe6\xd6\xb2Ku\xe2(\x1c\x8e\xe1h8\x8f\xa0\x7f\xe6\x88\xc2\xa2\\2\xa6\x92\xb15M\xb6\xdc\xf1{lc\xca;/7Qhrv\xc1\x81\xa4\xf1\x05I\xbb\xe3`.\xf2_e4\xd1\xe0h\xd6q]&\x9f\xbe2X\xc6&r\xe1M\xb2,2 \x1c\xd3\x83\x84\xb9\xfbQ\x06\xef)\x05U\xcdX=\x0c#2a\xaa\xce\x10\x7f\xe9\xc70\xe0\x8e\x8a``\x8a\xb4#\x9b\xa7\xbe\x90`\x13\xee\x1c\xdb\x8ccB\xfb73\x9e[\xc0\x15\x1c`\x0b\xcaBkn\x02\xc0(\xed\xb3-Q\xc43\xf2\x82\xa4\xc9:\xa9)\x93\xee4\xfd\x94O_\x99\xf8o;o\x0f\x83\x15\x18RX\x0d\xcc\xbeH\x8a\xd1\x93\x9f\xfd\xcbM\xfe3\xc6\x0eu\x9dh\xde\x0d H\xeb\xa1AE\xc7\x1d\x92\xbe}\xc2\x1c\x92\x1e\xe9\x1d\x92\x985\xf9#]~\xff\xd4i%\x05\xec&\x0f\x8e\x7f?=\xfb\xffv\xbe\xb9\xf7\x07?\xf8\xe3n\xf8\xf4\xc8\x93\xf7\x19\xdcp\xb6?\x15\x8d&~L\xa7\x0f\xfe>\x8d\xef\xffs\xef\xfe\x93\x8f\xf7\xa3\xf3\xff:\xdb\xfd\xe6A\x12\xd5\xa4\xaau,\xd7\xb6~\x01O\x0e\xf7\xb7\xb7\xd1?\xd8\xfe\xd3\xc3/0\xefo\xbd\xfa\xb7\xd4\x8a\xca\x00\xa9f\x95\xa6\xdd5\xb5\xec[ a\xcc\x9a\xc1\x84(\x96\x08\x95\x9a|(\xd8\xe6`\"\x14\xb3\xdb\xef\xa2\xef=\x8bw\xa3\x86\xcbbtR\x8c\x84\xc2\x9d\x18\xdc{\xe7\xed1\x16b\x8c\x06\xdfeLx \x80\x89F[q\xeb\xd7\xd4\x10n\xe4\n\xb3-\xdc\xbb\x07;;\x1d\xfd\xea\\D\xc8\xd2\x7f\xb8\xee\xc7\xc6\x8aC\x98z3a\xf6\xac:\xfd\xde\x9c\xb2\xf0\x00<\xb6\xcfP*)\xe5\xa6l\xd1\xbd\\]H\xe3\xb4E\xdb8\xad3\xf42P\x14\xd8W\xf4\x1f\x16\xd3\xa6s}\xd5\xc0\x0bG\xd5\xfc\x94a\x7f\x8e\xc1_il4\x06X\x13\x19\xe0&\x83$\x1bN\xde\"8\x98\xf9t(\xb6$p\xa4^O\xb3\x01{\x0f\xb4\x07\xb0\x9d\xd3R\xa1\xcb\xf3\xd6\x7f\xfel\xbb\x10\x03\x8e\xfd9zN\x0c\x9b\x9b\xb0!X\x9bCy?.\x92\xffEx4\xcc8\x00\x0f\x17\x93\xdf3\xf2\xe0\x98\xfeB8\x19\xc8\xeb\xf0$\x08\xc1c(\xd1\xab+.\xcf;\xb5\xd9\x9dp\xaf\xb6\x08\xc0\xa6\xd6\x1e\x9e\x1d\xa8>\x18\xcc/^\x8c\xde\xce\xf2\x80\x8c\x01\x1aW\xc9L\x8c\x86\x85\xccp\xfd\x1e\x14\xae \xc1@\xc1\xf6[\xcfnAuYT\xc4Uu\x9d\x97\x03a\xcatE\xc8\xb3\x8a\x7f,\x0buA\xd9\xa3\xca\x01z\xa2\xc8\xb5\x8a\x9e\xa9w\x8ep\x04\xde\x0f\x14\xfcN\xf1\xbf\xbc\xe5\x81*-R\xae>R\xa1\xe0r\xf9\xb9\x87a\xdf\xe9\x06\x8eVq\xf5\xf6:\x13'`{x\xb9-_\xb2d\xb3 \xcf)Bi\xfa\xdeS\xa8\xe1{8\xf8\xf6\xd1S\xd8\xdd\xad\x03 ,\xda&\xf3\xca\xa1t\xff{\xd8\x7fD\xb9\xb1=\xc5\xf2\xb1\xe5\x17\xd4q\x0c2\xab\xef:>:\xbeR\xb3\x8ebJ:?\xe4l\xca\xb6\xb3V\x91\x18\x8e\x00s\xce\xd5Q\x91\xc6I\xc6>\xa7\x9c\x1a\x87\xdd\xac$qM\xfcl\x93b|y\xca\x0b\x96l\xda%|/\x1d\xb8\xe8\xdc\xcb@UV\x91iy\x86\xf8\x98\xd1?\xd8\xef\xee\x92sS\xe9f\xcd1)6)\x97\xa43\xfe,\xec;\x92\xa2\xba\xb6IC\xd9\xe1\xc3\xd9\x0d\x99T\x7f \x9d\x9b\xd6\x03\x81\xd6\xed\xc6\x0e\x96\xeb\xa8\xb3\xa5E*gVDk\xfa%r\x9cS:\x1d\x83\xe8\xe5\xe7\xedE\xf8\xfc\x99\x8a(i\x9a_\xbf\x13\x18\x8c\x0fw\xcah\x16\xa7\xa9\xdfEo\xba7\x18\x11 S\x0cv\xbb\xb37b\xc3\x0fy\x809LK&\xcd\xecBLp\x87D\xbb\xfa\xbd\xa0\xcd}\xef\xdf\x8c\xcd)A'\xd0\x16\x9aS\xdc@m\xa7\xae\x95^#\xc7\xe0g}\xc1:\x0b!\xd1*\xc0\x18\x8c \xbe>\x062M\x10\x9f\x15\xad\xb6\x84\x02}\xc5k\xfc\xff\xec\xbdk\x97\x1c\xc7\x95 \xf6]\xbf\"P3KU\x0d\n\x8d\xee\x06@\x11MAt\xa3\xbb\x014\xd4\xe8n\xf6\x03 \x00a\xa0\xac\xcc\xa8\xaaDge&\xf2Q\xdd\x8d\x11\xe6\x90#\x8a\xc2\x83;\xb3\xde\x91\xa8\x91=cy\xd6$H\x00\xb3^\xdb\xeb\xb5\xd7\xf6\x8e\xf7\x1c>\xd6>Gs\xa8\x99\xbf\x80?\xb0\xfe >\x117\"2\xf3\xde\xc8\xac\x02 R\x9c\x1d\xd59\x12\x1by\xe3\x1d7\xee+\xee\xbdqFcp[\xfcSc\xeeB\x81M\xe2o(X%\xf9B\x8e\x97\xbe\x9cjS\xf7\xf8a\xda\x0e\xada4\xd6\xe1j\xd2\x1b^\xf7\xebc6ms\xc2#v\xf4\x88\x01\xe8t1bT\xde.\x01\xbe\x90\xa6\xfe \x9cDs\xd4\x18\xca\xf3\xcb\xa6\x0f\x13\xd2H\n\x88\x9d]\x0foX\x06\xc6\xd1\xc0<.$\x95F'A\xfb\x8b\x93\xaa7\xa8_\xc9\xb1X\xce.|Tf\x17f-\x946\xc0<e\xbe\x9e\x9e5_O\x7f\xc7|\x9d\x9b\x9f\x97q\xc5G\xf5\xc0\xe4\xa0\xd8\x82\x80\xb2\xb9\xf9W40\x12\xd8\x0e_\xe7gO\x96>\xcf\x9d\x9eg\xb2\xd9\xef\xb1\x97o\xb0\xa3\xe2\xcb\xfc+\xecG\xec\xe5\x13\xec%f\xea\x9c:5\x7f\xfae\xd3\xff\xa9\xef\x9c8y\xb2hb~\xfe\xa4nbn\xbe\xdc\x06\xb4\xca^b/\x9f\xb07\xddND\x0bs]\xb9\xb0/\x9f:u\xe2e)S\xcc\xcd\xce\xcb\"\x1d\xf6\xdd\xef\xb2\xb9Y\xf6#\xa6\xbe\xa0\xb5\x97; C89k\x86\xf0\n\x19\xc2\xdc<\x19C\xf3\xd0:\x0d\xac\xc2\xce\xd5\xddh\x14;ns\x14n\xf5\xcd6\x8aaQ\xefV\xdd\xc5Cd\xbdr\xa0\xe2g\x9cD\xf1\x02kE\xd5\x0c{\x96fI\xeef\x91zH\xbb\xf4\xa1\xe8\xab\x16\"4\x85b|\xdfb_VaU3/\x16C \x1bTS=\xfe\xcf\xe6g\x8f\x0f\x8a\x16\xca\xf7\xc4\xd5\xc50\x97\xb2\xad\xadsK'N\xbf\xf22J\x1f\xd3\x97i\x89\xe1m \x8a\xbd[\xe7\x96\xe6\xbes\xe2\x95ib\x8c\x88\x90\x19uY\xeb\xa8-\xf3\x04\xa5\x13jh\xcf\xd1\xcd\xc4+\xe6j'f\x1e-\xf5W\x8b\xc0a\x00f\x95\x9eo_\xf5\x0e\x02E(6P\xbe\xbdF\xb7/l\x9f\x9e\xc3a4\xbe\xfa>\x8f\xbe\x9b0W\xb5\xbd\x93n\xfdY\xe9\x04H\xef\xc8P\xbf{\x02O\xb9H\xc7\xac6/;\x9b,;\x99<\x13\x19\xf9\xf8\x1a\xe33\x03\x9e\xed\xf8#\xde\xee@\xf5\xd2\xbf\x17T\xbc\xfe\x11x\x19\xcf\xa2!Vt\xa6\xe2\xbb\xcc\xf62\x03\xe7@\xca\x9f0\xb0\x05\xf9\x97\xfcc\x9aY2\xb5\xf0A\x97\xb9\xf5t;oC\n\x97\\\x12h\xb52G,~f\xba\x02/\xf6\x0fhp\xf1\xef\xa9\xea\xfb\xd2\x80\xa0\x0b\x1e\xf1\x85\"\xa03\xe3\xe8\xd3\xd1\x01\xf3\x91\xfag\xd6\xe92\xc7\xcc\xb4\x81\x07\xa5\xb2\xe9z&#\xad\"\xe94\x13ef\xb2\xca\xbc\x083E\xbaDSm\xc9\xd0\x02`bA\xc5\x18\x14\x1c=\xda|\xe7);\xbe\x1e\xdcP,.\xb81U\x87\xba\xc8\xb4\xe9\xfeX\xad~\xa7\x7fc\xf5\xe8W4\xf1\x8d\xd4X\x96\xcaj\\\xf6\xb4\xc67M\xd2\x8c\xba\xe4s\xb5{\xde/v\x88\xc5\xd3n\x90\xdc\x9c\xfeL\x1a%Y\xbb\xd3e\xb1\xf9K\x06\xea\x95\x9e\x88\x14{\xf7=\xd8\xc3c\xc7\xeawM\x0e\x04v\x8c\xc5\xd3l\x98\xc1\x8e/\xd8\x99\x8c\xed\xbb\x1e\xdc\xe8\xb2#N\x9b_wotY&\xff?\x9c\x8c\xdbZx\xd14\xa8\x90yi\xfa\xfd\xbb\xc5\xb1\xab\xc0\xee\x96\x1c\xa6\x8c\x7fR\xde,kHu\x9c\x15Y\x17\xcfT\x1e\xce\xbaki0\xadm\xf0H\x1bH\xab\x95\xa8\x8a\xef:\xffV\xe9\xbbA\x0e\xe9\xcc\xa9;\xa9(\xfb3n\x14\xcb\xb7\xf8j\xc0\x92_I\xf1\xa8\xa0\x0c\xea!d[\x8f\xd7go<\xaf\x04\xa49%=(\xc0\x0e\xe8u\xb3\x8d}\x9e8=ka\x9f\x13/\x98\xd5\xe2Fj`H\xad\xbbK\x19o\xd8\x9e?1[1\xb4_L\xa3pS\x1cw\xfd\xa0\x9b3S\xfc\x13\xacN<^\n\xa2P>*=s\xd3\xfc\xb3*\xee\xe5\xd6%p#\xfe[G\xc8s\xa9+\xd4\x11\xa2\\&O\xa9;\xdc\xf9\x8c\xf8o\xf5@\xd9\x14\xaa\xc0*\xa9Kw\x03\xd0K\xean5\xb5\xd5\x9e.\xa7d\x02\xa2w\x0b\x17P\xd4\x1f\x8f\xab\xfcO\xc3i\xe4Mt\x97\x85\xb0q\xa6\x8cM\x8bs\x95\x93JR\xe3\xa7R ~\xd3\xd2\xcf\x91\xb9\"\xbc\xeb\x8cN|.\x1f\x98?2\xdb\xe9\xaa\x82V--a\xaf\xb1Dp\xc2\xd9.\xe3\xf2\xeeDH[l\x81\xc5\xf2\xa3\xcc\xb8\xdcR\x179\x00\xa2\xab4V\x99\x0d\xed\xe8XAE\x8b\xa5\x95\"=x\xb0{\x9e\xee7\x8a\xcd\xce\xb93\xa5\xe6\xe4\x1d\x8a:\n\x16\x9b\x9dlF\x9d\xc7\xe7jJ\x8bl\xe2T\xd6\xb7,\xa5C\xd3\xacT\xa3\x05\x8eO\xd1\x93D\xd4\x10D\x94.\xc3\x0d\x89\xad\xaa\x0c\xa1S?\x06ql\xca\x1d\xdaw@\x9a@\xe4\x11cg\x04\xf75\x88\xd81Od\x01\xb8\xc3\xb2a\x12\xed\x8b-#\xcai\xbb\xb5#\x1a0\xce\xc1\xac\xef\xf8\x01\xf7Z]\xd6\xdaY\xd9\xde\xb9\xb9\xb1\xb9\xb2\xb5\xb8\xb3\xba\xb1~\xf3\xdc\xe2\xea\xda\xcarK\xa2T\xd8e|\x82\x18\x86\x16G\xac8E\x92\xba\xcd\xad\xae]i\xc5\xab[\x88\xb7:\x0f\xecf^\xd9\xaa<\xef\xb4\xcd\xb0\x90\x18j\xeb&\xcd+h\x1e\x81g?\x8c\xe2\x1f\xca\x8bL\x9ed\x87\xccOY\x18eL\xa8\xf9Q\xbfX\xe2\x94\xa9\xa8J\xe6\x87l\xeb\xdc\xd2\xb1\x97O\xcf\xce\x8b\x05/\xd6zc\xf3\xe6\xea\xfa\xe5\xc5\xb5\xd5\xe6\xf5\xd6\xcbR%V\x95\x7fE\xca\x92\x8fT)\x8eU)m\xe6l\x03=`\x90WW2\xd0\xac\xdd:\xde\xb2\xd8>a\x17\xc8\xe7!;\xc3,\x8f\x16\x8cKv>\x0b\xb31!b\x146h\x80\x1d\xd6\x84\xe3J\xd3\xe2\xa1|\x1a\xae\x8e:\nb\xf8\xaa\xf5\xcaWl\xf9@\xda\x16\x877\x14\x95-\x11a\x08\xde.\xc7\xb3]\x1f\xdc`\xaf\xc9)\xf4\xc18\xd6\x9e\xed\xb2\xa1N\xc5z\\f\xe7\x1b\x8a\xee\xc7\xec\x18\xe4\xe2o\x8f\x98\xa1\xbc\x95\x00^\xd9\xf8aA\xb8G\x82R\x0f\x8f\x1e\xc5\xf7\xc8^\xad\x89_\xe2\xfa1@\xf4AG.\x9e\xa7\xad\xee\xd6\n\x0d\xae\x8aL\xe3\xbf\xb4\xf6\x95\xa5\xd2A\xa7\xf9H\xac\x1c\xc4\xdc\xcd\xb8\xc7\x9c\x90\xe5a\xea\x0f\x04\xba\xf7\x9c\x94\x1f\x9b\x9be\xea9d\xa6\x08\xf3\xc8\xd9\xf3\xc3\x01\xcb\x86\\6\x96\xf0>Ox\xe8r\x0f\nH\x80\xf4\xe9c<\xe0\xf2\xa8\xef\xfb\xd9P~\xbe\xc3\x93\xe8\x98h\xd6\x03\x81\xb5z\x8a6\x17w.\xdc\\][[9\xbf\xb8vsqkk\xf1\xea\xcd\xd5\xf5\xe5\x957\xd4\x99\x02\xed\x8e5\xbd\xe5W\x9d\xb2\xdc9\xb1\xa0\x7f\xfc\xc7\x83iu\x1b\xa6\x96p\xc8\xbew\x86\x8d'\xdd\xcb\xc8\x85\xae\xf2H\xf1e\xc0\xbeg6q\x021\x1fr\x19\xc6\xe1\xf7}\xbd&\xec\xd2\xee\xf6\x0e[\xdf\xd8a=\xce\x06\xd2W7a\xd9\xd0 a\xc5\xa5\xc1V\xd0'\xb5\xb8\xa9\xa0Jf\xc9\xab\x0bzyqmw\xe5\xe6\xc6\xee\xce\xcd\x8ds7\xcfn\xec\xae/oO\xbf\x96\xf2\xde \xd8\x92\xb4\xdc\xa7\xd7\xc5\xf4n\xc0\xedV\xd8e^\x97\x0d\x04\x99\xeb|\xfd<\x8b\xd5\xd1R\xfd\xb3\x08\xccE \xc3@\xb9\xc5\x1c9\xc3\x06E\xaa\x83?n\x15\xf8\xe2\xcc\xe4!\xe4\x9a\xdct\xb2a\xe1)8\x90\xa7\xbb\x113\xf0\xaa\xe5\xdf\x9cU\xab]1\xbaZ\x1e\x032Y\xc3\xa8l\x02s\x7fz\x81\xd9&\x16\x13\x07\xe1\xe6\xa5\x91\x7f\xb3\x94\xdf\xce\x05\xe5a\xa3<\xcd\xc4qq\xc2\xe2\x18l\xaf\xbc\xbe\xbb\xb2\xbe\xb4rs}c\xe7\xe6\xe2:\x10\x14\x1c\xe12-\xbb5\x9e>\xf2F\x9f\xef3\x1d\xd6\xa4\x0e\xb9\xf2\x00\xebB>Msk\x9a\xb3\xef\xb2\xf4U\x96\x1f=\xdaa\xfe\xf5\\\x86`\xcau\xba\x9e\x0bN\x05\xf7\xf7\x12R\x16\x8d\xac\xda\x8bO\x054\xbfqC\xe2 \x1bRw\x0bU\xbd\xf6\xa2^\xf4\xd3IVJ\x96rB\xa6\xba\xa9\x10&\xb5%\x1bg/\xae,\xed\xb4\x00k\xc5z\xbcJFy$\xbf\xce\xc5\x01\x9a\xb6\xdf\xafD\xa2\xab\x1f\x9eq\xbe-_\xd9\x81\x826\xe5xEa:b\x87\xa9\x86-\x0cr\x8aa)\x9f(9\x92\x82\xc4\x1d\x07\x12\xa7>\x177\x81\x8dc\xfdv\xfdX\xe5\xa9K3'Q\x1c\xbeu\xbc\xf5\xed/6\xde\xb2\x1a\xc7\xa9\x1a\xc7\xa5\x02 X\xadm\xb9\xa5\x027\xedr\x8b\xc2t\xb9\xe3\x84\xa7\xe2X\xb5U\x88\\/\xe0\x025~(F\xf5C\xe6\x84\x1e\xfb\xa1\x18\xcd\x0fK(\xd4\xa9n\xcd\xb9\xad\x8dK7\xb7V^\xdf]\xddZ\x994W#/\x98\xa9V\xd4c\xf3\xb5P+\xcd\x02\x94o\xa1\xb5Eq\xca\x99\xcb\xd2\xd3O\xdd\xf1\xbc\x1fv\xd9\x0f\xd5\xc8\xd4\"\x88\x115,\x02\xc8\x1b_\xfd*83C'\xdd\xd5\xc9n\xdaz%\xbeyK\xb1\xb4\xb8.H\xdd\xd2\xc6\xfa\xce\xe2\xea\xfa\xcd\xdd\xf5\xe5\x95s\xab\xeb\x13\x96\xc6r%Q6\xc5\xa8e\xa87cB\xa0\xb4<\xe3\x85:\xd8\x98_\x83)kxD+\xd8E 1\x1e_\xd2\x98\x94\x1d\x05\x15I\xfd\xb3y\x0f\x96\x9cP.4OdT\xb2\xa3\x16\xb7$\xe48\x99\x14f=\x9e\xfa \xf7\xa4u\xcfB\x03\xd5\xba..\x97W\xb2I\xe6\xab\xc1\xad\xb2\xe5\xc2|,\x0c\x0fM+\xed\x83W\x99\xa3\xdc\xac\xa2\xe7\x9a\xb8\x98be\xce\x8e\x9c\xa9\x10\xf33\xe6E\x1c\xf0\x91\x1f\xf8if\x99\xfd\xee\xfa\xd6\xca\xf6\xc6\xda\xe5\xc5\xb3k+\xd3\xce\x7f\n\xfaZ\x8fQ\x81\x10\x07\xdb\x16\xff}\xfdk2\xd0\xea\x1f\x18j\x81\\O\xbc\xa3\xab\xc9}.~wo\xd0c\xa3\x7fb\xaa\xd2\xeb\xbdq\xc9\xe4\x9c\x03\x99\xf9\xe2K\xec\x9a\x98\xc7\xd4\xfb&\xd9\xc3\xd4\xfb\xd6(\xd7yZ\xae\xc3;f\xf7\x8b\x93B\xd4\xf3Iq/J\xb8\xd6\xdd\x87\x1d\xd6oW\xe4\xeb\xb0\xd3\xc5\x02\xb7\xd0\x03~\xf4#\xa1\x11\xd0F\x1aL\x1e\x89L\x19\xf6\xa3\x1f\xd5\xe5\x01\xac\x84t(\xd7\xfc\xc2\xab1\x12\x82y\xd2\xe6\xd7\xa3\x1b\xd2\xb79\xd4\xc6\x9dI1\x0b\xcd\xee\x81\x926\x94\xfdn\xf1\x1a\xd7]\x81\x88\x1f\xecLm0\x99\xf9K:\xed\xca\xf7\x92\xcf\x1enF~\x98I\x0f\xfa\xc0Du\x17\xfc\xee\x0cs\xcdW\xd8\xdb3\xaco\xbel\xc9p\xbd\x04\xc7\xe7\xe2y\xe9\x0b2u\x8bb\x91\xd4A\xebM\xbe>\xc5V\xadaR\xd6\x8c\x8a\x85\x12\x13\x1c;\x81\xef9\x99\xf4\xe9\x8aK\x1f\x84\xd6\xe5}K\x15\x9b\xc6\xb3-l\xcf\xbfR\xea\xbd\xd6w\xdb\xa6h\x1dI\x94\xb72\x9f\xb9\x99\x81{\xac^\x9e\x9d\xc3\x98\xab5Y\x0de@U\xe6\x0b\xa9#\xe1.\xf7\xc7<\xe92\xf3\x96\x84L)\"x\xe2\x11|\xcc4*!\x1c\xf9BQ\x0b_(\xad\x0cM)SN'Sr\ni\xcf\xcfw*\x8ew\x96<25\xbe\x93\xf4\x909\xfd\x8c'k\x91\xe3M\x13a \xafk\x93(\xcaVC\x08\xc4>C?\xe9w\xc9\xd1\xf7\x19?\xf4\xb3\x8d\xc5<\x1bB\xb2\x98<\x1b.\xca\xde\xd2\x197\n\xfb\xfe O\xb8\x80Zj\xc6 7)\xdc\x16e*(is\xee\xf9\xa1\xd7\x86\xcb\x0f\xe94\xdeT\x0d\xf2\x1a\x9dan\xb5\x16%O\x94\xa5\xa6\x99\x93\xf1\xcd \x1f\xf8\xa15\x0eD\xfcD?u0&W_\x12\x87t\x81Ez\xb3\xeay\xb7\x03\xcb\xd2\x185\x96\xf2\x80\xbbY$Z\xb4\xbf\x0fY\x93\x95\x16r\xdd\xd4\x0ft?q\xe2E\xdd\xbf\xfdQ\xae\x89\xee!U\xdaa\xdd\x05\x0c(v\xb5\x8a\xf0\x91B\xf8\x13\xa7O\xe2\x9c\x19>\xbc<\xd4\x9e?A\xb2M:\nt\xe2\xf4)\x0c\xca\x0dH\xe6\xd90\xb0&\xb7c`C(\xdbc\xd3\xed{&\xa3J(iWQW6\xbc#\x89\xea&$\xe80\x91D*\x05@\x06\xd1\xdf\xfczX\x93K\xa2L$x9\xff\xa7M6\nj}\xaf\xa7\xcfzY\x93\xf1\xb2Y(s5\x89\xb5\x18\xdb\n\x9d\xacL;\x0c\nQ|/\x1e\x0d\xd9\xd6\xa7\x85\x16\xca\xa5\xcdR\x14\x12\xdc\xd5r\xfaMz5?\xddX\xdc>\xd1\x91 \xcd&>\xb2\xc1\x16\xd8\xf5\x96%\xd3b\xcb\x12\xa6*\xd4\x82\xbc\xdd\x11r\xc8j\xd8\xben\xd2E\xa4]v=\xbbA\xd2\xc1\xc0F\x04\xec5\xe6\xcb\x07\x99\x13\x94\n\xb3![\x99\xfd\xdc\xebdq\xb5\xae5:u\x9c\xcd\xcf\xd2F0\xc5\"8\x0b,\x98\xc9\xa2\x8b\xdb\xe8=gHS+NB#\"\xf4\xeb\x1c\x8d4U\x98\x1a\x0b\xfci\xb0\xc0\x81\xb7[j\xb1 7O ~eX \xc3\x98-X\x907aA\xca^c\xd1\xf3b\x81\x0d\xcb\xd5\x96\xa5So\x19\xfb\xa6\x89F]\xed\n-\xa5#\xca+$\x84d^r\x14d\x8e<\x00\x90Kq\xf5;\xe8+$\x1b\x9e\xc3\x11\x16\x81\x8a\x87\x98\xb7\xf2\x14\xf7\xeb!\xa7\xfa\xaf2\xa9\x97\xfeT:'kT\xca\xc9\xdae\xc1\xcc\xf6\x85\x8d+7\x17ww.\xdc\xdc\xdc\xd8\xdc\xdd\x9c\x90oY\xfb\x95e3\xb1-\x9f\x9f\x9e\xd1L\xca\xb3v+\x1dF\xfbe\x84\x17\xa8Q\xda;\xfbx\xc4P6\xb6V\xaf\xad<\xefH(B'&Op?\x89F\x17\xb7;BW&\xa5\x80\x90\x0c\xc4\x80\x8b\x1c\xc1-x8CV\xbe\xe4\xc4\x1d\x1c\xf8n\xd4%\x1ef\xc9\xe16\xbf\xdd\x9e6\xe3\xba\x96\x0dP\xbaN\xdee8\xb0U\xff\xe4,\xaf\xcf\xd6\xe46H$t\xae\x06\nIe\x159i\xc1 \x17T*\x939\xcfjl\x0c\x95T\xab2\xc7H\xe9\xa5\x1d\xbf#W,\x92[\x1c\xda\xcdG\x85\xa9\xac\x94\xdf\xd4\x9a\x97\x87\x95\xc2}\x8aq\xca\x93.\x86\xa9\xb9R\xebFC\xfca`\xaf\xab\x19\x96u\x9aLm|\xdb\xccET\x0e\xbbL\xd5ot\x9f.xe^?*H3\xb7P\xce\xa6\n\x8f\x93\xf5\xb2\xc8)?\xdaS\xf7Ls\xa7S\x1e\x96\xda\xba\x1b]\x98j[\x7f\x98\x98\x11B\x066\xc3y,\xa1\xb7\x10\xad\xa6?\x8a77\xc4\x9f\xf3/\xe6D\x86\x92Q\xdb\xcfaX\x97,\xd9\xa9\xf1u2\xe7\x10\xde\xeb!o\xfd\n\xaa\x17u \xcfH\x95\x14$z]$\xd6T\x96\xc6\x81\x15\x96\x88\xd7\xb9\xd1-\xe7\x05\xac[\xaa\xb5\x8d\xf3\x1b\xbb;/f\x81,\xc4hf\xdf\xcf\x86\x97\xf2\x0c\xaeG\xa6\xc8\xa8h\xc9\xe4\xd5\xf8\x8c+\x9f\x81\xc0\xb2\xda\x10^\x0b\x9a\xd5\x98N,\xb8\x96L^\xc0\xa5\x8d\xf5s\xab\xe7w\xb7V$/z\xde\x85l\x1a \x18\x16,\xdcG\x8d\xea\xb7+\xc0t\xc1\xf6\xb8\x04\x83\x94s\xf2\xd3E\xb3x\x90\xd4\xad\xfaO\xaf`\xa9\xe7\xa2d\x0bLY\xe0\xbe\xa4\xd2\x0f\x94\x98\xee\xd9\xc3ug\xc4S\\q'2}H\x90`\xd5a\xa9\x9a\xe5\xb8i\xdbS\xde\x0e\xdb'\x89t\x15)\x08\x95\xa1 o\xc3),D9J\xb4z\xbe8\xe2\xafDV\x1a\xab\x04B\xf5\xc7\x8a\x9a\x05\xcb\x967\xcb\xe2\x01\x19\x82\xec\x90Z\xe5\xe8\x08enr\x1f\x8a\xbc#\xd9\xa9\x83p\xa6v/'\xf7\\\xd3\xf1tb\x0b\xd2\xa2l\x0f \xb4\x8d\xec\xe4\x80\xecT\xfb\xcaQh\xe4\xa05?\xcd\x88\x90\xc5\xca\x96\x8b\xe7\x16\xb4\x18\x12\xb6\xa2\xa9\x84-fD\xaa:\x81\x8b)\x9c\xae\x17\xbaXIYt\xac\xe2c\xb9T.\xc9T\xd2\x95/%\x86\xe0\x1b\x9b\xa7\xc3vn#\xb9]\x9c\x17\x91\x92\x12\xeb\xe1o$\xa7S#@H\x11\x80\xce\xcb\x8d\xc24\n\xf8\xcc\xbe\x93\x84\xed\xd6\x95\xc5\xad\xf5\xd5\xf5\xf3\x0b\xcc>2?e\x1e\x8f\x13\xee:\xe00\xeb\xb1}?\x08X\x8f\xeb0\x1e\xed\x91\x19\xf2\x83\x8c\x8d\x9c[Q\xc2\xc6\\g\x9aB7\xe2;\xd3\x04\xbb\x11\xe7\x99\xce`,I\x98?\xa1W\x1b\x8f\xc1\xbf\xca\x9b\x039PF\xa9\xba(\xd7\x95T\xd0\xbc\x97^b\xed6\xbcp\xa1$\xe3(\xe6i\xab\xd3\x99\xd9\xe3_h%\x99\xf4~v\xa30s\xfc0U\x17N\xb2\x87T\x8bI\xdc\"w\xeb\xdf]\xe5\xc1\x98+I(\x08\xa2}\xeem\xc3\xa8\xba,\xed\xa8\xe46\x99\x84\xfb]f9\xe9\xba\x1d\x1f\x9e\n\x95\xb9\xcd\xec\xf4\xc0\xaf\xa3\x07\xddI\xa2B\xfdbh|u\x92\x81\xbc\x08L\x0b\x07\xb79V\xcd\x15f\x8a\\\x9f\xbb\xc1^\xab\xfes\xa1\xe9TMEtT\xa16\x18\xfa\n\xaec\xe7~e\xc6\xa3\xfa\xecL\x9f\x84\xdc\x1c\xf14\x1a\xf1)\xc5fSG \x1e/\xe1\x9b\x9f\xa4Y\xbb\x06G\xac\xb2t\xd3.V\xe4\xbf\xc9\xfc}\x82da3rh\xa2\x84\xb8 \x92D_$\x13\xa9\xeeg1\xa6\x06\xe2\x0b\x9b:\xe3\xa7\xe2?\x10\x1b|\xe4H\xa6\x8c\x95\xcf\xbd\xcf*\x97#2\x9b\xf2\xce\xcc\xc8\x89\xa7h\xa5\xd4\xd2\x91#!\xec\x7f\xddv\x1b\xaf\xd1#s\xb6\xad\xd7\x87\x0b\x99W\x19E\x84\x8a\xa2\xf0\xa5\x11A+F\xe5]\xff\x16\xfbFhD\xfc\x80\xbb\xb9\xf4,\xb0j!]\x95\xe5f\xfe\x94E\xd7\x90\xd6\xceH2\x88\xa4\xaa($\xcd\x8aB5^\xb8\"\xe1\x17\xe3\x99R/\xad\xa0\xb7]\xcd\xcf\x9a\x04)|\x9aj\x9f\x83\x89\x94\x1a\\\xe7\x8e\xe8\xa8\x0c\xd6\xd90\xaayr,\x97%\xa6x\xc1M,C\x968\x0d\xcf\xc9\xd6\x1f\x95\xe2\x80/(\x03\x90>\xeeb\x9f\xaa_\xd4\x89\xae\x97\x1eJ\xd4\x7f\x81%5*\x88\xdc~+hb\xfb\xe5W\xdd\xca\x1d\xe0VMS\xf6s_K\xc8x\x1b[\xa9\xac\x0d\x80\x93_\xcd\x1by\xb0\xa3\x0b\xcc\xb1\x83K\x0f\xde\xd4\xd8(\xcb\xaf\xe6X^\xbf\x95rJ\x1d-\xfa\x86P\x89/\xe3\xf1\xd2\x0f\xebnB\xd3\xa1\x94\xd8Vn\xe7N\xf0}~\x08(\x86\xbe\xd1\xf5\xaa[*j?\x917G\xdf\x80\x15\xa4#K\xdba\xfb$y\xe7:2>\x16\x13\xfd\x8dj\x05I>\xd3\xb7\x10\x16{\x82\x02\xf1\xf3\xa2\xfd0\x98\xd2\x1d\x89Y\xc8emj\n\xfd+\xf4D\x9e$\xea\x02\xb9Y]aZQ\x9at\x8d\x8c\x7f\x8e\xa94u?\x10\xf8Tp\xfb\xc95\x02I\x9f\xfb\xa0\xc4v\xcc\xddv6\x93 ~'\xf4\x8a< \xda\x9d\"\x93\xbf.\xb6\x9b\x04u6\n\xfdk\x1e\xbbL\x14#8\xac\xea\xa2[7\xc6\x00\xfe ,\xdc\x0d\xb8\x934\xbc\x8d\xa1\x7f\xcf\x83dB\xfe\x0f\xa6h3O\x82\x05[\x9e\x16\xfc\x13\x03\xde\x96^\xd1G\x1a\x1e<\xd4?\xf5 \xe9j\x98\xf1\xc4\xe5q\x16%\x0b2=\x0f\xfe*\x96j:\xf9\xb5\xfc#w\x8du\xbf\x1a\xef\xee\xf2/\xe1i\x1c\x85)'C%\x9f\x7f\xfbcu\x13\xee\xf10\xf3\x9d ]`\xad\xd4\x19qEg\x1b\xe2\xe0\xf4O\x91\xb7&\xa7\xf6\xf2OP\xc98[\xa8\xbe\xe2y+\x8d\xc2\xee\x1f\x1c\xff\x83\xc9\xe4\xad\xf9\x94\xdc\xed\xccdC\x1e\xb6\xfb]\xd6o\xb8$\xb0Bj\x96\xc9r\xc8\xa6\xd5\x8c\xb4@x\x1d\xa2\x1d\xcc\xd1\xec\xb2V\x11*\xa4i\x8a\xf9\x08zG\xab\xe1\x0d\xf4\xaa\x1553&Nx\\N\xdf\x01r\x95\x11G\xfcg\x01\xc4p)\x90Ws h\xdf\xa8\x92\x1d6\xebLdT\xd9a,\xa8\x85\x90\xb5n\xc2\x02\xddT\x93\xbb B\xf8\x04\xbcQ\xae#\xb6\x04n\xfaW\xb3I\xe4\xab\xcd\xff\xb9V\xb7\x0d\xaa\xdbh7\xe3N\xb7\xb9\xc6)\xa2\xce\x8c_\xfe\xddm\xb2\x0c\x97\x7fU+qe\xb8pc@\xcc\xd4\xfag\xbb\xd9\xb0\xda5i\xe7\xd3\x04\xd8L\x8a[113\x8d\xd9!u\x10N3v\xd5\xa3\xd5B\xb3\x0d\xd8\xf6S\xb3\xb6\xbc.g<\x98 \xd1)]\xf0nQD\xe6;m&=\xf5\x98\xdc`\xed,\xa2\x88j\x1e\xa0\xa2\x9b\xfa-\xfb\xbf\x90\xb5k\x82\xe7O\xf5\xab \xca\x99\x9f:&\xe7\xab\xf2 \xfa\xed\xda\xe5\xbe\xace\xf3\x85\x9e\xa4\x1a\xf32\xab\xe2M\xdf\x8e7\xf6\xba\xea\xdai\xbaH\xb9t\xe6EG\xca}\xe9x6j7u\xdba\xfb\xf4 \x12\x9c\xa6\xee\xa8N\x9c\xb0\\R\xc9\x00NZ\xc5Q\xa0\x93\xb3\xb3\xb6P\x04\x00\x11\x0bm\xaa\xc6pr\xb6\xe6\xecXB\xb9\xfe\xe9\xc5\xb3}\xcd\x01\x18c\x95T\xb2\xda\xc8\x80gk\x91\xeb\x04 `-4\x9b\x03\xb5\xf7\x834K\xc4N\x92\xf2\xab\xceHU\xed\xb4\x0bi\xa9q,\xbf}bf\xec\xd8g\x0fw\x130Tk\xfb>|op6\x85\xf3S\xb9v\xc0U'^w7_\xa2\x96\x169\x9b\xe9\x87`C\xef`E\xb9\xee\"^O\xe9\xb9\\#\xac\x06*}\x99[\xb9*\xa0\xf2\xb7<\xb7\xe6\x9cFh9\xda\\)\x1f~\x97\xf96\x03\xbf9\x0d~\xfd\x1dIh5\xe2\x87U#>{\x8d\xb5\xa3&\xfb\xbdR!:\x02w\x9f\xab\xd8n\x12\xb4[\xe2CU\x89\x08KV\xfd\xc2\xa8?\x93'\x81@2x\x81]HH\x99\x8a\x84#\xe7%\x04\x03\x89ED\xfd\x06\x9f\x9f2\xe6\x0fx6%\xa6q\x15\x0d\x83\xdf\xdf\x94\xf6\xfc\x05\x19J\xf8\x0d\x9d\xa5v\xef\xe8*\xe1q\xde\xf6\xda\x9f\xf4\xf0\xf0\xbf\xbc\x87\x07e\xb0u\xb1~\x82U\xdb\xef>e\x00\x91\x8e\xad+\xc5sE]\x96\xce\xecn./\xee\xac\xdc\x84\xd8\x86\xed A\x0df\xef\xe0\xb9\xf1j\xb4J\xa1\x04\xd0P\n\xdc\xeb\xce\xc6\xf9\xf3k\xd3\xf6\xfa\\1)8U\x89\x19\xb2\x8a\x05;\x82\x02=\xa2o\xc2=\xf7\xf3\xc9\xd3\xd7\x0d[\xb5\xd9\x1f\xa6\x91\xad\xa7\x90o+ \x16\xea\x8b1e-\xe0\xf8\x15\x8d\xe7\xd09\x9f\xfb\xbe\x91C&\x1b\x95c\xb4[xtNa\xb2f%\x84\xda\xf7C/\xda/.3\x86NZ\x93\x00\x0d\xff\xb2\x99\xc09\x8c\xf2L\xc7uKJ\xbe\xccy\xbc\xe6\x87{\x17\x9ct8\xcd\xfd\xd2\x04\x1b]-\xf4K\x98|\xc4\xae\x9a\xfc\xb6\xb5\x1b[\xf2\xcc\x99\x90\x06\xc4$\x1d\xdaq\x06\x0b\x85\xbb\x10\x1dJ\xe5\xcb\xdd\"\xd1\xacEUq\xa4\x9a`UU\x00\xf4\xb2-|\x07@\xdf\xb1+\x17\xce\xd7'W\xff\xf6 \x89\xbc\xcc\xd8v\x93(\x08v\xc0\xf5.U\xffPw\xe0\xf2[\xc2\x1d\xefp'\x82r\x8a\xb8\"\x1c\xae\xd45!X\xcd\x0e\x8f\xfd\xda\xb8\xf6\xbe5\xf2\n\x0c-'g\xb1\x97d\xaej\x9c>AR\xa34\x86\xb6c\xde(\xdf\xa0l\x07V\xac\xe8\x7f}X\xc1\xd4*\xc5\xe5e\x9cH/\x0b\xc67\xc9\xcf\x06\x9c5\x81&5\xc4\xbdLKp+\xef\xf8c\x0f{\xd8h-\xafU\xde\xc2\xcfT\xee\xe3\x08r\x1f\x17\x9e\xf6y\x8d\x99\x1e\xb2*V\xa9y\xd4\xe9\xb2\xb0\xdd\x91\x8f0\nT\xf4\xc3Ag\x8aG`\xc5\xfeG\x13#D\\Yj\xae\xe1\xd6 0O@k\xa14\x10Bi \x84\xd2\xa0\xa1\x9eV\xa6\x13!\xef\x8b\xe3#+\x9fK\xa2\xd1j\xba=\x8c\xf6\xc3\xef\xf3C\x89\x88u\x0d\xc8\xdca}\xf4:ls\x7f1\x8d&\xeeO\x8e\xa5\xf1\xd8\x19\x16O\\\xa9\xa1,\xd5\xb4Rr\xc0n\xa7\xac\x9e:B\xcc\x12\x93\xef\xc8\xa4\xa2\xf5u\xe7\xe5\x9d\x8cyX\xf65\\\xbb-\xe3\xd0\xe1\xcaA\xd3\xa4M'\x83v\xd9Q\xe6Iw\x16\xf1\xd7P\xaaTs\xd5\xf6^z\xe9\xb9\x1b\xac\x8b\x84\x98\xea.\xbe\xaa\x07N\xff\xb2Z\x95hT7\xc4\xc3\xf4\xb7\xf9j\xa4\xd6\xd8\xca\x8a\x8b( \x107\xa1\xcd\x9bYTs\xfdd\xae\x9dp\x1eIE\x06\xafs\xfaTW\xe3T\x86\xb5\x0cf\xaa95[GX\x85RV\xe4\xb2z\x0c\x9f\x92`2\x85\xe6`z)\xa8p\xa7J\x9f$\xbbh\xc2\x8f\xb1\xc9\x06\x04\x0f\x90\xcc5\x1c\x8d\xd6\x11\xf08\x13\xc4\x8c\xe9\xcc\xf9\x91\xa9\xd8\xe9J\xc4o*\xd1L4|\x9c\xf9w\xfah\x12\xfd\xd3'\x9e\xebwhT\xba\xdd\xf6\xf1\x9b\xc7\x07]\xd6b\xad >\x1c\x13(\x94#\xe9\xa8o\xe8\xa6\xa0\xa2\xbb%\xaa\xda\xf6\x1b\xe6\x18J\xfe\xdav\xba\xf0\xdc@h\x8eP\xdby!\xe7rl\x95\x9f&2\xf3\xa9,l\xac\xe2\xf7\x8b\xd0S\xe0\x9f\x96\xeb\x043\xa9Y\x03\xd7xi\xf9i;\x01\xfd;0Z:\xef\x80\xe1:D\x1a\x0c\x92\x11%g\xc7e*\x92\xa5-t\xacq\xddF5\xb2\xe8\x8b[\xb9f!A\xca\xbd`&\xec\x87\xc5Zn:\x89\x98/\x17\x92\x8cY9u\xd7-\x0b\xc8G\x1eg\xb2\xa8\x96\xac\xff\xd68\xc4@\xae(\x96\xf7\xa7\xb1\xd7O\xc3%d\xbb\x8aWP\x87\x1340\xbb\xe5\xa9\xda\x8d=\x9e\x01m\xc4\x94f\x04M\xf0\x8d\x97\xaf\xfeC\xe1U3\xe5\x97\x84|\x14\xe7\x19\xf7\xb6\xb3\xc3@\xe6#\xae\xad \xd6\xb4\xe5\xf4\xd2(\xc83\x95S;\x99\x89\xa3T\xc6\xea\xd4W\x93\xf1\xf7\xec5v\xbc\xed\xe4Y\xf4#X\xc7\x1f\x0d}\xcf\xe3a\xe78[\xa8\x02:\xc7\xeb\x99O\xab\xef\x1fp\x0f\xf7\\\xbc\x90f\xafidx\x99^\xf0U\xf9\x1fG\xf0\xe0b\x91^\xad\xa7\xd221\xbdm\xa5\x9cN\x97\xb5\x8f\xc8wTZi\xe6d\xbe\x0b\xae\xd3\xe5\x81\xbd\xf4\x12\xf3eZ\xe0v2\x13\x8dy\xd2\x0f\xa2}v\x94\x15\xff\xb8Z\xf9\xd7\x1b\x9d\xc2\xdd\xde>\x17=\xd3IX\x88\x14\xc5 \x960\xc0\xf3\xdaT\xa9\x93\x8d_\x88\x96-\xb0\x86D\xe7\xba\xec\x02\xab\x89q\x13\xbf\xcaQ^`\x83\x06,.\xb3\x9f\x056\xae/I\xa4\xae\x056\xb4\x13\x1f{\x1b\xa5{\xe9\xfa\x95\xa8r\xa6i\x1d\xbf\x18\xc3\x9e\xccM\xef$\xf5UZ\xac\xed\x01\xb4_\xd4{\xa44\x8b&\xa9\x1e^;\xf1\xbb,\xb7SgDX\xb2\xa1\x9fvY\x9d]\xd5\x08\xc1\xa9\xd5\x90\xed\x1aCv\xda\xe9J\xeb\xed\xec\xab\xac\x0f\x8f\xf8\xf5\x8f\x1e\xed0\xf7z\xbfj\xc8\xee7\xbf\x16/\xd8\x9cO3\xa7\xc2 \xe5\xbb\x83\xc1\xcc\xcd\x9b\xd2\xb9\xec\xe6M\xed\x12]\xf2)\x0f:\x1d\xe9a\xa6L\xe2\xbc\xcb\xae\x8b\xba&\xc9\xb2\xdb\xe9\xc8\xf0\x99(\\\x8b\x1co\xa2\xfdL\xff4\x07\xf6g\xe2$\x8a\xd3\"\x93\xc2L\x16\xc1\xc1j\xca5\xc0\x14\x17F\x92G8\x939\x83\xae|\x04U}]\xf5\x1a8*\xbe2\xadH\xb0\x82?\xd4\xe9\xc4p\xc3\x10\x12G\x02{V\"J\x96K\xe6\xe9\xbc\xb4\xd2\xf06<\x92I\x82.\xaby\xf6hO\x88=\xad\x84\x87\x1eOj\xcc\xa6\x8a\xdaL\xbc]a\xc5\xa0Rdq0Q\xaai\xec\x84\x84\x9c\xd1F\xfa\x0b\xf0\x9c\x04\xe0Cm\xe1\xbb\xdd\xda\x9e\xb8z\x90B\"F\x1d?\xa7\xab|\xa3\xd3E)\x19\xee\xb6\x8b.\xcc\x15\xf37\xda\x87\xe7\x1bG\xfaCi\x176\xff\xfc\x1d\xd9/\xfd~G\xf6\xbf8\xd9\xb7\xe8\x85\x9a\x13d\xce\xe0\x0b\xd3\xec\xf0w4\xfbw4\xfb\xab\xa6\xd9\xcf\xe7\x1ag!?\xb5It\xa28='\x13\xb2=\x87\xe3R10\xc4Kt\xba\xaf\x93\xb3\xa7-L\xe3E\xe5\xfb\xfa\xe6\xeeG\xa3\xb7(\xc9{gy/\xa5TA\xbe\xd5~\x86\x85&`\x13\x87\x0f\xfc\x97\x85\xa1\x93\xcc\xd4l\x8a`\xa8)\xed\x19\xcc\x04\xeaB$\xf9tlD\xff\xa6\xf5\x1e\xc2?U/\x91\x0f\xc0w\x1b\xbc7'\xb6f7\x9a\x19h\xb3\n\x03\x13\xbf\x98F!\x9e\xfc\x146L\xf6%\xe6os\xe3jwf\xa2P\x90\xdc\x80g\x96G!m?\xb3\x8c/\xbd\xc4Zz\x10\xe5@\xcdP^\xec\xa6<\xdb\xf1G<\xca\xa5\xbb3<\xb8\x7f\x86\x1d\x99\xeb|\x95+_\x0b\xad1s\x92\xaf\xd3\xd2Y9\x15\xeb\xa1/\xefF\xf9\xbd\xc6\x96\xe7d\xce\x82?r\x06\xfcx:\x1e\x1c=\x18\x05\xaf\xf6\x9c\x94\xbf|\xb2\xbbya}\xfe\xda\xe1\xd9\x13\xce\x95\xadYgy\xd6\xbftkq\xdf\xbd0\xf0W\x97\xceF\xd7\xae\x04\xa1s\xe1\xf5\xd3\xab\xb7V\xf7/]8{r\xd5_\x1c\xf0\xf3si/\xbctzu4\x9c\xf5.,\xbe\xbcvx\xfa\x84w\xc2\xcd\xbd;\x97\xf2\xde\x89\x8b\xe1\xda\x9d\xd5\xfdK\xcb\x8bc\xf7\xc4\xb5p\xd5?;\xef\\\xb9|\xe2\xf5\xd1\xe9\x93\x9b\xdb\xab\xfb\xab\xcb\x8b\x83K;\x8b\xfb\xab\xcb+\xfb\x97\x96V\x07\xee\x85\x8b\x81;\x7f\xf9\xd0\x1b]>\xeb\x9e8\x1b\\=\xb1\xb5}\xf5\x8d\xad\xb8wg\xd6\xe7+s\xf1\xb5s\xc1\xbas\xe5u\x7f\xf5\xfczz\xf5\x8d\xf5;\x9b\xdb\x17\xd3k\x17.e\xee\xe8t\xda;\x1f\xe4\xd7\x0eW\x07\xee\x89\xadS\xbd\xf3\xbb\xa7WG\x17\x87W\xe7\xb3\xd0\x1d\x9d\x9e\xeb\x8d^\xcf\x9c+s\xc3k\xf3\xbb/\xaf\x9e?5\xee\x8dv\xbf\xb3z\xbe\nw\xcf\x9f\xbe\xe3\x88\xbe\xe6O\xbe\xbcz>\xc8\xc5\xdfW\xaf\xec\x0f\x9c+\xa7b\xef|0\xec-\xa7\x83\xab\xa3s\xb7\x9cy\xef\xb0w\xe2r~mi\xee\xf0\xda\x1bg\x83\xabo\xbc^W\xde\xdf\xbcup\xcby\xe3\xe2\xad\xde\xf9\xdd\xc1\xd5\x13\x83\xd3\xab\xb7v\xf7W\xfd\xb3\xb7\xf8\xce\xac\xbf\xbe\xb3\xe8\xaf\x9e\xbf\x16\xf7\xce\xef\x9f^\x1d\xc91\xf9\xab\xe7O\x85kW\xce\xcdz\x17V3\xf7\xc4\xd6ao>\x0b6\xb7/~\x87\xcf\xaf\x8f{\xa3k\xf1\xb5\xc3S\xb7z\xf3\x07c7\x9c;\xbd\xea\x9f\xcd\xaf\x1d\xce\x0d\xbd\x0b[\x87ko\xac\xcf\xba\xa3\xd3\xc9\xb5\xed9\xb3o\xfcDv\xab7\x7fj\xe4\\qso>\xd8\xf3\xce\x0fO\xf7\xb7W\x07\xbd\x91\x9b]}ck\xd6\xf5\xe7\x0eQ\xdb\x87W\xafl\xc5\xde\x1b\xeb\xb8\xdc\x1d\xef\xc2\xc5\xb13\xbf\x9b];\x7f\xee\x8es\xfe\xdc\xa1;:w\n\xd5\xdd\xbb\xfa\xc6zt\xf5\x8d\x8b\x87W\xdf\x08d\xfdb\xfc\xab\xb7\xd6wv\xe7\xc4\xffV\xfd\xb3\xa6-\x18\x93X\x93\x15\xb1&\x87\x9b\xdb\xabw\xd6K\xf5\xd6\xael\x0d\xdd\xf9\xe1\xd0\x0d/\x0e\xc5z]\xda\xb9:\xbbvk\xef\xce\xa5;W\x0f\xd6\x97/\x1d\\\xba\xf3\xfa\xfc\xfa\xf2\xca\xdc\xea\xf2\xee\xfc\xda\xad\xbd\x13\xebw\x06'.\xed\xbc~g\xfd\xce\xe0\xf0\xd2\xce\xa5\x93\xab\xb7N\xber\xf5\xca\xa9\xb8w\xe5\xdc\xec\xb5\xcb[\x87W\xaf\x9c\xbasmt\xfa\xb0\xb7}V\xae\x99s\xe5\xe2\x9cw\xfe\xf2\xc6\xd5+sb\x8dg\xdd\xd1\xb9\xdc\x9d\xbf6vG\xb3\xfe\xea\x85\xadS\xae\xc0\xa1\xf0\xe2\xd8;\x7fn\xf6\xda\xf6\xea\xe0\xea\xfc\xb9\xf4\xea\xec\xdc\xf8\x9a\xc4\xad\x83\xb87\xbau\xf9|\x90]{\xe3\xd2\xe9\xd5[\x8b\xdf\xb9\xb4\xbd:\xb8v\xe1\xb2\x98\xf3\x81{\xb8:\xb8:\xba\x1c:WN\x9e^\xbdu\xf6\x8eX\x0b\xc0\xab\xade\x81g\xde\xf2\xac\xef\\9\xb5w\xed\xca\xb5\xb87\n\xc4X\x8en.\x9d\x1e\xf6F\x81\xd8\x9f\xe0\xf2\x85\x8b\xc3^\xb8>\xea\x9d\xb8\x98m\xde\xda\x1f_\x9d\x0f\x0e\xaf\xce\x1f\x04\xe2oq\xe66\x07\xd1\x99\xd67D\"X\x8a\x82\xc0\x89Sx\xbab\xcd\x0f\xf7\xe4\x1f\xe0\xcb#\xff\\\x0d\xe3\x1c\xfe\xda\xe1\x07\xd9b\xc2!\x0d\xea\xd9<\xcb\"\xe0\x16[\xd2KX6\xa5\xfe+\xb3}\xcb\xb7{\xeb\x82\x11\xa5\xff51Ch\xcf\xecW\xac\xafS\xf6mF\x10G7f3i\xf4mF\x90T\x01H\xef\x81\x02\x10#\x88\xab\x00\x15#\x88\xf4\x13\xb7\x9b\xbf\xbf&\x87m\xdaqLx\xbd\xb10p\xab\x85!3\x16\x06\xae^L\x98}\x95\x85\xec\xbb\x8c\xbf\xca\xc2\xa3G;L\xc5\x0d\x17\x16\x86\x10\xa9\xe1jb\xd9tI\xa3U\xe9#G\xd0\xac:3\xb7\"?l\xb7X\xab3\x93%\xfe\xa8\x8dEg&\xb5\xfc2f\xd5wd\x96#\x9b\x14\nLl \x99R\xdbSb\x1c\xc9\xa8a\xa4|G\xdc\xe9(\x99\x05\x8a\x17\x12K]\xec+\x1aIPj\x0b\x9e\xdfE6\x85\xccj=\x98`9\x98\xd6j\xa0\x11\xa4\xd0\xd6\xebET\x95\x834\x0f\x82\xd4M\xb8\xed\x81)\xfd\x0bM\xc9\xfa2\x96\\q\xbc\xcb\xae\xb7\x8a\xf6e&\x9d<\x08j\xdf\x1e\x93\xc9\xec\x8cg\x8e[k\xf5\xe0 \x88B4\xaf\xad!\xed\x84\xd4J\xf7\x9d\xc1\x80'\xc7\\\x8dn2\xabN\xc8^c\xadcr(l\x81\xb5\xea\xbc\xc6\xa7\x1fG\x9b>3\xe97\x99e\xdc\xc0I\xd3u\xf9XZ\xdc\xf6g\xcc?+\xafj\x95\x7fw'\xbb>\xde\xe8Tb\xfd\xdb\xae\xc5\xceR\xa5\xde\x1e\xf1\x97\x1bE=?\xe0bI\xaa\xfb\x9c9\xbd\x80g\x0b\xacu\x0c\xfeB`\x8f\xa7{Y\x14\x0b\xb8\xfa\x13\x15\x08\x9cd \x9a=6\xf4JW\xb3\xafV\xe8A\xf0;J\x00\xbf\xdf\x1a%\x18\xfa^CV8\xa0\x01{\x9c\xc7K\x90\x8d\xb3\xa1=I\x0b\xf8\x0c\xa0\x93\xd0\x02\x01m\xba\xd2\x9bB\"\x88\xf8Sb\x05\xf1\xdb\x90DC\x0cE\x90\x8brw\xe2\xdf\xd0\xa2|\xabQ!\"k\x19\x94c-\xd9b\x8b< k\x86%\x93\xf1\xbe\xf4\x12;\x12NAe\xc0\xb6*C\xe8\x9b\xa9\xcc\xf5\x1a{\xb6\xe1\xd89\xf3C\xe65\xbb>z(\xedG;\xefL\xd2\xf6\xf5u\x83W\x1b\xec\xa4\x7f\xa2\x83\x1c\x1e\x0d2F\xdc)L :\xc8\xa9\xa85\xb1'\xa6z\x0b\xd8w\xd9\xdc4}0\x99\xd4Q\xbe\xe5\xd2\n\xa3\x90\x0b\x02=mT\xad\xa0\xea~\x98O\x91hob =\x84^\x10\xb9{0\x86\xae\xf9\xe8F\xc11\xf9(\xa5\xfc\xde\xd8\xd6\xf3\xda%t\x0cW\x8c\x0c%\xd7K\\\xc1\\\xca8u\x88=\x11\x97\xbf0\xa7J\xb3\xc3\xa0\xf6yl\xfd\xf3\xfc4\x0e\x9c\xc3\x05\xe9}\xacv\xd1\xf2nG\xf9\xd7`9+1\xc7\x9a\x14J/\x86\x19v\x8d\xc2\xf3;\xb6\xf3\xe2\xd8\xce$T\xf4\xfc\xb1\x1d\x0dK|jZ\xc9\xa9\xa8R\x16\xa1Z\xfb\x89\x13\xc7<\xa9u\xd2{!\xd8S\x1c\xc4vI\x85\xfe\x1d&}}\x98\xd4\x93\x8b\xfeU#\x93\xea\xe5+\xc5\xa5\x8e\xfe&\x98?\xcd\x91Y\x1af\xabF|.\x19t\xeaQp\xd2\x82f\xfc s\x12\xee\xb4*\xb7\xec2\xb5\x936\x1d}\xf1\xc6}\xd1\x02j\xb9r\x86\x8c\xa1j\xaa3Tw\xa1Ws\x80(\xdb\xd4\xe6\xab/z\xb0dV6(-\xc7b\xe9b\x08\x85lo\x81\xeb\xe8\xcc\xba\x17 \xd4jB\x00\xa7<02\x15&\xfc\xb5\xc0\xf8\xcc(\x0f2?\x96V\xa7\xeb\xad\x96\xf4\x0bo\x89S \xaf\xf6j\xb3\xac\xaa\xa3\x17Q\xa4\xedZ/~\xf5\xef\x1bC\x13\x9e_\xa9Q\x0f\x0d^\x16\x1d4\x14\x06\xedF\xafj}\xb9\xa4hte\x14g\x87\xb2\xdd\xfa\xe2\x91\x1e\xab\xdc\x17\xd8?\xf9<\x12{\xcd\xfe\xbd-\xb3u!\xc8\x17\x15\xfa\xc4\x81jt\x0f)Q\x16+\xf9\xab\xad\xa8\x17\xaa1\xab\xac\xc6\xb6\x86\xe5 \x97\x86N8\xe0\xc6?\x05\xfei-/P\x94\xbdV?\xdd(V\"n\xfdt\xd5\x80Z\xf6d\xd6w\xbb\xacu\xecX\xab\xa3DWA\xf6\xaaq\xca\xd3\x054|\x99\x012}R\x1a\xa2 Y1\x91m\x999\xb7)}\xfd\xddnQ\xe8\xb7\xc9\xc2\n|92\x87\xac\xfe\xd5\xa3T\xbd\xd7\xa8\xda\xab\x86\x93BM\xcb\xd4\x81\x9e\x99\n\x8a\x95\x9b\x9a\x18\xf2\xc9'\x91\x1a\x08\x9e\xd6m7\x93\x83p\n*\xe3K\xab\x02\x84\xd7+N3\x939\xc9\x80g3\x80Ei\x83\xf3\xb43\xe1\xa5\x1b\x01\x8f\xd8k\xcc\x9f\xce\xd0\xaf\x7f\xc6\xb7\x06\xe8\n\xb7\xfb\x91\xdd}\x9e\xe0~\xd3\xa4\xc4\xe7\x9a\xf6\x04=\xd4\x93\x97\xe5\xba\x103\x04\x81!\x13\x0f\xbbS\xd3l\x17\xdc\x1a\x12[\x88>\xc2\xff\xeaR\x8f\x85\xd0`.\xd8\x9a':A\xe8g\xbfe\xc1\x9f\x91\xb9\xb2\x17\xc2\xec\xd9d\x86\xcf\x9e\x83\xe9\xb3)\x88\xab\xf3e\xf4\x00\xe8 X`\xad0\x8ab\x1e\xf2\x84\x85Q\xc2\xfb\x9fCe\xd5e\xb0\xce\xb6\xd1\x8c\x98c\xf3\x04\x9d;\xf4\x03/\xe1\x96\x90\xeeIK\x0e\x9a\xbc}U'\x9a\x8d\x86\xdc\x1f\x0c\xe5c\x13ymR\x18\xf1\xebE\x89\xc7\x93\x05eUj\x10H\x9cd\xe0\x87\x0b\xac\xe1\xa1\x92\xd8\xf1\x95\xfa\xf2O\xc9\x04\xb0\x1ee\x8b\xa1?r2\xee} \xc9_\xdfN\x17'\xccO7\xc4Y\xf5\x1a\x84\xc2\xb1\x8e\x19,\x1fL\x85\xf0\x82\xb1\xd4\xe2v\x18\xa5n\xe2\xc7\x99\xbe\x00\x98@6\xef\xda\xce\xc1oO\xe5Q\xab=I\xdb\xd1\x0b8I\xdb\xa9'\x11\xac\xb41\xec5p:\x0e\x95\x8f1,\xfc\xc4\x9dI:F\xe3!\xe8by\xb3\xe3\xc5\x8b\xa6z\x15,\xa2\xa9\x1a\xc6\x82v\x00d\xec\x9b\xe1\xffK\x9dp\xbcZ'\x1c\xcf\xe6j\xe3\xeb*6\x1f\x1c\xcf\xe6j\x93+\x8057\xa2gs\xb5 \x14\x80\xe4\xecw\x15\xe0\xf4+\xa71\xa8\xaf@sd`\xb1\x86\xd8\xfdt\xbc\xaf\xc7OG\xffE\xb4\x91\xe7\xa5\xf5E\xfcQ\xd2\xb5\xa5 \xc1d\xbc\xd6\x8c5!\xee(\xa8\xc4\x1d\xb9\xe0\x15\xe4B\xdc\x91{\xf4h\x87\x05\xd7\xdd\xaaW\x90k\xb9\xe0SK)\xa8\x866\x99\xe5\x84\x11\x81\xdf\x19aF\x115\x9b\xd5\xc5\x1c\x052\xe6(\x99\x19\xf0\xecR\xe4\xf1@HO\x13E\xec\xd2\xf8\x94\x17?7^\xfc\xad\xdf;^z\x15\xfbxKf\x93+2\x87\xfd\xe1\xcc\x1f\xfc\xde\x0f\xca%~p\xfcx\x97\xb5\xa4\x05\xc0\xd6\x96k\xd2\xd8\x1eO\xdd!\x1f9\xa4\xc9\x9aB\xbaQ\xd0\xca\xc8\x14\xee\xaaIo\xf1\xfe\xb6\xac\xf2<\x93N\x14[\xab\xbc\xbf;\xd3\xf7C\xafx\xde\xdbf!\xb8\xdb\x85\x9c\x14\x84\xa1'\xc4 \xa5V8H\xad\xc2\x81\xf3<\xc2\xc1\xd7\xca\x18Uj!\xb9=\xcdJ:\x9f\x98\xff\x94)2\xca\xa7}\xf9\xd8\x81\xc2r\x83\xebK\xe5\xb2T\xc2o\xe7~\xd2\xc4\x99SY.l4\xd2\xb9\x8a\xcbo\xf1~}\xa1\xbe\x99\xc3f\xeds\xf9L\x11`>\xa3nz\x9b\x8d\x832\x8dd\xbb\x05\xecN\x9e\xe4V\x83\xb9b\x08\xa5%\x95\x9aXx\x0c\x857\x13\x7f\xe4g\xfe\x98O\xac0bgX+\x92#i\xd0\x1e\x06\x82\x04\xc2\xab\x902)\xd0\xef\xff~\xc2\xfbuna2 \xa9|\xccx\x00\xe1\x0f\x1a\x07\xcbt\xab=\x10\xb4\xec\x88S\x14sJ\xc5\xccIo\xa7P\xcc\xb8\xa3\x04\xb5\xd6\xdcI\xa1~\xe5[\xa2\x91\x18\x06\x93\xff\x7f,\xf3\xb3\x80\xd7Z<_`\x7f\xd0\xd3\xcd\x9b\x19?\xc8j\xfb\x8b\x05_\x10\xbc\xa8\xb6c\x7f4h\xec7M\xdc\x05\x16\xb6O\xce\xcd5!\x95V/\xe7g\xe3\x83\x86\x8d\xdf\xf7\xbdl8\xb9\xd8Du\x96\x19\x15t\x8d\xf7E\xbfs|4\xe9\xa5=\x95\xbcL\x92\xc2\xc0\x11\xd8<\xa1F/\xca\xb2h\xb4\xc0Zb\xb0\xb5%k\xe2_\xea\\G\x04\x15=\x94\x89\x1a\xfctcq\xfbD\xbbS:\x07\x1e\x8f\x13\xeeJ\xcd\xad\xa6z\xba\xef\xcbL\x84\xae1:J\xbe\xe9\n\xa5\x8c-\xb0#G\x06]y\x06\xcb\xa7+;\x8c9\xbc\x997j2\xf9\xb8N\xca\xcd\xd9]h\\\x99 \x87\xc7\xa3\xb6\xa1\xc6\xe6\x18Bo5\x86\xc6:\xcfelb*\xc0N\x90\xdc\x05\xd6@\x9d\xf5\xaf\xe0F\x8d\xf7)\xfa\x07\\\xa6\xf1\xa12\xfd\x0b\xe5\x14\xa7xL\xbf\xc0\x85\x05v8\xb9\xb8d;\x0b\xccm^\xb4\xa6\xcc\xb1\xb0\xff\x8e\xe0\x0b_n\xfb\x87_r\xfba\x08/v\xf7\xff\xf1m\xa8\x96I\xea\x1e\x8b\xd3\xbf)\xf6T\xbd\xf8X\xbf\xa9P,\xccG=\x9eL,\xe6\x87\x19\x1fLQ\xae\x17E\x01w\xc2\x86rZ\x03\xfc2\xc86\xfe\x92vh\xa6\x91C\xc9\xa9\x13\xef\x02\xd9\x7f\xe9\xd8d\x85O\x8c\xe7\xac\xb5\x0c\x95\xb0s(\xb7d\xe70\xe6\xd4,\xa4\xd7\xa8o\xf6YZ\xa2\xb9w\xc9\x89\xa5Lm\x93\xd0\xab\x1b\x17\x9b\xaaB\x97i\xae\xa46o\xca*\x15\x95\xa3\\\x0b8Um=\xd8\xcd\xa28\x1c\xc4j\x99\x92\x88?\xa9\xa8\xa2\xf1E!q\xc4\xaaE\x8a}n*\xc5\x0fbG(\xac\xb1`\x87EA \x00hx\xd3\x14*\xf1VS.\xf0\xd3\xf2\xc2\x14\xa8Q\x8d\xa6\x87L\xa5\xbf]\xfb\x9e\x18Q\xea\x08\xdd\xfd\x8e\x0c\x90\n\xa8\xc1/\xb7Y\xd6\x84\xe6\xda\xce\xc1J\xd6\x95EN\xce\x9d\xea\xd8\x8c\x7f\xb2\xd0\xec)\xab\xfdO\xc2\xe6N\xd8\x0dm\xf9\xd7kh36\xb0\x19\xc7\xf3.D\xd1^\xbb\xd5\xe3\xfd(\xe1\xdbjy\x14\xd9M\x1b\xd3:\x9a{\xe6a\xc2\xfb0\xcc\x94g\x8bY\x96\xf8\xbd<\xe3m!\x80\xb7\xba\xf6\xdb\xbfN\xb74LlzM\xa7q\x89;\xfe\x87\xd7\x17\x8f]\xfbA:{\xec\xf4\x91\xd7~0s\xe3\xe8\xef\x1f\x1f\xa8d\xc5Ug8\xba\xda\xf5i\x98\x8a\x85\xd1\x88\"\xf0\x94\xae\xf5\xe2\xf2\xf2\xcd\xc5\x9d\x9d\xad\x05v\xbd\x05\x97\xe8\xadj\x86P\x92\xda\x82\xd5\xe6c\xc2C).\x11\xd3(O\\\x8bE\x00\xee\x19\x1a\xfc\x89\xfcBm8s\x06\xee\x0eZ\xd2w\xbc*B\x08\x95;mgE\xd6\xe6\xa4N{\xac\xbb\x94\xach\xabN\xb2\xe7E\xfbaU\xa4\xbbK\x0d\xac\x10\xbbq\x86\x85|\xbf\xb0c\xd6\x08\x8f\xc3l\x14\x88clg}\xd9a\x1c\x0d\x12'\x1e\xf2\xa4\xbeP/\xe1\xce^Z\x0f\x0f\xfcp\xcf\xef\x1f6\x17\xd8\x91\x9b\xbc\xc0Z7{\x81\x13\xeeY\xd2\xa8w\xd4EK;\xb3(\xd0\xae\xcc\x12\x96\xa3\x850w\xff\xafI\x15\x05\xf8\x9fq\x8d\x91\xe3\x8aa\x7fJ\x86\xa6\x01\x04\xb1FN \xd6\xeb\xd9Gx\xd7\x17/m.\xb0\xd6K\xa4|l\xf9\xba\x18J\xccy\xfc\xe7\xb84|\xbf\xf7!\xfd\xae@\x8f\x7fNA\x00\xf8K\nH\x83H>)\xf1\xec\xf1_P\xe0X\x02\xfe\x1b\x02\x90\xb3\xbbGvDz\xa6\xb6\x9e=z\x9f\x02d\x94\xac\xb5\xca(\x85\xf9`,\x02\x90\xe3\xc8\x16?\xb2\x03{\x12\xf8\xd8\x0e\x94\x07\xf2\xd1\x13;P\xf6\xf9\xe8\xa9\x1d\x08\xb3\xf8\x1b;P\xe2\xfc\xa3\x7fm\x07\xca\x85y\xf4?\xda\x81\x12#\x1f\xfd\x1b\nL2\xb9\x02\xbf\xb2A\xc6r\x8e\x0f\x08]\x01\x18L\xe3\xaf(0\x05\xfc\xbfGhE8HEo\x9f\xfc\x84\x02\xee8\x89\xc0\xe7g\xff\xfc?`T\x8c\x06\xd2\xee\xfa)9\xd0\x1a\x80[[\x8c\xe2>\x1c\xf5\x7fO\xaa(\xc8\xcf\xff%\x86\x88S\xf0\xec\xfe=\xf2Y\x10>\x89\x88d\xe9bID\x1fcJ\xe6\x00F\xdf\x7f@\xbe\xfbr\xc1\xee?$\x80(]`\xado\xe3Y\xc4qpxN1#+\xa9s\xe28\x89\x0ej\xc6-@\xfc\xb6u$\x8b\x89\xf4\xac\xb2l\x83\x06|\x80k\xa4.\x10\xcf\x7fI\x0e\xb1\x81\xfco\xa4N\xea\x0f\xe4\xc0\xef\xff\x8cT\x12X\xf0\x07\xe4\xeb\xe1\xa8f\x17\x04DM\xe6\x9f\xe3n2?\xf0$\x8d&L\xd1@\xfe\x07\\'\x17\x02G\xeb\x13\x82Q\xea;!!\xfbn\x14\xfa!\x1c\x14\xcc2\x9d}\x05\xf9\x08S\xf5\x9e\xe3\xee\xb9\x11\xd0\xab\xfb\xefZ\x80Z\xcf\xee\xbdG\xa0\x89\xa4\xbaO1}\xef9\xc9\x98\xcb\xb1<\xc0\xfd\x9du\x92}.1\xfb]\xcc\xbb{\x05\x08\xa3\x1a\x80\x80dS`/\xd9\x13\x80?%\xf3\xee%{\x99\x06\x92%\xab]\xeb\xb3 s\x90\xfd\x81\xcf\x98\xe7\xf6\xbc\xdby$\x97\x1dK\n=\xee:y*W\x0e\x8f\xec\xac\x04q+\xac\xd7\x08\x1b\xc5\xd9\xa1\\\xf4G\x98\x92\xf4\x04~X\x91\x83'a\x94\x8b:oc>qV\x82\x82\xc0Ok\xc0\x99\x9430\xf9\xeb\xa9\xef\xff\x0b\xfd\x0e\xa2\x0c\x1dB\xb6\xcf9\x1co\xd2\x89\x96\xb4\xc8\xbej\x00f6=\x7f\xe0\x02\x05~\x88\x05O\x01\x02\xd1\xf3\xd9/0 \x16\xb0\x1c\xaa\xe1\xc3\xdf\xf3\x07\x91\x17\xc1\xb9\xc4\xb2\x93\x80\xc5\x01l\xe4GX~\x12\xc0\xcc\x1fq\x80ZF\x93\xdeV}~D\xd0\xdd\x1f\xa4\x99#\xb9\xc5_\x90\xa9\xfb\x83,\xf1\xa5,\"\xf4&Q\xe6=rr\x8b2\xd0\xc3{\x98\xd6\xf4\xfcAnF\x8e\xa9W\xcf\x1f\xa83\xfa\xd02)s\xda\x1e\x92\xe5\xd8s\x92h_\x80\xde\xc7\xd4\xa2\x178\xee^\x10\xdd\xe1J\xb8\xfa\x10\xcb,\xb2@z;w\x12 \x7f\x0f\x0b<\x12\xae'%K`5\xa1R\xc2,\x0d\x968*\xa5\x02\xb8\xb5}\xf6\x0b\xb2;\xe5R\x89\xbaT~\xf6\x1e\x96\x02\xa4\xae- \xff\x023\x86^\xb077/\xeb\x90\x03\x12\xec\xcd\x9d\x94\x10BE\x82\xbd\x13\x00\xc1\xc2\xb2LO !\x98\xa1\xf5B\xb1\x18g\x9e\xfd\x183\xda^\xc8o\xe7\xbe$\x07\xf7\xff\xda\x02^\x07\x94~\x8a%\xc0^\x08\x80w\xb1\xbau\xd6\xc8B\xff\x07\xaebd!2nh\xeb\x01\xe9]_i\xdb@\xfb\x99\x0f\xe8E\xe6\x1a\x1d\xf4@J\xf9\xf0>\x05-\xaf \xc8\xcf\x7fa\x81\x04\x12\x82YT/:\xf0\xa0\x0eV4\x04D\xd6\xf9\x19^\x04\xd1\xda\x96\xac\x83%\x11\x01\x91\x07\xd6\xb2\x08\x07\x1e\xd4!\xa8\x10\x1dx\xb2\xce\xcf\x08O\x8f\x0e.\xc8*\x96\x01H2\xfa3r\xf6\xa2\x83\x0b\xcb\xb2\nVo\x05D\xb2\xce\x9fciD4\x06u\xe8.\x1c\x0ce\x9d\x9fa\x92,Z\xdb\x95u\xb0\xbe\" \x92\x95\xfc\x9c\xf0\xfc\xe8`\x08u\xb0\x02$ \xb2\xce\xcf\xc8i\x8e\x0eF~\x08\x04\xea\x01\xa1\xf2\xd1\x81&^\x0f\x08k\x8d\x0e\x0c\xd5}\x80\x15\xb5^t\xb0\x0b{\x8e\x95\x0d\x01\x01<\xc1\x82i/:\xc8\xa1\xce\x7fk\x81\x00\x9e`\xa5S\xb4\x06{\x8e\xb5N\x01\x01<\xf9\xa5\xa55\xa8ci-\x07<\xb1`\xddeY\x85\xd0\x92\xe8@\x9e\xfd\x9f\x11\xca\x16\x1d\\\x06\xd4\xb2\xec\xece\x89[?'\xb49:\x18C\x1dB\x95\xa3\x831\xe0#V\xb6Dk\xb0j\x844F\x07\x97a\xa5\xb1V'Z\x83:XA\x11\x10Xi\x0b\x0e_\x86U\xb3\xec\xf5eXi\x0b\xfa\x8c\xa1\x8e\x05y\xc6\xb0\xd2\x04\x0b\xeae\xe8\xb3\xca\x98\xf6k\xb2o\xf5\x80qO\xb2\xf7\x8f\xf1a=\x0bZ\x10\x95\xb7zF=\xfa\xdf \x84\x8f\x84p\xf7\xec\xad?#\x90:\xc9>Us!R}/\x8d\xc4:\xff\xe0\x07\x96\xefR\x85\xff\x90\xc8#i\x14\x0c\xd3\\\x02\x7fEHv\x1e\xc8m{\x93lu\x1e@j1\x1bH)o\x7fj\x01HM\xf9 \xb6L\x08\x08\xe8\xcax \xce\xe6F\xdf\xb35\xa7@\xb8\xd6\x92\xb6E~\x8a%3\xd7@~J\xea\x80\xfc\x88\x89\xbc\x12G\xefar\xe9:\xb16ta\xf9\xcbu\xe2^\xa2d\xc3\xc7\x98\xd5\xb9N\xac\x9a|\x8c\xf5\x7f\x01R\xb5\xf0\xe8\\'VB\xecc\xcc9\x96\x9c\xd8\xcf\x9c`\xd9\xef\xf7y\xc2\xc3\xccw\x02\xc9\x14~\x82w\xdaubPY\x1e\xff\xe7\x7f\x8f\x1bq\x9d\x04\xb6\xf3-,1\xbaN\"\x15\xd3_\xd3\x05;\x0c\xf8!h\x17X\nqu_\x8f1\x82.\xe9\xf6>\xc5<\xd35\x10Z\x87{\xbe\xd4\xc7\xc9\xb2\x18\x08\xe6YKJW\xf8\x14\xa3\xb4\xab\x01xc\x96J\xaa=V\xc0\\7W\xf3\xa1\xa3\xce\xe34\x95\xc7\xf41f\xf6K\xb0e\x9fb\xb3\x8b\xab\xbe\x93\xfdW\x93\xf9\x18\xcb\xa9K\x02\x1086\x90[R\x1b\xb1\xce\xe6J\x7f\x86\xd6\xc7\xf8\x84.\xf10\xe3\xc9\xb2\x1c\xc4\xc7\x98\x1c\xb9\x12\xe8\xd9\x81K\xfd\xc4\xbe\xdfZ\x9f\xc3D|\xe9\x02\xa8\xd6x{\xdc\xa1\xfc\xfe\x0fdC\x87\x1c$\xe5\xbf\xc4b\x98\x84\x8c\x9c\xc4\x0e]\x1a\n\x12\xfa9\xedF\xaa\xcd\xa4\x17\xb0\xe4\xfd\x82l\x00\xa0\xc6\xaf \xd5\xf0\x13W\x91\x1a,\x9f\nP\xc0\x9d$\x89\xf6\xb56\xf2\xce\xffY_\xc6\xe8\"\xef\xfc_\xd6B\x1eX\xc4\x9e=\xc0\xb2\x8a\x02k\x0d\xf8\x01\x96K\x14\xdcS\x06\x9d\x07X>Z\x92\xf0e%\xd0c\xd9E\xd5\x16L\xf5cL\x9c\x15l[T\xfcs|\x9a\xa0\xd9KF\xd2\xc3B:\xc07\xb5\xb0\x87%u\x00\xef\x18y\xcf\xb2\xba\x92c|\x88\xb5z\xd7\x07=\xd3\xb6\x1f}}\x8c?\xc2\x07\xd2\xf5\x93\x11\xd8^\x9fb\x0b\x82\xeb'\xa9B\x8b\x0f\xb1\xcc\xb5$\xd4\xb7}?\xe5KQ\x98Ey\xb2\x1af|\x908\x923\xde\xc3\x87n)\x88R\xbe\x94'\xc1\xe1r\x94\xf7\x02\xfez\x1ee w\x90-1%\x8b2dc\x82\xbc'\x97\xe6\x97X\x0c\x93\x90\xdc\xcf\xac\xc0\xa5\x08\xac\x89\xcf\xee\x91\xe3\xad \x0b\xb6\x1ap\x03\x83Ey\xd7\x80\x88\xfd\x16@\xb7k`\xa3\x91 Y]\xdbw1\xec\xff\x8a\x02\x80\xd5\x12\x16\x14\x8d\xe2>L\x07Kb\xae|\x19a\xc4\x15\xdd\xb6\xd5\x0c\xf8\x01`\xd7\xdbx_\x8d\x99\x90p\xca(\x1chv\x8bI\xddR\x14\x0e\x92\\ux\x1f\x0b\xbaK\x05\x0f!\x18V\x80\xf0\x11\xb3\xe1\x15-#\xb5t\xdb,\xb4\xfaNw N\"\xb8\xd6\"\xacI\x82r7\xb3C76\xaf\nR@d\x9e(>\xac\xfb\x9e\x02g\xc0\xe7q)\xca\x05?i%\xa2e\xa6\x90\xec!\x99M\xee9I\"W\xe7}26 \x93\xeb\xf3>^\x1f7\xe7\xb1\x84<$s\xcdy*9\xc7C\xacM\xb9y\xa0\x97\x1b\xdbv\x01$\xa7\xf5>\xd6A\x96\x94\xbd\x95\xf0i\xf8~\x0f\xab\x9an.\x84b%\xf9\x126\x92\xc7J\xfe&\xd7:nn\xe4e\xc2\x96s#/\x13\x11+\xd7\xf2\xf2\x03K\x83\x11\\\xe4\x91c\xaf\x84\xbc{O,\x02rn\x90\x92\x90T \x92\"\xe0\xfbX\x8dv\x05y\xe7\xb7\xe3\x84\xbb5\xdb\"\xe1i\xee\xd6mN\x12\x1cjc.\xd6\x80$\xb00\xe7\x12\\\xcd\x93D\x1a\xe6?\xc6J\xb7\x9b'c$\xb3\xd0\xad\xd7E\n\x91\x85N\xbc~d\xea\xba\x87\x0e\xaa|\x83F\x04V}\x83v\x0f_\xc5\xb8\x87\x81\x9b \xda\xf3\xec]L\x90\x97e\xaep\x01z\x13Sc\xaf\x00a\xc1\xd4s\x02}\xa3\x81\x0f\xd8\xb2\xdeh\xd2\xdc\"\x00~\x8aq\xde\xd35(\x00\xc4\xb171QXv\xd2!\\\xb0\xe1\xbd\xf14\xe4\x01f\xea^\xc9>\x8f\x97\xd5\xeb\x05\xd2\xd3\xe0\xd7X\xc8X6Z\x15\xde#\xcf@pc\xcb \xb3cv\xe2\xc1g,\x1e,\xdb\xb5M\xf0\xf5\xf8 >\xb3\x9e\xd7\xb0]z\x1d\x7f\x8a\x8f\xf3\xf2r\x94%\x0e\x984\xdf\xc7\x94\xd7\xf3\xa2,\x05!\xe41FQ\x8f\x0b\x0e\xff1\xd6\xe7\x969p\x1e\xac\x18,\xf3\x00\xae\xbf\xc8\xdc5\x00\xcf\xde+\xe9_\x18i\xbd\xbe\x9f\xc2\xd1\xf9\x00\xbb\xe0,k\x85 \x8f\xc0\xd3\x00\xb28\x17\xe0B\xe9\x03l\xeb\xf5\x86\x0ep\x8a\x9fb!Y@`=\xb1\xcc\xb0\xec;n\xe2g\xbe\xeb\x04\x8bun[\xa52\xa06\xfc\x1a\x0b\xa7\x95\x12B\xd6\xd5mQ,,J\x9eW\x9eT?\xac/\xb2\xa3\xae\xeb\x7f\x8d\x8dx\x9e\xefH2\xfb\x10[\\\x96}g\x14\x815\x86\xc0\xbc\xc90#Gcs\x9e\x80\xa75\x10\xb9h\xd8 N\xad0\xe4\x00\xf8\x03\x07\x04\xe3\xdf\xe0U\xf2\xfc\xd4\x97b\xeeCL\x18=y\x13\xf4 \xc1n\x7f\xec\x83c\x83\x1d\x12\x85\xc6\x94\xfe\x90 \x9a?\x8e\xc2\x03+h\xf9\"\x9ct\x8c5\xde-P\xda\xb1\x1c\xe3\x05n\x94\xc8\x81\xbf\x8b\xf9\x9b\x17\xb8\x89|b\xe0\xd9\xbb\x98\x0f{Q\x10H\x94\xfe}\xdc\xbd\xb9\xa9\xc2:\xb2gD]\xacH*c\x06\xde\x0e\xaf\x06q\xa3Li\xc2?&(\x16eJ\x9f\xc1$[B\x94Pq\x1f\xd3\xa0\xe5([\xb9\x9d\x83>8+:f\x01S\x0c\xae\x01\xd8Z\xc1\xb5\x9d\xf4\xd9}\x8c\x1f+\xb0hX\x0d\xe5\xb0fX\xca\xe1\xcbJ\xd2 \xaa\xc9\x8a\xba\x05\xc2\x83\xd5Fz\"cpU\x01\x1fR8\x9f?\xc1R\x1c\xef\xeb\x860cZ\xd1:\x066\xc3p\x0d\xc07FR\x8bz\xf6\x04o\xc5\x8a \x8b -\x19\x08fy| \x89\xf7\x132\xedA\xaa\x8e\xca\x13l\xe4\x05e\xed \x96\xe2VJ\x86_\xd2\x7f\xe0\x87\x19OdW\x7f\x86 \x13\x87K\xed\xb71\x93\xe2\x01\x0c\x0d\xef8\x0f\xcc\xd0\xf0\xda\xaf\xe8\xe8\x0b\xbc\xc6\\\x03H'B_\x94c\xc6\x04IBR\xb8\x86%@\x99ky{\xe4\x04\xc1\xb6\x91\x08\x7f\x81\xe5\xe3B\x17\xb5\xd7\xbf\xcc\x13\xdc\xc6{\xd8Y\x84\x8fRI{\xdf\xc4\x9cS\x00\xe6NH\x10V\xa3$H\xba\xbe\xbdI\xfa]?\xbf\xc0Z\x9f\x91\x83'-\xef\x9f\xe1\x0b8\x1e\xaa\xce1G^\xd1.\xfe\x0474\x80`\x87\xd1\"\xb0M\x8e\x1b-\x82\xe0`\x0cT\xf4!\xc1\x80\xd8IR\xe0\n\xd8*\xc3\xb5\xf4\xfe\x18Sx\xe5\xb4\xfb9&\xd6+\xc6\xd9\xfbs\xda\x8f\x01\xe1Z\x02$\xb6\xf67\x04p[_\n\x12\xba\xc7o\xd7\x931~[y\x97\xdc\xc7k\xcdo\xa7\x81\x13f\x83,\xb1\x1fT\x00\x07<\xb5\x9f\x16\xa3\x07=\xa6#\xcd\x1dy\xc4\xce\xd8\xaah\xad\xdf6\xa0\x9c\xc3\xb5\xe8}\xcc\x92Vn\xe7~\xe0\xf7\x12?\x97s\xf9)\x16\x18JN\x946\x08\xd8\xae\x1ec\xa5\x81\xdf\x1e\x17\x1b\x8e\xa5h\xaeY\xe0\x07d\xc3\x13Mq\xf1\xa1_\xd1nA\xd8\x10\xc55\x00\xf3m\xaeI\x0e\xd1&W\xd4\xbe=\xc6\xd7&\xbcnCW\xc0tE\xf8\x06|&|i\xe7\x82\xa0\xdb\xb8[\xb0\x96~\x82'\xb0\xa2\"%\xc8IV\xdf y\xc9\x13\xe9R\xff'\xd8A\x8a\x1f\xb8\xa2\xc2\x11\xf2\xd9\x87\xad\xbf\x87\xe9\xd1\x8a\x80\xa4V\x10?\x88\xb9\x9b9:^\x86\xac\xfa\xca\x01${\xf0\x9d@^/S\xdeY\x14\xb03\xd7\xbe\x13\x04\xbe\xbc$T\x96G\xc2d\xcf\x81\x98\x80\xa5\xe6>\x88 \x98\x82\xf6\xf9Hu\xf5K|\xf3\xd0\xef\xfb\x10\xf8\xf8\x9f\xff\x06\xcf\xb3\xdf\xd7\x10Z)\xd0 \xdc\xd59\xcd\xe4\xb1\x9c\xd6\xd7\x00L\xe2\x8a\x01`5\xe2\x9c\x1f\x04\xdc\xc3l \x13\\(ec>X\xec\xea\xdf\x82\x9e\xfa\xb70 p\xc0B\x87\xc5\xaeb\x9e\x18\xeb\xfbA\x16J\xf4x\x0f\x9f\xd3~\x18 \x06\xf0\x9f\xc8\x96\x19\x96\x81\xf5\xb3\xbea\x19\xf8\x10\x9d\x8b\x92E\x10'\xee\x91=\x88\x12\xa7\x1e$\xfdX\x1eb\xc3\x87\x00\xc0\xbd\x00\xe6g\xe7\xa2<\xf1y\x92%p\x0bL\xe6\x14;I\xa6\xfd\x1e\xb0\x10\xdaO\x1cW\xba\xb3\x7fL&& \x92\xa9\xff\x04\xd3, \x12L\xfdc\xbc\x9f\x12rJV\xc2\xc4_\x82^\x96 <\x01 zE\x82\xb0\xe0.@\xf30\n\xb2 \x02\x04}aF$@\xd2\xe1\xfec\xac(I\x08T\xc2\xfb%A0\nl\xfa\x13\xa0\x93P\x0bK\x19\x02t\n\xa6\x85e` \x82\x06\xb1=W\x80\xbe\x03 l\x13\xe8'\x0e\xb0\x97\xb7\x08%HT\xe8\xc3\xbbX\x08?\xa7y\x05\xd9{\xa3\xfbb\x81p\xa0U\xaf\xff\x07\xf3\xe2\xf3\xca\x08\xfd9\xdevm\x9d\xfe\x1c\xb3\x17Y\xc3\x13\x12\x08^\xb8\x81\x81\xe0\x15\x18\xc0\xcd\xed\x13l\x970\xa2\xc9\x13L\xd6\x00$\xf9\xfb\x13L\x8e\x15\x0c\xe6\x8a\x91~\xc0S5Yz\xf3.`0\xc8'\x988\x9c\xd7\x1c\x0b\xab\x17\x03\x0d\xc0\xec\xf7\xbcTd\x1fb\xda4\x00? ,\xac\x0c\x065\xc5\xfd\x11l\xce\xdbXx:\xaf\xaeN0\xa7\x1e\xa8\xab\x13\x82qpc\x80\x9b\x19Hg\xcfgO\xc8\x1e\x83\xbc\xf2\x04s\xaeApK~\xc7\xd3\x1d\x84\xea\x00\x92\x05\n\x8b\x98a\x0b\x10\x10\x98\xec\xc5\x9ckud]\x96U}\xaf\x82\xcf\xb4\xaf\x01X\xc6\xf0G\x0eh^\xb6\xb6\x06~\xe8$\x87\xab\xf6\xd5\x199\x83@\x9d\xe8\xb71j\x0b`\xec@\xca$\xbaw#\x99\xc5\xb4\xf5)\xd6\xd4\xfd\x91\xb4<={\x80Y\xb8?\x8a\xa5\xc3\xec\x7f\xc2\xf8\xb4:\x8a\x03\x1f\xd4\x1f\xe2`\xe2\x87l\xc1v\xf9\xe5\x87\xae2\xb0\xbd\x8d\xafc\xcc\xde\xdd\xc3\x8a\xb7\x84\xa8\xd0\xfd\x0f\xb1\xbe\xec\x87*\x87\x06\x99\xd1\xaa\xc2\x12\x82q\xea;\xd9\x8d0s\x81\xc6<\xc0B\x9c\xca\x08\x0d\xb1\x1a\x98\x81V\x9c\x97,\x8d\xf2\xa4\xae\xd9Uy\x11\xc8M\xf6$\x92X\xc4\x0f\xb3\xc0I\x86\xd2 \xf7\x11\x16\xda\xfc0\xd3A\x14\x1fa!q5\x1c\xfb\xa9/\x1d\xac\xc0fb![\xba\x88\x89qz\x0bK\xe5\xab\x1b@I\xb0m\xd5\x8f@\xf4!X\xabo\xbc0\xc1\xf35\x00\xdf%\xac\x1a\xae\x86\xf9\x92o \xd8\xac\xb5\n'\xf9s\xcc\x07\xd5 \xff\x1c\x0b\x16~\xed*\xf9Z\xca\xfe\x18\xb3\xf9U\xcd\x15\xc9\xe12\\\x11k?\xdaC\x92\xe2|\xea\x87Z\xf0&49\xf5A\xc8}HF\x9d\xfa`#~\x88\xbd_%DZb\x1fb\xca$@c\xfb 2\xfb\x0e\xeb\xfcS\x9f\xe2\xcbp\xdf@\x08\xc1\xcc\xf7\x00-\xb0\xee\xe1+\xc0?`s\xe8\xaa\xbaq\xc1\xac\xdbW\xdf1V\\\xd4\")\x9e\xfa-\x0d\xc0\xeb\xa8l\x1b\x18%\xc0\xb4\xf1\xf7xm/j\x06\x86y\xff-\x0d\xc02\xca-E6\xff_L\x1d/\x1a4\xc5\x87\xe4\x96\x81`}\xea\xa2\xc1!,\x94\xde2\x10\x8c\x90\x17S\x9e\xc0d\xf0\xce\xde\xd2\x90\x7f\xc0\xf2\xc4E\xbdQ\xd8\xa6uKo\x14\xe6\xf8\xdfw\xe2X\x9e!|\xe6\xf64\x00\x930 \x90\x97\xbfX<\xf9\xbe1\x8abo\xa5=\x03\xc1\xab\xf9}\x18/\xe9\x1d>\xe3\xbe\xbf\xafw\x0b\x0b^{\x1a\x80\x91zo\x90@B\xa8O\xb1\x90\xf5}\x15\x0d\x8cwdOE\x03cn\xf5}\x85qX8\xd9S\xd64,\x7f|\xdf`\x03\xa6\xf1{\x06B\xea\x18l\xc0\x82\xd6\x9e\x86\xfc9&\x9b\xc1\xa2\xd6\\\xf0\"\xae\x99\xfc\x02\xf88\x04\x06\x82W8pJ1\x04\xf80\x06\xce q\xe0\x16\x13\xb3\xff5g\xd4\xf3$\xbe`\xdc\x0f\x0c\x04\xabOk*k\xe6\xaf\xb0\xf8\x14h\x00\xdeM\x01\x80\xfc\x8e\x98\x11\x05\xc6\xb3\xccR \xcc\x8exC\xd7\x1c\xf9\xe2\x9a\xbe\xc4\xc23\n\x1cH\xb8\xf61f\xf0kZ\xab\xc7RK\xa0\xed\x00\x98\x85\x98\x986\x1b@\xc6\xf6\xfd\x14\x8b\x18\x12\xd2\x97\xec\xe0}|\xf9 `\n\x84e#\x01\x02\xe1\x81\xa8\xa2\x02\x14\xc8\x95x\x07\xcfH\x06\xd6I\x81\xe5}\x8a)\x89\xb6\xe7|\x80y\x8f\x80e\xb2\xda;\x98\xcb\xa8\x1b\xd2'\xa4\xa7\xc5\xcc\xf1\xa1'\x8a'\x06\x84\x89z\xe0@D\xf2\x13,\xfe\x0b\x00\x98\xa8\xfe5\xb5\x18\x05g\xd5\xb2\xbf\x8f\xa9E\xd0\xd3\x10|\x98\x03\x9d\xe4\xef\xaf\xb0n\x10\xf4\x12\xb0:\xfc\x91\x0d \xea\\\xa7\x80=9\xecGX\xd1\x16\x904\x00D\xc6\x1c\x12`2\x8f\xd1#\xcc\xac\xd6\x8c\xb7!V\xd0\x03\x03\xc1B\xca\x9a!\xbd\xf8\xf8\x05\x06\x82\xa5\xa4\xc0\xe5\xb0\x13\xefb\xd6\x13\xb82\x16\x15\xaf\xc1\x1a\x90F\xb2\xa5\xf0\x99t\xec\xb9R@}\x1f\xb3\x89\xc0\xe48\xc4\x84QB\xc0\xe2AN\x9d\x97x\xda\xe1\x143\xf1\xc0K\xf2T\x03\xc9.x`\xd2x\x87l5\x18!1 \x06\xf2r\x1f\x9fT\xe9\xf2/\x88\xcfY\x81\x07\xe01GhP%.\x80\x90\x81\xb5\xb2\x0d\x89R\x8f\x8a\x85\xc9V\xb7\xec\xedN(\x89)\x80\"\x04\xb0,g\xba\xd1\xc7\x90\x1cj\xd1\xd2\x12\xf7\x03H\xc7J\x91C\xc0\xc1\xf9\xbf\xbc\x14x\x19\xa1\x94t\xd7.\xf9\x8dc\x0b\x85.Ur\x1b\xc7\xb6\x9ej\x11\xed5\x8ei\x87(u.\x88\xa0\x8dw\xb1\xe9VLZy\xe0\xeb,\x7f\xc4\x1f\xbeT\x06\x02|\xdf!\xe7\x85\xf73\xb3|\xa0\x1ec+5\x0d\xf8 FaQ\xa4j+$\xf6\x99\x80\x14!\xadT\x8b\xa4\xb5[-\xcb\xa8iA)r>t\xa9\xf4v\xee\x0f\x8a\x1e1\x11\xb6\x05'`\x8a[\x8a\x9e!\xa1\xa4\nV,\x8c\x0d\x83\xab\xd8\x82%\x1d1\xd4l\x98p^\x84\x98\xe1\xd9\xc8FJ)\x1f\x1f\xe0S_.\xa0\x90\xe9CL\x9c\xcbe\x8c}\xf2\x01\x16\x93D)\x08\x92)\x0d\x19\x0b,P\xa8:-|\xa7\x0feJ\xa1\x1aXG(\x17\xd0\x07\x00\xeb\x04(\xda\x03\xe3.\x8d\xf4 \x82\xd0\n8\\S\xfc\x80\x0bi\xba\x19p\xc1CD\x1a}\xf3C k\xc9'\x80\x9e\xbe\xb4\xee\xbb\xba\x99#\xf2\x9e\xf1 x\x8c\xd7+(\xf9\x04`\xedM\xc1\xe4\x1a<\xc1\xb4&\xe0\xa9\x9a\xacE\xce\xe0\xa9r\\x\x82o\xd4\x03\x9e\xa6\xa5\xab;,\x81\n\xb0\xb6\x13`\x0dZ\xc0\xf8m\xe5\xf7jYc\x01\xd5`\xb25kO\xaa*\x14\xa1U\xa2\x08\x12\xb0 \xe1\x8a\xeeHrA\x94\x80\"\x95\xb8\x0d&\xcdC$\xc7x\x00k\xd9\xb6|\x06\xd7\x92GD\x18\xd0~:T\x1eOJ\x04\x92X{\x12\xa5\xc0R\x01=1\xb4\x91\xec\x00\xa4\x00z\x93X>\x12E3\x1f\x10\xca\x98:Z\xf9\xc6\xf8\xb9\xa6\xafF\x88dh\x8c\x92X\x98ZS\xaa5\xa1\x95\xb5\xdfk\xa4\x81\xc08}ac\x88\x80\x80`J8vz\xbbg\xb3\xc7\xa4z\x82\x041Rc] B\x92vb\xf8\x8c\xc8\x8b\x06\x82\xed\xbbk;\x0b\xac\xf5]\xfcQ\"\x05\xe5\x9a\x99\xa5l\xa0\x9d\xce\x08\xdd6Ng\x84\x86d\xb5\x82\xa4T\x8c\x16l:QP\xa8K\x84=e\x9a\x9d\x7f@hQ\xc9U\x8d\x98v4K&t$K\xe0:\x97hK\x81\x0e1&\x89\xf3\x83,\xd1\xeerdRy\xe2\x19\xc3\x0e9\xb3ybB\x90\xc9\nV|\xd0>\xb2H\xf3\xda\x07\xcd\x02S\xb7\xfa\x1f\xe3\xdb+\x13.\x83g0r\x80\x16\xfc%\xd6\xec\x04\x80\xc3\xe3\x1b\x04v \xc4\x89\xf71\x91\x1e\xc1\xf7w\xf0\x94\n\xfeT\x032\x96\x0dl\x1e\x03\xb0a)Xa\x03\xb0\xb2y\xe0k\x92\x91\x93\xec\x01\xc5z\x0f\xdf\xfd\x8et\xb6\xc5g\x1fa\x99\xf9\x12H\xa0\xd8\xbc7\x82\xcf\x98\xbd\x8eL\xca*l\xe5\x18\xe9H\xe6{\x98\xb1\x8f\xb8\x93\xe6 \xf7\x8a\x07\xb6\xb0\xf2q\x89{~>2Ndoa\x82{\x89\x07\x81\x1f\xeak\x01l\xf4\xbe\xa4\xd5\x01l\x88\x1bi\x00>\xe2\xa3\xa1\xdc\x9c\xb7\xc9\xea\xfb\xae\x0c?\xfb\x18K:*-\xe8=l(\x19\xf9\x9e\xfd\x8d\xa2\x91\xef)\xba\xf0\x14\x13\xd6\x91\xef\xd5\xa4\xcf-\xb2\xc0`\xb2.!\xf0\xc6\x16^\x1b \x82\xd1a \x0e@R]\xf9\x08/\x81\xcc\xc9\xaa\x13\xaf\xde\xc3\x8cq\x14\xb8\x90\xad\x10\xdb\x8fG\x01\xb3\xb4g\x1e\x1a\xa3\xb0\x0c\x1e9\xf8%\xa6M\x12\x02f\x85:\x18\xf8\xfc`\x1f\xbb\xb0'\x9d\x8c?\xc6\xd4:,R\xcc\xd3\xb1\x97r\xc9S\xa0\xce$\x89\x97}]\xdf\xe5|\x86\xb7*4\x10lz_\xd7w9\x9fa\xae\x11\x1a\x08\x96:C\x93r\x96\xf6S\xce9k\x19\xb9Jt\x89Q|\x1d\xc88\xd6\x14B\xf8\x8c\x15\xca\xd0Pw|\xbaT\x82_\xb2\xd4\\{F\xbd\x8fYU\xc8\xf5\xdd+V*D% y\xc7\nQ\xaa\x02\x85\x99\x88g2\xfdu>p2\x7f\xcc\x11\x1fy\x13KW\xba\xdc\xce\xd0w\xf7\xa6*\x16N.u\x99'\x87\xcd%Ko\xf5`KS\xc8S\xaer\"a[AX\x04l[&\x9cf\xdc\xa3A%$\x82\x02\n\x96-\x7fD\xde]\xe7\xfb\xca1\xf9\x07!\x19\x82 \xaf&\xf4\x86\x17\xf1\xd5\x18\xb6\xae\xf9.6\xb8\x85\x1a\x80\x87\x19\xea\x988\x8a\xd9*,\x0e;\x16\x86:\xce\xcd\x06\xb8]\xdfX9\xd6\xcd\x06O\xeb@:4\xccRI\xef\x13\x96\x1aB\x1d\xd6b!\xc9\x03\x00a\xb95\xd4\xc6[\x028\x9f\x01\x06=\xa5\x030\xd1\x0eX\xb7\x0cM\xb8\x03!\xacCexx\x8a\xd5\xbbPj\x0b\xf7\x08\x0e\xc3Cq\x0f1\xf3\x0b}\x10>\x1eb\xa9/\x04\x8c'\x0d\xad+\x93'V\x11Be\xf2\xc4\xea^h|8\xb0\xba\x19\x1a'\x0eZGI)XD\x0e\xf5E2]Du\x97\x8c\xa5\xb5\xb0z\x13L\xc7P\xb9\n&\x03\xb1\xdc \x92M\xb2\\!\x92\xed\xd278dx\xc5\x15\x8emJ\xe5[\x1c\x1b\x19jM\xdbr\x0e@\x1b\xa3\xddh\xb5\xf5!&W\xa1\xd1[\x1fbkZ\xb8\xa6\xce\xc8\x13:8-\xc1c6\xb5\x1e\x9dM\xb8#Y\xd8[\x98\xbb\xadG\xa1\x04\xfa\xe1@\x13w\"l\xac\xebX\x11\"\x9d\x18\x01\x16K\xec\xfam62|\xd0\n\xf0\xe7\xf5(\xab&\x95\xc7\x86\xc9_\x01.\x06\x81)\x7fQ\x06\xc5b\xda\x86b\xe3\x9d\x0d\xe5\x0c\xf7\xc4V\x9e\xa2\x08\x0e\xcclh\xadX&\xcc2\xd6\xa3\x8c\x86\xe2\xd8ZB\xf18\x14\xe1\xa3L\xb9B\x13I\\@\x8c/\xb4\xbd\xa2r\x87\xb6\x03\xc7N}\xbb\xf0\x10\xf4C\xac\xd9\x02\x0cr\x98c\xe3\xd5z\x94aO\x00r\xe8Q\x19\xe3\x0c`[\x19\xabG\x00\xa1\x15\xb2`\x0d\x8dS\xb0by1\xd5U\x05\xca\xc8c\x1dHY\xea\xb2\x0f\x95^\xac\xd6\x95+p\x06\x93\xd7\xf5(\xab\x93\x07\x9f\xfc+[sT(|\xf2\xd7\xb6\xadV\xa2\x00\xf6\xc8\x93\x10\x85\x04v\x18 \x01\xd6\xa9\x01\x06H\x805\x8f\xf5(\xdbL\xb8\xcb=\xf5\xd2\x0b\xb6\xf3\x95\xe0f\xad\x9e\xfc\x1b\xdb\xe4t\xb1\xea\xba>\xb4P\xac->\xe6I\xca\xcbD\x0fOG\x94\x92\x195\xcb\xc8IdlTHc\xa7EOA%\x8b\xe1Y\xa86\xe4\xc1\xd9\xce{*\xe7\xdb\x03+\xb6\x97K\x15\xcdYX\x84.\x18\x8b9C\x83\xd6\x01V\xcb\x15Mb\xd3\x97(Z\x8c\xedO(k7\x05\n\xb7\x1c\xa2#\x8b\"\xae\xcb\xb9\x07\xbb\x8e\x0d\xfa%x\xb1\xeb\xd4XQ*\x86v\x1d\x1b\x1aK%\x8b\xf3\xf4\x1f\xed\x0d\x96\x16\xea\xc75\xb3Ck\xf4\xc0\xc23\x8bn,\x93\x93\xc0\x82\xccXx\xa2,Qeg\xc4Z\xa4J\x15=Y\x86\x81\x99?\xd1\xd6\xe3\x1a\xa9@\x00\x9c P \xf1mPH\xcd\xf1\xf4o\xe9+\xb4\xa1\x8e\x80\xbbG\xa5\x810\x8e\x02\x1d\\\x88M\xc9!?}\xc7Z &Id\xcc4\x8f\x1b\x88\xb2\x02\xabI\xd6T\xd6\x93\xb4\xf4\x9b\xa9|;D\xc8\xd7qx\x9f\x10\x8b\x96\x81\x10;T\xa6\xbc\xd1h/\xe8yr\xaa\xe2\x96K\xc0d\xa8\xaeK\x9e/\xa7\x07\xbfRD\xb5C\x04\x0dy\xa5A\xec\xc3\xf2+1\x0f\xcb,\x9a\xbfG\xbfrH\xda\xf86\xbe\x13\x0es\x9d-\x96\xd8\xb3\xc7\xfa='\xcb.^^\xd6\xcf\x14\x12+\xd8e\xf3\x82!\xb1\x18\x8cM-B\xe6\xc6\xa6\x16Y\xc6\xb1N\xbbe\x19\xc7\x18\xf2\xcf\xd8 \x17t\xb8\n9\xbc\xe3\"\xfe\x1d\xdf\\\x85cm\xcbz\x1f\xdb\xe9\xc3\xb1\x8ee\xb0\xf5\x06. v\x88\xb9\xc4\xb7\x815\x0b{\x9f\xd0\xdd\xb1\xe1\n\x0f\xfe\x9d\xad\xa6~[\xf8?X\x80\xfb\xc6\xe8Oh\xda\xbe\xe6\x99\x04\x15\xf65\xcf\xb4B\x14W\xa3\xb0P\x9b\xc7\xf1\xd5\xe1\x86I\x11\x81\xef*\"\x03\xc1W\x81Q\xdd\xf3\x99\x91\xba\xac%\xeffn\xe8\xf4\x11XF\x894\x00kc*\\\x1b\xef=Dk\xff=\xd6\x89\xa2\xda\x1797\xf4\x9bM\x9f\xe1k\xed\xc8@05\x8a\xe0!\x98g\x1fa\x9a\x13\xe9\xd7\xce\xb0\x93V\xe4\xa5\x91\n{\xc2\x96\xdd\x8d\x15H\xbd\xf0\x19\xde\xff\x88+\x00Y\xf8\xbeZ\xc6G\xd8\x95iC\x1b\xfeI[\x1a\x80\x0f\xa6\nV\xff5\xde\xa9\x0d\x93\xc4\x824e \xd8\xa4\x1d\x81\xb1\xfdC\xcc\xba\"\x9d\xa8\xe7\x116\xc3DC\x81\xfd\x9fc9&\xaa{\xa112\xa6hl\x06\x8f\x02\xbd&d\xeb\x03\xf3(\xe1#\xec\xb4\x13\xe9\xc4\x12o\xd2Z0\x17,\xcbn(O\x98\xcf\xb0\n\x1bi\x006]o\x8c\xf8\xc0\xb1\xceR\x01~\x83\x19\xe8\x86\xf4\x8f\x90\xe9\xa7\xb1M3*@x\xef#%R=\xc2\x86\x9fhT\xfb.\xec\x861\x9e\xe2+\xd2\xc8@\xb0\n`\\)\xb1\xf1i#\xe6\xa1\xf5\xc5U|\xbdo\n\x16E\xb0_Z\x14sx\xf0\xf0\x11\x96\x11\x8c\xef%y\xc5vC\x0e\xeb1\xa1 N\xe2k\xbf\xc8(\x17\x04)\xc0\xb3\xf01\xa6\x14Q\xe2\x81\xb5\xe7mL\x8b$\x04R\x8a\xd8`2\x13\x17\x16>\xa2\xc4\x13\xb8\xff1A\xe4\xc4\x1f\xa8\xec$d#\x13\xf5b\"\xde\xc6(I\x83\x08D\xb9\xc7\xf8>7J$\xa9zLH\xb1\xfd%\xe1\x0d\xa3\\\x90\x01k\xc7\x0fB\x89u\x8a\xa4O\xc8.\x1a\x08!\x94\xeau\x8f\x07\xb8\xca\x86\x11\xf4\xf0\xf6F\x06\x82\xa9\xc8F\xe1s\x8bq\xb2p\xc7%\x8f\x1a\x03\xc8\x81zx\xa97T\xb6\x06\xb2\xd2\xea;\xd9\x9a\xb1\"q\xefbanc\xccu|\x11!2\x12\xa6\x82k\x9f\xfd\x19fe\x1a\xaa\xc2 \xff\x94\xac\xfb\x98'\x9bN\xc2\xc3l\xc8S\xb86\xfc3|\xd4\xb42\x85M\x06B\xd7\x13\xd8\x87\xe7Q\xd1\x01-\x95\x94\xb8\xf2\x14s\xfc\x92}\x82B\x94m\x02\x016\x9d\xc4<\xcfF\x81\xc0\xc61\xf9\x8b\xe13&}1O\\\xc91\xfe\x19\x05\xf82\x1f\xca\x0c\x05\x8c \xd6\xf3Mlt\xd6\x94\xe7\x01\x99>O2\x1eJ\x81\xecM\xac\x85lj\xfe\x8ayu\xac\x01XX\xde\x84\xa7\xd2\xb1\x96\x1b\xc3S\xe9\x98\x1c\xc7Cxu\x00\x1f\x8ax\xa8^q\xa6\xfeX\xf1P=\x17\xfd\x17\xf8&tS\xf6\x8c\xe9z,;\xc6\xfc.\xf63wX\x9b';\x86Q\xe1S\x12\x07N\x08\xef\xc7\x93\xa4i\x00\x82\x84jx\\\x02\x06i\xb7-\xd5$\xd1?j\xf9\xec(\xc6\xff\x11\x16\x92\x05\x104\x7f|\xb2\x04D\xd7\xc2\xa6\x04\x01\xf3\xa4\x9aE\xde\x81\x93 p\xf3#\xb8\x11\xe4\xe0\xd3\xfa\x18\x0bE\x9bA\x9e\xea\x87\xd9?\xc6h#\xaa\x8d\xc2:\x88:l\x1f\x11\x1c \xf24\xdb\x97c\xfc\x08\x8b\xeb\xf1\xc8\xd6\xdaf\x04\xc9\xa8\xc4\n\xcba\x92\xcc\x83\xb1\x90\xb9\xb4\xa1\x10c\xd9\xa6\xbe|\xc5bml\xa4\x04l\xbf\x8a\xa3\\>\xf6\xf81\xde\x95M\xb9\xecO0\xd3\x05S\xe4}\xcc\x0d\xe3DE\x18a\xc2nL\x94\xf7\xb1<\x1d\xc3[\xf5O\xc8y\xd0\x96K\xfa\xdd\xad\xe9\x9b\xbb\xa50&:\x02\xee\xaaw\x83\xad\xe3(\xdf\xb3\x90\xb6-\x97,5%\xaa\x96\xf6\xda^\n\xab4f2e\xe3\xab\x05T\x8e\xd4\xc2\xb2\x96\x84+;\xce\x13\xccu%P\x87Ya\xe9J\x00\xb5\xc5\x10\x0fh3Q\x16\xc37\xe9\x16i\x08>E\x12\x92\xdaq0\xd1Qht\xf8p\xc1j\x19z\xc3\xc0\xd5S\xed\x98\x02m\x96\x1ej'\xd4)\x89\xfaN\xa0\x04\x00\xac\xb3\x08\xa0V3\xde\xc5\xca\x94\x00\xa698\\\xbfKx\x87z\x7f\xed\x1e\x96D7\x93(\x8e\x12\x9dI\xed\x1e\xc6\xcc\x02\xac\x12\xb5\xe1\xfa\xa2a\xf0\x9b\xb7\x80\xea\xb6-N\xf2\x04\x04\x83\x07\x98en\x1a\xa1\x11\xdb\xc6bc\x91\xc6\x86\xc9Mx\x95\x87\xac\xbf\xfc\xfc\x1b,\x96\xc6y\xe8*\x13\x17\x06\xbd\xae9,&\xd7\xb75\x00\xef\xc8\xed\xbal\x8b\xafk:\x87\xcd\x13\xb7\x0d\x9d\xc3\xec\xe2\xb6\xc1\xd9\xb7\xb0\x80\xf9\xbaY\x15\xact\xdf6\xab\x82\xf9\xfc\xed\xdc\xc9x\x12\xfa*3\x01\xc9\x8c*\xe0z\xf4\x98\xeb\xea\xd8\x94\xd7l\xdf\x15\x91\xc2\x02\xd5\xeb\xbb\x1b;\x0b\xec\xdb\xado\xe3*Qf\xf9\x9c\x98\x84KX\x9b\xd0B\xec\xbd\xbf\xfd;\xcc{\xb6\x8c/5\xde\xa0\xc4@0\xc3I\x1c\x0f\x12\x90\xde\xc3;\x91\x94\xb34a\xfa\xb1\xa5c;1\x1a&\x1a\x80u\xf0\xc4\xa4U\xc2'S@\xe4\x94\x1ea^\x9f\x14 \x97hs*s\x12fo[Z\xd9\xc4R\x97\xb9\xfc\xa2\xfd\xab\x1a6\x00\x10\xbc\x0f0]KLR%:\xe6\"\xa9\x12\x19Bq\x97f\x81\xa8JX\x84J\x8atKXQL\x8atK\x18\xf1\x13\x93n\xe9\x03L\x0f\x92R\xba%\xac\xe9l\x99tK\xefc\xa4O\x8aLLX\xd2(]\x03\x92E7 \x97\xb0\xc2\x94\x14\xb9\x98(\xeae>\x10M\xac5IH\xa8\xfd\xe7q\xbd-\x93\x8d [\x18\x13\x03\xc1\x1c%1y\x9a0\x05HL\x9e&\xb2[:O\xd3]\x1b@\xd4\xb9A\x01*O\x13\xa6\x84I)O\x13\x16\xd3\x93R\x9e&<\xa3-\xe3\xa7\x8f\x15\xfb\xc4@0\x03\xdf2~\xfads\x0d\x04\xd3\xd6\xc4\xe4i\xc2\xc6\xb3\x04\xf24\xe15\xd8\x02\xcd\x91\xe0>8\xc3b\xad'\xd1y\x9a0kM\xbc\xc0\xa4\\\"\x87\xdf\xe4p\"\xf8V\xe4p\xa2 \x15\x17Jh\x19\xc8\xe9\x04?9\xf0t+@g\xc9%\xd4\x99;\x81\xc9\x92k\xab\x08\x88K\xc6\xc6A\xdey\x0f\xeb\xae[+\xe7\x05\x91\xc3|5\x81W\xfe\xf1g\x8b\xff\x0fvV\xd6E\xd03r5\xc5vcT\x90<\xb7\x9a\x14\x890\xb0=\")\x12a\x90\xe6U\x0eh\xb2BZ\x90 \xdd\xe8\xc4\x16\xf8\x16\xdb\x84'\x93\x17\x7f\x13\x9d\xd8\xe2\xa7\x04\xe7\x8a\xc4\x16\x98ln\xc98\xba\xcf\xb1\x8e\x95\xc8\xcf\xbf\xa1]DR+'\x8cX\xc6\x88\xe3|]\x18\x8bQ$9\xe6>\xc8}\x820\xa7\xaa\xf7\x84\xb5v%g\x17fTE\x89J\xd4\xfbO\xf1\xfd_\xd1\x91I\xda\x85\xe9\xbfl\xaa\x9c\xb5\x0b\x93\nY\x80\xa6\xed\xc2*\xb5*\x86\xf3v\xe1\xd3b\x8a\x95\x12wa\xb3\x16*\xa3\xf3\x0ea\xf1G\x16;W\x8b\xa7\xe5\x04V:\xc2\x95\"Z\xa9\x10\xf8\x06P\x8c\x13EP\xf6.\xeb:\x97\xf2\x80A)\xc2.D)\x9c{\x8bPf\x9ff\xd4\xb2.\xa2N\x97\x85em\x0d,\xb0\x13[F,\xcfr\x13Z(\x8a\xa0\x8cYx:\xc4\x17\xf1\x01\xa1\xceVG\xc4\xa6B\x85\xf7\x1a\x96\xdad1\x925\x0bK\x04\xaaTur\x98R\xa9B\xa5\xa4WX\x8b\xab\x94\xd0\xf8\x87\x05s\x94\xd3\x8c N \xae\x9b\xc0\xbak\x02\x87\xee\xd7D\x88\xf2\xd3\xea\x83\x8d\xa4\xa2I\xa6CP1\xd0\xe9 \x08\xfa\x05\x90\xf3\x81HQEf\x1bL\x0c\x93jf\x1b\x02\xd6\x81\x0cO \x933 d0WLL\x02\x19\xbc\xe8\x89I \x83iKbn\xd3\xb0&\xb8\xa5uQ\xc2\x95\x8d.J\x04\xde\"/ \x1duqGB\xf0/\xcaC\xaf\x94\xe0\xfe\x03\xac\xde'0\xc6\x8e\xe53\xdc\xf8>\"\x9a]\\r;$<\xc2d\x03!\x04\x19\x85\xf0\x90\xb3[d\xea\xc0\x06\xb5-};E\xebh]\x1b\xfb\xc6l)\xc9\x8b\xec}\xedw\x99\\\x83\x08\xd1&\xb9\x06\x16l\x93\"\xb9\x06\x01\x15\xa9)\x082\x17t \xc7ni\xdf\xc3\xf7\xb0\xa5\xab\xe4db\x81H\xc2zE:\xe2\xc5\x93\xf7d\xbc\xb5\xe8:\xf2a0\xefR\x88\xdc\xc9'd'G*\xaf<65\x08\x00\x84\xaa\xfd\x0d\xcd\x02\xb5\xbdqn\x07\xce*\xa9\x16\xf538\xadX\x9c\x01G\x9f\xe3\xf4\xab$\xe3\x1fb!_\x00\xd4E\x1aa!F\xf0\xc5rQj d\xc9bG]\xc1\xfe\x92\xa0\x99\x04\xe9w\xfd,\xd0\xc4z\xf0\xd3\xdbJ\x96x@\x98\x9f\x80\x80\xaf\xd1\x9f\xd3\xb5Ko\xab\xdc!\x0f\xb0\xb0,!P\xefg\x965\xbf\xad\xfcg\x88\xd4t[\x076`\xb5\xa7\x08\x94x@(\xce\xedR\xf8\x82\xb5^\xe1\xd7o\xab\x0b3 \xb4\xd4D_<\xc04P\x82L \\\x0dPuH\xebJK\xd9{\x98\xd5\x97^\xae'R@=\x08j\xe1g\xa8\xc8.\xd2p\xc0\x86\x02\x85R\x8f\x17\xcb\x16\x06\xd8X\xa4h\x8a\xb0\x11Yn7\xd4#\xa6\xf8\x93;p\x83L\x1e\xf2Oo\xe75\x80\xda\xeb\xa5msk\x89u\xc8\xd4hR\x98#\xa7\x0d\x02I\x03mJ35\xee\x87\x98jogp\xfa\x08 U\x80\xbf\xb0\x01d[\x7fAD\xc6,q\x04\x9f\xe6q\xea\x07r \x7f\x83\x95$]D9_as\\\x9a%\xd2\xeeE\xb2\xdfm\xc3\x01|H\xf0Z\x1dL\xc2r\xf3\x9e~\xb3\x9b\xa8\x0e&\x16\x89\x02\xe0d\x91\x19\xe7=\x9d\xaa\xe7)\xe1\xbayo\x94\x83\x07\xf3S\"[\xe7=\x90\xfa\x9fb\xbb\xa2\x80@_\x84\xc0\xe6=\xcdE\x9f`\xb2\x9c\xe6=\xc3E\xb1^Z\x1c#\xdb\x1a\x990*+H\x11\x05\xcb\xb4\xcb\x11T\xd6\x0e\x8b\xb3d\xaf\xad\x12\n\xdb\xa6 \xd0\xdbu\xeb\xa3\xfd\x1f\xb1-A\x80`\xd3\x9f\x12\xec\x11 \xc8\xf2F8\x86\n\xf6\xa2\xfaj\xee\x96]\x8f\xb0\xd6*\xc0e\xd7#\x8cL\xe5`_\xd2\xb6%\xd2\xb7\xa6\x04r=\xaa\xeb\xa5\x14\xe1k\x19\xa7\x0eY\xb3\x80\xca\xaeGD5\x15p\xedzD\xd4S\x01\xacUPs\xb7^\x0b\xcd\xdd\xe1\xce\xd0\xb1_Bm\xc3e\xd2=\xc2\xf7j\xbf\x83!\xf0\x97\x98\xb8n\xc3v?\xa4\x15\x80}\xd2\xd3\x1a\xcf \xf2\x82OO\x9a\xc7\xf3\xe2;\x91M\xf3\xf8\x84\xf8N\x84\xc7<\xd6\xe4\x05[ \x05H#(\x11XM\x84 \x05\x009\xa0\xd8\x1e\x1b\xd2\x83\x05\xb8j@w\x0d\xb08\xa0\x96\xa6\x87\xca7\xfcWXQ\x9405 |!\x9c\xe6\xb1I\xdbJOSl\xa8!\xa55\xb1\xa2\x86Dp\xcdcE\x0d)\x1d\x8855|J\xc45#\xed\xd8\xb6\xbfn]*b\x90eI\xca\xe1\x94V\xa8\xa6h\x96\xa1\x96)\x9ae\x8e\x9a\xa2\x11\x9e\x9e\xc7z\xad\x89\xc0!@@\xd1\x08\xbb/b\xd6\x88\x19\xc6\xc4\xacachjb\xd6\xac\x90\x9a\xbc\xd7\xe9~\xa8\x8d'D\xba\xb9\x03\x91S\x9f`=q\xc7\x113\xfaA\x86>gN2\x80\x9dy\x17Oh\xc7\x91!\x9aX\xaf\xc8\xe4\xe7\xdf`\xe4\xcf\x94\x9d\x9f\xf8\xea\xef\x18k\"i\xc9@\xb0\xa6\xb1cl\x80\xd8\xfe\x92\x19\x08\x96\xa9\x94zF+H\xdd\x0c#\xbf\xce\x9c\xfcclw\xcdx\xa0\xbcb\xdf\xc5\xeclG\xdb\x8b\xf0 \xcc4\x00\xdb\xcd\xb3!O\xf8I\xd1\xd8=\xb2,\x02\xd4\x8f@b'\xd0\xac\x11\xba3\xe4\xf0\x06*\xa6g\x99\x06`\xb6)\x01\xe9\xa1\xc0\xf7\xdf\xe0\xc3)ac;\xc4w\xf7J\x197\xf1A\x91\xf0:cJ5\x03\xe2[\xbf\xa2/\xf5gC?T\x9e\x8d\x98\xdeU\xb3\x1dbh6\xdcS\xb1\xbdtD\xf5\xe3\xb9\xb0\xb1\xb5.N\x066\xc7d\xc3(\x11X\xf8 \xe6\x1c\x86\xbb\x93\xb6t<\xce\xaf\xb1%\x1a\xa5\xdb\xc0\xc4\xce\x92k\x03\x8bq(\xd1\x06\x99\xa0\xba!\xf9\x84\xe0\xa0\x00\x80\xec\x8d\x15z\x00\x01\xc1\xf8\x88\xa0\xa8\x00\xc2\xbb\xb9XP\xc9\xea\x1e\xe0\xce\"\x0e>B\xd8n\x99\x81\xd7\xee\x03r\xd2\xa3\xb8\x07\xe7\xed],\xd0dQ\xac\xd3\x18\xe3\xa1\xed\x18\xdb\x06\xa6\xed\x99\x81`\xca! *d\xe3)6\x1bdQ\n\xc3\xc6rSVx_\x93\xa3\xb6\xb5\xb8,\x99\xe4\xdb\x84\xb0$\x0e\xec\x91\x05R\\\x9f\xbf\x87\x15.\x0d\xd4\xde\x0b\xefaA\x0d\xc7\xee\x93\xac\xea4t\x9f\xa4W\xd7E@F\xc6HJ\xe2\xfa\xc9\xa5\x9a%\xac\x9f\\\xafe\x89zU\xe5\xd9/\xb0IL_\xc9\xd9z6\xb6\xc1\x8f\xb0\xdc\xbb\x93\xf8q\xc0\x97\xeb\xe8\xb2\x80\xaa\x9a\x96\xe1\x02\xea\x7f\x88]\x06\xb3\xc4\xcf\xd4\xd6~\x84e\xa3,\x89\xf9\x1d\xe5F\xf5gx\x0fw\x8c-\x00k\xbe\x99\xb1\x05\x10\xa2\xa5nz0\xfb\xcf\xd4U\x0f\x96_v\xb4\xf9\x9f\xa0\xb7\xb6\xff\xe3E\xd81\xcf\x0f\xd0>4\x04_\xc0d\xfb>\\\x8c\xdc'\xdb\xb4\x1f\x0d\xb9\xe3U\xf3K\x12\xea\x08\x85\x90w\x13&1\xbb& \x1e\x1f\xba\xdc@\xf0~\xefj\xd1\x07\x8b*\xb9\x96\x960?\xcau\x0d\x0c\x10M\xe9\x00\xfb\x0f\xf0\xb6\xec\xf6\xd4\x93\xca\xf8\xa67W\x80\x7f\xc0s\xde\xed%\\\xc6y\x7f\x86\x97,7\x10L\x13wu\xb4>\xde\xb3\\\x030\xfe\xed\xc2\xa8\xb0\x1c\x93\xc3\x98\xf0\xa9\xcf=\xed:\x809\xc6\xae \xd6\xc7\x04<7\x10LZs\xe3\xca\x89M]y\xe1?\x88\xf9\xe1\xae\x16s\xb0\xd8\x91k\x00V\xd7vM\xc0<\x16as\x03\xc1\x879\xd7\x9e\x85da\x86N\x02\xeen\x98d\xe6& -\x1ern\xde\xc5\xc2\xdaJ.\xdf\xa7\x12\xa0w1\x95\xca\xcbOWY\x80*6\xe5]l\x1e\xcd\xcdC\x18X\xfc\xda\xd5\x11\xf2X\\\xcf5\x00\xbb\xedC\xb0\xed\xc7\x98\xc1\xee\x86\x9e\x8e\xa9\xc5\xef\xe5\x00\xc8\x84\xd4\xe2Ce\xc0:\xa6\x16\xd3sY\x00\x07\xd5\xe2{(c\x8a}\x88\xf1SBt\xb6\xff\x07\xf8\xa8\xed\xaad\x0b\x9fa\x0c\xc95\x00k\xf4\xbb\x86\xc5c\xcd-7\x10L\x04\x9b.\x1cw\xe3\xc2\xb9\x86\xd0\x95\x02f\xa9Wv\xda|\x1f\xdb\x8c\x15\xb8r'KOh\\\xbd\xb3\xc5\x8a\xc5n,\xa4\x81b|\x18\x9eW\xe1\x96\xfa\xd8+\x98\x9c\xeaX91\x9aw?\xc8\x19\xd2%\x8a\xa7\xa4\xc8a\x8ak\xb77\x8e\xf1[MX\x9b\x94E\xd0\xad1\x96awU\x08\x14^\xe4\\}\xc7\xeb*\xbe\x0fm\x15v\x8d\xc1\xfbs, \xe6\x85-\x9cn\x93v\xbf\xc4\x95$\xa4\x187mSa\x10x\x7fb\x99=O\x0c\xa9\xc1\xe7)/?\x02e\x01jRC\x16\\9\x19~F6Z\x03\xb0\xd8\x92k\x0f\xaa_`\x82\xbbkD\x1d\xc2?\x8c\xa8\x83U\xb7\xdc\xbc<\x84\xeb\xecj\xdd\xe83L\xbbr\x03\xc1\xf2w\xae\x9d\xbb0M\xca\x8d\x0b\x17\x96ps-\x0b\x90\xd5\xdeUy\n\x08\xe1V\xdf\xb1.\x97\xef\x1ba\xfd\x11\x96\x9d\xc6N8\x80;\xc8G\xb8\xb9\xb1\x934\\\xab\x8c\x9dD(\xce\xd2c\x01\xaf\xd0\xd8I\xc2H\xe8\xbe\xf0\x9a\x06\xc6\xc2\xb1\x93\xd4\\\xc6\x08\x88o\x0b:\x17\x80\xfa\xb8\xc6\xb1\x16\xa7,\xed%Vz\"\x00\xe0`\x8f\xe5\x86\xb1\x93\x18O\x0clR\x11\xb0\xea\x1d\x03\xbd\xd2-\x97Q7\x0d5\x85*\xa6\xbd\xe62\xca\xc0g-\xa4-\"\xc4\xb6!`H\xd3\"\xaf\x03\x97\xca\x18\xaaH\xfc\xa1/+\xcd\xfa)f\xe1c\xc53\x9e\xe2\x83 \x002\x8a\xef)>\x08\x97A$\xc4\xe4l\x0c\x9f\xf1\xf0\x8a$f\xb8\xeb\"\x87\x19\xee\xa1HaFFe\xea`]H\xb6&%\xaf\xa7\x98\xe3^V\x9e\x9c\xf8\xa6m\x0c\xdfI\xea\x991\xe7j\xb9\x1e`qx\xcc\xb9\xd2W\xb1\n1\xe6A\xe0\xc3\xbd\x02&w\x97y\xa2\xda{\x93\x1c\n\x0d\xfa\x11\xad\x93\xd5\xd5\xc8j\xca\x97\x13\x9bb\xb9T\xc3\xd5\x13\x17u\xd5\xb7y\xec$\x8e\xf2+\xff+,B\xebR\x85\xe5\x07#3}\x04\x04\x13\xe5\xcbZ\x0c\xc7\xc2\xf6X\x030\xee\x8e\xb5\xc4JQ\xdf\xe4\x8e\xb4dz\x1c\x9b\x9c\x8b\x96\x0c\x89\x97\x8dx\x86\x95\xf1\xb1\x81\x10:[\x1b\xef=6o\x17\x92sg\xd8\x16!R\x86ma\xc5z\\\xba\x01\xb6\x90\x8b\xd2-\xb0\x15j\xeeKj\xa0\xbc\x8eZ].\x0e\x17\xd6\x00\xc6w\xfc\xc1\x1dG\xb2\x82G\x18\xf1\xafh\xbfV\xcc\xfd\xf65\x00\xf3\x9d}\xee\xa9\xf3\xf0\x18+\x00W\xb8\x07Q\xbd\x0f\xf1\xe8\xf65\xe4\x1e\xde\x17 \x81C\x89qj\x9f\xfb*[\xcc\xdb\x18\x97\xafht\xc3\xf3\xd9\xd7\x00<\x9f+\x063\xb0\xa0\xb3o \x98\x94\xec\xdb;\xdfO\xac\xa7g?\xe1N6\xb4\x82\xae\x18D\xc2\x87`\xdf \x12\xd6A\x0e\x94'\xd4C\xcc\x04\x0f\xd4\xce<\xfb\x05\x16\xc0\x0e\x94\x13\x14\xd1\x9c\x0e<-\xfe\xe0k\xe67\xf4za\x9b\xc2\x81\x06\xe0\xfd?\xd0\x0f\xb5\x90\xb7o\x0f\xb4\x8eL\x9e\xbb}Cf#\xc06\x90\x03\xf9\x15\xab\x00\x07:\xbd$y\xcb\xf7@\xdfA\x927|\x0f\xd4\xf3d\xe4!\xdd\x03\xfd\xe2\x0bf\x05\x07:\x99\xe0Gx\xaf\xde0\xe8\x80\x95\xef\x03\x03\xc1,\xef\xa0\x88\x0d\xc1l\xea 2\xd6A\xb2\x91:<\x9d\xbc\xdc{\xa0}>\xc8\x83\xbdo\x18L\xc2\xc4\xea\xc0`\x12&\x8a\x07\xc6;\xee#l\x1f<0\n\xd7G\xf8\xb6\xed\xc0\x88\xcc\xa4\xa7q\x0dK>\xd8\xaf%\x00W\x8d\x8d\x0e\x93\xdfC\x03\xc1\xb8yu\x11\x84\x12\x8c\xe6\x87\x0e\xd8\xaf\xf0\xfe\\\xd5$\x0b/\xda\xa1\x06`\xbc\xbc\n\x1d`\xd9\xe6\x10\xda\xc7\xa4\xfd\x90\xcbdBX5\xbb\xaaO\n\x96\xdf\x0f5\x00\x8f\xe7\xea*\xf4\x8b\xef\xa2\x0f}\xe8\x18+\xadW\x0d\xe2a?\x9fC\x03\xc1D\xff\xaaA\x14L \x0f\x0d\xa2`JxU\xd9\x0b\xb1\x08t\xa8\x0c\x86\xa4<\xe8;\x9f\xe1\x83z\xa8\xf4 l\x00\xb8fBQ0\xc2\xdf1\x10LT\xae\x99\x1b\\\x8c\x1ew\x0c\x04\x93\x90k0\x0d\xbc\x8cw\xe03F\x82k\xea\xe5vL\"\xee\xa8\xef\x98\xa6\xdc\xe1\\?\xe2\x89\x19\xc65\x9eDW|/\x1b\xd6?\xa3vM]\x9fb\xc9\xf0\x8e\xfa\x8eq\xe5\x9a\n\x9b\xc6]\xdd\xd1\xc8E\xa6\xa3,\xfe\xa4\x030\xf8\xff=\xee\xe0\x8e?0!c\xf8l^\xd3ar\xf8\xb6\xed\x8e\xc1;|v\xae\x19\xbc\xc3D\xfa\x8e\xc1;|p\xef\xec\xdf\x92k\x85 \xd7\x9d\xfd\x10\x00\xef\xb6\xcc\xf7\xbb\xf2\xaf\xbb]\xd6\xcfC\xe9g\xda\xe6]\x96uY\xd8a\x7fd\n\xb5\xf2\x94\xb34K|7k\xbdj\xbe\x8e\x9d\x84%\xec\x0c\x0b\xdb'\xe7^\xe9T\xbb\x8a\xe4\xf7\xf9\xeftf\xf2\x90\xa7\xae\x13\xf3K^Q\x93\xcf\xf0\x838J\xb2\x94\x9d\xa9\xf6[\xeeTw\x11v\x99\xdfeN\x97\xe5\xec\x0c\xcb\xaa\xdd\x88\x9fh\x84\xcf\xc4Qz\xc99x\xb5\x02\xf5\xfb\xac\xfd\xf2,;sF\x14H\x13w\xc6\x1d:\xc9R\xe4\xf1\xc5\xac\x9dup_\xe2\xd7\x8f\x12\xd6\xce\x8e\x1e}\x95e\xec\xbb,}\xd5VF\xb7<\x07-\xb7Cfo\xbe\xc3\x12\x9e\xe5I\xc8\x8e\xcc\xbdZ\xdb\xc8\xcb\xf3\xb2\x91\xd0\x14v\xd8\x19\x96\xb4\xa36\xb4\x98\x06\xbe\xcb\xdb9;\xca\xe6\xc4\xeat:]v\xe4\x08\x9f\x89\x9d$\xe5\xc9\xcc\xd8 |\xcf\xc9\xf8\x9a\x1f\xee\xb5\x9d\x0e{\xe9%\xd6\x96+!\x16\n\xea\xf0\x99\xc0\x0f\xf7\x96\xa20\xe3a\xc6\xce\x88e<2\xdb\xb1\x8f\xe7\xb4\x1a\x8bhGV\x17K\xc0^\x13\x7f\x9fa\xf3l\x81eG\x8f\x92\x8aw\xc9\x173\xebo\xd5\x97\x93\xeb\xec\xb33lV\xad\xb4\xe8\xf3\xc4<;\xd2\xb4\xa0\xa2\xcc\x91v\xc8\xbe\xc7^\x11\x7f\x86\xec\xbbl\xeed\xe7\xd5\x0e\x19\x81XX\xebd:j.t\xfe\xfe\x83\xf4\xe8\xf1A\x97\xb5X\xab3\x93E\xf2\x0eg\xc9Iy\xfb\x85\xe0\xf0F\xef\x16w\xb3\x19\x8f\xf7\xfd\x90o&Q\xcc\x93\xec\xb0\x9duY\xeb\xe6M\x9e^\x8a\xbc<\xe0\xad.\xc1\xd6 \xe7\x0b\xec\xc8l1\x82N\x97\xc9V\x9c<\xc8\xca\xd3\xac\x99%\xc5\x147\x1a\xc5Q\xc8\xc3,]`\x8en\x89\"\xfb~\xe2\xc4K\xa5\xa2y}\xd14s2\xbe\x19\xe4\x03?L\x17jXA\x1as\xb7\x0e\xc6Tw\xdb<\x90\xb9&\xd2\x05\x96\xd0^\xf4/-J\xf9\xd6Bw\xedu\x9d<\x1b>\xc7\x08\xa2\xe7i;r\xd2\x13Mm;r\x8f\xd2\x05\x96\xd6\xcf+\xe1^\xeer\xd1\xb5[\xbf\xd4\xfaWZ\x84\xc0>P\xf2\xf5n\xcd)\xbcK\xe9l\xdc\x0e\xdb'\xe7\xe7;\x16\xc9\x14@'0\xc87\xa0\x93\x18$\x88W_\x82NaP\xaeA'H\xadT58\x7f\xe2e\x0c\nt_'\xc9\x08]\xdd\xe0\xc9\x13\x9d\xce\xab\xdf20}JX\xbf\x9e\x1c\x08\x02\xc6g\x8a\xc3\xc8^c\x9c\xd96Um\xce\x02\xe3u+j\xe98\xa6\x1d\x0b\x92Mz-\x88t\x95\xd4j\x0e\xfeGw)\xbb \xf3 `G\xce0N\xe59\xc9P$\xcfc~\xc8xG\x93\xa18\x89\xb2(;\x8c\xf9\xcc\xd0I7\xf6CM\x90f\\'\x08\x04Q\x0bA\xd6\xc9\xae\x877\x04S\xb9\x1e\xde@|N\x0d\xb3L\x8b\x04-,-\x02\xfbF\x90J?\xdd\xdew\x06\x03\x9e\xcc\x0b\x8e7\xe3\xa7\x1b\x8b\xdb'\xe4\x9f)O\xc6\xb7\x1b(\x82\x103y\x91\x942\xc5#KtY.\xddJ\xa4\xec\xaa\x93\xe6\xc7\x03&\"\x99\xb0\x90\x00\n\x17^l\xb1\x97{fz\xaek\xcd\x03\xcc\x9f9o0\xefp\xde\xa4=/2+vD\x00\x01 \"\x80$)Y\xd5}\xb0\x96\xad$\"\x10\xd7\x1d;\xf6}'a\x00\x9b*\xfaf\xe7\xbe\x92\x1bl\xbf\x0d\xf1\xed\xd6\x8e\x12\xc6}-\x8cW[\xd1\xde\x07]=\x1d\x13W\x0d\xd8;#\xc5\xe1U^\x10z\x91R\x1c_aP\xfc\xeb\xbb\x9c6\xa2&\xday_\xf6\xa6\x0b!\xdf\x16\xc7\xce\x1cz\xec\xcb\x85\xcdc\xa7\x851\xd5\xf8\xec\xa3\xcc\x94\xf7t\xc8\xb0/\x9fq\x03\xf4\xc5L\xd94s\xb7\x89\x85\xf1o E\xe3\xdf\x12\xfe\xc6\xbfk\xdc\xce\xfe\xac\xd0\xfe\xddLI,e\xffvUw\x8f\x91C\x1d\x82\x83)\x84\x13\xbcXn\x86\x7f\x95\xb8\x17\x87\xed\x85\xf9K\x1f\x89\x15F\xfe\x18\xcee=\xbd\xce=\xfb\xb9MP\x0c\xed6\x93\xc4_\xbf?=#\xe1\x9f\xa3\xe4IY,\x92,\xfc\x99\x18\x88\x8a\x9cR\xd1JZ\x9e\x96\x8c\x1e\xa8Hy\x05!\xe2+ \x91\xd2D\x88\xe4\x9f\x86\xd8\x16\xbf\xe8\x84#\x0d\xaan.\x95-\xee\xceP\x7f7k\x87.\x83}\x7f\xed6\xccvq\xab\x8c'\xdc\x01\xc2+>t\xdf{\x11\xe6\x85\xd3\x06\xfe\xeav#q\x91]\x1d\x92\xbf\xdb\x8e7O\xb2\x03\x7f\xb60\xcc\x0d\xa4[\x93\x1d\x06\xbe\xee\x0e\x1d\xc7\xd8Q3\xa2\x14R\x8a\xe9\xe6\xb1\xba\x14u\x0e\xd3\x91\xa6\x94\xe2\xdf\x92Q\x01\x94\x0d\xb1\x14g\xd8J(\xcb>\xb6P\xbe\x84bn\xfe\xc1c\x7f\xf6}D\xf7|\xd2\x04\x00m\xfdk\x0d\x03\x11#\x03\x92\x96\xf9\xc2\x8e\xc9\x05\xf8\x14\x81\xf3\x1b\xbd\xda\xd6_\xaeQ\x056\xf3\xe6aT\x90l\x00|@}\x88\x18FE\x91-Q\xd6\xbdv\x1cG\xc1v8.X\x8b\xa2H-\xfc\x14!\xd7\xf2\xd3\xf0\xcf\xe4J\xbc\xa1\x84\xc2\n\xc3/;\xfd\xd0>\xe2?\xc8\x7f\xadt\xe5*\x99\xbfJV@o\x8d\x8a\xad\xf2\"\x12\x9f\x15\x0b&2\x7f\x92e\xfe\x95\x9d\xc1c\x18\xc1>d\xb0\x01#\x98\xc0\xa6\xe3\".\x18=\x82\x10\xbe\x82\xec\x11\x84\xeb\xeb\x0e$\xd3\x90V8\x96[\x9b\x86\xc7\xdd\xcd\xa4}\xfaws\xd9\x97\x155\xe3\xd3\xcb=j1\x8b\xd3\xe2\x98\x92\x8b3\xbf\xb0\x13\x87r\x93mV3\xd1^\xff\xac\xe0\xf7\xbf\xff[\xf2\x8c\x9a\x9a\xbdK\xa1\x82\xdc\x06W\x1f\x0f\xe3\xebVe\x91\xef\x84\x8d\\\x99\x81\xbd3\xd6y \x03+\x13%\xf5\x86\xa1Z\xa7GB\xa0\xd5\xe4E\x1d\xde\xd6\xc8\xd7\xe6m\xbev\x18\xf1\xb2\x12\x8f\xe3\xf6*#\xccK[\xe1\x9fB\x89\x7f\xe2\n\xff\x14\x1c\xff\x14\x12\xfe\xc9\x18\xfe\xc9\xe0+(\x1eAF\xf1O<\xcd\xba\xf8'\xd3\xe0\x9f\x04Ug\xb7\xc6?\x127E\xf1\x8f\xdfB/1\xc59]\xd1\x8e\xe9\x88\xaf\x84\xd7?)+E>gV\xa9\x8b\x07\x99\x0e\xa2\xa3MH\xaa\xa2\xfb*N\x88\x15u\x98\xa4Z\xa9\xf1P\xaf\xd4\xd8T)5X\xd1H%\xcdcEz\xa5\xc6\xd6\xef\xab\xd4\x10\xbfd\x91\x7f\xb3\xa1\xa7~\x14\x9d\xfa\xb3\xf7\xf9\xa4&b\x9as\xf9\xb6(\xd2'\xa8\x88\x8b\xd4\x15\xde\x12Lc\xf5u\x12\\Mj\xfa\xbcY\xe7\x90a#\xad\xfa\x92\x97?M\xe2\xc2\x0f\xd1\xdfL\xa3\xbc\x94:;\x08B\xf4V\xc8\xd55_\xa7\x84%\xff\xa9\xfa\xd6(\xe9\x12Q\xf1E\x18\xbf\x9f@(j}\xe6\x87\xc3\xb7c\xbb\xab\x9fKxI\x07\x90C\xbc\xbe\xec\xd8\xa6p\x8cUF\x14l\x91\xa8XQ'\xf1\xd1A\xb4\xff.%\xa8\xf5B\xc0\xedr-\xb1\xb8\x18*ex\xb7\x0e7\x0cI\xc9\xec\x8d_,\xba\xe5LJbU@TA\xa6\xa5\xb0)\x0b\xe7`\xaf\x15\x95\x1e\xb0:\x03\x9cH\xe0\xe9ul+O}J\xf5\xd0\xdb\xc4\x05\xebU\x02\xd5$\xda\xcc4\x9d'SI-\xfd\xb4\xa6-z\x94@\xda\x8e\x83\xf0\xbc\x03e\xe2yO\xae&\x12c\"\x9ekW\xdf\xdcb\\\xcd\"\xc6\xeb\xaf=\xc8\\\xc7\xaa\xf1\x81Z_|\x91\x91\xb9\x10\x13\xecc[0\xb9\xd9\xf8A\xcc!W\x16_\xab\xc6\x17\x99XI\xba\x9b\xf2\x00\xa3jc\xe90\xd5\x8c-\xf0=\x9bUR\xaaa\x02\x83\n\xf7LZ\n\x0c\xf9\xd1q\xd3\xd0\xbf\xf3\xa5\x0b\n\xfe\x94\x98\xd6\x12pX\x13\x98\x99\xc5\x01\xb8\xe4Q\x8f\xc8\x00\xfd\x86,s\xa5%)\x16I\xd0\xdbV\x8a\xee1=\xa2\x15q\x9e\xe9=\xc3\xd8t\x17r\xba\xdd=\x12\x99(J.\x8e\xb2\xab\xe7\xc5\xeb\xb2\x98\xb4\x8d9\xe5\xe7Z!<\xd0\xbdo\xbfko\xe3\xb0C\xcb\x8eY\xfey\x194uo\xa3Pu\xe7\xd0\xcb\xc8\x0e\xc5\x9d\x13\xf6\xdf9\xe1\xe7}\xe7d5\xf1\xa1\xbbu\xa4*\xdf\xd3\x85\xeb\xd6\x0b\x07\xdfNX'\x9e\x87g\n\xa8/\xab\xfb\xabb \xba\x95\x98\xb1\xf8<\xee\x96D\xec\x0ee\x06\x84GW\xa9b\x9c3\xac\x12\xe6\x07\x97dV\x16\x8a\n\xf3\x9e+4\xc5\xf2$~\xba\xf0\xe33\xc5\xf7\x01\x82\x8d\xf5\xd2\xcf\xde\x07\xc9E\xac\x92?.X\x95e\x12\x90\xe8\xe0\xd2_\xa6\x11QU;g\xd5:\xb4\xa1\xaa\xee\x12\xb85q\xc1\xe4\x01\x01\xc9gY\x98\xd2\xad\xb7*]f\xf7\xb3\xb3\xd6g|\xe9\xf8'\xe4\x02\x12\xefu\x16\x90\x8c\x04/\xfd\xb4y\xce\xe9ZG\xb4\xda\x99\xf7\x9e\x08\xe1w\x98\xe5E\x9bu\xa3\x80v\x05{p\x86]\xa8\x90\xd6)\xec\x81\x95\xe0)fw\xd3U\xcd\xef\xa3\n\xdar\x81\xc9f\xdb\xb6?H\xa2\\\x19n2\xbc\xf5(\xeb\x1b\xce\xf0B\xba\x97\xcc\nRl\xe4EF\xfc%\xbf\x08\xe9$\x98\x91k\xe4\x85q@._\xcfm+\\\xfag\xe4\x1e[\x88N\xa1_\x06a\xa2+<\x0f\x03B\x0bu,\xf0 \xdb\xd6\xe7qZ\x16*m\x03\x9f\xcb\x0c\xf6\xeb\x0b\xae\x85DOt7\x1d\x93f[\xf3\x90b\xecK\xf3;\xc1\x0e\xa1\x82V\x98t\n\xb5\xa3)\\lL;(.'\xd0\x8f*/\xae\"b\xb2^\x07\xf4\x1a\x880\x98\x07\x1d\x9d\xb6b\xf72\x026F\xeb\xdf\xfe\xf5\x8f\x96\x90}\xdf\x14\x07\x81\x0e:NN\xf0p\xea:/]\x88(\xc0\xdf|\x85\x1a\xbdfI\xba\xc1O\xb8v\xba\xf6\x17\xfc^p,\xe7#L7 iFf~\xa1\xdb\x0b\xca\x95\x0b\xbcQ\xd5\xa4\x97\x82\xfc\xb7\xd8\x0d\xd3\xf8nw\x88dj\xb8w\x9c\x12\xe1\xec\x1a\xa9\xb0\x06+\xab\xabta\x1a\xf6<6\xf2\xfeA\x98\xa7~1[<\x8f\xc3\"\xf4\xa3\xef9\xcb\xaa`J\xc4\xc3n\xff (\xf8\x12\xf1H\x13\x9c\xa0\x9f\x94\x05\x1b`\xc1\xbaz\x01\xb4\xcd\xc8\x9c\xde\x04B}E\xcehs\x13\x06\x8a\xcf\xe7\xb0\x0f\x01L`\xae\xffhU*\x15\x18\xa5\x8azu\x83\xfd\x86z\xef\x9d\n\x1f(\xa5\x1dZC<\x18p\x07\xc9 \xb24\x9d\xfd@\x05'yRf32\x81es\x04\x86\x83\xb2P5\xd3\xbbW5K>\x01_\xc1p\xcb\xfc\xf8\x04\xcan\x0dr\x99\xfaq\xf0\x8c\xa4\xc5b\x02#\x85t@\xf0\xdbJ\x01\x9c\x80\xda+a\xb8\x83$\xac\x02\xf8jA\xd8\x9c \xc2d\xe2WQ\x9f\x13&z.\xe4\\w:3Y\xfb\xa3!\x12j M\xd5\x15\x90\xd58B\x96L#\x06\xec\xdd\x19\xe8]\xe9 \xefz\x8c\xa7\x15\xe9\xa2\xad\xd2\x90\xbc\xc5\x14\xeb\x95\xb0\xaf\xad\x9e\x18g\xcc\x89\x9d\xee\xed\x05B\x98\xc8\x996\xedh\xd2L\x12\x03VJn\xf8\x17\x0b\x8dW-\xfa\xaf~\xb2\x19\xff\xd4\xd4\x81\\\xc9zS\x818X=f\xaf\xf2\x83\"i!\x04Y\xdbCQd2\x87Z\xd1nY\xbd\x8a\xd1\xc2\xcb\xd3(,l\xeb\xc7\xd8r\x86)\xd3\x15\xad\xc4\xf0\x186a\x9f\x1b\xb3\x11X\x87\x91\xe3\xfd\x94\x84\xb1m\x81\xe5\xc0:\x14`V\xe0\xf2\xcat\x10\xeaM\xa3\xb8\xaa\xa5\xa9\xf5\xc5\x06\x8d\x1d&/\xfa\xe5z\xd8\xb6\xa8\xa8\xf3\xe6=q\xdc4,\xb4#\xafF\x91\xb2\xe5#\xef\n\xf6 \xc5\xb7\x9f\x1b\xf13S\x918 /\xe8\x908!/\xe8\x908>/Pz\xbb\xcfT$N\xce\x0b:*\xcf\x88\xdb\xe9\xd6c\x9d *gf\xa0rf\x9f\x9e\xca1;e\xf6P9x\xa5\xbb=\xc2\x90U\xa1'L\xce\x18\xd3\xd3k\x88M\x9f\xd0\xcbI\xc1\xbe\xaa\xd5Hx\x06\x14gY\xee\xe3{?\x0b\xfd\xd3\x88\xa0\xc8c\x85\x0e\x85R;\xec#\xc8bn\xb3^(\xfa\xd3\x7f\x951O\xfc2\xcbH\xcc\xbf4\xd3j\xd5\xa4\xcfH\xf1\xa4(\xb2\xf0\xb4,\x88m\x05~\xe1o\x9c\xf3>\xfb\xe8\xac\xe6\xc2\xa9\xaf\x06K,\x8d\x05{\xd5\x8d\x82\x91pb\x83\xa9\x0e3\xa66\xc68AZ9\xd1\x97\x9f\xfb\xd1\x04|e\xf1\xb5f\x8f\xabE\x1f\xb4\xa3\x8c\xe3\xc0\xddd_R.\x97\x04\xac\x85\x8e\xe9/\xef\x04\xcd\xdc:\xdc\x00\xfa\xafh\x90\x08\xb4\xbd7T\x9cE8\x8c\xb3\xa8\\\x8b\x9f\x85\xc1\xcb\xa4\x8c\xdb\xc9\xff\xe0\xa32\x19\xdcB^\x0d'\xa4 \xbcH\xf9\xd3\x96\xebcZ\x08%>#\xc7\xcb,\xb2\xfa/^\x15Y\xd7Z\x8b\x1f\xc2(zKf$<\xc7\xcb2\x1f\xb0&\xbd\xa7|\xc8\xa2\xc4\xb2sJ\xdf\xc9^\x15\x1f$\x955{\xe3+\xf5\xdaS\xba\xaf\x1eqk#\xd0\xb5\xab\xf9\xceD\xc4\xd1\x15@/\x19o\x1e\xc6\x81D\xfc\x0d\xa4\xfc\niwyl\xc5F\xdf\xda6LF{h\x8c\x11Vdl\x0b\xb0b\x15`\xe9\x1b\xb3CVO`\xc9\xdc\xaa<>\xa2\x96:zu\xfa7\xb1[\xf3\xc5o>|\x80\xac\xc7\xb0\x11$\xac\xd9n\xa2\xf7Cf\x92\xda_\x0fqj\xa1P\xb7Zz\xe6\x0e\xd4\x08\xb7\xa7Ha\xb31\xf4`\xdf\xa9\xf8\xc4\x8c\xd3\xee\xfc\x98\x0f\xdc7\xcd\xe9\x1e `9\x98\xcf\xc9\xac\x08\xcf\x89\xf8\xd2\x88E\xd0\xfb\xaa}\x92{\xd5\x1d\xb2k\x94|\x92MgW{\x82\x06\x1e5\xb3\x04\x87\xc7\x14\xf4\xf2\xf0g\x0d\n\xe4c\xceo*\x14\x91\xd5|\xc2\x13L\x0d\xd8\xae\xbe\x93\xc8?%\x91\xb1\x9bE\xb1\x8c\xbeA%\xf3\x8d;aa\xd1\x8c\xbd\xd4\xea\x03\x04\xf0&y\xad\xeb0fT 3\xb7k\xda\xa2\x98\x00\xa6o\xe1\x13&p\xeb3\xa0\xe6g[\x8693:C\\!W\xd7\x03\xa7\xdb\xa8\xa7\xb3G\xf6\x8a\x841N\x8e\x905\xf5\x00\x1374\xbe\x0b\x88\xa3\xb4LY\x90`\x83\x8eP\xb7A\xd6S^\x0b\xde\xbd}1\xb1\x0c]7Dg\xa1\x9d\xe1\x8c\xb4\xb5\x17\xdb\xb5d\x8b\xd3\x0c\xd2y5|\xd8\xb4s\xd2Wk\xd89\xf9\xab\xdd\xa9}\xe0\xd5c\x89\x03z\x7f\x0d\xf1\x98\xce\x1a\xda\x06\xd4~\x1bC\xea\xf1\xdb\x95\xc4\xe5\x12\xcd\x11ns\x8e\xe9\xd3\xe2\xe8z\xaf\xf9\xfa\xec\x13\x13\xcfkZ\x8e\xc6\x14V@\x050`\xbf\x06\xa2\x03\xa8\xe2?\x92`B/\xf3\xbd=Hl$\xa6\xfa\xa9\x1c\x86\x1a\xfa\xeb \x9cc\xacH\xb1\x87\x89\xfaq`\xa2\x9fm\x88\x96\xb8}\x93\xe5\xa6\xb5\x05\xb9T\xf1s\xf2\xc3G\xccW\xa2\xcf&\x0e\x86\x83\x83\xb9\x91.\x0c\x9a\x16D\xeb\xf0Q[Ctj\xf4\x88[\xeb\x05\xee\x13\xbb\xce\xf1\xed\xe7&v\x8dtb\xd7H'v\x8dtb\xd7H'v\x8dtb\xd7\x88\x89]\xebQEL\xc0\xaa\x12\xabF\x9f^\xac:\xbb\x8dXU\x12\xac(\xa4\xa7]\xad\xadVy\xdc\x92Z\xdeJy|+\x11\xcf\x9dr?}\xbcM1\xc4)F\x19\xe9\xa3\xa6Q4\xb7\xa5\xeb\xb5\x10\xb2\xa5\x98\x81I\xdbMk\x1f\xa1w\xee1+\xa4p~\xe5\xd8\xed:\x15\xd2\x17\xb0>GI8\x962\x0fE4\xe5a\xf3\xe8\xe3\x9d\xb9\x8b\xdb\x0fYX\x90\xd7qt\xd5\xc0\xbc\xedG\xa7\xabp%\xb0\x1f\x0c\x08\x83\xa1\xb7W\xcc\xc0\x80\x96\xe9\xee\xaa\xd3g\x02\xd9\x85\x1f\x07\x11y\xbd\xea\x88[\xa0;\x14\xd0(\x10\xdf\xfb)O\xe2{\xa1W\x90\xbc\xb0\x0b\x16\xc0^\xb6\x1d\xe0yf`2\xc8\xa6\x00VY\xbe\xf6\xe17m\xaf\xbc\x91vlX\xc1\"9;\x8b\xc8\xf3\xfc \x08\x8b\xaf\x93K0$\x99\x91\x1f\x19\xbf\xb2\xb1\x0f[y\xe9\xdb~\xb9W(F5\x815\x8c'\xc0\xfe2~\xa7\xb6\xc0\x84\x1e\x98\xc7\xa46\x9d\x08W\xf2#\x8fE\xe1|!\x9e\x0e\x82\xd6W\xe5\xa7A\xa3p\xa4\xc3\xea\x14t'w{f\x1bV\xb2\xa9\x80\x15\xf8o\xfa\x08\x05u\xe3\x16\xaa/\xf1\xc1*S\x1d\xf6[\xdd\x02\x02V\xb1\x82\x001\x85\x16\x9e\xe0\xb6\x04\xf5\xdf_~\xa9\x9e\xaa-Ur\\X\x93\x1a\xab\\N\x18\x11\xd8\xf8\xb3\xd2\xeb\x0f@\x0b2d\xae\x8e\xf1o\xbc\xd4\xcf\xc2\xe0]\x1a\xf8\x85.\x08\xc2M\xd7X\xa2\x11\xf8*\xcbo\xb4\xeb\xac\xda\xa5;\x9a\xb2V\x10\x05+\x1e\x86a\xeaxXA%\x0f\x15ie\x88\xb6\"?\x99P\x9f\x0f\x101A\xa5\x9f\x1fx?\x86\x98O\xce\xfa\xba,\n\xb3c#p\xba+\xb3\xad#rY<\xc9\x88\xd2\x15M~JV}\x11\x9e-\xa2\xf0lQ0\xb0\x9a\xf4T\xe1\xee\xab\x97\x9ef\\zz\x13W\xe0\x81\xd2\xd3\x94U\xcc\x0c\xa3@\xf2\xad\x8f\"\x1f\xaa\xf0\xd5SK\x91M\xcer!9\xee\xd9'\xc7\x85s\x13\xa3a-vk\xab\xe7*o^`\x19XS\xbfo\x99fC\xe6%b\x11\xa8\x82R\xf4\xcf\xe9\xc6c\xab|\x13\xf8\x94\xdfqH\x9bX\xb8Rz\xfe\xb4\x15\x01\x15,\x17\xce\xf1_\n\xa2\x06 \x83y8\xbd|\x1e\xacd\x17\x0b\x9ck 3\x12\xe0\xed&\"b\xf6~\xc5\x08\xa2\xfa\xe0\xf5\x7f\xd1q\xae\xe8\x91\xc7\x00\xdb\xbb\xbb\xdc\xbc7~\x9e_$Y\xb0\xf2\xe6\xfd\x11\x9fO\xb1w7\xdb\x0d\xbf,\x12z\xddG\xa4\xa0\xbb\x12\x93\x8b\x8d\x94\xcfu\xc0\xd7\xb1\x08\"8\xf8\x0b\x0ea+|q\xf3\xdd_\xe8\xfdkz\xc2z\x88\xa7\x07\xdd\xe7C\xf6\x85>\x84^\x9e\x83,\xe4\xa1\nf\xda[\xd5\xe0\"\xc8\x8a\x0dF\xf4\xda\x12\x11\xb6\xe4\x94\xf8\x19\xc9\xf8\xbdj\x82\xf7\xdf\xe9\xc6\xc3\xe1\xdd\xea\xca\xbb\xf1u\x87\xd7B\xf0\xd9]u7\xba\xe6\xee\xf6\x8ac\x16\x89\x16.\xcf\xe7\x86\"\x87_m\xab\"\x9c\xbb@6w\x81h\x86#\x99\x01\x08\xc6\xe8\x7fl\xda\xa9a\x08\x81,\xfb\xeb\xd4\x11\xab\x12\x0c\xf6\xfe\xed\xd1\xd1\x1b\xccLK\xe2\x82\xcbR'P\xc6y\x99\xa6IV\x90\x80IR\x08\xa5\x97\xac\xffh\xc1:\xa4\xb0N\x7f\xddN\xfc[\x0f\xaf\x16\x017W8\xed\xb3e\x919\xf6.{\xd1\x002\xb9)c4r\xc6\xab7-\x98\xf4\x1b\xcf\xb4\xab\xccLH_+D\x0b\xb5\x1e\xd5$3c33\xf1e\x95\x82\x92\xaf\x1d\xcf\xe9\xc3\xc4e\xfd\x02$w\xb3\x00\x9d\x99\xa8\xb2\x92\x1b\xb3\xbe\xd1;'O}J\xe3\xd6\xab\xa7\x96\x1e*s\x9d\xd1\x01\x9d\x99\x00\xca\xb4\x9cd\xc8r2Q\xbby9\xd9\xc5=h9\xd9\xeau\x86l\x17\xd5\xec\x15\x06\xb7\xf54\xe5\x15\x87\x9e\x94\xbf\xe2\x11\xa4E\xefT3\x96g\xbe\x17r\xe2\x95\xa7*\x0f\xdbp\xdbK\xd0\x90\xd5\xd0\xa0\x1fL\x15\xe9G\x0d0tM\xb4k\xa9r\xbc\xfa\xf4\x07q\x05LT-\xa7j\xe4\x03\x82\xc8\x19h;\xe5)T\xc7\xa9Q\x07\x8d\xcb\xebxn\xd2\xd5\xe17\x12\x08B\x87\xa0\xba\xbd\xfa\xf2ws\xf6MZY~\xfbp\x03\x85\x82\xde\xaaYGW\xa7\x06 \x96\xf7\x95R>k\xf1\x80$\xa1\xe7\xbc\x8d+u\xe5;pKo\xea\xa2\x11[p\xb8;t\xdb\xa1\xba\x9eT6(\xc2\x9b\xd6\xa3Z4\xa4*U\xef\xfe\x8d\xe2Yw\xe5J\xffhB\x83\xed-\xbd\xd4`\xab\xc3\xd3\x87UQ\xc7\xad\xd9\xaf\x8a\x1e\xe8d\x07\xdb[\x0fu\xd2\x83\xedme\x8ckV\xf4yX\xf2\xc9\xfb\xd9lHX\x8dHym\x9aSyR\x16\x8b\xe7\x05YJ\xb9\xc7\x9b\x15\xea\xec\x0c\x93ZR\xd0\xacR\xa7\xa26\xa6<%3\x1e\xb6\xd0\x9ba?\x98\x90\xeb\xeb\xab\xe7\x01\x89\x8b\xb0\xc0\xa06b\x08\x7f&W\xa8*\xc2\xbe;\x8db`mQ\xf5i\x12\xe7\xe5\x92\xe4?0\x01\xd1JB\xfb\xdea\x17\x8aa\x8b\x0eQX\xe0\xd8Ek\xd0\x9a\xe12_\xcf#\xfft\xd0\x00\x05\n\x97\xd2\xf2\xb1\xbc\x0f\xb0\x8f\xd1\xe0z-%\xea\x0f\xbf\x0f\xf3\x10\x85'k\x9bj*\x8d>\x14FN\xfd\xd9\xfb\xba\xb2:\x1c\x14\xa2QK\xd4^uP\xdd^\x0cCR\xcd\xc00(FO\xab\xd7\xde\xec\xc2\xa5\x98\xbbzT\xca5U\xf6\xa8A\x1f\xf0\xb9j9\xf4\xbb04z\x04\xd3n%\xf1Qv\x95\x94\x05:\x07\xeb+'\xbc2\xf3g\xee\xa9\x1cr\xbd\x99X{}M\x96\xe5\xd2\x8f\xa2\xe4\xe2(\xbbz^\xbc.\x0d\x96P,\x87e\xc1\xeb\x1d\xc4\xfei\xa4\"\xd5\xc4\x83\xf1\x1f\xbc\xb9A\x0b\x12\xad\x10\x0e#\xa8\xebb\x1ag}\xcd\x05\xd6\x1c\x18L\xf6\xbc\xaa\xdc\x1b\x1fv\xc9\xb6`H(\xd9\xb3\xaa\xea\x80!\\UZ\xce\x97\xa8\xc5\xd4\xd7<\xad\x06\xfb\xc6\xa8\x13=a\xdd\x0b\xad\x8e\xbe\xe2\x05\x86e\xaeQf\x8f\xc3\xd8\x01\xab. \xa5?\xd2\xc8%\xfb\x80\x07\x85;BZZ_\xfb\x90\xd5~Z\xa1\xca\x1e\x0f\xb0\xa7\xac\xfe\xdb\xdaM\xbc\xef\x8b\xf7\xb0\x07%\xa5m\x0c>\x7fO(Q\xe5\x859e\xbe\xf4\xb5^\xc3\x1e\x9c0\x16ArS7\xcd\xee\x0d\xec\xc1\xa9\x97G\xe1\x8cP\x9c\xb51rx\x82\xef\xc6\xf7F\xe5\xdf\x8dS\xad\x1a\xb4oZ\xcd\xcd\xc7\xe8\xacO\x05w'}\x0eP\xf5\xdd\xb8\x9f\xd5\x838T>~\x155\xd3\xcc\x1c\xac\xfdX# \x02\xc5l\xc3\x82,\xc1\x82u\x9e}\x8b\xd9\x93v\xae^\n\xf7\x96\x8f\xaa\x1b]2S\xc3\xca\xac\xa0\x13\x1c\xa6\x04\xd5\xf6\xc4#2W>F\xf5ZQv\x86\x1f\xba\x9a\x9er\x0c\xd9x?\xd1~J\x83\xf9h\xdb\xd9\"\xb9\xfe17\xb3F\xedR\xcce\x17\xcd\x9bu-\x1c\x98\x06J\x18\x0d\xa2\x14\x8b\x88\xa7A3\x193=6H1]r 9K\xb3\xf1\xb4\xdd\x02*\xe5\xf5\xaf\x1b\x1e\x10r=\xf4fI\x19\x17\xf6\xad\xceD\x0b\x1c#2\xa0cmg\"7\xcf\xb0\xee$\xc4\xb8zO\x14\xe7W\xa0\xa6\xaf\x96\x0d\xa8\xb3\x18<\xe2Y\x12\xc1,\x89N\xd8\x85\x03\x8d\xdd\x8aN\xd0IK7\x13\xeb\x15\xbap}\x8aq\xc8nO\xda\xe1<\x93}\xa3\x1c\xe3\xb8\x1a\x99\x94\x06\x99P\x82\x8c:%\x9f \xee7\x9fV]\xbd\xf4S/\xcc_\xfa)\xf3\x17R\xd8\x1f\xd2\xe7\xda\x0e\xa5\x8e\x07&o\xd2\xcd\xe7\xa2\xcf\x8fh\x1e\x1bc\x95@G\xcaj\x88ZB\x1fA\xc1O\xe0\x94\xd1\x80}\xd9\x84j\xb6g\x02\x06\xfe\x80>\x99\x7f\x81W\xe6\x04z\xe2T\xa4\xac\xd6\xa2F]?\x84\xc8\x82\xf8\xb5|\xc9\xbe\xc2\xf4%\xc6v\x98\xdb\x94\xec\x94h\xae\xdf\xcc\x04\xd4\xe7\xa3#\x7f!\xa4H\xf2\x97-QV\xff\xbaK\xb2t\x03\x07%jsNo\x02\xe7}\x8b)\xb8\xb7 \xf4\x04\xd7\xaeBEN\xe0\xbd\xb6\xa2.^h#;\x1c\x06\xd8\xbb\x0b,\x7f\x13\xe31m\xc7i}\xdd\xbfJ m\x90o0\x01\xcbj\xdc\x9bm\xb2\xe6\x8e\xee\xad\x8a\"\xab\xef.\xb8\xcbY\x1e\x1a\x07\":\x9f\xf0\xb0\xe2\x98Z\xb2K\xb8\x1a\x0e\x8a\x8c!\x14,c\x1f\xc1y]-\xf5\x13\xdb\xa1\xa4\xe2\xeb:t\xab\x9e9\xb8\x93\x95\xff\x87d/oJ\x0f\xd7\xe0}\x82w=\xa3\xda_\xd7r\x01\x8c7\x80; \xfd\xa9\xbd\x81\xb9$\x03#%\x1a \x83\xa6\x87\xb1\xae\xda\xa5iN\\\xe6y&\xe2\xfb>\xade4\xdc\xff\xe8\xccmk\x8a\xafL + y\xf2 \xf05\x10\xe9\x00\x1c\xef=\xb9\xc2\x1b\xdfH\xa8\xf3\x8b\xa1_\xd8/\x9e\xa5\x97\x93\xe2mg\x06\x03r\x1c\x8bh\xf8fd\x0dm\xdcn\xacmr\x0f\x1e\xc6\xfeI\xd1<\xf9\xd2m\xa0\x06Zw\xcaM@r\x93\x83t\x17\xb8\xf1\xa9\xd1,\xb7Blo\xf4+\xd2\x08\xfc\xf8zP\xbd\xef[\xe0\\\xbd3\x01s\x9d\xf8\xa1/\xf9\xaf|i\xaf\x06\xc1\x03\xdc\xdc\xb5\xa6T\xedG\xa85W\x9be?\x84\x03W0\xcck\xea\xdb\x8e)\x0f\x19C\xe3\n3D\x9d\x12\x0f'\xb5\xe5sY\x0dr\xc0\xa9\x84\xd5h)\xf1\xf0\xc3\x9c\xd0^\x9f\xc7L5\xd4\xfba_\xa4\x90\xc1\x88g\x95 ~Fh\xa7F\x97\xab_\x03Z|t\x03\x8bo\x95\xa5\xf7\xb9\xe8M\x1dD\xb6%\xa9\xe9\xcb\xb5\xd4\x12\x01\xf5Uoi\xb8\xba\xda\xcd\x86\xbe\xac\xab\x92\x95\x94\xdb\x13\x98\xd6!SZ\xf1h\xe9\xaa\x06\x06\x1b\xaf\xf3\xcf\xd0\xa8\xc6e\xa6\x0b\x1d\x03\x16\xcc)\x95\xc1\x1e$H\xecdM\xd3\x91\xccl:\xd2\xf4\x93k\x81\xac_[\xe8\x89W\xab\x98)\x0e4\x94SZ\x83\x85\x83\x84\x9a\xbaZ\\?\xadod\xe9G\xea$\xedyq\x15\x11\x9de)%\xfb\xcf\xb2\xa4\x8c\x83\xa7I\x84\x19\xdc\xff\x7f\x0f\x1e\x9e\xce7\xb7\xbb\xf7t\xeb\xe4\x19\xc6\x92fj\x19\x9dL\"\x9c3\x1bx\xab\xdd\xa8E\x17\xdf\x92O\xfegj\x0d\xd6\x03E\xd9\x10(\xd2\xd8K5\x0dj?\xcf\xe9\x07\xdax\x16\x81\xce\x18.\xd0\x19\xc3\x05:c\xb8@g\x0c\x17\xacf\x0c\x17\xa8\x8d\xe1\x82\xda\x18\xae\xebd\x93r\x0f\x81-\xa5\xb1[\xf0\xe9\x8d\xdd\xcc)\xfe$c7\x15\xed'\x19\xbd(L\xde:\x9e\xc2\x83M\xdbn\x95Q\xf8\xf31\xbf\xe93\xae)jO\xe0\x1es\x11JPO-t\xde\xd98M.\xadc\x03}O!L\xeb%\xcc\xd7i\x8d\xf9M\x88\xe0\xc2\"\xeeX\x9a\x91\x99_\x08i\x80\x1dsI\x8e\\\xc0.\xd7>U\xda0\x86\x8e\xcd\xa7n}\xe3\xc2\xcf\xe20>3\x89\xffE\xdd\x89uW|e\xec\xfd\x94\x84\xb1m\x81^\xe8\x91\xe8{J\xbd\x97t\x16\x1d\xfa\xf3\x97kW\x86\x01\xc3Pd\xb9\xb9\xc9\xb6\x88\xa4\x94#5d\x0b#\x97\xa9\x1f\x07\xcfX\xbd\xbaoOzO\xcf\x9b:\x01\xd4\xcd\x1c!\xfb\x1c \x19_\xa6\xbf\xb3\x16\x9f\xe75\xf4\xef\x0e\x1a\x9f\xad\x83\x86\xc15C\xaf\xa8\x890\x91c\x97\x89\x02~\x93\x87\xde<\xc9\x96\xbe\xa2_\xee\x92\xc1\x03\x9a\xab\xfd1\x84K\xd7\xda\xde\x1eD\x18\xd9\xfb4\x8c\xfd\xec\x8a\xbd\xc1\xecB\xd6\xa9\x9f\x93\xddm\xf1F\xef\xa9\xc1@_\xef\xd2\xa0\xf4\xe4\xe0\x01\x12\xe7\xa12\xdd\x90\x84\xeaJ\x1eS\n\xf6\xc1\n\xe3s?\n\x03\x8b\xc9\xe0\xbbm\x86E\xd4\xfc\xa2\xd4\xd4\\E$\x9a\xdbU\xcaK:\xda|\xba\xa9\x08\xd2\xaf\x90\x07\x04a\xce\xd9\xdc\xc2\x0b\xf3g\xfc\xaf\xe6a\xf8\xcch{\xb7\xca\xbd\xdfL\xef\x0duR~\xe1\xe8\x9e+\xde\xd5u3\x92\xa7I\x9c\x13I\xea\x01R\xa6\\\xcd\xebJ\xde\xc3\xdbnEN\xd2\xb9\xcb\xc6\xf6}\x05\xd6\xd3\"\xb7P\x8b\xdc\x8c\x84R\x15\xf0\xacP\x06<\x8b\xab\x80g\x94\x88\xccX\xc0\xb3\x0c\xbe\x82\xe2\x11d\xeb\xeb\x0e\xc4\xd3\xac\x19\xf0,\xd3\x07<\xab\x15\xf0&\x92\xadJzwx\x95\x17di;M\xdb\\\xfc\xeb\xbb\x9cN\xc7HW1Z\x96\xd9e:v\xc6r\xbf2j\x96\xad8?\xde\x0d^L<\xad\xdb\xf6\x0f\xdd_\x8a\x8d\x0c\xcd\xd1J\x854\xb6\x80}\xc0\xd4\x18\xcd\x06\xacc`\x81t\x9b/\x95x\x0e)\xd5\xe7\xb1\x1d\xf3\xec\x05-XW\xc0]kl\n\x03\x88V\xd3Sag\xfa\xcc/|\x8b}\xe22\x85\x03\xcbZr\x8c}\xb78YWw\x18\xee\xaa\xffn\xe3\xa6\x81\xa8N\xeb\xdd\x8d\xa4\xd3\xba~(j\x84\xd2?\x14q\x1eT\xae\xcc\x98\xb8\xa1\xbe\xf0\x84\x0f\xb3\xd6\xc9:\x91P\x9b\x9are~\x00Ul*\xc59\xc6\x80\xa2\xfb0\x0d\x11|;s\xc2\x98\xcf.\xc4\x02\x94\xf5\x15\x9a\xe7\x0bH\x94\x13\x15S\x8b\xbc\x96\xa6\x9d\xa2\xdb\x8ei\x1b\xb3a{\x93\x0f?\xc8\x9f\xc9\xa6\xc4C6\xc5\xbc#\x03\xb7#6n\xc7\n{\x11W\xaa\xb4\xcc{\x9dq\x17\xf5\xd4\xb1\x1d\xe5\xd6t.\xed!\xfb\xe3Br\xbb\x9d {w\xc6\xef\xdb\x99\x84\xc5\xddeq>\xf7k\x84\xe2\x9b6\x8a%#\x17\xa8G_M\xb5e\x08Mn\x9d\x82\xa8\xa7\x89G\x9de\xa3\xb4}\xa2\xbcrl\xdah\xac\xd9\xb6\x81\xb1\xbai\xeb\xa5\x97\x914\xf2g\xc4\x8e\xc9\x05\xbc%g\x07\x97\xa9m\xfdb\xc1:`D\xc6k\xcb\x05\xeb\xccr:*9\n\x11\xa5\x04\x1f\xf8\xf3\xf7\xa5+\x95\xca\x8e\xd2\x8e\xedqG\n\x1a\xf2\x92Q'4\x0fSX\x8c\xb7v\x95T]\xf9;\xb2\xac\x14\xfb\xfer\xed\xb6\xa5\x82\x99\x0b\xbe\xf7\xee\xcd\xb3'G\x07'\x87\x07/\x0e\x9e\x1e\x1d<;9}\xfd\xea\xe8\xe0\xd5\xd1\xc9\xd1\xdf\xde\xfc\xfbZ\xaa\x88\xe0\xd5\x16\xf5\xf0\xcd\xebW\x87\x07\xbf\xcf\xaa\xeadR\xaa\x98\xac=\xeb\x91\xb8\x10\xeaH\xf1U\x16\x84a\xaf\x93\xef\x9f\xbc}\xfe\xe4\xeb\x17\x07w{du$\xc4 \x0c\x16{\xef\x89\xc2\xa8\xc5\x17K\xad\x069 \xef)\xef\xfe\xcc\x85\xd0H\x11b\x05\xe3V\x94.\xf8\xcd\xf5\xcdnq%\xd72\x8fQ[\xbd\x97\xf0\xd7;\x0f\xa4\xfb6\xa1\xcb\x82y\xf4\x92\xec\xc0\x9f-l\xbdh\x01\xe9>\xef^\x18\x07\xe4\xd2\xfb)gr?-\xd5Gw4\xb1U1\"\x88G.\xd3$+\xf2)#\x80R?\x9f\xf9\xd1S?'\xdf\x84\x11\xa1\xdb\xe8\xd8\x85s\x8c\x1b#.\xd1}\xe9w\xdbAH\xba~\x07-\\loo\xefR\xb2H\x8c\x03\xd7eg\xb43\xe8k\xc3\xb2\x0b\x1b\x8d\xad\xb1L\xd0\xd4\x11\xbd\xecU\x0c5*Z#\x93\xa6W P\xdfd\xc92\xcc\x91r\x89\xed\xed\x9d\xfb\x8e\x0b\x87H\x91\xd7\xa65^^\xf8Y\x91\xff\x102\x0dIlo?\xd8\x1d4\xc3\xd8~8FM\xef\xc3\x07\x9dU\xda\xde\x19\xd6F\x1fpno?TB\xe7\xf6\x8e\xca\xc0%\xb6\xef\xb7_3b\xef\xfeHZ\xe9\xe6H\xc7[\xf7\x1d\x1b\x05n.X\xf8\xaf\xd5\x83\x87P\xbbt\x82\xd2;\x9b\x08'\xb3\x13\xda\xff\xa6\xf8\xe3=ES\xf5~\x18\x92x4T\xa6'\n!|\x15\xac\xe0Da\xd7\x18W\x85\xe1\xfa\xba\x12{\xac\x11\xdcTxL\x19\x94J\x9cm\xd7s\x10\xa2\xb9\xc4\x1e\xa1MzB\x0f\x9bE\x0f;\x8b\xd3\xc6\x8d\x0cYZ\xd9\xfa\x1d\x992\x99C\xec\xe2O\x89;\xbav\xab\xcah]\xf3D\x08*Q\xd7\xc0W:\xb3Y\x17\x0e\xfe\xac\xabg\xb6E\xe2\"\x0b\x890\x9co\xc3\x8f\xbc~\xf2F\xca\x0b\xac\x8e\xd0\xd8\xfb\xa5j\xaf\xf9*\xaaP\x17\x8b\xb9\xda\xdd\x93 \x89)\xdb\xb2f\xa6\xfdoy.F;\xeas\xf1\xb0\x1d\x95\x91\x1d\x8b\x87m\xc1\xb6\x8f\x9c\xc6#\xe9,\xeflb4\xf3\xd8\x1e=tl+,H\xe6\x17\x98CV\x0f\xbb|q(,\xd5\xb3k\xa1\x82>y\x1b\xa9\x11\x11\xc6\xef\xf6U:\x9e\x98\\\x16\x142Gn;u\x00\xed.\xc4\xb6)+\x0b\xcf\xaba\xaf\xb6\xdc\x12\xc2Q\xdf\x86[\xbb\xeau\xdd\xd5\xe2\x95\xedm\x07\xf6\x95\x9coHr\xe81@N\xecv\xa2\xa1Jk\x10\xbb\xb8y!\xaa\x07\x90\xda\xadT\x079S\x16\x94\xf0\x18\xf2G\x0ed\xde\xdc&\\\x182\xcd\xd7\xd7\x8f](\xa6q[\x08!\xa8\x8c\x9b.\xd8\xfd\x91\x9a|\x18\xa9!q{g[\xb3duw\x1a8\xab)\x0e\x96wFGQ\x94l%\xf4q-#$9\x84\xcaES U\xa3\x14\x1c#\x05iBI\x1cv\xa9\xc2\xda\x9e\xde\xb5\x117\xed\x11D\xf0\x18f\x8f\xf46\xc0\xb45\x9bne>\x9d\xad\xaf\x1f;\xb4\xcd\xd2\xa9\xcdU:\x1f2\xe1S\x7f\x970[_\xef\xe9\x16\xaf\x87\x19\x841\xe4Ho\xe4\xd3\xd91\x0b+\xea\xd4r\x0f\xac\xf2\xe1\x03j\xa2\xaak\xe5\xcb/a\xa3\x19\xbbhE\x1c'a\xb3]\xd5\xa9{\xe9\x17\x0bo\xe9_v\xc1\x88\x95\x84q\x1f \xe9\x11\xba\xcd\xb0\x0dq\x1c\xf8\n6a\x9f\x9e8X\xa7C\xdc\xa4\x97 C)7F\"\xea\xf9P\xac\xbds'\xc0\xaf\x83\xfc\x10\x83\xb8SHbD\x9eM k\x0d|\xb3#\xa2\xf3k\x8dPp\xc8\x0e\x88B+\xc1\xc6\x94\xe3\xda}\xf8\x009%/\"\x14\x87\xf1X\xb4\x9c\x9a\x9d\x80\x8dr8o\xb6\xf0\xb3\xa7I@\x9e\x14v\x8ek\xbe\xb33~\xb8K\xbf\x0d\xe11\xec\xecn\x8d\x1e\xb2\x86\xd6a\x84\xe0\x87\xb6\x04\xb6\xdf\xf9\x98V`\x0d\xecn\x8d\xb1s\x9f6p\x7fk{\x8b\xf7\xcf\xeacGt'a\xc2\xdf2/\xbd\xdc\xc5N\xc6\xb4\xcc\x87\x0d\xde\xcc:\x1d\xe7\x06\x1f\xd4W_\xc1h\xd3\x81u\xd8\xdd\xd9\xd9\xda\xbd\x1b\x08\xef\xdc\x1f\x1c vu\xd8\x90\x02\x8b\x83\x12e~\xa5\x0d\x8a*\xdc\xbd7\x90\x19\x13\x1f\xb6\xc4\xf0\xc5\"K.\x802\xef\x98%\x1dO\x80\x05a\x0eqR\x00R\x00\xa7\x11Y\xd3X~dv\xc1\xa2\xf0\x11g\xc5sB/\x81\x07\xc88\x8c\xb7\xb7\xf1\xdf\xed\xdd\x87\xec\xdf\xfb[\xec\xdf\x07\xfc\xfd\x83\x9d\x0eg\xb1\xbb\xe9\x08\xaefHg\xbd\x84\xd4\xaejgd\xd2(\x99\xc6\xf6\xe8\xbec[E\xc2N\xd5\x91\x7ff!\xdbi\xfdlQVn\x9d\x82\xfc\xda\x1eX\xd3\x04o{\xf8\xf9\xd8b\x0c\xd7\xfd-\xc7\xe6\x14@\xed\xc9\x00UCV?mU\xb5\x89\xe9j\x90l\xa7\x90i\x1dK\x1ah\x0c\xa94d-\xe4\x85\\\xa3\x1c\xfe\xa6\xc32\xac\xd8\xa3\xcdQ\xbf\x0d\xf5}:I\xb5(\x9f\xae\xe3\x03\x87Y\x1e:.X\xbe\xd2\xfe\x10\x83ik{i\xf7\xd6)l\x99\x088\x9e_\xaf\xc1\xa0\xf9KDK?\x11\xa2\xb8;0)\x0d\xbb4\xc4\xd5\xf8\xa8s\x0c\xd5z0Le#\x9d\xc3*\x02\xb6\xcdTG\x02$\xd8\x86d6\x13U\x89\xf3U\xf5\xa7\xd2\xb0\xe9\x1bE\x1e\xe5\xf5|\xf56\xd7>\xcep\xdb\xf8\xc6z\xea\xc7\xff\xb1\x80Y\x12\x9f\x93\xac\x00\x0e\xe9E\x02i\x16.\xc3\"<'\x8c\xcdZ\x95\x9a\xef;\xf3\xdb\xbbm\xc91\xc3\xc6\xe3\xed-%\xcd:RJ\x15Z\xec\xd3\x03\xc1>\xdd\xff\xef\x99}\xd2\xb0\xa5\xdb\xbb\xea\x95\x1dw\xc48>\xc7\xca\x94 }~p\xf2\xe6\xed\xeb\xa3\xd7\xed\x80\x15e\x9b\xdfo\x16\xb7\xc5\x01\x9d\xf58g\xb9+\x0b\xde\x15E\\\xe1<3D\xc6@+\x0c-5\x84$w\xe1\xa1S\x90\x17\x84y\x1a\xf9W\xf4v\x88\x93\x18\xf3E\xdb\xe3\x9d\x11\x9a\xf5\x938x\xba\x08\xa3\x00Y\xb7\xc2\xcb3\xcacX?\xf9\xe7>\xf3\xe9\x9dXU\x16J\xee\xfb\xf7C\x18\x07\xc9\x85\x17$3\x14\xa18^\x92\x92\xd8F\x18\xb9\xc8\xc2\x82\xd8\xd6W\xec\xd3\xc7\xa2\x8a\xf7\xcd\x1eC\xd1_\xfdx\x8f\x17\xa1j\xd7\x9bEI\x8e\xe9\x0ds<\xc1\xdf<\x82lc\xe3\x91\x03\x01\x89HA \xaf\x01i\x1aN\xb3c\xbdMYn\xb7`H\x8dI\xf9E\xc1,8)\x9dfD\xad\x889\x95tF\\F\x11J\x90)\x15g\x97-x'\x0ecpcrA\xf9\xbef1s\xff\x8aYZ^\x82\xa6g\x98\xd5\xc2qei\xab\x90p%v|+\x9a\x7f\xa46\x1e\xec\x9c\x08\x0e\xf9\xdb\x0f\xf4\x94\x1f\xbd\x98\xff{\x90\x1d\x8cF\x0f\xd4d\xf1\xb8\x8d\xa0\xb9\xf0`w\xd7\xb1\xd7\xda\x02\x075\xca\xb8\xc1\xfd\xce\x97\xa8\xe4\x84t\x17\x17\xe0\"u_Sfiz\xacX\xf3\x98\xf2\xd5\xa5\xc3\xa4\x04>\x8a\xf31%<^\x9b\x91\x88,\xa4\xf8\xf0\x11\x14BX\xcb\xf7\x03\xbf\xa3\xa8\x01w\x83\xb9\xa8\xfc\xa7\xd0\x8e\xb0\xb5\x0f\x1f\xea\xd6\xd4[\x14\xddt\x8b\x1e>\xd4\xac$\x83N\xdb\xfa\xd9r\xd0\xd5\x82\xd2\x81\xcf\xf3\x83\xb8\\2\xbe\xc1\x96`\x18L\xe6\xd1\x82\xd2=\xac\x93\x83\xd0s\x8d\xe6;y\x1a\x85\x85ma\x8e}\xde!\xb9\xf9 \xed@\x95\xd0ti.\xa7m\xdd\xdc{'\xd3\xe0\xd6\xff]T\xf5\xdf\x92\xa8J\x83\xb2\xb6w\xdb\xef\xc3\x01\x94\x8c__\x94\xd5\xc5e\xbcN\xcfH\xf1FT|=o^\xab\x1aX$\x02\x9d\x01fp\x0e\xf1dMQ\x1b\xad\xa2\xf0)\xa9\x90\xc4y\x91\x95\xb3\"\xc9\xd0\xe4 \xc28/\xfcx\xd6-\xddo\xfe-\xdd\xbe\x93\xe6g\x1c\x0f\xec\x83\xdf6\x00_q\xfdw\xb6nz&9\xfe\xc8V\x17XT\xf7'g\x1f(;P\xb1\x0c\x0f( \xcd\x98\xca-\xc7\x15\xde\xf0[\xfc\x82E\xc6\x80'\x8f\xb5G\x9bc\xc7\xe5>\xb5\x94Z\xc0\x83\x1b\xb5\xb8\x05\xf6\xaa!kp\xd1s6\x17\xba\xb3\xa0\x13m\xe1\xe9\xe1\xe1\xdb2\"/\xc2\\\x11\xec\xe0\xe9\xe1\xe1!%M\x9f\x91Y\xe4\xb3x\xd3\xdd\x80 O\x0f\x0f\xd1\x14\x817\xd1.\x8dB\x12\x17o\xc9\xacP\x97?{\xfd\xd2X\xc8\xe6\xa2->J\xde\x93X=\xf8g~\xe1\x1fe~\x9c\xcfI\xf6\xbc Ku\x1b\xdf\x84\x91f\xe4\xdf\x1e\xbd|\xf1$\x8a\x9e&Q\xc4\"P\xa9\xab\xf4\x95\x7f\x93dK\xee\x85\xa4\xae\xc0\x9c%\xb4U^\x92 \xf4\xd53|\x19. e\x89qs\xbb_\xbe\xf2\x97$x\x95\x04\xe4\xa5\x9f*J\x93@\xb3\xebo\xfc0\x16\xe1O\xd4K\xf3&*\xcfB\xc5|\xd9{\xcdp\x0e\xbf\xff\xd3\x0b\xbc\x8a\xd4m\x1e~\xff\xa7W\xe5\xf2\x94d\xda\xe27\x98%X\x03\x0b\xb4< c\xcd\x80\x0f\xbf\xff\x93 \x90\x0e\xbf\xff\x13\x83\x94$\xd3\x80\xc9!f\\\xfb\xba\x9c\xcf\xb5\x03\xa4\x07\xe5pAH\xa1^\xd5#rY\x1ce\xfe\xec\xfdS\xddQ\xa9jh\x8a\x93rV\xad]Ur\xed\xa2+zb\x07\x945a\x94\xf89|\x05\x0b\xc1s\xc2\xf9\xfa\xba\x8aZ]\xba\x18\xc9~1=W\x18\xbcQ&4\x98\x9e)JN\x91\xacW\x95\x9c\xc0\x1e\x9cR\xa4\x7f\xaa\xba\x90\x80_\xc5'H~\x9e\xd0\xfb\xf7\xc3\x07(\xed\x13\x17f.\xa4\x8e\x0b'\xd3y\xfdn\xee\xc2\x19E~\xd33\xca\x80\xa5.\xa8\xe2\xd2 r]\xd2[=s\xe0d\xba\xc4\xcfC\xfa\xf9\xd2\x85l\xba<\xae\xc5\x9b0\x14a\xf7\n\x804J\xcb\xed\xfbj\xbe\x03\x11w\xe3\xbd_Q\x94:&n\xbc\xbd\xfb\xefv%\xff8v%z\x82\xef\xbec[e\x9c\xcf\x92\x14\xbdU\xda$\\\"\xfc\xf5T\x07\xa6\x123@2\xcd\x8e\x99R`\xe7\x01\x1a\xaff.\xfc\xa2\x97\xf6u\x98\xfaiv<%\xf4\x18\xc9\xf6\xf0\xca\x99\xe8$\xfeF\xd8\xfb\x0c\xed\\\x84\xb1\xa9/(\xa9\xf1v[\xc2\x92W\xc4V\xe35\xa7\xb0\xc6\xaa\xb8%*\x8d\xcf\x9c5\xdf\x16\xd4\xb0p%\xf7\xb7[\xaf\x03\xdez\x1b\x85,8\ni\xd7?\xe7\xef\xdb\xf6\x10K\xd6\xebN\x1b\xb5\x9c\xf1\xf7[\x8e\x97\x93\xd6\xba_\xb1\xb6\x1elvb\xe1\x9dr`m\x8f\xea\x84\xb7\xd6\x1e\xd5\x05\x7f\xdf\x1e\xd5\x01R\x9a\x95\x8c\xbeYx\x89\x85i\x96\xccH\xde\xf2D?\xc4\"\xae\x98k\x16=\x85=\xb0\xf8Gx\xceg\xf6e\xab\xd7\xf7f\x89\xee\x13\xb4\xb0\xdd\x83So\xde,xM\x0f\xc4\x9aY\xda[dW\x1a\x9eW\xe0\xc8C/#y\x12\x9d\x13\xbb\xbdz\xf2\x83\x1e\x1aM\xf6g\x8f\x1ea\xa1\x1e\xccS2C\xfcr<(\x1b\x96x\x88\xfd\xde\x85\xf7z\xd6\xf7\xba\xcb\xd2\x83d\xc7\xf0\x14\xfdQU|\x1c\xdf\x8b\xb7\xe4'F\xd9\x1e\x9c\x93\xb8p\x98\x0fK\xb1 \xb1\xfd\xde\x919\xb4\xa2\xd3\xcd5\xcc\xfcb\xb6\x00\x9cCK\xf9\xd6\x06\xbf7\xbdsF\x15\xb5V\xa8\xbcf\xaf\xa5\xf4\xbb\xe6d*m\xb5\xcd\xe21\xd0a;8\x85\xe6h[\xe0r\xd4\x87\xed@\xe8\xb9\x88w\xa2\x95\x88\xd02\xc4\xb7\xea\x0d8\xe7\xb6\xcb\xc4;\x99\xa9k\\\xe95\xaa\xf2\xd3\xe0.\x89wr\xcex\xcb\x11`\x8c\x9a\x93\x9c\xb1\x97\x9b\x8c\xb5\xac\x05K}p\xc5\x85\x995\x02M`\x1f\n/y\x0f\x13(\xbc\xb9\x1f\xf6\x84@\x87*A\x14?\x1c\xfd\xd5#^\x9d\x02\\\x7fm\x9649H\x96~\x18\xab\x17P<\xfa\x13,?%\xa5?\x124\x1b\x19\xf3\xb5[PP\xf9 \x89)\xfck\x0fF\x8e+\xe2\xff\x94H\x81\xec\xa1I\xb5\x8d\x81*f\x1e\x89\x0b\x92\xd9\\\xa7P\xda\x19\xf2\xe8\x98\xa1\xd8#\x97aas\x06\x7fm\xd3au\xf6\xd0\x1b\x81\xdbX\xefCd\x1f\xd8\x16?w\x1b\xb3\x85\x1f\xc60\xbb\x9aE\xc4B\n\x08Ma\xde\xd8\x14\x82\xf7!d\xda\xd2\x18\xfdK\"Z\x9cc\xc9\x04\"[\x91\x1dP~\x1a\xe7\xb2wYp\xfck>\x9f\x1f\x9fDd\xf7\x84\xdf\xbc6\xe0#\x88k\xd9t\xf8\xc8\x01\xdf\x8e\xa7\xe1\xfaz[9 ?\xf4\x90\xa0\x90\xdc\xad\x8e\xd5\xc8\x05\xd42\xaf\x89}z\xa9\x1b\x93\"z\xe6\xb5\xe9\xf8\xbf\xec\xc5Egl\xf1s\x03\xfd,\x1eD[(\xc4\xe5f\xfbxB\xb5\x13\xa5[\xfc\xbc\xa3\x80\xa9J\xe7\x14\x08(|\xc0C\xe0\xf0\xa3c\xea\xed\xa7\xde\xdeV\x85_54\xca\x80U-\xfa\xb7l7,\x01S\x05\x87\xa9\xaa\x02\xdf.v\x0b\x9b\x92u\x0e\x00'\x01J\xf4L\x0d>\xfa\xc6\x9dz\xd5\xbbv\xc2T\x8er\xaa\xddu)\xbc\x93\x00\xaf\x10\xfcA1\xbd\xcb\xd6\xa0\xf0N.hA\xe1x'\x94\xa2\xa7d\x85wB/\xc81\xfe\xf2\xc5W\xccG\xfdd\xc6\xed\x0d\xe9Eqd\x17(\xc40\x8e\xfc\xed\xb0\x91\xbb\x15o\xaeV\xf5\xac\xc5\xdeI\xa0\x03\x86\xb8\x9e\x14*\xcd\xf9\x9c4\xd7\xaf\xf9\xda\xa5\x9d\xb1\x1b\xb0:X\xf5\xe5\x073\xb4\xec9\xa5\xa7\x19\x89\x87\x00\xc2\"'\xd1\\\x97?\x8f>\xb8\xceo\xd0\xbcj\x7f(\xf1\x04\x12\xaf\xde\x7f\x17\x9e\\L\xc0\x90l\xb1\xaa\x16h\xd3\xb2\x8aGC\x95\x8bg\x18\xc5\"\x0c(\xe9}\xfc\x16/\x98\x11\xde\xcd\xaf\xf8\xef\xbb$\x03^\xb1\xbe\xb2\xde\xc0\xdb\x86\x9b\xdf\xa1wL\x05\xfe1\x03\xff\x11\x85\xef\xd8\x855\xddx\x87\x8d\x93\x8f\xcf<\x91\x01\xfb\xd7\xb3w\xd7\xda\xf9w\xe7\xdd\"2\xea\x1d\x7f\x8dg\xfd\xd0x`\x17<\x82\xe7\xa1\x0b\xe2PX.X'\x0b\xcbq1\xd4\xa9\x0bY\x9d\xc5\xbau*\xd4\xe0Cl\x04\x13\xd6n\x05)\xe2\xcf\x16r1.\xfa\xabf\xfe\xec\xe6\x97\xd5_\xd7.\xbb\xc4\xf5\x93d\xd2>A\xd9\xb1\xbf\xe4\x9b\x97\xbd\xc9e f h?\xfc\xeb\xbcSy!Wf\x84b= \xa7i\xdeco?\x189\xf6\xa1l[\xdb\x1e\x1f\x89\x07\x84\xfa\x17\xac\xdc\x13{)v\xcd\x9cS\xfc=\xec)\xd9T\xa6\x7f\xc6\xb3A\x19\xacf\xad\x9a3G\xba\x97br\xce\xfd \x19C\xefb\xfe\xe7\xa4\xb5&\xb3*\x07U\xb5\xc6\"Y\xcc\x89\xdf.\xcbi\xd9\x11\x9f\xc7\x1a\x05\x93Xp(\xcd}n\x9e#\x04\x97\xbe(v\x92\xc5\"\x13!\x88q\xeaa\x88kG{\xe5\xd41\xb9\x80\xecQ\x17\xba\x04U\xc8n\\\xfa\x86\xdf(\xa8'}\x8b \xd5GNU\x84Z\xe6=v2\xb0D\x86\xe6SoNwy\x88\xb2\x98\xe0\xcdv\x88\xdb\x89?}JA\x93\x0b\x16\xf4m\x82\n\xf5\xc6$\xe7\xf6\xdc\xfb\x13\xac\xc3\xdc\xfb\x01\xff\xff\x0d\xfc\x11\xd6^\xb7\x01\xf2\x8d \x8a\x0e\x1b\x1f3\x13S[\xc6\x15\xdc\xfe}\xec\xd8\xf2+\xa6v\x90L\xe0Y\xc7\x87\x8d.%|\xd3\x9e\x1b]\x9e\xbeM\x16\x04\xd2\x13\x15f\x02I\xf4\xb4\xe9V\xdc\xbe\xc3\x14\x16j@\xeb\xacS=\\\xbb\xa4+\xbc\xf6\xda1\x8e\x1a\xf7\xbbo\xd8|T\x17v)\x0eG\xb5o\x870\x81>\\\xd7\x19\xda\x9a\xfd\x9a\xc9\xeb\xb7\x1fl\x99\xa2\x85\x1ez\xcc\xea\xd9\xc3\x13d\xbf\x97\xc1\xc24-?\x8a\xfa\xa6$\x93\xaa\xea[\x8fa-\x9d\xf1\x10\x8b\x86`\x14\xdf$\xbc\x8a^d\x13\x0e\xe7T\x05\x1e\x9d\x1a\"4\x03o\xd2\x90$\x1f\xb8~m\xa4\xa7\xb1\xce).\xa7\xd7\xc8p9\xeb9\x0f\xb6\x14\xae\xaf\xf7S\x80\xe8!a\xe8\x1f\x90\x98F\xcc\xcbP =\x9b\xeb\xebn--\xa3\x10\x81(r\xf8\x08\x01;\xa6\xa4E.\x88\xf4iy\xcc0\xdf\xc6\x062\x18\x99\x1d\xf7Q\x85Z\xa6\x198\x98KM)\xeb]\xeb\x8f|\xe8\xa1-Ub\x87\xde\xf9\xd0\x8b%\xf3g\xbdg\xf7\xae\x00]\x0f\xc5\xc9\nP\xbc:luw\xbd>v`\x90\xe6i\x93\x08jw a;\x90\xd9\x89i\x07$\x14\x84?o\xa4\"dB\xaf\xf6\xd4\x91\xc7\xb4\x1b\xb6]\x05\x8a\xed\xb9\xaasmo\x0f\x98\x84\x07\xc2\xb8f\x0dk\xa7\x8f\x18\xd6\xc1\x9a@\x18\xcf\x92,\xa3\xb7u\x18\x9f'34K\xd2\xb9\x9a\xdd\xdc\xbe\xb8\xa3\x02\x14z~\xb5;\xf7\xf6}\x95\x9f\xbc\xc2\x86\xbb\xe4f\x01m\xcdc\xce\x9bi\xdb\x02F,\xb0W\xe3\xdd\xac\xe5C\xc2u\x1c\xa6\xdd\x98\xbb\x90\xaa\x08\xa8\xc0\x85\x85\x0b\xe7\xae\xb0\x07Ia\xbf_2\xd4Y\\\xf1\\\xa30Ze\xff|\xc5|Fq E-p\xeb\xd4;E\x13\x96\x0e\xdc(I\xe6\xb3\x9b\xfa!\xa20\xd5>sT\xf3C\x9dJ\x802|a\x9d\xe0<\x82\x00\x1e\xc3\xe9#8\xd5Y\x9a\xa2\x95\xe9\x92\x07\x8c\xbd\xb2}\x9b2#dzz\xecL7\x8f]XLG\x18+\xf0\xca\xc6wN\xed\xa7\xba\xc4\x9f\xb3\xca\x0cu\xd9<\x8ej\x13X\xa6\xf7\xc1da\xdcq\xea\x11\xaca\x97\xe7^L.\x0b\xdbq\xbc \x89\x89\xc6\x1a\xb7\x1alb\x9f\xbbp\xe5\xc2\x82\x07\x82\x82b\xd8\xd0\xae\x1d\xef\xeb\xb7\x07O\xfeL\xc9ezq\xbd=8z\xf7\xf6\x15\xec\xc1l\xb5C\xb6\xd3o%-\xe07\xe90\x90JFW\xe0:\xd8\x87\xc2\xa6\xf7\x14.\x7f\xcc\x97\xbfh_\\\x15\xafk\x8c,I<\xd6\xacB\xe6\x87\xe0'\xe1\xaf\x90\xa1\xd8\xb0rhs\xdb\xfa\xc6?4\x7f\x0d^\xab\xae!QR\x1b\x99Hf\xa0M@7Y\x98\x0c3\x1f\xe1+*\xcd\x11\xaf\x11;cv3L\x8c\x87\x86W\xd3\xe4\x98\x0b\xf5n&:\x8d\x1c/a\x98\xc3NuY\xa1f\x0b?\xf3g\x05\xc9\x9e\xf9\x85?Q\xba\x94q\xfb\x9c\xde\x85H\xbd\xc0/\xd0j\x8aNe\xde\x03\xdfJ$\\\xf5\xa1\x9a\x85'\xde\xdc.\xd0TOA\xf0a\x82\xb4\x12\xb9\xe0\xaeK\n\xac\x1aX\xa5\x90\xe3M\x88\xa7u\x14nLo\x18\x89\xfc\xa4%U\xed\xde\x7f\x82Y\x9b\xde?\x9ef\xc7m,\x1br\x16\xae\xef\xec'M3y`\x13`,\xd4\xac\xd3q H\x04\xe3\xaaB:\x1d\x1c\xc5\xd3\x12t\xfc\x01\xb8\xf3C#t\\fg\xde\x1bX\x87\xcc{kP1\xcd\xc3\xd8\x8f\xa2\xab\xa1\xd2w\x9f+\x8d\x93*j0\xe5\x88\xc5\x1f\x1a\xd1{\xacSr\xab\x92\xd9\xb4\xd5\xc7\xb1,\xa7\xd4\x1ab\xf3\xcfJ\xcchj;m\xbd\x8a\x89\xcc\xeal\xb4\xfc\xa8\x8c\xcb(\xebF\xa9\x8b\x8f<.\x86`V\x1b\x96^u\xf9\x11\x81\xb7\xebP\"\x02\xf7l\xb7\xc0\xf1\xd0\x00\x88E6\x18\x08\xf1\"\\\x84\xb9\x01\xdcB\xa5}\xad\xd0J\xc7\x1eACwn\x0b0\xa9\x953\x8e\x1d\xa3\xd2\xa4_M=dAc{\xfb\xc1}\xae\xa5\x7f\xc0\xff}\xd8\x8cj\xc7\xc3co?\xe4Q\xed\x1e\x8a\xf7;\xfc_\xfe\xfdC\xfe\xfdC\xf6\xfd\x0e%G\xf0\xdf\x11\xffw\xcc\xff\xdd\xe2\xffn\xf3\x7fw\xf8\xbf\xbb\xfc\xdf\xfb\xfc\xdf\x07\xfc_\xde\xde\x88\xb77\xe2\xed\x8dx{#\xde\xdeh[\x19e\x8f9\xdb\x0eY\x8b^0\x1aw\xc2x\x87U\x90J\xbc\x92\x9f\xf2\x10\x8f]\x94(WJ\x02\x82\xfe\xc1-\xc8CD\x88\xe6\x04k\xcc\xd0}\x84\xf1V\xaa\xa0\x19Ul\x91\x0e\x82\x94\x1b\xed\x83\xd0:o\x9f+\xb4\xdc8\xe9n\n?_$\xed{\x0c\xbeVL\xc0\xa2\xc2\xed\xc1z\x9d\xc8\xcf\xc78; \xc5'\xa3\xd1h{4\x1a9\"v>C\x18o\xfd\xf8\x8c\xebH\nYG\xe2\x03\xa6\xb3\x84Y\x12\x10H\xe9dtv\x96\\i]\xc0W,\xba%\xecc4 \x0cy\xca\xa2_\xae\x83m\x17\xb0\xb1\xc7\xca\x1dx\xfc\x18\x10~\n\xf8\x0f0\xda\x1co\xc3:\x8b\x99\xd9\x9b1\x17$\xfc\xcb\xb3\x0c[\xb7\xc3a\xbd`\xa6\x8b\x1b4\xda\xdcR`+\x0dPd\xfe\xc5pP`\xb15\xbc\xcc\xbf\xe0LiX\xcbnM\xe0A\x81\xa7d`\x12\xc3c(\x1f9\xc0-\xb9x\xe4\xd6bZ\xae\xaf\x1f;\x18F\xe2+&kiV\xa8\xc1\xa6<6X\xab\xf9w\xb3\xf4\xea\xeb\x83\xe2\xacM\xc7\xb6\x8a,\\Z&\x85y\x9b\x9bV-\xaa`\x059\x15\xb2u\xbb\x01\xf7\xc2\xca\x8e&\xd6\xdf\xa6:\xbc\xd4\xf6\xc3\xf6{\xba}\xd6\xd4\x82u\xf0YD\xce\xaeXS$\xdb\xfa\xff\xd3Z%\xff\xcf\xfac\x9b/\x8a\xea\xaau\xa5/\xda\xb5f\x03\xb8o\x90\x85\x12\x8aT\xb2\xc0\xc7\x1d\x0e#S\x04k\xb2\xe6O\xc9\xb1\xcd\xbc\xf3~\xfb\xf5\xff\xf8\xb7\xff\xc2\xe2\x9d\xf2\x9fX\xa6l\xe3Zs\x8b\xd3\xb5I\x98;s\x89J\xbe9\x86\xe3\xed0\xca\x807\xfe\x97_\x82\x9dLcZ;GWnA\xfbR\x94_\xca\x07\xb9e\xf9\xd2Z\x809\xec\xc1\xcc\xa3\xb0\xda\xc7\xa0\x81\x04\x8er0eT\x05\x8e\x803\xef6\xe1jE\x96]-w\xc1\xc2\xbc\xeccM\x85HTh\x11\x1ej\xc1\x82Z\x0b+\x8fT\xaem\xfdX\xfc\x18\xffx\xfe\xe3\xfc\xc7\x0c\xfe\xed_\xff\xeb\xff\xf5\xeb\x7f\xfd\xd7\xff\xf3\xb7_\x7f\xfd\xed\xd7\xff\xfc\xdb\xaf\xff\xc3o\xbf\xfe\x8f\xbf\xfd\xfa?\xfd\xf6\xeb\x7f\xf9\xed\xd7\xff\xf9\xb7_\xff\x97\xdf~\xfd_\x7f\xfb\xf5\x7f\xfb\xed\xd7\xff\xfd\xb7_\xff\x9f\xdf\xfe\xf3\xff\xfd\xff\xfe\xfa\xeb\x8f\xe5xs\xfc\x00\xff\xff\xf0\xc7rN\xe6sk\xc8\x19\xbb!M9\xde\xde\xc1(n-vF\x8f\x91g\xe2\x8a~\xd2{I\x0b\xd5q\xafm\xf3 $r\xc3 \xea\x02\x8a\x8d:\xe1%(n\xb1,\x8f\xc4\x01\xe6_Q1x\x14\xc8\xe9\xa7[\x8em\x89z\x96\x81\xa6\x11u\xfaVJ\\_\xa1X*\x17\xe4\xf6\x95\xe76V\xdcg\xf0\x18F\xb0/\xa5#\x1e\x1d\xd7\x06\xcc\xcaV2\x96\xf1\xc7\x1c\xd3\xacl\xe9Iy\xee\x1b\x11\xf9\xddN\xd0\xe493 \x18~j\x0d\xbc\x82O\xc7\xcdM\xe1\xd1\x0f\xb3DM \xf7\xdc)a\x03\xeaK\xbbd6\x15\xf9\xef\x02O\xf7\xc7J\xde_\x06\x8d0\x9eEe\xc0\x82]\xe8@C\xd4\xe9\x03\x8d\n\xed\xff\xa7D\x02\x8e\xba\x07\x0fS;\xbd\xc6\x08\x91\xab\x80\xc3\xed\x0ecc\x99\x06\xe3\x8e\x8c\xa4\xc4/&x\x83\xef:+v\xd9\xb7_\xa3\x91\x96\xb6\xb8\xa9\xb4\xb8\x0e\xdcO\x99`\x05x\xa3\xc0E\x91\x89>\xe4\xf1P[\"S\xf48\xe5a\xfaC\xd8\xdb\x83\x11\xdc\x83M\x05Ca=M\xca\xb8\xa8\x1d\xb7br\xe6\x17\xe19is\x12\x0f/\xc9\xdd\x0f\xbd(>\xc9\xd8\x93\xb8\x98%\xd1\xc78\xb2\xb4i:|\xd1\xfc\xc7<\xb6\xb4\xaf<\xfc\x99|\xbcY\xf0\xd6?\xe6$\xc2\xc2\x8f\xc2Y\xbe\xd2\x1c\x86L!\xfc\x14\x80\xb42\xf2\x19\xb4\xfa\x88\xf6\x17\x19\x99\x7f\xe4\xa5\xcf\x97~\x14\xad4\xfc!\xa3\x17\xad~\xf4\xc5\xa7\xef\xdf\xaf\x06\xfc\x83\xc6/\x9a\xfd\xf8\x13(O\xef~\xf4\xe5'\xc1\xfey\x99~\x84\xa1\xa7w4\xf4\xd8\x1e\x8d)\xb9\xbc\xf4\x8b\xd9\xc2rad\xae.\x0dfZ\xd5S\x8a?\xd5k\"\x1e\xc1\x19\x10\x93\x921\x91e\x0f(z\xa8\xd2\x99\xc5\xd3B\x9f\x19C2\xafO`_\xd8\xe11/\xaa \x9a\xc0q)o\xecL\x8bc!\xc8\xcf:qA >\xbe\xe1jrQ\xa3\xe5\xc2\xf8\x06\xeb\x99)<4`\xd0\x92\x86}K\xea7\x964\x93\x974\x1b\xb8\xa4\x12?\x91a\\\xb3\x04W\x95\xbd\xe1k\x19:,N\xd3\xdd\xadhN\xfc\xec\xdf\x01\xf4\xee\x963\x8d\xc2B \x9e\x1d\x03K\xfd: \x0dGl\x8fw\xda\xbe& D!\xdd\xd7L\xef\x86J\xb4\xae\x90\xc4\x9a\xa1\xf1\x8a\xe5\x9f\x9e\xce,\x9ew\xe2\x9e}\xea\xfc\xf1\x9eC\x99\xe3\x0f\x1f`\x1bu\x1e\x05\xc9\x8b\xba|\x7f\xe2\xdcsac$\xc2:\xd1zc\xac\xe7\x9f\xca\xb5|lH\xaa\xc4\x1a\xf3\xea:\xde\xbeC\xffkT\x92\xcb\x1d[*\xa3\xdc;-\xaf\x8a\xbd\xfd\xaaP\x05r\xe7\xdc\xf7Y\x12\xa8\xde\xb3\x9d\xfd\xfd{\x1e\xb9$3\xdb\xb2\xe8\x1c\x15P3DO\x02\x92\xad\x9a\xd0]\xaa\xe3\x06@\xd3'gOx!\xf14<\x95%\\;\x95\x8a\xfc\xedZ\"\xa7_\xab\x83\xe8\xe1\xe8\xd4\x9f\x9d3K\xff\xdc\x85\x08\xc3T\xcfY8}\x93\x93z\xc0B}\x86gq\x92\x91\xa7>\xc6\xf6\xb3B\x0b&\xf4\xda\x83uZ\xb6,\xa3\"\x8c\xc2\x18\x8b\x96\x8d\xa22\x0eQ\x11\xbf\x0fV\xd9(\xc8\x8bp\xf6\xfe\x8a\xbe\xbf\xe2\xef\xf5CX\x98}\xe4\xcf\x9b\xbbY\xc0>l\x8f\x1fn?\xdc\xbd?~\xb8\x83\xe6\xfe\x8f\x1f?65\x80\xd1g\xeb\x03O\xbc\x1c\x83\xa3\xbb\x10\xc0:Xg:\xfb\x01\x94\xfea\xd0\x06t\x8e\x90Z`J\xce%o\x876\xf2\x85\xbd\xbf\xf6\xe3\x8f\xb9c\xb9\x10\xa84\xd4\xd5\x83\xfe\xeeK\x06\x8b<\xbe\xe7\x9amG\x18y\x0cE\xcd\xb0\x0e\xf9t\xf3\xb8\x82\xf0\xc7\x80\xf1\xd5\xec\x94\x07?\xe12\xa5\x85+>p\x1c\x17\xd6\xd0\xb6\xbf!\xf1\xc2\xa4!\x9b\xc7\x95F.s\xcd\xe4O\xe3\xc1\xa9\xcf1.\x01\xcc\xe1\xab\xae\xe4{\x03\xc6\x8f`\xbe\xbe\xee\xc8;S\x8b\xd8\xe6h\xe8k\xe3\x8f=\xa5D\xbc\xf1\\;nw\xf0|9\xbe\xaaC0\xa2]\x00s\x14J\xe9\x07l%F\x0e\xcf.!-\x1b\x8b1\x1f\xb9\x90V\xad\xee\xc1\xb9\xe3|\x00\xbec,\xa3O{\xfb\xe8\xa0\xeb\xc1\xc19\xecC\xca\xcb6]8\xc7O:#hY.3\x8f\x06kS\xa0F!\xd3\xdct\xa4\x15\xb3\x07a\xb6\xe6\xa5\xd9FW\xb0\x0f\xd3c\x98\x08\x1cT g\xdb\xdc\xa0Z\xcc-\xd1\x08\x1a\xa2\xeb\x06d\xd5\x8d\x08\x01\x89\xac\x8ak\xb2*\xeb\x90U\xb1\x8a\xac\xcaV\xa5\x03\xcc\xf2\xfa\xd4\x8e\xed\xedQ[\xec\x9c\x88\x92q\xbb$\x14%;\xed\x12\x9f\x97\x8c\xee?h\x17\x95\xbchgk\xb3]\x94\xf3\xa2\xadNO\x11/\xb9?\xden\x17\xcdz\x03\xf7U)\x98\x88wrB\xf2\x97IPFD\x97C\x14$\x99\xff/\nW\x10\x8c\xbb\xc7r\xe2\xe9B\x99\xd5\xf9\xdex\x0c\x86v\x8a!o\xe1\xe7\xaf/b\x91\xbe\xb5\nC\x17s\x95\x0d3\xb6 \xdd\x84oP\x83\x10&\xa6\xf3\xcb\xa8\xe0\xa1\x99\x9a\xa0A7e\xbb\xb3Ts\xae|q\x1e\xfd\xa1z/\x96\x0eR-\x8b\xdaY;\xcc\xf4<\x18Y\xa3.E\x92\xd6Y0\xde\xdd\xd9\xdd\x1c\x05-E\x1b\xbdv\xad-o\xf4\xc0\x1b\xb7J\xe8}j\x9d\xfa\xf1OI\xab\xe0\x8c\x16\x1c\xfa\x85\x0b\xe3\x1dxR\x9e\xc1xs\xf4\x006\xefOv\xc6\x93\xf1.\xfc\xe9\xe5\x91t\x10\x86\xe9\ns\xb1\xf4\xde9\xc9\xf20\x89s\xbc*;/?|\x80_\xae]E\x89\x97_\xf8gg${\x17*\x9d\x97x\xb5 (\x02\xdd\x9e\x85\xc5[r\x1e\xb2\xf2\x85\xb2\xfcY\x98\x15W\x13\x08\xba\x85\xa7e\x18\x05G\xe1\x92\xe4\x85\xbfL'p\xd6\xad\xb2\xf4g\x8b0&\x93v\x0c\x85.\x07Ph\x1d\xaf\x82dy\x12\x06,\xcf\x94\x1ao\x06\xc9\xf2U\x12\x10S\x95<%\xb3\x89\xde\x88*\x8b&J5,/\xccMMG\xfeUR\x16\x13\xb0\xbe\xf6s\xf2\x02\xff\xd0\xb4\x14$\xb3\x83\xcb\xd4\x8f\xd9r[Q\x98\xebj.\xfd\xcbg,\xf5( \x8e\xfc3c\xff\xf30*Hf\xaa\x81\xe6\xa4~\x91d\xefp\x9e\x8b\xa2H\xf3\xc9\xbd{IL)^\x01=^\x98\xdc\xab*j\x86\xc5|\x97r\xfdB\xce\xca\xbcH\x96\xfar\x9eO\xf5uJX\xea\xaa\xe7A7\xa9N\xab.\xcfz\xf4\xac\xd4%\xbb\xaa\xea\x13\x92\xbe\x08\xe3\xf7a|\xa6\xaf\x94\xb1\xd6\x9e\xc7\x05\xc9f$-\x92\xacOc[\x7f\xc9\xb0\x97\xb2\x82f\xba\x19\xc9\xd3$\xce\xc9'\xea._$\x17\xe8\xd3M\x02\xbejj\x073\xa8q\xeb\xcb$ \xd1[\x12\x07$\xc3u\xb3\xc8\xa5\xbfL#\xa2\x83`\xe9+\x04\xe5\xe0\x19I\x8b\xc5\x04\xb4{R\xd7\xcf\x87|@\xa7ppY\x10<#\xb9~\x1fi\xbd\xa7\xc9r\x99\xc4\x83j\x97)\xc5\xc3$8,O\x97a\xc1\xa2M\xe4\x13\x98Zg\x04\xd5.i\xc9\xfeIr\xfc\x97e\xd1\xa5\xbf\x92\x94nU\x8e\xfa\x01\xe2\x07X\x89\xcb8\xad\"\xf3g\xc4\xd20\x9eiFrR\xd0>\"\x81\xb0u51C\x17\xad\xa9\xa9\x10\xc6a\x11\xfa\xd1!\xddX\xfd\xd1\x9a\xc7\x86c\x99,\xd3$\xa6|\xcb\xa4\xed<\x05jp\xa2\xfc?%\xd3\xe7^\xeag99D\xb9Y'M p\x82\x89x\x1c\x057\xf1:OF\xac)\xa5X?\xe5\xdd\xf8b\x8d\x1c\x9b\xdeq\x05\xd2\xde\xb1\xa2\xb7+\xed5\x91_\xe5\x05Y\xaa\xc8\x08\xf1T\xd8+\xf5\xf8\xcfU\x0eW\xb5M\xa9\xc7\xf7V\x03kl\x9b\xda\xb3\xd2\x8eJ\\\x1ff~U\xd4J=\xf6K\xdd\xb7x\xc4\x95\x90z\xec\x97\xb6\xb2f\xaeP\xdf\x98\xc6~X\x1d\xdd\xc5)\x1e\xbc]S\xaf\xcc\"\xfd84;\x01\xa9'C\x7f\x97@V\xc4&\xe8\xfb\xa4\xa2\xa7O)=\xdd\xaa\xdd\xfa\xbbEZ\xdb\xa7HRK\xfdS\x15\x9a\x078`\xb2\xdc#\xa5\xc0\x86\xb0\x073\xc7\x85\x13/'\x05\x1bCn\x97\x8e\x0b\x17\x02;=\xc1\x99\xe7^\x94\xf8\x01 0\x8fI\x9d=\x9d6\xb5\x16\xd3CE\x7fZ \xf2\x84\x16KQ\xb0\xe9BX\x8f\xb2\xc4y3^p\xd3\x85\xa4S\"%|ck$:.\xd3\xc0/\xc8\xbb,\xb2-\x0b\x07\xd6-|\x91\xf8A\x18\x9fQ\xe8/s\xdb\xca\xcb\x19\x06~\xd1\xd4>L\xc9\xcc\xa6\x83\xc8:\x83\xc0d)\xcdo\x82\xe4\"\xa6s\x07\x0c\xea\xc1g\xaa\x1d\"\xd6\xe8\xf4+\xda\xe0\xc5\xe8\x81#6\xc0\x81\x0b/C\xd2\xa7\xde\x14\x17\xac'i\xaa\x93\x97V\x91J\xb0\xfeI\xa8\x0d\xcd\x0f\x1c0s9\xb2\xc6\xdfK\x92] \xf8\xab\x9b\xd0\x8bR\xab\xe1\xe5bXj4\xc9\xa3\x89P\xe0\xc0T8\xbceL\x06\xd0x\x89`\xf7\xe1\x03\xf04\x1e\"k\xc7\xe1\xfb0MI\x00YM\x07\xc6 \xfc\x0bk\xe5_ \xc9\xf07\xfd\xf8_\xe0\xc2\xcf\x11\xed\x87\xf3\x90\x04\xbau\xe2x\xe8\xa2\x8b\x18\xba\xe7\xeb\x92bB\x0e\xf2L\xa6\xc8~\xbf\xcb\"\xa5\xac\x0d\xe5\x98\x8dM\xee\xbc\xa0G\x9b\x9d\xa8\xaf\xaf\xdeq\xb0Y3\xd6\xf8\xf0\xc1\xd8\x82\xe2\xfa\xc6K\xed\xb2;\x1d\nlo\xc92)\x08\xfb^M\x81\xab\xd8\x90\xd4\xeb\xbeU}\xa9`)\xe8\xa7\x9d\xd7M\x1c\xec\xc2\x01fb\xb0\x8d\xf3\xbc\xa4\xd5\\\xb8\xa0\x87\xf1@r\x03\xba\x96\x91,\xe9\xa5E\x1c2\xe1\xd8\xde\x19=\xe88\xf0\x8ev\x1c\x8f\x8b\xfd\xde\x93\xab|HC\xf5\xcau\xac\xa0\x99\xb6\xf5\xe1\xae4\xe1\xd8\x1e\xef\xdcwx\xbaM\x03\x95\xd1631\xbb\xed4\xb3s\x03\xacnX\"/C\xb3\xa3J8\x18\xdb;\x9d\xc0\xb0\xb5pq\xd2\x9fb\xb3\xb3\x03\xdc\x83\x1b\x1d\xbe[\xfbp\x7f\xdb\xf1\xe6rL\x94!-\x0e\x9cD{\x9bn7\x89\x9d1\xf3\x07\x1f\xdd\xe7~\xe4c\xeeW>\xbe\xaf\x04\xaf\xc3\xab\xe5i\x12\x0di\xbb\xd7J_\x9d\x8e\xb7\x13\n\x83G\xe9m\xe7\xb2\xe4\x913\xda[\xca\x83\xf4\xee\xb4\x83\xf1\xf2\x19\x8c\xb7\x1d\xef\xcf\x07\x7fk\x96\xb1\xd4\xa1;\xed\xf1\x88\xcc\xa1\xed\x011\x81\xf6\xc3vX\xa1\x94{\x87\xb4\x8d\x13x\xea\xd0\xb6O\xc2\xa2\x82\x94\xe6\xfbs\xfe^\x9d9tg\xdc\xae/2\x87\xb6'\xcc\xb2\x86n\xb5G\xc3R\x86\x8e\xdb\xb5Y\xc6\xd0N\xdc\x87\x0b\xbe\x9a\xed\xb9\x1e\xb0%h\x8f\xf1\x92Wo\xcf\xf5\x90\x8f\xbd]\xff)\x1bL'X\xca{\xb6\xe5\xed\xd7O\x04Bj\xbe~\x0d{\xf0\xb4\x9d$\xf4\x0d\xec\xc1\xfb\xf6\xcb#\xcc\xfb\xd9z\xf9\x12/\x08\x06\xd7\xcd\x92\xe7\xd5\xd5\xd1|\xff\x13\xec\xc1sJ.<\xafQz\xb3\x06\xbd`\x02\xdb:Y\x84A@\xe2\xb6\xca\xff-+-\x927Y\xb8\x0c\x99\xbfM\xb3\xc63\xd4\x03y)g(\x9f\xe7\x07q\xb9d!\x91\x9b\x15_\xd0\x1b\xd2\xb6r\x1c\xfd\x06c\x05\xb3\xabvs\xef\xe4Z\x9dd\xc6\x7fg\xa5I\xba\xa1\xa9\xf0\x0d\xecu\xb4I\xcd\x1a?\xeb\x02\xc2\xbcl\xd6\xfb\x1aW\xf4/\xac\xb1f\xd1\xf7\xb0\x07k_cf\x88\xaf\xa5\x8c/\xad\xbf\xbdy\x18\x07O\x17a\xd4R4|\x0b<\x82odvr\xe6w\xce}X\xdb\x83K\xfb\x0d\xf2fh\xd7\xab&\xd0\x87\xc5\xd8\x82\xba\xe17\xb2\xad\xb0Y*\xc2\x93,\xdf\xd7V\xbav\xbcn\xd0#P\x8aA\xae\x9dv\xddkG\x0eg\xa3\xb1]\x03 !\xbf\xb6\xbfQ\x9b\xd3d\x92\xac\xe2\x9biq\xec\xc2\x9b\xaa=\x1e\x10\x92 \xb7\xf9\x0d\xfd\xf9\x06\x9b\xe9\x04\xc0\xbf\x86 \xbcin\xd9\x0f\xbd|\xbb\xe0\xd9\xdf1\xaf\xf1K\xfbe\x0d\x08&\x1d%fL\xef\xaa'\x9b\xdd\x7f\x07{\xf032\xc5\x0c\xea\x1bP\xeb\x89\x9b\xbb\xb1\x88\x06\x80R4B:\x0b0\xa8\xa5F\x94\xfd\x97\xa6\x19\xfcm`l\x80\xaa\xe1=\xb1I\x7f\xb3\xff^m\xe0\x15\xcb\xe2\x02{p\xc13\xd6\xd1w\xb4$\xb1\xdf\xa1\x91\xc4>\xc6\xd7\xa9\x10\x10f\\\xa5\xfd\xbdby\x85\xa7\xaf\x8e\xa7\x053s\x11\xbf\xf7x\x0e\"\xdc\xb4Xw\x10\xea&)\x17\xb1\x89\x89\x8bT\x90\x0d\x93\xba\xc3\x0f\x1f\x18\xf4\xbdr\xe1\xc0\x1ea6uJ\xa6\xd4\xfd\xd2\xe1\x7f[\xad\x06\xfd\xb6\x86V\xd3b\xfey\x88q\xc8\x95\xd2\xf5\xad\xd6\xbc\xb3\xe0\x1fK\x9e\xe8\xb3\xa0CKXj+\x16e\x97IP\x98\x1fe\xf2\xc8\x81\xbf\xa1\xfe\x1d\xc3\x05&\x18\x06\xa60j\xdf\x8d)7\xfe4\xf88=k\x18\xaf\xe0\xc6\x13\x96\xaaP\xdb\xf3\x1a\xd6\xae\x01\x08A\x83\xe5\xf7\\K(0\x11f\xc1e\xaf\xd9\x05\xa2\xec\xda\x17\x9f\xff\xf9N\xfc\x16%\x0cz\xe8o\xbay\xe4\x18\x0b\xdbv4\xcd)~1d\x8f\x98\xdd\x05]\xff.\\\x0b)\x11\x89\xa9\x9e\x94\xff\xc8\x11{\x82\x87\xcd\x17\xb3\x8a9\x04\x7f#v+dSz7-\x0c\xe70l\xce\xaa\xae\xf73nmi\xdb/M\x81\x0d1\x08\x14=N2\xa2\xef&\xc4\xb0\x18IZ\x87{\x92\x92\xd0w\xf2b\x9c\xf3\x8cj\xa9\xca\xebw\xb3\xe1\xf5\xbb)\xf9\xe6\xbb\x9d)6\"B*\xaf\x13\xe0Y\xdajl\xc0SZ\xfe\x9d](\xcd\x03\xce\xfe\x9a\xbe:\x16\xf8\xc2\xae\x8f\xbc\xb8'\xbe\xad\x0d\xe9\x10\xa9\xab\xd2\x1d]+\xa5|H\xf2}O\xff\xf7-\xdd\xc3N.@\x18\x14I5\xa7T^\x8bXp\\\xf8\xa1\x99\xeeM\xce8h\x15I\xe5\xe3\xdd'\x04)0C\xdf\xfb?\xc8M?\xc5\xa4t_\xb8\x94E\x81=\xf8\x1bF\x90\xdby\xe8\xe0_\x87\xf8\xff\x7fF\xae|\xbc\xc3\xde\xfd\x89\xf1\xe8\xbb\xec\xaf\xbf\xf2\xfc\xc6k\x94\xdf\xdc\xc6e-\xe9\xfc-\x15\xc3`\xb9\xf4kD0\x0b\xfc\xbaWR\xf5\x83\x1d4$2t\xc4\xbe\xedc\xaa;\x1fS\xdd\xf9,[\xda\xcf\xed\xf5f ;\x91\xe8\x16Y\\V\x1d\xe7\xbfPva\xe1\xe7\xcf\xf9\x01p\xc3\xfci\x12\xcf\xfc\xe20\xcd\x88\x1f \x9b#(0\x17\x9d\x85\\n\xbd\xeb2\xd7\x0c\x97\x07\xe8u\xd1\xde\xd3\x958)W\xec\xcc\x91\x7f\xe6\x96q>KR\xda\\.LC-\xd7\xa2\x17\x01a8\xe2/\xf5!!\xe4\x91\x03\x81\xfd\x97)!\xcd\xb4\xe65\x12\"\x98\x8f*\xf0\xf2\"\xc9\xe8\xe5\x12\xf3V\nR7\x13\xd3f\xce\xed\x82L\xe3V;t\x05\x0f\x1bk\xc7Ox7B]\xbf\xfdG%;{Ao\xb5\xf5=\xb47\xdf\x87\x17\xf4TM\xd8?{\xdd\xe4\xea-\x04\xfc\x9e\\}\xd3\xdf\x15Z\xe0\x7f\x87\x16\xf8\xc6\x9c=>0\x1a\xb8\x83\x9b\xa0\x19<-\x8c\xe1\x85ZCA{z\x81t\xdc\x9e\x9c\xba\xc3H\xc6\x9799$\x05\xaa\xb1\x8d|\xda\xf7\xaa\xf0\xc0\x9d\x96\xc2e\x1a\x91!-5\x93\xcd^w\x8eJk\xa3\x19\xc3\xdb\x8dq\x84A\xd4\x07$+\xedZ%\x17\xb0\x0f\x976\xa6\xa5\xfc\xb3}\xc9h\x1d\xe3f\x07d\x1e\xc6D\xa8\xa8'\xf07CqH\xf2 \xfc\xb9Y\xe1\x8c\x14\x92\x8a\xfb\x19\xc9gY\xc8\xd4\n_\x98*\xbe\xf2\x97\xb4\xb1\x7f6\xd5a\xc7 \x9f\xc0_\x1b\xeb\x88\"\x96\xe6b\xdakx\xc5\x1a\x98|q\x11\xbel\xc7<\x16\x8c\xda4.\xa3\xe8\x18c\x99\xfdd\x0b\xba\xd3\xfa\xe5\x9a\xbf\xe9\xae\xbd\xdf1,m}\xc26\xb7\x851\x1d\x17\xac\xef\x0e_\xbfR\x04\x01\xa9\xb4\x0c+\x10?\x9cd#\xc7\x8c\xa3\x18=R\xc5\xe0\xa1,\x05\xa7\xc9\xea\xeb>ib!\xf1\xf0L\xde\x9c \x1a\x1d\xbb`\x9f\xda\x9d\xa4n\x9c\xc4\xffN\xf6\xbf9\xe3\xd5\xecb\x089.\xfaRJ\x87X\x987\xa44;\x06\xf5\x8eK\xfb-\x1c\x0d\x1a\x00\x0e$t\xect\x1a.\xfc\xc4\xb5*\xcf\xbb\xc2\x87\x06XIB\x84\xe9[$\xc6c{g\xd3\x91\x85\x0b.\xbcm\xd4cI\xb6^\xcf1_\xe8\xcb\x1aq\xb3\xbf\xfdb\xe1\x82E\xff\xb1\xf8=;\xe7j\xa6\x1a\x06\xd66\x07\xa9\x00j\xe9xG\xca)\xa2B\xa9\x93\xd8QBaU\xbd\x94\xe0\x073e\xda\xb7\x98\xc5\xe5\xed\x1a\xce(2HV\xa0\xea\xbb\\\x00O\xf1\x11\xed=\xf4\xe6,/\xcb\xe6#(kH\x8d\x1e9\x90W\x16\xe8\x94`/\xa7\x11\x12\xe5HN2\x10V\x1f`Ia\xb8\xda\x8av\x84\xdb\xc2\x9b\x90\x92]\xdd5\xfd\xe5\xda\x13\xa4D\xb3\x10\x83\x03\xd5\x86\x14\x02\x96/\xc28H.P\xc9\\\xfd\xe2BS\x05F\x84}C\xa1\xcdZ\xa0\xb8]v\x8b\xab\xb5\xa3\x83\xa88\x0c\x8akM\xd9H\xe1\x07l\xf2\x18G\\\xe58\xeb\x95n\xe9\x93\xd5T\x04\x88\xca\xda\xaa7\xf9\xbb\x18\"w\xf4Q4\xd1<\xc06\xcf\xbf\xdc\xd4\x14\x0e\x02\x00\xa6K\xb1-?\xbf\x8ag\xcfWR\xc8\x89OY\xfa\x12\xa4\xa5\x07}\xa7\xd6|\x15\xde\xe9UA^\xb0#0\xe4\\F\xdas\x89\xe9\xa5:%\x19\x96\xb4}:\xf9Ro\xd1\xdb\x13\x83/9p\x0f\xb6aC\xe2\xcd\xaf](\xbc\"\xf9\xfa\xaa <3\x9catm\x9e\xfd\xa4\xb0\xe7\xce1|\xf5\x15\x8c\x1e\xc0\x87N\x11\xac\xc3\x88\x17\x8f\xd5\xc5cV\xbc\xab.\xddr\xe8JL\xf3\xf5u\xbc\xa60\xb2\xf2.| \xe3\x9d\x9d\xf6\xfb\x07\x9d\xd7\xe3\x9d\x1d\xf8\x12Z\x89\xa4\xc6<\xc5\xb5\xb8:\xd5\x93\xd1\x0c\x96\xce\xe5\xf1c\xd8\xeev\xd2\xc2\xb6\xa3A\xbd\x8c6\x8dK\xb6\xad_\xb1\xc7\x8fa\xa6\x87wZ\xb0u\xfd\x12v\xb7\xe8\x0bko\xcfB)\xf7\x98\xb7\"\xf6\xcbf\xed\x8cq\x1f\x1e8\xb0\xaemx\xb4)Z\xa6\x80Q\xb5\xcc\xbb\x1aK]Y\xed\xa1\x0b)L7\xdc\xf4\xb5\x82\x7f\x16B\xc7D\x12>Ze\xcc8\x8f@N\x0f\xfb.\x8c\x8b\x07l\x1f\xf7\xe5?&,\x9f\x0b\xdb\x14\xeb\xc9\xd7O\x9f\x1d|\xf3\xa7o\x9f\x7f\xf7\xe7\x17/_\xbd~\xf3\x97\xb7\x87G\xef\xbe\xff\xe1\xaf\x7f\xfbg\xfft\x16\x90\xf9\xd9\"\xfc\xe9}\xb4\x8c\x93\xf4\xefY^\x94\xe7\x17\x97W?o\x8e\xc6[\xdb;\xbb\xf7\x1f<\\\xbfg\xf1h\xdc\x0c\x8f\xf8\x95t\xbe\x84\xaf \x7f\x04\xeb\xeb\xa5\x03\x19K\xc6\xedOK:\xf0\xa9/\x83r\xe9`,c\x95[[\xa4\xc7\xea\x02\xd8\xba\x84U\x01\xff\x01\xb6)\x1a\x13\x8c6E\x9e\\\x16\xf8\xc1vn\xc2\x84!f:^9mfw\x1df:\x8c_g\x8cB\xf7S9:z\xc1v \xa6\xff\xac\xef\xc1\x96\x83\x00c\x13\xba\x13\x14\xe5P\xec9\xda\xbd?\x1a\xed>\xd8d>\xf6\xd3\x92\x9e-\x06\xe9\x14\\w\xc6\xbc\x84\xa1\x0fV>>\xa6\xac\xb9\x80|;\xc4\x8cZ\x08\xff\x0f$\x98\x0f\xf1\xcd\xb8\xfdfWz\xb1\xbb\x05_B\xd8\xe6\xa9*\x8a\xa6{\x14\xaa_\xc9\xd4\xda\xb0d\x08\xdaD\x08\xda\x1dS\xd0\xb2NTE[JzC^\xcd\xc2\xcb\x88\x1f(T\x81<(\x8a\x02\x0cCW\x10\xea\x0f\xe0\x8f\x90PZ\x80b\x06\x85`\x94.\xfc\x88\xaek\xe9\xa8k\xa0\xbf>\xaeY\xb7\x8c^\xcb\x1b\xf7\xbb\xef\xd1~\x06\xf6\xb1\xe3\x11LT\x01\x0bR^e\x83\x96+\x9a\x0e\x10QR2a\xde\"w\xb8\xc3\xfe\xfa\x1e\xa4\x0c\xc3\x04\xf0%\x9f\xc3\xc6\x8cM\x02\x02x\xfcx\x0f6f\x94rX\xa7'\x18f\x18\xd8\x14\xeb\x8fwv\xe1\x8f\x10\"\xc2d\x1d\xb8 \xda\x9b\xc1\xc6\x1e\xcc_\xf9\xaf\xb8\x8c\xa7\xc0\xb6\x18x\xec\x83\x8dY\x04D1o\x92!\xef\x19j\xe9}\xd1\xd6R5\xcf?\x85\x0dX\x1c\xc3\x87=\x18\x8d\xe9\xc1:o\xddp7b\x8a\xb9\x10\xa4)\x9c\xb6\x0b\x17\xac\xda\xac\xb5#B\xe5\x96S\xb2\xb1\xab4bAj^)\xa3G$\xbcd\xac\x8c+\x81%[\xaa\xb8\x12X\xa2\x8a*A\x0b:_\xe4\xbc\xa0\x13l\x82\x99\x9a\x8e\xef\xb7U\xaf\xcc\xd6\xb4mf9\xc7ff\xad\xb7)o\\\x11\xe6\x82\xd9\x9a\xee\xec\xb6\x03]/\xaaO\x1e\xb6?\xe1\xf6\xa6\xe3v\xdfK1\xb7\xce\xac\x99\xc5\xa9&\xa0\xc3\xd5\xa7\x0f\xe8p:D\x1a&%\x1bm\x82\xca\x89IU_M\x8b(UA\x92t\x9e\xb15J\xe5{\xed\n\xb8\xd6\x88\x0d\xb4y\xdc\xd5\xcb\xab\x82\x7f\xb4\xdc\xc9\x84a\x8d\x8b\x05i\xbb@-p\xcb\xcd^\xc1\xbd\xce\xc5+\xb8\xcd\x9a\xbc\xe3L\xde\xc7\xd0\xf1@\xd6\xd7\xcb\x92\xa4x\x1eS\xd4\xd1S\x11\xe7\xfdF\xccN\xe1\xd4\x0c]M\x99xN\x932\x0e\x0e\xc5\xc45\x95\x8a$\x89N\x93K\x8d\xc34bz4\x00\xa8\\\x18\xe9\x1d\x81\x16\x01\xd5\x1b\xef4\x8c\x03\x1e\xf0\x87\x95\xa1\x82\x99\xdd<{p\xeaVn\xd63\x14r|w\xc8\xf6\x9ayUr\xe1[\xb3\x93\xfe\xb0\x85\xe2\xa9\x18s\xda\xfe\x99\xc7\xf6\xf9hQ\xc6\xef_\x86A\x10\x91\x0b?#\x8e\x1d;\x86\xc0i \x06\xf2\x12\xe1FNN\xde\x1e<{\xf7\xd7\x93g\x07\xdf\x1f\xbd~\xfd\xe2\xf0\xe4\xe0\xafG\x07\xaf\x0e\x9f\xbf~u\xf2\xf4\xf5\xcb7\xaf\x0f\x0fNNP\x87\xc7\xbcGsE$\x1c\x90\xc8\xc6M\x97\xd6D=\xe9!\xaa\xdd\xf9\x84\x12;b\xfa\x9ez\x98\\\xffS\xa5*wTf$6?\xaf\x8eXk\x0cO\xc2\xbdK\xd1\x1a\x05\xdfVN\xb5\xf8\x17?\x1e:\xadRk\xbce}$\x89\x0b\xd3\xee\xba\xbf'W\x13\xb0\xe8f\xd1\x19)\xdc\xa2\xf9\x05gTCC\xcb\xc2\x04a\xa6;\xdf\xe6\x90U\xe8\x81\x8dFLx\xc0hz}l\xd7\xd4\xa9\x07txp\xc4t\xb0\xf2\x0b=\xb0\xc9y\x80\x81\xd8&\xd0\x16\x0f\xe5}\x18t\x879\xa37\x1cJ\x91b\xc09\xfe\x1a\xc5JNC\xdb\xa8\x06KU\x9b\xdf\x94\xf1\xac\xf1-\xb1\x0b4\xa0\xd5y\xf9\xaa\x1aQ\x8c\xc0[\xfai-:\xd7jW\xe5\xa7\x1e@\xc7\xde\xb5\xfd\\;^F\x82rF\xec\x0b4\xa35\x0f\x957\xacA\xa0\xc0t4mTg\xeb\x02\x00^p\xfc\xc5qU\x8c,\x01\xb7\x06m\x1cH\x85\xfe\x03\x9a\xd7r\x1f\x00\x08\xfcF\x9b\xd6O\xf1\x9c\x07\x17U\xc0\xedX\x0b\xb7\xe3\xe6\xfd=>\xeeq\x0d\x07Nd&\xde\xc2\xcf_\xa0\xb7\xb6yD(T\xd0W\x19\n\xd3\xa8\x07T\xa9\xdf\x0b\xcf\x9f\x17${\xc1\x9d\xa7\x91\x83X\xdbt\xe1\xc0\x96J\x1cY3\x1f\x9bB:\x9a\xcf\x84\xdc\x0c?\x1e}\x1e\x12\xd52M\x14\xd9\x9f\xc5c\x82\xdc\xbb=`\xcd\x99dB\x18\xd1\x7f*\x07\xcd\x03\x00TY\x80\xeb\"\xfd4\x85\x95\x18\xb0z\xd3\xc5\xbb\xa1\xad\xf0\x18T\xba\xe3\xd13\x02\xceG\x16\x82K\xe2o\x06u\xfe|9\x81\xb9XZ}\xb5\xb7\xc4\x9f\x15\x93:H\xa2\x1as\nn\x8cqi\x12\xcf \x18\xc6\xe5\x96p\xce\xa7u{p\x92\x07\xa9\x8bX5xdw9\xb0\x01\xc2\x82!c\x87\xce\xf8\xbbo\x0c3\xcaW\x99\x91\x96\xb7Q\x0c\x14\xf6\x14q\xf7\x06\x0f\xab\x894\x07\x0c\xcdxE2b\xc4p\xef {(b`\x0bLmW\x97\x18\x9f\x99,.a\xbea\x8c|JN\x7fz\xe9\xa7\x0e\xbdA\xfa\x97\ndZ\x89\xf1\x18\x99fW\xb9\x87V+\xd6\x0f\xa9X\x93\x9a8\x1bB\xe6\xf7RH<\xc6-F\x82&\xd3\xf8x\x85H\xe0\x82\x10Y\x91\x0c\xe9J\xf8br\x013\xef\xa5\x9f\x9a\x19\x05\xe0\x84\x89\xcc\x15\xf7s\x93k\x99)\xc2\xb0\xfc\x08\x93\x80lZx\x94\x1d\x18\xd0x/\xa3\x0d\x12'u`\xc7\x8e\xc9_N~\xf8\x88\xab D \x97\x0c'\xc6/\xf5\xac(\xa8\xc4\xbe\xed\x07aO\x0d\x95\xc8\x0f\xbbm\xa8,\xe4\x08X\x9b.\x04\xde,Y\x9e\x86\xb18M\xb9\xc3r\xea\x9f\xf6&\xc97\xa3\xdf\xa3\xabt\x88L\xa8W\nC\xa6\x9b\xc7^\x91\xbcKS\x92=\xf5sb\xa3\x11P\x15+\xbeW\xec\x86\xa7\x9e\xcd\xcd\xb1\xf5H\xa2\x1aP\xacH\xe7!?\xe7<\xb6y\xac\xcc\xf8-\x1eTT;\xf28\x92&}\x9c\xc1:\xc5u\xa1\x9aU\xba\xcd\xa5L\xc9\x13A+\x0f\xd8\x80!\xb72\xdfN\xdb\xca\xab\x86o7@N\xef\xdfbx\x02\x915\xc7\xe7\xf3v\x07\x82\x05^\x06d\xc5\xcb\xa0\x03T\xc4`\xd6\xa2z\x1a\x02\x06\x8a^\x1c\x13\xa0\x14\x9dL\xe0\xf2\xa3a\xb5o ?j\xeel\xc0n\xf5\x9ef\xba]\xc3\x98\xd1\x06_\xa8\xf2W\x07\xdd\x86\xc6\xcd\xfd\xe8\xbfpi\xaf*\xac0\x8d\xeb\x0c\x0e\x1b\xf7\x9dc\xef\"\xf3S>\xa4\xdeK:\xe3\xf8U\x03h\x03\x04\xbe\xe2\x0e\xca\xa6q\xcf\xb5\xc6\xbbD\xe3K\x14\x10 A\x91\x9d0\x1f\x17\xb4UL\x8e\x1d\n]m\x9ad\xc8P@Z\xaa\xde\xa3\xd9~\xc4\xbd\x88\x87\xa3!\xaci\xa9:\x14Q\xc4t\x8fB\xbf\xd8~\x90\x90\x90\xcfY\xe6\xc8\x16\x89\x92\x87\xb2\xb4\xad\x10\x13\x12\xe4P$\x954\xaa\x96\xd2\x16\x0b\xbf\xe0\xafs\xf0\xb1\x91\xaa\xcc\x0e \x14\x0b\x02\x17\xec\xe4\x00CD\x8e\x0e\x11\xc9\x0f\xef\xe8\xc0\xcez$\xdd<\xf0\xe67\xbcO)\x88\x08\xbd\xafM$\x82\xb6\xf8n\xf1\xc4*\xd7\x8e Q\n\xa2\xce\x8c,\xb26\xb2\xa8%D\xfd\x01\x0e\x9a'S\xce\xa5\xa3J\xe7%?\xe2TN3 9<4)\x16A\xb87)qL\xc2\xd0J5\xf8^\xc4\x12v\x10K\xb1\xc2\xf0A\x16\xcaO\xb3a\x88\xc5\xef\"\x16\x9f!\x16\xb4x\xf5\x99M\xaa\x82\xd9\xe9\x1d\nH\x14\xd5\xca\x88\xa5\xb2\xbe\x0d\x15\x1c\x0d3Mb\x83\x0d\x1dn#\xcdlr\xc3GP\xae\xaf;h\x0e\xdd\xe0M\xca\x9e\xe5\x10\x8f@\xf1\xc8\xcf\x990\xda\x94\xcb\x8b\x9e\xc7v\xe2\x1cS\x8e{\xe6\x17\xb6\xaf \xad\xdb\xcfM\x10\\hBp\x02\xc0~?\x0c\x17\xf6\xa1\xb7\xc2\x80\xde\xd4<\x0e\x08\xf4\xa6a\x81n\x87\xdeP\xca7\x08\x99\x0d\x90\x94fM\x0b\x17\x15.X]^\xd0\x14\x08\x10\njL\xec\xad^\x0e\xf7v\xe2\xbe\xa6|\xfd\x1fg]\x06#\x16\xc1m\xb3C\xabr\x11\x15\xcf\xf5G\\\xe3o\xe2\x01K{c\x99\xe5\xc4+\x93\xc7z\xeaV\x83\x92\xaa\xb05<\xb6\xf9\xbe~\xf4\xd0\x96,\x8b\xb2[m\xce\x9d\xd2jJz\xaa\xd2\x98T\x14\x99\xb3\xa2\x84EEa\xf5RFz6\xb0\x97\xc1\xe1-\xf4\x1e/\xf9ix\x84u\xc9\x8f\xb0\"?2\xa7\x8a\xe6\xe4\xc3W\x90=\x02\x9f\x92\x1f\xe1\xd4o\x92\x1f\xfe\x00\xf2\xe3\x9c\xa7C=\xb0cAl`*$\x0d\xa9\x11\x1a\x93W\xf2\x87O^i\\\x81\x89(m\xd6c\xe9\xd8\x85\xcd\xa2\xca\x1b\xdb4X\xd7|\x14q\xc5] )\x08\xc6\xe6\xfa\xf0\xa1\xa3\xf1\x13jt\xf5R\xcah\xca\xab\x85[\xed\xc8\x1d\xe2Q\x9f\x18\x99\x84\x1f\x80nl4(<\x0d\xc5\xbc\x9ff\xc4\xa7\x07\xcd\xa9\x10\x17\x90\xc1\xa6 \xd2\xc6\xd7\xce\x8b\x85\x99\xcd\xe8k\x1a\xe4\xeb\xb4\xe8\xb3\xe1\x82\x017\x9b\xfc\x08\xe9\x1f\x05\xfd~\xf8\xd6\xbb\xff\xb7\x1f\x94(\xdeB*!\"\x06\x0cZ\x1e\xe0\x1d\x0e\xabI\x1f\xba5\x138\xf7^\x1d\xfcpr\xf4\xed\xdb\xd7?\xbc:9x\xfb\xb6_\x03#\x1e\xcc\x80\xa0\xcf\x92\xa5zR\xff*J\xfc\x80\xa5\xf8Y\xc8j\x84AM\x98\xb5\x1bX\x03\xe6a\xecG\xd1\xd0-\x12@\xd5[\xd9\xdc\xb5\xc9\x02\xb0p\xb42\xd7[b\xaa\x97~\xca(\xe8\xe4M\x96\xa4C\x90\xd5\x10\xf9\xb7\x11\xcf\xf4\xb6\x04M\xac\xd2\xb2\xe3!\x03H\x9a\xdb.\xc93\x8e^\x87\xaf\xca \x92q\xd8\xb2\x0c!\xee\xec\xa6\x87\x02\x8a\xe5\x0dVL\xc8\x81\xd5VG:P\xea[\xb6c\xfam\xf5\xea\xdaV:\xaa\\hCG\xddZ\xc5\xab2\x02-\xd4\x0d\x9b\xac\xa2\x1b\x0d\x8fT\xde!\x0dA\x860\x03\x95\xb4\"\x83\xea\xcbF\x9a\xcd\xea\x05\n\xd8j\x96\x04)\x9a\xd6\xd5\xd6\xaa2\x80Z\x15T*\x91\xc8r\xe6\x1a$\x91\xf0*\xf9\x1a\x067\xe8H\xe9\xf7\xc1n}\x89&\xb6\x9c\x8c\x9b\xc6\x14\x18x\xf4\xea\xf6`\xa7\xd91\x86\x95\xc1yu\x1b\x99&.\xc4\xc7\xc6\xaf\x9bp\xa7\xd0\x19\xb7\xbe\x91\x13\xfdk\x9a\xd5\xba\xee\xcb\x8c}w[\xdb\xbb\xaa\x8a\xa1Y;\xddC\x18\x9b]B\x98\xa261$\xe5ow\x18V\xa9\xa3\x1aoe\xd5\x8f6\xc2.\xc8\xb2\xd5a\xca\xa2j.%\x9d\x8b\xdfG6\x9c\xf3,K~\xaf\xa8\xb2 `9\x93\xd6\xd2O\xa7\xf9\xb1+$\x9fye\xb1\xde\xd8\x96\xee\x9bir\xac|)O\xb2\xb7\x02\xed\x13\xe3z\xf4Ub\xf3\x13\xb0\xdfW\xdd LU_\xf2}\x88W\x8d\xf4I#2\xa1*J\xc4\x81>Z\xc6\xaa\x9e$*\x9c\xe9xQr\x86\x02]\x850$\x96\x93\xa9\xef1Ij\xcb\xf7\xc3D\xec\x0b'F#\xb1\xa0'\xa3\xa5\xb0\x98*N8\xab8\xe1B\x84\x12\x7f\x04 |\x05\xc5#H('\x9cQ\xf8\x92W@wb\x05\x82GcpN\xa7\x13\x17\xa6\xf4\xba\xaf\x00&SY\xae\x0c\x8d\xe5\x85\x11C\x9a\x19\xc3\x08\xcfE\xd7\x036\xd7\x7f\xe8\xfe\x92\x13\x8d\x9f\xe0\xdb\xdeX];[c\x85\x17\xb0\x9c\x14\xa9.U\x07\xc8S{\xca \x9dE\xdbI\x99\xb4\xa3\xca_\x0f\x19g=\xae\xf1\xa64\xdc\xcc\xce0\xcce\xc6b\x86\xb2|7\xda\xb8\xa1\xedX\x9e\x98+\xc5\x9b\xd7#q\x86\x0c\x85.\xd9\xb6)\x87\x94\x9f\xe7\xe1Y<\xa4\xa9\xfeY\xe9'\xc3z\x99`\"\x98-g\xc59\x98\x93\x0c\xc9\xa7\xf2Z\xbd\xfb\xd9\xed{\xa1\xeb\xd8\xf6\x9ef\xb1\x055\xc1\x1a\xb7\xd4\xb9\x8cv\xb6\xdaYyJ\xcc\x1aP\\$O\xf8\x01\x7f\x93$\x11i\xa5{\xc3Yx\xf3\xa4\xccL\xb5\"\xd8\x83{?\xde[\xbfw\xa6\"\x86gZ\xbfi\xdb\xb2`\x1d\xd0\"\x13MG\xed\xc8\x05\xeb\x8b/\xefYf\x94>W\xca>Q\xd0C\xeb\xf0\xfc\x1c\xf4\xcfY\x12\x17\xe4\xb2`1<\xf9\x9b2\xa6\x7fo\x1a{Hu\xe7Ul\x0b\xc1\x9e\xba\x18_\xd0\x9e\xd8m\x0b\xd33_\x99\x84\x19\x0f\xb1\x81\xac\xaf\x9bg\x1aHaI\x94\xf3\xcdH\xce\xf0\x98\x98\xf1{r\xf5&#\xf3\xf0R\x9a3_\x94\xb8\xb3(\xd9J\x8b\xb2\xe8_\x146\x9c\xee\xb2\xf8XZ\x8d\xad[\xa14\xaci.\xafi\xb7\x98\x02_\xc9\xd66o\xadms\x03\x9a\xc4WD\xa9\xfbs\nq\x19\xaeo\xe8\x15\x0b\xbfx\xcb\xd4\xac\x02\xd8)\x05\xcf\x13\x9e\x02\xcb\xe1\x98xa\xfe\xbd\x1f\x85\xc1ADh\x0d\xda\x0e}\x1f1\xc6 Jb\xf2$\x0e\xde2x\xfe3\xb9\xa2\x1d\xf8\xb0\x0e\xf6ZD\xe7\xcf\xe2\x9e MF\xff\xa2T\x01{\xbf\x0f\x96\x05\x13\x98\xd9\xf8\xa7\x03\xeb`\xdd\xb3\x1c\x0cU\xe8\xb8\"\xf0n\xe4\x98\xc1\xe5\xdc\xee\x0f\xcf\x04{`Y\xcd\x85\x113dq\xb9h\x8d\x19e\xc0\xd9\x10\xba\x1c\x03\xdd\xab\x802\xd2\x88\n\x02\xbb\xc0([\xd8a\xb3\xb2O\x87\xb3p\xa1\xa4\\\x92\x97\x91\x88\xf89\xb1K\xf3\x1c\x96=We\xe3\xce\xaf\xef\xf4\xb9\x14P7 \"\x95\x81I\xcd\xd88\x1a(\xaco\x9d\x8e\xc6\xcb\xce\x01\xa1\x9b\xe2\x07\x01]\x830>;J\xec\xb9\x98\xe8\x8d\x06R\x1dd\xa9W\xf9,K\xaf\xefp\xcc\x81\x0by\x8b\xae9\xeb\xc8>\xe7Iv\xe0\xcf\x16\x93^b\x06\x84-7\xb3\xb5\x96\xa2\xac+\xec\xc5\xabk\xb4 I*\xb7f\x84\xa3\x94\x85\x84\x9aWp\xd4\x8e\xc3\xdc\xc4\x0cK?\xfdH\x03\x9e*\xa8`\xfe\x15\x9e\xbf\xcc\x15\xbb\xc0\x9c\x8f\x8diJ\x96~\xfa<.\x92\x1f\xc2b\xf1g\xb1\xdb\x98?5\xf6\xa3 \x9c7+\xe3\x8e\x0e\xd0\x00\xf2\xd1\xe0\xb2-\xd9h\x8ckU$\x88\x12\xfb$y\x82\x95\xe8[\x80B,\x80\x1a\xa5vRg\xd5\xf0\xa9\xa6\xa2\xce\xf0\xed-\xa9\xa8\xd1f\x9b.\xc2\xc0\x7f\xb1\xfd\xc0\xe9\xb34\x16)U<\x91R\x85B+g\xa3\x86H<\x9b\xdf\xa5I\xda\xa3\x83b\xa7\x17\xfdjY(\x16Epr\xdd\x06\xc4\xe4\x02\xbf\xef$gP\xd0\x8a\xe6Y7R\x85\xd1&1)\x8fm\x8dw0\xc7\x85\x84\xdb*\x1fN\xc5\xfaPv\x92\x16\xa5I\x12\x1d\x86?\xd7n\x9d\xcd5\xa1\x97\x9b9\x9d\x04\xa5 \x92.\x01\xdb\x1d\xb7\x8c\xdf\x06\x9c\x15\x90\xc5`\xc6m\x89\x1bc\xe61%\xe3\x1a{\x01g\xf0}\xfa\xb6\x9a/K\xc7T\xfd\xb9\x07#L\xc6$\xb0\x18\xec\xd1\xbbS\x91\x9bIAZ\xc6\xa4I\x83O\xda\x0bB\x9f\x0e=?p\x0dn\x02\xe4 \xad\xddJ\x80\x0e*`\x8fyl~\xd5r\x80\x12\xe6A\x05\xf7\x9dT\x15\xa0^\xceb\x91\x91\xce\x82\x0e\xb90\xe0\x96\xab\x95\xdd\xc9je\xae\xf0\xcb\xeb\\1\xe2\x19\xbe`\xcax\x1e\x8a5\xeb\xf2\x81\xdd%3\x98\x91\xdcf\xd5\x92;Y\xb5\xa4Z5FM\xa8\x9d\xc0VZ\xb8NB\x88n\x0b\x9a{\x8d\x99k|\xac{m\x9b\xa5Z\x1e\xef\xdeW\xc5\xa2\x8b\xed\x9d\xadv\"]\xbf\xbe\x10c{g\xbb\x13^\xaed\xe5\x0f\x1d\x17,\xaf\x9d\xc6\x95N\xc8\x9aX\x9ax\xc5\n\xc4#\x08-\x0c \xd2\xcdx\x80\xef\x05cB8\x8b\xe4{$\x9f\xf9)\xb1 c\x92&\x18Z\x9e\xe5Q\xb0\xb7v\xdb\xd22\xb8\x990\xae\xa2\x06y\xdc\xccj\"\x84\xc7w\x9a\xb90\xd7\x11H\xa9\x8bq\xf2\x84\xb9F\x1761_I#05\x86\x91\xfd\x12\xacSz\xa2\xfcX\xbc\x12YP\x90|sk\x07F\xbcd,\x16\xab\xd9\xc27X\xd7\x8a\xcb\xe5)\xc9\xe47\xf5\xaa\xf2.\n\xef\x8b/\xf8\xc8\xd0\x15\xb2\"wg\x94{)\\\xca\x83\xb2\x00\xcd\xfbP\xc2: \x05\xb2\x89L\xb0\xe3\xc2HM\x13/0\xc6\xa5\xf2\xc8\x9c#\xb3)59\x81\x18\xd6A\xa1y\xa1\xab\xd2\xe4\xcf\x0b\x8d\x06\xa1\x92j/\x99\xc4zII\x8c*\xbc\xf6r}\xdd\x81\x05\xac\xef\x01\xb1S\xba\x0f\xd3\xe5\xb1\x0b\xe78\x97\xd4\x85\xa5\xc3w\xaf;\x02Ml[\x90\xd8\xa2P\x99\x8d\x10\xf8\xf0\xcf\xfaP\xd8\x95\x8b\xd1\x04\xcf8m\xd7\x13Z\xe6\x0c\xc1\xa0\xf0H\\d!\xe91s\xa9\x16\xe5\x84-\xca\x9a}\x05{p\xea\xc5\xe4\xb2\xb0\x1d\xc7\x0b\x12L\x1d&-\xcc\x15K;#\xad\xcd\xc9\xfa\xba~u\xc4CW\xa9\x7f$\xda\x01\xe8\x17H\x91i\xd2\x8e\xe1\xae\xcdSU(\x92P\xdd\xc1\xca4\xc7\xca\x0e\xc2P\x0e_\x0d\xc6\xd6\x9e5\x01koS\x03\xc1\xd6\x04\x8b\xc7V\x17J\xb4\xf2\x02\xeb\x0b\n\x93\x1d5\xc0\xbd\xe9\xde\xe4\xf8\xdeY\x1fc.5TL\xc9q\xb7_#GY\xc6w\xb3(\x9b8m\xdd\xa2\xec\x8di\xf1d\x95Ea\xcba[\x1e;\xccd\xba\x89\x1az\xbaV\xeco\xd4D\x13//O\x19\x15`\x8f\xd1\x97Pz1r\x1ci5\xed\xbd\xcd\x0f{c\xe7\xee\x17\xb4\x86W\xf5\xd9\xb9\x13\xfd\xd7\xfd]\x87\xc7\xe8\xfc\xc6\x9f\x15Iv\xd5=\xc5\n)\xc0\x84\xa2H\xbfM\xa5b\xd1\xe9i\xc6JOO3e\x85 \xc8H\x9e\xb3:\xec\xb7\xb2ZFx/\x19Qw\x94\x15\xe1,\"\xbc\x0e\xfeVV\xcb\xc3\x80W\xa2\xbf\x94U\xca LX\x15\xfaKU\xe5\x14\x8bO\x95E~\xce\xda\xa7?\x94\x15\x82\x90\x95\x07\xa1\xba8\xe1\xc5\xea\x9e\xc33V\x1c\x9e)\x8b\xa3d\xf6\xfe\xefeR\xf01T\x7f*+'\xc1\x15\xab\x96\x04W\xca\nl\xeb\xd4\x1bwZ\x16E\x12\xb3\n\xf8SUi\xe6\xc7\xe7>\xdb\\\xf6S])\xa5\xe0\xcak\xe1oe\xb5\x90\xcf\x8a\xfePVH\xf8\xd6\xd2\x1f\xea\n\x11/\x8f4\xc5gYR\xa6\xa2\x0e\xfe\xa1\xaa\x18\xf8\x05\x03F\xfaCW!\n\xf3\xa2\xaaD\xffPV\x0cX\x95@YH\xd8p\x03\xa2\x1cn@\n?\x8cr^\x05\x7f+\xab\xcd\xd9\xca\x06s\xe5\xaa\x06\xa1\x1f%\x0c\xa6\xd8Ou\xa5s^\xe3\\Y\xcc\xc7\xa9\x1e&_\x05\xe5\xfc\xc9\x12\x0b\xc9R]xJ\x02^~J\x94K4\x0fI\x14`\xd2\xe7\xcc\xb6\xc4\x1f\xea\x8ag2\x98\xd5\x7fj*\x97\x19\x11\x15\xcbL L\xf3$\xc1\\\xb5\xff\x1f{o\xda\x1d7\x92$\x08\xbe\xdd\x8f\xf5+\x9c\xf1\xaa% \x03\x0c1H\x89\x94B\xa2\xd8J%\xb3[\xdd\x99\x92FRVMw0\x8a Fx0PB\x00Q8xdQ\xef\xf5\xcc\xec\xdc\xf7\xee\\=\xf7\xd9\xb3;\xf7\xb1\xc7\xec\xce\xf4\xf4\x87\xce\xfc#\xf3\x07\xf6/\xecs3w\xc0\x017\x07\x10$\x95U\xbbo\xf1\x81D\xf8\x05wssss3s3Q\x08^\xe9B\xc9R\x16I\xc81.\x86\x90\xbd\x18\x92\x99\xdb\x98\xb9Mf\xee`\xe6\x0e\x99y\x1f3\xef\x93\x99\x0f0\xf3\x01\x99\xb9\x8b\x99\xbbd&\xf7qB\xc4\x8b\xad\x80\x04\n\xbe\x92\x85\xcaU\xb6\xb0\xae\xb1\x85l\x85n![\"\xca\x89\x17\xaa\x00\x92X\x92\xc0\x06\xf3\xc4_\xe2\xe4\xe2+Yh\x89K\"X\x92\xeb!\x88V9\xe2\x1c\xbc\xd1ERY\x80\\\x95\xefO\x10\x90\xefOH8\xbe\xe7\x97\xa7\x1cQ\x15_\xa9B\xa1\x7f\")\x04\xbc\x91E\xf8)\x8f\xf0K\xf8J\x16Bh\x85$\xb8\xc2 z/\xb3\xa3\xf7T\x81\xa5\x1f`G\xc5\x0b]`%\xf3\xc9\x89^\xfa\xc9{\x99\x9f\xd0\x1f\xe0Q\x8e\x05x\x94\xdb\n\x04\x99$%\xea\x07]P\xd2m\xf1b) \xb1\x17\xde\xa8\"\x91\x8f\xa40\xf2IR\x18\xc5\x18M\x19\xcb\xc8\x1fTA<0B1y\xac\xa5\n\xe1\xf4\xd2\xdbU\xbc\xca\xca\x85\xa4~X\n*\xba\x17[i^\x9cg\n\xa7\xf1\x95*\x84\xdf\"?\xb2\xf2\x13\x1fg\x00\xde\xc8\"\xc14StU\xbe\x93\xc5T\x11[v|Zp\x8c\xea\x07U\xf0gP\xe2gTV\x82\x03I\xc8\x91$\x08\x85\x84\x84@\x92\x9f \xcf$^\xa8\x02\xd8/\xb2C\xa9\xbf\xc4\xef\x8a\x17\xb2@\x89:v\xc4I\xf9\xb4\x98N\xf9N\x17\x0b\x15~\xe1+Yh\xe9\x87\x88b\xf0F\x16\x89\xf3d\x8a\x13\x82\xafd\xa1\x95/;\xb4\xf2\xe9\xdedI\x1c!I\xc5W\xba\xd0\xa5d\xe0\xe1\x8d,\x92#\xeb\x9d\xe6$\xf3\x9d\xe6\xcb\xa5\x9f\\\xca\"\xf0N\x17\x93\xf3@\xaf\x97\xcc?\x91\xfd\xc80R,Q\xa4\xe0\x9d3\x1b\xf3\x9c!\xd9\xcdH\x92\x9b\xf1\x8b\xac8\xd2\xa8\x1fdA\xc1[`)\xf1F\x16Y`\xfe\x82\xceT[vf\xdb\xb3\xb3@n\x87\xe2\x85.\x90)x\x887\xb2\x08R\xcd\x8c$\x99Y\xe2O\xdf\xcb|\x7fJ\xd2x$\xf0$u\xcf\x11As\x12;\xcf|\xfc\xf0\x99O~\xf9,\x98qW\xfc\xfa\x9c$\x11<\x0c\x83\x95<@\xcaw\xaa\x18\xae$\x9a5Y\xfa\xa7\x92\xbb\x11oT\x910\x88\xb0\x84x\xb1\x15\xf0\x93_K\xfcY\xc0\xa3\xac(Z&Q\x95\x96~\xaa\xf6\xf1\x94\x9c\xe3\x95\x82\xd0\xca\x02\x9d\x95\x9fe<\x89T\x19\xf1N\x16\x8b\xc3\xcbSI\x00\xe5\xbb\xadX1R\xf5\x83*(\xc6\xe4\x87\x95\xd1V\x93\xc8J\x8a\xb8&6\xd2\x9a\xc5\x92\xc8d1M\xec\xcf$=<#\xe7Q\x10\x85\x82:\x90\x05\n\xa2\x9b!\xd5\xad\x94\xb0\xc8\x88P\x05{\x0b2\xa2\xaa]f\xb5w2\x1a\xfb\xae\x1e|\xac\xd2 eMv\xc3~\x18\xc6\xd7\xf8\xe1\xba\xe95j`)\xfdk\xe4\x0c\xeb\xe1\xb5r\xd9\xf7zq\xb4\xa8\x7fp\xff\xbeeL\x8df\x1f\xcal\xe3&\xf2s&\x8doi\x19\xba\xfa\xcaT\x94x\xf2\xc4\x8f\xe2\xe8r\x19\xe7\xe9\xd3\xa7\x84\xa8tn\x95\xaf\xfah\x99v\xe6\xf4\xe0\x8dB;\x06\x82#\xc1\x98\x9e9\x85\x12\xd5RN\x0c\x17\xca\x15\xe3\xb6\x14Dm*\x14\x95\x8aUKA\xc55\x9f5q\xcd\x0c\x19\x8e@0\x1cg\x8eR\xde\xda\n\x02\xd0\xb1 \xbc\xda\n\xfa\xd1\xe5\x88-\x9cD7\xb3{ \xdab;(_\xcd\xdb\xe4\xdd\xeaQ\x9a\x9c\xaa\x7f\x1fk|\xcc\xfaS\xd3wh\xb7\x9a\\\xdd\x94b\xe6\xf4\xd4U\x13\xf6u\x8f\xf5!8j\xefk\x16\xcf\xcbx]\x98\x91`\xc6\xc2OY \x03\x16\x8b\x9a\xef.W\x9cEq\xe6\x83\x8a>\x88\xd2`\xc6\xd5P\x07m~\xb0\xce\xe4\xbd\xc0\xac\xd5\x99#\xdcn\xad;[k\x83\x01\x93\x9f\x00+F\xc7\xef\xee\xf4CBF\x05f\x16\xc3\x8f\xc5\xf0\xeb \x12 \xc5\xb4\x14\xd3\xd2|\xb5\n\x03>cY\xacC\xcdc\xfcb\xc5\xa7\x19\x9f1?B\xe8\x0c\x08g\xb1\xfa\xd3|Q\xbfP8\x87\xa8p\x0e\xd9\x13-\xc8u\xd8\xefw\x05\x0d\xdc\xd6p|\x8f\x85\x05f\x89\x1e\x8fE\xdfC\xf16\xe9y,\xef\x0091AS\xddf\x11.\xe5\x95\x16\x0e7\x18,ey^\x7fl>T\xe8\xa5\xc8q\x93\xea\xe0Q\x80\xdd|%\xae\x89\xe4|\x0d\xc4\xce?>b\xe7\x9d\x11\x9b\xa5At\x1ar\x8c\xbf \xd9\x80\x9ba\xf9M&\xde\x16^Ja\xe8\xf7J\x887\x1cp\xba\xa6\xad\x0e\xdey\x8e\xf1\xeeN\xe4/\xc1\x98\x95\xb8\x9fC=y\xab}\xb1\xedA\x1c\x1cL\xe3\xa8\xb8;qu\xc5\xaa)\xd0\x9bri\xb7c\x9fz\x94\xd1\x99\xd1X\xa7\x16>\x00\x14\x7f)\x90]\xcd\xa4\xa8\x0e%|(\xf1\x8bCw\x0b\x17\x05\xfa\xafk\x12\xb9\xc6\xbbL\xf5\x07\xd0f\xe9\xf0q6q\xeb\x0c\x86>\x01I9\x01\xb1\x05\xd8\x91IY\x80\xa4\xbc\x8cg\xbc\x95\xa3\xb8 \x0cm$\x03\xf9\xca\xef\x95`\xfc\xc2875\xd6V@\xeb\xbbZ;M\xea\xc6\x81UL\xba6*\xf1\xec\xd7_\xcb\xebpd\xf8\xcd\xd61k\\\x17\xf8\xa5h\x1d\xb6\x18\x90?X\xf8\xe9\xab\xf3\xa8\xb8[\x1ev\"\xfd\xac\x99A\x1b\x00\x83\xd6\x8d5c7e\xcf\xd8/\x80t\xc5\xd1\x1a[4q:\xd0<\xe5\x18\x07\xb4\x06\xbb\xbe\x9b-\xdd\x02A\x8a\x95\xa1{X\xe6\x05\x83\x9e\xeb\x17\x8fm\x8f\x18\xd4J\xcc<\x07\x7f\x1e:\x8c\xdb\x97\xa6Xp\xbf\xf1\xf6\xd5\xcb\x01\x9eu\x83\xf9\xa55\\\x80z\xd6\\i`\x1f\xaao~\x1d\x96Z\x1c\xc1\x8eY,\xcf\xa6\xfd\xf2\x1a\xe8\xf2\xee\xb2\xdd\x9cL=\xb7\x862\x157\x1f[\x8fYV\x99\xe9\xac\xfd(\xa6dAb\xef\xec@\x1f\xa9\x9d!*:\x1e8\x1bC\x8f\x15\xb3\xa7\x9c\x87T\xe6\xa6\x80\xd5\x80\x1d\xd6\x8f\xa5\xb0},\xf8\xf4}\x01\xc6\xd4c'y\xc6\x12>\xe5\xc1\x19\x9f\xb1_I\x99\x9f\xb1 \x9a\xf1\x0b\xf6+\xe9\xa0\xe7\xb1\x13\xf4\xed\x05\xf7\xa4k`\xb3\xcf\xee\xf7\xb2\x04\xa5o\xd1r:\xfc\xf6\xe9`\xda\n\xe2\x9d\xbc\x8f\xeaWX\xd3jo\x05\x81v;QG\xd6\x99\xc6vY\x9f\x96\xa5x{\xeb-]t0\xddT\xcf\x0d\xa7\xf4\xff;\xac\xc6\xd7\xf8\xc5\xaf\xd7\xe44:\x1d\xe0\nfa\x1cv\xc4\xd9i\x97f\x99lz\x0en n\x85\x0f\x99\x17\xa0\x9e\xb7\xd6i^\x12\xdd\x16\xcc\xed1%\xfc\x02BK~oX\x9fv\xc6\xfa\x10\xb0\xbe\xee`\xae\xfe\x18X\x1f\xde\x00\xeb\xc3[\xc7z\x85\xc2>:\x93\x04\xfe\xa9\x8dk)V\xca\\\xac\x94N(-J\xaf`\xa5\xcc;\xae\x94\x8d\xd5zpz\xcf\xe5\x99l\xdeL\x8e\x8f\xa2O\xfdY\xa1\xc2\x10\x195\x9e\x0da\x80\xd7\xf9{L^\x139\x8a@\xd3\x06\xb7J\xc8Z\xfa%\x13\xe5\xa7K\xd6\xef\xb0L\xcf\xe4\xa5\xb2\x95\x93zln\xae\xf6y\xb7\xd5.\xe0\xb6(\xc0\xb6\xf8\x05\xadc#\xf5\x83vE\x92\x99>\x87(\xfcQR+y\xfd\xef\xa0pR\x7fu\xc5\x86\xec\x1ed\xc0K\xc6F\x8c\xc3\x85I\xb8\xed\x07\x0cZ\xa5\xb5\x0f\x96o\xcfhJ\x02\x17g\x97J\"\x81\xe8\x84\xe2=\xf0\xd8\x1c`\x92\xa37\x1ep\xb1\x13#+\xfa\xdc\x0f\xc3 :-D\x0e)\x83\x95\x03\x8e\xb9\xd9,H\xf84\x0b/Y\x90\xb2(F65N\x04\xd18\xb9\x84\xc0*_\xaf\x92x\xb5)\x88N\xfa5[\xf9\xd3\xf7\xfe)\x1f\xb0\xafR\xce\xbe.\x1a\x1c\x00\xc3Z\xfct\xdc\xaf\xc5:\x9b\xfaa(\x9aX\x0e\xd8\x1b\xee\xcf\xd82N\xb8\xe0\\\x17Y\xb6\x1a\xdd\xbb7?\x19,\xf9\xbd<\xe5\x9bP{\xb3\xfc\x8eu\x91hx(f<\x19\x07\x13v\x007+\x8b\xcb\xa1*\x0d\x89\xc4\xbb\x05/\xcf:\x15\xa2\x19\xa4`\xe5(\x18\xef\x94%\xfcgy\x90\x80TQ?O!\xdf\x1dd\xa9$\x067b\xdc\xa9\xe0H\xdb\xa5k\xa6+\xe61\xbc3\x92\xa1\x0d*\xb4^\xba\xd6B\x1co\x10\xd7\xdd\xd5#\xc6\x10c,\x91\xa4\xdbm\xee\xa4v\x9b\xbb\x8b\x10\xe11\xdb\x80\x10\x91A\xed\x16ucMV\xeaBb\xbcB\xadM\xe4\xd0\x0e\x9a5nvS}\xea\xc8\xf5\x82\x17\x9f\xae7\xbbAx-\xf0cc\xe9\xf8\xe3\xe1\xa4\xd3@X\x17\xd9\x8e\x0d\xa3\xa5[\xd8\xf6\x05k~\xbf\xeeu\x96&s\xa7\xcdWL\x95\x9e\xc5\xba?\xd5\xe5\x85\xec\x80I\xbb(\xe0\xfc4\xf1\xfa\x1b~zx\xb1*\xef\x81\xf7XGG@\xf2K\xca\xf4\x08\xaf\x9c\x82;\x89\xb7ZJ6\xee\xfd\xea\xaf*\xd7\x1b\xef\xfc\xd3\x1e,\xe0\x16k\xb2L\xef &\x9bpD\xa7W\xa2\xe3\xaa\x07\xf58r6\xe0^\xda\xddwiN\x98a,\x05\xb5+UZx\x07\xd9\x84\xbc\x9a\x9bSR~m8\x01ht\xb0T\x99\xa1\xcf\xfcL\xfb\xfa\xcc\xcfx\x8f\xc6J\xa3&\xcemY7\xe1\xa7\xfcbE\\1\xb6\xa1Q7x\x9e4#+-\xd0/v\xec\xe6\xad\x1a\x91\xb6i\x1bn\xdd\xf6\xd4\xe8\xfd\x088\x9b\xc6=\xb4y+\xc620\x03M\x05$\x98;\xf4\xa8\xa9C]iL\x9b\xd3\xb7\xea/YIs>\xc9\xf6Q\xc5V\xa6xl^;\xa9\xb0}\xc1J\xcf\x07z\xc2\xdc\xd3\xa4b7\xf0C\xd0\xe4x\xa7P\xe9\xdfR\xfb\xbd\xe1\x83\xc1\xee@z\x1e\xb8Vkg\xa5\x8f\xe9\xdd\xfb\xee\xa0\x88\x98@Y\xf3\xb6\x19\x1b\x07\xb2\x9d\x07\xa4}\xef\x83\xfb{\x16\x83]\xdfQ\x92\xb9\xdb\x18\x87aG\x8c\x9d\x1fn\xd3n\xa3\xeb&\xca\xa2\xb3\xbdep\x11Di\xc7I\xad/xuf\x19\x13\xd2\xc3\xd4j\xef\x8b\x9f\x1c\xb1\xdeg\x87\x9f\xbfxyx\xfc\xe5\xb3\x97\xbfe\xf1\xad\x90f~\x16L\xbb\x95])\x0c\xefTZ\xfaS]\xa3\xc2\"\x08g\xcf\xd7\xadu\xca\xb3\xcf\x90\x1a@\x84\x9dj\x9d\xe3/\x0f\xdf\xfc\xda\xe1g\xf6\xaa/\xa2 \x0b\xfc\x10\"\x17\xadY\xf5\xb9\xd6\xddu\xaa&<\x82\xbb\xb4\xaa\xc6\xab\x97\xcf\x0f\xad \x94+\xe8\xc7A\x18~\x89\x8eK;\x80\xa4\xa8\xf6Y0\xbbF-\xf1\xb17\xa8($@j\xc3\xa3E\x9c\x0bp\xc86\xbeZ\xcd*\x10\xed:\xc8z\xbd.\xfd\xfd,\x98]\xa7\x1a|.Zv\x86\xcfW/\xdf>\xfb\xfc\xf0\xf8\x9asB\xd5^\x1b\xc8T#k\x0c=\x87\xa2\xc5\x1c\x8dX\xef\xd5\x8f\x0e\xdf\xbcy\xf1\xd9\xe1\xf1\xa7\xcf\xde\x1e\x12\xbc\x8f\xd9Nh%:\xb0\x10\x93\xe0\x8c\xcf`5}\x9e\xc4\xcb\x86\x15\xd9\xe5[S\xeb\xb7fA\xba\n\xfd\xcb\x97p\xe3\xbb\x13G\xce\x80\xf0j\xf5X]\xac\xab\x1e\x8b\xd6H\xd1\xd4\xce_\x13\x1cgK(\xb9B\xed\x11\xa1\x9a;\xaa\xb8a\x8b\xfa}W\n\xb4\xc7\xd1d-\x15\x17AJ;\xf7\x9b\x0f\x8c\xda\xe2\x88.C\xa6\x19y\xa4\xabP\xd6\xd0\xb5k\xf7\xca\xd2\xa1\x1b\xf4\xc5\xd8;\xd6\xe8N\xad.8\x13\xaa\xa7\xed\xb3\x85c\xa4B\xcb#\xb2\xf4Z\x08\xa9\xed\xc6kt{\xa5q\xa9\n\x84E\xda\xba\xf0+\x98\x87\xce\x1d\xd8\xe8^\x94u[C\xac\xba\x8e\x82\xa8\xbdU\xf5(>\xaf\xdd\xa6_=\xd0\x9f\xba)`\xd4\xd9\x14\x90)\xb1\x97\xe0\x16A\xd3\xd9\xed\xb3\xe2 \x9c\x8d\xd8cw\xc1\x88\xf6y\xe8\xa7\xe9\x88\xfdV\x9c3\x1f\xf4!\x19_\xae\xb2 :eY,C\xcf0\x9f%<\xe5\xc9\x19\x9f\x01\xa6\x88\x9ez\xec\xeb_I\xbf\xf60\x16>n\xd8\xd1\xd1\xdd\x8c\x9dp\x06\x11\xf2A\xb4\x0b3\xdac\xef\xf9\xe5\x80}\x86M\x05\x19\xf3S\xe6G\xa5\xc1\xb4j\x11R\xb8?{,\xca\x9c\x07a\xc8\xd2L\xfc=\xe1\xcc\x9fNy\x9a\x06'a\xd1\xb8n.~\x97vRo{\x94\xd8\x0b\x80\xd6A\xea\xa5\x1e\x90~\xad3;L\xe3\xb9Cs\xa2\xd9\x01\x0b\xc7\xd1D\xca\xe9\xbb\xf7\x83\x95\xa7\xcb\xc0\xa1\xb6C\x10{\xe4\x1e\xebu\x9e_1\x95\x02\xb2\x97q\x9eh\xb6\xc2\xa0 \xcb\x16~\xc4\xe2h\xca\x07\xec\xdd\"H\x05\xe4\xe7a0\xcd\xd8\xd2\xbf\x14s3\xcb\xb9h\xc9\xc7Mm\xd0C\x07\xc8gq0s8\xc6\x95_\xc0\x8b\xc7\xa8\x80S\xb6\xa7Y\xff\xab?\xf2#\xb4\xc7\xe5\xfa\xd3\xde\xac\xbd\xc4\x07\xa42\xeb\xd04?\xcf\xe2\x93 \x9aU-\xee\xd7PA\xd3\x81u\x98f#\x98\xd6\x11+\x13\x88\x95\x8e3;b\x9d\x10U\xee\xdc\x11\xc8Te\xe1\xd0Ml\x05\x8f \x12\xc2\xdc\x9fr\x1bB\xc5g`\x87Q\x9a#\x86eXj\xc9\xb3ENDg\x9f\xe5Y\xfci\x10\xcd^\xfbAb\x89TY\x8dR\x19\xd5\x97\x99\x0f\xcbl:@\xee\x1f\xa6T\xbe\xbb\xa4\xbfw\xf5\xc0\x1c\xd7\x1bC\xbb\x8a\x1cC\"\xb6\xedJg\xf2^h4\xce;X\x8e\xad`\xd8\xc6\xf7\xda\xf5\x80sg\x85!w\xa6fm\x97M\xc7\xf9D\x0c:li\xa9\xc1\xef\xb3\xfe\x881\xcd(\x02\xd8\xd6S\xd6d7\x0d\xc6+\xe0\xac{\x05\xb7\xdc\x86H*\x06\x8a\x92w\xdb\xc1\xc0P\xbfmR\xf4\xe7L\xba\xcfN[\x03\x96\xeaO\xe0\x80\x13q;\x13\xb0\xac\x13@\x99\\_\x81_E\x85\x11\x81 \xd1l\x15\x87\xc1\xf4\x92\xfdJ\n(\xfd\x9e\xc3\xeb\xf9\x82G\xb8\x02O\x81\xdd,\x96\xa6\xa8\x02\xc4x\x89\xb3\xdf\xd0\x9d\x03\x96`\xe4\xd2\x85#^\x042\xb0\x11\xd5C\xf4\xe0\x8be\xcf\x8a\xb2\xdd\xa0/\xddA\xcb\xda\x1d8+(\x1ec\xd0\x93\\|\xc7+*7\xd6m\xe0\x15\xcc-\xbe\x13\xa1\x9fY\xf7\xfb\xea\xb1$p\xa4AY\x83\xaf~\"=\xf3Xo\xc9\x93S\xaeB\x1c\xbd\x8c?\xcbW\xa1\xd8\x90\xf9o\xf2\xcb\xd4qG\xec\xb9\x1f\x89m\x17\x8a\xb1(\x8e6\xb1\x99\x14\x08x\xe62\xe2\xc8\x82Q\xca*:=`\xf8Z\xbf\xf5.\x91\x06-\xf8\xb5\xec<\x96\xf4;\xc5\xed^p\xfa\xa9\xbf\xe4\x18\x06]l\xbd\x9dv\xd6\xc7\x02D+\xf0\xf0*\xf6\x044\x92SE\xa7~\x9eJk\xb2\xf3\xb8.\xb6u\\\xb1\xc5\xd5\x0e\xd3\x8e\xab8\x0e\xc9w\x8b\x15P\xe9\xa7\xd8\x1c\x17\"\xf5=\xbfL\x15\x0b,\x19S\xcb\x0dUeB\xd8 -\x16m\x96\x88:{i\xdd\xf70\xb04F\x83\x15\x10\xf1\xcaH\xb2\x96{\x8e\xe2\x81C\xad\xa5\x96]=\xaaL\xe2\xca{(I{\xe1\xd2\xd6#\xb2\xef\xde\xe0^\x98\xf0\xd5\xcc4\xa5\x9b\x13\xe3\x14\xc0\x0b\x1dV\xa4\xdbz<\xbb1\xe0\xad\x00\xb7\x02\xf5\x9a]]\xb6\x1e\x1524\x9e\xa3\x94\xc4\n\xec\xb5/\xd5[1C\xd1\xa9\x87P\x13\xb4\x82\x86)\x83\xd6\xe3\xe3 \x85J`\xe3\xb7\xb1E\x96&H\xaa\x89\xb4\x97\xed\x1d\xac\x88\xea\xaf\xddG\xda\xde\xa5S\x1fO\xac}\x94\xfe\xc1\xa5\x02\xa9\xb3p\x0b\xfa\x87\xf2\xf8d\xc0\xa3\x9f\xe5<\xe7o\xb4\xa6$\x86\xad}z-\x06\xdc\x11N\xca\x16g\xa3\x0e\xb0\xeb\xc3\xea\xd8\x1e\xd6\x97iF\xa2\xce\xb1\xaeT\xd7y{vB\x90\xb6\x12\xb2M\xe42\xab\xa9T\x93\x06sPV\xa2\x89yXP\x91\xd7\xee\xdc\xe9\xf0e\xf5T.\x11r\xb2]\xcf\"\xeag\xfd}\xb6\xdd\xd6>\xab\xc9,\xdb\x8f\x05L\x9e\x88\xb2q\xc4\xfal\xd8\x81O\x85\xe0\x0b\xfbH\x99\xe2\xeb\xfaA\xf8\x00\xe8\xab\"\xda\xad\xa4t\x9b[C\xe7&|\x0e\x0e\xc4\xbc\xca\xbaP6\xeaQi1\x9fq\x19\xcb\xc7>\x90\xc2\xcaWT\xa9\xb1\n\xec\x80Lv\xdcV\x81^\xe0\x10\xacY\x0evuUs2`\xa6\x7f\x85\xf8\xc4\x88-\xc5\xc9W\xa2\x7fq]]\xf0.\xe2\xd3=\xb1\xb9\xe8\xea)q\n@~_P\xc14\xd0\x14w=\xb7\x06\x91\x9c^\xad-'\xde\x04\x84\xe5\x15c\x97\x88\x9f\xb3cOO\xac\xf8\x10\xc1h\xc8Z&\x85\xe22\xa8_>\x90!O\x9d\x95n\x00\x9e\xb9\xae\xc7VN\xe6\xb1S\xf5\xc2\xd5\xcb%\xec\xb0u\xb5\x08\\EP\xc1\xe6\x0bMI\xbd\x98\xe3\x82\xacB\xef\x1c*\xda=\xd6\xc3\xc0\x07pnr\x06\x83\x81`\x98M\xd1\x16NO\xb0\\\xa15\n\xf3\xd9\xd7\xd8\xc0\xd7\x92\x93\x04f:u\xf5\xf1\xcb@%N-I\x86\x9bj\xe4w\x9a,\x93n`\xd0s\xd6\x12\xd3\x0c\x0co\xca\xe2\x91cs\xe6g\xa7zr\x00F\x0cg\xee\xca\xe0\x96\xc3\xfb;\x10\xdd\xf2v\xc7\xb3\xbdG\xdb\xe2)\x1b\x00\xb1\xd5\xc5.Ek\xfd\x12*5Z\x0b\xc1X\x1f\xeby\x96#$\x8f\xf2%O\xd0\x01\xfe\x86%\xd0\xe8)\xef*]Q[\xf3\x80\x96\xb5\x13b\x82\xc6\xbe\x07\xdf{\xbf\x83[\xe9\xb7D\x93\x8e\x9d'\x1b\xcf\xea\x08\xc4\xf6\xd9\xd0Bv\x18uz\xb8\xc1\xfao\xa3E\x80\xb7\x9e\x14A\xe3M\xa3*\xca\x927\x95\xe0&\xf5 >Iyr&\x86.\xce\xdcp\x0bXK\x1a\xc9\xa0\xbc\xe2P\xad\x12{\x10\xd1]+\xb4\x8fvr\x19:\xc7\xd6\n\x92;\xf0\xf7\x02\x91\x8a\x80\xc7\xf0\xcf\x00Bn\xa4\x98[\x8fYP\x11\xf0\x04\xb4\xcb\xa2\xb3\xc2)N@\xc8f\xb6<\x1a\xc4|\xecO\xf0\xe2\xa7xA\x07G\xb6\xbd\x8ai\"\x11\xbd\xc7u\xeb\xab-\x93\xd8\xa6\x16F\x8a\xe6\xbc6:\x08\xca\xaa +\x04\x04E\xc5F\x91\xe9\x99\xe6a\xabY\xf2\x85\x07C\xec\xbamm\xeaO\x06\x1e\xc7\x04;\xfb\xe2\xe5\x8bw\x8d\xc5?\xb4\\Q\xd5No\xb1\xcb\xb2E\x12\x9f\x83P\x05n\x119w\xdf\xf0Y>\xe5 \xeb\xdde}\x96\x81\x1b\x90\x9e\xc4`>c\xc5V\xc9fy\x82*[\x90 \x05\xdfH\xe3\x9b\x17sT\xaf\x81\xd8g\xe5\xa7)j\xe2DZ\"[\x0e\xd2\xb2\x19\x8f]\xc69\xca5\xf8\xc5*\x0c\xa6A\x16^\x16\x0bf\xc1U\xfb\xd8\xe0\x80\xbd\xab'\x81\xfe-\x8a\xc1B\xb0h\x15\xba!\x1a\x9e\xc5\xd1\xdd\x8c\x9d\xfbQ&:\x91\xf2\x8c\xf9\xd2\x01\x81X'\xa0\xbf\x93\xbd\xc2\x8eL\xfd\x08\x0c?\x80\xb9\x91\x86\x83,\x9ek-7\xb9\x96\x11\xd3\x1f -\x10\xad^\xdc{\xfd\xe6\xd5\xa7\x87\xc7_\xbd\xfc\xcd\x97\xaf~\xfc\xf2\xf8\xd9\xf3w/^\xbd<\xee\xb1>\xfb\xd2\xcf\x16\x83\xc4\x8ff\xf1\xd2q+\xa1\xcd\xb5\xe0\x9e{\xee ]\x85A\xe6\xf4z*\x80o\xe3\xe7k\x93\xdb\x15\xbd\x10\xb5\xe8\xed\x86\x01>\xdd\x00K@\xbb\xbfJ\xe2\x13\xf1\x1ed\x0b\xe63\x1c6|v\xc0>\x83 \x12\xcb5\x8b\xd9\xc2\x8ff!z\x99P\x98\xce\xfa\xec.\x8b\x13\x16g\x0b\x9e0\x1f\xd6 \x88\x18z\x08\xe1Ozh\xd6\xb5\xf2\xd1<\x8a_\x82\x8d\xd54\x06/\xa3 X\x96\x06g\x80:\x85yO\x81q\x1a\x9aM\xf3$\x01\xa3\x03\xc0)\x81\x1c~t\xc9\xf2\xe8}\x14\x9fG\xea\xbb\x1e\xcb\xa3\x90\xa7)\x0b\xb2\x1a\x12\x07\x11;_\x04\xd3\x05\xde \xa4>PAZ\x8f%\xfc\xd4Of\xd0X\x8c+\x06\xbf!\xc1\xd2\x0d\xcd\xd1\xa9\x86\xc0\xd9\x13D\xd9\xc1]\x8b&\x86\xd0\xfe95\xd3\xa0\xca\x01\xd3(\x0e\xc2\xf1\x06\xfa\xddEo)\x96\x87\xd83\x0b\x9d\xa4\xd2`\xc6\xb2\x12\x14\xc9\x80\x8f\xb2\xf8*/\xbd\xbc\x88\xceb4\xdcz\xed'>\x84u\xff\xb2\xf0\xb1\x9b\x15\xac\x84\xf4\xf4@\x124\xf0\x16$\xb6\xae]\x97\xd8\xbbD\xd6\x83]#+(\xb2\xf6\\\xf2X\xeb[\x95\xba\xd2v\xa4\xb2\xfey\xf3\xfa\xb7\x1e\xc0\xb5\x05_\x1bj\xa2\xe6\xd8[\x0bd\xb1^\x8d\x82\xff/1\xe9\x15\xbds\x04\xe5%\xa61P3L\xcdU\xf0}\xcf\x15E\x9c\xed\x8e\x9f\x82\x1a\x89\xa6\x0e\xb5\x1b\x81\xa4\xb9\xa5'\xbb\xb7Y\x9cp6\x8b9zc^\xf8g\x1c%\xf3\xc1L\xc9\x1c\x06\xecK\xff=g\xf2*//#\x8c\x94J\x85\xfa\xe6\x1b\xa4\xday\xf7|\x11\xa7\x1c\xa7&\x05\x99\xb0l7\x1d\x10\xc1k}I'\x0b\x14s\x0d\xed\x13\xba\x0d-\xb6\x84\x17\x19\xaaM\x07A\xaa^\xf5\xb8.\x85\xbbd\x1f$\xd8A\x8aB\x91\xe2\\\x9e\xd5\xa2\xa2\xa8\xc1e18&\x88*\x81\xdf^,\x979\xc4\x83/\xbeZ\xdec\x9a\xc7a\x18\x9f\x07\xd1\xa9rx\x10\x80S\xaa\xbb\xac\xcf\x02T\x1a\xdc\xedy\xacw\x17eL\x83\xbb\xe6\xd8\xe1\xc0%f\xef-\xff\x19(#\xf0\\\xe8\x0e\xe6A\x98\xf1\xa4\xe5\xa8 \xc7\xbba\xdc\xdf\xaa\x1da\xeaZ)Y/\xd7e\xc0\x07\xac\xa7]\x19\x04\x81\x04^\x94,J\x1d\xb0\x9e\xf2\xeb\xd0c\xa3\xe2G\xc0S\x14\x97\xe1\xc0ss\xe0l\x1e\xe7\x118\xa5\xbe\xab&E\x03\x7f\x16\xb3y\x10\x15a\x83\x04\\Q\xf0\xaf\xe4_\x853 \xbcC.\xc5\x1a\x0dp\xd6\xef>\x96\x9dD\xff\x13'\\J\xeaf\x83\xbbuw\xca\xb7\xbf\x1b\xde\x1aE\xf3\xd6\"\x0euo\x9c]tH\xa4d\x13UH\xa0\x1a\x12X\xaed\xa7\x97+)\x0bEQ\xe7\xad\xc8?\xeb\x02(M\xb6y+\x13\xa4W\xacB\xab\xa0\xd0b\xd7\xae\x07\x00/\xe7\xa9:#]>\x199\x8fP\xc4\xfd\xe8\xa1[\xedy\xe4<\xd8\xdb\xead\xe0Y\x1e\xa1\x87\x86\xafC\xe9l\xf0\x91\xeb\xf4\x8a\xd8\xe0\xa4\xad\xf3\xde\x96\xc5\x8a;r\x86\x0f\\\x8d\x8a\xaeq*\xb0\x1d\x084ER6\x8e\xd1c\xad\x16\xbb\x1c\xee\x14@4\x81:\xcdJ\x1c]~\xd7 \xc0\xcdV\x86\xf7~\xe2\xfc\xca\xf6\xd6\xd5Q\xea~\xe2\xfc\xd4?\xf3\xd3i\x12\xac\xb2\xab\x99\x9f\xf9\xee\xbd`i\xc2\xf2\xde\xf8'G\x17\xdb[\x9bG\x17{\x87\x93{\xa7\xf5\"\x01\xb69\xfe\xc9h\xd2wG\xf7N\x97\xe6qk\xdc\x1b\x08Bt\xaf7\xa1\xe1]\x05h\xeaGA\x16|\xc3\xbfJ\xc26a\xd5\x99\xb4\xb5\xf1\xe4\x8e!\xaf\x95\x89cA\x8fRKw\x12\x10j\x05\xfd\x010\xec\xaf\xe6\x0e\x1foM\\\xf6\x94m\x12\xee\x97\x9d\xdc\x95&\xe7N\x04\x12\xc0\xa5\x9fM\x17N\xe0\x8ad4\xd9\x11\x873\x96\x0c2\x9ef\xe8\xb6\xa4\xe7\x9f\xc4y6: \xfd\xe8\xbd\xd86r\xb8\x1d\xae'V\xbe\xb3\xa6\x15e\xb9<\x1e\xd8\xec\xff\x1f\x0e]#\xdci\xc3f\n.\xa2\x07Y\xfcE|\xce\x93\xe7~\xca\x1dpG\x02\xfa\xa3\x03&\x90\x94\x8d\x0c\x1f\x1f\x96\xe5\x15\xaf7\x84]\xca\x9e>r\xb6\x1f\xda\x96\xaf}z\x95\xb0\xdbI\x1c\xeeVG\xb3\xe6\x1a+\xbb\xb7W\x17]|/\xa6\xe4`H\xdelF\xde\x0d$g\xff\xbf1y1\xc7\xf5 \x8e\xba\xd9\x8cw\x03t!d\xb9\x96\xe5\xb8\xbe\xa2)\x84\x13\xeb\xc1r\xa3g\x8f\xf2\xaf\x0b\xcb\xea\x9aCh\x96\xf5\x80\xc5\x03\x19\x94@\x814F\x12\x18 \xd1\x90\xe2y\xa34\x93\xa8\x0e\x96\x91hd\x91\x0d\xa6\x0b?y\x969[\x16%L*\xcb'N\xe4\xb1\xa1\xb2P\x82\x08!\xd9 \x0d\x83)w\x1a\"\xb0\xe4c>\x01\xc5wU\xd8\x7fm\xda\xbb\xfd\xb0\x1d\xc4\xf6cl\x0c;\x9a\x14\xdf\x93\x98T,2\xe9\x02\xea\x80\xc5\x82w\xf7\xd8\x06\x98\x01D\xec\xe9>\x8b\x95Ux\xf1\xa9\xeb\x8e\xe6\xc1^\x9d l\xc1\xbb\x9b\xd0g\x8e\x08\x02\x97\xb4\x92\xf6\xc5b\xe3h[\xbf\xc4Ks\xb65>\xa1\x10\xb97>:\xcag\x0f\xb7\xb66\xc5\xff\xf9|^\xbf\xf4\x96\xa8B[;Xhkgw~t\x94\xcf\xf96\xfc\x9c\xf3m\xf1s{k\x06?\xb7\xb7\xcc&\xe0\xc6\x00|fg:\xc6\xcf\x9c\xd8>\x07\x86~\xe3\x9f\xb4t\n.\xf49\x07#\xbd\xd1\x19\xdf\x85\xe2\xb3\xf9|\xe2\xfe|\xfb\x03y\xc5Oo\xf7d>\x9f@\xc2\xd4\xfe\xa1T~\xa8\x08\xe1sU\x84\x01r\xc5[\xef\xa0V!T\x9f\x99\xf3-\x8e\xff\xe6\x93\x03\x15\xe1\xc9\x91\x9d\xde\xde\xda\x9a\xc9V\xc7\x18\x93)\x9f\xc8\x95~\x85A\xe2\\k\x1b=\xf7\x93\xfaY`\xaa\xf5r\x1c\xa8\xae\x1e\xf4\xf0\x1a<(\x08\xa3z\xfb\xb5~\xcf\xd9\xbe\x0c\x8c\xe0\xc0\xe8\x9c\x83\xfdr\xa40\xe8)F\x8a\xec\x9d\xf6\xae\xbb&\xb8\xe4*\xe7p_t<\xb9\xee2\xde~hc\x08m\xcb\x98\xf2%/G\xdb\x1b\xdf\xfdo\xbf\xf3\xbb\x93\xde\x8dF\xd6\xbc\x9d\xa8\xdd\xdd \x1c\xb1o\x14,\xbe\x0f,\xbe\x0b\xce\x1ez\xbd\x1b\xdd9\xd2h\x9c\x058\x06\x0b\n\x87\x9e\xf1\xd1\xc5T\x1c\x8bf\xbbG\x17\xb3\x87\x9bG\x17\xf3\xdd\xa3\x8b9\xbc\xcc\x8f\xf2\xad\xa1X\x19\xf9\xd6po>\xb9w\xda\x00\xc2u\xc9\xc3M`\xed\x80\xd0\x1a\xa4\x82 \xa9U\xd0\x0c<\x96\xd4a{} \xdew\x9d\xea\xd7{\x7f\xf8;\xbd\x11\xeb=\xab\xad\x9b\xde\x1f\xfe1:\xf9\x8f\xd3\xc9\x7f\x82N\xfe\x1f\xe8\xe4?I'\xffC\x91\xec\x1b\xc9\xff\x88N\xfe\xc7t\xf2?\xa1\x93\xff)\x9d\xfc\xcf\xe8\xe4?-\x92\x9f\x1b\xc9\xff\\$O\x8d\xe4\xbf\"\x92\xeb\xde\xf1{\x7f\xf8\xefD\xf2\xccH\xfe3\"\xb9\xee;\xbe\xf7\x87\x7f\x96N\xfest\xf2\x9f\xa7\x93\xffg\x91\xcc\x8d\xe4\xff\x85N\xfe\x17t\xf2\xbf\xa4\x93\xff\x82H~a$\xffE:\xf9/\xd1\xc9\x7f\x99N\xfeW\"90\x92\xff5\x9d\xfco\xe8\xe4\x7fK'\xffU\x91\xfc\xd2H\xfe\xf7\"92\x92\xffG\x91\xfc\xcaH\xfe\x9f\xe8\xe4\xbfF'\xffu:\xf9o\xd0\xc9\x7f\x8bN\xfe\x0f\"96\x92\xff#\x9d\xfc\xbf\xd2\xc9\xff\x1b\x9d\xfc\xbf\xd3\xc9\xff\x89N\xfe]\x91\xfc\x95\x91\xfc\xb7\xe9\xe4\xbfC'\xff]:\xf9\xff\x14\xc9\xb9\x91\xfc\x7f\xd1\xc9\xff\x99N\xfe/t\xf2\xdf\x13\xc9\xf5\xd8\x01\xbd?\xfc}\x91|i$\xff\x01\x9d\xfc\xa7D\xf23s9\xfc\x9eH\xf7\xcd\xf4\xbf/\xd2\xdf-\x8c\xf4\xff*\xd233\xfd\x1f\x88\xf44\xad\xa7\x7fK\x93\xe5oi\xfa\xfb-Mh\xbf\x05\"n\x90\xb7o\xff\x04\x9d\xfc'\xe9d\x80\x80A\x0c\xbf\xfd3t\xf2\x9f\xa3\x93\xff\x02\x9d\x0c\x84\xd6\xa0\xa8\xdf\xfeY:\xf9\xcf\xd3\xc9\x7f\x91N\x06\x12d\x90\xe5oij\xfd-P&\x83Z\x7f\xfbW\xe9d \x13\x06\xfd\xfd\xf6\xaf\xd1\xc9\x7f\x83N\xfe[t\xf2\xdf\xa6\x93\x81\x04\x19\xf8\xf6\xed_\xa7\x93\xff&\x9d\xfc\xbbt\xf2\xdf\xa1\x93a\xcd\xfe\x9a\x91\xfc\xf7\xe9\xe4\x7fH'\xffc:\x19\x16\xe7\xa9\x91\xfc\x0f\xe8\xe4\x7fD'\xff\x13:\x196\xfb_7\x92\x7f\x8fN\x06\x1e\xc0X\x98\xdf\xfes:\x19\xb6Xc\x07\xfb\xf6_\xd0\xc9\xff\x8aN\xfe7t\xf2\xbf\xa3\x93a\xfb66\xb6o\xff%\x9dLo\x9a\xdf\xd2\xbb\xe3\xb7\xff\x9eN\x86\xed\xe47\x8cd\xd8N~j$\xc3v\xf2\x9bF\xf2\xff!\x92\xdf\x1b\xc9\xff\x89N\x86\x9d\xe0\x0b#\xf9?\xd3\xc9\xbfO'\xff\x01\x99\xfc\xdd\x1f\xa3K\xc3.\x13\x1a\xc9\xff\x85N\xfe\xafd\xf2w\xbfC'\xffq:\x19H\xaf\xc1\x8d|\xf7'\xe9\xe4?M'\xff9:\x196\x01\x83\xa5\xf9\xeeO\xd1\xc9\x7f\x86N\xfe\xf3t2\xd0o\x83I\xf9\xee/\xd1\xc9\x7f\x85N\x06Bm\xf0\x17\xdf\xfde:\xf9\xaf\xd2\xc9@c\xdf\x18\xc9\x7f\x83N\xfe[t2P\xcd\xc4H\xfe\x9bt\xf2\xef\xd2\xc9@\xa8\xdf\x1a\xc9\x7f\x97N\xfe\xfbt\xf2?\xa4\x93\x81\"\x1b\\\xc1w\x7f\x8fN\xfe\x07t\xf2?\xa2\x93\x81\"\xbf3\x92\xff)\x9d\xfc{t2\x90\xde\xccH\xfegt\xf2?\xa7\x93\x81\x98\x1aL\xe1w\xff\x82N\xfeWt\xf2\xbf\xa1\x93\xff\x1d\x9d\xfc\x1f\xe8d\xa0\xb1\x06\x0b\xf9\xdd\xbf\xa4\x93\xff5\x9d\xfco\xe9\xe4\x7fO'\xffG:\x19H\xef\x8f\x8dd \xbd\xe7F2\x90^\x83\xc7\xfd\x0eH\xaf\xc1\xcc~\xf7\x9f\xe8\xd2@z\x7f\xdbH\xfe\xcft\xf2\xef\xd3\xc9@L\xbf1\x92\xff\x0b\x9d\xfc_\xc9\xe4oav^\x98\x1b\x0f\xc0*0v\x9e\xef\xf0\xb8fp.\xdf\x01\xb3\x14\x9b\xe9\xc0X\xde5\xc9\x1b\xec\x1bi\xa9\xd9\xb5)Hi\x8f>\xd7\x16rw\x12\xb0\x11\xce\xd4F`\xa3[\xa9p\x03\xc9Z=\xf6\xa3\x12;R\x96\xdf\x84\xc4M\x9am?l\xf7\xbcG\xabT\n\x0b\xc5}\xd0+x\xba\xea\x04u\xf4\xfa\xc0AA%\xd5\x10~\xa9\x86\x80\x00T(\x87\xcd\xba\xc9a)\xb5\x01\x18Tlmm\x1e]l\xcf\x8f.v\xfc\xcd\xa3\x8b\xfb[G\x17\x0fN6\x8f.v\xb7\x8e.\xf6\xc4\xcb\xde|\xd2\xbfw]%\xa3\xeadt\x93N\xfa\x9b\xdfL\xc6\xcf6\x7f{r\x05\x7f\x7f\xbe\xed}\x80\xb4\xab\xf1\xd6\xe6\xa3\x89x\xc5L\xf9\x02\xa9W\xe3\x9f\xe0\xcf\xad\xcdGlr\xef\x9a\xdd\x8f\xd0Pb-\xb5O\xa1\x939:\xba\xf0\xa7GG\x17'\xc3\xa3\xa3\x8b\xd9\xde\xd1\xd1\xc5\\\xfc\x01\x01\xab\x008B\x1c@\x8e0\x07\xa0#\xd4\x8f.NP\xe0\xba%\x05\xae\xbbsvt\x94\x89\xea'GG\xa2\xae\xbf\x05r\xd9\xf9\xfc\xe8(::J\xa0\xd0\xf6C\xfc\xf7\xe8\xe8(\x1f\xee>\x14%\x86\x0fA\xf9 \x1a\xc2\x7fC\xfc\xb7\x8d\xffv\xf0\xdf}\xfc\xf7\x00\xff\xed\xe2\xbf=\xfc\x87mn=\xc2\x7f>~\x01;\xf7@\xfc\xdb\xd9\xda\xda\xaa\x11\x18\xd46\xf5X\x9fE\xac\xcfz\x16M\xd2\xac\xdf3\x17\x1cH\xa1\xb7\xf7\xe4\xb0\xf7Nh\xa5\x91\x98j\x01\xd4\xb9\x80\xd4|\xf7\x08\xa5\xddG\x17\xa6\xea''5Q\xaak\xa0\x18\xa9}\xd0\xda\xf4\xb3\xcd\xdf>BA;H\xdaQ\xd4~t1\xe36u\xd3\x1az\xad\xf0Zz-\xd0\x18\x8d;\xf7k\xae)\x98\xfcB\x0d\x96S\x8a\xa4\x95Vt\xda\\t&\x8b\xae\xa9>\xb8\xb2\xa9\x12\xdd\xba2naU\xc6\xcd,\xca8R\xf5\xc8R\x8f\x85\x9d\xf4s3Z?wV\xd1\xcf\xd1\xed\x89\xbc\xda}\xcbe\xa9b\x19OQ\xa3\xa7\xe0\xdf\x17`\x03\xc5\x95s0\x9a]\x85\xe1\xd5\xf2*\xe1W\xe9Uvu\xc6]\xf7@\xaa\xef\xc6\x89\xc7\xa6\x1e\xeb\xfd\xb0g\xaa\xff\xd8\xcah\xe8\xb3\xab/\xbe\xb8\xfa\xf2\xea\xcd\xe1\xd5\xdb\xabwW?:\xac5\xc4\xfalnk\xac\xec\xdf\xbcK\xffT\x8d\xb6\xcf\xf79\xc0\x1d\xeb\x87\xd7\xa6\xec\x1b\xce\x06\xd8t \xea\xa6l\x10\xc0\x14\x97\x1d\xb0\x15\x18A#\xe3\xef\x17\x0eG\xd9Z\xa8S\xdc\xb5~d\xbdk}o\xfc\x93\xc1\xa4\xff\xc3{\x03~\xc1\xa7N,z\x10\xc35\xb1\xf2m\xf0\xe2\xf0\xf8\xf5\x9bW\xef^\x81\x91~\x0f\xac\xb8{\xe8\xc8\xd1I\x93\xa9{<\x1c\xa0E\xd3\x88\xf5z\xd7\x85\xc4F >\x18@`\xd6k\x8c\x14\x91~\xcf\x1d\xf7\x8e\x8f\xa7q\xc27\x7f\x9a\x1e\xa7\x0b?\xe1\xb3\xe3c\x9b\x95\xfdu\xa5\nv\xdf6\xed2\x83\xf6s[7\xb0\xa9\xad\x01\x88\xcb\xc2\x87\xcd\xe3\xce\x1de\xde[!JcN{\x05)\xe9\xd2\xe6>\xcb\xd8\x01\x1b\xb2\x11l\xda\xd7\x05\xbf\xa0\x9e\xc4 \xeb\xf88\x8cg~\xba8\x16{\xfdqqg\xe8\xf8\x988v\xb5\xb8OX\x17\xb9*PR\xf0\xa8\x02#\x983\xc7pZ\xcc\xb4\xf3sf\xc0\x8fULN\xf7\xd1\xa6\xb4\x98\xee\xa6@J\xb2VPx\x15\x86\x95.\xbeP\xd8\xfd\xde.\xf0\xbf\x7fx\x16\xc6\xe7\x07\xd5+>0\xc4X\x1b\xf8\xed\x0e\xb4\x01\xcb\xda\x06\xd9\xe4=\xacu\x9c\xe5\"\xeaW\x17#rdC\x8fEb\xe8\xfbh\x8d\xaf\x89\xd82i\x9d\x9c!\x83pS\x02\xd1\xc6\x96\x8c'\xb7\xc4\x88\x0cw(\xf6\x18\x83\xd7h\xcc\xd8*\x0c\xa6\xbc\x0d\xf2\x9d\xd0\x8bf}\x13D\"rN6\x9c\x88=A\xc7\x11N\x04\x9e\xa0\xd4\xd5\xd4M6\x14\xebm\xb0\x8a\xd1WD\x89\x8f`\x1e\xef\xb1\xcd\xcd\x02H\x1e\xdb\xba\xd6\x9e[@\xe9\x174z\x1c\xbb.\xba\x1dG\x93\xf1\xb0m\x0b\xba\xd5\xa1\x146\xaa\xd5\xb1\x08rW\xb91\xf6\x11\xba\xd2u5\x9b\x80\x8d\x01\xb0\x91\x15\xb0\xb1\x04\xac\xd3\xefkH\x12a\xec\xd0\xb1\xf8\xf0\xc4\x85\x08P\xe3X\xc0[F9j_\xdb\x0d\xc3\xddn\x1d\xae\x0d\x89\x12\x15\xf9\xcd\x95G+\xdb-\xa1\xebr\x01\xad\x14\xc9\x8e\xdf\xd2S\x1d\xd9\x9d\x1e\x9e\xe8\xd1\x81\x1b\xf0\x9bQ\xbe<\xe1\x89\x96\x90\x02\xe7\xa9%\x9c\xc4q\xc8}\xe9\xf4M\xf0\xa6\xc7\xc7@\x89\x8e\x8f{2\x10\xc0Hs\xce\xf7}\xceFe\x1d\xc0d\x9c\xf2\x0eb\xfc\x8f\xdc\x07\xdc\xa1>f\x1f\x1a\x16a\xd9\x0fz\x05F\x80\x8c4e\x03\xc1\x034\xeeU7\xdeHnk\xc8\x8a\xc9\x8d\xf7fK\x8f\xb6{7\xae\x8eI\xe5\xdc\xfdV\x90X\xa6\xa5(\x80{\x10\xe9u\xef\xac\xe2w\x9d\xbcI\x06\x8e/b's\xa9\xfa\xaa\x8dT\x11\xb8\x1d\xa2\x05&o\xaa\x05\xe0{(j\xec\xbb\xfe\xc8q\xa4N>\xe6\x13\xb8|\x90wu3k\xa6\x9cI\x8f\xbc\xbc\x00\x87\x95\xf3\x0ea'a\x07,\x1f\xa7\xc0C\x87\x82\xc1\x0c F\x9a\xb1\x1bH\x03w\x87\xf5[ \xf2\x02\x84!`AL\xd8~\xd4*A\xb2\x12\xc6\xd8F\xa3\x87\x15&\xe6\xce\x1d\x96\x8d\xb7&\xe3\xed \xde\x19\x14\xef[\x82\xbd\x13/\xc3\x89\xd8\x82\x8ao5\xdd`\x8e\xa4\x13Q\x88\xb6\x16QAB\xaf\x0d\xb5\xa1qwF]\x8d\xa3\xa064%U\xdbm0\xc4\xaf\x0bd#\x80\x99\x02\x1d\x91n4\x8d\xe1\x0b\x04K\xcd\xe4)\xdbg\x1b\xb9y8,\xce\xf4\x85\xdf\x98\x8dZ\xfc\n\x10\xb0\xf2\x8a\xc7\x03\x96nnZ\xa5\xabs\xd1\xbdqjq}=\x85`\xa18\xbbs\xc1G\xc0\x166\x9e\x8f\xb7&\x02\xb97\x1c\xf1\x06b\x92\xd2\x93\xcdFS\xac\x0f\xe8\xdec\xd6\xef\xa7\xec \x0b\xad\xbdZ\xb1}\xe6\xa8\xae\xb9V\xe7i3\x10\x0d\xaf,\xb9\x0b1IV\xaf\xde\xc5\xd0l\x04\xa5\xe6\x90\x04B\xdco8\xab\xe6\xd1\x8aG\xc6}\xb7\xd3\xbe3\x86Q)\x1bBQ\xe7.\x94\\\xb2}\x96;3\x8f-<\xb6\xc2U\xe1\xb13\x0b\xc5\x04\xba\xabwy f\x12\x0b\x8f\xcd<\x16\xb0+y_\xeeL,\xcae\xf3\x08\x1afP\xd5\xba\xc1\xa1\xad\xf5\xeai}J\xea\x07HT\xd1\xacu\x86\xbc\x01\x8b\xd8~\x04\xca:\xf3\xb5\xa2\xac\xe4\xd5o\xbd\xc3\xfa\xc7T\x7f\xbb\xf1x\xb7\xf4\xad\x9b\xf2r\x16\x8d\xe0C\xea~\x9fH\xaf\x97\x07b\xbd\xd5\xead\xa1\xeb\xa9\x8c \xbfLy\xd9\x8a\xe7ft1\xa6\xb1G\x91\xa5\x15V\xf0Gb\xab+\xdcT=a>\xdbd\xc3bM\xe6\x95\x83\\\x15\xd3\xfb\xfdH\xa2\x90H5\x9b7\xc6!\x17L\xe0\xe4\x1d\\M[\xf8Z\xc5\xd6\xde\x90\x93\xb5n\xc5u1\x9ade\xb7\xa9x\xa7\"\x9d\xd2\x1c \x14\xaa\xab?Sl\xbf\xaeq\x08ew\xea\xcdL%\xdfTO\x9f\x9b\x9c\xc1J\x0f\xac\xfaLy\xf0\xac\x9b\x97\xcc\xaa\xa5\x12\xff\xb2^b\xa1\x97\xc0M\xbb^\xe4\xec\xe6\xc2S\xc5\xa2,=v\xea\xb1K\n\xffO\x04+\xe2PG\xa1c\xc8\xc9\x88\x9cs\xb6\xcfN\xd8\x01\x9b\xb1\x11\xcb\xc9\xba\x87l\x9f\x1d\x17%\xa86.\xc4^/\x1a:\x17\x9c\xcd\x8a\x1d\xb0\x05\x1b\xb1sW\xfc\"8\xa6\xb7\xa2\xb8h\xf5P/~h+\xfe\\5|h.\xe7\xe7bK\x0fA\xd7e\xaedX\xa5!\x9cb\x8a\x8d\xd2\\l'\xe0+\xc5\x83A42>\xc5\xf76.\x8a\x06/A*x\xa964\xd7c'\"e\x8a\"\xdb\x98\x98\xb5\x11\x0bd\xeay%\xc3\x1c\xdb\x86\x13\xb1;lN\x0eM\xcc\xf6{\xb6\xcf.@\x0c\\\xb8\x96\xe9\x1d\x1f\x9f'\xfej\x05\x82jb\xa2\xc4\xf3\x8c\xed\xb3\xb7Z\xb5\xac^\x8d&w\xef\xc5\xb8\x9e5\x9d\x07_\xb1}\xf6\x9e\x1d0>\x00Wr \x11mp\x9a\xfe\x9a\xed\xb3g >-\x8bg4[d\x05\xf6\xa9\xf3\xcac\xaf\x15\x1c/\xdb|^\xd3l\xd0\x06L\xaac\xb6\xee\x9b\xd3w\xfd\xad\xd1\xd8\xea\xe4\xc1o\x9b6\x96\xd9\xdd\x1ev\xf5\xe3zv\xcbf\x1du.M\xb7\xef\x80\x02\xfel\xe6\x80w\xe1\x1a0\xc4\xe3k\xf4\xcd\x9f\xcd\xc0\xabP\x99\"\xb6D4\xca\xf0\x0d\xfb\x8b\xa0jj\xe1\x93\xf0\xad\x037\xba\x99\xae\xa6\x13O$w\xd3\xc8\xed\xb4s~\x9f\x8cX\xfb\xb7\xec\xbae\x00\xbb\x93\xb5}\xc2\x8a\xd06/I\x86\xb9\x93d\xf5\xb6(7\x17\x14\xdf\x90K\xfc\xafo\xf8\xa9L\xaf\xb7\x13\x9a\x1b\xbb\xe0\x01\xb6\xcd\xed\xbf\xd8\xa3?E o}\x93\xae\xf0\x03\x9f\xf9\x99aiZa\x05\xc0\xa3e#+\xf0\xa5\xbf\xa2\xf8\x00-\xd8\xfb\xf2\x84\x1bM,\xf5\"h\x97R/r\xaa\x17y\xcb\x0dn\xe3\xb2\x92\x0f\x12\xf0z\x91\x93J\x11\x10\x81\xd7\x8b\x1c\x1b\x8c\xcf\xa7\xf9|nv\xf8\xbc\x066\xffG\x01?\xaf\x17:,\x9c\xaa\x15\xeb\xde\xe2\x9b\xea\x02\x18\x83\x03v\x88\xfb\xc2\xabyg\xd7k\x8aX'\x1e;\xf4\xd8[\x8f=\xaf\xe3~z\x1e\x80\x0f4R\x8e\x05q\xdc\xceGF:\x93; \x1f\x9c\\f\xfc\x0bd\xf77\xc41P\xfb}u\xc50\xff\xd5|\x9e\xf2\xac\xcc\xc7\xdf\x8d\x1c\x88x8x\xa3:\x01\x00{\xd2\x1b \xfe2\xcbCG\x8f\xe9\x8e\x16:\xcb\xb6\xden\xbcu\x04u\x8f1\x18\x0c\xbce\xaeKl\xfe\xf0\xb5\xb9\xf95H_Y\xd2\xcf\x1a{\x178}\xee\xb1>%y\x86\xda\xb3\xc6\xda|\x10\x81Oq1&x\x03O+K\xe53\x1c\xc2\x9d\xe0\x0fK\xf3KK\xa7/\x9b?\x8b\xfa\xa0~\xc5(\xa9R\x7fA\xd7W\xbcZn\xa9vj\xaf\xf6\x0c5\xfd,\xb4\x8b\x8b\x80/sD\xfb)x{\x85\xb3\xde\x86\x12R\x00\xbb\xfa\xac\x15\xfb\x14\xfb\xf6\\\n\x1b\xec\x9f{U\xb4\xf5\n\xe0aa\xd8\xd8\xd5>\x9bz\xecyy\x14\xb5\x7f\xf858\xb4{\x0f\x88\xf8\x1eC\x15\x94\x0b\xb8\x91!|^\nm<\xf6\xda\x02\xde\x13\xfb\x8a.\xf9\xf8\x0b\xe55P\x0cJ\xfe\xb0J\xaf\x99\xb6\xce\xda\x94\xcf\xed[\xf4\xba\xec\x9c\x0c\xe1\x04\xd3K\xcb\xaa\xb8\x195\x82\n\xa5\x0e\x0d\x8e\xfb\xfdl\xc2\xf6\xc1\x86\x9e\xd7\xee\xa2\xb9\x1fC\xc4\xf5q\x86\xd786\xbe\xf6\xb0\xecv\xb3\x8f(\xf1\xc7\xd0\xe4xn\xe9\xb0\x8f\xf2\xde\x94\x02\"\x08@\xd8\x1d\x16\x9bp\x9c\x82f\x8e:\xcb\x0b6hJ\xf2\xffb=\xcc\x05\xe1H\x9c\xcc\xd5tC\x1b\xa1\x95z\x14\xd1\x8a\x04\xe34\x7f\xccV\x0dJ\n\xc1:M\xc7+\x8b$\x7f\xc3 A\xc0\x00^\x9aG\x9aA\xdb\xcc\xed\xa8\x95\x10\xdfX\x80\x190E\xc1\xc47`4\xa9\x0c\x87R4\xba \xa8\x98\x12\xf0o\xd4\xbc\xab\xa6\xba`-U\xf1P\xea\xdf*\xa0\"\x18\xb9P\x1c\x9eV\xec \x9b[!s\n\x1a\x10\x05\x1f\x8b\"\xe4\x12,\x07g\x16\xf0\xf9n!\xfe \xe1B\xe5%\x1cWg\x80E\x1c\xf0g\xc4|G\x9c`!\x15\xd1+\xb5)~u\x05\xc4 ;\x10=\xdc\xdf\xc7\xd3w.\x1bA\xd4\x84vO\xecJb\x90\xa8\xd0\x14\xfc$\xe1\xfe{#\xc7T\xe1.a{\x03\x9exZ\x1a\x92\x83m\xc6\xac\x89>\x83\xea\x07\xf0wi\x03\xfc1\xb0\\Z\xab4\xe8\xcf\x81\x17\xd3\x8a\x99\x03:\x16\xeb\xe6\\|\xad\xda\xc9@F\xec0R3\xd4D\x91\x01\x06\x8fE\xde\xb1.\xa6\x86\x14\xb2,|\xf3\\/{\x8eF\xdf\x08\xfa\x0e\x1bX\xaao\xa1\xc5\x0f\x81\xe0g?\xa8V\\\x9f\xf4\x13\x87\xcfJ|\xc7\xcd!F\x83\xb5 (\xd0\xdc|\x0b\x03>\x8e'b)E\xec K\xacK\xc9\x87\xa5T\x8fZ(\x9e\xcc\xf1\x01i\xd1\xac\xd9 \xc6q\xbf\x0f\xb1\x0e;\x80(\xf8\xde\x00\xa1\xa23\xaa\x91\xf2\xc7.K0(cf\x04'\x91\xbdKZzg7E\xa0\x05\xf9\xf7\xa9\xfb\xe2\x94\x94\xbcm\x0b\xb3\xc8\x1dbiZ\x9eHf\xeb\xc6\xd0\xb5|\xa7\x953[\x170C\xcbMz\x03`>\x84)-\xc1\xe3\x8f\x0b\xf0}\x1e\xc6~\xb6\xb3-\xb5\x08\x80\x80\xb5\xcc\xdd\xfbt\xe6\x8b({h\xcd\x19\xeeZ\xb3l\x1f\xfb*\xb06\x08Y\xcfC\x7f\xb9\xe23{ \xdb7E^\xe5\xa3\x1b[\x9e\x9e\xafaP\xad&\xdd^E\xf0P\xcb+\xe48\xb5\xf4R\x08afp#Q\nr\xea\xb3!q\xc5\xc8\x00\xa9N-MIrj\xc9J\x17TKVB\x9dZ2\x08r\xeaiRxSK\xfe1\xf7\xdf\x17\xfd\xd8\x18z\xeb-\xc1@.\xc1\xd8\xe1E\x94&\xb1\x1fm\xf8c\xb1*o`\xdaK\xfb\xa0\xd85\xac\xdfn\x81C\xae\x8f\x0dc5\xe9\xf1\x98L\xfb'u\xf6\x18O,,[$6\xe7\xc2\xec\xc6\xd5\x9c\xf6G\xae\xb9\x91o\x00\x03~\x87e\xa8\xea\xb5\x10\xe86\xcb\xd7\x86\xb3\xc6\x9e\xebh\x81\xb6<\xd93\x8b\xe9\x05}\xfd\xc8N\xe5v\\\x07\xae8y\xac\xa7\xd6\x8b\xed\xe2\xd9\x0d\x9a~\x9d\xc4\xcb \xe5\x1f\xa1\xe5\xb7<\xfb\x08\xad\xca\x95uK-o\x1b\x97v\xe5\x8aX\xdf\xc0\xb3\x12\x856.B8gE\x00\xda\xa8\xe1\xf4\x15\xc0\xf1!\xb2\x1c.\x90m\n(\xb6 \x99\x0f\xe9\x06\x96\x95\xd2E0\xcf\x9c\x06D\xd5.\xfe\x03k\xd1\xb64E\xf9\xc0\x89\x8b\xbd\xcb\xde\xb2x\x00\xf8q\xc3\xa2\xa2)-\x99\x8aS\xe1$\xec\xa9\xf4%\xa6\xf6\xbc\x91\xd8\xc0Y\x9f9\xd2\xc8\xfd\x80\xf5\x9e\xdc\x13TM\xfe\xee\xb3\xde\xd3\x9e^Jn\xa0\x82\xa1\x8aD\xe9\xa3Hf\x83\xa6\x10\xe4\xa0\xd4\xc2\xb3\xcfb`\xdf\xc2\xd4)kC\xc7\x138J\x96\xbf\x07\xfej\xc5#\xf0\xef\xe0\xe9\xf84\xc0\xc4\xb8\x92\xa8\xcc\x18\x9c\x0dq\x06\xdd\xd8\xeaB\"\xe0N\x06br\x01\xb5*\xbc4pi\x80*W\xbf2s=`=\x86e\xb5\x072\x0e\xd6\xabN/\x8a3\xe6\xa7ip\x1a\xf1\x19\xcbb\xe6\xb3\x95\x9f\xf0(\xdb\xa0\xf8\x07\xf5\x9ci\xfe\x91\xe8^\xaa\xa7\xf4H\xa3 f\xec\x0d\xe7\x8e\xd6[IT#\xaf\xd2\x02\x8a\x80\xfa\x82\xc1P\x94\xd6\xf5\x9agE\x7f\x14{\xe9P\xbc\xa2zlT\xca\xc2f\x08\x9a\xd7uJ\xb4\x0d\x17\x0d<\xc4\xd0\xe0\x84\xcb\x95\xd7\x1d\xc1\xe7\xaa\x1c\xd1\xd3\xce$\xd3*\xfa\xac]d+~}pK\xc7\xc3\xce\x83\x07\xf2\x80\xdd$\xe8W\xdbyu\x80\xbd;\xbd\x11\xeb\xdd\xf1\x97\xab\xc75\xa2x\xb7wW\xe4\xfc,\x8f\xb3zV\xef.VZ\xc5\xa9\x91\xf5\x04\xb2B\xb3\xceS\xc88\xcd\x1ek\xc1\xfa\xda\x04\xe3\x16\xa9\xb8$^\x92\xb2\x01\xf1*\xc4=\xce\xf8N\xef\xc9\xd3\xbb\x18c\xa1U\xd8\xa6\x04\xccFP>\xe0\xd9\xca\x8e\x92\xd0\xad\x91G}\x08\xf1\xe3\n\xdc\xa5\x19\xc1\xa3\x1dwpx\xc6\xa3\xecp\x19d\x19O(o\x1f\xe6A:\x913\xbd\x08\x0cu\xb5x\"\xe7\xe1\xd0ub\x0f\xfc\x97\xc4\x837%\xc5\x14_\xbc\x0f\x89?N\x82\xacH\xdc\xdd}\x00\x89\x9f\xe5\xab\x90_\xc8\xa4]Hz\x97\xf8Q:\x8f\x93\xa5L\xdd\x83\xd4\xd7~\x9a\xbe[$q~\xba\x90\xe9\x0f!\x1de\xe2x\xb0\x8bu\x97\x1f\xc1\x8a\xb7\xe97\xce4\xdf]6\xc9yL\x9fF\xf9\xe0\\\x0d\x07U \xb8\xd5\x88D.j\x80\xd5\xd8\xca\xcfS\xae\xbd\x1a\xc7&\xfa\x93\x01I\x85\xa2r\x1f\x82\x16\x13\x9e\xe6\xcb\xca{\xe3\xa9,\x1a\xc4Q\xc1\x92\xc5`,\x08 \x89\x1fD=\x8f\x05\x90r\x1c\xa4o\xb3Y\x00r\xfcL\x1b\x18\x1e\x9e\xc1\x119\xd4\x12l\x9c\xc7r`\x88\xc4od\xdb<\x96\xd6\xa5xg\xd2Ztch\x83oN\x0e\xd6\x87\x8f\xf9r\xc7\xe5H\xc7\xbaA/\xed\xd0 y\xa9\x8d\x0ff<\xcd\x92\xf8\x12\x17\xb6\xfc\xd1\xf5\xb3!M\xb7\xc5\x16:u\\OZ\x02$\x830H3\x1e\xf1\xe4\xb9\xd8\x87\xa4\x13\xe1\x1e\x17\x9bi\xcfU\xfbk\x9d\xde\xd2_\x9cZ\xd1d\x19\x9f\xf1/\xe4wjsndj\xf3oV\xd5\xe7\xb9\x9eW\xce9Y\x13F$\x98%\xea\xabz\xae\xed\xab\xd3\xc6\xafN\xc9v\xcb\xdc\x86\x95\xa0\xc8-br\xa5\x9f\xf5\x14\x1d\xdb\xa7\x06\xb6O\x8b:\xd5\x14<\xca\x08\x02\x04gL\xaf\x95\x86\xbb\x10`\xa9\x89\xac\xf7\x04!I\xb3$\x98f=\x92\xaa\xdf\x1f\xba\x03\xbc\xadDZ\x08\xec\xb6z\x9c\xaf\xe3R\x81f\x9cD\xb3\x8d\xf6m\x8d\x15\xa6\x91\x9ci7E3Wg#\xdf]\xae\xb8d%\x9f\xfb\x91\xe0&\xc5>\xc3|6\x0d\xfd4e~\xca\xfc\xe2K\xc4\xb9\xf0C\xe9\x86\x1b\x19\x9e\x05\xf7g\xd2LK\xa6d~\x10VS\xe4y`\xdf\xea\\\x99i\xbb\xbc\xe9E\xaa\x99QS\xbc\xad\xe5h\xe9g\xbe\xd5;Y\xc4/2\x94G\x99\xe34y3}(O\xc1\x16\xa9\x18.\x88}@Q>\xaa@%\xab\x82$\xf3\x98\x8c\x01\x80\xcdT\xa1\xe1U\xc6\x9eG \xfc\xfe\xf8\xc3/\xfa\xdb\x05\x062\x06\x89\x06 \x10\x06\xebc\xac!\xc6:c6Fl#\xf0R\x00V\xb6\xdat`\xe5\xeaH#z4\x10\x10\xa1\xcf3\x12\x01\x87\xc6\x10\x0f\xaa\x03\xaa\xe1x}\xca\x8b/ \xf0\x16\x91A\x949\x05a\xce\xde\x04\x11\x15\xf5\xae\x11\"M\xbdkY\x81\xd5\xaf\xfd4\x0e\xda\x1d\xb8#\xfc\xf7\xeb\xf0\x97\xd0\xa3|\xe6Tn4\x15\x9d\xc5kM=\x14\xc7\xc3\xacHoH\x02n\x8f]\x16\xb1\xfe>\xe8\xc03\xcb\x9c\xd1f\"5\xf8\xc5\xd1\xd4o_D\xcdcJ\x06~\x18\xc6Sg\xcbb\x8an`LQ\xb3\x0d\xedJ\xc8\xc0\xb19F\xb3)\xf9\xbd\xaf\xa2\xd4\x9fs\x87\xb3\xa7O\x9f\x82x\xd2\xaf\x82/\x17\xd3\xf9\x98\xf9\x8f]\x00\x9c\x0f\xdf@\xa8\x06x\xa3>\xf7@\x97\xb6\xbaD\x9b\x1fQ\xa5\xaf\nV\x0c||\x04\xba\x0d\xc4\x81\x01\xe2\"\xe1\x83`\xb5d\xf4\xb7 JW|\x9aU~\x0c\xa6y\x9a\xc5K \x13\xa5t\xa6\x98\xa0q\xbd\xe0\xa4 \xd9\xd5j.*\x11r5\x1c\xd6\x88YI\x8e\xe5\xf2\xa6(\xae]\xfa,to\xa0/\xd2\xc6k=rw6H\xa2\xb6\xef\xea\xeeN+nH\x8eD=\xb0\xefC0\xcb\x17\xcb%\x9f\x05~f\x95jH\x05\x0d\x1a\x19I\xbf3\xe6}7\xfd \xe1\xa2\xbb=\x7f\xda\xa0\x9baRw\xc3\x07\xb3x\n\x922{\xb9Uitt\xca\xb3\xd7\nI^\x81R\x83\xcc\xb0\xba\xb0\x12M\xad\xc0\x92D\xc0\xe4]\xb0\xe4q\x9e\xc9\xe8\x88\xdc+\xfd\x1c\xac\x92x\xca\xd3t\xd2\x835\xfc\xf3\x0fEpIy!x \x0b\xa0\xb1m\x1b\x1dQ\x8f\xa6\x07j\xa4\xdc\xfa\xb3p\x88\x0b_\xea\xb1 \xb8\xd8HG\x9d\xa6O\x80\x12u\xb0\x8a\xd3\xecK\xe9@M\x9c6\xf9 X\x8a%\xf9v\x9a\x04\xab\xccj\xef\xa3\x1eE\xc47\xb6\x9a\xa5\x88LJ\x12\x05\xb3nu\xd1\xa6?\x05\xf3W\x94o\xdb\xf4\xeaOF\xeb\x10\xf4\x07\xf7\x86\x12\x02N\xaf\xe7\xb1\xde'=y\xaa(?\x1c\xd5o\xd9UZ\xa1g\xc2qA\"%\x9b~\xbe\xf0\xa3\x88\x838\xdb\x01{J~\xce\xaaY\xee@\xc0}H\x0f\xb8\x11\xb9\x16\x0e\x07\nn\x93y\xae\x81\xa7\x01tb\xbb\x02\x14\x0b\x16\x82l\x0c\x16b/\x8e\x12\xee\xcf.\xd3\xcc\xcf\xf8t\xe1G\xa7\x1c|\xdd\xcc\x07\xd3\x84\xfb\x19\x97\xa2w\xa7\x97\x02R\xf5\x04`\xc0\x8eq^\x90\x00Yd\x9d\xae*\xd4\xb3~\xc5\x8e`\xd9\xc0\xec\xf1:\xe8%E\xbdt+\xc8d\xc5\xf2d\xfc|\x11\x8430s\xced\x9e\x1d\x8fD-\x94m\xabZv\xc0w\x87SI\xed\x9c\x85\xc7\xb6\x8c\x1bF\xea\x11\xa4\x03\xc43=}\xcf\xf8\xa1\xd8\xed\xe0\x16P\xe2G\xb3x\xe9\xc8@\xb5\xc8m\x14=h4a\xcc\x06i\x9c'S.ob\x08\x8c\xd1\x83sI\x1b\xa5\x812\xe9\x93|\x172%A4\xe3\x17\xaf\xe6\x8e\x0f\x02\xbd\x85\xd3\x97\xe9\xa0pq\x14\xd3b3q\x14\xeb\xd8\x9f\xcd@\xd8\xaad\x14\xb0*\xeb\x89NO.\xba\x1el\x7f\x1bC\x10\xfc\x0e\xfc,\xf3\xa7\x0b(\xe9\xf4\x8a\x85)\x052Ig\x00T\x89\x8c/XX\xa43\x96\xf9\xf5p\x93*&\xa1\xf3\\kR\xb5\x8d\x9a\x19/\x97DGy7q\x80\xd1\xe6MF\x7f\x156\xbd48.\x14\\\xea\x10\xb1 \x11\x0f#\xe4>#\xf6DwM\xd0\xef\xbb\xca\x97@Qo\x0c\xaaA\x8b\xdd>\xd3\xec\xbe\x9aW\xa1\xd8\x8fO\xfc\xe9\xfbF_\xe3\xe2\xf1\x93\xd3\x942\xb8S\x0fq\xacU\x8f\xdc\x86\xc2q:A\x01w\xe2\xa4\xae\xc7\xd2~\xdf\x86p+<\xa2\xe9sG\x1c\xa4\x1b\x8c\x08f\x0d\x16%\x18\x947\xac\xdfhd-M6\x18\xa9\x80t\xd4\xa5\x88\x04\x0d\x94\x86\xe88L#\xca!\x19\xebV=p\x85\xad\x8d\xc8N ?|\xf5'K.;p\x02\x1b\x1dW\x8f\xfe\xa8\x81\xa0RW\xa0Y;\x83\xa3\x9e\x04\xea \xack\xee\xbdz\x94\x91u\xd2\"\xbb\xa0\x1e0\xbc\xde\xb2\x1b\xdfRO\xa3\x01%\xf5\xb4\x98i\xd7\x1f\xe8\xd3p\xdd>%\xe3-\xeajw\xd3s\x9d~m_\xa7_\x1eK\xc6\xc3\xef\xa3w;\xd7\xef\x9d\xf8\xbb\xfd\x91\xfb\xd8j\xebM=\xa0\xb0\x0fA\xe4@\xd8{P\x0f\xcdQWJ\xd8\x98\xa3\xa2\x00\x9b\x07\x91\x1f\x86]\xe8\xc3\x0c\xd8\xb9i\x87\xf3\x825\xb7\xab\xe1oM\xb6\xe7\xf4\x8a\x98\x05:/\x94\xf2p^^aW\xf7W\xb3E\x90\xc2\x0d\xd7\x11\x14\xd0\x94\xc0\xba\x11\xc0\x0e\xec\xc5v[\x80\xee\xd7\xa2\x8a\xed\xc3B6\xed\xc4\x17\xadV\x06a<\xf5\xc3\xb7Y\x9c\xf8\xa7\xbc9\xe6\xda\xd4\x07\x02\xd8\xe6\x15\xa45\xda\x19\xd3U\xca\x95\xef7\xc6^\x97>#\xc0\x9c\xac\x97%9\xc7\xc3?\x9e\xfb\x9d\xc8\x1dd\xf1\x17\xf19O\x9e\xfb\x84\x06Y\xff\xd5\xf9^\x1fS\x97a\x9c^\x14\x7f\xc6W \x9f\x82\xe9ZO\xbb\x97g\xf6Wi\x9b(\xd7\xaa\xf5\x9b\x82M\x1b\xfe\x06ycS/\x119=\xd0\x10\xd5\xbaV7>\xb29\xf7f`\x90\xd0\xcb\x12\x7f\xca+M\xb0\x036\x8d\xa34\x0e\xf9\x002\x1d\xf0w\xa4\x92\xce\xfd$B7\xe0\xb0\xf7w\\SL\x17\x17 \xa9\xc9@%UZb\xb5\xadC\xebR\xea\xb4\x86hA\\\xc5\xf9N\x99\\j\x0cw\x86\x96+\xe5[\xbbd\x00\x98\xc0\\\x1f\xa8\xdc\x03\xc2\xa0\xe9\xf7\x82\x12\x890v\x98\xe1N\xbb4%!\x02\xe8\x8b'\x1e\x04\xd1\x82'A&\x1d\xc1\x0c\xc1\xd2C\xa59\x01\x9a\x99\x04\x9a`\xfd8\xd3\x8cF\x9a\xa0\xc5\x007\xf0\x94\xdc\xea/\xa4\xc1\xb6&r\x86\x8f\x1et\x9a\x9fj\xad\xdd\xebT\x1a>\xba\xef\x96f1\xd7\xac\xaf\x19\xd0ti\xa1M\xe3\xbc3\xa4\x02\xe8\x8bt\x8bK\x82\xbd\xf6[\xea\xf5\x89\x92\xaa\x08\xbc\xac]\x1e\xe0\x0c^H\xa2\x9b?\x88\xe2d\xe9\x87\xc17<\x81k\xa9\xa0\x96s2\xed\x8678.+\x95\x0d\xa5G\x0c\x7f\xe0\xa7\x97\xd1\xd4E\xcf\x04\xfe`\x95\x04\xcb \x0b\xce\xc4\xd6\xa7\x8c`\xd8A\xf5\x13p\xb1z\x0b\x0e\xeb\x19\\\xb3\xc0\xaaF\x89m\x17<\x7f\x8f\xea\xb5\xb5vE\xb1\x1d\x17bQU\x13\xf70Q\xbc>\x84f\x8a\xae\x82\xe5\x8f\xb3\xb7\xf5\xc8\x95Q\x8d\x96\x8146r\xf6\x86\xa0\x9f\x19\xcc\x82t\x15\x97\x89\xbb\x90\xb8\xf4/\x9e\x9d\x16i{*M&lc\xcd\x84\xcf\xc1@\x85'*}[\xac8\x81(\xfe\x9a\xab\xa6\x0d\x91v\xf7(D\x02\xa1\x8f\x7f\x92\x9a\xa8\x049\xf30\xd6\x1dbwC'\xa5>J_\xfa/\xd1_\x05\xba\xe8\x00,\x11Get\xa7\nN?\xee\xdcaA\xfay\x10\x05\xe0\xa2\x1a\x1c\x0dq\xf0\xf2\xe1\xc4\xd2\xdfP\x9bQG'0\xd4\x88\xc3\xde\xb6\x0b\x82[\x18c\x1a\x9cF0\xf5\xbb{;\x9d\x88F\xfb'\xac\xfb\xb3Re\x15\x1f&\x17\x18m6\x05h/\x0d\xe0\x9c!z\xa5\xdbT\xbf7\xb7\xb7\xd6u\xe7\xb1\xc60\xec\xb6\x99\xdadz\xe5\x8c\x03Q\xd0=\xb2pi:\x81>pn\xa3\x9f%b?\xa0\xbd\xd2\x0e\xef\xd7\xfd\xdaH\x02Y\xf7\x98$\x03V\xee\xd1\x01+\x05\x9dm\x86\x0e\xe3\xb4\xb3\x81\x08oCUgX\xec\xe5\xe8\x10\x03n^I\x97\n\x15\x9a\xebjtG\xd1\x1b\xc2\"\xfc\xd5J|\x1d\xf3 l\xe8\xca\x9f\xf4\xb4\xe6\xce\xa8\xe5\xcc\x9bbEt\xd8z\xa0\xda =6\xf7X4\xe6\x13\x88\xe9\x81Nx\xc8K\xe5\xb6\xe3\xea\xad\xe0\xf2\xae%\x16\xe0\xce\x90\xf6K9\xbco\x89 \xfcp\xcf\x1d,y\xb6\x88g)Ejw\x0d\xff\xc0\xa9\xe4\xec\xeaG\xa8\x90^\x0cp,\xac\x96\x9cv]6\xf3re\xa0\xa6\xb1\x9a\xad\xd9(\xa0(G\x12\xcb\x80\xd7\x86\x82!1\xe3\x9a\xdf\x80\x05\xa4\xf2e\x90uXX\xc4Q\n\xec\xbb=vVD*\xf5\xd8\x89\xc7\x8e!\xc8\xec\xa1\xc7.0\x9a\x96\xc7\xde{\xec\x99\xc7^y\x10tk\x0e\xe7/\x9a\xe2c\x00\x11y\xa1\x14i\xb9\xdc\xbd\x0b\xf14\xee\xd6\\#\xe8\x1aW-\x10\xff\x02\x9cu\xea\xc9\xae\x07Qq.\x06\xa7<\xf3 \xf2\xcd\xc5 \x15\xaf\x97\xf0\x8a\x9a\x0d\x0f\x02\xd9\\\xa0\x06\xc5\xf5J\xc1\xcc \xe1i\x1c\x9e\xf1$\x85\xe6_\xc9\xad\xa5H\x15\x8b\xfa\x19SA\xf3\xed\"-Vn\xc0\xd2\xb4\xaa\xa0 &\xf9\x10\x1b\xf2+\xf8\x1e\xf8\xbeq\x02\xb7\xec\xd2>n\xd2K\x91\x08\x8aIb\x9b|-f\xab8\x89C\xe0]_Z&\x9f\xf2\xac\x07\xab6@s<\xd7c\xaf\xc9\xe8%\xa2\x0f\xe8tO\xf0LAi\x808-\xe8 \x9e\xe2\x83\xf1\xd6DP\x80\xb0\x9e\xae\xfa\xbc\x8f\x9e\xa1\xecB!bd\x8a\xb7H\x9c\xde\xf3 \x99\xe6\xa1\x9f\xb0 :\x8b\xa54\xc7c\xbd\xe7/\xde<\xff\xea\x8bgo\x8e_\xbc\xfc\xd1\xab\xe7\xcf\xde\xbdx\xf5\xd2\xa6x\x17\xad\x9e:\x01!\x8bA\xa5\x92\xe8C\x03\x18o\xa9'r6^\xa3J2\xf6\xd8s}^R5/R\x89/\xf8\x90*\xfd\xf4\xd8\x99[x\x15\x14\xeb\xa3Q\xe0\x06\xc7gzV-C\xc5\xbb\x02\x8dh\xa3\xae\x13\x14\xa8[\xe2\x90\xc5\xaa\x10\xf4m:\xb2\x97xT\xc7\x97Rf\xc6F5$s=\x1b\x9a\x17\x9d\xbe\xe5IB\x93\x000\x19&\xa6\xa9\xb8C\x8eV\xad\xa6'l\xdd\x93\xfa\xed\x92\x02\xfd\x8e'lyRT\x0c\xab\xd0\n\xa6\xb8qZ\xe3*5\xa0\xfc\xda\xc12\xbd)5h\xe8\xdc-O\xdf8\x16k,\"'/V\xf3\x16U\x82\xf21\\c>\xa9\xfc\x8f\x93\xe04\x88\xfc\x90T\xf8+n}\xc4\x9e\x99\x99\x92\xd5\x7f \xde\x83`\xb7W?\xcd\xb2\xa7<\xebr\x15T\x0e\xf2U\xc1\xe8\xbdr\xb8\x0b\xbb\xdc\x01[\xa2\xb3\x07\x89\x14\\L\x86I\xf5\xcc//\xfct\x8d/[\xe6\x91r\x12o~\n\xf7\xdb._\xb3\x900\x86\xfd\xa5{\xc00\xaa\xfa\x9d;\xec\x12-\xa5\xd8>{\x0d\xbc\xaa\xb4`\xc0\x1f\xefu\xb4\xc0\x9c\x1e\x86\xa8\xa3\x1cE\x99\x83\x006a\xd4\xae\xf2P\xa2\x15\"N(\x83\x80\xc8w\xee\xb0\x13q\xe6\xd3X#\xaf\xe8\x18|\xa5\xd7\x15\xb0q4j?\xb52M\xa0#\x16\x7f!\x10y\x0bz\x0f6\x02\x1b\xac2\xf9y\x91,\xa1TZRA\xfcW\xf0\xe41\xab\x08\xf5i\xdf\x15f\x7f\xc5\x18Glaf\x14\x87\xe1\x0e\x00\xe6\xc8\xd9\xca\xe5i~\xb6\xbe\xbc\x8fMV\xcd~\x95\x05-\x8b\x1a\x883.A8\xe5\xe1\xf1\xae\xe4d2\xe0d\"\xe4\xd1\xfc2\xc6]\xbdC\xeb\xec\xe9\x85\xa8[\xb6&7\xbfj\x93\xacmi\x11\xe4\xa3\xdcTp\x17\xf1\xcb\x00}\xf5\xfe\x9e\x83\x14\xbd\x95\xf5\xe0\xad\xb0\x93\xdd(\x87.\xf7\xdc\x91\xda\xef4\xb0r9k\x02\xa0%u\x8b\xb0\xb3bE\x9b\x82\x97\xc3\x8f\xd6O\x1f\x82\xd8K\xd8\x93\xdd-\xb1\xa0\xa1\xe3\x1210\xe6\xbe\xd9\xff\x95\xf3\xcc#\xfa\xac\x0b\xbfF,\x00\xd7UV\x12\x1b8\xc7D\xae\xa4]\x81\xe3\xab\xd3\x8e\xf9\x15\xd8\x89\x02\xe7\x9c\xca\x83\xbd\"p\x0e\xcd>\xfbE\xca\xad\x1c\xf1w\x86T \x10q$\xb7h\x99\xea\xe2-\xb1\x97\x83`r0\xf5WY\x9e\xf0\xb7\x99?}\xff.\xf1\xa7\x9a(\xa9\xe2\xab\xa3U#\x15I{D\x94wR\xd1n\xf3\x8aphH\x88\x90\xd2\x9a\x90\x89<\x0b\x07N*\xddm\xe5\xb8\xa9I\x8f\xa4\xca\xa9=hdR\x19\xd50\xc2\x9b\xb8\x81*\x1b\x0d\xa6\xf1L\xe0^\x0eWu \x08D\x84\x8c\xea\x9a\x0e\xa8\xd7\x90\xc7\x93j\x05\xdc\x81\xa5\x90\x02}\x85t\xd7.H\xf7n\x0e\xed\x15e\x1e\xc7#\xd6K\xfcozu\x1ae\x96=\x11\x18\xdf\x9b\x9d\xfb\x1d\xcaf\xc97\x97#\xd6\x13\xffz\x06\x8a\xf3\xc1<\x8eY\x9f\xf1\xc1\x89\x9f\xc0\x7fQ\x0eh\x83\xe8\xca\xec\xdc\x87z\xb7,\xb8\xdd5\xa2B5Hn\xd7\x08\x9c`\xd1\x10\x94\x17q\x02\xc3\xe4\xd6c\xdb5\xbe\x1blu\xb9.\xe9\x04n\xb4b\xa4M\x8a\x1a\xedV<|\x9c@\xfc\xd1qBX\x9b\xb6\x9a\xecD\xe8\xac@\xac\xebV\xf3\x0bd\xf8\x87\x8f\x99\xcf\x9e\xb0\xf41\xeb\xf7}y\x85\xadX\xa0\xfe\xc4\xc3\xf8\xd4\xca=Q\xee\x9a\xea\x13\xcd5KT\xe8EHL\xff\x18\xaa\xc3\x87CT\x1dj\"vT\x1e>\xdc\xfe\xd8\xcaCz\x12\x15\x8f\xa1\xf9\x96\xed\x15Z\xf5\x1ex[\xac\xceC\xe3\xa4\xd26X\xb7-P\xa6\x94#\xda\x00\xda\x96S\xbd\xe3\xb2\xd31x\xc3-\xe6\x06\x8fg\xeb\x1a\x9f\\\xab\xef\x04\xc5\x94\x9f\x18\x91\x97\xa6\xf0\x16\xda\xc8\x98\x9ak\x0e\x1c\x86}\xe7\x0e\x8b\xc7J11\x11\xebr\xdd\x10\xb9\xed\xa8)\xd0\xfc\x01\xe2\xbf\xbc.W\xb9s\x9b\xf9A\xa4V\xc3\xee\x0dV\x83\x82\xb6N\xe6\xd7\\+M{]R\xf6Ulz\x1b\xcae\x88Ju`\xf7R\xbe\xeb\xeby\xf38\xee\xdd\x8e\xaa]\x0d\xd3\x00\xa5\xbc\x0es]l\xa8\x1d\x11+\xcae\xf6\xf46\xf5\xef\xb5\xeb\xa4\x9er\xc8N\xe9\x80\xe6\xb4^t\xd5Y\x953\xeb\xaa\xcaY4\xabr\xce,\xaa\x9c\xda\xe7\x96]5>\xa7\xed\xc1n\xab\x15.I\x8a1\x8d\xa3yp\x9a\x83\xf6\x95\xa6\x1a\xbc\xd0\xce\xd2\xae\xaf\x95\xa7\xa4&\xba\x92\x1b\xdf\x164*i\xe3V\x98\xe2X\xac\x87\xb69\x185\x9c\xea\xb8\xd7;>\xe6\x1c\x0c\x07\x0e4\x07s\x90&\xcer\"\xe9rp\xe6\x87\xb9\xe0h\x16J\"sV\xab\xed\xb1K\xd7\xd3\n\xcab\xd1\x98O\xd8\x01\xe5t]\xe6\x88\x7f\xe8\xb1\x0d\xacO!u\x9f\x8dQ\x9b\x9aM\xca$\xe9\xad\xa3\n\xb1\x1a\x8d\x8f\xa6|\x04\x94\xbe\x1b\x94<\xdd'\x98z*\x80\x8a\x95[>c\xb9F]\xee(J5u\x8c5\xe0*\x992\xdah\xb7\x8a\x05\x07;\x02\xba\xaf\xa2i\xe1\xd4\xe7\xf8\xb8#(\xe6\xf3\x11\xf0\xbe]!!\x89\x04-\xe7F`l\xd0hS\xf1\xa7@\xd7\x97q\x80J\xc4r\xc7|\xd2\xa1\x9e\x896\xe8`T\xd46!\xc6\x14\xeb\x1d\xe0\xed71y\xc98\x98\x08\x1e6pY\\\xfa\xe5\x8d)\xb8b\xae`\x94\xb7\x95s*%\xd2\x97(\x98\x8c\x03i%7\x14\x88\x99\x0c\xd2\x15\xdc|\x0c<6\xa4\xee\xee\x81*-)?\x9b4~V\x8ac\xa3&\xeb\xf8\xb6iG \xa2\xdfzG\xf1\xac\xf0j\xd18\xef\x16:!\xb6\xe3\xb8:\xa1\xf6\x19\xa1\xe7\xb1\xd9\x19<\xccbD(\xc9d\xac6-\xde\n\xdew\xcc\xf0\xc8\x92\xb1',\x12\xd3\x9d\xb9,\x18g\"\xb3z\xd91k\xb8\x08\x07\x1f\x8d\xc1\x81\x05^h\x95\xedn=\x06\xc2\x1b\x8b\xca\xd8\xb4\\\xc5I\xa9\xc9!\x1b\x95\xbaTu\xa3\xac>\x96&\x00t\xb9\xb55+\x88\x0b\xe8\xa9\xec\x03c\xedw\x8b\xba\xdc\xc6\xaa~\xaf\xc6\xb0\xdc\xfc\xeb-\xb7\xad\x9a\xbe\xeeU\x84G7\xebK\xa7[U\xbf\x10\xfc\x14\xcf\xaa\x06\x05\x1b\xe6\xfd\x80\xfe\xf5\x81\xf2\xc6,8\x8b\xa9S\x17z\xe2^:u\xe2z\xba\xd8X\xa6N\xe0R\x84g\xea\xe8\xe6\xd0hG\xb8t~\xfe\x01\x85q:{\xdc\xec\xf5G\x19\x8bi\xa1*\x17N\x88\xce\x88\x8bSc5T\xa4\xc72e\xb4\xc4\xf6Y\xfe\x03vS\x8eY\x9e\xa3\xea\xb1~\x1b\x04\xab\x04\xdb,\xf88\xd2=q\xf9\xbdf\xe7\x01\x1a\xdd\x1f,\xfdU\xbb#hU\x81\x1d\xb0\xcc\xe1\xe3\x08T\xcf\xe2\x7f\x15%\\\xe9|\xc9\xc9+Zi\xf3\n\xff\x07o\xbdc\x0d\xc8\xbd@\xe0\xd516O O\xc5\xbe\xa1Zq\x05\xd7u\x12D\xb3\xf6P\xb6\xddg\x16\x8f=\x8f(S9\x9c\xa8 \x85\xff\xd7<\xd5\xc5(\xda\xe0\x10\xce\xfdv\xba\xdd\xe9 \xadD\xcb\xc8\x98\xe2H\xe6I\\\x0b\xc8\xd5t\xdcF\xff\xed\xe0]\x00\xe6p\x0c\x82d\x0fe\xc4\x13\xd7c\x9f\xc6q\xc8\xfd\xc8\x01V&+}.C\x01\xd4\x05\x81]\xf4m\x8cY\x13\xe4<\xdav\x07A\xc6\x13?\x8big\x8e\xc6\\\xca%\xfa\xc8fAN\x1a\x90\x1bK7\xa5\xe5\xc9!\xbd\xfe\xa7\xf2\x9bur1\xaf\xe3U\xa7c\xb5yX\x9e\xdd\xc6a\x94\xc8\xd7\x0f\xa3f.\x1c\xe6\x08\x1f\x8c\x1f\xac'\xf9\xeaQ}\xddET\xb2\xa5V\x13\xcaV]\xd2\xdbF]\x128Z*%\xf3)J\xe6C\xe7B\x06\x08\xbf\x90\x0e\x12\x99t\x19\x0eh\x0e\x13'R\x02\xf4\xf8\xec\x16\xbe\xf2\xaa\x8d[\xfc1\xc0 \xe8\xc2zF\x9c3y\x89F\xaeN4\xf7tN\xb5\x10\xc5\x82\xa4 \x16\xc9\xdb\xdb\xf2\xc2\x9e8\x9f;\xcb\n\xc71t!b\xd9>\xe3p\x19}i\xe1\x86\xf0T'\xbe\xda\xc2\x85W[\xaft\xaa\xe2f\xe4T\xb05\x91\xcb\x96h\xcc\xc7I\x0bJ\xf5\xc8\x91.\xc9\x02\xe6\xa5R3e !\x03\x7f`/\x040\x9f\x1bzdf*'\x9cs\xe8n2\xb1\xc2\x02\xe0p\x02f\xae\xe7\xf2J*\x1a\xd2\x08\x82\xa9\xe0#\x0e\xc8\xe2l~\x02\xce\xc5\x9c\x128\x1b\xc7\x83Y\x1c\xf1\xc7.(\xe0/\xd8\x81b\xe2\xd0\x1a\xf8\x18%&\xd2\x90\xbd\xf8%\xf6ogVHS\x0e=\xb6p\x96\xb02fp\xddJ\x82\xf9\xb0\xfe\xd1~\xdf\x125K\xcc\x1c\x11\"\xa84\xf7\x9c6`\x03@\xe0\xb4\x123\xdb\x1c=\x8c\xd7\x03\xb9]\x0d'\x0e%B\xc8Py\"GZ%\xed\xb3\xc3\xc1t\xe1'\xcf\xe3\x19\x7f\x969[\xae\xcb\x9e\xee\xb3\x07\x0f\xb6\x1f\xed\x82\xc5\x12{\xb2\xcf\x1e\xec\xee\x0c\x1fA\xf9Cp:9\xee\xf7\xa3\x89\xb4g0\xc0y(\xedG\x0e\xad <+Ax&A\xd8\xef\x9f\xd9\x81v\xd6\x82\x8e\x1a:\x89=\xf0\xd4D\xb8\x02z\xbe\xa3\xad\x9d\x1a\x00\x9dS\x97^P\xe40%4\x15o\xd7\x1d_H~\x00\xbb2\xab\xc8\xee<\xb6,/\x89B\x8c\x90\xa2\xe6\x0d\xf6\xf5\x9a\x96\xe2\xd1\x8e\xd4R\\.O\xe2\x10U\x12\x8f\xee\xdf\x82J\xa2v\xc2)\xf48\xb5-\x1e>[\x91\xc3\xb6\xe9vH\xbe\xcb\xdcb\xc8{(8J\xcd\xf9Bm\xf7`\xfb\xb2\x88\xd3\xcbx\x9a\xc9\xee\xd5\x8d:i\xf5\xa22o\xac\x9b>\xddD\x89\xa8\x97\xd9H\xc6\x95Q\x14,\xd9\x04\x953F~\x16\xbfV\xdaM(B\x95\xc0N\xbf\xf3O'\xb7\xc74\xea\xba\x0e\x8b\x8aC!_\xfdZL\xd8\xac\x90\x98v\xd54\xcc\xbbi.V\x84B\xc2d\xfa\xc2\xfa\xed\x90\x1az\xed\x1b\xe8U;\x97\x14X\xb5\x06\x1a%\x8e+=\xda6i\xa5\xeb\xeaf&\xe7`\x81\x9b\x80\xb3(\xbb\xef50}57\xbb \x92\xc0\xc5\x98c\xac?\x8c\xa1q-wF\xe3\xca)\xb4z\x98\x8f\xbb\\\x8f5\x89[\xbd\xb3\xfc\xd6:\xeb\xc3\xcdrP\x04\x01\xf4CG\xf3j!\xc5h\xda^\x0b\x01\x1a{\xa5\x15\xa1\xe0B\xa6ND[ \xce8\xfa\xa2\x0c\xe2\xe8\xf8x\xc4r\xf0/\x9aQ\xe6|\xc7\x91\xbf\xe4e\x993\xa7n\x02\xfd\xa1*\x1f\x99:q\xfd\x93\xf38\x11\xd5\x9b\xb1L\x0ez\x86\x8a0\xf87\xc2\x7f\xfb,v\n\x8anHE*\xbf\xdf\xf3\xcb\xcf\xbb|\xccb:\x0e\x8b/cA\xc4R`jgv!\xfel\x9cM\xd0\xd6\xb9\xd4\xdc4vm\xe1\xa7/$\x96(X&\xa8\x06\xd1r\xd0\xa2\xaf\xa7\xa5\x18\x01\xd3\x83\xf49\xc8\xaa\xde\xaeT\xc8\x97Zsf\x01\xd9\xaa\x99a6.\xf7\xb1z\x932Y5$\x7f\x1a\xd5\x97\x82\x1c\xd6\xeaB\x9a\xac\x08\xefF-\x19\x19\xa9VO\xc5N\xc2\x9a\xf2\x97Q7\xe5~b|\x12\x13eM\xfcaV\\\xf1i\xc0\xd3zMLUU\xf1\x17Q7\x0c2\xa3f\x18dE\xbd0\xc8\x8cZ\x1a\x0fP\xab\xab\xe5\xc8\x16\xb4\x14\xa2\x9d\x82S0\xda)r\x8av\x8a\x14\xa3\x9dW\xddS\xdfoT!\xeb\xc2_E\x95j+\xae\xd6\xb1\xd8\xde1\xfd\xcb]\xbe\xaa\xc8\xb7\x031\xdcQ\xf01\xa8\x91Q\xd6g=\xd70 \xad\xfc\x863\xc5\xaby\xd7\xaf\xa6\xb5\x98Z\xcc\x1c\xe5\xbc:\xcaXG&\xaf\x0d\xac\xea\xfa\x89\xfc\x0e-\x1e\x95\x8cw-B<8\xc8(0\xce\xd1;E\xf7\xaa@D\xe8\xd5\xb4\xe7)\x98\xf6\xb0B\xd0^!\xae8\xe3\xafp\xcct\x13UHPM\x94l\xf9M\x1cj\xe9\x02\xda\xdd\xb5=\x19\xa1\xdf3\x108P\x9c\x03\xba\xf6/\xf8\x06\xfa\x1c$'\xeb\xd6\x8dG[E\xfc\x1b\x1bx\xd9\x87D\x93\xab+\x91\xaf\xc7*\xc0\xb2o\x8b\xb2\xe0\xc6\xb4\x1e\xca\xe0\xce\x1dV-2\xae\x16\xaa\xce\xfcm\x0cYM\xa0a\x12\xa5>U]\xc6`K\x81\x12\x88.\xcb\xb8\x10\xc0V\x17\xb2\xe3\xae\x8d*Uk9\xee\x02x\xe2_,\x04\"gg\xb8}\xed\xa1\xd8\xdd\x06\xfdR\x0d\xb2\x12\xf2|\xbd\x01\xa6\x86CqX\x18\x88\xe6\xa6)\x88\xf2\xcf\xa1\x1d)\xb0o\xa2R\x0d&\xee\xedY\xcc\x9e\xe9^`\xd6\x1d*\xc1N7O\xef\x01\xb1XR\x9e\x91\xd7g\xe1\xaeQ-\xea\x9d8\x12\xd1\x91\xa4\xa0t\xe2\xf0\xc1)'.\xd3i\x01R\x07)\x071a\x06/\xfbP'\xe5\x10\x9d\\\xdenC\x15\xa0\xfa\x81%\xf0\x07\xdc9\x93\x01\x8f\xb0\x90\n~$\xca\xe0\xad)\x88\xd1\x0d\xfd\x94\x1f\xc8\xd0\xc1Dv;\x14k\x8d\x89)\x04 J\xdej\x1eb\xb5\xa0\xff\xbd\xff\xbeW\xcd\x97\x87\xa2\xfd\xf2\xd20\xc8e'\xeec\xb6\xb9\x99@D\x9f\xfe>\xeb\xfdw V\x00q4\x89 \xd9\xf77j\xb5\x19\xea\xf7%Ik\xbfB\xd8\x12\x95\xc3\xcb\xf0\xd6`\x82\xf2{A\x02\xb8\x18h\xac\xc2<\xe1@\xb3q\xbf\x9f48\xf61\xd0\xb5\xcb>Q\x8b'\x7f\xcb\x17\x18\x86\x86\n8\xae\x8b\xf8Z\x00mc\x1f ]i\x06*)3=\x82\xd3\xbc\xdd\xc5\x8beA7\x9f\xe6\x99f\xc2JwG=\x01\xd8\x8bZ\xb3}\xeb\"QOPD\xdf\xf2\x8b\x15\x13\x8c}\xb8\xba Fe\xaf%>-J\xda\x06\xc0\x14>>f1{\xc2|\xb6\xc9\x86\x8f\x9b\n3\xd9\xb0t\xa7\x07\"\"\xb9?\x04\xa0\xed\xe4\xe3x\xe2j\x0eW\xad\xdd+Z\x83.\x0e'\xa0C\xe9\xf7ckaS\x05\xa9\x1e\xf9\xad\x96>\xb1\x03\x15\x8eN~N\x81\x8fl\x97\xfe\x9a6*#\x9f\xb8M\x9eV\xd0\xc8jo)\xd0(@ao\x03\x1a\xe5\xcdh\x04\xd2\xc4\x8eh\x94\xba,\xc7\x10\x0e\xfd\xbe%\xf0PK`\x03@\x1ah\xe3\xeaJ\xbe\xec\xb3q\xe3DS+\xb3\x9ao\xcd\x9e\xc8\xab{\xe2;\xf2V\x9c\xc4\xd4M\xe9\xfc\xc3 \xcaI\xcfa\xd2c\x81\xf6h(\x1b@\xd5-i\xe4\x0e\x19\xa2\xa2\xc7\xf2\xf1P&~\xc4\xae\x17}\x1fN\xc6\x01\xe0\xb8\xff\xf8F\xfdv=\xd5\x18N\xe05\xf0WJ8\xc9p\x8b\xe6P\xd7\xf3\x8e!\xdd\xc74`\xb2\xdf\x8c\xc9\xb9\xb4/o\xc6\xf5\\\xe9\xc1\xad\xa5B\xd8\x0e:\xac\x05\xc9l\xf9\x02\xbb\xec\x8bAT\x81X\x80\xe3\xb4\x0b=\x0d4,\xedNO5\xee\xdf\x07t\xc8\xc7\x81FO\x9bIi\x88\x88\xe2\xa3\xa7&\xec\xebp2\x8e\x01\xe9\x82k\x10\xd6[\xe9Yq\x15\xb7\xe8\x8c\xa8\xaf\x0c\xf7c\x0f\x10Z\xe4U\x92\x1e\xb3\x0d(&\x15\xe0w\xee\xb0P\x117\x176\xdcp\xb0\x8aW\x8e\xeb\xe1\xa4\xc8_n\x87\x96\xd7X.\xda}\x80.\xeb\xa4\xab\x03\x16\xc9\xa7\xe8|\x89\xd9\xfc\x0f\xe8_7\xe0\xca\xaa\x9a\xff\xbd-y?\x11\xdd\xd2\x0e\xc0\xa9\x9dt\xec|\x93+\x89k1q\xfa\xb7\xd79\xca\x81\xc2\x9b;?\xff\x00\x84\x92;/\xfd\x97x\x0b\x91;;\xf7\xbf\xcf\xb3N\xc1\xf5o\xec\xdf\x8e\x1c\xac\xca:_\x13\xack\xf2\xc6u\"y\x1bl\xb1F.2\x0f,\xe1,fpU\xe6-.\xb9\xb4h\x1cwZuU&\xab\xcd\x7fh\x8642\xc1\x03W\x84\xbf\xfa}\xee~\x9c\xbdP\x93XA\x10)\xd8\xf87`\xa0x\x86\xaf\x12\xab\xa8\xf2\x9b\xa0\n\xb7Ct\x08~\xe5#\xd0\x9b\xdb<\x05\xd2B\x06\x1a\xd5#++j\xe3\xe3\x08x\x10%\x83\x1b\x1e#\xad\xbe\xaf\n\x89@\xc1:\xa1\xa142\x11\xbc\x95\x89h\xdc\xa6\xb3\xca6\xddr \xeb\xc434\xb2\x96-\xfd(\x97\xb7\xfc\x8c\xf5\x10\xd6\xba\xd2\xad\xc7\xa9\x02\x9c\xd2\x00i\x0b\xaf\xdcD\x8fY\xae\x81\xb3\xe0\xc0\xfd\xb2\xa7\xa9\xe4\xc0s\xc5\x81\x8b\xbcT\xe3\xc0surH;\x9c\x1c\x9aN\x0d\x96\x13\x03\x9c\x16R\xf8\xe8p\x02N>\xfa\xfd\xbc\x0b\xdd\xbc\xce(\\O}\x06\xce\x11\x99\xc7\x02\xb0/\x10hHxN\xee@\x0b;a8\x1es\x91\xcb\xc7\xc1\n\xb2\x14\x82\x18 \x93\xc7\xbbk\xe3<\x9e\xa1B8C\xb5\xb3\xa6)B$W\xc1\xbf\xe5)\x0d\x91\xdf_\x03\xf9eo6\x1a{\xd3rd\xc8\xf4\xcf\xe7&#\x9b\x13,r^e\x91\xd3*\x8b\x9c\x16,r^\xfe\"Xd\xb3ekO%G,f\xaa#xn\xb0e\xd9 9\xbb\xe6\xf2\xf2t\"nv\xf5\x07\xf4\xaf[\xda\x03m\xbe\xc1\xe9\xcb3;C\xfa\x82\x9b\xe9K\\\x1aY\x1a\x17_R\xdb\xcd\xb7j\xb1\xf5\\\x84[6m\x88\x16!\xe3\x18\xb4\xdcx\x97B\xd3\xb9\xc7V\x1e\xd8WN\xa5\x81\xa21\x1f\x8b\xa6\xcc3\xd0n(\xc7sf\xfe\x12\xf2\x95\x13\xc6*F\x97\xf5\xc0$\xbc\x99\x97S\x9cF\xe9_\x98\xc4\xad\x04|C\xa9\xa8\x0ep\xaf\xd4*\xa9\xa7\x9d\xad0\xe5\xb1/A3\xbb\xb4`\x9f\xb7<\xb69\x14[\xc3\x99\xbc}2/\x9c\"\xac\xc4\x9b\xa9s\xead\xb1\x1c8\x1a\x00\xd9Y\x83\xe1\xf2\x87\x1a\xf8\xe2H\xb9\xe9m\x87]\xe3\xf5v\xf2\x02%+\xcc\xdd4\x17\x05$\xcct\xc3\xbd}6\x9e\x81\xcb\x8aH\x19\xf1!u\x8f\\\xd4\xc1\x01h \xeeM= nH`\x91\x89tb%}L@\xa8|e\x93\xdfbD\xa3\x1e\xe0?\xect\x94\xf2\x15\xbb\x901\x0d`\xbf^\xa0\xf7\x8d\xd2%2\xac-\xf4\x07\x1b\xe0~%\xbd\x19'\x10M!\x8e2~\x91A,\xa6\xe44u\x0b\xfb\xcd\x04\xe3G\xc4\x88)A\x89BbNlq\xa2[I#\x86\xfb\x96k\xab\xcd\x0d\xc7\x19^\x8c\x94F\xe1\xd6E\x11\x89\xa1\xf3jd-\xe9\xffC5\xcf\xb8\x1da\x14\xff\x8c,\x05\x1f\x043\xbb\xe4O\xfa\xc2d\x8d\xf1\xfc\x01\x03q\xbb\x13\xadaOf\xe3\xb4t\xdb\x8b?\xe2R'ct>\x03W\x9a\xa9t\x80\xc8\x0e\x98\xd2\xec:\xe0P\xdcY\xa0\xe0\xdc\xde \x86\xf6lbnG\xb8\xe2\x1b\x8bbh\xe7\x06Q_\x89Ri\x89R\xa9G\xaf\xaeXF6\x88\x8b;\xc9nCI\x14\xc3\xd5/\xc7C\xf5n\xd7\x90\xf5Gk\x8c\xb7\xdc\xb4gr\\\xe8)\xdc\xc2\xb5\xa1\x087wBy\x9b\xd9\xf4\xfeB\x1d\xb6q+\xa6\xa8\x00\x97\xbc\xb4\x94\xb3\xca\xae.U\xb3\x1c\xe2\x03NOp\xc9E\xb8\x00}\xcd\x05\xf9\xb2\xc5\xfd\xcc\x07OR\xd9\xb4\x03\x95\x85\x95#I\xe1\x1adr0=\xa9Q\xca\xc1\xf4\xc4-\x0d\xa0\xc5\xcf\x02\xd7\xf1G4\x08\xc4\x96)\x9d\xef\x001e\xa3\x12\xa9\x89\xeb\xe38\x8a\xc2\x9bu\xfbvA\xb0\xeb\x14\xb1\x9c\x01\xb1\xbc\xba\x02BY\xec\x9c\x0b\xdd\xabv\x95\x84b\xa2FEU$\x19 \x98 n\xb1\xf5^\xb9\xbcn\xa7r\xa2\x0bD\xff5>\xa6\xe8\x0f4\xaa\xba\x13\x0b\x8cl_\x1d\x92\xce\xc8\x9e\xf3\xa2\xe7&\xea\x1ac)~\xde\n3k2\xad\xc8\xcc\xee\x191\x18\x03\x99^\xbf\xc4\xed\xcb\xf4\xba7]\x15K\x8c\x0epc2\xb9\x1dn\x0c\xc5N/[p\xf0\xd8/\xfe\x8fd$d\xb8X\x1fG\\\xfd/\xd2\xdd:[\xabB\x19val\xb5\x0b7\xc6\xac\xc4M\x99s\xea\xa6\x11S\xa62[\xca\xec_]\x0e\xac\x96)\x14T\x1c\xfc\xa3\n\xf2\xb3\x01\x91\x96\xe8k!w{\xac\x0f\xde\x1eX\x9f\xf5\xee*3\xcf3?\x0cfL\x0dv\x19\xcf\xb8q\xf1\x8d\"I \xee\xeb\xb65\x11Z\x02\xf4\xc2\xb0r\xc7/ES1:X\xf5\xa5\xc9\x14\xb1Q%\xf4\xe14\xc2\x8aC\x8f\xcde\x13f\x19\xd1\x95i\xabS&\xbd4`\xee\x98\xb2\xb7Q\x8f\x18BH\x04\x9c\xfb\x12yj\xce\xb8\xf8=b\x9f\xf1\x8cO3>cy\x14'3\x9e\xf0\x19\x13\x88x%\xb0\x8e\xdd)\"sC\xf8\x9e\\t\xcec\xe7\x8b`\xba`A\xc4\x002K\xff=O\x19F\x1fc3hMpC\xf1\x9c\xa5\xf9t\xca\xd3\xf4\xde\xdc\x0f\xc2<\xe1,X\xae\xe24\x0dNB\xce\x9c\xf3\x05\x8fD\x13wu\xec\xbe\x0b\x13\xeb\x1eE\xcf\xe3(\x0df\x80N\x04m3*?\x1c7\x1f\x1b\xc6 \x15\xbd\xc8\x02\x89\xb5N\x0e\x84'T\x9dc\xac\xf0\x96:\xbbh9S$k\x9d)H\x13\x97\x8fz\x8a\xa8\x8b\xa6\xa5\x90\xe0#\xe9\x89\x9b\x14\xb7JOY\x06\x90k\x06[\x86\xe7\xe3\xfa\xc5\xfc\xea\xe5\xf3\x9b\x03\x88p}\xa5NYm\x91\x96\xad\x86*\xe8\xf9\xfdV\xe7Q\x9c\xca\xd6\xbf\xbd\xd1\xe8\xa2\x1f\xaf\xe28\xe5\x15\x19p\xe8\xa6]\xfc\xd3\xa2\x895H\xad\xcd\x89\xa3\x0eC\xaf\xfd4\xe5\xb3B\x10\xa3\x05\x84\xc6K4\xc1\x9c\xcf\xea\xf1\x8cn\x17~{\x86JG\xcc\xf3\xbd\xf1Qt\x94\x1c\xe5\xdb[\xdb\x0f\xe1\xef\xa3\xc9\xbd\xd3u\xc1\xac\xd0_\xcc:\x89\xfb\x85\xc2\xe2)\x1bnm1\xe5\x80.\x93\x0eX\xb7<\xf6\xe8\x11\x1c\x13\xff\xdb\xef\xfc^O\xde\xff\xcf\xd4=iAq\x9b\x97\x8a\xfc\xcao\xbc}\xf5r\xa0\xc0y\xe9pW6?\x04\xc5Fm\x19\xdd.p\xff_\x83\x9cJ\xcf1~\x19G\x9b\xd3\x98'S<\xc6e\xb1DD\x17o\xf2N>\xea\x85\x8d\xdb\x88\x11o\xd3&\x96\xdf\x0b\x06\xb3 ]\xc5\xa6L\x85p\xa9)\xfaV\xb3\x81\x08 6\xa5\xa2\x9dg\xa7]W\xe0\xcc\x03\xa7B\x1e\xab\xf93\x05\x89#\xf8\xe4AY\x0b\xdbg+\xc5\x96.@\x89P,\xd0\xd4\xb2@\xd3\xe2\xc7\x01\xeb\xe1za#\x06\xbea\ny#\xeb\x8b\xcf\x17\x1d%\xf1u\x86\x0e\xd6R\x9e\xbd\x0b\x96<\xce\xb3\xf6sO!\x00\x8aH\xe1\n\xb7\xe9\xbb\xc4\xa7\x06y\x94\xf0\xb9\x18@\xf9\xcb\x81\x88\xa7\xe0UNt\xe6\xce\x1d\xd6\x8b\xf8E\xf6.\x98\xbe\xef\x81u\x90J\x86\x05\xa4\xba)\x12E\xc5\xf5\xfb/\x8f,\xcb\xbasa\xd9\xff3[\xff\x97\x95\xfe/\xb5\xfe\xb7hpj\xf3@.\xfb\xca\xd8f\x18\xef\xbf\xd0\x98\x8a\xb3\x15B\xc8\x80\x0c\xa7 \xa3\xd7^\x92A\x15\x05.\xf1\xcf\xb9\xd8XE\xb3g\x18\x1ct\x7f\x7f_\xcf\xb9\xba\x92Q\xdb\xcb4\xb1m\x0fvvv\xd8\x88M\x9d\xb9\x83\xa6\xe8z>\x1aGmI\xcc^\xb2}\xf6\xf3\x0f\xd2\xaf\xd6\x90m\xb23\x97}\x82\xd2M%\xaa\xa8\x03\x07t\xde9\x05\"\x18\xec\xd5\x15\x83\x01\xb2}\x0dK<\x16\xb4O\xbbE\xda!\x1e\x0d\xaa\xfb\x1aT\x1d\x0d\x84\x9e\xae\xb0\xabl\xa1h\xbb\xe6\xc4\xae\x8b\nA\x08\xe8W\xb1\xb3\x91\xc6\x03\xd2b\xae\xb2\x8c}'@Hu\x12O\x84\x1e\x0b5 \x05\xfc\xa4$\x9c\xa6\xdf\xa7\xea\x1eT\x839\xbd\x0d\xcd\xdaP\x96\xd5\xd1\x96\xdc\x8b\xd0\\I \x01bp\xec,\xbb4\\Ctn`\xb9\xe5c\x88q\xc6\xf8\x8b\xdf\xb7\xb2\x05\x1a\xbe\x98\xd5\x11\xf3\xd1\xda\\\xb3\xe0\xca\xa4\x01\x87\xd8\x0e\x9e\xb2\xb8\xc9\xb7\x08\xbf\x98r>K\xd9\xd2\xbf\x08\x96\xf9\x92\x15z\x8b\x0c\xa1\xf2}9\x1b\xd9\x1e\xde\xdf\xbb\xffpg\xf7\xfe\xde\xf5\xdbk\x07\xe76\xad\x17\xdd\xd5\xafx\x04bG\xee\xb8\x1d\xcb8R\xc4^\x9c\x14{q.\xdd\xc0Kk\xf258\xe5\xe6\x8d\xd8G\x13\x9bf\xc4\xd7\xdd\xfb\x02\x8b0X\x04\x99\xeaZ\xbb\xc1\xc0i\xf9)b\x0b\x12\xa3W^\x11\x0cr\x00\x99\xd2\x1d\xc2m K\xcb\xe46(\x9f\x83\xf6xW\xeb\xae\xb1\xb32\x044q\xf3\x01\xc2F\x9a\xc9y)\xff23\xd3\xa6\xcc\x10\xda*R\x1f\xed\x15\xa9\xc3\xedm\xb8\x0f\np\x02\x18 \n\x8e]\xae&\x02\xdcz\xff\xf7\x1f\xfc~\xafq\x1d\x9av\xef\x84\x1d\x85\x8e\xb1 \x82\xc178j{\x15D\x96a>\xabK\xb5\xea\xbe;\xd1\x05\x87\x1f\xdc\xe2\xc2N\xe4\xec\x0co\xe2\xdb\x93\xf4]/\x1a\xee\x1d\x1f\xf3\xf4\xcbx\x96\x87\xbcW\xa7\xda2T\x90\x1eJ\xc1EY\x0f\xc4\xd3k\xb2UQF\x00\x89*\xec\xb1X\xbd\x96\x1b\xd0\x07\x93\xdd\x08\x1cq\xb8}Pw\xf3\x1b\xcb\xac\xfb\xdb\x10\x95\xb3\xc8S\x1d\xc0\x90cd\x1f8\x12\x99r\x9c\xd2\xef+\xb5Ca\x9c\xc0\xba\x9f\xbe\xf5\x88\xe9/\xc7\x04\xa8}\x87&\x8b\xd3x\xb9\x8a#A\x0e)8\xa8\xe7\xd9j5b\x97\xc5\x0cZ\xcb\xf9y\xb6\x88\x93\xe0\x1b_\xf4\xe4u\xbc\xcaW#v\xd2\xbd\x1a\xff4\x8bF\xecx\x8d\n\xafV<\x81\x8fA\xcd\xf3n5\xd3\x11;l/\xf9,\xcf\x16/2\xbe\x1c\xb1\x8b\xf6\xc2\xa2\xd9C4{{\xdb^:\x16\xc5\xb7G\xecY{Q\x7f\x15\xfc&\xbf\x14}\x19\xb1\xe7\xed\xc5O\xfc4\x98b\xe9\xf7\xed\xa5\xe5\x91\xe4U{\xc908\xe3ox\xba\x8a\xa3\x94\x8f\xd8\xeb\xf6\nA4\x8fG\xec\x8f\xb4\x17|\x11\xcd\xe3\xe7\x18\xd8\x9d'#\xc6y{\x95\xdf\xc8\x97\xabw\xf1k_\x8c2\xebP>\x8e\xc2 \xe2?\xf2\xc3`\xe6gq\xf2\xa9?;\xe5#\xf6\xaeCE\x85]\xe9\x88}\xb9F\xf1\x11\xfbi{\xe9\x02u\xdf\xe6\xcb\xa5\x9f\\\x8e\xd8\xcb\xf5+} A1G\xec\xcd\xfaU\x11~\x9f\xb5W\\\x04\xa7\x8b08]d\x82\xe1\x18\xb1\x9f\xb5\xd7H$\xa6\xa4#\xf6y\xf7\xd2#\xf6M\xf7\xc2\x9f\xc6\xb3\xcb\x11\xfb\xb4\xbd\xc2\xcaO\xfc%\xcfx\x92\x8e\xd8\x8f\xd6(\xfe&>\x1f\xb1\xdfh\xaf\xc0/\xf84\xcf\xf8\x88\xfdV{\xd9\x05\xf7g\xd0\x91\xdfl/\x0bF\xb4\xe9\x88\xfdZ{Q\xb8\xc5\x17e\x82y\x1d\xb1\x1f\xb6\x97\x8f\xcfxr\x16\xf0\xf3\x11\xfb\xed\xf6\xc2\xf38\xce\xc4\xc2\x8c:,\xb4\xcf\x830\xe3\x89\xb6\x9a\x93\x0e\x95^\x0b\x88\xe3t\xc6\x1d\x8aO\xf3$\x1c\xb1\xa0C\xc9t\xba\xe0K\x81\x83~\x87\xc2o\xb1\xb0\xd6\xf7\xbcC\xade<\xe3\xe1\xe1\x85\xbf\\\x85|\xc4\xc2\x0e5\xbe\x145~\x9c\xf8\xab\x95\xf8\xc6\xb4k\x8d\xe7q\x18\xfa+\xb1F\xd2\xaeUFl\xde\xb5h:b\xab\x0ee\x0f\xa3|)\x9b\x9eu(\x8e\x8c\x8e\xac\xb0\xe8P\x01\xcc6e\xf9\xb3\x0e\xe5\x0bg\xf7\xb2\xce\xb2S\x1dd\xb8F\xec\xb4C\xe9w\xc9\xe5\x8b\xecU\x9e}\x9ag\x99 \xeb\x97\x1d\xea|\xe9'\xefg\xf1y4b\x17\x1dJ\x7f\xea\xa7\xfc\x0b\xff2\xce\xb3\x11{\xdb\xa1\xfc\x8fx\x92\n\xde*\xf1O\x97>\xae\xb7\x11;\xe9^\xf1m\xe6/W#v\xdc\xa1F\xb1a\x1c^d#\xf6\xc5z\x15\x80|~\xd5^\xe7\xb5\xa2\xb7\xf0\x91__\xa3\xc2\x8bh\x1a\xe63~\xb8\\\x89\xd9\xfcq{\xcd\xa2{\x10i\xe4\xc5\x1a\x154\xaap\xda^\xed3\xceW_\x04\xd1\xfb\x11;\xef\x00e\xc1\xff|%H\xda\x1f\x1d\xc8\xd7\xe6\xb2\x02ap\xeb\xc6\n\xeaw\x03i;;}\x96\xa6\\p\xf8\x87E\x87\xc8\xd2\x9d\xe4\xd8\xb4\x9frV;K<\xef\xa4F\x88:\xb5\xf5\x9eh\x8b\xd4\x1c\x8dg\x05\xbc\xd9\xbc|M\xcbW\xbf|\x0d\xcaW\xeal\x8az@\xf9\x8a\x87\xbb\xb0L\x88<6-\x7f\xad\xca\xd7E\xf9zV\xbe.\xd5k\xe3\x89\xf7\x15\x87\xe0\x03\x8f\xa8#/\xe6m\xef\x1a\x11\x8e\x8a\xbc\x9d\xedz\x9e_\xe4\xdd\xdf3\xa2\xe5\x14y\x0f\xef\x1b\xf1\x80\xca<\xe3\xf8\x1d\x96yF_\xa6E\xde\xa3\x9dz\xde\xbc\xcc3\xfa\xb2*\xf3\x1e\xd6\xf3fe\x9e\x01\x97\x85\xca\xbb\xbfe|\xef\xac\xcc3\xda\\\x16y\xc3\xadz\xde\xa9\xca{\xb4c\x8c\xef\xb2\xcc3\xc6pR\xe6\x19\xdf;.\xf3\x8c1\x9c\x17y\xf7\x8d\xbe\x1c\x96y\xc3z\xdeE\x99g\xcc\xfb\xdb2\xcf\x80\xcb\xf32\xcf\x98\xf7\xf7e\x9e1\xef\xcf\xca<\x03.\xaf\xca\xdaq\x07\xdc\xebv\x11G\xab6\xcd5\xd9\x1amW\xc7\xceQzs\xa8\xc5\xe8=}\x10\xa0\xad\x1a\x04D\x10\xa0\xadj3b\x1a5w\xc9\x807\xbfU5\xb2\xf5x\xfd]ugDN48\x81\x1eD\x837\xf0\x03tX7#\xd7\x12\x8e\xa3\x00X)\x8d\xb3\xdb\x87.>\xaa\xdd\x02\xb2\xaaM\xf1\xc1\xaf\xf3\x14Y\x11\x8f\x84)\xc3\xf6\xd4j\x82\x10\xaf\xb4F\xf5\x98\x06z\xc2\xff\x8c\xf9H\xf5-\\j6\xaf\xbe&\x13\xc9\xd0\x19\x14&\xc5\x1b\xd3\xd1\x0c\xc6\xc2\x82D\xff\xda\xaalar\xad\xaf\xb54\xe7\x05ab\x9b\xe7\xac5\xd6\x1a\xec\xe4Y\xe5\xae\x1d\xb1s\xdd\xc7\x01n\x96\x06\xb8\xa9\x0c\x106]\xb7_$\xa9\x86;\xb8\xbfg0\x14.\xe7\xac\xa9\xcc\xb93D|\xc1\x83\x0c\x83\x9b\xd1\x1b\x98\xa3!G\xe2\xac\xf3\x00x\xcf!\x85\x97\xb0|\x0e\xcb^\xcf\x05\x8c\xea\xbe\xec\xc3\n&p\xed\xac\xa7\xcbY\x1f\x96\x8c\x8c\xb0\xaf\x86\x10+\xe6^\x99\xf4-\x0e\xc6\xb5p\xf7\xc7A<\x87\x0e:f,\x06!\xbdM\x1d\xd7E\x0f\n\xcd\x10\x88\xb3@\x17\xadi4\xc0\xab\xe8>\xb0\x01q\x8b)Q\xa4\x19\x944b\x924}\x9f5W\xc9%\xa6\xe0\xfd7!\x1b\xd5\x8d\xcd\xc9\xc6\xb3\x9d/<\xc10{6;\xc9\xe3\xc1B\xd4\x89\x9c!\xab\xc8\xa6NyT\xeb\x07\x12\xef\xd0\x19\xed\xed!)\x15\x14\xf5\xd9\xa6 \xac[\xe2\xef\x9e\xf8\xfbTKh?p\xf3\xc46]Y\xc0\x95\x87\xcd\xec\xcb0\xbf\xb5\x88i\xbc\xcb\x9a\x83A\xa0'\xd0\x92$VI\xe8BO\xb8\xd7\x82u\xa9\x14\xcf\xf9zU\x87r)\x1a\xa9\x96_\xf3N\xb7\xab\xe5+A\xe7\xab\xe5KQ\xbe\xe3\x0e\x12ZQ\xcb\xde Z\xbf\xe3:U^_\xf4^\x9d\xda\xb9h\xad*Y\xde\x88\xf2*;u\x88\xb1ws+\xb3\xf2\xc3[\x1eI;\x8e<\x9aT\x82q\x9e\xe0#\xb1\xee\xe5G\xaf\x18\x05\x17/!\x01\xf7\x9c\xdb*w_1\x0f\xa9(b\x0f`\x1fw\xc9\xc5`Q~p\xcc\xd8\x97\x8e\xdd\x04T\xef\xcf\x0e\x8a\xdd\xc9\xc9\x00\xa3\x8f]S\xa7\x8aG\xea\x87QC\xa7\x9cZ\x17\xed\xa6\xa6\xa13z\xe6*\xb9\xcbg\xad\xac\xfd\xe4\x87:W}\xb82\x1b\xc3\x1b\xa2\xe1\x08\xc2\xe5\xbcb\xf4]{>\x8a\xb5\xf8H\xff\xe0\x11\xd3\x0e\xafi\xc8M\xdb(w;\xbbr\xd5\x94\xa7\x9a\xa0\xf7\xe6 \xc8\x9f\xab\xe8\xf7\xa1q\xce\xd7\xf5\x8c\xa5P\xcc\xa3\xe3t\xd6\x0e\x8fi\xa9\x8b\xea\x84G\x11\x1f\xb6p\xa2)\x0f\xa7<\x98\xd3\xa6`\x85 M\xf0\xe9\xe0\\\xebM\x0bH\x83\xcfCt\xa7\xd4/\xc0\xb5\x08xH\x07\xe7\x9e\xbe\xc6]\xb3\xc5-\xa8\xd2#O\x18z~\xcd\xcd.\xd1\xd0\x91\x0e\xce\x93RZ\x8c\xbcE\xa37\xb9\xfc\x08c\xd8\x82|F\x18\x817\xba\xc2\x98\xa5\x0b\xe2[nq\xe4'\x11\xf1.ps4W\x0fDu\x86p\xcd\xb5=\xac=\x8fV\xc4oH\xede\xde\xc1\xea'c\xf2\x0c\x1at:\x9b\x02v\xe8\x14\xfb\x07\xda\xb5\xe2\xaf}tj\x15\x0e\xb2\xac>\x97\x83\xc6\xe0\xa0\xb9\xbd7\xa0aJcG\xf0\x1f\x19\xba\xbap\xdfPo@o\xfd\xd4\x11\xeed\x9d\xa1\xcb\xeb\xb0\xdd\xa6\xd8\xe2\x07\xce\xa1\xd3\x15\xfbn\xc3\xbb$~\x08\xde\x9d\x17\xd0.\x0fI\xcd\xd6\xf1\x83\x13rk\xd8<1N\"\x9cA\x13\x87\x9f\xd8\x81\x13\x9b\xa9\x01T\xf7e#Xp\xfc\x1d\"\xe6'&\x11\xe8\xdc.\xd5\x8f\xde\x95\x07\x9f\xd4\xf8\x8d\xc8\xb7\x08\xaf\xec\x89 O\xec\xa08uR\x94D\xad#\xff\xd8n\xe4\xfch\xd2\x0f\x9e{\x15\x0e\xce\x8d\x01=\xc3bR(`\x8b9\x19\x8e_\xfb\xb1\x8b:q\x19\x98\x99o\xac\xe2\xf0\x03\x8f\x84\x8f1\x8c\x98`\x1e\xe6\xe0\xa7 \x0d\x16\xb60\xba\x08\xe7\x0f\xe8&=i\xcb<\x81\"Z7\x9f\x85\xe77c\x08\x9b9\x93\xf3\xf9X\xcd\xf1\xaf\xfb\x18\xb8r\xf9i\xc7\xb1\xa4\xf9E@\xe0|\x14\x01\x9e\xd9\xf7#\xf1\xfd[\xb2\x01Gy\xbe\x8c/?\xf9]v\xc6\xe4\xe8\x1fr\xf4\x1f1\xfc\x0e\xfb\xd01\x8d\xb7\xdd8\xc5\xf8\xec\x13i\xb1~\x0dk\xf7\xd98\x7f\x8deQy\xbb*\xfe\x11\xb8\xd7O\xac\x1b\xf6RD.>\xe9\x83\xdc\x14\xdd>t\xcf/\xbbn\x1f\xe6\xdc\xd5Jx\xcc\\\xfaU\x17;=\xfaP\x07\xd1\x84\xb7\x9bc\x8a\xfcY!.V\xa0\x1f\x15=\xd7\xe0\xa1\xa8\xbb\xfa\xfc\x107O\x925Ppv\xfc\x97z\xf2\xf2\x92\x84\x8b/\xfc\xc7\\\xf2~\xf8\xeb\xbaV\xf9R\xad\xcc\x19\xc5b@nq\xa5&\xd4\x1d\xbb\xaes\xa2\xc4\x8c\xaa\x8d\x8f\x86\xe3fQP\x8ar\x07\xceJ\xae\x9ak\xd3\x15FWe\x9dtGI\xce\xca\xcey\xb67\x98\x80e\xd4\\\xe3\xd9\xc9jq\xe9\x07\xd9\x18v\x16\x8b\x9f\xe3\nL\xbc\"\x97\x8f\x841k\x80\x7f\xad>K\xd8\xb3S1\x8f\xceH\x0dTS^\xe7\xf2>Bti\xd2\xdc\xcb\xebH\xd6\x11\xaa\x10\xe48\xcd8$\x82\xe8\x18\x89\xb9\xd4\xc1\x84\xf4\xa6\xea\xb8\x89\xdd\x14\xe9\x07\xa8\x98\xa18Q0\x04\xecG\xbc\xaf\x1a\xb9\xf9#\xc6\xa4\xe0\x93#\xf1D\xc5\xe6\x8b\xc1\x82\xad\xb2\x15\xa5\x8b\x08\x0f\xfb\xfb\x80>r\xfc+a\x1c4\xbd\xe1\xbe[c\x0c-R\x9a\xe4\xc2Y\x0c~\x82\x1e,\x06\xbf\xe1\xffx\xbfr\\E\xc8\x0f\x92):)\xbd\x1c:\xcf\xf6\\G%\x15B\xbb\xba\xeb:j\x11\xa9*Xy\xbf'\xa5\x1e\x15rS\x9d\x1a\x83N\xd3\x1aK\xfe\xe8@G\x98@\xd1<1\xf4\x14\x10w\x1d\x1e\x8aD\x8bg50\x15\xc3u2\x06\xe0\xce\xb1k\x1d5.w\xd3\xb0\xc5\xa8n\x9cL\xee\x8d|\xd9Nro_+\x9aV \xe9\x1c\xb3\x86\x1ao\xc8N\x06x\x84\xbb\x03\xdc@\xce\x95\x8a\x15\xb6i\x91 h\x9a\x92\xca\xa9\xea\x0f=N\xb4R\x83\xd2\x92\xbb\xf2Z\xb57\x91\xa8b\xd6\xd8\xf8\xed\x05UIFm\xb9 A4iI\x90\x0f2\x96\x8b\x99\xc5\xbaf\xa4\x9c\x9d\"\xed\xd5\xac\x18|\x01\xf6\xc1\xef\xf5\x9a\x19\xc0\xc4\x90\xb6C\xfd\x88\xec\xc9\x9c\x02\xb2\xbd\xd9\xeb\xf5\x0be\x19\xc3\x88\x96\xa9\x0e\xd4O\x82\x9cE\x92'q\xc8D\x12\x89\x8d\x0d\x94/b'lb\n\x8d23\x084W\x9a\xd2\xd6\xd3eG\x90.\xc6\x03\x1e}\xc2\xf1\x07\xd7m\xcf\x95\x98x\x8d{\xf7[!\xba\x19\x8b\xa3\x07`\xf1\xc3q\xab\xbe\xea\xc5\xb6\x03\x8b2O#\xdd\x82}\x05\xa2\x81\x08\xc0\x1b\xd9V@!A\xf8\xf5KmMtgu\\\xdcuc\x94\xc1\xf2P\x93\x1b\x1f\xb9\xce4\x8f\\P\x87\x9cG\x12\n\xc3\xb1~%e\xb8\xa1 P\x8c%L\x85\x9aT\x03\x12lg\xd4\xa2\x9dt:\x9c\xa9m\xf5!\xd5gd\xc7\x167[\xb6\xc8Z\x19i\xda\x15\xe5\x86\xd6\xb7\x1e\xd4:\xfb\x7f\xd3\xd8\x87xj\xe8i\xfb\x0bzb\xffo5\xf4'\xea\x180N\xe9B\xc4=\xc66\x94SQ\x8b\x91f\xbb\xb1\xea\x8d\\d\xb9\x1d\xc5\x14\x84\x83\xf7Y\x8a.1\xc7\x17 \x8d\xaf)\x06v\x88\x07\xbf\xd1\x8b_\xfc\xb4\xfa\xac\xfc>O#\xad\xbd\xde\xcc\xf0\x91\xf6z3\xa9^o\x86\xce\xb3-\xd7!M\xd7\xf9ZNX\x1ay\xb5\xca+\x19\xf7ui\x13\xf0> \xa5\x00\x94\xde\x88\x90*\xa4\x06\x16o\x00\x9e\x035&\x98\xe6J\xeeE\xd8G\xbe\x9c\xa2\xdd\xc5\x97(\x88\"M\xd2\x0cPEScl4\xc8\xa3\xd5cl\x1c$\x04\xa9\")\xb6\x8d>V/)\xb5\"\x00\xc2\xaf|\xca\xf8\\\x9e\xaf\xbf\x00'qy\"D\xdb\x9a\x90\x81\x0cv\xe9\x04\xd6\x06\xf3D\x1e\x1d\x9fcgH\xae\xfd%I\xa5n<\xff9HR\x12\xceI\x10\x85\x1a\xad\x05\xc6\x7fC\x83\x1ey\xda\x98\x00z-\xf2\x7f\xe5\x15\x1d\x83\x1a\xaeq\x8a\xf2\xe3\x89\xc8\xa5\xadu)|\xce\xad\xda\x8frU\x95.M\xb5\x06\x92\xfa\xdd\xb1\xe0\\\x94\xb6\x8b5\xec\xc3<\xf2x\x94\x1c\x1e\xff\xeb\x94\xde\xa6G\xd1\x9c:]\x9d\x8e\x92\x8b~\x81;\x888\xe5p\xd6\xba\xb0Q\xec\xe3]\x92\x98x)\x8d_\x93\x94\x8c\xaby2@J|m\x00\xb1\x1e\xccI\x8a\xb7\xbel*\x8b\x06\xfc\xd6\x12\xe1\xbc\x0f\xedf\xbb\x16A\x08\xf5\xdd/\xc21\xc4\x06~\x0cS\xb2\xf2\x9d\xd4\xb4D\x80\xfb\x8e\xc7\xb2b\xef\xc1>\x86\xcf\xa5<\xfe\x0c\xcf\x0e\x1a\xa2\x9e\x1c\x1f\x19\xe6\xd4\xea\xdch2\xbd2\x9c&5\x93J_o\xa8\xc5\xc5\xef\x9a!\x8fLA\xae\xda\x804\xd0\xfe\xdaN\x95,\xb0>\xc1,\x8f\xa8\x15\xf1\x88Zq-D!W\x07\xe1ej\xcaD\x06\x8cf\xbapR\x0c\x93\xaaa\xc0\xa2p\xe1/\xb3\x98\\p#\xdb\xfa\x12/i\xda\"\x0c\xa0\xa2\x0djB\xcd\x07\x9e\xff\x8d\xeb\xa87\xa13\xaccm\xd5\x89\xc1\xf2*\xcbm\xa2\x8aNc'\x1e|\x80\x1e\xc4\x83\x8f\x16i^\xa4\xf7j+\xe8\x10\xa1\x9e\x8b$G\xc1\xf6\x82/\x7f\x18\xa4\x9c\xd0\x84\x1e\x9a\xa0c5E]\x08\x93blF\x93\x17\xf1\x1aOH\xe0\xb8U\x11\xd6v H\xe5\xa8\xb6\x82\xee\x1a\x8f1\x99}\xf8\xee\xe3\x12\x91\xd3\x1e4,\xb3\x96\xe8;\"o\xddt\xcf\xcfM\xf7\xca\xe8xbA\xc44n\x8d\x84\x11#\x11\x987\xda\x88n\xbe\xd6\x92A*\x00\xc3\x01E\x93\"\xa1u\x1d\x17r\xb0\xeb\x84(\x9f6k\x04\xdb\x00T\x82\xce\xba\xde&b\xf4\xd9A\xa32\x99_\xc2\xe9*\x15\xbb5+J\x0c\x01?\x88\xe9\x92\x864f\x0c\xd8\xc7,L\xfd\x15\n\xdd\xc2\xa9gIS\xc5\x95\xe7\x88\xach\xe2\xc4\xee\xc0\x0f\xe7\xf4\xf6x\xc1\xda\xaf\xbe\xdcu\xe1eM\xe3\xe5\x83\x08c\xa7\xeb\xae\x809&{\xd1\x0d\xa8\xe0c\xcb\xd6\xb7{\xec\xd4\xc2\xb4\xec\xfa\xb7\x94\xc8\xf9\xc8;\xd5yx\x11}S\xf7~\xb1p\xc6\xeb%\xeb`\x8b\xf7\xb5\xeb\xae\xb6\xa5\x18u\xd6\xeel\xf4;\x0c\n\xa37tU\xaf\xf8`\xd5\xb1\x9c/v\xd95\xab^\xcb7\x91\xdd\x93\xbb\xd5E\x14\xc0D~\x19\xd7\xccVA\x9c5\xfe\xc0O9@\xd0\xbe\xf1?\xffS\xfe\xec\xd6\xeb\xa3\x8e\x92\x87}}[~\xa9T\xa6y3\xc17e\xb0\xc3S\xb2\x14\xef)%\x9a\xb7\xf0\x92*BX\x95\xce\x94zMOX\xf7\x99\x91\x15\x04\xc2z.\x04\xc8\xf0\xa9\xa8\xe9\xb9\xad8w\xc7\xd4\x0d\xecC\x80\xb9\xa6d\x93\x0c\xde\xee\xe0&&\x8c\x99?\xaf\x93))\x03t\x93,Y\xd3pN\xe7')\x89S\x0d\x0c@H\x04E\xcd\xbf\xfa4\x98\x1bj\xa2C\n\x8f\xa9\xe4\x87:\x90\x820\x06\xefz\xd1j\xcd\xf6\x92\xa9\xa5k\x9ePA\xfbl\xa5qC\xc4\xf2)\x995\xd1Bhb\xce\xf4\xc0Z\x16\xbbfI\xd3\x0fr\xe3\x1c/\xf4#\xbc\x83}X\xb2e^:K\xe7\xbd3\x9d\xb9\xbaKS\xf48\xb9C\xb3(\x14n\x85pw\x87I\xb3ej\x91;\xcd\x8blD\x17h\x9c\xad\xde\xf9\x1e\x96~\x95\x028;+M+\xb7\xa5\xfa\x17\x15\xeb\xed\x93>\x9cT\x8an\xfbp2M\x18\x88o1MW@\x90\xc6\xb3\xe5\xfcIb\xa4(\xbf\xf8\xa5\xcf\xd7mp6\xc3\x83\xd2\x19\xb2\x0fW8m\x8c'\xaeu+\xb5!j$n\xe8\xaf\x9cs\xf5\x0d{dh\xed\xde`\xa7\xf9\x04\"t\xca\xe2\x1e]\x0f\xb9'\xcbU\xcb\"\x9f\x0e\xe5\x8e]Jk\xfa%\xd0\"\xf7+\xc4\x8f\x8b*vuY\xd97 \xb2}\xb8\xc8O\xe3\x074\xd6\x9d\xf2\xd3\x18\xf2\x01Ur\x1e\x82\\\xe0+z\xd7\x9c\x8a\x04\x14R35\xa46\xa8\xf9\xaf\xa7\xd2\xa8\xc4\xba\xbe\xec\x94\xbe\xa6qB\xab\\\xb4\xfa\x91\xa3\x83f;>\x91\xd9@\xde\x1d\x19\x15\xd4\xeaG\xca\x06\xe9`\x1d\xadMZM\xf5\x83\x0c\xb5\x98fn\xd0\xc3\x91\x08\xd3h\x84\x1c\xb5\xb8\x91\x92^l\x94\x1f\xb3\xa5\x1c(\x02q\xde\xde\xd0\xd6\x9e\x96Hx|`l\x91\xdf\xf7\xe1\xb4D\xe8\xf4\xa0Q\x0e\x8c1\x9c\xeaW%\xa6 m\xb4\x02\x91\x1f\xccz\xc1\xedp\xe8\xb5b\x9a%\x14y\xf2gBCy\x81;8\x17?B\xf1L\x81'\xffM\x03\xba$\x18\xa5\x84'\x92\xc4\xd2\x15\x86 \x95\xd9\xc0\xba\xa2\x94\xc4K\xa5\xa54\xbe;\x0c\xd3\xd8\xa7\x89\xcc\x97\xec|p\xfb\xd0i\xb0h,\xa2\x9d\xb3uG\x91\x17\xbaiWxo\x88P\xdbCW\xe1N\xb8v\x86;Kux\xea\xb4\x9eL\n;\x12 \x86X\x1d\xe1[i :z\xf0'i\xb4n\xa1\\\x03i\x00\x95\xa3\x8f\x19\xb7\xa5\x0dU\x05H\xd3\xe1l XP?\xb2\xb8\xd8`*}\xd4\x93p\x98\xd0\x01\x1eJ\xf2\n\x86-\x82\xf9eU\xd3\x14_\x93zb\x020\x83\x821\"L\x8c<\xbc\xf5\xe8:\xc5\xa8\xb4\x0f\xc4J\x06\x9c|\xa0v\x00\x156\xdf\xcd\xb4*vL\xa9\xf6\xd5\x8f\xd4J\x0d\xc4\x96\x140\xecC&\xf0\x16m\xc4\xc5NA\xef\x11\xae\x04\xaf\xa3\xba\xc4s\x86\xcc\x1d\x8b_\x85y\xe4\x12\xc5\xfd:\x1aHg\x9d\x0d\x18=\x07\x1fU\x11\xcfacC\x1b\x17B\xfd\\\x8b\x1c\xffU\xac\xf2\x1b\xcc{@H\xb1\xa4\x15\xf2\x81D\xc08\x8a\xc4\x9e$\xac\xb7w\x91\x97\x13\xe8\xd8\xe9\xd2pn3\x1d\x97\xad\xc8W\xe1\xc5>\xe4d\xabi\xa2 &\x8b\xb9kD6\xf4>tQ\xc3\xf1.\xf2\xba\x96\xd3M\xfd\x04\xe5\xd7\x85J\x18\x1bhw,\xe1\x9dm\xd0f\xb4P\xa3\xcc/0=/\x1f\xb0\x02\xb7\xa2\x10\x1d\x10\x9a\xc7\x01\xda\x96\x8b\xb9\x94\xdaV\x8a\x1b\x1b\xfe\\\\z&\xdfs\x8a\x8d\x0d\x7f6i\x1et\x1f\xbc\xa3\x0d\xd4\xfc\x1b\"\xf7F\x1a\xdfA\x92\x92\x94b\xd6\xf4\x1b?\xbd\x8c\xb2T(\xc5\xa2X\xde\x07\xb4Yy\xf8n\x10\xb7\xd6\xb0\x98\xf9?\x84\x84\x93\x8b8[\xa7-l\xac\xe5G\xe15\xed\x94*\xcc)\x95\xf1Z@~r&\xb0B\xa9B\x03\xbf+?\\\xb9\xaa\xa1\x18\n+\x10W\xb6rny-\x96*.-U3VI\"m\x10\xe8\xd5\xcfEL\xc9\xd57]D@}&\xa6)\xc5\xc6\xc5y\x8f\xfa\x02\x99>\xac+}z\xf0\x16Q\x01\x0e\xc8\xd4%\xbe2el\xcc\x17\xac\x9c\x05\xdb\xe5a\xe2s\xd7\xd7\xfc`@-^#wA\xe4\x11K\xfb@\xc4a\x99\xf6\xb11\xc7\xc2=\x8a\xa3W\x1do\x1f\xae]a\x0e,GA\x1d\xf2 \x06N\xbe\xf6\x00\xa4\xff\x16\x1cVi\xc58<4\xcb\xc6\x1fLJ\xf3\xc7\xf6a\x0c\xe2\xea\xa3R\xd3\xc9Y7\xb9\x83\x04\xf3\xc2\xfe\xd6\x98s\xd1D\x19\xc0\xfctf=\x84Q\xbc\"A\xa9\x07y5\xed\xa8o\xa4n\x1f\x0c\x1e\x7fz\xa0/\xfc\xd0O\x1a\xfd\x13\xf2\xda\x05\xc7o'2iNd\xda\xf9\xd3k\x88L\xda\x82\xc8\x84\xea\x8e\x11\xdbKe\x9csL\x0c\x95\xad\x81\xc9\x89\x17)\x8d\x19e\xe9\xa3\xe3\xb8 h\xf0P\xb2\xdd\xca\xdbC~\xfe\xfd\xa0)\xa8\x92\x80d;\xa2\xcb\x8d\x84\xdb\xb2\xa4\xa0\xd9\xb5\xb1\xd8\xb5\xcd\xfd\x81\xa26\x8b\xed\xbb[\xfd|0\xd9d\xab\x1f\xfb\xb1\x0e\x05\xc10\xcb\x11\xf0\x85GG\x8d\x0b\xf2\x03&\xca\x07\x82\xef!iJW\xeb\xb4\xfb j*\xb5\x01x\xe32\xae\xea%\xad&\x82\xea\x0eR\x94\n\xf6\xe5\x91Woc\x8c7`\xe7\xecc\x9adAzDVt\x0c\x0d\x01-\x18]{\x17yc\x83m\"p\x85\x0e?\x9d\xb8\xe2A\xa1\xab9u,\xc4@\x03q\xac\x95VM\xc0J?sy\xf6\xbcA\xcd+q\x95\x9f\xf1\x8a\x9eI\x89\x0fs(\xf2\xe6\x1d\xea\x01Q\xcb\xa7\xe9D\xaa\x82[\xfb\x0e\x11Z\xe5S\x07\xef8\xa7:[f\xb1\xc8\xfe\xe0\xdc\x0f\xaf#\x8c\x02j\xb3\x15P?\xb9\xdd\x80U\x8b\x99\xb7f\x8a\x95(?\\s\xc8\xd6n\xae\x11\x08rm-\xf8 \x90 \xa6d~\x07q\x16\x86~\xb8\xb4\x89\x01E\xabZc\xf9jU\x95\x1e\xe5\x19\xc6\x0d\xd9\xf0\xe5GL\xf4\xadA9\x0e\xcd\x9a\x85\xb0\xe0\x00\"<\x96\x10O\xfd\xe7\x8d*Z\xc9\xf6\x85\xf9\x06m&\xef\xa4\xa9Q\x10\x0dg\xe8\x14B\x18\x064\xd3W4\x96m\xd32\xc8\xca\x08\xe3\xeb\"\xafns\x1f\xa0(\x85\x1a+\x7f\xa9x\x06\x12\x13\nZ\"\x97\xc7\x85Pjb\xc3B\x0d\xdb|\xfe\xe4\x92\xb9\x8a]E\xa3\xcd0+\x90x!q\x92m\xbc\xcb~\x9b\xde\x01\x9d\xa9j\xba@\x07_m\xf0v\xe2C/1\xb6\xa1BU\xc3\x01\x97O\x9d\x82o\xe5\xad6l\x18\xd8\x87\xb9\xbd\x8a\xd4\x17\xdd\xe4D\xa8\x19\xb1K\xdcq\xd2\x9a\x99\x10\xc0\x957 \x13\xb8\x841\xac\xfb \x8e\x8b\x87\"i\xe3u\xa6\xfa\x11I\xfd\xb0\xabvZ06\xc6\xb1\x18k\xe3\x0b_\xb3\x07T\\MrQ\xc3\xc9\xf1\xae\x90Y\xa4ZV\xd2\xad\xc4\x8eX\x06F\xbaV\xfa\x99-}\xd8\x07\xe2\xf6+\xc97M\xc7\xf0\x8d\xed\xc42;S4\xaeX\x8ai\xb5$z\x99\xd7\x89\xc4\xcb\xdc\xb3\x07\x87\xd1v\xa6\x8d\x11\x1c\xda\x0eQ,E\xc3\x08\xdb\x0e\xab\x15\xd0\x0f1\x9e\xa0\xe1\xe1\xad\xed\xe1\x89\xed\xe1+=0\xa6R\x01\x91c\x9d$=\xb3\xfc\xce\xcal\xd8&?\"hg;\xf1Le\x83\x05\x93\x84v\xb2\xadW\xb7j\xee\xaa\x9f\xf0\x95\xc5\x9a\xb4Nu\xd4\xd1\xa83\xb1\x19\x1a\xe4]\xf9\xad,\x8d\xe9\x8dt\xa7W \xda\xc0\xc3A\xc9\xb2\x90\x07\xbc\x8ey\x90\xbc\xa6\xd7@\xe1:n\x1c:\x0dg\x18a n\xc9{Hr\xd5\xd9\xdf\x177Fm:\x04\xe5\xa8\xc9\xda\x13a\x10\xd7\x11 \xbf@n\x1e!\x14pE\xcb=\x8dE`\xa0(E\x03L\x05\x8bV/]\x17&r\x1dr\xef\xa2` \x9e>\xc8\xb8\xa3\xfaI\x1d\xb9\x99\xa8X\xa2V\xaf~~\x88\xeb\xae\xfaI\x9d|\xd3>\xacC\x17\xc6u\x10|\xd5\xd4\x93\xdc$\x01C\xc9'-\x07\xd2j\xc8\xcd\n\x04\xe2d-x/\xb1w\xd2Z\xb0\xf8R\xad\xb6T\x08\x14J\x06\"K;\x87\xa0\x8f{z\xcc\xa8B\x9dv\xb5\"]\x07\xd6\xc8/<\xec\xa6\xd4\x0bL\xe5\xfd\xacF\x11U\xb0\xb9F\x99\x13or\xea&\x0e*\xb3\x92\xb6`\xac}L:/\xc74\x10\x80\xa9^\x1f\x17\xca\xd8\xc2PB\xcc\xd5\xd0e\xaev\xbc6\xd3\x84T\xc3:\xe5\x1d\x943\xd0\x9f^\xd2\\\xa1\x02\xf3\x88&\x10F)\xac\xe3\xe8\xda\x9fS \xf0\x18\xdf\x7f\x0c\xbcA\x93b\xc8\x86\x0b\x9aH}\xdaE\x8c\x90*\xc7}e%\xc5\xa85\xf4\xb9&H\x0bz,\xf1\xcf\x02\x80Hh\xc5\xebK\xac\x81\xa8\xbc\xeb\x89\xf4B\x90Tm\xe0\x95\x88\xe0\xed\x9dt\x8a4D\xe8\x9dfx}!\xe2\x99\xa7\x85B_\xa8\x9b\n\xee\x02\xcf\x95\xb4\xa4P\xb2\xdb\x19\xe8f\xc0\xb3\xcd\x8f\xcb\xef6\xa0@\xbe\xfc|\xd0\xe0s\x1c !\x88#\xc4\xd4W\xab\x9d{lwa\xd1o \xae\x1d\x1e\x03\x9d\x0egu\xf4\xa9\xaf\xc3\x88\x9b\x9ar\xa0\xc9\xcbd\xcc\xc72\x9a\xb9}\xd8T\x1f\xabz|\xa0\xdc\x1d>\xd7\xd2c\xd1\xd6\xcc\xad\x9b+\xa19]\xdan\xce\x1f\xecs\xa6\xea\xed\xd9\xfd\xbd\xf6\xfa,\xcdMR\xa4L \xbd:R\x8e\xbf\xa5F\xf6\xab\xd1\x94\x0d\x03;\xd5\x0f\xac2W\xd8\x87\xa9}]\xb8\xa9G}e08\xacd\x92\x8f9\x10\x8b\xc8N M\x9d\xea\xfd\xbei\xa4\xef\xf5#E\xaaj\xd3\x16\"|\xa7\xc4p\x07\x81\xb4]\xa1\x12|\x7f R\x9fom\x8fJ\xcf_\x1d\x7f<,?/eU\x1a\xbc>|s\xf0\xe9\xdd\xe9y\xb5\x9fQ\xa5\x1fY\xef\xcd\xa7w\xefJ\xf5\xb6wJ\xf5\x82\x88\xcc\xf1\xc2\x94}\xa9>8\x08\x82\xfc\xd9\x01\xe3 \x8a\xc7 Y\xd0w\xf2]\xf9CWA\xb6\xa1\xfcV\xab\xcd\xb3\xd5\x1a\xb95\xf6\xa5\xfa\xfek\xf9P\xfeP+\xfc\xf5\xe0\xfd\xbb\\q-`\xb0W\x9a\xdb\xfb\xb7Go\xdf\x1f\xbc\xb3-G[0Z \x98x\x84\xbb\xedv\xd9\xb7n\xe9\xd9\x9a\xc4\x18F\xd1w\xba\xf8\xb5\xfc\x14\x93\x19\xcb\xe7\xe2G\xb9\x06\x99\xcf_\x95<\xa5|\xa7[.\xeb~\x93M\xfc\xb4\xea\x06\x1d\x15\x00-\x95\x8b\xb4Z\xdb\xfaDq\x08\xbdRyV\x80\xacT\x9eh\x9cE\xad^\xa1\x01F\xbd-\x15y\x18\x07\xbaL\xaba\x1f\xb6\xcaE\x0c\x81\xb6\xcbE\xf3z[\x97\xf5\xb6\xae\xebm\xad`\x1f\x9eL\xcfn\x87\xc3\x8d\xb3\xdb\xe1\xd3\xb3\xdb\xe1\x8fg\xb7\xc3Wg\xb7\xc3\xc3\x8d\xb3\xdb\xd1\x9b\xb3\xdb\xbd7\x1bg\xb7O\xb7\xcfn\x9f\xeen\x9c\xdd>{s\x96\xbdy\xf3\xe6\x10\xff\x7f3\xbb\x9f\x9ee\xaf\x9f\xb2\x97\xb3\xd7?\xbey3s&\x1dV\xf2\x8a\x97\xb0\x1a\xee\xbd3\x19O\x7f/W\xbb\xff\xdd\xadT{R\x1e\xd6R\x0c\xeb\xe9\xceY\xb69\xdc|\x8a\xff?\xab\xd6\xba\xc3Z\xfd\xb3\xe9\xd9\xec\xec\x1fg\x9f\xab\x8f/\xd8\xe3\xdf\x9d\xc9\xb8s\xdf\xe9\xdcw\xa6d\xe3\xefg\x1b\xb3^\xc7\xfd\xf3\x13\xbf\\\xf3\xbc\xa89\xfd\xbdh\xcfu&\xe3\xff\x98\x0e7\x9e\x91\x8d\xc5\xec\x1f\x9b\x9f\xef\xf9\xf7\xbf\x9fm\xfc_\xcf\xcf\x9e\x9cM\xc6\xff\xf9h\xff\xacw\xf6\xe7\xfe\xf9\xd9\xa0\xf3?g?<>s\xce\\\xf6\xf6\xcc\xfd\xe1\xcfO|\xddYqc<+F\xc3\xc2\x8an\xb4\xc5\xbf+\xd4\xbc\xde\xd4\xa1\xb1\xa9gEK[\x9b-Z\xba}HK8\xbe\x87\x8e\xf5\xc4\xd8\xc3\xf6v\xd1\xd4\xb3\x91\xf2}K\xe9b\xb3\xf4c\xa7E\x87\x1a\xbd\xbaF\xc5,\xc7\xf0\x14^\xec\x0bgI\xf6mg\x0f\x13Zn\xb0\x07cx\xb6\xc7\xca0\xaa\xf8\xd6&\xdc\x0b\x9bF4a\x1c\x0d7\xd1\x9ca\x83U\xea1\xb0\x8cacd\x1d\x98F\xff]\x8c\x82Or\x02\xdd\xb3a\x97\xf7\x9c\x97\xfc\xff\xb0@\xadr\xc1JF\xa3]\xa5(\xc5J\xd5\x82Q\xbe\\\xac(\xe4EjK\xd7X4\xdcT\x8a\x16\xbc\xd6\xb6R\x14\xf3Z\xa3\xa2\xe8\xff\xcfJ\xb6\x94\xd7\x00\x0b\x8a\x97\x1ew\x1f\xc3\x18\xb6\x95i<\xc1\x11\xaa=\x9d\xb1\x92=e8\xff\xe7\x7fc\x9d\x1d\xa5\xe4\xff\xc6:\xeaL\x91*\xb0\xd2\xa7\xc3J\xe93V\xda\xedZ\x17\xe1\xc0\xb8\x08\xb8\xfe\xbb;;[;0\x01\xeet\x87y\x0b_]\x92\xf8U4\xc7\x9c\xa8c\xed\x83\x9d\x9d\xcdg\xbb\xd0\x03\x87!\x0eka\x17^\xbe\x84\x11\xe3uvv\xb76\x87\xe5G\x8f\x18\xbc\xb7\x14o\xd9\x82_\xcb\xed\xe4\x8e\x85\x9a\x043\xee9\x9b;\x8c5\xfb\xa0);\x054\x97;\x85\x17\xb0\xb9\xb3\xfb\x1cN{=\x17\x8e\xa7\xa73\xd8\x87+\xe7\xd4\x85 \x8c`\x0c\xc3>|(\nu\xc4\xe9\xbdV\xc1\xa9\\\x94Dx\xdf\xc7\xc3\x17\x0f\x16~@C\xb2\xa2\xa8,\x0b\xd7Y\x8aN\xb4Q\xe2\xa7huH\x07\x81\x1fR\xb5\x0c6D!:\xd0\x97\xe6^\x1f\xcb[\xedX8\xcf,\xc6i}\xff\x0f\xed\xfbt\x10\x85\xbf\x918\xf4\xc3%w\x8d\xce\x7f\x8a@\x85\xa8U\x12\xed\xeb\x16\x87\xad\xcbQMe\xc4\x18\xb7\x9a\xd1\x99V\xb9{]$\xa4\xab\xcb\x8e\"7\xf0>\xd0\xc15\x8d\x136\x8dG\x8f8$\xba\xf3l\x1d\xf8\x1eF\x1d\x84h\x01\xff\xc1\xba\x84\xb9\x1fS/\xf5\xaf\x91\xc7\xe2IC\xf2\xa4:\xf9\x9b\xe5\x9a@<\xc6`&@o\x89\x97\x06w\xc0d]\x99\x03\x12\xe3E\xb3A\xb0-\x85w\xe0O~w\xd8\xa17\xeb\xb9g\x03\xf9\xed\xcfO\x06\xf4\x96zN8\x1d\xce\xb8\x17\x1b\xef\xc8\x0f\x82\x8dE\x14\xaf\x98\xa4\"\x1a\x04L\xb0I\xa1>Z\xc6\x8e!\x03\xf96L\x9d\x18\xc3B\xe2^\xf1\xcb\xe5\x9b\xb2\x9c\xcf.*z\xcbB>\x13r\x11\x88\xf6%\xccD\x9f20\x1b\xe7?\xe5\xc3}\x081\x12%\x1dx\x97\xd4\xbbz\xe7\x87\xf4\xc7\x98\x92+\x0c{\xc1v\x90\xec\n\x0d\xdc7\x8b\xaf\x7f\x88^\x93l\xcd8Y:o\xe8\xb4\xb4\xba\xd5\xccb\x07?=\x0c]\xea\xb8\xb2iX\xed\xd3\x83\x9f,\x8b\x9d\xdeDE\xc2O\x06\x988\x07\x08\xf2\xc7\xb8\x0e\x17\x83\x94&\xa9\x13\xa3\xa8][\xda\x94,\x81'o\x01g\xe1\xc7I\x9a7\xe8J \x94\xc6\xc0zI\x84\xeef\x90\x92\xe5{\xb2\xc6\xcb[9\xe2\xc7\xe9%\x8d)\x9a\xbb\xc1:\xa6\xd7~\x94%\xc1\x1d\xcc\xa9\x17\x90\x98\xce!\xc9\x16\x0b\xff\x16\xa9b\xf71\xf4 \x86\x1e<\xee*\xc3x\xec\xf6\xe1\x9c\x0f92\x0fy\x1dS\xd6\x8c\x93P/\n\xe7-\xc6,\x07;\x8dg\xb6xr::\xfa\xd1b'\x89\xb7\x0cy>\xb5\xf2\xba\xa2f\x10^\xe8QA\x18\x93Ib+\xdcH\x11q\x8c\xd1\x81\xf1(\x89\xb8\x83\xad\x8fw\xbfB\xed\x06\x11\xbc\x00\x9f\xfd\xe9\xed\xc3\xc8\x15<\x83C\xb0\x8e'\x8e\xb4\x03\x06PW\xf0~/\xf6y|8\x82|\xcfh\xb4=\x1a\x8d\n`\xd3\xdb5\xf5\xd8\x9e\xb8&\x81?\x87\xbf\x9c\x1c\x1f\x15\x11\x0cuv\x8bhp\xb5\xe2\xab\x96)4\x84-E\x92\xc6\x94\xac\xd0\x16\x89\xf8a\x02a\x14n\xacc?\xe4[=o6\xd1\xb6+n=\xd8\xbc2\xd3\x9ai\x96\xecu\xb1d5\x87M\xbc\x7f\xe1\xeb\xd5\x87\xa0\xdc'B8\x1e\xf8 \x17\xfd\x9cP\xc1@\xa1\xaaY\xd1xIaE\xd6k?\\&\xcf\x11\xdb\xc4\xdd\xd6\x1c\x92(\x8b=*.9\xd8&P\xc9\x1aC\xc3\x8c\xaf\x1e\x13\x16\x1d\xc58\xf6\x8a\xdea\xa2\xb7|A3x\x01\x01\xfb\xc3\x17\x14\x9dd\xa6\xd9,\xdf{)\xda&`r!\x1e\x95 \x9c\x12\xb6\xeb\xf9\x0fU#\xae\x03\xcf;\x05\xa3\xd5t\xaa:P\x05}\xf0\xeax\xcd\xb0\x90\xb3MN\xa4\x9e2y\xc4\x11\xf8\x07\xe6\x83N\xc9r|GV\xc1 \x8a\x97\xfd\xcd\xe1ps\x8c\xf0\x13\xa6\xf3u4gm\xf3\xf4\xd2~\xc2\x99\"\xdf\x96\x958\xe0\xe0\xf4\xf0BL\xc2.\x80\x17\xe0\xb1?\x1cv\x12\x17\xfci0\xd3\x9b\xe4!\xf6\xe6\xd5\xeau\xf09\x1d\xfc\x91\xf0\xbb\x95$\x8f\x82\xcc T\xa7X\x13^\xe0p\xbe\x08\xd8\x1e\xc3\x0c_5\xd6i\x1f2\xfe\xa4`\xb0\xca|\x01\x9dK\x14\x83+z\x87!M\xd2i\x84\x17\x7f\xf9\xadM8\x8dfZ\x01(\xb5.\xfe\xa7V\xb2\x94\x102D\x8aMN\xa3\x14JR\x8c\x1c\xf32\x15?{=&Vl d\x98\x80\xa3>\xea\xe7\xa2\xa6\xb5E\xce\xcb\x15\xaf1\x1e\x9d\x83\x87\x00\x02\x16\x9d\x9e\xd8\xf6\x92\x84\x8aSx|\xd6\xc3\xe4C\ng\x8a\x13\x90\x8dY!\xf37\xd3\xd9]J\xc69\x94\x19\xfflSx.\xb2~GZchqyr\xe8D\xees\xd7\xd4Z\xaf\xa7\xb6\xa7\xdd)\xb8\xdb\xb6\xb8he\x08\xf0?\x8f,\x979mz\xd6\xbe\xfc\x19n.}\xc62\x8c\x86\x05#7\xda*\xbe\x8bb\xc3\xb8;7x\x14\xe12\xd6k t>a\xf2\x90f@\xf7!fx\xc5\xd7\xfbm8\xe7\xe6\xcd\xc3\xe7R\x90e\x0b\xa0>d\x95\x1f<\xed\xcf\xba]\xb6!8\xf4b\xba1G\\e$/\xf8c\xcel\xce\xe9\xc2\xf7|V\xec\xe3S\xe4\xfe\x91k\xb3b\xe5\x1b\xc3~\xed\x8bD\xb3r\xc8ZR\xd0q\xb6wpl\xa6\x8d,2\xe7n\xefr[\x01\x0c\xfd$\x84\x96z]\xe81\x82\xdaTe\x93\x13\xc1\x90m\xc5\xad\xbe\x80MC\xff\x9d['u\x1bd\xc8\xbfke\xc0QNjTf\x81\xeb.R\xcc\xda\xcfc\xce\x15\xcf\xe2AL\xd7\x94\xa4N\xf7\x0c\xcdd`\xa3\x94(K\xd7\xf5\x8f\xda\xae\xafE\\A\x89Q)\xd1X\xe2\xf9\xdck2\xf4.\xaby\xb3A\xa8\xa5u\x99Q2M\xae\x11\xeetQ\x08\x95\xbcM1=\xfe\x831\xb8\xf2;;,\x88\x90 \xda\x11+lk\x9b\x93\x13\xfc~\xebX_Dtp5\x97\xbe\x92\xb9\xed\x0c\xfbP\xa6\xffHbY\xf1\xc6\xc8\xad\xef\x96}\x06c\x99\xbb*\x0b\x82v\xa3\xafu\x9f{.\xf0\x0d\xc2O\xdf\xdf\x04q_\xf0<\x1e\x1d\xcc\xce\xc2\xbb\x92\xc8\xe1\x96\xc7\xd7\xa6\xf3~q\xd8#-\xc8\x8f{1\xa5\x97\"^\x8c\x00\xb0+\xce\xb1\x0b2W\x89\x00\x93Z\x08$\xf4o\x19\x0d=\n4Lcm\x94\x80|b\x15\"\x93ji\xa9$\x01\x9dL\xe0\x08\x13\x9c\xd0W'\xc7\x1dd'\xe8\xe0\xca\x0f\xd1\xaaG\x8e\xa0\xdb/6\xd3>\xe3\x0c\x9b\x18\xca_\xcd4*g1\xf95\xbev\x07T1\x9dMq\x8b\x9f&N\xf3\x11P\xd8\x0f\xe8\xdaQ6\x0c\x9b\xbfI\x03C\x84X\xc9\xafv\x18U\xde\x15\x1cP\x9b\xd3\x82\xf1@\xc8\xcfw\xcc\xdcA\xe5\x851lq.)b\xef\x12%\x01g\xb7\xd3\xe9\xb6o\x85\xbf\xd1\xedC\x99\xd11\x98<\x1b\xd9\x816\xdd\xd5^\xcc\xd9\x00\x85\x0b\xd8\xdd4\x1e\xfd\n\xe5(lF\xd8\xecc\x9d \\\xdaem\x86W\xb0\x89Y\x98K\xb04\x9cK\x9d\x80\x10Do\xfc\xf4\xd2\x0f\x81\xc05\x8d/H\xea\xaf\xd8\xcaW\x15<\xa6p \x82sS\xe6\xdb\xb9\xe5\\\\\xbe\x9al\xaf\x11\x98H \x98,\xa5\xceC\x08\x90B\x10\x06z\xeb\x05d\xc5\x11pE\xe2\xab\xa4\x9b\xa7k\xae\xc0\x82\x1dP%\xf1\xa1\x87\xc9\xed\x84bG\x95QCR\xd1\xe9T\xfaL2\xef\xb2$r\xcb\xcc\xe5U\xf4\xe1\xa4\xbd\x1d\xdc\xeb\x0b\xdd\xbc\x9ew\xb9R\xaa\xd0\x15\x18!\xb5\x08\xa2\x1bF.\xd9v\x8d\xe2\xd2\xf8\xcb\xab\xa6#\x7fx\x90u\xce\xf5\xfd1x5\xc0h\x8c\xf6\x1b\xb1\xcb\x03KH\"\x1a\xc3\xb8\xae\x06\x0b]\xa5F\xaep\ng\xa8\xe6\x1a\xb3]*N\x89\xa2\x16+\x93Ou\x8f\xeb\xf2\xb3\xac\xcf\xb5mY\x98k\xd6\x94UG\xcdZ\x88\x9a\xb5\xc7\x98\xda\xdeJ\xbc\x7f6\x13o\x0dY~\xca\xc9r\xf8\x15d\xd9\xcc\xc8\xe8Is\x08\xa2\x86J\x9e\x0d\x03(af\x15\xab\xe5\xc6\x0d\xc5\xc6\xe5\xa2f\xe7\xc4 \xd9\x0en\xd3\xa2\xf6\x84U\xb6M\xae\x03)\xf6cy\na4\xa7\xb0\xca\x92\x02\xdfH\n\x01%I\x8a\xaa{E\xcbV:\xa6\xed\xbb\xa9a\x81\x7fS\xb4a\x9as\x01\xddqQ\x1b\xb6\xea\xc3\xb2\x0fw}\xb8\xe8\xc3y\x1f\xae\xf8e\x94\xe6\xd0~o8\xcc\xff0\x1c\xe6\xcab\x07~\x92\xd2\x90\xe6\xb2\x12\xff\xe5t\xa35\x0d1\xbfx?\xc7~~}\xa3@A\x16\x08~E\xfe\xcc9\x15^\x80jO\xd8Gc\x88u\xc1\x97-\xf8W\x11q\xad\xca\x88:\xefs~\xb5\xcc\xbe\xc1\x84\x03\x01\xd3_\xa9B\xa6\x90:\xf0\xba\xae\xfa\xf0\x85P\x84\x9d\xa2\xf1\xa5\x8b\x17\x1e\xec\x85\xd3\xfa\x19*N\x14\xe4\xa0\xee\xefq3>w\xcb\xc3\x9b\x14\xa3[q~\xec\xbb\x0c\x12\xc6\xd8\xbcn\xfdV \x832\xbfg\x83\xf4\xf3\x1b\x9cS\xf6`-6\x15\x93\xfa\xce1\"w\x0et/'i\x98\n\x80\x1d+}\xb8*\x1f5\xa5{\xc4\x1cR0\x01\xde+\xca^W\x08\x9c\x87\xdc\xb1\xf4\x0b%ob\x96\xce@X\xee\x98%4\xf6YXBr\xcf-\xcf.%Nj\x9f^[\x9f\xae\xacO\x97\x86\x0d\x08\xc2\x8eF\x97\xa7\xf2\x0b\xe4\xc7\x85PY\xb7\x93\x1f3\xa3\xe7\xbf\xf4Vn\x16'\xfbB`\xe6B\x1b\xa9\xf0\xb4\xbb\\(@\x81f\xe7\xa9\xf8~\x7f\xcfhyl\xb5\x84F\xad\x13\xd2\xc1\xb0\x0f^.\x02\x1auP\xea{\x8a\x80\xd7\xe8F\x880n\x03\xb1C'c\xfb\xdcP\xb5\x81\xbfR?l\x84;\xdc\xde\"s\xe1\xd6\xd4y\x85S\xce9F\xc2X\xf8\x94&k\xe2)\xa7\x8f\xaa[\x05td@\x0e\xfa\x8a\xdemp\xd3\xea\x84\xae \xf7\xf0\xc8\xd9\xe9\x8b \xf2\xae\xa4\xd6\x9a\x1d_(l9x\xd7\xb0\xe8\xc3\xbc\x0f\x97}\xb8\xe6w\x05n\x1f\xf7\xc6\xb5\xa0\xd2\xa2\xe8N\x109\x81\xdc\xc8|\xb2\xbf\x97\xf9\xfe\xc57$\xc1\xb7\xc3\xa5e\xf2+\xa6\x04\x88\x97vF\xe9\xba\x91Q2\xe5'a\x80\x17\xe6\xa0\xce\xba\x19\x17\xf8\x9d\xd8\xb3\xad\xbe\xd0\x83sM\xac.P\xbd\x85\xf2\xb1>G\x9b\x9caX\x1beQ\xf9a\x1d\x8e6wD\x8fC\xde\xe3?\xda8\xf4|\x01[\x15\xbb}0\x80\xa1|\xf2\x0b\xfc_[\x19\xab|\xab\xb1\xbd\xda\x06\xbc\xe2\xbe\xb0.\xbe\xf2\x9b4\x8e\xbb\x97%\xdc\xbdVp\x97\xd1\xdb\x1c\x7falR\x1b\xc7\xe6\xc3d^\xf0\x1f\x9c>\x82\x17\xadV\x04.hzC\xa9P\xf8xQ\x10P.\xc0R\xeeD\xc8H\xa3\xc7\xb6\x95H~\xc9\xc5=\x1f\xef\xd99\x9a\x88\x13a\x0dm//@F*%\xf6\xeb\x8a\xd4\xcdU\x0e\xe5\xeb\x84@\xb9N\xf0\n>%Q(h\xa9\x19\xe3\xc2\x97\x05z\x02\xf9\xe5H!\\ \x8ew\x8d\xe4Xj\x9b\xdb\xe0Qe\x04\xba\xb1/\xca$\x9f\xad1\xd2\xb8\x18\xe9\xbc\x874d\xc1]\x81'\x10\xf3{\x13\xac\xc0\x17A\xa9\xc3*\x89\nI\xb5ga\x1e\xde\nI'\xe0\xcc\x1f0G\xd6-\xd6\x1f\xb5\xd8\xb3\x0fQ\x13W\x90\xb1\xaasd-\x9d\xb3\xd1\xa2\xee\x83 \xd9<\xfdn[R]\x15T\xe7f!\xd5$\xf0y\x96g\x0b\x0c\x8a\xab}\xb4\x86Z\xfe9\xf9\xd1\xe9\x01 \xa7\xa9b\x11I\xf3\"\xba\x82\x87\x7f0\xe1\x16\xb7\x08\xa4\x15\xddntP\x04I\xa6\x95\xab.\x8f\x04$.S\xacnW\x12\\b\xf0deC\xdb\xde\xb2N\xbf.h\x89\x1bU\xe22\xfc\xdcg\xe4k\x82+-\x1a\"\xc8\x7f\x8d1\x80\x17\xc7K~=\xcd\x99\x1b\xef2Z!w\xb3B\x86\x92q-\xfe\xc2\xd7[\xe1A\xb3\xd8\x83b\x80\x83\xc4\x83\xbbI\xa0\xbc\xc8\x93ne\xb9\xb3D&\x9d%6F\xbfF\xf1`\xdf\x18\x11\xbe\x8e5\x0c^\x87\x0e1\xea\x16\xac\xe65m0D?\x0ey\xaf\x86]\x9b\xf9\xfe-\x89Y\xc6!X\xc7\x07_3FP\xc7\xd9\xb9q\x88r\xcf\xad\x19\x90aC*\x1b\xce0P\xc5\x1a\xa8j\xe4\xd37\x8d\xbe\x9d\xf2\xc4\xe9x5Y\xe9\x05;\xe4\x1e=\x92\xd6CDc=\xd4\x06b\xe6%\xebxP5{x \x0bdC\x169{\xc1\x1f\xb8}\xb8A\xd4[\xf7z_\xbc\xd9\xeb\xb3\xb3\xe3C\x82\xf3\xbe\xae\x98\xd3TLf\x02\xf4A\xe9\xc1\x1a\xc6\x8c\xb5\x1e\x8b\xb70\xc4\x88\xcc\xf1\xa8\xd8\xe2\x9c\x85M)\x0f\xecA\xed\xcd\xaa\x0fa\x11=\x01\xb6Q\x18\xc7\xb0\xca\xd9\xb8\x96\x83\xe7Zo\xf9\xe6\xc8\xfa\xe6Z\xf0\x8ccA\xed\xd60\xd1M\x17\x90\xee\xd8\xdaix^\x1e!\xb7\x16\xee\x0c%\xe9\xea\x8b\x83\xbbj\xfe\x05\xd5M\xf8\xdc\xfd\n\\e\x9f\x8fB_\xaaj`;\xa3\xb6\xa4\xd3(@W\x8ek\xc9A=P\xbc\xd53'[\xcf\xbe\xfez\x12\xdar\x0bUi!\xc6\xec\xbd\xfb\x9a\x0b\xc76\xe3\xb1\xb0\x1c[\xdc\xa0\xdf\x9a\xf2\x82\xd5\xfb(8\xf6\xd2\x821\xee\xbe\x01,e\x9e\xa5\x00\x8cE\x17\x18\x97\xe6Y\x85D\x19\n\x863\x0e\xa9\xd7\x8d\x83\xb7\xe6\xf9\xd0#1b4\xf6\xe3\xb2\xc3H\x88_u\xf0\xf2}\x94Kt\xfb\xfb\xfb%\xc3\xdfG\x8f\xb8\xf1\xe4\xc4\xca\xefK\x1f\x9f\x82\xe3O\xfcp\x19P\xf8[\x16\xb1\xaab\xedEBJ\xf3,5\x1b\xe9!b\x86\xbe\xd3o\xb1ST\x01\xc3\xb0k\xb69z\xb4P\xd3}\xfb]\x13\xa29\x85v\xd7\xb4\x18\x8fU3\"|W\xb3|\xd0Z\x8a6t\xabC2!>\xaa\xb16e\x9b-\xf6\xa2\xae\xab\x9bvW4\xae\x8a\xfd\xe6}\x98\xeb53\xee/\xca\x90\xfex\x9a\xcd\xdc\xd2\x01\xf3\x01}G\xd4I\xb6h\x11%\x9c\xd1\xa60\x83\xc3`\x93l/m\xa2+\xf1^.\xcal\xc3\x18\x9e\xee\xe4?\x99\xd80t\xe1%\xfb\xaf\xc5]Y\xc4/\xb4}n\xb4\x1d\xb1\xf7\x9eC\xb4\xb1\xe1b\xef\xaf\xda\xc2\x8a )0\xc1f\x1c\x1f^\xbc\x80m\x17z@r\x91*\xdf\x81\x97\xf4\x96\xcc\xa9\xe7\xafH`wiR?*(\x0f\x1c\xbf\x82/f\xbe\x85\xc3RR\x81\xab0\xba \x81&\x1eY\xd3\xdc\xd8\xd3\xd6u}g\xd8)iVPR\xbe\xf5M\x94\xb4\xde\xf0w\xa2\xa4\xf3(\xbbhCI+\x83i\xc1K<\x84\xb4\xeaG\xa1%\xad\x8a\x1aG\xc95o\x0e\xbd\xc6!\xad\xa7\xaa\xdb\\\x87\xd1|\xf1\xdd\x86\xaa\x1a\x1aie\xee\xc4M\xe0n\x85\xf5[\xe7\xc4\x89\x19\xd9l\xd3b}0\x0f2y\n|\x92<\xc8\xe2Ic\xfc\xd8/\x9b:)*\xf5J8\x16\xd5\x10\xf2q\x16\xe6j\x80\xb9\x18G\xc5(N9\x93T5}8\xab\xde]\xd5\xd9U\x86&_j\x8a\x82ZWO\xea[\xd9IiV\xce\x99/\xba\x19z\xdd:^3b1\x88\x9c8\x1ew\xfb\xe4D\x1a\x85\xde\xad\xa7\xc5\xf7\xedM\xa5|\xab\xf8.\x15}\xf8cW\xad\xf4L\xf9\xae\xd4\xd9\xdaS\xea+\xe5\xcfx\xa8\x07\xcf\x8a\xe5x\xe2\xec*\xdd\x0b\xb5\x99\xc7u\xf4\xb7\xcd\xdbHHg\xf7\xf7\xdc\xbe\x8f\xa1y\x8b\x8d\xd5\xcc\xaeD\xe8K^fw\x85\xd5\xba\xd8`\x9e\x95\x0b\x11\xd6\x19\xd6Dp|A\xbfh\x8a\x16\xe1YI\xaf\xb8\xb5\xd3v\x10\xf6\x01\xa0\xafL\x8b>\x9b\xb4\x12\x8dGM1G\xafY\xfb\xc8\xda\xbc\xc1\x8a\xcdV\x10Y\xaef\x91\xd74\x8a\xf1Y\x90\x17p\x95\x89rrn\x8cjw\xd4\xfb\xf6\x04o\xf2C\x14\xf9\xfd\x8b\xb5U\xe2#S:X+\xda\x839\xab\xc0\xe7\xfe\x1f\xdcx\x80\xd1'u%\xc4\xfduI\xe7\x16|{=\x8e\xbe\x14/\xc08/\xc3\xe9gg$y\x191\xde\x0d\xc8\\\xdb\xe6t\xfbp((\x9fS\xae!\x0c\xcd\x0c\xcb\xd1\xe0\xf2`:\x11\xabC\xedtr2\xc2]\x82\x05\x99Y\x94\xe8\xcb\xba\xaeQ\xe1\xacH_ZQr\xf2\xf7\x87@\xa1\xdc\xd1:\xf7f\xc9\x8d\x0d\xba\x93.\xea\xa6,u\x95\x12q\xb3[\xd8\x81\x15gur\x19e\xc1\x1cmu.\xc95\x05\x12\xdeI\xcbk\xbc\x84\x95\xfe\xde\xad\xaf\xbb\xf3{\xc5Buv\x9a\xcf\n\x8d<\x85\x8dg\xa5i1\xean\xa7[\x14\xe8\x9d\xcd\xba\x93n1S\xab&y\xc9ugw|\xed\x85\x11\xd2\xe9\xdd:OZ\xf7\x1c\x96\xf0\x02\xee\xd8\x1f\xf4\x1f\xb7\xd2\x1c\xe7\xa2\xde\xcet9s\x072\xe0\xbb2u;\x9dPp\xe2b\x90'lW]\xd3\xe4:_\xf0\x1b\xe6/\\\x82o\xbb\x7f\x05\xb1/\xb1t\xe7\xb6`T\x0b\x86N\x19\x13\xbfw\x16\xc7\xdb\x91\xf0\xf0;\x9a\x863\xa9cc\xf4\xf4\x0f\xa1q\xe0\xf44W\x82\x15hZ\xd2<\xfc\xc9\xdcy\x99\x1e\x0c\x15\xd1H\xec\xf7\xc2=\xdfN(\xdaV\xe4\xf1\x1c\xdaW\xdet\xcb\x11]D\x84\x07u\xdc\x0c D\xb3W\x13T\xd0\xadH\\\x8b\xdb\xf2[\xc1\xd3\x8bi\xa2\x9d\xc6Z1N+\x03\xa6N\xa4\x1f=\x82%w\xf0,\xaf\xbd_^{\xc8Cq\x84Q\xb8qp\xf2\xea\xed[%\x9eL\x02$\xa6\xe0\x87)\x8d\xd71E\xc7\x87\x04\xc5\xad<\xe8\x9c\\\xda\xa4\x166\xa0\x85<;\x81\xed\xddf \xbb\x82\x15h\x80\xb0RA\xf1\xa4\xdeP\xa9d]\x1f\x1a\xc5\xa8\x0b\x15\xe8Yxp\x94\xd6\xc3z\x18\xff\xd5\xd1Fa,bAQqv\xa0\xcc\xc3\xce\xc8\xa1\xe4\x17\xf2\xb8v2d\x0c-\x03\xa0\x98\x02\x82@\xc4\x92\xb1Wrhn^\xd0\x87\xdd\x9d\xcd=\x11+U}i(k\xb2r\x8e\x15#\xb7J\xfb\xaeE\xde\xe9\x90\xde4\xdf\xaca\xe6 \\B\xc0DL\xf8[F\xcfds/~\x08\x96G\xd4Id\\\xf6T~\xbd\xbfg27>,\x02Y\xb2\xe7\xc5\xafr\x13\x9c\x13\xc1*\xe2\xeb\xfd=W\xeb\xb3\xa7\x18\xa0\x8a=\x93\x91\xaa\xf2'9\xbb\x86o\xca\x1f\xe5\xb6KB\x8cL\xc2\xcd\x07\x8a\x81\xc0\xfd\x80\xce\xdf\x8a:2\x97 \xe7\xdf\x0d\x95O\xf9\xd3|\xe8\xb8v\x052\x88rE\x171\xccG\x8b\xea\x08\xf5\xa7\xd4H\xa8e\xaa!\x10O\xf7,\xf7'\xf2\x17eB\xcb\x97S\xc3\x04\x86b-\x11\x93\x86\xdd\xaev\xe5\x97s\x93t\xf2\xdc$EZ\x12_3#%$V\x11\x82-\x86\x17\x10\xb1?<\x04[\xea\xf8\xd3xf\xa7-?i7\x9c\xdc\x99\x7f\xd5\xad\x1f\x1b\xb1p\xe8\x96\xd9P4\xfb\x95\xd5\x1a\x89%\x95\xb5$X\xa7C\x8dOA\x91\xc9!r\x8a\x8b\xc3\xfc\x86>\xa7\xa0~\xa8P\xd7>\\d),\xa2\x8c\x9drQL\x1f\x94\xc9\xa1He\xf0K\xbf\x9e\xfa\xe0\xa7\xbe1kA\xd3-D\x8b5E\x94\x89\x07\xf46\xa5\xe1\xdc\xa9\x83\x8fo\xea1\x90\xf2|Xg\x95\xe5\x90\xc8\xf7\x85\x8d\xfdI\xf9\xa9M\xe3`\xa5\xccb6?}\xe9l\xea\xf1\x81\xbf>c\x81.\x98h\xe4\x94B/V\xa7\x81tL\x1c$\xf2l\xb9\xc8\x16\x0bN\xba\xeb$3,\x93\xccX\xfc\xf4\xa2 [\x85\xa5@\xa7\x05\xde))\xd8\x07K\x9a\x9e\x84\xfezM\xd3&\x00\xd7\xcc\xd5\xeb{\xb1\xa3\x0c\xd7U\x95\x06:\xd9\x1bD\x00\xf8m\x85c\xd8\xdb\x11\x11p\xc4\xadKi\xb6\xc2:\x80\x1d\xe7\x1b|?w\xcf\x86g\xf1Y\xf8\x7f\xfe\xb7\x9aU\xa0;\xf0\xc39\xbd=^8\xcah\x90\x8a\x1f\xa4N\xc4\xef/\x0c!\xab\"\xd8@2^\x06\xf2\x06\xf6\x9b\xc2\x13\xd8\xe4\x9c\x87^X\xc3q\xc3`0\x00\x1c|o\x1fv\xf4RJ\x1bw3\x04\x91/ A\xea\x90 \xf0B\xc5\x0d\x85\xbd\xfab\xd0\x10#X\x1c\"\xc8\xf8F\x052-\xa0\xe2\xabP!\x0c\xbe_\x01\x15\x81Q\x99\x84\x87\x98\x00\xe7\xea\"\xee\x8aX\x98R\x02\xaa\xa1\x84\xe4\x95\xa1\x01x\x8f\x07\xcc\xefUkAO\xb3\xe6=\xe5\xbc\xe8A\xf7\xf7\xaeJ\xa0\xd4=\x94F\x9c\xfb\xb5\xe6\xe6UB\xf6u\xbb\xda3\xbe\xd8\xfa\x8caE\x0e\xe2\xb1\x1fr\xe1\xb1x\x86\xd1\x92\x1f\xe3U9\xe3XH\xca%\x186)\xa7\xa0\x04(\xd7\xf5\xd8\xdc\x04%(\x9e\x8b\x02~\x05\x82;\x10\x85r|VP\x03G\xa8\xa8x/c\x0e5\xd4]j\xc9tNi\xbe\x92h\x8ev\x953Em\x9d\x9d\xc6\xb1\xa3 \x87\x93\xa4q\xb7_\x81\xf5\x95\x1f\xce\xc7\xc5}n\xe9Y\xae\x90\x1d7\x98w\xd4t\x9e\x98D\xa2\x94\x8b\x00\xca\x07\xbb\xfb/\x82\x00\xfd\x9b\x11\x02\xb9c\xde\xb7\x85A\x95\xb9\xfe\x97\xc3`E\xd6&\x18\xe4\x8e\xb6\xdf\x16\x04\x15\xd7\xd0\x7f=\x08\xd8\x08\x1f\xb4\x13\xc4\xedA\x13\x00|\x19\xbe\x07Ek\xabm\xf0u\x9e\x8cR\xc8\x01&h\xca\x98\x9d\x8f\x1eA\xf7\x7f\xc4\xcd\x1d\xf2\x02E\xb9\xd3\xc5 \x15\xcf\xbaG\xd5\xdf\x9f\xde\xbd\x13\xbf+\xbcv\xf3R7\xac\xb4\xad\xb9uL1\x10Y#\xe0T\xcc\xc1Q\xdaZ\x8d\xe9:\xa6 \x0d\xd3\xb1\xa6%\x8f\x84Q\xe8{$h\x98\x01\x14\xbdv\xffG\x93J\xb3~5\x12D74\xf6HB\x1f\xd02\xaeK\x9b\xc6\xb3\xf5\xfa\xc1\x8d\xe3\xa2\xb6i\xdc#+\x1a<\xb4q\xfd\xc8m\xeb2\xa7\x0b\x92\x05\xe9Iz\x17\xd01tsxu\xff\xe5\xfb\xfd\"\x8a\xfe\xa9\xfb]c?\xd5z\xbf\x97\xf6u\x1agT\xdd\xc7\xa7\xd5\xdf\x1f?\x1d\xca}\xcd\nv\xd4\x97\x17$HJ\xb5\xdf\xd4\n\x0e\xde\x9d\x1c~)]\xb0m\xe4\x87\x0c\xfc[\x12\x90\xeeT\xa4\x13\xf81\x8a\x02J\xc2\x19\xef\xa3\x96\x9cN\xb2\xa12\x03\xed\x17\x93\x1b\x1dQ0&\xc8\x95\xf6\xa00\x91\x00\x1a\x83X\xa56\xdbXG#Z\xf5\xc5\x81=\x96\xeb\xdd\xa6/\x1d\xc9h\xd7\x97\x9c\xd7\x1b\xc3\xbc\xfe\x1d(\x88)C\xe2\xee\x03\x93\x9c\xd6\xb2\xa7\xed\x14\x03\xd54D\xda7\xb4\xa74$\xbfUI]\xa4#u~\x98\xfe;P:\xae\xb4Q5\xd8Z\xcc\x89\xccn\xf5\xba\xa8\xde \x95'q\xa3ylw\x83\x1bB\xf1[\xd4i4C\x19\xad\xdb\x13y\xdesY\x8eN{\xbdh\xe6\xf6\xa1;\x14\x99\xfe\x8d\xe29j=z\x82!\x8b\x1b=\xbfp\x14\x17\xbcQ\xb5+S\xfb\x90\xbby\xf4z\xa4\x9fb\xe6\xb7\x959\x8ev\xddA\x1a}b\x02\xe9+\x92PG@\xa2\xb1\x9a\x0526\x1c\xab\xc8\x85b*\x15I&aO\x0f\x02\x9f$4\xb1\xe1\xe2t\xb3\x0f\xdd\x0b?\xecjR \xe4\x98>\xedC7\xf2R]\x95\x1c\x8e\xd3\xd1\x10\x13Uy\xbaZ%\x88OG\xbb}\xe8^\xd2\xdb\xee\xf7\xbd\x0b0\x8b\xb5\xe5b_\x08\x90\x1f\xe9\xf2\xf0v\xedt\x7fw&\xe3\xe9Fo6q&\xe3\xe1\xfdt\xb4\xf1l\xc6\x8e\xd8\xf3\xd9\x0f\xae3\x19\x9f\x9d\x0d\xe4/VaJ\x0fgXY\xa4\xc4\x9d\xdc\xe7\x15z\xda\xc7\xc5/\xd1\x8c3\x19\x97\x0f\xf2\xa2\x07^\xf9\xecl\xe0L\xc6~\xb8\xb8\x7f\xcb\xfe\x1d\xbdq\xefyQH\xc2\xfb#rt\x7ftp\xe4\xba\x7fV-\xef1.?&\xedU:\xa7O\xcczB\xad\xf0\xbc\x08\"\xf2]\xc4gU\xbf\xcdoF\x18\xa5u:\xbe\xe0`\\\x95\xf9\xa1S\xd5zo\xf6\xcdy\x1am@\x189B\xd8\x07\xc9G\x08\x03\xe4\x1a;2H\xa3w\xd1\x8d\xdc\xd2\x8c\x97\x80 ;\xc8\xc7 b\x00Og}\xe8\xf66\x94+tdX^\x8a\x13\x86\xdf\xa1\x16\xccH\x1fX\xcdE\xc1{\x08\x0b$\x98\x88\xc3l\xf0\xe1\xf8\xe4\xed\xe9\xdb_\x0f\xcf\xdf\x1e\xbdy{\xf4\xf6\xf4\xaf0\x96\x8f\x8e\x0e\x7f:\xa8>\xea\x0eB\x12\x16\xcd\x1d\x91#\x18CZf1\x04is\xd2/\xe33\xa22\x9f\xf1\x86!\x8e\x95\xd3\x10\xb6w1\xe74\xa2\x07t\x95JN#f\xaf\x9b9\x8d\x10~`|\xf3\x18\xbf(\xa3J\xff\x9dx\x0d\x873\x1b\x9d}\xee\x8d\xa1\xe15\xda2\x1b%Bi\xc2\xf8P\xaf\x1c\xf2\x93#r\xc4\xfa\x82\xe4\xc6O\xbdKp\x8c\xca\x03\x8f$T\xd5D\x8e\xb5\xb5@\x01\x0e\"\x9f^<\xe2\x8d\xe5z\xdc6\x8d\x1d\x1d\x1cY\x1b\xcb\x15\xb5\xad\x1a#G\x1a\x8dl\xe1\xf8l\xdcnB\xeb\xf7=\xa0\xc5v\xfe7\x83\xd6\xdb\xa37\xdf\x0eZo\xc3E\x1bh\xd5)\xd0\xf7\x83\xd6\xc67\x05\xd7\xc67\x85\xd7F#\xc0t\xbb\xbdx}8\x18j\xc6\xa2\x9cKe\xbe\xb7\x0f$\xcf\xe95\x810?\xa6\xba\xb4\xcb\x0e\x14\x1e\x083\xb4\x11\x93\x7f\xd6mC\x8d\xff\x8aj\xfcW\xce\x1e)\xff\xb9\x1b\x8e\xe9\xc7\x9f\xbb\x8d\x1c]c\x8b\x93\xca/\xc6\xbb\x9d\xa6\xb3\xfb)\x9c\x9d\xa5\xb3\x9e[z8V{/\xfd\xe0\x0c\"/\xf9\xc1\xe5\x1c\"\xb6\xf0\x83\xf3\xdf\xf7\x0ec\xc6\xdcj7\xa5\xf7\xdd\x89\xebNJ\xac\\\xab\x1b\xdd\xd4_\xd1$%+\xa3)\xcb7\xe7\xd6\x8a\xb0\xe5\xd1\x80\xdeRO0my\xa9/K\xbf\x03\xbf\xa6\x89\x87b\xb85Y\x0b\xf7L\xfd\xb9\x97\xdf\xe0 \x0b\x96\xcf\xc3\xcd\xb9\xb2b\x12j\x9erW1\xf3>\x8c\xe3(v\xba\xafIJs\x9fZ\xca\xcat\xc1\x99|\x91W\xb4\x97NG3\xce\xfc\xf4\xd2\xe9\xe6\x8c{-\x11\xfesk\xd6\x87N:\xdd\x9e\x15f\xb0\xf4\x06X\x07\x0e\xfbo\xf0\xe9\xf4\x95#\xc0\xa0\xf3\xc3\xf3E\x98\x8a\x1ek\x82G\xa9\xe8\xa5\xd3\x9d\x19\x8fO\xd1K\xa7\xbb\xb3>\xa4\xd3\xbd\x99\x89\n\xa3\xca\x15\x03\xdfN\xf7f\x82+\x1d\xf6a\xcb}\x0e\x8b\xc2\xa7r\xeb\xb9\x0b\x0b4\xf0\xd3Q)l\x87u\xb7\xa8\xd3?\x13z\xa5\xd3g3\x04<[\xb3]\xba\x0d?\x80\xb3;\x84\x1f\x10Z\xc3\x19\xf4\xa0\xe7\xa4\xd3\xd1h\xc6\xd0l(\x95\x80\xb8 \xea\x9b\x1bkW\xc4g0\x82M\xc1\x9e\x85\x8bQ\xd5\x1f=\x02o\x90\xd0\xf4\xd4_Q\xc7\x1b,\xc57\x1760\x88\xa6gCa?LR\x12z\xf4x1\xc6\xeeZph\x96M\xc6\x88\xfa\xdb\x93cA\xd7\x8d\x8e\x00\xdf\x8a\x10?\x90\xcc\xf0\x04\xfc\xdf\x8f\xc4t_\xbcP\xac\"L\xe6O\xdf\x0e\x0c\xc5\xcf4\xbe\xab\x0c\x8b\xc3hg\xdb\x1d\xfc\x88\xb6\xc2E\xaf\xe0\x11dd\xd8L>\x97\x1a\xb4(\x18\xba\x07?\xbez}\xf8\xe6\xa7\x9f\xdf\xfe\xe5\x97w\xef\x8f\x8e?\xfc\xd7\xc7\x93\xd3O\xbf\xfe\xf6\xbf\xfe\xfa\xdf\xe4\xc2\x9b\xd3\xc5\xf2\xd2\xff\xe3*X\x85\xd1\xfaoq\x92f\xd77\xb7w\x7f\x1f\x8e6\xb7\xb6wv\xf7\x9e>\xeb=\xd9?\x0b\xcf\xe2\xee\x03%x\xae\xe4\xf9\x1e+\xf6\xc57\xe0\x06J\x1d5^\x8e3\xfa\xe8\x1b\xae\x88B\x1e\x030\xe4\xbeC\xa1\xed\x9e\xa8\xe3 i'\xb9\xfcK\xa5\x19;\x8f\x06\x08\xbb\xdb\x8d7G)\xbc\x80a\xab\xdb\x1f\xd4\x8b\xefj\x1f\x1b)a\x0c\xff\x01OQ\x01]\xc6\xfb\xaf>:\xa3\xb2\x02cz\x16\x9f\x85\xfb3\xa1\xc60\x03=\xb2.K\x86\x91\x80\xb4\x8f\x12\xf3r\x07\x86;\xa1\xdc\xd3{\xf8\x1c\x18\x94\xc9sH{=\x17R\xf8\x0f4\x05\xe3*\x13~\xa5\x13\x88L\x11\xf0\xf2%\x8cv\xe1\x11l\xee\xec\xb8}P\x8b\x9fVK7wv\xe0\x11$\x8c\xec'\x98\x0e\xe4\xc5\x0b\xd8\x85{\xc8rt\x88$:\xa4\xba\xe3U,\xd1\x10dH\\\x82\x03\xfb\x01v\xf1\x9a\xe6\xab\x86\x04c\x18=\xcdu=\xe5\xb6\x86\xda\xb66E)\xbe*|\x0f\x19h\xd4:\xdb\xf9\x9b1\xa6\xdfX\xc4\xd1*\xff\xe2\x04(\x16 \xbd\xc7\xaf\xdf\xd4~\x15C|0)\x87S\xd0\xf67'm\x11:\xe6n.F\x82b@>\xd2Hk2\x0b\xad1`\xe7V\x05;q\xe7g\xd3\x08\x97\x8f-\xfa\xee\x16\xf2|J\xe9\xa6\xaet\xb7R\xb8\xbb\x05\x8f\x00Mr\xd8\x8c\x9c\x88a\xecS\x17z@\xa7\xa9\xf9R\xb5\x8c\xa0[\xfc\x0e\xf1\x1b\x8f\x08\xc6\xb0Y\xa0k\xa9\x9d\xa1\xae\x9d\xedZ\xe1\x8b\x17P\xedqw\x1b\x1b\x1e\x15\xc8\\j\xb9>\xc0\x17/j\x0d\xefn\x97\xdb\xebC\\F\xbc\xfc\xd7Ws\x10f\x89\xb6\xa6\xff+\x87\x9c\xacs\x08F\x85\xe1\x03\x99\xb4\xc8\xe2\xd1`\xf0\xea\xf8\xca3\xdfd\xcf_\x91\xd7\xb8*\xdcx\x1cP\xdb~\xe3\x97\xd2A\xee%\xccv_\xf8\x9c+\x83\xcd\x1ed\"uh0MgE>\xb0\\]\xcb\x01>\xeb\ny\x15\xd5\xb2q\xb3Q\x87\x88\x89\xe3\x87\x10\xdb\xadx\"\xd1$Jj\x16\x8eB\xd6\xcf\x1a\xbb\x96\x9f/\xb2\xd6A\xe6\xa7\xb9\x0fVM\x98!$\xf9\xa1H\x9a\xc1\"\"[\xb4\xca\xdf\x91#Ny[~!\x83S\xd7O\xfc\xb3\\\x8dZ\xec\xfa/\xdc\xc4k\xe2\xc7\xc9\xbf\xd7.\x16\xbe\xbb\x96\x9dJ\xc4\x8c\x0e\xe2\x98\xdc9\x99t\x81\xcco{\xd8\x16\xce\xbel\x0bg\xb8\x85\xf5[7j\xbdu}\xf4\xe7G\xc3!\x85\xe2^\xd1\xbb\x84\xbd]u\xf17\xb5B\xa6\xe9\x8c\xd12\x7f:d\xe7\x0c\xfe\x9d\xcd\xfe\xe9hoXG\x1dW}]\x0d{&R\xd1\x18\xd6\xd1/\xad#\xd1\xae#1\xad#[-\x82\xab\x15\xd5@\xdc\x07_\xc0.\x12\xb0\x8b\x10vF6\xc6\xff7\xd8\xc1\xe5s\xfb\x81\xfb8\xa1\xc6\x0bt\xbdw\xe1\xf7\xdb\xc4\xd6#\xd6\x0f\xc1\x10\x08L9\xc9\xc2\xbe\xb0D\xccIm8Mg\xd6\xfd\xf2mQ\xdeD\xe9\xff\xed<*\xffH\x9ed\xe1\x9c.\xfc\x90\xce\xbfR\xfbb\x81\xc3\xc3\xa1\xea\xd6\xf2\xcd?T\xa6\xbb\x8e\xfc\xb9\x8c/f\xeb]'\xcd\xd94\x7f\xffn\xae\xd1\x7f$Ob\xba\xa4\xb7\xdf\xe5F\xe5\x01\xca3\x1f\x03\xd5`\xbd6\xe7S\xeeW\xa7\xe7\xb3\x19\x11xr\xf6\xc4\x99.\xfd\xd5\xec\x07\xf7\xcfO\xe4\x05\x87\xbez\xac 9\x00\xd2z\xfa\x89\xd4\xbe\x0f\x8dw \xfc\xc2C\x9a\xf2\x86\xd3\x11\xcab\xf2\x16\xe1%\x93K[\x9c\xd8\xac'4\xeb\x9d\xa6\x85!P\\\xb2 *\x9a\xa9\xb5\xf2\xbd\x8f\xe1\x7f\x0e\xc4\xe56Q\x80\xceo\xe1\xaa\xd0-\x19\x13\xf5\xc1\x001\xbc\xd0*.H\xd3~U\x96\xf9J*\x913j\xbc\x83\xb6&1\x0f%(\xd6\x05a\xb0\xea\x01\x1d$Q\x16{\x14z\xac\xc0\x08X:X\x06\xd1\x05 \xc4\xd5_o\x1f\xbaK\x1e\xb9\xaf\xc8D_\x11\xf5\x9fV\xca3\x9b\xd2\xaf\\5i\xd6.\x94_\x08`\x1f\x9eU\xc8 \xec\xc3\xa8r\xad\xb5\x80}\xd8\xda\xac`\x03+\xdb*\x97\xcdY\xd9v\xb9\xec\x92\x95\xed\x94\xcb\xaeY\xd9^\xb9l\xc5\xca\x9e\x96\xcb\x96\xac\xac2\xbe;\xd8\x87\xed\xcaX.XY\xa5\xdfsVV\xe9\xf7\x06\xf6a\xa7\xd2\xc7!\xec\xc3n\xa5\xbd[VV\x99\xdb +\xab\xf4\xf1\x8a\x81\xaf\xe2\x93x\xc5\xca*\xef\x1e\xb0\xb2\xddr\xd91\xe6/\xacT\xfc\x80\x85\x95^N\xb1\xb02\x95\xf7\xb0\xafA\xfa\xe1\x18\xbaggC\xcdQ\xb4\x87O\x88\xe6\xc9S|r\xa1y\xf2\x0c\x9f\xa4\x9a'#\xdeQ\xa8{4\xc2G\xd7\xbaG\x9b\xf8h\xa1{\xb4\x85\x8f\xaa\x0c\x1d\xfbl\xf2\xa1Wu\xd1\xec\xb3\xb5=\x86\xc7gg\xdd\xc7\x9a\xb1\xf3\xbe\xce\xce\xb4\x9d\xf1\xde\x8et\xcfv\xf9\xd4\xceu\x90\xda\xdc\xe2\xad\xbe\xd3?\xe4\xad~\xa8(\x1a\xcaU\xdf\xb2\xf3\xba{\xd7\xedC\xf7\xaf\xec\xbf;\x9a\xe0w\xf1\xe7\xf0\x84\xfdA\xb6\xb7{\xcc\xff?b\xff\xe3W\xfe-\xc2\xaf\xfc\xffc\xac\xbdX`E\xf1\xe7\xcd\x9b\xeeL\x17U\xe3\x8f:\x9d,\xb4\xb6\x95\xabhn\x82\xb2ou-\xeb\xf3\xc8\x19\x9b;;.\xe7\x85n\xbb<\x80\xeff\xb9\xad\xdc\x1a\x19\xab\xef\xee\xecl\xc9\x172\xf1\xc2\xb6\xe6\x05=\xd7\xde\xe1\x8dlo>\xdb~\xb6\xbb\xb7\xf9l\xc7u\xcb\x11q\xbdhNa\x1d\xf9\xa5\x8c\xb9<\x00\xe2\x8a\xdc\xc9L\x0c\xcb\x98\x92\x94\xc6<\x19\xc3\xf0\xf6\x8d\xf8\xe8X\x07\x1c\xe8'1\xd0\xa7\xe5\x95-\xfd\x92\x87\xde\xd9YW\x84u,\xe28\x0e\xf1\xfd\x8d\\Vv\xa1\xa7\x08p\xba\xc8%G\xf5\xc5R\xa2X\xf3x\xe1y\x98n_\x06\xc9\x961\xa7\xdf\x93\xf4r\xb0\"\xb7\x0e\xa6\x0c\x17\xc5\xf7\xf7\xb0\xe9\xcah\xdfW\xfe\xfamxM\x02\x7f\xce\xdbR~\xab\xa1\xb9\x17At\xf3\x8e^\xd3\x00\x99X?9\x8a\x18L\x97\x0e-\x9e\xb8\xd2\x17I)\x93\xbd\xa4w\x81\x08\xc1]:YMLu=%p\x93Ym\xe1\xdb\xff\x8f\xcf\x06\xcds(\x12\xa2pk\x0d\x9e\x845\xae\xdc\x1b\xa4\xf9\xd5\x0c\x8f\x04\xe0?\xe7ARG\x90\x89\x86X?\xac=\x91\xe4!\x18\xa8>\x97}\xc8xg\x19^\\\xab\x8f\xa6\x19\x1b_8%3\xd8\xaf\x06\xc3\x05E\xcd]\xc6gGA1\x868\xd8b\"\x0d%s\xdc\x89\xe2\xf4\x17z\xc7\xb3\xcf\xe4?\xca\x01\xddC\xfa\x9b?\x97\x01\xd5\xf3_\xf7\xf7\xf0T\x86C\x0f\xa3\x8ft\xc1\xdb\x10_\xd5\x16\xc2\xe8U\xb4Z\x93\xf4=\xdb\xce\xbc\x8eR\xa0\xd6\xf4\"\x86\xdd\xe8zu#@\xa9\x14\xa85\xbf \x84\xbcLOd{\xe5\xf0\xb6\x1cu\x1e\xd3`\x85E\xe4\xfaR\xb6F,\x99g\xec\x0d\x92Ra\xaf\xc0K\xb3\x84\xce_\xabOJ\xb1\xfet4\xe2\xa3v3!\xd2\x8b\xdd\x14\xc1~%\x9al\xea\x8at\xc6\xfc~nc\xc4\xf1\x9a\x8d-Q\x83\xa5\x81\x0f/ y\xeeb\xda\x064`\x97\xd9\xfa\x85K\x1f;\xfb\xc1w\xd1\xec\x87\xfb\x8a\x88\xac\x16\xa2\x83\x04\xb3\xbd\x95\x9e\xb0.ydW\x1f\xad\x86\xf8\xf7P\xd5C\x9c Q0\x14x\xdd\xdb\x87\xc8eC\xec\xedW]\xcb\x04\ngV\x10\xbd\xb6\x85\xe3\xd6\x87\xdb\x95\xe4\xf2\x07H]k\xdb\xef\xea$Z\xca\x1c\x08\xb1\x05\xc3>\xfe\xd5\xbe\x8e\x9f\x8c\x0dmm\x96\xa3T\x8d6wQ~\xdf\x1dU\xc3`m>\xdba\xbf\x18\x87RxP0\x96D\xfc\xba\xbf\x87\x9d\xbd\xad\xed\xed\xf2{\xec0\xdeb\xbfx~\x8a\xbc*+\xdf\xadt=\x1am\x8fF#\xebD\xfef\x9c\x08N\xb1\xd2\x0f\xb6\xcc\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebM\xf1\xf5\xd2:\xac7\xc6a=\xf9\xfd,\xfc\x01dT\x13u\xb9\xe57\xb6\x91\xfe^\x0f<\xf2#cs\xcaE\xbf2Y\xa5\\\xf43\xe3m\xcaE\xbf\x01\x06\x99\xae\x0f\xf2/\xf6\xd0\xebl\x1c\xbej\xe7\xd4\xd1\x84B \x0c\xe5\x0b\xdc\xe9<\xeeG\xfd\xe9{N\x07j\xe5\x8cS\xfd$\x12\x92\x96r\x96TV\x12\x83\xf3t\xde9\xfc0\xca\xb0\xec\xbc\xf8z[|\xbd)\xbe^\x14__\x15_\xaf\x8a\xaf\xc7\xc5\xd7\xd3\xe2\xebe\xf1uU|\xbd+\xbe\xae\x8b\xaf\x1f\x8a\xaf\x87\xc5\xd7e\xf1u^|\xbd.\xbe\x9e\x14_\x0f\xc4\xcc\xcc\x89^49\x1f\xd2\xbaJ(7y\x18r\xba\xaaP\xd9^\xcfv\xb3\xd5\xf9$\xc8\xae\xd2\xbf\xafD\x05\xfaM\xaf\x04f+\xf7\x96\x8d\xfdoZc)\x13\x83\xfd\xc5\xc3\xd4\x0e\x12 \x9f\xe7rd\x1d\xf6a\x01hQ\xcdX\x15\xe4Ya\x03\xde\xe3\xe9\xf2\x92[\xf1vA$\xd2\x9c\xbeg'\xc3\xac\x8f\x88\xe9\x1b\xf4\xdc\xb9P\xc1@\xf4\xb5\x00\xd1n$\x1c%\x0e\xbaq\xa8\x7f2\xb7&\xc6\x85\xdcM\x00\x13\x08\xe1%<\x83\"\xed\xd2o0\xc6\xf2\x9fa\x0c\xbf\xc2\x98\x8f\xb2\x13\xf1\x87\x7f\x871\xfch%m\x7fU\xa8Fu\x85\xe8`\x9e\xadJ\xbc\xb7\xe9.\x84\xdf\xfe\xa6\xd5\xdb\xdf\xee\xe3\xc7\x86\x9b\xd9N\x85!\xe3\xa1\xfd\x19H\xde\x16!\x08\x14W\xd3\xc7\x18\xa0\x1dz\xec\x9b\xfeF\xd9\xcf\xb9\x0b;\xe9\x94\xfc\x17'\xed\xf3$\xc6\xbeH\xdeL\x14\x85\xa3\xd1eY\x80\xb0Q~\x92\x1f)G\xe97\x02\x94\xdcYd\xc0H}\xa6\xd9\x90\x87D\xe3\xd9\x82\xccv\xa8 p\xa2\x9ah6\x9c\xe5\x19H\x15T0\xc5n\x04\xeb\xbd\x0d@\x9e$\xa9\xbe{\x8d\x96\xaf\xe8Q\xfd\xf7F?jM\x06{\x90o\xff\xd8\xf8\xb6\xc0\xed\xc2\xe7\xe51z\xbb<~\xdcuM\xf8\x0e\xb2\xf5_\x9b[\xbfg\xad\xff\xc2\xf3\x04r\xbca\xcd\xfe\xe4|dE\xbe)M\"\xb6\xfess\xeb/\x8d\xad\xb7\xc67(\xcb\xee\xb0\x0fO\x9c\xb3\xb0\xe7:\xd3\xdf\xcf\xc2\xd9\x0f\xee\x93\xa5~W\xa9\x1f\x94\xc9\xb3\x9a|\xe1r\xd9DP\x96\x0c&\x90\xa1\x9aA\xb8U@4\x08H\x92\xbeeo\xf0\xfc\xe0\x7f\xce#\xd3\x0d\xfb\x98\x7f;u\x0d{Z\xfd\xa0\xa8~\x16\xcaP0Ct\xffd$^\xfe6c,\x88\xc9k$l\xf5#b\x0c\xc6\xaa\x0b\xb01\xc1\xa7\xfaam'\xc0\xc3\xbc5O\x04\xc4\xc9\x15O7\x1b\xc6\x0cyJ\x18>\xcb\x00o\x80|\xb6\xd3\x13\xe81Y\x0f\x13\xdc38\x88\n0a_\xc7<\x9f\x1d\xf4\xe0\xcfN\xc0\x85I\xbc\xb5\xb0vf\x8ey \x05*\xfa\xc6J\x9f\x19z\x12\xb7 \xdb\x7fk\xc4\xf6\xc7\x98\xac\xa4\xf9~O~rA\xba\xe0\xca\x85\xa4l\xe4\x91\x84\xce\xb4\xc2\x08\xbd\xe4\x02\xda.\xa0\xe7\x0e\x13\xd7v\xb7F\xc8\x04\xd4\x83\x95\xfa(\x15\xf3wv\xb76\x87PD.\xdd\xda\xdeb\xc26*\xa6\xfepF\xc3Mt`Na\x83\xb7\xce\x93\xc9l\x88\xd7z\\\x86c`c\xbc\xdb\x98\xeb\xbc\xde\x0b\xab\xd9\xde>t\x90\x93\xf9\xe4`Zh:\xf5g0\xe6\xa7\xdc\x1fz\xb74\xf5#\xafSmk\xe6\xf2\x8c\xa2\xfa\x86D \x08\xf3\x92\x95t\xba\xfej\x1d%\x89\x7f\x11\x08\xc7\xf71\xf8BU\xc9\x8d@x \xb2n\x13c\xf7\xd9\xb1\xcb\xf3\xbf\x983K\xc1\xbe\xe4\xd7\xa4\x02\x10\xe3\xafin\x01\xe221)\xc5\x95\xd2\xea/B\xb6\xdfx\x8em\xfd{\x9b\x9c\x1e\xe5\xcf\xd8(\xba\xbd..\x97\xdc\x94\x1b\xfc\xb09\x0b\xbb\xd6\x19\xfed\x14\x84MCf\xb8Q\x90\xd4\x8d\x11\xa6\xf7\xb4\xf6\xf1g-\x14\xd1\x1aAq\xbcV\xc9k\xce\x1bTl\x87UE\x96\xe2CY+:\xae2\x90\x85*\x9d\xc0\x0b\x08\xd8\x1f=\x07\x89\xa2\xa3\xe31)oJf\xee\xa0\x88s\xc0P\xc4\x1b\xe4\xf6\x06\\\xcb\xdd\xf1*5\xba\xdc\xbc\x80aR\x9e9\x90\xd3XY/Z\x80\xfaR\xdeN\xder\xa5#F\xfal\x82.\x95\xea]\x98\x80\x87\xdf\xc7\xd0\x9dt\xfb\xe0\x0dr\xbb\x04\xdb\xb1\xc2\xdaXp\x95\xa8\xb8\x1a\x99b33>\x0e5>N\xdfh>\x91\xf1\xbb\x00\xb5K\xee\x13\xa1\x94\xb03sa\xa1\xe2\x06\x0d\x80\xfaA9/\xa9\xf5\x85\x11-\xca\xf4\x99'\xe8\xf7D\x82\xfe\xc7/1k\xbf\xe0\xfdc \x9eG\xd7i\x82Wo\xfc\x04\xe6i\xc2\x10\x02\x8f\x9bN\x9a\xf2\xb4\xa6\x8b\x19\x9f\x99\xf9\xe41OY\x8a\xc3\xb1\xb6\x8a5\xfe\xb4\xc6&K+\xe6w\xec\xfa\xd1\xffU\xd2\xf1\xf1M_\x95\xd9\xd5\xfb\x83|\xc8a\x9fo\xe5\xb0\x0f\x9d\x11F\xc1\xc9\x7f\x0e5\xd9\x82\x13\xc8\xb1\x847Q\xcd\xdb\x9a\x13?U\xa4}\xc1#\xc4\x95\xa5\xdcjVS\xd6|\xd0\x87E\x1f\xed?\xea\xdeR\x0cAQ\xd9\x91?B\x17\x1f\xf9\xa4\xae.C\x85\x9d\xa3h(\xc5\x8dXqI\x92\xcb\x04\xa1\x8b7f\x85o\x06\x02\xeb\xd1#\xb6\x05\x95\x02T\xdb\xdc\xdf\x83P\x84K\xa5\x02\x12\x86\x97 R.\xfb\xa8*u\x85Z\x8aVn_\xa6\xc1\xcc-\xa0\xdf\xfd!\xa6\x8bs\x86\xe3\x15\xf1\xderQ\x8d\xd3\xc2\xb6;\x9a\xc6q\x08\xba\xf2}\x9eR\xdc\x00W\x97\xaf\x1c\xcf*\xab\xde_\x8aU\x96\xc7\xcd\x04\x9cN\xcd\x96I\xa3!\x92\x9f\xb2r\xb9\xaf.\xb0\xc5\xa2\x95\xdf\x1c\xa7\xc4\"\xe0]V\xeeYM\xb9\xf1\x91\xd6H\x1f\x04y\xa5\xe8\xc2%~w\x9aT\x80J\x0e\xd9\xe2$\xd0\xb4\xa3\x145\xb4\xa8\xbe\\\"u\xf9u\xe7*K\xd0\x92\x80\xc0\x05O|\xc3\x13\x98\xdb\x8c\x10\xa1\xa4b\xe5,\xc4e\xe9\xbe\x8d<\xe72\xd8\xc8E\x95=\x135\xc4\x823\xc8\xf8\x0c\xa9\x1d\x0c\x89$\xae\xb5D\x88\x89p\xca\x18\x9c\xcb\xa9?\x9b\xf5\x05\x8d\xe1\x96\x80\x19O\xcb\xce\xffq\xbc\xc7\xdd\xd5b\x07 \xe4\xc7\xbd\xc1\xbe\x15\x1e\x15L\xf0\x90\x89\xe0e\x1dO,\x1d\xd6,\xe77\x9f\x88 N\x13\xc6\xa8\x8a\xaf\xd0\xc5\x8d\xd7\x93\xaf0\x0e\x83S\x81\xd2\xdc\xd4\xa9$|\x1a\xc1\x17\xf4<.z\x1eC\x97\xe1uo_\xed\xdd$\xedHZk\xa2\xee\x89}&g\xe4K\xda\xe2\x14t\xe4QNG\x90\xc9\xe3\x9d3\xd9\xac\xbe[m[\xb5b#\x914\xec\xd3\xa0y\x9fz-\xf7i5\xa7\xb6\x97\xa3o%\xa7vV\xbf\x8a\x9f\xa0\x00\x8eR\x93\xa0`\xfc\x18\xc2\xbb\xddn\x1fq\x02\x95 S\xb6?\xbci\\`3N\xb63\xe2\x87_\x01\xd22N*\x8dq\x04\xcb\x8a%f2\x96q8\xc8x\xa3eF\xbd\x0e\x17\xaf\xb099\x14R\x1e\n\xb2\xe6Y{lR\x8f\xf5\xee?X\xaf \xeb\xbf\x11\xa3\x9a\xd0\xa9\x0b]\x05\xa9\xeac(\xa8\xa5\xf6`.\x1d-e\xf0~\xc9iRx\x00\xdb03\x93\x98i\xc16\xc5l'4\xd9\xe8\xa8\x84\"D[\x1d\x95\xe4)$4B\x12J\xcad\xa6%1\xc1\xb7\xba\x1b\x0c!\xc4W\x9e5\xb8Xy\xfb\xc2g\xca\xc2\x13\xce!\xcd\x9a\x16\xfd\x9fAF\x1a\xd6\x88\xb4X#\x85\"\x84&\x8a\x90\xf3\xbe\xd3xV\xdeA*1\xf091h\xd8\x8c\xae\xd0U\xb6\x82;Q7\xdc\xb4+S-7\xc2\xbe \xf0\xad6\x9cY\x94\xcc\xb7!\xd7(\x89@\x03I\x93\xf4X2\xd5k\xf4m\x84\xaa*-\x0b\xb98F.\x02\x8a\x9eT\x10-\x801/|,i\x048W$Kz!K/'\x95\xf9\x87G\x8f\xf8\xc5\xa4DbT\xe0\xd6\xc1]+i\xe2K\xca\xab\xc1\xc5N*\xc4\xce\xeeKu=\xfed\xee\xa8.\xd2\xe9D\xb5\xff2+\x03sm\x94.\xd4\x8c\xce\x1d\x87\xc7\xbb\x94-\xa3\xfb\x97\x89~*\xb4\xb3\xbe\xa2\xb9\xe5c'O \xa6\xd1\x80\x98}\xec7\x94\xc0\x14\xa1zO[Xy\x15ia|\xdc\x9c1\xf7ui\xbc\x85\x0fy\xbd\xd4\xed\xf3ce\xe0'<\xb4C\xaa\x89\xce.?Uf851\xc3\xd4I\xa7\xfeL@\xcd<\x12{G\xd5X\x11\x15K\xb8\xc8\xd6y\xc4y\xeb\xb0\xee\xc4\xca\xd0$\xe2dZ\xb9R\xf5\x0d\x97\xa8\x90\xaar-\x82,\x9a\xfa\xd3p6\xabL+\xd5\x98\x03\xe6\xe12b\xbb\xd2\x8fR\xab\"\x9b\xb5s\xc43\x02\xb0S\xe8\x1fUOB\xa9\x97V\xcc2q3\x84\xc8\x03\x85}6GZ\x9c\xb0\x13\x08%\x8b\x85\xda\xcbR\x0e\xf2b\xe7\xe5n\x9fr\xfbR\xaadh\x1f$dA_W\xac\x15,\x96{|\x8a\xf1\x80\xde\xa64\x9c;\xf5}\xc4m4\xc7@\xca\xab\x85'~et_\xe4\xf6\xa3z\xb1Z\x07,\x0d\xe9\xd5\xac\x07x\xd9\xd6q(\xecC\x8f\x9aC\xcaX\xa3\x99\xf3h\xe1\x97i\xba\xd6\x04\n\xe7\x0fo\x12C\x0cq\xd1\xdfS\xc1\xec\xd57T\xd1\xb8\xae \xd9zC\xf3\xdb\xdb[\xf6\xf6\x17\xda\xb1+-l\x8e\xec\x0d,\xa3\xf5%\x8d\xedm\xec5Lr\xe1\x07\xa6P\xebzs\x04\xeda\":\xf9\x16\x98%\x1d\xca\x1a\x83\xc4\xd47~d\xbc\xde\x99S/\x9a\xd3O\x1f\xdf\xbe\x8aV\xeb(\xa4a\xea(Q:\xcfzh\xb2\xc0\x18+\xcd\xceM\x07\xdc\x7f\xc2_\xdc5!{NT\xaa\xf1\x05$\xed\xd1\x9e\x8c\xdcQ\xdc\x0f\xa1\xcb;R\x9d\xcd\xf95\x0dZOO\xd0#\xde\x85X(6\xd1H\xf2\xd1#\x10G\x0f\x0dkS\x8cP\xb2\xdbG\xb6\xa0\xfe\x94'\xf03\xd0\xbe\\\xf4I\xd1O\xf2\x8f\xc8\x0f\x9d\xee\xa3\xae[!o}H\xb9go 2U\xb0\x94.\x92\xd1@b\xfa\xfb\xfe\xe4\xd1\xac\xe7\xeeO\x9c\xe9\xef\x8f\xb8\x95\x04\xae\xfa?>?G(\x86V3\x01i0\x159\xe8\xb4i6\x8fb\x156\xabg\x0b \x9b\xe2\x87\xfc\xba\xd7\x89\xa7\xfe\x8c\xb1\xc9-x\xa6\xf8a\x08^\xf8FnU}\x1a\xb9o\xe4\xde\xee\xb6\xd67rk\xb8\xa9\xf1\x8d\xec\x1e\xde\xae\xa9\x97\xd2\xb9\xaag+W\xcb\x14\xdf\x97\xf2\x93$\x7f\xe2\x87-\xc8\xb8\xe1\xcaL\xdc\x94\xf5a\xdd\x87y\x1f.\xfb\xe8\xc9\xa8\x89\x01\xba2X\xe2.\x0d\xe5w\xa8\xf9-\xafSE\xb5Yl\x8a\x92?\xf4\xe9\xdd\x9ar\x9fh\xa2\xe6R\x06\x950\\\xe8\xcf\x10\xb9+\x03=\x02\xe1\xddK\x1du\x04.\x04\xec)\xec\x8bh=\x1c\x10)W\x1a\xd3\x01Y\xaf\x83;'\xeeW#>}6\x0c\xf0\xdc\xech\x8f\x16\x12\xb0\x01\xe6\xfc\xedJ\xbc\xa0Kn\xb7\xf2R\x90\xa1P\xdei\xa0\xe8\xc0Z\xb9f\xcf\x16\xad\xc6t\xa35\x97dC\xa2\xb8\xb3t\xbbj\x01\xce\xb9\x9ac\xe3\x90\xed\xe0Z\xb59\xec\x83\x08\x05\x1fe\xa9s\xd3oa\x94\"A\x91\xc2\x068\x08\x0f{\x00\x88%L a\xdc\xdaB\xbep\xed\xd6\xf3s\x00ga\xabn\xdf\x06\x88\x1cZ\x1d\xad\xe7\n2\xa0Av\x00\x13\xb8`\xaf\x8c\xf9\x9d\x8e\x8a-5 M\xdf\xe3m\xd3\x1a\xe81\x97\x01\xea\\\x0bz\xb6Bl,$^f+\x1a\xa6 \x0f\xe4\x9f^\xfaI\x1fo+\xa8Ei\xc2^V\x90\xad\x10\xbf\x9b\x97\x0f\x14t\xe5\xbd\xd4\x91\x80 $\xab\x02fkmC\x9f\x1d\xd3\xc2\xb3\xd1-]u5\xea\xcd_8\x97m\xe4\xf0\xfa\xc6BSyG\xd7\xa8\xdb\xaf\x8cT{r`\xaa\x0bF\x85\xee\xefQFrB\xae\xfbA:\xd9a\xe7-\x99\xfb\xe1\x92g\xdap\x18\x95\xec\xae\xc8\xedo\xc4O\xbbty\xbb\xb5PS\xe5~p\xa2{#\x97u\xff@ *\xdd\xeb9\xe1-]B\x0f\xab\xac\x05\x82\xe43\xa1\xaf\x0f\x9d\xd8\xa9\xc4\xcd\xccs\x08\x15\x0c\":`\x8c\xc1#\xe1\xe3\x94\xcd\x0dH\x02\xb9|\xd9\xa9\xd8O~\xd6\xef\xd0\x1a\x80\xc6\xa0]\x14\x14-\xba\xe7\xe7\xd8\xfe\xf99R\xe4\x7f|\x86I\x15LZ-\xa89\xe8\x16\x8fC\xe7l?s\x1di\x15\x85\xe2`\x9f\x81vw\xe8\x0e\x16NUp\xee\x832\x0c\\\xbc>l\xba.\xeb\x7f*\xc3\xd9u\x1c\xaa\xda\x8c\xa1\x9aM\xe78\xd5\x14y*\xd5G\xcd6\x9e\xb0*0\x8cl\x87\xa8\xebK%\\\x8aFx\xf9\x9c\xd0\x1cM\xd0@\xf6\xb8\xae\x06\xad\x9a\xc1\xfe\xe33\xbf|\x19\x8b\x83\xa6\x82z\xde%\xf5\xae\xc6\x8aEv\xebM\xab\x92\xf5\x02\xe5\x8b\x8d\xdb\x82\xe8\x1b\x8f\x1d\x0fC6\xf0:\x0f\x1b\xd9\x97\xed}\xde\xdf\x18\xc7\xff\xcc}\xe0~oV\x1a2p\xed|E[\nx\xab2\xb4\x90\xad\xf7\xb4I\x88\x9d\xad\xbd-m\xdc\xa1\xa7\xba\xb0C\xa1\xb3]\xad\xcd\x07\xfft\xbbZ=\x10\xe5\xd5\x83\xc0\x13\xbdVG\xb9\xe0\xf5w\x86\xa5\xd3\xf0\x99\xf2+\x1a\xf8![\x1a\xa7\x82U\xeb\x1a\x19Z\xf8\xe1\xfc\xf5\xf1\xfb\xa3hN\xc7Ui6\xa6\xe1\x9c\xc6c\xf0\x07\xfc[e\x92\xe1*\xca\xc24\xd7\n\x1d\xa4\xbc\x11\x7f\xa0\x7fR~\xfb\x9a\xc6\x89\x1f\x85cH\xaa\xad&x\xc3v~\xc1\xe8\x05\x9d\x7fZ\xcfIJ\x931d\x83r\x89\xe15>\xd2\x93\xec\"\x8d)}\x1b\xa6\xd1\xab(L\x89\x1f\xb2y\x14\xc2\xabB\xa1\xf5\x91\x1a\xcf\xcf?\x1e\x1e\xbc:=\x7f}\xf8\xeb\xe9\xf1\xf1\xbb\x93\xf3\x9f\xde\x1d\xffx\xf0\xee\xfc\xe7\xe3\xe3_\xce\xd1CWk9e\x7fM,\n{\xbbU\xc5\x8ar>\x87\xe7iL\xa9.i\xf8\x92\xa6\xaf\x82(\xa1I\xfaV\x10\xe47q\xb4\xe2\xab\x12\x0f\xccO5\xba\x16\x8aK\xc6*\xc8\xcaM1\xc3@\xb9b\x18\x88e\xa0\xf3|\xcc\xfc\x02\x921\xfbR/\n=?`\xcb_\\h|\xaepH\xeboAL\xf6\xf6\xaa\xd1\xca$5\xa9\xeewNM\xf6\x9e\xea4u\xac\xbc\x1a\xdd,\x13\xe5U\xaa$\x88\xe1\xd3j\xbf\x81(\xaf\xf6\xcb\xe9\xc9\xde3==\xa9\x11\xc35'3\xa3*Y\x9a\xf3\xf2\xcd\xea\xe1w)\xcaG\x95\xf2kQ^\x9d\xeeJ\x94W\xc9\xe4R\x94W\xc1p'\xca\xab`\xb8\xe0\xe5[\xd5\xf6\xcfEy\xb5\xfd\x1bQ^\x9d\xef!*\x18\xdb\xf0n|{6\xc4\xce>D>\xeeP\xb8p/\x07\x87\xd74L\x0fW~\x9a\xd2Xl\xf0\x8f\x94x)\x96\xbf\xf3\x93\x94\x864vVn^\xf7C\x90-\xfd\xf0\xe7\xecB\xd4V\n\x8f\xe39\x8d\x1dR\xad\xfb)\xf5\x83D\xd4.Q\x0bga\xab\xcaj\x9c\xc6\x84\x91d\x12\xa0\x80\xde<\x82\xe4\xc7\xbb#\xb2\xa2\x9a\xfbC\xf69\xf1W\xeb\x80*\xd5\xc7pS\xa72\xecs\x18\xa64~G\xc9u\xb9v\xa6\xaf\xfd\xea\x92\x84\xcbrMCv\xb3\x13\x1a\x94\x07<\x86s}\xcd\x1f\xe9\"\x8a\xe9\xdbp\x9d\x95\xab\xd7]\xb4>#d~\x8e\x92\x02\xb8\x020?\xb1\xb5\xf3\xbd\xbc\xf8U@\x92\xc4\xf1\x8c\xf5O\xe9mZ\xa9|\x89\x95_\x1f\xbf\x97\xd7T\xa2\xaaR\xf2*\n\x17\xfe\x1235\xb4\xab\x99\xb4\xaey\xc1\x17}\xb5f%\xe5\xb1\x96\x0b\xdf\x10/\x8d\xe2\xbb\x16\xb1>\xa5\xc2\x81\xde\xc0\xba\x1a\x98\xb2\x80\xa68\xcd\xf3\x0d!\xc8\xf5iL\xc2\x84\xf0\x1e\xee4\x15\x7fd\xbc\x80\x1f.O\xd2\x98\xa4ty\xe7\\c\xa5\xda\xd8\xc3k?\x8e\xc2\x15\x0dS'0K\xf3\xf8\xed\x8b\xc8\xbf\x99F\x08\x00\xfb\x8cw\xa9\x03\xa8Kb\x9flxY\x1c\xd30\xed\x8eu\xf7 \xbc\xca\x9c\xa6\xc4\x0f\x12k\x15?a\xac\xcf\xdcV\xe7\xd2\x9f\xcfih\xab!\xfc\x02mU\xae\xe8]r\x19\xc5\xa9\x97\xa5\xd6\x01\x05\xe4\x82\x06\xb6\nq\x14\xd09M\xbc\xd8_#\x07e\xa9J\xb24\xf2\"FMRj\xab\x87\x92\x97\x1d\x06\xf4vM\xc2y\x03\x9cH\xb2\x8e\xd6\xd9\xda:=zm\x9f\xde*\x9a\x13{\x05\x19\xb5\xbc\xb1R\x82d\x8c-\xaf\xadj\x14\xfb4LI\x13,\xf1\xce\xfa2\n\xe64\xb6V\x8bi\x92\xd8\xc1\x14S2\x8f\xc2\xe0\xce^\xe7o\x99\x1f\xdb\xdb\xe1\xd3k\xa8\x13\xc5\xd6\x1drM\x82\x8c\xae\xc8ms\x1d\xdf\n\x1d\xac\x13F7\x8duRzk\x1d\x10I\xa3\x95\xef\xd9j\\d\x89\x15t\x81\x7fm]\xef\x98\x06\xf4\x9a4\x10\x0eF\x7f\x16\x0b&\x9f[j-crqa\x87?\xa3\xc2\xd7\xb8]i8o\xe8\xd4\x8b\x02\x8f\xf1\xe1\x0du\xd0P\xae\xa1N\xb2&\xd6\xe5\xf2\xa20\x8d\xa3\x06\xca\x884\xe6\x82\xce/\xac\xe0F\xcf\xe8\x15M\x12\xb2\xb4\x82}\x11D7id]8F\xf9\x82\xa6\xfe\xa2\x9b\xd0:\xecu\x94\xf8aB\xadP\x8c\xa3\x9bFH\xc7\xd1M#\xa4\xe3\xe8\xa6 \xd2 M\x13\xff\xef\x08\x99R\x8d\x8a\x00\xf6\xfa\xf8\xfdA\x9a\xc6\xfeE\x96R\xc6\x1a\xb2s\xaf^E\xf2\x1dy\x8d\xbc\xc2W\x9c\xc2\x8aFgX\x95V\xc4\xd5\x81^\xa3\xb3\xb7W\xad.e\xb0\xaap#e\xb0\xaap\x83q\x08\x9f\xf5a\xb4\xd5\x87\xcd\xbd>lmV,[\x990\xb6\xb9\xa9 \x14\x1d\x0d<\x12~J\xe8\xeb\xe3\xf7\xa8O@\xde%\xf1\xd9\xcc\x91\x0fE\xbd/O\x11Q~\x19\xc5\xb5R\xda\xfcjS\xf3\xc8\xc3+\xda\xf7\xd1\x9cb3\xb2\x00\xa4\xc3\xa0,\x18\xa8U\xab\xca\"~\xd3Zm\x9c\xf1\xae\xd5\x01\xb2\x07\x1d\xee\xb2\xe7\xd4\x0dk1\xf5\xbbHv\xc1V\x9f\xb8F\x05\xcaz \x14C\xac\x06\x9a\x07\xbd\x0dS'/u\xdc>\x8c\x86.\x8f\xe7\xa7\x11?+cu:\x1e\xc8HT\x0b\xc0\xec\xbe\xec\x0b\x86\xe4\xabL\xf6Z\x13\xa6{\x95G-\xc5t\xbc\xaf\x84W\x03\xe35K\xf5\x96\xdax\xd2\x17\x85\\\xa1\xe3\x00\xd9g}I\x12:\xffH\x97~\xc2\xf8X?\n\xe5\xb6\xd0Vg\x9f\x8b\xec\x82\xf1zc\xe8F\xa1\"\xb9X\xbc\x10<\xb2N\xb3\xb8\xfe\xca+^^\xb7\xe5\x87\xfa\xde\x96\x9f9]\xd3pNC\x0f\xd9\xdai7\x8d\xd6*\xda\x86\xf3n\x1fX\xe1/\xf4\xee\x03\xe3\"\xc4O\x862b\x98\xf8\xfb\x03IR\xda\xd5$\xe5\xab\xf7\xea\x95\x9a\xffN\x80\xac\xce\xa1\x1d,\xcbo}#p\xfe\x18d\xb1\x80\x92 \xb2\xaf\xa3\x9bP\x0f\xe7_\xe8\xdd\xa7\xb5\xf8\xfe>\xca\x12\x8aU\x1f\n\xe7\x93\x94\xc4\xdf\x0be_U\xba\xf9\x02X\xe3{\xdf\x15\xdabd\xff,xs\xc9\xf6\xfb\x03\x9c\xf7\xf3\x05\x10\xe7/~W\x90\xcb\xb1}C\x98\x97J*\xe3\xbb\x13\xaa\xbe\xbc07\x9b\xba\xd0^\xa5I{r\xad\xb2\x83[C\xe7C\xb3ZD\xd7r\xf7\xa2G\xc5\xab\xf2\xe1\xabk\x18gim:o {\xd0D\xd3S\x9b\xe3\x105\x19\xa8\x97@k\xa9\x84ki\xb7\x00\xd7\xc4\xac\xb3F0j\xb2\x1c\xd7ymhL \xafe\xde\xb7\x01W\xa0\x94G!:1\x05A\xe9\xceIJ\x90\xbbIa\x02\xe9\x80\xfd\xac\xdeI\x14#b]\xdd\xe4,Y}t\x87\x92\x8f5\x84\xa6\xcd\xfa\xba\xd8\x0e\x1e\x86l\xb3\x99FC\x13^\x82\xbaT5\xf2\xd6\x18\xf3k9\xa8\x9e z\xe39]\x17\xec\xbczX\x07\x87\xe1\xbc}\xf3\x82Z<\xac\x07\xfeR\x13\x9d\xe0\xd7O7\xdc\x96\x10\x85\x8fG\"J|u\xb8h=\xd7df\"1M\xd9\xc4\"\x92\xd3\xa3G\xca\x8e-\x07\xba\x16\x031\xf7\x8e\xab\xe1\xf6AI\x18^\x16\x08\x00\xf9a\xf6.\xc6q\x17\xe1{kMp\x1c\xab>:\x0c\xd1j\x8f\xe7\xa9c\xf2\xcd\xcd`I\xd3\xd7$%\x8e\xcb\x81\xb3\x0f>\xdawEQ@\xe7NTu\x05`X\xbd\xc0,\xc4E\xa5\xac\xd8\x03udO\\X\xf0]V\x8bsbp\x05\x95\x97\xd9\xe7Z\x7f\xfb\xdc\x92GDH\x91m\xb7qn\x8c\x07\xc4\xf3\xb2U\x16\x90\x94\x9e\xdeD\x1f\xd8\xf1\xfb\xdaO\xd6x\xf9\x9c\xe0E\xca\xc2J\x8dn\x1b\xf6;\xa9\xcf\xbf\x83\xd1\xa2\xe6U\x13\x9fo\xb6\xe3[m\xc7s\xa7\x1a\xb0F~\xda\x1c\x1c\xf2\x93\x1fF7\x97\xbew\x89\x8bp\x0d\x13\xbe\"cp\xee\xc4u\xd8\xaa\xa9\xabBd0\xf7\x95\x1bv\xe3\xfa\xea\x1b\x04\xe5&\x02Q\x1dc_\xdf\x15C\n\xf5\xef5\x86\xd9S\xf6]3M\xc1\xad\xdc\x82\\0d\xb81\xad,:5\xd4\x17\xb6\x88\x0c\xd7\xf1\xd8\xdc\x04\x07cj\x05\x14\xc0)\x1b\xbb\x11z\xfe \xa6\x01% un\xdc~~\xe0\xf5\x0d\x01,\xf5\xae\xce\xeda\x06\x0fBu.O\xb6Z\xabo\x8e\xe1\x8f\x1eA\xa7\x85iD\xe5m\x87\x0e\xbc4\x0e~\xa1w\xb8\x1ayJ~\xd8\xd0\xd1\xa2\xcf\xd1s\x80\xf2\x83\xf7\xba\xf9\xbe\xb9t<]XD\xa8\xb1\xa8\xf8*\x1b \xba1\x8b\xdcQ\x1a\xda\xd6HX\x01J\x810\xc1\xaa\xac\x96\xbc\x0d\x1d\x9c\xdf\xc4d\xbd\xa6\xf1I*\xb2~\xa4\xe5\"\xf3\xd5\x01gT0\xd0\x980\xd7\x0d8\xaf\xd3\x0d\xb3\xd5\x05\x8d\xf3\x95c\x0b`\x19\x0b(\xacw\x97\xe7\x8c\xc3\x03\xcc\xdc3`\xf4\xb5%Ms\x93TG\x9cyn\x112\x17\x1d\xefk\x15\xb4+\"?\xfa{\x8dz)\x9eB\x81\xd1\xe1D\xafp}\x8f\xa5_)*\xef=\xd595\xab)\xde#q\xa4\x8a$\xe2V\xb4i\x197\xd5@\xe0\xf8\xe5\\L\x17\xf5\x85\x928\x18\xd60\xd7\xe2\xce\xaf\xcfV\x00\x13\xa0\x0e\x0f8\x92]\x04\xbe\x97SMd\x02\xe2\x01\x99\x17n\xa8\x07\xc9G\xba8\x8d0m_\xbf\x1ab\x0bp\xe1B.\xc8\x0d\xce\xa3\x9b\x90Vc\x96\x16K\xc8\xc4\xb7\xe42\xca\x02!\x06\xb5\x81\xa6\x84I]r\x03\xa9\xae\xac]a\xe4\xd0\xa7\x06\xe8c\xb9\xc8\x86\x16\xd3\x85LL)\x86_\xbf\x0f\x89\x8c\x03\xf0\xb5\x03P.W\xecX\x90\x13\xcb\x94\x8f\xc3\xc7\xafb\x1c}\x08\xf1m\x0c#\x9eG+,\xde\x8e\x90\xc0\xf1\xbdY\x062g\x89\xdb\x80\xf7\xff5\xc8\x8a<;\xe2fLW\xd15-\xa3';\xf9\xbf \x82~\x075\\)\xe2\x80Q\x03iP\x8a\xfc\xe6\xc1^\x0b\x13G\xedR\xa7\x91Xh\xf3\xfb\x1e\xe6\\\x9a@d\x89\xfc\xe2\xac\x8d\xc1V\xd8\xe73_\x81 W8z\xe6!\x8b\xf0\xa0\xfb\xfb\xe0\xb5\xc4\x94\xb9h\x16D\x92\xe4\x04\xc6|\xb05\xf5G`\xb8\x96\x07\x19uD\xb4\xe2Y[\xf1,\xad\\WlZ\xc9\xa0 P\x88\xd0\xb8S\x0ds\xc9ov\xf0\x9d\x80S'V\xcc\x17\x0c\xd3`]WVq_\x17\x95\x17\x04dV\xfa\xd1 \x81\xc60\xca\x96\xd1\x08\xd0\xaf\xca\x83\xa2\x9c\xb6\xb3\xe2\xbc\x7f\xf6\xab:\xa8y\xd9\xce\xa98D\x95{\xa9\xeb>\xac\xf8&w\xfb0e\xbf\x1a \xa9\xfe\x8c\xcf\xb0\xf4+\x0f\xd2Z\xf4\x1bv\x8e\xca\x00+~\x14\x0e\xde\x7f:9=\xfftrx\xfe\xe1\xe3\xf1\x87\xc3\x8f\xa7\x7f\xad\x9f\xafj\xf5\x9f\x0fN\xce\x7f<>~wxpt\xfe\xeb\xc1\xbbO\x87\xf5c\xb7Z\xfd\xe8\xd3\xfb\xc3\x8fo_\xe9\xaag\x9a\xea\x1f\x8eO\xde\x9e\xbe\xfd\xf5\xd0\xf6^\xa2y\xef\xf8\xd7\xc3\x8f\xef\x8e\x0f^\x1f\xbe\xb6\x0d0\xd0\x9eR~\xf2*K\xd2h\x95k;\xc6\xf0\x91.\x0fo\xd7J\x94\xfc\x94&\xe9\xe0\xc2\x0f\xe7NHo\xc4c\xa7\xfb\xbb3')\xb9'\xb1O\xdc\x0d\xcc\x01\x14\x0f\x0eNO?\xbe\xfd\xf1\xd3\xe9\xe1\xf9\xd1\xc1\xfb\xc3\xf3W?\x1f|\xc4\xbc@?\xfc\xb9\xab\xcb\x1ao\x0f\x85\xc1><\xb3\x8e\xd6\x07\xb9x\xfc\xea\x92\xc4\x185\xd1R+I~\xa1w\x96\x1a)\xc6\x1c3=\x0e\x82\xe8\xe6M\x16\x04'^L\xa99\xb6\x0c\xd6\xc3\x08%xjx\x96\x0e\x03\xcbp\x13\xcb\xa3\xbb\xd03w\x9f\xa5\xd1+\x11\x12\xc3\xdcD\x96F\x1f\x02rglE\\\xec\x9b\x9f\xd3 \xf8@\xe6s?\\\x1a;auN\xd6\xc4\xb3\xd6\xb9$\xf1\x89e\xd5\xbcK\x12\x04\x14-\x1c\x8c50\xb4\xc7\x18\"\xb87\x8e\xd6\xb7\xc0\xc2\x0bH\x92\xbc}m\x7f\xceYLS\x8d(H\x8cA\x89\xbc\x88\x01\xc1\x8cV^\x14\xa64\xb4@\x80??\x9c\xfb\x18\xe8\xc3^\xef6}O\xc3\xccZ'\xc6\xc1\x9a\x00%*\xbc\xf3\x13\xdb\x88\xa2xnFO/\x8e\x92\xe48\xf61L\x92\xa1\x0e\xb7\x0c2?\xa4\xa7\xbe\x05\xdey|\\\xc3,\xe6t\x81\x81 \x0dO\xfd\xd8\xdc\xb2\x08\x96c~9\xba \x83\x88\xcck\x91 \xf3\n1Y.\xad\x0bEC\x8f \x04\xc6\xe7\x8b(^Y\x1f\x1e\xd8\xe9\x14\xabr\xd8\xa2\x8f\xf74\xbd\x8c\xe6\xd6*G\xd1\xaf$\xf0\xb9\xff\xa9\x01 \xac\x1a\xe7\x0f\xcc-\xc5dE\x7f\x8cb\x8c\x16i\xa8sI\xc9\x9c\xc6f\xa4\xba\xa4\xfe\xf2\xd2\xdc\x05\x0f`d\x1c\xe4\xa5\xbf\xbc4\xbf\x1b\xd3\x85\xf5\xe1;b!`\x97\xe9*x\x13Y&\x96\xa6\xeb\xc3\xbfe\xfe\xb5\xb1\x86\xefY\x16\xd37/\x10\xden\xbd\xc7\xf0\x8d\xc6\x1a)]\xc6~j>\x81|3\xc4\xaf\xe8\xdd\x07\x12\x93\x95\xb5\x86\x15\xc9\xae\xfc\xd0d\xeet83ov*nd\xd9$e\xba]D(4\x7f2\xec\"~]\x19\x95\xea3\x08a\x08|\xda\xd7\xed\xbe\xca>3$WK\xbe\x052\xd5\xd0C\xe4\x87xVE2\x11\x9b\xf4\x99>?\x84.\xd9L\xac\xac\xe8\xa40\x9d\xe7\x89x\x04\x85r\xbas\xff\xfa\xffa\xefM\xdb\xdb\xc6\x91E\xe1\xef\xf3+`\xde9ij,)\x96\x9d\xc5Q\xe2\xf6u;\xce\xe9\xdc\xc9\xf6\xc6N/\xa3\xf6\xf8\xc0$$\xf1\x84\"8\\d\xbb;\xf9\xef\xef\x83\x02@\x82d\x81\xa4lgf\xeey.?\xd8\"P\x00\xb1\x16\xaa\n\xb58\xfa\xbe\xb7\xb9\xf2\x1e\xfe\xfd\xb7\xf4//\xdc\xdf\xae\xb6\x07\x0f\xf1Q\xe8\xa5\xdbX\xbb\xca\xcf\xc5\x9a\xa2\xee\xd6\x04\xd1DL:\xfd[\x91\x8ab\xf8\x8af\xde\xd2M\xdb/>\x01Ug\xb3\xc9yU\x1f\xbc9\xf1\xa8yVH\x94np\xe0\xd6u'\xe1\x82\x1bkd4\x0e\xa2\x88%b\xbb\x08\x9c<\x9b\x9c\x93m\xc2\xc86 g\xbb\xc8\n/B\x1a{\x00\xbds\xfe\x9cx\xa3\xd1\xf3\x81\xd4\x0c\x1d\x874\xcd`\xe1V\x17\xa6\\\xda\xd5O\xb1\xe6\x90\xce\xb5B\x98\x9a\xf4\xf4\x87\x9b3\xba\x80H\x0d\x8e\xf4\xb7^?a\xe7:`\xb3\x8c\x16\xadgkH\xb8;\x1f\x8c\xe7<9\xa1\xde\xd2\xcd\xeaF\x80E/br \x83~\x81\xfa\x89\x1b\x8d=\xd1x\xb1m\xd3\xc1s\xb3?\xa2\x87Z\xdfQn\xe42\x0f7\x99,\xf1\xfc\xd7\xfb\xd8\x7f\xfb\x96\xcdm_\x82\xaa\x1d\xedkT+7nI\xcd\x1cTC\xb7\xaa\xd0x`\x86#~\xf0\x808r\x06\xc05\x03T\xb2\xe5:)\xcb^G\x19K\xd64\x94\xe9\x83\x8a\xde\xbc\xa9\x13)p\xb3 \xcd\xe1\xf3r*\x82\x14\xfe\x8b\x06\x8bO{4\x0c\x19S\xf5\x83\xa9G\xc6V\xaa\xda\xea2\x13%\x0eI\xa3\x12 \xa2\xc0\xf6\xbf\xdb\x98\xa3\xdc\xaf6\x7f b'\xe1\x0d\xd5c\xb7U\xd5n\xb6\x85r\x86\xc3\x08\x16+20\x99\x91\xad\x0c.\xc1x\x81\x8c\xc8\xa4\x18 ]\x1c\x9d\x9c\xb1\x1c7\xa3\x9ez(\xf9AK\xbc=\xb5.d?\xcb[v\x18F\x15\x87\x1d\xc1Jf\x9c\xbc&UX\xec\xbaH\xef:7\x13[U\xfa\x9e\xe0\xe4\x05\xc9\x9e\x13\xbe\xbd= \xd1\x8c\x9f\x8bI\x98q\x04\x05i\xf5\x9c\xe6\xdcO\xc9\x8c\x9d\xdf\xef\xb6\xb3\x1c{XP\xa4\xbb\x1ec\xa0\x13\x89h\xed\xcd&C\xf2\xdd\x0b\xc9\x1f\x16\x02\xec\x03'Kr\xe6|\xff\xdd\x908/\x1e\xca\xcc\xef\x9d\xf3\xe6\xc1(J;/\x80\xb1\xfc\xde\x01`\xf5\x1b\xf1\xf4=\xdb+a_d\x97\xdc\xbf\xf9\xfeE\x96\xe8b\xc9\xf7/\x1e\xaaDK\x1d^\xd9\xda\xf5\x82\\\xaf\xc2(=\x00\x8eo\xfa\xf0\xe1\xd5\xd5\xd5\xf8jo\xcc\x93\xc5\xc3\xdd\x9d\x9d\x9d\x87\xe9zQ\xb4~\xbdhT5G\xa9x\xe7/\xceT\xf6\xe8\xf0\x85\x1f\xacU\xcb\xe0\xd7y\xf38\xa4 \xa3\n\xfc\xc5\x8a\xc6\n\x1a~!\xd0\x1e\x0f\xa7d\xb6\xdb\x1c\x01\xddi\x8f\x87\x8b\x84\xe7\xba\x9e\xe2\xd56\x1a\xe2 \xd9\x82E\xben\xc4<`\xa1\x9f\xb2L\xd5P\xbe\"%c\x9a\xd0\x95.(1\x8b*\xa6_\x90BY\x82vAM`\xeb\xdc\x11y\xb7\xb0\x90\"wDn\xcacy\xad\x8bdyT\xe5!l\x92\x1e&4\x13\x9a\x84\xe7\xcc9\xcf\xf0\x9c%\xb3\xdcog~#\x08\xa0,0\xad\xbb\xa7,w\xfa\xcc\xf1\x82\xc4\x0b\x81\xc5\xf5\xc2 \xfe@\xb3\xa5\xf8\xed\xb39\xb8n`a\x18\xc4)d/\xc4\x9f`E\xa5\xaf\x07\x08\x80\xa2\xfe\xd3\xe4?\x13\xea\x07,\x02-\xdd\x15M\xc1\x03D\xac\xaaR72\xf0\x93\x877\x0b^\xfc\xd4u\x88\xc244\xebHddJ'\xcd\xb8\xf4\x0d\xc1\xae\xa5\x060\x84;8/(\x1b\xfba6\x07\x0f>\xc4\x1b\x12*\x7f\x99\xc1xk^N:i\x88@\x9c6\\\x9e\"\xf3\xda)\xa2N?p!\xe4\xfcEpV\xd4\x02\x11T\xe8?\xe7/\xa5m\xb5\xf3\"\x0c\xa2\xcf\xe4\xe1\xf7\x0e\x99\x12\xe7\x85\xa3HP\xe7\xfb\x17\x0f\xcb\xdfN\xd9\x95`<\x0f\x12M}\xa9\xe4C\xd9e\xd4\xd3\xed]\x0f\x01T\xc8`Qwoe~q\xe1BO\xeeW\x1f\x9d\xb8\x82(\xe6\x83\x99\x80\xab\n%\xfb\xd0\x0e/\xa2>\xac$Nl\xde\xc1<\xa2S,\xd1p@\xa3\x19\xc9z$=-\x97\xa8\xcfI\x8eK7R5\x85x\x9c\xc1\x86\x02\xa6\n[\xfa\xa4\xce\xbe\xaa0\x83\x0dW>\xb1\xaa\xbe\x9e.\xe3\x0cN\x1e\xd7;+\xe3\x0c\xee=\xae\xc3\xaf\xf1\x15\xa5\xc2\x0c\xee\xd4;\xab\xc2\x0c\xee\xd4 \x91\x1b\xd5\xfc\xfa`\xaa0\x83\x0d\xbb\x8d\x0b)\xb5\xd9{6\x18B\xb8\xc4\x9d\xba\n\xa4\x8a7\xd8\x18\xbe\x13U\xf0\x11\x14\x9c\xf8\xeb\xebB\xa2`r\x0b\xa2\x85\x16{\xf7\xa8\x10\xf9;\xe4l\x19\xa4D\xd0\xf6\x82c%W4%:L,\xb9\xbc!\xff%\xce\xa9H\x9cS\xff5Fn6\xfed\x7f\xd3\x1f(Ka./\xde\xa1'\x83\xb4Z\xfd?36\xbe\xc8\xe8\xe2\\\x1a\xd7(s\xcfl\xac\x97\x85\x1e)\x99jY\x0c\x8a\x1fu&{O\x1dA\x1d\x88\n\x87\xf6\xc1?$\x0e\x81\x0btA\x8f\xa9\x91P\xaa;\x84\xcf \x9c\xda\x96\xb2\xe5\xc0\x8b\xe1\x1a\xc3\x91\x0f\xf6\x89]M\xb4uO6\xfc\xc9\x0eHu\x11\x9b\xd9\xb6\xfa\xce\xc0\xa3\xa4\x15B\x8a\x94\x9fL\x9cA\xa5\x81p\xcf^1\xd158\xf72W\x14\xddu\x86\xb0\xec\x07\xed.M>\xb6x\xdc\x90N\xb6\x133P\xfd\x15\xea!\x19\xf1\x88\xa8m\xa6\xd9\xf8b \xa1!\xda[\xe4\x05\xac\xf2\x07\x0f\xf4\xcfRN#h\xb6\xd7`\x99#a\xa6\xe2W\x87 \xd3\x91\x9b\x0dI\x00>\xb2\x16L\x06\x8e\x85\x88\xc7\x1f\x19\xf5o\xdc\x81v\xa6\xe5\xbe\xc4\xee\x0e\xa0QQ\x9aM \x12\xeb\x99\xa0\xb6v\x16\x97\x9a\xa1:3\xa6\x88\xdf\xe7\xafVKQd\xb6^6\\ \xcd\xc7q^\xc6\xc1\x05\xe7\x92\xa2\xcd\xca\xcfd\xbd\x85*Y\xb7\xa7}i\xbci|l5\x8ey*G\xf0g\xe9\xca\x02\xbe\xd8^\xcd\xa7F5\x97\xb7\xa9\xe6\x1f\x8dj\x16\xdd\xd5\xe8_b5\xbej\x1ca\x19\x8f\x8f.y\x02w\xd3\xe2\x7f\xed\xcc\xcbx|L#i\x0e\xe0x4\x8aCzc\x05)\xfc\xe1h\xc8L&4\x0b\xbc\xcc\xe5|\x1c+\x0f\x85\x8e\xaf\x12<\xcc\xab`\xc6\xe3\x93U\x9c\x05\xe0K\x90\xc9_\x08H\xe4%7q&\x81\xf4o\x0c\xccW >\x9a\x9d$p\xa3\x0e\x91\xfd\x9a\xd9o8\xf5\x99/\xfd\xd6:!\xbc@\xc8\x0f\x0b\xe0[\x96Q\xdf\x04^\xa9\x04\xbc\x80\x8a\x9f\x04\xb0)\x12\xe4\x08\x1c\x96\xe7\xa9\x18\xb0X\xfcG\xb2\xe5L\xe1\xd3$2\x81\x88\x80\xfc Z _$\xa0X\xe6\xc4\xeag\x13\xe8#\xcdX1s \xcd\x98m\xd6N\x19\x03\xf3\x0b'\x85\x1f8\x80lQ*\x7f! \x19\x0d\xa5\xcf\xc9T\xfeB@\xf24\x06I\x8f\x93\xca_M\x90\xb3`\xc5t\xb4$'\x0bV,\xc7B\x1ae<\xfe\x89\x87\xf9\xaa\xec\xdd\x1a^m\xfd\xfb\x99\x06\x99l\xfe\x95\xfce\xd0\x11\x18 \xf6{c\xff^\x8f\xb3\x84z\x9f{\xec\xfd\x1f\x1aeK_\xcb\x82\xe0~\xfdR\x1f\x98{\xf5\x8b\x1a\xb1\xf3\x199 \xea3\xd5\xcc\xc2W\xbe.\xfe\xc8)<\xf4ft\x81\x1du\xd2\xd3{\x00\xba\xfb\xd6 ?\xeap\xc6\xdd\xb5\xcb\xeaMW@\x05>\x06\xb9\xa9/\x86%\xfeA\xba\x1bU\x0e\xdc\xd4\x1e\x01\xb9\x8f\xfc\xcf\x06\x96k\xe0\xcb\x84\xd1\xcf\xcd,\xd9\xb0u\xe03nm6\xcd\xfd\x00\xcb%\xa6\x0c=+]a\xdb\xfbp>$\xaf\x06\xe4U]\x1e\x93\x01\xb1\xd7Vx\x1c\xe7\xe9\xd2E\x86 \x1b\x92W\xb3\xec\\t\xdcB7\xb7v\\j\xac\xdd\xef\x8c\x9cH4Y\xe0\xcb[\xceI\xb0Z|\xf3v\x0d\xc9\xb7\\Us\x9e\xac\xee\xb7\x0b\x1f\x19h\x88\x11'Q?Z\xbap\x9a_\xae\x02)\xb4\xd4\xbfn\xd7\x8d\xc0\x128E\xad \xe9*\xce\x1a\xd7\x8b]g4a\xf4~\xc7\xe1\xb5\n/>\x14\xad\xd3?\x99=$\x01\x82;\x7fj\xe0\xce\x1b\xa0\x9b\xe4\x89\xd0\x87p\xfa\x11\xe5\xfd\xe5%\x07&k\xb8\xa4\xe2\x94Fs\x12<\x1d\xae@\xb0\x0c\xb6\xba\x14\xc7\x1f\x96\xb5\xb4\xd4\x15\xac,\"\x90@\xc6\x14\xc5\xb2>\xb3\x9b\x05\x8b\xf0\xbc0\x88>\xe39\x82\x9e\xc1s\xd4\x1d\n\x96\xa5Ug\xb1<8\x0e\xf1\xac\xab\xcbN\xe1\xcd\xcf\xe84\x89Uf\x95\n\xc5\x89\xad%j5w}\xf3\xff\x80\xff\xbe\xe6WW,\xca\x83\x8c\xad\x90\xf2\xe4\xc7\x9ap\xedW\xd0\xa2\x99\xd1\xd1\xefG\xa3\xbf\x9d\xab\xff\xd3\x8b\xdf\xc6\xbf\x8d~\xf3\xcf\xff\xf2\xe7\x87U\xf0\xbf\"\xb7\x95\xff i\xb5\xd3\x06#B\xfe\x8cJ3\n\xedJ\x1d^\xd0\x199\x03\xf2\xfd\x01\xd9\xa9J0\x02[\xa4\x92\xbfA\xb0\x01\xe4{\xbf\xb4\xc5\xd8\x13|{\x15\x17u\x85\xc4\xf9Oy\x03\xfeW\xf03\xfb\xe5\x0bq\x7f\x05\xf3su\xcf!\x08\x98\xc7\nW\xfeU\xdf\xbd4\xdc\xbc\x16\x04NUFb\x86\x03\xc9\xe8\x824\\C\xea\xcc\x88\xaeX\x1aS\x8f}\xfa\xf8\x9aT\xe3ph\xb9\x94\xbee\xa8e\xc7 [\x07r\x9e\xb9e\x9dRZ[\x1a\xa4\x05,u%\xa99\x17\xb4\xbe\xa5\x9d*\xbcv\xee\xc6\x16\x08\xd5s\x18\x92\xd7Q\x90\x054\xd4t\xbb\xa0%\xe7C\x92\x0c\xc9\xd5@\xfa\xd8o\xfa\xf4\xfb\xda\xe6fP|\xfd\xa4\\\x98\xf0\x8d\xf71\x8b\xce\xe8B\x9a\xdd\x1cE\xfe\x87\xf2\xda*\x85\x0f\xb6,\xf6\xebZ]JA@\xd6\xa5[k\xe9\xa7h\xfe\xd6\xb5@)?\xce\x8a]yN\x0e\xc9\x89X\xdeR\xf3\xebD\xaet\xb2M\xae\xc5/\xb9\xfc\xadKC\x02\xf7@\xe0\x1b\x92\xaf]\x14O\xc7\xc9\xf2\xa68\x82\xe6c\x9ag\x1c\xc2\x88H\xd3\xba\xd6r\xc1x. M\xfe\xe3\x9fr\x14w4\xeb\xd3\xbfSwZ\xa9\" r\x99gY+-\xf7o\xd0\x8dNz\xb3\xa3Q\xff\xe8O\xbc(\x99J\xab\xbeN\x0f\xcc\xd0CCQ+\xd6\xc8\x03l\x83\xb3\xb0\xb8\xd2H\xe0J\x03?\xc7@\xa7\xa7~\x8f\x91t\xc6\x89\x06/\xee\xb3\xa4\xc5T\xcf\x0c)\x11\xd8\xcfP\x0d\xfa\x1ek\x03x\xa7\xfe\xa8N\xa1\x04\xe2\xa2\xd8\x0e\x04\xfdt8\x87\xd5\x8f\x03\xba$\x92\x96\x01\xcb.7P\x7f5&\xc6$6\xdc\xfd\xe3\xebP+\xa2\x08\xa2-\x80x\xf6r\x9a\xe5\xfc\xbe\xe2 \x94H\xdd@-\xa6\x8e\x06\x135\xa29\xc1\xdc\xeccOA'\x9b\xf4\xe4\x9fK,\x0c\xeb\xe8\x90\xbcm\x8e(\xc8\xd4\xc4\x87\xbcz\x9bk~ ]1\xd8\x10(\x01\x85.\xab\x94\xda'\xb9\xd4 \"\xdb\x07\xc4\x01\x15\xa5\xbc}\xc2\xfb\xc6\xcb0\xcc\xc2#\x9f%g\\\xf0\xf9\x81'\xdbA\x0eID\xa6\xfa\xf4\xa9\xd2\x1cf[\x1a\xad\x07\xfa\x03\xf4\x8eZ\x80^\xbfT\x15\x83\xech\xd0\xea\xd3\x1d;\xb5\xfb\xf9s_\x17\xe1Kp\xe2\x80\x93\x16\xb5\xad\xe6J1\xf7\x1c\x1f\x14\x0b\x85\x8f\xa5\xce#\xccRB\xca\x04divP=b\xc1\x7f\x98\x15\x1aYZUL\xd0\x1b\x86\xe2\x98M\x01R?T\xadu\xc0\x0df\x84p]\x83\x9d_)Q\n\x0c\xdc\x89\x1b\xb4\xd1\xc5f \xda\x86\xd3\x12\xbd\xef\xa5\xfcQ\x13\x8aT\xc5[\x18\xff7\x0f\"\xd7qng\xa7O\xca\xa5\xfc\xb3I\xa3 \xce\xf37\x15\x02,\x19{K\x9a\x1ce\xee\x8e\xd8\xbb\x90\xbcM\x1225\xe2^\x10\xeb\xca\xab\xd1\xb7\xbd\xa5\xa6Z\x89\xed~\x97X>\x86\xd3T\x94\x17\x08\xe2\x7f\xc6bs\xa4\x83\x89\xc0\xe8 \x84\x86\x06\x0c\xd8{\x05Z\x1bY\x9c\xd5i\xfbB\x94\xec\xca\xces\x12\x92\x17$\xd5\xb6\x94$\xdc\xde\x1e\xe8fI\x0e6\x19\x92t\x16\x9ew\x912\x8d\xe8\x14\x1e\x0b\x8c\xf0\x14\x9ba1\x8c6i\x0e\x0d\x06e\xdc\xceHv\xb0h\x81\x9b\xc1\xc9\xdf\x8czR7\xe8\xab\x16\xbb\xc5\x16\x00\x19=\xbe\x8c\x82o+\xd7\xefb\x8c\xb8M\xdc\xcb\x15 \x82f\xda\x96%\xb9\x17J\x9a\xdb\xa4\xb3\xbaMh\xe6\x9d\xda\xd4)\xba\xe56\xf1\xacn\x13\x9ay\xa76\xf5\xe0\x03\xb9M\xec\xaa[\x85f\"$\xb3\x9d\x01\x7fW\x14j\x13\xaapE@7`\n,\xa3 \xc4V\x19v\x8b\xf8\xfa-\xde\x95\xda\xd1\x15M\x8c!\xb9\xc6\x83\xe3\xde\x95\x03\xec1\x1f\x97X\x83\xee\xf0\xc9\xcee\xd9\xc1t\xfe\xd4\x8f\xe9\xac\x9f\xfc\xc8\x0co\x80\xade\x8cI\x0b\xcf\x98 >\x00\xf4\x03:\xf3\x08\xc3(Y~4Y\x1f\x7fl\x96 \xe7\x91Yq\x85+\xeb#YN\xed\xecZ;\x1f\x05\xfd\x0cD?\xd3\x01I\xeb\xed\x0e\xa4\xec\x1fX%pU\xf2\xc7\xd7\xc1,8\x07B\xbd\x83\x9d\xb33\x8f\xedW\x8e\x92Z@\xb8`r\x08\x03G L\xad\xdc\xe6\x89`\xcc*\x0c\x1fka\xf8f\xd8A\xecB\x11\xd1\xed9\x90\x81q\xc5dfn\xaa\xd1\xc4\x83M\xd6x\xebZ\x12\xe0\x10\x98\xa6\x87Pb.\xa6\xb0}\xf1\x0dI\xdc\xb5\xa7Hek\xc4\x03\xb2\x15#{\xe3\xcb\x172\x87\xb1\xc0\xf3n\xb5o\xaa_\x9e\x0f\xd0\xca\x1f< \xb1\xa8OL\xc1\\\xfc\xb0\xecR\x91\xd7!\x81\x90\xfbM\x14E\"\xfb\xe9\xa7\xa0\xe0Q\xe9\x94\x98\x1aC85\x07|;\x95k\xa3\xdc\xaa=j\xaf\xc9n\x06\xf6\x9d\x9c\xb2\xacm\x1b\xb7\xdf\x8d\x17\xdf\xdb`\xa3w\xa3`\xdf\xa6|^\x7f\xca\xddrX\xedI\xd1K_u\x81L\xed\xd8\xc5\xdf0\x10k3\x05\x84U\xd4l\x80\x12\xd8\x15\xe3\x98c'\xb2\xf5\xfc\xbd5\xd7]\xb0\xb6\xac\xc2\xda\xb2~\xac\xed\xdd\x99c\nZz-6|\xd6L\xc5\xd1\xe3\xd5\xe6m\x02\x05\xd0\x8f\xbfU\xb5\xa9\xc1\xc6\xf3\x92\x8d/G\x0b/\x16vq\xffx1\xaf\xf25\x03\xbd[\xbc\x07\xcf+\x9f1\xe0\x11\x1aKg\xa5\x05q\xa4B]e\x06\xff\xabIr\x89\xb8#uF{\xa2\xc8\x16 _\x03\xf8\x8c]gJ\xf8\xe8V,>\x03PF(\xe4\x16\xd6\"d\x9b\x04\x03\xe3\x98\xcc\xc9!\xa1P.\xaf\x95SW\x92\x8e\x14\xf2\x1aE\xc2\x1a`\xd1\x81\x10\x0bg]\xdbL\x8a\xffy\x07\x0e\x85\x8b]\x84\xed\x1d%F\xab\x1b\xd5 u\xe6\x91]\x95\x10\xabyC\x9e\xfd\xff\xe9\xe2\x19\x8f\xd6\xf9\x95c\x87[\x01\xd8\x0f\x07iV\xdezvT<\\\xed<'\x11yA\xb2B\xfa\x15mo\x0fH6\x8b\xce\x95\x0e\x87\xcd\xf2\x9c\xf4a\xe7\xda\xf8\xd9\xde<\xe6\xf58\xcdx|\x96P\xefs\x10-\xbaN\xc7\xce6\x81\xc3\x82\xb6&-\x19\xf5\xdboo\xb9\x7f\xd3\xd2\xde\xc4u\x9e6\x1f\xe93\\\xf6\xd9i*C\xea\xa7\x8f&\x8bA6\xe0\x07\xa2\xf4h|\xc7\x03\xf1\xe9\xb3\xba\xcb2\x0e\x86\x87\xa3U:\xea\xf4\xdc]_\xeaj\xeb&n\xe1e\xdd\xe5C\xe2\xac\xd2\x913\xa8\xe3\xda;\xb5\xfb\xe1\xc8\x1d\x0f\x1e.n\xd9\xbe\xb2u\xc9\xb0\x1b\x85kW\xe0\xe3\x8c\x7f\x12\x14$\xe2\x02\xfc\xeb\xbdv\xceF\xa5(\xaa!\x19\x07\xe9\xa7(\xc8B\x96\xa6\xef\xc0\x7f\xd9\xa0k\x1cZ]\x19iQ\x02h@9\x97\x9c\x87\x8cV\\\x17\xcb\x0c\xa5\xc0_z\xe0\xaa\xed\x04\xady\x11\xa4\xef\xe8;7\xab\xa1\x07\xbd2DU \xe80\x9c(s\xc4?\xe5\x83\x07\x84K/\x922\xd2\x05\x99\x82\x08\xbc\x11!\x80HG\xe3`\x96\x99\x04+\xd0\xcf\xca\xc4y\x13_7N\xf7;N\xca\xfe\x0e6)\x0f\xff~\xb7\x8d2\xa8\xec\x94\x11l\x95\xfbl\xf7Cwv4\xfa\xdb\xf9=m\x16g\xf4\xe7\x893\xb08\xc3\xbfCk\xfb\xb5H\xcb\x0b\xfe\xf8\x8a.\xae\xa2 z\xe6\x17\xdb\xb8\xb6\xd8\"y\xf9\x90\xcd\"pq-M\x89\xa5\x14>\x82\xd54\x8b\xec~\x05\xc8m;lpg\x8fw:\xf7\xafej\xbes\xbe#\xdb\xb0\x88\xc8\xb6x\xb9\xe7\x86M\xcc\x86i\x92\xa9\xda\x10q\x08\x87\xecL\xd9\xfcb\xa2l\x8e\xcdE\x97A7\x01?\xa9\xea\xa6\x1b\xdc>\xa4 !(|\xa7B\xda\xff\x07\xf7\xe0[\x13\x84\x9ft\x931\xbb\xce\x12\xeae\xbat\xd9\x1e+s\x8e\xcf\xc2\xbd\x84~\xd9}2\xc0\xec\xe09z\xe8h\x9e\xc1\xb2\xcc\xa3\x19\xabn\xc0s\xcc*=\x9a9?\xb3\xcb\xcfA\x06\xae\xff\x80\x1c\xb9*\xde3\xc8\x7f\xcb\x7f/3W\xf2E\xe6\xac\xd22\xe3\xedi\x99\xfe\xbeL\xe6\x90\xda\xf8jm \x12\xe3`hN3\x8d\x82\x15\xb8\xf8\x02OM\xdcu\x8et\x823$\xe5\xcbI\xe4c|KQ:\xc8\x98\xf4\x14\xd6R\xc7k\x0d\xd3Z\x93\n\xf5g\xad\x05\x9cqa5d\x89\xa0?\xcd\xae\x9c\x15)\xa2\x86\xf2\x0d:S]\x81My\x02\xe6v\xde\\\x0d\xa6k{q\x00\xe6\xfd\x18\xf6\xca\xa0\x8a}\x01Q\x1b\xae\x82\xc8\xe7W\x80\x04\xa5\xa8\x8d\x04csf\xca\x97!i\x02\x14\x83\xdf\x0e\x06#[\xbe\x0e\xaac\x82\xb4\xa5\xa8\xa22\xb4\xc6[o\x9f\xd9\x82\xc6\xa13v^P.\xe2\xe5y\x03d+0a\x90h(\xe2\xe4 \x1aE\x0d\x113\xce)\xa2\\b$5\\D\x91\xbc\xd2.P`\x88\xce\xd1\x8d_qIJ\xee\x8e\x946s\xfc\xdct\xc1,%_\xbb\x93\xba\x0f\xe3\x1c\x97:J\xc7\xcf\x8f\xf6\x8cCE\xbb#~\x86b\xc7\xb0\xdb\xbd\x19h\x13 zY\xc6@5\xeb\xf5\xac\x07\xaa\xe3-\x99\xf7\xf9\x92_\xebHU:,\x1c\xb8\x84\xe7\x95\xd4\xc3R;d\x0c\xc5\x98oj\x8c\x8c!R\x9b\x05\x1d6\xa3)\x98\xaa|\x1b\x88\x95\xe8x\xa1$ nf\x11\xed$\x1a\xecX6\xb2A\x9a\x93\xb2\xff\x98\xcf\x1a\xf1\xc8\xb0\x9aR\xe8f\xb9f\x850\xa8m\x10\x10(\xba\x15\x80^k\x80F\xfeWX\xddx\xe3Tx\x7f\xd5\xbd\xf6o(\xd8\x9fd\xd8\xc16H\x15\x99P\xcfg\xa4\xccFX\xed\x9e*\x90*\xf4P!^\x91\xa7\xdb\xa5\xabJ\xc8!h\xe8[\xaaR\xfd\xc0++\xddc\xd6K\xeb\x9c\xe6\xd0\xb5\x9e6\xa6\xd9\xff\x06\xeb.\x1b\x9b#\xd9\\O\xac\xa7\x8b\x8dj\x9f\xcb1\xca\x8a-uh\xfc\x9e\x96\xdfm\x1d%sR\xcc:aN\xa1F\xf9kJl\xb7\xffU\x8f\x1f]s\xd1M\xcc\x92\xc6m'\xa6\x11\xde.\x9b\x95\xfb\x9d]3/\xcf\xd8{\xf5q7k\xb7mK\xc74\xa5\xb1\x1bv\x1aI\xae\x0b\x85\xf6\x88\xaeZ,\xe4Azh`Ce\xfbk\xe8k\xa2\x14\xbf\xf9\x14G\xa68Xr\xfb=\xd1\x10\xee0\x82\xe7\xc43\xc2\xf7=\x1f@j%\xa9\xdf\xd7\xe6P\xec\x1f9KnNA\xf7\x96'Ga\xe8\xca\x9b\xdb\x99\xe8\xf5\x81\xa0i\xff\xcf\xe9\xfbwc)i\x08\xe67Re\x01D\xd8\xdf\x9d\x83\xda\xcc\x81\xea\xfd\xf9w\x03\xe9\x02`\xe79\x89\xc9\x8b\"\xf4\xd9s\x12oow\x0d\x01Q#\xee\x83\xd6Y\xdc!\xb3$j\xdc\xfdR'\xc3\x1f\xcfy\xb2\x82\x19\x08\xe0g\x9f/\x12\xf5\xd5\xa5\x1ew=\xdeb\xec\xe1\xd2\xb5\x1e;\xcd\xf6,\x95c\xadg\xe0\xe4\xbb\\d\xcbn\xc9*.\xfa\xec\xce\xb5\xe7\xa0\x01\xa8\xf4\xf3u|\x19D>\x1a\x9eO<\x1e\x8f\xb2\x84Ko\xb2\x1e\xa6N\xd0\xaaM]\xa1<\xba\xf0\xc0\xda\xea@\xbfe\xf3Kd\xab\x10`sn\xca\xe3\xe9\xc1\x03\x12\xa0\xdaq\xf8\x06\x13\xdc\xb4\xa3\xaa\x85;\x1b\x88}\x8b\xcc\xbe&\x17\xad\xd5\xe0\xb8\xb1N\x9b4+\xaeZ\x84\xe1x|N\\)'\xe4pG\xa1M\xde\x00{\x0f\xf4\x0f\xc1\x8d\xeeX\xc4\xf2\xc5MD\x11\xd2\xad\xc4Y]\xb8\x1aD\xec4I\xe5]\xa1\xab\xbe6$\x93\x1d\x90\x18\xb5\xdc\xc9\xb8\\\xeai)\x8f1RcK\xb7VbH0\xa9,\xdb/\x91\x0c\xbe\x80e'\xca\xe2\x1a\x1c\xaf\x039\x8b!\xd6\xa3\x16\xf2*x\x03_W\xcfr\xd9\xd4JJ\xf1\xc9&\xa4[\x03E\x01\xb5f\xd9\x81y\xaec\x0d8.\xf3\xca\x8au\xe2\x01\xd9\xda\xaaC\xb6\x926u/\xe8\xdfl\x7f\xda\xb6Fs*\ne\xb1\xd6\x05\xa8\xf4\xab\xa4\xd7\xd66\xed\x1c\xe9\x05\xb6\xc5d\xa5KA\x08\x02\xbd\xb7~\x02\x9a\x06\x1a\x85\xdc\xa3\xed*I+\x1ee\xcbv=\xaa\xae\xaf]1f\xd3n#\x10a\xb5\xdc2C\xe3-\xea\xa0i\xf5\xd32\xaa\xaa\x82>\xdf\x8ej\x0c\xa2~\x9a\xc7\\\xc1\xb0[(3eb*\xdd\x11H \xa99?,\xbbdl\xa2zZ_(\xfc3u\x05\xcd\xe2\xcd\"M\x9dC\xea\xad\x04\x17f5\xce\xe9\xc9\xf1\xc7\x93\xb3\x8b\x97\xef/\xde\xbd?\xbb\xf8ptzzq\xf6\xe3\xeb\xd3\x8b\xf7\x1f/~}\xff\xe9\xe2\xe7\xd7o\xde\\\xfcpr\xf1\xea\xf5\xc7\x93\x97\xce\xed\xbfi\x08K\xeaR\x11\x15o\xb9\x1e\x0d+\xc0\x85\x1f\x94\xe0q\xa0\xf2\xf2^\x0f\x8e\xdf\"\xb3\x90V\xa4\xf6{\x90\xfa\x15\x9c\xe6\xe2\xc7Z\xad\xae\x88K\xc7\x86\x1d\xc8\xaf\x90[\x10\xe9\x9f\xacq\xd3&\xc5 \xe5)Z\xa6\x1f\x92\x8cl\x8b\x92SiN\x01\xd2\xc8\xad\x9d\xba\x9c}0$Y\xb9:*#\x1c\xe2\xee\xd9\xb8\xe9K\xc2\xd0\xa5\x96\x94\x8b2\xf6\xab\x17,d3\x92!\x01\xc4\x03\xea\xd5\xd7\x99[\xbf\xa8 V\xe4\x10\x0c[\xbc\x80\x98=\xb7X@\x08\x90\xc0PDo2\xca\xdbb\xf7OI\xea\x96\xfa\xef\x03\xf9\xd1\xad\xc9\xb0\x16\xe0\xb7]7\xa9\xe0\xc6\x0c{\xf4\xa4b\x8fn-J4\xf7 .\x0ef\xe1\xb9\xe4~\xfa0>rEv\xb36\x80\xda[\xa1,\x8a\x1b\xa5Y\x90l\x9dl\xda\xed\xe5\"r\xbd\x08\xa6$\xefX\x04\xdf\x96\xe8\xb1s\x1c\x06!\x19X\xe8\x9f\x8a\x037\xd7\x01xg\xa8K\xb6\xd2n\xb7\x14\x87&\x16\xf9e9\x9cm\"\xbf2l[\x8b\x14\x12\xa1\xeaJ\x99oU$\xa7\xbf\xaaN\xcc\xe2\xd5\x0ei\xe1\xbf\xc0\xe7\xa3\xb9\xf7\xec\x02\\\xf5-\xaft5\xcd+\xd7r\xa4\xcf!-U\xee\xeez`nt\xbb\xd0\xbcE\xa0\xf8A\x9aoz\x8b\x90\xf6\xbaE\x08;n\x11\xf4/\xfc\xb8\xdap\xb9j\x81E\xc9\xff\xd8\xad\x9e\x12\xd7y6q \x82\xfe\x1fmRp%\xaf\xbe\x1f\xe1w\xb9\x13\x1c\x159nC\xa1\xf7\xbf\x8b\x9c:\xe8\xbe\x1f\xb1\x9c\xf8\xa6fT+\xc5@\x1b\xe2p\xbb\x187$\x07\x9d\x0ed*\x96QnE\xd7V\xac\x85]\xb1\x16\xaa'n(\xc5 \xa1:F\xc9\x8b\x032\xd1\xf2\xb9=G\xf9~ g;\xe7\x03\xe9\xdc\x16\xe644\xb8r\xa9\xc8K5\xd7\x00\xc2\x9b\xe6\xfc4R\xfa\x1efUq\xbc\x94S\xfc_&w\x0f6\x95\xbb\xab-\x9eK\xc9hZ8m\xec\x10Rv\x8c\xfa\xbfD\xfcH7\x92\xfc%\xf5]\xd7E\x92v\x10\xe3\x92\x9e\xc2\x07Z\xda(F%%\xe2\x96\xfc5\xafH\x9d\x1ar\xab\xa8.\xb7B\xa4o\xcd\x15o\x17\x995+\xac\xc9\xc0\xda\xe6\xf1\xb6D\xdbf3#E\xc9Yi\xc1\x89P2\xea\x82\xdb\x8e\xee\xa1\xafY)\xc5\xd8\x90\xfd\xff\x96\x94\xc5\xee.f\xcf\xe4\n\xf8]\x19\xe4X\xda\xf2l\xaeg\xa3A\x9f*v\xc3\xa85\xfd\x90\xf0\xa1\x9dQ\x04Y\xbfv\x90\xd6\xd6\xec\x14\x1cGgC8;i\xdd`\x99\x0dE-\xc5\xe7\xa4\x06\xa9\xbd\x86\xf28B\x17V\xc7\xaa\xe0bU\xd0\x86\x05q\x04\x12T\xd8\x0fQ}M\xf0\"\x9a\xf6d\xdffg\xa5\x95\xbeg\xaer+h_DR\x1d\xca9;\xf9\xe5\xec\xe2\xf8\xfd\xbb\xb3\x93wg\x16G\xacD]1\xc3\xd0X\xa2 \x8bg\x0e\x07\xb8\xcf\xae\xbb\xbcR\xce\xd5M}\x17\\\xc6{UG\xe7\x19K\xca\xfaP\xb8\xaf\x03\xcc\x1d\xa4m14\xdd\xd8\xfe\x8f_\x07\xa7'g\x17o\x8f>\xfe\xf5\xd3\x87\xff\xb7\nH\xdeq\x1c\xdbVCf\xf8\x16\xbc\x1dIp\xdb/\xd7\xcf\xc9\xea\"\xb4\x8f\x1aG\x14\xb5\xcd\x87v\x9c\x809r6W\x89\x19Wz0\xa5\x92\xa0\xb0\x9f\xcf\xe2\x1c\x84\xab\x97V\xe7wp\x0c\x0d\x0b\x973\xed'\x1f(6\xb5\x83\xf8\xdd \xcbn\x90\xb5\xf5\xe6B?\xb0\xe1=\xa9*\xddZ\x15\x0cC}\xcb{\x9d\xe4\x00Qc\xb3\"\xeav3\x99y=\xe8\x02\xf1,\x04E8\xf3z\xa8oIU\xad\x059$\xee\x1c\xa4\xb9su\xe4\x97\xc1cVC\xb2\x1eB$\x9e\xc1@\x86\xe3yK\xb3\xe5xE\xaf\xdd\x95y\xc0\x0b\x80!Y\xd5\xce\xfc\x18|\xf1\xad\x80\xb1h/\xabB:\x95M\xb8(\x11\xe8\x91\x04s\x17CBg\xcbs\xdd\xa2L\xd9B-\xb7\xb7\x07C\x12\x0b\xf2b\xad\xf9|\xed\x81\xc7E\x9c\x7f\x98\x8f]\x7f\xab\x9c`>h\x1a\x03zR\xbaUk\xb2\x89\xf5]\x980\xc2g\xde\xf9\xa0\xcdm>\xf8?\xd2\xe8}^\xfa\x0fi\xd2\xb5\xcdK\x17\x82\xf6\x00\xc3\x7f\x91\x95\\o=\x087<\x05\x9b\xe7^f\xfah\xb5\x84\x9c\xec\xd3\x81bA\xf6vLF\n7\x05\xe6\x92|!\x80\xeb\x96y\x1d\xa8\x98\x94\xf4g\xfb\x9eU'\xef\xdb\xf7?\x9d\\\x9c\xfc\xf2\xfa\xf4\xec\xf5\xbb\xffl9|\x89y\x00w#?\xe3\x1c\xae\xf4\xa9\xbb\x94{\xcd\xae\x11\xaf\xac\xc7E\n\xb1L\xed}\xcd\xeb\xc7\x13\xd8\xc3\xef\xde\xbf<\xe9;\xab\xdd\xe3\x7f\xd7\xfd\xdbB\xa2\x93\xfeT5\xe9IY\x93\x8em\xdbkV\x9bg\xf8-$a\x85\xc5w\x95\xb4H\xd4\xa9b\xe0\x05Qe\xd4\xbbm\xe6Q\xd5s\xcd\xe9\x0b<\xf8\xb0\x19b\x8f\xe1w\xf0\xc4\xde\xfcH\xbaBl\xb6\xf4O\xf8\x9bEt\xedA\xea\xadD\xd7\xa5\x9b'\xd4\xd6W\xb9\x17\xa8\xfb\xe1 \x86\xa7\xae\xfa-8)\xa5\xdb\xbb\xbb{ \x97\xde\xdd\xdd\xad\x0b\xb4\x89\xa1x\xb6_\x1b\xb4\xdau91\x85\xccy\xc7\x81\xbfV\xb6\x1b\x86\x17&\xd60Z$\xe6} \xa8\x89H\xa1\xb7\xb4\xb3\xe7\x82^i*\x89U\xc7FV\xbfu\xa0*x\x0fN \x11\x15\x0f\x81=.N\xde\xfd4%N\x9cp?\x87^ \xe8\xe4\xe7\x93\x1f>\x1c\x1d\xff\xf5\xe2\xf5\xbb7\xaf\xdf\x9d\\\x9c\x9e\xfd\xfa\xe6\xe4tJ\xb6&\xd5F\xd4FJ\x8b\x0b\x9b\xdfE\xa4\xd8\x1b\x13M\xfa\x8e\x8a\x0dL\xb5\x80v\xb9j\xdd0\\?Z\xbc.>\x9d\xcb@\x01\x1b\x88\xf1\xda\xba@\xa1\xc2\x14\xa2U{\xe0k\xd7\xde#\xf0\xe9\xd1y#+\xf8\x9c\x0e\x9e/n\xf1\xbd\xa4\x1f\xd4\xba6\xee\xcd\xf3 \x06\x15\xd8%\xb8\xd8b\xb3\xf8\x1c\xb8\x0d\xbf~G\xda\x8f\x1d\\\x83\xf5n_k\x1e\xbd9@?(p\x97C\xb2\x1e\x0cH2\xae\x07Sq}`\xc3\xf2!\xf8b\xca\xa4\x1f\xa2\x96\xb1\xd3O\x0f\xbfJ\xfa\x91*JTV\x9dT\xa8W\x1f\xdc.\xd4\xbd\xa2\x8a6mM\xfa\xc4(#\x06w\xcd\xdd5l\xfa~\xa5TOW\xfd\xa0\xc57\x16\xd0\xfaZKW\xf5\xa5\xdb\xaf\xbeH\x8a\xcf;\x98Z\xd2\xca\xd8\xb6\xe7\x96k\x9c\x0d\xc8V\xc3\xc7[\x0cV&\x80\xf8\x90\x05.\xcd\xf5\xc1[[|.\x98\xf5\x8d\xa7\x0em\xd7]Y\xdc\x96\x13\xbdj(o\xf1vG\x88\xc5\xe3]\xd4\xb9\xa55r\xc4O\"\xf3A\xc6\x84\xa3\xb4\x8c~\x90Q\xa9\xa4\xd4\xd0\xb1I5\x94\x17|_\x07\xca\xb5\x8c8\xac\x1f?V\x13p+z\xa2\xf3*\xdc\xa2d\xd7PV\xa7\x96\x8bs\xa5dW\xf7\x89\x99*U\xbd\xba#\x80P\xb5\xa5\x9e\xeeU|h\xee=y\\'P\xe68\xe5\x13\xcb\xfa\x1a>9}Y\xdf\xbe\xa2w&\xf5\xea\x96\xaa;\xf5v\xacK%\xfbzO\x05Z\xaa9\xce\x14Xd\x17\xbb\xd2\x07\xc7T\x7f`\xb7\xf2\x97\xe8\xca/\x15H\xcb\xe5rS:\x7fU\xd1 M\xdf\x15\x18u\xc8\xc8\x01 \xc5\xbe\x96:\x89xX\xe8\xc6\x02\x85\xbb\x0b\xe9\x94Z\xaa\xf7(\x12^*\x97Wbf\xd5c\x0d(*B\xf5\xa9\xa2\xb5_]\x82\x17\xcd\xb1\xbbB\xe9$\x8fGi\x96\xe4^\xaf\xebALM\xcb\x88\xf3eq\xf7\xeb\x89\xad\x9c\x06\x19;\xbb\x89YA\xf4\xcb\xbc@i\xc6\xd4\x92\x8d\xd0\x8f\xcd\x8c\xca%l-_\x0e\xdb\x0f4\xf3\x96\xd2\xffZ-?f\x91\x1fD\x8b\xb2\xedH&h\xd6\x80\x03#<\xff\xa3\xf4\xb9\xa5\x15\xeb\xb6&\xb5\xfcW<\xf1\x98\xbc-\xa8dk\xc1\x9f\x18!d(\n\xb9\xa0\xc6|\xb5|\xb5>j\xa9\x80,\xdf'r\xb1\x16C\x9e)\xafOJi \xef\xc71\x0d\xc3K\xea}N\xeb\x1f\xa2ah4\xe3\xe7 \x0c?I\xa4\x0c\xddi\xac\x0c\xabZD[\xe46\xab%z\xbd\xb3\x1c\xed\xe9\xc5\xf66\xbaV\xb2\xd6\x85b'\xdd\xe9\xd0\xb8\xf3\xe9\xaf\x83G\x14\xe6U\xe3\xaa\x14}\n+\x11{!\xcf\xf61\x1ce\xe8g\x0eJ\x82\x0b\x96\xc9\xe5%\xbdl\xb5|\xc6o\xf5\xbeS\x7f\x14v\xd9r\xb7X\x89\n\xc1\xfa\xd8x\x1f\x07)\x04\xbe*f\xb7\xe5lv\xbd\x96\xb6-\xcb!\xd08\xa8B\x08I\xca\xd0F\x13\xfafD\x86%1LV\x97\x1ay\x1f\xf6\xf2eF6\xe8\xf8\x87\x9d\xe9\xb3tl\xb2\xeb\xb6N\x05\xd2\xb8!\x91\x1e\x06b\x1eD\x99-\xa0\x07\xee\xaa^?E\xd4Vl\xa5V\x9b\x83#f\xed\xda>o=\x0e\xc6 \x97\xa4\x91K\x07u\x1c\x86\xee=7o\xd9\xf9\xa0\x96]\xadC#\xa7\n\xdd\xf0\xc1(Y/`2\ne\xaa\xc2\xc2\x83\x016\xbeV\xba\xb2\xc9bo\xed\x808\xa2\xd2\xeb;\x0fu\xdbZ\x0dn\xb9\x1ao\xb5\xf8\x8aq\xd6\xe3f\xa7IZ4_\x83\x12\x83 \x8a\xb8@|.\x96\xe1v,\x87\xa0\xc7\n\x08\xf4\xa4\x07\xe5<\x0f\x86\x15\xc1~\xa1\xaan\xce4\x90\x0543&\xdc\xb5 \x03\xd7\xca\xe5\xbd'\x90\xb78\xecQ\xcf\x18\xa4\xa1flp0H0,b\x08\xe6\xcd\x81\x07a|\x95|\x02i8\xdc\"x\xe3\x93\xb7\x1f\xce~m\xbf>\xb2,hI\x85\xcc\x11\x15\xdeD/\x92*\x81\xbe\x0cB\xdf\xa0\xd2\xb1(\xde\xc8z\xec\x1f\xd2\x8a\x187\xb3\x15\xb1\x9f\xa5\x03\xbd>\xbfi\xf4+\xa2E\xf0\x96ov\\\x02d\x8dmc\x97\xdcII\xbf\x87q\x8c\x0f\x1e\x90\xad\xac\x8d\xa7\xecs\x87\xd0\xc1\x92\xee\x0c\xdb\xef4\xf4S\xb9\xb8, \xbam\xe2\xa0mw\x07\x1d\x01\x05\x08\xe8w\x07\xd1\x9a\x7ff\xff\x99\xd3\xc4g\xbe\xe6\xa9A\x05\x00\xadU\x9a\x93e-!E )\xac\xd6\xf1*\xda\x82a\xd9\xb6\x08\xe8i51\xbf\x05\x1c\xd3W\xba\xa5\xd8\xa2&\xe1\xf9\xf6\x14r%\xdb&\xe3h\x95\x03\xe1\x92\x16\\\xb8e\x93\xb4\x84:p\x99\x8dE\xec\xb3\xe5/V4\xfd\xac\x10U\x9f\xed\xben3\xa7\x04\x1eVuM\xcc\xa3%\xec\x07\xf8\xdb-C \xc4v\xfc\x8e\xf9\xc1\xd6O5~N6 \xd1,9o\x0d`c\xf5\x14\x87\x8dKU\xd2\xb2\xf9\xd0\x18\xe3j=\xf2\xf4\x99\xb3Q\x83\x8c\x93\xa5w\xabL=\xfb\x8d\xa4AM\xca\xc6>\xa5\x81t3[6\x8f\xe8\xe8\x0c\x8d\x1c\x19\xa8\xa1\x0d\xa1VC\xf0 \\\xb5\xf2rpl\xac\xb6\x82\xa5~\xba9K=\x90\x1f\xc2j\xd5B\x8f\xfd\xcdj\x15g\xbe\x1d\x89\x96.w\xbf\x02\xdf\xdb{\x0f\x13\x83\x1d\xeb\xb5n\x80`7;\xd4_\xab\x0f\xf3\x81\xd1H\xaa_X\xf7\xaf~]Q\xbd\xef{\xe5\xceM\xa1\x9e\xe8T\x1b9\xd9\x86\x84\x95\xdeCyP\x011\xc7@I\xaa\x9f\xaa\xa4b\x1f\xe4\xd9\xf0z\xfe\x8e\x89\x0dJ\x93\x9b>\xfb\xb2P\x8e\xc1\xdayH\xe6ME\x80\xcc\xb0\x14\xab\xc2\x0f\xcb\xfb\x11M\xc7\x97\xce\xa8\x0f\xac\xa7\xe1\x97/\xf6\x83\xee\x10\x1f\xa3\xf2;\xd5\xd9jO\xad\\;\x99M\x94 \xb6\x1b\x95>SPk z\x0f\xd0a\xfdI{\xe2\xb8\xc8\xf4\x97 0\xc2\xde\xa6\xa2\xbb\x16\x16i\x08\xbc\xcc\xd6\xa4m1\x17D\xc3\x81\x0c\xd2\x9b\x83\x11\xb8N\x9dJ\xd7[jF\xab\xf7\x04\xc1@\xd5o\xd3\xbeX+\xc7&\x9dW\x11\x10\xe2\xd8\xe6\x1d\x88\xc0\xd5#X\xe5\x03\xeeW\x9f\x1cJ\x17\x98\xb4Ji~\x94\xeb\x1b\xbc\xa6td\xbb\x9e=\xa6\xd9Z\x07\xfe7\xfb]\xe1r\xa1\xb0\xbdGq\x8bw(\xeb\xf6\x80\xf8h\xe3t\xc9\xf3\xb0$K\x8b\xad\x13\xc3\xc4\xa0\xb9\xa25\xf3\xa1\x8c\x82\xacg\xb5\"\n?8 \xd2\x8c\x03\xda\xe5\xbb\xe1\x90x\xb0\xac\xb6|\xf1E\xd1\xa3!\x99\x03\x9f\xde\xbe{\x86$&\x87\x9a7\xeb$e\x01\x91\xd5\xdb\x1aI\x9d\x19\xb8(ab\x17\x81\x95 \xb6\xd5\xc57\x9b\xb4m0$\xb4\x10\xea{\xe2E\xcb$\xe6Cc\xe5\x1e`\xa6=-$\x909\xbb=\xd5O*|Y\x0f)My,5\xd0f\x1fb \xe1,\xect\x93\xb5\x08\xc6m \xcc\xccVii\x11\xb5]dHGo\x0f\x1e\x90\x89r\xa4+\x1d\xc6\x14\x85\x93\xd9\x8e\x85p6\x88\xb1\x03E\xb2\x08\xfc#\n\x88sF~T\xb9\x84\x13\x19\x132%;\xcfI^\xf1\xee\x96\xb7\xfb\xc5^\x1bf\xd9v\xb2\x89\xbbtH\x1c=\xe5\xa6'\xc2\x94\x1c\x92T\xea\xd8H\x8dE\xb9\x1c\xa6$\xbd\x05e\x85\xf8\xbf\xc1\x96#\xbakn\xa1y\xad\xaf\x87\x87\xda\x13A\xdfe*\xb0\xf1\x0f2d\x9b\x1bV\xee?d[,8\xd3#\xda\xe3O\xa8%\x809\xbc(\xf4\x02\xbe:\n\x91\xe0\x90\x845\x19\x81D \xe07\x0b\xc9(\xee\x03p\xaa\xc0\xd4\xe6\xa8\xa0\x8a\xb0@\x15\xd9P\xb7E\xe2\x95\xd0@\x15I\x15\xef}\xac\xcb\x06\\\x18\xe8\xa1\xec#o\xbf2\xc2\x86L\nO\xc2B\xe9Ut\xbf\x1fv\xb24\xe8V\x18\xaa).iEU\xd1m\xc8g\xbb,\xb7\x1d\xc5\xd9\xa4\xd7s\xe2.]\x10\x95\x0f0\xf2URb\xacMP\x9a\xd9\xa4\xc8\x1d\xca\xac\x1a5U%\xa16{Y\xf1 r\xaah\x88\xbb@\xd7OS\x92\x8d\xb9\xdb\xd6Ou\x1a\xbb\xa5\xd9d\x03\x896\xef'\xd1&-\xb2\xba\xd6\x90\xac\x9a\x18\xc4\xc4\xdd\xc5\xfc\x95:1fJ\xcd{E\xdbT\x8bm\xda\xddp8\x0d\xc5\xf0\xfd\x1cdK\xe9]@\x1c\x01!\xca\xa2\x91\xdeR/\xb4\xe2\xfe\x9c+\x1d\xe3-c\x1b\xd8\xd9Y\xf7\x9fy\xb9\xfb>i\x8az\xda0\x08\xeb\xc9\xcb\x14\xc62\xb2\x11\xee\xddZ\xdc\xb7q]4P\x95\x14\x16+|\xd1F2\xe4c\x85\xf4T\xa7[VS\xeb\x95\xafx\xba\xaf\xb8\xd0iA\x06N?_\xc9<\x88h\x18v}\xd9\xec\x05\xca\xf5\xea\xa7\xd5\xf9\xec\xad\xdb\xdf.*\xd5\xdaA\xcc\xd0\x0eb\xa8v\x10+\xb5\x83\x9em\xc8\x16\x0f\xfbI\xb2h\x96Qo\xf9\x91\xcdos\xa2.X\xf6!\xbf\x0c\x03\xafp\x94f\xe9\xb9\xe6\xf2#\xcd\xe5Ov\xda\x18w\x194\xa7w\xedn\xa4\x14\x99\x0e\x0e\x80=\xd3\xaf\xe4\x8f\xaf@I\x8b\xb7\x81\x0c\x04\xd7\xcbv\xc7g\xc8\x98\xd8\x06D\x05\xd5\xb3\x8d\x07||\xc6\xce\xfb|W\xcdl\xdf\x8d\x7f;\xe1s\xf3~\x10\xcc!*)\xe3B9\x86[\xdcQ\x15\xa8\xae\xa6\xae\xa6l+j\xa9\xacPbS\xf9\xfa\xb5\xaf@\xaa1\xb0\x1b\x8fQ/\xcc\x8d!L\xedc\x02\x96\xf0\xb4\xdf\xa6\xb2\x93\x19\x88\xcd\xaa\xc56R*X\xdd\xc9\x96a\x82\xd7l\x1d9\xcd\xb2no\x17\xc9_\xef\xde\n\x94\xb1<\xbdY]rp\xc7*\x7f\x8d\x057\\ys\x9dD\x8c\xdc\x98\xc9U\xed\x00\xba{\xb23\xd9\xd9\xc3{\x95\xfc\xb3Z*\xa3s\xf2\xa4:\xed\xe0W\xf3\x7f\xffo\x9dy\xeb8\xcc*\x04\x0c\xa8\xe6\xcd\x92s\xd8=3~^\xc3|\xe0\xb3\x1dkmy\x01X\x0f\x0cp\xab\x91i\xb1\xb2\x95V\xb2\xcf\x1b\x9d\x90F4\x9b\x19\xc7\xf2\x0e%;0_\x12CR\\Y\x19\xc1\x12\xda\xf6?\x18/\xb53^\x86^\x0e\xb7\x9a9\xed\x0c\xa5\xa9md\x1a\xdf\xba\\\xda\xddvG\xb8\xaa\x0e\xd2\xbf\xca\x04\xd7\x16\xdc\xd5r\xda\xe3\x96\xb4\x08\x02m\xbbS\xd6(\xc5\xd57@-\x8e\xd3\xbf\x891\x17\x1eb\xe4I\xdd3\xba\x0e1\xf2\x14\xb1\xe6*\xcd\xad\xf6'\x0d\x07\xa79x\xa4\xaa~\xbai\xd9\xacd#\xd5S\xabb\x1e_\xfc.6E\xd8D\x12p>%L9\x8f\x0d~g\x10\xef\x97\xaa\x1a\x87:_\x90\xaag\xfc4\xa3Y\xe0I\x1e\xca\x10\x0f\xe5);6\xa3\x19\x9b\xf2\xd0\xbc\xb4NP\xea\xe5\xb4\xd5k{\xd3\xdd\xa9\xe0\xe2\xcb6)\xe5\x8a\xb4\xe3\xb4V\x8b\xa4\xea!\xa8v\xac6EN\xfd*M;*5\x0c2\xfaUX\x1f\xa8\xb6\xfa}\xa6\xa9\xa8\xda\xccW\xc1J\xed\xcfV0\xad\xe6\xd9\xb2\x8a\nP7,\x0d \xc03\xaa7\x18\x12>\xa6\xbe\xff\x81\xf30\x88\x16g\xdc\x0dk\x18\xe1^\x1c \xef\xee>2\x10\xbfD\xfa&\x14o#@\x8a\xb5\xcf\x9a\xe7\x0d\xa9\xc5\xb8o\xe1Q@\x15\xc6eD\xd3|p.\x0eH\xb6L\xf8\x15\xacjA\xd8I\xfd_\xe7\x98F\x11\xcf\x88\xc0<\x84\x12/\xa4iJhJh\xf1%\x07\xc1\xee\xea\xd6\xb8\xd0\xb5\xca\xca%/\xce\x83\xea\x92\xa8\xce\xa1\xa6\x9bM\xf3\x14X\xd3\xac\xdb\xe6G\x9b\xbb\xd4\x10\xfb\xb0R\x9dB5Z\x81\xaa\x8e\xe9-\xf2\x97z7\xc6A\xfa:\xaa`\x17\xe0\xdc\xea\xb5\xe3\xb2\x19\xbcE\xd5k\xb2\xf6\x9en\xd8\x1c\xa3\xea\xba\xc3w\xbc-\xb5\x0b\xa1\xceU\xb5a{\xcc\xea\xdd\xa6\x1e\n\xde\xa6S\x96}\xab\xf6\xe8\xaa-m)1\x88\xc9a\x9b\xa8\x81\xdf\x07j\xb0\x9c\xc5\xfb\xb6\xb3\x189\x8a{\xac\x1a\xe4\x0e\xb5f\x87\xfa\x8e\xfbu\xa5\xc5[\xdb\xad\xfa|%\xf5\n\xab\x83jbbjb\xe2j\xa3\xbb\xcd-\xad\xbeb\xa8\xbc\xa0\x08\xfcc@\x1e\xc9\xf6v\x93\xf8\xaa6\x91\xa2\x9d\xdd\xd4\xf0R\x0b\xec\x1d\x02\xec\xd9\x88\xad\xe2\xecfJ B\xa5\xf1\xb9m\xe2\x10D\x0bW\xfa!\xa8\x93 m\x14|*\xfb\xc9\xaf\"\x96\xbc\xe4^\x0e\x12\x0e\xe55\x89\xaf@HfSb\xd06\x0b\xe38a\x1e\xf5\x96\xacP\xe5\x967P\xdcEn1\x9b\xf2\xc0\x9aT\xb7FX\x1d\xca0^\xceo\xd7{\xde\xd6h$\xc6!\x17\xbd\x1f\x8d~\xbb\xdecNm\xaf\xd5\xce\x02\xab\x8eW\xf3\xf0\xef\xaf\xc4^t\xdb\x1a\x04\xba\xadQ-\xda\xea(\x930\xce\xa3\xea\xd8\xd6j/qK\x8d\xda\xa0\xf7\x82R&\x15b\x03\x0f\x1b\xc0Q4\xea\x14\xb8\xc0\x01\xe7\x19J\xd0\xba\x07\xd1]j\x99\x99\x91Y]k\x86\x07\x0eP.\x06\x86\xf39\xe1\xcfI3\x80\x1d\x89\xea\x9b\xb4\x12\xb5{G\x1a\x03e\xcf }\x0e\xbfh\xb5t\x80\x96~N\"2\"\x01\xf9\x9e\xec<\x1f\x80\xbc\x8bU\xaf\x91\xa2\xd1\x08-\x16\x90\x11\x89T1@\x04\xd5b\x01ZL\xef\xfe\xe89\xc9G\xa3\xe7v^\x1dB\x02\xb71\x8dHK\x1b\xad\xb0\xac$R\x15\xa5\xff\xa9 a\xae\xb3j\x0b\x83\xf4(\xf2XZ\xa5\xc8m\xa7\xacm\x89$\xc9lr\xbe\x89\x96W\xdb\xdc\xf5gIk\xea\n\x06\xea\xb5\x88\x08\xda8\x07i\xe8\x88\xec\x0e\xbcS\x05\xd1\x01*\xf1v\xa6x\x1c\xb1\xeb\xec4\xb8\x0c\x83h\xf1\xdcJ\xa7\x93\xda\xc5X\xa6\x14Z\x9e\x14\xd6q\x12\xe9\x0e\x86d_2A\xe3H\xab)>x@j\xf8\xcc\x80\x90\x11\x0d[\xbeJ\xcaE\\\xc7 \x16c-\xfd\xb4G\xe0\xb6;\xd3\x94\x04\x981,=\x17\x8d\x9e:A\xe1U\x0fx\x1c\xab\x9d[\xcedVWa\xba\x9b\xa8\xe2vD\x81\xc0\xd0\xb7\x15q\xdc\xcb\x85\x8aEj\xfa\x08'\x07\xf1\x1bL\x19h\xb1:x\x16\xef\xcb\xfafqJh\xf3\xb0\x15\x83\xd7\xb5\xd7 (\x02\x07)\xd8\xce\x04\xd1B\x85M\xb4\xb8\xa0k\x9b_Qfv\xdb6\xf2\xf1<\xcc\xd3%\xb4\x82)-\xf4T\xaa\xa1\xf3\x86\x04Gv%+\xbb!e0\xc9`\x08\x85A\x17m\xee\xd6<\x91}%W\xcb d\xc4\xadKT\x8cX\x82 \x97\xe1\xe4E\xa5n-b\xe1 \xa1\x81\xc5Qd\xce\xf8\xf9\x90,\xc7\xcaC\xd7\x99\x9a\x03\x97U\xa6C:\xb53\x87j\xd8\x18;\x1c\x17\xc7v.\xde\xa6\xa9\xd1\x18&lu\x18$Du\x81\x18\x19\xf5\x01h\xde\x19\x96M\x06n\xb1\xa2\xaa!\xf8\xc5qv\xc5\x8f\x92\x05\xf0\xb5\"\xa7\xe2dx\xad\x1c\xefW\x1b|\xc1\"z\x192\x7f*0d5\xa7:\xc4X\xdc\x95\x9f_\xbf{\xf9\xfe\xe7\x8b\x1f\x8f\xde\xbd|s2%\xc1\xd8\xa3\xd1\xa7\x94\xbd|\xff\x96\x1c\x92\xab \xf2\xf9\x15\xc1\xca\xa5,\xfb\xb1Vy\xbb\xe4\xa81\xe1bQT\xc7\xa6\xf1\x85\x13\xdd\xb1\xce\xaa\xd5\x10\x88Sb\xab\xb5\xd6 mV\xdar\xfc\x96U\xb7U\x9a%4\xfeAJ\x1faQ\xf4\x13V\xeb\xdb\x0drH\xf8X\x06\xf0W\xb1\x89\x96\xa0Z-\x0e@\xa8N\x124r\x99\xb1\x81\x16\xd7v5\xe8X\x892o\xdb\"%\n\xbd\xaf&\xadx\x14d<9\xf5\x12\x1e\xca\x88\xe8]\xd3\xaaQf;\x94x\x98\xeb\xb9r\xad\"\x8e\x9b\xbeV\xdb\xda$<\x8a\xc1\x97U\x0c\x89\x93B#\x1dD\x8d\xa2\x8aN\xcc\x11\xe9)\xd3(\x17T\x1b\xd1$0f\x0c\x86\x06\x02\x05\xb4\xc6\xeei\xb7\xcfI\xc7U\"\xce\xf5\xedr\x81\x1eF7\xf18a!\xa3)so+\\(\xde,$\xd7\x12RoEr\xf5S\xc1.\xc4`?K\xe4\x067\x1d\x86\x0eY\x91q\x88\x8c\x03\xc4\xc5\x8a\xe9\x82\xfd\xf2~>O\x99\x0c\xd82\xf6\xb5\xc6\x82\xfe\xa1m4\xe4:z\xc3\xe6\x88\x00\xf5FW\xf5\xeb\x06U\x9d\xf1\xaaX\xf0+\xc1\x82\xceC+;\xbfm\xa9\xf1O\xd5_\xb7\x9a\x89\x92\xf8\xdd\xaf3\xaa\xea\x9acb!~\x1b\xd7\"\xed\x81\x16\xf6\x9e\xe0\x91\x16&\x8f\xeb\xf5\x84\n\xbe\xde\x1e\x0f\xa7\x97q\xbe\xc9\x10B\xd0q\x10\xfd7\x83qi\x8e\xef\xcb\xf7ou\xfc\x8d)I\xda OVqvcT\x9b\xb7\x02\x0b<\xf3!\xcc\x17A\xf4c~)\xb8\xdf~\xc0\x9f\xb2 L\xc5\xd9\xde\x05~\xb2\n\xb2\x8c%S\xf0\x9bg\x05\xfd\x11t\x88\x8a&\x87m\xb0\x05\xef\xe8\x95P\xd5\xf5\xf6/\xe0\xbc\x1e\xd7\x99\xa6\x00g\xb1\xa8e-\xa9\xb5\xf7\xb4\x9e\x9eV\xd4\xc8'\x8f\x9e\xd6\xd5\xc8\x15\x17\xb6[\xff\xbe\xd7-\x03\x01\x8e\xe0\x94\x85r\x08_G\x82\xd9\xa5\xf8\x98+\xd9H>N\x80\x16eE\xa9\xea\xc0c\xf1\xb9\xcd/v\xca\x7f\xb4\xbc\x97\x8e\x0b\xa2\xaa\xc3&\x92\x8eK\xa2\xce\x85X\xe3\xbd\x0c\xad\xea\x02)+\x1dP\xa9\x1f \x94S\x17D\xddu\x04\x94\xa4\xa8\xa2\xb0.F\x9da\xc6\xad=:\xb6\xd1w\"\x9e\x05\xf3\x9b\xa30\xc4\xbeU\xed(*\xf8B\x98\xfbv\xc9W\xbb\xe5Aa^Pk'\xa8Q\x94\x94Ldx\x99D\x8c\x14\x0c-\xd5\xca\x86\x8e\xef\xd5\x06\xc1\xab\xad\x83z\xc5\xb7\xb2A\xc0:\xdf\xf1\x9d\x8d\xcd\x12Z)l\x9b\x81\xc1&\x0d\xae\xf8\xa8n\xfb\x18b\xa6`W\x18hl\x11\xed\xca\xba\xa1\xc6]y\xed\xcd\xae\xf3\x82,\xc5>7\xb0.\xcc&\xcfR.\xbf\x12\x91%\xee\xdc\x14)\xa4C\x12\x0f\x86$\xa8\xf2\xee\xf3\xba\xe1\x15\x14\xbf\xe3\x01\xd6\x90\x05*]\xea\xddz\xdc\xa7@\x1dl{\xa8\x18\x8f\xb6h)\x94\xd78\xdap[*\xa8%\x96\x8d\x98KO\xe6\x85\x90\xe0\xc1\x03\xe2\xa4\xfa\x80\x01\x85/M\xb9\x8a\xac-\xd71\x8f-\xc8W\x8cZ\xf3\xe8l\xce\xeb\x82e\x928N\xa7$'\x87=N\x00\xcd3\x16tt\xd16u}\xff\x91F\x8b\xd6\xa0,`\xdb1\xce\xd8u\xa6d8vP\xb8\xb3\x1d\xfby\x1c\x06\x1e\xcd\xac\xd7\xb5 \x84\xaa?\xe3\n\xcb\x9dI\xb7\xa6C\x92\xc8\xd3\xca\xff\x00\xbb\xcd9\x89|@\xaaI\xe6\xd8\xb9=-rK\xcc\x16\xb6\x9e\xb9-\xbc\xa1\xf8VC\xed\xcf|X\xe4OA\x03\xa5\xe9\xf7\x95\xe0\xcc\x1e\xe9\xc2\x07\xc4\x98$\xb9\x12*\x84\x8dX4H\xb2mh\xe5-\xb1`\x9dv\xd4-k\"\xe6\x174mz\x86\x05\x95\xf3M#o\xc9!\xdep\xd7tKH\xb9,\xed\xb0\xd2\xb7\xc1\x9c{y\xda^iP\x02v\xd5\x99k\x7f \xb0\x86\x8f2\xd7\xe6\x91\xb0]$\x90\x8fa\xe2\x0b+\x80\xe2\xeazH\xf21\x8b\xfcf\x06>\xf9:XC\x9f\xd8=\xa8\x07\x00\x82.!b\x98\x04P\xb723\xf5\xd1\xaf\x8cpu\x14\x07\xe4\x90\xec\x10A\x04g\xfc\x14\xd40\xdcA\xe7~\x0eA\xf2\xee\x85<\xd2h\x02\x1f\xdfPa\x15\xf1]p\x06\x12e)\xec\xe8P\xedh\xb7>\xc6C=\xea\xaau\xf6\xe5\xe8)\x0d\xa7z\xf9\xd0,/^\xcd\x99R\xef\xd5\xae\x87\x9bt]\xf0\xbb\x1e\xd9&-\xee+c\x13\xadV\x90)\xde\x9bX\x0c\x06\xe03W\xb94\x8b\xf5\xf0p\xbb\x03#\xad\xd2\x14\x8f=\x1e\x864N\x99%`k_\xf4\xe6\x8bs\x83L\x89\xd7\x81\xe6\x04\x9c'\xd0W\xcfu\x8a\x90\xf3\xa9\xf5\xb8\xear\xb52\xd4\n\xcb]\xe7V\xf7icX\xbagbQ\x90CIL\x00\xf2\x801!\xd3\xe2\xd7\xf7\x05\x8c+\x01X\xe4\x0f\x15\xa2\x03\x08\xf0Zi\x94\xd5\x99,\xf2\xc1\xd4\x14?\xd9d\xba\x9c{\xc7[\xd2\x84z\x19K\x1ci\x19\xce[\x8e=^\x14\x16\xcb\xa4R4!\xa3\xa2\xb8\x18\x1a\x8c\xeb!=\x84\xb0D\x1d\x1b\xc8)\xd3\x86\xc8\xf4Q\x81\x1eN\xf6\xa5E\xd4\xb9\xc1f\x81;8\xef\xdc\x86DI\x1d\xde\xd2l9^\x05\x91[\x0e{\xc7G\xf2\xaa\x93\x03=\xad\x94L\xcd\xca\xe4\xf4\xb6\xa9\x95\x89\x035\x1a\xb3\xebL\x94\x7f\xf0\x80P\xf2=i\x0d\xc7C\x0c|\xdd\xe2\xa0\x8d\xa86Ri\xff\x92Z\x01\xed\x9aJZ9\x15\xb4\xd6i\xc7xx\x1a\xd0f7FTo\xc1\xe9\x87\xd7\xa7\x87\xf3\x0d\x11\xa0~\xe6%\"\x0c\xe1L\x15\xe8\x9aK\\=\x04\xc7Eb\xc1\x1f\x85!\xd4\x96\xba\x10/\xe8{\xc0 n$\xb8\x0c\xf9\x959\x00\xcb\x99q=U\x91\xa7+\x82\x8d:\xd7\x08\xb6\x91-\x8a\x1a5\xe1\xc2{b\x1d\xfeN\xb1>.\xc5\x93\xb3\xbc\x11\x13T$\x17\xdcKbWB\x00\xe1\xfdx\x1e$\xa9t\x91_(\"\x18I\x95\x82\x9a\xdb)\x12\xb1\xdb{n\xff\xa0\xdd\x16\xca\xd4\xa0+\xf5\x1a+\xea\x86\x8d\x82\xb2\xad\xa5\xeaCuH\xff\xd4\xfc\xd5\xdb\xb3G\xc5`-\x01\x9cl\x18\x9f\xed<'\x91\xb5'{\x92\x13,\x88\xbf6\x1cJ\xc1i\xed6\x89\x80\x1bQ\xa4\x90Fr$ /\x94\xea$%\xdf\x9b\x86b\xf6\xad\x16\x81\x96)\"\xd3\xd4\x8f\\\xceS\x92\x91\x11\x12\xa6\x8a\x90FHi\xfd\x04\x851b\x05\xb8\x91\"\x07\x8c\xbb\xd1\xe0\x9b\x9a\x7f\xec\xef\xedX\x8c\xb0\x8be(\xd5\x9c,\xfc\xfa\x96b{\xb6\"\xb0\x01WVe\x11$%n&\x13\x137\x1a\x14\xfaR\xc6:\x13\xb8\xc2\xf1$\xf1\x98*\xbb\xb6C\x88f#\x93D\xb1)\xd9\xda\x92\xf1mhR(\xda\x7f\xe0i\xa0\xb9\xb4\xad-w\xf2\x84< V 1\x84\x0d\x15\x8d;\x0f\xdb\xa4c\xd8\xac\x17~\x80F\x1e< {\xe0\xe9\xa6\xc9\xdb\xdc\xa1}\xfd\xda\xa1\xb9^\x97\x899\x19W\xec+\xe0\xf2\x8fL\x8b\xe3e0\xf6\xd9\x9c\xe6a\xf6S\xc0\xaeD\xa6$;Pd\xb6\xe5nI\x17\x83\x16_Qc0\xba9\xac\xder\xaa\xd4)\xeak \x84:\x118D\xaf\xa4W\x95\x9c\xa5v{\x13\xe0\x1d]\xb1\xfb\x9dwg\x99e\xf1\xf4\xe1\xc3\xab\xab\xab\xf1\xd5\xde\x98'\x8b\x87\x93g\xcf\x9e=\xbc\x0e\x83\xe8\xb3\xd3\x94\x90!\xf0\xbf\xbc}#\xca\xec?\x8c\xe8\x8a\xa51\xf5\x98\xd3\x94\xa05\xf1\x12\xf5<\x16e?\xb2`\xb1\xcc\xa6\xc4\x91\xaf\xa3%\xbc#>\x9a\xa8\xe7\xe5\xab<\x04O\xd6;H\xb6\xef\x07Y\xb0\xb6d\x86\xc1\"\x12s\xff\x03MY\x18DL|O\xa7\x8d.U\"\xf6\xd10\xe4W\x1f\x19O|\x96@\x99\xf2\x15\x85\x8e\x97\xf4\x92e\x81\x87\xb7b\x15\x87A\x96\xfb\x966&\xf42\xf0^\xf1d%>\x04/\xa39OV\xd8wR\x0fn\x07\xb1Z\xb2, .\xf3\x8cI7\x88N\xe5\x1d\xabJ\xe7\x8b\xa5g\xc2\x8bw\x0c>\xcf\xf8G\x06\xc6\x92\x02\xba|\xc3`\x7f\x0fVy\xb6D\xdb)\xc6\xfcU\xc2\xfe\x91\xb3\xc8\xbb\x99\x12\xa7\xf2\x8e\xd4%\xf2?$|\x1e\x84LA\xab7\x0b\xac\x98\xcf\xd3e0\xcf\x14\xb4x\x1f\xa5\"\x01+p\xc9\xaf\xf1V\xb2E\x10\xe19\x01M\xf1\x8c\x1b4\xd9\xa3\xa1\xf7\x16\x0e`G\xffD\x1a\xe2\xd1\xb8\xd8\x0f\x1e\x8d\xed\x9b\xc1\x0b\x83\x18\xffN\x18\xc4\x1f\xa8\x18tG\xfc\x1c\xc54[Z\xca\x7f\xcca,\x01,\xc9\xd1\x91\xd4\xb5}\x8a\x02\xc1w;\x95w\x0c\x9e\x87\xb3#\x1b?\x98\xcf\xf3\x94\x1ds\xe9\xabsJ\x9cZ\n\xd2\x1b?H$go\xa9\x11\xbc\x9eZ\xf2\xd6\x81m |\xbe\n\"Z\xc1\xef:\xa9\x0d\xbd\xfb\xb9\xa5:|\\}\xbca\xcc_0\xb5\xb7\xf5O\xe4[,dkj\xed\xb8\xd4[\xfb\x81z\x9f\x17 \xcf#_\xd4\x05I\xa3\xcb\"\x0d\xab4\xc2'U\xd0L\x91m\xda\x04\x9b\x9bD4\xfc\xc8R\x9e'\x1eK?\xb2\x7f\xe4A\xc2\xe0\xa3\xb6<\xe4\xe3\xf3 \x0c\xd1\x0f\x88\x8c\xf71\xf5\x02\xf0k#\xdeF\\\xbeZjQ\xa8\x08 -\xa8H\xeew\xdb\xe72\x96|d\xa9\xacB\xfe\xb6V\xa1q\x99\xf1\x86\xc1\x86\x9c\xfb\xc7\x02\x13\x08P\xf12\x02\xbc`\x035\xba\x0b\xc0-\xfd\xe5^\x9e\x8a\x99\xc5\xfb\xc2\xa3\xec\x15]\x05!T\xc5\xa3l4\x877\xb4\xa2(;\x05]\n \x98\x06\xbf\xa3\x03\xa7\xc0\x8e\xfc\xff\xce\xd3\xcc\x04\x1eQH\xb2\x95\xc9\x12\x96y\xcb\xa2\x80|\xb5\x02\xdf\x84eC\xc4\x8b\x05\xf0'\x9a\x04\x12U\x00\xe8Z\xbeZ\x80\x7f\xd6g!\xc0^\xd9\x0eC\xa9\xae\x83\x0fg\xc2Wx\x06\xbe\xc3\xe7\xf8\x0e_L\xf0\xe4]<9\xbc\x89\x97\x8a\xfe\x82\xdf\xa3\x08'\xbe \xf3}\x12\xb0(\x03\xcc\xf0#O\x82\xdf\x05\x9f\x18\x16%y\x99;Z\x16\xd9=\xea\xfa\x89%Y\xe0YjZ\xabL[=\xe0\xb8\xdb\xd1?1\xa8\x84\xfa\xa2:\xd0\x12\x99K\x9a\xb5\x91\xd6RNo\xc2\xca;\x02\xbf\xa4\xd1\x02Ned\x98a8\x8e\xfc\xf5/S\xe2\xc0\xef\x11\xf5\xd7\xa3k\xac\x16\x91\xfb> \x16AT\x02sxG\xe1\x03\x9f\xf1EB\xe3\xa5\x85\x90\x0fVt\xc1L\x92\x01\x12ZI\x86 \"xU\x11\xbe\x86\x80\xd8\xf1X\x8c/\xeb\xcfx*\xbeJ?\xe3_\xf8\xbc\x87'?\xc2\x93Y\x12\xb1\xf0-\xcd\x92\xe0zJ\x1c\xf3\x15\xe9\xad\xcc\x16\x93\xfa\x06\xe4UE\x892\xc9R\xca6\xd9\x9f\xd9\x0d\xdci\xa4P\x95\xfa\x8d\xd6qs\x1a\x8b\xd3^\x01\xaa\x17\x1c\xf2,Xi8\xf8\x89@Iy[\x81;\xcdW\x14:\xcbXr*p?\xac\x0b\xf9>Je\x02V@\xa040\xa6\x95'\x8d~\xb7\x1e6`\x8f\x0e\x05\"v\x14-\x00\xe96\xd2\xb0r\x1cp\x012\xb2+\x9a|f\xc9 \x90\x1c\xf2\xf7\x88\xa1\xb4\x86\xcc|\x1b\x18\x80\xab\xc0\x0ex*\xaf\x085h*o\xa1,\xc0\x05\xd7c\xbeZ\xa15\xf60\xde\xac\xb0?\x07>\xac?\xe3\x0d\x85M\xf1=U\x84\xcb-qV=\xc9R\x9d n\x87\xcb\x96lE\x15\xa2\xc6>\xcf-\xd2\x82(_\xbd\xf72\xba\x86\xf5[\xbe \xdf\xd0R]\xa4\x12\xae\x89\x164O\xbaa\xc73\xa5<\x04\xcd ld\xa7q\x00\xd9\xf2m\xdc6_\xb3d\x1e\xf2+k\xa6\xd8\xe4Z6:%\x8eN\x1a\xc5*\x0d\x1b\x17\x05s\xb6\x0c\xbc\xcf\x11KS\xb3\\\xa6\x13\x91\x821\x0d\xa2\xec\xbd\x92\x08\xc1\xcb\xc8&\x10\x8ai\xc4S6\x018\xf1k4A\x81\xb2e\x81&\xcb\x17\x1cRP\xe7\xb5\xf5\x88\xa4\xda\xcb\x9a\x07v=\xc9^\xaa\xf6)\xeb78\x1c[\xa0\xee\x0e\xe0\xf2}\xc4 \xc1V\x00\x97\xa3\xc8\xac\xa3\xec\x17]\x8f\xf8m\xad\xe2(\xfb\xd5\x80\xfb\xb5\x05\xeeo\x06\xdc\xdf0\xb8\x84\xa5,Y\xb3\xa30^R\xf0\x1bo\xbc\xb7\xc1\xa71\xf3\xb2\x8fby\x9b\xa5\xcaT\xb4,`\xee5+\xc6\xb7\x92\x80\x94\xc07\x9d \xa2r|\x18\x136\x17#(\xfea\xd5\xb1\xf9\xaf2\x17\x1b\xb2\x82\x9ey\x0d+\x0b\x00U\n\x08cP\xba=a1\xa3\x19(\x89A\x81\xe2\xcd\n\xfbR0\xe1N\xf1\x1b\x85\x93<\xe8\xc9u\xc6\xa24\xe0Q\n\x05\xea\x89-%_1\x9a\xe5 3\xcb\xe9$\xb4\x94\xd2oA\x074\xcdCK\x16\xcflR\x94\x04g7\x12\x1c\xf7\xa6\x1e\xb5\xb0\x87)c8\xc3\x9f.i\\!I!\xa1\x95$MC\x1e[\xbe\xa2 \x184\x8fyyH\x13C\xe8SO\xc2\xbe\xa5@N\n\xb9\x84SO\xc2K\xd9\xba\x1b'\x8c\xfaoY\xb6\xe4>\xd4U\xbeb\xf5\x94\xda]\x02\xb8|Ca\xfd\x97l\x1dh\xe1\xa5\xf9\x8aB\xb3\x15.\xe0\x169kKN\x90y\xcb\xb3 \x84\xe5h\xbc\xa1\xf5\xf3X\xd3\x86\xe2\xb7\x95.\x14\x99\xa5\x0c\x02@\xed\"\x884K\x82\xcf,[&<_,\x8dc\xb3\x92\xdevvV\x00\xcd\x03\xb4ZC\xdb)*o\xb8,\x03\x94\xf0\xcf\x96\x95 Y/i\xba\xa4IBeWE\xca\xc8\xd7I\xf8\xa7T!^\xae\x81\xa2\x14\xb7\xaf\x04\x01\xf3&\x88\x98G\xe3\xb2L(\x13Z\x0b\xfc7\x0f\xa2j \x91b-\xf26\xc8\x04\xdd\xb1\n\x8c\xa6\xad\x8a4k1s\xbe\xa1L\xeb\x8c\xf3\xcfL\xd3\xc2\n\xfc\xcaB\x0c\xa7y2\xa7\x1e;\x95X\xc81_1\xe8\x1b\xb1\xd4\xdf\xd0h\x91\xd3\x05\xc0W\x12\x90\x12\x19\xbd\x0c\xa5\xb7&\xb1d\x8c7\x146Y0 \x02\xd4/+\xcc\xaf\x05\x0cv\x96e\xec:;\x02\xfdV\x01\xc6\xae\xb3\x91\xd4v\xb5\x80\xbed\x1eO4\x0e\x00p\xbfH\xb1\x141\x91/\x94h\xc3\xbd\x02\xa0\xa0\xf9\xca\x17\x0c\x92\xa3\x1b!+\xe98$7\xc7%\x019. \xc8E;k\x14t\x91\xd6\x86\x06\n \x13\x05\x94%\xdb\xb6\x7f\x1e\x05\x9e\x8d\xb7Qy?\x04~\x00\xf5\xc1\xdb\xe82\xf0\x03{E\xa0|e@\x83\xaa:\x0e\x9e\xa5\x1fXr\xb2\x92\xc0Y:\x8a\x05\x85\x8a\x11\xbf\xeb#\xe3>\xd7Y\x8f\xca\xeb]\x0c\xf8G-\xaar\xd6#%\xb6\xc2\xc0^\x9b\xb2%g=2dM\x18\xf8\xdb\n\x87\xe8\xacG&\xcb\x88\x15P\xdb\n\x19\xd65\xf32\x9e\x9c\xcc\xe7\xcc\x13xF\xbe\x8e\x18\xbcc5\xb1$\xb5\xb1jk\x96dG\xfe\xfaW\xa8&\xc9@\xf0\x86\xa1\x1d\x91Y\xca\xdd\x00\xb4E\xecVB\xffZ\x83F\xeb\x0e\xd8\xd5\x0f\xfcZ@\xca_\x16\x983\xc0 \nL\xbe\xa0\x90ip\x19\x846n\x18P%>\xacW<\xf1K\x89\x8fxk\x91\xf7\\% \xa9Q\xb7E\xeam\xb4\xc2o\x8cp\x9a\xf1\xba\x90\x95\\\xdb\xef\x87\xafq\x04p\x8d#\x80\xeb\xe3%\x8d\"\x16J\xad[@\x91\xf5$\xec\x1ba\x10}>\xf2\xb2\x1c\x88^\x07^\xa7T\xbe[\xc1\x13/\xe1\xa1\x01.\xdfm\xe0?& \x88\x96\xb0\xcb\x04\x15EC\xe6G\xb3\xd2\xb6\x1aO\x97\xfc\xaa\x00L\x97\xfc\xca\x06x\x16dF\x95\x99x\xb3\x82\xca\xab\\\x05\x89_\xe2^\xaf\xc2\x1f\xc0\xd3\xb6s\xbd\n\xa7\x97\x14U\x98\xb8^\x85\x11\xbe\xc8 \xe7\x17\xf8\x00\xd4\x10\xa5SLAG\x81\x8a\xb3W})\xa4\xe8:\xbc^\x85b\xcd\xea\xf6`J;D\xfa2@\x1as\x83/\xae\x1b|q\xdd4\x17W= \xf9\xf2\xefh]\xbfs\xbe:\x8a\xfc\x0fT\x1cQ\xe5K\xab\x7fT\x8a*\x1f)\x17\x02\x81\xc0\x95\xf5@\x11Dz\x1982Ug`\x84R\xcc!\x04il\x85\xa4Y\x1dil\x806 \xb9\xec\xdb >v\xd6!\x17z\x1b\x84Z\xe1\xad \xb0\xb2m\x10zI[\x8c\xdc\x8a\x85h\xcfWk\xb0WH\xd9\xc6\x8cL\xcd\xc8]\xa4\xaa\x9d*#\x02\x8e?\xb3\x9b\xd4\x0d\x06\xe39ON\xa8\xb7t\xed\n\x84t\\\xae\x08\x19\xe7vgH\x02\xf1\xeb\xc1\x03\xe2\xd2q\xe3\xeb\x12H@\x18\xeax\xdf$@\xc7N\xddu\x02\xc7\xedW[\x82\xfe`\x0e\x15\xa4\xa3\x85Guk\xd7T\x81\xef\xe2>>\x1e\xe3>>vw\xeb\xd5\xcf\xc16\xbdj\xcb\xaa50\xdf\xea\xf8\x05\xa69k\xc3;\x8b\x80\"/\x0e\xc8\xa4\xe6=\xb1i\xaeN@2\x12\x02]\x83o\xd0xIS\xe6\x7fd\x8b \xcd$\x15\xaf\x97\x10\n.\x1e\xe5\xf1~J\x1c\x1eID\x85\xa0)\xfdh\xd7\xf6\x06\xb4r\x11\xe5\xa0e\x90\xf5M@\xd9&\x16LC\xe4\x01^\x9a9\x19\x8f\x7f\x08\xf3\xc4\x19\x12\x07\x04\x01\x10\x1b\xfb-\x8br\x95\xf2\x8a{y\xaa~\xff\x95\xdd\xbc\xe4WQ\xf9\xf6)V\xbf\xdf\xf2\x06\xe8I\xe47'\xab\xa9\xa2\xbf\xa1EV\x8b\x05q\x87\x0b\x12\xfbf*\x0dM\xa7=\x0d\x82Mc\xd4io\xd3\xe0\xc2du\xda\xcfB\xd8\xb0j\x9dV\x8d\\\xf1m\xdb\xb17\x88\x1a\xed\xa6\xa5a\xab\x85b\x0f\xdb\xc4[\x8e\xbb\xb4KP&\x84\xd3\xc2PA\x07\xc7o\xb1\xf3\x92Q\x12\xa4\xf1I\x0b\x14\x8f\x05\xd0%\xcf#\x1f|5\xc4v\xd8\x90\xcd3\x13\xf8\x0d\x9b\xdfn\x94\xbf\xba~m<\xc0\xb2n\x0d\x8a\xfa\x9e\xbb\x16\x07,6\xde\x80~\x9a\x03\xa9\xcd\xfes\xc3\x93J\xac\xe6aH\x96Cbq\x10\xa7\x06\x9fC\xb4xr\xa0]58C\x91\x04|\xa6\x98\xd7!I\xc6\xa5\xea\xba\x8e\xb8\xf3Ry\xb7c\xa9\x0bf\x99\xd5\xfe\xfd \xf9\x8c%N\x93h\xfce3X\xee\x9aE\xa0\x84\x9aNImF\xd8u\x96P/\xd3wtu\xca\xa4%|\xf4\xd6\xa2\xc3\xea_\x0fdF\x0em\xb1\xd3\x06d\x8a\x9a[\x88'\xbd\n\xdam\xde=\x9a2\xe3\xd8\x9bZW\x9a\x1b\xba\x1c\x82\x9d;Y\x923\xe9#\x9e\x8f\x95\xaa\xed\x89\x1f\x80\xc8Q\x9a\xf1\xf82\xb6\xc7R\xfa\xa2\xd5\x07T\x8b\xd1!\xb8\x82\xc7\xb3\x8b\xf6\xc1\x99mo^qd\x96\xc7d\xf1\xe5\xbb}\xb8<\xe9\xed_\x87\xe3\xd6\x12\x17\x8b\xf4\xfc\x8eI\x89\xe0_\xaa6\xe9S\xdc\xd2 \xb5\xa6\x14\x19@n\xa4E{G\x0b\xeaT\x8b\xbdz\xb1t\xe7\x83^\xdd\xd2$TG\x97$m\xd5\xd9!\xd5\x91\x0edFZ\x1c94\\b\xfa\x1f\xf2\xec\x0d\xf8\xd3d\xf5\xe8k\x16\xaf\xa3%\xf1*M\x97a\xd1\x03u\xb5c\xb5\xc1\xc3\x8d\xaf.!\xf5\xae\xcc\x0c\x1e\x99\xc9\xe6\xaf\xbb\xc9\xfbP\x9c\xc9\xc9\x95\x05\xdbc\x94\x9b\xd9\xdf\xab\xf3J!\xce\xfc(\x8f\xdd{u&g\xae\xd2\xeb\xf0\xb1jM=\xdd\x97\xf0\x8f\xea\xbdZ\xaa\xf4\xfa(\xacUz\x9d\xe9Z\xa9A\xab\xc3/\x14|\xdd\x07\xdf\x8d\x1c\xcd\xfa\xe8\\*\x1e\xad>\n\x17e\x84\xaa?\xbe\xd6\xf2\xaej\xe1\xe8g\x0e\xbd\xe4\xe0G\xc0\xa1Q \xdd\xe3\x9dD~\xe5\xfdu\xc6\xf4\x15\x89\x91\xaa\xfd\x0f8\x97\x8a\x95\xf1h\xf4!\xa47\xc6\xcf3ya\x08)a\xe0}\x86\x1fUn\xc7\xe3\xb1,\x91C]>\xcf/Cv\xac\x81\xfd\x84.\xf4\x7f\xd5*\xf9S\xfa7\x90/\xd7A\xa6\x7fC\x8c7\xfd\xf2~]\x02\x15\x8d\xf5\x13\x0e\x1c\x92\x9f\xcb.)<3$\x0e[\xc5Y\x00Q\xcc\x1c\x16y\xc9M\x9c\xe9\x17_\xfdH\x12\x0e\x15\xce5{\x16D\xb1lv\x10\xadi\x18\x00\xd4\xe7\x92_\xfb\xccn>$pO\x02\xbf%k\x16r\xea\xeb\xff\xcc\x7fI3Z\xbe\xbde\x19\xf5\x8d\x94\xa2\xd5+\x93\xd5\x83\x97\xb7\\v\x14^\xde\xe7%\x94\xee\xf5\xaa\xe4\x06c\x9afL\xfe\xc8S\xf9C\xcd\x93\xf8\x0f\x12m\xe2\xc4 _\xe8\xc6&4c\xe5\xc0\x80s>\xc7t\xf1\xeb\xa4\x8c}\x96\x83\"~\xa9\x1a\xd2\x8c\x86\xa1J\xcd/WrV\xd2<\x8d\x99\x9c\xb9,X\xa9P\xd4\xf0\xc6soy,\xc8\x87\xb0xUS\x0c\xbfu\x07\xe1\xa5\x18\x08\xb8\x1f\x0b\x8cE\xba\xe6a\xbe2\x1a{EA\xf6\x0e?\x97\x8c\x85\xcey\x0f)\x91f\x8d\xd8l\xe7|\x9c\xf1Oq\xcc\x92c\x9a2w@\xb6\x05c\x16\x06\x1es\xeb\x9b\x95(\xcbg\x87G\x10\xe3\xb7\x99\x0bv\x98\x19\x8f-\xd9\x1c\x15x\x90;\x8a5Z\x0c\xc1KiFD\xb6\x89s\x0f\x92\x8c\x04\x91*T\x0f\xe3\x0b)P\xe3Cr5K\xce\x8b\x80\xd9\x00Y\xf3\xd2~\xa2PS\x91X\x08\x07\xae\xad\x16\xca\xce\x18\xe2P\x8d/\x12\xce\x81.}\xfd\xb2\xac\x1f\xa9\xe9\xd4^\xd3e\x9ee\xd2\x0c\xf8@\x06\xe0T\xdb\xdbHH\x8d#W\xa6\x08TF\x13FU\x9a\xf1m\xfdK\xf4\xec\xb8\x95\x92\xbf\xd8\x90\x92\xe7(\x13D\x13B\x87pR\\\xcd\xd89.-\xd8\xba\xe9 \xf5\xfb\xd3\xeaGpjtPT\xc7\xeaD\xe8\x07\xa6O\x8b\x0e\xe8\x97U\xcc\xdd\x01}\xa2\xb0z\x17X\x81\xf1;\x01\xfd\x1e@pRt\x00\xbd\x86\xd5\xd5 $\x0f\x96\x0e\xb07\xe2P\xe9\x01\xa3\x0e\x9c^\x90\xc5a\xd4\x03Z\xe2\xe7\x0e\xc0\x0fp\xfat\x01\xf5X/\x1f\xd4\xa9\xd5\x05\xa6O\xb4\x0e\xb8\x8f\xe5i\xd7\x05 'a\x07\xd0\xa9<\x1b{@\xf5\xe8\xc3\xa9:S\xbb\xc0\xe4y\xdb %\xcf\xe2\x0e\xb0\xb3\xf2\x9c\xee\x80\xfc\xc9<|;`\x7fV\x07\xb3\x9d\xbf\x12<\xc0\x1d\x19\xe5\xbfj\x8a\xab\x9do\x94\xfe\x9e.\xdd\xa8M\x82\xac\x9f\xfbf#!\xb8\xd3\xdd\xba\xd9\"\x88(`\xba\x84)\xa2\x19\xde\xdd\x9a!\xc9\xf4\xf6\xa1\xdeU\xaeq\xe4\xe9\xba\xc9p\xbf4X\x81\x8e\xbev\xc9G\xaa\x80@Y\xf6\x01\xb4Nc\x15\xec}7\x1a\x7f[P\xe6\x1d\x80\xdd\x12\x18\xa2\xe6.\xbe\xdb\xdc\xbd\x14\x9cUGc^*\xae\xab\x17X\xd6\xdd\xb9\x97\x9a[\xeb\x01'9\xb9\x1e\x80}F\xf5e\xc1\x01v\x02\xf2\xae\xadkq\xadHz\x8e\xfb\x99\xc1\xf6t\xe1a\xcd\x12\xf5\x81\xeb\xb3\xa8\xcfJV\xaa\xbd\x8f\x16\xef\xb8\xa4g\x1f\x8fLABG\x9b\x8e\x9aB\x86\xbe%\xfa\xf4\xa4\xc5\xbb^\x9f\x9e\x9cU\xd8\xcd\xf6O\xad\xef\xf6)\x19\xe4\xa7\xe3\x1b\xab\xbb}\xe3g\xe0\x88\xdb?\x81\xf8\\\xd3O\x9fO\x1c\xf3\xb8\x93~;\xeeF\x98\x1f@d\xd1\xde\xd2\xa6?\xc4\xa6\x08\x96\n.-q\x9d\xfd'\x0e\x1e\xc8H\xf0M\x17\x10\x90\xa1\xbc%\xba)9\xadf\x01u\x80\x05\xed\xb7?\x17\x83!\xb9\xa8\x94\xbd\x07\xa1/\xdcV\xf3H\x1e\x89\xa5\xdcw\xeb\xd4e\xe3\x8b\x8c.\xd0\xdb1b\x08j\x05\x1fm\x17\x0f\x04z\x18\x90`\x83\xf8\xac\x9f\x08\x96\xfe\xcb\x17\xe2\x9e(\xde^G\x85\n\x0c\x89\xdf\x0d\x16_\xaamh\xae\x820|\xc9B\x961\xcb\xf0\xdc\xfb\xd8Djll\xbd\x8c\xce\x95\xc3Iw0$>4\x0dR\xbb\xfaU\xbcYd\xef\xc7\x90zG\xd9\xfb\xa3}\xd4\x81=o\x11\x18h\xf7nc\x8f\x86\xa1\x8a\xacn@\x97\xcd.~%c\x9aC\xbc\xf8\xe3\x90\xa6\xa9\xcb\xeba@\n\xa9\xb0\xf4\x8f\xd0\xd4\x06a\xd2/\xb1\xe0-\xb0\xec8e\xb9\xcf\xcb\x0b\xed\xca\xadhM\xfd\x8a\xdf\xd3\xa85o,\x9a+\xc4\x0b\x83\xf8\x92\xd3\x04\xf8\xe6>~\xda\xb54\xa9RP\xe9\x94\x1c\x126\xae\xa4\x17\xb7\xa6\xd5\xe4\xaee\x85Mw\xf0-\xa7;\x90^\x86\xcdI\x08\xeec\x12&\x93\xc9\xbf\xc1\xdaM\x98@\xe2\xbeV(\xff\xf6k\xafy\xf1\xc3-79\xb8\x87\xbd\xcf\xecf\n\xf7V\xf5[4\xa2<\x02d\xa0\xe0\xdf\xdce\xe2\xf1\xb2$\xfc+T\x80f\x83/\xb5\x96|\x1a\xb6\xe5\xaeXF[\xb2\xa51\xa8-\x17|\x19\xa0\xd8\x81\xc8\xb8\x16o\xb9\x1f\xcc\x03pA\x90 8wwR\xbf\x18\x14\x8f\xb7\xa4\xc9q5\xf4~\xe7v\xfd\xccnb\x10\x1cH9\xae\xd4\xfd8\x94nm\xa7\xb5x\xa4\x04\x17\x8f\x7ff7\xb7\xf8\xaa/\xb8V\xf3\xa3_\xbe@z\x1e\xd7\x9a\xc2\xc6\xea\x03}\xdbs\xb5\x0c\xbc\xe5\x86\xadi\x19\x83\xfbll%\x05Eg\xf4[b\x00:$\xc1\xb7P\xe9m\xee_\xfcP9I\xbd)qNR\x8f\xa26\x05\xa0=}I\x93)q\x08\x92\xfd\x06\xf4\xad\x9c\xa3$\xe1W\xe27\x02\xf2)\xd6\x00\x9f0\x83\xc6\x8f\xca\xd0\x04 >ZLM^\xf2\xabH\xc3\xc8\x9b\xc7&\x08\x0b\xa7\xc4\x91\xa4\x1a\x92\xfd3\x18K\xbe?E\xb2\xde\xb2(\x9f\x12\xa7\xa2\xf9\xda\x00:\x8a\xe3\xb4\x13H\xb2MS\xe2\xc8\x1fo\xb8\x87\x19O\xbc\xe5\xbf\x7fH\x82\x08\x14\x84\x00?9\x9f\xa2\xc0gQ&\xf0\x89\xdfjg\x80\xa3\xe0\xfd)q~\xa0\xdeg\x9b\x85\xc5\xb3)q\xce\xe8%\x923\xd9\x15}\n\x19\xc5\xcc#&{ba\xc8\xdb\xedf\xe6\x13\xd1M\x8b\xaf\xcb\xc9S5T \xc7\xec\xc7&\xa2\xc1G!ZR\xb4U\xca\xe6\x9b\x99\xbb;S\xb8(L-\x03\xbb\xfb\xb4m%\xef\xedZ\xd6\xf0\xde\x1e|s\xc1\xd0\xf5\xb9\xf7H\xe5Z\xd6\xdd\xdec\x18%\xcc$|O\x8c\xd1\x8f\x1cu\xcb\xb5\xf7\xb4c\xdb\xec\xed\xb7n\x9b\xbdg]{\xe6\xd1N\xc7\x8ey$Z\xfe:J\x19\xea3\xe7\xd1\x93\xb6\xed4\x81\x95\xf3\ns52\x81u\xf3j\x17\xcd\x12\x83\xf9j\x0f\xcd\x12\xady\xf5\x08\xcd\x12My\xf5\x18\xcd\x12\xc3\xf8\xea \x9a%\x06\xf0\xd5S4K\x0c\xde\xab}tC\x88Q{\xf5\x0c\xcd\x9a@\x97w\xd0<9\x1c\xe8x\xec\xc2xL\xd0\x01y$\x06\xe4]\xbe\xb2\xac\xe8 \xccQ+6\xd9\xdd\x15U\xbce\x19\xada\x0e\x9c\xcb\xb3\x9f\xc0\xd2\x0b\xfegvc\xbb\xd1\xcd\x04\xc99\x03\x90s\x19\xec\xf63\xbbir\xa9\xc0\xfcV0\x1ah\xc8\x97\xde\xe3\xab\n\xb9_\x1b\x8d@\xcf~[\xa3\xb4\x7f|\xabld\xa2\xfc\xe1\x93C\x8d\xcc\xc8\x94\xc8\xb0:\xe3y\xc2W\xc7\x8a@\xab\x07DF\x15d7\xa2;\x82YAy\xc0x\xd5\x06eJ\x9cr\xc6\xee\xc1\xc9\xb6\xd4\x11\xfb\xd7s0>\xcd\xa8t\xf7\xc3\x92\x7f\x1d\x03\xd3\\-\xa0\xbb\xc3R\x1bI/\xb5\xa9\xcf\xda\x81<\xb8]\xf4;\xa0\xee\xc4\x96\xdc\x91%\xb2q&\xd5\xb5\xfd?\x86i\xff\xb7X\xf1\xb1\n\x15\xfd\x7f\x8b\xb8\xe9\xdf\x04O\xb00\xa3\xbft\xf1\x84\x1a\xf1JhCv%\x13\x04\x16\x05\xd5\xba\x97\xd5\xfc\x11\x1b\x1b\xc9\x0d\xc6\xaf\x11\xa74\xcc\xe8\xaf\x1b5\xe5\xd7zS~\xad6\xe5W\xbc)5(\x1c\xa8Ws\xff\x86-%\xc8\x91\x86\xff\xdfj\x19 \xce\xf2\xf1\xa0\xb9\xac\x9eu\xd1\x1b\x88\xac\\\x1f\xe0\xcd\xb1\xbe\xc8x\xfc\x86\xadY\xa8\xe2\x02O b`u\x11\xf8\xe0\xf5KdO\x90\xecJ\x84\x8e\xa9\x8a\x91R\x84\xc0\x80 \xa9\" \xc2\xa9U\xa3y\xd8\xb0\xeb\x85\x8co\x83\xe8O^dta~B\xe0\x82q\xc6\xdf\xf0\xabB{\xd3^\xa9\xb6\xfd\xfe\xf4\xf1uQ\x87\x91F\xa6\x88\xda\xfesl{F\xb5}x\xab\x196\xa7\xaf:3\xf5x\xcfS\xb2U3\xa0\xcfS\xf6*\xb8\x14\x13\xb25\xb9\x8f\xb6\x18\x91c\x1e\xd5\x15\xe6\xc51\xff\xf0\xb7\x87\x87\xdf?\xac\xa6\x0b&\xf9\xe1\xdf_\xfc\xb6\xf5\xdb\xe8\xb7Q-\x0f7\xd4?\xfe\xf1\xe4\xf8\xaf\xa7\x9f\xde^\x1c\x9d\x9d}\xbcxw\xf4\xf6dJ\x1cA\xc7\x8c \xe4\xf0\x08b*\xa79\x1a&\xc3\xf7\x8fU\xee\x19\x97\xb1\xb4\xbb\xf0\x081\xe8i\x9ct%\xe6\xd5^\xc6\xd2LTt\x08\x01f\xd88aqH=&\x10\xaaC\x1c\xb2M\xe8\xb8\xd9~\xb2M\xbe;p\xbe#\xdb$\x13?\x9d??\xf8\xae_@s\x1a}dy\xca\x9a=\xe9\x8a\x80\xa8c\x9b\x16\x16\xec.\xd6\xae\xf6\xce\x8aJ 6QL\x93\x94\xbd\x8e \xf0\xe4dg0\x94\xc1\x7f\x80\x8eo\xf6\xc2\xb6/\xeeY\xa4\xf6\xe4\xf1\xe3\xddI\x17\x92\xab\x0fQ\x11\xc7KL\xf6d\x08=\xdc\x91\x91\"wdH/V\x84\xdb\x12ks\xf4\x88< \xc1s\xc2\xc9\x0bB\xd1\x10_E\x8d\xb9\x19f\x90\x93m\xf2h\xe7\xd9\x93!\xa1\x03Y:\x17\xff\xb6\x0f\xc8\xa3\x01\x89\xc4\x7f7\x13\x7f\xd9X\x0b\xa4\x8f2\x97\x0f\x06d\x1b\xcd \xdbd\xd2\x96\xb9\xdb\x96\xb97@f9#\xffq@\x121\x00\xffa\xc6\xa6&\x8d T\x91\xdaD\x17\xc48lo\xab\xf6c\xcdGq\xa0+?5 _\x88\x1b\xa9\x9f/^\x90\xc9\x93\xfb\xc0G\xe6\xac;\x93\xc7\xe3'\xe3]\xe7\xf6\xb5u\xd8,\xb9\x91\xfb\xe8\xc9`(m\x91p\xdb\xa5I\xdd\x9aG{bx40\x8f\xec}\xa8\xe5\xd9\xc6\xa1\xb7\x04;\x1e)kw\xd6\xa2/'\xe0&\x8a\xfb-\xe3\xce)pV\x85\xd5\xbb\x01\xac7\x1b\xe8O\xd4T\x8a\n\xdcL\x06\x11\x1e\x08\xf4\xc7\xed\xe6\x9e\xcd\x16\xa1\xa1\xb4\x04\xf2\x8c|&N\xfd\xc4u\x1e=rDY\xf1\xeb\xb13\xac\xb8\xf3\xb8\xe7\xf8WbB\xf6,\x83\x9f\xa86\x9d\xe6\x97Y\xc2\x04\xd2\xe3EX\xe0\xdb\x7f9\x1b_\\\xb0\xf4-\xf7\xf3\x90\x81!\xdeP\x86\x87\x8b\x98\x97\x01\xa6\xfe\x90\xf0u \x86BG\x1dm\xb6:p#w\xff\xf1n}\xe5\xf1\"\xeb\xd1\x00e#\x02\xabY\x83\x8a\xf7h4M\x1ejM,\xa7\xa2\xa7MIwL\xc5J_\x12\x1dw\xad\xda_\xae\x93\xefyDU\xad-\x83\x18\xb9u\xfb<\x0eK:r'\xd8\x96\x16\x19{O\x1f\x9b\x18T&=\xc1\xc7\x9a\xfes\xc7Z\x9f;-\x07\x9en\x99\n\x1a\x8d|o\xab\x1fU\x016\"n5\xe8\xdd`@\xb2e\xc2\xafH\xc4\xae\x88@2`\xdc\xe0:\xc74\x8axF\x04oJ(\xf1\x04\xc3IhJh\xf1%\x07\xa1~\x14\x17\x8b\x99\xdd\xaf\x95\x95y\xff\x862\xb3e\x1f\xd9\x9c%,\xf2t\xf3\xc4\x87\xc8\x92\xa6\xd1w\x19\xb9d,\"A\x14d\x01\x0d\x83\x94\xf9dD\xd2\xd3\x05\x1b\x93O)+\xeb\x1b\x83\xb4\xa2xu\x07$\xe3\xf2d\xcc\x96l5&\x1f\x19\xf5\xc9J`m\x9a\x11\x15hu~9^\xb1\x87y\xca\xa4\xa8cT~\xc5\xa9\xdf\x8a\xe1\xa3\x91\xb5-~\x1b]A`\xd0\xcb\x95 \xb8\xe1&\xaf\x80\x0b\x08\x95kn\x04C^r\x1e\xa2\x19\xa2\xb1h\x86\x8c\x94\x8bf\xc9\xa3\x15\xcd\xd2\xce\xc5\xb1\xac\x9b\xd5\xa5\xa5\x114\xc2[\x0d\xfdy?Ge\x8bLK\xdb\x90r\x9a:\xb2\x14\x95\xf2Jk\xc7,\xa5xd\xab\x0fr\xa4\xc7F$\x17\xe2\x01\xe0]\xb8\xa6b\x18kW\xbf(\xff\x1e\xd5\x160\x91r\x83\xb1\x99 \x0e\xec\xa2\xec\x1d\xf0F\x83\xa8o\xa2\x14u\x82\xd14\x0d\x16\x10\x9e\xbb\xaf\xb0\xe79\xc9\xc8\x0bB\x93\x05\x88\x94S%\xe6yN\xb2\xedml\xaf\xe8\xa5^\x14\x98e\x88\xe1t\xf1\x89\x84\x04\x91\xe8\xa1j^y,-i\xfa\xfe*R\x8e&o$-')qqN3\xa9\x1b\x1f\xcd\x92\xf3\x1e\xd7\xdd\x86 9~\xe8\xb4\x8d8Q\x9d\xf2\xccN\xa9Q \xdf\x93=\xd1\x1e\xc95\x01\x8e,\xfb\xbdwN\x0e\xab\xaf\xb8\xfb\xd4\x159 ?p\x1e2\x1a\xa1\xa6\x04\x0b\xa2\x0c\xe3\xe7\xcd\xbc\x1b\x84e\xd3\xe9x\x14n}S@\x0e\x89\xbb#\x0e=5\n\x03)\x81\x88\x9b\x88\x0b<\xa2\x80\x8b\xc0\xe6\xf7\x05\xbd\xe3\x8d\xe3H\xf2z\x1dNb\xdc\x99^u\xcd]Y\x8a\xe6\xd58\x00\xe5\xdb\xbdp\xd4\xeeJ\xcb\xd3\xe8\xcb\x17\xb2%\xe8oZ\xd2\xdf\xba\xce\x12j e$\xf5\xb2\x07\x82\x0d\xa8\xbb\xb2\xd5\x0f: \x95\x11\xbd\x8f1\xa9N\xd1\x1d\x87\xc5\xaf\xe0\xad\x96\x91\xa9\x00\x9a\x83\xe3\xd70\xdf\xa6\xe3\xf3\x96%\x0b\xe6\xdfit\xba$OX9\xb1_/\x8b\x02\xed\xacf\x8b\xf3j\xd2\x85\xa1H\xc1N\x1a\xcb\x08\x1b\xd3\xcd\xa6oKV\xb9*\x07O\xcc\xc8)L\x0b>\x81\x06\xa89}f\x0d\x9bL^\x90\x9e\xe6\x97\xa9\x97\x04\x97\xfd\xe7K\xb5\x1d\x97\xa9\x89\xc6\xe4Q\xaa+\xed\xd3\x86,\xb9)\x1a\xd1\xb7\x0d+p\xbeQ\xffZ9\x1ef\xe2\x81q\x1f8.\x92%\xdc\x92F~\xa8\xa8\xe2\xf1e\x10\xf9\x90<\x18\x0cI#\xdbE\xfc\x8c\x10\xb47\x9f*\x1f\xef\xd5\x9f^=qu\xb3\xaa\xbd\x13\xecd\xaf\xa6\x15\x92\x83\x97\x81\xff\x96\xe7Q\xe7]\xab~\xe0\xa3\xe64\xb9\x9b}\xef\xe7 \x0c?2\x8f\x05k\x84\x93h\xfb\xf0U\xcbN\x90[\x0c\xdc\xc3\xa8\xb9j\xf2@M\x7f\xe5\xfaik\xea\xa7hu\x9b\xd1\xf9\x84\xcc\x94)\xb3\xe8\xd5\x8e\x02~\xa3\xaf\xd7\xb17h\xa5\xd7\xcf\xc2jz\x15c\x18\x19\xb6q,\xb2\x9b\xecd5\x7fm\x9c\xf7?0\x16}H\x98GC\x0f\\\x19\xf9\xca[\x7f\xadi\x06H\xc0#\x10\xa3T\x1b%o\xe6\x99\xaf\xb4\xd4\xab\x99v\xa2\x0b\x01\xaa\xf1%\x0d-|\xfd\xd4&\xc6\xc4\x04}\xa7\x06\x14\x1fk\xfb\xb5\xcf\xa1VCY}\xf9[\x02:\xb9\x07\xc6\xd8\x8eK\xe9Z\xfb\xd9\x07\xec\x8b\x14'\x00\xd1\xd9\xd9L]\xe8\xaa\xc4\xc3m\x1c]\x9f\xea\x08&\xcd\xef\xa2\xf2\xebO\x96\xdcl\x00M\xcc\xab \x1a\xc7\xe1\x8dk\x11\xe2`\xcfW\xe2\xd1vo\xc6\xb6G}s9\x06y\x9a<\xb0\x97\xbdk\xb0\xcb\xb3\xccGQ+6r^\xee\x8a\x0e\x8aI?\xb0<\n\xe7\x9a\xfd\xcaDp\xd3\xb5\xc4\xc8o|\xb7\xab\xd1\x18\xf4\xc7#\xedb?\xd2k\xa8z\xe1\xb4T\xef\xc0~\xd3l\xca\xb4q\n\xc8|\xbe\xb6\xaf\xb8\x16\xe9e\x1f\xbc\xb5`\x99\xb4\xb7\xf2\xb5zu_\xec\xa59\x8c\xea\x15\xc7\xf5\x908g\x9cP\xcfci\n\x97\x12W\xb2\xfa\xe2\xf6kHnxN\"\xc6|\x92q\x88\xe0\x1f\xcco\xc8\x1fD]kNI\x96\xe4\x8c|%T\x16\x9f\xf3<\xc9\x96\xc5\xe50\x01\"\x12\xeeF\xe0~q\x00\xf7HcgP\x1c\x04\xf3t|U\xedQ\x9fq\xe8\xa7\xda\xa5\x1f}\xcdi;\x10\xdb\x11qT\x96l\xae\xab\xf6\xa2\x81\xf9\xd1\x96\xe5\xdf^\x0b\xad\x9c\x02\xb6=\xd7^G\xae\xeb\xa8\x1d\xbd\xf6\xdd_\x1cw\x16\nb\xd2AAL\xfa\xef\xfc\xcd(\x08\xaa\xefih\xbb`-\x95{\xbeuX\xc2\x8e0Hp \xe6\x80\xf5R\xad, /e\xba\xce\xc8!\xd4m\xc2\xb6\n\x88:\x84\x84\x1e\x12\x1d\xb1\xfe\xccU\xb4D[~@\x0ee=;dJ\x803u=\xbd*l\xe7\x8a+x\xa7\x10`\xe7UXT\x82\xe2\xb6]\xc5\x16L\xf2\xd6\x96\xeb\x81\xd6\x07\x8c\xe6\xa0\x18\"\xab\xe8\xc1\x95\xbcqN\x0eIN\xa6jY6i\xc8k\xa5\xf9\xc1\xd5\xf5\x99\xca\x01\x1e#q\xff\xf8\xda$\x95\xbb\xee\xd3d\xe0\xe9\x1a~\xc2#`\x10\xc0\xfd\x03\xd1\x88TX\xc7j\xc5\xd5U\xb4l\xac^um^\xb5\xdf\xaf\x16Z\x93\x03\xe5!\xe0~\xb4\x1e\x87v\xa5\xbez'\xc1K\x90ti[\xdcR\xd5\x8f8\xcd\x98U-\xea\x9a\xc7KR\x83\xa9#\x19\xb0>\xd4\x1a\x83\x82\xd3L\xd4K\xf9\xe5\xda\x81T\xa8G\xf2\xb2j\x9bj\xa44\xbf\xddyN\x02\xf2\x82D\x85zf\xb0\xbd\xdd\xc4\x91\xc0\xd3p\xa5\x194$\xd1,8\x07a\x12\x9b\x89\x9f\xe7\xf2\xeeE\xfe\xb6\xb6\xad\x18\xac\xda\x0e\xf9\xb6Sh\xd9\xe7\x05\x00\xca0\x1b\xd4|\x02\x82\xce#\x00\x06\xdb\x7f\x9e\xa4\xf2\xbc\xe9\x89&\x957\xc2\xa7J\xb4\xd6\xd1[(QV\xd0J\x83\xe3#C\x0c\xb9\x08\x8e\x04\x1a\xd6\nv5\x12\xaf\x17\x94\x1aw8v[\xa0\xcaS\xd2\x0e\xb4`\xd9\xcb^\xb5\x01`\x12\xac\x99\x0fd\xd5\xab\x84\xaf:J\xac\x82\xeb j\xc9/\xceS;H\x06\x8a\xdf\x08+\x8dh\xe7f\xd6\xf1\x8fZG@\xee\xc3\xd6f\xca\xed\xdc2k4\x0c\xc1\x05E[~K\xf9B\xf7\xb8\x0d$\xc8n\xfa\x0e\x85\x81\x0b}6\x0f\"V\xa0\xa0\xe6\xce+A\x17,3\xb0\x15\xc4\\k\xc2s\x1b\xfc)\x98 %\x02[\x89\x97,\xf5\x92 \xce0^\x8fV\n\x19\xdaMMPA\xcaPAEP\xa5'\x85[\xe9\x17\xb4H\xea\x86C\xe2\x0d\xc9\x1cCD\xa0['\x0d-L\xcd:\xcf\xc6\x8e\x0bx\xd4\x0eG?\x023\xc4`g\xeb\xb5\xf0\x12\xb1h\x7f\x0cX\x1d\xb83hc,\xda\x88\x16\xc1e+\xe2S>\xb8\xf8\xb0}\x8a\x13\x1d\x1d\xd8\x17\x84\xb1G3\x97\xbb\xde\xc0\xc6\xe5\x14\x87\xdbR\x9e[K\xf2\x82\xf8\xc5\xb9\xb5\xbd\xbd\xec\xea\xb8 \x1b\xfc\xd9\x121+\xd0\x8fRN\x9e\xad\xc1a]\xa6\xfe\xcfE;\xe7\xb3\xf5\xb9\xd5o\xbd~\xc4WV`\x1f\xee\x0d\xc9\xbaC`\xd8O\xfc\x1a\x89\xb1_\x0f\xc9\xaaC\xf2e\xcaW7\x16\x83\xa1\xa9j\xa56%\xfeMp\x14\xd48\x12\xab\xde\x97\x12\xb7\xd7Y\xd8\xed\x81\xa2^\x1aL\xd1\xf8\x90\x04\xb8A\x9a\xd6\xdcn\x0e:\x084\x9a\xb3%\n\x18\x96\x08\xd9@\xc6\xbaeWD)\xaf\xbe\x0d\"\xf0fH\xd8\xb5\xc7b\xd8\xcf\xdc\xf3\xf2$a\xfes\"\x9a\x9f-\x19\x89x4Zi@\x9f\xad \x8b\xd6A\xc2#\xe0\xab\xc5\xa2\x06\xc9^\x1e\x86\x04\x82\x9a\x92\x15KS\xba`\x84F>\xa1\xbe\x0f\x11OhH\x96,\x8c\xe7yH\xaeh\x12\x05\xd1\"\x1dc\xda\xe2,L\x99eQ\x89>\n\xcehV\x1f\xa6s\xbb\xe0\xc3\x83\x9d\x86f\xbb\xd5\xa1\xc8\n\xbf<\x0f\xff#}\xb8\x18\xf6\x13\x1d\xeau3\xf3\xb6\xb7\x9b\x01\x1c\x88d\xfa\x07\xd2\xee\xe1\x808\xaf\xa35M\x02\x1ae\xe4\xa7\x80K\xe1\x15b\x00\xd1H\x91\xf2\xact\xd2\xec\xcc\x1f_\xf1\x1d\x828Hi\x02\xea\xd5\x87\x89\xd0\xa4#\xa8l\xd8A\x95\x13C}L\xbaE\x91\xf6\xd1!\\k\x83<\xb04\xaf\x9a\x0c\x86\x98\x8d\xff`Hr\xd1QO0d\xa0h,\xc5o\xa2\x7f\xdc\x8d\x86\xe4\xe9\x90\xa4\xd8\x01T\x1c>s\xe3;\xcf\xc9|4z> \x01\xa8\xfc\xcd\xe6\xe7-R\xa2\xeaR\xb3\x99\xdd\xa2\x0b\xcf\x1c\x8c\xde\xbe\xe5\x8a\x06\x8b\xae\x8d&C\xa2E\xbc0U\xe4\x90\xec\x80Nvy|F\xe4\x05I\xe0\x86R\xe9\xd2\xb9l\x16\x9dK.~\xf0\x1c\xa7b\xea1V{o\x99\xc6\x9a\x96;\xe6\xc9\xa3.{d\xac\xab\xa6\xec\x06\xd6\x11w\xb3AE\x90u?\xad\xdb{\xba\xffo\xd1\xbcF\x88t\xd9\xbcI#\x02\xbbB7O\xea\x88\x82vK\x07\xba\xfa\x89\x9e\xad\x89\xcb\xca \x8eA\xc3\xb7\x91\xbe(\xe2\xa84D\xac\xd3\xd9\xb9E\x9e\x91\x835\xd0\xc0u\x0c\x1b\x0c\xa0\x88sP\xe0\x83\x8b\x00*\xe5\x13L\x9c\xfc \xd1\x8e\xc6q\x9e.\xdd\x1c_\xbb]\x06\xb4\xdd\xbb\xae>\x06\xba\x7f\xf5^\x14Hr\xeb\xa0.]%\xd5\x9d\x1aDj^` 3\xd9\xfe\xba\xaa\x9e\xc6\x81\x9b-\x9f\x8e\x88\xdb\xdaM\x1321\x1c\xe2j+c\xb3\x83\xaay\x8f\x8c\xebdx\x95\x14i8\xd3\x05\xd4>R\x8f\x14\xb9B=\xacR\x0ff%N\x943\x81\xa0\x9c\x90\x03Q\xf5!I\xc6?\xe4\xf39K\xc8T\x99}\xdaX\xb3CB\xc74\x0c\xb9\xf7)J\xe9\x9c\x15\xf0\xd5A\xee\xbd\xbb \xa9;\xed\xd21\xca\x91\xc3`]h\xa4+e\xe4\x06\x04QL0\xdc\xc6\xb8\x11h\"\xb3+\x02z\xdez\xe1\xa3\xba\xe3\xc5\xc7=\x1e\xdf\xb8\xc9`h\xf52\xf7uP\n\xf2\xdc\xc9\xde\xa3A\xe1\xeek\xf3-\x80\x0c\x88q\xe64\x1bi\xf4\x1d\xd9\xe9\x99TP#\x07\xe4(I\xa8\xe8\xc5\xa08\x99\x9e\x0fH6\x8b\xce!0|t~\x1f;\xa2\x13\xdfO\xf6\xefr\x1c%\"\x13P\x9d)+\xbc\x9f\x96\xed=\xedt\xdcqO-\xab7+\xba\xff\xa3C\xa3M\xfb\xa6H\x14\xabQ\xdd\x05\x16\xc9\x8a4\x82\xd5B\x13\x03\xcf\xccv\xce\xe5\xa9\xa0\x8f '\x88|v\xedH\xcd\xe0d\x0co\xd0\x0e\xf85$\")\xce3\x95\x14\xe7YeSm8\x93\xbb\xbb8\x93\xb0\xff\xb4N\xae\xabS\xfb)\xee\xdap\xff\xe9\x1e\xca%\xec?\xad\x9f\xf2b\xd4\x9d\x99D\xb8\xdaQ\xc0\xb9\xd3d\x19\n\x98\x974cu\x00\xcf\x04xK\xe3z\xfe\xdc\xcc\x7f\x07\x8eD\xea \xb1 \xf2\x91-N\xae\x1b\xb5\xf8&\xc8)\xcb\xea\xf9\xcbJ>Lm\x1dd]\x01\x01\xe9_\x1dde\x82\x00\x86\x91GF\x1dnQ\x1b\x14\xfaS\xc0\xae\xea@7&\xd0\xab\x90\xd3lo\x17\xea\xac\x03^6\x00\x9f\x01\xd4\xb1\xbbA\x1d\xe2\xef\xc4Z\xd3\xde\xc65\x89\xbf\xbb\xbd\xbc\xe7j+a1\xd6\xb7]\xa9\xfb\xb6\x1b\x90G\xf8R\x9d<\xc3tk\x04\x1b\xdbzH\x90\x9aL\xcd\xc9\xb8\x143;-\x91\x0c*^\xf5\x9aHH<}<\xfb)\x83\x07\xc1~\xe0\x00\xa6\xbb\xbf\x06@\xcd\"V\xb0i\x01\xbe\xf3\xf0\x18`\xdd\xbb\xc5\xb2O[93\xbd\x04,\xab\xa4{\xe3j\xd6h\x7f\xa76\xb2bYL\x9e4\x97\xc4K\x9a\xb1q\xc4\xaf6\xc5:\x9a\xdeA&0hj\xbf\xf5\xe9\xfbZ;\x02\xb5\xf9 \xc8\x01{\x8e\x88K\xc9\x08\xf5O+\x98L\x88\x86#\x0e\xa7\xef\xc9\x0e\xf6\x15\x0d\xb7\xbd\x9d\x91\xef\x0fHapnx\x8e\xdei\xaa\xd4}\x95\x1a\x82\x19\xae\xd7W\xdb\xb8\x9a\xcd,j\xbc'\x89\xe1\xe4\x11.\xe3hluEn?\xc3\xc9\xed\x06S\x9a\x93\x03T\x0d&\x85\xf4\x86\x16L\xd8}\x95Y-\xe0\x011\xde\x89G@ \xdb\xcd\xe0\xf0\x92\xb1\xbb\x80\xc6L\x95\xd6Os\xd8\xc5\x94\xa0\xf3[\xd5\x0c\xc9\x06$,\xf1\xb1\xe6|\x80D\xcafQ\x1d#[\xa8+o\xb3\xa9\xda\x7f\x86\xc7\x93\xd8\xdb\xe9\xbe\x1a\xb7R\xbc\x05\x08v\n\x13\xe3\xfb\x18iG\xf4\xbahU\xa1\x90\xfc\xaf$\xbf\xa2YPeL\xec\xbbR\x14\xd9\x85\"\xbb\xe7\x16\xc5\x10\xa2\xe7\x85\x1aW\xd6\xda\x9f;\xea\xe6Ip\xdan0\x1a\x81mu\xd1\x06\xa9Y\xcf]\xf3`\xcd\xe5U\xb4l\xfc\x0b\xb2g2\x06T\xdak\x81^c\xb1p\x05\x95A\xb6\xb7\x13\x08\x16h\xc3\x12\x9aP\x8ef\x89E\xf5\x1d\xcc\x95\x81\xdcNe4\x8f\xa6\x92\x92U\xb8V\x0bip\xeb\x83\xbeyp\xab\x95fa\xc2\xf7\xf6m\x11\xe5\xfap\x83\x81\xab\x83='bS\x92m\xe28\x1b6\xbd+\x12\xcb\xfe3\x1c\xcb\xed?{j \x1bWo+\xd8/\x03j\xf2xH\xaa\x8e\x8aB\x9a.e(\x882\x91\xe6\xd9\xb2\x9a\xb2\xe4i\xcd\xfd\x8f\x18\xa4&\x8cR\xb0\xae86Jku\xa5\x8c&^-\xed\x1f9Knj\x1f\xa0\xd9\xb2Y\x9dH\xad} asRs)T.\xb2l\x0c!P\xc9\x01\xb9\x1c\x92l\x9c\xb0\x94\x87\xebN\x97\xaejr\xc1\xc7\xdd\xd6\x04\xfc\xba\xe9\xa2\xa6\xaf\x9a\xafF\x95r\x1f\xf5\xac\x98\x91C\xb4\xf2b3V<\xac\xc3g\xe6\x0eRIl*y\x16H}.\xad\xd7D\x15\xdf\xf9\x01D\xe0\x96_\x81\x18\xcb\xa6\x1f\x0f\x99\xac\xafZ\xaa\x0d\xfb\x94\x88%\x15TW.\x85\xd0\xc1\xee\x8c\x8e~\xdf\x19=\x1bo\x8f\xce\xb7\xa7\x83\x87A\xf3\x98}8\x9d\xed\x8c\x9e\x9d\xff\xe5\xcf\x0f\x9bG\xed\xc3\xbf\xbb\xbf=\xfc\xed\xe1\xa1{\xb8\xf5\xdb\xc3\xc1\xec\xef\xbf\x1d\xfe\x96\x9e\xffe\xe0\xfev8\xfb;\xfc:\xac\x97\x02\xb3\x04\xe7\x0fgH\x9c\xaf\xe2\xcf\x17\xf1\xe7\xb7\xdf\xc4\xdf\xbf\x8b?\xff\xe5\x9ck\x03\xa1\x99\xf3B\xa4|\xef\x0c\xc9w\xcew\x90\x07q\x80E\x81\x04\xfeF\xf07s\xce\x07\xcd\xd3{\xe6|WV\x15\xd6\x00\xe6\x00\xf0\x1f\xa2\xf8C\xf1\xe7P\xfcy.\xfe\xfc\xaf\xb2\x90W+\x14C\xa1\x12\xfe\x7f95s\n\x1fFd\xb6-\x87\xf4h\xf4\xb7\x8b\xd1\xf9\x1f;\xc3'{_\xeb\xa3\xb0T\x83\x8f\x80\x0e\xdc\xf1_\x06u\xf85ja\xf8\xdftM\xa5!\x1b\xce\x958\x06\x80\xd3\xe0(j\xd6{\xabo\xff\x89\x05\xfa \x88\xcb\x84V.r,\x86\x89s[\x99\x05\x8f\x976\x83\xc8y`\xe3\xdf\x1ch\x84\xd3\x92\x99Zs\xe7-%Uk\xacEE\x83:\x87\xedF\x9d%\xfb\xe8Yri\x93q\xfc\xff\xec\xbd\xeb~\xdbF\x928\xfa}\x9e\xa2\x84\xec8@\x08R\xa4\xe4+mZ\xeb\xc8\xcaF3\x89\xedc\xd93\xbb\x87V\xf4\x87\xc8&\x89\x18\x048\x00\xa8K\xc6\xdeg9\xcfr\x9e\xec\xff\xeb\xaa\xeeF\x03\xe8\x06@\xdb\xc9dv\x07\x1fl\x11\xe8{\xd7\xbd\xab\xab\xe8\xfa:\x17<\x06a\xa6\\\x8d\xc9\xbc\xa2S\x95\xa6\xe4\xb5\xd2\x1b/4R\xa7\x94(\xb7\x1a@\xdde\x0e\xc7\xa1Q)I\xe9\xdb\xec3\xe2\x12\xbaF,-)\x05^\x05i\xb0f9K\xe1\xebm\x1a}M\x19\x05.\x19\x04\"gU-\x81\x80\xc9Q=,<\x01_.\\\xe7\xc81(s[\x94Q\x8b\x14g\\h\xd3\xea|\xe5xp\xc4\xe9\x02\x8c9a\xa8\xd7\x8f(S\xc6&\n\xf3\x9a\x97z4\x1d\x9e\xc3\x04\xff+\xaeV\xbd{\xb7\xbfD\xf2d\x18\xf0%\xa6\xfb\x99@4\xf89 \xe3Z{|\xf5x\x91\xcbA\x9e\x86k\xd7\xf3a\x0fS\x8d\xcb\xb4\xc54\n>\xe6\x06\xf3\x17\xef\xe7\x02&\x90\x91#\xc3\xa5Ew\xbd(\x07\xf0\x16\xcc\xff\xb2\xcc\xf9/\xeb\x02\xc3\x05J\xc1\x17\\\xf8>\x92\x81\xd0\xa4\xd4\xc1\xdfV\xa4\x8e\x1c\x8e\xe0V\x80\x9bV\x18\xc3\x96\xe6\xa9;\xf2T\x10n\xe3\x07(\xa2\xad\xc9N\x1c\xa7\xd2\xc5\xdf?\x8a82e\\\xac-\xfe5\xd7\xd6\xcd\x8b\x82\x91\xffl\x8by\x02\x13py\xe5\xeb\xe9\xf0\xdc\x1b\xe4\xc9\x0f\xc95K\x8f\x83\xcc\xe8>^\x15\x08O|\xa0-\x15\x13\xbb\xaey\x1f@m\xb4x\x19\x81\xab\xa6\x18\xc1\xf0r\xb0\xc6H\xea\xfb?q\x96=\xfd\xe9\xdf\xdf\xed\x9f\xf7\xfe]\xfc\xbfo\xbc\xef\xca\x87\x8dn\x83\xfb\xfb\x0e\xc2\x8e\xea~\xe8\xc3\x81a\xd4{7\xd4\xdd\x9d;\xb0\x9e^\xe3\x8dZ\xb74\xec\x03\xaf&\xd5V#\x91\xd6\xe7\xb0\x87m\xf1-,\x9a\xdf[N\xaf\xcd\x97t\x95&}\xe6\xc3\xb1\x8f\x9e\x87\xfd\x91\x8f\xde\x82\xc3\xc7\xf0\x0c\x9e\xc0F]\x85zfNP\xc6\x1f\x81\xec\xeeK\x1c\xbeD\xf4\xcd\xf4\xd9\xb9\x88/\xdc'tz\xcf\x87\xf4\x12\x9e\xc0{z\xcd\xfb{iP\xaa\xb8^J-\x1e\x13)\xa1\xcaGpY8\xffpJ\xf2\xef\x98\xa9\xbb\xf6\xd2\x87\xf7\xa2\xdf3ZO\xbcw0\xf4\xe1\xd8S\x90\x81\xaf\x8e1\xa1}YM\x98\xb3Y2go_\x9f\xaa E\xee\x99\xe7\xc9\xb5\xb1(\xbd\xda\x82-\xba,\x18_\xf2\x97\x8f\x8bi\x96\x17n\xf1y\x0bG\x15d\xb1K \xfce\xddG[\x95\xf7\x95Uy\xef)\x12\x94f\xec\xfb$\xcb]\xaf\xae\x14\x95\x7f\x7f\xf8\x00\x8e%\xb3\xd6+<\xd7&\x9c(U\x12\x8e\xe7\xce\xb9\xe9[\xe9\x974'\xf4adP\xd5\x11\xec_\x99\xef\x81+\x00\x7fS\x1d\xb2\xa0\xec\xfb\xef\x06\xfb\x9e\x0f?r\x82\x83\xbb\xe8\xc3\x1b\xb9b\xb4\xa1?6\xee$\x88Y\x9e\xc2\x04\xdeL\x9f\xb5\\\xa2?Et<\x15\xd4e\xdezq^\x0d\xffgA\x85_\xd0\x10_\xc3\x04N\x15\xa0\xbd\x80'\xf0\xfa1\xbc\xe0\xa3<\x1d\xccVAz\x9c\xcc\xd9\xb3\xdc}\xe1\xc1S\x18\x1d<\x80#\xf8\x19z\x13pn8\xcf\xc5?O\xa7/\x1a\xc6\nrY\x7f\xee\x97\x8b~ \x19\xc2\x198\x1e\xf4\xe0\xd2\x80\x15\xcf\x8b\x12\xedc\xb9LY\xf0\xbe\xb1T\xdd\xbc\xd4\xfc\xa5\xfe\xd6\x88GO\xe1\xe0\xde=\x99\xeeA\x1b\xbd\xe3H\xc9\xc0\x86\xe8eV\xec\xc3+-vvQ%\x1d\xe4\xc9\xb3\xb3\xe3\xd3\xd3\xf2\x17\xd3\x05b\x0e2\x7f\x93\xbd\xa0\x15\xe6\x08\x9c1\n\xa1\xea\xcd\x98\x83\xbeq\xbe\xdfu%D:\xe9\xfb\x0ez\xf07]\xe8\xeai\x8d\xf0))\x01\xc8\xba\nRb\xf2\xcd\xeb\xdb\x07\xce\xbb9\xccp\xea~)\x08\x9d\x06H\x97^+\x1f\xbf\x9a\x9e\x9c[.E\n:\xc5i\xd6\xac\xe06\xad\xa4\x8a/\xf5/\xbc\x8e\x95L\xf1\x8e\x05//\xb8\xd1/\x8d\xa8\xcf\x1b\xfd\x96\x8b\xd8q\x8dm\xfe\xd2\x80\x02\xdf\"\xc9\xff\x05\x97\x05\xabg\xb3`\xc3x_\x8a\x17!y\xfe\xc5#\x84\xfa\xd6L\xde\xeb\xf0^\x97A\xffR\xe2\xad\\\x92/\x18\xef_\xb4\xbd&\xcb\x9e\x92\xbe\xfeR\xe1\x8aC\x1f\xfeR\x05`\xde\xfc\xf7\xe5\xe6\x8f\xaa\x88\xaf\xad\xe9\xf7u\xf1]u\xf7\xbdW\x11\xb1\x8b/RH)\xc6*\xcb\x94\xa4||\xe9\xd5G\xfd\xfd\x8eb\xfdeQR\xd3A8\xb1[NO\x10\x90\xcb\xb8\xa1\x82w\xab\xd2\xa6\xfa\\9\xabj62\xbb\x18\x0d\xc8\x04e\x05e\xd0\xea\xd8\x04\x8d\xbf\xaa\x88\xb54\xc1&R t\xaf\xbfA\x0f\xfe\xda\x80\x89\xba\xba&\xf43\xfc[\x1a\x16+JP%^p\xdd\xc8i:eU\xd4\x05\x05P\xc3\xa0\x992~\xe2?\x06Lc\x9e\xa7\xc5\x199|\xb6\x1f\xfa\x9c\x88\x92 \x7f\x02\\N\xae\x03\xae\x8aM\xac4'\xec\xbbNhc\xf3&\xd4\x0b\xa6Z\xcc\xe2\x95\xadPh *\x1b @\x96\x87YP\xed#2\xcb\xdd!\xf5\x14+\xe6\x18#\xc1*\x9c\xd1\xb0.\x86\xe0p\xberD\xc0\xc7r]\x0ex\xfc[\x0f\x8f\xad\xb6r\xe2\x18\xa8\xabR\x94/\x14-\xca\x16ij\x0fB>Ht7/phz\xf4\xd5y)ZOSLQ#B\x96\x89\x8a\xc7\xe5E\xec{\xab:q\xber|p\xfexp\xe8\xe0\xd7\xd4FEL\x87<\x96\x83\x18\xdc\xa2\xf2\xe1\x8b~.\xe3)\xba\xd5\xd2\x97\xe1\xf4\xc7du\xac\x18\x1d\xcd6\x91\xdcl\x16\x85\xe24K\x1b\xa1O\xd4\xb0\x81\"\x97\xe2\xb7`\xbb\x14\xc2\xa5\x8aQ\x9e\x8f\x14e\xf8\x18\x02x\xa2\"\x84>\x86\xc0\x9ef\x1d\xfdO\xa6\x81\xc9\x83q\xba=\x17\x086\xdd\x9e7\x8c\x8eB\x93\nQ\x02\xbd&V>\x97\xaa\xc9\x96\xc89H\x11\x0cH\x1d\xf5i\xdc$\xae\xcb\x0eL\xe1\x1c\x85\x82\x90\xd4\xba\xd1\x9c\x93\xd5\xc3\xac\xa2Uu\xf8\x18\"x\x02E\xd6\xf9\xa8Y\\\x9c\xc1\x04\xb2id\x11\x17\x1d9\x16B\xb5\x19\xe1\xf1tF\xd1\x08f\x06\xf1\xd5z\\\xbe\x9c\xc6jf\xe2:zI\xc0\x88\xcb\xd2E\xacNN\xeb2\x86ya[6\xadXW@g_\xf5\x8bHU\xd3\xa2\xa3\xb4\xbe\x9c\x16u\xcem+Z\n\x96T\xdd\x9e\x0dm\xcf\xa6dB\xda\xb4\x1b\x1e0\x04\xf1t\xd3\xa0\xcc\xc7\xd39\xed\xc8\xdc\x12K\xcc\xf8\xb6\x11L;l,\xa1\x82f\x95-\x16\xc8\xe7\xb8\xc09\xf8\x87\x0f\xb0./\\i?\x99\xfaQ\x9f\\CD\xb7R@D\x97U\xc4\x16O\x9a\xf4\xf7\xb9\"\xb0\xd2X\xee\x9e\xcb\xa4\x8a\xb8\x1a\x90=\xc0\xabEx\x92O1\x83\xa2\x162*V\xd2E]V\xd6\xaf=$\x07\x1c\xa8VB+\\)\xe3\x03~]\xe9\xfe\xf8\xf5\xcf\xa5\xf5Y c\xc3\xbe!\xdf\xbbmC\x94\xf0\xcf\xc4\x9f\xbcM)\xff3\xfa\xcb\x17\xd8G4LL\x93+\x0b\xb14\x922\xfc\xc3\xd7\xb1tR\x999\x13\xeat,}+\x18\xfeQ\x9a\xc2\x87\x0f\x107H\xff @\xfc\xaa\x8c\xe8\x16\xc1R>x\x04\xd8\xa2\x03\xf0G\xd1\x90+\xe8\xc1m\x87\x05T\x18\xa1y\x99\xe8\x02\x91\xa2\xd4\x9f@\x83\xe4IU\x99\xce9\xe2(\xa1x[H3\xf5\x05\xb8(\xed\x173\xb6\xc4:\xb5t\x0d\x13\xb8\xe0\x8d\\\xd2\x16a\x9bD\x17E\xedz\x9d\x13\x98\xc0u\xfd\xf5MmR\xdad\nL\xe4\xfdL\x0d\x11\x17\xcf8\n\xafJ\xb4\xa0<\x90z\x1b\x1a\xb9\x06:\xfc\xd0X\x8bA9?\x13\x1c\xa5\x84\xa7\x1a\xdc\x92sN\xb1\x08\xae\xe0\xe77\x1c\x81\x8f\xe8\xbf\x89\xfc>\x86\x1b\x85\xb0\xf4\xca\xf34t\xe2\x0d\x97YM\x99@P_\xac\xdc5\xabu\xbd\xa2\xaeW\xd45\x93]\x17\xb4\x82\xa9\xae\x15q\xc2\x0c\x7f>n\xedu\xad-D\x135+^\xef\xc23\x13\x01)\xca\x90R\xa6\xba\x8e\x15\xb6[ B\xa9.\xbe<\xd2\x7f\x8c\xb5\xba>t%T\x1c\xbc*WY\x903\xf0\x8d]\xa9\x13[<\nso\xe8*\x8b\x0f7\x83M\xb2\xe1\x18\xc9\xdf\xdcH\x17\x96\x95\xd7\xb5[K\x7fx\x08\xffb\x1bE/\xd3\xb71Et\x9e\xbb\xb2\x19\xa3|\x8c\xe0\xe7\x95\x17M\xad\xfa\x8d\xe4A>\xb8\xaf\xb8\xd2\xbc\xe7\x16@H\x7f\x15\n\xed\xbf;\x1eyD\x17\xdf\x04b\xfc\xbb#\x8e\x92\x14\xf1~U4\xac:+\x0d\xe1U\xc1\xfd\x1a\x88`\x87\x85\xf2A.\x89[`=\x8eF{/\xe9?\xdf\"E\x93\xb5\xf2p\xa4\x13\x901g\xa2\xa8\xb1\xc9\x11\x1c\x15\x83\xc1\x8f\x9f*\x02\xee\xdd(xQ\x93\xdcT\xbd\xf6J\xbd\x8a\xb1\n\xad\xb5\x18D!\x9dJ\xd2\xd1*\xe9+\x99\xe5\x98v\x1e\x8dw\xfd\x91\x87^\xb0\xefiA\n\xca.\xff\xba)\x0c\xfaB_w\x06\x84e\xc7\x88q\x03\xf9\xcb\xd3\x10\xf0X\x9c\xef\xfa\xf0\x12\xfb\x92\xb2\xe6Kx\x8a\x12\xe8\xcb~\xdf\x03\xd9\x0e\x1e\xc0\xdeL_\x9e{\x9c\xd4!L\xcd\x98\xfbR\xdc\x7f+:\xe0J\x7f\xf9\xb3O\xa6\xe81<\xc3\x81\xd5>\xf6\xfb\x06Z\xbcG\xe7\xd5'\x16\xc3\xf7c^\xed1<\xf34*\xcb\xc7Pi\x89\xb2\x10\xead\x9a\xaf\x95\xb8\xfb\xf0\xf0\xfe\xdd\x07fM\x8ck\xfc\x87\xf7\xcd\xdff\x18f\xdc\xf8\x89\x83\xf9\x81\xa5\xda\x867\xf9\xd0\xfcm\x0e\x13xP\xbd\x13'\x1f\x8ez\x0f\x0e\xcc\xdf\xb8n9:\xb0\xb4\x8a\x91\xf1\xfa\x16]s\x89~\xc97q\xbf\xbfo.\xc0\x05\xa1\xfd\xe9O\xefn\x0e\x86\xfdw7\x0fN\xce-\xe5.\xb1\xdc\xbb\x9b\x83\x93w\xdb\xc3\xe1\xf0\xe0\xdd\xf6\xbb\xef\x86'\xfc\xdf\xfb\xa3\xf3\xfd\xa5\xb9\xd2\x855\x8f\n\x7f\x92+\x96.\xa2\xe4z\x0c\xceK\xf5'Em\x8c\x19\x9bgp\x1d\xceY\na\x9c\xb3%K3\xc8\x13\xd8\xa4\xc9\x8ceY\x83b\xed\xc4I\xde\xbf\x0c\xb2p\xe6\x8c\xc19\x8d\"\xb6\x0c\"\xd1*\x17\x1dn\x1e\x0e\xc1\x8d\x93\x1c\x02\xc0R\x80h\xb4I\xc28\xf7\x9a\x9a\x0d\xe3\xab \n\xe7}l \x9b\xa6\x17\xd4\xb49\xf1\x9d!\x9d\n\x08\xc55\x82>\xcc\xcc\x9f\xb9\x8e\xfac\x90\xaf\x06\x8b(\xb1\xe5\xae\xe4:\x01\x19\xb5\x07\x8b4Y\x1f\x0bo\x1a\xcd\x9dX>\xca\xad\xf8\xcc|<\x00*\xc6\xfe\xeb ^\n/\xdc\x8b)3\xdaE\xed\xad\x1f[o\xd4A\xd5\x1e\xaeB\x85\xa2I|z\xfe\x18b\x0c\xc4\x9eR\x84X\n]n1hI?\xe5\x9d\xc6\xf6\xbeql\xc5\xb0\n\x89\xc2\x0e\x07\xa9\xe1\x00P}\x93\x02y!\xef\x82<\xf8\x89\xb98\xd5\x03\xf4\xfbC\xceON=)\xf4\xe0\xd8\xa5\x13Su\xe6r\xe9s\xc9\xd6S6@\xca \xeb\x15N;;\xcd\xfe\x99}\xdf\xd5\xb6P\xac\x06\xda\x0e\x1f\xaf:\x0d}\xe1D-\x05\xef\x84\xae\xa9\xb9\xa4jk\xee[I\xaf\xe7y\x1c\xb5\xee\xdd;xt\x9f8\xc7\x93 \xdc\xbb\x7f8z\x84R\x0b\xaf\x08G\xfc\xc5\xc1\x10\xe3\xa2\xdc\xbf{ot\x00\xe24\xad\xde\x96G\x01\xce\xb8\xbc\xea\xba\xa3\xe1\xc1!\xdc\xe1\xbb\xf7\xe4 \x8c\x86(\xc5\x88w1\xffq\xff\xde\xbd\xc3\xfb(X\x89*9\x17\xa0\xb8r0\x06\xf5\xe6\x0b\xc2\xd2K\xfbj\x8a\xf6\x10\x13\x9a\x8f\xe4\xe4#O\x9el\x00\x05\xfa\xbd\xa1\xa78\xd7{\xa0\x0e}\n\xa3!\xdc\x01\\\x9e\x0f\xb4\x1dB\xa0\xa1\xb5\xff\x00b\xe5\x18\x1d*\xf2&\x0c!\xcd\x01\xcf\x02\x05\xb4\xed\x08l\xaf\x1aQM\xcd\xa5\x07\x07\x07\xd0\x83\x07\xf7\xe0\x1bp\x19<\x81\x83\xfb\x1e\xf4\xc1u\x87\x18\xcd\x0c7\xfb\xden=\xbf\xb1\xdd<\x90\xcf\x95\xb8\xfd`I\x89\x82\xb8\x80\x98 Gp\xe22\xd8\x879\x06\x95\x03\xbe\xae\xc2G\x81\xde\xe7\xdec\xdc\x8fk\xf8\x06\x16\xf8\xf91G\xe4 D\x1e\xae6\x95\xban\x06\xbb\x13\x97\xe3\xbe{\x8d~3\xf0\x0d\xf0*._\x99\x8d\xb7\xdb\xc4\x7f\xb4\xc3\x98\x86\xdaz\xce\x18L\x075\xf7a\xe9\xc3-9\xe2\x98\x8c\x9a\xf2\xb9\xd0I\xb6\xb5\xd4\xb5\xf9\x16\xbe|8\xbf\xba\xb2\x7f>\xae\x1b\xc8\xe4\x83\xfb\"(\x85\xeeA\xbd\xf6f\x82\x82\xd0\xf3\xe1\xc4\xbdF<\x86\xa7\xc0'xc\xe8\xea\x86\xf0\x9d\xca\xf1\x89\xfe\x11\xb3\x03_J\x0b\xd1u\xaf\x87\xa1\xa7n\xba\xfa\xfcA\x81\xfb/\xdd\xcb\xddp\xfc\xf4sq\xdc\x87\x0b\x9fC\x9b\xb8>QMr!\x1f\x04\xccK\xe9\xc3\xf5\x0c]\xb6\xa4\xb0\x96#\n\xa3\xa8$\x84\x83U\xc9{\xe1\x92c\\\xe0\x11tN\x83s\x8e\x9e\x02\xd5\xde\x13j\xdd\xb85\xaf\xa0R\xc7)\x06{\x99\xc0{\xd5g\xa2\xd5^{\x84\xd9\x97\xed\xa8\xc5\x91)k\x19\xdcS\x91\x81\xfc\x16\x9e\x88,\xe6\xbc\xd6m\x837\xa8h\xba\x0fy\x81\x1a1G\x0d\xf7\x02c\x82pBn\x02\xda\x98C\x12U\xe4\x84\xfe\x82\x96rk\x1a\x9f\xb5o\x10\xa6\xc7\xd2\xea\xe2\xf8{\xbd\x18\xa1\xb8\xde\xef-P\xda3\xfbb\xc9\x07g\xc6IK\xec\xa3\x8e\x1a=\x96\xc8\xcc\xd1q\xce\x919\x14\xc8<\xe7\x0b\x17j\xc8<\xc70(\xdec\x98\x0bd\xe68\xb8\x81>\x87<\xa9\xe8,\xfd\x02\x04^\xb9K.\xf3\xc2\x1f98\x0e=O8\x15\x9c\xb8\xc7\x0dF(O\xf9\xb4\x13OAj\xafW\x97\xf0\xf4\xe7c\xaf\x17\xf3R\xf5\x84S\xd0\x86\xc7\xef\x9b\x84\xa4\xea\x9b\xadU\x17\xbebi\x16&\xf1\x18\x1c4\xe6X\xb4\xd0\xed,;0\xe5\xb2\x96\x0f] \x1a\xc33;\x9b%\x1f\xb01\xbc4O\xd5b\xb4\x10\xed\xfeh\xfe,\xdb<5\x7f\x16.\xf6\xe3\x8e\x12\xb1\\\xd8\xee2\xb4V\xebv\x90\xb3,\xa7\x98|\xceM\xdc\xef;\xd0#\xd2iJ\x99-\x9f\x8f\x16\x02n\x9b\xcf\xdb8\xa4\x19w\x1b\xdfg\xcdh\xa9\xcd\xe8GW\xe6\xa6\xb9[\xb9k\xf8i\xf3\xab\x83\xac\x0fZ\xbeD\x94n\xac\xa6Y\xf9\x88qn\xeb\x8d\x15\xc1nP,g\x14\x02\xd3\xd5c}$\x15\xffC\xdd\xe3\xcf\x90\xe6\x86\xffy8\xb2d\xbb\xe9\x14\xdfC\xef\xbc<\x1f\xe9\"\xd8\xb6\xabb\xbe\xa6\x0c%\xe5\xb9\xf8\x95\xe6\xc9\x91\xaak\xf3\x16K\xab\x88\xf58i\xeb\xec\xc56\x8a:v%\"\x85vjR;1\xde\xad\xf5\x1dC\x89u\xda\xcb|@\x84 \x0d\xf8\xf2\x16z\xec>|\xf4\x88+\xb7\x03\"Kd\xdd\x97\xde\xc9@q\xaa\xba%\xf3.\xf7\xaa^+\x91,m\x8a5\xd2\x12\x99J%\xb1\xa9e\xf0\x81\x96\xb0\x87>\xd4l\xf8x\x84\x81G\x89w\x1cbzxC\xd8\x99\x18\xf2\x8a\x07\x86L\x90\xa19M1zC\x0c\x853D\xe5\xc89\xa8\xb7\x8cqE\xde\xf5\xf6+\xc29\xd3\x0ckU;\x8ct\x01\x1d\xb1\xc3\xca\x888\xac;1\xe6\xa3\xd1q \x1c\xac\x83\x9b?\xb3[\x14v0\x85\xa9zch:\xd2\xcdW\xa5\xaf\x99\x0c\xf5\x19I\xc9 \x13PV\x1bQ\xd61J\xa4\n3\x8c,\n\xbd\x9e1\x833zLJ\xa9{\xe5\xa3\xc9\x9eMg\xc5\xfd\xff-\xfaQ\x0fm\xc6\xc55\x17\xaf\xd5\x81\xa7)5\xc6\x1a\xed\xd7p\x04\xee\x02\xcb\x16gTk!D\xa9wk!\x8c\x8eEY\xfa\x8c\xc7\x94s\xf3\xed\xe1\x85\xe7\x83\xe5b\xf1\x86k\xd6n\xe0\xc3\xdc\xa3\xb0\xd3\xd39\x1e\xb4\xf3\xffI\x16[a\x1cTr\xe0\x9c\xf2\xff}X\x9d\x17\xafV\x16\xec\x87\x02a\x82\x02\x0f\x8a\x89\xe3\xf9\x97\xcc'6\x083\xfc\x9f\x83e\xab\x8by9Q\x90\xb8\xba[CJ\x19&\xb2\x1ecgw\x02\xa1\x8f9m\xf4IWYld\xf8\n\x030atO\x89\x94\xcdA>\xebpB\x95/)gTKm.)\xe5\xe9\x96a\x94\x8bE\x10e\xcc`\x8a\xa4\x06\x05>6\xe7B\xc9\xbe\x0b\xe30g$\xb1\xd0\xc1s\xbd\xbd9[\x04\xdb(ol\xc9q,@\xf3\xd1\xcc\xce\xeb\x84\xb2\x16sX\xb4l\xa7\x97\xbe\xc6\x0dA\xdef\"\x91\xc8\xb3\x1c\x7f\x1eA\xe8\x06(\x9b\xa8\x01\x046\xea\xc0I\xa4\xe1\x16F\xea\x06x\xb5\xc2\x90wW\x8c8qI\xe3\xe3\x9d\xf1\xbf\xba\x08\x92R0\x83\x9e\xb9Of\xb22\n\xa3/\x86\xc2\xb2\xd7\xe4c\xa9\xde\x1c)U<2W\xdc\xd24\x1bF\x84\xf0\xf2\xfb\xa2\x04\xe6`o&\xd6O\x0e\xfa\xeb`\xa3\xe5\x92\\\x07\x9b\x1a\xdb+\x9d\x85M\xcfKV\xcb\xe2\xb8%\xed\xf5<\x99\x035w\xd94\xe5\x05-\xfe*\xd5d\xa8\xa0q{\xcd\x81\xbfy\xbd\xae,\xf9O\xcba,\x99\xd7Y\xb6\xa1 \x97\xbfR\x1a\xd4\xda\xea\xef5\xeb*fb-\x9fn!0\xe5#\xc6\xee\x96\x82.\xe5\x82\xde\xc5\xec\x1ar\xb7\x80(\x97S\x8e\xcb0\x0e\xd2[\xc7\xf3\x8a\xd7\xcee\x90\xb1\xfbw[-\x07V\xa5\xe8\xde]O$M\xed$\xce^iY)\xcdA\xdd\x0f, \xcf\x0f\x87\xe6\x84\xe7\xf7;\x05\xf47\x1c\xc8(\xde3\x01\"\x9d1\x14\x19\x0bb\x91\xb1 uC7\xf6\xd0\xc2\xaa\xc4O_$ \xc6P\xacB\x17\x8e\xd1\xbeV\xb8\xe6 un\x81*}@\x9f6p\xc9 \x84\xbe\x8c\xd7o\x14\xc7`\xf0\x84\xe6\x81\xf0\xe0)\xad\x1a\xaf.j\xa5\x9eN\x14\xd4\x90\x13\xf4n\xc8p\xa5%\xfe5E\x84\x1f\xd57\xf3n\xdb\x86YfL\xb9\x16\xe0\x03\x84m2\x92\xde\xc0^C\xc3\x16\xed\nt2\x9b\x9bQ\xd0\xaa\xaf\xc8\x95-.\xfb\xf9\xb0?\xfd\x89\x02\xf2\xbd\xeb\x7f\xf5o\x7f\xbc\xf3\xf57\xbd\xc1\xbb\x9f.\xfe\xcf\x87\xff>\xdf\x0f\xa5m\xc5\x12\x88L\xfaw\xccVA\x1a\xccrtD\x81\x15\x0b\xe6,\x85E\xc8\xa29\xc4\xc1\x9a\x99\"h(\xf2_\xb2\xd2\x94\xd1\xda2\xe7\x8ef\x87\xb6iW\xf5msg\xa9\xb93\xc9 \xcc\xd4/f7\xba\x19\xc3F$Ak\x88I\x7fK\xbbqWL\xd0\xde\x16\x7f\xe6I\xcc\xc6\xba\x8d\xca\xe0\x10\xa8?\"6\xbb\xd9\xb0\x0b5Rk\x7fkH'%\x06\xbc\x1a\x849\x85\x88\xa7s\xf9)%/\xa5\xb7y\x92\x9e\xef`D\xab\x8f\x13\xe3\x97u\xda\xca\xc4\xbc\x95\xe8\x9f\xb8\x0e6\xa8\xf6\xfb\xe50\x81\x89\x0c>z\x12\xccV\xed\x81\xb1Us\xc1f\xc3\xe29%\xbb\xa9\x8f\x98n`\xa3G\xb5.\xab \x85\xc0\xd0]\x97\xbe\x18:\x98\xb3\xe9\xc8\xe4\x94T\xf4\x88{ \xc4\x93%\xcb5\xa1\xe4E\xb0f\x99\xcb\xbcz\xff\x9d\xe7:\xcd\x1b:\xef\xb4G\xa1\x9d\x9e\xb1\xc1e2\xbf}\x9b\xb1\xb9\x12\x1e_\xa5\xc9:\xcc\xd8 exC\xbaB\x9c\x9eE)\x0b\xe6\xb7\xc0\xffuL\x87jE\x8b\x18\x90\xad\xd3\x00\x83f[\x1e\xbb\x96\x83j\x0f\x02\x0e8\x84$\x8e\x92`\xde\x05\x05\xf8\xc3\xc5\xa6\x94e\xdb(\xb7Y\xe4\xb1I\xc6W\xa0k\x9b\xb1\xcb\x06X\xa1\xb3\x11\xbc\xdb^n\x9bI'_\xab\xef\xc2\x88\xbdFva\xa6R1\xca?&\xe7$I\x0f\x06|w\x9feZ\xb2c\x12\x97:\x8d0k\x826\x94\x9dj9\xef\xabn\xfdP\x99Q\x91b\xd8-\xa5\xe9l\x98A\xc6\x08t\xf5\xaa\x18\x82B\xa4j\xec4\x95\xa8)K\x05\xe2\xa9\x0e\xeb2\xdc\xd1E\x18\x87\xf9\xb7\xc9\xfc\xb6\x93P\xcf\xd7\x85\xaa\xf1\xb6N\xe3\x10\x19\x97\x91\xc6\xe9UL\x07\x01\x1e\x14\x0d\xbda7\xd8\x90\x9d\xf3i\x17\xc1.\xa3\x04\xc3\xda|\x1b%\x97\x9a~\x15f\xaf\xe4\xdf/\x17B^\x91\xed\xf3\xa2\x9d\xdb_$\xe9\xfay\x90\xa3\xf3\xf4w\xe2\xef\x8e\xfd\xc8\xe2\x9d\xfb\xa2\xcb\x05\x18\xcc\x15-\xaco_\xffp\xa6\xbd\xea\xd8\xad\\>M\x9d\xea\xd4{P\xa0\x0c\xe0\xf5d\xb9\xb4\xebJ\x07\x1an\xc1\x84\xe3\x8cL'\xeaC\x0d\x1a8\x1c\xf3\xf5v\xa7\xc6\xfa6\x97Uh\xbe\x07.\x1f\xbcXT\x1e\xf9\x87\x0f\xb0\xa7u\xd0\xb0f\x80WH+\xb2\xac`\x15\xdb8\xdbn\xb8\xa8\xcf\xe6\xf0\xad\x9c\x0d\xaf\xd9\x16\xfc\xada\x95\xecH!s\x94T\xb7\xd0\xe6\xe2H7(\x90Lf\x9ci\xbb\xce,\x89s\x16\xe7}\x1a\"\x1e\x1a\x9a\xb0LE\xc6\x11u\xb3Z]\x1f\x9c\x9c\xdd\xe4\xfb\x9b(\x08\xe3\xc7\\\x8c\xcfX>y\xfb\xe6\xbb\xfeCG\x05\x97-\xb0H\x86\x8cRo\x06\xbc\x95.\xdd\x18\xaayx\xd1\xf5\xd3\x91@\x8d\xa6qz\xc1f\x13\x85\xb3\x80S\xb6\xfd\x9b\xfe\xf5\xf5u\x9f\xa3x\x7f\x9bFda\x9bWgm\x94`\n\xec \nxI4\xa5\x95\xbf\xca\xeb9!\x8521\xef/\xf2\x1b[@j\xbdPy\x11\x0db\x90\xc8\x04P.\xd6\xa5=\x0dz\xad\xcd\xb6\xe2v\xa7\x9e$\x954`\xe1,\xd9r\x8d1\xc9QdS\xe4\x17x5\x082\xe0\x8bnC\xc8\x1d\xc6\xcc\xb1\xadj\x9d\x85BP-\x91\x97\x0e[\xac\xf3\xd8\x1a%8\x92;\xcfq\xd4\xbeO\xa5\xe5\x17X\xc7g\xebz\x83|\xc5bwk2D\x8b\xe1\xe6D\xfeZh\xd2m \x8ak\x05\x06\xc1Q\xda\xfb\xd85i\x88n^\x98\xf74Kx^\xb1\x84OQ\x956\\yq\xf3i#\xeb\x95\xda\x8b\xddU\x0b*+\xa6/D\xa7\x95\xfb\x0c\xb4\xe7\x00\xbe#\xda\x97\x91\xddB\xd1uQ\x8fj,\n \xae\x15\x9dt\xb4\xe7#\x94\xa8\xbah@\xd5\x9f\xb3$\xfe\x9c\xb6\xfft\xf6\xf2\x05\xf9qX\xa9W\xe9\xbdMY\x98Y-\x18\xf2\xcc\xc5U'\x80\x7f\xff\xe8\xa1\xeaP_\x7f\xa4\x15\xba\xb5\xc4x\xe6\x0f\x06\xf5\xddhK,\xab\xeb\x0d\x92\xd06%\xb7\x85m*S\xed\xccR6gq\x1e\x06QFn\xdf\xc5o\xaeF \xf9\x00\x8a\x00\xb7\xe2\x05\xa1X\xe22\xf9FE\xfe[\xb3|\x95\xcc\xb11\xfaS\xbe'\x87\x19\x86\x7f\xf8t*\xaa\x1cx4I\x18\xef\x1cC\xe9\x9d_\xb57\x18\xf6P\x13\x0ci\x96\xca`i^~\xc3\xec\xf3\xd2o\x19\x98\xb3\xf2\xceI\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedfc\xcf\x04\x00\x05\x1a\xdc*\x8f\x0ftF\xef\x8f\xb8\xbcit\xe7\xfb\xe8\xe6r0r\xe2\xc5O\xe7?N\xde\xa8\xe8\x87k\xe9\xf8\x84\x7f\xa8\xc2\xe2\x87\x96\xc5)e\x0b\x96\xa6( \xd0[\x17\xdb)BRj\x1d|\x7f\xf2\xecy\xed\x0b]\xc7\xb7\xc0<\xaa\xdex\xd12\x8a\x92k6G\xb6\xf0\x1f'o I\x81\xb7\x06)\xfb\xdb\x96eyfB\x08\"rR\x83w\xe3nV\x99E\x07\xab\x8c \x83MV{L\xb1!/\xdf\xddq\x0cV\xc3F3B\xabxP\xbam8i\xbam\xc8\x9f\x94.\xdd\x93\x05]\xcb&\xd2\xc3l\"\xd0V\x1d\x0f\xf7\x04\xf3\x9b8\xc6\x06\xec\xcc3\x97\x16P\x83[\x10\xd7\x91\x0d\xaf\x13\x83\xf4 \x16S[W\xeb\xf6\xa6}_\x93\x86\x0d\x951\xf4\xd3\xa3w\xf1\xfe.\xbbY\xdb\xacq\xdb\xd5\xd0b\xa3\x08\x8a\xec\xe2C\xed\xb6\xbf\xfeH\x7f\x07\xb9qc\xa7\xb9A\xd0\xf7*\xf5\xab\x9e\xb5\xf2\xf9\x9c=\x98[\xf9*q\x84\\O\xb8B\xaa\xf3\x04\x1c\xe1\xea#\x95\xe4,\x0f\xf2-'\xb7\x0e\xfd\xe5`jLN\xf3\xe4\xa71\x1c\x0c\x87\xa2t\xf2^\xc5\x8b\xa5\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\xe8\x95\xb49\x14\xbfj\x1da\x9118/\xff,\xc7f\xe7\x05\xbe\xce\xb5r\xfc_\x84\x9a\xab\x90\xa9j@\xd5\xd2/4\xf0\xb0\xc1\x82\xe5\xe68rW\"\x16\xa0\x19*tS\xc2\x18\x9c\x8a%\x01\xa7g\x08w\xc6\x1fy@5\x06\x87\x0e\xa7\xa80\xfaX\xcac*|E_\xcd\x8dp\x85m\x0cN\xa1\xd0h\x8dp\x0d\xa3\xf8\xd9*\x00\xf2'Oo[\xcca\xda\xa1\x03o\xdf7eO\x96\xcfG\x98\x05\xe8R\xd7\xd5\xad~odo\xcb\x8c8\xb6l\xc0R\xaa\xe6k#\xfel\xda\x0bM\xfd\x1e\x83\xa3)\x1aT\xa9\x8e\x9ef\xd1\xa8d&\xf4\x10r\xae0\x95\x9dtv:\x95\xfa\xd6\xb9\xe3\x17.P\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83\xe5z\xea\xba\x93\\\x06\xba\xeb\xc6\x9d;\xc07\xe9/!\xbbn0\xbf\x99\x81\xc0<\x88\xa5\xf4K\x13V\xda0\xe3\x8d7;[\xe9\x8f>\xb4\xc2\x01\xb8\xd5E\x8d\xc4E\xf3@\xebP\x93h-\x11\x9b\xa8\xf8\xbbX\xd9\x11\xa3\x90\x0cB;\x8f\xdd\xd4\xc2\x82$\xcb\"\xf10\xd8L\x99\xe5\x8e\xa1V@$wO\xa0\x07\x8e\x8f\x81\xb1al\xba\x8f\xef\x97\xc6?g\x11\xcbY\xa7\xad\x17EU\x97|\"\x86\xbc\xda\xe5\xf6\x97,\xef\xd4\xb8\xda8\xb9@\xc4F\x82\x8c\x0e\xbb\xf5y\x8e\xcb\xa9R-\x1d\xaf\x82\x9d\x1c\xd0d\x07\x15\x07<77;w\x96\xfb\xca*\x93l\x80\x80\xf2\xea hk_\x08Ym\xb9Y\xe5SI\x96-z\xf4\xacs$\xe7B\xa6\xfc\xe1\xd4\x18\xe3s\xbaqT;\x957\x8c\x11\x9d\";\x98,\xa4u\xd1vkV\xdf\x8f\xba\x83A\xc3 9\xe0)\xb9p\x904\xa32\xfa\xde\x9bM\"\xfaT\xd0\xd5\xe57\x98L\x87\x99\xd8N\xef;\xce\x84\xc5y\x1a\xfe\x16S\xe9\xb6/S\x0eL\x06\xcf\x0fh\x99R\xc51H\x9b\xa1\xc9E\xc8\xb0\x00\x96\xb3\xf8[\xe4\xf3\xcfO~8ys\xc2\xf9%W\xd8}\xa1\x9e\xfb\xe0\xbc|\xf5\xe6\xf4\xe5\x8b3\xfe\xe7\xab\x97g\xf8\xe9\xd5\xdb7\x8ea\x81fZ\x97\xb3(\x89Y\x97\x15\xd7\xa4\xb2\x19ZP\xfc\x86\x15\xbcL\xe6\xb7\xfa)\xdbi\x1cZ\xee\xd8\x1aWP\xa4\xcb\xd7\xc6\xe9\xa9\x97\xf3\xd2\xcb\xf9gNe^9\xf9o\x9a\x14i\x0fc]\xdb\xb0k\x84\x85\xaa1\xae\xaa'\xf6JB\xeb\x18K5D\xd3M\x1a\x94\xcfm\x1a\x8d\x95\x9a\xb2\xc3*\xcf\x07\x9d\xfdi$\xba\xd1\x92\x91\xc5\xa8}\xa1\x1a\x82\x82\xe8\xcb\xe3X\"h5\x9b\xcf\x98R4q\x16N\xd5\xf3\x11\xcc\xd2\xd0\x95\x88c==\x1c\x8e|8\x1c\x1e\xf0\x7f\x0e\xf9?\x0f\xf8?\x0f\x0d\xe82\x1f\xa4l\x1e\xa6\x1d\xd2\x8d\xcb'\\\xa8\xfc.\x97\x9a\x95O\xb7\x96i\x11\xb7\x94\xbb\xa9Pjg\xc9\xdcz@_\x02\xdd\xae\xfb\xd0\x05\xe2\x9a\x95\xa7(\xa1\xa3\xe6\xc6\xcb\xc6;\x80\x1e\x1b|\xafT\xee\x84\xff|M\x06A\x98\xc0\x8c~f\x9b$\xc6{\x9ds\xfe\x1b5\xe7\xae\xab\xaf\xadQ\xcdi-n\x10v@\xb7\xbe \x99\xc3^\x9aml\xa1(\xfc\x9f?\xfe\xf0}\x9eo\xc4<\xec\xa6\x9apG\xcf8\xd0\xb0\xaf\xb9\x14h;\x1e\xb6\xd2\xa7r\x0dB\xc4\xb0\x13\x91\x92\x8f\x02\x9d\x8d\x1br\xc1\xf9Y\x14\xc9m\x13\x9b\xeb\x8a\xa8\xbev\x97\x110#\xa9\xfe0a|qR\xd1\xf8\xdb\xd7?\xa0\xca\x1c\xc2\x11\x84\x03\xed-\x8c\x81\x95\xfdI\xfe\xb3/\xf6\xa3\xcf+\xb5\xf8\xbcH\x93\xa2\xea\xc8\xd0\x0b\xe6\xe9\x97?\xf8257\x19\xbb\x82\xc7\xe0%x;\xe6\xf8\x08\x16\x9d\xa9\xb1|\xd2\xaak\xe8\x0b\x96_'\xe9{i^\x87E\x10Fln\xf2\xfd\x90\x8f\xe8:\x0f\xd7,\xd9v:o\x97\xcf\x17\xeb|\xc3b7Q\xc7Q \x9d\x7fa\xaa\x1d'\x8cg\xd1v\xce\xe8\xf0!)\x9d\xf6p\xc9*\x1c\\\x87\xf9\xea\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|\xb8$\xc9+\xc5sWsoO\xb4C\xb7#:\x8a\x1b\xeb/mR\xa9\x99\xd8\"\xf9\x1cl\x92\xe8v\x11F\x91\xc9+X\xfd\xe5:[y\xd1_\xbfk\x90\xb1h\x01G\xf4\xdfXS\xb1>\xeb\xa2l\xec>\x1a\x9a\xae\xaf\xf0\xf7\x0f\xcd\x17\x92\x1e>\xb2\xdc<*\xef\n\x85!\xe6\x84\xb0\xdc\n\x1e2\x8f!)\xbfUQ\x02\xc6\xb5\x9c\xf7\x9f9\xbf\xc3\x87\xd5y$j\x1e\xf5\xf9\xd5!\xeb2\x0df\xef\x19\x9fHg\xd3\x00f\x84\x9b\x9e\xd7e*\x83\x0d+\x8c\xe7\xe1\x8c\x95Zo\xe7\xab\xd4\x01f\x96\xa3\xe4s]zJ\xd9\x86\x05\xad10@\xeb\xa5\xdej\x19d\xeb\xf7\xd2\x9e\x079+Y\xcdN\xcf^\x92\xe1\xac\\\xd6\x1c\x8dg\xce\xa2p\xcd\x15\xb31\xde\x0e\xae}\x97\xc1n\xf6\x0cR-}K\xc7\x90\x8a\xe0\x13\xb6\"\x7fA]\xfde\x1c\xdd\x8e\x8d9\x063\x96\x86A\x14\xfe\xc2\xf8\\vX\xad\xa0v{U>\x86\xbd\xc8\xde\x87\x9b\x17\xdb(\xca,c@p\xe6\x05\xbe\x0f\xe2y\x84\x91Q*V\xf3J\xa3\xba\xc6\x0eL\x04~Q\xf1\xc82\x1f\"\x9f\x8buE\x88\x04\xd3l\xa4%\xdb\xc0R\xd1\xdbZv\xa0{\x82F\x1eV\x89\xb8Xwe\xba !\xdd\x82\xaft\x7f\x0e\xbe\xb6Tq\xe36\xd6RW\xc2\xaf\x9a\x04\xfdP\xb9LQ\x06\xb4\x15\xa7\x93|D[\x01\x0c\xe8\xfbf\xb8\xe2\xcd\x9f+\xf4\x8fm\x81u\xb0{\x9c_\xa1\x84U\x8f\x97A\xefe \x80\xea\x87t\x10f\xe2V\xc1\x95\xa7\x0d\xff\x08\xa6s\x17#\xc4\xc3\xb8:\x07\x8f#\xfb\x84\xa3\xfd\xdc\xcd\xdc\xab\xd2\xa7s\x18\xf3\x9a\xb1^F\xb8x\\y\x9eA\xa5\xe2\x9b\xbd\xf6\xd1~n\xb2\xe0\xe0\x96\x15\xcc\xf0J\x0d\xd1\x10\xff\x8f\x97-\xdf7\x8a<\x0f\x8f\x07\"\xcb\xd6\xdaU\xdc\xdbJ\xda3\x13t\x808|\x98\xc1\x11\xdc\x0e\xb2$\xcd\xdd\x19\xdf\xe0. \x9a\x94\xa9\xf3\x92\xbc\xdd.\xe1 \xac\x95\xb7[\xafw\xd9\xa4\x7f_\xc0\x04\xd6\xd3K\x8b\xc1\x0b\xdd\xbd\n\x80\x9d^`&\x07wY\xbd9\xef^yp\x04K\x99S\x86\xb9\xbc\xa8\x0f FP\xf3Z\xd0\x96\xcf\xb3V5\x86\x1e\xb8\\8p\x06|\xe7/T\x9e\xd2\x0b\x95\x9b\xb4\xb9Q\x03\xd1\xaa\xbd\x91\xfb_&CfQ\xa0\x91\x99\xa9s\xfd:\xe1\x0b\x80n\xe5\xa6\x83 \xcb\xc2e\xec\xfe\xfd#606\xc6\xcdQ\x01\x99\x02\x89\x07x\x8aS\xdc\xf7-\xbd\xd7\xc8W!T\x05\x05\x810\xba\xd1\x9c\x88\xfa\xab\x00\x03\xa0_2\x08\xd4\xe4j9E\xaeD\xdc\x1b\x0do\x82\x81bjp\x04[\xed\xd7X\xffV_\x89\x19\n\xc4u\xe2\x11\x0c\xea\xcc\x01\x8e\xcc\xaf\xc7\xb05\xbc\xae\xf7\xb5\xb0\xf7%\xf9\x14u\xa1~a\xcb\xf2W\xbd\xc1\x8d\xb5A\x11\x18\xea\xa8\xf8s\xac\xa8X\xbd\x1d\xae\xa2\x1b\xb9N\xb1\xb1G\xda\xdfES\x86\x05]\xd9\xdb\xca(\xa5\xbc\xf8\x83N\x8b\xea\x0d\\\x15;K\xb0\x85\x9eU\xcf\x93\x1cy\x8e\xf6\xb3^u\xdd\xd0\xb7.n\xd0 Jop\xa5\xf57\xf5\xd6\x97-\xab]H<\xdaji/\x8be+^\xd6\x91\xad\x04\xd4$\xdc{\xea/4\xa2\x0bo\x93r\xd5\"\xf3U\xa7\xc8\x15\x89h0gi\xe6\x17\x1dY\xb0\xf3m\xfc>N\xaec\xa1k@\xb2A\xf1g\x93&W\xe1\x9c\xcd\x8d\xf8)\xc2\xb1\xe2\x80\x8b\xae\xa6\xb2\xa7\ni\xb7l\xda\"\x8c\x08\xa1\xd1\xa1\x95s\x12\xf9\xces1/\\\xfd\x06\xae*\x80\xba/&o\xd7\xab\xd5\x07z\xedc*\x82*oF!D\xc6\xc2)\xe8\x98\xee.:\xe1\xfd\x0bj]\xbd\xf8s\x8d\x9d\xa2\xff\xc2w\xb4h\xc2\xc0R~9\xe6\x8a?*&\xa8\xba\x07X@\xbc\xe1lF}\x1csE\x9f\xeb\x15\x8e^\xa7>\x9b\x1b\x98@8\xbd\xaeL\x06\x83\xc8\xb8U\x96\x1f{\x18\x0d\xeb\xce\x1d\xc9\xdc\xabw\x1c\x15\x0f?#\x1e~\x06O\xe0V\xe3\xe1g6\xe1\xf6\x18&p;=3\xf0\xefE\x89w\xc7\xd3c\xe2\xdd|\x07N$\xb7\xcd\\\xfe\x1e\xa3\xf8\xde(\x0e\nG0\x97$\x83C\xd6\xca\x87+\x9f\x0bV\x17>,\xab\x8c\xf5cm]\xdec\x07\xe8f\x16\x19\xcc\x9c\xcf\xd0P \x90.\x98\xcf\xff\x9f-Ko_\xa5l\x11\xde\xf0m8r\x0c1\x9e\xc4\xce\xbf/\xf2 \x0c\xe1\x08\x9eA\x0f\xdeW\"\xfc\xe0_\xbf\x8az\xdd\x82\xeb]\xf4nEN\xcd*\x12~Vn#\xb6B\x1c\xa4\x7f\xe0,v\x0c\x07\x06\xa5\x91\x1c(Qi\xa4?ME\x9au\xd29\xdb\xe4\xab1\xdc30\xc1 \x0d\xd6,g\xa9\x18\xc0\x88\x1d\x1a\nEA\x18\xd3j}1]0\xe8\x10L\x05\xda\xbce\xd5\x0ekl\xeeH\xcb\x92\xb1\xffn\xe0N\x7f\x1aL\xcf{\x1e:\xb2N\xffmt\x8e\xf7\xfa,\xbeW 6z\xdf}7\x9d\xfe4}w~\xfe\xcd\xb9gK\\\x03b\x16\xe5\xc2\x94h*m:\x86\xe3\xd4\x0d\xc5Gq\xa5\xda'\xb2\xc5n0!\x85\xbdb\xd6p\x8e\xcd\x97\xa9\x16\xcd\xacZ`/\x1e\xe8[ \x98/\x0c9Z\x15\x1504\x1a\xa5\xab\xae\xc0\xb0$\xdav\x83vF\xa7\xe2\x86;[`=\xfdQ\xc4R\xe4\xf6VB\xb3\x1b`\x08G\xb1\xa88\xa6\x08\x9e@<@\x90n\x0c\xf3\xcdg\x1cA\x0fC\xe7\xef2\xf3`::\x17[3\xf2\xa1/\x02v\x7f\xc6J\x04\xc6\xa0\x14`]\x0ci\xab\xe1\xdd\x8a&HQ\x92\x10\xa3\xc0E\xe8M\xd6\x01tA\xb0Ry\xb9\x0d\x1c\xa9r\xca\xf2\xa2%7\x1b\x89\xe4\x03\xc3\xc7\xd0\xef'm\x8d\x81@\xd0\x90\xa2\x98\xb3i\xd2\x90\xda[>(9LE\x0c\xb6\xc0Cl\xc44\x08\xd3sO\xb28\x9b{\x99\xfet\xb8M-\x1f\xb4\x18\x97\xc1\xe3H\xf2\x86Y\xca\x82\x9c\xa1\x0eg\xd2\xefl\xcf\x95\x08\xe5\xc7\xb7\x8d\xd8b\x91\x9f\x91+y\xe7\x95\xd7\x81\xb6\xc6\x1e\xc9\xd7\x1a\xfcq-\xcc\xbe\xc7\xd5\x87S 4_\x9f\xc6\xb9\xbb\xf5ad\n\xd9`z\xf6\xc2\xecE\xf0\xc2\xcdp\x88\x01b\x1f\x06\xbd\x17\x06\x9a\xcc\xc31\xe3\xab\x8c\xc2\x8c\x8a\x1c\xc8i\xc6P|\xcc\xe8\xd3\x13\xa4\xc7\x8a\xa9\xc1\x91\xda\xc0iv\x8eQ\xf0\xc7\x10N\xb7\xf8g\xeb\xc0\xcc\x18\xa2?\x1cT\xc3\xc6R\xcdm\x08l\xb3\x0f\xe5\xa3\x9b \xec\xa9\x15\xa9\x98\x9a?\xc3\xcc\xf0 \xf6\x84X\x88\x03U{B\xe9\xbd\xd1\x9e\xa0JX4\x96\xe7l\x07{\x02\x8ei\x10.\xe3$e\xba\xe4\xa7dB\xc3G\x1f\x87 \x8d\x0c\x13S\xacl\xbd\x80\xb0D\xbef\xcb\x93\x9b\x8d\xab}\xf10I\xa5n\xae\x085s\x85\xe4\x12\xbc\x83\xba\xe5S~\xc3?eI\x8c\x83=\x11\x9eZ\xc1\xa0\xf8\xe9#f\xb1\xcd\xb1\xf0B\x0e\x06\x17\xea'f\xa5\xc8f\xc1\x86\xbd\n\xf2\x95\xba0\x8b\xa5\x0c\xefy\xf1ml\xab`\xfcR\x1e\xfe\xd6\x90\xd7\xaf\xd5\xad^\xc0c\xbb\xcf\x01]\xd0\xbc\xccXzE\x1e\x9c\xd3syk\xf3\xf2g\xa8f\xfc\x80\xba<]\xbdQ\x17\xed<\xb4\xb6@\x95\x9cv]\x06\xb3\xf7\x14\xc8\xad4`\x98\x98\xa2mV\x07h\x8a\xfd=\xab/I)\x8b*\xe5\x9cJ1-\xb9\xa471<\x81\xf41\xc4\xbd^]\xcb@\xdb\xce4>\xa7e\xc3H\x0bd[\xb7N\x0d\x19VlQ\xb7/S\x16\xbco\x99\xd9\xc2\xcd\xe9\xbe\x88\xaf:\xe3\x7fm8\x14s\x11\x0b\xd3D\xa8\xdfR{E\xabJ\x81\xaaz\x1b\xa2\xa4\xe1\x08\x81R\xc8\x8a\xefF#q\xa8\x1b\x891\x94\xad,.`\x8a\x15\xfb\xa8n\xfc\xf0_n\x88\x89\xbf4jY\xdf\xac\x85\xab\xb2\x01\xd4,\x1a\x18b\x82\x92\xe9\x98\x96\xda(\xa4\xe7\x83<\xf9\xd3\xd9\xcb\x17@9X'\xea\x85k\n\x14\xa3\xe0\"D\x9epAK\xfdg\xce\x9ar\x8f\x84\xa1\xf2[\xe6\x91\x98\xb37\"\xde\x17\x94\xac3\x99\xb0\xced\xfd~\xa3X\x83\xe6\x18\xe4T\xd3\xec\xbc\xc1\xa2\xb8\x97\xd6.\x8e\xf9\xb0\xf1*\xd2g>\xdd\x9cWt\xd0\x08Mf$\xc0\x94\x8f\x98rO\xc5\xac\xb7\x9bg\x92\x0d\x1e\xd9\xac\x93+\xd6\x90o{\x13\xe4\xab1\xdd\x0c\xdc'\xf3\x98\x81\xe0\xb9\x1b\xfb\xc5\x1c\\HK\xae\xd7\x16\x03\xd2\x95\xc8\xf9\xc2\xe7n7\xaf\x18\xf2ADP$i\xa2\x1f\x86B3\xbd\xd0\x8c\x0b\x89.\x89\xa2\x1cJ[\xe7\xcb\x85\x1d2\x11`;\xee\xde\xd0o_r(\x96\x1d\x05\xf3\x86u\x87\x1d\xd6\xbe\xb9\x15\x11}9\xd5X\xa0;kr\x81\xedjF5\xfbEm9\xe0*j\xb2W`\x8f\xb9YDNMm\x08\x15\xb5\xcez\xbd&\xeb'\x07\x8e\x0d\x9e%f\x0d\xc0Q\xc3-f\xc3-\xae\xfau\xde\xbf`>\xff\x87\xed\x1d\x1fm\xd3\xf6u\xd8=\xcd\xc5X\xfd\xc5\xa5\x1c\xc1\x96\xdb\xeciZQ=+\x02\x97\x94:\xb6\x80\n,\x99\xbe\x9bE\x9cR\x08\xb3!\xf1\xf5\x82\xa1\xe7\x94`871tPL=\xd7\x98\xba\xd2\xe1\xf9\xeb\xf2\x9a\xd4\x02 \xf1\xda\x898\xdao\x95vJz\xb9\x90?\xb9bq\xfeC\x98\xe5,F\xfb\xa3\xed\x93\xeb\xac\x93m\xc6\xb6\x1b\x87\xac.\xd6b\xef\xd9m{!lk\x9e\\\xc7m\x05\xdf\xb3\xdb.\xc5f\xab ^2,\x85\x807Of\xdb5\x8b\xf3\x81\xfc\xe3$b\xf8;\xc8\xf3`\xb6\xc2\xda\xae\x93\xc4\xe59u\xad\xa5O\xb1k\x9d\xea\x8c\xbb\xd6+/@\xd7Z\xfazt0A\xc4\x15\xb9;\x16\xaa\x01iO\xb1\x99J\x9b\x80z\x86y[\x8c m\x84\xddV\x12\xa7\n~!R'\x1f\x03\xc9+\xf4\xc3\x12\xc9C\x9e\xadw%r\x80\xc7>\x8c,\x08\xc9 _\x87\xaehH\x02\xb1\x0d\x13\x0d_-\xc8h,i\xc0G{\x8bu\\\xb3\xb5\xa9J6\xe3\xdb\x9c}\n\xbeUju\xc27SO]0\xa7\xdeW1\xb5\n\xeap\x8eT\xc0\x01\x85n`\xd7@I\x99\xbcRD\xd6\x8fd\xad\x8aYJ&\xa8\x19\xff\x8dv\xbe\xb4\x9b\xa0bp \x91F\x90B\xb1Em\xbd\x9a\x01\xac\xc9h\xa8\xb4\xe3\xcfI\x02\xd69\xadW)\xe1\xafQ\xa9\xd63\x94\x1d\x95~\x8d!\xf6\x06\xd9*\\s\xf6\xdd:/\xb9dZ\xc6\xb7%\xeer\x86'\xf2v\xa2%\x06\xdd\x12q'\x90\xadi\x92\xa7\xd9DdH\xab#}!-Ck\x0d\xf6\xa3mo\xbd?C\xee\x17uK\xcb\xac\x82\xd2\xfb\xfa\xb1\x19\xd3\x8c=\x9d\x9ce\x99\x0f\x0e\xff\x831\x87\x1cij\xb56\xa2\xfciv\x12o\xd7\x14\x11\xc3P\xf7\xc3\x07\xdd\xa5\xec\xa3Kq4\x0b\xc8\x89\xe1\x08}\x0b\x12oPD\xb3\x9f@JVR\xfdUb\x04\x94\x9d|\n\x8d`JQ;p\xe12\x11F\xad\xfaQ\x85\xf4(\x1d\xa8Y\xf6F.y1ih\xba\xebU\xda\xd1\xe6\xf1\xb1\xc1,\x89\xb3<\xdd\xce\xd0\xc0=\x99\xe8\xdf\xd0t \x86\xabv \x8e\x8aI\x8d\x0d#3A\xb9\x1d\xea\xb4\x93\xcc#\x0ee\x11\xb6\xaa\x9fh\xf2\xf7\x1a_\x1c\xeb0:)9z\xd7\x8bR\xa2\xc8#Sz!\x07\xcf\xe5K\xed\xb5\xf4\x9b\xb6\xe1\x96!g\x8f\xc4e}\xc8 \x0d\x00\xb3\xc2\x8c\xd58\xb4/\x81[\xc9Bo\xea\xcc\x90\x7fG\xe9\\\xeb`\xe3\x86\xcdc5\xe4\xa4\x91\xf4\xdcz$,\xe9y\x15\xbdE\x80%7\x9f\xc6\xe7\x18W\x9dM\xe3Z\x10\xfc:\xb57\x8c\xca\x90\x87\xa6\xa4\\+\xbaZ\x18\x82G\x15\x83\xa3*2\x1d\x9d\xf3\xb5\xd4\x7f\x8eIX5;\xf0bT6\xb6\n\xae\xc2d\x9b\x8e\xc15\xf4u`\xed\xeb\xa0\xdc\xd7\xc19\x1e3z\x83r\xabx\xc5N\x9a\xd5J#Pg\xe4|\xeb\x9a\xad\x0d\n\xb91&u\xb9\x15\xcf'+:}\xf3\xa5\x13e\xc4\x85\\%\xf2F&Y\xb7\x94\xbf:\x9dF\xe7t\xda\xad\x1f\x91\xceD\xe2\xe8\xe1c\xd8\xc0\x13X\xa8\x067v#\x18o\x11#WL7\x0d\xa7\xe6+.\xf0L\xe7\x0d%\xae0\x97\xe3\xaa\xc1\x12\xb5\xc6\x12\xe1tn\x8b\xef^\xba\x8a\x80W\xde\xec\x12?\x96- \xe3\x13X7\xa9\x1b \xe6\x8a\x0e z'k8\x02>\xa8\x0e>\x83!%\xc0\xce\xd0\xebk\xba\xf4a\xeb\xae\xbcs\xa3\xbb\x99|D\x9clQs[\xbbz \x1fu\xadE\xa76m\xf3\xd7\x8av\x9a\xfb-\x1ex\xdb\x86 \x1f1V\x07O\xbd\x1d\xe1\x17VA\x13Z2\xe9+pk\xbe,)\x9f\xf2\x1a\xd8\x07\xa0\x97Z\xd5J\x18\xd5\\\xfd\xc0H5\xd3)\x17f#\xd5\"\x12$NA\x90\x84\x1dA\x8en\x1ecL\x1e\xcd)\xc1Hd6(R\x1a\xf0\x02\xe7zk\xd3\xd4,\xefg\xe4\x16Q\x8c\xdd/\x06=\x88\x93\x1f\xb7y\x907*\xe6j\xf0\xcc8\xf8\\\x0d^\xe6g\x18\x92\x1e\xcdH\x8f\x06\xc1\x07\x8a\x81V\x0f \xd5@\xc9\xbf\xd1<\xd2\xeb0_\xbd\xc4+R5\xdfI{\xba\xd5L}\xafl]\x8b\x8cg\x0f\x0c!\xf3\x8fC\xec>\x1a\xdd\xab\x10\xa0\x8b\x0b\x96\xfd\x98\xcc\xb7\x11^\xf3\xdf\xad\xcb\xd8\x1d=x\xc0\x17\xd0}t@\xff\x8d\xee\x8b\x9f#\xf1\xff\xa1\xe7\x97\x05[wt\xcf\x1b\xfc\x95\x05\xef\x7f\x0c6]\xfah\x10]}\x99\xc9\xf7p\xe4\xb9U?\x8ePtV\xbd,C^\x0e\xa3\x83\xbb\x95\xf7[j\xea~5Y0\x0d\xfa\xd1\xa8\x1a\xbb\"\xa2\xf2\xd5\xe6g\xf8\xfa^\xd5{d!\xbcG\x0e*\xef\xf1\xdcr\xb0d9_\x91\xf2\xa7y\xc1\xbb\xc2\xec\xe4&gq\x16^F\x95\xcb\x1e\x9c\xedd\x83\xed\"\xcb\x93\xb4\xf2\xe9\x8a,\xca\xa5w\xed\x01d\xab^\x076\xaa)Y\xb8\x88\x8ag\x904\x86%qbx\xaed\xd3V\xd7\xe3\xf2\x98\x97FYg\xc9:\x05\xd6\xc0{\x13(A\xdb\x89\xbf\xa4q\x1bcj\x06\xf9\x88 \x0b?\xe0\x1c\x8e`\xe5.\xc4\xec\x1d\x01\xcf\x8e\xe7a\x0c&\x94}1\xfa\xb6HU\x14\x16\xb37v`8\xf4\xab\x8b Yy\xca\xedAK\xb2\xc1\x9c-\x0c\x83\xf4\xd1?d\xc7m\xb8\xadj\xa8\xee\xa3\x83\xa1\xe7\xaaV\xf1\n\xde\x12o\xbb\xef\x0d1\x96Q\xb1\x963\xb7\xcd\x18\xf1\x00\xf6&\x80\x96\xa5[\x0fs\x7f\xc9\xbb,\x8b\x94\xb1_P\x18\xa4\x17\x9e{\xe5\xf9\xf0\x80\xd6Yc\xff\x1fI~\xdf\xba.\xa6l\xe3\x9f\x8f\x0b\xad\xd0]\x977I\xbb!\xb3\xf4|\x08\x06/NN\x9e\xe3\x01\xba\x0f\x89;u(\x8e\xae\xe3\x83\xb3\n2\xfe\xdf\x92\xe5\xfc\xbf\x8c\xe5\xce\xb9\xdf\x00w\x12\x96n\xb5.j\xeb\x8c>\xf2\xb5x\xc1!\xc6L\xd2\x1a\xcf\x0d^\x1c\xa0`:'\x03\xc4\x1c\x9d\x10\xcc`@\xb0\xb7(\xd2\x7f\\,\xc4\xe1TSP\xe3P\x065\xbeXL\xd99\x8d\xc2\\Zj\x86|U@\xe8\x9b\xbc&\x8c\x0d\x97\x18\xec\x0e\x91\"\xa8-\x02i^\x8b\xe5\xffQ\xdfc\xfa\xbbs\xa2\xf0G\xa3\x87\x96\xc8I\x8dh$\x07\xc6\xae]\xd4\xbe\xf5\x10\xaf\x9d\xf8b1\x82\x1a\x7f\x10\x1c\xab\xc6\x96\x04\xbbz\xe4\xb9N\xb6a\xb3\x90\x95\xd2\x84t\x93\xd8\x10\xf8\x8cb\nj\xe5\x1c?LW(\x84\xf1I3\xa2\xa0}\x8a\x9c\x85PJBHK\\\xcd\xce\xe5\xa9\x1c\x08\x82\xa6\xfb\x90\n\x90T\xe6\x10\xf2\x18\x9a\x86\xe7\x9e\xf2\x1f\x12\x85\x8b\x1c\xf1\x92\x96R7\xe3\xd6T\xf6\xdd\x85\x03Z\xe7\xe1}\xe3\xfas\xf6o\xe6\xba\xc2\xcd\xb3Z-0\xef\xa6\x10\x1a\x86UaBH:w\xab\xef#%\xaf\x18\xa5\x86\xaat\xd0$5DnU\x92\x9b\xe3\xdb\xea\xc8WxxT\x86\x93\xaeR\x00\x1b\\`\xea\x07\x17\xff \xd2\xb1\xae\x1e\x10\x94~\xae\xdbN\xcb\x90\xb2\x04hrojg\xd9\x86\xa3P\x8cr\xe3\xb2A\xd0D\x94+\xe5\x19\x17F\x10\xf0j\xa5\xaa\xd9\x90\x0b\x98Zk\xd6\xc3\xaa<\xd2A\x16\x91|a)\xe8\x9c5 \x94:\x83\xcb\xa7\xa3\xc6\x15Z\x05\xad\x01\xd2\xa4\xc8\xb2W\xf4\xda\xd4b7\xf9B\x1e;4\xcd$F\xe7yT\xf5r\x99\x021\x10\xf1\xa5Y=\xbete\x1c\xc4|\xdb&'WT\x043\xd6\x01\xa0M.\xca%\x00\x18\x9cv\x0d\xb3\x11\xb5\xfe;\x07\x99\x88%\x90\x07\xa2\xb9\x8f\x97\x08\xf6\xf6\xfe\xbb\x9aTF\xfd\xe57(fe!e\\#u>\x84\xb6\xa9\xa3\xdbc)J\xa35\xc4\xeb\x96\x7f\x8d\xb0E\xe7\"$g\xd7\x8b\x9c\xdcE\xd8\xe0\x82S\xbcU\xaf\xe7\x83@r\xa2\xcc~a$\x04\xbc|\x97\xb9)\x8e\x88M\xc3ss*|\xfb\xd2\xa5n\xa4\x8b\\\xe6av\xdbv\xf9\xa0Gg\x80\x92\xbd\x04\xf3\x91]x\x97@\x9b\xec \xe2s \xbeR\xd2s\xeey\"\x11\x03I\xf71_\x93\x99\x1b\xab\x9c8\xc8\xe4D\xfe\x85X\x89\xfd\xc6\xbe,\xee3\x1d0Z>\xff\x88\xd9\x8bD\x0f\xa6\xa9\x9bgi\x80\x10\x1f\xa2f\xcc_\xd4\x91\xc0\x86\x01)YK\xd1\xb7x\xcft/\xb8<\xa1\x14'\xc4H\xbb\xc8\xc5\xa5\x9bt\xcaP9\x9b d7\x0dM\xa8\xd8c\xb8*P\xfb\x0f\xf0\x05$\x94\xaa( \x04D\x8b9\xa3f\xb6\x08\xcc\xf6\x06\x12L\xeeU[\xc9,RQd\x91Wf\x16\xf9fa\x16\x876$uW\xc3\x9b\xce\xf1\xf5\xdd\xa17X\xd4e\x13\x8b\xf9\xe6\x8a\xea\xdcm\x15\x82%\xa5$\xed\xf3\xd6$\x13_\xe2y\x003\xd8\xe6/`\x02\x97\xf5\xd7\xd7\x9c\xbf\xe1!!\xa30;f?\xd4\x13\x98\xc0\x05G\x86\x8b&m\xef\xc6p\x1e%@\xf3\xcaz\xba\x89\xcd\xba\x18\xad\xe7D\xe5\x16\xe1Rx`W\xa5\xf9\x83*\xf4\x85'\x93*\xb8\x1ez\"\xb9U\x95\xca\x83#p/0\x91\x8b\xaen\x1aqm\xc6\xbf\\\xa0j\xea\\\xcc0\xeb\xe2\xe0b&\xa4\xc1K\x9dO a\xc0\xebsK\x1f\xf2\xe9\xf5y\xcd\xca\xc0)\xc0\xca\xe5\xcb\xe9\xa3\xc3\x94O\x04\xd3\x173\xf4\x97,\xf7WA\xe6g,\xf7\xdf\xb3\xdb\xcc\xa7<\x1f\xbe\x98\x8eO\xb7\x0f\x1c\x99\x9e\xce\xe7\xa3\xe9&&\xe0\x16\x82\xbcnZ\xa8\xacu\xb2\xc1 \x8c\xe1\x84\x9c\xcdq\x03\x1c\x1c**L\xa4Em]}\xc3K:{S\xa8uN\xb4e\x16 \xbe\x9e\x9cn\xa1LA\xfa\xd5\xc2\x8d\x0br\x8e\x06\x07\x1a\xae:\xaf\xb3\xab\xec*\x0f\xd1\xc5\x8c\xab\xec\x05\x05\x1frr\xed[\xd5})\x0f\x15z{R+W\x15\x89=\x9f\x82H\xcd\xcb\x8b\xe0d\xe1/\xcc1\xf1\xf6\xb2t\xdc&\x9a\xd1,\x06\xbc\xb5\xfaPjP<&(^W\xcd=dIY\xfap\xed\xf9\x90\x95G\x1a\xe3\xadOe\xf0\xf1|\xd8\xb8b;n(G\xd3\x85\x0f\x89\x9b\x0c\xfe\x03z\x90\x0c\xfe\x8a\xff~\xe7\xc3\x8d\x9c\xf9\x9a\xb3\x90\xb3\xc9J\x98\xa4\xcd\xb0\x16\xa1\x1eTy\xaf\xec#\xe72=O\xb5\xe7\xc3\xfe\xf4\xa7\xa0\xff\xcb\xb0\xff\xe8]\xff\xab\x7f\xfb\xe3\x9d\xaf\xbf\xe9\x0d\xde\xfdt\xf1\x7f>\xfc\xf7\xf9~8\xc8Y\x86\xb9\xd7\xcc\x81Wd\x82\x97\xd9*H\x83Y\xceR\xceW)\xcd\x00,B\x16\xcd!\x0e\xd6\xc6\x9c/\xca\xfa\x94'?$\xd72\xaftyq-sn\xb6\x84t\x9e6\xeb\xd4\x99\xc1\xf1\x11t'$#p\xc5\x98u\xa4\x95\xac\x82\xd6\x10\x93Iv[\x957{[\xfc\x99'1+9\x88\xb5$<\x11\xb7\xa2\xccI\xac\xc0\xa8\xe2\x99\xdf\x1a\xbcF\xc4\x80+i\xc3rS\xb2\xb0\xd6\xb5\x92\xb2C\xbd\xdf\xce\xd9~\x0d\xde}\xa0\xa5\x02\x14\x97sJ\x19\xf2\x13\x0c\xfd\xb1S\xbe\x0c2\x1eQ\xd6bs\x82\x0c\x91\xf9\xbf\x1e\xcd\x14\xbd\xeaL\xddu\xe9\x8bM\x87\xe7>0c\xe86\xadG\xdc\x03q\xee\xb6d\xb9\xe6\x1e\xf7\"X3\xae\xfd\xef\x90!\xaf:\xd7\xa9)\xab\xdcGS\xe6B\xdb\x1e\x19|\x13A]k\x90\xd9\xf8\x95\x04-\xb2 \x0dR\xc6\xe7S\xcd\xdb\xf2,JY0\xbf\x05\xfe\xafc\xba\xcc\\\xc9\xef\xdfi\x80\x06\x7fF(K0\xb5\xd4LM\x81\xec\xd8\x8eY\x93r\x97\xcf6\xdbF\xb6D)x\xff}\xb7\x8c;\xb1\xcb(aZw\x1bO\xa7\xa52\xf8n\x82F\xf1\xf8Z\x15\xb9\x97\xcdT*FW\xa9\xdc\xce?\xf2\x01\xdf\xddg\x99\x96\xac\x96\xdc}:\x8d\xd0\xe0\xc7 \n\xda0\x86\x8cvCP\x04\x9f1\x8cE\x9fQ\x91\x8f\x98\x03\xecm\xce~\xa0\x0b\xbb\x0d3\xc8\x18\x81\xae^\xd5C\x15\xfc\x12'\xd4i*QS| \xc4S\x1d\xd6G\xd54\xdf\xad\xa7E \x0f/JY\x05\xe9\"UC\x12\xa0\xd0\x9c\xdd\x81yZ\x0eE\x91\xd9\xdc\xa0\xa6\xcbG\xf9\x05\x16\x89\x8e\xbe\x8d\x92K\xcd%\xbf\x9a\xecXo\x9f\x17\xed\xdc\xbeL~\xcd\xfb\x90\xe1g:\xf6#\x8bw\xeeK\xcf\x7f\xce\xfb\xab$@\xef\xd8\xad\\>u\xc1\xa2I\x86\xd0z\xd7\xd2mC)\x87\xd4\xba\xd2\x81\x86[\xe8\xf7\xc9\x04\\\xca\xec\xc0:4\xc4\"\xb7\xb9;5\xd6\xb79\xbdB{\x00\x03\x90&\xf1\xf2\xc8?|\x80==S\xb5}\xcd\xd0\x00\xb3\xac\xc8\xb2\x82U\xe8\xd7-\xbe\x95\xb3\xe15\xdbr\xab5\xac\x92\x1d)\x84+hm\x0b\xab1\xa7\xe5\x83\x05K\xf9\xdffI\x9c\xb38\xef\xd3\x10\xf1\xf8\xd6\x12\x04\xadT7\xab\xd5\xf5\xc1\xc9\xd9M\xbe\x8f\x01\xa9\x1es1>c\xf9\xe4\xed\x9b\xef\xfa\x0f1\x04W\x05\x8b\xe4\xe1\x98z3\x10W-Z\xbb1T\xe3\xed\x7f\x0e\x12\xa8\xd14N/\xd8l\xa2\x90\x92<\xee\xdf\xf4\xaf\xaf\xaf\xfb\x1c\xc5\xfb\xdb4\xa2\xe8\xfc\xf3\xea\xac\x8d\x12\x8c\x96a\x8d\x88)\xd1\x94V\xfe*\x8d&!i\xcc\xe6\xfd\x0d)d\xb4\xe44\xf6B\xe5E4\x88AY\x12]\xb1j\xb1.\xedi\xd0km\xb6\x15\xb7;\xf5$\xa9\xa4\x01\x0bg\xc9\x96k\x8cI\x8e\"\x9b\"\xbf\x98t\x17\x82\x0c(\x93]\xa3e\xa2\xcb\x989\xb6\x9d\x9b\xb7\x99\x04\xda\x12&\xb7nq\xc9\xaaY\xa5\x04Gr\xe79\x8e\xda\xf7\xa9\xb4\xfc\x02\xeb\xf8l]o\x90\xafXl\x8aM\xfdQ\x92\xdf\x9c\x88G\xeb8\x7f\x13Pl\x17\"`G\x11P>vQP>\x15\x91\x90o\xb3A\x16\x94\xcf\xc7_\x0bM\xba-A\xc9\xf3\xbe&\xfd\x91\xbfzaS\xcde\xdc\x17\xf2\xba\x1f\n\xaf{u\xb5E:\xdf\x9f+\x1b\xc7`\x91&\xeb\xe3U\x90\x1e's\xe6\xe6\xd3F\xd6+\xb5\x17J\x99`\xcbk\xfa\xd1\xb2\x10\x9dV\xee3\xd0\x9e\x03\xf8\x8eh_Fv\x0bE\xd7E=\xaa\xb1($\xb8Vt\xd2\xd1>\xc7\xf37B\xd5E\x03\xaa\xfe\x9c%\xf1\xe7\xb4\xfd\xa7\xb3\x97/(\x06\xaf\x95z\x95\xde\xdb\x94\x85Y\xab\xe7\x0f\xf9\xf5\xd1\xfd,\x0fU\x87\xfa\xfa#\xad\xd0\xad%\xc6\x08\x94`P\xdf\x8d\xb6\xc4\xb2\xba\xde Q\xda\\F\xf9T\xf1\xcd\xac\x94)\x95\xe9\xbf\xb9\x1a%\xe4\x83\xc2Gv\xa5r4\xc7\x98\x8f\\e\xd7\xf5\xe4NQ\xd6VlL&p\xa5\xf7\xc9\x9c\xd1\xdbd\xce\xfcR\x82\x18`\x9a$\xcc\xbb\xc2l\\z\x06\xf6\x8a\xbd\xc1\xb0\x87\x9a`H\xb3T\x06K\xf3\xf2\x1bf\x9f\x97~\x7f\xf8P_\xa1\x0f\x1f\xc0I\xd6a\xee\xf8\xb0W,NE\x98\xb2/Vn_\xacv\xd2W\x98;\xf3\xe4\xedf#\xed\xbe\x8d\xc8}\xabe\x1a\x87\xa7\xd0\xa7{H\xa6\x8c\xdd\x1f\xdd\\\x0eFN\xbc\xf8\xe9\xfc\xc7\xc9\x1b\xc7+\xefcN\x7f\xa8\xc2\xe2\x07\xe5\x9d\xc1W)[\xb04EI\x80\xde\xba\xd8\x0e\x99V+\x1d|\x7f\xf2\xecy\xed\x0b\xf9\xcbZ`\x1eUoN\xf90&4\x9b#[\xf8\x8f\x937\x90\xa4\xc0[\x939\x873\x13B\x10\x91\x93\x1a|5\x8e\x8f\x0d\xf7\x17\x1d\xac2\x82\x0c6Y\xed\xd3p\xedz\xf2\x8c\xfe\x8ec\xb0\x1a6\x9a\x11Z\xc5\x03B\x1e\xd1~cxb\xfe\xe0\xf6H\x0b\xba\x96M\xa5\x87YT\xa0\xad:\x1e\xdc \xe67q\x8c\x0d\xd8\x99g.-\xa0\x14d\xf8\xed\xeb\xd3\"&\x19\xd7\x91\x0d\xaf\x93\xeeq\xe1:[\xb77\xed\xfb\x9a4l(\xad\xf4\xfe\xbb\xf4\xe8]\xbc\xbf\xcbn\xd66k\xdc\xb4\xda\xe5\x8d\"(\xb2\x8b\x0f\xdd2\xda\x8b\x8d\x1b;\xcd\x0d\x82\xbeWi\xed\x0e\x82|>g\x0f\xe6V\xbe\x9a+_\xfa\xbf\x17\x82\xbbH\xd0-\xae\xeeI%\x99R\xd5SXs\xfe\x17\xe6\nC\xf7\x0d\xf9i\x0c\x07\xc3\xa1\x8c\xfe\xfa^\xfa\x85\x88\x8fO'\xfc\xab\"\xe7\xe2\xed\x138TU\x8a\\\xf8E'\xfcW\xad#,2\x06\xe7\xe5\x9f\xe5\xd8\xec\xbc\xc0\xd7\xb9V\x8e\xffc\x8a\xfc\xaa\xa1\xb1j\x17)/7\x1axDZo\x1b4\xaf\xac\xc7n\xba)a\x0cN\xc5\x92\x80\xd3\xb3\xe4Q\x92\x07Tcp\xceD\xcc\x88P\x06\xa6\x90\xc7T\xf8\x8a\xbe\x9a\x1b\xe1\n\xdb\x18\x9cB\xa1\xd1\x1a\xe1\x1aF\xf1\xb3U\x00\xe4O\x9e\xde\xb6\x98\xc3\xb4C\x07\xde\xbe_=\xc3\xd0\x9f\x8f0\xc3\xe0\xd4\xcd\x94\x174\x97\xca\x91\xbd-3\xe2T\xa3\x1f\xcbGJ\xd5|m\xc4\x9fM{\xa1\xa9\xdfcp4E\x83*\xd5\xd1\xd3,\x1a\x95\xcc\x84\x1eB\xce\x15L`\xaa\xe2\xd5\x9cJ}\xeb\xdc\xf1\x8b(6\x85\x1aV\x7f}\x1c\x05\xeb\x0d\x9b\xd7\xbf\x9e\xc6\xf9\xe8\xbe\xb9\x92\xe9\xfdi\x9c\x1f\x1e\x98\x8b\x9b\xde\x7f\x17%\x81\xfd\xc3\xfd\xbb\xe2\x83%,A\xfbuP\xf9H^\xc0!\x94o\xd2_Bv\xdd`~3\x03\x81y\x10*[\xaf\xb0\xd2\x86\x19o\x9cS\x88\xdd\x87v\xa5\xc4\xc1\xd6\x10C$.\x9a\x07Z\x87\x9aDk\x89\xd8D\xc5 \xd5\xca\x8eP\x94D\xb5\x9d<\x83\x9a\xae\xde)?\xbeu\xb0\xb1:Di\x05`\x82\xa7\xd0\x18\xfd\xd4\xc7\xe8\xa706$\xff\xc1 ^\xc5\xf8\x85\x93z\x97\xad\x17EU\x97|\"u\x9f\xf6J\xfbK\x96wj\\m\x9c\\ b#\xe4f~T\x9a'\xa5{l\xebx\x154\xfbFU:\x96\x1d\xd4\xc2Bs\xe8h\xeb+\xabL\xb2\x01\x02\xca\xab'\x80\xa0\xad}\xe9\xf3\xdb\xe1\x1a\x14\xd4\x02\xdc\xc8\x1e=\xeb\x1c)\xdc\x8d\x88L\x95\xfb\xc5\x18\xe3st\xfc\xcak\xa7\xf2\x861b\xd0\xb2\x0e&\x0bi]\xb4\xe5\xfb\xd3\xf7\xa3\xee`\xd0\x92\xea\x8d\xc9\xc8lfT\xc6\x8b\x89f\x93\x88>\x15\xf23\xfe\xf5'\xd3a&\xb6\xd3\xfb\x8e3\x11\xae\xd2\xbf\xfeT\xba\xed\xcb4\xae\xdf\xf7\x92O\xd3\x94*\x8eA\xda\x0cM.B\x86\x05\xb0\x9c\xc5\xdf\"\x9f\x7f~\xf2\xc3\xc9\x9b\x13\xce/\xb9\xc2\xee\x0b\xf5\xdc\x07\xe7\xe5\xab7\xa7/_\x9c\xf1?_\xbd<\xc3O\xaf\xde\xbeq\x0c\x0b4\xd3\xba\x9c\x89\xf4\x17\xad+\xaeIe\xd2\x13\xdc\xbe\x82\x97\xc9\xfcV?e;\x8dC\xb3+\x96!\x16\xf5G\x1f\"Bnm\x9c\x9ez9/\xbd\x9c\x7f\xe6T\xe6\x95\x93\xff\xa6I\x91\xf60\xd6\xb5\x0d\xbbFX\xa8\x1a\xe3\xaazb\xaf$\xb4\x8e\xb1TC4\xdd\xa4A\xf9\xdc\xa6\xd1X\xa9);\xac\xf2|\xd0\xd9\x9fF\xa2\x1b-\x19Y\x8c\xda\x17\xca\x90D\xb7\\\x84\x96\xc7q,\x83nDm\xa6\x14M\x9c\x85S\xf5|\x04\xb34$/\xd5L\x0f\x87#\x1f\x0e\x87\x07\xfc\x9fC\xfe\xcf\x03\xfe\xcfC\x03\xba\xcc\x07)\x9b\x87)\x05\xd8\xed\xc4\xd2\xb8\xa0.RK]jV>\xddZ\xf6:\x88\x97UwS\xa1\xd4\xce\x92\xb9\xf5\x80\xbe\x04\xba]\xf7\xa1\x0b\xc45+OQBG\xcd&\xeb\xa4|,\xea\x93\x11\xf4\xd8\xe0{\xa5r'\xfc\xe7k2\x08\x02\x86^\xe5?\xb3M\x12g|{\xe7\xfc7j\xce]W_[\xa3\x9a\xd3Z\xd3%\x17\xd0\xad/H\xe6\xb0\x97f\x1b[(\n\xff\xe7\x8f?|\x9f\xe7\x1b1\x0f\xbb\xa9&\xdc\xd13\x0e4\xeck.\x05\xda\x8e\x87\xad\xf4\xa9\\\x83\x101\xecD\xa4\xe4\xa3@g\xe3bN\xa7gQ$\xb7Ml\xae\xeb\x91\xb1\xc4\xee2\x02f$\xd5\x1f&\x8c/N*\x1a\x7f\xfb\xfa\x07G&\xa2\x0f\x07\xda[\x18\x03+\xfb\x93\xfcg_\xecG\x9fWj\xf1y\x91&E\xd5\x91\xa1\x17L\x0f(\x7f\xf0ejn2v\x05\x8f\xf1\xc1$\x97\xcb\xe7\xa3\x8f`\xd1\x99\x1a\xcb'\xad\xba\x86\xbe`\xf9u\x92\xbe\x97\xe6uX\x04a\xc4\xe6&\xdf\x0f\xf9\x88\xaes\x8a\xfe\xfd\x0f\xe9|\xc3b7Q\xc7Q \x9d\x7f\xe1\xe5&'\x8cg\xd1v.\xe2\xd4%\xa5\xd3\x1e.Y\x85\x18\xa5\xec\xb8tND\x15\xd5\x16\xddn\xe46\x96|\xc1\\m\x17\x05\x17!/\x0c>\x00 B;\xf9G\xcb'\xe4\xea\x95\x80:B\x03\x8b\xbb\xb4|0j\xe4 c\xf1\\\x0f\xa6\x9ah\x87n*}\xa0\xf6\xd2&\x95\x9a\x89-\x92\xcf\xc1&\x89n\x17a\x14\x99\xbc\x82\xd5_\xae\x9e\xc1\x163[\x90lQ\x8d\x85\xf6\x07\xd1xiqv\xbai\x94\x9bn\x19\xdd\xbb\xeb\x0d\xc8\x98b\nd\x1b\x1a\xb7\xc0lQ\x14\\\xc0pLQ5\xd5J\x13\xa2Q'\x10\xcd\xa4*\x8d\x9b\xf4\xc6\xe5\x03\xd1|\x13m\xeb\xa9\xfe\xaa\xb6\xd0\xc6\xcd\n\xb5\x18\xef2\x89\xec\xdd\xf2`W\xf9Ml\xe9\x9eQF\xffE*KN\x910\xdc\x9a&\xe7J\xc4\x1b\xcd\xe0I\x11N\xfa\x88k\xd6\xc2\xbf\xe2Y\xee\xa2s\xfd\x8b\xe0E\x9d\xcee\xd7!\xae\x9a5\xdb\xfd,\xc8\x18\x0c\xc7V\xc0\x97\x0dX\x8f\xd7\xe5\x83\x0d\x1d>\xb0\xb7$\x1f-\xd9\x80\xb8z\xd5\x10Y@>\x98\x86\xad\xb9\x18\x0e\xe0\xeea\xfb\x00\xf0J\xac\xcb\xd7\xf4\xf0\xa0\x85\xdb\xc8\xc0\x86\xadm\x06\xd3\xa8\xd73'\xea\x94\x8fY\xf2\x82\xe6\xc9\xe1\xa4F\xf6\xfe\xb9\x0c\x1b\x92<6\x83\xa7\x13\xb8\xfb\x90On\xc6!\xeb\xde\x03\x0f\xd7z\x06}\xb8\xfb\xd0>O\xe5\x95\x8b\x0d\xdc\xbf\xa7\x1ax0,\x1a\xb8\x7f\x0fz0\xb2\xdc\x10\x86\x1d\x1ch\xa9\x97G\x0fT/\xa3\xe1Ac\xf0<\xf9\xa8\x15>|\xe0k\xcb-p\xab#\x045\x96\xb2o\x10\x08\xb0\xe5+\xf1\xe8\x01\xae\xc4'l3\x1f\xe8\x81}\xa0mPp\xd0\x0c\x05\x82\xc4\x98\xa0 \xfd\\(H\x7f\xe7P\x10\xea\x10\xf1\xeb\x83B\xfa\xd9\xa0\xa0F;\xba\x0f\xdf@\x0c=\x93Q\xfd\x0f\xf6_\x82\xdf\x05ER\xe2\x08\xfaz\xea\x94\x8f\xbe\xc6\xca\xf8\n\x15\xab\xa2XVP\xf2\xf2;\xb8w_2\xaa\xc7\xb0\x85'pp\xef\xfec\xe8\xf5\xb6\x1e\x04\xd3-\x86#\xfe\xa3\x03=p]\xfeqt\x1f\x8e\xc0\x19:\"]r\x0f\xb6\x05\x97\x1d\xdd\xf7<\x9b\x87\x8d\xcc\x9e\xd6hFo\xb8E\xd9\x9b\xf0\xfe\xca[\\\xf2ft\x9cR\xceP\xe1\xac\xc8\xb4T\xc5F\xcdRj\x94%\xb6j:I!\xf0=<$\xf9\x8fkNw\xefi\x7f\xdf/\xfe~\xa4\xbd\x1f\x1dh\x1f\x12\x0e\xfb\x87\x8f\xf8\x8c\x12\x0e\xfbw\x0f\xd4[B\xdc\x84\x10W\xbd%l\xc4\xb7\x8f\x86\xea-a\x0f\xbe\x1d\x1d\x1cX\x04xtd\x80>\xc4*\x1dh\xce\xd7P^(BE\x9b\x8b\xd3|K\x0f\x1e\x12\xbdO9T\xfb\x80\x05\x83ib\xb1\xdd*\x82\xc1\xeb\x1e\x0c\xef\x1a+\x8f\x1e\x1d\x00\x0e\xf7)\xdc?\x87\x1e\x7fs\xf0\x10>\xc0\xfdC\xb8\x03\x9dZ\xbew\xef\xe0\xd1}5\xe7{\x0f\x0e\xef\xde5utppWv4:\xd0{\xa2\xbe\xe1\x0e\xdc?\xdcm\x00\xcd\xd6\x87\xb0\xc1v\x80\x10\xd2\xeb\xe9pW2*\xbd}}*\x94\xb1\xb7\xafOa\x1dD\x8b$]3\xab\xdb!\x08\xfb\xc5hx\xc0\x07]\x81P\xdf\xb4\x18w\x87\xf0\x81\x12\xc5\xdd\xbfw\xef\xf0>b\xad\xa8\x9ex\xf0\xe4 \x8cx\x81\xd0\xf3p\xbd\x1e\xd6\xd6ktP[\xb0\xe6u4\x0e\xbc\x03\x01+\x02\x890\x8c\xfbT\x12qs\xe8\x15\x80\xea\x95c7\x96\x15\x95\x96\x88\x05\xd4\x97\xe5\x8e\n\xef\xd8\x94\xb9\x85#K\x98}\x17\xc6!E\xe4:\x02\x87\x93?,~\x99$\x11\x0b\xe2zSG\xe0\xe4\xe9\x96!Y\\\x04QF\x7f9\xfa\xb8\x0b:,\xf5\xa5hw}\xc9\xae\x1e5\xc51,8\x02F\x1e\x18vQ\x87h\xd1\xc2\xc5-&\x0c\xa4[+U\xa5\xc8\x9c\x0fX9\xf1:w\x04MF\x87UgR\xb9ht\xa5\x12\xfa\xd2\xd8\xca_\x89\x0e\xd8\xa2\x18%bD\xba\xe6H\x96\x03<\xb3\xa9\x7f\xe4\xf8B\x99b'\xf6d>\xa6%,qM=\xe3\x83\xcc1\x1c\xa8\x88$\\\xbd\xdbrvL\xd9\xf29GZ\x10+Z\xc0\x13\xd8r\x1e\xb4h2\xe1S\xaa\xe1EC\xa6\x879\xa5$n\xc9\x16\x11\xba\x19\xe6\xb7\xedU\xd3A\xca\x87\xafm\xf9\x12\xf8\xbcQ\x08Skp\x05\x13\x98\xab\xf9\xaea\x02W4\xdf%\xcds O\xe0\x8a\xcfs\xe9\xc1\x8c\xd3\xa4\x15\xf4p8\xf3\xe9\xf2\x9c\xf3\x1b^`-\xd4\xb0\xde\x04\x9a.V`\x08+\xbep\x91^\xdeLp\x88r\x97{\xe4\xdd\xb5W\xaf\x8bj\x02gf\xedDL\xc7o.v\xa1\x8f<\x024\x995\xbe<\xba\x04\x86\x88_\xa1-\xea\xc6\x87\x0f2[\x8fdFJ|,\xb7`\xa8\x9d\x17\"CM\xec\xba\x12)\xf1c \x08\xb5%$\x8fp\xdbW\x8e\x1b#vXn\x94P\xbdN\x8e\x93\xc1:\xb8\xf93\xbb\xcd\x94\xee\xae\xde\x18\x86\xc5\xd1m\x04\xfbU\xb5p\xa6\x84 ^`f\xa8\xb8\xc1m\x93T\xd2443\x15\xaa\xdb\xaf\xb0\x9b\x0d\x8e\xb3\xfe\xd1&\xc0r\xbc\xde m\n}D\xe1\xe9\xb9\x8f\xc86$,\x1b\n\x0c\xf3\xf1\x94\x99\x13\x96K\xf1\xff\x05\x9d\xc1\\\xd3\x7f'T\xe8\x86\xb0\xf1\xa6\"\x00\xdf\xd8\x04\xe0\xb3\xaa\x00|c\x11\x80\xcfp\x8c\xb9^tm\xa5\x1c\xbc\x82\x18<:]\xb9\x87\x0f\x10\x1c\xcf\xe0\x08\x07:\x821\x9c\xa8\x9d9+\xc4\xe0\xb3B\x0c>+\xc4\xe03RJ\xd5[\x12\x83\xcf\xa4\x12 G\xc0es\xe8\xf5(\xc2\xda5Y\x9b\xb1\x8f \x86\x91\xe6\xb4\xc7j\x0e\x035CJ\xba\xa2\xcdp\xd9\xaa\xa0\xf2\x8a\xbd\xde\x12\xabn=\xb8\x82'\xe0\xbe\x87 \xdc@\x1f\x96\\B\xa38\xd5\xb7\xba\x04~\xe5\xc3{N\xa2\xc4\x96]a\xf1^\x9bIl\x96\xc4y\x18ow=\xe6\x03\xe1\x0d7\xe4\x00\xf3\x9bo\xc5Ee+\xcc4\xdc\xf8\xf6\xee\xa1\x18'o\x077\x10\x8e\xc0\xe5\xebz\xa5\x86[]\xd6\x1b\x0f\xe3\xa9q\xd2\xf5\xc7\x83\xa1\xc0\x11\xea\xbfR\xf3\xd2T\xf3R\xaby-\x8f,\xd4\xf6\x188H\xa1\xb7\xf4zk\x1cn\xd6\xc4\xe5\x8f}\x90\xb0\xb1\xb6o8oN\xce\x97\xc3\xd3{\x1b\x04\xc1X\xfb^\x9d\x10B\x98\x8c\xf88\x81\xc8\xbd\xf5a\xc3\xdf]\x8b\xe2\xfc\xdd\xa5x'\x8e\xc4W\xeaH\xfc\xd6\xf3 \x98\xde\x9ec(KXMW\x82\x96\xf0\x17\x86\x9bY 4(\xf7\x18\xe5\x98\xdbsO\xbf\xa6\x85r\x06\x1c\xc1\xf1\xf4Xk\xe6\x12\xc6\xb2\x8b\xe9\xb1\x0f\x97\x16\xc5\x8c\xaf\x06\x06\xf5\xea\xf7\x17^\x93\xc1\x8cou\x99\x16\xdeb/D,\xd5.\x12UE\x8c\xa8\xef\xe7\x1f\xec\xbf\x16\nt\xaet\x95\xe5\xc3\x07X\xf2/^\xfd\x93\x0e\xb7\xe5\xdd\xe3;\xb7\x86'\x90\x19v\xce\xfb\xcc}\xe3Hb\xdd9D\x84\xcf\xd9\xa3\ns\x90B\xc5\x1f\xcak\xd69\x93\xc1#K*\x83\xc3\x87#\xaf\xfdtO\xba\x13\xc8\xebpp\x04\x7f\xffH \x0dAB\x8b\x91\xeb\xc7e\x9d2]\xea\x03\xaeF\xd5\x13\x03\x1e\xb6GI\xb4'\x85HE\xa7\xad~p\xa2|\xe2\xb2Z\xfa\xb3\xd6\xc8p\xd69\x8d\x0e-s\xba[M[D\x81\x05\x1f<\xea2U\xc3\x0cJ\xfaT\x7fD:\x94\x12\x16Qt\xfc\xfbG.\xad\x04\xa83\xd9D\x16\xbc\xf01\x0d,\x9a\x10\xe6\xe9\xe3#\x88\x0c\x82L\xec\xce\xf8\x07\xa0\x98\x81>\x84nDA:g6\xbd\x18\x8aU\xcfv[`\xf3\x19\xeb\xfe7{E\xdb\xdf\xc0,I\xde\x87L\x7fs\x9cln\xd3p\xb9\xca\xdd\x99\x07\x07\xc3\xd1A\xff`8\xba\x0b\xaf\x93u\x10\xc3\xd9*\xbf\x8d\xd6A\xdcT\xe1\x1e\x1d\x9e#\x0f\x99\xa3*O\xfcf\xc4\x99H)w\n\xc4\xd3\x0d\x95\xc3?&\xb0u\xe7>d\xed\xa1)M8SI\xe4\x9d\xb14\x0c\xa2\xf0\x17\x93~\\],E\xa0\xc4v\xd7WZ7O}\xf8P\xbdm\x88pY\xa8n\x05d\x86\x16\xc8L0\xa9\x1e\x88\x06\xc3\x0cB\xf2\xfe\xab\xee2\xeep\xd0\x12\xa8R\x81y\x1c\xac\x9b\x1a\x93\x1auX\x8b4A\x07|\x18\x9e\x9b\xfa\xda\xb6\xf6u\x15D-]\xe1uu\xe8\x813q\xa0\x07\xdbz\x8f\xc2R\x06)W\xb5\x9f-\xadW<#(\xca@\xdft\x18\x8b\xc7\xd4\xd9\x8b\xe0\x85\x1b\x99\" \x89\xaa\xd9\n\x831 \x0dxA&\x00\x03\x14g(\x98?\x86\x1f\x83\x9b\xfe\xb3%\xc3\xc1\xff\x18\xe4\xab\xc1\"J\x92\xd4\x8d\x9a\xa87\x1e\x87\x0c\xe6\xc9:\x08\x8d=\xe8o\xb0\xd7\xe4\x15$'(\xfa\x98\x9cUe\x9b\xea\xd3\xe6\xdd\xe0D\xc1\x8d\xb3C\x87?\x047\x9f\xd3\x9b\x90\xc5v\xe8\xf0sf\xd8\xeaF\xd4\x04\xf4j\xbfu\xa8\xaf\xb5\xd4\x81\xffj2k1L\xc9Y\xebF\xca\xba\x1aP?N\xa9\xab\x04\xfb\x8f\xe1\x9b\xfd\xf2k.\x9a\xed\xff4}\xb7\x1d\x0e\x87\x8f\xf8\xbf\x07\xc3>\xff\xef\x01\xe3\xff>\xa4\x1f\x8b\xc5y\xef\xdf\xf6M\xc7c\xdb\xdf\xeax\xac\x1a\x93\xb9\xfc\xd7'I\xf8\x1dC\xaa\x8b\xfek\xcb\xeb2-\x1c\xc4t\xefk\xd7\xfb\xe6|\x7f\xd9\x16\x8b\\\x1eK\xa0\xbbF\xc9\x9e;\xf4J^\x1ae'\x8d\xf2\xec\xdb4H\xbd\xe3n\xb3,\xb9i\xc8\x1c\xf32+\xb2\x92\xc7c\xbb<\x9eV\xcd\xd3\xb1E\xe4N\xd1U\x00\x1d\x07\xee\xdc\x81\x14m\x97\xf7\x0fG\xe8q\x11C\x0fF\xfa\xc9|\x83X^s\x08\xc1\xca\x16\xc1\x9a\x0e*\x9fbW\x07h\x1c\x12n\x1c\\un0\x1c\xcb\xe3\xcf\xd1\xf0\xe0.|C\xde\x1a8v\x0fz\x90\xf0\x1f\xd8^\x8f\x8e\xf2\xed\xe4'\xa7\xebp\x07w\x87ey(\x84}\xb8\x7f\xb7\xf8\xc7\xf3at\xf0\xd0Z\xc6\x83?\xc2\xfd\xbb\xd62\xe5\xcf!\xfeB\x1f\x84^\xa3\x1bg\xa3\xbd\xban\xf25\x9c\xc6Qh\x89\xbb\x0f1B\x04\xcd\xf4\xe0ny\x84i\xf3$S\xc3\x04R\x9a\x00\xe7\x97\xbc\x03\xfeR\xb5?zt`l\xa0^WTH;\xd8\x0d\xda\xd2O\xea\x90\xb2gw\xf3\xe7@\xc3la\xf9\xedF\xb2J\x91\x86\x0b\x96(\\\xa6z\xfe/\xcb\x19\xb2\xc4\x93\x86[d\xa1\xddAs\x9e\xb4`F\x80V!v\xc3f\x8d\xa9\xc5\x94\xb62\x99L h4\x0d\x83\xd2\xcbCx\x02\\\xbao)\x9c\x90S\xcd\xf0\\\x19\xa7\xc2^\xcf\x0c\xc8p\xbd\n#\xa6\x14'>\x14s\xbb\xd2v\xc7\x81N\xf3x\xe9\x8f\xcc\x19r\xfe`\xdfIK\x8a\x00\xd0\x9d\x04\x85v\xbaS\xbb\xc2\xach\xa3\x8eZz\x8d;\"\xbd\xc1\xd4\x99\xfet\xee\x9c\x97\xcd\x07d;\xe0\xa2l\xcd\x9e\xa3\xda\x12\xa4\xbd\xed\x92\xf0\x0ea\x81\xb0\x1a!%\x1bd\xc96\x9d\xd9\"Fx\xbe,\x18\xca\x82\xe48\x98\x0efI<\x0bD\x10Gv\x0d\xaf\xd9\xf2\xe4f\xe3\xa6\"\xe0\xcf\x07\xc7\xab\x99]\xc1H\xba\xd8`\x11\xc6\xf3\xe3U\x90\x9e\xc6sv\xd3fB\x93\x0f\x87\xd1\\\x87\x0f\x85\x89\xfd\x86\xb3\xa22\xceZ.>\x95,i\x89\xeb\xf9\x02E\x0b\xd7\x98X\xa2\x1c\xda\x1c\xdcx\x10\x05YN\xc3\x7f\n\xb9\xf7\xd8\xe38\xd0\xb8]\x86\xfc\xcc\xbeX\x8aoos\xb6\xd3R\xc8\xd9\xf0\xd5\xc0\x1b\xb4\xb4 \xe4\x95\x858\x83\xf5q&\xe6x\x8b\xc4\xc5\x9fu\xbe\x1a*\x17\x87n\xa6\xebc\xa6j\xf6\x0d\xe0\xd2\x0c\x9e\x88\xc6\xc6\xbd\xb3EY.\xe4\x1b\xe5\x98\xc9\x85\x8d\xea\x89\x88\xfe$\xe8t\x84\xfb\xd4\x92~KQ\xc6\x84\xeb\x8c\x94)?\x99\x0e\x8dq6tyg\x97\xd5j\xbd)\xa3?r\\Hc\n\xdc\x92(\xe8#\xb50\xee%\x7f>\xb6\xedA\x8a\x06W\xd9\x8b\xf1^\x0c\xd8D\xbc\x96\xa5$\xa9\xf2\xc9\x84\xbcA\x92B\xb4+\xcd\x89\x8f\x15}?\x87\x9e\xafdN\xe95\xca<\xa7\xd0=\xa8\x07\xee\xa2Q\xe0\x10\xde$\x9c\xf4\xbdJ\xc2\xb8\xc5\xe6!\x9f.\xb6\x0f\\\xdb\x99lW\xae\xb1\xc6=DjIU\xc4\x13\xd6\x12\xa1~j\xef\x1b\xa7o\xe1\xfajBo\x84\x85\xe8\x8bM\xac?\xb9\xcf\xd7\xf2\xf9w\xdf\x9d\x1b_\xeek\xbb\xfeQ\x1c\x16t=\x13\xf8\xba\xdf\xef\xbf\x8b1\x00\x96\xb3\xca\xf3M6\xde\xdf\xdf\xb0\x1c\xf3\xdd\x0f\xb2\xeb`\xb9d\xe9 L\xf6\xaf\x0e\xf6\xe5\xaf\x9f\xb3$v\xde\xc5\xf3d}\x11\xce\xc7\xe0|%>\xf4\xb7\xa1\xf3\x8e\x0e\xc1\x82\xd2>\xab\xa60\xf2\xc15-\x07\xf4a\xe6\xc1>$\x1dg\xa5?ie{\xb4\xa3\xc0\x0cz\x10\xc17d\xee\x1d\xdc\x83#8\xc08\x0e\xdf`$&\xfe\xbf{\x17\xfa\xf4\xd2C\x95\xd2\xa6\xe0\xd8\x9e\x02Py\x17#\x0e\xac\x08\\\xdf3t\xef\xf5\xf0\x00\xf2 \x10`\x0f\x88L\xd37.\xb1\xa0\x0b\x90\xbe\xd2\x81\x0f\x8f\x1eiPo\xc7\xce\xea\xf3\xd1\x87G\x1d\x8b\x7ft\x9b\xcb\xd9/%5\x90\x84h\x07S\x85|2wK\xf1\x9e\x8dG4\xf2\xb1\x84\xb4\x93\x8c\xc8N\xa4X\xbe\xdd\x8c\xbb[\xbb\xa1h\xd4\x1571\x91*y\xeap\x8c\x8fU|B\x87\xe6\xdcS\xc6\x9d\xdck\x8a\x1d)\x1f\xe1`\xf4|\x9b\x8a\x00\x90q;\xb8\xb3\xf9\x92\xbd\\,2\x96\x9bBz\xeb\xcf'\xed[\x9e\x8c\xc1\x92\xab\x80>\xff\xd7\xb8\x89\xd6\x85q\x9e\xfc%d\xd7\xe5u6]\x9c\xad>\x92Wc\x9c\xf0o\x93m<\x0f\xe3\xe5q\x14\xb28\x7f\xcdf\xb9\xeb\x0dV\x88'\xed+\x14H\x8a\xae\xf8Z\x0f\xc2\xf6j3YM\xe2j{\x95\xc5N\xbcc\xc3Q\x02zm\xa1n0\x05\xf2\x13Xp\x88\n\x91^<\x85\x19\x1cQ\xbc\x01Z\xc91\x04\xe2\xc3\x06\x8e s\x03N/\xf9\x9b\xa2\x00\xb1\xd2\x06\xccn\x80\x81\x19\x8bs\x96\xd6\xb60\xed\xb0\x8b\x99\xdb$]\x94I\xe1>\x1c@\x8f\xa3\x0b\xc7\xaa\x96]\xe7\x85=OL\xefS\xe6\x94\xe5\xc9f\x0c\x81\xbd\xc0:\xb9\n\xe3e\xc7\x0c\xfcP\xd0\x86\xbd\xbd\xfa!\x90|\x1a\xc6\xc3\x81f,\x80\xa7\xb1\x14.\xdfX[Jca\x833N\xbdUN\xb3\xa4\x14?\x90\x7f\x9cDl]s \x04\xc1G[\x17C,\x82\xd0E\x88\x9f\xfd\x17\x1a\x91\xc5\x8f7\xc9\xa6\xcb\xd0\xd0j\xef\x9a\xfb\xa0x\xd7j\xe0\xd4n\x18/\xc5\xc8yo\xea#/k^N\xa4\\\xddd\xe5\xd2l\xde$\x1c\x92wL]\x81\x9bkIN\xa9P\xa0#\xac\x95\x978\x8cc\x96\n\x89\x01\x97y\x86\xc8Bov\x1c\xa3\x00\xadn\x8b\"\xf5T+\xa2\xe6\xc9\x86\x93 \x14\xde\xe2A\x82,\xca\xb4\xfb`\x06W\x83\xb75\x06%\x0drv\x86\x1bQ\x8b\xeah\xa3G\xd2N\xd5\x08N\x96D2e(i \xcb\xaf \x9c\x03\xef\x8ek\xff_\xbb\xed>k@'h\xec\xe8S`M\xc9\xe7\xac\x04^~' \xdc\x15S>\x0d\nw\x86/\x01/\x7f\xa8\xbct\x82\xf9\xfc\xe4\x8a\xc5\xf9\x0fa\x96\xb3Xd\x0c*L.{b\xcaq\xf2\xff\xb2\x98\xcc/\xf8\x9a\xb9%\x9ac\xbc'&E\x1ag\x15fy\x92\xdeV\xad9\x9bm\xb6:\xcb\x83\x9c\xcc<\xa2\x90y\x9d\xb8L\x13\x92 \x08\xe1\xe05\xe3\x85Qj\xd4+\xd7%\x0b\xcaT*>\x0fj\x95\xf9\xe8\x82m\x9e8\x9e\xda\xdc\xea\x82\xb8N\x94\x04s\xc7o\x87 \xeakWE\xb1ql\xeb \xde\x06\x91%\x86=Wq\x1a\x86\xbdI6\x19\xaen\x9b\xe7\xb5|\x18\x86\xe8&K\xdc/,\x16\xdc\x8cRH\x15\x9f\x12T\xf1\xc4\x8bAQ\xce\x06\xf7\xb0\x87\x97\xf3\xc40e\xb0\xf7\xc1*\xc8\x10\x92v].iUL\x06\xa8\xd0\xb8\xde\xa0\xd0\x08\x9aO\x0dZ\xedC\xd2h\xa7 {\xc9\xa4x\xf0\xed\xed\xe9\xdc\xadM!e\x0b\x99\xc1\xef+\xc7\x9b\x8e\x9a\xf2\x05\x83t\x8ek\x1b\x05\xd4\x0c\x05$L&\x850\x99s\x1e\xc3:\x88\xdc \xe4\x98D\x08\xe9\x9c5\xb5+\xf4Cx2\x81\x14\xc8 \x1d\xd0\xff\xdc \x124\xa8\xa8\xd0\xac}\xd9\xa1\xd9D\xb6\xf6L\xae\xebW2\x8aO\xe1\x86\xe5\xb8?}x\xf7.\xf34J\xe5\xbe{\x97}\xf87\xcf\xe4\xc2i\xc5\x9aY\x14\xce\xdewB\x99\xd2\xb1!\x1b\xe4A\xbad\xf9c:\x89q\x9e9\"\xd8L\x1e,_\x04k\xf6\xd8\x13G\x9f\x9b eq\xfe\"\x997$\n\xdfs\xf7\x90\xb1\x8c(\xe0\xd7\xe0z\x15\xceV\xa4&`\x1a\xc8?\xb3[\xfa\xb5fy\xa0~\xcc\xf24R?\x82\x88\x97j\x8c\xfd\x82\x16\xc86h\x94\x90\xa8\xa8\x94\xa2\x10\xf5\x08d\xe52G\x95\xdf\xe3\x9a\x91\xbc\xfa\xc4\x1a5\xd1\x80\xb6\xb9R{\xca?\xd0\x88\xac\xb8\x96\x82\\\xc7\x8d\xeb\xe7k\xd5\xa7\x94\x02pW\x90\x06\xdd\xc5\x0b\xb3\x18\xe4y\x1a^ns\xe6:\x9cv8\"\x85A3\xd9\x12\xc6\xfe\xe2\xce\xf6W\x0e\xf9\xb7n\xc9C:\x1f\xcc\xa2 \xcb8\x90\xb5\x86\xfa\x91\x06\xdf\x06\xb7w\xf9D\x0d\x840-\xdcZ\xdcQ\x9b\x89\x10\x8fW\xber\xc4\xd1j\x87\xbdB\x0c\x88\xe4\xd1J;\xb9\xca$\xac\x10q\x8c>\x95.\x01egJ\x19'\x08\xcf\xc94\xd5\x06}W\xe2\xcac'\xd6\xa5?\x15^\x02\x93\x16c\x164\xab\xd3\xf2Y\xec\xcc\x19\xa9\x16]\xff,3\x9c\x0c\xfa\xb0@/\xeb;\"x\xd9N\xb3\x94(\xa7\xa4<\xf7\xef\\\xdet\x8c>^\xfa\xf3\x11C\xbb\xa2\x94\x91\xf9\"\x83\xf4\xac\xc1\xe8af'\x16V\xf2\x07{!\xe9\x07\xa7^~t\xcb\xdea\x18\x9e\xd1\x18J-\xc5[\xad\xc1f\x13\xdd\x92\xa7 \x8c9\xac\x7f\xf8\x00\xae~\xa2\x1c\x9a\x0f\xa0;\xdd\xc9\x13\xc1\x1b\xe9\x94\xb2\xc8\xc9\xe7\x83sq\xc1\xb2\x1f\x93\xf96\xe2\x92^y_0}\xdbX\xcf\xc8\xa0\xeb\x99\x926m\xdc\xd8\xbd\xeb\x19\x02\xa8\xf0\x0f\x07\xd5\x0f\xa1\xf8pX\xfd\x10\x88\x0f\xf7\xaa\x1f\xb6\xe2\xc3\xfd\xea\x07L\xf6\xe0\x0e+o#,^MJ\x85'G\xbc\x15\x94&\xf1\x0f\xb2\x88\xb9\x87\x0f\x1fT\x1b^P\x94\x17\xcft1\xd3\x90\xf4Y?\x83f\x83b=E\x9c\xd5:\xac\xcb\x9b\xb1-\x97/A,2E\xbdX\xb1h\xc3\xd2l\x90lN\xe7\xe5\xe1\xb6;\x02\xaa\xd1\x0b\x7f:\x0b\xfe\x91\x9c(F\xe7\x89Lj6\xcf:\xa9\x9e\xf1JA\xb5\x92\x9b\x0f..0\xfd\xd9\x05\xc5\\\x1b\xfa\x18\x19R\x16\xf2<\x91#\x11K\x93{g\xe3\xc1D8\xc8\x93\xe52bg\xab\xe4:\xeeJK\xa4\xb0\x1f\x0e6i\xb2i9c\xcc\x85\xd3\xeem\xb2\xcd\x9fa\xdb-\x15b!\xb7-\x9b\x8b\x91\x97\x1cG8$\xd5\xd5\xcd\xab>\xc25;\xc3\x896\x17E\xad\x96s\xae\xd7,K\xa2+6?\xdb^\xe6)k<\x0f\xc53P\xcd?'@;\xf9@$\xc6\xa95\x84!KV\xc9\xb5;u\xd4\x0c2\x87\xec\xd9\xe7>\xec\xd9\x9c\x9a)u\xcfq\x10\xcfXt\xccE\xe2\xae[\x869j\x04\xbdo\xde\xae\xf4\xf64\x7f\xb9\xcdO\xe2\xe02b\xf31\xec\x85B\xa7\xac|\xb1\xb6b\xc8H\x03\xc5\xd8\xdf\xa4\x1c\x10v\x1a\xfb'\x80[\xb6a\xb3\x1d\x80m\x13\x98b\x8a\xea\x0fA\x1be,j\x10\x0c\x7f\xcbU\xe60\x84.\x1b\x7f!\xbf$F\xc9\xc11\x87ejs\xab\xa3M8\xb9a\xb3m\xde)q\"\xec2-F\xed\x9e\xc6\xaf\xd2d\x99\xb2,\x1b7&\xf2n\x18c\x1d\xfb\xba\x0e\xf6\x13\xa1\xe5\x8cEl\x96'\xe9\xaf\x00/]\x08\x13\x1f\xc2\xab _\xd9aK\xdd\x07\xc0\xac\xf6\x1b6\xab\x12\x15.\x9b\xfd\xe9\xcc\xf5\xe8\x12\xb1\xa9\xc4\xd4\xe1\x03Wt\xa6a\xf9\xcdt\xebW\xde\x82_\x0da\x7f\x85\x0d\xb0\x10\xf6\xf2\x1eX\nu\xdf\x06R\xd1\x9b\xb2\x00\xd6 \xc9\xc8>[\x13zZr\x8a\xfb\xa6;\x97\xb57\xca\x11\xc1\x87\xad&\x85\xf8\xc2\x07\x81OA\x7f;5\xcf\xe3=\xbb\x1d\x83\xb3\x0e6Hb\xde$\\\x8c\xce\x1c\xf34\x84\xe8\xdc\xd9]B\x1aJ\xf2A\xb2i\x07\x98\\\xc8)\x1d\x89A\"\xc4\xb4\x9c\xdc\x1d\xe3E\xb8\xcc\xbc\xb63w\n&?Of'7\x9b \xce\xc2\xa4\x834\xc2\x85G\xb6\xf9!\x8c\xdf\x87q\x8bX\xb4\xa5\xe2a\xb6\x89\x82\xdb\x97]\xa5\xa3L\xaf%R\xd9I\xff\x8f\xe6\x9a\x11\xa9\xb6\xdb\x0d\xd7\xa6\x10\xc6\xd7a\xfe#\xa2]\xcb\xeaa'OO\x16\x83\x1f\x83M\xab\xd2\xfe\xb3\xd0\xf4\x17x\x13\xfcOg^\x0b\x8b\x03T4\xc6p\xda\xdc,\x7f\xf2`\xd9\xe9\x86\x05\xa7\xdfV\xef]\xfd\xc9\xa4\xee\x91[\x14-\xfa.\xf4,\xc7\xc2\xdd\xf4g\xce6)\x9b\x059\x17\xf1OI\xf3-^9B]3\xf6\xa5\x15\xa3\xee\x9a\xccS\xf2!\x0e4\x86\xa4\xbdh\xa1\xa7t\xb8JQ\xd6UZTi\xa8\xaa\x8a-j\x19\x96\xaf\xdb \xc4\x82u\xb7X\xb4\xf7R\xd2/;\\\xf0SzU\x8b.\ne\x15\xaaE\xf6\x80\xbaN\xd9B\xf2AW\x81Z\xf4O\xb0\xe8\xc6-\xda(4\xe8\xc7-B\x12X\xd5\xfd\x16\xce\x0ff\x89\x96\x04b<\xd2\xa9}mo\xb0f\xd6\xd5\x9a\xebzB\x04P\xf7_\xd7\x1fa-\x89\xa4\x89V\xb8\xb5\x0b\x8f\"\xf7\xc7\xb6\xabb\n\x9c\xc7\xf0s\xf3\x8c\nm\xba\xcdh\xdf\x11<\xba\x82\xb4v\xb6-\x96P{\xd3\\\xb5tR)*\x97\xde\xb5U\xd7\x0eiUu\xed][uqD\xa7\xaa\x8a\xdf\xcd\xd5\xa4<5\x86\xcb\xf6\x82\x82\x95\x8f\xe1\xba\xbd\xac\xe2\xe3c\xb8h\x19y!$\x8c\xe1e{Y\xad\xe5W\xcd\xa5K\xf2\xd0\x18\x8e\xbb\x94\xd6Z?k.\xaf Och\xd9\x9d\x92\xe44\x86g\xcd\xa5u\xc1r\x0c'\x1d\n\xa3T9\x86\x9b\xe6\xa2\x8bx\x0co\xac%l\x87\xab\xb5\xb7\x1f\xcf=\xbfrO\xe4\xa3\x9b\x0d^mSfJ1\xb9\x92\xe4\x02-\x1d\xb5\xb3\xa9\x12s\xda\xab84\x16t\x00\xdd\xc7J\xdf*\xbc\xa4Z\xd5\xc4\x0c\xaa\xb2\x84\x8d\xf2k\xc6\x05\xcc\x15#&\x00\x13\xa0\\\x14\xbf7\xc7\xaf\xc8\xe6\xf8\x15\xd9\x1c\xbf\"\x9b\xe3Wds\xfc\x8al\x8e_\xfc\xc3Pw\x1a\x8a\xc8\xb9\xcb\x92k\xfa\xb7\xf6\xd9\x9a5\xfadi\xfeX&k\x8cv\\ip\xc7\xf2?\xd9\xe5Jx\x18bq\x992\xa7\x9a\xd6\xc8\xe8\xd4\xf8\x19\x07\xa7d\xa0Z\xb2\xfc\x07$t\x06)\xbe\xab}j\x17\xdbT\xbe\x83\xaa\x1c\x9b\x14\xdf\xc1l\x9b\xa6\\\xbch\x10t\xd1>\xe9\xc6\x98T\xbc\xd1y\x0d\xef\xe8\xb6\xceO\xab\x90Yd\x1dg5r\xa4O\xeb\xd7\xf0\"\x11\xdc\x03D\xf0\x19\xbcS\xe0|\x8d\xe7\xf5_;\xf0ug\xd2Z\x86\x00\x93@\xd5bg\xfc\xa4=T@a\xb3\xe6\xb6\xac\x06\xa3\xa50\\\xfb(\xcf\xa7\xcc88\xd3\x90\xed\x99\x18\x87Nwg>\xccj|\x84Z\xff\x171\x16\xcf\xfftb\x8c \x8b(\x15\xfa\xd5|a\xb0\x8b\xd3\xac\xba\xf0\xc3WL\x91_\x15_?\x82 \xe5 u3\x8fr\xe8\x0f\x1f\xc3\x0c\x9e@\xf6\x18f\xbd\x9e\x07\xd1tv\xae\xd7\x9c\xce\x0ca\x01\xc5R\xc6x\xe1\xd1\xe6\x9c\x8b\x18\xd8\xca-fA\x14 \x96\xc1|\x98\xf2\xba\xe72\xf4b\x84IZ\xc3\xc1,J\xb2N\xeeV\xc2\xc5J\xb7\xfd\xa11\xfc9G\x85\x10\x7f\xbbU\xffz 4\xc3\x8bZ5\xa6\xc77\xe3\xb7\xe0\\_\x96\xe4ub[\x1d\x0d\x9eqwcj\xba\x03;\xa4\xd3\x15\x96\xa6\x1d\x86\x10\xeeb\xf1\x0e\x84\xf1t\xf0\xec\xec\x8d\xbd\x14\xdfm\xed\x04-\x90)m\x1b\xcc`\x98\x0e\x15\xa1)\xd6\xc1\xa9\x81sS\x8aT\x87\xaf]f\xcb\xd0\xd0\xc6\x8a\xe7\xe1U\x8dT\xeb\x8f\xbaV5\x06g\x1e\x06Q\xb2\xecoo\xacWq\xbfH7\x97\xc1\xec\xfd\x1f\xea\xe57Z<9\xa5>^\xcf\xff\x8d\xfaZ\xb1`\xfe)\x9d\xad\x0e\x95\x1c\xe8<\xbb\n\xc2(\xb8\x8c\x18\xea\xfbI\x1a\xfe\"\\\xb8\x9a6\xfbr\x9b\xe7h\xe0\xb5\x0f8\xbf\xdd P\x89\x92\x9d&\x86\xfc\xa0\x8f\xd3k\xa8\x91\xc4\xba\xb9 \xeb\xec\xbc\x02\xd9\xd5\xb2q\xf4\xd7\xe1<_\x8d\xc19\x186\x0cd%\xa2;\xf0R;\x8f`\x9b\xd5e5\xfdY\xa5l1\x06\xe7+\x9c_\xc3 n\xa20~\xff}\xa9\xb0\x05y\x91\xe9~Y\x00\x9c%q\xce\xe2\xdc:\xfbh\x80|\xee\x8c\xfd\xcd\xf5\x06\xeb`S\xcaI\xdex\xfd\xb7\x85~\xce\xda\xcc\xb6\xc8~[\x0e?\x9e\x9d\xbdi=\xf0\x98\x17,\xc1\x1a\xb7D>e\x13X\xcb\x19\x96\xce\"[\x0f\x81*\xa6\xb8\x96\x93\xdb\x92\x91\xaf\xc5\x00\\1{\xd6\xdd\xa1\xe5c\xb3\xb4y\xf8\xd4\xbe}9%\n\xdf\xfeK_\x12\xcf\xbf\xf4\xa5\xff\xc5\xfa\x92\xe0|]4\xa6\xce\x97S\xf2\xeez@\\\xd7/\x06\x1a}|\x93\xa8\x83g\x9bI&\xafim\xe6\xd4\x15\xffR\xda\xccO,\x80\xac\xac\x8dy\xa4\x8b(\xd9\xedU\xb2\xd9n\x1c4,6+u{{\xbb)>\x89\xa8\x13\x14\xee\xce\xde \x0b\x7f\xb1D\x13\xf9\x92:\x10\xef\xb2\x7f\x9d\x06\x9b\xcd\xa7\x08\xbc\x1d\xe4U\xad\xb3\x04\x8e\xc0\xb9\xccc%\x113\x88\x92\xd9{6w`\\\xfd\xb0\x8d\xc5\xa7\xae\xf2\xaa\xf8\xb5\xf3\x14\xb2M\x10kR\xbb\x1c@\xa3\x98\xfe\xcf\"\xe5\xe2\x82\x7f\xa5\xad\xf1W\x1d\x96U\x13|\x1b\xea\x9bG\x8c\xf4\x14\xddkm#\x8f\x85u\xf8_\x92\x0d\xfcK\xb2\x81\x7fI6\xbf\xbddc\xbd7\xc0\x06Y\x9el8\xd4\x07\xcb\x80\xf8\xb0\x99\xff\xc8\xcb\x05\xd2z,:\xb1\x88&\xe8lop\xa9\xff\x9f(\x8e\x94\x1c\xd5?\x8dy\xef\xc6R9\n\x96\x85\x94\x8b\x0b\xceH5\x9am\xf8\xda\x81\x0b8A\x1a\x06\xfd(\xb8d\x91c\xea\x06h\x9c\xd6\x8e\xe4\xf7\x0e]}!>\xfeO\xc2\x93\xd9g\xf2\xe4\x86\xfa\xe6\x11\xff/\xb4\"\xcc8K\xad\xf1\xd4D|\xa9q\xe1PV11\xdb\x99\x89\x0bo\xc5\x87\x1a\x17\xce\xc4\x87\x1a\x17\x8e\xc4\x87\x12\x17\x9e\xc9\xc8G3\x11\xf9\xc8\xc4\x8fg\xbf=?^t\xe5\xc7\xb6\xb0EU*l\xe5\xb9W\"\xafz\x95\x98[}g\x92:\x0fl W$\x16+\x18$1\xa7\xcd\xc7\xab ^\xb6g0\x02\x8d\xcf\xb1A\x1c\xac-\xbaXP\\[\xab\xb0\xe8\xbf\x7fDL`&\xf4\xe3\xfc.\xc3\xbb\xee|H\x9d\x06S\x0fb\xc7\x1b\xa9\x1f\xdf*\x15\xca\x0d\xc8\xe3\xd7\xd2}\x94,M\x91tv\xe8\xbfY8\x08\xda\x14t\x8a\xab\xd0\xc9@B\xc1\x154\x93H\xcd\xe6\xdd\x1a\x80U@\x819\xa25 \x1d\x19\xe4 \xc9w\x96\x99\xc5b\xcd\\s:\xd3\xa0~\xec\xbe\xc3b\x9a7\xb3\xe3Y|P\x84\xfa\xe0\xbf,8\x0ee\xd9)3\xcaN\xc1?@vj6\xe2t1\xf6\xc4U\x00i\x83\xa5\xee\x87\xeeyW\x1bR\x88\x85\xbb\x9d\xd0\x07t\xd2\xcd\x91\xff4g\xeb\xa6\xabH[*Jy\xe0\xda\x8cO\x19\x15\xfe\x96d\xc8\x96\xa3\xf6\xa4do\xb2\x97\xa5\xc0\x19\x8b0\xcaY\xfaIH\xb7\xb77\xc3k?\x96(\xea\x80\xd8g\xef\x7fc\xee\xbfc\xe7r\xe5D\xd4]\xbc~\x94\xdfnXC\x8c\xd8\xa6\xc1\xcc\xbf\xcc`&;\x0c\xa6Q\x8f\xb0\xdd\xbf\xd8\xdd\x088K\xe2<\x08\x9b\x0e\xd9\xf7\xf66h\x95\xe4b\x87\xb5\xdfE\x92\xae\x1b;Nb\x8a\xf2\"o\xa5(6h\xebvS\xa6\xf6mI\x97Z\x16&\xe8t\xc2\xd9v\xba7[\xb1u\xd0z`\x18\xe3\xf2\xb6\xb4\xb5\xd3\xe9\xa6.\xc3\x8c\x81\x95d\x9a\xe6\x9a\x81vy\xad\xe5\xdeK\xf9\x08\xf5\x13\x8e.\x0bN\xea\x7fA\x00\xbd\xcc\xe3VK\xb5\x00P\x8e^\x0b\xfa\xf3\xc8:\x82\xack\xef\\e\xa6\xa3yi\xa3\xee\xac\xcdjR\x96m\xc8\xce\x0fX\xc6\xf1`\xfciC\x15\x1e!\x84H\x1d=B\xeaS*\x00\xc4\xba\xb8e\xeb\xf8'\x8d\xb5e\x0c|\x8b\xe7I\xdc\xe4\x97\xb1\x83\x97\x8as\x8cn\x1bh\n\x9bs\xa25o\x03 \x01\x94t\x18\xf0E 7\x9b%\x1b\xd6\x9f\xb3E\x83/\x87\xa5\x9bMq,q\xc6[\xc9 H\x19l36\x87<\x81e\x1a\xc49\x041\x04\x9bM\x14\x8a\x80\xd3\xf3p\xb1`)\x8bs\x88\xd8\x15\x8b2H\x16\x10\xccf,\xcbx\x95y\x90\x07\x90\xc4p\xc9VA\xb4\xe0\xdf\xf2\x15\x03\x16\xcfy\xa3\xe9\x00N\x82\xd9\n\x9e\xbd:\x85up\x0bs6\x8bx\x7fI\xcc Ia\x9d\xa4\x0cp2\xd9\xa0i\xf7\xf5Q\xf3\xa6R\xf6\xb7m\x98\xb2\x0c\xbbZ$Q\x94\\\x87\xf1R\xb6\x04Dg\x80b\xe1'1\xcb\xe06\xd9\xc25\x9f\x9a\x9ac\x9e\xc0\x19\xa5\xd1\x85\xb7\xa7\x03\x07\xe3\x03\xef\xc6\x81?\x8d\xfb~\xac\xbb\xd64J<\x9f\xcb\x91A2\x9f\x06%\xc5\xbe\xf0\xdb\xb6\xa6w`\x00\x92\xbd\xb5\x05\x8dA\x10oR\xa9\xda\x19\x04\xa7z\x9ft] \xeal\xa3\xa2\xe4b\xbf7\x1b\xd5\xef\xf2<\xc8\xa7?,\x96\xa8\x7f\xb6\x93\xa1\xffy\x17\xb6\xbe\xa8\xda\xdd\xa6T\x8b\xd0\xaaH\x0b\x9aUo2\x905\xeb\xdc\xbb9\xbaw\x93kC\xe5\xe3\xd1\x16\x1a(\xd8\xc1}^h\xdc\xc1&\xfc3\xbb\xe5\xc3hR\xa4#*|\x19d\xe1\xac\xad\xecL9\xd17+\xdb\xb9\xce\x9a\xcc\xda_v\x1db\x06\x93E\x13C\x9a\x05\x19\x031\x0fgl-\x06bh\xb6\x83\x8dV\xce\x02\x1d\xb5&\xe8\xae9AW\xed j\xfaJ\x87\xc8\x1c:+\xec\x10\xf9c'\x0d\x0dHF\x15\x1a\x9a=\x8d&4\xe8\xf6\xf2\xb9LY`9V\x05\xb5\xbf\x08z\x9f\xb1\xbd\xd1\xbf\xb6\xf7\xf7\xb9\xbd\x92U~\xf2\xcev\x928A\xedn\xf3\\|p\xde\xc6\xef\xe3\xe4:Vas4'nTB\xc1\xf1a\xd1\xf5v+t8\x0bo\x1b?\x8d\x1bz\xe0\xf4\x7f\xde\xae7V\x15\xcb\x90h\xe6\x7f\xf8 \xe8\xefR\xba\xfc\x97L\xf9\xbfD\xa6\xe4\x82V\xd2@HU\x1c\x00\xd7A;E\x93\xd0\x14\x17e\xd7,\xcb\x82%k*\x9d\x16\xa5\xb3d\x9b\xce\xac\x02\xd4\xe7\x92\x1e\xdd\xc6\x83\xb3\xb5\x85m\x05\xcc\xd3}\x1b1\x13\xe4\xea\xcfe0{\xbfL\x93m\xd4)\xd5\xe7\xfbm\x80\x1e\xf5\x07\x97\xe7\x1f\x16\x98\xbay\xa7\xa1t#\xaa\xc9\x95\x16t\x7f\xea;w\x8a\xd4\x10\x9c\xe0\xe14\x1c[z\x9c\xfa\x92\xdbX\xd8\xef\"\x94w\x1b\xdc\x83.(u0\xb2\x81\x12\x95\xba\x99\xc4@\x19\xe6\xda\xf7.\xc44\x8d\xcei\xbc\xd9\xe6m1v\x03*\xfb:\xb9n+\xb9\xa5\x92\xc7I\xa3\xb0\x08*\xff$\x1e\x19\x9fp\xc1\xac\xad\xfc\x8c\xca\xff\x18\xa4\xef\xe7\xc9ukX`\xcaB\xe9\xfc C\x9d\xbe\n\xf2U\x9bO\x0e\x08\x17\x96\\\x04W\x12\xa4\xa9\xb9\xc2\x1c Y\x10E8\x85\xcc\xf5v;\xf0\x92\x8fdo$\x11\xf3%9\x9d;\x1e\x9e\x7f}\xba\xe9\xa2\xdb9W\xcb\x19\xea\xean{\x99Y2g\xaaT\xa2\xe2\x04\xbb\x0e\x07B<\x07t\xfe\xff\xff\x0f\\2pz\x8e\xbd\xa5E\x9b\x11\x84\xa2#OU\x16\x19\xcd\xe7\xce\xf1!9\xb7V\xc6\xb4\xb6\x9bF\x87\x98\xd5}\xc3\xf5\xb2y\xd3\x19j\xd0\xb62\xad\xb7\xf4I\xf7\x19\xcb\xf5\x9a\xb3l\x96\x86\x9b\x1c\xa3^7\xcf\xe5\x93\xc7\xa4\x1f\xfc\n\xbd\xa8\xeb\xd6\x96w\xf5\x8b\x8d\xe24\xde}\x0ca\xfc\xd9#\xa0;\x13j\x14\x88\xeec\x07\xc1\xa4\xc1\xf1\xa04\x18\x07\xbe\xc1\x07\x1a\x9dB\xb6mC \xdb\xc0Dx\x8ep\xe5\xabE\xcd*L\x9e\xf2\x92\x06\xfel\x82%\xcf\x87yS\x98\x8a\xae\xde\x83\x9f\xe4g\"\x1fT\xcd[\x0f\xb2\xa1\xfd\xe4\x1d\xc0\xea\xefD\x9f:\x0b\x1a\xa6\x80\xa9\xa6\xc3\xec\xf2\x907m\x97\xd3u\xc1\xa2N\xbbK\xbb\xa67e\xdd\x85+\x91\xfa\x8e\x15\x97\xbcZN\xe3\xc8[6\x0f\xd2%\xcbi\xe3\xede\xe5\xdd\xb7\x8a\xbf<#\x91\xbcmg\x85\xc0ega6\xf6\xc5\no\xfd\x10\xd3L\x87\xadz\xfc\xbf|\n\x8a\xe7\x93\xac\xbe\xffd>\x05\xb0\x9bN\xde\xe9f)\x88\x9e\x7f\x83\xc4\xdc\x0b*\x186\x8cb\xdb%|\x05\xdf\xd1m\xab\xde\x11a\xa9f\x9d`&\xf3a\x0b\xc1w\xb0\xcdXj\xbfP#v\xbfK\xf6RR\xce\x1b4o\xa9\x9c7\xccS*\xe7p\xd4Bs\xe4\xa8m\x8a<\x7f>r\xf0\xb4\x9a\x19\x7f\xeb\x94\xa8\xffp=\xbf\x8bc\x06\x94\\HZ\x95\x0e\xbaM,\xf5\xfcX\xd3\xf39\xda\xd8\xd6\xbe\xbe\xf0\xffK\xb5\xfdv\xed}\x978\x93\xf0;\xd0\xf6\xa3O\xd3\xf6wS\xdf\x17\xbb\x99\x08\x0c\xda\xbe\"z\xedj\x7f\xf2\xab\xaa\xfduc\xa3\xfetP\xfb[N\xccH#\xb1GH,\xd4~\xe7\xdb \x0bg\xe5\xe8\x88\x8e\xbdj\xab\xce\xdb\xac\xc3\xa7]tx\xfb\xb0\xad:\xbc\xadJ\xd0\xb6\x14\xad6\x89O\xd7\xe1?yLU\xdd\xf5\xad\xe4yR}\xb5V\xac\xa8\xaf\x8e\x0f\x1b\xfc\x9f\xeb\xaf\x0d~e\xcd\xc3\xf9\x82\xfa\xabpC\x9f#q\xa7?[j\x10\xafw$\xde\xfe*\xfa\xf1\x17\xdb\xa8WA\x96]'\xe9|\xe7\x8d\xd2\xed\x0c\xbf\xde>\xed\xbe\xfa\xc16O8g\x8bX\xcew!f\xd7\xfd\x8d\x98c\xb7}\xebXZ@P\xc7\xd2\x9f\xb6\xcb_\xc4\n\xf2Y\xde{\xff$V\x10\xd3\x11yy\xc8\x8b\xdf\xbf\x15$\xd5\xac \xf6R \xda\xf7;\x18I\xd2\x16\x99\x8d\x1c\x9b)\xb5\x176gf\xe0\xc14<\xe7\xb2\x85\xaf\x9b@\x9a\xe4V\x94q\x03\xf3n\xa2\xe5\x84Y\xa3\x0b\x94w\xf5\x9f\xc9\xc7aa\x8d\x1b\xb2\xb0\xf98,l>\x0e\x0b\x9b\x8f\xc3\xc2\xe6\xe3\xb0\xb0\xf98,\xc8\xb2R\xfe\xc0\x05Yw!M,\xfc\x8fGw\x1fxf#\xcb\xe2\xb77\xb2l\xbe\xa4\x91\xe5\xf7\xe6\xf80\xff]:>\x04\x9d\x14\xee\x85*\xd9A\xc3\xe3\xbb8\xe3 B\x17\xf8\xb3\x06\xc5\x07\xa3\x98\x0c\x8a\x04d\xae\xd0\xc8\xed5\xae`Bb\xf7\x86$\\%j\xb5f\x16]Wj\xce\xa2\x90\xc5\xf9\xa9H&\xba\x1a\xc8\xdfm\xed,\x8d\xed\x9c\xb1Y\xca\xf2r[\xf4\xae\xad\xbd\xdbJ{R\xacx\x8379\xb0\xb6\xc8Q\xd8\xbfL\xe6\xb7\xceg\xbb\xa7\x04\x9b\x0d\x9d\xb5\xad\x06\xe2O\xfb\xe0\xbe\x84+\x0b]\xdb\x1c\xc3\xf4\xbc\x01\x14\xc5\xe27\xa6\xdb\xd4W\xb51\xb9favkH\xea(\xd7y\xdc\xb8;\xfan\x8c\xe1\xd6X\xee\x1f\xe0\x8e\xf3\xab\x18\x9b\x9a%\xbd\xaeaU@\x85Vi\xa3?\x00\xbbEV\x81]\xa3\xab\xc0\x8e\x11V@\xb0\xe1\xbc\x83\xcdkKS\xec\x96/\x05\x8a0+\x9d\x8c^\"\xa9I\x07\xa3\xd7\x82Jv0zm\xba\x86y\x01\xe9J\xb2\x83\x85lE\xe5w\xb3\x90]Q\xa5\xae\x16\xb25\x9e\x1b\x84\xd9\xcbgg\x87\xcd%9\x89^\xbb^-\xfe\xe01\xd7c1\xea ^o\xc7\x9f\xcd-\xdd\x16-\x11\xf59N\xd9\x9c\xc5y\x18D\x19\xb5T\\\xa4oi\xea\xff\xb2\xf7\xef\xebm\x1b\xc9\xa28\xfa\xffz\x8a\x12fN\x06\x1c\x93\xb0(\xdf\x99(>\x89-\xef8c\xc7\xde\x96\x9d\xcc\xda\x1ao} \xd0$\x11\x83\x00\x02\x80\x944\x89\xdfe?\xcbz\xb2\xdf\xd7\xd5\xdd\xb8\xf6\x0d\x94l\xcb\x19c\xd6r(\xa0\x80\xbeUW\xd7\xbd\xe6\x98\x04\x06I\xfc\"6/\xeci\x0d\x8eu*I\xc8\xe2\xf9\xd9\x91\xc0\x9f\x14\xfc\x96\xfeSg\x98)\xba\x9d\xb9\x07\xdf\xf7\x0d/\x1e\xa1\x15\xe6Cj\x16\xe5\xc2\x82\xb8t9u\x80W\xc5\xdf;\xbaT\xa7\x9c\xad\x1fG![\xbff\x88\xbf\x08\x040\xf4\x0fsC\xe8;y\\/dK\x1dgT\x9a^\x99\xaf\x94?\x06\x07\xdc\x17\xdfm\xca\xd5\xc1\x18\xe8\xed\x16\x1a\x823\xd2\xb9\xbc\xacL\xca\x02\xbd\x0e\xd57\xe8P\xcb\xba\xca4\xe7Ft\x1e/\xab;\x0d\x9dj\xbd\xf5\xd0g\xa7\xff\xa5J\x9b\xc8\xde8\xd6\xb9\\mM\xc3\x14\xaaU\xd9Zj\x868\x86\xb3\x1d=\xbd\\'Z\xd3\x11F%\xc3\xcc9\xdd\xf8s\xfc\xb9\x1ci\xbf\x99\xf5?\xc9R}\xbcy\xf5l\x80{SRo\xd8\xea\x13o\xf2\x98\xe5F\xa9\x19\xd5~\xef\xea\x9f\x17\xd6\x1d}\x9d\xbe#\xac\x83\xd6\xfds\x1a\xb8\\\xd2\xd7\xab\xcei\x1b\xd4/s3F\x077\x88zm\xc7\xe0<\x89\xd3\xb3\xe13\xca6\x1e\xfa\"\xd6\x93\xb8\x87\x93\xf8\x10!5\x0e\\\x81i\xe7\x1b\x01*=\xb0~\"V\xe5:~\x82AB\x98\x01\xe5\xb4\x92\xb4\xb4\x13\xb2ij\xff\xcf\x068\xaf\xb57pe\xf9\x12;X\xf5\x19\xa3E\xa4\xf4\xe71\x15\x17\xa6\x9a\xf8y@UE\xf1\xaeL3\n\xa8\x1b\xa0r8\x11\xf2u\xa6\xdeDa\x7f>\x0dl\xb7\xb5\xb9\xc2 \xfd\xd2\x9f\xe0'/a\x83@\xfe\xd4JE\xfd\xb1\x11\xb0\xda*Z\x04\xcc\x9aV\x8d!\x08h\xe3=\xf9\xf9b\x9b\xa5\xb1b\x98i\xa3\x8dq\x96/}\x16\x18'\xc6r\x8a\xf94\xb4\x08\x87S6\x14\xd9\xda\xd4\xae\xa9d\xf8|(^\x81r\xafqR\x11 \xdb\xf3\xb9\x0bV\xbd6\xbf\xb8\x1bfiF\x98f\xdc\xbf@?B\xaeoi\xab\xe9\xb48\xf3\x8aA\x02B\xea\xf8\x95\x81=`i=\xb4M\xd7\x0e\x14W\xd9\xf0o\x1b\x92\x1b\xc6\xfc\xbf)\x08d~\xee\xafII\xf2\x02}\xe6)#\xc99E\xd4t\xaa9^|\xdce9\xbf\xfaJ\x8c\x19\xd9'\xc5\x96B\x1e\xd4\xdd;\xa3\x9f@f\xbc\x01'\x14\x8fZ>\xf5\xea\xe9\x0bk\xf642\x1cf\x15\xd8`\x02\xf3g=\xcd\xea\x89\xb3:\xc8,\xd8\xa6\x86\x9fA\x07\xbd\x0c\xda+\x86\xfa\x12\\\x1aB\xde*+\xc4\x87 m\xbd\xfduE{\xe9\xa3\xef\x93\x82YWl\xf6\n\x03\xfd\xb2_\xda\xfb\x85O\xe0n\x18\xcd,.W\xb5\xdfd\xf8\x7fl\xd3\xbdK\xec\x81=$\xfb\xa7\xf8\x8fe:W{-\x01W\xc2\xee\xb4\x92\x98\x9d\x9d\xe3 \xd3\xef\"\xe6\x9e\x0e\xcb^\x0df\xa5\xa1\xd1\x13\x12\xacS:]j\xe2\xa03y\xc1\x8a\x04\xef\xe6\xa9\xa2 \xb8\xb84\xadZEt1\x9cc^\xdfV\xe9\xc3\xe8\xdea9\xa2\x1c\xb8\x01s\xfc%\xba\x8a\xb7\x84\xfb\x8c\xd9PD\xaf0*(i\x08gpf\x06\xe6[\xa9\x9a\x19\xf3\x1b\xf5\xce ^\x9a \x1e\x19\xb6\x05p\xdd\xe4% 54\x89\xb5\xf5|\xed\xba\xd4\"\x9d\x8a\xb9OM\x0c\x8bJ]~\x170M\xc4.H\x8dTp\xe7Q\x9au\x94\xd0iO\xaf\x96\x03\xd6^r9\xbd(t\xdal\xea\xbfMM\x97\xf2\xb2\xd4\x15\x84$\xb5\xef\x18\x8e\xae\xc2\x03R5\xe0\xd0f\xb8\x1f\xcf\x03\xf2\x92\xf87<\xeb=\xb0\x859G\xc9H\xc7'eC\xda\xd6&\x887\x1e\xee\xbd\x0c\xf8\xba\x9e\xdb$\xc0\xff4}\xaf\xde\xd2v\xbf\x91\x15_\xb3\xfa\x97\x1d\x81Ej|\x18\x90\x1e\x1fx\xe7\xab\x14\xf9R(K\xc7\xddz\xcc*\xc7\xdd\xf0\n\x1cw{\xe5\x95\x94\x94\xa3\x94\x94W\"\xbb\x97Wj\xe3\x82i$\xc0GS\xd6n\xc3\xea%\x1b\\\x04\x8b\xe4\xb9\x112\xad\x1dq\xd0\x15O\x0d\x19\x0dq\xc1\xf1\xe1\x10R]\xe2\x92\x8d\x88\xf4\xac\\\x00\x15\x0en^\x10\x13?\xd7\xf8\x1f3\xc7\x82\x19\xe8Y2\xce]\xf9\xfa\x82\x1c\xc2\xd8\xcb\xe0\xe4h\xce\xbd\xb6\x02\x81\xc7#C\xdffU\xa4\xba\x16\x8c\xaf\x94\x96M\xad\x17T\x9b{6`S\xaa\xcd\x7fK\x9b|$\xe06\x8a\x91*\x11\xbc\xc5mZm3\xe1\x1covw\xcf\xd1q\x02\xb9H\x9doj\x8a`\x94\xc1/D\n\x019\x06E\x0bp\xb1\xcc\xf4d\xca==\x18K\xca\xcbJDIH\xce_,\xdctd\xf2\x97\x8b\xa0\xf72\xaf\xa0{\x92\xbe\xd5\xf8uXy\xd1C\xc3crx\x15\x1d qA`/g\x1e\xda\x8a\xf1\xc1\xb7t\n\x18\x84\xb9C\xa23\x9d\xcf\x0dv\xba\xa9\x9c\xc7\xf7\xb4\x89\x84\x94\xf5\x8148\xd8P\x04\\1\x0e\xb6\x91KOY0\xaa\xd5\x14\x9e\xe1\xcbsX\xa4cPE\xdf7\x16\xc9WO\x02\xe3\x98\xacF\xdf?\xe8\xd4\x1e\xe9\x89\xcdy\xc46\xaa\xd5y\xc4\xe6\xd3\xe6_\xfb\xe7\xca\xbf\xbe\xf2\xb2M\xb1r\x9d\x9c\x14Y\x9a\x14\x04\xed\xca\x87\xa8\xd3WP3E\xde|\xd6^ev\x1c\xd2\x1a\xba\x9c\xed\xd4\\\xdf\x95\xf8C\xcca\xcf\xf3y\xc8\xe0\xd8T\xb6^hS0\x87R\xa0d\xe9\xc0\xe1!\x92\xd1t\xc1\xa2X\xc4\xe7*C\xdd!\xaa\xff\x12\xfa\xc17\xaf\x9eV\xb2\x9e\x9bu\x03\xa5(A\xd9b.\x03Vr\xeb\x15 \xa3\x9c\x04\xe5\x9bZ\x9f\xd1\x13\xe8t\x0c+\xfe\xd1\xaf\x9c\xd1[\xf6\x93\x8bS\xa7\x95\x84\xe1\x8b\"9\xa6@\xb09\x8b\xe5\xd4\x19\x89\xba\x06\xa2y\x99Lp\xee \xcd\xe6q\x1a\xbc\xc3\x12\xeey\x1a\x9f\x9e\xceK]\x08c\xdbF\xc4\xff\x92B3\x0b\x11\xf1sI\\\x94\xb1\xde\x89\xa9\xce\xc9\xf5\xcc\xa1\x8aD_\x9a\x03\xe4Z\xd69\x19\xb3\x1f\x07X\x15\xd9\xbd\xf7y\x9c\x05\xd0\xd29\xad\x88\x1f\x92\\b\xf53\xed\x19\xbb\xe0\xc9F\x98\xa1\xa0=\xc0\x9b\xd4\x17\xb2\xce\x1b\xd9\xc1\xbb\x12L{\x81\xcc\xc9N\xea\xd1\x86\\d\xfc(\xc3e\xae\xe9\xa2I\xfb\xe1\x8e\xc1\x81u\xe1\xe8G\x1d\x1aGm8\xf3\xa1M\xa0%Y^\xc6;gr\xb1\xa9\xa7\x06=*\x06W\x9c\xdb\xa1X\xa5\x9b8\xac\x08\xe1\x9b,\xf4K\xdb|\xac6\x15\xcd\xeb$\x0e\x9e\xd0\xf9\xa0tI\xea?\xff\xf8\xa3 E\x0fq\x0e\x81?\xdbO\xd9\xf1\xcd\x9f\xf3?\xda\x10aTd\xb1\x7f\xc11\xeb\xb1P\x7f\xb07\xe4\x0f\xa5c\xf8\xdcR\xb2\x8a\xe9\xd4\xc3\x0eM\xca\x9a\xd6\xf0\x06C=T\xd5\x8e\xe5\x93\xac\x7f\xd3\xafx=\x0b3?T\xcax=\xc7\x07\xfc\xc8\x12\x98\xa2\x87\x0c\x98\xf3\x00\xba\\<\xdfPi8\x14\xe4\xe9!\xf8\xde\xbau\xebI\x9a\xbb\x9b1\x14#\x98\x81\xef\xe5\x9d\x9b\xfa\x86B\xa8\n(S\xa1{cL\xa9\xb0\xa2\xa7+\xcf@$\xd7\x974\xafm\xfd\xf9\xea\x10\xf1\xca\xf4\xc7cSE\x97u\xfdb\x92\x96\x8f\xd3\x00I\x12\x86\x87k\xdf[\xd6\xef\x11\x9b\xf4\x1d\x175<\xfa.\x1a\xc0\xe75x\xe3\x98\xd0\xber\xda\xb7{n-\xd2VlO\x1c\xca\x9f\x92\xa4\x9c`\xe4\xd8[JZ\xb6'\xce#~\x13\xa3\xc24y\x85\x80\xeb\x94\x12\xd7 ,\x16\xea\x9c\x81\x8a\x8d\xfb=\x0b\xcf\xd2\xber\x0c\x87]wm\xa3)\x1c,\x0enk_W\xe8p\xf9\x0c\xc3\xe2\xc8\xe8\xf5%.\xa4\x95z\xa7\\\xe0l=8\x98\xe3\xcc\xc1\x90\xf7\xed y\xcb\xa2\x15\xb5\xef\x9a\x92x<\xa2\xe24\x1e\x06\xc7\\\xe0\x96\x8b\x82`1iMn'\xd0E\xaa\x1c\x99f\x96\xd3\x0fm\xe2\xf6\xd1\x18V\xda\xf4\x06v\xcc\xd7\xed>\xf3\xf5\xe6\xd53-\xdf5\xd4)TD&\xd2-\xa0\x1e\x8f%\xa3\xb7\xd2\xa7Xh\x8e\xe7\x98\xe4[\x92\x83\xd8O\xda1a\xf0\xcc\xc0Q\xb1\xcf\x16\x13\xf6\xeeN#+\xe9~1\xafR\x99\xef\xd85\xb6\x1dw\xec[8\xa8\xd1 \x8d!H\xe3S\xd6d5\xeb\x13z\x8f\x1fk\xban8h$\xd4.\xd1\xd5\xf5\xc7\xca}\x9cv\xea1)\xfd(.\x0cy=J\x8c\xa4\xfdP\xab\xf8\xd1Vo\xe8\x92\x85cX_e(S\xd5\xfe& kfc\xa7\xd1G\x8d\xe0\xba7\x8d\xaf\x81S\xf9\xf8_1\xaa\xed\x84_K\xdd\xf4\xb5\xca\xf7\xb6\n\x8e\xc1\x0d<\x04\xe1\x86\xb8]\x95\x99\xae\x03\x18.4\x9f>7\x0e\x8e183\xb80\xb0\xc8\x0c\x8e\xa5'4\x04\x17m\xf2x\x06\x06\xe6\x9c\xf3\xa7\xda\xcc\x89\xf4j\xca+\xba\x98\xb1\xf7\xf5|<\xd2\xcc\x871\xb4\xb2\xea\xd7\xb1MS\x11=\x96\xe7\x97 k\x10|\xed\x0c\xe6\xe6\x06\xd5\xe1-\x97\xf0\x85\x97\xeb?C\xbc{\xdd\xf4\x9f+\xa5\xfe\x13\x9f\xf4\xb4\x96\x91x\"S\x80\xaed\x9a\xd1\x0d\x7f\xd0\xd3\x8c\x16\xfcA\xaf\x8d\x98?\xe8iF\x03\xfe\xa0\x97\x1dy!\x1a\xdf\x7f\xd0}\x94Q\xf1e%\xb4\xa7h}\xec@\x84\xa2\x83\x8a\x9aU\xab\x8f\xafO\xdd\xda\xda\xd6T\xa9\x94\xa5&*\x99\xfd\xac\x99B\xb9\xb0Q\xbcEm\xc5\x9bE\ne\xac\xd0\\\xc7]\xbc\xc9\xe3!\x96-\x9eU\xb9\xad\xce\x90\xcb\x19\xc2LG\xce`!z\xe9\x12o\x93\xc7.\xe6\xe5\x17;5N\x99\xa3\x00\x95\xe4\x99;\x87+\xd1\x14\xca\xe7*\xe5s\xd5\xd4\xe3\x8c\xdc\x91\xc7\x1d\x8f\xd2\xbc\xe7\xf3\x04`\x9d\xe3\x17\xc9|\x7f\xbaT\xba\x86f\x9b\xb3\xa6\xabd\n\x0f\xc1Y\x95eV\xccn\xdeL\x13*Q\n\xbf\x06/JoV\xef9 \xab\xaa\xd7K\x8a\xab\xb4\xb1\xc5\x0d\\\xa8\x15\xa6m\xcb\x9b\xd2\xc6\x16\x08z\xf9K\x14\xc7\xafH@\xa2-\xd2\xb6\xc2\xc2\xec\xa6\x94\xd3\x85\xe2}\xf8\x12\x81\x88;\xb2p\xac\xc7uB`\xdb\xa5\x02\xddr\x95\x03\x96K\x1eZ'\xf3\xb1o/\xa1\xec\xd4\xbc\"[\xa7\xd8\xa9t\xce\x1b\xba\xe3\xf6\xe4\xd3\xed\xab\x9e\x1a\xb1d\x99W\xf8t.\xffM\xde\xe41\xa3Bu\xb1\x83j\xf2TqF^\xb0\xc9s\x92\x94OXj\x08s\x85\x93-%I{\xcc\xf9\x03\x7f\xbb\x1b,4\x97f\x05\xff\xc6f\x0c\x18\x9f\x88~\x16{Q\xf1\x93\xff\x93\xbbB\xfd\xca\x8a)0\xc4K\x1b\xaf\x88\xa3\x80\xd0M\xb2\xd2U\xc9m\xf9dlzy\xc5|\x13\x9fDw\xc3F \x87\xeb\xa4\xd5:\xea\n\xba@=dU\xbf\xac\x12\x92\xb1\x9d]\xb5\x89\x89\xf5\x0c\xf5\xb5\x00\xb5 \xcb\x17\xf3_\xad\x12\x99\x95\xfeR\x9b-F\\\x9d\xdd\xa7\xcdB\xd3~\xa7\xca[\x93\x9a\xdf\xa8\xf7\x9f6\x8bC\x0b\xdc\xc2& \x8c\xe7\xe8\xae\xbei\xe9\xa1!,\xf0\xe5\xcf|L\xa3m|\x0d*\xb2\xc5\x8d\xc5\xe5*5:\xf1\x89+\xc5@M\x816\xcf\xa2\x82\x9e\x8b\xb4ez\x98&c\xc8u9g\xc4\xc5\xd1\x8f\xc7j\xba%\xaf\xa3\x85\xa5\xad2\x98\xc1bTi \xf3Q\xad\x16\xdc\xb9\xb0\xba\xb8XJ\xd1*3\xa4\x05\x9a\xd0\x8b\x9e\x1e/\xb1\xac\x90\x05\x96\xd0+\xcd\xac\xd0\x1b\xaarE\x169@\x01\x83\xb9\xe9JY\xa17T\xdb\xc7\x08\xaa\x91\x8c\xd8\xe3F>D%d\x13\x8a\"3\xa6\xb5\xfd\x06\xa6\xbaB\xde\xab[\x0d\xaf\x8c\x9fR\xa8\xc9\x17p\x856D \xce\xfe^]8\xe9R\x96mYy\xe6\xcf\xc9\xb2-\xad\xe1\x9b\xaaj\xf8F\xaa\x1a\xbe\xbe\xaa\x86\xefFU\xc3\xb7P\xd5\xf0\x8d{5|Y \xcf\x82K\x05m\xe8@\x04\xcb~\x16%~\x0d\\\xfb\xa7\xe4\xd8\xafi\x88\xe0\x10\xee\x9cq\xe6\x8c\x1bPC%\x02J\x0d\xc2\x8e\xb2`\x15\xc5aN4\x944\x1d\xc6\xa9GC\xb8t\xdf\x9aC\xdf\x0c\x90/\xb0p\xb2\x8e%_\xb0\xc38\x0d\x8e\xce3?)\xb4Q\x14\x19?\xb8I\xf6,J\xdeE\x89fFCQ\x04\xd8Y\xf8qAX\n\xfeL\x0dO\xb9\xf4\x0d\x96\xfd\x8c\xfd\x0c\x1dk\x95\xa0[\x06jSes\xcd@\x1f\xf3\x1e\xeb@\x97\x0c\xd4\x04V\x05\x164\xa1\x1aJ1\x9cb\xab\xb7\x15\xb5r\xc8\xe7yz\xa6\x19\xdcY\x14R\xd2\xe0\x1c\xec\xeb\xbccH\xb4\\\x95\x0cjpo7\x85>\x14\x88\xed\x08\\\xab\xbf\xc4\x14\xcf&\xd8\xe7 r8t\xa9\x9aw5\x9d<\x8f\xa3\xe4\xdd\x0f\x83>\xa6\"6:\xad\xa3\xb6\x86rT\xbc\xc8HB \xf6\x91j\x9er\xa3\xf9@\x92JC'xg\xe2)\x1a\xe6{\xce'BcX\xab\x9d\x16y\xba\xfe\xf1\xd8\xfd\xbd\x1b\xcd\x87\x1a\x0f\xa7\x9e\x94\xf7\xe3k\x97\xd0\xb4/\xd4g*\xa1>S \xf5\x99J\xa8\xcfTB}6,GS\xe6vc\x94\xa9\xe4\xeef:\x97\xf3\x05~\xed^sY\xb96@&\xecg\x1f_\xd8\xd7\x9b\xe9\xbe\x08\xfb\xe2\xfap\xc2\xbeP\xa4\xaa\xe1r\xcbT\x05)\x87\xc3@R\x0dc\xc9\xb4\x07\xe9r\x19\x13d1\xd5\xa0L\x82O\x93\xd79\x15\xf8\xf1\xb8T\x03o8\xf0#? Hl\x00.8\xf0\xd19 6\xba|\xfb\x0b\xa3\xe1.\x1b\xa0<\x08\xadU\x12\xabjq\x8cz\x8e\xed\x10s\xea\x1a\x81\xad2q/+P\x8b\xef^\xb0 \xf5\x8b[\xc6\xef\xce+P\x8b\xef\x9e\xb6\xdd\xce*\xc6J\xc3z`\xb8\xbd)w\x02\x15\x9f\xcf\xbc\x90d9 \xfcRW=\xe0\x1c!\xb98\xa4\x06;F0}n\x8bG\x08c\xcak\xf1\x0e\xa1R\x8dn\xe7;\x84\xd0*\xe0^\xf0\x8f\xf0\xe9\xd2\x95\x9c|\x89\xa0~\x1c\xa7g\xaf\xf3\x8b\xa7\xe5\x8b\x8d\x06\x83_\xb3y\x1b\x98-\xe49\xeb0\xff\xfa\x11\x13?\xd5\xe0O\x11\x9c\xb0\xbd\xf94y\x99\xa7\xcb\x9c\x14\x1a,\xf9\x15\x0e\xe1\x9d\xd7P\xea\xa8A\x7fB\xd0\xa6\xeeF\x0d\xfb\na1\xdd\xb7,\xa3\xb7\xb8\x1e#\xc6 %Q\x9ai\xb5@\xcf\xe0\x10\x1e3#_\x15\x02\xae\xd3\x8f\xbd\xa9\xe1\xb3<\x0d7\x81\x1e\xfc7\xee\x8f\x8c\xa9G\x9eEE9r\x1f\x8f\xe1\xc4iT\xd5\xd5\xf5\xee \x1c\xc2\xb6F\x9bc\x1c\xba{<\x86G\x9a\x97\xfe\xddQl9c\xf8n\x0c/4\xca\xab\xef\x9b\xbd<:/ \xeaI\x8b\x91\xfbX\xd3\xcc\xcf\xc8\x04\xd9\xcd\xda\x0f\x0c\xb6YKX\x0d\xfc\x0b\x03\xe6\xf8\xa6\x83\xfc\x91A\x06,w\x9d\x1a\xee\xbf\x19\x9c\x8d\xf2\xf5\x1f\x0c\xd4F\xf9\xfa\xbf\x18(\xc7G\x1d\xe4_\x19d\xe5\xd5\xc1\xb2,h_\xf9?\x9dW\x8e\xf4I^\xfe\xd9ma\xb3^\xfb\xb96\x17\xca\xfff\xaf\x98\x14\xc2\x84\xf2/!\xcf\xe9S\xe3\x86\xda\xa5\xf7\x19f\x8fe)d\xd1\xc4\xf9-\xec\x9b\xdc\x95\xd0\x9d~\xef\x19\xee+\x1e\x9a\x97{\xad\xec>,F\x87\x838\x9c{\xd3\xb9p\xe4\xe8\xe0R\xf43\xf1\x8c\xa1$\xb6\x16R\x10\x1e\x04\xb4\x7f't\xdfI\xd2\x84\x02\xd8\xe69\xb1\x12\xe6\x9b\xaa\xdb*\xe7c}2R\xf9\xf6\\\x06\xe2\xc0\x0dx\x047\xc0\x91\xe9x\xdbP\xea\xd5\x8e\xc2\x99F\x03\xfe\xefZ\x01\xaa\xd4\x80\xaa\xa6\xe0\x9fZ-\xb1\xc0[\x94ngp\xaa\xeea\x83S\xd5\xfa\x98\xb4}K4\xa7w\xab\x84\xd3Z\x0f\xd7\xf0\x9f\xd1\x1c\xf6\xb53\x84\xca!W=M\xffm\xa7x8\x1f:\xfdC0\xb0R\x8d\xab\xeb\xe2\xbf\x1f\xc3c\xba!\x1f\xb3-\xfe\xc7\x1f\xcc\xff\xe4\xf0\xf0\x10\x1e\xd7\xce(\xea\\\x13\x06?\xe8J\x15u\xeb \xd3\xd5S\x15z-\x03\x18\xbaU'\xee\xed\xe9TC\xe8d\x13\x10\xa7~\x18%\xcb\x89\x9fDk_c\x1f\x19\x8d\xe1H\x9bX\xc8`%\x91\xb5\x8d\xea\xcd\xd3$\xcd\xd7\xbe\"\x07\x10&x\xfa\xc5\xcf\x93(Y\xce\xe0qM\"Fc\xf8\xd5\"\xcf\xd1\xb0\xfe4\xd89}\xa9\xca\xab\xc6Bcf\x10M\x83\xff\xb01G\xfc\xaaX\xd4\xd1h\x0c?\xd1y\xfc \xc3=/\x91\xb6E6,\xc1\xf3N\xc24(v\x9f\xd1\x0f\x86YO\xa2$\x84u\x9a\x13\x08EF\x9f+^\xd8\xd6\x0c\x0c\x1f\xb91\xd0\xd5\xd8\xe6\xa99\xeb\xcceq\xeb\xa7\xa6\x18\xa4\xc23u\x1b\xff[\xd7\x86}\xb0\xac\xc5L\xc4\x91\xf6\x0bJ\x8b\xd6O\xda\xe8X\xf6\xb4\x91c\xa7yj\xa87\xd4\x0f\xbaa\xd7R\xc4\x0c~\xb3:\x85yA\x10;\xf1\xa3\xe2Ef\xf0X\x03\xc5+x\xff\x03\xdd%uj\xb8\xa6\xbaL\xeb\xaa\xdb\xd2\x95I\xeb]\x89\xab#\xb9\xcf\xe0\xb9\x86mi*\x12f\xf0R\x0d\xb9H\xa4Ev\xc4e\xcdP5\xb4d\xda\xecE-\x15\x996\x7fQ\xe6\x97\xab\xe7\xdc\xb1\x93q\xe1\x86nr\x17\xe4P\xb1\xe1*l|\xae\xc1\xc1\xbf\xeap\xd0z2\x98M\xfeX\x0d \x1cV5Ly\xda\x91\x1bgB\x03Q\x98\xe5H\xda~\xf5\xda\x16\x15b\x85;\x12\xda\x91\xe31T\x1f\xd1\xe9!\x96\x84\xbb\x83\x91\x90}l\x06s\xafh\xdd\xd1\xacs\xff\xe5\x0b\xafw\xd3\xf0>\x05\xf9\xd9\xcf#\x8a\xf0?3\xed;\xffH\xef\x89a\x18Mx6\x8ca_8Z,HPF[\">\x85\x9d\x11\xdf\xa9\x9e\xe2}3\xfe}\xf5\x15\xbc\xa4\xff\xbc\xc2\x7fLtq\xa7cV((T4Z\xd5\xd8\xff\xd2\x9eo\xec\xa33x\xf5aq\xdf\x96\x98\xf0H\x16\xa6!\x9b\xc1\x13\xc5\xcc\xd7S\x7f\x15S\xfc\xbcRu\xbc\xa4\x12\xf9\xbcL&\xcb<\xddd(ys\xfd\x95\x91\xb3{.\xdeW\xf5\xe8\x17+\xc9Y{Z\xd9\xce\xe20\x92|\xd9\xb5\xad\xec=3(\xacvJn\x9a\xaa\x1f\xb5(k9 \xf6C\xd3wz4\x86\xa7W\xb5\x97\x85 \x1aT\xc1dCw\xf3.\xcd)]'\xaaey\xa6\x19\xe0\xcf\xba\xd6*\xb5\xf1\x0c\x9e\xa9g\xbaJ\xea\xab\x89*\x11\xcc\x90(\xfb\xa0\x8d\xfd\xb0>\xb7[l\xc4Ul\x98\x86-N\x9b#\xd2\x1aK\xb9\xf5a\x06o\xcc@\xfc\x90\xda\x8a\x80\xbf\x97\xfc\xfe\x934w\x19C\xa59\xfc\xfb\x8c\xb4\x95\xce\xdf~\x1b\xa9A\xe4\x86\xad\x19\xbcV\xbf\x82\\\xac\x89\x9a\x10\xf4\xa0\xf8\xdet\xdc\xfe\x1f\x1d\x06\x93J\x17>\x83\xef\xad1\xce@2vq\x1bz\xb9\xc9\x89\xcce\xa8\xca|'w\x19j\x9c\x1c8)\xad\x87y\xb5\x99d\xcf\xf8\xa6\xec?\xaaQ\x85J\x8a\x0b\x8fY\xbc\xba>5\xcc6\xa1\xf3B\xfa\x12Z\xd4\x9e1\xa5\x17\xd2B\xee\x85\xb4\xa8\xbd\x90\xee5S\x19-4\xeeF_b\x8b\xfe\x03\xdd\x8d\xac\xfc~\x86\xc4\xfb\xe7\xf6\x0e-\xe9\x10\x87\x16\xe6\xa6\xd4\xb6\x13\xa9\xa1}K_\xaa\x0d\xd6\xd039\xa7\x14,\\\x9d\x91-5X\x80`QQ\x95=\xd5\xf0\x0d\x0b\x845\xb9\x9ed\x08\xa5s= Y\xd7V\xe9\xd9\xb1\xa9{+\xfe1\x0b\x17\x94-\x03\xcd\xa3e\x94\xf8\xf1\x0b\x9bW0\x12I8\xa2X\xbd\xb1\x84C\xc8\xcc\xb3z\x81K\xc4\xd5\x1d\xc1&\x8fJ\xadU{\xce\x12(Tu`\xab\xae|_j\x8d\xf9\xa7\x9d\xc4\x0b|:\x9f\x1b\x03\xbf\xcf\xe4/\xbe4\x04\x9a\xf3\x1a'?n\xd6\xd9\xeb\x14\x811;\xc4\x07\xb7.\xd7Z\x01\xd6O\xe8\xfc\x8d\x06b\x8d\x16\xb0\xae*(\x05\xd1\x08 \xa7\xba\x1e\n^P\xc5\xb9\xa9?{f\xaf\xa6\xd3\x05>v\x0c\xd0\x1a\xc3r\xcd\xe3\xc8\xe3\xc6ig\xc3\xab\x92\xfb\xba\xabcc\xafX\xd2\x83\xad\xa8\x99],\x8a\xedn\xe9\xdd\xd5\xc8\"{\xfen=\xab\x93\\D\x8a\x02\x04\xef\xc7 :Qg\xdc\xff\xea+\xb8\xf0\x82t\x93\x94\xae\xaeos\xbdY\xbc&\xb93\xd0d\xcc\x1a\x1e\xe3!N\xd4\x941\x94\x98\xef\x97JMT\"\x89r\xec[\xe1^\x982\x89 \x81\xae\x13\x06\x17\xae\xc2\x01\x05z\xacEu\xd7\xac\xb8\xd2V\xc8\xc9\xb4\x08{\x85B\x87!N\xa1\xbb\xcfL\"D\xb0\xb3\x08q=\x03\x19>i\xa6\xb2\x01\xc5\xa6?\xa32\xa3_\xc4\x04q\xed.&hK:\x9b\xb8\x8fK\x1d\x1b<\xb3\x8e\xf4\xdd\xf7c\x94P\xded\x19\xc9\x1f\xf9\x05\x91%W\xd9\x99P-\x86\x13\xaa\xfa\xbb\xe3\xcf\xa0\xc4\xf1g\xaa\xad\x10\x91S_\x94\x16\xff\xb1\xd4H\xcd\xc0\x95\x034\x11\x89Dc`\x14\xf5\xe9\xc6I\xac\xe2PR\x844\xc6\xa1D\x08\xa6\x8fC\xf1\x11F\x1b?\x82u\xf1\xed\x84\xf7\x82w\xecq\x9d\xc6\xc4\x18\xe1AO\xd8\xb2\x99G\xe4\xc3\x9f\x04y3'\x838\x0d\xe8<\x9d\x9e\xb6\x9d\x9d\xa5@\x83\xcd_\xdazUU\x02\x06\x9d\x02J$`\xd0\x98\xa2\xb2\x06\xdf\xca\x9ao\xfbO\xfbXy\x80J\xd8\x1b\x0d\x0e\xb2,\x0d\x91|\x84Wy\x04^7v\x99\x9e\xaa\xcd\x80\x078\xe4\xe5R\xfa\x87[D\xcf\x84\xfb\xb2\xd3-\xea\x96\xd0\x8f\xd8\xe9\";=\xa2\x8f\x7fz\xf8\x98\xc1\xa63J\xf5q\xb2\xad*\xca\xd7\xe6\xa6>\xe6$\xed\xd27b\xa5\xdb\xe1#\xaf\xd2\xb3\xee\xbe\xe6\x83M\x87j*\xa4\x0c\x9d,\x81\xcc\xfb\xf1\x95~\\Z\x9bS\xd7F\xb3\xb4i\x1d\xbb\xe2P^\xe3R\xfd\xc2\xf2\xa5*c\xbc\xaeC\xa2f*\xeb\x93\x1a\xacU\xe3T\x0d\x96[\xc0\xc8\xeb2\xaa\xcb~\xf6\x06\xe3<\x89H\x8cN\xe5\x1f\xb2\x114Q\xb3\xa2\xa1\xeafZECK\x8f$e~qL~\xc3\xec\xb7\xa6\xcc\xa0\xdbF\x8d\xa8f\x9d\x9f1\x1c(\x881=\xbb\xcb\x93}\x85\xb3!\xee\xe4\x93\xa9$ \xc8\xb0\xad\x12\xd5Q\x84\x0cUT\xa5\xdeT\xb8\x8a\x9e\xa3\xcb\xa9BAy\xfe\xb3\x1f\xcb\xf4<\x9d\x04\x96\xef\xdb\x05\x10\xdf\xcb\xcf\x04\xf6\x99\xebu&\xbcJ\xcf\x0c\xc7\xc2\xed\xe9\x9f\xe2X`\x03\xb59\x19(B\xc8\xcf\x04\xe2Q|\xe8?C\xa6\x14\x1eR\xa63\xfd\xf1\xb8\xfa\xe1\xa2\x92\x91+\x1a\x87\x9d\x14\xd6\x94\x88o]#1ap\x9d\xbd\x1a}&H\xdbG\xcc?Q\x02\x13\n\xf0\xe0\xee\xfe\x9f#g \n\x9f\x98\x949\x1a\xc3\xa6O\xca\x15\x82z\x1fp\x91\xe6\xe0\xd2\xaf\xd1 \xaf$p^Bn\x8c\x13\xceR\xff\x16\xa31N\xf4\xfe\xd7\x10\xc07P|\x0d\xc1\x8d\x1b#\x88O\x82\xb7\xcd7O\x02\xf5\xc1B\xb7v\xc4O\xb2\xbe\xb2\x00ei\xa3\xc2 \xf0\xe3\x98k\x0d\xc8\x18N\xe8\xbboE\x11\x87\x18O\xe1\xc8Cs\x85\x1fG\xff\xae\xa5\x07c\x19\x07zE\x1e\xa1\xe3\xed{?\xbfG\xadBz\x865y^\x936\xef\xab\xfa\x1a\xf3$\xaai\x00\xd7X\xe2\xbe\xa3\xdfc\x7f.\xa2\x98PN\x03S-\n\xef%\xaf|\x0b)Z\x0dY E\xac\xce\x9c\xc07\xacVa\n7 \x82o\x0f\x99;n\xc2\xe2\xbbqs\xf39}\xcc\xd6JV]u\xcc4\x19=E\x17\xdd}\x1fC[u\x95\xb5\xcf\x98\x9c\xbf\x8a\x96\xab\x98\xce9\xaf[I$\xc1P\x1d ]\xc6\xff\xf5\xbb\xf7&\x0b\xfd\x92\\\xaf\xfe}\x02e\xdfV\x1f\x90\xc1vV%h\xe87\x14\xa9\x88\x0f\x15\xc3\xb4:.,0\x86\xc4\xc4\xb9\"\x9f\xeaj!&A\x1a\xaa\xca2\x8eQ/v%\xed\x89\xa1Nx\xc5yY57q\xd5^\x1dt]\x9a\x14Z\xd5M\xe71\x07r\xcc\x96i'\xcb\xf5\xc9\x01YYN\xda\xb4\xe4\xc8\xd1\xf5\xfa\x97\x15!qU\x04KG\xd0\xd5_i\xcc\x19\x96=\x80uD\xbf\xa0\xae{\xfa\x9er\x00\xc6M\xd4W\xc3\x99Tpr\xa7\xd7\xe6N\"\x1e9\xcf\xd2\xbc,Z\xc7S\x9f\xbd\x85\x06\xe7\x99\x903\xf8>N\xe7\xee y+[\x83\xf2\"\xc3\x91ST\xa7\xfc@\xc4\x8ad\xdfL\x83\x92\x94\x93\xa2\xcc\x89\xbf\xeeH\xeb\x1d\xf6'ZT\xf5v\xf7\x0e\x0f\xe1,J\xc2\xf4\xccK\xfcm\xb4\xf4\xcb4\xf7\xd6\xc5\xb1\xbf%\xb4\x0f#\xddC7\xefsV$.\x88\x82k\xa3\x87\x1e\xff\xda\x9bW\xcf8\xc61\x0e\xfe\xcd\xabgn\xae\x91\xe9C\x9e\x0c\xa4\x8b\xa6\xbeL\xef\x1dyX/W\xb8\xb6\xc1!8I\x9aP|\x8e\xbcUN(G\x9c\xd2\xdf\x05)\xbf+\xcb<\x9aoJ\xe2V\x9b\xcfa\xb2N\xa3\x1cq\xcd\x00\xd13\xb3\xfb\x1ec$\x9cq\x15\xd3;\x1a\xd7\xdd\x9d\xa7\xe1\x05\xe5\xd9H\x12>ZEq\xe8F\xc8\xa6\x05t\xeb\xba=\xc0\x9c\xac\xd3-\xa9\x01\x1b\x93\x95\x93m\xfa\xae1Y\xa9\xea\xe8}/E\xc9\xeb L\xc9\x95\xbfR1+R\x89Y\xbeJ\xcc\xda\xa8\xc4\xacB%f\xc5\xfcAOb\nx\xca\xc7\xbe\x1cUKZYU\x12B\x98>+\xe0?\x81`\x95\x8f\xc1\x97\x0bV\xd1u\x14\xacr.Xml\x05\xabt\xa8`\x95{\"x\\\x84\xe1\xfc\xc2B\x04\xad\x84\x0e\xde\xd5\\T\x88\xac\xc3\x85\xbc\xa0\xf5QT\xa8\xba'\x02\x10M\x90\xd5k\xcc\xed\xe2-\xe5\x9f{\xad\xbcg]\x14\xf1T\x8f\x18\xfb\xf0\xfa\"#\xac\xd7V\xdd\xace#\xca~\xe4i\\|\x17\x04$+\x7f@\xf5\xaf\x89\x9f30})\xe6v2\xb0\x8f\x11\xba\xedY\xa5@\xf4\x11To\xa4\xdd \x8c\xceO\xa6\xac\x08\xbad\xea4EZ9\xd1\xd3\xe5\xb4d\xde{j\x00\xe1>\xbb\x91BH\xaa\x17\xbd\x1f3\xabs\xafp4\xdd\xad\x96\x82X!\x15\xc4|;A\xacX\xa5\x9b8\xacX\"ka\xc7\xb4/\x1a>M\xdd\xc0@\xe4NH\xff\xb6(\xbf\xcf\xde\xaab\xdb8x\xfdw\x1bN\x84\xd6q\xb0\xeaO9\x14n\xc6\x0e(\xbb\xd7\x86\x97\x07\xbc\xf1\x17\x15\x0f;-\xfa\xe5J4D\x7f\xb6\x9f2D\xe1\xcf\xd9\x1f}\xdch/\xffG\x92\x06\xf5$\xc1F^d\x1e\x19\xd5z\xe9)C\xd2\xc3\x03=yH,\xbdN65\xac!\xa5,\xf3\xd3\xb0\xcc\x13\x8bl\x841\xefm\xd2\xc6-5p\xc8\xdc\\\x06\xa6\x0d]U=\xd6G\xd5l\xf9\x11Zi\xed\x8e1\x89\xdf\xa34$#7\xd5x>\xac\xb1\x98\x8f\x13\xd4d\xd3T\xd1\xc6w\x9d8\xda\x12\xb1\x86\xa6\xca6~\x1d\xbbj\n\"\x91m\xf5\xaf\xbe\x92\xdd\x16Q\xa4\xb27f\xb5\x84\xf7\xb2\xf5D\xdd\xf8)\x1cB\xd1\xac\xf6\xc7\xa6rIJv\x82>b\xe7)\x95p\xc5\xb0\xe9\xacJ\xcd6\xe229\xee\x0c\xd1+T\x1b\xcc\x98\xd9\xe0J\x9a\xb3q\x01\x10\x971O\x16w\x05x\xd5\x88_n\xcf\xb5)q]\xec\xcfI]3\xc4\xe4\x08\xd5i\x0e8b\xa3\xcc\xad\xcb\xa6\xa5\xad\x16\xc3\x89\xab&(L\xb0\x97\\1\xa2\xe065\xc4\xa6\xde\x7f\xc5\x0c\xe6\x1a\xc0\xc6:\x89t\x17\xfc\xe5 \x8eQ\xbeJ#]\xc6\xabA\xc8Q\xe3b\x94\xe8\x92\"Df\xa5\x9a~E\xb5\xd5^\xea`i\xeb|\x94\x1a^\xae\x99y@\x93\x03\xaa\x93y@CP\x18\xf7\xd8a\x11\xcc\xbcd\x8fk\xd0\x1c'\x8a0}U\xfe\xa5\xe1\xdb\xd4B\xc9(\\k\x86b\x0e{o0=i\xbb\xe8\xa8\xc1\xf2\x1d\xba\xb4+\x8dS\xb8\xe1\x88K\xed\x8eS\xa1\xf0\x84\xde\xe39wU\xcd;\xf4 \xd7&\x03\xbc\xa2~\xd8\x04\xbb9\x8f\x1b@]j\xfe\xa1;\x18G\xc9;\xcd<=\xc3\xc7un\x07\xdd\x8c\xb5<\x9bR\xa5gS\xa9b\xa5\x81\xb3\xd3I\xdf\xc3\xa9T{8\x89\x0bYg\xa5\xa7\x93\xb8\xb0|\xc9\xc9\xd4\x00\x15\x027\x18F\xed\x0c\xcepx\x08)<\xac\xf1\xfc\x94'#A'_G\xce\xb8\x80\x99y\xb9\xd0\xad$\x08a\xc5P\x96\xb8\x8e:[\xb1\x1c':6\x15\xd0\x1d\xf8\xb1\xd0\xa6mQ\xafkh`\x91h#\x13\xa1\x8du\x1aZ\x8b\x90iH\x8cC\xaaO%M8/\x0c:I\x803\x07]u\xce\x8c\xa2\xc6\xe1\xa1.m30\xbe\xa4\xabK\x9aa\xd9\x0f\xa5\xaa\xc9\xdc\x15\x0e\xae\xe5\x87\xc0\xfeT\x85\xfeI\xad\x84U\x14\x85n\x15\x83\xde!\xa1K\x8d\xe7;$u\xe9'C\xeaGX\xd6\x99\x83\x98\x85\x98U\x8a\x1a\xb9'-\xfb\xcf\xaf\x85\xa4\x16\xa7\xea\xa0\xdf\x9b\xd6\x03\xf8\x1c2\xb9\x84*w\xacP\xe5\x8e\x15\xaa\xdc\xb1B\x95;V\xa8r\xc7\n\xa5\xe6\x8b\x98?\x91Z\x10\xdcP\xd8\n\xc2\xcaV\x80\xbf\xa6\xb7z\x05\xa4\x17R\x8b\x03\xaa\x07Te\xa5\xc3\x8fo\\X\xd9\x1a\x17\x88\xc4\xb6 C<\xb3hkjo);O)\x0e\x8d}\x914\xc1'+\xf2N%$n\x90\xba<2)\xb9\x12\xe6\xeb\xd3oF\xfd\ns%\x92\xd1m\xf9\x99\x8b*\xec\xe3\xd2/uJ\xeb\xbcO\xb2\xbbK/\xae\xf7h\xd82\n\xb4\x9a\x11\xc8\xcf\x9c\\\xd1Z\xef6\xfa{Q6\x84\xf4\xe8\xa5\xb8\xa4\xc3q\xfa\xac\x1d\xfd\x94\x02\xbf\xe1\n\xdd\x94\xaeF\xb3\xca\x08-Z\xe0RK\x1d*3\x9aP\xfeB\x0d\xc3\xac%\xe6\x02d\xccbb\xe1\x9a\x13\"\xa0Y\xaf\xb8B8\x9d\x12t\x8b\x10v\x9a\xdau\x0dk\xd0\xd4.\xab\xfeYhj/\xf8\x0cVx\xa4\x06\x9dW\xa0\xf6\xf6\xb1S8\x84\x95\x17%\x0b\x92c\xaeS\x8d\"\xe1\x0c\x0ea\xc9\xc5!5\xd4\x11\x1c\x82\xcf8u&\xe2h\x93\xfa\x9d\xd7\xd0\xe4\xdc_g\xb1>\x07\xe0q\x0d\xced%\x0d\xec#8\x84\xadU'\xdeqH\xe1P\xc5\xe5Q%\xfcw\x0c~\x9d\x86$>b\xbd\xd6\x81\xbf`\xe06%\x80^2\xd0*.\xd3TL\xe75\x83\xb7Tp?\x17\x9b\x16i\x97'\xa1Q\xf4\xc8\xbaPP\xf1\x05\xb8g\xee\xc8$/>\x15+\x84\xc5\xb2x\xc7\x9c1<\x7f;\xe6\x8a\xe7\xe7~6r\x7f\x7f\xdfe3\xba\xd7\xafp\x08O\xb9\xc4\x87\x88\xe9\xf4>\xa0\x16\xf1\xeaP?4M=ma\x98#\x94\xe0\x99W`m\xa0hq1r\xbb0T\xccf@KR\x1e\xe3M\xb6AF\xee\xaf\"\xec\xd70\x9b&A2J\x82x\x13\x92W\xc4\x0f_$\xf1E\x8b\xcb\xec^\xf4\xd0\xa3\xc7\xcd\xaf\xf0\x10\xcaJy\x95\xf0;\xa7U\x9fj\xc5V\xce\x9f\xb9\x8d\xcc\x89\xcd\x151\xf5]L\xfb[\xfaI\x85\xe6\x8d9T\xd1^\x9c\xba\xbe\xe8\x01k\xda\xf7V~Q\xad\x1d\x9d\xf2\x90g\xfb\xacnQ\xb9\x14\x07\x95T\x0b\xd2\x9b\xebd\x0c\xcfu\xf3(\x99C\xcdi\xc4\x80\x7f\xc9\xa3\x92hg\xfc\xbd\xde\xfcq\x8e\xbe\xcc\x94v\x9d[\x04\x8a\x89K\xb0\xc0\x94\x1d\xa2l/+&\xf5\xd7\xbf\xe6d\xe1\x08\x97.\xda\xae\x8a\xebQ\xe0;\xddu?Y8\xf05/a\xdcF\x0bTeo\x1a\x16\xff\xd6\xbc\x9a\xb1p\x0d3\xbe&\x16\xaey\xe5\xda\xb8\xb8\xe6\x95\xf2\x1893\xa4\xe0\xd0[{<5%V\xba\xa4YK\\\xc8t\xc9\xd9IqiMKw*\xcd]\xaeQ\xf2)\xe3\xfe\x9aW\xdb\xa4\xc2h\x9by\xf68[(\x8f\x19\x17\x97,v\xbc~V+-(J_\xd6^b\x1c\xeb\xf0q\n1A3\x06A\x05\xe4\x1b\x92\xa2\xf7\xf9\x18\xde\xed\x98\xdc`\x07M>8p\x03\xdc\x0ds#\xd7l,'\xf4K\x9f\xb9\x85+\x03\xff\xafN\xdd>D\xd7\x1f]\xa1\x9a\x7f\xb0n\x7f\xe7}-[\x8bn\xab\xa7\xa7z\x93\xa1\xaa\xf1\x17\xba\x86E\xd5\x1f_\x94)l\xd8&T\xa7\xc4\x18\xce\xcc\xbb\xcdj\xacL\x9dWQ\xf3\xe6\xd0\x1b6Y\xd3\xcet\x84@2\xf1Q\"\x11\xd6\xa8\x19\xcc5[o\xe84\xbe\xb60q\x1b8\x1e\xf5\x94\xb4\xec\xd7|-\x04#E9\x9b\xee-\xef\x1da\xc7(\x88\xc4\xd5\xc7\xe4\xb7^\xd2\xb9\xe6\xd51\xb1\xcb\xf4>\x8a\xf5\x1e\xc3\\\x9b\x83q\xed\xc7\xb5\x83\x81\xc3\x9d=\n\xd0E\xa1 \xe1\xa8^ar\xa43\x1a\x83\x03l\xe9\xbc\xda\x06Uq\x9b?i:\xf1\x9d\x16\xc5+K\x89u\x9a}MV\xfc\xa6Z^S{\xb1c\xa2\xd0\xd5^D>T\x88\x02L\xb5\xfd\"\x0fIN\xc2\x91\x9bhV\x94\x1fB3\xf8I\xb1p\xd5\xd4\x1di\xa6\xee\x91n\xea\xb8h;\x83#\xeb\x99\xd3\xf7e4\xae\x04\xfc+\xb5w\x0e0r\x1e\xc3C8\xf6\xcaT\xc6\x85v\xa2W\xba\x97\xe1\xc0}i\"T\xc8\xb5i\x14<\xf4JpP\x06 :B\xad\xfe\x11,\x17\x064\xa4p\xa4\xad\x87Yo\xdf\x9fR\xe0\xaa\x92j\x95{\x1f\xbc\x94\x05i\xa5\xb7 \xd5fCF \x85u\xe8\xf7\xf7]s\x89\xcc\x9a\xd7TL6T\xffm\x9b\xd0\xea\xbf\xf8\xcdke\x13Z)sG\xacTQ%+UT\xc9J\x15U\xb2RE\x95\xacTQ%+\xa5Mh%lB+\x8c\xc8\xbf-\xb5\x04\xb1g\xbd/W\xe6\xa0\xf6\xedP\xf4]\x91no\xf5\xf1\x0dE[[C\xd1\x97(\x94\x8e\xd1\xca\x14\x85\xa2\xb7\x88d~^\x90\x90oq\x85X\x85\x91\"\x1bt\xdd\x7f\xd9\x04\x1fd\xf2\x12!)\x9c\x1bSk3\x99\xff|\xa9\x16b)\x10S\x91@\x94\x14\xa5\x9f\x04$]\x00\x0b<4\xebC\x12\x1e,\xf9$\x8aQ=\xa52\x8f\x89+\xf1R\x16\xc6g\x91\xc3\xa0y\xe56\xe6\xb5\xe6\xd5] \xca\x0cobydn\xf3R\x9cD\xd5\xe31~\xca\x0f\xbf+^\x93\xf3\xd2\xd5L,\xd7\x1bZ\xf7\xbc\xd3\xe3\x92\xf2\x07\xac\xaa\xbbN\x03!C\xafO\x1b\xa4r\x95\xd9\x02PN\x90\xec\x15\xd7\xea\x88W\x07a\xec\x942@\xb9)\x95\xbd$b\x7f^\xa2\xabWc\xd5\xb4\xb4d\xd6\xc1g\x16YB\xad\xccu\xac^\xc9&\x97$T\x12\x17\xabR\xc2\xf9|5\x98_\x9b;Xz\x8d\x87\xf0\xfb{\xd0\xba\x0fo\x06d>-\xdav\xa3\xd6nT\xbf\x85\xf5A\x06X\xd5\xe8\xc1\\\xfb\xf2\xa1\xa6\x8b\x92\xcf\xc7~I\xb0\xbe\xe8\xebhMt\"\xf4\xba\x9a\x04\x8d4$\xc9\xf5\xd5\xbc(\xc5\xa7\xcb\x92\x8aL\x0d7\xffo\xc3\x87\xe9_\xad \xf6\x9b\x91W\x92\xa2t\x93\x11\x05\xf6O\x1c>#\x93\xc7Q\x91\xa5\x05f\xe6w\xde\xd2\xe3\xe3\xa6_\x96~\xb0\xa2\x07\xb5xI\x05.\xbe%4,\xa1\xdd\xb7\xa4\xe0\xbd~5\xb4G\xec[\xf4h\x82\xd7\xb9\x9f\x14\x0b\x92\xcb\xba\xd6|\xa3\xd75\xeb\xcfI\xdf\xd0(\x8f\xe9*8\xf4\x98u Jx\x9c\xb9\xe9$\xa4[\xf9\xa2\xca\xb1Q\x92\xf3\xf2\xe6\xaa\\\xc7\x16\xban\x0c\xce\xe9\x1e\xf0\xc2\xcaV%;(\xa5\xc9\x0ed\x17K\x80pa\x84\xed\xca?\xb2\xebT\x9f\x94`n\xf1\x8938\x84\x93\x0b\xca\xd0\x15\x9byQ\xe6n\xea\xc5~Q>MBr\xfeb\xe1:7\x9d\x11\xdc\x80\xe9h\x0c\xa7o\xbd_\xd3(q\x9d\x99n\x9b\x8a\x0b\xed\xfc*D\xd5l\x08=\x13\xd4\xc9\xfdpdZv\xe0K\x7f^\x99{\xc8y\x99\xfbA\xf9\x84\xe7oz\x92\xa7k\xde\x8fF7\x98W\xc4\xc8=2\x18\x84\xe8\x85!<\xb43\xcc\xeaG\xe7\xf3\xdc\xc0 i\x9fR\x1aTy]\xd6\x99+\xe8\xc7%\xb7yB\x8b\x17\xf9\x8b\x8c$\x1c3/eIq|\xa3\xc6\x16\xaa\xfa\xec\x06\x07\\\xd8\xa9\x06\x8a\xb88We3hw>\x863\xfd\xa4\x83q\xe2\x9bYf`\x11 #\xff\xb5\x9aM\x91\xcbc\x06g\x83\xc7\xa2|\x81\xb3\xdb\x14\xf1\x94\xe3`)u\xb8\xce\xa8\xfa2\xe7< $%\x96\xd6\x86\xf9\xa6\x84\x8bt\x93\xc3\xd7r/\xda\x99f\x96k\xda\xe7\x06'\x84\xa2\x81\xdbN~\xc8x\xd7\x9b\x14\xe8_7\xb3\xd8\x8f\x92\x9b\x8d\xd9\xff\xc8\x036\xf0k\xc2\x88\xa7\x181\xcc\xe0\xe6\xff\x8d\xd6\xfe\x92\xfc\xebf\x0b\x87\x12\x8f\xbb\xfd\x14\xaeSl\x97\x8e\xd6\xb0\xd1\xa4\xf9\x0e8\xa8Fv\xc0\xd1+\xdb\xd7K\xed!\x80\xf9\x9ed\x9a\xcb\xe6\xb5\xf6\xcf\x7f\x89\xc2r5\x03g\xba\xbf\xff\xff\x93c\" \xe5W7\x94\x073\x1d\xbb\xa8\xd0\xc8\xf0\xb9\xf37a\x94v\xe6\xce\xea\xb8P\x9f\x8d\xf4\x8bzC\x117G\xaa\x1d\xb1tA\xd1h\x1c\xd7O=\x9d\x11]\xado\x96\xacL\xb5\x89\xe8\xc48\xcc\x7f\x88n\x1f\x04O\x17P~\xfc\xbdQ\x9e\xcbtE\xe22o\x0d\xee\xe4\xf5-\xec\xc3C(lw\x80z\xf9\xad\xcd\x7f\x91:\x9c\xf1M\x92\x93 ]&\xd1\xbfIX\x99\x89p\x8e\xbf\x16\x81A\x94\x89\x10A\xee~\x81\xd4\xdd\xd3E\x8a~\xca\xd9/4\xa4\xf8\xd3M\xe4\x06K\x91@\x99\x8a)\xad\x8d\xf7Z\xb7\xa5\xe5\xa5q\xa4\xe1\xc5Vg,\xc0\xb0Tz\x9e*]\xab\xacm\x916UH\x98Yu'\xcb`\x95\xef\xd0}p\xf7\x8e\xc4\x88\xa7\xd7}\xd6\xbe\x9eY\x1c\x95\xeeM\xf7\x9b\x7f\xdd|x\xf2\x7f\xbf}{\xe3\xdb\xd1\xcd\xe5\xc8[DqIr\x0b\x0fK\xfe!\xc7\xa9\xb2\x0dEkY\"\xdc\x8e\xfa\xba\xdd\xdf\xc8\xb6\xbf7\xbf\xf9\xd7\xcd\x1b\xac\x9b\x9c\x11 \xda\x0f\xfb\xf6\x1f\xc6\xaf\xfe\xeb\xa6\xddw7\xb6\xdf\xb5\x9e@\xec\xc0\x9er\\\x80\xc8E0\xef\xf0^$~\xf8\xbdn\xd6\xf8!\xcf\x9d\xd9\xed\x850JuM|\xf0-Li\x13\x0d]Gm\xcb\x9b\xbe\x85\x87\xed?g\xf0\xbb\xe4\xdcg\xb1[\x82\x83\xed?G\xbd\xad'a\x89\xfb\xa01\x1c\xca\xf4\xa6\x01\x1c\xc2IGeSg\xb2\xa5\x7fu\xe2\xac\xe9x\x17c4\x07\xbb\x0b8\x042\x86\xd4]\xd8\xb8\x13\xf3uR)\xeau!]\xec\x14wK\xd6^\xe4\x96\x94uq\x1e\xc5i\x11%\xcb\xd7\xfe\xd2\x81\x19l\xf8\xdd\x17\x19I\xea\xbb>\xbf{L\xe2E\x1b\xdeyM\xe4\xb9\xbe\xe5\x01\x81\xed\xa3\xf7\xfdH\xe2\xba2\x86TeR\x8eLI\xeaX\xfdq\xa4\xe8\xbd\xe7\xad\x81R\x1e\xdf\xa7\x88\x15O&\xf2\x9e\xd2\xad\x95\xbb\xc9\x18b\x85\x92\x0fK\x89\xc3\x0d\x88\xfa\xef\xa3b\xb69\x83us7n\x8c\xa1\xd0\xd9Y(J\xa4'%L@\xe7\xbe\x1dVP\x07\nM\xa1|\xb8l\xb9\xf0\xef\x0c\xe7 ov\xbb\x1aV\x8f\x109\x1d\xac\x9c\x057 ds\x0f7 \xab~ET\xe8\xc4\x80\x05\xec\xcd\x18\xb0\xeb\xc6\xf0kh\xd0\xa6\x0eN\xb4\xc7\xc3\x81\x02o\x91\xe6G~\xb0\xb2\xdb\x1e\xd9 yK\xf7_\xf7\xe4\xa42jfw\xaa\xf0/\xed\xedu\xfc%F\\\xfb\xfb\xaf\xa6o\xe9%\x12\xb6\xde\xfc\xfb^\xdd\xc0\xdf!'\x19\xf1\xd1vB\x99\xbaoVe\x99\x15\xb3\x9b7\x97Q\xb9\xda\xcc\xbd ]\xdf\xfc5M\x8a`\x15G\xc9;\x92\x977[\xf0\xdf6\xbe\xd4\xfc\xe8\xa34\xbb\xc8\xa3\xe5\xaa\x047\x18\xc1\xc1\xfe\xf4\xf6\xe4`\x7fzg\x0c?\xa6 \x1cW\x1f\xf3\x9a\xef<\x8b\x02\x92\x14$\x84M\x12\x92\x1c\xca\x15\x81\xe7O_\x8b\xdbM\xd0\x9b\xd5od\x06X\xd4c3\xb3\x842\x7frw\xdeq\xe3\x08Ab\xaf\x12$\xc8\x08\xcaU\x9e\x9e\xa1\x9d\xe1\xf5EF\x8e\xf2<\xcd]\x87\x9cgL\xdd\xe6\x03\x7fI\x92\"y\x8a(]\x8e*^\xa3\x0fr\xd0\x05\x81\x1b]0\xe1\xa9@\xc4\xc1\xf4w(\xfb\x1f\xca\x19\xf7A\xa9~\xc3\xce\x98\x8fX\x16\xf4\xfe\xc4@S\x9d\x97Vg\xde!\xc5\x1b\xde\x97\xca\x1e\xb1O\xb1\xa9\xfd*z\xc7|\x8d\xa5\x00\xaa\x97\xd1\x0d\xe3[\x98~=\xa2''\x0b]qS\xb8q\x88F\xf8\x12\xbe\xfd\xf6\x10\xa6c:\xc4\xc3\xee\x18E\x8b\xf4P\xe2o\xb4\x1a\x1f\x86\xed5cxw:2\xe1\x82\xc2\xbb)w\xc9\xc8+\xd3g\xe9\x99\xa8D;\xac\x0f\x1f\xdd\x99\xed3,\xfe\xba\xa82\x1b\xd0_\xf7F\x7f\x8e\x82\xaf\xdb/\x05f\xd4\x05f\x84\x17\xfd\x80h8\x81\xe0\xb9\xaa\x8a\xf6\xa8\xe2\xa8\x8e\xceKM1\xef\xb4[\xb2;U\x97\xecN?\xbeZ\x88 t\x9d\xb1\x98-\x8b\xe6z\xddReh>t\xb7Jy\xa7\xd3Sr^\x92\xa4\xe8\x1d\xf6\xef\x99\xe7\xd4\x0c\x9c1\xf0\xa3)1\xd7\xda\x8e\xae\x1bB=e\x9ecG\xeb\xac\xbc0\x94\x89\xef\xc5\xd4\x8a*\xf1\x98S\xb5~'\x12\xfa\xc9\x88\xeb'\xafU\xc5x\xd5\xc8m\xf0\x10\xb1B\x85\x88Q\xc1\xbf(9\xea\x98\xf9S}\x02\xfb\xfc\x0b\x8f\xa3\x02)\x9d\x14\xa1\xf9\xb9\x8f4\x0f{\x8d\xda-\xf4\xf6\xbb\x0c\xaew\xf4\xa9-\xd4\xa7\xad\x9c\"\x0e\x9d\x96\xe9r\xa9\x11>B\xdesY\xfa\xe7\x9e\xeb\x86\xba\xbfQ\x92mJi#\xcc\x04\xee\x04+\x12\xbc\x9b\xa7\xe7\x12MY\xa3\x0b\xfd\x87\xf8\x1e\x1e!\xa8t\x90(tj^\xc9\xac\x9c\x8c\\Q\xc1\xda\xe3\x1f6\x1e\xb7\xa318\xc7$ \x01'\x95mL\xa7\xe7#\xf4Y\x95\xe8\xff\xa49\xa1\xe5&\x93Pj2Q\x94\x93T\xa4\x88\xbeu\xd0\xcb\x0b\xf0%\x17\xb4\xdc\xb0ag\xd4\xb0\xcd\x05-v\xe0.f\x82\xa1\xeeG_}\xd5\xfa[-F$&\x1bD\xc3\x02\x90TC\x18\xb9\x89'$\xc618\xcc9\x03\xad\xcb\x88\x13\xcc\xbaLD^\xc2\x84\xd5PB\x91\xbfOG\x9a\x96\x14\xebCK\\\xdbai\xb2\xad\x94\xc8y\xad\xc2W\x03\xa5\xd6\x9af\x1fS\x1aX\xc9\xb4\x9b\x1a\x94\x8a\xc4\xda\x05IxT6\xce\x15.\x04N\x1e\xe5\xe4\xdct\x0c\xfe\x186*S\x10\xe6\xf3\xe6\xd5*X\xcdA\x8b\x8c\x05\xc2\x00c\x9ci\xc6KX\xea\xf6\x13\x10u M\xd3\xc8\xca\xb5WHg\\\x18\xb5r\"\x19C\xae\x98\xdbF\xf4\"\x96\xf0`k!\x0e\xb3\xaf\xbe\x02\x07\xb5Y\xb8\xdf\xd2z\xa1t\xfa$\xc1\x9a\xe9\xa2\x96\x01\xcf\xc3\xa88>\xf3\x97K\x92\x1f\xa0N\xd6\x87\xaa\x8d\xf3I\x9d\xf9\xf6\x8f?\xd8]L\xcf\xcbi\x11\x8f\xed\xad\xefW w\xabT\x8aj\x88\xc67f\xd8\x0b\x9e=\xea\xab\xaf\xc0m\xf4A\xd1\x83\xddZ\xaa+`\xef \x07\xb0\x1e}tY8h\xb2Y\xcfI\xfe\x9a\xeb\xc7F\xae\xaf\x88\x93\xeb{q\xc90\xdd\x1d}\x9c|\xedU\x12\x86_\xa28~E\x02\x12m\x91;\x91\xd5\xdc\xb7\xce\xc5Ps\xea\x9fxw\x99R\x88G\x97\xda\x83Hd\xa2\x02 \x1b\xee\x84\x1cf*3\x9a\xcd\xeeJ\xab\xed\xe4F\xad|\xd4#q\xa8\x07,%\xf5h\xc4Q=\xd9\xac\x91w\xf5\x81\xe5b\x88:\xf7u\xad \x17\xcd\xc6{53lJoP\x18\x86\xd2\xd84\x1b\x8c\x03\xa1\xff\x9d\x893#'\xbfm\xa2\x9c\x84\x8cT\xe1\xae\xf2\xd9\x19L\xf72\xba\x89x\x8b(/J\xb7\xb3\x01\xb1\x90e\xc1?+jZ\xdam\xc7bTe\xd1\xee\xee\xb4\xfe\x86lo<\x99\x18\xf4\x01\xbc\x05\xec\xce+\xc3q\x9fX\xee\x8f|@V\x8e\xb4\x865\x98\xcb#.?sm\xaf\x9e\xd7 Z{\xfe\xa6%\xaa\x0b\x95\xb7\x1e#\xad\xe9M`Mo\xc2\xea\xb3\xe6\n\x0f\x85\x91\xde`\x95\x07cj\x11\xafX\xa5gGB\xdde(\xef\xc0\xa0\x1f\xa5\xebu\x9a\xd8\xbcs\x81^\xd9\xce\x8fE\x9a\xb0\xcc\xe7O\xd2|m*)\x9b\xbb\xcc\x98\xfc=\x0b\xaaQ\xc2\x9e\n\xc7\n\xc6n\xa8\x01\xcf\xe0\xb0\xc9\xa2\x9c\x9a\x0b\x98\xceM\xf6\xac\xb6\xc1\xc9`\x15Y$Zk6\xd4\xf6#\x83\x95)\xa8\xec3\x85W\x15S\x10\xd8\xea\x06\x06\xbbP\xd0\xf4\x8f\xa2\x9fh\xa4\xf3\xc1{\xf4\x135\xcd$E\xd9\xc8\\hot\x92\x91I\xbbwk\xf3\x93\xa1\xf4X\xc3\xc2\xa3\xc9\x05\x04\x83\x8b\xb65\x8dL\x81\x12R\x97\xe1\xe4\x88\xe1\xafm\x0d\x8ds\x06nSC\xe3\xb8\xb13\xb8\"\xddT&\xa4 \xde\x94!MEC\n-\x93\x12P\x89^\xfd\x81\xef\xea]\xb9H\xf3\xb5\xaf\xed\xe5\x0b8\x04\xf4\x81^!7Rv\x18\x11\xed\x86x \x87\xf0\x82\xbdP\x1a\x10\xf45%\x00\xb47\x8f\xfd\xd2wL5\xf8\x9eS\xe8'\x15t\x94\xd4\xa1\xe5\xea\x97\x9e\xd6\xc3\xae\x19\x0e5\xf8\xaf\xa2\xf3(\x0cD%Y\x17T\x16\xc0\x81t\xab\xc95\xaf\x9f\xe0\x10\xde\xc1Cx\xd7\xe5\xa1\x1cM$\xe7+8\xc4\xc0GW\xd4\xa2\xe8\x12\xf0\x91[Vy{\x95_y\x0c\x87\xb0n~e\xe0\xfb\xcf,\x12Y\xbd\xb1\x80\xf9\xcd\x02\xe6 \x1c\xc2\xdeT\xab)h0z\xcc\xe9\xfeY\x8dOl=:\xec\xe03:v\xda\xc1gM\xbew\x8c\xfd\xe1\xb7\x84(\x87\x86\xe37\xf5\xf7\x04h\xe3koh\x9bo\xea\xf0e\xda\x03\xec\xf5~\x1b\x8e\xf5\xed\xb7\xfa[U\x1b\xe3f\xccB\xd9\x15G\xb1\x02FWL\xd6z\xa4\xe8\xf3\xf6\xb3\xdc\xfbH\x17&\xa8\xb0\x99\xd9\xba$4\xdf\x8c\x12\xa7\xe5\xde }\xe9\ns\xf8\x0fq&\xba\nC\xffSx\xd82#\xd2\x06\xa1\xa2\x070\xeb=T\xf6\xa6=\xb9\xf8au\xc6\x00VF]\xddC\xabT\x0dA\x1ac\xbe\x10\xdaS\xf5\xd9\xa7\xea\xaf\xf3?\xff\xef\xefN\xc3\x8f\xee*f\xb39Y\x9a:\xe9cx9\x86_Q\x0fu\xe2\xc0\x0d\xf8\x15n\x80\xf3\xd6\x19\xc3w\x18\xc2\xb7\xf3\xac\xb5z\x92\xa7\xd9\x84\x9fg\xca)p\xffJ\x1b\x1d\x833\xd2o\xb5\x1d\xa7 $YN\x02\xbfT\xad\xcf\xfbq}\x96\xd6\xdb\xbf\xf1\x16\xc6\x846\xfe\xfep\xab\x15i\x9c\xe4\\g\xdcb\xdbq\xba\xc6\xb0\xa4}~%\x94\xe3\xaf\xae4G\xfa\xb1\x89\x9dgnW\x14o&\x14\x83\x0c\xeeR\xe7\xff\xb0H\xa9~\xfe\xb3\x1f\xeb\xcb\xb0\xc8g\xa8N\xa0\xbf\xa63\xf2X\xcc\xc8\xe3\xff\xf8\x19\xb9\xc2\x1a+;8wV\xdb\xa9\xe1\xe2\xa9!\xca\xe7Zz\xcc\xeb\x9f\xc8\xbei\xc2\x8a\xbd3\xd4\x0b\xc3\x1f\x7f\xc0\xde\x13\xb3$\xab\xed\x87\xca\xf9\x85\xb2+\xea\xb5\x14\xbdw\xbe\x89\xbe\xfdn\xebG1\xa6\xe2@V\xb4\xf8\xe6f\xf4-=\xe6\xe0\x06\xbc\xb1\x88\x8eo^\xc2|\xaa\xc1\x8f\xda7\x8f\x07\xf5\x8eU\xc9\xcd\xde\x8fZ3\xd5\xe0\x94~\xfb0s&\xd82\xbbi\xe3*A6i\x8d9\xfbM9\x98\xd7t,{\xcf\xb5'Z+\xcb\x13\xc6\xdc\xce\x0cY\xed*)\x07\xcb\xebP\x94\x8a\xcc\xd3\xa3\xad$o\xd0uX\xebM\xb8N\xf3'5\x84`\xabf\xf0T\x0d\xd4\xd8Z\xf2\xedVK\x9d\x8c\xd5\xa2\x14\x0f&\xd0p\xb9m\x83\xcfXx\xbd%\xef\xbb\xabV\x84\xd0\xc5+fB\xccc\x7f\xea\x1a\x12\xf5\\^(\x11\x087\xc3\x0b\x0d\xc5:\xd2-\xab\xf5\xba\xd5\x0e\x96\xdd\xba\x88\x06\xa4\xe0\x0e\xd9\x9a\xacVvZ\x1f{\x8d\x8f\x98\xb3\x8e\xd6A\xb3*\xa2\xf6\x8d<\x89\xa5\x84H\xefX\x01G\x816M\x1d\x8en\x9a\x84K\xda\xac\xa9\xc9\xa9\xec\xe0\xc7\xa4,\xa3d\xf9$\xcd\xdd\xa0'g4\x183\xcdD\xd4>k3\xf8\x89\xb96PY\xf5'\xe4U\xd4\xaf %\xa7~\xf6\xae\xca\x89\xf9\xfa\x97R T\xaeT\x81\xca\x95*P\xb9R\x05*W\xaa`\x98+U\xe0\x16\x8d\x8e\x06jO\xe2\xe0\xe3\xfb?-l\xfd\x9f\xbe\x04\x98\x0b@\xfb\x00\xf38\n\xde}j\x87\x17k?$R[?4goevS\xc30\xcb\xe0\x1aU\xferma\xe2m\xfd8\xe2\x85\x1e\xfcu\xe1\x9e\xa4c\xf0\x91\x02UO\xbe'\x8b4'\xfcp\x12\x00\xa8\xb7\xe3\xb3\xe4\xa5 \x7f\xca|::7\xdd\xd1\x18\x12\x8f\xf0?4\xc7\x82\x18\xb4\xf6\x04\xce\xf0\xf4\xd5\x9c\xa3kn\xe1\xe8\xfb\xec\x02\x12*\x837\xda\xcb<\x0d7\xc1\xb0\xb8\xfe\xca\xdb\x8f\x8d\\\x92r\x80\x7f\x94\x19\xc9O\x04 \xae^\xf5\x1a\xeb\xf8\xdb?i,\xbf)\xf6y\xce\xa2\xabme\x93y\x99\x00G)\x10\xe1G\xfc\xd8f\xa9\xa6\xae\xdb\xb1\x8d\x19X\xee\xab\xb2\xc6H+\xa0I\xd3\xc9\xf8\xaat2\x1bU:\x99B\x95N&\xe6\x0f\xe4\x15\xd0Z\xb9c\xaeY\xc6\x98\xfeG\x84\x1e\xfa/\x0f\x1e<\x90 \xe9\"M\xcac\xa6\xcfv\xa2\xd2\x8f\xa3\xa0\x1b\xa2\xd3\xfa34\xd2'\x03\xe3\x00m\x1a!)\x83\xd6\xab\xbb\xa4\xf6\x93\xee\x94\x1fc\xc72\x03\xaf\x18\x02#\xff\xdb\xe9\xd1\x8e\xa5\x9b\xc0L\xb9`\x00\xf5\x82\x81\xfeEP\xb1\x08\xc62@\xc0\x19\x04:\xac\xb6\x17\xd1\xc8u\xc4\xd6V\xf9\x05C#\x94\x06\x9ae\xe1wVyC\x87\xd0\xf2\xfe\xeb\xe39\x01\xf46&C>\x06\x90\xb7yz\xaaI\xca\x00\x9c>\xff\xc0\xcb\xa9\xea\xe3\xe4\x8dI\x06@\xde\x85\xdd\x86;$\xd3\xc0\xd0.M\xf2\xf4l\xd7^\xed\xd2\\\x90\xc6\xfa\x05\xb8l\x92\x02\xd8\xb1\xddV6\x82\x8f\xdf<\xf3\x1a\x1a\x90\x05\xa1\xf4HR\xe6\x17\xb2\x12\xb9&\xdd\xb1\xf0\x01\xee\xc8?d\x0c\x07\x06\xbf%\x10\xee\xbb'\xfb\x9ax\x10q\xa1\x0b\xef\xc9\xd4\xa2\xda\xcf\x9e$\x1f\x83\x1b\x8d\xaa<\x81\xeaL\xd5\xe2\x12N\xbc\x91\xd7\xf1\x19\x7f;\x12N\xb4\x1dOr\xee=\x02\xb3\xc6S\xa3G\x89\xb86\xb2\xa6Z\x0e\xec\xfa\xee\x9a\xd8W\x8b\xbd\x0c\xe2HJ\xb5`\x97\xf0\x0f\x10\xd7P|\x06\xd6lz \x13\x94\xb8vl:\x92(\xa3?]o|^Fb\xa39H\x13\x9b\xf6)\x97\x80\xb6CGx\xcb\x991\x95\xbe\x83\xa6D\x83\x97\xa0\x80\xe5\xdcb\xa6\x1f\x94F\xfdX\xc3t\x93CHS\xbd\x83\x94c\xeb\x88?x\xcbP\x82\xba)\n\x85x\xf7\xba\x89B\x9fT\x83\x19\xc8\x04\x1e* \xb9\x81\x10xP\xdc\xf93\xa8/\x1b\xfc\xbeDK\xd9g\xf9m#5m$\x90k\xaa/\x19\"m0I\x83\x84Q\x99\xe6F\x0d#SF\x92<\xb7P\\2md\xec_\xa4\x9b\xd2\x02\xbf\xb3p\xb9#\xcc \x884\xdcH\x18\xe55\xf8\xf3\xd5\x07\x84\xcaL\x04\x82gv\x8a\x8c\x04\xe6\xe1\x84W9\x9c+\xeb<\xf3\x0b\x93#\xc8h\xa7tj\xb6\xfc\xfc\xa2\xcdL\xeb\x93\xa7C+\xcc\x19gA>\x05\x0c?u\xc7;\x9e\x95\xa5\xe1h\x14\xec}\xd9<\xa2\x94V\xea\x9d\xf6jo\x9f\xaa\x8f\x9f\xf7c,Mgh\x86\xe9\x90\xf4\xa7\x87\xd031\x7f\x1fVg\xaf\xe9+\xcd\x99\x0fx\x08+\xb7\x03\xc5\x1c\xc3\x1a\xae_\x02\x16Co\xc4\xcd\xcc/W\xf8\xbe\xb2\x1f\xc5\xda\x8f\xe3F-F\xbf\x84\xee\xeb\x0d\x7fW\xf5gt\xce\xebFw\xff\xb3UT\x92\xe3\xcc\x0f\x98k;\x99\xe0\n\xabw\x95U\x15Gi\xaa\x01>\xb05)\n\x7fI\xb4\x07\x8b\x16]\x8cC\xc2\x8a\xa0\x93\x90\x04)3\x91;3p\xb0\x12\x8aah\xc1&/\xd0\xdc\x94\xa5QR*\xb9\x1f\xd9\xd8\xb0\xb6\xb5\x8e\xe6i\xaa(W\x07\x7f\xe2\xcd\xa3$t\x19:\xe4R\xbb\xb6\xf3\xe3f\x9dA\x99\x02\x1d\n\xc5\x96\xbc\xd6U\x88\x1fm\xb24\xd4\x04\xb6\x13m\x91C\xe5\xbc\x8c\x8f\x92ZtwJ\x8e%h\x9fEE\xe9E\x05\xfd\x8f\xdb\xd9\x0c\xf6\x9bI\xb2\x97\xb8\x9f\xb0\xc7v\xd5%>\xc4\xd2\x804\xc8!\xfa\xe3&\xe8\xe5\x91c\xcc\xa4\xdd\xa7\xd3\xa4Z\xc6\xd6\xe7v\xde\x19\x9f\x90\x90Z\x13I\x0c\x0fB\xc4\xfd\xc8$\xcd~3\xff\x99 \xd5\x95\xd2\xa86\xd6Z\xd1\xab\xf6+\x06\xda%\xd3\xd6\xad\x94\xda:\x17\xd3k9\xce\x88W\xa4t\xc0\xb1\xb1\x1d \x11\xfcd\xff\xadW\xa6o\xe8va\xf5\x8a\xe0\x06\x10\xaf\x88\xa3\x80\xb8\xd3N\xc7\x04-\x81^\x1d10\xa7\xccm\xf2\xa4-\xa51\xfb\xc2\x17\xbd.\xbf,\xf5\xbaA\x95\xbb\xefO\xa3\xe1\xfd\xe2\xa0jQ\x01\xe9\x12>\x87\xe2\x13u\x12O\xdc\n\xd7\xd0\x93\xb0\xca\x92\xf58\n\x9f\xa7\x9bD\x16Td\xab$\xaf\x95\xe3\xcdl\x1fE\x95\xce\xa837\n\xf0*?R\x7f\xb2\xda\xf3!;J>`\xea/\xd2\x1bT\xfbN\x9d\xe6\xa9s\xbf*\x9d\xcf+)0\x9dH\x13G\xa4\xc3\xbf\xc4\xf8?\x81\xb9\xa39\x04\x93\xb5\xa3\xe2\"M\xa6\x0e\xec\xaeV%\xddv\xb3\xda\x89\x89\x82^\xc8&\x8edR^dD\xb0\xb7\xc8f\xba ?\xfe\xa5\x9f\xd1\xe9\x11\x0b4\xd6\xec\xd4\x03s\xcd\xf4\x9c\xf5J\xab\xf7\xd5\xc4\x85\xa9\x06SZp6\xe22\xe9fR\xe6C`\xa5\x953\xe8\xdb\xf8\xa05\x81\x9bR\x8fm\x80\xaeE}\xc7\xda\xe9z\xa5\xdbB\xcf\x98I\x12@\x8fzU\xa9\xf9\x08\x93^~\x93\xe6\x16cI\xb5co\x91\xa7\xeb\x1f\x8fG\xee\x89C\x0f\xb5(@.\xff\xe6\xafE\x9a8o\x1b\x9c\xe3\xf8\xday:\xd3\x1e\xbd\x10!\x06\xcf\xa2\xe4\x9d&5\xfcug\x10\x13\xf7\xb6* \xfdg\xc9\x18^\x05?\x98H\xf9\xc1\xa8\xe2\x07\x93\x11\xe3|\xf6\xbf\x86\x0d|\x03\xc9\xd7\xb0\xa1\xfc`t\xb2i\xf3\x83\x1b ?(\xf8\xcd\x0f\xc5\x08F#M\x12i\xcc\xb2\xf8\xda_\xa2\x05\x17u1\xa7\x8d\x1bLx\xa5\xccn\xa1X,\xb8B\xe6\xad\xd9\xb2\xc5i\xaf3:5\x98\xb1\x96\xc7\x003\xfd)\xf2F\xb7\x87\xa8\xe6G\xe87^d\xd7\xb9\x87\x9f\x80c\x1a\x14\xadf\xed\xf4\x91\x0fq\xfaH\x07\xa4\xcad eK\x7f\xb9$aE\xb8\x0b]\xc6G\xcc\\lv 11\x0f\xf6\x8aB;\xee*\xdd\x92|\x1b\x913S\x8d\xc1\x17\x1c\xceA\xa1p\xb0\xf56\xad\xad\xb7U(\x9d6\xaa\x1e\xf8$\x9f4z\xe8/\x0bg\x0c\xa5\xc1Y\x98y\xcf\x08\xa7\x92\x08\x1dI\x8c\xb6\xe2\x9dye\xa86M\xd5OT\xc2*_\xb8\x84\x9f\x05\xec\xe4\xb6\x00\xf5(sF\x1d\xe8\x9cl\xd4\xee\n\x00=;F\xf7jbPL\xd9\x95\xe6\"\xe9}\xd3\x85\xef\xaa3A\xa7\x87\x1b\x0e\xf3\xa2S\xcd\x89o\x9a\x90\xda\xef\xc1\xe0\x93j\xf4}\x00\xd6\xc3t\x00\xab\x0f-\x0bN\x992\x86PG\x06\xc4U\xa7\xeb7\xc32b\xb36d\xb0\x15\x17\xf33\x8b, \xe9N1$G\x05\xce\xde%\x0d/\xad\xc6\x06\x1e\xc3\xc6\xd29}g_\x0b\x10\x1b\xcc\xa2\xa7\xc6\xf8[q\x898\\C\nSzE\xe1\x0c\xd2*\x19\x93\xc5\x0bt\x8b%Z/\x9c&\xe4\x8b\xec\xa9\x19u\x9b\xc0/s\xb2\x88\xce\xb1\xb0]\xbd\x0c\xc6\xb7W9Y\xcc\xc0\xf9K\xf5\x12\x8e\xc6\xa2\xd9\x8a\xde0\xda\xa1'\x1a\xb6\xfe\xdbR\xb0&\x08&\xca\x8f\xfeM\xe0\x1bVUDM1o5\x0c\xfa?\xa5u\x9cv\x01L*\x0b!J01\xc9\x1eHm&\xad;\x03\xe5[\x83SI_\xa4\xb3\x12D\xa4\x04\xc7Z\xe4\x10\xd2\xc6\xae^\xc9\xcd\xfa1\x1a\xbe?i$.H\xbcS\xfe\x077VQ!\xb0=\xaf\xff%\xf9\xc4\xe5\xf9}\xde\xea\xc7\xe5S\xf964\xb1\xa8\xed\xed*'\x91\xcc\xc3\x98\x8fb\xe4\x9e$\xc8\xdc\xc0\x1e{[V\xe4\xbf=\xab\xd7\x8a\x81\xd7\x1d8I#\xd7\x83\x89Y\xc7\xa1\x9b\x98tJ\xcev\xe2\x9fc\x8fnE\xdd\x99\xc3(\xa5\xe6\x0c1\x9a\x99\x81\x87J\xffB\xa2\xe5\xaa\x9cAN\xb9\x9dy\x1a\xb3,\xa4I\x9a\xaf}m\xfc\x9ez\xec\xb2\xe4\x00j\xf0\x96wl\x9c\x06\xef\xaad\x04\x94e\x1b\xee\x05l%z\x08\x9f\x0b;\xe9\x83\xce\xca$\xf6\xe7$\xc6\xf3HQ#|\x0cI\xdbT\xbc\xb3/\x03(\xdbW'\x1f\xb4\xb0=\xd8\x1c\x1b\xff\x05\xd7B\xcb\xf84Y\xa4o\xf2\x18\x8f'\xfa\xfb{\xbf /\xfdr\xa5Q8JS+\xa4\xaa\xd4\n\x91*\xb5\x82\xafJ\xad\xb0Q\xa5V(T\xa9\x15\xe2Vj\x05\xb4C\xb7\x01\xea\xdc\x0b\xdcR=\xdd\xbf\x16\xa9\x17zsn\xc5\x11h\xdc(\xbeD%5\xe1\x86\x9eY\xab\xb4\xd0\xe8x\xd8\xa95\xe7\x8b\xb5\xd3q3(\x16\x84\xb64\xd9\xe4jR\xe4\x9c\x00E\x1dx\xf3\xea\x19\x96\xc1-\xd1g\xc1\x81\xb7\xbb$\x80\xd11\xb6vn\xd1\x06\x0c\x85O\x8c\xa5\xd0\x9b\x05\xb8\x12l\x053\xc6\xc2\x00\xac\x85\x81\x98\x0b\x15\xf6\x86~i\x90\x89\x93\x01\x1aM\x00h:\x9e\xf3\x94\x9c\x7f\xfc\x01N\xb9\"\x10\x92-\x89\xe9\xc9c\x905\xd3\xfa\x0b\x14\x93-\x14|\x1c\x9a\xac\xfd\xc8\x08\xefc\xf2<\x87\xb2p\x16\xf1\x1fV\x8cL\xaa\x15/mX\x1e\xa3\x86\x8aq\x94.\x96\xf5*\xfc$*\xa3\x7f\x937y\x99%r\x90\xfb\xbb\x9d8\xc5\x14\x9e\x945\xd4\xb1\xf3L\xb5\xb9\xc9c\x1d\x10\xb3\xd3\x08\xee\xc4\xe4\xe5^\xa2\x0c\xa9\x83bR[S\xca\xd3A\xc7\xcc\xea\x83L\xee\x15x\xcdc\xee\x98\xbc\xcaV\xa8\xa6\xe1\xb1\x8e\x86\xd3\xdeh\xf99\xe4\x984\x829c\x085\x06\xbc\x9a\x19\xd4\x9cZ\xcd9\xd4\xba\x91\xb6\xcfA\x85\xa3\x8d\xfa\xa4\xb8\x949\xb9y8\xb0\xda\xfe\xd7\xedp(T\x87C\xa1:\x1c\n\xd5\xe1P\xa8\x0e\x87\x82\x1d\x0e2\x92_||\x92\xaf\xd7\xa0\x7f!\xf9\xe2\xb2%\xf9\xc2/v\x97 Z\xc6\x1cXo\xa1\xf8Zn\xa1\xeb\xc1_\xf5\xf7\xd6\x17v\xea\xcf\xb2\xb7v\xd6/4u\x0b\x8b4Ugp\xfa\x8f;\xf7\xae\xc7\xa6\x157\xffDB\xd1\x97\x94B\xda\x94BO0\x9f9K\xff`4\xe5\x03\x9fO\x1ed\xd7\xc8 $\x17\x06\"i\\\xf4&\x0b\xfd\x92\xb0\x86e\xc6\xdbO\x9e{\xe8\xd2d\xf2\x03K\x9d\x83\x82\xae\xa5\x96\xfdG\xa9\xd6\x90B\xe9\x8e\x13\xa7~\x18%K\x96\xd5\xb8\xf4\xf8\x9f\xc7\xa5_n\xb4B\"\xc5[g\xe1G1 \x07\xbf\x8bn\x85^\xb0\xc9s\x92\x94\x1cC\x0c\xd2\xeb\xef\xef\xb5\x82(\xba\xde\xb9\x1b\x0f\x0b\xea\xd1\x9e\xe5$tF\xdc\xdb\xb0y\xff/\xbe\xefk\xb3\xa07%W\xfa/\x8e\x0dmw{S\xfe\xbb\xaa\x1a\x7f5\x07$\x8e\x1f\xebU\xfaQ\xb2CN\xfa|XK rf\xaa'|\x9d\xce\xa3\x98\xcc`z0\xb4/N\x94d\x1b\xfbTCut$\x9f\x05\xfe\xba\xf2\xe5,\xf6\x03\xb2J\xe3\x90\xe43p\x18\xea\xc0\xfc\x02J\x7f\xa9y\xab\xbc\xc8\xd0\xbeE\xceu\xdf\xee%*j\x12M\xf5k\xd5\xc1_c\x8aS\xe6\x1b\xe2T\xd8\xe28\xa0U<\x84U\x81qs\x14\x94\xdcn\xf6\x81\x13x_O^*S\xf1R\x99\x8a\x97\xcaT\xbcT\xa6\xe2\xa5\xb2a%\xc53\xca\x15\xb4\xeeb`L\xa6\x89\x9cY\xe0\xc7\xa6\xfbR.,\xfb\xf8\\X\x08\x87\xf0\x84\xb7\xef!\xebAwO\xbb\xcf\xfa@\x1a\xe8\x84\xd7v\xf0\xa4yYse\xc0{\xa7\xe6\x96\xec8%\x11iK\xfb\xa4Wmn\x19|\xc4B\xa3K\xbf$\xd2\n\xae\xe2\x8a\x8a\xa30*\xbfO\xcfg\xb075\x12\x0bGI\xe4#\xc3.\x86+a\x80`P\x02F\x18\xc0\x13\x81H\x95\xc3\xd8?\xacq]4\xa7\xbef\x96\xac\xcdc\xaa\xd3dx\xb6E\x90\x8cD\x9boB;\x14U\xa2\xb7\xa1#\xf8d\xfel\x8c\xcf\x14\xe7\xde\xa34)6k]\xfeD\xa8\x9c\xd62?\xf7\xd7z@\xe6\xb5\x16\x15\xbcf\xb6\x1e8\x1a\xc2\x1eC\xe5\xb7\x96\xf9\xe5\xea\xb9E\x9a\x8e\xcd\x003\x0ep\n\xbfq\x9d\xefYE\x1c\x0dk\n\x9c\x82o\\\xe759/\xbf\xcb\x89o\x02\xcf\x18\xf8*Z\xae\xe2h\xb9*\x1f\xa5\xa1\xd1\x81,d\xef4R\xf0\x99\xde@\xef\xed\x08\x8bg\xe2Z\x91\x92\xe4\xbfD8[\xfe\xf7\x17OC\x92\x94Qy\xe1\xfa\xdc\xe7<\x1fyu\xd9\x94\xc2\x19s\xd3\xf7\xb3\xa8(Gn\xf7\xc8\xea^[,\xa7\xd9\xe8\x1c\xdb*\xae\xcf?\x9a\x93\xdf6\xa4(\x1f\xd9\xf7~\xddBb\xfai\xc4\xccN*Wq[\xf8,\xc8\xde\x98\xd5\x8c\x0c%\n\xd5\x03}\xfbK\xd1>\x12~=\xec\x05\x1c\xc2\x92\x89\xc7z\xc09\x02V\x07\x85\xd1[\xed\xca\xaa6\xcf\xd3\xf0b\x82X`\xf0zpB\xbf\xf4\x19\xe4\x04c6f\x907#8\xec\xdf\x8e\x92\xfa\xdd(\xd1\xd5\xfc\x1a\xc3\x9c.k\xaa\xa9\xae\xb9\xd8m\xb0\xa7\xa7\xc8\xf0\xc3\x0dpW\x0d\xeb\xa3\x03Q\xb2\xf5\xe3\x88e\x070\x0d\x8a\x93\xdf\x0b\x03\xadk\x8b\x0e+? c\xf2\x82\xdfT\x8f\x9d\xee\xbc\x0b:z\xd5\xc8\x8d\xce@\xaa\x91\x13\xab\n\xa3bp\x9a\x1ej\xca\xae\xee\x8e\x86\x13\x96\x91U_P[\x87\x11\x97i\x9b\x84Q\xa9mX\xd5h1\xa0\xc19\xa6\xa0(\x13\x08\xfc$ 1H\xd6\x86u\x04D%\xb50*\xd5PF\xeck\xa4\xa9(\xd3\xe52&O\x05\x99\xd1\xef\xbc\x87\xe0<\xc2\x1ebG\xe8+u\xd5\x02\xcd\xd2\xb3\x0c\x0e\xa6\xf9X\x95\xeb\xf8 \xd6q\xd8i\xbe\xdb\xf1N\xceKq\x8c\x89L\xb4\xc0\xca\x92\xa9?`\xf4U\xe3\xf8\xbf\xd5Oo;\xf1\xad\x89\xeb\xa9(\x81\xc1\xf9Z\x81\x9d\xad\xe4\xcb\x9a}\xa9L\xea\xd4\xbb\xab\xf0.k\xc7\x9c\xd4\x87\xd1\xaay\\\xf6D\x1eq|\n\xdf8m\x02\xe0\xf6\x04\xe0\xf8\xba\xef\xfd\xfe\xbe+\xbfW\xf3\x17\xca\x1f<\xaaz\x10V\xcf\xdf\xb7\x95\x03\xdb\xa6x\xda\xe5\x97\x9b\x98y\x05\x89\xd9\xfdY\xcdLDU\xde\x10T/\xa5B\xbd\xa4\xd0\x1cQ6\xf9\xe6\xf9:\xbe\x19y%)J*\xceJ\xe1(\x83\x8c\xcbf\x02D\xab\x08<\x84\x84\xc7\x80\xd0\x9e\x9e\x9e\xafYu\xb0\xe6M\x99\xe7P\xb4\x00\x97w~\xef\xf0\x10\n\x9db=\x86C\xd8C\x8e\x0f\x93\x17\xfe\xfe\x9e\x8e\xb2\x903M\xc4+HyLY5W'\x1c\xe1fW\xd4\xb0\x1e\x8d\x9b9\xf1\xf5\x9eH\xc5?\xd7\xb1V\xa1\xd7P\x06(\x12\x9cK\x94u@\xe2\x82\xe0\xdc\xb6\x92\xf3\x17x\x0c\xb8\x0e\xce\xb1\xaa[\xfa.i\xbb\x83L\x88\xacEMc\xda\xcf\xb5)\x0d\x17\xf8\xd97\xad7\x14\xd1I\xafXvK\xb7\xe3R\xae$J\xbcE\xe2E\xc9\x82\xe4\xc7X\xe2\x7f\xe4\xe6<\xdaF\x9dg\x8d\xbe\xb7\xa0h|\x8c=\x16/\xa6\xa8\xefT\xcc\x07+\xb0\xf0K\x1e\x95\xe4E\x12_H\xf3]*\xe6EL{kf\x14\n3\xa1\xf7Lj\x19B=~\n\xf4\xcf\xb5\xa44\x99q\xaf\xf0}\xa2\x90\x90\x0d\x8bOw\xd1i]bc\x0c\xa9|\xdc\xa7C\x06\xee\x92N\xed\x0e\xf8\xe3\x0f\x08G\x0c^\xfa\xf96\x03>\x14\xedl\xe8p\xde%\x98\x89\x82`\xa6\x1d\n\xac\x82\xa3\x84=\xa7Bl\xcb\xe0\xea\x95y\xb4vYA6\xbd!\xb6\xb1\x85\x95ek9\x99\xe8\xc7\xba(\xb0\xb3\xc3J\xea\x8eUh\xa8\xa6k\x0c3+\xd9\xf8;v\x8aURc\xbe\x14^\xc2\xfc\xa8\x0c\xc9\xef\xe5\x96\x8e\xeb\xe9J\x7f\xdd+\x10\xd0\x1f\x0f\xee\xdf\x1a\xfd9\x8a\x10\xfc\xf9\x1c\xc2\x189|\x92\x06\x9bK\x96 \xe2$\x88\x15\x94\xa1\x1cB\x98\x068\x0e\x8f\x9c\x93\xe0Q\xba^\xfbI\xe8:A\x9a]\x98Sd\xc9\xa8\xd4\x07\xf3\xcc\xf0\xb8\x12R\xcd\xb4\x95\x9ck\x88\xeb9%W\xe0\xfd\xae\x0e\xce\xac\x8bK:\x8fX\xee&\xd3\x17\xd5T\xb2]\xbf'\xa3\xd2dQ\xaa\xb3\xcb+\xdb)\xc9y\xe9\xe7D](\x11P\x14CTj)\xbb\xf0\x8ezrs\xe2\x87\x8c7b\xb6q5dk$tZ\xd4\xa0V\x89A[\xc52/\x91\x0bT\xb0E\xf2)\xfd\xa0\xe6\xf7\xebP0\xa7\x7f(m\xe8\xa14\x95\x9dJ\xf4\xc9\xf4\xbe\xecX\xa2O\x1eLUqlj\n$\xbc\xd1N$\xa5\x08(\xe3&\xab?U\xd9|\\gE\xfc\x90\xe4EW$\xa5\xe2h\xe9e\x9bb\xe52T\xc3\x84\x9d\xec\xef\xc9?\x9d\xb1x\x9d\xe5\xd1\xc5\x18N\xfe\xf8o\xce\xdf\xb0zf\x9d\xa1\x08n\xc0\xdf\x9c\xbf\x8dx|\xf4\x06M\x12*V\x93\x9e\xaa{\xfbrTC\xb1Wa@\x0e$9C\xc5U\xe6\x17\x8a\x8dP94.\xc6h{\xea\x9c\x1b\xdd)\xf2HR\xe6\x11)\xa8\x90\x04{.\x16\xba\xa1\xc7i\xe6%\xe4\xbctG#/L\x132\xfa\x9a\x8f\xc2d\x8e\xc4L`6\xd6\x91\x15\xefZ\xe3\xc8\x0d\xc7p`R\xcfS\x9e\xedd\xdfP\xa1b\x8dPS\x89#\xa6\xb8(\x12\xad\x1b\xab\xff\x038\xdd\xd5\xde\xc2\x0dpf\x98?m\xcdW[N\x0b\xfa\x84\x00\x02\xbf\x0cV\xa0>Yc\x86\x11\xb8\xc2}{\xc1{XD\x89\x1f\xc7\xaa\x15V\xaf=\xbd\x98\x12%\xf3\xf8\xa1\xd5\xf8\xed*\x06`h\x0e\xf8\xd6\x89GP\xae\xf2\xf4\x8c\xbb\x07u/\xc9<\xfc\x97\xfa/\xfaA\x8e\x8a\xf34\xbc\x90\xa5\xd6\xa1 \xcez\x13\x97Q\xe6\xe7\xe5\xcdE\x9a\xaf'\xa1_\xfa\xcc\xd1\nG\xe6\xbc|q\xfc\x9a\xfd\xdd\xdd\xbb\x1aNa\xa9\xd9\x8f\xc0-|:\xa7\x8e\xb9f_\x82q}\xaa\xfdy:\xc6\x8c\x1c\xf2\xfd\xc9&\x057\xe7\xc51\xf9\x8d\xefN\xdas\xf7\x14\x0e\xe1\xac\xbb;\x97\xc6\xdd |\xf4G\xfd\x8dw\xca7\xacq\xfb\x01\xcf\xf5qd\xdc\x82\xc0\xb7\xe1\x91v\x1b\x02\x9e\x08|\x0f>q0h>J\x8a\xd2O\x02\x92.j\xae\xdb{\x12\xa1\xb0\xd0\xda\xa0\xe7t\x83\x1e\xfe\xffq\x83z\x89\xbf&\xf4\xef\xaf\xcb\x8b\x8c\x1c\xb2{\xf4'\xdf\xb9(P\xf7\xde5\xeem\x90\xe25X\xedq\x10\x98\xb4?F\x8c\x91\xdb\x05m6\x9f\x1e\x9f\xe8\xb5\x87\xc1\xfcg\x8d=\x7f\xa6\xdf\xf3`\xd94\xf0}x!\xf6\xfe|\xe8\xabe\x0f\x1b\x94\xb7#E\xb5 \x84\x97\x13t\x07uo\xfe\xeb_\xc9\xcd\xe5\x18\x1c\xa7\xab\xd8\xe3\xe3/e\xe5\xac\xdb\x1c\x8d\xcf\xb9\x93[\x8aJz\x9b\x8f'\xc4^7F\xefK\xcc\xca\x97\x98\x95O\x11\xb32 Z%B\x95c\xb0\"k\xab\x9a\xd7\x0dp\xab\xcf\x0b\xf1#29\xd5 c\xa0.K\x1b\xb3\x072\xbeD\xc1/\xa0#\\U_\xb0\x1e\x19\xe2J~\x0dCiZ>\x98\x97\xad\xe3-Q\xde\x148\x01\n\xeb\x1f305\xd6\xff\x9aV\xf0n\xba\xa7\xb1\xd0\x17\x8e\x82H\x9b\xf8\x10\xebr\xdd*p\xcc\xa3\xdb\x1b\xb3x\xfd\xf2c\xff\x00\xca7\xbd\xd2\xad\xea\xbc~_\x91\xf64\xec\xa6\x993;\xae\xd4N+\xbcW\xc3\x95h\xc6\x94\xa3M\x1d\x17o\xc5T\x0e\xf2\x98wF[\x89\xc5\\\xe7[Q\x8c\xdb\xa8\xf6R\x16\x8a\xe1d\x16E\x92\x01u\xfcL\xebdY\xb2\x9b\xf7\xce\xa0Z`\x85\xbd\x95 \xb6%\xbbM[jw\x05\xdf\xf5\x8c\xaf\xf9\xc2\xf7} \xbe\xef\xcfg`\xfa\x14gF\xcd\"\x99\xce\x0d\xcb\xb0\x82|@\x90\x00s\xb1\xa8\xc2\x17\xf91\xac\xd1\x96D\xf8\x02'\xf6\xe6\xd8\xd8\x82\x04\x9b<*/\x1e\xd3}\x1d\x95\xa6Z\xc7t+\xe5\xc6x\xdf\x98A\xf9\x9br\x95\xe6\xd1\xbf\xc9\xf7%\xa5\xb0{\xdd@\xb6\xe6\x15\xb0W\xc4Qx\x05\xf60\x8c\xd4\xe5\xc5&\xff\xf8\x03\xfd\x9d\xae\xc4\xea\xc5\xbax\x890\xda\xcd\xb0\x96\x8a+\x89\xa3m\xce\x86z\"\x02m\xd7\x9a\\\x91>\x84\x94u\\\x9b\xdf\xaa\xb1\xad\xd4\xc6\xae\xcaAX\xb7z<~\xbaJq\xf5\x1f\x9b\xeb\xea\x93zo\xc8\xe3T\x03\xb7ht4P\x1f\xad\xd7\xd9wC\x15Xj\xad6\xd9~\xf8\x80\xd2\x88\xfbP\x89*\xf4\xa1\xc9\x87\n\x1a\xf94\xd2\xe45\xbe\xcchD\xfb\x9e+n\xac\xd3\x90\xc4\x942\x8da\x8f\x07\xaaz\xe4<\xf3\x93\x90\x84#\xa1\xea0\xb8\xc6\n\xf8Y\xff\x13\n\n\xd0\xdf\xc3\xf2\xe9\xdd\x98\xb4&\x18iW\xb5&\x87\x89\x11&\x10S\xc8\xe3\xc8\x94\x1a*S\xb8n=ZE\x9f\xba-\xcd F\x99[\xac\xfeK\xee$\xd8\x86\xeaOI7\x9a\xf7\xc3\xf0^6\x11\xbc\x1f\x8e\x0d[E!9&\xf1\xe2Er\x84\xd3j\xe2\xc5\xf4+\x0d\x15\x1bV\xa1\xb5B\xe7C\xf7D\xd2\x89\x07\xac\xf6F\xdes\x0c\x85!\x1a\x90\x0f\xad\xfd\x11s\x80N\xf0\xf5\x94T\xa3\x19\xb4cw\xd8\xaa\xb6\xf3\xf0 \xb8z\xd4\x82\x98p\x08\x991\x956P\x98|\xaa\xe8\xcd\xfe\xfc\xb2U\xe8b\xae.\xdcl\x88F'\xc1\x0c \xea\xf2\xb6\x0d\xb5\xde*\x8a\xc3\x9c$\x943\xfa(M\xebB\x0d\xcd\x0d\xc9\xc2\xcc\xaasM\xc3Q\xdaxi\x05\x9b\xbc@\xa5[\x96F\x892_\x1c\xf4\xb0\xb7\xba\xcb$\xe7?\xed\xe0v\x1fX\xab\x92\x04%\xaa\x1368\x8c\x8b\x95\xed\x12\x1eP\xe4\xd4\xc7\xa0\"|\x17S\xf6\xcb\xbf Ar\x985a\xbb\x87\xa7\x91J\xf5\x85\x02\x990\xb0h\x1d\xd1\x92\xe8\xb5\xee\xc1\xee\xfc\xeey\xde\xfb\x0e\x89k\xb0C\x1d\xaf\x0f$O\\\xf8i=\x10GO\x9b(v\xdc \xbb\x14\x87~\xbf\x1e\xd2\xf83\xf0\xf9\xbb\x96*\xc11\xfb\xa10\xdc_g\xe5\xe0\xe7!\xc1\xf8A\x19m\xc9k\x7f>\xc8VZ\x99aC\xbf\xf4\x0bR\xa2G\x8e\xfc\xc8\xb6\x92Q\xaa^\xa8\xd5\x12\xbd\xdb\x97\x13JP\x13\x98,\xa2\xa5\x02\x8a\x89%\x86\xc0\xce\x00\x13QW\xb9\x86\x9fS\n\xfc\n\xf9\xaa(Y*E\x18G\xc4\xef#\x8b\x18\xa0k\x1b\x12\xef\xc6\x0d\x97~\xba\x02\xb4HS\xd4\x98\xc1\x98R\xf9\xaa\x8d\x99\xc4\x83\xefc\x0b/W\xc9j7\xb2\xce\xb0-^\xffIg\xafq8\xb5\xe0ly\xef\xc6XG\xee\xc4\xd1\x90\xefG%Y#\x9fY\xd3\x9a\xc3\xc3ff\x9d\xc6\xd9\xf2\x10\x1c\xbe\xb3x^\x96\xc1}\xd3\x07\xadt\xba\x16G\xc9;U\x860\xa8\x92\xd9\xf0$8\x8e9\x9dJ[~\xa8\x86\xa5\x1aDD\xc7{\x14F%`\x8c)\xcb\xbe\xc1\x1a\xe1wX\x154\x8dqd\xd7\xa5\xe0\xe7\xc8\xf5Z\x08\xda\xb3\x88'\xe7i5n\xbbBlTW\xb6>l\xc7\xd6\xb9P\xcc\xb1Y<\x92\xcb\x8c\xe8_}\x05\xe9\x18\x8c\xcb\xa0\xa9\x84\xa65\x071b\xab\xad\x94\xd2.M\xa2\xa1\xf55 \xd5\xa6;h\x1d\x06\xda\xc4'\xa4\xa6\x993\xd0\x14\xb3\x14\x14Y\x97\xef\xb4\xf7\xc0(1~\xdef\xa4\x05\x15\xb1z\x12S\xca\x9f\xf4\xa4\xb2H\xbc\"\x13\xbe\x162\xa9l\xc3\x1f\xf4\xda(\xf8\x83\x9eT\x16K\x0dL(\xfe\xb8qS,W\x1b\x98\x16\x1f_<\xcbl\xc53\xbd\xcfn>\x06\xbf\x7f\x92wy\xdfk\xe3\xb3+\x92\x84ozb\xa2\xc2g7\xed\x8b\x8az\x9f\xdd\xbc6X\x1d\xb6\xb7\x8e\x8aG\xcde\x89\xe3\x01\xabE\xc92\xca\x17\xab\xf4\xcc=a\x94\xb3p\xc6@\xde\xd2o\xf7\xe9\xc0\x989Q\x8c\xbb\xe3\xa5+f\xe9\x0dSH\x85\x1a\xdfN\xa8\xb9\xe6\xbc\xbb\x0dc\x9c6\xf8V\xdd!\x1c\x19B\x9f\x9a\xda\xf8\xe6\x92V\xc7\x05J\xb2Q\xdb\xdb\xb7\x03\xe2E\xc5\xf1*=K\x9aK\xdf\x80\xa6\x1c\xc0[\xccB\xa0?\xa0\xed8\x12\xa6\"\x9d\xa7\xe7J\xdeX\xd5L:\xeejX~o\xa9\xfbu=h\x1e\xb4\xc6\xe3\x93\x84Z\x0f\x8e\x90\x9d\xae\x9ax\xb5ZYY2'P\xf6\xa7\xa9]~l\x97]C\x16\xde\xa7T\xa3\x9f\xf5\x06v<\xabc\xe3\x19\x9d\xe1]\xc3\x19\xed\xea\x1e\x82\xf2\x10\x07\xbe\xad\xd0^\xe2\xf06)g\n%\xc6\x9c\x89^\xcc\xa0c\x84\x16G5\xe7\x02\xfc\xa2\x88\x96h\x931\xeb,\xaa\xe3\x806<\xfd\x1aJ\xf8\xa6w*|\x0d%\xa5\xfcj4\xda\xf2<6\xf5\xa1Pj\x82\xed\xaa&s:\xb4d$\xba]%\xfd\xf6V~\xf1\xe2,\x11l\x0c\xd3\x16b\x04\x02\xeeZr\x92\xd3\x13(9\xc9\xdf\xdaF\xc2B\xe3x\xef\xe3D\x1f\x01S\x1bw\x89\xea\xc4&\xda\xc3\x06\x9aCN\xd8\x81\x9a\xc07PV\xb3\x9b\xe8g\x17\x1a+\\\x9e$\x860\xc6\xdc#\xc9fMr\x7f\x8e\xe7a\xebO,&1\xc6\x9a\x88t\xd3o\x04\xd0\xde\xfe\x18x\xf64\xba$X8\xd1\xcd\xbd\xb3<*+\x88\xd1X\xc1d\x12\xfa\xc1w\xe4B\x1a!\".\xdb\xa0<\xa8\x17\xaa\x9a\xff\x92\x87\x9fh\xa6\xa8\xe27(\xeb\xe66P\x89\xee=^ \x12\xd3B\xe5\xbd\x9c\x84\xe2\xea\xf7\xe5\xbd;\xeao\xb3\xc8\xa8\x8c\xae\xd0\"2\xd5\xb9\xb2\xe2U\x80G>\xee\xb9\xa4\x19\x92Z\x8eD$dB\xce\xe0\xf5EF\x8e\xf2<\xcd]\xe7\x91\x9f$i t\xcf\x80\xcf\x8e\x18\xf0\x0b\xf0\xab\xd6T\x825g\xcbT \xf8\xa014c\x87At\x9a4{\xf9\x8a,HN\x92@t\x956\x08+\xbfH\xfeV\xc2\x9c\x90\x04\xd0\xe5\xd4\x8f\xa3\x82\x840\x81b\x93\x91\xdc\x1d\xb5 \xe8\xb0H\xa8+\xb9\x0f\xf5\xfc\xee\x95h\x97N\x11m\x1d\xd8;\xc4\xcc\x9dt\xf2\x90\xc0V\x13\xd2z\xc2\x98}9\x8e@c\x9e\xdc\xa8\xcd\xba\xf2\xcd\xb1$\xe5K\x81|/\x16nd\xe9\x1e\x0dR\x0c\x1c\x82'\x18\xa5.\x1f\xd2W_\xb1\xc21\xa8\x84V\xa0\xcd1\x9dlz\xe0\xe6\xa4((\xf6\xae7E $*W$\x879a\x1fH\xf3\x06\x1e\x8d\x81\xe2\x99\x037\xaa\x86\x14\xabB\xea\xedX\x9fQ\x8c\x87q\xb1s\xad\xfd\xaaa\x97\xd2\xa4(\xf3\x0d\xe5\xcdL\x96o\xbb\xf8\x8c\x9a2\xea\x8b'\xd0K\xd0\xc2\x996b\x1fX7+\xda*M\xc9'.\x05M\x1cq\x87 \x97\xcfT\xd1\xc2(x\x08\xd2\xfb\x1c7f(\xb9\n\xb4<\x94\x8a)n4\x86\xa62b\x0c)\xbd\xa5-\xd7P\xac\xd2M\x1cV\xef\xbc\xc1l\xa5\x96\x95\x03\xb4\x019\x82\xf5\xc0\xed\xa1\x9d\xd7T\"\xaf\xc2\xb70\xa5s\xd5H\xeeY\xf3 \xd3\xb7\xf0\xb0\xfd\xe7\xacg\x1a\xef^Q+\x01;\xdd\xd7\xaa\x02P\xd0\xa03\xcc\x9f\x81\xa5p}\x910\x1f\x80\x9a$\xbc#\x17\x85\x9b#WNZu(F#\x8flI~Q\xb3\x8b\xdaC\xae\xd1b\xe2E\x05\xf2Ac\xb6y\xb2B\xc9\x0c\x01\xe2\x14\x1e\xfd\xedn\xa2\xb9I\xd1\xcf\x94\x9e\x03\xfd\xeeiW\x12:\xddKO\xa8\x9c\x1c\x9d\x10m\xc7\xe4{\xa0\x8f\xb4\x94S\xef\x18\x06\xbb\xc73\xf1\x9e\xae\xd7\x1b\xdc\xa5\xad$\xc3p\x08\xd1\x18H\x83\x89\x8f4\xbc\x8cNa\x06R\xa5\x19\xb4\x07\xf2\x9e%\x88t\xf7E\xdd\x1d|r\xdd\xb4z\xa14WR\xca\x9f\xdc\xef)\xe9\"\xfe\xa4\xa7\xef\xf3\xf9\x83\x9e\xbeo\xc3\x1f\xf4>U\xf0\x07=}_\xcc\x1f\xf4\xf4}\x81T\xdf\xb7@\xf0\xa0s7\xe3\x1f\xb9\xd7t*\x08\xd5\x8a\xc0\xf0\xe3+\x02\xf5e\x8c\x86(\x02\x15\xc1\xfb=\x97\x0c\xad\"0\x96*\x02\x83J\x11\x18\x8f\xc68\xd7\xfb_\xc3\x02\xbe\x81\xf8kXP\x81%8Y\xb4\x15\x81\x0b;E`a\xab\x08\x8c\xec\x15\x81\x01W\x04.yd\xb2\xff=\xaf\xa9n#\xc7\xf1>\n\xdd_\xcb\xaa\xe0E\xc5\x8b\xef\x8eoa\x01\x87\x93\xdak\xa0p\xc6<\x1e\xc7/\x1cz\xae\x9c8a\x1d1\xe5\xbc\xed\xb5\xf3\x9e\xf7\xeeQ\xc7\x13l@\xff\x1c\xe8\xab\x86\xf0\xb3,\x11\xde\x15h@\x15\x8aN\xce\x8f4\xe7G\xbc\xc0\x93\x1b\xbe\"E\x1aoIx\xbc\x99\x979!\xeeI\xb50\x1d\x85\xaed\x85\\\xbar\xf4\x900\xa5\x17(Z\nU\xdb\xf4\x02\xb1T\xa1\xba\xf9\x04\nU\xbd*\xd5F\xe5\xca\xb2\x1d:\xfaa3<\xcf\xfd\x80\xa0\x8d\x18\xb8#\xb9\xaa=F\xb8,\xa9\x90\x1dE\xb4\xebb\x94$$\x9f\x18z\xa7l\n\x1d&\xad\xdb\xda\x0d\xe1\x9c\x12k' z}\xa4\x99#\xa7\xcc\xb5\x9d\xb1\xcb|\x96\xc6\x98\xf8\xec/w\xef\xde5h\\\x17iR\x1e\xb3o:Q\xe9\xc7Q\xb0C\x9a4\xf5`\xc2\xfa\x90jp\x893GG\x99\x1a/\xa9`^h\xa7(\xdd\xe4\x01\x99\xc1\x91\xbc\xbb\xa3Q\x8d\x80\xe7\x94H\x9f\x8b<\xd0\xe7J\xc3\xb4\x95\x0fw\xc7i\xcf\xa2\x8e\x1b\x0bi2\xd9\xae\xd1=\xe9dj\x80\xa2\xf2\xe4\xa9\x8b\xa7\x8e/\xd8\xf2,'\x81_\xea\x99X\xe0\x02\xe6\nm\xa9^T\xa0I\xf5\x1d~\xe8\x9d\xc7\xad&\x85\x9b\x1b>\x91)\xf3\x1f5\xaf-\xe5\xdc\x03?\xfe.\x8e\x96\xc9\x0c\x9c2\xcd\x0c\xf8I\xaf\x8cr\xff\xc9\xf2\x15\xf7\x9c\xd8\xf7\x0e\xc8\xda\xc03\x1amQ,\x026\xf3(\xfe\xff\x82>\x19p\x08\xce<\x8dC=n\xeaw'\x08\xad\x84&\x0d\x04\xb4I\xca\x86G;Vk\xa5\xde~\xa6=\xa3\xef\x17\xa7\x1c\x99\xee\xfb9\xe7dv'\xcc`K\xa3\xa0A\xa7r\xdd\xb0AIy\x80\x1f<\x7f\xd7s:\xf6sc\xee\xb1\x0c\x81w\xef\xb9\xaa\xcb/\xc7\xddT\x00\x16(\xc7\x03\xbd\xd0V\x99\xc0\x0dp\xf0WN\x7f\x9d\xd2_\xbe\xae'F7\x07!\x0f\x1b-\xf1m\xbf\x00\x83\xd5\xab!\x9b\xf1:\x84\x0d\xcd\x00\x86+\x9a\xdb\xe2\x0e\x02\x81\xa1%\xeeIa\xf0 \xe0Q\xdc\x0b\xb8\xa1\xb3\xa8\x8dd\xd62\xf6\xa46\xa8U\x87\xcc\x99\xf1\xb8\xe7'\xe4\xff\xfc?\xa7\xfdV\xf9\xb1\x0f\xa4\xc4\xea@J\xf9\x81\xa4&\xb2\x18\x8dw>\xe1%b\xbd\"\x8e\x02B{s\xa0,\x08+\xae-/\n\x99\xc2CH\xbd2\xfd\xf1\xb8\xfa\x81S\x9a\xf2 \xb2\x8a\x80\xbc\x0c\x19\x07\xb1\xaf,\x1cU\xac\xc9\x074\x99\xb3{\xf7\xee\xe9i\x07h\xe9\x07\xd8\x1c \x0c\x97\x92K\x92G\x18:\xc6\xc1d\x12l\x86\xda\xf1\xfc\xf3U\xbb\x10\xd4\xbc\xaal\x7f\x1e\xd3\x13\xefX0\x816;\xd5f\xce\x9do\xe0\xef\xf0\xed\xa59]\xc9Q`\"\xd75\xa9\xd6EuZ\xd3\xe9>\x8d\x1e\xaa\x8c\xb5$\xd3\x82D\x1f\xabA\x8c\xe4\x19Is\xb5\xb2\xbf^\xe5z\xa2\x0e\x0c&\xdf\xda\xae\xe8\xaf\x1d\x8am\x88\x197\x91,\x1b\x1f)\xa4W\x9a\xd8\xed+E3\xb0F5\x18\x82n G9T@\xa2\x89\xd2\xdc\x8c\x19\xd5\xa0\x81n\x06\xa7 #\xca\x01(\x92\xad@W\xda\xfc\xe9*\xd1\x11U\xaa\x03\xd0\xf1\xa7/\xe8\xd8\xb8.\x89\x8eL\x9f\xfd\x99\xa3\xe3\xab\xabD\xc7$-\x07 \xa3\x01\xad>\xbf#\x11\x0d\x14Wv\x02\xbe\xba\xec XW\xff\xba\x94 \xa0\xaf\x08\x0e\xe2\xb4\xd0\x94K}\xef\xec\xe0G\x98\x19\xfd\x08\x99\xe1\xee\xba9Pe\xca\xcc\x90\x99\xd4M*\xe2O\xa41\xe4\x99*\x86^z\x971\xa8\xdc\xbc\xac\xdc\xc6\xa0\xf2\xf42\xbbR\x01W\xe1G\x83E\xffd&\xf4\xb7^\x94\x84\xe4\xfc\xc5\xc2\x95\xa4\x12j^\xa6\xd8\xa0%\xcf\xeci\xe1\xfa\x03\xdci\xac\x1c\xe0\xd6\x03\xdcw\xcc&y(p\xe7\xb1\xd2u\xc4\x81h\x02?\x83C\xd8R\xd2~\xb98\x17\xd8\xc5\xbb\x02\xe0\n\"l`wg\x06`\xedo/\x13\xe0d\xd5GK;3\xe8\xe7C\x1b\x9d\x0b\xb5\xeb\x82!\xc4\xaf\xf6L\xf0\xe1\x9bC\xd8\x18\xc8L\xbf\xc2\xd3\x89\xe7yo\xb5#pN\x9c1\xac\x85\xdem\xbd\x9b\xae\x1b:\xfa\xeef\x90\xa9Y\xdf\x0d\xd6:o\xa8\xcc\xb5:\xbd7\x98q\xc1\x18\x97\x05\x95\xe2\xb96\xe2\x98\xfbF\x8f\xd0\x7fX\xaa\xab)\xec\xcf~l\xb4R\nX\xceB\xc9+\x1d\x8aK\x91\xcb\x8a=\xaad\xce\x0c\x1e\xee\x1ej+\x0c\xfb\x1a\x13&m\xa9B\xa9K\xc5\x1b\xb6v\xa3\xa0\xda6C4\x11\x01=\xd4\xfc\x12\xe9\x8c\xc1>\xa51\xb4\xa4\xd8\x80K\xb1V\x078\x0bvN\xb4\x9ex\xd0\x10f\x0d\\\x87\x9dh\x0e\xb5\xe8\xeb\x1bU\x1fcpZ\xf17\xad\xe7\xbd\xbb\x1dy\x14o}\xb6\xb1mr\xc93UI\x9e\x91J\xf2\xf4U\x92\xe7F%y\x16*\xc9S]\xad \xeb\xc5qRy\xd4\xcd\xea0\x9c\xe9\xfe\xe7\"\x80\xde\x9d\xd3\xff]?\x19TR\x14\xa1/\xf4)e\xd0\xf4\x03\xc8\xa0;\xe6\xf8\x87\xeb\"\x83\xdaH\x89\xc9@i5\xddAZ5\xcb\x8a\xfe0Yqc+\xda\x16\x18D\xdb\x0d\x15\xd1{\x03\xb0d\xc4{\xe8\x9f\\E\xa4\x18J\x07\xa0\x06S\x9f\x0d$n\xc4yP\x81\xce\xc2K\x8d\x83/\xd2|\xedk\x95\xb6\xc0\xb7#\x7f\xe1|m\x94\xaa\xb654F\xaa\x1a\xc0\xd7\xd2 \x15\x9f\xfec\xc8\xa7\xb1\x1c\x1c|\x03\\\xa8d\xe1vKR\xd6\x0bG\xf7\xb6\xfeE\x94,\xafL\xf2\xc6\xa9\x19C%\x81\xf3\x95\xb8\x02\x11\x9cw\xf1\xa7\xb4\xdc\xb9\x97\x17\xde\xca/\xcc-\xe9\xe7\xeb\x14\x8fe\x18\x83i.)Y<_\xc7\xe8\xfa\xb7\xfa\x0f\xd9\x13vS\x07;m\x0c\xe3\x84\x83\x81\xf1h\xae\xbd\xf3?\xff\x8f\xfe\xcf\xc1\x14\xe2\xce\x0c\x9c1\x1c\x97y\x94,\xddT\xe7M\xdaL\x94T!\xe8Vw\xe6\x9e\x99&\x83K\xaa[\x03\xa7\xdf\xf2II4=\xbc\x9c\xc2\xcb\\\xfa\xeb:(\xbc\xc6Pz\xe2}I <}\x86\xa7k\x91\xe0I\x14Qj\x8d\xc3&\xd3\x13?\x1e\xfa\xd8\x92T\x8f\x7f\xf6%*\xd9\xb4z\x8c\x87\xc0\x15ef\xe2{\xb2\x97\x0d\xc9*\x05S\xd9\xd9yI3W\x92\x1c\xf9\xa2k\x80|}<\x8be:\xd5\x94?\xe8\xe9T#\xfe\xa0\xa7S\xf5\xf9\x83\x9eNu\xc3\x1f\xf4t\xaa\x05\x7f\xd0B\xf2X\x8d\xe4\xf1\xc7G\xf2\xe0\x8a\xb2\x14\xa5*\x05f\xcf\xbbF\xa6\xc0\xcc\x87+0\x95Y\x8a6R\xc5edR\\~\xb2,Ei\xf2:\xbfH7%\xa6\xdfV\x03'\x1c\xf8\x91\x9f\x04$6\x00\xe7\xcc\xab%\xf1\xe71 \xb5\x01\xfe\x86\xba\xdd\xea\xb3\xb1U\xa8<\xbf\x98\xa4\x1buT\xb7\xb6R\xfb|S\x96\xf6Y\xd1\x9dy\x99\x00o\xef\xf4\x94\xfe\x11\xe0\x84\xd8\x147\x97\x1f\xcb\x94\x0fd\x93\x8aa]\x1f\xaa\x9f6\x1dT\xd4\xfc\x1b\x83\xf3:\xbf\x80\xa8\x84tS\x82\xccdfp\xdd\xd4\x17\xf7\xaeX#V\x12\xaak?i\xe1\xe7\x0c\x9e\xf0\x1d\xd0\xa8\x86\xd6\x01o`\xa8\x19\x9c\xe3\xe8\x0c\xf6jc!&\xc8\xa8\x0f\x95\xebYp\xfc\xcb\xa1\xf2\xe5P\xb9\xbe\x87\xca\xfc\"\xf3\x0bC\x91\x16\xe2E\xc5\xf1\x99\xbf\\\x92\xfc\xc0t\x94\xb0\\?\x1a\x12\x86P~\\\xa4\xc7\xab\xf4L{\xe2\x94\xba\xc3\xa0\x19XP\x8f\xd6\x0bVQ\x1c\xe6$A\xa1\x0e\xcb\xfc\x98?bG\xa6\xb7$/\xa24\x99d\xb9\xbf\\\xfb\xca\x13,\x1d\x7f\x88\xe6NO\xd7\xa4(\xfc%\x01\xc5\xfd\xc9\xc4_\xcf\xa3\xe5&\xdd\xa8\x0b~X\xcd\xa5\x12hu\xab\x0e\x0ey\x83\xb4\x18\xca\x14\x18\xc6\xe2\n@]\xea\x06\x13\xc7\xa8>\x94\x99\xdb\n\xd2\x90\xd4\xad\x15\x0c\xf5X\"V? \xa9\xa4a\xf9j\x9a\x91\xc4\xcf\"\xf6\xea\"\"qXP6 IK\x98\x13\xc8rR\x90\xa4\xc4\x8a\xd4+\x02\x85\xbf&\xc0\xf1\x1c\xd2\x1c^d$\xf9\xee\xe5\xd3\xc6\xb8\xeeY\x8e\xdc9\xdedY\x9a\x97$\x14\x0b*z\xe7\xe7d\xc0\xf8\xf8\xd4\xa0\xf0\xf57\xe7\xc0\xdbw\xfeV\xcdR\xb9J\x0b\x02\xe5\xca/a\xed\x97\xc1j\xc0g\xf9\xb4\xcd\xe0\x96\xb7\xef%l\xf6\xdcE\x9a\x039\xf7\xd7YL\xc6\xbb~k\x1f\xbf5\xf2\x1c\x11\xd3BI\xb0\xc5\x16\xd5\xee\xf3\x0f\xb0\xdf\xae\xdf\xf6^GE\x11%\xcb\xcfgs;\xafWt\x87\xa5\xdb($a\xe3u\x08SR`\xad\xdd\"#A\xb4\xb8\x00\x9f\x1eoQg'X\xef$\xbe#\xa3$\x8c\x02\xbf$\xd5\xd7$\x1b\xb9\xdd\x00|\xd9\x83\x97\x11\x10Z5I\xed\x85\x04q\xf2\xcb<\x0e\xc5\xa6\x96=c|\xca\xe7\xc7\xfd_c\xd5\xe5\xe0\xdc\xf4l\x97\x0c\xd48\xae\xfd8\xae0Q \x96\xe5\xf2\x9cm\x12\x9a\xd9u\xb7\x03\x07\x13\xb6\xe3\x7f\xafY\x92v\x8a\xa0\x8f \xc9\x9eE\xc9\xbb\xcf]\xbd\xdd\x18\x87\x0d\xb2pq]\xa9\xde\x96F/1\xe1\xa0$\xe7\xe50$\xf3\x8d\xb8\x93\xa4\xa8\xe1\x96\x88V\xb5N\x05\x1e\x1a<5\xa11\xd9^\x96\x93-I\xca\xc7\xacG\xae\x84\x92*\xf3\x9b\xae\xb0\xa2[\x89\x15\xddn\xb2\xf4N\x0c\xb4\x8b\xd9&=>\xdbT\xe9g\xa9n\x1f\xe3j\xf7\x1d\x89)\xb6\xb9\xb8+F\xacLk\x0b\xa1s=B\xe7\xed\x19\x94O\x86R\x8a\xe6k\x1b\xd9\xb0RJ UU\xc1\xf3u\x9c\x143pVe\x99\xcdn\xde<;;\xf3\xcenyi\xbe\xbcy\xb0\xbf\xbf\x7f\x13_\x93\xbf\xf4\xcf8J\xdeI\xdf\x9c>x\xf0\xe0&\x16 \x94\xbc\xabM\xf0\x93\xa5\x05rc3p\xfcy\x91\xc6\x1be\xf9{^\x05QQ\xbcF\x94?\xdc\xef\xa3\x7f\x17\x99\xd5\xd3J\x16\x85\xc5\xbc^\xac\xe7i,\x9d\xdamD\xce\xbeO\xcfg\xe0\xec\xc3>\x1c\xd0\xff\x93\x0c\x06\x0bNm\x928\x0d\xdeu\xd3\xd3\xe9z\x97\xb1<\xe0\x12\xa4\x9b\x81\xf3|z\xc7\xbb\x0f\xf7\x7f\x98\xde\xfe\xf9\x8ew\xf7\xd1\xf46\x1cx\xf7\xf6o\xc1\xf4\xc0\xbb{\xf7\x0eLa\xba\x0fS\xb8\xe7\xdd\xbau\x1b\xa6p\x97?\xbd\x0bw\xbc\xbb?\xdf]\x1dl'\xde\xfd\xfd\xe9\xa3\xfbp\xcb\xbbw\xe76\xdc\xf7\xee=\xb8\x07\xb7\xe8K\xb7\x82\xa9w\xb0\x7f\x8b\x0e\x07\xf0\xd9\x01\x1cx\xd3\x07\x0f~\xbe\xff\xc3\xed`\xe2\xdd\xb9s\x0b\xf6'S\xf0\xee\xde\xbe;\x99\xc2\x14\x1fM\xef\x05\xfb\xe0\xdd\xb9\xfd\xc0\xbb}p\x9f\xde\xbb\xf5\xc0{p\x87>\xbd\xb5\x7f/\xa60\xf7\xbc[\xf7\xef=\xba\xe3\xdd\xbdw\x00\xd3\xfb\xde\xfd\xbbS\xb8\xeb\xdd\xb9\x03\xd3\x07p\xcf\x9b\xc2\xf4\xc1\xea\x8ew?\xa0\x9f\x80}\x98\xd2\xcfL\xe8W`J\xbf3\xa9>swB\xbf\x13xw\x0enO\xbc\xe9\xdd{\xde\x83;\xb7&\xde\xbd;\xec\x07m\xee\xee\xcf\x0fh\x97\x1eM\xef\xc1}\xdaG\x98\xde\xf5n\xdd9\x80\xfb\xc0&\xec\xdf\x9d\xf9\x1f\x8d>\xf8\xca_\x9bu\xff\x93\xac\xe0\xf3\xe9\x01\xdc\xff\xe1\xfe\xcfw\x10l\x10\n\x7f\x82\xd5\x97\xe4\xb9\xb8\xc4\xe2\xdf\xf6n\xdd\xbe\x0f\xd3\xdb\xde\xfd\xdb\x0f\x82\x89w\xfb\xee\x03\xfa\xff\x93\xa9wp ~\xdd}p\x0f\xf6\x9fQ4\x98z\xf7\xa7\x0f\xe2\xc9\x81w\xf7\xce\x94\n`\x07\xdaW\xf0Q\xe3\x1f\x04\xa0\x98B\x1f\xc7\x07\xde\xbd;\xf7'\xb7\xbc\xe9\x9d \xfd\xf9\x00\x7f\x1e\x04\xb2\x97\xee\x8b\x97\xaa\xdb\x80\xb7\xc5\xcf\xaa\x83\xf7\xbd\xe9\xfd[1vor\xcb\xdb\xbf5\x0dto\x80\xe8z\xf5\x9ca\x1a\xed\x1d\xf6\x89b\xc2\xf4\x0e]k\xf1;P\xbe\xf2)0AY,\xf7\x12\xf8p\xcb;\xb8\x03\xd3\xfdgw\xbd\xe9\xfe\x038\xf0\xee\xdc\x0f&\xde\xc1\xdd\xfb\x13\xef\xe0\x1e\xffqo\x1f\x17\xf7\xc1\xbd\x07\xe2\x81wo\x7f\x8a\xff}p\xf7\x01\xec\xc7\xf7\xbc\xfb\xb7\xe0\x9e\xf7`\xff~@!\xbc\x83{S\xfc\xef\xbd}:[\xf4\xc5x\xd2\x80\x99\x08 \xfa\xe9)\xb6\x83\xdf\x11\xed\xd2\x15\xec4\xfcL\xf4\xf3\xd3\xce\xfa\xa4\x1fyy\x89\xa9\xbf\xe7\xdd\x9e\xde\x07\x9c\xf8\xc0;\xb8w0\x11\x93\xc6~<\xb8\xf7\x00\xf6\x0b\x9c\xcc{\xfbS\x9c\xc8\xbb8\x91\x0f\xf6\xef\x03\x9d\xce\x00\x97@\xcc\x14\xfb\x81/q\xa0I\x05\xd4XQ\xfc\x14N8[\x81~\x93\xb8\xf3\xe9t\xc7\xd8\xc1\xc9=oz{\xfa\x81\xe6\xfd6\x1c\xdcV\xcd;/\xcbqe\xd3\xfd\x00\xeemo\xffp\xc7\xbb\x7f+\xbe\xe5!)\xba\xf3\xe0\xd9}\xb8\x1bO\xee\x02\xfb\xdf\xd4\xbb=\x9d\xd0\x7f\x9eQ(\x98\xde\xfa\xe1`\xfa\xf3\xbdO0t\x16\xf1~e#\xdf\x87\xe9\xfd\xd5\xed\xed\xe4`5\xb9\xbd=\xf8\xf7\xf3[pw{\xb0\x9a\xde\xff\xf9\xee\x0f\xb7\xfe\xbd\xbe\x05\xf7V\xd3\x83\xed\xe4\xe0\x87\xbb\xdb\xff\x8f\xbdw[r\xe4F\x16\x04\xdf\xfb+\x90l\x9d*\xb2x\xc9d\xd6E\x123\xb3\xb2\xd5j\xe9\xb4\xd6T\xdd2\xa9\xfa\xcc\xce\x90\xacj0\x08\x92\xa1\x8c\x9b\x10\x08ff 5\xd6\x0fk\xfb\x03\xbb\x0f;f\xbb/\xfb0k\xf3\xb2f\xfb\x0b\xf3)\xfd%kp\x07\x107D0\x98U\xea\xd3\xe7LS\xb2\xca\x08\x04.\x0e\xc0\xe1\xeep8\xdc\xcf\xeb\x9d\x1d|\x1c\xc5\x84Q\x18D\xfd\xf3O\x07\x13\x9a\xa6\xfe6\xaa\x9f+G\xfd\xe9\xd9Y\xd5\xa6\xd47\x1f\x9e9\xce\x95\xd5\x87\xe9s\xc7\xb9\xb2\xfa\xf0\xb4\xbaCK\xf1\xc3\xf3j\x13\x81\xf3F\xa5\xdd\x9b\xa9\xba\x9e}\xee0u\xdddA\x80\x9f\x9f\xbb\x82\xedxq\x18\xc6QH\xf9\x8d\xce4\xad\x1c\xc5\xba\xd4$\x9ekP\xd5\x0f\xce\x10R\xee\x91+\xf5\x19\xdeX\x04\xd1\xbb\xf5[\x0c\xd7\x95\xd0}\x8b~\xd6_D|\xc3\xe0\xc3|\xa9S\xfc(\xf0#\xf6*^3rEN\xa6\xa5T<\x0d\x85G\x9d\xbeR\"(\x1e\xba\xaa'\x9d\x8aJv\x86\xa7\xa7\xe6\xc5\xb4x\x9f\xc4[N\x93\x9d\xfe\\x/\xa0S\xbd\xf7\x1b\xe7-\xa9^\n\xe6y=rrE\xc4}\xc2\xe2\x0d\xea\x8c\xfa\xa0\xb1\x19\xc1\xc1qOOWoP\xedL\xc4nIV\xe9\x89J\xa3:\xcd\x8b\xb9\xc9\xe6\xd7\xbb\xa6\x92c\x93\x9c\x056-\xad\x8d\xba\xbd\x1e\xef\xc1\xd5\xc9\x8c\xb3~0gK\x03O\xcaD\x1f\xae\x1e\xfe\xfc\xbe\xba\xa4`\x08r\xf3\x11\x95\xb5UY\xc5\xfb\xc5\xa6G\x84\x15*\x1c\x95j\xb2\xa0tR~\xa9Z\xcb\xfa+\xb80\xc9\x06D\xecx|\x0b\xfd\xfe\x8a\xf3\x98\xf7{\xff\x81\xc7\xd1\x96\xfc\x993\x85\xdet\x15\xb0?\xe3\xa1\xa4\x18\x11o\xc7\xbc\x1b\xb8\x9c\x7f\xea\xa1\x13\x8e\xea\xbd0\x8b\x9f\x18\xabF\x8d\x8cM\x1a\x8c\x88\x02[\xab\xe7!\x87V\xe4\xdc\xb0\xfb\xb4_\xfc6\x98lb\xfe\x15\xf5v\xb9-{m\xd5`sy\x99y\xb4\x84i\xc4\xa6\xcd\x1b\xd7Z\xbf\xbe3+\xc4\xd2\xaa\x10\xc6\xa6\x01W\xd4\xef\x8a\xb4\xde\xf93\x8a\xb8\x82\xc1\x87zj\xaa1\xa1\xfcp\x9dj\x06#\x8d\x99\x9e\xae\x18\xf29\xd5\x91\x16\xedU3\x1eK\xd3~4\x18\x91H\xd3\x89&@\xf4\xa1Z\xb7\xde\x01:!\xb6W\xd6\x94~@\x14\x86\xcea=\xe5\xf5\xa4RZG\xe4\x1b\xb3\xbc?\xe2\xb8D\x15\xbax6\xfa\xa0\xa1\xea\x06\xe2\x03\x06\x0c+\xee2l\xe0\xf7+\xe6B\xd1\xa7M\xe1u\x92 ?H\x0dC\xfe\x15\xf9(|\xbd\x81\xa1?u\x1e\x07\xf85%\xa6%\xb1)D\xfeE!\x01\x9c\x8e\xc4\xa6\x97[&~\xcb\x19U\x14<\xb6/\x0ebZ\xec\xb6\xaf$\xa7nS\xe3\xe0\xba\x9b\x98\x93\xbe\xe9e\x0e\xe1Hk\xfc\x03\x16m\xc5n\x04B\xca\xd9\x08D\x92^\xef\x82\xc4\xe3\xf1\xc5\x80P2\xbc\"|\xce\xe6\xfeR1@\xb6T\x8d\xf8\xc3!\xb6\x84]r#\"-\xcea\x1d\xfa\x8f\x0b\xf7x\x9a\x03>\x1c\xfa\xe4\x92\xc4\x17\x03\xd2\xc3\xa5\x80\x8e\xf3m\x17\xc85\xf6\xaa\x80\xa0\x06\x19U\x16s\x0ej`\x9a5\x8c\xc1Q#\xf0\x91\xb0s\xb2\xa3\xa9\x0bC\xd5\xa7,b\xa9G\x13\xf6j\xed\x92=U\x0e\xce\x92\x80z\xec\xabH\xf8\xc2g\xa9K\x12U\xd9\xb0\x9a\xdf\x8b0\xa8\x8b\xa4?\x17\xb4\xfa\x19J\"?e\xb1`o!\xa6\xd5a\xed~\xef2/\xf3rQ\xd8\x88\xbe\x1f\x95\xeb\x03\x95QG\xb2\xd3\xbb<-\xd4\xda#C\x92b\xf6r\xed\x1eR\xc4.5\xb2\xb9Xj9\xeb\x9a\xf4.\x13\xce^^\xaa\xe2P9\xed\xc3g-\x17\xc0u\xe6\xcbS\xf8zy\xaar\x16\x00 3\xd2\xebR\xb02\x0e\x1b\x16y\xae\x85=R2`\xe0\xe2\x0f\xdeH\x91F\x08\x1d;\x17\x8ekjkX\x1b\x8e\xc305\xeb\x93\x80F\xdb\xef8\xdb\xf8wu\xc9)Q\xe4\x9a\x86\xa9K(Q\xdf\xc1\xc9\x0c\xf8\x9f\xd1\x19'i\x12\xf8\xa2\x7f\xbaH\x87\xa7\xdb\xc1@\x87\xf2\x86H\xde\xbc\x1f\xe0\x12\xc6\x1e\xbe\xf5\xb2T\xc4\xe1\x88x\xf3\xb3\xe5\xc0\xfa\xb1p\xe5\x99\xab,\xcb\xca8\xd4\xed\x17U7\x1f\xe3\xd1\xe3U\xef1\x19\x92\x1d\x0c\xbb\xdf\x8f\xfb\x9b\xc1@\x8d\xf8\xe3\xde\xe3R)\xa7)ia\xc6\xd5\xbc\xad\xd5L\xc1\x0c\xf6\xa3\xc9\xce\xdf\xee\x02\x88p\xf4\xe8\x11)\xbcj\xc3\xd5B\xca\x88\xcc\x133\xd90\xeb\x1e\x15}o\x80n)\xfa\xf6\xd3\xa0\x15\x83\x1c\x88\xa1\x87DK\xeb\xd9d\xc7\xe8\xda\x8f\xb6\xb5%\xd8\xbabv\xaa\x0d@\xc7\xdd\xb7l\xcf\x02\xecb\xb95S\xf1\x91k\xd1Yum\xad\xef\xbap\x00c\xda\x1bM\xeev\"\x0c\xfe\x98\xc1\xb1\xed\xe5\x8e\x93\xd3\x97=X\\;\xfe\x12<\n8\x87k\x95\x05\x01\x13o\x03?\x15\xdd T\x168\x08S\xa1\xa2#G#\x0b\x9a\xa7\x13\xea\xf3\x05\x0b\xbbC\x17\xf8\xd5Y\xca+\xa9A\xd6\x0cU\xe0\xd7;\x19s%\xaa\xad\xdd\xc3\xd5&\x98\xaa\xb9v2\xc0\xdee\x1c\xe8e\x03\x95\x93\x97dJ\xae\xc9c\x92\n\xca\x05\xaeP\xf3 \x96&FTu#L \xbc#'!n\x99\x04E\xb5`[\xdf\xa9\xcfE\x06!\x80\x0c\\\x93\x1e\xa2bR\x9d\x99\xbc\xe6N\xe0\x9a\xe1<\xe9\x17jW;\xe659\x07\xe1\xf1%\x05\x1b\x10\x03\x07R*\xce6\x06\x06\x0c\xf3\x15\xbb(\"\x8c\xc1\x11\xcb\x8cV+\xf0C\xba\xed\"\xb2\x9b\x01|LR\xee\x95 M\xb9\xa7\x01\xad\x8fS\xf6\xd0!oX\xbd~\xb85Q\xcf\xfa\x8f \x0d\xf4hc-4P\xf3\x80\xcc\xd5$\xa0]1.\xe1\xc7\xbd\xc7\xeaO\x86\xeb\xbfH\xbf\xc9i\xaf\xb0\xd0+#\x04\x11D\xbb\xd3C\xc8^'\x16X\xcb\x113\xd5T\x8f\xe2\x81G@\xa3\xb27\xd5r\x0c4\x0d\xf5\xac\xe2\xf5\xfd\x11\xd0\xa8\xecM\xb5\x1c\x03MC=\xfc\x08Pxm\x9e\xf9Q p\xd7\xa8v\xa2\xd8\x1d\xb8\x94\xd8i.E\x03\x7f\x1bi\x0eu\xaf\xd6\x8d`wb\x0c\xa93\xa43\x98\xa3\xca\xac\xea\x90\x1d\xd3\xb7]\xad|\x1d\xe5\x1e\xda\xb3\xf5G\xee\xd9qh\xbc\xae\x96O\x05\x8f\x1d\xa2jc\x15\x98\xbf\xa1\x96# q\xd7s\x8c\xe0\xc5BG\xe9# \xa8\x97_\xb3\xa0{\xf3k\x16\xb8\xca\x1f\x01\x80\xa3\x06?J\xbbC\xe0G\xa9\xab\xfc\x11\x108j\x08)\xaf\x0b\x15\x8d5\xa8\xdc\xce\x1a\x8e\x00\xc2UG\x9a\xad\x0e\xad\xb5\x1c#\xb3U\xf3f\x1e>V\xebN\x8e\xa8;i\xab\xbb&`\xee(_\xaf\xb4.\xf1\x90D\xa1\x1b\xa9\xec\xa4Vj'\xb5\x88P\x12\\9\x88l\x1ao\xc4\xd1M@\x81\x94\\whM=\xd6);\xbb\x13\x1d\x07\xad2T\x95\xf1\x11a`N\xcb\xbaTV\xac\xaa^\x93\xa0\xdb\x0f\xae\x87\xaeVu\xae\xd9R\xd3\xe3KU\xe2\xa0\x14\xf7\xf2\xb1\xa3\x99#\x16\x85\xca_SB\xc5\xb1\x88b\xc1\xder\xb69\x04\xad\xe1D\x7f\xc8\xc2\x15\xe3\x08\x9f\xbf&C2\x1dLD\xac\x1d\x938N\x97\x95\x88\xdb\xdbD\x9cm\xc0\x10\xdb\xc9\xc4P\xea\xcdV\xdf\xac\xc9Kr\x06G\xa6\x9c\x0c\xafHof\xf5\x0c\xf0u0\"\x8f\xd5\n2\xea\x1f\x03\xffX\xd5\xfe\xd2\n\xfd\xbf\xdeD\x8fuL\xdf\xc7=\xe2\xaf\xaf\xac\xc4\xff\xb8\xf7rn>\xf5\x96Jxw.:;.\x80Y]wD\xba3eI\xf8\xf1\xe5\x8eW\xc1M\xc7)Kz\xb0N\x14\x1fn\xce\xa22\xc0\xec_\xa6\x0c\x9a\xaeeSY.\xe3\xa0^\\m\xa1\xa1|k\xcf\x8e\xc0\x9f8PM\x9dj@\xeaT\xc4\xd6|\x14\xea\x07>\xcc\x0fNX;j\xe1l\xd6\xa6\xde\x17,\xac-\x0e\x0b\xcc\x11\x1dt\xe9Kl=4\xf2v\xf1\xc1CE\xb3Fr|o\xefR\xd7\xc5\x105-\x06\x92\xe3|\x01\xe3\xabC\xb4\xa2\xde\x0d\xac\x90\xbf\xfe\xaf\xffM\xe1|e\xb0\xd6\xc7\xc8(\x0e\xcd\xd9\xfa\x08\xcd\xdbZ\xd4D\x9c#\xf6^\xeb\x9a\xb0\xb9>N>rC\x7fL\x0d\xc2Q\xc3Q\x02\xf3\xba\xb2\xe9+\x1f\x03\xa5\xe4\x8ad\xc5\xf3\xc3.\xcb\xa8_\xe4\xa4\x84\xf5]\xc4\xa9\x90}8\x8c\xc8\xcb+\"\xf4\xe9\x1a\x19\x93s\xc5\xc7\x15\x9b.+\xcaP\x13\x05\xd6\x07F\x0b\x85/FmU\xd2X\x89\xb9B\xbf\x82\xc6\xea\xac\x9c\xac\x99\xa5iU\x15\xafh\xcf\x8a\xf5\x9c\x97\xda\xd4 Z\xab\x85=Tip\xc5\xb9\xd4\xcf\xf78P\x03ri\x8f\x0f\xa1\xa9\x8a\n\xd5*\xd9\xecya\xaf.\xa7\xe4SS<\xa8\xcd \xf5\x03\x0f\xfa\xea\xc6]1\xb9\"\xf3\xda\x94\xcd{@\xa8{\xe8\xdb\xff\xec\xf9\xc0q\xf03\xef)\xden\xb2\xbcpg\xe1l\xc38\x8b<\x08\x13\x0f\x19?ug\xd4S\xaa3}\xe6\xced\xe9\xa2\xa0~`\xf2~\xde\x0c\xdc\xb9\xce3=k\x82\x0e\x8e-C\x16 \x03\xdft\xea\xce\x9a\x86\x94\x0b8\x06\xb49\xcf\xdd9\x03?\xba\xf17\xf7&\xd7\xd3\xc1\xb2\x94iy\xc4q\xbf\xc3z\xaahd\xc5\xcb\x84\xdc\x1ej+\x92pvA\x18\xb9$\xb1F\xc6\x0b\xc2\x86\xc3A\xa1\n\x8c$\x12\xcf\xd9r~\xb6\x1c\x11x\x98.]\xa6W\xc5\x03vm\xe5Q\"\x10.n\x84Gi.\xf8\x04\x9a\x02D\xe66X\x01\xa2-\x13\xdfg\x01K\xfb\xbd\xde``\xe1\x16\xe4\x92D\x17D(\xf0\xf9\\,\xfb\xac\xd1\x84\xe3\x03n\xc3\x95,A\x1a\xbb\xc6\x8a\x160\xd7\x84i;\x17\x1c\xcb:\xe1SC6\xb3\xd4\xcae\x01\xa9\x830\xb1I\xca=s\x88\xde?]D\xa7[\xbc\xf6:\x11\xdc\x0f]\xe2m\xc0\xf6,p\xde\xdeRm\xa532?\x1b\x91\xa9\x03?\xf3\xbb\xd8\xf32^\x82CWm\xc2h\x0c\x8f\x14X\xa3\xa2\xbd$\x9b\xb0h?\xb2\x1d\xff\xd8\xc6\xafO\xab\xb6\xaa\xdaJ\xe6y\x93\x91\x0c3\xa7\xb6\xbe\x0b\x0b)\x9c\xe6\xa6#\x12\x8c\xe0\x18\xbb~\x04\xfd\xec\x9c\x9c(\x82<\xf1v\x94\x7f\x19\xaf\xd9\x17\xa2\x7f\x96\x9f\x17\x8f\xa7\xf5\"\x9fO\xebE\xa6\xedE\xb4G}f\x1d\xe4\xf7\x96\xb3^{\x11j\x96x\xa1\x8b#2_\x0eF\xa4\x9f\xc1\xd5b:\"S\xe07gDJ\xf2\xfc\xb3:T\x19\xc8}\x8d\xcd\xc0r\x0c\xc8\x15\xa1\x93$N_\xd1\xbb\x11\x8a\x01\x8a\xc1]\x90\x94\\\x92@\xb1\xb0\xe9\x19\xd4L\x01E\x0b\xb5\xa7\x83\x0b\x92\x0e\x87naR\x873\x0c|\x8f\xf5\xcfG$\x1b\x8c4[\x86C}\xf3\x05\x9a\x1a\x91\xd4\xa0\xb9Y\xf4\xe4\x9a\x8c\xa7dF\xfa>l7\xd9\xde\xa7H\x07\xa5\xac\xa7)\xda8\x18\xe9;\xd8\xd0F%\xc7\x1c%Xo 2m\xe3\xc7+\xb2\x19(X\x1c\x14\xb0\x1bq(\xd0=\xf0'\x82Q=p\xa1\xb8\xccF\x0b\xb4\xa4~\xc9\xd8\xd2\xca)\xd2J\x9aKM\xd3\x12M\xac\x954\x0d8\x85*Z=\xde+\x89R\xd4\xca%\x8dR\x92\xaa\xc0J[.a\xcf\xfc\xa0\x03jY\xd3\x82\xc6\xe2\x82\xf0\x82pt\xd2\xef\xab\xf5\xed\xf7\xf9\xa8`R]\xa56\x88\xe3\x83\x8b\x01\x10 \xaeQ'68S\xb7\xd40\xbfb\xc3\xaa\xe4(o\\\xe1Q>\x14 \xde\xa1=c\xde=\x9bx\xc8[\xef/N\xf9\\6W\xcf\xa6U{B\xaa\xd3\xab\x86\xf8h\xed\xff\xec\xfc\xccIA\xd3\x9c\xbc\xd4\xccp\x14t\x9apB\xe4\x80\xf5\x88\xecFd?\"\xe1\x88l\xbb\xd1\xc5\x03\xa4\xf4\x01t1\xa8\xd3\xc5\xd4\xd0E\x0f\xe8b0\"g\xedt\xd1\xeb@\x17\x13rE\x02K\x17\x15\xd1\xf2\x90.n\xc8%\xc6p\xe8?=G\x8a\xb6\x86\xac\x15\xea\xb8Ac\x9c)R\xa4\xf5\xe0\x82lj\xb4\x12\xc8\x80\xaf\x00\xde\x1c\x80f\x0fM(\xc1R\xc7m\x1ca\xfc)\x03\xa4\x82px\xa5(\xc3G\x04\x0fZ\xb6\xf5\xed`\x1c7\xea\x91\"\xc8\xe4\x9a\xf4\xc3:`\x16(%O@\x86^\x0fSw\x83\x02|\x1a<\x07d\x17\x03\x05\x8c\x93\xad\xd8\xd2\x9a)9J[\xde\xb1U\xbc\xacoX\xcdtD\xbcA\x99M\xa4\x93|s2\xdf\"w\xa8\xa6\xb9.\xbe\xe8\xb8\x9c\xa1\xc3\xe4\x0d\xfc?\xecK\xe9\x8a7m>\x1eS\xf1[\x99\n\x10\xccB\x17\xb4\xc7\x8eR\x92\xb6\xa1>\x92\xff\xf8\xc7\xf3\x9f\"g\xf1\x1b8K\xce\x99\xfc\x1agr\xf2\x1f\xffh\xfe\xe3\x1f\xe2?\xe9/\xc4\x7f\xfcv\xfe\xe3\xbb\xf8\x8f\xff7\xe5?\x0fA\xc1F\xfc\x83\x01\x8fpw\x07n>\xec\x0e.\"\x97\x84_\x90H\xed\xe0JX\x01\x08\x16\xcf\xa3\xe5\xc0\xce\xba\x99\x07\xbd\x03\x11f\x00]\xbb\x10\x91{\x8b\xfb\xd7\x1a\x0d\x90\xcaK\xdb\x0c\x18\x80\xfar\xc2{d\xb5\xf4\xa4b\xf8LJ\x0b\xd9\xaa\xd5\x816\xb1\xfc\xa2\x9a\xddx\xd6B}\xb5\xe8\xdfz\xc5c\x17\xa4\x06\x85\xf5\xc7\x8cB\n$t\x85\x8b\xe6F\x1cF2\x0f\xe8\x8a\x05#r2\x053\x1cGUE\xfdV\xb9\xae\xe9\x88$Z\xce\x0e\x14IMM5}`'z\xfb\xcc\x06#r\xb2\xa9^$\xd2\x93\x9d\x0f\x05\x18%\x0e\\\xdd\x04\x04\xa4\x96\xe4\x95K\x8c\x0en\xd6I\xbeaw\x9c\xc348Q\xd1\xdbpo8\xac}\x06/Q\xb9\xb2\x83:\x15\x1an0\xa0']\xe0%\x0e\x98[\xa0%\xfa\nmK\x90\xc3\x96\x0e\x11\xdd)\xdc% *^\x93>lG\xe7\xcbAG8+\xb4\xbf\x19\x12\x81\x0eh\xda\x82\xcdv\x006\xeb\x08V\xa3\x8e\xc6\xfc\xac\xae\xc6eEh~\x06\xa0\x96j\xac\xfa\xa50\x8c\x1f\x0c}\x95U~\x8cQ\x1d\x8f\xbd\x06\xb8\xe0\xe2\x8a\x82\x1eh\x02\xd0&\x886\xab\xd7x\xfei9\xc8\x97]\x91ji\x83\xf5l\x80\xf2\x8c\x9b\xd3\x9b\xdcs[,\x97@\xac\xf6<_$q\xd2\xcf\x03\xbe\xc4\xf9\xbe3\x8b\x04\x9cg]\x17\x13fJ\xac\xe1\xa8%\xe5p\xa3\x87p\xb5\x1c\x1f\xba\xe6\xf0\x98\xee\xe1\xab\x0e\x0e\xd6Z\xc3|\x1b\xccj\x98\x12\xb7\x14\xe2#G-\xf6\xc9\x1ft\xa3\x84\xc4\xd1\xcbC\xb8u\x10q\xea4\xb2\x96\xd2\x0567\x95n\x83\xae\x05\xb2\nT\x1f$W\xd9d\xbb\xbf\xe6\xcd^\xfdruo\x7f>\xee\x0f\x16\xf3\xc5\xf2\xe7\xf7\xc3\xeb'\x93O\x16o\xe4h\xf6\xeb\xcb\x93\xc5b9\x00E\xf0b\xf1\xc9\xb4\xf71\xf6\x10\x0ey\xa5\xb8\xbb\xef\xb0\xb7()\xcf\x1a\xb6\x0dy\xce\xef\xd9\xf6\xab\xbb\x04\xc4]\xb8&\xd4\x7f#\xe7=\x08\xd2\xb8\x88\xfa\x83\xf9\xf2\xf1\xa27\x19\x9d\\\x8f{\xfafO\xaf\x87\xc1\xb7\xb8\xb9\xdb\x83\xa6\x82\xcbA_\x95*_t\xaeC\xd31n\x97\x9d\x804[\xa5\x82\xf7\xa7\x0e\xbc\x1cL\xd2\x98w\x0cN\xaa\xeb+\x9ck\x9a\x13@W\xbd\xa5\xeeI\xec\xdf\xa0\xff\xc9\x03\xc7\xa5g\xe4\xa3\xc2h\xa3\x82\x04_\xfa\xeb\x11\xe9m{j\xe7\xbb\xb1\x92Q\x9e\x17E\x933$\x98\xbb\x92\xc0\x1e\xa3\xc0\xee\xa6+\xd5\xed\xdd\xce\x9c\xd5\xba\xf3\x93\xe2\x86\xb2\xafH>\x14\xb0\xd2{eo\xf9\x12\xe8\xb2\x18\x8f\x9bk#\x06\n\xc1\xee\x84\xdeLP\xbd\xd9\x1b\x1c\xdc\x1b\x9a\x9f\xd5\x80\x9f\x8d@OF\xf3\xdd\xc6f\x12\xd0T|\x13\xad\xd9\x1d~\xf7\xb4\x0c\xb7g\x81\x11\x8d/@|\xdfL\xd8\x1d\xf3\xfa\x19\xe8-\n\xa5^\xa2\xfa\xfc \x95-\xfe4e\x83N5\xd3\xd9\xe2\xcf\x8a%\x99\xde\x98\x06#\x92\xa0>\x8d\x0cI2\x9f.\xf5\xe0v\x08EG\x0e\xf1\x99\xe2\xef=\xb8q>\xbeo\xd6L\xadc\x07\xb5\xb6\xc5\xb1\xde\xb5\xb8\x91\xcc\xcf\x97\x1d\xa2\xe7\x91\xc3\xf2b\xf1\xf7\xd0\xee=d\xeaT\x0f\xba\x15\xf9\xdb\xcc\xce!>_\xfc\x1d\xe0\xf9\xc5\x9f\x82)\x80\x05\x93/\x921I\xe6O\x0d\x8a6\xabR\xcc/-ho\xfa\x01\xb9$Y!\xe1!\xfd}\xc8t\xd9\x95\xf6K,\xa9\x12aT\x04\x0d(\x8d\x91\x98}\xdd\xf4\xd9\x08\\\x1b\xa4#bR\x04\xea\xb4\xdb)\xe6\x07 7&\xd5\x1cZ\x9c.\x86c\xb9\x98,&rq\x8d\xff\xc9\x93\x93\x93\x139\x1a\xc9\xf1\xf8\xb4~\x98q\xba\xe8\xf7=)B\xc9e2X\x0cN\xb7~\xfd`\xa3>w\xde\x8c\xf4\xfe\xfb\x7fsL\x11W\x1f\xfe_\xc7\x87D}\xf8\x7f\x1c\x1fD8#\xbd\xbf\xfe/\xffw\xaf\xf4\xa5\xc1\xda\xa6\x8b4\x95\xcbQ.iIk\xab\x8a\xbe}\x1a\xe4\xa5\xd2\xde\xa8\xc8\nS\xcd\n\xd3&VXc\xc4v\xd3\x94v\xe7\xc7\x19)\x97;\xcc\x96I\x91\xed*,\xcd,\xdb\x85\x95 gQ9/U\xafx\xd0<\xc8Oz\xfa=<\xa3\xb9&\x01\x99\x91\xc0J\xc3\xf1\xa8\xdd\xf6\xac\xfa\xd3\xd2\x97?\x17\x13\x11\x7f\x1b\xdf2\xfe%MY\xbfbtS\xfc\xa9e\xc6'\x82\xa5\xa2O\x07\x16^Z0\xbf\x18\x8eA\xec\xfe\xef\xff_oPH\x9d\xfc|>z\x0f\x1f\xfe\xfa\x97\xffZ\xfc\xd2\x9f_\x9f,\x07\x7f\xfd\xcb\x7f\x85\x8f\x9fL'\x93\xfa\xd7\x9f\x9f\xe9\xb2\x9fL\xd5\x7f\xc5\x0c#[\xef\xa8T\xee\x8d\x9c\xbf\x19/\x07\xe3\xf1\xb8\xaf\x1e\xe4'\x83\xd3m\x085\xfc\xf5/\xff\xfb'\xe7\x95\xbc\x8bt0\x1e\xf7\x17i)\xdb\xffV\xcb6\x7f3^\xa4\xaa\xd2>>\xd5\xb3\x83\xff\x96\\mM?\x8an\xd5\x12\x8d\xf9\xe3\xde\xd2E\x1c }[\xa7\x08\xa7\xf3\xf1\"\xc5\xdd\xd1\xf2\xd4\xb5\xc3\xa2m\x16\x8a'}a\x0e\x02\x01\x7f\x8d`\x0e\xd3~\xe2#\x120\x85\xbc\x85N\xd6\xdb\xc8\x0e\x98^\xdb\xad\x04\xd0em\x10k\x13\x914WF\x91<\x80\xde\xf8\xceM\x9b=\x92\x1d\x91\xfb\x11Y\x8d\xc8\xdb\x11\xb9\xfd0\x82t\xab5\xbf\xab&\xc2\xb4\xd2\xc4`u.\xc5\x9a\xccFaK\xaer\x88a\xe8\xb60tx\xfct;\xdf\xea\x9c\xe4\xf2\x8al\x06\x17d;\x1e\xb7\x9c(\x99_a\x0c\xb6\n\xb9P\xae\xd2\x9b\x14\xd8_\xd9\x15<\xe8,[\xb1\x19v\xe1\x82(\xc1\xca\x03\xc2\x18\x97vAz\xe3\x13\xe3\x86\xc7\x1f\x0c.\xda\x87\xd9\xfc\xc0\xd7\x07\xb9\"'\xb4\xafPX\xefN\xc6d\xaa\x05\xc2\xd4\xeeW\xa6#rO\xaeH\xef1NL\n\xa6\x89\xa0:\xc0\xb2\x01\x1e[']\xe6\xc3\xfcT\xeb{U\xc3zDB\xf57\xe9\x06\xb5\xf9\xc1\xa0\xb4\xcdc_\xcd\x83\x9a\xcaQeJ\xc9f\xa0\xa7\xf4\xa8\x06\x89\x06z7I\xfdh\x1b0\x18\x8a{\xd5R\xa1r\x95\xb69f\x18\x8a\xbf\x1c\xe0{rM\xfao\xe7;\\j\xc5\xe3\xca\xcc\x91<\";\xb46\xc8\x89 Z\xc4\xce\xcf\x97\x15\xb6\x91\xf5\x0b\x02\x80\x9e`G\xb9\xa7K\xd0&\x7f\x0c\x10\xce\x1e\x08\xc2t\xa9X^qI\x1d^+\xae\x9fj\xca\x8f2V \xbe\xd1\xe5WW\x836\xfd\xf6\xe4\x9a\xdc\x1e\xb3\xcf1?\x18\xc5V\x1d\xb4\xeb\x97\xc4\xe9\xcc\x0e\xddQ%\x11ug\xc4\x11\x07\xbb\xed\xa7\xf7J\x9b\xce\x85\xc0j5T\x8b\x03VH\xff0\x02\xf4\xfe\xfa\x97\xff\xe2\x8a\xa0\xea\xfa\xbd',H\xd9G\xad\xfa\xa3\xee\xc1\xc0\xc0\xbc\xea\xf8\x15\xe4\xa9\xdb\xdb[\xf9\x1b\xb9\x98-N\x17\xa7N\xb9\xc9o\xd4L\x9f\xbe\xb9\\\x9c\xd2E\xfa\xe4\xe5\xa9\x91\x90\xda\xc5#Z3^7F\xe8s\x87^CX\x0b.7\x06\xab\xce&\xe82\xaa\xf9\x9c*\xe3\xc1\x8c\x9c4\xc4\xae`!\xf5[>\x8b[_\x08\xc6\x9b+\xd7\xf2\xf2\xd7Q!0g\xd3\xdd\x16\xf3Ko}\xe1\xed\x14\x92l\x99x}\x9f\xb0\xfeA\xa1\xc1\xa3)#\xbd\x8c\x07\xbd\xd9Add\xc7\xacy%\xb2\xccH4\x81\xc8dl\xfd\x9a\xddu\\\xf60\xaa\xd0\x83?\xf1\xc0\x11\xf9\xa6\xfak:w*\xfe\xe0\xc2n{6\x1c\x08\x98\xb5\xbf\xaf\xa1\xe8)\x90D\x0cjF\x18\x96\xafTB\xbf\xb0\xa3z\xa3s\x9c\xfa\xa3\x92[\x9b\xa6\x9f\xe3\x0c\xcc~j\xfcb63Sg\x8ez\xb9\xea\xb4\xe8\xf2\xf5\x11\x0b\xfc\xe8&\x9d\x11V\x1f\x12\x9a\x89X}U\xcb\xa4\x1c\x93\xda\x15L\xea\xd8\x8d\x0co:\x80*\xeee\n;\x80:|jg\x12eA\xab\xe2E\xdf\xc3i\xd8\xe3\x14,\x95\xee]\x96J\xce\xb1\xaemk\xee;\x1e|\x14\xb6+\xa0o\xb9\xffX\xe7\x1f\xb9\xdb\xa0\x1eXD\x822);\xea\x14\x04\xea\xd1\xb7\xd0\xb5\xdc\x9d\xabr\xb6 \x9f[Vw\xfa\xe6\x92\xce_.\xd2\xa5a\x0d\xdb\x01\x1a\x87\xea+\xa3\xbb\xf1xD\xfc~\x9a;\x18P\x89\xc3\xe1@\xc9\xc6\x90\x0bR\n\x9b\xaf\xbc\xad\x18k\xcc\xcbv\x01\x9e\xe8\x0e\xac\xe0\x90Q\xc9\xf9}\x85\x1b\x14.\x13(\xf4F\xa1\x7f5\xc91\xda\xee:l\xaf\xf6\xa5=e\x08\x05\xfb\x81\x82yo\x15\x06F\xbc;L\xf1\x88\x99tOo\xa3\xd7\xd0\x9a\xde\x11np\xc7\xba!\x97\xb6Y4\xbe\xcdM\xdf \xce%\x15\xec[\x05\xc6~\xbeYN2\x1e\xa0\xa6J\xdb%\x1b-\x1a|\xd4;T\xf5Y\xb5\xb4\x1e\x11\xef\x18\x12I\x1e\xa4\x0d'E\x8dx\x90\xab\xa5\x93\x8eJq\x92\x0b{\xebN\x05 \xb2\xc0C;f\x1d\x8c\x1d\xd1;m\xcc\xab\x87\xbf{9}`\xd5f&T\xfd\x99\x81\xe8p.E\xb4\x02\xf3\xa1#\xf1\xd0)\xb6\x98\xd6\xbd\xec\x91\xd3\xfb\xf0>\x15h\xe0\xd1\xd0\x8d\xc7\xdd\xe1\x0b\xd0\x92\x1eP=!\xc3|L\x0c\x91\xe8 \x0e\xa9_P8\xb4zh\x9f\x1f:\x8fG \xf2\xd1\xf3w_9\xbb\xcaJgWY\xf9\xec\xca\x1b\xd9\x834}vu\xb0\x9d\xf6m2\xee\xd5\x0eV\x82\xe7\x1e\xe3\xf1\x05pI\xadM9\xb9\xb2\x14\x9a\xe0\xadmC/\xe0Sf\xac\xd7/\x06\x8a-\xdb6:\xed\xe0\xf6:(\xe2\x88\xf89z\xc4\xfa\xe6+\x1a\xc0\xd9\xe2U\x8ew\xfa\xe4\xa4\xdc\xa1'\xe4\x0b\xcb\xc7&?\xa6\xd5\x8fg\x93\xe9\xf3\xc9\xd3Jj5\xd3\x97qr\xcf\xfd\xedN\xf4\xbd\x019?\x9b>'\xff\xcc\xd96\xe6\xf7\xe4\x7f\xa2^\xbcJ\xc9\xe5\x96\xb3\xedo\xd4?\xe3\x1f!e\xe2\xc5\xe1\xcbj5\xaf\xbeyM\xbe\xf5=\x16\xa5l=!\x85\x18\x86j\xdc\xd28\xe3\x1e\x83X\x86\x01\xe6IOC_\x8c\xf5\xcb$\xd9%\x07\xa0T\x15\xa6\xb3\xd3\xd3\xad/v\xd9JAp\xaa B\x80N\xdbF\xe1\xb4\xf4\x0e[\xd1Q\xd9\x80\xbd\xddF(\x9e\xfcI\xf8\x81q\xb0\xae\x9d\xe2W\xac\xc4\x9c\x02v\x9c_\x94v\x9fe\xc6Q*x\xe6\x89\x98\xcfH\\_\x88\x19\x0fR\xf7\xb6\xb5eG\x9b\xeff\x1d\x1f#v\xfb\x1f\xfch\x1d\xdf\xba?\x97\xb7\xda\xae\xcay\xa6\xd6.\x9b\xe9{3\xf5\x1c\xc5X\xac.'\xd0\"\x0c\xbe\xa3\x14\x9d\xf8\xe9\x97A\x9c\xa2\x13\x9ck\x18\x89WT\xec&!\xbd\xebGj\xaf2R\xd2\xfc\x0cvK#\xa2\x1d\nT\xfd\xd5\x17\x7f\xa0KC0\"\xe1\x8b{\x0b\xc51e\xf1\xeeV\xab.\x86\x98\xcb\x8bfz\xf5N\xf0\x07\xc1[\xdbP?\x0dJ\xd0\xb2OGX,\xcc\xce\x8cnV\xa5\xe9\x04\xb7F|\xb5\\\xef\xddX\x8d\xc0w\xc1mc\x8c\xa8\xb1\xfaU\xbe\xb6\nj\x0bf\x02w@\xa0,\xc8\xf3=\x94\xfb\x17\x1a\xe8\xa8\x03] s\x15\xef\x02#,=\xf74\x14\xc1\xb7j8bb\x19\x95\x93'\x1e\x0d\x02\x13%FS\xe9\xc1(\x8f\x86te\xa3! rM\x04\x99\x91\x13\xbco\n\xbe\\\xec\xe8\xa0V\x08\x8c\xc7\x05\xf1\xa3T\xd0\xc8S\x85\xe2\x89\" \xaf\xe9V\x15.\xfa\x83\x9a\xd9\xd1}m\x89R\x7f0Y\xa9\xa7>+\xfaY\xea2\x88%\xd23k\x16\x05\xcc\xcf\xa8V\x01\x86\x9c\xbc\xb6\x0e'\x83\xcd\xb1\xa3\x94 \xe0TH\x9a\xe4\xd0\x0cF\x8e\xb3\x0cw\x17^\x15i\xf8q}(\x90\xffc:Q(f{QH\x9b\x141\xbf\x99T \xcb\x85\n\xd5c3\xa9\xd5\x1c\x18r\xc2ssV\xcb\x91!\xb3~k\xce^b\xc2P\xa4\x90\xe2&.\x83#f\xe6u\x81q\x1e719\xcb=f^\xf2RvZ\xbe\x80\xdb\x11\x85\xc5\xd2<\x1f\x05\x81\x05j\xb3\xef-\xc3me\x14l_\xbf6\x17(\x88,H\x05\xcd\xfbQ\x83]Jy?\"1p\x99C\x9e\xb3H>n06}\x81j\xaa~U\xc0\x1c\x19t\xd6\xbe\x7f\xe2\xf2\xaa\xfd9\xcfPIS\xb2\xabS\xfa\xa4\xabTp\xea\x89WL\xec\xe2u\x07d\xc0\xa0f=S\xae\xd7\x05\xe1Ph\x9e\x1d\x1e\x04R\x94\xc3\"\xe2G*\x9b\x98\xech\xfa\xc7\xdb\xc8F\xa3\x8fP\x14a\xf3hI\xd0#X\x03\xfb6\xb8\xd8\x05Fv'X\xb4\xee\x08#\x80\x87\xf2\x1f\xcb\xc5\xfbf\xe4\xaan\xe7\xde7\xdc\xcc)m\x15\x1a\x16\x98\x91\x18AW]\x1b\x9b^a;\xd1\x1b\x00\x93*\xa4\x90\x0e\x13L@\xde)\x14\xd2\x81F\x90\x99R\xbe\xcd\xc01V\x83\x843(u\x01\xc2\x03\xb6\xce\x0d-\x81\x07q\x19\xe9$\xcd\x12\xc6a\x01\xe2\x0d\xe95\x0b\x98`\xe5\xae\x8c*;2\x8a\n\x84\xa8\xd3\\\x07\x81\x9f\xa4~:k\xdd\xa2\x17\x7f\xd6\xa4K\xebh^b\x90\x04\x98\x83(\x0b\x02%VD\xe4\x9a\xf4&\x93\x9e\x12~1\xbc\xa21\xf6Rl\x1f\xf4\xfcc\x12Y\xd5\xf1\x90D] \xb6V\xecvDN%\x0f\x7f\xc19\xbd/x\xe8\xd25\x0c\xf2\x8e\x18eq5r\x83\xf9\x15\x96\xa1\xdd\xeb\xb0\xceG\"\xc4\x9c\xbb\xc0\x1aU\xd2\x95m:j\xc5\x87q\xfd8\xcb1 p\xff\xe5\x8bh\xfd%MD\xc6\xd9\x11\x03s\"&\xdb ^\xd1\xc0\x11\x9e\xf1\xcfP\xed\xf7l\xcb\xee\xfeL\xc2,\x15dG\xf7\x8c\x88\x1d#\x8f\xb7\x8f\xc9&\xa0[\x92\xb2Z`F\xf3\xcbG\xac\xb23\xbc \xb8T\xc1@\x8a\x81\xcf\x00}\xb9\xb9\x80\x1f\xf1\x08\"\xe9\xad\xd9\xdd \xdf7Eh\xbf\x82\xe1(\x8c9\x94Jl\xb5\xdf\xb2\x1b\x8az#Pw}\x84\xeb\\\xc6H\xb9Wf\x99!}\xec\xe3m+W\xdc\xdc\xdb\x9d/X\x9aP\x8f\xc1\x08\xce\x08\x04dr\xec\x0f\x8a\xfa\x8e\xc3\xdb\x02\xb7\xde\xc5\x86+\x8d\x18W\xa0\x1a9#O\x90\xb2\x98\xf2\xfa\xd5\xb7\x9d\xf0\xcanw\xbb\x80V\xdc\x96\x08,\x86\xa1UE12\xa5\xf95\nb\x95\xe6\x8eiMJ\xd2\xeb\xc4\x81S&\xbe\x10\xe5\xbdb\x87\xbbkzC\xa3J\xa6\xfd\xc1\x9c-\xf30\xba]\x1a\xdd\xd6\x1b=\xba\xc5.\xed\xe8\xce\xa5]\x1a\xaa*xtK\xad\x0b\xa9\x82\x829\xfeu\x01n[\x07\xae\xcb PU\x06d\xe8\xc2\xebU)\x0c\xae\xf9\xb9G\xe4K\xc5>\xbb\x8cH\xb1U=\x92\xfd\x1e0\xdf^M\xc3I\x1a\xe4\xbb\xf5\xbass\xb9\x9a\x0d\xd5hf\"\xa0\x82\xfe`\x94\xc7^\xac\x10\x14\xd4\xaf\xe9\xb9\xd0\xdc\x0bo\x11D\xe0\xf8\x1d\xefDr\xb5\x13W\x94\x17\xef/\x98\xc4\x0b\x98\xf4l\x92\xee\xfc\x8d\xe8+\x12<&\xb8\xed\xf7QrP\xdc\x9c\"\xc1l\xe2\x88n\x1c\x9d\x189\x85\x16\x03\xcfu\xc5\x0e\xce\xc2x\xcf\xfe\xee\x07\x8f\x16oX\x95FR\x0de\xbbv\x13\\p\xe2 _\xc0\xa8\xc3\xb1\n\x8e\xb7j\xc1c\xfdtD\x1c\xd7m\xc9!\x8d\xd9G\x9d\x89m}\xc9tY1\xb5\xe6;\x93\xe4\x1dM;\xcf\xbb\x15\x8e\xd0\x9a\xa3GzdX\x9d|\xb8(\xdc+\xdc\xa5\x81LL'w\x81(e\xe2\x1b\xc3?\x8f\x80\xaa\xc6\x89\x8f\xe3\x80\xae&\x8fk\xb1\xf3\x90\x1b\x1d\\\x87\x96J:\x8f\xa2\x16\xbcE\xe5`\xb2\x83\xce\x0f\xb0\xe2\x07\xc1\x0f\xf0\x96y\xef\xb2\x87\xd1\x95 \xaa \xf5\xdcb`2\xd2{\xd9\xcb\xa3\xf8\xda\x91R+\xbdwy\x8a\x05{/{\xcb\xa3T\xc7%\xf0:\x0c\x05\x8a\xcd\x96\x0bYA\xbe\x1a\xc5\xcb\xfc\xaaC\xa7\xd7G\xfb\xc0\xcd\x97\x87\x84j\xe2G\x84\x0d\x08sk\x03\x84\x16\x98\xc9\x90<\xc6\x08\x0b\xb0\xf5\xc0\xa8`\xed\xf4<\xa7\x16\xf5\xd1+\xa5\xbcW\xa2xMou\x84\x88\xfcQD\xdf\xceS\xdc\xa5\x89\xa2\xd6\xc9\xc8\xfcm\xbe?\x8c\xb4\xda\xa3-f\x06\x14\xe5\x1d\x98\x7f<\x0d@\x14`\x85\xd3+T\xb5\xe3X\xfe\x9e\xb3M\x7f\xd0\x82 ~N\"\xa0R\xedoZ\xcf\x04\xbb\x13\xfdBm\xa8\xb7oROt\x19\xbd\x02\xcc\x1d\x05f\xb3On\x1e9bm\x87Dc\x1e\x07(\xe6g\xf9:\xc2\xf6e\x8a\xbcC\xed&\xdb\xe6\x95\x1b\x13u\xa3K1\x1b'\xabA\xd5\x190\xb6!\xb9\"\xbd\xb7\xab\x80F7\xbd\xae\xaa\x942<]P\xae$\x81[-k\xfb\x12\x85\x93\x9a\xa1\xa5\x8dC\xd2\x1b#s\x9bu\xa4\xfc5\x8c\xe9\x02\xa9Uek`\xd7\xf1k\xadF\xae*f\x89\xbb\xd5\xbc\xc0\x11\xcd\x19b\xa2uT\xf6X\xce\xa8\xb0\x15\xbb\xc3@\x1e\x93\xef\xfe\xf8\xc37\xaf\xbf\xf9\x97\xaf\xde~\xf3\x87\xaf\xbf\xf9\xc37\xaf\xffc7\n\xe6<\xd69\x82\x8c\xa9\xf2z\x8f\x0f\x1a\xfe\xd3\xfe\xf5\xac7\x7f\xd3[>\xb9\xee\xc9\xc7\xf37\x8f\x97O\xae\x1f\xcb\xf9\x9b\xc7\xbd\xab\xcb\x97\x7f^\xa4\xcb\xe1\xe0\x14\x19\xdc\xe9\xfc\xcd\"]\x9c\xf5\x1e\xbf\\\x9c^-\xee\xce\xa6\xe3\xc5\xdd\xf4\xeb\xc5\xdd\xa7_/\x87\xa7\x134\x0fQ\xb3\xdb\xbf\x9e-\x16\xe9\x93+\xf5O\x0foM\xdao\x83\xeb\xde\xa8\xe8\xcbd\xaer+Vy\xd9?\xf9\xdd\x1f\xbf|\xfd\x1f\xbf\xfbj\xa0^u\xeab\x91\x0e\xf3W1\"= \xeeQ\n\x15\xaa\xcf\x83'\x86\xdb\xe2\xbb,Tq\xd9?\x85F{\xe0o\xe6t~6\xfe\x9c\x8e\xdf}1\xfeO\xcb\xfcq\xb6|rZ\xad\xb3\x0c\x81\xb0\xad\xa8^\x9d^\x17\xda\xcb\xf9\xf7\x88\xf4\xb6~\xcfE\x0b\xd5\xa0\x7f\xb9\xa3\x9cz\x82q\x13Q\xddhZ\xfa\x8f\xa2U\x9a\\\xc8G\xbf\x9e\xbe8\xbb\x90\x8f\x02\xa1\x9e\xe1q\x8b\x8f\xe7\x17\xf2\xd1OY\x0c/O\x9f\xc1\xbf\x9f_\xd4\xaf\xdb\xab\x1f\x989tA\xd8\xd2n\xa4\xb0\xf7\xb0\xf8Q\xb2\x8c\x98//PUzb|]\x82\xf2g\xfe\xf4@nE\x10ON\xc4A7\x1bAE\x93\x1b\x8f\x88\xd0\x9a\xbaf\xab\x81\xc0\xaa\x87\x91c\xa91Ut\xe7\x8bh\x0d\x93w\xff\x87x\xcdR0'\xf6At\xd1Zv\x7fD\xa2\x81M\xec\x17h\xfeWh\xa4\xa1\xca\xf5\xb5\x8f\x81\x81\xd6\x0d\n\xab\x1b\xa4M>\x86H\xe3fJ\x89wq!@\xc9\xa1\xa9\xf0\xaa\xc3\xd12\n^\xb7Q\xf0\xdc\xa3pD'4\xed\xf4\xbbP\xe5\x06(\x8e\xc3x\xad\xdf\x8dr\xb2Y\xd1I[\xba\xdd\xbcp\xf5~]\xaf\x8f\xc8*\xd79Z\x0eA\xd0\xb1\xf3C\xd3\x01{\xf89\xef\xb02\xa29\x07/\xb2\xcd\xd3E\x0b\x92t\x01\xf3\xd4X!\xda)\x84\xcb\xdc\x99\xf2\x91\xecg\x0f\x99\xba\xbaX\xd4(m\x14V\xc2\xd1'85\xc3\x86\xe2\xb2j\x11|Adh9\xe1\xb3\x92q\xc5\xe1Ds \x0f\xad\xa8\xaa!\x83\xcc\xef\x18Q5\x1f\xfb.H\xdc8\x12\xf9\x0c\x1e\x1c\x88\x0f\x06\xd9\xe0\xd4\x87\x00l\xf1\xf2\xe3\x81\xfb\xabr\x06\x87\xb4\xa4\x1a^\x9e\x8e\xb4S\xb0I\xffz\xe6G\x82\xf1\x08\xbc\xf4\xd1@Z\xf2\xe7\xc7\x91z\x01\x92\x14\xf3T2\x95-\xe1~\xcaR\x99\xecb\x81^i\xeee\xc2\xe35fO\xe5&\xce\xa25\xd4$\xfd0\x8cW~\xe0\xb3H\xfa\xd1:S}`\xa9\x0ciD\xb7\xb0VU\xb9\x84q%tI\xc1\xbc]\x14\x07\xf1\xf6^z;\xee\xa7\"\xa4\xa9\xf4\xe20\xcc\"_\xdc\xcb\xb5\xcf\x99\x82\xe1^\xb2u\xe6a\xf5\xec\xa7\xccO\xa0\x1e?J\x85/2\xc1dH\xf9\x0d\x13~\xb4\x95i\x1cd\x08\xd1\x9eb\x81T\xae(\xdfR_=\xc4\x99\xf0\x7f\xca\x98\\\xa1\xa20\x95j\xfb\xaedf\xe9\x05\x8cF\xf8\x10\x8b\x1d<\xc4a\x92 \xc6\xe5\x9a\x85\xb1\xc7\xa9\x90k\x9f\x86q\xb4N%\xf4\xdf\xf7R\xb9\x8b\x83\xb5\x1fmS\x19\xf8\xdb\x1d\xb4\x9fP.\"Us\x12d\xe1\n \xca\x92$\x80\xber\xeaC\x13{\x16)y4\x95\xd4\xa3k\x16\xdeK\x8fr\x06\xd0\xc4aB\xa3{\xe9\xf1\x0c\x06{\x1d\x87\x007\xbbK\xe2\x94\xad\xe5\x06\x9aI\xe5&\x88\xd5X\xc9-\x0d\x02\xc6\xef\xe56\xf3\x05\xe5\x00\x8e\xbf\xa6\xf7\xf2\xc6WX\x11\xc9\x88e\xa9\xa0\\\xc67~Do\xa9\xe4\xcc\xf3\x13\x96J\xce\"A\x03\xf5w\xef\xb3\xdbT\xa6;\xff&\xddQ\x89\xce R\x009\xe6B\xa6\xf7\xa9`a*\xe9\x96E\xde\xbd\\1\x1e\xf8\x91\xf4h\xc88\x95\x1e\xa0\x85\xf4\xe2\xcd\x861\x85/\xeb8\x95\n\x05\xa2\xadd\xa9\xa0\x82I\xa6z\n\xe03.\xe4&\x13\xab8\x9074\xdb\xb0H\x06\xd9]\xc6\xefeH\xfd4\x8ed\x18G4\xdd\xc90KY\x16\xca\x88n\xe3{\x8a\xb8\xa6\xa0L\xa8\xcf\xd5\x1f\x80)\xf6|\x1a\xe0\xa8\xdeKA\x85\x88c)|\x16\xad\xa9\x1a\xe1=\x0b\xe4\xde\xa7?\xb2T\xee\xfd \xa0\xeaO\xaa\xd0f\x1f\x03d\xfb\xf8\x9en\x99\x04\xccF4P\xa3\xbfN\xa5\xb7c4\x91\x9e\xdaw\xc85\x8d<&a\xd1\xcam@S5\xb2Y\xaa\xd0,\xda\xc62\xf2\xa3\x1f)L\xb4^\x0e2\xdd\xc5j\xd4\xe2\x80r)b5\x03\"\xbe\xb9\x8f\xa5\x88\xe3 \x95\xb7j\x8d\xca\xdb\x98\xdf\xa4\x922\x1eK\xca\x13*i\xeaS\xb9b\xa9\x90+\xff\x86\xc9U\x00h\xf9\xee\x9d\x1a\xdeDzA\xb6\x92^\x1c\xabU\x19'rCy(7~\xba\x93[\x7f#\xe46\xe3\x99\xf4\xa3M,\x7f\x8cW\xa9\xbc\xf1o}y\xc3\xd9Z\x064Z\xcb\xc0\x0fc\x19\xf8\xd1\x8d\x0cY\x94I\xb5\x18e\x18\xaf\xa9\x8ch\xc8d\xa2\xf06Q_\x938\x15\xf2\xa7$\x8e$\xf7\xbd\x9d\xe4\xd9\x8e\xcb\x94\xdd\xddK\xe1'\xa9\x1a/\xa6\xfe\x89\xe5-\x8d\xb6\xf2V-\xe7[\xff\xc6\x97\xef\xe2\x88\xa9%%W\xfeZ\xae|\x05\xf0J\xad#\xe9\xb1Xa\xb0Z\xaar\x1b\xef\xa5\x1f y\xe3\x872\xf4\x03\x191!\xe3(\x901\xdf\xaa\xe5/\x93l%\x15\xc0\x82\x052\x8bby\xcb\xd6\xf2\xee\xeeN\xde\xdd\xbf\x93\xd4\x93t-)\x93t#\xe9VR_\xd2@\xd2P\xd2H\xd2X\xd2\x9f$\xe5\x92\xa6\x92\nI3Io%\xbd\x93\xf4\x9d\\Q\xb9Z\xc9\xd5Z\xae\x98\\m\xe4j+W;\xb9\xf2\xe5\xeaG\xb9\n\xe5*\x92\xabX\xae\xb8\\\xa5r%\xe4j/W\xb7ru/W\n|\xe9y\xd2[Ko#\xbd\xad\xf4v\xd2\xf3\xa5w#\xbd@z\xa1\xf4\x14)\x94\x1e\x97^&\xbd\xbd\xf4n\xa5w'\xbd{\xe9\xbd\x93k&\xd7?\xca\xf5\x8d\\\x87r\x1d\xcb\xf5;\xc9<\xc9\x98d[\xc9\xb8d\xa9dB\xb2Ln|\xb9\xf9Qnn\xe4&\x94\x9bXn\xb8\xdcR\xb9]\xc9\xedZn\x99\xdcn\xe4v+\xb7jb\xe56\x90\xdbPn#\xb9M\xe4\xf6'\xb9\xe5r\x9b\xca\xad\x9an\xb9\xbd\x95\xdb{\xb9\xbb\x91\xbbP\xee\"\xb9\xe3r'\xe4.\x93\xfeZ\xfaL\xfa\x81\xf4C\xe9G\xd2\x8f\xa5\xff\x93\xf4\xb9\xf4S\xe9\x0b\xf9#\x93?\x86\xf2\xc7X\xfe\x98\xc8\x1b&o\xb6\xf2f'o|y\x13\xca\x9bH\xde$\xf2\x86\xcb\x9b[ys/o\xde\xc9\x80\xca`%\x03O\x06\xbe\x0cnd\xc0e\x90\xca@\xc8 \x93\xc1^\x06j\xa9\xca\xd0\x93\xe1Z\x86L\x86[\x19\xeedx#\xc3@\x86\xa1\x0c\xd5\n\x96a\"\xc3\x9fd\xc8e\x98\xcaP\xc80\x93\xe1^\x86\xb72\xbc\x93\xe1\xbd\x0c\xdf\xc9\x88\xca\xc8\x93\x11\x93\xd1FF[\x19\xf92\nd\x14\xcb(\x91\x11\x97Q&\xa3w2\x0eeBe\xc2d\xb2\x91\xc9V&;\x99\xdc\xc8$\x90I(\x93H&\\&\xa9L\x84Lner/\x7fR4M\xf2X\xf2T\xf2L\xf2[\x99R\x99\xaed\xea\xc9t-S&\xd3\xadLw2\xf5e\xfa\xa3Lod\x1a\xc84\x94i$\xd3X\xa6\\\xa6B\xa6\x99L\xf72\xbd\x93\xe9\xbdL\xdfI\xe1I\xb1\x96b#\xc5V\x8a\x9d\x14?Jq#E E(E$E,E\"\x05\x97BH\xb1\x97\xe2V\x8aw2\xa32\xdb\xca\xecFf\xa9\xcc\xeee\xf6N\xee\xa9\xdc{r\xcf\xe4~+\xf7\xbe\xdcGr\x9f\xc9\xdb\x8d\xbcM\xe5=\x93\xf7B\xbe\xa3\xf2](\xdf\xdd\x0e\x16\xab\xd3\xaa\xe6\xb47\"\xe8\xffoq\xbb\x1c\xfc\xa6\xbf\xb8\xfdy:\x9a>\x7f?0\xba\xcc\xb2:\x14r_\xcf\xe6\x8b\xf1\xc5\xec\xd1\xd5b\xb8\xf8d\xb4\xb8]L\x96\xc3\xdf\x14\nD\xf6\x897Ub4\xa3\xb6B\x94\x19\x96\xf3\xf1dh\xc5\x87\xe5p\xd6\xbf>i\xfa\xb48]\x9c\x0e\xfa\xd7'\x8b\xf5pqz=\xe8_c\xca\xb5\x13\x90\xbaJ\xb7?\xb9>E\xa5\xaej\xff\xf6\xf6v19\xbadsG\xad\xf6\x17\xd4\xc5\x8b\xb1\x05|\xf8\xe87\xbf^\x9c\xfe\xd3\xd5\x7f~\xdb\x1f\xc8\xc7\x9f\x80@Tg\xe1O\xbc\x0du\xc8\x11\xb3@\x8c\x0f\xaf\x03y\x12=\x1a\x7f\xe2\x81&-''Y\xb7\"\xdf\xb3\x80\n\x7f\xcfl\xb9\xcd\x81S\xc8\xa3/\xfa\x117\x99$\x87NX\x9a\x87\xd0\xd2\xf7\x19I\x9a\xa1\xb54\x7fF\x1cZc\xf3\x0b\xb1\xdf\x0d\xc1~\xba\x10\xf7vj\xd4E\x08\x81\xdb\xe4\x03\xe3bX!\xf9\x17\xa2_\"W\x87\xf8\xb4\x00$\xc6\x95r\xba\xe8\x9fn\x0f\xdc\xb7\x8fJ\xf9\x07\xa7\xdb\x03<\x1b\xb9\x80\x0d\x0e#%9\x1b\x90K\xd2\x07\xf2\x14\x95\x92-!?9\xeb8\xa6$\x9fs\x87w8\x976\xf2UU0\xeb\xaa\x84\xf4#pK\xd5(X\xce\x17\xb7\xcb\x06\xc1rG\xd3\xaf\xb3 \xc8\x8b\x9a\"-\x12\xbf\xa3\x9a\x8c\xfb?x;\x16\xb2\x83\x15\xb8a\xf8\x0f1_\x7f\xa90d#\x18\xaf\x023\x9b\xbfY\xa4\xcb'\xd7\xa6JG\x15E\xe6\xdb]\x1e5\xd3S\x94\x06tM\x7f2\x1dR\xec\xca\xdcb\xc94!\xfa]\xcc\xd2?\xc4\xe2\xf7to)\xf6\x1f\xf9\xefb\xa1\xad\xd3Z\xb2\x7f!\xbee4\x15\x7f\x8c\x98\xe9q\xa5\x8c\x9f~S\x9b\xcc\x9c\x92\xf5]\xe7\xf1\xce\x13\x89r'\xba,\xd7\xea\x82\xd3](\xce\xeb`~\xb6,\x1f\xac\xb6J\xf1\xbd\x1f\xe9\x9e\xa6\x1e\xf7\x131Cg=0\xce\xbd\xfd\xaa\x9c\xd8\xa5G\x87\x86\xbe\xa3\x89\xa0\x9d\xf1\x13\x86\x8e\xe7\xd5\xfa\x07\xfb\x00\xc7:@\x9fw89c\x13A\xdb\x1avO\\\xded\xbbA^\xc7\x82\x87\x81\x7f\x827&NL\x0f\x9aWQ\xcdW\xac\xf99\x91\xa7\x0d\x05\xbb\xa0\x92\x01\xf3\x84\xd9\xf1m#Q\xcd\xc09\x88$\n#P\xf8\x08\n\xf9Q\xf6\xcf]\x06\xef\x01\xc7\xbc\xaf\x8abS\xd7C\xae\xc2\xbe\x18Jv\x84-7\xf5=\x06\xc2\xa2\xc1\xa6\xb3T\xe3<\xc1\x8e\xc3q\xf6W\x98\xc5\x8fs\xe6\x87\x1ej;\x8e\xc2W\xb8\x7f\xe9Zy\xbe\x1f\xecX\x7fq\x94\xbb6R\xf4g\xfb\xc0\x06\x1f\x80A\x0d\x8d4\xce\xa7\xde\x8a\xfd-fT\xef\xd5\xba\xce\xe9\xeb\xf2\xd6\xaek3E\x0d\x00\x96\xed\xd8\xde\x83\xe6\xd88N\xd3\x0d\x82\xe74;\xe1\x0f\x87\xe2\xb8\x89\xef\xfd\xa6k\x93\x8dh\xf0'\xfe\x80E\x9d\xf1\x00\xf7S\xb9\xc2\x13\xc6\xc3(\x8d\xfb\xa8\x00\xbe>uY\xc3VX\x91\xad\xa2A\x1e5\xf9\xbf\xe3,a\xd1\x9a\xad?\x96\xedI\xc6;S\x99?\xf1.4\xa6tO'\xe3\x0dJ\xa2\"\xb6:\xf7\xb8V\x80\xacn\x9ak\x1f\xec\x90\x94}\xc3d0\xa5=\xed+\x10\xcc\xbdGM\x05!\xf4}G\xaf \x0f\\*\xd0\xb2qv\x9e\xfb\xf4~D\xc3\xe4\x02\xe21=\xeav\xcd\xea\xd85R\xbd6\x05\xed?tN\x8c\xbe\xae\xa8P(\xe7\xc3\x05\xd1\x07\xe7XU\xb5\x83\xa3\xf8\x9f\xcc\x12\xc2\x12\xf6#^`}\xcd\xa9\x1f\xf8\xd1\xf6\x87\x80B\xcc\xf6.\xe3S\xae\xb6\x8bl\xe4V\xd1\x97\x17\xb7\xdb\xe1zS\xf3\xeeAy8,Nb\xd1\x19$\xc7X\x1e\x01J\xef\xb4M\xe1Q\xd4\xe0\x1a\x87\xab\xe3i'/F\x8a\xfa\xda\x94\xf7#\xedh\x11c$\xf16?\xa5\x1a\xb0x\x92\xfb\xe5\x84\xbb\xc0\xf9`\xbc7\xbeeFd\xbe\xc4(>\xfd\xa2\xdbx\x1d\x8a\xeaC\xa3a\x1b\x8c\xc8<\x0fa\xde\x1b\x91\x1e\x04\xa4\x86\xf02\xea-\xf0S\xd1s\x85(\x9d\x973Bm\x9f\x7f@m;\xaek9?\xfb\x80Z\xe0\x93\xaeg\xdaZ\x8f\xbb\xbc \xcbm\xea8\xaf\xd4\xd1\x00;\xa3k?\xda\x9aBO\x1f\xd0pP\xa9\xe3\x99{\xf6v\"\x0c\xa0.\x93\xef\xf9\x03\xda\x12t\x15\xd8\x1e~\xda\xa9\x87k\xb6)\x0em\x15m\xdc\x85\x8aPA\xb1\xcf+\x81\x0d\x97\xee\x98x\xd5\x05\x8a\x14<\x0b\xacW\xb6\x8a\xcb){\xdd\x81\xa1\x1b\x1bF.\x89o\xaf)\xb0\xe1pP\xa8BG\x92\x9f\xb3%\xc4\xe7\x82\x87\xe9\xd2%\x8e\xd1@\xcc\x08\xe6<\x87\xf3\x85\xf9r\xa0\xa9\xd2\xa0BzrJa\x9fh\xc1\xad\x11\x04\x82\xf0\xdf\xb1\xaa\x835\x87\xe6\xcd\xf6E{\xfb-\x00\xbee\xe2\xfb,`)\x1e\xa3\xa3\xa3\x04\xec$\xbaH\x10\xe8\x10\xe1dzA(\xb9\xd4GHl\x12\xf8\x91j\x98\"Q\xbd\xf1\x93\xaf\xc2D\xdc\x7f\xebG,\xedS\x08m@\xc9\xcb+\x12\xa1\x17\xfe\x93>\x9b\x88\x1fv\xfeF\xcc\xe9\x12\xae\xdb\xac\x82\x9bo\xa25\x8b\x84\xfb\xfa\x13\x00\xccq\xe0\xe1F\x08\xd4\x12\xcf\xf9Ru\x91\xc2\xf1\xe6\xc9tpA\xf8p\xe8\x90\x130\xea\x85\xf0\xb7;\xa1`\xcfF\x84M\xfc\x14@4\xb0[\xbe\x90\x19\xb9\xaa\x8f\x9dQ_\x07\xa6\xa7y1\xda\xa86W\x8da%#2\x1c\xdaAB\xaa\xa1\xb9RB9\x8b@\xe8\xad\xd7\xda\x12\x0e&\x1f\xe7\xda\xe7\n\x9f\xcaq\xa5\xcc\x0420S]D\x0bQ\x8b%\x99\x82q*W\x1f\xb3\xb3\xb3\xcf\x9e/\xe5|\x91\x9d?;\x7f\xb6\xc8\xce\xcf\xce?\xd3\x89\xd5R\x01\x94\xca\xce\xce\xe8\xd9i!,X\x111\xe1\x8e\x91\x03+G\x84W\xc7P\x81\xe8#\xa2\xb9<)\x03\x02\x94\x92\xe1>>\xb3\xc7\x02\xd5\x9b\xf3\xc0\xe55\xab7\xc2I0\x02'\x10\xb98\x9b\x8eHo\x11\xa9\x14\xabU\\\x88\xde \x8f^W.\x9f\x15\x18p\x93Z\x1b\xd6V}\x0e5\x94\xd3\xb3\x82p\xf2e\xbcf_\x88~4 \xd7:,,F\xf9\xf3t<\x14\x08\xfe\xa6P\xbf\xa7j\xe8i\xda\x00\xee\x85)\x19\x13o@\xfe\x89<3\xc7\xb5\x90\x08\xc5y\x95z\xe8\xd5\x8c>\x15\x99\xf1\x07k\xe6\xc1\xdc\xab\xd54\xa4\xef\x8f\x14q\xf3#f\xfe\xbe\xa2w\x05\x024*\x05\xb4Al\x1fz\x1epZ\x86U?@e\x18kM\x9a\xeb\xae\xae\x96\xab\xdf\x8a\x00\x9c\x0dj\xa8X\xac;\xdf7\xfd\xaa\x0e\x08/\xbaUD\x1e\xd6\x1a<\xa0\xb8Y\xc7\xfa\xe7li\xd5`(\x11\xb0\xa5\xa2\xbc\x85.\x14=\x9f\xbd\x1f\x95\xda,K\x1a\xadM\xd7]\xda\xeb\xfe\xa2(\x87g\x8f\xfdC\x90]V\x00\x1b\xa0\xe8w\xe1\xea%k\x83\xfa\x87\x84zGC\x9cr/\x978\x0d\xd0z\x15\xd9\x0c\x85%\xc8\x1e\x0c\xde\x97;\xca\xd3C\xaezKn1\x9d\x00F\xf6\xe4\xa9\x06\x19\x02\xfdA\xf0\xfd\x96z5w\xc2\x0e\x86\x0c\xd2\x1f\xb9\x04\x97\xf8\xa6n\x07\xdfP\x10\xbf$\x91#b/Z\xaa\x9d4\x0c\xf2x\xccr\xbb\x04\xa6\x96\xedq\xdd\xd92Q\xc7\xdeV \xa9j\x19\xa98]],b\xb0\x8c\x1a=\x14\xa9,\x81\x82\xb6\xe2\x92\xd4/\xaf\xffy\xa0V\x01F5\xf0\xf1\x10\xce,\x87`9\x02\xb7\xad\x8acpr]Z\x19Pjj\x1c\xc1\xdb\xc4Q>\x82(\xc7\xa8~\x0c\x1c\x93\x91iQ\x05|\xb7\xf6\x05\x19\x83\xe1\xac\xf6 \x1a(\xd4\xbf \x81\xa2\xbc\xf1p8\x80\x88ne\xc8\x06j*Ax\x03&?\x18\x01\x07;\xb3)gZ\x1c\xaa\xf54\xc5\xfe\xe0\xc8\xa8\x15&e\xf7\xcee\xf3xY\\\n\x8d}\xd4c\x9d\xd5}UUD+\xb4\x8d;J\xb42\xa9\xee\x90\x83\xee%b\xf6\x82\x0e,2c*\x96j\x12\n\"\xcd%y\x96\x9b\xe3L\x1ds\x18\x03^\\\x81\x8f\x9a)\xee\xdb\x9aVW\xbe\x03\xe2j-\xb9x~\x8b\xdd\x1fl\x02rHy\x15\xd2\x97W\xe4Y\xfb\xc6J\x81:\x1c\x1er\x06k\xf5\x9cZ\x86\xe3\xa3<\xf6{C\x8c*\x1d\x8b\nUf\xb5\xaf6\xe6TN\x05\xd4\x96\"\x1e\x91g\xe0\xe8\xc5va\x04[\xd2ZyP\xc2\xb8\xaf'*\x10\xd3\x19\x99\x8b\x91\x86\xd7\xa1<\xd1\xe1\xab\x18\xca\x8c\xa5\xcf\xef\x95\xf0\x96\x8bI\xef\x7f\x194\xecN\xdf\\\xc7F\xe8|C/^\xb1\x84\x11\xb3\xc8Z\xcf\xbe\x81\xec\xccd\xaf\xa3\xbaG\x86\xe4)yI6\x8dh\xadrM\xcf_\xa0\xd7\x96\x18u\x1def\xe0\xa1\x82\xe3s\xcc\x13\xb7\xd6\x04\x92\xf7\x08%\xe7\xbeg5'\xc0\xda\xfa\x9e\xda\x03\x0d\xc8\x98\xa4\x03rI\x9e\xb6V\xa45\x159\xc5\x01C\xf9\x89\xe0~\xd8/\xeej\xff\xac7\xb5\xad\x95\xf1\x82\x8d]\x03a\x16\x17\xe4\xa4?\x1cf\xa8\xd1A\xc1 :\x90\x16g$+\xcdH\xb6\x04\x9b\xbe\xd2$\xa84P\x7f\xd8<5]P\x03\xb5\xa8\x8d:0\xb1\xb8\xa2[\xca\\\x84\x00\x04\xf8\xe6\xd1\x06\xe5R9\x0b\x8aj0\xb5\x10\xb0\xbe\x81\n\x01\x9a\x9e\xb9\xe9\x0b\x90\x9en\xd4\xc5\x87vs<\xce\xc9MF\x86\x8ae_\x03\xeb\x81\x93\xbfn\xc4\x07\x94\xf1\x0e\xea\x93PN\xc3tFhG\xc2\x84\x8a\x85\x0c\x16\xa7\x93\x1c\xfd{\xa29\xf5\xb0\xbb\xc7Q\x9b\xf0\x10\xb5\xd9\x93\x97$l]\x89/\xce\xb5\xb1[\x05\xdb\xf7\xc3\xe1\xa0\xb5\xa0\x1e\\\x85\xeey\xac\xdf\x90\xde\xfd\x81\xa5\xc2\x8f\xb6\x1f\xb2\xfc\xf5f\xa3\x0e\x13\xac\xe4\xbd\x92\xc84\x11\xc8Y\x17\xab\xeaA \xeaaa,\x01\xc9\xf3\x91\xbd\"{\x14\xce X\xed\x9e\\\x92\x10\xc2\x11\x15\xd6\xe2~@fd\x0f\xd4,D\x81m^\x98\x0d\xa8/\x17[T\x1d\xe3b\x0b#\xcd\x0bP-TS|\x17\x8e6\x8cO)\x94`b\xb3\xa39\xe9\xf7K\xe8\x10\x97\xd0!^\x02`\xfd\x12\n\xc4\xcb\xc1\x00\x03\xa09IZ\xfb\\7\x8b=~\xabXc\x03+\x9fLGpW\xe7\x0c\xaf\xa6l\xec&-!\x97d}A\x92C\xb1\x0b6\xf3d\xa9/eE\xb0\xfa\xdbt6\x04\xaeA4SC\xf3sSE\xf3k\xf6\xd0\xb5k\xedtf\\\xfd\xdb\xc9Q{\x14\x93\x98\xcf\xd1\xa88c\xa0A{\xfa\xf4\xd3:\x8dF\xc1\xb3\x03\xde;\xdb-\xa2\xc8\xf1x}\x18\xe8\x12f\xc7K\xc7\x8a\x0dH\xf9\xc0aT>~\xb8\xaa\x9c{v\xe4)y\x99\xa6\xa0\xc1\x9a\x19@\x84g1\".wue^P \xed\xfb~0\xca\x97\xa8\xd5K#\x11\x8f\xbb3\xbf\x02\xa0M\xf1om\x9c\xdb&\xa6T\x190\xc5\x1b\xe6\xd3\xa5=\x1d\xd2K\x0b\x17\x13\xcd\x97\x16F\xac\xd6s\x93\x90!\x01Z\x94\xcd\x93\"}\xb2\xe9t\x9e,\xdd\x8a\x83\x12\xf9L\xff.xd\x99\x17:\x0cJ\x0eq\xbf~F\x86%9Gm\xd8\xd3V\xce\xf4\xec\xbcE\xee\xce\x80N>zD\x9e=G\xc9\x1b\xa4\xf0\xe7\x07\xa4pX jEN/HF.I\xea<|\xac\x88\xd8\xb5Vm{O\x11B\xda\xd8\x1e\x01\xbfrVT\xf5\xab(\xef\x9a\xfe\x93\xbe\x8f\x1b\x80G\x8fH\xff\xe4\x84k\xbb\x10-\x13j\xa1\xac\xe3b\xd8\xf1\xe6\x85\xfaaR\xdb\xa0z:}\x14N\xda\xe4\xcai\x90\x0b \xf5\xf9\x90s\xa9\xf4y\x9b\x90\x86\\9.\xa3\xe6\x80\\\x93\xb1\x12\xa8\x0dzE\xae\x89\xe6\x15\xf4\x02)\xe0\xd9S\xfd\xack\xe0\xe4\xb2\x84\x07\xf5Zlc\xbc0Z\xf5\xce\xc7\xad\x9d?N\x0e\x8d\x0f\xadD\xf0\x83\xa8F&_&c\xd7\x1e\xb3e\\.\xc9\xb3\xcf\x14ZF\xe4%y\xfeic5\xa8em\\b\xbc\x1d\x08b\x15=m\xa0\xa8\x1d\xdegj\x0e\"ry\xa5\x80i\x13\x9e\x9e\xa1\xee3R\xb0?{a\xa2\xa6\xb6\x88\x16\x16\xb4\xda\xd7\xa6\xe3\xf7B\xa9\x07\xa2\x87yj\xa7\xd7\xb534p\x87\xd9\xb2\x9b\x19)\x01c;\"\xf7#\xb2\x1a\x91\xb7#r;\"_\x8d\xc8\xdd\x88\xfc0\"_\x8e\xc8\xcd\x88|\xe1\x10\xe1\x00\x15\x94\x08\xa9q\xd4(\x14\xb6\x8e\xbc\x0d\x1a;=\x89\xaa\x12^\xaa\xa4\x95lB\x03\xd3\x96Q\xfe\xd0\x8dO\xe8B\xaa\xb5\xbe\xcf\xed\xb7\xef\x8aV\xb8gG\x12l\xace\xb6\xe4\x1a\xef\x017\xafV\xd8T\xa2\xffj\xad\xd4\xd07\xca\xd5<\x911I\xf0~fg\xfa\x1e\xf35\xe3l\xfd6\xf0S\xd1$\x97A\x9e\x19\xd972\x82\xdb\x87KlJz\xed\x08\xea*\x0b\x02&Z!\xfdpx\xac\xc9\xd2[\xbd\x07\xbak\xdb\xf7\x81\x81\xce\xe0\x82\x9c\xf4O\xfa`\xb6\x836\x98\xb0\x81\xea\xdfW\xd5AkD[K[\xe9Rkf\xee\xc9\x98\xac\x958\xf3\x0cX\xb6*\xadPhG.\xc9\xb4\x94\xa2\xa4\xa8uQ~\xa7\n?v\x9dg\x1b\xc6\xce\x17,<0\x80_}\xc8\x00\x06\xd5\xdd<\xea\xc5\xc0H\xc1\xec\xf5\x0b\x08\xbdq\xec6\x8a;\xf1\xfb\xeaN\xbc,\xdd\x82e\x965\x808\xab\xefU\xb4}`\xd3\xc6\x00\xf7\xa6y%j\xaf\xfe\x16f\x11\x88\x99\x1a\xf5\xb7Vn'c\"\xc8K\x9c\x14\xa7=X\x15\xba\xa0\xda\x9b\xb4\x08\xaeW\x83v\xf3\x80\xa9|\xf0&\x050\xbd\xb0'\xf9\n\xb7(tD\xee+\xd2:\xd1\xa6xj\\\x8a\xa6g\xf8~\xbc]\xde\x8d^\\?\xa0\x82\xe1KrE\xee\xec.\xe8\x07rI\xbe\xbc ?4)\x18\x14\xe9\xbd\x9b\xffP\xb4\xe3kW.\xdc\x1cP,4+\x15\xea\n\x05\xd5\xf8M#\xc7W_\xb7m\xf2C\xce\x08)HAg\x83&Eo\xeev#\xe7{\xe52\xee\xe6C\xb7\xa4\xb0\xd6\xf7\xf6\xeb\xad5\x1cXuAB\xc5\xaf\xca\x1c\x04q\x91T\xa8\xf5\x831\xf4\xd6bdn\xc7\xa8\xa4\x8cG\x8f\xda\xcd\x0cHY\xf2G\x1c\x07>?$\xe7\xf5q\x03\x9c\x8c\xf4\xde\xe8\xdc\x08\xcc%\xe6L\xc6\xe4\xbc\x14\xb7\xd3f\x98GKcAevi\xb9\x851\xd2Y\xad\x08\xca\xf3\x0bm\xc6\xd9\xcf\x13U\xcb\xcb\n!+\x14(\xa4G\xe8\xd8\xbc1k\x97\x82\xa1\x7fO\x9b\x8bv$\x08\x99\xb6g\x1b\x92sT+\xf43\xb3\x0b\xf4\x14\x17x\xfe\x99{\x08\x87\xc3lPVDd\xc3\xa1\xc2m\x16\xed'\xe6VCjn\xae\x94\xd2 \\c-\xeb\x84\xb3\x8d3?~\xd0\x85R+\x9a\xe3\xf1f\x80\x0b;S\xcb\xb8\xa1\xcey\x0f\xae\xf0\xa6Km\x1a\xd9\x8d\x04\xda\x9b\x19o9\xdb0\xce\"\xafY\xbdIW\x8a\xda9\xe2\xe1\x1f\x14\xa9\xe2*?\xae\x1d\xf9\xd1\x03RTI\x10\xcd\x06d\x8c\x82S\xf1\x08%+\x0b/\xc3+\xf2\xac.M\x15.\xa2\x14\x1b(1~C\xd9\xec\xd7\xe1U\xedx\xc7\xb6;.}k\xd1\xe0\xe6\x82Z \"Z\x86z\xac\xa1.\xf6\xdd\xaf\xf64\xfe\x90\xd9}03SR\xca\x07\xe9\xbcL\xea\x07Q\xe7\xe3\xe8\xf2A\xad,\x9c\xe8\xb7ka\x9f>o\xd3\xc2\xe2\xb5\xb5\x03\xd5\xe4ZW\xb3\x16\x1cd\xe6\x82<}\x9e\xf3`P\xce\x82\xca\x94\\^\x91\x17\x17\x03\xe2\x83\xf1Wci\x17\xd5;\xe9\xfb\xe4%y\x81\x10\xea\xfa\xb4.&.S\xb5\xd4\xae1kg\xd8OG\xe4\xa9\":\xf9\xcd\x90\xfa\xf7\xe7\xea\xbb\xda\xfae$7\xcc\xac\x01H\xf3\xcb&`=?(\x08DG\xeas\xf1:W\x13\x8d\xda}\x8bX\xec\xb8\xc9\xfd\x11\x94\xbev\x0c;\x02\xebG\xaa\x9dv+\xa8\x9c\xc6CH\x1fm\xc2r\x084\x18\xb3\x07u\xd1\xdb\xf9\xc1\x1a\x1ci\xcd\x97\xb5\x0ev\xec\x97\x99\x84&R\xd26\x0b\xbf\xacZ\xdd\xa4>\xc4\x12pd\xee\xe1\x88F\x8bV{\xa7K\xcb\x10\xcd{GG\x86\x8aa\x8e=\xe0\xe8\xf7K\xec\x91\x96\x88\x1a\xd5:|\xbfH\xc8\xe8R\xcb$\xfdg\xcf\xf3\x8b\xb8\xb5U\x17#mz\x81:_\x8eE\xe2\xf2B\xee\xc7x\x17\xc6BQ`\xb31l\xd7\xfcb\xb9F\xb5^\xe1>\xdc/\xb0\x9cM\x17\xb4\xbe\xe9\xfca\xa8\x7f\x00\xf7:\x82|\xdc\xa2\x06V\x9d\x1f\xbd|\xdc\xe5\xad\xa8\xea\xbf\xf2\x12\xef03\x87W\xfc\xe0# \x16\x85;\xdfg\xe7\xd5\xbb\xdd\n\x81O\xdf\\\xf6\xe7:x\x9fvu=_\xa4\x8b\xd3\x97U\xd7n>f^\x9c:\xb2\xbf\\\x9ev#4#B]\xb4&?\xa0\xa8H\xc5\xb5\xa1\xab\xd8o\xd63$e1\xba.\xbbxJvMF\xe4$\xdf\xdc\xedD\x18\xb4\xca;\x89\xa2M\x8apx\xb0[zyu\xc0<\xf4\xc5\x99{\xeb\xe4\xb5\xef<\x9f\xe2\xa6\xae\x9f\xb9H\x97\xa7w\xae\x8a|a\xbe\xaci_Y8{._rz\xdfv\x1c\xf3\xecS\x00\x1a\xa4\x96\x93\x96\x1b)\xe6g.\xa5<='\xb2z\xf5\xc0\xfc4\x18`t\xf9\xf9\xa7\xaaf\xa1d\xb7\xe9\xf9y-\xfb\xfb.\xdb\xdeg\x9f6\xf7\x9c\xd8c\xa5\xeaV\x11-a\xd1\x95\x9e?(\xb6R\x87\"W\xd2\xb5\xd7\x13\x0f\x0eC{\x82h\xc0\xe7\xe9|Zq\xd6\xb7o\x0b\xd5m\xfcm\xc6\xa1U\xb5\xb3e\x1c\x9fx\xa8\xfe\xee\xa6\xf0\xef9\xfc\xfb\x14\xfe}\x06\xff>\x87\x7f_\xc0\xbf\x8c\xae\xb1\xd4\xce\xc2\x03\x1e2z\xfe\x86\xd3P\xbb\xc1P\xff\x86\x14>\xc6\xe0\xd9\x0f\x9e\x00\xd28\x13I\x06\xef\xf09A`\x12\x1eo9K\xa1\xf3\xe8b\x12\x9e\x98g\xe0N\xc5=\x8e\xa6\xf1\x11\xd1\x13f\xd8\x04tY\xb0;A9\xa3\xf0\xbc\xc1\x0b\xaf=\x01~'\x04\xc7gF!g\x06p\xec\xfd5\x8b{\xcb\xc9&\xe6_Qo\xd7o\xb9\x808g\xcb\xf2\x0dP\xad\x95\xfa\x90\x1b76\xb9\x8b\xf9\x8aCr\xcc\x95)\xb5u\xc0\xdb\xb6\xecv\xf9\x16N\x8e\xc1BdL\"\x97\xb7\x88v\xf6\xdc\xf5\xcau\xd1\x8a\xa0\xce\xc8\x04\xb2\xc9\xc2];\x17\xbb\x0bJ[]\xe4\xd8Am\xd7\xd0RA\xbf\xa4\xfa\x08J\x12x\xb0,\x9f\xcc\x06\xcd\x14\xd7\x87\x0b\x1d\xa80\xd6\xbb\n\x87J#\xb7\xfb\x81\x1b\xbfZ;\xea\xb7\xd6J\xady\x030\xef\x1199}3\x1f\xcf$Y\x0e?9EW\x9b\xb4]$\x80\x1b\x08\x14C\xa9\xf6{\xb2\xa7\xf6\x1f\x10\x03\xb5M\xad\x92\xe8\xeb\xe7)Z$\xa6\xe4\x92\xe472[no\x9f\xc0\xb9\x947O\x97\xe6\xdaH\x1b\x9fE\xff\x05\xa0\xb8M\xe1\xd1+\xb9W2\xd7\xb2[\x05\x83\x83\xde\x98\x89\x01\xed\xf4\xcd\xecz<\x9c]\x9bq[\xb7\xb3\xdf\xe7\x9f\x01H\xeb\xd2\x81Y \xbek\x92 {se=S\xdf{\x18b\x0b\xce\xbe\xb8\xbf\xdd\x89\xde\x80\xcc\x9c5\x9f\x15\xaa\xeb\x05l\x839MB\xaf\xed\x06\xb7\xea\xdc\x18w\x0c\x05tq\xdc\xdb\x81\xb9o\xc1\x14D\x14\xeb\x9d\xed\xcdB\xca\x85\xfc\x04\xfc\xb3\xf5\x06\x05\x04\x1a\x91\xc4\x8c\xc3Ia\xd2Z\xeb\x8e\xdb-_:\x8a\x0b@\xe8\x0f\x98)\xec>\xc4L\xa1+\x1c\x8ao\x1c\x80C\xc1\x00\x8b\xf6\x97\x84\x83\xff\x92@4/\xfe\xae\xe0\xed\x9a\xc0\xa3\x81\xbf\x8df$\x99\xa7.\xc0>\x02\xec\x1d!<\xacw(\xd0\xb2\x8f\x00\xe9/\xa3W\x10\xbb\x87\x1e@|\xc0R\xe4\x0fm\xf3\x88n\xa9U\xf6\x8b\xb7\xa2d\xc6\x03\xcbh\x0f4\x05\x8f\x0b\x1fDW\x8c\xa0r\x8e\xdb+}\xfb\xa7Efy\xf4\xc88)\xcfiz\xe0\xa6\xe9p\x83\xbd\xd1\xaa\xa6;Q?4^\xa4\x0b\xdd!\x87F\x83|0q!\x058\x1a\x8909DdHW@7F\xa0\xc9\xc3\xf3+Q\x0f\xc4\x15\x95\\e\xe2p\xabrD\x9a\xf2\xc0{Y\x8a\xa8$\x91Y1\xc5j7\x8f\x19\x97F\xb2F\x8a\xa4\xad!\x8a\xca!\x8aE\xda\xa8\x16\xe9\xb8\xf8Hi\x12\x9b\xd689\xb4\xce\x89\x83\x8a\x11\xd8\xa2to\xbe\x99\x90\x91n\xcd\x97W{\xe9\xcdn\xad\x8e E\xbf8\xc1\x03!\xea\xc1\xad\xec\xd0\xfcj\x8f\x7f\x82QI\xed\xf3a\xea\x13\x9b\xdce\x03\\\xb0\xe2\xea|r\xedw\xd8\x06\xc7j\xd3\xe7\x1b\x13z{M\xdf}\x18d\xees\xe8\xbd\x1c7\xc5b\x14\xc7#\xd7\xe9\x8f\xce\x12\x95\xda\x89*\xe3F~\x91}\xb6\xb5\xd6o\x15\xd0\xfb,\xf7\x08\x06\x96\x85\x8f\x1e\xd9\x89x\xe9t\x9d\xb7)\xee\xc3\x8d\xaep\x03\x05\x87\xc3\xcd\xc1m\xbc\x9d\xb3\xcdQ{w\xdf0\xc6\x8d1\x81lm\x03\xd0\xf9h\x9b,m\xa7\\4\xfb\xeb\xbc\xd2\xd6\xc1\x01\xb9\"\xf8\x90\xbdJ\x866\xe9J<\xa8\xf8\xafc\xb3\xb6K2\xf0\xe9^\xdb\x0dn\xb5\xd1\xed\xa1\x1e\x91B\xaf\x1a-\xedIA$\xceF$\xfb\x10\xb6{\x04@\xdd\xb8]A\x03\xac`3\xd8Z\xf4\x8d2m>J$\x1d\x8f\x13I\xb7!\xf8\x98\xfcs\xddlKK\x0e\x11t\x82\xfc\xd3\x89'$_\x9d\x07A!\x05pZe2\x92\x8f\x8f\"k\xf3\x8d\x1b\xf9m\xd6C\xa8B\xf4x\xe1\xb5\x1b}\x9d`\x0d/\x86\x86\x8d\xf4\x89^a\xa6\xf7\xc5#>\xba\x1c\x81\xd2\xa0j)W4\xd9gE\x1f\x89E\xfb\x03\xd8\x12\x14\x13\x14M/\xdd\xc5\x18\x91\xf6\xab\x08\xb9\xb7b\xa7\x91\x1bu\xdfF\xd8\x82\x81\xd1\xbd\xb9\x8d\xb0\x05\xb0\xf4\xf15=x\x1b\xa1\x08\xee\xbe\x08`X\x83oW\x1d\x8adT\x1e\x8du7d%%\x0ciCX\xd2\x05i\x89\xd9F\xa0\x18\xb2\xb1\xfdW\x02\xfb\xcb\xfc\x02^\xd3\xb1\xe2\x01\xb6s\xb0\xac\x83\xf9\xb4\\\xf8\x03\x1a]_x\xb5\x14\xe4\xa5/\xdb\xee\x0f\xfa\xda-\xf0\xa6\xc8j\xb3f\xb7T\xa5\x8e\xd6<\xe3\xb4\x95\x82\x8d'\xd0\xc9\xc1a\x90J\x17@\x1e=\"t8\xcc/\x88t\x01\xadn\xec\xd3\x06\x9a\xef\xbe\xfdP\xca\xfc!\x92\xf8:x\xb8\x80\x1ch\x94,H\xc6\x9b\x11\xb9\xff\xc7\xfd\x04\xe7\xfd\x04\xef\xa3\x1d\xba6\x8a\xcb-\xdb\x87\xe2\xfd\x04\xb7\x91\x9a\x0f\x1e\xb6.\x8d,\xaf\x8f\xc5\x07\x95s\xf1\xd4\x11=\xceZ\xf37\xde\x14\xcc}\xce\x0fP\x13\x12\xd5\xaaE\x9dH#\x19*\xe8\x90R\x971\\\xdb\x0d(\xeb\\O\xc9\x7f>^\xba\x82%o\xd51>\xb9$\xf4\x82\xf8m^]\x88\xa1Is\x1f._\xa5]._\x99_\xdc\xc1\xbb\x0b9\xe8\xe1\x858i\xa9\xf9\xe9\xcdM\xd7\xfb\\\x9aN\xe0j*\xda\x0c\xa4\xcd\xd2b\xbe\xd0\xd3\x11\xe1f\xf1\x15\x97\xca\x01rSYzu\xa2\x03K\xc9\x1d\xf5\xa8\x8b\x19DY\x8c\xaaQ\xac\x8eP\x1eV\x96\xf3CMw\xb4\xc1\xfb\x85\xec\xef\xf2an\"\xeem\xe3\xdc6\x86\x1f\x8d\x88\x1d\x8e\xb0r\xfe\xf4\xb9#\xc0J\xd4?\xff\xb4\x92L\x1b\xe2\xae\x08vgbc<\x9d\xba#wD\xec\x16\xa7\x1as\x9d\xbbs\xb1\xd4\xa3\x89\xcd\xf4\xd4\x9diE\xbd\x1b\xe1{7&\x8a\xcb\xd3\x86`!k\x16\x98\x1c\xcf\xdd9\xfc\xc8\xd6\xf1\xc2\x9d#\xa4\xdc\xc4\x1ay\xda\x10Q\x86\x85\xc9\x8e\xa6\xbe\xad\xe93w\xb64[\x99\x1c\x9f7\xe5Ht\x8egg\xee\x1c\x81\x1f\xd9^?k\x18h{\x95\xc4\xac-\xcc\xdd0\xe0\xc5\x8b'&k\xc3\xb0S\x1d\x1e\xc8dk \xd1\"\xa8 \xe4\xf2\xaca\\Y$|qo2}\xd6%0J\xf6Q\x02\xa3\xe4^\x90\x9c\x81Q\xa8 \x8cB10JE\x11\x0c\xd9\xf7\x18\x81\x99}\xebG7\x8a@\x17\x16i\x1d\xea\xb4n\xe9\xb3\xb7\x81t\x91\xd8\xb7E\xcc\xd5\xbc\xc3\x1c\xc6\xabb\xbe9z\xf9J\x8d\xa1\xafXI\xf1\xf8f\xd63\xf1hU\x89\xb9\x0d\xa6\xdb\x1b\x15\xe3\xed\xf6\xc0H\x0bM\x9c\xd6T\xd0\xde\xd2\xd6 \xcc\x11\xce\xac7\x98\x9f-]\xe6:Y\xc5\xe7\xf5kE*[=\x86C\x9fG\xc6KLa\xd4KQ]j\x88\x02\x8ez\x8d\x8e\xac\xf6\x15u\xafI\x9c:4y([y\xd4\xdb\xb1\x7ff\xa2\xef\xc3\xe5\x97\xb3\x01\xe6W\xe8R\xd1o\xb9MP1l\x03b\x8f \x97$\xbe \xa2Mx\xe2s\x01\"\xcbI\xc1g\x08\x04\xe2\xd2\xa0\xfc\xa0@\x19!\x10\xce3\x86$N\xf1\xdeb={)w>\x17\xefG\xa5\xe90\x1b\xfd\x8e\xfe\xdb\x0fNIy\n\xf2!G\xf7\xf40\x98\x97\xc4o\xd6\nF8x\x91q1s\x02\xc3\xc9\xe7\x11\x8e\xd3t0\xc0}\x84{W\xd6\x18\xe8\x187z\xaa\xf5\x97`\xef\xd4z\xbb\x9dM\x12\x16\xad\xfdh\x8b7\x04S\xee\xcd\xf5H/\x1b\x06\x95\xe0d\xe8R\xa0\xf7P\xe4\xe1;L\xe8\x0f\x9aF\xff\xd8\x802\xcdaO\x1ct\xc7\xeap\xfcF\xa7\xdc\xd9\xaf\xc8\xb1bB\x9dd\xf1:\xc2\xa4\xb7\xbe\xf0v\xc4mw\xed\xd1\x94\x91\xe9\xd9\xcc\xfd\xe1\xf3\xf3\xa6\x0f/\x1a>m\x1a\xad\xa7\x9f65\xdf4(\xd3\xf3\xc6\x91o\x82\xebE\xd38>w\x8c\n)\x98\xd29vbk\xb6\xa1Y \xda\xcb5\xf9S\xeap\x94\xd5H\xec\"\xcb.\x80\x1c\x192\x06T\x89\xd7]7G\x83\xc1\xc5@\xd1&'G\x8e\xf4e\nE\x82\xd4\xb6L\xe8\xbb\xe2UJ\xa3\xad\xf4!\xa3Z\x87\x83Q\xce\x82\xca\xf6\xe2\x1f \xe2w\x1e\x8b\xaa2\xc8\xc9;\xa7\x0d\x17E\xe2v[?=\xbc\xd8\xff\x82\xf1\x81\xd1#\xe1h\x8f\xc8\x89p;\x9a\x85\xd3\xcb\xb3\xd2\xf5TSYyV\x9c\x88ck\x98\x1e\xacA\xbb(9\xa0\xc6\xb0\xf4\x19U^>\x9eS\x12\x7f<>\xac\xb9\xb0~\xd4\x1c\xcd\xfb\x9d\xd4\x189\"\x15\xab\xc9\xedE\xce\x14+\x1e\x92iC\xe8\xd9\xe2\xefC4\x1d\xec\x90\xfe\x9d\xe4[\xe1\x1d\xe5kh\xabE O\xdaw\xbd\xc5\xdf{\xf70\xd7Xzi|\n1SG\x87\x81\xd7\x80\xa7\xf1F\x1c\x02\xbc\x03\xd0N\xa3\x11\x0d\xeb\xc1\x13\xb7C0\x1ch\xdfiv\x17\x0f\x87\xe8\x19\x9a\x93\x96;\xdf\xb1\xa2rq\xe3\xfd\x1b$U\xf1\xc7RF\xd8\xa5\xc5\xb59\xb8\x0e\x9c\xa2\xc0<\x7f\xfe\x02\xfdP\x13\xbd\x19;+\xf4\xaa\xb7X\x9c,z\xbf\xfe\xe4\x9f\x1e=\xee\x0f\x9e\x0cG\x93\xd3\xd9\xc5\xe5\xd5\xcb\xeb\xdf\xcc\x97o\xde\xfe\xf9g\xf9\xfe?\x8f{f\xe3\xd2\x1bt\xbboQ6\xb4Z\x92\xabb$\xa9\xca\xe5\x8b.d\xd5\xd2\xd4\x96\xad\x8a\x92\x9bk\xa4\xf3\xf3\x06\xbf\x8b\x07(\xeep\x18\xe3\xc5\xdf:j\xf9\x8d\x8e1\xf1\xb6\xf0\xf9\xf3\x17\n)\xcc]\xb0(\xbf\x88\xd0\xc4\xc8\x8c\x8fg\x85\x10\xc3+r>r2w\xcd?\xb4\xc3J7\xca\xebM\x15\xf8\xf4\xea\xb6B\xbb\x90\x96N+\x14\xa2\xf2 \xb6\xf9\xc7/\n\xf3k]\x1c\xb6\xb1_5\xbf5\x0fuo\xb1\xe8\x99aV\x1b\xc1\x8f\xb3\xea\x8eE\xe4\xd29F\xb3\xa0\xa0c\x89\x1c\xe3*\xc8\xee \xb3\x11\x01\x0f=\xbc\xb4\xa1\xcc\x0c\xb5\xfa\xfcE\x93+\xa1\x8b\x81*\xe8\"w\xa4,rE\xe8\x12\xc3\xd7\xc1_\xb3\x0b\xb0\x84\xac\xdc\xa7)D \x81\x93\xbf\xe6\x8d,\x85sx\xb8\xceH\x0fAIU=\xd4\x85>>\\\xc0\x19+\xa8\xae\xf2\x00\xb6\xe5\xc5\xd7\x85_4\x84\xed!\xa4\xd9i\x85_\x08\x93?'\x8bh9\x04\x93]\xd2k7Q1\x91|\x9a,S\x0e1\xa6\\\xde\xa5\xb5u\xd2uU\xc4E\xca\x93G\xfd\xfd;Z\x1cJ\xb2\xadu>m\x91\xb1\xcf\x1b\xd6N\xdaN\xf2\xdb\xed\xd7R\xf4^\x06w\x91[\xb257\xfe\xcb9\"\xf3u \xce\x94\xbc$g\x18\\\xa0\xda6\xd8.\xcf\xc0)\x96\xd3\xa7\xb9\x82\xee|0\x02\x03\xca\xab\x83\xd7\xdcL\xaef\x9f\xe7~\xee\xed\x8c*\x9c\xd3|\xab\xb9\x00\xd0\x01\xaeC`\x9ec\xdc0\xb8\x99n\xda\xaa\x81\xcc\x15!\xa8\x05\x0d\xf3\xd1\xa74T\x93\xc7O\xb2\x08\xce\xc9\x98\xa4\xa3FF\xacWt:\"\x1c\x0f\x89\x1c@\x9a%\x97\xe2A~\x8c\x8e\xe4u\x0b\x10>.k\xf4v\xdd\xd8\x19TC\xb6\xf6\xd7\xb6\x80\xceH\x9c\xf7\x161\x0f\xda\x0dY[Xj\x96\n\\\xd2T\xc3\xea@\x11\x9b\x01\xd1\xc4\x82b\xef?\x9a\x8d\x17\xbc\xd8P\xa8\xd7$\x1e\x8f\xc9\xcc:\xc1/|\x84\xe7\x18\x1d6]\x82\xa7\xe7&\xa1%\xfa\xc0\x18J\x04wSxjou\xe6}\xd6\xc1\xd4;\"\xd7zF1\x06\xaa\xd6%T\xe6\xd8\xa2K\xbb\x15\nk6 m3\x8c{\xef\xf6\x98\xd6\xb6\xcb*\xb4\xf8@\xc3\x97\x02\xef\xb0\xdd\xd7\xd6qv02P\xa2\x90Y\x01\xe7A\xad\xfco\x963h\xdf\xfd\xff*\x8c\xa1\xb1\xed\x7f\x13|\xe1\xd9\xd3\x0elAg\xfa[p\x85g\x0d\xee0\xdb\x98\xc2\xc9\x95\xae\xe7\xef\x8e-4\xf5&\xe7\n\xad9\x8e`\n\x1a\x0b\x1f\xce\x13t\x05\xff` \x9dX\x82\x1f\xa5\x7fc\x96\xa0Z\xfc\x07K\xa8\xfcZX\xc2\x8b\x06w\xc3\x7f\x0b\x96\xd0\xd8\xf6\xbf \x96\xa0\xdd\x9e\xb5\xb3\x04\x9d\xe9o\xc1\x12tS\xffNXBSor\x96\xd0\x9a\xe3\x08\x96\xf0b\xfa\x81,AW\xf0\x0f\x96\xd0\x89%\x84\x94\xdf\xfc\x8dy\x024\xf9o\x8c)\xd8\xe46\xd3 \xb3f\x89\x0d\x00\xc50\x00\x14\xa8\xfaT\xea\x8b\xe76\xf5\xf33\x9b\x8a\x9e\xe9X\xd53\xdd\xd1Q\xb9\n\xfeR\xeb\x03\x9b\xa1-}-=mH\x0fZY\x98\xe7Z\xc6\xc2u4\x85\x97\x0c\x1a\xc8\xbb\xc8\xc9;\xeaZ\x03\x18\x89j6\x8a\xa1\x95=\x97\xaaU\x0f:\xdc\x16\x81\xd2`5\x0f\xf7\x9a\xfa\xa8\x10\x1e\xeb\xab\xa7\xcf\xc85\x8c\x02\xf4x\xaa\xf0\xe3i!\x9a\x1f\xb6\xee\x80\x91\x16U\x10H%bt;o\xda\xd1\xd5D\x85\x1c\x91u\xe1\x0c9>G\xa7\xb0\x1e\xc0\xc7\xfb\xda[\xad\xad\x80\xf7\xe3\xdc\x15\xf3\xc9t\xa0\xd0\xbc\xbe|<\x1a\xc1J\x9d\x91\xcc1!4\xc25\xe5t\x07\xbff\x81\x1f\xa63\xe27\x10\x97\x07\xd8Z\xe4RO\xf5\xdap+\xe2l\x9a\x0f\xce\x12\x17Nm\x06uF\xa9C*&\xb0\x01\xc0\xb1O>@\\\xfb\xbb\xdcW>z\x84\xfd\xd3s\xa4\xbax]7\xb7\xb0\x01\x05\x90\xad\xa3C\xea\xd3\xfe\x1b9\x7f\xb3X,\x07\xfd\xc5b\xb1\x18\x00\x83>9\xcc\xf9U\xb6(?K\xd5\xb1\xf8\x80\xcc\x18s\x08\xe3\xdc\xd4\xde\x07}p\xfc\xe1\xc0O\x9du\xe0\x87+2_\x0e\xcc\xee\xac\xfe\xbd\xe0V\xd4E\x0e\xe2\xc3\xe8Xv\x0cR\xa7\xcb\xeb\x87\x84\x8d\xac\xac\x1b\xdc=\xd6\x1c\xa1\xba\x17S\xbd\x93s\x7f\xa9\x06\xaf\xde\x03\xa8p\x96W\x9d&\xb8\x9d\xa9H\xfe\x95%ZXCqm\x07\x90\xd9\x08x\x1fc1\x1d\xbbhJa/\x9b\x17M\xcbU\x1d\xc5\xba\x9e\x92\x97\x07\x8c\\N\x1c\xf8ZM\x83 \xd6\xad\xb54EGo\xb9\x16\xd4\xa60\xc8~9K#k\xa7\x93\xe5v:\xf4\x82\xf0\xe3\xa3\xa3\xf3\xc3\x81\xd7\xa6\x0d\x02}\x87\xa2M\x81\xd5y\xf7\xc0\xeahG\x04\xfd\xd4\xe4\x8e\xab\xe1B\xd7\x8a}\xae\x96cT\x11k2\xe3\x05\x10\x05#-\x12\xe1\x1c5\xc65\x8f\x96\xcd\xe4\xaf\x1bMk\xaf\xfc\x12D9\xad\xaah%|\x0e\x82\x11\xbb \x86\x8e\x98\x1e\xb9\xb4\x08Y$f\xe4\xacN8\xda`\x84\xa8\xcd3\xe2\x82\xb1\x94\xb1\x99~\xcf\xe3\xe5\x04\xdan\xec\x08~\xd6\xd2\xc7\x87R\xf2\xd8\xc1\x80\xb3\xd57\x0f\xa0\xf1\x05\"\xcaK\x04\x94~\xc4\xc0\xe4\x05Y\xe4\xecY\xd5u\x99\xd1\x99|\xe6\xd0\x99\x14\xe2\x8a\x9e\x8d?\x9f\x9c\x80\xf2\xf4\xc9pqzum\x15\xa6\xc3\xdf\xe49\x96\xfd\xebY\xfe6^\xfe|6z1}_\xf8>\xb8\xee_\xcf\x16\x93\xa3J\x0c\x9e\x0c^\x9e\xd6\xf56\x05\xd8&\x8b\xf1\xf2\xe7\xe9\xe8\xfc\xf9\xfb\xc1\xac?\x7fs\xf9rqwv6^\xdc\x9d\x9f-U\xd9\x87\xf3\x91\x92n\xa7U\xc2z\xd1\xa8}\xd0\xd4\xa3_\xa5\x16\x9b\xa2\x13\xaa\x97\xbd\x82(\x04\xaa\x90H\xab\x0f)\xb8\xab?\xe9s\x9b9\xab\xc5\xa1,\x94U\xbb\xa1l~\xb6\xd4\x8dL\xf5\xd5~\x0f\xac\x08\x02\xb5\xe7:\xb1\x02C\xd1/W?(\x8ba\x1dd\xef\xd6\xfd\xc3\xc1]Be\x1d\x1c^\x96\x02|\xe69(\x8e\xd6[\xba\xc2S\xb2\xaa\xe3\xc3\xa3[\xed\xb2\xcb8\xb0\xb2\x87zF\xf2[\x98\x03E\xedN04i\x94\x874\xb5\x13\x986M`/\xa4~ b \x87m\x93\xe9\xfdc2K\xbf\x8f:\x99iu2?\x0e\x91.\xd2\xa6y\xcf\x8b1N\xe7:\xf6\xeb\x8e\xe8(\xa5\xfa\x0fD\xe6\xa4\xab\x18CwR\x0f\x0b\x99?>\x04\xd6\xf48\xfe\x05\xb7u\xf0\x17#\x94\xfa\x18\xffs\x0d>\x1d\xads\xbb\x8d\x80\xb2[\x16\xc3\x1f\xfdo\xb2\xd3\xd1E\x9f\x9ec\x04R\x81\xd9\xd4_(\xee\xd3;\xf8\xa3\x9b\xf6C\xfcW\xbfE\x1b\xa8\xc7O\xf0\x95\xfb\xa9\xf9;Y1f\x13'w\x89W|\xces\x05\xb7\xef\xd4s\xb0\xc6\nq\x19\xc0\x13\xf6-Lyb\xfeB\xa9P\xfc\x84 Y\xa2V\x85z\x8c\xd8-|\x8a6\xf8\xc7\xc7\x7f!\x16i\x14a\x7f\xe2\x84\xfe\x94\xb1 \xf6n`+\xa4\x92\x92\xd8DD\x85b\\\xa4\xf0\x9e2\xbe\xf7=\x86\x8fij\xe2\xa1\x9a\x81I}\xb6\xc7\x8f\xbe~G\xb8\xd2\x10\xffD!&\xc74\xb1C`_ \x0b\xfa\x84\xec p\xca\xa9\xfeD\x188V\xe8\x19\x12;?\x0dY\x9a\x82\x06\x8a\xf4D\xf4\xf4\xfc\xd33x\xc2\x16\x05\xccr\xc6\x01\xae=\x0bC\xe8/\x0e\xc1-\x86t\xbd\xf3\x10j\xf5w\x9c\xa5L#\xca]\x18\xf0\xc4\xb3`\x15^\xb1T\x88\xd3\xf8\xee\xe9\xe7\x93\xe7g<\x7fDd\\\xfbYx'8b\xe8&\xc1?\xf8 \xb1\x82j$\x16\x82z\xbb\x90E\xf8v\xab\xfe]\xb1tG1\xf4\xec\xca\x17^\xeccX\xde8\x80\xb9\xf6h\xa0g\xdd\xdb\xf1\x18\x83\xda\xe2\xd3\x98\xdd \x16\xa566o8f{\x16\x89\x15\xf7\x05\x1bS!X\xb4f\x98\x1d \x0c<\xee\x01\xa8u\x10\xd1q\x12\xd0\xfb\xd4\x8f\xb6\xda\xbf\xa3IR\xb9\xa9\x1f!\xea\xaf\x05T\xbe\xde\xaf\xd4\x1f\xb6>\xbfQ\x7f7\xd4c\xc2GX6\xcc\x84\xf9\x8d\xb6:\x84\xaf\x9f\x02zma*\xb7\xbe\xc0?\xef\xc28\xe1\xb1 \xc0\xbb\x154\x80\xbav\x1e\xae\x04=+~\x82\x7f\xb8^\x13\xde\x0b\xfd\x17\x97\x85@L\xfa\x91BK?\xe2\xdb\x0d\xbbO(\x16\x08h*60\xe0j\xd5\xe0\xa2\xa0[\x8dD\xa1M\xe17:%G\xa5\x10\xeb\n\xd3\xf1\x8e\x05zYE8wa\x16\xea8\xbf\xe1\x1e\xa0\x03\x19[=\xc4\x88; \x0dB\xfc\x9bPN\xdf\xbd\x03\xa4K\x02*L4\xe3\x84\xc7w\x10\x1f8I\xef\x01\xce\x9f2\xc6!\xc1,0\x96\xc6\x19\xc7\x95\xc5\x11iyz\x1fA^.\xf4\xb2a^\x1c\xad\x03\x7f\x83KL\xaf\x88t\x8bk\xf0\xe6>\xc1\xf4\x10\xa6*\x8d\x835\xc5\xc0\xc5I,\xfc\x0d4\x96\xe2\xc4\xa4\x82Q\x00+\xc5\xee\xa8\xd74\x01\xc7)\xb0\xc2\xa2-\xc0\x94\xad\xa1\x81,\xe2\x8c\xc2r\xcc\xc4\xf9\xd9\x19DaVx\xc6}D\xd0\xbd\xcfn\xc79\xf4\xb7l\xe5a\xf6[Aq\xf5\xdd{\xfe\xed= \xc3\xdd\xc6GD\xbf\xe3\xf0\xe9>L\xb7\xbc\xb7|8\xff( \xf9\x9f\x0e&\xbf\x7f\xfd\xea\xdb\xb7\xaf\xbf\xf8\xe7\xb7\xdf\x7f\xf5p\x01\xb8\xa2Eq+\x17+A\xf8I~CE+^\xc8Ic0}\n\xc7\x1aE3\x05\x14\x97\x9f\xea;\x8dN\x97\x0e\x06\x17\xa7\x15\x8d\\\x8a\xe5@u\x04\x98\xac3?\x9d\xbeW\x99\x1f\xce*\x8b\x97v\x1c\x04\xab\xc0\x0f\xeb\xfa\xf8\xa7\x9f\xb9\xb9\xa3w(Z8\xde8\xdd\xb8/\xa9<}\xee\xd6Iy\x9a}\xbai\xa6\xbf1f(9\x93\xf1\x0c'+\x1cI\xa0rA\xf1\xe7\xde\x1dF\xaa \xe6\xd3\xa5b %\xdd\x14\xb9&\xa0\xa1\xf8&\x12}\x95\xc1\xe85\x06#2}\x01\x01\xd6\x8b_Gd\x8aa\xb6\n\x97\x81\xfc~\xa4j\xa1}\xa0\xcc\xb4\xff\xe2\xf9\xf3\xa7OK;\xf2\xa0\xcc\xb6\xea\xc4\x1am6\xc0p\xa8\xb1k)2\xe9X\xf1\x01\x05J\xb5\xa7%\x98\xf8\\eY\xb6\x00\xe1\x14\x95\\\x0e\xec\x1e\xfd\xc2\xfe\xeb\xca\xb3\xac\x05\xb5\x99c\xf2\x95\xe0\xe1\xf6[v\xa7>\xfd1k\x88\xca\x01\x07*iC\xc4\x0e\x1am\xbf\xe3l\xe3\xdf\xcd\xd4\x8e$\xdaft\xcb\xc6.\xed\x8b\x1f\xdd\xf8\x9b\xfb\xc6\xf8*7\xaf)\xdf21sJ\x03\xe2>\x89!\xa8\x08\xe3\xee\n\x809\xa63\xd2\xfb\xeb_\xfe\xcf\xbf\xfe\xe5\xff\xfa\xeb_\xfe\x8f\xbf\xfe\xe5\xbf\xb8\xd4]\xfev\x17`\xfc\x91(\x0b\x1cJ\xa8\xfc\x8clF\xce\xab\xa7\x1c\xa5W/\x0e\x938b\x91p\x8e\xb5\x17s\xe6JW?\x9e\x05\x10\x8a\xa5\x07\x9e\xe4z\xa3<\xea\x8b\xda\x1c\x19+\x19|\x03\xc9E1\"x\xd7\x83\x88{\x1f\xca\x05v\xbb^\x8e\xaeV\xfc\\=\xd8\xa3\x0eA\xfd\xa0\xe7\x08\x83\xe8\x98mto\xd7\x05th\xbe72\xce\xf7\xd4\x06\xd9@`\x1aV\xcf;F\xd7\xc8 {;T2\x890\xb0}\x0f\n\x9fu\x90\xbeB\xd0\xa6\x91\x8e\xa5\xdb\x0dv\x1c\xc7\x83\xc0\x17\x02w\x94b\xa7\xe8\x00)\xc5\x00&y\\\x8e<\x14K5FH!\xc2\x87\x0dHR\x08\xef\x82\xbaP\x07\xfc\xbfr\xbf\xfd\x83,\x14?\xfe\xbb$\x0b-\xcb\xae\x0d\xab\xff\xce0\xc6q\x1d\xbe\x801\x8e\xaf\xff\xc0\x18\xf8=\x04cj\xe9\xe4(F\x82\x0c\xa1\x13\x0d\xfd8\xf4\xffCh~'0?\x94\xd4\x1f\xa2\xf1\xff\n4\x1d\xb6]\xf9\xd2\xe4\xc5}IU\x98w\xaffS\x0b\x83#&jf\x1e\xfez<\x8e\xeeQ?\xbf^s\x86\x07\x04\x943\xcc\xc5\x85\xef\xa1\xde\x97\xa6>N&\xcd\xd6>h=A\xc9\xbaZ\xfb\xf8\x07\x93|\x18\x99\x95\x1d\xda\x12:\xac\xe25\x8c&\xb6\xbc\xca\x84\xd0z{\x1a\xed\xf1D\xcb\xa3\x890\xca|\x16 T\xa6{~\x19\x9b\xbc8\xd0\x7f\xb6<\xce\xf0\xc4+W\xef\xe7\xa7]\x82\x1a\x1cZ\xe39\x18\xf3bNE\x8cZ}d\xe9k\xa6$ d\xf2\x1b\xd4\xf3\xfb\xf8\xdd\xc7\xc32\xcc\x05\xb5\xb0\x80\x99S\x0b\x06\x03\xb6\xf1Y\xb0N\x99\x8e\x11\xb5-\x00\xbf\xf1\xb7\x19\xd72\x01\x96P\xb2\x81>\x1b\xd0\n\xf1\xdd\x14\xfe\x05yl\x87\x87k\xa0X\xde=\x87\x7fA\xe9\xaf\xd6\x83\xf9\xab\x0f\xe2l\x9f\xf3\xf5\xa3\xfe\xc2,\xf8!\x0c\xbf\x1f%x.\x88a\xdbz7+\xa8\x04\xacw\xe0\x81mY\x84IP,\xa4x\xde\x12\x9aC6\x08\xe5\xa6\xfe\xfe\x94\xe1\xf1I\xc8\xa2\xcc\xfc\xf5\x05\xf6>d\xbaC\x11\x9e+F1\xce+\xceN\x9c\x08\x0bil\xc7%\xce\x84\x06\xcd\x9c\xad\xe1\x9fxk0\xef'\xf5\x0f\x9e\xe9q\xc8\xc8\xb3\x15\n\xb6\xf0\x0f\xb5\xe7\x00\xa6\xca\x94\x05\xfa<%\xdd\xd1u\x0c\xc7IiH\x03\x80\"\xd7\xc9\xa7 \xf5\x10\xdc4\xa1XPp\xff\x86\xe9\xa7\x18\x89N*\xee\x11\xdb1\x08]/\xcd\xc2\x90\xe2)\x05\x06\x9d\xd3R\xa7z0\xd8,`$\x05\x0b\x93@\x1f8*\"`V\x90P\x13\x0f\x0f(\xb4\x9a\x195gG\x82\xe3\xbf\x14)\xa0\x80\xbc0\xd6\x19\xf4`\x8f\xc7<{\x7f\x8d\x07\xb3\xb7+\xdes\x04\x8a\x03\xa3\xb0^\xba\x87^\xe0\xd2\x0d\xc46\xb8GQ\xd9<\xafQ.5\xaff&i\xe4\x87T0/\x0epm\xe8\xf706c\xac\x13\x04\xa7Qj\xd0\xd7\x92\x81\xc2\xea\xf5\xb9&\x16^\xe0' \xc5.\xaf\xd9F\x0b\xd1)\x9c\xe5\xb0 \xf0\x93\x14\x17\x87\x1f\xd8E\x81\xcb\x04\xcf\xcb\x0c\xdc\xf0`\x84\xe9\x1b\x86G\x9a\xda\xf6\x1e\xe8\xaf\xfdK\xf9\x96\xd3\xb5\xaf\x97'\x9cnq|J\x11\x97\x99\xa0\x862\x84\x06\xb2\xc2_\xa1+O\xe2\xe0~\x1b\xdbG\xcb5\xe9\xda\xa7A\xb1 n\x90N\xe01q\x8e9\x10\x01\n\x9e\xee\xc3U\xac\x0fq\xef\x84\xf9k\x1a\x05\xabzx\xd0\x1d\x14\x061\xed\\\xef}\x06\xe8\xbc\x87\xae;f=\x82Y\xdf\xb0\xdf\x06z=o\xd8\x97j\x12_Q\xc1\xfd;\x93\xa0\xc5\x88\xd70{z\xb819\xd5\x94U\xbdF\xfb8\xd8\xb3b\xc9\xdf\xf9\x9bM\x96\xb2o\x958\xa3\x99\xb2JL\xed\xde\xf3\x15\xd2\x0bH\x144\x12\x90\x13S\xbe\x0e\xe2XC\xf4u\x16y_\xe4\x8f\xbf\xcd\x1f\xff9\x7f\xfc\x1e\x1f\xff\x99fi\xea\xd3\xe8\xb7A\xa6\xe1|\xc5\xf8\x96\x15\x1e\xff`E\x8aW1Ovq\x10o\xef\xf1\xfd\x8f\x9b\x8d\xa1\xc5\xa87,\x80\xf3C\xc2\xbc,\xa0\xbc\xdc\x97\x1f\x92\xb8\x98\xe9\xb5\xb1\x84`\xaf3\xbe\xca\x02%\xb4\xb8F\x1d\"r\xf4B=\x8f!\x8b\xb4e\x89z\xe6\x1c\x97P\x08\"\x0f\x9a(l8\x05\xc4\x0f-^\xe3\xe9f\x08\x04\x99\xad\x91\x04\x84a\x16\xf8h\xea\x81\xa7\xb0H\x92\xd1\xd8!\xdektN\xe8z\xad\xabMv4\x121\x92b\xae\x89L\xc8\x91\x00\xea\x83\xdc\x04\xa8\x1e&\xfc\x84\xe44\xbc\xb7\x98\x1aj\"\x17j\xd2\xa6\xde\xcd\xa3%s!\x92\xb7\xd0\xa0p\xa8\xa1\xcd\"\xcd\x90\xf0 \x00t\x8cU\x0cc\xf5k\x14\x8b\x1c\xd2\x1a\n$\x9e\xc7\xb4m\x80%\xeb4\xf0\xb7\xfa\x01\xbfd\"V\x12q\xc0\xb4,A\xbd\x1b\xc5`\x10\xefW[K\xbcV1\xd7\x90y,\x08\xd4x\xe9\xf9V\xafj<\xcc\xeb\x8ey78\x94V\xc0\x08(2!/`Hvm\xad^\x8cB\x82\xfa\xab\x97\xa9\x17\xc7|\x8d\x89\x9a:A3\x8a!\x8cW4e\x86g\xd2\xd436>\xe6L\xcf \x84M00\xd3w~\x98!`\xaa\x8a\x8d\x9a \x16y\xf7&A\xd59Nw\xfe\x06\xea[1\xbd\xd2V>\n\x1e(!\x16\x96/ZB\xa9\xbfc\xc3o\xe1E\xed\xffz\x95u\x1d\xf3\xb1Z <\x89\x03j7\x1f\xf5\xe41\n+i\xfe9\xe1\xb11\x9e\xc3\x04\xce\x14)4\xf4\x05f\x07\xbb\x80\x8b\x1d\x12Pf\\#k\xf5\xe2\x08\x18'&\xf1\\\xa8]\x03\x97\xd5Y\xf7~\xaa\xf7,\xc8\x14\xd9z\xcbB\xcd\x06Y\xc0\xf6\x16j#\x04\xf8(\xfc\xaa\xbf\xe3XQ<\\\xf9\xf0nF\xa0 z)V=\xb6#\x82\xaf\xc5bq$\xc6\x1b\x1a\xfaA\xfejP\xdb\xbe\x8c\xe9\xfa\xc7,\x15y\x9a\xe0L\x8bA\xfa]c1\xbc\xed)\xf7i\x94\xe7\xbe\xb5h\xb6A\xd9\x03Z\xda\xc2\x06i\x0b\x1b$`\x9dc\x83?E\xb9\xd0\x08eY\xe4#\xe34 %i\xb5@8u9M\x1a\x950Y\x9e8D-?\x82va\x99\xdf\x00 7\x98\x00;\xb5\x1b\xd8\xa9)\xb1L\x17\xbaa\xf7\x89\x929R\xfd\x92&\x10X]\xbf)n\x00\xcf\x96\xd4\x02%\xcd\xc7,`\x8a\xd6\x8d\x0b\xecI\xd5\xcd\x82\xd0\x8ac\xf8\xae:\x99S\xe1@K3\xf9\xe4\x05\xb16P\x1c\xb3\x84\xef\xbc\x1d\x8d\"\x16\xa0\x00\x84=\xbdw\xa4Asw\xd0\x8f;\xe8\x07\xca\x1f*7\xfc\x03_\xee\xe1\x0b\x18|\xbf\x8b\xe3\x90Fk%09d\x94\xac \xa3\xf4P8\x81U\xaa\x97\xb4\x15{Vl\xcf\x02-k\xdbM\x9a\x17\x07Y\x18\xa56\x13\xbe[r\xad?kQm\xcd\xa28\xb4Y\xd7,\xd1:\x0d+\xcb\xe7l\x1a\x1es>\x07\xbbG\xf5\xc05ykbA\x81\xc2\x1f-q\x17H{\xc4\xc4\xce\xf7n\"\xad\x17\x0b\xecV.\xb0\xfaT\xb5\x05-\xef\x83T\x8a]g\xea\xc50j\xf5\\\xe0\xba!\xbd\xb3_\xfc\xc8>\xc6{\xb55\x81U\x03\x8dFqNL\xa3,\x1f\x07#\xad\xf3\xf8\xd6\xa6\xf1\xf8\xd6\x8e!\n\xcc\x06w\n\xe23\xb7\xbd\xe0\xb6\x17\xb8\xe7\x05\x03\xc5\xfc\xb5\x00\x95\xde\x13\xfb\xef\x98\xde[\xf8Z\x8f\x07\xe8e\xb5\x80 \xb5L\xc2\xbeh\xe2\x03\xa2\x88V\xe2\xe9 \xffV\x96L\xb3\xa4\x9ar\x1f\x86Lp\x1f\xe4\xf1}N}\x0e\x8b\xcex\x83\xe3.\xf0\xa3\x9b\x99\x99\xe3\xbb0\x98i\xebzH\xb7\xe2\xba\xfa`G\x03\xaa\x9cA\x8e\xde\xb2`?I\x8a&\x8f\x81\xd3\n\x89T#7\x9b\xab\x9d\x17$\x1a\x8f/\x06\xa8\xe8\x8c\xb6=ru\x05\xa6\xa6\xf1\x86\x88\xb9\xb9}:\x87[\x98\xeaO\xe5f\xd9\x88\xb0\xb9J^6x\xdf2\xa6\x9b\x95\x83\x0d7\xe4^\xbb-\xae\xebp\x93h\xf5\x16^\xa6\xad\xb7\xaf\xbdc\xfb\x11a\x03\xf2\xc7\xd5\x8f\xcc\x13\x85\xf0\xf2;\x9a\xfe\xf16\xfa\x8e+\xd1A\xdcO<\x1a\xc0\xe0i\xcf\xd1\xba\xd7l\x1e-\x1d\x9eT\x8c\xc9N\xc3\x91\x0d\xd1\x80o\xc0\xbb\xdc\xcf\x8b\x9f\xe7\x8bt\xf1\xc3\xf2\x89\xd4\x7f\x17\xef\x17\xefO\xb7a\xbdG\x89*p\xf9O\x95\xec\xff\xf4\xd2\x99y\x0d\xd6jk*\xe8x\xbe\x18/n'\x8b\xec\xec\xec\xb7\x9f\x8e\x17\xd9\xd7_\x7f\xfd\xf5\xf2\xd4q\xf2\x08%\xd4\x12\xc7\x12\xcb\xe1'\x8e\\{\xc8\xd5\xbf\x9e\xe1\xff\x1b\xb9\x13\x03\x91\xa4\xd7\x12o\xd6H\xc1\x02\x89\xd7-\xa4\xe7\xaf\xe5]\x98$\x83\x99\x9c\xbf\xa1\xe3wK9\xa7\xe3w\xc3\xc9b\xbc\x1c\xf6\xafg\x90\xa6\xdefK\xf9\xc9`P5\xb7#\xda\xb3\x154\xb6\xb8\x1d\xe2\"\x93`\x829se\xde\xaa\xccs\xd5\xcd\xb3\xb3\xb1\xfas~\xa6\xfe\xfd\xe2l\x91M_|\xa6\xfe\xfd\xec\xec\xabEv\x8e\x9f\xcf\xcf\xce?W\xff>\xdf,\xb2\xa7ggg\xcb\xd3m\xbd\xca{rEz\x06 \x8b\xf8\xff\x03hf\x15.\x18%m\xed\xe3D\xc9\x0f\x8a\x86\x90\xeb\x03\x16\xe5\xa4\x803XC\xdd\xa9\xee{2\xeb^\x0b\x03\xc0\xda\xe1f\x13\x10\xd1x\xa6\x18,\x18\xe1\x15\xbe\x81M\xa1\xee\x86]\x13\xe4:\xef\xec\xac\x05\xd2&\xea\xb3r\xc3\xedoH\xff\x0b%\xb5M\xfc\x14\xfe\xf6Y\xa3\x85\xa1%Sj\xd1\x9f\xe1=z]\xc6\x98\xb0_\x10\x01\x11\xe7\x0d \x13\xc3\xe1\x80Ds\x81\xebU,\xeb\xcb\x95\x14\xdc\xf5\xd5{\xd3\xb4\xba\x11\xe4\x0d\x8f\xc3vG\x80\n\xda\xb7m\x07\xae\x85:{J\x00\xd9\xf8\x11[\x17\xe7\xec\xd6\x8f\xd6\xf1-\xb9\x06{\x002\xd3\xef\xe5&\x9d6\x83v\xe4o\x9d\x8d*\xc8\xbe\"W\x84\xf2m\x06\x86`&\x92\xfcK\x8c\x0d_\xf0B`\xb3\xcc\xcf\x96\xe4\xba\xfc:#o\x9b\x02\x9a\xde\x95\x0c`\x9b&\x95\xe4\x10\xdfV\xc7\xd2\xfc\xde\xbb\xbd5\xdcM\xf6\x8c\xa7\xaa\x8bW\xa47\x9d\x9cM\xd4\xae\xfan\xc2Y\x18\xef\xd9Z\xc7\xbd>\xf9\n\x9ck|5Y\xc7\x1e\x80\xad^?\x87~\xe5i\x93(^\xb3\xd7\xf7 \xb3\xb6\x9bw\x13?\xfd!K\x92\x98\x0b\xa8\xead:\"wu0\xd4(\xfe@\x8aU\xb9\xc7\xe2\xcb\x06\xbf~\xeaw\xd3\xf2\xed\x8b\x0eu\xff\x11\xf2\xfcN\xe7\xf9\x9a\xd3ms\xde\xef \xef\xef_\xbf\xfa\xf6\xb5>p\xfc\nO\xa5\xdd\xd9_C\xf6?\xd4,\xad\xcd\xef\x95\xfd\xfe5\xe8\x83\xdc\xb9\xbe\xc1\\4dk\x95\xf5\x15M\xdc\xf9~\xb4\xfc\x1a(\xd27\xe4\xbaRLM\xddW\x93W\xf1;H\xfcB\x08\xae\x12g\xe4\x1bw}\x7f\x80v_\xb3\xbb\x86\xde}\x0f\xdf\xbfD\x8b|w\x96\xdf\xe1\xd8\xfe\xf1\xd5wp[\xda\x9d\xe9[\xc8\xf4?\xbf\xfa\xf6\xf7B$\xdf\xb3\x9f2\x966T\xf7\xa7r\x0f\xbf\x85\x1e\x96\x0b\x92\x19\xf9\xd6]\xf8'h\x86Ej\xff\xf6\xa7\xef\x1b\xfa\xfcu\xb9\x85\x9f\xa0\x05[\x86\xcc\xc8O\xee\xb5\xe4\xe4\x17\xdf5-Z\x85\xf6\xef\x14\xf5\xfd\xff\xd9\xfb\xda\xae\xb8m%\xe0\xef\xf7W\x0c~zR\xfb\xe05\x90\xa4\xb7\xed\x06\xc2!\xb0ii\x03\xe4\x02i\xdaK\xf3p\xcc\xaev\xd7\xc1k\xed\xe3\x17^z\xcb\x7f\x7f\x8eF\x92-\xdb\x92\xec%iz?\\\x7fHXk$K\xa3\x91\xe6E\xa3\x99`\x9c\x92\x8a\x88\xdc\xea\x18\xdb\x10\xc4\xff\x8f@\x98D\xd8\x16S\xfe\x08\xe8mBRI\xc1(c1\xc27\x94\xdb.\xd5\xc8\x87u\xf0\x15\xeb\xa0\x1eK\xbf\xc0\x0e\xbc\n\xa2\xc5\x92\xf7\x1b\x95\x14=\xe4\x8f\x08\xc9G\xc9\xa8\xf0P\xb0u=\xf4{\x84\x9e\x91\\ ${u\x7f\x1e\xce\x18\xb5\xea\xe1\x7fRZ\xef\xb7\x80\x7f\x83\x1d8c=\xa7in^\x97?\xa3T\xdc\x9e\x82\xe6\xae\xf6Kc\xa7\xffE\xf4\x85m\x10\xeat\xf0\xfdr\xaf\xdc\x88\x8e\xe8Ds\xf7\x8d!\xfd\x07\x8c\x8c\xa6\xed\xd4W\xb0\x03\x86\x95\xffo\xd8\x81\x89\xbe\xe8W\xd8\x81\xb9\xbe\xe8_\x18wM[D\x08\xec\x80F\xa4cON0(\xa0\xb6,aez\xcf;@F\x05;\x10\xbb\xffy\xf0\xe1\xe2\x03\xa3\xceq\x98\xbbW\x188\xeb\xca\xcd\xf1\xdf\x04\xffM\xf1_\xeay\x06\xdeH\xed\xdf\x89\xf4\xdf\x89\xb0\xd5\x10\xff-\xf0\xdf\xcc\xf8\x85\xd0\xfe\x85\xc2^\x9c\x11Cb\"\xc0[\x81\x96\xc21\xb1\xb0\xb3\xa9\xadpi+\x9c\xd8\n\xe7\xb6\xc2\x1b[\xe1\xc2V8\xb3\x15\xde\xdb\n\xafl\x18\xba\xb4\x15\xde\x12\x8bB;R\xc8\xa2r\xa0\x91.A\xd2\xa3\xa0\x8a\xf7PZ\x93T\xef\"\xe1\xe4\xc3\xbdD>\x98d7\xed\x97J\xcf\x12\xe1(V\xb9Gq\xa7\x1aSkg\xb5\xd6\xb8a\xb99}uh\xf8\x98R\xc6*\xb1\x97\x85ZI\xfb)\xa5LVB\xfaw\xde\x9d\x8d.\xdf\x9e\x9e\xbc>|3\x92\x9fz\xf2\x04\xa6\x81\xfa\xde\x17\x9b\x14\x0f\x82'\xfa}\xb9wz\xb8\x87\x0d\xfab\x9b\xaa\x17\x1f\xec\x9d\xcbb\xdc\xa8\xe4\xfbw\xc7?\x1f\x9f\xbc?f\x8d\x9f\x9f\xec\x9f\xbc9C\xa5a\xcb\xe7;\xd648\xdb{=\xba|}rz\xf9\xd3\xbf\xde\x8dN\x7f\x93\xa5\xcbF\xe9\xf9\xe8\xe8\xed\x9b\xbd\xf3QY}\xc2\x01\xde\xffx\xf2ftyp\xb2\xff\xeeht|.\x0b\x17\xbc\xf0tt\xfe\xee\xf4\xf8\xf2\xe0\xe4H\x16\xcc\x9a\x05\x97\xafO\xf7~P\xab\xde\xb7 \x0e\x8f\xde\x9e\x9c\x96\xe57\xbc\xfc\xf5\xc9\xe9\xfe\xe8\xf2\xd5\xc9A\xd9\xe3\xab\x1aR\xce\xf6\x8e\x0f\xcf\x0f\xff\xcd\xbav\xe4\x8b\x8dI\x96\xfd<\x1a\xbd\xbd\xdc?9>\x1f\x1d\x9f\xfb\x9ciV\xc4\xf1\xee\xf4\xf0\xf2t\xf4\xc3\xe8\xd7\xb7\xac\xe1\x9c *0\x0c\x11\x91i\xd5f\xfc\x05\xdfa7=\x9cZ\x0c\xecI\xb4\xbc\x0dy%\xa7OT\xdb\xf8Z\xb8%Uh\x80\xd8M\x88\x0f\x8c\xd7\xc6.%>D<\xb3\x97\x84\xcbnf\nX^\x82\x85\xe5_Y\xab\x02\xd7Z2\xa5^\xd2]\x8f\xed\xb3Gj\x97\xd2\x12\xb2P\xebx\xb8\x9a\x0e\xf8\xa2(\x87\xbe\xb3\xc3\xa4\x88\x12\x11c7!\x1e\xd6b-U\xf0UmF\xad\x08Oy\xed\x88\x94\xbf`\xecRQ\x9b\x12\x15\xbe\xaa\xcd&\n\xc9S6\x13\xbbgD[\xe8!\x01\xf0\x8e\x95.Wr\xee\xb8\x85\x94\x1b\x96RB\xfe \xb8*\xab\xb7\xc2\x82\xca\xcb\xdc\xa9\xe7\xf3\xadu\xaa\xdd\xfd\x0c\xdc\xed\x84\xf46\x18\x94J\xbe)&\x82\xfa\x08\xbf\xeb\xa1\xc6Z%\x9f\x07K\xce\xb1<\xbd\xb7\xf4\x04dv\x08\x92\xa0<.:\xb6?\x8f\xe2\x89\xc9\x9c\x01h\xd1\x1b\x87\xf9x\x8ey8\xbaZ\xa7ENR&\x92c\xe8rs\x93\xab \xfb-\xe9\xba\x9e\xac>\xdd8XiF\xd8S\xfa\xf0\x0c!g\x1a\xd3\x9e\xfc\xcd\xb0\xc8$\xea\xce\x16\xa6)]\x0c\x1bv\xf6\xe6\xf3\xd0c\x06\xac\x94\x06\x9f86\xb3p\xa1>\x9f:\x14\xf3\xc4\x89\xae\x97\xd85\x9a\xd8\xf4\x9d<\xef\xbf&\xa5a\x96K2\xf61\xdbNf\xe4\x13M\xc1\xbd\xe1\x1b\x12\xca\x04\xdb|$/\xb77\xc4\x1f\x0e\xac#7\xb8\xee\x9a\xbfn\xeae\x0f\xfb\xc8k\xdb\x92\x85&\xd1\x98\xd1\x0ej\xb4\x03r\x0b\xef\xcc\xc3dO\x1a\xa4$[\xd2$C\x1b$\x1b\xacT\xb4\x1d\x1f\xd2\x80.I\xe2:?\x8c\xce\x1dq/e\xc86\xe7\x0d\xc6\x18_\x8c\xe7a\x9a\x91|\xa7\xc8\xa7\x83\xef|D\x89/\xd2\x9a\x06\x19I&.#@\x8fGE\xa9>\xf3\x08Jb\xd3\xb1\xef\xf5\xc0%\xfb\x92\xcb\x06}\xe0\xf1\x18\x83\xafS\xba8\xc33D\xb6\xcf8e\xdf\x9d\x9ek\xd3\xdc\xa7\xf2v\xfc\x93'\x90\x97\xc6 !\xa8\xe3\x95y\x9e^\x94uIg\xdap\x1d\xc7\xf3\x82+:\xb9\xf7L[x\xa2\x16L\xa34\x93\xcdc1\x13\xc4k\xdb3\xa3\xc7\xf7\xfc\xbc0G\xe9oW\\\xb1\x81\xa1\xb8\xbf\xe4]l\xb6\xefw\x81\xde\xc8]7\xd70 \xd8v\x8c\x00\xca-\xads\xe2~\xbd\x9d\xdd\xcc^n\xcf\x80\xa2\x8f\xf0\x0e\x06~k\x0f\xd3\xf5\x9c\x97\xdb\x1b\xb3\x97\xdb\x1b\x0c\xfck\x03#$\x01\x86\xdb:\x13.\x19.j\x91\x18\x82\xc9\xbd\xe62\x82\xbe\x9e\x9d\\\xdczW\x97/\xb7Qo{\xb9\x1d-f\x90\xa5\xe3\x1dg{\xa3\xf1\xe6\x0eh\x82^\xf2;aL\xd2\xdc\xdd\xf266\x9c\x97_{\x9e\xa6\x83\xc0\xd4T\xae7\xed\xf3N\xea\x11o'\xb6\x07W36\x86\xe7\xa3\xfe{\xa3 \xd4\x1f\xc5Ir\xc3\xde\xf9\xe7\x9fl\xd1\x12\x1f\x8e\x82\xb3\x1fO\xde_\x8e\xde\x8c\xb8\xac/_\xec\x9f\x1c\xd5_\x9c\x8f~=\xf7\xbb\xa9\xa1\xf1\xf9\xa3\xe0\xf5\xe1\x9b\xf3\xd1\xe9\xe5\xde\xfe\xfe\xe8\xed\xb9y\xf5\xd5s.\xd5\x8b\xb4\xaf\x0fWFE\xa9\xfd\xee4\xb4\xdfs\x8d\xf6{\x8e\xb1l D\xe8U6&t\n\xe70\x14\x07\x9d\xa6\x86\x88\xa6!\xc2\xd5h')\x16W$UM\xdd\xa4<\x02\xe2\xc7\xba-\x9f\x07\x0ep\x1c.\x0c)O\xf5\x88\xf9\xd8\x12\xb3\x1a\x973\x9b\xcf\xcf\x17\x04]+\xd8\xff\xc1\x94\xa6\xa3pN<\x95\x0c\x8eQ\xfdT\xdf\x9cb\xe8/\x8d\xcfJ9\x7f\x86 \xce\x03\xc6\x99\xf6\xab\xe3 \xed\x91H\xaer\x07\xcewJi/S\xfb\xf1\xb1\xb3\x89R&\xb3@f\x8a`\\\x05\x969\xe1\xb9\x1al\xf9\x7f\xa5\xf4Q\x91m\xddA\xa7{J\x8a%M\x1a\x13\xc2\xe7\xa3\x83\xfd\xf3\xf3\x8e!\x18\x8eH\xe4\x13\xc61\xbd%\x93\xf3p\x96\x0d!\xb1\xa9f>\xac%\xe4\"\xfd\x80\x01\xff\xd8\x1f]\x8b\x80\x8d\x80\xab\xb2k#\xach\xc2/ \xa2$#i\xbe7\xf9\x18\x8eI\x923&\xdeG\xc4\x01\\i\xed\xba\xae\xb37\xcdI:Bg:\x06\x90p\xc1\xe0\xb3\xc9\x94\xcd\xf97c\xadk\xff]\x9b\x12\x1eT\xb0%\xd3\xf0\xd7\xca1]\xf9C\x0f\xbb\xb6\xb1\xbd1\x0br\x92\xe5.Q\x97\x10\x97\x0eV\xd2\x9d*M=\x18\xc74\xe1\xaa\xa0m\x03\xaba\x99'9\xa9:P\x06\xe8c\x1d\xf4\xc1y\x12\xe7/\x1c\xcf\x93\xa6*\x99\xeaA\xdd\xf7\xb9\xb8X\xfeS\x1fO\xd9\xde\x0f>8\xc0$G\xf9\xe2+\xfe\xc2\xafW\xa8\x82J~\x01,\xa8\xdf\xdd\x81\x84\x0d\x93-\xe2\x90\xd1\xa3}[\xddZ\x85\x0b\x9c\xae\xc8\x05V\xd6\x07\xedpiO8\xda\x13.\xea \x17\xf6\x84+\x1e\xcd\xf2\xca]\xbe>;<\x82j\xc5a\xba\xb6>\x86\xf4v\xcc\x15\xdd\xc3\xda\xe4\x1b\xb5.\xa0\x89\x0e\xfa\x970.z\x82_\x13\xb2d#\xd2\xc7ki>\x82\x15T(\x18\x0253\x04\xd0\xebJ\xea\x83\x8ebl.\xc2\xd2\x11\xac@_\xd6n\xb4\xc8\xec\x92(k\x84\x17\xc5\x07/H\xc2\x05\xf1\x91\xf4\xf2\x00\x0f\x98\x82<\x8d\x16\xae\xe7\xf3\xa0\x85u\xbe\xeaC\x16H\xd4\xf2\x04P\xfc7\"\x8f'\xeb\xc8\x02\x89\x1e\x91J\xb3\xc9m\xf7\x94\x18\x96hJ\xe6_W\x1a\x92\x07d\xb8\x85Q\xe4o\x87G?8\xca\x8e&\x05\x9d0\x88&\x1e\xd29\xfb\x8b\x13\x14w^\xab\xbc]1\xa0]\x10.\x97\xf1=\x1e.\xbf%.?\x8e#\xfcG\xc2\xff\n\xcbL\x12\x91\x07/\xa1\xe0\xbcA\x95PD\xb5\x88\xa3\xc9\"c\xc8\xc7\x90\x12Q\xf7\xa0\x93\xca\xe1\xf1\xdbw\xe7\xbaa\xf2\xbb\x0e\n:\xf0f\x1d\xb7\xb6\x0bs\xf9\x05E b\xad`\x7fy\x1eF\xc5\x8d\x92B\xe3\xc7\xa0{\xd8\xc8\xb0\xb9D3\xec\xc4\x07\xc7Qp\xd5\xd9\xa2\x9d\xcb\x83\x18\xaeB(\x18)\xf8\nY6\xf6d\xad\x1c(\xa7\x03\xfe\x9b\x0d\xcfM!J`\x8f\xfd\x8d\x7f]\x13\xcf\xe8P\xd9|\xd8G\x05#d\x04\x87\xff\xa4\x9dl\xcf\xc3\xa3\xb6'O\xe0\xdf\\\n\xa0^\x8f\x99\x079\xfb8P\xac\xfe\xebc\xaa\xf7\x1b\x18\x88\xc1\xad\x95d\xc0\xa9`E\"\x00\xd1\xcc\x19V\xee_\xa7\x1chN\xf8\x18+\xa4\x12\x82\xb4\xd3w\xcc\xa0\xb6\x86\x97~\x15RPn\x0eT\x04\xc1\x1d{\xaa,0\xdc\x80\xc8m7kw\xe4\xc2\xa4 |\xe8\xa6b\xf5\xc1\xb0\xa2\\\xe6\xfe\xd7g\x18#\xa8\xe3L\xaby\xea\xd5@\xf7\xea\x82N\xd3T\xf3i\xaf\xf8\xd4\xf3\xd5\x93\x01\xba\xb4\xc8h\xea\xb3\x82\xb8\x0f\x9d\x83\xb1\x97\xb6$@\xad\x94alb\xa5\x03\xa5\x03U2\x04b?\xd7\x92wM\xfa\xc8Tl\x13:b\xed\x99\xa9\x07\xf9}[\xa6:\xc3\x80>\x07'G\x0e7\x87\xb0\xc1\xbe\xc0\xef\xa6AB\xeer.X\xbf\xf0Z\x0c\x98W\x14\xa1B\x92R\x18;&n\xc2\xb5\x9a\xa4\xd4\x8f\x14\x8d\xff\x049CU\xe6\xf9p\xcajX:\xde\x9a ]\x97\xf5\xb3`\xbcxr\x17d\xa2\xb1\xbe'|}g\xa3\x8f\xf4\xddG\xf2\xee#u\x87\x1d\x924f#\xe4Qqa\x07\x9c\xdf\xef\x9e\x8d\xd7\x06\x83\xdf\xef\x9e\x11\xc6\x88K\xf3\xceZ\xa5\xeb\xe3\xdetH,\xf7\x0b\xa0\xed\x0b\xab\xd4\x0fr\xcaO1<\xc8\xe7)\xbd\xc5\x83\x1d\xa68\x8e\xd2\x94\xa6\xae#\x8b!\xca \xa19\x84%\xf2M\xce\xb0\xe5\xf7Z\xbd\xc5AU_t\x19\x0b\xd7~t\x12\xa5\xf9}\xf5E\xde\x90\x0f\xe1\x15M1N\x8d\x81x\x8c(]\xab\x1d9t\"J\xb5\xbd\xde\xbb#\xecp\x98GcnHa\xc2\x8a\xce\xec\xd2\x84\xeb\xb6\xe6\xe8\xec\xb1\xa55\xac\xde\x9c\xdb%w\xb2\xf6\x04\x19\x18\x1a\xa8NtV\xdd\x1b\xc1t\xb3M>f\xcc\xcf\x91\x9a\xf7\x08\xba\x916/1\xd4M\xdf\x1e\xf0,\xbb\\HK\xf8\x19J} x\xf5#\x06\xc5a\x98\xed\x04k\x9b\x9eW\xb7w\xbf:9\xf8M\x88\xcb\x95\\\xbd\xcb\xf7J\x18B\xc2\xb4\x03\x92L\xf8\x99Xj:$\xb2\x0bdH_\\\\_\x9b\xe0\x7f\x03\x99-\xb8\x14N\xb6\x1d%\x7f\xb7}\xd5\xac\xc9\x91\xa3\x80+\xea\xf0^\xf3\x9b2\x06W \xfd\x14\xf0\x93\xe6\x13\xb6}\xa3\x95\x8b\x1f\xef\xe9{P\xdeC*8kJ\xbc\x17\xb8\xef\x15u\xae\xc2\x0dL\xb4\x86h\xca]x\xd8T\x1f\x13\x97rnB\x8d\xdc\xe4\x80T\x85\x9c\x9dP\x91\x8c\x98\x1a\xfa\xc60\xb3\xb0\xdae\x18\xc4\xacCG\xc1\x11\xb2-\xf8'~\x9e\x904<\xf0_\x80\x8a\xa6\x17\x1e\x845\x02\xe9\x81C\x90\xf4\x82A\xfb\xcd0b^\xef\xb9V\xc2\x80\x7f\xe3]:\xf3e\xaaK\x1f\xc2\x15&Z4\x88G\xb3\xea\xd9-#\xf2\xd2\x94\xd8\xaa\xf9\xc0\xd6dF\xf2}\x9aL\xa3Y/\x1b\xd8\x1e7\xd2r\xdfdMly\xd6\"\x06\x8aj\xb7ij\xb2rW\x95.\xcf\xfaf\xc3\xc9\xe4GJ\xaf\xfb\xf2\x7f\xfd\xd9\x03\"\x1c\x8f\xa3v\xf8\xa9\xd4\x9f\x7f\xe2^\x84'Sh\xc6\xcc=\xcdU\x8cj\xf3ju\xc1\xf4\xfd\xda\x99\x97^\x90n4\x9b\xad\xd4\xae\x1c\xc5\x85F\xa7Q\x1a\xde\x8b\xe3V\xdb\xc6\xa6\xd1\x0fW\xdbZ\xed\xe5\x832\x16\x9e\xce\xb6\x0c\x8b\x9c\x8a\xa2G\xc5W\x16\xfev\xfcpS\xdeSvs\x1f\x9c\xcbK\x92\x1d\xd1 \x0f\xd3S\xef\xfc\x0d7\xe0\xa9\xa9\x02\x94\xd5)O\x8cb7q\x9f7o\x15PQ\xf0\xb4Y\x10\x89\x82g\xcd\x82P\x14|\xd3,(D\xc1?\x9b\x05\x99(\xd8T%f\xf6b\x8b\xbd(\xdf\x94:F\xdc\x9ey\xf5\x06, *T\xe0\xe9\xb1.\xa8\xaf\x88\xaf\xd6\xf4\x0dlF\xd8\x05\x81\x9f\xb1\x95\xee\xca\x9e\xe5\xb6k\x9e\xee\xa6\x0f4\x10\x1f\xf6\xdc|\x1ee\xdc]\x95\x15\x84\xcd\x027\x0f./\xd1Twy\x89\xccb\xd3\x87T\x01\xf2;\xd3\x88P\xd0%\xbb>\xba\xaf\xab\xe0\xc5\x82\x93\xb4\xb4\x88\x99 \"[/\xaa\x8554]\xc3\xe4`lM\x0dM7<\x01\x0f\x0e3z6\xa7\xb7f\x92[Zmh\xe6\x01,;\x87\x18\xf7Et\x94Li\xba\xe01 ;\x88\xc2\xd2\xa1\xb1\xeds\x0bz\x15\xc5d\x08[OWm\x96\x8aqz\x96\x91N:q1\xed\x84\x98wB\xc4rg\xf8D\x0cXx\x08\xc9\xaes\xba|\x0c\x9a\xc2\x1eh\xfa\xaf\x1e@Q\x0e@\xa7\xb3\xd5\xde<|\xf0|\xe5*\xc2\x83[\xb5Y\nS\n\xa3\xcbe)\xec\xc0\x18\xdf\xfe\xbd\n\x8d\x0fy\xf0SF\x13\x14\x15\xc2Kn\xa1D&\xad\xbc\xbd\xa24&a\xd2|\x8d\xe1\x03\x9b/\xb9\xe9\xb1\xf1\xf65M\x17\x1a.-u\xa8{\xa6*\xb5T\"*KZ:Q$JZzW(\xab\xe8\xb4\xa8{\x9d\xde\x95\x89\x82\xd67bQ\xd0\xd2\xbb\xb8\x94\xd7\x14\x88\xa6\x08>n\xbc]\x8aF\xb6\x9a\x8dp\x01\xed\xdb\xc6\xdb\xb9\x04\xdfj\xf5\xf3F\x16\xb5\x86\xb6\x90%\x9b\xdf\xb4\x061\x13\x89\x8a\xb5\n\xe1\xfd\x97U\x08\x97\xe5\xba`=\x08\xa2\xecT\x84\x85\xf6\x95\xa20\xb9\xf7\x1b\x90\x96bN\xad\x86\xa6x\xa1\x0f7\xe5\x9b8\xcar\x15\x82\x91\xb5\xedw\x98\xdc\xd7i\xf5\xaa\xe5*t\xa3w\xf2\xa1\xc9\xfe\xf9\x86\xb6]\xcd:\xff\x1c:\x7fK\xb5\x97:\x7f\xd6,\xd0\xe9\xfc\xaaF\xfe\xa9:\x7f\xac\xb4U\xe9\xfcuK\x80Q\xe7/\xd3J\x1dD\x93#\x1eG\xb6\x05\xf9\xd7\xa9\xff\x93([\x86\xf9x~\xc8t\x860\xe6\xceP\xc6:\xdc\npc\x07\xe2^\xd2\x92\xc0\xf5\x1a\x17\x1aCS7\xe9\xe4\x9d:\x16\xff\xf7\xd9J\x90\x84\xbb\xd0\xc3\x97Z\x17~:\x90\x18\xd5\x90h\x91\xd8W\xb0\xcb\x14\x08;5\x1c\x0e\xe4AN\x7f\xe2\xd7\xaa9{g?]\xd3a\xbb\xf4\x8b\xb4|.F\x17\xbb\xfc~i\xe9\xfe\x18a\xb8\x9a\xbf\xe0\xa6\x80>*\xa9\x0f\xb4=\xe3\x06\xc6\xd3\x06\xac\x9di6c\x02\xfa\xb88x\xa8\xc5\xc2\xe3\xf9\xaa7_\xc0\x18\xb6\xa1x\x01\xe3\xf5u\x0f\xe2\x8b\xf1\x07\xb5\xe6\xc5X\x13kQ\xc6Y\xc4S\xe5\x1d\x03\xf3\xc3=\xae\x93\x01\x8e\xc38\x16\\\x90\xf8p\xc1\xea\x96\xc1$\xb8\x9e\x96\x96\xdbQ\xaf\xc3\"\xe9\xae\xaez\x8er\x92\x17\xfbh \xa2`\x92\x80G\xec\x0e\x18\xa0\x88\x81X\xbeC\xba4,<\xd1\x9a\xec\x15\xe3\xb2\xf2\x9d\x90\x90\xb4\xc7Sl\x1c\xa3\xa4X\xac0\x16\x81\xe7\xd6\x17\xf5\x1f@\x9bvK\x14a\xf4\xf4%\xe4\x89\xbf\x81/\xf6c?+\x08\x0f]\x8c\x96\xf6b\xb4\x9c\x87J\x99\xb8\x8b\x87N\x08\x8f\xf3d\x8c\\\x07\x82\x85\xa6\x01I\x8a\x85\xd92\xcd:G93\xdd\x15\x7f\xb8\x1e\x0c\xf1\xac\xb7\xe82U#Ou\x1d~\"c\xf3s\xea`;V\xbe\x02u\x8b\x1a\x95\x91Jw\xc1\x89\x12\xcc\x07\x84\xd7\xab;\xee%`\x90\xa8Zm\xda\xa3\x96\xb8\x9b\x80\x82ff\xe5]P\xd1\xaceF@\xb69Z,\xf3{q\xa5b\xcd\xc2\xa2\xa0\xc6\xcb\x90\xc8\xd5\xfd\xc0X\xcft\xbb\xd3\xb8\x86b\xdc\xfch\xba8\x08\xf3Pn\x80\x11\xba\xbb\xaf\xb9\xce\xeb\xb2 JD\x0c\xda\x8e\x83\xa3\xdcu\x0e1\x91\xa4]\x10\xa9\xed\xb7b\x8b5Q\x89\xd5\x82\xc6\xea\x0eEs\x96\x9e}\x12\x1d\xadNC\xad\xa9\xeb\x92\x90e~\xaf!\xc4\xfa dk\xd3\x84\xa0\x85|\xdf\x03Q\xcb0\xcbni:\x91\xb8\xe7R-CFU2\x94\xb9\x07\xffk\xf0\xd9\xbd\xc2\x16Q\xf2\x06[\x1b\xda\xfcK'\xe4\x8a\x16\xc9\x98\x9cG\x0bB\x8b|\x08\xcf\xbe\xb1@+\xa1\xe7\xacb\xe9_0\xdb\xad\xd7\x9fU\x02\x95\x16\xcf^\x02(1\xdc]\xef-dJ\xf3\xe8c\xad\x1e<\xae\x06Bc_\xcc\xd1\xf7\xf5\xc2\xdf\xaa\xf2R\x1ady\x98\x0b!\xc0(\x9c\x1d\xe6D'\x9cY\x1c\xae\xd2 #\xf9\x19k\xba\xba\xdao\x8d\n :hg\x91ri\x88Kj\x19\xc9\xb98f\xacd\xf2\xefW\xb0g\x184w\x98b\x03\xef'\x8fj\xc6k\xbd\x1f\xb0\xcax\xe5\xa5<\x11\xce\xe4/\x19o8\x994\x07\xbb\xcaX\xfb\x04\xc4\x10T\x06;p\xe9J\x8a\xeb\x12\x8a\x04\x06\x048w\xcaslau\x1e\x8d\x80\xd5U\x10\x0d\x1az`\xa1\xdfx\xff\x82\x01\xe2B7^\x9c\x15\x1f\xaefF\xdbH\xed\xe5_\xa3-\x95\xd6\xd7\xf7Q\x1c\x9f\x921\x89n\xf0\xb4,\xeb\xa1@\x19\xe7J\x92\xde\xda\x8e\xd0\xa2\x94]\x8f\x89\x7f\xfc\x9d\x9cN\x9bB\xa0\x92\xa3~*:\xf9\xd9\x17\xb2\xa0\xdau\xc4>\xba$?=\xec\xa7KR\x84\xedV\xed\"\x84\xebR'C\x84\xeaR'\x0b\x842\x99OC\xbc\x11,\xb4\xbeP\xd5\xfa\xec\x06\xd4\"\x88\x92)I\xb9\xf8\xe0FA\x94\x93E\xd6\xedhV?Q\xe9\xe1s\xf6\x8ag\xf7\xef\xf0\x1f\xcbP\xb7\xb5\x88W\xd0\xa6h\xb3&\xbc\xec\xd2v\xe7\xd2\xd3\xed\x13\xb5\xddy\xd7\xc6\xaeH\xd5\xe1\xeaR5T\x92\xb5R;\xecQKf\xdf\xed\xbe\xb7/\xd6\x9c\x85\x96\xa1\xad=\x1b\xa2\xbf\xd7\xa0kz1\xfd\x9b\xf5\xe2\x8ey\x14\x0eW\xdc\xedc\x8dGC\x99\x04\x98]\x91\xfd-\xfet=\xd8\x86\xad\xea^\xca$X\x84KE\x10\xf2\x81v\x11^$\x84\xe6\xb4n\x96\xcf:.\x96\xc9\xd9\xb75\x0f\xe2\x13K\xdc\x10xZ\xd7\x9e\x92\x8b|J \x06\xaf\xf1\xf0[/\xd6J\xb6p\xab\x80'\xeb\x82j\xe5\x9d\x8f\x8b\xe5\xc5\xe6\x07\xbe\xe3\xc1:P\xcb\xdd\xe4\xce{Y\x1dsi\x1f-2\xa2\x0e\xa2T}\xbf>f4\x19\xf0\xed|\xc0\xf4\xeb\x01\xdb.\xad\x0e\x81\xa6\xeeY\xdd\xcd\xa0\xfbd\x05Z\xa7+\x1dF*)]\xf7]\x81\xfd\x04{\xf9\x94$\xa3\xaaO|)\xd8)\xc7\xde\x1dy\x9e\x13Y\x96\xbf\x19\xc7V\xf3\x124\xa6\xf6*O\xe0*O\x06\xd9\x02\xb4\xb3<\xe0\xfaH\xc7\x86K\x93\xfd8\x1a_\xf7\x10^\xd4\xa7\xc4^\xa5\x87\xb9]\x88\xb3\x11\x9d\x03\x03pL\x9e\xa8^\x90S~\xf4\xf3X\xd4\xad\x84\xb6p2\x01\x07\xd6\xab\xcd\xab\xc1\xf8\xb8\x1b\xa1\xf1[%B\x91#\x08\xbdM?06\xee\xbd\xc9\x04\xd8g\xb5\xc3\xef\xb4\xb4\xbc-R\xb2\x8a\xb5\xa5r;\xebeo\xf9\xdf\x81\xdf\xca\x07~\xabj\xa9\xff;(\xd3?\x7f\xd1AY\x97\xceB{\x1d\xa7\xd5\x0f\xca\x0c\xa7\x0bx\xf2%\xf4\x9b\xb4\x9f~\x13\xf69\xcc\xea\x10#\xc2\x9e\x1ba\xba\xbaX/Dz\xa5f\xda\xcfX.\x82\x08$\xb6\xdbFuA\x9d\xbb\xc6MS\xba\xf8\xe9\xccs)jYx\xff\xd3\xc9S\x9e`e\x1a\xc6\x999\xe1\x0b\xe8\xa5\xf9\xb2\x1d\xdb\x81\xd7\xaaB}\xb7I\xe1\xd3L\xe4\xa5\x07\xf1\xa3\xf7\xec\xde{\xb2\\\xa1\x9fl\x1f\xb7X\xc6\xd9\xc2\xc9H\x8esrN\xcf\xc2\xc52\xeee#\xaf\xbc\xbb\\\xf6\xe5\x19\xdb\x1cxm\x8e'\xcf%5w \xfd\xdd`\xa2\xb5\xcb\x1bEF\xd2\xf2\x990\xb4:\x0f\x93ILNVi\xfb\xa6\xccw\xdc\xed\xbb\xa1\x0c^\xe7\x03\xe8\x1b\xbd\x85\xe132\x80\xcf\xe9y\xb9V1\x81\x86\x9dO\x9d\xc3\xf2e\x9bdtw\xb4\xeb8\xf8B\x86\xbc\xffbN\x96\xbb\xce9\xb9\xcb\xf7R\x12>\x92\x9b\xd4\x0c\x0c& \xda\x93\xe50R\x9b+\x06\x04c\x1d\xf6\x08\x9e\xc4\xd8M\x16\xfda\x0d\xcfkF\xbddX\xac\x05d\xc3\x1fi\x94\xb8\x8c}x\xfd8\x97EGm\xb0\x89\xfa\x06\xa0\xad\xf5(w\xbe.\x11\x1f\x81\x1fu\xe3E\x1e\x86\xe2E\x87\x7fz\xc1\x818\x91F\xa7\x89\n,\xad\x17\xf0\x10\x92\xb58\x02\x8f\xef\xc2g\xbdt\xd3\xec\xa6\xe9n\x8c\xf8h\x98e\xd1,a\x8c\xcc.\xa6\xd7\x92>o\xf1\xfc\xceMuE\xe4y\xb6\xef\xf3\x95\xa6bJ\x03]~\n\x03'&=\xf3\xc2c(8\xb4Ta\xac\xe9\x1dH.R]\xa0\x89\xd6\x1b\xc9\x90\xeb$X\xa7x\xda\xc5\x9aK\xd1\x83XO\x9ck\x19\xfe7_@\x02\xdbj\xa2\x7f3\xf6@\x99\xb9\xfc\"1`\x0e\x90P\x99tG\xd2\xf0\n\x05\x8a\xdaO\x91|,e\n\xdb4\x9a\x15\x12hm\xb3L\xda\xc7P\xce\xe3\\\xa6\xc1m\x1a\xe5%D\x99}\xaaI\xa7\x845xM\xee\x19\xfe\xf5\x0b\xbe\xff$\xa8\xd6X>\xa1V\x85\x91\x07\x01u\x15\xd2\xe0\x99\xc3R\xf1\x9eG\x07l{\x157\xb6\x9b\xe6\xc5r\xa6\xd8\x14<\x02F\xbd \x14\x05[\x9b\xdf|\xab\x0f\x86Q|\x91\xbbOn{\x99\xf7\x92\x8a\xb5+{\xad\x9f\xb3\x04\x8f\xf5T\x8b\x80\x95\x9b\xc2\xa1\xed\x87IBs`\xeb\x12B\xce\xfb \xccj\xa1\xd8\xdas\xd2!\x90'}\xbd:\xb0\xa3D\xed\xd9)\x99\x92\x94$\xe32D\xdc<\xca`\x1ef\xc9\xd79\\\x11\x92@\xc4\xaf\xb1D\x19\x99\xc0\x00\xb2bIR\xd7\xabA\xb0\xa1\x90I\x87\xf8\xb0\x86\xc7\x0dJB\xc9Z\x10\x1fm8\xbb\\P\x81\x86F\x0d\xfa\x86X\x843\xc2\x98\x1f'\xfa\x93i\xcb-\xc7\xa2y$\xab9d\x93`I\xd2,\xcarSX\x05\xc9\x14\x92\xee\xd3\xbdd\xa5\xe3kU\x1f\xd0o,=s\xaf\xb0\x1e\xd2~=dO\xe9\x06\xf7\x92U\xe1\x82x\xe9\xcd\x86\xe1\xaa\x12\x9aGS\xbc\xe68,\xb7oxYU|\xf2\xa4\x02J\xf1\x88\xa8G\xbe\x066\xd8!\x08p1\xf8\xaeZP\xe1\xcb\x92\x91\x0e\xf4\xeayUd29\xb7\x89\x12\x13-%?\x93\xfb\x03zk7\xa0\xca\xa7\"\x0f\xa9C\x8a\xda\xfa pFI\xceS\xc20\xf1\xfe\x9a\xdcsdNi:&\xc7\x12\xed\xbe\xc85e0\x10\xb2.\xbe\x8a\x8b\xf4\x91\xfdcUM\xf4\xbbb?\xb8\x86\x80\xf0\x11\xe9\xd7\x1f\x1eQs\x1b6\xbd\x92\x86\xba\x84\x0f\xf9\xc8\x05^\xc4\x06/F\x83V-\x03\xfc\x8a\x84=\xb5\x0f'\xc1\x84\xf2\xf1Z*\xdb\x97^.L)\x8a\xed\xa5\x1b\x0d\xf2I\x82(\x13\xbc\x8e\xdf\xd1a\x02L\xd5)\xab\x9f\x19\xdb\x07\xcd\xcb\\\x87\xddGtg\xd3\xd7\xcf\xbf|\x90\x0e\xa6q\x91\xcd\xfbN#TS\x99\xf3\x9a\xb6\xb4\x13Hf\x8c!\xc7\xab\xb4\xafEk.\x1a\xb2}NOXz\xea\x97\x93\xd4\xa7cI\xc3\xc4$\xce\x18D|Z\xe5r\xad\xfeS\xca\xba\xec5\x9f\x98_\xa0\x86\x03\x1b\xc6J\x0c\xe3^$\x91d&--K\xec8\x81\x04\x0d\xb31\x7f!Wx\x14E\x9e\xa4\xac\x08\x0c\xa2X\xfe\xfeR\x0c\xe8\xf1i3{\x07\xdf\xc1\xa9\xee\xe5\"(\xdd\xe6\x98<\xd6f\x8c\xd8\x8en_\xa9Aj\xcd\x87\x9d\"\xa81r1\xb2\n\xf4=A\x07?\x83\xe8|\xc6\x84O w\xcb\x94d\x19\x93\xda\x17E\x96\x03\x89\xf29I\xe1\x8a\xf0\x06h\xaa\xc8\xd2>\x06\x1dv`\xbd\xfc\x90\x862I\xa5\"U\xba?\xe7N\xae\xc8\xdb\xa8\xe8Pz\xd4\x8ei\x92\xe5i1\xcei\xaaS[\xe4#g\xc0L\xef\x95F\xda\x8e8\xa0>R\xff\xb4\xbbA\xa9\xba\xec\xd0\x94\x8cICK\x92{\xbb\x02\x1bYM\xa2\x86]\xd0\xbe\x17\xf3>DUN\x8a\xe5l:\xeb\xa4\xc3t\xcf\xf2T\xa0a\xbd\xf2\x81\xf630\xbf\x8f\xe2\xf8S-\xcch\x95\xab\x8b!\xaeb`n\xdc\xbf\xe8\xb2\x97X\xac\xc9\x7f\x89K\xac\xdcH;\xb7\xd0D\\\xc6\xab\x8dF\xbf}\xe2\xe8k\x8b\xff\xcf?\xcb\x8c\x85\xb84+g[\xc5\x01\xb7Q\xd2[\x8f1\xddi\xf6!\xa9<}\xb5\x93Q~\xac1}I\xb7\x01\xb5\xe74\xbdK\x16\x9f\x83\xbc\xb8t#{k\x92Xzw\xf1o8\x97\x10\xb9\xbe\xec\xf4\xe5*\x91\x15J\x8a\x04R\xb1k\xbfM\x82\xec\x95\"\x9b\xbc\xbaG\xf5\xc6\xe68\xc3\xa3-TUNP\x1f\xb1\x9c\xef\x8a\x90\x0fB\xab2\x03\x16\x02\xd0\xde\\\x86PQ\xb2,\xf2S25\xc3\xc5}\xcd1\xf2\x916\x9c\xff\xf4I\x1aUZ\x7f\x89\x07y\x19\x96<\xf5\x98\xb8\xb3\xa9XA\xec&aR\x9a\x84\x13n\x12\xc6\xac\x85\xf6\xcfK\x1d\xca\x08\xf4\x80~/\x8e\xa0\x18\xc7\x07G\x12\x85S\x1aQ}pJ\xa2\xc0d\xd1u\xa2\xc0\x83\xfb\x16Q4\xde\xf2y\xe7\xed\x8b\xb9\xe5?\xe4k9G\xd6\xd3\xffqG\x0cKt\xf3\x86]\xcb\xdc\x95_/\x1d\x01\xc4o\xfd\xbe\x06C\x08\xfb\xb6g\x88\x17\x0eC#\x910\xba\x98v\x0c\x89\x95\xd3\x8e.0\x1c\x96\xe3a?\x8c=)z\xb5T\xadB\x99\xba\xb4(r\xaeueb\xe8\xba\"\xf3=\xd8\xd6\xdd\xd7\xad\xcd\x06D{\x93h\x8b\xc2\xad-\xa3\x0d\"w\n\xd9\xc1\n\x97\xf8W\xc7\x99\xa5\xe5\xae\xa0\xdc\xd3\x9d\xd1\xdd\x92\x8cs2QM\xfcmBIa\x07\x8e\xc3\xe3v\x01cz\xce\x85\xf0\xf09\xbb_\\\xd1\xf8\x83\xa6~\x04;\xb0\xf1\x7f\x7f\xcf\xd6\xff\xfc=[\xffjc\xd6\x86\x08\x11\xe2b\xb0\xfea\xf3\xeebs\xf0}8\x98~X\xffjC\xe3\xe6T \xe4\xe6\xd5\xc5\xe6\x96\x01\"\xe3\x10\xf4bs\xf0\xad\x01\x841A\xcc\xad\x7f\xa8\x93\x1d\xd8\xde\xaa\xa4f\xa9\xe9\x81B\xe7:\x11NM;R'\xc3\xd7\xed\xa6\xa6\xfa\xa62\x12OY\x0d\xf5\x7f}\x9b\xac\xa4\xdd,\xdb\x80\xc6x\xf6\xcb\xfey-\xe7\xd9\x91\xd6\xa7y\x949\x9e.\xec\xf2\xa4R\"+\x16,\xd3\xe4\xb4\xc1\xe7\xb0\x03Ga>\x0f\x16\xe1\x9dF\xac+K#\x8d\xf8\xd2\xef\xb6'\xef\xf028`\xdbNBou\xf2\xa7r^\x07\xea\xb9\xd8L\xaf\x7fH\xddC&\xba1\x1e\xa8\xac\xad\xf1\xac\x18\xb5 \xd2d\xddiz\xa7\xea{\xa3\x89\x9e\x08\xd2\xac\xa0\xc9\x97nK\xd3\xc2\xeat\xebX\xa2\xbe\x93\xe1\xba\xab5\xde\xed\x16\xd0hD\xa0BC\xaa\x066\xc0Z}\xf2\x04&B`\xf3@{i\xe5AM\x13\xa4\xb1\xcdc.\x15KF\xa9\x9b2\xa8PmBdF)\xdc\xbdQ\xe5/\xffF'U\x93\x17\x1a\xec\xc0\x8cm\x86\xbb\x90\xc3:\x8f)\xd6u\xc6\x0c\xcd\x0cJk\x9a)\xac\x12\xe6\x13\x18\xc2\xba\xe6\xf3D\xb8\xdc\xf2\x84~\x11\xe6\xf33\x1f\x97\x16\"\x1d\xb4\xe5,\x90\xcdp&\xc1`\x17bW\xe4!u\x9f\xa2\x86\xba\x0bOa\x08\xdf1l\x84\nX\x8a\xfdk\xd0\xb3\xfaK\xf5\x8ci0\x17\xed\xa1>\x1e\xd1\xf9\x10a6\x99\xc2\x87\x0c\x85\x13\xf4w\xd7\x0b\x1cSn\xb2\xd3\x96--e\x13\xb4\xd9\xebIH\x9fpLo\xa8K\xbc\xc6v\x02\xea\"\xbe\xea\xf6w\xb4\\_b|2\xb2Jv\x8ca*\xe9\xdbx\xa0\x17_\xa8x\xdcr\x9e26\xae\xa1Js\xa75\x91;\xe5#;M`\x00\xb1\xb5gJ\xc0\xbd\x98\x11W\xc2T\xb6\x9c\xff\xb5\xcdu\xb7%zB\xc0\x00\xc6\xac\xac\xad\x04\xd8\xfax\xdb\xa9\xf4/l\xe1\xff/k\xf9\xc6\x8c9\xca\x18\xd5f$\x17\x82\x99{\xeb\xf7\xdc\x05K_V\x18\x80\x8b\xb8\xea\xbe\x9c\xba\x84]\xb8q\x13\x1fBYi\xec\xa1\x05\xdf\xb8a\xae6\xab\xa3\xce\x9d?S\x08i\x02\x98\x1dk\x17\xae\xf89\x82\xdb\xa4\xb4b\xb5\xaf\xdf\xf5\x99/\xf3JHx\x1c\x06\xcb\x8cR\xd5\xa5\x8c\xe7\xe4\xe2.\x10L63EJQ\x1bP\x086\xf3\xdaV\xfe.\xb3\x86\xa80\xe6_k\x13N\xee\xf90\xad\xf0\xa9W\x14\x01g\xd6F,\xe2^\xb42c\xed\xcf\\\xb9\xa6\x00\xfb=\x17l\x86b\x8c\xaeq\xcf\xd7\xf4\xdc\xe8\xc5\x95c\xe4\xe8\x1ccbn\xfa0s\x85\x15\x06\xf7\xec\xb54\x88 \xe6f\xe0Y\xb0]\xb6[;\x8b\xf0\xee}\x18\xe5\xdc\xfd\x8cq\x98\xb9{\xef\xa6\x81x-[B\xc3{\xe8\xe3&\xee\xe4i\x18\xc5\xc8K\xd1em\x17\x9b\x96/a\x08\x13L\xe0\xd7\xffhT\xb1\x00#\"0)\x98\xc4B&o_\xf1\xebG\xb1X\x15\xd5\xd2ic\x87}\xbd\xf7\xb9\xafn2v\xa1\x80!\x8c\xdc\x85kH\xf0U{\xa9\xb8\x87IW \x1f\x12\xf7\xd9\x96\xa8\xdc\xa1\xe5I\xe7\xc2z\xf7\x9c`#\x8c\xe3\xe0c\xe6\x0c\xe1\xf9\xf3\xe7~\xab\xb0\xc8\xe7\x1b!6\x9aq\xa8\xa7\xcf\x9e\xea\xa1\xd0\x88\xc7a\x9e}\xffL\x0f\x93\x92I1&i&\xc1\x0c\x1f\xccd\xe2! \xf7\x8d\x01nI\xc6\x83\xdb4\\\x0ej]|\xf6\xfd?[\xf0\xfc\x10)k\x8e\xa5\xdd\x01 8'\xf1\xb2\xec\xe9\xd3g\xed\x01I\xc0\xda\xb8\xbf7\x82\xd5\x87\xfe|\xb3\x8dE \xd9\x18\xfd\xf3\xcd-3(C@mH\xcf\x9b&\x06'\xd8\x98\x10\xb2\x1c\xc4Qr\x1d%\xb3\xfa\xb8\x9eo\xb61[\x83V\x06\xf7|\xb3\x8d\x83\x1al\x1c\xde\xd3\"\x97\xc0m\xcc\xd6\x80\xcb|K\x83<\x9c\xe1\x1c.I\x1a|\xcc\xee\xb0\xf2\xb7}+7+\xb6'~Bo\x93\x98\x86\x93A\x91\xc6r\x96\xbekA\x914\xad\x93\xc6\xd6\xd3v\x1f\x18\x10\xdeG\x18\xe4i\x98dS\x9a.H\x9am\xcc)\xbd\x16-?mO\x95\xa1R\xedGB\xf3\x01\x9d\x0eP\xc9\x16\x0d\xb5\xc9\xa3OC\xcb0\x0d\x17$'\xe9\x80&\x84Nec\xed\x89\xeb\xd3\x18\xd3d\x96\x03\xe9\x0e*\xdbj\xcf+kK]\x04[\xedE\xc0@\x1ak\xffi\x9bN\x19Ts\xe9?m\x13(\x8f\x9dP'\xcd\xf6\x8c\n(\xba\xccxV* \xd9\xee\x1c\xa7\xdb\xc6\xce\xa0YF\x02N\x1d\xea\xd36\xbd \xa8\xe6h\xdb\xd4$\x00[\x03n\x0f%\xa6\x8dm\xe6\xbb6Rh\x98=knn\xed\xceq\xa8\"\x9f\x0f\xc8]N\x92\x8cAo\xe0\x06\xda\xdct44\x83\x95\xcb\xe3\xc5l\x83\xf1\xa0\xabp|\x9d\xc9\xd5\xa7\xc1F\xb3\xce<\xcf\x97\x03\xd6\x01YG\xc3M\x9au\xd4\x89\xd6\x90C\x13\xbc\xda\x1c\xd8vQ\xf6\xad\x8dVs\xc5\x8c\xa7X+\xfb\xd8\x8d\x8b\x94\xfc\xbf\x82d\xf9\xe0\x8aN\xee\x07d\x12\xe5\xb4\xdc\x93\x9e\xb5\xf7\x04[\xed\xb2\xc3m\x8aiV\x13\xdd\xac\xb2\x1d\x95\x9fl\x13\xaf\xa1n\xf9\xb5\xf6\xb2\xc0\x1a5n\xf1\xcc\x80\xfc\xda\x04\x19F\xdb`\x7f\xcf\x0d(m\x92\xe1s\x03y \xe3Sh\xb8E\xbe\xedmJ[OO\xfb\x86\x8f\"\xb0\x82C\\HQN\x16%\xde\x0d\x0b\xa0YQE\x98F\x04\xd1\xd6Q\xa38p\x1b\x93D\x91\x01\xe3\xcd\x06\x16az\xcd\x98\xa1\xfc\xaea2[\xd5\xe8\x84\xc4r\x80\xcf\x0d\x84\xd5\xacD\x938J\xc8\x00\xaf\xb6\x859M\x07W\xe1dF\xe4\x97\x0d\xb4\xd6l\xa4df\xd5B4\xac\x89f\xcd\x1b\x9e\x02r\x90\xe5\xe1bYV\xd6\xec\x00 \xd6\x8aINjs\xb2\xd5\x1ef\x86\xb71\xb3\x8d\xa9\xc0\xdf\xd6\xf7m\"\x910\xb5\xad\xba=\xbd\x8c\x06\x9b\xdcF\xd3\x18\x83R[\xd2\xec\x94\x08\xd3\xe04\x9a\xcd\n\xc1\x1aD\xfeT#U\"\x9cF\x9c~\xde&k\x99\xd5\xeecc\xb4m\xc8\"\x8f\xe2\xba\x8c\xdc\x9e\xc4\x9b\x88\xdc\xd6`\x9e\x1b`RJ\xf3A\x94|$\xe3\xbc\xec\xdcw%\xa46]\x0d5^\xd8I\xdc\xa8fly\xd0\xd4\x8e\xda\xb5\xa5\xad9\xbd \x8d[Z\xfc\x06M\x0e\xeb\xb0U\xbb8S\xbf43\x8d\x92 ,\xf8\x0d\xa1\xaf\x1dX\x07\x02\xeb\xe0|\x1d4\x0d\xbdR\xd7V\xfa'\xff\xa2\xc15\xb9\xb7\xe6O\x16\x95\xc5\x11\x0e\x83v\x95\xcb[\x0f>\xd0 %\x19\x8do\x08St\xeb\x17\x1d)+\x8d\x98\n\xbe\xb5\xf9\x0d\xc7\xee\xc3\x07\xef\x1f\x0f\xde\x8b\x7fll\xfc\x1f\xc8h\x91\x8e\xc9Q\xb8\\F\xc9\xec\xdd\xe9\x9b\x9d*\xc3\xe1\xe0\xaaH&1[\xe7\xc1\"\\\xfe\xff\x00\x00\x00\xff\xffPK\x07\x08-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8\xec\xbdys\xdc6\x9a0\xfe\xff|\x8aG|w\x152M\xd1\xdd\xad\xc3:,k\x1d\xc7\x9e\xf5\xbb\xf1Q\x963\xf3\x9b\xb7\xa3UQl\xb4\x9a1\x9b\xec\xe1!Y\x13i?\xfb\xaf\xf0\x00 \x01\x10 \xd9\xb2\xb33\xbb5\xacT\xac\x06A\xdcx\xeec\x0b\x16U\x1a\x95q\x96\xba\xa5\x0f\xc4\x83\xdf\xfe\x00\x00\xe0dW\xbf\x92\xa8t\xe0\xf4\x14\xca\xbb5\xc9\x16@\xbe\xac\xb3\xbc,`{\xdb\xf4v\x95\xcd\xab\x84\xc0\x19\xff#\x10\xb5O\x81\xb8\x1e\x1c\x83#\xba\x91?\x9a\x93E\x9c\x12\xda\"\xfb+\x08Ws8\xe3?\xdc\xd9\x05\x0e\xe8\xb8k0g\xe2\xaf\xe0\xfc6\xbc\xbe&\xf9\xcfo\xce\xcb0\x9d\x87I\x96\x92\x0f9)HY\x0f\xa1\xec\xab\xf3\x87\x07\xb7\\\xc6\x85\xdf,\x89X\x8e\x9c\x94U\x9eJK%^\xd0\xe7&\xcc\x81\xc0)\xfc\xf6p\xf2\x87\xbaPT\x85\xd4\xcd\xe5\xca\xf4\x89\x17\xe0\x92Y~\xe1\x89v\xe9\x0f\xb1b'JU\xdavLG7\xcb/h\x17\xcaKl\xeb\x18r\xbfU\x9a\x1c\xc3\xd6\xa4]\xcc\xbb8\x86\xdf\x1e\x94w\x0fj\xa7|T%\x1dU\x14&\x89\x1b\x8b\xc1\xf9\x10\xfb \xfdJ=\xfa3\x81S\xd8\x1aK/\xea\xd6\x9anx\x9bi\xb0\x82S(}H\x83\x88N\x8b\xfe1\x87S\xf5\x10\xfa\xd0Z\xb24\xc8\xf8\xf9\xbc\xbf\x87\xf7x\x1c\x02vL>\xe4\xd9\x9a\xe4\xe5\x1d\xff\xb2\xbdBQ\x96.\xe2\xeb*\x0f\xaf\x12bY\x96\xb4Z\x11\xf1~\xdc~\x7fM\xcac\xc8\xd5\x15\xf3\x9a9\xd29\xa4\xca\x1c\xf4\xd1\x8b\x13R\xd2\xa3^\x06\x97\x97\xa4x+\xeeK\xeb\xac\xc9\x8f\xd8 :\xd7\xb0JJu\x0cp<\xec\xeb\x01{\x9d\x06s\x97\xf8\xe0\x84\x0e]d\x1f\x88:\xbdL\xdf\"\xbd;\xde\x0c\xdf\x99u\x9e\x95\x19\xbd\xa9\xc12,\xde\xdf\xa6b\x8f\xd8i\xc2\xef\xd5\xf6\xd7p\n\xce\x93y\\\x94\x8e\x0f\xa9\x9b\x06\x14pL\xc7\x07\xac\xda\x83;\xd3\xceG*\xf7\xefT\x05\x81\xa2\xcc\xe3\xa8tN\x94[\x99\xc3)\xa4\xee\xfe\xd4S\xf7\x94^\xa8\x99\xf39N\xe7\x8e\x0fNN\x8a,\xb9!\xf4\xcf(K\x8b2\xaf\":\n'N\x8b2L#\xf2~A\x7f\xads2\x8f\xa3\xb0$\xec\x935\x05\x1b)\xd6\xe3[s^\xde%\xf8\xb2\xa0\x7f\xbcH\xe2\xb0 \x85s\xa1\xf6\x9ca\xcfE\x14&a\x8eu\xc9_+\x92F\xf8\xdd*\\\xaf\xe3\xf4\xda\xb9h\xe6PJ`\xb4s\xf9\xe9dS\x1f\xaa\x936\x9c\xa1\xb7\x8c^\x9a\xdf\x1e|\xb1=\x9f\xc9]\xe1\x12/Xd\xf9\xab0Z\xbau\xd3\xadvE+;\x138==\x858\x88\xd39\xf9\xf2~\xe1\x12\xcf\x83r\x99g\xb7\x90\x92[\xc8\xdd\xef~N?\xa7\xd9m\n\xd9\x1a\xa1\x9e\xf3\x1d\x8c\x80\xc0\x08\xbes .`EJ\x88S\x06\xd8c\xac\x90-X\x9d\x92\xd5\xf9\xcb\x8b\xb7?!l\x0f\xbe\xf3\xb4\x8b\xe6\x03\x05\xcaA\x19^3\xc8\x81\xbf\xe8\xe6\xd1\x99\xb1?\xee\xef!\xad\x92\x84\xbf\xe3\x1b\x8a\xaf\xc5\xdf\xf7\xf7\x83\xae\xca\xd6X\xed\x9c\xb7X\x9f\x0bl\xb3\xf9%\xb7\xda\xba\xf4`\xbd\x81\xbc\xd5\xe6\x80a\xb3\xd2Ou>\xf5\xd1\xc3j\xcd/}\xd6\xfcL\xf2y\x8b_j-\xf9\xb0bE\xa5@\xad+\x1fd8\x057\xc5\x0f\x94\xd2\xfa\x83\n\xf1\x9f\x8f\xbf`\xeb\xf4\x14R\n\xea\xe4\xf3\x96\x1a\xce\x9bq\xcd\xd2Yy1\xf0h\xd2\xa7\x9a\x9d\x97y\x9c^\xbb\xc4\xa3\x18\xb2lUzh\x1f\xa8\xca\xf3\x81\x1f\xe9\xac>\xd2\xf5\xb9\xb2\x1dm\xd0F%\x1e:\xba\xc8\x87\x85\x0f\x89\x0fk\x1f\x96\x8c\x06\x81\"x\xdd\xa6r\xe83\xaf+\xfc\xd1\\\xe1\xa6\xaepn\xaepWW\xf8`\xaep]W\xf8\xc1\\\x81\x12\x88\x94\x0b\xc8\xe1\x18n\xe8\xbf3\"N\x17A\x1a\xf8\x81\x12\xf3\xae(\xfe\xed\xc1k\xe8\x0ds\x8b\x97\xbc\xc5\x98\x9eB\xd1Z\\\xb7f\xfe\xe8\nN\xe1\xb2i\x19\xbf\x91\x7f\xe3\xa7'\xadO\xe9\xf5w#Dvx\x98\x10hz\xb8?\x94Lv]\n\xec\xb7\x96\xf4\xdd\x8a\xfe\xef&\x8b\xe70F\x90\xb9\x9aE\x17\x1e\xe5\xa0\xe0\x18Ro\x16]\xf8@\xe9\xa2kZm\x01g\x10\xba R\xc6\xc7p\x87L\x98\xe9\x0e'X\xef5\x7f\x83\xf4\x96\x0f \xfd&\xf1Y\x87\x95\xbb\xf2\xe9\xa1\xa0P\x1e\xb7\xe1g\xcf\x87\xcbYt\x01[\xa7\x90\xe0\xcdu/\xb1\xc6\xda\xf3YOW\xf2[\x17\x7f\x9dB\xa2\x81\xd5f)\xf2 bw9\xf6\xe9I\x83S\x98\xd0?\xfeHI:\xfa\xc79\x9c\xc2\x1e\xfd\xe3\x03\x9c\xc2!\xfd\xe3\x07Z\xe7\x80\xfe\xf5g8\x85]\xac\xf53\x9c\xc2\x01V\xfbH\xdfN\x0f}\xe5\xc6\x17\x9b\xdd\xce]\xe3\xed\xdc\xd3\x8b\xf9\xed\xd4\xef\x1b\xbd\x9dO\x9c'\xd7\xed\xcb\xa9\xf7n`]@b\xe38\xaa\xca\xdc\xd2\xb3\x1c;\xda\xa8\xf3\x8c\x02H\xd2>\\\x1c\xde:N\x83b\xdd\x10F\xa7\xe0\x00\xfd\"\xa5\x18\xe7\x14\x91\x0f\xef(\xf7(%\x90\x84\x11q+\x1f\x9c\xed\xbfVYy\xe2x\x88\x99\xbe\xf3|\x08a\x04\xces\xfamL\xffz\xf6\xc4\xe1d\x9b\xf3\xdc\xb1m\xeffD)\xe7\x8b\xe5\xf2\x94a \xe2\x86\x9e\x0f\xb9\x9b\x07\x1f`\x04y\xf0\x1a\xbe\x87\xd8\xed\xa4\xd2\x04\x1f\xe580+/\\:\x07\xeb\"\x11\\#\x12\x94\xd9O\xd9-\xc9_\x86\x05q\x91{$A\xb1N\xe2\x12\xbf\x0e\x12\x92^\x97Kx\x0e\xbb\xeat=\x1f\x1c\xb6\x86\x94!\xe9C\xdc}\xe8\xc9\xa9R\xc6\xac\xce\xe9\xce\x89\xbbz\x1b\xa7\xf3\xec\x96n\"\xfb+x\x1b\x96Kz\x97\xf1\xdf3\xf1\xfe\xd8\xf2yA\x92\x05\xfd\x98\xfe\xab\x7f\x8a\xef\x8eA\xc0\x01\xd7\x11\x84\xe82.\x1c\xcf\xf5z\xf0\xe05\xc7\x83\xd7\x8f\xc0\x83G\x9d\xa4\xca\xbe\x8e&\xd9\x8d;\xfa\xdfC\xaa\xd8\x89\xb8\x03\x9d\x16\xa0Kb\x90m\xc9\x1b[o0#\xa5\x91d\xe5\x7f\xf27\xed\xe5\xcc\xe9\\b\xfa\xbf\x01\xfb/\xaf^6\xf8p\xbf\xc8\xf3\xf0.\x88\x0b\xfc\xd7\xdcX:\xb8\xb1\xff\xe57E\x9e\xf2\xb0\xb3J9nN\x17\xd0\xbe\x04;\xf2\xe9nM^\xe5y\x96\xbb\xce\xcb0\xfd\xae\x04\x8a\xdd)k\xbd\xcc\xe6\x90\xa5\x00\xec\xac\x9aey\x9bB\xb0\xa6\x15E\xb4e\xb9Vt\xb5\x9a\x1e\x94\xf3\x95\xdfi\x9f\xd0\xf6\xd2\xce\xd3\x89wq\xec\x03\xb9 \x13\xcfuXq\xd3\xfee\xd9\xc7\xbf\xcc\xfb\xf8\x97\x9b>\xfe\xe5\xae\x8f\x7fi\x18\x9c?\xdb\x19\x9c\xe5\xa6\xec\x08\xe5aV}\x8c\xce\x15o\x99\xb2Ns\xc1:\xd9x\xa5.\xdee\xa9\xf1.\x8ckY#3\xa0q-W\xc8\xb5loC\x88\x8c\x05\xbb\xbc\x94\xd5\xa1,\x0b\xf2\n\xc7\x90\"3\xb3b\x8c\xc3Rc^\x9a\xd3\x8f\xb5\xcf\xb0\xb6`rh#Y\xcd\xf7\\\xd7\xdc\xc8\xe9)\xb2:\xdd\x92$\x90H\xc6F\x90d\xa7\xd2\xc5C\xaf'\x05: Dr\xecf\xda?\xa0Oq\x1b#T\n\xf3\xebjE\xd2\xb2\xe0\xb4e\xdfw\xf4\x89\xc2\x82\xc0\xf8\xb8\xb7\x1eH\x02{r\x0be{\x0b\xf5\x07[\x9el\xde\xb2K\x0c\x94\xb5\xfe`\xe3\xd3\xc74\xae\xd0\xd4\xa6\xe7\xa1\xf3m\xab1\xba\xa1\xd6/\xecm\xd5\xea\x95p\xbdN\xee\xb8\xf2\xaf\xde@s\x8b\x0f\xe6u\x11\\\x87\"!\x904!\xb2J\xa5n\xcaE\xce\xfc\xa6\x93\x9b\xcfl\xdc<~\xe6\xba\xab\xe0&\xce\xcb*L\xf0\xe25\xbf\x10\x96x\x9cW\x17\xbc\xfeG\xfa\xcd%\xfd\xdf\x16\xb2\xfc(\x0f`\xdc~\xe2yV\x8e\xfe\x1f\x85\x8b\x9f\xeab3.dk\x953\x1cu\xa8#4\x8a\xa2\x8c\xca\xc3f\xaa$X\xb06\xf7=83W\x96\xd5n\x16\xccE!H\xee\x96\x9e\x8f\xb0'\xa3gtk\x8c\xdc.jL=\x03Y\x04\xcd!\xaa\xeaf\xd5\x0d\x91 \x9f\x87V\x7f\xce5)\x1d\n\xbc\x91\xb8r\n\xf1\xcb@>\xbe\x88\"R\x14Y\xce\x08\x8a\xa2Z\xd3\xfd \xf3-\x0bA\xe1\xdc\x84IEx\xdb\xf4\xd0\x95\x0cY\xa5\x01\xbe\xf0\xfcMI\x0e\xf9\x08l\xa5\xee\xf4\xc8\xb3\xf3\xfd|\x0cO)\x9e0+~\x7f{\xe0\x8a\xcb\xf6\x82\xa2\xe6\xb6S\xa4 w\xd1\xbe\xa0\xea\xfa{A\xd8\xcc\xb3\x9f\xd8o\xe4\x1f\x9a\x1a\xb4\x8f\\\xb4\xebWS\xa3\x06u\xc8\x92K\x82j\xcb%\xda\xdd\xb3\xb0\x85\xa9\xbb7\xf5\x14dk>\xf4\x82\xc5\x0e\x16\xbcF\xecNh5\x99t\xef\xbf:\xb5\xf1\x01;b\x1b\x9f-I\xe67\xb1L\xa8\x9b0\xdf\xa2\x17\xb7}iT\x1a<\x05\xc6k\xd8\xaeL\xdf\xa0\xfb\xf8`uX\xff\x8d\n\x8dne\xba\xb2rCd\x82\x88\x9bc\x1f2\x1f*\x1fB\x1f\n3\xa8\xa4@d\xcbHc!\x03\xd0\xc6\xb9\n\x8fL\xc9T\x88\xe8\x1c\xc9-p\x18\xf76N\x99B\x8e|\x89\x08SJgQT\xe59\x99\x9f\x00\x9dd\xb9$\x90f\xe9\xceJT\x9c\x93\x1b \xe9M\x9cg)\xc5\xffH\x0e\xd3J\x8b*I\x80\xd0VaE\x8a\"\xbc&\x10\xa6s\x08\xe7sTe\x87 ,I\xb2^T \xdc\x86y\x1a\xa7\xd7E\xa0\x9f\n\xfa\x90\xa4 \x1dD*E;3}\xb1.\xcct>}(\x86\x1f\x9bi\x11W]\nR\xcb\x80\x9f\xfck\xf1\xe4\xda`\xdedz\xf8A^\xcc\x92\xd1\xe8\xc2X\xeb\xc1\xf3\xbc \x0dW(\x91}\x93\xde\x84y\x1c\xa6%\xfc)\xce\x92\x10)\x99\xd6WmJ\x8c\xdd\xb2(X\xe4\xe1\x8a\x14\x9f\xb2\x0f\xd9\x9aQ\x1a\xd1\x1f\xcc\x1f\x0e\x82\x01}\x16!OM\x9c\xae\xa4\xac\xeeW\xec\x0b\xb6bvaa\xa3\xd8\xa5\x8eS\xca8\x90`]\x15K7\xed\x10V\xab\xb35_\xacD\x9d\nW\xf2\xca@.\x0b\xe2tI\xf2\x98\x83\xed\xdd}O\xfd\x84\xb1\xe8\x93C\x1d\x03p\x1e}\xf2\xd4\xd8\x16e\xbf*\xe9M=?\xdaK\xec\x86\x0d\x91\xeb\xf9x\x0b\xc7'\x10\xc13\x10\x1c\xd0 D\xa3\x91\xbe\x88\xe2\xc8\x17\xb3H[\xc2\xa4io\xb6`\xcc\xb1Vt\n\xa1R \xa3\xc2f\x94|\xff \xb1\x80\xf9\x16\x8b\x97x\x9e\xccY\xd0\xef\xd4\x91U\x1c\xfb\"\x9b@\x89\xbbP/@\xa9\xec\x16\xb3,(\x83\x9c\x84\xf3\xf0*a@\x98\x1bi\xf0\x92S\xd8\x9a\xb4\xea\xdf\xe6q\xa9\xd6\xafKD}Z\x18&Iv\xfb\xefa\xb2x\xbf&)7\xbdS\x1bRk\xd4\xad\xb5>\xac\x9b\xcc\xd2\x88\xb8\x0eA\x83\xa8u\xf7r\xae[P\xc3\xd0\xf6\xfd=+\xbd\x14\x138/\xc3\x92\x04$\x9d\x13\xb4\xd6\xc9\x83\x94|)?\xc5\xd1gw\xc9\x86\xd0\xdd\xe9\xb2\xbd\x87%m\xcd5\x89\xf2\xccTb\"\xf3b\x8e\x18\xd7\xbf\xc7\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\x16 \xa9\x18\x06j\x86\x83\xfd\xa4\xa5$\xd5\xd4\x17b)w\xab\xde\xfdfB\x9e?h*sR\x94yvG\xe6\xad\xe1\x0f\x1e\xa2$\xcea\xa3\x15\xe7\x14G\xab |\x0c\xf3i\x8e\x98\xfaeP\x8f\x8d\xd60-D]Acu4a\xa12\x113@\xfe\xfd\xa7\xd0X\x9f\xd9&A\xabx\x1d\xdb)m\\p\xc9\xbf\xea\xa3\xfc\xb1C\x86?\xaa$\x11\x17\x16\xcf\xbe/\xdf#\xe2\xcb}\x7f\x13499\xda\xb3\xea\x8a\xec\xbb!\x8e=\xaetN\xd7\xb56\n\xeb\xa3\x8a7\x1c\xdf\xde\xc1\x9e\x01\x8f\xbf\x0d\xcbe\xb0\n\xbfv\xeds7\xde|\x02\xd2\x80\xcc\xe3\xd9\xb73\x88LZ2\x90\xb5\xfb\x87a\x10\xa7\x87\x1b/\xf0\xdf\x85A\x1c64!\xaci+\xc1J8\x93\xee\xa0\xcd\x19\xe3\xdb\x8f\xa8S\xc8\xb5\xb5U\xba\x1d\xf2-\xebg\x9a\x85\xeec\xf7\xdeb\xaeg\x16$\xee\xeb\x06\x96\x8c\x90>:\xf4\\\xa7\xc8#\xdd\xd4\x81\x92\xd3\xb5\xd0\xb6\xcc\x98\x1dI[\xfd\xe5:\x0e\x8c \xf4\xb8=\x8a#j\xca'\x06-\x08\x838-\xd6$*\xcf\xb3*\x8f\xc8\x90C \x08S\xe9f\xf96K \xc1\xa5\x87&\x12=\xb2Y`\xa4\xea\xa9\x8e\x10\x7ffn\xea\x83CYB\x07\xf5@q\xf3\x9b\x1e \x8a\xbc\xe8\xadm\x8c\x97\xa4\xcf\xaa\xe6\x8b\x8a\xd7;\x03\\\xa1\x92i\xb1\x8a\xe0\xd7,N\xdd\xda\xda\xd7\xc3\xf6\x90\xe2\xcd\xe1\xac\x86\x07p\x0c\xa1\xf8\xa9\x94\xc6\xcd\x818\x06wN\x12R\x12|\xefK\xaf\x14K\x8fF\xf2.\xd3[\xf56u0\xd2\xe2.\x1a\xef\x19e;894\xab\x90\xc1\x91\xf8\x08\xb9\xffot\x0d\x7fo\xc0\xb01\xd66_\xbd\x03\x93\xa2\xd9M\xdd\x83\x03\xcf\xc7\xf7\xe3\x86 \xb69\x98\x18\xaf\xe9\xe4@7\xf3\x0b\x8d\xaeT\x9f\xc9\x9d\xd9\xff''\x0b\xf3\x8b\xcb\xcb\x82$\xf6wx]\x8f[ \xcb\xe4%VX\xb7M&[\x83\x9c,\xa4\xcdh7\x13\x0dk\xe63\xb9\xd3\xf6\x14$\x96\xbc\x0d\x1ar!\x962\xc2\x88\xb6\xbc\x92>\xff\xf2/\xec\xf8\x1cC\xd5^\x1c\xfa\xea\x18\xca\xf6\x0b\xdc\x03\x83v\x1b\xb7 m\x97\xaf\xf3l]\x1cChX\xff\xec6%\xf917j\x12\x8f\xd9\xfbI\xb2]\x91\xc4\x1cA\x94\x93\xb0$\xaf\x12\xb2bn\x15}\x94 \x9e\xf1\xda\x17\xa25\xa2\x84\x9e\xc6*I\x0c\xb3\xe0o\xd4\xc1QZ\x83\xdfNY\xdc/\x1e\x14\xc3\xe4\x10\xd3\xc3CP\x03\xef\xae\xb9\xef\xc7\xc2\xf3!\x12\x85 3\x98\x1c\x01\xa1\xfb\xee\xf9 \x8bM\x03v\x84\x05\x1c8\xaeK\xda\xd5\x18\xf2Q+b\x19\x02\xa5\x8c\x810\xe6\xbb\xb7\xbd\x0d[\xa1v5]V\xeeV\xcc\x93\x11\xfd\x1fOZ\xcb\xb7\x84S\xd05\xe8\xb0\x03\xd3\xf6\xca0Y\xc7\xd2\x83*\x88\x96q2\xcfQ\xa4\xa1\xa1%\x94\xb9\xd2\xdaKx\x0e\x13\x13YQ\x0b\xb3\xe6\xc2\xac\xcd]\xd25bb\xac\x1bx\x06\xcb\x13\xb8\x19\x8d<\x98\xcfn.\xe4\xd1\xcdn`\x04S\x83\xfco\xec\xabc\x9a\xab'\xb05\x13\xee\x15\xc8=q\xe8z\xb5\x84\xe4\xc0\x97\x07\x8dO\x94\x9a\x16\xf1#\x9e\x8b;O\xdeD\\xi\x07\xee\xe8\x0et\x0cM\x08\x80\xe9ig\xee\x03c\xfc/\x0eP\x8a\x9e\x96\x14g7\x17\xc7\xaf/\xcc\xeb0*\xb3\xfcn\x90G\xa4v\xc9\x82\xab8\x9d\xbb\xdc\x07\xc9L8\x93@(\xd75/\xc5E\x10%YJ^\xa4\xf3\x8fL\xdc\xfd\x1f\xa4\x97\xb9n\xe6\x18p%\xbd\xcf\xa0,\xfd\x87\xdf\x03\xfa\x07?\xe7e\xc0\xa0\x8a\xcf4\xfb\xebB\x9f?\x1d\xc0f\xf0\xa2\xaa\x0d\x9brTd\x8a\x86\xdb@\x02m\x9b\xe8\x15n\xbfB\xc1\x03\x0e\xbb}j(\x12\xed\x9a\x8b\xb79\xd0\xa9\x14\xa03\x17@\x87\xdd\x9a\xfax\xc80h\xa9\xc3 \xb6\xde\xec\xe0#\x1e\x97\xcft\x0d\xb6\x0c\xef<\x0d\xdaT\x16h\xc3\xca\x15\x15\x11%\xb6T9P\x02g\xb0\xa6\xc5\xa7\x90\xd0\x7f\x8e\xc5/Z\xd7\x00\x9d\xee6\x84Nw\x1e\xac\x87@\xa7\xbb^\xe8t]C'\xbaz+\x06\x9dV\xf0\x0c\xeeN`E\xa1\xd3\xf5l\xa5B\xa7\x95\x05:)\x03\xba\x1et\xff\xf9\xddX\xfa0\x17@\xe0F\x95\x13\xd3\xc3\x1f\x17\x7f\n\x93xn:\xfe\x9bP\xa4\x8a\xbc\x88\x1d\x10AJ00&\xf7\xaa\x10\xc0\x7f\x80~\xe2T\xd2\x0e\x1f\x98Y\xc0\xdd\x83~\xa9@\x87\xb3\x03c%\xcc\xa0+wS\x8f\"P8\xe6\x87\xb0\x99\x8aq\xec\xfa\xc09%\xa6\xab\x8a\x8d\x04ef\x10\xd3\x0b\xc3R\xae!-H\xf9)^\x91\xac*a\x192\xb1\xc5\x15!\xdcK\x97\xcc\x9dn\x91|\xd5\xdfA\x94\x900\xff\x8a.B\xb3\xfc%\xc5s\xd0\x8c\xbe\xd6\xda4Et\xf9\xc6\x06\xc8\xc6\xbf\xcd(\xd3\xb5\x95\"\x880\xb4C\xf7\xb1)\xf6{\xda\xed\x94r\xa4\xec\x0b\xf5\x9a 9\x87\xd1\xa7\xd5\xdc\x1c\xb4l@8\x92l\xb5\x0e\xbd=\xb4\xdb\xe2\n,s[\x16\x10\xf1\xb0eg\x7f\xcdsHm\xb2\x04\xe9 \x9e\xc9?Z\xc4{\xa7\x80(\xad=\x18\xea\xfa\x03\x06\x95\xdb\x06\xa5\x1c\xde3\xf5\xe7\xb1\x04\x85\xa0w`\xb4\x8b\xca\xb6\x8a\xae\xa6\xa2-\x98\nu\xa6i\xfe\xd1\xfeV\xd3@Q\x0c\xb931]\xfe\xb6\x8e\x8e\xf9? J\xe4M\xd5\xeaY:9z\xe0\x83(K\xa3\xb0t#\xb4/\xc4\xb6}\x88D\xa5\xedmX\xba^\x9f\x96\xcet]\xb7\x166j\x96\"\x89\xd0]\x1b\xd4\xe28F\x83uC\x8d\x0f)\x01\x18\xd5\xfaerb;\xe7\xf8\x01\x85\x92\x91X\xd7\x13\x18\x8d\x12x\x86\xdf\xe0\x82\x14\xb3\xe4\"\xc8\xab\xd4\xb5X\xbc\x8a\xa5\x90\xbb\xec\xb9%\xc0%|\xec\x8e\x9a\xf6N\x865\xbc\x92\x0b[Jk\xbd\x1d\xdeP\x85 \x90\xf1d\xc6F\xe9\xa9\x95_\xf8\xc3\xbb\xb1\x830\xf1\xe4n\xd9\x864\xe2\xe9\x87^\xe2\xe9\xef\x08d\xb5\x83\x0c7\xed\xdd\xc3FC\x80V\x07\xc2\x1a\xa0\xbb\x03\xfb\xec\x8do\x1e\xf4\x05{\xe8\xbc\x89s\xbb*qQ\xa5\x92&3\xa44%%x;\x9b\xbbq\x15\x8b\xd3\xb8\xd6:\x0e\xe2\xf1(E\xc0hW\x03\xed<1`\xe9V5J\x1d\xdba\x01\x9d\xcf\xe4\x04Rx\xd6\"\xceO \xa5\xc41\x99\xa5\xb4+\x95@N5\xe28\xe2ZVr+\x96\xcf\xf3a\x82th\x0d\x05\xef\xef\x01\xa3s\x84\xeeR\xa1~\xe7\x92D2\xaf:=\xa6\xc4&p\x9bs)\xde\x06\xee\x85\xd2l\x1c\x94q\x89\xd6\x1f\xceU\x9e\xdd\x16$wh!\xff\xbb\x89\xba\x94\xde\xf0\xf0\x1bq\x10\xe6\xd77\x0c\x7f@\x1cp\xbbAd\xbe\xa4\xdfE]\x1b\xdf\xdd\xe0w\xf3\xf9OqQ\x92\x14\xdb\xbda/Q\xd9\xc0\xfe^,\xc4\x9f9Ye7D\xaf\xccJ_$\x89xQ\x887d\x15\x97\xe2\xefuN\xd6$m\xf5\xc4\x8b\xdf\xa7Q\xab\xddDj\xae\x97\xa1\x98]\xa8\xabw\x15\xa7\xf38\xbd\xeeVR\xe9T\xeb:\xcf\"R\x14\xf5\xc7\xb1f%\xedh[\x14\xdd\xce\x07x\xc89O\x1c\xed\xb3\xe5\x0f\x18\xd9&\\\x88\x91R\xe22y&\xc8\x81\xb3\xe1\xbd\xf9\xd3\xab\xcb7\xef^\xbfy\xf7\xe6\xd3_\xb0\xc6\x04\x9e\xd8V\x9a|)I\xda\x8a\x8bh[\x02\xa6\x9dk\xd3Q6\xf9-.\x0d[:7S-\x9f]\xe2y\x0d\xed\x04\xcf o\xd6\xae\x9c\xc5\x94\xc5\x9e\xa5\x17LD\x1a_|\xfb+$J%9\x9d\xd9]\xa5\x15\xd4\x8fYj\x8c=\xd35\xac:5v\x063n1\x95 N\xa3\xa4\x9a\x93\xa1\xa1\xcb(\xa7_\xf7\xa5\xbc~\xe0\xc6\x0fC[2D@3\x8c_<\x84\x85\xc7C\xe5.\xfdk{[\x84\xc6ce\xf8\xe7\xf66\xe4\xc2\x12\xbd\xd5\n\x1d_\xca\xde\xea\x9c\x06\xbeY\xc4IIr\xb7\xf3-IN(\x11\x17\xa2\x17\n\xfb\x06\xc11z\x0d, \xd4\xe3\xa740d\x0b\x08\xa1\x88\x96d\x15\x06\xf0F\xbcb\xf1\x0d)>\xc8\x16PT\xd1\x12[(Z\xc4a\xe0\x18\x8e\xe3\x12C\x1b\xae\xd6qB\xe6o\x9a\x95\xab8\x0b\xeb\x88\x018>\xcc.\xf4\x0f^}i\x7f \xd6\xd3\xf8\x01E\xcco\xc3u\x17E\nB0\xc4n\x90\xd1\xae\x80>l\xb1\x8e\x8dZv|\xcf\xc3j\xdak\xf0`\x9b\xf6\n\x8b0I\xae\xc2\xe8s+V.}d\x89{\xfdA\x07\xce\x17O:cW\xf1b\x86\xd7\x94\xf9P\x8a\x9e\x9a2C\x0c\xc3vw\x14\x90\x97\x0c\x90\x13\x83Z\xea\x04J\x86\xf9J\x0e\xbd\x1b\xc6W\n\xaf\xa8k\xff@\x12\x0d\xab\xe7\xc55\x9e\x16\xcb\x99\x90/\xb7\xf8+\x0c~|\xf5\xfa\xc5\xcf?}\xaa\xe5b\xa1`\x19:N\x848\x0d\xea07\xf1\xb5\xef\xf2\x80G\x01\xa4\x18\x97\xb6\x8e\xb3\xb1AyF\x9f\xab\x9c\x84\x9f\xdb\xaf\xba\x9c\xe1K\xada\xbd\xab\xc9f]q}\xa8\xa5/\x19\xc8\xfc9\xcf\xd2k`\x9e\x81\x08AD\x97x~\xce\x194\xe1\xbbP\xb3v]F\x01\xcc^\x81\x02vN\x0c\xd6N\xceM \xf3\xe5\x0b\xc8\x0d\xc9\xefz\x80\xa7\xc0\xb3\xb2\x1bN\xa8\x01*\x0dn\x9e\xd7\x916\x05XDn\x88\x83\xc6\x02\xdc,\xa7\x802N\xaf\x13\xc2g\xc8Mq=\xca\xa0\x95a\x9c\n\x98\xab\xbcm\xf9\xec!wA\x1e=\x8dl\xd3i\xd4\x81B\xb59P\xb8i\x9b\x81\xf4\xae5~q\x8f\xc9-\x84\xae\x01o1\xf4id\x89\x05\x1c?\xd6\x1d\xd3\x14\x11\x83\xcc\xa4\xb1M\x1bj\xab\xf8\xdb \xcaP2Ho\x05\xc6\xe4\x81Om\x16\xe9\x83}\xf9j\xcdl\xe9C\xac\x83\xad^},s\xee\x16\x06\xa1\x9b\xb2\xaf\x9a\x0e\xce\x0b\x8a$\x8e\x88{\xe8\xc3\xce\xa4o(\xdd\x0e\xf5{\xbb\xff+\x1d\xea\x87-\xeb?\x80\xd5\xf9\xb7:\xf7\xfb&?U\xe6\xdf\x12\xa7\x8f\xa3\xec\xb3\x9eC:@/+\xb7=\\7+\xf5\xf1\xa3&F\x1d4z\xfaQ\xcf\xd8\x91\x86\xda\xb8a\xfcJj\x19\xc3\xc1\xc8\xb21\xac`\xeaO8\xdc\x0e\xeeR\x81\x9e]G\xe6C\x1e\xaf\xe22\xbe\x19\xbcL*\xa1i\x04\x1d\xf8\xc2p\xbdX\xfc\xc5\xf6\x05a\xe5\xed#\xaeS\xb2FPW-\x16x\xe9\xcb\xfaG]\xed\xc1\xab\xddaR\xf7\xe0\xd0\x0b\xd8{\xb3@es\x0b\x06\x03\xe9\x8e\x1b(9-s=\x80\x08\x06\xf6\x97\x17o\x7fz%\xc2\xae9u\x82\xaa\xb0\xc8d\xdb\xc3U\x98\x7f\xe6\xa6?\xf8\x93\xc7V;mb%\xd1\xfat\xcd\xdc\x8a\xa7`be\x1ef\xb0p\x9bF\xcex\x02\x8c\xba\xa4\xc6b,\xf7\xa4\xe3\xf9\xf5\x90\xd7e\x95\x93\xf32\x8c>\x7f\xcaCth\xb4\xbc\x11\x86\x9cK9\x01X\x86q\x88\xb1\xac\xa05\xd1EYXhy\xbc\x8c\x0eY\xb2\xf6\xaa\xff\xca;,\x9c\xd8 \xe4HZ\xb9\xd5\xf2&W_\x8a\xb9\x0e\xa3U\xea}\x1a\x81s\x0c\x8e\x91f!h%\xd1\xb7 >l1\x07\x9dz\x1f(\x85C\x9a|$\xa6\xed\xd0s\x0b\xca\x94\xd6\xa0\x84\n\xbd\xf6\x026\xf7\x1d\x96\xcdK]\x95Z\x08>K\xdd\xe9x\xeaiV\xf7B\x01\x8a\xef\xf7w'\xe8\x88\xbe\xbf\xdb\xaa\xd7\xc8\xcb\xb1\xde.\xaf\xb7\xc7\xff\xdd\xe7\xff\x1ex\x92\xc5\xcbc\xc5\x9dv/\xc66(S\xcc\xda\xdc lCip,\xd4\xcc\xd6\xdc\xa9\xa5\x9ed\x00\xe7\xeeY\xeap3;Mm\xa0\xdd\x85!ru\xcd\xc4.\x17\x82\xcf\xb8\xa3Q\n#\xc8\xbd\xe6\x00\xef\x1e<>\xae\xce\xe3\x03\xfapV\xea\x11a\x89$%\x8a\x1e\xc4\x84\x87\xf7oE\x1f\xcax\xb9\xce\xb0n\x10=\x99\x05\x8c\xfdg\xf4\xe4\xea\x9bDO6\xdd\x8f\xbfOPa\xd3H\xf0ZF$N,7v\x91dY\xde7:\xcb\xd0\xe2\xe2]\xf8\x0e\x15\xce#\x14#\x8c\xe1\x18\\\xa1\xc1\xc81OZ\xbfD\xc1.\xaa\xe9\x0f\x10\xdcw@\xd5\x10\xb4|\xd4\x9a @X+\x18\xad\xb7\xba\xcc\x13xs\xf5h\xac\xe6_R\xe5\xb2!\x05\xdb\xf27\xfa\x18D\xd7]\xa6\x0b\xad1\xf4\xe4Nh\x0f\xc3\x1a\x9b\xdf6\x92\xdd\xe1#Ah\xb0\xe1`\x14E\xaf\xfc\x0c\x90N\xd6\x9dw0\x0e\"\x9b\x00\xb1\xa6\x12\xd8\x04\x1f\x0e\xbb.qoB\x99\xded2\x8f\x0dTf\x8f\xaefQ\xdaO\xc6\xbd\xb7\xce\x02\x0d\x1e\x15\xd6\xae\x8f^l\x85\xfc\xe2\xf2Z}\xf0\x0c+\xb62\x06VbNm\x19m\xea>\x16\xbe\xdc\xf0\xa8:\xa1k\xa4\xd7\xb0\xed\xca\x87\xc2\xe7\x99\xf0\x0c\x95(\x1e\x8efcC\x00\xe9\x04\xdf\xe8&G\xd9\xb0\xcc{\x1d\x9a/2+.\xba4\x9fZu\x83q\x80\xcf\x8c\x12xv\xbf\x96\xc5(\"\xcf\x98\x07\x00S\x1c\x17|X y\xc0\xe41\xf2\xab\xc2\x87)\x93\xb5\x9eu\xe3BhF\x96\xd4\xf8\x90q\x80\xfa@\xa0/\x16\xa9\xb1\x1d}6}\xc7Xn\x98\x91U\xbf=\x18\x15\xd0\x8f\xbf\x04\xc3.\x9f\xa2\xeb5y\xf01\xedo\x13p\xfd# \xa3\x92\x07L\xff?\x0e\xcf\x84\xec\x9c\xc0M\\\xc4%,\xcbr}\xfc\xe4\xc9\"\x8c\xc8U\x96}\x0e\xae\xe3rY]\x05q\xf6$\xa7\xdf=\x99gQ\xf1\x04?\xde\x99\x93(\x9b\x93>\x81\x9c\x999\xe6\xa3\x91\xc7,\xd5\x9d\xed0\xbf.f\x17X\x8f\xa4\xb4\x89\x9f?\xbey\x99\xad\xd6YJRY\xaf\x96\xc3\x08&\xba\xf2\x8c\xb5\xa1\x06\x7f\x17\xa2\x89,\x1f\x1e9\xbe\x89\x1a_\xf4\x87\x8b?i]\xff\x18\xe4\x10\xee\xba\xaa\x8e\xc1\xf4\xb83\xfa\xba\x0fq;\xacz\xdcs\xea\x06\x9d\x1b\x89\x82\xb2q4\x8f`\xe5\xebb\xf1I\x87\xf7\xcc <\xac^\xb8?\xb4\xff\x12\xeb,\xb7&\xc1\xb78(\x97a\xf9\x11[+\x98\xd8E)z\x1d&\x05Z>\xba\x18H[y\xf7)\xaf\xf8\xab\xb1\xfe\x8a+\x17r\x11\xcfW\xfdn\x19w\x9a\x8f\x88\xb9)\xf9\xf6\xb46^\xf0\x03>\x04\xa5\x9a\xfdO\xe0\x94\x1f\x94\x8d6P\x94v(\xa5\x9e|\xbf\xa5n\xd7\xf7\xf0iI\xe0\x8a 7W\xd9\xbcJ\x08,\xf2l\x05i6'\xc1\xaf\x85__D\xee\xf4\x1ah\xdf\xeb\xcd\xfd[X\x95\xcb,\x07\x80\xd7$\xcf\x8a\x02^\\e\xd5\xe7e8\x8f\x7f%Kx\xb6\xc0\xc2\x7fc\xff\x04Y~\xfd\x1c\x9e \x88\xd4\x94\xb5\x1a\x15\xf6H\x8aA\x12{\xf9\xa4uu\xb9\x1c\xaa\xc5?CC\\\xb4\xb2\xe4A\x93X\x0f\xef\x94\xf2\xb2\xbe\x10\xed\x98+\xd0le\x11|\xfa\xcb\x87W?^\xbe\xf8\xf8\xf1\xc5_.\xcf\x7f\xfe\xf0\xe1\xfd\xc7Op\x06\xd3\xc9\xde\xd3\xbd\xc3\xdd\x83\xbd\xa7p\x0c\x93\xf1\xd3\xdd\xa7{\x93\xc3\xa9\x96\xef\xd6\xd2ah\xc5\x95\x94\xe2\xa4\xc3yF_7\x86\x17\x1f\xc3\xf4Z\xf0\xc9\x14(%\xf1\x1cI\xd190Os\x865:\xcc+l\xb3p\x85\xbd\xd3\xcfqZ\x1e\nCc/\xb8\xbcDl\x7fy\x89!,\x1a\xf9\xea\xb1b*\x82l7o\x00}\x9c\xe8a\xe7\x18\x8c\xe5\xb8\xd3\xa1\x85y=\n\x1b\xc5\x06\xc2\x88\xcb5O\x80\x07\xc4\x97\x95 \x85\x9an\xa0i\xba\xbd6H\xde\x1b\x14\x0d6\x12\x0b\xeb\xb7\x15\x10\xcaN\x89MZ0\x1c\xc9=\x9d\x8b\xda,\xb9\\\x12\xe6\x86\xb2\x88\xf3\xa2\xac\x11?\xac\xaa\x02\xedgB(Z\xd1j\xe5G\x10A\xf6x\x08\x0f\xb63\x105\x01i\x0cr\x1c\xcb\xd6Db\xfd,\x0c\xaae\x0d\x89\xd9l\xe8;!\xb5Q\xe7\xcdm\x87BnR\xdf\x91~\xda\x9c\x89\x16\xcf-W\xe5lo\x03\x91\xcf\x83\xfc\xae\x1dK\xbb\x83\xedFW\xbf\xe0\xea\xae$?\xe1\x89\xf6\xd1\x0co\x0c\x98\xeb\xba)\x86g\x8d4K\xbf\xaa\xdfe\x8bEA\xca\xef\xe8\x11\xc8*4G\xbf\xca\xaat^\xd8vW\xef\x936\x0e#p1\xf7\xf0\xd8\xb3\xf6\xc3\xee\xdc\xf0~0\x00A#cI\xa5\x00n\xa7<\xf0o\x0b(\xd4F.\xd6*x\x81\x8fM\xc5t\x99\xcd#\xe9\x04L\xa4\x0b\x10\xd1\nk\x06H;\xaf\x8a\xc1\xd0O\xd9\xfdc\x93R\xb1\xc5\xd8tx \x1a>\xc7\x05\xad\xf3\xc9\xdf\xdf3\xe7P\xa7*\x17\x87][\xbfU\x04q\xf1\x8a\xc3\x0d7\xb58`\x7f\xe7\x08\xd0\xe2H`\x83!\x056\x94\x1a\xf6\x98n\x12H\xf8t\x0c\xf70g\x1bg\xf6\xd7\x02\x8e\\]\x16T\xa8d\x86\x8e\xb7y\\\x12\xd7\x02U\xd9'u\x96\x02\x97\xf9\x042#\xfc\xb1\x0f\xb1\xf7\xe8\xed\xf2\xfaL\x1f\xc5C\xd7\xb2\xa8\x15\xba\x141uH\xb3j\xd5\x08\xdc\xc3\xd2%\xc2\xe7\xc9\x166c\x08\x906\x9a]Iu\x82\xb8\xf8SLX\xda\xfdv\xb1\xc9\"L\xaa%\x8f\xb4!0\xdb\xa3\xad\xa9\x99-\xd5R\x0e\x11\x1dK\x1caX\xe2\x9b:\xd9f\xd7*pj\xb3\x1eIW(\xc2\x1c\xc3\xfb\x9d\x9cx\xb5\xa2\xcf\x8a Q\xbd\xe5\x84E\x14\xc7\x8eY\xc9\xc5j$a\x19\xa7\x93\xce*Wq\x1a\xe6w\x96* )w\xcd\xe8\x845\x82d^W/U\xb9\xd8\xe9\xac\xc1\x08\xed\xdeQ\xfc\xec\x96\x9eu\xc1\xa1\xe9.*\xa6\xdd\xe3\x89\x8a\x9d\x9e\x1a\xe5br\x90\x90\xbe:;\x1d\x95\xa0\x19\xf7\x14\xbe\xef^\xc1%\xf9\xd2\xdfJ\n\xcf\x9f?\x07\x83?\x114\xdb\x19\x16\xe4`\xaf\xbf\xa9\x1f\xfa\x16\xb2\xd37\x1c\xa0v\x0c\x19\xba1\xc0\x990\x96\xac\x86Ph\xf6SvK\xf2\x97aA0\x03\x19F\xa1k}\xaa\xebR\xcd\xe0\xeb\xa6\x8bc\x11w\xab\x9c\x11\x03\xec\xe7F\x14\x14\xfd\xf9\x02 \xe6\x83:\xbd\x93\x98*\x8b\xfe\xb8\x01\x01eM1\xf2\x05\xdb1l\xa3E\xdc\x92R\xee\x10\x85\x81\xdc?\x0eyNx.K\xe4\xce\xf0\x8d\"\xa2\xa3\xd8}\xa7.9D\x90F+Ie\x1ekp\x94\xfa\xdcB\x82\x852\xc6j1G\xce\xa5\x1ccQ\x88\x04D\xa5\xfa\xe5\x08i\xfd\x94\"\xc0\xb2#\x88\x82\x98e\xdc\xb9\x0e\xc0C\xe0\xc8]\xb7OF\x13\xf6h\\\x99\xc2J\x91\x86}\xda\x99\xc01\\k'\xcarB\x8c\xc2'\xde0\x81m\xa4u|\x8b\x9c\xc1\x86t\x1b\xf1\x85d\x10\xcac\xee\xc0\x19\x1e\x86\xae*\x8d\xe5\x0f\xe7Z\x8d\x95\x93\xb0(\xdfX>\xc0\xb9c\x12%\xfb\xec\x8d\xbc\xcbM\x98\xd4\x84\xbd`WD\xa0\x8a\x9c\x93W\xadP\x14\xe6\x1b\xad\xaf\xbf\x05\x98d,5\x8b%\xbc_(\x1d\\s\x8dB\xa2\x82\xcd[,\xa5\x16`\"\x05\x86\xd1\x18\xffM!\x01'\x04s\x0d\x8c\"=\xc4\x91\x1b\x17Za\x01\xc7ej\xd1\x8eTf\x95\x17\xc4,*\x91\xa0\xd8\xa7L\x18\xd8\xfc\xee\xbdWt\xa5\xa6>\x84\xf0\x04\xff-\xf8\xbf)\xfek\xb8o\xad\"M0k\x1b(\x1f\x06\x0b\x17U\x89\x8c]\xc7<{\xee\xcfo\xd2rr\xf0\xc3+\x97\xc0\xf7r\xb6\x11\xf1\x98\xef\xb9\xd5&H85\xda&\x8d4\x1d\xaaaN \x83g\x10\x9e@6\x1a\x99\x992\xe0\x9d\xe1\xf42\x0f\xc7\x1fQ\xf0\xc1C_-8\x1c\xce`\x07\x16\x9dr\x1d\xd1R\xfd\xa1\x88\xd2\x9dy>\xfb\x1cF|\x81\x8az\xdf\x16tA\xacMr \xbb\xc3\xc2\xd7\xb2\x163\xd89\xe5\xa3\xf1\xf9*X\x80\xb3}mR\x18A\x01\xcf!\xac1I\x08;P\xe08\xf9\xaa=Gf.\xdb\xd9\xe9\x9arM<'<\x88\xed\x9a\xf1\x80kx\x06\xc5 \xac\xbb\x16\x1d\x94\x85\x87\x11\xac=\x16\xa4\x97.\xfe\xbaw\xa5\x81\x9b\xc0\x98\xfc\xbb\xf5\x07\xe3\xeft\xd62\xcbq\x80\x0f1\xa9\xb7+3\xd6\xb3j@vt7k3\xe0[\xf5h\x07\xe8\x061o1J!\xdc\xdf\x9b\xf8\x18\xa1\x04\x97\x90\xb6\x81\xe2\xcd\x05-\xc3\x9b\xa3\x90\xe79\xc4x\x0chqLq\x01\xfea\xee!\xeb\x85\x9d\x19\xfc+L)/7\xb68r\x0bu\xe2\x92|\xe9P=\xe5\xf0\x1c2x\x02\xd3zh\xf8\xabK\xfeP\xb1\xb3W\xb1h\x87\xa3Q\xd5\x05>(\x9aX\x87yA\xde\xa4\xa5K\x82\xa2\xba*\xca\xdc\xa5|B\xe5\xc3\xd4\xf3ar\xd0!7g\xd4\x9a$(\xac\xccu\xcb\x19\xbdi\x98\x8a&\x1c\x00\xf4Dc\x83\x0e\xcde\xcf\xa1\xe1\x8d\xfd\xd5\xfd\x19s\nK\xc7\xc2C\x95\\\xdb\xa0\xd3\xd6\xd3\xd5\xd0\x9e\xec\x06\x03u\x9b\xb2\x11\xd2\xecB 8Q\xb3\xf2L\"\xc6\xb3\xed3\xc1Q\x19D<\xe4\xc4\x8b\xd2M{$\xfam\xc0\xf7\xc0dy\x9bL\xfav\xd8\xa4\x95\xb5\x19\xd4\xf0\x97a\x0d\xff\xd5\xfda\xf3A\x9f\x0fm{\x90VC\x0e\xec\xc0\x83\x93\xf2]\x93\xaeZ}\xb0\xb6\xb7a\xcbu \xc5NS\x0f9\x02~ \x19+!\xed_\xc5\xf9M\xcaO\xc3!\xcb\x84\x93R\xb0\xb1\x7f\xe0C\xc6\xb6=\xf6\xea?m\x9a<+H~\xf8\xda\x03\xff\xaa\x8b\x9fUY\x08\xf4\xe9TXL\xf4\xd5\xa7<\xc8\x0fw%\x91<\xa2[\x85\\E\x85\xfd\x0c\x1b\xd7\x8b\xaeq\xa5RL\xa1\x9af\x1c \xb2\xc5\x10\xf3\x18\x83\x1ab\x14\xddv\x81\xcd\x8c\x85\xf8\xf0E~\x93r\x16\x1bLS\xc5\x83N$\xc6L\x89\xe2A#V\xcaJ\xef\x1e\xc1\x19\xec\xc11\xfb5\xdd\x853\xd8\xe5\xbf&G\x138\x83)\x1c\xdbD/\x08\x91a\x04 \xad\x87[|\x83\xe1Z\x8c\xf8\xc5#\x8f\x8f\x81\x05\xf6kz\xe1kS\xc9p\xf4jY%\xcdh\xb2_\xcfh2\x85{p\xc5\x9c\xe4)Vt\x8a\xd3\xf1\xdeS\xfe\xdd3\xd8\xdf\x9f\x1e\x1dP\x92\x88\x92\xb3\xfbOw\xf7v\xbdo:\xff\xbd\xc7\xcf?\xac\x7f\xedn\xb0\x1ajYhY\xa1Cm\x85\xa4%\xab\xd4%\x0b\xe9\x92\x1d\xec\xef\xef\xee\x03\x06\xf4x\x06\x93\xc9do2\x99J\xcbd\x9c\xa2\x99$\xae\x8d\xb1(_\x84\x9f\xd3\xb6w}\xbc\xc9\x18tl!\xf7\xe7.(>\xa0?\x0f|\x11\xb5x\xc1\xc4\xa8c\xd8\x86\xc9x\xba\x0b\xf7l\x1397\xb3\x7f\xb0;\x1d\xc3={\xb5\xcd\x0c\xc2\xf9w\x1e\x05T\xa3SH\xda\x10\xdf\x06\xa5\xfb)\x12A\x8c\xd8\x15 \x14\xe3\x14\xbc\xbc\xafI>C8,\xee1\xc2\x13\x85\x1b\xf5\x16 \xe9.\x1c\xc7\x0e\x18s\xb32\x10\x04\xf4\x16\x06\xd3\xdcXz\xc0`8\xba\xc9}\xa6\x9a{\xdfCD\xa5\xedEv[\xe8S\xfeE\x82\xda\xb7\xbd\xf0\x81\x04\xe7Iv[\x97t\xef\xc3\xa8l\"\xab`,\xdc.\xbbBT\xdd\xb9#S\xa0\x837\xef\xce?\xbcz\xf9\xe9\xf2\xed\x8b\xff\xef\xf2\x87\xbf|zuN\xcf\xd3\xd8&\x8b;U\x93)\x9b\xcd\x82\xcc\xe5=\xb1\x13\xed\xf9\x8cn\xa4\x88o\x92\xc9\x92\x9e=G<\xb5\x02M\xb6J\xb2\xe3\xb4\xba\x96Y\x00\xd8\x81\xa8\xb3l@8H\xf1\xf0Q\xed\xb5\xe5G\xe21\xc3\x8e\x07\x1f\xf6\xa6\x9cVZd\x99\xebY\xc5\xa1%e\xc8\x98\xa5\xe9\xf6\xb6p\xeb\xad\xcb\xdc\x89\x0f\x13OR*\xb6\x8fjg\x0c4h\xe6\xb0e\x90\x9d\xa8\xe7\xca\xf5\xe8\xc9\xfa\xfc6\xfc\xc2-\xe4P\xc5L\xcf\xd4:\xcb\x92\xf3\xf8o\x14x\x1cN\x8e\xa6\xb4\xe82\xac\xae{M\xb6\xc1\xb6\xb1\x85\xe2\x0c\xa3\x1fo&\xd8\x1e\xe0u$\xb5\x1f5\xe9\x05\x0d\x16\x98\x1dBjW\x1a\x8b2F\xe3\xb9\xa237\xd6\xf1-\xf6\x93<\x9c\xcc\xf66\xff+@{U\xc2\xf3\xb8\xa9e\x17LbF_\x99\xc3\x9c\x16\xbe\xd6\x8a)\xe0)wh7S\xa3\x9d _\x1e\x98\x1a\x01\xc1\xcef\xab\xbf\x81\xed\xa7\xf8\x02Y>D4ca\xd6$\x1bB2\xf3\xbe3\x93\x05`\xde\xd4\x0f\x161\x0b\xea\x86\xc6\x86j\xa1Tb\x00\xf0}\xa7\x05\x17\xe1\xe7\xb4\x08\x17\x83\xe3\xafX2\xb5\xe9\xcdQl\xf1-\x9a\x94\"\xac\x0cjk\xcbmb\xa1\xdd\xdf\xc3V\x19\\\x8a&\x0c\xadG\xd9j\x1d\xe6\xa4\xcf!\x1bd\xf3\xca\xdar\x03\xdb\xd7\xf4QF \xd9\x8b:\xba\xb7P\xac\xb0/\x8c\xb6&\xcc\xf0Eu\\\xee2s\x90\x15{\x8c\x0d'\xf5\xaf\x98\xc5\xa1\xcfdN\x92\x99\xd2\"k\x98Q\x86\xde\xe2t\x8b\xc3\x98\xc5\x17xD\xc9,\xbe\xe8B\"\xa9\xe0\x1cY\xff\xad\x0c$\xf2c\x97\xddZ\x89>\xccw\"\x94zh\x8e\x04g0Q\xe2\xe1Bs^\x84\xf9k\xef\x89\x11l%W\xfe\x94-\xe5\x8fy\xc2}\x06\x06\xdf\xca\x84\xe3\xbf\xc1\x1ee\x80\x8d\xc3?\xa8\x01\x88) )\x0c1\xb3\x18L'\xf8u\xe6\xd5\xc1\xd0!\xb3\xa6\xbc\xfa\xceI\xe2\xa24\x99N\xf2\xe0{\x90-\x04P\xb0YQZ\x0c\x1f\x04\x01m\xa2\xb1\x11>\x98[S\x02$\x18W\x0b!\x0ca\x10\xa4C\xaa\x8b!\x89f\xe9\x85\x95\xdd\x12r)\x05=P\xbch\x86;f>IO\x1d\xa5\x8d\xc2N\x9cW\xdc\x18\xc5\xce\x06\xca \xbc\xfa\x9d\xf6\x8f>\x153\xe6FM8g|E\xf4\xd6\x9e\xb3\x08\xcd\xb9mEg+dg\x8fS\x98\xfb\xa0Pz\x12\xfa\xdc\x1a\xab\xef\x8a\xdbp=9\xe8\xf3\x0c\x17\x0c\x0e\xc6\x8c\xea\xd2\x13\x95F=\x91l\xae\xc9GRP\x12\xbb1\x1d^UI\x19\xaf\x13BWpr\xb0s\x15\x97F\xb4\xa8(\x1a\xc6'h\xbe[\x9e\xb0\xe37\xf5\xe0\x86\xbb&\x11Jm\x8dZ\xd9KA\"\xd1e\x17M\x10\x8b\xa8.\xcb\xee\xf4\x9b.\xcb\xdeW.\xcb\xee\xf4Q\xcb\xb2\xd7Z\x96]\xcfo\x8a\xe82\xb1\x7fLZ\xb8\x0dV\xeb`\xef\x9b\xae\xd6\xe1W\xae\xd6\xc1\xde\xa3V\xeb\xb0\xb5ZO\xcd\xabu\xa0\x15O\xd9?\xfbZ\xf1.\xfbg\xef\xf1kk\x8a\x1f\xd7\xb5\xbah\x9e\xdc\xb5\xc2\x8a\xa6\xa3\x8e\xaa\xc5~\xb6\x02\x08\x9c\xc1\x0b>\x9b1\xa5\xcc\x07\x84\x87\x92\xc7\x93wh\xf2\xe9F+\xf8\x07\x8d`\x98\xcd\x99\xb0\xfa\x1a#\xdb\xf4\\\x9eO\xe3Q\xe2\x0ck\x17\xfd\xa6R\xbd\x91\xda\xd4N*D3<\x8a7\xcda\xb69Y\xc1\x10j\x15\x06Q\xac\xe2\xe1\x9d\xbf\xd8\xa4\xf3.:W<\xbc\xdd_7i\xb7\x93:\x86a\x14\xb2xx\xff\x9f7\xe9\xbf\xd7v\x18\x9a\x86_m\xd2p\x075\x0e\x83(r\x18H\x95\xc3&\x9494\xb3y;l6\xbd\xc4:4v\xd1F\xc6\xfag\x1e\xf9Rx+\x1e\x83\xcd\xbd@~J\xe6\x8e8\x02\xc7\x19j6\x0dF\x9a\xec\x81\x8b\xe4\xd9dmA\xa5T\xa0N\xfeZ\x85Iw`\x170J\x1bzd\x0b\x122\x146\x9a\x9d\x88\x87\xe3\x80\xfb{\x0e,kY\x88\xd9/\\\x9bE\x9c\x16k-xr\x17f\xb2)F\x98\xffRK\xca\xdf9p\x81\x9f\x9es\xb3\xe9\x9a\xae\xa8\xddy\x10Fr\x7f\xc9`\x15\x96\xd1\xd2}\x12\xfc6}xr-2l\x80#\"\xe3\xd6\x8d\xf1\x10\x80,\xc8L\x10\x04\xe0x\x9e\x0f\xce3No\xd4\xe1r\x9e;]\xebb\x91'\xf5\x1a\xb5\x7f\xfb\xad\xd6y<\x05\xb3\xea\x9e\xdb\x0c!\xa2v\x84/\xc8\xb1^/\xaf\xed\xb6\xb4\x17\xcc\xd6,naT\"|\xdd\x11\x03\x8bv\xef\xefQ\x80\x83/b\x1d5\x9b)>\xee\x8f\x9e\xd3\"@\xfbh\xdb|sx\xce\xc7C\xe8_\x9dnBM\xfd^\x17\x02\xad1{-\xa4\x03|H\xeb\xbf\xf2\xfa\xaf\xb8\xfe\xab\xb9|\x83\xc4{\x19\xba\x0e\xec\xd0\xd3\x83!\xcd`\x87\x1e\xa7P\x96\xe8e>T\x1e7\xdf\xc0\x00\xc8B/\x18s\x15\xacb\x99\xc24\xbb\xe3\x13H\x98!\xedh\x94\xd8%\x80\xd1,a\x12\xc0\xc5,\xe9\x94\x00f\x18\xbc,\xe1:sZ\xdb\x0e\x83\x1f!\x01\xcc\xe0\x19\x1a!\xa3\x04\xb0\x82g\x90\xd9%\x802\x94\xc2(\xc2C\"\xbbI}q\xe3\\\\J\x91%\xd7.Ao[\xf7o\xd4\xd9\x9d\x1aR\x03\x03\xaavu\"\x99\xfc\x7fmG\x93\xce\x8e\xd0C\xdf\x0c\xc7l@L\x8b\xb9Y\x93\xb8L|$\xddt\x9f\xf3_\xadVj\x0f\x14\x1d@\x99\x83\xa6\xe4,J\xf9F\xad\x9b\x8f0\xc2\xe0\xb8x\x1d\xa7\x18\x97\xc03\x04d\xe1\xae\x92,r\x81p\x8c\x10\x84\x87\x0f,P\xc7\xcc\xe7\x91t.<\x16\xc9\x11\x92,\xbd\xa6\xfc\xaa\x88Fk\x0f\xa8q\xcf\x00\x85\x18D\xea\xc1\x19\x05\xcc\xac\xd8\x08\x899\x07Ay3\xd9\x9f\x89\xd5\x1db\x94_\xdb\x18K\xa8pGO\xea\n]\xacU,98\xc9\xc1{\x9e\xd7NM\"\xe2 \xe3\xef\xf0\xafA`_r\xeeeg1\xab\xca\"\x9e\xd7A\xa9\xec\xf1I\xf2:\xae\x805^\x86\x02^U'Q\xabJo\x08\xff\xc5/\xdbJ\x0b\x94c\xde\xf2^\xd6k\x18\xdb\xc5\xfb\xbc\xdc\xa0\xcf>\x8e\x8b7y\xb5A\x93_\xab\x8a\x80\xa6\xdb\xdb\x0d\xba\xed\xe5\xb1x\x9b_6h\xf3\x1fN\xd9q>h\xf0\xbd\xdc\x14Z\xf3o\xc4I\xd9,u\x01\x98A\x13s>\xd5\xbd\xa6\x98\xc2\xb1\xdf\xf9T\x97v\xfd\xdf\xf3\xf7\xef\xfa8\n\xbe\"\xe6\x1bJ\xdb9\x06\x11\x0c\xc4\xccr\xcc\xc32<\x06\xdd\x93\x0e\xe9\xa3&oFp\x19\xe6\xb9\x88\x0d\xe6\xf7\xc3R-\xf8*\x05,\xef\xe1\x14\xf6\xc6G\x07\xb6\x90q\xbfv\xe1l!A3I\x92\x1ec\x16\xac\x98\x03\xa3\xce\x97\xd9\x8c\x992@\xa2\xc1)js\xed\x0c\xe40\x87\xde\xcf\xff\xa8S\xfc\x16\x93{3drv\x1bDw\xcb&\xf5t\xb78r\x95\xd8\xa7\xbc\xc1\xb2\xa6+\xa9,\x82\xe3\xb0\xfbG\x98\xab\x1c.F\xe61}\xd3k\xb7\x9ce\x1dS\x8f\x07M\xfdm\xd7\xd4\x15St\x8d\xf1\x90\x877f\xc3\xcbk=^\xc659\xb1m\xd7\xf2Yv\x01#\x98\xee\x1f\xc0\xf7\x90\xcf2S\x90X\xd8t.\x9f\xba\xe6\"4\x12\x13\xd4H\xb0\xd8\x18\xf6H6\x0e#\x01E\x04\xef*NK\xbb}\xc7\x08\xc9 k\xdc\xb7O\xf9]\x9c^c`\x13Lj\x00W\xe4.K\xe7\x82\xf6ak6\xd0\x0b\xf7\xa5*\x82@\xa7\xc8\xc7K!\xbes\xd8\x18\x8ca\x80\xb8\xb0D\xc4\x0f\xb1i\xb2 \xba\xa8\xf1\xe3\x9fY\x03\x03\xe9\x91\xfe\xf4\xd8t\xb6\xe615\x88$t\xb0\xc7\xc1\x9c\x93/ \x8b\x17\x06\xae\xe8\x87\x1ef\x88\xd4>\xfd\x84\xdbS\xef\xe3\x86\x9b\xf5\x92\xca\xed\xd5\xadud\xaf\x17\x1f\xa6\xaa\xe1\x0ewG\x8b/\x00\xf5\x10\xdb\x18\x94\xe7\xd938\x84\xef)\xfd{\x061\x1c\xc3\x04v \xf6<\xb4\xd16\xbc\x184\xe1\x8f\x1bMxoz\xb4wt\xf0tz\xf4\x8df\xbdg\x9f5iOk\x17\xa7\xc5\x16c\xd0\xe4\xde\x0d\xbe\x1f_s\xb0lG\xb5\x03\x9e<\xfa|\xfe\xa4\xcc\xc88\x9dZ\xaer\x7f\xcf\x16`\xec\xb3\xa5\xf6!\xe6<\xae\xdc\xc6t\x97\xbd\xa3+\xb07h\x0c?>z\x0c\x87\x961\xecO\xd9;:\x86Cm\x0c\xf2\xafB\xa7\xeb\x86\xd8\xef\x08\xaf\xb8aJ\xeaS\xf8\xaf\xff*}=\x08&\xe1\xb9O\xfe\xeb\xbf\x88\xcf0\x05\x0bC9\xa2X\xbb\xbe!\xa5\x888RR\xc4^\x17\xe5^\x13\x92\x8c\xe5\xea\x92\xbe!\xe2\x1bR\x7fC\xa4o\xca\xba\x04\x93\x1d\x1b\x03\x985:\xcf\xda\xea\x1a\xd7\xc2\x1a s#\xf9IM\x81\xc1\x8e\x9eeE3\x86\x11\xec\xec\x101\xef\x13<\xda\xe3\x9e\xe9\xd2\x0f\xbe~\xc2\x87C\x00\x02o\x90\xd4s\x9c\xf8\x9a\x82\x83o\xdc\x90\x1e'\x07\xedc5\xa8\xd3\xa9\xa5Sn\xe9\x81\x8b2\xb9@\x9c?l\x1c\xed\xcd\xfe\xbaq \xb5\xa1\x0cf\xc88v\xa7\x8f\\\x8f=}\x1c\xae}A\xe4\xa2)\x16\xb18\x7f\x93\x83\xa7O\x9fN'\x94\x8b\xa8\xdf\xef\x0e\x1c\xf6#\x97\xaf5\xec\xd6\x18.D\xe2Li\x06\x93\x83\xf6\x14\x94Y\xed^t\x8a\xf0\xe9\xb0\xff\xd7A4x~\xca?\x9fL\x0f=.\n\xdf\xe1\xb4\xe3:\xbbu)\x95\x00\xdf\x03\x06\xf3\xec\x05\x07\x7f\x0f\xf0G\x94\x85\x91`[~q\x82\xe4e\x1b\nf\x1a\x14\xcc\xbb\x17)3,Rf]\xa4l\xc0\"}#\x90\x89\xbe\xd7\xf5\x89Gu\xde\xf7\x80\x11!v\xa4{0\x11\xa9\\\x07@\xd7\x0d\x80\xab\x15\x9a\xb5\xd7\xf1F\xf8UX\x81\x8bu\xedw\xa7O\x0f\xe8$S8c\x8c\xd0x\xf2\xf4`\x0c\xf7\x90\xc2q?\x05\xb2\x01\x8c~\xf4t\xd8$\xee\x15\x10\xfe\xfbM\xe7\xdb\x81\xfa\xcd \xbd\n'i\xd9to\xd0p\x87\xad\xfe\xf0\xe1b\xcf\xedA\x0f\x00\xee}\xc3}\x9dd\xa1\x01\xba?n\xb816\xd9(\x1a\xb6\xc6\x82\xeb\x1b4\x8co\xb5j\xadaL\x86\x0e\xe3\xc7\xac\xbaJ\xc8#\x97\xe3\xb0w\x1cc\xc1\x80\x0e\x1b\xc7#\xd7\xa3\x7f\x1c\x93!\xe3@\xe6\xd9\xca\xcdX\x848<\x9d\xa7\x82\xe0\x98\x15\x0b\xaam_\xea\x06\x04:2I=\x96t\xcc\xe6\x88\x12\xdbc\xfce\x1dN\x1fx!H\x13r\xba\x14\x94D\xdaB\x93\xac*#\"N\xa1\x84'\x1039\x90\x15\xbc\xd1\xca\x9dP\xac^I#\x99\xf0w\\\xc9\x14\xabXW\xd3`\xa4$\xad\xa6\x10\x9f\xd5+\xba\xb3\x13c\x808N*\x18\x964\x16K\x9a}\xb3%m\x11\x15\xdd\x16,\x86E\xd5\xd7\x92\x02\x8b\xfd}\x1f\xf5(\xd6|?\xb8;M\x06\\\xb7\xf4\x04\xb4\x96O\x197\xf9\x1f4\x11\x13\x05\xf2\xd5s\x99\xfaLr\xdc5\x9b3\xc3\xf5\xf0\x9b=\x9b\xb0=C\x11)\xa5\xa9>(\x1dl1\x1b\xfb\x91\x166\xd2>\xc9\xc1\x94\xf2\xef8I>\x1b}\x92|\xee\x86IN6\x9a\xa4\x89Z\xf9\xeaI\xee\xf9\x92H|\xd0L\x19\xcd\"f;\xdd\x93\xa6;m\xca'\x07\x96\xbd6\x1cg\xba2\x1f\xcd\xdb\xdfI\x16I+\xf3;l\xff\xe6+cY\x95\x89eU\xa6\xe63\xb3\xdb\xbd2\x93\xc1+\xb3!\x8a\x15\xd2cyY\xb6\xac\x06G\x02\xd4\xb7\xd0\x03\x86\x8e6\xcbN[\xb8%f\xa8d\xc7\xe0\xe6m\xb6\x07C\\lF,=Qz\x1f\x89\xc1+\x19\xdd\x08\x917wJb\x7f\nsL\x86\xdb\xe9\x84.\xf0\xcb\x10C\x14\xf9\x1a\xdew)\x96\xaa\xe0\xf9s\x18S<\x1a~\x13|\xb5!\x05\xf0?e\xa3;\xa8\x88\xaf\xdal\xb1\x17\x12\x81\x915\x04\xc6\xc6;>\xfa\xfb\xec\xf8\xefB\xa0L\xa6O}\xd8\x99L\x0f7\xa7Q\x14\x1d\x12]Z\xe6\x930\xf9\x1a\xfa\xe5w$_v\xa7O\x0f\xe8\\Q\x860\x0c\xb4\xff\x8e4\xcc\xefH\xc2\x04_K{0`\xca\xdd{;\x80\xc4QH\xa2\xaf\"h~Gz\xc6\xbeD\xea\xf5U\x8c$\xc4-\x1e\xb0\x8a\xff@\xc4\x8fE\xfe\xd4\xbd\x8a?i{\xd6\xe7U\xd1\xf4\xb4\xe9~i=M\x06\xf5d\x93\"uw\xf5\xe3c&e\x13\x14m\xd4U\xef\xac\xa2l}\xb7\x19\xdd\xd2\xa4\x9b\x1c\xa3Cd\xed\"\xd8\xd8\xd5\x97\x9a\xa7\x97\x94\xa5\xa41E\x90+\xd0\x0fI\xdd\"Wq\xe45 \x88\xce\x0b\xcc\xfb\xb2/\xbdS\xdc\x8a\x84\xd2\x0cP\x1eVO\x13\xa4\xcb\xf0\xa6\x0c\xf3kR\x9e\x97a^\xf6gC\xad\xcdx\x80\x19kj\xc30\xf7PdU\x1e\x91\x0dz\xc8\xbb\xc6\xcbZ{\x95\xce\xfb\xdb\xcaU\xe7\x8bz\xf5\xd5\x1d\x95\xec\xaf\x08\xc6^\xda\x916Jy92Z\xe5\"A\xcb\xf4[\xb99n=\x12\xc8\x8d\x1b*\x06]\xe6\xcaA\xec\xb1#$M\x0c,]\xc2\xe4\x04b\x9e\xd5`g\x07\xcd\xc2b\x18\x01\x03\x92\x14\xd6\xd1_\xa6\xb8/\xb5\x93\x11eA&d\x17X\x18\xaf\xcd\xb2\xfe\xb105\x9aY\xda\x06\xfd\x1b\xf3\xb9\x14\xa4\xac\xf3\xb8\x94\x8a\xa9N\xca\xcc\x9e2\xcf\x9c\x0bS\xe8\xfd\xba\x00\xc1\"\xc6\xf4\xf6\x1b\x00\x02\x83\xd3\xd5\xc6\x99\xadEz\x02\x0c\xa9\xc1\xd1\xa6vC\x8c\xe9s%\xb8\xd0\xfe\xc4\xe7Y7\xfa2#\x81\xec\xe2$\x07,\xb7Y\x1e\xd1\x87n\xe9t\xff\xa0F\xd4\x96\xf8h\xf6|\xabz\xb2\x19C><\x9b?{\x9d\xf1{h2o\xcb\xb2c\xbfj.\xe0\xdc\xe6Ul\xf3\xfch\xf5\xc7s\x97\x98\xf2\x9d\xf3\xc5b\xa9\x92\xacF\xbf\x1cF\xca\xe0\xe7\x19\xc3\x0dj\x91\xd5*\xfa\xfd`O`\x0c\xe7\xd1\xc4\xcf\xa3\xed\x9b\xa1Tf\x1bl\xe3\xcc\xab%\xba>SF{\xcc\x93\xc8\x8d}h\"{P,gL\x0bo\x87'\x06\x8b}\x04\"L\x93a\x01\"viB\x85\xb6|r\xacB\x96Q\xf8g7\x15)\xeds)\x01\xa6\xd7\x91\xbc\x99\xb2\xdc\"N\x95\xf9\x10\xd6\x13\xe0\xb6z\xe8\xa3\xacLB\xc0\xc5j\x96\xc1\xbfB\xb8\x81\xcd^\xd9\x8a\x91\xa3\x8e\x81N\xf6op\nOf\xff9\xfa\xe5\xc9x\xe7\xe8\xc5\xce\xff\x0bw\xfe\xb6sy\xf1\xe4\xda\xe6z\xf3\xba;\x84+\xa0r\xf6\x0c\x9c1:\xfd\xabiB\x8f\xb5\x02ul\x96\x0e\x7f\xb6*\x00o\xcc\x01\xda\x08\xf0\xa88\x13x\xd2\x9b\xe3\xb2q\x90\x89Ex~S^\x87\xee\x14*1\x0bl\xd3J\xec\xe0\xc1s\x8c\xe6\xbd/P\xf4\xfe\xd3\xdd\xbd\xbd.\x80\x1b\xf3\xfcp\xf6\x1aP_\xd2\xe7\xb0\x7f\xb0;9\xea\xabL\x1f\x96\x88b\x97\x8eggB\x07\xc3\x93ILw\x8f|\x98\x1cM|\x98\x1c\x1eu\x80u\xf1DYZ\xc6ie\xce\xa5$\x1e{\xf6 \xe0c\xaf@\xa4~\xb2J\xf5\xe4\xe7\x1fi\xf4\x98\x10\xaa\xb3Jo/\xdd\xd9\x95\xf0\x98\x1c\xecN\xad)\x04\xc53lU\xfc\xdfy\xc8)\xf7\xd18\x80\x11\xa5\xebvx\n\x82g\xcf`\xc2\x0c]v\xf8l\x8c-\x88\xb4\x89\x9c\xef\x190\x1f;&o\xeeo\xca\x12U\xf4\xdd3\xd6\xe1\x84eg\xe9K\x7f\xc0\x07\x93v\xcf\x83\xef\xdft\xbc7\xb0\xf7\xe9f\xbd\xc3\xf3\xe7\x98\xcb\x00\x03lcB\x83\x94\xfe\x9a\x1e\x0e\x1a\x16\xee\xd3\xb0q\xedn>.L\xba0\x9d\xee\xb1\x10\x1ep\x00\xdbt\x848\xba\x0d\xc6\xda\x03\x1aq\x1e(\x14!\x92\xb4&V\xd2\xdar\xf6\x99p\x86\x19X(i+\x93\xab\xfbu\xd6\x7fy\x8cw\xa6\xe3t'\x13>\xb5\x07\xbfS\xb8&h\xa8\xd4}\xea\x05,\xe8|\xd3q\x19\x90/\xeb,/\x8b:\x85\xf1\xe0\xd6\xf6\x0e5\x8a:f\xc5GZ1\xa5\xd3\x9cY\x86a\xf0y\xd0\xfb\x0b\xc7<\x02\xfb\x89\x15'\xa7\xc0\xefU\xc6\x8c\xae6\xfdb{\x1b\x90\x0d8=\x95\xee\xdd\xc3f\x93\xda\xdd\xf5\\\x16\xb1\xdf\x07'\xcaIX*~m_\xb1\\\xbbOw\x8d\xeb\xb5\xfbt\xcf\xb0`\xb4|_+\xafx\xf9\x81V\x1e\xf2\xf2\xa7\x9e\xc4\x0d\xd4\x07\xbbh/\xe6\x0d\x8f\x0e\xbac\xd0}\xa6\x1c?\x03\x0f\x9f)\xa7sV\xcfk\xad\n\x0d\xa2\x84\x84\xb9\x8b\x87\x9cX\xb3q\xddt\xa7\xd4FQ\x10)\xdd|6\xbe\xf0!\x9fMt\xbb\xff?\xb4\xffRd\xc0t\x0ctWT\x89\xd0\x9c$\x04c\xfc\xc4j\xf95\xa1\x102S\x0b\x97!\xdd\xd7J-,\xb0f\xe8+{_l\xb6\xf7O\xf7,gH\xf9\\_5c\xf8\xfb\x13HwvN\xda\xf0\x17\x05\xa8n9K/p\x01\xa5\xbc\xd1\x1aU\xc9K\xa5,\x9f\xe6+\"\x8ff\xf0\x90\x1b5\x92\x88y\xdad\xc9!\xf4/\xf2\xe8\x8b\xf9\xf4\xe81k\xd8,\xdf\xe5\xe5<,\xc3\xcbK\xe3j\xe4.\xf1\xe0\x0c\xd2\x99E\xbeW\x17\x1f\x83\xb3\x0c\x8b\xa5s\x01\xc7\x90\x06\xabp\xfd\xd8\xf9\xec\x8d-\xe0s\xa2_{\x06\x0e\xf0v\x8b\xa2\x8d`f\xc6D#9\xcb\xe8G!\xe5c\xc7<\xb1\x80\xb0\xc9d\xf7\xb1\x83CP#NH\xec6\xd2N\x8aY\xf3\xaf\x18\xeb\xd3\xb1a\xa8\x9a\xa8a\xd8Hmbbz\xbaY\x0c\x01q\xea\xdbb\x1bT\x12a\x14N\xe3\xb1s\xc6\xd8\"\xaa\x04\xe8\xd8\xe8\xbd\x81\x9d\x98\x1e\xb8\x9d1=l\x1b^\x17\xa7*XB\xf3\xa8\x94:lh\xc6\xd6\xf5\xd8\"\xc1\x0d\xc9\x0b\x8a'j\x0dS]TG\x86sn\xc6\x81\xe3u\xd7\x98\xd0\x1a\xb5]\x8b\xb9\xc6!\xads\xa6,{\x1bO\xa4\xe4K\xf9)\x8e>\xab\xb1\x98;bK\x82\xd8#Q_\x96B\x97\xb6\x08\x0f\x94\x8e\xba\n\xa3\xcf\xc6\x18\x0f\xa2%[\x98\xfb\x9b&\xab$\xb4\xc3J\x9b\xbf\x11\xb1\xb7\xc2.b\x1c\xa3&\x8d{\x02\xd5\xf6$\x80\x14\x16@\x81XI\xb7+X,\xb6\xd8\x93\xdf\xb1\xddb\xbd5}\xe2\x0f\xc0k\x86D+\xe7\xfa\xcd\xac\x83x\x1e\xfa\x86\xda\x93\xdb\xf1\x9b\x0e\xb5\x95{U\x7fzG\xdb\x93\x89\xf1[\x8f\xd6\xb7ir\xc4\xd35\xe0\xde\xd8Z \xcb\xc1\xe9}b\x1ci\x88\x16|\x8a\x1c6\x137\xc1\x83lV\x8dF\x17\xf2-\x99U\x1dq3\xe1[\xac\n\x8bX\xcc\xa5\xc4}\x0bb|\xdd\xc7\xe2? U\xdc\x801 N\xcb,\xda\xee\xde\xa6,\xda\x81\x89*\xc8y\x96B\x13y\x9f\xf5\x91\x8eqJ\x81 \x99q\xae3m\x14\x13\x0f\x86\xe6*\x9by\x86\xe0L\xeb\xf7R3\xe2\xaf\x98e{\xa3\x98\x9c\xa7\x1ek\xfe\xe4 \xb8\xf4\x02L\xa1\xa5\xa2\x84\x1c\x8e\xc1\xcd\xdc\x9cN\xcb\x9734V\x9e\x0f\x99\x1b\xb3H\xb0\xd5\xd0\xccr\x88\x1aL\x8a\xaa!\x01\x88\xd3\x8cc\x04\xde\x80gD\xe3\xa6E\xa1#\x1c\x9a~M\x19b/\xee2\xc5H6\x0fO\x1c\xab\xb8\x85\x01\xf8\xc0%5.1ghKYf\xe8\x98\x9fh\x9e\x13\x1a\x7fJ\x7f\x8f\x15?\xe4f\xee\x03\xb2\xae\xfd^so\xb6\xc6\xb4)\x03\xf3\xb7\xfd\xce\x83\xcb\xa5|\xa3\x1b\x93\xbafZO\xbeH\xa9\xbbwp\xe4\xb9\xce\"\xcb_\x85\x91\x08\xa5\xf5\xa8f%\x1e\xe0H\x17?p\x1e\xe0H\xe7\x0d2\xce\x1b\xe8\x10\x8d\x891\xf6\x9e\x1eJ\x8b\xe2n\xc6\xd0\xf9\x94\xfa\xe2 \xbd\x8d+\xdb\xca\xf4\xf1\x0c\xa6\x94~5\xd8)\x94p\xc6r\x15s\xf3\x8d\xd2g\xc9N\xab$\xa1'\xbcPP\xd7\xf4\xc2W\xa4#\xa8N\x0cy\xe2!\x16g\x15#\xd5\xa6\xa8P\x16v.N\xe4\xf0\x80\x91R\x19\xa1e\xa1Zv\x8b\x01\xd9##]\xcc\x93A\x1a\x12\xa2\xaa\x99 \xd3v\x05\x92V+\xc2_g\xed\xd7\xb7y\\\xb2\x97\xa1\xf2\xee\xc1\x87\x02\x19\xc7\xd8-\xe8\xb0\xe8\xcc\xa2\xe6\x90z\xc1\xf5\x90\xa8\xd3t\xc3\xf8V\xf9\xb00\xb3A\x96]\x89\x1a\xd3\x18\xf3\xe6D\xca\xe6\xecJ\x9bC\xc1\x99\x14\xba\xe8\x182\xce\xe1\xf3\xf7\x14\xae\xa5\xea\xfb\x149\x1c\xb9S\x1e\xc1\x87nh\xd4\x8cAz\xa3\x1d\x06q\x10\x8a\xe6 \x84\x86\x83P\xb4\x0e\x02\x8fa\xde\xde\xf4kR\x1a\xb7\xbc\xa0\xe5\x86\x9dV\x8fB\xd8}\x14Z\x89y\"\xbe\xdb\x11\x1d\x0ff\xc3\xf9\x16 I\x92\xe1\x1c\xdaD\xa9\xc1\x8f\xaf^\xbf\xf8\xf9\xa7O\x9c\xb0\xcc]\x0d\x0e\xb3 \xe7\xc70K\xdd\xfd]O\xcb\xdeO\xbe\xac\x938\x8aK\xfe\xfa)\xdd\x16w\x7f\xf7\x90\xff{\xe4I$\xcf \x18hgP\x05\x8d\x0c\xa9;m p./I\xf16\x9bWZ>\xd6AKG\xdb\x93\x05\\\x8a\xf5C\xea\xd6\x1abwz\xc0AI\xea\xee\x1eq\xaa;u\x0f<\xd7\x11&\x1b\x9f\xc2k\x01Z\x9c\x97\xe7\xe7\x1f\xab\x84\xfc\x14\x17\xa5\xff\xf2\xfc\xfc\xbc\xbcK\xc8\x8f$J\xc2<\xa4#\xa1e\x7f\xa2p\x85UHb\x92\x96\x1fIT\xe2\xcf\x1f\xdf\xbf\x95\xfff\x8d\x8b_\x9f\xb2\xcf$e?\xc22\xfc\x94\x87i\xb1 \xf9\x9b\x92\xac\xb0\xf0u\xcc;\xfd\xf7Oo\x7fz\x91$/\xb3$!8y,\xd1~\xbe\xce\xf2\xd5\xab\x84\xd0[\x8c\xbf\xcf }+J\xde\x92y\x1cbco\xe3\x15\xa1\xe8\x96\xa5\xe9}\x17\xae\xc8\xfc]6'o\xc3\xb5O\xff\xc5:\x1f\xc2\x98\xce\xe1\xaf\x15)\xd8\xd0?$\xd5u\x9c\xf2\x7f\xd8\x97\xe7\x7f\xfa#K&\x87\x15\xce\xff\xf4\xc7w\x88\xa5\xc5\xaf\x0fa\xb9<'\xd7\xf5\xcf,NK\xf1CZ\x85\xf3?\xfd\x91\xcd;\xcb\xd9\xa4\xcf\xd1D\x95\xa1sV@\x97\xfb|I\x08\xfb\xfc\x13eg\xf20\xfa\xfc\x92/x]\xc0~eU\x84#r\x82b\x9d\xc4\xa5\xeb\xf8\x02Z\x8cO0 ~X\xcb\x80\x8b\xd1\xc8\x04g\x11\x1e\xce\x8a\x8b\xf6\xbd\xa7\xe0%\x9fE\x867h0I\xe9\xf2E#\xf4V\xa14\xe6<\xdeJf\xd5\x05\x13\xd2%(\xf9\xa0@\"\x9bE\x94\xab\xc8\x02\\\xd7\x9e\x13\xaf3<\x14\x8e\xfe\xf6P[\x1am*\x96\x13\x02D\x0eH=\x1e\x86\xf5\xd0\x87\x9dI\x1f)e\xbb\xec\xdd\x94`m\"\xd7\x10\x80\x12\xf1\xf72L\xbf+\x81\x0e\x06V\xa4\\fs\xc8R0\xe6\xeaii+7\x1b$\x07-\x83Y\xca\xa9\x0d\xeav\xd2Y\xa8\xc7\xef\x13o\xa6\xbe\x1e\xa1\x87\x19\x16ZR\xa4s\xe3+\xb1\xe3B\xc8\x8b\x80Mlc\xd3\x9f\xa1\xe5\x8eF\x91\xbe\xff\xf4\xde1h\x1aeY\xcc\x83\xfa\xba\xd0^\xb7`\x0d\x1dl\xc9\xa9(w2=\xf4\\'^\xe4\xe1\x8a\xe8\x1d\x89'G\xe8b\x13\xab\"\x92$AA\xc1l0\x8f\x8bu\x12\xdeQ\xac\x97f)q|\x9c\xfb\xa1\x17\x84\xeb5I\xe7/\x97q2g\x99\xca\x83\"\xa7\x80\xd2\xf95\xbc \x8b(\x8f\xd7\xe5\xb1\xe33\xabV\x12DYZ\x92\xb4\xfcs\x9c\xce\xb3\xdb`\x9eEH\\zA\xb6&\xa9\x8bn\x03,j\xa7\xf3\x8c}\xfa\\T ^\x9f2\xc5\xf1\xb3_\x9e\xf0W\x98\x81)\x88\x92\x8cE\x8c/\xf08\xbd>\x81|g\xe7\xc4\x03\xae\x9a\x94t\x8d\xb3l\x96_\xd8\xad\x02\nWS\x89\x9a\xaf5O8\xcf\x94\xd7\x94\xa4\xed\xe7\xa7\x8c\xf0\x89\xabf\x04m\xdb\x0c\x93\xa2\x12\xb7\xf4\xfc:\xdce\xe8\x83\xfa\x9aK$)\xc68e\x0eX\xb4j\xe1\xaaY\x95\x08\xd2\xe0\xc7\x10\xbb\xa9/'\xe8\xed\x07\x87\x02}\xa0\xf7hDb-=~\xae8\x96\xf6\x01?\x9b\xa4\xabx\x17\xbe\xe3\x0e\xce\x1eW\x84\xbb%\xfa\xf5\xb0\x10\xa8\xa9\xb71\xcf.\x11t\xbb\x9e\xeb|&w\x85~\xf2\xd9\xa5U,\xcc7\x1av\x8e\xe1\xa3\xee\xc1\xc5?\x98\xec\xe7\xf1\xa34 #g\xce\xe5e\x94\xe5d\xe7\xd7\xe2\xb2X\x869\x99_^:\xa2O\xf3;\x8a\xe8\x1f;\xa1XL(f\x13\xfa\xed\xa1o:6\xc4\xe9DYZ\x94y\x15\x95Y\xee/\xc3\xe2\xfdm\xfa!\xcf\xd6$/\xef\xfc\xb8\xf8 \xce\xef\xfb\x85\xbf\xe6\xc5o\x8aW5\xbf\xe4\x97\xd9OY\x14&\x84a\x03_\xa0\x05\x9fc\x1e\x99j\xdbl\x95'{^\xb00\xcaTtQKf&\xf6\xfbV\xd6\xcc\x98\xa3\xcau+\xc6#\x9er\xdb\xf9\xb2\xb9\xc6\x18\xd0\x98\x99\xd4\xa0\xb8\xa5\x0d\xcdUfs\xcb\x10PA\xc8,\x94\x17\xbd\xfb\xb7!W9\x9d\x1cy\xee\x96\xec\xeeBq\xcb\xbe\xc7s\xde\xfb\xe0\xb0?\x1c\xbf\xe3\xb0\xa1\xfd\xc9%]\x8a:S>\xf7O\xbaD\x83\xaff\xc8\xbe\x1d\xc5I\xe8\x8d\xb7g\xb6\xaf\xe1\xed\x9a\xa1\xaebHvf\x17\x041@\xda\xee`\x9e\xa5*\xffI\x9f\x07\x06\xbc(\xe0\xc6\xe5m\xe66\x92\x8d\xeb\xad\x9d\x19&\xc2\xfb\x99X\xf7v\xc3[\xb071\xcb\x15[\x9cm\xebF\xd4r\xd7\x02\x89\xb7\xbc[]\xa4K\x08\xd5\xf1\xbb^\xefm2\xed:A\xfd[\xd5%d\xaf\xf3\x11\xff\x9c\xce\xc9\"N\xc9\xdc\xa1H\x84\xc9\x8f\xf8\xabwU\x928Fg1\xa4E;\x119\x0e8\xbf3\x94Jc)g\xc4\xe0\x98\x02QX\xa7\xe6\xd5\xf4\\\xe8\xd1\xca(\n\xbc\x12\xb1\xe7q\xac\x9d\xa1\xb0\x08\xb5\x00\x0e\xab\x80\xc3u+v\xca<\xcfFV\x03KBCP\xe3 m\xdd1T=\x80\xc1D\x02\x8c-\xa8?\x0f\xd3y\xb6r7\xdeM!\x92d\x86\x8a\xaeC \xc2(,]}\x17\xe9xK\x1f\x1c\xef\x92\xd2\x8e\xa3Q*\x92\x04q\xf8\xb1{\xf0x\xb4\xbbk\xbe\n\xfb^M\x8f\xb6/A\xee\xc6\x1c\\\xc7\x9c\xf4\xe3\xf2\x93\xc7\xae\x00\xdd_\xad)fA\xf4\x9bn\x8a7x^\x93\xddn\xaa\xe7\xa8\x9fS\xfd\xef\xa0z\xf6\x9fZ\xf0\xf1\xbe.\xf1\xcb\xcc \xaao\x12\xff\xbb\xf1\xf1\xc1\xc4\xb4\x00\xc1b\xc8>Rn\xc2^ $h\xdb\xe6\x92\x10\xa3\xad\xf3l\x15\x17\x843&\xa5+O\xc4\xea\xc5\xa4y\xb4\"\xd3$\xfdN\x0d\xd2\x9e\x1f\xc29|\xe0}Id\xa5=\xf3!\xea.\xd2\xdalX~\x1e\x04:\xceI\x91%7\x84\x03\xd0\xba\xf0W\x96\x858\xd7\xddZ\x1e\xbe\x82\xff\x98\xec\x99\xa5\x05\x93\xf1#O/\xb3?m\xb2JJk\xc5n\xc6\xffq\xd0L~\x04\x0e\xcc3R\xa4\xdf\x95\x98\xf7g]BN\xae\xc9\x97-\x8b\x8e\x94\x83\xd3\xaf\xba\xd0\xf4\x82b\x8e\xe4\xfe\xabiD\xeep\nO\x82'\x9a|\xc7\x88j\x9d'\xc1\x13\x07f\xe5\x85K\xb4\xbd\x128\xb6\xb5p0\x04o\x93Y~\x81J%\x1f\xb6\xac}@\x0f.7-\xef\xa6z\n\xf3\xe5'A\xa3\xfb@ e\x1b.Tn\xeaN\x0f\x0ft/\xdc\xb8~u\xa8\xbfB\xd2\xceD?\xc4\x01W\xc3 \x85\xd1\xf6\x08\xc8\xeb\xf7g=\xc0DPE\\\xe7\xa8\xed\xd8\xf1\xc0\xaf\xad\x84\x8e2\xd02\x90\xe0\x04\xcb*\xad\xbcFPS\x17I\xe2\x94\xb3f\x8e\xc7\x96\xa1\x9a\x0c\x83*+\x90\xe5\xc3\x91\xb6\x8c!\x9b\xf6\x0ckuWi9I\x0f\xd2\x11\x10\x93\xd9p\xd7N!s\xeb\x1d\xf3:\xb7\xccBPW2A\x9d)@\xb1s\x0f\xff\x1e\xfb\xb7\xc1\xd8\x87\\G\x82h5u\x0f6d\xb6L\x82\x9d\xd4\x9d\x1a\xc9\x9bC\xb3\x01\xc7dl\xf6CAi\xc6c\xc1l\xcc\x1d\x94\x98\xc0G\xfc8Eb\xf4\xb7\x0748j*\xfc\xa6[3:\x97l\xf7\xd0\xbd\x1bC`0\x0f\x84\x98\x87\x9f\x0e)\xf3[v\xb0\xb9U\xb0p\xb5\x08\x06\xbd\xd4Q{;\xb8\x00\xf6\x9a\x94\x92\x84\x89\x0d{C\xbf\x91\xdd\x03}K\x84\xcf\x90\x99\x12\xdd=\xd4\xad\xde\xb9\xcf\xd0\xa1\xceQp\x9f\xa1\xc3\xe9?}\x86\xfeA}\x86(\xaf\x94\xbaO=\x1f\x9c\xb7\xe1\xfa[9\xa1\x1d\xea\xde%\xdc\xebdj\xf6:\xd9\xdb\xd5\x0f ;P\xfa\xf1\x0by\xedG\xfb\x81\x18\xe1o\xc9\x11\x93|\xb628\x06'k\xe4\x0dR\xd5\x8a9\xba\xc4n\x89\xe7\xa1\xa4\xe7\x81\x82\x0c\xc6\xb6\x86\xfd\xc0U_3z\xae\x8f\xc6\xe3\xa7\x93\xa3\xa3\xe9\xfe\xde\xd3\xbd\xf1\xd1\xd1\xa4-nx\xf2\x9f\xee\xd9\xf1\xf8~6\xd99\xba\xf8e\xfe\xbd\xf7/O\xfa\xd6\xc0\xa2\x86\xc1\x10>|:FZxk\xcb%\xd2U\x13\xfa\x13\xc2\xb2\x9f\xc8F\xae13v\xe3hg\xeb\x94\xf9\xee\xe7AI\x8a\x12u\xba\x88\xb1\x84\x0b?\xcb\xffy\xcaC\x97\x96\xf0\xac\xd7\xefd\xc8J\xf5\xad\x82\xed$Xb\xeft\x0c\xf7T\nu:\x08m6\x17\xc2\xec\x84\xd5r\x1e\xa2\xb7\xe1\xc9/\xc1\xfd/3\xf7\xecx\xf6\x9f\xb3_..\xbe\xbfwg\xcew\x17\x9e{v\xec\x9em\xfd2\xf1f\xff\xf9\xcb/\x17\xf7\xbf\xfc\x12x\xdf\x9f\xfd2\xf1~\xb9x\xd2\xbe9O\xfe\xf3\x97\xdb\xef\x1fu@\xb8\x7f_\xa3o\xde\xd2\xc2\xdf\x8bm\xe8>A\x8a9k\xaa\x90bu\xc1U\x96%$L\x9b\x12\xc5Ik\x0bY1z\xbe*q\x9c0\xbaX&\xff\x12_\x10\xb6Cq*d\x88\x1b\xa9\xf9j|\xd4\x96\xe42\xf15\xb9!).\x9d\xf2\x13I\x03!\xe1^\x85_~\x8a\x8b\x92\xa4$o**\x855\xb3/\x8d\xac=\x84|C\xd0\xd5\xd9Xlo\xcc\x04\xda\x9a-8\xedi8\x1bD4k[\x00\xda9L}H\x83Wt-_\xad\xe2\xb2D\xdb{,k\x10\\\xb3\xf2\\\x0d\xa1\xbe\xd5\x16\xbd\xa9\xc3\xa9\xe3\xb7\xea\xfb\x89\xf6}A\xf4\x1av\xa8a3\xd1\x06\x91\xc9\x18\xdd\xc3\x99.\xd7$\x9cH%c\xeduV0K\x8cN\xabm\xf3\xb9\xf2\xd50N\x0f\xea\x8c\xc8*\xee\x8e\xc8 )\x11,\x96\xcd1\x8f&(\x1fsW\xbb\x06\xbf=Pr\x81\xd0\x999M\xd4AwK\xae\x16\xe0k\xee4\xdf*gF.\xedr\xe1\x97i\xa2\xd2x|\x0e\xd9\x14\x97b^\x91!9[\xb0\xb0\x1fb\xf1\x0dY7\xe9\xec\x17\\f\xc7\x1d\xf4~N\xa3\xb0\xba^\x96>Ti\xb1&Q\xbc\x88\xc9\xbc\x9e\x1b\x0e-\x00\xf7;\x9e}\xd7\xf1L\x927\xd6\xdf\x82\xd9t|)\x99 \xefB\xa9\xf6\xd0Z\xe3\xac\xc9\"\xcaW`V^\xd8\xc1.\x83\xcb\xa9\xe75\x0e~\x9a\xed\xb9i\xc9\xba\xfc\xf8\xd2&G\xbfE\x9ah \x7f\xd2\xe5\xca'5\xea\xab\xfb\xb4y\x17\x16\x17r\x82\xde\xb8\xaa}\x92\xb7,\"\xdcD4\xdb\xf6\x91\xed\x84\x92=\xa0J\x813)\xb9\xadG\xbf\xcd2\xe8!\xdct\x1d\xe9\x8d\x83\x0c|\xee\x92@\x0c\x89\x92\xfc\xcd/$\x87}\xfd\xfa2\xae@\xbb\xd2\"\xcaaS\xc4\xc2\x06\x11\x91\x9aOn\xe0\x14fZ\x91\x0f\xe4\xc2X\x91\xf8\xa6\xcet\xb0J\xbb\xbb\x0d\xf3\x94\xcc\x81\xa5\x0b8\xa5\xc8\xbb\x85ZP\xdbjD\x9b\xc7\x06D\x84\xddT\"\xf6\xb0\xde\x1d\xb7)x\x0e\x15vi\x19\x0dsa\x88\xb2\xb4\xc8\x12\xc2\x80\xbf\xeb\xb8i6'\x1e\xd0*\x18>s\x9d\x15E|\x95\x10P\xc8\x84\x15Ye\xf9\x1d$$\xfc\x0csR\x92\xa8$\xf3\x00\xfeu\x0eI=\xeap>\xa7e?\x17\x04\x08\xfbJ\xc7\xf6\xae\x07e\x06q\x1a\xe5\x84\x02\x9b$^\xc5e\xe0\xb4\xb6\xb4\x89\x93j\xa4\xbf\xc4\xf8\xcb<\x8c\x90\x08U\n\\\x91\x0e\xc9v\x932\x14i\x98\xaf\x96^\xb3?\xf9\xf67\xbaY\x82\xc2\xa7(Hy!\xd1\x95&dS25\xd2*\xbb!b\x0et\x98\xb1\xc7\xe3\xbb#\xc2\xa3\x9bNT\xf0#\xa0Y+\x82\x92\xfcKXi57\x10o\x00\xf6\xc9\x96#\xeeYkud}kyS\xfb\x7fQB\xe9w\x81`\xd8\x8c\x0e\xbf\xf4\xcb\xdb\x11w5^\xb0\xfbl$$j\x0c\x901a\x1a\xddQ\xa1s\xcc\xddT\x02k\x94\xea\x97V\xf5\x14\x83\xbdr\xd9T\x0b\x16)\x90T[Q\x15\x98\xaa/\x19<\xd5\xe3-\xab\xb8\xd0p\xa4jlX\x9d@\xb8\xb3C!\x8e!&\x0d\xf0\xc5Hg\xe1E3K\xfa\xab\x99\x17\x9d\xa5R\xc0'\xda\xeeS\xf5\xdf\xc4\xfe\xab\xf6\"I\x86\xf1Vf]{\xebz\xf4\\\x85\xad\x8e97!\xecYf\x1c\xddm\xf3Lg\xf4Q \xa0\xe3\xdc\xed\xed\xce{\xd1\x1e\x92\xb97\xebA'\xe8D\xaf\xccX\xdf\x1en8 \xb6\xb0\xbd\xd0nGLs\xdb'z'\xda\xf9\xc1\xe5\xd0`+\x18y\x9a\xdc\xc2\xd3X0\x83\x1e\xee\xbe Oi\xa1\x8bO\xea\xbbqbotV\xdf\x99\x1dh\xf1\x1d|%\xba\xb6\xd1v\xa8\x93Ag\xd9D\x96\xb6i$\x16'I\xbf\xc6g-\xe2\xcf@\xf9 \x1a\x1f\x8eav\xd17\xd6\x97Y\x95v\x0b\x04tv\xdf\xa6\x1e!\xed\x8dm\x9f\xb3\xc68\x83/\x83!u&z\xee\xd4\x15\x84\x05j?\xbc\xd1\xb8\x11\xfb\x0c;\xc2\x85\xa9_\xf5\x0b 5q.\xcf\xc5!{\xbeO\x0e\x9fz^p^\xe6$\\q\xd7\xdd\xe0# \xe7\xe1\x15Z(\xe0\xef?s\xbfg\xf6\xc1\xe4)\xfa\x86\xfcX\xad\x13\xf2\x85\xa9C1MLP;\xf9\xb1zGS,\xfd\x10\x16\xc5\xa7e\x9eU\xd7K\xa6\xfb\xd8?\x1c\xa4\x83\xed\x0d\xd1d\x0ett#\x92\x99\xb9\x18\x07MyW\x93\x7f\x06\x95?h\xc7\xc4$$\x89\x0b\x8c\xb4\x02\xc2o\x83!\xa1\xb4\xcc\xef\xd4\xa2E\x9c\xc6\xc5\xb2\xcf\xc7\x87>[\x9dK\xa0?\xb5\x96\x8fujG\xed\xa52*{=\x0e\x93r\xa3NQ~\x84\xd6%\x0fD8({\xa3\x80\xfa\xdd5I\xe7qz\x1d]\xed\xecP6\x8f't\x81\x1cW\xd0\xfam\x9b\xf2\x10\x0f \xa2,\xffL\xe6\xdcc\xb5x\x9d\xa3]\xac\xa9XlRIy\\\xd3g\xa7\x86\x00\xa8\xf4y@\xb5\xb7\xc1V\xa8\xe3r\xcb\xb7i\xd5fCB\xee\xe4N\x82\xab<\xbb-\x18\xf12sn\xc6\xc1d\xec\xf8@\xff8\n\x9c\x8b:\xfaW\x13\x0f\x8cA\xc9\xb1\x0f\xfb\x1e\x8f!\xcd\xbci\xb2:\xda\x8f\xda\xdb\xaa\xbe\xa6\xe7e\x88Z\xd9\xeb\xf6pP\xc8\xe2\xee\xeby\x04\xa3 N\x97$\x8f9L\xd8\xd5\xd36\x08\xb1\xa3\xf9\x90\xcc\xc9:'QX\x92c\xbc\xdeO\x0d\x0b\xd8V\x85'\x1c\xfa\xe8z%\xfa\xac\x99\xc6i\xec\xf1\x906\xed\x1aK4\x81h\xf2\xa6(\xde[\x1e\xfcfH\x0c0\xf7\xe1\x86\xf7i\x07\x0cw\xf8\xb1\xe5\xe5\xb5\x114\x03\x97\xaf\x85H\xb23X\xc8N\x1f\xaaW\xda\xf7D\xdcb\"\x0b~\x0dt:\x82\x12\xa6\xe5x\x9b\xcd\xd1\\l\xab\x94\n|\x16V\xd7m\xd7\xd3K(W\xb6\xc5\xfc\xf1\xe8\xf9x_\xbf1PZ\xb5~5X\xc6\xd7\xcb?\x87%\xc9\xdf\x86\xf9\xe7\xf6\x16\xd0'\xc2\x8a\xa2\xdd\x7f\xef\xff`a\x18\xdd\x19L\x0e\xe0\x18&\x07\xbb\x87{\x96UP\x86\x02\\k\xcbh\xd3\x18\xce \x86c\xbe\x16Q\xf3\"\xa2\xe4H\x04\xc7\xb0\xf0\xcd\x8d\xc8\x19\x15[\xef\xbd\x06\x94\x87\xc9\xcb0I\x98\xc0g\xe2\x0b4@\xe6?\xe6a\x9c\xca\x85\x0c\xe2i%\xeaw\x0c3\xa8esR\x94yv\xc7\x0b\xcd;\x92\xe0;\x9e\xe7fN\xa2l\xce\xbd\xablxJ\xa9C?N\xea\xdePB&R\xc1\x00kP-\xbb\xbf\x07\xa7*\x17\x87B\x98$spX@w\\\x9b*\x03\xb3R\x9d\xe2.\x8d\xb8\xb8\x04\x7f_\xe1U\xfe\x90g\x11)\n\xed\xe3,E_\xd1N:O<[\xdd\x94\x92\xfc\xdc41Moe\xd8h>\x9b\xe2\xc9\x99 \xfa.\x8d\xba\xeb1\xf7f\x1cxteG\x87\x94\\\xec\x9f\x95xJ}mE\x07\x0d\x85Q3\x07\xe2\xee\x91\x84\xa4\xbe\xf4\xb7\xe2\x86\xa5?\x0f\x88\x8a\x89g =\xba#G\x8aggGB\xee>\x1a\xe0\xbb\x0dNrc\x1fr\xcf\x97\xb0\x94\xfb\x8as\xe4~k\x1f\x98\xd0\x94 E\x85<\xb5\xe4\\=\xd3_\xd1\xc60f\xbfO\xc5\x1b\xcf\xf3!\x91T\xc5\x83\xf6\xf4R\x05\x8aL\x8en\xdae\"\x1f{\n>\xa4\xbbQ\x89\x9f\x1c\x9e\xa3\xe6@\xc2\x8b\xe8\xbc$V\x8aBN\"0!K*\xc1\xde\xb8\xac\xf7\xe6\x9d\xdc\xcad\xd0l\xae\xa4\xd9\x98&\x91B_\xf4\x03\xf1\x88\xb8\xc6\x1c\x07moc\xf4QA\x0ca\xda\x9b6q\xc4!\xf2\x9c\x969\x06(\xfc\xe0\x96\"\x86\xa5\xc26\xe6n\x03\xbb\x07\xcd\xf3\xd6:vb\xa4?\x0c\xd9\xb4\x04\xcd@t\xd0a\x16\x04\xd5\xdb\x87\xf2y\xa6\x8a\xa0\x98\xcf\xb6~5\xf1o\x84Lv\x82#\x069\x92ln\x89\x02\x02\\\xeao\xe2z\xcd\x98(k$\x05\xe6\nu|\xad\x90\x81\xcd\x82\xad\x1b\xda!\xc7\xa8\xae`&O\x98^\x0e\x95d\x05\x0b\xea\xc6\xa3^\xe0j\xf8\x10\xc2\xe8\xd4$L\xa3\x0f\xc69e\x88\x00\xcd\x7f\xfd\xfa\xf6\xb1\x1bSg4\xf3\xc1q(i\xc1\x10\x80z^F#\xac\xda\x81R\x18IB\xc9\x15\x8bP \xe3c\xcdd)\x8fg\x17\"0<\xc1\xce\xad\x0d\xcf\xb4\xcfz\x17\x05!d\xc4\x9d\xf2\x98\x9a\x8f\x0f\xa2e\x95Z\x18-\xf1\xa0\xb1P \xd29v\xd7M@\xc4\xeb\xe9\x16\xf0\xd0s_\xef\xd0\x04!\x93\xc2\xcd\xc11D\xf5\xa6E>e\xc0\x12\xed8\x98\x17\x8c\xde\xf9\x1a`z\x1b)\xa8\xe8S\xbb\x88\x0b@d?\x0d}2\x1e\x90@\x86\xf2\xado\x81$\xc3\xe0\xf0\x97n\xff(\xc1Abtx%\xab\xb10ld\x85\xfa\xb8\xd0d\xa2\xe1-\xd9O\xbe\x8c\x83\xc6un\x85\x9b%G\xa7\x0d\x0bc\x95Pj\xc0\x1b7A'\xc6SviU\x1aN\"\xda\xeb7\x8e\x05\xf2\xd3\xe7a\x182xe\x9d\x94\x80\xf1_\xbatM\xec\x10\x0d\xe46\xd59\xdd\xdf\x03Q$\x07\x14,Z\x88\x17N\xad T\xd2\x80\x99&{\x18+\\\xd59\xe7\xaa\x90;\x1a\xb8\xa4]\xa8W \xf6\x86\xe6fw\xc8\xd2j\xd3\xa4/\xd9\x94C\xeb\"5\x92EJ\xf2R0p\xad:\x8a\xd4A\xab;e\xe55\x16*\x85\x00I\xbb\x03,\x98\xc8\xec\xe2\x04\xca\x13\x8fN\xa3*\x96,4 \x12\x82t\xd9\xac;\xadyy\xb7\x81d\xaf\x18\xdf\xee\x96J\x1f\xee\xe6\xc4\xfc\xd7\x84\x9b\x93{-{\xac;l:\x8e\xc9\xe5J~0\xcc\xe9\"\xa8%\xae\x9b\x05|\x97U{\xf5\xd2\xbbv\xde\x10\x18\xc7\xe7hL7\x1b+\xc4E#\xf9\xe5\x96JZ\xc5f{)wC\xc2y\xe0\xf8\xe0\xfc\xf8\xea\xc3x<\xde\xb5\xa4F\x83\xf6\x05\xaf\x8b\xed.\xbb\xf8\xda\xb5\xb1\x08\xdc\x13n{\x9b\xff\x15,\xc3\xe2\x0d\xe7\xb7\xc0\xe6\xd3\xf8\x9a\x97IQ\xc7\xda__\xd0\x8bK\xef\xc6\xb0\xda\xbe\xe5,\xac|\xc3\xc8:\xdc\xef\xfa\xe5I\xb5#\xcc\\66-\x1b~\x93\xde\xf6\x15\xf0T\xcd\xdb-\xc9\x8a\xcc\x8f^\xf7a\xcb\x07\x84B\xf3^\xf1]\xedG*5^\xb6\x94\xf2>\xac$\x10\xb1\x8e\xd7\xa4\x0f:0 \x80\x8ah\x9a\x1c\x8a/\xc34\xcdJ\xa0\x0d\xf9\x18\xa7>\xe7\xeaM\x9d\x15\xd1zn\x8b$\xed\x1a:$\xebY\xe4Y\x03cn&\xbb*\xc6\x1e\x19\xdfa\x80\xe4X\xa6\xab\xea\x84\xfb>\xac\x9b\\\xce9nh./\xe8\xd2\x8e\xd2B$\x0d\xd6J*h\x91\xd9|\xf0\x91Zc>\x01\xdd\xfb\x13\x80\xe7\x10\xb4\\A6\x81T\n\x0eM\xa90\xca\x17\xb0\xf0\xd3\x02\x00Rj\x1b\xd1%sr\xd5$\xd3j\xeb[R\xf0}\xd1\xfa\x9d\xe7C\xcc\xe5\xeeg\xc3p\xb7\xa0\x06\xa4#\xc3\xb6>\\\x94$\x07\x92\xcem\xc1*L\xd4\x8d\x84\xa2\xf1\xb0\x98V \xefb\xca\xc3^\xeb\x9c\xb7\x9dK\x07I=c\nZ\"\x9e\xca\xa2H\x00\x89\xb8iH\xe53\xe6\xa9\xa8\x06\xe8\x7f\x1b\xde\xe1Ua\x0b\x81\xb5\x11\xf4\x14PfP\xa0\xb1\x80cM\xd6\xdf\x04\x05a= 9\xa4\xaa\xa3\\C\x9f\"\xd7i\x9a\xa5;\xac\xd9'\x1c\xd3 \x9f\x83\xc1\xbf\xb9A\xae\xb6\xee\x95\xba\xee9+\x89\x05\x1f\x1a[\xf7 f2S\xe6\xe6\xe7\xc6*\x01V\x19\xee~-\x0d\xb2\xed\x0f\xdaq\xf5*\xf1MM\xf7!\xf0R\xd7\xe8\x19\xd5A`\x8e\xdd\xdf\xdc)~}\xb1\xc7\x1e\xe9\xb4\x91<\x92\x9f\x87\xda\x08\xc3\xdeP\x8e\x06_U}A)\x11\x19K\x17\x9e\x99\x05T\x16\x8co\xbd\x03!J9Z|g\xde\x99Y\xaa\x16[\x8d\xac\x86\x91\xb4\xed\x02$ \xd73 \xaaf\xd0\xfc\x1d3\xdd\xd7d_c\xcb\xba\xa0\x05Q-\x18\xc4\xeb\xc1\x04\x0c}\xe7&b#k\xb3\xb5\x1d\xfa\n\x0b\x17\xdc}\xd8\xf0\xc6\x1d\x83A\xf3.?B\xacp\x0cq\x8f\xaa\x8c\"\x1cc\x1c~\xf9\x11\x92\x07c\xee\x05\xf9\xa17\x9d9;\xdb\x8f&\x0b\xd2\x1f Q\x8ey\x19\x8e\x8dL\xbe\xb1\xaeU\xc83:\x85\x89\xf9\xf02I\x8f,) \x1b\xf8\xd1 \x9e\x8b.\x88\x152\xce\x0f/\xb0/\x85\x82\x836 CO\xd5 \xe2I#\xdc\xd9i\x1c\x8d\xba\xda\xae\xd2!\xad+<\x9b\xda\x8bA\xa7!4a\x0c\xc8\xb3\x1f;;\xbe\xa4\x15\xa5\xe4\xab\xa4/\x93\xa4\x1e\xf8\xcb\xa8=k\x0bL\x98\xf6\x8c\x93\xc4\x9dD`A\xca\x1f[\x1a\xf3nZ)\xb6\xa5A\x14\xa4V\x19\x94\xd9O\xd9-\xc9_\x86\x05\xf3\xb0\xd8rg\xce\x92|\xa1\xdc\x11\xd7\xbb\xd3\x7fw\xf0\x8f\xb0\x88\xe2\x98\xfeq\x15\xa7a~\x87\x7f\x85\x059\xd8\xc3ZQ1\xe5\xff\xeeL\xf9g\x93\x83\x84\x88\x16\xc4\xdfyx+\x19\x19\xb9,\xd3\xa2\xa7\x8d\x03\xad\x8cp0\xb59\xe2\x90\xbbm\x8d[\xc1,\xae\x9bt5\x12{@ \xccM\x98 )\x10\xf7\xf6\xb6\x1c\x98\x8e\xb1\xb8\xb5\x8eZ\xc8\xbcr\x19\xde\xe4\x8d \x8bP\x1e3\x10\x8774\x17\xb2Y\xcan)@g\xc8J\x01\"\xe2\xc6>h\\\x0b7\xfdZX]\xb7y&\xd3\xb2)\xd3\x04fiDj\xa1[\x07\xe9F\x1a\x93\xa3\xb1/\x99f\xb5E\xd4 !\x95\xbc\xc5\xa8\x0c\xbc\x82\xb5\xe9\x92\xf1\xdamt\xad\xe4\xdd2\xa8\xb6k\x0bt\x1d\xa0\xf0\x01\xb4\xe7\xd6\xbe\xe6\x852\x1e+\x9fk\xe9\xde\xed\xec\x9f\x9e\xe1~1\x89z\xd3\x1a%\xf7\x8d\xf8[\xbb\xa6U*\xd7\xa9\x7fi\xb5\x9a:\xbd\xfc.\x93\x94\xa4s\xd7\xf3\x81\xb4\"8\xfd\xa1\x19\xa9\x9a\x9b\x11\xb3\xe8\x1f\x8d=\x8a\x0e\xdf\xacVd\x1e\x87%\xd9$\xb5~\x7f\x0e6\xfb\xbe\xf0\x03\xd2\x1b=\xe2\x9b\x0c#u\xf7\x0e\xf7<\xd7\x833\xee\xbf\x8c\xc9\x13\xd1\xb0\xf5p\xff+\xa6z\xd3\x84o>2\x87R\x99\x9a\xd3\xc2\xed\xea\xc1\xc3*\x83k5G\xec\xedPC\xfc\x1275\xb5h\xee\xca\x07\x850\x8a\x0c\xaf\n\xf5M\xf4Uy\x02n\xea\x90\x0d\x0b\x1f4k\xf4\xb8\x95=\xa5\xb2\xf8V\xaa\xdf\xa1B \xc5\x00\xb6\xcc\x1b\xd8k\xfc\\\x17Z\x84\x05\x86#h)\x0bo\xb1\x10Y\n\x16\xf0\xfc\x14\xb3\x14D\xee\x82\xa7\xfc^\xc6\x8d\x93\xd3\x0eDn\xe1.<\xef\x04X\xe4-\x18\x8d\x0c\xea(\xb4\xf3\x91\xa5\xac<\xccP\xc2Q\xe3\x8c\\\xf8\x90\xbb\x89\x94\x02E\xc3\x8f\xbc\xb47\xd3\xfc\xa0\x93\xa6xH\xb4\xb0\x91\x10Tj\x03\x18F\xd4\x9aDo\x96\x14\x8fHa\n\xc2\xc4\xeeA\n\x12]\xa5\xbcx`R\x82\xeeA5\x07\x8b\xd6\xad\xf3\x8b\xb0P\xcc\x9f\xc8\x97\xf2]6'\xaec\xcb\x99\x92ah\x01\xdbx\xb4\xb0\xb8]\x029\x0b\xfb\xcd\x1d\x858\x82g\xcau\x16#\x9bX\xf1w\xb7u\xa1\x90.\xb1!v0\xfdp\xaai\xe5\xc4c\x96\xa8\xa0\xcb\x9aJNY\xe4\xb8i\xe3\xc3\x08u\xfa?V\x1f1x\xe9Zf\x86\x176\x0e\xe6a\x19b\x98\xc2S\x18\x8d2\xf8W\x982s\x07l-(\x96\xf1\xa2t1\x04\x05\x17\xbf\x08\xafkN\xe1\x95\x06m\xd5\x83\x17dW\x05\xc9o\xd0R\xca\xbcx\xd12\xcc\xc3\xa8$\xf9\x8fa\x19\xb6\x82\xfe\xb3V,\x16\xeb\xbd\xf4\x02}X\x9a\x17\x0cai&X\x99\x94{F|(/P\xec\xc0\x15\x94\xa8\xbde\x04\xb0iq\x86\x88\xc5\x1e|3\x1c\xb6^\xe3v\xe4$$p\xec\xaa\xb0&\xc1\xb4\xe4\xf6f\xf6B\xe9\xe8D\xdcO\xdaM\x9d.\xa8C\x8cj\x1c\xca\xdb\xaa\xc4\x84|\xef\xd9\x8e7~\xb1\xb1\xdbze\xbf\x95\xc6\xa6\xffL\xae\xfe#.;:\xb0Th\x1f%\x1bH1\xdf\xa8\xde\xe0\xbb\x80\x8c_\xee\xea\xa2\n\x00\x16\xb8\xd5\xd8lA\xcaO\xf1\x8ad\x15J;\x0c\xdb!U\x182\x80\xa6\xba\xcb\x0e\xfb\xd8<\x98\x96T\xeeA\xba\xb2\x83\xe8\xcaoBeY3h\x9a\xb2f\xaay1\xa7l\\\xfb\xd3}\xfe\xef\xc1\xc6y1;F'\xd2S\x1e\x9a\x92\x8d\xa1\x86\x8f\xa7'P\xc3\x0e\xe7\xdda\x87\xd5X\xe9\x96|WV\xc8 \x84t\xed\x0e\x92,\xc2\xc3~\xdcJaF\x9fe\\\x94Y~g~\x99\xadI\xaa\xb2\x7f\x86J\x98\xf2\xab\xb7\xd6\xeb8\xd1+\xd9\xe6\x0b\xe2\x86K\xf1\x82\x9b3\x7f\x8b\xc9\xcal\x89\xfa\xccV\x1cta\xd8wmxr\xc3\x1dFm\xda\xb8\xb4C\xc5\x9b\xd7\xf1\xde\x0c\x82P\xab=Im\x08\x13\xf3\xb0Ih\x15$\x82B\xbb3\x87\xae\x95\xe3\x83\xf3C\x92]\xd1\x7f_g\xf9\x8a\"=\xe7\xc2;\x01\x16\x16\x13\x13\xf3U\x08\xc0]\xcf\x0b\xe6YJ\x90\xc4E\x8dE\x07\x92\x13z\x97\x98\xe5\x10\xb4\x93\x1f!\xc4)_3\xc693;QV2\x0b/\x86`5,\x91\x0d>\xec\x0b\x93;\x8c\xee\xe0P`\xe0\xd0k\xcb\x0b]=\xc9@\xaf;\xbb$\x1eW\xcf\\\x9f\xb8@h\xd6\xe7>\xdc\xf8p\xe7\xc3\xb5\xde|\x81y\x0f}\x98\x1b\xdc\x92W>\\\xfap\xe5\xc3m/\xbb\x08\x82\x83Z\x83\x08\xb6\xfa\xa2\xc6\x05/\x8c\xf1 \xe8#\xc2\x15v2\x00\x18\xef\x8fe\xec1\x87\xe0k*1C\x8a\x8ej\xd0\xacf/\xfbi\xf8\x86R8i\xad\xdd\xea\xfc\xca\xe2\xfce,\xdddD\xc3Gb\x00vmt\xf9\x05\xbd\xa5G\xe0\xc0\x1bq\xa0\xdb\x95\xce\xe1\xb4^[\n&n\xdaU^Y\xd0\xf1\x0bT\xca5\x82\xedV\x85\xf7p\n/f fNz1s\xfe\xed\xdf\xea\x8b\x85E\xe8\xfc\xf1bvcH\x1a\xfd+\x05\x86L\xdfxc\xe00?S\"\x00\xce\xe0\x1c\xce\xe0\xd6uHZ\xe61)\x10\xa2\xfd\n\xf6\xd4uoX2\xb7<\xbc\xc3\xa9\"\xa2z\x11\xf0\xafio\xef\xdb\x14\xd1\x1bD\xc5W\xf4\x96\xb8o\x18\x19\x8e\"\x0e\xcf\xf3P\xea\xae\x8b\ni\xf5+\xa6>G\xcfj\xf7\xca\x87/>%\x11(\xba\xa5<\x85\x89\xed\xb8\xe2\xabT\xd1\xea\x89\x0fK\xcf\xf3\xe1\x9c\xb6\xf0\x1e\xe1\x8c\xd8 \xec1H\xc3\x15\x93\xad\xbf\xe2x\xfc\xd7\x81P\xe6\xbd\xd5\x9f\xcb\xe3n\xf1[L\xf7\x8bW}\xeb\x15\xdb 1\xb4\x178\xb4_=\x1f\xc2\x19\xa1\x94\xc9\xaf\xf4\xaf/\xf4\xaf\xa5\x0f7f\x11\xdf\xcaj4\xc1\xe6t\x8c\x9bHw\xed\xd6\x15\xd3\xb4\xc8\x14(\x988\x86\xbb\xa6\xba)\xd3\x97x\xf8\xae\x1e\x83A\xb1\xe8\x9bl3A\x90\x89\x97\x14\xc2\xad<\xc0\x7f_\xd0\xa9gt\xea\x97>\xacf\x97\xa6\xf0\xa2,|\x91\x1b\x07\x1f`\x04q\xf0\x1a\xbe\x07wM\xbf{\xe5!\xfc]\x99c\x11\xad\xea\xc2A8\xf7FJH9\xb5\xd0\x0f]\xdfC\x1d\xa7\xa7\xd4\xd2\xe4\xda\x08{\x01\xc1\x8d\xba\xb9\xae\x08\xb3:\xcc\xeb4\xd2\x12}7,\xae\x05\xe4\xb5\x17\xbe+ mk\x0c\x1d\xd6\x81`\x1c\x06\xfd`\xa3\x91X\xe2\xd6\x9aF\xd2\xe30n\x1c\x8c\xd5\x1f\xb9+\xce\xca\x10\xf4S\xf7\xc64\x08DV\x1fX\x9a\x1etb\xe5\x93\xb9\x95\xba\x93}\x16\xa54u\xa7G\x9e]B\xccG\xf3\x14\xb6N-\xcaT\x91\xda{\x1e\xdf8\x9e\x0fN\xf8\xf5j\xd4\xa7m \xa1\xce\xdc\x0b\xc2f\xf2\x1b\x92\xfbS35|\xf4?3\xdd\xa2\xaa\xf6\x9bn\x9a\x19\xa8\x95s\x98\xab\xf1\xcc\xf9A\xa6\x93}\xcf\xdd\xd2)uc&\xf9\xbeu\xb1\xc7\xfa\x0cyB\xc76\")\xda @\x813\x163\x8d\xec\xe5\x9a\xb58\x85\xd0\x83\x94\x1e\xde\x8a\xed_\x88K\xb1\xbd\x0d\x11\x13^\xeb\xc1\x0d\xb8\xf3\"i\xc2\xe7\x16'\x1e\xff\x8e\x12p\xb3b4b\xf1}\xdd\xff\xca\xdc\x08[\xbb\xbfoZ3#\x97h\xb3M\xed\xdd\x9f}s\xaa\xe8\xcel\xfe\x95A\x93\xda\xc5\xf7\x06\xd7\xa4\x94\xb2d\xabV\"\x96c]\x8a\xbd\xe3y+\x91\xc5\x9de\x176\xf9\xae\x9ae\x8b\xf33\x8dW\x85\xf2\xf6L\xfd-\xd1x\xc7\xeag\x9c!?\x83J\x97\xe4n\xb8\xf8\x87\xe6\xc5o%\xe4no\xc5?s\x14\xd7\x03\xee\xcbu\xf8?;G\xb1\xf5\xec\x98\x12/\xfd\xcf\xcd\xa5\xdf\xb9\xcd\xbc\xb7\xf6.+\x16\x8b\xee\x04\xb6\xc1\x04\xd5\xb5<\xb6\xee\xd4RO\xd8,\xd1:{\x96:\xe6\x8c\xb7\x9b\xeda\x9f4m\xb2{\xd0N@\xbf\xfb\xf4\x9f \xe8\xa5\xe7\x7f@\x02\xfa}sR\xc4\x01\x19q-\xe7\xbf\xae`\xb3\x9f\xa4}\xf3@\xe6\xcd\xbe\xc7\x14.\x99y\xe6\x82g\x016\xbf\xa5TOhu\x14\xe1c*DJ\x9c\x82ns\x84 \xd6x6s\x8e\x03\x8e\xc1\xc5\x08\xdb\x98D\xf1e6'/J\xb7\xf0\xe4\xee\x9d\xe7\xc3\xdd\x1f\xa4\xa2e\xe7t\xa5\xdd\x91?r\xf8\x15\xc0!\xa4\xee\xde\xc4s\x13\x0f-i\xbb\x1aK\x1a\xd7\xcb\n\x83\xf4\xfa0\x91\xcc\xae\x1f(eI\xf7\xe1&H\xb3\xdb\xde\xd6\xb0\x96\xb5\xa19\x86\xce\x16\x06\x99\x94\xa2\x9c{\x01\x05zS\x1fb\xfcc\x12d\xe9\x8a]68\xa5\xd4\x07\xc6\xcap\xb3`\x9d\x15%\xbf\x85\x08h&\x18\x81i\x11\x84\xf39&\x1a\x94Se\x197Cj\x00\xc9\xbcE\x10\xafh\x8f\xe7Q\x1e\xaf\xcb\x82\x8e\xac{j\x0by\x0c\xdc\xa1\xdc\x07\xe7{)\xac\x17\x85\x94\xad\x11\xb9\x0e\x9f\x90\x83\xe4\xd4\x16\x1b9\xed\xcb\xc9\xd2\x9c\x84\xf3\xbb\xa2\x0cK\x12-\xc3\xf4\x9a [\x1d\xb9N\x81\xa3r\xbcNK\xf5\"\x08\xd7k\x92\xce_.\xe3d\xeeJ_yA\xbb\xe5\xbe3,\x123\xb1\xc6J\x16MY\xdcS\xab2\xb9\xd3\x94Q\xb2\xa0oN\x84bG\x8f\x99>%\xc4\xd7\xfa\xfe\x18\xd6\x1af\xa0\xb0\xfa\x18\x9a\xecC\x9b\xd1)\xf6\xc1\x9a\x95\x0fVy5},\xce\xf5\xf4\xb996{\xee\xa8\xeb\xd8i\xd7\xda\xdb\xb5\xc5\x04\x9bv\xdd\xd7q\xcf\xeamJ\xe9\xb4\x0c29\xa53\x1ed\xed\xa2O\xbe1u\x89]\xe6YH\x14\xe5\x1e\xea\x9bl\x9e\x857<\xb6U\x16,ZQ\xc4\x05!\x8c9\xc5sRd\xc9\x0d\xf10\x9c-F\xb1[\xc5\x05y\xec\xc2\xb4V\x80-\xcc\x9e\x9d\x04\\\xd1\xad\xef'\x00M\xd4\x9f\xd9\x99\xb2\x0en&9\x963O+N\xdemmQ\x02\xcf\xf9H\xae_}Y#h\x8c\x15\x0f\x9bAS\xb6\xdf\xd6\xda5#u\xa7\x87:A\xd7\xb8v(\xf2\xffA]\xca\x12V\xe3*\xeb\x9dq\x03\x84\xa3\xde\xc5\xb5Q\xd7\x88\xa1\x02\xae\x1b\xc6\xa46\x1eW\x8f\xb12J\x16\xb5\xaeX\x85\x84\x9d\xba5\x15\xcf\xfb\xcb\xb2A\xb9yp\x0e#\xc8\x91Y\xce\xba\xf5\xbc\xf4\x90(\x85\x98\xbf\x9dk*}9|\xd4\xa054\xcb\xae\x89\xecr#\xc2\xb5\xf3}\xec[(\x14\x8e\xba\x8a2\x9d\xd8B\xa9\xf0\x80\x84\x14\x97@\x08Q\x12\x16\x05\x84\x85\xe2%\xfb\xbbLG\x93\xd2\x0bO\xa4\xc9\xbe\xe9\xc4|{W$\xe3Z\xb6\xc8\n\xfe\x02J\xab^\xbc&oS\x96\x1a<\xc5\x18]\\\x9d\x03\xe9h\xd4E\xe8\xe7h\x89\x92Z\x08\xfd\"\xd2\x84\xac\xa0s\x01\x0f\xad\xaeB\xf6\x89\xe4\x95\xbd\x95\x07\x0b\xce\x97\xb1\x80J\xe5\x8c\\l\xb8_\x8f\x03%8WJY\x1d\xea\x1a\xdf\x98\xbf\xda\x1dO\xf5W\x19\x7fE\xe1\x8f\x9c\x86\xb0F|\x86\xdc\xa4\xb5\x89 \x0b\xd4,\x83\xa5\xb2\x1b,iA5\xfe\xd0\xfek#\xf8d\xb9\xea\";\xc1\x163\xc27\x12=\xe7\x14:\x01\xf9\xb2\xceIQ`\xd6\xa4\xaa(\x81\xc4\xe5\x92\xe4p\xc5c\xccf\xb9D\x05\xb1`\xcd\x0e\x8c6\x86J\x1a\xb8\x935s\xccc6\x96\xaa3\x8eJ\xc2\x8d\xed\xe5\x94\xd8-\xd3jC\xa7\xf5\x0d\x0c\x08@\x07\xaa\x91\x96\x85\x95\xd5\xcc\xbd\x0c1,\xd4\xdd\xc6\xfb\xc8\xa8\x11\xb1\xc7g8\xfd\\\xa1CD\xb2\xa1K\\\x83\xcbKJ!}\x93\xfb\xa3\x1aX\xef\x8e\xbfM\xfc\xa4\x03\x93}`\xea\xee\x99\xedz'-\xc5\x12zMS\xe09f\xe1\x07\x0e&\x9eb\x906e\xe5\xbb\xe3\x03\xe3\xf5\x0cMc\x06a\x97\xb6\xce\xb3u\xd1\x845\xa4\x98\xaa\xe4\x01HyIN\x16\x05K\x0d\xc5B\xcc\xad\xe7a\x89\xf9\x0f0Nr&\xad{\xbb\xef\xe2\xef\xd8w\xa4\xba\xdd\x87r\xf4\xa9\xe2# \xa3\xf2e\xb6Zg)\xc1\xbc7\xbf=\xf8J\x95\x82\x94\"EY'\x90\x91\x88\x11%n\xa69\xf4\x90\x04x\xd8\x8f\xdcu\x0e\xf7\xeb\xec\xef|~\x01I\xffZ\x91\x8a\x9c\xf31\xd4V\x15\xbe\x94\x87^\xab\xfb\x92\x87\xa2\x15\x11\x9d|p\xc4\x14T\x01\xa7<\xc9E\x96G\xe4gl\xa8[\xb6f\xe8\xf0u\xf3\xad\x906\x96\x03\x07W\xfa\xe0H]\xab\xe3\x8b\x14\xd8\x17\xcap\xaeP^Qp\x1d)\x85\xaa\x94 \n\x1fb\xb7\x90\x1b\x90Z\xf3\xd4/\xe3\xe2C\x95\x93\xd6\xa9\xe0 D,\x8cB]\xf3\x18B\xf5\xca\xd2\xc6\xa4\xb7\xc5\xb7\x00N\xa9{ ;\xaf\x0b\xf8\xa2\xe1\xbc\xe2mV\xa5%\x99\xf7\xc5\x0d\x14\x14\xb5fc\xa9NC\xdb\xbe6ae\xae/\x1d\x0dm\x18\xe6\xfa\x1f\xc9: #\x16\xa0ph\x1f\xe2n\x18\xea7\x8bm\x86\xec\xf9\xe3\xf7@,\xba\x1c\xac\xfe\x1b7\xfd\xdb\xb7\x1f\xb5\xfd\x04GU\x9e\xe3 \xdd\xdcu\xa2{\x16\xc3\xb2\x9a,\x98#H\xf3\xcburz\x05\x03\xc2\xd4\xf8\x0e\xfa\xdb\x1c\x8c'\xe3\xdd\xdfuQ\x9c\xf3W/?\xbe\xfat\xf9\xe3\xfb\xcbw\xef?]~xq~~\xf9\xe9\xdf\xdf\x9c_\xbe\xffx\xf9\x97\xf7?_\xfe\xf9\xcdO?]\xfe\xf0\xea\xf2\xf5\x9b\x8f\xaf~t\x86\xf4\xa9Q\x12\xd3\x897L*\xd1\x17!\xafu\x97\xcd~z\x14\xfc7T\xb7\xd1I\x8f\xd3\x7f\xba17\xa6\xbb\xba&\x14\n\xae\xb2\xf4\xd5\x97\x92\xa4\x94\xf8-0\xca\xf85)\xb5\x12RD\xe1\x9a\xfcH\xc8\xfa\xa78\xfd\xfc!\xc4\xa4\xcb\x84;\xbb\xb5\x8a\x8be\x98$\xd9\xed\xab\xbfVa\xf2\x1f\xe4\xae\xe0i\x05\xe3d.\x82\xbe\xb0jY^\xb2\xccz$\xb8*3^H\xf28L\xe2\xbf\x91s\x12\xe6\x11ko\x1d\xe6\x85\xfc\xfb\x9a\x94\xe7\xe1j\x9d\x90\xf3hIV\xec;L\xd1\x10\x96\xe4C\x98\x87+\xad\xa4,I\x9e*eo\xe3\xf4'\x91;Z*\x0d\xbf\x18J\xffX\xc5s\xa5\xe0\xc7\xb0$\x9f\xe2\x15Q\n\x99%\x8cR\xf4C\x96%$T;~\x1d'\xeawo\xd2\x92\\#\xad\xd3\x94\xbd\xabVWZ\xd1\xdb8\x8dW\xd5J\x1fn]Fi\xac\x97K\x12}\xe6\xdf\xad\xc8*\x8b\xff\xc6\xba\x8a\x8b7\xabU%\x84~\xa6\xd0>\xe2:_Q\xd6p\xfa\xd4d\xbd\x1e\xd7\xaf\x8fL\xaf3\xfe\xfap\xcf\xf4\xb6\x12\x1f\xef\xee\x9a^\x87\xf5kc\xd7\x05\x7f\xcd9S\xf9\x15\x9d\xdc\xff=\x7f\xff\x8e\xeb\x00\xfa\xec\x19\xec\x9eK\xc2*\x816\xc6\xce\x9b1\xb9-p~\x93\x85\xa4kb\x97\x0d\x11P\x15*+X+\xc6Z\x9d\xf4\xa4\x93\xb2\xa1\xf4:\xedD\xbc\xb8\xeb] \xde\xc8+\x17C\xd6|qy\xe4\x9a2\xfb\xbf\xe7.\xb2]\xaa\xdfj\xdd\xc3\xff\xcf\xde\x9fw\xb7\x8d#\x0f\xa3\xf0\xff\xcf\xa7(\xeb\xc9/C\xb6i\xc5r\x96N\x9c(\x9et\xe2\xa4\xdd\xd9z\xb2\xf42\x8a\xc6\x87\x96 \x8b\x1d\x89TH\xd0\xb62\xf2\xfb\xd9\xdf\x83\x02@\x82$\x00\x82\x8e\xbbg~\xf7^\x9e\xd3\x1d\x8b\x0b\x96B\xa1P{\x85i\x1a\xae;t@E\xb3\xe8\xd8\xaa\xfe\x8d\xbd\xbc\xf70@v4nv4K\x93\xe5O\xef\xdf\xa6S\x92\x125\xef7PO\xab|g\xabr\xe1\x11c*S(VN\xb1\x84,\xe5\x92\xf4\xd9\xbe\xb4}Z\xc0\x8b\x94\x19x\xa3\x8c\xcf\x04oM\x8a\xa6\xde\x93/\x1e\xf1\xfb\xcbp\xe5Q\xccd\x1fe\x14g[\xbe\"\xa6\xf5:\\\x95oB#\xc6 +;D\xf1\xf4C\xe2$\xa2\x80b\x16\xab\x1b\xb8\xa0jV\x0d\x159\xdb\xef\xcf\xa2\x05%J<\xa3\xb1 \x91hA\xefD\xa3\x8d\xf9\xf3\xd9i\x7f\x18N\xe6e\xeb\xc6\x1c\x01\xd2*0J\xc7h\x0dM\xc78{O\xe4^\xd7X#\x9a%\xfe\x18\xc8\xe2$]\xe2 \xc2qn\x08\xef\x03\xa4\x13\xcfcW\xa4m\xc9\xe8\\\xf4\x14e\x05\xdd9\x14}\xe4X\xfd\xf8\x9a{\x91\x13qj\xb6\x8a\x9bu\x97\x10A%^\x87+\x17t2\xa2LJ\xa6\xf9D)\xf2g\xcb\xfdP]W\xe2\xb1\x95\xe5\xa6\x9df&\xd8\xcb\xa0\x12\xd1\x08\xca\x90\xdfa\x97\x7f\xd9\xa8\xcfD=\xabr\xbc\x06\xcb\x9cP\xf7Z\x0f\x84\xa8\xed@\x88D\xa5\xa7\xdd\x00\xf2\xf2n\x1c@\xd4 L\xd9:\xa3d\xf9a\x9e\xc7\x9f_G\xd3\xe9\x82\x9c\x87\xa9]\xe4\x07\x9d\xe5\xce\x04\x13\xd2\x9fJ\xf7I\xc1\x85\xe9K*@\x97Fu/7\xf4H\x86\x0f\x8cyKc\x8fz\xe8\xbfE\x9c$\x8b\xe9\xc3\x1e/_\x8f\xff\xa9\xaf\xe2\xbd\xf1h\x05\x07\xb8v\xb7\xe1\x00\xf6`\x1f!|\x0f\x0e\xe0\x8e\xf8\x9b\xdd\xbf\x0d\xfb\xb0}\xeb_^\xe8\x9dd4\x0d't\xb3\x88\xc2l\x13O7\xd2y{\xc3\xf6\xec&\xf3\x96\x9b\x8c\xa4\xd4?\xd8\xe44\xf17'^\x98\x91\x0d9\x8d\xe2M\x92,<\x12\xc6\xfe\xc1&%\xe1\xe7\xcd\x9a\x12\x7f3\xc1\xc7\xec\xc0\xd9\xcc\xc3t\x83\xf2\xedt\xb3\x08\xb3l\xb3Hb\xb2I\x96\xab\xc5&\x893\xbaIb\x1a\xc59\xf17S\xe2\x9d\xe4\xa7\xa7$\xddL\xa2e\xb8\xd8L\x16aJ63\x8f\xed\xf1\x0dI\xfd\x83M\x14Gt\xb3\xf0\xc8iH\xc9\x86P\xe2\x1f\xf8\x9bi\xb2\x99&\xf9\xc9\x82l\x887\x99'\x9bEv\x10\xcd6\x8b\x8cx\xd1\xcc?`\xf3\x88\xb3<%\x9b8_n\xceHL7\x17\xde\x84\xac\xe8\x86L6+\x0fS4o\x92\x94\xfa\x1bJ\xbcx\x9amPs\xb2Ic\xdf\xf7Y\xd7\x8b\x05\x9d\xa7I~:\xdf\x84\x8b\x8cl\xb0l\xf9b\xcd\x86r\xc1\xa6\x93\x84\xeck\x8f\x84\x939\x9b}D\x18\xd8\x92\xe5&\x8f'\x1e\xdb\xbdl\x80\xa7\x8b\xe4$\\lN\x13\x9alN\xf30\x9dn\"o\xb6Y\xae<\x8e\x03\xd9F\x19D\xecEt3Y\xe4S\xe2\x1d'\xf1\x84\xf8\x07\x9bE\xc4\xa0\x95\xd3\x8d\x14}6\xd4#\xe9,\x9c\x90\x0dI\xe3p\xe1\x1f\xf8\x07\x9b\xcc\xdf,\xbcpy2\x0d7\x84n\x92\xc9\xe7M\x12\x9f\xfa\x9b\xa5\x17M\xd2\x04I\xe0\x06\xf5L\x1b\xaeK\xf07o\xc27\x9b\xd8\x0b\x97$[\xb1\x96B\x1a\x9d\x91\x0d\xb9\xa0\x1br\xbe\x89\x16\x9b\x84n\xf2\xc5\xc2\xdf$\x1e\xb2E\x9b\x15\x8f\xaf\xdc\xa4\x9b\x9cn\xceH\x9aFS\xe2oV^8\xf9\x1c\x9e\x92M\x98\x86\xcbl\x93Fgl]\xd2\x84\x92 %\x0c\x104\x99$\x8bM~\xb2\x88&\xfe&\xf5\xc2\x88a\x8c\x17N\x93x\xb1f\x0b7\xdb\x9cF\x19%\xe9fEB\xba\xf9\x92Gi9\xefl\x92\x93\x0d\xd7\xb3mh\xba\xde0\xaa\xe8\xfb\x9b\xcc;Y\xb3\xc5\x0f\x17d\xba!\x8b\xd9f\x9e\xa4t\x13\x9d\xc6d\xba\x89\xbe\"xB\x1aM6\xa8\xd3\xd9\xa0\xa9a\x93\x9fp\x97\x84M\xbe\"\xe9f\x1dO\xe6i\x12G_\xc9t\x83\xb1\xc4>\x83\xe8r\xb5`\x83\x9f\x93x3\x8f\xb2\xcd\xf7|L\xd1\xce\x06\x87\x11^\xf3z\x8a\xf6\xcc)E\xfb\x14\xab\xfc\xa2AB\xefGR\xbc\xdc\xf4\x86\x99\x06Pw\x06\xae_X\x8b\x8c1\xa6\xd6\xb7N\xf1\xadA\xcb[K\xc6\xd3z\xa7\x01\xc4\"\x83\xc9\x00K\xede\x84za\x00k[\x81\xe2&*H\xa1c\xc9\x84\x8e\\: .1\x19\n\x0fq[\xea\xb9A\x0d\xb1hMU\xdb(\x9a([0\x11\xa7\xc2\x9b\x8d{\x87\x95\x84\xbe$U\xa3\x81\x86\xb8H%\\\xa3\x08J\x80\xf6\xb5l\x12.\x9e\x86\x19\x1b\xd6\x93\xea\x9d\xe7b\x90\xad\xa0\x91\xeaG\x8f\xf6Sn\xe8\xf7n}\xea\x8f\xfe\xd5\xbf5\xfe\xee\xc6-&J4K\x7f\x92~\x16\xc6\x11\x8d\xbe\x92\x8f\xe9\xa2\xb5\x87H\xad_\xabz\xdb0a\xadW\x8b7\xd2\xc9\xd6\x8abp\xa6\xf6\xeck\x8f\xe0SB\x9fL\x18\x97\xcf\xb0%M\x16\x8b(>}G\xb2U\x12g\xed\xd0\xa8\x9dd\xa5\xc2\xbf\x1fe\x8a\xf6_Q\x87\xb0\xa51i\x0c\xaa\xc7\x9e\xfe\xcdR\xbf4\x8b\xe2\xa9\xd7\xaa\xac\x91Wq\xc2e4Li\xf6kD\xe7^o\xafW\xe8#U\x15*\x83\x89\xd7\x9b\xf0\xdd\xc3\xad\xf6\xff\xbe\xf4K,lz\xfe\x01\x98+X\x15\xaa\x1d\xaf'\xba\xe8\x89\xc4\x9b\x1a;\x89\xa1\x8d\x14\x9d\xe64\xe3\xd27\xe2\x17\xca7a\xea*\xb3\xa4\xc5\"O\xa2Y+\xc7\x9aM\x9bx2%d\xb5X\xbf\xa7i\xb4zI\xd65~\xcd\x927\xecZX\xaab\x99[\x94\x81:\xa7L=\xb6ut\xbb\xafZ51\x99N]K\xb7\xd9\xa8\xe4\x8f\xf1q\xb1\xcd\xd4&5\xef5e\xf8\xbf\x19\xb05d\xb1\x86\xa3\x91\xc6\xe4dVh\xe3\x98b\xee\xa1\x17a=D\xd4*\x8a\xc8mv\x87 5<\xa1\x0c\x15o\xe8\xd3V_\x9aU\x90\x91\x86\xec!\x15s\xb1\xa3F\x86\xa2\xdd\xa6\x94\xe2\x80^)\x0c\xb9A-\xeb\xcdp\xddp\xa6\x18\xad\x16\xb4m\xc1)\xb7Z\x94\xd5\x8dMn\xf5P%\xbeU7_n\xdf\xd3T\x94+\x98\x9d6\x83d\x91o\xb1\xd9\x84iM\x18L\xc4g\x1a\xd2\x1f\xa3\x03\xc6\x87\xa4p\xeapX#\xfe\x8da\x8d\x94\xde\x8chR3\xfdU\xdfc\x9bb\"\xfd \xee5\xfc\xfa\xa1\xc8\xbaq\xfbN=<\x05D\xee\x0d\xf4\xb0\xb83\xd0}\xba\x92-\x7f\xbf\xab{\xaa\x0f\x89\xaf\x16_e\x0f\xcf*\x07\x89\n-\xa3\x05\x19\xb3\x16\xf4\xa3\x18\xf5\xe3\x99\x17\x97\x0c\xb8N\xb7\x02\xaa'\x809:\xd7m\xa3\xc1\x01(\"A\x84A\x13\x11\x16Z5\xf2\\.hm\x8d\x95t\xf1<\xc0C\x9c\xe2\xa7Q\x93\x18p\xfe\xad\x9f%K\xd5s\xa2\x8d\xddd\xbd\xac\x95a\x8eb\xc6[\x8db\x8d\xdd\xeb\xb2\xbe%\x9a'\xdf[\x83\xdfc\xeb\xfe\x80\"\x10\xf01\x94\x02T\xef\x97p\x91\x13\x1e\xe8uB`A\xb2\x0c\xe8<\x8cA\xb4\xdck\x8e\xb1\xb9;\xfe0\xf8gv\x18\xd3#\xf3\x98NQ\xe5\x9e\x8aa\xf1\xc6\x9d\x86\xf5Y\xefI\xda~Z\xa0\xa4y\xeb_;\x07\x9f\xa6\xdb\xde\xa7>\xfb\xc7?\x90\xb6\x01EN\xad\x0d4\x04\xc1\xf8\xb8\x0c\xee\xc8\xe0\xfa\xdamt\x0e\x83\x8a!\xe2\x8d;\x0d\xeb\xb5\xceE\xd7mLx*\xd5\xf2+\xd4\xbc\n\xcd\x90\x9bE\x0b\xe24\xc0\x0f\x06\xbfb\xb71\xf6h\x9a\x13N\x1aD\xccR\xb8\xc8\xd4\x1b[\xbb\xca\xdf\x03\xc9\xca\x9bF}\xc2\xbbw\x1a\xf8S\xbd\x8f\xb4\xdb\xb8\xf9`5\n\x1f\xf3\xd8\xc4\xcb.C\xfb\xd9\xe4\xd3\xed68^\xb1\x9f}V\xb8\x0b[VZ6\xef4\xb2w:\xf7s\xb7QIqO\n\x1b}\x9a\xbcJ\xceI\xfa4\xcc\x88\xe7\x07\xb0u\xeb_\xa3\x7f{\xe3\x83\xd1\xee\xce\x83pg6\xfe\xf7\xfd\xcb\x9d\xe2\xef;\x0e\x7f\x0f\xf6.G\xfe\xe5\xd8\x890\xb0\x91;M\xf8\x8d\xd1\x0b\xdf\x9d\x98\x96\xbc\x89\x1b\x9d\xe7]8\x0d\xef\x951t\xa0\xfb\xf0:\x90\xfc\x0e#|f\x08xp\x1e\xdf\x16O\xebpzx\x81\x1e\xc9\xb6\xa5\x9d%\x8bEr\x0e+\xd1I\x0f\xb6u.\xec\xd53\xbc\x19\x9e\xd1:\xb2\xabr\xb67oV~\x9b\xb9Z\x13\xc7\x8b\xac\x1eR\x9e\x93d\xba\x16je\xae`\x8c\xe2\x1ew\x93\xc7_h\xc8:\xbeX.z\xc7\xd0\xf9LyS\xb0\x1e\x867\x17\xe5\x9b<\xc9\x85\xfe\xb5U\xf9\xda,I\x97!5\xbd8\xaf\x8cQ\xec\x00\xc3\xbb\xd3\xca(\xed\xef\x9e\x95\xef\n\xc4\xad\xa7\x1e\x01\x01G\xeet\x950\xa67\xb2f\xe6\\3\x91\xbdT\xcc\x0d\x01\xbf\x8c\xf4\xfd\x83Pe\xf4B\x99\xe0[\xbc_\x15\x9ay\x82\x97H\x16\xd306u\xackJot\x94MN\x92<\xa6&-:\xbbN0\x9c\x8fq$\xcal\xccl\x8d\xb9!\xd4eH&\xa1l\xcb\x8bx\xa6\".\x96X\x06r\xc1\xbe/\xb5i\x95\xcfw[\xbf\xc6\x94\xf1\x92\xf9\xeb\xfe\xf9\xa1\xc1\xc8\x0e\xd2\x00\xd7\xd0B,\xcc\x9e|V\xed\xaa\x9bdvhp\x08\x90\x17O\xef\xad\xd7\x11G6u\xac\xbc\x94\x80\xa7\xc8\x0fD\x7f\xc6/\xda\xed\xcf\xf2\x92\xb4\x88\x1b\xb8{H\xf7 ;\xde\xf88y\\bq\xf6\xe1\xf1\x80c\xe9\xf9\x81\xa1\xfc8h\xf5\xb9 \xb6\xe3\x13F\xd2\xd7\x01\x9c\x16\xb5#0\xb5\xfd\xfb\x00\x0e\xc75\xe1\xd5:\xf6R\xdf\xa4}E\xa7\xe6\x07\xb1\xd4 \xf2\xcfe\xf9 9\xf7w\x82\xd6\xc3,\"\x8b)D\x19\xe6\x0fY\xa5\xc9Y4\xc5\x13@G\xb1e\xa3g\xb6\xc1\xb2\x89\x7f\x85!<\xf3\xa2\x00\xce,N _\xd1\xc4\xc1\xc7\xf3\xd5\xd5\xd9\x00\xc4\x10\xe6\xe5\xd6\x99\xb7\x8d\xe69\x0c\xe1\x0d\x1b\xcd\xdc2\x9a\xe7\xcah\x9ew\x1d\xcd\xb4m\x08\x1fa\x08\xaf\xd8\x10\xea\xa5E\xd4\xeb\xa32\x84\x8f]\x87\x10\x96\x00 \xdbF\xf3\x03\x0c\xe1-\x1bMh\x19\xcd\x0f\xcah~\xe8:\x9aY9\x9aY\xdbh\xbe\xc0\x10\xfe`\xa3\x99YF\xf3E\x19\xcd\x97\xae\xa3\xa9\x1e\x89m\xe3\xf9\xdd\xe2\xb7$/\xe4n\xbc\xdfQC\x1eR\xb2C\x99\x1c\x85\xcd\xaf\xe0\x00~\xf6P\x85\xd6\xcb\x99\xb0Q\xdc}\xc7\xef>\xe5D\xd4\xcc\x17\xc9K\xcc\xf6w\x93\x1bKIf\xab\x07[\xdb\xfc~\x85!|\xf0\"\x0b\xb0qv\xbfv\x18\xe3\xaf\xedc\xac\x1c\x9emC\xfc\x05\x86\xf0\xb9}\x88\xbft\x18\xe2/\xedC\xac\x9e\xd0mc| C8j\x1f\xe3\xcb\x0ec|\xd9>F\x95\xc1j\x1b\xe1\x8b\x96\xa1\x1d#\xf3S\xb0a.\x03}!y\xd6\xa3\xd8\x1b\xf5\"J\x96Y/\x00\xceg\x8f\xfd\x00\xa2\xa6\xa1\xbb\xcd\xd7\x03\x14\xc1\xaam\xdb\xb1\xab\x82I/\xd0I\x82!\x0b\x06\xabV\x97P><\x12\x0fU*\xf0\x02\x190\xf6\xf4)\x13*\x03ap\xe7\xeb`\x1f,\xbb\xa2xJ.\xf6\xa1\xc5g\x90]$M\x93t_\x13/\xa7^\x97\x96x\xb0v\x9cP\x18\xe46\x94\xb8\x01Cx\xdd\x8e\xb47\\pA\x00\xeb\x86+56\xda\xbd5\xfe+\xcdl\nvNI:\x1a}\xbb\xbb\xb1\xc6\xd2 \xc2/\xa8\xab\xd8\xdf0h\xe9\"\xa0\x19\xbco],\x17BwE\x8c\xf2]\xc4\xbd\xae.\x96\x0b\xdc\xb6\xf8\x17\x166\xb2\xad9\xd7\xf3\xb0o\x98\x94/\xbe\xfd\xf7e\xc0\xbe\xbfq#%3\xd5\x1d`\xbdBO\x18\xda\xc7}\xcd\xff\x14%WD\xb9'\xda\x0f\xa7S\xf4M\x0c\x17?\x97O\x0e\xe0o\x8f\x0eX\xe3g$\xcd\xa2$\x1e\xf6\x06\xfd\xdd\x1e\x90x\x92L\xa3\xf8t\xd8\xfb\xf8\xe1\xf9\xce\xfd\xde\xc1\xe3O\xb1pl\x87\xdf^\xbf\x02r\x81K\x0c\x13\x9e\xe2\xf7\x84\xc0)\x89I\x1aR2\x05\x1e\xa4\xf47\xa3\xff\x93\xbc\xa4!LL\xa7\x8f\xa9\xb1\xbd[\x9f\xde\x7f\xf7\xe9\x96\xf7\xe9\xfd\xb6\x7f\xe3\x96\x05\xd9K \xc2\x10\xa2\xd1\xa0\x19\x8c\x08F\xc6B1\x16\x9eJK\xed\xf4)\xea\xcb~{\xfd\xea\x90\xcf\x8d;\x93\xb8\xf8\x80\xb0\x89$\xc2\xc3\xa8l\x8fo\x82\xe7i\xb2\xe4\x1bA\xb4\xd7\x9c\x91T\x8a\x99$\xbb\xa4M\xb2K\xb0\xbcm\xcd\x13&)=a`_\xc9y\x06Pxi\xaaYP\xac\x8e_g\xa2\x0eI=\xa9\x92\xbc\xd8\x12\x94\xe2\xfc\"\x99\x84\xac\xa9~\x86\x8d\x1b\xf4K\xa5\xde\xd2\xb4\xb5z\xa8\xa47\xee\x11y\xf0\x90~\x96\x9fd4\xf5\x06\xbe\xac\x17tS\xa7\x8d\x01\xd5C=\x85(\x86\xd8\x87\xb8^>%\xe5\x8e\x8a\x18g8J\xc7\xb2\xc5!&[\x1bM\xc9$\x99\x92\x8f\xef\x8e\x8a,]^:\xda\x1d\xfbc,\xdd;@u\xa1\xf6\x9d\xc1\x98\xdbU{.\xf8$\xb7us\xcd\x9a\xd9l\xec\xb4\xd5h\x15_\x86+\x07\x7f6\xf19\x12\x83\xea\x8c\x88\x0f\xdb\xd0\x1b\xa2\xb6\xb6\xf9\xb4\x9a\x99T^\x97~\xff\x8f$\x8aqy\x9aS\x13\x19{\xec\x83\x92\xf3\xa9d\xdd\xa0\"n\x17K\xd5yD1W\x04\xd0\xcb\xe9l\xe7~\xcf\xf7\xcb\xbb\xbd\x930#\xf7\xee\xe8\xc6Pf\x10jv\x9d`\xb8Y\x94\xc4\xd9{|\xcb\xe4\xb5\x13.V\xf3\xb0%\x97\xacz\x154\\j\x13\xe7=\x1f\xb7\xd0\x02S\xc1\x85)\xf1\x88\xfa\xccpd\xeb7\xe6\x92\xd0y2\xbd\xf2h\xf8\xe7\xa6\xf1\xc8\xa7\xceLDs\x8c4<\xfd\xb3\xc0Y\x1b\xb2\xf3 5\x98Y\xcb4\xe5\xc6\xce\xe8\x9cT\x94\x8c\xeeQ\x0cF\xbd\x91\xf4\xe6\xa5F\x0f\x11\x85m\xe1\xa5oz\xe5\xdf\xa2\xcc\xd1(\x0e\xd8\x06\x0dt\xfb3\xf5K\x9f\xfa\xff\xd9\xdb\xbdu\x1a@o\xbb\xe7\x8f\xc5\xfe\xd4-\xa9\x91J\x11\xdb\xa6\xd6d\xee\xaa\xac\xa4\xc1\xb1\xa6P\x9a1\xc25- W\xac8\xe5\xb4\xb9\x8ct\xf2\x18\xa9\x8e\xbc\ns\xa9\x143\xa4's\"\xc0:\x8f[d\xcaT:&\xcc\xd9\x98\xd4(\x8d\x96\x9e\xb2H\x9f2\\\xa3c\xb4\xd8\xf4z\xb6\xe1\x1a\x92\xab9\x0d\x93\xc1\xec\xb8\x84\xd9\xd7\xa6{Y\xa0I\xe7\xe6\xd44m\xe6\x9b\xb0\xecd\xf1\xd1\xad\x7f]\xec\x14\xccu\xeb\xb2\x05\xc6\x14t\x7f\xe6\x08\x85\xfdgS\xd8\x976\x85\xf5h#\xecb\x1ba\xf5r\x9f\xca\xff)\x1f\xf0\x94\xdfl\xa7x\xf7\xee\xfb\xfd\x1f\xf2\xd9\x8c\x08\x7fq[\xf5\xa3\xb3\"sSq\xf2\x95x\xa2\xa6\x19\xacX\x8c\xc0%S|o\xc49U\xfe\xe9\x18\x91:nT\x8cr\xca\x06\x89\x94\xae\x1cWjcD\xf59\x0eAaO\xf9T\x94d\xbc\x8bhBL^\x97\xc4\xb8\xbc<\xa4\xaa\x9aL[\xe4K\xe4\x14@-1\xe1c)+S.\xd9zZr\xfdP\xecx\x99\x97\xbe\xaf/\x9b%\xb9\xf4-\xa6\xd6\x16\xc3\xb2\xc5\x17\xae-F\xd6\x16\xb3\xb2\xc5\x1b\xae-&\xed\xb3\xbey\x13\xb6&e\xd3?\xba6\xadI-\xaf4\xbd\xe5mQ.\x87\x8f\x16c\xb7\x06C\xd7\x06\xeb\x898L\x0df\xae\x0d\xce\x1d\x1b\x9c\xb4\xaf\xf8f\x83\xdd:57s\x1d\xdf\xb41>\xf5\x17\xf1R^\x83\x85x\x91\xfc#\xe1\x7f\xc4\x8a3+\xcf\xd5\xcd\xee\xbc$kL\xcf\x17\x8a\x17\xe2)\xb9\xc0\x1b\x19\xbf\xf1$\xcb\x92I\x84\x99!\x00s\xb8\xc4e\x00\x1c`x~\xdc\x97m\xb0\xae\xfbe\x0bl\x00\xfd\xf7\x04k84\xe9\x07\xa6\x19\xf8\xfb\xdf\x8f\x8f\x8f^\xbf\xfe\xf8\xe1\xc9\x0f\xaf\x0e\x8f\x8f>\x1c\xbe\xc3?\x8e\xff\xfew\x8dji\xd5\xfc\xe2\xe5\xe1\xef\x87\xcf\x0c\xaf\xcf5\x1d\xbcyv\xf8\x9b\xf1\x83i\xf3\x83\xb7\xef\x9e\x1d\xbe3~p\x06C\xb8\xdb\xbc\xbd\x86!\x0c\xe0\xd1#]\xb5\xf3S\x18\xc2\x1av@\x93\xaa\x7fi\x90\xf7\x8f\xed5\xae\xf7\xeb\x89$A\xcf\xf9\x9f\\\xa5\x19\x13-?o9\xd8\xb9q\x18\x0b\xbb;\x92\xe4\x0b}\x8bT\x1c\x0dE\x83\xbbn\xdb\xe9=O*\xaf\x7fxh9\x89D\x84\x9bF\xaf^\xa9\x0e%\x0bH{\x98x\\\xa88w\xb0JH*r\x9e\xcb\x94\x05<\xd3\xc6\xeeCLw\x11?\x84h{\xdb\x87t\x14\xf1$\x89\x11\x13\xe8\xcd\xee\xf5\xa9\xd3l\xed\x01\x0d\xaa;:\x06\xa2\n\x98f<\\\x82\xf6\x8f\x8fy\xe9|\xe2\xfd\xc1OW\xf6\xc4\xa9\xe3\xb7\xd6Tb\x85\xf5A)\xe9a\x13\xc1P\xb9\x04\x8f\x1f?6\x995\x84\x92j\x1bb\x11C\xbd\xd9\xc0\x9d\xbd\x07w\x1e\xdc\xfb~\xef\xc1]\x9ca\x19\x99\xf8&|\xa3o\x85MZ\x93\x92\xcf\x04>\"\xcax#\x90\xb7Q\xf1\xe1\x06\x9c?l\xc5\xf2\xeb\xf9\x9c\x0dm|v\x90\xda<\x19jP\x16\x9d\xde\x92Q\x91\x14\x1e\x0da'\xae\x14,\x1cJ\xd0\xd5_&\xf0xXW\xc0\x9a\x06v\xd4\x96\xbd\xf1\x83\x18\xb9\xe3\x86}\xed\xda^\xbd\xaa\x8f\xa1\xbd\x0f\x0e\x80\xab\xc5i\xc4\x986\x97/\xb6\xba\xbf l\x03\x1a\xc5j\xb1\xb4\x8cC\x92\xe5\xe2\x99\xbc`\xac\xde\n\x02\xbf\x9f6\xabT\x83pd\xd6\x9c\x07\xef`\x08{\xcd\xdbo\x9c\xb3\xb6\xf3M\x9d\xa4\xcd6^\xf1\x93N\xbe\xa09\xda\x9e\xc1\x10\xde0\x1cye:\x02\xbe\x1a\x08\xf6<\xca0\xbb\x8833\xfe\\\xae\x94!\x99\xa7\xb4Z\x94\x0b\xc5\xb6\xe0\xa0\xb2l#\xf6\xbd\x85\x8a\xc2\x01\xa4\xc5\x19\x12\x89\xb2\xc0\xd6\xd3\xd0\xe0\x078Mb\xd3\x89\xebH\xab?\xda\xa8\x82uH\x1c\xfd\xac\xe3j\xad\xdcc\x18\xd4\x0fv\xees\xebWW6\xf6\x8b\x9d1\x00S\xd5h\x8a8\xe3\xd4\xc5\xefv5\xe0\xaf\xda\xf4\x1d\x05-\xe7Un\xb5\xc5\x96\xf5\xdd\xfdj\xef\x8e3(o\x90\xd6\x8e\xde`\xedR:ze\xcaM\xa4\x9d\xbb\x92\xb7\xdaiD\xbf8\xc0X\x13\xcc,\xb8\x14\xa7.^Z\xbb(\x92\x01\xa8G\x8e\xdc\x8e \xcf\x95-\x85\xe8>M0]\x83\xb5\x80\xb5\xbc$P\xd1y\xbd\x12\x167\xac\xd5\xe6!\xe7@\xa85\xc3\xfb\x96\xa9^\xd8\xe1\xc5\n3\xd3q\x06\x0d\x92\x14\")\x15 5K2\xe3[.\x0b\xd8\xd3\xcf(\xdd\xf0G\xfb\xe8.o\xeaV\xbb\x8a\xecj\xa6\x083\xc0\xfd\xc5\xb7\xc1\xbdO\x13\x94\xc5$\xc4\xc5\"\x84\xcd\xb5\xa0\x98\x9f\xfd0\xa6\xe9\xbax\x99\xba\x8e\xf2\xc6\xb7\x8dR30\xa2\x0e\x84\x8dSH\x91\xf2V\xe8<\xb6\x1f\xadc\xf3\xbe}pr4h\xe0\"\x14\xef\xd7F\xa6\xfe\xfa\xaa\xa8\xaa\xa8&\x1f\x81e\xb0\xbd\xd1\x918\xa0\xc75\x05t\x00_\xfb/\x0f\x7f\x7f\x0fCx\xca\xfe\xfe\xe5\xc9\xab\x8f\x87\xec\xd7\xcf\xec\xd7\xe1\x9b\x0f\xef\x8e\xf0\xe7\xbb\xa0\xd2\x7f\x14g+\x9e\xed\xbc6\xaa$O\xab\x99\xb9m\xf4\x85\x1d\xf0\xe6\xdc\x0bJ\xcb\xa3g\xe3\x0em\xd6\x1b\"\xdeK\xae\xb7x\xd9Of\x8e\xed\xbc\xf4\n'\x92\xc6\xc0^V\xa7L\xbe8\xb6\xa9\x1b\xdb\xcb\xab/*\x82\xef\xf8\xb84\x8e\xb2\x91\xfc\xbb\x17@\xef\xb2i\xcfQ\xfb\x99\x84\x939yG\xb2\x962\xc7JW[\xbc/\xfc\x10d\xc5\xafB\xd6\xfb\x18\xe3\x83)\x17\x06\x957\x87\xfc\xc5\x12\xeb\xcb\x8a\x0f\xa2\xfc\x99\x14\x1c\xcb\x8f\xc4\xd9\"^\xb0M\xa3\xe8\xdf%\x86HLdB\xcb\x82d\xbc\x02\xa8K\x0f\x89S\x00\xbe\xe8b\xd6\xda\x05\xf1^\x04\xf0\xd2\x0f\xe0Ee\xf1%\xbdu\\\x13=\xa6\xdf\xe0-\xdfp\xc7\xf4\x1b\x16L\xbfQ\x19`II\x1d\x9b\xd6\x0d\xf1\xc65#\xfc\x88!\xfc\xb8\x89\xf07\xae\x19S\xea\xb5\xdd\xf5=|\x13\xa64\xbb \xde\x8f|=\x7ft_\xcf\x1f-\xeb\xf9c\x8dr\xd1o[\xcb\x97\xfd(\xe3-D\x94\xfd\x92\xda[\x86\xdeB]\xcb\xc6\xaf(ro4\xb5\xb7?\x05\xf0\xcf\x00~\x0b\xe0\x1fM\xa5\xe9\xfb\xc3\x7f\xa0\xc2\xd4$9Rj\x11\x1d\x8fCQ+\x83\xd6\x88M\x17\xf6\x95\x18z\x90\xfc\xa50.}&\xebL\xcbC\xf2\x91$\xb26\x88\x1c\xca\xf1gQ\x0b\xab:4\xd2eh\xb1u\xf2Q\xa9\x9f7\xcc\x9f{\x16:+\xe8\xd2\xf6\xee\x84\xe1,\xa8\xdd{*\x0e\x83zm\x1fCG\x91\xa1#y\x16\x95\x06\x8c\x7f8\x1aX\x90\x1b36\xf8\x13k\xcd\xfbI\xe8Z)\xf5F\xe3Ff\x16}\xbby\x0brh\xd2\xe0\x88.\xa8\xdf\xe4\x9a\xbf\x94o\xa4\xfa7~(\xdf\x88\xf5oh\xa5\x9c\x83R\xc8)TOf\xcf\xbe\xabK:\xa3\xcf\x01\x9c\x8dAd\x8a\xed \xf1t\x92Y\xc3\x16\xa0gza\xee\xdb\xa7\xc7\x05\xb9k\x9aEfG\xf2_j\xd8\xa2A\x0f\x0d>\x14\xab\xeb4\x04v\xc29\xa9\xcb\xa8`\xcd\xf4@\x8dL\"xa\xe5H\xd8\x01QZ6\x06\x01\x864\xef>\x84\x1c\x1e\x0d!y\x08\xf9\xf6\xb6\xa9\x11\x10\xe3\x08\xd1S8f\xa2\x15\xec@\xced+\x83\x7f\x15\xc8\xc5\xe6z=\xe2\x85\xa3\xc18@\xc5]8\xda\x1d\xb3/\x03P\x02\xdas\xd8\x86\xa6\x12\x0e\x1a\xe2\x97\xbc\xe4g\x8d\x87\x96\x04s\x0dV\x99g\x83tZ\xa6\xd9\x9f\xbcL\xda\x152B\x96\xaf\x9c\x0d0\x0c\x1b\xbfzV\x96B^\xd2\xf9\xc3}a%\xf0\xb7\xb7\xe11:W\x9b\x1b\x077u\xa7\xbc\x8cjOy]\xc2>\xc7\xcc\xb9P\x1f\xa9i8s\xfbp\xa4E\xbe\xe2w5\x94r}\x8e\xf4z\xa8\xe9\x93j\xbe,\x03\xb8\x05\xbb\x85?\x8b\xf0{\xf1\x03\x89\xce\xf2C\xdb\xc1\xf6\xcfbh\xff\xd4#\xce?\x85\xcd\xa0e\xab\x99\xa0u\xda\x02-\xaa\xaa \xb8\x8a\xc0\xd1WhIm\xceB\xfa\xa5X\xd6\x96BiC\xbf\x1a\xa7\xd4\x13\xaeV\x01\xf4\x9e\xf2(\xde\x8c\x92\x15\x84\xf0.\x8cO \x9c\xaca\x17\x83\x1eAX'w\x83\xea*\xc9\xba#\xb8V~\xa0$\x01\xe0\x9eo\xa2\x1a#.ax\x92\xa1\xeb!\x81G\x82cco\xef\xc4\xd2\x84s\x8c\xc5\"T\xbd\x1f\x89\xa7\x8aj\xf3\x18\x87\x86\x83U\xb1FE\x0f\xfc{B\xa2\x85\xe7\x11\xd8a\x04\xf8\x16\xc4L\xb4\xf2\x99l\xde\x0dw~+`\xf9\x9b\x1ew~\xfb6\xdc9\xd6\xeb\x129\xbe(*\xa5'\xa2\xfaa\xdd2ah\xf6\x84\xda\xdcL\xcf\xadO/\xc4S\xf5\xa1b\xc6\x1a\xfdc,\n\x01\x11\x8f\xd2\x00n\xb0\x95S\xe3\x1eN\x89SIW\xc9\xb5\xb3U`\xe4\x91\xdb\xb4KM\xfb\xe8\xad4g\xf8c]\x05\xf3J\x9f\x9dL2\x15\x7fY\xa5G\xe1![Q-\x95\x1e\xb2CH\xb9\x8b\xac\x11W\x84\x8a\x88z\xf1\x88Q\xae\x14v\xd0\xa3+\x1a\xa3\xf0\xc7:*wf\xc4P\xd1H\xb5\x1bu\x1d\xb4\x93u\xb3\x0e\xe9&\xaa\x9dBc\xf2\xfa\x89\xea56\xdd\xb45\x05\x10\x1e\xa3\xfa\xc3\xc6\x819i\\\xac\xda\x16\xaei\xa1\\\x02/Wf{\x9b\xad\xcd\xf6\xb6C\x14 CuB\x03x\xc1\xe8\xd6\xd5Q\xbd\xee\xe5\xaaC}\xae\x1f\x1eQ-\xcaW\xfa\x9e\x87\xee\xf1lJ\xd3\xf5(wM}\xa2\xeb\xdcX\xbcS\xbe\xb3JSU \xd8ju\xa7%|\xa7%l\xa7E\x0f!1+q\xcfDY\xbc\x14\x173\x82\x1dH`\x1f\x12\x83\x9e\xaf\xb63\xf31V!\xae\xee\xc6D\xab\xb45\n\xa3\xcd\x14\n\xd7\xb5=\x05\xb8\x8c\xfbS\x01\xa1qw\xa6\xad{8\xb9\x8e=\xdcm\x15$\xe4P\xd3\x1a\xfdu{>g{>w\xdb\xe3\xca\"\x8e\xa6\xe5!\x17\x8bC.\xd6\xee\x8b\xc2[\xc5a\xad\x19*\x96\x121\xaeeEhR\x84\x0c\x03\xf7,\xb1\xe5w\xafj\x96\xb5\xd4\xb02\xe8$\xbex\xb1A\x06-vq\xf4\x10\xb6\xbc\x08O\x05\xb5*#(\xb9\xbc\xbdHT]\x84t{[\xec*]\xfdR1\xe5F\x8e -LK}\xf5\xb5\x025I;C\xd5\xa0\xce\xf9\xa2j\x89\xf9v\xf9hh\xd6\xb0\x02\xdd\xb7\x1aQ\xd6\xa1E\xcb\x81\x8b\xc4\x9d\xd1q\x0f\xe0\xd2\x08\x15\x9e\xd3F\xf0R\x81\xf2\xe9\x7f\x01\xcaW\xea\xc8\x17$\xb0\x08!\xe0\xb6\xaa\xa6\x83\x80z\xa0\x14\xc6\xa8\x87\x0e\xcc[4J\xc6\x01#T\x8dC\xc206\xb6KbEK\xc4w\x89\xb1\xf2\xbc\xa4\x9b\xb1M\x9b\x84&\xb6Q2\xe6\xe1\x90\xc5\xd8\xf2\xea\xc0NR\x12~n.\xa8 \xdb\x1a\xc7\x96vy\xffc\xbb\xaf\xb6\xb0F\x82\xa6[l=\x10\xafc\xef\xe1J\xc0\xe3\xf2XmS\x18\xb6oT\x90p\xe3En\x8b\x8dkQ,\xf2\xa0<\xb1\x87\xb5\xafY\xad\xcb\x92\xfdMG\xee\x0c\xefZ\xd0\x805\xbd\xba\x8b]M\xd0\x86\x03\xe8\xbd#+\x12R\x18\x8d{\xb0_\xfe\xe2^\x10\x8aZh\x1bz\xe5=\xfc\x96\xdd\xa1\xd1\x92d\xd0t:^_\x9d)\xd71\xe1|\x08\x1a\x06\xbc\xd2\x8f\xac\xf4\xe3\xca\x85O\xa9\xaa\xf8jFe\xd5\x9a\xc7\x94\x05.\x13\xa9\xec\x1f\x06*#\xca+1{|\xaa\"U\xd2\xba6\xb2\xd7\xa2\xba\xe4\x0e\x0f\xa6\xab3\n\xf5\x91\xa6\xe4\x8c\xa4Y\x177\xed\x16\xb8N\xc9\xc5\xdb\xd9\xd5\xc1\n\x07\xa81\xdc\x19X\xbbY\x84\x19=\xba\x86\xaeJ\x0cm\xed\xf2\xea\xc2\xd4\xeeC\x88\xe1\x91\xb2\xc4\x10;i\"*\xc3\x8d\xeb'ZlUB\xc4Ns\xe9.\xe5tbU\xbb\x11k\xc9f\xc2#\x88%\xc5)Y\xa0X@\xc27\xd6\xd9\x83\xeb\x12?\x1c(l\x05\x9a\xc2H\xe9\x88\x87\xb4\xaaz\x87\x83&f*S=k\xda\xfb\x19}_\n\xfa\xbe\xbcf\xfa\x8e*cI\xde\xf9\x0f\x85\xbas\xed\xee6\xf4\xfa\xfd~y\x97\xc4S\xd8\x06O\x08\x15\xf3B\xcd{\x00=8YW>'+\xcc{\x84I\xe74'\xc1\xf2zO\x029\xdcR\x17 \xdfU\x87\xd28#\x96W:#$\xe7\xe0Q\xd8Q\xfb\xf6\xe1\x96\xd2\x9fq\x7f`\x80\xf4.7\xc8+d\x82\xdf`k\x84:\xf1\xd9\"\xd1\xd8\x1ejCv>wj\x87J\xd1\xa9r\xb8\xa0K\x01\x9e!\xe5\xd3\x80\xdb\n\xf0\x8c)\xef\xfa\xf0hX\xf8\x96.\xa9\xb7\x1b\xc0\xae/\x8e\xa7\xa5@\xeeSB=\xd5* M\x06\xec>\xd1\xdcG\x905\xcf\xae\xe5U\x0e\x9b\xb3\"\xaa\xb2\xb2B\x0d\x85/\x18\x031.\xc3\x1c\xd4r\x07V\x87\x03\xe1Z\x89N\x96\xece\xeeSa\x19((x\xba\x0b\x1b\x93s\x14\x1e\xa1qY\x8d\xd3\x8b\xe1_C5G\xd1w@\xfd\x87\x0c1\x94\x9b\x0f}\xc0\xd7(\xdcR\xdf\xb5\x12\xdcC\xea9\xa5J\x8f\xea%]\x145b\x99\x9a\xffg\xaax\x99\xeb1\x0d\x94UxEG\xd4\x9e(\xb7\xea\xb1\xf2\x96ao\x00o8\xac\xdf\x89\x9c\x19\x14\xd3\xe1\xc0+\x9e\xe8\x1c\x9f3*\x8e\x8d\xb3\x83\xef*Y\x16`\x9fw\xd6 \xc7\xe7a6\x7f\x9aLU\xc8\xc8[:\xe5bT\xaf\nV~\xe8\x08B3\xe3\xf9\x9a\xd6\\M\x11~G\xdccM\xadPji\xa3\xfe5\x1d=\xa5c\xa7/\xb7>\x1b\xc7\x0d\xa6\xc6\xfb\xa2\xea\xc1\xfa(;\x8c\xf3\xa5\x08\xc0Bw8\xdd\x13\xa7\xb1\x98:k\x07\xaf\xfa\xb5p\x98\x8c\x93)\xf9\xb0^\x11@\xd2\x9e\x9dG\xbc\xfeYq\xbf\xad)vM\xc2\x8c\xc0`\xbf\xf5=Ph\x7f?\x8f\xa3/99zf\x9e\xa3\xbc\xb0\xf9\x07\x1d\x9b\x9f&\x13\x0c\x18>\\\x10\xf6\x0f\x9fl\xedf1\x06k\xd3z\xa56\x88-\xa5\xac\x96\xf6=\xfd\xd7l\xb9\xb6\xb7?\xd0@=\xfan\xc2\x07\xbe\xf7?\xe0\xde\xb7\x84\x88\xbc\xa6>\xc3\xfa\x8c\x18=\x1c\xc1\xc1\xd1\xb5\x8aB\x7f\xc8\xfa\xc8C\xfc\x81.\xcfu\x8f\xc1\xde\x9b$\xde!<\x95q\x19H\x98A\x98\x12,\xfa\x86\xd9\xb5\xc9\x14\xc2\x0c>\x93u\xd67\xd5=\x90\xdd\xb3\x0d%\xa2\x8dy9\x89\xd2#$\x80\xa7\xd4\x14W\"/R\xec\x9b}\xd8\xb2\x04x\xb1k\x92\xc4\xb3\xe84w|\xfb<\x8d\xa8\xdb\x9b\x82O\xd7/>\x80\xb9\xa4\x1e\xa8\xe5\x0d+N\xf5\xddH\x86`\x93\x95H\x12\x85\x83\xd7}\xe0\x1b\x1b\xb2\xab\xdb\xd4K\x95\xb5\xdd{\xee\x87\xab\xd5b-\xd8xCD\xbfz]\x06\x162\xc9\xce\xc0\x16\xc8\xb6\x13\xc1\x8aSzI\xf2\x1ax\xff1F\x08\xd1\x042B!\x84\x98\xed\x83\x12rr\x8c\x90\xc4bOXQ\x9f]T\xce\xc1<\xfb\x0e\xf4\xc4z\xeaw:\xed\xa5\xf2\xb5 k\x8caP2\xdah\xf3\x01\xd4\xa0\xc5\xcb)\xb3&y\xfddT\x93\x96\xa5y\x18\xf7@\xa6}G/\xd2\xb7\x06\xde\xbeP\xc7\x10\xce(\xa9\x16\niiG\x03\x05\xbep{\x00\xdf\xf1T\x85\xfd\xc9\x829\xf3Ld\x15\x16\xd6\x97)\xdc\xbdu\x9d\x11\xfcW6_r\x85\xa7\x92\x01\xeau\xb82\xa6<\xfb\xfa\x8d\x96\xc5\xe34IJ\xcd,\xfb\x81\xa2s\x11K\xc3\xf36\xf9:\x93b\xa5\xeb\xacS\xd7\xffP\x93B\xd9\xe7\x94\x11z\x14wh\x1a'\x92\xaf\xa6!%G\xf8\xf22h?c\xcd\xdc\x92}p)Y&g\xed\x92\xb6f\xd6K{\xc3S\xb2 l\x02\xaeM7f\xed:\xe5e\xd7)\xf3N\xea\x0bbO\x1c\xcdE\xc8F\x89\xcb\x03\xe1\n\xe2K\xe3L1\x81\x11\x1d\x8bF\x1d\xc6\xd2D\x0f\xc3h0\xd8\x15\x9d\"E,&Gq\x8b\x8flA\xa2]\x12I\x9c\x898P.\x80-\xcd:\xd1\xbc\xd5\x17\x8f\x91\xbb\\\xf8\xe1\x99\x89\xe2\x99H\x19\x93`\xf0Hk\xc5\xd8\x0c\x86\x10y\xb6\xb2\xdcb\xb92\xbe\\\xc2Y\xb7\x19C\x06F\xa9\xe3\x94z \x03\xb2\xc8\x1b\x9c\x11\x1a@/\x8ay\xb5\xfb\xcfd\xfd3V\x883Cf\x82%\x80-\x1e\xa8\xec\xa5\x99\x98\xf2\x92M\x19\xa9\xd5\x84\xed'\xf3\x07X\xa0\xd4\x9b\x95\x0bhU\x94r\xd6e&f\xcf\x7f-\xd9/\xb1\xdb\xbd \xc3W/)y\x19\xe2\xe3\xd91 `\xa1\xe1\x01\xc4\x9e\x8fc\xd4\xe9\x1a\"\x1eE\xdfi\xd1\x9b\xe0\x9a\xea\x96\xd9\xfa\x0e\x98,Hh-J\xa44\xdet\x8b\xa1\xdc\x1fB\x1c8\xc9yL\xd2\xa3gp BaE\x0c\xe3n\xa0\x9e\x14CQ\xb4S|\x83\xc1\xfb\xc3\xf2\xac\xe0w\xc3\x05\x15\xf5N\xb6\xc4M_pw\xd6\xc9,Iz\xda\xaat\x90\x90\"\x02\xae\xb2ks>\xc0f\x1f\xbfF\xd5\x92c\xb6\xf3\xa4\xe8\x08\xfd\x97\xea|\xd2\xa0\xe9\xc8\xd1\xec\xaeJ\xa0\xec\x86pM\x0fFl\xa9\xd2L\x12 \x84\x03\x07\xad\xaf\xf8\xde \xf0\xf3e8\x90\x7fI\x1d\x0d\x12\xd5}\x88Gj4^\xb3\xa8m\xcb\xf1\x81M>#\x18,\xdbi\x9d#\xd2m\x8dY\x1fN\xeb|%\xd0\x17\xc3J\x88\x87b\x85\xe3\x88\xfe7\xa2\x02\xae\xd6\x81\xfa\xebzQ\"KR\xea\xca\xe7\x1c\x11\xef\x17R\x98\xfd\xdb\xdb\xfda\xdd\x81uT\x1b'\xed\xedWd\xa0\xd6 \x14\xb2\x16[\xa90{\xcdu\x11:\x06@.)\"\x16\xe9\x9f\x87\xd9\x13NO=\x1f\x8f\xa1\xe3c\x12gyJ\xde2z\xedU\x89\xb7d\xa5\xac\x03/zw\xdc\x83\x8d\xf3\xa1zn\xa8\xa3a\xa2\xd8{;\xd8\xc2\xecHjb\xba\xf5\xaf\xf6\xd3\xb22\x05\xc8\xba\xf5 \xce-k\xdb\xdd\x1c\x9c\xa4F\x84\x9c\xc3\x0dw\x99\xa7\x93\x17\xda\xb7:1+\x87{\xe1m\x83r`3\xb3H\x0b\x11\xe1\xc1v\x1e\xc1\x043\x043\xca\xe8l\xee\x01/\xfb\xd4\x02\x01e\xb5[\xf7\x96\x9cI\xc9\xe0\xe8\xb0\x15\x0e\xe0\x9f\xb4dmT\xb6&(\xf3: K\x83\x1c^\xad!%\xf7\x83\xca\xe0\x0c\x04\x83\xa3\x99N\x941\xc9}\x08\xcf5\x9eC\x1fi\x00?\xd0f2\xe0\xd7O~6TO\xfb\xc2\xdeV\x81dR\x0f\xfenN\xfc\x81\xc3oNH$*j\x18\x1f\x8c5>\xac @\x0c\x9d\x9cDt\x89\xe0\x90\x90\x8f\x13\xee\x82\x1c;\xf5\xf9\xcbU\xfa\x9c$yL\xaf\xdc\xe5\xcb\xabt\xf9\x99\xac\x7f\xe4L1i@\xd7\xad\xdb\x17\xd7\xd7\xed\xda\xb9\xd3\x1b\xed\x9d\x1eS^j\xb4\xdc9E\x84M\\\xfa6\x87\x93\xcf\xc8\xbc\x14\x14\xe5'\xea\x89_n\xda\xd0\x1f[S<\xf2\nH\xa6}\xac\x0b\x025!\x0f\xad\xa9,$fGAA}\x10u\xa9FM\xd1\xd4Q\xf8X\xe4\x0c9\x84\x08w\x9bN_a\xc0G\x11%^\xe8\x97\xf8\x82\x06\x10Zy\x15&Qq\x89\xcd\xd3~\xba\xcf\x10Q\xac'e\xfc\xc8\x85\x17\xfa\x01\\x\x0cU\x18\xc4_\xc8\x1c\xae#\xf6\x99k:wB\xec;\xbeVy6\xf74\x9eEF\xf2\x92K\xa0En@\x8e\xac@.v=zm\x95j\x95\x9b7\x01\xb3\xb0V\xd4+<'c\x91\xd8\x97o\x7f7\xce<\xb1\xef\xeeR\x9433\x15\x002\\\x0cu\xf8Ue\x1a\x8e\xb7\x92\x8c\xba\xf2\x9c\xab\x84\xcc\x9ax<\xb9\x8a\xce\xadjx\x9e\x8d2\xf2\x85\x1e>jY9\x13@r\x97e\xe1\xdb\x1c-Cq\x7f\x16\xb1\x93\xc1\x01\xfd\x8a\x8f\xcb\xc4\xb9\xcdA\xfa\xbeb\xedb\x07\xb2\x9af\x17\xe9jy\x8am\x18\xa9\xc0\x94\x87\xca7W7\xb5\xa7\"\x1a\xaa\xf8\xc4\xb6\xe2\x80&pq\x1e\xa5U\xabi\xab\xf7pE\xfe^\x8a\x1a\xa3\x08x\xec\xd2\xf8\xad\xc6e\x02o\xabA0\xa6\xa5\x93\x17\x95n\x19\x86\xf4\xb1\x97\xd5z\xd2\x05A\xc3\xb2\xd2\xf1(\x1a\x17\x0e!\x9a\x81bf\xf2\xca\xd1\xe7\xc5\xa3]G\x89#l9iA\x84\x86x\xf7\xef\xde\x7f\xf0\xe0\xf6\x9d\xbb\x0fx,\xcf\xce\x10\x03ax\x1c\xcc\x9d\xdb\x83{w\xef~\x7f\xef\xae\xef3f\x0f\x1f\xec\xc1M(\xbeQ\xee\xdfa'\xd3\xde\xdd\xbd{w\xee\x0en\xdf\x0d\x80\xc2\xb6h\xea~\x00\x83\xbd\xefy\xf3\xf2\xde\xe0\x9e\xdb42\xe2(\x85\xa4\x02\xc5\x0fm\x15E\xa3\x11\x19\x0b\x01\xa3\xd6\xbb\xfa\xeb\x0b\xba\xba\x08\xde\xec\x0b\x15\xe6p\x18\xb2\xbf\xb9\x15.(\xffD\x9dz\xf1\xd2Q\x1c\xc0\xef-N\x11\xe6\xb9T\x0eCUz\x17\xc7\"g.\xa2\xf2X\x84G\x90\xf3\xd3\xd1HH\xa7\x88\x9e\xd1(\x193\xd4)s-\xb2\x1b\x03\xe7R\xe6\xb5Y\x19\xcd\xf0*\x1fi\x9d!\x16\x1b\xe1;6\xc0\xd3\xb9:\xdd \x9f\xee\x0c\xcfc9\xdd <\x02\x8cm\xda\x9abB\xe0l4\xc1I=\x84\xc9\xf6\xb6\x81![\xc0\x90\x7f\xa7\x17\xc8\x16p\xc0\x9b\x19\x8cq0\x11\xec3\xeeWQN\xea\xbf\xe3|\xb0\x17\xa2g\xd4\x02]\xc9.\xbc\x84IQaIH\xb3\x96\xec8\x18\xc4\x81\x0e~[!\xfb\x7f\xe1\x9a\xf0x\x08\x13]\x98\x8a\x15y\xe4\xc5\xa5Z\xe9\xb1\xf8\xdebp\xaf\xa0\x9b\xe0\xfah\x00\xe8\x88\x1a\xc0\x88u4\xf6+\x1c\x19q\xe1\xc8\xe4%\x9d\x0d\xc8\xc8\x94\x00O^\x11b\xb5 \xff\xb4\"\xa2\xe6\xa8h\xc9\x8d\xd5?@\xcbE\xc9K\"\xbb\x9e6\xb3\xae2\xabQ\x9eMa\x05\":LQ\xf0J9\xd3\xd81\x93\xf7V\x0c\xb7\x90\"em6\xff\x03\xe4\xaf'\xc2\xf6\xbf\x03\x038\x80y\x7f\x95\xf0J\x10\xf3\xd1\x84Q\xa3\xc6\x8d\x11\x1b9\xe3\xc7\xe7\x9c\xc1\xe4\xbf\xfd\x00{\xf6j\xda\xbfyi\n\x97\x02s\x00\xf36\x96\xf42\x80_\xafL\xce\xb4\xd1e\x88]\x86\xcd\x8aB=\x13W<\xafZ?\x9cG~R\x94}\x0c\x9a\x91D\xd2\x10\xae\xe95\x126\xd60\x93snr\xee\xae\x08\xcdF\xe5\xec($\xfc\x11fF\x1e\xf38..#\x11\x1d;Q\x07\xcf\x95\xe9b%3\xb4L\x00\xfd\x84z\xa9 T\x8a\x80H\x04\xcb\x13#\x90\x88E\xaa\xcc$|C\xfd\xf3I\x15\x86\xfa\x97f\x18S\xb95\x04o\x027A\x87\xdaH\xd7\x90PGue\x8e\x96\xa0J:\x1d\x12\xde$\x02_\xdf\xf9J\x8e\x10\x97K\xff\x0e\x1a\xdd\xe1\x00V\xa3\xc5\x18Z\n\xb1sE\xd9\x9c\x9b\xc5\xf8BW\xd7J?;\x1e%>w8(8\x1c0\x94|\xa5\x90\xf7\x99\x95\xbc[\xdc\xbc*\x15\xbf\x04C\xc0\xf63\xaf7\xb3\xf6\x03\xc4\x8c\xdd\x87\x82\xd5\x8f\x1fB\x88i~\x18n\x0ca\xe0C>\n\xc7\x88\x067Q\xb3@F\xc9\xf6\xf6\xd8R\xb3\x0e\x14\xa1t\x94\x8e\xb9\x8a\x8b\xf5\xc8M\"\x98\xe3A\x1f\xcc\xcf\x1e\xaf\x02\x98\x04\x10\x0605@R\x9c\xe7\xec\xffj\xb9z\xb5H\x7f\x93*\x11\xb4x\xb2\x04\xb6\"\x12\x0df\x81c\\\xeaWxS^q\x0eRQp.W\x88?{k\xe03V4\x1fc\x9ck\x0e\xdb\xc6\xd4\xb8\xd0~xs\xa8iA\xd6\xc2!\x15\x1c\xb6\x84\x9a1M \x14\nu\x84\xda\xb6@\xaa\xa8\x84\\!P\xb8\x80.\xa9\x80\x8e\xab\xd6\x10tb\xcf\x86\xf0\x08\"\xdc\xb1>\xbb%h\xbb\x97\xf0-\x1b\xf3\xd7w\x06\xa8\x9d\xe5\xf7\xe8(\x84m\x97rn\x86\xc2\x1f*\xee\x19\x8f\xcc\xe3\x82\x9d(\xac\xa8'5\x93\xe6y\x95\xbb\xe0&\xda\x93\x00\xce\x1b\xe7\xe5/\x7f-;aa$Z\xf8\x08\xce\x10Df\x11)\x81\x03Ht,\x82\xceo\xf2\x97\xffel\x82\x94\xcd\xb4/L\x1cNa\xc6&LF\xa1\x81Lg<\xf8\xc6\x911\xa0\xc4\x9bu=\xa2\x85#\xadC\x0f\x05O\x81\xf6z\xc3\xb1\xd2.\xc3\xed\xec\xac\xe0\x11,\xae,\xb7U\x08\xecn\xa0?\xe0cy\xc0s\xa1y\xc0%\xe5R,c\x14d\"\xce\xfc\x0c\x1e=\xc2#\xbf]L\x9b\xa1\x98\xa6[\xac\xca\x9beT0\x1e\xb3!\xfe\x89\xb4\xd1\x8b`3d\xc2T\xce\xf9 \x06yc[\xad\xf2ZIB\"-k\x01\x92\xbd\x98 \x87\x11\x1a\xcd\x8c\xab\xedm\xfd\x9a\xcf\xbb\x9e\xf2\x8cS\xcc\x88\xc7\x99\x99\x05\x93\x9c\x8cta^\x90K\xe9\x00\xb2\xaaQ\xcbi\x95ZrNj\xc5\x98\xa4:\xd9xyej\xf9\xdf\xacKz\xf9\x9f#\x86\x82\xae\xe9wy\\\xe6Z\x14\x86\xbab\x8e\xa1\x92\xc0\x8f+\x7f\xb8\xbe'&\x8a_\x1d\x0eZH\xe1\x9a1\x14K\xf2\xff }WXr\xee\xb3\x8a\xd5\xf4E\x99\x97P\xc0\x92M\x80\xb1\xee\x13\x93\xf1\xb4\xb3\xa6\xa5]\xcb\xf2\x1f\xd4\xb0\xbc\xd4\x00`\xde\xd8\xe0/\xae\xbc\xc1\xa5\x18\xc3\xa3B\x0b\x9f+\x86 2\xa2\x8e\xdf\x18\x8cu\x0c\xc9\x8b\xeb\xd9\x835U\xaev\x99\x90\xe4!\x06W\x87i\\./\xc3\xea\x19\x05\x12(\xf3\x08\xfd\xc6F\x0ce\xc0\n\xc3H\xd8\x87\x0c-\x01Z4\xaa\xac\x1a\xb68,\xca\x10\x89e\xd3\xe1\xadXv\xde\xa5f\xd7#\xd1)w~c\x91+\xba\xf3\xd2\xb9\xf6\xa5\xfeve\x0d\xac\xa4=n\xd0\x91\x94\xd3\x91\xa8V\xb6\xe8!\xa4\xa2\x84L\xea\x94\"9.\xea\x97\xa0\xe7\xc1X\xadwY\x9f\xdc\xaf\xfaY\xfcrm\x93\xe3L\xa6\xdb\xd4\x0c\xbcN!|\xd5\xe6\xa5\xe7w\x18(\x12(\xb3\xcf$\xfdJ9\x06\x13,@\xa7=}qE0H\x8a\xac\xa0k\x03\xad\x88w\x83\x06\xf0\xd5\x0f\xe0\x86\xdaKL.ZS;\x14P\xa6\x12\xca\xe8_\x19\x94A\x02\xdc\x99\xf2!\xd8\x8b6\x88\xfa\x13\x04\x17\xc9\xac\x0e\xc7\xd4\x98<\x0b\xaa\x8e#\x03)f\x8b\x89Z8\xd6\xa8\xa8\xadZ\n\xe1\xdcg3\xd5AI^\x97en\x9bT\xee\x96\xb6n\xb0\xbe\x99\xa8b!>Q\xf0\xce\xd7v\x1f\x91l\xc4\xc1'\xddS\x0f\xb0\xcc\x1e\xafy\xd6:6\xb5KD\xfbj\x87v\x95FR~f\x19\x83]\xd1\x91\xb4I\x0b\xf8\x92\\\xa6\n\x00\xe4]\xbb\x0cQ\xc3/\x18\xc2O\xd4K\x8c\xf6s\xb0\x8a\x0b\x93$\xa6Q\xdc\xa9\xf8C\xb3\x7f\xe5W\x9f\xfb\xcc\xb6\xecj(\xb7\xa7ic\xb4\xe6J5\xe6I\xad\x11\x90*0\xd9*c\x1e\xea5\xdc\x82;\xcd\x96g\xf2\xd9^\xf3\xd9\xa2\xf8\xce\xe4\xb9\xbf2x\x0c\x9c\x89\xd8\xa1\x0bc~=\x87<\x96\x9a\x88Z\xf6\xe5\x9cxJ\xcaI\x8d\xf0-O\x82\xc8\xa3\x96\x0c\xa3\xb1\xbd\xc6\x03\x1fL*t@\xde3~\\\xa7\xf0\x98g\x8dN\xe1\x11\xac\xe1\x00\xce\x89\xb7\x8b\x0c\xcfY \xe2L\xb1\x10\x04\xf1\xe2>M\xb8\xfc\xedcYZ\xd2\xd9-\x06\xfdD\xdeG_ \xf6\xacI\x03\xd2\xa6\xe9-4\xb5-\xfe&:/\x127O\x8b\xb9\xddaD\xc9\x032%-y@\xd8ArN\x19\x9bL\x1c\xf2\x80(\xc2\x87g\x8e\xb1\xe49\xbc\xc4\x11\xf7\xad9-^E\x19\x85Q/\x80\xde\xb8\x99\xd4\xa2\xd2\x93cR\x8bH\xd6\x8a/\x93\xe2\xfbEVrZ\xcdJn9M\x99\x00[\xb0\x96\xe8+\x83#O\xd2\xe842y\xb6I\x99\x8b\xf5\x14\xf7y\x99P\n7\xe1T\x13\ni\x02P#\xbbF\x05\x06\xdd\xb2k\xb8\xda/\x10d\x84\x83\x8c\xb3U\x95\xaa\xf9&\xbfo\xf4\x0d|\xac:\xb1\x11x\xa4d\x83\xed\xee\xb2\x06x,<\x82]8\x80\xb7\x82\xc7\xc3m\xb6+\"L\xdfJ\xa7\x04\xb4\x00\xf0gD\x1b]\x06`N\xb0Gp=\xe5b\xea\xdf)\xed9\xc74\x8c\x16v\x86J\xba\xf7\x1b_J\xac\x81\x02\x08\xc5\xcf\x18%0 W\xe1$\xa2kn\x10\x1f\xc2{t\xc2\xabG\x0dpy\x10E\xac\x88\xbf\x14\xd5^\xa2\xfd\xe3\x059#\x8b\xf2]\xf3\"n%\x8e\xe1\x06Q\xfa\xd0Z\xee\x00\xf8\xd8\xd6\xba\xd0\x13\x8e\xc6\xec$\xd3w\x13 \xbf\x0b\xae\x8a\xd4\xf7\"\xaa^\x98)y\x0e\xea(F6\x03\x16\x16\xa9\xcf\x19\xdd\xca+`F\xd8\xc2\x0e\xea8}\x1fG\x83o%\x15P5\xa9\xb2v\xc0\xdcJ\x169@9\x84!\x1c\x96\xb9\xb3\xf4\xf3\xdfJ\xf4*\x95\x8a\xe3\xc4\xeeC\xc8\xb8\x8bi\x86~\x92\x02\x16\xd9\xb8\x10\xbf\x8c\x049B7\x91\xb0\x80\x1e\xa3\xf1~\x00a\x9d\x82ip\xf4\xc9\x8c\x92\xc6\xf1\xde\x8a\xa2^\x15G1\xc8\xf8\x1b0UX?Q\xa8oA\xd8\xc8\x8e\xb0\xfaN\x9cp0\xa9\xe2\xa0\xc9\xa2\x848\x98b\xb2L\x86]*\x185(\x88/Ez\xc8\xa0\xf1\xab#r\xca\xcdbE9\xd1d.z\x13\xca\x8a\x08\x95|\x81\xf0k\xcb\x8bi2&\xca\x0f \xaf\"K\xf3x;%\x01,I\xc0\x98\x06[\x1a\xf5\x13\xf3iU\xf2\xea\xf2\x10\xd7BX(\n\x8b\x93]\xbf\x0c\x80J\xbe\xd4\x165\xc3\x0f}3|*\x89D\x04\xe3\xb0\xeb\xd7&\x06\x95\xb8g6\xb70\x00\xa3\x8d\xb5\xa2\xc7 +\xe5\xac\x0c\x9e&\xf2\x92\xc4$\x17\xfeK\x07\x12\xc1\xf8\xf1\xbe/\xa3\xdc\xf1\xa7\x99G\x05\xe1\x97\x92\x8b\xca\x87\xbb\xe8\x19\xbb\x03\xb9\xfd\x93 F\x9a\xee@n\xe0\x1b\xf1\x95\xc7\xb0F\xdca/\xdb\xec\xa1\x02\x08\xad<\xbc\xbc\"t\x9ce\xd3\x9e\x14\xfb\xe1\xd8Rt\x04\x14\xb5\x04V{\xdc\x99\xc0>\xa3\x9a\xf6OD\xcb\xe8\xd9\x15\x8e\xa8>W\nh\xb7\x1d\x80\x0c\xab\xab\xbb\xe5G\xa89nYV\x11 \xea\xbc\x80\x13$/\xd5\x05L\xe0\xf1c\x88\xec\xdf\xcd0\x00f\x9b\x1d\xeb\xf2\x03\xcb2\xcd\x8a\x05\x9d]\xf3\x82\xe2\xb9\xf6\xd0\xe8`\xa1^l\xed\xb5\x19]tW\xa1\x8b2 }\xf5+\x12E\xf6\x98\xa8\xd3\xa6\x90\xaf_\xa1P\x85\xb6\xbel\xb6\xe3\xcb\x8b\x0dcR\xf3%lCpP\x08&G\xf2\x19\xec\xc3\xa4\x0d\xc9A\x8c<\xe7\xae\xe8\x19f\xde\x8f\xf8\xa1\x940\xd4\x88\xd9\xa9\x1d\xf9f\xb7\x04\xb0N\xc9\xb27\x90.6\x1e\xbb%\x948\xd7&\xfb1\x1d\"a#;\xd7\x99E\xa3\x10J59;\x9b\xd98UU9\xfeTT\xe5\x04oH=y\x8c\xbf\xca\xacGa\xa1$\x8f\xf0\x87\"5&\xfc\x86\xd0\x97\xe7\xfcW5\xb9W\xe8\x04\x8a\x0bb\xd3\xa8\x9d\xa2i\xd0C\xc5\"\xb7\xeb3\xf1\xcd\xd1\x14\xfe\xbe e\x13\x88s\xee\x8f/\x92\xf3\xd8c*(w\x9a\x7f$\x89\x9bT\xcc6>@^\x18\xf1R\xf1\xa5\x88l\x1b\x93\xb3\x9c-\x9c\xdb\xa4F\\G\xa1%c\xce\x8c\x9b\xf8&\x1c\x0e|cHXX5I3~B\xc9\xbcQ\x9ed\xc3\xd0\xc6[t\xccXi}\xd8\xa0iE\xb3\xea\xc8\x8b\xe3\x9f\x96n\x99jWA\x05v\x1c\xf2(\xec4xK8(nJ\x13Y\xae\x8e\xb3\x19\x83`\xc2\x9bC3OW\xa8\xd9\xd0\x1f\xa0\x88\xc1\xa3\x8ag*\x15\x1e\xa8k\xe2\xf1\xfc\\\x82-E\xae\x94\x8d\x8a\x89\x97\x8d\x02P\xfa\x91<1\x8f\xa4\xb0\xa0\xd7l\xbf\xaaeU\xcf\x0f\xf2/\x1fq\x81F\xb2\x82\xb0\x0dg&\xa4\xab\xfarJ&R\xf0\xad\xf8\xf5C\xee\xb7\x80\xae8XXuX\xf80\xf0P\xad\x14=\x19\xd8G;C8\xb3\"^[\x99wcE/k\x92\x1e%\xe8EF\x9d\xf1r\xc7\xea\x13\x19\x7f`(o\xac\x98\xf5\xd5t;\x98\x9f\xc1\xcc\xb6\xb7\xb0\xff\x89\x0b\xfb\x8f1\x1e\xb0m*\xce\x10\x1623bc\x8c\xdc\xf4>\x9a\x8dv\xf1\xefm\x0c\x19c-h<\x16\x18>\xe4\xf5\xfd\x95\xb4\x91\xa9\x9c\xe1\x9e\x12s\xc0\x0d\xbf:N\xa5\x1a/Q\x88\x1e\x13\x15\x99f2\xe8t\x1bfl\xd4\x0f}|.\xf6\xd1\x84\x8dkR\xdd\xf1\x070\x92\xc6\xa3\xc9X\xec*&\xd8\xcd`[f\x1f\xc8\xd8\x9fg\xba\x11q\x99\x90=\x9e\x05\xbc\x8c\xfa\x8c\x1d\x00\xfc\xdf\x04\xff\xb5Md\xc1\xa5\xb1\x04#\x08\xf0\xcf\xd0\x7f\x08+\x06\x11\xec9c\xbb\xc9i\n\x95\xa1\xf3\xf1\xea\xf1n\xde\xe6N2\xc5 \x8aG\x18#\xc1\xc9F\xc8%\xee}60\xbc\xad\xa8\xb70\xba\xd1pda\x905\xff\xe6\xe6M\x8c\x03F\xd1l^SA\xb4\xd0\x8a5F\xb0 !\x9f\xf0\xe9-a\x08\xd9CX\xc2c8c\xff0J\xd0&K\x1c\xc3\x10\x16HA\x96z%\x89\xbcXwkAr\x8e\xc7\xbc\xdf\xf2\xb71\x81\x94\x9e\xbf\x93\x1f\xf2\x9e\xcf\x90v\xc1\x10\xe6-\x94 $\x83/A\xe6\xb1E\xc1(\xf6iEq\x92\"\x1b\x13\xfax\xd6=\x1e\xc2\xca\x87\x9c\x81c\x85\x8b\x86\xfff\xdcmaR8(4\x9a\x12z@\xde\x96.|\xb2pGf\xc2q\xc4(\x15\xe2\x87u\xe5\xc4>\x9cX\x85\x19\xb60'\\\xe8~\xfc\x98\x1d\xe8\xb6\x85a\x038A\xea\xba*_\xf7\xe1$%\xe1g\xf3W'BP\xdb\x1e\x82\xc7\xb7\x94\x0f\xdf\xc1 n\x92\x9d\x022b?\x8dN\xf4\xc2\xad~q'\x1c\xab\x1f\x0b5\"o\xa7\x0e\xd2\x8c\xad\xcc\x0e\xcc\xd8\x12M\xf8~x\xc4\xf7C\xe5\x83b93F \xc4\xfb\x92\xba\xec\x08\xaa\xb2\xa3\x8d\xa2\xec\x9c\x924D\xb5Fy\x9cp\xb6\x9bV\xd8\xf9\xb0\xd4\xed\x00\xc6q\x96\xeeU\x13\xd5\xbdj\xea\xea^\xc5\xc8\xc49\xf1r.\xee`\xa4f=\xba\xd1p\x1c\xff\xe1\x96/2U\xf3EV\"\xe8\xcb,k\xa1=\"\x04\x93b[\x99\xe0 Z\x01M\xe9{&\x1c\xc2\x8f\xc5\x9eMp}E\xa5\xbf\xdc\xcbxJI\xbe\xea\xd7\x9dR2\xe5\xf1h\x93\x0e\xe8\x91\xc0c\xe94y\xf3&O\x10Uz%'HR$\xe4\xebYn\x0c+\xf5\xb9-\xc5\x1cw\xab\xdeE\xa5\x9c\xd4Y\x9f\xb1My\xe6\xd4\xfe\x91\xbd}k\xa1\xc7\xa7\x9ce~M\xca\xfa\x8e\xecVg\xbf\x9b\xb3\xff\xf5\xf5\x1d_\xdb\xa1X\x94\xc2\x9c\xd5\x11\xce\xd4\xe0\x07\xd7\x94|U\xd5\xc3\x91bT1+!\xca\x14\xe1(\x02\xe1\x8f}\xb4\xdb\xf7\x8fy\xea \x9e;|\xc1\xed\xcb\x0e\xb9\xc3\x9d\xe6\xf4\xd4\xaaLXre\xc2\x92\x8d\xeb\x03\xf1xu\x9b\x0b\xe25B\xfd\x0c\xad\xffl\x970\x84i'\x90,\xbd1\xf5R.\xf8\xe0(3x\xfdb=6LIA\x0c\n\xff\xac\xe4\xf8\xd9\xd1\x1a\x9aT C\x9e\xb7I\x8f\xb7\\?\xd1\xa6(\xcc\x05y\x1cr\xedi\xf9s\x0f\xbe\x83D:n\xa2\x8d\x88\x1b+\x9b\xc9O\x0d\"\xac\xbcD\xff\xca|\x84\x8a\x05\xa55\xc3>\xf2\xfb4yI\xd6d\xfa\x9e|\xf1\xfc\xee\x94\x99\x8ev\x0d\\\x83\xdf\x9f-\xa2\x95\xc7:x\x1d\xf2|:\nn2\xa2\x9bVp\xb5\x8a\xb9\xaa\x933:\\\xa0\xf1L\x96}c\xd4%\xc2\xc3\x9c+1\x14\xe7\xde\\Q[0\"\x12J\xd1T\xa3\xbcTb\xcd\x8c\xb6\x99\x12\x01rD\xa5\xd0\x1f\x0d\xc6m\x8b\x9dr\xd5\x1e_G1\n\x9ej\xdd8\x08>?\xe1L\x9fK\x12Z\xb6\x90\x8bB)\xa2\x19#\xc90\xf1=\xa9,\xb4\")\x07\xf7\x0d\x17\x94#\xd2s2\x0c\x8c\x1f\x90\x93s\xcc\xbc\xfc\xae\xc5\xeb\x04\xdd\x95\x14\xaf\x93\xe3<#/\xc9:SJYH\x8a\xd7L\xe2k\xea\xf4\x8d\x81\xa6k{\xec\xde\xfc\xab?\xb7\xf9g\x7fn\xf3_[\xe2\xd8\xfeAl)b\x89:\x02R\xed\x9e\xdd`[\xbc\xcd\xabSi\x8e6\xb1?\xc0b\x8e\xb2xIkCgE\x99d\xf1\x91\xac\x7f\x86\xdeg\xb6\xbe\xdd\x07\x0b\xean\x12\xddx\x06F$\xd0U\x14as\x9a\x87Y\xab\x1b*\xa8\x1dE\xf1d\x91OIV\xafj_\xb4(_\xe8\xd6\xec<4\xb78 's\xf2\x8ed\xf9\x02\xf9\xdf8\x00\xc5\xa3\xf0c\x8c\x8f+e\xbbl\x11L\x85ZO\xebL\x01U\n\xd5\xa8g\xe5\xc8\x18\n\xafC\xf4\xb5\xa7fu\x84\xb1\xd8\x95\xe2\x9d\xdau~\\\xdf\xcb\x0e\x82wmR\xbd\xd4n\xca\xaex\xbbf1]\xb2\xf0nN\xac\xf2\x92v\xcd\xd4Z\xbeV^\xc8\xa5\xd0\xd6:\xb6\xf2*\xf7\x19\xba\xb9\x8ev[\xb2!\x01\x86u\xcaw\x95\x0f\x07\xe3@\xf9\xbb\xe1^X\xbf\xecfQ#\x19\x91\x97)\x8b\xb9\x1b>\xb2\x95\xc2\x15\xfe\x99\xc9L\xb0\x0f?\x1b\x11\xa9r\xd3D{\x9f\xb7s\xba\xad\x148\xad\x13\xdd\xb4;i1\xd3\x80\xb4\x1e\xd2\xe9RT\x99\x97%O\xcd\x85~\x0b\x19{(r\xd0G\x18&\x8c\xbe\xf6\xbc\xc4N\xaa\x15\xedp@V\x02\xe44\xbc\xab\x12\xa0\xa8\xc5\xd9\xa6J\x83R\xaf\x9c\x91\xfcXX\x04MD)j\x99\xb2\x9e(9\xcdY\xc5\xe1w\xe6\x14\xce\xdd)\x8d\x14_\x93V*\x83\x8ev\x82\xc0H\xf9\xd5\xfc\xf6\x99\xf0I\x8b8m\xb0\xbb\xa8\xa0o\x82\x95\x06I\xf9\x9dA+\x0c\x14d\xcb\x91\x02\x85\x0c\xdf\xb4\x0b\x00\x06uB\xa3*\xa2a\x8f\x7fl\xf7\\\xb3o\xf0Xe\xb1\xe2\xfan\x8f\xbb0G6.\x8br\xf6\x07-s\xce\x9c\x90<\x05\xbe\xeag\x00*w\xd5a\x9c\xa0\xeeE.%\x9a\xb6\x8c\xae\x8c\x07\x83J\x8dl\xd9\xd2 \x16=\xa1&@\xe4}\xdc\x19\xc0\x8e&\x855\x08\xee\xa1Nc\x8d\\A\x95\xc6V\x1a7\xb4|56\xae\x85;\x8c5\xbc\\\xac\x8f\x0e\xf9\x8f\xf3p-\xc5H.\x03\xd82\xc1N\x1f[d\x9b\x91\xf6\x8c7\xf7\xe0\xb4\xe5\x7fpU\xf9\xb5\x9c\xec\xb8\x19\xa3:\xaa\x19\xf1\xf8\xacH\xd4\xebv\xfcFxL-Y/[[%A\x8c,\xa7o\xf4\xe7\xb2\x03\xc5x\x9a\xbc\x80\xb0\xb5kJ\x0b\xf9\\\x87ia\nl\xde\x94gJ\x9c\x80\xf9\x8c \xf5Uy\xa1\x1d\xe1\x13\x8b[/H\xa9A\xe5\x13\xf0\x832\x91\xe2\xf6v\x00\x91\x87~ \x1c\x02hn6\xe7\xf9dS\xad\xfb\x84\x81\\<;\x1f\xe1\x04\xa6\x1a\x1f\x91X*/\xb6\x03\xad\x03\x9b\xe1\xe8\xfc)q.o\xe5F@\x06eT9\x92\xc4\xfe\x854\x84%.\\ \x08\x9bX6\xda\xb5X\xcd\xe4\x85\xd9,\xb5\x89A\xd5\xab\x8a/34\x15*9\x81\x9ecED\x91[\x1d\x91gfd8\xc1(\xf8\xe8\xf9\x1d7\xdb\xc0\x17W\xe2G\x0d\x11\xa7l\x86\x9d\xdc\x88\x98\x101\x80[\xe8\x83\x83\x81\x88\xe8\x93#\xde\xff,*\x98E\xady\x93\x18\xda\x1c\xf1:ff{\xc2k\xa4\x90\x86\x80\x1cF\xc0 \x81\xcd\x06r\xf6W^\xf4\xc8`\xd2\xa7 W\xa1+\x07\xb1\xe7\x97\x90\xd2\x0fJ8y\xe7\xb0\xa3\xc3\xcc\x0c\x86C\xee\xe9\xe7\xb1\xcd\x96 G\xa4]\xd8\xd7V\x9a8\x13^\x8d\xf6cg\"Y\xcc2\xdc \xc4\xcaZ\xd2\x18\x1a\x96\x06\xc4\x00\xb6\xf0\x94\x8a\xa4Y,,\xd2\xf8x\x93\xfaY\xe1p\x0c\xcb\x0c7\"\xdc\xb4L\nDDQE\xc9\xa4m3:\x89\xe9f4~l~\x00\x93o\xd3SEV\x1e'*\xb2\xea\x95\x8eY\x06B\x87\xd6\x81J8Nu\xfd\x95S\xc3\xa2\x03\x92\xd4\xd7\x12E\x9cqW\x02\xe3\xf3I+1\xbe\x12\xcb&|o7\x1b\xd8\xc2r\x90\xf9\xf66<\x82\xa4\xdcl\x13F\x83\n\xad\x9c8\xc7b,\xf8\x80\xe7X\x84h3\xe1\xe65\x031\n`\xa2\xa3G\x93oT\xd6 \x9b\x1e\xeb\xdfi\x89\xecz:\x896J\xabM\x15\x9fy}\x1c\x96\xf7\x9a\xcfR\xb9V\x0f}\x88ZOK\x06\xaf\xed\xed\x0c\x1e+(\xdfv\x12;E\xbfC[\x04<\xbb.\xedj\x024P\xb5N\xa1\xe0\xaa1 \x96\xd4\xe2Q\x0c\xb0'\x01\xaf\xa3\x13\x88'Oe\x92\\\xf4\xc6P5\x95]\x14\x04U\xac5\x1d\x98\xbf\xbb\x1e\x98v\xb2}M<\xb0\x99\x8c%.{\x84x\x16\x97\xf73\x11da\xa3S\xed\x88n\xe1\xb4'\xad\xa4\x8a\xa7\xe4\xc6\xd3\xb2\xceuO\xfc\x92je\x0d\xb6;\xb3\xb3\xdd~\x00\x9a@\xcbk\xe2\xb9\xbf}Y\x92\xd4e]\xba0\xf7\xdf~\xdet X\xb8\xc9q\x914\x89\xda\xe55MZ(R$\xb3\x0e\x86\x82V\xf8U\xd6\x1f)CT\xa3\x0cQ\xc0\x8f\xb0\xa8\x8d.\xb4\xcb\x0d\x8b\xd2\xeaa\x7f\x99q\xa2\x0b\xac\xe47\xc3\xbfX\x07\x9c\xcb\xcb*x;\x13\xf1L\x16\xf6\x1e\xce\xe7\xd1\x82\x80\xd1)\x0fTu\x00\xda\xae\xd4\x99'\xd8G'\x9a\xe7&$\xfcz-\x86\x8fo\xb6\x04X\xf0\x17\xe9\x94\xa1\xce\x91\x18@1\x1b\xeae-\xb4\xe7LT\x0d1oeve:\xca\x16\xb5(\x10@\xe1\x9e\xb7\xd0\xf3j\x02\x8f\xb0`\xcdM\xc8=\xac\xda\x87e\xf2'\x18\xa8\x0d\xfb2M7R\x84X\x94\x03HPR\xf4\x0bIbk\x17\x8bs\x9a\xf1\xca\xac*g\x0b\xcb\xben\x96P\xfa3L\x19\xa9Y\\\x03\xb1\x8a\xa3\x96B\xe7\xd7F\xa5\x04[\x958))\xa8\x93\xc9\x04\xe4\xb9%R\xcdw2\xcfN\\\xe9\x0d\x88^RA\x01\n\xf7\xeb\xd1`\xcc$T\xd4\x10z\xa1\x8c\xa7@\xecb\xc7h\xeeM\xca#3.\x08G\x1a\xf0\xf3s\xd2N\x16\xd9\x15r\xe7\xdcD\x94F\x9b4\x96\xd7\xda\x82\xf0\x8eJ\x90\xac\xa3g\x97\x19i\xdb(`\xdb\xaa]#C\xdb\x81\xa2\xba\x99\x99~\xb1RT\xee\x91\x89\xd1\xaa:\xf9E\x12\xdc\xd0\x986:2SK\xbe'\xa5v\xa3\xe2 HZ\x8a8 \xb8\x8fR\x1cy\xc4K/\x1e\x00\xffP\xb8\x97\x11\xa3\xfb`\x91e\xdaxD$\xfd,I\xa9\x9b4+>!\x1e\x1d\xdd\x1e\x07\x10\x8fn\x8f\x11\xcb\xe9ho\x0c;\x10\x8f\xf64\x19\x82\xfd\xb2 y-+\x83q\x97\x96;i\x08{\xcd6\xeb\x15\xfal\x0d1\xd0\x8f\x06\xba\x81q\xce\xf5\x85\xa8\xf1\xc1\xdd\xbao\xf0_?z5\x85\xa0 \xa7^Zq\x8a\xfb\xbb(x\xe5b7\xfa6\xed\x82,u\xe0\xdcRG\xe0\xcaK\x02\x99\xad\x0f;\x99\xe0w\x0fC\xd8K\x9fK\x86\xef\x96\x03\xff\xea\xfa6\x07\xf6\xbf\x03g\x88\xab\xd9*\x80\xa1n\x02\x973\xb9\"\xa0\x04\x16\xd8\x00\xc2\x13\x90\xf4\xb3dI\xae\xd2\x01C/K\xf3\xa2\xbe\xd4_\xc8H\xc9\xfc\x989\xe6\xc7\x14\xce\xbe\xa2\x1c\xc5U\xa1\x88\x03\xb4\xcd\xf2\xfa\x05\xe2\x1f[s!p\x13\x0b\xaf\xc9A\xfb\x93$\xceh\x9aOP\xb3\xecF\xdf\x7f28zGE6\x1b\x1e\x81\x84%F\xe8(6j\x0d\x810\x01\xc9\xcd\x818mI\x9c\xcc9\x88\x82\x04Zs\x8aq\x0bv\x14g4\x8c'$\x99)\x15\xcf-N\x11\x089D\x8f\xea\xa7\x95d\x9f\xa9gR=\x17MX9tv\xc5\xa8\x96j\xd7\xb2\xe6e(\xe5g\xb2\xce\x8c~\x89\xf2\xdar\xe3\xca\xd4\x8b\xa6k\x87\xb7\xd8E\xb4\x11\xaeN\x9d\xc8K\xcceJfQL~N\x93\x15I\xe9Zp\xbe\xee\xad\xb0\xeb\x94PE\xb4\xec2\x06y\xa9$\x88\x87Mvj\xe2\xb2\xdd F\xbd\xb2\xcax[\x8fo\xdduJk\x89\x98\x03\xe8=\x0d\xe38\xa1\xacuHb\x08c\x88\x8a\xf4\xbc)\x99$\xe9\xb4\xdf+H&\x8f\xb6\xb3\xb0\x98\xba\xab=s\x9b\xbc\x0c\xd1\x08\xf5\xeb\xb2\x7f\x12\xc5S\xaf\x8c\xbak\xff\xec\x12&!\x9d\xcc\x01\xc1f\x1f\xd0\xa5']\xd3\xe5\x11\x91\x0b\xfd\x04r\xfdq\x88\x81\xbcK\x93\xe5aL\xd35\xd7\x95*\xca\x9fv\\\xe9V(\x81\x0b\x7f\xc3F\x95\x04\x87\xfc\xda\xa4B\x14*\xdd\x1a\xcd\x08%!\x11KT\xfd\xc8\xbc\xacp\x00\x1f\x88p\xe5\xecPmA\x1e-D\xdd\xd9<\xef\x85F\xa2AHF\x99BH\x87\xf0\x9aT\xe1;\x9a\xca\xea\x06\x15\xa8\x17u\x0e4\xfb6\x00\xe2\xbd#\x01\xbc\xf0\x03xw\x05\n\xdc\x14\xfc\x90\x02\xeb0\xa1\xd2|-n\xa0\xb5\\\x1ao\x9b\x17M\xb36\x8c\xfa\x91\xf7\xe4K'\x9a\x81\x8d\xcb/\x9bt\xe1]\x15nN\xa1BgJEf=\xbe\xb1&>Jr\xb8\xa5K6X\x19\xa3L6\x80F\x0d\xe7i\xaa\xcd\x88yJ+\x8798\xfc\xd2o\x04\x89\xd6\x80\xc01\xb7\x15;T\xb2\xa8\x07\x02\xa3\x02\xcf+\x87M\x070\xa4W\x01C\\\x03\xc32\\i\xf0\x15\x04\x18\x1a\x85_\xde}\xdb\x19\x11XB\x94\x9a(Y\x1e\x13\xd5\xc9+\xe6<\x07\xc7e\xea\x11S\xcc\xd2%#P2\xdf\xf2?y7>\xcf\xd2S\xf4`T\x9d\x17\xcdG\x81\xc8\xd7\x1c\xc3>/\x06\xa4\xeb\xcao%\n\xdd\x8e&<\x1eT\xb0\xf8\x16\x08\xca\xe3I\x7f\\\xc4U\xddS\xc3\xa0aD\xdd:\xd8\x8c\x8b\xea\xa8\x90\x97\x96\xa1\xd8\xea}Q\x88 hP\xe1JCT4\xf3U\xc0\x82\xf8\xe8\x17V\x98Wt\xcba[\x8a\xf2$!\xde\x1b\x12\xc0\x0d?\x807\xeaR\xe9\x02\x01\x1d\x89x\x11\x0d\xd8\xa4\xe4o\xbems\xb5R\x1a\xf3\xfah7\x9d3o\x86;\x0cA\xee\xca\x92ig\xea\x86\xf7\xdf\x84\xb0\xd7\x82\xa1\xc4\x15C\x89\xc4P\"14\xe5\xa6\x10\x81\x97N5\xc3\x88\xf7\x8a\x04\xf0\xa3\x1f\xc0\xabo\xe7 ,\xc8\xf7\xeaZ\x90\xef\xcf\xc40\xe2\x8e_\xda\xc9\\\x1b~\xfd\x87\x91\xa8\xc4\x9f\x8e\x88\xf4Lp\xba\xcfT\xe8\x10!\xcc\xb4\xf1\x10\xcdu\x14,D\xbd\x9fg\xff\x95\x88\x84.1\xa6\x87\xec\xfa\x89x\xc6\"z\x8a\x93En}\xab@W,\xd1\x8f\xc2\x00:vr\xb1\xb5\xbc\xb9\xcbo\x1a\xa4Xv5\xf5rZD\xd7\x02\xfb\xbf\x06\xd1\x1d\"C\xdd\xf6\x02\x14\xe1\x95\x15\xb7p\x8b\xf3\xa4\\/\xd2\xe6e\x89\xde\x95\xb6\x11\x02G\x0e]\x18\xa0zI\xde%o}S\x0c\x1e\xf7r\x04\x07<\x91\x0bG\x89\x14Q\xa2\xbc9\xe07\x07\xcd|\xf9\xeaepYt\xa0 \x95s\xb8\x9a\x86\xe0\x9d\xf9\xd1+\xf3\xa3g\xe6G\x98\xa3\xcaK\xe3\x00N(\x13-b\xe5\xcdoT\xb0\x86\xb1\xe0A\xb7\xa1g\xd4\xb0V:\xec||V4\xea\xec\xf3\xb7\xe7qi\xf2\xb1w\xe6\xa8L\xe0i\x9e\xe6Eut\x1b\x9aW7oep#\xaa\x89S\xae\xcc\x85\x89\xaf\x07\xe5\xdfRg\xa1\x89\xd9\xac\xcf\xc4I\xf9[J&Z\x95\x15\xef\xff\xe6Me\x00\x15}\xae~\xb2R\x99\xa0\xda\x06\xcc\xd3\xec\x1f\x93\xe5\x8a\xaeQL.~\x0c!\x8f\x85\xa8\xfd\x1bm\xa6<\xadM\xd5Qc\xdc\\\xb4\xd2J\xcd-\xd4\x7fS\xacZy\xfc9N\xcec\xf8L\xd6\xd0\xfb\x1bl\x03\x85m\xf8[\x0f\x92\x18\xd8/\x89\xc7\x06#y\x05z[%\xf8D1\xfd\xb2\x16\x87\x16)\x1c\xf4\x86\x15cBu\x892\xa9\xd7j\xc1\xadJY\x08e4%\xce\xc1~\xb9\x0e\xcd:\xcc\x955pT\xae\x1b7\x8ey\xa6\xc48\xfb({\x8f\x9a\xf8I\xdcT\x01\xcd\xe2\x00\x16\x0c\xc7z\x7f\xff\xfb\xf1\xf1\xd1\xeb\xd7\x1f?<\xf9\xe1\xd5\xe1\xf1\xfb\xc3\x0f\xc7\xc7\x7f\xff{\xaf\xe9\x08\xb2bog\x0eJ\xa3y;\"\x18\xaa5\x91z\xb5& \x05Y([j\x88\x91\xcd\xe5\x87\xa6\xf4\x8eg\xa0^\xae\xe8\x9a\x87O\x17`tSDL\xdb\xf7bU\xc9\xb5\xb2\x04a\x94\xd9\xeck\xe5\xebb9-\xca\xb3z\x97kJ\\\x93p\x9fY\xe9\xd2\x0c\xf3\x0ex36\xdei\xec\xe9L5\x86v\xd7\xdf\xa0\xd2:\xe7*\xad\xd3\xb8\xd4d\x9d\xff\xbfM\x93uj\x87_\xa1\xee\xd3\x14XT\x7f\xad\xe2\xd1\"\x96\x0et+E\xa9\xb5*\x95Z\xab\xaa\x82I\xfe\xac>\x10\xac\xc1*VuV+\x17\x85\xcf\xca\xa6\xf0Y\xb5)|V\xb1\xdc\x870\x84\xb3X\xdc`[\x11Q2\x00\xe2\xadcF\x9c\xfc\x00\xd6\xd7\xa7\x11Z\xff)\x1a\xa1\xf5uj\x84\x84\xff\xbdM1\xb4\x8eK?}N\xb9O5\x94{\x19\x07p\xcc\xf6\xc9\xda\x81\x16\x9ft%l\xc7\xff!\xc2vn\x85\xe6\x92\x13\xb6%\x1b\xefI\xec=u/\xbby\xf1\x0d\x84\xed3'l\xef\x15\xc2\xc6n\xf5\xf38\x9bG3\xfad\xb1p\x8d\xe6\x7f\xef\xac\xe8~bWt\x1f\xc7\xa5\x83\xed\xb1\xba\xd7\xcecqC\xec\xb5\x13\xdck\x17q\x00\xe7\xd4\x0f\xe0\xe2\xfa\xf6\xda\xc5u\xee\x8a\xf74\x9c|\x86\x11\xdb\x10\xe3\xe6\x86\xb8\xb8\x82+H\xd5\x18?'\xe1\xb4\x89\xcf\xa8\xb7\xa2JRn\xea?\xe4\x89\xd7\xe9\xce\xceC\x1f\xbf\xe7^U\xe6\xbd\x00\x07 \x92\xd0\xe8\xe2\xfe*#_\x11\xf2\xb9\x13\x80\xd8\xa8K\xc3!\xfb\xa5\xc9\xde\xd1\xe8%\xcf\xe6m\xbd(9\xbe\xe5\xfa\xbai\x1d\nM_\xe1L\x82\xbb\x7f\xbb\xd1N\xa00\xc0l\xe0\x01\x02\xb3\xfe\x16\xec\xc0\x80A\xfc1W\x1b\xee\xec\xf8\xf8\x99\x89/\xc0\xcc*E\x1b\xa3\xd8\x90\xfb\x90-X}-\xd8\xa5I\xb4\\\xc5GC0e\xc1i\xe3z(\xf1V\x8d\x8a\xa1\xfcn\xad\xfc\xb9p\xed\xff#\xd6\x8b'\x8d\xc5{\xc2H\x91\x83`\"\xd4\xc9\x98\x1f\xda\xa3\xbe\xcf9\"\xfb\xfa\x959HZ\xa4\x16d\xc0\xf5\xd0m\xd9T\x05o_\x84\x07u\xe0\xd0\x08\xcf\x92gB\x01(\xd1\xc0P\xf5\x18\x8a\xf5o\xa6\xce\x87\x06\x19\xc5;E`\xaci\xfdIm\xfd\xe3\xab\xae\x7f\xd3\xfd\xba\xb1\xfeIke*\x15e\xb3E4!\xde\xc0\xde\xa68\xa6\xba\xb4\xcb\xd0\xd0Q\x1d\xa5\xeb\xca\x05\x83\xeb\xdd\xe9N\xd1Z\xeb\xdd\xa7\x91\xac\xae2\x8b.V\xa6o\x8d\xcf\x16(U\xc3\xa0.x\xc5X\x11;\xd8\x18\x92\xb8\x1c\x99\x8c\xa8|\x16\x8e\x1e\xc5`]\\\xc1b,.\xa2V\xe95h\xb8_{\x95\xa6\xab\x16\xaa\xa2\xa3sZ\x1f}\x99\xa6\xc7\x18\xe3W\x9cLi\xe5d\xc22gQ\x95d\xb1\x83\xe6\xa1\x8fw#\xfb\xe9n_\xc4\xb4\xb6\x88\xd1\x95\xd6\xef\x8fXWa\xba\xb6\x86\xdd\xd4V\x85.\xa9\xa9\xb9R\x10\x14\x0e\xf0L*\xa8\xbd2\x99\x8ea\xc8\xea\xcc\x06\x06=\xd4\xc5\x95\xb5\xa0\"\xee@]\x92\xf2hQ<\xbflH\x11\xf3=\x97\xd6\x10!\xad$\x13Le0H\xac$\x13\xc4o\xd2\x16&\xd0i\xb2n:R\xa7\xd9&z\x1db9S\xed\xd9\x97\xba\x9d\xdc\x8e\x91 \xad^\xff\x92\x9fH\xdb\xe2\x07D\xbf%\xa0\x03\xee\xd9\x8f\xcb`\xb2\xfa\xeag\xc8[je\x1e\xda\xb2\xf3Y3\xf3\xb9D\x05\\\xa0\xd6\x15\x85\x9a!\xbc\xd7H\xef\x87q\x00Otz\xd7\x0fO\x9e\xbe4h^\xdf\xb2\xf7/\x1c\xa4\xfd?\nw\xbd\x96\xfc\xa15\x8f=kF\x99\x92\x19\x8eTN8\xaa;\xeaE%\xfdK\xf9\xaf*upK\x19\xf8\xd9z\xea\x1er=\xc0!\x03\xc8\x1f\xb1\xd7pO14z\xd4..\x16ho4K*\x87\xd3\x08ut\xec\x9f&J\x18!\xa9\xa6\xef\"%o\x1c\xfb\x01\x94.\x93Jh\xc4\xfb\xf5\xf2$Y`\x85\x04\xdb\xf3z[\xb4\x06\x11\xf5\xd7\xdbx\xf4\xa4P/\xbeu\xd1\x06\xbe\xb5i\x03\xdf\xb6i\x03Y\x17\xaam\xed\x8b\x9aE%\x80\xb8\x7fT\x12\xc8\xaf\x01[\xa6X\x97\xfeK\xa4\xc4vH\xf3\xf5\x8cz6V\x04\xc4\x82S\x91\x1b\x97g\xda.\x8f\xf6\xcdFk\xa3\x87\x1acP\xe6{0\x98\xde\xac\xa6m*\xb0GOc\x1a+\x88w\x9b4\x81&G\xf1\x94\\\x90\xe9{\xf2\xc5\x010\n\x89\x7f#\xa2\xce\xddz\xf9\xe9\xbd{\xeb\x08\x1cm*l\x17\xcd\"W\x87pa\x84p\xefn\x1d{!\xa7,\xd2\x94]\xd2I!\x17;\xf6\xde\xa9\xdb\xec:\xbb\xed\xbcI^u\"\xa6\x9d\x9a\xcf\xaa\xb3R >\xce,\xac?/WY\xaa!\xe4\x9c\\ \x052\xae\xee#\xbc\xb86\xd0\xbf\x8a\xb2\x0eK\xbe\"\xd7\xd5/7C\xb8\xf7\xdc\x1b!\xc7r\xb2 \xe3\x9eK\x0f\xa5\xa9\xc3\xb1\xfc\x85Y\xbb\x04\xdb&\xc6\xf2\xba\x9f\xbe\xf2\x12\xc3\xcc\xb91\x8f\x97\xd9e\x94?\xc5\xb0\xc7}\xce\x14\xc2\x01\xe4\x98\x92|\x1fB\xea!\x7f\xd8\x8f2\xc1'J#\xe0\x88\x8e\xb5\x94[\xbd.}wOo\xf5*\x10\xc0\xe2\xf5\xad^\xa6\x8a\x1dP1\x16D\x0d+\x8f\xfd\xabA\xed+\xfb\xb8\xcfD%\x84h\xb4\xebP\xe79)\xed\xad\xb8\x08\xa1\x97\xa0\xc7\xae\x0c\xc4\xcd<\xa5\xd0j\xb3\xde\x96\xbc\xcc\xd9W\xcfD\x95(Q\xfdBW\xd7X^\x92\x92ci\xe9!L\xeaT\x14\xc7\xc4$N\xf9T\xd2S?\x90\xf7f\x8b\x90R\x12{[\xbb\xc2\x12\x83\xdaEM\xd1\x13\xebV\x00\x01\x1c%\xcd\xa8\x13\xba\xc8-\xc4\xfd\xa0\xec\xc0\x87f\x1fJ\x85X\xd86XN\xe4e\x06\xf8%\xaf\x8d\xd6,g\x8b\x0f\xa5\xfaV\xe3\x0e\xed\xc6\x8eH\x8f^\x97\xb4\xc9*\xbbV\xf5 v\x897\x98\xda\x12#k\x0b!4n\x91\x98\xa6Qe\xac.CU\xf4{\xef\xdc\xba9#\xe9\xda\xf1Lq\xe4\x82cK*\xf2\x16.8\x0d\xc0V\xf2\x13\x8a@s\x8e\x03\xbc\xd6\x11~\xa1\x14Z\xe3Z\xa2\xad\x81\x01\xf8uG\x12\xd0\x03\x86\x13]G\xc8\xd4O\xae\x1f\xd4|\x82\x9a\xf0'0\xf5\x19Ok=\xbaT\x8db\xc0d\x9fbNT\xcf`\xde\x00UOz\x80 M\xf4\xe5\xc15\xc3\xe2Z\xa1n\xb0\xa8 KP_q\xeei\x89y\xbb\x89\xaf/S\xa3\x19\x08\xe3@\\6o\xbd\xef\xc2\x92\xc2\xe9!\x1c@\x0f\x19\x1f\xd8\x87^\xd03c2#\xc1=\x8d\x1eU^\xdf\x82\xe96\x1c\x8fE\xa9\xfe\xad\x01\xba\xacn\xa3\xd2\x14\xffE7\xa3-YBJ\x99\x14\xaei\xe1E\x83gN\xaf\xc9Y\x82\xd8\x01N|\xdbg\xb2\xfe\x06\xf2\xf3\xd4iE\x97\x159\xd4\x01\xad\x8a-VM\xd9\xe9\xd4\x19?K;n\xb0\x00\"\xeb\x02\xd7p\xad\xe1\xa0\xf2\x08\xf60?\"\xc3\x14\xd8\xe7\xf9\x90\x1a\xdbAU\x03`\xcdZ\x1b\x01\x84\x03\xf0\"A\xe5\xb09_\xb4K\x8b\xd2\xb7\xbcb`b-\xc8\x9c\xba\x83\xec]t:\xa7\x1d\xe1& \x93\xca\x08\x95\x86(;}\x12\\\x8f0\xbd\xa7F\xbb;\x98\x06\x8d\xbd\xb8\xe3n\x81Tj2\\\xa7\xae\xd0\xb8|E\x0c\xfer\xb5C\x82q#\xddz\xe4yYx\xac\xdc\xbb\x18K\x85\xe9\xb2`\xe8\xbaJ\x9djL\xd4gf\x0c\xc8\x01}?(u\x7f\x03\xad\xf9\xd9\xa9\x97\x93\x9c\xbe\n\xbb\xa8\x07\xf8\xbeF\x0f\x99\xdd\x00v\x06N\xbdD\xd9\xe1rE]l\x0c\xa2\x17\xf5dR\xe4\xf4\xba\xe4\xbe/\x96\xb1\xca\x8c:\xf0\xa2&#\xa4\xd3l&I\x1e\xd7w~\xcb|\x9ex\xb4T%\xf1m/\x04X\xfeq\x07\xbd\n\xf6\xfe\x83+{*\xfaw\xa5R\xa0P\xaa\xaf\xd4\xf3K\x83\x94-\x03\x9eD\x0d\x1d\xf1nc]\xf1{\x917\xc1+\xeb\x94\xf3J\xe2lW\xaa9\x8f\x9d\xa46E\xe6\xd2\xb3\xbb\xf2\xb2\x94R\xc1\xb3@5\xb7\x19*\xe4]\xaa\xe7\xad\xcb\xea\x91/y\xb8\xe8\"l\x9d\xd1\x82l8\xb5/\xb2f:l5\xd5\xe1T\xbf\xb6\x18\xa8\xd5?\xc6ty\x95\xe2L\x94\x96\xf7\xed\x9cb\xb5z\xeb\xcf\xb1_S\xb5Z\xcf$\x0e\xc6A\x0b\x1d3\xc3@\xa2\xa0\x1b\x05\x8e\xaa\x94\xb7\xd5\xfc\xa4P\xb0\x00\x12OG\"\xe5e\x18\x7fgQc\x1ev\x913\x90\x0e\x89\x84\xcbK\x1eC\xb0t\xec\xe5\xa8\x0b\x0d\x97\xfdp\xaf\xd1.=E\xd9\xfb\xfc\xc4\xb1\xc0g!\x03\x0eM>aE\xa5\x14nu\xe6<\xba\xa2\x13r[\xda\xe2<.\x12\xe3t\xc8\xa7\xa5\x9f\xe2\x8a\xf1B]&\xe9\xd9f)`\xa6\xcc\xd2/n\xba\x9fj\x9f\xc9\xfa\xed\xac\xc3\x90\x8aC\x8d1s\x9d y\x0dFB\x1eq\xee~\xc4W\xb42lW?mH\xa9.\xdd.\xba\xab\xd1\x1a%\xbf\xfa\xc8\xcf\xba\xf7\xf7\xf2*\xebb\xe0\xbdq\x8d\xb5\xb9\xac\x9a}/\xc3\x8b\x0e\xbd\xbe$\x9dT\x18\xcb\xf0\xa2\xeb\x99\xfa\xb2\x92\x8f\xc8\xa9\x137\xa3Yc\x06p\x00ob\xee\xc2\xf2\xd5MPZF\xf1\xd5\xa7\xc3\xbb#\xbc;\xd7\xb9\xa5\xa43&jC\x1eA\xdf|\xf69Zu\x80\x9d\xd2\xfe\xeb\x90\xce\xfb\xcb\xf0\xc23T$6tV\x17\xbe]\xa5\x04\xc3\x1ecMzT\xb9\xe3<\x90_\xe7\xd1\xa2\xa3\x99\xa1\x18\xcc\xefW4l|\x8eV\x1fc\x1a-\xbau\xcb\x81.\x87\xdcM\x05\xc5\x13\x82u\xeb\xafi\xe5\xd0d\x06\x03}\x7f4\xfcL:,/\xad\x18 \xae\x80R\xac\xbfkF)\xd6dw\x94b_}\x0bJ]E\x92\xf8\x87\x13w\xab\x940\xfa\x18\xa3\x9a\xb7\x92\xbc\x0d#+[\x18^\xc9NS\xa3vY^L\xa4\x8b\xaa\xb1yJ\x81\x96J\x18\x08vlo\xedL\xd4\xf3o)\xfb_0n\x1a\xc1\x87\xa2J$l\x9b\xa1\xd2L)\xfd\x14\xdf\xde\xbc \xdb\xdb9\n\xa9\xa2AC\xa1ry]\xfa\x01\xe4\xc67.\x03P\xcb \xfd\x17\xadJ\x92vY\x16Z\xf1\xc6b\xdf\xd9\xe5Zv\x85\x16\x8f\x12y\x89q:FY\xaa\x17\xfaN\x85\xc5L\xdb?\x00\xf7\x88G\xf5\xb2F?\xaa\x97!VB\xbd\xa4\xe9&o-N%/\xae\xc3\xaf\x14\xa9\xb2x\xa9\xcaKF4R\x11\xc3\xdb\xfa\x01\xbb2\xe1\xac\xea\xf6\xf6\x04\xdf\x1e\xb4\xb8\xb6\x82n\xafM\x02\xc8P\xe3y\xc0H\xdbp\x08\xef\x84\x98\xf3\x9cad\x86/\xf04\x7f\xa1\xf0\x0c\xf9/X\xdc6\"`\xa5\x00\xda\x87\xdd5\xaf\xec\xe0\xb9*SQ\x1cZ\xdd\x98\n\x19C\xd0\x91/\xed.\x86\xcd\xc3l\xfe4\x99vpt\xa1\xf32\xbb\x00\xd6e\x9a\xab\xd9\x06\xday\x04(\xb6\x17wP\x1e\x0ea\x00\xb7`\xb7\xd8h\x16\xd2%\xcd\xa4\xb3V\x05\x9f\x9b+\x7f*\x8a\xdf\x0e\xf4Uo\x8b\xd7\xf8\xc0\x9c\x16\xbf\xf6\x0d\x1b\xed{\x14\xd2o\xdf\xb9\xbd\xf7`p\xff\xf6\xdd\xdb~P\xdc\x86G\x8f`p\x176@\xe0\xf1\xe3\xc7\xb03\xb8\x1b\xc0\x9d{\x83\xfbw\xee>\xd8\xfd\xbe\xfe\xdem\xe5\xbd\xdb\x01\xdc-\x9fc:w\x8f\xc06\xdc\xbe\x7f\xef\xce\xde\x83\xbd\xc1\x83{\xb0a0\xfd\x17\xdb\xd2\xff\x12\x9f\x0d\xee\x05\xb0\xb7w\xe7\xde\xfd\xbd\xbd\xbbE\xf3\x87\xe2s\xec\xa6x\xf3v\x00\xb7\xf7\xee\xdd\xbbs\xff\xc1\x83\xdd\x07\xbe\xda\x84e\xcby*\x7f\x10c\xad\xcb\x83\x8eP\x83!\xdc\x1e\xc0w\x90\xc26<\x8f\xbd'\x147\xcd\x13\xea\x11\xdfg32w\x0e\x8e\xbbS^\\+~\x85^\xaa\x93r\xe9\xa6\x98\x11v\xd4\xdaA\xb7\xc6\x1d\xdb\xf5\xb5\xe5\xac\xa1 \x88:RX\xb9SW\x06\xb3\xbd\xf8\x9a''Sr\x01\xa8o\xbc\x8eG\x0b\x19\xe0\xfd:\x1e=c\x7f\xbf\x16&\x8b\x8c\xdd\x12\xa1\xa3\xfc\xb6\x08\xac.\xee\xab\x81C0\x84W1>\x89\xe2l\xc5s\xe3\xe3'\xef\x93<\xad\xe6\x95\xd1\x81\xac\xa6D\x12\xee\xad\xd5\xd9a\xeb\x93y\x18\xc5\xbcma\xcb\xe4\xb7\x93\x98\x86\x11F\xa5\xe3\x10\xb8\xee\x12c\xc4S\xdd)9[D\x1dB#\x0b\x01\xe5+1\xae\x84N\xed\xb3:l\xb8\xf7\xbbZ\xff\xcdT15\xcb\x02V\xe1\xae\x93a\xb5\x90&\xa4\x93\xc4( \x1a\x9b\x8bO\x03p\xa3\xaab\x93t\x14\x1a\x97\xe1\xeae\xd5\x07\xd9\x15FW\x00\x02[\xf7:,\xda\xc4\x8c\x06,x4\x82\x05\x08\xd8\xc9Uv\xeb\x87\x18\x93\x9b\xb4f\xeexj\x06\x92<\xd5\xaa}\x19\xda\xf9\xb9\xb5\x9d\x11 \x80\x8e\x9d\x1a{g \x87\xf5\xb3\xb9e\xb3mQ\x97d\\\xd0\x84\xa7aXo\xaegX;\xd7<\xacW\xf6a\xf52\xa4\x81\x15\xe3\x07\x1c\xc0O\xef\xdf\xbe\xe9\xf3G\xd1l\xcd\xd5\xb6\x82Z:\xe6\x16}f%\xc0\x87\xc6L\x9e\x86\xe6\xbe\xb6b\x10\x85G\x05\x07G\xe11\xfe\xbd\x83\xec\x9cS\x07\xcf\x1d:`\xac\xcf6\xec\xdd\xbb{\xe7\xce\xed\xbb\xdf\xdf{\x00\xdb\xe0Q\xc6\x90\xdd\xf3\xf9\x9f\x8f\x1f\xc3^\xf3\xf4\xad.\x94h\xedCT\xaf\xc2h`\x95\xcb\xe5\x95|\xb3\xad\xaeu@J\x1b\xdeV\x82\xa5\x00\xf8\xba\xf2\xd0R&\xa2G\xbe\xaf$-\xc5f\xc5}k\xcb\x97\xac\xf7\xc0\x96GC\x85\xa8\xdel\xe7\x0c\xd2\x80[\xee*1~\xd8\x7f\xeb\xe4\xdd\xed\xa1W\xb0\x9f\x15\x90\x8d\x18ds\xf8\x1f&;\xb0\xad\xc7p \xa9\xb8\x00c\xcc\xef>\x7f\x07\x0e\xe09\x9b{\xce\xd3\x91\xa2\xd5F\xfe\x8cd\xca\xd86\xf0[\xad%\x86T\xe5%\x95p\xde\xc6\x0b\x12\x9e\xb9p^\xd2,7b]\x8c5\x87\xb2oY,\xb6/op\x02 \xf5/\x01\xdc\xe8'3t\xa65~\xc6\xf3\x93(\xde\xf9\xd6s\x96\x14\x1b\xdf+\x88\x81\xb8\xc7\xe8\x80\xc8H\x13\x94\x94\xc8\xcd\xc7\xa9\xab\xcb\xdd\x92z\xbbj\xcaj\x97>\xae\xe0_\xc7\x0e|\xc7\x08\xd5\xebv\xefq<\xf9\xbf^I\xafzC\xfe\xf1,\x0el\xc8\xe6<\x86_#:w9\xa7\xa4\xcc\xa3\xf6b\xc77\xc6\xd3\xc9\x00\x81\xe6\xf8M&\xcb\xca\x9dK\x9fQ\x842=\xec\\\xea\x1b\xd4\x9bE\xdd\x96#t\\o\x0e\xbf3\x8f\x85\x18\xc4kA\x0b\xb3\xb2\x93\x9cv\xd5|:\x9a\xaa\xd3p=\x9b\x0d\x9b/s\xb89@;Q\xf2l\xf3\x12\xda\x15+\x81\xfaX\xb1$\xa8\xb7+&\x85\x17\x81\xaa\xa4\xf5\xf1\xde\x8d\xca\xf2\xf1{?V\x9a\xe6\xf7N\xa8\xe6\xe3s\xaa\xf9\xfa\x82\xd6?oBE\xe6\x97\xdb\x87\xb8 W\x04\xea\xcb\xe6\xfd\xa7\xc9bA\x10\xd2\xfbp\xac)\x90\x81\x01b_5\x0f\xd4\xb4\x92G\x1a\xe7 \x9e\x97o\xa5y\"R\x05^hGI\xf7!\xd3\xe5{\xbb\xbb\xd3O\x9f\xf2\xe9\xfd\xdd\xdd\x1d\xf6\xefl6\xfb\xf4)\xdf\xbd\xcd\x7f\xee\xde\xbe\xc7~\xce\xc8\x1e\xfe\x9c\x91\xbd\x19~3\xc5\x9f{\xbb3\xfet\x97\xf0\x7ffc\xd3\xe0\xcc\x14\xad\x100(\xc9\xa8J\xc7.\xbb\xc1i\xb0\xfb\xa0\xc6\xeb0.\xb2wx\xb1\"\x13J\xa6\x10\x16\xed\xf4\x14c\x8f\xbc\x07\x89\x96\xb0G3\xf0\x94\xf8\x88-\xc5D\xb0\xd9\xc8\xecA\x1cE\xb4\xaf\x11\x1f\xe8\x9e\x864<>\x16\xd9F\x9bX\xa9h\xf1\x84\x14[\x83\x0c\xbb&\x9a\x1aTQP\xb9]\x14\x82M\xaa\xf7yQ\xc4\xbcz\x933\xc4a\xf5f\x86ofUB4\xe9\xb6:\xb7\x1f\xe8\x97\xe7\xce\x83\x96\xe3\x18\xa8\xc8\xcb\xc1Co\x1b\x8e\xeb\xca\xe6\x15\xc6\x0eOT\xe6\x04R\x9c\x80\xf2\xd1V\xc4\xb8\xab\x9b7\xd9\x1f\xb1\x8fJay8\xc6\xec\xaf\x98\x1dA\x95\xfe(\xeb\xf2\xca'\xfe\xed\x07\xb7\xb5\xb3\x1e|_G>\x81\x94\x0f\xeei\x90r\xd0\xc4\xc7\xbd6\xd2!k\xb9pG\xe1\x99\x0e\x15\x17\x98\xb5\xf8&\xe4\xcd\x03\x17\x0b\xb2\xca\xb2\x8c\x8d\xa7s\xc4H\x9dY\x8a\x11\xa8\x15\x03\xe4\x1c\x81\xec-\xd8?sx\x0c+;]F\x9d!\x0f\xd0\xf5\x9b-bAK\xfeX\xa9-6\xc5%n\xb6u\x06C\xd8\x194G\xbd\xe62t\xe3\xfe\xa9\x00C\x08\x07|'\x82\xf4\x8e\xae\xb6\x8dy\x01fx\xfc#\xa9\x0f\x80\xff \xbc\x06\xe8\xf6\xf6\x19<\x82\x956\x11\x00\x1b\xd6\x92\x81ttf\xe0n\x8e\xb1(\xcc\x99\xc6Q\x9c\x01 \xf3\xb1\x89\x13\x18\xc2\x02\x0e \xf3\x8e\x03X\x06p\xc6\x03\x91py\xf7!\xf3\x96\x01\x1c\xe3]\xbe\xfa3\x0d?SK\xe2{b\x92\xae\xd9{'>0\x018\x8aM)\x0b\x10\xa2\x03\xfd\xb3\x93\x94\x84\x9f\x1bO\x9a\xe7\n\xeb\xe8\xd46\n\xb6e;\xd8\x0c\xf0\x93\xc4;\xc5\xd7n\xde\x04oY\xe6\x8c\x9e0\x08Q\xb9-f~\x89K\xa7<\x16\xdf\x18\xdel\xeb\xd1\x06\x050B\x02\xb4\xd0\xb8\x04\xb2\xc8\x08Nb\x89\x0bt\x8c\xfbh\"\x96\xb6\x18\xb8a8\xdf\xba \xda\x13y&N\x10t\xba-~0\xfc_\xff\x9f\xea\x876n\xc8H\xa5\xeas\xa9\xd4_\xdb\x11 /%\x11\xa7\x98&o\xbf\xa0Ml\xdb\xc5\xf0\x08\xd2\x87\xcd\x95C\xd3\xb8GG\xf1\x18\x01\xa7r\x86\xbbZ\xfeOI\xef\xd4\x91\xcc\xdf\x19\xd4y\x83\xe2pkRyQ\x91\xa98^\x9b\xf4\x1e%\x19\xa5\\S\x93\xfc\xa3*\x08\x9f\x1de\x87q\xbe\xe4\x8a\x9f&{\x92\xda\xad\x1db\xe2\x85\xb8VE\x06\xcf\xf7\x85 \xde\xae\xec\x13\xad0\xe6\x9bak.X\xcc\x00z\xec\x0fBz\xfc\xc4\x0d\x9b\xf7\xab\xfd\xe9\x8f\xb4\xcce),\x99\xf2\x15\x06Qch\x10\xeb4\x18h\x9e%m*\x97-\xd2\x8f\x93)aB3\xdek6\x81\xab\x89\xa2w\xb3\x1d\xca\x8d\xd4\xac\x1dZiG\xa3sbk\x9es\xe0\x16\x90A\xc1\xe4\x00\xd2\xfe\x0f\xf9lF\xcaS\xab\xf95\x03\xa3\xc7\x8e\xb7\xb0\x1fe\xb5\xb7Q\x8a\x8d\xccJ\"E\xe2\xa9(\x89\xee\x0f\xfc\xc2X\xdc}\xdf\x1b\x988\xda?''\xabp\xf2\xf9\xe7d\xb1\x9eE\x8b\x05\x0fY\xe9O\xc9*%\x93Z\xedG&O0\x96t\x15\xd29k}4\xc6L\xf1\xf3h1MI,\xbe,~\xb2\xe7e\xb9\xb4)\x99E1\x91\xfb\x0bqr\x91\x84S2\xed\xe9\x14\xab\xa4\xd8a\xfbz\x0e\xa2K\xd1\x19\xda_4\x1e7\x95\xd4\xe6qF\x7f\xc9\x18#\x8716Wk\x08\x83J\x02\x9b\xced\xd4 #\x0c\xea\\t\"\xee\xdf\xd1p\xcb\xb8\xdf\x92~\x94\xb1\xfd4\xe5Q\n\x95\x97\xf8f:\x80\xc8\xcbQ\xe5\xa4\xa7;a\xb7\xb1\xdf\xdd\xbd\xaaZ\x91\xf2\x83\x8d\xd1\x81\xb4]\xb9\xd8\xbe\xb74g\xaa<\xc9\xe5;Z\x87\x17\xa9!\x10\xfa\x05\x91E\x90\x8e\x85;_\xcd\xdf\x84p\x8f\x92H\x16'\xf4\xe2\x9a\xa9\xeb\xf2\xaaX0\xb8_\x97\x818\x16|\x7f\xbf\x15\xc2m\xec\xc4.\xf72\xf0\xb8\x1a\x88\x07\xf1\x17\x9cD\xa1X\xe1\xd2\xe0#H\x1e\xfa<\x85\xe8(\xf2\xc8(\xde\xde\x1e\xfbc\xbdv\x8f\x7f!\x082-h\xebU!\xa0\xd7\xd9\x0d\x1a\xd8.v\xc1^\xfd`\xe3\x8a\x8c;\xdf_\x05^bJii\x18\x8c\xc4{\x07\xc0\x90a\x1f\x12/\xaf\xb8 9M\xae\x97g\x042\x9aF\x13\xaa\xa8\xf6*^X\x0d?\x11\xe9j\x13{\xdf?\xa8\xebF\x94\xe9\x1c7E@&\xbas\x98\xdd\xfb\xbe\xf6\xe5q\xff\x1d \xa7\x8cN\xbe\xa7\xfc@YV_`\x80\xbe\xeb\xf7\x0f\xcfHL\x0f\x97\x11\xa5$mv\x10\xb6\x81Q^%\xd1\x8f2Jb\x92b\xd1M\x8er\x8d\x0ft\x96{\xb1%\xea(\x01\"\xb88\xf6\xee\xef\xfa\x82\x03h\xbe1CA\xfdc\x14\xd3\xfbH\x07\xd9\x9e\xad\x9c\x9f\xcd\x99-85\x1b\xd4\xc0\xb6\xe8G\xf1\x9c\xa4\x11\x15J\xaf\xbb\x1a\xf3\xc0\x8a\xa3\xdd\xdd:\xb1\x06\xa12\xd0 \xd5\xec\xfe\x8am\x9fU\x7fJN\xf2\xd3Er\n\x07\xca\x0f\xaf\x97\xd1\x94\x84\xcb\x9e\x0f\xfbmC\x9f\x06(\xfb\xb3!\xd4w\n\x08\xe1\x88\x81\xb2\x8eK\xe5\xd4\x98X]7\xf9\xb3\x86O\x19\xf7\xd0#i\x9a\xa4=\xc6\xbd.\x92\x8c\xb0?\xa6$\xa3i\xb2f\x7f\xae\xc2\x9c\xdfKI\x96/Iol\x8a\xd6Y\x1a\xd1%\x01\xa1i\x8e\xbd\xbd\x81\xa8a\x81b\xab\xae\xbe\xa0$\x16\x04\xa28\xa3a\x94w\x86\xe5S\xdf\x0f \x13j\x85F\xb6?\x13 OJ\xe5\xb8)\xdaS\xe1!h\x0d\"M\xb0 \xdd\x147i{ym\x8f9q \xa8\xaa\xe2{X\xae\x93^\x89\xc7_\x14xfSJ\x9e\x15\xc5\xdd\xc4\xcb\xacu[*\x15\xce\xc3J\xaa\xc4\xa0N\x04\xdd\xe2\xaa\xd1\xd8\x0f\n\x9d?l\xb3\x86\xab\xd4\x17\xf6\x8b\xaf\x0dJT\xed]RR\xae\xdd\x00\x0e\xb5\x86I\x06\xba\x1c\xeb,zH\xb3\x11\xdf\x9d\xe0\x8aP\xd0\xcf9\xe5Uy&\x85F\xc4KQ\x15\x92\xaa\xdbf\x86\x94\xa6\x19}I\x94\xb8\x83a!\x0c\xd5NK\xcc\x12\\u\xaa\xe8\x1d\xc5g\xe1\"\x9aB\x9c\xc4;\xbc\xd9[\xe2p\x98\xcc\xf3\xf8s\xcf\xb7\xc5\xd3\x18&\"\xb6\xb5\x06n9: \x06\\*A\x02\xee\x15\\L\xc2\xe0\x99\xd7\x86,\x1c\x89\xc4*?\xc6\xc8\x1f\xcf4\xff\xfa\xc7e\xa5\xf9\x9f\xa5j\xf3\xed\xcc#<]\xb1bND\xd8\x10\xa7\xe4#bn\x13\x0c%\xd7\xe3\x06N0e\xa7\xb4z\xe45\xe7\xcb\x16B,\x02\xe7(\xfby\x9c\xcd\xa3\x19\xf5|\x08g\x94\xa4@\xe2)\x10\xc6\xf5\xf7\x10\xd7\xce\x11\xedd:;\x04\x16GU\x97\xb6q\xcb\xc8\x86\x0f\xdf>\xe7M6\x88C^\x1c\x19L\xfa\x8f\x19\xb4 &>\x92\x9b\xf6<\x8d\x84\xae\xbd\x0em!\x85\xcb\xb5:\xa8\x8cw\xc0z{[\xee\x9b\xea3\x9fW\x8fb\xcbP\x1d\x90\x0e\xfb\xea\xaa\x83\xb6\xb5\xda\xa2\x02LH\xb8\xab\xdc\x04n\x92\xa2HV\x8d9,\x99.j\xa4#\x97^\xeeF\xe3\xcf\x15\x1a\xaf\x1b0)\xb8\xa8\x9b7\xe5\x1eVh\xdf\x16\xe1l\xd1\x01\x9b\x02_\xebiHC\xb6\xd4\xa8\xf7b@\xf3v\xf9\x9a:\x12E\x8e\xa4\x05M\x95\xc8\x17\xb36t\x94\xb6\x02\xb8\xff?{\xff\xbe\xdc6\x924\n\xe2\xff\x7fO\x91\xc2o\xc6\x03|\x84h\x92\xba\xd8\xa6M\xeb\x93e\xb9\xc7\xd3\xed\xcbH\xb6\xbb{\xd8\xfa\xa9!\xb2H\xa2\x05\x02l\\(\xab\xc7:\xd1gw\xcf^#\xf6\x01\xf6\x9f=o\xb0O\xb0\xb1\x11\xe7MN\xef\x03\xec+lTV\x15P(T\x01\xa0,\xf7\xec9\xdf\x87\x88nS\xa8B]\xb2\xb2\xb22\xb3\xf2r\xef\x1e\x92F\xc7e\x8bJL\x9a\x16\xfa\xe85\x87\xe7\xd2}C.\xb8\x18\xd4\x9d\x1b\xa9\nU\x17$\x85\x7f\xb8wO\xf7\xba\xe0\xfc\xaaK\xac\x91\x81\xdb\x05\x0c6to\xd7\xf6OO\xf86F\xc3\xe7%\x83\n\xc1\x88\\\x8b\xdf\xe5\n\xe7Y(\xd7\xc9\xffRj\x15u\x1a\x0f3&\x0d vdA@\x11D\xe3\x06.7N\xeb\xb6ix]\x8es\xdf\xc8\xec\x08\xf5P\x19\xd1C\x91\xebN\x1b\xa9\x80.\x02\xd25f\xf1\xa6r\xf3,Hv\\f\xb8\xa9\xc0#\xc8>\xbbl'\x98\x99\xd1qyg\x8eK\x19\xb9\x92SB\xc5\x9fC\x81 \xdfs\x8d'\x0f\x9f\xa3\xd4<\x93 (\x87\xa2z\xc4+]\xf8\xc9[/K\xca.P5]l\xf5\x8b\x94_\n\x86r\xfaT\xd7YBd)\xa9\xd5\x9c\xda\xc91\x95\xcd\xa2\x885\x86z\xb2p\xc3j\x94G_U\xac|\x84\x11<\xdcy\xf8p\xbf\xf7\xd0\xa4/95\xa2n\xae>\x7f2b\xfe\x8dU:N\xf2#\xbb\x87d\xb6B\x9dS\xa6\xf0=(\x1f\x08\xd2\xa9\x9a\x93\xe6\x05\xf1\xa6]z\x08\x88\xb2aQm\x88a%\x80(\x07\x1ac\xa2U\x8dA3!\xcb'\xf6t\x04\x1fQ K\xff\xa5\x9dloSY\xeb\x13\x1d2F\xf7*\xfd5(\xfd\xb5[\xfa\xeba\xf9\xbb}\x17\xd2NG\x9bk\xe0\x86\x9d3\x08U \x0e\xe8!\x92CS\x9e9\xa9h\x0cz\x98\x9f\xb9\xd59}\xac\x87Bn(\xd7H\x8f\xaa\xbd\xf7\xe9\xe9\xa9*+(\xd6/l\x8b\xbe\x16\xef,\xb7XtG\xf7\x0d\x9bI\xce \xb0|\x1f\xef\xfc\xc9\xa5}\xc8#/\x1eV\xdceM\xf3<\xd4\xcf\x93\x0f \xc4$-\xe4.\x18\xc3!\xbf{\xd56\xa0\xcb\x1b\xe3n!%}\x08\xb2\xe0\xaa\x86\x04\x9d\x8e\xf2I\xfe\xa4u`2u\xfc\x93\xb1\xe3\xd2\x05Ln5FY,\xc1z2\x86K\xda\x7f[\xa4\xe0!I\xc10\xea\xf6\xd7\xc2\xb6\x96\xde\xf5\x05\xa1\xab\x86\xf3@\xf5B\xcf\x92\xd94\x17m\xfb\x8a\xce\x9d\xc7Ny0\x0d\xc0\x1a\xa9\x89\xbfL@\xb84\xaer\xae/\xa1\xe0M\xfd\xc9\xa5n\x9c\xad\xfax\xd9\xbc\xc2\x02\xdb\x99\xe6M\xd7\x13\xe2\xbb^1G\xaa\xca\xb4\x1c!Q\xb3\xcd\xd1\xd1\x05u\xc9\xa4\xe5\xdclJ\xaf>\x97\x08 \x8a-l\x8b\x8e\xa7\xb4\xad\x1f\x97\x07\x99\xa7R\xe6\xe3s\x1e+\x02\x8fi\x84\xef\x9a\x0e!\xe5\xe89`]!u\xac0J\xf9\x91\"\xc4\xcf!l\xa5\xec6\xf5i\xa9\x0d\xbb\xa4\xc0\x91\x0f\xa3\x9f\"?\xb4-\xbc\x13\xe9\xf3\x9eyI\xcd\xc1%\x0b\x1a\xdc\x9f\x92\x14>\xb1EQ@\xbc\xd8F\xd9&\xd4X\x94\xd6\xa9Z\x0c\x1a\x8a\x94\xed]\xf5\x00=\x00Lu$\x97H\x91B\\\xb9@[-u\xf2,\xc8\x1c\x06\x9a.\x88\x04\xe5p\x93\xf0\x96\x05\xc5\xa2\xad\xea/\"\xc4\x13Wmt\xd5\x07\xef1qlf\x15\\\n\xdb#\xf0\x8dDI<\x88\xed\x8f\x81\xc5r\xa4\xf4\xa46\xf7\x14\x08uf>\x80\xfa\x81\x82\xb8\x91\x81\xa7\x10\x15p\x8c\x8a\x13\xbf!\xb2\xb2?\x03;c\xd6I\xc5\xe7>\x95\x8e#\x18\xf2\x1f\xe5\x85f\x9b\xc7\xc6\xe9g\xb5\xa6\x96\xe2\xa9\xb4ow:\xb1\xcb\xc1\x81\xab\xbe`Zf\xfefX\xbc!\xdd\xd4\xf3\x03\xae\xe7\xe7\x02\xbc\xa8\xecr\x08A1\xc4\xcc\xa4\x91\x93\x1f\xb3\x85\xa7xn:\x1d}xc0jFA\xb2m\x17\x13\xddFw\xa0\xaam\x0e\x085)q6\x89\xab*p|\xd2\xf5\x82 \x9a\xbc\x0f\x13oF\xdaE\xe1m\xb1+(\xca\xd7\x98\xc5\xc6l\xa7N\xa2\xd55\xaa\xde\x04\xe7c\x97\x83\xe4\x8b\xe0\xbc\x1eSaS\x9c\xf7k\xc2]\xb8M\xc1\x974\xb9\xee\xf0+~\xde\xb9\xc5 K\x19E\xc3ev\xb9{\x13\x9bp\xf4\xb9\x8c\x0c\xbb\xde\xe1\x13\x7f\n=\xd95\x93)\x98\xffd\x910\x17Ql\xc7\x024\xa5\x9dB\x14\xe2\x9d\x02Y\xae\xd2k`J\xe8?i\xe6Bd%9\x13\x02\xe4\xfb\x17\x89\xfd\x7f\xabMrb\x8c\x1dj\xd6\\)=rU\xa1\x98$\xb3\xd2,_V\xf7\\\xce\xcbVD:\x9b\xce\xdej9\xa6\x93v\"I\x8fk\xbfr\xc9\x84\xd9\x93C\xd8\xe9\xe8/\xb20\x1a\xfa8\xe4vq\xc5\xbd\xaaQY\xb6\xadJ\x0f\xf2_\xb2B'f{\xb2^C\xc0\xa5 \x8b\x9d\x9d)\x8c`\xe5\xc5 y\x19\xa2[J_\x17\"e]\xf2;+\xe1\xa0\x9e\x12b\xa43=z\xf2\xf5\xe3\xca\x0d\x9dQ@N\xdd\x98\xffyE\x93-a\xf8\xa8\"\xd3}\xfa$\xd4\x0c\xc5\x8d5\x9f\xf1\x10*\xe2;k\xc7\xcd?qku@G\xec\x92\x18\x86pl\xf3\xcblJ\x10M\xf3\xe4\x04z$TP\x8e\xd4\x9ac`\xfc\xef\xdd\x13\xbd\x98\xdaF>\x99\xa5\x13-\x83\xc6\x88>\x0b\xdb\xa2\xf5\n%\x01\xe6\x15\x11#$\xd2N\"\xd2IS\x95\x97q\xfc\x0b\xdb\xe2u\x02\x92$\x90.\xbc\x10\xaeh\x8d\xa5\x17_Zl\\\xa8\\\x15`\xc3f\x85hw \xd6\x82\xfe\x11\xe1\x95\x19\xde!\xf8l\xe1\x91\xbf\xe3R\xf94\xc2\x01[\x8e+}_R\xa9pMQ\x05\x80:\x8dRI\xe3\xa8*\xd5\x1c\xb9\xc9\xbe\xab\x08\xc2l\x05C\\A\xbe*lic~\xc4\xf7\xe0 \x17\xf0\x86\xfc\x88<0\xe8\xb5\xd0\x0e\xc7\x91u\x7f\xdb\xa8\xec\xd4\xce\"\x07\xa0aFa\xb1\x95$\x85\x07\xc7\x1f1T\xd4\x8d\xe7\xd7(\xa5\xbb\xa8\xb8\x92w\\Q\x10\x9f\xb7\"(R\xc3\x9a\x0bM\x06q\x07\xfc\x04\xc2(\x05\x7f\xb9\n\xc8\x92\x84)\xa9\xd2a\xe5\x06\xc2_\x91\xd67\x10\xb5\x01\xd5\xa2\xb6\x97\x13\xc9\x95\x8f\xae\xc6\x91d8eb\xad&^B\xa07\xd4\x96\x01:\xe0\x0b{\xac\x1af\x0f\x99 }1\xb6\xdfo\xd3\xfe\x98\xfft!\xad\xc9\x13S\xd3\x15\xbfOi\xec\x8b] 5^wI_0\xd3\xb3\x0e\x95n\xe9\xce\xc7%\xc5 \xa0\xa3?N!Z\xa5\xc9\xe8\x8f?Yn\xa9\xb6\x9e\x1f\xa3\x8b\x8c^([\xcc\x90\xb0\xcf\x15r$\x9c\"YJ\xf9\x1dP\x92N\xa3,U\xde\x908\xa6\x92;\x0c\xe1\\\xb9%\x80\xb2\xc3\xb5\xce\x88X<\x0b\xdb\x8a\xc2,\xa4\x03\xb5\xd8m\x92\x08\x88\xca.\xdf\x99\x1e%\xee.\xbc\xe4=\xd6b7\xd8\xa5\x17\x8c\x06,lk\x12\x10/\xccVB\xa7\xb6\x8c\xd6\xdc\xf6\x8d\xc4vn\x1e:\xd7\x96\xce\xfc\xd0O\x16\x96\x0bKm\xf14\xf6\xfc\xd0r!\xd0\x96\x8a\xfdy\xad-\xe5\xb3saB\x89G\xf5\xe3\x90\x92\xeaYM\xd9\xb9\xb6\x8cS\x9b\xb5\xe3\xa2\x85/\xde\x82E\xb2\x96\x10\xaf\xf5\xcf\xafb?-]\xbcn\xa9/\x91\x08\xe6\x9f\x04\xfa\xa8\xf8\xe6\xf5\x9d\x19\xaf\xa2qm\x913d\x86{\xd3\xc68P\x808^2\x18\x91x_\xe4\x11\xc2n\x14N\x88\x00\x0dZ\xbeu\xa3\xb0\x04e=\x9e\x07\x8d\x14\x174v\x15Mrz;\x01B<|\xb3\xbe \x9fs|\x92\xd5\xba\x8e\xa2\xe5\xc5\xf3\xa7\xf8{{\xbb8\xcf\xca\xb58\xfc\x8c+\x8cQ1m\x886~(h\xc1\x7fc\xeb\x84-\x06\xe3b\x17\xe8A\x8cx\xa8\xd1-\xac\xb9+9-3#\xd2\xda\x9c\xab\x171\x89M\xd0\x05\xa1\x12\xe7\xd4*\xcd\xadq(\xfa\xb2\x83\xdd\xees\xa9\\\"\x97\xe8}\xc4\x89\xbb\xf0<.Ux\n}Z\x89\x87_=\xb1\x0b\xfa\xcf\xe3t\xae\x04\x135\xf3\x82\x84\x00v\x0b1IVQ\x98\x10\x17\x84\xady\xa8^\xc0\x96\x96\xb8\xa6\xb4\xd3\xe1\x93C.\xa4\x8b\xedm\xba\x1b\xaf\x1b\x80(H\x15q\\8\xb7\x1b\xa9\x19C8\x86`\xec=;\x17\x14\xc6D\x17L\xb1f\x90s\xe3\xb6j \xcc\xe7Z\nb\xeehYO\x9bx\xdb\x8d\xc7\xc5\xa6\xdd\x9e\xd7u[\x1cva\x97\xfdnw\xf6\x0by\x96\xed\xc4\x9c\xf8k\xbbi{;\x00P T%\x1b\xfb\xaeb\xb2\"\xe1T\x00\xa5\x08P\xae\x96\xb0h\xcd5*\xf4\xee9\x9a\xf0%\x0cy\xf8\x1fcr\x06\x07\x90\xd9\xf2\x0b\xf4n\x92\xfe.[d\x95>\x1d\xc18tK\xaf\xce\xb0\x8a\x08\x1e\xad'x\x12*\x8b\x03\x9b\x1d(e\xfe\x80\xbdS\xb8\x02\x86\xf4\xfc\x9c 1f\xa1 \xb4\xfcn\x0fY\xb1\xe2F.\xe4\xb7y\xb6S\xb9\xd4\xaf\x18\xc1T\x18\xf3Z\x9d\xd5&*\x03\xf3\xda\x17L\xd4P\xbdL\x15\x8f\xc6\xc9\xa5\x90\xc3I\x89\xa3\x17\xd8\xa1\x0d_O?\xea\xd7|T0\x97\xbc\x9c\x07\xccfV\x1cBb\xe4exT\x96\x1d3H\xc5+\xa3t\n\xf6\xb95\xbcX\xc4\x9c]Hy\xc4YnH\xaf\x1f\xf8Vmp\xd2\xb8\x18\x98Y\x83\xedCy\xe6\xfa\xcd\xb2\xe9\xac\xf4\xad\xe4\x8a4\x16\xe7\x1a\"x\x02\xfec\x88:\x1d\x07\xe2qtf\x82A\xad\xc2\xb6b8\x04Z2\xb5\xe61\xdcNlR\x9c\x9f5:8D\x89LZl\xfeY\x97eg\xb03\x17\x9d\x97K\x80\xd8F\xc9\xa7\x8aM\x9c\xf9\x11 \xe4\xbf\xc6\xbd3i\xf7\x9a\x16\xbensF\x95\x1b\xd7:\x899)}Y\xb8Ap\xc3\x0d=\x861\x8a\xce8\x13'gm\xcc\x06h\xb9\xeaA\x10\x18\x8dRY\x84,)lVD\xfb\xf5\xb8\xdcJ\xa8\x07\xbc+*+\x91c\x8d\xcb\x11\xdd\xb9\xba\xf7\xecB\xa4\xa2\xc9\x89\x0d\x0eM\xb1\xa4\xec\x8a%}\xceq\xae<\x94\x04\x85K\xbe\xa6\x9b\x1c\xabu\xeb\xefM\xf3\x93\x0eF\nf\xb8\x8a\xaa\x18m;Z\xc4cL\xdb\x02:?s\x95\xa3\xa68eR\x85\xddo\xc4T\xe0f)eC\x13a|T1?)\xdf@\xbc4GP.\xa2\x9c\xeb\xec\x0c\x15=\x14\xe5n\x9b\x00U\xa8Z\xe9.b\x1c6\xf0\xc92\x1dG\xcd\x16q\xdc\x96\xfb\x08\x0fnd\xde\x0d\x16\x94\xca9R(\xe6\xf8W-\xa6{\x15{\xab\x8dN\xf7\x9a\x1b\x80\xb6g\x7fl8\"\xf2\xe3\xc1\x07?\xe4\xa2\x1d\xd7B4\x89\xbd\x94\x9c,l\x8b\xcefE\xa6\xc0\x85\xfb\xb0\xec/!t\xf1\xf5\x92s\xca,\x1f\xda\xb9A\xf1\xb3[\xbe>0i\xcd\xc0x\x8dI$S\xed*\xf2\xe6\x9a\x04\xce[\xe7\xb00&\x1e\x94!!\x84\xd3\x12(l\xbf4G&\xa7\xfa\x14]\xb6B\xc5o$W*\xa3\xa6^\xb2\xde\xf7\x99Ho\xab\x1f`=a\x95\"\xc4~\x9c\x9f\xef0\xa2+t\xe3\xb9 \xa9\xdb\xb2\x0e\xdaLJ>S\x14\xbb\xc6\xfe\x19\x94\xe3\xd2JR\x01/\xb4EE \xa9\x9b\xdc\xed\x1b\xd1K\xaa\x9bR\xe6\x9f\x87\x81\xadM\xe5\x07\x065\x86\xaf\xbb.\xd7qF\xf3\xfc\x8a\x11\x19$D\x82\xf98:\x93vz\xf7\xc2\x0f\xa7\x9c\xba\xd1\xa2\x1a\x8f\x9cT\xf6\xa6l\x86\x8c\x84B\xe7\xfc\xfe\x908\xc2\xfb;\x16\x14\xa7\x10#\xaa\x13\xd5\xd3\x9e6\xee&\x82\x84\x94|\xbb\x9b\xa3\xd8hL\xaa6rM\xd1Q\xd8\xd2\xc5Qu\x8e\xe5\xd9\xa1\xdf\xc7\xf9,\x8e\x96\xf4T\x86\x11\xbc\xfb\xa7\xa2\xac\x1c1\xdb\xc50\xd8\xed\x02g\x97bpW\xa3M\xb4iB\x1fNc]\x84\xbaz\xa4\x8dI\xeakO\xea\x1a%\xcb\x8dv\xd0\xe5\xcf\xb9\x1bK\x0b\xbb\xa3[_\xf5@\x93\x1bQMd\x01\xfc\xac\xa2\x9c\xd6\xbc.Z3\xee9t\xb2\xce\x98\x9b\xde\x01\xfa\xe0\x14\xc6\x9b\xed\xfbA8\x97\xb8\xd9\x9c\xe7\xf1\x85\xb8 |,\xd0Z\xc7\x00\x91F\xcf&\xe9\xde\xb420\xbb\x16\x02\xe5\x8f\xf9k;\x8f(\xee\xb6Ppo\xf1$\\\x07\x94-\x97'\x18\xb2\xd9\x85\xbaA\xa9/\xcb\xb0\xc2A\xe1\xed+\x9e\xccZu\x96A\xcc*\xfd\x99;d5\xd0\x92[\xc3\xbd\xafg\xef\xe2j\xf4\x85\x8a\x0b\xcd\xb4\xb6\x05%\xaa\xc3\xe7,o_\xfb\xadf\x04\x95ru\n\xe5\nL\x95U\xdf\x86\xb2\xa8\xaaO\x95B~>?\xf6\x9f\xec\xa4\xc8\xb0\x12#H\x84\xec\xd4\x9a\xca\xe1\xf0\x13\x12\xcch\x15\xfc\xf7\xd3'\xb8\xf2\xc3itU\xa5/\xbe>\xb272\x12&_&}\x00\x7f\xc81\xcd\x9f\x16\xaeS\xdds4\xc4~\x816\xc8\x06\xf0\x00\xf2\x9a I\xdf\xf9K\x12eiK)'$W\x10\xd9>;\xc0\x8a\xaf1\x1cB\xc1\xff\xb8\x80\x03\xe0\x85\x15\xb5\x05\xf6\xfb2LI\xbc\xf6\x82[v,>\xd7\xf7,J5]\xcb#C\xfdK\xe9\x83F\xf1\x873\xf9\xa8\x88\xad&\x96\x8fJ\xda\xd2\x98\xcc\x94\xec/\xec\x8d<_\xe5#l\xb7 $\xa55f\x10\x89\xdd\x1c\x0f4s&a\x1c\x05A\x1b\xfd\x90\x0c\x1d;\xa5\xcd\x05\x84\xff\xf9r\x8a\xd2\x87\xfc\xaa\x8a_\xb4\xb7,\xd4\xf4w'\x9d\xa9\xd6p\xb4\xb7s\x84\xf3\xe1$\xf5\xd7\xe8'\xda\xf5\xc4\xcf\xcf\xe9\\\x7f?\xc8/R\xa5\xaa\x1a\x8dV\x91bQm\x15FPl\x99\xe6\\ri\xf7<\n\xc5\xe4\xd9\x9dD\xfe\xb7\xee\xb2G\xe3q\xe5bD\xab}G\xec\xb9\xe5\x92L}\x16\x9b\xa5\x99\x84\x95\xbfP\xb2e\xb2\x01\xa95(\x0e\xe6\xac\x8b\\\x98\xef\xbc\x0d\x87\xa0|\xa3\x1dD\xb5Ni\x18\xe5\xe2\xe2|\xb8M\xde\x9a&\xde\xd9\x14P\xcdGU\xa2\x9f\xc8Q\x88\xea\xd1S\xd8#\xe1\x8d\x82eA\x07R~\xab\x99F\xdfDW,W\x8em\xb4\xfeF\x13\"kA>Zz\xd3\x1eV\x8eq\x90\x1a*l\xd7\xd7\xf0\x92\x89\xef\xd7\xd6\xb8\xf0C/\xbe\xae\xaf\xe2%d\x7f\xb7~$\x93d\xd0Ta\xbb\xa1F:\xeb\xef\x07\xa4\xa9\xcevc\xa5\xd8\xbb2\x94\x83\xe4\x9fm\xc8+\xd9hq\x95\xfbwWwxys\x1b\xf2\xfc\xe8\x18\x19Ee+\x90\x0b\xf7\x07i\xeb\x07.(`3\xff.\xae\xa3\xf8T\x18\x9e5\\\x03\x91\xc7\x8f\x9db`u\xca\x97F\xdc\x85V\xf8+\x9e\x16\x83\x846h\x08\xadP\x11Z\xa2#\xb4EI\xf1H\xd3\xc0\xdaM3 \xbc\xd4\x0f\xfb\x8d\xbd\xd7\xee^\xf1\x88\xbey\x9bM]\xd7nwhEZ\xa0\x05\x8d\x13\x8fP\xe9\x98\x87\xd5\xb8'A8X\xd4\x87\xd8\x12\x0f\xa5\xd96'\xdaez\xcdbQl\xf5\xb4\x9f\xeb4\x84\xba{I\xbc/\x13\xd12\xb6\xca\xc1\xc5\xed\xd213\x1a\xf1X\x85,\xbdQ\xd5'\xc4z\x1f^\x86\xd1U\x08\x82\n\x0c\x81\x0d\xdb\xa8\xc7`\x07l\x99\x12\x15a\x1d\xf2\xb8t:\x8e\xab\x05\xdac#)\xf9(\x92\xc6\xb06)\xe74a\xa0\xd3Dh\x04\xb3\x89k#\xa9\xc0\x0ef~\x10|\xe3\xa1\x96\xce\xbb}/\xb5X-\xcfkV\x9aW\xc0z\xdc\xd9\xa8\xc7Z\x84\x95U\x98\xcc\xfek\x04+\x96f\xdc\x96:^\x98$g\x10\xe3\x0d\xbc$}MP\xce\x16\x81\x11\xe9\xabwQ\x8a\x82\x92\xfc\xeeh\xe11\x8f:\xd9\x1b\xb0\xa4\x0c\xcc\x7f\xe6gUV\x13\xd6\xfa\xc9\x08\xfa\x83\x07\"c\x03<}\n{0\x1a\xc1>\x1c\xc0@\xbc\xd9\xa5o\xfa\xbbp\x00;\xe2\xd5\x0e}\xb5\xd3\x83\x03\xd8\x15\xaf\xf6\xe9\xab\x01\x1c\xc0v\x1f\x86\xb0=\xa8\x1d\x92g8>\x852\xb0\x98\xfev\x19DU!\x7f\x13\x07h\xb4;\x19<\xa4{\xd9\xee?\x1a\xc0=L\x0f\xebH\xb6L\xe5\xa5\xb0\xfe\x9f\xff\xeb\xff4PY\xf40*\xaas{A\xc91\xac_w\xb4\xea\x06\xd27\x0d\xa4_;\x10\xd0\x0df\xa0\x0c\x06\xffV;\x1c\x98:\x1c\xf0\x0e\xdb\x13O\xae\x0f}\xacC2I\x90\x08\xd1\xbd~\xa8`\xfd\x13\xc9\xd7\x0c\xa3y\xa1Wf \xe5qY\xe5}@?t\x94}\x91\xa7l+\xf3[nuS\xb1\xa8`\xb5\x1d\x89\xcb4y?\xe7#\xde\x96\x02\xa0\xd5\xef\xbdD\xab\x01\xa0\xebe\xa7\x85'\x10q0!\xf9\x08\x1dWjt\xf2\xc5\x0cs\xf2n\xb6\"\xa9\x0f\x03\x80\x97\x91\x93\x85\x17\x1fESr\x98\xda\x92\x07\xac\x1aWZ<\xb4\xd1\x98J\xdd{{\x83G\xfb\x80f\xf9OF\xb0\xb7\xbf\xd3\x7fT2\xf8Rp\xa9B\xd0v\x95\x85\xe3)\x9a\xc7\x12D\x06gj\x9d~\xa5N\xff\xcc\x85\xb0pS\xd7\xe6\xd9\xae\xbc\xd1\x9bxh\x89\xa32\x93\xbef&\x83\xe6\x99\xf41\xe5\x85v\xe1\n4C\xa8\xd7\"R]\xaa:\x90\xef\xc3\x0f\xa4\x03\x89]~X\n\xe5@jQ\xdaH\x0d\xf7@fr\\\xc3\xbdtL\x9bS\x82@\xaf\x1a\x0eL\xb7\x12\xa4\x1623\xed\x16\x13\xe3\xafl\xb3\x1d-\x91\xeaq_\x93\x83\xd2ZqV\x83\xbb\x9d\xd9*F\xec\xc06\xde\x94\xa8X\xb1#\xec\xd1B\xb1\x1a\xb5\xf8Qj\xfa\xb3\xf6\x83\xe3\x1a\x86_\xc2\xb4\xb0\x81f\x05w\x87j\xda\xadtP\x8b\x1d\xf9\xa0{.\x02X\xc1\xd4a\x036\xac\xcc\xcc\x8e\xe1|\xa8\x07\xc6\xa2\x86yj\x82\x85\xd4\xb0\xf8E\xca\xd1\xdcX\xc6\xc7\xa8d\x1b\xe4\xa7\xf5\xc2\x7faq\x9b\x9fA\xb9`\xa8\x80\x1f\x97\xcdU\xdd\x9e[\xed\x7f\xbfHB\x87\x9e\x989k&\x98x&\xe7\x18:\x06\xd9\xba\xf12u\xbd\x84\x02>\x1e}\xae\x9a\xdeJ4\xb2\xbd\x8d\x83\xa1\xab\xb7=`bv\xdd\xc0\x90\xb1\x92F\xe6\xb4\x1e\xc3\xe0\xf7\x1f\x03o\x0bC\xef\x8cD\xca\xbc\xf2\xa8v\xf4\xa3\x12\x9d\x97\xb7\x8f\xd9\xb0\x98\xe9 \xcb[\xbeJ\x15E\xb8~\xf5\xeb\xca\xf9\x16V\xa9\x8c\x1c\x9e\x01\xb6\xc1\x0e+\x94[\xbf1\xb4,x\x8f\xf9M\xeb\x86FKL\x1bFR/\xd4S\xcf\xf2v|\xa2!\xa4\xfaq\xd5\xf3Bw*\xa0(+p\xeb\xe1\x14bLy\xd2\x92\x04\xa3\x9cR\xb7\xba\x99)e?/^\x17\x176\x035y\x1f\xcfq\xae\xcf\xcb\xac\xd1\xae#\n#\x04J\xd9T\xca9\x13\xa2j\xda\xf0\x92\xc9}n\x8b\x91\xc6^\x98\xcc\xa2x\xc9\x8c1tn1\x18\x17\xfc\x9d\xa8\xd7\xc2r\nT\xaeY\xe9E/T\x85\xdd\xbcV\xbd\x1fG!\xb5\xe1y3\xb90\x0bi[qY\x1c3\x06\x0e`\xcc\x06\x85\xd0\x857\xb9\x14qj\x96Y\x90\xfa\xab\x80@\xea/Ib\x8cw/\x06\xb2\xc8\xc2\xcb\xdcG%\x1f]\xf1\x86\xa7\xec*L\xadx\x1aWW\x93O[<\xe2\x80apl\xe1}\xe0+\x86;\xb6_ k.\xecc\xe1 \xf8\x9a\xa8\x1bEW\xb6Z\\\xe9\xf1\xa6\xb0\x01\xd58\xdd\xd1\x8e%\xc4\xd1\xd9H\xcak\xae\xaf\xc1\xc1\xc8\x82]\x98\x8a)\xe8kk\x14\xdafZ\xa9|\\\xe8\xad\x97t\x0154\xd5\xa4P\x1e\xb5\x89E\xf2\x89J\x06O\xc5\xbb\x91\\\xc3\x9cgd\x16d\xc9Bj\x80\xfd=\x12%\xc2\xe4\x1e\x0d\xb6W1\xc9\x1d\xf5\xb2&\xbd\xa8\x8e\x9d\x12\xbe\x18e<\xd3\x8fL\x1a\xcd\x81\xfcW)g\x9a\x96\x19\xf3r\xdaZ^\x14\xcaDz\x9c\\\x15\xfb\xa7~\x1e\x9e\x89\xeb+\xdd\xa4hLH\xabLB)\xb1`Z\xc4\xba\xaf\x84 \x10\xe7e\xe5\x9e\xe3\xc8\x0b\x02\xba\x0d\x8bE\x9eF!\x81\xab\x05 \xe1*\xcf\xa8\xb45\x82\x9e\xa5\xe9?U\x89f\x89:n\xd8]\x92\xfaAP\xdajj\x979d4\xbe\x00\x85\xcc\xe6W\xf2\xaa\xb9\xd2;;b\xdcJ\xb4adw\x99@\xab\x93.Q\x90\xdc\xe9\xa9\xdc~\xc5\x97\xac\x18yy0\xa5\xfd\xd6$(T\x00\\|m\x080c\xec\xb6*\xc9\xea\xbb,{\x9a\xd5\x9d\x99(\x9b\xc8\x07\x0c\x85J\xe9\x10J\xf37\xd2m;qa+V\x10I/\x1e\xb5>r\xecXY#<_\xbe\xd0\x89sc\x04\xb1\xeaYP\x7f\xa9R\x0b\xdb\xdc\xe7\x84\xc8\x10\xc5[\x04\x01p\x16B\xb8\xc4\xae`\x0c&\x95\x81\xe9U\xb8,[n\xd4\x15M\x16\xfc/\xe9\x96\xb9-f@\\\xdd\x06=#$Z\xe6i\x90\xf93\x95Q\xac\xb6\xa6l\xb1z{\x0c\x96{=\xe4D\x969\x90\xab\xc4]!.\xb7b\xb5%\x9eZ\x97\x89\x17sH\xcaBQ\x14\x1f{\x93E\xb9\xa2\x94\xe2|\x12\x93\x12.\xb4K\x8b+\xf0*bDSKU\xb9\x0din3\xda\x04@Lgz\xef\xde\x06\x8c\xb6\x9e\x15DK\x97\x10\xbd\xd9\x1c \x18\x04\x10\xd2qxV\xa9|c\xf3\xb4\xb8\x18\xc9X]+\xb7\xa4h\x84\xdb.\x97\x16\x9e\x0e\xfc\xfd3\x9a\x940`\xc7iZ93\xcd\xf5\xf5\xab\x96\xbc\xf6^\xdb\x98X\x16\x95\x18\x84\xa9/\xf0\xe2\xee\xde=\xae\xad\xd8\xc6\xc4\x0c>\x86\xb6\x1e\xe6\x8e\x95x#\xd4\x9c\x1d\xb9\xd5\x1c\xcb\xfe7\xbb\x0f\x06\x8eM\x87\xc4\x91\xd6K\x12\x7f\x1e\xc2\x10\x8bv>\xd7\xa2\xd0\x05\xdf\xc5Tr.x.\xcf\xe6:P\x13\xa4N\x9aH\x0b\xe8\xee+\xe8#\xe7\xcc\x8f\xaf\x95\xaf\xf4\xaeY\x13\x17x\x08@\xad\x07\xd6$\ng\xfe<\xab\xc9$.\x985\xbdl\xd1\xe4\xc1\xb5\xf6\x82\x8c\x0cA1\x02\x96\xd6\x15&^n>V\x9cN\xec\xcec\"]\xe5\xc6\x15\xc9\xba~\xe8\xe6a\x97\x87\\\x8c\x84\xc55\xd4B\xd1\xdd8\xa12\xa5h J\xa6\xb9*k\xc4s\x06\xa60\xa4\x87>B\x86\xb1\x14\xe8\xa7U\xacR,_\xaa\xe0m\x11\xcfn\xfc\xe8\xa1\xe3b:\xd4\xf1\x19\xcbl\xdd@U]\x9d\x02\x9cr>\xde8=\xcb\x99y\xfaG\xb9\n\x92=\x82\xfd<\x86t{\xfb\xb1#|\\-\xcf\x82\x0e\xd8\x9dN\xe8\x14\x1a\xa8\x9d}U\xae\x97\xf4(\xc2i\xc2\xb6f!K\x98\x8bE\xb9\xc4a\xd3\x06 \x0fq\xef\x82\xe5@\x87\xfe\xef\xef\xa2\x8dY(\xbc5\xf1\xec,\xdc\x06\x1e\xc3\xcd\xe32\xcb\xd8z\x8d4\x14\x1f\xe5\x1b\xc3\x9a\x15b\x8f\xc2\xe7\xe0\xa9E\x9c\x8a\xea\xa1\xba7\xe9\x93\xd9\xe8\nU\xde z\xf4\x07\xdd\xed\xf2\xcd\xe7\x12'&r\xe8\xb2\xad\xeb\x91\xbeTM:\xe7\xe7$}s\x15\x8aj\xcfI2\x89\xfdU\x1a)\xf6\xd3\x99\xe9\x83\xd7\xdeR\x0dh\xe2\x99\xea\x9e^//\xa2 iq2i\xd7\x98\x91`~4\xc76Q\xf1\x14\xe5D\xb9\x06\x86\x18\xc8\xec\xc4\x11\xccN!~kC\x0d\xeaW\x1a\x9b\xb6\x99\x87M\xc4\xc2\x14j\x14?\xf2\xd2k\x9b@\xee\xb2\xfa]\x19\x81L\xaa\x0e\x0f0\x82\xdb\x7fY3\x91\xed{r ]/g\xffS\xb9\x95\xcf\xdc\x15}\x1d\xff\x1b\xda\x0fUUs\xa4w\x03\xa3\xdc\xe9mq\x94\x9ek\x9a,xt\xfb\xe4\xc4n<8\xd3B!Fj\x85\x0b$w\xc4\xd8\x10O\xb7\x1a\xe18>C\x07'\xe1H\x91\xa1<\"\xbe\xa8\xacH\xd8\x00g\xb9\x8fv\xfc>\x1f\xfa\xd6\x16W\xf6\xb1\xf0\x03\xe5\x14r\x9f>\x19\xb4d\xc8\xd5\x9b\xf4\x83\x0b\xd24\xdaVX\xa1\xe7\xa3\x88\x0b\xd6\xf99I^E\xd3\x0c\x0dN\xd4\xa5D>G\x16+Yt!/N\xc8\xf7\xde28BnE\x93\x16\x7f]D\x88\x0e\xed\xbdAO\x83q\xc8\xfc\xb0\x80\x0dq\xb7\x18\x04\x1c@\x0cC\xcd\"\x0bSS5\\p\xd1\xa9n`\xb5\xa8\xaa'\x0f|-#\x91\xe3\xaf\x9bx3\xf2M\xe4M+ \xacjID\xce3\xb1\xd0\xc8q|\x88\x03I\xba!\xb9zG\x89@x\x1c\xc7v\xa1IB*\xad\x1c\x97\x1bz\x916\x11\x84\x9d\x87\x06q\x88\x8e\"\xb6\xcbs\xf0\xc3I\x90M\xc9\x10\xc6\xa1=\xe8\xed8g\x12\x12\xfcC\x07\xd3\x1f\x0c\x9c3\x85\xb0-W\x81?\xf1S,\xdf\x1b<\xc0P\x06{\x83\x87\xfc\xdfG\xec\xdf\x9d\xde\x1dM\xe2N7S\x10y\xcc[\x99t\xdf\xbd\xf9\xea\xabo\x8e\xcf\x8f\xde\xbc~\xf1\xf2\xabS|\xf5\xfe\xed\xf3\xc3w\xf2\xab\xda\x9d6\xe8\xed\xfdN;-[M\xbd\xaa\xf6\xd2@\x165\x07\xf3\xf5\x8a\x0c!\xab\x9e\x10+\xef\x9a\x02d\x08v\xcf-\xb6\xa0c\xff\xfdF\xd5\xe2\x02(\x9a?\xd2M\xa3\xf9<\xa87\x0ej\x18\x91&\xabJ>\xa2\xd4\xd4uy12\xfd\xbaYL\xb2K\xce\x19\xe4\xac*\xaf\xa8Y\xff\xfc#63K^\x81\x1cod\xad\x89n\xaeU\xad\n|\x1eA!2\x12\x8dJ\x0ef%l\xec\xef\xa9\x0c\xc8\x97\xc2F^\xa7\x85b'\xa7\xca~\xc8\xe2:\x94\xd1\x8c}U\x1d\x04\xdf\xbca\x83\xae@\xa3i\xd8H\x17\xa1\x18\xac\xa0\xa9\x16\x8b\xde\x19\xba\x9br\x87\x94\x1a\x10\xf9\x1c\x18\xdeQy\xa1\x8f\xb7\">\xdd\xd1\xd6%\xb9N\x90\x91&\xdc\xa3\xc2\xc2\x1d\\\xbc\xc3\xe47C\x16\x14w\x1c\x9e\x9d\x95t.\xa22\xdeZ\x1e\ny\x05%\x0c\x0e\xe9\xd8f]\xa0\x91\x86T\x1d\xc3\xd0\xa7\xb1O\xff\xd2\xe2O\xa3haT}7~\xb9\xd1\x01\xcc \x9a&\x18\xde4\n))\xda2\x1ew\xb7\x1c\x9d:4\xbf\x1cJyK\x96\x87\x98\x90\xfc\xeezE8o\x0c\x1d\xb0\xc4\xed\xaa\x977\xbae\xba\xafn\x18\xec\x86\x9b\xf8\x91~\x0f\xef\xedj\xb7\xf0#\x95\x05\xcbP\x18.\x1a\x0e\xed\xc1\xbecg\x94\xf2\xec;\xb6\xe5\xa7$\xf6\xd2(\xa6\xe8\xd3t\x94\xa7r\xf0\xb2\x1b\xa7F;\xa8\xbb\xba.h&\x8c \xa6#\xa8\xe2EH>\xa6t\x13i\x12\x91\xd3\xdd\x80m\xe3b\xbc\xcc\x87\xbd\x19\xb0%\xf5\x84\n?N\x1a\x1fh\xc1\xba\xdb3\x93\xc0=\xe9\xea\xa3\xc4\x94\xfb$i\xca%\xe8W\x14\x9dEf-\x17\xd7.B}\x04\xe5\xd02N\x81\x98\x06\xae\xf7\x18\x85\xbd\x07;\xbb;\xbc\x7fV\x1f;\xa2\xc8\x82\xce\xdf\xf4-\xf3\xc2L\\\xecd@\xcb2\xd8\xe6\xcdt\xe88\xb7\xf9\xa0\x9e<\x81~\xcf\x81\x0e\xec\xef\xed\xed\xec\xdf\xcd\xa6\xaf\x1c\xa9\xfc\xe0\x18\xf4\x8dg\xea\xc0\xe9\xceI*\x0e\xf9\xe6[Y\xa4\xf3\xeaIjd\xf1H\x03\x8b\x87<\xd1E@L\x0c^l\x13n{\xe4\xdcz'\xf6w\xf4\xd7#\nOV\xa10(\xa4\xb5\x03\xdb+\x92.\xa2z\x034\xc9\x8dl\x0b\xa3\xcd\x0b\x9a:\xf6\xcf0\xc0\xc5\xd8\xfa\x97\x7f\xc9\x87\x83\xaf\xa21\xa5Ng\x9b\xcd\x9b\xae\xf6\x0eJ\xbb\xfd\x1d&\xf5\x0evv\xf9\xbfLM:\xd8ej\xd2\xc1^\xaf\"\x0e\xf7\x1f9B\x14o\xd3Y#C\xad\xc3G\x99E\xf6\xc7\xa1\xddwlK\xdc\xc6\xbf\xf3\xe6\x96s\x06#\xb0~\xc1L\x8d\x1d\xba\xcf\xb7F`\x8d\xd9E\x0b\xfcrf1\x1d\xc1N\xcf\xe1VK\xa5\xe8\xbd\xa2\xa1\xba\xb0\xdd\x1c\xf2y\x9b\x16t\xe89\x80\x01L;`\x9d\x95\x9c\xe3\xb6\xda\xe9\x07d0n\x85\xf6\xee\x80%G\n\xed\xdd\x1d\xc7\x1cx\x8d\x8f\xe4\x01\x9d\xa2^\xd7\x1c\xda\x8f\x1e9\xb65\xf5\xd7Tl\xb0<\xad\x19\xccF\x81\x86\x1fT\n\xd5\x9b\xcc\xaeW\x00\xa0\xd5\xe4%]\xbf\x89\xd0\xd4\xb3\xe6\xe8\xaa\x81'\xb1\xdeV\x813\xe9~\x95\xea\x10\xd3\x95\x9a]\x8e\x13\xc0\x96#\xe6\xb1\xc7\x05I)|\xd1j\xe9\x99\xda(\xca\xd4of\x9b\xb7\xb9\xf5e\x86\xab\x92X\xeb\xc8\x0b\xff\x94\xc2$\n\xd7$N\x81\xa3y\x1a\xc1*\xf6\x97>\x06+\xc4)l*\xd25m\xf7\x81\xe1\xfc\xe9\xef\xe8%\xe8~O\xe5_\xaa\"t\xff\x01\x17\xa1\xfb\xff\xaaE\xe8\x87\x86\x83]}\xcf\x01\xbb\xab\x03,\x05x\xcf\xb1\xad\x97\xc7\xe7oO\xde\xbc{\xa3\x1ez\x9e\xaa\x9e*\x17\xab\xda\xab\n\x15U\xba/F\x8c>?\xf9\xe1>/b9FxXV&\x1e\xa7\xdd\x17\x8f!F\x8b\xb3) HJ\xe4\xac7\xe3h\x1c\x9fir\xa6\n.W\x8d\xed\xaa\xa7\xa3%c\xe5rP\xc7v\xa6b\xbc\xbb\xdc\xca\x1d\xefF<\x05\xdd\xd1\x80\x1b\xd8\x0d\xad\xe7B\xb9\x98{\xe3\x8c3\xb4'\xc6\xec\x93hzVX\xc0\x8c$}\xac\xcf\xb2\x19\xdf\x16\xf1\xf7\x0c\x14\xc5\x80\xf75\x1c\x1b=\x92\xff5(\x8f\xf6\xf4\xa4b_wEG\x99\xc2\xbeco\xb5\xa3\x16\xb78\xd99\x80<.5T\xe9\x00\x82\xa8\xfaz\xc2\xcc7\xab\x10Gsv\xcfaJ\xa2\x8c\x19Z{\x08\x8b{\xf7`\"\xfc\xb44\x1f>\x96\xa3@\xe1j\xe0w\x94,\xe0Z\xb0d!\xff.\xb2'\xd8\xda\xa7OEk\xfa\x05\x9a\xdcv\x81vM<\x12\xb7\xe3\xb3~\xb1\x1c\xba\xe1\x90\x01|\x99\x1c\xe7\xf7\x8ev\xaf\xc0\xe0\x12\xc2\x9a\x18\\\xce\nS.#f\x96\xec)&\x10Km\xcb\xa2\xfb6\xb7\xfa\xbf\xedT*H\xc5pmWg\x9c@ \xb6I\xb5\xdb8\x95\x92^\xe2\xdf\xf4\x94\xff\x15\xe9)\x0d\xe4j\xb0\xa3\xfa\x1dD-8\x18\xc9j7?\xb1j\xcf\xd19I\xdf\x8a\x8aof\xf5A\x92s\x90pZF\xf7\x94\x0b\x11n\xabqt\x06C\x93i\xdf$\n\x934\xce&i\xc4r\xe3\x83\xe4\xb7_.=(\xff-\x1d\xbb\xc3\xf2g\x9c\x08\x1c@\x06\x8aG\xf3\x86\xe0\xef\xdfzK\xcaV\xc7\x9b\xf5\x9e\x1f\x9d\xc2w\x07\xfdH\xf3\x03\xdc\x15\xda\x97\x9e\xe3\xf2\x93h\x8f\x1f\xad(\x0e\x08\xcf\x94\xdd]\xc7\xc5\xfdLe\x03\x177\xed\xa4,\"\x04\xecUI\xb9\xc0\xf2\x82'\xe2~wQq\xcc8:==\xc9XN\xbe\xaa\x19\xc7\xd1\xe9\xe9)eH\x9f\x93I\xe0\xc5\x1e\x9da\xd5E\xe3\xe8\xf4\xf4\x03\x15\xafx\x13ji\xe0\x930=!\x93T_\xfe\xfc\xcd\xab\xdaB6\x17c\xf1\xbb\xe8\x92\x84\xfa\xc1?\xf7R\x8fy\x11\x92\xf8eJ\x96\xfa6^\xf8\x81a\xe4\x7f~\xf7\xea\x9b\xc3 8\x8a\x82\x80L\xf4S\xa7U\x9a\xca_D\xf1\x92k\xbb\xf5\x15N \xfd\xdeX\xe5\x15\x99\xfa\x9e~\x86\xaf\xfc%\xa1b0.n\xf5\xcb\xd7\xde\x92L_GS\xf2\xca[iJ\xa3\xa9a\xd5\xdfz>]\xb1\x9f3\x92\x18\xd6\xe5m\x90\xcd}\xcd|\xd9{\xc3pN?|\xf5\x0d\x1eC\xfa6O?|\xf5:[^\x90\xd8X\xfc\xd6K\x17\xa7\xc4\x80\x0b\xb4<\xf2C\xc3\x80O?|U\x87H\xa7\x1f\xbe\xca\xfdM\x0d5\xa2,\x9e\x10\x16z\xdeP\x83n\x94\xd3\x05!\xa9\x1e\xaa\xef\xc8\xc7\xf4]\xecM.\x8fL[%\xafa(\x8e\xb2I\x0e\xbb\xbc\xe4\x86\xa5\x0b\xf7m\x0cY\xc98\xf05<\x81\xa9\x904a\xdd\xe9\xe8\xf8\xd4k\x17\xe60\x82\xe9x\xad\x18\x9d\xd2g #X\x8c\xe7\x9a\x92sd\xe7u%\x170\x82sJ\xf1\xcfu\xa7\x11\xf0c\x18\xdd\x89\xed\x0bz\xf6~\xfa\x04\x9e}\xe1\xc2\xcc\x85\x95\xe3\xc2\xc58(\xde\x05,\x07s2\x9e\x9f\xb1\xe8\xbaK\x8d/\x03R\xd6kz\xa2\xc7\x0e\\\x8c\xaf\x99\x1a\x99~~\xedB<\xbe>+\xf4\x99\xd0\x96Z7*}\xb4>9\xf4\xbd\xe1~_\xd5\x05e\x82\x954In\xfd\x9d\x07\xfff\xf9\xf4_\x8e\xe5\x93\x99\xd7pl+\x0b\x93I\xb4\xa2\xd2L\xa22o\x1a\xa7m \xdf\x84f\x01\xfcq|\xc6\xae\x00\xfa\x0f\x1c\xdbG\xef\x8f\xbf\x9b\xf5{\x15I~\x1c\x9f\x8d\xd33\xc5\x89^;\x11\x93~\xbf\x16\xf5\xf8\xa2\xea\xc4\x93\xbb5\xc4j\xbfMe\xb7^\xbe\xa1T\xa6;\x11lV\xe9-c\xae\xf6U\xab\xa8\x19\xbe\xae\xdc\xed\x04\x8ckS\xde\xae\xd8[U\xc3\xb0`M\xab\xaf\xa7\x9ct\xa8\xd6\x91k\xf6~W\x1d\xca5\x17,\xd5^\xe7\xfc\xfd\xae\xd3M\x88\xb2e\x97\xbc\xad=\xc7V\xbe:\xe7,\xb1*\xd5^\xf0\xd6T\xf8\\\xf1\xf7*\x01\xfc\x88\x1cf\xae\x8fW\x8eE\x91\x0c{B\x12\xc5\x91\xf0\x18\x8b\xf8\xfd[\xb9\xe8\x10F`\xf1\x8fp\x87\xcf\xecS\xa5\xd77\xf5\xea\xdb\x9f0\x92\xde\x08\xce\xbb\xb3r\x01\xa5\x84[[\xf5\xaa]\xb3\x7f\x9d\xa0\x8e\xc7\xdd\x98$Q\xb0&\xb6\xba\xa6\xf2CX ZY\xe6\x19\xd1\xdd\xcb\xaf\x01\x93\x15\x99 a9\xab\xdd\xc3\xea\x93\xdao\\xc\x96v5\xd9\xfaA\xb2\x0394zl\xf1\xa58!?1\x86\x163_\x8a\xac8\x0b\x12\xdao\x1cY*\xab\x8a\xe55\x1e\xb27*\xf6\xbdl\x9c\xf3\xba\x9aX\x05\xa4s\xc4\xde\xc2\x98\xaf\xe5\xc9\xe4w\xf1,p)\x0e\xdb\xc1)\xa8\x89\xb4J\x7f\xbej\xa2s \xae\xb4\xd2\xee\xb9Q B\xcb\x14\xc7\x01\xf9Y\xe7\xe1\xbc\xcf'\xfa\x1a\xcb\xe6\xa4U\xa0J\x94i\xf7|\xcd\xe4\xc9>.e\xf7\x1c\x00\xe9F\x97\x18\x94e\xe6\xf9\x9ahc\xea\x93\xe0\xc5\x03\xdf\x1b\xcd\xd5'\xbc:E\xb8\xe6\xda3\xac=\x8d\x96\x9e\xdf\x94 \xc4\xb8\x81\xe5\xc7c\xc1.>}b19)\xec0\xdc\xd8[\xc6E\xd1\xbfF\x18\xa4t\x8b)\xf9=d=Fh\xedoc\x0e\xadY\x97\x84)\x89m~\x81\xe0\xd91\x8a\xe6\x94\xc5\x9du\xc9G?\xb5\xb9P\xbf\xd5sX\x1d\x8c\xb4\xb3\xe2\xe6\xff\x070\xb1?\xda\x16\xdfw\xdb\x93\x85\xe7\x870\xb9\x9e\x04\xc4b\xa1\xea\xe9:\xbe\xb4)\x06\x1f\x087\xd0\xd0\x85\xc4\x85 -N\xb0d\x08\x13;6S\x03P\xf7e#Xp\xfc[\x19\x9f\x1f\x9f\xc4\xc4\x94f[<75\xf4\x08\xc2B\x19\x1d=v \xb3\xc3q\xd4\xe9\xe8\"\xc8\x8a\x87n\x12\x1e\xe1&p\xd4p\xad\x9a\xde\xde6\xf6\xb6)\xfe\xea\xb1QF\xac\x1c\xe8\x7ff\xaba \x9c\"\x1c\xa7\xf2\n|\xb9\xd8)\\\x83Rm\xd0I\xa0\x12\xddS\xad\xb7~\xedJ\x9d4\xc2n-\x05S\xab\xc2\x85t\xcf1S\xb4\x8d?X\x184\x84\x01\xe9\x9e_\xd1\x02\xe2t\xcf\xd7,F\x1d\xe9\x9e',{\x04\xe1+l\x13\x86y\xa4{>\xe1\xc6\x94\xf4\xa0xe\x13\xd4]\xd4\x8e\xfcu\xbb\x91\xbb\x86\xc8g X\x9a\xb0{\xae\x0d\x05\x0f\x18\xec5\x9f\x14\xde\x90\xf39\x19\x8e\xdf\xfac\x17\x03M\xb2\x00\xf6bc\x15\x87\x1fL\xd0\x88\xe7\x82\xeefd\x1e\xa6\xe0\xa7 f\xaa\xa9\xa4\xfc \x9c_\xa2%\xd5A[\xe6 $!\xbd\xf9,<\xbf\xd2zGV\xaaM\x87\xba\x84\x82\xf2c\xe0\xca\xc5\xd3\x8ec\x11\xe6\xa1\xf4<~\x8d\x07L\x1f\xcf\xe6\x13\xfe\xfb.\xd9\x80\x93\"\xf3\xed\xadO~g\x88y\xc39\xfa\x87\x0c\xfd\xfb\x14\xbfC\x17\xb6L\xe3m7N>\xbe\xfa\x89\xb4X\xbf\x86\xb5\xbb1\xce\xbf:o\x85\xc9(V\xfc\x12\xf7\xfaq\xed\x86\x9d\xf2\xa8I\xc7.\x88Ma\xb9`\x9d/,\xc7\xc5t\x14\xae\x1c\xd5\xbaU\x14\xa3\xd4F4a\xed\xe6\x98\"\xfeT\x88K-\xd0O\xca\xf1\xb4\xcb_\xe6\x7f\xdd\xb8\xec\x107O\x92\xa9\xf9r\xce\x0e\xff\x92O^\xf6&\x91U\x97\xe5l\xe5\xebJ\xe5\x85\\\x991\x8a\xc5\x80\x9c\xb2-\x8f=\xd8\xddw\xecc\xd9\x86V\x1d\x1f [\xc4\xfc\x16\xa2\xdcO\xb6\x88uu\xac\x0b\x97-\xac\x8f\xa8\x0c5\xd2\x8a\xa9\xec\xca\x19\xf7\x06\x15\xb0\xca\xb5F\xe5\xd4\x83\x94\x92s\xe9\x07\xd9\x18z\x16\xf3?\x87\nL&R\x08_\x0e\xe3<\xf0\xa8\xa7\x96a*\xdfW|\x1e\x98\xb8>\x14\x12Jy\x9d\xcb\xfb\x08\xd1\xa5\xce.\x03\xca\xd6\x89L\x85\x90\x8f\xd3\x88C\x8e\x12.\xcd\xa4\xa0\xc6x\x1a\x8f\xab\xd8%\xb8\xc2\"];?Q\xf0z\xf45\xc6[\xc8\xb3\xf33&\x05KNx\x89\x8c\xcd\xe7]*s\xfe\xd4\xe6\x828\xc5\x93\xed\x18\x97\x13\x7ff\x94\x83\xe6\xc1\xe9Q\x8d-\x1b\x9e8.\x04v\xd0\xfd\n:\x10t\xbf\xc5\xff\xbf\x80\x7f\x86\xadK\x15!\xdf\n\xa6\xe8\xb8\xf41\xb3&\xb5eZ\xc1\xad\xdd\x1f8\xb6\xfcJD\xa3\xcb\x0d\xddY\xc7\xa7\xa5.%z\xa3\xce\x8d\x82\xa7i\x91\x05\x83\xf4\x93\x8e2\x81\xa4z\xea\xb9\xb9\xb4\xef\xb0\xe8\x9bzD\xab\xc0\xa9\x18\xae\x8dl\xd3\xd6\xa5S;j\\\xef\xa6a\xf3Q]\xd9\xf9\xe6\xc8\xd7\xed\x98'\x93i\xc0S\x05\x92\xf6%\xd3\xd4\x0fv\x1fJV\xf0\x95\xbe\x8f\xbb\xcc\xc0\xb9\x8b;\xc8~#\xa3E\xdd\xb4\xbc h\x9a\x92\xcc\xaa\xeaO=F\xb5L\xf6BxsQ\xaf\xbe\xf1y\x15\xb3\xca&j/\xa9\n::\xd6\xdc'\xcaO\xa4\xb7\x9b\x93\x1f\x8a\xe8\x86\x14\n\xf4YSZN\x8f\x91\xf6zV\xb4\xb0\x82\x11D\x9dN3\x07\x98\xd4\xa4p\x10O\xc8(/#\x81tov:n\xa1-\xa3\x18\x81$\xb2\xfd\x08\x01;\xa6\xacE\"\x98\xf4\xb1w\xc6(\xdf\xf6vFKb;l\xe2\n\x8dB3p4\x97\x9a\xd2\xd6\xbb1o\xf9\xa8\x8bG\x97oG\xddu\xdb\x83%\xf6&\x8d{\xf7\xae\x10\xdd\x8c\xc5\xfe\x06X\xbc9nUW\xbd\xd8vP\xa3\xcd\xd3\x88\xb7P\xbf\x02>[\x81\xd8\xf6\xebV@\"A\xf8\xf3V\x97\x83L\xe9\xa5N\x9dgp)\xdd\x1c\xa0\xda^\n \xc84<S l\xc4\xe5\xb6\xa6m\xef\x97m\xe2\x81\x8d\x9fIN\xb38Z\xdaQ\x83\xad\x0c;7\x07F\x90\xe8ma[[\xd6\x17\x01T\xb6\x8a\xb4\xe3\xaa\x86Y\xe8\xcf\xd5\xf7z~A\x02\x9c\x9e\xd8\xa0g\xbf\x06\xa6\x90\x1f\xb9MP\x85:\x9f\x00\xf10\x0f\x80\xb0\xba\x00\xe2\xd1\x9cj.\x0el\x83\xee3]\x1b\xa9\x1d\xd5\xdczk\xe9\xfa\x9d\xa4\xa9\x90\xc8\xa5\x9e\xcbV=\x00\"-u\xe2\xf4\xa6\xa2.\xe4~\x0e\xbb\xfb\xd2\xba\xc5v\xdc}\x0b\x1d\x88\xbb'5wJ3?\xf4\x82\xe0\xba\xad\xba=\xe3\xb7\xc4~\x1e\xc1\x9aJ\xc2\xe2\x0f\x83\xae=4\xddjk\x98\xdd\xca}q(\xab&\x8d\x96\xd7\xfc3\x8fRGT\x84\x95/R\xea\xf8\xab\xca2\xcb\x8f\xce\x9a\x8c\x8al\x94\xad\xf8\xc2\xe3\xe2 u6\x1a\x96\xf9\xae\xf2\x0b\xa2n\xc5\x7fD\x84?\xd8S\xb0\xf1\xb4\x06\x0f\xd3\xb85\x0e\xd2C0\xd5g\xe0\x86<\xd1\x97\xce\x9eV\xdcB\x87]\x82\x86\xed\xfc\xee\x7fX\\\xc68v\x88\x97$\xcd\xd7\xd2m\xe0\x19\xda\x83\xbd\x01\x8f=\xb7\xc3\xff\xdd-\xc7\xaa\xdb{\xc0\xff\xe5\xb1\xea\xf6x\xac\xba\xfd\x1e\xff\x97\x7f\xbf\xcf\xbf\xdf\xe7\xb1\xed\xf6\xf9\xf7\xfb\xfb\xfc_\xde\xce>og\x9f\xb7\xf3\x80\xb7\xf3\xa0\xcf\xff\xe5\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=\xe0\xed=x\xa4\x8d\x9d\xc7|j\xdb\xc0\xa2\x11\x8b*\xbeNQ\x1ep\x13\x8f\xe3#\x1e\xae\xb2J\x10\xe5J\xd1\x94\xa0\x17\xb0\x82xH\x06\xd1z`\x8b\xd9\xb5\xf71\x9eJ\x1e\x16#\x8f\x1dR!\x8fr\xa3M\x08\x9a3\xb4\xdc\xe4r|\xe6\xe2\x9c\xf3\xccPy\xa4\x9c\x8c\xf9\xe9\xc6\xf0\x142\xb3v\x80g\xb9\xeb\x14\x99\xa52\x8c\xa2\xe3Sj\xd2\xef\xf7w\xfb\xfd\xbe\xc3r\xf7\x8a;\x91\x13/\x9c\xf3K\x11R\x8e-\xbe\xf6\x02\x7f\n\x93hJ`E'c2\xab\xe4w\xd4\x04\x9e\xb0H\x9dp\x80\xb1~0B,\x8b\xe4\xd9\x01\xdb&\xb0=b\xe5\x0e<}\n\xfd\x1e\xca\x14\x7f\x84~o\xb0\x0b\x1d\x16\xffS\x97|\xcc\xb4'C\x9eSP\xcd\x9c\xbb\xe1\x8ek\xc22CT -\xa52`D\xec]\xb5\xc7\x03\x16;\xa3\x1b{W\\\x10\x8d\num\x1dnP\xcc\xf1\x18\x8e\x84\xf0\x14\xbc\xc7\x0edl]x\x08Z2\xf6:\x9d3\x07\xe3D\xdc\x87\x9eF\x8a\xb0\x8e\xa2,L\x0b\xe7\xac\x90\xcc\xbd\xd4_\x13U|\xe0\xc1\xf8\"x\xaa\x1ar\xf1\xc7\x8e\xe0\xe9\xd3\xa7#\xe8;\xdc\x9b\xb53B\xc3#zb2\x07\xd7\x90\xbdz\xac\xac\xd3\xef\xa7\x84\xdb\x948\x17 \xda\x9a6aQ\xb3n\x1b\x16\xb5\x9a6\xa2\x8eD\x97\xfa\xd0\xad\x00\xe2\x88o\xe7\x84r\x93\x1d\xea\xe6\xe1DM\x99/\xe2[\x10\xd6\x18\x97\xad \xac!\x15\x92(\xec\x84E\x0b%\xac\xf1g\x11\x07\x93dBW\xc5\x0b'\x8b(\xdeH2\xa9\xe5\x06\xf9b`\xd4z+\xf4\x96\xc4\xaaK\xec\xf9\xd9\xc3\xbf\xf0\xe7\x1b\x8d\xbd\xcd\xd0Y\x9b\x16\xfe\xf7\x05G\x1e\xf8\xe1\xe5\xdd\x8f\x9d\xb7\xfa\xc5G\x1f\x05\xd3\xbb\x1f\xfc\xef0\xf0\x99\xff\x91\xdc\xfd\xc8\xd3\xf4\xf7\x18z\x14\xa6\x93(\xf8\x12\xbb\x956MG/\x9a\xff\x82;\x96v\x95\xf8\xbf\x90/7 \xde\xfa\x17\x9c\x83\x9fz\x81?I6\x9aB\x9b\x19\xf8\xbf\x03\x16mLvZ\xc1\x1e\xc9\xfd\"&\xb3/\x0b\xf8d\xe9\x05\xc1F\xa3o3x\xd1\xea\x97\x06=}}\xb9\x19\xe2\xb7\x1a\xbeh\xf6\x8b\x8f?\xbb\xb8\xfb\xc1g\xbf\x07\xd5O\xb2\xd5\x17\x18\xf9\xea\x8eF\x1e\xda\xfb;\x8em-\xbdt\xb2\xb0\\\xe8\xd7\xd7\x96\xc62\xce\xebi\x15\x9dz\x88\x88GH\x02i\xddE\xa2/+\x1aP\xcf\x90\xe7_\x0b\xc7\xc4\x9c\xdaB2\x9b\xf7\xe1@\xd8\xd81\xcf\xa8!\x9a\xb7q}n\xe8\x8c\xc9\x99P\xd8\xc7\x95X\x1f\x10n\x9a\xd5\x9f\x03\x93\xeb\x14-\x17\x06\xb7\x00g\xecV\xdd.\xa0\x15D\xa3&\x88f%\x88\xc62D\xe3\x96\x10\x95\x04\x88\x18C\x95\xf9\x08T\xf6\x86\x832rX\xe8\xa5;\x03hB\xbc\xf8\xdf\xd0\xf3\xce\xa0\xb9\n\xfcT\x8b\x9c\x15\xcbI3\x98\xc4EFh\xf7wUc=\x10z\x8f\xeakv\xb9\x867eU\x8d\x885A\xe3\x14\xcb\xbb\xb8\x98X\x92\x89mYt\x8e\x1a\xa4is\x1d\x02\x92%\x9a\xd0\x01\xe8\x03\x01@\xd9\xd7f$\\\x8bx\x12\x9d\xdc\xceMM\x86\"\x7f\xbb\xe5\xcb\xa9\xd3\x8a\xa8x8:\xfdgkf\xc2\x9f\xb80\xc1p\xd3\x01\x0b\x8b_\xe7u\xbe`\xa1;\xfdy\x18\xc5\xe4\xc8\xc3`}\x96o\xc1\x90\x1ey\xd0\xa1e\xcb,H\xfd\xc0\x0f\xb1hY*\xcaB\x1f\xaf\xda\x0f\xc0\xcaJ\x05I\xeaO.\xaf\xe9\xfbk\xfe\xde<\x84i\xbd\xd3\xfb\xba\xbc\x9a\xb4\xb3\xdd\xc1\xa3\xddG\xfb\x0f\x06\x8f\xf6\xd0\x8e\xff\xe9\xd3\xa7u\x0d`4\xd9b\xbf\xa7\xdd\x04\x83\x9c\xbb\xb0\x80\x0eXs\x93\x85\x00\xaa\xfaX\xf0\xaa\xb8\xdc\x02\xbb\xcb\xbc\xe6\xed\xd0F\xfe`\x1fl\xfd\xf0C\xe2X.,t\xd7\xd0\xf9\x83\x0e\xec\xd7\x0c\x17y\xc0\xce-\xdb\x9e`(1\xd4*C\x07\x92q\xef,\xc7\xf0\xa70E\xad\xe1\x8aG3\xe1*\xa4\xa9+>p\x1c\x17\xb6\xd0h\xbf\xa4\xe0\xc2\xc4\x1f\xbd\xb3\xfc\xe2-v\xebY\x9f\xd2\x83S\x0f0\xd0\x00\x04\xf0\xa4\xaa\xe4\xde\x86\xc1c\x08:\x1dG^\x99B\xa3\x16\xa0\x15\xaf\x8d?FZ\xe5w\xe9\xb9q\xdc\xea\xe098\x9e\x141\x15\xf1\xf2\x9f9\x00\xad\xe8\x07\x0c\x12}\x87g\x89\x90\xc0\xc6b\xc5O\\X\xe5\xad\x8e`\xed8\x8f\x1d\xb8\xee\x06^\x92\xbe\xc4\xb6\xf1>\x83\xf7s\xef\x9e\\\xa4\xc6\xf4\x16\x0f\xdf\x8cSv%S\x84\xf5\xde\x9a\xb1\x06(\xc9\xc4,<\x9f>\x01_1\x96\x93G]>:\xe8bp\xb0\x86\x03X\xf1\xb2\x9e\x0bk\xfc\xa42\x02\xc5,\x99\xb9*X=A\x1a\x85\n\xb3\xe7H\x10\xb3[Q\xb6\xf2\x99\xa9\x92+8\x80\xf1\x19\x0c\x05\x0d\xcau\xb1\xaa\x14\xa8\xd7iK,\x82\x81\xe5\xba\x05Su+>@b\xaa\xc2\x82\xa9\x8a+LU\xa8c\xaa\xe2M\xd9\x80z\xe5|f\x87\xf6\xe0a_U3\xfb\xbchg0P\x8b\"^\xb4\xd7\x7fHIL^&\xc6\x80A\xf1\xf5\\\x1a.f\xda=?'\xc9\xabh\x9a\x05\x18G\x1e\x86\x9a\xa5\x98\x92\x99\x97\x05\xe9P\xbd\x9f\xff\xa7\xea/q\xd2\x8e\xfd.\xff\xca\x85\xa8\xf8i\xa46|L\xd5\xbe'\xd1r\x15\x85\x94\x80\xe8F\x06\x98{B\xf8.}\xe3]GYJ\x17\x8fw\xd8\xb4Y\x8a H\xa8\"_Ny\xb7_S}\x8eW\xe2\x82U@\xbcr\x0b\xc2\x03\xc7\xcb\xe1\xea\x9d*\x9aLl\xca\xf9=\xd4\xa1 \x16\xed\xf5th\xc2\x8a*\xc8\x95\xe5E;j\x91\x97\x17\xed\xabEI^\xf4@>\xda\xf0\xd5\xfe\x9e\x1e\x15'\xbf?*\xcej/\x18\xf3\x91\x91:\xc1\x9f\xd2\xde\x1c\x9b\x1dN\xe8\x88\xe3bA\xa6\x16\xd8\xa4{~\x8e\xce\xe7\xe7\xe7\xc8&\xf4\xdc\x02\x1f\x1d\x9b8\x0e?\xadX\xf5\xfcxTE\x0c\x1d\x98h[\x9e\xd4\x96\x0b)\x1fFTz;\xae\xce\xe5\x92\\\x0f\xc1\x8aI8%\xb1\xe6\xa6\x94\xe3]#3\xb0\x96\xf3c\xac\xe2he\x88?\x03\"UFwN\xd2#\xb1\x85\xcduYd\xf0dE&,!P\x14\xd74\x1c\xb3\xd0\x1fq\xdc\xa2.\xdd\x13\xc4\xb6\x8e\xa20\xf5\xfc\x90T\x1cn\xe4'buO\xa2\xab\xbaZ\x99h1\xa8\xab\xe5\xb1Z\x18\xb57\xb10\x9c\xa9\xb9\xf2\x84U~\x17\xad.\xbc\xb8\xa9\xf2\x8cU~\xe6%\x9c\xde5}\x10\xb0\x0f\xa2\x90r\xeb\x1f\xbc\xc0\x9fzi\x14?\xf3\xa6s\xd2\xf4)&t\xe8\x06\x917\xf5\xc3\xf9i\xea\xa5Y\xa2F\xb2\x97\x9f\x05z/S~\x89\xdd\x9f7\xb0\xf7\x94GZP\x04\xb1\xad%I\x12oN\x90+\xb24J\x01(6A\"P\x9d;T\xf2\xdcQ\xb6o\xf2\x94\xa4\xcf$\xf0\x92\xe4\xb5\xb7$C\xb0\x92+o>'\xf1v\xe6[\xda\xfa7.L\xe0\xc0\xd8\xcf\xc4\xc5$l\x0eO\xc6\xe6\x82\xc5\xe1c!_\xb4b|\xaa\xfe[\xcc\xed\xddv\x9c~8\x8b\x8c#\xbc\x93\x1e\xf8\xc0\xb7'\xf9\xee\xf8=\xba3t\xe2`\xf8\xb7\x99\xe7\x07d\xfa\xaf\x12\x94\x8b\xdd\xd6\xbd\xa5~\x1a\x10c\x0f\xd6\x0b\x04\"\xa4\x11\xd0a\xc1\xe1\xdb\x97\x80l\x88Oi{\xd7r\xcc\x83\xf08rKkq\x84\xae\x95_dE\xcc\xe4\x013A\x9b\x18>\xf1,\xbd\x8f\xdf\xfa\xd3t1\x04\xeb\xe1\xc3\xde\xeacM{\xacz<\xf7\xc3o\xc8,\x1d\x82\xe5ei]\xffE\xfd\x13\x7f\xbeh\xf9AJ>\xa6\x87\x81?\x0f\x87`M\xd0\xdf_\xbfDP9\xdf\xf3\xb7\xff\n\xb01&\xcb(%\x85\xc7n#NZ+\xcb\xe5\xa4v\x8a\x88\xb9\xb5B\xe5_\x92MD,\x8c\x06\xcc\x9cq\xac6\xf7\x11\x89\x1eL\x15\xb2\xa6\nA\xbes\xaa:\x0dE\xea8+\x85H\xba\xb1\x8b&sNIb\xa9\x89(m\x1bl\x8a\x8a\x90;\x15\x8f\xa5\x81\xd3\xd5\xe6Am\xd3\xa2d\xdc\xa7\xcf\xff\xd6\xdf\x91\xad\x96\xa9p\xf2\xc8\xb1\xadrGV\xb3\xf4g\xe6\xd4\xa5J\xbe\x92\x86\x14\xe06\x17o\x83\x87{\x1a\xc1J\x02\x93^\x1ely\x01\x12\xabb\x9f\xa8^\x8c\xb3\xcd0\x8ba\xf5U\xeb\xce\xc2\xabk\x8b\na\x94\\\xb3qWvmy$C\\\x1d\xa7;\xdb\x10b2\x10*\xed3\x89\x8c\x02U\xbd\x8d($\xbaas\x0e\xb6\xca\"=b\x0ey\x0f\xf7\xaa\xfew\xbd}\xa7;\x93\xfd\xe8\xdb\xb4\xd8r\x12\xaa\x01\xeb\xe7Mb\xf0\x88\xbb!>\xe2n\x86|V\x83G\x0ft\x9b\xf4\xf4zy\x11\x05m\x9an\xb2\xf34\xd8\xe1\xaa;\x98\xdby\x1a\xbc\xad\x0d\xce\xd6\x03\xb5q>\xfeG}\xa7\xfb\xf5\xf1\xf7\xe5\xb2 /S>\xe1\xa9\xe5\xd4\x1eXj\xb9G\xeaxXn\xb9=\xf55\xcf-\xa7\xbc\x9d\xe6HR~\xbf\xe6\xefU4\xbd\xe6#T=\xe4\xe6\xfc\xbd:F\x9eV\xae\x82\xed\xec\xb5\x1a\xfe\x92\xa5\x94\x1b\xe83\xcaU\xb0\xed#\x9b\xa8\x1a\xfb\xee\x94\x81E\x95\xd6\x8e\xf9\x08\xd5\xea\x87|U\xd5N\xdf\xb0\xf7j\xf5\x9f\xf0u\xc5\x0d\xf5\x12Fp\xa8\xe6\x90{ #x\xa3\xbe|\x85i\xe1\x94\x97\xefP\x1ed\x18].9\xc2\x92\xbf\x9c\xbey]~\xff\x16FpD\x8f\xf2\xa3n\x82\xaaW\x7fv]\xaeqB\x05G\xdb:_\xf8\xd3) U\x11\xfc5+M\xa3\xb7\xb1\xbf\xf4\x99\xadv\xb9\xc67\xe8\x00\xa6\xcd\xb9_\xae\xf8\x9c\x92{\xdbJp\xf4\xdb1\x99\xfbI\x1a_\xab\xcd\xfd\"\xd7\xaa\xa4\xb9|\xc1J\xa3\xd5\xb6\xa1\xc2{M\x12\xf3r\x8dg\xa6\xf8\x01\xef\xca\xf5~F\x88\xfe\x955V.\xfa\x1eF\xb0\xf53F\x0e\xffY\xca\x08\xa0\xfc\xdd\x9d\xf9\xe1\xf4h\xe1\x07\xd3\xf2\xd7\xdf\x02\x8f\xf18\xa9w\x8d\xe3G\xdf\x03\xd8\x1a\xc1\xa9\xfd\xd2\xfe\xfb\x0d7\x0f\xd33\x91\xed\xe2\xb1@\xd1\xf0K\xd9\xe4\xac^0\xe0\xda\xac\x07\xc6J7N\xd7\xd3\x16V\xd9\xf2\x1bG\xad{\xe3\xc8\xd1\x0f\x0c\x8c\x00H\xa4\xf8\xd2~\xaf\xbf\x9dE\xd7\xd5) HJ\xe0\xfd\x98\x9c\xb9t\x92\xbc=\x1e8,\xc5;\x8a\xf7\xf4\xe7Kl\xa6\x12 \xf9\x06\x86\xf0\xb2\xbcd\x1fj\xb5\x9e \xd9\xd0\xff\xc2|\x0dO\xedw\x05\"\x98\x0d\xd8 K\xa5\x9bV\"|\x96\xbb\xff\x1aF\xf0\x8c\x8e\x98o\x8b\x12\xd6v\xc5\x91]\x02b\x0dBi\x1aI+\x00h\xd5R)\n\xf3\xbb\xba\x19|\xd5\x82\xd5+5<\x12\x8b\xf4\x95\xfd\"_\xc0%\x8b\xf2\x0f#\xb8\xe2\x19\x8d\xe8;Z\xe2\xdb\xbf\xe0\x9d\xdb\x01\xc6c\xc8 \x10f\xe4\xa3\xfd\x9d\xb0\xbc\x93\xe3\x93\xb31a\xb7\xa6\xe2\xf7\x88\xe7\xa8\xc0E\x0bM\x1b\xa1hr\x08\x1f\xed\x1e&\xb6\xd0a6\x0c\x8b\x0e?}b\xd8w\xe2\xc2G\xbb\x8fyv)\x7fR\xf4K\x87\xffm\x0e\x0d\xfa\xed\xcb*_\x0bU`\xfe\xa1\xcd]\xe3R\xeb8\x91;\x93\x87\xcca\xfc\x9a'\x82#th>K}\xc2\xa21\x8a|\xdf\x11<\x05\xff\xb1\x03_\xd9)\x83R<\xf61n\x00\x19\x87\xba\x10\x96b\x05\xeb&\xf0\xe7\xd6\xdb\xe9\x9b\xd2](.|\xcaRY\x19{\xde\xc2\xda\x05\x02!j\xb0\xbc\xa3[>E\xa6\x94\x19\x04\xd8[6#\xd9\x85\x0b'\xff\xf3\x17\xf1[\x94p\xecY\xf8 ]\xbc\xf4\x0c\x0b\xd5k\xd9\xf2\x14\xff\xd2f\x8d\xfc\x19s\xdc\xbd\xd0\xe0\xb5\xa0S\xf9\x90\x08\x1f\xd2\x0b\x16bY\x8f\xa7\xc2n\xe6\xd2\xae\xb1_\x11\x80\n\xab\x8dW\xb6\xca\xa7O\xca\x8e\xe2x[\x8d$sS\x07\x8e\xbf5\xae\xb8\x1a\xee\xe2\x95}\xc1\x9c\xa0c\x1e\xc1 \xe2\x11\x0c\xba\xa5\xdc\x8fl\xf4\x94\xd9b) qe(e;\xc9\x7f%,T#\x0bDa\xc6\x9b\xb8n\xfc\xdfm<~N\xc2\xd8\xf8_a\xe0\xa1\x170\x04>\xa9\x88OJ\x84\xee(&\x95=v\xc4\x9a\xe0f\xcb\xc4\xacB\x8e\xc1\xef\xc5jElJ\xbf\x8cI\xcd>\x8c\xca\xb3*\xea=\xc3\xa5\xf5l\xfb]]\x14,\xc4P\xba\x9ddB_\x0d\x99n1\x96\xb4\x88\x0f\"\xe5(\xaeDN\x17W^+\x9d\xcfX\xaf\xe43\xd6\x93\xbc:\xdd\xca\x14\x89\x94\xd3\x01\xc9\x19\xa9\xac4\xca=\x04\x9b\xf4E)K\xc4\xffOr\xd3\x87\x98\xb4\xe8/.\x15Q`\x04_a\xc4\xa1\xbd]\x07\xff:\xc6\xff\xff\x8d\xbe\xdb\xe7\xaf\xfe\x8c\x15z\x0f\xd9_\xdf\xf1\xf4\x97[\xa1\xfd\xf0!\x02\xd5\xa3\xb3\xb7t\xe2\x82\xe5\xd2\x8f\x91\xbcL\xbb\xf5\x17\xcd|\xbc\x1f\xecEIuE\xc7\x9b\xd9\x19&B\xca0\x11R\xc6T:\xcfTh3\x84\x1dJ\\\x8bl\x17\x90o\xe6\xbfRaa\xe1%/9\xfa\xbb~r\x14\x85\x13/=]\xc5\xc4\x9b\xa2\x90#\xf8/\x17\xcd\xce]n\n\xe623_\x97\x87rt\xd1x\xc8\x95\xe4(W\xac\xcb;o\xee\xca\x99\xfd\xb9\x9d\x91\xe5Z\xf4\x18H\x19\x85\xf8k\xb1E\xd2\xf4\xb1\x03\x0b\xfb\xaf\xe34-'\xbd-HP\x8a\xd9J\x16\xdd$\x8dbB\xa95o\x85\xa4E3!mfm\x93t\x1c*\xedP\x08\x9e\x96`\xc7\xf7w5\xa0Q\x14\xb7d\x15}\xfb9=\xd3:#4^<\x80\xe7tO\x0d\xd9?\xa3j\xea]\x85\xfc^\x92\xeb\x17\xcd]\xa19\xe7\xd7h\xceY\x9b\xd3\xc1\x03\xc6\x01W(\x13\x94\xc3\xed\xf8!<\xd7\xdb\xd3\xd1\x9e\x9e#\x177\x92\xe3\xbb\xd72\xf1YBNI\x9a\x92\xb8AJ\xfb^\x17I\xb2\xd2\x92\xbf\\\x05M\xf6\x05\xdf\x97\xb3\xd7\x01\x94\xf5\xba\xaen\xa1\x0d:O\xa6\x9ao\x91\xca\xaej\xe2F\x99\xf0S\x1b\x93\x96\xfd\xc1>e\x9cN\xedb\xab\xfa\xd5\xafj\x8a}\x92\x0c\xe1\x0f\xe5\ns\x92\xbe\xb9\n\xc5\xf7\xcfI2\x89\xfdUJ\xd1\xe7/u\x15_{K\xda\xd8\xdf\xea\xea\xb0m\x90\x0c\xe1\xbb\x12\x1cQ\xc1R\x06\xa6\xbd\x85\x07l\x8d\x88/\x8e\xc1wjxL!\xa6\x8d\xc3,\x08\xce0\xfe\xcd[[p\x9d\xd6\xdfo\xf8\x9b*\xec\xbd\x8a\x11\x8f\xf2 [\\\x85b:.X\x7f9}\xf3Z\xe3@\xce\xf5EM\xfb\xae\xc4\xfap\x86-=\xe3Y\xe4\x1f\xebb7P\x81\x82sd\xc5a\xef\xebSx\xf3<\xaf\x9c\x1d\xea\x9f\xb9`\x9f\xdb\x95\x94?\x9c\xc1\xffZ6\xe6\x9e\xf3j6i\xc3\x8c\x8b\xbe\xb4\xba!\x16\x1a\x08\xf9\xcc\x8au\xa6\xe3\xd2~\x89c \x03\xc0\x91\x84\x8e\x9dN\xc3\x85\xb7\xdc`\xe9\xa8\xaaz(\xa1\x95\xa4B\x18\xbfFV<\xb4\x07\xfb\x8e\xacZp\xe1u\xa9\x1eK\xc2\xf2f\x86\xd9\xe4\xde\x15\x84\x1b\xff~\xe5\xa5\x0b\x17,\xfa\x0f\xb7S\x81\xc0\xe6J\xc3\x1c\x07\xb6z\xad4\xff\xd2\x0d\xd6\x9ec[K\x92z\xba\xd0\xbb\x1a\xe5m\xa4\xd7\x9a\x8b`\xa4\x8e\xaa\xf3\xf4\xaav\xebI\xa1\xe4\xf3\x93\xe3\x8f) \x13\x9f\xca&\x9f>\xd5\x13D!\xf8\xd4R\xd7 \xa5\x9a\xa8]o\xa5\x9eK\xec\\\xddH\xd6$L\xf9p\xa20\xb1\xa9\xc0\xaf\xec\xc7rW\xf5<\x0e\xe0Q\x9c\xa2\xf7\x91I\xdaC\xb5\x9c\xbe\x90>\xfe\x10\xac7\x16t\xa0\xd3\xf1\xaa\xbc\xa4x\xae\x86j\xb0Z\xf1\xe8\xb4wu\xb0\x0b\x94\x1cR\xd5\x91}}\xfc\xbd68\xf9\xeb\xe3\xe3\xe7C\xd8\xeaWKf^\x92~M\xae[\x9c=\xa0u\xe9\xd0\xa9\xbb\xb85$s$e\x86Fr\x99u\x8a\xde\x14o\xd1\xcd\xc2\x90C\x81e\x01\xc0\xe51J\xe3y\xbd\xa44\xa0\x17\x06{\xac\xbcz\xe1\xb9b\x1d\xd7\xd4\x9d\xa9\\\x93x\xf4\x8b)x\xfcq|\xd6\xad\xe6\xce\xd7\x84p\x9b\x93\xf4[\xe2]n\x02\xf9[\x01dK\x1f\xe3\xa5\xa8M\x8c\x11\xab\xe5\xe73\xc0q\xd5\x06\x1cQ\xf8\"&\xe4\x97\xc6d\x82P4>\xa1\xc7F\xd0\xa5\xc8\x8d\xe6\x146?\xa68\x98\xe8\xef\x19rD\xed\x0c\xab[\xd3\xe4\xca\xbd\x93\x08\x19\xa4'\xc6\xfb\xa6\xe4G\xe6\x89\n\x05]\xac\xcd\xd4\x16\xb2\xc0\xba\xe5\xb5\xc2\x83\xbc\xbaB9\xf7\x90\xb9\xfc2\x94\x02\x84\xf6\x1eug,\xa1J\xef1x\x05\xf30y\xec@\x92g.\xa7\xe7\x867\x9e\xa0\x96\x04\xe5{\xe4*2=O%\x19\x89l\x06\xd0\x87\xfb\x06\x08\xb1\x08\xef~\xc2RY\xc9\x07\x90If\xb5\xb0*\x92\x9c\xd8\xbe}\xa6\xab\xca\xed'_\xe2\xbd\xea \x1a\xb1\x1b:!oV\xcf]+b\\\xbfD\x06\xaf\xfcp\x1a]Q\x88\x16\xbf\ns\x17\x95m\x86\x83\x9aB\x9b\xb5@\x05\x80\xb1\xce+\xa0\x9d\xa8\x8f\x81v\xad1\x1b)|\x8bM\x9e\xe1\x88\xf3Di\x8d\x17 \xe6\xbc7\xb9\x94\xaa!!\xcd\xf9\xe3\xc5\x10\xb9kQ\xa3\xbd\x92\xcdS8\x97\xedn\xf4\x08\xe0\xc0\xdf\x1b-\"\xfa\xbd\x07\x8emy\xc9u8y\xb9\x91\xfd\x86\xf8\x94%GA\x1dL\xab\xef\xda\xd9}<\xba[\xbb\x8f\x9d^\xaf\xc6\x08+\xf9\x0c#\xac\xaa1\x90Y\x12.\xf73\xc4q\xf51\xa7U1\x9fV0\x94\xb6\xb2J\x95}\xbd5D\xd4F\x8c\xa1T\xd6G\x12\xba\x15S\xf9\xe7\xde=4\xa3+\x07v.\x14#\x84eCe\x11\xd9\x12\x92\x82\x97@.Ml\xa9\xe1\x18\xf44\xb0\x02\xa0!h\x17\x05e1+w\xe6\xb0\xc0\x0f\xe1\xef7\xd5\xbb_m\xca\x1b\xf3\xde\xb5\xf9\"R\xd1\xe8\x05o I\x82\xcb\x0d6\xba3\xbbb\x12\x00\xd28XF2\x188\x0e\x1d\xc0\xf8\x8c\xdf\xc5(Yf\x91l\xdf\x86:\x10}f\x8a*W\xc2\xc9\x88\x0c\x0d\xa3V[(\x95Y%\x96\x0f5\x95\x1ceF\x10\xc2\x90\xe5\xc0 \xdb\xf0\x17h]\xb0\xd5wL\xfa\xf6\xc9\x82L.\x87\xd2uB\xabM\xdb\x8aN\xecT\"\xe2}.\x9d\xd8\xfdlKD\xc3!\x14s\x1bUVg\xb3\x81\xdd\x8e\xdc\x08\xc5\x1bZ*\x15\x1d\xb6\xa20M\xf6l\xbb\x06\xdb\xd3==\x97\xb8S\xb1\xf2b2\xfbN_\xb5\xf2bl\xdc\x8e\xfa:\xe1\xd5u\xe9\x89\xe9{\xb5\xf9\x19\x7f\xaf\x0e'\xe0\xcd\xab8\xba\xc2Li%+\xe2r\x85\x85T\xe1\x857I\xa3X\xb1\x85\x9a\xb2\nA\x14\xea\x1bXW\xe3@\\7\xca\xf0mn\xc4\xe7Za\x19\x8d\x87b\x12\x9aD\xfc\xa5\xb7\x1aB\xd4]z+\xbdp?\x8b\xe2co\xb2\xa0u\xf8O}\xbdI\x94\x85):\x1e\xd3\x1f\xfa:i\x84\x04\x90\xd6\xe2?\xf5\xf5\xa20\xb8\x1e\x82&\xe7Y\xb5zn\x9c=\x04\xbf[\xe3\xd3\xf66\x8bI\xa9n\xe9E\xb5~ \x03\x86\xa0\x01\x8e\xbc\xc2C\x98V+\xf8 \xfau\xe5U\xbcn\xf9\x8df\x90q\xb4\xa2\xc7j2\x04\x8d\xf7\x1c\x1b\xd2Q\xe0%\xc9\x10f\xa6r\x8e\x93C\xd0\xac\x13\xab\xf1\xca\xff\xe8\x87C\xd0\xc0\xfe\xf9\x9bWC\xc8\xaa\xef\xd7$N\xfc(\x1c\xc2\xa4Zv~\x9e\xe05\xd6\x10\xd6e\xe4\xd4S\xc8V\xa99\xea\x89\x8e\xacQ3\xf4\x12\x7f~/\x94V\xe9y\xaa\nM\xe2\x02\xb0\x81\xb2\xf5T\x0e\x96\xa5\x13M\xaf\xa2C\xae\xb6~\x1bE\x81\x9a\x8e\x14g\xd1\x9dEY\\W\x8bR\xbd\xfb?\xdc\xef\xdc\x9f\xeb\\{gFA\xc8\xb6,\xe8@\xea\x94\x82\xbd\xff\xe1\xde}K>\x8f\xaa\x0d\x06\xdas\x0d/|i\x1df\x85\x86\x7fN\xa20e\xb9\xb9H\xfe&c7\x88\xb5=\xact\x0b\x05\xd2\xb2\xa4\xd8\x93f\xb3a\x19\xefV\x91\xdb\x99l\xe7c\xc3)\x1b\x88\x9c?]7\x8e\x85\x18\x87\x86\x93\xc4\xe9\xc4$a\xde\x1fb\xc6\x97\xe4\xfamLf\xfeGi\xce\x1c(a\x05(\xf1F@\x996\x03\x85\x0d\xa7\n\x96\x0cK\xf3\xb1U+x50Md\x98j\xa8 ;\xe8(l\x13\x05\xb6\xe5\x05(\xe97\xec \x95\xb1\xd7\x14\xe3b\x84o\xd4M\x17^z\x82\x88\x99\x08d\x17\x8e\x9c\xb05b\n0\xdbW\xa8'm\x87\xbe\x9f\xa0\x9a\x08\x89\xf1a8=a\xf8\xfc5\xb9\xa6\x1dd\xd0\x01{kB\xe7\xcf,yP\xb9C\xff\xc2\xe4\xf2\xf8\xeb\x00,\x0b\x860\xb3\xf1O\x87\x8a2\xf7Qg\x1b\xa2\xe1\x10S\x05M\x9cztYK\xe8\xe2V#g\xacy\xd4\x0c\xd5\x89V\xcc\x90\xdd\x0c\xa1hf\x87b\x08U\x83\x17\xbaV\xe8\x9a\x8b\xa4`j\x13\x8c\x8c\x81\x1d\x96+\xa3\xc6\x7f\xea\x82\xe7\xb8\xb0\xe8\xc6$ ^Bl\xaf~\x0e\xd7&,\xe34\x83\x0eVj@\xfc\n\xa4\x8b\xa3)\x11\x06;u\xf6@\xa5\xad\x81\xee[\xca\xee(\xbd\xacl\x10\xba(\xdetJa\xe0\x87\xf3w\x91\x1d\x88\x89\xdej \xf9F\x96z\x95\xf7\xb2\xf4\xfa\x0e\xc7\xbcp!Q\x04\x8c*\xfb\x96\xb3^u\xa7\x98xP3J\xf1\xa9dM\xa0\xb9x\x10D#(c\x92.\xc9:\xe2\xd1\nS\x17@\x90\xe3\x91z\xdfX\xa6\x0c\xc8O~\x91\x01\xeb\"p S\x01\x9b]q\xb1U\x10\xa6\xda\x0d\xc3|\x19\xa6\xd1\xb7~\xba\xf8Z\xac\xf6\xcb0%q\xe8\x05CX+\xc7,\xe3m\x1b\xf5&B\x87G+\\s\xd7\xc3\xbaA\xe4\xfcp=\xf3/\xf4\xe4M\x00 \x02\x00z\x92Z1\x10/\xf0\xf3\x8b\xf1j\xa1\xbd\xaf\xd31\xdb\xa1M%\xaf\x86y\x0b\xc3\xc1\xae\xd0\xa0Pl\xad (\x07\x12\xac\xaa\xdf\xad\xa2\x95)\xf3\xb5\xc0=\xdc\xbd<\x12|\x15^P\xa7p \xc9\x15~_1B\xaa\xd5\xbfi\x95T\xb2\xc2\x08\x0d\x0f?}\x82\xd8\xb6\x06{h\xcb%\xd16\xdbq5\xf3\xe4w\x1cOx8\x90(\nN\xfd_\x880>V`B\x0f\xb7z\xb3\xa9\x0c\x934\x97^yZAS\xa6o-\xf6\nH\x96\xc6\x86\xebQ\x01\xda\xd2\x98\xb9\xd1kXP/\xb4\xeb\xf8\xf4 2\xfa6\x9f/3:\xce\xff\x1c\xb1\x8cp\xa1\xa0b0\xa2g\xa7\xc6\x02\xb9\xca\xe7P\xce\xa2\xc4\x83\x0fU\x80\xd0\xa7\xc2\xcf\xb7\x84\xc1m\x90\x1cd\xd8m\x82\xe8\xa0Cv\x11\xa8P\x07\x0e\xd0\xe2<\xe8\xf0\xbeb\x92\x05zp\xa6\x8b\x98T\x00\xda\xe6\xc0\x80\xcf\x84V|'\xd0\x8a\x19\xb4tG\x8cx\xda\x03\xac\xe2\xa5\x01z\x98U\xe5\xc0*\xc8\x0c:o\xf8L\xa8\xf9w\x025?\x87\x1a\xe3&\xaa\xb6\x03\xb0)\xe0*\x86O\xd5\x16\x0c\xe7\xdag\xc4\x0fk>\xd7\xfa\x05\x1f\x15?f${\x1f^\xd7\n\xb3\xe5\x05\x89\xe57\x05Ty\x17\xa4\xfb\x87?\xf0\x91\xd1wE\xfe\xf4\x99\xcd8V\xcb\xca\x93\x87y\xd0\x81 \x9dp\x0f\xc5`\xc7\x05\x8d\xc5\n\x9dqM8\xd65\x8a\x9bR\x93CLd\x93\xe8\xa1R\x96\xd0\x89\xc6\x1f\x01d+\x8bkfOq\x0dO\xf2$<\x8f\xe1\xba\xd3q`\n\x9d\x11\xa4\xf6\x8a\x9e\xc9\xe3\xeb3\x17\xd68\x97\x95\x0b\xd7\x0e_\xbd\xea\x0808\xa6\x99C\x98\xb3,\xa5\x06rC\x87?o\"bK\x17\xdd\xc0\xe7\x9c\xbb\xab\xa1\\\xd8\x1c\xbb\xe8\xec\x920\x8d}\x92\xe8\x81!\x9e\x1c(\x17\x0c([\xf6\x12Fp\x8e\xa9\xe9m\xc7\xe9N\xa3\x90<.\x01f\xc9\x0c,%\xd8\\t:f\xe8\x88\x87B\xa9y$\xc6\x01\x98\x01$\x1e:\x89\xabb|\xe6\x91\x88\x07\x0d:lifWhZ\xbbF\x03fN.\xae\xc6\xbd3\x87\"\x9e\x98kO\xcc\xb4\x1e\xac\x06[B\x86+\xb8\x91K[\xac \x01>\x1a\x92\x91\xc9\xcfi\x11+\xba\x0eCb\xdb\xda\xe9[naG\xc2n\xdd\xce\xd8HN\xe1@\xec~\xb8\xf2\xd3\x05\\\x92\xeb\x04\xfenAG\xdcg\xd3\x176qx\x9a[\x17P\xd9d\xddX0\x84S\x17>\xb65?3J\"\xd3R\xc1\x0d\xa5\xb8\x96\xa5\xf2\x1a\xadn\x1b\xeb\x8f@\xad\x8d3\xf7\xe1\xbaw\x8f\xff\xca\x1d\x8b\xabg\xa5\xf5/\xff\x92\x07\n\xd1\x9f\xd3f9)\x97\xf2\x80\xc5\xcdEg\xc3\x18\xcd\x9b\xd3\xb1\xafZ\x80\x1b-\xb2\x89\xc6\xdc\xfa\x0e S\x1e+\xdb\x08me|=\x1a[#k\x08\xd6\xa8g\xc0`k\x88\xc5\x83j\xb8\xa7\x1b\xa3\xc6\xc0\xfa\x03\xc5\xc9\xcaE\xc0\xfd\xf1hxv\x7f\xde$\x9aK\x0d\x91qzV\xed\xb7^\xa6\x0c\xef\x06(=\x9c\xb6 (\xa3\x01-\x1en\x02\x14\x06\x0e\xdb\xea\xb2\xcd\x9c\x8e{\xe8\xe8Ma\xc5\xfe\xee\x9f\xa1\x8dD\x92]0.\xc0\x1e\xd0#Z~\xd1w\x1c \x9a\xf6\xa8\xf7i4p\xee\x1e\xa0\x05\xbe\xea\xf7\xce\xdd\xdc\x80\x0d\x9c\xba\x9bn_\xaf\x07\x18R\x12Y\xb1\xe4\xc7\xa2\x8b\x8b\x98\x95^\\h\x83~z\xd3iL\x92\x84\xd5a\xbf\xb5\xd5b\xc2{\x89\x89\xbe\xa38\xf5'\x01\xe1u\xf0\xb7\xb6Z\xe2Oy%\xfaK[%\x9b\xfa\x11\xabB\x7f\xe9\xaa\\`\xf1\x85\xb6\xc8KX\xfb\xf4\x87\xb6\xc2\xd4g\xe5S__\x1c\xf1b}\xcf\xfe\x9c\x15\xfbsmq\x10M.\x7f\xce\xa2\x94\x8f!\xffS[9\x9a^\xb3j\xd1\xb4\x12P\x05+\xb0\xa5\xd3/\xdcE\x96\xa6Q\xc8*\xe0O]\xa5\x89\x17\xae=\xb6\xb8\xec\xa7\xbe\xd2*\xf5yS\xfc\xb7\xb6\x9a\xcfgE\x7fh+D|i\xe9\x0f}\x85\x80\x97kc\xc6N\xa2`\x1eG\xd9J\xd4\xc1?t\x15\xa7^\xca\x90\x91\xfe0U\x08\xfc$\xcd+\xd1?\xb4\x15\xa7\xac\xcaT[H\xd8p\xa7D;\xdc)I=?Hx\x15\xfc\xad\xad6c\x90\x9d\xce\xb4P\x9d\xfa^\x101\x9cb?\xf5\x95\xd6\xbc\xc6Z[\xcc\xc7\xa9\x1f&\x87\x82v\xfed\x89\x85d\xa9/\xbc S^~A\xb4 \x9a\xf9$\x98\xa2\xe9`l[\xe2\x0f}\xc5\xb9\x8cf\xc5\x9f\x86\xcaYLD\xc5,\xd6\"\xd3,\x8a\xd0+\x93V\xc2\x9f\xfaJ\xf1\x92W\x89\xb5s\\\xf4\xb1x\xd1\xd7\x16\x0eX\xe1@[\xb8\xc3\nw\xb4\x85\xbb\xacpW[\xb8\xc7\n\xf7\xb4\x85\xfb\xacp_[\x88V\x1f\xb4\x98x\xda\xf5\xa0\xef9P\xd8Om\xa5b\x97-\x8c{l\xc1[\xd1\xb7\x90.\x19\xca\xd1\x1f\xba\n\x8c\xc4j \xac?\x8b1\\&-\xc7\x9f\xdaJK\xb6%\xfc\xa5v?\xf8\xe1*c8\x87\xbf\xf4U\x12^A\xbb+//\x18 //\xb4p\xbc$\xd7s\xc2P\x95\xfd\xd4U\n\xbc\x0bN!\xf0\x97\xb6\n\x99\x93\x90\xf5\xc4~j+1h\x05Zp\x05~x\xc9\x8b\xc3K]\x85\xa5\xe7\xb3\x81\xd2\x1f\xfa\n+^\xae]\xe8\xa5\x17_\xf2\xf2X\xdf\x01 3V\x81\x84\x99\xa9\x82\x9frR\"\xfe\xd0W\xe4t[\xe7w\xc8+p\xec\xc5_\xba*\xa1\xc7Ha\xe8iIa\x181\xbfaV\x87\xff\xa1\xab\xc8\x04F\xac\xc6\xc5Z]%\xb6\xbc\xfa\xe3*Z\xa5\xc5F\x12\x7f\x18*\n\xba\x17\x19i^\x94\xa5\x02\xa7\xd9O]%\xd6\x97\xb6\x93\x95\x17{l\x05\xf0\x97\xb6\x8a?I\x05]\xe5\xbf\xb5\xd5D\x15Sq4\xcf9F\xf1\x87\xae\xe2\xcfX\xe3g]Q\xcc&\x12kg\x123(\xc4Z\x08\xc4\xd9\x05\xe3\x99\xe8\x0f]\x056.\xed\x80\x12o\xc9\xfa\xa5?\xb4\x15\n\xd41#NB&\xf9r\xf2\xdf\xfaj\x81\xc0/\xf6S[i\xe9\x05\x0c\xc5X\nN]\x15L\xa3\xc4\xea\xe0Om\xa5\x95\xc7\x07\xb4\xf2\xf4\xa3I\xe3(d$\x95\xfd\xd4W\xba\xe6\x0c<\xfe\xd2V\xc9\x18\xeb\x9ddZ\xe6;\xc9\x96K/\xbe\xe6U\xf0\xb7\xbe\x1a_\x07\xfd~IY\x1c\x95\xd8\xb6R\xe6\xdb\xa2\xa9\x92\xf3\xce\xa9\x89yN\x19\xd9M\xb5$7%\x1f\xd3\\\xa4\x11\x7fh+R\xde\x82\xd5\xa2\xbf\xb4U\x16\xac\\\x9br=\xcd\x8f\xec\xd4tf\xa7>?\x0e\xe9\x0f}\x85T\xc0\x03#L\xeb\xaa0\xaa\x99jIf\x1a{\x93K^\xeeM\xb44\x9e\x11x-u\xcf\x18\x82fZ\xec\\{\xac\xe3\xb5\xa7\xedy\xedO \x13\xa7\xf0\x97\xae\xca\x15\x17r\xae\xf4R\xce\xc4\x8f\x85T\xc9~j+\x05\xfe\xea\xad\xc7\xd7A\xfc\xa1\xab8%3\xc1\xaf\xcf\xb4$\x82\x04\x81\xbf\xe2\x02$\xff\xad\xab\xc6v\x92\x9e5Yzs\xce\xdd,1\x93C\xb5J\xe0\x87\xac\x06\xfda\xaa\xe0\xc5_\xc5\xde\xd4G3f^\xb5x\xa5\xfbh\xe9%\xe2\x1cO\xb4k\xbc\x12\x10Z\x19\xa0\xb3\xf2\xd2\x94\xc4\xa1\xa8C\x7fk\xabE\xc1\xf5\x9c\x13@\xfe\xdbT-\x9f\xa9\xf8CW\x91\xce\xc9\x0bJ\xb3-\xbf\xd2~$\x88kl\"\xadi\xc4\x89L\x1a\xe9\x89\xfd\x9a\xd3\xc3\xb5v\x1d)Q\xc8\xa9\x83\xb6BNtSFuK5\x0c:\"v {\x07:\xa2:\xbbvn3\xdd7\xb9\x07\xfb\xc2\x9e\xecs\xc7\xd1\xdf\xdb\xd8\x01Yx\xe4\xd0\xfe\xe4`\x8cw\xa0\x03\xd6\xd8\x83s\x8f<\xf5\xf6\x97[\x8f\xebcYT\xdckx\xa8\xe7}5V\xb0\xf0\x8b1\xf9\x18\xd7\xda\xa2\x08[\x92\xcfQ\xe9\x03\xb7\x08\xd6\xab\xf5E/3Z\xe3\xc9\x13/\x8c\xc2\xebe\x94%O\x9fj\xb4\xb7\x81Q\xe5\xeb1s\xb9\xb5m\xe1/\xddN\x00\xd4eQ^ym\xe7\xf7\xba\x86zt\xbaX/\x9f\xb7\xa1\"\xbb\xe0\xc5\xaa\xfc\xae\xd7PQ0\xf2\xeb:F\x1e\xf2\xc08X\x91\xdf'\x9b*\xf2 ck\x11\xcf\xd8T\xd1\x0b\xaf\x870\xb5c\xd9\xf6\xef5^`\x9bA\xf9f\xd6\xa4\x82\x17\x8f\xb8\\*\xe2\x99\x14\xe6\xce.DM\xf7\x8b\xca\x15\xccVal\xe0\xc8\xf6\x1d\x0b\xdb\x12n\xdf\xf0\xa3\x05\x1d\x88\xa0\x03\xd6\x8f\x10\xcd\x8a\x94s\xac f\x05\x0b/\x01?\\S\xea\x93{\xcf@\x18\xa5\x98\xc0\x82\x8a\xdd\xfe\x94\x88\xa9vM\xe9C\xc5C\x11\x14\x13I\x8dCC\xb2W\xf1`D\x89\xf2\xa5yV\x1b\xb0B<\xb4\x0b4\xad\xacD\x17\xd0=e\xc8\xbc\xe4\xf3\xa4\xd3\xf71\x16\x99\x02\"\x0c \x8d\xef\x12\xf6.\xc9V\xab\xc0gi>$\xa8\xb9@>\xae\xc8$%S\xf0B\x06\x9d\xaeu\x9b\xebX\xf1\xe4w\xe0<\xd0\xc2\x04\x9e@\x96\x1b\x06L:\x9d\xb6\xa0\x99aj\xc9\x0c\x93\xe2r\xcc\xa2#\x1e\xd3\xb1O\xe8\xaf3\xcb\x05\xaf\x05\xe4\xe8\x02\xcddCJ\xf4T.\x8c.>c\xb2:sx\xf5\xb91\xdc\xe2\xea\xb7\"\x11\x1eb\xf9\xde\xfa\x82;qC$O7@l\xef\xcb#\xb6\xd7\x1a\xb1!\xf1\xc3y@\xe0\x84x\x93\x94s&\x9f\x87\xe5\x9f\xb3\xf0\xa6\xack\x02C\x7fWB\xbce\xd3\xc5/\x99\x19\xb7^c\xe6P\x14zK\x16)K?+\xf5\xf1\x1a\x8d\x9eM\x0f\xc3\xc1\xae\x14\n\x16\xe3\x0d\x97\xde\xe0h\x8a\xad\xdd\x8c}\xe2\x11vp\x95\xc6Z\xb5pc\x1b\xa2W\xab\xcf\x97Gv\xb1\x92\xf4s\xac\x91a\x8d\x7f\x1c\xba\x1b\xb8(\xbc\x92\xbb%\x91\xabu\xb0R\x1fD\x9bk;\x1d\x933Ge0\xe4\x05\x88\x8b\x05\xf0\x0d\xc0\x0e\xab\x94\x05I\xca\xebhJ\x1a9\x8a\xcf\x81\xa1\x89d0\xbe\xf2w%\x18\xff0\xceM\xcc\xb5\x11\xd0\xf2\xa9\xd6L\x93\xdaq`%+\xb3\xad\xd1\x08\x92:T\xbaC\x8e\x8c\xf5\xd98g\x89\xeb\xf2C\xc8\xea\xf7:\xf0 e\xdd\x85\x97H\xd1\x95\xecI+\xd2\x0f\xf5\x0cZ\x17\x19\xb4v\xac\x19|.{\x06\xff\x00\xd2\x15\x85\x1b\x1c\xd1\x1a\xe9@\x8aTW\x11\xd0jL\x0d?o\xeb\x16Q\xd1\xc4\xce`\x810\x1f\x83\x07O \xcd\x19tO\xf6\x866=tR+\xba\xf2\xe9\xd8\x93\x89j\xed\x04@\x12y\xfer\xfa\xe6u\x91?H\x9bYB~6\xdcih\xb2*\x1f~-\xb6Z\x14\xe2\x89\x99o\xcf\xba\xf3\xf2\x16\xe8B)\xda\xef\x8e2R\xe8i\x16\xad\xbb\xb4\xd2\xa4Y\x14\x13\xba\xa0T\x9b\xa9_~\x8c'C\x98\x0f<\xb2\xb7\xfa.\xe4\xab'\xe2\xf4\x96\xd6&\x87U\x17\x8eU\xb1\x14\x8f\x8f\x05\x99\\\xe6`L\\\xb8\xc8R\x88\xc9\x84\xf8k2\x85?&\xe0\xa5\xe0\x87S\xf2\x11\xfe\x98t-\x17\xce1\x99\x0bA\xe7m\x05l\xe6\xd5\xfd]\xb6`\xef1d\xa5\xe5\xc8\x9a\x97\x03\xa4\x1d\x94\x8e\xb3\x86%\x01(\xfb\xd5&\xe5\xd1R\x02\xed\xb4\xa2\x8e\xd0\x9a\xc6\xb6\xd9\x9f\x86\xadxw\xfb-Y\xb4\xb0&\x15\xcfg.\xe9\x7f=\xac\xc6\x8f\xac\xc7\x1f7\xe44Z p9\xb30\x9e\xb4\xc4\xd9Y\x9bf\x817\x1d`\xac\x84;\xe1C\x82\x1c\xd4\xf5\xdb\x01\x1a\xb7D\xbb\x0dswL \xf9\xe8M\xd2\xdf\x11\xeb\x93\xd6X?A\xacO6\xc5\xfa\xc9g`\xfd\xe4\xce\xb1^\xa0p\x86q\xed\x18\xff\xd4\xc4\xb5\xe4;%\xa0;\xa5\x15J\xd3\xda+\xdc)A\xcb\x9d\xb2\xb5\xda\x0cN\x97\x84\xcbdA=9\xfe!|\xe6M\xf3+\x0cZ\xa0\xf0l\x0c\x06,\xc6\x80\x05\xdcs\xe5\x87\x10/\xff\xd0\xd1E\xfb\x95\xec\xf7\x92:\xa5\xef[l\xd35\xf7s[\xd9\x89\x0bAu\xb7\x07\xedv;\x85\xdb4\x07\xdb\xf4\x1f\xb4\x8f+oo$\xafM\xa8\x06B\xd2\xe1\x8f\xd0Z\xe5\x891x\xf2\x02\xf8\xf4 \xfap\x1f\x0b\xf0\x07\x81!f\x00c^2\x84\xfeR\x03@\xe8\xfb^\x18\x02\x13,\xfc\xa4\xbb$I\xe2\xcd\x89\x14\xf8(I\xbd\xc9%\xbaW\xb5j|j\xc8\xff \xcaC\x9b\x11\xa5\xc8\x85\xcc\x85\x04)\xbc\xd6\xe5\x93>6=\x883\xa6\x89D\xa23\xc1\xa4V.\xb0X\xa5\x9e\xc3S.`b&dE\x8f\xbc \xf0\xc3y\x11j\x0dp\xe7xi\x14'0\xf5c2I\x83k\x91\xe4\x85n\x94(\xa6D\xe3\xe2\x1a\xd2\x05\x81\x1fWq\xb4\xda\xa6D'\xf9\x11V\xde\xe4\xd2\x9b\x93.\xbcO\x08\xfc\x987\xd8E\x865\xff\xd3v~\xa4\xfbl\xe2\x05\x01mb\xd9\x85\x13\xe2Ma\x19\xc5\x84r\xae\x8b4]\x0d\xef\xdf\x9f]t\x97\xe4~\x96\x90m\xfcz\xbb\xe8\xc7\xb8I$<\xc48\xd0\xe3\xe8\x0c\x0e\xd0\xd93\xf7W\x15\xef\x18\x91x\xb7 \x85\xacS\"\x9a~\x82\x86\x97\x94\xf1N &?g~\x8cZEY\x9eb|\xb7\x9f&\\\xd4\xf2\x13\xf8\x91vD\xe9(\x0c\xbf\\\x1f\xb9\xbf\xae\xe8\x88Nn\x08\xa9]\xc2\x91&Op\x90\xaf\xe6\xbb\x17~8\xb5\x19\x19\xda\xeak\xc0\x9b\x8b]~r\"F\xaa~\xd7\xabF\x981`\xfc\xba6\xa4\xa3\xe9@v!3a\xbd\xb8k1_\xe1\xf0\xb6\xe7\xb6\xe7p\xe2p\xd0\xee\xa8(\x1d\xa9K\xfay\xdbS\x95\xbeM\x05[\xcf\xd7\xa9\xba(\xaa\x17\x93\x1eb\xd7\xb6\x96\xf2%W>\x8b\x92\x9b{\xef\xe9\xe13\xf1\x12\x92;e\x0fk\xaa\xf0\x9b\xf7\xba*\x85\xbb\xb8\xbe\x16\x14\xd06\xa5 `\x0d S\x84\xe6f\x0c\x9e\xb7\xac\x19\xce.\x99[\xd1\xbas\x8b\xb6I\x97\xacI|m_7x@\x97=\xdeS\xb9\x89\xbaD\x0bk5Bc\xa3\xa8\xb0.9r\x86\xcc\x913\xe4\x8e\x9c\x93\xa6\xdb\x95\x8d\x1c;\xd5\xe7\xa6\xd1\x0f|+n\x953\x82\xce\xc1\x17)O[9\x98\xc7\x8a\x83y\x1b%\xc2c\xd8\xb2}LhPv\xec\xae\xfd\x12\x8a\xbb\x10\x9fyuK\x0b\xd97\x83f\x03gs\xdd\x98Zr\xbd\x18Z\xa8\xad\xb39*\xaf1\xf1\xc5\xb5\x9d\x8d\xfbg\xad&\x02mt;&\x8c\x16\xe1\xa5\x1b\xbf\xaf\xf6\x7f\xd3\x8a\xcc\xcd\xeb\xbd^\xc5=\x8b\xf1|R\xf5\x85p\x00\xdc.\n9?I\xbd~B\xe6\xc7\x1fW\x85k\xba\x05-\xa3\x13\xf1\x9e\xa4\xfc7\x9c\xd3\x14I\xa1\x18\x95\x18[\xff\xf2/R*B\x0b7p\x835\x19\x91\x07\xc8^W\xe1\xc8\"q\xd1\x81\x8b\x11T2W\x1a\x80\xbb4\xc7\x14\x93\x12\xcb\xe1\\rjW\\i1\xb7\xe8*\xe4\xc5\xda\xcc\xb5\xfa\xebJ\\\x82\xfa\xa8O2\x00\x9e{\xa9\x94\xb1g\xea\xa5\xc4\x90\xb4\xa7\xf2%[\xdb\xe2\xdb\x98\xcc\xc9\xc7\x95\xc6\xeb\xd9\x84F\xed\xe0y^\x8f\xac\xfaT\xd1\xe2\xc4n8\xaa\x19\xd2\xd6\x1d\xc3\x8d\xc7\x9e\x98\xbd\x17\"gS{\x86\xd6\x1f\xc5\xac\x0e\xae@]\x05\x0e\xe6\x16#\xaa\x1bP[\x1a\xd3\x14\x89\xae\xfc\x17\xffH\x8a\x88 #v\xc5&g/\x08\x14I\x05F\x94\x95\x0e\xba\xf2\x8b\xc0\x055\xe8\xe7\xad\xccb\xebb\x01\xe5W\xfaw\xd4\xbe\xd5\xdf\xeb\xeewy0\x84[\xb5\xb6.\xc2\xec\xef=tLa\xc5\xfdV\xf6\xcf>\x7fu\xf8\xfa{C\xbc\x87$\xf5R\x7f\xd2\xae\xee\xaa\x08\xb4\xde\xa26\x8f\xf2\xba\xc1\x07\x0b?\x98\x1em\xfa\xd5\x9c\xa4\xcf\x199\xa0;P\xf9\xe6\xfc\xd5\xf1\xc9W\xc7\xcf\xcd\x9f\xbe\x0c\xfd\xd4\xf7\x82\xd3\x14S=l\xf4\xe9\x914\xdcM>\x8dI\x88\xfe\xbd\xe2\x8b7\xaf\x8f\x8e\x8d \xe4[\xe8[?\x08^\xb1p\xaa-@\x92\x7f\xf6\xdc\x9f\xde\xe2+\xda\xd9 \xbb)\xd4\x80\xd4\x84G\x8b(\xa3\xe0\xe0m\xbc_MK\x10m;I\xf5\xbb6\xe3}\xeeOo\xf3\x19v\x17.[\xc3\xe7\xfd\xeb\xd3\xc3\x17\xc7\xe7\xb7\\\x13\xdd\xd7\x1b\x03Y\xd7\xc8\x06S\xcf\xb0\xaa\x94\xcf\xc1z\xf3\xe1\xf8\xe4\xe4\xe5\xf3\xe3\xf3g\x87\xa7\xc7\x1a\xe6\xa7\xda\xce\xc4Htp#\xc6\xfe\x9aLq7\xbd\x88\xa3e\xcd\x8el\xd3\xd7\xcc\xd8\xd7\xd4OV\x81\x87I\xceZ\xb2\xe4\x80\x84W\xfa\x0eT\xbd\xaex\x0c\xd7F\x82\xa6\xb6\xee\x8d\xb2\x9c\x9a\xd8\x9e\xf2\x93\xdf{\x84\xec\x9e;,\x85\x86\x0b;\x1d\x87k\xb4\xc7\xe1\xd9Fw\\\x1aR\xdaz\xdci\xb7\xf25f\x1b\xfc\xfb\x8d\xab+\xd3\x060\x85\x9a\xa1\xddzT\x86\x01}\xc6X*g\xc7\x06\xc3Q\xbe\xc5\x00G\xea\xbb\x11L\xed\xca[ly\xa8\xad\xbd\x11BJ\xa7\xf1\x06\xc3^Il\xaa\x00a\xfenS\xf8\xe5\xccC\xeb\x01l\xb5\xaf\n\xed\xf6\x10\x94\xf7\x91\x1f6\xb7*\x1e\xc1\xe85\x1b\xf5\x8b\x07\xc7\xa3\xda\x02\x86\xadm\x01A\xe8\xbd(\xbb\x88W\x9d\xed\xba\xa5Odo\xf9.\xfc \xadhy6\x9b\xef\xa3\x0c<\xbc\x10I\xc9r\x95\xfa\xe1\x1c\xd2\x88gi\x07\x0fb\x92\x90xM\xa6\x88)t\xa4.\xfc\xf8\xc7\xe4G\x17\xd2\x85\x97\xf2\x03;\xfc\xe1O)\\\x10\x88B\xbc\xa9\xb1\xf8\x8aZpI\xae\xbb\xf0\x9c5\xe5cn:/,,\xa6E\x8b\xf8\x86x\xd3\xc7\xb4\xce\x95\x1f\x04\x90\xa4\xf4\xff\x17\x04\xbc\xc9\x84$,94o\\\xb6\x17\xff\x93>t\xbe\xe9\x11z/\x04\x9a!\xee\xb5\xeeA\xf5\xd7&\xab\x03\x12\xcf=\xa9.4\x1c\xc0d\x1c\x9eqE}\xfbq@!^F\xb6\xee8D\xbd\x87\xe7\x82\xd5z}\xe9RR\xc8^GY,\x19\x0b\xe3\x0dY\xba\xf0B\x88\xc2 \xe9\xc2\xbb\x85\x9fP\xc8\xcf\x02\x7f\x92\xc2\xd2\xbb\xa6k3\xcd\x08m\xc9c\x87Z\xd7ba\x99\xd7\x91?\xb5Q\x8f\x8ct\x0bo\xad\xe3\x86\x80\x93\xf2S\x7f\x01,?\xbc\x13}\x1ch\xf5in\xd6\\\xe3\x86Q\x99Mh\x9a\x97\xa5\xd1\x85\x1fN\xcb&\xf7\x1b\xdcA\xeb\xd3\xfd\x80d$\x98\xa8\x88E(b%cbF\xacs\xcd'\xf7\xeeQd*\xb3p,tm \x8f0?\xc3\xcc\x9b\x10\x13BEk\x12\xc7\xfe\x94\xa3\xd4,\x8e\x96\x1c\xa9\xe8\xd7\x90\xac\xc8\xc4\x9f\xf9\x13\xb40\xef\xc2q\x98d\x0c\xc3RVkI\xd2E4\x85\x10\x93\xd1N#\xbc\x01\xa6-\x06\xde\x8a\x85\xf2\xc4\x91\xf0jhjH\x1c\x97\xdd\\\x94\xb7\x82\x08\xbb\xfb\xe9\x93\x96a\xbc\xcd\xcc\xbe\xc8V!\xedn\xe3\x90q3\xa7\xf00\x11\xa5\xc8`\x1cZ%\x0d\x7f\xaaL7K(\xd9/&\xc8\x160\x8a\x8bAQ2\xceg\x02/\x19\xe9v\xe1\xa7,I\xf9\xb71\x99g\x81\x17\x17\xb6\xf4.=w\x08\xda\x86n\xde\xff\xc6\xbd\xe9 \xea:\xcf\xd7T\xa8\xe1\x8c;\xde\xc7\xfb\xa4\xf3\xf3\x98\x0e\xf60K\xa3g~8}\xeb\xf9\xb1&\x863\xc8\xac\x83G\x8f\x96P\xddf\x19\xcb\x14\xdee\xdc?.)\xff\xedh\xa3\xd0\x8b\x07\xd7Xm\x8c\x19Vxx\x8d\xd5x*\xad\xb9ch8\xf6Z\x98\x8e\xadp\xda\x95\xfe\x9a/\x02\x03{\xc5\x12\x01\xcd\xaa_;0\x1b{gt\xd2\x93\x86\x96jbQ\xcb\x0f\x9d\xd3BG\x00\x9bF\nu\x86\xd3h\xbd\x82\x01\xc4W\xe8\xe6\xd6g\xa4\xa2+(y\xbb\x13\x0c-\xf5\x9b\x16E~\xd6<\xa4w2\xf6Zr\x8f\x80\xfb\x1b\x03\x9b\x9b\x99\x80k\x95\x00\xf2\xd7\xea\x0e|\x1f\xe6V\x04\x94D\xc3*\n\xfc\xc95\xfc1A\x94\xbe$\xf8\xf3jAB\xb6\x03\xe7\x14\xbd\x8b\xadI?Ab|\xcdV\xbff8\x07\x10\x8f=\xc6\x13\xd0\x1f\x14\x19`\xa8\x1b!\x8b*\xcc\xea\xae\xf3\xba\xed\xa0\xcfCT\xf3\xaf'\xcd\xf0d\x11\xadY*\x16\x8f\xf6\xe3\xe6\x1f\xd7~[\xc3+T\x8f\xf8V\x84~a<\xef\xcbbIds\x8b\xb2\x9a\xfc\x01\x9a\xf7\xc4\x05kI\xe29\x11\x89\x97^G\xcf\xb3U@\x0fd\xf25\xb9Nlg\x08G^H\x8f]\xac\x06a\x14n\xb3f\x12$\xe0\xc4\x01\x8d\xc8\xc2r\xa7\x95.\xf5\x90\xe1k\xec\xeb]\xcc-ZXo\xe9U\xc4\xe9w\xc2\x8e{\xca\xe9'\xde\x92P\x14\x1c\xe2\xd1\xdb\xead}LA\xb4\xc2\xa8\xb3\xf4L`Vr\xa2\xea\xc4\xcb\x12nNv\x15\xa9j[\xdb\xa1G\x9c\"L\xdb\x8e\xe088\xdfMw@i\x9c\xf4p\\\xd0\xb7\x97\xe4:\x11,0gL\x0d.\xaa\xc2\x86\xb0\x15ZL\x9bL\x11e\xf6\xd2x\xee\xa1OI\xd7[\xad\x82k\xccE\xe2\xe6\xde \x89\xc1\xd1\x91>(\xd4\x1a\xbe2\xdf\x8f\n\x9b\xb8\xc2\x11%n\xae\\\x18{\x84\xe6\xd3\x1bC\x1ek\xe2G\x83t\xebf\xfbl \xf0\x87>\xd9I\xbb\xfd\xb8\xfel\xc0\x1b\x01n\x04\xea-\x87z\xdd(*\x10f=\xa7\xbb%\x16`WzR[\xd1\xe77\x06\xfd5A#h@X\xb4\x9e\x9f\xfb ~\x84F~\x9a$\xeb\xa0'\xa9U\xa4]6\x0f\xb0\xa4\xaa\xbf\xf5\x18\xf5\x06/\xad\xc6xn\x1c#\x8fY\xce/\x90Z+\xb7p|L\x1f\x1fwI\xf8sF2r\"5\xc51lc\xe95\x9fpK8 c\x9c-\x15`\xb7\x87\xd5\x859\xd90HV\xa2\xf6\x85|\xab.\xf3\xf6p\xae!m\x05d\xeb\xc8%Q\xaeT\xe3\x1a{P(\xd0\xa4*,\x88|p\x94\xf9o\xecY<%/\xc2T\xdb\xaekP\xf5Cg\x04\x83\xa6\xf6A\xd1Y6\x8b\x05\xc0%\"2\x0e\xa1\x03\xfd\x16|*&\x84\x181\xca\xe4\xdf6\x10\xc2\x0d\xa2\xaf\xc8\xb3\xb7\xe2\xda\xedj\x96c\x91\xd07&3\x0cj\xe6\x96\xf6\x850R\x0f\x0b\x93\xf9T\xe4\x172ODh\xef\xf0\x13\x85U\x80\x03\xedk\xdbiT\xe8E\xb6\x865\xf3\xd0\xb0\xaelO\x86\xcc\xf4\x1f5]\x0caI%_\x8e\xfe\xb9\xbf:\xe5]h\xd7\x16=\\\xe4\xeb)*\x050~\x9fR\xc1\xc4\x97.\xee,G\x81\x88\xa7\xdf\xad\x0d\x12o\x8c\xca\xf2\x92\xb5KH\xae\xe0\xc2\x95_\x96\x82\x88`\x8ef\xb9P\x87\xe2<\xd5\xa0'\x12\xdf\xdb+\xd9\x02\x9c8\x8e\x0b+\x9b\xb80\x17?R\xf1c\x89'\xacz-\x82\xbe\x08\xdd\xa9rS\xa2V\xb3\x1d\xd4U\xc8\x83c\x17\xed.XR\nx\xbb\xdb\xedR\x86\xb9\xaa\xdab\xcb\xe3/W\xcc\x1c\x05<\xf8\x915\xf0#\xe7$\x91\x99N\x1cy\xfe\xd3E\xa64'\x13\x8fJ\xb4\xfc\x83A\x14\x92\xffJ\xcb~ \xca\xad\x8d`p5\x80e\xd1\n5\xa9\xd3Y\x80BM\xc1\x0c#\x12j\nD\x04BM\x91p\xd8\xd3\x14\x89(\x83\xba\"\x1eWPS\x84\x91\x04u\xefE\xc8@\x8d\xd62\x8fa\xa6\xf9N\x0er\xa5\xf9\x94\x85\x052N\xcc\xf0\x15\x8f\xc8a*a\xc1\x174\xa5\xdcU\\7\x05\xe6N\xab\x98\xc3jy\xbe\xb0j:\x19\xbb\x10\x96L'C9\x9f\xeag\x10\x0e\xee>\xc9n\x00\x8a[\x13\x17\xac\xf3s\x92\xbc\x8a\xa6Y@,WA?4\xaa\x1f\xca\xd2\xcc\x0d\x1eI\xfc\xf0\xa9\xa3\x1e|\x8aUt\xce\x85\x98dh`\xef\xdeE\xab\x0b/\x1eB$\xfa\xa9\xd42Y\xad\xde(\x84\xd2\xcd\x89\xfc\x8e\x86*\xda\x94\x90\xfa\xa8\xf9\x89\xbb\x05\x14\xe0\x00b\xd0\x8dMX\xd9V\x1c\xb6\xe0\x1f\xbe(\xd5\x03be\x87v\x7f\xf7\xa1\x9a\x03\xd4\x17E{=]^QVT\xc9\x1c\x9a\xe5E\x95l\xa4^^\xb4\xaf\x16%\xdcfU=\xa8&\xcc\x0fWy;\xa3+\x82-\xed\xef1\x9e\x88\xae\xdb\xae\xa3\xb6\x1a\xf0\xf3l\xdf\xd1\xa5*]\x19\xcfg\xd4'\xa6\xe5uN\xeb\xd7\xd9D\xcdoJ\xd0^\xd4r\x07\xd2\xb9a\xba\xff\xb2{.\xf8\x02\xd7\x1d.\xe9\xea\x9c\x7fho\x88\xb8=\x172\xf5\x03\x9br\x9f\xc8v\x9d\x9f#\x13\xd6s!.*\x11\xc7a^E\xb9 \x1d\xea\\B\xc5\xa5|7\n\xdf\xc7\xc1\xd1\xc2\x0b\xe7\xa4\x95+V!\xe6\xa5^<'i\x9dCN\xd4MH\xca\xc4\x00\xb3\x80\x97\xc5\x81JE\xc5\xa3\xf1\x8b\xbeq!\xea\x06\x917=]\x91I\xab\x01GL\x0e\xebR\xa6\xf7\x10\xeb\nA\xeb}\x1c\xa0\x87\xb9\xae\xc64\xba\ni7j\xba\xf3|\x0c\x08\xb7S\xcc\x8e\xd0j\x18z\xb8\xa1\xe7\x9ax\xb3\x88\x89\xc1.\xa6\x98\xb2Mp\xc0\x14\xae\xd87\x99\xd2Y\xe0\xcdrw\x15\x935 \x85t`\x1b\x06.f\xf6>\x0eZ\x0d\\\xea;b\x82W7\x8b\x83\x0d:\xc4\xb1z\xf1\xa4~\xff\x88G\xc0\x89\xa2u\xd0]yqB\xd8\xd7\x8e)\x834\x19[Y\x1cPq\xdb_z1\n\x91\xd6Y\x1ew\xd2\xac\x9c\xa5\\\xd8\x95\x1fN\xa3\xabn\x10\xf1k~\xdcW\x93\x08#\x1f\xdc\xbfoA\xa7Rc\x11%\xa9\xe6\xf5\xcaK\x17\xe6\xeeXmJ\x98\xf8w\x0b?I\xa3\xf8\xba\xfa\x06/v\x98\xcc^-\x93un\\\xac\xb4,\x97\xc5\x1c<\xa0\x83e@KH\xec{\x81\xffK\x0e8]\x86\xde\x9b*\x1am\xb4>b\xd3\xccIz\x14\x853\x7f\x9e\xd8\x0eE\x8c\x84\xa2\xf4\xd8\xa0p\xc1I\x11I\xc7\xc4n\x86r\x899\xef^\xe7\x12Pj\x88v\xc5]\xb2\xf0B\xa7\x0d\xa5\x81<\xb5 \x99\xbe\x0c\xa7\xe4\xe3\xd0\x90\xc2\x1e8\x03$\xe1\xae1\xcb\xb1\x89FE\xe1\x0b?HI\xfc\xc5H+\x03\x7f\xe0]GYZ\xa6k\xacc\x9d\xfd [t\xae<\xd1\x0f\x02\xc9q\x8a\xb4\x90\xa1F\x14'\x14\xd8\xa6\xf8\x92\n@\xab\xfap\xdag\xe9\xa5\xd6\xf9\x88b\xae'\x9dbL;B\xdfF\xa5\xb7\xe3\xea\xa8\xf1\xbe\xcd2\x1a\x98kl\xc29g\xd5\xbc\"L\xd9\xd4\x8cYf\xa0\xb5\xc6\x992\x88T^\x10\xf4\xf3D\x9du\x8b \xd6a\\\xcau\x86f\xa5*\x11Z\xc5\xea\x8e7\x7f\xc4.q\x9a\x08\x02\xde\xa8\xd1\x1d\x1cr\xa2P\xb7\xe9\x0b\x15\xb0\x86\xe0\x9bU\x981k\x7fc\x1a\x03Hg0v1F\xc7`|e\x0bl\x10OkZ\x03z\x9ch(j\xbc\xb7o\x81D\xe2\x06\xec\x8ep\xe86g\x02\xe7\xd7\xa53\x816\x94\xf3\x1c\xe9\xb8\xd0\xf8vK\x10=C>\xe4\xf6@`Z\xce;\x9dy\xc3\x1eb\x80\xd1z\x07\xca\x0f\xbb\xfb.\x11\x13s\xe5\xb8h\x18!n\xae\x89\xf7!\xb6\xf5\xcc\x98pU<\x11\xab\xf8\x8d!i\x9fx\xd0\xc9\x8f\xae\x93\x1f\xce\xb9\x95b\x97\xffIwHVK\x1e\xbc\x9a\x9bqk\xe6\xf9\x01\x99\x1a\xda\xc4\xf3\xde\xebN\xa2\x00\x15\xf3V\x8c\xd9=!S\xdf\xff\xff<\xcf\xab\xb3\xac\x0b\xd0\x11\x80\xe1\xa7y\x9c+\x83\x0f\xa2x\x16\xb5\xf72<`\\=I\x9bb\x17f\xfa\x15TIW\xd3-+}\xa6\xccFh\"\x8eO\x9e\x9aYh\xadE:?\xdd\xfeP\x1f\xdc/5\xb6\x87\xe2\xe1\x1b'\xa50\xad'v.\xe7\xcek\xac\xa4(\x03\xb6j\x98\x03\xcb]\xd94\x054\x07e.S<\x9f\xdd6\xff\xb0\xf6\xb3E\xba\x0c^Dq\xfeQ\xd5uK<7.\x18\x87\x88\xf9\x95\xf2(f\\`\xf4\xf0\n\x86\xa2\xad\xf9;\xd6g\xd3\xdc\xfci1\xbe\xfa\xe9L\xfd\xc4\xbb\x08\xc8t\x08Y}\xc5(d<\xeb\x90\x116I\xd0\xad\xff\x8e\xaf~PO\xb0\xeb\x808uLL63{[\x08b+\xc9\xb0\xcdH\xc2\xd2\xac\xd6\x01RF\x10\xd1\xf4v\x16\x07\xdb\xfcS\xe3\x87)\xaa\x8dY\x9a\xad\x1az\xaa\x01({c\xfeFl\xa5\x02\x94Y\x1c\x98\xab\xb7Z\\\x9e#\xd1pi\xea4\xef7\xffV@\xe4\x19\xbek\xe1\x13\xf8\x93\xcbaem\xf5\x03u\xc1:\xfe\xb8\n\xa2\x984\x05;3\xa2\xc4\xd4_\xb7F\x88\x14\xb5\xd4\xfa\xcd_\xb7\xf17\xe9\xe3*\xf6V+\xf2\x85;a\x13\xd9\xbem_\x91 b\xe6\x8d\xb6\x9c\xd7\x0efA\xfc\xf9\"\x1d\x82\xb5\xd3\xab\xc1\x86+\x7f\x9a.\x9a*%\xf1d\x0831\x90\x1a6#\xa0\xfd\x9d^y\xf39\x89\xe1\xfdK\xc3\xack q\x89\x80'\xac)\xcb\xa9\xfb\x04\x13v\xb7]\x96\xd2^\x11\x8bS\xb7YN\xb3\x8b\xa5\x9f\x0eaaZ\xc1Uw\xe9\xad\xda3\x0b\x92\x04\x9et'A\x14\x8a\x898\xf4\xd3\xfa\xe3\x87q\x06f\x9an\x92\x7f\x1d\x1d\xa5W8\xf73\xc7\x95\x9a\xbe\x91\xa8R\xceCK\xdb_\xbe\xacb\x90Qojd\x18\x94\x02\x80`J~\xccxy\x7f\x15\xce\x1f_x \xd9\xdfu\xfd\x0f\xcf\xde\x9c\\\xf5\xbe\xfej\x1e\x1d\x1e\x1e\x1e\xbe>}\xbf8~??<<|\xb6K\xff&G\x87\xaf\xe8\xbf\xaf\x1e\x04\xfb\x7f\xa5?\xbe\x7f\xf1\xec\xd5\x87\xe3\xf7\xb4\xc2\xfb\xd9\xd5\xad\xfe\xeb\x05\xbf<\xbb\x1f\xf6\x9e\xcd\x16\x1f\x9f\xad~\xba>\xea}\xdc\xbd\x7f\xff\xfe\xfd\xce\xcf\xeb\xdd\xa3\xbf\xac\xfa\xcf{\x8f:\x9dY\xbast\xff\x97\xbd\xfb_\xf7\xf7\xef\xbf\xdfy\xf0\xe8\xfd\xec\xea\xf9l\xef\xe1\xfd\x9f\x1f<\xea\xbc\x8f\x07\xcf\x07'G\x97\x8f\xe8x\xfe\xfc\xdd\xc9\xe9\xbb\xe0\xd5\xe1\xf1\xf1\xe1U\xf8\xe8\xfe\xfd_v\x0e\xe7\xeb\xdd\xfb\xeb\xef_>\xbf\xaf>\xef_\x91\x9f\xfc\xfe\xe5\xe1\xe1\xe1\xf3\x87\xa7\xefO\x9e}\xf8\xf3\xfcY\xf0\xb7W/\x0e\xa3\xbf^=?|w\xf2\xf1\xe2\xbbg\x0ff\x9d\xf5\xdb\xaf\xc3\xe0\xbb\xc3\xbf\x85\xfb\x97\x83\xc9l\xe7\xf0\xd1/\xf7\xdf\xce\xde\x1c=|\xf9\xf2\xfb\xd0\xdf{\xb1\\\x1e>{\xf5\xf0\xc5\xab\xc5\xd5\xbb\xfe\x83\xc9\xa3E\xb8\xf0\xff\xf6M\xff\xe8j}\xfcM?]\xbe}\xde\xfb\xf9\xf4\xeb\x9f\xf7\xe7\xdei\xfa\xed\xfd\xcbW\xdfy\xe1\x87\xe5\xe1\x87\x93\xe7\xef\x83?\xf7\xdf\xac\xb3\xec\xdd\xcb\xd7\xd1\xfe\xe5\xa3\xde\xe9\xc7\xd9\xc3\x9f\x937\xe9\x8b\xfd\xf9\xeel\xd6\x8f\x92\xb7;o\xc2W\x93\x0f\x0f\xa6\xbb\xab_\xa6/\xdf\xa7Y?:\xdc\xfd\xd0{\xfe\xb7\xe8\xeb\xe5\xc7ep\xfc\xfd:}\xfe\xfe\xa7\x9fNw\xd2\xe5\xd7\xcb\x9f\x9fuV\xdf_?\\=\xef\x7fx;{\xf0\xd3\xdb\xe3\xde\xcb\xdd\xde\x9f\xff<\xf1\x9e]\x85\x19\xd9\x9f}\xf5\xcb\xfc\xfat/\xfd\xee\xe5\xfbG\xfbo?<\x88/\x9f\x7f\xfb\xe7\xd7\xdf|\xe8=\xffz\xf7\xc5e\xf4\xf5\xf2\xc5\xea\xf5^\xf4>\\\xfb\x0f\xbf\x8e\xc8\xe1\xe0\xfe_\xbeK\x96\xdf\xfd5\x8b.?\xf6\x12\xff\xa4\xff\xd5\xc3\xf4\x9b\xcb\xd7\xfb\xe4\xd9\xa3\xe4\x9b\xab\xbf\xac\xee__/'\xd7\xde\xdb\xfb\xef\xe2\xb7\x9d\x93\xb7\xcb\x8bW\xaf\xfc\x8f\x93\xbf|\x98\xbf;\xe9{\xef\xff\xf6h'\xfa\xea\xbbd\xfe\xdd_\x0f\xbd\xaf\xf6\x8f\xaf\xe8\xb2\x1c\x9e\xbe\xff\xf0\xe6\xe4\xeb\xbd\xa3\xef_\xbe\x1c}F\xd0\x19\xd2\xbd\xb8N\xc97Lj\xae\xd3.\n\xad\xe2\xc4N5\xf2\x18\xaai\xc6=\x8d\x84\xc34-\xaa\xe9\x1c'\x16;\xf0\xcf`\x87\xd0\x81\xd8\x81\xfb\xb0\x0b\xdb\xd2]\xe9\x8d\x0b\xa4\x9bF\xcf\xaeS\x82\xa6a\xf5\xd7f\xb9\xe9 \xb3\x10\xc4Q2\xcb\x17:*\xe6\xfc:\xee\xf3\\\x14!\xb9\x82\xa8\x92\xe4\xa7\xc6N\x03\xc7I\xa0C+\xb1q*f\xc3x{\xe6BF\xe99%\x06=\x97\x05q\x86\xa7\xd0\xc3\x0b\xe2m\xd8\x85!\xad\x120\xfb\xc5\x00\x9e\xc0\x8c\xfe\xd3\x19\xc1\xae\x83\x90\xf5\xc7iw\xb2\xf0\xe2\xa3hJ\x0eS;p\xce\xe0\xc9\x13\xe8?\x84O\x95\"\xe8@\x9f\x17\x0f\xf4\xc5\x03V\xbc\xaf/\xddq($\xc6I\xa7\x83\xe6\xfa\xf0\xf4)\xf4\xf7\xe1\x1e\x0c\xf6\xf6\xd4\xf7\x0f+\xaf\x07{{pO\x0d-5@)\x9bI\xcf\xe6\xc9\x18\x06K\xe7\xf2\xf4)\xecV;Q\x18\xb3~\xab^\xfa\xbdZ\x90\xed\x9a!\xf6\xf4)\x0cZ\x03\xc0\xd1\xa2\xb4WF\xe0Y\x1c-o\x87\xc2B\x97\xc5\x8d\x12\xe0\x8f\xb0\xc3\xc2=\x8e9>\xf782\xc36\xf8,\xc7\x83G\xff\xe9\x8c\xa0\xbf\xbf\xf3p\xc7\x81\x88\xb1\xe13\x8a\xe0\x99\x8b\xd1n\xb1\x04\x9e\x82\x07\x07\xe0\xc1\xb0x\xa7\xb2\xc0\x0c\xd2>\x1c0@\xa7c\xda\x0d\xdd?\xbc\xd1x\x8c\xc0\x19\x9c\xd1\xcd;&\x0c\xae\xf7`\x7f\x87\xbe\xb0F#\xcbq`\xc8\xb1\xc2\xcf\xd7\xcbf\xed\x0cp\x1d\x1e:\xd016\xdc\xef\x89\x96)b\xe4-\xf3\xae\x06RW\x15\xee=\xbf\x93\xfe)\xf2C\xdb\x92\xec\xb4$E\x91d\xc5\xc9 \xea\xf3\x7f)\x84\xa5\xf8\xab\x92\x9f\xdc{?L\x1f\xb2u<\x90\xff\x18\xb2\x90\x88lQ\xac\xc3gG\xcf\x8f_|\xf5\xe7\x97\x7f\xf9\xfa\x9bW\xaf\xdf\xbc\xfd\xeb\xc9\xe9\xbb\xf7\x1f\xbe\xfd\xee\xfb\xbfy\x17\x93)\x99\xcd\x17\xfeO\x97\xc12\x8cV?\xc7I\x9a\xad\xaf\xfe_\xea\xde\xb4\xc9\x91d9\x0c\xb4\xdd/k\xf6\xfe\xc2~q\xa4\x86\xdd\x99\x83\x04\n@\xdd\xa8F\xd7\xeb\xd7\xd3#55\xd3\xfdl\xaa\x1f\x9fH\x00S\xcaJ\x04\n9\x0dd\x82yTW\xcdT\xafQ\xd2R\xa2H]\xdc\x95(R\x07\x0f\x1d\xe4.IQ\xa4\xb4\x07wy\x99\xed\x9b\xf9#\xfa\x03\xfb\x17\xd6\xc2#\"32#\"\x13\xa8\xaay\xd4\xc2\xac\xbb\x00\xcf\xc88=\xdc=\xdc=\xdc\xafo\xbe\xec\xf5\x07\xbb{\xfb\x07\x87G\xc7\xed\x1d\x8b\xa7\xcbat\xa4\xc8g\xe9\xc1\x13HN\xa0\xdd\xf6\x1cqS+\xc3+b\xc18\x93Q\xd9s\xe8#O\xe7\xec\xe0\x9b\xa9z\x9e\x1d\xa4\xf4\x14\xc35\xc0O\xc0\x1e%c\x0e\xa4\x8b8z\x87\xc4\x13\xa3\xba\x15Q}\x99\xc3W\x178\x1bAO\xd0\x0b\x02\x1e\xac\xb2e\x1a\xac\x97\x98\xf0f\xaf\xaaE\xbb\xca\xef\xe7`\"\x95\xd7s\x9b.\xa6v-;\xfcN\"\xb0x\xad#\xbc\x03=\x0eq\xa3\xe4\xf1\xc8\x87\x8c0\xd3\xfeN\x8b%\xd7\xcc\xc3\xdcD\xf1s\xa4\xe0\xa1\x90\x85+.m\x90\xad@H\xff\xb4G\xb0\xeb \xc2\xd8)] Jr(\xf5\xec\x1f\x1c\xf6\xfb\x07G=\x8a\xd7\xf4 \xba\x8c#\xa6St\xdd\x1f\xf0'\x8c|\xb0\xe7\x03*\x9df\x02\xf3\xed\x88y\x18Q\xfc?\x92p>B\xc8\xa0\n9\x90\x00\x07\xbb\xf0\x08\xa2\xea\xad+>}\x99f+\xe4\xdf\x82\xb1\xd5\xb1d\x0c\xea!\x06\x1d\x0c(jY\xe7\xbaG\xbbZyC\x9eM\xd2\x8d\x897\xab\x0b\xbb\xa7\xa0\x02\x0b\xabM\xe7\xfa\x08>\x84\x80\xca\x02\x942\xa8\x12\x05\xdd\x17v\x9f\xce\xab\xe7\xe8K\xf80\x82\x04\xe7L}F\xd9r\xe7P\x85\xa3\x9f\x10\x9cb\xc3}\x18BO-\xb2\xe6E:\xf4\xb9\xa6\xea\x05K`\x04m\xa8\xe6T@\xc4B^\xbff\x14f\x01\x8f\xf8\x18:s6\x08X\xc0\xd3\xa7#\xe8\xcc\xa9\xe4\xd0\xa6;\x18\xe6t\xdb\x9d`\xf9\xc1\xfe\x01|\x88\xe1\xb2E\x03.\x88\xfa\xe6\xd0\x19\xc1\x91\xa3i\x91\"p\xa4\xb6\x14\x95[\x8a\xf3\x96\xb2\xbc\xa5l\xf3\x96(\x91`7 #\x07\xfb\xda\x87N\xf5\x06\xaa\xe1~3}5\xc2W\x8b\xcc3\x19\x9c\xc2+\xef\x15\x9da\xd8\x81\x1e\x15\xbc\x16\xf9\x9ck\xf44\xc8\xf0>\xf5\xd2Ew\x1d\xbd\xb3\x07\xec\xee[D;Z\xbe\xc8\xaa7\x17KU\xe3\xa8?,U\x15Q$\x94\xf6\x0ce\xe8\xef\xe2 \xad^\x93\xa9\xcdiBq\x9b\"6\x0b\x19\xcf\xd1\x9b\xd6\x1c\xe8\x91w\x9e\xa3\xb7o@o\xf4\xb00\xa07\xc5\xd1\xc1n\xce\xbc\xe5\xd1t\x06{\xb4\xc2\x12\xe8\xf0\xd0\xd1\xe3:\xc5\xe5\x98\x93\xd5H\xdf\x8d\x19/B\xa7\xaf\xa3y~\x85\x12\xd4\x13\xe8\xc1\xed-\xbf#\x8b\x8e\x1b,K\xc4\x13\x14\x8cq\xa7i0\x97\xce0v\xd4\xbbH\xd0-)H^y\xafl\x82>\xf2\xcc\x90\xca\xd0\xe3\x14lJ2\xf2\xc7\xbcJF\xbc\xe7tp\xb8\x0b\xb0\xae\xf92\x8ab\x1b\xbf.\xa3KZz\x87=\xf8\xe4\xd5\xc0q\x81P\\K\xa0\x8cM\x9d\xccq\xe0 \xf4\x91\xf3d\x9d\x0ee\xcb\x1f\x8e\x80\x96\xa7\x07\x82\x11\xee\x94%<\xa5\xfd9\x855\xec@\x02CXW\x10\x89n\x89\xa5CQ,\xa1E\x07\xac\xb6v\x9b\xd6\xb6\xc3j\xcb\xeb\x99\x8b1\xc9\x83(\xb5\x82Om\x82\xb5u\x18\xe6\xca\x8d\x05\xac\xb6\x11,q\xf8\xc8\xbd*E\x96\xe6\xf7F\xd0s\x9c\x13\x08hcG'(\x9f\xb5aQ\x88\xbd\x1e\xa5T\xed\x11\xcc(\xad\xdeAzA\x85\xa7:\x12\x94Qd\x0e\xe0\x96\xbe\xeb\xd3w\x83\x13\xf0\x19\xc5Q\xaa\xcf\x8a\xea\xb3\xbcz_W=\x7f\x15:0\x9b\xc2\xed\x08\xfa\x03\xba\xb1\xae*\x1c\xae\xe1P,+p\xca\xdb6\xf7\xea\x0c\xed\xdd\xc1Q\xe5\xc8[x\x85\x96\x1dk7i\xb2\xb8\x921\xd08\xdb\xc6\xdd\x9f<{\xfd\n\x1d2\xf9W\x9d\x87M\x9e\xe6fXI{S&yMW8\xccwS\xf2\n\xf9\x85\xdd@{[w\xa3\xf1\x9a\xf4\x0e\x92g\xed\xa8\x14\x0d]LPd\x87\xf6\xee\xae\xe2w\x1c\xf0GG{\x8e\xd6\xa57\xfa\xf1\xba\xf4n\xe3\xdd\xde\xa8KU\xd3(H\xf9\x185q\xbbh\xf9\x8a\xe3.\xf3\x11\xa7\xef9\x1b7\x0b\x924^g\xa5\x8eq\xa5j\x94\xcaxM\xd8\xfc\x9c\x12\x03\x161\xc1\xe0\xc3\x11\xdf\xd4(\x8a\x8bP3\xeclT\xf5\x83vN\xa0\x85>\xfaH\xf2\x92Rv\x00f\xee\x0fy\xbc\x0b\x9e\x94\xc0\x85\x16z\xce\n\xa7!\x96\x1f\xc19\xe1\xe34\x18\x85\xde\x83\xef\xb1\x84 u\xda\xf0\x88M\x15\xcb\\n\xa8g\x1e\x84\xderY7\xe4\xfa \xa1\x9f\x16\xfa\x13%]\xbe\xd4\xd2w\x83\xd3\x18l\xd84\x08\xf9L\x9c\xfb2su\xfa\xf1i\xa1\xda[\xf7X\x9ca\xa7:\xe7\xc5\xa9\xf3\xcd\xcd\x9aTN\x9e<\x80\x12\x0bV\xc5\xeeYf1\x8b\xe1\x11\xa4$\xf6.\x96E\xc0\x7f\xe5\xc2V\xd14{\xf2 \xbcb\xb7\x1a\xdb\xfa>\xbc\"\xb4\x8f\xf6\x1d\x17B\xfb\xf8\x00=\xa5\x8b\x0e\xd0\x96\x06\x1bu\xbb\xe07\xfd]\x1d\xc7 \xed\x03\xc7\xb6p\xb6\xd2(\xaez\xea\xb0\xeb\x80\xbb\xa6x\xe1\x94\x89u\x83\xe4\xa5\x98\xebM4\xc89\x85\xd2\x9eUyD\x15\xdc\x8a\xe3\x80\xa5t\xf8\xeew\xf3\xee\xe1\x9d[L\xb7U\x8d\xc9\x12\x97|k7\x9a\xde\x0dWt\xefAWtww_Y\xcb\x81\xd3\xe5w{\xbc$ .\xc3Mj\x92\xd7U\x9a\xca\xd8\x8e\xbbg\xd0\x86\xb8\xfb\xb1\x0b\x16\xabU1\"\xb2V\xd8\xe8\x0e\xa4I\xdb\x08\xa1\x9an\x9a\xeeU\xaf\x94\xf2\xa8\xef\xbd\xaa\x14\xc5p\xeb\xa0:\xbd,F\xfd~5v\xbc\xc7j\x19T\x8b'9J\xf1\xc9\xd3cj\x0b\xbd\x07C{p\xec\xd8F>-\\\xf1\xbe\xd2\xc4e \x068e\x9a,\x91\x88\xceQ\x0d}\xc8t\x9a?K\x8b\xfd<\x80\xce!e\xe9\xc9z\x19\xa4\xb6e9\x1a\xc7-\x1d\xeb!\xe3t\xaap\x9b\xf7\x8e\x0b\x87\xd0\x1aA\xc2\x82\xd5:<\xcf\x91\x9c\x1e\x91=\"\x8e\x93\xab\x89\xe8\x0b\x92%\x86\x1e\xabj\x85\x88R \xe6\x0cm/t\xces\x911We\xd3\xf3o\x9f\xd9F\x82\xee\x9cYC\xa2\xee\xfc\x84\x9e\x8b\xc0\xd7\xe4\x15\xcak^\xbbx&\xf5\xec\xbc\xd2\xb1\xdfnO\x1d\x17\xcf\xa1\xf4\xd0\x14\xdb\x0b\xa7\xebG\xa1\xef\xa5\xf6\xdc^\xa0\x02\x9a\xc2\\<\x89\xce\xf2>\xdc0\x0b\xcc\x15<\x85\x9b\x13\x07\x96\xec\x9e\xd3\xc2\xc5\xb3\xf3l|Cke\xe2\xc2xM't1^\x1b\xf4j\xd2MK\x18B\xb2\xc9\xe6\xd9\x90\xe4<\xe4\x81\x83\xd6w\\Cr(\x0elRO\xb1\xc3\x95\xbd\x19\x88\x8d\x7f\"\xb5\xda\xdf;vl\x8b\xd6n\xb9[\x88\xc65f\xb8\xc0\x8e\xa9`[Fp M7\x19E=\xf5\xda\xf9\xdc\xfe\x89A\xefv\x928\x1f\xda_xW^\xe2\xc7\xc1:\xbd\x9dy\xa9\xe7\xec\x04+u\xd4;\xe3\xcf'\xd7\x83^gr}\xf8b\xbasY-\x12\xb1:\xc7\x9f\x0f\xa7mg\xb8s\xb9RI\xdd\xd8\xeaZ.X;\xb2\xef\xb9\x19K\x12/\x0c\xd2\xe0K\xf2\x83x\xd9t\xf3@\xd8\x92\x98R5\x15\xd7~\xe8Y\xce\xd2y\xb4n\xb4\x12 k\x95\x85\xde>\x1d\xf7\xa6\x0e<\x85\x8e&'\x95\xed9\xdc\xd6\x84\x8a{\xaf\xbb\xa2\xd2\xb3\x1d9\x8e\xb0-1\x0bm\xdcMI\x922\x15\x8e\xe5]DY:\xbcXz\xe1[\x0b\x86\xe0a\xc4<\x19hB\x81M0\xa0\xc0\xe3\xdd=\xbd@\xb4\xbb\xbf\xeblc\x1e\xc6`\xf8\xdd4\xfa$zG\xe2\xe7^Bl\x0c\xd1\xda\xa6C\xa6t \x03\x96W\xe3\x9e\x1a$\xaa`\xbb!\xec\xe9\xc3:\xf4\x0f\xef\x1e\x98\x027Yy4[\xcaUE\xf7\x0e\xaa h\xf8\x04\xefU\xb98\x93\x05\xaad\x8f\x89\x02\x87U\x81\xc2\x03\xae\xfeS%\x81\x98N\xb8\x14\x93e\xc8\x05\xcarIf 8\x85\xa4+\xf2\x87\xe5\x05\xebg\x0d\xb3\x12V\xe6\x0d\x03k\xf2\xa4\x8e\xfal\x80\xaa\xc2<\x92\x93\x1b\x06<\xdfX\x1b,K-\x9a\xc9E}8\x05_\xa4\xfb\xa3\x9b\xa2\xf2\x82\xe0\xc1DS\x19\xaf\xc2\xeaa/\xc3B\x15;\x1aA\xc7\xa3\xdb\xae\xd3\xa3\xbb\xad)~\x80\x89\x9dm.!t\xfa\xdc7\x83\x07\xc1K\xb9\xa2\xb9l\xf2f\n\x90\xd89\x81v;\x84'\x10\x9f8\x10\xf0\x00\x83<\xbcv\xa8\xe6\xc6\x16s\xfa\xa0\x18\xcb9\xa5!~.Z\xed*\xc7\x11\x15\x8f\x83\x1c\xd7TdfX+\xe5\xb2\xdb\x10\x1d\xcd\x87\xac\x88\xdf\xde\xc6\xf0\xa4\xa5\x12 \xae\x86(qW\xf5\xda\x86\x94G$5\xe8m\xc4\xccUB\xd8\x95\xb4$\xef\x95.\x06h\xdbf]\xd4/`\xcc\x9d\x06NE\x07B\x18\xc2\x8c,IJ\x10R\x8ap\xd8\x8c\xa8\x02\xf5\xaa+\x99O\xfa\xb6\x13-D@1\x88\xbb\xe2\xdb\xee^\x95\xe8 \n\xaeO\x92\xb5\xbb\xaf\xcb\x92\x85\x8c\xe0\x8eC\xc8\x0bhu\x83\x04%zSx\x01:\xa5\x01c\xda\x11\xa3H:r+>\xcc]\xe5\x149>\xe5\x88hZF\xb3\xb2\xbe|\xc2\xcb\xc7v\xe8B_:\x9e\xd0w\x93e\xe0\x13\xbb&\x91\xb27N\xa76\xa5\xaaI\x193\xef\xbeR&-H\x93\xa8 0^\xefe!0)\xdfd\xdc\xd7\xe1\x14\x02J\x8dQK\xf9\xe8\x11\x84\xf0\x94\xd9\xf4R<\xd7\x88\xa6\xb6\xd8\x03\xdbv9f\xa4Z\x99_\xf3P\x98YOx\xfbt\x08<\xc5\x1eS\xda\x1e@\x1b\xbd6P\n\x0c\xf9\x03\x1c\xa0\x93\xbf\x84a\xfc\x02\x87\x91\x7f\xfar\xc8_\x0e\xa1\x83\xceXO\xa1\xe7\xb2/#\xad\xd9\xf0\x8aG\xbc`\xac#@\xd6\x11\xc3\x13\x08N\x1c\x88Xh\xb1t\x1c\xd3\x9e\xe8\xfd\x11\xa3;\xe3\xc6~u\xb76\xed\xe2A#.\x19\xe5\xb3\x94m\xb7\x94\x1dp\x1bIO3\n\x18ZJ\x0b\x15\xc4\x16M\x08\xb2`\x8d'\x93lv\xd4\xebu\xe8\xdf\xf9|>\xad\xb8\xa3\xc7\xa2Po\x97\x15\xea\xed\x1e\xcc'\x93lN\x06\xf8sN\x06\xf4\xe7\xa07\xc3\x9f\x83\x9eZ\x05\x9dd\x0b\x9b\xd9\xf5\xc7\xac\x99\x0bSs\xe8\xd85\xfe\xbc\xa1S\xe8\xc3e\x9f\x0e\xe5Jg\xe4\x00\x8b\xcf\xe6\xf3\xa9\xf3\xd5\xe0\xbd\xa52\xf0\xf2`/\xe6\xf3)\x02|sC o(\xcfk~\x9b\xe7Fw,\x16\x89A\x95Y\xb1\x999\xe9\x11\xf6g>=\x15i\xefm\xde\xe9A\xaf7\xe3\xb5\x8e\xb9G\xcd\x94\xd3\xcd[\x0bEL\xc7X\x87\xe5|XU\xff\xce\xa5^\x8e#\xd1\xd5S+\x0f\xed\xe6BX\xad\xbf\xd2\xef%\x8cx\xb6X\x1bGg\x9f\x8e\x8a\x91\xe2\xa0\xe7\xd0\x06\xdf\x05\xeb\xd2\xba\xeb\x9eH\xf9\xa9r\xe9\xb0+\xc2w\xdf\xc6\xd5s\x898\x10V\xa3\x01\x8am\xac;\xb1\xf0\xd1Z\xe3\xc7\xff\xe5\xe7~mj\xddkd\xf5\xccY\xc8JvdS.\x9c\x1f\xf13<\xe2;\x18\xb7\xc72\xdb=\x1a\xf7rC\x02U\x13\x9f\xd31\x8d\xa8F\xde\xd7Pr\x14\xff\xa2\xdc\xdf/\x1d\xb7\xdb\xc1\x14\xe9y\x00O :q\xd81\x87\n\x06\xe98\x98\xa2\xeb\x8dA\x92l:\xcf\xd4`\x83A\xcfU=s\xa3\x96g<\xb9\xf6{\x9d\xc9\xf5\xec`r=;\xeaL\xae\xe7\x07\x93\xeb9~\x99O\xb2^\x9f\x92\x82\xac\xd7?\x9cOw.kpf[zx\x1f\xe4\xb2S\x14\xdfR\xc7a\x96q\x81>\x11]\xdb\n2\xdd}\x12\x0f\x9dJ\x90\x03\xebG?g\x0d\xc1zV!\x14\xd6\x8f\xfe\x96\x1e\xfc\xb7\xf5\xe0\xbf\xa3\x07\xff\x8fz\xf0\xcf\xeb\xc1\xbfI\xc1\x9e\x02\xfe-=\xf8\xdf\xe8\xc1\xffV\x0f\xfewz\xf0\xbf\xd7\x83\xff\x1e\x05?W\xc0\xbfC\xc1\xbe\x02\xfe'\x14\\M\x91j\xfd\xe8\x0f)x\xa6\x80\x7f\x81\x82\xab D\xad\x1f\xfd}=\xf8\x17\xf5\xe0_\xd2\x83\xff\x17\n&\n\xf8\x7f\xd5\x83\x7fW\x0f\xfe==\xf8\x1fP\xf0K\x05\xfc\x0f\xf5\xe0\x7f\xa4\x07\xffc=\xf8\xf7)8P\xc0\xffA\x0f\xfe\x03=\xf8?\xea\xc1\xbfL\xc1\xaf\x14\xf0\x1fQp\xf5\n\xab\xf5\xa3\xff\x89\x82_+\xe0\xffY\x0f\xfe\xa7z\xf0?\xd3\x83\x7fE\x0f\xfeU=\xf8?Qp\xa4\x80\xff\xb3\x1e\xfc\xbf\xe9\xc1\xff\xbb\x1e\xfc\x7f\xe8\xc1\x7f\xac\x07\xff\x1a\x05\xff@\x01\xff\x0b=\xf8_\xea\xc1\xffJ\x0f\xfe\xbf(8S\xc0\xff\xb7\x1e\xfc'z\xf0\x9f\xea\xc1\xff\x9a\x82\xab d\xad\x1f\xfd\x19\x05\xdf(\xe0\xbf\xd0\x83\xff.\x05?S\xb7\xc3oS\xb8\xa7\xc2\x7f\x9d\xc2\xdf,\x14\xf8\x9fSx\xaa\xc2\x7f\x83\xc2\x93jH#\xebk=Y\xfeZO\x7f\xbf\xd6\x13\xda\xaf\x91\x88+\xe4\xed\xeb\xbf\xa3\x07\xff\xbc\x1e\x8c3\xa0\x10\xc3\xaf\x7fA\x0f\xfeE=\xf8\x1f\xe8\xc1Hh\x15\x8a\xfa\xf5\xdf\xd7\x83\x7fI\x0f\xfe\x87z0\x92 \x85,\x7f\xad\xa7\xd6_#eR\xa8\xf5\xd7\xbf\xac\x07#\x99P\xe8\xef\xd7\xffT\x0f\xfe\x15=\xf8W\xf5\xe0\x7f\xa1\x07# R\xf0\xed\xeb\x7f\xa6\x07\xffs=\xf8\xd7\xf4\xe0\x7f\xa9\x07\xe3\x9e\xfd\xab\n\xf8\xd7\xf5\xe0\xdf\xd4\x83\xff\x8d\x1e\x8c\x9b\xf3R\x01\xff\x86\x1e\xfc[z\xf0\xbf\xd5\x83\x91\xd9\xff5\x05\xfc\xdbz0\xca\x00\xca\xc6\xfc\xfaw\xf4`d\xb1\n\x07\xfb\xfaw\xf5\xe0\xdf\xd7\x83\xff@\x0f\xfeC=\x18\xd9\xb7\xc2\xd8\xbe\xfe==X\xcf4\xbf\xd6s\xc7\xaf\xffH\x0fFv\xf2\x93\n\x18\xd9\xc9\x17\n\x18\xd9\xc9_W\xc0\xff'\x05\xbfU\xc0\x7f\xac\x07#'\xf8D\x01\xff\x89\x1e\xfcgz\xf0_h\xc1\xdf\xfc-}i\xe42\xd5\x981\xd6\xd7\x7f\xaa\x07\xff\xb9\x16\xfc\xcd\xcf\xe9\xc1\x7f[\x0fF\xd2\xabH#\xdf\xfc\xbc\x1e\xfc\xf7\xf4\xe0_\xd4\x83\x91 (\"\xcd7\x7fW\x0f\xfe\x05=\xf8\x97\xf4`\xa4\xdf\x8a\x90\xf2\xcd?\xd2\x83\xff\x89\x1e\x8c\x84Z\x91/\xbe\xf9\xc7z\xf0/\xeb\xc1Hc?S\xc0\xbf\xa2\x07\xff\xaa\x1e\x8cT\xb3\x1a\x93\xc1\xfa\xe6\x9f\xeb\xc1\xbf\xa6\x07#\xa1>S\xc0\xffJ\x0f\xfeu=\xf87\xf5`\xa4\xc8\x8aT\xf0\xcd\xbf\xd6\x83\x7fC\x0f\xfe-=\x18)\xf2\x1b\x05\xfc\xef\xf4\xe0\xdf\xd6\x83\x91\xf4VC\xe4X\xdf\xfc{=\xf8w\xf4`$\xa6\x8aP\xf8\xcd\xef\xea\xc1\xbf\xaf\x07\xff\x81\x1e\xfc\x87z\xf0\x7f\xd2\x83\x91\xc6*\"\xe47\xbf\xa7\x07\xff\x07=\xf8?\xea\xc1\x7f\xa4\x07\xffg=\x18I\xef\x0f\x150\x92\xdew\n\x18I\xaf\"\xe3~\x83\xa4W\x11f\xbf\xf9c}i$\xbd?\xa3\x80\xffD\x0f\xfe3=\x18\x89\xe9\x97\n\xf8O\xf5\xe0?\xd7\x82\xbf\xc6\xd5y\xa92\x1e\x9c\xab@\xe1<\xdf\xb0\xe3\x9a\"\xb9|\x83\xc2R\xa4\xc2Q\xb0|\xac\x927\xe4\x1bI\xe1\xcab\xf2\x08a\x8ex\xdb\xab\xe9\xee\xa3Q\x945u\xdc(5\x84tL\xa6\xa5\x17\x9aT\x895J!\x83_\xc8\x81>\x1d\x89\xa2q\xcbx\xf1~\xa3\xeaKo\xde\x12zc\xbcK\x92\xf2\xe4\xdd\xdc\xf2\xc6\x9c\x92\xe4\x81\xa3}\x93\xdb]\xb2\xc2\xee\x82\x1aL\xa6x&\x9b)\x9euv\x12\xf4 \xeb\xf5:\x93\xeb\xc1|r\xbd\xebu&\xd7{\xbd\xc9\xf5\xfeEgr}\xd0\x9b\\\x1f\xd2/\x87\xf3i{\xe7\xae6j\xd1\xc9\xf0>\x9d\xf4:_N\xc7\xcf:?3\xbd\xc5\xff\xbf\x1a\xb8\xef\x11v;\xeeu\x8e\xa7\xf4+{\xc8\xbf \xf4v\xfc9\xfb\xd9\xeb\x1c\xc3t\xe7\x8e\xdd\x0f\x99g\xd8Vv\xae\xdc\x085\x99\\{\xfedr}\xd1\x9fL\xaeg\x87\x93\xc9\xf5\x9c\xfe\x87\nV:\xe1l\xc6q\xca\xd9\x9c\xe3\xa4\xb3Y\x9f\\_0\x85k\x8f+\\\x0f\xe60\x99\xa4\xf4\xf5\x8b\xc9\x84\xbe\xeb\xf5P/;\x9fO&\xe1d\x12c\xa1\xc1\x11\xfbs<\x99d\xfd\x83#Z\xa2\x7f\x84\xd6\x16Z\x11\xfb\xd3g\x7f\x06\xec\xcf.\xfb\xb3\xc7\xfe\xec\xb3?\x07\xec\xcf!\xfb\xc3\xea\xec\x1d\xb3?\x1ek\x81un\x9f\xfe\xd9\xed\xf5\xaaq\xae\x98y\xcd\x826\x0b\xecm0\x9d\xcd\xda\x96\xba\xe1P\x0b=8\xe4\xc3>\xbc\xd0[\xc9\xe8R\xd3I\x9d\xd3\x99\x9a\x1fL\x98\xb6{r\xad\xda\xba<\xad\xe9Mt\x0d-A\x95\x06\x8dU?\xeb\xfc\xcc\x84)\xdaQ\xd3\xceT\xed\x93\xeb\x191\xd9\xd7\xb60\xe4\xf9w2\xe4\xa1\x89l\xbcq\xbf\x96\x92E-\xcb\xed~\x9e\xcer\xb6\x96\x8a\xce\xeb\x8b.x\xd1-\xcd\x07\xb7&\xdb\xa9S\xb5>\xce\x8c\xd6\xc7\x85\xc1\xfa\xa8\xb5\xb5\xe2\x1d\xe8\x8d\x0c\x92\x0b\xbdA\xf2\xaad\x90\xd4\xd7G\x9f\xcd\xca\xaf\xdd\x14&\x96\xf1<\x8fs\x8f\xf3\xdf\xa6\xd3\x86\x96:\xfbt8\xbb].oW\xb71\xb9Mn\xd3\xdb+\xe28\xa7\xdc^9\x8e]\x98\xbb`}`\xa9\xf6NX+\x15}t\xfb\xc9'\xb7\x9f\xde~\xf6\xe2\xf6\xec\xf6\xcd\xedO\xbd\xa8T\x04mX\x9a*+\xfa\xb7\xdc\xa4\x7f\xe2\x8d\xa6\xe6-\x17\xf7\xfb\x87\xf6\xe9\xb0\x7f\xf6\xe6v\xf0\xea\xa3\xdb\xdd\xcf>\xba\xb5O[\xe3\xfe`w\xeaL&\xb37\x7f\xcd\xb1OG\x93\xc9\x05\x92\xf1\xf3\xa9#\xbf\x93\xa4\xb7\x83pv\xbb\x1b\xcfJ\xef\xa4\x8b\xfc\x9dg\x9d\x9fa\xef\x04.\\I\x03\xbb\x97\x8dJ0\xaf\x9b\xcd\x98\x97Y\xe48\xa8\xe6\xf4a\"\xc7a\xd5\x05\x98'@\xeb7:\xd0V;\xcc\x82l\x06_\x12vw\x9b\xe7\xc6\x9cy\xa9w\xae\xcf\x7f\xba\xf0\x92\xc5\x10o\xb6\xc5\xae\xf2p\xe5\xad\xf1\x99\x1d\xd1q\x07\x1a\x0f)\x91f\x0b+(=\xbd\xbb\\\xa6\\\xc6\x11rYU^\xe3\xf6o\xc55\x97\x0bf\x8a\xdb\x8b\xc7\xe1\x03\xed\x9d\xdd\xc4\xec\xc8\xa8\xb3%\x87\xdb\xd9\x92Y\xd6\xcc%\xf1b\x1b-\xc8\x04\x03\xb9\xe8\xa4_1\x13T\xd2U\xfd\xcaD\x18\x7f;f\x1e\xeb\xe3\xfe\xb4\xde\xb4N?\x89\x9c\x0b\x92\xf6\x81e\xed\x92\xc1\xdc\xab\x11\x13x\xca\xf0K\x82\xf2i\x19\xb8\xf0(\x12fe`\x82%\xbd\xf2\x1d\x8f-/u\x1c6\xca\xd2Z\x84\x970\xb5\x9d\xf1d\xfa\xd5\xfb\xdb\xe9\xce%\xd2\xf1\x0f\x1eYR\xb1r3\xb7\xf9}\x07\xa7\xfb\xe1)R\xf4\x89\xed\xdc\xe2\x06\xea\xb69`\xea`M\x1f\xf4\xbb\x1f\x9e2~\xf5\xc1\x9d\xe9z\xcbn\xa1\x0b\x1b%n\xc2\x03\x01o\x1e`\x18\x8d!x\x0e\x13\xfb\xb3\xd2\x8d\x9f\xcdQ'\xcf\xe5\xa6$\xbe\xccs\xb9\xed\x8c?\xefN\xdb\x1f\xect\xc95\xf1m\x8cR\x16\xe0m\xa8\xe2[\xf7\xe5\x8b\xf3\xef\x7f\xf6\xfa\xcdk\xbc\x87j\xe1\xa5\x15\x8b\xdf\xf6Kb\xdf9\xefw\x99\x03W\xd9\x15\x7f\xbb\x99hE\xcc\xd9%\x08\xb7M\xfa)\xed^gl\x9d\x9f\xfbQL:_$\xe7\xc9\xc2\x8b\xc9\xec\xfc\xdct\xa7\xe8\xae*\x05\x8dc\xff\xc6\n\x83\xe6C\xdbf\xb3&\x18\x03\xd2\x96\x85\x87\xac\xe3\xd1\xa3\xdc5\\\xa6I\xe3T\xef\xe6Y\x90\xa5\x0e\x0b\x1e\xc6c\xc6\x90;\xcf\xbe\xce\xfb\xd3:?_F3/Y\x9cSF\x7f\x9e\xc7\x94;?\xd7\x1c\xb9\x14\xbf\xf4\xf2\xf6\xdc\x16\xb5J\x93$\xa6\xa3<\x17\xc1\x1cl\xc5\x83\x0b\xa4\xb33Q\xa6\x0fJ\xde\xca<\xc4P\xbe\xdau\x99\xf4\x85\x7f-\xbf\xba\x82\xd7]N\xd9\x8dU\xe12\xfe\xa0s\xff\xe3\x9f\xce\xfc\xda\xc2i\xf9\n;\x8e0\x90\xc6\xfd\xa0\xe3\xac\xc1\xb1\xa61j\xf6\xb2X\xf9\xe6a\x16;\xa8]\xde\x89L\x18\xeb\xbb\x10\xb2\xdb\xc8\xe8\xc7')\xd7\x08\xf7\xfa&L8\xb8/uh\x12I\xc6\xd3\x07\x12B\xb42\x08\x0b\xd5\"\x89a\xebe\xe0\x93\xa6\x89\xdf\x08\xb9\xf4Bo\xccPH\xbb$-;\x14\xc1\xb6l\xba;\x8b\x04i\x1d\x8c\x1aE\xba\xebh\x8d\xa9\xda\x0bl\xc4k\x15.t:\xf9\x1c\xb9\xd0\xbb\x13\xbb\x15\x93\xf4\x974\xf8\x90\xc7\x13+T\xb6\xe3p:\xee7q\x9f\x87\x1cI\xee\x8b[\x1e\n\xa5t\xa5\x9b\xb1\x0f\xdf\x93Mw\xb2:\xad\x18q\xca\xae\xb9E\xc7\xa7\xd5n\xb7%\x0c\xe1at\xc6\xb4\xe1)^\xb3\x0f\xc7\x01\x9dm\x96\xe0~\x83}m\x1e\xed~\xe3hM\x18\x14\x8bT\xa5\x0e?P\x99n\x96\xdd\x95\xfb7\x12#3r\xb3\x1b\xa1\xa9\xb6;\xf2\xd5Q\x8clb\xb1\xac\xdb\x12\x80e\xcd\x96\x00\x17Q\xb4$^\xc8!\xa7\x94\x0d\xf0T\xae\x16\xb2\x9d\x94\xae \x93\xc8F\xf7\x90)\xb7_\x8c\xd2&\xc0\xb5\xb8$\x1b\xa8\xee\xbf\xdd.0\xd6\xf4-v\xa1f\x03\x16\xdd\xd0\xef\xbe\x101QO\xd3P\xd7\x80\x95\xbbe\x86\x1brv6\xcaoW\xf5\xef\xb7\xedv\x8f\xf6\x1c;\xb4\xf7v\x0f\x9c\xad\x8c\x90\xe63{_\x7f\x1f\xeaPw\x18\x0b\xed\xc3\x83\xc696,s^\x80q\xb3\xcc$\xd0zE\xe0!\xdd]F*\x0c\xb7\x02\xbci\xad\xbe/\xeaH\x04\xb5\xdc\xd5\xd4\x00\xfc\xaed\x84\xe1*\xc3\xda\xbe\xcb\x1f>\x8e\xc4\xf6\xc6\xe9\x14/lx\x86l\x17\nT\x85\xd0^\xfa\x94\xe0\xe4\xd3a\x14\xe0}\xe4Jp\n\xde8AQ\xdc\xa7\x82\xaa\xaf\x91\xc7\x01\xee\xa3Q<2\xdc\xa1P\xe2\xf8p\xbd\xeb\xd1\xde\xd6\xa8 \xc8l`\xa2\xf8\xfd\x928\xf4\xe8\x11\xa6*\x18\x0f\xa6\xec\xd6*\xfd\xde\x9b\xba\x0c\xd8\x9fR~\x96\xb7\xa5\x18\x8e\xa1z\x04J)Af<\xd4Ub<\xdcu\xd6\xfa\x87\xd5\xfbF\xe2:\xa1N\xe5\xd5W\xd5]\x83\xa69\x14wx<\xddd&H\x98\xf8]|e\xf8\x18\xba+`i3b=\xe5\xa3\x0d{\x0e\x96\xbc\xc1(M\x0b\x17f.\xac\xd9\xaep\xe1\xca@1\x91\xee\xca]\xbeAO\x8b\x99\x0b\x0b\x17\"\xb8\xe5w\x0c\xaf\xe8\xa6\xbc\xa9\x1fA\xcd\n\x8a\xb7\xee~\xfak\xbc\xad[]\x91\xeaA\x94Yy\xb6:\x8b\xdeC\xdel>L\x91\x8d\x85dZ\x96\xcb\xfd\x0f\xdea\xb91\xd1\xdf\xcd$\xc6\x07j\xeb\x9e\xa2\xa1>|P\xbf\xaf\xf7b\xea\xf7\xaaV4$\xd5\xbd\xc6 \x1f\x9b\x1e\xf04\xc4\x17D\xf4\xcbh\xae\xde\xd7\x04I8\n\x0d\xb5@.\x1dQF\xe7 &\xfa\x042\x16C\x9aO\xabW:\x13\x96\x11\xbd\xdd\x0e9\x06Q\xa8Z\xbd2\x0e\x10)z<\x13?\x85F1YH\xc9\xf7\x13\x8c\xcd\x8cX/\xc8\xee\x1e\xeb=\xd5\xf6zz\x83\xe8^\xbf\x8a\x12\xc8{\x95@H>\x17\x8e\xaa\x885\xe7\xf0*\".U\xb1\x00\xbdI\x84\xad\xeb\x99\x08\xa2WuOY\x94K\xc5\xdeM\xb5\xc4L.\xc18v\xb5\xc8\xd5\xfd5\xb0B>\xb9q\xe1\xd2\x85\x95\x0e\xfd)\x9a$\xdalT\x17\xf8\x84h\x9e\xbc\x83\x11\x9c\xc3),`\x08\x9e\xf6\xddk\x18\xc1E^BW\xc7\x19e\xf4\xb4\xa2wT\xacY\xc3)\xcc`\x08\xef\x1c\xfak\xa6\x16\x7fA\x8b\xd3Z\xaf\xe5\xe2\xd7\xa6\xe2\xcfD\xc5\xd7\xean~F\xf9\xb9\x8f\xd62u#\xe3&\xf5\xe5`Q\xad\xbe\xba\xd7\xcey\\\xe23\x0c\xd5\\\xb3\xbb\xf2\xf6Zgy\x85+T.\xae\x04;s\\8\xa7\x909S\xfc\x06\x9aU\x1bB\xc4\xa1\xefJ\x0f\xd4\xb1\xb5\xec\x10\x1ea\x90|=\x8dz\x0d#8Cer\x1e\xd9\xc8:?g\x89\x0eg\xe7\xe7\xa6\x0c\xd3_\xc0\x08^H\xaf\x91\xeakzj\x87\xf6\xbe/\xea\x0e\x83o)\x8e\xc3)\xa4,\x984*Vk2H\xbe\x84\x11|\x81Z\xd8\xa28\xd1\xcbD\xc6\xc9\xbe\xb4\xdf\xba\xf0R\xcc\xe3J=&n\"\x03\xb5pQm\xb5\xf6L]\xbe;3F\x95\xd3qc\xec\xb1\xfe\xd4\xb7{\xbc\xaf\xf5\x0b\xc9\xbe}\xbf\x90\xaa\x8c&;\x88`\x01o6\xb3\xd31\x99V'\x83~2\x89\xbey\xb3\x19\x06\xb5* \x94#2\xaf\x8eLq\xe0\x88\xca\xbe\x1a\x99v~\xab\x93\x1b\xde\xcf\xe2\xb3\x91D\xc4\x99i\xe8l\xc48\x7f\x9cbXs[f\xf3t\x8aM\x90\xa6&\x8c\x08m\x8acx\xac\x8fi\xac\xb8\x9ad\x06\xa9\x81\xbbE\x1d\xeb\xa5\x80\xbd^\x95\xdf\xfb*_\xa7\"\xc0@\xe5\xfe9\x8b\xfe\x1e\xd3\x15WytI\x1c\xf8\xc8K\x15G\xd5\x92$\x80a\xd7k%\x81O\xbd\xb5N\x0c\xc8\x9f\xbfB\xa5v\xb5\xc8\x8d\\\x849\xb6T\x8b\\\xcaE\xce\x88\"l\xacJ\xcfQ\x97^-r^*\x82\xca\xf4j\x91\x0bE\xee\xf9^6\x9f\xab\x1d~W\x996\xef\xa7\x02\xf2\xaeZ\xe8z\xe3@\x94g(\x17\x9c\xc25c\x0b\xaf\xe7\x1b\x07\xfe\x13\xb4:v\xe1\xda\x85\x17.<\xab\xa2~\xf2.\xc0\x08|Z\x1d\x96\xef%\x04\xde\x0d\x158p\x06\x98\xcayA[\xa3r\x9e\xd0\xdb[`\xcf_\xcf\xe7 I\x8b\xe7\xecw\xad\x00B?)\x06\x10\xbb\xc0 vy\xf4T\xf6K-\x8f\x1d\xbd\xd0w4\xb7|6\xf5\xb6\xf5\xc2\xa6\xc4=\xc0\xab\x1e\xec\x1bqtY\xbf\xb1\xb5\xa5\xda\x1a\xc2\xd7\x06\xf8Um\xef\"\xbb\x9d\xba\xd0\xd6i\x9d\xf1\xedE\xed\xdbi7\xf4V\x84\xe9/\xf1\x1b\x06jY\x91$\xf1.9\x98\xff0T\x7fc\xe8\xf4\xaa\xbeYfYR\x83\x88\xe6\xef\xcf\xf4\xef\x0bQ\xcd3\xbcvi~\xed\x0b\xe6.P\xcd\x1d&>\xb9Xf\xd3\xfa\x13\x0ch\x8d'\xbd\x96\xd0P\xa0\xb4\xfaE#\xf6 \xe9\xed\x19\xd74\x98\x9b{\x9b\xd7\xf5\x16\xe7\xc3 \xaf\xc1\xed\x08\xe6.<+\x0e\xa2\xe6\x86_b8\xc5\xd7\x88\x88\xaf\xd1T m\xe0Zy\xf0Y\xa1\xb1q\xe1\xa5az\xcf\xcd;\xba\x10\xe3\xcfD\xccJ:\xa83\x11M\xb6\xf4\xa2^v\xbc\xbb\x11\xdb\xe9\x16 3\xf5\x94\xed\xae.i\xdb\xca\x87<\xad\x0e\"\x8cA\xf5\xa5\x89\xb7\xaf v\x85\x15\x8e\xdbm2\x85\x11:\xf5\xa7\x95\xcbq\xce\xb7\xa11\xfbv\x86W;65\xa1@\xd3\xb0\x8cx\xb0\xd7\xd3i\xcc\xfa\xaa\x08\xf5@\xda\x03\x9ewO7\x89\xa8Q\x81G\x10\xa8\xf38gv[\xcd\x89\x123\xef\x19S\xa5.1m\x82M\x1c\xc9\xd2\xd4\xf2\x8d\xf4\xa8Hm\x00#X\x9e\xc0\xba\xc6\xe4\x81\xb9\xb9\xc7k\x83]\xa0e\xfb\xa8\xb1\xc0\xdc(C\xc9\xcbn\xe1lh\xe3\xa0m\xcc\xd03YG\x13i\x1b3\x96[\x88>\x96T\x0c3\x0d]\x14\xe6\x82V%Bg\"+\xea\xd8\x0f\x8dCO>+T4\xf4\xe9il\x0dO`i\x9c\x99K\xb4\xa7\x88\xf91\x98UV\xe8\xce\xb80L_\xe6\xe4\xfa$\x1fox\xae\xf0\xfc\xbb@,J\x11\x7f\x86\x90\xd9\xf4H\x8cP\x86^\x89\xc9\x8c,\x9b3\xce\xe1\x94\xf6p4b\xc7y\x8fW\xc2P\x13\xeb=7\x9b\x9cQE\xa3\xe7 \x171\xf1\xde*OT\x83\xf0\x0d2L\x94\xb2\xfd\xc2\xb7\x1d\xfdF\x16u\x14\x1f\x0dI\x88\xbf7\xa6\x89\xbf@!N\xaaU?\xf5\xefP\xba\x93\x8a\xa9\x03\xba\xa0\xfb\xe6\x1dm\xad\xdc\xc9\x80\xa7lS\xa0\x8c\xd3\xdb\x96\xd8\xf0r\xd8\xf5\x0b\xfa\xecBV{#D[\x16\xdb|'\x97}\xc7\xfc\xd0\xd9\xd4o\xc0\x12\x13\x99)\xe7?(\x82o\x99\x88P\xa6\x91\xfa\xeb\x0e{=}\x0c\xca\xbb\xfbN`\x10\xe1\xc8\x85\xe0\xce\xc7\xe2\xbd\x9e\xfe\xbe\xd0Qc\x97\xd4ZE\xcd\x11\x8b\xefnpHc\xaa\xc6\x08o`G.\x84\x1b\xdc\x0ehf\xb2\x1a\xbd\x816^=)\xc5\xa7\xcf5KR|\xfat\x1c@\x1bX\x8c\xfaqh\xf0>\xbf\xfbl\x9b\xf2\xae\xe8\x8c\x11\n\x0b]s\xe6\xf92y\x11f+\x96\xb0K\xd5R\xf0\xd7.I*\xf1[vfNT\xddEV\xca\x0c\xa4#\x15\xc2J#\xa9\xe5\xc6S\x18V\x0c\xfe.\xc46\xcb\x1b\x94\xd7\xa6\x0dO \xd5XD\xb8'\x1aMh5K\x0c\x0c!\xd0\xe3\xa4\xf7-#M}\x92\x83\x9e\xc8\xe9/c\x91\x9e\xe0f,\x0f\xbf\x86\x89a\x8cN\xf4\xe2D\xea\x15\x8d\x83v\x1b\x13\xc4o@\xc1\x9aB^7N\x84\x81\xb8\xdc\xfd\xa6\xe6\x9eAy\xdc?\xd4_B\xd4'\x0dQme<\x81X\xbf*\x82&\x06\x1b\x9a\xee.\xd7\xf6r\xa8\x8e\xc4\x85\"\xec\x84\xb2\x92\xe8D\x83\xa99\x02\xa3\x00\xca\x9e\xb7\xd0\x19$\xd3\x96ZWJ\xb5\x96(\xbci\xcb.P\x0e\xbe\xbd\x859\xfdoI\xff[\xab\xa5f\x98\xb3\xfc\x94\xb2\x8c\x1c}\x99\xae\x8d\xca0\xba\x9c\xa1r\xce-\xa3\x84\x87~)<\xbe}\xcb\xcf74\xbb\xeb\x8b\xf2\xb3m\xb1*\x90m\xdf\xb0.\"8BUS\x01\xb6\xd6^LB\x0e\xc0\xf7\xd7\xac S,I\x05\x0b\xd5P\x05\xf8Z\xaa\xd2a\xe2\xda\x8d\x0bW\x0e~\x9f1\x03\xf7\x8d\x9e/\xcd\xee\xbb\x8b6&'\"-\xac\xa0\x17\xe9\x89\x03\xb1\xc8\x8a\x12\xea{\x17\xdfy+\xeasS\xec\xe96\xa2\xce\xb6\xdc\xb4?\x0c\xb4#\xe0w\xbab\xae\xa3\xf8\xb6h\xd4\xdd\x15\x1a\xa6\xa4\x1d\xfd\xaa\xec\x16\xe9',\xc3d\x82\xc5\xf4d\xe3|\xfa>^F^\xba;\xe0\xb6w$\xe3\x95\x87\x07{\xfa\x87/\x85\x86E\xf7\xa4\x7f`|dj\xacP\xd9\xe8\x1f=_z\xab5\x99\x99K\x98\xda\xa4\xcfJ\x8db\xa6\xdc\xb1\x0e\x83*o\xea\xeb+\xe9\xeb+\xcfr\xf3G\x05^\xe8\xee\xd5\x07D\x01r\xfbGu58\xae(\x0f\xd0\x18R\x81 \x03H\x05,<(*`a\x0b\xa9\x80\xd1\xfeQ\x85q\x9bG\x05\xfcC\xe2\xbd\xcd\xfb\xd1\xea\xbb\xdbm\xc1\x88o\xc1 '\xf8\xf8\xb3\xd5\xca\xc6tW61\xf7\xc6\x1d\xd9\xec\xcf]#L\xa6fu\xe5F\xfb\xb8F\xf3Ul\xf1\xbeb\xf3\x03\xbe\xcf-6\xc3\xa5d_tr\x18\x1b#\xdd0\x9a\x9177k\x06S\xab\xc0tQx&U\xeba)\xca\xb1\x9e\xb4T\x8f\xc6\xb5\x80\xd2\x10vs\xb8\x98\xe0\x11\xaf\x1a-O>I4~\xba^\x1da\x14\x9f\xfa\xc4\xd3W\xb6+\\Q\x95\xfe\xb1\x98S\\\x8b\xb3\xfbG}'?Zn\xce\x15\xfa\x86\x03Z\x7f\xa3\x03\xdav\xb2eu\xe9P\xf7\x14\xcb \xe3U\x7fx\xa1=\x1eO\x0d\"YHE\xb2\"\x85\xbct\xc8\nq\xff\x97U1-\x9eF\x8e\xb9:\x98\xa4\x8fm\xeeU]\x19\xd2tm;\x19b\xa0<\xe5\xbfQ\xfd$\x99\xbbF\xa0W(\x11>\xc2\xdc\x92{{\xdb\x9cv\xa9\x06E\x8eD\x8e~\x0c0\xe0\xf2\xa1nu\xed\xa6\x99\xba\x9a=!\xf22uW\x1bR\x9b\xca\x92\xf7\xa2\xb1\xd2\x90\x07\x86\x84\xd0\x067\xd9\xbdA\xd5W\x92\xfbP\x0e\xaa'4\xeeC9\xa8\n]\x89^F\xe3N\x94\x8as\x06=t\xf9v\\\x81b0\x0e\xbb\x1axg\x8d\xd0\xa8\x02] 4\xab@g\x08\xad\xe6\xdf\xa3\x07#\x89 \xb2L'\x1a\xb1\x84\xee\xae+4[\xc7\xf8\xbf$\xe4\xd8}\x87\x1dJ\x82\xd2\xbb\xc8\xed\x8b\xd7\x02,\x12\x95\x8a|?\x8eVABD1J\xae\x93hyElV_V*\x8c\xc2FQ_\xc6\xceD\xa5\"\xb9\x90Q\x14\xf3\x9cB\x87\xda\xbcA\xf5\x87\xd2P\xe7c*.;\x96\xb6sM\xc69\xc4>8\x05\x9f\xa2\xba\x9a*\x93\xc7?\x10^\x12Z\xfb\x1e\xdaT\xe7\xb5\x96r\xcd\xca\xa9\xdc\xce\xe4V\xa0\xab\x07\xa7\xd3P\x85\xc6\x03AWE\xbe\xca\x86j\xea]\x0e\xca\xebo\xa8\xc2`\xfe\xafV\x91\xe3\x87\x81\x94\x80\x96MT\x92U_mGovw\x1d;\xb4\x0f\x1d\x17,\xb1&\xa6(5[\xdej\x94j\xe6S\xfc\xf0\x15\x9f\x91\xf4\xe1+\xe5\xcb\xf0@\x15\xf7\x8f\x0c\xa1\xd4\xb6\xb7D\xe4\x82\x87\xb8\xbf\xe7\xf2\xdb)B\xb5\x1e\xd6\x18E#\xaeeW\xb7>p\xa6\x91\x8e#\x9d\xba\x94\xa9Kx~\xb4\xd8\xce\x1cSX[\xd8\\\x8a\xa9\xb9B`\xba\x01\xa9\x0f_\xb57\xd0)\x0b(\xbb\xd4\xc5\xaf\xd2\xad\x86PhV\xcb3\xfewXe\x8bs\xd5\x04\xbf\xdc\xf0\n\xa1A\xc6\xc8\xf8\xe1\xd1c\x99A\x13\xdb\xc7\x95%\xcdW+\x85\x9e;\xd0\x05%\x90Z\x90L\xac\xec\xd4\x90\x07\x17\x89\xd8\x9bh \"\xb8\xc0s\xb8\x85\xe5\x03\xc92\xfd\xa3\x8dn\x83\x1bL[\xb8\xf0\xba@I,\x9d\xa7^|\x96\x86\x1a\xc0)\xa6\xc1mJ|k\xe8\xfe\xce\xf8\xf3\xeex2\x9d\xb6o'c\xfbthwN'\xb3\xb6}:\x9ct'\xb3\xb6s\xea\xdc\xdac\xeb\xf1\xd4\xb1\xe9\xb3\xd3\xd6d\xe0\x8c?\x9fL\xa6\xb7\x93I\xd7\xf9\xf0\xd4\x99\x0c\x9c\xc9\xf4\xd6>\x1d\xe1\x1b\xb7\x93\xf1d\xea\x14_o?p\x9cj^3:\xdc\x9d\xc9\xc4\x9eL\x9c\xd3\xea3\x81\xebGN\x83\x1b\x8a\xe9\xc8\x02\xc5\x0c\xed\x1d\xb0\x9b\xb8\x98N\xf6y4#\x98RV:\x98X\x16r\x14\x11\xfa,.O\x17s\xa2\x8cLGa^GLq\xab\x94C\xff\x83>f\xa2E\xe5y\xaa3A\xc9!%\x18D\x8f:\xd16\x8bH \x8a\xce\x89f\xbf\xf9\x1a\x99I\x06C\xec\xab_\x05\x90,y\"\xf8\x00W5\x84\"\xb4\xa2[\xf1\x14\x026 \n\x8c\x11x\xdf\xf3\x17\xfa\xb8\x07w\xa6\xb4{\xbb\xfa\x83\xc6\xdench\xc3\x1ab\x86\x1b\xb6\xc5\x8f\x92\xe2\x8eK\xdct\x00\xbc\xcf\x11\xad\xd4\")\x9d\xc8\xef:5}\xc35\xfc-mj\x8a\xedL\xd8\xd4\xf4,\xe8\xf0\xae~\x00\xb9X\xe0s\xcb\x07\xe5Q6)\x82\x009\xb9\x15j\xc9\xbcd\xa0\xdd\xf6\xe1 \xcck\xafg'6\x19\xfbS\xa3\xdf\xceR\x90g1\xf7\xd8\xbf5=k\xa1\xbf\x8d\xfa^\xca/s\x97\x1eh\xc5\x074\xac\xd1>\xb6F0\x87SX\xc2\x10Z-{\x0ef\x031g\xa1s\xfc\x9b\xd9k\x17\xe6\xdc\xbekKq\x13\xef\x8d\x87\x06$\xbc\xbb\x97\xc2\xae\xde'doW\xef\xbf\xa2\xca5\xd9\xa6\xc8c\xe8z\xc4\x9cD\x98G\x01\x06\xbcj\xde9w\x9e\xa7\xbc@\x9d\xc2Z,1)\x87\xa8\xaaz\x8c\xdeu\xca7\x91J\xee\xd3\xfd\xb8\x12\xb9\x0e\xee\xd3\xd9\xbd\xdd\xaa2T\xa8\x83\xf4\xa9\xb2\xf7vu\xc4\xe8S/]tW\xdeu\xd3\xb0\xcd\xc2\x98W\xb3\xf5TMA\xcb\xcb\xd5\xaa\x9d\x8aO\xde\x95\x88\x98\xc1+\x13I\xcb#\x93B4\xc9\x13\x9e'\xe8\x0d\xeeA\x1b\x12\x0c\xbc\xe62^\x1c\xd0\xf9\xdeu\\H\xee\x8f\xb6\xc2\x15V\xd1o\xe44V\xf6eb\xde(!\xb4\x01\x05\x9e>\x0c\xa1\xd3wN\xf06K\xd4\xe9\xc0\x10\xda\xed\x88%TW\x90\x85N\x13\xb1\xe9\x91\x0b\xbd\xca$Et\xa4\x9d\x86\xbb\xc7D\xdb\xdbm\xce\xc4_#\xec\x98d\x12\xf8 \xe8\xeb%\x12\xb1w\xe9\xd2\x12\xe8\xa0\x10N`\xd8\x18\xc2\xc1<\x82=\x9d\xa8\xd2\x87\x9d\xaa\"\x0b\xe3\xbbt\x0f\x8f\x0f\x0f\x8ew\xfb\xbb{G\x07\x83\xdd\xfe\xfe!\xd9\xed\x1dm;\x01\xb9\xaa\xfb\x94\xf9^1S\x01\x13\xe3\xa8\x04\x8b_;\x01{\xcc\xc2\xbeu\xe8\xfa\xf7\x1d\xf8\x10\x1d\xeeR\xb1SR:r\xfc7\x92!w\x9d\x0b%^3\xd7&\xe8\xb4\xc3\xaf\xbcW*-\xd8\xf9|\x92\xb4o'I\xfb\x83\xea)\x83Ex\x1ew\xda\xd3\xde\xf5\xb8\xd79\xf6:\xf3i\xfb\x83\x9d@\x15Vv>\xef]\x8c{}\xcdS\x9f=\x8d\xc6\xbd\xce\xa1\xe61\xe5\xe0k/N\xc8\xcb0\xddvI\xe8\x8e\x91\xa3\xbd #`\xbeqR\x95\x10\x05\xb6yc\xa1J\xd3p=\\\xe0\xbf\xd6\xc6\x91\xe6\xd7\xcfN\x8b\xef\xecJ\xb3^\xe8\x89\xd9\xc9\x9e\xdd\x10\xa2\x9b\xa1T\xea\xbd:J\x11\xe4\xae\xa5\x19e\x19\x8f\xda\x95&\xd9e\xb1r2j\x95\x00\x87,\xac6K\x14\xa3\xdd\xc4xN\xf3E\x118\x85\xb9\x9dv\x93e\xe0\x13{\x80j\xa7S\x18\xc0\x10\x8e\xe8\xa8=\xa9X\x84}\xba+r\xf7\x15uK\x03\xb7\xdb\xab\x8a\xd8\x99V \xe7\xa6\x8f\xbdf!\xc9\xcc\x01\x19\xf7a\xb2\x12\xe5W\x86iC)4\xaf\x86\xb2-\x8aGL\x8c\xa1VE\xf1\xfcc\xd3\x172.\xdaf\xf0\x04\"\xe6\xe8\xd4\xc7\xb8q\x81\xed\x8d\xb3)\xbbH\xe6\x9c\x98\xf5\xd1\xa6\xd8\xe7\xdb\xae\x84\x9eN\x18\x82\x0d\xa9\xea\x98L\x08T\x1b\xac\xa7\x86)\xe0\nd\xf2\nT\xef\x1f\x89\x83\x93\xf0\x8d\xd0\xd2\xdeV\xab$\xd5x\x18\x1b\x86\xb1\x8e\x08\xf7e\xae\xe0\x18\x96\xa2\xdfz\xb9\xbe+\xe4\xee\x9f\xe1\x98L\xb7\x8f\x99ne \xc1\xec8~*\x99/\xb9\xd3\x05\x0b\x97!\x9clx<\x18\x92|\x1a\xcd\xb2%\xb1\\\x85\xc1,32,E\x8es\\\xbcs\xbd\x8a\x82/\xc9\xec\xcc[\xad\x97\xe4\xe38Z\x9d\xf9\x0b\xb2\xf2`$=|\x1e\x13/%\x7f\xe3\xd3O^\\c1\x16J\x0d\xbf\xfe\x8d\xd5\xb2\xf2R\x10\xceI,\xfdN\xd4\x9a\xb9\xa1\x1bH\xd7Wk^\x9eh\xf0\xa9\xaf\xa4H \x90\xe7\x87\xf6\xde>=n*H\x85\x8f\x0ev\x9dM\xa3\xb1\xc8|\"\xed\x16\x13\xc9e9\x95\x1a\xcc\xc8\xdc\xcb\x96\xe9\xb0z\xab\xf4;\xea7\x81kj%\"\xf3Q\x8e\x04&\xaa\xcc\xbb'\x90L)\xf3^= \xb2\xa2\xe7d\xe5\x05\xcb-Z\xc8\x12\x12\x7f\x97\xb0\xd5\xe8\xfa\xd1j\xa3\xb6x\xbf\xceg^J:i\xb0\"\xd6\xe6-\xa2\xaf\xc5G^J\x9cn\x1a\xbd<{\xcd\xbc@m\x8d\x1dBs\xda\xc5\xcd\xb9y[\xbd\xcd+=\x9f/#/}\xe0\xaa\x830%\x97\x0f\xdea\x1eD{X#T\x88\x8fX\xe5<\xee\xb6t\x8c\xe9r\x94fQ1\xf8\x0f\xb5\xfd2\xba\xab\x07\xd0\xfaN\\\xe5\xfel#\xb0{.\xc4]\xe6`\x11\xcco\x1c\xadB\x03rC\x8b\x9a\x82H|\x02|>\x8f\xe2\x95g\x88\\EI\x827\xc6\xfc\x91\xe7\x16\xb4!\x98\xa2\x0b\x90\xf6\x12\x92\xc0K\xec]\x90|\x9c\x85\xbecGx\x82\xb2\xd1\x1ek\xfd |\x1bF\xefBxs\xb3&C\xa0\xf5\xa5\xd8\xbb\xba\xa9\xf1M\xc40\xa7J\xa9^u)\x0e\x85\x9e\xf0%\x17\x97\xb2\x9fB\x1f\x8a\x9c\x14\x94\xc9\xe7E\xc6\xfd)\x15\xde\xe4\x9f\x98\xc7\xca8{\xcaR\xe8\xe2\xc5\x81\xf0\xf9\xadY\n\xb4yw9\xfd\xd0\x17\xf1\xb0\x08\xbf\xc4\x17\x10\x8dg/\xf0\xf9\n\xba\xdel\x16\xd0\xc9\xf1\x96\xdfo(?\xc7\xf2AJV\x86\x02h\x14\xe9\x06\xa1\xbf\xccf\xe43\xe2\xcd^\x87\xcb\x1b}\xd1\xb5\\\xf4\x87q\x90\x12ZV/\xe8I\xd3\x9f9e\xdc\x99\x11\xb2^\xdePz\xb6\xfe\xeb\xe4\xc6\xc1#\xff\x07\x1f\xc4dnma\xa5\x94\xe5\x8a\x92ou7\x08g\xe4\xfa\xf5\xdc\xb6\xfe\x8aU\xc9\xcc >\xefM\x16\xa2H\xef\x7f\x1c\xb0\xe0\xb7\x91\xe4\x1a\xae\x176kb\xec\x82hc.f\xc3 \xaf\x8a\xdb6^\x1c{7*\x97\x01\xedy\x01U0\x85\xb7\xf9\xc8l\xed\xbe\xe2\xc1\x06\x14\xcc\xae\xba1\xca\x9fY\xe56\x8b\xfc\xc9E\xf5+*\xd8-\x1cX\x8c\xaf\xa6t%\xe8\xdf\xee\x8c\xacc\xe2{)\x99\xe1\x8d/\xf9Q\xccq\x0d\xd8\x05\xb6\xea\xe3w\x02\xbf\xf0\xf9\x1a\xef\xb9\xcfh\x81\x11\xa46-A\x85B\x83\xd0\x8f\x13\xcd\xb4N\xbe\x03\xb3\xcav\xe9\xd7\x8c\x06W\x90\xbe\xee\xebQ\x01\xaa\x11\x0c\x94y\xf4\x1d\x97\xc5,\xb0o\\\x8c\xb2\xb6\x82\x11\xf4O`\x05O`\xef\x04V\xed\xb6\x03\xb3\xb1U\xee\x12\xa5\x95+:\xb4K}\xb78\xd2\xcfTT6\x91i\x8e?\x0c\x19\xe0\x94\xa7\xb2 \x12v\xbdl\xde\xf5\xc2\x9b\xd7s\xd4\x92\xb1\xaf\xdd\x95\xb7.<5\x9a\xee\xe6\xb2\xf8\xf3:\x9f\x08\x18*ME!\x11M\xe1\xd7\x07lj\x9c\xdas\xfa\x94\xd2q\xd2%a\xb6\xc2\x10\x8c\x82c\xcb\xdf\x87|\xa9B\xca\x0e\x97\xc1\x97\x04\xbb\xe7\xd8\xec5g\xdc\xa3uX\xf3`IX\x8a\x8d\x08\x1d\x9b\xd0\xa5I\x17/_U\x12\xdbU\x19\xbf\x9e\x96\x89\xe1u\x13V\xfe\xd1#\xa6\xb6\x17\x00\xf4h)\xb8\x01{\x8e\x1cF\"C\x8aO\xc6{\xd7x\x04\xd9\x88\xa1\xb2K\xcb\xdf\x1aO\x8d\xb6\xe1\xa9x\xff\xa5\x86\xa7z\xf8|\x13\x86\x19m\xc90\xa3&\x86\x19\xd5\xb3\xf25c\xba\x9b\xf0\xd4\x85\\4\xe7\xa9\xfa\xb23l\x99#\xb4\xbe\xc8\x15\xd26\xfd\xb3\x9b\x9ag\x97(\x86]\xaf\x96\xfa\xc7\x94\x86]b|2\xfd\xf3s|\xbe\x8e\xc9<\xb8\xd6\x97\xb8\xc8kH\xd6\x9eo\xa8\xe6\x1d\x9b\xda0[\xe9\x9f_\xe7\x87d\x03\x03\xcfj\x188\x9a\x07\x1c\x96\xda\xfc\xc7\xc1\xc5\xb3&.\x8e\xd1Y1l\x8c\x15F\xa9wI'\xc7b\xfe\xb1\xf69\x9c\xc29\x15\xcb\x87\x16\xba\xb6;\x94A\xb8p\xc1\xf4\xf37c\xfa\xdc\xba^-\xc3\x043e\x9f\xd3B\xf8\x13o\x03^\x18\x04\x1c\x99)\xa0[\xe5\xdcD|i\xe99\xc5\x07J8\xf0\xef\xed-\\\xd2\xff\xbez\xef2\x08\x0f\\'\xff\xa0e\x18\x96\xc0e\x97\xc7\xe0\xcd\x85\xbf+\xee\x95;u+\x1cbIy\xc3R\x8dZe\xe4\x0c\xf43\x17;\x90\xe5\xa4\xa2\x953?>\xe4\x08U\xfd\xbe\xf8h\xf8\xd3\x8c\xb6>\xdb\xbau\xc1V\xb6n]L\x03/9u\x01%\x9c\xa2\ns\xab\xe7^\x9a\xc6C\xb81T\xee\xc2\x95\x1e\x1b)e?3\xb8XB\xc1\x8a4\xabb\xdfsY\xce6\x9a\x15\x17\xce\x0c\xebb\xdfsa\xb6j\x9f\x97R\nm nk\xd3\x12\x01\x9f\xfa\x17zq\xbbA\x9c~F\xc5ii\xcf\xd0\x9d\xb8\x14\x1b\xf0\x85Y:\xa5}{Q\xb9jh?ct\xa3\xf5b\xfcL\x12\xbcooa-?(Dn*\x8c\x1b\xa6\xab\xd4\x0e}\x8b\x11\x89\xfc\xab\xe8!\xff\xdd\xa58\x1b\\di\xed\xb2\x89\xcf\x15\x8f.YF\x05\xac\x0b\xa54\xda\xd9\xfc\x971\x05K\xf5\xf3\x85\xe8_-\xd3\xae~\xde\x8a\xb78F\x99)\xbd\xf8\xdc\x8c\xf3Q\x0br\xf8l\x9a\xb3,\x14\x9b\xbe\xa0#\xf8\x82>\x91\x80\xcb\xf13<\xf7\xe0\xdf\xf2\xa3\xb7\x14\xfe\x96\x0214f\x82sQ\xbf0\xb5\xa9^\xe4O\xb9\xb3#P;\xef\xca\xce\xe9\xf2\x0cV\x84A1\x00\xbbT\x86\xc1Mv\x19\xe9s\xc5\xe3f\xa6lt\xcd/\x94\xd1\xe3%\xa5\x14|\xa7 \x19\xf5\xa3\xd0\xf7R\n\x1fJt\xf5e\xc3\xb4\xd5\x91Fq\x98\xe4\x0d5\x11\xea\xb2\xb49\x04\xebYx\x93.\x82\xf0\x12|/\x84\x0b\x02\x0b\x12\x13\x83T@;\xedo\xca\x11\xaa\x0d%\xa6s+%r\x0f\xc8g6\xa0\x91|\xe6\xae\xcb\xf8\xbf\xe4\xae\xb1\x12h\xc63&\x94\x17\xf5\x1d]\xd4w\xecT\x96\xb0\x80kl\x85o\xe0\x14\xc6\xfa\xbe\x1b\xfb\xfd\xde\x85kZ\xd1u\xb5\xeb\xef\xb5v\x90\xa5\xd9\x17\x81\xca;\xeci\x19K\xd1\x08Z\xd2s\x05\x82n8vX\xb5:\x01\x1aJ\xfc\xa5\x17{\xb4\xc1!\xb44\xd7\x1b\x83pF\xc2t\x08\xd6$\xad\xdc\xae\xab\x9a\xcb\x00o1\xd4X\xa5h\x7f\xa2\xa2?\xcb&\x13W\xa5<\xc7\xa9\x06\xab\\\x0d\x87\x96<\x05\xf6\xabn1PxK\xec\x0f\x9c\xeeY\x1a\x13O#\xfe\xa3N\x8c~\xb1\xa4\x15\x83\x8a\xf5Jo\xf5\x04\x919\x80\xd24\xcd\xc9\x01=\x05\xd0\xa5\x11\xc7\x1e0\xd1!\xbf\x92k\xb3\xf7\x9c\xee\x17Q\x10\xda\xe8KgYU\xdb\x9a\xf8$\x94\x8c\x19\x84oC4\x08\x1b\xbdD\xd3\xb1\x142\xe0-\xb9I\xec\xd4\x19\xf7\xa6SdyI\xf7\x9c,\xc9\xaa0\xdbr\x80\xa0\xdc\x91\x9bC\x02?\xcaB*\xfd\x84\x12\x0c1\x89\x0d\xab\x0c\xa3-{20%q\x9c\xadS\xcc\x00'\xc0\xfa\x19\xf3\x99\xd3\xbe.4\x14\xf0S2\x957\x95\x87\xf9z\xad\xcd:\xde\xf24l-\x02\"y\xab\xf5m\xa8~r3g\x1b\x1e\x8f\xac\xc7\xd0f\x0epmxl=6\xbe\xf8\x1e\xbd\xa6\xc7dj\x14,7 \x93\xe2z2\xc7\x08%\x94\xad\xf8\xe0\xa5\\\x81B\xfa\xbb\xb9Pv\xc6\x18\xd1\xca\x0c\xf7\x1a\xc4'\xe9\"\xcd\xa48\xb6\xb6\xf9\x0f\x0cty\xee\xcf\xbc\x14\x95RK6\x9d\xb6\xf5\xa45~\xfe\xd1\xb37\xcf\xc6\xf4\xc0)J8\xb9\xe3\xde\xced:\x99>\xdd\xb9t\xc1\x9aN\xa7\xd3\xa7y\xf1\xa7xx\xb5\xa6\xd3\xa7\x16V\xcdW\x13Q\xdf\xe7\xa1k\x96\xd2=\xaed\xc3\xf8\xc5\xf2G\xbb\xb7N\xc1\xc2\x01!T\xd9YpJ1\x90\x0f\x19\x86\xa2\x0b9\x15\x816\xf4\xf1r\x81\xbdd\x89\xb5]T%\xb5zyo\xd1\x13\xd3,T\xbc\xc77no\xa5\xc1\xd5\x8865\x0b%L\xea\xc6w\xf3\xfe$\x9a\xee\x189\xb3~F)E\x19B\xa4\xdf\xd49}\x18\xd2U\xd3\x16\xc9\xc5\xfdd\x08s\x83F.\nS\xe4l\x06e\x13#aC\x08M\x9d@\xca5\x04\xaf\xeey\xd5e\x15\x94\xa9xo\xe0#^\x1d\x1f)\x11\xf2\xc2HL$\x97&\x8a\xcf\xba\x08\xf1\x82 \x12\x89\xcc2\x0f|\x0c\x9fK\xa7$\xbf\x9d`\xa6\x9a\x81\xd14\xce\xd3X*\x95\xd5\xed\x1d\xe1$W\xbc\x94,\x82yZ\x0d\xa8#\x7f*\xc6=\xadKX\xb5|d\x07N\xb3\xc2\x8c~p\xf25gp\xf1\xd1K\xe9z([\n;F\xed\xf5)\xce;\xe3yB\xa1f\xf3\x94\x0b\xa7`=\xd9\xa1T\x8d\xffn\x83\xf5\xd4\x92Kq\x06\xfa\xe8\x11\xb4BZz\x12\xf2\xc7\xe8W\x8c\x17\xc9t\x1b\xcf\xbc\x8aQ\xa3\xd9\xa3\xd5\x92\xf1\x04\x9dr\x8b\xdf]o\xbd&\xe1\x8c\x8a\x0d\xae\x8cO]\x06\x0cJ@\x11\x1d\xccn\xf5\x1c\x17Z\xbdMH\x04]4\x8e\xc9\xf9\xac\x95\xe7K\x9a.i\xa2\x8a\xdd/,\x07\xa7`\x01++=CI\xca\x02\xcb)\xde\x8dq\x85D\xf5|\xfaqo\x08\xd8\x8eiM\xc4\x02\x97\x96\xa5\x15W\xb7\xa4xC.\xa8\"#\xae\x0c\xde\xbd3]\x87\x82\x1a\xa7;-\xcd\xd0\xd0\x0bD\x1a\xf4H6\xa8_9\x0d\x0b\xd5\xb52Q\x16\xf41\xc5\x08\x00\xdd\x04eh8e\x99Px\xaax\xb3\xb5\xc3\xb2\xcc\"\x9c\x89\xcc\x0bW\x00>\xa3\xfc|,A\"\xda\xac\xf894\xb6\xb1\xe0q\xe4\xcd[ef\xe6\xfe\x0b\x863\xe4:}\x13\xf8o\x99\x13J\xba\xe5N\xbc\xaa\x95\x0f+\xc4\x0e\xf5\x1e\xf6\x1c\xda#\x96\x8c\x12\xf2\xd8\xab(\xc9 \xb7\xc79\xe7\xd7V{\xa2\xd0\xb2\x89\x08\xe3\xc1\xd2L\x1agv\xa3g\x94\xf8\xf8]\xb2\nR\xdb\xa2\xd2\x99\xa5\xb5\x9c\x8a\x0f\x15P\xd8\xfaoHT\xeb\xe6\xf1\xa6v\x1e=\xfb\x8a'\xa0[\xbb\x98\"\x91\xb2\xbd\x9e\xa3\x0f\xed\\\xd3\xca\xa5q\xf8\xccf\xdf0\xcb\xe9\xb75\xcb)\x95\xf58\x88\x843\x0b\x7f\xc6\xc4\x9by\x17x\x00\xa7\x04H<\xf7\x97QB\x0c\x91\xee@\x7fl\x00\xc3rT!\xc2M\xa0y\x1c\x0b5=$p\x94\x08\xbb\x92j\x02q\x1b\x8f\xee2\xd4\xc5s\xae\xbe\xe6+\x12'\xa8\xd3\xb0\xfa\xdd\x9ea\xd7\x93\xd0\x8ff\xe8\xe1\x19w\xc5wFr)\xbd\xfa^\x8a\xd9\xd4%K\xb2b*\x85\x02\xf6\"\x87\xd5b\x9f\xd8\x87\xfa\xe1\xa2\xc2a\x08\x99\xcd\xb4\x81E\xecD\xbc\xc8\xc5\x82\x15\xe6\xbe\x06&%\x0c=\x0dm\xe2\xf5 \xc2\x9a\xcb\xf2@\xa2L\xe5@\xba\x88\xa3wH\xc61(\xacm\x85Q\n^\x92\x04\x97!\x99A\x1a\x81\x07,\x14uK'?\x88\xcf\x95\x94\xaa\xbb\xde\xdePdG\x96\x143\xe6\x8a=[\xea-'\xaa\xa1[\xaa\x81\xa9\x80\xdaT\xc0\x10\x94V\x0e\xbc\xdfD\xdb\x08\xaf\xdc\xd6\xc9\x8a\xe2c\xa2R\x86#\x1f\xa5y\x9b.\x89\xc4p\xd9\xee\xa1Ccv<\x91\x01\x9a\xca\xb9\xe2 \xed\xe9\xc6$S\x9dW!$\x96\x91=\xffU\x8a\x1a\xba\xbbg\x88\x18*\x0fG\xb0\xf3\xf2\x00\xadG\xd6\x10\xacG\xdej}R!\x8a\x8f\xad\xc7\xf4\xc9\xcffQZ}d=f/\xad\xa3Dy\xf4\x04\x1f-\xd5w\x9e\xe2\x83\xcb\xf4\xa4\xa0\xa3\xd2\xb0\xb7\xbal\xc5\x89\x17\xa7lH\xbcru\x8f=~d=y\xfax\xea\xec\\\xd6LF\xa5\xc2pL\xaaI\xb4`\xb8m(\x8a\xd2%\xba\x93\xd2\xbc\xf3[\x11\xfd}\xa7\xfb\xe2\x8a\x84\xe9\x8bU\x90\xa6$\xd6)\xf9\xd5\x83t\xccc\xa1.\x02\xe5Z>\xfd\x84\xf6\xee\xbec\x07.&\xd3\x0d\xba\x9f\x15\x14\x93\xb6x\x80\xc0\x1f\xc6A\x9a\x03\xf7\xf6\x8f\x11\xf8Q\xb6^\x92k\x06:\xe8!\xe8M\xec\x85\xc9<\x8aW\x1c\xdaG\xe8\xf7\xbd$y\xb3\x88\xa3\xecr\xc1\xe1\x03\x843\x9d8;\xd8\x05r\xc2\x8f\x00\x9d\xc1j'\xffJ\xca#o\xd2\x9c\x07\xfa\xd3h\x8a\x06a\x1c\x0e\xbb0\xc5X\x0dZ\x89\xe9\x1b\x18\x1bh\xede \x91\xbe*\xc7&}\x93\x91\x96\n\x85\x05\x1f\xc2\x1ac\x92d\xab\xd2\xf7\xdaSY\xd8\x8d\xc2\\$\x0b\xd0\x81\x0e\x01\xb1\x17\x84\x96\x0b\x11B\xce\x83\xe4,\x9d\x05\x11\x957\xe4\x81\x11$*\xb7\xb7`\xb3j\xa8\x18\xe7\x82\x87\x02\x11\xfd\xcd\xc46\x17\x92\xaa\x16\xef\x8a\x874k\xf5M\xf3\xebi\x07\x9bac\x19\xe7\xb8)\xa3c\x9b\xcd^\xb2A\x85\x86{\xe03\x92\xa4qt\xc366\xff\xb1i\xb3\xbe\x9en\xa3\xaf\x90\xed\xb8\xdcN\x1cw\x97A\x92\x92\x90\xc4\xcf)\x1f\xc2\xfd\xe4\x82E(3\xb5\x1c\xc1_\xab\xf4V\xdf\xe2\xdc\x88&\xab\xe8\x8a|\xc2\xdb\xa9\xac\xb9\xf2PZ\x7f\xf5Uy\x9d\xab\xcf\x8a5\xd7\xbe\x89#\xa2\xc2\x92\xaeU\xf9\xa9\xa9\xd5ym\xabsm\xbd\xc5\xd3\x9a\x9d \xc8-\xc3\xe4R?\xab\x10\x19\xdb\xe7\n\xb6\xcf\xf3w\xca\x10v\x94\xa1\x04\xc8b^\xceM4\xdca\x8ec5d]\x7f\xab\xaf\xa0\xeaG=\xa7\xcb\xc2\xe3\x96\x19\x9e0\x1e6\x86\xc8\xa9\xa2R\x8ee\xa9\x16\xcbZ\xcd\\\x0d\x84\x00i\xa7 %\x19#\x8e,E\xbe\xb9Y\x13.I>\xf7B*LR6\x03\x1e\xf8K/I\xc0K\xc0\xcb[\xd2\x1c\x0b\xdf\xf3\x0d\x94\xcb>\x0b\xe2\xcd\x80E\xa3\xe1\x90\xd4\x0b\x96e\x08?\x0e\x8c\xaa^\xcb:$I\xd5\x8c\xe6\xf5r\x9a\x10m\xf5\xf3A\xb7\xa21S~H\xaeS\xa6\x8eR\xc7\xa9\x8af\xf2P\x9eb\xc0\x92|\xb8\xa8\xf5\xc1\xdb\xc0\xc3\xd2\xac\x90\xf2\x94\x10\x17\xdam\xa9\x9a\xf2l\xb8\xa5\xb1g!\xea\xbe\xbf\xfd\xe1\xe7\xfd\xddd\x0ex\xec\x0ci&\xd0\x11\\\x1ec\x051\xb6\x19\xb32b\x13}\xe7\xe2xQk\xddy5\x15'\x1a\xda\xa3.\x9d\x91Z\xbf\xc3\xbe2\xc4\xd3\xd2\x80\xaa8^Y\xf2\xa2%:\xbd.t:RU\xda\x98\x85u3\x82\xb1\x0e\x9bf\xa4\xaew\x0d;\xb0\xdc\xda\x17Q\x106\"\x1c\x9b\xffQu\xfe\xc5E\x0f\x8d\x17s)\xean\xdeY\xe6Zl1m<\xae\nO\xcdM\xe7\xed\xc4\x81\x10\xda#4\x81\x13\xc3\x9a \xaeR;\x7f\xe8{u\xcf1\xc5]o\xb9\x8c|\xbbg\xf0cV0\xa6\xd0\xf57\xa0]13xj\x0eXl\x08\xde\xde\x0f\xc2\xc4\x9b\x13;\x85\xa7O\x9f\xa2v2+O\x9fG\x97\xf3\x04\xb2\x13\x07'.\xc36\xd8\xacF\xfc\xe2\x04^\xde\x8e\xd67,\xb0\x01}\xa5-\n\x96\xa2\x18dl\xd2MS\x1c)S\x9c\x03\xdeSI\x0b\x03s\x06\xdd L\xd6\xc4OK?\xba~\x96\xa4\xd1\x8a\x91\x89\\9\x93/\xd0\xb8ZpZ\x87\xecb7\xe7/i\xd4jlXC0\x92\x1c}\xb8\x1e,.\x05z\xcfMo\xec\xe2h1^\xe3\x89{c\x7f$\x1d\xfb.sw\xbd\xddF+\x90\x88\x0fS\x1cu\x13\x92\xbe\\\xad\xc8,\xf0\xcc\x1e\xae\xdc>\xc3|\x8cx\xcab5&\xb3\xfc\xf1k\xaej\x007\xdb\x98L3\xc0M7iw\x16\xf9\xa8(3\x97[\x97\x12B~_ \xc9k\xcc*\xa7}`\xcc\xa7N\xab\xc2\x8clk:'o\x82\x15\x89\xb2\x14NaM\xc9\xb5[D\x8c\xe7yk\xa6\xccq\xfa\xab\xf7\xdd4bW\xdb\xf9\xe9[$\xb6aQ\x8b\x9a\xe8\x88\xf8Hf\xa0Z\xca-\x7ff\xb6&\xaa\xaf\xf8\x98\xf4[0\x94Q\xa7\xae \xb4\xa1v\xd7Q\x92~\xca\xb3\xf9\xb3\xac?\xc1\x8an\xc93?\x0e\xd6\xa9\xd1\xddG|\x04\x11\xd79\x08V?x\xcc\xefF\xe1\x8a5Woh\xcf\x85\xbf\xbc|\x13\xd3\xab~\x88\xde\x84 \x7f\x18o(f\xc0\xb6,\x17\xac\x0f-~\xa8(\x1a\x0e\xab\xa1\x94K\xb5\xe8W\xc2vP!\xc5\xab~\xbe\xf0\xc2\x90,\xe1\x14l\x1b\xa3\xa7\x90wP~\xe4t\xe9\xbc\xf7\xf5\x03\xaeE\xae\x99\x9d\"\x057\xa9<\xb7\xc0\xd3\x08;1(M\x8a\x01\x0bQ5\x86\xc6E+\nc\xe2\xcdn\x92\xd4K\x89\xbf\xf0\xc2K\x82i\x92\x97\xa3\xddvD\xbe\x8b\xe2\x0e.Z\x06\x0d\x97\xbd@r\xfb\xaa\xdf\x85\x94\x1f_x\xfe[\xe3qV|\xbc\xf82\xd1\xf9\xdb\x89\x8f\xe1\xae=\x14l\xc8\x1f'S\xa6\xdf\x8e\xed\xc4q!i\xb7M\x08\xb7fG4y\xed\x16J\xd9:\x1f\x82\x85y\x89Yzw\xf0\xab\x81\x9b\xa1\xa1\xca\x1a\x1f\x15T\x8e::\"\xa1\x9f\x94\x86\xbb;\x02[h\x17\xeb}\xf4\x1a}\x9e\xe7\xdc\xf5\xa6\xaeL}\x9a@\xf1im\xb8{\xe4O~:\xed\n4k\x16p\xc4'\xc6\xf7(\xd6\xd5\xf7^|\xf2\x14P\x0d\xba\x0b\xdd\x07\xfd\xae{f\xdf[\xdd\x87\xd4\xf9O\xea>\x0d^\xda\xd5\x0f\xf6\xa9\xbfm\x9f\xe2qo\x93\xbbU\xf2\xe7.\xfd\x1a\xdc\xa5_.\xc4\xe3\xfe\x8f\xa3w\xbbw\xef\x1d\xfd\x7f\xf0-\xf7\xb1\xd1\xd5[\xf7A{\xfd\x12U\x0e\x1aw\x0f\xddG/Q\x97J\x98\x84\xa3\xbc\x00\xcc\x83\xd0[.7\xa1\x0f\xccp?\xdf\xe0\xbc`|\xba\xa9\xdfoE\xb7g[Y\xc8\x02\x02\xcedY(!\xcby\x11\xa9?\x0fN\xbc\x08\x12\x0c\x83=\xc4\x02\x92\x0d\xb8\x949\x14y\xb1\xd9\x15`\xf3[Q9\xfb0\x90M3\xf1E\xdd\x03\xe9.#\xdf[\x9e\xa5Q\xec]\x12)\xa2\xa3:)r\xfeTm\x855\xef*\x10aQ.\xb7\xaf\xe5GBa\xc8sn\xa07\x99\x95\xc6\x19a\x87\x7f\x1e\xd2.t\xbai\xf4I\xf4\x8e\xc4\xcf=\x8d\x01Y\xfe\xb5q\xf0R\x10wal+\x8c>\xe2A\x88\xd0\xc0b\x8a\xbd\x0d\x92\xb1\xa9\x1a\x15\x13\x8a\xb14\x9eapm\xb4ai\xe5\x12\xa1m\xa1\x85\xa8\xd2\xb5\xaa\xef\x91\xee\x1e\x81\xf8\xd0*b\xcf'\xa5*\xe0\x14\xfc(L\xa2%\xe9\xe2C\x16\xc0F\x80\xdeyq\x88g%\x1c\xa4\x1aD\x0f\x8c;-W\x170R\x93\xa2I\xaap\xc4j\xda\x87\xc6\xad\xb4\xd1\x1e\xd2+\xe2J\x19\x96\n\xb0\xe4\x06r\xac\xcb\xa3\x14\xda\xfb}\xed\xad\xcfH\xdd\x1e\xdc\xb6G\xe9\x82d\xde\x8b\n\x1c\xa2+\x15\xa9\x01\xc9\x0bG\x12MpS\xac\xb8\x1b\x84\x0b\x12\x07\xd8yt,q%\x98\x1d1'\x93H\xd2\xab\x9f\xa7\x92\xcbH\xddd\x01\xa2\x06\xb7DT\xdb\xde\xc2\xb3\x86.\xcf\xe1F\xcbS~k\xd0\xbf\xc3K\xfd\xfe\x81S8\xc5\xdc\xf1}\xc9}f\x93\x1a\x9a\xec\xcd\xfdc}\x16\xc4\xfe\xb1>\xcf\xcd\xdeAs\xac\xf6\xeaBqK\x04\x0bH-\xc7P\xd2\xeb\xcc\xb3\"zU\x8c\x97R\xd1*g\x13)\x8a5\xe6\xd6\xcb\n\xebWau\xe8z\xc9M\xe8\xf3\xe4\xadYw\x1d\x07\xab \x0d\xae\x08\x9c\xe6.0pZn\x02\x87u\xbc\xef`6\x0c\x1e\x03\xca\xd6\x948pl\x82w\xe5*\xcf\xa4zi\xb1C\x07S\x0e\xc8\xc0\xfd^\x9f\x01\xe9\xd7\x01V\x93w\x15\xfd~\xec\xfd\xde.\x82\xd6,!\xa7\x00\xee!p\x16$\xeb(\x07\xf6\xd1f\xd3]y\xd7\xcf.sX_\xc0\x04\x80\xbd\x19\x939\xba\xa7\x90X\xc0\x0f\xe8\x8e\xa3\x88\x92m\xb9k\x9a\x10i\xef@\x17\xb9\x1du>\xdeE\xa2\xa2\x12>\x99/#9\x97\xf5f\xe8\xc4\xd1$H^y\xafl\x8c\xfb\xcf\xd2x \x96\xa40\x82W\x18\xc3\x153H\x0d\xd8\x9e\x92\x07\xc6\xcb\xc9l\xfd\xe4\xe8\x02\xd9]\xb1 v\x89\x0b~y\x81\x03L\x9dBe\x1f\xbb\xc8?_&\xb9\x8eDv\x04\xb9\xd1\xb8\x83\xbf^\xd3\xc6\x13x\x8c\xa5\x1f\x83\x17\xce\xe01/\xfe\x18|\xe6\xe2sA K\xd0]\xfc\x92\xa4\x0b\x12W\xb5\xe5|\x19\xcbazr\xd1\xc8:?\x17\xd1\x19\xce\xcf-\x16\xaf>\xec\xce\xa3\x18\x9dp \x0cYf)\xcf.B\xe3\x93\xfc[X\x0c#\xe24\x9f]\x0c\xcbh\xd5 s\xd7\n\xa8\x8c\xd1(A\x87c\x82q]R\x1e\xa8\xddW\xee\x13\xb1T\xce\xe7\xe7\xeb8\x9a\x07K\x12\x9f\x9f\x03\x8f\x14^@0$\xa6\xdf\xcd\xd63/%/\xc2+\xbcJ\x9d\x87\x9fx\x90\xbd\xd3\x88\x93\xbb\xba\\\xbcBU+\x89Y\x17A8S\xb1TS\x90.\x95\x8a\xb6r\xe2\xff\xd2\xc3\xa4x(y[\xf1u\x7f\x99\xbc\x08\xb3\x15\x89\xbd\x8b%i\xa2\x07\x9b%j\xd0\xde\x84\xa2\x934g7\xd3\n\xbc\x1f\x18\xe27\xacK\xa5vk\x0ew\xc5n\n\xec\x90\xa58\xf3\xf9q\xdf\xb3)\xae\xa1Ux\xdeM\xa28\xb5\xb5\x04v\x8d\xa9W\x11\xf9\xd7\xb8\xdc\xc3\"\xfbL\x83\xc6}>N\xa7\xc8\xcf\x99\xc4\xed\xd2\x01\xca\x93e<\x88\xf1\xde'\xecE\x96R\xf8T\xd4\xe3\xbb\xb0t!\x1c\xa7S\x17R\x91gD{\xa3\xdctX}\x10\\\xde;\xacRR!\x81\xea\xf3E\x1c\xe9\xd3E\xec\x1d\xf5\x9d\xee\x8a\xa4\x8bh\x96\xe8(\xed\x9e\xf2\x1eg\xd6\xc7\xba\x04\xd3\x9a\xbd\x80g\xc2r\xc9\xf9\xa6\xbbfYl\x0cff,?\x96\x1c\x14J\x89\x1d\x94\xf0\x9d\x0b\x94\x81\xa3J\xcc\x80\x19B\xc9*hL\xdd\xa5?H\xa1o\xb7\x0bW.\xdc\xb8p\xe9\xc2\xca\x85s\x17.\\x\xe7\xc2\xb5\x0bg.\xbcp\xe1\x99\x0b\xaf]\xf8\xc2\x85\xb7.\x86\xb1Z\xe2\xe9KO\xf0\xaf\x98T\xdc\xe2\x020%\xe5\x9cw\xe7\xbai\xc6\xabS\x89\x9eK25\xc5\xfb3\xcct*\x831\xb8\xd3\x08\xce\xba\x97$e\xd1\x87\xcf\xba \xfd\xba\xc2\xaf\xcc\xac\xe1b\x94\xce3f>q\xdcB+\xd3\x8dI\x12-\xafH\xcc\x82\xcc\xbe\xe5\x9c%\x87\xd2=\xfd\x05\x8f\xbc\x144\x04a\xe1\xfc\x97\xfbU\xe5\x04D\xa5\x1e\x94\x1fcp3\xb4\xd6\xbf\xb5#\xa7\xe8\xd2\x88\xf1\xe8\x1b\n\xa4Et\\\xf2%]\xad\xfc\x1c\xfe\x82\x16\xcb\xb8W\xf2%I-\xdc\xb4\x11\xf3\xc5s\\x\xa9\x8dhO\xfb\xc0\xd2\xf2a\x94\xe4\xc2\xfbp\x9e\x93\x13v\x86\x8f\xc6\xbd)\xeaQ\xaap\xd1\xe7\x11\xcb}c\xd6\x08iF&D\x8b\xd8\xb6\x9e\x07\xb1\x9f-\xbd\x18\x82\xf0*\xe2\xaa\x1c\x17\xac\xe7/?{\xfe\x83O\x9e}v\xfe\xf2\xd5O\xbd~\xfe\xec\xcd\xcb\xd7\xafLVwZ\xeb\xa5\xad\x89_\xfe\xbe\x08i]3\x8d\x0f\xd4\x13\xbe\x1a/\x99=2p\xe1\x99\xbc.\x89X\x17n\xc1\xa7bH\x99|\xbap\xe5\xe4y\x07\xe9\xfe\xa8\xd5\xb6\xe1\xe1Y\xbf\xaa\x86\xa1\xb2{\x02\xb5h#\xae\x12\xe4\xa8[\xe0\x90\xc1\xa5\x10\x8dm\xba\xa0\xc9\xa7\n\xbe\x14\n3\x18V\x90\xccqMh\x9ew\xfa\x81\x17\x89\xf9\x03\xa0\xbf\xb0f\x99\xf2\xfb\xe3\xb8VD\xcdu.\xa7\xfa\x7fXR \xdf\xefD\x8e\xc7\xf5\xc4\xb8\x0b\x8d\xd3\x14\xd4.kP\xa6\x06\xba\xcc]\xb8M\xefK\x0dj:\xf7\xc0\xcb7\x0e\xe8\x1e\x0b\xb5\x8b\x17\x88u\xa3\xe2\x97\xe2\xae\x9bi-\xffQ\x1c\\\x06\xa1\xb7\xd4Z\xfb\x85\xb0>\x84/\xd4\x87\\\xd2\x7f\x85\x91\x83\x90\xdb\x8b\x9fj\xd9K\x92nr\x0d\x94\x0f\xf2m.\xe7\xbd\xb5S\x07\xb9\xdc)\xdc\xb0@\x0f\x1c)R\xba\x18*\xd5S[^x\xc9\x16-\x1b\xd6Q\xe3\xda\xa3i\x8a\xf1\xdbMZ3\x900`\xfd\xd5\xf7\x00\xe7\x04\xfd{W\xccM\nF\xf0\x12EU\xee\xbe\xc0~\xbc\x96\xd1\x82=\xb1P\x9a%\xba Q\xea PL\xd8 #\x8fP\xac\xbc\xd4\x0f\x03\xcf\x83\xe7\xf4\xc8'\x89Fn\xde1l\xc5\xdatb\xa3R2\x9f\x9aK9B\x9dC7\x7f\xae\x0ey\x81F\x0f\xccI&\x83\x9f\xe5`>K\x85\x1b\x95\xfdZD\xf1X\x94T\xfa\xfa\xb8\x15j\x7f\xe9\x18\x870S\x1f\xe4g\xe1\x0d&8e\x92-\xdf\x9ej\xb3\xd5\xed}\xa1\x8aj\xe6{,n9\x87\x8e\xba\x86l\x0b\x86\xb8\x05\xc3\xb2\x8cFP\x92 \x99\x8c\x96q)\xb3j7\xde\x92\xa7\xe7\x8an^\x1bg~\xe5*\xa1iki\xc8G\xc1T\x18\x17\xc9[\xa8\xa6=w1\n}P\xefF\x8cH\xdf8w\xbc\x1b\xc5\xd09\xcf\x1d\n~'Mk\xcaW\x8dNhA\xddB\xd6Y\xba\xa3U\xbd\xcb\xf5\xb7\xd6\xcf\xac\xbb\xf0\x121\xf7\xda\xee\x16XP\xd3q\x8e\x18\xb4\xaeT\x93pum\x7f\xa1\x0b\x8c*\xeb\xbe\x86\x10a\xd8*#\x89\x8d\xec\x0b\xcdSN\xbb\";\x13\xa7\x1d\xb5\x15\xe4D\x91\xfdN\xf7\x0cyEd_\xab}\xcer\xc8\x83\x9c\xf0\xfb\xc7\xba\xfc}\xf4\xe4\xaf?\xe1\x0ft'|\xd4Kv}o\x9df19K=\xff\xed\x9b\xd8\xf3%\xb6B\xe48\x1d\x8d\xf6\xa8\x90;#2u\xa7.\xf7\x98\x07\xe5\xfc\x1fj\x89\xa4\xa2c\xd2\x9e\x85#;\xe1\xa1\xb6<\xc6\xd4x4R\x91\xb8\x1f\xed1\x89\xc8\x14\xc9n\xe1F\xa2l\xd8\xf5\xa3\x19\x8a\xddxO\x87\"\x1a-CJ\x02\xcf=\xd6hs\xa3\x02\xe3\xc0\\I\xc1\xe2\x84ln[`\xb1l\x88\xad\x8f\x882\x8f\xa2!X\xb1\xf7\xa5U\xa5Qj\xd9\x0b\x8a\xf1\xd6\xec\x9d\xb7A\xd94\xfe\xf2f\x08\x16\xfdS\x0d-\xecb\x80\x9a\x08s\xb7]x1\xcb\xe1\x16\x7fy\x83\xb4\x81ve\xf6\xce\xc3\xf7\x1eXo\xbbgH\x8d\xaaU\xdc\xa2\x11g\xe5]o\xa0\xd41\x18\x08\x8a[8\x91\xe2o\xeb\xc2\xa0\"w\xa3\xa3n*+:Q\x1a-yhk5\x8df\x17\x9et\x1cS\xf9\x9d\x8cc\x8d\xabi\xa3\xbfN\xc8\x02\x15\xd0}\xdd\xe8{\xc1\x04\xfe\xfe d\xf0\x04\x92\x13h\xb73v\x7f\xad\xd8\xa0\xd9\xd4\xc5\x80\xb7yh\xa2jv\x82J\x1c\xb407\x8bh1\xfd\xdb0\x1c\x1e\xee3\xc3\xa1\xa4ag\xa6\xc3\xc3\x83o\xdbt\xa8_D>V9\xae\xac\x95\xdb\xd4-\x8c\xb4X^\x87\xdaE\xd5;`=\xb0>Y\xe1\x1eA\xd9d\xd1\xb4\x9d\xaa\x1d\x17\xe6f\x8c\x84\x9b\xaf\x0d;\x9em\xebzr\xa7\xbek(&oB\x1fR\x9d]A\x1b*Ks\xc7\x81\xe3\xb0\x1f=\x82`,\xec\x12\x98\xbe\xa1\xf5 f\xd6*\xfe\x1f3\xfc\xe7w\xe5J\x17nS/\x08\xf9n8\xea\xddc7\x88\xd9\x96\xc9\xfc\x96{\xa5\x8e\xd7\xc5E_1\xe7\x88\x08\x17\"\xa06r/\x91\x9d\xbb\xfal\x1eE\xd6\xc3\x18\xda\xc50\x95\xa9\xe4wa\xee\x8a\x0d\x95#b\xc9\xb6\\NDy\xdf\xceW\xee\x92\xba\"\x18\xbb\xc6\x04\xb4\xd4[E\xd7\x1b[r\x16\x9bZrf\xf5\x96\x9c+\x83%\xa7\xd2\xdc\xcd\xa6\x06\x9fK\x9dE\xb5\xac4)\xbf\xb0\xd2\x12\x0c?\n\xe7\xc1e\x86\xb6W=\xd1 \xb9mV\x1f\xf5Z\x04I\xaa#+j\x9akJ\xa2\xe2&a\x05\x84\xc0b<\xb3-\xd1\xa5\xe1RF=\xeb\xfc\x9c\x10t\x1b8\x95b\xcb!\x8c\x1e\xe5(h\xd5\xc5\xbc\xe70\x82\x99P\xc8\\U\xdeva\xe5\xb8RA^,\x1c\xa7S8\xd5\xc5[\xe7O\xe8\x1f\x16\xac\x0d=O\x11:\x821\xb3\xa5\x92i\x01\xe2\x91:\xca3V\x11\xf5B\x9f\x0c\x91\xd0o6K\xae\x1c\x0eL|J\x13\x15\x88\x88|\xcan\x0d7\xb9\x9f\xc8\x8d\xd4\x01{\x03\xaf\x91 \x97\x8df\x8fX\x8c\xadCg\xf7u\xe8\xe7\xf1|\xce\xcf7\x9c\x8a\xf9|\x88\xa2\xef\xa63\xc1i\x84^\xcd\xcd&\xa3\xa5G\x9bR,\x05\xfd\xfb-\xbb\x82X\xce8\x9dn\xf0\x9e\x8a6,\xb6(}[\x9d1\x10\x92w\xc4n\xbe\xd1\xc5\x8b\xc7\xd1\x94\x8a\xb0\x91\x03A\x11\x927\xd0\xcd+{J\xe5\xe4\x81\x88K%4\xfa\x1c\x05\xe3q\xc4]\xe40ie\xdcM\xd6x\xeb1r\xa1\xaf\xbb\xb7\x87\x96\xb4\xb8h6\xaem\x96kc\xc3:\xcf\xf8\xa6eg\n\xc4\xac\xf1~\xe2U\x1e\xd1\xa2v\xdd\x0dt\x82r\xe3\xa0\xbc\xa0\xe6\x15\xd1\xafc}\x1cx\\\xc5Pc#c\xb6!9\xd5\n\xbb\xebH\xd8\x89\x85\xc0\x13\x08\xe9r\x13\x07\xa21\xa1\x0f\xcb\x17\x1dI\xcd%8l4\xc0\xe0\x15\xec2+\xaf\xb7w\x82\x847\xa0/\xb3\xaa\xf9.\x8e\x0bC\x8e\xb6RnJ\x15\xb7\xc9\xaac\xa9\x9b\x80Mnl-\n\xe2\xb2\x08\x92\x86{F\x0d\xf7\x8a6\xb9\x89Un\xaf\"\xaf\xdc\xbf\xf5\x86\x9bVu\xad\xbb%\xdd\xd1\xfd\xfa\xb2\xd1\x8d\xaa\xbf\x14\xfc\xa4\x9fue\x16L\x98\xf7\x1d\xfd\xaf\xf7\xba@\xcch$\xb1\xab:O\xc6K\xe7vP\x85S\xc62\xb7#GGx\xe6\xb6\xec\x0b\xcd\xbc\x08o\xec\xaf\xde3]\x9c,\x1d\xd7_\xa1\x16\xaeb\xccU\x02\xad.3\xdbgq\x88\xf3C#\xadTn\x8c\x08\x9f%:\xa3\xdf\x81\xfb\n\xcc\xdc\xd5\xa9\xea\xd3_\xa3W\xd5\x88\xcd^\x9e\x9b\xb0\x12\x99\xb8h\xaf>p\x80D\xf7+i\xb05\xdeG\xd2\x0b\xe8,d\xa7\xe3\x10-\xcf\xf4o\x19%\x1c\x91\xf4\xce+\x19\xa5\xd5\xeb\xfb\xef\xdd\xedN5\xa8\xf6B}\xd7\x86iy\"~(\xce\x14\xcb\x8aC\xa5\xae\x8b ,\xc5]\xb9\xefQ\x88\xadS\xffX\xa3\x1d(%\x94\xbb\xe3\xa1.`\x9a\x8d\x94\x8a\x07\x0f\xd4\xed\x8d\xce\xd1B\xb3\xcc\x04S6\x92y\x1cUrq\xd5\x9d\xb6Y\xe8v\x14\xddq\x0d\xc7\xa8Gv\x99\x8ax\xea\xb8\xf0\xbd(Z\x12/\xb4Q\x94!E\xb8e,\xc0LA\xe8\x15\xfd\x10c\x96\xf4\xbcG\x07N7HI\xec\xa5\x91>\x90\xe3\xb1\xde}|O\xb9\xcd\xc5\xf6\xe8\xa0\xba\xa3=\xfd\xd6M\xf4\xead_\xbf\xff\xe7\xbc\xcdj\xe5\xcb*^mt\xacV\x0f\xcb\x8b\x878\x8cj\x9e\xcb\x87Q\xf5)\x1e\xe64\xf1\x17\xdf\x1bO\xf2\xe5\xa3\xfa\xb6\x9b\xa8\x10K\x8d\x1e\x94\x8d\xa6\xa4\x17\xb5\xa6$\x0c\xb2T(\xe6\x13\xa6\x98\xf7\xed3\xa4A\x9e}\xc6\x83#\x02\x8f\x16\x8eh\x8e\x0bG!\x11\x0b\xf6\xec\xe4q\xf2\xca\x95\x1bb1\xe0 \xe8\xcc$\xee\xa1S!\xde\xa0\xe1\xbb\x93y{\xda\x97P\xc4\xe9\xa7$\x85a\x11\xbf\xb9\xcdo\xeb\xd1\xf3\xb9}S\x928\xfa\x0e&+\x1bA\x8a\x17\xd1o\x0c\xd2\x10;\xd5\xd1V\x1b\xa4\xf0r\xed\xa5N\x95B\x8c\\R\xb1&t\xe0\x86\xf9\xf2\xa5Z\x07J\xf1\xe1#5$\x0cU\xa0*\xe4\x06\xb3\x05~\xc7\\\x08\xe7|\xa9\x98\x91A\xb5M\xd8\xef\xb0\xbb\xf1\xd48\x178\x0f\xe7\xe8\xe5\xfa\x8e_Ge~4\x94`\x8a\xf9\xa1\x07\xe4\x0b\x18\xc19\x06\x16\xb3\x8b\xc9i]tgQHN\x1c\xb4\xbf\x9f\xc1\xa9\x10\xe2\x983\xf0\x05\xd3\x98p7\xf6\xfc\x17\xe5\xdf\xf6\"\xd7\xa6\\\xbb0\xb3opg,\xf0\xae\x15\x9f\xe6\xebj\xa3\xed\xb6!a\x16]9Mv\xa0\xc2\xdbs^\x83\x0d8\x03\xf2\xda\xebF\x8f\xe3uQoW\xc1\x89k\x8e\x10\xbfz7\xa4\x82]#\x05\xbb*\xc7\x92\x1c\xa9\xb6\xc0\xa2\xd8vx0\xdb:\x9bt\xd5\xd8\x0c| f\x8c\x07\xd8\xb3\xa2\xfbn\x8d\xccW\x89\xb0\x1b3\n8\x1b\xa7,\xcb\x1f\xcb\x9e<=q\xa0\xdd\x8e\xb5\xd4\x0b\x8b\x8e\x80\x17\x9d\x8a\x9c\xab\xf6\x9a\xa9]\xac\xef~\x17\x03\xab\xb9\xe0u/\x13.:\xd5\x1fI\x0bo V\x13\xd3\xb5\x10\x17<&.\xe2\x93~\xf5\xb4Zry\x97\x83\xd8F\xb52/J\xa4J\xc4\x08}y\xfa\xf9\xf9\x8c\xb00\x94A\x14\x9e\x9f\x0f\xc1\xc3\xd0\xa2D\xe7\xccw\x1ez+R\x94\xb9\xb2\xab\x0e\xd0\xef\xcb\xea\x91\xb9\x1dT\x9b\x9cG1}\xbd\x1e\xcb\xf8\xa0\x17\xcc\x0e\x86\x7f\x86\xec\xcf\x08\x02;'\xe8\x8aR\xa4\xf4\xfb-\xb9\xf9x\x93\xc6\x0c\x8e\xe3\xb8\xf9\x08\x04!$(\xd3.\xcc:\xfc\xc5\x98L\x99\xa7s\xce\xc1Hm\xd7\x16^\xf2\x92c\x89\x98\xcb\x98YA\xa4'\xcc\x9f\xcf\x92 J\xaa\xf4 y\x8e\xaa\xaa\xb3\xb5H\xf6R\xa9N-\xc0kU\x1f\xa8\x95s6V\xad\x92\x83EE\xfc\xa7\xf2\xfa\x8a\x92\xc3\xca\xbb\x08\xe3/\xe2w\xe5-\x9e\x13\xa9\xf2\x9e\xc8\x9a\xc4\xde\xe4\xbf\x94w\x13\xe2\xc5J\x93\x0c\xc8\xdfd?\xd4\x17\xd7\xc4\x0fHR}\x93A\xc5\xab\xec\x97\xe6\xdde\x90*o.\x834\x7fo\x19\xa4\xca[\x92\x08PyWz\xc2k\x90 \x9azrAA\xa9'\x7f\x92\xd7\x93C\x94z\xb20\xf1\xa35E\x83\xea,HOx=\x12\xa4\xe4E\x82$F\xa2J\xd5\x9d/\x119\xdaFU{.\xba'\xda\xaf\xb5 \xcb\xba_A\x95*;\xae\xd2\xb1\xc0\xdc1\xb9\xe5MZ\x15\xe4\xdb\xc6\xec\xedL\xef\xd1\xad\x90Qh\x83\xe5(\x0e\xa1\xa5\xdfx\xa4x=\xdf\xb4\xd5\xa4\x92M\x0b\xd4Q.\xcb\xa3\x0cddr\x9b\xa6U\\>\xe1\xed\xe8\xb5\xa3\\\xee\xae\xe4\x86\xc7\xe0\x189\xc6\xd9r\xa7\xf4\xbd\xca\x11\x11{\xe5[\xae\x98S\x8b\xbd\x105\xbf\x10\x94\xe2\xf0\x97\x04f}\x15\xe5\x99\xd0UQH\xe5\xf7\x89\xa5%\xe9g\x8f{[G1b!\xcfP\xdf\xa0\x93\x1cR\x8c\xea\x9f\xcb\x0d\xfac\x90\xd8\x1c\xc52\xdc}4\x9b\xf5:?\n\xb1\xab>Z4\xb9\xbd\xa5\xcf\xe54\x05\xac\xecY^\x16#\x98V\xb3\x18\x9e\xf2\x8b{\xb4\x1d~'\x8ecj\x87\x87\xfe\xb0\xa3b\xd1=\\\xf4\x80\xa2=\xf3\x93\xc5X&\xe3\x1e\xf7q\xc7\x07\xf4E\x17\xbcq\x9f\x03\xbf\xc5\xae\xe7}\xefO\xc7\x11\xe2xvr\xaf~;\xae\xa8\x8c-\xe0\x1d\xf0\x97k8\xb5\x99\x16\xd5\xa1n\x17\x1b\x83\x07\x8f\xa9\xc1\xe4\xac\x1e\x93=\xee^^\x8f\xebyn>c)\x1f\xd9\xc1\x06{\x81\x0b[\x19\xc5.\xf3f\xa0\xaf`\x1a\xc0q\xb2 =\x8d$,\xdd\x9c\x9eJ\xd2\x7f\x86\xe8\xe0\x8d#\x89\x9e\xd6\x93R\x9f!J\xc6\xe24\xb1\xbe\xf6\xa7\xe3\x00\x91.\xba\x03a}\x90\x9e\xe5\x17q\xf3\xce\xd0\xf7\x85\xdf~\xe0\"B\xd3g%\xd0 \xb4\xb0\x18\xb7\x7f?z\x04\xbe n\x0e2\\\xbf\xbb\x8e\xd6\xb6\xe3\xb2E\xe1\xbf\x9c\x0dj\xdeb\xbbH\xd7\x016\xd9'\x9b\x86_\xe1r\x8a,\x97\xa8\xd5\x7fG\xff\xeb\x1eRY\xc5\xf0\x7f\xcco'\xb2\x90\xb4]\x0ci\xc7\x83:\xdf\xe7B\xe2VB\x9c\xdc\xf66G9\xb4w\xa7\xf6W\xef\x91P\xa6\xf6+\xef\x15\xbb\x83\x98\x16I\x1e\xe0\xe1fk\x03\xa9\xbf5z\x18=XYt\xbe\xe3\xb4n)\x1bW\x89\xe4C\x88\xc5\x12\xb9 .:\xc2\x19\xbc\xe0\xca\xc2[PHi\xe18\xd8h\xd7\x95\x85\xac\xa6\xe0\xa1,_6K\xac\xe3B\xc8~\xb5\xdb\xa9\xf3\xed\xf0BIc\x85\xf9\xa3\x90\xf1\xb7p\xa0\xec\x0c_&Va\xe9\xb7\x86*<\x0c\xd1\xd1\xc8+\xdf\x02\xbdy\xc8S\xa0^\xc9\xa0G\xf5\xd0(\x8a\x9a\xe48\xcd|hJF\xf7\n\xc7\x15\xcd\xe09\x82\xb8\x10\xa1\x7f\x01ECM\xd8\xe4\x0dh\xe1F\x18\xce\x8e\xb9L\xcag\x83\xa5d\xc9G5\x00\xe1\xc7\xbb;\xe3<;C\xf9x\x86j\x16M\x136#\x9e\xcb\xf3~\xf3S\x1aC\xfel\x0b\xe4\xe7\xbdi\xd5\xf6\xa6\xe1\xc8@\xe4\xe6=U\x90\xf54\"\xb2W\x16\x91\x93\xb2\x88\x9c\xe4\"\xb2W\xfc\xd2\x88\xc8j\xcd\xc6\x9er\x89\x98\xae\xd4\x86\xd3s\x0f\x96e&\xe4p\xc7\xed\xe5\xcaD\\\xed\xeaw\xf4\xbf\x1e\x86\x07j\xef;\x85v\xff\xb8\n\x8f8\xfcH\x7f\xbfM $..\xcfT\xef\xe0$\xa6\x8bo\xe5b\xdb\x05\x0870mL\x15\xc1\x93\x184\\x\xe7J\xd3\xa5\x0bk\x17\xfd+\xe7\xdcAQ\xa5/u\x0f\xaf\xd0\xba!\xc2\xce\xa9\xcfo\xf0\xb9\x08\xc1X\xc6\xe8\xe2=\xf4\x08\xaf\x97\xe5\x84\xa4QD\x17\xd6\xe2V\x8c\x91\xa1DJ\x07\xbcVj\xd4\xd4\xebC\xad\x80\x88\xd7\x1737\xbb$\x17\x9f{.t\xfa\x945\\\xf1\xcb'\xcb<&\xc2\x9a6\xab\xda\x9c6rX\x8eli\x02\xe1\xaa\xc6o\xf9}e\xfa\xa2P\x04\xe9m\x9e\xbb\xda\xdb\xed\xda\xfb\x93\x90\xbb\xbbI\x11\n\xb4s&;\xee\x8d`\xbc\xc0\x88\x15\xa1p\xe2c\xd4=t\x98\x0d\x0e\xa7V#\xbd\x89O\xcc\x18\x12\xdd\x95KF'\xd6LZ^b\x96|\xe1\x92\xdf\xe0D#>(\x7f\x98\xe9\xa8.R\xec\x8c'4@~=c\xc17\x8a\x80\xc8\xb8\xb7X4\xd8\x88\xf1+\x1e\xcb8\xc6T\nQ\x98\x92\xeb\x14\xf30\xc5\x97\x89\x93\xfbo\xc6,yD\xc00%*P\x88\xae\x89)Et#id\x99\xbe\xf9\xdej\x8a\xc2q\xc5\xeeEr\x9fp\xe3\xa6\x08\xe9\xd0\xd3rV-\x1e\xfeCT\x0f\xa9\x19a\x84\xfc\xccD\x8a\xb4\x1b\xcc\xcc\x9a?\x1e \x13jS\xf9\xd3\x82\x9c\xdd\xd1\xdaXO\x16\xe3\xa4\x08\xda\xcb~\x04\x85MF\xe9>\xbf3\x86X\xa1\xf4\x8a\xffX\xe2\x8f\x9cq\xc5\xdb\xf5e\x81\x0eZZ\x94\xc6\x1b 6-\xc0\x88\x8e\xc3\xa9\x0es*^8\x90u\xe9\xcf\x0dD\xa1\xc4\x9esa\x85\x8b\x14Z \xa5qJ\x12{\xad\xe3\x0fj\xefs\x1a\xc2\xa8\xa2\xe8\xaf\xf9x\xa6\xbd`\x9b\xe1M\xfb\x0d6\xc5g$\x8d\x03rE\n\x8a3\x8b\x08#D\xc1j\xbd$T(\x12h(\x90\xf8\xb1\x96*\x89\x0fk\xda\x9e\xbb\xa0\x1bqe|9\xb5\xff\xafq\x9c\xe5\xcdj\x1aoM\xdf\xf8\xfb\x0f\xd6\xbd\xbc?\xdb\xf5P\xac\x08\xe6n\xe0oh\xd1\xb1\x04)\x04\xaf\xaa\x8a\x81\x85\xca3q\x1a\x93\x8a\x01\xf9`\xbb\xad\x0f\xeaW\xe3\xe7D\x19\xc0R\xfb\x12\x88\x03\xfe\xa64I\x7f\x8e\xc7\xc1\xe8\xe9\x8e\xbeM\xcf\x8e\x1c\x93\x8c\x1f\xe1\\cVF\x9ct\x84x\xb3\x03I\x1elH\xf2\x7f\xd5\xefa\xe9\"\x1asj*\xee\x84y\xccO\xb1\xd5\xe9x\xe2\xe4R:\xac\xb4z\x98\x9fP{]L\xc3\xbf.I\xfa\x19G\xd0\x1f\xd38z\xc5 <\x16LV\xb3\xfd\xef\xa7\xd4\x92\xd2\x0f\xe96X\xe8B%DsXD\xecm\xf1\x88\xbd\x04\x86\"\xa5b#s@\xaf\xb2\xee\xf3\xb33\xba\x1c\xf8\xa5K\x12\xdf[\x17\xfaT\x19\xa8N\x95`,\xcd,H\xc4dP2z\x19\xbc\xd8\xfef\xd1\xec\xdf\x84\x98\xfcl\x16\xc4$\x01\xaf\x08}g\xf4X*\xc5\xbb\x96\x82L\xf1\x10La\x9ea\x81\x12\xcfN\x9f\x1d\x83)ya\xa2t)[\xc2 \xb4\xdb\x01<\x81\xf8\xc4\xc1\x19\xe6\xf9{\xe4B\x01\xde{\x8c\xa0Mg\xff\xe9\x08\xfa(\x05S\x01d\xb7\x8ftgp\x08\"\x03!N@\xc0\n<\x1d\xc1\xdeQ^v\xff\x10\xcb\xd6=\x7f\xf4\x08\xf6\xf6i\x81\x8c\x12\xc6\xc9\x04\x83F\x15\x96\x89\xfe\x01Zr\x80\x12K\x1b\xfb\x1a\xb0*[\xfdJ\xd8\x01\x82uup\xc4\x1f\x88\x0e\x1e\x17_\xf5=D\xe8\xc1~\x0e=\xee\xe5\xd0\xe3\xc3\x1c\xda\x1f\x0c\xf02(\xce\x13\xce\x11\xa5\xe0\xac\xcbe \xce\x9b\xf5\xff\xfe\xc5\x9fY\xb5\xfbPuz\xd78Q\xc8\x18\x8b\x1a\x18\xf6\x0dO\xdan \x91Y\x8a\xcfJt\xe5r\xec\xeeX\xd6\x1b\xbew\xf2\xdb:\xa1\xdd\xef\xdf'\xb0\xa76p=\xad\xd8:?'\xc9\xa7\xd1,[\x12\xabJ\xb5y\x9a 9\x8d\x82\xc3T=\x98K\xaf\xceQ\xc5x}9I\xbd\x94|\x7f\x99]\x06a24l\xdadM|\xd33\xfa\xf1\xb0\xcdd\x08\x99Y\xc8O\xc8\x92\xf8i\x14'C0\x04c\xd2\xbf\xcbR/\x19\xbb\x068\xb6Y\xe6\x13Zs\"\xa6\xc2\xdc\x8f\xbc\xaf\xd1F}\xf5\xf4}U\xf1\xf0;\xfa_\xefU\xf9mn\x87\xf6~\xffX\x89\x90\xcd\xed\x0c:\xbb\x84o\xd3'{J\xa0e\xfeh\x7f\xaf_}\xe4\xe5\x8f\x06J\x90i\xd1\x87\xbd]\xc79\xf9N\xfeL\xe0\x0e\xf8z\xc5O\xca\x98C\x81\x9f\x05s8\xa9\xa0)\xe3\x06_U6\xa7|+G\xa3\x10\x93b\xe6\x05!=\xb65\x1c\xac\x0bC\x1d\xa7eEF$\x93\x19\xbc\xd8(i\xd9\x8fC\x9d\x84\xb9\xd1\xbdB\x99\x07\x1e\xb4X'a\xb1\x1c\x97\xd5 \x93\xdfQ\xbf\xd1q/\x95[B\x97$\xfd$\xf2\xbd\xe5s\xdc\x04\x9b\xc5\xfa\xb3{\x18\x8c\xd8\x8b\x13\xf2\xd3\xde\x8a\xbf\xea\xd8\xb1\x18\xfcv^\x0erC2]|\xdc\xe9t&a\x16/\x87`-\xd2t\x9d\x0cwv\xd6$M\xd2(&\xdd\xe4\x9dwyI\xe2n\x10\xed\\\x0dv\xc4\xaf/\x92(\xb4&\xe1,Z\x9d\x07\xb3!X\x7f\x85?\xe8d\x815 \xd11\xddK\xa3\xf8\x07\xa5:\xa3p\x19\x84\xe5\x1aEAk\x12F^\x96.\x06\x9f\x91Y\x10\x13?-\xde\x1c\xee\xec,\xe9\xbc-\xa2$\x1d\xee\x0ez\xbd\x1dV\xb2\x13\xf3\xa2\xddE\xbaZZ\x93\xf0\xb1v\xd0\x1bQp\xc9\xb5c\xd07hR\xe3\x87\xa9^\x7f\xdc\xdb\xdf\xebi\xb7od\xc4\xdcZ\xf4Q\xbcH\x85\xb5\x120\xfe\xa6\x88\x15=#\xeb\x98\xf8^Jf\xe0\x853\xc9\x91&K\xc8\xac\xdb\xe0C\x03\xf2\xfct\xa9\x98\x87#\xe9\xc9IK\xbbg\xfe\x82\xac\x98uu\xf7\xa8\xf4\xe4\xe3g/?9{\xf6\xf1\x8b\xf3\xb3\xe7\x7f\xed\xc5\xa7\xcf\xb8\xc1vP*\xf3\x93g\xaf_\xc9\xcf\x07\xbd\xdd\xd2\xf3\xe7\xaf?{Q~^~\xff\xa3\x17\x1f?\xfb\xc1'o\xce\xab\xed\xec\xefj\x8b}\xfc\x83O>\x91\x8b\x1d\x95\x8b-#o\x86\xa1\x02\xe8\x97\xea\x83g\xf4P\xc1\x9f=c\x17\xce\xc4\xe3\xc4\x9b\x93O\xc4\xbb\xe2\x87\xae\x80\xa8C\xfa-\x17\x9be\xab5\xc6\x0c\xa4_\xaa\xef\x7f$\x1e\x8a\x1fr\x81\x9f~\xf6\xe9'/\xae}\x82!\xe89\x1e\x96\x86\xf6\xe9\xcbW/?}\xf6I\xddZl8\x87\xe6\xe9K|/D\xd5\x81E\xbfY\xa5gH\xe1\xd8C\xfcZ~\xeaG+\xee{\x12\xd9\x16\xffQ.\xe1\xcdf\xcf\xa5\xf0\xe1X\xb0\x0c\xb3\xee!\xdfI\xfe}\xd5\xab\xfcA>\x9b%0\xbfD\xa5h\xa0\xb3|\xeaJ`/\x9f\xaf\x128iVH\x97_\xf0U\x85\xf2\x1cF0(\x83(\x92\xed\x96A\x14u\xf6\xca\xa0\x85Z\xd7L\xad\xebJ\xad\xeb\x86\xb9\xc2]\xf7z\x9d\xc9u\xefhr\xdd\xfb\xde\xe4\xba\xf7|r\xdd{\xd1\x99\\\xf7?\x9e\\\x1f~\xdc\x99\\\x1f\xedM\xae\x8f\x0e:\x93\xeb\xe3\x8f'\xd9\xc7\x1f\x7f\xfc\x02\xff\xffxz;\x9ed\x1f\x1d\xd1\x97\xb3\x8f\xbe\xf7\xf1\xc7S\xfb\xb4E!\xcf\x19\x84\x96pn\xed\xd3\xe1\xf8\xf3r\xb1\xdb\xcf\x9dJ\xb1\x9dr\xb7.y\xb7\x8e\xf6\xcb\x1ez\xe5R+,\xe5N\xc6\x93\xe9\xe4\xab\xc9\xfb\xea\xe3s\xfa\xf8s\xfbt\xd8\xbam\xb5n[c\xaf\xf3\xe5\xa43m\xb7\x9c\x0fv\x82r\xc9\x8b\xa2\xe4\xf8\xf3\xa2>\xc7>\x1d\xfe\xc4\xb8\xd79\xf6:\xf3\xe9W\x83\xf7\xb7\xec\xfb\x97\x93\xce_9\x99\xecLN\x87\xdf}4\x9a\xb4'\x1f\xb8\xe7\x93n\xeb\x7f\x98|\xf8xbO\x1c\xfa\xf6\xd4\xf9\xf0\x83\x9d@\xc7\"\xde\x19YD\x9f_B\xc33\xe3.\xfb.\x11q\xb5\xaakcU\xc7EM\xbb\x83\x0dj:\xdb\xa6&\xec\xdf\xb6}}alao\xaf\xa8\xea\xb8/}\xdf\x95\x9a\x18\x94~\xeco\xd0\xe03\x83yG+\x9e\xee\x1d\xa1\xb9\x02\xa5K~\xd2>\xc5 9{G0\xa4\xc7\xea'\\\xef\xb0;\x80[`\xc9\x9c\xd91\xbb7@}O\x87\x16j\xd3i\x19B\xa7_\xdb\xb1\xd7\xe6\x998\xca\x15]\xd6\xa4g\xb1\x96s\xc8\x7f\x87\x00\xb9\xc8\x05\x85\xf4\xfb\x07\x12(\xc5BU@?_.\n\n\x19H\xae\xe9\nA\xbd\x81\x04\x9a\xb3R{\x12(f\xa5\xfa\x05\xe8\xbf\xa7\x90]\xe95\xd4}\xec\x16/=\xb6\x1e\xc3\x10\xf6\xa4a\xec`\x0f\xe5\x96&\x14r(u\xe7\xff\xf9y,\xb3/A~\x13\xcb\xc8#E\xaa@\xa1G\xbd\n\xf4\x98)\xabk\x17\xe1\x8b\x9a#\xc6\x93\x11\x1c\xec\xef\xef\xee\xc3)W\\a\x96\xe9\xe7\\\xdfd\xa7\x85\x03j\xf9\x01K\xe9\xd9\xa6\xa7\xb5\x0e\xd6p\x00O\x9fB\x9fJX\xfb\x07\xbb\x83^\xf9\xd1#:\xdf\xbb\x8a\x11\x15\xe4\xd3\xd8[\x90\x13\xd3\x0e\xf6\x0f\x1c\x17^j`\x9f\xb2\x84r\x9f\xc2\x13\x18\xec\x1f\x9c\xc0\xa7\xed\xb6\x03o\xc7\x9f\xd23\xd9k\xfbS\x87\xc7\x19\xe8\xb9\xf0\xb2\x00\xea\x88\xd3\x1b\xad\x1e_hb\xc9;\x08P\x01C\xdeQI\xb7;\x0f\x96$\xf4V\x84\xb2\xf6 \\g)\xde\xdb\x8f\x92 \xc5;\x96i\x97\x9e\x1fd\x18t8\xf0,\xf5\xe2\xb2\x9b\xbc\xda\x97\xe7\xda\xbe0Q\x99\xf7\xb3\xf6\xfd\xef\xeb\xdf\xefF\xe1\x0f\xbd8\x0c\xc2Kv\x96\xcc\x7f\xf2\xeb\xea\xe8y\xca\xeb\xd7-\x0e]\x97\xcf\x94\xd3\"\x15\xd9\x86\x8d\x16\x1a\xf1\xbe1d\x0b?\xa2\x8f \xed^\x918\xa1\xc3x\xf4\x88\xcd\x845\xcb\xd6\xcb\xc0\xf7R~3\xf5'h\x93\xc0\x8eT\x98Q\xca\xe5\x91\x0fC)`\x15{\xb3\\\x12<\x9f\x8a\x96 \x90k\xcfO\xf1b*\xc9U\xba\xb4\x9a\\\xe3n\xc7\x8c+R\xa67m;\x93\xae\xf8\xf6\xc1N\x97\\\x13\xdf\x0e\xc7=\x1e\x03\x8d5\x14,\x97\x9dy\x14\xafdw\xffh\x0e\xe9\x82\x80\xda[*\x8b\xa1\xf4\xf82L\xedx\xdc\x9f\xbal\xafDe\xf8@\xc0\xa5\xb8\x8e\xac\xb5,d#\xc1lhX\xbf\x983\xde\xe6,\xf2\xf3A\x15\x13:\x82\x90E-\xef\xfa\x0b\xe2\xbf\xfd$\x08\xc9\xf7b\xe2\xbd\xa5\xe2[Dw\x90h\n\xef\xdc\x0e\x8a\xaf\xdf\xe7\xad&\xd9\x9a\x8a\xb1d\xd6\xd0hiu+*\xb67\xcf\xfe\xeav\xe8\xa2\xe2\xca\xc0\xb0\xdao\x9e\xfd\xd5\x9a\xc5N\xdfE\x85\xfe\xdf\x12\ny\x16\xd1\x0e\xbf\xd1u8\xef\xa6$I\xed\x18\x03@(K\x9bz\x97\xb0\xf0\xc2\xd9\x92\x80=\x0f\xe2$\xcd+t\xc4$\x94\xfa@[\xc9C*\xa4\xde\xe5\xa7\xde\xda\x85\xb8@\x9b\xc7\xe9\x82\xc4\x84\x1ep=X\xc7\xe4*\x88\xb2dy\x033\xe2/\xbd\x98\xcc \xc9\xe6\xf3\xe0\x1a\xa9\xa2\xf5\x18\xda\x10C\x1b\x1e[R7\x1e;.\\\xb0.\x07\xe6.\xafcB\xab\xb1\x13\xe2G\xe1l\x83>\x8b\xce2\xbf\x87r\xe0\xfc\x92\x96Q\xa5=\xaf\xc4\x92\xe2@U)\xa4\xc8\xdf\xaa\xaa\xe9\x08<\xd1\xa3\x02\xbac\xb0\xd8;\x94\xd8\xf2+\x1e\x888\xb4\x19\xa5<\x08V\x120sz$E\xf5f\xf9\x08\"\xfa\xa7=\x82\xbe\xc3e\x06t\x0e\xf0\xaa\xb6\x15&\xfb=\x19AF\xd7,C\xb9\xa7\xdf\xdf\xeb\xf7\xfb\xc5d\x93\xeb5\xbb\x83\xcf\xa2\x1c\xfc\xe4\xd9\xebW@\xab\xf1\xfc\x94(\xb90A\xdc4\xbca\xab\xe6I4\x84.E\x92\xc6\xc4[\xa1\xc3\x81\x17\x84 \x84Q\xd8Y\xc7A\xc8\xb6z^m\xa2\xab7\xed\xc6$\xc9\x96\x98/\xd53\xad\x99f\xc9>)\x96Lqo\xb9\xe2 \x04\xd0-\xac\xe2,\x833\x1cw\x83\x84\xa7\xdb\x0f%\x0c\xe4\x1a\x9a\x15\x89/ \xac\xbc\xf5:\x08/\x93\x13\xc4\xb6u\x1c]\x053\x8a\xddQ\x16\xfb\x84\xe7o\xa6\x9b@&k\x96\x93\x87\xd8\xa4\x87E[\xf2*xKn\x12;t\x9c|A=x\x02>\xfd\xc3\x164\xc3\x80\x8f\xde\xd4\x95\xe2\x9ce\xd87\x9b\xb0\x90\x94!\xfa\xdb\x04\xecG\xabW\xcfM?\x920Z\xce?\xac\x9b*\xdf\x85\xb9\x8a\xd7Aa\x08\x0cd.\xc3S\xf2\x08#\x91\x95z\x97\xc3\x1bo\xb5\xecF\xf1\xa5;\xe8\xf5\x06C\x9c?\xe6q\xabAsZ7\xbb\xeb\x18$L(2E>\xc0\xa5\xe2\xae0\xf4\xa0\x1d\xe5s\xe7\xc3\x13\x98\xd3?l\xee\x04.Dc\x1fS\x90\x1b\xb07/\xa6\x96\xc1\xe7)\xea]\xe9\x94'y\x8cb\x9e\xde\xa9X\x13\x06\xb0\x99\\\x04t\x8f\xdd\xde\xeaD\xa7\x11x\xecI!`\x95\xe5\x022\x13(\x06o\xc9\x0d&\xe0#\xe3`\xcaB$\xe5\x97~\x83\xe6D>\xea\xe2\x7f\xb9\xd1Y\x8a\x1f2p)\x05\x8d\x92(I\xd1s\x87\xdd\xe8\x12?\xdbmz\xac\xd8\xe5\xc8p\n\xb6\xfc\xc8\xcd\x8f\x9a\xb552Y\xaex\x8d\xca\xe8lz<\xc0\x89\xbd\xa0,\x9en/A\xa8\x18\x85\xc7gmt3\x92$S\x1c\x80\xa8\xacvf>6\xf1\xee\\\x86\x97s\x0e\xd5\x0e\xe1\x84;\x10\x04\xda\xb8\xac\xdc+\xeb\xda\x0e\x1c\x1e}TS[\xbb-\xd7\xa7\xdd)\xb8\xdbv\xd9\xd1\xca\xe0!7\x8bj\x0c~\x9b\xb4\xac}\xf9=\xbc[\x04Td\xe8\xf7\nA\xae\xbf[|\xe7`C\xbf[\xef\x90\x15\xe12\xaa%pv\xbeD\x07\x83\xe6\x89v!\xa6x\xc5\xd6\xfbe8\xa3R*\x9e\x9f\xf8A\x96.\x80\xfc\x90\x16\xdez\xd8\xefu\xbb\x8c\x87\xb0\x0d\x8b\xe1\xc6\x0cq\xa5\x9e\xcd\x0c\x99\x06\x8f{\xc16\x08\xe3\xbe?\xc5\x89\xfb\xd2\x85V\x1f\xbd\xe3\\\xd1\x94@\x0e\xa7\xdc\xbfM\x1aw\x0bf\x8f\xb4 g\xf7|HO\xb9\x83\x10\x9f`\x87\xf3\xb1\x0bo&\x13\x01zj\xf1 !?\x9b\x91\xd0'@\xc24\xbe1\x8a\xd9\xcc\xc7\xacDd\x88\x96\x96\n\x12\xd0\xf28\x8e\xd0\x83\x13Kd$p\x07\xc5\x89\xb4\xfb6\x08g0\x02K\xf4\xc0r\x8b\xcd\x841\xc6\x9a\x04\xca\x9f6\xd3\xa8\\\xc4D\x8c\xd6\xef\x80*\xa6\xd3!\xee\xee\x16\x11\xc2\x1b\x04\x90\xdc\x7fBW\x8f\xb4a\xe8\xf8M\x1a\x18\x8f\x1f+\x99i\x87R\xe5\x03.\x01m\xc2-0\x12m\xc41~\xb3\x17\x86\xb0\xcb\xa4\xa4@D\xb1\xc58\\t\x19Z-k\xf3Z\xd8\x1b\x16\x0b6 \x0b\x94\x91N\xf20\x8a\x03\x9b4\xa7\xbc\x98\x8b\x01\x92\x14p00\xb2~\x89r<\xc9\xb3\xf8\xd1\xd1\xc7\xba\x83pi\x97m\xd2\xbdBL\xcc\xc2\xfc\x04K\xc2\x99\xd0 \xf0\x83\xe8\xbb ]\x04!xpE\xe2\x0b/\x0dVt\xe5\xab\n\x1eS\xa8#.\xb9I\xe3m\x9d1)._M\x96D\xe0T\x9c\x80\xbdK\xa1\xf3\xe0\x07H~\x10\x06r\xed/\xbd\x15C\xc0\x95\x17\xbfM\xac<\x0eqe.X\x16\x85\n\xdd\xcd\x15;\xf2\x195\xf4*:\x9dJ\x9bI\xe6/JGn\xe6\xa5I1\xaf\x8c>\x8c\xb4o6\xef\xeaB7\xaf\xe7*WJ\x15\xba\x02\xe3L\xcd\x97\xd1;J.\xe9v\x8d\xe2R\xff\xcb\xab\xa6#\x7f\xc8\xc8Z\x17\xfa\xf60\x99u\xfd\x1c\x0d\xd1m#F]\xe6)\x08\"\x1a\xc3PU\x83\x85\x8eT\"W8\x85STs\x0d\xe9.\xe5\\\xa2(Ea\xe2\xa9\xee\xb1z~\x16\xe5\x99\xb6-\x0bs\xcd\x9a\xb4\xea\xa8Y\x0bQ\xb3\xf6\x18=\xc1k\x89\xf7\x0f\xcd\xc4[C\x96\x8f\x18Y\x0e\xefA\x96\xcd\x82\x8c\x9e4\x87\xc0K\xc8\xe4\xd9\xd0\x81\x12fV\xb1Zl\xdc\x90o\\v\xd4l\xbd\xb0C\x07\x93\xc76\xd7\xa8\xe5\xb0\xd2\xb6\xc9u \xc5~,\x0f!\x8cf\x04VYR\xe0\x9b\x97\xc2\x92xI\x8a\xaa{I\xcbVb\xd3\xf5\xbb\xa9a\x81\x7fJ\xd2\x86i\xf8\xc2U~I\xf2\xc6\x85K\x17V.\x9c\xbbp\xe1\xc2kf\x8c\xd20\xed7\x06f\xfe}\x033\x97\x16{\x19$) I~Vb\xbfl+Zc\xd4\xd9T\xe8j\xa1\x88\x1e\x9d\xcf\x82\x00pyE\xfc\xcc%\x15\x06@\xb5'\x8c\xd0\x19b]\xc8eLA\x85A\xeb\x1f=R\x04Q\xfbM.\xaf\x96\xc578e\x93\x00\xc3\xca!\x93\x9f:\xd0\\W}\xf8\x84+\xc2>E\x97x\x07\x0d\x1e\xf4\x85O\x0d\xde\x9a'L\x82\xba\xbd\xc5\xcdx\xe2\x94\xbbwZ\xf4\xee\x86\xc9c\xdfJ'a\x88\xd5\xeb\xd6\x8f\x07j\x80\x11\xbc\xa1\x9d\x8cr\x0b\xce\xa7\xf4\xc1\x9ao*z\xea\xbb\x80\x11\xf8\xc5\xa4\xcfs\x92F\xf0<\xd6\xa6\x9c\xecu\x99\xd5\x94\xec\x88\xf9L\xc1)\xbf:\x8eg\xaf\xd789\xdb\xd8X\xdcB\xc9\x9b\x98Og\xc0=w\xcc'4\xe0^;_\xd5\x8475=\xcb\x91T\xfb\xf4\xaa\xf6\xe9M\xed\xd3K\xc3\x06\x04\xeeG\xa3\x0b\"|\x87\xf3\xe3\x92\xab\xac7;?z\xc6$D\x18\x84\xa8\xa9\x1e.\xd6D\xd2\xa1-\xab\xc8\xb4\x07\xecP\x80\x07\x9a\xfd#\xfe\xfd\xf6\x96\xd2\xf2\xb8\xf9\n%\xd2\xc1\xd0\xc5[\xaf\xec\x08h\xd4A\xc9\xefI\x07<\xadL-\x7fX\xaa\xdf\xa6\x91:'pm{t\x9f\x1b\x8a6\xc8W\xf2\x87\xf6p\x9f\xf9[x\x0e\x9c\x99\x1a\xafH\xca\xb9\xc4\xe8Q\x11\xfe\xffc\xee[\xbb\xdb\xb6\x95E\xbf\xf7W\x8cx{\x1c2\x92\x15I~$Qlk\xa5i\xd2z7ur\x9a\xa4\xfbt\xcbj\x16-A6\x1b\x89T\xf9\x88\xed\xbd\xdd\xf3\xed\xfe\xb1\xfb\xcb\xee\xc2\x0c\x00\x82$@\xd2N\xd2\xd6k\xb5\xa1@\x10\xcf\xc1`\xde\x93\xb2d\xe3\xcf\xb5\xdbG\x97\xad\x82\xbf\xe4%\x9c\x82\xfe\xc0\xae\xb7\xd1w\x02\x12\xb6\xf1c\xa4\xc6\x149}\xb6\x8a\xe6\x1f\xa4\xd4\x9a__\xc8l\xb9\xa8kX\xf5\xf2\xa88Z\xc4\x9b\x8f\x02K\x8b\xa2\xb5@r\x02\xb8\x91\xf8\xe4\xff.\xd4\xf9\xc5/$\xc2\xaf_\x97\x86\x9c\xcc\xf2\x0f\x01c\xad\xb9g\xd1\xd5\x93\x14\xee\x9d9\x07\x96\xfa\xee\xf8\x9f\xd2\x13aD\xd8\x98\xf9\x0b~\xf1\x07kN\xcd\x04\xa9\x12\xe8o\xfc ~\x02>\xcc\xa3U\x14\xf2\x95^\x07IR \x9bW\xfe3\xbbKC\x1d\xb3\xa2\xff}\xaey\x9a\xe6X\xdcz\x12_\xf0 \xae\xb3U\x1a\xe0\xd9\xf9\xc0\xaea\xed_\x830q\xd6W\x05\xd5\x1b\xf6\xb9\x19\xdf\x88\x19\xef\x13\xcb\xe5\xf3\x0b\xf2\xd3\x80Mp\xed\xe42yN\xedi08\xc8Y\xcb \x9cG\xeb\x0d\xea_\xd8\x95ec\xf9l\x91\xceS{\xfb\x04\xa2\x18\x96\xd1j\x15]\xb2\x05\x9c]\x83\x8fj\xd0\xd4?\xcbV\xa8\xeca\xebMz\x8d\xca\x0d\"\xfcr\x9c\xa8\xbc\xa6c\xf3\xc6P(\x11\x0dEYeP\xae\xa4\x037DZ\x04T\xca\xa7\xab\x1f+A\x06hB\xb1s\xbc\xd9+k{-b\xd9\x1b\x97\xb7(Hk\xc6\x88\x9e\x81\xa8Qr3\xbfVnV\x80;\x9b\x17c\x93\xe8\xac\xf2Q\x15\xf2\xc4\xd1AH\xb3\x01\xda\xba j\xab\x9c\xae\\\xd4&\xf1d\x81~\xc5\x16\n\xfd\xfe\x81\xc4O\x0f\xce\xbc*\x01d\xa3~\xcaZ]\xccY\xb3\xd4\x93\x88u,\xf9\xc6\x17\xf5\x84\xd2\xc7FB\xe9\xda\xe0\xad\x04\x02H\x859\xa8\xbbi\x86\x05\xd2\x89=\xde\xe9 98IbM\xe9\xc9k0\x1f\xefs8\"\x82ac\xe5EUmN>\x8f\xf6D\x8f\x03\xea\xf1?M\xfeip7\xb2*\xf6(\xc3T\xd3=- \xabM-a\xa5\x8e\x1a\xf3z\xad\x96W\xe8\x0b\xab\xec+i\xd2\x08v\x17\x05\xd8\xfd\xa8\xc1.\xc7\xb7\n~al\x13\x1b\xc7\xf6\xcb\xe4\"\xa7?\x08?\xc2>9\xc5\x9f\x04\xe1\xf9\x8a\xc1\xefY\xc4\xab\x8a\xbdGZ\xa2n\x96\x86\x83t\x1b6\xc3\xdc\xe9\xe78):\x83a95\xbb\x04\x1e-\xc4t\x9f\xff\xd4`\xe2m\xf3\xa9i1\x9eZ\xc9\x88\xf0]\xf5\xd5\xa0\x8d\x18m\xe0\x95\x87d\x03|\x14c\x8dd\x9b-\xce\xa2\xa9\xab\xcbv*\x1aO\x87~\xfb9TrM\x9f\xfcE9\xd0\x7f\x98\xfa3\xafp\xc1\x1c\xa3\xef\x88>\xc9\x16-Rp\xd1\x910\x83\xe3\x1c\x8b\xcf\xcf\xd2\x08]\x89\x1f*Vf\x17\xc6\xf0hO\xfd\xe4l\xc3\xc0\x83#\xfe\xbf\x16\xba\xb2\x80\x14\xda\x11\x19m\x07\xfc\xbb'\x10lo{\xd8\xfb\xd3\xb6k\xc5\x99\x14\x0c\x1b\x87~5\x07\x07\xb0\xebA\x172\xc5R\xa9\x13x\xc1\xae\xfc\x05\x9b\x07k\x7fU\xef\xd2\xa4\xff\xe9K\xf9\x9b\x1b\x95\xe0\xc5N\xb7\xd0ZJ,\xf0!\x8c.C\x10\x11\xd3\x94\xcc\xac\xa6\xeb\xea\xc9\xa8\xc7\xa4~\x8eI\xe9\xe8\xdb0i\xb5\xe1/\x84I\x17Qv\xd6\x06\x93\x96\x06\xd3\x82\x96\xb8\x0dj5\x8f\xc2\x88Z51NGC\xb26\x0c+\x0c\\\xcdXu\x97d\x18\xcd\x8a\xef6X\xd5\xd2H+s'2\x81{#\xac\xdf:\xcf\xdd\x98\xa3\xcd6-V\x07s+\x93\xa7U\xe0'\xb7\xb2x2\x18?\xf6\x8a\xa6N\x9aH\xbd\x14\x8eE7\x84\xbc\x97\x85J\x0c\xb0\x10\xe3(\x19\xc5iw\x92.\xa6\x0fge\xddU\x95\\\xe5`rWS\x14\x94\xba.\xa5\xbc\x95\xdf\x94v\xe1\x9c]\xd1\xcd\xc1\xeb\x8d\xbbl\x06,\xbe\"\xcf\xdd%\xb9}\x12\x92F\xa6w\xe7Q\xfe\xbc;\xd2\xcaw\xf2g)\xe8\xc3\x1f\xfbz\xa5\xc7\xda\xb3Vg\xe7\xa1V_+\x7fL\xa1\x1e\x96\xb5P\x8e7\xce\xbe\xd6\xbd\x10\x9b-IF\xff\xa6\xf9\x18 \xee\xec\xe6\x86\xec\xfb8\x98\xb78X\xcd\xe4J\x80\xbe\xe4ErWX\xad\x8b\x03\xb6\xac\xa5B\x84u\xc6\xb2\x89b\xb8\xe3\x14k\x98g-\x8f\xef\xce^\xdbA\xd4\x0f\x00}eZ\xf4\xd9$\x95h\xbcj\xf29.\x9b\xa5\x8f\xbc\xcdK\xac\xd8l\x05\xe1+1\x8bT\xd3h\xc6gsU@\"\x13\xed\xe6DdP\x14\xdc\x1c\xda\xb3t\xe9\x7f\x99\xc6\xbf\xdfYZ%\xfej\xe3\xb6\xcb?\xbb\xc0\x04\x8af\xf8\xc2\xff\x83\x8c\x078~\xd2wB\xe8\xaf\x0b27Kr\x01\xf9w\x179\x8e\xb9\x14\x15`D\xcb\x10\xfe\xec\x0c%-#\xc6\xbb\x0d\xbeWw8\xbd\x1e\\ \xcc\xe7\x16k\x08C3\xcbv4\xb8<\xd8n\xc4\xf2P;\x1d\x85F\xc8%X\xa0\x99\xa2\xc5\xea\xa6*Q!R\xa4'\xad( \xfd\xbd\x16 \x94\x07\xd0\x96\xde,\xca\xd8\xc0\x998(\x9b\xaa\xa9\xab\x95\x08\xcdnn\x07\x96\xdf\xd5\xc9E\x94\xad\x16h\xabs\xe1\x7fd\xe0\x87\xd7\xd2\xf2\x1a\x95\xb0\xd2\xdf\xbb\xb5\xba[\xe9\x15s\xd1\xd9\x8fjVh\xe4)l\xe1h\xf5\x91\xb9\xda\xd4\xeb\xf1\x84\x06\x13\xef\xfbs\x19;OwM\x93\xfb\xfc\x9e4\xccw\xdc\x82\xcf{~\x05\xb2\xcf=!\xae7\x8c\xbaFh\xbf\xb9\x01g\xe9\xafVg\xfe\xfc\x833\xeb\xc9\xed\x99\x80X\xb7\xda\xeaS\xac=+\xccT\xac\xd1\xd6\x16\xbc\xa7O\xa8\x18\x1f\xcd\xa1d\x10\xa2\xf1=\xdf\xfe\xce\x01\xc6\xe0\xc4\x95\xec\xc2\xbd#H\xfds\xd4< \x98?\x13\xbe\x13\xa2uN+\xf6\xf0 `i\x9a\x97\xdeC\xff\x9b\xca.\x93\xc3{\xd3N\xdeq\xebr#4\xa1'\x13\xdd\xa31\xd9\x82!\xbfS\x9a\xa1s\x94+\xe1\xd0\xcbI\xf7\x91\"~\x94W,\x7fdI(\xd5\xc2\x8a\x7f\xbe\x8a\x12&\xcc\xf8K'\x99_\xe8\x95\x89\xdf\xdc\xc0\xeb\xafr\xf8R\x8f\xcaw\xe1\x87v\x9e\x85\x1a\xfa\xaf\x00\xa9\xc9\xc3P\x90~Z\x18!\xe1KP\x0d#\x94\xf6W\xec\xdc\x9f_\xf7\x94K\x8f\xc8l\xa6m\x18\x99=I\xb1U\x0b\x97E\xdc\xf1\"\x9f\xd1\xfcU\x0f:nIs4\x10tw\x07-z\xcc\xd20\x9ck\x06\xed\x9d\x13m|d\xc1\xdf\xadMC5\xbc\xect\xd63\xfa\xba\x15\xd8=\x19\x0f\x05\x0e\xc8\x8d[\xb8\x07\xa9xH\xc8k\"kiR\x1b\xeb\xe6\xcc!PKNCd\x06\xf8L\xd1\x19\xa0\xa8\xa1\xad\xcd\xb1\xd4\xa8\xa3m3\x04;\xd26\xf8hR\xfc\x05\xfbUPC\xdd[gZ\x1b\xd2\x01\xe4\xb2~1\xc0\xe2\x7f\xb1t\xe7\xae\x81\xa8\x16\x04\x9d6&\xd2;\x8b\xeb\xed'\xe1\xe1\xf7\xd34\x9cI\x19\x1b\xc7\xa7\xaf\x85\xc4\x81\xf0\xa9\x12\x82\xe5`Z\x90<|e\xef\xbc\x88\x0f\x06\x1ak$\xce{\xee\x9e_\x8f(\xdaV\xa4x\x0e\xed+\x8f\xbcbD\x17\x11\xe1A\x1f7_\x90\xccpV\x13\x14\xd0\xad\xfd\xb8\x12\xb7\xe5\xe7\x9c\xa6\x17\xd3D;\x8d\x8df\x9cV\\\x98*\x92\xde\xda\x82sr\xf0,\xee}T\xdc{P\xa18\xc2(\xdc~\xfa\xe6\xd9\xf1\xb1\x16O&\x01?f\x10\x84)\x8b71C\xc7\x87\x04\xd9-\x15tNnmR \x1b\xd0\x82\x9f\x9d\xc0\xee~\xf3\"{\x82\x14hXa\xad\x82\xe6I\xbd\xadc\xc9\xaa<4\x8aQ\x16*\xc03\xf7\xe0(\xecG\xede\xfc\x9dk\x8c\xc2XL\n\xc3d\x86(~G\x0e$\xbd\xa0\xe2\xda\xc9\x901\xa5\x05\xc8\xa7\x80K b\xc9\xd4Wrs\xf3\x82\x1e\xec\xef\x8d\x1e\x8aX\xa9\xfaG\x03Y\x93\x97\x8b<\xfa^\x19\xf7Q\xb2\x04\n\xc5\xd9\xa8YK/\x82\x84\xb6\x100\xfd\x01\xfe\x96\xd131!\x92\xfa!H\x1eQ'\x91\xf1\xd8\x99|\xbc\xb9A\x9e\x9b\xbf\xcc\x03Y\x1eb\xda*\xf9\xab\xd8\x04Q\"XE<\xde\xdc\x90\xd5\x02\x7f\x8b\x01\xaa\xf8;\x19\xa9J\xbdQ\xe4\x1a~)\x7f\x14\xdb.01|j\xf9\x981\nx\xb0b\x8bcQG|\"\xe8wK\xe5\xb7\xf4V\x0d\x1d\xf7.\x07\x06Q\xae\xc9\"\x06j\xb4(\x8e\xd0\x7fJ\x89\x84^\xa6\x1b\x02a\xa1:\x9fH_\x14\x11-m\xa7\x81\x08\x0c\xc5^\"$\x0d\x1c\x158(\xac\x1e\xd3P\xbb\x80<\x08\xf5A\x90\x9bFX8\xb7&\x92\xf3\x89^\xe7 \x0f\xf8\xb8\x0d\xc3'\x1e\xfc\xe0Z<\x8c\xc3|n\xb5\x07\xf4k\x9b8Z\x13E\xc3!\x9d\xe3rW\xc8G\xcb\x96\x1c\xcc-B\xf9\x88\xf3\xfc$\x91aFZH\xac<\x04[\x0c\x07\x10\xf0\x7f(\x04\x1bs\xa3i<\xab\xc7-\xdf\x1b\x0f\x9c<\x99\xdf\x99\xf6/XJ\xaa&T\xc9\xaf\xaa\xe7\x95\xd7\x1a\x8a-\x95\xb5\xe4\xb2N\x07\x06\x9f\x82<\x81C\xe0\xe6\x8aC\xa5\xa1W\x184\x085\xec\xda\x83\xb3,\x85e\x94\xf1[.\x8a\xd9\xad\x128\xe4I\x0c\xbe\xeeU\x93\x1e|\xdf\xb3\xe6+h\xd2B\xb4\xd8S\x04\x99\xb8\xcf\xaeR\x16.\xdc\xea\xf2\xd1\xa1\x1eCV\x9c\x0f\xef\xac\xb4\x1d\x12\xf8\xee\xd8\xd8W\xdaOc\x02\x87Z\xcc,f\xf3\xfd]gS\x8d\x0f\xfc\xe9\xe9\nL\xc1D\x03\xb7\x10z\xb1r\x97r<&.\x12\x89e\xcf\xb2\xe5\x92Pw\x15e\x86E\x94\x19\x8b\x9f\xf3h\x95\xad\xc3B\xa0\xd3\x1c\xee\x02-\xa3\xc19K\xdf\x84\xc1f\xc3\xd2\xa6\x05\xae\x98\xabW\xcfbG\x1b\xae\xa7\x0b\x0dL\xbc7\x88\x00\xf0\xbb\x1a\xc5\xf0pOD\xc0\x91\xf1o\xf4\xd9\n\xeb\x00~\x9do\xd3yvN\x07\xa7\xf1i\xf8\xff\xfe\xaf\x9eU\xc0\xe9\x07\xe1\x82]\xbdZ\xba\xdah\x10\x8b?M\xdd\x80\xf4\x17\x96\x90U\x01lS\xf0\xc0\xc2\"oc\xbf\x0c\x1e\xc0\x88(\x0f3\xb3\x86\xe3\x86~\xbf\x0f8\xf8\xee!\xec\x99\xb9\x946\xeef\xb8Dz\x1e\xbd\xd2Jd\x9c\xec\xd3\xa6\x97\x93Ww^\x9a\xcc\xba,n&\xd0\xf8vieZ\xacJ\xa4\xafJ\xc6\xd7\xf7\x13VE@\x94/\xd7CL\x80\xa8\xba\x80\\\x11sSJ@1\x94\xe0\xbc|4\x00\xefR\xc0\xfcn\xb9\x16t\x0d{\xde\xd5\xee\x8b.8\xbf::\x82\xd2\xcf\x90L\x19\xd86\x1b\xb5\xe3\x18\xef\xf8\xfc\xe8s\x82\x15)\x88{A($\x8f\xea\x1dFK\xbe\x87\xaarN\xb1\xf8)q0\x0e\xc6\xa3W\x98\x00\xf9\xba.\x9f\x9b\xc0\x04\xf9{Q@*\x10\xd2M0\xb9\xa096p\x85\x88\x8az\x19\xd3\xaa1\xde\xad\x11M+L\xf3\x89Hs\xa0])z\xe3\xfc2\x8e]C4\x9c$\x8d+\xd9\xfd>\x04\xe1b\x9c\xabs\x0b\xef\x94\xf7\xd7lu\xdb\xc6\xcd#\xaf\xdb\x17\x91\xe7\xf1Mz\xbdbcp\xd4z9\x7f\xf5q?\x8b\xa2?\xf5\xb8\x1bL\xa7Z\x1f\xf7\xc2\xb1N\xe3\x8c\xe9\xc7\xf8m\xf9\xf7O\xef\x9e\xcbc\xcd\x0b\xf6\xf4\x8f\x97\xfe*)\xd4~Q)x\xfa\xf2\xcd\xf3\xbb\xa2\x85\xbas|\x9b\x81\x7fN\xfc\xe1LE&\x81o\xa2h\xc5\xfcpF}T\xf2\xd2I\nT\xa8\xe1k\xe7^\x8bmL8\xc1\x9a\x82\\\xd2\xad0\x91\x0b4\x06\xb1KmN\xb1 E\xb4\xea\x8b\x16{,\xf7\xbbM_&\x8c\xd1\xae/9\xaf\x17\x96y\xfd\x1d\x10\x88%3\xe2m\xb3\x9aV\xf2\xa6\xed\xe5\xe344\x94\xb5o\xe8\xa1\xd6\x90|*c\xba\xc0\x84\xe9\x820\xfd; :\x12\xd7\xe8\xb2k#\xe0\x04v\x87zS\xc3\xca\"\x17\xee\xe4FU\xe8\x1a_\xe7\xbfD3\xeed\\\xbc\xc7\xf3\x1e\xa8\xf2\xe9i\xdf\x9d\x8c\x83pys\xcc\xff;y\xe1\xddPQ\xe8\x877'\xfe\xc9\xcd\xc9\xd3\x13\xcf\xfbZ7\xb9\xc7\x80\xfc\x98\xadW\xeb\x9c=\xb0K \x8d\xbc\xf3r\x15\xf9_\x84{\xd6\x85\xdb\xa4\x15\xe1\x88\xd6\xedD\x82\x80\xf1t\xda'\x9d\xeaf{\xb3\xcfN\xd2\x18#\xc1\xc8\x11\xc2!H2BX\x1eW\xa8\x91~\x1a\xbd\x8c.\xe5\x89\xe6\xa4\x04L\xf8=>\x06\x11\xfcw:\xeb\x81\xd3\xdd\xceu\xe7\x0c\xe9\x95#q\xc1\xb8d\xf2\xa7h\x91\x1e\xf0\x9a\xcb\x9c\xf4\x10\xa6G0\x11wY\xff\xf5\xab7\xc7o\x8f\x7f~\xfe\xfe\xf8\xe4\xc5\xf1\xc9\xf1\xdb_`,_\x9d<\xff\xeei\xf9\x95\xd3\x0f\xfd0o\xee\xc4?\x811\xb0\"\x85!0\x9b\xcb\xeeFf\x04E2\xe3\x05\x07\x9cZBCX\xe7\xc5Dh\x04\xb7\xe8\x8aIB#\xe6\x9f\xdb \x8d\x10\xees\xb2y\x8c\x0f\xda\xa8\xd8\xdf\x89\xd4p\x89\xd6\xe8\x1c\x92\x1b\x86\x81\xd4hKk\x14\xf0\xa4\x0d\xe2C\xb3l(HN\xfc\x13\xde\x17$\x97A:\xbf\x00\xd7*;\x98\xfb \xd3\xe5\x90cc-\xd0\x16\x07\x81\xcf\xcc\x1dQcJ\x8a\xdb\xa6\xb1\x93\xa7'\xb5\x8d)1m\xab\xc6\xfc\x13\x83<6\xf7x\xb6\x1e7!\xf4\xfb\x12\xab\xc5O\xfeg[\xad\xe3\x93\x17\x9fo\xb5\x8e\xc3e\x9b\xd5\xaab\xa0/\xb7Z\xdb\x9fu\xb9\xb6?\xebzm7.\x98\xe9\xb4\xe7\x9f\x0f\xfa\x03\xc3X\xb4{\xa9H\xf6\xf6 S\xc9\xbc&\x10\xaak\xcaa\x0e\xbfP(\x02fX\x87L\xfe,]C\x99\xfc\n*\xe4\x97\xa2\x8e\xb4\xffy\xdb\xae\xed\xc7\xd7N#A\xd7\xd8\xe2\xa4\xf4\x8b\x93no\xd3\xd9\xcd\x14NO\xd3Y\xd7+\xbc\x1c\xeb\xbd\x17~\x10}H%\xf7=\"\x10\xb1\x85\xfb\xee\xbfn\\N\x8by\xe5n\n\xdf{\x13\xcf\x9b\x14(\xb9V\xea\xdc4X\xb3$\xf5\xd7V+\x96\xcfN\xac\xe5\xe1\xca\x83>\xbbbsA\xb3\xa9\xd2H\x96~\x01r\xcd\x10\x07\xc5\xa23\xd9\x08\xb7L\xf3\xb5\xa7\xf47H\x81\xa9yx\x8a(\xcb'\xa1\xe7'\xf74\xf3\xee\xe7q\x1c\xc5\xae\xf3\xad\x9f2\xe5K\xcbx\x99)(S \xf2\x89v\xd9t8#\xda\xa7\xcb\xa6\xa3\x19y+e\xf4sg\xd6\x83\x0e\x9b\xee\xcer\xf3Wv \xbc\x03\x97\xff\xaf\xff\xee\xed3W,\x83\xc9\xff.\x10\xe1)\xba\xbc \x8aN\xd1e\xd3\xbd\x19\xc5\xa5\xe8\xb2\xe9\xfe\xac\x07l\xfapfC\xc2(p\xc5\x80\xb7\xd3\x873A\x94\x0ez\xb0\xe3=\x81U\xeeK\xb9\xf3\xc4\x83\x15\x1a\xf6\x99\x90\x14\x88\xa8\xd1\xddU\x15\xfd\xd9\xc0\x8bM\x1f\xcfp\xe1\xf9\x9e\xed\xb3]\xb8\x0f\xee\xfe\x00\xee\xe3j\x0df\xd0\x85\xae\xcb\xa6\xc3\xe1\x8c\x83\xd9@\x8a\x00qC\xf4/\xb77\x9e\x88\xcb`]6\x0dzV\x1eFS\xdf\xda\x82e?a\xe9\xdb`\xcd\xdce\xff\\\x93?\n\x0d\xda\xa5\x0b\xce\xd3o\x9e}\xfb\xfc\xc5w\xdf\x1f\xff\xe3\x87\x97?\x9e\xbcz\xfd\xdf?\xbdy\xfb\xee\xe7\x7f\xfe\xcf/\xff\xf2\xcf\xe6\x0b\xb6<\xbf\x08~\xfb\xb0Z\x87\xd1\xe6\xf78I\xb3\x8f\x97W\xd7\xff\x1e\x0cG;\xbb{\xfb\x0f\x1f=\xee>8<\x0dOc\xe7\x96\xec; x\xbe\xc4\x86\xddY\xfbm\xc1\xd3A\xa3b\x9cc\xc7\xc8\xa2\x1e\n)\xf2_H\x1eCa\x9d\x8e\xa8\xe3\"b\xcfr3vi\xbcN1\x00a\x7f\xb7Qk\xc4\xe0\x00\x06\xad4?(\x13\xdf7\xbe\xb6\xe2\xc1\x18\xfe\x0b\x1e\xa1\xf0\xb9\x08\xf6\x9f|q\x06E\xe9\xc5\xf44>\x0d\x0fgB\x86a_\xf4\xa0v[|\x8c\xffc|\x95\xd8\xb7{n\xd1\x07)\xff\xee\xc1\x13\xe0\xab\x9c=\x01\xd6\xedz\xc0\xe0\xbf\xd0\n\x8c\xe4%\xa4\xce\x99\x8b\xfc\x10pt\x04\xc3}\xd8\x82\xd1\xde\x9e\xd7\x03\xbd\xf8Q\xb9t\xb4\xb7\x07[\x90p\xa4\x9f`\x12\x90\x83\x03\xd8\x87\x1b\xf0\x158\x04\x12\x1c\x98\xe9r\x15[4\x00\x19\x087\xc3\x81\xdd\x87}T\xd1|\xd2\x90`\x0c\xc3GJ\xd0Slk`lk$J\xf1S\xe1q\xc8\x97F\xaf\xb3\xab\xbe\x8c1\xe9\xc62\x8e\xd6\xea\xc1\x9d#O\x80\xe8\x1e\x1f\xe7u w[\xa9\x08\x06\xf6\xe0,\x0e!\xd0\xf6Z\x93\xb6\x00\x1d\x93s\x8b\x15\xa1X\x80/k\xc45~\x0d\xae\xb1@\xe7N :\xf1\xe4\xfb\xd3\x00\xb7\x8fo\xfa\xfe\x0eR|Z\xe9\xc8T\xba_*\xdc\xdf\x81-@s\x1c>#7\xe0\x10\xfb\xc8\x83.\xa4SfW\xa8\x16\x01t\x87\xf4\x87\x9fyD0\x86Q\x0e\xae\x85v\x06\xa6vv+\x85\x07\x07P\xeeq\x7f\x17\x1b\x1e\xe6\xc0\\h\xb9:\xc0\x83\x83J\xc3\xfb\xbb\xc5\xf6z\x10\x17\x01O\xfd\xfad\x02\xc2\xca\xceVd\x7f\xc58\x93U\x02\xc1*,\xbc%\x89\x16\xd5x2X\x9c9>\xf1\xca\xb7\x19\xf2\x97\x985\x12\x83[o\x03C\x80\xca\xfc\xb8\x91>z\xae\\\x83\xf9\xe1\x0b\x9f\x90 \xd8\xea6\x16\x88|\xa1\xf3)\x9b\xe5I\xc0\x94\xa8\x96\x16|\xe6\x08f\x15E\xb2q\xb3=\x87\x08\x84\x13\x84\x10\xd7\x1b\xf0\x04\xa2Id\xd3j\x08\nY\xdfo\xecZ\xfe\xdd\xc9P\x07i\x9f\xe6>x5a\x81\x90\xa8;1k^\x16\x11\xce\xa2U\xd2\x0e\x058\xc5SyG\xfa\xa6*\x9c\xf8\x93<\x8cZ\x1c\xfa;\x9e\xe1\x8d\x1f\xc4\xc9\xdf\xeb\x10\x0b\x7f\xdd\x9a\x83\x9a\x89\x19=\x8dc\xff\xda\xf5\xa5\xdb\xa3R\xf4\xf0\x13\xec\xdf\xed\x04\xfbx\x82\xcd'7h}r\x03\xf4\xe1G\x93!\x0d\xe1~`\xd7 \xff\xba\xec\xd6ok%\x9b\xb2\x19Ge\xd1t\xc0o\x19\xfcw6\xfb\xd3\xa1\xde\xb2\x8f&\x9a\xfac9\xd4\x99\xf0\x06\xb6\xeccT\xd8\xc7\xcc\xb8\x8f\x99m\x1f\xf9ne\xb8[Ae\x89{\x10\x89\xb5\x0b\xc4\xda\x05\xb8vV\"&\xfa\xeb\x0fp\xf1\xd6\xbe\xe51N\x98Uun\xf6)\xfcrg\xb8\xf6\x82\x0dB\xb0\xc4\xfe\xd2\xee\xb1\xb0'L\x10\x15\xa2\x0d\xa7lV{\\>/\xc4\xdb\xf0\xfc\xdf\xcd\x8f\xf2\xb7\xe4A\x16.\xd82\x08\xd9\xe2\x13%/5\xcbp\xfbE\xf5*\x19\xe6o\xcb\xcf}\x8c\x82\x85\x8c(V\xd7\xbb\x89\x93\xab\x13\xfa\xfd\xcd\xbc\xa1\x7fK\x1e\xc4\xec\x9c]}\x11U\xca-\xe4f\x01F\xa6\xc1zm.'\xe5Mg\xa6\xb19\nxp\xfa\xc0\x9d\x9e\x07\xeb\xd9}\xef\xeb\x07R\xb3a\xae\x1e\x1bb\x0c\x80\x18\x94\xf3@\x8a\xdd\x07V%\x02i:\xa4\x05o8\x1d\"\x1b&\xd5\x07G\x9c%mq]\xf3\x9e\xd0\x9aw\xcar\x03\xa0\xb8`\x0b\x947Si\xe5K\xdf\xc1\x7f\xce\x8a\xcbS\xa2-:\xa9\xdf\xca\xab[0\"\xea\x81e\xc5P\x93\x95kFY\xaf\xcc\xc7|\"\x92PT\x1au\xd0\xd6\x14\xe6\xb6\xf8\xa4vC\xf8Zu!\xed'Q\x16\xcf\x19ty\x81ua\xd3\xfe\xf9*:\xf3WB\xe7\xd7=\x04\xe7\x9cB\xf5\xe5\xa9\xe7\xf3Wkz\x15\x9c\x87Q\xcc\x9e\xf9\x89\xfe.\xe0\xef\xd8\x97BfO\xb4J\xea~\xd1\xa21]\x06\xe1\"\xbaT@A?\xfb,\xd9\xc4\xc1\xda/\x19\x06\x06\x8d\x98\xd1\xa8N\xf8-y \x07\xff\x17\xe3\xc6\xaa\xbaF\xfe)\x18p\x11\x06\xf8\xe6{\x16\x11!\xc8\xf48}4\x0e\xe3g\xa1\x9eM\x8f\xfd\xf0\x9c\x8dkyo[TQq8^\xc7\xd1y\xec\xaf\xe9P\x84\x18\xfb\x8e\xef\x98\x0c-v\x16-\xae\xb58<\xce\xf3+\x0e\xf9I\x10\x85oR?ek\x16\xa6\x8eVu:\x98\xa9&\\\xe7i\x1cG\x97/\xc4\n\xe7_\x96?`\xea\x0d}\x8bN\xcf\xb7\xfd\xca\xc0\xe6\xebZ\xb1\xba5hD\xd4\x9f\x84\x8eEt\x9c\xe6\xcd\x0f\xb4\x8d\x0f\xeb6\xbe~\xd3\xff\xb0`s\x9b\xc3\x0b\xdej\n\n\x88\x81\x95\xdb0\x14\xbfu(\xe0\xbbc\x84\x82\xbc\xaa\x82\x02^\xd7\n\x04\xc5\xfae \xe0\xc0v\xeb\xaf\x0cf\x10/\xfc`\xc5\x16\x90F\xca\x16B!\x0c\xbb6\xc5\xd8\xc1\xc6\x8f\xfdur\x0b\xab\xd0H\x06T\x0d\xfd\xb5 >\xc5\x0di\xec\x0cW\x1c7\xba\x07\xce7\xabh\xfe\xa1t\xde\xec_\xe1\xf2Mp\x0d\xe4\x02\xbaQ\x0fB\x199x\x8a\x96\x0b\xfc>\x9e\x0egt\x01\x0b\x95\x8b^\xdd\x91\x08\x02#F\xe5\x9f\xd2g\xf5&4w\xbe\xa1\xe5\x00\xfe\xd4;Z\xdd\xba\xcat\xed\xcb\xda8X<\x00\xf6F&\x8b1\xf7\xd1N\xa98\xa3\xda\xe5b\xbfN\xdaW\xac\x9a4\xcb\x15J\x08\x0f\x0e\xe1q\xb1h \x870,i\xb3Vp\x08;\xa3\x12(\xf0\xb2\x9db\xd9\x05/\xdb-\x96-x\xd9^\xb1\xec#/{X,\xbb\xe6e\x8f\x8ae\xe7\xbc\xac4\xbe5\x1c\xc2ni,\xefyY\xa9\xdf3^V\xea\xf7\x12\x0ea\xaf\xd4\xc7\x15\x1c\xc2~\xa9\xbd7\xbc\xac4\xb7\xe7\xbc\xac\xd4\xc7S\xbe|%7\xc4W\xbc\xac\xf4\xedo\xbcl\xbfX\xf6\x01\x93\x15\x96*\x1eca\xa9\x97\x1f\xb1\xb04\x95\xb7ph\x80\xf8\xc1\x18\x9c\xd3\xd3\x81\xe1\x1ez\x88o|\xc3\x9bG\xf8\xe6\xcc\xf0\xe61\xbeI\x0do\x86\xd4Qhz5\xc4W\x1fM\xafF\xf8jiz\xb5\x83\xaf\xca\xd4\x1c\xff\x1b\xd1\xd0\xcbBh\xfe\xb7\xb3;\x86{\xa7\xa7\xce=\xc3\xd8\xa9\xaf\xd3Scg\xd4\xdb\x89\xe9\xdd>M\xed\xbdi\xa5F;\xd4\xeaK\xf3Kj\xf5uI\xc6P\xac\xfa\x8c_\xd6\xce\xb5\xd3\x03\xe7\x17\xfe\xbfk\x96\xe0\xb3\xf8\xe7\xf9\x1b\xfe\x0f\xd2\xbc\xce+\xfa\xff \xff?>\xd2S\x84\x8f\xf4\xffWX{\xb9\xc4\x8a\xe2\x9f\x17/\x9c\x99)\x90\xc6\xeb*\x92\xcc\xc5\xb5%\x0d4Y\x9e\x1c\xd6z\x93\xf5(X\xc6ho\xcf#B\xe8\xca\xa1h\xbd\xa3b[\xca\x02\x19\xab\xef\xef\xed\xed\xc8\x0f2\xf1\xc1\xae\xe1\x033\xc9\xde\xa1FvG\x8fw\x1f\xef?\x1c=\xde\xf3\xbcb\xf8\xdby\xb4`\xb0\x89\x82Bz\\\x8av\xb8\xf6\xafe\xda\x85\xf3\x98\xf9)\x8b)\xf3\xc2\xe0\xea\x85\xf83\xd1\x0d8\xd0wb\xa0\x8f\x8a;[\xf8%o\xbc\xd3SG\xc4p\xcc\x836\x0e\xf0\xfbm\xc5'{\xd0\xd5\x987S\xb0\x92\x9f\xaa\x9b\xa5\x85\xac\xc6\x9d\xc9crG2\"\xb6\x0c0\xfd\xa3\x9f^\xf4\xd7\xfe\x95\x8b\xf9\xc1E\xf1\xcd\x0d\x8c<\x19\xda\xfbC\xb09\x0e?\xfa\xab`Ami\xbf\xf58\xdc\xcbUt\xf9\x92}d+\xa4`\x83\xe4$\xe2kz\xee\xa6\xf9\x1bO\xfa\x1fie\xb2\x97\xf4z%\xe2m\x17\xaeU\x1bE]\xcd\xffkH\xdfU\xe0\xdcrw\xfe\xff\xfca\x919\x87\"\xfb \x19iP\xc6\xd5\xb8\xa40`J'C\xce\xff\xd1\x13\x8a\x88:\xa4\x8c\xe4\xf14\x10Z]q\x16\xd84C\x0f\xeeN\x87\xc8\x99,7]\x1d\x91A/\xff\xcc\xc0\xd5r\xd0\xc8\x94\xff\xb6\xd7\x03\x97\x12\xb8\x95B\x90\xf7eV!\xde\x0foOdt\x98\xf7u7\xcb\x1e\xf8\xd4\x99\x8f\nk\xfd\xd5\xd4\xe7\xe3\x0b\xa7\xd9\x0c\x0e\xcb\x91oA\x13p\x17\xe1\xd9\xd5@\x8c\x03\x0e\xb6\x98H\xf3H\x05;Q\x9c\xfe\xc0\xae)\xd5\x8c\xfaQ\x8c\xde\x1e\xb2\x7f\x06\x0b\x19=]\xfd\xba\xb9\x81G2\xf6y\x18\xfd\xc4\x96\xd4\x86x\xd4[\x08\xa3g\xd1z\xe3\xa7?\xf2\xe3Lu\xb4\x02\xbd\xe6<\xe2\xd0\x8d\xeeV\x97b)\xb5\x02\xbd\xe6\x1d\xe2\xc5\xcb\\Du\x9f<\xbf*\x86\x98\xc7\x9cWa\x1e\xa6\xbe\x98I\x9a\x97,2\xfe\x85\x9f2a\xa7@\xa5Y\xc2\x16\xdf\xeao\n\xc1\xfdL8\xe2\xc4x\x98\x10\xe8\xc5i\n\xe0\xb0\x14:\x96y\"w1)\xe6\xb6\x87\x04\xd7|l\x89f\xaa\xf4\x04\"8\x80\xe4\x89\x879\x1a\xd0j]\xa6\xe6\x17n|\x98\xf8?\xf2\xd0\xda\x87\xfcCD\n\x0b\xd1A\x82\xa9\xdd\nox\x97\x14\xc65Bc!z\x0eu!\xc4\xa9\xe0\x03C\x01\xd7\xddC\x08<>\xc4\xeea\xd9\x9dL\x80\xb0_\xbbD/\xebbo\x9bc\xebJty\x1f4\xce\xce\xd4\xf6\xb7U\x14-\x19\x0e\\\xb1\x15\x87>z\x9c\xd76\xf4okC;\xa3b`\xaa\xe1h\x1f\x99\xf7\xfda9\xf2\xd5\xe8\xf1\x1e\xff\xc5)\x94\xdcm\x82\x93$\xe2\xd7\xcd\x0d\xec=\xdc\xd9\xdd-~\xc7/\xe3\x1d\xfe\x8b\x92Q\xa8\xaa\xbc|\xbf\xd4\xf5p\xb8;\x1c\x0ek'\xf2\xc2:\x11\x9cb\xa9\x1fl\x99?\xbe\xcf\x1f\x9f\xe6\x8f\xaf\xf2\xc7\x0f\xf9\xe3\x8f\xf9\xe3e\xfe\xb8\xa8\x1d\xd6;\xeb\xb0\x1e\xfcz\x1a\xde\x07\x19\xc8D\xdfn\xf9\xc4\x0f\xd27\xd5X#\xbfs2\xa7X\xf4\x0b\xe7U\x8aE\xff\xe4\xb4M\xb1\xe8g\xc0\x88\xd2\xd5A\xfeP\x1fg\x9d\x8f#\xd2\xed\x9b:\x86\xe8'sK\xf9\nO:\x85\xfa\xa8\xbe}Kx\xa0R\xce)\xd5\x7f\x8b\xec\xa3\x85\x04%\xa5\x9d\xc4x<\x9do]\xba\x8c|,;\xcb\x1f\xdf\xe4\x8f\x97\xf9\xe3\xfb\xfc\xf1i\xfe\xf8*\x7f\xfc\x90?\xfe\x98?.\xf2\xc7\xeb\xfcq\x9d?n\xf2\xc7\xe3\xfc\xf1*\x7f<\xcf\x1f/\xf2\xc7\x8f\xf9\xe3\xf3\xfc\xf1713{V\x17C\x82\x07\x839\x8a\x97\xbf\xed\x10\x0bb\xf2\x06\x0e[\xff\x13a\x05c\xdd\xef\xd7\x9a\xcdS\xff\xe3m'@\x91\xdd\x9a'\x02\xe2\xe6\x8a\xa7\xa3\x861\x83\xca\xffB\xb3\x9c\xa3\xfa'\xe2'=\x81.\xe7\xf50\x9b=_\x07Q\x01&\xfcqL\xc9\xeb\xa0\x0b\xffp\xe7\xc4L\xa2\xd2\xa2\xb63{\x98K\xc8A1\xb2V\xfa\x83\x83g\xe65A\xfb\xcf\x8d\xd0~\x0f3\x934+\xf7\xe4\x9fb\xa4s\xaa\\p\xcaV\x1aI\xc8LK\x84\xd0\x111h\xfb\x80\x0e;\x9c]\xdb\xdf\x19\"\x11P\x8dO\x1a!WL\xdf\xec\xef\x8c\x06\x90\x07+\xdd\xd9\xdd\xe1\xcc6\n\xa6^\xbb\xc3\xc1\x08\xbd\x96\x19lS\xeb\x949f[|\xd6%\x1e\x8e/\x1b\xa7\xdd\xc6$\xf3z+\xcce\xbb\x87\xd0AJ\xe6\xdf\xfc\xe2\x99@:\x8df0\xa6[\xee\xb5\xd9\x1bM\xff\x93\xba\xd4\xba=\xf3(}\xa8\xb9!\x11\xfc\xc1\xbee\x05\x99n\xb0\xdeDI\x12\x9c\xad\x84\xb7\xfb\x18\x02!\xaa$\x0b\x10\x8a=\xe64\x11v\x7f\xb8\xf5\xfc\xfc\xd7\xf64Rp(\xe95)\x00\xc4\x90k\x06-@\\D&\x85XRF\xf9E\xc8\xcf\x1b%\xd46\x7f7\"|\xa4\xde\xf1Q8]\x07\xb7K\x1e\xcam\xbalNC\xa7v\x86\xdf[\x19a\xdb\x909l\xe4(u{\x88\xb9/\xa9\xf4\x85a,\x8a\xf8\x99\xb2\xf1/E6\xfe{G\x98\xa2_\xd0\xfe1\xf8\xf39\xdb\xa4 \xaa\xde\xf0\x06^QN0\\\x81{M7MqZ\xd3\xd5\x8cff\xbfy\xecW\x8ad\x87cc\x95\xda\x90\xd3\x06\x83,#\x9b\xdf\xa9\x97\x8f\xfeOA\xc6G\x87\xbe\xcc\xb3\x17\xf4\x07r\xc8a\x8f\x8er\xd8\x83\xce\x10C\xdf\xa8\x9f\x03Cj\xe0\x04\x14\x94P\x13\xe5$\xad\n\xf9\xe9,\xed\x01E\x85+r\xb9\xe5\x14\xa6\xbc\xf9y\x0fV=\xb4\xff\xa8\xbaIq\x00Ea\x87z\x85\xbe=\xf2MU\\\x86\x02;W\x93P\n\x8dX\xae$Q\xbbM\"@-al~\x13\x18\xda\xd1\x8a\x1aZ\xd4?.\xa0:\xa5\xee\\g Z\x12\xf8pF\xa9n([y\x9d\x05\"\x14D\xacDB,\n\xfa\xb6\xec \xf1`C\x0fE\xf6\x9c\xd5\x10\x1b\xceW&\xe2@\xedb\x1c$\xa1\xd6\x12\x91%\xc2)'p\x16\xd3h6\xeb \x1cCf\x80>\xe5`\xa7\xff\x08\xee\xf1t\xb58A\x02\xf8\xf1l\xf0\xa7\xdc\x9b\x823\x1e2\xeb\xbb\xac\xb3\x14[\x875\x8b\xc9\xcc'\"r\xd3\x84\x13\xaa\xe2\x11\x1c\xe5\xf1MS-\x1d{?\xf1\x97\xec\xdb\x92\xb5B\x8d\xe5\x1eM1\xee\xb3\xab\x94\x85\x0b\xb7z\x8e\xc8Fs\x0cYq\xb7\xf0\xc6/\x8d\xeeN>?\x02\x90\xc85V\xba\xd6\xf0\x83\xed\xbc\x7f\xcf\x92\x1f\xa3E\xb6\xaa\xc6.\xfd\xe8\xaf\xb2\xa2w\x1f:\x8a\xf5\xcfY\xfa,\n\x97\xc1\xf97\xd7\xefb\x0c\x86\xdb_D\x97\xe1*\xf2\x17T\x0e\x87\"\x1eB>\x80\xdc\xe9h4\x18j;h\xf8\xd4\xae\xf1*\xdb\x16\x18\x15\xbd\xa2\x92;\xe0C]\x86\xfd%K\xe7\x17^\xc5E+\x9f\x93qJmvU\xd51\x92-\xca\x97\xb8\x9fl\xd8\xfc)\xd6L\xccH2\xf7\xe7\x0dJ\xcb\xe1\xa6^?\xbd`\xe8\x07\x17\xe9\xe9F\xe5\x9f:E\x91y\x14\x80\x9aSM\xbe\x8c\xce\x88\xa8.\xed'\xa9\x9ff \x1c\x1d\xc2\xee\x00\xd3[\x04\xfdl\xb3\xf0S\xf62\xf2\x17Ax\xfe\x06\xdf\xbb\xce\x12\x1d\x17i@\x9c\xb3\xb8e\xb5w\xf1\xcaux\xc1<\n\x93h\xc5\xfa\xa8\x14se\xffo\xd9U\xaa\x91'Y\xbc\xe2@\x86\x17\x07R\x89\xcc\xe5[)\xdcQ\x7f\xf1\xd7+\xea\xc1s\xc3~\xca\xae\xca!\xb4\xa1\xaaF\xfb[\x9d\x1f\x1d\xf2\xcfY\xda\x12\xd2R^\xf78t\xcbw\x15L\x80\xc1\x18\xa6l\xf6\xf7\xc2\x12\xa5s\xaf\x08w~\xfa\xf7\x0c^\x84H\x91\xcb\x1b<\xef\x0b&\x10\x83)9\x93\xd4\xc7\x96\x83\x17\x16[F5\x9a;\xdc\x7fT\xea1\x11#\xd9-\xe2!j\x93\x02I\x92\x0b\x06\x07\xbcL\xbe\xf0\xdc\xa0\x07I\xff\xdd\xebo\x9f\xbe}\xfe\xfe\xd9\xab\x93\x17\xc7\xdf\xbd\xe9\xb5\xdc>\x0c\x0e\x8d\x80\xeccp\xd1\x7f\xbc\xf1\\\xd6\xdf\xf8\xd7\xfc\xa8\xeb(\xde3\xf7\xfa\xf6\xd5w\xdf\xbdl\xdb\xab\xbc9U\x07f\xb5/\x02UEt\xa2\x86\x9c\xf0\x97=\xe8\xc4\xc5\xd1\x05\xc2\xf3t\xe6}\xc5\xf7\xf9\xc1\x83\xff\x03\x14J\xe2G\n\xdb\xf4\xee\xa7\x97\x87\xc9\xa5\x7f~\xce\xe2\xed,\xd8\xe6xg\xe1\xaf\xa2\x90m\xa3N$\xed\xff\x96\xf4\xd7\xfe\xe6\xff\x07\x00\x00\xff\xffPK\x07\x08v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00swagger-ui.cssUT\x05\x00\x01\x80Cm8\xec\xfd{s\xdb8\xb27\x8e\xff\xff\xbc\n=\xbb\x95\x9a\x99\x1dS!EQ\x17\xabf\xeb\xc8\xb1\x93q6r\xc6\xcem\x92\xad\xad)\x8a\x84$\xda\xe0\xe5\x90\xd4\xcdz\xf6\xbd\xff\x8aw\\\x1a $;s\xf6\xf7\xad\xb3\xd9dl\xe2\xd3\x8dFw\x03h4\x00\xb2\x9bl\xed\xe5\x12\xc5\xda\xda;\xfc\x9fN\xe7\xe5\xdf\xfeo'\x08c\xdf\xc6\xde#\xea:I\xd2\xd9\x0c\xbbzW\xef\xfc\xbf\xce\xec\xfac\xe7\x9d\xe7\xa0 A\x9d\xff\xd7Yz\xe9j=\xef:\xa1\xff2@N\x88\xed\xe4%M\xf7\xb7\x97\x8b0H\xb5\x85\xed{x\x7f\x9e\xd8A\xa2%(\xf6\x16\x13'\xc4a|\xfeWs\xde7,\xe3\xdfD\xfd\x9dU\xea\xe3\x03\xf6\x02\xa4\xad\x90\xb7\\\xa5\xe7F\xd7\xb0&\x9a\x9fh)\xda\xa5Z\xe2=\"\xcdv\xef\xd7Izn\xe8\xfa\x8b\x89\xb6E\xf3\x07/\x85K)\xce\xf3\xd0\xdd\x1f|;^z\xc1\xb9N\x95\xd8q\xea9\x18\x9dQ\xcf\x12\xcf\xa5\x9f,\xc20E1\xf5h\x85l\x97y\x14\xd8\x1b\xea\xf7\x049\xa9\x17\x06\x07\xd7K\"l\xef\xcf\xe78t\x1e\xe8\x16\x1b\x87\\K\x99\xf0\xe7=\xe4OJ\x19\xbb\x83!\xf2;\xb4\xa4\x0bo\xe9\xd8Q\xc6\xf0\x8cy\xbc\x8eii}\xdb\x93UZPT\xea0\x90\xdf\xe9\xeb\xd1\x8e\x96+>T\xca\x9d\x87\xbbL\xe4\xdd2\x1f:\x16a\xec\xf3\xca\xfbg\xba\x8f\xd0/1JP\xfa\xaf3\xbe Y\xcf}\x8f)\x01*\xcbf\xb5\x92\xa2(\xfdW=\xb6\xdaQ\x84\xec\xd8\x0e\x1ct^\x14\x01\xd5\x974\xe7\xe7\x9a\x1f>j\x8b\xd0Y'\x9a\x17\x04\xcc\xd4C\x8a\xaa\x04-\x85o\xc1\x16\x95\xf3 \xde\xeb&\x91\xed\xba\xd9l\xa0K\xda\xd0\xb0\x89\xbd`)n@+\xae\x92^\x02,E\xa7\x11\x87p\x9df\xbevnD\xbbr\xec\xed\\\xe4\xc0\x8fh\x972\xb3$\xc2n\x82\xd2C\xd5\xb0\xaei!\xbf\xd3\x1d\xe6\xff\x0e\xb8a\x01\xa3%\n\\h\xda\xac\xe7\x14j\xd6$\x9e\x16\x83a5\xacW\xdd>\xb5\xe7\x18M|{\xa7m=7]\x15\x1d\xa5\xd6\xf2d\xbb\xf2R\xa4\xe5\x83\xf4y\x11y1Sl\xb8\x8cQ\x92\x80\x83\x8f\xd2(Xw\xe1\xbaw\xd9\xeb4\x04\xac\xeb\xac\x90\xf30\x0fwP\x1f\x89m\xd7\x0b\xffu\x92Vd\x0e\x15\xac\xfd9\x8a3\xef-\x19\xe7^\xa9%\x91\x17h@\x17\x14\x10\x85\xeb\x94&:\x94C\x90\xa0\xa1 \xb2cg\x05v\xdfLY\xb9\xc7LJ\x0f\xd3\xc2\xc5\"A\xe9\xb9\xd6cB+\x8aU#K\xf1@s2nX\xdc\x06\x11]\x13\\@\xd2q#[C\xbf\xf00\xd2\xd6\x11\x0em\xb7R\x82pt\xcaG\xed\xcaO\xe9X\x00\xa5\xb6\x87\x13:\nE\xc1Z\x12\x85&k\xdf\xb7\xe3}\x8d\xc0^\x92j^\xca\xf4*\xc7\x0e66\xec\xc4\xb4V\x8b \xed_\xcc$\xe4G\xd8N\x115\x93Rd]\x17\xcd\xd7\xcb\xce\xdf\xa8q! \xb1\xe7v\x96!v\x01\xac\x96\xf7;\x90\xe2\xaf\x8b\xc5\x02\xa2\x98c\xdby\x80)\xd8\xf8\xa7\xa4X\xc6\x9eK\x04Ndx\xdbY\xc7\xf8G\xd7N\xeds\xcf\xb7\x97\xe8e\x14,'Y\xf7\x1d\xf4\xcf\xbc\xcf\x17\xef\xef\xb6\xfa?\xde,\xc3\xe9t:\xbd\xf9\xf0iu\xf5i\x99\xfd\x98\xffs\xfdj\xfau:\x9d^^]\x0e\x07\xef\xb2\x07o~\xbf{\xfd\xe5\xd7\xbb\x8f\xf3\xde7\xdd\xed\xbd\xde\x7f\xbb\xbd\xb8\xf8\xf6f\xec}\xfbp\xf1v\xfe\xe5u\xf0\xed\xf3[\xfc\xf5\xcb\x9d\xe58\x18\xff\x96\x11\xecW\xd1\xe7\xd7+\xfd\xcb\x951{\xef\xdfl\xe6\x1f\xacU\x81\xb7\xfa\xf3\xdf\xa7\xc5\xff.\xb7/\xd1\xaf\x17\xab\xaf\xbd\x14\xbb\xaf.\xbco_\xdch~\xaf{\xc3\xe1\xfa\xe5\xb5w\x11}\xbb\xd4\xbd\xcf\x8f\x9fofW\xc6\xf6\xb6\xf79\xb4?\xad\x06\x8e\xff\xf9#z\xb0>}5\xa3\xf8\xeb#~\xb8\xbe\x1f\xfd|}\xb9\xeb\xbf\x0fV\xa9\xf3\xc6\xc0\xee\x9b\xab%zc$\xf3`6@\x97\xba\xf7\xf5\xcb\xdd\xe6\xab\xffi\x90\xfd>\xff\xf2Y\xff\xfaa\xe4]\xff\xba\x1c\xa07\xc6\xd6}\x93\x8c\xaf\x1f^?\xcc{o\xf1\xf5\xeb\xd5\xcd\xa7W\x17\x97s\xf3-\xbe\xbe\xfc\xb4\xbe\xf1\x8c\xfb\xd9\xc7\xab\xdd\xf5\xa5c\xbd\xbb\xbf2\xde_\xce\xf67\x1f\xb6\xcb\xd9\xfdtw\xf3a\xb4}\xffa\xb4\x9b\xbd\xd2\xb7\xb3\x8f\xe1nv\x19\xeeg\xaf\xa6\xcb\xeb\xea\xef}\x7f\xf9\xdb\xafo\x1f\xbe\xddG\x1f\xee\xae\xbe\xd6\xf28\xfe\x9d\xff\xdb\x87\xb7\xa1\xfb\xeb\xdd\xf6\xbd7\xda\xb8\xa6k\xbe\x0b\x9c\xc7w\xfex\xffm?\xda\xbd\xff\xf8`\xbd{\x9c\xee\xdf=^\xef\xdf\xfd\xfe\xf6\xe1\x9bg<\xa2/\x96\xfe\xf5\xf7e:\x0ff\xf7\x04\xdf\xabo\xbf\xdf\xdc;>\xde\xbao\xf0f\xee]\xec\xbf\xbd\xf9:\xf8\xfa\xe5\xed\xc6\xfd\xfdv|\xed]7:xcl?~\xd2\xc7\xd7\xfeJw\x7f\x9d\x0e\xde\xed\xc7kg_\xdb\xe2~\xde\xd37\xe8\xcd\xeb\xed\xbb\xc7\xab\xf5\xec\xd58\x9d\xe7\xfaY\xa5\xf37\xd6\xe3\xfb\xe0F\xff\xe4\x7f\xa6d\x9e\x07\xb3u\xa9\xd3\xf5\xd7\xde8}g\xaeV\xce\xab\xd1\xee\xdd\xfdt\xe3\x18w\x96\xf3\xe6\xd3\xe6\x93\xff\xf9qn~\xde\x7f\xed}\xfe\xf0\xed\xcb\xd7\xfbk\xef\xa2?\xff\xb2[;\x8fQf{EY\n9\x9c+\xe3\xe6\xfd\xc3\xdd\xe6\xab\xf99\xfd\xf6\xc5\xd2?|\xba\x1d_g\xb6~e=\xd8_n\x07\xb3\x8fw\x97\xef?~\xed\xdf\xe8\x9fz7\xfa\xe7\xd7\xb3\x8f\xaf_\xdf\xdc/{\xb3\xc7o\x97\xb7\xf7\x0f\xdb\x9b\x87\xdb\xfe\xec~\xb9\x9d]]\x13\xfc\xf0\xda1\xefVs\xff\x06\x13\xfc\"\x9a\xdf\xad\x1a\xbf\xcb\xe8\xd2\xf1?\xaf\xdc7\xe3\xfd\xe77\xe3\xcd\xfcR\xf7n\x0b\xfd,?\xbdYm\xdc7\xe3G\xfb\xcdx{}usy}y\xbd\x9d}\xfc\xb4\xfc\xc7\x95\xb1\xfa\xda\xc3\xeb\xbc\xec\xd5\x83\xf7\x9b7\x1d\x95v\x1a\xdc\xbd\xf9\xbc\xb7\x7f\xff\x86\xbf]}\xdb\xcf{\xfa\xd21\xef2\x1d\x0e\xec/\xd6\xa3\xfb\xe6\xf5\xfak\xef\xf3\xdb\xbbK\xdd\xcb\xf0\xef|\x1c}\xbb\x0c\xcd\x9b{g\x7f\xfbpk\xde\xdc\x7f5o\x1f?\xedf\x9f>\xf5n\xef\xdf\xbe\xba\xd5?\xedo.\xa7\xfd\xd9\xc7\xe9vv\x7fe\xce>\\\xd7\xfc\xbe\xbd\x19\xdf\xbb_\x0c<\x0f\xee\x08~w4\xbf\xc7V~\x9bL\xf6w&\xe0\x93\x99\xaf\xbe\x1a\xe7~\xf9\xe9\xe1\xeeM\x81+\xfa]\xde\x0f?\xf6\x97\xbf]\x8e\xfb\xce\x9b\xd7\xf7v\xef\xb3~\xfd\xe6\xf3:\xeb\xef\x8ew\xfd\xf2\xb7\xe4\xe2\xc3\xcfof\xd9\x08q\xff\xe1\xd3\xdd\xc5\xe7_\xef\xed\xaf\x9b\xc7\x97/\x1fG\x97\xef\x92\xcb\xfe\xd2y\xf3\xbb\xf7\xf5j\xfa\xe6\xe2\xfa\x1fo.\x02\xf4\xf2\xe5\xe2u\xb4\x9d.\xb7\xd3\x8b\xf1hj\xbf\xeeE\xf7\xf8\xd3mF~\xf1\xf6\xee\x93u\x15?\xbc].\x97\xbf\xfc\xf2S'F\x11\xb2\xd3\x8e\xde\x11\x8e\xa4\x9a1x\xc6\xc1\xf4\"\x1f\xe6n\x8b\xc1t\xba\x18\xbd\x1c\xaf\xfew0\xfd\xdf\xc1\xf4?u0}\x7f\xf9u\x7fw\xbf\xba\xba\xbb\xcc\x06\xd3\xaf\xfb\xd6\xc1\xafe0m\xf8\xdd\xaa\xf1\xfb\x0f\x1aLo?\xb6\x0e~G\x0d\xa6\xb7\xed\x83\xf3\xf7\x19L7\xaf>\xe8\xc6u6\x18\xcd\xea\xc1\xd4\xbf\xeb\xbf\xb4~\xbex\xfd\xdb\xc5b:{\xed\xbf\x9c],w\xa3\xbb\xe9\x9b/\xaf\x02c:\xf5?,\xcd\xfe\xed\xe0\xe1\xe2\xf2\x1f\xb37\xb3\xcbW\xdb\xebWhv\x8d\xfc\xd7/\xad[{{\xe5E\xd3/\xdbO\xab\xed\xd5\xfd\xecr3\x9f~\xc1_\x1e6\x9f/\xb6\xeb\xd1\xe6\xf6zz1\xbd\xda^\xbc\x8aV\xa3O\x03G\xcf\xc7\xa5+\xfc\xfa\xe3\xc3\x87\xf5\xad\xff\xea\x95\xd2\x00<\xd2\xf2x\x97\x1c\x85\xb3`\x99\x1d~\xef#T\x8f\xbf/\xc7\xf7/\xfb\xb7\xd3\xafw\xbf\xaf\xa2o\xcb\xe9\xf4\xc3\xa7\x87\xff.\x03\xd9\xe6\x7f\xbf\xbdL\xa6\x17\xaf\xaf\xdc/71\xba\xcdF\xe6\xdbj\xe0|\xd9\xbf\x9d\xed\xec_\xeft\xe72\xdc\xbc\xebY\x8f\xef\xfcb\x1c{\x97\x8f\xb5\xe3\xfe\xd7\xdf\xa7\x9b\xd9\x87\xfe\xf6\xddv:\xfa\xcd\\m\xbf~\xb9\x89\xbf\xfd~\xbb\xfc\xea\x7f\x0e\xec/\xfd\xf1\xf5\xfa\xe7\xe1f\x7f\xbd\xb4\xbf\xdc\x8e\xaf\xb1c|\xfcxq\xe3\\\xdd`\xfb\x0d\xbeF\xc1[\xfc\xc9\x8c\xde\x7f~s3\xb0{3\xeb\xdb\xab\xeb\x97\xb9\x8f^f\xfd\xf7\"\xfd\xf6\xfb\xdd\xaa\x19#\x96\xe3\xeb\xb2\xee\xf7\xbe\xf5\xf8\xde\xcf\xc7\xe0M\xd6\xe7\xf31\xf9\xd7\xbb\xf8\xb7\x0fo\xab\xb9\xe2\xeb\xc7\xcf\xd3\xe5mo\xbc\xff\xf6aj\xbc\xbb\xff\x9a~}\xbc\xda\xcd>L\xcd\xf7\x1f\xfa\xbb\x9b\x8f\xcb\xc7\xd9\xfd\xa7\xa4\xec'\x9b\xd9\xe5\xc3f\xf6q\x9a\xce.\xaf\x06\xb3\x8f\xd3\xc1\xec\x9e\x18c_]g\xe3~\xed_\x8d<\x99/\xea^\xad\x1b\xd35\xdd\xbde\xce\xf6\xd6\xc6\xf1\x9d\xcd\xec\xe3\x83\xf5\xfe\xc3h;\xf3F\xfb\x99gd\xf4\xa9cf}\xf1u\xff\xdd\x17\xeb\xf1z\xdf\xf0\xbd{\xf3\xf9\xf1\xab\xf96r~\xbd\x8b\xe6\xbd\xfe2\x1b\xbf\xdf\xfb\xaf\xbd\xb9\xf9Y\xff\xed\xc351Nf\xe3\x00Q\xa7\xcc\x1e\xfb\xff\xc0\xb1\xf9\xf7\xe9\xe0\xd6|\x8b\xbf\xfe~\xb7q\xf0\xddf\xde\xdb\x12\xf3\xe2E87\xef6No\xb5q^]\\\xde\xee\xa7\xfb\xd9\xe5\x95q\xfdju\xf3\xf5\xcbM4\x0f\xb2\xb2eT\xf0\xb9\xb8\xf9\xf81z;\x0fn\xf4\xaf_\xac\xfbo\x9f\xf0\xd5o\x1f\xdef\xfc\xd7\xf6\x17\xfc\xf0\xfe\xe1z7\xbb\xbf\xd6\xdf\x7ft\x1eo\xee\xddW\xb3\xc7\xab\xdd\xdd\xc7o\xaff\x0fo/\xef>^\xeb\xb3\xcb\xe5nv9\xdd\xcf>:;\x82\xdf\xd5\xbcwc\xcc\xbf|^\xbbW\x0d\xbfoo(~z+\xbf|\xee\xac\xe7\x13\xec\xf8\xb8\xf7\xed\xcb\xdd\x1b\xc7\x1f\xa7\xd7\xbf\x16\xba|\xef\x8b\xe7\x85\xdb\xfb\xab\xfd\xec\xfe\xd6\xbay\xbc\xea\xdd\xe8\xd7\x8f\xf9\xbc\xf0p\xbd\xbf}\xb8y=\xbb\xbf\xdd\xbe\xbf\xbc\xda\xce.\xafw7\x8fW^\xc3O\xde\xfa7\x97\xa3\xf0\x1f\x97\xe3_\x7f{\xfc\xf4\xb2\x8d\xa6\xfd\xef\xe2\xe5v:\xbd{5\x9d^O\xa7\xcb\xcb\xe9\x87\xeb\xe9tuu1\xdd]]\xbc\x1c\xddN\xbfd\xe3\xe6\xed\x14\xf8\xdf\xd7\x8b\xe9\xed\x15\xf0\xfc\xfa\xeajzu1\x9d\xce.\x98\x82\x8b\xe9\xe5\xd5\xab\xa9~u7\x9d^]^\xf0<\xef\xae?\xbe\xbe\xf8\xf4\xe5\xea\xc3\xf5\xe6\xa5=\x9dn/\xa7\xb7\xd3WW\xb7\xb3\xbb\xe9\xe5h\x1a\xbe\x0f>~6n?^\x0e\xdf\xbeMV\xbf\x99\x9b\x0f3\xf3\xb7\x97/\xbf)\xcd/\xc6@m\x829*\xbe\xcf\xe6\xd7W\xb7\x0f_\x96\xbd\xe9\xff\xc6\xf7\xff\x7f\x1d\xdf\xab\xce\x01t\x1c\x9e\x8d\xad\x8asV\xcfH\xc9y\xab\x8c!U\xe7\xad\xc7\xcf\xbf\xe2\xed\xb7\x0f\xe3\x0f\xdf~\xbf\xd9\xb8\xbf\xbf\xbd\xcf|\xe9\x9b7{\xb6\xf8Y%\xae\xbfy\xfcj\xce\x1e\xde^\x15I\x97\x99!\x1f\xbf\xdb\xd7\x1d\x0d\xbf\xaf\xad\xfc\x9e-\xbeoOn\x1c\x15\xdf\xdf]\xb6\xf2\xfbN\xf1=\x1a\xbc5\x1f\xb2\x11\xe2\x91M\x96\xe8\x9f.\x93\xd9vv\xff\xe1.\xfc\xfa\x9b\xf5\xe6\xbf\xfb\x1f~\xbb\x99\xdf\xdd\x7f\x9e]\xdd\x1a\x8bWw\x97\xcb\x9f\xbd\xe0\xe5\xe0\xe7\xb7\xc6\xf4\xed\xa7]\xb2\x9c^\xbd\x99NM\xe3b\xfav\xf6A\x7f\xf3\xb5\x18\xcf?|\xfa\xfc\xfe\xee\x1f\xd6\xab\xaf\xd7\xd7\x92\x04J\xb3\x15C\x1f\x8e\xa1\x7f\x03\x8e\xcf\xccCwO=\xe0N\"\xb8\xf4A\x04\xd7\xa3\xcf\xcd\xb8\x98\xfe\x95\xdeZ\xae6\xe6\xe8\x87\xfc\x01\x9dE\x18\xfb\xf4F\xacA\xff\xda\xa3\x7f5\xe9_\xfb\xf4\xaf\x16\xfd\xeb\x80\xfe\x95?\x0b\xb4J}\xba\x15\xf9Nu\xb1\x89\x83|\xdb\xc3\xff\x12\x95\x96\xdbT\xa2\xe2\xc8N\x92m\x18\xbbB@\x8a\xc4\xbcS\xb4K\x85\x85\xeb\x98!,\xb64\xe9G\x1e\xbd\xc7c{\xf4.UH7\x9a>'\x101\xe7\x94\xca\xf3Q\xd4\xb3|\xd7\x93~BKPmK\xd2\x0fW\xf4\xaf\xb4-\xd6\xf8\x94\x0dH\xba7\xd8I\x84\x9cT\xcb\xf7\xd8\x0e\xe2\xf3%b\"M3\x06\xbbq\xb5\x9b\\\x9d0\xb2\x06\xdd\x9e\xf5BF5\xde\x19\x03\x96\xca\x18\x0e\xbb\xc3\xa1\x94\xac\xbf3Y\xaa\xa1\xbc\"s\xd7\xe7\xea1\xcd\xaeiJ\xa9\x06<\xd5`\xd0\x1d\xb4\xc8\xc6\xb7\xc8\xd2\xa5$\xa3\x9d\xc5U\xd3\xeb\xca\x1bd\xedF\\5\x03y5C\xbe\x9a\xa1\xd1\xed\xf7Z\xea\x19r\xf5\xf4\xe5\xf5\x18;\x83#a\xcf,2$\xc5\xc9\xb5C\xedq\xf6< \xf1:E\x934\x8c\xce\xf5I\\zd\xc9M\x9f`\xb4\xc8~'\xce\x0eT\xe7k\xb2\x9f\x1f5/p\xd1.\xfb\xe5\xdf\xff\xe5#\xd7\xb3;\x89\x13#\x14t\xec\xc0\xed\xfc\xe8{Ay\xea\xc0\xd4\x91\xff\xd3A,W\x90<\xa17d\xd4'u\x08\x80P\xadO\x00\x84\xed\xdd\x02\xaaM\xa9g\x00\x84*\x9d\x03\xaa\xaf\xbd\x7f@\x95)t\x11\xa8\xb2\xf6^\x02\xe9Q\xa5\xa3@\xb5\xb5\xf7\x15\x88J\xa9\xbb\xe4\x84\xcf\xdfc\x14\xbaL\xf9\xb0>\xbd3h\xe9G\xfeS\xba\x91\x7fb/\xe2\xe8\x14;\x11G\xa7\xd0\x87\xf8\xba\xd4\xba\x10G\xa7\xd4\x83\xf8\xda\x14:\x10_\x95J\xff\xe1\xabR\xe8>\xbc\x06\x95z\x0f_\x97B\xe7\xe1\x89\xd4\xfa\x8e\xff\xe7w\x9d\xb6^\x82\x9f\xd2K\xf0\x89\xbd\x84\xa3S\xec%\x1c\x9dB/\xe1\xebR\xeb%\x1c\x9dR/\xe1kS\xe8%|U*\xbd\x84\xafJ\xa1\x97\xf0\x1aT\xea%|]\n\xbd\x84'R\xeb%\xf8\xbb\xf4\x12\xb2^\xcf_\x1e\xe8c\xa0\xb4XN\xb8A1y\xce>?W\x9d?\xfd\xbf\x9e\x1f\x85qj\x07)K\x12\xa4\xb6\x17\x00D\xf9s\x82\xac}\xa6;\xf0\xc2d\xd3\xee)\xf2\xc0t\xacH\n2)\xcc\xbe\x85\xa0\xfeirBd\xc7\x89)\x94\x08\x9f&\x11D\xc6IDQ\xce\x97\x9a\x83\x82\x94v\x9d\"\x19t\x1e\x84\xe5O\x13\xa2\xac\xf6sn\x90\x98/\xb54\x8c\x8e\xe6\x93\x86\x11\xc7'\xef4Gs\xe2;\xc5\xbc\xea\xc7G\xf3*\xc88nY\xe7=\x9a\xd7\xf1\x8b\xab\xda*L_P\xaaN`\x98SX ms\n3\x89yNa'\xb1\xd0)\xec\xda\x82\x12\xd5\x11\xa51\xdd\xf1N'\xb2\xdc\xf1\x9c\xc4\x86;\x9e\x97\xccn\xc7s\x93\x99\xedxnmV\x93\x1a\x08\x1f]\x9d\xc8@\xc7s\x12\x1b\xe8x^2\x03\x1d\xcfMf\xa0\xe3\xb91QL\xb7<\xfe\xce\x1f\x83\x07a\x1aqL\x1389O\x94\xc2\xe4zMt\xfc\x18\\\xf1\x08\x92\x13\x84\x05\xa9\x14\xe4%\xe9\xda|[uD\xaa\x98\xfb\xa7\xb4\x03 Ri\x86\xaf\xdc\n\x89\xc0\xf8\x14\x81\x01\"\x15\x811)0\xed\xfb6}\xcf-g9)\x1f\x95\xd18s\xbb\xa7;O+\x9alt\x00\xe8\xb2\xc7\"\xda\xfa^]1\x1e\x00\xd4E\x81\x88~N\xdf_\x86\x18\x94%\"\x0e\xb8\xe2\x90wz\x80>\x7f.\xa2\x0e\x80{\x81\x94\xba\x8e\xef\x8bs;\x9f\xd2\x8f7\x03Av\x8a%\x08\xf2S\x8dA\xb08\xdd\x1e\x04\x93\xd3L\xc2\xa9\x0f\xb2\x8a\x82Y\x14\x86\x9b\xb9\x9d\xcd\xe3'\x98\xca\x7f\x92\xa5\xfc'\x1b\xca\x7f\x06;\xf9O4\x93\xffT+\xc1\x06\xc1'\x19\x04?\xc9 \xf8\xc9\x06\xc1\xcf`\x90'\x0ee\xac\xe6@\x83\xd04Zq\xd5\xaf\xa2\x13\xbc\xe3 \xc3\x05\xc8\x8eA\xb0a\x18\x1c\xd8\xb5\xe3\x07m\x19\xdb{\x06k\x9a&\x87\xf5=\x17\x82Z\x96\xc5A\x01\xd8p8\xe4`\x89\x877\xcd\x85\xef\x128\x1e\x8f9 .\x8c\x0d\xc1m\xdb\xe6%\x0d\xc3\x00\x92\xc1q\x1c\x01k\x00\x8c\x10\x82u\x9b\xdf\xd2d\xc0\x8b~\xf6\x87\xc3\x83P\xf6&g\x85\xd3\xc6:\x0d]%\xd8\xfeQ?\xd3_\x9ce\xb1\xf8Yw\xfc\x93\x80p\xd4B8\x12\x11\x0e[\x08\x87\"\xc2A\x0b\xe1@Dh\xb5\x10Z\"\xc2~\x0ba_Dh\xb6\x10\x9a\"\xc2^\x0baODh\xb4\x10\x1a\"B\xdd\x92\x13\xeaB\xed\xe8\xbd6\xd2\x9e\x98\xd6h%6 \xea|\x8c\xe1\x9c6^\xces\xda3\x1dt\xd8\x82\x88uX\x92\x08p\xd6\x82\x88uV\x92\x08p\xd4\x82\x88uT\x92\x08p\xd2\x82\x88uR\x92H\xa8\x08\xd6AI\"\xc09\x0b\"\xd69I\"\xc01\x0b\"\xd61I\"\xc0)\x0b\"\xd6)I\"\xc0!\x0b\"\xd6!I\"\xc8\x19K*\xd6\x9f(2\xb1+\xf1\x8eH\x11\x82N\x98O`1r\xd9\xc1{\xa8\xf7u~\x9c\xe5\x81\x8bE\xdf0\x07\x82Y\x01\x82\x0f{\x16?\x89\x84\xb1\x1d,\xf9\x81~`\x02\xf3\xf32\xc4<\xd7\xf9\x10@\xee\x11\xc6\xe1\x96\xc6\xf2\xaf\x0e\xa8\xa5\x85\xe0\x7f]\xcc\x17\x86\xcdO\xa8\xd1:\x8e0+\xb0\x85z\x8e\xcdO\xe6\x05w\x90\xc2\xee\x0f\xccE\x0f6J\xe4\x05l\x04\xe2Z\xba>\xe2\xad\xb2\nS\x08\x9d\x99f\xce\xcf\xa9 r\xa4\x0b\xa7v\x10o\x9b.\x1f\x8e\x94\xc1\x10B\x01\x837\xcc\xe1\xd0\xe2\x9b B\xc7\xf6x\xc8\x0b]E\x19<\xc1\x18\xa1\xb9\xc3\xeb$\xb07l@\xa2\xeb\xc6\xbc\xcf\xb3\xce\xa5\x9e\xe35k\x1b]\xef\xf7\xc7|\x08\x03 Mk\x88\\\x91W\x01\xf8\xf1\xc0q\x80 &\xc7\xa3\x04$q\\\x04\x91l\xedd\x85\\\x88`1X,\x16\xbc\xf4%\x01\xa4H4Z\xb8\x0b\xde{K\n\xb8s,\x16\x0e\x9a\x8bH\xa0\xde\xef.\\\xbe\x15d:\x91\"\x10f\x88\xe6\x9aV\xbe\xea\x84&\x80\xde\x7f\xd2\x9d\xc7\xf5\xd0\x1d\xdb\xae\xb7N\xce\xd9\xa1\"6\x18@\xd7\xe8Y1b\xd3\xadq\x8f\x85\x81(\x93EA\xa0>\x032\x00\x8cf\xe8\xac\xe4@R9\xd6\"\x0fc\x067\x1e\x8f\xc7\xc0\xea\xaf\xdew+\xc0y\x92<[iUz!\xd7\x90\xc5:P\xa41\xad\xd8U,\xe0UV\x1bbU\x96\xb5q+\xf7\x16[\xe4\x82*\xe2y\x15\xdb\x81\xa2\x96\xc8\x05kO\xb6\x1cX\xe7\"\xd3Q\"\xff\xe21\"\x17\x03\x90\xb0\x97\x01@\xd0\xd1x\x9c\xc8\xd7\x00\xa4\xc8\xddx\xa8\xdc\xe3\x98\x8c\xdfS\x9c\x8eO\xdd=\xd9\xefT\xa4Sw=\x86\xdb1\xde\xa7\xe0~*\xb9\xbeX'\x12oB\x97d!B\x8f\xe4\x80\x02\x87\xe4p\xb0?\xb20\xa1;r@\xa17\xb2\xc8\x16g|\xb6\x01\x90\xcbN>\xdd\x15\xdbe;\xc2\x13\xfd\xef\xe3\x88\x02\x9fc'!\xc0\xe7X\x88\xd0\xe78\xa0\xc0\xe78\x1c\xecs,L\xe8s\x1cP\xe8s\xc7M\xb9,\xbc6oc \xa2\xa0<\x9e\x06\xfb\x1c\x9b\x80}\xba\xcf\xe1\xe7\xf49|\xb2\xcf\xd1\xfc4\xadx d\xc5\xaeH\xf5\x02/\xe5-\x82\xf8,\xe4d\xa0\xf93\x0eZ\xdeF&\x91\xc0&f\xb6\x84\x08\x03D\xe3\xf2w\xd4\xb5\x0f\xd1\x07\xb8!\xdcn\x8f\xb4-\xd8\x92a\xb5\xc8(\x1cDd\x17\x1e\x08\x9b\x86\xc7\x81\xd6\xe1`\xa0\x818\x14l#&\xee\x15\x9a\x89\xdb\xbe\x17Z\x8a\x0f\xf5\x85\xc6b\xf7\xe2\xebm\xc0v\x83\xa9\x0cl[\"\x1a\x15\x1a\xd1W\xb4!\x8b\x13\x98\x90\x85\xc1\x16\xf4U\x0c\xe8+\xd9\xcfW3\x9f\xafj=68\x16\x1b\xcf?\xc1v\x023\xe1V3aE3\xb18\x81\x99X\x18l&\xacb&\xacd&\xacf&\xacj&6\x9e\x14\x9b \xc3f\xa2\x80\xc9\xcav\xc3\xadf\xd0\xd7\xba\xf3\x87\xe7zG\xef\xf4\xa3]\xa7\x17\xed:\xf4\xa6\xcbD \x05\xd6\xd4\x13\xd54R\xaa F\x815\x99PM\xbd\x92\xbe\xbd]r$Xc_Vc&\xb9\xaeP\x1f\x84\x03k\xb3\xa0\xda\xfa\xa5\xc4m\xb5\xc9p\n\x83\xf0\x01t\xa2lT\xff\xd3\xfcHR\xd9\xf3\xbb\x92\xa0\xb2\xef\xebM-\x95\xb6\x99\xf8x\x87\x12T\xf8,>\xa5\xe0T\n3{\xedi\xfe\x9f\xe8h\xc2\xba\xbe\x83\x9f\x81u}g7\x93\xd6\xd9f\xf4\x13\xbc\x0c\xac\xefOp2\x99?\xe1?\xd1\x9f\x84u}\x07\x7f\x02\xeb\xfa\xce\xfe$\xad\xb3\xcd\xbe'\xf8\x13X\xdf\xf3\xf8\x13Ua\x14\xa3\xfa\x0b\x1e\xda.\xff\xb4E\xfdq.m_~\x08\xa8\xf9\\W\xe2\xc4!\xa6?%\xd2\xcdb@=\xff\xe6\x11\x13\xb0\x15Q\x9f~\x80S\x89E\xa4\xa7W\x9fRb\x8a\xf3\xf0N?\x14\xe9I\xbe>#\xaf\x8f\x0fa\x8b*\x8d\xb2J \xc4-j5\xaaZyD^\xb1QT\xcc\x97fu\xf7\xf2\xba\xf9\xc8\xb8\xa8\xbbW\xd6\x0dD\xceE\xdd\xbd\xaan\x1e\x91\xd7\xdd+\xea\xe6K\xb3\xba\xcb\x86k\xa2\x96\xd7M\x07\x10e\xfdM\xe3\x01L.A\xd5|\xa0<\x97\xa1P\x80&\xd2@\xad\x02\x00Q\xc9P+\x01\xc0\x142\x94j\x00\xca\xab{\xd4\x9a\xb6\xf00>HoS+\xcc\xd0\x07\xde\x99\xb3\x98\x01\xf0\xe7\xc2'\xb3B\xc8-Ko\xcf\x8a\xa5\x0e_\xa4 \x9f\xcf\x1d\xbb\xaa[\xe4\x99u\xf5B\xe7o$\x10\xfb?!\x84\xc0\xc9+9D^Z\xcb!\xec\x08\x8d\x1c\xe2\xbe@\xc8!r\xf8J\x10\x89\xcf75\xc9\xdc\x9e\xa8K\xec\xf9u\xb3\x84\xce_\xcb#\xf6\x7fB\x1eI\x17 \xe5\x11\xf6\x82F\x9e\xb6\x8eP;\xad\xb0/(t\x06\x85p\xb5\xe8!\xbe\xa4\x83\xf8\xd2\xfe\xe1\xb7t\x0f_\xda;|y\xe7\xf0\xdb\xfa\x86\xdf\xde5\xfc\xb6\x9e\xe1\xcb;\x86\xdf\xd6/\xfc\xf6n\xe1\xb7\xf6\n\xbf\xb5S\xf8*}\xc2W\xe8\x12~[\x8f\xf0[;\x84\xaf\xd2\x1f|\x85\xee\xe0\xab\xf6\x06\xffI\x9dA\xe8\xf7X\xe2\xf7X\xea\xf7\xb8\xc5\xef\xb1\xd4\xef\xb1\xdc\xefq\x9b\xdf\xe3v\xbf\xc7m~\x8f\xe5~\x8f\xdb\xfc\x1e\xb7\xfb=n\xf5{\xdc\xea\xf7X\xc5\xef\xb1\x82\xdf\xe36\xbf\xc7\xad~\x8fU\xfc\x1e+\xf8=V\xf5\xfb\xb6\x80\x88&v\x16\xe7\xf6\x82}5j\xf6t\x8e\x16a\x8c\x0e\xe5\xc7{\xcf\xff\xd2\xf9\x0b\xfd\xe5A\x98\xcd\xc1\xc1\xc8\x8e\xcf\xe7a\xbab\x01\x87\xbf=\x86\x99o1\xcfqI\x92I\xc7\x14U\xdc\xf2\x960esqMAYt\xd2N\xb9\x93O\xa3b\x91\x9aRP\xaa\xa6\x18\x12\xac)U\xd8 V\x9d\x8e\x9dl\xa8\x93\x08\xecK\xe5\xf5e\xe2\xfa\xea\xd2\xc2\x82\xc9\x8c[\x17\xc2\x82a\x99`\x98\x12\x8c*u\x03\xd9\xe7\xfc<\xe6S\x81L\xf1\\\xf2A\xc2\xae\xeb\xcd\xdb?4\xd8u\xbd\x94E\x01\xfd\xc5m@`\xa9C\x17k\x0eb\x17\xddn\xaa\xc5\xe1\x96\x81\xc5\xe1\x16Bi\xcb8\\G<\xb6x\xceQ8!^\xfb\x01+A\xfeP\x80\x05+ \x8b8:m\xe1\xed\x90{(\x90\xd8\xde\x87\xeb\xf4<\x7fD\xbc\xfeJ\xa1\x7f\x1c\x18\xdbg=Lf~\xb2\x1c\xf6\x00\x12\x01;\x01\xcfC\xe0\x07\x00\x1046\x89\x83\xbd\x81C\x08\x1d\x82GJ}\x02\x84K\xdd\x02\x10\xa5\xdd3DDR\xe7\xc8\xd73R\xffPp\x10\x85\x01\xd4\xcd\x06:\xa9\xd3\xf8m>\xe3\xb7\xb9\x0c\xcbA\xe41\x1c\x0ev\x18\xbf\xcd_|Uwa\x81ro\x01\xd0rg\xe1\xe4P\xf0\x15\x98F\xee*\xfe\x93<\x05v\n,w\n\xdc\xe6\x14\xb8\xcd)X\x0e\"\xa7\xe0p\xb0S\xe06\xa7\xc0\xaaN\xc1\x02\xe5N\x01\xa0\xe5N\xc1\xc9\xa1\xe0\x140\x8d\xdc)p\x9bSPt\x0b\x8cvu%D\xee\xbd\x0e{5?\xd12\x10\xf9,\xfb\x9dfS\x9a\x08\xe4V\x99\x99aJ\x90\x90E\xc4c^R\xcd^\xa7!\xb5E\x90==7&\x95\x94\xe7F\xc7\xe8\xe4\xd9|\xfa\xb7\xc6\xeb\xf5\xfc\xe7\xea\x85\xa9@\x15\xf9\xe1S\xae\n\xbd\xa9\"\x7f\xe7A\xfd\x13\xc0\xa1\x8c$H\x1ea\xece\xeb\x89\xea\x0b\xe3\x13\xb2\xcc\xf5\xe2\xe2\x95\xff\xe5\x17\xcb\xeb\x9a\x88\x92\x82\xe5\x04|\nH\x90\xc5H@\xf5\xab0\xf6\x1e\xc3 =A\x808\xdc\xb2\xb5s\xfd#/\xdf\xc6vt\xa8\x19d\xbf\x9dg\xffL\xe8_A\xbd\x03\xa4\xc5\xc3 \xfb@P\xaf\x16\xa3\x0d\x8a\x13\x04\xd4_\x15M\xe0\xc7B+6,\x8f\xb6fU\xa3\xd0\x9c\xb4L\xa2R\xd8\xbc2\xb9Z\xcd,\x91\x8c`\x0d\xd8\x1b\x96\xc9K\x91\x9fhIj\xc7)%N\xf1\x19\xfd\xfcyS\x15\xf90\xff9\xff\xbcy\x92\x8f)\x05\x0f\x889\n\\\x805\n\\\x96q\xf6\x88c\x8b\x02\x17bZ\xbe\xe8\x93\xe7[\x14\xb0\xac\xcb\xa7$\xf7\xe2\x11\xc4{n'(\x1b\xc8\x00\xeeU\x11\xcb\xbf~N\xd6P=\x845\x1e\xa3\xd4Y\x81:\xcfKx\xad\x17\x8f\xc9\n\xcag4\xff\x04\xe1Ee\xd0\x8aE\x06\x07\xac\x97A\x85\xc6\xcb\xf9\xe4\xb6\x03\xb84\xa6jxp\x96\xca9T\x86\x02\x98PF\xc9\xf9@6\xc9\xb94&\x01\xf80\xca\xcf9\xc1\xba/uS\xaa\x1e\xd4\x0e\xa9\xe5\x9c\x13\xa8\xe4\xfbu\x92z\x8b=\xd0q\"\xdby`\xfb\x0d\xf1\xac\"\xac\xb2T\"\xedW8\xb6\xf3\xe4\xac\xa8\xbeS?\x01YsF\xa9Q|\x07\xca9\xb1\xfd\x87|\xc8\xd6\x00\x99\xab\xc2\xccQ\xbaE(\xe0+(\x01L\x0d\xd5S\xb6\x8a$\xb2\x1dT1\x83k\xb2\xf3\xd74\x1eh~\xae\x97\xa4\xb17_\xa7H\xc0\xb2\xa0\xa29\x96\x08\xb6\xf7\xe4A\x0da\xc3\xc29\xda,X1\xa3\xbaP\xc3\xaa\xe9Ar{Ul\xd8~\xd4p\xa2\xba\x91\xcc4\x15\xab\xda4<\xaf\xca\x0c43\x89\x11*\x9e\xac\x11\x1a\x96\x84% \xaer;0=\x95\xb4\x04\xd9Qk\x96P_-\x0e\xdf\xea\xccl\xebz\x81\x8d\x8bh\x9c\x88A\xb5\x1c|\xaeO\xca\xffB\x9c\x0c \xa7\x1e\xcb\xc9(9\x19\x10\xa7\x9e\x84\x93\xc9r\xea\x95\x9cz\x10'S\xc2\xa9\xcfr2KN&\xc4\xa9/\xe1d\xb1\x9c\xfa%\xa7>\xc4\xc9\x92p\x1a\xb0\x9c\xac\x92\x93\x05q\x1aH8\x0dYN\x83\x92\xd3\x00\xe24\x94p\x1a\xb1\x9c\x86%\xa7!\xc4i$\xe14f9\x8dJN#\x88\x13\xb6\x93T\xe6\x9cz\xf6?\x96\xe38\xfb\xdf\x84\xf8\x19\x085\x97Y\xd4\xa7\xcb\xd6C\xe5\xbbm7\xe8\\\x9f\xd4$\xe0\xca*\xe7e\xc8\x96o\x0d/\x83\xe0e\x00\xbc\x92U\xec\x05\x0f\x99d\x15i\x80\x966)F\x81\x00\x05)\x89\x0d\x80\xd8\xa0\x88\x0d\x85\\\xdb\x81\xe7O\xe4\xfd\x88\xc6\x9e\xbe\xa4\x86\x18>\xf7\xaaZc\x0e\x0c/\xbe\xcb\xc2\x1a\xac\xe5\xf8\xb55\xcbFmA\xf6\x9c\xcbk\x81\x04\xadK\xafgZa\xe7\xd5W<\x8e^d\xf3\xd4\xa7\xad\xb3a)\x9e\xba\xd4>\xcd\xb8\x7f\xcaj\xfbT\xab\x7f\xbf\x057+\xd1\xf3\xae\xb9a\xee\xcf\xb2\xec\x86Y?\xe3\xca\x1b\xae\xe0\xb9\x17\xdf\"\xfd?\xd7\xfa\x9b\xeabOY\x82\x8b\x18\x1d\xbb\n\x17\xf19a!.bu\xdaZ\\\xac\xa9\x13\x96\xe3\xacY\x9f\x7fE\x0e\xd6\xf0|\x8br\x90\xfd3\xaf\xcb\xc1:\xbe\xd3\xd2\x9c\xb2\xee3\xad\xce)\x9eO^\xa0\x0b\xb8\x9d\xb6F\x170;u\x99.`\xf7\xc4\x95\xba\x80\xeb\xd3\x17\xebB\xc3\x1c\xbb^\xe7\xe7\xeb',\xd9\xe5\xcc\x8e\\\xb5\xcb\x99\x1d\xb9p\x973;r\xed.gv\xe4\xf2]\xce\xec\xc8\x15\xbc\x9c\xd9\x91\x8bx9\xb3#\xd7\xf1rf\xc7/\xe5[\xfc\xf6\x89\xaby\x96\xfb\xe2i\x0bz\x90\xddS\xd6\xf4T\xf7?aY\x0f\xd3\xb3+{\x85\xa5\xbd\xc21\x9a\x9c\xa7\xff\xcc\xcb}\x9e\xdf\xb3\xaf\xf6\xfd?c\xb1\x0fTr\xc2Z\xdf?a5\xf8\xacK}P\x80\xd65\xdfs\xad\xf4\xfd\xa7,\xf4Y\xe2\x13\xd7\xf9\x90\x0cO^\xe6\x9fb\xd7?g\x95\x7f\x9a\xc1\xbf\xe3\"\xdf\xff\x9ek|\x88\xf9\xf3,\xf1!\xce\xcf\xb9\xc2\x87\xf8?\xfb\x02\x1f\xd6\xfd\xb3\xad\xef\xfdgZ\xde\xc3|\x8e^\xdd\xc3lNY\xdc\xc3\x9cN\\\xdb\x8b\xb4t\xca\xd2\xde\xff\xde+{\xa0\x82g\\\xd8\x03\xdc\x9f{]\x0fT\xf1\xbd\x96\xf5\xfe\xf3\xaf\xea\xfd\xe7\\\xd4\x83\xccN\\\xd3\x83\xbcN^\xd2\x83\xdc\x9e\xba\xa2\x07\x99>\xc3\x82^`\x93\xa3\xd7\xf3\xec\xcc\xfc\x94\xe5\xbc\x8c\xd7\xb1\xaby\x19\xafc\x17\xf32^\xc7\xae\xe5e\xbc\x8e]\xca\xcbx\x1d\xbb\x92\x97\xf1:v!/\xe3u\xec:^\xc6\xeb\x84e\xbc\xd4]\x9f\xba\x8a\x97\xae\xae\x8e^\xc4K\x17\x84'\xac\xe1\xfd\xa7-\xe1!\xf2\xe3V\xf0\xa2\xc5:~\xe6\xc5:\xcf\xef\xd9\x17\xeb\xf8\xcfX\xac\x03\x95\x9c\xb0X\xc7',\xea\x9eu\xb1\x0e\n\xd0\xbav{\xae\xc5:~\xcab\x9d%>q\xb1\x0e\xc9\xf0\xe4\xc5\xfa)v\xfds\x16\xeb\xa7\x19\xfc;.\xd6\xf1\xf7\\\xacC\xcc\x9fg\xb1\x0eq~\xce\xc5:\xc4\xff\xd9\x17\xeb\xb0\xee\x9fm\xb1\x8e\x9fi\xb1\x0e\xf39z\xb1\x0e\xb39e\xb1\x0es:q\xb1.\xd2\xd2)\x8bu\xfc\xbd\x17\xeb@\x05\xcf\xb8X\x07\xb8?\xf7b\x1d\xa8\xe2{-\xd6\xf1\xf3/\xd6\xf1s.\xd6Af'.\xd6A^'/\xd6AnO]\xac\x83L\x9fa\xb1.\xb0\xc9\xd1\x8buvf~\xcab]\xc6\xeb\xd8\xc5\xba\x8c\xd7\xb1\x8bu\x19\xafc\x17\xeb2^\xc7.\xd6e\xbc\x8e]\xac\xcbx\x1d\xbbX\x97\xf1:v\xb1.\xe3u\xc2b]\xea\xaeO]\xacKWWG/\xd6\xa5\x0b\xc2\x13\x16\xeb\xf8i\x8bu\x88\x9c[\xac3\xf4\x87\x05\x0e\xed4\x7fG\xce\xe4\x0fz-\xcc@\xe3\x12\x9a\xbf1\xa7\x05\x1b\x94\xd8\x93\xde\x82\xb4\xc8\xdf\x82\xa4.W\x83V\x12\xad\x81+\xbcYH\xfd\xfc\x81\xe6\x1f#\xb2\x7f\x94\xc4\xbe\xba\xc0\xb0l\xc7\x98\xb9\x06\xab\xc9\x86)\xd9\xa8\xd2\xc4\x0e\x12-A\xb1\xb78,\xc2 \xd5\x16\xb6\xef\xe1\xfd\xb9fG\x11FZ\xb2OR\xe4\x9f]`/x\x98\xd9\xce\x87\xfc\xd7\xd7a\x90\x9e\xd9\x1b\x14xq'@\xbb\xea\xe7\xb3\x15\xc2\x1b\x94-r\x9b\x9f:\x01Z\xa3\xb3\xf5|\x1d\xa4\xeb\xb38\x9c\x87ix\x16d\xff$h\x19\xa2\xce\xda;\xb3c\xcf\xc6g\x8d\x14\x8ct\x9c`K\x14\xc6K\xcf>\x83\xc0\xb9t\x9a\xa0E\xc2*J*\x9e\x80\xc7:\xa1\x8b\xa8\xf7\xa0e\x0f(\xa2Wa\x90\x84\xd8N\xce\xfc0\xb0\x9d0\xfbO\x98G\x13,\xa3u\xec\xa1\x98!\xcd\x9fun2\x95\x96\x00\x11}\xad`\x8a\x03\xa3\xf6\xc6\x1e\xa2\xb6\x17\x86\xa3x\x00v\x15R\xa7+\x84\xed\x84&/\x9e\x9dI\xccT\x16\xa9Z5\xf5|D\xd7\x91?\x81\xa0\xf3\xd0\x0d\x03\x8f\xc2^\xe4\x8f:\xb3\x8f\x10\xde\xb1\xb1\x97\xa4!m\x85\xe2\x99\x80bi\xc7\xb6\x1f\x06.-|\xf9\x10\x14\xc9N\x1eP\xbc\xf10\xa6\xfd\x84x\x0e\x91\x95\x8d(>\xa1\xe5\xa56\xf6\x98\x0f_/\x12\xad\xc8\xc3\x91\xc0\xe2\x89\xc2`I\x8f=\xf9;\xafT\xebc\xb0e\x95\nu*\x0c\xd0^6\x88\xaa\xca\xe1\x1f-\x06X#V\xaf\x11\xd25\x8d%M\xb2-r\xc8}\xee\x93\xefT1\xf7E\xf8\xc5\xd6\xa0\x00\x06\x0f\xe8Q\x80\x1e\x0f0)\x00\xf7y\xfa\xc5\xb6/\x17q\xb1\xb5(\x80\xc5\x03\x06\x14`\xc0\x03\x86m\xcd\x1cQ\x80\x11\x0f\x18S\x80\xb1~\xfc\x9b\xba\x19\x8f\x15Z\x84E@Fa1\x90]X\x0cd\x1a\x16\x03Y\xa7U\xe2E\xf1\xb9\xb36\x1b\xb1\x18\xc8L\nm\x1f\xb1\x18\xc8X,&\xb3\x97\x82\xc1\x14F\x05\xba\xbf\x8b\x8d\xe8\xb7\xb5\xc3` \xa0 \xfdv\x0b\xfa\xed\x06l\x11v\x91\x7f\xed\xac\xd5|~\xbb\xf5Z\x1b=b \xa0\xed\xfc#M'\xb6R\xdb\xe0\xc7\x00@+\xe1v+\xe1v+\xe1v+\xb5\x08\xbb\xc8?v\xd6j%\xdcn\xa5\xd6F\x8f\x18\x08h%\xcc[\x89\xc2xA\xb4N\xb5\x18%\xa8\xb9\xdfnG\x11\xb2c;p\x8a/qN4?|d\x1f2&Z\xa7i\x18\x14l\xce\xcfs\xfc\"t\xd6\x89\xe6\x05\x01\xfb\x16`\xa2F\x1eZ~\x86\xed\\\x9fD\xb6\xebz\xc1\x92]\x18\xaf\x8cC\xb9\xd1\xca\xbf>y\xd5\xab\xca\xf8\xd7\x19\xaf\xcc\xaa\xac\xcf\x97\xf5\xab\xb2\x11_f\xd5\xf5\x0d\xf8B\xadW\x17\xf7\xac\x17l\xa1\xa5W\x85\x16\xfb\xa9\xe5\x956\xac)\x87<\xa5\xa1\xd7\xa4\xfcg\x9a\xf3\xcd\xe6\x1cBl;\xf3\xb0\x0d-\xddf\xc5\x15\x93\xf2\x01\xc5\xa4\x84@1-#\x0b\xc8D\xdb@R\xb2\xc0U\xf1\xce\xb9\x12\x90\xfd\xcc\x96{\xc1\n\xc5^ZA\xca_\x15\xe6\x89\x03\xe39\xd9t#q\x1e\xa2\x18\xf2\x1f\xa2\x18r!\xa2\x18\xf2\"\xb2n\xd8\x91\xc8\xea!_\"\xcaAw\"\xcaa\x8f\"E\x10;U\x86j\xf7+JX\xd0\xb5(qA\xef\xa2\x04\x86\x1d\x8c\x16Y\xecc\xbc\xd0\xb0\x9b\x11\xfc$\x9eF\xa0*gS\xf06\x85\xa8d\x95E\x132\x0f\xf4\xa5\x0e\xe8K\xfd\xcf\x97\xba\x9f\xdf\xe6}\xbe\xdc\xf9|\xb9\xef\xf9-\xae\xe7\xabx\x9e\xaf\xe2x~\x9b\xdf\xf9mn\xe7\xb7z\x9d\xaf\xe6t\xac\xbc\x02\x9f\xf3U\\\xce?\xce\xe3`\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2R\xe7\xc2m\xce\x85\xe5\xce\x85\xe5\xce\x85[\x9c\x0b\xab8\x17Vq.\xdc\xe6\\\xb8\xcd\xb9p\xabsa5\xe7b\xe5\x158\x17Vq.\xcc9\x17\x05Lc\xdby@\xee\x01\xa34E\xb1\x96D\xb6\x93E^]\x83\xfb>E\x01\xd4\xd2\x8c\x19\x0b\xd7\xba\xba%\"\xf0\xd1\xd2\xe6\xd8\xf72x\xfb\xb8z\x009\xe6\xdf/:F\\\x80\xa2Mb\xa8\x92\\h\x05\xa9\x15f\x83\xba\xaac[\xc2\x11\xb46\x84\xafB\xa1\x1d\x12\x91\xf1\xb1\"s\x04\xad\"\xf3U\x14\"S\x14x\xa5%!\xf6\xdcC\xbe\x8f^u\x16\x0e\x93z)F4\xa6\xdb\xb38\x98\x13F{\x06e)\x98\xfa\x00\x8a\x94;O\xbbT\x1cL$\x18\x0f\xb4\x9e\xc9\x0fk\x89}%\x81}EyY\\\x9b\xb82\xc9\xb0\x92dXQ2\x16g\xb1^\xe5\x05\x0f\x87\x14\xedR\xcdEN\x18\xdb\xe5 Vv\xd1\x9b\xc1\xce\xb8'\xe7\xb6\x93z\x1b\x04\x14\xe4\xcb\\\xe0\xf9*\xdc\xb0k\xe4\xfc\xb9\x80\xff\xc6K\xbc\x145o\x1cMc;H\xbc\xea\\g\x18w\xba\x86\x95t\x90\x9d \xcd\x0b&\xd2R\xbe=\x85\x90\x87p\x9df*:7\xa2]\xc7\x0d\xd3\x14\xb9\x1dg\x1d\xc7(H_eLX\xba$=d\xff\x14Yn-\xddGP\x8e\xc0\xdf\x16\xab\xc1\xda\x15\x81\xd9zk\x90\xe5\\,\xe1o{D9\x1f\xc6\xf8[\x93(\xe7\x03\x19\x7f\xdb'\xca\xf9P\xc6\xdfZd\xfd|0\xe3o\x07\x04\xc0\x84$\x18\x92\x12@U\x8c\x08\xc0\x00\x92qL\x00\xc6\x90\x0c\xc5+\xd4\x1b\xd0I\x9b\xf1\x859\xf2\x85\x93\xdc\"\x0c\x042\n\x0d\x01\xedBC@\xd3\xd0\x10\xd0:\x8c,\xa0\x81h\x0cl#F\x1a\xd0L4\x06\xb6\x14\x8d\x11\x1b\x8b\xc6)\xec\xf6\xab\x8e\xdd\xa5\x15\xfdV#\xfa\xad6\xf4[M\xe8\xb7Z\xd0o5\xa0\xdfn?\xbf\xdd|~\xbb\xf5\xfcv\xe3\xf9j\xb6\xf3\x8f3\x9d\xd8J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xd5J\xb8\xddJ\xb8\xddJ\xb8\xddJ\xb8\xddJX\xcdJ\x98\xb3\x12\x05\xdb\x1a\x07\x91Z\xb7\xbd\x83H\x9f[\xf3 R\xe4\xb6\x7f\x10ipk\x1d\x84\xaa\xcb<\xa1*e=`\xab\xf5\xaa\xb2\x1ePVq\xe5\xd6\xd0[\xcd\xac\xe8L\x9e\xce\xac\xda`\x9a|Y\xd5\x08\xb3\xcf\x95\xf5+\x9e}\x9e\xa7U\x95q\x0b\xf6\xad6\xa8\xca\x06|\xd9\xb0*\x1b\x02eU\xfb\xb8U\xfeV\x1bUt#\x9en\\\x95\x8d\xf9\xb2,\xe0\x10\xf5\xb7\xad\x96\xae\xbc\xd8\xad\x95\xd35\xb3\xff\xf1\xa0mX\x00\x93\xaaY\x83\xee`0\x18\x0c9d\x9e\xc7.0\xf9b\xbc}\x80?0.\x9aM\x13b/mJ!GmJ!_mJ!w%\xea\x85=\x96\x00@NKH\x06\xf9-Q\x0c\xb9nS\x0cz/Q\x0c90Q\x0c\xf90\xa1\x16\xc8\x8d\x9bb\xd0\x93\x9bb\xd0\x99\x9bb\xd0\x9f\x89b\xc8\xa5 \x9b@^\xdd\x14\xc3\x8eM\xdaD\xe0\xdb\xa4\xeaZ\xdd\x9bh\xab\xcc\xc3\x1bX\xee\xe4\n^\xae\x10\xc6\xe4\x01\x8a\xc4\xf3}\x99\xe3\xfb2\xbf\xf7en\xef\xb7x\xbd/uz_\xea\xf3\xbe\xd4\xe5}\xa9\xc7\xfbR\x87\xf7\xa5\xfe\xeeK\xdd\xdd\x97z\xbb/uv_\xea\xeb\xbe\xd4\xd5}\xa9\xa7\xfbrG\xf7[\xfd\xdc?\xc2\xcd}%/\xf7\xd5\x9d\x1c\xf6g,\xf3g,\xf3g,\xf3g,\xf3g\xdc\xe2\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xea\xcfX\xee\xcf\xb8\xd5\x9f\xf1\x11\xfe\x8c\x95\xfc\x19S\xfeL!\xc2\x0d\x8a\x178\xdcj\x1b/\xf1\xe6\x18\x1d\xaa\x07\xe7\xe5\x03\x01|\xe5\xb9.\n\x1at\xf1\xbb\x00\x9c8q\x88q\x03.~\x17\x80\xf3H\xaa\x86\xf2;\x1b5p\xc7\xc9\xac\xedZ\xa4\xde\xb1rk;\xb9\xe4;Vvm'\x97~G\xcb\xaf\xedd-\xd8\xf3-\xd8\xb7\xb4`\xcf\xb5`/o\xc1\x9ek\xc1^\xde\x82=\xd3\x82\xfdi\x01-\xebXY\xe8p\x94oQ\x04\n\xeeE\xe1[=\x8cB\xab8\x19I\xa0\xecg\x0c\x91\x92\xab14\n\xde\xc6P\xa88\x1cE\xa2\xeas\x0c\x91\x92\xdb14\n\x9e\xc7P(\xcc\xc1\xaa\x81&\xe7\x92\xfe\x91\x1e\xe9\x1f\xe7\x90\xfe1\xfe\xe8\x1f\xe9\x8e\xfe \xde\xe8\x1f\xef\x8c\xfe\xb1\xbe\xe8\x1f\xed\x8a\xfe \x9e\xe8\x1f\xef\x88\xfe\xb1~\xe8\x1f\xe9\x86*\x1e\x87\x8f\xf48|\x9c\xc7\x1d3\xc7\x92`%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dki\x02%\x8f\xc3'x\x1c>\xde\xe3\x8e\x9dsi\x02 XKR;\xf5\x9cCq\x055\xcc\xdf\x8d\x91\xb2\xb7Ob\x84\xf3;\xa2\x0d\xaazB\xe3\xecy\x12\xe2uJ\xe0\xaa'4\xae\xf8\xa8~\x0d\xca\x7fU\x18\x8e\x0f\x80\xe0\xd9\xc8\xae$;\x05\x94\x8bOA%-\xa0pE#\x14Z\xa10\xa9\x94M\xf3\x15[\xe6+7\xccWk\x97\x7f\\\xb3\xc4-\xc0\x8a-\xc0\xca-\xc0j-\xc0\\\x0b\xe8N\x92'r\xc3\xc8v\xbct\xcf\xbdu@\x1b7e\xdd1[8\"\n\xd9\xbb\xe9\xda\x90(d/\xc1k\x03\xa2\x90\xbdm\xafYD!{\xad_\xeb\x13\x85\xec\xfb\x034\x93(d_T\xa0\xf5\x88B\xf6\x8d\x08\x9aA\x14rJ\xd0\xad\xa6P\xe7$\xd2{d1{0\"\xd4\x1a\xce\xccy\xfb8L\xed\x14i}\x8b>o\xb0\x08c\xff\xbc(\xfb\xb1o\xb9h\xf9\xd3D\xf0\x1cd7\xd6\xc5\xec\xc6:\xcc\xaex\x0e\xb23L\x89x\x86)\x90\xaf,\x809\x8e$\x12\x1a#\x81\x88e\x01\xc8\xb1\xd7\x93\xc8\xd8\xeb d,\x0b`\x8eC\x89\x8c\xbd\xa1@\xc6\xb2\x00\xe4h\x1a\x12\x19MC cY\xa00\x96\x1e`\xd7\xd2\x88\x0f\x1c<\x8fwI9\x9e\xe6`R\x96\xa7\xfa\x98\x9c\xe9\x89n&ez\xaa\xa7\xc9\x99\x9e\xe8lR\xa6\xad\xfe\xa6\xe0p\n\x93w\xe3\x85\xfes;\xa1\x84\xe1\x89>(\xe1x\xb2\x0b\xcax\x9e\xea\x81\x12\x9e';\xa0\x8c\xe7\xa9\xfe'\xe1\xf9D\xf7\x93z\x1a~nO\x930<\xd1\xd3$\x1cO\xf64\x19\xcfS=M\xc2\xf3dO\x93\xf1<\xd5\xd3$<\xdb=\x8db:\xc7\xb6\xf3\x90EP\xf9y\xce\xf3x9\xb7\x7f\xd4\xcf\xb2?\xdd\xf1O\x10t\x04AG t\x08A\x87 t\x00A\x07 \xd4\x82\xa0\x16\x08\xedC\xd0>\x085!\xa8 B{\x10\xb4\x07B\x0d\x08j\x80P\xdd\x02\xa0:\xdb\xae\xed\xca+\x02\xde\x02\xbbJp\x8e}qf\xe8\xfa\x0b\xded\x05|$\x82\xb3f+\xe0C\x11\x9c5]\x01\x1f\x88\xe0\xac\xf9\n\xb8%\x82\xc3M\xed\x8b\xe0\xac\x19\x0b\xb8)\x82\xb3\xa6,\xe0=\x11\x9c5g\x017Dp\xd0\xa4%\xf6\xaf:{\x93:@v\xacQ\x10\xc3`V`\xae\x1d?h\xcb\xd8\xdeW\x08\xd3dVw\xbe\xe7R\x00\xcbb\x96ad\xe1p\xc8\xacG\x13\x0foP\\\x15s\xefB\xc3\xf95\x0b\x1ad\xdb6#A\x18\x06\x94\x08\x8e\xe3@lH\x08B\x08\xd0E\xae\xdd\n\xb2\xe8g\x7f\x00\xf5\xd7\x80\xc5\x02PV\x8c\xdc\xba\x92\xa1\xde\xd7\x19\x0cQ\xbcX\xf4\x0ds\x00IJ\x81\x86=\x8biN\x18\xdb\xc1\x92\x10c\xc0]\xe9_\x86\x98\xe00\xe7\xae\xd9\xef\x11\xc6\xe1\xb6Dd`H\n\n\xf4\xd7\xc5|a\xd8\x8cy\xa2u\x1c\xe1Z\x10\x0b\xf5\x1c\x9b\xbd\x9c\x90s\xa2qv\x7f`.z\x80\xea\"/\xa8=\xd1\xb5t}\xc4\xe8n\x15\xa6\x14&S\xe0\x9c\xb1\x10]>\xd2aW\xa0Q\xb6\xe9\x0eA\xb7G(\xa8{\x869\x1cZ=\xd6\xb3I\xc0\xd8\x1e\x0f\xfb\xb0\xdf\x11\xb01Bs\x87iW`o\xf6M'5\xe6\xfd> \xcd\x1c\xafQ\x03\xea\xf7\xc7\xec\xcb\n\x88r\xd3\x1a\"\x17\xb4)\x89\x1a\x0f\x1c\x87u\xe1\x1c\x85\x12\x1a\xe8\xb8\x88\x03n\xedd\x85\\\n\xb6\x18,\x16\x0b\x04\xc2(\x15\xa0\xd1\xc2]X \x8eq\xb9\xc5\xc2As\x10H\xf5\x10w\xe1ro'\xc3a\\_\xb1/\x80\xd5-AZkK\xad\x8e<\xe6\xb6\xf3\xb0,\xde\x91ZPH\x83\x90\x8ap\xd4B\xc8\x85$\x15\xe1\xb0\x85\x90\x0bP*\xc2A\x0b!\x17\xaeT\x84V\x0b!\x17\xbcT\x84\xfd\x16B.\x94\xa9\x08\xcd\x16B.\xb0\xa9\x08{-\x84\\\x98S\x11\x1a-\x84\xdc\x0cY\x11\xea\x96\x9c\x90\x0b\x81\xe6K\xad\x8e\x828\xca\xb6\x80\xa8&\x86\xdc\xa7-<\xaa\x89!\x17j\x0b\x96jb\xc8\x8d\xdaB\xa7\x9a\x18r\xa5\xb6@\xaa&\x86\xdc\xa9-\xac\xaa\x89!\x97j\x0b\xb2jb\xc8\xad\xdaB\xae\x9a\x18r\xad\xd6\x00\xact/\x9e\x92\x0f\xc7\xe6K\x8d\x88\xc8x\x02.8\x9b/\xb5&>\xe3\xf1\\\xa86_ju\xb4\xc6\xc3\xb9\xc0m\xbe\x14A\xb90n\xbe\xac\x824\x1e\xcc\x05u\xf3\xa5F\xc5u< \x17\xe2e\x92\xd7Q\x1e\x8f\xe7\x02\xbe\xba\n\x01\x01\x17\xfeU\xba/\x02<\x9e\x00\n\x06+\xc7\x80\xe0\xect9_\x16+\xe4\xc8\x8eQ\x90\xf2\x14D!l\xe3l\xc2\x03\xda\x01D\x98\xf3\xa5\x00\x0c\xc5\x9b\xb5\xa2D$|\xf49_je\x00\n\xe1\xf9X4s\xa3,\x1c\x85\xd0|d:_VA\x00\x87\xe7\xe3\xd4Zz\x11 \x18\xb5\xce\x97U@\nt\x02 \x86\xadk\x11RA\x11me\xb8<\xd4\xe4I\xa0\xf8v\xbe\xd4\xea\x10\x176\x1f\x1b\xedfM\x11\xa1\xf9\xd8\xb7i\x88\x88\x86\x8f\x84\x9b1&\x8b\xe0\x80A \x88\x8b\xf3\x81C\x00\x07\xa2d\xa2\xb3\xc2DP\xcc\x9cu\xd8,l\x86\xc6U>\x82\xaeZ\x91\x87\xab\x10 \x10O/Eh(\xba\xae\xdb \xa0\x81b\xed\x8a\xa6\x0e\xb7\x81\x81\x0d\x88\xbc\xb3a\x87\x08\xbe\x013\x02qxC$R2\x14\x957T\xe2\x0e\x06\xc4\xe8\x0d\x99hT\xe1#\xf6\xf9\xb2\x0e\xd79\x020r\xcf\xef\x97\x17s%t\x07\x9d,\xce\x7fn\xd6N\xec\xbb\xd7rd3\xf3\x8a\xb9\x11\x18\x8a%71\x17\xf0zn\x16sl \x14Cn\xe6.\xd0\xd5\xe4-\xe6W#(v\xdc\xcc^\x80\xe5\xacx6\xdc\xac_\x00\x8bY\\\xcc\xa8,\xa7Xq1A\x01%\xc3\x021C\nE\xb1\xe5\xe2\x86R+U\xe8 Q\\\x0d\xa1\x18r\x81\x05)\x81\x9c#\x81\xa1Xr\xa1\x07\xe1[y8\xd1\xe2\x7f\x05\x86b \x05'\x05E\x0bC\x88\x17;\xdc\x10\x1dI\x1b\xeb-]-C\x90\xecd+h\x92l\xd4\xcax$f\xcc.\x8fH\xb2a+\xe3\xa1\x981\xbbt\"\xc9\x06\xad\x8c\x07b\xc6\xec\xb2\x8a$\xb3Z\x19[b\xc6\xec\x92\x8b$\xeb\xb72\xee\x8b\x19\xb3\xcb1\x92\xcclel\x8a\x19\xb3K5\x92\xac\xd7\xca\xb8'f\xcc.\xe3H2\xa3\x95\xb1!f\xcc.\xf1\x88\xae$\xed 5\x82d\xdc\x96' Ie\x9d\xa4F\xc8\x98\xc3\x1d\xa5J%\xb41\x1f\xca\x99\xc3\x9d\xa5J5\xb41\x1f\xc8\x99\xc3\x1d\xa6JE\xb41\xb7\xe4\xcc\xe1NS\xa5*\xda\x98\xf7\xe5\xcc\xe1\x8eS\xa52\xda\x98\x9br\xe6p\xe7\xa9R\x1dm\xcc{r\xe6p\x07\xaaR!m\xcc\x0d9s\xb8\x13\x95\x81\x9e\x98w\x05 Y\xcb\xa2\xc3e[HW#\n\x8e\xd0\xd2\x00\x0c\x17\xa9\\\x8d\x94=\x174\x02\x8b\"8~$\xd3;\xd2*\xd8(\x12X\xb2\xc0\x01%\x91\x10\x92V\xc0\x84\x95\xc0\xb2\x19\x8e0\xcb\x0c\x92\x94\xb7\x94\xaf \xe4\xac\xd3MR\xceT\x84\x08,\xc9\xe0\x18\x94\xc9NIk\x00\"Q 9\x00\x07\xa5dJK\xae|&4\x05V\x89p\x94J%\xc1\x14\xda!\xadC\x10\xb6Ry\xb3\xf6~@\x06\x9c\xc0\xbaP\x18\xc7V\xa96i\x0d-\xcc\x05\x81-\x95\x98\x93\xf2'q\x82Z\x84i\xbc\x9a\x89B \xbddci\xae\x1a\x85\xb0z\xa9\x12Y/\xd9\xe0ZZ\x93 \xce^\xaa\x84\xdaK6\xda\x96\xd6$\x08\xbc\x97*\xb1\xf7\x92\x0d\xbf\xa55 \"\xf1\xa5J0\xbed\xe3qiM\x82\xd0|\xa9\x12\x9d/\xd9\x00]Z\x93 V_\xaa\x84\xebK6b\x97\xd6$\x08\xde\x97*\xf1\xfb\x92\x0d\xe1\xa55 \xa2\xf9\xa5J@\xbfdcziMpdBl\xf6\xb5\x8fA\x92\x9e\xab\x16\xef\x13\xbb\x83\n\xb5\x89{\xaf\xda\x02\x80\xd8NT\xa8M\xdc\x83\xd5V\x04\xc4\xfe\xa3Bm\xe2^\xac\xb6D 6,\x15j\x13\xf7d\xb55\x03\xb1\xc3\xa9P\x9b\xb87\xab-\"\x88-Q\x85\xda\xc4=ZmUA\xec\xa1*\xd4&\xee\xd5j\xcb\x0cb\xd3U\xa16q\xcfV[wT;l\xe2\xaajDQO\x15\x14\x01\xdbo\x05^\xca\x8c\xe3\x03\xed\xcc\x15\xd0zsN\xcc\xad\x810<\xf9\xad\xbb\x82\xa0\xd8\xbd\x133,\xcb\x19n\xfc\xc6^\x81^\x86X\"\\^\xcap\xe27\xfd\nl\xb1\xc7 \xe6U\x96\x93\xdc\xf8-AR'm\x0c)\x14-$\xb0mX\xd0\x14{\x80b\x9ee9\xc5\x0d\xdaT$%h\xe3I\xa1(\xce\xd0\xc6#\xe1\xb0\x91\xe0\x05\xbd,\x84\xe2 \x9f\xbc\xcb\x08\xaa\xcdI1\xcb\x1a\xc1\xb97\xbbsYjK\xca\x0d\xe2\xc4\xefjR:\x92\xf2#0\x0cW~\xdf\x93PQ\xbec\xd6\xa2\xc6\x02Cq\x85vF\xcbN!g\x08\xf1\x02\xb6M\xc96\xb5p$A\x14_hg\xb5 \xec\x8dd\xcd\x98\x97R\x9c\xa0]WB?s\xbc\x968x\x03ax\xf2\xdb\xb2\x05\x81\x9c\x1d\xcf \xda\xb2%U#\xe7G`h\xed\x01\x9b\xba\x04E\xb5\xaf\xdb\xc2\xb8\x86Q\xbc\xa1\x9d\xdf\x82\x88\xd8\xfc\x15s&A\xb4\xaf\x03\x9b\xc3\x14I\x8b+Q(\x8a3\xb4\x81L\xd1\xb4\x0d\xc74\x8c\x96\x1a\xd8e\xa6\x88\xa43$\x81a\xb8\xf2\xfb\xd0\xa5\x07-\x15b\x02\x12T\xf0\x05\xd2&\xc2\x08\xa18\xa6#\xe5.c,\x0e\x19\xc8#=R\xf6l\xe0\x00U\"\x8a!\xeaC@\xd2\x1a\xa8H\x02b/\n*\xca3CR\xe6Dh\x01\xb1\x16E\x19\xf5\x01#)s\xca 9\xf6\xa2\xb0\x839\x8f\xa4\xa0}y=\x928\xa4>\xc4$\xad\x84\x8a\x19x\xf6\xe2\xc0\x849\xf3\xa4\xd0\x92\x96\xaa\xc4\x91\nyP\xaa\xbd\xb3\x11\xb37_\x898t!\x8eVI\xeb`\x02\x18\xb8\xdf\xc1\xb1Ly\x16Kn\x0f9kQpC\x1d\xdcR\xb1\x85\xbc\x1aQ\xb4C\x9d\xf5j7\x059\x07\xf0\xd5\x88\xc3\x9f\xeax\x98\xbcw\xcb\x99\x0b\xe3!\xfa0\x99\x82\xae\xe4\x15\x89\x03\xa4\xf2\x00\x9a\xb4\x06\"L\xe2Y\x8b#&\xf2\xb4Z\xbb\x19\x889\x1e\xaaD\x18B-\xdb\xf9KY\x8bc*\xea0\x9c\x82 \xa4\xd5\x88\x83,\xf6\xfc\\{ML\xa8\xc5W&\x8e\xba\xe8Sw\xd2\xaa\xf8\xd8\x0b\xe8\x84\xc20\x8c9\xa9\xa7R\x93\xdc\x85\xc5q\x19{\xbcO\xa5\xae\xb6 K\x18\xa8Q\x87\x02Uj\x92\x07&\x92\xc8\xadu\x17\x99\xc0\x08*\x00\xf7\x94#[?\x08\xbe\xdf\x1a\xd9F]\xd4\xedY\xdc{j#\xbb\xd7\x94C\xc5f]\xcc\xbfY7\xb2\xfbu)\xffj\xdd\xc8\xb6\xeaR\xfe\xdd\xba\x91=\xa8K\xf9\x97\xebF\xf6\xb0\xa9\x97\x7f\xbbn\x84\xeb\x06k\x18-R\xae\xd5\xd8\xa0\xcb\xc1\xa6\xe3\x1e\x03\x820&\x8d\x01\x94\x80\xfb4\x04\xd0\x04\xb6h\x08\xa0\x0e<\xa0!\x80N\xf0\x90\x91\x05PL\xdc(&\xce\x06\x16N3\xb1\xc1\x00@\xd5\xc4=\x16\x05\x81L\x06\x04('\xee3\x18@;\xb1\xc5`\x00\xf5\xc4\x03\x06\x03\xe8'\x1e\xb2\xf2\x00\n\x9a7\n\x9a\x87i\x1a\xfa\x9c\x86\xe6\x06\x8b\x00U4\xefq0\x08e\xb2(@I\xf3>\x0b\x02\xb44\xb7X\x10\xa0\xa6\xf9\x80\x05\x01z\x9a\x0f9\x99\x00E\xa5\x8d\xa2\xd20\xe2\xb4\x94\x1aT1\xa8\xa2\xb4Gc \x88IA\x00\xe5\xa4}\n\x01h&\xb5(\x04\xa0\x96t@!\x00\x9d\xa4CZ\x0e@!\x1bF!\x93\x16?\xda@\x1ab\x89@\xbdm\x00\xbdq\x84\x10\x1d\xafL\x96\x0cP\xf0\x86W0K\x05(}\xc3+\x9d\xa5\x02\x0c\xb1\xe1\x0d\xc1R\x01\xc6\xd9\x00\xc6\xe1\x1a\x06Xl\xc5\xce\x125\x11<6\xae\xc0Y\x83!\x02-\xb6\x82\xa6\x12\x96\x10\xa2\x03\xa6\x17\x86\x0c\xb0\xd8\n\x98q\x18*\xc0b+`\x12b\xa8\x00\x8b\xad\x80y\x89\xa1\x02,\xb6\x82\xa6*\xb6a\xc0\xc7\x85l\xfd\xe0\xdb\xf1\xd2\x0bX\xdb\xf8\xb6Q\x95@\x06\xf0\xed^]\x0c\x95\x9aU)\xf0\x95'\xbb_\x15\x02\x9fU\xb2\xad\xaa\x10\xf8Z\x92=\xa8\n\x81\xaf-\xd9\xc3\xbaN\xa0\xa1\xb8j(\x18\xbf\xf8\xd8\xa0\x8a\xc1&\xe3\x1e\x8d\x81 &\x05\x01\x1a\x8f\xfb\x14\x02\xd0\x00\xb6(\x04\xa0\x06<\xa0\x10\x80.\xf0\x90\x96\x03PH\\+\x04\xec\x9b~l\xd0\xe5\xa0J\xe2\x1e\x03\x820&\x8d\x01\x94\x12\xf7i\x08\xa0\x95\xd8\xa2!\x80Z\xe2\x01\x0d\x01\xf4\x12\x0f\x19Y\x00\xc5\xcck\xc5\xc0\xf3\x8c?7\x18\x00\xa8\x9ay\x8fEA \x93\x01\x01\xca\x99\xf7\x19\x0c\xa0\x9d\xb9\xc5`\x00\xf5\xcc\x07\x0c\x06\xd0\xcf|\xc8\xca\x03((\xad\x15\x04\xc4)~j\x90\xa5\xa0j\xd2\x1e\x05\x81\x10&\x89\x00\x94\x92\xf6I\x00\xa0\x91\xd4\"\x01\x80:\xd2\x01 \x00t\x91\x0e)\x19\x00ElhEL\xe4n\xb3\x01\x143Qp\xa4\x0d\xaf-\x96\x0c\xa2\xe248i\xf5\xb4\x0d\xa7\xd4I\xab\xe7m8=OZ=q\xc3\xa9~\xd2\xea\x99\x1b\xde\x1al\x83\x00\x0b\xad\x98Q\xbf\"\x81\x87\xbc\x154 \xd0$\xa0\x85V\xc0\xc4\xc0\x90AT\xfc\\A\x13\x01\x16Z\xf1\xb3\x07M\x03Xh\xc5\xcf'4\x0d`\xa1\x15?\xc3\xd04\x80\x85V\xc0\x9c\xc34(\xb7P\xfb[-\xe9\xd7\nFv\xfer\xce2\x96\x01\xf2-d\xa9 \xe5BA \x84I\"\xc0\xc4\x0b \x00s/$\x00L\xbf\x90\x000\x03C\xc9\x00&a\x08\x84(\x0f\xc3A\x04\xa9\x18\x1e\x07\xc1L\x0e\x06&d8\x14\x98\x93\xe1P`Z\x86C\x81\x99\x19^.09C\xc2D\xf9\x19\x1e#H\xd1\x00@\x08g\xf280Q\xc3\xc3\xc0\\\x0d\x0f\x03\xd35<\x0c\xcc\xd8\x00\xb2\x81I\x1b\x12'\xcc\xdb\x00 A\xea\x06BB@\x13\x00\x82 \x1c\x00\x07\xe6p\x00\x1c\x98\xc6\x01p`&\x07\x92\x0fL\xe6\x90@8\x9f\xc3\"\x04)\x1d\x0e\x06\xa1L\x16\x05&vX\x10\x98\xdbaA`z\x87\x05\x81\x19\x1eN&0\xc9\xc3)\xaa=\xcf\x03kN1\xd5\x03\xeaS-\xdb\x03)Y)\xe1\x03)^)\xe7\x03\x19C)\xed\x03\x19H)\xf3\x03\x1aM-\xf9C\x92*\xe6\x7f8\x92cR@<1D\x0b\x91\xc2\xd3\x9aJ\"\x88#T\xcd\x05q\x84\xaa\xe9 \x8eP5#\xc4\xb7Q9)\xa4\xe5\xdfs\x8f\xe1\xbc\x10Q(H\x0d\x91\x08\x08`\x12\x000AD\x94\x839\"\xa2\x1cL\x13\x11\xe5`\xa6\x88\xac\x1fL\x165\x00Q\xbe\x88E\x08RF\x1c\x0cB\x99,\nL\x1c\xb1 0w\xc4\x82\xc0\xf4\x11\x0b\x023H\x9cL`\x12\x89@\x89\xf2H\x1cD\x90J\xe2q\x10\xcc\xe4``B\x89C\x819%\x0e\x05\xa6\x958\x14\x98Y\xe2\xe5\x02\x93K\x04L\x98_\xe21\x82\x14\x13\x00\x84p&\x8f\x03\x13M<\x0c\xcc5\xf100\xdd\xc4\xc3\xc0\x8c\x13 \x1b\x98t\"pp\xde\x89\x01\x08RO,\n\x02\x99\x0c\x08L@1\x180\x07\xc5`\xc04\x14\x83\x013Q\xac<`2\x8aUPk>\nT\x98ZJ\n\xd2\xa2RV\n\xd0\xacJb\nP\xb6Jn\n\xd0\xbfJz\n0\x89J\x86\n\xb2\x92R\x92\x8a T\xcbS\xb1\x04G\xa4\xaa8R\x80\x12\"\x04\xe7(\x85\x84\x15K\xa6\x98\xb3b\xc9\x14\xd3V,\x99b\xe6\x8ak\x9b(y\xa5\x90\xbdR\xf8&Kd\xeb\x9a_\xc5fPF\xab)\x14%\xb4\x08\x04\x040 \x00\x9c\xcej\xca\xe1lVS\x0e'\xb3\x9ar8\x97E\xd4\x0f\xa7\xb2|f\xad\xc0\"\x0c\x16!Jd\xb10\x08e\xb2(8\x8d\xe5\xf3\xb1=\x0b\xb2X\x10\x9c\xc4\xf2\xf9\x98\x9d\x05\x0d9\x99\xe0\x14V\x83\x12f\xb0X\x88(\x81\xc5\xe1 \x98\xc9\xc1\xe0\xf4\x15\x8b\x82\xb3W,\nN^\xb1(8w\xc5\xc9\x05\xa7\xae\x1a\x988s\xc5aD\x89+\x1e\x08\xe1L\x1e\x07\xa7\xad8\x18\x9c\xb5\xe2`p\xd2\x8a\x83\xc19+^68e\xd5\xe0\x04\x19+\x1a JX1(\x08d2 8]Ec\xe0l\x15\x8d\x81\x93U4\x06\xceU1\xf2\xc0\xa9*FA\n\x99*Hc\xaa\x89*@\x8f\x8ay*^\xb9ji*^\xe1jY*\xde\x08jI*\xde0j9*\xc0X\x8a)\xaa\x86R5C\xc5P\x1c\x95\xa0bi!R\x88\x12\x9c\xae\x94\xd2S\x0c\x9drv\x8a\xa1SNN1t\xca\xb9)\xb6}\xea\xa9)\xbf\x8c\xd4\xa0\xccT]&JL5\x00\xa8\xdcl\xca\xe1\xb4T]\x0cg\xa5\xeab8)U\x17\xc39\xa9\xa6n8%\xe5\xd3k\x04\x16`0\x00QB\xca\xe7\xc3\x7f\x16d2 8\x1d\xe5sq=\x8b\xb1\x18\x0c\x9c\x8c\xf2\xb9\x88\x9d\xc5\x0cYy\xe0TT\x0d\x12f\xa2\x18\x84(\x11\xc5\xc2 \x94\xc9\xa2\xe04\x14\x03\x82\xb3P\x0c\x08NB1 8\x07\xc5\xca\x04\xa7\xa0j\x948\x03\xc5BD (\x0e\x07\xc1L\x0e\x06\xa7\x9fX\x14\x9c}bQp\xf2\x89E\xc1\xb9'N.8\xf5T\xc3\x04\x99'\xaa\\\x94x\xa2A\x10\xc6\xa41p\xda\x89\x82\xc0Y'\n\x02'\x9d(\x08\x9cs\xa2e\x81SN\xb4b\xda3N\x80\xa2\x14\x13N\xbc\xf6\xd4\xf2M\x9cF\x95\xd2M\x9c\x92\x95\xb2M\x9c\xde\x95\x92M\x9c)\x94rM\xbcu\xd4RM5\x9db\xa6\x89\xc6\x1f\x93hb(\x01B\x88\x0e\x9a{T\xd2L4\x95j\x96\x89\xa6RM2\xd1T\xaa9&\xa6]\xa7\xa5\x98\x04\xd9$\\\x85SP6\xa9)\x14e\x93\x08\x04\x040 \x00\x9cMj\xca\xe1lRS\x0eg\x93\x9ar8\x9bD\xd4\x0fg\x930\x13\xd7\xb3\x08\x83E\x88\xb2I,\x0cB\x99,\n\xce&a>\x16gA\x16\x0b\x82\xb3I\x98\x8f\xb2Y\xd0\x90\x93 \xce&5(a6\x89\x85\x88\xb2I\x1c\x0e\x82\x99\x1c\x0c\xce&\xb1(8\x9b\xc4\xa2\xe0l\x12\x8b\x82\xb3I\x9c\\p6\xa9\x81\x89\xb3I\x1cF\x94M\xe2\x81\x10\xce\xe4qp6\x89\x83\xc1\xd9$\x0e\x06g\x938\x18\x9cM\xe2e\x83\xb3I\x0dN\x90M\xa2\x01\xa2l\x12\x83\x82@&\x03\x82\xb3I4\x06\xce&\xd1\x188\x9bDc\xe0l\x12#\x0f\x9cMb\x14\xa4\x90M\x824\xa6\x9aM\x02\xf4\xa8\x98M\xe2\x95\xab\x96M\xe2\x15\xae\x96M\xe2\x8d\xa0\x96M\xe2\x0d\xa3\x96M\x02\x8c\xa5\x98Mj(U\xb3I\x0c\xc5Q\xd9$\x96\x16\"\x85(\xc1\xe9J)\x9b\xc4\xd0)g\x93\x18:\xe5l\x12C\xa7\x9cMb\xdb\xa7\x9eM\xc2eP\x06e\x93\xea2Q6\xa9\x01@\xe5fS\x0eg\x93\xeab8\x9bT\x17\xc3\xd9\xa4\xba\x18\xce&5u\xc3\xd9$L\xaf\x03X\x80\xc1\x00D\xd9$\xcc\x07\xf9,\xc8d@p6 s\xf1;\x8b\xb1\x18\x0c\x9cM\xc2\\l\xceb\x86\xac{U\x1fl?w\x15\x1fV\x00w\x17\x1f\xd4\x00w\x19\x1fR\x01w\x1b\x1f\xd2\x01w\x1d\x1fR\x02w\x1f\x1f\xd2\x02w!\x1fT\x03}\xe7\x1e\xd6\x01}\xe9\x1eT\x00}\xeb\x1ej=}\xed\x1ej:}\xef\x1ej7}\xf1\x1ej4}\xf3\xbelq\xfb\xc1\xcb\x033f\x90\x17UD\xa3\x1d\x05\x01\x07<\x12\x01\x8ey$\x00\x1c\xf6H\x008\xf2\x91\x00p\xf0\xa3d\x00\xc7?\xf6\x00\xabh\x08\xe4q\xe0(\xc8\xc1\xc0\x81\x90C\x81c!\x87\x02\x87C\x0e\x05\x8e\x88\xbc\\\xe0\xa0H\xc0\xe4\xe3\"\x00\x04\x87F\x1e\x07\x8e\x8e<\x0c\x1c y\x188F\xf20p\x98\x04d\x03GJ\x02\xd72XBHp\xbc\x04\x80\xe0\x90 \xe0\xc0Q\x13\xc0\x81\x03'\x80\x03\xc7NH>p\xf8$\x80\xb2\x11\x94\x83\x81\x83(\x8b\x02\xc7Q\x16\x04\x0e\xa5,\x08\x1cMY\x108\xa0r2)l5\xaa\x9ef\x0f\xc8\x83W\xc2\x81\x96@\xc0\xe3l\x03\x80\x87\xd9\xa6\x1c\x1ee\x9brx\x90m\xca\xe11\x96\xa8\x1f\x1eb\xe9\xfd[\xe1\x08\xcb\xc2\xe0\x01\x96A\xc1\xe3+\x03\x82\x87W\x06\x04\x8f\xae\x0c\x08\x1e\\Y\x99\xe0\xb1\xd5gF\x1b\xd1\xd0\xca\xe1\xe0\x91\x95\x85\xc1\x03+\x8b\x82\xc7U\x16\x05\x0f\xab,\n\x1eU9\xb9\xe0A\xd5g\x07\x18\xd1\x98\xca\x03\xe1!\x95\xc3\xc1#*\x07\x83\x07T\x0e\x06\x8f\xa7\x1c\x0c\x1eNy\xd9\xe0\xd1\xd4\xa7\xc6\x1a\xd1`\xca\xa0\xe0\xb1\x94\x06\xc1C)\x8d\x81GR\x1a\x03\x0f\xa44\x06\x1eG\x19y\x14\x86Q\xc1\x88\x89\xeb\xe1F4b\x12\x08x\xc4l\x00\xf0\x88\xd9\x94\xc3#fS\x0e\x8f\x98M9\x96\xdc\xca\x05\xfajr\xc1\xa8\x10\xa6\x95C\xdb7\x12Kf\xae\x1d?\xb4\xf2\x92}I5\xe3\xf3\x80\x0e)\xda\xa5\x9a\x8b\x9c0\xb6S/\x0c\xce\xb1\x17 -]\xc5\xe1z\xb9\xa2 \xd6\x81\x8b\xe2\xac\x98\xa3\xa9K\x18\xc7\x0b51M\x10\x06Ha\xe9s\x00d\xce\xd6Q'\x88\x0d\x91)H\x0e\x91\xe5\xc2+H\xaf\xb0p+\x9b\xe4\x9f\xd4\"\x9eJ\xa5A<\x95B{\xc4\xa2\xe3\x93D\xe7\xa9TD\xe7\xa9\n\xd1)\x8a\xb4D\xd9\xd8[\x06\xe7YT\xc0\x94\xc7dy>Q2\x00\x87\x048(HQ\xac`\xed\x03#E\xed9bA\x18\x08(\x0b\x83)\xc5Q\x90G\xc1\xfbR\\y\x83DF\xbf]D\xffh aaZ-G#`a0$\x0c\x0d\xaa,\x9c\x7f!~\x11\xc6\xfe\xb9cG^jc\xef\x11\xb1P\xccBq\xb8E\xb1c'\x1cr\xcd\"\xd7Q\x04#\x03\x16y\xd2p\x98\x12\xce\xa1\xd4\x12\x00-n\x0c\x00\x16\xb7\x07\x00+\x0c*\xcan\xda\xb8\x98Z;9\xb0\xa4\x99\x1cV\xd2J\x0e\xab\xd0HA{8\xb7\x92\xb5\xe7\x08\x1f\xe4\xb1\x92\xf6pX`8]h\x833\xe6\xc1\n\xd9n>\xab/\xc2 \x8b\xf5\x1e\xd19\x1fR/4\x8b\xa5K\xd6s\x80\x94\x0f\xa1\x17\x06Ql\xf2\xc5=\xa2\xb8\x07\x05\xea\x0b\x93@\x18@\x90\xbe\xe8S\x00\x88\x85E\"\xf8\xe2\x01Q\xdc\x1d\x0d\x01\x06C\x12Q\x00\xda{\xc3\x81\xd5\xbd\x16$\"\xf5g\x9d\xae\xc5\x02\x005a\x04\x9a\x01d\x07\x1a\x01\x99\x82F\x08\xacA\x83`\x83\xb0\x18\xd0&\x0c\x080\x0b\x8d\x10X\x86\x01\x15\x18\x05\xeb(\x8cU\x99\xc9|\xa1\xc5\xfcV\x83q\xb4\xa4\xbd\xfc6s\xf9m\xd6\xf2\x15\x8c\xe5\xb7\xdb\xcaW0\x95\xdff)_\xc1P\xfe\xb1v\x12\x98\x04\x0bM\x82[M\xc2\xd1\x92&\xc1m&\xc1m&\xc1\n&\xc1\xed&\xc1\n&\xc1m&\xc1\n&\xc1\x80I(\x8c\x8f\xecd\x1d\xa3C\xd3O\xb2\xce\x03b\xb2r\n\xd8\x17\x01\x03;\x8e\xc3-\x01\xedq<\xbd\xc0EAZLi\xc5\xcf\xe7Fs\"+m?\xcf\x98\xf86\xc6\x9acG\xe5\xe8\xb0\xb1c\xcf\x0e\xd2\xf3\xe69\x8dO\xe3u\xe0\xd8):\xe4\xc9\x81<5\x82\xce\x83p\x1b\xdb\xd1$\xdc\xa0x\x91\x7f\x9c\xcfs]\x14Lr\xa9\xea\x87\x08c/J\xbcDa\xcc9\xc0\xeaH\x94\xd5\xcb`[4L\xa3EJ\xae\xe3\xbd'\xea\xb9\x1e\x88UU\x9d\x11\x9c\xaem\x05u+\x0c\xf1\x95\xc2|u\x13\xf8\xc7X\xc0W1\x80\xff<\xfa\xf7\x8fT\xbf\xff\xdd\xb4/Q4VW4>F\xd1XE\xd1\xf8y\x14\x8d\x8fT4~\x8a\xa2)\x96U\xb9\xe6\x84Aj{\x01\x8a\x0f\xf5\xa3\xfdy\xe2\xc4!\xc64E\xb1h\xa6\xb7\x12\xecu\x1aN\xc8\x9d\x96\xec\x01\xa3\xddX\xcb\x1e\xf2t\x0c\x0cS\xb0\x86Y{\xe7<\x00bj\xec\xd9\x1buIARPX\x8d9\xf4\x94\x03\x15\x04V\x18M\xcaV\xf8'7\x02\xa0\x84\xdb\xe0\x1f\xdb\x04\xb1\xb4\xf8di\x01JXZ\x0cHK\x8b\x82\xbd\xe8\x10\x85\x89\x97'\x02\x17\xde\x0e\xb9\xff\xd7\xf3\xa30N\xed \x9d\xfcQ\x97\xd8\xf3$\xc4\xeb\x14\x11\x85\x19\xe9y\x8c\x9c\xf4G#\xdau\x88\xbf?\xd1Eg\xc4\xdf\x9f\x14\xcc}\xe0\x04\xcc\x1c\xe7\xcf\x94QAH\x15\x9f\xcc$\xf7\xff\x83\x04\x17\xc9\x88\xff\\\x19)\x01\xb6\x89\x16\x84\xb1o\xb3#u\xf6\x88F\x16\xa370\xa0\xd3\xb0(\xa6#\xc9(>>'X\x0b\xc5\x07J\"\xb9\xe0\x90\x8a\x13\x8d\x85e\xd2)\x88\xa7\xe0m\x8d\xcclt!\x14\x19\nCx\x89\xfd#\x05\x96\xca\xa6jfp\xe6\xe6e\xc3\xbcl\x14f\xa3\xcd\xed\x04\x1d6(N=\xc7\xc6e:;{\xc6\xef\x91l4\xdfsY\xa8\xef\xb9.\xe6\x80i\x18\xb1\xc04\x8c\xb8\xaaS\x9f\xab9\x0fp\x14\x0c~\x00\x9a\x91\xf9\x8ezK\x00\xb4\xb01\x00\x16n\x0f$B\xd1$\x856)8q\xd9P^o\x92vr`q39\xa8\xa0\x95\"\xbb\x1d\xed\xf8e{\xf01\xed\xe1\xc0\xe2\xf6pPA{\xf8\xfa\xcb\xf6PX\xd7\xf3\x0fad;^\xba?7\xb8\xa23\xf6\x01\xf41\xfa\xecq\xf1\xfdym\x8b\xe6\x0f^\x99\x15/f\x90\x92w\xa7kXI\x07ez\xf1\x82IK9'\x86\xbc\xd6J\xfc\xae\xc5\x13\xdaN\xeamP\x03\x19M\x94d\x0c\xd7\xa9\\\xc8p\xcd\xec\x9e-q\xb8=\xe3\x9e@\x82\xe7\xcf\xbf\xa3\xbe\x14\xea\x15\x18|\x95-\x03\xf3S\x11\x9dn\xfe\x9f\x1a\xa8\xab\xa9\xedXQ\x9b\nKC\x95\xf5\x9e\x89Py\xb3\xda@y\x1b\xd9\x16\x18\xdf\xa7\x05\xcd\x06{^+\xa4w\x16R\x98 _\x7f\xb6\xef\xe1/\xe3p{\xd0\xfc\xf0Q\x0b\x93\x9dVd\x0f\xfd0LW^\xb0<_\xc6\xf6>ql\x8c\xea\xb6\xcdm\xe7aa;H\xdbx\x897\xf7p\xd6\xf2r\xc1+)\xa24\x93of\xe5?a;E\xdf~\xd4\x7f\x9a\x88\x9e\x03\x1a\xe5Xu\xba=A\xa7:\x02z:\xe4\xac\xa5\x16^\xdb`\xd7\x89\xe1.\x9b\xeb$\xb7\xc0\x8fFW\xb7HM\x11O\x81:\xcaaI\xc4\xac;\xe6Yu\xc7\x00#\x0d\xdb\xf1\x12\xfd\x7f\xc5A\xbc\xe0\x18\x1f\xe1\xd1OEI\x9d\xa5\x80\x88L \xf2\x9a\xb2\xb4\xcdwz\x90\xeb\xf4\x84\x06o\xf7\x1f\xc0\x17\xb3\x87L0\x1dzAZ\x8fH\xce:N\xc2\xf8\xbc|H#\x93\x95\xed\x86[\x0d\x02N\xea\xc5b\x8c\xb0\x9d\x89\x05\x99\xdd\xc6\xb8\xd3\xb5\x92\x8e\xb3\x9e{\x8e6G\x8f\x1e\x8a\x7f\xec\x1a\x03\xeb\xac;\xea\x9fu\xfb\xfd3\xe3\xa7\xc9\x91x\xb1\x88\xe7\xf6\"\xcd\x04\x0d\x83\x14\x05\xe9\xf9_\xfe\xd2\xf8\x7f\xb8\xd3\n\xe4\xb9\xde\xd1;\xc6 \xdauz\xd1\xaeC\x9e\xf7\xeb\xfd4Q\x86\xe5\x07;c\xdb\xf5\xd6\xc9\xb9\x17\xacP\xec\xa5\x93f\xd2\xe4\xd6\xd1\x93\"\xf3\x99\xe7e\xf4I\x11A\x1a\xba\xfeb\xb2ByN'\xff\xf91\xcf\x98\xee\xce5\xf9\x9cu\x846Ui$\x1a\xcd\xfd\xbb\xd0\xeb\x99\x18Ej_\x10d\xcc\x97\x9a\x1dx\xbe\x9d\xa23\xc1s\xa8/\x11\xa5\xc2\xd0\x89=\xc4IM\xdb\xec(\xd0\n\xa6\xa5~\xd4\xf4Ce\x17\x9d-2\xea\"\x83-\xea\xd5E=\xb6\xc8\xac\x8bL\xb6\xa8_\x17\xf5\xd9\"\xab.\xb2\xd8\xa2\xf1x\\\x17\x8e\xc7c\xa0\x98*\xe7\x00\xbe\xbdk\xa45\xfa\xc3\xfe\xc8\x1c\xf4\x87,\xaa\xf4\xf2\x1aY\xfe\xce\xc3\xbc\xd4\xb3q\x0d\xe3\xb3\x95\x8f\xda:HP\xc3(\xff\x8d\x86\x04(IQf\xa0h\xaf\x15\x11T\xdeM:!\xb3\xaf,\xc2Ej\xb05>\x10\xbf\x9e\x1b\xecB\xa2\xa4k6\xae \xda\x95\x01\xd6\x01c{G`\xcd#\xb0\xfd#\xb0\xd6\x11\xd8\x01\xa3\x17\xe8`\x7fA\x8f\xbd$\xd5b\x94 \xa1q\x08\xc4\x9a{\xf1\x1c\x99\xaf\xd6'94I\xf7\x18i\xe9>B\xc5\xd1*\xa1%\x8b\xed\xa5N\xf4sDm7u\x8f\xdbo\"9&(B\xb1\x9d\x86q\xce\x94\xe0at-A\xfb=\x7f\xd9\xf1\xfc\xe5\x81\x18\xd2\x9b\x9cG\xfe\xab\xeb%\x11\xb6\xf7\xe7s\x1c:\x0f\x02\x1d\x06\x0fI\xc7>\x94\xe7\xe1Mk\x88\\\x17\x9a\x02\xf8\x01k\"-\x95\xd5\x06\x0d\xb6\x0c\xa2\x9c\xf5\x0b\xa9\xc6\x03\xc7Y,\x9e_\xaamlG\x11\x8a\x05\n\xec\x0f\xf4hW\x1a\xf0\\\xef\xe4\x9b&\xa5\x0b\x9d\xeb\x9d^VH\xcd\xf0\xdecVRN\xcf\xf3p7\x01\x9f\xd2\x12\x84Qn\x1a-\xb5\x97Z\x82\x9cL\xeaCe4\x82ymH\xcdO\xb4\x05F;\xf2Y\xf6;%I\x18{\x993V\x99\x18\xaa\xcc\xf5\xe2\xa2\x9a2%:\xa98\x12%N\x88\xd7~0\x01\x9f\n\xc5\x7f\xba\xd8\xe4 \xe0F,\xeai\xfe\x8b\xe6\xa5\xc8O\xaaG\x95E\x0c=\x0b\x97\xb2\x7f\x8c\xea\x9f \x134\x8aB\xc4^\xc2E\x81\xbddR\x9b,\xef\xb9F\xb4\xeb$!\xf6\xdc\"\x1c\xb3\xc6g\x03\xebld\x9cu\xcd\x9f\x84*)\x9d\xb8\x99\xf5\xa9\x1b\x1e:\x1bj\x93\xca$\x8e\x18\xf5I'\xd4;V\xb4\x9b\xe4\xa5\x0b\xdb\xf7\xf0\xfe<\xb1\x83DKP\xec-&U\x1f\x9e\xf7\x0d\xcb\x10\xf2\xee\x06\xa1\xe6\xa2\xc4\xe9$\x91\x1d\x1cH\x03d\xfa>7j\xd5\x9f\x1b\x93\xe2?BV\x9dd\xb3\x84\x82\xa2\\\x85}^\xab\xfdD\xc2\xca\xb71u\xde\xa9_5t[\xcc\x04}]\x9f\xa8HK\xf4\xd1\xdc \x8eWVd\xc7\xb6\x8fR\x14\xff\xf1G6\x15\x90B\xf5\xa2]\xcd\xdf\x8av\x1d\x9db\xef\x87A\x98o\x10P\x82\x0ft]V\xdb\xc6C[\xad\x9a\x06\x1f\x0e\xfc\xca&\x9b\x04\xcch7\xa9\x0e>\x90\xfe`\xa9{\xb9\xc5\xdb\xc3\x82\xedq \xdc\xcd\xc8j(\xba\x02\xd1\x07\xfe\xaa\xeb:\xb3\x10\xe9\xb3\xc3a\xb3\x921\x99E\x8c1\xe6\x16;\x00\x04\x14\xad\xd3M\xedy\x1e8\xa0\xf8\xe9#\xceQ\x0eOV]\xfc\x9c\x8dC\x87\xc6\xdb\xfa\xfc\x90s\x04\xa3\xf3\x85\x17'\xa9\x16.\xf2\xf0\x83a\xdb\xd1;\xfa\x11\xbc\xbaebs\xd5/:9\xe7S\xa7\xf3*\xd7Y\xfc\"\xb3\xbe\xad\x999L\x1eSY\xfa\x8bj\xb5\xd9kV\x9b\x99\x9f\x00kd \x9b\xf3\xfb\x8f\x9a\xa5\xbf\x00\x13=U\x111\xb4.c{\x0f6\xab\xeb%Z\x18\xa1\xa0\x19n\x92\xb5\xef\xdb\xf1\xfe \x1a\xe13\xef\x16h\xa8fQL\x8a\x95'V\xd6\x1a\x95s\xd0\xc4\xf7\x82*\x82\xb5\xb2\xdf A\xd9\x1b\x83\xa3\x9f\xe0~c\x00\xcb\x7f\x83\xe980\xe6(\xd9\xcf\x8e\x01w\xb0=G\xf8\xe9\x1d\xef\xa4\xa9\xfe\xa8f\x95\x922C79,\x0fu\xbd\x1eG\xb9\xc30'\xcc\x1aJ\x02\x95\xfd\x91\x9a\xa1$\x9d[\xc0j\xd5g'J\x95Q\xadi\xeds4\xae\xe8C\x9a\x8f\xd2U\xe8\xca\xe6\xed\\\xcf\xf5\xd6\xe5H'f\xd0A\x16\xa8e\xe3\x05w\x03\x8c\x99\\L\xba\x0b\xe5\xd3ONC\xf5\x04\x9d\xed+\xf2v.\x16\x0b\xc5F\x86\xf9\xd2,3\x80\xe7\xb6\xf5\x97\x92$\xb2\xd3\xd5\x11\xd0?\xfepQ\x14#\xc7N\x11\xa5\xccAD\xf4\xacS{[n~\xbdq\x08\xbdc\x16\xab\x19\xfa\xb7'w\xd0\xc96\x8c]m\x1e#\xfb\xe1<\xffW\xb31\x96\x85c\xaa\xf1R\xb9\x19N\xec\xe8\x0f\x07\xa3h\xc7l\x81\xff\x07\x9a\xaf\x17\xed\xd8\xd3\x9d\xcal\xd8\xcd:,\xbc\xa6\xab\xd4p\xa6\x8b*r\xc8\x16\n\xb1\x17\xe5\xebR\x82\x81\xa9:\xe4<\xdfH\xf3?4\xe9\x90\xd1\xbeZp\xc7\xc8\xad\x18\xe0\xf7\xea\x00\x9f\x98\x95\x9e=\xb2\xe7\xa4\xab\xf6\xad\x19\x19\xcb\xb0m\xc4,5\xe0\xf8\xaab\x19\x85IJ\xbc\x8f\"3p\x7f\xec8c}\xc2\xae\x80\x87\xe6YO\xef\x9f\x19\xfd\xbe0\\\xa1\xb8\n\xa7\x1drN(\xea:\x81\x19(\xb3\n\x1f\xf5p\xf9h9\xd7\xac&\x17\x8em\x98\xbc&{V\xef\xcc\x18\x18g\xfd\x91\x82&\xd7j\x8a,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2\xdeE\x18\xa5\x88\x95kl\"\x13\xf1\x9a\xec\x8f\xcf\x06\xbd\xec\xff\xad\x8a,\xd8\xaa\xe92\xaf\xec$v\xa0\xd8j\x9cN\xd4\xa8B\x0dK\xc4:\xe6\xc0\xb0\x17\x0b^\x9d\xe3\xe1\x991\xb4\xcez\x96B\x17_\"5\xc7,\xaa:\x9e\x17(\xb1\x02\x9b\xd3\xd4\xa8\xc2>\xb2Sg\xc5\x88e\xe9\xc8tz\x9c\"G\xfaY\xaf7<3\xc6\n\x8a\xcc\xd9*\xa9\xb2\xa8\xec\x14n\xa0\xd4J\x8cNS\xa7J\x05\x19WF\xae\xb1n\xf4\x00\xb7\xcc\xa6\x1cc\xa4\xe6\x96\x19W%e\x16u\x9d\xc0\x0c\x94Y\x85\xcfi\xaaT\xe1\x1f\xe6\xb1^\xc2H\xa6\xbb\x96m\x0fym\x9agc\xfd\xcc\x18\x0c\xdb\x95Y\xf2U\xd2gQ\xdbi\xfc@\xc1\x15Y\x9d\xa6U\x95*\x88\xb0\xbe>\x15:\x98\xd0\xa2\xa2y\xf6\x07\xce\x14\x8d{\xc0\xab\xa5\xc4\x95(i\xb9\xa8\xefd\x96\x07Hzun\xa7\xe9ZR\x0b!\xa0\xb3B>J\xb8\xa4\x9c\x1aY\xa7[\xfe\xa0\xa5^\x8aQk\xaef\xe1\xe14kD\xb3\xd6*\x9eh^\x90Eq\xd4\xd6b\x1eI\xe7{T:\xb5oU%\xd8{M\n\xd2\x1d\xb9.b\xbc*\xb5\xe7\xa7\xad\x82\xa8\x9a\x8bex\xdd,b\xe3\x1b\xd8\xf3N\xedy\x07{l\x1a\x8d<\x89N\xf1b\x16,\xc7\xaf\xfe\x8a\xfa\xd8\\8\xb7bbv\xf2\x99\xcf\x96\xf5X[C\\\x85\x89\xecb\xdf\xbe`5\xa8WeF\xb4\xa3\xceK\x11)l\xc1\xfe\x1e\xbb\xbdW\x08Q\xfa\xf8\x81\xc9\x90\x81\xbeI\xae\xbe\xb5r\xaf\x1aLJhh\x97\xa28\xb0\xb1\xe6\x86N\"\x87\xe6^\xfdGy\x13\x8a\xb5+\xbd\xcdX\xbb\xa8U\xa5\xb5\x8f7\xa8\xa4)\xdc\x11\x12ik\x84h\xb2ALf\x14h\xd3\xf3\xb6 :\xa6\x01\x020%\x7f\xc4fR\x9f\x9e\xb3\x15\xaa\x939\x0fC\x13\xa3\x1dr\xd6)\xaa\xe0\xf50\x98\xbb\x81\xfc\x9d^\x0ci\xa7;O\x03r\x1c$\xc7\xe5>7.\xcfCw\xaf\xe5;\xb0u,r\xd2\x98\xf7?s \x82\x97\x9ez\x86\\/=P'\x16\xf4V\xfab#\x83T\x9a\"M'A\x189i\xb5\x9bkB\xb3W\x8c\x92(\x0c\x12\x94h^\x100f\x96\"\xb9\xee\xc8\x95[\x82\x9eXN\xa3\xa7u\xc6\xaa\x96,\xec\xf8#I\xedt\x9d\x80{\x0fOeJ<\\\x07n\xe8\xac}\x140\xb9]\xe3\xd8d\xf6X\xcf\xfeH\xaa\xce\xcf>1\x9f\x0f\xcd\xcf\x93UY\xef\xbe\x8e\xfc\xc9\xf36\xb78o\xf5?\xd1Zb<\xfd\xe3\x8f\xc2g\\o\xd3\xf5\xed\xf8\xc1\x0d\xb7\x01\xec]2\xca\x18\x05.\x8a\x91;+9\x80\x9b\x7fE\xa0\x93\xbf\xb9\xcd\xa1\x8f\xc75C-\x10\x9a\x91\xa7\x1c\xa8d\x9e\xd1\xef\xf7\xd1q\x9a\xe1\xf6\x9dT\x1aW\xa9\x85\x9dEThY\xc5t\xa2\x038\xad|g\xc9\xedg\x90\xdc>\x1c%\xf0h<_\xe8\xfd\x89\xe2\xbd'\x15\x89\x9a\xd6\x14\xa9\xf3\xe7h\x13}\xd8qd\xcc\x0d\xddy\x82d\xec\xce\x95\n1'T\xba:N\xd3\x8b\xc5BxbN\xb8\xd3\xaaeSW\xf3\x1b\x0e\xed|\xe4+\x0e\xdd\x93G!\xa9\x0ej6gl\x9b\xfd\xfa\x96\xb7TP\x15F1w\xa6\x0b\xee\xfb\xcc\x95\xef<\xa2)69\xb3\x9f\xca=\xce\xecwx\xe7\x93{\x98C\xab\xe0c\xb5\x8fV(H\n\xf1\xb3\xa0\x83z@\xfd\xa24\x06\xd5/\x89ae;\xd6\x8er\xcd\x15'\x18\x1at\xf3\x96\x86\x16\xban\xb1\xdc\xcf\xba\xddAr.y\xe5-W\xc5{\xc0\x9d\xd0\x05\xd6~2\xf4\xdf\xbb\xbe\xe7\xc4a\xfe\x80|iN\xe9!\xbb\xeaHN_g\xce\xe8\x0c\xd8\x13\xd6Y\x1f\xc8\xdcQ+\xd7y\x89\xf8\xc4S\xee)\xe5\xca\x138tJZj\xe8\x8ezc\x138\xed@n2\xf2\xc6&\x0d\xf8\xd1K=\x8c\xbd\xb5\xdf\xf9\x82\xe6g\xc4\x84/\xe9\x97L\xc4P\xb6\xd9\xd4\xeb\xc5\xed\x90\xdb\xdb+r \xc4+\x88\x88eT\x8f\\\xf3\x9bE6\x83\xdaG \x8ej\x83\xa7\x95\x98s\x1a\x96\xe0P\x13\x07\x93\x8bX'n\x9e\xbe^8i\xa7XQ\xba\xbf+\x1dLzr\x13\xbe\xe7\x92\xa7\x1a-\xb5\xe2\xb8\xb5U,,N\x88D[\x94T/`\xeat\x93a\xd6\xcb\xcf\xe6T\xa0\xe0\x85\xb9\xd5l\xd2\xf8p\xe5\xb3\xe5\x89J\xe2x\x7fq\xd1\"\x9bW\x9a1\xc1x\x8e\xa37\x91\xed\xbc_'\xa9\xb7\xd8W\xe3L\x8d}\xaa7\xfei\xce\xd0\xa2\xf4\xfaQ\xdbH.\xa6,3uD\x8f\xd1\x81\x1e\x03'\xf2,\xfdEs\x18\xb5\xce\xd9\x95\x8c\xa5\xa7O\xf3\x13\xa6g\xc2\x13\xa8T\xb1\xc0\x1fO\xe8\x11\x12-\xcc\xd1\"\x8c\x91 aI\xb5\x93\x8e\x9a\x88Dm5\xdb\x11G\xc8\xb5\xbcG\x01\x07r\xeb \xec<\x0e\xd3\xfc\x87\x8e\x91t\xbc`\xe1\x05^\x8a:\xd94n\xc7g\xc4%\xcf\xc9\xf1\x14\xcd{\x12\xb8\x04x\xb1\xf7i\x9d\x15\xff/\x0e\xbe\xe6\xf3b\x1aF\xe5\x9e\x039;\x0c\xd8{\xb1y\xa6\xa9\xf6\xf3S.\xa0\xff\xfb\xbf*\xf2\x07\xb4_\xc4\xb6\x8f\x92N\xd5\xb0C\x1a\x02\xf7\xa0\xf3R\xf4\xa3\x91\xae\xe3\x80t\x1a\xea\xf9\xbf\xff\xfd_\xcf\xccO\x14\xec\xe7&\xa5N\x93W\xc3\x9c\x02I7\xfb%\x0eq\xa2\xd9\x8e\x83\xa2\xb4\xda\xac)\x87dj\xf3g\x19#\x14<\x85g~\xf5\x83\xe0ED,\xdd!\xf2!K\xcc\xb1\x17<\xa0\xf8`\xe9/\x9a\x17\x86P\xba\x15 H1\xcbc\xb5\x9d\x95y8\xba\xab\xda\xdd \xcc\x93 u\xb8\xe1\x05\xdc\x92\xb2\x06\x9d\x81O\xcf3\xa7\x83\xce\xfaU\xb7\xba\x8b\xea\xeb\xdf$\xc7\xcf6(N\xbc0\xd0\xa2\xd8^\xfa\xf6\x81\xdc\xaa\xa8\x83K\xe4\xb3\xe9?\x9a\xea\x8f?|\x94$\xf6\x12==\x82:u\xde#\xe5&\x06\xfcn\x0f\xf9@\xd8\xcc\\\xa0E>q\xd8\xb4\xcb\xc5\xf4\x82\xc6\xfe\xdd\xf56\xc4\x8bE-\xcbY)\x9dmTb\xde\xc9\x171Mt\\m\x97\xba(\xfbS\x8b\xdb\x8fv\x9d~\x11\xf6\xb2\x8bN\xba\x9ay\x1a\xb4\x9d\xb5&\xaf'\xf5\xc8\x83\x9a\xec\x19A\x93?6h&\xfcH\xbc\x8c\xed\xbd|\x05\x9as\x89\xec\x18\x05\xe9s_e8a\n\x9d\xa7A\xf6WK|\xd1\xc5\xad~\xa9\x19\x8e\xee\x9f\xae\x97\xd8s\x8c\xdc\x7fU\xef\x9b\x08\xc2\xcc\xe5p\xb8En=[uM\x8e\x90y?\x00s\xb9\xc9b\x9aer\xd7\x9fx\x04\xdf&\xc7\x0e\x1c\x84\xd9Sa\x8b\x81> \x97_e\x01i\x12\xb9\n\x0b\x0e|u\xf6:]\x85\xb1\xf7\x88\xe8\xeb\xd8\x13z\xb4\xab\xb8T\x07=\xe5\xa7?y\xe1$\xf5\x16\x89\x86\x05\x0e\xed4\xff\xb6\x0cm>p/\x9e\xa1\xdf,\x0f\x0b\x0fc\xf8\xc8e\x86-w\xaa\x80\xfe\xd9\x1f\x8fu\xd4\x03\x92[T9\xc7Q\xcb\xb8D\xa7\x0d\x9f\xe4\x8aZ\xc0\xb8\xe8\xff\xc7\x0fN4\x83r\x1f\xbcxU\x15\xd7\xb13\xadv\xb8\x03\xe2\x0c\x07l\x0b\x18\xe4\xa4\xf9_F\xdd\x95Y\xec\"\xf3\x98\xb5\x83\xb9\x18P\x0e\x0e\xca\xa2\xd3\\3\x0f\x95s\xce}\x98\xb8\xf7Y\xf6B~w\x8ef\xcc\xa8V\x06-\x0f\x80\x13}E\xcf\xfe\xb4\x89-\xbc\xf5\x0bO*\x05\xeb\xa1\x9e\xfd\xa1X\xcf\xd7i\x1a\x06\xec\xdb}\xc2u\x9a\x0d.\xbc\x02\x0bx\xd7\x0b66\xf6\xdc\x03\xbfVIV\xf6\x03\xeat\xfbI\xc7\x98\xc0O\xdb\x0e\x03\xffu\x81\xb83Fe\xd0{\xc4\xc4\x9b\xa7\x18\xac\xea\x1e:\x7f\xbc\xa7\xcc\xd9\xca\x13\xbb\x8ba\xf6\xa7\xb3\x8e\xf1\x8f\xae\x9d\xda\xe7\x9eo/\xd1\xcbd\xb3\xfcy\xe7\xe3\xc9\xdcN\xd0\xa0\x7f\xf6\xdb\xaf7\xbdo\xfb\x8b\xfe\xfc\xcbn\xed<\xea\x9e\xfd\xeb\x9d\xee\\\x86\x9bw\xa6k\xba{\xcb\x9c\xed\xad\x8d\xe3;\x9b\xd9\xfdt;{5~t}\xc7\xbb\xfe\xf5[\xf4\xedw\xf7\xd5\xdc\\\x8e\xaf\xef\xa7\xcb\xd9\xab\xe9\xbe\xf8{\xfd\xf3\xf5\xab\xe9\xf2\xfar\xb7\xfd\xfa\xfb]x\xfd\xe6v|\xfd\xa0\xeff\xfb\xbe>\xfb\xb8\\\xde\xec\xfb\xfd\x9b\x8f\xf8\xfe\xdd\xfd\xb59\xfb\xa0\xafg\xf7_\xfb\xef\xee\x9d\xed\xfb\xfa\xe7\x07\xf3\xfd\xab\xe9\xf6\xfaU\x7f\x7f\xb3\xef\xefo\xee\x97\xeb\xd9\xbd\xb3\xcf0\xb3\x0f\xf9s\xeb\xe6\x1e'\xef>\xce\xd6\xef?N\xfb\xd7\x97\xb3\xf5\xfb\xcb\x9b\xfbw\x1fj|\x9aa\x9b\x9f\x1f\xcc\xf7\x1f\xa6\xdb\xf9+\xfd\xf1\xdd\xfd\xc3\xf6}\xfe\xdf\xe5\xe3\xd7}V\x9f\x93\xbe\xbb\xbf\xee\xdd\xd4?\x17u\xbc\xfb\x90\xd5\xf1\x90=\xdb\xe5|\xef\x97\xeb\x9b\xc7\xa9U\xfd\xfc\xfe\xa3\xd3\xbf\xbe\xbc\x98\xcd>N\x97\xb3\x8f\xaf\x93\xb2m\xe9l\xdf\xdf\xdd\\\xbe\x1e\\{\xa3\x9f\x7f+\xf4\xf4\xf3O\x9d<\xaf[\x9c\xfc*b\xceN\x10j1\x8a\x90\x9d\x92\xf3ZqS\x9f{#\x84<\xa3\xd9SK|f0\x95(\xa8Y\xb9G\x11\xb2\xe3,Z(F\xa4\xfcEm\xecC\xe6w\xc0\xdd\xff\xe9\xafq\xeaE\x18\xfd\xabJ\xfeZ\xd4\xc15\x0b\xf4V\x80\xd1\x9f\xde]\xe9\xbd\x07.\x89\xd8\xcbg\xd8\xa3\xee\x94 8\x19#\x9d\xbd\xe0\xa5\x94\xdd}\xea\x99\xa4\xfch\xe1?\xb3%\xf5/\xc8\xb7=\xfc\xaf3A\xe9\xc2\xc3HX\x18\xd9I\xb2\x0dcW\x08H\x90\x1d;+aq\xb6\x1e\xa3\x0b\xb3'v\x8clRE:\x91l\xa2\x1dh\xc4\x0c\x8f\xc4\x86\xa1;\xce\xfe\xb4\x0d\x8f\x8b\x85\x9a\x15\xff\xf3\xd5\xd5\xbct&\xdf\x8a\x91\x1b\xbb\xeaO\xd2V\xb4\x81\xea\xd6\xb4\x01\xcbV\xb5\xc1\xf2\xd6\x81\xa0\xaa\x95\x7f\xca0\x00d\x8ar6\x07C\x7fq6\xd6_\x00Y\xb6:\xa5k\xba?jF\xb4\xcbF]0\xe5K\x96\xff\xbb\xa7\xbf8\x1b\xb5\xf2\xeb\xc9\xd9U\xc5\xff6\xf5\x17g\x96\xfe\xe2l\xd8\xcaQ\xeb\xb7HX\x95\xff\xbb\xaf\xbf8\x1b\xb4\xf2kaWs#3k\xff\xab\xd1g\xd1(8\x1403\x07y|\xbc\xd9\x9a\xeaQ\xb7\xe8\xf9\xd5\x137l\x92\x01u\xcb\xbb(\x8e:-\x00\xccMUK\x8aw|\x1d\xf8\xd0\x17\xb8\x1fU\x0f\x11\xce:\xe6\x0f%\x13[r\xe4d\xc2\x9c\xd5\x88QN\"P\xc0\xb3\x9f\xd9rV\xc8y\x98\x87\xbb\x03\x19\xf5\x97+Y`mD\xeez\x08\x1eW*\xd5\xb3?peOx\xfd\x86\x80aD\x1dD\xef\xeb:\xf1\xd1\x8d\xc2\x0e\xe4y\xb9J\xf3,HU\x8bP\xba\xae\x16\x85\x98L\xaag\xff\xaa\x9b\xca/\xa5\xa5t?\xe7\x8a\xfa{\xb7xC\x8f\xf0\x8dJt.K#\xf7\xcb\xf27/Tn7 \xcf\x91\x8f\xca\xedn2\x0ef\xcf|\xd0[Q\x8c\xff\xa1Q\xf6G\xf4\xb2$=_\x02T i!\x97\x08\"\xde\xf1\x90\xf7\x83\xfa\xa7\x13U\xd7\xfe\xca_\x85WFKk;\xcf\x7fB.e0^Y\xf9\x1a\xf8/\xc0\"\xd8Y\xd9q\x82\xd2_\xd6\xe9B\x1b\x9d\xbd0_%\x9be'\xb7\xe0/?\x18\xfa\x0f\x9d\xc2\x82\xbf\xfc0\xfa\xa1\xb3\xf1\xd0\xf6\"\xdc\xfd\xf2\x83\xd9\x19v\x0c\xbd3\xfa\xa1\xb3\xf3q\x90\xfc\xf2\xc3*M\xa3\xf3\x97/\xb7\xdbmwkv\xc3x\xf9\xb2\xa7\xebzV\xc7\x0f/\xcc\xab\x17\xe6\xab\xc8NW\x9d\x85\x87\xf1/?\xbc\xe8\x99}\xa3?\xec_\xfd\x90?\xd0\xe25F\xbf\xfc\x806(\x08]\xf7\x87\x8e\xfb\xcb\x0f\xb3A\xd74\xcd\x8ea\xbd3;\x86\xd1\x1d\x0c\x86\xd8\xc8\x9eh\xd9\xbf\xfdN\xaf\xd3{W<\xce\xc40;\xa3\xac\xec\xf1\x87\x97EMY\xa5/\xcc\xab\xbf\xfc\xd4\xb1\xf4\x17\xcdZ\x93\xd6\xa8\xeb\xd98\\j\xeb\x1d\xf35\x9d \xf9\xa2U\xea\x1e\x8b^\x1dV\xaa^\x03,`\xd8\xe9f\xbaw\xe30\x02\xb8K\x19\x8an\xc1\x8c~\x12V\xe5\x87\xae\x8d\xa9z\xea-m\xae!\xd4\xfe63)\x16\xbf\x9a\xe5\xdcP\x7f\xf3\xc3\xe2\x86\xe2\x937\xf8\xf9\x05JuY\xafm\x81\"\xc8\x07\xe8\xd1\xaeS\x9c\x9c\x92\xbe\x04Z\x8ckUj\xb5\xb1&;\x06g\xf5\xc90\x82O*J\xd8\xd2\x17U\x80{6U\x9e\x9c\x9fk\x95V\xb8\xd2\xba\xe9K>#f\x81=h\x16\xd8O8\x9a\x04\xd5\xff\x94\xd7\xce\xd5\xb1J\xaf8/':*[:\x16\xe96'\x9d\xffQmM\xa7\xeb\xe00AZ\xfe\xf8\x88\x94\xfc\xf3e\x9bd\xc2\xad\xc8\x0f\x83\xf7\xd8c?\x03\xf2\x0d^\x8d\xe8\\\x1eN\xb4Ir\x82[\xf8\xa1+O\xef\x98\xfa\x91g\xea\x85\xb5t\xba\xc4}\xd9$\xb2\x99\x1b\x11<&u\xabc\xb9\xb6\x9e\xfd\x11\x9d\xcc\xe5(\xff\x9e\xba\xcc\x8dK\xf5w\x0f\xe5\xcc\xb44\\.1b\x8fh\xc1\x81\xd7@\x14x\x95\xa6\xccF\xa9N\xd7D\xbe\xc2\xebo\xb8\xe1]\xf8*`u\xe4\xa9\x08\xe8C\x0e$\x03~**\xcf\xf1\x8cu\x17-\x81\xf3=\xe5s\x8eN\x0bc/\xcf\xa6\xe9/\xb2(a\"*\x10\x1b\xaa\xeb\x84\x18\xdbQ\x82\\\xf1\xa9#\x81P\xf9c1\xe7\xf2\xac\x1et\x02\x8d\xdd\xc0\x12\\\xa1=*\xd2k\x0f\xe0\xaa`\xb0\xd7o\x82\xc1\xec\xe7:\x1a\xcc\x83\xea~\xa7\xd7'c\xbd,\x8c3\xf4\xce\xe0\xdd\xa8k\x8d;\xc3n\xdf\xe8\x18f\xd7\x18v\x8c\x1e\xd6\xfa]k\xd4\xe9w\xad\xf1;C\xef\x18#<\xd0\x06m\xf1\x1b\xb7W\x90\x05/\x90\x16\xef\xd7~\xa4\xa5a\xfe60`\xe1\";\x01\xc43\x10\xbfz\x8a:;\xa8u\xfb\\g\x03-\\\xdc\x87\x97\x1f\xe3$\xa0\xd5\xbb\xa5\x8aG+/H\x0f\xc4!\xbb\xfcG\xf6cc\x04T \xab\xd1\x1d!\x7f\xc2\x9f\xe3\xab\x86\xff\xae\x81\xfcN~\x14\x08\xf8\x1eo9<\xaa\x04od\xb85\x84\x1c\x9e\xb8D\x95\xad\xfb\x99\xc3F\xe5\xc9\xb2\x02\x9a\xd4W0ub\xf2\x97\xbdR\x9a\x97M\xc2\xbdz\xc1)1{\xeb\xfc\x0b\x0f`\x9a,\x96b\"7Qh\"\x7f\xef5\xcd\x9e \xd1\x9e\xe5-\x86'\x85Ap\xb2\xe8Y\xdf\x13.\x0f\"\x06:w\xbc\x86S\xd5\x13_\xa3\x0d\xf0;\xe9\xcd\xde\x1c\x9f\xe3\xde_\xce\x92[\xac\x07\x90\xddEo\xdd\xf6\x02\x0e\x0b05\xa8\x0d\x99\xf9\xeaQ\xda\x17*F\xc0e\x97\xfa\x82\xc3Q\x1f\x1c\x02\xde\xc6\xa7>\xd8\xb0\xdf\xeej\x91\xb5\xc5F\xc3\xe3\x98\xd1Q \xf1\xda\x90\xa3\xb8\xe4\xa7\x83\x18&\xad#\x12\xc7\xa6|\x90\x08\x0cLM\x0b\xa3\xfa\nVf\xab\xe6\x15;\x96B\x85\xf3pw\x90\x1e\xdai`T\xc2\x19\x8ca\x95\xcd\xcc\xbe\xcc\xa7\xae\xe4\x08\xb7\xe6Ni\xd5L\xba\xd0\x0b\x87,\xf1\xa4\xce\xf4Ty\xcf\xb4\xf4\xec\x0f\xc4\xac\xa9U\xdb\xdaq\xe0\x05K\x903\xb7|\xab^\xdcR\xddn\x17\x1fV\xe4_Q\x97\x8du\x7f\xcf\xfe)\xa7\xe5\xee<\xb6\x1d\xa4\xe5\xabZjF\x84\xceBEq\x18i\x81\xed\xb3\x87\xb8\xa9\x15I#\x1d@\x9c\xfbx\xa5\x18\xcb\x06\x10(X\xfb\xb2\x0b\x8f9(\x0b\xb1\xed\xf4 \x9e4\xba \x8a7(\x16\\\x1f{\xb6\x0bYd%\xa2\xebW\xf47f@\x06\x9dU\xbf[\x9d%\xaf\xee\x1e\x94\x01E\x8fUcE\x92\xdas\x8c:i\xf55\x16So\x01\xba\"\x9b\xd5\xd2eQ \xf8\x85\xdb u\x1f\x82H\x82i\xc4\x9dNy\xe5\xf0\xeb\xfaKWik\xa3\xdb\xe1^\x0eE\x1c|\x87I\xbbN\xe8G\xeb\xack\xadc\\\x0f\xcd\xfc\x91~\x10_\x1cC\x07\xf5E\x9c\xaa\x9d\x88&l\xce\xf5\x978\x9c\xdbX+\xea\xfa\x8f\xbe%*\x90\xb4\xd6S9\x00\x92g\x9c{\xd50$~=S\xf5\xaa/\xc0\xdd\xcb1C\xe0\xed\xb9\x03@/\xc3\xa12nZ\xb5>?\xaf~\xe0\x99\x94\xc3]\x9a\x9fLJ\xe3\xac?\xd4\xbcX\xafg?\xd6,`\xc0\xf8tu\"\xa5O\xbe\xe2\xab\xd8\x84\x82ZU\xde\xefN2IZ\x12dp\xa7|j\xda\xac\xec\\\x80B\xaa7\xb7)\xe9E\xa2\x91fl\xe9Q{\x0f\x03\xe2\xe6 \xf0V\x9f\x92m\xfe\xea\xc6\x9c\xed\x99\xact\xd5vz\x8cI%\x13\xd7b\xf2c\xf2\x8a\xeb\xb7\x9e\xda\xa9Bf\xae\xaa\xbe\x8c\x93\xb0/\x93\xe0\xce\x02\xc1\x1f\xd52\xf9\x17>Ix\xd2\x97\xcdJ\x86B\xfa?\xfe\xc8grI\xc4\xd1\xd7O\x99\x14\x99\n\xba1\xfa\xef\xb5\x17W\xaf\xc7\x11\x0d\x12\"*\xf86+\x1c\xe0i\x03\xfasCM\xca\xac\xe2\xf6\x97R\xf0\xf2e\xd0V1\n\x0e\xd8o\xae6\xb2\xa0]\x8a\x82\xc4\x0b\x99l2\x81\xf0\x14^\x9csLW\xe5?\xccBT&|m\xfe\x13+\x8d\x91+V\x81\x1f\xa5\xfb?66^\xa3?\xf8\xc4\xb5ID\x03\xe5\xda\x91\x8b\x0e\xb8\x17\x0cJ\xb9\x97\x93=\x15L\x0e\x8f\xe2\xd0\xad\xee%5\xc1<\xffjH\x8c\x80\xab\xee\xfc\xa6^\x1aFs\x9b\xfeb\x0dpE\xa7|s\x0eDZ\xfd\x17~\xcd`\x89\xb1O\xdb%{r\xbe\x07\x14\x98:U\x95\xe7\x06\xd9!U%WB\x8eb\xf9^3\xbbIR\x1c\xb9\x90\xaf_\xd8cD\x95\x84E\xca\x06\xd8\xcc\xe2#\xd1\xca\n\xf5+J\xd61\xae_\xd3\xf7d\xad\xe7m5\x9b\xd6\x9b\x93\xea \x01\xca/r\xa2\xc0e\xaevfO\xd8{\x9dy)\n\\\xf56\xb4\xcc$\xa5\x86\xf8seV\x7f\xb8\x80\xbeJV]h\x12\xdf*\x91\x8b\xd3-f!\xed\xf4\xb3WOw\xeb 8\x99\x0e\xa8\xe3p\xa76\xa9\xbcgG\xcf\x9aJ\x1d\x82\xf6\xd2<\xc0\x92\xbf\x19\xf2\x18\xa1\x8a\xa9\x9f\x93\xa3\xd7\xc8\xd1\x9b\x94\xff!\x94#t\x0b\xea\x04$\xb0\xee(\xcf\x0dR\xbf\x1f#<\xf5\xb4\xbc\xd5$\x89D\xc88\xae_\x1e\xf2\x90\x9c\xe1$\xae\xd5Q\x8b\xa8\xb2qG\x0e:^\xb0\x08\xeb;\x1d\xc0K(\xb3\xf2\xce*\xbf\xee\xd7\xf5m/`\x97urt\x87=\xc4\n\xc0\xb1w\xc6?\x8c\x80g\xc5z\x89\xe0w\xda+\x0f\x0b\x19\x0d\xa0\x02\xf6\xf3\xc8\xc5C\x13z\xd8\x87\x1eZ\xc7\xbf9\xa0\xa0,\xdenU\xad\x8f\x8b\xdbb\xea\xe9C\xdd:\xf2\xa4.\xf4\xee\xf7\\\x0e\x9b\xd5\xeeQ\x1b\x11-\xb6\x80\xae\xc9\x16\xb5\xd2\xef\xbc3\x16\x83\xb1\x03xay7\x9f\xdc\x9f\x02\x98u\xe7v\x824\xe0\xe80\xa9\x0b\x93:\xdbZ\xcf#G)Qh\xcc.\x9bF5\x07O{w/\xc1\x95\xff2\xaad\xc1`\xb5\x1c\xae(\xd6\xef\xe4\xcb\x9d{\xc5\xc0\xc2.\x8d\x93u\xc4\x1dd\xb5\x86\xcc\x01\xb7\xa1;\xea\x8f!\xf3\x92\x92\xe7\xaf\xdbST\x057T\xd9\xebt\xa5\xcd\xd3\xe0i\x01\x0e\xbd6\x7f\x8e\x17U\xc8\xa5,\xeeK\xbba\x80\x0e\xf2\x14rN\xf8\xa4\xa6)M\xd4\xcf\x1a\xbb\x912w\x88\xd7\x040)\xd0&4\xd1\x9a\x97\xe3\x01\x9c\xc0\xe4\xa1\xc1\xdeo(\xd2\x89-\xa7\xe6d\xdc\xe1M)a\x1dl8E3#v\xcd\xcbc\xffV\xb4\x13\x1d\xb7bH\xeb\x8f\x8e\xf3\xc1\xbe\x94\xae\xf5&\x9a\x84\xa0\x08\xa3\xd9\x1b\x90R)Q\x1c\x87q\xc2\x0e\xa8\xd4\x06\x18?Y=y0M\x9c0BIg\xd5{\xfa\x94\x9f\xb3\xd2\\\xb4\x90\x1f\x8b(\x1b\xaa1V\xe9\xc1\x0eXu$\xe2\x92\x9acc\xf4)b^\x80E>\xe5C\xd2\xea\xfaZ\xebd/\xf9&\x15-v\xf9;\xdb\nx\xd3\x0b$e\x8fl\x08\xdf=\x7f\x92]\x05U&\xc4\x8b\x9f\xc0M/\x86\xae\x882\x9f>P\x9e\xb4\x06S\x90\x8c\xd6a\x8f\xba\xac\xa44P+\xb99t\xc7\xb1\xf0\xb7\x03x9\xad\xbc\x971\x02\xeej\x8c~\x9a4\xaf\xc6\x02\xdfAV\x00\x0d\x9e\xd6hH\x0d\xfav\xe0\xff\xb4,\x94\x9d\xee\xf2kaq\xb7\no\x9aTZ\xe5\x1d\xf9J\xef\xff\xbc\xfc\xdb_;I\xb8\x8e\x1d4\xb3\xa3\xc8\x0b\x96\x9f\xee\xde\xfd\xd20\xea:I\xd2\xf5\xed\xe8o/\xff\x7f\x01\x00\x00\xff\xffPK\x07\x08_;\x94/\xe8Y\x00\x00\xa8X\x02\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00 \x00swagger.yamlUT\x05\x00\x01\x80Cm8\xec}\xe9r\xe3\xb6\xd6\xe0\x7f?\x05J\x99\x9aN*\xb1-\xc9\x92l\xb9&3\xe5\xb5\xdb\xe9\xf1\x8d\xd3v:\xf7\xe6\x8f\x1a\"!\n\x16 \xd0\x04\xa8\xc5\xa9T\xcds\xcc\x9fy\xc5y\x84\xaf\x08p\x01I\x90\")\xc9q/\xfa\xbe\xcauK8\x07\xc0\xd9\xcf\xc1\xb6\xbf\xbf\xbf\xc7\x16\xd0\xb2\x90w\nZ\xdd\x83vk\x0f\x93 =\xdd\x03`\x8e<\x86)9\x05\xad\xa3\xe0k\x008\xe66:\x05o!\x86\xfb\xff\x1bs\x04&\xd4\x03\x17\x949\x94\xed\x01`\"fx\xd8\xe5\x02\xe4\x0c|\xb8\xba\x7f\x00\x98p\xe4M\xa0!\x9b2\x0e9\x02O>\xf20b?\x01\xeeA\xc2\xa0\x11\x00\x00\x0b\x11\xe4A\xf1'$&\x18{\x14\x9a\x06d\x1c\x13\xeb`\x8fC\x8b\x05\x03\xda\x07\x04:\xe8\x14<$\x80A\xc7\x99\xae\xef\x11\xf4\x8c\xe9O\x00\x11\x83\x9a\xe8'@\xbd\x04\x9d\xda';PQ\"b\"\xcf\xc1\x84\x83\x0fw\x17y\xa4\xca\xefgw7\xec'\xc0|c\n \x13\xb3Y\x81\xb1M\x8dYzJLLd\x0emlBN=\x86\xb8\xd2\xdd\x99\xcf\xa7\xf9N\x82o\x11\xe1\xd8\x08\xc8\x04\x0d\x83\xfa\x843\x05\xea\x1c\x92Y\x1e\xea\xc2C\xa2\xbdJ\xb6\xd4@\x14\x0c\xf7\x1c\xce0\xb14D\xe3p\x86\x80CM\xdfFb\x86\n\xd0[:G\x1e\x81\xc4@y\xb8\xe4\xb7\x02\xe0{\x1b\xb2\xa9\xbe\xcb\xf0\x97\x02\xc0K\xcc\xb8\x87\xc7~\xd08\x0f|\x8d\x100\x95\x16E\xbd\xfb\xaek\xaf4}\x8b\xef\x0b\x80B\xb9W\xbe\xb9\xc5\x84\xe7\x91\x04\xdf\x16\x8f\xff\x163#\x0f\xf3\x9b\x10\x17\xe8\xbaq/\xcc\x98\"\x07\x85\xf2=\xe5\xdce{S\xca\xf8i\xa0-\x9e\x059:0\x84\x86\x1d\x10\xc4\x17\xd4\x9b\xed1d\xf8\x1e\xe6\xabK4\xc1\x04\x0b\x0e\x07\xc03G\xfc\x0f\x00|\xe5\xa2S0\x86\x0c\x1b{.\xe4S\xf1\xf5!\xa1&\x1aE\x9a\x0d\x80\x85\xb8\xfc#3\xbe\x1b2\xa1\x9e\x13*\xe2\x98\xfa\x1c\xf0)\x02\x06%\x04\x19\x1c\x99 @\x13\xc21\xdfq\xa0\xb7:\x05\x0fS\x04\\\x8f\xba\xc8\xe3\x181@'\xc50\x91&\xcb\xcf\xbeN\xed@\x80\xcb\xf4\x0d\x94j\x08]\xd7\x0e\xf4\x02Sr\xf8\xc8B\x89\x00\xc0C\xcc\xa5\x84\xa9m\xbb\xedv\xf2\x8f\xcc\xec\xfeEM$\xac\x90\xcf\x94&\x82\x03P\x05\x8a\x88H\xc7\x8f\xc8\xe0\xa9\x1f\x92\x89\xa6\x01\x80:\xc4Qd:3M\xca\xc0\x83\xcf\xd8\xc7\xb69J\x13I\xfd\xc8Q\x05R\x1f*T\xfac\xd8\x18\x11>\x12\xf2\xd7\x08\x9e:\x0e\xe6\x8d@-\xda\x08\xac\xf1P\x19\xf2\xe6\xc8k>\xd5B\x06\xad\x81\xcd\xa8\x91\xfa)g-6k\xf7\x15|\x1cJ\xf0\x0cy\x8d`\x01@K\xe8\xb8\x81\xcb\x8e\xdd\xd0~@1M[\xd7\xa3\x9c\x1a\xd4.\x96\\\xb0v\x8a\xa2E\xd7-\xfa\xa9\xc2x\x81:\xe6\xe3\x82&\xc2\xd7n\xab\x93N\xbb\xa0\x0dt\xb76\x11]\x17\xa11\xdf\x94\xafV\x10\x8auu\xaa<\x85\x84 \xbb\x99\x1d\xb11\xe3\x88\x8c\xa0in,x\x9da\xf7\xa0389\xe8\x0f\x0e:\xa7\xdd\xc1\xa0?\xa8\xab\x8cEaX\xe2\xa5\xf3\x9f\xea\xe3k\x1ft\xfa\x07:\x06Q>-R\xbb\xd4\x88\x1c\xea!\x80\x15\xafII44V26\x8dg\x89>\xeb\x95\x8c/G\x98\x98h\xb9-\x01-\xa0\"\x00\x9ek\x08)@\xacp,u\xfb\xe2\x86{zx\xd8>\x10\xff'$\"\xd1\xf3~\x89\xeb\xbe\x86\xd8F&\xe04\x8c\xb8I\xca\x95\x1f\xb2\x1510\xb1$\xb8\x12\xdc\xc4A\xca\xbdl\x10\xe6 tR;*I\x87\xbd\x88\x03,\xe3\x1c1\x10\xcc\x80\xe1{\x1e\"\xdc^\x01\xb6\"AO\x0b\xcc\xa7R\x8cD\x9bH\x1av\x1d\xdd(\xf3\xdcE\x94\x93\"\xb4\xfa \xc3NJm\x04I%\x96\xde\x0b\x1f.sD\x02m\x80<\x8fz\x013e6uhC\x8e\x18/fi\xc0\x84\x80\x03\xb2\x9d\xf4\x0bu8\xba;N<\xe8G\x05\n\xd8\xf0\xdf<49\x05\xad\xef\x0e\xcd$\xa4?<\x0f\x00E\xba\xd0\xda\n5\xff\x9a\"lM\xf9\xdf\xe5\xf4\x84r\xc0\x00\x06\x7f\x1a\xc8\xe3\x10\x13 !wDZ\x17z\xd0A\x1cy\xa9\xc6\x98\x9c\x82 yQf+\xd3\xaa\xd4X@\x96\x08\x82h\xf96\x1ez\xf2\xb1\x87\xccS\xc0=_\x0d\x7f\xa4\xd0\x12\xdf\x19#O\xf9z\xb9\x9f8\xb0\xe6\x12\xa0P\x92\xb9\xc8\xc0\x13l\xe4\x87\xb6\x91<\xf4\xda\xbd\xc2\x11|@O~,\x80a\xbf\xc0\xa4\x88\x917\\AP<\x85\x1b\"\xa2\xc6\xec\x90\x1b\xc9\xa0Z\x06\xa9\xad\xd810\x90E\x14\xb0\x03)l\xc0\xde\xd4\xb0\x026gmQ5f7\xb0\xc1\x02\xfbHb/2\xc4Z\x7f\x9c\xb0\xa1\x08\x0cz\x1e\\\xe5~\xc3\x1c9\xda\x08@+\xa9 />F\xfdmh\xc2\xd2\xe2S\xd5\x92e\x18\xf4\xcd\x9cmf\xcer\xf2\xae\x98\xb5o\xf2^$\xefe&\xfa,\xb3?\xaf\xd1\xba\x9c\x00*Zw~q~\xd5m_\x9d\\\xf6\x06\xc7\xfd\x93\xf3\xe1\xe0\xec\xaa\x7frrt~<\xec\x9e\xf4O\xba\xc3\xc1\xd9E{p\xd5\xef\xf4\x8e\xfa\xbda\xfb\xfa\xfc\xf2\xe2\xec\xaa\xdb?;\xee\x9e\x1f]\\\x1c\x0f\xce\x1b*\xedR\xe6 \\\xd6g\xe7\xd8DfvR\x95\xc3\x8f\x87e\x8dX\xf4&\x12\xaeP\xd8\xae\x14\x19\xcb W\x05\xe1I\xb28\xb1\xb6\x93]\xdd\xc8\xf5\xafi\x16H,\x9a#\xc2\xd9V\xe5J$\xa39\xc1r\x10c\xd0B\x07\xb2\xef\xf5\xa2\x92\x1a|KU 9\xe4x\xb5\xe9M\x1a\xf3\xcf\x0c\x11\xf3\x0dXL\xb11\x0d\xa4\xc3\xb79\x03\x98\x08\x8eO\xa8m\xd3E\xa0P\x88\x98.\xc5\x84\x9f\x827o\xaf\x1e\x04\x13\xfe\x97\x0eOK/\xbc\xad\xe0\xc7V\xady3a\x117\x99w \x14Rz%\xae5c\xff\xef\xe9\x9e\x7f\x96K&\x9d\xc1r\x85\x1c\x179\xae;\xec.\x87\xd3\xd5\xf3\xf3p\xe1Y\x93a\xcf\x1b<\x0e\xa7\xfdI{\xe0.\x97\xf3\xc2\x89\xd7\xc2R\x8dB.\xb4P\x11\x01\xee\xa0\x85\xf2\xce[\x92.0\xd7\xd6:\xa7^\xde\xb5\x8d\x1d\\\x18i\xdc\xc2%v|'\xec\x1e\xd0\x89\xf4\x81\xc0E^v\xcc5\x06T\xd3`\x9d\xd96\xe0K\x06\x1c\xc8\x0d\xb12\x98\xb2\\R\x15\x14\xd8\xca\xb6\xeb\x0eZ\x98@\x8eLa\xc2\x1e\x96Lu\xcf\xeb\x9d+\x93\xd6$\xd3\x7f\x13\xfb\x07\x80KY#\xc3w\x1e\xaf\xecB\xc0\xb0E\x90 \xf8rO\xd3\xb5\xb6!\xe0\x14@0\xf1m[-{\x19\x940\xdf\xa9f\x00\xb7a+\xc7\xd4\xccK%_\xc6#.\xa2f\x10|\xf2%p\xfc \xb1B\xc9\xbc\xee\xb9\xf9\xb0<\x10?3\xdfu\xa9\xc7\x91\xba\x04\xeeP\x13\x05\xd6\xd0\xb0}\x13\x81O-\x11E\xb6>}\xef!\xee{\x04\xc0 G^\x80W\xaez\xfd\xf0\x13\xf8\xd4b+b\xa8-\x90\x07.\xa6\xc8\x98=,\x7f\x10\xeb\xeb\x9fZ0\xdd\xc4\x13q\x1b\\\xc0\xd5\x0f\x07\xca\xf0\x0bC\x85-\x05\xc4\\S\xfa\xd5J\xbe Q+\xd36 LQ<\\P\xc6\x8d5[-g\xd5\xd4\xee jT\xb6u\x84\xfe\xaa\x89>\xc7\x12\xf3\xb0\xbc\x10\xcc\xfb Pm\x1e\x9b\x1c\xca=#\xa7\x1b\xa9\xea\x95\xc0\x91 m9\x15\xc6\xec\xcc\xc1\x84\x82\x05\xf6\xc4\x86\x18\x07r\x9d\nk\x11|\x1f\n=\xf5\x824\xe1\x070\xf1\xa8\x03~\xb9\xff\xf5_\x01\xea1dh\xd0\xdb\x97\xa37\xc3^\x18\xf20\xb4\xf1s\xa0\x14+\x1e\xd7\x9f_\x89\xd6\xafQvN\xc3\xfd;\xaf^\xab\xeaj\x81\x9c\xdf\x02\x8a\xa0\xce@\x8c\x05fy\x05L$y\x17\x98\x19\x0fE\xac|\x99IVS}Q\xd2\xd4\x08\xda~V\xd0\xc4^/.\xe6\x19c+\xf3\xb3\nI\x1chK\xbdP\xa6\xde8\x99\x95$\xddL\x99/\x91F\x17\x85\xeeUVg-\n\xad:WR\xe3@5\x02\xbd\xff\xdc\xd4Yr\xe3\x1fV\xe7j\x92~??\xc7\xffF\xbd\xf7wO\xf8\xf1?\x7f\xd2\xf7\xd7\xd7\xef\xae\x9e\x7f91\xba\xef\xee\xce&\xf3\xee\xd5\xf5\xef\xc6\xcdt\xd9^\xddAkq5}X\xb5\xe7wg?\xbe};\xbd\xb8b\xf6\xfb\x7f\xc3\xde\xd9\xa4\xed\x9f\xff\xe8L\xee\xa7\xf4\xf6\xc2\xfa\xf8l\xbe\xbd\xf6\xfe\xfcpsy\xfbp\xb6\xb8\xb2~\xfbm\xf1\x0b\xbd\x0d{\xdf\xae\xf5XC\xc4\xf5\xc6\xec\xe5\x15u\x0c\xc9\xecp\x0cmH\x0c\xc4\x0e\xff\nW\x9b\xd7\x94\xa0\x02\xdd\x0b\xf7G\x82\x08\xb6@\xb9\xe3=\x93[\x92\x7fm!)\x1cu\xd1\xe4\xcf\xc2\xa1\x86\xcd\x82\x0c}\x8c\x8c\xe9Q7m5\xc0fE\xa6Z\xd9j3\xf1;\xd3\xd3\x1c\x94*m\xbe\xc4\xaa-\xafj\x85\xf3\x82bR-\xac+\x17\xb0h3m\"`\x87\xc2\x1aOb\x0e\xab\x0eB)7\x11\x13\x18\x14\x13&\x0d4%\x89\xdc\x05)\x15\x11\xcb\xfb\xeb%\xef\xa5\x8d\xf4\x97%\xa4\xa5\xae'dG\xd1\xacDr(jB\"\xb2\xe2Ku\xafN\x95)m\xc9\x19\x05\xae}\xe4\xa1\xa7\xbcK\xd2g8\x90\xa1\x0f\xe8)\x9b\xb9A'\x98k\x91[\xdb\xc6Z\x86\xaapZ\xe3\xd0-\xf6\x0e\xaa_\x92n)\xdc\xd5\xbfs\xc7\x14Uj<\xb9\xe6\x1d7kd0\xa0\xcf\xa7\x1a\x83Q\xdd#ev\x83\x89\x94\xd9\x98B\x1c\xc9[\xd6P\xc4\x87\x01^\x8f\xf6W\xd1\x8c\xdd({C\x8f\x94\xa19\x8f\xb6_\xa8t\x07\xdbSg\x01R\xa0\x86E+\x91~!D\xc1f\xc0\xf2\x8d\x80\xa1\xb4\x8dd\xc5V\xd7bM\xb8\x0b@\xe9\xd6\xbe5\xb0\xc2+\x96A\xea\xad\x11(\xb3H\xa0\x8aUR?\xae?\xb6\xb11\x9a\xa1\x95\x1e\x9d\xbe\x1a,\x80\xde\xa3\x95\x0e#\x0b,\x081\xeal*\xafld\x90bd\x98<\x88sh\"\x1bYb\x85\xfa\xf0\xaf\xf8\xef3\xd3\xf4\xfe\x8e~\x8a\xcew\xe84^\xa3\xefR\xdbS\xa8\xf6\xb4C;\x97\xce\xfd\xcc0\xceB\x8fO'\xe02\x82\x8ba\n\xd4_K\x89\x8dT\xbfp\xb9\xd9\xb6\x81B\n\x19\x88\xc1d\x86!L\xd6\xa8\xaa'\x9dj\xd9\xd5\x9a\x06\xe8\xd7\xf7\xca/\xbb \x82/\xe3\xd9\xd7\xf3\x861\x89rV\xbdI}\xb4(D\xf6\xc7\x0e\xe6\n\x87\"\x8a\xd7\xab \xe4\xe0s\xe3\x12\xa7\x8c c\x0b\xea\x99\xd1\x19#%\x16\xf7\x90C\xe7()\xcd\xbc\xbf\xbd\xaf\xc4\x98Zf\x7f;Q\\\xcc\x97\xe2\xbd\xd5Zt\xa1\x9ef\xd1\xc5\x9b\\j\xa2\x8b7\xab\x14\xe0MXR\x11\xa1j\xa5\xcb\xd5q7\xf9\xd0\xb65\xb7\xd9\xdaC#\xcd\x04\xd4S\xe8\x1d\xc5\xb1iU\xe9\xb5;\x85\x88\xdf\xa3U\xa2\x1c\x98\x81\x85G+z\xa8\xc2\xe5\x90z\x1e\xea\xf0\xaf\xb9*N\x7f\x7f\x0d\x1e\xabd6)b\x94\xce\xe6W7\xc8\x90b%\x0c\xa6\x14\x03ogJsh\x07\xf6l\xed\xcc\xba\x8b\x1e\xe9\xda\x02\x8f\xce\x17\xcb\x93\xab\xe2d\xa7<\xe9\xa0\n\xec\x18\xf1\x05BD\xf5\xcb\"\xdf\x86\xb9\xc9\xbcZ?\xbd[\xd7\x1b(x\xb2\x81r\x1b\xfe\xb8\x8a\x82\xfadL\x89\x89\x895\xfa\x16L\xc6\xc1dL\x94\xaf<\xac\xfc=\xa2\xc3\xd6\x84|#y.\x8f/!\xd1\xf2-\xe2\xc3\xb7`\xf33\x0f6\xd9\x14z\xba:G\xb5\x05\xbbV\xa7\xdd\xfe\x16y6\xd2\xd9\xc01\xe94\xebs\x89A\xb5.\xee[4\x9a\x9a\xcd\x17\x1b\x8d\x16\xbb\xf3/5 \xd58\xed;\x88\xbd-\x18\x81\x1dE\xa7\x1e\xaa\x1cz\xa6\xb7-g\x14qKJ8\x816+\x15\xd9\xe2\xa1$\xae/\xf0\xfb\xa5\xe3\xf9\x08me<\xf7\x9e\xf1\xb1D\x89\xb62$Nk\x0c\xe8\x92\xf1\x0d\x06T\x16[\xa7\xb8\x0d\xbe\x9f`\x9b#\x0f\x8cW\xe1\xa1}\xc1y\xf6\xc3k\xd7\xbc\xcdc\xeb\x0f\n\x1d\xaa\xed_X\xabH%>\xb0\x86\x8e}\x19\xbe\xae4SH a$L\xbbH\x10\xaa\xec+\xf8:r\x01\xe6\xc5\xf7\x84T\xc7\xba.%H\xd0\x9b\x8c\xd7\x1c\xf1:\xdc_U\xba\xf1\n6fh#\x0eUO\xf5\xe9\xc6\xae\x0cf\xf6\\\xf0\x97m-\xcb\xe3\xe7\x84\x16\x80O\xc5\xa9\xee\x84Y\x98\x81 \xdc\x14\xf7\xeeT\x92\xf7\xdd \xf1\x0b8m\xed\xe9\xed\x06\xb2\xfd\x82\xe2\xfb-\xd1M\xcd\xa6z\xa2\x9b \x88\xb7;\x9b\xed\xe6\xb8Jf\xf6Y+\xe8Nun\x87\x19l\xd6Y\x94\xe5?\xc9\x10\x0cH\xcc\xe0O\xc4\x0e\xc0\xf9\n\x98h\x02}\x9b\x03\xcc\x81<:\xc8\x00%\xb6\\O\x0b9\x98\xf4\x13\x9fP\xafy\xec\xb3\xd2h`X\xd4M\xb1\xe1\xfa&\xc3\xc4\xb2\xd1g^\x04m\xe84v\xee\x1f\xb2\xd2\xb8\xd1\x16\xce/P2K\xb6s~6\x02\xb9\xb95l\xba\xdc\xfe\xf2\xe2\xbb\x9d\xed#_\xb0 \x97n%\xf9\x8aDz\xe3\xad$;\x92m\x97R\xfb\xb4\x94\x8b\xea~\xb6\xf8j\xe0\xe0\xcb\x10\x05\x08P|\x1e\x0c\xacUP\xb6)eh\xc4\xe9\x0c\xe9Nq\x94\x94Ae$\xdf\x04\x12\x93\x89-H1\xb2!\xe3#\x8euW\xf7W\x81\xaf\x05\x15\xa4`\xb2Cq\xcf\x0bc\xc1\x00<\xc4P\xbd\x9b\x04]\x0f\xcdG\xe1\xdck\x15\x8f7\x93\xdf\x8c\x95\xad(\xc5Rp#X \xce\x1e\x15\x9d\x8f\xfe\x9c\x858\x91(/\xe0\xb21\x85\xc4j(S#\x07\xd6\xbb.@\x81\xc4\xf5$\xd2\xa2\xd0\x0eE\xa9\x16\\\xe2\x8dk\xab\x8e\x03\x97\xa3l\x19#\x0f\x9aOSA\xa8\xf0#\x13\x11\xeaT\xec\xb2\x89\xc4\x7f\x07\x1e~\xbd\xfc\xf5\x14\xfc\x81\x00A\xf2\x9a\xf7\xb0\xe80\xc1K\xc0\xa7\x98\x01\x8f\xfa\x1c\x81\xc5\x14\xc9\xd3}\x89\xc7\xc0L\\\x9c9\xa1>\x11\xf7i@\xd3\x0c\x12\x1d\x1b\xb2\xa9ZU\x89\xee\xc53(\xe1\x1e48\xe0\x88\x89\xeb\xbc\xbe\x0b\x14.|\x8bH\x1f\x0d\xdd\xf9\xe3\xf7h\xf5\xf7!\xc3\x16 \xe8\x1f\xbd\xff\xf1\x1dHt\xf2;9\xdb\x94Z\x06\xedE\x12\x168\x15\x0b\xcf\x11I\x05\x05\xdfi\x08T\x1dLU\xd5\xef\"bk\xb55\xfc5\xd2|\xa5\xb1\xf28S\x845eq\x94\xa6\x19V\xc7\xbf\xe8#\xb9\x84\xea\xf2\x88\x1e\x98\xa1U\x1a*\x13\x1bJ\x12\xa7\x9b\xe4\"=\xe57%\xd6T\xbe\xd5\xc5{\x06%\xcc\xf5\xc7\x9dg\xe3\xd1\xf4\x91\xfb\xd4\x9e\xfb\xddgkf\xcdzC4\x81m\xf2\xb4x&&$O}\xa7g\x1c\xbb\xf0\xc8\xefA\xf7\xb9gu\xbd\xa1\xc5\xdc'k`\x0d\x0dv4\x1b\x1a\xfe$\xe9+e\xfb\xe2\xfeC\xf3WD\x1aa\x01\x95\x1f\x13#\xa8|Y\xb4\xe4&%\xef\x86LhK\x05\xe8\x95w\x99\x0f\xaf\xf4\xec\xe8\xafCS\xe4\xa7\"\xb5Q5c\x8d\xaf\xd2Hwz\x19jO3\x82\x1c\xa0\x16\xa4\x86\xf7\xca\xb9A\xf5\x99\xb2\xfa\x95\xd7\xed]\xebXx\xdcz\xbb\x85\xcc-\xdc\xf7Xe\xa4\xfd\xbd\xa8\xed.\x82\x85MR\x96\x8cF\xc9O\xbdT%\xd1%q\xe3\x15%q\xf2\x90\x93\xccf^q\x8d[\x8a\x92\xf4G\x88\xc3,G\xbb/\xe6w\xd1\x00@\xf0(\x9fP\xc9\xe6\xa5\xa9\xa1\x88\x1bO2\x17\xd7\xf9k\x10\xecf\x93C#\x0d\xad\xb2\xe0\x91sQ\xf9;\x0f\xca\n\x17%\x82\xaf\xb9|a\x1b\x15\x08 \xa6\x96\x9aC\xab\x95\x1b\xadd\xf3yz;\x93f\x87\xd3+\xbb\xe9d\xa3\xbb\xf4\xb6\xbc\xb1e+;\xecsr%\x1e \x0d\x89\x12\x03of\x0e\x1a\xe5\x85\xd1s\x9b pEM\xab\xae\xb35\xf9W\xd1\xcc\xd7\x12\xbf \xd5Asl\"b\xa0\x11\xac\x99\x0e\xcaK\xf9F\xf2\xc5\x9c\xd1\x02\x13\x93.j!p0\x19\x85H\\\xe45\xc1`R\x7fl#\x81d$3\xbe\x91\xe9{\x0d\xea\x1etA\x82\n\xc8\xde\xfa\x04\x0cH\xc0\x18\x81O-\x8e\x96\xbc\xf5\xe9\xa7\xa0I\x846,\xae\xc8o\x19\x9d\xf0\x05\xf4\xd0\xc8w-\x0f\x9a\xa8\xf5I\xe1O\x18\x10S\xc6Gq\x07\xe3\xcf\xcd+\x15l\xa0\x95\x0f`W\x13[\xf9Q9P\x07.\xc5\x9bZ\x90\xea>U\xc1H-f\xdd\x8dAu\xf6\xfe\x8a\xdf\xa1=2\x91K\x99\xee\xcd\xd8\xa2\x08\x1dl\xffJ\xb2\x97\x0b\x10\x1a\xec|\x8d\x18\xb9\xf9\xc6\xd6\xb2\xd5\xfe\xd8\xd4\xedi\x10f\x9a\xa4\xb6\x05\x88'\x16r\x11\xc1\xee-Sq\xda:\xa7<\x95f\xa6mX\xf0c\xe9%e\xe9\xc3+\x05\x9aR\xd6\x7f(\xd1\xb4p\x0cq\x83\xdd\x8e#\xb7\x99+5\x88X\xacd\xb3\x9fT\xeb\xce\x12K\x1e\x8e5\x08F05\xa5\xf5\x9eS\x8e\x89\x95\xfa\xca\x85\x8c\xa1\xf0o\x0f=\x8ag\xbbSF\xbd\xd6\xbcj*i\xc5(p\x93d\xff\x01-\xf9]H\xb1z\xea\xab\x9c[R\x15\xa4\x99\x06\xa7\xa3\x12\x19\xc5\xa7V-\xb4!\xca\xdb\xd0b\x05\xb1F\xbc\xa6#\xa1\x121P\xa2\x93=\xcd\x88\x1a\xe2x\xbd\xc1\x8c\xdc X4\x93\xc0\xd6\xca\xcd\xb2\x06%\x1cb\xc2D\xc10\x0b\x90O\xf9\xbf\x85-\xd1G}\x07\xe7.\xa0\x1b\xb8\x90\x11`\xa6e\xd3\x00'\xdd\xc3\xef\xae\x19H\xa7\x03\x97J\xedl7\x91\xcb\x0bG,\xd1'\x94\xb8]v+\xd8\x94\xe6RMS,.\xf8V\xf2\x9c/'x\xca\x98\xde\xbf\xa2?o\xcc\x92\x8b]\xa3\x0d\xf8e\xb9a\xb6\x0d\x18\xaf\x00\x8e\xe8\xb3{\xa3X\xa0a\xa15\x8b'\xa9\xfc\xd4\xb0z\xd9\xea6\x94\xa9\xb5\xee}\xcb\x1e;f\x04\xaev3}\x03\x899L[\xa2ua9\xd2V\xdae\x8b\xe8\xa9\x8c\xa8\xa5\xf8\"'q\xdf\xe4\xa8\xa9\x1c\xdd\x85tm(C7\x97;\x93\xa1\xd0\x0f\xb1u2\x14\xb5+\x96\xa1\xa8E`zr\xac\xfa&:\x15Dg\xf3\x0c\xe3R\xf2\xa0\xa1\x98mh\xaa\n\x12\x87pL@\xee\xd5\x93o\xc0\x95\xb92]\x99\xd3\\\x8f\xe3U% U\x16\x00ud\xdf\xad\xbcVY\xbf\x13\xa9G\x94\xaf\x7f)\x99G\\+\xa9\x88\xa6 f\x7f\x05\xef\x1el`]v\xb7\xd6\xa8H\xb2\xbcAV*\xeb?yeW\x05\x8fw\xf8W,\x16k\x83\xef\xb0\xe5\x9ef\x08\xa9\x06i\xdf'\xee\xe4(\xaa\xd3}\xcdF\xa8\xca\xc8\xc2\xfd\x11ee\xce\xe2Ji\xc3Q69\x05\xfe\"j\xba\xa9_W\xd4RC\xc9\xb2\xc7\xe1\xaf\xc5~VB3*\xb0}\xb5\x9cS\x8e\xd6F\xa1\xa2\xf0^\x12\x83\xca\xdfSK\x0b\xaf*\x1a\xfd\x87uo\xdb\xb2\xbay\xc0\xfa\x91r\xd4X\xaa7\x92\xc5\x82h5\x18P\x83\x00u\xae\x05\xfb\x16\x93\xae\x15\xc9\\L\x9a\xdb\x1f@\xc5\x0f\xadO`\x82\x91m&\x8bJ+\xc4\xe4R\x11\xa1\xd1\xff\x8e\x16\x98OGs\xc4i\xebS\xf4R\xf2\x98q\x88I\xd1\xde\x80\x80o_L\x98+\xac_E\x14\x05!\xae$v\x1eG\xc5\x95\xfe\x80'\xe1\xcf\xdb\xb65/\x16\xbe\nU~\xa5\xb1\xabp\x92\x87\x7f F\xaf\x0dZ\x83V{\x9a\x8e\x93_\x8b\x1c%\x08cW\xdd:\xf7\xd7l\xa8\xaa\x8c,\xda\xd7[\xb0E@\xbf\xbb\xa0\xe1\xe8^m\xbc\xba\x91[\x8f\x94\xb0a\xa0\xaa\x88\xfd\xf6\x15\x90C\xdb^\x15+\x9e8\x91\x1d\xcf\xe5\x0d\x03\xa2=\x90o\xcc\x03\x98\xde\n\xcb\xb1\xa3\xd5\xcf\xb7\x88\xb3zX\x0e\xc0\xcdD\xa9\xdf\x07Td\xc0E\xd1\x99\xf0\xb0.\xfb=>@\xe1F\x0d\xf0&\xcc(\xee\xc4\xf6\x8b7?\xa87\x02A\x02\x90\xe3\xf2U\xaa\xdb\x83\xaf\\\xfd_Dm\x1e\x02\x82o\xe8\xc2\xb6 \xfa1[\x0eS\xab\xd4\xc5\x9e\xc6\x8a9\x1b\x17Br{U4.\xa8\x14\xec \xba\xe5h\x94\xde=\x04|\x12\x083\xf4\x02\xef\x05\x08$\x94!\x83\x12\x93\xedBB\xb7\xcd\xf5\x06\xa1\xa1\x83\xc9Koo\x8c>y\xe2\x17\x8d`m`x2\xe8\xb5\x93O5\xe1\xfe\x1f\xe2Y\xe3Q\xa0\x9d\xff3:^\x1b\xdeg\x11n\x88\x8an\xebL)p%'Q(\xa6\xdb\xd1\x1ba91\xb1j(\x8e4\xb65\xd5\xa6\x00\xe85\xab@\xb1\xa4\xf3\xa9\x87\xd8\x94\xda\xcd\xa5\xac}\xd0\xd7\xc8\x98\xfc\x04Y\xe1\x06\x88\x8f\x8ez\x05\x88\x13v\x8c\\D\xa0\xcd5\xaf\x80V\xee\xa6\xddy=:R \\\xdb\xd1\x10\xb9\xfd\xb3\x86~H\x80\xba\n\x92\x83\x92n%\xb5\xf9\xf4\xb3\xf6(\xc5\xea\x94\x9acc\x91|m\x86\xbbH\x0c\x1aJ\xa5\x89\x03\n\x8c}\xc1\xc7\xd2\xbb\xed\x17\xd03C\x1a\xe7\xe3\xd6\xd7\x7f\xb1\xed\xf1b8\xe0\xe6\xdc\x81\xcfpf.f\x8b\xae\xdf?\xeev\x8f\x91\xe9\xfb6\xec\x1a\xab\xfeq\xd7\x9e\x14jct\x8a\x8fS\x0em\x10\x12\x03\x8c\xa1-\x94L\xde4\x95\xbeQM\xa7\x9d\x11\x16\xe6;\xd1\xe1\xf9\xe0\x9f\x11:\x04=\x82L0^\xa5\x9fPY%\xf7\x05f\x9f\xe1\xd8@1/\x15\xc6\xefH5\xcb\xae]\xa3\xdeC@\xca\x0fr\xea\xd5\xd4j77=\x17\xd4\xc1\xff\xc0|jzp\x11s)\xee\xf5\x0dK\xbf\xcf$f\xa0cw3\x14\x15\x18\xb5\x9b\xb2z\xf1 \x11\xed\xe3\x0c\xf1\xec<\xdd\x95\xf1u\x8d\xf5f\x05\xe0\x17\x91\xdc\xad\xd4Z\x8be\xf7\x85\x8b\xac5\x0d\xff\xc67\xbc\xbe\x1aGP\xa7V\xf85]\xa6\x18\xed\xa0\xce\x19\xa6=\xcd\xe4\xa2\xc6i\xbf\x94\xc0H\xaf\x95uW\x15L[\x0d{\xb5m\xa5\xdf|17]C\xd8\xcc\x1c4Q\xea\xb5\xae\xac\x1as\xf3\xed\xf5\x1e\xab\xe6e\xc6\xdf\x1c\xd9\x17\xed\xc82o\xa7\xff\x93\x0b\x88U}\xdb\"\xe4|\xfaU\xa3\xcf\xd1\xa9m'\xbb\x89\x12\x91\x88.\xd0\xce\x98%m6\xa3$+oJP\x1c\x80\x87)f\x01\x87\xc5+\xbe!10\x01\x8b)6\xa6\xe2K\x9f!\x0f,\xb0x\xc0\xce@x\x8e\x94Q\x81\x89O\xea\xc4\xc85\xec\xc5\x8b(]n\xbfA#%\xdbH/\n\xdc\xc3\x07\xe4\xda\xd0@\x0d$@\x85\xac(\x05\xe1\xc1\x1a\x82\x16\x80\x92\xf8\x8d\x86\n\xfc\xfc\xe6*\n6\xbd\xe8\x8d\xd8Zl\x19y|\x11\x1d\xd8\xb2\xe3\xa1^<\xf9W\x95M%Gg7\xce\x9a\xbe\xc0<#~5\x02\xa8D\xd3\xbc\xdb\xa8I8\x84\xa1)\x80\x12\x15\xb5\x0db\xd1\x1aV\xe3E\x94%\xa6S0\xd6M\xeeG\xdc\x86\xeb\xa8*\xe0\x87\xd4\xe7\x8cCy?r\xd3\x9a\xf1\x17(\xf4\xd7(#\xb8\n\x9dbo\xf9\xf9\n\xf0\xb6S\xe7\x9d\x8a\xe87\xb1\x8c\xc5\xf2\"\xbe\x8b_lwd\xc8\x9e\xec\xe7\x0b\xd3\xa5\x82Y`\xa5\x8d\xca\x98c\x845\x82\xc1/H\xd4_\xde\x96\xaf\xab\x12\xa5n\xce}\xc3*\xadpda\xb2\xfc\x0ed \x11\x89,\xce\n\x1c\xff\x16\xfe\x7f\xb6\x95\xa2b\xe9\xfd'\x83\xf4@\x1a}\x82\xf9jT\xfe:\xcdE\xd4NS\xdd\xa0\x0eM\xd0j\x01 _\xd7.z\xdf\xac\xb0;\x9bZ\xa5\xbf\xa7\x85F\x97\x0b\xe4\xf2\x00\xad>\xbf\xffx\x07qxw[$\xe0*\xd1@[%\x90\xf8\xaf2\xc2\xe0?Y\xfa\x04\x1dVMK4\x08S\x8a\xddz\xe8\x8c|F\xf0S\xc8\xa6k \xdfA6\x8d\x9b#lMy\xb1\x00I\x9eU\x9e\xf9\x0c\xadJEIDm\x85-n\x99\xa5\xf6\x14\x7f\x7f\xa6\xaeF\xe7\xb0\xa6l\xfaXV\xcd\x11 $\xcaLU\x89\xb2\x1b]L\xe4\xce\xfa=\xc3\x87\x8f\xd6\xec\x19\xc1\xc1\xb3k\xcd\x9e\x8e\x06\x9c<.\xcc\xe7y\x0fN\x8c#\xb3{\xbc\x07\x92U\xc6\x9d\x8c\xa3v\xf9=\x08/*3$\x937j=l< \xc6\xe1,\x8aJ\xb2\xf7\xb3\x95C\xb6\xfa\xe28\xcb\xbbX\xf8r\xcd\xe3\xa6WW\xfd\xeb\xa3^\xbb\xd7>\xea]\xf4\xbb\xbd~\xbb3\xe8\x0e\xcf\xfb\x83\xab\xf6\xe5\xe5\xc5\xd1\xc9\xf5\xd9\xe5\xa0\xdf\xb9n\xf7\xf6\x00xX\x8aE\x80\xca\x93M\xcb\xfe\x9a\x11_\xb6O\xfa\x9d\xa3\x93\xcba\xe7h8<\x1av\x86\xdda\xff\xfa\xbaw\xde>\x1bv\xda\xc7\xd7\x9d\xeb\xee\xc5\xe5U\xfb\xf2\xe8\xf8\xec\xe4\xf8\xe2\xaa=\xe8\xf5\xae\xba\x9d\xe3\x8b\xf3\xeb\xa3\xf3\xdep\xd0\x1f\x94kP\xe6q\xc0\xb8\xdb\xa3\xc1I\xf8\xe5Z\x05\xcf>\xa3\x15[ \xa0\xa3\x06(\xac!\xa4\xcc;(\"\x8c\xde\x9f\x944Wi\xd9\xcd\x1d\xe6\xcb\xbb\xb0\xaa\xa8\x06G\xfd\x9e\x8a)m\xaeA\x81\x07\x02\xb5\n\x91\x89'\xba\x83V\x102\"SH\xda\xc3\x92U\x166\x91L\x8c\x0c\x9d\x96\x141?z\xe4\xb1\x11\x90\x0b-4\x92\x8d\x1a\x81*\xd9cuH\xf1\xe0dU\xa0\xa3\xc8\xe5\xf2\xe5V\xe2\x85P\xfd\x036 ]\xa8\xcc\x1a\x87\xe5\x02\x9a&\xfd\xdf2+\x12\xc4 \xcay\xadJ\xbagA\xad\xec\xe64@w\x1b\xe6\xe6b\xae\xd6\xdb\x1d\xe4\x94G\x81\x0c[\x04r\xdfk6Q\x0dtA?\xf2\x13\x0b\xcd\xed\xd5\xef\x177\xbf]\xb6\xbb\x13vy\xe7\xc1\x93[>\xfe\xc0V\xe7\x9d\xc5\xf1\xf8\xe9\xe1\xb6\xdf\xff\xc3\xef\x1c\x9d<\xff6\xbe6\xfeX\xf6~\xbc\xb8^\x9d\xddX\xa8\xff\xc7\xbf\xee&\xefo\xfc\xf9\xf3\xf9\x9f\x83\xe1\xed\xea\xe9\x1d{\xba<\xb9\xef\xdc,\xf0\x95\xfb#\xfe}<\xf8xor\xdb\xb5\xfe\xf3\xb3\xd2\xab\xeb\x8fG\xa9\x00\x05\x14\xcd\x11\x94\x16d\x05H\xe6\xbb\x92\xa9\x82\xcc\xfbM\xc4D\x9e\x83 ?\x94\x0f$\xdf#\xc3\xed\xf6\x07\xb3Nv3^&V\x8a>\x15\xfb9\x9b?\xb7{\x1f\xa7\xfc\xfd/\xd3\x93\xb3\x8b\x8b\x8f\xcf\xf6\xcd |\xa0\xec\xed\xaa\x8dg\xd7\xff~\x7f\xf3\xf1\xddoG\x8f\xefo=\xca\xde\xa9=CCX\xa7\x9c\xa5)\xedZ9u\xad\xe2b\xe8\xc9G\xc4\xa8-\x13\x12\xcb\xb9M\x8d\xd9\xcdee\xad\xaf\x19\xf9\xba\xd0\xe393UI\xd63\x96\x14\x14\x19F\xa0\xce\xa9\xad|\x9d\x1e)X7ZA\x88w\x08\x9a\xa8z .\xea}#\x9cK\x0d\x8b\xc2!\x19\x8bN\xfd\xf1~7\"g\xad\xb8&r\x18\xe9G\x00\xcb\xfblu\xdb\x9d\xe3\xfdNw\xff\xa8\xfd\xd0\xee\x9f\xf6\x8fN\xdb\xc3\x83\xee\xc9\xf1\x8f\xed\xcei\x12M\x10\xdf\x19i\x1cJ\xd1@\":\xdb\x90q\xf9\xeac\x8a\n\xfa\xa5i)iQ\x8f\xd2\xb3\xd7\xe8\xf3\xa8\xafv*\xf6&\xf0QMq\x0c\xb2\xdd\xba0\xc9\xc6\xa4\xba\x90\x04-\xf9\xa89\xb8A C\x84\xf9\xb5\x01\xa1\xeb\xd6\x05\x114\x95\xe1o\xed\xee\xe2WCk\xc2\xc5k\xa2\xb9}\xd0U\xf6?\xcf\x91\xc7\xb0z\xf1_\x0d\xdb\"$\xb6\xae\xbd\xec\xa8\xc6\x05\xban]\xf8vdd*\x9b\x97\xa9b\x8d@\xa9ZI\xbb\x15\xabV\xed\xc8P3\xfa\x88\xab\x9bcR\xf4\xb59\xb7R\xf6\x05T\xb41\x12'\x92}o\x92\xdf\x14\xc6/e\x11\x8cb:\x8a\xb7\xfa\xaf 5\x12\x0c\x98\x98H\xb3>\xbc\x06\xbe0j\x90\x9f\xac\xfbI>\x9b`\xf5\xa8O4\x0b\xcb\x9b!\x0d|\x1e\xe3\xd0q7B\\\xc9\x19f\xf0\x16\xf6\xa7\x89D\xe4'\xee\xaf\x9b\xfbQ/\xcc\xf2SU\xa4\xe5\xa7 \x19\x90\x9f\xca\x049\xf6\x1f.\x8e{\xbf\xd93\xf2\xf4\x9f?\xae\x16\xd6\xf1G2\xb8=\xf9\xd59\xbe\xf6\xffl_\xfd\xda\x1b?\xce\xfd\xc7\x81\xb7x\xd7q\x1e~\xff\xc5\xfb\xe0\xdf\xde\xfe9?;{z\x18~|\xfc\x97u\xd7\xfepv\xf8p\xe9\x0e\xfc\xc3a\xf7\xec\xc9\xfbs\xf2\xef_\xee\xdd\xf3\xdf~\xfe9\x0e\xa9\xea\x15\x97$y\x1c\x94\xaf\xddW2\x15Y\x8b jXM\xb0\x91\xa9\xc9\xb8\x94b\x90\x96\\\xf5\x08\xf7Y\xc6\xe7\n?(\xfb\x82*\xd0\xa9\xc4\xb2h\xbb\xceVW\xa3ag\xb7#U\xb2\xf1\xa5i\xb0\xf6\xba\x90\xca\xf3Jm\xf5\xdel@\x85DNE\xa0\xdb\xe8)\x9az\xb8\xe1\xb3\xf2d'^\xf5\xaaq+\xac\xa7[C8\xf5\x06\xcb\xe9\x94\xf7=\xe7i\x8e\xc8\xa0{Bf\xf6\xd2\xf6\x9fW\xf3\x93\xe7\xe1\xe3\xd3\xa3\xe1\x18\x89L\xa7\xea\xe5\xf7\"-V\xcf\xff\xbeG\xab1dH\xac\xba\x03N\xe3\x87\xea\x00\xd4\xbcj\xb9\xb6\xba\xa1\x8e\xf7\x1e\x11\x0e\xe6\x18\x82\x0b1p\xf0\x91\xae\xa0\x85<\xf0\xff\xff\xdf\xff\xfd?q\x84[3\x7fjI\\\xfb\xef\xfcq\x1c\xeb\x16d\xd2k\x10\xc5\xb6>\x9f=\xaf\x81\x8c+\x08\xa9\xaa\xd3\x1a\xa0t\xe9\xd6\x82l\x04\xcdG\x9fq\x07\xd5\xa8\xfew\x0e\xe2\xbb='\x08mEG\xd4\xda\x15\xc3\x8eoC\x9e\xa3\xc4\x98R\x1bA\x92\x1fP\xfaE\xdb\x94\xa0]1\x8e\x9d@\x8c,\x18\x1d U\xaf\x9f\xff\xde\x80\x84P\x0e\xc6\xe2\x04\xb1 0 \x92\x9dG\x9f\x84\xaf'b>\x8d%qD\x89\xbd\xfaa\x0f\x80\x87\xb8\xa6\x13\x1b\xb3\xca\x9a\xb6\x99\x91\xcc\x95\xb4\xca\x19\x15\xaf8\x05 \x9c\xeb\x8f;\xcf\xc6\xa3\xe9#\xf7\xa9=\xf7\xbb\xcf\xd6\xcc\x9a\xf5\x86h\x02\xdb\xe4i\xf1LLH\x9e\xfaN\xcf8v\xe1\x91\xdf\x83\xees\xcf\xeazC\x8b\xb9O\xd6\xc0\x1a\x1a\xech64\xfcI\xd8ItA\x1a]\xd4\x10\xf5\x8e\"yq\xca\xe5z\x98z\x98W\x9eS\x8cE}_\xb02\xf9\xdd\x10@\xa3\xea\xe9U\xf4\xcc\x83\xab\x9a1\xa9bV\xd6.\xee2\x1d\xc2\x95\xb5\x94\x17\x0d\x97\xb6\x9d`\x12\xa0\x84\xb6\xbd\x1ae\x17\x8b\xf4\xd5\xf5\xfc\xfd\xb8\xcc\x1f;\x98\x8f\xd6\xd6pd\x81$w\x91\xeav\x94=\x94$\xc6\xa1\xb7f(\xd1C\x80\xdb`\xb6\x86\xee%\x82|\xa9N\xbdB\xc7\xfa\xc5\xd4M)Uu:\x9a\x17\xbb\xd6\x951\x14\xe1\xa8<\xc9U\xde\xf0\x17;\xb9\x83\xfc=\x88\xe1\xeb\x12\x1b\xe1 \xd5\xa3\x00=x\xf2\xeaEcD\x1fi\xe4\xa9*\x10-\xf3\xccD\x99\x11X\xc3\xe5\xeck\x13\x99&\xf5=\x13\x0dO\x1en\x18\xc7'\xb5B\xd7\x1f\xbf\n_\xf5\x08\xb1\x9d\xdf4\x95\x0e&\xf4\x167\xe3\x12\xc4\x03\x8akT/\x8c\xefGl\n\xbd5\nR\xe2@*\xa5\x98\x0e%xVq\xcd\x06\x9b\x88p\x9c\xbd^\xb7\xa0\xf1\x02\x8d\x19\xe6\xd5\x96q\x182\xfc\xc0}\x8f\xc43\xed\x86vM3\x07d\"\x0e\xb1\xcd\xd6\xb6\x1dSb\x8e\xf4\x0b\x13\xeb\xe3i\x01\x8c \xf7\xe0\x88/\xe5\xc2}^\xf5\xd2\x1c\xd6\xac+\xf8$\xc0\x13x\xa8\xa6\xe3H0\xacu\xb4\xa90gx\xdc\xdeow\xf6\xdb\x9d\x87v\xfbT\xfc\xff\x9f\x89\x8eE\xc7!\x1b \x8e\x07+2\xb7\xb0\x10\xe6\xc0\xe5h;X\xe49\x95- \xf3\xc5\xfb\xf3\x19\x1aWDTD\xeb$q\xaflE\x13\x0b\x903\xa3\x9a\x81\x94\x14O4\xad+\xd8\x94\xf0Z\xd95\xb6;\x8a*~\x8fD3\x99h\xad\xed\x7f\xbb\x9c,\"\xdc\xc3\xdb\xc9-\xe3i^I\x9c\xa9\xa9_\xa9\xfdT\x98\xb3\xc0\n\xedQ\x8e\xd2U\xb8\xa1icxH\x1e\x9d\xaa`_\x1cL\xca\x8d\x88\x86\xa1\x95'\xb6Kf\xbe0\xd1\xd2V\xbd\x88jI\xab\x0f\xc8|9z1\xcf\xa8 a2^ b\x8b*\xa3R\xa4\x95\xa1P\xa01\xd5\xeb\xe8\xb5Xe\xd0\xc0\"\x8b\xe6\xe5\x1c\x03;\x10*i^\x03j\x176\xcb]\xacS\x99\x0c[\x8a\xae\x19\xb2'\xe2\xbc\xe4h\x8b\xf5\xe9T:.69\x16\x85\x17\x9b`\xbf\xf3\xc766\xde\xa3\xea\xa2#\x9a\x94q\xac|{\xf9=\xb6\x08&V-6\xc92D\x05S,\x16AGt2a\xa8\xbc\xa1\xcc>F>\xe18W\xe2\xcf\x18w\xc6\x909\nO\xb6\x16\xc4\xab1\xc4\x1d\xf4\xa0s\xa1\x1c\xf2\xad29\x7f\xcc\\X\xa3\xc2\xcc8\x9ca\x12\xef\x8a\xac\x91\xcd\xb5n\xe12\x96a\xa6\x14\x9c\xea\xe0h\x95\xb19\x9e\xa9<\xceV\x99\x08[^k\xf9\xaf\x00\x00\x00\xff\xffPK\x07\x08\xa7j\"\x13n \x00\x00}\x0d\x01\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xd4`4t\xc7\x01\x00\x00\xbd\x01\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00favicon-16x16.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(6B\xc8\xd7\x7f\x04\x00\x00u\x04\x00\x00\x11\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x0f\x02\x00\x00favicon-32x32.pngUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb9\xb1\xf1mT\x02\x00\x008\x05\x00\x00\n\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd6\x06\x00\x00index.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(]\x12r 9\x03\x00\x00T \x00\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81k \x00\x00oauth2-redirect.htmlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(-\xe3\xb5\x97=9\x05\x00\xf7\x0c\x1b\x00\x14\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xef\x0c\x00\x00swagger-ui-bundle.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(v\xf2\x8aA\x86\xba\x01\x00\xc5\x87\x08\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81wF\x05\x00swagger-ui-standalone-preset.jsUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(_;\x94/\xe8Y\x00\x00\xa8X\x02\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81S\x01\x07\x00swagger-ui.cssUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xa7j\"\x13n \x00\x00}\x0d\x01\x00\x0c\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x80[\x07\x00swagger.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x08\x00\x08\x00E\x02\x00\x001|\x07\x00\x00\x00" - fs.Register(data) -} diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml deleted file mode 100644 index bb38b8f92283..000000000000 --- a/client/lcd/swagger-ui/swagger.yaml +++ /dev/null @@ -1,2542 +0,0 @@ ---- -swagger: "2.0" -info: - version: "3.0" - title: Gaia-Lite for Cosmos - description: A REST interface for state queries, transaction generation and broadcasting. -tags: - - name: Transactions - description: Search, encode, or broadcast transactions. - - name: Tendermint RPC - description: Tendermint APIs, such as query blocks, transactions and validatorset - - name: Auth - description: Authenticate accounts - - name: Bank - description: Create and broadcast transactions - - name: Staking - description: Stake module APIs - - name: Governance - description: Governance module APIs - - name: Slashing - description: Slashing module APIs - - name: Distribution - description: Fee distribution module APIs - - name: Supply - description: Supply module APIs - - name: version - - name: Mint - description: Minting module APIs - - name: Misc - description: Query app version -schemes: - - https -host: stargate.cosmos.network -securityDefinitions: - kms: - type: basic -paths: - /node_info: - get: - description: Information about the connected node - summary: The properties of the connected node - tags: - - Tendermint RPC - produces: - - application/json - responses: - 200: - description: Node status - schema: - type: object - properties: - application_version: - properties: - build_tags: - type: string - client_name: - type: string - commit: - type: string - go: - type: string - name: - type: string - server_name: - type: string - version: - type: string - node_info: - properties: - id: - type: string - moniker: - type: string - example: validator-name - protocol_version: - properties: - p2p: - type: string - example: 7 - block: - type: string - example: 10 - app: - type: string - example: 0 - network: - type: string - example: gaia-2 - channels: - type: string - listen_addr: - type: string - example: 192.168.56.1:26656 - version: - description: Tendermint version - type: string - example: 0.15.0 - other: - description: more information on versions - type: object - properties: - tx_index: - type: string - example: on - rpc_address: - type: string - example: tcp://0.0.0.0:26657 - 500: - description: Failed to query node status - /syncing: - get: - summary: Syncing state of node - tags: - - Tendermint RPC - description: Get if the node is currently syning with other nodes - produces: - - application/json - responses: - 200: - description: Node syncing status - schema: - type: object - properties: - syncing: - type: boolean - 500: - description: Server internal error - /blocks/latest: - get: - summary: Get the latest block - tags: - - Tendermint RPC - produces: - - application/json - responses: - 200: - description: The latest block - schema: - $ref: "#/definitions/BlockQuery" - 500: - description: Server internal error - /blocks/{height}: - get: - summary: Get a block at a certain height - tags: - - Tendermint RPC - produces: - - application/json - parameters: - - in: path - name: height - description: Block height - required: true - type: number - x-example: 1 - responses: - 200: - description: The block at a specific height - schema: - $ref: "#/definitions/BlockQuery" - 404: - description: Request block height doesn't - 400: - description: Invalid height - 500: - description: Server internal error - /validatorsets/latest: - get: - summary: Get the latest validator set - tags: - - Tendermint RPC - produces: - - application/json - responses: - 200: - description: The validator set at the latest block height - schema: - type: object - properties: - block_height: - type: string - validators: - type: array - items: - $ref: "#/definitions/TendermintValidator" - 500: - description: Server internal error - /validatorsets/{height}: - get: - summary: Get a validator set a certain height - tags: - - Tendermint RPC - produces: - - application/json - parameters: - - in: path - name: height - description: Block height - required: true - type: number - x-example: 1 - responses: - 200: - description: The validator set at a specific block height - schema: - type: object - properties: - block_height: - type: string - validators: - type: array - items: - $ref: "#/definitions/TendermintValidator" - 404: - description: Block at height not available - 400: - description: Invalid height - 500: - description: Server internal error - /txs/{hash}: - get: - summary: Get a Tx by hash - tags: - - Transactions - description: Retrieve a transaction using its hash. - produces: - - application/json - parameters: - - in: path - name: hash - description: Tx hash - required: true - type: string - x-example: BCBE20E8D46758B96AE5883B792858296AC06E51435490FBDCAE25A72B3CC76B - responses: - 200: - description: Tx with the provided hash - schema: - $ref: "#/definitions/TxQuery" - 500: - description: Internal Server Error - /txs: - get: - tags: - - Transactions - summary: Search transactions - description: Search transactions by events. - produces: - - application/json - parameters: - - in: query - name: message.action - type: string - description: "transaction events such as 'message.action=send' which results in the following endpoint: 'GET /txs?message.action=send'. note that each module documents its own events. look for xx_events.md in the corresponding cosmos-sdk/docs/spec directory" - x-example: "send" - - in: query - name: message.sender - type: string - description: "transaction tags with sender: 'GET /txs?message.action=send&message.sender=fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym'" - x-example: "fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym" - - in: query - name: page - description: Page number - type: integer - x-example: 1 - - in: query - name: limit - description: Maximum number of items per page - type: integer - x-example: 1 - - in: query - name: tx.minheight - type: integer - description: "transactions on blocks with height greater or equal this value" - x-example: 25 - - in: query - name: tx.maxheight - type: integer - description: "transactions on blocks with height less than or equal this value" - x-example: 800000 - responses: - 200: - description: All txs matching the provided events - schema: - $ref: "#/definitions/PaginatedQueryTxs" - 400: - description: Invalid search events - 500: - description: Internal Server Error - post: - tags: - - Transactions - summary: Broadcast a signed tx - description: Broadcast a signed tx to a full node - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: txBroadcast - description: The tx must be a signed StdTx. The supported broadcast modes include `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away). - required: true - schema: - type: object - properties: - tx: - $ref: "#/definitions/StdTx" - mode: - type: string - example: block - responses: - 200: - description: Tx broadcasting result - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 500: - description: Internal Server Error - /txs/encode: - post: - tags: - - Transactions - summary: Encode a transaction to the Amino wire format - description: Encode a transaction (signed or not) from JSON to base64-encoded Amino serialized bytes - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: tx - description: The tx to encode - required: true - schema: - type: object - properties: - tx: - $ref: "#/definitions/StdTx" - responses: - 200: - description: The tx was successfully decoded and re-encoded - schema: - type: object - properties: - tx: - type: string - example: The base64-encoded Amino-serialized bytes for the tx - 400: - description: The tx was malformated - 500: - description: Server internal error - /txs/decode: - post: - tags: - - Transactions - summary: Decode a transaction from the Amino wire format - description: Decode a transaction (signed or not) from base64-encoded Amino serialized bytes to JSON - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: tx - description: The tx to decode - required: true - schema: - type: object - properties: - tx: - type: string - example: SvBiXe4KPqijYZoKFFHEzJ8c2HPAfv2EFUcIhx0yPagwEhTy0vPA+GGhCEslKXa4Af0uB+mfShoMCgVzdGFrZRIDMTAwEgQQwJoM - responses: - 200: - description: The tx was successfully decoded - schema: - $ref: "#/definitions/StdTx" - 400: - description: The tx was malformated - 500: - description: Server internal error - /bank/balances/{address}: - get: - summary: Get the account balances - tags: - - Bank - produces: - - application/json - parameters: - - in: path - name: address - description: Account address in bech32 format - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - responses: - 200: - description: Account balances - schema: - type: array - items: - $ref: "#/definitions/Coin" - 500: - description: Server internal error - /bank/accounts/{address}/transfers: - post: - summary: Send coins from one account to another - tags: - - Bank - consumes: - - application/json - produces: - - application/json - parameters: - - in: path - name: address - description: Account address in bech32 format - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - - in: body - name: account - description: The sender and tx information - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - amount: - type: array - items: - $ref: "#/definitions/Coin" - responses: - 202: - description: Tx was succesfully generated - schema: - $ref: "#/definitions/StdTx" - 400: - description: Invalid request - 500: - description: Server internal error - /auth/accounts/{address}: - get: - summary: Get the account information on blockchain - tags: - - Auth - produces: - - application/json - parameters: - - in: path - name: address - description: Account address - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - responses: - 200: - description: Account information on the blockchain - schema: - type: object - properties: - type: - type: string - value: - type: object - properties: - account_number: - type: string - address: - type: string - coins: - type: array - items: - $ref: "#/definitions/Coin" - public_key: - $ref: "#/definitions/PublicKey" - sequence: - type: string - 500: - description: Server internel error - /staking/delegators/{delegatorAddr}/delegations: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - get: - summary: Get all delegations from a delegator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Delegation" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - post: - summary: Submit delegation - parameters: - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_address: - $ref: "#/definitions/Address" - validator_address: - $ref: "#/definitions/ValidatorAddress" - delegation: - $ref: "#/definitions/Coin" - tags: - - Staking - consumes: - - application/json - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator address or delegation request body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Query the current delegation between a delegator and a validator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Delegation" - 400: - description: Invalid delegator address or validator address - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/unbonding_delegations: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - get: - summary: Get all unbonding delegations from a delegator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/UnbondingDelegation" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - post: - summary: Submit an unbonding delegation - parameters: - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_address: - $ref: "#/definitions/Address" - validator_address: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" - tags: - - Staking - consumes: - - application/json - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator address or unbonding delegation request body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Query all unbonding delegations between a delegator and a validator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/UnbondingDelegationPair" - 400: - description: Invalid delegator address or validator address - 500: - description: Internal Server Error - /staking/redelegations: - parameters: - - in: query - name: delegator - description: Bech32 AccAddress of Delegator - required: false - type: string - - in: query - name: validator_from - description: Bech32 ValAddress of SrcValidator - required: false - type: string - - in: query - name: validator_to - description: Bech32 ValAddress of DstValidator - required: false - type: string - get: - summary: Get all redelegations (filter by query params) - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Redelegation" - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/redelegations: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - post: - summary: Submit a redelegation - parameters: - - in: body - name: delegation - description: The sender and tx information - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_address: - $ref: "#/definitions/Address" - validator_src_addressess: - $ref: "#/definitions/ValidatorAddress" - validator_dst_address: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" - tags: - - Staking - consumes: - - application/json - produces: - - application/json - responses: - 200: - description: Tx was succesfully generated - schema: - $ref: "#/definitions/StdTx" - 400: - description: Invalid delegator address or redelegation request body - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/validators: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - get: - summary: Query all validators that a delegator is bonded to - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Validator" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - /staking/delegators/{delegatorAddr}/validators/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - - in: path - name: validatorAddr - description: Bech32 ValAddress of Delegator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Query a validator that a delegator is bonded to - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Validator" - 400: - description: Invalid delegator address or validator address - 500: - description: Internal Server Error - /staking/validators: - get: - summary: Get all validator candidates. By default it returns only the bonded validators. - parameters: - - in: query - name: status - type: string - description: The validator bond status. Must be either 'bonded', 'unbonded', or 'unbonding'. - x-example: bonded - - in: query - name: page - description: The page number. - type: integer - x-example: 1 - - in: query - name: limit - description: The maximum number of items per page. - type: integer - x-example: 1 - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Validator" - 500: - description: Internal Server Error - /staking/validators/{validatorAddr}: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Query the information from a single validator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Validator" - 400: - description: Invalid validator address - 500: - description: Internal Server Error - /staking/validators/{validatorAddr}/delegations: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Get all delegations from a validator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Delegation" - 400: - description: Invalid validator address - 500: - description: Internal Server Error - /staking/validators/{validatorAddr}/unbonding_delegations: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Get all unbonding delegations from a validator - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/UnbondingDelegation" - 400: - description: Invalid validator address - 500: - description: Internal Server Error - /staking/pool: - get: - summary: Get the current state of the staking pool - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - loose_tokens: - type: string - bonded_tokens: - type: string - inflation_last_time: - type: string - inflation: - type: string - date_last_commission_reset: - type: string - prev_bonded_shares: - type: string - 500: - description: Internal Server Error - /staking/parameters: - get: - summary: Get the current staking parameter values - tags: - - Staking - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - inflation_rate_change: - type: string - inflation_max: - type: string - inflation_min: - type: string - goal_bonded: - type: string - unbonding_time: - type: string - max_validators: - type: integer - bond_denom: - type: string - 500: - description: Internal Server Error - # TODO: We need to either fix this route when the validator is not found or add a slashed validator in the contract tests - # /slashing/validators/{validatorPubKey}/signing_info: - # get: - # summary: Get sign info of given validator - # description: Get sign info of given validator - # produces: - # - application/json - # tags: - # - Slashing - # parameters: - # - type: string - # description: Bech32 validator public key - # name: validatorPubKey - # required: true - # in: path - # x-example: fetchvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cslatcyp - # responses: - # 200: - # description: OK - # schema: - # $ref: "#/definitions/SigningInfo" - # 400: - # description: Invalid validator public key - # 500: - # description: Internal Server Error - /slashing/signing_infos: - get: - summary: Get sign info of given all validators - description: Get sign info of all validators - produces: - - application/json - tags: - - Slashing - parameters: - - in: query - name: page - description: Page number - type: integer - required: true - x-example: 1 - - in: query - name: limit - description: Maximum number of items per page - type: integer - required: true - x-example: 5 - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/SigningInfo" - 400: - description: Invalid validator public key for one of the validators - 500: - description: Internal Server Error - /slashing/validators/{validatorAddr}/unjail: - post: - summary: Unjail a jailed validator - description: Send transaction to unjail a jailed validator - consumes: - - application/json - produces: - - application/json - tags: - - Slashing - parameters: - - type: string - description: Bech32 validator address - name: validatorAddr - required: true - in: path - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - - description: "" - name: UnjailBody - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/StdTx" - responses: - 200: - description: Tx was succesfully generated - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid validator address or base_req - 500: - description: Internal Server Error - /slashing/parameters: - get: - summary: Get the current slashing parameters - tags: - - Slashing - produces: - - application/json - responses: - 200: - description: OK - schema: - type: object - properties: - max_evidence_age: - type: string - signed_blocks_window: - type: string - min_signed_per_window: - type: string - double_sign_unbond_duration: - type: string - downtime_unbond_duration: - type: string - slash_fraction_double_sign: - type: string - slash_fraction_downtime: - type: string - 500: - description: Internal Server Error - /gov/proposals: - post: - summary: Submit a proposal - description: Send transaction to submit a proposal - consumes: - - application/json - produces: - - application/json - tags: - - Governance - parameters: - - description: valid value of `"proposal_type"` can be `"text"`, `"parameter_change"`, `"software_upgrade"` - name: post_proposal_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - title: - type: string - description: - type: string - proposal_type: - type: string - example: "text" - proposer: - $ref: "#/definitions/Address" - initial_deposit: - type: array - items: - $ref: "#/definitions/Coin" - responses: - 200: - description: Tx was succesfully generated - schema: - $ref: "#/definitions/StdTx" - 400: - description: Invalid proposal body - 500: - description: Internal Server Error - get: - summary: Query proposals - description: Query proposals information with parameters - produces: - - application/json - tags: - - Governance - parameters: - - in: query - name: voter - description: voter address - required: false - type: string - - in: query - name: depositor - description: depositor address - required: false - type: string - - in: query - name: status - description: proposal status, valid values can be `"deposit_period"`, `"voting_period"`, `"passed"`, `"rejected"` - required: false - type: string - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/TextProposal" - 400: - description: Invalid query parameters - 500: - description: Internal Server Error - /gov/proposals/param_change: - post: - summary: Generate a parameter change proposal transaction - description: Generate a parameter change proposal transaction - consumes: - - application/json - produces: - - application/json - tags: - - Governance - parameters: - - description: The parameter change proposal body that contains all parameter changes - name: post_proposal_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - title: - type: string - x-example: "Param Change" - description: - type: string - x-example: "Update max validators" - proposer: - $ref: "#/definitions/Address" - deposit: - type: array - items: - $ref: "#/definitions/Coin" - changes: - type: array - items: - $ref: "#/definitions/ParamChange" - responses: - 200: - description: The transaction was succesfully generated - schema: - $ref: "#/definitions/StdTx" - 400: - description: Invalid proposal body - 500: - description: Internal Server Error - /gov/proposals/{proposalId}: - get: - summary: Query a proposal - description: Query a proposal by id - produces: - - application/json - tags: - - Governance - parameters: - - type: string - name: proposalId - required: true - in: path - x-example: "2" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/TextProposal" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/proposer: - get: - summary: Query proposer - description: Query for the proposer for a proposal - produces: - - application/json - tags: - - Governance - parameters: - - type: string - name: proposalId - required: true - in: path - x-example: "2" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Proposer" - 400: - description: Invalid proposal ID - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/deposits: - get: - summary: Query deposits - description: Query deposits by proposalId - produces: - - application/json - tags: - - Governance - parameters: - - type: string - name: proposalId - required: true - in: path - x-example: "2" - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Deposit" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - post: - summary: Deposit tokens to a proposal - description: Send transaction to deposit tokens to a proposal - consumes: - - application/json - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - - description: "" - name: post_deposit_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - depositor: - $ref: "#/definitions/Address" - amount: - type: array - items: - $ref: "#/definitions/Coin" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid proposal id or deposit body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/deposits/{depositor}: - get: - summary: Query deposit - description: Query deposit by proposalId and depositor address - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - - type: string - description: Bech32 depositor address - name: depositor - required: true - in: path - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Deposit" - 400: - description: Invalid proposal id or depositor address - 404: - description: Found no deposit - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/votes: - get: - summary: Query voters - description: Query voters information by proposalId - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Vote" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - post: - summary: Vote a proposal - description: Send transaction to vote a proposal - consumes: - - application/json - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` - name: post_vote_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - voter: - $ref: "#/definitions/Address" - option: - type: string - example: "yes" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid proposal id or vote body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/votes/{voter}: - get: - summary: Query vote - description: Query vote information by proposal Id and voter address - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - - type: string - description: Bech32 voter address - name: voter - required: true - in: path - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Vote" - 400: - description: Invalid proposal id or voter address - 404: - description: Found no vote - 500: - description: Internal Server Error - /gov/proposals/{proposalId}/tally: - get: - summary: Get a proposal's tally result at the current time - description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. - produces: - - application/json - tags: - - Governance - parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - x-example: "2" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/TallyResult" - 400: - description: Invalid proposal id - 500: - description: Internal Server Error - /gov/parameters/deposit: - get: - summary: Query governance deposit parameters - description: Query governance deposit parameters. The max_deposit_period units are in nanoseconds. - produces: - - application/json - tags: - - Governance - responses: - 200: - description: OK - schema: - type: object - properties: - min_deposit: - type: array - items: - $ref: "#/definitions/Coin" - max_deposit_period: - type: string - example: "86400000000000" - 400: - description: is not a valid query request path - 404: - description: Found no deposit parameters - 500: - description: Internal Server Error - /gov/parameters/tallying: - get: - summary: Query governance tally parameters - description: Query governance tally parameters - produces: - - application/json - tags: - - Governance - responses: - 200: - description: OK - schema: - properties: - threshold: - type: string - example: "0.5000000000" - veto: - type: string - example: "0.3340000000" - governance_penalty: - type: string - example: "0.0100000000" - 400: - description: is not a valid query request path - 404: - description: Found no tally parameters - 500: - description: Internal Server Error - /gov/parameters/voting: - get: - summary: Query governance voting parameters - description: Query governance voting parameters. The voting_period units are in nanoseconds. - produces: - - application/json - tags: - - Governance - responses: - 200: - description: OK - schema: - properties: - voting_period: - type: string - example: "86400000000000" - 400: - description: is not a valid query request path - 404: - description: Found no voting parameters - 500: - description: Internal Server Error - /distribution/delegators/{delegatorAddr}/rewards: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - get: - summary: Get the total rewards balance from all delegations - description: Get the sum of all the rewards earned by delegations by a single delegator - produces: - - application/json - tags: - - Distribution - responses: - 200: - description: OK - schema: - $ref: "#/definitions/DelegatorTotalRewards" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - post: - summary: Withdraw all the delegator's delegation rewards - description: Withdraw all the delegator's delegation rewards - tags: - - Distribution - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator address - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: fetch16xyempempp92x9hyzz9wrgf94r6j9h5fu8gzym - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Query a delegation reward - description: Query a single delegation reward by a delegator - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Coin" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - post: - summary: Withdraw a delegation reward - description: Withdraw a delegator's delegation reward from a single validator - tags: - - Distribution - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator address or delegation body - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /distribution/delegators/{delegatorAddr}/withdraw_address: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - x-example: cosmos167w96tdvmazakdwkw2u57227eduula2cy572lf - get: - summary: Get the rewards withdrawal address - description: Get the delegations' rewards withdrawal address. This is the address in which the user will receive the reward funds - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Address" - 400: - description: Invalid delegator address - 500: - description: Internal Server Error - post: - summary: Replace the rewards withdrawal address - description: Replace the delegations' rewards withdrawal address for a new one. - tags: - - Distribution - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" - withdraw_address: - $ref: "#/definitions/Address" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid delegator or withdraw address - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /distribution/validators/{validatorAddr}: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Validator distribution information - description: Query the distribution information of a single validator - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/ValidatorDistInfo" - 400: - description: Invalid validator address - 500: - description: Internal Server Error - /distribution/validators/{validatorAddr}/outstanding_rewards: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Fee distribution outstanding rewards of a single validator - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Coin" - 500: - description: Internal Server Error - /distribution/validators/{validatorAddr}/rewards: - parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string - x-example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - get: - summary: Commission and self-delegation rewards of a single validator - description: Query the commission and self-delegation rewards of validator. - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Coin" - 400: - description: Invalid validator address - 500: - description: Internal Server Error - post: - summary: Withdraw the validator's rewards - description: Withdraw the validator's self-delegation and commissions rewards - tags: - - Distribution - consumes: - - application/json - produces: - - application/json - parameters: - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" - responses: - 200: - description: OK - schema: - $ref: "#/definitions/BroadcastTxCommitResult" - 400: - description: Invalid validator address - 401: - description: Key password is wrong - 500: - description: Internal Server Error - /distribution/community_pool: - get: - summary: Community pool parameters - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - type: array - items: - $ref: "#/definitions/Coin" - 500: - description: Internal Server Error - /distribution/parameters: - get: - summary: Fee distribution parameters - tags: - - Distribution - produces: - - application/json - responses: - 200: - description: OK - schema: - properties: - base_proposer_reward: - type: string - bonus_proposer_reward: - type: string - community_tax: - type: string - 500: - description: Internal Server Error - /minting/parameters: - get: - summary: Minting module parameters - tags: - - Mint - produces: - - application/json - responses: - 200: - description: OK - schema: - properties: - mint_denom: - type: string - inflation_rate_change: - type: string - inflation_max: - type: string - inflation_min: - type: string - goal_bonded: - type: string - blocks_per_year: - type: string - 500: - description: Internal Server Error - /minting/inflation: - get: - summary: Current minting inflation value - tags: - - Mint - produces: - - application/json - responses: - 200: - description: OK - schema: - type: string - 500: - description: Internal Server Error - /minting/annual-provisions: - get: - summary: Current minting annual provisions value - tags: - - Mint - produces: - - application/json - responses: - 200: - description: OK - schema: - type: string - 500: - description: Internal Server Error - /supply/total: - get: - summary: Total supply of coins in the chain - tags: - - Supply - produces: - - application/json - responses: - 200: - description: OK - schema: - $ref: "#/definitions/Supply" - 500: - description: Internal Server Error - /supply/total/{denomination}: - parameters: - - in: path - name: denomination - description: Coin denomination - required: true - type: string - x-example: uatom - get: - summary: Total supply of a single coin denomination - tags: - - Supply - produces: - - application/json - responses: - 200: - description: OK - schema: - type: string - 400: - description: Invalid coin denomination - 500: - description: Internal Server Error -definitions: - CheckTxResult: - type: object - properties: - code: - type: integer - data: - type: string - gas_used: - type: integer - gas_wanted: - type: integer - info: - type: string - log: - type: string - tags: - type: array - items: - $ref: "#/definitions/KVPair" - example: - code: 0 - data: data - log: log - gas_used: 5000 - gas_wanted: 10000 - info: info - tags: - - "" - - "" - DeliverTxResult: - type: object - properties: - code: - type: integer - data: - type: string - gas_used: - type: integer - gas_wanted: - type: integer - info: - type: string - log: - type: string - tags: - type: array - items: - $ref: "#/definitions/KVPair" - example: - code: 5 - data: data - log: log - gas_used: 5000 - gas_wanted: 10000 - info: info - tags: - - "" - - "" - BroadcastTxCommitResult: - type: object - properties: - check_tx: - $ref: "#/definitions/CheckTxResult" - deliver_tx: - $ref: "#/definitions/DeliverTxResult" - hash: - $ref: "#/definitions/Hash" - height: - type: integer - KVPair: - type: object - properties: - key: - type: string - value: - type: string - Msg: - type: string - Address: - type: string - description: bech32 encoded address - example: cosmos1depk54cuajgkzea6zpgkq36tnjwdzv4afc3d27 - ValidatorAddress: - type: string - description: bech32 encoded address - example: fetchvaloper16xyempempp92x9hyzz9wrgf94r6j9h5ferhphu - Coin: - type: object - properties: - denom: - type: string - example: stake - amount: - type: string - example: "50" - Hash: - type: string - example: EE5F3404034C524501629B56E0DDC38FAD651F04 - TxQuery: - type: object - properties: - hash: - type: string - example: "D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656" - height: - type: number - example: 368 - tx: - $ref: "#/definitions/StdTx" - result: - type: object - properties: - log: - type: string - gas_wanted: - type: string - example: "200000" - gas_used: - type: string - example: "26354" - tags: - type: array - items: - $ref: "#/definitions/KVPair" - PaginatedQueryTxs: - type: object - properties: - total_count: - type: number - example: 1 - count: - type: number - example: 1 - page_number: - type: number - example: 1 - page_total: - type: number - example: 1 - limit: - type: number - example: 30 - txs: - type: array - items: - $ref: "#/definitions/TxQuery" - StdTx: - type: object - properties: - msg: - type: array - items: - $ref: "#/definitions/Msg" - fee: - type: object - properties: - gas: - type: string - amount: - type: array - items: - $ref: "#/definitions/Coin" - memo: - type: string - signature: - type: object - properties: - signature: - type: string - example: MEUCIQD02fsDPra8MtbRsyB1w7bqTM55Wu138zQbFcWx4+CFyAIge5WNPfKIuvzBZ69MyqHsqD8S1IwiEp+iUb6VSdtlpgY= - pub_key: - type: object - properties: - type: - type: string - example: "tendermint/PubKeySecp256k1" - value: - type: string - example: "Avz04VhtKJh8ACCVzlI8aTosGy0ikFXKIVHQ3jKMrosH" - account_number: - type: string - example: "0" - sequence: - type: string - example: "0" - BlockID: - type: object - properties: - hash: - $ref: "#/definitions/Hash" - parts: - type: object - properties: - total: - type: number - example: 0 - hash: - $ref: "#/definitions/Hash" - BlockHeader: - type: object - properties: - chain_id: - type: string - example: cosmoshub-2 - height: - type: number - example: 1 - time: - type: string - example: "2017-12-30T05:53:09.287+01:00" - num_txs: - type: number - example: 0 - last_block_id: - $ref: "#/definitions/BlockID" - total_txs: - type: number - example: 35 - last_commit_hash: - $ref: "#/definitions/Hash" - data_hash: - $ref: "#/definitions/Hash" - validators_hash: - $ref: "#/definitions/Hash" - next_validators_hash: - $ref: "#/definitions/Hash" - consensus_hash: - $ref: "#/definitions/Hash" - app_hash: - $ref: "#/definitions/Hash" - last_results_hash: - $ref: "#/definitions/Hash" - evidence_hash: - $ref: "#/definitions/Hash" - proposer_address: - $ref: "#/definitions/Address" - version: - type: object - properties: - block: - type: string - example: 10 - app: - type: string - example: 0 - Block: - type: object - properties: - header: - $ref: "#/definitions/BlockHeader" - txs: - type: array - items: - type: string - evidence: - type: array - items: - type: string - last_commit: - type: object - properties: - block_id: - $ref: "#/definitions/BlockID" - precommits: - type: array - items: - type: object - properties: - validator_address: - type: string - validator_index: - type: string - example: "0" - height: - type: string - example: "0" - round: - type: string - example: "0" - timestamp: - type: string - example: "2017-12-30T05:53:09.287+01:00" - type: - type: number - example: 2 - block_id: - $ref: "#/definitions/BlockID" - signature: - type: string - example: "7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ==" - BlockQuery: - type: object - properties: - block_meta: - type: object - properties: - header: - $ref: "#/definitions/BlockHeader" - block_id: - $ref: "#/definitions/BlockID" - block: - $ref: "#/definitions/Block" - DelegationDelegatorReward: - type: object - properties: - validator_address: - $ref: "#/definitions/ValidatorAddress" - reward: - type: array - items: - $ref: "#/definitions/Coin" - DelegatorTotalRewards: - type: object - properties: - rewards: - type: array - items: - $ref: "#/definitions/DelegationDelegatorReward" - total: - type: array - items: - $ref: "#/definitions/Coin" - BaseReq: - type: object - properties: - from: - type: string - example: "fetch1g9ahr6xhht5rmqven628nklxluzyv8z9pamue0" - description: Sender address or Keybase name to generate a transaction - memo: - type: string - example: "Sent via Cosmos Voyager 🚀" - chain_id: - type: string - example: "Cosmos-Hub" - account_number: - type: string - example: "0" - sequence: - type: string - example: "1" - gas: - type: string - example: "200000" - gas_adjustment: - type: string - example: "1.2" - fees: - type: array - items: - $ref: "#/definitions/Coin" - simulate: - type: boolean - example: false - description: Estimate gas for a transaction (cannot be used in conjunction with generate_only) - TendermintValidator: - type: object - properties: - address: - $ref: "#/definitions/ValidatorAddress" - pub_key: - type: string - example: fetchvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cslatcyp - voting_power: - type: string - example: "1000" - proposer_priority: - type: string - example: "1000" - TextProposal: - type: object - properties: - proposal_id: - type: integer - title: - type: string - description: - type: string - proposal_type: - type: string - proposal_status: - type: string - final_tally_result: - $ref: "#/definitions/TallyResult" - submit_time: - type: string - total_deposit: - type: array - items: - $ref: "#/definitions/Coin" - voting_start_time: - type: string - Proposer: - type: object - properties: - proposal_id: - type: string - proposer: - type: string - Deposit: - type: object - properties: - amount: - type: array - items: - $ref: "#/definitions/Coin" - proposal_id: - type: string - depositor: - $ref: "#/definitions/Address" - TallyResult: - type: object - properties: - yes: - type: string - example: "0.0000000000" - abstain: - type: string - example: "0.0000000000" - no: - type: string - example: "0.0000000000" - no_with_veto: - type: string - example: "0.0000000000" - Vote: - type: object - properties: - voter: - type: string - proposal_id: - type: string - option: - type: string - Validator: - type: object - properties: - operator_address: - $ref: "#/definitions/ValidatorAddress" - consensus_pubkey: - type: string - example: fetchvalconspub1zcjduepq0vu2zgkgk49efa0nqwzndanq5m4c7pa3u4apz4g2r9gspqg6g9cslatcyp - jailed: - type: boolean - status: - type: integer - tokens: - type: string - delegator_shares: - type: string - description: - type: object - properties: - moniker: - type: string - identity: - type: string - website: - type: string - security_contact: - type: string - details: - type: string - bond_height: - type: string - example: "0" - bond_intra_tx_counter: - type: integer - example: 0 - unbonding_height: - type: string - example: "0" - unbonding_time: - type: string - example: "1970-01-01T00:00:00Z" - commission: - type: object - properties: - rate: - type: string - example: "0" - max_rate: - type: string - example: "0" - max_change_rate: - type: string - example: "0" - update_time: - type: string - example: "1970-01-01T00:00:00Z" - Delegation: - type: object - properties: - delegator_address: - type: string - validator_address: - type: string - shares: - type: string - balance: - $ref: "#/definitions/Coin" - UnbondingDelegationPair: - type: object - properties: - delegator_address: - type: string - validator_address: - type: string - entries: - type: array - items: - $ref: "#/definitions/UnbondingEntries" - UnbondingEntries: - type: object - properties: - initial_balance: - type: string - balance: - type: string - creation_height: - type: string - min_time: - type: string - UnbondingDelegation: - type: object - properties: - delegator_address: - type: string - validator_address: - type: string - initial_balance: - type: string - balance: - type: string - creation_height: - type: integer - min_time: - type: integer - Redelegation: - type: object - properties: - delegator_address: - type: string - validator_src_address: - type: string - validator_dst_address: - type: string - entries: - type: array - items: - $ref: "#/definitions/Redelegation" - RedelegationEntry: - type: object - properties: - creation_height: - type: integer - completion_time: - type: integer - initial_balance: - type: string - balance: - type: string - shares_dst: - type: string - ValidatorDistInfo: - type: object - properties: - operator_address: - $ref: "#/definitions/ValidatorAddress" - self_bond_rewards: - type: array - items: - $ref: "#/definitions/Coin" - val_commission: - type: array - items: - $ref: "#/definitions/Coin" - PublicKey: - type: object - properties: - type: - type: string - value: - type: string - SigningInfo: - type: object - properties: - start_height: - type: string - index_offset: - type: string - jailed_until: - type: string - missed_blocks_counter: - type: string - ParamChange: - type: object - properties: - subspace: - type: string - example: "staking" - key: - type: string - example: "MaxValidators" - subkey: - type: string - example: "" - value: - type: object - Supply: - type: object - properties: - total: - type: array - items: - $ref: "#/definitions/Coin" diff --git a/client/query.go b/client/query.go new file mode 100644 index 000000000000..85749a739a87 --- /dev/null +++ b/client/query.go @@ -0,0 +1,151 @@ +package client + +import ( + "context" + "fmt" + "strings" + + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + rpcclient "github.com/tendermint/tendermint/rpc/client" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// GetNode returns an RPC client. If the context's client is not defined, an +// error is returned. +func (ctx Context) GetNode() (rpcclient.Client, error) { + if ctx.Client == nil { + return nil, errors.New("no RPC client is defined in offline mode") + } + + return ctx.Client, nil +} + +// Query performs a query to a Tendermint node with the provided path. +// It returns the result and height of the query upon success or an error if +// the query fails. +func (ctx Context) Query(path string) ([]byte, int64, error) { + return ctx.query(path, nil) +} + +// QueryWithData performs a query to a Tendermint node with the provided path +// and a data payload. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx Context) QueryWithData(path string, data []byte) ([]byte, int64, error) { + return ctx.query(path, data) +} + +// QueryStore performs a query to a Tendermint node with the provided key and +// store name. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx Context) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { + return ctx.queryStore(key, storeName, "key") +} + +// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. +// It returns the ResultQuery obtained from the query. +func (ctx Context) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + return ctx.queryABCI(req) +} + +// GetFromAddress returns the from address from the context's name. +func (ctx Context) GetFromAddress() sdk.AccAddress { + return ctx.FromAddress +} + +// GetFromName returns the key name for the current context. +func (ctx Context) GetFromName() string { + return ctx.FromName +} + +func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + node, err := ctx.GetNode() + if err != nil { + return abci.ResponseQuery{}, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: ctx.Height, + Prove: req.Prove, + } + + result, err := node.ABCIQueryWithOptions(context.Background(), req.Path, req.Data, opts) + if err != nil { + return abci.ResponseQuery{}, err + } + + if !result.Response.IsOK() { + return abci.ResponseQuery{}, sdkErrorToGRPCError(result.Response) + } + + // data from trusted node or subspace query doesn't need verification + if !opts.Prove || !isQueryStoreWithProof(req.Path) { + return result.Response, nil + } + + return result.Response, nil +} + +func sdkErrorToGRPCError(resp abci.ResponseQuery) error { + switch resp.Code { + case sdkerrors.ErrInvalidRequest.ABCICode(): + return status.Error(codes.InvalidArgument, resp.Log) + case sdkerrors.ErrUnauthorized.ABCICode(): + return status.Error(codes.Unauthenticated, resp.Log) + case sdkerrors.ErrKeyNotFound.ABCICode(): + return status.Error(codes.NotFound, resp.Log) + default: + return status.Error(codes.Unknown, resp.Log) + } +} + +// query performs a query to a Tendermint node with the provided store name +// and path. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx Context) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { + resp, err := ctx.queryABCI(abci.RequestQuery{ + Path: path, + Data: key, + }) + if err != nil { + return nil, 0, err + } + + return resp.Value, resp.Height, nil +} + +// queryStore performs a query to a Tendermint node with the provided a store +// name and path. It returns the result and height of the query upon success +// or an error if the query fails. +func (ctx Context) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { + path := fmt.Sprintf("/store/%s/%s", storeName, endPath) + return ctx.query(path, key) +} + +// isQueryStoreWithProof expects a format like /// +// queryType must be "store" and subpath must be "key" to require a proof. +func isQueryStoreWithProof(path string) bool { + if !strings.HasPrefix(path, "/") { + return false + } + + paths := strings.SplitN(path[1:], "/", 3) + + switch { + case len(paths) != 3: + return false + case paths[0] != "store": + return false + case rootmulti.RequireProof("/" + paths[2]): + return true + } + + return false +} diff --git a/client/rest/rest.go b/client/rest/rest.go new file mode 100644 index 000000000000..ac05891e04c7 --- /dev/null +++ b/client/rest/rest.go @@ -0,0 +1,32 @@ +package rest + +import ( + "net/http" + + "github.com/gorilla/mux" +) + +// DeprecationURL is the URL for migrating deprecated REST endpoints to newer ones. +// TODO Switch to `/` (not `/master`) once v0.40 docs are deployed. +// https://github.com/cosmos/cosmos-sdk/issues/8019 +const DeprecationURL = "https://docs.cosmos.network/master/migrations/rest.html" + +// addHTTPDeprecationHeaders is a mux middleware function for adding HTTP +// Deprecation headers to a http handler +func addHTTPDeprecationHeaders(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Deprecation", "true") + w.Header().Set("Link", "<"+DeprecationURL+">; rel=\"deprecation\"") + w.Header().Set("Warning", "199 - \"this endpoint is deprecated and may not work as before, see deprecation link for more info\"") + h.ServeHTTP(w, r) + }) +} + +// WithHTTPDeprecationHeaders returns a new *mux.Router, identical to its input +// but with the addition of HTTP Deprecation headers. This is used to mark legacy +// amino REST endpoints as deprecated in the REST API. +func WithHTTPDeprecationHeaders(r *mux.Router) *mux.Router { + subRouter := r.NewRoute().Subrouter() + subRouter.Use(addHTTPDeprecationHeaders) + return subRouter +} diff --git a/client/routes.go b/client/routes.go deleted file mode 100644 index 9eac8fa0d828..000000000000 --- a/client/routes.go +++ /dev/null @@ -1,13 +0,0 @@ -package client - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/rpc" -) - -// Register routes -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - rpc.RegisterRPCRoutes(cliCtx, r) -} diff --git a/client/rpc/block.go b/client/rpc/block.go index bce145370c33..d6b79c9fe6da 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -1,20 +1,18 @@ package rpc import ( + "context" "fmt" "net/http" "strconv" "github.com/gorilla/mux" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" "github.com/cosmos/cosmos-sdk/types/rest" - - tmliteProxy "github.com/tendermint/tendermint/lite/proxy" ) //BlockCommand returns the verified block data for a given heights @@ -23,18 +21,43 @@ func BlockCommand() *cobra.Command { Use: "block [height]", Short: "Get verified data for a the block at given height", Args: cobra.MaximumNArgs(1), - RunE: printBlock, + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + var height *int64 + + // optional height + if len(args) > 0 { + h, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + if h > 0 { + tmp := int64(h) + height = &tmp + } + } + + output, err := getBlock(clientCtx, height) + if err != nil { + return err + } + + fmt.Println(string(output)) + return nil + }, } + cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) - cmd.Flags().Bool(flags.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) + return cmd } -func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { +func getBlock(clientCtx client.Context, height *int64) ([]byte, error) { // get the node - node, err := cliCtx.GetNode() + node, err := clientCtx.GetNode() if err != nil { return nil, err } @@ -42,41 +65,22 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { // header -> BlockchainInfo // header, tx -> Block // results -> BlockResults - res, err := node.Block(height) + res, err := node.Block(context.Background(), height) if err != nil { return nil, err } - if !cliCtx.TrustNode { - check, err := cliCtx.Verify(res.Block.Height) - if err != nil { - return nil, err - } - - if err := tmliteProxy.ValidateHeader(&res.Block.Header, check); err != nil { - return nil, err - } - - if err = tmliteProxy.ValidateBlock(res.Block, check); err != nil { - return nil, err - } - } - - if cliCtx.Indent { - return codec.Cdc.MarshalJSONIndent(res, "", " ") - } - - return codec.Cdc.MarshalJSON(res) + return legacy.Cdc.MarshalJSON(res) } // get the current blockchain height -func GetChainHeight(cliCtx context.CLIContext) (int64, error) { - node, err := cliCtx.GetNode() +func GetChainHeight(clientCtx client.Context) (int64, error) { + node, err := clientCtx.GetNode() if err != nil { return -1, err } - status, err := node.Status() + status, err := node.Status(context.Background()) if err != nil { return -1, err } @@ -85,35 +89,8 @@ func GetChainHeight(cliCtx context.CLIContext) (int64, error) { return height, nil } -// CMD - -func printBlock(cmd *cobra.Command, args []string) error { - var height *int64 - // optional height - if len(args) > 0 { - h, err := strconv.Atoi(args[0]) - if err != nil { - return err - } - if h > 0 { - tmp := int64(h) - height = &tmp - } - } - - output, err := getBlock(context.NewCLIContext(), height) - if err != nil { - return err - } - - fmt.Println(string(output)) - return nil -} - -// REST - // REST handler to get a block -func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func BlockRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -124,7 +101,7 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - chainHeight, err := GetChainHeight(cliCtx) + chainHeight, err := GetChainHeight(clientCtx) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, "failed to parse chain height") return @@ -135,25 +112,23 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - output, err := getBlock(cliCtx, &height) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + output, err := getBlock(clientCtx, &height) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, cliCtx, output) + rest.PostProcessResponseBare(w, clientCtx, output) } } // REST handler to get the latest block -func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func LatestBlockRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - output, err := getBlock(cliCtx, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + output, err := getBlock(clientCtx, nil) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, cliCtx, output) + rest.PostProcessResponseBare(w, clientCtx, output) } } diff --git a/client/rpc/root.go b/client/rpc/root.go deleted file mode 100644 index d614253e0f58..000000000000 --- a/client/rpc/root.go +++ /dev/null @@ -1,17 +0,0 @@ -package rpc - -import ( - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" -) - -// Register REST endpoints -func RegisterRPCRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(cliCtx)).Methods("GET") -} diff --git a/client/rpc/routes.go b/client/rpc/routes.go new file mode 100644 index 000000000000..7934ece7c92b --- /dev/null +++ b/client/rpc/routes.go @@ -0,0 +1,17 @@ +package rpc + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client" +) + +// Register REST endpoints. +func RegisterRoutes(clientCtx client.Context, r *mux.Router) { + r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(clientCtx)).Methods("GET") +} diff --git a/client/rpc/rpc_test.go b/client/rpc/rpc_test.go new file mode 100644 index 000000000000..adb7dac82d21 --- /dev/null +++ b/client/rpc/rpc_test.go @@ -0,0 +1,61 @@ +package rpc_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/codec/legacy" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/types/rest" +) + +type IntegrationTestSuite struct { + suite.Suite + + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.network = network.New(s.T(), network.DefaultConfig()) + s.Require().NotNil(s.network) + + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestStatusCommand() { + val0 := s.network.Validators[0] + cmd := rpc.StatusCommand() + + out, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cmd, []string{}) + s.Require().NoError(err) + + // Make sure the output has the validator moniker. + s.Require().Contains(out.String(), fmt.Sprintf("\"moniker\":\"%s\"", val0.Moniker)) +} + +func (s *IntegrationTestSuite) TestLatestBlocks() { + val0 := s.network.Validators[0] + + res, err := rest.GetRequest(fmt.Sprintf("%s/blocks/latest", val0.APIAddress)) + s.Require().NoError(err) + + var result ctypes.ResultBlock + err = legacy.Cdc.UnmarshalJSON(res, &result) + s.Require().NoError(err) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/client/rpc/status.go b/client/rpc/status.go index 330d595d5736..82f65520245f 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -1,67 +1,92 @@ package rpc import ( - "fmt" + "context" "net/http" "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/version" - - "github.com/tendermint/tendermint/p2p" ) +// ValidatorInfo is info about the node's validator, same as Tendermint, +// except that we use our own PubKey. +type validatorInfo struct { + Address bytes.HexBytes + PubKey cryptotypes.PubKey + VotingPower int64 +} + +// ResultStatus is node's info, same as Tendermint, except that we use our own +// PubKey. +type resultStatus struct { + NodeInfo p2p.DefaultNodeInfo + SyncInfo ctypes.SyncInfo + ValidatorInfo validatorInfo +} + // StatusCommand returns the command to return the status of the network. func StatusCommand() *cobra.Command { cmd := &cobra.Command{ Use: "status", Short: "Query remote node for status", - RunE: printNodeStatus, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + status, err := getNodeStatus(clientCtx) + if err != nil { + return err + } + + // `status` has TM pubkeys, we need to convert them to our pubkeys. + pk, err := cryptocodec.FromTmPubKeyInterface(status.ValidatorInfo.PubKey) + if err != nil { + return err + } + statusWithPk := resultStatus{ + NodeInfo: status.NodeInfo, + SyncInfo: status.SyncInfo, + ValidatorInfo: validatorInfo{ + Address: status.ValidatorInfo.Address, + PubKey: pk, + VotingPower: status.ValidatorInfo.VotingPower, + }, + } + + output, err := clientCtx.LegacyAmino.MarshalJSON(statusWithPk) + if err != nil { + return err + } + + cmd.Println(string(output)) + return nil + }, } cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) - cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") + return cmd } -func getNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) { - node, err := cliCtx.GetNode() +func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) { + node, err := clientCtx.GetNode() if err != nil { return &ctypes.ResultStatus{}, err } - return node.Status() -} - -func printNodeStatus(_ *cobra.Command, _ []string) error { - // No need to verify proof in getting node status - viper.Set(flags.FlagTrustNode, true) - cliCtx := context.NewCLIContext() - status, err := getNodeStatus(cliCtx) - if err != nil { - return err - } - - var output []byte - if cliCtx.Indent { - output, err = codec.Cdc.MarshalJSONIndent(status, "", " ") - } else { - output, err = codec.Cdc.MarshalJSON(status) - } - if err != nil { - return err - } - - fmt.Println(string(output)) - return nil + return node.Status(context.Background()) } // NodeInfoResponse defines a response type that contains node status and version @@ -73,11 +98,10 @@ type NodeInfoResponse struct { } // REST handler for node info -func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func NodeInfoRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := getNodeStatus(cliCtx) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + status, err := getNodeStatus(clientCtx) + if rest.CheckInternalServerError(w, err) { return } @@ -85,7 +109,8 @@ func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { DefaultNodeInfo: status.NodeInfo, ApplicationVersion: version.NewInfo(), } - rest.PostProcessResponseBare(w, cliCtx, resp) + + rest.PostProcessResponseBare(w, clientCtx, resp) } } @@ -95,14 +120,13 @@ type SyncingResponse struct { } // REST handler for node syncing -func NodeSyncingRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func NodeSyncingRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := getNodeStatus(cliCtx) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + status, err := getNodeStatus(clientCtx) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, cliCtx, SyncingResponse{Syncing: status.SyncInfo.CatchingUp}) + rest.PostProcessResponseBare(w, clientCtx, SyncingResponse{Syncing: status.SyncInfo.CatchingUp}) } } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 0fa06db3b821..66c594966ed7 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -1,7 +1,7 @@ package rpc import ( - "bytes" + "context" "fmt" "net/http" "strconv" @@ -9,13 +9,13 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" - "github.com/spf13/viper" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" ) @@ -23,12 +23,16 @@ import ( // TODO these next two functions feel kinda hacky based on their placement //ValidatorCommand returns the validator set for a given height -func ValidatorCommand(cdc *codec.Codec) *cobra.Command { +func ValidatorCommand() *cobra.Command { cmd := &cobra.Command{ Use: "tendermint-validator-set [height]", Short: "Get the full tendermint validator set at given height", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } var height *int64 // optional height @@ -43,25 +47,21 @@ func ValidatorCommand(cdc *codec.Codec) *cobra.Command { } } - cliCtx := context.NewCLIContext().WithCodec(cdc) + page, _ := cmd.Flags().GetInt(flags.FlagPage) + limit, _ := cmd.Flags().GetInt(flags.FlagLimit) - result, err := GetValidators(cliCtx, height, viper.GetInt(flags.FlagPage), viper.GetInt(flags.FlagLimit)) + result, err := GetValidators(clientCtx, height, &page, &limit) if err != nil { return err } - return cliCtx.PrintOutput(result) + return clientCtx.PrintObjectLegacy(result) }, } cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) - cmd.Flags().Bool(flags.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) - cmd.Flags().Bool(flags.FlagIndentResponse, false, "indent JSON response") - viper.BindPFlag(flags.FlagIndentResponse, cmd.Flags().Lookup(flags.FlagIndentResponse)) - cmd.Flags().Int(flags.FlagPage, 0, "Query a specific page of paginated results") - viper.BindPFlag(flags.FlagPage, cmd.Flags().Lookup(flags.FlagPage)) + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.Flags().Int(flags.FlagPage, rest.DefaultPage, "Query a specific page of paginated results") cmd.Flags().Int(flags.FlagLimit, 100, "Query number of results returned per page") return cmd @@ -69,10 +69,10 @@ func ValidatorCommand(cdc *codec.Codec) *cobra.Command { // Validator output in bech32 format type ValidatorOutput struct { - Address sdk.ConsAddress `json:"address"` - PubKey string `json:"pub_key"` - ProposerPriority int64 `json:"proposer_priority"` - VotingPower int64 `json:"voting_power"` + Address sdk.ConsAddress `json:"address"` + PubKey cryptotypes.PubKey `json:"pub_key"` + ProposerPriority int64 `json:"proposer_priority"` + VotingPower int64 `json:"voting_power"` } // Validators at a certain height output in bech32 format @@ -102,51 +102,40 @@ func (rvo ResultValidatorsOutput) String() string { return b.String() } -func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { - bechValPubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, validator.PubKey) +func validatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { + pk, err := cryptocodec.FromTmPubKeyInterface(validator.PubKey) if err != nil { return ValidatorOutput{}, err } return ValidatorOutput{ Address: sdk.ConsAddress(validator.Address), - PubKey: bechValPubkey, + PubKey: pk, ProposerPriority: validator.ProposerPriority, VotingPower: validator.VotingPower, }, nil } // GetValidators from client -func GetValidators(cliCtx context.CLIContext, height *int64, page, limit int) (ResultValidatorsOutput, error) { +func GetValidators(clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) { // get the node - node, err := cliCtx.GetNode() + node, err := clientCtx.GetNode() if err != nil { return ResultValidatorsOutput{}, err } - validatorsRes, err := node.Validators(height, page, limit) + validatorsRes, err := node.Validators(context.Background(), height, page, limit) if err != nil { return ResultValidatorsOutput{}, err } - if !cliCtx.TrustNode { - check, err := cliCtx.Verify(validatorsRes.BlockHeight) - if err != nil { - return ResultValidatorsOutput{}, err - } - - if !bytes.Equal(check.ValidatorsHash, tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) { - return ResultValidatorsOutput{}, fmt.Errorf("received invalid validatorset") - } - } - outputValidatorsRes := ResultValidatorsOutput{ BlockHeight: validatorsRes.BlockHeight, Validators: make([]ValidatorOutput, len(validatorsRes.Validators)), } for i := 0; i < len(validatorsRes.Validators); i++ { - outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i]) + outputValidatorsRes.Validators[i], err = validatorOutput(validatorsRes.Validators[i]) if err != nil { return ResultValidatorsOutput{}, err } @@ -158,7 +147,7 @@ func GetValidators(cliCtx context.CLIContext, height *int64, page, limit int) (R // REST // Validator Set at a height REST handler -func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 100) if err != nil { @@ -173,7 +162,7 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - chainHeight, err := GetChainHeight(cliCtx) + chainHeight, err := GetChainHeight(clientCtx) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, "failed to parse chain height") return @@ -183,17 +172,16 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - output, err := GetValidators(cliCtx, &height, page, limit) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + output, err := GetValidators(clientCtx, &height, &page, &limit) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, output) + rest.PostProcessResponse(w, clientCtx, output) } } // Latest Validator Set REST handler -func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func LatestValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 100) if err != nil { @@ -201,12 +189,11 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF return } - output, err := GetValidators(cliCtx, nil, page, limit) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + output, err := GetValidators(clientCtx, nil, &page, &limit) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, output) + rest.PostProcessResponse(w, clientCtx, output) } } diff --git a/client/test_helpers.go b/client/test_helpers.go new file mode 100644 index 000000000000..214184b50f17 --- /dev/null +++ b/client/test_helpers.go @@ -0,0 +1,83 @@ +package client + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ AccountRetriever = TestAccountRetriever{} + _ Account = TestAccount{} +) + +// TestAccount represents a client Account that can be used in unit tests +type TestAccount struct { + Address sdk.AccAddress + Num uint64 + Seq uint64 +} + +// GetAddress implements client Account.GetAddress +func (t TestAccount) GetAddress() sdk.AccAddress { + return t.Address +} + +// GetPubKey implements client Account.GetPubKey +func (t TestAccount) GetPubKey() cryptotypes.PubKey { + return nil +} + +// GetAccountNumber implements client Account.GetAccountNumber +func (t TestAccount) GetAccountNumber() uint64 { + return t.Num +} + +// GetSequence implements client Account.GetSequence +func (t TestAccount) GetSequence() uint64 { + return t.Seq +} + +// TestAccountRetriever is an AccountRetriever that can be used in unit tests +type TestAccountRetriever struct { + Accounts map[string]TestAccount +} + +// GetAccount implements AccountRetriever.GetAccount +func (t TestAccountRetriever) GetAccount(_ Context, addr sdk.AccAddress) (Account, error) { + acc, ok := t.Accounts[addr.String()] + if !ok { + return nil, fmt.Errorf("account %s not found", addr) + } + + return acc, nil +} + +// GetAccountWithHeight implements AccountRetriever.GetAccountWithHeight +func (t TestAccountRetriever) GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (Account, int64, error) { + acc, err := t.GetAccount(clientCtx, addr) + if err != nil { + return nil, 0, err + } + + return acc, 0, nil +} + +// EnsureExists implements AccountRetriever.EnsureExists +func (t TestAccountRetriever) EnsureExists(_ Context, addr sdk.AccAddress) error { + _, ok := t.Accounts[addr.String()] + if !ok { + return fmt.Errorf("account %s not found", addr) + } + return nil +} + +// GetAccountNumberSequence implements AccountRetriever.GetAccountNumberSequence +func (t TestAccountRetriever) GetAccountNumberSequence(_ Context, addr sdk.AccAddress) (accNum uint64, accSeq uint64, err error) { + acc, ok := t.Accounts[addr.String()] + if !ok { + return 0, 0, fmt.Errorf("account %s not found", addr) + } + return acc.Num, acc.Seq, nil +} diff --git a/client/tx/factory.go b/client/tx/factory.go new file mode 100644 index 000000000000..b10d728d6b87 --- /dev/null +++ b/client/tx/factory.go @@ -0,0 +1,191 @@ +package tx + +import ( + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// Factory defines a client transaction factory that facilitates generating and +// signing an application-specific transaction. +type Factory struct { + keybase keyring.Keyring + txConfig client.TxConfig + accountRetriever client.AccountRetriever + accountNumber uint64 + sequence uint64 + gas uint64 + timeoutHeight uint64 + gasAdjustment float64 + chainID string + memo string + fees sdk.Coins + gasPrices sdk.DecCoins + signMode signing.SignMode + simulateAndExecute bool +} + +// NewFactoryCLI creates a new Factory. +func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) Factory { + signModeStr := clientCtx.SignModeStr + + signMode := signing.SignMode_SIGN_MODE_UNSPECIFIED + switch signModeStr { + case flags.SignModeDirect: + signMode = signing.SignMode_SIGN_MODE_DIRECT + case flags.SignModeLegacyAminoJSON: + signMode = signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON + } + + accNum, _ := flagSet.GetUint64(flags.FlagAccountNumber) + accSeq, _ := flagSet.GetUint64(flags.FlagSequence) + gasAdj, _ := flagSet.GetFloat64(flags.FlagGasAdjustment) + memo, _ := flagSet.GetString(flags.FlagMemo) + timeoutHeight, _ := flagSet.GetUint64(flags.FlagTimeoutHeight) + + gasStr, _ := flagSet.GetString(flags.FlagGas) + gasSetting, _ := flags.ParseGasSetting(gasStr) + + f := Factory{ + txConfig: clientCtx.TxConfig, + accountRetriever: clientCtx.AccountRetriever, + keybase: clientCtx.Keyring, + chainID: clientCtx.ChainID, + gas: gasSetting.Gas, + simulateAndExecute: gasSetting.Simulate, + accountNumber: accNum, + sequence: accSeq, + timeoutHeight: timeoutHeight, + gasAdjustment: gasAdj, + memo: memo, + signMode: signMode, + } + + feesStr, _ := flagSet.GetString(flags.FlagFees) + f = f.WithFees(feesStr) + + gasPricesStr, _ := flagSet.GetString(flags.FlagGasPrices) + f = f.WithGasPrices(gasPricesStr) + + return f +} + +func (f Factory) AccountNumber() uint64 { return f.accountNumber } +func (f Factory) Sequence() uint64 { return f.sequence } +func (f Factory) Gas() uint64 { return f.gas } +func (f Factory) GasAdjustment() float64 { return f.gasAdjustment } +func (f Factory) Keybase() keyring.Keyring { return f.keybase } +func (f Factory) ChainID() string { return f.chainID } +func (f Factory) Memo() string { return f.memo } +func (f Factory) Fees() sdk.Coins { return f.fees } +func (f Factory) GasPrices() sdk.DecCoins { return f.gasPrices } +func (f Factory) AccountRetriever() client.AccountRetriever { return f.accountRetriever } +func (f Factory) TimeoutHeight() uint64 { return f.timeoutHeight } + +// SimulateAndExecute returns the option to simulate and then execute the transaction +// using the gas from the simulation results +func (f Factory) SimulateAndExecute() bool { return f.simulateAndExecute } + +// WithTxConfig returns a copy of the Factory with an updated TxConfig. +func (f Factory) WithTxConfig(g client.TxConfig) Factory { + f.txConfig = g + return f +} + +// WithAccountRetriever returns a copy of the Factory with an updated AccountRetriever. +func (f Factory) WithAccountRetriever(ar client.AccountRetriever) Factory { + f.accountRetriever = ar + return f +} + +// WithChainID returns a copy of the Factory with an updated chainID. +func (f Factory) WithChainID(chainID string) Factory { + f.chainID = chainID + return f +} + +// WithGas returns a copy of the Factory with an updated gas value. +func (f Factory) WithGas(gas uint64) Factory { + f.gas = gas + return f +} + +// WithFees returns a copy of the Factory with an updated fee. +func (f Factory) WithFees(fees string) Factory { + parsedFees, err := sdk.ParseCoinsNormalized(fees) + if err != nil { + panic(err) + } + + f.fees = parsedFees + return f +} + +// WithGasPrices returns a copy of the Factory with updated gas prices. +func (f Factory) WithGasPrices(gasPrices string) Factory { + parsedGasPrices, err := sdk.ParseDecCoins(gasPrices) + if err != nil { + panic(err) + } + + f.gasPrices = parsedGasPrices + return f +} + +// WithKeybase returns a copy of the Factory with updated Keybase. +func (f Factory) WithKeybase(keybase keyring.Keyring) Factory { + f.keybase = keybase + return f +} + +// WithSequence returns a copy of the Factory with an updated sequence number. +func (f Factory) WithSequence(sequence uint64) Factory { + f.sequence = sequence + return f +} + +// WithMemo returns a copy of the Factory with an updated memo. +func (f Factory) WithMemo(memo string) Factory { + f.memo = memo + return f +} + +// WithAccountNumber returns a copy of the Factory with an updated account number. +func (f Factory) WithAccountNumber(accnum uint64) Factory { + f.accountNumber = accnum + return f +} + +// WithGasAdjustment returns a copy of the Factory with an updated gas adjustment. +func (f Factory) WithGasAdjustment(gasAdj float64) Factory { + f.gasAdjustment = gasAdj + return f +} + +// WithSimulateAndExecute returns a copy of the Factory with an updated gas +// simulation value. +func (f Factory) WithSimulateAndExecute(sim bool) Factory { + f.simulateAndExecute = sim + return f +} + +// SignMode returns the sign mode configured in the Factory +func (f Factory) SignMode() signing.SignMode { + return f.signMode +} + +// WithSignMode returns a copy of the Factory with an updated sign mode value. +func (f Factory) WithSignMode(mode signing.SignMode) Factory { + f.signMode = mode + return f +} + +// WithTimeoutHeight returns a copy of the Factory with an updated timeout height. +func (f Factory) WithTimeoutHeight(height uint64) Factory { + f.timeoutHeight = height + return f +} diff --git a/client/tx/legacy.go b/client/tx/legacy.go new file mode 100644 index 000000000000..b551ecebb81c --- /dev/null +++ b/client/tx/legacy.go @@ -0,0 +1,88 @@ +package tx + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// ConvertTxToStdTx converts a transaction to the legacy StdTx format +func ConvertTxToStdTx(codec *codec.LegacyAmino, tx signing.Tx) (legacytx.StdTx, error) { + if stdTx, ok := tx.(legacytx.StdTx); ok { + return stdTx, nil + } + + aminoTxConfig := legacytx.StdTxConfig{Cdc: codec} + builder := aminoTxConfig.NewTxBuilder() + + err := CopyTx(tx, builder, true) + if err != nil { + + return legacytx.StdTx{}, err + } + + stdTx, ok := builder.GetTx().(legacytx.StdTx) + if !ok { + return legacytx.StdTx{}, fmt.Errorf("expected %T, got %+v", legacytx.StdTx{}, builder.GetTx()) + } + + return stdTx, nil +} + +// CopyTx copies a Tx to a new TxBuilder, allowing conversion between +// different transaction formats. If ignoreSignatureError is true, copying will continue +// tx even if the signature cannot be set in the target builder resulting in an unsigned tx. +func CopyTx(tx signing.Tx, builder client.TxBuilder, ignoreSignatureError bool) error { + err := builder.SetMsgs(tx.GetMsgs()...) + if err != nil { + return err + } + + sigs, err := tx.GetSignaturesV2() + if err != nil { + return err + } + + err = builder.SetSignatures(sigs...) + if err != nil { + if ignoreSignatureError { + // we call SetSignatures() agan with no args to clear any signatures in case the + // previous call to SetSignatures() had any partial side-effects + _ = builder.SetSignatures() + } else { + return err + } + } + + builder.SetMemo(tx.GetMemo()) + builder.SetFeeAmount(tx.GetFee()) + builder.SetGasLimit(tx.GetGas()) + builder.SetTimeoutHeight(tx.GetTimeoutHeight()) + + return nil +} + +// ConvertAndEncodeStdTx encodes the stdTx as a transaction in the format specified by txConfig +func ConvertAndEncodeStdTx(txConfig client.TxConfig, stdTx legacytx.StdTx) ([]byte, error) { + builder := txConfig.NewTxBuilder() + + var theTx sdk.Tx + + // check if we need a StdTx anyway, in that case don't copy + if _, ok := builder.GetTx().(legacytx.StdTx); ok { + theTx = stdTx + } else { + err := CopyTx(stdTx, builder, false) + if err != nil { + return nil, err + } + + theTx = builder.GetTx() + } + + return txConfig.TxEncoder()(theTx) +} diff --git a/client/tx/legacy_test.go b/client/tx/legacy_test.go new file mode 100644 index 000000000000..b10c51e34402 --- /dev/null +++ b/client/tx/legacy_test.go @@ -0,0 +1,160 @@ +package tx_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client" + tx2 "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "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" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +const ( + memo = "waboom" + gas = uint64(10000) + timeoutHeight = 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{ + SignMode: signing2.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: []byte("dummy"), + }, + } +) + +func buildTestTx(t *testing.T, builder client.TxBuilder) { + builder.SetMemo(memo) + builder.SetGasLimit(gas) + builder.SetFeeAmount(fee) + err := builder.SetMsgs(msg) + require.NoError(t, err) + err = builder.SetSignatures(sig) + require.NoError(t, err) + builder.SetTimeoutHeight(timeoutHeight) +} + +type TestSuite struct { + suite.Suite + encCfg params.EncodingConfig + protoCfg client.TxConfig + aminoCfg client.TxConfig +} + +func (s *TestSuite) SetupSuite() { + encCfg := simapp.MakeTestEncodingConfig() + s.encCfg = encCfg + s.protoCfg = tx.NewTxConfig(codec.NewProtoCodec(encCfg.InterfaceRegistry), tx.DefaultSignModes) + s.aminoCfg = legacytx.StdTxConfig{Cdc: encCfg.Amino} +} + +func (s *TestSuite) TestCopyTx() { + // proto -> amino -> proto + protoBuilder := s.protoCfg.NewTxBuilder() + buildTestTx(s.T(), protoBuilder) + aminoBuilder := s.aminoCfg.NewTxBuilder() + err := tx2.CopyTx(protoBuilder.GetTx(), aminoBuilder, false) + s.Require().NoError(err) + protoBuilder2 := s.protoCfg.NewTxBuilder() + err = tx2.CopyTx(aminoBuilder.GetTx(), protoBuilder2, false) + s.Require().NoError(err) + bz, err := s.protoCfg.TxEncoder()(protoBuilder.GetTx()) + s.Require().NoError(err) + bz2, err := s.protoCfg.TxEncoder()(protoBuilder2.GetTx()) + s.Require().NoError(err) + s.Require().Equal(bz, bz2) + + // amino -> proto -> amino + aminoBuilder = s.aminoCfg.NewTxBuilder() + buildTestTx(s.T(), aminoBuilder) + protoBuilder = s.protoCfg.NewTxBuilder() + err = tx2.CopyTx(aminoBuilder.GetTx(), protoBuilder, false) + s.Require().NoError(err) + aminoBuilder2 := s.aminoCfg.NewTxBuilder() + err = tx2.CopyTx(protoBuilder.GetTx(), aminoBuilder2, false) + s.Require().NoError(err) + bz, err = s.aminoCfg.TxEncoder()(aminoBuilder.GetTx()) + s.Require().NoError(err) + bz2, err = s.aminoCfg.TxEncoder()(aminoBuilder2.GetTx()) + s.Require().NoError(err) + s.Require().Equal(bz, bz2) +} + +func (s *TestSuite) TestConvertTxToStdTx() { + // proto tx + protoBuilder := s.protoCfg.NewTxBuilder() + buildTestTx(s.T(), protoBuilder) + stdTx, err := tx2.ConvertTxToStdTx(s.encCfg.Amino, protoBuilder.GetTx()) + s.Require().NoError(err) + 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(timeoutHeight, stdTx.TimeoutHeight) + s.Require().Equal(sig.PubKey, stdTx.Signatures[0].PubKey) + s.Require().Equal(sig.Data.(*signing2.SingleSignatureData).Signature, stdTx.Signatures[0].Signature) + + // SIGN_MODE_DIRECT should fall back to an unsigned tx + err = protoBuilder.SetSignatures(signing2.SignatureV2{ + PubKey: pub1, + Data: &signing2.SingleSignatureData{ + SignMode: signing2.SignMode_SIGN_MODE_DIRECT, + Signature: []byte("dummy"), + }, + }) + s.Require().NoError(err) + stdTx, err = tx2.ConvertTxToStdTx(s.encCfg.Amino, protoBuilder.GetTx()) + s.Require().NoError(err) + 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(timeoutHeight, stdTx.TimeoutHeight) + s.Require().Empty(stdTx.Signatures) + + // std tx + aminoBuilder := s.aminoCfg.NewTxBuilder() + buildTestTx(s.T(), aminoBuilder) + stdTx = aminoBuilder.GetTx().(legacytx.StdTx) + stdTx2, err := tx2.ConvertTxToStdTx(s.encCfg.Amino, stdTx) + s.Require().NoError(err) + s.Require().Equal(stdTx, stdTx2) +} + +func (s *TestSuite) TestConvertAndEncodeStdTx() { + // convert amino -> proto -> amino + aminoBuilder := s.aminoCfg.NewTxBuilder() + buildTestTx(s.T(), aminoBuilder) + stdTx := aminoBuilder.GetTx().(legacytx.StdTx) + txBz, err := tx2.ConvertAndEncodeStdTx(s.protoCfg, stdTx) + s.Require().NoError(err) + decodedTx, err := s.protoCfg.TxDecoder()(txBz) + s.Require().NoError(err) + aminoBuilder2 := s.aminoCfg.NewTxBuilder() + s.Require().NoError(tx2.CopyTx(decodedTx.(signing.Tx), aminoBuilder2, false)) + s.Require().Equal(stdTx, aminoBuilder2.GetTx()) + + // just use amino everywhere + txBz, err = tx2.ConvertAndEncodeStdTx(s.aminoCfg, stdTx) + s.Require().NoError(err) + decodedTx, err = s.aminoCfg.TxDecoder()(txBz) + s.Require().NoError(err) + s.Require().Equal(stdTx, decodedTx) +} diff --git a/client/tx/tx.go b/client/tx/tx.go new file mode 100644 index 000000000000..594293732bc8 --- /dev/null +++ b/client/tx/tx.go @@ -0,0 +1,475 @@ +package tx + +import ( + "bufio" + "errors" + "fmt" + "net/http" + "os" + + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +// GenerateOrBroadcastTxCLI will either generate and print and unsigned transaction +// or sign it and broadcast it returning an error upon failure. +func GenerateOrBroadcastTxCLI(clientCtx client.Context, flagSet *pflag.FlagSet, msgs ...sdk.Msg) error { + txf := NewFactoryCLI(clientCtx, flagSet) + return GenerateOrBroadcastTxWithFactory(clientCtx, txf, msgs...) +} + +// GenerateOrBroadcastTxWithFactory will either generate and print and unsigned transaction +// or sign it and broadcast it returning an error upon failure. +func GenerateOrBroadcastTxWithFactory(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { + if clientCtx.GenerateOnly { + return GenerateTx(clientCtx, txf, msgs...) + } + + return BroadcastTx(clientCtx, txf, msgs...) +} + +// GenerateTx will generate an unsigned transaction and print it to the writer +// specified by ctx.Output. If simulation was requested, the gas will be +// simulated and also printed to the same writer before the transaction is +// printed. +func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { + if txf.SimulateAndExecute() { + if clientCtx.Offline { + return errors.New("cannot estimate gas in offline mode") + } + + _, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...) + if err != nil { + return err + } + + txf = txf.WithGas(adjusted) + _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return err + } + + json, err := clientCtx.TxConfig.TxJSONEncoder()(tx.GetTx()) + if err != nil { + return err + } + + return clientCtx.PrintString(fmt.Sprintf("%s\n", json)) +} + +// BroadcastTx attempts to generate, sign and broadcast a transaction with the +// given set of messages. It will also simulate gas requirements if necessary. +// It will return an error upon failure. +func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { + txf, err := PrepareFactory(clientCtx, txf) + if err != nil { + return err + } + + if txf.SimulateAndExecute() || clientCtx.Simulate { + _, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...) + if err != nil { + return err + } + + txf = txf.WithGas(adjusted) + _, _ = fmt.Fprintf(os.Stderr, "%s\n", GasEstimateResponse{GasEstimate: txf.Gas()}) + } + + if clientCtx.Simulate { + return nil + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return err + } + + if !clientCtx.SkipConfirm { + out, err := clientCtx.TxConfig.TxJSONEncoder()(tx.GetTx()) + if err != nil { + return err + } + + _, _ = fmt.Fprintf(os.Stderr, "%s\n\n", out) + + buf := bufio.NewReader(os.Stdin) + ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr) + + if err != nil || !ok { + _, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction") + return err + } + } + + err = Sign(txf, clientCtx.GetFromName(), tx, true) + if err != nil { + return err + } + + txBytes, err := clientCtx.TxConfig.TxEncoder()(tx.GetTx()) + if err != nil { + return err + } + + // broadcast to a Tendermint node + res, err := clientCtx.BroadcastTx(txBytes) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) +} + +// WriteGeneratedTxResponse writes a generated unsigned transaction to the +// provided http.ResponseWriter. It will simulate gas costs if requested by the +// BaseReq. Upon any error, the error will be written to the http.ResponseWriter. +// Note that this function returns the legacy StdTx Amino JSON format for compatibility +// with legacy clients. +func WriteGeneratedTxResponse( + ctx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg, +) { + gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment) + if !ok { + return + } + + gasSetting, err := flags.ParseGasSetting(br.Gas) + if rest.CheckBadRequestError(w, err) { + return + } + + txf := Factory{fees: br.Fees, gasPrices: br.GasPrices}. + WithAccountNumber(br.AccountNumber). + WithSequence(br.Sequence). + WithGas(gasSetting.Gas). + WithGasAdjustment(gasAdj). + WithMemo(br.Memo). + WithChainID(br.ChainID). + WithSimulateAndExecute(br.Simulate). + WithTxConfig(ctx.TxConfig). + WithTimeoutHeight(br.TimeoutHeight) + + if br.Simulate || gasSetting.Simulate { + if gasAdj < 0 { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkerrors.ErrorInvalidGasAdjustment.Error()) + return + } + + _, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...) + if rest.CheckInternalServerError(w, err) { + return + } + + txf = txf.WithGas(adjusted) + + if br.Simulate { + rest.WriteSimulationResponse(w, ctx.LegacyAmino, txf.Gas()) + return + } + } + + tx, err := BuildUnsignedTx(txf, msgs...) + if rest.CheckBadRequestError(w, err) { + return + } + + stdTx, err := ConvertTxToStdTx(ctx.LegacyAmino, tx.GetTx()) + if rest.CheckInternalServerError(w, err) { + return + } + + output, err := ctx.LegacyAmino.MarshalJSON(stdTx) + if rest.CheckInternalServerError(w, err) { + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(output) +} + +// BuildUnsignedTx builds a transaction to be signed given a set of messages. The +// transaction is initially created via the provided factory's generator. Once +// created, the fee, memo, and messages are set. +func BuildUnsignedTx(txf Factory, msgs ...sdk.Msg) (client.TxBuilder, error) { + if txf.chainID == "" { + return nil, fmt.Errorf("chain ID required but not specified") + } + + fees := txf.fees + + if !txf.gasPrices.IsZero() { + if !fees.IsZero() { + return nil, errors.New("cannot provide both fees and gas prices") + } + + glDec := sdk.NewDec(int64(txf.gas)) + + // Derive the fees based on the provided gas prices, where + // fee = ceil(gasPrice * gasLimit). + fees = make(sdk.Coins, len(txf.gasPrices)) + + for i, gp := range txf.gasPrices { + fee := gp.Amount.Mul(glDec) + fees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + } + + tx := txf.txConfig.NewTxBuilder() + + if err := tx.SetMsgs(msgs...); err != nil { + return nil, err + } + + tx.SetMemo(txf.memo) + tx.SetFeeAmount(fees) + tx.SetGasLimit(txf.gas) + tx.SetTimeoutHeight(txf.TimeoutHeight()) + + return tx, nil +} + +// BuildSimTx creates an unsigned tx with an empty single signature and returns +// the encoded transaction or an error if the unsigned transaction cannot be +// built. +func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) { + txb, err := BuildUnsignedTx(txf, msgs...) + if err != nil { + return nil, err + } + + // Create an empty signature literal as the ante handler will populate with a + // sentinel pubkey. + sig := signing.SignatureV2{ + PubKey: &secp256k1.PubKey{}, + Data: &signing.SingleSignatureData{ + SignMode: txf.signMode, + }, + Sequence: txf.Sequence(), + } + if err := txb.SetSignatures(sig); err != nil { + return nil, err + } + + protoProvider, ok := txb.(authtx.ProtoTxProvider) + if !ok { + return nil, fmt.Errorf("cannot simulate amino tx") + } + simReq := tx.SimulateRequest{Tx: protoProvider.GetProtoTx()} + + return simReq.Marshal() +} + +// CalculateGas simulates the execution of a transaction and returns the +// simulation response obtained by the query and the adjusted gas amount. +func CalculateGas( + queryFunc func(string, []byte) ([]byte, int64, error), txf Factory, msgs ...sdk.Msg, +) (tx.SimulateResponse, uint64, error) { + txBytes, err := BuildSimTx(txf, msgs...) + if err != nil { + return tx.SimulateResponse{}, 0, err + } + + // TODO This should use the generated tx service Client. + // https://github.com/cosmos/cosmos-sdk/issues/7726 + bz, _, err := queryFunc("/cosmos.tx.v1beta1.Service/Simulate", txBytes) + if err != nil { + return tx.SimulateResponse{}, 0, err + } + + var simRes tx.SimulateResponse + + if err := simRes.Unmarshal(bz); err != nil { + return tx.SimulateResponse{}, 0, err + } + + return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasInfo.GasUsed)), nil +} + +// PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and +// if the account number and/or the account sequence number are zero (not set), +// they will be queried for and set on the provided Factory. A new Factory with +// the updated fields will be returned. +func PrepareFactory(clientCtx client.Context, txf Factory) (Factory, error) { + from := clientCtx.GetFromAddress() + + if err := txf.accountRetriever.EnsureExists(clientCtx, from); err != nil { + return txf, err + } + + initNum, initSeq := txf.accountNumber, txf.sequence + if initNum == 0 || initSeq == 0 { + num, seq, err := txf.accountRetriever.GetAccountNumberSequence(clientCtx, from) + if err != nil { + return txf, err + } + + if initNum == 0 { + txf = txf.WithAccountNumber(num) + } + + if initSeq == 0 { + txf = txf.WithSequence(seq) + } + } + + return txf, nil +} + +// SignWithPrivKey signs a given tx with the given private key, and returns the +// corresponding SignatureV2 if the signing is successful. +func SignWithPrivKey( + signMode signing.SignMode, signerData authsigning.SignerData, + txBuilder client.TxBuilder, priv cryptotypes.PrivKey, txConfig client.TxConfig, + accSeq uint64, +) (signing.SignatureV2, error) { + var sigV2 signing.SignatureV2 + + // Generate the bytes to be signed. + signBytes, err := txConfig.SignModeHandler().GetSignBytes(signMode, signerData, txBuilder.GetTx()) + if err != nil { + return sigV2, err + } + + // Sign those bytes + signature, err := priv.Sign(signBytes) + if err != nil { + return sigV2, err + } + + // Construct the SignatureV2 struct + sigData := signing.SingleSignatureData{ + SignMode: signMode, + Signature: signature, + } + + sigV2 = signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &sigData, + Sequence: accSeq, + } + + return sigV2, nil +} + +func checkMultipleSigners(mode signing.SignMode, tx authsigning.Tx) error { + if mode == signing.SignMode_SIGN_MODE_DIRECT && + len(tx.GetSigners()) > 1 { + return sdkerrors.Wrap(sdkerrors.ErrNotSupported, "Signing in DIRECT mode is only supported for transactions with one signer only") + } + return nil +} + +// Sign signs a given tx with a named key. The bytes signed over are canconical. +// The resulting signature will be added to the transaction builder overwriting the previous +// ones if overwrite=true (otherwise, the signature will be appended). +// Signing a transaction with mutltiple signers in the DIRECT mode is not supprted and will +// return an error. +// An error is returned upon failure. +func Sign(txf Factory, name string, txBuilder client.TxBuilder, overwriteSig bool) error { + if txf.keybase == nil { + return errors.New("keybase must be set prior to signing a transaction") + } + + signMode := txf.signMode + if signMode == signing.SignMode_SIGN_MODE_UNSPECIFIED { + // use the SignModeHandler's default mode if unspecified + signMode = txf.txConfig.SignModeHandler().DefaultMode() + } + if err := checkMultipleSigners(signMode, txBuilder.GetTx()); err != nil { + return err + } + + key, err := txf.keybase.Key(name) + if err != nil { + return err + } + pubKey := key.GetPubKey() + signerData := authsigning.SignerData{ + ChainID: txf.chainID, + AccountNumber: txf.accountNumber, + Sequence: txf.sequence, + } + + // For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on + // TxBuilder under the hood, and SignerInfos is needed to generated the + // sign bytes. This is the reason for setting SetSignatures here, with a + // nil signature. + // + // Note: this line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it + // also doesn't affect its generated sign bytes, so for code's simplicity + // sake, we put it here. + sigData := signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + } + sig := signing.SignatureV2{ + PubKey: pubKey, + Data: &sigData, + Sequence: txf.Sequence(), + } + var prevSignatures []signing.SignatureV2 + if !overwriteSig { + prevSignatures, err = txBuilder.GetTx().GetSignaturesV2() + if err != nil { + return err + } + } + if err := txBuilder.SetSignatures(sig); err != nil { + return err + } + + // Generate the bytes to be signed. + bytesToSign, err := txf.txConfig.SignModeHandler().GetSignBytes(signMode, signerData, txBuilder.GetTx()) + if err != nil { + return err + } + + // Sign those bytes + sigBytes, _, err := txf.keybase.Sign(name, bytesToSign) + if err != nil { + return err + } + + // Construct the SignatureV2 struct + sigData = signing.SingleSignatureData{ + SignMode: signMode, + Signature: sigBytes, + } + sig = signing.SignatureV2{ + PubKey: pubKey, + Data: &sigData, + Sequence: txf.Sequence(), + } + + if overwriteSig { + return txBuilder.SetSignatures(sig) + } + prevSignatures = append(prevSignatures, sig) + return txBuilder.SetSignatures(prevSignatures...) +} + +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"` +} + +func (gr GasEstimateResponse) String() string { + return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) +} diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go new file mode 100644 index 000000000000..3c7a4bebef0e --- /dev/null +++ b/client/tx/tx_test.go @@ -0,0 +1,232 @@ +package tx_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func NewTestTxConfig() client.TxConfig { + cfg := simapp.MakeTestEncodingConfig() + return cfg.TxConfig +} + +func TestCalculateGas(t *testing.T) { + makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) { + return func(string, []byte) ([]byte, int64, error) { + if wantErr { + return nil, 0, errors.New("query failed") + } + simRes := &txtypes.SimulateResponse{ + GasInfo: &sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, + } + + bz, err := simRes.Marshal() + if err != nil { + return nil, 0, err + } + + return bz, 0, nil + } + } + + type args struct { + queryFuncGasUsed uint64 + queryFuncWantErr bool + adjustment float64 + } + + testCases := []struct { + name string + args args + wantEstimate uint64 + wantAdjusted uint64 + expPass bool + }{ + {"error", args{0, true, 1.2}, 0, 0, false}, + {"adjusted gas", args{10, false, 1.2}, 10, 12, true}, + } + + for _, tc := range testCases { + stc := tc + txCfg := NewTestTxConfig() + + txf := tx.Factory{}. + WithChainID("test-chain"). + WithTxConfig(txCfg).WithSignMode(txCfg.SignModeHandler().DefaultMode()) + + t.Run(stc.name, func(t *testing.T) { + queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr) + simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, txf.WithGasAdjustment(stc.args.adjustment)) + if stc.expPass { + require.NoError(t, err) + require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate) + require.Equal(t, gotAdjusted, stc.wantAdjusted) + require.NotNil(t, simRes.Result) + } else { + require.Error(t, err) + require.Nil(t, simRes.Result) + } + }) + } +} + +func TestBuildSimTx(t *testing.T) { + txCfg := NewTestTxConfig() + + txf := tx.Factory{}. + WithTxConfig(txCfg). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain"). + WithSignMode(txCfg.SignModeHandler().DefaultMode()) + + msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) + bz, err := tx.BuildSimTx(txf, msg) + require.NoError(t, err) + require.NotNil(t, bz) +} + +func TestBuildUnsignedTx(t *testing.T) { + txf := tx.Factory{}. + WithTxConfig(NewTestTxConfig()). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain") + + msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) + tx, err := tx.BuildUnsignedTx(txf, msg) + require.NoError(t, err) + require.NotNil(t, tx) + + sigs, err := tx.GetTx().(signing.SigVerifiableTx).GetSignaturesV2() + require.NoError(t, err) + require.Empty(t, sigs) +} + +func TestSign(t *testing.T) { + requireT := require.New(t) + path := hd.CreateHDPath(118, 0, 0).String() + kr, err := keyring.New(t.Name(), "test", t.TempDir(), nil) + requireT.NoError(err) + + var from1 = "test_key1" + var from2 = "test_key2" + + // create a new key using a mnemonic generator and test if we can reuse seed to recreate that account + _, seed, err := kr.NewMnemonic(from1, keyring.English, path, hd.Secp256k1) + requireT.NoError(err) + requireT.NoError(kr.Delete(from1)) + info1, _, err := kr.NewMnemonic(from1, keyring.English, path, hd.Secp256k1) + requireT.NoError(err) + + info2, err := kr.NewAccount(from2, seed, "", path, hd.Secp256k1) + requireT.NoError(err) + + pubKey1 := info1.GetPubKey() + pubKey2 := info2.GetPubKey() + requireT.NotEqual(pubKey1.Bytes(), pubKey2.Bytes()) + t.Log("Pub keys:", pubKey1, pubKey2) + + txfNoKeybase := tx.Factory{}. + WithTxConfig(NewTestTxConfig()). + WithAccountNumber(50). + WithSequence(23). + WithFees("50stake"). + WithMemo("memo"). + WithChainID("test-chain") + txfDirect := txfNoKeybase. + WithKeybase(kr). + WithSignMode(signingtypes.SignMode_SIGN_MODE_DIRECT) + txfAmino := txfDirect. + WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + msg1 := banktypes.NewMsgSend(info1.GetAddress(), sdk.AccAddress("to"), nil) + msg2 := banktypes.NewMsgSend(info2.GetAddress(), sdk.AccAddress("to"), nil) + txb, err := tx.BuildUnsignedTx(txfNoKeybase, msg1, msg2) + requireT.NoError(err) + txb2, err := tx.BuildUnsignedTx(txfNoKeybase, msg1, msg2) + requireT.NoError(err) + txbSimple, err := tx.BuildUnsignedTx(txfNoKeybase, msg2) + requireT.NoError(err) + + testCases := []struct { + name string + txf tx.Factory + txb client.TxBuilder + from string + overwrite bool + expectedPKs []cryptotypes.PubKey + matchingSigs []int // if not nil, check matching signature against old ones. + }{ + {"should fail if txf without keyring", + txfNoKeybase, txb, from1, true, nil, nil}, + {"should fail for non existing key", + txfAmino, txb, "unknown", true, nil, nil}, + {"amino: should succeed with keyring", + txfAmino, txbSimple, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, + {"direct: should succeed with keyring", + txfDirect, txbSimple, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, + + /**** test double sign Amino mode ****/ + {"amino: should sign multi-signers tx", + txfAmino, txb, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, + {"amino: should append a second signature and not overwrite", + txfAmino, txb, from2, false, []cryptotypes.PubKey{pubKey1, pubKey2}, []int{0, 0}}, + {"amino: should overwrite a signature", + txfAmino, txb, from2, true, []cryptotypes.PubKey{pubKey2}, []int{1, 0}}, + + /**** test double sign Direct mode + signing transaction with more than 2 signers should fail in DIRECT mode ****/ + {"direct: should fail to append a signature with different mode", + txfDirect, txb, from1, false, []cryptotypes.PubKey{}, nil}, + {"direct: should fail to sign multi-signers tx", + txfDirect, txb2, from1, false, []cryptotypes.PubKey{}, nil}, + {"direct: should fail to overwrite multi-signers tx", + txfDirect, txb2, from1, true, []cryptotypes.PubKey{}, nil}, + } + var prevSigs []signingtypes.SignatureV2 + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err = tx.Sign(tc.txf, tc.from, tc.txb, tc.overwrite) + if len(tc.expectedPKs) == 0 { + requireT.Error(err) + } else { + requireT.NoError(err) + sigs := testSigners(requireT, tc.txb.GetTx(), tc.expectedPKs...) + if tc.matchingSigs != nil { + requireT.Equal(prevSigs[tc.matchingSigs[0]], sigs[tc.matchingSigs[1]]) + } + prevSigs = sigs + } + }) + } +} + +func testSigners(require *require.Assertions, tr signing.Tx, pks ...cryptotypes.PubKey) []signingtypes.SignatureV2 { + sigs, err := tr.GetSignaturesV2() + require.Len(sigs, len(pks)) + require.NoError(err) + require.Len(sigs, len(pks)) + for i := range pks { + require.True(sigs[i].PubKey.Equals(pks[i]), "Signature is signed with a wrong pubkey. Got: %s, expected: %s", sigs[i].PubKey, pks[i]) + } + return sigs +} diff --git a/client/tx_config.go b/client/tx_config.go new file mode 100644 index 000000000000..6992a7a2402d --- /dev/null +++ b/client/tx_config.go @@ -0,0 +1,46 @@ +package client + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +type ( + // TxEncodingConfig defines an interface that contains transaction + // encoders and decoders + TxEncodingConfig interface { + TxEncoder() sdk.TxEncoder + TxDecoder() sdk.TxDecoder + TxJSONEncoder() sdk.TxEncoder + TxJSONDecoder() sdk.TxDecoder + MarshalSignatureJSON([]signingtypes.SignatureV2) ([]byte, error) + UnmarshalSignatureJSON([]byte) ([]signingtypes.SignatureV2, error) + } + + // TxConfig defines an interface a client can utilize to generate an + // application-defined concrete transaction type. The type returned must + // implement TxBuilder. + TxConfig interface { + TxEncodingConfig + + NewTxBuilder() TxBuilder + WrapTxBuilder(sdk.Tx) (TxBuilder, error) + SignModeHandler() signing.SignModeHandler + } + + // TxBuilder defines an interface which an application-defined concrete transaction + // type must implement. Namely, it must be able to set messages, generate + // signatures, and provide canonical bytes to sign over. The transaction must + // also know how to encode itself. + TxBuilder interface { + GetTx() signing.Tx + + SetMsgs(msgs ...sdk.Msg) error + SetSignatures(signatures ...signingtypes.SignatureV2) error + SetMemo(memo string) + SetFeeAmount(amount sdk.Coins) + SetGasLimit(limit uint64) + SetTimeoutHeight(height uint64) + } +) diff --git a/client/utils.go b/client/utils.go index 1a71dfcea66d..b833d4c1f0c5 100644 --- a/client/utils.go +++ b/client/utils.go @@ -1,16 +1,32 @@ package client +import ( + "github.com/spf13/pflag" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" +) + // Paginate returns the correct starting and ending index for a paginated query, // given that client provides a desired page and limit of objects and the handler -// provides the total number of objects. If the start page is invalid, non-positive -// values are returned signaling the request is invalid. -// -// NOTE: The start page is assumed to be 1-indexed. +// provides the total number of objects. The start page is assumed to be 1-indexed. +// If the start page is invalid, non-positive values are returned signaling the +// request is invalid; it returns non-positive values if limit is non-positive and +// defLimit is negative. func Paginate(numObjs, page, limit, defLimit int) (start, end int) { - if page == 0 { + if page <= 0 { // invalid start page return -1, -1 - } else if limit == 0 { + } + + // fallback to default limit if supplied limit is invalid + if limit <= 0 { + if defLimit < 0 { + // invalid default limit + return -1, -1 + } limit = defLimit } @@ -28,3 +44,35 @@ func Paginate(numObjs, page, limit, defLimit int) (start, end int) { return start, end } + +// ReadPageRequest reads and builds the necessary page request flags for pagination. +func ReadPageRequest(flagSet *pflag.FlagSet) (*query.PageRequest, error) { + pageKey, _ := flagSet.GetString(flags.FlagPageKey) + offset, _ := flagSet.GetUint64(flags.FlagOffset) + limit, _ := flagSet.GetUint64(flags.FlagLimit) + countTotal, _ := flagSet.GetBool(flags.FlagCountTotal) + page, _ := flagSet.GetUint64(flags.FlagPage) + + if page > 1 && offset > 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "page and offset cannot be used together") + } + + if page > 1 { + offset = (page - 1) * limit + } + + return &query.PageRequest{ + Key: []byte(pageKey), + Offset: offset, + Limit: limit, + CountTotal: countTotal, + }, nil +} + +// NewClientFromNode sets up Client implementation that communicates with a Tendermint node over +// JSON RPC and WebSockets +// TODO: We might not need to manually append `/websocket`: +// https://github.com/cosmos/cosmos-sdk/issues/8986 +func NewClientFromNode(nodeURI string) (*rpchttp.HTTP, error) { + return rpchttp.New(nodeURI, "/websocket") +} diff --git a/client/utils_test.go b/client/utils_test.go index 0f1506c562e2..c8cb93a9f56d 100644 --- a/client/utils_test.go +++ b/client/utils_test.go @@ -39,6 +39,11 @@ func TestPaginate(t *testing.T) { 75, 2, 50, 100, 50, 75, }, + { + "fallback to default limit", + 75, 5, 0, 10, + 40, 50, + }, { "invalid start page", 75, 4, 25, 100, @@ -49,6 +54,16 @@ func TestPaginate(t *testing.T) { 75, 0, 25, 100, -1, -1, }, + { + "invalid negative start page", + 75, -1, 25, 100, + -1, -1, + }, + { + "invalid default limit", + 75, 2, 0, -10, + -1, -1, + }, } for i, tc := range testCases { diff --git a/codec/amino.go b/codec/amino.go new file mode 100644 index 000000000000..78fd650ca4b8 --- /dev/null +++ b/codec/amino.go @@ -0,0 +1,197 @@ +package codec + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + + amino "github.com/tendermint/go-amino" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// deprecated: LegacyAmino defines a wrapper for an Amino codec that properly handles protobuf +// types with Any's +type LegacyAmino struct { + Amino *amino.Codec +} + +func (cdc *LegacyAmino) Seal() { + cdc.Amino.Seal() +} + +func NewLegacyAmino() *LegacyAmino { + return &LegacyAmino{amino.NewCodec()} +} + +// RegisterEvidences registers Tendermint evidence types with the provided Amino +// codec. +func RegisterEvidences(cdc *LegacyAmino) { + cdc.Amino.RegisterInterface((*tmtypes.Evidence)(nil), nil) + cdc.Amino.RegisterConcrete(&tmtypes.DuplicateVoteEvidence{}, "tendermint/DuplicateVoteEvidence", nil) +} + +// MarshalJSONIndent provides a utility for indented JSON encoding of an object +// via an Amino codec. It returns an error if it cannot serialize or indent as +// JSON. +func MarshalJSONIndent(cdc *LegacyAmino, obj interface{}) ([]byte, error) { + bz, err := cdc.MarshalJSON(obj) + if err != nil { + return nil, err + } + + var out bytes.Buffer + if err = json.Indent(&out, bz, "", " "); err != nil { + return nil, err + } + + return out.Bytes(), nil +} + +// MustMarshalJSONIndent executes MarshalJSONIndent except it panics upon failure. +func MustMarshalJSONIndent(cdc *LegacyAmino, obj interface{}) []byte { + bz, err := MarshalJSONIndent(cdc, obj) + if err != nil { + panic(fmt.Sprintf("failed to marshal JSON: %s", err)) + } + + return bz +} + +func (cdc *LegacyAmino) marshalAnys(o interface{}) error { + return types.UnpackInterfaces(o, types.AminoPacker{Cdc: cdc.Amino}) +} + +func (cdc *LegacyAmino) unmarshalAnys(o interface{}) error { + return types.UnpackInterfaces(o, types.AminoUnpacker{Cdc: cdc.Amino}) +} + +func (cdc *LegacyAmino) jsonMarshalAnys(o interface{}) error { + return types.UnpackInterfaces(o, types.AminoJSONPacker{Cdc: cdc.Amino}) +} + +func (cdc *LegacyAmino) jsonUnmarshalAnys(o interface{}) error { + return types.UnpackInterfaces(o, types.AminoJSONUnpacker{Cdc: cdc.Amino}) +} + +func (cdc *LegacyAmino) MarshalBinaryBare(o interface{}) ([]byte, error) { + err := cdc.marshalAnys(o) + if err != nil { + return nil, err + } + return cdc.Amino.MarshalBinaryBare(o) +} + +func (cdc *LegacyAmino) MustMarshalBinaryBare(o interface{}) []byte { + bz, err := cdc.MarshalBinaryBare(o) + if err != nil { + panic(err) + } + return bz +} + +func (cdc *LegacyAmino) MarshalBinaryLengthPrefixed(o interface{}) ([]byte, error) { + err := cdc.marshalAnys(o) + if err != nil { + return nil, err + } + return cdc.Amino.MarshalBinaryLengthPrefixed(o) +} + +func (cdc *LegacyAmino) MustMarshalBinaryLengthPrefixed(o interface{}) []byte { + bz, err := cdc.MarshalBinaryLengthPrefixed(o) + if err != nil { + panic(err) + } + return bz +} + +func (cdc *LegacyAmino) UnmarshalBinaryBare(bz []byte, ptr interface{}) error { + err := cdc.Amino.UnmarshalBinaryBare(bz, ptr) + if err != nil { + return err + } + return cdc.unmarshalAnys(ptr) +} + +func (cdc *LegacyAmino) MustUnmarshalBinaryBare(bz []byte, ptr interface{}) { + err := cdc.UnmarshalBinaryBare(bz, ptr) + if err != nil { + panic(err) + } +} + +func (cdc *LegacyAmino) UnmarshalBinaryLengthPrefixed(bz []byte, ptr interface{}) error { + err := cdc.Amino.UnmarshalBinaryLengthPrefixed(bz, ptr) + if err != nil { + return err + } + return cdc.unmarshalAnys(ptr) +} + +func (cdc *LegacyAmino) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr interface{}) { + err := cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) + if err != nil { + panic(err) + } +} + +// MarshalJSON implements codec.Marshaler interface +func (cdc *LegacyAmino) MarshalJSON(o interface{}) ([]byte, error) { + err := cdc.jsonMarshalAnys(o) + if err != nil { + return nil, err + } + return cdc.Amino.MarshalJSON(o) +} + +func (cdc *LegacyAmino) MustMarshalJSON(o interface{}) []byte { + bz, err := cdc.MarshalJSON(o) + if err != nil { + panic(err) + } + return bz +} + +// UnmarshalJSON implements codec.Marshaler interface +func (cdc *LegacyAmino) UnmarshalJSON(bz []byte, ptr interface{}) error { + err := cdc.Amino.UnmarshalJSON(bz, ptr) + if err != nil { + return err + } + return cdc.jsonUnmarshalAnys(ptr) +} + +func (cdc *LegacyAmino) MustUnmarshalJSON(bz []byte, ptr interface{}) { + err := cdc.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } +} + +func (*LegacyAmino) UnpackAny(*types.Any, interface{}) error { + return errors.New("AminoCodec can't handle unpack protobuf Any's") +} + +func (cdc *LegacyAmino) RegisterInterface(ptr interface{}, iopts *amino.InterfaceOptions) { + cdc.Amino.RegisterInterface(ptr, iopts) +} + +func (cdc *LegacyAmino) RegisterConcrete(o interface{}, name string, copts *amino.ConcreteOptions) { + cdc.Amino.RegisterConcrete(o, name, copts) +} + +func (cdc *LegacyAmino) MarshalJSONIndent(o interface{}, prefix, indent string) ([]byte, error) { + err := cdc.jsonMarshalAnys(o) + if err != nil { + panic(err) + } + return cdc.Amino.MarshalJSONIndent(o, prefix, indent) +} + +func (cdc *LegacyAmino) PrintTypes(out io.Writer) error { + return cdc.Amino.PrintTypes(out) +} diff --git a/codec/amino_codec.go b/codec/amino_codec.go new file mode 100644 index 000000000000..3ba7a2feb9b7 --- /dev/null +++ b/codec/amino_codec.go @@ -0,0 +1,124 @@ +package codec + +import ( + "github.com/gogo/protobuf/proto" +) + +// AminoCodec defines a codec that utilizes Codec for both binary and JSON +// encoding. +type AminoCodec struct { + *LegacyAmino +} + +var _ Marshaler = &AminoCodec{} + +// NewAminoCodec returns a reference to a new AminoCodec +func NewAminoCodec(codec *LegacyAmino) *AminoCodec { + return &AminoCodec{LegacyAmino: codec} +} + +// MarshalBinaryBare implements BinaryMarshaler.MarshalBinaryBare method. +func (ac *AminoCodec) MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) { + return ac.LegacyAmino.MarshalBinaryBare(o) +} + +// MustMarshalBinaryBare implements BinaryMarshaler.MustMarshalBinaryBare method. +func (ac *AminoCodec) MustMarshalBinaryBare(o ProtoMarshaler) []byte { + return ac.LegacyAmino.MustMarshalBinaryBare(o) +} + +// MarshalBinaryLengthPrefixed implements BinaryMarshaler.MarshalBinaryLengthPrefixed method. +func (ac *AminoCodec) MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, error) { + return ac.LegacyAmino.MarshalBinaryLengthPrefixed(o) +} + +// MustMarshalBinaryLengthPrefixed implements BinaryMarshaler.MustMarshalBinaryLengthPrefixed method. +func (ac *AminoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte { + return ac.LegacyAmino.MustMarshalBinaryLengthPrefixed(o) +} + +// UnmarshalBinaryBare implements BinaryMarshaler.UnmarshalBinaryBare method. +func (ac *AminoCodec) UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error { + return ac.LegacyAmino.UnmarshalBinaryBare(bz, ptr) +} + +// MustUnmarshalBinaryBare implements BinaryMarshaler.MustUnmarshalBinaryBare method. +func (ac *AminoCodec) MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) { + ac.LegacyAmino.MustUnmarshalBinaryBare(bz, ptr) +} + +// UnmarshalBinaryLengthPrefixed implements BinaryMarshaler.UnmarshalBinaryLengthPrefixed method. +func (ac *AminoCodec) UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error { + return ac.LegacyAmino.UnmarshalBinaryLengthPrefixed(bz, ptr) +} + +// MustUnmarshalBinaryLengthPrefixed implements BinaryMarshaler.MustUnmarshalBinaryLengthPrefixed method. +func (ac *AminoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) { + ac.LegacyAmino.MustUnmarshalBinaryLengthPrefixed(bz, ptr) +} + +// MarshalJSON implements JSONMarshaler.MarshalJSON method, +// it marshals to JSON using legacy amino codec. +func (ac *AminoCodec) MarshalJSON(o proto.Message) ([]byte, error) { + return ac.LegacyAmino.MarshalJSON(o) +} + +// MustMarshalJSON implements JSONMarshaler.MustMarshalJSON method, +// it executes MarshalJSON except it panics upon failure. +func (ac *AminoCodec) MustMarshalJSON(o proto.Message) []byte { + return ac.LegacyAmino.MustMarshalJSON(o) +} + +// UnmarshalJSON implements JSONMarshaler.UnmarshalJSON method, +// it unmarshals from JSON using legacy amino codec. +func (ac *AminoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error { + return ac.LegacyAmino.UnmarshalJSON(bz, ptr) +} + +// MustUnmarshalJSON implements JSONMarshaler.MustUnmarshalJSON method, +// it executes UnmarshalJSON except it panics upon failure. +func (ac *AminoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) { + ac.LegacyAmino.MustUnmarshalJSON(bz, ptr) +} + +// MarshalInterface is a convenience function for amino marshaling interfaces. +// The `i` must be an interface. +// NOTE: to marshal a concrete type, you should use MarshalBinaryBare instead +func (ac *AminoCodec) MarshalInterface(i proto.Message) ([]byte, error) { + if err := assertNotNil(i); err != nil { + return nil, err + } + return ac.LegacyAmino.MarshalBinaryBare(i) +} + +// UnmarshalInterface is a convenience function for amino unmarshaling interfaces. +// `ptr` must be a pointer to an interface. +// NOTE: to unmarshal a concrete type, you should use UnmarshalBinaryBare instead +// +// Example: +// var x MyInterface +// err := cdc.UnmarshalInterface(bz, &x) +func (ac *AminoCodec) UnmarshalInterface(bz []byte, ptr interface{}) error { + return ac.LegacyAmino.UnmarshalBinaryBare(bz, ptr) +} + +// MarshalInterfaceJSON is a convenience function for amino marshaling interfaces. +// The `i` must be an interface. +// NOTE: to marshal a concrete type, you should use MarshalJSON instead +func (ac *AminoCodec) MarshalInterfaceJSON(i proto.Message) ([]byte, error) { + if err := assertNotNil(i); err != nil { + return nil, err + } + return ac.LegacyAmino.MarshalJSON(i) +} + +// UnmarshalInterfaceJSON is a convenience function for amino unmarshaling interfaces. +// `ptr` must be a pointer to an interface. +// NOTE: to unmarshal a concrete type, you should use UnmarshalJSON instead +// +// Example: +// var x MyInterface +// err := cdc.UnmarshalInterfaceJSON(bz, &x) +func (ac *AminoCodec) UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error { + return ac.LegacyAmino.UnmarshalJSON(bz, ptr) +} diff --git a/codec/amino_codec_test.go b/codec/amino_codec_test.go new file mode 100644 index 000000000000..d95ddc8d97fa --- /dev/null +++ b/codec/amino_codec_test.go @@ -0,0 +1,142 @@ +package codec_test + +import ( + "bytes" + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" +) + +func createTestCodec() *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + cdc.RegisterInterface((*testdata.Animal)(nil), nil) + // NOTE: since we unmarshal interface using pointers, we need to register a pointer + // types here. + cdc.RegisterConcrete(&testdata.Dog{}, "testdata/Dog", nil) + cdc.RegisterConcrete(&testdata.Cat{}, "testdata/Cat", nil) + + return cdc +} + +func TestAminoMarsharlInterface(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + m := interfaceMarshaler{cdc.MarshalInterface, cdc.UnmarshalInterface} + testInterfaceMarshaling(require.New(t), m, true) + + m = interfaceMarshaler{cdc.MarshalInterfaceJSON, cdc.UnmarshalInterfaceJSON} + testInterfaceMarshaling(require.New(t), m, false) +} + +func TestAminoCodec(t *testing.T) { + testMarshaling(t, codec.NewAminoCodec(createTestCodec())) +} + +func TestAminoCodecMarshalJSONIndent(t *testing.T) { + any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"}) + require.NoError(t, err) + + testCases := []struct { + name string + input interface{} + marshalErr bool + wantJSON string + }{ + { + name: "valid encoding and decoding", + input: &testdata.Dog{Name: "rufus"}, + wantJSON: `{ + "type": "testdata/Dog", + "value": { + "name": "rufus" + } +}`, + }, + { + name: "any marshaling", + input: &testdata.HasAnimal{Animal: any}, + wantJSON: `{ + "animal": { + "type": "testdata/Dog", + "value": { + "name": "rufus" + } + } +}`, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + bz, err := cdc.MarshalJSONIndent(tc.input, "", " ") + + if tc.marshalErr { + require.Error(t, err) + require.Panics(t, func() { codec.MustMarshalJSONIndent(cdc.LegacyAmino, tc.input) }) + return + } + + // Otherwise these are expected to pass. + require.NoError(t, err) + require.Equal(t, bz, []byte(tc.wantJSON)) + + var bz2 []byte + require.NotPanics(t, func() { bz2 = codec.MustMarshalJSONIndent(cdc.LegacyAmino, tc.input) }) + require.Equal(t, bz2, []byte(tc.wantJSON)) + }) + } +} + +func TestAminoCodecPrintTypes(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + buf := new(bytes.Buffer) + require.NoError(t, cdc.PrintTypes(buf)) + lines := bytes.Split(buf.Bytes(), []byte("\n")) + require.True(t, len(lines) > 1) + wantHeader := "| Type | Name | Prefix | Length | Notes |" + require.Equal(t, lines[0], []byte(wantHeader)) + + // Expecting the types to be listed in the order that they were registered. + require.True(t, bytes.HasPrefix(lines[2], []byte("| Dog | testdata/Dog |"))) + require.True(t, bytes.HasPrefix(lines[3], []byte("| Cat | testdata/Cat |"))) +} + +func TestAminoCodecUnpackAnyFails(t *testing.T) { + cdc := codec.NewAminoCodec(createTestCodec()) + err := cdc.UnpackAny(new(types.Any), &testdata.Cat{}) + require.Error(t, err) + require.Equal(t, err, errors.New("AminoCodec can't handle unpack protobuf Any's")) +} + +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 tx legacytx.StdTx + err := legacyCdc.UnmarshalJSON([]byte(txSigned), &tx) + require.NoError(t, err) + + // Marshalling/unmarshalling the tx should work. + marshaledTx, err := legacyCdc.MarshalJSON(tx) + require.NoError(t, err) + require.Equal(t, string(marshaledTx), txSigned) + + // Marshalling/unmarshalling the tx wrapped in a struct should work. + txRequest := &rest.BroadcastReq{ + Mode: "block", + Tx: tx, + } + _, err = legacyCdc.MarshalJSON(txRequest) + require.NoError(t, err) +} diff --git a/codec/any_test.go b/codec/any_test.go new file mode 100644 index 000000000000..16e1b7b7788d --- /dev/null +++ b/codec/any_test.go @@ -0,0 +1,55 @@ +package codec_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func NewTestInterfaceRegistry() types.InterfaceRegistry { + registry := types.NewInterfaceRegistry() + registry.RegisterInterface("Animal", (*testdata.Animal)(nil)) + registry.RegisterImplementations( + (*testdata.Animal)(nil), + &testdata.Dog{}, + &testdata.Cat{}, + ) + return registry +} + +func TestMarshalAny(t *testing.T) { + registry := types.NewInterfaceRegistry() + + cdc := codec.NewProtoCodec(registry) + + kitty := &testdata.Cat{Moniker: "Kitty"} + bz, err := cdc.MarshalInterface(kitty) + require.NoError(t, err) + + var animal testdata.Animal + + // empty registry should fail + err = cdc.UnmarshalInterface(bz, &animal) + require.Error(t, err) + + // wrong type registration should fail + registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{}) + err = cdc.UnmarshalInterface(bz, &animal) + require.Error(t, err) + + // should pass + registry = NewTestInterfaceRegistry() + cdc = codec.NewProtoCodec(registry) + err = cdc.UnmarshalInterface(bz, &animal) + require.NoError(t, err) + require.Equal(t, kitty, animal) + + // nil should fail + registry = NewTestInterfaceRegistry() + err = cdc.UnmarshalInterface(bz, nil) + require.Error(t, err) +} diff --git a/codec/codec.go b/codec/codec.go index 6cf2f623d7e4..4746c58b6fae 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -1,65 +1,71 @@ package codec import ( - "bytes" - "encoding/json" - "fmt" + "github.com/gogo/protobuf/proto" - amino "github.com/tendermint/go-amino" - cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" - tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec/types" ) -// amino codec to marshal/unmarshal -type Codec = amino.Codec +type ( + // Marshaler defines the interface module codecs must implement in order to support + // backwards compatibility with Amino while allowing custom Protobuf-based + // serialization. Note, Amino can still be used without any dependency on + // Protobuf. There are two typical implementations that fulfill this contract: + // + // 1. AminoCodec: Provides full Amino serialization compatibility. + // 2. ProtoCodec: Provides full Protobuf serialization compatibility. + Marshaler interface { + BinaryMarshaler + JSONMarshaler + } -func New() *Codec { - return amino.NewCodec() -} + BinaryMarshaler interface { + MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) + MustMarshalBinaryBare(o ProtoMarshaler) []byte -// Register the go-crypto to the codec -func RegisterCrypto(cdc *Codec) { - cryptoamino.RegisterAmino(cdc) -} + MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, error) + MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte -// RegisterEvidences registers Tendermint evidence types with the provided codec. -func RegisterEvidences(cdc *Codec) { - tmtypes.RegisterEvidences(cdc) -} + UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error + MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) -// attempt to make some pretty json -func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) { - bz, err := cdc.MarshalJSON(obj) - if err != nil { - return nil, err - } + UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error + MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) - var out bytes.Buffer - err = json.Indent(&out, bz, "", " ") - if err != nil { - return nil, err - } - return out.Bytes(), nil -} + MarshalInterface(i proto.Message) ([]byte, error) + UnmarshalInterface(bz []byte, ptr interface{}) error -// MustMarshalJSONIndent executes MarshalJSONIndent except it panics upon failure. -func MustMarshalJSONIndent(cdc *Codec, obj interface{}) []byte { - bz, err := MarshalJSONIndent(cdc, obj) - if err != nil { - panic(fmt.Sprintf("failed to marshal JSON: %s", err)) + types.AnyUnpacker } - return bz -} + JSONMarshaler interface { + MarshalJSON(o proto.Message) ([]byte, error) + MustMarshalJSON(o proto.Message) []byte + MarshalInterfaceJSON(i proto.Message) ([]byte, error) + UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error + + UnmarshalJSON(bz []byte, ptr proto.Message) error + MustUnmarshalJSON(bz []byte, ptr proto.Message) + } -//__________________________________________________________________ + // ProtoMarshaler defines an interface a type must implement as protocol buffer + // defined message. + ProtoMarshaler interface { + proto.Message // for JSON serialization -// generic sealed codec to be used throughout sdk -var Cdc *Codec + Marshal() ([]byte, error) + MarshalTo(data []byte) (n int, err error) + MarshalToSizedBuffer(dAtA []byte) (int, error) + Size() int + Unmarshal(data []byte) error + } -func init() { - cdc := New() - RegisterCrypto(cdc) - RegisterEvidences(cdc) - Cdc = cdc.Seal() -} + // AminoMarshaler defines an interface where Amino marshalling can be + // overridden by custom marshalling. + AminoMarshaler interface { + MarshalAmino() ([]byte, error) + UnmarshalAmino([]byte) error + MarshalAminoJSON() ([]byte, error) + UnmarshalAminoJSON([]byte) error + } +) diff --git a/codec/codec_common_test.go b/codec/codec_common_test.go new file mode 100644 index 000000000000..12e9bd2224e1 --- /dev/null +++ b/codec/codec_common_test.go @@ -0,0 +1,135 @@ +package codec_test + +import ( + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +type interfaceMarshaler struct { + marshal func(i proto.Message) ([]byte, error) + unmarshal func(bz []byte, ptr interface{}) error +} + +func testInterfaceMarshaling(require *require.Assertions, cdc interfaceMarshaler, isAminoBin bool) { + _, err := cdc.marshal(nil) + require.Error(err, "can't marshal a nil value") + + dog := &testdata.Dog{Name: "rufus"} + var dogI testdata.Animal = dog + bz, err := cdc.marshal(dogI) + require.NoError(err) + + var animal testdata.Animal + if isAminoBin { + require.PanicsWithValue("Unmarshal expects a pointer", func() { + cdc.unmarshal(bz, animal) + }) + } else { + err = cdc.unmarshal(bz, animal) + require.Error(err) + require.Contains(err.Error(), "expects a pointer") + } + require.NoError(cdc.unmarshal(bz, &animal)) + require.Equal(dog, animal) + + // Amino doesn't wrap into Any, so it doesn't need to register self type + if isAminoBin { + var dog2 testdata.Dog + require.NoError(cdc.unmarshal(bz, &dog2)) + require.Equal(*dog, dog2) + } + + var cat testdata.Cat + require.Error(cdc.unmarshal(bz, &cat)) +} + +type mustMarshaler struct { + marshal func(i codec.ProtoMarshaler) ([]byte, error) + mustMarshal func(i codec.ProtoMarshaler) []byte + unmarshal func(bz []byte, ptr codec.ProtoMarshaler) error + mustUnmarshal func(bz []byte, ptr codec.ProtoMarshaler) +} + +type testCase struct { + name string + input codec.ProtoMarshaler + recv codec.ProtoMarshaler + marshalErr bool + unmarshalErr bool +} + +func testMarshalingTestCase(require *require.Assertions, tc testCase, m mustMarshaler) { + bz, err := m.marshal(tc.input) + if tc.marshalErr { + require.Error(err) + require.Panics(func() { m.mustMarshal(tc.input) }) + } else { + var bz2 []byte + require.NoError(err) + require.NotPanics(func() { bz2 = m.mustMarshal(tc.input) }) + require.Equal(bz, bz2) + + err := m.unmarshal(bz, tc.recv) + if tc.unmarshalErr { + require.Error(err) + require.Panics(func() { m.mustUnmarshal(bz, tc.recv) }) + } else { + require.NoError(err) + require.NotPanics(func() { m.mustUnmarshal(bz, tc.recv) }) + require.Equal(tc.input, tc.recv) + } + } +} + +func testMarshaling(t *testing.T, cdc codec.Marshaler) { + any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"}) + require.NoError(t, err) + + testCases := []testCase{ + { + "valid encoding and decoding", + &testdata.Dog{Name: "rufus"}, + &testdata.Dog{}, + false, + false, + }, { + "invalid decode type", + &testdata.Dog{Name: "rufus"}, + &testdata.Cat{}, + false, + true, + }} + if _, ok := cdc.(*codec.AminoCodec); ok { + testCases = append(testCases, testCase{ + "any marshaling", + &testdata.HasAnimal{Animal: any}, + &testdata.HasAnimal{Animal: any}, + false, + false, + }) + } + + for _, tc := range testCases { + tc := tc + m1 := mustMarshaler{cdc.MarshalBinaryBare, cdc.MustMarshalBinaryBare, cdc.UnmarshalBinaryBare, cdc.MustUnmarshalBinaryBare} + m2 := mustMarshaler{cdc.MarshalBinaryLengthPrefixed, cdc.MustMarshalBinaryLengthPrefixed, cdc.UnmarshalBinaryLengthPrefixed, cdc.MustUnmarshalBinaryLengthPrefixed} + m3 := mustMarshaler{ + func(i codec.ProtoMarshaler) ([]byte, error) { return cdc.MarshalJSON(i) }, + func(i codec.ProtoMarshaler) []byte { return cdc.MustMarshalJSON(i) }, + func(bz []byte, ptr codec.ProtoMarshaler) error { return cdc.UnmarshalJSON(bz, ptr) }, + func(bz []byte, ptr codec.ProtoMarshaler) { cdc.MustUnmarshalJSON(bz, ptr) }} + + t.Run(tc.name+"_BinaryBare", + func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m1) }) + t.Run(tc.name+"_BinaryLengthPrefixed", + func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m2) }) + t.Run(tc.name+"_JSON", + func(t *testing.T) { testMarshalingTestCase(require.New(t), tc, m3) }) + } +} diff --git a/codec/json.go b/codec/json.go new file mode 100644 index 000000000000..db77365e0356 --- /dev/null +++ b/codec/json.go @@ -0,0 +1,30 @@ +package codec + +import ( + "bytes" + + "github.com/cosmos/cosmos-sdk/codec/types" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" +) + +// ProtoMarshalJSON provides an auxiliary function to return Proto3 JSON encoded +// bytes of a message. +func ProtoMarshalJSON(msg proto.Message, resolver jsonpb.AnyResolver) ([]byte, error) { + // We use the OrigName because camel casing fields just doesn't make sense. + // EmitDefaults is also often the more expected behavior for CLI users + jm := &jsonpb.Marshaler{OrigName: true, EmitDefaults: true, AnyResolver: resolver} + err := types.UnpackInterfaces(msg, types.ProtoJSONPacker{JSONPBMarshaler: jm}) + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + + if err := jm.Marshal(buf, msg); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/codec/legacy/codec.go b/codec/legacy/codec.go new file mode 100644 index 000000000000..5ec6b2976cab --- /dev/null +++ b/codec/legacy/codec.go @@ -0,0 +1,31 @@ +package legacy + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// Cdc defines a global generic sealed Amino codec to be used throughout sdk. It +// has all Tendermint crypto and evidence types registered. +// +// TODO: Deprecated - remove this global. +var Cdc *codec.LegacyAmino + +func init() { + Cdc = codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(Cdc) + codec.RegisterEvidences(Cdc) +} + +// PrivKeyFromBytes unmarshals private key bytes and returns a PrivKey +func PrivKeyFromBytes(privKeyBytes []byte) (privKey cryptotypes.PrivKey, err error) { + err = Cdc.UnmarshalBinaryBare(privKeyBytes, &privKey) + return +} + +// PubKeyFromBytes unmarshals public key bytes and returns a PubKey +func PubKeyFromBytes(pubKeyBytes []byte) (pubKey cryptotypes.PubKey, err error) { + err = Cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) + return +} diff --git a/codec/legacy/doc.go b/codec/legacy/doc.go new file mode 100644 index 000000000000..d89944f3df42 --- /dev/null +++ b/codec/legacy/doc.go @@ -0,0 +1,4 @@ +// Package legacy contains a global amino Cdc which is deprecated but +// still used in several places within the SDK. This package is intended +// to be removed at some point in the future when the global Cdc is removed. +package legacy diff --git a/codec/proto_codec.go b/codec/proto_codec.go new file mode 100644 index 000000000000..38f60ba3ae30 --- /dev/null +++ b/codec/proto_codec.go @@ -0,0 +1,241 @@ +package codec + +import ( + "encoding/binary" + "errors" + "fmt" + "strings" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// ProtoCodecMarshaler defines an interface for codecs that utilize Protobuf for both +// binary and JSON encoding. +type ProtoCodecMarshaler interface { + Marshaler + InterfaceRegistry() types.InterfaceRegistry +} + +// ProtoCodec defines a codec that utilizes Protobuf for both binary and JSON +// encoding. +type ProtoCodec struct { + interfaceRegistry types.InterfaceRegistry +} + +var _ Marshaler = &ProtoCodec{} +var _ ProtoCodecMarshaler = &ProtoCodec{} + +// NewProtoCodec returns a reference to a new ProtoCodec +func NewProtoCodec(interfaceRegistry types.InterfaceRegistry) *ProtoCodec { + return &ProtoCodec{interfaceRegistry: interfaceRegistry} +} + +// MarshalBinaryBare implements BinaryMarshaler.MarshalBinaryBare method. +func (pc *ProtoCodec) MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) { + return o.Marshal() +} + +// MustMarshalBinaryBare implements BinaryMarshaler.MustMarshalBinaryBare method. +func (pc *ProtoCodec) MustMarshalBinaryBare(o ProtoMarshaler) []byte { + bz, err := pc.MarshalBinaryBare(o) + if err != nil { + panic(err) + } + + return bz +} + +// MarshalBinaryLengthPrefixed implements BinaryMarshaler.MarshalBinaryLengthPrefixed method. +func (pc *ProtoCodec) MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, error) { + bz, err := pc.MarshalBinaryBare(o) + if err != nil { + return nil, err + } + + var sizeBuf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(sizeBuf[:], uint64(o.Size())) + return append(sizeBuf[:n], bz...), nil +} + +// MustMarshalBinaryLengthPrefixed implements BinaryMarshaler.MustMarshalBinaryLengthPrefixed method. +func (pc *ProtoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte { + bz, err := pc.MarshalBinaryLengthPrefixed(o) + if err != nil { + panic(err) + } + + return bz +} + +// UnmarshalBinaryBare implements BinaryMarshaler.UnmarshalBinaryBare method. +func (pc *ProtoCodec) UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error { + err := ptr.Unmarshal(bz) + if err != nil { + return err + } + err = types.UnpackInterfaces(ptr, pc.interfaceRegistry) + if err != nil { + return err + } + return nil +} + +// MustUnmarshalBinaryBare implements BinaryMarshaler.MustUnmarshalBinaryBare method. +func (pc *ProtoCodec) MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) { + if err := pc.UnmarshalBinaryBare(bz, ptr); err != nil { + panic(err) + } +} + +// UnmarshalBinaryLengthPrefixed implements BinaryMarshaler.UnmarshalBinaryLengthPrefixed method. +func (pc *ProtoCodec) UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) error { + size, n := binary.Uvarint(bz) + if n < 0 { + return fmt.Errorf("invalid number of bytes read from length-prefixed encoding: %d", n) + } + + if size > uint64(len(bz)-n) { + return fmt.Errorf("not enough bytes to read; want: %v, got: %v", size, len(bz)-n) + } else if size < uint64(len(bz)-n) { + return fmt.Errorf("too many bytes to read; want: %v, got: %v", size, len(bz)-n) + } + + bz = bz[n:] + return pc.UnmarshalBinaryBare(bz, ptr) +} + +// MustUnmarshalBinaryLengthPrefixed implements BinaryMarshaler.MustUnmarshalBinaryLengthPrefixed method. +func (pc *ProtoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) { + if err := pc.UnmarshalBinaryLengthPrefixed(bz, ptr); err != nil { + panic(err) + } +} + +// MarshalJSON implements JSONMarshaler.MarshalJSON method, +// it marshals to JSON using proto codec. +func (pc *ProtoCodec) MarshalJSON(o proto.Message) ([]byte, error) { + m, ok := o.(ProtoMarshaler) + if !ok { + return nil, fmt.Errorf("cannot protobuf JSON encode unsupported type: %T", o) + } + + return ProtoMarshalJSON(m, pc.interfaceRegistry) +} + +// MustMarshalJSON implements JSONMarshaler.MustMarshalJSON method, +// it executes MarshalJSON except it panics upon failure. +func (pc *ProtoCodec) MustMarshalJSON(o proto.Message) []byte { + bz, err := pc.MarshalJSON(o) + if err != nil { + panic(err) + } + + return bz +} + +// UnmarshalJSON implements JSONMarshaler.UnmarshalJSON method, +// it unmarshals from JSON using proto codec. +func (pc *ProtoCodec) UnmarshalJSON(bz []byte, ptr proto.Message) error { + m, ok := ptr.(ProtoMarshaler) + if !ok { + return fmt.Errorf("cannot protobuf JSON decode unsupported type: %T", ptr) + } + + unmarshaler := jsonpb.Unmarshaler{AnyResolver: pc.interfaceRegistry} + err := unmarshaler.Unmarshal(strings.NewReader(string(bz)), m) + if err != nil { + return err + } + + return types.UnpackInterfaces(ptr, pc.interfaceRegistry) +} + +// MustUnmarshalJSON implements JSONMarshaler.MustUnmarshalJSON method, +// it executes UnmarshalJSON except it panics upon failure. +func (pc *ProtoCodec) MustUnmarshalJSON(bz []byte, ptr proto.Message) { + if err := pc.UnmarshalJSON(bz, ptr); err != nil { + panic(err) + } +} + +// MarshalInterface is a convenience function for proto marshalling interfaces. It packs +// the provided value, which must be an interface, in an Any and then marshals it to bytes. +// NOTE: to marshal a concrete type, you should use MarshalBinaryBare instead +func (pc *ProtoCodec) MarshalInterface(i proto.Message) ([]byte, error) { + if err := assertNotNil(i); err != nil { + return nil, err + } + any, err := types.NewAnyWithValue(i) + if err != nil { + return nil, err + } + + return pc.MarshalBinaryBare(any) +} + +// UnmarshalInterface is a convenience function for proto unmarshaling interfaces. It +// unmarshals an Any from bz bytes and then unpacks it to the `ptr`, which must +// be a pointer to a non empty interface with registered implementations. +// NOTE: to unmarshal a concrete type, you should use UnmarshalBinaryBare instead +// +// Example: +// var x MyInterface +// err := cdc.UnmarshalInterface(bz, &x) +func (pc *ProtoCodec) UnmarshalInterface(bz []byte, ptr interface{}) error { + any := &types.Any{} + err := pc.UnmarshalBinaryBare(bz, any) + if err != nil { + return err + } + + return pc.UnpackAny(any, ptr) +} + +// MarshalInterfaceJSON is a convenience function for proto marshalling interfaces. It +// packs the provided value in an Any and then marshals it to bytes. +// NOTE: to marshal a concrete type, you should use MarshalJSON instead +func (pc *ProtoCodec) MarshalInterfaceJSON(x proto.Message) ([]byte, error) { + any, err := types.NewAnyWithValue(x) + if err != nil { + return nil, err + } + return pc.MarshalJSON(any) +} + +// UnmarshalInterfaceJSON is a convenience function for proto unmarshaling interfaces. +// It unmarshals an Any from bz bytes and then unpacks it to the `iface`, which must +// be a pointer to a non empty interface, implementing proto.Message with registered implementations. +// NOTE: to unmarshal a concrete type, you should use UnmarshalJSON instead +// +// Example: +// var x MyInterface // must implement proto.Message +// err := cdc.UnmarshalInterfaceJSON(&x, bz) +func (pc *ProtoCodec) UnmarshalInterfaceJSON(bz []byte, iface interface{}) error { + any := &types.Any{} + err := pc.UnmarshalJSON(bz, any) + if err != nil { + return err + } + return pc.UnpackAny(any, iface) +} + +// UnpackAny implements AnyUnpacker.UnpackAny method, +// it unpacks the value in any to the interface pointer passed in as +// iface. +func (pc *ProtoCodec) UnpackAny(any *types.Any, iface interface{}) error { + return pc.interfaceRegistry.UnpackAny(any, iface) +} + +func (pc *ProtoCodec) InterfaceRegistry() types.InterfaceRegistry { + return pc.interfaceRegistry +} + +func assertNotNil(i interface{}) error { + if i == nil { + return errors.New("can't marshal value") + } + return nil +} diff --git a/codec/proto_codec_test.go b/codec/proto_codec_test.go new file mode 100644 index 000000000000..706d025d5994 --- /dev/null +++ b/codec/proto_codec_test.go @@ -0,0 +1,133 @@ +package codec_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func createTestInterfaceRegistry() types.InterfaceRegistry { + interfaceRegistry := types.NewInterfaceRegistry() + interfaceRegistry.RegisterInterface("testdata.Animal", + (*testdata.Animal)(nil), + &testdata.Dog{}, + &testdata.Cat{}, + ) + + return interfaceRegistry +} + +func TestProtoMarsharlInterface(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + m := interfaceMarshaler{cdc.MarshalInterface, cdc.UnmarshalInterface} + testInterfaceMarshaling(require.New(t), m, false) + m = interfaceMarshaler{cdc.MarshalInterfaceJSON, cdc.UnmarshalInterfaceJSON} + testInterfaceMarshaling(require.New(t), m, false) +} + +func TestProtoCodec(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + testMarshaling(t, cdc) +} + +type lyingProtoMarshaler struct { + codec.ProtoMarshaler + falseSize int +} + +func (lpm *lyingProtoMarshaler) Size() int { + return lpm.falseSize +} + +func TestProtoCodecUnmarshalBinaryLengthPrefixedChecks(t *testing.T) { + cdc := codec.NewProtoCodec(createTestInterfaceRegistry()) + + truth := &testdata.Cat{Lives: 9, Moniker: "glowing"} + realSize := len(cdc.MustMarshalBinaryBare(truth)) + + falseSizes := []int{ + 100, + 5, + } + + for _, falseSize := range falseSizes { + falseSize := falseSize + + t.Run(fmt.Sprintf("ByMarshaling falseSize=%d", falseSize), func(t *testing.T) { + lpm := &lyingProtoMarshaler{ + ProtoMarshaler: &testdata.Cat{Lives: 9, Moniker: "glowing"}, + falseSize: falseSize, + } + var serialized []byte + require.NotPanics(t, func() { serialized = cdc.MustMarshalBinaryLengthPrefixed(lpm) }) + + recv := new(testdata.Cat) + gotErr := cdc.UnmarshalBinaryLengthPrefixed(serialized, recv) + var wantErr error + if falseSize > realSize { + wantErr = fmt.Errorf("not enough bytes to read; want: %d, got: %d", falseSize, realSize) + } else { + wantErr = fmt.Errorf("too many bytes to read; want: %d, got: %d", falseSize, realSize) + } + require.Equal(t, gotErr, wantErr) + }) + } + + t.Run("Crafted bad uvarint size", func(t *testing.T) { + crafted := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f} + recv := new(testdata.Cat) + gotErr := cdc.UnmarshalBinaryLengthPrefixed(crafted, recv) + require.Equal(t, gotErr, errors.New("invalid number of bytes read from length-prefixed encoding: -10")) + + require.Panics(t, func() { cdc.MustUnmarshalBinaryLengthPrefixed(crafted, recv) }) + }) +} + +func mustAny(msg proto.Message) *types.Any { + any, err := types.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + return any +} + +func BenchmarkProtoCodecMarshalBinaryLengthPrefixed(b *testing.B) { + var pCdc = codec.NewProtoCodec(types.NewInterfaceRegistry()) + var msg = &testdata.HasAnimal{ + X: 1000, + Animal: mustAny(&testdata.HasAnimal{ + X: 2000, + Animal: mustAny(&testdata.HasAnimal{ + X: 3000, + Animal: mustAny(&testdata.HasAnimal{ + X: 4000, + Animal: mustAny(&testdata.HasAnimal{ + X: 5000, + Animal: mustAny(&testdata.Cat{ + Moniker: "Garfield", + Lives: 6, + }), + }), + }), + }), + }), + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + blob, err := pCdc.MarshalBinaryLengthPrefixed(msg) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(blob))) + } +} diff --git a/codec/types/any.go b/codec/types/any.go new file mode 100644 index 000000000000..faa8693d731a --- /dev/null +++ b/codec/types/any.go @@ -0,0 +1,123 @@ +package types + +import ( + "github.com/gogo/protobuf/proto" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type Any struct { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + + // nolint + TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // Must be a valid serialized protocol buffer of the above specified type. + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + + // nolint + XXX_NoUnkeyedLiteral struct{} `json:"-"` + + // nolint + XXX_unrecognized []byte `json:"-"` + + // nolint + XXX_sizecache int32 `json:"-"` + + cachedValue interface{} + + compat *anyCompat +} + +// NewAnyWithValue constructs a new Any packed with the value provided or +// returns an error if that value couldn't be packed. This also caches +// the packed value so that it can be retrieved from GetCachedValue without +// unmarshaling +func NewAnyWithValue(v proto.Message) (*Any, error) { + if v == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, "Expecting non nil value to create a new Any") + } + return NewAnyWithCustomTypeURL(v, "/"+proto.MessageName(v)) +} + +// NewAnyWithCustomTypeURL same as NewAnyWithValue, but sets a custom type url, instead +// using the one from proto.Message. +// NOTE: This functions should be only used for types with additional logic bundled +// 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, + }, nil +} + +// UnsafePackAny packs the value x in the Any and instead of returning the error +// in the case of a packing failure, keeps the cached value. This should only +// be used in situations where compatibility is needed with amino. Amino-only +// values can safely be packed using this method when they will only be +// marshaled with amino and not protobuf. +func UnsafePackAny(x interface{}) *Any { + if msg, ok := x.(proto.Message); ok { + any, err := NewAnyWithValue(msg) + if err == nil { + return any + } + } + return &Any{cachedValue: x} +} + +// pack packs the value x in the Any or returns an error. This also caches +// the packed value so that it can be retrieved from GetCachedValue without +// unmarshaling +func (any *Any) pack(x proto.Message) error { + any.TypeUrl = "/" + proto.MessageName(x) + bz, err := proto.Marshal(x) + if err != nil { + return err + } + + any.Value = bz + any.cachedValue = x + + return nil +} + +// GetCachedValue returns the cached value from the Any if present +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.pb.go b/codec/types/any.pb.go new file mode 100644 index 000000000000..97d9f1c2aa64 --- /dev/null +++ b/codec/types/any.pb.go @@ -0,0 +1,581 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: google/protobuf/any.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// 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 + +func (m *Any) Reset() { *m = Any{} } +func (*Any) ProtoMessage() {} +func (*Any) Descriptor() ([]byte, []int) { + return fileDescriptor_b53526c13ae22eb4, []int{0} +} +func (*Any) XXX_WellKnownType() string { return "Any" } +func (m *Any) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Any.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 *Any) XXX_Merge(src proto.Message) { + xxx_messageInfo_Any.Merge(m, src) +} +func (m *Any) XXX_Size() int { + return m.Size() +} +func (m *Any) XXX_DiscardUnknown() { + xxx_messageInfo_Any.DiscardUnknown(m) +} + +var xxx_messageInfo_Any proto.InternalMessageInfo + +func (m *Any) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Any) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (*Any) XXX_MessageName() string { + return "google.protobuf.Any" +} +func init() { +} + +func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) } + +var fileDescriptor_b53526c13ae22eb4 = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4, + 0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x30, + 0x4f, 0x1f, 0xc4, 0x82, 0x48, 0x28, 0xd9, 0x70, 0x31, 0x3b, 0xe6, 0x55, 0x0a, 0x49, 0x72, 0x71, + 0x94, 0x54, 0x16, 0xa4, 0xc6, 0x97, 0x16, 0xe5, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xb1, + 0x83, 0xf8, 0xa1, 0x45, 0x39, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x89, 0x39, 0xa5, 0xa9, 0x12, 0x4c, + 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x10, 0x8e, 0x15, 0xcb, 0x87, 0x85, 0xf2, 0x0c, 0x4e, 0xcd, 0x8c, + 0x37, 0x1e, 0xca, 0x31, 0x7c, 0x78, 0x28, 0xc7, 0xf8, 0xe3, 0xa1, 0x1c, 0x63, 0xc3, 0x23, 0x39, + 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, + 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x80, 0xc4, 0x1f, 0xcb, 0x31, 0x1e, 0x78, 0x2c, 0xc7, + 0x70, 0xe2, 0xb1, 0x1c, 0x23, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xfb, 0x9c, 0x38, 0x1c, + 0xf3, 0x2a, 0x03, 0x40, 0x9c, 0x00, 0xc6, 0x28, 0x56, 0x90, 0xe5, 0xc5, 0x8b, 0x98, 0x98, 0xdd, + 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x94, 0x06, 0x40, 0x95, 0xea, 0x85, 0xa7, 0xe6, 0xe4, + 0x78, 0xe7, 0xe5, 0x97, 0xe7, 0x85, 0x80, 0x94, 0x25, 0xb1, 0x81, 0xcd, 0x30, 0x06, 0x04, 0x00, + 0x00, 0xff, 0xff, 0xe6, 0xfb, 0xa0, 0x21, 0x0e, 0x01, 0x00, 0x00, +} + +func (this *Any) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Any) + if !ok { + that2, ok := that.(Any) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.TypeUrl != that1.TypeUrl { + if this.TypeUrl < that1.TypeUrl { + return -1 + } + return 1 + } + if c := bytes.Compare(this.Value, that1.Value); c != 0 { + return c + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Any) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Any) + if !ok { + that2, ok := that.(Any) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.TypeUrl != that1.TypeUrl { + return false + } + if !bytes.Equal(this.Value, that1.Value) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Any) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&types.Any{") + s = append(s, "TypeUrl: "+fmt.Sprintf("%#v", this.TypeUrl)+",\n") + s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringAny(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *Any) 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 *Any) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Any) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintAny(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = encodeVarintAny(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAny(dAtA []byte, offset int, v uint64) int { + offset -= sovAny(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func NewPopulatedAny(r randyAny, easy bool) *Any { + this := &Any{} + this.TypeUrl = string(randStringAny(r)) + v1 := r.Intn(100) + this.Value = make([]byte, v1) + for i := 0; i < v1; i++ { + this.Value[i] = byte(r.Intn(256)) + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedAny(r, 3) + } + return this +} + +type randyAny interface { + Float32() float32 + Float64() float64 + Int63() int64 + Int31() int32 + Uint32() uint32 + Intn(n int) int +} + +func randUTF8RuneAny(r randyAny) rune { + ru := r.Intn(62) + if ru < 10 { + return rune(ru + 48) + } else if ru < 36 { + return rune(ru + 55) + } + return rune(ru + 61) +} +func randStringAny(r randyAny) string { + v2 := r.Intn(100) + tmps := make([]rune, v2) + for i := 0; i < v2; i++ { + tmps[i] = randUTF8RuneAny(r) + } + return string(tmps) +} +func randUnrecognizedAny(r randyAny, maxFieldNumber int) (dAtA []byte) { + l := r.Intn(5) + for i := 0; i < l; i++ { + wire := r.Intn(4) + if wire == 3 { + wire = 5 + } + fieldNumber := maxFieldNumber + r.Intn(100) + dAtA = randFieldAny(dAtA, r, fieldNumber, wire) + } + return dAtA +} +func randFieldAny(dAtA []byte, r randyAny, fieldNumber int, wire int) []byte { + key := uint32(fieldNumber)<<3 | uint32(wire) + switch wire { + case 0: + dAtA = encodeVarintPopulateAny(dAtA, uint64(key)) + v3 := r.Int63() + if r.Intn(2) == 0 { + v3 *= -1 + } + dAtA = encodeVarintPopulateAny(dAtA, uint64(v3)) + case 1: + dAtA = encodeVarintPopulateAny(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + case 2: + dAtA = encodeVarintPopulateAny(dAtA, uint64(key)) + ll := r.Intn(100) + dAtA = encodeVarintPopulateAny(dAtA, uint64(ll)) + for j := 0; j < ll; j++ { + dAtA = append(dAtA, byte(r.Intn(256))) + } + default: + dAtA = encodeVarintPopulateAny(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + } + return dAtA +} +func encodeVarintPopulateAny(dAtA []byte, v uint64) []byte { + for v >= 1<<7 { + dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80)) + v >>= 7 + } + dAtA = append(dAtA, uint8(v)) + return dAtA +} +func (m *Any) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + sovAny(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovAny(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovAny(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAny(x uint64) (n int) { + return sovAny(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Any) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Any{`, + `TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringAny(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Any) 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 ErrIntOverflowAny + } + 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: Any: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Any: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAny + } + 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 ErrInvalidLengthAny + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAny + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAny + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAny + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAny + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAny(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAny + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAny + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAny(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, ErrIntOverflowAny + } + 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, ErrIntOverflowAny + } + 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, ErrIntOverflowAny + } + 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, ErrInvalidLengthAny + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAny + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAny + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAny = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAny = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAny = fmt.Errorf("proto: unexpected end of group") +) 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/compat.go b/codec/types/compat.go new file mode 100644 index 000000000000..5cd372dc73bf --- /dev/null +++ b/codec/types/compat.go @@ -0,0 +1,211 @@ +package types + +import ( + "fmt" + "reflect" + "runtime/debug" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" + + amino "github.com/tendermint/go-amino" +) + +type anyCompat struct { + aminoBz []byte + jsonBz []byte + err error +} + +var Debug = true + +func anyCompatError(errType string, x interface{}) error { + if Debug { + debug.PrintStack() + } + return fmt.Errorf( + "%s marshaling error for %+v, this is likely because "+ + "amino is being used directly (instead of codec.LegacyAmino which is preferred) "+ + "or UnpackInterfacesMessage is not defined for some type which contains "+ + "a protobuf Any either directly or via one of its members. To see a "+ + "stacktrace of where the error is coming from, set the var Debug = true "+ + "in codec/types/compat.go", + errType, x, + ) +} + +func (any Any) MarshalAmino() ([]byte, error) { + ac := any.compat + if ac == nil { + return nil, anyCompatError("amino binary unmarshal", any) + } + return ac.aminoBz, ac.err +} + +func (any *Any) UnmarshalAmino(bz []byte) error { + any.compat = &anyCompat{ + aminoBz: bz, + err: nil, + } + return nil +} + +func (any *Any) MarshalJSON() ([]byte, error) { + ac := any.compat + if ac == nil { + return nil, anyCompatError("JSON marshal", any) + } + return ac.jsonBz, ac.err +} + +func (any *Any) UnmarshalJSON(bz []byte) error { + any.compat = &anyCompat{ + jsonBz: bz, + err: nil, + } + return nil +} + +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the binary un-marshaling phase +type AminoUnpacker struct { + Cdc *amino.Codec +} + +var _ AnyUnpacker = AminoUnpacker{} + +func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error { + ac := any.compat + if ac == nil { + return anyCompatError("amino binary unmarshal", reflect.TypeOf(iface)) + } + err := a.Cdc.UnmarshalBinaryBare(ac.aminoBz, iface) + if err != nil { + return err + } + val := reflect.ValueOf(iface).Elem().Interface() + err = UnpackInterfaces(val, a) + if err != nil { + return err + } + if m, ok := val.(proto.Message); ok { + if err = any.pack(m); err != nil { + return err + } + } else { + any.cachedValue = val + } + + // this is necessary for tests that use reflect.DeepEqual and compare + // proto vs amino marshaled values + any.compat = nil + + return nil +} + +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the binary marshaling phase +type AminoPacker struct { + Cdc *amino.Codec +} + +var _ AnyUnpacker = AminoPacker{} + +func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error { + err := UnpackInterfaces(any.cachedValue, a) + if err != nil { + return err + } + bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue) + any.compat = &anyCompat{ + aminoBz: bz, + err: err, + } + return err +} + +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the JSON marshaling phase +type AminoJSONUnpacker struct { + Cdc *amino.Codec +} + +var _ AnyUnpacker = AminoJSONUnpacker{} + +func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error { + ac := any.compat + if ac == nil { + return anyCompatError("JSON unmarshal", reflect.TypeOf(iface)) + } + err := a.Cdc.UnmarshalJSON(ac.jsonBz, iface) + if err != nil { + return err + } + val := reflect.ValueOf(iface).Elem().Interface() + err = UnpackInterfaces(val, a) + if err != nil { + return err + } + if m, ok := val.(proto.Message); ok { + if err = any.pack(m); err != nil { + return err + } + } else { + any.cachedValue = val + } + + // this is necessary for tests that use reflect.DeepEqual and compare + // proto vs amino marshaled values + any.compat = nil + + return nil +} + +// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with +// amino for the JSON un-marshaling phase +type AminoJSONPacker struct { + Cdc *amino.Codec +} + +var _ AnyUnpacker = AminoJSONPacker{} + +func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error { + err := UnpackInterfaces(any.cachedValue, a) + if err != nil { + return err + } + bz, err := a.Cdc.MarshalJSON(any.cachedValue) + any.compat = &anyCompat{ + jsonBz: bz, + err: err, + } + return err +} + +// ProtoJSONPacker is an AnyUnpacker provided for compatibility with jsonpb +type ProtoJSONPacker struct { + JSONPBMarshaler *jsonpb.Marshaler +} + +var _ AnyUnpacker = ProtoJSONPacker{} + +func (a ProtoJSONPacker) UnpackAny(any *Any, _ interface{}) error { + if any == nil { + return nil + } + + if any.cachedValue != nil { + err := UnpackInterfaces(any.cachedValue, a) + if err != nil { + return err + } + } + + bz, err := a.JSONPBMarshaler.MarshalToString(any) + any.compat = &anyCompat{ + jsonBz: []byte(bz), + err: err, + } + + return err +} diff --git a/codec/types/compat_test.go b/codec/types/compat_test.go new file mode 100644 index 000000000000..56cb32e82cc0 --- /dev/null +++ b/codec/types/compat_test.go @@ -0,0 +1,132 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +type TypeWithInterface struct { + Animal testdata.Animal `json:"animal"` + X int64 `json:"x,omitempty"` +} + +type Suite struct { + suite.Suite + cdc *amino.Codec + a TypeWithInterface + b testdata.HasAnimal + spot *testdata.Dog +} + +func (s *Suite) SetupTest() { + s.cdc = amino.NewCodec() + s.cdc.RegisterInterface((*testdata.Animal)(nil), nil) + s.cdc.RegisterConcrete(&testdata.Dog{}, "testdata/Dog", nil) + + s.spot = &testdata.Dog{Size_: "small", Name: "Spot"} + s.a = TypeWithInterface{Animal: s.spot} + + any, err := types.NewAnyWithValue(s.spot) + s.Require().NoError(err) + s.b = testdata.HasAnimal{Animal: any} +} + +func (s *Suite) TestAminoBinary() { + bz, err := s.cdc.MarshalBinaryBare(s.a) + s.Require().NoError(err) + + // expect plain amino marshal to fail + _, err = s.cdc.MarshalBinaryBare(s.b) + s.Require().Error(err) + + // expect unpack interfaces before amino marshal to succeed + err = types.UnpackInterfaces(s.b, types.AminoPacker{Cdc: s.cdc}) + s.Require().NoError(err) + bz2, err := s.cdc.MarshalBinaryBare(s.b) + s.Require().NoError(err) + s.Require().Equal(bz, bz2) + + var c testdata.HasAnimal + err = s.cdc.UnmarshalBinaryBare(bz, &c) + s.Require().NoError(err) + err = types.UnpackInterfaces(c, types.AminoUnpacker{Cdc: s.cdc}) + s.Require().NoError(err) + s.Require().Equal(s.spot, c.Animal.GetCachedValue()) +} + +func (s *Suite) TestAminoJSON() { + bz, err := s.cdc.MarshalJSON(s.a) + s.Require().NoError(err) + + // expect plain amino marshal to fail + _, err = s.cdc.MarshalJSON(s.b) + s.Require().Error(err) + + // expect unpack interfaces before amino marshal to succeed + err = types.UnpackInterfaces(s.b, types.AminoJSONPacker{Cdc: s.cdc}) + s.Require().NoError(err) + bz2, err := s.cdc.MarshalJSON(s.b) + s.Require().NoError(err) + s.Require().Equal(string(bz), string(bz2)) + + var c testdata.HasAnimal + err = s.cdc.UnmarshalJSON(bz, &c) + s.Require().NoError(err) + err = types.UnpackInterfaces(c, types.AminoJSONUnpacker{Cdc: s.cdc}) + s.Require().NoError(err) + s.Require().Equal(s.spot, c.Animal.GetCachedValue()) +} + +func (s *Suite) TestNested() { + s.cdc.RegisterInterface((*testdata.HasAnimalI)(nil), nil) + s.cdc.RegisterInterface((*testdata.HasHasAnimalI)(nil), nil) + s.cdc.RegisterConcrete(&testdata.HasAnimal{}, "testdata/HasAnimal", nil) + s.cdc.RegisterConcrete(&testdata.HasHasAnimal{}, "testdata/HasHasAnimal", nil) + s.cdc.RegisterConcrete(&testdata.HasHasHasAnimal{}, "testdata/HasHasHasAnimal", nil) + + any, err := types.NewAnyWithValue(&s.b) + s.Require().NoError(err) + hha := testdata.HasHasAnimal{HasAnimal: any} + any2, err := types.NewAnyWithValue(&hha) + s.Require().NoError(err) + hhha := testdata.HasHasHasAnimal{HasHasAnimal: any2} + + // marshal + err = types.UnpackInterfaces(hhha, types.AminoPacker{Cdc: s.cdc}) + s.Require().NoError(err) + bz, err := s.cdc.MarshalBinaryBare(hhha) + s.Require().NoError(err) + + // unmarshal + var hhha2 testdata.HasHasHasAnimal + err = s.cdc.UnmarshalBinaryBare(bz, &hhha2) + s.Require().NoError(err) + err = types.UnpackInterfaces(hhha2, types.AminoUnpacker{Cdc: s.cdc}) + s.Require().NoError(err) + + s.Require().Equal(s.spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal()) + + // json marshal + err = types.UnpackInterfaces(hhha, types.AminoJSONPacker{Cdc: s.cdc}) + s.Require().NoError(err) + jsonBz, err := s.cdc.MarshalJSON(hhha) + s.Require().NoError(err) + + // json unmarshal + var hhha3 testdata.HasHasHasAnimal + err = s.cdc.UnmarshalJSON(jsonBz, &hhha3) + s.Require().NoError(err) + err = types.UnpackInterfaces(hhha3, types.AminoJSONUnpacker{Cdc: s.cdc}) + s.Require().NoError(err) + + s.Require().Equal(s.spot, hhha3.TheHasHasAnimal().TheHasAnimal().TheAnimal()) +} + +func TestSuite(t *testing.T) { + suite.Run(t, &Suite{}) +} diff --git a/codec/types/doc.go b/codec/types/doc.go new file mode 100644 index 000000000000..9f89f0c91284 --- /dev/null +++ b/codec/types/doc.go @@ -0,0 +1,6 @@ +/* +Package types defines a custom wrapper for google.protobuf.Any which supports +cached values as well as InterfaceRegistry which keeps track of types which can +be used with Any for both security and introspection +*/ +package types diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go new file mode 100644 index 000000000000..0f9eb760beca --- /dev/null +++ b/codec/types/interface_registry.go @@ -0,0 +1,289 @@ +package types + +import ( + "fmt" + "reflect" + + "github.com/gogo/protobuf/jsonpb" + + "github.com/gogo/protobuf/proto" +) + +// AnyUnpacker is an interface which allows safely unpacking types packed +// in Any's against a whitelist of registered types +type AnyUnpacker interface { + // UnpackAny unpacks the value in any to the interface pointer passed in as + // iface. Note that the type in any must have been registered in the + // underlying whitelist registry as a concrete type for that interface + // Ex: + // var msg sdk.Msg + // err := cdc.UnpackAny(any, &msg) + // ... + UnpackAny(any *Any, iface interface{}) error +} + +// InterfaceRegistry provides a mechanism for registering interfaces and +// implementations that can be safely unpacked from Any +type InterfaceRegistry interface { + AnyUnpacker + jsonpb.AnyResolver + + // RegisterInterface associates protoName as the public name for the + // interface passed in as iface. This is to be used primarily to create + // a public facing registry of interface implementations for clients. + // protoName should be a well-chosen public facing name that remains stable. + // RegisterInterface takes an optional list of impls to be registered + // as implementations of iface. + // + // Ex: + // registry.RegisterInterface("cosmos.base.v1beta1.Msg", (*sdk.Msg)(nil)) + RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) + + // RegisterImplementations registers impls as concrete implementations of + // the interface iface. + // + // Ex: + // registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{}) + RegisterImplementations(iface interface{}, impls ...proto.Message) + + // RegisterCustomTypeURL allows a protobuf message to be registered as a + // google.protobuf.Any with a custom typeURL (besides its own canonical + // typeURL). iface should be an interface as type, as in RegisterInterface + // and RegisterImplementations. + // + // Ex: + // This will allow us to pack service methods in Any's using the full method name + // as the type URL and the request body as the value, and allow us to unpack + // such packed methods using the normal UnpackAny method for the interface iface. + RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) + + // ListAllInterfaces list the type URLs of all registered interfaces. + ListAllInterfaces() []string + + // ListImplementations lists the valid type URLs for the given interface name that can be used + // for the provided interface type URL. + ListImplementations(ifaceTypeURL string) []string +} + +// UnpackInterfacesMessage is meant to extend protobuf types (which implement +// proto.Message) to support a post-deserialization phase which unpacks +// types packed within Any's using the whitelist provided by AnyUnpacker +type UnpackInterfacesMessage interface { + // UnpackInterfaces is implemented in order to unpack values packed within + // Any's using the AnyUnpacker. It should generally be implemented as + // follows: + // func (s *MyStruct) UnpackInterfaces(unpacker AnyUnpacker) error { + // var x AnyInterface + // // where X is an Any field on MyStruct + // err := unpacker.UnpackAny(s.X, &x) + // if err != nil { + // return nil + // } + // // where Y is a field on MyStruct that implements UnpackInterfacesMessage itself + // err = s.Y.UnpackInterfaces(unpacker) + // if err != nil { + // return nil + // } + // return nil + // } + UnpackInterfaces(unpacker AnyUnpacker) error +} + +type interfaceRegistry struct { + interfaceNames map[string]reflect.Type + interfaceImpls map[reflect.Type]interfaceMap + typeURLMap map[string]reflect.Type +} + +type interfaceMap = map[string]reflect.Type + +// NewInterfaceRegistry returns a new InterfaceRegistry +func NewInterfaceRegistry() InterfaceRegistry { + return &interfaceRegistry{ + interfaceNames: map[string]reflect.Type{}, + interfaceImpls: map[reflect.Type]interfaceMap{}, + typeURLMap: map[string]reflect.Type{}, + } +} + +func (registry *interfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) { + typ := reflect.TypeOf(iface) + if typ.Elem().Kind() != reflect.Interface { + panic(fmt.Errorf("%T is not an interface type", iface)) + } + registry.interfaceNames[protoName] = typ + registry.RegisterImplementations(iface, impls...) +} + +// RegisterImplementations registers a concrete proto Message which implements +// the given interface. +// +// This function PANICs if different concrete types are registered under the +// same typeURL. +func (registry *interfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) { + for _, impl := range impls { + typeURL := "/" + proto.MessageName(impl) + registry.registerImpl(iface, typeURL, impl) + } +} + +// RegisterCustomTypeURL registers a concrete type which implements the given +// interface under `typeURL`. +// +// This function PANICs if different concrete types are registered under the +// same typeURL. +func (registry *interfaceRegistry) RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) { + registry.registerImpl(iface, typeURL, impl) +} + +// registerImpl registers a concrete type which implements the given +// interface under `typeURL`. +// +// This function PANICs if different concrete types are registered under the +// same typeURL. +func (registry *interfaceRegistry) registerImpl(iface interface{}, typeURL string, impl proto.Message) { + ityp := reflect.TypeOf(iface).Elem() + imap, found := registry.interfaceImpls[ityp] + if !found { + imap = map[string]reflect.Type{} + } + + implType := reflect.TypeOf(impl) + if !implType.AssignableTo(ityp) { + panic(fmt.Errorf("type %T doesn't actually implement interface %+v", impl, ityp)) + } + + // Check if we already registered something under the given typeURL. It's + // okay to register the same concrete type again, but if we are registering + // a new concrete type under the same typeURL, then we throw an error (here, + // we panic). + foundImplType, found := imap[typeURL] + if found && foundImplType != implType { + panic( + fmt.Errorf( + "concrete type %s has already been registered under typeURL %s, cannot register %s under same typeURL. "+ + "This usually means that there are conflicting modules registering different concrete types "+ + "for a same interface implementation", + foundImplType, + typeURL, + implType, + ), + ) + } + + imap[typeURL] = implType + registry.typeURLMap[typeURL] = implType + + registry.interfaceImpls[ityp] = imap +} + +func (registry *interfaceRegistry) ListAllInterfaces() []string { + interfaceNames := registry.interfaceNames + keys := make([]string, 0, len(interfaceNames)) + for key := range interfaceNames { + keys = append(keys, key) + } + return keys +} + +func (registry *interfaceRegistry) ListImplementations(ifaceName string) []string { + typ, ok := registry.interfaceNames[ifaceName] + if !ok { + return []string{} + } + + impls, ok := registry.interfaceImpls[typ.Elem()] + if !ok { + return []string{} + } + + keys := make([]string, 0, len(impls)) + for key := range impls { + keys = append(keys, key) + } + return keys +} + +func (registry *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error { + // here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding + if any == nil { + return nil + } + + if any.TypeUrl == "" { + // if TypeUrl is empty return nil because without it we can't actually unpack anything + return nil + } + + rv := reflect.ValueOf(iface) + if rv.Kind() != reflect.Ptr { + return fmt.Errorf("UnpackAny expects a pointer") + } + + rt := rv.Elem().Type() + + cachedValue := any.cachedValue + if cachedValue != nil { + if reflect.TypeOf(cachedValue).AssignableTo(rt) { + rv.Elem().Set(reflect.ValueOf(cachedValue)) + return nil + } + } + + imap, found := registry.interfaceImpls[rt] + if !found { + return fmt.Errorf("no registered implementations of type %+v", rt) + } + + typ, found := imap[any.TypeUrl] + if !found { + return fmt.Errorf("no concrete type registered for type URL %s against interface %T", any.TypeUrl, iface) + } + + msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message) + if !ok { + return fmt.Errorf("can't proto unmarshal %T", msg) + } + + err := proto.Unmarshal(any.Value, msg) + if err != nil { + return err + } + + err = UnpackInterfaces(msg, registry) + if err != nil { + return err + } + + rv.Elem().Set(reflect.ValueOf(msg)) + + any.cachedValue = msg + + return nil +} + +// Resolve returns the proto message given its typeURL. It works with types +// registered with RegisterInterface/RegisterImplementations, as well as those +// registered with RegisterWithCustomTypeURL. +func (registry *interfaceRegistry) Resolve(typeURL string) (proto.Message, error) { + typ, found := registry.typeURLMap[typeURL] + if !found { + return nil, fmt.Errorf("unable to resolve type URL %s", typeURL) + } + + msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message) + if !ok { + return nil, fmt.Errorf("can't resolve type URL %s", typeURL) + } + + return msg, nil +} + +// UnpackInterfaces is a convenience function that calls UnpackInterfaces +// on x if x implements UnpackInterfacesMessage +func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error { + if msg, ok := x.(UnpackInterfacesMessage); ok { + return msg.UnpackInterfaces(unpacker) + } + return nil +} diff --git a/codec/types/types_test.go b/codec/types/types_test.go new file mode 100644 index 000000000000..b2d014269502 --- /dev/null +++ b/codec/types/types_test.go @@ -0,0 +1,251 @@ +package types_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "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" + + "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) { + registry := testdata.NewTestInterfaceRegistry() + + spot := &testdata.Dog{Name: "Spot"} + var animal testdata.Animal + + // with cache + any, err := types.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.ClearCachedValue() + err = registry.UnpackAny(any, &animal) + require.NoError(t, err) + require.Equal(t, spot, animal) +} + +type TestI interface { + DoSomething() +} + +// A struct that has the same typeURL as testdata.Dog, but is actually another +// concrete type. +type FakeDog struct{} + +var ( + _ proto.Message = &FakeDog{} + _ testdata.Animal = &FakeDog{} +) + +// dummy implementation of proto.Message and testdata.Animal +func (dog FakeDog) Reset() {} +func (dog FakeDog) String() string { return "fakedog" } +func (dog FakeDog) ProtoMessage() {} +func (dog FakeDog) XXX_MessageName() string { return proto.MessageName(&testdata.Dog{}) } +func (dog FakeDog) Greet() string { return "fakedog" } + +func TestRegister(t *testing.T) { + registry := types.NewInterfaceRegistry() + registry.RegisterInterface("Animal", (*testdata.Animal)(nil)) + registry.RegisterInterface("TestI", (*TestI)(nil)) + + // Happy path. + require.NotPanics(t, func() { + registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{}) + }) + + // testdata.Dog doesn't implement TestI + require.Panics(t, func() { + registry.RegisterImplementations((*TestI)(nil), &testdata.Dog{}) + }) + + // nil proto message + require.Panics(t, func() { + registry.RegisterImplementations((*TestI)(nil), nil) + }) + + // Not an interface. + require.Panics(t, func() { + registry.RegisterInterface("not_an_interface", (*testdata.Dog)(nil)) + }) + + // Duplicate registration with same concrete type. + require.NotPanics(t, func() { + registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{}) + }) + + // Duplicate registration with different concrete type on same typeURL. + require.PanicsWithError( + t, + "concrete type *testdata.Dog has already been registered under typeURL /testdata.Dog, cannot register *types_test.FakeDog under same typeURL. "+ + "This usually means that there are conflicting modules registering different concrete types for a same interface implementation", + func() { + registry.RegisterImplementations((*testdata.Animal)(nil), &FakeDog{}) + }, + ) +} + +func TestUnpackInterfaces(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + + spot := &testdata.Dog{Name: "Spot"} + any, err := types.NewAnyWithValue(spot) + require.NoError(t, err) + + hasAny := testdata.HasAnimal{ + Animal: any, + X: 1, + } + bz, err := hasAny.Marshal() + require.NoError(t, err) + + var hasAny2 testdata.HasAnimal + err = hasAny2.Unmarshal(bz) + require.NoError(t, err) + + err = types.UnpackInterfaces(hasAny2, registry) + require.NoError(t, err) + + require.Equal(t, spot, hasAny2.Animal.GetCachedValue()) +} + +func TestNested(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + + spot := &testdata.Dog{Name: "Spot"} + any, err := types.NewAnyWithValue(spot) + require.NoError(t, err) + + ha := &testdata.HasAnimal{Animal: any} + any2, err := types.NewAnyWithValue(ha) + require.NoError(t, err) + + hha := &testdata.HasHasAnimal{HasAnimal: any2} + any3, err := types.NewAnyWithValue(hha) + require.NoError(t, err) + + hhha := testdata.HasHasHasAnimal{HasHasAnimal: any3} + + // marshal + bz, err := hhha.Marshal() + require.NoError(t, err) + + // unmarshal + var hhha2 testdata.HasHasHasAnimal + err = hhha2.Unmarshal(bz) + require.NoError(t, err) + err = types.UnpackInterfaces(hhha2, registry) + require.NoError(t, err) + + require.Equal(t, spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal()) +} + +func TestAny_ProtoJSON(t *testing.T) { + spot := &testdata.Dog{Name: "Spot"} + any, err := types.NewAnyWithValue(spot) + require.NoError(t, err) + + jm := &jsonpb.Marshaler{} + json, err := jm.MarshalToString(any) + require.NoError(t, err) + require.Equal(t, "{\"@type\":\"/testdata.Dog\",\"name\":\"Spot\"}", json) + + registry := testdata.NewTestInterfaceRegistry() + jum := &jsonpb.Unmarshaler{} + var any2 types.Any + err = jum.Unmarshal(strings.NewReader(json), &any2) + require.NoError(t, err) + var animal testdata.Animal + err = registry.UnpackAny(&any2, &animal) + require.NoError(t, err) + require.Equal(t, spot, animal) + + ha := &testdata.HasAnimal{ + Animal: any, + } + err = ha.UnpackInterfaces(types.ProtoJSONPacker{JSONPBMarshaler: jm}) + require.NoError(t, err) + json, err = jm.MarshalToString(ha) + require.NoError(t, err) + require.Equal(t, "{\"animal\":{\"@type\":\"/testdata.Dog\",\"name\":\"Spot\"}}", json) + + require.NoError(t, err) + var ha2 testdata.HasAnimal + err = jum.Unmarshal(strings.NewReader(json), &ha2) + require.NoError(t, err) + err = ha2.UnpackInterfaces(registry) + require.NoError(t, err) + require.Equal(t, spot, ha2.Animal.GetCachedValue()) +} + +// this instance of grpc.ClientConn is used to test packing service method +// requests into Any's +type testAnyPackClient struct { + any types.Any + interfaceRegistry types.InterfaceRegistry +} + +var _ grpc.ClientConn = &testAnyPackClient{} + +func (t *testAnyPackClient) Invoke(_ context.Context, method string, args, _ interface{}, _ ...grpc2.CallOption) error { + reqMsg, ok := args.(proto.Message) + if !ok { + return fmt.Errorf("can't proto marshal %T", args) + } + + // registry the method request type with the interface registry + t.interfaceRegistry.RegisterCustomTypeURL((*interface{})(nil), method, reqMsg) + + bz, err := proto.Marshal(reqMsg) + if err != nil { + return err + } + + t.any.TypeUrl = method + t.any.Value = bz + + return nil +} + +func (t *testAnyPackClient) NewStream(context.Context, *grpc2.StreamDesc, string, ...grpc2.CallOption) (grpc2.ClientStream, error) { + return nil, fmt.Errorf("not supported") +} + +func TestAny_ServiceRequestProtoJSON(t *testing.T) { + interfaceRegistry := types.NewInterfaceRegistry() + anyPacker := &testAnyPackClient{interfaceRegistry: interfaceRegistry} + dogMsgClient := testdata.NewMsgClient(anyPacker) + _, err := dogMsgClient.CreateDog(context.Background(), &testdata.MsgCreateDog{Dog: &testdata.Dog{ + Name: "spot", + }}) + require.NoError(t, err) + + // marshal JSON + cdc := codec.NewProtoCodec(interfaceRegistry) + bz, err := cdc.MarshalJSON(&anyPacker.any) + require.NoError(t, err) + require.Equal(t, + `{"@type":"/testdata.Msg/CreateDog","dog":{"size":"","name":"spot"}}`, + string(bz)) + + // unmarshal JSON + var any2 types.Any + err = cdc.UnmarshalJSON(bz, &any2) + require.NoError(t, err) + require.Equal(t, anyPacker.any, any2) +} diff --git a/codec/unknownproto/benchmarks_test.go b/codec/unknownproto/benchmarks_test.go new file mode 100644 index 000000000000..373dda7acfd5 --- /dev/null +++ b/codec/unknownproto/benchmarks_test.go @@ -0,0 +1,113 @@ +package unknownproto_test + +import ( + "sync" + "testing" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/unknownproto" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +var n1BBlob []byte + +func init() { + n1B := &testdata.Nested1B{ + Id: 1, + Age: 99, + Nested: &testdata.Nested2B{ + Id: 2, + Route: "Wintery route", + Fee: 99, + Nested: &testdata.Nested3B{ + Id: 3, + Name: "3A this one that one there those oens", + Age: 4588, + B4: []*testdata.Nested4B{ + { + Id: 4, + Age: 88, + Name: "Nested4B", + }, + }, + }, + }, + } + + var err error + n1BBlob, err = proto.Marshal(n1B) + if err != nil { + panic(err) + } +} + +func BenchmarkRejectUnknownFields_serial(b *testing.B) { + benchmarkRejectUnknownFields(b, false) +} +func BenchmarkRejectUnknownFields_parallel(b *testing.B) { + benchmarkRejectUnknownFields(b, true) +} + +func benchmarkRejectUnknownFields(b *testing.B, parallel bool) { + b.ReportAllocs() + + if !parallel { + b.ResetTimer() + for i := 0; i < b.N; i++ { + n1A := new(testdata.Nested1A) + if err := unknownproto.RejectUnknownFieldsStrict(n1BBlob, n1A, unknownproto.DefaultAnyResolver{}); err == nil { + b.Fatal("expected an error") + } + b.SetBytes(int64(len(n1BBlob))) + } + } else { + var mu sync.Mutex + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + // To simulate the conditions of multiple transactions being processed in parallel. + n1A := new(testdata.Nested1A) + if err := unknownproto.RejectUnknownFieldsStrict(n1BBlob, n1A, unknownproto.DefaultAnyResolver{}); err == nil { + b.Fatal("expected an error") + } + mu.Lock() + b.SetBytes(int64(len(n1BBlob))) + mu.Unlock() + } + }) + } +} + +func BenchmarkProtoUnmarshal_serial(b *testing.B) { + benchmarkProtoUnmarshal(b, false) +} +func BenchmarkProtoUnmarshal_parallel(b *testing.B) { + benchmarkProtoUnmarshal(b, true) +} +func benchmarkProtoUnmarshal(b *testing.B, parallel bool) { + b.ReportAllocs() + + if !parallel { + for i := 0; i < b.N; i++ { + n1A := new(testdata.Nested1A) + if err := proto.Unmarshal(n1BBlob, n1A); err == nil { + b.Fatal("expected an error") + } + b.SetBytes(int64(len(n1BBlob))) + } + } else { + var mu sync.Mutex + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + n1A := new(testdata.Nested1A) + if err := proto.Unmarshal(n1BBlob, n1A); err == nil { + b.Fatal("expected an error") + } + mu.Lock() + b.SetBytes(int64(len(n1BBlob))) + mu.Unlock() + } + }) + } +} diff --git a/codec/unknownproto/doc.go b/codec/unknownproto/doc.go new file mode 100644 index 000000000000..0e0a46342291 --- /dev/null +++ b/codec/unknownproto/doc.go @@ -0,0 +1,24 @@ +/* +unknownproto implements functionality to "type check" protobuf serialized byte sequences +against an expected proto.Message to report: + +a) Unknown fields in the stream -- this is indicative of mismatched services, perhaps a malicious actor + +b) Mismatched wire types for a field -- this is indicative of mismatched services + +Its API signature is similar to proto.Unmarshal([]byte, proto.Message) in the strict case + + if err := RejectUnknownFieldsStrict(protoBlob, protoMessage, false); err != nil { + // Handle the error. + } + +and ideally should be added before invoking proto.Unmarshal, if you'd like to enforce the features mentioned above. + +By default, for security we report every single field that's unknown, whether a non-critical field or not. To customize +this behavior, please set the boolean parameter allowUnknownNonCriticals to true to RejectUnknownFields: + + if err := RejectUnknownFields(protoBlob, protoMessage, true); err != nil { + // Handle the error. + } +*/ +package unknownproto diff --git a/codec/unknownproto/regression_test.go b/codec/unknownproto/regression_test.go new file mode 100644 index 000000000000..24c4056fbd39 --- /dev/null +++ b/codec/unknownproto/regression_test.go @@ -0,0 +1,25 @@ +package unknownproto_test + +import ( + "encoding/hex" + "io" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" +) + +// Issue #7739: Catch parse errors resulting from unexpected EOF in +// protowire.ConsumeFieldValue. Discovered from fuzzing. +func TestBadBytesPassedIntoDecoder(t *testing.T) { + data, _ := hex.DecodeString("0A9F010A9C200A2D2F6962632E636F72652E636F6E6E656374696F6E2E76312E4D7367436F6E6E656374696F584F75656E496E6974126B0A0D6962637A65726F636C69656E74120B6962637A65726F636F6E6E1A1C0A0C6962636F6E65636C69656E74120A6962636F6E65636F6E6E00002205312E302E302A283235454635364341373935313335453430393336384536444238313130463232413442453035433212080A0612040A0208011A40143342993E25DA936CDDC7BE3D8F603CA6E9661518D536D0C482E18A0154AA096E438A6B9BCADFCFC2F0D689DCCAF55B96399D67A8361B70F5DA13091E2F929") + cfg := simapp.MakeTestEncodingConfig() + decoder := cfg.TxConfig.TxDecoder() + tx, err := decoder(data) + + // TODO: When issue https://github.com/cosmos/cosmos-sdk/issues/7846 + // is addressed, we'll remove this .Contains check. + require.Contains(t, err.Error(), io.ErrUnexpectedEOF.Error()) + require.Nil(t, tx) +} diff --git a/codec/unknownproto/unit_helpers_test.go b/codec/unknownproto/unit_helpers_test.go new file mode 100644 index 000000000000..9c408a6d1f8a --- /dev/null +++ b/codec/unknownproto/unit_helpers_test.go @@ -0,0 +1,32 @@ +package unknownproto + +import ( + "fmt" + "testing" + + "google.golang.org/protobuf/encoding/protowire" +) + +func TestWireTypeToString(t *testing.T) { + tests := []struct { + typ protowire.Type + want string + }{ + {typ: 0, want: "varint"}, + {typ: 1, want: "fixed64"}, + {typ: 2, want: "bytes"}, + {typ: 3, want: "start_group"}, + {typ: 4, want: "end_group"}, + {typ: 5, want: "fixed32"}, + {typ: 95, want: "unknown type: 95"}, + } + + for _, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("wireType=%d", tt.typ), func(t *testing.T) { + if g, w := wireTypeToString(tt.typ), tt.want; g != w { + t.Fatalf("Mismatch:\nGot: %q\nWant: %q\n", g, w) + } + }) + } +} diff --git a/codec/unknownproto/unknown_fields.go b/codec/unknownproto/unknown_fields.go new file mode 100644 index 000000000000..b9db6429628b --- /dev/null +++ b/codec/unknownproto/unknown_fields.go @@ -0,0 +1,439 @@ +package unknownproto + +import ( + "bytes" + "compress/gzip" + "errors" + "fmt" + "io/ioutil" + "reflect" + "strings" + "sync" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + "google.golang.org/protobuf/encoding/protowire" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +const bit11NonCritical = 1 << 10 + +type descriptorIface interface { + Descriptor() ([]byte, []int) +} + +// RejectUnknownFieldsStrict rejects any bytes bz with an error that has unknown fields for the provided proto.Message type. +// This function traverses inside of messages nested via google.protobuf.Any. It does not do any deserialization of the proto.Message. +// An AnyResolver must be provided for traversing inside google.protobuf.Any's. +func RejectUnknownFieldsStrict(bz []byte, msg proto.Message, resolver jsonpb.AnyResolver) error { + _, err := RejectUnknownFields(bz, msg, false, resolver) + return err +} + +// RejectUnknownFields rejects any bytes bz with an error that has unknown fields for the provided proto.Message type with an +// option to allow non-critical fields (specified as those fields with bit 11) to pass through. In either case, the +// hasUnknownNonCriticals will be set to true if non-critical fields were encountered during traversal. This flag can be +// used to treat a message with non-critical field different in different security contexts (such as transaction signing). +// This function traverses inside of messages nested via google.protobuf.Any. It does not do any deserialization of the proto.Message. +// An AnyResolver must be provided for traversing inside google.protobuf.Any's. +func RejectUnknownFields(bz []byte, msg proto.Message, allowUnknownNonCriticals bool, resolver jsonpb.AnyResolver) (hasUnknownNonCriticals bool, err error) { + if len(bz) == 0 { + return hasUnknownNonCriticals, nil + } + + desc, ok := msg.(descriptorIface) + if !ok { + return hasUnknownNonCriticals, fmt.Errorf("%T does not have a Descriptor() method", msg) + } + + fieldDescProtoFromTagNum, _, err := getDescriptorInfo(desc, msg) + if err != nil { + return hasUnknownNonCriticals, err + } + + for len(bz) > 0 { + tagNum, wireType, m := protowire.ConsumeTag(bz) + if m < 0 { + return hasUnknownNonCriticals, errors.New("invalid length") + } + + fieldDescProto, ok := fieldDescProtoFromTagNum[int32(tagNum)] + switch { + case ok: + // Assert that the wireTypes match. + if !canEncodeType(wireType, fieldDescProto.GetType()) { + return hasUnknownNonCriticals, &errMismatchedWireType{ + Type: reflect.ValueOf(msg).Type().String(), + TagNum: tagNum, + GotWireType: wireType, + WantWireType: protowire.Type(fieldDescProto.WireType()), + } + } + + default: + isCriticalField := tagNum&bit11NonCritical == 0 + + if !isCriticalField { + hasUnknownNonCriticals = true + } + + if isCriticalField || !allowUnknownNonCriticals { + // The tag is critical, so report it. + return hasUnknownNonCriticals, &errUnknownField{ + Type: reflect.ValueOf(msg).Type().String(), + TagNum: tagNum, + WireType: wireType, + } + } + } + + // Skip over the bytes that store fieldNumber and wireType bytes. + bz = bz[m:] + n := protowire.ConsumeFieldValue(tagNum, wireType, bz) + if n < 0 { + err = fmt.Errorf("could not consume field value for tagNum: %d, wireType: %q; %w", + tagNum, wireTypeToString(wireType), protowire.ParseError(n)) + return hasUnknownNonCriticals, err + } + fieldBytes := bz[:n] + bz = bz[n:] + + // An unknown but non-critical field or just a scalar type (aka *INT and BYTES like). + if fieldDescProto == nil || fieldDescProto.IsScalar() { + continue + } + + protoMessageName := fieldDescProto.GetTypeName() + if protoMessageName == "" { + switch typ := fieldDescProto.GetType(); typ { + case descriptor.FieldDescriptorProto_TYPE_STRING, descriptor.FieldDescriptorProto_TYPE_BYTES: + // At this point only TYPE_STRING is expected to be unregistered, since FieldDescriptorProto.IsScalar() returns false for + // TYPE_BYTES and TYPE_STRING as per + // https://github.com/gogo/protobuf/blob/5628607bb4c51c3157aacc3a50f0ab707582b805/protoc-gen-gogo/descriptor/descriptor.go#L95-L118 + default: + return hasUnknownNonCriticals, fmt.Errorf("failed to get typename for message of type %v, can only be TYPE_STRING or TYPE_BYTES", typ) + } + continue + } + + // Let's recursively traverse and typecheck the field. + + // consume length prefix of nested message + _, o := protowire.ConsumeVarint(fieldBytes) + fieldBytes = fieldBytes[o:] + + var msg proto.Message + var err error + + if protoMessageName == ".google.protobuf.Any" { + // Firstly typecheck types.Any to ensure nothing snuck in. + hasUnknownNonCriticalsChild, err := RejectUnknownFields(fieldBytes, (*types.Any)(nil), allowUnknownNonCriticals, resolver) + hasUnknownNonCriticals = hasUnknownNonCriticals || hasUnknownNonCriticalsChild + if err != nil { + return hasUnknownNonCriticals, err + } + // And finally we can extract the TypeURL containing the protoMessageName. + any := new(types.Any) + if err := proto.Unmarshal(fieldBytes, any); err != nil { + return hasUnknownNonCriticals, err + } + protoMessageName = any.TypeUrl + fieldBytes = any.Value + msg, err = resolver.Resolve(protoMessageName) + if err != nil { + return hasUnknownNonCriticals, err + } + } else { + msg, err = protoMessageForTypeName(protoMessageName[1:]) + if err != nil { + return hasUnknownNonCriticals, err + } + } + + hasUnknownNonCriticalsChild, err := RejectUnknownFields(fieldBytes, msg, allowUnknownNonCriticals, resolver) + hasUnknownNonCriticals = hasUnknownNonCriticals || hasUnknownNonCriticalsChild + if err != nil { + return hasUnknownNonCriticals, err + } + } + + return hasUnknownNonCriticals, nil +} + +var protoMessageForTypeNameMu sync.RWMutex +var protoMessageForTypeNameCache = make(map[string]proto.Message) + +// protoMessageForTypeName takes in a fully qualified name e.g. testdata.TestVersionFD1 +// and returns a corresponding empty protobuf message that serves the prototype for typechecking. +func protoMessageForTypeName(protoMessageName string) (proto.Message, error) { + protoMessageForTypeNameMu.RLock() + msg, ok := protoMessageForTypeNameCache[protoMessageName] + protoMessageForTypeNameMu.RUnlock() + if ok { + return msg, nil + } + + concreteGoType := proto.MessageType(protoMessageName) + if concreteGoType == nil { + return nil, fmt.Errorf("failed to retrieve the message of type %q", protoMessageName) + } + + value := reflect.New(concreteGoType).Elem() + msg, ok = value.Interface().(proto.Message) + if !ok { + return nil, fmt.Errorf("%q does not implement proto.Message", protoMessageName) + } + + // Now cache it. + protoMessageForTypeNameMu.Lock() + protoMessageForTypeNameCache[protoMessageName] = msg + protoMessageForTypeNameMu.Unlock() + + return msg, nil +} + +// checks is a mapping of protowire.Type to supported descriptor.FieldDescriptorProto_Type. +// it is implemented this way so as to have constant time lookups and avoid the overhead +// from O(n) walking of switch. The change to using this mapping boosts throughput by about 200%. +var checks = [...]map[descriptor.FieldDescriptorProto_Type]bool{ + // "0 Varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum" + 0: { + descriptor.FieldDescriptorProto_TYPE_INT32: true, + descriptor.FieldDescriptorProto_TYPE_INT64: true, + descriptor.FieldDescriptorProto_TYPE_UINT32: true, + descriptor.FieldDescriptorProto_TYPE_UINT64: true, + descriptor.FieldDescriptorProto_TYPE_SINT32: true, + descriptor.FieldDescriptorProto_TYPE_SINT64: true, + descriptor.FieldDescriptorProto_TYPE_BOOL: true, + descriptor.FieldDescriptorProto_TYPE_ENUM: true, + }, + + // "1 64-bit: fixed64, sfixed64, double" + 1: { + descriptor.FieldDescriptorProto_TYPE_FIXED64: true, + descriptor.FieldDescriptorProto_TYPE_SFIXED64: true, + descriptor.FieldDescriptorProto_TYPE_DOUBLE: true, + }, + + // "2 Length-delimited: string, bytes, embedded messages, packed repeated fields" + 2: { + descriptor.FieldDescriptorProto_TYPE_STRING: true, + descriptor.FieldDescriptorProto_TYPE_BYTES: true, + descriptor.FieldDescriptorProto_TYPE_MESSAGE: true, + // The following types can be packed repeated. + // ref: "Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire types) can be declared "packed"." + // ref: https://developers.google.com/protocol-buffers/docs/encoding#packed + descriptor.FieldDescriptorProto_TYPE_INT32: true, + descriptor.FieldDescriptorProto_TYPE_INT64: true, + descriptor.FieldDescriptorProto_TYPE_UINT32: true, + descriptor.FieldDescriptorProto_TYPE_UINT64: true, + descriptor.FieldDescriptorProto_TYPE_SINT32: true, + descriptor.FieldDescriptorProto_TYPE_SINT64: true, + descriptor.FieldDescriptorProto_TYPE_BOOL: true, + descriptor.FieldDescriptorProto_TYPE_ENUM: true, + descriptor.FieldDescriptorProto_TYPE_FIXED64: true, + descriptor.FieldDescriptorProto_TYPE_SFIXED64: true, + descriptor.FieldDescriptorProto_TYPE_DOUBLE: true, + }, + + // "3 Start group: groups (deprecated)" + 3: { + descriptor.FieldDescriptorProto_TYPE_GROUP: true, + }, + + // "4 End group: groups (deprecated)" + 4: { + descriptor.FieldDescriptorProto_TYPE_GROUP: true, + }, + + // "5 32-bit: fixed32, sfixed32, float" + 5: { + descriptor.FieldDescriptorProto_TYPE_FIXED32: true, + descriptor.FieldDescriptorProto_TYPE_SFIXED32: true, + descriptor.FieldDescriptorProto_TYPE_FLOAT: true, + }, +} + +// canEncodeType returns true if the wireType is suitable for encoding the descriptor type. +// See https://developers.google.com/protocol-buffers/docs/encoding#structure. +func canEncodeType(wireType protowire.Type, descType descriptor.FieldDescriptorProto_Type) bool { + if iwt := int(wireType); iwt < 0 || iwt >= len(checks) { + return false + } + return checks[wireType][descType] +} + +// errMismatchedWireType describes a mismatch between +// expected and got wireTypes for a specific tag number. +type errMismatchedWireType struct { + Type string + GotWireType protowire.Type + WantWireType protowire.Type + TagNum protowire.Number +} + +// String implements fmt.Stringer. +func (mwt *errMismatchedWireType) String() string { + return fmt.Sprintf("Mismatched %q: {TagNum: %d, GotWireType: %q != WantWireType: %q}", + mwt.Type, mwt.TagNum, wireTypeToString(mwt.GotWireType), wireTypeToString(mwt.WantWireType)) +} + +// Error implements the error interface. +func (mwt *errMismatchedWireType) Error() string { + return mwt.String() +} + +var _ error = (*errMismatchedWireType)(nil) + +func wireTypeToString(wt protowire.Type) string { + switch wt { + case 0: + return "varint" + case 1: + return "fixed64" + case 2: + return "bytes" + case 3: + return "start_group" + case 4: + return "end_group" + case 5: + return "fixed32" + default: + return fmt.Sprintf("unknown type: %d", wt) + } +} + +// errUnknownField represents an error indicating that we encountered +// a field that isn't available in the target proto.Message. +type errUnknownField struct { + Type string + TagNum protowire.Number + WireType protowire.Type +} + +// String implements fmt.Stringer. +func (twt *errUnknownField) String() string { + return fmt.Sprintf("errUnknownField %q: {TagNum: %d, WireType:%q}", + twt.Type, twt.TagNum, wireTypeToString(twt.WireType)) +} + +// Error implements the error interface. +func (twt *errUnknownField) Error() string { + return twt.String() +} + +var _ error = (*errUnknownField)(nil) + +var ( + protoFileToDesc = make(map[string]*descriptor.FileDescriptorProto) + protoFileToDescMu sync.RWMutex +) + +func unnestDesc(mdescs []*descriptor.DescriptorProto, indices []int) *descriptor.DescriptorProto { + mdesc := mdescs[indices[0]] + for _, index := range indices[1:] { + mdesc = mdesc.NestedType[index] + } + return mdesc +} + +// Invoking descriptor.ForMessage(proto.Message.(Descriptor).Descriptor()) is incredibly slow +// for every single message, thus the need for a hand-rolled custom version that's performant and cacheable. +func extractFileDescMessageDesc(desc descriptorIface) (*descriptor.FileDescriptorProto, *descriptor.DescriptorProto, error) { + gzippedPb, indices := desc.Descriptor() + + protoFileToDescMu.RLock() + cached, ok := protoFileToDesc[string(gzippedPb)] + protoFileToDescMu.RUnlock() + + if ok { + return cached, unnestDesc(cached.MessageType, indices), nil + } + + // Time to gunzip the content of the FileDescriptor and then proto unmarshal them. + gzr, err := gzip.NewReader(bytes.NewReader(gzippedPb)) + if err != nil { + return nil, nil, err + } + protoBlob, err := ioutil.ReadAll(gzr) + if err != nil { + return nil, nil, err + } + + fdesc := new(descriptor.FileDescriptorProto) + if err := proto.Unmarshal(protoBlob, fdesc); err != nil { + return nil, nil, err + } + + // Now cache the FileDescriptor. + protoFileToDescMu.Lock() + protoFileToDesc[string(gzippedPb)] = fdesc + protoFileToDescMu.Unlock() + + // Unnest the type if necessary. + return fdesc, unnestDesc(fdesc.MessageType, indices), nil +} + +type descriptorMatch struct { + cache map[int32]*descriptor.FieldDescriptorProto + desc *descriptor.DescriptorProto +} + +var descprotoCacheMu sync.RWMutex +var descprotoCache = make(map[reflect.Type]*descriptorMatch) + +// getDescriptorInfo retrieves the mapping of field numbers to their respective field descriptors. +func getDescriptorInfo(desc descriptorIface, msg proto.Message) (map[int32]*descriptor.FieldDescriptorProto, *descriptor.DescriptorProto, error) { + key := reflect.ValueOf(msg).Type() + + descprotoCacheMu.RLock() + got, ok := descprotoCache[key] + descprotoCacheMu.RUnlock() + + if ok { + return got.cache, got.desc, nil + } + + // Now compute and cache the index. + _, md, err := extractFileDescMessageDesc(desc) + if err != nil { + return nil, nil, err + } + + tagNumToTypeIndex := make(map[int32]*descriptor.FieldDescriptorProto) + for _, field := range md.Field { + tagNumToTypeIndex[field.GetNumber()] = field + } + + descprotoCacheMu.Lock() + descprotoCache[key] = &descriptorMatch{ + cache: tagNumToTypeIndex, + desc: md, + } + descprotoCacheMu.Unlock() + + return tagNumToTypeIndex, md, nil +} + +// DefaultAnyResolver is a default implementation of AnyResolver which uses +// the default encoding of type URLs as specified by the protobuf specification. +type DefaultAnyResolver struct{} + +var _ jsonpb.AnyResolver = DefaultAnyResolver{} + +// Resolve is the AnyResolver.Resolve method. +func (d DefaultAnyResolver) Resolve(typeURL string) (proto.Message, error) { + // Only the part of typeURL after the last slash is relevant. + mname := typeURL + if slash := strings.LastIndex(mname, "/"); slash >= 0 { + mname = mname[slash+1:] + } + mt := proto.MessageType(mname) + if mt == nil { + return nil, fmt.Errorf("unknown message type %q", mname) + } + return reflect.New(mt.Elem()).Interface().(proto.Message), nil +} diff --git a/codec/unknownproto/unknown_fields_test.go b/codec/unknownproto/unknown_fields_test.go new file mode 100644 index 000000000000..ad3926cedb55 --- /dev/null +++ b/codec/unknownproto/unknown_fields_test.go @@ -0,0 +1,673 @@ +package unknownproto + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func TestRejectUnknownFieldsRepeated(t *testing.T) { + tests := []struct { + name string + in proto.Message + recv proto.Message + wantErr error + allowUnknownNonCriticals bool + hasUnknownNonCriticals bool + }{ + { + name: "Unknown field in midst of repeated values", + in: &testdata.TestVersion2{ + C: []*testdata.TestVersion2{ + { + C: []*testdata.TestVersion2{ + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + A: &testdata.TestVersion2{ + B: &testdata.TestVersion2{ + H: []*testdata.TestVersion1{ + { + X: 0x01, + }, + }, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + A: &testdata.TestVersion2{ + B: &testdata.TestVersion2{ + H: []*testdata.TestVersion1{ + { + X: 0x02, + }, + }, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + NewField: 411, + }, + }, + }, + }, + }, + }, + }, + recv: new(testdata.TestVersion1), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion1", + TagNum: 25, + WireType: 0, + }, + }, + { + name: "Unknown field in midst of repeated values, allowUnknownNonCriticals set", + allowUnknownNonCriticals: true, + in: &testdata.TestVersion2{ + C: []*testdata.TestVersion2{ + { + C: []*testdata.TestVersion2{ + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + A: &testdata.TestVersion2{ + B: &testdata.TestVersion2{ + H: []*testdata.TestVersion1{ + { + X: 0x01, + }, + }, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + A: &testdata.TestVersion2{ + B: &testdata.TestVersion2{ + H: []*testdata.TestVersion1{ + { + X: 0x02, + }, + }, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + NewField: 411, + }, + }, + }, + }, + }, + }, + }, + recv: new(testdata.TestVersion1), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion1", + TagNum: 25, + WireType: 0, + }, + }, + { + name: "Unknown field in midst of repeated values, non-critical field to be rejected", + in: &testdata.TestVersion3{ + C: []*testdata.TestVersion3{ + { + C: []*testdata.TestVersion3{ + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + A: &testdata.TestVersion3{ + B: &testdata.TestVersion3{ + X: 0x01, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + A: &testdata.TestVersion3{ + B: &testdata.TestVersion3{ + X: 0x02, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + NonCriticalField: "non-critical", + }, + }, + }, + }, + }, + }, + }, + recv: new(testdata.TestVersion1), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion1", + TagNum: 1031, + WireType: 2, + }, + hasUnknownNonCriticals: true, + }, + { + name: "Unknown field in midst of repeated values, non-critical field ignored", + allowUnknownNonCriticals: true, + in: &testdata.TestVersion3{ + C: []*testdata.TestVersion3{ + { + C: []*testdata.TestVersion3{ + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + A: &testdata.TestVersion3{ + B: &testdata.TestVersion3{ + X: 0x01, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + A: &testdata.TestVersion3{ + B: &testdata.TestVersion3{ + X: 0x02, + }, + }, + }, + }, + }, + { + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + NonCriticalField: "non-critical", + }, + }, + }, + }, + }, + }, + }, + recv: new(testdata.TestVersion1), + wantErr: nil, + hasUnknownNonCriticals: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + protoBlob, err := proto.Marshal(tt.in) + if err != nil { + t.Fatal(err) + } + hasUnknownNonCriticals, gotErr := RejectUnknownFields(protoBlob, tt.recv, tt.allowUnknownNonCriticals, DefaultAnyResolver{}) + require.Equal(t, tt.wantErr, gotErr) + require.Equal(t, tt.hasUnknownNonCriticals, hasUnknownNonCriticals) + }) + } +} + +func TestRejectUnknownFields_allowUnknownNonCriticals(t *testing.T) { + tests := []struct { + name string + in proto.Message + allowUnknownNonCriticals bool + wantErr error + }{ + { + name: "Field that's in the reserved range, should fail by default", + in: &testdata.Customer2{ + Id: 289, + Reserved: 99, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 1047, + WireType: 0, + }, + }, + { + name: "Field that's in the reserved range, toggle allowUnknownNonCriticals", + allowUnknownNonCriticals: true, + in: &testdata.Customer2{ + Id: 289, + Reserved: 99, + }, + wantErr: nil, + }, + { + name: "Unknown fields that are critical, but with allowUnknownNonCriticals set", + allowUnknownNonCriticals: true, + in: &testdata.Customer2{ + Id: 289, + City: testdata.Customer2_PaloAlto, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 6, + WireType: 0, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + blob, err := proto.Marshal(tt.in) + if err != nil { + t.Fatalf("Failed to marshal input: %v", err) + } + + c1 := new(testdata.Customer1) + _, gotErr := RejectUnknownFields(blob, c1, tt.allowUnknownNonCriticals, DefaultAnyResolver{}) + if !reflect.DeepEqual(gotErr, tt.wantErr) { + t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) + } + }) + } +} + +func TestRejectUnknownFieldsNested(t *testing.T) { + tests := []struct { + name string + in proto.Message + recv proto.Message + wantErr error + }{ + { + name: "TestVersion3 from TestVersionFD1", + in: &testdata.TestVersion2{ + X: 5, + Sum: &testdata.TestVersion2_E{ + E: 100, + }, + H: []*testdata.TestVersion1{ + {X: 999}, + {X: -55}, + { + X: 102, + Sum: &testdata.TestVersion1_F{ + F: &testdata.TestVersion1{ + X: 4, + }, + }, + }, + }, + Customer1: &testdata.Customer1{ + Id: 45, + Name: "customer1", + SubscriptionFee: 99, + }, + }, + recv: new(testdata.TestVersionFD1), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersionFD1", + TagNum: 12, + WireType: 2, + }, + }, + { + name: "Alternating oneofs", + in: &testdata.TestVersion3{ + Sum: &testdata.TestVersion3_E{ + E: 99, + }, + }, + recv: new(testdata.TestVersion3LoneOneOfValue), + wantErr: nil, + }, + { + name: "Alternating oneofs mismatched field", + in: &testdata.TestVersion3{ + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + X: 99, + }, + }, + }, + recv: new(testdata.TestVersion3LoneOneOfValue), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion3LoneOneOfValue", + TagNum: 7, + WireType: 2, + }, + }, + { + name: "Discrepancy in a deeply nested one of field", + in: &testdata.TestVersion3{ + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + Sum: &testdata.TestVersion3_F{ + F: &testdata.TestVersion3{ + X: 19, + Sum: &testdata.TestVersion3_E{ + E: 99, + }, + }, + }, + }, + }, + }, + recv: new(testdata.TestVersion3LoneNesting), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion3LoneNesting", + TagNum: 6, + WireType: 0, + }, + }, + { + name: "unknown field types.Any in G", + in: &testdata.TestVersion3{ + G: &types.Any{ + TypeUrl: "/testdata.TestVersion1", + Value: mustMarshal(&testdata.TestVersion2{ + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + NewField: 999, + }, + }, + }), + }, + }, + recv: new(testdata.TestVersion3), + wantErr: &errUnknownField{ + Type: "*testdata.TestVersion1", + TagNum: 25, + }, + }, + { + name: "types.Any with extra fields", + in: &testdata.TestVersionFD1WithExtraAny{ + G: &testdata.AnyWithExtra{ + Any: &types.Any{ + TypeUrl: "/testdata.TestVersion1", + Value: mustMarshal(&testdata.TestVersion2{ + Sum: &testdata.TestVersion2_F{ + F: &testdata.TestVersion2{ + NewField: 999, + }, + }, + }), + }, + B: 3, + C: 2, + }, + }, + recv: new(testdata.TestVersion3), + wantErr: &errUnknownField{ + Type: "*types.Any", + TagNum: 3, + WireType: 0, + }, + }, + { + name: "mismatched types.Any in G", + in: &testdata.TestVersion1{ + G: &types.Any{ + TypeUrl: "/testdata.TestVersion4LoneNesting", + Value: mustMarshal(&testdata.TestVersion3LoneNesting_Inner1{ + Inner: &testdata.TestVersion3LoneNesting_Inner1_InnerInner{ + Id: "ID", + City: "Gotham", + }, + }), + }, + }, + recv: new(testdata.TestVersion1), + wantErr: &errMismatchedWireType{ + Type: "*testdata.TestVersion3", + TagNum: 8, + GotWireType: 7, + WantWireType: 2, + }, + }, + { + name: "From nested proto message, message index 0", + in: &testdata.TestVersion3LoneNesting{ + Inner1: &testdata.TestVersion3LoneNesting_Inner1{ + Id: 10, + Name: "foo", + Inner: &testdata.TestVersion3LoneNesting_Inner1_InnerInner{ + Id: "ID", + City: "Palo Alto", + }, + }, + }, + recv: new(testdata.TestVersion4LoneNesting), + wantErr: nil, + }, + { + name: "From nested proto message, message index 1", + in: &testdata.TestVersion3LoneNesting{ + Inner2: &testdata.TestVersion3LoneNesting_Inner2{ + Id: "ID", + Country: "Maldives", + Inner: &testdata.TestVersion3LoneNesting_Inner2_InnerInner{ + Id: "ID", + City: "Unknown", + }, + }, + }, + recv: new(testdata.TestVersion4LoneNesting), + wantErr: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + protoBlob, err := proto.Marshal(tt.in) + if err != nil { + t.Fatal(err) + } + gotErr := RejectUnknownFieldsStrict(protoBlob, tt.recv, DefaultAnyResolver{}) + if !reflect.DeepEqual(gotErr, tt.wantErr) { + t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) + } + }) + } +} + +func TestRejectUnknownFieldsFlat(t *testing.T) { + tests := []struct { + name string + in proto.Message + wantErr error + }{ + { + name: "Oneof with same field number, shouldn't complain", + in: &testdata.Customer3{ + Id: 68, + Name: "ACME3", + Payment: &testdata.Customer3_CreditCardNo{ + CreditCardNo: "123-XXXX-XXX881", + }, + }, + wantErr: nil, + }, + { + name: "Oneof with different field number, should fail", + in: &testdata.Customer3{ + Id: 68, + Name: "ACME3", + Payment: &testdata.Customer3_ChequeNo{ + ChequeNo: "123XXXXXXX881", + }, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 8, WireType: 2, + }, + }, + { + name: "Any in a field, the extra field will be serialized so should fail", + in: &testdata.Customer2{ + Miscellaneous: &types.Any{}, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 10, + WireType: 2, + }, + }, + { + name: "With a nested struct as a field", + in: &testdata.Customer3{ + Id: 289, + Original: &testdata.Customer1{ + Id: 991, + }, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 9, + WireType: 2, + }, + }, + { + name: "An extra field that's non-existent in Customer1", + in: &testdata.Customer2{ + Id: 289, + Name: "Customer1", + Industry: 5299, + Fewer: 199.9, + }, + wantErr: &errMismatchedWireType{ + Type: "*testdata.Customer1", + TagNum: 2, GotWireType: 0, WantWireType: 2, + }, + }, + { + name: "Using a field that's in the reserved range, should fail by default", + in: &testdata.Customer2{ + Id: 289, + Reserved: 99, + }, + wantErr: &errUnknownField{ + Type: "*testdata.Customer1", + TagNum: 1047, + WireType: 0, + }, + }, + { + name: "Only fields matching", + in: &testdata.Customer2{ + Id: 289, + Name: "Customer1", + }, + wantErr: &errMismatchedWireType{ + Type: "*testdata.Customer1", + TagNum: 3, GotWireType: 2, WantWireType: 5, + }, + }, + { + name: "Extra field that's non-existent in Customer1, along with Reserved set", + in: &testdata.Customer2{ + Id: 289, + Name: "Customer1", + Industry: 5299, + Fewer: 199.9, + Reserved: 819, + }, + wantErr: &errMismatchedWireType{ + Type: "*testdata.Customer1", + TagNum: 2, GotWireType: 0, WantWireType: 2, + }, + }, + { + name: "Using enumerated field", + in: &testdata.Customer2{ + Id: 289, + Name: "Customer1", + Industry: 5299, + City: testdata.Customer2_PaloAlto, + }, + wantErr: &errMismatchedWireType{ + Type: "*testdata.Customer1", + TagNum: 2, + GotWireType: 0, WantWireType: 2, + }, + }, + { + name: "multiple extraneous fields", + in: &testdata.Customer2{ + Id: 289, + Name: "Customer1", + Industry: 5299, + City: testdata.Customer2_PaloAlto, + Fewer: 45, + }, + wantErr: &errMismatchedWireType{ + TagNum: 2, GotWireType: 0, WantWireType: 2, + Type: "*testdata.Customer1", + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + blob, err := proto.Marshal(tt.in) + if err != nil { + t.Fatalf("Failed to marshal input: %v", err) + } + + c1 := new(testdata.Customer1) + gotErr := RejectUnknownFieldsStrict(blob, c1, DefaultAnyResolver{}) + if !reflect.DeepEqual(gotErr, tt.wantErr) { + t.Fatalf("Error mismatch\nGot:\n%s\n\nWant:\n%s", gotErr, tt.wantErr) + } + }) + } +} + +// Issue https://github.com/cosmos/cosmos-sdk/issues/7222, we need to ensure that repeated +// uint64 are recognized as packed. +func TestPackedEncoding(t *testing.T) { + data := testdata.TestRepeatedUints{Nums: []uint64{12, 13}} + + marshalled, err := data.Marshal() + require.NoError(t, err) + + unmarshalled := &testdata.TestRepeatedUints{} + _, err = RejectUnknownFields(marshalled, unmarshalled, false, DefaultAnyResolver{}) + require.NoError(t, err) +} + +func mustMarshal(msg proto.Message) []byte { + blob, err := proto.Marshal(msg) + if err != nil { + panic(err) + } + return blob +} diff --git a/codec/yaml.go b/codec/yaml.go new file mode 100644 index 000000000000..bba5e90ed821 --- /dev/null +++ b/codec/yaml.go @@ -0,0 +1,29 @@ +package codec + +import ( + "encoding/json" + + "github.com/gogo/protobuf/proto" + "gopkg.in/yaml.v2" +) + +// MarshalYAML marshals toPrint using jsonMarshaler to leverage specialized MarshalJSON methods +// (usually related to serialize data with protobuf or amin depending on a configuration). +// This involves additional roundtrip through JSON. +func MarshalYAML(jsonMarshaler JSONMarshaler, toPrint proto.Message) ([]byte, error) { + // We are OK with the performance hit of the additional JSON roundtip. MarshalYAML is not + // used in any critical parts of the system. + bz, err := jsonMarshaler.MarshalJSON(toPrint) + if err != nil { + return nil, err + } + + // generate YAML by decoding JSON and re-encoding to YAML + var j interface{} + err = json.Unmarshal(bz, &j) + if err != nil { + return nil, err + } + + return yaml.Marshal(j) +} diff --git a/codec/yaml_test.go b/codec/yaml_test.go new file mode 100644 index 000000000000..e9206a74a3d7 --- /dev/null +++ b/codec/yaml_test.go @@ -0,0 +1,49 @@ +package codec_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func TestMarshalYAML(t *testing.T) { + dog := &testdata.Dog{ + Size_: "small", + Name: "Spot", + } + any, err := types.NewAnyWithValue(dog) + require.NoError(t, err) + hasAnimal := &testdata.HasAnimal{ + Animal: any, + X: 0, + } + + // proto + protoCdc := codec.NewProtoCodec(NewTestInterfaceRegistry()) + bz, err := codec.MarshalYAML(protoCdc, hasAnimal) + require.NoError(t, err) + require.Equal(t, `animal: + '@type': /testdata.Dog + name: Spot + size: small +x: "0" +`, string(bz)) + + // amino + aminoCdc := codec.NewAminoCodec(&codec.LegacyAmino{testdata.NewTestAmino()}) + bz, err = codec.MarshalYAML(aminoCdc, hasAnimal) + require.NoError(t, err) + require.Equal(t, `type: testdata/HasAnimal +value: + animal: + type: testdata/Dog + value: + name: Spot + size: small +`, string(bz)) +} diff --git a/contrib/devtools/Makefile b/contrib/devtools/Makefile index 735ab395178c..2e3741fd852f 100644 --- a/contrib/devtools/Makefile +++ b/contrib/devtools/Makefile @@ -15,13 +15,9 @@ ifeq ($(GO),) $(error could not find go. Is it in PATH? $(GO)) endif -GOPATH ?= $(shell $(GO) env GOPATH) -GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com -GOLANGCI_LINT_HASHSUM := 8d21cc95da8d3daf8321ac40091456fc26123c964d7c2281d339d431f2f4c840 - -### -# Functions -### +############################################################################### +### Functions ### +############################################################################### go_get = $(if $(findstring Windows_NT,$(OS)),\ IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ @@ -35,29 +31,36 @@ cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) -### -# tools -### -TOOLS_DESTDIR ?= $(GOPATH)/bin +############################################################################### +### Tools ### +############################################################################### + +PREFIX ?= /usr/local +BIN ?= $(PREFIX)/bin +UNAME_S ?= $(shell uname -s) +UNAME_M ?= $(shell uname -m) -GOLANGCI_LINT = $(TOOLS_DESTDIR)/golangci-lint -STATIK = $(TOOLS_DESTDIR)/statik -RUNSIM = $(TOOLS_DESTDIR)/runsim +GOPATH ?= $(shell $(GO) env GOPATH) +GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com -all: tools +BUF_VERSION ?= 0.11.0 -tools: statik runsim golangci-lint +TOOLS_DESTDIR ?= $(GOPATH)/bin +STATIK = $(TOOLS_DESTDIR)/statik +RUNSIM = $(TOOLS_DESTDIR)/runsim -golangci-lint: $(GOLANGCI_LINT) -$(GOLANGCI_LINT): $(mkfile_dir)/install-golangci-lint.sh - @echo "Installing golangci-lint..." - @bash $(mkfile_dir)/install-golangci-lint.sh $(TOOLS_DESTDIR) $(GOLANGCI_LINT_HASHSUM) +tools: tools-stamp +tools-stamp: statik runsim + # Create dummy file to satisfy dependency and avoid + # rebuilding when this Makefile target is hit twice + # in a row. + touch $@ # Install the runsim binary with a temporary workaround of entering an outside # directory as the "go get" command ignores the -mod option and will polute the # go.{mod, sum} files. -# +# # ref: https://github.com/golang/go/issues/30515 statik: $(STATIK) $(STATIK): @@ -67,7 +70,7 @@ $(STATIK): # Install the runsim binary with a temporary workaround of entering an outside # directory as the "go get" command ignores the -mod option and will polute the # go.{mod, sum} files. -# +# # ref: https://github.com/golang/go/issues/30515 runsim: $(RUNSIM) $(RUNSIM): @@ -78,4 +81,4 @@ tools-clean: rm -f $(STATIK) $(GOLANGCI_LINT) $(RUNSIM) rm -f tools-stamp -.PHONY: all tools tools-clean +.PHONY: tools-clean statik runsim 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/devtools/install-golangci-lint.sh b/contrib/devtools/install-golangci-lint.sh deleted file mode 100644 index 3b0842e59cf3..000000000000 --- a/contrib/devtools/install-golangci-lint.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -installer="$(mktemp)" -trap "rm -f ${installer}" EXIT - -GOBIN="${1}" -CURL="$(which curl)" -HASHSUM="${2}" - -f_sha256() { - local l_file - l_file=$1 - python -sBc "import hashlib;print(hashlib.sha256(open('$l_file','rb').read()).hexdigest())" -} - -get_latest_release() { - "${CURL}" --silent "https://api.github.com/repos/$1/releases/latest" | \ - grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' -} - -VERSION="$(get_latest_release golangci/golangci-lint)" - -echo "Downloading golangci-lint ${VERSION} installer ..." >&2 -"${CURL}" -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/${VERSION}/install.sh" > "${installer}" - -echo "Checking hashsum ..." >&2 -[ "${HASHSUM}" = "$(f_sha256 ${installer})" ] -chmod +x "${installer}" - -echo "Launching installer ..." >&2 -exec "${installer}" -d -b "${GOBIN}" "${VERSION}" diff --git a/contrib/githooks/README.md b/contrib/githooks/README.md new file mode 100644 index 000000000000..83057835d49a --- /dev/null +++ b/contrib/githooks/README.md @@ -0,0 +1,21 @@ +# Git hooks + +Installation: + +``` +$ git config core.hooksPath contrib/githooks +``` + +## pre-commit + +The hook automatically runs `gofmt`, `goimports`, and `misspell` +to correctly format the `.go` files included in the commit, provided +that all the aforementioned commands are installed and available +in the user's search `$PATH` environment variable: + +``` +$ go get golang.org/x/tools/cmd/goimports +$ go get github.com/golangci/misspell/cmd/misspell@master +``` + +It also runs `go mod tidy` and `golangci-lint` if available. diff --git a/contrib/githooks/pre-commit b/contrib/githooks/pre-commit new file mode 100755 index 000000000000..2a9ad27af683 --- /dev/null +++ b/contrib/githooks/pre-commit @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e + +CMDS='git go gofmt goimports misspell' +STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go') + +f_echo_stderr() { + echo $@ >&2 +} + +f_exit_success() { + [ x"$@" != "x" ] && f_echo_stderr $@ || exit 0 +} +trap f_exit_success EXIT + +f_check_cmds() { + for cmd in ${CMDS}; do + which ${cmd} &>/dev/null || f_exit_success "couldn't find ${cmd}, skipping" + done +} + +f_check_cmds + +if [[ $STAGED_GO_FILES != "" ]]; then + f_echo_stderr "[pre-commit] fmt'ing staged files..." + for file in $STAGED_GO_FILES; do + if [[ $file =~ vendor/ ]] || [[ $file =~ client/docs/statik/ ]] || [[ $file =~ tests/mocks/ ]] || [[ $file =~ \.pb\.go ]]; then + continue + fi + + gofmt -w -s $file + misspell -w $file + goimports -w -local github.com/cosmos/cosmos-sdk $file + git add $file + + done +fi + +# Run go mod tidy +go mod tidy && git add go.mod go.sum diff --git a/contrib/images/Makefile b/contrib/images/Makefile new file mode 100644 index 000000000000..c0ec5240fdb8 --- /dev/null +++ b/contrib/images/Makefile @@ -0,0 +1,6 @@ +all: simd-env + +simd-env: + docker build --build-arg UID=$(shell id -u) --build-arg GID=$(shell id -g) --tag cosmossdk/simd-env simd-env + +.PHONY: all simd-env diff --git a/contrib/images/simd-env/Dockerfile b/contrib/images/simd-env/Dockerfile new file mode 100644 index 000000000000..d728812a2557 --- /dev/null +++ b/contrib/images/simd-env/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y libc6 curl jq file + +ARG UID=1000 +ARG GID=1000 + +USER ${UID}:${GID} +VOLUME /simd +WORKDIR /simd +EXPOSE 26656 26657 +ENTRYPOINT ["/usr/bin/wrapper.sh"] +CMD ["start", "--log_format", "plain"] +STOPSIGNAL SIGTERM + +COPY wrapper.sh /usr/bin/wrapper.sh diff --git a/contrib/images/simd-env/wrapper.sh b/contrib/images/simd-env/wrapper.sh new file mode 100755 index 000000000000..a2250098577c --- /dev/null +++ b/contrib/images/simd-env/wrapper.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +BINARY=/simd/${BINARY:-simd} +ID=${ID:-0} +LOG=${LOG:-simd.log} + +if ! [ -f "${BINARY}" ]; then + echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'simd'" + exit 1 +fi + +BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')" + +if [ -z "${BINARY_CHECK}" ]; then + echo "Binary needs to be OS linux, ARCH amd64" + exit 1 +fi + +export SIMDHOME="/simd/node${ID}/simd" + +if [ -d "$(dirname "${SIMDHOME}"/"${LOG}")" ]; then + "${BINARY}" --home "${SIMDHOME}" "$@" | tee "${SIMDHOME}/${LOG}" +else + "${BINARY}" --home "${SIMDHOME}" "$@" +fi diff --git a/contrib/localnet_liveness.sh b/contrib/localnet_liveness.sh new file mode 100755 index 000000000000..00c360b98dc7 --- /dev/null +++ b/contrib/localnet_liveness.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +CNT=0 +ITER=$1 +SLEEP=$2 +NUMBLOCKS=$3 +NODEADDR=$4 + +if [ -z "$1" ]; then + echo "Need to input number of iterations to run..." + exit 1 +fi + +if [ -z "$2" ]; then + echo "Need to input number of seconds to sleep between iterations" + exit 1 +fi + +if [ -z "$3" ]; then + echo "Need to input block height to declare completion..." + exit 1 +fi + +if [ -z "$4" ]; then + echo "Need to input node address to poll..." + exit 1 +fi + +docker_containers=( $(docker ps -q -f name=simd --format='{{.Names}}') ) + +while [ ${CNT} -lt $ITER ]; do + curr_block=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height') + + if [ ! -z ${curr_block} ] ; then + echo "Number of Blocks: ${curr_block}" + fi + + if [ ! -z ${curr_block} ] && [ ${curr_block} -gt ${NUMBLOCKS} ]; then + echo "Number of blocks reached. Success!" + exit 0 + fi + + # Emulate network chaos: + # + # Every 10 blocks, pick a random container and restart it. + if ! ((${CNT} % 10)); then + rand_container=${docker_containers["$[RANDOM % ${#docker_containers[@]}]"]}; + echo "Restarting random docker container ${rand_container}" + docker restart ${rand_container} &>/dev/null & + fi + let CNT=CNT+1 + sleep $SLEEP +done +echo "Timeout reached. Failure!" +exit 1 diff --git a/tests/test_cover.sh b/contrib/test_cover.sh similarity index 100% rename from tests/test_cover.sh rename to contrib/test_cover.sh diff --git a/cosmovisor/Makefile b/cosmovisor/Makefile new file mode 100644 index 000000000000..bd5b5c5430da --- /dev/null +++ b/cosmovisor/Makefile @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + + +all: cosmovisor test + +cosmovisor: + go build -mod=readonly ./cmd/cosmovisor + +test: + go test -mod=readonly -race ./... + +.PHONY: all cosmovisor test diff --git a/cosmovisor/README.md b/cosmovisor/README.md new file mode 100644 index 000000000000..3892efd0db2d --- /dev/null +++ b/cosmovisor/README.md @@ -0,0 +1,148 @@ +# 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/args.go b/cosmovisor/args.go new file mode 100644 index 000000000000..33fb62e0180f --- /dev/null +++ b/cosmovisor/args.go @@ -0,0 +1,136 @@ +package cosmovisor + +import ( + "errors" + "fmt" + "net/url" + "os" + "path/filepath" +) + +const ( + rootName = "cosmovisor" + genesisDir = "genesis" + upgradesDir = "upgrades" + currentLink = "current" +) + +// Config is the information passed in to control the daemon +type Config struct { + Home string + Name string + AllowDownloadBinaries bool + RestartAfterUpgrade bool +} + +// Root returns the root directory where all info lives +func (cfg *Config) Root() string { + return filepath.Join(cfg.Home, rootName) +} + +// GenesisBin is the path to the genesis binary - must be in place to start manager +func (cfg *Config) GenesisBin() string { + return filepath.Join(cfg.Root(), genesisDir, "bin", cfg.Name) +} + +// UpgradeBin is the path to the binary for the named upgrade +func (cfg *Config) UpgradeBin(upgradeName string) string { + return filepath.Join(cfg.UpgradeDir(upgradeName), "bin", cfg.Name) +} + +// UpgradeDir is the directory named upgrade +func (cfg *Config) UpgradeDir(upgradeName string) string { + safeName := url.PathEscape(upgradeName) + return filepath.Join(cfg.Root(), upgradesDir, safeName) +} + +// Symlink to genesis +func (cfg *Config) SymLinkToGenesis() (string, error) { + genesis := filepath.Join(cfg.Root(), genesisDir) + link := filepath.Join(cfg.Root(), currentLink) + + if err := os.Symlink(genesis, link); err != nil { + return "", err + } + // and return the genesis binary + return cfg.GenesisBin(), nil +} + +// CurrentBin is the path to the currently selected binary (genesis if no link is set) +// This will resolve the symlink to the underlying directory to make it easier to debug +func (cfg *Config) CurrentBin() (string, error) { + cur := filepath.Join(cfg.Root(), currentLink) + // if nothing here, fallback to genesis + info, err := os.Lstat(cur) + if err != nil { + //Create symlink to the genesis + return cfg.SymLinkToGenesis() + } + // if it is there, ensure it is a symlink + if info.Mode()&os.ModeSymlink == 0 { + //Create symlink to the genesis + return cfg.SymLinkToGenesis() + } + + // resolve it + dest, err := os.Readlink(cur) + if err != nil { + //Create symlink to the genesis + return cfg.SymLinkToGenesis() + } + + // and return the binary + return filepath.Join(dest, "bin", cfg.Name), nil +} + +// GetConfigFromEnv will read the environmental variables into a config +// and then validate it is reasonable +func GetConfigFromEnv() (*Config, error) { + cfg := &Config{ + Home: os.Getenv("DAEMON_HOME"), + Name: os.Getenv("DAEMON_NAME"), + } + + if os.Getenv("DAEMON_ALLOW_DOWNLOAD_BINARIES") == "true" { + cfg.AllowDownloadBinaries = true + } + + if os.Getenv("DAEMON_RESTART_AFTER_UPGRADE") == "true" { + cfg.RestartAfterUpgrade = true + } + + if err := cfg.validate(); err != nil { + return nil, err + } + + return cfg, nil +} + +// validate returns an error if this config is invalid. +// it enforces Home/cosmovisor is a valid directory and exists, +// and that Name is set +func (cfg *Config) validate() error { + if cfg.Name == "" { + return errors.New("DAEMON_NAME is not set") + } + + if cfg.Home == "" { + return errors.New("DAEMON_HOME is not set") + } + + if !filepath.IsAbs(cfg.Home) { + return errors.New("DAEMON_HOME must be an absolute path") + } + + // ensure the root directory exists + info, err := os.Stat(cfg.Root()) + if err != nil { + return fmt.Errorf("cannot stat home dir: %w", err) + } + + if !info.IsDir() { + return fmt.Errorf("%s is not a directory", info.Name()) + } + + return nil +} diff --git a/cosmovisor/args_test.go b/cosmovisor/args_test.go new file mode 100644 index 000000000000..9e0ce3bb04a5 --- /dev/null +++ b/cosmovisor/args_test.go @@ -0,0 +1,131 @@ +package cosmovisor + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/stretchr/testify/suite" +) + +type argsTestSuite struct { + suite.Suite +} + +func TestArgsTestSuite(t *testing.T) { + suite.Run(t, new(argsTestSuite)) +} + +func (s *argsTestSuite) TestConfigPaths() { + cases := map[string]struct { + cfg Config + upgradeName string + expectRoot string + expectGenesis string + expectUpgrade string + }{ + "simple": { + cfg: Config{Home: "/foo", Name: "myd"}, + upgradeName: "bar", + expectRoot: fmt.Sprintf("/foo/%s", rootName), + expectGenesis: fmt.Sprintf("/foo/%s/genesis/bin/myd", rootName), + expectUpgrade: fmt.Sprintf("/foo/%s/upgrades/bar/bin/myd", rootName), + }, + "handle space": { + cfg: Config{Home: "/longer/prefix/", Name: "yourd"}, + upgradeName: "some spaces", + expectRoot: fmt.Sprintf("/longer/prefix/%s", rootName), + expectGenesis: fmt.Sprintf("/longer/prefix/%s/genesis/bin/yourd", rootName), + expectUpgrade: "/longer/prefix/cosmovisor/upgrades/some%20spaces/bin/yourd", + }, + } + + for _, tc := range cases { + s.Require().Equal(tc.cfg.Root(), filepath.FromSlash(tc.expectRoot)) + s.Require().Equal(tc.cfg.GenesisBin(), filepath.FromSlash(tc.expectGenesis)) + s.Require().Equal(tc.cfg.UpgradeBin(tc.upgradeName), filepath.FromSlash(tc.expectUpgrade)) + } +} + +// Test validate +func (s *argsTestSuite) TestValidate() { + relPath := filepath.Join("testdata", "validate") + absPath, err := filepath.Abs(relPath) + s.Require().NoError(err) + + testdata, err := filepath.Abs("testdata") + s.Require().NoError(err) + + cases := map[string]struct { + cfg Config + valid bool + }{ + "happy": { + cfg: Config{Home: absPath, Name: "bind"}, + valid: true, + }, + "happy with download": { + cfg: Config{Home: absPath, Name: "bind", AllowDownloadBinaries: true}, + valid: true, + }, + "missing home": { + cfg: Config{Name: "bind"}, + valid: false, + }, + "missing name": { + cfg: Config{Home: absPath}, + valid: false, + }, + "relative path": { + cfg: Config{Home: relPath, Name: "bind"}, + valid: false, + }, + "no upgrade manager subdir": { + cfg: Config{Home: testdata, Name: "bind"}, + valid: false, + }, + "no such dir": { + cfg: Config{Home: filepath.FromSlash("/no/such/dir"), Name: "bind"}, + valid: false, + }, + } + + for _, tc := range cases { + err := tc.cfg.validate() + if tc.valid { + s.Require().NoError(err) + } else { + s.Require().Error(err) + } + } +} + +func (s *argsTestSuite) TestEnsureBin() { + relPath := filepath.Join("testdata", "validate") + absPath, err := filepath.Abs(relPath) + s.Require().NoError(err) + + cfg := Config{Home: absPath, Name: "dummyd"} + s.Require().NoError(cfg.validate()) + + s.Require().NoError(EnsureBinary(cfg.GenesisBin())) + + cases := map[string]struct { + upgrade string + hasBin bool + }{ + "proper": {"chain2", true}, + "no binary": {"nobin", false}, + "not executable": {"noexec", false}, + "no directory": {"foobarbaz", false}, + } + + for _, tc := range cases { + err := EnsureBinary(cfg.UpgradeBin(tc.upgrade)) + if tc.hasBin { + s.Require().NoError(err) + } else { + s.Require().Error(err) + } + } +} diff --git a/cosmovisor/cmd/cosmovisor/main.go b/cosmovisor/cmd/cosmovisor/main.go new file mode 100644 index 000000000000..a165acab38f6 --- /dev/null +++ b/cosmovisor/cmd/cosmovisor/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/cosmovisor" +) + +func main() { + if err := Run(os.Args[1:]); err != nil { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) + } +} + +// Run is the main loop, but returns an error +func Run(args []string) error { + cfg, err := cosmovisor.GetConfigFromEnv() + if err != nil { + return err + } + + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) + // if RestartAfterUpgrade, we launch after a successful upgrade (only condition LaunchProcess returns nil) + for cfg.RestartAfterUpgrade && err == nil && doUpgrade { + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) + } + return err +} diff --git a/cosmovisor/go.mod b/cosmovisor/go.mod new file mode 100644 index 000000000000..fe6be0cdd4e0 --- /dev/null +++ b/cosmovisor/go.mod @@ -0,0 +1,9 @@ +module github.com/cosmos/cosmos-sdk/cosmovisor + +go 1.14 + +require ( + github.com/hashicorp/go-getter v1.4.1 + github.com/otiai10/copy v1.2.0 + github.com/stretchr/testify v1.6.1 +) diff --git a/cosmovisor/go.sum b/cosmovisor/go.sum new file mode 100644 index 000000000000..4506c2a5a19d --- /dev/null +++ b/cosmovisor/go.sum @@ -0,0 +1,170 @@ +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= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +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/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY= +github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/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/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +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= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA= +github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +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= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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/sys v0.0.0-20180830151530-49385e6e1522/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-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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/tools v0.0.0-20180917221912-90fa682c2a6e/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= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +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-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= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +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-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= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/cosmovisor/process.go b/cosmovisor/process.go new file mode 100644 index 000000000000..cfd201e2be99 --- /dev/null +++ b/cosmovisor/process.go @@ -0,0 +1,142 @@ +package cosmovisor + +import ( + "bufio" + "fmt" + "io" + "log" + "os" + "os/exec" + "os/signal" + "strings" + "sync" + "syscall" +) + +// LaunchProcess runs a subprocess and returns when the subprocess exits, +// either when it dies, or *after* a successful upgrade. +func LaunchProcess(cfg *Config, args []string, stdout, stderr io.Writer) (bool, error) { + bin, err := cfg.CurrentBin() + if err != nil { + return false, fmt.Errorf("error creating symlink to genesis: %w", err) + } + + if err := EnsureBinary(bin); err != nil { + return false, fmt.Errorf("current binary invalid: %w", err) + } + + cmd := exec.Command(bin, args...) + outpipe, err := cmd.StdoutPipe() + if err != nil { + return false, err + } + + errpipe, err := cmd.StderrPipe() + if err != nil { + return false, err + } + + scanOut := bufio.NewScanner(io.TeeReader(outpipe, stdout)) + scanErr := bufio.NewScanner(io.TeeReader(errpipe, stderr)) + + if err := cmd.Start(); err != nil { + return false, fmt.Errorf("launching process %s %s: %w", bin, strings.Join(args, " "), err) + } + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGQUIT, syscall.SIGTERM) + go func() { + sig := <-sigs + if err := cmd.Process.Signal(sig); err != nil { + log.Fatal(err) + } + }() + + // three ways to exit - command ends, find regexp in scanOut, find regexp in scanErr + upgradeInfo, err := WaitForUpgradeOrExit(cmd, scanOut, scanErr) + if err != nil { + return false, err + } + + if upgradeInfo != nil { + return true, DoUpgrade(cfg, upgradeInfo) + } + + return false, nil +} + +// WaitResult is used to wrap feedback on cmd state with some mutex logic. +// This is needed as multiple go-routines can affect this - two read pipes that can trigger upgrade +// As well as the command, which can fail +type WaitResult struct { + // both err and info may be updated from several go-routines + // access is wrapped by mutex and should only be done through methods + err error + info *UpgradeInfo + mutex sync.Mutex +} + +// AsResult reads the data protected by mutex to avoid race conditions +func (u *WaitResult) AsResult() (*UpgradeInfo, error) { + u.mutex.Lock() + defer u.mutex.Unlock() + return u.info, u.err +} + +// SetError will set with the first error using a mutex +// don't set it once info is set, that means we chose to kill the process +func (u *WaitResult) SetError(myErr error) { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.info == nil && myErr != nil { + u.err = myErr + } +} + +// SetUpgrade sets first non-nil upgrade info, ensure error is then nil +// pass in a command to shutdown on successful upgrade +func (u *WaitResult) SetUpgrade(up *UpgradeInfo) { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.info == nil && up != nil { + u.info = up + u.err = nil + } +} + +// WaitForUpgradeOrExit listens to both output streams of the process, as well as the process state itself +// When it returns, the process is finished and all streams have closed. +// +// It returns (info, nil) if an upgrade should be initiated (and we killed the process) +// It returns (nil, err) if the process died by itself, or there was an issue reading the pipes +// It returns (nil, nil) if the process exited normally without triggering an upgrade. This is very unlikely +// to happened with "start" but may happened with short-lived commands like `gaiad export ...` +func WaitForUpgradeOrExit(cmd *exec.Cmd, scanOut, scanErr *bufio.Scanner) (*UpgradeInfo, error) { + var res WaitResult + + waitScan := func(scan *bufio.Scanner) { + upgrade, err := WaitForUpdate(scan) + if err != nil { + res.SetError(err) + } else if upgrade != nil { + res.SetUpgrade(upgrade) + // now we need to kill the process + _ = cmd.Process.Kill() + } + } + + // wait for the scanners, which can trigger upgrade and kill cmd + go waitScan(scanOut) + go waitScan(scanErr) + + // if the command exits normally (eg. short command like `gaiad version`), just return (nil, nil) + // we often get broken read pipes if it runs too fast. + // if we had upgrade info, we would have killed it, and thus got a non-nil error code + err := cmd.Wait() + if err == nil { + return nil, nil + } + // this will set the error code if it wasn't killed due to upgrade + res.SetError(err) + return res.AsResult() +} diff --git a/cosmovisor/process_test.go b/cosmovisor/process_test.go new file mode 100644 index 000000000000..6dc964f21ee0 --- /dev/null +++ b/cosmovisor/process_test.go @@ -0,0 +1,114 @@ +// +build linux + +package cosmovisor_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/cosmovisor" +) + +type processTestSuite struct { + suite.Suite +} + +func TestProcessTestSuite(t *testing.T) { + suite.Run(t, new(processTestSuite)) +} + +// TestLaunchProcess will try running the script a few times and watch upgrades work properly +// and args are passed through +func (s *processTestSuite) TestLaunchProcess() { + home := copyTestData(s.T(), "validate") + cfg := &cosmovisor.Config{Home: home, Name: "dummyd"} + + // should run the genesis binary and produce expected output + var stdout, stderr bytes.Buffer + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.GenesisBin(), currentBin) + + args := []string{"foo", "bar", "1234"} + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Genesis foo bar 1234\nUPGRADE \"chain2\" NEEDED at height: 49: {}\n", stdout.String()) + + // ensure this is upgraded now and produces new output + + currentBin, err = cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) + args = []string{"second", "run", "--verbose"} + stdout.Reset() + stderr.Reset() + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().False(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Chain 2 is live!\nArgs: second run --verbose\nFinished successfully\n", stdout.String()) + + // ended without other upgrade + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) +} + +// TestLaunchProcess will try running the script a few times and watch upgrades work properly +// and args are passed through +func (s *processTestSuite) TestLaunchProcessWithDownloads() { + // this is a fun path + // genesis -> "chain2" = zip_binary + // zip_binary -> "chain3" = ref_zipped -> zip_directory + // zip_directory no upgrade + home := copyTestData(s.T(), "download") + cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true} + + // should run the genesis binary and produce expected output + var stdout, stderr bytes.Buffer + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.GenesisBin(), currentBin) + args := []string{"some", "args"} + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Preparing auto-download some args\n"+`ERROR: UPGRADE "chain2" NEEDED at height: 49: {"binaries":{"linux/amd64":"https://github.com/cosmos/cosmos-sdk/raw/51249cb93130810033408934454841c98423ed4b/cosmovisor/testdata/repo/zip_binary/autod.zip?checksum=sha256:dc48829b4126ae95bc0db316c66d4e9da5f3db95e212665b6080638cca77e998"}} module=main`+"\n", stdout.String()) + + // ensure this is upgraded now and produces new output + currentBin, err = cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) + args = []string{"run", "--fast"} + stdout.Reset() + stderr.Reset() + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Chain 2 from zipped binary link to referral\nArgs: run --fast\n"+`ERROR: UPGRADE "chain3" NEEDED at height: 936: https://github.com/cosmos/cosmos-sdk/raw/0eae1a50612b8bf803336d35055896fbddaa1ddd/cosmovisor/testdata/repo/ref_zipped?checksum=sha256:0a428575de718ed3cf0771c9687eefaf6f19359977eca4d94a0abd0e11ef8e64 module=main`+"\n", stdout.String()) + + // ended with one more upgrade + currentBin, err = cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain3"), currentBin) + // make sure this is the proper binary now.... + args = []string{"end", "--halt"} + stdout.Reset() + stderr.Reset() + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().False(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Chain 2 from zipped directory\nArgs: end --halt\n", stdout.String()) + + // and this doesn't upgrade + currentBin, err = cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain3"), currentBin) +} diff --git a/cosmovisor/scanner.go b/cosmovisor/scanner.go new file mode 100644 index 000000000000..81bca0717a4e --- /dev/null +++ b/cosmovisor/scanner.go @@ -0,0 +1,42 @@ +package cosmovisor + +import ( + "bufio" + "regexp" +) + +// Trim off whitespace around the info - match least greedy, grab as much space on both sides +// Defined here: https://github.com/cosmos/cosmos-sdk/blob/release/v0.38.2/x/upgrade/abci.go#L38 +// fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info) +// DueAt defined here: https://github.com/cosmos/cosmos-sdk/blob/release/v0.38.2/x/upgrade/internal/types/plan.go#L73-L78 +// +// if !p.Time.IsZero() { +// return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) +// } +// return fmt.Sprintf("height: %d", p.Height) +var upgradeRegex = regexp.MustCompile(`UPGRADE "(.*)" NEEDED at ((height): (\d+)|(time): (\S+)):\s+(\S*)`) + +// UpgradeInfo is the details from the regexp +type UpgradeInfo struct { + Name string + Info string +} + +// WaitForUpdate will listen to the scanner until a line matches upgradeRegexp. +// It returns (info, nil) on a matching line +// It returns (nil, err) if the input stream errored +// It returns (nil, nil) if the input closed without ever matching the regexp +func WaitForUpdate(scanner *bufio.Scanner) (*UpgradeInfo, error) { + for scanner.Scan() { + line := scanner.Text() + if upgradeRegex.MatchString(line) { + subs := upgradeRegex.FindStringSubmatch(line) + info := UpgradeInfo{ + Name: subs[1], + Info: subs[7], + } + return &info, nil + } + } + return nil, scanner.Err() +} diff --git a/cosmovisor/scanner_test.go b/cosmovisor/scanner_test.go new file mode 100644 index 000000000000..9e42410c7be4 --- /dev/null +++ b/cosmovisor/scanner_test.go @@ -0,0 +1,63 @@ +package cosmovisor_test + +import ( + "bufio" + "io" + "testing" + + "github.com/cosmos/cosmos-sdk/cosmovisor" + + "github.com/stretchr/testify/require" +) + +func TestWaitForInfo(t *testing.T) { + cases := map[string]struct { + write []string + expectUpgrade *cosmovisor.UpgradeInfo + expectErr bool + }{ + "no match": { + write: []string{"some", "random\ninfo\n"}, + }, + "match name with no info": { + write: []string{"first line\n", `UPGRADE "myname" NEEDED at height: 123: `, "\nnext line\n"}, + expectUpgrade: &cosmovisor.UpgradeInfo{ + Name: "myname", + Info: "", + }, + }, + "match name with info": { + write: []string{"first line\n", `UPGRADE "take2" NEEDED at height: 123: DownloadData here!`, "\nnext line\n"}, + expectUpgrade: &cosmovisor.UpgradeInfo{ + Name: "take2", + Info: "DownloadData", + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r, w := io.Pipe() + scan := bufio.NewScanner(r) + + // write all info in separate routine + go func() { + for _, line := range tc.write { + n, err := w.Write([]byte(line)) + require.NoError(t, err) + require.Equal(t, len(line), n) + } + w.Close() + }() + + // now scan the info + info, err := cosmovisor.WaitForUpdate(scan) + if tc.expectErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.expectUpgrade, info) + }) + } +} diff --git a/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod b/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod new file mode 100755 index 000000000000..113cce7f797f --- /dev/null +++ b/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod @@ -0,0 +1,7 @@ +#!/bin/sh + +echo Preparing auto-download $@ +sleep 1 +echo 'ERROR: UPGRADE "chain2" NEEDED at height: 49: {"binaries":{"linux/amd64":"https://github.com/cosmos/cosmos-sdk/raw/51249cb93130810033408934454841c98423ed4b/cosmovisor/testdata/repo/zip_binary/autod.zip?checksum=sha256:dc48829b4126ae95bc0db316c66d4e9da5f3db95e212665b6080638cca77e998"}} module=main' +sleep 4 +echo Never should be printed!!! diff --git a/cosmovisor/testdata/repo/raw_binary/autod b/cosmovisor/testdata/repo/raw_binary/autod new file mode 100755 index 000000000000..0022b84af292 --- /dev/null +++ b/cosmovisor/testdata/repo/raw_binary/autod @@ -0,0 +1,6 @@ +#!/bin/sh + +echo Chain 2 is live! +echo Args: $@ +sleep 1 +echo Finished successfully diff --git a/cosmovisor/testdata/repo/ref_zipped b/cosmovisor/testdata/repo/ref_zipped new file mode 100644 index 000000000000..fd63c7161f81 --- /dev/null +++ b/cosmovisor/testdata/repo/ref_zipped @@ -0,0 +1,5 @@ +{ + "binaries": { + "linux/amd64": "https://github.com/cosmos/cosmos-sdk/raw/aa5d6140ad4011bb33d472dca8246a0dcbe223ee/cosmovisor/testdata/repo/zip_directory/autod.zip?checksum=sha256:3784e4574cad69b67e34d4ea4425eff140063a3870270a301d6bb24a098a27ae" + } +} \ No newline at end of file diff --git a/cosmovisor/testdata/repo/zip_binary/autod b/cosmovisor/testdata/repo/zip_binary/autod new file mode 100755 index 000000000000..4ed1dea36de4 --- /dev/null +++ b/cosmovisor/testdata/repo/zip_binary/autod @@ -0,0 +1,8 @@ +#!/bin/sh + +echo Chain 2 from zipped binary link to referral +echo Args: $@ +# note that we just have a url (follow the ref), not a full link +echo 'ERROR: UPGRADE "chain3" NEEDED at height: 936: https://github.com/cosmos/cosmos-sdk/raw/0eae1a50612b8bf803336d35055896fbddaa1ddd/cosmovisor/testdata/repo/ref_zipped?checksum=sha256:0a428575de718ed3cf0771c9687eefaf6f19359977eca4d94a0abd0e11ef8e64 module=main' +sleep 4 +echo 'Do not print' diff --git a/cosmovisor/testdata/repo/zip_binary/autod.zip b/cosmovisor/testdata/repo/zip_binary/autod.zip new file mode 100644 index 000000000000..0fe45f18f625 Binary files /dev/null and b/cosmovisor/testdata/repo/zip_binary/autod.zip differ diff --git a/cosmovisor/testdata/repo/zip_directory/autod.zip b/cosmovisor/testdata/repo/zip_directory/autod.zip new file mode 100644 index 000000000000..225cd4672a78 Binary files /dev/null and b/cosmovisor/testdata/repo/zip_directory/autod.zip differ diff --git a/cosmovisor/testdata/repo/zip_directory/bin/autod b/cosmovisor/testdata/repo/zip_directory/bin/autod new file mode 100755 index 000000000000..ea3a6f8311a4 --- /dev/null +++ b/cosmovisor/testdata/repo/zip_directory/bin/autod @@ -0,0 +1,4 @@ +#!/bin/sh + +echo Chain 2 from zipped directory +echo Args: $@ diff --git a/cosmovisor/testdata/validate/cosmovisor/genesis/bin/dummyd b/cosmovisor/testdata/validate/cosmovisor/genesis/bin/dummyd new file mode 100755 index 000000000000..c240b802a0be --- /dev/null +++ b/cosmovisor/testdata/validate/cosmovisor/genesis/bin/dummyd @@ -0,0 +1,7 @@ +#!/bin/sh + +echo Genesis $@ +sleep 1 +echo 'UPGRADE "chain2" NEEDED at height: 49: {}' +sleep 2 +echo Never should be printed!!! diff --git a/cosmovisor/testdata/validate/cosmovisor/upgrades/chain2/bin/dummyd b/cosmovisor/testdata/validate/cosmovisor/upgrades/chain2/bin/dummyd new file mode 100755 index 000000000000..0022b84af292 --- /dev/null +++ b/cosmovisor/testdata/validate/cosmovisor/upgrades/chain2/bin/dummyd @@ -0,0 +1,6 @@ +#!/bin/sh + +echo Chain 2 is live! +echo Args: $@ +sleep 1 +echo Finished successfully diff --git a/cosmovisor/testdata/validate/cosmovisor/upgrades/chain3/bin/dummyd b/cosmovisor/testdata/validate/cosmovisor/upgrades/chain3/bin/dummyd new file mode 100755 index 000000000000..edfb6c403ed8 --- /dev/null +++ b/cosmovisor/testdata/validate/cosmovisor/upgrades/chain3/bin/dummyd @@ -0,0 +1,6 @@ +#!/bin/sh + +echo Chain 3 finally! +echo Args: $@ +sleep 1 +echo Finished successfully diff --git a/cosmovisor/testdata/validate/cosmovisor/upgrades/nobin/bin/.keep b/cosmovisor/testdata/validate/cosmovisor/upgrades/nobin/bin/.keep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cosmovisor/testdata/validate/cosmovisor/upgrades/noexec/bin/dummyd b/cosmovisor/testdata/validate/cosmovisor/upgrades/noexec/bin/dummyd new file mode 100644 index 000000000000..9fa65cddebd0 --- /dev/null +++ b/cosmovisor/testdata/validate/cosmovisor/upgrades/noexec/bin/dummyd @@ -0,0 +1,3 @@ +#!/bin/sh + +echo 'exec flag not set' diff --git a/cosmovisor/upgrade.go b/cosmovisor/upgrade.go new file mode 100644 index 000000000000..3057597f7df1 --- /dev/null +++ b/cosmovisor/upgrade.go @@ -0,0 +1,185 @@ +package cosmovisor + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/hashicorp/go-getter" +) + +// DoUpgrade will be called after the log message has been parsed and the process has terminated. +// We can now make any changes to the underlying directory without interference and leave it +// in a state, so we can make a proper restart +func DoUpgrade(cfg *Config, info *UpgradeInfo) error { + // Simplest case is to switch the link + err := EnsureBinary(cfg.UpgradeBin(info.Name)) + if err == nil { + // we have the binary - do it + return cfg.SetCurrentUpgrade(info.Name) + } + // if auto-download is disabled, we fail + if !cfg.AllowDownloadBinaries { + return fmt.Errorf("binary not present, downloading disabled: %w", err) + } + + // if the dir is there already, don't download either + if _, err := os.Stat(cfg.UpgradeDir(info.Name)); !os.IsNotExist(err) { + return errors.New("upgrade dir already exists, won't overwrite") + } + + // If not there, then we try to download it... maybe + if err := DownloadBinary(cfg, info); err != nil { + return fmt.Errorf("cannot download binary: %w", err) + } + + // and then set the binary again + if err := EnsureBinary(cfg.UpgradeBin(info.Name)); err != nil { + return fmt.Errorf("downloaded binary doesn't check out: %w", err) + } + + return cfg.SetCurrentUpgrade(info.Name) +} + +// DownloadBinary will grab the binary and place it in the proper directory +func DownloadBinary(cfg *Config, info *UpgradeInfo) error { + url, err := GetDownloadURL(info) + if err != nil { + return err + } + + // download into the bin dir (works for one file) + binPath := cfg.UpgradeBin(info.Name) + err = getter.GetFile(binPath, url) + + // if this fails, let's see if it is a zipped directory + if err != nil { + dirPath := cfg.UpgradeDir(info.Name) + err = getter.Get(dirPath, url) + } + if err != nil { + return err + } + // if it is successful, let's ensure the binary is executable + return MarkExecutable(binPath) +} + +// MarkExecutable will try to set the executable bits if not already set +// Fails if file doesn't exist or we cannot set those bits +func MarkExecutable(path string) error { + info, err := os.Stat(path) + if err != nil { + return fmt.Errorf("stating binary: %w", err) + } + // end early if world exec already set + if info.Mode()&0001 == 1 { + return nil + } + // now try to set all exec bits + newMode := info.Mode().Perm() | 0111 + return os.Chmod(path, newMode) +} + +// UpgradeConfig is expected format for the info field to allow auto-download +type UpgradeConfig struct { + Binaries map[string]string `json:"binaries"` +} + +// GetDownloadURL will check if there is an arch-dependent binary specified in Info +func GetDownloadURL(info *UpgradeInfo) (string, error) { + doc := strings.TrimSpace(info.Info) + // if this is a url, then we download that and try to get a new doc with the real info + if _, err := url.Parse(doc); err == nil { + tmpDir, err := ioutil.TempDir("", "upgrade-manager-reference") + if err != nil { + return "", fmt.Errorf("create tempdir for reference file: %w", err) + } + defer os.RemoveAll(tmpDir) + + refPath := filepath.Join(tmpDir, "ref") + if err := getter.GetFile(refPath, doc); err != nil { + return "", fmt.Errorf("downloading reference link %s: %w", doc, err) + } + + refBytes, err := ioutil.ReadFile(refPath) + if err != nil { + return "", fmt.Errorf("reading downloaded reference: %w", err) + } + // if download worked properly, then we use this new file as the binary map to parse + doc = string(refBytes) + } + + // check if it is the upgrade config + var config UpgradeConfig + + if err := json.Unmarshal([]byte(doc), &config); err == nil { + url, ok := config.Binaries[OSArch()] + if !ok { + url, ok = config.Binaries["any"] + } + if !ok { + return "", fmt.Errorf("cannot find binary for os/arch: neither %s, nor any", OSArch()) + } + + return url, nil + } + + return "", errors.New("upgrade info doesn't contain binary map") +} + +func OSArch() string { + return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) +} + +// SetCurrentUpgrade sets the named upgrade to be the current link, returns error if this binary doesn't exist +func (cfg *Config) SetCurrentUpgrade(upgradeName string) error { + // ensure named upgrade exists + bin := cfg.UpgradeBin(upgradeName) + + if err := EnsureBinary(bin); err != nil { + return err + } + + // set a symbolic link + link := filepath.Join(cfg.Root(), currentLink) + safeName := url.PathEscape(upgradeName) + upgrade := filepath.Join(cfg.Root(), upgradesDir, safeName) + + // remove link if it exists + if _, err := os.Stat(link); err == nil { + os.Remove(link) + } + + // point to the new directory + if err := os.Symlink(upgrade, link); err != nil { + return fmt.Errorf("creating current symlink: %w", err) + } + + return nil +} + +// EnsureBinary ensures the file exists and is executable, or returns an error +func EnsureBinary(path string) error { + info, err := os.Stat(path) + if err != nil { + return fmt.Errorf("cannot stat dir %s: %w", path, err) + } + + if !info.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", info.Name()) + } + + // this checks if the world-executable bit is set (we cannot check owner easily) + exec := info.Mode().Perm() & 0001 + if exec == 0 { + return fmt.Errorf("%s is not world executable", info.Name()) + } + + return nil +} diff --git a/cosmovisor/upgrade_test.go b/cosmovisor/upgrade_test.go new file mode 100644 index 000000000000..2123df8e4035 --- /dev/null +++ b/cosmovisor/upgrade_test.go @@ -0,0 +1,279 @@ +// +build linux + +package cosmovisor_test + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/otiai10/copy" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/cosmovisor" +) + +type upgradeTestSuite struct { + suite.Suite +} + +func TestUpgradeTestSuite(t *testing.T) { + suite.Run(t, new(upgradeTestSuite)) +} + +func (s *upgradeTestSuite) TestCurrentBin() { + home := copyTestData(s.T(), "validate") + cfg := cosmovisor.Config{Home: home, Name: "dummyd"} + + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.GenesisBin(), currentBin) + + // ensure we cannot set this to an invalid value + for _, name := range []string{"missing", "nobin", "noexec"} { + s.Require().Error(cfg.SetCurrentUpgrade(name), name) + + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.GenesisBin(), currentBin, name) + } + + // try a few times to make sure this can be reproduced + for _, upgrade := range []string{"chain2", "chain3", "chain2"} { + // now set it to a valid upgrade and make sure CurrentBin is now set properly + err = cfg.SetCurrentUpgrade(upgrade) + s.Require().NoError(err) + // we should see current point to the new upgrade dir + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.UpgradeBin(upgrade), currentBin) + } +} + +func (s *upgradeTestSuite) TestCurrentAlwaysSymlinkToDirectory() { + home := copyTestData(s.T(), "validate") + cfg := cosmovisor.Config{Home: home, Name: "dummyd"} + + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.GenesisBin(), currentBin) + s.assertCurrentLink(cfg, "genesis") + + err = cfg.SetCurrentUpgrade("chain2") + s.Require().NoError(err) + currentBin, err = cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) + s.assertCurrentLink(cfg, filepath.Join("upgrades", "chain2")) +} + +func (s *upgradeTestSuite) assertCurrentLink(cfg cosmovisor.Config, target string) { + link := filepath.Join(cfg.Root(), "current") + // ensure this is a symlink + info, err := os.Lstat(link) + s.Require().NoError(err) + s.Require().Equal(os.ModeSymlink, info.Mode()&os.ModeSymlink) + + dest, err := os.Readlink(link) + s.Require().NoError(err) + expected := filepath.Join(cfg.Root(), target) + s.Require().Equal(expected, dest) +} + +// TODO: test with download (and test all download functions) +func (s *upgradeTestSuite) TestDoUpgradeNoDownloadUrl() { + home := copyTestData(s.T(), "validate") + cfg := &cosmovisor.Config{Home: home, Name: "dummyd", AllowDownloadBinaries: true} + + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(cfg.GenesisBin(), currentBin) + + // do upgrade ignores bad files + for _, name := range []string{"missing", "nobin", "noexec"} { + info := &cosmovisor.UpgradeInfo{Name: name} + err = cosmovisor.DoUpgrade(cfg, info) + s.Require().Error(err, name) + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + s.Require().Equal(cfg.GenesisBin(), currentBin, name) + } + + // make sure it updates a few times + for _, upgrade := range []string{"chain2", "chain3"} { + // now set it to a valid upgrade and make sure CurrentBin is now set properly + info := &cosmovisor.UpgradeInfo{Name: upgrade} + err = cosmovisor.DoUpgrade(cfg, info) + s.Require().NoError(err) + // we should see current point to the new upgrade dir + upgradeBin := cfg.UpgradeBin(upgrade) + currentBin, err := cfg.CurrentBin() + s.Require().NoError(err) + + s.Require().Equal(upgradeBin, currentBin) + } +} + +func (s *upgradeTestSuite) TestOsArch() { + // all download tests will fail if we are not on linux... + s.Require().Equal("linux/amd64", cosmovisor.OSArch()) +} + +func (s *upgradeTestSuite) TestGetDownloadURL() { + // all download tests will fail if we are not on linux... + ref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/ref_zipped")) + s.Require().NoError(err) + badref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/zip_binary/autod.zip")) + s.Require().NoError(err) + + cases := map[string]struct { + info string + url string + isErr bool + }{ + "missing": { + isErr: true, + }, + "follow reference": { + info: ref, + url: "https://github.com/cosmos/cosmos-sdk/raw/aa5d6140ad4011bb33d472dca8246a0dcbe223ee/cosmovisor/testdata/repo/zip_directory/autod.zip?checksum=sha256:3784e4574cad69b67e34d4ea4425eff140063a3870270a301d6bb24a098a27ae", + }, + "malformated reference target": { + info: badref, + isErr: true, + }, + "missing link": { + info: "https://no.such.domain/exists.txt", + isErr: true, + }, + "proper binary": { + info: `{"binaries": {"linux/amd64": "https://foo.bar/", "windows/amd64": "https://something.else"}}`, + url: "https://foo.bar/", + }, + "any architecture not used": { + info: `{"binaries": {"linux/amd64": "https://foo.bar/", "*": "https://something.else"}}`, + url: "https://foo.bar/", + }, + "any architecture used": { + info: `{"binaries": {"linux/arm": "https://foo.bar/arm-only", "any": "https://foo.bar/portable"}}`, + url: "https://foo.bar/portable", + }, + "missing binary": { + info: `{"binaries": {"linux/arm": "https://foo.bar/"}}`, + isErr: true, + }, + } + + for _, tc := range cases { + url, err := cosmovisor.GetDownloadURL(&cosmovisor.UpgradeInfo{Info: tc.info}) + if tc.isErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.url, url) + } + } +} + +func (s *upgradeTestSuite) TestDownloadBinary() { + cases := map[string]struct { + url string + canDownload bool + validBinary bool + }{ + "get raw binary": { + url: "./testdata/repo/raw_binary/autod", + canDownload: true, + validBinary: true, + }, + "get raw binary with checksum": { + // sha256sum ./testdata/repo/raw_binary/autod + url: "./testdata/repo/raw_binary/autod?checksum=sha256:e6bc7851600a2a9917f7bf88eb7bdee1ec162c671101485690b4deb089077b0d", + canDownload: true, + validBinary: true, + }, + "get raw binary with invalid checksum": { + url: "./testdata/repo/raw_binary/autod?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906", + canDownload: false, + }, + "get zipped directory": { + url: "./testdata/repo/zip_directory/autod.zip", + canDownload: true, + validBinary: true, + }, + "get zipped directory with valid checksum": { + // sha256sum ./testdata/repo/zip_directory/autod.zip + url: "./testdata/repo/zip_directory/autod.zip?checksum=sha256:3784e4574cad69b67e34d4ea4425eff140063a3870270a301d6bb24a098a27ae", + canDownload: true, + validBinary: true, + }, + "get zipped directory with invalid checksum": { + url: "./testdata/repo/zip_directory/autod.zip?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906", + canDownload: false, + }, + "invalid url": { + url: "./testdata/repo/bad_dir/autod", + canDownload: false, + }, + } + + for _, tc := range cases { + var err error + // make temp dir + home := copyTestData(s.T(), "download") + + cfg := &cosmovisor.Config{ + Home: home, + Name: "autod", + AllowDownloadBinaries: true, + } + + // if we have a relative path, make it absolute, but don't change eg. https://... urls + url := tc.url + if strings.HasPrefix(url, "./") { + url, err = filepath.Abs(url) + s.Require().NoError(err) + } + + upgrade := "amazonas" + info := &cosmovisor.UpgradeInfo{ + Name: upgrade, + Info: fmt.Sprintf(`{"binaries":{"%s": "%s"}}`, cosmovisor.OSArch(), url), + } + + err = cosmovisor.DownloadBinary(cfg, info) + if !tc.canDownload { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + err = cosmovisor.EnsureBinary(cfg.UpgradeBin(upgrade)) + if tc.validBinary { + s.Require().NoError(err) + } else { + s.Require().Error(err) + } + } +} + +// copyTestData will make a tempdir and then +// "cp -r" a subdirectory under testdata there +// returns the directory (which can now be used as Config.Home) and modified safely +func copyTestData(t *testing.T, subdir string) string { + t.Helper() + + tmpdir := t.TempDir() + + require.NoError(t, copy.Copy(filepath.Join("testdata", subdir), tmpdir)) + + return tmpdir +} diff --git a/crypto/amino.go b/crypto/amino.go deleted file mode 100644 index 553a4723f9db..000000000000 --- a/crypto/amino.go +++ /dev/null @@ -1,19 +0,0 @@ -package crypto - -import ( - amino "github.com/tendermint/go-amino" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" -) - -var cdc = amino.NewCodec() - -func init() { - RegisterAmino(cdc) - cryptoAmino.RegisterAmino(cdc) -} - -// RegisterAmino registers all go-crypto related types in the given (amino) codec. -func RegisterAmino(cdc *amino.Codec) { - cdc.RegisterConcrete(PrivKeyLedgerSecp256k1{}, - "tendermint/PrivKeyLedgerSecp256k1", nil) -} diff --git a/crypto/armor.go b/crypto/armor.go new file mode 100644 index 000000000000..35deb107798f --- /dev/null +++ b/crypto/armor.go @@ -0,0 +1,209 @@ +package crypto + +import ( + "encoding/hex" + "fmt" + + "github.com/tendermint/crypto/bcrypt" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/armor" + "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + blockTypePrivKey = "TENDERMINT PRIVATE KEY" + blockTypeKeyInfo = "TENDERMINT KEY INFO" + blockTypePubKey = "TENDERMINT PUBLIC KEY" + + defaultAlgo = "secp256k1" + + headerVersion = "version" + headerType = "type" +) + +// BcryptSecurityParameter is security parameter var, and it can be changed within the lcd test. +// Making the bcrypt security parameter a var shouldn't be a security issue: +// One can't verify an invalid key by maliciously changing the bcrypt +// parameter during a runtime vulnerability. The main security +// threat this then exposes would be something that changes this during +// runtime before the user creates their key. This vulnerability must +// succeed to update this to that same value before every subsequent call +// to the keys command in future startups / or the attacker must get access +// to the filesystem. However, with a similar threat model (changing +// variables in runtime), one can cause the user to sign a different tx +// than what they see, which is a significantly cheaper attack then breaking +// a bcrypt hash. (Recall that the nonce still exists to break rainbow tables) +// For further notes on security parameter choice, see README.md +var BcryptSecurityParameter = 12 + +//----------------------------------------------------------------- +// add armor + +// Armor the InfoBytes +func ArmorInfoBytes(bz []byte) string { + header := map[string]string{ + headerType: "Info", + headerVersion: "0.0.0", + } + + return armor.EncodeArmor(blockTypeKeyInfo, header, bz) +} + +// Armor the PubKeyBytes +func ArmorPubKeyBytes(bz []byte, algo string) string { + header := map[string]string{ + headerVersion: "0.0.1", + } + if algo != "" { + header[headerType] = algo + } + + return armor.EncodeArmor(blockTypePubKey, header, bz) +} + +//----------------------------------------------------------------- +// remove armor + +// Unarmor the InfoBytes +func UnarmorInfoBytes(armorStr string) ([]byte, error) { + bz, header, err := unarmorBytes(armorStr, blockTypeKeyInfo) + if err != nil { + return nil, err + } + + if header[headerVersion] != "0.0.0" { + return nil, fmt.Errorf("unrecognized version: %v", header[headerVersion]) + } + + return bz, nil +} + +// UnarmorPubKeyBytes returns the pubkey byte slice, a string of the algo type, and an error +func UnarmorPubKeyBytes(armorStr string) (bz []byte, algo string, err error) { + bz, header, err := unarmorBytes(armorStr, blockTypePubKey) + if err != nil { + return nil, "", fmt.Errorf("couldn't unarmor bytes: %v", err) + } + + switch header[headerVersion] { + case "0.0.0": + return bz, defaultAlgo, err + case "0.0.1": + if header[headerType] == "" { + header[headerType] = defaultAlgo + } + + return bz, header[headerType], err + case "": + return nil, "", fmt.Errorf("header's version field is empty") + default: + err = fmt.Errorf("unrecognized version: %v", header[headerVersion]) + return nil, "", err + } +} + +func unarmorBytes(armorStr, blockType string) (bz []byte, header map[string]string, err error) { + bType, header, bz, err := armor.DecodeArmor(armorStr) + if err != nil { + return + } + + if bType != blockType { + err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType) + return + } + + return +} + +//----------------------------------------------------------------- +// encrypt/decrypt with armor + +// Encrypt and armor the private key. +func EncryptArmorPrivKey(privKey cryptotypes.PrivKey, passphrase string, algo string) string { + saltBytes, encBytes := encryptPrivKey(privKey, passphrase) + header := map[string]string{ + "kdf": "bcrypt", + "salt": fmt.Sprintf("%X", saltBytes), + } + + if algo != "" { + header[headerType] = algo + } + + armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes) + + return armorStr +} + +// encrypt the given privKey with the passphrase using a randomly +// generated salt and the xsalsa20 cipher. returns the salt and the +// encrypted priv key. +func encryptPrivKey(privKey cryptotypes.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { + saltBytes = crypto.CRandBytes(16) + key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter) + + if err != nil { + panic(sdkerrors.Wrap(err, "error generating bcrypt key from passphrase")) + } + + key = crypto.Sha256(key) // get 32 bytes + privKeyBytes := legacy.Cdc.MustMarshalBinaryBare(privKey) + + return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) +} + +// UnarmorDecryptPrivKey returns the privkey byte slice, a string of the algo type, and an error +func UnarmorDecryptPrivKey(armorStr string, passphrase string) (privKey cryptotypes.PrivKey, algo string, err error) { + blockType, header, encBytes, err := armor.DecodeArmor(armorStr) + if err != nil { + return privKey, "", err + } + + if blockType != blockTypePrivKey { + return privKey, "", fmt.Errorf("unrecognized armor type: %v", blockType) + } + + if header["kdf"] != "bcrypt" { + return privKey, "", fmt.Errorf("unrecognized KDF type: %v", header["kdf"]) + } + + if header["salt"] == "" { + return privKey, "", fmt.Errorf("missing salt bytes") + } + + saltBytes, err := hex.DecodeString(header["salt"]) + if err != nil { + return privKey, "", fmt.Errorf("error decoding salt: %v", err.Error()) + } + + privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) + + if header[headerType] == "" { + header[headerType] = defaultAlgo + } + + return privKey, header[headerType], err +} + +func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey cryptotypes.PrivKey, err error) { + key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter) + if err != nil { + return privKey, sdkerrors.Wrap(err, "error generating bcrypt key from passphrase") + } + + key = crypto.Sha256(key) // Get 32 bytes + + privKeyBytes, err := xsalsa20symmetric.DecryptSymmetric(encBytes, key) + if err != nil && err.Error() == "Ciphertext decryption failed" { + return privKey, sdkerrors.ErrWrongPassword + } else if err != nil { + return privKey, err + } + + return legacy.PrivKeyFromBytes(privKeyBytes) +} diff --git a/crypto/armor_test.go b/crypto/armor_test.go new file mode 100644 index 000000000000..56699b3919e7 --- /dev/null +++ b/crypto/armor_test.go @@ -0,0 +1,174 @@ +package crypto_test + +import ( + "bytes" + "errors" + "fmt" + "io" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/crypto/bcrypt" + tmcrypto "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/armor" + "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" +) + +func TestArmorUnarmorPrivKey(t *testing.T) { + priv := secp256k1.GenPrivKey() + armored := crypto.EncryptArmorPrivKey(priv, "passphrase", "") + _, _, err := crypto.UnarmorDecryptPrivKey(armored, "wrongpassphrase") + require.Error(t, err) + decrypted, algo, err := crypto.UnarmorDecryptPrivKey(armored, "passphrase") + require.NoError(t, err) + require.Equal(t, string(hd.Secp256k1Type), algo) + require.True(t, priv.Equals(decrypted)) + + // empty string + decrypted, algo, err = crypto.UnarmorDecryptPrivKey("", "passphrase") + require.Error(t, err) + require.True(t, errors.Is(io.EOF, err)) + require.Nil(t, decrypted) + require.Empty(t, algo) + + // wrong key type + armored = crypto.ArmorPubKeyBytes(priv.PubKey().Bytes(), "") + _, _, err = crypto.UnarmorDecryptPrivKey(armored, "passphrase") + require.Error(t, err) + require.Contains(t, err.Error(), "unrecognized armor type") + + // armor key manually + encryptPrivKeyFn := func(privKey cryptotypes.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { + saltBytes = tmcrypto.CRandBytes(16) + key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), crypto.BcryptSecurityParameter) + require.NoError(t, err) + key = tmcrypto.Sha256(key) // get 32 bytes + privKeyBytes := legacy.Cdc.Amino.MustMarshalBinaryBare(privKey) + return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) + } + saltBytes, encBytes := encryptPrivKeyFn(priv, "passphrase") + + // wrong kdf header + headerWrongKdf := map[string]string{ + "kdf": "wrong", + "salt": fmt.Sprintf("%X", saltBytes), + "type": "secp256k", + } + armored = armor.EncodeArmor("TENDERMINT PRIVATE KEY", headerWrongKdf, encBytes) + _, _, err = crypto.UnarmorDecryptPrivKey(armored, "passphrase") + require.Error(t, err) + require.Equal(t, "unrecognized KDF type: wrong", err.Error()) +} + +func TestArmorUnarmorPubKey(t *testing.T) { + // Select the encryption and storage for your cryptostore + cstore := keyring.NewInMemory() + + // Add keys and see they return in alphabetical order + info, _, err := cstore.NewMnemonic("Bob", keyring.English, types.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + armored := crypto.ArmorPubKeyBytes(legacy.Cdc.Amino.MustMarshalBinaryBare(info.GetPubKey()), "") + pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armored) + require.NoError(t, err) + pub, err := legacy.PubKeyFromBytes(pubBytes) + require.NoError(t, err) + require.Equal(t, string(hd.Secp256k1Type), algo) + require.True(t, pub.Equals(info.GetPubKey())) + + armored = crypto.ArmorPubKeyBytes(legacy.Cdc.Amino.MustMarshalBinaryBare(info.GetPubKey()), "unknown") + pubBytes, algo, err = crypto.UnarmorPubKeyBytes(armored) + require.NoError(t, err) + pub, err = legacy.PubKeyFromBytes(pubBytes) + require.NoError(t, err) + require.Equal(t, "unknown", algo) + require.True(t, pub.Equals(info.GetPubKey())) + + armored, err = cstore.ExportPrivKeyArmor("Bob", "passphrase") + require.NoError(t, err) + _, _, err = crypto.UnarmorPubKeyBytes(armored) + require.Error(t, err) + require.Equal(t, `couldn't unarmor bytes: unrecognized armor type "TENDERMINT PRIVATE KEY", expected: "TENDERMINT PUBLIC KEY"`, err.Error()) + + // armor pubkey manually + header := map[string]string{ + "version": "0.0.0", + "type": "unknown", + } + armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) + _, algo, err = crypto.UnarmorPubKeyBytes(armored) + require.NoError(t, err) + // return secp256k1 if version is 0.0.0 + require.Equal(t, "secp256k1", algo) + + // missing version header + header = map[string]string{ + "type": "unknown", + } + armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) + bz, algo, err := crypto.UnarmorPubKeyBytes(armored) + require.Nil(t, bz) + require.Empty(t, algo) + require.Error(t, err) + require.Equal(t, "header's version field is empty", err.Error()) + + // unknown version header + header = map[string]string{ + "type": "unknown", + "version": "unknown", + } + armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) + bz, algo, err = crypto.UnarmorPubKeyBytes(armored) + require.Nil(t, bz) + require.Empty(t, algo) + require.Error(t, err) + require.Equal(t, "unrecognized version: unknown", err.Error()) +} + +func TestArmorInfoBytes(t *testing.T) { + bs := []byte("test") + armoredString := crypto.ArmorInfoBytes(bs) + unarmoredBytes, err := crypto.UnarmorInfoBytes(armoredString) + require.NoError(t, err) + require.True(t, bytes.Equal(bs, unarmoredBytes)) +} + +func TestUnarmorInfoBytesErrors(t *testing.T) { + unarmoredBytes, err := crypto.UnarmorInfoBytes("") + require.Error(t, err) + require.True(t, errors.Is(io.EOF, err)) + require.Nil(t, unarmoredBytes) + + header := map[string]string{ + "type": "Info", + "version": "0.0.1", + } + unarmoredBytes, err = crypto.UnarmorInfoBytes(armor.EncodeArmor( + "TENDERMINT KEY INFO", header, []byte("plain-text"))) + require.Error(t, err) + require.Equal(t, "unrecognized version: 0.0.1", err.Error()) + require.Nil(t, unarmoredBytes) +} + +func BenchmarkBcryptGenerateFromPassword(b *testing.B) { + passphrase := []byte("passphrase") + for securityParam := 9; securityParam < 16; securityParam++ { + param := securityParam + b.Run(fmt.Sprintf("benchmark-security-param-%d", param), func(b *testing.B) { + b.ReportAllocs() + saltBytes := tmcrypto.CRandBytes(16) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := bcrypt.GenerateFromPassword(saltBytes, passphrase, param) + require.Nil(b, err) + } + }) + } +} diff --git a/crypto/keys/mintkey/README.md b/crypto/bcrypt_readme.md similarity index 100% rename from crypto/keys/mintkey/README.md rename to crypto/bcrypt_readme.md diff --git a/crypto/codec/amino.go b/crypto/codec/amino.go new file mode 100644 index 000000000000..d50a08864c24 --- /dev/null +++ b/crypto/codec/amino.go @@ -0,0 +1,33 @@ +package codec + +import ( + "github.com/tendermint/tendermint/crypto/sr25519" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// RegisterCrypto registers all crypto dependency types with the provided Amino +// codec. +func RegisterCrypto(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*cryptotypes.PubKey)(nil), nil) + cdc.RegisterConcrete(sr25519.PubKey{}, + sr25519.PubKeyName, nil) + cdc.RegisterConcrete(&ed25519.PubKey{}, + ed25519.PubKeyName, nil) + cdc.RegisterConcrete(&secp256k1.PubKey{}, + secp256k1.PubKeyName, nil) + cdc.RegisterConcrete(&kmultisig.LegacyAminoPubKey{}, + kmultisig.PubKeyAminoRoute, nil) + + cdc.RegisterInterface((*cryptotypes.PrivKey)(nil), nil) + cdc.RegisterConcrete(sr25519.PrivKey{}, + sr25519.PrivKeyName, nil) + cdc.RegisterConcrete(&ed25519.PrivKey{}, + ed25519.PrivKeyName, nil) + cdc.RegisterConcrete(&secp256k1.PrivKey{}, + secp256k1.PrivKeyName, nil) +} diff --git a/crypto/codec/proto.go b/crypto/codec/proto.go new file mode 100644 index 000000000000..9c07ca110596 --- /dev/null +++ b/crypto/codec/proto.go @@ -0,0 +1,17 @@ +package codec + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// RegisterInterfaces registers the sdk.Tx interface. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface("cosmos.crypto.PubKey", (*cryptotypes.PubKey)(nil)) + registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &ed25519.PubKey{}) + registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &secp256k1.PubKey{}) + registry.RegisterImplementations((*cryptotypes.PubKey)(nil), &multisig.LegacyAminoPubKey{}) +} diff --git a/crypto/codec/tm.go b/crypto/codec/tm.go new file mode 100644 index 000000000000..8c841e96b3c7 --- /dev/null +++ b/crypto/codec/tm.go @@ -0,0 +1,68 @@ +package codec + +import ( + tmcrypto "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/encoding" + tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// FromTmProtoPublicKey converts a TM's tmprotocrypto.PublicKey into our own PubKey. +func FromTmProtoPublicKey(protoPk tmprotocrypto.PublicKey) (cryptotypes.PubKey, error) { + switch protoPk := protoPk.Sum.(type) { + case *tmprotocrypto.PublicKey_Ed25519: + return &ed25519.PubKey{ + Key: protoPk.Ed25519, + }, nil + case *tmprotocrypto.PublicKey_Secp256K1: + return &secp256k1.PubKey{ + Key: protoPk.Secp256K1, + }, nil + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v from Tendermint public key", protoPk) + } +} + +// ToTmProtoPublicKey converts our own PubKey to TM's tmprotocrypto.PublicKey. +func ToTmProtoPublicKey(pk cryptotypes.PubKey) (tmprotocrypto.PublicKey, error) { + switch pk := pk.(type) { + case *ed25519.PubKey: + return tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Ed25519{ + Ed25519: pk.Key, + }, + }, nil + case *secp256k1.PubKey: + return tmprotocrypto.PublicKey{ + Sum: &tmprotocrypto.PublicKey_Secp256K1{ + Secp256K1: pk.Key, + }, + }, nil + default: + return tmprotocrypto.PublicKey{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v to Tendermint public key", pk) + } +} + +// FromTmPubKeyInterface converts TM's tmcrypto.PubKey to our own PubKey. +func FromTmPubKeyInterface(tmPk tmcrypto.PubKey) (cryptotypes.PubKey, error) { + tmProtoPk, err := encoding.PubKeyToProto(tmPk) + if err != nil { + return nil, err + } + + return FromTmProtoPublicKey(tmProtoPk) +} + +// ToTmPubKeyInterface converts our own PubKey to TM's tmcrypto.PubKey. +func ToTmPubKeyInterface(pk cryptotypes.PubKey) (tmcrypto.PubKey, error) { + tmProtoPk, err := ToTmProtoPublicKey(pk) + if err != nil { + return nil, err + } + + return encoding.PubKeyFromProto(tmProtoPk) +} diff --git a/crypto/encode_test.go b/crypto/encode_test.go deleted file mode 100644 index 858a703994c3..000000000000 --- a/crypto/encode_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package crypto - -import ( - "os" - "testing" - - "github.com/stretchr/testify/require" - - tcrypto "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" -) - -type byter interface { - Bytes() []byte -} - -func checkAminoBinary(t *testing.T, src, dst interface{}, size int) { - // Marshal to binary bytes. - bz, err := cdc.MarshalBinaryBare(src) - require.Nil(t, err, "%+v", err) - if byterSrc, ok := src.(byter); ok { - // Make sure this is compatible with current (Bytes()) encoding. - require.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch") - } - // Make sure we have the expected length. - if size != -1 { - require.Equal(t, size, len(bz), "Amino binary size mismatch") - } - // Unmarshal. - err = cdc.UnmarshalBinaryBare(bz, dst) - require.Nil(t, err, "%+v", err) -} - -func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) { - // Marshal to JSON bytes. - js, err := cdc.MarshalJSON(src) - require.Nil(t, err, "%+v", err) - if isNil { - require.Equal(t, string(js), `null`) - } else { - require.Contains(t, string(js), `"type":`) - require.Contains(t, string(js), `"value":`) - } - // Unmarshal. - err = cdc.UnmarshalJSON(js, dst) - require.Nil(t, err, "%+v", err) -} - -// nolint: govet -func ExamplePrintRegisteredTypes() { - cdc.PrintTypes(os.Stdout) - // Output: | Type | Name | Prefix | Length | Notes | - //| ---- | ---- | ------ | ----- | ------ | - //| PrivKeyLedgerSecp256k1 | tendermint/PrivKeyLedgerSecp256k1 | 0x10CAB393 | variable | | - //| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | - //| PubKeySr25519 | tendermint/PubKeySr25519 | 0x0DFB1005 | 0x20 | | - //| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | - //| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | | - //| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | - //| PrivKeySr25519 | tendermint/PrivKeySr25519 | 0x2F82D78B | 0x20 | | - //| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | -} - -func TestKeyEncodings(t *testing.T) { - cases := []struct { - privKey tcrypto.PrivKey - privSize, pubSize int // binary sizes with the amino overhead - }{ - { - privKey: ed25519.GenPrivKey(), - privSize: 69, - pubSize: 37, - }, - { - privKey: secp256k1.GenPrivKey(), - privSize: 37, - pubSize: 38, - }, - } - - for _, tc := range cases { - - // Check (de/en)codings of PrivKeys. - var priv2, priv3 tcrypto.PrivKey - checkAminoBinary(t, tc.privKey, &priv2, tc.privSize) - require.EqualValues(t, tc.privKey, priv2) - checkAminoJSON(t, tc.privKey, &priv3, false) // TODO also check Prefix bytes. - require.EqualValues(t, tc.privKey, priv3) - - // Check (de/en)codings of Signatures. - var sig1, sig2 []byte - sig1, err := tc.privKey.Sign([]byte("something")) - require.NoError(t, err) - checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways. - require.EqualValues(t, sig1, sig2) - - // Check (de/en)codings of PubKeys. - pubKey := tc.privKey.PubKey() - var pub2, pub3 tcrypto.PubKey - checkAminoBinary(t, pubKey, &pub2, tc.pubSize) - require.EqualValues(t, pubKey, pub2) - checkAminoJSON(t, pubKey, &pub3, false) // TODO also check Prefix bytes. - require.EqualValues(t, pubKey, pub3) - } -} - -func TestNilEncodings(t *testing.T) { - - // Check nil Signature. - var a, b []byte - checkAminoJSON(t, &a, &b, true) - require.EqualValues(t, a, b) - - // Check nil PubKey. - var c, d tcrypto.PubKey - checkAminoJSON(t, &c, &d, true) - require.EqualValues(t, c, d) - - // Check nil PrivKey. - var e, f tcrypto.PrivKey - checkAminoJSON(t, &e, &f, true) - require.EqualValues(t, e, f) - -} diff --git a/crypto/hd/algo.go b/crypto/hd/algo.go new file mode 100644 index 000000000000..f934ad08aece --- /dev/null +++ b/crypto/hd/algo.go @@ -0,0 +1,71 @@ +package hd + +import ( + bip39 "github.com/cosmos/go-bip39" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// PubKeyType defines an algorithm to derive key-pairs which can be used for cryptographic signing. +type PubKeyType string + +const ( + // MultiType implies that a pubkey is a multisignature + MultiType = PubKeyType("multi") + // Secp256k1Type uses the Bitcoin secp256k1 ECDSA parameters. + Secp256k1Type = PubKeyType("secp256k1") + // Ed25519Type represents the Ed25519Type signature system. + // It is currently not supported for end-user keys (wallets/ledgers). + Ed25519Type = PubKeyType("ed25519") + // Sr25519Type represents the Sr25519Type signature system. + Sr25519Type = PubKeyType("sr25519") +) + +var ( + // Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters. + Secp256k1 = secp256k1Algo{} +) + +type DeriveFn func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) +type GenerateFn func(bz []byte) types.PrivKey + +type WalletGenerator interface { + Derive(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) + Generate(bz []byte) types.PrivKey +} + +type secp256k1Algo struct { +} + +func (s secp256k1Algo) Name() PubKeyType { + return Secp256k1Type +} + +// Derive derives and returns the secp256k1 private key for the given seed and HD path. +func (s secp256k1Algo) Derive() DeriveFn { + return func(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) { + seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) + if err != nil { + return nil, err + } + + masterPriv, ch := ComputeMastersFromSeed(seed) + if len(hdPath) == 0 { + return masterPriv[:], nil + } + derivedKey, err := DerivePrivateKeyForPath(masterPriv, ch, hdPath) + + return derivedKey, err + } +} + +// Generate generates a secp256k1 private key from the given bytes. +func (s secp256k1Algo) Generate() GenerateFn { + return func(bz []byte) types.PrivKey { + var bzArr = make([]byte, secp256k1.PrivKeySize) + copy(bzArr, bz) + + return &secp256k1.PrivKey{Key: bzArr} + } +} diff --git a/crypto/hd/algo_test.go b/crypto/hd/algo_test.go new file mode 100644 index 000000000000..767b42154034 --- /dev/null +++ b/crypto/hd/algo_test.go @@ -0,0 +1,16 @@ +package hd_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/hd" +) + +func TestDefaults(t *testing.T) { + require.Equal(t, hd.PubKeyType("multi"), hd.MultiType) + require.Equal(t, hd.PubKeyType("secp256k1"), hd.Secp256k1Type) + require.Equal(t, hd.PubKeyType("ed25519"), hd.Ed25519Type) + require.Equal(t, hd.PubKeyType("sr25519"), hd.Sr25519Type) +} diff --git a/crypto/hd/doc.go b/crypto/hd/doc.go new file mode 100644 index 000000000000..38d65213c177 --- /dev/null +++ b/crypto/hd/doc.go @@ -0,0 +1,12 @@ +// Package hd provides support for hierarchical deterministic wallets generation and derivation. +// +// The user must understand the overall concept of the BIP 32 and the BIP 44 specs: +// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +// +// In combination with the bip39 package in go-crypto this package provides the functionality for +// deriving keys using a BIP 44 HD path, or, more general, by passing a BIP 32 path. +// +// In particular, this package (together with bip39) provides all necessary functionality to derive +// keys from mnemonics generated during the cosmos fundraiser. +package hd diff --git a/crypto/hd/fundraiser_test.go b/crypto/hd/fundraiser_test.go new file mode 100644 index 000000000000..4afbfc5a9ced --- /dev/null +++ b/crypto/hd/fundraiser_test.go @@ -0,0 +1,90 @@ +package hd_test + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/require" + + bip39 "github.com/cosmos/go-bip39" + + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" +) + +type addrData struct { + Mnemonic string + Master string + Seed string + Priv string + Pub string + Addr string +} + +func TestFullFundraiserPath(t *testing.T) { + require.Equal(t, "m/44'/118'/0'/0/0", hd.NewFundraiserParams(0, 118, 0).String()) +} + +func initFundraiserTestVectors(t *testing.T) []addrData { + // NOTE: atom fundraiser address + // var hdPath string = "m/44'/118'/0'/0/0" + var hdToAddrTable []addrData + + b, err := ioutil.ReadFile("testdata/test.json") + if err != nil { + t.Fatalf("could not read fundraiser test vector file (testdata/test.json): %s", err) + } + + err = json.Unmarshal(b, &hdToAddrTable) + if err != nil { + t.Fatalf("could not decode test vectors (testdata/test.json): %s", err) + } + return hdToAddrTable +} + +func TestFundraiserCompatibility(t *testing.T) { + hdToAddrTable := initFundraiserTestVectors(t) + + for i, d := range hdToAddrTable { + privB, _ := hex.DecodeString(d.Priv) + pubB, _ := hex.DecodeString(d.Pub) + addrB, _ := hex.DecodeString(d.Addr) + seedB, _ := hex.DecodeString(d.Seed) + masterB, _ := hex.DecodeString(d.Master) + + seed := bip39.NewSeed(d.Mnemonic, "") + + t.Log("================================") + t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic) + + master, ch := hd.ComputeMastersFromSeed(seed) + priv, err := hd.DerivePrivateKeyForPath(master, ch, "m/44'/118'/0'/0/0") + require.NoError(t, err) + + privKey := &secp256k1.PrivKey{Key: priv} + pub := privKey.PubKey() + + t.Log("\tNODEJS GOLANG\n") + t.Logf("SEED \t%X %X\n", seedB, seed) + t.Logf("MSTR \t%X %X\n", masterB, master) + t.Logf("PRIV \t%X %X\n", privB, priv) + t.Logf("PUB \t%X %X\n", pubB, pub) + + require.Equal(t, seedB, seed) + require.Equal(t, master[:], masterB, fmt.Sprintf("Expected masters to match for %d", i)) + require.Equal(t, priv[:], privB, "Expected priv keys to match") + pubBFixed := make([]byte, secp256k1.PubKeySize) + copy(pubBFixed, pubB) + require.Equal(t, pub, &secp256k1.PubKey{Key: pubBFixed}, fmt.Sprintf("Expected pub keys to match for %d", i)) + + addr := pub.Address() + t.Logf("ADDR \t%X %X\n", addrB, addr) + require.Equal(t, addr, crypto.Address(addrB), fmt.Sprintf("Expected addresses to match %d", i)) + + } +} diff --git a/crypto/hd/hdpath.go b/crypto/hd/hdpath.go new file mode 100644 index 000000000000..058ddcc45667 --- /dev/null +++ b/crypto/hd/hdpath.go @@ -0,0 +1,293 @@ +package hd + +import ( + "crypto/hmac" + "crypto/sha512" + "encoding/binary" + "fmt" + "math/big" + "path/filepath" + "strconv" + "strings" + + "github.com/btcsuite/btcd/btcec" +) + +// BIP44Params wraps BIP 44 params (5 level BIP 32 path). +// To receive a canonical string representation ala +// m / purpose' / coinType' / account' / change / addressIndex +// call String() on a BIP44Params instance. +type BIP44Params struct { + Purpose uint32 `json:"purpose"` + CoinType uint32 `json:"coinType"` + Account uint32 `json:"account"` + Change bool `json:"change"` + AddressIndex uint32 `json:"addressIndex"` +} + +// NewParams creates a BIP 44 parameter object from the params: +// m / purpose' / coinType' / account' / change / addressIndex +func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32) *BIP44Params { + return &BIP44Params{ + Purpose: purpose, + CoinType: coinType, + Account: account, + Change: change, + AddressIndex: addressIdx, + } +} + +// NewParamsFromPath parses the BIP44 path and unmarshals it into a Bip44Params. It supports both +// absolute and relative paths. +func NewParamsFromPath(path string) (*BIP44Params, error) { + spl := strings.Split(path, "/") + + // Handle absolute or relative paths + switch { + case spl[0] == path: + return nil, fmt.Errorf("path %s doesn't contain '/' separators", path) + + case strings.TrimSpace(spl[0]) == "": + return nil, fmt.Errorf("ambiguous path %s: use 'm/' prefix for absolute paths, or no leading '/' for relative ones", path) + + case strings.TrimSpace(spl[0]) == "m": + spl = spl[1:] + } + + if len(spl) != 5 { + return nil, fmt.Errorf("invalid path length %s", path) + } + + // Check items can be parsed + purpose, err := hardenedInt(spl[0]) + if err != nil { + return nil, fmt.Errorf("invalid HD path purpose %s: %w", spl[0], err) + } + + coinType, err := hardenedInt(spl[1]) + if err != nil { + return nil, fmt.Errorf("invalid HD path coin type %s: %w", spl[1], err) + } + + account, err := hardenedInt(spl[2]) + if err != nil { + return nil, fmt.Errorf("invalid HD path account %s: %w", spl[2], err) + } + + change, err := hardenedInt(spl[3]) + if err != nil { + return nil, fmt.Errorf("invalid HD path change %s: %w", spl[3], err) + } + + addressIdx, err := hardenedInt(spl[4]) + if err != nil { + return nil, fmt.Errorf("invalid HD path address index %s: %w", spl[4], err) + } + + // Confirm valid values + if spl[0] != "44'" { + return nil, fmt.Errorf("first field in path must be 44', got %s", spl[0]) + } + + if !isHardened(spl[1]) || !isHardened(spl[2]) { + return nil, + fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %s and %s", spl[1], spl[2]) + } + + if isHardened(spl[3]) || isHardened(spl[4]) { + return nil, + fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %s and %s", spl[3], spl[4]) + } + + if !(change == 0 || change == 1) { + return nil, fmt.Errorf("change field can only be 0 or 1") + } + + return &BIP44Params{ + Purpose: purpose, + CoinType: coinType, + Account: account, + Change: change > 0, + AddressIndex: addressIdx, + }, nil +} + +func hardenedInt(field string) (uint32, error) { + field = strings.TrimSuffix(field, "'") + + i, err := strconv.ParseUint(field, 10, 32) + if err != nil { + return 0, err + } + + return uint32(i), nil +} + +func isHardened(field string) bool { + return strings.HasSuffix(field, "'") +} + +// NewFundraiserParams creates a BIP 44 parameter object from the params: +// m / 44' / coinType' / account' / 0 / address_index +// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser. +func NewFundraiserParams(account, coinType, addressIdx uint32) *BIP44Params { + return NewParams(44, coinType, account, false, addressIdx) +} + +// DerivationPath returns the BIP44 fields as an array. +func (p BIP44Params) DerivationPath() []uint32 { + change := uint32(0) + if p.Change { + change = 1 + } + + return []uint32{ + p.Purpose, + p.CoinType, + p.Account, + change, + p.AddressIndex, + } +} + +// String returns the full absolute HD path of the BIP44 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) params: +// m / purpose' / coin_type' / account' / change / address_index +func (p BIP44Params) String() string { + var changeStr string + if p.Change { + changeStr = "1" + } else { + changeStr = "0" + } + return fmt.Sprintf("m/%d'/%d'/%d'/%s/%d", + p.Purpose, + p.CoinType, + p.Account, + changeStr, + p.AddressIndex) +} + +// ComputeMastersFromSeed returns the master secret key's, and chain code. +func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) { + curveIdentifier := []byte("Bitcoin seed") + secret, chainCode = i64(curveIdentifier, seed) + + return +} + +// 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, "/") + + switch { + case parts[0] == path: + return nil, fmt.Errorf("path '%s' doesn't contain '/' separators", path) + case strings.TrimSpace(parts[0]) == "m": + parts = parts[1:] + } + + 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: + if harden { + part = part[:len(part)-1] + } + + // As per the extended keys specification in + // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys + // index values are in the range [0, 1<<31-1] aka [0, max(int32)] + idx, err := strconv.ParseUint(part, 10, 31) + if err != nil { + return []byte{}, fmt.Errorf("invalid BIP 32 path %s: %w", path, err) + } + + data, chainCode = derivePrivateKey(data, chainCode, uint32(idx), harden) + } + + derivedKey := make([]byte, 32) + n := copy(derivedKey, data[:]) + + if n != 32 || len(data) != 32 { + return []byte{}, fmt.Errorf("expected a key of length 32, got length: %d", len(data)) + } + + return derivedKey, nil +} + +// derivePrivateKey derives the private key with index and chainCode. +// If harden is true, the derivation is 'hardened'. +// It returns the new private key and new chain code. +// For more information on hardened keys see: +// - https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, harden bool) ([32]byte, [32]byte) { + var data []byte + + if harden { + index |= 0x80000000 + + data = append([]byte{byte(0)}, privKeyBytes[:]...) + } else { + // this can't return an error: + _, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:]) + pubkeyBytes := ecPub.SerializeCompressed() + data = pubkeyBytes + + /* By using btcec, we can remove the dependency on tendermint/crypto/secp256k1 + pubkey := secp256k1.PrivKeySecp256k1(privKeyBytes).PubKey() + public := pubkey.(secp256k1.PubKeySecp256k1) + data = public[:] + */ + } + + data = append(data, uint32ToBytes(index)...) + data2, chainCode2 := i64(chainCode[:], data) + x := addScalars(privKeyBytes[:], data2[:]) + + return x, chainCode2 +} + +// modular big endian addition +func addScalars(a []byte, b []byte) [32]byte { + aInt := new(big.Int).SetBytes(a) + bInt := new(big.Int).SetBytes(b) + sInt := new(big.Int).Add(aInt, bInt) + x := sInt.Mod(sInt, btcec.S256().N).Bytes() + x2 := [32]byte{} + copy(x2[32-len(x):], x) + + return x2 +} + +func uint32ToBytes(i uint32) []byte { + b := [4]byte{} + binary.BigEndian.PutUint32(b[:], i) + + return b[:] +} + +// i64 returns the two halfs of the SHA512 HMAC of key and data. +func i64(key []byte, data []byte) (il [32]byte, ir [32]byte) { + mac := hmac.New(sha512.New, key) + // sha512 does not err + _, _ = mac.Write(data) + + I := mac.Sum(nil) + copy(il[:], I[:32]) + copy(ir[:], I[32:]) + + return +} + +// CreateHDPath returns BIP 44 object from account and index parameters. +func CreateHDPath(coinType, account, index uint32) *BIP44Params { + return NewFundraiserParams(account, coinType, index) +} diff --git a/crypto/hd/hdpath_test.go b/crypto/hd/hdpath_test.go new file mode 100644 index 000000000000..8b1c40692c82 --- /dev/null +++ b/crypto/hd/hdpath_test.go @@ -0,0 +1,306 @@ +package hd_test + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/types" + + bip39 "github.com/cosmos/go-bip39" + "github.com/stretchr/testify/require" +) + +var defaultBIP39Passphrase = "" + +// return bip39 seed with empty passphrase +func mnemonicToSeed(mnemonic string) []byte { + return bip39.NewSeed(mnemonic, defaultBIP39Passphrase) +} + +func TestStringifyFundraiserPathParams(t *testing.T) { + path := hd.NewFundraiserParams(4, types.CoinType, 22) + require.Equal(t, "m/44'/118'/4'/0/22", path.String()) + + path = hd.NewFundraiserParams(4, types.CoinType, 57) + require.Equal(t, "m/44'/118'/4'/0/57", path.String()) + + path = hd.NewFundraiserParams(4, 12345, 57) + require.Equal(t, "m/44'/12345'/4'/0/57", path.String()) +} + +func TestPathToArray(t *testing.T) { + path := hd.NewParams(44, 118, 1, false, 4) + require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath())) + + path = hd.NewParams(44, 118, 2, true, 15) + require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath())) +} + +func TestParamsFromPath(t *testing.T) { + goodCases := []struct { + params *hd.BIP44Params + path string + }{ + {&hd.BIP44Params{44, 0, 0, false, 0}, "m/44'/0'/0'/0/0"}, + {&hd.BIP44Params{44, 1, 0, false, 0}, "m/44'/1'/0'/0/0"}, + {&hd.BIP44Params{44, 0, 1, false, 0}, "m/44'/0'/1'/0/0"}, + {&hd.BIP44Params{44, 0, 0, true, 0}, "m/44'/0'/0'/1/0"}, + {&hd.BIP44Params{44, 0, 0, false, 1}, "m/44'/0'/0'/0/1"}, + {&hd.BIP44Params{44, 1, 1, true, 1}, "m/44'/1'/1'/1/1"}, + {&hd.BIP44Params{44, 118, 52, true, 41}, "m/44'/118'/52'/1/41"}, + } + + for i, c := range goodCases { + params, err := hd.NewParamsFromPath(c.path) + errStr := fmt.Sprintf("%d %v", i, c) + require.NoError(t, err, errStr) + require.EqualValues(t, c.params, params, errStr) + require.Equal(t, c.path, c.params.String()) + } + + badCases := []struct { + path string + }{ + {"m/43'/0'/0'/0/0"}, // doesn't start with 44 + {"m/44'/1'/0'/0/0/5"}, // too many fields + {"m/44'/0'/1'/0"}, // too few fields + {"m/44'/0'/0'/2/0"}, // change field can only be 0/1 + {"m/44/0'/0'/0/0"}, // first field needs ' + {"m/44'/0/0'/0/0"}, // second field needs ' + {"m/44'/0'/0/0/0"}, // third field needs ' + {"m/44'/0'/0'/0'/0"}, // fourth field must not have ' + {"m/44'/0'/0'/0/0'"}, // fifth field must not have ' + {"m/44'/-1'/0'/0/0"}, // no negatives + {"m/44'/0'/0'/-1/0"}, // no negatives + {"m/a'/0'/0'/-1/0"}, // invalid values + {"m/0/X/0'/-1/0"}, // invalid values + {"m/44'/0'/X/-1/0"}, // invalid values + {"m/44'/0'/0'/%/0"}, // invalid values + {"m/44'/0'/0'/0/%"}, // invalid values + {"m44'0'0'00"}, // no separators + {" /44'/0'/0'/0/0"}, // blank first component + } + + for i, c := range badCases { + params, err := hd.NewParamsFromPath(c.path) + errStr := fmt.Sprintf("%d %v", i, c) + require.Nil(t, params, errStr) + require.Error(t, err, errStr) + } + +} + +func TestCreateHDPath(t *testing.T) { + type args struct { + coinType uint32 + account uint32 + index uint32 + } + tests := []struct { + name string + args args + want hd.BIP44Params + }{ + {"m/44'/0'/0'/0/0", args{0, 0, 0}, hd.BIP44Params{Purpose: 44}}, + {"m/44'/114'/0'/0/0", args{114, 0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 0, AddressIndex: 0}}, + {"m/44'/114'/1'/1/0", args{114, 1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 1, AddressIndex: 1}}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + tt := tt + require.Equal(t, tt.want, *hd.CreateHDPath(tt.args.coinType, tt.args.account, tt.args.index)) + }) + } +} + +// Tests to ensure that any index value is in the range [0, max(int32)] as per +// the extended keys specification. If the index belongs to that of a hardened key, +// its 0x80000000 bit will be set, so we can still accept values in [0, max(int32)] and then +// increase its value as deriveKeyPath already augments. +// See issue https://github.com/cosmos/cosmos-sdk/issues/7627. +func TestDeriveHDPathRange(t *testing.T) { + seed := mnemonicToSeed("I am become Death, the destroyer of worlds!") + + tests := []struct { + path string + wantErr string + }{ + { + path: "m/1'/2147483648/0'/0/0", + wantErr: "out of range", + }, + { + path: "m/2147483648'/1/0/0", + wantErr: "out of range", + }, + { + path: "m/2147483648'/2147483648/0'/0/0", + wantErr: "out of range", + }, + { + path: "m/1'/-5/0'/0/0", + wantErr: "invalid syntax", + }, + { + path: "m/-2147483646'/1/0/0", + wantErr: "invalid syntax", + }, + { + path: "m/-2147483648'/-2147483648/0'/0/0", + wantErr: "invalid syntax", + }, + { + path: "m44'118'0'00", + wantErr: "path 'm44'118'0'00' doesn't contain '/' separators", + }, + { + path: "", + wantErr: "path '' doesn't contain '/' separators", + }, + { + // Should pass. + path: "m/1'/2147483647'/1/0'/0/0", + }, + { + // Should pass. + path: "1'/2147483647'/1/0'/0/0", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.path, func(t *testing.T) { + master, ch := hd.ComputeMastersFromSeed(seed) + _, err := hd.DerivePrivateKeyForPath(master, ch, tt.path) + + if tt.wantErr == "" { + require.NoError(t, err, "unexpected error") + } else { + require.Error(t, err, "expected a report of an int overflow") + require.Contains(t, err.Error(), tt.wantErr) + } + }) + } +} + +func ExampleStringifyPathParams() { + path := hd.NewParams(44, 0, 0, false, 0) + fmt.Println(path.String()) + path = hd.NewParams(44, 33, 7, true, 9) + fmt.Println(path.String()) + // Output: + // m/44'/0'/0'/0/0 + // m/44'/33'/7'/1/9 +} + +func ExampleSomeBIP32TestVecs() { + seed := mnemonicToSeed("barrel original fuel morning among eternal " + + "filter ball stove pluck matrix mechanic") + master, ch := hd.ComputeMastersFromSeed(seed) + fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)") + fmt.Println() + // cosmos + priv, err := hd.DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath) + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + // bitcoin + priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + // ether + priv, err = hd.DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + // INVALID + priv, err = hd.DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + priv, err = hd.DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + + fmt.Println() + fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html") + fmt.Println() + + seed = mnemonicToSeed( + "advice process birth april short trust crater change bacon monkey medal garment " + + "gorilla ranch hour rival razor call lunar mention taste vacant woman sister") + master, ch = hd.ComputeMastersFromSeed(seed) + priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4") + fmt.Println(hex.EncodeToString(priv[:])) + + seed = mnemonicToSeed("idea naive region square margin day captain habit " + + "gun second farm pact pulse someone armed") + master, ch = hd.ComputeMastersFromSeed(seed) + priv, _ = hd.DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420") + fmt.Println(hex.EncodeToString(priv[:])) + + fmt.Println() + fmt.Println("BIP 32 example") + fmt.Println() + + // bip32 path: m/0/7 + seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history") + master, ch = hd.ComputeMastersFromSeed(seed) + priv, _ = hd.DerivePrivateKeyForPath(master, ch, "0/7") + fmt.Println(hex.EncodeToString(priv[:])) + + // Output: keys from fundraiser test-vector (cosmos, bitcoin, ether) + // + // bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c + // e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d + // 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc + // INVALID + // INVALID + // + // keys generated via https://coinomi.com/recovery-phrase-tool.html + // + // a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163 + // 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f + // + // BIP 32 example + // + // 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/keys/hd/test.json b/crypto/hd/testdata/test.json similarity index 100% rename from crypto/keys/hd/test.json rename to crypto/hd/testdata/test.json diff --git a/crypto/keyring/codec.go b/crypto/keyring/codec.go new file mode 100644 index 000000000000..558f377752d2 --- /dev/null +++ b/crypto/keyring/codec.go @@ -0,0 +1,21 @@ +package keyring + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto/hd" +) + +func init() { + RegisterLegacyAminoCodec(legacy.Cdc) +} + +// RegisterLegacyAminoCodec registers concrete types and interfaces on the given codec. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*Info)(nil), nil) + cdc.RegisterConcrete(hd.BIP44Params{}, "crypto/keys/hd/BIP44Params", nil) + cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil) + cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil) + cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil) + cdc.RegisterConcrete(multiInfo{}, "crypto/keys/multiInfo", nil) +} diff --git a/crypto/keyring/doc.go b/crypto/keyring/doc.go new file mode 100644 index 000000000000..04a8b98cdfba --- /dev/null +++ b/crypto/keyring/doc.go @@ -0,0 +1,46 @@ +// Package keys provides common key management API. +// +// +// The Keybase interface +// +// The Keybase interface defines the methods that a type needs to implement to be used +// as key storage backend. This package provides few implementations out-of-the-box. +// +// NewLegacy +// +// The NewLegacy constructor returns an on-disk implementation backed by LevelDB storage that has been +// the default implementation used by the SDK until v0.38.0. Due to security concerns, it is +// recommended to drop it in favor of the NewKeyring constructor as it will be removed in future releases. +// +// NewInMemory +// +// The NewInMemory constructor returns an implementation backed by an in-memory, goroutine-safe +// map that has historically been used for testing purposes or on-the-fly key generation as the +// generated keys are discarded when the process terminates or the type instance is garbage +// collected. +// +// New +// +// The New constructor returns an implementation backed by a keyring library +// (https://github.com/99designs/keyring), whose aim is to provide a common abstraction and uniform +// interface between secret stores available for Windows, macOS, and most GNU/Linux distributions +// as well as operating system-agnostic encrypted file-based backends. +// +// The backends: +// os The instance returned by this constructor uses the operating system's default +// credentials store to handle keys storage operations securely. It should be noted +// that the keyring keyring may be kept unlocked for the whole duration of the user +// session. +// file This backend more closely resembles the previous keyring storage used prior to +// v0.38.1. It stores the keyring encrypted within the app's configuration directory. +// This keyring will request a password each time it is accessed, which may occur +// multiple times in a single command resulting in repeated password prompts. +// kwallet This backend uses KDE Wallet Manager as a credentials management application: +// https://github.com/KDE/kwallet +// pass This backend uses the pass command line utility to store and retrieve keys: +// https://www.passwordstore.org/ +// test This backend stores keys insecurely to disk. It does not prompt for a password to +// be unlocked and it should be use only for testing purposes. +// memory Same instance as returned by NewInMemory. This backend uses a transient storage. Keys +// are discarded when the process terminates or the type instance is garbage collected. +package keyring diff --git a/crypto/keyring/errors.go b/crypto/keyring/errors.go new file mode 100644 index 000000000000..4ec6d2e465fc --- /dev/null +++ b/crypto/keyring/errors.go @@ -0,0 +1,13 @@ +package keyring + +import "github.com/pkg/errors" + +var ( + // ErrUnsupportedSigningAlgo is raised when the caller tries to use a + // different signing scheme than secp256k1. + ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo") + + // ErrUnsupportedLanguage is raised when the caller tries to use a + // different language than english for creating a mnemonic sentence. + ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported") +) diff --git a/crypto/keyring/info.go b/crypto/keyring/info.go new file mode 100644 index 000000000000..24024599bba7 --- /dev/null +++ b/crypto/keyring/info.go @@ -0,0 +1,276 @@ +package keyring + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" +) + +// Info is the publicly exposed information about a keypair +type Info interface { + // Human-readable type for key listing + GetType() KeyType + // Name of the key + GetName() string + // Public key + GetPubKey() cryptotypes.PubKey + // Address + GetAddress() types.AccAddress + // Bip44 Path + GetPath() (*hd.BIP44Params, error) + // Algo + GetAlgo() hd.PubKeyType +} + +var ( + _ Info = &localInfo{} + _ Info = &ledgerInfo{} + _ Info = &offlineInfo{} + _ Info = &multiInfo{} +) + +// localInfo is the public information about a locally stored key +// Note: Algo must be last field in struct for backwards amino compatibility +type localInfo struct { + Name string `json:"name"` + PubKey cryptotypes.PubKey `json:"pubkey"` + PrivKeyArmor string `json:"privkey.armor"` + Algo hd.PubKeyType `json:"algo"` +} + +func newLocalInfo(name string, pub cryptotypes.PubKey, privArmor string, algo hd.PubKeyType) Info { + return &localInfo{ + Name: name, + PubKey: pub, + PrivKeyArmor: privArmor, + Algo: algo, + } +} + +// GetType implements Info interface +func (i localInfo) GetType() KeyType { + return TypeLocal +} + +// GetType implements Info interface +func (i localInfo) GetName() string { + return i.Name +} + +// GetType implements Info interface +func (i localInfo) GetPubKey() cryptotypes.PubKey { + return i.PubKey +} + +// GetType implements Info interface +func (i localInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + +// GetType implements Info interface +func (i localInfo) GetAlgo() hd.PubKeyType { + return i.Algo +} + +// GetType implements Info interface +func (i localInfo) GetPath() (*hd.BIP44Params, error) { + return nil, fmt.Errorf("BIP44 Paths are not available for this type") +} + +// ledgerInfo is the public information about a Ledger key +// Note: Algo must be last field in struct for backwards amino compatibility +type ledgerInfo struct { + Name string `json:"name"` + PubKey cryptotypes.PubKey `json:"pubkey"` + Path hd.BIP44Params `json:"path"` + Algo hd.PubKeyType `json:"algo"` +} + +func newLedgerInfo(name string, pub cryptotypes.PubKey, path hd.BIP44Params, algo hd.PubKeyType) Info { + return &ledgerInfo{ + Name: name, + PubKey: pub, + Path: path, + Algo: algo, + } +} + +// GetType implements Info interface +func (i ledgerInfo) GetType() KeyType { + return TypeLedger +} + +// GetName implements Info interface +func (i ledgerInfo) GetName() string { + return i.Name +} + +// GetPubKey implements Info interface +func (i ledgerInfo) GetPubKey() cryptotypes.PubKey { + return i.PubKey +} + +// GetAddress implements Info interface +func (i ledgerInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + +// GetPath implements Info interface +func (i ledgerInfo) GetAlgo() hd.PubKeyType { + return i.Algo +} + +// GetPath implements Info interface +func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) { + tmp := i.Path + return &tmp, nil +} + +// offlineInfo is the public information about an offline key +// Note: Algo must be last field in struct for backwards amino compatibility +type offlineInfo struct { + Name string `json:"name"` + PubKey cryptotypes.PubKey `json:"pubkey"` + Algo hd.PubKeyType `json:"algo"` +} + +func newOfflineInfo(name string, pub cryptotypes.PubKey, algo hd.PubKeyType) Info { + return &offlineInfo{ + Name: name, + PubKey: pub, + Algo: algo, + } +} + +// GetType implements Info interface +func (i offlineInfo) GetType() KeyType { + return TypeOffline +} + +// GetName implements Info interface +func (i offlineInfo) GetName() string { + return i.Name +} + +// GetPubKey implements Info interface +func (i offlineInfo) GetPubKey() cryptotypes.PubKey { + return i.PubKey +} + +// GetAlgo returns the signing algorithm for the key +func (i offlineInfo) GetAlgo() hd.PubKeyType { + return i.Algo +} + +// GetAddress implements Info interface +func (i offlineInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + +// GetPath implements Info interface +func (i offlineInfo) GetPath() (*hd.BIP44Params, error) { + return nil, fmt.Errorf("BIP44 Paths are not available for this type") +} + +type multisigPubKeyInfo struct { + PubKey cryptotypes.PubKey `json:"pubkey"` + Weight uint `json:"weight"` +} + +// multiInfo is the public information about a multisig key +type multiInfo struct { + Name string `json:"name"` + PubKey cryptotypes.PubKey `json:"pubkey"` + Threshold uint `json:"threshold"` + PubKeys []multisigPubKeyInfo `json:"pubkeys"` +} + +// NewMultiInfo creates a new multiInfo instance +func NewMultiInfo(name string, pub cryptotypes.PubKey) Info { + multiPK := pub.(*multisig.LegacyAminoPubKey) + + pubKeys := make([]multisigPubKeyInfo, len(multiPK.PubKeys)) + for i, pk := range multiPK.GetPubKeys() { + // TODO: Recursively check pk for total weight? + pubKeys[i] = multisigPubKeyInfo{pk, 1} + } + + return &multiInfo{ + Name: name, + PubKey: pub, + Threshold: uint(multiPK.Threshold), + PubKeys: pubKeys, + } +} + +// GetType implements Info interface +func (i multiInfo) GetType() KeyType { + return TypeMulti +} + +// GetName implements Info interface +func (i multiInfo) GetName() string { + return i.Name +} + +// GetPubKey implements Info interface +func (i multiInfo) GetPubKey() cryptotypes.PubKey { + return i.PubKey +} + +// GetAddress implements Info interface +func (i multiInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + +// GetPath implements Info interface +func (i multiInfo) GetAlgo() hd.PubKeyType { + return hd.MultiType +} + +// GetPath implements Info interface +func (i multiInfo) GetPath() (*hd.BIP44Params, error) { + return nil, fmt.Errorf("BIP44 Paths are not available for this type") +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (i multiInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + multiPK := i.PubKey.(*multisig.LegacyAminoPubKey) + + return codectypes.UnpackInterfaces(multiPK, unpacker) +} + +// encoding info +func marshalInfo(i Info) []byte { + return legacy.Cdc.MustMarshalBinaryLengthPrefixed(i) +} + +// decoding info +func unmarshalInfo(bz []byte) (info Info, err error) { + err = legacy.Cdc.UnmarshalBinaryLengthPrefixed(bz, &info) + if err != nil { + return nil, err + } + + // After unmarshalling into &info, if we notice that the info is a + // multiInfo, then we unmarshal again, explicitly in a multiInfo this time. + // Since multiInfo implements UnpackInterfacesMessage, this will correctly + // unpack the underlying anys inside the multiInfo. + // + // This is a workaround, as go cannot check that an interface (Info) + // implements another interface (UnpackInterfacesMessage). + _, ok := info.(multiInfo) + if ok { + var multi multiInfo + err = legacy.Cdc.UnmarshalBinaryLengthPrefixed(bz, &multi) + + return multi, err + } + + return +} diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go new file mode 100644 index 000000000000..febad555df9c --- /dev/null +++ b/crypto/keyring/keyring.go @@ -0,0 +1,835 @@ +package keyring + +import ( + "bufio" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/99designs/keyring" + bip39 "github.com/cosmos/go-bip39" + "github.com/pkg/errors" + "github.com/tendermint/crypto/bcrypt" + tmcrypto "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/ledger" + "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Backend options for Keyring +const ( + BackendFile = "file" + BackendOS = "os" + BackendKWallet = "kwallet" + BackendPass = "pass" + BackendTest = "test" + BackendMemory = "memory" +) + +const ( + keyringFileDirName = "keyring-file" + keyringTestDirName = "keyring-test" + passKeyringPrefix = "keyring-%s" +) + +var ( + _ Keyring = &keystore{} + maxPassphraseEntryAttempts = 3 +) + +// Keyring exposes operations over a backend supported by github.com/99designs/keyring. +type Keyring interface { + // List all keys. + List() ([]Info, error) + + // Supported signing algorithms for Keyring and Ledger respectively. + SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) + + // Key and KeyByAddress return keys by uid and address respectively. + Key(uid string) (Info, error) + KeyByAddress(address sdk.Address) (Info, error) + + // Delete and DeleteByAddress remove keys from the keyring. + Delete(uid string) error + DeleteByAddress(address sdk.Address) error + + // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic + // key from that, and persists it to the storage. Returns the generated mnemonic and the key + // Info. It returns an error if it fails to generate a key for the given algo type, or if + // another key is already stored under the same name. + NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error) + + // NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it. + NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error) + + // SaveLedgerKey retrieves a public key reference from a Ledger device and persists it. + SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) + + // SavePubKey stores a public key and returns the persisted Info structure. + SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error) + + // SaveMultisig stores and returns a new multsig (offline) key reference. + SaveMultisig(uid string, pubkey types.PubKey) (Info, error) + + Signer + + Importer + Exporter +} + +// UnsafeKeyring exposes unsafe operations such as unsafe unarmored export in +// addition to those that are made available by the Keyring interface. +type UnsafeKeyring interface { + Keyring + UnsafeExporter +} + +// Signer is implemented by key stores that want to provide signing capabilities. +type Signer interface { + // Sign sign byte messages with a user key. + Sign(uid string, msg []byte) ([]byte, types.PubKey, error) + + // SignByAddress sign byte messages with a user key providing the address. + SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error) +} + +// Importer is implemented by key stores that support import of public and private keys. +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 +} + +// LegacyInfoImporter is implemented by key stores that support import of Info types. +type LegacyInfoImporter interface { + // ImportInfo import a keyring.Info into the current keyring. + // It is used to migrate multisig, ledger, and public key Info structure. + ImportInfo(oldInfo Info) error +} + +// Exporter is implemented by key stores that support export of public and private keys. +type Exporter interface { + // Export public key + ExportPubKeyArmor(uid string) (string, error) + ExportPubKeyArmorByAddress(address sdk.Address) (string, error) + + // ExportPrivKeyArmor returns a private key in ASCII armored format. + // It returns an error if the key does not exist or a wrong encryption passphrase is supplied. + ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) + ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) +} + +// UnsafeExporter is implemented by key stores that support unsafe export +// of private keys' material. +type UnsafeExporter interface { + // UnsafeExportPrivKeyHex returns a private key in unarmored hex format + UnsafeExportPrivKeyHex(uid string) (string, error) +} + +// Option overrides keyring configuration options. +type Option func(options *Options) + +// Options define the options of the Keyring. +type Options struct { + // supported signing algorithms for keyring + SupportedAlgos SigningAlgoList + // supported signing algorithms for Ledger + SupportedAlgosLedger SigningAlgoList +} + +// NewInMemory creates a transient keyring useful for testing +// purposes and on-the-fly key generation. +// Keybase options can be applied when generating this new Keybase. +func NewInMemory(opts ...Option) Keyring { + return newKeystore(keyring.NewArrayKeyring(nil), opts...) +} + +// New creates a new instance of a keyring. +// Keyring ptions can be applied when generating the new instance. +// Available backends are "os", "file", "kwallet", "memory", "pass", "test". +func New( + appName, backend, rootDir string, userInput io.Reader, opts ...Option, +) (Keyring, error) { + var ( + db keyring.Keyring + err error + ) + + switch backend { + case BackendMemory: + return NewInMemory(opts...), err + case BackendTest: + db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir)) + case BackendFile: + db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput)) + case BackendOS: + db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput)) + case BackendKWallet: + db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput)) + case BackendPass: + db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput)) + default: + return nil, fmt.Errorf("unknown keyring backend %v", backend) + } + + if err != nil { + return nil, err + } + + return newKeystore(db, opts...), nil +} + +type keystore struct { + db keyring.Keyring + options Options +} + +func newKeystore(kr keyring.Keyring, opts ...Option) keystore { + // Default options for keybase + options := Options{ + SupportedAlgos: SigningAlgoList{hd.Secp256k1}, + SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1}, + } + + for _, optionFn := range opts { + optionFn(&options) + } + + return keystore{kr, options} +} + +func (ks keystore) ExportPubKeyArmor(uid string) (string, error) { + bz, err := ks.Key(uid) + if err != nil { + return "", err + } + + if bz == nil { + return "", fmt.Errorf("no key to export with name: %s", uid) + } + + return crypto.ArmorPubKeyBytes(legacy.Cdc.MustMarshalBinaryBare(bz.GetPubKey()), string(bz.GetAlgo())), nil +} + +func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) { + info, err := ks.KeyByAddress(address) + if err != nil { + return "", err + } + + return ks.ExportPubKeyArmor(info.GetName()) +} + +func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) { + priv, err := ks.ExportPrivateKeyObject(uid) + if err != nil { + return "", err + } + + info, err := ks.Key(uid) + if err != nil { + return "", err + } + + return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil +} + +// ExportPrivateKeyObject exports an armored private key object. +func (ks keystore) ExportPrivateKeyObject(uid string) (types.PrivKey, error) { + info, err := ks.Key(uid) + if err != nil { + return nil, err + } + + var priv types.PrivKey + + switch linfo := info.(type) { + case localInfo: + if linfo.PrivKeyArmor == "" { + err = fmt.Errorf("private key not available") + return nil, err + } + + priv, err = legacy.PrivKeyFromBytes([]byte(linfo.PrivKeyArmor)) + if err != nil { + return nil, err + } + + case ledgerInfo, offlineInfo, multiInfo: + return nil, errors.New("only works on local private keys") + } + + return priv, nil +} + +func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) { + byAddress, err := ks.KeyByAddress(address) + if err != nil { + return "", err + } + + return ks.ExportPrivKeyArmor(byAddress.GetName(), encryptPassphrase) +} + +func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { + if _, err := ks.Key(uid); err == nil { + return fmt.Errorf("cannot overwrite key: %s", uid) + } + + privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase) + if err != nil { + return errors.Wrap(err, "failed to decrypt private key") + } + + _, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo)) + if err != nil { + return err + } + + return nil +} + +func (ks keystore) ImportPubKey(uid string, armor string) error { + if _, err := ks.Key(uid); err == nil { + return fmt.Errorf("cannot overwrite key: %s", uid) + } + + pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor) + if err != nil { + return err + } + + pubKey, err := legacy.PubKeyFromBytes(pubBytes) + if err != nil { + return err + } + + _, err = ks.writeOfflineKey(uid, pubKey, hd.PubKeyType(algo)) + if err != nil { + return err + } + + return nil +} + +// ImportInfo implements Importer.MigrateInfo. +func (ks keystore) ImportInfo(oldInfo Info) error { + if _, err := ks.Key(oldInfo.GetName()); err == nil { + return fmt.Errorf("cannot overwrite key: %s", oldInfo.GetName()) + } + + return ks.writeInfo(oldInfo) +} + +func (ks keystore) Sign(uid string, msg []byte) ([]byte, types.PubKey, error) { + info, err := ks.Key(uid) + if err != nil { + return nil, nil, err + } + + var priv types.PrivKey + + switch i := info.(type) { + case localInfo: + if i.PrivKeyArmor == "" { + return nil, nil, fmt.Errorf("private key not available") + } + + priv, err = legacy.PrivKeyFromBytes([]byte(i.PrivKeyArmor)) + if err != nil { + return nil, nil, err + } + + case ledgerInfo: + return SignWithLedger(info, msg) + + case offlineInfo, multiInfo: + return nil, info.GetPubKey(), errors.New("cannot sign with offline keys") + } + + sig, err := priv.Sign(msg) + if err != nil { + return nil, nil, err + } + + return sig, priv.PubKey(), nil +} + +func (ks keystore) SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error) { + key, err := ks.KeyByAddress(address) + if err != nil { + return nil, nil, err + } + + return ks.Sign(key.GetName(), msg) +} + +func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) { + if !ks.options.SupportedAlgosLedger.Contains(algo) { + return nil, ErrUnsupportedSigningAlgo + } + + hdPath := hd.NewFundraiserParams(account, coinType, index) + + priv, _, err := ledger.NewPrivKeySecp256k1(*hdPath, hrp) + if err != nil { + return nil, err + } + + return ks.writeLedgerKey(uid, priv.PubKey(), *hdPath, algo.Name()) +} + +func (ks keystore) writeLedgerKey(name string, pub types.PubKey, path hd.BIP44Params, algo hd.PubKeyType) (Info, error) { + info := newLedgerInfo(name, pub, path, algo) + if err := ks.writeInfo(info); err != nil { + return nil, err + } + + return info, nil +} + +func (ks keystore) SaveMultisig(uid string, pubkey types.PubKey) (Info, error) { + return ks.writeMultisigKey(uid, pubkey) +} + +func (ks keystore) SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error) { + return ks.writeOfflineKey(uid, pubkey, algo) +} + +func (ks keystore) DeleteByAddress(address sdk.Address) error { + info, err := ks.KeyByAddress(address) + if err != nil { + return err + } + + err = ks.Delete(info.GetName()) + if err != nil { + return err + } + + return nil +} + +func (ks keystore) Delete(uid string) error { + info, err := ks.Key(uid) + if err != nil { + return err + } + + err = ks.db.Remove(addrHexKeyAsString(info.GetAddress())) + if err != nil { + return err + } + + err = ks.db.Remove(string(infoKey(uid))) + if err != nil { + return err + } + + return nil +} + +func (ks keystore) KeyByAddress(address sdk.Address) (Info, error) { + ik, err := ks.db.Get(addrHexKeyAsString(address)) + if err != nil { + return nil, err + } + + if len(ik.Data) == 0 { + return nil, fmt.Errorf("key with address %s not found", address) + } + + bs, err := ks.db.Get(string(ik.Data)) + if err != nil { + return nil, err + } + + return unmarshalInfo(bs.Data) +} + +func (ks keystore) List() ([]Info, error) { + var res []Info + + keys, err := ks.db.Keys() + if err != nil { + return nil, err + } + + sort.Strings(keys) + + for _, key := range keys { + if strings.HasSuffix(key, infoSuffix) { + rawInfo, err := ks.db.Get(key) + if err != nil { + return nil, err + } + + if len(rawInfo.Data) == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key) + } + + info, err := unmarshalInfo(rawInfo.Data) + if err != nil { + return nil, err + } + + res = append(res, info) + } + } + + return res, nil +} + +func (ks keystore) NewMnemonic(uid string, language Language, hdPath string, algo SignatureAlgo) (Info, string, error) { + if language != English { + return nil, "", ErrUnsupportedLanguage + } + + if !ks.isSupportedSigningAlgo(algo) { + return nil, "", ErrUnsupportedSigningAlgo + } + + // Default number of words (24): This generates a mnemonic directly from the + // number of words by reading system entropy. + entropy, err := bip39.NewEntropy(defaultEntropySize) + if err != nil { + return nil, "", err + } + + mnemonic, err := bip39.NewMnemonic(entropy) + if err != nil { + return nil, "", err + } + + info, err := ks.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, hdPath, algo) + if err != nil { + return nil, "", err + } + + return info, mnemonic, err +} + +func (ks keystore) NewAccount(uid string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (Info, error) { + if !ks.isSupportedSigningAlgo(algo) { + return nil, ErrUnsupportedSigningAlgo + } + + // create master key and derive first key for keyring + derivedPriv, err := algo.Derive()(mnemonic, bip39Passphrase, hdPath) + if err != nil { + return nil, err + } + + privKey := algo.Generate()(derivedPriv) + + return ks.writeLocalKey(uid, privKey, algo.Name()) +} + +func (ks keystore) isSupportedSigningAlgo(algo SignatureAlgo) bool { + return ks.options.SupportedAlgos.Contains(algo) +} + +func (ks keystore) Key(uid string) (Info, error) { + key := infoKey(uid) + + bs, err := ks.db.Get(string(key)) + if err != nil { + return nil, err + } + + if len(bs.Data) == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, uid) + } + + return unmarshalInfo(bs.Data) +} + +// SupportedAlgorithms returns the keystore Options' supported signing algorithm. +// for the keyring and Ledger. +func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) { + return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger +} + +// SignWithLedger signs a binary message with the ledger device referenced by an Info object +// and returns the signed bytes and the public key. It returns an error if the device could +// not be queried or it returned an error. +func SignWithLedger(info Info, msg []byte) (sig []byte, pub types.PubKey, err error) { + switch info.(type) { + case *ledgerInfo, ledgerInfo: + default: + return nil, nil, errors.New("not a ledger object") + } + + path, err := info.GetPath() + if err != nil { + return + } + + priv, err := ledger.NewPrivKeySecp256k1Unsafe(*path) + if err != nil { + return + } + + sig, err = priv.Sign(msg) + if err != nil { + return nil, nil, err + } + + return sig, priv.PubKey(), nil +} + +func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config { + return keyring.Config{ + ServiceName: appName, + FileDir: dir, + KeychainTrustApplication: true, + FilePasswordFunc: newRealPrompt(dir, buf), + } +} + +func newTestBackendKeyringConfig(appName, dir string) keyring.Config { + return keyring.Config{ + AllowedBackends: []keyring.BackendType{keyring.FileBackend}, + ServiceName: appName, + FileDir: filepath.Join(dir, keyringTestDirName), + FilePasswordFunc: func(_ string) (string, error) { + return "test", nil + }, + } +} + +func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { + return keyring.Config{ + AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, + ServiceName: "kdewallet", + KWalletAppID: appName, + KWalletFolder: "", + } +} + +func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { + prefix := fmt.Sprintf(passKeyringPrefix, appName) + + return keyring.Config{ + AllowedBackends: []keyring.BackendType{keyring.PassBackend}, + ServiceName: appName, + PassPrefix: prefix, + } +} + +func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config { + fileDir := filepath.Join(dir, keyringFileDirName) + + return keyring.Config{ + AllowedBackends: []keyring.BackendType{keyring.FileBackend}, + ServiceName: name, + FileDir: fileDir, + FilePasswordFunc: newRealPrompt(fileDir, buf), + } +} + +func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { + return func(prompt string) (string, error) { + keyhashStored := false + keyhashFilePath := filepath.Join(dir, "keyhash") + + var keyhash []byte + + _, err := os.Stat(keyhashFilePath) + + switch { + case err == nil: + keyhash, err = ioutil.ReadFile(keyhashFilePath) + if err != nil { + return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err) + } + + keyhashStored = true + + case os.IsNotExist(err): + keyhashStored = false + + default: + return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err) + } + + failureCounter := 0 + + for { + failureCounter++ + if failureCounter > maxPassphraseEntryAttempts { + return "", fmt.Errorf("too many failed passphrase attempts") + } + + buf := bufio.NewReader(buf) + pass, err := input.GetPassword("Enter keyring passphrase:", buf) + if err != nil { + // NOTE: LGTM.io reports a false positive alert that states we are printing the password, + // but we only log the error. + // + // lgtm [go/clear-text-logging] + fmt.Fprintln(os.Stderr, err) + continue + } + + if keyhashStored { + if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { + fmt.Fprintln(os.Stderr, "incorrect passphrase") + continue + } + + return pass, nil + } + + reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) + if err != nil { + // NOTE: LGTM.io reports a false positive alert that states we are printing the password, + // but we only log the error. + // + // lgtm [go/clear-text-logging] + fmt.Fprintln(os.Stderr, err) + continue + } + + if pass != reEnteredPass { + fmt.Fprintln(os.Stderr, "passphrase do not match") + continue + } + + saltBytes := tmcrypto.CRandBytes(16) + passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + + if err := ioutil.WriteFile(dir+"/keyhash", passwordHash, 0555); err != nil { + return "", err + } + + return pass, nil + } + } +} + +func (ks keystore) writeLocalKey(name string, priv types.PrivKey, algo hd.PubKeyType) (Info, error) { + // encrypt private key using keyring + pub := priv.PubKey() + + info := newLocalInfo(name, pub, string(legacy.Cdc.MustMarshalBinaryBare(priv)), algo) + if err := ks.writeInfo(info); err != nil { + return nil, err + } + + return info, nil +} + +func (ks keystore) writeInfo(info Info) error { + // write the info by key + key := infoKey(info.GetName()) + serializedInfo := marshalInfo(info) + + exists, err := ks.existsInDb(info) + if exists { + return errors.New("public key already exist in keybase") + } + + if err != nil { + return err + } + + err = ks.db.Set(keyring.Item{ + Key: string(key), + Data: serializedInfo, + }) + if err != nil { + return err + } + + err = ks.db.Set(keyring.Item{ + Key: addrHexKeyAsString(info.GetAddress()), + Data: key, + }) + if err != nil { + return err + } + + return nil +} + +func (ks keystore) existsInDb(info Info) (bool, error) { + if _, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil { + return true, nil // address lookup succeeds - info exists + } else if err != keyring.ErrKeyNotFound { + return false, err // received unexpected error - returns error + } + + if _, err := ks.db.Get(string(infoKey(info.GetName()))); err == nil { + return true, nil // uid lookup succeeds - info exists + } else if err != keyring.ErrKeyNotFound { + return false, err // received unexpected error - returns + } + + // both lookups failed, info does not exist + return false, nil +} + +func (ks keystore) writeOfflineKey(name string, pub types.PubKey, algo hd.PubKeyType) (Info, error) { + info := newOfflineInfo(name, pub, algo) + err := ks.writeInfo(info) + if err != nil { + return nil, err + } + + return info, nil +} + +func (ks keystore) writeMultisigKey(name string, pub types.PubKey) (Info, error) { + info := NewMultiInfo(name, pub) + err := ks.writeInfo(info) + if err != nil { + return nil, err + } + + return info, nil +} + +type unsafeKeystore struct { + keystore +} + +// NewUnsafe returns a new keyring that provides support for unsafe operations. +func NewUnsafe(kr Keyring) UnsafeKeyring { + // The type assertion is against the only keystore + // implementation that is currently provided. + ks := kr.(keystore) + + return unsafeKeystore{ks} +} + +// UnsafeExportPrivKeyHex exports private keys in unarmored hexadecimal format. +func (ks unsafeKeystore) UnsafeExportPrivKeyHex(uid string) (privkey string, err error) { + priv, err := ks.ExportPrivateKeyObject(uid) + if err != nil { + return "", err + } + + return hex.EncodeToString(priv.Bytes()), nil +} + +func addrHexKeyAsString(address sdk.Address) string { + return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix) +} diff --git a/crypto/keyring/keyring_ledger_test.go b/crypto/keyring/keyring_ledger_test.go new file mode 100644 index 000000000000..eb7e85f9e89c --- /dev/null +++ b/crypto/keyring/keyring_ledger_test.go @@ -0,0 +1,127 @@ +//+build ledger test_ledger_mock + +package keyring + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestInMemoryCreateLedger(t *testing.T) { + kb := NewInMemory() + + ledger, err := kb.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1) + + if err != nil { + require.Error(t, err) + require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) + require.Nil(t, ledger) + t.Skip("ledger nano S: support for ledger devices is not available in this executable") + return + } + + // The mock is available, check that the address is correct + pubKey := ledger.GetPubKey() + pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + // Check that restoring the key gets the same results + restoredKey, err := kb.Key("some_account") + require.NoError(t, err) + require.NotNil(t, restoredKey) + require.Equal(t, "some_account", restoredKey.GetName()) + require.Equal(t, TypeLedger, restoredKey.GetType()) + pubKey = restoredKey.GetPubKey() + pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + path, err := restoredKey.GetPath() + require.NoError(t, err) + require.Equal(t, "m/44'/118'/3'/0/1", path.String()) +} + +// TestSignVerify does some detailed checks on how we sign and validate +// signatures +func TestSignVerifyKeyRingWithLedger(t *testing.T) { + dir := t.TempDir() + + kb, err := New("keybasename", "test", dir, nil) + require.NoError(t, err) + + i1, err := kb.SaveLedgerKey("key", hd.Secp256k1, "cosmos", 118, 0, 0) + if err != nil { + require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) + t.Skip("ledger nano S: support for ledger devices is not available in this executable") + return + } + require.Equal(t, "key", i1.GetName()) + + d1 := []byte("my first message") + s1, pub1, err := kb.Sign("key", d1) + require.NoError(t, err) + + s2, pub2, err := SignWithLedger(i1, d1) + require.NoError(t, err) + + require.True(t, pub1.Equals(pub2)) + require.True(t, bytes.Equal(s1, s2)) + + require.Equal(t, i1.GetPubKey(), pub1) + require.Equal(t, i1.GetPubKey(), pub2) + require.True(t, pub1.VerifySignature(d1, s1)) + require.True(t, i1.GetPubKey().VerifySignature(d1, s1)) + require.True(t, bytes.Equal(s1, s2)) + + localInfo, _, err := kb.NewMnemonic("test", English, types.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + _, _, err = SignWithLedger(localInfo, d1) + require.Error(t, err) + require.Equal(t, "not a ledger object", err.Error()) +} + +func TestAltKeyring_SaveLedgerKey(t *testing.T) { + dir := t.TempDir() + + keyring, err := New(t.Name(), BackendTest, dir, nil) + require.NoError(t, err) + + // Test unsupported Algo + _, err = keyring.SaveLedgerKey("key", notSupportedAlgo{}, "cosmos", 118, 0, 0) + require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) + + ledger, err := keyring.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1) + if err != nil { + require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) + t.Skip("ledger nano S: support for ledger devices is not available in this executable") + return + } + // The mock is available, check that the address is correct + require.Equal(t, "some_account", ledger.GetName()) + pubKey := ledger.GetPubKey() + pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + // Check that restoring the key gets the same results + restoredKey, err := keyring.Key("some_account") + require.NoError(t, err) + require.NotNil(t, restoredKey) + require.Equal(t, "some_account", restoredKey.GetName()) + require.Equal(t, TypeLedger, restoredKey.GetType()) + pubKey = restoredKey.GetPubKey() + pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) + + path, err := restoredKey.GetPath() + require.NoError(t, err) + require.Equal(t, "m/44'/118'/3'/0/1", path.String()) +} diff --git a/crypto/keyring/keyring_test.go b/crypto/keyring/keyring_test.go new file mode 100644 index 000000000000..bcd7eddf39f9 --- /dev/null +++ b/crypto/keyring/keyring_test.go @@ -0,0 +1,1164 @@ +package keyring + +import ( + "encoding/hex" + "fmt" + "strings" + "testing" + + "github.com/99designs/keyring" + bip39 "github.com/cosmos/go-bip39" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + someKey = "theKey" + theID = "theID" + otherID = "otherID" +) + +func init() { + crypto.BcryptSecurityParameter = 1 +} + +func TestNewKeyring(t *testing.T) { + dir := t.TempDir() + mockIn := strings.NewReader("") + + kr, err := New("cosmos", BackendFile, dir, mockIn) + require.NoError(t, err) + + nilKr, err := New("cosmos", "fuzzy", dir, mockIn) + require.Error(t, err) + require.Nil(t, nilKr) + require.Equal(t, "unknown keyring backend fuzzy", err.Error()) + + mockIn.Reset("password\npassword\n") + info, _, err := kr.NewMnemonic("foo", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + require.Equal(t, "foo", info.GetName()) +} + +func TestKeyManagementKeyRing(t *testing.T) { + kb, err := New("keybasename", "test", t.TempDir(), nil) + require.NoError(t, err) + + algo := hd.Secp256k1 + n1, n2, n3 := "personal", "business", "other" + + // Check empty state + l, err := kb.List() + require.Nil(t, err) + require.Empty(t, l) + + _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.Error(t, err, "ed25519 keys are currently not supported by keybase") + + // create some keys + _, err = kb.Key(n1) + require.Error(t, err) + i, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + + require.NoError(t, err) + require.Equal(t, n1, i.GetName()) + _, _, err = kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + require.NoError(t, err) + + // we can get these keys + i2, err := kb.Key(n2) + require.NoError(t, err) + _, err = kb.Key(n3) + require.NotNil(t, err) + _, err = kb.KeyByAddress(accAddr(i2)) + require.NoError(t, err) + addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + require.NoError(t, err) + _, err = kb.KeyByAddress(addr) + require.NotNil(t, err) + + // list shows them in order + keyS, err := kb.List() + require.NoError(t, err) + require.Equal(t, 2, len(keyS)) + // note these are in alphabetical order + require.Equal(t, n2, keyS[0].GetName()) + require.Equal(t, n1, keyS[1].GetName()) + require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) + + // deleting a key removes it + err = kb.Delete("bad name") + require.NotNil(t, err) + err = kb.Delete(n1) + require.NoError(t, err) + keyS, err = kb.List() + require.NoError(t, err) + require.Equal(t, 1, len(keyS)) + _, err = kb.Key(n1) + require.Error(t, err) + + // create an offline key + o1 := "offline" + priv1 := ed25519.GenPrivKey() + pub1 := priv1.PubKey() + i, err = kb.SavePubKey(o1, pub1, hd.Ed25519Type) + require.Nil(t, err) + require.Equal(t, pub1, i.GetPubKey()) + require.Equal(t, o1, i.GetName()) + keyS, err = kb.List() + require.NoError(t, err) + require.Equal(t, 2, len(keyS)) + + // delete the offline key + err = kb.Delete(o1) + require.NoError(t, err) + keyS, err = kb.List() + require.NoError(t, err) + require.Equal(t, 1, len(keyS)) + + // addr cache gets nuked - and test skip flag + require.NoError(t, kb.Delete(n2)) +} + +func TestSignVerifyKeyRing(t *testing.T) { + dir := t.TempDir() + + kb, err := New("keybasename", "test", dir, nil) + require.NoError(t, err) + algo := hd.Secp256k1 + + n1, n2, n3 := "some dude", "a dudette", "dude-ish" + + // create two users and get their info + i1, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err) + + i2, _, err := kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err) + + // let's try to sign some messages + d1 := []byte("my first message") + d2 := []byte("some other important info!") + d3 := []byte("feels like I forgot something...") + + // try signing both data with both .. + s11, pub1, err := kb.Sign(n1, d1) + require.Nil(t, err) + require.Equal(t, i1.GetPubKey(), pub1) + + s12, pub1, err := kb.Sign(n1, d2) + require.Nil(t, err) + require.Equal(t, i1.GetPubKey(), pub1) + + s21, pub2, err := kb.Sign(n2, d1) + require.Nil(t, err) + require.Equal(t, i2.GetPubKey(), pub2) + + s22, pub2, err := kb.Sign(n2, d2) + require.Nil(t, err) + require.Equal(t, i2.GetPubKey(), pub2) + + // let's try to validate and make sure it only works when everything is proper + cases := []struct { + key types.PubKey + data []byte + sig []byte + valid bool + }{ + // proper matches + {i1.GetPubKey(), d1, s11, true}, + // change data, pubkey, or signature leads to fail + {i1.GetPubKey(), d2, s11, false}, + {i2.GetPubKey(), d1, s11, false}, + {i1.GetPubKey(), d1, s21, false}, + // make sure other successes + {i1.GetPubKey(), d2, s12, true}, + {i2.GetPubKey(), d1, s21, true}, + {i2.GetPubKey(), d2, s22, true}, + } + + for i, tc := range cases { + valid := tc.key.VerifySignature(tc.data, tc.sig) + require.Equal(t, tc.valid, valid, "%d", i) + } + + // Now try to sign data with a secret-less key + // Import a public key + armor, err := kb.ExportPubKeyArmor(n2) + require.NoError(t, err) + require.NoError(t, kb.Delete(n2)) + + require.NoError(t, kb.ImportPubKey(n3, armor)) + i3, err := kb.Key(n3) + require.NoError(t, err) + require.Equal(t, i3.GetName(), n3) + + _, _, err = kb.Sign(n3, d3) + require.Error(t, err) + require.Equal(t, "cannot sign with offline keys", err.Error()) +} + +func TestExportImportKeyRing(t *testing.T) { + kb, err := New("keybasename", "test", t.TempDir(), nil) + require.NoError(t, err) + + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + require.Equal(t, info.GetName(), "john") + + john, err := kb.Key("john") + require.NoError(t, err) + require.Equal(t, info.GetName(), "john") + johnAddr := info.GetPubKey().Address() + + armor, err := kb.ExportPrivKeyArmor("john", "apassphrase") + require.NoError(t, err) + err = kb.Delete("john") + require.NoError(t, err) + + err = kb.ImportPrivKey("john2", armor, "apassphrase") + require.NoError(t, err) + + john2, err := kb.Key("john2") + require.NoError(t, err) + + require.Equal(t, john.GetPubKey().Address(), johnAddr) + require.Equal(t, john.GetName(), "john") + require.Equal(t, john.GetAddress(), john2.GetAddress()) + require.Equal(t, john.GetAlgo(), john2.GetAlgo()) + require.Equal(t, john.GetPubKey(), john2.GetPubKey()) + require.Equal(t, john.GetType(), john2.GetType()) +} + +func TestExportImportPubKeyKeyRing(t *testing.T) { + kb, err := New("keybasename", "test", t.TempDir(), nil) + require.NoError(t, err) + algo := hd.Secp256k1 + + // CreateMnemonic a private-public key pair and ensure consistency + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, algo) + require.Nil(t, err) + require.NotEqual(t, info, "") + require.Equal(t, info.GetName(), "john") + addr := info.GetPubKey().Address() + john, err := kb.Key("john") + require.NoError(t, err) + require.Equal(t, john.GetName(), "john") + require.Equal(t, john.GetPubKey().Address(), addr) + + // Export the public key only + armor, err := kb.ExportPubKeyArmor("john") + require.NoError(t, err) + err = kb.Delete("john") + require.NoError(t, err) + + // Import it under a different name + err = kb.ImportPubKey("john-pubkey-only", armor) + require.NoError(t, err) + + // Ensure consistency + john2, err := kb.Key("john-pubkey-only") + require.NoError(t, err) + + // Compare the public keys + require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) + + // Ensure keys cannot be overwritten + err = kb.ImportPubKey("john-pubkey-only", armor) + require.NotNil(t, err) +} + +func TestAdvancedKeyManagementKeyRing(t *testing.T) { + dir := t.TempDir() + + kb, err := New("keybasename", "test", dir, nil) + require.NoError(t, err) + + algo := hd.Secp256k1 + n1, n2 := "old-name", "new name" + + // make sure key works with initial password + _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err, "%+v", err) + + _, err = kb.ExportPubKeyArmor(n1 + ".notreal") + require.NotNil(t, err) + _, err = kb.ExportPubKeyArmor(" " + n1) + require.NotNil(t, err) + _, err = kb.ExportPubKeyArmor(n1 + " ") + require.NotNil(t, err) + _, err = kb.ExportPubKeyArmor("") + require.NotNil(t, err) + exported, err := kb.ExportPubKeyArmor(n1) + require.Nil(t, err, "%+v", err) + err = kb.Delete(n1) + require.NoError(t, err) + + // import succeeds + err = kb.ImportPubKey(n2, exported) + require.NoError(t, err) + + // second import fails + err = kb.ImportPubKey(n2, exported) + require.NotNil(t, err) +} + +func TestSeedPhraseKeyRing(t *testing.T) { + dir := t.TempDir() + + kb, err := New("keybasename", "test", dir, nil) + require.NoError(t, err) + + algo := hd.Secp256k1 + n1, n2 := "lost-key", "found-again" + + // make sure key works with initial password + info, mnemonic, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err, "%+v", err) + require.Equal(t, n1, info.GetName()) + require.NotEmpty(t, mnemonic) + + // now, let us delete this key + err = kb.Delete(n1) + require.Nil(t, err, "%+v", err) + _, err = kb.Key(n1) + require.NotNil(t, err) + + // let us re-create it from the mnemonic-phrase + params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) + hdPath := params.String() + newInfo, err := kb.NewAccount(n2, mnemonic, DefaultBIP39Passphrase, hdPath, hd.Secp256k1) + require.NoError(t, err) + require.Equal(t, n2, newInfo.GetName()) + require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) + require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) +} + +func TestKeyringKeybaseExportImportPrivKey(t *testing.T) { + kb, err := New("keybasename", "test", t.TempDir(), nil) + require.NoError(t, err) + + _, _, err = kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + keystr, err := kb.ExportPrivKeyArmor("john", "somepassword") + require.NoError(t, err) + require.NotEmpty(t, keystr) + err = kb.Delete("john") + require.NoError(t, err) + + // try import the key - wrong password + err = kb.ImportPrivKey("john2", keystr, "bad pass") + require.Equal(t, "failed to decrypt private key: ciphertext decryption failed", err.Error()) + + // try import the key with the correct password + require.NoError(t, kb.ImportPrivKey("john2", keystr, "somepassword")) + + // overwrite is not allowed + err = kb.ImportPrivKey("john2", keystr, "password") + require.Equal(t, "cannot overwrite key: john2", err.Error()) + + // try export non existing key + _, err = kb.ExportPrivKeyArmor("john3", "wrongpassword") + require.Equal(t, "The specified item could not be found in the keyring", err.Error()) +} + +func TestInMemoryLanguage(t *testing.T) { + kb := NewInMemory() + _, _, err := kb.NewMnemonic("something", Japanese, sdk.FullFundraiserPath, hd.Secp256k1) + require.Error(t, err) + require.Equal(t, "unsupported language: only english is supported", err.Error()) +} + +func TestInMemoryCreateMultisig(t *testing.T) { + kb, err := New("keybasename", "memory", "", nil) + require.NoError(t, err) + multi := multisig.NewLegacyAminoPubKey( + 1, []types.PubKey{ + secp256k1.GenPrivKey().PubKey(), + }, + ) + _, err = kb.SaveMultisig("multi", multi) + require.NoError(t, err) +} + +func TestInMemoryCreateAccountInvalidMnemonic(t *testing.T) { + kb := NewInMemory() + _, err := kb.NewAccount( + "some_account", + "malarkey pair crucial catch public canyon evil outer stage ten gym tornado", + "", hd.CreateHDPath(118, 0, 0).String(), hd.Secp256k1) + require.Error(t, err) + require.Equal(t, "Invalid mnemonic", err.Error()) +} + +// TestInMemoryKeyManagement makes sure we can manipulate these keys well +func TestInMemoryKeyManagement(t *testing.T) { + // make the storage with reasonable defaults + cstore := NewInMemory() + + algo := hd.Secp256k1 + n1, n2, n3 := "personal", "business", "other" + + // Check empty state + l, err := cstore.List() + require.Nil(t, err) + require.Empty(t, l) + + _, _, err = cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.Error(t, err, "ed25519 keys are currently not supported by keybase") + + // create some keys + _, err = cstore.Key(n1) + require.Error(t, err) + i, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + + require.NoError(t, err) + require.Equal(t, n1, i.GetName()) + _, _, err = cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + require.NoError(t, err) + + // we can get these keys + i2, err := cstore.Key(n2) + require.NoError(t, err) + _, err = cstore.Key(n3) + require.NotNil(t, err) + _, err = cstore.KeyByAddress(accAddr(i2)) + require.NoError(t, err) + addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + require.NoError(t, err) + _, err = cstore.KeyByAddress(addr) + require.NotNil(t, err) + + // list shows them in order + keyS, err := cstore.List() + require.NoError(t, err) + require.Equal(t, 2, len(keyS)) + // note these are in alphabetical order + require.Equal(t, n2, keyS[0].GetName()) + require.Equal(t, n1, keyS[1].GetName()) + require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) + + // deleting a key removes it + err = cstore.Delete("bad name") + require.NotNil(t, err) + err = cstore.Delete(n1) + require.NoError(t, err) + keyS, err = cstore.List() + require.NoError(t, err) + require.Equal(t, 1, len(keyS)) + _, err = cstore.Key(n1) + require.Error(t, err) + + // create an offline key + o1 := "offline" + priv1 := ed25519.GenPrivKey() + pub1 := priv1.PubKey() + i, err = cstore.SavePubKey(o1, pub1, hd.Ed25519Type) + require.Nil(t, err) + require.Equal(t, pub1, i.GetPubKey()) + require.Equal(t, o1, i.GetName()) + iOffline := i.(*offlineInfo) + require.Equal(t, hd.Ed25519Type, iOffline.GetAlgo()) + keyS, err = cstore.List() + require.NoError(t, err) + require.Equal(t, 2, len(keyS)) + + // delete the offline key + err = cstore.Delete(o1) + require.NoError(t, err) + keyS, err = cstore.List() + require.NoError(t, err) + require.Equal(t, 1, len(keyS)) + + // addr cache gets nuked - and test skip flag + err = cstore.Delete(n2) + require.NoError(t, err) +} + +// TestInMemorySignVerify does some detailed checks on how we sign and validate +// signatures +func TestInMemorySignVerify(t *testing.T) { + cstore := NewInMemory() + algo := hd.Secp256k1 + + n1, n2, n3 := "some dude", "a dudette", "dude-ish" + + // create two users and get their info + i1, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err) + + i2, _, err := cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err) + + // let's try to sign some messages + d1 := []byte("my first message") + d2 := []byte("some other important info!") + d3 := []byte("feels like I forgot something...") + + // try signing both data with both .. + s11, pub1, err := cstore.Sign(n1, d1) + require.Nil(t, err) + require.Equal(t, i1.GetPubKey(), pub1) + + s12, pub1, err := cstore.Sign(n1, d2) + require.Nil(t, err) + require.Equal(t, i1.GetPubKey(), pub1) + + s21, pub2, err := cstore.Sign(n2, d1) + require.Nil(t, err) + require.Equal(t, i2.GetPubKey(), pub2) + + s22, pub2, err := cstore.Sign(n2, d2) + require.Nil(t, err) + require.Equal(t, i2.GetPubKey(), pub2) + + // let's try to validate and make sure it only works when everything is proper + cases := []struct { + key types.PubKey + data []byte + sig []byte + valid bool + }{ + // proper matches + {i1.GetPubKey(), d1, s11, true}, + // change data, pubkey, or signature leads to fail + {i1.GetPubKey(), d2, s11, false}, + {i2.GetPubKey(), d1, s11, false}, + {i1.GetPubKey(), d1, s21, false}, + // make sure other successes + {i1.GetPubKey(), d2, s12, true}, + {i2.GetPubKey(), d1, s21, true}, + {i2.GetPubKey(), d2, s22, true}, + } + + for i, tc := range cases { + valid := tc.key.VerifySignature(tc.data, tc.sig) + require.Equal(t, tc.valid, valid, "%d", i) + } + + // Import a public key + armor, err := cstore.ExportPubKeyArmor(n2) + require.Nil(t, err) + err = cstore.Delete(n2) + require.NoError(t, err) + err = cstore.ImportPubKey(n3, armor) + require.NoError(t, err) + i3, err := cstore.Key(n3) + require.NoError(t, err) + require.Equal(t, i3.GetName(), n3) + + // Now try to sign data with a secret-less key + _, _, err = cstore.Sign(n3, d3) + require.Error(t, err) + require.Equal(t, "cannot sign with offline keys", err.Error()) +} + +// TestInMemoryExportImport tests exporting and importing +func TestInMemoryExportImport(t *testing.T) { + // make the storage with reasonable defaults + cstore := NewInMemory() + + info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + require.Equal(t, info.GetName(), "john") + + john, err := cstore.Key("john") + require.NoError(t, err) + require.Equal(t, info.GetName(), "john") + johnAddr := info.GetPubKey().Address() + + armor, err := cstore.ExportPubKeyArmor("john") + require.NoError(t, err) + err = cstore.Delete("john") + require.NoError(t, err) + + err = cstore.ImportPubKey("john2", armor) + require.NoError(t, err) + + john2, err := cstore.Key("john2") + require.NoError(t, err) + + require.Equal(t, john.GetPubKey().Address(), johnAddr) + require.Equal(t, john.GetName(), "john") + require.Equal(t, john.GetAddress(), john2.GetAddress()) + require.Equal(t, john.GetAlgo(), john2.GetAlgo()) + require.Equal(t, john.GetPubKey(), john2.GetPubKey()) +} + +func TestInMemoryExportImportPrivKey(t *testing.T) { + kb := NewInMemory() + + info, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + require.Equal(t, info.GetName(), "john") + priv1, err := kb.Key("john") + require.NoError(t, err) + + armored, err := kb.ExportPrivKeyArmor("john", "secretcpw") + require.NoError(t, err) + + // delete exported key + require.NoError(t, kb.Delete("john")) + _, err = kb.Key("john") + require.Error(t, err) + + // import armored key + require.NoError(t, kb.ImportPrivKey("john", armored, "secretcpw")) + + // ensure old and new keys match + priv2, err := kb.Key("john") + require.NoError(t, err) + require.True(t, priv1.GetPubKey().Equals(priv2.GetPubKey())) +} + +func TestInMemoryExportImportPubKey(t *testing.T) { + // make the storage with reasonable defaults + cstore := NewInMemory() + + // CreateMnemonic a private-public key pair and ensure consistency + info, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.Nil(t, err) + require.NotEqual(t, info, "") + require.Equal(t, info.GetName(), "john") + addr := info.GetPubKey().Address() + john, err := cstore.Key("john") + require.NoError(t, err) + require.Equal(t, john.GetName(), "john") + require.Equal(t, john.GetPubKey().Address(), addr) + + // Export the public key only + armor, err := cstore.ExportPubKeyArmor("john") + require.NoError(t, err) + err = cstore.Delete("john") + require.NoError(t, err) + + // Import it under a different name + err = cstore.ImportPubKey("john-pubkey-only", armor) + require.NoError(t, err) + // Ensure consistency + john2, err := cstore.Key("john-pubkey-only") + require.NoError(t, err) + // Compare the public keys + require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) + + // Ensure keys cannot be overwritten + err = cstore.ImportPubKey("john-pubkey-only", armor) + require.NotNil(t, err) +} + +// TestInMemoryAdvancedKeyManagement verifies update, import, export functionality +func TestInMemoryAdvancedKeyManagement(t *testing.T) { + // make the storage with reasonable defaults + cstore := NewInMemory() + + algo := hd.Secp256k1 + n1, n2 := "old-name", "new name" + + // make sure key works with initial password + _, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err, "%+v", err) + + // exporting requires the proper name and passphrase + _, err = cstore.ExportPubKeyArmor(n1 + ".notreal") + require.NotNil(t, err) + _, err = cstore.ExportPubKeyArmor(" " + n1) + require.NotNil(t, err) + _, err = cstore.ExportPubKeyArmor(n1 + " ") + require.NotNil(t, err) + _, err = cstore.ExportPubKeyArmor("") + require.NotNil(t, err) + exported, err := cstore.ExportPubKeyArmor(n1) + require.Nil(t, err, "%+v", err) + err = cstore.Delete(n1) + require.NoError(t, err) + + // import succeeds + err = cstore.ImportPubKey(n2, exported) + require.NoError(t, err) + + // second import fails + err = cstore.ImportPubKey(n2, exported) + require.NotNil(t, err) +} + +// TestInMemorySeedPhrase verifies restoring from a seed phrase +func TestInMemorySeedPhrase(t *testing.T) { + // make the storage with reasonable defaults + cstore := NewInMemory() + + algo := hd.Secp256k1 + n1, n2 := "lost-key", "found-again" + + // make sure key works with initial password + info, mnemonic, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, algo) + require.Nil(t, err, "%+v", err) + require.Equal(t, n1, info.GetName()) + require.NotEmpty(t, mnemonic) + + // now, let us delete this key + err = cstore.Delete(n1) + require.Nil(t, err, "%+v", err) + _, err = cstore.Key(n1) + require.NotNil(t, err) + + // let us re-create it from the mnemonic-phrase + params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) + hdPath := params.String() + newInfo, err := cstore.NewAccount(n2, mnemonic, DefaultBIP39Passphrase, hdPath, algo) + require.NoError(t, err) + require.Equal(t, n2, newInfo.GetName()) + require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) + require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) +} + +func TestKeyChain_ShouldFailWhenAddingSameGeneratedAccount(t *testing.T) { + kr, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + // Given we create a mnemonic + _, seed, err := kr.NewMnemonic("test", English, "", hd.Secp256k1) + require.NoError(t, err) + + require.NoError(t, kr.Delete("test")) + + path := hd.CreateHDPath(118, 0, 0).String() + _, err = kr.NewAccount("test1", seed, "", path, hd.Secp256k1) + require.NoError(t, err) + + // Creating another account with different uid but same seed should fail due to have same pub address + _, err = kr.NewAccount("test2", seed, "", path, hd.Secp256k1) + require.Error(t, err) +} + +func ExampleNew() { + // Select the encryption and storage for your cryptostore + cstore := NewInMemory() + + sec := hd.Secp256k1 + + // Add keys and see they return in alphabetical order + bob, _, err := cstore.NewMnemonic("Bob", English, sdk.FullFundraiserPath, sec) + if err != nil { + // this should never happen + fmt.Println(err) + } else { + // return info here just like in List + fmt.Println(bob.GetName()) + } + _, _, _ = cstore.NewMnemonic("Alice", English, sdk.FullFundraiserPath, sec) + _, _, _ = cstore.NewMnemonic("Carl", English, sdk.FullFundraiserPath, sec) + info, _ := cstore.List() + for _, i := range info { + fmt.Println(i.GetName()) + } + + // We need to use passphrase to generate a signature + tx := []byte("deadbeef") + sig, pub, err := cstore.Sign("Bob", tx) + if err != nil { + fmt.Println("don't accept real passphrase") + } + + // and we can validate the signature with publicly available info + binfo, _ := cstore.Key("Bob") + if !binfo.GetPubKey().Equals(bob.GetPubKey()) { + fmt.Println("Get and Create return different keys") + } + + if pub.Equals(binfo.GetPubKey()) { + fmt.Println("signed by Bob") + } + if !pub.VerifySignature(tx, sig) { + fmt.Println("invalid signature") + } + + // Output: + // Bob + // Alice + // Bob + // Carl + // signed by Bob +} + +func TestAltKeyring_List(t *testing.T) { + dir := t.TempDir() + + keyring, err := New(t.Name(), BackendTest, dir, nil) + require.NoError(t, err) + + list, err := keyring.List() + require.NoError(t, err) + require.Empty(t, list) + + // Fails on creating unsupported pubKeyType + _, _, err = keyring.NewMnemonic("failing", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) + + // Create 3 keys + uid1, uid2, uid3 := "Zkey", "Bkey", "Rkey" + _, _, err = keyring.NewMnemonic(uid1, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + _, _, err = keyring.NewMnemonic(uid2, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + _, _, err = keyring.NewMnemonic(uid3, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + list, err = keyring.List() + require.NoError(t, err) + require.Len(t, list, 3) + + // Check they are in alphabetical order + require.Equal(t, uid2, list[0].GetName()) + require.Equal(t, uid3, list[1].GetName()) + require.Equal(t, uid1, list[2].GetName()) +} + +func TestAltKeyring_NewAccount(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + entropy, err := bip39.NewEntropy(defaultEntropySize) + require.NoError(t, err) + + mnemonic, err := bip39.NewMnemonic(entropy) + require.NoError(t, err) + + uid := "newUid" + + // Fails on creating unsupported pubKeyType + _, err = keyring.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) + + info, err := keyring.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + require.Equal(t, uid, info.GetName()) + + list, err := keyring.List() + require.NoError(t, err) + require.Len(t, list, 1) +} + +func TestAltKeyring_Get(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := someKey + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + key, err := keyring.Key(uid) + require.NoError(t, err) + requireEqualInfo(t, mnemonic, key) +} + +func TestAltKeyring_KeyByAddress(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := someKey + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + key, err := keyring.KeyByAddress(mnemonic.GetAddress()) + require.NoError(t, err) + requireEqualInfo(t, key, mnemonic) +} + +func TestAltKeyring_Delete(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := someKey + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + list, err := keyring.List() + require.NoError(t, err) + require.Len(t, list, 1) + + err = keyring.Delete(uid) + require.NoError(t, err) + + list, err = keyring.List() + require.NoError(t, err) + require.Empty(t, list) +} + +func TestAltKeyring_DeleteByAddress(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := someKey + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + list, err := keyring.List() + require.NoError(t, err) + require.Len(t, list, 1) + + err = keyring.DeleteByAddress(mnemonic.GetAddress()) + require.NoError(t, err) + + list, err = keyring.List() + require.NoError(t, err) + require.Empty(t, list) +} + +func TestAltKeyring_SavePubKey(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + list, err := keyring.List() + require.NoError(t, err) + require.Empty(t, list) + + key := someKey + priv := ed25519.GenPrivKey() + pub := priv.PubKey() + + info, err := keyring.SavePubKey(key, pub, hd.Secp256k1.Name()) + require.Nil(t, err) + require.Equal(t, pub, info.GetPubKey()) + require.Equal(t, key, info.GetName()) + require.Equal(t, hd.Secp256k1.Name(), info.GetAlgo()) + + list, err = keyring.List() + require.NoError(t, err) + require.Equal(t, 1, len(list)) +} + +func TestAltKeyring_SaveMultisig(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + mnemonic1, _, err := keyring.NewMnemonic("key1", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + mnemonic2, _, err := keyring.NewMnemonic("key2", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + key := "multi" + pub := multisig.NewLegacyAminoPubKey( + 2, + []types.PubKey{ + &secp256k1.PubKey{Key: mnemonic1.GetPubKey().Bytes()}, + &secp256k1.PubKey{Key: mnemonic2.GetPubKey().Bytes()}, + }, + ) + + info, err := keyring.SaveMultisig(key, pub) + require.Nil(t, err) + require.Equal(t, pub, info.GetPubKey()) + require.Equal(t, key, info.GetName()) + + list, err := keyring.List() + require.NoError(t, err) + require.Len(t, list, 3) +} + +func TestAltKeyring_Sign(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := "jack" + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + msg := []byte("some message") + + sign, key, err := keyring.Sign(uid, msg) + require.NoError(t, err) + + require.True(t, key.VerifySignature(msg, sign)) +} + +func TestAltKeyring_SignByAddress(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := "jack" + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + msg := []byte("some message") + + sign, key, err := keyring.SignByAddress(mnemonic.GetAddress(), msg) + require.NoError(t, err) + + require.True(t, key.VerifySignature(msg, sign)) +} + +func TestAltKeyring_ImportExportPrivKey(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := theID + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + passphrase := "somePass" + armor, err := keyring.ExportPrivKeyArmor(uid, passphrase) + require.NoError(t, err) + err = keyring.Delete(uid) + require.NoError(t, err) + newUID := otherID + // Should fail importing with wrong password + err = keyring.ImportPrivKey(newUID, armor, "wrongPass") + require.EqualError(t, err, "failed to decrypt private key: ciphertext decryption failed") + + err = keyring.ImportPrivKey(newUID, armor, passphrase) + require.NoError(t, err) + + // Should fail importing private key on existing key. + err = keyring.ImportPrivKey(newUID, armor, passphrase) + require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) +} + +func TestAltKeyring_ImportExportPrivKey_ByAddress(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := theID + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + passphrase := "somePass" + armor, err := keyring.ExportPrivKeyArmorByAddress(mnemonic.GetAddress(), passphrase) + require.NoError(t, err) + err = keyring.Delete(uid) + require.NoError(t, err) + + newUID := otherID + // Should fail importing with wrong password + err = keyring.ImportPrivKey(newUID, armor, "wrongPass") + require.EqualError(t, err, "failed to decrypt private key: ciphertext decryption failed") + + err = keyring.ImportPrivKey(newUID, armor, passphrase) + require.NoError(t, err) + + // Should fail importing private key on existing key. + err = keyring.ImportPrivKey(newUID, armor, passphrase) + require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) +} + +func TestAltKeyring_ImportExportPubKey(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := theID + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + armor, err := keyring.ExportPubKeyArmor(uid) + require.NoError(t, err) + err = keyring.Delete(uid) + require.NoError(t, err) + + newUID := otherID + err = keyring.ImportPubKey(newUID, armor) + require.NoError(t, err) + + // Should fail importing private key on existing key. + err = keyring.ImportPubKey(newUID, armor) + require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) +} + +func TestAltKeyring_ImportExportPubKey_ByAddress(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := theID + mnemonic, _, err := keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + armor, err := keyring.ExportPubKeyArmorByAddress(mnemonic.GetAddress()) + require.NoError(t, err) + err = keyring.Delete(uid) + require.NoError(t, err) + + newUID := otherID + err = keyring.ImportPubKey(newUID, armor) + require.NoError(t, err) + + // Should fail importing private key on existing key. + err = keyring.ImportPubKey(newUID, armor) + require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) +} + +func TestAltKeyring_UnsafeExportPrivKeyHex(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + uid := theID + + _, _, err = keyring.NewMnemonic(uid, English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + unsafeKeyring := NewUnsafe(keyring) + privKey, err := unsafeKeyring.UnsafeExportPrivKeyHex(uid) + + require.NoError(t, err) + require.Equal(t, 64, len(privKey)) + + _, err = hex.DecodeString(privKey) + require.NoError(t, err) + + // test error on non existing key + _, err = unsafeKeyring.UnsafeExportPrivKeyHex("non-existing") + require.Error(t, err) +} + +func TestAltKeyring_ConstructorSupportedAlgos(t *testing.T) { + keyring, err := New(t.Name(), BackendTest, t.TempDir(), nil) + require.NoError(t, err) + + // should fail when using unsupported signing algorythm. + _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.EqualError(t, err, "unsupported signing algo") + + // but works with default signing algo. + _, _, err = keyring.NewMnemonic("test", English, sdk.FullFundraiserPath, hd.Secp256k1) + require.NoError(t, err) + + // but we can create a new keybase with our provided algos. + keyring2, err := New(t.Name(), BackendTest, t.TempDir(), nil, func(options *Options) { + options.SupportedAlgos = SigningAlgoList{ + notSupportedAlgo{}, + } + }) + require.NoError(t, err) + + // now this new keyring does not fail when signing with provided algo + _, _, err = keyring2.NewMnemonic("test", English, sdk.FullFundraiserPath, notSupportedAlgo{}) + require.NoError(t, err) +} + +func TestBackendConfigConstructors(t *testing.T) { + backend := newKWalletBackendKeyringConfig("test", "", nil) + require.Equal(t, []keyring.BackendType{keyring.KWalletBackend}, backend.AllowedBackends) + require.Equal(t, "kdewallet", backend.ServiceName) + require.Equal(t, "test", backend.KWalletAppID) + + backend = newPassBackendKeyringConfig("test", "directory", nil) + require.Equal(t, []keyring.BackendType{keyring.PassBackend}, backend.AllowedBackends) + require.Equal(t, "test", backend.ServiceName) + require.Equal(t, "keyring-test", backend.PassPrefix) +} + +func requireEqualInfo(t *testing.T, key Info, mnemonic Info) { + require.Equal(t, key.GetName(), mnemonic.GetName()) + require.Equal(t, key.GetAddress(), mnemonic.GetAddress()) + require.Equal(t, key.GetPubKey(), mnemonic.GetPubKey()) + require.Equal(t, key.GetAlgo(), mnemonic.GetAlgo()) + require.Equal(t, key.GetType(), mnemonic.GetType()) +} + +func accAddr(info Info) sdk.AccAddress { return info.GetAddress() } diff --git a/crypto/keys/keys.toml b/crypto/keyring/keys.toml similarity index 100% rename from crypto/keys/keys.toml rename to crypto/keyring/keys.toml diff --git a/crypto/keyring/legacy.go b/crypto/keyring/legacy.go new file mode 100644 index 000000000000..59bdc3fc5dad --- /dev/null +++ b/crypto/keyring/legacy.go @@ -0,0 +1,235 @@ +package keyring + +import ( + "fmt" + "io" + "strings" + + "github.com/pkg/errors" + tmos "github.com/tendermint/tendermint/libs/os" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// LegacyKeybase is implemented by the legacy keybase implementation. +type LegacyKeybase interface { + List() ([]Info, error) + Export(name string) (armor string, err error) + ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error) + ExportPubKey(name string) (armor string, err error) + Close() error +} + +// NewLegacy creates a new instance of a legacy keybase. +func NewLegacy(name, dir string, opts ...KeybaseOption) (LegacyKeybase, error) { + if err := tmos.EnsureDir(dir, 0700); err != nil { + return nil, fmt.Errorf("failed to create Keybase directory: %s", err) + } + + db, err := sdk.NewLevelDB(name, dir) + if err != nil { + return nil, err + } + + return newDBKeybase(db), nil +} + +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. +type dbKeybase struct { + db dbm.DB +} + +// newDBKeybase creates a new dbKeybase instance using the provided DB for +// reading and writing keys. +func newDBKeybase(db dbm.DB) dbKeybase { + return dbKeybase{ + db: db, + } +} + +// List returns the keys from storage in alphabetical order. +func (kb dbKeybase) List() ([]Info, error) { + var res []Info + + iter, err := kb.db.Iterator(nil, nil) + if err != nil { + return nil, err + } + + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + key := string(iter.Key()) + + // need to include only keys in storage that have an info suffix + if strings.HasSuffix(key, infoSuffix) { + info, err := unmarshalInfo(iter.Value()) + if err != nil { + return nil, err + } + + res = append(res, info) + } + } + + return res, nil +} + +// Get returns the public information about one key. +func (kb dbKeybase) Get(name string) (Info, error) { + bs, err := kb.db.Get(infoKey(name)) + if err != nil { + return nil, err + } + + if len(bs) == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name) + } + + return unmarshalInfo(bs) +} + +// ExportPrivateKeyObject returns a PrivKey object given the key name and +// passphrase. An error is returned if the key does not exist or if the Info for +// the key is invalid. +func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (types.PrivKey, error) { + info, err := kb.Get(name) + if err != nil { + return nil, err + } + + var priv types.PrivKey + + switch i := info.(type) { + case localInfo: + linfo := i + if linfo.PrivKeyArmor == "" { + err = fmt.Errorf("private key not available") + return nil, err + } + + priv, _, err = crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase) + if err != nil { + return nil, err + } + + case ledgerInfo, offlineInfo, multiInfo: + return nil, errors.New("only works on local private keys") + } + + return priv, nil +} + +func (kb dbKeybase) Export(name string) (armor string, err error) { + bz, err := kb.db.Get(infoKey(name)) + if err != nil { + return "", err + } + + if bz == nil { + return "", fmt.Errorf("no key to export with name %s", name) + } + + return crypto.ArmorInfoBytes(bz), nil +} + +// ExportPubKey returns public keys in ASCII armored format. It retrieves a Info +// object by its name and return the public key in a portable format. +func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) { + bz, err := kb.db.Get(infoKey(name)) + if err != nil { + return "", err + } + + if bz == nil { + return "", fmt.Errorf("no key to export with name %s", name) + } + + info, err := unmarshalInfo(bz) + if err != nil { + return + } + + return crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil +} + +// ExportPrivKey returns a private key in ASCII armored format. +// It returns an error if the key does not exist or a wrong encryption passphrase +// is supplied. +func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string, + encryptPassphrase string) (armor string, err error) { + priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase) + if err != nil { + return "", err + } + + info, err := kb.Get(name) + if err != nil { + return "", err + } + + return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil +} + +// Close the underlying storage. +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) + +type kbOptions struct { +} diff --git a/crypto/keyring/legacy_test.go b/crypto/keyring/legacy_test.go new file mode 100644 index 000000000000..27503bdea056 --- /dev/null +++ b/crypto/keyring/legacy_test.go @@ -0,0 +1,57 @@ +package keyring_test + +import ( + "io" + "path/filepath" + "testing" + + "github.com/otiai10/copy" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" +) + +func TestNewLegacyKeyBase(t *testing.T) { + dir := t.TempDir() + + kb, err := keyring.NewLegacy("keybasename", dir) + require.NoError(t, err) + require.NoError(t, kb.Close()) +} + +func TestLegacyKeybase(t *testing.T) { + dir := t.TempDir() + + // Backup testdata + require.NoError(t, copy.Copy("testdata", dir)) + + kb, err := keyring.NewLegacy("keys", filepath.Join(dir, "keys")) + require.NoError(t, err) + t.Cleanup(func() { kb.Close() }) + + keys, err := kb.List() + require.NoError(t, err) + require.Equal(t, 2, len(keys)) + + armor, err := kb.ExportPubKey(keys[0].GetName()) + require.NoError(t, err) + require.NotEmpty(t, armor) + + _, err = kb.ExportPrivKey(keys[0].GetName(), "12345678", "12345678") + require.Error(t, err) + + 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/output.go b/crypto/keyring/output.go similarity index 99% rename from crypto/keys/output.go rename to crypto/keyring/output.go index 6c911f46cebe..5f76789caadf 100644 --- a/crypto/keys/output.go +++ b/crypto/keyring/output.go @@ -1,4 +1,4 @@ -package keys +package keyring import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/crypto/keys/output_test.go b/crypto/keyring/output_test.go similarity index 77% rename from crypto/keys/output_test.go rename to crypto/keyring/output_test.go index d6091c190e85..c43b36bf7ce0 100644 --- a/crypto/keys/output_test.go +++ b/crypto/keyring/output_test.go @@ -1,13 +1,13 @@ -package keys +package keyring import ( "testing" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -16,7 +16,7 @@ func TestBech32KeysOutput(t *testing.T) { bechTmpKey := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, tmpKey) tmpAddr := sdk.AccAddress(tmpKey.Address().Bytes()) - multisigPks := multisig.NewPubKeyMultisigThreshold(1, []crypto.PubKey{tmpKey}) + multisigPks := kmultisig.NewLegacyAminoPubKey(1, []types.PubKey{tmpKey}) multiInfo := NewMultiInfo("multisig", multisigPks) accAddr := sdk.AccAddress(multiInfo.GetPubKey().Address().Bytes()) bechPubKey := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, multiInfo.GetPubKey()) diff --git a/crypto/keyring/signing_algorithms.go b/crypto/keyring/signing_algorithms.go new file mode 100644 index 000000000000..920dd7b55310 --- /dev/null +++ b/crypto/keyring/signing_algorithms.go @@ -0,0 +1,49 @@ +package keyring + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/crypto/hd" +) + +// SignatureAlgo defines the interface for a keyring supported algorithm. +type SignatureAlgo interface { + Name() hd.PubKeyType + Derive() hd.DeriveFn + Generate() hd.GenerateFn +} + +// NewSigningAlgoFromString creates a supported SignatureAlgo. +func NewSigningAlgoFromString(str string, algoList SigningAlgoList) (SignatureAlgo, error) { + for _, algo := range algoList { + if str == string(algo.Name()) { + return algo, nil + } + } + return nil, fmt.Errorf("provided algorithm %q is not supported", str) +} + +// SigningAlgoList is a slice of signature algorithms +type SigningAlgoList []SignatureAlgo + +// Contains returns true if the SigningAlgoList the given SignatureAlgo. +func (sal SigningAlgoList) Contains(algo SignatureAlgo) bool { + for _, cAlgo := range sal { + if cAlgo.Name() == algo.Name() { + return true + } + } + + return false +} + +// String returns a comma separated string of the signature algorithm names in the list. +func (sal SigningAlgoList) String() string { + names := make([]string, len(sal)) + for i := range sal { + names[i] = string(sal[i].Name()) + } + + return strings.Join(names, ",") +} diff --git a/crypto/keyring/signing_algorithms_test.go b/crypto/keyring/signing_algorithms_test.go new file mode 100644 index 000000000000..14283b910846 --- /dev/null +++ b/crypto/keyring/signing_algorithms_test.go @@ -0,0 +1,75 @@ +package keyring + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/hd" +) + +func TestNewSigningAlgoByString(t *testing.T) { + tests := []struct { + name string + algoStr string + isSupported bool + expectedAlgo SignatureAlgo + expectedErr error + }{ + { + "supported algorithm", + "secp256k1", + true, + hd.Secp256k1, + nil, + }, + { + "not supported", + "notsupportedalgo", + false, + nil, + fmt.Errorf("provided algorithm \"notsupportedalgo\" is not supported"), + }, + } + + list := SigningAlgoList{hd.Secp256k1} + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + algorithm, err := NewSigningAlgoFromString(tt.algoStr, list) + if tt.isSupported { + require.Equal(t, hd.Secp256k1, algorithm) + } else { + require.EqualError(t, err, tt.expectedErr.Error()) + } + }) + } +} + +func TestAltSigningAlgoList_Contains(t *testing.T) { + list := SigningAlgoList{hd.Secp256k1} + + require.True(t, list.Contains(hd.Secp256k1)) + require.False(t, list.Contains(notSupportedAlgo{})) +} + +func TestAltSigningAlgoList_String(t *testing.T) { + list := SigningAlgoList{hd.Secp256k1, notSupportedAlgo{}} + require.Equal(t, fmt.Sprintf("%s,notSupported", string(hd.Secp256k1Type)), list.String()) +} + +type notSupportedAlgo struct { +} + +func (n notSupportedAlgo) Name() hd.PubKeyType { + return "notSupported" +} + +func (n notSupportedAlgo) Derive() hd.DeriveFn { + return hd.Secp256k1.Derive() +} + +func (n notSupportedAlgo) Generate() hd.GenerateFn { + return hd.Secp256k1.Generate() +} diff --git a/crypto/keyring/testdata/keys/keys.db/000002.ldb b/crypto/keyring/testdata/keys/keys.db/000002.ldb new file mode 100644 index 000000000000..b36586df3626 Binary files /dev/null and b/crypto/keyring/testdata/keys/keys.db/000002.ldb differ diff --git a/crypto/keyring/testdata/keys/keys.db/CURRENT b/crypto/keyring/testdata/keys/keys.db/CURRENT new file mode 100644 index 000000000000..cacca7574c03 --- /dev/null +++ b/crypto/keyring/testdata/keys/keys.db/CURRENT @@ -0,0 +1 @@ +MANIFEST-000004 diff --git a/crypto/keyring/testdata/keys/keys.db/CURRENT.bak b/crypto/keyring/testdata/keys/keys.db/CURRENT.bak new file mode 100644 index 000000000000..feda7d6b2481 --- /dev/null +++ b/crypto/keyring/testdata/keys/keys.db/CURRENT.bak @@ -0,0 +1 @@ +MANIFEST-000000 diff --git a/crypto/keyring/testdata/keys/keys.db/LOCK b/crypto/keyring/testdata/keys/keys.db/LOCK new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/crypto/keyring/testdata/keys/keys.db/LOG b/crypto/keyring/testdata/keys/keys.db/LOG new file mode 100644 index 000000000000..386101e4fb91 --- /dev/null +++ b/crypto/keyring/testdata/keys/keys.db/LOG @@ -0,0 +1,18 @@ +=============== 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 diff --git a/crypto/keyring/testdata/keys/keys.db/MANIFEST-000004 b/crypto/keyring/testdata/keys/keys.db/MANIFEST-000004 new file mode 100644 index 000000000000..557b4bdbbc93 Binary files /dev/null and b/crypto/keyring/testdata/keys/keys.db/MANIFEST-000004 differ diff --git a/crypto/keyring/types.go b/crypto/keyring/types.go new file mode 100644 index 000000000000..0b893ea4cccc --- /dev/null +++ b/crypto/keyring/types.go @@ -0,0 +1,71 @@ +package keyring + +import ( + "github.com/cosmos/cosmos-sdk/crypto/hd" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// Language is a language to create the BIP 39 mnemonic in. +// Currently, only english is supported though. +// Find a list of all supported languages in the BIP 39 spec (word lists). +type Language int + +const ( + // English is the default language to create a mnemonic. + // It is the only supported language by this package. + English Language = iota + 1 + // Japanese is currently not supported. + Japanese + // Korean is currently not supported. + Korean + // Spanish is currently not supported. + Spanish + // ChineseSimplified is currently not supported. + ChineseSimplified + // ChineseTraditional is currently not supported. + ChineseTraditional + // French is currently not supported. + French + // Italian is currently not supported. + Italian +) + +const ( + // DefaultBIP39Passphrase used for deriving seed from mnemonic + DefaultBIP39Passphrase = "" + + // bits of entropy to draw when creating a mnemonic + defaultEntropySize = 256 + addressSuffix = "address" + infoSuffix = "info" +) + +// KeyType reflects a human-readable type for key listing. +type KeyType uint + +// Info KeyTypes +const ( + TypeLocal KeyType = 0 + TypeLedger KeyType = 1 + TypeOffline KeyType = 2 + TypeMulti KeyType = 3 +) + +var keyTypes = map[KeyType]string{ + TypeLocal: "local", + TypeLedger: "ledger", + TypeOffline: "offline", + TypeMulti: "multi", +} + +// String implements the stringer interface for KeyType. +func (kt KeyType) String() string { + return keyTypes[kt] +} + +type ( + // DeriveKeyFunc defines the function to derive a new key from a seed and hd path + DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo hd.PubKeyType) ([]byte, error) + // PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key + PrivKeyGenFunc func(bz []byte, algo hd.PubKeyType) (cryptotypes.PrivKey, error) +) diff --git a/crypto/keyring/types_test.go b/crypto/keyring/types_test.go new file mode 100644 index 000000000000..b04aa4547964 --- /dev/null +++ b/crypto/keyring/types_test.go @@ -0,0 +1,43 @@ +package keyring + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func Test_writeReadLedgerInfo(t *testing.T) { + tmpKey := make([]byte, secp256k1.PubKeySize) + bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") + copy(tmpKey[:], bz) + + lInfo := newLedgerInfo("some_name", &secp256k1.PubKey{Key: tmpKey}, *hd.NewFundraiserParams(5, sdk.CoinType, 1), hd.Secp256k1Type) + require.Equal(t, TypeLedger, lInfo.GetType()) + + path, err := lInfo.GetPath() + require.NoError(t, err) + require.Equal(t, "m/44'/118'/5'/0/1", path.String()) + require.Equal(t, + "cosmospub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5wmxv8p", + sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, lInfo.GetPubKey())) + + // Serialize and restore + serialized := marshalInfo(lInfo) + restoredInfo, err := unmarshalInfo(serialized) + require.NoError(t, err) + require.NotNil(t, restoredInfo) + + // Check both keys match + require.Equal(t, lInfo.GetName(), restoredInfo.GetName()) + require.Equal(t, lInfo.GetType(), restoredInfo.GetType()) + require.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey()) + + restoredPath, err := restoredInfo.GetPath() + require.NoError(t, err) + require.Equal(t, path, restoredPath) +} diff --git a/crypto/keys/README.md b/crypto/keys/README.md deleted file mode 100644 index df690636bc2a..000000000000 --- a/crypto/keys/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Keys API - -[![API Reference](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys?status.svg)](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys) - - -## The Keybase interface - -The [Keybase](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#Keybase) interface defines -the methods that a type needs to implement to be used as key storage backend. This package provides -few implementations out-of-the-box. - -## Constructors - -### New - -The [New](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#New) constructor returns -an on-disk implementation backed by LevelDB storage that has been the default implementation used by the SDK until v0.38.0. -Due to [security concerns](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-006-secret-store-replacement.md), we recommend to drop -it in favor of the `NewKeyring` or `NewKeyringFile` constructors. We strongly advise to migrate away from this function as **it may be removed in a future -release**. - -### NewInMemory - -The [NewInMemory](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#NewInMemory) constructor returns -an implementation backed by an in-memory, goroutine-safe map that we've historically used for testing purposes or on-the-fly -key generation and we consider safe for the aforementioned use cases since the generated keys are discarded when the process -terminates or the type instance is garbage collected. - -### NewKeyring - -The [NewKeyring](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#NewKeyring) constructor returns -an implementation backed by the [Keyring](https://github.com/99designs/keyring) library, whose aim is to provide a common -abstraction and uniform interface between secret stores available for Windows, macOS, and most GNU/Linux distributions. -The instance returned by this constructor will use the operating system's default credentials store, which will then handle -keys storage operations securely. - -### NewKeyringFile, NewTestKeyring - -Both [NewKeyringFile](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#NewKeyringFile) and -[NewTestKeyring](https://godoc.org/github.com/cosmos/cosmos-sdk/crypto/keys#NewTestKeyring) constructors return -on-disk implementations backed by the [Keyring](https://github.com/99designs/keyring) `file` backend. -Whilst `NewKeyringFile` returns a secure, encrypted file-based type that requires user's password in order to -function correctly, the implementation returned by `NewTestKeyring` stores keys information in clear text and **must be used -only for testing purposes**. - -`NewKeyringFile` and `NewTestKeyring` store key files in the client home directory's `keyring` -and `keyring-test` subdirectories respectively. diff --git a/crypto/keys/codec.go b/crypto/keys/codec.go deleted file mode 100644 index c3b3e76a3a8b..000000000000 --- a/crypto/keys/codec.go +++ /dev/null @@ -1,28 +0,0 @@ -package keys - -import ( - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" -) - -// CryptoCdc defines the codec required for keys and info -var CryptoCdc *codec.Codec - -func init() { - CryptoCdc = codec.New() - cryptoAmino.RegisterAmino(CryptoCdc) - RegisterCodec(CryptoCdc) - CryptoCdc.Seal() -} - -// RegisterCodec registers concrete types and interfaces on the given codec. -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*Info)(nil), nil) - cdc.RegisterConcrete(hd.BIP44Params{}, "crypto/keys/hd/BIP44Params", nil) - cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil) - cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil) - cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil) - cdc.RegisterConcrete(multiInfo{}, "crypto/keys/multiInfo", nil) -} diff --git a/crypto/keys/ed25519/ed25519.go b/crypto/keys/ed25519/ed25519.go new file mode 100644 index 000000000000..17368c4b12ff --- /dev/null +++ b/crypto/keys/ed25519/ed25519.go @@ -0,0 +1,216 @@ +package ed25519 + +import ( + "crypto/ed25519" + "crypto/subtle" + "fmt" + "io" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/tmhash" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/errors" +) + +//------------------------------------- + +const ( + PrivKeyName = "tendermint/PrivKeyEd25519" + PubKeyName = "tendermint/PubKeyEd25519" + // PubKeySize is is the size, in bytes, of public keys as used in this package. + PubKeySize = 32 + // PrivKeySize is the size, in bytes, of private keys as used in this package. + PrivKeySize = 64 + // Size of an Edwards25519 signature. Namely the size of a compressed + // Edwards25519 point, and a field element. Both of which are 32 bytes. + SignatureSize = 64 + // SeedSize is the size, in bytes, of private key seeds. These are the + // private key representations used by RFC 8032. + SeedSize = 32 + + keyType = "ed25519" +) + +var _ cryptotypes.PrivKey = &PrivKey{} +var _ codec.AminoMarshaler = &PrivKey{} + +// Bytes returns the privkey byte format. +func (privKey *PrivKey) Bytes() []byte { + return privKey.Key +} + +// Sign produces a signature on the provided message. +// This assumes the privkey is wellformed in the golang format. +// The first 32 bytes should be random, +// corresponding to the normal ed25519 private key. +// The latter 32 bytes should be the compressed public key. +// If these conditions aren't met, Sign will panic or produce an +// incorrect signature. +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + return ed25519.Sign(privKey.Key, msg), nil +} + +// PubKey gets the corresponding public key from the private key. +// +// Panics if the private key is not initialized. +func (privKey *PrivKey) PubKey() cryptotypes.PubKey { + // If the latter 32 bytes of the privkey are all zero, privkey is not + // initialized. + initialized := false + for _, v := range privKey.Key[32:] { + if v != 0 { + initialized = true + break + } + } + + if !initialized { + panic("Expected ed25519 PrivKey to include concatenated pubkey bytes") + } + + pubkeyBytes := make([]byte, PubKeySize) + copy(pubkeyBytes, privKey.Key[32:]) + return &PubKey{Key: pubkeyBytes} +} + +// Equals - you probably don't need to use this. +// Runs in constant time based on length of the keys. +func (privKey *PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + if privKey.Type() != other.Type() { + return false + } + + return 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 ed25519 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 genPrivKey(crypto.CReader()) +} + +// genPrivKey generates a new ed25519 private key using the provided reader. +func genPrivKey(rand io.Reader) *PrivKey { + seed := make([]byte, SeedSize) + + _, err := io.ReadFull(rand, seed) + if err != nil { + panic(err) + } + + return &PrivKey{Key: ed25519.NewKeyFromSeed(seed)} +} + +// 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 { + seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. + + return &PrivKey{Key: ed25519.NewKeyFromSeed(seed)} +} + +//------------------------------------- + +var _ cryptotypes.PubKey = &PubKey{} +var _ codec.AminoMarshaler = &PubKey{} + +// Address is the SHA256-20 of the raw pubkey bytes. +func (pubKey *PubKey) Address() crypto.Address { + if len(pubKey.Key) != PubKeySize { + panic("pubkey is incorrect size") + } + return crypto.Address(tmhash.SumTruncated(pubKey.Key)) +} + +// Bytes returns the PubKey byte format. +func (pubKey *PubKey) Bytes() []byte { + return pubKey.Key +} + +func (pubKey *PubKey) VerifySignature(msg []byte, sig []byte) bool { + // make sure we use the same algorithm to sign + if len(sig) != SignatureSize { + return false + } + + return ed25519.Verify(pubKey.Key, msg, sig) +} + +func (pubKey *PubKey) String() string { + return fmt.Sprintf("PubKeyEd25519{%X}", pubKey.Key) +} + +func (pubKey *PubKey) Type() string { + return keyType +} + +func (pubKey *PubKey) Equals(other cryptotypes.PubKey) bool { + if pubKey.Type() != other.Type() { + return false + } + + return subtle.ConstantTimeCompare(pubKey.Bytes(), other.Bytes()) == 1 +} + +// 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/ed25519/ed25519_test.go b/crypto/keys/ed25519/ed25519_test.go new file mode 100644 index 000000000000..59cce4066ac2 --- /dev/null +++ b/crypto/keys/ed25519/ed25519_test.go @@ -0,0 +1,230 @@ +package ed25519_test + +import ( + stded25519 "crypto/ed25519" + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + tmed25519 "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/cosmos/cosmos-sdk/codec" + ed25519 "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +func TestSignAndValidateEd25519(t *testing.T) { + privKey := ed25519.GenPrivKey() + pubKey := privKey.PubKey() + + msg := crypto.CRandBytes(1000) + sig, err := privKey.Sign(msg) + require.Nil(t, err) + + // Test the signature + assert.True(t, pubKey.VerifySignature(msg, sig)) + + // ---- + // Test cross packages verification + stdPrivKey := stded25519.PrivateKey(privKey.Key) + stdPubKey := stdPrivKey.Public().(stded25519.PublicKey) + + assert.Equal(t, stdPubKey, pubKey.(*ed25519.PubKey).Key) + assert.Equal(t, stdPrivKey, privKey.Key) + assert.True(t, stded25519.Verify(stdPubKey, msg, sig)) + sig2 := stded25519.Sign(stdPrivKey, msg) + assert.True(t, pubKey.VerifySignature(msg, sig2)) + + // ---- + // Mutate the signature, just one bit. + // TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10 + sig[7] ^= byte(0x01) + assert.False(t, pubKey.VerifySignature(msg, sig)) +} + +func TestPubKeyEquals(t *testing.T) { + ed25519PubKey := ed25519.GenPrivKey().PubKey().(*ed25519.PubKey) + + testCases := []struct { + msg string + pubKey cryptotypes.PubKey + other cryptotypes.PubKey + expectEq bool + }{ + { + "different bytes", + ed25519PubKey, + ed25519.GenPrivKey().PubKey(), + false, + }, + { + "equals", + ed25519PubKey, + &ed25519.PubKey{ + Key: ed25519PubKey.Key, + }, + true, + }, + { + "different types", + ed25519PubKey, + secp256k1.GenPrivKey().PubKey(), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := tc.pubKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + +func TestPrivKeyEquals(t *testing.T) { + ed25519PrivKey := ed25519.GenPrivKey() + + testCases := []struct { + msg string + privKey cryptotypes.PrivKey + other cryptotypes.PrivKey + expectEq bool + }{ + { + "different bytes", + ed25519PrivKey, + ed25519.GenPrivKey(), + false, + }, + { + "equals", + ed25519PrivKey, + &ed25519.PrivKey{ + Key: ed25519PrivKey.Key, + }, + true, + }, + { + "different types", + ed25519PrivKey, + secp256k1.GenPrivKey(), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := tc.privKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + +func TestMarshalAmino(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + privKey := ed25519.GenPrivKey() + pubKey := privKey.PubKey().(*ed25519.PubKey) + + testCases := []struct { + desc string + msg codec.AminoMarshaler + typ interface{} + expBinary []byte + expJSON string + }{ + { + "ed25519 private key", + privKey, + &ed25519.PrivKey{}, + append([]byte{64}, privKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(privKey.Bytes()) + "\"", + }, + { + "ed25519 public key", + pubKey, + &ed25519.PubKey{}, + append([]byte{32}, pubKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(pubKey.Bytes()) + "\"", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Do a round trip of encoding/decoding binary. + bz, err := aminoCdc.MarshalBinaryBare(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expBinary, bz) + + err = aminoCdc.UnmarshalBinaryBare(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + + // Do a round trip of encoding/decoding JSON. + bz, err = aminoCdc.MarshalJSON(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expJSON, string(bz)) + + err = aminoCdc.UnmarshalJSON(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + }) + } +} + +func TestMarshalAmino_BackwardsCompatibility(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + // Create Tendermint keys. + tmPrivKey := tmed25519.GenPrivKey() + tmPubKey := tmPrivKey.PubKey() + // Create our own keys, with the same private key as Tendermint's. + privKey := &ed25519.PrivKey{Key: []byte(tmPrivKey)} + pubKey := privKey.PubKey().(*ed25519.PubKey) + + testCases := []struct { + desc string + tmKey interface{} + ourKey interface{} + marshalFn func(o interface{}) ([]byte, error) + }{ + { + "ed25519 private key, binary", + tmPrivKey, + privKey, + aminoCdc.MarshalBinaryBare, + }, + { + "ed25519 private key, JSON", + tmPrivKey, + privKey, + aminoCdc.MarshalJSON, + }, + { + "ed25519 public key, binary", + tmPubKey, + pubKey, + aminoCdc.MarshalBinaryBare, + }, + { + "ed25519 public key, JSON", + tmPubKey, + pubKey, + aminoCdc.MarshalJSON, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Make sure Amino encoding override is not breaking backwards compatibility. + bz1, err := tc.marshalFn(tc.tmKey) + require.NoError(t, err) + bz2, err := tc.marshalFn(tc.ourKey) + require.NoError(t, err) + require.Equal(t, bz1, bz2) + }) + } +} diff --git a/crypto/keys/ed25519/keys.pb.go b/crypto/keys/ed25519/keys.pb.go new file mode 100644 index 000000000000..35a98cf05861 --- /dev/null +++ b/crypto/keys/ed25519/keys.pb.go @@ -0,0 +1,499 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/ed25519/keys.proto + +package ed25519 + +import ( + crypto_ed25519 "crypto/ed25519" + 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 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. +type PubKey struct { + Key crypto_ed25519.PublicKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PublicKey" json:"key,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_48fe3336771e732d, []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() crypto_ed25519.PublicKey { + if m != nil { + return m.Key + } + return nil +} + +// PrivKey defines a ed25519 private key. +type PrivKey struct { + Key crypto_ed25519.PrivateKey `protobuf:"bytes,1,opt,name=key,proto3,casttype=crypto/ed25519.PrivateKey" 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_48fe3336771e732d, []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() crypto_ed25519.PrivateKey { + if m != nil { + return m.Key + } + return nil +} + +func init() { + proto.RegisterType((*PubKey)(nil), "cosmos.crypto.ed25519.PubKey") + proto.RegisterType((*PrivKey)(nil), "cosmos.crypto.ed25519.PrivKey") +} + +func init() { proto.RegisterFile("cosmos/crypto/ed25519/keys.proto", fileDescriptor_48fe3336771e732d) } + +var fileDescriptor_48fe3336771e732d = []byte{ + // 221 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, 0x4f, 0x4d, 0x31, 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, 0xec, 0xb8, 0xd8, 0x02, 0x4a, 0x93, 0xbc, 0x53, 0x2b, 0x85, 0xf4, 0xb8, + 0x98, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x9c, 0x64, 0x7e, 0xdd, 0x93, 0x97, + 0x40, 0xb5, 0x42, 0x2f, 0xa0, 0x34, 0x29, 0x27, 0x33, 0xd9, 0x3b, 0xb5, 0x32, 0x08, 0xa4, 0xd0, + 0x8a, 0x65, 0xc6, 0x02, 0x79, 0x06, 0x25, 0x2b, 0x2e, 0xf6, 0x80, 0xa2, 0xcc, 0x32, 0x90, 0x01, + 0xfa, 0xc8, 0x06, 0xc8, 0xfe, 0xba, 0x27, 0x2f, 0x89, 0x6e, 0x40, 0x51, 0x66, 0x59, 0x62, 0x49, + 0x2a, 0xcc, 0x04, 0x27, 0xaf, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, + 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, + 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0xf9, 0x17, 0x4c, 0xe9, + 0x16, 0xa7, 0x64, 0xc3, 0xbc, 0x0e, 0xf2, 0x32, 0xcc, 0xec, 0x24, 0x36, 0xb0, 0x77, 0x8c, 0x01, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xb0, 0xd8, 0x01, 0xc0, 0x1f, 0x01, 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/hd/fundraiser_test.go b/crypto/keys/hd/fundraiser_test.go deleted file mode 100644 index 6fa4ca725f0d..000000000000 --- a/crypto/keys/hd/fundraiser_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package hd - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" - "testing" - - "github.com/stretchr/testify/require" - - bip39 "github.com/cosmos/go-bip39" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" -) - -type addrData struct { - Mnemonic string - Master string - Seed string - Priv string - Pub string - Addr string -} - -func initFundraiserTestVectors(t *testing.T) []addrData { - // NOTE: atom fundraiser address - // var hdPath string = "m/44'/118'/0'/0/0" - var hdToAddrTable []addrData - - b, err := ioutil.ReadFile("test.json") - if err != nil { - t.Fatalf("could not read fundraiser test vector file (test.json): %s", err) - } - - err = json.Unmarshal(b, &hdToAddrTable) - if err != nil { - t.Fatalf("could not decode test vectors (test.json): %s", err) - } - return hdToAddrTable -} - -func TestFundraiserCompatibility(t *testing.T) { - hdToAddrTable := initFundraiserTestVectors(t) - - for i, d := range hdToAddrTable { - privB, _ := hex.DecodeString(d.Priv) - pubB, _ := hex.DecodeString(d.Pub) - addrB, _ := hex.DecodeString(d.Addr) - seedB, _ := hex.DecodeString(d.Seed) - masterB, _ := hex.DecodeString(d.Master) - - seed := bip39.NewSeed(d.Mnemonic, "") - - t.Log("================================") - t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic) - - master, ch := ComputeMastersFromSeed(seed) - priv, err := DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0") - require.NoError(t, err) - pub := secp256k1.PrivKeySecp256k1(priv).PubKey() - - t.Log("\tNODEJS GOLANG\n") - t.Logf("SEED \t%X %X\n", seedB, seed) - t.Logf("MSTR \t%X %X\n", masterB, master) - t.Logf("PRIV \t%X %X\n", privB, priv) - t.Logf("PUB \t%X %X\n", pubB, pub) - - require.Equal(t, seedB, seed) - require.Equal(t, master[:], masterB, fmt.Sprintf("Expected masters to match for %d", i)) - require.Equal(t, priv[:], privB, "Expected priv keys to match") - var pubBFixed [33]byte - copy(pubBFixed[:], pubB) - require.Equal(t, pub, secp256k1.PubKeySecp256k1(pubBFixed), fmt.Sprintf("Expected pub keys to match for %d", i)) - - addr := pub.Address() - t.Logf("ADDR \t%X %X\n", addrB, addr) - require.Equal(t, addr, crypto.Address(addrB), fmt.Sprintf("Expected addresses to match %d", i)) - - } -} diff --git a/crypto/keys/hd/hdpath.go b/crypto/keys/hd/hdpath.go deleted file mode 100644 index a92d79be1f21..000000000000 --- a/crypto/keys/hd/hdpath.go +++ /dev/null @@ -1,257 +0,0 @@ -// Package hd provides basic functionality Hierarchical Deterministic Wallets. -// -// The user must understand the overall concept of the BIP 32 and the BIP 44 specs: -// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki -// -// In combination with the bip39 package in go-crypto this package provides the functionality for deriving keys using a -// BIP 44 HD path, or, more general, by passing a BIP 32 path. -// -// In particular, this package (together with bip39) provides all necessary functionality to derive keys from -// mnemonics generated during the cosmos fundraiser. -package hd - -import ( - "crypto/hmac" - "crypto/sha512" - - "encoding/binary" - "errors" - "fmt" - "math/big" - "strconv" - "strings" - - "github.com/btcsuite/btcd/btcec" -) - -// BIP44Params wraps BIP 44 params (5 level BIP 32 path). -// To receive a canonical string representation ala -// m / purpose' / coinType' / account' / change / addressIndex -// call String() on a BIP44Params instance. -type BIP44Params struct { - Purpose uint32 `json:"purpose"` - CoinType uint32 `json:"coinType"` - Account uint32 `json:"account"` - Change bool `json:"change"` - AddressIndex uint32 `json:"addressIndex"` -} - -// NewParams creates a BIP 44 parameter object from the params: -// m / purpose' / coinType' / account' / change / addressIndex -func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32) *BIP44Params { - return &BIP44Params{ - Purpose: purpose, - CoinType: coinType, - Account: account, - Change: change, - AddressIndex: addressIdx, - } -} - -// Parse the BIP44 path and unmarshal into the struct. -func NewParamsFromPath(path string) (*BIP44Params, error) { - spl := strings.Split(path, "/") - if len(spl) != 5 { - return nil, fmt.Errorf("path length is wrong. Expected 5, got %d", len(spl)) - } - - // Check items can be parsed - purpose, err := hardenedInt(spl[0]) - if err != nil { - return nil, err - } - coinType, err := hardenedInt(spl[1]) - if err != nil { - return nil, err - } - account, err := hardenedInt(spl[2]) - if err != nil { - return nil, err - } - change, err := hardenedInt(spl[3]) - if err != nil { - return nil, err - } - - addressIdx, err := hardenedInt(spl[4]) - if err != nil { - return nil, err - } - - // Confirm valid values - if spl[0] != "44'" { - return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0]) - } - - if !isHardened(spl[1]) || !isHardened(spl[2]) { - return nil, - fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2]) - } - if isHardened(spl[3]) || isHardened(spl[4]) { - return nil, - fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4]) - } - - if !(change == 0 || change == 1) { - return nil, fmt.Errorf("change field can only be 0 or 1") - } - - return &BIP44Params{ - Purpose: purpose, - CoinType: coinType, - Account: account, - Change: change > 0, - AddressIndex: addressIdx, - }, nil -} - -func hardenedInt(field string) (uint32, error) { - field = strings.TrimSuffix(field, "'") - i, err := strconv.Atoi(field) - if err != nil { - return 0, err - } - if i < 0 { - return 0, fmt.Errorf("fields must not be negative. got %d", i) - } - return uint32(i), nil -} - -func isHardened(field string) bool { - return strings.HasSuffix(field, "'") -} - -// NewFundraiserParams creates a BIP 44 parameter object from the params: -// m / 44' / coinType' / account' / 0 / address_index -// The fixed parameters (purpose', coin_type', and change) are determined by what was used in the fundraiser. -func NewFundraiserParams(account, coinType, addressIdx uint32) *BIP44Params { - return NewParams(44, coinType, account, false, addressIdx) -} - -// DerivationPath returns the BIP44 fields as an array. -func (p BIP44Params) DerivationPath() []uint32 { - change := uint32(0) - if p.Change { - change = 1 - } - return []uint32{ - p.Purpose, - p.CoinType, - p.Account, - change, - p.AddressIndex, - } -} - -func (p BIP44Params) String() string { - var changeStr string - if p.Change { - changeStr = "1" - } else { - changeStr = "0" - } - // m / Purpose' / coin_type' / Account' / Change / address_index - return fmt.Sprintf("%d'/%d'/%d'/%s/%d", - p.Purpose, - p.CoinType, - p.Account, - changeStr, - p.AddressIndex) -} - -// ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex. -func ComputeMastersFromSeed(seed []byte) (secret [32]byte, chainCode [32]byte) { - masterSecret := []byte("Bitcoin seed") - secret, chainCode = i64(masterSecret, seed) - - return -} - -// DerivePrivateKeyForPath derives the private key by following the BIP 32/44 path from privKeyBytes, -// using the given chainCode. -func DerivePrivateKeyForPath(privKeyBytes [32]byte, chainCode [32]byte, path string) ([32]byte, error) { - data := privKeyBytes - parts := strings.Split(path, "/") - for _, part := range parts { - // do we have an apostrophe? - harden := part[len(part)-1:] == "'" - // harden == private derivation, else public derivation: - if harden { - part = part[:len(part)-1] - } - idx, err := strconv.Atoi(part) - if err != nil { - return [32]byte{}, fmt.Errorf("invalid BIP 32 path: %s", err) - } - if idx < 0 { - return [32]byte{}, errors.New("invalid BIP 32 path: index negative ot too large") - } - data, chainCode = derivePrivateKey(data, chainCode, uint32(idx), harden) - } - var derivedKey [32]byte - n := copy(derivedKey[:], data[:]) - if n != 32 || len(data) != 32 { - return [32]byte{}, fmt.Errorf("expected a (secp256k1) key of length 32, got length: %v", len(data)) - } - - return derivedKey, nil -} - -// derivePrivateKey derives the private key with index and chainCode. -// If harden is true, the derivation is 'hardened'. -// It returns the new private key and new chain code. -// For more information on hardened keys see: -// - https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki -func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, harden bool) ([32]byte, [32]byte) { - var data []byte - if harden { - index |= 0x80000000 - data = append([]byte{byte(0)}, privKeyBytes[:]...) - } else { - // this can't return an error: - _, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:]) - pubkeyBytes := ecPub.SerializeCompressed() - data = pubkeyBytes - - /* By using btcec, we can remove the dependency on tendermint/crypto/secp256k1 - pubkey := secp256k1.PrivKeySecp256k1(privKeyBytes).PubKey() - public := pubkey.(secp256k1.PubKeySecp256k1) - data = public[:] - */ - } - data = append(data, uint32ToBytes(index)...) - data2, chainCode2 := i64(chainCode[:], data) - x := addScalars(privKeyBytes[:], data2[:]) - return x, chainCode2 -} - -// modular big endian addition -func addScalars(a []byte, b []byte) [32]byte { - aInt := new(big.Int).SetBytes(a) - bInt := new(big.Int).SetBytes(b) - sInt := new(big.Int).Add(aInt, bInt) - x := sInt.Mod(sInt, btcec.S256().N).Bytes() - x2 := [32]byte{} - copy(x2[32-len(x):], x) - return x2 -} - -func uint32ToBytes(i uint32) []byte { - b := [4]byte{} - binary.BigEndian.PutUint32(b[:], i) - return b[:] -} - -// i64 returns the two halfs of the SHA512 HMAC of key and data. -func i64(key []byte, data []byte) (il [32]byte, ir [32]byte) { - mac := hmac.New(sha512.New, key) - // sha512 does not err - _, _ = mac.Write(data) - - I := mac.Sum(nil) - copy(il[:], I[:32]) - copy(ir[:], I[32:]) - - return -} diff --git a/crypto/keys/hd/hdpath_test.go b/crypto/keys/hd/hdpath_test.go deleted file mode 100644 index a5110e2cbde2..000000000000 --- a/crypto/keys/hd/hdpath_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package hd - -import ( - "encoding/hex" - "fmt" - "testing" - - "github.com/cosmos/cosmos-sdk/types" - - bip39 "github.com/cosmos/go-bip39" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var defaultBIP39Passphrase = "" - -// return bip39 seed with empty passphrase -func mnemonicToSeed(mnemonic string) []byte { - return bip39.NewSeed(mnemonic, defaultBIP39Passphrase) -} - -// nolint:govet -func ExampleStringifyPathParams() { - path := NewParams(44, 0, 0, false, 0) - fmt.Println(path.String()) - path = NewParams(44, 33, 7, true, 9) - fmt.Println(path.String()) - // Output: - // 44'/0'/0'/0/0 - // 44'/33'/7'/1/9 -} - -func TestStringifyFundraiserPathParams(t *testing.T) { - path := NewFundraiserParams(4, types.CoinType, 22) - require.Equal(t, "44'/118'/4'/0/22", path.String()) - - path = NewFundraiserParams(4, types.CoinType, 57) - require.Equal(t, "44'/118'/4'/0/57", path.String()) - - path = NewFundraiserParams(4, 12345, 57) - require.Equal(t, "44'/12345'/4'/0/57", path.String()) -} - -func TestPathToArray(t *testing.T) { - path := NewParams(44, 118, 1, false, 4) - require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath())) - - path = NewParams(44, 118, 2, true, 15) - require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath())) -} - -func TestParamsFromPath(t *testing.T) { - goodCases := []struct { - params *BIP44Params - path string - }{ - {&BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"}, - {&BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"}, - {&BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"}, - {&BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"}, - {&BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"}, - {&BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"}, - {&BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"}, - } - - for i, c := range goodCases { - params, err := NewParamsFromPath(c.path) - errStr := fmt.Sprintf("%d %v", i, c) - assert.NoError(t, err, errStr) - assert.EqualValues(t, c.params, params, errStr) - assert.Equal(t, c.path, c.params.String()) - } - - badCases := []struct { - path string - }{ - {"43'/0'/0'/0/0"}, // doesnt start with 44 - {"44'/1'/0'/0/0/5"}, // too many fields - {"44'/0'/1'/0"}, // too few fields - {"44'/0'/0'/2/0"}, // change field can only be 0/1 - {"44/0'/0'/0/0"}, // first field needs ' - {"44'/0/0'/0/0"}, // second field needs ' - {"44'/0'/0/0/0"}, // third field needs ' - {"44'/0'/0'/0'/0"}, // fourth field must not have ' - {"44'/0'/0'/0/0'"}, // fifth field must not have ' - {"44'/-1'/0'/0/0"}, // no negatives - {"44'/0'/0'/-1/0"}, // no negatives - {"a'/0'/0'/-1/0"}, // valid values - {"0/X/0'/-1/0"}, // valid values - {"44'/0'/X/-1/0"}, // valid values - {"44'/0'/0'/%/0"}, // valid values - {"44'/0'/0'/0/%"}, // valid values - } - - for i, c := range badCases { - params, err := NewParamsFromPath(c.path) - errStr := fmt.Sprintf("%d %v", i, c) - assert.Nil(t, params, errStr) - assert.Error(t, err, errStr) - } - -} - -// nolint:govet -func ExampleSomeBIP32TestVecs() { - - seed := mnemonicToSeed("barrel original fuel morning among eternal " + - "filter ball stove pluck matrix mechanic") - master, ch := ComputeMastersFromSeed(seed) - fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)") - fmt.Println() - // cosmos - priv, err := DerivePrivateKeyForPath(master, ch, types.FullFundraiserPath) - if err != nil { - fmt.Println("INVALID") - } else { - fmt.Println(hex.EncodeToString(priv[:])) - } - // bitcoin - priv, err = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0") - if err != nil { - fmt.Println("INVALID") - } else { - fmt.Println(hex.EncodeToString(priv[:])) - } - // ether - priv, err = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0") - if err != nil { - fmt.Println("INVALID") - } else { - fmt.Println(hex.EncodeToString(priv[:])) - } - // INVALID - priv, err = DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0") - if err != nil { - fmt.Println("INVALID") - } else { - fmt.Println(hex.EncodeToString(priv[:])) - } - priv, err = DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0") - if err != nil { - fmt.Println("INVALID") - } else { - fmt.Println(hex.EncodeToString(priv[:])) - } - - fmt.Println() - fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html") - fmt.Println() - - seed = mnemonicToSeed( - "advice process birth april short trust crater change bacon monkey medal garment " + - "gorilla ranch hour rival razor call lunar mention taste vacant woman sister") - master, ch = ComputeMastersFromSeed(seed) - priv, _ = DerivePrivateKeyForPath(master, ch, "44'/1'/1'/0/4") - fmt.Println(hex.EncodeToString(priv[:])) - - seed = mnemonicToSeed("idea naive region square margin day captain habit " + - "gun second farm pact pulse someone armed") - master, ch = ComputeMastersFromSeed(seed) - priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/420") - fmt.Println(hex.EncodeToString(priv[:])) - - fmt.Println() - fmt.Println("BIP 32 example") - fmt.Println() - - // bip32 path: m/0/7 - seed = mnemonicToSeed("monitor flock loyal sick object grunt duty ride develop assault harsh history") - master, ch = ComputeMastersFromSeed(seed) - priv, _ = DerivePrivateKeyForPath(master, ch, "0/7") - fmt.Println(hex.EncodeToString(priv[:])) - - // Output: keys from fundraiser test-vector (cosmos, bitcoin, ether) - // - // bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c - // e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d - // 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc - // INVALID - // INVALID - // - // keys generated via https://coinomi.com/recovery-phrase-tool.html - // - // a61f10c5fecf40c084c94fa54273b6f5d7989386be4a37669e6d6f7b0169c163 - // 32c4599843de3ef161a629a461d12c60b009b676c35050be5f7ded3a3b23501f - // - // BIP 32 example - // - // c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78 -} diff --git a/crypto/keys/internal/benchmarking/bench.go b/crypto/keys/internal/benchmarking/bench.go new file mode 100644 index 000000000000..a789da91f9c9 --- /dev/null +++ b/crypto/keys/internal/benchmarking/bench.go @@ -0,0 +1,92 @@ +package benchmarking + +import ( + "io" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// The code in this file is adapted from agl/ed25519. +// As such it is under the following license. +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found at the bottom of this file. + +type zeroReader struct{} + +func (zeroReader) Read(buf []byte) (int, error) { + for i := range buf { + buf[i] = 0 + } + return len(buf), nil +} + +// BenchmarkKeyGeneration benchmarks the given key generation algorithm using +// a dummy reader. +func BenchmarkKeyGeneration(b *testing.B, generateKey func(reader io.Reader) types.PrivKey) { + var zero zeroReader + for i := 0; i < b.N; i++ { + generateKey(zero) + } +} + +// BenchmarkSigning benchmarks the given signing algorithm using +// the provided privkey. +func BenchmarkSigning(b *testing.B, priv types.PrivKey) { + message := []byte("Hello, world!") + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := priv.Sign(message) + + if err != nil { + b.FailNow() + } + } +} + +// BenchmarkVerification benchmarks the given verification algorithm using +// the provided privkey on a constant message. +func BenchmarkVerification(b *testing.B, priv types.PrivKey) { + pub := priv.PubKey() + // use a short message, so this time doesn't get dominated by hashing. + message := []byte("Hello, world!") + signature, err := priv.Sign(message) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + pub.VerifySignature(message, signature) + } +} + +// Below is the aforementioned license. + +// Copyright (c) 2012 The Go Authors. All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: + +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go deleted file mode 100644 index 9d95a5ccda2b..000000000000 --- a/crypto/keys/keybase.go +++ /dev/null @@ -1,475 +0,0 @@ -package keys - -import ( - "fmt" - "reflect" - "strings" - - "github.com/pkg/errors" - tmcrypto "github.com/tendermint/tendermint/crypto" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" - "github.com/cosmos/cosmos-sdk/types" -) - -var _ Keybase = dbKeybase{} - -// Language is a language to create the BIP 39 mnemonic in. -// Currently, only english is supported though. -// Find a list of all supported languages in the BIP 39 spec (word lists). -type Language int - -//noinspection ALL -const ( - // English is the default language to create a mnemonic. - // It is the only supported language by this package. - English Language = iota + 1 - // Japanese is currently not supported. - Japanese - // Korean is currently not supported. - Korean - // Spanish is currently not supported. - Spanish - // ChineseSimplified is currently not supported. - ChineseSimplified - // ChineseTraditional is currently not supported. - ChineseTraditional - // French is currently not supported. - French - // Italian is currently not supported. - Italian - addressSuffix = "address" - infoSuffix = "info" -) - -const ( - // used for deriving seed from mnemonic - DefaultBIP39Passphrase = "" - - // bits of entropy to draw when creating a mnemonic - defaultEntropySize = 256 -) - -var ( - // ErrUnsupportedSigningAlgo is raised when the caller tries to use a - // different signing scheme than secp256k1. - ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo") - - // ErrUnsupportedLanguage is raised when the caller tries to use a - // different language than english for creating a mnemonic sentence. - ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported") -) - -// dbKeybase combines encryption and storage implementation to provide a -// full-featured key manager. -// -// NOTE: dbKeybase will be deprecated in favor of keyringKeybase. -type dbKeybase struct { - base baseKeybase - db dbm.DB -} - -// newDBKeybase creates a new dbKeybase instance using the provided DB for -// reading and writing keys. -func newDBKeybase(db dbm.DB, opts ...KeybaseOption) Keybase { - return dbKeybase{ - base: newBaseKeybase(opts...), - db: db, - } -} - -// NewInMemory creates a transient keybase on top of in-memory storage -// instance useful for testing purposes and on-the-fly key generation. -// Keybase options can be applied when generating this new Keybase. -func NewInMemory(opts ...KeybaseOption) Keybase { return newDBKeybase(dbm.NewMemDB(), opts...) } - -// CreateMnemonic generates a new key and persists it to storage, encrypted -// using the provided password. It returns the generated mnemonic and the key Info. -// It returns an error if it fails to generate a key for the given key algorithm -// type, or if another key is already stored under the same name. -func (kb dbKeybase) CreateMnemonic( - name string, language Language, passwd string, algo SigningAlgo, -) (info Info, mnemonic string, err error) { - - return kb.base.CreateMnemonic(kb, name, language, passwd, algo) -} - -// CreateAccount converts a mnemonic to a private key and persists it, encrypted -// with the given password. -func (kb dbKeybase) CreateAccount( - name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo, -) (Info, error) { - - return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo) -} - -// CreateLedger creates a new locally-stored reference to a Ledger keypair. -// It returns the created key info and an error if the Ledger could not be queried. -func (kb dbKeybase) CreateLedger( - name string, algo SigningAlgo, hrp string, account, index uint32, -) (Info, error) { - - return kb.base.CreateLedger(kb, name, algo, hrp, account, index) -} - -// CreateOffline creates a new reference to an offline keypair. It returns the -// created key info. -func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) { - return kb.base.writeOfflineKey(kb, name, pub, algo), nil -} - -// CreateMulti creates a new reference to a multisig (offline) keypair. It -// returns the created key info. -func (kb dbKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) { - return kb.base.writeMultisigKey(kb, name, pub), nil -} - -// List returns the keys from storage in alphabetical order. -func (kb dbKeybase) List() ([]Info, error) { - var res []Info - - iter, err := kb.db.Iterator(nil, nil) - if err != nil { - return nil, err - } - - defer iter.Close() - - for ; iter.Valid(); iter.Next() { - key := string(iter.Key()) - - // need to include only keys in storage that have an info suffix - if strings.HasSuffix(key, infoSuffix) { - info, err := unmarshalInfo(iter.Value()) - if err != nil { - return nil, err - } - - res = append(res, info) - } - } - - return res, nil -} - -// Get returns the public information about one key. -func (kb dbKeybase) Get(name string) (Info, error) { - bs, err := kb.db.Get(infoKey(name)) - if err != nil { - return nil, err - } - - if len(bs) == 0 { - return nil, keyerror.NewErrKeyNotFound(name) - } - - return unmarshalInfo(bs) -} - -// GetByAddress returns Info based on a provided AccAddress. An error is returned -// if the address does not exist. -func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) { - ik, err := kb.db.Get(addrKey(address)) - if err != nil { - return nil, err - } - - if len(ik) == 0 { - return nil, fmt.Errorf("key with address %s not found", address) - } - - bs, err := kb.db.Get(ik) - if err != nil { - return nil, err - } - - return unmarshalInfo(bs) -} - -// Sign signs the msg with the named key. It returns an error if the key doesn't -// exist or the decryption fails. -func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { - info, err := kb.Get(name) - if err != nil { - return - } - - var priv tmcrypto.PrivKey - - switch i := info.(type) { - case localInfo: - if i.PrivKeyArmor == "" { - err = fmt.Errorf("private key not available") - return - } - - priv, _, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase) - if err != nil { - return nil, nil, err - } - - case ledgerInfo: - return kb.base.SignWithLedger(info, msg) - - case offlineInfo, multiInfo: - return kb.base.DecodeSignature(info, msg) - } - - sig, err = priv.Sign(msg) - if err != nil { - return nil, nil, err - } - - return sig, priv.PubKey(), nil -} - -// ExportPrivateKeyObject returns a PrivKey object given the key name and -// passphrase. An error is returned if the key does not exist or if the Info for -// the key is invalid. -func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) { - info, err := kb.Get(name) - if err != nil { - return nil, err - } - - var priv tmcrypto.PrivKey - - switch i := info.(type) { - case localInfo: - linfo := i - if linfo.PrivKeyArmor == "" { - err = fmt.Errorf("private key not available") - return nil, err - } - - priv, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase) - if err != nil { - return nil, err - } - - case ledgerInfo, offlineInfo, multiInfo: - return nil, errors.New("only works on local private keys") - } - - return priv, nil -} - -func (kb dbKeybase) Export(name string) (armor string, err error) { - bz, err := kb.db.Get(infoKey(name)) - if err != nil { - return "", err - } - - if bz == nil { - return "", fmt.Errorf("no key to export with name %s", name) - } - - return mintkey.ArmorInfoBytes(bz), nil -} - -// ExportPubKey returns public keys in ASCII armored format. It retrieves a Info -// object by its name and return the public key in a portable format. -func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) { - bz, err := kb.db.Get(infoKey(name)) - if err != nil { - return "", err - } - - if bz == nil { - return "", fmt.Errorf("no key to export with name %s", name) - } - - info, err := unmarshalInfo(bz) - if err != nil { - return - } - - return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil -} - -// ExportPrivKey returns a private key in ASCII armored format. -// It returns an error if the key does not exist or a wrong encryption passphrase -// is supplied. -func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string, - encryptPassphrase string) (armor string, err error) { - priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase) - if err != nil { - return "", err - } - - info, err := kb.Get(name) - if err != nil { - return "", err - } - - return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil -} - -// ImportPrivKey imports a private key in ASCII armor format. It returns an -// error if a key with the same name exists or a wrong encryption passphrase is -// supplied. -func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string) error { - if _, err := kb.Get(name); err == nil { - return errors.New("Cannot overwrite key " + name) - } - - privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase) - if err != nil { - return errors.Wrap(err, "couldn't import private key") - } - - kb.writeLocalKey(name, privKey, passphrase, SigningAlgo(algo)) - return nil -} - -func (kb dbKeybase) Import(name string, armor string) (err error) { - bz, err := kb.db.Get(infoKey(name)) - if err != nil { - return err - } - - if len(bz) > 0 { - return errors.New("cannot overwrite data for name " + name) - } - - infoBytes, err := mintkey.UnarmorInfoBytes(armor) - if err != nil { - return - } - - kb.db.Set(infoKey(name), infoBytes) - return nil -} - -// ImportPubKey imports ASCII-armored public keys. Store a new Info object holding -// a public key only, i.e. it will not be possible to sign with it as it lacks the -// secret key. -func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) { - bz, err := kb.db.Get(infoKey(name)) - if err != nil { - return err - } - - if len(bz) > 0 { - return errors.New("cannot overwrite data for name " + name) - } - - pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor) - if err != nil { - return - } - - pubKey, err := cryptoAmino.PubKeyFromBytes(pubBytes) - if err != nil { - return - } - - kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo)) - return -} - -// Delete removes key forever, but we must present the proper passphrase before -// deleting it (for security). It returns an error if the key doesn't exist or -// passphrases don't match. Passphrase is ignored when deleting references to -// offline and Ledger / HW wallet keys. -func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error { - // verify we have the proper password before deleting - info, err := kb.Get(name) - if err != nil { - return err - } - - if linfo, ok := info.(localInfo); ok && !skipPass { - if _, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil { - return err - } - } - - kb.db.DeleteSync(addrKey(info.GetAddress())) - kb.db.DeleteSync(infoKey(name)) - - return nil -} - -// Update changes the passphrase with which an already stored key is -// encrypted. -// -// oldpass must be the current passphrase used for encryption, -// getNewpass is a function to get the passphrase to permanently replace -// the current passphrase -func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error { - info, err := kb.Get(name) - if err != nil { - return err - } - - switch i := info.(type) { - case localInfo: - linfo := i - - key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass) - if err != nil { - return err - } - - newpass, err := getNewpass() - if err != nil { - return err - } - - kb.writeLocalKey(name, key, newpass, i.GetAlgo()) - return nil - - default: - return fmt.Errorf("locally stored key required. Received: %v", reflect.TypeOf(info).String()) - } -} - -// CloseDB releases the lock and closes the storage backend. -func (kb dbKeybase) CloseDB() { - kb.db.Close() -} - -// SupportedAlgos returns a list of supported signing algorithms. -func (kb dbKeybase) SupportedAlgos() []SigningAlgo { - return kb.base.SupportedAlgos() -} - -// SupportedAlgosLedger returns a list of supported ledger signing algorithms. -func (kb dbKeybase) SupportedAlgosLedger() []SigningAlgo { - return kb.base.SupportedAlgosLedger() -} - -func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info { - // encrypt private key using passphrase - privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase, string(algo)) - - // make Info - pub := priv.PubKey() - info := newLocalInfo(name, pub, privArmor, algo) - - kb.writeInfo(name, info) - return info -} - -func (kb dbKeybase) writeInfo(name string, info Info) { - // write the info by key - key := infoKey(name) - serializedInfo := marshalInfo(info) - - kb.db.SetSync(key, serializedInfo) - - // store a pointer to the infokey by address for fast lookup - kb.db.SetSync(addrKey(info.GetAddress()), key) -} - -func addrKey(address types.AccAddress) []byte { - return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix)) -} - -func infoKey(name string) []byte { - return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) -} diff --git a/crypto/keys/keybase_base.go b/crypto/keys/keybase_base.go deleted file mode 100644 index 3003b08828ce..000000000000 --- a/crypto/keys/keybase_base.go +++ /dev/null @@ -1,298 +0,0 @@ -package keys - -import ( - "bufio" - "fmt" - "os" - - "github.com/cosmos/go-bip39" - "github.com/pkg/errors" - tmcrypto "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/types" -) - -type ( - kbOptions struct { - keygenFunc PrivKeyGenFunc - deriveFunc DeriveKeyFunc - supportedAlgos []SigningAlgo - supportedAlgosLedger []SigningAlgo - } - - // baseKeybase is an auxiliary type that groups Keybase storage agnostic features - // together. - baseKeybase struct { - options kbOptions - } - - keyWriter interface { - writeLocalKeyer - infoWriter - } - - writeLocalKeyer interface { - writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info - } - - infoWriter interface { - writeInfo(name string, info Info) - } -) - -// WithKeygenFunc applies an overridden key generation function to generate the private key. -func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption { - return func(o *kbOptions) { - o.keygenFunc = f - } -} - -// WithDeriveFunc applies an overridden key derivation function to generate the private key. -func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption { - return func(o *kbOptions) { - o.deriveFunc = f - } -} - -// WithSupportedAlgos defines the list of accepted SigningAlgos. -func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption { - return func(o *kbOptions) { - o.supportedAlgos = algos - } -} - -// WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger. -func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption { - return func(o *kbOptions) { - o.supportedAlgosLedger = algos - } -} - -// newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type -func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase { - // Default options for keybase - options := kbOptions{ - keygenFunc: StdPrivKeyGen, - deriveFunc: StdDeriveKey, - supportedAlgos: []SigningAlgo{Secp256k1}, - supportedAlgosLedger: []SigningAlgo{Secp256k1}, - } - - for _, optionFn := range optionsFns { - optionFn(&options) - } - - return baseKeybase{options: options} -} - -// StdPrivKeyGen is the default PrivKeyGen function in the keybase. -// For now, it only supports Secp256k1 -func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) { - if algo == Secp256k1 { - return SecpPrivKeyGen(bz), nil - } - return nil, ErrUnsupportedSigningAlgo -} - -// SecpPrivKeyGen generates a secp256k1 private key from the given bytes -func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey { - var bzArr [32]byte - copy(bzArr[:], bz) - return secp256k1.PrivKeySecp256k1(bzArr) -} - -// SignWithLedger signs a binary message with the ledger device referenced by an Info object -// and returns the signed bytes and the public key. It returns an error if the device could -// not be queried or it returned an error. -func (kb baseKeybase) SignWithLedger(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { - i := info.(ledgerInfo) - priv, err := crypto.NewPrivKeyLedgerSecp256k1Unsafe(i.Path) - if err != nil { - return - } - - sig, err = priv.Sign(msg) - if err != nil { - return nil, nil, err - } - - return sig, priv.PubKey(), nil -} - -// DecodeSignature decodes a an length-prefixed binary signature from standard input -// and return it as a byte slice. -func (kb baseKeybase) DecodeSignature(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { - _, err = fmt.Fprintf(os.Stderr, "Message to sign:\n\n%s\n", msg) - if err != nil { - return nil, nil, err - } - - buf := bufio.NewReader(os.Stdin) - _, err = fmt.Fprintf(os.Stderr, "\nEnter Amino-encoded signature:\n") - if err != nil { - return nil, nil, err - } - - // will block until user inputs the signature - signed, err := buf.ReadString('\n') - if err != nil { - return nil, nil, err - } - - if err := CryptoCdc.UnmarshalBinaryLengthPrefixed([]byte(signed), sig); err != nil { - return nil, nil, errors.Wrap(err, "failed to decode signature") - } - - return sig, info.GetPubKey(), nil -} - -// CreateAccount creates an account Info object. -func (kb baseKeybase) CreateAccount( - keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo, -) (Info, error) { - - // create master key and derive first key for keyring - derivedPriv, err := kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo) - if err != nil { - return nil, err - } - - privKey, err := kb.options.keygenFunc(derivedPriv, algo) - if err != nil { - return nil, err - } - - var info Info - - if encryptPasswd != "" { - info = keyWriter.writeLocalKey(name, privKey, encryptPasswd, algo) - } else { - info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo) - } - - return info, nil -} - -// CreateLedger creates a new reference to a Ledger key pair. It returns a public -// key and a derivation path. It returns an error if the device could not be queried. -func (kb baseKeybase) CreateLedger( - w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32, -) (Info, error) { - - if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) { - return nil, ErrUnsupportedSigningAlgo - } - - coinType := types.GetConfig().GetCoinType() - hdPath := hd.NewFundraiserParams(account, coinType, index) - - priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp) - if err != nil { - return nil, err - } - - return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil -} - -// CreateMnemonic generates a new key with the given algorithm and language pair. -func (kb baseKeybase) CreateMnemonic( - keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo, -) (info Info, mnemonic string, err error) { - - if language != English { - return nil, "", ErrUnsupportedLanguage - } - - if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) { - return nil, "", ErrUnsupportedSigningAlgo - } - - // Default number of words (24): This generates a mnemonic directly from the - // number of words by reading system entropy. - entropy, err := bip39.NewEntropy(defaultEntropySize) - if err != nil { - return nil, "", err - } - - mnemonic, err = bip39.NewMnemonic(entropy) - if err != nil { - return nil, "", err - } - - info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo) - if err != nil { - return nil, "", err - } - - return info, mnemonic, err -} - -func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info { - info := newLedgerInfo(name, pub, path, algo) - w.writeInfo(name, info) - return info -} - -func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info { - info := newOfflineInfo(name, pub, algo) - w.writeInfo(name, info) - return info -} - -func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.PubKey) Info { - info := NewMultiInfo(name, pub) - w.writeInfo(name, info) - return info -} - -// StdDeriveKey is the default DeriveKey function in the keybase. -// For now, it only supports Secp256k1 -func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) { - if algo == Secp256k1 { - return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath) - } - return nil, ErrUnsupportedSigningAlgo -} - -// SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path. -func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) { - seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) - if err != nil { - return nil, err - } - - masterPriv, ch := hd.ComputeMastersFromSeed(seed) - if len(hdPath) == 0 { - return masterPriv[:], nil - } - derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath) - return derivedKey[:], err -} - -// CreateHDPath returns BIP 44 object from account and index parameters. -func CreateHDPath(account uint32, index uint32) *hd.BIP44Params { - return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index) -} - -// SupportedAlgos returns a list of supported signing algorithms. -func (kb baseKeybase) SupportedAlgos() []SigningAlgo { - return kb.options.supportedAlgos -} - -// SupportedAlgosLedger returns a list of supported ledger signing algorithms. -func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo { - return kb.options.supportedAlgosLedger -} - -// IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms. -func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool { - for _, supportedAlgo := range supported { - if algo == supportedAlgo { - return true - } - } - return false -} diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go deleted file mode 100644 index 621ee46004cf..000000000000 --- a/crypto/keys/keybase_test.go +++ /dev/null @@ -1,495 +0,0 @@ -//nolint: goconst -package keys - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func init() { - mintkey.BcryptSecurityParameter = 1 -} - -const ( - nums = "1234" - foobar = "foobar" -) - -func TestLanguage(t *testing.T) { - kb := NewInMemory() - _, _, err := kb.CreateMnemonic("something", Japanese, "no_pass", Secp256k1) - assert.Error(t, err) - assert.Equal(t, "unsupported language: only english is supported", err.Error()) -} - -func TestCreateAccountInvalidMnemonic(t *testing.T) { - kb := NewInMemory() - _, err := kb.CreateAccount( - "some_account", - "malarkey pair crucial catch public canyon evil outer stage ten gym tornado", - "", "", CreateHDPath(0, 0).String(), Secp256k1) - assert.Error(t, err) - assert.Equal(t, "Invalid mnemonic", err.Error()) -} - -func TestCreateLedgerUnsupportedAlgo(t *testing.T) { - kb := NewInMemory() - - supportedLedgerAlgos := kb.SupportedAlgosLedger() - for _, supportedAlgo := range supportedLedgerAlgos { - if Ed25519 == supportedAlgo { - assert.FailNow(t, "Was not an unsupported algorithm") - } - } - - _, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1) - assert.Error(t, err) - assert.Equal(t, "unsupported signing algo", err.Error()) -} - -func TestCreateLedger(t *testing.T) { - kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519})) - - // test_cover and test_unit will result in different answers - // test_cover does not compile some dependencies so ledger is disabled - // test_unit may add a ledger mock - // both cases are acceptable - supportedLedgerAlgos := kb.SupportedAlgosLedger() - secpSupported := false - edSupported := false - for _, supportedAlgo := range supportedLedgerAlgos { - secpSupported = secpSupported || (supportedAlgo == Secp256k1) - edSupported = edSupported || (supportedAlgo == Ed25519) - } - assert.True(t, secpSupported) - assert.True(t, edSupported) - - ledger, err := kb.CreateLedger("some_account", Secp256k1, "fetch", 3, 1) - - if err != nil { - assert.Error(t, err) - assert.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) - assert.Nil(t, ledger) - t.Skip("ledger nano S: support for ledger devices is not available in this executable") - return - } - - // The mock is available, check that the address is correct - pubKey := ledger.GetPubKey() - pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) - assert.NoError(t, err) - assert.Equal(t, "fetchpub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqydkmf9x", pk) - - // Check that restoring the key gets the same results - restoredKey, err := kb.Get("some_account") - assert.NoError(t, err) - assert.NotNil(t, restoredKey) - assert.Equal(t, "some_account", restoredKey.GetName()) - assert.Equal(t, TypeLedger, restoredKey.GetType()) - pubKey = restoredKey.GetPubKey() - pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) - assert.NoError(t, err) - assert.Equal(t, "fetchpub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqydkmf9x", pk) - - path, err := restoredKey.GetPath() - assert.NoError(t, err) - assert.Equal(t, "44'/118'/3'/0/1", path.String()) -} - -// TestKeyManagement makes sure we can manipulate these keys well -func TestKeyManagement(t *testing.T) { - // make the storage with reasonable defaults - cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519})) - - // Test modified supported algos - supportedAlgos := cstore.SupportedAlgos() - secpSupported := false - edSupported := false - srSupported := false - for _, supportedAlgo := range supportedAlgos { - secpSupported = secpSupported || (supportedAlgo == Secp256k1) - edSupported = edSupported || (supportedAlgo == Ed25519) - srSupported = srSupported || (supportedAlgo == Sr25519) - } - assert.True(t, secpSupported) - assert.False(t, edSupported) - assert.True(t, srSupported) - - algo := Secp256k1 - n1, n2, n3 := "personal", "business", "other" - p1, p2 := nums, "really-secure!@#$" - - // Check empty state - l, err := cstore.List() - require.Nil(t, err) - assert.Empty(t, l) - - _, _, err = cstore.CreateMnemonic(n1, English, p1, Ed25519) - require.Error(t, err, "ed25519 keys are currently not supported by keybase") - - // create some keys - _, err = cstore.Get(n1) - require.Error(t, err) - i, _, err := cstore.CreateMnemonic(n1, English, p1, algo) - - require.NoError(t, err) - require.Equal(t, n1, i.GetName()) - _, _, err = cstore.CreateMnemonic(n2, English, p2, algo) - require.NoError(t, err) - - // we can get these keys - i2, err := cstore.Get(n2) - require.NoError(t, err) - _, err = cstore.Get(n3) - require.NotNil(t, err) - _, err = cstore.GetByAddress(accAddr(i2)) - require.NoError(t, err) - addr, err := sdk.AccAddressFromBech32("fetch1yq8lgssgxlx9smjhes6ryjasmqmd3ts28fvvdu") - require.NoError(t, err) - _, err = cstore.GetByAddress(addr) - require.NotNil(t, err) - - // list shows them in order - keyS, err := cstore.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - // note these are in alphabetical order - require.Equal(t, n2, keyS[0].GetName()) - require.Equal(t, n1, keyS[1].GetName()) - require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) - - // deleting a key removes it - err = cstore.Delete("bad name", "foo", false) - require.NotNil(t, err) - err = cstore.Delete(n1, p1, false) - require.NoError(t, err) - keyS, err = cstore.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - _, err = cstore.Get(n1) - require.Error(t, err) - - // create an offline key - o1 := "offline" - priv1 := ed25519.GenPrivKey() - pub1 := priv1.PubKey() - i, err = cstore.CreateOffline(o1, pub1, algo) - require.Nil(t, err) - require.Equal(t, pub1, i.GetPubKey()) - require.Equal(t, o1, i.GetName()) - iOffline := i.(*offlineInfo) - require.Equal(t, algo, iOffline.GetAlgo()) - keyS, err = cstore.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - - // delete the offline key - err = cstore.Delete(o1, "", false) - require.NoError(t, err) - keyS, err = cstore.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - - // addr cache gets nuked - and test skip flag - err = cstore.Delete(n2, "", true) - require.NoError(t, err) -} - -// TestSignVerify does some detailed checks on how we sign and validate -// signatures -func TestSignVerify(t *testing.T) { - cstore := NewInMemory() - algo := Secp256k1 - - n1, n2, n3 := "some dude", "a dudette", "dude-ish" - p1, p2, p3 := nums, foobar, foobar - - // create two users and get their info - i1, _, err := cstore.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err) - - i2, _, err := cstore.CreateMnemonic(n2, English, p2, algo) - require.Nil(t, err) - - // Import a public key - armor, err := cstore.ExportPubKey(n2) - require.Nil(t, err) - cstore.ImportPubKey(n3, armor) - i3, err := cstore.Get(n3) - require.NoError(t, err) - require.Equal(t, i3.GetName(), n3) - - // let's try to sign some messages - d1 := []byte("my first message") - d2 := []byte("some other important info!") - d3 := []byte("feels like I forgot something...") - - // try signing both data with both .. - s11, pub1, err := cstore.Sign(n1, p1, d1) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s12, pub1, err := cstore.Sign(n1, p1, d2) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s21, pub2, err := cstore.Sign(n2, p2, d1) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - s22, pub2, err := cstore.Sign(n2, p2, d2) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - // let's try to validate and make sure it only works when everything is proper - cases := []struct { - key crypto.PubKey - data []byte - sig []byte - valid bool - }{ - // proper matches - {i1.GetPubKey(), d1, s11, true}, - // change data, pubkey, or signature leads to fail - {i1.GetPubKey(), d2, s11, false}, - {i2.GetPubKey(), d1, s11, false}, - {i1.GetPubKey(), d1, s21, false}, - // make sure other successes - {i1.GetPubKey(), d2, s12, true}, - {i2.GetPubKey(), d1, s21, true}, - {i2.GetPubKey(), d2, s22, true}, - } - - for i, tc := range cases { - valid := tc.key.VerifyBytes(tc.data, tc.sig) - require.Equal(t, tc.valid, valid, "%d", i) - } - - // Now try to sign data with a secret-less key - _, _, err = cstore.Sign(n3, p3, d3) - require.NotNil(t, err) -} - -func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) { - getNewpass := func() (string, error) { return pass, nil } - err := cstore.Update(name, badpass, getNewpass) - require.NotNil(t, err) - err = cstore.Update(name, pass, getNewpass) - require.Nil(t, err, "%+v", err) -} - -// TestExportImport tests exporting and importing -func TestExportImport(t *testing.T) { - // make the storage with reasonable defaults - cstore := NewInMemory() - - info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - - john, err := cstore.Get("john") - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - johnAddr := info.GetPubKey().Address() - - armor, err := cstore.Export("john") - require.NoError(t, err) - - err = cstore.Import("john2", armor) - require.NoError(t, err) - - john2, err := cstore.Get("john2") - require.NoError(t, err) - - require.Equal(t, john.GetPubKey().Address(), johnAddr) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john, john2) -} - -// -func TestExportImportPubKey(t *testing.T) { - // make the storage with reasonable defaults - cstore := NewInMemory() - - // CreateMnemonic a private-public key pair and ensure consistency - notPasswd := "n9y25ah7" - info, _, err := cstore.CreateMnemonic("john", English, notPasswd, Secp256k1) - require.Nil(t, err) - require.NotEqual(t, info, "") - require.Equal(t, info.GetName(), "john") - addr := info.GetPubKey().Address() - john, err := cstore.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john.GetPubKey().Address(), addr) - - // Export the public key only - armor, err := cstore.ExportPubKey("john") - require.NoError(t, err) - // Import it under a different name - err = cstore.ImportPubKey("john-pubkey-only", armor) - require.NoError(t, err) - // Ensure consistency - john2, err := cstore.Get("john-pubkey-only") - require.NoError(t, err) - // Compare the public keys - require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) - // Ensure the original key hasn't changed - john, err = cstore.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetPubKey().Address(), addr) - require.Equal(t, john.GetName(), "john") - - // Ensure keys cannot be overwritten - err = cstore.ImportPubKey("john-pubkey-only", armor) - require.NotNil(t, err) -} - -// TestAdvancedKeyManagement verifies update, import, export functionality -func TestAdvancedKeyManagement(t *testing.T) { - // make the storage with reasonable defaults - cstore := NewInMemory() - - algo := Secp256k1 - n1, n2 := "old-name", "new name" - p1, p2 := nums, foobar - - // make sure key works with initial password - _, _, err := cstore.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - assertPassword(t, cstore, n1, p1, p2) - - // update password requires the existing password - getNewpass := func() (string, error) { return p2, nil } - err = cstore.Update(n1, "jkkgkg", getNewpass) - require.NotNil(t, err) - assertPassword(t, cstore, n1, p1, p2) - - // then it changes the password when correct - err = cstore.Update(n1, p1, getNewpass) - require.NoError(t, err) - // p2 is now the proper one! - assertPassword(t, cstore, n1, p2, p1) - - // exporting requires the proper name and passphrase - _, err = cstore.Export(n1 + ".notreal") - require.NotNil(t, err) - _, err = cstore.Export(" " + n1) - require.NotNil(t, err) - _, err = cstore.Export(n1 + " ") - require.NotNil(t, err) - _, err = cstore.Export("") - require.NotNil(t, err) - exported, err := cstore.Export(n1) - require.Nil(t, err, "%+v", err) - - // import succeeds - err = cstore.Import(n2, exported) - require.NoError(t, err) - - // second import fails - err = cstore.Import(n2, exported) - require.NotNil(t, err) -} - -// TestSeedPhrase verifies restoring from a seed phrase -func TestSeedPhrase(t *testing.T) { - - // make the storage with reasonable defaults - cstore := NewInMemory() - - algo := Secp256k1 - n1, n2 := "lost-key", "found-again" - p1, p2 := nums, foobar - - // make sure key works with initial password - info, mnemonic, err := cstore.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - require.Equal(t, n1, info.GetName()) - assert.NotEmpty(t, mnemonic) - - // now, let us delete this key - err = cstore.Delete(n1, p1, false) - require.Nil(t, err, "%+v", err) - _, err = cstore.Get(n1) - require.NotNil(t, err) - - // let us re-create it from the mnemonic-phrase - params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - hdPath := params.String() - newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1) - require.NoError(t, err) - require.Equal(t, n2, newInfo.GetName()) - require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) - require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) -} - -func ExampleNew() { - // Select the encryption and storage for your cryptostore - customKeyGenFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) { - var bzArr [32]byte - copy(bzArr[:], bz) - return secp256k1.PrivKeySecp256k1(bzArr), nil - } - cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc)) - - sec := Secp256k1 - - // Add keys and see they return in alphabetical order - bob, _, err := cstore.CreateMnemonic("Bob", English, "friend", sec) - if err != nil { - // this should never happen - fmt.Println(err) - } else { - // return info here just like in List - fmt.Println(bob.GetName()) - } - _, _, _ = cstore.CreateMnemonic("Alice", English, "secret", sec) - _, _, _ = cstore.CreateMnemonic("Carl", English, "mitm", sec) - info, _ := cstore.List() - for _, i := range info { - fmt.Println(i.GetName()) - } - - // We need to use passphrase to generate a signature - tx := []byte("deadbeef") - sig, pub, err := cstore.Sign("Bob", "friend", tx) - if err != nil { - fmt.Println("don't accept real passphrase") - } - - // and we can validate the signature with publicly available info - binfo, _ := cstore.Get("Bob") - if !binfo.GetPubKey().Equals(bob.GetPubKey()) { - fmt.Println("Get and Create return different keys") - } - - if pub.Equals(binfo.GetPubKey()) { - fmt.Println("signed by Bob") - } - if !pub.VerifyBytes(tx, sig) { - fmt.Println("invalid signature") - } - - // Output: - // Bob - // Alice - // Bob - // Carl - // signed by Bob -} - -func accAddr(info Info) sdk.AccAddress { - return (sdk.AccAddress)(info.GetPubKey().Address()) -} diff --git a/crypto/keys/keyerror/errors.go b/crypto/keys/keyerror/errors.go deleted file mode 100644 index 5b3a9a7b1f38..000000000000 --- a/crypto/keys/keyerror/errors.go +++ /dev/null @@ -1,81 +0,0 @@ -package keyerror - -import ( - "fmt" -) - -const ( - codeKeyNotFound = 1 - codeWrongPassword = 2 -) - -type keybaseError interface { - error - Code() int -} - -type errKeyNotFound struct { - code int - name string -} - -func (e errKeyNotFound) Code() int { - return e.code -} - -func (e errKeyNotFound) Error() string { - return fmt.Sprintf("Key %s not found", e.name) -} - -// NewErrKeyNotFound returns a standardized error reflecting that the specified key doesn't exist -func NewErrKeyNotFound(name string) error { - return errKeyNotFound{ - code: codeKeyNotFound, - name: name, - } -} - -// IsErrKeyNotFound returns true if the given error is errKeyNotFound -func IsErrKeyNotFound(err error) bool { - if err == nil { - return false - } - if keyErr, ok := err.(keybaseError); ok { - if keyErr.Code() == codeKeyNotFound { - return true - } - } - return false -} - -type errWrongPassword struct { - code int -} - -func (e errWrongPassword) Code() int { - return e.code -} - -func (e errWrongPassword) Error() string { - return "invalid account password" -} - -// NewErrWrongPassword returns a standardized error reflecting that the specified password is wrong -func NewErrWrongPassword() error { - return errWrongPassword{ - code: codeWrongPassword, - } -} - -// IsErrWrongPassword returns true if the given error is errWrongPassword -func IsErrWrongPassword(err error) bool { - if err == nil { - return false - } - if keyErr, ok := err.(keybaseError); ok { - if keyErr.Code() == codeWrongPassword { - return true - } - } - return false -} diff --git a/crypto/keys/keyring.go b/crypto/keys/keyring.go deleted file mode 100644 index 2135c3381719..000000000000 --- a/crypto/keys/keyring.go +++ /dev/null @@ -1,609 +0,0 @@ -package keys - -import ( - "bufio" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "sort" - "strings" - - "github.com/99designs/keyring" - "github.com/pkg/errors" - - "github.com/tendermint/crypto/bcrypt" - "github.com/tendermint/tendermint/crypto" - tmcrypto "github.com/tendermint/tendermint/crypto" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" - "github.com/cosmos/cosmos-sdk/types" -) - -const ( - BackendFile = "file" - BackendOS = "os" - BackendKWallet = "kwallet" - BackendPass = "pass" - BackendTest = "test" -) - -const ( - keyringDirNameFmt = "keyring-%s" - testKeyringDirNameFmt = "keyring-test-%s" -) - -var _ Keybase = keyringKeybase{} - -// keyringKeybase implements the Keybase interface by using the Keyring library -// for account key persistence. -type keyringKeybase struct { - base baseKeybase - db keyring.Keyring -} - -var maxPassphraseEntryAttempts = 3 - -func newKeyringKeybase(db keyring.Keyring, opts ...KeybaseOption) Keybase { - return keyringKeybase{ - db: db, - base: newBaseKeybase(opts...), - } -} - -// NewKeyring creates a new instance of a keyring. Keybase -// options can be applied when generating this new Keybase. -// Available backends are "os", "file", "test". -func NewKeyring( - appName, backend, rootDir string, userInput io.Reader, opts ...KeybaseOption, -) (Keybase, error) { - - var db keyring.Keyring - var err error - - switch backend { - case BackendTest: - db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, nil, true)) - case BackendFile: - db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput)) - case BackendOS: - db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, userInput, false)) - case BackendKWallet: - db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput)) - case BackendPass: - db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput)) - default: - return nil, fmt.Errorf("unknown keyring backend %v", backend) - } - if err != nil { - return nil, err - } - - return newKeyringKeybase(db, opts...), nil -} - -// CreateMnemonic generates a new key and persists it to storage, encrypted -// using the provided password. It returns the generated mnemonic and the key Info. -// An error is returned if it fails to generate a key for the given algo type, -// or if another key is already stored under the same name. -func (kb keyringKeybase) CreateMnemonic( - name string, language Language, passwd string, algo SigningAlgo, -) (info Info, mnemonic string, err error) { - - return kb.base.CreateMnemonic(kb, name, language, passwd, algo) -} - -// CreateAccount converts a mnemonic to a private key and persists it, encrypted -// with the given password. -func (kb keyringKeybase) CreateAccount( - name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo, -) (Info, error) { - - return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo) -} - -// CreateLedger creates a new locally-stored reference to a Ledger keypair. -// It returns the created key info and an error if the Ledger could not be queried. -func (kb keyringKeybase) CreateLedger( - name string, algo SigningAlgo, hrp string, account, index uint32, -) (Info, error) { - - return kb.base.CreateLedger(kb, name, algo, hrp, account, index) -} - -// CreateOffline creates a new reference to an offline keypair. It returns the -// created key info. -func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) { - return kb.base.writeOfflineKey(kb, name, pub, algo), nil -} - -// CreateMulti creates a new reference to a multisig (offline) keypair. It -// returns the created key Info object. -func (kb keyringKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) { - return kb.base.writeMultisigKey(kb, name, pub), nil -} - -// List returns the keys from storage in alphabetical order. -func (kb keyringKeybase) List() ([]Info, error) { - var res []Info - keys, err := kb.db.Keys() - if err != nil { - return nil, err - } - - sort.Strings(keys) - - for _, key := range keys { - if strings.HasSuffix(key, infoSuffix) { - rawInfo, err := kb.db.Get(key) - if err != nil { - return nil, err - } - - if len(rawInfo.Data) == 0 { - return nil, keyerror.NewErrKeyNotFound(key) - } - - info, err := unmarshalInfo(rawInfo.Data) - if err != nil { - return nil, err - } - - res = append(res, info) - } - } - - return res, nil -} - -// Get returns the public information about one key. -func (kb keyringKeybase) Get(name string) (Info, error) { - key := infoKey(name) - - bs, err := kb.db.Get(string(key)) - if err != nil { - return nil, err - } - - if len(bs.Data) == 0 { - return nil, keyerror.NewErrKeyNotFound(name) - } - - return unmarshalInfo(bs.Data) -} - -// GetByAddress fetches a key by address and returns its public information. -func (kb keyringKeybase) GetByAddress(address types.AccAddress) (Info, error) { - ik, err := kb.db.Get(string(addrKey(address))) - if err != nil { - return nil, err - } - - if len(ik.Data) == 0 { - return nil, fmt.Errorf("key with address %s not found", address) - } - - bs, err := kb.db.Get(string(ik.Data)) - if err != nil { - return nil, err - } - - return unmarshalInfo(bs.Data) -} - -// Sign signs an arbitrary set of bytes with the named key. It returns an error -// if the key doesn't exist or the decryption fails. -func (kb keyringKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { - info, err := kb.Get(name) - if err != nil { - return - } - - var priv tmcrypto.PrivKey - - switch i := info.(type) { - case localInfo: - if i.PrivKeyArmor == "" { - return nil, nil, fmt.Errorf("private key not available") - } - - priv, err = cryptoAmino.PrivKeyFromBytes([]byte(i.PrivKeyArmor)) - if err != nil { - return nil, nil, err - } - - case ledgerInfo: - return kb.base.SignWithLedger(info, msg) - - case offlineInfo, multiInfo: - return kb.base.DecodeSignature(info, msg) - } - - sig, err = priv.Sign(msg) - if err != nil { - return nil, nil, err - } - - return sig, priv.PubKey(), nil -} - -// ExportPrivateKeyObject exports an armored private key object. -func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) { - info, err := kb.Get(name) - if err != nil { - return nil, err - } - - var priv tmcrypto.PrivKey - - switch linfo := info.(type) { - case localInfo: - if linfo.PrivKeyArmor == "" { - err = fmt.Errorf("private key not available") - return nil, err - } - - priv, err = cryptoAmino.PrivKeyFromBytes([]byte(linfo.PrivKeyArmor)) - if err != nil { - return nil, err - } - - case ledgerInfo, offlineInfo, multiInfo: - return nil, errors.New("only works on local private keys") - } - - return priv, nil -} - -// Export exports armored private key to the caller. -func (kb keyringKeybase) Export(name string) (armor string, err error) { - bz, err := kb.db.Get(string(infoKey(name))) - if err != nil { - return "", err - } - - if bz.Data == nil { - return "", fmt.Errorf("no key to export with name: %s", name) - } - - return mintkey.ArmorInfoBytes(bz.Data), nil -} - -// ExportPubKey returns public keys in ASCII armored format. It retrieves an Info -// object by its name and return the public key in a portable format. -func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) { - bz, err := kb.Get(name) - if err != nil { - return "", err - } - - if bz == nil { - return "", fmt.Errorf("no key to export with name: %s", name) - } - - return mintkey.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil -} - -// Import imports armored private key. -func (kb keyringKeybase) Import(name string, armor string) error { - bz, _ := kb.Get(name) - - if bz != nil { - pubkey := bz.GetPubKey() - - if len(pubkey.Bytes()) > 0 { - return fmt.Errorf("cannot overwrite data for name: %s", name) - } - } - - infoBytes, err := mintkey.UnarmorInfoBytes(armor) - if err != nil { - return err - } - - info, err := unmarshalInfo(infoBytes) - if err != nil { - return err - } - - kb.writeInfo(name, info) - - err = kb.db.Set(keyring.Item{ - Key: string(addrKey(info.GetAddress())), - Data: infoKey(name), - }) - if err != nil { - return err - } - - return nil -} - -// ExportPrivKey returns a private key in ASCII armored format. An error is returned -// if the key does not exist or a wrong encryption passphrase is supplied. -func (kb keyringKeybase) ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error) { - priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase) - if err != nil { - return "", err - } - - info, err := kb.Get(name) - if err != nil { - return "", err - } - - return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil -} - -// ImportPrivKey imports a private key in ASCII armor format. An error is returned -// if a key with the same name exists or a wrong encryption passphrase is -// supplied. -func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error { - if kb.HasKey(name) { - return fmt.Errorf("cannot overwrite key: %s", name) - } - - privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase) - if err != nil { - return errors.Wrap(err, "failed to decrypt private key") - } - - // NOTE: The keyring keystore has no need for a passphrase. - kb.writeLocalKey(name, privKey, "", SigningAlgo(algo)) - return nil -} - -// HasKey returns whether the key exists in the keyring. -func (kb keyringKeybase) HasKey(name string) bool { - bz, _ := kb.Get(name) - return bz != nil -} - -// ImportPubKey imports an ASCII-armored public key. It will store a new Info -// object holding a public key only, i.e. it will not be possible to sign with -// it as it lacks the secret key. -func (kb keyringKeybase) ImportPubKey(name string, armor string) error { - bz, _ := kb.Get(name) - if bz != nil { - pubkey := bz.GetPubKey() - - if len(pubkey.Bytes()) > 0 { - return fmt.Errorf("cannot overwrite data for name: %s", name) - } - } - - pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor) - if err != nil { - return err - } - - pubKey, err := cryptoAmino.PubKeyFromBytes(pubBytes) - if err != nil { - return err - } - - kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo)) - return nil -} - -// Delete removes key forever, but we must present the proper passphrase before -// deleting it (for security). It returns an error if the key doesn't exist or -// passphrases don't match. The passphrase is ignored when deleting references to -// offline and Ledger / HW wallet keys. -func (kb keyringKeybase) Delete(name, _ string, _ bool) error { - // verify we have the proper password before deleting - info, err := kb.Get(name) - if err != nil { - return err - } - - err = kb.db.Remove(string(addrKey(info.GetAddress()))) - if err != nil { - return err - } - - err = kb.db.Remove(string(infoKey(name))) - if err != nil { - return err - } - - return nil -} - -// Update changes the passphrase with which an already stored key is encrypted. -// The oldpass must be the current passphrase used for encryption, getNewpass is -// a function to get the passphrase to permanently replace the current passphrase. -func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error { - info, err := kb.Get(name) - if err != nil { - return err - } - - switch linfo := info.(type) { - case localInfo: - key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass) - if err != nil { - return err - } - - newpass, err := getNewpass() - if err != nil { - return err - } - - kb.writeLocalKey(name, key, newpass, linfo.GetAlgo()) - return nil - - default: - return fmt.Errorf("locally stored key required; received: %v", reflect.TypeOf(info).String()) - } -} - -// SupportedAlgos returns a list of supported signing algorithms. -func (kb keyringKeybase) SupportedAlgos() []SigningAlgo { - return kb.base.SupportedAlgos() -} - -// SupportedAlgosLedger returns a list of supported ledger signing algorithms. -func (kb keyringKeybase) SupportedAlgosLedger() []SigningAlgo { - return kb.base.SupportedAlgosLedger() -} - -// CloseDB releases the lock and closes the storage backend. -func (kb keyringKeybase) CloseDB() {} - -func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string, algo SigningAlgo) Info { - // encrypt private key using keyring - pub := priv.PubKey() - info := newLocalInfo(name, pub, string(priv.Bytes()), algo) - - kb.writeInfo(name, info) - return info -} - -func (kb keyringKeybase) writeInfo(name string, info Info) { - // write the info by key - key := infoKey(name) - serializedInfo := marshalInfo(info) - - err := kb.db.Set(keyring.Item{ - Key: string(key), - Data: serializedInfo, - }) - if err != nil { - panic(err) - } - - err = kb.db.Set(keyring.Item{ - Key: string(addrKey(info.GetAddress())), - Data: key, - }) - if err != nil { - panic(err) - } -} - -func lkbToKeyringConfig(appName, dir string, buf io.Reader, test bool) keyring.Config { - if test { - return keyring.Config{ - AllowedBackends: []keyring.BackendType{keyring.FileBackend}, - ServiceName: appName, - FileDir: filepath.Join(dir, fmt.Sprintf(testKeyringDirNameFmt, appName)), - FilePasswordFunc: func(_ string) (string, error) { - return "test", nil - }, - } - } - - return keyring.Config{ - ServiceName: appName, - FileDir: dir, - FilePasswordFunc: newRealPrompt(dir, buf), - } -} - -func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config { - return keyring.Config{ - AllowedBackends: []keyring.BackendType{keyring.KWalletBackend}, - ServiceName: "kdewallet", - KWalletAppID: appName, - KWalletFolder: "", - } -} - -func newPassBackendKeyringConfig(appName, dir string, _ io.Reader) keyring.Config { - prefix := filepath.Join(dir, fmt.Sprintf(keyringDirNameFmt, appName)) - return keyring.Config{ - AllowedBackends: []keyring.BackendType{keyring.PassBackend}, - ServiceName: appName, - PassPrefix: prefix, - } -} - -func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config { - fileDir := filepath.Join(dir, fmt.Sprintf(keyringDirNameFmt, name)) - return keyring.Config{ - AllowedBackends: []keyring.BackendType{keyring.FileBackend}, - ServiceName: name, - FileDir: fileDir, - FilePasswordFunc: newRealPrompt(fileDir, buf), - } -} - -func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { - return func(prompt string) (string, error) { - keyhashStored := false - keyhashFilePath := filepath.Join(dir, "keyhash") - - var keyhash []byte - - _, err := os.Stat(keyhashFilePath) - switch { - case err == nil: - keyhash, err = ioutil.ReadFile(keyhashFilePath) - if err != nil { - return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err) - } - - keyhashStored = true - - case os.IsNotExist(err): - keyhashStored = false - - default: - return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err) - } - - failureCounter := 0 - for { - failureCounter++ - if failureCounter > maxPassphraseEntryAttempts { - return "", fmt.Errorf("too many failed passphrase attempts") - } - - buf := bufio.NewReader(buf) - pass, err := input.GetPassword("Enter keyring passphrase:", buf) - if err != nil { - fmt.Fprintln(os.Stderr, err) - continue - } - - if keyhashStored { - if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil { - fmt.Fprintln(os.Stderr, "incorrect passphrase") - continue - } - return pass, nil - } - - reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf) - if err != nil { - fmt.Fprintln(os.Stderr, err) - continue - } - - if pass != reEnteredPass { - fmt.Fprintln(os.Stderr, "passphrase do not match") - continue - } - - saltBytes := crypto.CRandBytes(16) - passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2) - if err != nil { - fmt.Fprintln(os.Stderr, err) - continue - } - - if err := ioutil.WriteFile(dir+"/keyhash", passwordHash, 0555); err != nil { - return "", err - } - - return pass, nil - } - } -} diff --git a/crypto/keys/keyring_test.go b/crypto/keys/keyring_test.go deleted file mode 100644 index 3e4a29864c32..000000000000 --- a/crypto/keys/keyring_test.go +++ /dev/null @@ -1,327 +0,0 @@ -// nolint: goconst -package keys - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/tests" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestLazyKeyManagementKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - - algo := Secp256k1 - n1, n2, n3 := "personal", "business", "other" - p1, p2 := "1234", "really-secure!@#$" - - // Check empty state - l, err := kb.List() - require.Nil(t, err) - assert.Empty(t, l) - - _, _, err = kb.CreateMnemonic(n1, English, p1, Ed25519) - require.Error(t, err, "ed25519 keys are currently not supported by keybase") - - // create some keys - _, err = kb.Get(n1) - require.Error(t, err) - i, _, err := kb.CreateMnemonic(n1, English, p1, algo) - - require.NoError(t, err) - require.Equal(t, n1, i.GetName()) - _, _, err = kb.CreateMnemonic(n2, English, p2, algo) - require.NoError(t, err) - - // we can get these keys - i2, err := kb.Get(n2) - require.NoError(t, err) - _, err = kb.Get(n3) - require.NotNil(t, err) - _, err = kb.GetByAddress(accAddr(i2)) - require.NoError(t, err) - addr, err := sdk.AccAddressFromBech32("fetch1yq8lgssgxlx9smjhes6ryjasmqmd3ts28fvvdu") - require.NoError(t, err) - _, err = kb.GetByAddress(addr) - require.NotNil(t, err) - - // list shows them in order - keyS, err := kb.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - // note these are in alphabetical order - require.Equal(t, n2, keyS[0].GetName()) - require.Equal(t, n1, keyS[1].GetName()) - require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) - - // deleting a key removes it - err = kb.Delete("bad name", "foo", false) - require.NotNil(t, err) - err = kb.Delete(n1, p1, false) - require.NoError(t, err) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - _, err = kb.Get(n1) - require.Error(t, err) - - // create an offline key - o1 := "offline" - priv1 := ed25519.GenPrivKey() - pub1 := priv1.PubKey() - i, err = kb.CreateOffline(o1, pub1, Ed25519) - require.Nil(t, err) - require.Equal(t, pub1, i.GetPubKey()) - require.Equal(t, o1, i.GetName()) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - - // delete the offline key - err = kb.Delete(o1, "", false) - require.NoError(t, err) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - - // addr cache gets nuked - and test skip flag - err = kb.Delete(n2, "", true) - require.NoError(t, err) -} - -func TestLazySignVerifyKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - algo := Secp256k1 - - n1, n2, n3 := "some dude", "a dudette", "dude-ish" - p1, p2, p3 := "1234", "foobar", "foobar" - - // create two users and get their info - i1, _, err := kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err) - - i2, _, err := kb.CreateMnemonic(n2, English, p2, algo) - require.Nil(t, err) - - // Import a public key - armor, err := kb.ExportPubKey(n2) - require.Nil(t, err) - kb.ImportPubKey(n3, armor) - i3, err := kb.Get(n3) - require.NoError(t, err) - require.Equal(t, i3.GetName(), n3) - - // let's try to sign some messages - d1 := []byte("my first message") - d2 := []byte("some other important info!") - d3 := []byte("feels like I forgot something...") - - // try signing both data with both .. - s11, pub1, err := kb.Sign(n1, p1, d1) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s12, pub1, err := kb.Sign(n1, p1, d2) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s21, pub2, err := kb.Sign(n2, p2, d1) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - s22, pub2, err := kb.Sign(n2, p2, d2) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - // let's try to validate and make sure it only works when everything is proper - cases := []struct { - key crypto.PubKey - data []byte - sig []byte - valid bool - }{ - // proper matches - {i1.GetPubKey(), d1, s11, true}, - // change data, pubkey, or signature leads to fail - {i1.GetPubKey(), d2, s11, false}, - {i2.GetPubKey(), d1, s11, false}, - {i1.GetPubKey(), d1, s21, false}, - // make sure other successes - {i1.GetPubKey(), d2, s12, true}, - {i2.GetPubKey(), d1, s21, true}, - {i2.GetPubKey(), d2, s22, true}, - } - - for i, tc := range cases { - valid := tc.key.VerifyBytes(tc.data, tc.sig) - require.Equal(t, tc.valid, valid, "%d", i) - } - - // Now try to sign data with a secret-less key - _, _, err = kb.Sign(n3, p3, d3) - require.NotNil(t, err) -} - -func TestLazyExportImportKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - - info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - - john, err := kb.Get("john") - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - johnAddr := info.GetPubKey().Address() - - armor, err := kb.Export("john") - require.NoError(t, err) - - err = kb.Import("john2", armor) - require.NoError(t, err) - - john2, err := kb.Get("john2") - require.NoError(t, err) - - require.Equal(t, john.GetPubKey().Address(), johnAddr) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john, john2) -} - -func TestLazyExportImportPubKeyKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - algo := Secp256k1 - - // CreateMnemonic a private-public key pair and ensure consistency - notPasswd := "n9y25ah7" - info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo) - require.Nil(t, err) - require.NotEqual(t, info, "") - require.Equal(t, info.GetName(), "john") - addr := info.GetPubKey().Address() - john, err := kb.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john.GetPubKey().Address(), addr) - - // Export the public key only - armor, err := kb.ExportPubKey("john") - require.NoError(t, err) - // Import it under a different name - err = kb.ImportPubKey("john-pubkey-only", armor) - require.NoError(t, err) - // Ensure consistency - john2, err := kb.Get("john-pubkey-only") - require.NoError(t, err) - // Compare the public keys - require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) - // Ensure the original key hasn't changed - john, err = kb.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetPubKey().Address(), addr) - require.Equal(t, john.GetName(), "john") - - // Ensure keys cannot be overwritten - err = kb.ImportPubKey("john-pubkey-only", armor) - require.NotNil(t, err) -} - -func TestLazyExportPrivateKeyObjectKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - - info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - - // export private key object - exported, err := kb.ExportPrivateKeyObject("john", "secretcpw") - require.Nil(t, err, "%+v", err) - require.True(t, exported.PubKey().Equals(info.GetPubKey())) -} - -func TestLazyAdvancedKeyManagementKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - - algo := Secp256k1 - n1, n2 := "old-name", "new name" - p1 := "1234" - - // make sure key works with initial password - _, _, err = kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - - _, err = kb.Export(n1 + ".notreal") - require.NotNil(t, err) - _, err = kb.Export(" " + n1) - require.NotNil(t, err) - _, err = kb.Export(n1 + " ") - require.NotNil(t, err) - _, err = kb.Export("") - require.NotNil(t, err) - exported, err := kb.Export(n1) - require.Nil(t, err, "%+v", err) - - // import succeeds - err = kb.Import(n2, exported) - require.NoError(t, err) - - // second import fails - err = kb.Import(n2, exported) - require.NotNil(t, err) -} - -func TestLazySeedPhraseKeyRing(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb, err := NewKeyring("keybasename", "test", dir, nil) - require.NoError(t, err) - - algo := Secp256k1 - n1, n2 := "lost-key", "found-again" - p1, p2 := "1234", "foobar" - - // make sure key works with initial password - info, mnemonic, err := kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - require.Equal(t, n1, info.GetName()) - assert.NotEmpty(t, mnemonic) - - // now, let us delete this key - err = kb.Delete(n1, p1, false) - require.Nil(t, err, "%+v", err) - _, err = kb.Get(n1) - require.NotNil(t, err) - - // let us re-create it from the mnemonic-phrase - params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - hdPath := params.String() - newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1) - require.NoError(t, err) - require.Equal(t, n2, newInfo.GetName()) - require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) - require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) -} diff --git a/crypto/keys/keys.go b/crypto/keys/keys.go deleted file mode 100644 index af5590814b49..000000000000 --- a/crypto/keys/keys.go +++ /dev/null @@ -1,16 +0,0 @@ -package keys - -// SigningAlgo defines an algorithm to derive key-pairs which can be used for cryptographic signing. -type SigningAlgo string - -const ( - // MultiAlgo implies that a pubkey is a multisignature - MultiAlgo = SigningAlgo("multi") - // Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters. - Secp256k1 = SigningAlgo("secp256k1") - // Ed25519 represents the Ed25519 signature system. - // It is currently not supported for end-user keys (wallets/ledgers). - Ed25519 = SigningAlgo("ed25519") - // Sr25519 represents the Sr25519 signature system. - Sr25519 = SigningAlgo("sr25519") -) diff --git a/crypto/keys/lazy_keybase.go b/crypto/keys/lazy_keybase.go deleted file mode 100644 index 2a92ecd304ce..000000000000 --- a/crypto/keys/lazy_keybase.go +++ /dev/null @@ -1,223 +0,0 @@ -package keys - -import ( - "fmt" - - "github.com/tendermint/tendermint/crypto" - tmos "github.com/tendermint/tendermint/libs/os" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var _ Keybase = lazyKeybase{} - -// NOTE: lazyKeybase will be deprecated in favor of lazyKeybaseKeyring. -type lazyKeybase struct { - name string - dir string - options []KeybaseOption -} - -// New creates a new instance of a lazy keybase. -func New(name, dir string, opts ...KeybaseOption) Keybase { - if err := tmos.EnsureDir(dir, 0700); err != nil { - panic(fmt.Sprintf("failed to create Keybase directory: %s", err)) - } - - return lazyKeybase{name: name, dir: dir, options: opts} -} - -func (lkb lazyKeybase) List() ([]Info, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).List() -} - -func (lkb lazyKeybase) Get(name string) (Info, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Get(name) -} - -func (lkb lazyKeybase) GetByAddress(address sdk.AccAddress) (Info, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).GetByAddress(address) -} - -func (lkb lazyKeybase) Delete(name, passphrase string, skipPass bool) error { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Delete(name, passphrase, skipPass) -} - -func (lkb lazyKeybase) Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Sign(name, passphrase, msg) -} - -func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, "", err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).CreateMnemonic(name, language, passwd, algo) -} - -func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, - lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo) -} - -func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).CreateLedger(name, algo, hrp, account, index) -} - -func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey, algo) -} - -func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).CreateMulti(name, pubkey) -} - -func (lkb lazyKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Update(name, oldpass, getNewpass) -} - -func (lkb lazyKeybase) Import(name string, armor string) (err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Import(name, armor) -} - -func (lkb lazyKeybase) ImportPrivKey(name string, armor string, passphrase string) error { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).ImportPrivKey(name, armor, passphrase) -} - -func (lkb lazyKeybase) ImportPubKey(name string, armor string) (err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).ImportPubKey(name, armor) -} - -func (lkb lazyKeybase) Export(name string) (armor string, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return "", err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).Export(name) -} - -func (lkb lazyKeybase) ExportPubKey(name string) (armor string, err error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return "", err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).ExportPubKey(name) -} - -func (lkb lazyKeybase) ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) { - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return nil, err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).ExportPrivateKeyObject(name, passphrase) -} - -func (lkb lazyKeybase) ExportPrivKey(name string, decryptPassphrase string, - encryptPassphrase string) (armor string, err error) { - - db, err := sdk.NewLevelDB(lkb.name, lkb.dir) - if err != nil { - return "", err - } - defer db.Close() - - return newDBKeybase(db, lkb.options...).ExportPrivKey(name, decryptPassphrase, encryptPassphrase) -} - -// SupportedAlgos returns a list of supported signing algorithms. -func (lkb lazyKeybase) SupportedAlgos() []SigningAlgo { - return newBaseKeybase(lkb.options...).SupportedAlgos() -} - -// SupportedAlgosLedger returns a list of supported ledger signing algorithms. -func (lkb lazyKeybase) SupportedAlgosLedger() []SigningAlgo { - return newBaseKeybase(lkb.options...).SupportedAlgosLedger() -} - -func (lkb lazyKeybase) CloseDB() {} diff --git a/crypto/keys/lazy_keybase_test.go b/crypto/keys/lazy_keybase_test.go deleted file mode 100644 index e7f4f1609437..000000000000 --- a/crypto/keys/lazy_keybase_test.go +++ /dev/null @@ -1,452 +0,0 @@ -package keys - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/tests" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestNew(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - lazykb, ok := kb.(lazyKeybase) - require.True(t, ok) - require.Equal(t, lazykb.name, "keybasename") - require.Equal(t, lazykb.dir, dir) -} - -func TestLazyKeyManagement(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - algo := Secp256k1 - n1, n2, n3 := "personal", "business", "other" - p1, p2 := nums, "really-secure!@#$" - - // Check empty state - l, err := kb.List() - require.Nil(t, err) - assert.Empty(t, l) - - _, _, err = kb.CreateMnemonic(n1, English, p1, Ed25519) - require.Error(t, err, "ed25519 keys are currently not supported by keybase") - - // create some keys - _, err = kb.Get(n1) - require.Error(t, err) - i, _, err := kb.CreateMnemonic(n1, English, p1, algo) - - require.NoError(t, err) - require.Equal(t, n1, i.GetName()) - _, _, err = kb.CreateMnemonic(n2, English, p2, algo) - require.NoError(t, err) - - // we can get these keys - i2, err := kb.Get(n2) - require.NoError(t, err) - _, err = kb.Get(n3) - require.NotNil(t, err) - _, err = kb.GetByAddress(accAddr(i2)) - require.NoError(t, err) - addr, err := sdk.AccAddressFromBech32("fetch1yq8lgssgxlx9smjhes6ryjasmqmd3ts28fvvdu") - require.NoError(t, err) - _, err = kb.GetByAddress(addr) - require.NotNil(t, err) - - // list shows them in order - keyS, err := kb.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - // note these are in alphabetical order - require.Equal(t, n2, keyS[0].GetName()) - require.Equal(t, n1, keyS[1].GetName()) - require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) - - // deleting a key removes it - err = kb.Delete("bad name", "foo", false) - require.NotNil(t, err) - err = kb.Delete(n1, p1, false) - require.NoError(t, err) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - _, err = kb.Get(n1) - require.Error(t, err) - - // create an offline key - o1 := "offline" - priv1 := ed25519.GenPrivKey() - pub1 := priv1.PubKey() - i, err = kb.CreateOffline(o1, pub1, algo) - require.Nil(t, err) - require.Equal(t, pub1, i.GetPubKey()) - require.Equal(t, o1, i.GetName()) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 2, len(keyS)) - - // delete the offline key - err = kb.Delete(o1, "", false) - require.NoError(t, err) - keyS, err = kb.List() - require.NoError(t, err) - require.Equal(t, 1, len(keyS)) - - // addr cache gets nuked - and test skip flag - err = kb.Delete(n2, "", true) - require.NoError(t, err) -} - -func TestLazySignVerify(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - algo := Secp256k1 - - n1, n2, n3 := "some dude", "a dudette", "dude-ish" - p1, p2, p3 := nums, foobar, foobar - - // create two users and get their info - i1, _, err := kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err) - - i2, _, err := kb.CreateMnemonic(n2, English, p2, algo) - require.Nil(t, err) - - // Import a public key - armor, err := kb.ExportPubKey(n2) - require.Nil(t, err) - kb.ImportPubKey(n3, armor) - i3, err := kb.Get(n3) - require.NoError(t, err) - require.Equal(t, i3.GetName(), n3) - - // let's try to sign some messages - d1 := []byte("my first message") - d2 := []byte("some other important info!") - d3 := []byte("feels like I forgot something...") - - // try signing both data with both .. - s11, pub1, err := kb.Sign(n1, p1, d1) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s12, pub1, err := kb.Sign(n1, p1, d2) - require.Nil(t, err) - require.Equal(t, i1.GetPubKey(), pub1) - - s21, pub2, err := kb.Sign(n2, p2, d1) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - s22, pub2, err := kb.Sign(n2, p2, d2) - require.Nil(t, err) - require.Equal(t, i2.GetPubKey(), pub2) - - // let's try to validate and make sure it only works when everything is proper - cases := []struct { - key crypto.PubKey - data []byte - sig []byte - valid bool - }{ - // proper matches - {i1.GetPubKey(), d1, s11, true}, - // change data, pubkey, or signature leads to fail - {i1.GetPubKey(), d2, s11, false}, - {i2.GetPubKey(), d1, s11, false}, - {i1.GetPubKey(), d1, s21, false}, - // make sure other successes - {i1.GetPubKey(), d2, s12, true}, - {i2.GetPubKey(), d1, s21, true}, - {i2.GetPubKey(), d2, s22, true}, - } - - for i, tc := range cases { - valid := tc.key.VerifyBytes(tc.data, tc.sig) - require.Equal(t, tc.valid, valid, "%d", i) - } - - // Now try to sign data with a secret-less key - _, _, err = kb.Sign(n3, p3, d3) - require.NotNil(t, err) -} - -func TestLazyExportImport(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - - john, err := kb.Get("john") - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - johnAddr := info.GetPubKey().Address() - - armor, err := kb.Export("john") - require.NoError(t, err) - - err = kb.Import("john2", armor) - require.NoError(t, err) - - john2, err := kb.Get("john2") - require.NoError(t, err) - - require.Equal(t, john.GetPubKey().Address(), johnAddr) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john, john2) -} - -func TestLazyExportImportPrivKey(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - priv1, err := kb.Get("john") - require.NoError(t, err) - - // decrypt local private key, and produce encrypted ASCII armored output - armored, err := kb.ExportPrivKey("john", "secretcpw", "new_secretcpw") - require.NoError(t, err) - - // delete exported key - require.NoError(t, kb.Delete("john", "", true)) - _, err = kb.Get("john") - require.Error(t, err) - - // import armored key - require.NoError(t, kb.ImportPrivKey("john", armored, "new_secretcpw")) - - // ensure old and new keys match - priv2, err := kb.Get("john") - require.NoError(t, err) - require.True(t, priv1.GetPubKey().Equals(priv2.GetPubKey())) -} - -func TestLazyExportImportPubKey(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - algo := Secp256k1 - - // CreateMnemonic a private-public key pair and ensure consistency - notPasswd := "n9y25ah7" - info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo) - require.Nil(t, err) - require.NotEqual(t, info, "") - require.Equal(t, info.GetName(), "john") - addr := info.GetPubKey().Address() - john, err := kb.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetName(), "john") - require.Equal(t, john.GetPubKey().Address(), addr) - - // Export the public key only - armor, err := kb.ExportPubKey("john") - require.NoError(t, err) - // Import it under a different name - err = kb.ImportPubKey("john-pubkey-only", armor) - require.NoError(t, err) - // Ensure consistency - john2, err := kb.Get("john-pubkey-only") - require.NoError(t, err) - // Compare the public keys - require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) - // Ensure the original key hasn't changed - john, err = kb.Get("john") - require.NoError(t, err) - require.Equal(t, john.GetPubKey().Address(), addr) - require.Equal(t, john.GetName(), "john") - - // Ensure keys cannot be overwritten - err = kb.ImportPubKey("john-pubkey-only", armor) - require.NotNil(t, err) -} - -func TestLazyExportPrivateKeyObject(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), "john") - - // export private key object - _, err = kb.ExportPrivateKeyObject("john", "invalid") - require.NotNil(t, err, "%+v", err) - exported, err := kb.ExportPrivateKeyObject("john", "secretcpw") - require.Nil(t, err, "%+v", err) - require.True(t, exported.PubKey().Equals(info.GetPubKey())) -} - -func TestLazyAdvancedKeyManagement(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - algo := Secp256k1 - n1, n2 := "old-name", "new name" - p1, p2 := nums, foobar - - // make sure key works with initial password - _, _, err := kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - assertPassword(t, kb, n1, p1, p2) - - // update password requires the existing password - getNewpass := func() (string, error) { return p2, nil } - err = kb.Update(n1, "jkkgkg", getNewpass) - require.NotNil(t, err) - assertPassword(t, kb, n1, p1, p2) - - // then it changes the password when correct - err = kb.Update(n1, p1, getNewpass) - require.NoError(t, err) - // p2 is now the proper one! - assertPassword(t, kb, n1, p2, p1) - - // exporting requires the proper name and passphrase - _, err = kb.Export(n1 + ".notreal") - require.NotNil(t, err) - _, err = kb.Export(" " + n1) - require.NotNil(t, err) - _, err = kb.Export(n1 + " ") - require.NotNil(t, err) - _, err = kb.Export("") - require.NotNil(t, err) - exported, err := kb.Export(n1) - require.Nil(t, err, "%+v", err) - - // import succeeds - err = kb.Import(n2, exported) - require.NoError(t, err) - - // second import fails - err = kb.Import(n2, exported) - require.NotNil(t, err) -} - -// TestSeedPhrase verifies restoring from a seed phrase -func TestLazySeedPhrase(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - kb := New("keybasename", dir) - - algo := Secp256k1 - n1, n2 := "lost-key", "found-again" - p1, p2 := nums, foobar - - // make sure key works with initial password - info, mnemonic, err := kb.CreateMnemonic(n1, English, p1, algo) - require.Nil(t, err, "%+v", err) - require.Equal(t, n1, info.GetName()) - assert.NotEmpty(t, mnemonic) - - // now, let us delete this key - err = kb.Delete(n1, p1, false) - require.Nil(t, err, "%+v", err) - _, err = kb.Get(n1) - require.NotNil(t, err) - - // let us re-create it from the mnemonic-phrase - params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - hdPath := params.String() - newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, algo) - require.NoError(t, err) - require.Equal(t, n2, newInfo.GetName()) - require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) - require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) -} - -var _ crypto.PrivKey = testPriv{} -var _ crypto.PubKey = testPub{} -var testCdc *amino.Codec - -type testPriv []byte - -func (privkey testPriv) PubKey() crypto.PubKey { return testPub{} } -func (privkey testPriv) Bytes() []byte { - return testCdc.MustMarshalBinaryBare(privkey) -} -func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil } -func (privkey testPriv) Equals(other crypto.PrivKey) bool { return true } - -type testPub []byte - -func (key testPub) Address() crypto.Address { return crypto.Address{} } -func (key testPub) Bytes() []byte { - return testCdc.MustMarshalBinaryBare(key) -} -func (key testPub) VerifyBytes(msg []byte, sig []byte) bool { return true } -func (key testPub) Equals(other crypto.PubKey) bool { return true } - -func TestKeygenOverride(t *testing.T) { - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - - // Save existing codec and reset after test - cryptoCdc := CryptoCdc - defer func() { - CryptoCdc = cryptoCdc - }() - - // Setup testCdc encoding and decoding new key type - testCdc = codec.New() - RegisterCodec(testCdc) - tmamino.RegisterAmino(testCdc) - - // Set up codecs for using new key types - privName, pubName := "test/priv_name", "test/pub_name" - tmamino.RegisterKeyType(testPriv{}, privName) - tmamino.RegisterKeyType(testPub{}, pubName) - testCdc.RegisterConcrete(testPriv{}, privName, nil) - testCdc.RegisterConcrete(testPub{}, pubName, nil) - CryptoCdc = testCdc - - overrideCalled := false - dummyFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) { - overrideCalled = true - return testPriv(bz[:]), nil - } - - kb := New("keybasename", dir, WithKeygenFunc(dummyFunc)) - - testName, pw := "name", "testPassword" - - // create new key which will generate with - info, _, err := kb.CreateMnemonic(testName, English, pw, Secp256k1) - require.NoError(t, err) - require.Equal(t, info.GetName(), testName) - - // Assert overridden function was called - require.True(t, overrideCalled) - - // export private key object - exported, err := kb.ExportPrivateKeyObject(testName, pw) - require.Nil(t, err, "%+v", err) - - // require that the key type is the new key - _, ok := exported.(testPriv) - require.True(t, ok) - - require.True(t, exported.PubKey().Equals(info.GetPubKey())) -} diff --git a/crypto/keys/mintkey/mintkey.go b/crypto/keys/mintkey/mintkey.go deleted file mode 100644 index 361c5d7f8c0a..000000000000 --- a/crypto/keys/mintkey/mintkey.go +++ /dev/null @@ -1,191 +0,0 @@ -package mintkey - -import ( - "encoding/hex" - "fmt" - - "github.com/tendermint/crypto/bcrypt" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/armor" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - "github.com/tendermint/tendermint/crypto/xsalsa20symmetric" - - tmos "github.com/tendermint/tendermint/libs/os" - - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" -) - -const ( - blockTypePrivKey = "TENDERMINT PRIVATE KEY" - blockTypeKeyInfo = "TENDERMINT KEY INFO" - blockTypePubKey = "TENDERMINT PUBLIC KEY" - - defaultAlgo = "secp256k1" - - headerVersion = "version" - headerType = "type" -) - -// Make bcrypt security parameter var, so it can be changed within the lcd test -// Making the bcrypt security parameter a var shouldn't be a security issue: -// One can't verify an invalid key by maliciously changing the bcrypt -// parameter during a runtime vulnerability. The main security -// threat this then exposes would be something that changes this during -// runtime before the user creates their key. This vulnerability must -// succeed to update this to that same value before every subsequent call -// to the keys command in future startups / or the attacker must get access -// to the filesystem. However, with a similar threat model (changing -// variables in runtime), one can cause the user to sign a different tx -// than what they see, which is a significantly cheaper attack then breaking -// a bcrypt hash. (Recall that the nonce still exists to break rainbow tables) -// For further notes on security parameter choice, see README.md -var BcryptSecurityParameter = 12 - -//----------------------------------------------------------------- -// add armor - -// Armor the InfoBytes -func ArmorInfoBytes(bz []byte) string { - header := map[string]string{ - headerType: "Info", - headerVersion: "0.0.0", - } - return armor.EncodeArmor(blockTypeKeyInfo, header, bz) -} - -// Armor the PubKeyBytes -func ArmorPubKeyBytes(bz []byte, algo string) string { - header := map[string]string{ - headerVersion: "0.0.1", - } - if algo != "" { - header[headerType] = algo - } - return armor.EncodeArmor(blockTypePubKey, header, bz) -} - -//----------------------------------------------------------------- -// remove armor - -// Unarmor the InfoBytes -func UnarmorInfoBytes(armorStr string) ([]byte, error) { - bz, header, err := unarmorBytes(armorStr, blockTypeKeyInfo) - if err != nil { - return nil, err - } - - if header[headerVersion] != "0.0.0" { - return nil, fmt.Errorf("unrecognized version: %v", header[headerVersion]) - } - return bz, nil -} - -// UnarmorPubKeyBytes returns the pubkey byte slice, a string of the algo type, and an error -func UnarmorPubKeyBytes(armorStr string) (bz []byte, algo string, err error) { - bz, header, err := unarmorBytes(armorStr, blockTypePubKey) - if err != nil { - return nil, "", fmt.Errorf("couldn't unarmor bytes: %v", err) - } - - switch header[headerVersion] { - case "0.0.0": - return bz, defaultAlgo, err - case "0.0.1": - if header[headerType] == "" { - header[headerType] = defaultAlgo - } - return bz, header[headerType], err - case "": - return nil, "", fmt.Errorf("header's version field is empty") - default: - err = fmt.Errorf("unrecognized version: %v", header[headerVersion]) - return nil, "", err - } -} - -func unarmorBytes(armorStr, blockType string) (bz []byte, header map[string]string, err error) { - bType, header, bz, err := armor.DecodeArmor(armorStr) - if err != nil { - return - } - if bType != blockType { - err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType) - return - } - return -} - -//----------------------------------------------------------------- -// encrypt/decrypt with armor - -// Encrypt and armor the private key. -func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string, algo string) string { - saltBytes, encBytes := encryptPrivKey(privKey, passphrase) - header := map[string]string{ - "kdf": "bcrypt", - "salt": fmt.Sprintf("%X", saltBytes), - } - if algo != "" { - header[headerType] = algo - } - armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes) - return armorStr -} - -// encrypt the given privKey with the passphrase using a randomly -// generated salt and the xsalsa20 cipher. returns the salt and the -// encrypted priv key. -func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { - saltBytes = crypto.CRandBytes(16) - key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter) - if err != nil { - tmos.Exit("Error generating bcrypt key from passphrase: " + err.Error()) - } - key = crypto.Sha256(key) // get 32 bytes - privKeyBytes := privKey.Bytes() - return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key) -} - -// UnarmorDecryptPrivKey returns the privkey byte slice, a string of the algo type, and an error -func UnarmorDecryptPrivKey(armorStr string, passphrase string) (privKey crypto.PrivKey, algo string, err error) { - blockType, header, encBytes, err := armor.DecodeArmor(armorStr) - if err != nil { - return privKey, "", err - } - if blockType != blockTypePrivKey { - return privKey, "", fmt.Errorf("unrecognized armor type: %v", blockType) - } - if header["kdf"] != "bcrypt" { - return privKey, "", fmt.Errorf("unrecognized KDF type: %v", header["KDF"]) - } - if header["salt"] == "" { - return privKey, "", fmt.Errorf("missing salt bytes") - } - saltBytes, err := hex.DecodeString(header["salt"]) - if err != nil { - return privKey, "", fmt.Errorf("error decoding salt: %v", err.Error()) - } - privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) - - if header[headerType] == "" { - header[headerType] = defaultAlgo - } - return privKey, header[headerType], err -} - -func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) { - key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), BcryptSecurityParameter) - if err != nil { - tmos.Exit("error generating bcrypt key from passphrase: " + err.Error()) - } - key = crypto.Sha256(key) // Get 32 bytes - privKeyBytes, err := xsalsa20symmetric.DecryptSymmetric(encBytes, key) - if err != nil && err.Error() == "Ciphertext decryption failed" { - return privKey, keyerror.NewErrWrongPassword() - } else if err != nil { - return privKey, err - } - privKey, err = cryptoAmino.PrivKeyFromBytes(privKeyBytes) - return privKey, err -} diff --git a/crypto/keys/mintkey/mintkey_bench_test.go b/crypto/keys/mintkey/mintkey_bench_test.go deleted file mode 100644 index 1dd0b36cb373..000000000000 --- a/crypto/keys/mintkey/mintkey_bench_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package mintkey - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/crypto/bcrypt" - - "github.com/tendermint/tendermint/crypto" -) - -func BenchmarkBcryptGenerateFromPassword(b *testing.B) { - passphrase := []byte("passphrase") - for securityParam := 9; securityParam < 16; securityParam++ { - param := securityParam - b.Run(fmt.Sprintf("benchmark-security-param-%d", param), func(b *testing.B) { - saltBytes := crypto.CRandBytes(16) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := bcrypt.GenerateFromPassword(saltBytes, passphrase, param) - require.Nil(b, err) - } - }) - } -} diff --git a/crypto/keys/mintkey/mintkey_test.go b/crypto/keys/mintkey/mintkey_test.go deleted file mode 100644 index b0c836e9a2da..000000000000 --- a/crypto/keys/mintkey/mintkey_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package mintkey_test - -import ( - "bytes" - "errors" - "io" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/armor" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" -) - -func TestArmorUnarmorPrivKey(t *testing.T) { - priv := secp256k1.GenPrivKey() - armor := mintkey.EncryptArmorPrivKey(priv, "passphrase", "") - _, _, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase") - require.Error(t, err) - decrypted, algo, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase") - require.NoError(t, err) - require.Equal(t, string(keys.Secp256k1), algo) - require.True(t, priv.Equals(decrypted)) -} - -func TestArmorUnarmorPubKey(t *testing.T) { - // Select the encryption and storage for your cryptostore - cstore := keys.NewInMemory() - - // Add keys and see they return in alphabetical order - info, _, err := cstore.CreateMnemonic("Bob", keys.English, "passphrase", keys.Secp256k1) - require.NoError(t, err) - armored := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "") - pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armored) - require.NoError(t, err) - pub, err := cryptoAmino.PubKeyFromBytes(pubBytes) - require.NoError(t, err) - require.Equal(t, string(keys.Secp256k1), algo) - require.True(t, pub.Equals(info.GetPubKey())) - - armored = mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "unknown") - pubBytes, algo, err = mintkey.UnarmorPubKeyBytes(armored) - require.NoError(t, err) - pub, err = cryptoAmino.PubKeyFromBytes(pubBytes) - require.NoError(t, err) - require.Equal(t, "unknown", algo) - require.True(t, pub.Equals(info.GetPubKey())) - - armored, err = cstore.ExportPrivKey("Bob", "passphrase", "alessio") - require.NoError(t, err) - _, _, err = mintkey.UnarmorPubKeyBytes(armored) - require.Equal(t, `couldn't unarmor bytes: unrecognized armor type "TENDERMINT PRIVATE KEY", expected: "TENDERMINT PUBLIC KEY"`, err.Error()) - - // armor pubkey manually - header := map[string]string{ - "version": "0.0.0", - "type": "unknown", - } - armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) - _, algo, err = mintkey.UnarmorPubKeyBytes(armored) - require.NoError(t, err) - // return secp256k1 if version is 0.0.0 - require.Equal(t, "secp256k1", algo) - - // missing version header - header = map[string]string{ - "type": "unknown", - } - armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) - bz, algo, err := mintkey.UnarmorPubKeyBytes(armored) - require.Nil(t, bz) - require.Empty(t, algo) - require.Error(t, err) - require.Equal(t, "header's version field is empty", err.Error()) - - // unknown version header - header = map[string]string{ - "type": "unknown", - "version": "unknown", - } - armored = armor.EncodeArmor("TENDERMINT PUBLIC KEY", header, pubBytes) - bz, algo, err = mintkey.UnarmorPubKeyBytes(armored) - require.Nil(t, bz) - require.Empty(t, algo) - require.Error(t, err) - require.Equal(t, "unrecognized version: unknown", err.Error()) -} - -func TestArmorInfoBytes(t *testing.T) { - bs := []byte("test") - armoredString := mintkey.ArmorInfoBytes(bs) - unarmoredBytes, err := mintkey.UnarmorInfoBytes(armoredString) - require.NoError(t, err) - require.True(t, bytes.Equal(bs, unarmoredBytes)) -} - -func TestUnarmorInfoBytesErrors(t *testing.T) { - unarmoredBytes, err := mintkey.UnarmorInfoBytes("") - require.Error(t, err) - require.True(t, errors.Is(io.EOF, err)) - require.Nil(t, unarmoredBytes) - - header := map[string]string{ - "type": "Info", - "version": "0.0.1", - } - unarmoredBytes, err = mintkey.UnarmorInfoBytes(armor.EncodeArmor( - "TENDERMINT KEY INFO", header, []byte("plain-text"))) - require.Error(t, err) - require.Equal(t, "unrecognized version: 0.0.1", err.Error()) - require.Nil(t, unarmoredBytes) -} diff --git a/crypto/keys/multisig/amino.go b/crypto/keys/multisig/amino.go new file mode 100644 index 000000000000..4849a23173d2 --- /dev/null +++ b/crypto/keys/multisig/amino.go @@ -0,0 +1,88 @@ +package multisig + +import ( + types "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// tmMultisig implements a K of N threshold multisig. It is used for +// Amino JSON marshaling of LegacyAminoPubKey (see below for details). +// +// This struct is copy-pasted from: +// https://github.com/tendermint/tendermint/blob/v0.33.9/crypto/multisig/threshold_pubkey.go +// +// This struct was used in the SDK <=0.39. In 0.40 and the switch to protobuf, +// it has been converted to LegacyAminoPubKey. However, there's one difference: +// the threshold field was an `uint` before, and an `uint32` after. This caused +// amino marshaling to be breaking: amino marshals `uint32` as a JSON number, +// and `uint` as a JSON string. +// +// In this file, we're overriding LegacyAminoPubKey's default JSON Amino +// marshaling by using this struct. Please note that we are NOT overriding the +// Amino binary marshaling, as that _might_ introduce breaking changes in the +// keyring, where multisigs are amino-binary-encoded. +// +// ref: https://github.com/cosmos/cosmos-sdk/issues/8776 +type tmMultisig struct { + K uint `json:"threshold"` + PubKeys []cryptotypes.PubKey `json:"pubkeys"` +} + +// protoToTm converts a LegacyAminoPubKey into a tmMultisig. +func protoToTm(protoPk *LegacyAminoPubKey) (tmMultisig, error) { + var ok bool + pks := make([]cryptotypes.PubKey, len(protoPk.PubKeys)) + for i, pk := range protoPk.PubKeys { + pks[i], ok = pk.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return tmMultisig{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (cryptotypes.PubKey)(nil), pk.GetCachedValue()) + } + } + + return tmMultisig{ + K: uint(protoPk.Threshold), + PubKeys: pks, + }, nil +} + +// tmToProto converts a tmMultisig into a LegacyAminoPubKey. +func tmToProto(tmPk tmMultisig) (*LegacyAminoPubKey, error) { + var err error + pks := make([]*types.Any, len(tmPk.PubKeys)) + for i, pk := range tmPk.PubKeys { + pks[i], err = types.NewAnyWithValue(pk) + if err != nil { + return nil, err + } + } + + return &LegacyAminoPubKey{ + Threshold: uint32(tmPk.K), + PubKeys: pks, + }, nil +} + +// MarshalAminoJSON overrides amino JSON unmarshaling. +func (m LegacyAminoPubKey) MarshalAminoJSON() (tmMultisig, error) { //nolint:golint + return protoToTm(&m) +} + +// UnmarshalAminoJSON overrides amino JSON unmarshaling. +func (m *LegacyAminoPubKey) UnmarshalAminoJSON(tmPk tmMultisig) error { + protoPk, err := tmToProto(tmPk) + if err != nil { + return err + } + + // Instead of just doing `*m = *protoPk`, we prefer to modify in-place the + // existing Anys inside `m` (instead of allocating new Anys), as so not to + // break the `.compat` fields in the existing Anys. + for i := range m.PubKeys { + m.PubKeys[i].TypeUrl = protoPk.PubKeys[i].TypeUrl + m.PubKeys[i].Value = protoPk.PubKeys[i].Value + } + m.Threshold = protoPk.Threshold + + return nil +} diff --git a/crypto/keys/multisig/codec.go b/crypto/keys/multisig/codec.go new file mode 100644 index 000000000000..d501e1b427cf --- /dev/null +++ b/crypto/keys/multisig/codec.go @@ -0,0 +1,33 @@ +package multisig + +import ( + "github.com/tendermint/tendermint/crypto/sr25519" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// TODO: Figure out API for others to either add their own pubkey types, or +// to make verify / marshal accept a AminoCdc. +const ( + PubKeyAminoRoute = "tendermint/PubKeyMultisigThreshold" +) + +//nolint +// Deprecated: Amino is being deprecated in the SDK. But even if you need to +// use Amino for some reason, please use `codec/legacy.Cdc` instead. +var AminoCdc = codec.NewLegacyAmino() + +func init() { + AminoCdc.RegisterInterface((*cryptotypes.PubKey)(nil), nil) + AminoCdc.RegisterConcrete(ed25519.PubKey{}, + ed25519.PubKeyName, nil) + AminoCdc.RegisterConcrete(sr25519.PubKey{}, + sr25519.PubKeyName, nil) + AminoCdc.RegisterConcrete(&secp256k1.PubKey{}, + secp256k1.PubKeyName, nil) + AminoCdc.RegisterConcrete(&LegacyAminoPubKey{}, + PubKeyAminoRoute, nil) +} diff --git a/crypto/keys/multisig/keys.pb.go b/crypto/keys/multisig/keys.pb.go new file mode 100644 index 000000000000..27ccf0fa5a27 --- /dev/null +++ b/crypto/keys/multisig/keys.pb.go @@ -0,0 +1,359 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/multisig/keys.proto + +package multisig + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "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 + +// LegacyAminoPubKey specifies a public key type +// which nests multiple public keys and a threshold, +// it uses legacy amino address rules. +type LegacyAminoPubKey struct { + Threshold uint32 `protobuf:"varint,1,opt,name=threshold,proto3" json:"threshold,omitempty" yaml:"threshold"` + PubKeys []*types.Any `protobuf:"bytes,2,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty" yaml:"pubkeys"` +} + +func (m *LegacyAminoPubKey) Reset() { *m = LegacyAminoPubKey{} } +func (m *LegacyAminoPubKey) String() string { return proto.CompactTextString(m) } +func (*LegacyAminoPubKey) ProtoMessage() {} +func (*LegacyAminoPubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_46b57537e097d47d, []int{0} +} +func (m *LegacyAminoPubKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LegacyAminoPubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LegacyAminoPubKey.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 *LegacyAminoPubKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_LegacyAminoPubKey.Merge(m, src) +} +func (m *LegacyAminoPubKey) XXX_Size() int { + return m.Size() +} +func (m *LegacyAminoPubKey) XXX_DiscardUnknown() { + xxx_messageInfo_LegacyAminoPubKey.DiscardUnknown(m) +} + +var xxx_messageInfo_LegacyAminoPubKey proto.InternalMessageInfo + +func init() { + proto.RegisterType((*LegacyAminoPubKey)(nil), "cosmos.crypto.multisig.LegacyAminoPubKey") +} + +func init() { proto.RegisterFile("cosmos/crypto/multisig/keys.proto", fileDescriptor_46b57537e097d47d) } + +var fileDescriptor_46b57537e097d47d = []byte{ + // 287 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcf, 0x2d, 0xcd, 0x29, 0xc9, 0x2c, + 0xce, 0x4c, 0xd7, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x83, + 0x28, 0xd1, 0x83, 0x28, 0xd1, 0x83, 0x29, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, + 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0x24, 0xd3, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0xc1, 0xbc, 0xa4, + 0xd2, 0x34, 0xfd, 0xc4, 0xbc, 0x4a, 0x88, 0x94, 0xd2, 0x62, 0x46, 0x2e, 0x41, 0x9f, 0xd4, 0xf4, + 0xc4, 0xe4, 0x4a, 0xc7, 0xdc, 0xcc, 0xbc, 0xfc, 0x80, 0xd2, 0x24, 0xef, 0xd4, 0x4a, 0x21, 0x23, + 0x2e, 0xce, 0x92, 0x8c, 0xa2, 0xd4, 0xe2, 0x8c, 0xfc, 0x9c, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, + 0x5e, 0x27, 0x91, 0x4f, 0xf7, 0xe4, 0x05, 0x2a, 0x13, 0x73, 0x73, 0xac, 0x94, 0xe0, 0x52, 0x4a, + 0x41, 0x08, 0x65, 0x42, 0x21, 0x5c, 0xdc, 0x05, 0xa5, 0x49, 0x39, 0x99, 0xc9, 0xf1, 0x20, 0x77, + 0x4a, 0x30, 0x29, 0x30, 0x6b, 0x70, 0x1b, 0x89, 0xe8, 0x41, 0xac, 0xd6, 0x83, 0x59, 0xad, 0xe7, + 0x98, 0x57, 0xe9, 0x24, 0xfb, 0xe8, 0x9e, 0x3c, 0x3b, 0xc4, 0xaa, 0xe2, 0x4f, 0xf7, 0xe4, 0xf9, + 0x20, 0xc6, 0x16, 0x94, 0x26, 0x81, 0x74, 0x2a, 0x05, 0x71, 0x41, 0xcc, 0x01, 0xc9, 0x5a, 0xb1, + 0x74, 0x2c, 0x90, 0x67, 0x70, 0xf2, 0x3e, 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, 0xc3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0x58, 0xb0, 0x81, + 0x29, 0xdd, 0xe2, 0x94, 0x6c, 0x58, 0x08, 0x82, 0x8c, 0x85, 0x07, 0x63, 0x12, 0x1b, 0xd8, 0x2d, + 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x76, 0x6e, 0xbf, 0x3c, 0x67, 0x01, 0x00, 0x00, +} + +func (m *LegacyAminoPubKey) 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 *LegacyAminoPubKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LegacyAminoPubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PubKeys) > 0 { + for iNdEx := len(m.PubKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PubKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintKeys(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Threshold != 0 { + i = encodeVarintKeys(dAtA, i, uint64(m.Threshold)) + i-- + dAtA[i] = 0x8 + } + 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 *LegacyAminoPubKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Threshold != 0 { + n += 1 + sovKeys(uint64(m.Threshold)) + } + if len(m.PubKeys) > 0 { + for _, e := range m.PubKeys { + l = e.Size() + 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 *LegacyAminoPubKey) 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: LegacyAminoPubKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LegacyAminoPubKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Threshold", wireType) + } + m.Threshold = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Threshold |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeys", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKeys = append(m.PubKeys, &types.Any{}) + if err := m.PubKeys[len(m.PubKeys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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/multisig/multisig.go b/crypto/keys/multisig/multisig.go new file mode 100644 index 000000000000..590e5ba11001 --- /dev/null +++ b/crypto/keys/multisig/multisig.go @@ -0,0 +1,166 @@ +package multisig + +import ( + fmt "fmt" + + tmcrypto "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + multisigtypes "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +var _ multisigtypes.PubKey = &LegacyAminoPubKey{} +var _ types.UnpackInterfacesMessage = &LegacyAminoPubKey{} + +// NewLegacyAminoPubKey returns a new LegacyAminoPubKey. +// Panics if len(pubKeys) < k or 0 >= k. +func NewLegacyAminoPubKey(k int, pubKeys []cryptotypes.PubKey) *LegacyAminoPubKey { + if k <= 0 { + panic("threshold k of n multisignature: k <= 0") + } + if len(pubKeys) < k { + panic("threshold k of n multisignature: len(pubKeys) < k") + } + anyPubKeys, err := packPubKeys(pubKeys) + if err != nil { + panic(err) + } + return &LegacyAminoPubKey{Threshold: uint32(k), PubKeys: anyPubKeys} +} + +// Address implements cryptotypes.PubKey Address method +func (m *LegacyAminoPubKey) Address() cryptotypes.Address { + return tmcrypto.AddressHash(m.Bytes()) +} + +// Bytes returns the proto encoded version of the LegacyAminoPubKey +func (m *LegacyAminoPubKey) Bytes() []byte { + return AminoCdc.MustMarshalBinaryBare(m) +} + +// VerifyMultisignature implements the multisigtypes.PubKey VerifyMultisignature method +func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetSignBytesFunc, sig *signing.MultiSignatureData) error { + bitarray := sig.BitArray + sigs := sig.Signatures + size := bitarray.Count() + pubKeys := m.GetPubKeys() + // ensure bit array is the correct size + if len(pubKeys) != size { + return fmt.Errorf("bit array size is incorrect %d", len(pubKeys)) + } + // ensure size of signature list + if len(sigs) < int(m.Threshold) || len(sigs) > size { + return fmt.Errorf("signature size is incorrect %d", len(sigs)) + } + // ensure at least k signatures are set + if bitarray.NumTrueBitsBefore(size) < int(m.Threshold) { + return fmt.Errorf("minimum number of signatures not set, have %d, expected %d", bitarray.NumTrueBitsBefore(size), int(m.Threshold)) + } + // index in the list of signatures which we are concerned with. + sigIndex := 0 + for i := 0; i < size; i++ { + if bitarray.GetIndex(i) { + si := sig.Signatures[sigIndex] + switch si := si.(type) { + case *signing.SingleSignatureData: + msg, err := getSignBytes(si.SignMode) + if err != nil { + return err + } + if !pubKeys[i].VerifySignature(msg, si.Signature) { + return fmt.Errorf("unable to verify signature at index %d", i) + } + case *signing.MultiSignatureData: + nestedMultisigPk, ok := pubKeys[i].(multisigtypes.PubKey) + if !ok { + return fmt.Errorf("unable to parse pubkey of index %d", i) + } + if err := nestedMultisigPk.VerifyMultisignature(getSignBytes, si); err != nil { + return err + } + default: + return fmt.Errorf("improper signature data type for index %d", sigIndex) + } + sigIndex++ + } + } + return nil +} + +// VerifySignature implements cryptotypes.PubKey VerifySignature method, +// it panics because it can't handle MultiSignatureData +// cf. https://github.com/cosmos/cosmos-sdk/issues/7109#issuecomment-686329936 +func (m *LegacyAminoPubKey) VerifySignature(msg []byte, sig []byte) bool { + panic("not implemented") +} + +// GetPubKeys implements the PubKey.GetPubKeys method +func (m *LegacyAminoPubKey) GetPubKeys() []cryptotypes.PubKey { + if m != nil { + pubKeys := make([]cryptotypes.PubKey, len(m.PubKeys)) + for i := 0; i < len(m.PubKeys); i++ { + pubKeys[i] = m.PubKeys[i].GetCachedValue().(cryptotypes.PubKey) + } + return pubKeys + } + + return nil +} + +// Equals returns true if m and other both have the same number of keys, and +// all constituent keys are the same, and in the same order. +func (m *LegacyAminoPubKey) Equals(key cryptotypes.PubKey) bool { + otherKey, ok := key.(multisigtypes.PubKey) + if !ok { + return false + } + pubKeys := m.GetPubKeys() + otherPubKeys := otherKey.GetPubKeys() + if m.GetThreshold() != otherKey.GetThreshold() || len(pubKeys) != len(otherPubKeys) { + return false + } + + for i := 0; i < len(pubKeys); i++ { + if !pubKeys[i].Equals(otherPubKeys[i]) { + return false + } + } + return true +} + +// GetThreshold implements the PubKey.GetThreshold method +func (m *LegacyAminoPubKey) GetThreshold() uint { + return uint(m.Threshold) +} + +// Type returns multisig type +func (m *LegacyAminoPubKey) Type() string { + return "PubKeyMultisigThreshold" +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (m *LegacyAminoPubKey) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, any := range m.PubKeys { + var pk cryptotypes.PubKey + err := unpacker.UnpackAny(any, &pk) + if err != nil { + return err + } + } + return nil +} + +func packPubKeys(pubKeys []cryptotypes.PubKey) ([]*types.Any, error) { + anyPubKeys := make([]*types.Any, len(pubKeys)) + + for i := 0; i < len(pubKeys); i++ { + any, err := types.NewAnyWithValue(pubKeys[i]) + if err != nil { + return nil, err + } + anyPubKeys[i] = any + } + return anyPubKeys, nil +} diff --git a/crypto/keys/multisig/multisig_test.go b/crypto/keys/multisig/multisig_test.go new file mode 100644 index 000000000000..1284c6c88476 --- /dev/null +++ b/crypto/keys/multisig/multisig_test.go @@ -0,0 +1,383 @@ +package multisig_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" +) + +func TestAddress(t *testing.T) { + msg := []byte{1, 2, 3, 4} + pubKeys, _ := generatePubKeysAndSignatures(5, msg) + multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubKeys) + + require.Len(t, multisigKey.Address().Bytes(), 20) +} + +func TestEquals(t *testing.T) { + pubKey1 := secp256k1.GenPrivKey().PubKey() + pubKey2 := secp256k1.GenPrivKey().PubKey() + + multisigKey := kmultisig.NewLegacyAminoPubKey(1, []cryptotypes.PubKey{pubKey1, pubKey2}) + otherMultisigKey := kmultisig.NewLegacyAminoPubKey(1, []cryptotypes.PubKey{pubKey1, multisigKey}) + + testCases := []struct { + msg string + other cryptotypes.PubKey + expectEq bool + }{ + { + "equals with proto pub key", + &kmultisig.LegacyAminoPubKey{Threshold: 1, PubKeys: multisigKey.PubKeys}, + true, + }, + { + "different threshold", + &kmultisig.LegacyAminoPubKey{Threshold: 2, PubKeys: multisigKey.PubKeys}, + false, + }, + { + "different pub keys length", + &kmultisig.LegacyAminoPubKey{Threshold: 1, PubKeys: []*types.Any{multisigKey.PubKeys[0]}}, + false, + }, + { + "different pub keys", + otherMultisigKey, + false, + }, + { + "different types", + secp256k1.GenPrivKey().PubKey(), + false, + }, + { + "ensure that reordering pubkeys is treated as a different pubkey", + reorderPubKey(multisigKey), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := multisigKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + +func TestVerifyMultisignature(t *testing.T) { + var ( + pk multisig.PubKey + sig *signing.MultiSignatureData + ) + msg := []byte{1, 2, 3, 4} + signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil } + + testCases := []struct { + msg string + malleate func() + expectPass bool + }{ + { + "nested multisignature", + func() { + genPk, genSig := generateNestedMultiSignature(3, msg) + sig = genSig + pk = genPk + }, + true, + }, + { + "wrong size for sig bit array", + func() { + pubKeys, _ := generatePubKeysAndSignatures(3, msg) + pk = kmultisig.NewLegacyAminoPubKey(3, pubKeys) + sig = multisig.NewMultisig(1) + }, + false, + }, + { + "single signature data, expects the first k signatures to be valid", + func() { + k := 2 + signingIndices := []int{0, 3, 1} + pubKeys, sigs := generatePubKeysAndSignatures(5, msg) + pk = kmultisig.NewLegacyAminoPubKey(k, pubKeys) + sig = multisig.NewMultisig(len(pubKeys)) + signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil } + + for i := 0; i < k-1; i++ { + signingIndex := signingIndices[i] + require.NoError( + t, + multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys), + ) + require.Error( + t, + pk.VerifyMultisignature(signBytesFn, sig), + "multisig passed when i < k, i %d", i, + ) + require.NoError( + t, + multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys), + ) + require.Equal( + t, + i+1, + len(sig.Signatures), + "adding a signature for the same pubkey twice increased signature count by 2, index %d", i, + ) + } + require.Error( + t, + pk.VerifyMultisignature(signBytesFn, sig), + "multisig passed with k - 1 sigs", + ) + require.NoError( + t, + multisig.AddSignatureFromPubKey( + sig, + sigs[signingIndices[k]], + pubKeys[signingIndices[k]], + pubKeys, + ), + ) + require.NoError( + t, + pk.VerifyMultisignature(signBytesFn, sig), + "multisig failed after k good signatures", + ) + }, + true, + }, + { + "duplicate signatures", + func() { + pubKeys, sigs := generatePubKeysAndSignatures(5, msg) + pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys) + sig = multisig.NewMultisig(5) + + require.Error(t, pk.VerifyMultisignature(signBytesFn, sig)) + multisig.AddSignatureFromPubKey(sig, sigs[0], pubKeys[0], pubKeys) + // Add second signature manually + sig.Signatures = append(sig.Signatures, sigs[0]) + }, + false, + }, + { + "unable to verify signature", + func() { + pubKeys, _ := generatePubKeysAndSignatures(2, msg) + _, sigs := generatePubKeysAndSignatures(2, msg) + pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys) + sig = multisig.NewMultisig(2) + multisig.AddSignatureFromPubKey(sig, sigs[0], pubKeys[0], pubKeys) + multisig.AddSignatureFromPubKey(sig, sigs[1], pubKeys[1], pubKeys) + }, + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + tc.malleate() + err := pk.VerifyMultisignature(signBytesFn, sig) + if tc.expectPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestAddSignatureFromPubKeyNilCheck(t *testing.T) { + pkSet, sigs := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4}) + multisignature := multisig.NewMultisig(5) + + // verify no error is returned with all non-nil values + err := multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], pkSet) + require.NoError(t, err) + // verify error is returned when key value is nil + err = multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], nil) + require.Error(t, err) + // verify error is returned when pubkey value is nil + err = multisig.AddSignatureFromPubKey(multisignature, sigs[0], nil, pkSet) + require.Error(t, err) + // verify error is returned when signature value is nil + err = multisig.AddSignatureFromPubKey(multisignature, nil, pkSet[0], pkSet) + require.Error(t, err) + // verify error is returned when multisignature value is nil + err = multisig.AddSignatureFromPubKey(nil, sigs[0], pkSet[0], pkSet) + require.Error(t, err) +} + +func TestMultiSigMigration(t *testing.T) { + msg := []byte{1, 2, 3, 4} + pkSet, sigs := generatePubKeysAndSignatures(2, msg) + multisignature := multisig.NewMultisig(2) + + multisigKey := kmultisig.NewLegacyAminoPubKey(2, pkSet) + signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil } + + cdc := codec.NewLegacyAmino() + + require.NoError(t, multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], pkSet)) + + // create a StdSignature for msg, and convert it to sigV2 + sig := legacytx.StdSignature{PubKey: pkSet[1], Signature: sigs[1].(*signing.SingleSignatureData).Signature} + sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, sig) + require.NoError(t, multisig.AddSignatureV2(multisignature, sigV2, pkSet)) + + require.NoError(t, err) + require.NotNil(t, sigV2) + + require.NoError(t, multisigKey.VerifyMultisignature(signBytesFn, multisignature)) +} + +func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { + msg := []byte{1, 2, 3, 4} + pubkeys, _ := generatePubKeysAndSignatures(5, msg) + multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubkeys) + + ab, err := legacy.Cdc.MarshalBinaryLengthPrefixed(multisigKey) + require.NoError(t, err) + // like other cryptotypes.Pubkey implementations (e.g. ed25519.PubKey), + // LegacyAminoPubKey should be deserializable into a cryptotypes.LegacyAminoPubKey: + var pubKey kmultisig.LegacyAminoPubKey + err = legacy.Cdc.UnmarshalBinaryLengthPrefixed(ab, &pubKey) + require.NoError(t, err) + + require.Equal(t, multisigKey.Equals(&pubKey), true) +} + +func generatePubKeysAndSignatures(n int, msg []byte) (pubKeys []cryptotypes.PubKey, signatures []signing.SignatureData) { + pubKeys = make([]cryptotypes.PubKey, n) + signatures = make([]signing.SignatureData, n) + + for i := 0; i < n; i++ { + privkey := secp256k1.GenPrivKey() + pubKeys[i] = privkey.PubKey() + + sig, _ := privkey.Sign(msg) + signatures[i] = &signing.SingleSignatureData{Signature: sig} + } + return +} + +func generateNestedMultiSignature(n int, msg []byte) (multisig.PubKey, *signing.MultiSignatureData) { + pubKeys := make([]cryptotypes.PubKey, n) + signatures := make([]signing.SignatureData, n) + bitArray := cryptotypes.NewCompactBitArray(n) + for i := 0; i < n; i++ { + nestedPks, nestedSigs := generatePubKeysAndSignatures(5, msg) + nestedBitArray := cryptotypes.NewCompactBitArray(5) + for j := 0; j < 5; j++ { + nestedBitArray.SetIndex(j, true) + } + nestedSig := &signing.MultiSignatureData{ + BitArray: nestedBitArray, + Signatures: nestedSigs, + } + signatures[i] = nestedSig + pubKeys[i] = kmultisig.NewLegacyAminoPubKey(5, nestedPks) + bitArray.SetIndex(i, true) + } + return kmultisig.NewLegacyAminoPubKey(n, pubKeys), &signing.MultiSignatureData{ + BitArray: bitArray, + Signatures: signatures, + } +} + +func reorderPubKey(pk *kmultisig.LegacyAminoPubKey) (other *kmultisig.LegacyAminoPubKey) { + pubkeysCpy := make([]*types.Any, len(pk.PubKeys)) + copy(pubkeysCpy, pk.PubKeys) + pubkeysCpy[0] = pk.PubKeys[1] + pubkeysCpy[1] = pk.PubKeys[0] + other = &kmultisig.LegacyAminoPubKey{Threshold: 2, PubKeys: pubkeysCpy} + return +} + +func TestAminoBinary(t *testing.T) { + pubKey1 := secp256k1.GenPrivKey().PubKey() + pubKey2 := secp256k1.GenPrivKey().PubKey() + multisigKey := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pubKey1, pubKey2}) + + // Do a round-trip key->bytes->key. + bz, err := legacy.Cdc.MarshalBinaryBare(multisigKey) + require.NoError(t, err) + var newMultisigKey cryptotypes.PubKey + err = legacy.Cdc.UnmarshalBinaryBare(bz, &newMultisigKey) + require.NoError(t, err) + require.Equal(t, multisigKey.Threshold, newMultisigKey.(*kmultisig.LegacyAminoPubKey).Threshold) +} + +func TestAminoMarshalJSON(t *testing.T) { + pubKey1 := secp256k1.GenPrivKey().PubKey() + pubKey2 := secp256k1.GenPrivKey().PubKey() + multisigKey := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pubKey1, pubKey2}) + + bz, err := legacy.Cdc.MarshalJSON(multisigKey) + require.NoError(t, err) + + // Note the quotes around `"2"`. They are present because we are overriding + // the Amino JSON marshaling of LegacyAminoPubKey (using tmMultisig). + // Without the override, there would not be any quotes. + require.Contains(t, string(bz), "\"threshold\":\"2\"") +} + +func TestAminoUnmarshalJSON(t *testing.T) { + // This is a real multisig from the Akash chain. It has been exported from + // v0.39, hence the `threshold` field as a string. + // We are testing that when unmarshaling this JSON into a LegacyAminoPubKey + // with amino, there's no error. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8776 + pkJSON := `{ + "type": "tendermint/PubKeyMultisigThreshold", + "value": { + "pubkeys": [ + { + "type": "tendermint/PubKeySecp256k1", + "value": "AzYxq2VNeD10TyABwOgV36OVWDIMn8AtI4OFA0uQX2MK" + }, + { + "type": "tendermint/PubKeySecp256k1", + "value": "A39cdsrm00bTeQ3RVZVqjkH8MvIViO9o99c8iLiNO35h" + }, + { + "type": "tendermint/PubKeySecp256k1", + "value": "A/uLLCZph8MkFg2tCxqSMGwFfPHdt1kkObmmrqy9aiYD" + }, + { + "type": "tendermint/PubKeySecp256k1", + "value": "A4mOMhM5gPDtBAkAophjRs6uDGZm4tD4Dbok3ai4qJi8" + }, + { + "type": "tendermint/PubKeySecp256k1", + "value": "A90icFucrjNNz2SAdJWMApfSQcARIqt+M2x++t6w5fFs" + } + ], + "threshold": "3" + } +}` + + cdc := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(cdc) + + var pk cryptotypes.PubKey + err := cdc.UnmarshalJSON([]byte(pkJSON), &pk) + require.NoError(t, err) + require.Equal(t, uint32(3), pk.(*kmultisig.LegacyAminoPubKey).Threshold) +} diff --git a/crypto/keys/secp256k1/bench_test.go b/crypto/keys/secp256k1/bench_test.go new file mode 100644 index 000000000000..423f7a5a52de --- /dev/null +++ b/crypto/keys/secp256k1/bench_test.go @@ -0,0 +1,27 @@ +package secp256k1 + +import ( + "io" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/internal/benchmarking" + "github.com/cosmos/cosmos-sdk/crypto/types" +) + +func BenchmarkKeyGeneration(b *testing.B) { + benchmarkKeygenWrapper := func(reader io.Reader) types.PrivKey { + priv := genPrivKey(reader) + return &PrivKey{Key: priv} + } + benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper) +} + +func BenchmarkSigning(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkSigning(b, priv) +} + +func BenchmarkVerification(b *testing.B) { + priv := GenPrivKey() + benchmarking.BenchmarkVerification(b, priv) +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/.gitignore b/crypto/keys/secp256k1/internal/secp256k1/.gitignore new file mode 100644 index 000000000000..802b6744a1d9 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +*~ diff --git a/crypto/keys/secp256k1/internal/secp256k1/LICENSE b/crypto/keys/secp256k1/internal/secp256k1/LICENSE new file mode 100644 index 000000000000..f9090e14236b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2010 The Go Authors. All rights reserved. +Copyright (c) 2011 ThePiachu. All rights reserved. +Copyright (c) 2015 Jeffrey Wilcke. All rights reserved. +Copyright (c) 2015 Felix Lange. All rights reserved. +Copyright (c) 2015 Gustav Simonsson. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/crypto/keys/secp256k1/internal/secp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/README.md new file mode 100644 index 000000000000..d899ca27005a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/README.md @@ -0,0 +1,3 @@ +This package is copied from https://github.com/ethereum/go-ethereum/tree/729bf365b5f17325be9107b63b233da54100eec6/crypto/secp256k1 + +Unlike the rest of go-ethereum it is MIT licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream. diff --git a/crypto/keys/secp256k1/internal/secp256k1/curve.go b/crypto/keys/secp256k1/internal/secp256k1/curve.go new file mode 100644 index 000000000000..7a2387365c6b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/curve.go @@ -0,0 +1,328 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// * The name of ThePiachu may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// nolint:gocritic +package secp256k1 + +import ( + "crypto/elliptic" + "math/big" + "unsafe" +) + +/* +#include "libsecp256k1/include/secp256k1.h" +extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, + const unsigned char *point, + const unsigned char *scalar); +*/ +import "C" + +const ( + // number of bits in a big.Word + wordBits = 32 << (uint64(^big.Word(0)) >> 63) + // number of bytes in a big.Word + wordBytes = wordBits / 8 +) + +// readBits encodes the absolute value of bigint as big-endian bytes. Callers +// must ensure that buf has enough space. If buf is too short the result will +// be incomplete. +func readBits(bigint *big.Int, buf []byte) { + i := len(buf) + for _, d := range bigint.Bits() { + for j := 0; j < wordBytes && i > 0; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } +} + +// This code is from https://github.com/ThePiachu/GoBit and implements +// several Koblitz elliptic curves over prime fields. +// +// The curve methods, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, +// z1) where x = x1/z1² and y = y1/z1³. The greatest speedups come +// when the whole calculation can be performed within the transform +// (as in ScalarMult and ScalarBaseMult). But even for Add and Double, +// it's faster to apply and reverse the transform than to operate in +// affine coordinates. + +// A BitCurve represents a Koblitz Curve with a=0. +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +type BitCurve struct { + P *big.Int // the order of the underlying field + N *big.Int // the order of the base point + B *big.Int // the constant of the BitCurve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +func (BitCurve *BitCurve) Params() *elliptic.CurveParams { + return &elliptic.CurveParams{ + P: BitCurve.P, + N: BitCurve.N, + B: BitCurve.B, + Gx: BitCurve.Gx, + Gy: BitCurve.Gy, + BitSize: BitCurve.BitSize, + } +} + +// IsOnCurve returns true if the given (x,y) lies on the BitCurve. +func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ + b + y2 := new(big.Int).Mul(y, y) //y² + y2.Mod(y2, BitCurve.P) //y²%P + + x3 := new(big.Int).Mul(x, x) //x² + x3.Mul(x3, x) //x³ + + x3.Add(x3, BitCurve.B) //x³+B + x3.Mod(x3, BitCurve.P) //(x³+B)%P + + return x3.Cmp(y2) == 0 +} + +//TODO: double check if the function is okay +// affineFromJacobian reverses the Jacobian transform. See the comment at the +// top of the file. +func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, BitCurve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, BitCurve.P) + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, BitCurve.P) + return +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, BitCurve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, BitCurve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, BitCurve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, BitCurve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, BitCurve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, BitCurve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, BitCurve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, BitCurve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + // Ensure scalar is exactly 32 bytes. We pad always, even if + // scalar is 32 bytes long, to avoid a timing side channel. + if len(scalar) > 32 { + panic("can't handle scalars > 256 bits") + } + // NOTE: potential timing issue + padded := make([]byte, 32) + copy(padded[32-len(scalar):], scalar) + scalar = padded + + // Do the multiplication in C, updating point. + point := make([]byte, 64) + readBits(Bx, point[:32]) + readBits(By, point[32:]) + + pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) + scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) + res := C.secp256k1_ext_scalar_mul(context, pointPtr, scalarPtr) + + // Unpack the result and clear temporaries. + x := new(big.Int).SetBytes(point[:32]) + y := new(big.Int).SetBytes(point[32:]) + for i := range point { + point[i] = 0 + } + for i := range padded { + scalar[i] = 0 + } + if res != 1 { + return nil, nil + } + return x, y +} + +// ScalarBaseMult returns k*G, where G is the base point of the group and k is +// an integer in big-endian form. +func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k) +} + +// Marshal converts a point into the form specified in section 4.3.6 of ANSI +// X9.62. +func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (BitCurve.BitSize + 7) >> 3 + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point flag + readBits(x, ret[1:1+byteLen]) + readBits(y, ret[1+byteLen:]) + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (BitCurve.BitSize + 7) >> 3 + if len(data) != 1+2*byteLen { + return + } + if data[0] != 4 { // uncompressed form + return + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return +} + +var theCurve = new(BitCurve) + +func init() { + // See SEC 2 section 2.7.1 + // curve parameters taken from: + // http://www.secg.org/sec2-v2.pdf + theCurve.P, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 0) + theCurve.N, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 0) + theCurve.B, _ = new(big.Int).SetString("0x0000000000000000000000000000000000000000000000000000000000000007", 0) + theCurve.Gx, _ = new(big.Int).SetString("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 0) + theCurve.Gy, _ = new(big.Int).SetString("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 0) + theCurve.BitSize = 256 +} + +// S256 returns a BitCurve which implements secp256k1. +func S256() *BitCurve { + return theCurve +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/ext.h b/crypto/keys/secp256k1/internal/secp256k1/ext.h new file mode 100644 index 000000000000..e422fe4b496d --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/ext.h @@ -0,0 +1,130 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// secp256k1_context_create_sign_verify creates a context for signing and signature verification. +static secp256k1_context* secp256k1_context_create_sign_verify() { + return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); +} + +// secp256k1_ext_ecdsa_recover recovers the public key of an encoded compact signature. +// +// Returns: 1: recovery was successful +// 0: recovery was not successful +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL) +// In: sigdata: pointer to a 65-byte signature with the recovery id at the end (cannot be NULL) +// msgdata: pointer to a 32-byte message (cannot be NULL) +static int secp256k1_ext_ecdsa_recover( + const secp256k1_context* ctx, + unsigned char *pubkey_out, + const unsigned char *sigdata, + const unsigned char *msgdata +) { + secp256k1_ecdsa_recoverable_signature sig; + secp256k1_pubkey pubkey; + + if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) { + return 0; + } + if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) { + return 0; + } + size_t outputlen = 65; + return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED); +} + +// secp256k1_ext_ecdsa_verify verifies an encoded compact signature. +// +// Returns: 1: signature is valid +// 0: signature is invalid +// Args: ctx: pointer to a context object (cannot be NULL) +// In: sigdata: pointer to a 64-byte signature (cannot be NULL) +// msgdata: pointer to a 32-byte message (cannot be NULL) +// pubkeydata: pointer to public key data (cannot be NULL) +// pubkeylen: length of pubkeydata +static int secp256k1_ext_ecdsa_verify( + const secp256k1_context* ctx, + const unsigned char *sigdata, + const unsigned char *msgdata, + const unsigned char *pubkeydata, + size_t pubkeylen +) { + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata)) { + return 0; + } + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) { + return 0; + } + return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey); +} + +// secp256k1_ext_reencode_pubkey decodes then encodes a public key. It can be used to +// convert between public key formats. The input/output formats are chosen depending on the +// length of the input/output buffers. +// +// Returns: 1: conversion successful +// 0: conversion unsuccessful +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: out: output buffer that will contain the reencoded key (cannot be NULL) +// In: outlen: length of out (33 for compressed keys, 65 for uncompressed keys) +// pubkeydata: the input public key (cannot be NULL) +// pubkeylen: length of pubkeydata +static int secp256k1_ext_reencode_pubkey( + const secp256k1_context* ctx, + unsigned char *out, + size_t outlen, + const unsigned char *pubkeydata, + size_t pubkeylen +) { + secp256k1_pubkey pubkey; + + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) { + return 0; + } + unsigned int flag = (outlen == 33) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED; + return secp256k1_ec_pubkey_serialize(ctx, out, &outlen, &pubkey, flag); +} + +// secp256k1_ext_scalar_mul multiplies a point by a scalar in constant time. +// +// Returns: 1: multiplication was successful +// 0: scalar was invalid (zero or overflow) +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: point: the multiplied point (usually secret) +// In: point: pointer to a 64-byte public point, +// encoded as two 256bit big-endian numbers. +// scalar: a 32-byte scalar with which to multiply the point +int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_fe feX, feY; + secp256k1_gej res; + secp256k1_ge ge; + secp256k1_scalar s; + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + (void)ctx; + + secp256k1_fe_set_b32(&feX, point); + secp256k1_fe_set_b32(&feY, point+32); + secp256k1_ge_set_xy(&ge, &feX, &feY); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + secp256k1_ecmult_const(&res, &ge, &s); + secp256k1_ge_set_gej(&ge, &res); + /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */ + secp256k1_fe_normalize(&ge.x); + secp256k1_fe_normalize(&ge.y); + secp256k1_fe_get_b32(point, &ge.x); + secp256k1_fe_get_b32(point+32, &ge.y); + ret = 1; + } + secp256k1_scalar_clear(&s); + return ret; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.gitignore b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.gitignore new file mode 100644 index 000000000000..87fea161ba5a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.gitignore @@ -0,0 +1,49 @@ +bench_inv +bench_ecdh +bench_sign +bench_verify +bench_schnorr_verify +bench_recover +bench_internal +tests +exhaustive_tests +gen_context +*.exe +*.so +*.a +!.gitignore + +Makefile +configure +.libs/ +Makefile.in +aclocal.m4 +autom4te.cache/ +config.log +config.status +*.tar.gz +*.la +libtool +.deps/ +.dirstamp +*.lo +*.o +*~ +src/libsecp256k1-config.h +src/libsecp256k1-config.h.in +src/ecmult_static_context.h +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver +src/stamp-h1 +libsecp256k1.pc diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml new file mode 100644 index 000000000000..24395292426d --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml @@ -0,0 +1,69 @@ +language: c +sudo: false +addons: + apt: + packages: libgmp-dev +compiler: + - clang + - gcc +cache: + directories: + - src/java/guava/ +env: + global: + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no + - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar + matrix: + - SCALAR=32bit RECOVERY=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=64bit + - FIELD=64bit RECOVERY=yes + - FIELD=64bit ENDOMORPHISM=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ASM=x86_64 + - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 + - FIELD=32bit ENDOMORPHISM=yes + - BIGNUM=no + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no STATICPRECOMPUTATION=no + - BUILD=distcheck + - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC + - EXTRAFLAGS=CFLAGS=-O0 + - BUILD=check-java ECDH=yes EXPERIMENTAL=yes +matrix: + fast_finish: true + include: + - compiler: clang + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 + - compiler: clang + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 +before_install: mkdir -p `dirname $GUAVA_JAR` +install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi +before_script: ./autogen.sh +script: + - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi + - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD +os: linux diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/COPYING b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/COPYING new file mode 100644 index 000000000000..4522a5990e28 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2013 Pieter Wuille + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am new file mode 100644 index 000000000000..c071fbe2753c --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am @@ -0,0 +1,177 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 + +lib_LTLIBRARIES = libsecp256k1.la +if USE_JNI +JNI_LIB = libsecp256k1_jni.la +noinst_LTLIBRARIES = $(JNI_LIB) +else +JNI_LIB = +endif +include_HEADERS = include/secp256k1.h +noinst_HEADERS = +noinst_HEADERS += src/scalar.h +noinst_HEADERS += src/scalar_4x64.h +noinst_HEADERS += src/scalar_8x32.h +noinst_HEADERS += src/scalar_low.h +noinst_HEADERS += src/scalar_impl.h +noinst_HEADERS += src/scalar_4x64_impl.h +noinst_HEADERS += src/scalar_8x32_impl.h +noinst_HEADERS += src/scalar_low_impl.h +noinst_HEADERS += src/group.h +noinst_HEADERS += src/group_impl.h +noinst_HEADERS += src/num_gmp.h +noinst_HEADERS += src/num_gmp_impl.h +noinst_HEADERS += src/ecdsa.h +noinst_HEADERS += src/ecdsa_impl.h +noinst_HEADERS += src/eckey.h +noinst_HEADERS += src/eckey_impl.h +noinst_HEADERS += src/ecmult.h +noinst_HEADERS += src/ecmult_impl.h +noinst_HEADERS += src/ecmult_const.h +noinst_HEADERS += src/ecmult_const_impl.h +noinst_HEADERS += src/ecmult_gen.h +noinst_HEADERS += src/ecmult_gen_impl.h +noinst_HEADERS += src/num.h +noinst_HEADERS += src/num_impl.h +noinst_HEADERS += src/field_10x26.h +noinst_HEADERS += src/field_10x26_impl.h +noinst_HEADERS += src/field_5x52.h +noinst_HEADERS += src/field_5x52_impl.h +noinst_HEADERS += src/field_5x52_int128_impl.h +noinst_HEADERS += src/field_5x52_asm_impl.h +noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h +noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h +noinst_HEADERS += src/util.h +noinst_HEADERS += src/testrand.h +noinst_HEADERS += src/testrand_impl.h +noinst_HEADERS += src/hash.h +noinst_HEADERS += src/hash_impl.h +noinst_HEADERS += src/field.h +noinst_HEADERS += src/field_impl.h +noinst_HEADERS += src/bench.h +noinst_HEADERS += contrib/lax_der_parsing.h +noinst_HEADERS += contrib/lax_der_parsing.c +noinst_HEADERS += contrib/lax_der_privatekey_parsing.h +noinst_HEADERS += contrib/lax_der_privatekey_parsing.c + +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libsecp256k1.pc + +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +endif +endif + +libsecp256k1_la_SOURCES = src/secp256k1.c +libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) + +libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c +libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) + +noinst_PROGRAMS = +if USE_BENCHMARK +noinst_PROGRAMS += bench_verify bench_sign bench_internal +bench_verify_SOURCES = src/bench_verify.c +bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_sign_SOURCES = src/bench_sign.c +bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_internal_SOURCES = src/bench_internal.c +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +endif + +TESTS = +if USE_TESTS +noinst_PROGRAMS += tests +tests_SOURCES = src/tests.c +tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +if !ENABLE_COVERAGE +tests_CPPFLAGS += -DVERIFY +endif +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +tests_LDFLAGS = -static +TESTS += tests +endif + +if USE_EXHAUSTIVE_TESTS +noinst_PROGRAMS += exhaustive_tests +exhaustive_tests_SOURCES = src/tests_exhaustive.c +exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES) +if !ENABLE_COVERAGE +exhaustive_tests_CPPFLAGS += -DVERIFY +endif +exhaustive_tests_LDADD = $(SECP_LIBS) +exhaustive_tests_LDFLAGS = -static +TESTS += exhaustive_tests +endif + +JAVAROOT=src/java +JAVAORG=org/bitcoin +JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar +CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA) +JAVA_FILES= \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \ + $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java + +if USE_JNI + +$(JAVA_GUAVA): + @echo Guava is missing. Fetch it via: \ + wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@) + @false + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java + $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test + +endif +endif + +if USE_ECMULT_STATIC_PRECOMPUTATION +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function + +gen_context_OBJECTS = gen_context.o +gen_context_BIN = gen_context$(BUILD_EXEEXT) +gen_%.o: src/gen_%.c + $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ + +$(gen_context_BIN): $(gen_context_OBJECTS) + $(CC_FOR_BUILD) $^ -o $@ + +$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h +$(tests_OBJECTS): src/ecmult_static_context.h +$(bench_internal_OBJECTS): src/ecmult_static_context.h + +src/ecmult_static_context.h: $(gen_context_BIN) + ./$(gen_context_BIN) + +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java +endif + +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) + +if ENABLE_MODULE_ECDH +include src/modules/ecdh/Makefile.am.include +endif + +if ENABLE_MODULE_RECOVERY +include src/modules/recovery/Makefile.am.include +endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md new file mode 100644 index 000000000000..8cd344ea8123 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/README.md @@ -0,0 +1,61 @@ +libsecp256k1 +============ + +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) + +Optimized C library for EC operations on curve secp256k1. + +This library is a work in progress and is being used to research best practices. Use at your own risk. + +Features: +* secp256k1 ECDSA signing/verification and key generation. +* Adding/multiplying private/public keys. +* Serialization/parsing of private keys, public keys, signatures. +* Constant time, constant memory access signing and pubkey generation. +* Derandomized DSA (via RFC6979 or with a caller provided function.) +* Very efficient implementation. + +Implementation details +---------------------- + +* General + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") +* Field operations + * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). + * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). + * Using 10 26-bit limbs. + * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). +* Scalar operations + * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. + * Using 4 64-bit limbs (relying on __int128 support in the compiler). + * Using 8 32-bit limbs. +* Group operations + * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). + * Use addition between points in Jacobian and affine coordinates where possible. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. +* Point multiplication for verification (a*P + b*G). + * Use wNAF notation for point multiplicands. + * Use a much larger window for multiples of G, using precomputed multiples. + * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. + * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. +* Point multiplication for signing + * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. + +Build steps +----------- + +libsecp256k1 is built using autotools: + + $ ./autogen.sh + $ ./configure + $ make + $ ./tests + $ sudo make install # optional diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/TODO b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/TODO new file mode 100644 index 000000000000..a300e1c5eb9b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/TODO @@ -0,0 +1,3 @@ +* Unit tests for fieldelem/groupelem, including ones intended to + trigger fieldelem's boundary cases. +* Complete constant-time operations for signing/keygen diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh new file mode 100755 index 000000000000..65286b935303 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +autoreconf -if --warnings=all diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 000000000000..1fc36276144a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_JNI_INCLUDE_DIR +# +# DESCRIPTION +# +# AX_JNI_INCLUDE_DIR finds include directories needed for compiling +# programs using the JNI interface. +# +# JNI include directories are usually in the Java distribution. This is +# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in +# that order. When this macro completes, a list of directories is left in +# the variable JNI_INCLUDE_DIRS. +# +# Example usage follows: +# +# AX_JNI_INCLUDE_DIR +# +# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS +# do +# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" +# done +# +# If you want to force a specific compiler: +# +# - at the configure.in level, set JAVAC=yourcompiler before calling +# AX_JNI_INCLUDE_DIR +# +# - at the configure level, setenv JAVAC +# +# Note: This macro can work with the autoconf M4 macros for Java programs. +# This particular macro is not part of the original set of macros. +# +# LICENSE +# +# Copyright (c) 2008 Don Anderson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) +AC_DEFUN([AX_JNI_INCLUDE_DIR],[ + +JNI_INCLUDE_DIRS="" + +if test "x$JAVA_HOME" != x; then + _JTOPDIR="$JAVA_HOME" +else + if test "x$JAVAC" = x; then + JAVAC=javac + fi + AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) + if test "x$_ACJNI_JAVAC" = xno; then + AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME]) + fi + _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") + _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` +fi + +case "$host_os" in + darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + _JINC="$_JTOPDIR/Headers";; + *) _JINC="$_JTOPDIR/include";; +esac +_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) +_AS_ECHO_LOG([_JINC=$_JINC]) + +# On Mac OS X 10.6.4, jni.h is a symlink: +# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h +# -> ../../CurrentJDK/Headers/jni.h. + +AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, +[ +if test -f "$_JINC/jni.h"; then + ac_cv_jni_header_path="$_JINC" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" +else + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + ac_cv_jni_header_path="$_JTOPDIR/include" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" + else + ac_cv_jni_header_path=none + fi +fi +]) + + + +# get the likely subdirectories for system specific java includes +case "$host_os" in +bsdi*) _JNI_INC_SUBDIRS="bsdos";; +darwin*) _JNI_INC_SUBDIRS="darwin";; +freebsd*) _JNI_INC_SUBDIRS="freebsd";; +linux*) _JNI_INC_SUBDIRS="linux genunix";; +osf*) _JNI_INC_SUBDIRS="alpha";; +solaris*) _JNI_INC_SUBDIRS="solaris";; +mingw*) _JNI_INC_SUBDIRS="win32";; +cygwin*) _JNI_INC_SUBDIRS="win32";; +*) _JNI_INC_SUBDIRS="genunix";; +esac + +if test "x$ac_cv_jni_header_path" != "xnone"; then + # add any subdirectories that are present + for JINCSUBDIR in $_JNI_INC_SUBDIRS + do + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi + done +fi +]) + +# _ACJNI_FOLLOW_SYMLINKS +# Follows symbolic links on , +# finally setting variable _ACJNI_FOLLOWED +# ---------------------------------------- +AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ +# find the include directory relative to the javac executable +_cur="$1" +while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do + AC_MSG_CHECKING([symlink for $_cur]) + _slink=`ls -ld "$_cur" | sed 's/.* -> //'` + case "$_slink" in + /*) _cur="$_slink";; + # 'X' avoids triggering unwanted echo options. + *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; + esac + AC_MSG_RESULT([$_cur]) +done +_ACJNI_FOLLOWED="$_cur" +])# _ACJNI diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 000000000000..77fd346a79a6 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_EXEEXT])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_cpp], ac_build_cpp)dnl +pushdef([ac_compile], ac_build_compile)dnl +pushdef([ac_link], ac_build_link)dnl + +save_cross_compiling=$cross_compiling +save_ac_tool_prefix=$ac_tool_prefix +cross_compiling=no +ac_tool_prefix= + +AC_PROG_CC +AC_PROG_CPP +AC_EXEEXT + +ac_tool_prefix=$save_ac_tool_prefix +cross_compiling=$save_cross_compiling + +dnl Restore the old definitions +dnl +popdef([ac_link])dnl +popdef([ac_compile])dnl +popdef([ac_cpp])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([LDFLAGS])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl Finally, set Makefile variables +dnl +BUILD_EXEEXT=$ac_build_exeext +BUILD_OBJEXT=$ac_build_objext +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 new file mode 100644 index 000000000000..b74acb8c1388 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 @@ -0,0 +1,69 @@ +dnl libsecp25k1 helper checks +AC_DEFUN([SECP_INT128_CHECK],[ +has_int128=$ac_cv_type___int128 +]) + +dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. +AC_DEFUN([SECP_64BIT_ASM_CHECK],[ +AC_MSG_CHECKING(for x86_64 assembly availability) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include ]],[[ + uint64_t a = 11, tmp; + __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); + ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) +AC_MSG_RESULT([$has_64bit_asm]) +]) + +dnl +AC_DEFUN([SECP_OPENSSL_CHECK],[ + has_libcrypto=no + m4_ifdef([PKG_CHECK_MODULES],[ + PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no]) + if test x"$has_libcrypto" = x"yes"; then + TEMP_LIBS="$LIBS" + LIBS="$LIBS $CRYPTO_LIBS" + AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no]) + LIBS="$TEMP_LIBS" + fi + ]) + if test x$has_libcrypto = xno; then + AC_CHECK_HEADER(openssl/crypto.h,[ + AC_CHECK_LIB(crypto, main,[ + has_libcrypto=yes + CRYPTO_LIBS=-lcrypto + AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed]) + ]) + ]) + LIBS= + fi +if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then + AC_MSG_CHECKING(for EC functions in libcrypto) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + #include ]],[[ + EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1); + ECDSA_sign(0, NULL, 0, NULL, NULL, eckey); + ECDSA_verify(0, NULL, 0, NULL, 0, eckey); + EC_KEY_free(eckey); + ECDSA_SIG *sig_openssl; + sig_openssl = ECDSA_SIG_new(); + (void)sig_openssl->r; + ECDSA_SIG_free(sig_openssl); + ]])],[has_openssl_ec=yes],[has_openssl_ec=no]) + AC_MSG_RESULT([$has_openssl_ec]) +fi +]) + +dnl +AC_DEFUN([SECP_GMP_CHECK],[ +if test x"$has_gmp" != x"yes"; then + CPPFLAGS_TEMP="$CPPFLAGS" + CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS" + LIBS_TEMP="$LIBS" + LIBS="$GMP_LIBS $LIBS" + AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])]) + CPPFLAGS="$CPPFLAGS_TEMP" + LIBS="$LIBS_TEMP" +fi +]) diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/configure.ac b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/configure.ac new file mode 100644 index 000000000000..e5fcbcb4edf2 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/configure.ac @@ -0,0 +1,493 @@ +AC_PREREQ([2.60]) +AC_INIT([libsecp256k1],[0.1]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AC_CANONICAL_HOST +AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) +AH_TOP([#define LIBSECP256K1_CONFIG_H]) +AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) +LT_INIT + +dnl make the compilation flags quiet unless V=1 is used +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +PKG_PROG_PKG_CONFIG + +AC_PATH_TOOL(AR, ar) +AC_PATH_TOOL(RANLIB, ranlib) +AC_PATH_TOOL(STRIP, strip) +AX_PROG_CC_FOR_BUILD + +if test "x$CFLAGS" = "x"; then + CFLAGS="-g" +fi + +AM_PROG_CC_C_O + +AC_PROG_CC_C89 +if test x"$ac_cv_prog_cc_c89" = x"no"; then + AC_MSG_ERROR([c89 compiler support required]) +fi +AM_PROG_AS + +case $host_os in + *darwin*) + if test x$cross_compiling != xyes; then + AC_PATH_PROG([BREW],brew,) + if test x$BREW != x; then + dnl These Homebrew packages may be keg-only, meaning that they won't be found + dnl in expected paths because they may conflict with system files. Ask + dnl Homebrew where each one is located, then adjust paths accordingly. + + openssl_prefix=`$BREW --prefix openssl 2>/dev/null` + gmp_prefix=`$BREW --prefix gmp 2>/dev/null` + if test x$openssl_prefix != x; then + PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" + export PKG_CONFIG_PATH + fi + if test x$gmp_prefix != x; then + GMP_CPPFLAGS="-I$gmp_prefix/include" + GMP_LIBS="-L$gmp_prefix/lib" + fi + else + AC_PATH_PROG([PORT],port,) + dnl if homebrew isn't installed and macports is, add the macports default paths + dnl as a last resort. + if test x$PORT != x; then + CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + fi + fi + fi + ;; +esac + +CFLAGS="$CFLAGS -W" + +warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings" +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $warn_CFLAGS" +AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden" +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +AC_ARG_ENABLE(benchmark, + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + [use_benchmark=$enableval], + [use_benchmark=no]) + +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + [enable_coverage=$enableval], + [enable_coverage=no]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + [use_tests=$enableval], + [use_tests=yes]) + +AC_ARG_ENABLE(openssl_tests, + AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), + [enable_openssl_tests=$enableval], + [enable_openssl_tests=auto]) + +AC_ARG_ENABLE(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + +AC_ARG_ENABLE(exhaustive_tests, + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), + [use_exhaustive_tests=$enableval], + [use_exhaustive_tests=yes]) + +AC_ARG_ENABLE(endomorphism, + AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), + [use_endomorphism=$enableval], + [use_endomorphism=no]) + +AC_ARG_ENABLE(ecmult_static_precomputation, + AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), + [use_ecmult_static_precomputation=$enableval], + [use_ecmult_static_precomputation=auto]) + +AC_ARG_ENABLE(module_ecdh, + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), + [enable_module_ecdh=$enableval], + [enable_module_ecdh=no]) + +AC_ARG_ENABLE(module_recovery, + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), + [enable_module_recovery=$enableval], + [enable_module_recovery=no]) + +AC_ARG_ENABLE(jni, + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]), + [use_jni=$enableval], + [use_jni=auto]) + +AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], +[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) + +AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], +[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) + +AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], +[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) + +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] +[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) + +AC_CHECK_TYPES([__int128]) + +AC_MSG_CHECKING([for __builtin_expect]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ], + [ AC_MSG_RESULT([no]) + ]) + +if test x"$enable_coverage" = x"yes"; then + AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) + CFLAGS="$CFLAGS -O0 --coverage" + LDFLAGS="--coverage" +else + CFLAGS="$CFLAGS -O3" +fi + +if test x"$use_ecmult_static_precomputation" != x"no"; then + save_cross_compiling=$cross_compiling + cross_compiling=no + TEMP_CC="$CC" + CC="$CC_FOR_BUILD" + AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([], [return 0])], + [working_native_cc=yes], + [working_native_cc=no],[dnl]) + CC="$TEMP_CC" + cross_compiling=$save_cross_compiling + + if test x"$working_native_cc" = x"no"; then + set_precomp=no + if test x"$use_ecmult_static_precomputation" = x"yes"; then + AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + else + AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + +if test x"$req_asm" = x"auto"; then + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" = x"yes"; then + set_asm=x86_64 + fi + if test x"$set_asm" = x; then + set_asm=no + fi +else + set_asm=$req_asm + case $set_asm in + x86_64) + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" != x"yes"; then + AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) + fi + ;; + arm) + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid assembly optimization selection]) + ;; + esac +fi + +if test x"$req_field" = x"auto"; then + if test x"set_asm" = x"x86_64"; then + set_field=64bit + fi + if test x"$set_field" = x; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_field=64bit + fi + fi + if test x"$set_field" = x; then + set_field=32bit + fi +else + set_field=$req_field + case $set_field in + 64bit) + if test x"$set_asm" != x"x86_64"; then + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available]) + fi + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid field implementation selection]) + ;; + esac +fi + +if test x"$req_scalar" = x"auto"; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_scalar=64bit + fi + if test x"$set_scalar" = x; then + set_scalar=32bit + fi +else + set_scalar=$req_scalar + case $set_scalar in + 64bit) + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available]) + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid scalar implementation selected]) + ;; + esac +fi + +if test x"$req_bignum" = x"auto"; then + SECP_GMP_CHECK + if test x"$has_gmp" = x"yes"; then + set_bignum=gmp + fi + + if test x"$set_bignum" = x; then + set_bignum=no + fi +else + set_bignum=$req_bignum + case $set_bignum in + gmp) + SECP_GMP_CHECK + if test x"$has_gmp" != x"yes"; then + AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available]) + fi + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid bignum implementation selection]) + ;; + esac +fi + +# select assembly optimization +use_external_asm=no + +case $set_asm in +x86_64) + AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) + ;; +arm) + use_external_asm=yes + ;; +no) + ;; +*) + AC_MSG_ERROR([invalid assembly optimizations]) + ;; +esac + +# select field implementation +case $set_field in +64bit) + AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation]) + ;; +32bit) + AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation]) + ;; +*) + AC_MSG_ERROR([invalid field implementation]) + ;; +esac + +# select bignum implementation +case $set_bignum in +gmp) + AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed]) + AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num]) + AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation]) + ;; +no) + AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation]) + AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation]) + ;; +*) + AC_MSG_ERROR([invalid bignum implementation]) + ;; +esac + +#select scalar implementation +case $set_scalar in +64bit) + AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation]) + ;; +32bit) + AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation]) + ;; +*) + AC_MSG_ERROR([invalid scalar implementation]) + ;; +esac + +if test x"$use_tests" = x"yes"; then + SECP_OPENSSL_CHECK + if test x"$has_openssl_ec" = x"yes"; then + if test x"$enable_openssl_tests" != x"no"; then + AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) + SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" + SECP_TEST_LIBS="$CRYPTO_LIBS" + + case $host in + *mingw*) + SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" + ;; + esac + fi + else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available]) + fi + fi +else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled]) + fi +fi + +if test x"$use_jni" != x"no"; then + AX_JNI_INCLUDE_DIR + have_jni_dependencies=yes + if test x"$enable_module_ecdh" = x"no"; then + have_jni_dependencies=no + fi + if test "x$JNI_INCLUDE_DIRS" = "x"; then + have_jni_dependencies=no + fi + if test "x$have_jni_dependencies" = "xno"; then + if test x"$use_jni" = x"yes"; then + AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.]) + fi + AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) + use_jni=no + else + use_jni=yes + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do + JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR" + done + fi +fi + +if test x"$set_bignum" = x"gmp"; then + SECP_LIBS="$SECP_LIBS $GMP_LIBS" + SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS" +fi + +if test x"$use_endomorphism" = x"yes"; then + AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) +fi + +if test x"$set_precomp" = x"yes"; then + AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) +fi + +if test x"$enable_module_ecdh" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +fi + +if test x"$enable_module_recovery" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) +fi + +AC_C_BIGENDIAN() + +if test x"$use_external_asm" = x"yes"; then + AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) +fi + +AC_MSG_NOTICE([Using static precomputation: $set_precomp]) +AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) +AC_MSG_NOTICE([Using field implementation: $set_field]) +AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) +AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) +AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) +AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage]) +AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) +AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) +AC_MSG_NOTICE([Using jni: $use_jni]) + +if test x"$enable_experimental" = x"yes"; then + AC_MSG_NOTICE([******]) + AC_MSG_NOTICE([WARNING: experimental build]) + AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) + AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([******]) +else + if test x"$enable_module_ecdh" = x"yes"; then + AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$set_asm" = x"arm"; then + AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) + fi +fi + +AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) +AC_CONFIG_FILES([Makefile libsecp256k1.pc]) +AC_SUBST(JNI_INCLUDES) +AC_SUBST(SECP_INCLUDES) +AC_SUBST(SECP_LIBS) +AC_SUBST(SECP_TEST_LIBS) +AC_SUBST(SECP_TEST_INCLUDES) +AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) +AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) +AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) +AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) + +dnl make sure nothing new is exported so that we don't break the cache +PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" +unset PKG_CONFIG_PATH +PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" + +AC_OUTPUT diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c new file mode 100644 index 000000000000..5b141a99481c --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_parsing.h" + +int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + size_t rpos, rlen, spos, slen; + size_t pos = 0; + size_t lenbyte; + unsigned char tmpsig[64] = {0}; + int overflow = 0; + + /* Hack to initialize sig with a correctly-parsed but invalid signature. */ + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + + /* Sequence tag byte */ + if (pos == inputlen || input[pos] != 0x30) { + return 0; + } + pos++; + + /* Sequence length bytes */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + pos += lenbyte; + } + + /* Integer tag byte for R */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for R */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + rlen = 0; + while (lenbyte > 0) { + rlen = (rlen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + rlen = lenbyte; + } + if (rlen > inputlen - pos) { + return 0; + } + rpos = pos; + pos += rlen; + + /* Integer tag byte for S */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for S */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + slen = 0; + while (lenbyte > 0) { + slen = (slen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + slen = lenbyte; + } + if (slen > inputlen - pos) { + return 0; + } + spos = pos; + pos += slen; + + /* Ignore leading zeroes in R */ + while (rlen > 0 && input[rpos] == 0) { + rlen--; + rpos++; + } + /* Copy R value */ + if (rlen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 32 - rlen, input + rpos, rlen); + } + + /* Ignore leading zeroes in S */ + while (slen > 0 && input[spos] == 0) { + slen--; + spos++; + } + /* Copy S value */ + if (slen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 64 - slen, input + spos, slen); + } + + if (!overflow) { + overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + if (overflow) { + memset(tmpsig, 0, 64); + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + return 1; +} + diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h new file mode 100644 index 000000000000..6d27871a7ccd --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file defines a function that parses DER with various errors and + * violations. This is not a part of the library itself, because the allowed + * violations are chosen arbitrarily and do not follow or establish any + * standard. + * + * In many places it matters that different implementations do not only accept + * the same set of valid signatures, but also reject the same set of signatures. + * The only means to accomplish that is by strictly obeying a standard, and not + * accepting anything else. + * + * Nonetheless, sometimes there is a need for compatibility with systems that + * use signatures which do not strictly obey DER. The snippet below shows how + * certain violations are easily supported. You may need to adapt it. + * + * Do not use this for new systems. Use well-defined DER or compact signatures + * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and + * secp256k1_ecdsa_signature_parse_compact). + * + * The supported violations are: + * - All numbers are parsed as nonnegative integers, even though X.609-0207 + * section 8.3.3 specifies that integers are always encoded as two's + * complement. + * - Integers can have length 0, even though section 8.3.1 says they can't. + * - Integers with overly long padding are accepted, violation section + * 8.3.2. + * - 127-byte long length descriptors are accepted, even though section + * 8.1.3.5.c says that they are not. + * - Trailing garbage data inside or after the signature is ignored. + * - The length descriptor of the sequence is ignored. + * + * Compared to for example OpenSSL, many violations are NOT supported: + * - Using overly long tag descriptors for the sequence or integers inside, + * violating section 8.1.2.2. + * - Encoding primitive integers as constructed values, violating section + * 8.3.1. + */ + +#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Parse a signature in "lax DER" format + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. In addition, it will accept signatures + * which violate the DER spec in various ways. Its purpose is to allow + * validation of the Bitcoin blockchain, which includes non-DER signatures + * from before the network rules were updated to enforce DER. Note that + * the set of supported violations is a strict subset of what OpenSSL will + * accept. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +int ecdsa_signature_parse_der_lax( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c new file mode 100644 index 000000000000..c2e63b4b8d7b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_privatekey_parsing.h" + +int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) { + const unsigned char *end = privkey + privkeylen; + int lenb = 0; + int len = 0; + memset(out32, 0, 32); + /* sequence header */ + if (end < privkey+1 || *privkey != 0x30) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end < privkey+1 || !(*privkey & 0x80)) { + return 0; + } + lenb = *privkey & ~0x80; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end < privkey+lenb) { + return 0; + } + /* sequence length */ + len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); + privkey += lenb; + if (end < privkey+len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) { + return 0; + } + memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); + if (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 33; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11, + 0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10, + 0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 65; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } + return 1; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h new file mode 100644 index 000000000000..2fd088f8abf1 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h @@ -0,0 +1,90 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file contains code snippets that parse DER private keys with + * various errors and violations. This is not a part of the library + * itself, because the allowed violations are chosen arbitrarily and + * do not follow or establish any standard. + * + * It also contains code to serialize private keys in a compatible + * manner. + * + * These functions are meant for compatibility with applications + * that require BER encoded keys. When working with secp256k1-specific + * code, the simple 32-byte private keys normally used by the + * library are sufficient. + */ + +#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Export a private key in DER format. + * + * Returns: 1 if the private key was valid. + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: privkey: pointer to an array for storing the private key in BER. + * Should have space for 279 bytes, and cannot be NULL. + * privkeylen: Pointer to an int where the length of the private key in + * privkey will be stored. + * In: seckey: pointer to a 32-byte secret key to export. + * compressed: 1 if the key should be exported in + * compressed format, 0 otherwise + * + * This function is purely meant for compatibility with applications that + * require BER encoded keys. When working with secp256k1-specific code, the + * simple 32-byte private keys are sufficient. + * + * Note that this function does not guarantee correct DER output. It is + * guaranteed to be parsable by secp256k1_ec_privkey_import_der + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der( + const secp256k1_context* ctx, + unsigned char *privkey, + size_t *privkeylen, + const unsigned char *seckey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. + * Returns: 1 if a private key was extracted. + * Args: ctx: pointer to a context object (cannot be NULL). + * Out: seckey: pointer to a 32-byte array for storing the private key. + * (cannot be NULL). + * In: privkey: pointer to a private key in DER format (cannot be NULL). + * privkeylen: length of the DER private key pointed to be privkey. + * + * This function will accept more than just strict DER, and even allow some BER + * violations. The public key stored inside the DER-encoded private key is not + * verified for correctness, nor are the curve parameters. Use this function + * only if you know in advance it is supposed to contain a secp256k1 private + * key. + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *privkey, + size_t privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h new file mode 100644 index 000000000000..f268e309d0bf --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h @@ -0,0 +1,577 @@ +#ifndef _SECP256K1_ +# define _SECP256K1_ + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/* These rules specify the order of arguments in API calls: + * + * 1. Context pointers go first, followed by output arguments, combined + * output/input arguments, and finally input-only arguments. + * 2. Array lengths always immediately the follow the argument whose length + * they describe, even if this violates rule 1. + * 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated + * later go first. This means: signatures, public nonces, private nonces, + * messages, public keys, secret keys, tweaks. + * 4. Arguments that are not data pointers go last, from more complex to less + * complex: function pointers, algorithm names, messages, void pointers, + * counts, flags, booleans. + * 5. Opaque data pointers follow the function pointer they are to be passed to. + */ + +/** Opaque data structure that holds context information (precomputed tables etc.). + * + * The purpose of context structures is to cache large precomputed data tables + * that are expensive to construct, and also to maintain the randomization data + * for blinding. + * + * Do not create a new context object for each operation, as construction is + * far slower than all other API calls (~100 times slower than an ECDSA + * verification). + * + * A constructed context can safely be used from multiple threads + * simultaneously, but API call that take a non-const pointer to a context + * need exclusive access to it. In particular this is the case for + * secp256k1_context_destroy and secp256k1_context_randomize. + * + * Regarding randomization, either do it once at creation time (in which case + * you do not need any locking for the other calls), or use a read-write lock. + */ +typedef struct secp256k1_context_struct secp256k1_context; + +/** Opaque data structure that holds a parsed and valid public key. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey; + +/** Opaque data structured that holds a parsed ECDSA signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_serialize_* functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_signature; + +/** A pointer to a function to deterministically generate a nonce. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * data: Arbitrary data pointer that is passed through. + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the algorithm, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + void *data, + unsigned int attempt +); + +# if !defined(SECP256K1_GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define SECP256K1_GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 +# endif +# endif + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +#ifndef SECP256K1_API +# if defined(_WIN32) +# ifdef SECP256K1_BUILD +# define SECP256K1_API __declspec(dllexport) +# else +# define SECP256K1_API +# endif +# elif defined(__GNUC__) && defined(SECP256K1_BUILD) +# define SECP256K1_API __attribute__ ((visibility ("default"))) +# else +# define SECP256K1_API +# endif +#endif + +/**Warning attributes + * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out + * some paranoid null checks. */ +# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) +# else +# define SECP256K1_WARN_UNUSED_RESULT +# endif +# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) +# else +# define SECP256K1_ARG_NONNULL(_x) +# endif + +/** All flags' lower 8 bits indicate what they're for. Do not use directly. */ +#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) +#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) +#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1) +/** The higher bits contain the actual data. Do not use directly. */ +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) + +/** Flags to pass to secp256k1_context_create. */ +#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) +#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) +#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) + +/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */ +#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) +#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) + +/** Create a secp256k1 context object. + * + * Returns: a newly created context object. + * In: flags: which parts of the context to initialize. + */ +SECP256K1_API secp256k1_context* secp256k1_context_create( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Copies a secp256k1 context object. + * + * Returns: a newly created context object. + * Args: ctx: an existing context to copy (cannot be NULL) + */ +SECP256K1_API secp256k1_context* secp256k1_context_clone( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object. + * + * The context pointer may not be used afterwards. + * Args: ctx: an existing context to destroy (cannot be NULL) + */ +SECP256K1_API void secp256k1_context_destroy( + secp256k1_context* ctx +); + +/** Set a callback function to be called when an illegal argument is passed to + * an API call. It will only trigger for violations that are mentioned + * explicitly in the header. + * + * The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * to cause a crash, though its return value and output arguments are + * undefined. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an illegal argument is + * passed to the API, taking a message and an opaque pointer + * (NULL restores a default handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_illegal_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Set a callback function to be called when an internal consistency check + * fails. The default is crashing. + * + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see secp256k1_context_set_illegal_callback + * for that). After this callback returns, anything may happen, including + * crashing. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an internal error occurs, + * taking a message and an opaque pointer (NULL restores a default + * handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_error_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Parse a variable-length public key into the pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * Args: ctx: a secp256k1 context object. + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * In: input: pointer to a serialized public key + * inputlen: length of the array pointed to by input + * + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * byte 0x06 or 0x07) format public keys. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( + const secp256k1_context* ctx, + secp256k1_pubkey* pubkey, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a pubkey object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * compressed==1) byte array to place the serialized key + * in. + * In/Out: outputlen: a pointer to an integer which is initially set to the + * size of output, and is overwritten with the written + * size. + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key. + * flags: SECP256K1_EC_COMPRESSED if serialization should be in + * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. + */ +SECP256K1_API int secp256k1_ec_pubkey_serialize( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_pubkey* pubkey, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Parse an ECDSA signature in compact (64 bytes) format. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to the 64-byte array to parse + * + * The signature must consist of a 32-byte big endian R value, followed by a + * 32-byte big endian S value. If R or S fall outside of [0..order-1], the + * encoding is invalid. R and S with value 0 are allowed in the encoding. + * + * After the call, sig will always be initialized. If parsing failed or R or + * S are zero, the resulting sig value is guaranteed to fail validation for any + * message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a DER ECDSA signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_der( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in DER format. + * + * Returns: 1 if enough space was available to serialize, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: output: a pointer to an array to store the DER serialization + * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * should be set to the length of output. After the call + * it will be set to the length of the serialization (even + * if 0 was returned). + * In: sig: a pointer to an initialized signature object + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Serialize an ECDSA signature in compact (64 byte) format. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array to store the compact serialization + * In: sig: a pointer to an initialized signature object + * + * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify an ECDSA signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + * + * To avoid accepting malleable signatures, only ECDSA signatures in lower-S + * form are accepted. + * + * If you need to accept ECDSA signatures from sources that do not obey this + * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to + * validation, but be aware that doing so results in malleable signatures. + * + * For details, see the comments for that function. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Convert a signature to a normalized lower-S form. + * + * Returns: 1 if sigin was not normalized, 0 if it already was. + * Args: ctx: a secp256k1 context object + * Out: sigout: a pointer to a signature to fill with the normalized form, + * or copy if the input was already normalized. (can be NULL if + * you're only interested in whether the input was already + * normalized). + * In: sigin: a pointer to a signature to check/normalize (cannot be NULL, + * can be identical to sigout) + * + * With ECDSA a third-party can forge a second distinct signature of the same + * message, given a single initial signature, but without knowing the key. This + * is done by negating the S value modulo the order of the curve, 'flipping' + * the sign of the random point R which is not included in the signature. + * + * Forgery of the same message isn't universally problematic, but in systems + * where message malleability or uniqueness of signatures is important this can + * cause issues. This forgery can be blocked by all verifiers forcing signers + * to use a normalized form. + * + * The lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to verify, + * making it a good choice. Security of always using lower-S is assured because + * anyone can trivially modify a signature after the fact to enforce this + * property anyway. + * + * The lower S value is always between 0x1 and + * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive. + * + * No other forms of ECDSA malleability are known and none seem likely, but + * there is no formal proof that ECDSA, even with this additional restriction, + * is free of other malleability. Commonly used serialization schemes will also + * accept various non-unique encodings, so care should be taken when this + * property is required for an application. + * + * The secp256k1_ecdsa_sign function will by default create signatures in the + * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case + * signatures come from a system that cannot enforce this property, + * secp256k1_ecdsa_signature_normalize must be called before verification. + */ +SECP256K1_API int secp256k1_ecdsa_signature_normalize( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sigout, + const secp256k1_ecdsa_signature *sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); + +/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; + +/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; + +/** Create an ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * + * The created signature is always in lower-S form. See + * secp256k1_ecdsa_signature_normalize for more details. + */ +SECP256K1_API int secp256k1_ecdsa_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify an ECDSA secret key. + * + * Returns: 1: secret key is valid + * 0: secret key is invalid + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seckey: pointer to a 32-byte secret key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + const secp256k1_context* ctx, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Compute the public key for a secret key. + * + * Returns: 1: secret was valid, public key stores + * 0: secret was invalid, try again + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: pubkey: pointer to the created public key (cannot be NULL) + * In: seckey: pointer to a 32-byte private key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by adding tweak to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting private key + * would be invalid (only when the tweak is the complement of the + * private key). 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by adding tweak times the generator to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting public key + * would be invalid (only when the tweak is the complement of the + * corresponding private key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key object. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by multiplying it by a tweak. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by multiplying it by a tweak value. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key obkect. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates the context randomization. + * Returns: 1: randomization successfully updated + * 0: error + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + secp256k1_context* ctx, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1); + +/** Add a number of public keys together. + * Returns: 1: the sum of the public keys is valid. + * 0: the sum of the public keys is not valid. + * Args: ctx: pointer to a context object + * Out: out: pointer to a public key object for placing the resulting public key + * (cannot be NULL) + * In: ins: pointer to array of pointers to public keys (cannot be NULL) + * n: the number of public keys to add together (must be at least 1) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_pubkey *out, + const secp256k1_pubkey * const * ins, + size_t n +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h new file mode 100644 index 000000000000..4b84d7a96349 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h @@ -0,0 +1,31 @@ +#ifndef _SECP256K1_ECDH_ +# define _SECP256K1_ECDH_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Compute an EC Diffie-Hellman secret in constant time + * Returns: 1: exponentiation was successful + * 0: scalar was invalid (zero or overflow) + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: result: a 32-byte array which will be populated by an ECDH + * secret computed from the point and scalar + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * privkey: a 32-byte scalar with which to multiply the point + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context* ctx, + unsigned char *result, + const secp256k1_pubkey *pubkey, + const unsigned char *privkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h new file mode 100644 index 000000000000..05537972532f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h @@ -0,0 +1,110 @@ +#ifndef _SECP256K1_RECOVERY_ +# define _SECP256K1_RECOVERY_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Opaque data structured that holds a parsed ECDSA signature, + * supporting pubkey recovery. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 65 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + * + * Furthermore, it is guaranteed that identical signatures (including their + * recoverability) will have identical representation, so they can be + * memcmp'ed. + */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_recoverable_signature; + +/** Parse a compact ECDSA signature (64 bytes + recovery id). + * + * Returns: 1 when the signature could be parsed, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to a 64-byte compact signature + * recid: the recovery id (0, 1, 2 or 3) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature* sig, + const unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Convert a recoverable signature into a normal signature. + * + * Returns: 1 + * Out: sig: a pointer to a normal signature (cannot be NULL). + * In: sigin: a pointer to a recoverable signature (cannot be NULL). + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const secp256k1_ecdsa_recoverable_signature* sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in compact format (64 bytes + recovery id). + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array of the compact signature (cannot be NULL) + * recid: a pointer to an integer to hold the recovery id (can be NULL). + * In: sig: a pointer to an initialized signature object (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + int *recid, + const secp256k1_ecdsa_recoverable_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Create a recoverable ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_sign_recoverable( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an ECDSA public key from a signature. + * + * Returns: 1: public key successfully recovered (which guarantees a correct signature). + * 0: otherwise. + * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL) + * Out: pubkey: pointer to the recovered public key (cannot be NULL) + * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in new file mode 100644 index 000000000000..a0d006f1131f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsecp256k1 +Description: Optimized C library for EC operations on curve secp256k1 +URL: https://github.com/bitcoin-core/secp256k1 +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs.private: @SECP_LIBS@ +Libs: -L${libdir} -lsecp256k1 + diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/obj/.gitignore b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/obj/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage new file mode 100644 index 000000000000..ab580c5b23bb --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# exectured. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# as we assume that all constraints in it are complementary with each other. +# +# Based on the sage verification scripts used in the Explicit-Formulas Database +# by Tanja Lange and others, see http://hyperelliptic.org/EFD + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +def conflicts(R, con): + """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" + zero = R.ideal(map(numerator, con.zero)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(f) + return nonzero + + +def prove_nonzero(R, exprs, assume): + """Check whether an expression is provably nonzero, given assumptions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + return (False, [exprs[expr]]) + allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) + for (f, n) in allexprs.factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in numerator(expr).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in zero.reduce(numerator(expr)).factor(): + if f not in nonzero: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +def prove_zero(R, exprs, assume): + """Check whether all of the passed expressions are provably zero, given assumptions""" + r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) + if not r: + return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) + zero = R.ideal(map(numerator, assume.zero)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +def describe_extra(R, assume, assumeExtra): + """Describe what assumptions are added, given existing assumptions""" + zerox = assume.zero.copy() + zerox.update(assumeExtra.zero) + zero = R.ideal(map(numerator, assume.zero)) + zeroextra = R.ideal(map(numerator, zerox)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + nzr = zeroextra.reduce(numerator(nz)) + if nzr not in zeroextra: + for (f,n) in nzr.factor(): + if zeroextra.reduce(f) not in nonzero: + ret.add("%s != 0" % zeroextra.reduce(f)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage new file mode 100644 index 000000000000..a97e732f7fa3 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +load("weierstrass_prover.sage") + +def formula_secp256k1_gej_double_var(a): + """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" + rz = a.Z * a.Y + rz = rz * 2 + t1 = a.X^2 + t1 = t1 * 3 + t2 = t1^2 + t3 = a.Y^2 + t3 = t3 * 2 + t4 = t3^2 + t4 = t4 * 2 + t3 = t3 * a.X + rx = t3 + rx = rx * 4 + rx = -rx + rx = rx + t2 + t2 = -t2 + t3 = t3 * 6 + t3 = t3 + t2 + ry = t1 * t3 + t2 = -t4 + ry = ry + t2 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r) + if branch == 3: + return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if (branch == 2): + r = formula_secp256k1_gej_double_var(a) + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if (branch == 3): + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_zinv_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_zinv_var""" + bzinv = b.Z^(-1) + if branch == 0: + return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a) + if branch == 1: + bzinv2 = bzinv^2 + bzinv3 = bzinv2 * bzinv + rx = b.X * bzinv2 + ry = b.Y * bzinv3 + rz = 1 + return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if branch == 3: + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + zeroes.update({m : 'm_zero'}) + else: + nonzeroes.update({m : 'm_nonzero'}) + if (branch & 2) != 0: + zeroes.update({rr : 'rr_zero'}) + else: + nonzeroes.update({rr : 'rr_nonzero'}) + rr_alt = s1 + rr_alt = rr_alt * 2 + m_alt = m_alt + u1 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + n = n^2 + if degenerate: + n = m + t = rr_alt^2 + rz = a.Z * m_alt + infinity = False + if (branch & 8) != 0: + if not a_infinity: + infinity = True + zeroes.update({rz : 'r.z=0'}) + else: + nonzeroes.update({rz : 'r.z!=0'}) + rz = rz * 2 + q = -q + t = t + q + rx = t + t = t * 2 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_old(branch, a, b): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) + +if __name__ == "__main__": + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + + if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage new file mode 100644 index 000000000000..03ef2ec901ea --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field""" + F = Integers(p) + print "Formula %s on Z%i:" % (name, p) + points = [] + for x in xrange(0, p): + for y in xrange(0, p): + point = affinepoint(F(x), F(y)) + r, e = concrete_verify(on_weierstrass_curve(A, B, point)) + if r: + points.append(point) + + for za in xrange(1, p): + for zb in xrange(1, p): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(2): + pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia) + pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib) + for branch in xrange(0, branches): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + r, e = concrete_verify(assumeLaw) + if r: + if match: + print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity) + else: + match = True + r, e = concrete_verify(require) + if not r: + print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e) + print + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + pC.X = lift(pC.X) + pC.Y = lift(pC.Y) + pC.Z = lift(pC.Z) + pC.Infinity = lift(pC.Infinity) + + for key in laws_jacobian_weierstrass: + res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + + for key in res: + print " %s:" % key + val = res[key] + for x in val: + if x[0] is not None: + print " branch %i: %s" % (x[1], x[0]) + + print diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 000000000000..5df561f2fc93 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm: +/********************************************************************** + * Copyright (c) 2014 Wladimir J. van der Laan * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the the even passes + +*/ + + .syntax unified + .arch armv7-a + @ eabi attributes - see readelf -A + .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes + .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no + .eabi_attribute 10, 0 @ Tag_FP_arch = none + .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte + .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 + .text + + @ Field constants + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h new file mode 100644 index 000000000000..c4c16eb7ca7d --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h @@ -0,0 +1,32 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BASIC_CONFIG_ +#define _SECP256K1_BASIC_CONFIG_ + +#ifdef USE_BASIC_CONFIG + +#undef USE_ASM_X86_64 +#undef USE_ENDOMORPHISM +#undef USE_FIELD_10X26 +#undef USE_FIELD_5X52 +#undef USE_FIELD_INV_BUILTIN +#undef USE_FIELD_INV_NUM +#undef USE_NUM_GMP +#undef USE_NUM_NONE +#undef USE_SCALAR_4X64 +#undef USE_SCALAR_8X32 +#undef USE_SCALAR_INV_BUILTIN +#undef USE_SCALAR_INV_NUM + +#define USE_NUM_NONE 1 +#define USE_FIELD_INV_BUILTIN 1 +#define USE_SCALAR_INV_BUILTIN 1 +#define USE_FIELD_10X26 1 +#define USE_SCALAR_8X32 1 + +#endif // USE_BASIC_CONFIG +#endif // _SECP256K1_BASIC_CONFIG_ diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h new file mode 100644 index 000000000000..3a71b4aafa04 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h @@ -0,0 +1,66 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BENCH_H_ +#define _SECP256K1_BENCH_H_ + +#include +#include +#include "sys/time.h" + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +void print_number(double x) { + double y = x; + int c = 0; + if (y < 0.0) { + y = -y; + } + while (y < 100.0) { + y *= 10.0; + c++; + } + printf("%.*f", c, x); +} + +void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { + int i; + double min = HUGE_VAL; + double sum = 0.0; + double max = 0.0; + for (i = 0; i < count; i++) { + double begin, total; + if (setup != NULL) { + setup(data); + } + begin = gettimedouble(); + benchmark(data); + total = gettimedouble() - begin; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } + sum += total; + } + printf("%s: min ", name); + print_number(min * 1000000.0 / iter); + printf("us / avg "); + print_number((sum / count) * 1000000.0 / iter); + printf("us / max "); + print_number(max * 1000000.0 / iter); + printf("us\n"); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c new file mode 100644 index 000000000000..cde5e2dbb4e4 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char scalar[32]; +} bench_ecdh_t; + +static void bench_ecdh_setup(void* arg) { + int i; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + + /* create a context with no capabilities */ + data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + for (i = 0; i < 32; i++) { + data->scalar[i] = i + 1; + } + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ecdh(void* arg) { + int i; + unsigned char res[32]; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + } +} + +int main(void) { + bench_ecdh_t data; + + run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c new file mode 100644 index 000000000000..0809f77bda10 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c @@ -0,0 +1,382 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "util.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "field_impl.h" +#include "group_impl.h" +#include "scalar_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_impl.h" +#include "bench.h" +#include "secp256k1.c" + +typedef struct { + secp256k1_scalar scalar_x, scalar_y; + secp256k1_fe fe_x, fe_y; + secp256k1_ge ge_x, ge_y; + secp256k1_gej gej_x, gej_y; + unsigned char data[64]; + int wnaf[256]; +} bench_inv_t; + +void bench_setup(void* arg) { + bench_inv_t *data = (bench_inv_t*)arg; + + static const unsigned char init_x[32] = { + 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, + 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, + 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, + 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83 + }; + + static const unsigned char init_y[32] = { + 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83, + 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5, + 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9, + 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3 + }; + + secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL); + secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL); + secp256k1_fe_set_b32(&data->fe_x, init_x); + secp256k1_fe_set_b32(&data->fe_y, init_y); + CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0)); + CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1)); + secp256k1_gej_set_ge(&data->gej_x, &data->ge_x); + secp256k1_gej_set_ge(&data->gej_y, &data->ge_y); + memcpy(data->data, init_x, 32); + memcpy(data->data + 32, init_y, 32); +} + +void bench_scalar_add(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_negate(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +#ifdef USE_ENDOMORPHISM +void bench_scalar_split(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_scalar l, r; + secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} +#endif + +void bench_scalar_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_field_normalize(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize(&data->fe_x); + } +} + +void bench_field_normalize_weak(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize_weak(&data->fe_x); + } +} + +void bench_field_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); + } +} + +void bench_field_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_sqr(&data->fe_x, &data->fe_x); + } +} + +void bench_field_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_sqrt(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_group_double_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); + } +} + +void bench_group_add_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); + } +} + +void bench_group_add_affine(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y); + } +} + +void bench_group_add_affine_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); + } +} + +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); + } +} + +void bench_ecmult_wnaf(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_wnaf_const(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + + +void bench_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_sha256_t sha; + + for (i = 0; i < 20000; i++) { + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data->data, 32); + secp256k1_sha256_finalize(&sha, data->data); + } +} + +void bench_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_hmac_sha256_t hmac; + + for (i = 0; i < 20000; i++) { + secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); + secp256k1_hmac_sha256_write(&hmac, data->data, 32); + secp256k1_hmac_sha256_finalize(&hmac, data->data); + } +} + +void bench_rfc6979_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_rfc6979_hmac_sha256_t rng; + + for (i = 0; i < 20000; i++) { + secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); + secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32); + } +} + +void bench_context_verify(void* arg) { + int i; + (void)arg; + for (i = 0; i < 20; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + } +} + +void bench_context_sign(void* arg) { + int i; + (void)arg; + for (i = 0; i < 200; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN)); + } +} + +#ifndef USE_NUM_NONE +void bench_num_jacobi(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_num nx, norder; + + secp256k1_scalar_get_num(&nx, &data->scalar_x); + secp256k1_scalar_order_get_num(&norder); + secp256k1_scalar_get_num(&norder, &data->scalar_y); + + for (i = 0; i < 200000; i++) { + secp256k1_num_jacobi(&nx, &norder); + } +} +#endif + +int have_flag(int argc, char** argv, char *flag) { + char** argm = argv + argc; + argv++; + if (argv == argm) { + return 1; + } + while (argv != NULL && argv != argm) { + if (strcmp(*argv, flag) == 0) { + return 1; + } + argv++; + } + return 0; +} + +int main(int argc, char **argv) { + bench_inv_t data; + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000); +#ifdef USE_ENDOMORPHISM + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000); +#endif + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000); + + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20); + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200); + +#ifndef USE_NUM_NONE + if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000); +#endif + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c new file mode 100644 index 000000000000..6489378cc64a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "include/secp256k1_recovery.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char sig[64]; +} bench_recover_t; + +void bench_recover(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + secp256k1_pubkey pubkey; + unsigned char pubkeyc[33]; + + for (i = 0; i < 20000; i++) { + int j; + size_t pubkeylen = 33; + secp256k1_ecdsa_recoverable_signature sig; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(data->ctx, &sig, data->sig, i % 2)); + CHECK(secp256k1_ecdsa_recover(data->ctx, &pubkey, &sig, data->msg)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pubkeyc, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + for (j = 0; j < 32; j++) { + data->sig[j + 32] = data->msg[j]; /* Move former message to S. */ + data->msg[j] = data->sig[j]; /* Move former R to message. */ + data->sig[j] = pubkeyc[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ + } + } +} + +void bench_recover_setup(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (i = 0; i < 64; i++) { + data->sig[i] = 65 + i; + } +} + +int main(void) { + bench_recover_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + + run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c new file mode 100644 index 000000000000..5f137dda23ef --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorr.h" +#include "util.h" +#include "bench.h" + +typedef struct { + unsigned char key[32]; + unsigned char sig[64]; + unsigned char pubkey[33]; + size_t pubkeylen; +} benchmark_schnorr_sig_t; + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + benchmark_schnorr_sig_t sigs[64]; + int numsigs; +} benchmark_schnorr_verify_t; + +static void benchmark_schnorr_init(void* arg) { + int i, k; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (k = 0; k < data->numsigs; k++) { + secp256k1_pubkey pubkey; + for (i = 0; i < 32; i++) { + data->sigs[k].key[i] = 33 + i + k; + } + secp256k1_schnorr_sign(data->ctx, data->sigs[k].sig, data->msg, data->sigs[k].key, NULL, NULL); + data->sigs[k].pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->sigs[k].key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + } +} + +static void benchmark_schnorr_verify(void* arg) { + int i; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 20000 / data->numsigs; i++) { + secp256k1_pubkey pubkey; + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen)); + CHECK(secp256k1_schnorr_verify(data->ctx, data->sigs[0].sig, data->msg, &pubkey) == ((i & 0xFF) == 0)); + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + } +} + + + +int main(void) { + benchmark_schnorr_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.numsigs = 1; + run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c new file mode 100644 index 000000000000..ed7224d757e4 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c @@ -0,0 +1,56 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char msg[32]; + unsigned char key[32]; +} bench_sign_t; + +static void bench_sign_setup(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = i + 1; + } + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_sign(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + unsigned char sig[74]; + for (i = 0; i < 20000; i++) { + size_t siglen = 74; + int j; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_sign(data->ctx, &signature, data->msg, data->key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data->ctx, sig, &siglen, &signature)); + for (j = 0; j < 32; j++) { + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; + } + } +} + +int main(void) { + bench_sign_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + + run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c new file mode 100644 index 000000000000..418defa0aa22 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include +#include +#include +#endif + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char sig[72]; + size_t siglen; + unsigned char pubkey[33]; + size_t pubkeylen; +#ifdef ENABLE_OPENSSL_TESTS + EC_GROUP* ec_group; +#endif +} benchmark_verify_t; + +static void benchmark_verify(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(data->ctx, &sig, data->sig, data->siglen) == 1); + CHECK(secp256k1_ecdsa_verify(data->ctx, &sig, data->msg, &pubkey) == (i == 0)); + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} + +#ifdef ENABLE_OPENSSL_TESTS +static void benchmark_verify_openssl(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + { + EC_KEY *pkey = EC_KEY_new(); + const unsigned char *pubkey = &data->pubkey[0]; + int result; + + CHECK(pkey != NULL); + result = EC_KEY_set_group(pkey, data->ec_group); + CHECK(result); + result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL; + CHECK(result); + result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0); + CHECK(result); + EC_KEY_free(pkey); + } + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} +#endif + +int main(void) { + int i; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + benchmark_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + for (i = 0; i < 32; i++) { + data.msg[i] = 1 + i; + } + for (i = 0; i < 32; i++) { + data.key[i] = 33 + i; + } + data.siglen = 72; + CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig)); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key)); + data.pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + + run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000); +#ifdef ENABLE_OPENSSL_TESTS + data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); + run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); + EC_GROUP_free(data.ec_group); +#endif + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h new file mode 100644 index 000000000000..54ae101b9241 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h @@ -0,0 +1,21 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECDSA_ +#define _SECP256K1_ECDSA_ + +#include + +#include "scalar.h" +#include "group.h" +#include "ecmult.h" + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size); +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s); +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h new file mode 100644 index 000000000000..453bb1188066 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_ECDSA_IMPL_H_ +#define _SECP256K1_ECDSA_IMPL_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "ecdsa.h" + +/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 + * sage: for t in xrange(1023, -1, -1): + * .. p = 2**256 - 2**32 - t + * .. if p.is_prime(): + * .. print '%x'%p + * .. break + * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) + * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + */ +static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL +); + +/** Difference between field and order, values 'p' and 'n' values defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) + * '14551231950b75fc4402da1722fc9baee' + */ +static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( + 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL +); + +static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { + int lenleft, b1; + size_t ret = 0; + if (*sigp >= sigend) { + return -1; + } + b1 = *((*sigp)++); + if (b1 == 0xFF) { + /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ + return -1; + } + if ((b1 & 0x80) == 0) { + /* X.690-0207 8.1.3.4 short form length octets */ + return b1; + } + if (b1 == 0x80) { + /* Indefinite length is not allowed in DER. */ + return -1; + } + /* X.690-207 8.1.3.5 long form length octets */ + lenleft = b1 & 0x7F; + if (lenleft > sigend - *sigp) { + return -1; + } + if (**sigp == 0) { + /* Not the shortest possible length encoding. */ + return -1; + } + if ((size_t)lenleft > sizeof(size_t)) { + /* The resulting length would exceed the range of a size_t, so + * certainly longer than the passed array size. + */ + return -1; + } + while (lenleft > 0) { + if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { + } + ret = (ret << 8) | **sigp; + if (ret + lenleft > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return -1; + } + (*sigp)++; + lenleft--; + } + if (ret < 128) { + /* Not the shortest possible length encoding. */ + return -1; + } + return ret; +} + +static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { + int overflow = 0; + unsigned char ra[32] = {0}; + int rlen; + + if (*sig == sigend || **sig != 0x02) { + /* Not a primitive integer (X.690-0207 8.3.1). */ + return 0; + } + (*sig)++; + rlen = secp256k1_der_read_len(sig, sigend); + if (rlen <= 0 || (*sig) + rlen > sigend) { + /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ + return 0; + } + if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { + /* Excessive 0x00 padding. */ + return 0; + } + if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { + /* Excessive 0xFF padding. */ + return 0; + } + if ((**sig & 0x80) == 0x80) { + /* Negative. */ + overflow = 1; + } + while (rlen > 0 && **sig == 0) { + /* Skip leading zero bytes */ + rlen--; + (*sig)++; + } + if (rlen > 32) { + overflow = 1; + } + if (!overflow) { + memcpy(ra + 32 - rlen, *sig, rlen); + secp256k1_scalar_set_b32(r, ra, &overflow); + } + if (overflow) { + secp256k1_scalar_set_int(r, 0); + } + (*sig) += rlen; + return 1; +} + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { + const unsigned char *sigend = sig + size; + int rlen; + if (sig == sigend || *(sig++) != 0x30) { + /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ + return 0; + } + rlen = secp256k1_der_read_len(&sig, sigend); + if (rlen < 0 || sig + rlen > sigend) { + /* Tuple exceeds bounds */ + return 0; + } + if (sig + rlen != sigend) { + /* Garbage after tuple. */ + return 0; + } + + if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { + return 0; + } + if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { + return 0; + } + + if (sig != sigend) { + /* Trailing garbage inside tuple. */ + return 0; + } + + return 1; +} + +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { + unsigned char r[33] = {0}, s[33] = {0}; + unsigned char *rp = r, *sp = s; + size_t lenR = 33, lenS = 33; + secp256k1_scalar_get_b32(&r[1], ar); + secp256k1_scalar_get_b32(&s[1], as); + while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } + while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } + if (*size < 6+lenS+lenR) { + *size = 6 + lenS + lenR; + return 0; + } + *size = 6 + lenS + lenR; + sig[0] = 0x30; + sig[1] = 4 + lenS + lenR; + sig[2] = 0x02; + sig[3] = lenR; + memcpy(sig+4, rp, lenR); + sig[4+lenR] = 0x02; + sig[5+lenR] = lenS; + memcpy(sig+lenR+6, sp, lenS); + return 1; +} + +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { + unsigned char c[32]; + secp256k1_scalar sn, u1, u2; +#if !defined(EXHAUSTIVE_TEST_ORDER) + secp256k1_fe xr; +#endif + secp256k1_gej pubkeyj; + secp256k1_gej pr; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_inverse_var(&sn, sigs); + secp256k1_scalar_mul(&u1, &sn, message); + secp256k1_scalar_mul(&u2, &sn, sigr); + secp256k1_gej_set_ge(&pubkeyj, pubkey); + secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&pr)) { + return 0; + } + +#if defined(EXHAUSTIVE_TEST_ORDER) +{ + secp256k1_scalar computed_r; + secp256k1_ge pr_ge; + secp256k1_ge_set_gej(&pr_ge, &pr); + secp256k1_fe_normalize(&pr_ge.x); + + secp256k1_fe_get_b32(c, &pr_ge.x); + secp256k1_scalar_set_b32(&computed_r, c, NULL); + return secp256k1_scalar_eq(sigr, &computed_r); +} +#else + secp256k1_scalar_get_b32(c, sigr); + secp256k1_fe_set_b32(&xr, c); + + /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) + * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), + * compute the remainder modulo n, and compare it to xr. However: + * + * xr == X(pr) mod n + * <=> exists h. (xr + h * n < p && xr + h * n == X(pr)) + * [Since 2 * n > p, h can only be 0 or 1] + * <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr)) + * [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p] + * <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p) + * [Multiplying both sides of the equations by pr.z^2 mod p] + * <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x) + * + * Thus, we can avoid the inversion, but we have to check both cases separately. + * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. + */ + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + /* xr + n >= p, so we can skip testing the second case. */ + return 0; + } + secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + return 0; +#endif +} + +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { + unsigned char b[32]; + secp256k1_gej rp; + secp256k1_ge r; + secp256k1_scalar n; + int overflow = 0; + + secp256k1_ecmult_gen(ctx, &rp, nonce); + secp256k1_ge_set_gej(&r, &rp); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_normalize(&r.y); + secp256k1_fe_get_b32(b, &r.x); + secp256k1_scalar_set_b32(sigr, b, &overflow); + /* These two conditions should be checked before calling */ + VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr)); + VERIFY_CHECK(overflow == 0); + + if (recid) { + /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log + * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria. + */ + *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0); + } + secp256k1_scalar_mul(&n, sigr, seckey); + secp256k1_scalar_add(&n, &n, message); + secp256k1_scalar_inverse(sigs, nonce); + secp256k1_scalar_mul(sigs, sigs, &n); + secp256k1_scalar_clear(&n); + secp256k1_gej_clear(&rp); + secp256k1_ge_clear(&r); + if (secp256k1_scalar_is_zero(sigs)) { + return 0; + } + if (secp256k1_scalar_is_high(sigs)) { + secp256k1_scalar_negate(sigs, sigs); + if (recid) { + *recid ^= 1; + } + } + return 1; +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h new file mode 100644 index 000000000000..42739a3bea7c --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h @@ -0,0 +1,25 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_ +#define _SECP256K1_ECKEY_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed); + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h new file mode 100644 index 000000000000..ce38071ac2e5 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h @@ -0,0 +1,99 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_IMPL_H_ +#define _SECP256K1_ECKEY_IMPL_H_ + +#include "eckey.h" + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { + if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + secp256k1_fe x; + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { + secp256k1_fe x, y; + if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + return 0; + } + secp256k1_ge_set_xy(elem, &x, &y); + if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + return 0; + } + return secp256k1_ge_is_valid_var(elem); + } else { + return 0; + } +} + +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { + if (secp256k1_ge_is_infinity(elem)) { + return 0; + } + secp256k1_fe_normalize_var(&elem->x); + secp256k1_fe_normalize_var(&elem->y); + secp256k1_fe_get_b32(&pub[1], &elem->x); + if (compressed) { + *size = 33; + pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + } else { + *size = 65; + pub[0] = 0x04; + secp256k1_fe_get_b32(&pub[33], &elem->y); + } + return 1; +} + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + secp256k1_scalar_add(key, key, tweak); + if (secp256k1_scalar_is_zero(key)) { + return 0; + } + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_gej pt; + secp256k1_scalar one; + secp256k1_gej_set_ge(&pt, key); + secp256k1_scalar_set_int(&one, 1); + secp256k1_ecmult(ctx, &pt, &pt, &one, tweak); + + if (secp256k1_gej_is_infinity(&pt)) { + return 0; + } + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_mul(key, key, tweak); + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_scalar zero; + secp256k1_gej pt; + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_set_int(&zero, 0); + secp256k1_gej_set_ge(&pt, key); + secp256k1_ecmult(ctx, &pt, &pt, tweak, &zero); + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h new file mode 100644 index 000000000000..20484134f524 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h @@ -0,0 +1,31 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_ +#define _SECP256K1_ECMULT_ + +#include "num.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*P + b*G: */ + secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ +#endif +} secp256k1_ecmult_context; + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); + +/** Double multiply: R = na*A + ng*G */ +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h new file mode 100644 index 000000000000..2b0097655c19 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_ +#define _SECP256K1_ECMULT_CONST_ + +#include "scalar.h" +#include "group.h" + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h new file mode 100644 index 000000000000..0db314c48e0c --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h @@ -0,0 +1,239 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_IMPL_ +#define _SECP256K1_ECMULT_CONST_IMPL_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#endif +#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w)) + +/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ +#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ + int m; \ + int abs_n = (n) * (((n) > 0) * 2 - 1); \ + int idx_n = abs_n / 2; \ + secp256k1_fe neg_y; \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ + for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \ + /* This loop is used to avoid secret data in array indices. See + * the comment in ecmult_gen_impl.h for rationale. */ \ + secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ + secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ + } \ + (r)->infinity = 0; \ + secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) + * with the following guarantees: + * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) + * - each wnaf[i] is nonzero + * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * + * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar + * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) + * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003 + * + * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 + */ +static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { + int global_sign; + int skew = 0; + int word = 0; + + /* 1 2 3 */ + int u_last; + int u; + + int flip; + int bit; + secp256k1_scalar neg_s; + int not_neg_one; + /* Note that we cannot handle even numbers by negating them to be odd, as is + * done in other implementations, since if our scalars were specified to have + * width < 256 for performance reasons, their negations would have width 256 + * and we'd lose any performance benefit. Instead, we use a technique from + * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) + * or 2 (for odd) to the number we are encoding, returning a skew value indicating + * this, and having the caller compensate after doing the multiplication. */ + + /* Negative numbers will be negated to keep their bit representation below the maximum width */ + flip = secp256k1_scalar_is_high(&s); + /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ + bit = flip ^ !secp256k1_scalar_is_even(&s); + /* We check for negative one, since adding 2 to it will cause an overflow */ + secp256k1_scalar_negate(&neg_s, &s); + not_neg_one = !secp256k1_scalar_is_one(&neg_s); + secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); + /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects + * that we added two to it and flipped it. In fact for -1 these operations are + * identical. We only flipped, but since skewing is required (in the sense that + * the skew must be 1 or 2, never zero) and flipping is not, we need to change + * our flags to claim that we only skewed. */ + global_sign = secp256k1_scalar_cond_negate(&s, flip); + global_sign *= not_neg_one * 2 - 1; + skew = 1 << bit; + + /* 4 */ + u_last = secp256k1_scalar_shr_int(&s, w); + while (word * w < WNAF_BITS) { + int sign; + int even; + + /* 4.1 4.4 */ + u = secp256k1_scalar_shr_int(&s, w); + /* 4.2 */ + even = ((u & 1) == 0); + sign = 2 * (u_last > 0) - 1; + u += sign * even; + u_last -= sign * even * (1 << w); + + /* 4.3, adapted for global sign change */ + wnaf[word++] = u_last * global_sign; + + u_last = u; + } + wnaf[word] = u * global_sign; + + VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); + VERIFY_CHECK(word == WNAF_SIZE(w)); + return skew; +} + + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + int skew_1; + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; + int skew_lam; + secp256k1_scalar q_1, q_lam; +#endif + + int i; + secp256k1_scalar sc = *scalar; + + /* build wnaf representation for q. */ +#ifdef USE_ENDOMORPHISM + /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); + skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1); + skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1); +#else + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1); +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + */ + secp256k1_gej_set_ge(r, a); + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_fe_normalize_weak(&pre_a[i].y); + } +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } +#endif + + /* first loop iteration (separated out so we can directly set r, rather + * than having it start at infinity, get doubled several times, then have + * its new value added to it) */ + i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); + secp256k1_gej_set_ge(r, &tmpa); +#ifdef USE_ENDOMORPHISM + i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + /* remaining loop iterations */ + for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) { + int n; + int j; + for (j = 0; j < WINDOW_A - 1; ++j) { + secp256k1_gej_double_nonzero(r, r, NULL); + } + + n = wnaf_1[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#ifdef USE_ENDOMORPHISM + n = wnaf_lam[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + } + + secp256k1_fe_mul(&r->z, &r->z, &Z); + + { + /* Correct for wNAF skew */ + secp256k1_ge correction = *a; + secp256k1_ge_storage correction_1_stor; +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage correction_lam_stor; +#endif + secp256k1_ge_storage a2_stor; + secp256k1_gej tmpj; + secp256k1_gej_set_ge(&tmpj, &correction); + secp256k1_gej_double_var(&tmpj, &tmpj, NULL); + secp256k1_ge_set_gej(&correction, &tmpj); + secp256k1_ge_to_storage(&correction_1_stor, a); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_to_storage(&correction_lam_stor, a); +#endif + secp256k1_ge_to_storage(&a2_stor, &correction); + + /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ + secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); +#endif + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#ifdef USE_ENDOMORPHISM + secp256k1_ge_from_storage(&correction, &correction_lam_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_ge_mul_lambda(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); +#endif + } +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h new file mode 100644 index 000000000000..eb2cc9ead6ed --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h @@ -0,0 +1,43 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_ +#define _SECP256K1_ECMULT_GEN_ + +#include "scalar.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*G: + * To harden against timing attacks, use the following mechanism: + * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. + * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where: + * * U_i = U * 2^i (for i=0..62) + * * U_i = U * (1-2^63) (for i=63) + * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0. + * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is + * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63). + * None of the resulting prec group elements have a known scalar, and neither do any of + * the intermediate sums while computing a*G. + */ + secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_scalar blind; + secp256k1_gej initial; +} secp256k1_ecmult_gen_context; + +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); + +/** Multiply with the generator: R = a*G */ +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); + +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h new file mode 100644 index 000000000000..35f25460773f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h @@ -0,0 +1,210 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ +#define _SECP256K1_ECMULT_GEN_IMPL_H_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_gen.h" +#include "hash_impl.h" +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION +#include "ecmult_static_context.h" +#endif +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_ge prec[1024]; + secp256k1_gej gj; + secp256k1_gej nums_gej; + int i, j; +#endif + + if (ctx->prec != NULL) { + return; + } +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ + { + static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; + secp256k1_fe nums_x; + secp256k1_ge nums_ge; + int r; + r = secp256k1_fe_set_b32(&nums_x, nums_b32); + (void)r; + VERIFY_CHECK(r); + r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); + (void)r; + VERIFY_CHECK(r); + secp256k1_gej_set_ge(&nums_gej, &nums_ge); + /* Add G to make the bits in x uniformly distributed. */ + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); + } + + /* compute prec. */ + { + secp256k1_gej precj[1024]; /* Jacobian versions of prec. */ + secp256k1_gej gbase; + secp256k1_gej numsbase; + gbase = gj; /* 16^j * G */ + numsbase = nums_gej; /* 2^j * nums. */ + for (j = 0; j < 64; j++) { + /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ + precj[j*16] = numsbase; + for (i = 1; i < 16; i++) { + secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + } + /* Multiply gbase by 16. */ + for (i = 0; i < 4; i++) { + secp256k1_gej_double_var(&gbase, &gbase, NULL); + } + /* Multiply numbase by 2. */ + secp256k1_gej_double_var(&numsbase, &numsbase, NULL); + if (j == 62) { + /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ + secp256k1_gej_neg(&numsbase, &numsbase); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); + } + } + secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); + } + for (j = 0; j < 64; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + } + } +#else + (void)cb; + ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; +#endif + secp256k1_ecmult_gen_blind(ctx, NULL); +} + +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); +#else + (void)cb; + dst->prec = src->prec; +#endif + dst->initial = src->initial; + dst->blind = src->blind; + } +} + +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + free(ctx->prec); +#endif + secp256k1_scalar_clear(&ctx->blind); + secp256k1_gej_clear(&ctx->initial); + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + secp256k1_scalar gnb; + int bits; + int i, j; + memset(&adds, 0, sizeof(adds)); + *r = ctx->initial; + /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ + secp256k1_scalar_add(&gnb, gn, &ctx->blind); + add.infinity = 0; + for (j = 0; j < 64; j++) { + bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); + for (i = 0; i < 16; i++) { + /** This uses a conditional move to avoid any secret data in array indexes. + * _Any_ use of secret indexes has been demonstrated to result in timing + * sidechannels, even when the cache-line access patterns are uniform. + * See also: + * "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe + * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and + * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, + * by Dag Arne Osvik, Adi Shamir, and Eran Tromer + * (http://www.tau.ac.il/~tromer/papers/cache.pdf) + */ + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); + secp256k1_scalar_clear(&gnb); +} + +/* Setup blinding values for secp256k1_ecmult_gen. */ +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { + secp256k1_scalar b; + secp256k1_gej gb; + secp256k1_fe s; + unsigned char nonce32[32]; + secp256k1_rfc6979_hmac_sha256_t rng; + int retry; + unsigned char keydata[64] = {0}; + if (seed32 == NULL) { + /* When seed is NULL, reset the initial point and blinding value. */ + secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); + secp256k1_gej_neg(&ctx->initial, &ctx->initial); + secp256k1_scalar_set_int(&ctx->blind, 1); + } + /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ + secp256k1_scalar_get_b32(nonce32, &ctx->blind); + /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, + * and guards against weak or adversarial seeds. This is a simpler and safer interface than + * asking the caller for blinding values directly and expecting them to retry on failure. + */ + memcpy(keydata, nonce32, 32); + if (seed32 != NULL) { + memcpy(keydata + 32, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + memset(keydata, 0, sizeof(keydata)); + /* Retry for out of range results to achieve uniformity. */ + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + retry = !secp256k1_fe_set_b32(&s, nonce32); + retry |= secp256k1_fe_is_zero(&s); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ + /* Randomize the projection to defend against multiplier sidechannels. */ + secp256k1_gej_rescale(&ctx->initial, &s); + secp256k1_fe_clear(&s); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + secp256k1_scalar_set_b32(&b, nonce32, &retry); + /* A blinding value of 0 works, but would undermine the projection hardening. */ + retry |= secp256k1_scalar_is_zero(&b); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + memset(nonce32, 0, 32); + secp256k1_ecmult_gen(ctx, &gb, &b); + secp256k1_scalar_negate(&b, &b); + ctx->blind = b; + ctx->initial = gb; + secp256k1_scalar_clear(&b); + secp256k1_gej_clear(&gb); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h new file mode 100644 index 000000000000..4e40104ad43c --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h @@ -0,0 +1,406 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_IMPL_H_ +#define _SECP256K1_ECMULT_IMPL_H_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * affine-isomorphism stuff which tracks z-ratios) */ +# if EXHAUSTIVE_TEST_ORDER > 128 +# define WINDOW_A 5 +# define WINDOW_G 8 +# elif EXHAUSTIVE_TEST_ORDER > 8 +# define WINDOW_A 4 +# define WINDOW_G 4 +# else +# define WINDOW_A 2 +# define WINDOW_G 2 +# endif +#else +/* optimal for 128-bit and 256-bit exponents. */ +#define WINDOW_A 5 +/** larger numbers may result in slightly better performance, at the cost of + exponentially larger precomputed tables. */ +#ifdef USE_ENDOMORPHISM +/** Two tables for window size 15: 1.375 MiB. */ +#define WINDOW_G 15 +#else +/** One table for window size 16: 1.375 MiB. */ +#define WINDOW_G 16 +#endif +#endif + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain + * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will + * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. + * Prej's Z values are undefined, except for the last value. + */ +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge a_ge, d_ge; + int i; + + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* + * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate + * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); + prej[0].x = a_ge.x; + prej[0].y = a_ge.y; + prej[0].z = a->z; + prej[0].infinity = 0; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + } + + /* + * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only + * the final point's z coordinate is actually used though, so just update that. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); +} + +/** Fill a table 'pre' with precomputed odd multiples of a. + * + * There are two versions of this function: + * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its + * resulting point set to a single constant Z denominator, stores the X and Y + * coordinates as ge_storage points in pre, and stores the global Z in rz. + * It only operates on tables sized for WINDOW_A wnaf multiples. + * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its + * resulting point set to actually affine points, and stores those in pre. + * It operates on tables of any size, but uses heap-allocated temporaries. + * + * To compute a*P + b*G, we compute a table for P using the first function, + * and for G using the second (which requires an inverse, but it only needs to + * happen once). + */ +static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +} + +static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { + secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); + secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); + secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); + int i; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); + /* Convert them in batch to affine coordinates. */ + secp256k1_ge_set_table_gej_var(prea, prej, zr, n); + /* Convert them to compact storage form. */ + for (i = 0; i < n; i++) { + secp256k1_ge_to_storage(&pre[i], &prea[i]); + } + + free(prea); + free(prej); + free(zr); +} + +/** The following two macro retrieves a particular odd multiple from a table + * of precomputed multiples. */ +#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + *(r) = (pre)[((n)-1)/2]; \ + } else { \ + secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ + } \ +} while(0) + +#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ + } else { \ + secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ + secp256k1_ge_neg((r), (r)); \ + } \ +} while(0) + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { + ctx->pre_g = NULL; +#ifdef USE_ENDOMORPHISM + ctx->pre_g_128 = NULL; +#endif +} + +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { + secp256k1_gej gj; + + if (ctx->pre_g != NULL) { + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* precompute the tables with odd multiples */ + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); + +#ifdef USE_ENDOMORPHISM + { + secp256k1_gej g_128j; + int i; + + ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* calculate 2^128*generator */ + g_128j = gj; + for (i = 0; i < 128; i++) { + secp256k1_gej_double_var(&g_128j, &g_128j, NULL); + } + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); + } +#endif +} + +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { + if (src->pre_g == NULL) { + dst->pre_g = NULL; + } else { + size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g, src->pre_g, size); + } +#ifdef USE_ENDOMORPHISM + if (src->pre_g_128 == NULL) { + dst->pre_g_128 = NULL; + } else { + size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g_128, src->pre_g_128, size); + } +#endif +} + +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { + return ctx->pre_g != NULL; +} + +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { + free(ctx->pre_g); +#ifdef USE_ENDOMORPHISM + free(ctx->pre_g_128); +#endif + secp256k1_ecmult_context_init(ctx); +} + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), + * with the following guarantees: + * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) + * - two non-zero entries in wnaf are separated by at least w-1 zeroes. + * - the number of set values in wnaf is returned. This number is at most 256, and at most one more + * than the number of bits in the (absolute value) of the input. + */ +static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { + secp256k1_scalar s = *a; + int last_set_bit = -1; + int bit = 0; + int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); + + if (secp256k1_scalar_get_bits(&s, 255, 1)) { + secp256k1_scalar_negate(&s, &s); + sign = -1; + } + + while (bit < len) { + int now; + int word; + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + bit++; + continue; + } + + now = w; + if (now > len - bit) { + now = len - bit; + } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + + bit += now; + } +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#endif + return last_set_bit + 1; +} + +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_scalar na_1, na_lam; + /* Splitted G factors. */ + secp256k1_scalar ng_1, ng_128; + int wnaf_na_1[130]; + int wnaf_na_lam[130]; + int bits_na_1; + int bits_na_lam; + int wnaf_ng_1[129]; + int bits_ng_1; + int wnaf_ng_128[129]; + int bits_ng_128; +#else + int wnaf_na[256]; + int bits_na; + int wnaf_ng[256]; + int bits_ng; +#endif + int i; + int bits; + +#ifdef USE_ENDOMORPHISM + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&na_1, &na_lam, na); + + /* build wnaf representation for na_1 and na_lam. */ + bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A); + bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A); + VERIFY_CHECK(bits_na_1 <= 130); + VERIFY_CHECK(bits_na_lam <= 130); + bits = bits_na_1; + if (bits_na_lam > bits) { + bits = bits_na_lam; + } +#else + /* build wnaf representation for na. */ + bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); + bits = bits_na; +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a); + +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } + + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } +#else + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); + if (bits_ng > bits) { + bits = bits_ng; + } +#endif + + secp256k1_gej_set_infinity(r); + + for (i = bits - 1; i >= 0; i--) { + int n; + secp256k1_gej_double_var(r, r, NULL); +#ifdef USE_ENDOMORPHISM + if (i < bits_na_1 && (n = wnaf_na_1[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_na_lam && (n = wnaf_na_lam[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } + if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#else + if (i < bits_na && (n = wnaf_na[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng && (n = wnaf_ng[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field.h new file mode 100644 index 000000000000..bbb1ee866cc4 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field.h @@ -0,0 +1,132 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_ +#define _SECP256K1_FIELD_ + +/** Field element module. + * + * Field elements can be represented in several ways, but code accessing + * it (and implementations) need to take certain properties into account: + * - Each field element can be normalized or not. + * - Each field element has a magnitude, which represents how far away + * its representation is away from normalization. Normalized elements + * always have a magnitude of 1, but a magnitude of 1 doesn't imply + * normality. + */ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_FIELD_10X26) +#include "field_10x26.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52.h" +#else +#error "Please select field implementation" +#endif + +#include "util.h" + +/** Normalize a field element. */ +static void secp256k1_fe_normalize(secp256k1_fe *r); + +/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ +static void secp256k1_fe_normalize_weak(secp256k1_fe *r); + +/** Normalize a field element, without constant-time guarantee. */ +static void secp256k1_fe_normalize_var(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r); + +/** Set a field element equal to a small integer. Resulting field element is normalized. */ +static void secp256k1_fe_set_int(secp256k1_fe *r, int a); + +/** Sets a field element equal to zero, initializing all fields. */ +static void secp256k1_fe_clear(secp256k1_fe *a); + +/** Verify whether a field element is zero. Requires the input to be normalized. */ +static int secp256k1_fe_is_zero(const secp256k1_fe *a); + +/** Check the "oddness" of a field element. Requires the input to be normalized. */ +static int secp256k1_fe_is_odd(const secp256k1_fe *a); + +/** Compare two field elements. Requires magnitude-1 inputs. */ +static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Same as secp256k1_fe_equal, but may be variable time. */ +static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Compare two field elements. Requires both inputs to be normalized */ +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); + +/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input + * as an argument. The magnitude of the output is one higher. */ +static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); + +/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that + * small integer. */ +static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); + +/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); + +/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); + +/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); + +/** If a has a square root, it is computed in r and 1 is returned. If a does not + * have a square root, the root of its negation is computed and 0 is returned. + * The input's magnitude can be at most 8. The output magnitude is 1 (but not + * guaranteed to be normalized). The result in r will always be a square + * itself. */ +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); + +/** Checks whether a field element is a quadratic residue. */ +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); + +/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be + * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); + +/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); + +/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be + * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and + * outputs must not overlap in memory. */ +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len); + +/** Convert a field element to the storage type. */ +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); + +/** Convert a field element back from the storage type. */ +static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h new file mode 100644 index 000000000000..61ee1e096564 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..9, elem[i]*2^26) mod n */ + uint32_t n[10]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) & 0x3FFFFFFUL, \ + (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ + (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ + (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ + (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ + (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ + (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ + (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ + (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ + (((uint32_t)d7) >> 10) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint32_t n[8]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h new file mode 100644 index 000000000000..5fb092f1beb5 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h @@ -0,0 +1,1140 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#include "util.h" +#include "num.h" +#include "field.h" + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint32_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + r &= (d[0] <= 0x3FFFFFFUL * m); + r &= (d[1] <= 0x3FFFFFFUL * m); + r &= (d[2] <= 0x3FFFFFFUL * m); + r &= (d[3] <= 0x3FFFFFFUL * m); + r &= (d[4] <= 0x3FFFFFFUL * m); + r &= (d[5] <= 0x3FFFFFFUL * m); + r &= (d[6] <= 0x3FFFFFFUL * m); + r &= (d[7] <= 0x3FFFFFFUL * m); + r &= (d[8] <= 0x3FFFFFFUL * m); + r &= (d[9] <= 0x03FFFFFUL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 32); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[9] == 0x03FFFFFUL)) { + uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; + if (mid == 0x3FFFFFFUL) { + r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); + } + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + if (x) { + t0 += 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint32_t z0, z1; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t z0, z1; + uint32_t x; + + t0 = r->n[0]; + t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + x = t9 >> 22; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0x3FFFFFFUL; + z1 = z0 ^ 0x3D0UL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + t4 = r->n[4]; + t5 = r->n[5]; + t6 = r->n[6]; + t7 = r->n[7]; + t8 = r->n[8]; + + t9 &= 0x03FFFFFUL; + t1 += (x << 6); + + t1 += (t0 >> 26); + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint32_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<10; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 9; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + r->n[limb] |= (uint32_t)((a[31-i] >> (2*j)) & 0x3) << shift; + } + } + if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + c |= ((a->n[limb] >> shift) & 0x3) << (2 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[4]; + r->n[5] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[5]; + r->n[6] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[6]; + r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; + r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; + r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; + r->n[5] *= a; + r->n[6] *= a; + r->n[7] *= a; + r->n[8] *= a; + r->n[9] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; + r->n[5] += a->n[5]; + r->n[6] += a->n[6]; + r->n[7] += a->n[7]; + r->n[8] += a->n[8]; + r->n[9] += a->n[9]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); + +#else + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + VERIFY_BITS(b[0], 30); + VERIFY_BITS(b[1], 30); + VERIFY_BITS(b[2], 30); + VERIFY_BITS(b[3], 30); + VERIFY_BITS(b[4], 30); + VERIFY_BITS(b[5], 30); + VERIFY_BITS(b[6], 30); + VERIFY_BITS(b[7], 30); + VERIFY_BITS(b[8], 30); + VERIFY_BITS(b[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)a[0] * b[9] + + (uint64_t)a[1] * b[8] + + (uint64_t)a[2] * b[7] + + (uint64_t)a[3] * b[6] + + (uint64_t)a[4] * b[5] + + (uint64_t)a[5] * b[4] + + (uint64_t)a[6] * b[3] + + (uint64_t)a[7] * b[2] + + (uint64_t)a[8] * b[1] + + (uint64_t)a[9] * b[0]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * b[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)a[1] * b[9] + + (uint64_t)a[2] * b[8] + + (uint64_t)a[3] * b[7] + + (uint64_t)a[4] * b[6] + + (uint64_t)a[5] * b[5] + + (uint64_t)a[6] * b[4] + + (uint64_t)a[7] * b[3] + + (uint64_t)a[8] * b[2] + + (uint64_t)a[9] * b[1]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)a[0] * b[1] + + (uint64_t)a[1] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)a[2] * b[9] + + (uint64_t)a[3] * b[8] + + (uint64_t)a[4] * b[7] + + (uint64_t)a[5] * b[6] + + (uint64_t)a[6] * b[5] + + (uint64_t)a[7] * b[4] + + (uint64_t)a[8] * b[3] + + (uint64_t)a[9] * b[2]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)a[0] * b[2] + + (uint64_t)a[1] * b[1] + + (uint64_t)a[2] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)a[3] * b[9] + + (uint64_t)a[4] * b[8] + + (uint64_t)a[5] * b[7] + + (uint64_t)a[6] * b[6] + + (uint64_t)a[7] * b[5] + + (uint64_t)a[8] * b[4] + + (uint64_t)a[9] * b[3]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[3] + + (uint64_t)a[1] * b[2] + + (uint64_t)a[2] * b[1] + + (uint64_t)a[3] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)a[4] * b[9] + + (uint64_t)a[5] * b[8] + + (uint64_t)a[6] * b[7] + + (uint64_t)a[7] * b[6] + + (uint64_t)a[8] * b[5] + + (uint64_t)a[9] * b[4]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[4] + + (uint64_t)a[1] * b[3] + + (uint64_t)a[2] * b[2] + + (uint64_t)a[3] * b[1] + + (uint64_t)a[4] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[5] * b[9] + + (uint64_t)a[6] * b[8] + + (uint64_t)a[7] * b[7] + + (uint64_t)a[8] * b[6] + + (uint64_t)a[9] * b[5]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[5] + + (uint64_t)a[1] * b[4] + + (uint64_t)a[2] * b[3] + + (uint64_t)a[3] * b[2] + + (uint64_t)a[4] * b[1] + + (uint64_t)a[5] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[6] * b[9] + + (uint64_t)a[7] * b[8] + + (uint64_t)a[8] * b[7] + + (uint64_t)a[9] * b[6]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[6] + + (uint64_t)a[1] * b[5] + + (uint64_t)a[2] * b[4] + + (uint64_t)a[3] * b[3] + + (uint64_t)a[4] * b[2] + + (uint64_t)a[5] * b[1] + + (uint64_t)a[6] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[7] * b[9] + + (uint64_t)a[8] * b[8] + + (uint64_t)a[9] * b[7]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[7] + + (uint64_t)a[1] * b[6] + + (uint64_t)a[2] * b[5] + + (uint64_t)a[3] * b[4] + + (uint64_t)a[4] * b[3] + + (uint64_t)a[5] * b[2] + + (uint64_t)a[6] * b[1] + + (uint64_t)a[7] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[8] * b[9] + + (uint64_t)a[9] * b[8]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[8] + + (uint64_t)a[1] * b[7] + + (uint64_t)a[2] * b[6] + + (uint64_t)a[3] * b[5] + + (uint64_t)a[4] * b[4] + + (uint64_t)a[5] * b[3] + + (uint64_t)a[6] * b[2] + + (uint64_t)a[7] * b[1] + + (uint64_t)a[8] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * b[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t0, t1, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)(a[0]*2) * a[9] + + (uint64_t)(a[1]*2) * a[8] + + (uint64_t)(a[2]*2) * a[7] + + (uint64_t)(a[3]*2) * a[6] + + (uint64_t)(a[4]*2) * a[5]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * a[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)(a[1]*2) * a[9] + + (uint64_t)(a[2]*2) * a[8] + + (uint64_t)(a[3]*2) * a[7] + + (uint64_t)(a[4]*2) * a[6] + + (uint64_t)a[5] * a[5]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)(a[0]*2) * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)(a[2]*2) * a[9] + + (uint64_t)(a[3]*2) * a[8] + + (uint64_t)(a[4]*2) * a[7] + + (uint64_t)(a[5]*2) * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[2] + + (uint64_t)a[1] * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)(a[3]*2) * a[9] + + (uint64_t)(a[4]*2) * a[8] + + (uint64_t)(a[5]*2) * a[7] + + (uint64_t)a[6] * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[3] + + (uint64_t)(a[1]*2) * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)(a[4]*2) * a[9] + + (uint64_t)(a[5]*2) * a[8] + + (uint64_t)(a[6]*2) * a[7]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[4] + + (uint64_t)(a[1]*2) * a[3] + + (uint64_t)a[2] * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[5]*2) * a[9] + + (uint64_t)(a[6]*2) * a[8] + + (uint64_t)a[7] * a[7]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[5] + + (uint64_t)(a[1]*2) * a[4] + + (uint64_t)(a[2]*2) * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[6]*2) * a[9] + + (uint64_t)(a[7]*2) * a[8]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[6] + + (uint64_t)(a[1]*2) * a[5] + + (uint64_t)(a[2]*2) * a[4] + + (uint64_t)a[3] * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[7]*2) * a[9] + + (uint64_t)a[8] * a[8]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[7] + + (uint64_t)(a[1]*2) * a[6] + + (uint64_t)(a[2]*2) * a[5] + + (uint64_t)(a[3]*2) * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[8]*2) * a[9]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[8] + + (uint64_t)(a[1]*2) * a[7] + + (uint64_t)(a[2]*2) * a[6] + + (uint64_t)(a[3]*2) * a[5] + + (uint64_t)a[4] * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * a[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} +#endif + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); + r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); + r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 26; + r->n[1] = a->n[1] >> 6 | a->n[2] << 20; + r->n[2] = a->n[2] >> 12 | a->n[3] << 14; + r->n[3] = a->n[3] >> 18 | a->n[4] << 8; + r->n[4] = a->n[4] >> 24 | a->n[5] << 2 | a->n[6] << 28; + r->n[5] = a->n[6] >> 4 | a->n[7] << 22; + r->n[6] = a->n[7] >> 10 | a->n[8] << 16; + r->n[7] = a->n[8] >> 16 | a->n[9] << 10; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0x3FFFFFFUL; + r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); + r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); + r->n[3] = a->n[2] >> 14 | ((a->n[3] << 18) & 0x3FFFFFFUL); + r->n[4] = a->n[3] >> 8 | ((a->n[4] << 24) & 0x3FFFFFFUL); + r->n[5] = (a->n[4] >> 2) & 0x3FFFFFFUL; + r->n[6] = a->n[4] >> 28 | ((a->n[5] << 4) & 0x3FFFFFFUL); + r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); + r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); + r->n[9] = a->n[7] >> 10; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h new file mode 100644 index 000000000000..8e69a560dccb --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..4, elem[i]*2^52) mod n */ + uint64_t n[5]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ + ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ + ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ + ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ + ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint64_t n[4]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ + (d0) | (((uint64_t)(d1)) << 32), \ + (d2) | (((uint64_t)(d3)) << 32), \ + (d4) | (((uint64_t)(d5)) << 32), \ + (d6) | (((uint64_t)(d7)) << 32) \ +}} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h new file mode 100644 index 000000000000..98cc004bf04a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h @@ -0,0 +1,502 @@ +/********************************************************************** + * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * Changelog: + * - March 2013, Diederik Huys: original version + * - November 2014, Pieter Wuille: updated to use Peter Dettman's parallel multiplication algorithm + * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly + */ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * r15:rcx = d + * r10-r14 = a0-a4 + * rbx = b + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + + /* d += a3 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rcx\n" + "movq %%rdx,%%r15\n" + /* d += a2 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d = a0 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c = a4 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* d += a4 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a0 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* t4 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a4 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* u0 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a1 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a4 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a2 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a1 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b2 (last use of %%r10 = a0) */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0), t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* d += a4 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rcx only) */ + "shrdq $52,%%r15,%%rcx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rcx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "b"(b), "D"(r) +: "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * rcx:rbx = d + * r10-r14 = a0-a4 + * r15 = M (0xfffffffffffff) + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + "movq $0xfffffffffffff,%%r15\n" + + /* d = (a0*2) * a3 */ + "leaq (%%r10,%%r10,1),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rbx\n" + "movq %%rdx,%%rcx\n" + /* d += (a1*2) * a2 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c = a4 * a4 */ + "movq %%r14,%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* a4 *= 2 */ + "addq %%r14,%%r14\n" + /* d += a0 * a4 */ + "movq %%r10,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d+= (a1*2) * a3 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a2 * a2 */ + "movq %%r12,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* t4 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * a0 */ + "movq %%r10,%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a1 * a4 */ + "movq %%r11,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += (a2*2) * a3 */ + "leaq (%%r12,%%r12,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* u0 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* a0 *= 2 */ + "addq %%r10,%%r10\n" + /* c += a0 * a1 */ + "movq %%r10,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a2 * a4 */ + "movq %%r12,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a3 * a3 */ + "movq %%r13,%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a0 * a2 (last use of %%r10) */ + "movq %%r10,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0),t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* c += a1 * a1 */ + "movq %%r11,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a3 * a4 */ + "movq %%r13,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rbx only) */ + "shrdq $52,%%rcx,%%rbx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rbx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "D"(r) +: "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h new file mode 100644 index 000000000000..dd88f38c77bb --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h @@ -0,0 +1,451 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" +#include "num.h" +#include "field.h" + +#if defined(USE_ASM_X86_64) +#include "field_5x52_asm_impl.h" +#else +#include "field_5x52_int128_impl.h" +#endif + +/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, + * represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular, + * each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element + * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations + * accept any input with magnitude at most M, and have different rules for propagating magnitude to their + * output. + */ + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint64_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 2048); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + r &= (d[0] < 0xFFFFEFFFFFC2FULL); + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + if (x) { + t0 += 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint64_t z0, z1; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint64_t t0, t1, t2, t3, t4; + uint64_t z0, z1; + uint64_t x; + + t0 = r->n[0]; + t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + x = t4 >> 48; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0xFFFFFFFFFFFFFULL; + z1 = z0 ^ 0x1000003D0ULL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + + t4 &= 0x0FFFFFFFFFFFFULL; + + t1 += (t0 >> 52); + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint64_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<5; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 4; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + r->n[limb] |= (uint64_t)((a[31-i] >> (4*j)) & 0xF) << shift; + } + } + if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + c |= ((a->n[limb] >> shift) & 0xF) << (4 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 52; + r->n[1] = a->n[1] >> 12 | a->n[2] << 40; + r->n[2] = a->n[2] >> 24 | a->n[3] << 28; + r->n[3] = a->n[3] >> 36 | a->n[4] << 16; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; + r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); + r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); + r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); + r->n[4] = a->n[3] >> 16; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h new file mode 100644 index 000000000000..0bf22bdd3ec8 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h @@ -0,0 +1,277 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +#include + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { + uint128_t c, d; + uint64_t t3, t4, tx, u0; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + VERIFY_BITS(b[0], 56); + VERIFY_BITS(b[1], 56); + VERIFY_BITS(b[2], 56); + VERIFY_BITS(b[3], 56); + VERIFY_BITS(b[4], 52); + VERIFY_CHECK(r != b); + + /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)a0 * b[3] + + (uint128_t)a1 * b[2] + + (uint128_t)a2 * b[1] + + (uint128_t)a3 * b[0]; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * b[4]; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + d += (uint128_t)a0 * b[4] + + (uint128_t)a1 * b[3] + + (uint128_t)a2 * b[2] + + (uint128_t)a3 * b[1] + + (uint128_t)a4 * b[0]; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * b[0]; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * b[4] + + (uint128_t)a2 * b[3] + + (uint128_t)a3 * b[2] + + (uint128_t)a4 * b[1]; + VERIFY_BITS(d, 115); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 63); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 115); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + c += (uint128_t)a0 * b[1] + + (uint128_t)a1 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * b[4] + + (uint128_t)a3 * b[3] + + (uint128_t)a4 * b[2]; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * b[2] + + (uint128_t)a1 * b[1] + + (uint128_t)a2 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * b[4] + + (uint128_t)a4 * b[3]; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { + uint128_t c, d; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + int64_t t3, t4, tx, u0; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + + /** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)(a0*2) * a3 + + (uint128_t)(a1*2) * a2; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * a4; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + a4 *= 2; + d += (uint128_t)a0 * a4 + + (uint128_t)(a1*2) * a3 + + (uint128_t)a2 * a2; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * a0; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * a4 + + (uint128_t)(a2*2) * a3; + VERIFY_BITS(d, 114); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 62); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 113); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + a0 *= 2; + c += (uint128_t)a0 * a1; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * a4 + + (uint128_t)a3 * a3; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * a2 + + (uint128_t)a1 * a1; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * a4; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h new file mode 100644 index 000000000000..5127b279bc7f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_IMPL_H_ +#define _SECP256K1_FIELD_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" + +#if defined(USE_FIELD_10X26) +#include "field_10x26_impl.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52_impl.h" +#else +#error "Please select field implementation" +#endif + +SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero(&na); +} + +SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero_var(&na); +} + +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { + /** Given that p is congruent to 3 mod 4, we can compute the square root of + * a mod p as the (p+1)/4'th power of a. + * + * As (p+1)/4 is an even number, it will have the same result for a and for + * (-a). Only one of these two numbers actually has a square root however, + * so we test at the end by squaring and comparing to the input. + * Also because (p+1)/4 is an even number, the computed square root is + * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). + */ + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in + * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<6; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_sqr(r, &t1); + + /* Check that a square root was actually calculated */ + + secp256k1_fe_sqr(&t1, r); + return secp256k1_fe_equal(&t1, a); +} + +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) { + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in + * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<5; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, a); + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(r, a, &t1); +} + +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) { +#if defined(USE_FIELD_INV_BUILTIN) + secp256k1_fe_inv(r, a); +#elif defined(USE_FIELD_INV_NUM) + secp256k1_num n, m; + static const secp256k1_fe negone = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL + ); + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + unsigned char b[32]; + int res; + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + res = secp256k1_fe_set_b32(r, b); + (void)res; + VERIFY_CHECK(res); + /* Verify the result is the (unique) valid inverse using non-GMP code. */ + secp256k1_fe_mul(&c, &c, r); + secp256k1_fe_add(&c, &negone); + CHECK(secp256k1_fe_normalizes_to_zero_var(&c)); +#else +#error "Please select field inverse implementation" +#endif +} + +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) { + secp256k1_fe u; + size_t i; + if (len < 1) { + return; + } + + VERIFY_CHECK((r + len <= a) || (a + len <= r)); + + r[0] = a[0]; + + i = 0; + while (++i < len) { + secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]); + } + + secp256k1_fe_inv_var(&u, &r[--i]); + + while (i > 0) { + size_t j = i--; + secp256k1_fe_mul(&r[j], &r[i], &u); + secp256k1_fe_mul(&u, &u, &a[j]); + } + + r[0] = u; +} + +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { +#ifndef USE_NUM_NONE + unsigned char b[32]; + secp256k1_num n; + secp256k1_num m; + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + return secp256k1_num_jacobi(&n, &m) >= 0; +#else + secp256k1_fe r; + return secp256k1_fe_sqrt(&r, a); +#endif +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c new file mode 100644 index 000000000000..1835fd491d16 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#define USE_BASIC_CONFIG 1 + +#include "basic-config.h" +#include "include/secp256k1.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_gen_impl.h" + +static void default_error_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + +int main(int argc, char **argv) { + secp256k1_ecmult_gen_context ctx; + int inner; + int outer; + FILE* fp; + + (void)argc; + (void)argv; + + fp = fopen("src/ecmult_static_context.h","w"); + if (fp == NULL) { + fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); + return -1; + } + + fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); + + secp256k1_ecmult_gen_context_init(&ctx); + secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); + for(outer = 0; outer != 64; outer++) { + fprintf(fp,"{\n"); + for(inner = 0; inner != 16; inner++) { + fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); + if (inner != 15) { + fprintf(fp,",\n"); + } else { + fprintf(fp,"\n"); + } + } + if (outer != 63) { + fprintf(fp,"},\n"); + } else { + fprintf(fp,"}\n"); + } + } + fprintf(fp,"};\n"); + secp256k1_ecmult_gen_context_clear(&ctx); + + fprintf(fp, "#undef SC\n"); + fprintf(fp, "#endif\n"); + fclose(fp); + + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group.h new file mode 100644 index 000000000000..4957b248fe6a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group.h @@ -0,0 +1,144 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_ +#define _SECP256K1_GROUP_ + +#include "num.h" +#include "field.h" + +/** A group element of the secp256k1 curve, in affine coordinates. */ +typedef struct { + secp256k1_fe x; + secp256k1_fe y; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_ge; + +#define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} +#define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +/** A group element of the secp256k1 curve, in jacobian coordinates. */ +typedef struct { + secp256k1_fe x; /* actual X: x/z^2 */ + secp256k1_fe y; /* actual Y: y/z^3 */ + secp256k1_fe z; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_gej; + +#define SECP256K1_GEJ_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} +#define SECP256K1_GEJ_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +typedef struct { + secp256k1_fe_storage x; + secp256k1_fe_storage y; +} secp256k1_ge_storage; + +#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} + +#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) + +/** Set a group element equal to the point with given X and Y coordinates */ +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); + +/** Set a group element (affine) equal to the point with the given X coordinate + * and a Y coordinate that is a quadratic residue modulo p. The return value + * is true iff a coordinate with the given X coordinate exists. + */ +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); + +/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness + * for Y. Return value indicates whether the result is valid. */ +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_ge_is_infinity(const secp256k1_ge *a); + +/** Check whether a group element is valid (i.e., on the curve). */ +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); + +/** Set a group element equal to another which is given in jacobian coordinates */ +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); + +/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); + +/** Set a batch of group elements equal to the inputs given in jacobian + * coordinates (with known z-ratios). zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len); + +/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to + * the same global z "denominator". zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y + * coordinates of the result are stored in r, the common z coordinate is + * stored in globalz. */ +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); + +/** Set a group element (jacobian) equal to the point at infinity. */ +static void secp256k1_gej_set_infinity(secp256k1_gej *r); + +/** Set a group element (jacobian) equal to another which is given in affine coordinates. */ +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); + +/** Compare the X coordinate of a group element (jacobian). */ +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); + +/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_gej_is_infinity(const secp256k1_gej *a); + +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). + * a may not be zero. Constant time. */ +static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); + +/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient + than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time + guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */ +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv); + +#ifdef USE_ENDOMORPHISM +/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */ +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); +#endif + +/** Clear a secp256k1_gej to prevent leaking sensitive information. */ +static void secp256k1_gej_clear(secp256k1_gej *r); + +/** Clear a secp256k1_ge to prevent leaking sensitive information. */ +static void secp256k1_ge_clear(secp256k1_ge *r); + +/** Convert a group element to the storage type. */ +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); + +/** Convert a group element back from the storage type. */ +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag); + +/** Rescale a jacobian point by b which must be non-zero. Constant-time. */ +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h new file mode 100644 index 000000000000..7d723532ff3e --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h @@ -0,0 +1,700 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_IMPL_H_ +#define _SECP256K1_GROUP_IMPL_H_ + +#include "num.h" +#include "field.h" +#include "group.h" + +/* These points can be generated in sage as follows: + * + * 0. Setup a worksheet with the following parameters. + * b = 4 # whatever CURVE_B will be set to + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (b)]) + * + * 1. Determine all the small orders available to you. (If there are + * no satisfactory ones, go back and change b.) + * print C.order().factor(limit=1000) + * + * 2. Choose an order as one of the prime factors listed in the above step. + * (You can also multiply some to get a composite order, though the + * tests will crash trying to invert scalars during signing.) We take a + * random point and scale it to drop its order to the desired value. + * There is some probability this won't work; just try again. + * order = 199 + * P = C.random_point() + * P = (int(P.order()) / int(order)) * P + * assert(P.order() == order) + * + * 3. Print the values. You'll need to use a vim macro or something to + * split the hex output into 4-byte chunks. + * print "%x %x" % P.xy() + */ +#if defined(EXHAUSTIVE_TEST_ORDER) +# if EXHAUSTIVE_TEST_ORDER == 199 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069, + 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18, + 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868, + 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED +); + +const int CURVE_B = 4; +# elif EXHAUSTIVE_TEST_ORDER == 13 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0, + 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15, + 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e, + 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac +); +const int CURVE_B = 2; +# else +# error No known generator for the specified exhaustive test group order. +# endif +#else +/** Generator for secp256k1, value 'g' defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + */ +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL, + 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL, + 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL, + 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL +); + +const int CURVE_B = 7; +#endif + +static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { + secp256k1_fe zi2; + secp256k1_fe zi3; + secp256k1_fe_sqr(&zi2, zi); + secp256k1_fe_mul(&zi3, &zi2, zi); + secp256k1_fe_mul(&r->x, &a->x, &zi2); + secp256k1_fe_mul(&r->y, &a->y, &zi3); + r->infinity = a->infinity; +} + +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + r->infinity = 0; + r->x = *x; + r->y = *y; +} + +static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + return a->infinity; +} + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + *r = *a; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + secp256k1_fe_inv(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + if (a->infinity) { + return; + } + secp256k1_fe_inv_var(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { + secp256k1_fe *az; + secp256k1_fe *azi; + size_t i; + size_t count = 0; + az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + for (i = 0; i < len; i++) { + if (!a[i].infinity) { + az[count++] = a[i].z; + } + } + + azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); + secp256k1_fe_inv_all_var(azi, az, count); + free(az); + + count = 0; + for (i = 0; i < len; i++) { + r[i].infinity = a[i].infinity; + if (!a[i].infinity) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); + } + } + free(azi); +} + +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) { + size_t i = len - 1; + secp256k1_fe zi; + + if (len > 0) { + /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */ + secp256k1_fe_inv(&zi, &a[i].z); + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + + /* Work out way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + secp256k1_fe_mul(&zi, &zi, &zr[i]); + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + } + } +} + +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { + size_t i = len - 1; + secp256k1_fe zs; + + if (len > 0) { + /* The z of the final point gives us the "global Z" for the table. */ + r[i].x = a[i].x; + r[i].y = a[i].y; + *globalz = a[i].z; + r[i].infinity = 0; + zs = zr[i]; + + /* Work our way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + if (i != len - 1) { + secp256k1_fe_mul(&zs, &zs, &zr[i]); + } + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + } + } +} + +static void secp256k1_gej_set_infinity(secp256k1_gej *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_gej_clear(secp256k1_gej *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_ge_clear(secp256k1_ge *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); +} + +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { + secp256k1_fe x2, x3, c; + r->x = *x; + secp256k1_fe_sqr(&x2, x); + secp256k1_fe_mul(&x3, x, &x2); + r->infinity = 0; + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&c, &x3); + return secp256k1_fe_sqrt(&r->y, &c); +} + +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + if (!secp256k1_ge_set_xquad(r, x)) { + return 0; + } + secp256k1_fe_normalize_var(&r->y); + if (secp256k1_fe_is_odd(&r->y) != odd) { + secp256k1_fe_negate(&r->y, &r->y, 1); + } + return 1; + +} + +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + secp256k1_fe_set_int(&r->z, 1); +} + +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { + secp256k1_fe r, r2; + VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); + r2 = a->x; secp256k1_fe_normalize_weak(&r2); + return secp256k1_fe_equal_var(&r, &r2); +} + +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + r->z = a->z; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { + return a->infinity; +} + +static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) { + secp256k1_fe y2, x3, z2, z6; + if (a->infinity) { + return 0; + } + /** y^2 = x^3 + 7 + * (Y/Z^3)^2 = (X/Z^2)^3 + 7 + * Y^2 / Z^6 = X^3 / Z^6 + 7 + * Y^2 = X^3 + 7*Z^6 + */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2); + secp256k1_fe_mul_int(&z6, CURVE_B); + secp256k1_fe_add(&x3, &z6); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { + secp256k1_fe y2, x3, c; + if (a->infinity) { + return 0; + } + /* y^2 = x^3 + 7 */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&x3, &c); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. + * + * Note that there is an implementation described at + * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + * which trades a multiply for a square, but in practice this is actually slower, + * mainly because it requires more normalizations. + */ + secp256k1_fe t1,t2,t3,t4; + /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, + * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have + * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * the infinity flag even though the point doubles to infinity, and the result + * point will be gibberish (z = 0 but infinity = 0). + */ + r->infinity = a->infinity; + if (r->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + return; + } + + if (rzr != NULL) { + *rzr = a->y; + secp256k1_fe_normalize_weak(rzr); + secp256k1_fe_mul_int(rzr, 2); + } + + secp256k1_fe_mul(&r->z, &a->z, &a->y); + secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ + secp256k1_fe_sqr(&t1, &a->x); + secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ + secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ + secp256k1_fe_sqr(&t3, &a->y); + secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ + secp256k1_fe_sqr(&t4, &t3); + secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ + secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ + r->x = t3; + secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ + secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ + secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ + secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ + secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ + secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ + secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ + secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ + secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ +} + +static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); + secp256k1_gej_double_var(r, a, rzr); +} + +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { + /* Operations: 12 mul, 4 sqr, 2 normalize, 12 mul_int/add/negate */ + secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + *r = *b; + return; + } + + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + + r->infinity = 0; + secp256k1_fe_sqr(&z22, &b->z); + secp256k1_fe_sqr(&z12, &a->z); + secp256k1_fe_mul(&u1, &a->x, &z22); + secp256k1_fe_mul(&u2, &b->x, &z12); + secp256k1_fe_mul(&s1, &a->y, &z22); secp256k1_fe_mul(&s1, &s1, &b->z); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + secp256k1_fe_mul(&h, &h, &b->z); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { + /* 8 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + secp256k1_gej_set_ge(r, b); + return; + } + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + r->infinity = 0; + + secp256k1_fe_sqr(&z12, &a->z); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { + /* 9 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe az, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (b->infinity) { + *r = *a; + return; + } + if (a->infinity) { + secp256k1_fe bzinv2, bzinv3; + r->infinity = b->infinity; + secp256k1_fe_sqr(&bzinv2, bzinv); + secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); + secp256k1_fe_mul(&r->x, &b->x, &bzinv2); + secp256k1_fe_mul(&r->y, &b->y, &bzinv3); + secp256k1_fe_set_int(&r->z, 1); + return; + } + r->infinity = 0; + + /** We need to calculate (rx,ry,rz) = (ax,ay,az) + (bx,by,1/bzinv). Due to + * secp256k1's isomorphism we can multiply the Z coordinates on both sides + * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). + * This means that (rx,ry,rz) can be calculated as + * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. + * The variable az below holds the modified Z coordinate for a, which is used + * for the computation of rx and ry, but not for rz. + */ + secp256k1_fe_mul(&az, &a->z, bzinv); + + secp256k1_fe_sqr(&z12, &az); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &az); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, NULL); + } else { + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + r->z = a->z; secp256k1_fe_mul(&r->z, &r->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + + +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { + /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ + static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; + secp256k1_fe m_alt, rr_alt; + int infinity, degenerate; + VERIFY_CHECK(!b->infinity); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + + /** In: + * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. + * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. + * we find as solution for a unified addition/doubling formula: + * lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. + * x3 = lambda^2 - (x1 + x2) + * 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). + * + * Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: + * U1 = X1*Z2^2, U2 = X2*Z1^2 + * S1 = Y1*Z2^3, S2 = Y2*Z1^3 + * Z = Z1*Z2 + * T = U1+U2 + * M = S1+S2 + * Q = T*M^2 + * R = T^2-U1*U2 + * X3 = 4*(R^2-Q) + * Y3 = 4*(R*(3*Q-2*R^2)-M^4) + * Z3 = 2*M*Z + * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) + * + * This formula has the benefit of being the same for both addition + * of distinct points and doubling. However, it breaks down in the + * case that either point is infinity, or that y1 = -y2. We handle + * these cases in the following ways: + * + * - If b is infinity we simply bail by means of a VERIFY_CHECK. + * + * - If a is infinity, we detect this, and at the end of the + * computation replace the result (which will be meaningless, + * but we compute to be constant-time) with b.x : b.y : 1. + * + * - If a = -b, we have y1 = -y2, which is a degenerate case. + * But here the answer is infinity, so we simply set the + * infinity flag of the result, overriding the computed values + * without even needing to cmov. + * + * - If y1 = -y2 but x1 != x2, which does occur thanks to certain + * properties of our curve (specifically, 1 has nontrivial cube + * roots in our field, and the curve equation has no x coefficient) + * then the answer is not infinity but also not given by the above + * equation. In this case, we cmov in place an alternate expression + * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these + * expressions for lambda are defined, they are equal, and can be + * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) + * then substitution of x^3 + 7 for y^2 (using the curve equation). + * For all pairs of nonzero points (a, b) at least one is defined, + * so this covers everything. + */ + + secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ + u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ + secp256k1_fe_mul(&u2, &b->x, &zz); /* u2 = U2 = X2*Z1^2 (1) */ + s1 = a->y; secp256k1_fe_normalize_weak(&s1); /* s1 = S1 = Y1*Z2^3 (1) */ + secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z1^2 (1) */ + secp256k1_fe_mul(&s2, &s2, &a->z); /* s2 = S2 = Y2*Z1^3 (1) */ + t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ + m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ + secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ + secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ + secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ + secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ + /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" + * case that Z = z1z2 = 0, and this is special-cased later on). */ + degenerate = secp256k1_fe_normalizes_to_zero(&m) & + secp256k1_fe_normalizes_to_zero(&rr); + /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. + * This means either x1 == beta*x2 or beta*x1 == x2, where beta is + * a nontrivial cube root of one. In either case, an alternate + * non-indeterminate expression for lambda is (y1 - y2)/(x1 - x2), + * so we set R/M equal to this. */ + rr_alt = s1; + secp256k1_fe_mul_int(&rr_alt, 2); /* rr = Y1*Z2^3 - Y2*Z1^3 (2) */ + secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 */ + + secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); + secp256k1_fe_cmov(&m_alt, &m, !degenerate); + /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. + * From here on out Ralt and Malt represent the numerator + * and denominator of lambda; R and M represent the explicit + * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ + secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ + secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ + /* These two lines use the observation that either M == Malt or M == 0, + * so M^3 * Malt is either Malt^4 (which is computed by squaring), or + * zero (which is "computed" by cmov). So the cost is one squaring + * versus two multiplications. */ + secp256k1_fe_sqr(&n, &n); + secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ + secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ + secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ + infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity); + secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ + secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ + secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ + secp256k1_fe_normalize_weak(&t); + r->x = t; /* r->x = Ralt^2-Q (1) */ + secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ + secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ + secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ + secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ + secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ + secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ + + /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ + secp256k1_fe_cmov(&r->x, &b->x, a->infinity); + secp256k1_fe_cmov(&r->y, &b->y, a->infinity); + secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); + r->infinity = infinity; +} + +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { + /* Operations: 4 mul, 1 sqr */ + secp256k1_fe zz; + VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + secp256k1_fe_sqr(&zz, s); + secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ + secp256k1_fe_mul(&r->y, &r->y, &zz); + secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ + secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ +} + +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { + secp256k1_fe x, y; + VERIFY_CHECK(!a->infinity); + x = a->x; + secp256k1_fe_normalize(&x); + y = a->y; + secp256k1_fe_normalize(&y); + secp256k1_fe_to_storage(&r->x, &x); + secp256k1_fe_to_storage(&r->y, &y); +} + +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { + secp256k1_fe_from_storage(&r->x, &a->x); + secp256k1_fe_from_storage(&r->y, &a->y); + r->infinity = 0; +} + +static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { + secp256k1_fe_storage_cmov(&r->x, &a->x, flag); + secp256k1_fe_storage_cmov(&r->y, &a->y, flag); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { + static const secp256k1_fe beta = SECP256K1_FE_CONST( + 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, + 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul + ); + *r = *a; + secp256k1_fe_mul(&r->x, &r->x, &beta); +} +#endif + +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_quad_var(&yz); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h new file mode 100644 index 000000000000..fca98cab9f83 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h @@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_ +#define _SECP256K1_HASH_ + +#include +#include + +typedef struct { + uint32_t s[8]; + uint32_t buf[16]; /* In big endian */ + size_t bytes; +} secp256k1_sha256_t; + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash); +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32); + +typedef struct { + secp256k1_sha256_t inner, outer; +} secp256k1_hmac_sha256_t; + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32); + +typedef struct { + unsigned char v[32]; + unsigned char k[32]; + int retry; +} secp256k1_rfc6979_hmac_sha256_t; + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h new file mode 100644 index 000000000000..b47e65f830a9 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h @@ -0,0 +1,281 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_IMPL_H_ +#define _SECP256K1_HASH_IMPL_H_ + +#include "hash.h" + +#include +#include +#include + +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10)) +#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7)) +#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3)) +#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10)) + +#define Round(a,b,c,d,e,f,g,h,k,w) do { \ + uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \ + uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \ + (d) += t1; \ + (h) = t1 + t2; \ +} while(0) + +#ifdef WORDS_BIGENDIAN +#define BE32(x) (x) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#endif + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) { + hash->s[0] = 0x6a09e667ul; + hash->s[1] = 0xbb67ae85ul; + hash->s[2] = 0x3c6ef372ul; + hash->s[3] = 0xa54ff53aul; + hash->s[4] = 0x510e527ful; + hash->s[5] = 0x9b05688cul; + hash->s[6] = 0x1f83d9abul; + hash->s[7] = 0x5be0cd19ul; + hash->bytes = 0; +} + +/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ +static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) { + size_t bufsize = hash->bytes & 0x3F; + hash->bytes += len; + while (bufsize + len >= 64) { + /* Fill the buffer, and process it. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); + data += 64 - bufsize; + len -= 64 - bufsize; + secp256k1_sha256_transform(hash->s, hash->buf); + bufsize = 0; + } + if (len) { + /* Fill the buffer with what remains. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, len); + } +} + +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) { + static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t sizedesc[2]; + uint32_t out[8]; + int i = 0; + sizedesc[0] = BE32(hash->bytes >> 29); + sizedesc[1] = BE32(hash->bytes << 3); + secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); + secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + for (i = 0; i < 8; i++) { + out[i] = BE32(hash->s[i]); + hash->s[i] = 0; + } + memcpy(out32, (const unsigned char*)out, 32); +} + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) { + int n; + unsigned char rkey[64]; + if (keylen <= 64) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 64 - keylen); + } else { + secp256k1_sha256_t sha256; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, key, keylen); + secp256k1_sha256_finalize(&sha256, rkey); + memset(rkey + 32, 0, 32); + } + + secp256k1_sha256_initialize(&hash->outer); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c; + } + secp256k1_sha256_write(&hash->outer, rkey, 64); + + secp256k1_sha256_initialize(&hash->inner); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c ^ 0x36; + } + secp256k1_sha256_write(&hash->inner, rkey, 64); + memset(rkey, 0, 64); +} + +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) { + secp256k1_sha256_write(&hash->inner, data, size); +} + +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) { + unsigned char temp[32]; + secp256k1_sha256_finalize(&hash->inner, temp); + secp256k1_sha256_write(&hash->outer, temp, 32); + memset(temp, 0, 32); + secp256k1_sha256_finalize(&hash->outer, out32); +} + + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256_t hmac; + static const unsigned char zero[1] = {0x00}; + static const unsigned char one[1] = {0x01}; + + memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */ + memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */ + + /* RFC6979 3.2.d. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + + /* RFC6979 3.2.f. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, one, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + rng->retry = 0; +} + +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) { + /* RFC6979 3.2.h. */ + static const unsigned char zero[1] = {0x00}; + if (rng->retry) { + secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + } + + while (outlen > 0) { + secp256k1_hmac_sha256_t hmac; + int now = outlen; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + if (now > 32) { + now = 32; + } + memcpy(out, rng->v, now); + out += now; + outlen -= now; + } + + rng->retry = 1; +} + +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) { + memset(rng->k, 0, 32); + memset(rng->v, 0, 32); + rng->retry = 0; +} + +#undef BE32 +#undef Round +#undef sigma1 +#undef sigma0 +#undef Sigma1 +#undef Sigma0 +#undef Maj +#undef Ch + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java new file mode 100644 index 000000000000..1c67802fba82 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -0,0 +1,446 @@ +/* + * Copyright 2013 Google Inc. + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import java.math.BigInteger; +import com.google.common.base.Preconditions; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + *

This class holds native methods to handle ECDSA verification.

+ * + *

You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1

+ * + *

To build secp256k1 for use with bitcoinj, run + * `./configure --enable-jni --enable-experimental --enable-module-ecdh` + * and `make` then copy `.libs/libsecp256k1.so` to your system library path + * or point the JVM to the folder containing it with -Djava.library.path + *

+ */ +public class NativeSecp256k1 { + + private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private static final Lock r = rwl.readLock(); + private static final Lock w = rwl.writeLock(); + private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); + /** + * Verifies the given secp256k1 signature in native code. + * Calling when enabled == false is undefined (probably library not loaded) + * + * @param data The data which was signed, must be exactly 32 bytes + * @param signature The signature + * @param pub The public key which did the signing + */ + public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 520) { + byteBuff = ByteBuffer.allocateDirect(520); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(signature); + byteBuff.put(pub); + + byte[][] retByteArray; + + r.lock(); + try { + return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; + } finally { + r.unlock(); + } + } + + /** + * libsecp256k1 Create an ECDSA signature. + * + * @param data Message hash, 32 bytes + * @param key Secret key, 32 bytes + * + * Return values + * @param sig byte array of signature + */ + public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && sec.length <= 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + 32) { + byteBuff = ByteBuffer.allocateDirect(32 + 32); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(sec); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] sigArr = retByteArray[0]; + int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(sigArr.length, sigLen, "Got bad signature length."); + + return retVal == 0 ? new byte[0] : sigArr; + } + + /** + * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid + * + * @param seckey ECDSA Secret key, 32 bytes + */ + public static boolean secKeyVerify(byte[] seckey) { + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + r.lock(); + try { + return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; + } finally { + r.unlock(); + } + } + + + /** + * libsecp256k1 Compute Pubkey - computes public key from secret key + * + * @param seckey ECDSA Secret key, 32 bytes + * + * Return values + * @param pubkey ECDSA Public key, 33 or 65 bytes + */ + //TODO add a 'compressed' arg + public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + return retVal == 0 ? new byte[0]: pubArr; + } + + /** + * libsecp256k1 Cleanup - This destroys the secp256k1 context object + * This should be called at the end of the program for proper cleanup of the context. + */ + public static synchronized void cleanup() { + w.lock(); + try { + secp256k1_destroy_context(Secp256k1Context.getContext()); + } finally { + w.unlock(); + } + } + + public static long cloneContext() { + r.lock(); + try { + return secp256k1_ctx_clone(Secp256k1Context.getContext()); + } finally { r.unlock(); } + } + + /** + * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 create ECDH secret - constant time ECDH calculation + * + * @param seckey byte array of secret key used in exponentiaion + * @param pubkey byte array of public key used in exponentiaion + */ + public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { + byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + byteBuff.put(pubkey); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] resArr = retByteArray[0]; + int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + + assertEquals(resArr.length, 32, "Got bad result length."); + assertEquals(retVal, 1, "Failed return value check."); + + return resArr; + } + + /** + * libsecp256k1 randomize - updates the context randomization + * + * @param seed 32-byte random seed + */ + public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ + Preconditions.checkArgument(seed.length == 32 || seed == null); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seed.length) { + byteBuff = ByteBuffer.allocateDirect(seed.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seed); + + w.lock(); + try { + return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; + } finally { + w.unlock(); + } + } + + private static native long secp256k1_ctx_clone(long context); + + private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); + + private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); + + private static native void secp256k1_destroy_context(long context); + + private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); + + private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); + + private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); + + private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); + +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java new file mode 100644 index 000000000000..c00d08899b9b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,226 @@ +package org.bitcoin; + +import com.google.common.io.BaseEncoding; +import java.util.Arrays; +import java.math.BigInteger; +import javax.xml.bind.DatatypeConverter; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + * This class holds test cases defined for testing this library. + */ +public class NativeSecp256k1Test { + + //TODO improve comments/add more tests + /** + * This tests verify() for a valid signature + */ + public static void testVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + assertEquals( result, true , "testVerifyPos"); + } + + /** + * This tests verify() for a non-valid signature + */ + public static void testVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testVerifyNeg"); + } + + /** + * This tests secret key verify() for a valid secretkey + */ + public static void testSecKeyVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, true , "testSecKeyVerifyPos"); + } + + /** + * This tests secret key verify() for a invalid secretkey + */ + public static void testSecKeyVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testSecKeyVerifyNeg"); + } + + /** + * This tests public key create() for a valid secretkey + */ + public static void testPubKeyCreatePos() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); + } + + /** + * This tests public key create() for a invalid secretkey + */ + public static void testPubKeyCreateNeg() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); + } + + /** + * This tests sign() for a valid secretkey + */ + public static void testSignPos() throws AssertFailException{ + + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); + } + + /** + * This tests sign() for a invalid secretkey + */ + public static void testSignNeg() throws AssertFailException{ + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "" , "testSignNeg"); + } + + /** + * This tests private key tweak-add + */ + public static void testPrivKeyTweakAdd_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); + } + + /** + * This tests private key tweak-mul + */ + public static void testPrivKeyTweakMul_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); + } + + /** + * This tests private key tweak-add uncompressed + */ + public static void testPrivKeyTweakAdd_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); + } + + /** + * This tests private key tweak-mul uncompressed + */ + public static void testPrivKeyTweakMul_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); + } + + /** + * This tests seed randomization + */ + public static void testRandomize() throws AssertFailException { + byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random" + boolean result = NativeSecp256k1.randomize(seed); + assertEquals( result, true, "testRandomize"); + } + + public static void testCreateECDHSecret() throws AssertFailException{ + + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); + String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); + } + + public static void main(String[] args) throws AssertFailException{ + + + System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n"); + + assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" ); + + //Test verify() success/fail + testVerifyPos(); + testVerifyNeg(); + + //Test secKeyVerify() success/fail + testSecKeyVerifyPos(); + testSecKeyVerifyNeg(); + + //Test computePubkey() success/fail + testPubKeyCreatePos(); + testPubKeyCreateNeg(); + + //Test sign() success/fail + testSignPos(); + testSignNeg(); + + //Test privKeyTweakAdd() 1 + testPrivKeyTweakAdd_1(); + + //Test privKeyTweakMul() 2 + testPrivKeyTweakMul_1(); + + //Test privKeyTweakAdd() 3 + testPrivKeyTweakAdd_2(); + + //Test privKeyTweakMul() 4 + testPrivKeyTweakMul_2(); + + //Test randomize() + testRandomize(); + + //Test ECDH + testCreateECDHSecret(); + + NativeSecp256k1.cleanup(); + + System.out.println(" All tests passed." ); + + } +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java new file mode 100644 index 000000000000..04732ba04436 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +public class NativeSecp256k1Util{ + + public static void assertEquals( int val, int val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + } + + public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static void assertEquals( String val, String val2, String message ) throws AssertFailException{ + if( !val.equals(val2) ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static class AssertFailException extends Exception { + public AssertFailException(String message) { + super( message ); + } + } +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java new file mode 100644 index 000000000000..216c986a8b56 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +/** + * This class holds the context reference used in native methods + * to handle ECDSA operations. + */ +public class Secp256k1Context { + private static final boolean enabled; //true if the library is loaded + private static final long context; //ref to pointer to context obj + + static { //static initializer + boolean isEnabled = true; + long contextRef = -1; + try { + System.loadLibrary("secp256k1"); + contextRef = secp256k1_init_context(); + } catch (UnsatisfiedLinkError e) { + System.out.println("UnsatisfiedLinkError: " + e.toString()); + isEnabled = false; + } + enabled = isEnabled; + context = contextRef; + } + + public static boolean isEnabled() { + return enabled; + } + + public static long getContext() { + if(!enabled) return -1; //sanity check + return context; + } + + private static native long secp256k1_init_context(); +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c new file mode 100644 index 000000000000..bcef7b32ce3e --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include "org_bitcoin_NativeSecp256k1.h" +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" + + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); + + (void)classObject;(void)env; + + return ctx_clone_l; + +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_context_randomize(ctx, seed); + +} + +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + secp256k1_context_destroy(ctx); + + (void)classObject;(void)env; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* sigdata = { (unsigned char*) (data + 32) }; + const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; + + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); + + if( ret ) { + ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if( ret ) { + ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); + } + } + + (void)classObject; + + return ret; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + unsigned char* secKey = (unsigned char*) (data + 32); + + jobjectArray retArray; + jbyteArray sigArray, intsByteArray; + unsigned char intsarray[2]; + + secp256k1_ecdsa_signature sig[72]; + + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + + unsigned char outputSer[72]; + size_t outputLen = 72; + + if( ret ) { + int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + sigArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_ec_seckey_verify(ctx, secKey); +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + secp256k1_pubkey pubkey; + + jobjectArray retArray; + jbyteArray pubkeyArray, intsByteArray; + unsigned char intsarray[2]; + + int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); + + unsigned char outputSer[65]; + size_t outputLen = 65; + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubkeyArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; + +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; +/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if( ret ) { + ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if ( ret ) { + ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine + (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys) +{ + (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys; + + return 0; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* pubdata = (const unsigned char*) (secdata + 32); + + jobjectArray retArray; + jbyteArray outArray, intsByteArray; + unsigned char intsarray[1]; + secp256k1_pubkey pubkey; + unsigned char nonce_res[32]; + size_t outputLen = 32; + + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if (ret) { + ret = secp256k1_ecdh( + ctx, + nonce_res, + &pubkey, + secdata + ); + } + + intsarray[0] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + outArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); + (*env)->SetObjectArrayElement(env, retArray, 0, outArray); + + intsByteArray = (*env)->NewByteArray(env, 1); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h new file mode 100644 index 000000000000..fe613c9e9e77 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -0,0 +1,119 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_NativeSecp256k1 */ + +#ifndef _Included_org_bitcoin_NativeSecp256k1 +#define _Included_org_bitcoin_NativeSecp256k1 +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ctx_clone + * Signature: (J)J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_context_randomize + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_destroy_context + * Signature: (J)V + */ +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_verify + * Signature: (Ljava/nio/ByteBuffer;JII)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv *, jclass, jobject, jlong, jint, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_sign + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_seckey_verify + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_create + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_parse + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdh + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 000000000000..a52939e7e7da --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#include +#include +#include "org_bitcoin_Secp256k1Context.h" +#include "include/secp256k1.h" + +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv* env, jclass classObject) +{ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + (void)classObject;(void)env; + + return (uintptr_t)ctx; +} + diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h new file mode 100644 index 000000000000..0d2bc84b7f3f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_Secp256k1Context */ + +#ifndef _Included_org_bitcoin_Secp256k1Context +#define _Included_org_bitcoin_Secp256k1Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_Secp256k1Context + * Method: secp256k1_init_context + * Signature: ()J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include new file mode 100644 index 000000000000..e3088b469790 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_ecdh.h +noinst_HEADERS += src/modules/ecdh/main_impl.h +noinst_HEADERS += src/modules/ecdh/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_ecdh +bench_ecdh_SOURCES = src/bench_ecdh.c +bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h new file mode 100644 index 000000000000..9e30fb73dd7f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_MAIN_ +#define _SECP256K1_MODULE_ECDH_MAIN_ + +#include "include/secp256k1_ecdh.h" +#include "ecmult_const_impl.h" + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_gej res; + secp256k1_ge pt; + secp256k1_scalar s; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(result != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + secp256k1_pubkey_load(ctx, &pt, point); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + unsigned char x[32]; + unsigned char y[1]; + secp256k1_sha256_t sha; + + secp256k1_ecmult_const(&res, &pt, &s); + secp256k1_ge_set_gej(&pt, &res); + /* Compute a hash of the point in compressed form + * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not + * expect its output to be secret and has a timing sidechannel. */ + secp256k1_fe_normalize(&pt.x); + secp256k1_fe_normalize(&pt.y); + secp256k1_fe_get_b32(x, &pt.x); + y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, y, sizeof(y)); + secp256k1_sha256_write(&sha, x, sizeof(x)); + secp256k1_sha256_finalize(&sha, result); + ret = 1; + } + + secp256k1_scalar_clear(&s); + return ret; +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h new file mode 100644 index 000000000000..85a5d0a9a69e --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_TESTS_ +#define _SECP256K1_MODULE_ECDH_TESTS_ + +void test_ecdh_api(void) { + /* Setup context that just counts errors */ + secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_pubkey point; + unsigned char res[32]; + unsigned char s_one[32] = { 0 }; + int32_t ecount = 0; + s_one[31] = 1; + + secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); + + /* Check all NULLs are detected */ + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 3); + + /* Cleanup */ + secp256k1_context_destroy(tctx); +} + +void test_ecdh_generator_basepoint(void) { + unsigned char s_one[32] = { 0 }; + secp256k1_pubkey point[2]; + int i; + + s_one[31] = 1; + /* Check against pubkey creation when the basepoint is the generator */ + for (i = 0; i < 100; ++i) { + secp256k1_sha256_t sha; + unsigned char s_b32[32]; + unsigned char output_ecdh[32]; + unsigned char output_ser[32]; + unsigned char point_ser[33]; + size_t point_ser_len = sizeof(point_ser); + secp256k1_scalar s; + + random_scalar_order(&s); + secp256k1_scalar_get_b32(s_b32, &s); + + /* compute using ECDH function */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); + CHECK(point_ser_len == sizeof(point_ser)); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, point_ser, point_ser_len); + secp256k1_sha256_finalize(&sha, output_ser); + /* compare */ + CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + } +} + +void test_bad_scalar(void) { + unsigned char s_zero[32] = { 0 }; + unsigned char s_overflow[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + unsigned char s_rand[32] = { 0 }; + unsigned char output[32]; + secp256k1_scalar rand; + secp256k1_pubkey point; + + /* Create random point */ + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s_rand, &rand); + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); + + /* Try to multiply it by bad values */ + CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + /* ...and a good one */ + s_overflow[31] -= 1; + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); +} + +void run_ecdh_tests(void) { + test_ecdh_api(); + test_ecdh_generator_basepoint(); + test_bad_scalar(); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include new file mode 100644 index 000000000000..bf23c26e71c5 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_recovery.h +noinst_HEADERS += src/modules/recovery/main_impl.h +noinst_HEADERS += src/modules/recovery/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_recover +bench_recover_SOURCES = src/bench_recover.c +bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h new file mode 100755 index 000000000000..c6fbe239813a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ +#define _SECP256K1_MODULE_RECOVERY_MAIN_ + +#include "include/secp256k1_recovery.h" + +static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } + *recid = sig->data[64]; +} + +static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } + sig->data[64] = recid; +} + +int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + ARG_CHECK(recid >= 0 && recid <= 3); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig) { + secp256k1_scalar r, s; + + (void)ctx; + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(recid != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const secp256k1_ecdsa_recoverable_signature* sigin) { + secp256k1_scalar r, s; + int recid; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, sigin); + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; +} + +static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { + unsigned char brx[32]; + secp256k1_fe fx; + secp256k1_ge x; + secp256k1_gej xj; + secp256k1_scalar rn, u1, u2; + secp256k1_gej qj; + int r; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_get_b32(brx, sigr); + r = secp256k1_fe_set_b32(&fx, brx); + (void)r; + VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ + if (recid & 2) { + if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + return 0; + } + secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); + } + if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { + return 0; + } + secp256k1_gej_set_ge(&xj, &x); + secp256k1_scalar_inverse_var(&rn, sigr); + secp256k1_scalar_mul(&u1, &rn, message); + secp256k1_scalar_negate(&u1, &u1); + secp256k1_scalar_mul(&u2, &rn, sigs); + secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); + secp256k1_ge_set_gej_var(pubkey, &qj); + return !secp256k1_gej_is_infinity(&qj); +} + +int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int recid; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!secp256k1_scalar_is_zero(&non) && !overflow) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + int recid; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature); + VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */ + secp256k1_scalar_set_b32(&m, msg32, NULL); + if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) { + secp256k1_pubkey_save(pubkey, &q); + return 1; + } else { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h new file mode 100644 index 000000000000..765c7dd81e95 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h @@ -0,0 +1,393 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ +#define _SECP256K1_MODULE_RECOVERY_TESTS_ + +static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + + /* On the first run, return 0 to force a second run */ + if (counter == 0) { + memset(nonce32, 0, 32); + return 1; + } + /* On the second run, return an overflow to force a third run */ + if (counter == 1) { + memset(nonce32, 0xff, 32); + return 1; + } + /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */ + memset(nonce32, 1, 32); + return secp256k1_rand_bits(1); +} + +void test_ecdsa_recovery_api(void) { + /* Setup contexts that just count errors */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + secp256k1_ecdsa_signature normal_sig; + secp256k1_ecdsa_recoverable_signature recsig; + unsigned char privkey[32] = { 1 }; + unsigned char message[32] = { 2 }; + int32_t ecount = 0; + int recid = 0; + unsigned char sig[74]; + unsigned char zero_privkey[32] = { 0 }; + unsigned char over_privkey[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Check bad contexts and NULLs for signing */ + ecount = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */ + secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL); + CHECK(ecount == 5); + /* These will all fail, but not in ARG_CHECK way */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0); + /* This one will succeed. */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 5); + + /* Check signing with a goofy nonce function */ + + /* Check bad contexts and NULLs for recovery */ + ecount = 0; + CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0); + CHECK(ecount == 5); + + /* Check NULLs for conversion */ + CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1); + + /* Check NULLs for de/serialization */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1); + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0); + CHECK(ecount == 7); + /* overflow in signature will fail but not affect ecount */ + memcpy(sig, over_privkey, 32); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0); + CHECK(ecount == 7); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +void test_ecdsa_recovery_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + secp256k1_ecdsa_signature signature[5]; + secp256k1_ecdsa_recoverable_signature rsignature[5]; + unsigned char sig[74]; + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + int recid = 0; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Serialize/parse compact and verify/recover. */ + extra[0] = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(memcmp(&signature[4], &signature[0], 64) == 0); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + memset(&rsignature[4], 0, sizeof(rsignature[4])); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + /* Parse compact (with recovery id) and recover. */ + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1); + CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0); + /* Serialize/destroy/parse signature and verify again. */ + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0); + /* Recover again */ + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +} + +/* Tests several edge cases. */ +void test_ecdsa_recovery_edge_cases(void) { + const unsigned char msg32[32] = { + 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', + 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', + 's', 's', 'a', 'g', 'e', '.', '.', '.' + }; + const unsigned char sig64[64] = { + /* Generated by signing the above message with nonce 'This is the nonce we will use...' + * and secret key 0 (which is not valid), resulting in recid 0. */ + 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, + 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, + 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, + 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, + 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, + 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, + 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, + 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 + }; + secp256k1_pubkey pubkey; + /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ + const unsigned char sigb64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + secp256k1_pubkey pubkeyb; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + int recid; + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 0)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 1)); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 2)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 3)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + + for (recid = 0; recid < 4; recid++) { + int i; + int recid2; + /* (4,4) encoded in DER. */ + unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; + unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; + unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; + unsigned char sigbderalt1[39] = { + 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt2[39] = { + 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + unsigned char sigbderalt3[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt4[40] = { + 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + /* (order + r,4) encoded in DER. */ + unsigned char sigbderlong[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, + 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, + 0x8C, 0xD0, 0x36, 0x41, 0x45, 0x02, 0x01, 0x04 + }; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1); + for (recid2 = 0; recid2 < 4; recid2++) { + secp256k1_pubkey pubkey2b; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1); + /* Verifying with (order + r,4) should always fail. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + /* DER parsing tests. */ + /* Zero length r/s. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); + /* Leading zeros. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); + sigbderalt3[4] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbderalt4[7] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + /* Damage signature. */ + sigbder[7]++; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbder[7]--; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, 6) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder) - 1) == 0); + for(i = 0; i < 8; i++) { + int c; + unsigned char orig = sigbder[i]; + /*Try every single-byte change.*/ + for (c = 0; c < 256; c++) { + if (c == orig ) { + continue; + } + sigbder[i] = c; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + sigbder[i] = orig; + } + } + + /* Test r/s equal to zero */ + { + /* (1,1) encoded in DER. */ + unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; + unsigned char sigc64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + secp256k1_pubkey pubkeyc; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyc, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 1); + sigcder[4] = 0; + sigc64[31] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + sigcder[4] = 1; + sigcder[7] = 0; + sigc64[31] = 1; + sigc64[63] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + } +} + +void run_recovery_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_ecdsa_recovery_api(); + } + for (i = 0; i < 64*count; i++) { + test_ecdsa_recovery_end_to_end(); + } + test_ecdsa_recovery_edge_cases(); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num.h new file mode 100644 index 000000000000..eff842200fea --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num.h @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_ +#define _SECP256K1_NUM_ + +#ifndef USE_NUM_NONE + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_NUM_GMP) +#include "num_gmp.h" +#else +#error "Please select num implementation" +#endif + +/** Copy a number. */ +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a); + +/** Convert a number's absolute value to a binary big-endian string. + * There must be enough place. */ +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a); + +/** Set a number to the value of a binary big-endian string. */ +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen); + +/** Compute a modular inverse. The input must be less than the modulus. */ +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m); + +/** Compute the jacobi symbol (a|b). b must be positive and odd. */ +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b); + +/** Compare the absolute value of two numbers. */ +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b); + +/** Test whether two number are equal (including sign). */ +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b); + +/** Add two (signed) numbers. */ +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Subtract two (signed) numbers. */ +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Multiply two (signed) numbers. */ +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1, + even if r was negative. */ +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m); + +/** Right-shift the passed number by bits. */ +static void secp256k1_num_shift(secp256k1_num *r, int bits); + +/** Check whether a number is zero. */ +static int secp256k1_num_is_zero(const secp256k1_num *a); + +/** Check whether a number is one. */ +static int secp256k1_num_is_one(const secp256k1_num *a); + +/** Check whether a number is strictly negative. */ +static int secp256k1_num_is_neg(const secp256k1_num *a); + +/** Change a number's sign. */ +static void secp256k1_num_negate(secp256k1_num *r); + +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h new file mode 100644 index 000000000000..7dd813088afc --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_ +#define _SECP256K1_NUM_REPR_ + +#include + +#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS) + +typedef struct { + mp_limb_t data[2*NUM_LIMBS]; + int neg; + int limbs; +} secp256k1_num; + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h new file mode 100644 index 000000000000..3a46495eeac7 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h @@ -0,0 +1,288 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_IMPL_H_ +#define _SECP256K1_NUM_REPR_IMPL_H_ + +#include +#include +#include + +#include "util.h" +#include "num.h" + +#ifdef VERIFY +static void secp256k1_num_sanity(const secp256k1_num *a) { + VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0)); +} +#else +#define secp256k1_num_sanity(a) do { } while(0) +#endif + +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) { + *r = *a; +} + +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) { + unsigned char tmp[65]; + int len = 0; + int shift = 0; + if (a->limbs>1 || a->data[0] != 0) { + len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs); + } + while (shift < len && tmp[shift] == 0) shift++; + VERIFY_CHECK(len-shift <= (int)rlen); + memset(r, 0, rlen - len + shift); + if (len > shift) { + memcpy(r + rlen - len + shift, tmp + shift, len - shift); + } + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) { + int len; + VERIFY_CHECK(alen > 0); + VERIFY_CHECK(alen <= 64); + len = mpn_set_str(r->data, a, alen, 256); + if (len == 0) { + r->data[0] = 0; + len = 1; + } + VERIFY_CHECK(len <= NUM_LIMBS*2); + r->limbs = len; + r->neg = 0; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs); + r->limbs = a->limbs; + if (c != 0) { + VERIFY_CHECK(r->limbs < 2*NUM_LIMBS); + r->data[r->limbs++] = c; + } +} + +static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs); + (void)c; + VERIFY_CHECK(c == 0); + r->limbs = a->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) { + secp256k1_num_sanity(r); + secp256k1_num_sanity(m); + + if (r->limbs >= m->limbs) { + mp_limb_t t[2*NUM_LIMBS]; + mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs); + memset(t, 0, sizeof(t)); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } + + if (r->neg && (r->limbs > 1 || r->data[0] != 0)) { + secp256k1_num_sub_abs(r, m, r); + r->neg = 0; + } +} + +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) { + int i; + mp_limb_t g[NUM_LIMBS+1]; + mp_limb_t u[NUM_LIMBS+1]; + mp_limb_t v[NUM_LIMBS+1]; + mp_size_t sn; + mp_size_t gn; + secp256k1_num_sanity(a); + secp256k1_num_sanity(m); + + /** mpn_gcdext computes: (G,S) = gcdext(U,V), where + * * G = gcd(U,V) + * * G = U*S + V*T + * * U has equal or more limbs than V, and V has no padding + * If we set U to be (a padded version of) a, and V = m: + * G = a*S + m*T + * G = a*S mod m + * Assuming G=1: + * S = 1/a mod m + */ + VERIFY_CHECK(m->limbs <= NUM_LIMBS); + VERIFY_CHECK(m->data[m->limbs-1] != 0); + for (i = 0; i < m->limbs; i++) { + u[i] = (i < a->limbs) ? a->data[i] : 0; + v[i] = m->data[i]; + } + sn = NUM_LIMBS+1; + gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs); + (void)gn; + VERIFY_CHECK(gn == 1); + VERIFY_CHECK(g[0] == 1); + r->neg = a->neg ^ m->neg; + if (sn < 0) { + mpn_sub(r->data, m->data, m->limbs, r->data, -sn); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } else { + r->limbs = sn; + } + memset(g, 0, sizeof(g)); + memset(u, 0, sizeof(u)); + memset(v, 0, sizeof(v)); +} + +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) { + int ret; + mpz_t ga, gb; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1)); + + mpz_inits(ga, gb, NULL); + + mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data); + mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data); + if (a->neg) { + mpz_neg(ga, ga); + } + + ret = mpz_jacobi(ga, gb); + + mpz_clears(ga, gb, NULL); + + return ret; +} + +static int secp256k1_num_is_one(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 1); +} + +static int secp256k1_num_is_zero(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 0); +} + +static int secp256k1_num_is_neg(const secp256k1_num *a) { + return (a->limbs > 1 || a->data[0] != 0) && a->neg; +} + +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 1; + } + if (a->limbs < b->limbs) { + return -1; + } + return mpn_cmp(a->data, b->data, a->limbs); +} + +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 0; + } + if (a->limbs < b->limbs) { + return 0; + } + if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) { + return 0; + } + return mpn_cmp(a->data, b->data, a->limbs) == 0; +} + +static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) { + if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */ + r->neg = a->neg; + if (a->limbs >= b->limbs) { + secp256k1_num_add_abs(r, a, b); + } else { + secp256k1_num_add_abs(r, b, a); + } + } else { + if (secp256k1_num_cmp(a, b) > 0) { + r->neg = a->neg; + secp256k1_num_sub_abs(r, a, b); + } else { + r->neg = b->neg ^ bneg; + secp256k1_num_sub_abs(r, b, a); + } + } +} + +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 0); +} + +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 1); +} + +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t tmp[2*NUM_LIMBS+1]; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + + VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1); + if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) { + r->limbs = 1; + r->neg = 0; + r->data[0] = 0; + return; + } + if (a->limbs >= b->limbs) { + mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs); + } else { + mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs); + } + r->limbs = a->limbs + b->limbs; + if (r->limbs > 1 && tmp[r->limbs - 1]==0) { + r->limbs--; + } + VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS); + mpn_copyi(r->data, tmp, r->limbs); + r->neg = a->neg ^ b->neg; + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_shift(secp256k1_num *r, int bits) { + if (bits % GMP_NUMB_BITS) { + /* Shift within limbs. */ + mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS); + } + if (bits >= GMP_NUMB_BITS) { + int i; + /* Shift full limbs. */ + for (i = 0; i < r->limbs; i++) { + int index = i + (bits / GMP_NUMB_BITS); + if (index < r->limbs && index < 2*NUM_LIMBS) { + r->data[i] = r->data[index]; + } else { + r->data[i] = 0; + } + } + } + while (r->limbs>1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_negate(secp256k1_num *r) { + r->neg ^= 1; +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h new file mode 100644 index 000000000000..0b0e3a072a15 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_IMPL_H_ +#define _SECP256K1_NUM_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "num.h" + +#if defined(USE_NUM_GMP) +#include "num_gmp_impl.h" +#elif defined(USE_NUM_NONE) +/* Nothing. */ +#else +#error "Please select num implementation" +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h new file mode 100644 index 000000000000..27e9d8375e8b --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h @@ -0,0 +1,106 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_ +#define _SECP256K1_SCALAR_ + +#include "num.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32.h" +#else +#error "Please select scalar implementation" +#endif + +/** Clear a scalar to prevent the leak of sensitive data. */ +static void secp256k1_scalar_clear(secp256k1_scalar *r); + +/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ +static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Access bits from a scalar. Not constant time. */ +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Set a scalar from a big endian byte array. */ +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); + +/** Set a scalar to an unsigned integer. */ +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); + +/** Convert a scalar to a byte array. */ +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); + +/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); + +/** Multiply two scalars (modulo the group order). */ +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Shift a scalar right by some amount strictly between 0 and 16, returning + * the low bits that were shifted off */ +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); + +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order). */ +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the complement of a scalar (modulo the group order). */ +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Check whether a scalar equals zero. */ +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); + +/** Check whether a scalar equals one. */ +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); + +/** Check whether a scalar is higher than the group order divided by 2. */ +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * Returns -1 if the number was negated, 1 otherwise */ +static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); + +#ifndef USE_NUM_NONE +/** Convert a scalar to a number. */ +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a); + +/** Get the order of the group as a number. */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r); +#endif + +/** Compare two scalars. */ +static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); + +#ifdef USE_ENDOMORPHISM +/** Find r1 and r2 such that r1+r2*2^128 = a. */ +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +#endif + +/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ +static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h new file mode 100644 index 000000000000..cff406038fbe --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint64_t d[4]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h new file mode 100644 index 000000000000..56e7bd82afd6 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h @@ -0,0 +1,949 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) +#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) +#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) +#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) +#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) +#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) +#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); + return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 6) + 1 < 4); + return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ + no |= (a->d[2] < SECP256K1_N_2); + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1); + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { + uint128_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint64_t)r->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint128_t t = (uint128_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint128_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 64) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; + r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; + r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; + r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; + bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; + bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; + bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); + uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_H_3); + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint64_t mask = !flag - 1; + uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; + uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; + return 2 * (mask == 0) - 1; +} + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +#ifdef USE_ASM_X86_64 + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; + uint64_t c; + + __asm__ __volatile__( + /* Preload. */ + "movq 32(%%rsi), %%r11\n" + "movq 40(%%rsi), %%r12\n" + "movq 48(%%rsi), %%r13\n" + "movq 56(%%rsi), %%r14\n" + /* Initialize r8,r9,r10 */ + "movq 0(%%rsi), %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += n0 * c0 */ + "movq %8, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract m0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += l1 */ + "addq 8(%%rsi), %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += n1 * c0 */ + "movq %8, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n0 * c1 */ + "movq %9, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract m1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += l2 */ + "addq 16(%%rsi), %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n2 * c0 */ + "movq %8, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n1 * c1 */ + "movq %9, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n0 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract m2 */ + "movq %%r10, %q2\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += l3 */ + "addq 24(%%rsi), %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n3 * c0 */ + "movq %8, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n2 * c1 */ + "movq %9, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n1 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* extract m3 */ + "movq %%r8, %q3\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += n3 * c1 */ + "movq %9, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n2 */ + "addq %%r13, %%r9\n" + "adcq $0, %%r10\n" + "adcq $0, %%r8\n" + /* extract m4 */ + "movq %%r9, %q4\n" + /* (r10,r8) += n3 */ + "addq %%r14, %%r10\n" + "adcq $0, %%r8\n" + /* extract m5 */ + "movq %%r10, %q5\n" + /* extract m6 */ + "movq %%r8, %q6\n" + : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + + /* Reduce 385 bits into 258. */ + __asm__ __volatile__( + /* Preload */ + "movq %q9, %%r11\n" + "movq %q10, %%r12\n" + "movq %q11, %%r13\n" + /* Initialize (r8,r9,r10) */ + "movq %q5, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += m4 * c0 */ + "movq %12, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract p0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += m1 */ + "addq %q6, %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += m5 * c0 */ + "movq %12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += m4 * c1 */ + "movq %13, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract p1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += m2 */ + "addq %q7, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m6 * c0 */ + "movq %12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m5 * c1 */ + "movq %13, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m4 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract p2 */ + "movq %%r10, %q2\n" + /* (r8,r9) += m3 */ + "addq %q8, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += m6 * c1 */ + "movq %13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* (r8,r9) += m5 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + /* extract p3 */ + "movq %%r8, %q3\n" + /* (r9) += m6 */ + "addq %%r13, %%r9\n" + /* extract p4 */ + "movq %%r9, %q4\n" + : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) + : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + + /* Reduce 258 bits into 256. */ + __asm__ __volatile__( + /* Preload */ + "movq %q5, %%r10\n" + /* (rax,rdx) = p4 * c0 */ + "movq %7, %%rax\n" + "mulq %%r10\n" + /* (rax,rdx) += p0 */ + "addq %q1, %%rax\n" + "adcq $0, %%rdx\n" + /* extract r0 */ + "movq %%rax, 0(%q6)\n" + /* Move to (r8,r9) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p1 */ + "addq %q2, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += p4 * c1 */ + "movq %8, %%rax\n" + "mulq %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* Extract r1 */ + "movq %%r8, 8(%q6)\n" + "xorq %%r8, %%r8\n" + /* (r9,r8) += p4 */ + "addq %%r10, %%r9\n" + "adcq $0, %%r8\n" + /* (r9,r8) += p2 */ + "addq %q3, %%r9\n" + "adcq $0, %%r8\n" + /* Extract r2 */ + "movq %%r9, 16(%q6)\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p3 */ + "addq %q4, %%r8\n" + "adcq $0, %%r9\n" + /* Extract r3 */ + "movq %%r8, 24(%q6)\n" + /* Extract c */ + "movq %%r9, %q0\n" + : "=g"(c) + : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); +#else + uint128_t c; + uint64_t c0, c1, c2; + uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; + uint64_t m0, m1, m2, m3, m4, m5; + uint32_t m6; + uint64_t p0, p1, p2, p3; + uint32_t p4; + + /* Reduce 512 bits into 385. */ + /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + sumadd(n0); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + sumadd(n1); + extract(m3); + muladd(n3, SECP256K1_N_C_1); + sumadd(n2); + extract(m4); + sumadd_fast(n3); + extract_fast(m5); + VERIFY_CHECK(c0 <= 1); + m6 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m4, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m5, SECP256K1_N_C_0); + muladd(m4, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m6, SECP256K1_N_C_0); + muladd(m5, SECP256K1_N_C_1); + sumadd(m4); + extract(p2); + sumadd_fast(m3); + muladd_fast(m6, SECP256K1_N_C_1); + sumadd_fast(m5); + extract_fast(p3); + p4 = c0 + m6; + VERIFY_CHECK(p4 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ + c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; + r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; + r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p2 + (uint128_t)p4; + r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p3; + r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; +#endif + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { +#ifdef USE_ASM_X86_64 + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r15\n" + "movq 8(%%rdi), %%rbx\n" + "movq 16(%%rdi), %%rcx\n" + "movq 0(%%rdx), %%r11\n" + "movq 8(%%rdx), %%r12\n" + "movq 16(%%rdx), %%r13\n" + "movq 24(%%rdx), %%r14\n" + /* (rax,rdx) = a0 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a0 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a1 * b0 */ + "movq %%rbx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a0 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * b1 */ + "movq %%rbx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a2 * b0 */ + "movq %%rcx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += a0 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Preload a3 */ + "movq 24(%%rdi), %%r15\n" + /* (r10,r8,r9) += a1 * b2 */ + "movq %%rbx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a2 * b1 */ + "movq %%rcx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a3 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a1 * b3 */ + "movq %%rbx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * b2 */ + "movq %%rcx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a3 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a2 * b3 */ + "movq %%rcx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a3 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : "+d"(pb) + : "S"(l), "D"(a->d) + : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + extract(l[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + extract(l[5]); + muladd_fast(a->d[3], b->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint64_t l[8]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); + r->d[3] = (r->d[3] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = 0; + r1->d[3] = 0; + r2->d[0] = a->d[2]; + r2->d[1] = a->d[3]; + r2->d[2] = 0; + r2->d[3] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint64_t l[8]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 6; + shiftlow = shift & 0x3F; + shifthigh = 64 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h new file mode 100644 index 000000000000..1319664f6549 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint32_t d[8]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h new file mode 100644 index 000000000000..aae4f35c0856 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h @@ -0,0 +1,721 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) +#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) +#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) +#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) +#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) +#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (~SECP256K1_N_2) +#define SECP256K1_N_C_3 (~SECP256K1_N_3) +#define SECP256K1_N_C_4 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) +#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) +#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) +#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) +#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); + return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 5) + 1 < 8); + return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ + no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_4); + yes |= (a->d[4] > SECP256K1_N_4) & ~no; + no |= (a->d[3] < SECP256K1_N_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_3) & ~no; + no |= (a->d[2] < SECP256K1_N_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { + uint64_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; + r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; + r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[5]; + r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[6]; + r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[7]; + r->d[7] = t & 0xFFFFFFFFUL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint64_t t = (uint64_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[4] + b->d[4]; + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[5] + b->d[5]; + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[6] + b->d[6]; + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[7] + b->d[7]; + r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint64_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); + r->d[7] = t & 0xFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 32) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; + r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; + r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; + r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; + r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; + r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; + r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; + r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; + bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; + bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; + bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; + bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; + bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; + bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; + bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); + uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; + r->d[7] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_H_7); + yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; + no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ + no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint32_t mask = !flag - 1; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { + uint64_t c; + uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; + uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; + uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; + + /* 96 bit accumulator. */ + uint32_t c0, c1, c2; + + /* Reduce 512 bits into 385. */ + /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + muladd(n0, SECP256K1_N_C_2); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + muladd(n1, SECP256K1_N_C_2); + muladd(n0, SECP256K1_N_C_3); + extract(m3); + sumadd(l[4]); + muladd(n4, SECP256K1_N_C_0); + muladd(n3, SECP256K1_N_C_1); + muladd(n2, SECP256K1_N_C_2); + muladd(n1, SECP256K1_N_C_3); + sumadd(n0); + extract(m4); + sumadd(l[5]); + muladd(n5, SECP256K1_N_C_0); + muladd(n4, SECP256K1_N_C_1); + muladd(n3, SECP256K1_N_C_2); + muladd(n2, SECP256K1_N_C_3); + sumadd(n1); + extract(m5); + sumadd(l[6]); + muladd(n6, SECP256K1_N_C_0); + muladd(n5, SECP256K1_N_C_1); + muladd(n4, SECP256K1_N_C_2); + muladd(n3, SECP256K1_N_C_3); + sumadd(n2); + extract(m6); + sumadd(l[7]); + muladd(n7, SECP256K1_N_C_0); + muladd(n6, SECP256K1_N_C_1); + muladd(n5, SECP256K1_N_C_2); + muladd(n4, SECP256K1_N_C_3); + sumadd(n3); + extract(m7); + muladd(n7, SECP256K1_N_C_1); + muladd(n6, SECP256K1_N_C_2); + muladd(n5, SECP256K1_N_C_3); + sumadd(n4); + extract(m8); + muladd(n7, SECP256K1_N_C_2); + muladd(n6, SECP256K1_N_C_3); + sumadd(n5); + extract(m9); + muladd(n7, SECP256K1_N_C_3); + sumadd(n6); + extract(m10); + sumadd_fast(n7); + extract_fast(m11); + VERIFY_CHECK(c0 <= 1); + m12 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m8, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m9, SECP256K1_N_C_0); + muladd(m8, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m10, SECP256K1_N_C_0); + muladd(m9, SECP256K1_N_C_1); + muladd(m8, SECP256K1_N_C_2); + extract(p2); + sumadd(m3); + muladd(m11, SECP256K1_N_C_0); + muladd(m10, SECP256K1_N_C_1); + muladd(m9, SECP256K1_N_C_2); + muladd(m8, SECP256K1_N_C_3); + extract(p3); + sumadd(m4); + muladd(m12, SECP256K1_N_C_0); + muladd(m11, SECP256K1_N_C_1); + muladd(m10, SECP256K1_N_C_2); + muladd(m9, SECP256K1_N_C_3); + sumadd(m8); + extract(p4); + sumadd(m5); + muladd(m12, SECP256K1_N_C_1); + muladd(m11, SECP256K1_N_C_2); + muladd(m10, SECP256K1_N_C_3); + sumadd(m9); + extract(p5); + sumadd(m6); + muladd(m12, SECP256K1_N_C_2); + muladd(m11, SECP256K1_N_C_3); + sumadd(m10); + extract(p6); + sumadd_fast(m7); + muladd_fast(m12, SECP256K1_N_C_3); + sumadd_fast(m11); + extract_fast(p7); + p8 = c0 + m12; + VERIFY_CHECK(p8 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ + c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; + r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; + c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; + r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; + c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; + r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; + c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; + r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; + c += p4 + (uint64_t)p8; + r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; + c += p5; + r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; + c += p6; + r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; + c += p7; + r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7] * b[0..7]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[0], b->d[4]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + muladd(a->d[4], b->d[0]); + extract(l[4]); + muladd(a->d[0], b->d[5]); + muladd(a->d[1], b->d[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + muladd(a->d[4], b->d[1]); + muladd(a->d[5], b->d[0]); + extract(l[5]); + muladd(a->d[0], b->d[6]); + muladd(a->d[1], b->d[5]); + muladd(a->d[2], b->d[4]); + muladd(a->d[3], b->d[3]); + muladd(a->d[4], b->d[2]); + muladd(a->d[5], b->d[1]); + muladd(a->d[6], b->d[0]); + extract(l[6]); + muladd(a->d[0], b->d[7]); + muladd(a->d[1], b->d[6]); + muladd(a->d[2], b->d[5]); + muladd(a->d[3], b->d[4]); + muladd(a->d[4], b->d[3]); + muladd(a->d[5], b->d[2]); + muladd(a->d[6], b->d[1]); + muladd(a->d[7], b->d[0]); + extract(l[7]); + muladd(a->d[1], b->d[7]); + muladd(a->d[2], b->d[6]); + muladd(a->d[3], b->d[5]); + muladd(a->d[4], b->d[4]); + muladd(a->d[5], b->d[3]); + muladd(a->d[6], b->d[2]); + muladd(a->d[7], b->d[1]); + extract(l[8]); + muladd(a->d[2], b->d[7]); + muladd(a->d[3], b->d[6]); + muladd(a->d[4], b->d[5]); + muladd(a->d[5], b->d[4]); + muladd(a->d[6], b->d[3]); + muladd(a->d[7], b->d[2]); + extract(l[9]); + muladd(a->d[3], b->d[7]); + muladd(a->d[4], b->d[6]); + muladd(a->d[5], b->d[5]); + muladd(a->d[6], b->d[4]); + muladd(a->d[7], b->d[3]); + extract(l[10]); + muladd(a->d[4], b->d[7]); + muladd(a->d[5], b->d[6]); + muladd(a->d[6], b->d[5]); + muladd(a->d[7], b->d[4]); + extract(l[11]); + muladd(a->d[5], b->d[7]); + muladd(a->d[6], b->d[6]); + muladd(a->d[7], b->d[5]); + extract(l[12]); + muladd(a->d[6], b->d[7]); + muladd(a->d[7], b->d[6]); + extract(l[13]); + muladd_fast(a->d[7], b->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint32_t l[16]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); + r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); + r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); + r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); + r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); + r->d[7] = (r->d[7] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = a->d[2]; + r1->d[3] = a->d[3]; + r1->d[4] = 0; + r1->d[5] = 0; + r1->d[6] = 0; + r1->d[7] = 0; + r2->d[0] = a->d[4]; + r2->d[1] = a->d[5]; + r2->d[2] = a->d[6]; + r2->d[3] = a->d[7]; + r2->d[4] = 0; + r2->d[5] = 0; + r2->d[6] = 0; + r2->d[7] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint32_t l[16]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 5; + shiftlow = shift & 0x1F; + shifthigh = 32 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h new file mode 100644 index 000000000000..f5b2376407bd --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h @@ -0,0 +1,370 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_IMPL_H_ +#define _SECP256K1_SCALAR_IMPL_H_ + +#include "group.h" +#include "scalar.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64_impl.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32_impl.h" +#else +#error "Please select scalar implementation" +#endif + +#ifndef USE_NUM_NONE +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) { + unsigned char c[32]; + secp256k1_scalar_get_b32(c, a); + secp256k1_num_set_bin(r, c, 32); +} + +/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r) { +#if defined(EXHAUSTIVE_TEST_ORDER) + static const unsigned char order[32] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER + }; +#else + static const unsigned char order[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; +#endif + secp256k1_num_set_bin(r, order, 32); +} +#endif + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(EXHAUSTIVE_TEST_ORDER) + int i; + *r = 0; + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) + if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) + *r = i; + /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus + * have a composite group order; fix it in exhaustive_tests.c). */ + VERIFY_CHECK(*r != 0); +} +#else + secp256k1_scalar *t; + int i; + /* First compute x ^ (2^N - 1) for some values of N. */ + secp256k1_scalar x2, x3, x4, x6, x7, x8, x15, x30, x60, x120, x127; + + secp256k1_scalar_sqr(&x2, x); + secp256k1_scalar_mul(&x2, &x2, x); + + secp256k1_scalar_sqr(&x3, &x2); + secp256k1_scalar_mul(&x3, &x3, x); + + secp256k1_scalar_sqr(&x4, &x3); + secp256k1_scalar_mul(&x4, &x4, x); + + secp256k1_scalar_sqr(&x6, &x4); + secp256k1_scalar_sqr(&x6, &x6); + secp256k1_scalar_mul(&x6, &x6, &x2); + + secp256k1_scalar_sqr(&x7, &x6); + secp256k1_scalar_mul(&x7, &x7, x); + + secp256k1_scalar_sqr(&x8, &x7); + secp256k1_scalar_mul(&x8, &x8, x); + + secp256k1_scalar_sqr(&x15, &x8); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x15, &x15); + } + secp256k1_scalar_mul(&x15, &x15, &x7); + + secp256k1_scalar_sqr(&x30, &x15); + for (i = 0; i < 14; i++) { + secp256k1_scalar_sqr(&x30, &x30); + } + secp256k1_scalar_mul(&x30, &x30, &x15); + + secp256k1_scalar_sqr(&x60, &x30); + for (i = 0; i < 29; i++) { + secp256k1_scalar_sqr(&x60, &x60); + } + secp256k1_scalar_mul(&x60, &x60, &x30); + + secp256k1_scalar_sqr(&x120, &x60); + for (i = 0; i < 59; i++) { + secp256k1_scalar_sqr(&x120, &x120); + } + secp256k1_scalar_mul(&x120, &x120, &x60); + + secp256k1_scalar_sqr(&x127, &x120); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x127, &x127); + } + secp256k1_scalar_mul(&x127, &x127, &x7); + + /* Then accumulate the final result (t starts at x127). */ + t = &x127; + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 5; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 10; i++) { /* 0000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 9; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 6; i++) { /* 00000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(r, t, &x6); /* 111111 */ +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(USE_SCALAR_INV_BUILTIN) + secp256k1_scalar_inverse(r, x); +#elif defined(USE_SCALAR_INV_NUM) + unsigned char b[32]; + secp256k1_num n, m; + secp256k1_scalar t = *x; + secp256k1_scalar_get_b32(b, &t); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_scalar_order_get_num(&m); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + secp256k1_scalar_set_b32(r, b, NULL); + /* Verify that the inverse was computed correctly, without GMP code. */ + secp256k1_scalar_mul(&t, &t, r); + CHECK(secp256k1_scalar_is_one(&t)); +#else +#error "Please select scalar inverse implementation" +#endif +} + +#ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the + * full case we don't bother making k1 and k2 be small, we just want them to be + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER; + *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; +} +#else +/** + * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where + * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, + * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72} + * + * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm + * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 + * and k2 have a small size. + * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are: + * + * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} + * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} + * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * + * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives + * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and + * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2. + * + * g1, g2 are precomputed constants used to replace division with a rounded multiplication + * when decomposing the scalar for an endomorphism-based point multiplication. + * + * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve + * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. + * + * The derivation is described in the paper "Efficient Software Implementation of Public-Key + * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), + * Section 4.3 (here we use a somewhat higher-precision estimate): + * d = a1*b2 - b1*a2 + * g1 = round((2^272)*b2/d) + * g2 = round((2^272)*b1/d) + * + * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found + * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda'). + * + * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + */ + +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + secp256k1_scalar c1, c2; + static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST( + 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL, + 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL + ); + static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL + ); + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL + ); + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, + 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + ); + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, + 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + ); + VERIFY_CHECK(r1 != a); + VERIFY_CHECK(r2 != a); + /* these _var calls are constant time since the shift amount is constant */ + secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272); + secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272); + secp256k1_scalar_mul(&c1, &c1, &minus_b1); + secp256k1_scalar_mul(&c2, &c2, &minus_b2); + secp256k1_scalar_add(r2, &c1, &c2); + secp256k1_scalar_mul(r1, r2, &minus_lambda); + secp256k1_scalar_add(r1, r1, a); +} +#endif +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h new file mode 100644 index 000000000000..5574c44c7aeb --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h new file mode 100644 index 000000000000..4f94441f492a --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h @@ -0,0 +1,114 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(*a & 1); +} + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + if (offset < 32) + return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); + else + return 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + return secp256k1_scalar_get_bits(a, offset, count); +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + memset(bin, 0, 32); + bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, r); + return flag ? -1 : 1; +} + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = *r & ((1 << n) - 1); + *r >>= n; + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c new file mode 100755 index 000000000000..7d637bfad1c5 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c @@ -0,0 +1,559 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" + +#include "util.h" +#include "num_impl.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_gen_impl.h" +#include "ecdsa_impl.h" +#include "eckey_impl.h" +#include "hash_impl.h" + +#define ARG_CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return 0; \ + } \ +} while(0) + +static void default_illegal_callback_fn(const char* str, void* data) { + fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); + abort(); +} + +static const secp256k1_callback default_illegal_callback = { + default_illegal_callback_fn, + NULL +}; + +static void default_error_callback_fn(const char* str, void* data) { + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + + +struct secp256k1_context_struct { + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; +}; + +secp256k1_context* secp256k1_context_create(unsigned int flags) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = default_illegal_callback; + ret->error_callback = default_error_callback; + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&ret->illegal_callback, + "Invalid flags"); + free(ret); + return NULL; + } + + secp256k1_ecmult_context_init(&ret->ecmult_ctx); + secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + } + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + } + + return ret; +} + +secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = ctx->illegal_callback; + ret->error_callback = ctx->error_callback; + secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); + secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); + return ret; +} + +void secp256k1_context_destroy(secp256k1_context* ctx) { + if (ctx != NULL) { + secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); + secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + + free(ctx); + } +} + +void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_illegal_callback_fn; + } + ctx->illegal_callback.fn = fun; + ctx->illegal_callback.data = data; +} + +void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_error_callback_fn; + } + ctx->error_callback.fn = fun; + ctx->error_callback.data = data; +} + +static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { + if (sizeof(secp256k1_ge_storage) == 64) { + /* When the secp256k1_ge_storage type is exactly 64 byte, use its + * representation inside secp256k1_pubkey, as conversion is very fast. + * Note that secp256k1_pubkey_save must use the same representation. */ + secp256k1_ge_storage s; + memcpy(&s, &pubkey->data[0], 64); + secp256k1_ge_from_storage(ge, &s); + } else { + /* Otherwise, fall back to 32-byte big endian for X and Y. */ + secp256k1_fe x, y; + secp256k1_fe_set_b32(&x, pubkey->data); + secp256k1_fe_set_b32(&y, pubkey->data + 32); + secp256k1_ge_set_xy(ge, &x, &y); + } + ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); + return 1; +} + +static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { + if (sizeof(secp256k1_ge_storage) == 64) { + secp256k1_ge_storage s; + secp256k1_ge_to_storage(&s, ge); + memcpy(&pubkey->data[0], &s, 64); + } else { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(pubkey->data, &ge->x); + secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); + } +} + +int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input != NULL); + if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { + return 0; + } + secp256k1_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} + +int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { + secp256k1_ge Q; + size_t len; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + len = *outputlen; + *outputlen = 0; + ARG_CHECK(output != NULL); + memset(output, 0, len); + ARG_CHECK(pubkey != NULL); + ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); + if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + if (ret) { + *outputlen = len; + } + } + return ret; +} + +static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } +} + +static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } +} + +int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; + } else { + memset(sig, 0, sizeof(*sig)); + return 0; + } +} + +int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); +} + +int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { + secp256k1_scalar r, s; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); + ret = secp256k1_scalar_is_high(&s); + if (sigout != NULL) { + if (ret) { + secp256k1_scalar_negate(&s, &s); + } + secp256k1_ecdsa_signature_save(sigout, &r, &s); + } + + return ret; +} + +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_scalar_set_b32(&m, msg32, NULL); + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return (!secp256k1_scalar_is_high(&s) && + secp256k1_pubkey_load(ctx, &q, pubkey) && + secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); +} + +static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + unsigned char keydata[112]; + int keylen = 64; + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned int i; + /* We feed a byte array to the PRNG as input, consisting of: + * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. + * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. + * - optionally 16 extra bytes with the algorithm name. + * Because the arguments have distinct fixed lengths it is not possible for + * different argument mixtures to emulate each other and result in the same + * nonces. + */ + memcpy(keydata, key32, 32); + memcpy(keydata + 32, msg32, 32); + if (data != NULL) { + memcpy(keydata + 64, data, 32); + keylen = 96; + } + if (algo16 != NULL) { + memcpy(keydata + keylen, algo16, 16); + keylen += 16; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + memset(keydata, 0, sizeof(keydata)); + for (i = 0; i <= counter; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + return 1; +} + +const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; +const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; + +int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!overflow && !secp256k1_scalar_is_zero(&non)) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_signature_save(signature, &r, &s); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { + secp256k1_scalar sec; + int ret; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = !overflow && !secp256k1_scalar_is_zero(&sec); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); + if (ret) { + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(pubkey, &p); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar term; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + + ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&term); + return ret; +} + +int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar term; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar factor; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&factor); + return ret; +} + +int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar factor; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + return 1; +} + +int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { + size_t i; + secp256k1_gej Qj; + secp256k1_ge Q; + + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(n >= 1); + ARG_CHECK(pubnonces != NULL); + + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); + return 1; +} + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/main_impl.h" +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h new file mode 100644 index 000000000000..f8efa93c7c3f --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_H_ +#define _SECP256K1_TESTRAND_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +/* A non-cryptographic RNG used only for test infrastructure. */ + +/** Seed the pseudorandom number generator for testing. */ +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16); + +/** Generate a pseudorandom number in the range [0..2**32-1]. */ +static uint32_t secp256k1_rand32(void); + +/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or + * more. */ +static uint32_t secp256k1_rand_bits(int bits); + +/** Generate a pseudorandom number in the range [0..range-1]. */ +static uint32_t secp256k1_rand_int(uint32_t range); + +/** Generate a pseudorandom 32-byte array. */ +static void secp256k1_rand256(unsigned char *b32); + +/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ +static void secp256k1_rand256_test(unsigned char *b32); + +/** Generate pseudorandom bytes with long sequences of zero and one bits. */ +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h new file mode 100644 index 000000000000..15c7b9f12df0 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_IMPL_H_ +#define _SECP256K1_TESTRAND_IMPL_H_ + +#include +#include + +#include "testrand.h" +#include "hash.h" + +static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng; +static uint32_t secp256k1_test_rng_precomputed[8]; +static int secp256k1_test_rng_precomputed_used = 8; +static uint64_t secp256k1_test_rng_integer; +static int secp256k1_test_rng_integer_bits_left = 0; + +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { + secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); +} + +SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { + if (secp256k1_test_rng_precomputed_used == 8) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed)); + secp256k1_test_rng_precomputed_used = 0; + } + return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; +} + +static uint32_t secp256k1_rand_bits(int bits) { + uint32_t ret; + if (secp256k1_test_rng_integer_bits_left < bits) { + secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left); + secp256k1_test_rng_integer_bits_left += 32; + } + ret = secp256k1_test_rng_integer; + secp256k1_test_rng_integer >>= bits; + secp256k1_test_rng_integer_bits_left -= bits; + ret &= ((~((uint32_t)0)) >> (32 - bits)); + return ret; +} + +static uint32_t secp256k1_rand_int(uint32_t range) { + /* We want a uniform integer between 0 and range-1, inclusive. + * B is the smallest number such that range <= 2**B. + * two mechanisms implemented here: + * - generate B bits numbers until one below range is found, and return it + * - find the largest multiple M of range that is <= 2**(B+A), generate B+A + * bits numbers until one below M is found, and return it modulo range + * The second mechanism consumes A more bits of entropy in every iteration, + * but may need fewer iterations due to M being closer to 2**(B+A) then + * range is to 2**B. The array below (indexed by B) contains a 0 when the + * first mechanism is to be used, and the number A otherwise. + */ + static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; + uint32_t trange, mult; + int bits = 0; + if (range <= 1) { + return 0; + } + trange = range - 1; + while (trange > 0) { + trange >>= 1; + bits++; + } + if (addbits[bits]) { + bits = bits + addbits[bits]; + mult = ((~((uint32_t)0)) >> (32 - bits)) / range; + trange = range * mult; + } else { + trange = range; + mult = 1; + } + while(1) { + uint32_t x = secp256k1_rand_bits(bits); + if (x < trange) { + return (mult == 1) ? x : (x % range); + } + } +} + +static void secp256k1_rand256(unsigned char *b32) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); +} + +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) { + size_t bits = 0; + memset(bytes, 0, len); + while (bits < len * 8) { + int now; + uint32_t val; + now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31; + val = secp256k1_rand_bits(1); + while (now > 0 && bits < len * 8) { + bytes[bits / 8] |= val << (bits % 8); + now--; + bits++; + } + } +} + +static void secp256k1_rand256_test(unsigned char *b32) { + secp256k1_rand_bytes_test(b32, 32); +} + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c new file mode 100644 index 000000000000..9ae7d3028130 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c @@ -0,0 +1,4525 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#include "secp256k1.c" +#include "include/secp256k1.h" +#include "testrand_impl.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ecdsa.h" +#include "openssl/obj_mac.h" +#endif + +#include "contrib/lax_der_parsing.c" +#include "contrib/lax_der_privatekey_parsing.c" + +#if !defined(VG_CHECK) +# if defined(VALGRIND) +# include +# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) +# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) +# else +# define VG_UNDEF(x,y) +# define VG_CHECK(x,y) +# endif +#endif + +static int count = 64; +static secp256k1_context *ctx = NULL; + +static void counting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts. */ + int32_t *p; + (void)str; + p = data; + (*p)++; +} + +static void uncounting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts (backwards). */ + int32_t *p; + (void)str; + p = data; + (*p)--; +} + +void random_field_element_test(secp256k1_fe *fe) { + do { + unsigned char b32[32]; + secp256k1_rand256_test(b32); + if (secp256k1_fe_set_b32(fe, b32)) { + break; + } + } while(1); +} + +void random_field_element_magnitude(secp256k1_fe *fe) { + secp256k1_fe zero; + int n = secp256k1_rand_int(9); + secp256k1_fe_normalize(fe); + if (n == 0) { + return; + } + secp256k1_fe_clear(&zero); + secp256k1_fe_negate(&zero, &zero, 0); + secp256k1_fe_mul_int(&zero, n - 1); + secp256k1_fe_add(fe, &zero); + VERIFY_CHECK(fe->magnitude == n); +} + +void random_group_element_test(secp256k1_ge *ge) { + secp256k1_fe fe; + do { + random_field_element_test(&fe); + if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) { + secp256k1_fe_normalize(&ge->y); + break; + } + } while(1); +} + +void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { + secp256k1_fe z2, z3; + do { + random_field_element_test(&gej->z); + if (!secp256k1_fe_is_zero(&gej->z)) { + break; + } + } while(1); + secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + +void random_scalar_order_test(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256_test(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void run_context_tests(void) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + unsigned char ctmp[32]; + int32_t ecount; + int32_t ecount2; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar msg, key, nonce; + secp256k1_scalar sigr, sigs; + + ecount = 0; + ecount2 = 10; + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + + /*** clone and destroy all of them to make sure cloning was complete ***/ + { + secp256k1_context *ctx_tmp; + + ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); + } + + /* Verify that the error callback makes it across the clone. */ + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + /* And that it resets back to default. */ + secp256k1_context_set_error_callback(sign, NULL, NULL); + CHECK(vrfy->error_callback.fn == sign->error_callback.fn); + + /*** attempt to use them ***/ + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&both->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + + /* Verify context-type checking illegal-argument errors. */ + memset(ctmp, 1, 32); + CHECK(secp256k1_ec_pubkey_create(vrfy, &pubkey, ctmp) == 0); + CHECK(ecount == 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(sign, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ecdsa_sign(vrfy, &sig, ctmp, ctmp, NULL, NULL) == 0); + CHECK(ecount == 2); + VG_UNDEF(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_sign(sign, &sig, ctmp, ctmp, NULL, NULL) == 1); + VG_CHECK(&sig, sizeof(sig)); + CHECK(ecount2 == 10); + CHECK(secp256k1_ecdsa_verify(sign, &sig, ctmp, &pubkey) == 0); + CHECK(ecount2 == 11); + CHECK(secp256k1_ecdsa_verify(vrfy, &sig, ctmp, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_add(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 12); + CHECK(secp256k1_ec_pubkey_tweak_add(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_mul(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 13); + CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_context_randomize(sign, NULL) == 1); + CHECK(ecount2 == 13); + secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); + secp256k1_context_set_illegal_callback(sign, NULL, NULL); + + /* This shouldn't leak memory, due to already-set tests. */ + secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL); + secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL); + + /* obtain a working nonce */ + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try signing */ + CHECK(secp256k1_ecdsa_sig_sign(&sign->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + CHECK(secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try verifying */ + CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + /* Defined as no-op. */ + secp256k1_context_destroy(NULL); +} + +/***** HASH TESTS *****/ + +void run_sha256_tests(void) { + static const char *inputs[8] = { + "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "For this sample, this 63-byte string will be used as input data", + "This is exactly 64 bytes long, not counting the terminating byte" + }; + static const unsigned char outputs[8][32] = { + {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, + {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, + {0xf7, 0x84, 0x6f, 0x55, 0xcf, 0x23, 0xe1, 0x4e, 0xeb, 0xea, 0xb5, 0xb4, 0xe1, 0x55, 0x0c, 0xad, 0x5b, 0x50, 0x9e, 0x33, 0x48, 0xfb, 0xc4, 0xef, 0xa3, 0xa1, 0x41, 0x3d, 0x39, 0x3c, 0xb6, 0x50}, + {0xf3, 0x0c, 0xeb, 0x2b, 0xb2, 0x82, 0x9e, 0x79, 0xe4, 0xca, 0x97, 0x53, 0xd3, 0x5a, 0x8e, 0xcc, 0x00, 0x26, 0x2d, 0x16, 0x4c, 0xc0, 0x77, 0x08, 0x02, 0x95, 0x38, 0x1c, 0xbd, 0x64, 0x3f, 0x0d}, + {0x68, 0x19, 0xd9, 0x15, 0xc7, 0x3f, 0x4d, 0x1e, 0x77, 0xe4, 0xe1, 0xb5, 0x2d, 0x1f, 0xa0, 0xf9, 0xcf, 0x9b, 0xea, 0xea, 0xd3, 0x93, 0x9f, 0x15, 0x87, 0x4b, 0xd9, 0x88, 0xe2, 0xa2, 0x36, 0x30}, + {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}, + {0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42}, + {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8} + }; + int i; + for (i = 0; i < 8; i++) { + unsigned char out[32]; + secp256k1_sha256_t hasher; + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_hmac_sha256_tests(void) { + static const char *keys[6] = { + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + "\x4a\x65\x66\x65", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + }; + static const char *inputs[6] = { + "\x48\x69\x20\x54\x68\x65\x72\x65", + "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e" + }; + static const unsigned char outputs[6][32] = { + {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7}, + {0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43}, + {0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe}, + {0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b}, + {0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54}, + {0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2} + }; + int i; + for (i = 0; i < 6; i++) { + secp256k1_hmac_sha256_t hasher; + unsigned char out[32]; + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_rfc6979_hmac_sha256_tests(void) { + static const unsigned char key1[65] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x4b, 0xf5, 0x12, 0x2f, 0x34, 0x45, 0x54, 0xc5, 0x3b, 0xde, 0x2e, 0xbb, 0x8c, 0xd2, 0xb7, 0xe3, 0xd1, 0x60, 0x0a, 0xd6, 0x31, 0xc3, 0x85, 0xa5, 0xd7, 0xcc, 0xe2, 0x3c, 0x77, 0x85, 0x45, 0x9a, 0}; + static const unsigned char out1[3][32] = { + {0x4f, 0xe2, 0x95, 0x25, 0xb2, 0x08, 0x68, 0x09, 0x15, 0x9a, 0xcd, 0xf0, 0x50, 0x6e, 0xfb, 0x86, 0xb0, 0xec, 0x93, 0x2c, 0x7b, 0xa4, 0x42, 0x56, 0xab, 0x32, 0x1e, 0x42, 0x1e, 0x67, 0xe9, 0xfb}, + {0x2b, 0xf0, 0xff, 0xf1, 0xd3, 0xc3, 0x78, 0xa2, 0x2d, 0xc5, 0xde, 0x1d, 0x85, 0x65, 0x22, 0x32, 0x5c, 0x65, 0xb5, 0x04, 0x49, 0x1a, 0x0c, 0xbd, 0x01, 0xcb, 0x8f, 0x3a, 0xa6, 0x7f, 0xfd, 0x4a}, + {0xf5, 0x28, 0xb4, 0x10, 0xcb, 0x54, 0x1f, 0x77, 0x00, 0x0d, 0x7a, 0xfb, 0x6c, 0x5b, 0x53, 0xc5, 0xc4, 0x71, 0xea, 0xb4, 0x3e, 0x46, 0x6d, 0x9a, 0xc5, 0x19, 0x0c, 0x39, 0xc8, 0x2f, 0xd8, 0x2e} + }; + + static const unsigned char key2[64] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + static const unsigned char out2[3][32] = { + {0x9c, 0x23, 0x6c, 0x16, 0x5b, 0x82, 0xae, 0x0c, 0xd5, 0x90, 0x65, 0x9e, 0x10, 0x0b, 0x6b, 0xab, 0x30, 0x36, 0xe7, 0xba, 0x8b, 0x06, 0x74, 0x9b, 0xaf, 0x69, 0x81, 0xe1, 0x6f, 0x1a, 0x2b, 0x95}, + {0xdf, 0x47, 0x10, 0x61, 0x62, 0x5b, 0xc0, 0xea, 0x14, 0xb6, 0x82, 0xfe, 0xee, 0x2c, 0x9c, 0x02, 0xf2, 0x35, 0xda, 0x04, 0x20, 0x4c, 0x1d, 0x62, 0xa1, 0x53, 0x6c, 0x6e, 0x17, 0xae, 0xd7, 0xa9}, + {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} + }; + + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned char out[32]; + int i; + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 65); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) != 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out2[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); +} + +/***** RANDOM TESTS *****/ + +void test_rand_bits(int rand32, int bits) { + /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to + * get a false negative chance below once in a billion */ + static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; + /* We try multiplying the results with various odd numbers, which shouldn't + * influence the uniform distribution modulo a power of 2. */ + static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011}; + /* We only select up to 6 bits from the output to analyse */ + unsigned int usebits = bits > 6 ? 6 : bits; + unsigned int maxshift = bits - usebits; + /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit + number, track all observed outcomes, one per bit in a uint64_t. */ + uint64_t x[6][27] = {{0}}; + unsigned int i, shift, m; + /* Multiply the output of all rand calls with the odd number m, which + should not change the uniformity of its distribution. */ + for (i = 0; i < rounds[usebits]; i++) { + uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits)); + CHECK((((uint64_t)r) >> bits) == 0); + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + uint32_t rm = r * mults[m]; + for (shift = 0; shift <= maxshift; shift++) { + x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1))); + } + } + } + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + for (shift = 0; shift <= maxshift; shift++) { + /* Test that the lower usebits bits of x[shift] are 1 */ + CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0); + } + } +} + +/* Subrange must be a whole divisor of range, and at most 64 */ +void test_rand_int(uint32_t range, uint32_t subrange) { + /* (1-1/subrange)^rounds < 1/10^9 */ + int rounds = (subrange * 2073) / 100; + int i; + uint64_t x = 0; + CHECK((range % subrange) == 0); + for (i = 0; i < rounds; i++) { + uint32_t r = secp256k1_rand_int(range); + CHECK(r < range); + r = r % subrange; + x |= (((uint64_t)1) << r); + } + /* Test that the lower subrange bits of x are 1. */ + CHECK(((~x) << (64 - subrange)) == 0); +} + +void run_rand_bits(void) { + size_t b; + test_rand_bits(1, 32); + for (b = 1; b <= 32; b++) { + test_rand_bits(0, b); + } +} + +void run_rand_int(void) { + static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; + static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; + unsigned int m, s; + for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) { + for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) { + test_rand_int(ms[m] * ss[s], ss[s]); + } + } +} + +/***** NUM TESTS *****/ + +#ifndef USE_NUM_NONE +void random_num_negate(secp256k1_num *num) { + if (secp256k1_rand_bits(1)) { + secp256k1_num_negate(num); + } +} + +void random_num_order_test(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order_test(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void random_num_order(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void test_num_negate(void) { + secp256k1_num n1; + secp256k1_num n2; + random_num_order_test(&n1); /* n1 = R */ + random_num_negate(&n1); + secp256k1_num_copy(&n2, &n1); /* n2 = R */ + secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(!secp256k1_num_is_zero(&n1)); + secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2)); + secp256k1_num_negate(&n1); /* n1 = R */ + CHECK(secp256k1_num_eq(&n1, &n2)); +} + +void test_num_add_sub(void) { + int i; + secp256k1_scalar s; + secp256k1_num n1; + secp256k1_num n2; + secp256k1_num n1p2, n2p1, n1m2, n2m1; + random_num_order_test(&n1); /* n1 = R1 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n1); + } + random_num_order_test(&n2); /* n2 = R2 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n2); + } + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */ + secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */ + secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */ + secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */ + CHECK(secp256k1_num_eq(&n1p2, &n2p1)); + CHECK(!secp256k1_num_eq(&n1p2, &n1m2)); + secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1m2)); + CHECK(!secp256k1_num_eq(&n2m1, &n1)); + secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1)); + CHECK(!secp256k1_num_eq(&n2p1, &n1)); + secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */ + CHECK(secp256k1_num_eq(&n2p1, &n1)); + + /* check is_one */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&n1, &s); + CHECK(secp256k1_num_is_one(&n1)); + /* check that 2^n + 1 is never 1 */ + secp256k1_scalar_get_num(&n2, &s); + for (i = 0; i < 250; ++i) { + secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */ + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */ + CHECK(!secp256k1_num_is_one(&n1p2)); + } +} + +void test_num_mod(void) { + int i; + secp256k1_scalar s; + secp256k1_num order, n; + + /* check that 0 mod anything is 0 */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_set_int(&s, 0); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that anything mod 1 is 0 */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that increasing the number past 2^256 does not break this */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&n, &s); + /* multiply by 2^8, which'll test this case with high probability */ + for (i = 0; i < 8; ++i) { + secp256k1_num_add(&n, &n, &n); + } + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); +} + +void test_num_jacobi(void) { + secp256k1_scalar sqr; + secp256k1_scalar small; + secp256k1_scalar five; /* five is not a quadratic residue */ + secp256k1_num order, n; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* check some small values with 5 as the order */ + secp256k1_scalar_set_int(&five, 5); + secp256k1_scalar_get_num(&order, &five); + for (i = 0; i < 10; ++i) { + secp256k1_scalar_set_int(&small, i); + secp256k1_scalar_get_num(&n, &small); + CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]); + } + + /** test large values with 5 as group order */ + secp256k1_scalar_get_num(&order, &five); + /* we first need a scalar which is not a multiple of 5 */ + do { + secp256k1_num fiven; + random_scalar_order_test(&sqr); + secp256k1_scalar_get_num(&fiven, &five); + secp256k1_scalar_get_num(&n, &sqr); + secp256k1_num_mod(&n, &fiven); + } while (secp256k1_num_is_zero(&n)); + /* next force it to be a residue. 2 is a nonresidue mod 5 so we can + * just multiply by two, i.e. add the number to itself */ + if (secp256k1_num_jacobi(&n, &order) == -1) { + secp256k1_num_add(&n, &n, &n); + } + + /* test residue */ + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_num_add(&n, &n, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + + /** test with secp group order as order */ + secp256k1_scalar_order_get_num(&order); + random_scalar_order_test(&sqr); + secp256k1_scalar_sqr(&sqr, &sqr); + /* test residue */ + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_scalar_mul(&sqr, &sqr, &five); + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + /* test multiple of the order*/ + CHECK(secp256k1_num_jacobi(&order, &order) == 0); + + /* check one less than the order */ + secp256k1_scalar_set_int(&small, 1); + secp256k1_scalar_get_num(&n, &small); + secp256k1_num_sub(&n, &order, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */ +} + +void run_num_smalltests(void) { + int i; + for (i = 0; i < 100*count; i++) { + test_num_negate(); + test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); + } +} +#endif + +/***** SCALAR TESTS *****/ + +void scalar_test(void) { + secp256k1_scalar s; + secp256k1_scalar s1; + secp256k1_scalar s2; +#ifndef USE_NUM_NONE + secp256k1_num snum, s1num, s2num; + secp256k1_num order, half_order; +#endif + unsigned char c[32]; + + /* Set 's' to a random scalar, with value 'snum'. */ + random_scalar_order_test(&s); + + /* Set 's1' to a random scalar, with value 's1num'. */ + random_scalar_order_test(&s1); + + /* Set 's2' to a random scalar, with value 'snum2', and byte array representation 'c'. */ + random_scalar_order_test(&s2); + secp256k1_scalar_get_b32(c, &s2); + +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&snum, &s); + secp256k1_scalar_get_num(&s1num, &s1); + secp256k1_scalar_get_num(&s2num, &s2); + + secp256k1_scalar_order_get_num(&order); + half_order = order; + secp256k1_num_shift(&half_order, 1); +#endif + + { + int i; + /* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + secp256k1_scalar_set_int(&n, 0); + for (i = 0; i < 256; i += 4) { + secp256k1_scalar t; + int j; + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits(&s, 256 - 4 - i, 4)); + for (j = 0; j < 4; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + + { + /* Test that fetching groups of randomly-sized bits from a scalar and recursing n(i)=b*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + int i = 0; + secp256k1_scalar_set_int(&n, 0); + while (i < 256) { + secp256k1_scalar t; + int j; + int now = secp256k1_rand_int(15) + 1; + if (now + i > 256) { + now = 256 - i; + } + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits_var(&s, 256 - now - i, now)); + for (j = 0; j < now; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + i += now; + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + +#ifndef USE_NUM_NONE + { + /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */ + secp256k1_num rnum; + secp256k1_num r2num; + secp256k1_scalar r; + secp256k1_num_add(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_add(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + } + + { + /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */ + secp256k1_scalar r; + secp256k1_num r2num; + secp256k1_num rnum; + secp256k1_num_mul(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_mul(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + /* The result can only be zero if at least one of the factors was zero. */ + CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2))); + /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */ + CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2))); + CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s))); + } + + { + secp256k1_scalar neg; + secp256k1_num negnum; + secp256k1_num negnum2; + /* Check that comparison with zero matches comparison with zero on the number. */ + CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s)); + /* Check that comparison with the half order is equal to testing for high scalar. */ + CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0)); + secp256k1_scalar_negate(&neg, &s); + secp256k1_num_sub(&negnum, &order, &snum); + secp256k1_num_mod(&negnum, &order); + /* Check that comparison with the half order is equal to testing for high scalar after negation. */ + CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0)); + /* Negating should change the high property, unless the value was already zero. */ + CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s)); + secp256k1_scalar_get_num(&negnum2, &neg); + /* Negating a scalar should be equal to (order - n) mod order on the number. */ + CHECK(secp256k1_num_eq(&negnum, &negnum2)); + secp256k1_scalar_add(&neg, &neg, &s); + /* Adding a number to its negation should result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + secp256k1_scalar_negate(&neg, &neg); + /* Negating zero should still result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + } + + { + /* Test secp256k1_scalar_mul_shift_var. */ + secp256k1_scalar r; + secp256k1_num one; + secp256k1_num rnum; + secp256k1_num rnum2; + unsigned char cone[1] = {0x01}; + unsigned int shift = 256 + secp256k1_rand_int(257); + secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift); + secp256k1_num_mul(&rnum, &s1num, &s2num); + secp256k1_num_shift(&rnum, shift - 1); + secp256k1_num_set_bin(&one, cone, 1); + secp256k1_num_add(&rnum, &rnum, &one); + secp256k1_num_shift(&rnum, 1); + secp256k1_scalar_get_num(&rnum2, &r); + CHECK(secp256k1_num_eq(&rnum, &rnum2)); + } + + { + /* test secp256k1_scalar_shr_int */ + secp256k1_scalar r; + int i; + random_scalar_order_test(&r); + for (i = 0; i < 100; ++i) { + int low; + int shift = 1 + secp256k1_rand_int(15); + int expected = r.d[0] % (1 << shift); + low = secp256k1_scalar_shr_int(&r, shift); + CHECK(expected == low); + } + } +#endif + + { + /* Test that scalar inverses are equal to the inverse of their number modulo the order. */ + if (!secp256k1_scalar_is_zero(&s)) { + secp256k1_scalar inv; +#ifndef USE_NUM_NONE + secp256k1_num invnum; + secp256k1_num invnum2; +#endif + secp256k1_scalar_inverse(&inv, &s); +#ifndef USE_NUM_NONE + secp256k1_num_mod_inverse(&invnum, &snum, &order); + secp256k1_scalar_get_num(&invnum2, &inv); + CHECK(secp256k1_num_eq(&invnum, &invnum2)); +#endif + secp256k1_scalar_mul(&inv, &inv, &s); + /* Multiplying a scalar with its inverse must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); + secp256k1_scalar_inverse(&inv, &inv); + /* Inverting one must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&invnum, &inv); + CHECK(secp256k1_num_is_one(&invnum)); +#endif + } + } + + { + /* Test commutativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + secp256k1_scalar r1, r2; + secp256k1_scalar b; + int i; + /* Test add_bit. */ + int bit = secp256k1_rand_bits(8); + secp256k1_scalar_set_int(&b, 1); + CHECK(secp256k1_scalar_is_one(&b)); + for (i = 0; i < bit; i++) { + secp256k1_scalar_add(&b, &b, &b); + } + r1 = s1; + r2 = s1; + if (!secp256k1_scalar_add(&r1, &r1, &b)) { + /* No overflow happened. */ + secp256k1_scalar_cadd_bit(&r2, bit, 1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + /* cadd is a noop when flag is zero */ + secp256k1_scalar_cadd_bit(&r2, bit, 0); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + } + + { + /* Test commutativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r1, &r1, &s); + secp256k1_scalar_add(&r2, &s2, &s); + secp256k1_scalar_add(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s2, &s); + secp256k1_scalar_mul(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test distributitivity of mul over add. */ + secp256k1_scalar r1, r2, t; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s1, &s); + secp256k1_scalar_mul(&t, &s2, &s); + secp256k1_scalar_add(&r2, &r2, &t); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test multiplicative identity. */ + secp256k1_scalar r1, v1; + secp256k1_scalar_set_int(&v1,1); + secp256k1_scalar_mul(&r1, &s1, &v1); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test additive identity. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_add(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test zero product property. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_mul(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &v0)); + } + +} + +void run_scalar_tests(void) { + int i; + for (i = 0; i < 128 * count; i++) { + scalar_test(); + } + + { + /* (-1)+1 should be zero. */ + secp256k1_scalar s, o; + secp256k1_scalar_set_int(&s, 1); + CHECK(secp256k1_scalar_is_one(&s)); + secp256k1_scalar_negate(&o, &s); + secp256k1_scalar_add(&o, &o, &s); + CHECK(secp256k1_scalar_is_zero(&o)); + secp256k1_scalar_negate(&o, &o); + CHECK(secp256k1_scalar_is_zero(&o)); + } + +#ifndef USE_NUM_NONE + { + /* A scalar with value of the curve order should be 0. */ + secp256k1_num order; + secp256k1_scalar zero; + unsigned char bin[32]; + int overflow = 0; + secp256k1_scalar_order_get_num(&order); + secp256k1_num_get_bin(bin, 32, &order); + secp256k1_scalar_set_b32(&zero, bin, &overflow); + CHECK(overflow == 1); + CHECK(secp256k1_scalar_is_zero(&zero)); + } +#endif + + { + /* Does check_overflow check catch all ones? */ + static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + CHECK(secp256k1_scalar_check_overflow(&overflowed)); + } + + { + /* Static test vectors. + * These were reduced from ~10^12 random vectors based on comparison-decision + * and edge-case coverage on 32-bit and 64-bit implementations. + * The responses were generated with Sage 5.9. + */ + secp256k1_scalar x; + secp256k1_scalar y; + secp256k1_scalar z; + secp256k1_scalar zz; + secp256k1_scalar one; + secp256k1_scalar r1; + secp256k1_scalar r2; +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar zzv; +#endif + int overflow; + unsigned char chal[33][2][32] = { + {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}}, + {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, + 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}, + {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff}, + {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, + 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}}, + {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00}, + {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}}, + {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}}, + {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}}, + {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f, + 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}}, + {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, + 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} + }; + unsigned char res[33][2][32] = { + {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, + 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, + 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, + 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35}, + {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d, + 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c, + 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49, + 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}}, + {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22, + 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c, + 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f, + 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8}, + {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77, + 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4, + 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59, + 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}}, + {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef, + 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab, + 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55, + 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c}, + {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96, + 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f, + 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12, + 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}}, + {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c, + 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf, + 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9, + 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48}, + {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42, + 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5, + 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c, + 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}}, + {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb, + 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74, + 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6, + 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63}, + {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3, + 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99, + 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58, + 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}}, + {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b, + 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7, + 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f, + 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0}, + {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d, + 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d, + 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9, + 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}}, + {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7, + 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70, + 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06, + 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e}, + {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9, + 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79, + 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e, + 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}}, + {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb, + 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5, + 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a, + 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe}, + {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48, + 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e, + 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc, + 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}}, + {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b, + 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0, + 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53, + 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8}, + {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c, + 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01, + 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f, + 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}}, + {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7, + 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c, + 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92, + 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30}, + {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62, + 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e, + 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb, + 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}}, + {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25, + 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d, + 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0, + 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13}, + {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60, + 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00, + 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4, + 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}}, + {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31, + 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4, + 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88, + 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa}, + {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57, + 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38, + 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51, + 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}}, + {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c, + 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f, + 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2, + 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4}, + {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01, + 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4, + 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86, + 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}}, + {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5, + 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51, + 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3, + 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62}, + {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c, + 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91, + 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c, + 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}}, + {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e, + 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56, + 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58, + 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4}, + {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41, + 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7, + 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92, + 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}}, + {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec, + 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19, + 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3, + 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4}, + {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87, + 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a, + 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92, + 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}}, + {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64, + 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3, + 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f, + 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33}, + {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c, + 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d, + 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea, + 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}}, + {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7, + 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a, + 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae, + 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe}, + {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc, + 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39, + 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14, + 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}}, + {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23, + 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d, + 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2, + 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16}, + {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c, + 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84, + 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0, + 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}}, + {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb, + 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94, + 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b, + 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e}, + {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54, + 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00, + 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb, + 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0, + 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b, + 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94, + 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8}, + {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26, + 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d, + 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a, + 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39, + 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea, + 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf, + 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae}, + {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b, + 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb, + 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6, + 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}}, + {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a, + 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f, + 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9, + 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56}, + {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93, + 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07, + 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71, + 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}}, + {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87, + 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9, + 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55, + 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73}, + {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d, + 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86, + 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb, + 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}}, + {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2, + 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7, + 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41, + 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7}, + {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06, + 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04, + 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08, + 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}}, + {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2, + 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b, + 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40, + 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68}, + {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e, + 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a, + 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b, + 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}}, + {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67, + 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f, + 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a, + 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51}, + {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, + 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, + 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} + }; + secp256k1_scalar_set_int(&one, 1); + for (i = 0; i < 33; i++) { + secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r1, res[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r2, res[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_mul(&z, &x, &y); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&r1, &z)); + if (!secp256k1_scalar_is_zero(&y)) { + secp256k1_scalar_inverse(&zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar_inverse_var(&zzv, &y); + CHECK(secp256k1_scalar_eq(&zzv, &zz)); +#endif + secp256k1_scalar_mul(&z, &z, &zz); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&x, &z)); + secp256k1_scalar_mul(&zz, &zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&one, &zz)); + } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(!secp256k1_scalar_check_overflow(&z)); + secp256k1_scalar_sqr(&zz, &x); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&zz, &z)); + CHECK(secp256k1_scalar_eq(&r2, &zz)); + } + } +} + +/***** FIELD TESTS *****/ + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256_test(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_non_zero(secp256k1_fe *nz) { + int tries = 10; + while (--tries >= 0) { + random_fe(nz); + secp256k1_fe_normalize(nz); + if (!secp256k1_fe_is_zero(nz)) { + break; + } + } + /* Infinitesimal probability of spurious failure here */ + CHECK(tries >= 0); +} + +void random_fe_non_square(secp256k1_fe *ns) { + secp256k1_fe r; + random_fe_non_zero(ns); + if (secp256k1_fe_sqrt(&r, ns)) { + secp256k1_fe_negate(ns, ns, 1); + } +} + +int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe an = *a; + secp256k1_fe bn = *b; + secp256k1_fe_normalize_weak(&an); + secp256k1_fe_normalize_var(&bn); + return secp256k1_fe_equal_var(&an, &bn); +} + +int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) { + secp256k1_fe x; + secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe_mul(&x, a, ai); + return check_fe_equal(&x, &one); +} + +void run_field_convert(void) { + static const unsigned char b32[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40 + }; + static const secp256k1_fe_storage fes = SECP256K1_FE_STORAGE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + static const secp256k1_fe fe = SECP256K1_FE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + secp256k1_fe fe2; + unsigned char b322[32]; + secp256k1_fe_storage fes2; + /* Check conversions to fe. */ + CHECK(secp256k1_fe_set_b32(&fe2, b32)); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + secp256k1_fe_from_storage(&fe2, &fes); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + /* Check conversion from fe. */ + secp256k1_fe_get_b32(b322, &fe); + CHECK(memcmp(b322, b32, 32) == 0); + secp256k1_fe_to_storage(&fes2, &fe); + CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); +} + +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe t = *b; +#ifdef VERIFY + t.magnitude = a->magnitude; + t.normalized = a->normalized; +#endif + return memcmp(a, &t, sizeof(secp256k1_fe)); +} + +void run_field_misc(void) { + secp256k1_fe x; + secp256k1_fe y; + secp256k1_fe z; + secp256k1_fe q; + secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); + int i, j; + for (i = 0; i < 5*count; i++) { + secp256k1_fe_storage xs, ys, zs; + random_fe(&x); + random_fe_non_zero(&y); + /* Test the fe equality and comparison operations. */ + CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); + CHECK(secp256k1_fe_equal_var(&x, &x)); + z = x; + secp256k1_fe_add(&z,&y); + /* Test fe conditional move; z is not normalized here. */ + q = x; + secp256k1_fe_cmov(&x, &z, 0); + VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude); + secp256k1_fe_cmov(&x, &x, 1); + CHECK(fe_memcmp(&x, &z) != 0); + CHECK(fe_memcmp(&x, &q) == 0); + secp256k1_fe_cmov(&q, &z, 1); + VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(fe_memcmp(&q, &z) == 0); + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&z); + CHECK(!secp256k1_fe_equal_var(&x, &z)); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (i&1)); + VERIFY_CHECK(q.normalized && q.magnitude == 1); + for (j = 0; j < 6; j++) { + secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (j&1)); + VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); + } + secp256k1_fe_normalize_var(&z); + /* Test storage conversion and conditional moves. */ + secp256k1_fe_to_storage(&xs, &x); + secp256k1_fe_to_storage(&ys, &y); + secp256k1_fe_to_storage(&zs, &z); + secp256k1_fe_storage_cmov(&zs, &xs, 0); + secp256k1_fe_storage_cmov(&zs, &zs, 1); + CHECK(memcmp(&xs, &zs, sizeof(xs)) != 0); + secp256k1_fe_storage_cmov(&ys, &xs, 1); + CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0); + secp256k1_fe_from_storage(&x, &xs); + secp256k1_fe_from_storage(&y, &ys); + secp256k1_fe_from_storage(&z, &zs); + /* Test that mul_int, mul, and add agree. */ + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&y, &x); + z = x; + secp256k1_fe_mul_int(&z, 3); + CHECK(check_fe_equal(&y, &z)); + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&z, &x); + CHECK(check_fe_equal(&z, &y)); + z = x; + secp256k1_fe_mul_int(&z, 5); + secp256k1_fe_mul(&q, &x, &fe5); + CHECK(check_fe_equal(&z, &q)); + secp256k1_fe_negate(&x, &x, 1); + secp256k1_fe_add(&z, &x); + secp256k1_fe_add(&q, &x); + CHECK(check_fe_equal(&y, &z)); + CHECK(check_fe_equal(&q, &y)); + } +} + +void run_field_inv(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_var(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv_var(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv_var(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_all_var(void) { + secp256k1_fe x[16], xi[16], xii[16]; + int i; + /* Check it's safe to call for 0 elements */ + secp256k1_fe_inv_all_var(xi, x, 0); + for (i = 0; i < count; i++) { + size_t j; + size_t len = secp256k1_rand_int(15) + 1; + for (j = 0; j < len; j++) { + random_fe_non_zero(&x[j]); + } + secp256k1_fe_inv_all_var(xi, x, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_inverse(&x[j], &xi[j])); + } + secp256k1_fe_inv_all_var(xii, xi, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_equal(&x[j], &xii[j])); + } + } +} + +void run_sqr(void) { + secp256k1_fe x, s; + + { + int i; + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_negate(&x, &x, 1); + + for (i = 1; i <= 512; ++i) { + secp256k1_fe_mul_int(&x, 2); + secp256k1_fe_normalize(&x); + secp256k1_fe_sqr(&s, &x); + } + } +} + +void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { + secp256k1_fe r1, r2; + int v = secp256k1_fe_sqrt(&r1, a); + CHECK((v == 0) == (k == NULL)); + + if (k != NULL) { + /* Check that the returned root is +/- the given known answer */ + secp256k1_fe_negate(&r2, &r1, 1); + secp256k1_fe_add(&r1, k); secp256k1_fe_add(&r2, k); + secp256k1_fe_normalize(&r1); secp256k1_fe_normalize(&r2); + CHECK(secp256k1_fe_is_zero(&r1) || secp256k1_fe_is_zero(&r2)); + } +} + +void run_sqrt(void) { + secp256k1_fe ns, x, s, t; + int i; + + /* Check sqrt(0) is 0 */ + secp256k1_fe_set_int(&x, 0); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + + /* Check sqrt of small squares (and their negatives) */ + for (i = 1; i <= 100; i++) { + secp256k1_fe_set_int(&x, i); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + } + + /* Consistency checks for large random values */ + for (i = 0; i < 10; i++) { + int j; + random_fe_non_square(&ns); + for (j = 0; j < count; j++) { + random_fe(&x); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + secp256k1_fe_mul(&t, &s, &ns); + test_sqrt(&t, NULL); + } + } +} + +/***** GROUP TESTS *****/ + +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +/* This compares jacobian points including their Z, not just their geometric meaning. */ +int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej a2; + secp256k1_gej b2; + int ret = 1; + ret &= a->infinity == b->infinity; + if (ret && !a->infinity) { + a2 = *a; + b2 = *b; + secp256k1_fe_normalize(&a2.x); + secp256k1_fe_normalize(&a2.y); + secp256k1_fe_normalize(&a2.z); + secp256k1_fe_normalize(&b2.x); + secp256k1_fe_normalize(&b2.y); + secp256k1_fe_normalize(&b2.z); + ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0; + ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0; + ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0; + } + return ret; +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void test_ge(void) { + int i, i1; +#ifdef USE_ENDOMORPHISM + int runs = 6; +#else + int runs = 4; +#endif + /* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4). + * The second in each pair of identical points uses a random Z coordinate in the Jacobian form. + * All magnitudes are randomized. + * All 17*17 combinations of points are added to each other, using all applicable methods. + * + * When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well. + */ + secp256k1_ge *ge = (secp256k1_ge *)malloc(sizeof(secp256k1_ge) * (1 + 4 * runs)); + secp256k1_gej *gej = (secp256k1_gej *)malloc(sizeof(secp256k1_gej) * (1 + 4 * runs)); + secp256k1_fe *zinv = (secp256k1_fe *)malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + secp256k1_fe zf; + secp256k1_fe zfi2, zfi3; + + secp256k1_gej_set_infinity(&gej[0]); + secp256k1_ge_clear(&ge[0]); + secp256k1_ge_set_gej_var(&ge[0], &gej[0]); + for (i = 0; i < runs; i++) { + int j; + secp256k1_ge g; + random_group_element_test(&g); +#ifdef USE_ENDOMORPHISM + if (i >= runs - 2) { + secp256k1_ge_mul_lambda(&g, &ge[1]); + } + if (i >= runs - 1) { + secp256k1_ge_mul_lambda(&g, &g); + } +#endif + ge[1 + 4 * i] = g; + ge[2 + 4 * i] = g; + secp256k1_ge_neg(&ge[3 + 4 * i], &g); + secp256k1_ge_neg(&ge[4 + 4 * i], &g); + secp256k1_gej_set_ge(&gej[1 + 4 * i], &ge[1 + 4 * i]); + random_group_element_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); + secp256k1_gej_set_ge(&gej[3 + 4 * i], &ge[3 + 4 * i]); + random_group_element_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); + for (j = 0; j < 4; j++) { + random_field_element_magnitude(&ge[1 + j + 4 * i].x); + random_field_element_magnitude(&ge[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].x); + random_field_element_magnitude(&gej[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].z); + } + } + + /* Compute z inverses. */ + { + secp256k1_fe *zs = malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + for (i = 0; i < 4 * runs + 1; i++) { + if (i == 0) { + /* The point at infinity does not have a meaningful z inverse. Any should do. */ + do { + random_field_element_test(&zs[i]); + } while(secp256k1_fe_is_zero(&zs[i])); + } else { + zs[i] = gej[i].z; + } + } + secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1); + free(zs); + } + + /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ + do { + random_field_element_test(&zf); + } while(secp256k1_fe_is_zero(&zf)); + random_field_element_magnitude(&zf); + secp256k1_fe_inv_var(&zfi3, &zf); + secp256k1_fe_sqr(&zfi2, &zfi3); + secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { + int i2; + for (i2 = 0; i2 < 1 + 4 * runs; i2++) { + /* Compute reference result using gej + gej (var). */ + secp256k1_gej refj, resj; + secp256k1_ge ref; + secp256k1_fe zr; + secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + /* Check Z ratio. */ + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&refj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &refj.z)); + } + secp256k1_ge_set_gej_var(&ref, &refj); + + /* Test gej + ge with Z ratio result (var). */ + secp256k1_gej_add_ge_var(&resj, &gej[i1], &ge[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + ge_equals_gej(&ref, &resj); + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&resj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &resj.z)); + } + + /* Test gej + ge (var, with additional Z factor). */ + { + secp256k1_ge ge2_zfi = ge[i2]; /* the second term with x and y rescaled for z = 1/zf */ + secp256k1_fe_mul(&ge2_zfi.x, &ge2_zfi.x, &zfi2); + secp256k1_fe_mul(&ge2_zfi.y, &ge2_zfi.y, &zfi3); + random_field_element_magnitude(&ge2_zfi.x); + random_field_element_magnitude(&ge2_zfi.y); + secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); + ge_equals_gej(&ref, &resj); + } + + /* Test gej + ge (const). */ + if (i2 != 0) { + /* secp256k1_gej_add_ge does not support its second argument being infinity. */ + secp256k1_gej_add_ge(&resj, &gej[i1], &ge[i2]); + ge_equals_gej(&ref, &resj); + } + + /* Test doubling (var). */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 == ((i2 + 3)%4)/2)) { + secp256k1_fe zr2; + /* Normal doubling with Z ratio result. */ + secp256k1_gej_double_var(&resj, &gej[i1], &zr2); + ge_equals_gej(&ref, &resj); + /* Check Z ratio. */ + secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + /* Normal doubling. */ + secp256k1_gej_double_var(&resj, &gej[i2], NULL); + ge_equals_gej(&ref, &resj); + } + + /* Test adding opposites. */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 != ((i2 + 3)%4)/2)) { + CHECK(secp256k1_ge_is_infinity(&ref)); + } + + /* Test adding infinity. */ + if (i1 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i1])); + CHECK(secp256k1_gej_is_infinity(&gej[i1])); + ge_equals_gej(&ref, &gej[i2]); + } + if (i2 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i2])); + CHECK(secp256k1_gej_is_infinity(&gej[i2])); + ge_equals_gej(&ref, &gej[i1]); + } + } + } + + /* Test adding all points together in random order equals infinity. */ + { + secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; + secp256k1_gej *gej_shuffled = (secp256k1_gej *)malloc((4 * runs + 1) * sizeof(secp256k1_gej)); + for (i = 0; i < 4 * runs + 1; i++) { + gej_shuffled[i] = gej[i]; + } + for (i = 0; i < 4 * runs + 1; i++) { + int swap = i + secp256k1_rand_int(4 * runs + 1 - i); + if (swap != i) { + secp256k1_gej t = gej_shuffled[i]; + gej_shuffled[i] = gej_shuffled[swap]; + gej_shuffled[swap] = t; + } + } + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i], NULL); + } + CHECK(secp256k1_gej_is_infinity(&sum)); + free(gej_shuffled); + } + + /* Test batch gej -> ge conversion with and without known z ratios. */ + { + secp256k1_fe *zr = (secp256k1_fe *)malloc((4 * runs + 1) * sizeof(secp256k1_fe)); + secp256k1_ge *ge_set_table = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + secp256k1_ge *ge_set_all = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + for (i = 0; i < 4 * runs + 1; i++) { + /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ + if (i < 4 * runs) { + secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); + } + } + secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); + secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback); + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe s; + random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); + ge_equals_gej(&ge_set_table[i], &gej[i]); + ge_equals_gej(&ge_set_all[i], &gej[i]); + } + free(ge_set_table); + free(ge_set_all); + free(zr); + } + + free(ge); + free(gej); + free(zinv); +} + +void test_add_neg_y_diff_x(void) { + /* The point of this test is to check that we can add two points + * whose y-coordinates are negatives of each other but whose x + * coordinates differ. If the x-coordinates were the same, these + * points would be negatives of each other and their sum is + * infinity. This is cool because it "covers up" any degeneracy + * in the addition algorithm that would cause the xy coordinates + * of the sum to be wrong (since infinity has no xy coordinates). + * HOWEVER, if the x-coordinates are different, infinity is the + * wrong answer, and such degeneracies are exposed. This is the + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * which this test is a regression test for. + * + * These points were generated in sage as + * # secp256k1 params + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (7)]) + * G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) + * N = FiniteField(G.order()) + * + * # endomorphism values (lambda is 1^{1/3} in N, beta is 1^{1/3} in F) + * x = polygen(N) + * lam = (1 - x^3).roots()[1][0] + * + * # random "bad pair" + * P = C.random_element() + * Q = -int(lam) * P + * print " P: %x %x" % P.xy() + * print " Q: %x %x" % Q.xy() + * print "P + Q: %x %x" % (P + Q).xy() + */ + secp256k1_gej aj = SECP256K1_GEJ_CONST( + 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, + 0x0643d79f, 0x05a59614, 0x2f8ec030, 0xd58977cb, + 0x001e337a, 0x38093dcd, 0x6c0f386d, 0x0b1293a8, + 0x4d72c879, 0xd7681924, 0x44e6d2f3, 0x9190117d + ); + secp256k1_gej bj = SECP256K1_GEJ_CONST( + 0xc7b74206, 0x1f788cd9, 0xabd0937d, 0x164a0d86, + 0x95f6ff75, 0xf19a4ce9, 0xd013bd7b, 0xbf92d2a7, + 0xffe1cc85, 0xc7f6c232, 0x93f0c792, 0xf4ed6c57, + 0xb28d3786, 0x2897e6db, 0xbb192d0b, 0x6e6feab2 + ); + secp256k1_gej sumj = SECP256K1_GEJ_CONST( + 0x671a63c0, 0x3efdad4c, 0x389a7798, 0x24356027, + 0xb3d69010, 0x278625c3, 0x5c86d390, 0x184a8f7a, + 0x5f6409c2, 0x2ce01f2b, 0x511fd375, 0x25071d08, + 0xda651801, 0x70e95caf, 0x8f0d893c, 0xbed8fbbe + ); + secp256k1_ge b; + secp256k1_gej resj; + secp256k1_ge res; + secp256k1_ge_set_gej(&b, &bj); + + secp256k1_gej_add_var(&resj, &aj, &bj, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge(&resj, &aj, &b); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); +} + +void run_ge(void) { + int i; + for (i = 0; i < count * 32; i++) { + test_ge(); + } + test_add_neg_y_diff_x(); +} + +void test_ec_combine(void) { + secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey data[6]; + const secp256k1_pubkey* d[6]; + secp256k1_pubkey sd; + secp256k1_pubkey sd2; + secp256k1_gej Qj; + secp256k1_ge Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&data[i - 1], &Q); + d[i - 1] = &data[i - 1]; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&sd, &Q); + CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1); + CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + +void test_group_decompress(const secp256k1_fe* x) { + /* The input itself, normalized. */ + secp256k1_fe fex = *x; + secp256k1_fe fez; + /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; + /* Return values of the above calls. */ + int res_quad, res_even, res_odd; + + secp256k1_fe_normalize_var(&fex); + + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); + res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); + res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); + + CHECK(res_quad == res_even); + CHECK(res_quad == res_odd); + + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); + secp256k1_fe_normalize_var(&ge_odd.x); + secp256k1_fe_normalize_var(&ge_even.x); + secp256k1_fe_normalize_var(&ge_quad.y); + secp256k1_fe_normalize_var(&ge_odd.y); + secp256k1_fe_normalize_var(&ge_even.y); + + /* No infinity allowed. */ + CHECK(!ge_quad.infinity); + CHECK(!ge_even.infinity); + CHECK(!ge_odd.infinity); + + /* Check that the x coordinates check out. */ + CHECK(secp256k1_fe_equal_var(&ge_quad.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_even.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); + + /* Check that the Y coordinate result in ge_quad is a square. */ + CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); + + /* Check odd/even Y in ge_odd, ge_even. */ + CHECK(secp256k1_fe_is_odd(&ge_odd.y)); + CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + } +} + +void run_group_decompress(void) { + int i; + for (i = 0; i < count * 4; i++) { + secp256k1_fe fe; + random_fe_test(&fe); + test_group_decompress(&fe); + } +} + +/***** ECMULT TESTS *****/ + +void run_ecmult_chain(void) { + /* random starting point A (on the curve) */ + secp256k1_gej a = SECP256K1_GEJ_CONST( + 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, + 0x727fd8bc, 0x04d3362c, 0x6c7bf458, 0xe2846004, + 0xa357ae91, 0x5c4a6528, 0x1309edf2, 0x0504740f, + 0x0eb33439, 0x90216b4f, 0x81063cb6, 0x5f2f7e0f + ); + /* two random initial factors xn and gn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x84cc5452, 0xf7fde1ed, 0xb4d38a8c, 0xe9b1b84c, + 0xcef31f14, 0x6e569be9, 0x705d357a, 0x42985407 + ); + secp256k1_scalar gn = SECP256K1_SCALAR_CONST( + 0xa1e58d22, 0x553dcd42, 0xb2398062, 0x5d4c57a9, + 0x6e9323d4, 0x2b3152e5, 0xca2c3990, 0xedc7c9de + ); + /* two small multipliers to be applied to xn and gn in every iteration: */ + static const secp256k1_scalar xf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x1337); + static const secp256k1_scalar gf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x7113); + /* accumulators with the resulting coefficients to A and G */ + secp256k1_scalar ae = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar ge = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + /* actual points */ + secp256k1_gej x; + secp256k1_gej x2; + int i; + + /* the point being computed */ + x = a; + for (i = 0; i < 200*count; i++) { + /* in each iteration, compute X = xn*X + gn*G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x, &x, &xn, &gn); + /* also compute ae and ge: the actual accumulated factors for A and G */ + /* if X was (ae*A+ge*G), xn*X + gn*G results in (xn*ae*A + (xn*ge+gn)*G) */ + secp256k1_scalar_mul(&ae, &ae, &xn); + secp256k1_scalar_mul(&ge, &ge, &xn); + secp256k1_scalar_add(&ge, &ge, &gn); + /* modify xn and gn */ + secp256k1_scalar_mul(&xn, &xn, &xf); + secp256k1_scalar_mul(&gn, &gn, &gf); + + /* verify */ + if (i == 19999) { + /* expected result after 19999 iterations */ + secp256k1_gej rp = SECP256K1_GEJ_CONST( + 0xD6E96687, 0xF9B10D09, 0x2A6F3543, 0x9D86CEBE, + 0xA4535D0D, 0x409F5358, 0x6440BD74, 0xB933E830, + 0xB95CBCA2, 0xC77DA786, 0x539BE8FD, 0x53354D2D, + 0x3B4F566A, 0xE6580454, 0x07ED6015, 0xEE1B2A88 + ); + + secp256k1_gej_neg(&rp, &rp); + secp256k1_gej_add_var(&rp, &rp, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&rp)); + } + } + /* redo the computation, but directly with the resulting ae and ge coefficients: */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x2, &a, &ae, &ge); + secp256k1_gej_neg(&x2, &x2); + secp256k1_gej_add_var(&x2, &x2, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&x2)); +} + +void test_point_times_order(const secp256k1_gej *point) { + /* X * (point + G) + (order-X) * (pointer + G) = 0 */ + secp256k1_scalar x; + secp256k1_scalar nx; + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_gej res1, res2; + secp256k1_ge res3; + unsigned char pub[65]; + size_t psize = 65; + random_scalar_order_test(&x); + secp256k1_scalar_negate(&nx, &x); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &x, &x); /* calc res1 = x * point + x * G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ + secp256k1_gej_add_var(&res1, &res1, &res2, NULL); + CHECK(secp256k1_gej_is_infinity(&res1)); + CHECK(secp256k1_gej_is_valid_var(&res1) == 0); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + CHECK(secp256k1_ge_is_valid_var(&res3) == 0); + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 0) == 0); + psize = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 1) == 0); + /* check zero/one edge cases */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &zero); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_gej(&res3, point); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_ge(&res3, &secp256k1_ge_const_g); +} + +void run_point_times_order(void) { + int i; + secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); + static const secp256k1_fe xr = SECP256K1_FE_CONST( + 0x7603CB59, 0xB0EF6C63, 0xFE608479, 0x2A0C378C, + 0xDB3233A8, 0x0F8A9A09, 0xA877DEAD, 0x31B38C45 + ); + for (i = 0; i < 500; i++) { + secp256k1_ge p; + if (secp256k1_ge_set_xo_var(&p, &x, 1)) { + secp256k1_gej j; + CHECK(secp256k1_ge_is_valid_var(&p)); + secp256k1_gej_set_ge(&j, &p); + CHECK(secp256k1_gej_is_valid_var(&j)); + test_point_times_order(&j); + } + secp256k1_fe_sqr(&x, &x); + } + secp256k1_fe_normalize_var(&x); + CHECK(secp256k1_fe_equal_var(&x, &xr)); +} + +void ecmult_const_random_mult(void) { + /* random starting point A (on the curve) */ + secp256k1_ge a = SECP256K1_GE_CONST( + 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, + 0xa97f9263, 0xb1e88044, 0x93351325, 0x91bc450a, + 0x535c59f7, 0x325e5d2b, 0xc391fbe8, 0x3c12787c, + 0x337e4a98, 0xe82a9011, 0x0123ba37, 0xdd769c7d + ); + /* random initial factor xn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x649d4f77, 0xc4242df7, 0x7f2079c9, 0x14530327, + 0xa31b876a, 0xd2d8ce2a, 0x2236d5c6, 0xd7b2029b + ); + /* expected xn * A (from sage) */ + secp256k1_ge expected_b = SECP256K1_GE_CONST( + 0x23773684, 0x4d209dc7, 0x098a786f, 0x20d06fcd, + 0x070a38bf, 0xc11ac651, 0x03004319, 0x1e2a8786, + 0xed8c3b8e, 0xc06dd57b, 0xd06ea66e, 0x45492b0f, + 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 + ); + secp256k1_gej b; + secp256k1_ecmult_const(&b, &a, &xn); + + CHECK(secp256k1_ge_is_valid_var(&a)); + ge_equals_gej(&expected_b, &b); +} + +void ecmult_const_commutativity(void) { + secp256k1_scalar a; + secp256k1_scalar b; + secp256k1_gej res1; + secp256k1_gej res2; + secp256k1_ge mid1; + secp256k1_ge mid2; + random_scalar_order_test(&a); + random_scalar_order_test(&b); + + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + ge_equals_ge(&mid1, &mid2); +} + +void ecmult_const_mult_zero_one(void) { + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar negone; + secp256k1_gej res1; + secp256k1_ge res2; + secp256k1_ge point; + secp256k1_scalar_negate(&negone, &one); + + random_group_element_test(&point); + secp256k1_ecmult_const(&res1, &point, &zero); + secp256k1_ge_set_gej(&res2, &res1); + CHECK(secp256k1_ge_is_infinity(&res2)); + secp256k1_ecmult_const(&res1, &point, &one); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); + secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_gej_neg(&res1, &res1); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); +} + +void ecmult_const_chain_multiply(void) { + /* Check known result (randomly generated test problem from sage) */ + const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( + 0x4968d524, 0x2abf9b7a, 0x466abbcf, 0x34b11b6d, + 0xcd83d307, 0x827bed62, 0x05fad0ce, 0x18fae63b + ); + const secp256k1_gej expected_point = SECP256K1_GEJ_CONST( + 0x5494c15d, 0x32099706, 0xc2395f94, 0x348745fd, + 0x757ce30e, 0x4e8c90fb, 0xa2bad184, 0xf883c69f, + 0x5d195d20, 0xe191bf7f, 0x1be3e55f, 0x56a80196, + 0x6071ad01, 0xf1462f66, 0xc997fa94, 0xdb858435 + ); + secp256k1_gej point; + secp256k1_ge res; + int i; + + secp256k1_gej_set_ge(&point, &secp256k1_ge_const_g); + for (i = 0; i < 100; ++i) { + secp256k1_ge tmp; + secp256k1_ge_set_gej(&tmp, &point); + secp256k1_ecmult_const(&point, &tmp, &scalar); + } + secp256k1_ge_set_gej(&res, &point); + ge_equals_gej(&res, &expected_point); +} + +void run_ecmult_const_tests(void) { + ecmult_const_mult_zero_one(); + ecmult_const_random_mult(); + ecmult_const_commutativity(); + ecmult_const_chain_multiply(); +} + +void test_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, two, t; + int wnaf[256]; + int zeroes = -1; + int i; + int bits; + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&two, 2); + bits = secp256k1_ecmult_wnaf(wnaf, 256, number, w); + CHECK(bits <= 256); + for (i = bits-1; i >= 0; i--) { + int v = wnaf[i]; + secp256k1_scalar_mul(&x, &x, &two); + if (v) { + CHECK(zeroes == -1 || zeroes >= w-1); /* check that distance between non-zero elements is at least w-1 */ + zeroes=0; + CHECK((v & 1) == 1); /* check non-zero elements are odd */ + CHECK(v <= (1 << (w-1)) - 1); /* check range below */ + CHECK(v >= -(1 << (w-1)) - 1); /* check range above */ + } else { + CHECK(zeroes != -1); /* check that no unnecessary zero padding exists */ + zeroes++; + } + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + CHECK(secp256k1_scalar_eq(&x, number)); /* check that wnaf represents number */ +} + +void test_constant_wnaf_negate(const secp256k1_scalar *number) { + secp256k1_scalar neg1 = *number; + secp256k1_scalar neg2 = *number; + int sign1 = 1; + int sign2 = 1; + + if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { + secp256k1_scalar_negate(&neg1, &neg1); + sign1 = -1; + } + sign2 = secp256k1_scalar_cond_negate(&neg2, secp256k1_scalar_is_even(&neg2)); + CHECK(sign1 == sign2); + CHECK(secp256k1_scalar_eq(&neg1, &neg2)); +} + +void test_constant_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, shift; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&shift, 1 << w); + /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */ +#ifdef USE_ENDOMORPHISM + for (i = 0; i < 16; ++i) { + secp256k1_scalar_shr_int(&num, 8); + } +#endif + skew = secp256k1_wnaf_const(wnaf, num, w); + + for (i = WNAF_SIZE(w); i >= 0; --i) { + secp256k1_scalar t; + int v = wnaf[i]; + CHECK(v != 0); /* check nonzero */ + CHECK(v & 1); /* check parity */ + CHECK(v > -(1 << w)); /* check range above */ + CHECK(v < (1 << w)); /* check range below */ + + secp256k1_scalar_mul(&x, &x, &shift); + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + /* Skew num because when encoding numbers as odd we use an offset */ + secp256k1_scalar_cadd_bit(&num, skew == 2, 1); + CHECK(secp256k1_scalar_eq(&x, &num)); +} + +void run_wnaf(void) { + int i; + secp256k1_scalar n = {{0}}; + + /* Sanity check: 1 and 2 are the smallest odd and even numbers and should + * have easier-to-diagnose failure modes */ + n.d[0] = 1; + test_constant_wnaf(&n, 4); + n.d[0] = 2; + test_constant_wnaf(&n, 4); + /* Random tests */ + for (i = 0; i < count; i++) { + random_scalar_order(&n); + test_wnaf(&n, 4+(i%10)); + test_constant_wnaf_negate(&n); + test_constant_wnaf(&n, 4 + (i % 10)); + } + secp256k1_scalar_set_int(&n, 0); + CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); + CHECK(secp256k1_scalar_is_zero(&n)); + CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1); + CHECK(secp256k1_scalar_is_zero(&n)); +} + +void test_ecmult_constants(void) { + /* Test ecmult_gen() for [0..36) and [order-36..0). */ + secp256k1_scalar x; + secp256k1_gej r; + secp256k1_ge ng; + int i; + int j; + secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); + for (i = 0; i < 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&secp256k1_ge_const_g, &r); + } + secp256k1_gej_add_ge(&r, &r, &ng); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } + for (i = 1; i <= 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_scalar_negate(&x, &x); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&ng, &r); + } + secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } +} + +void run_ecmult_constants(void) { + test_ecmult_constants(); +} + +void test_ecmult_gen_blind(void) { + /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */ + secp256k1_scalar key; + secp256k1_scalar b; + unsigned char seed32[32]; + secp256k1_gej pgej; + secp256k1_gej pgej2; + secp256k1_gej i; + secp256k1_ge pge; + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); + secp256k1_rand256(seed32); + b = ctx->ecmult_gen_ctx.blind; + i = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key); + CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); + CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ge_set_gej(&pge, &pgej); + ge_equals_gej(&pge, &pgej2); +} + +void test_ecmult_gen_blind_reset(void) { + /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ + secp256k1_scalar b; + secp256k1_gej initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + b = ctx->ecmult_gen_ctx.blind; + initial = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); +} + +void run_ecmult_gen_blind(void) { + int i; + test_ecmult_gen_blind_reset(); + for (i = 0; i < 10; i++) { + test_ecmult_gen_blind(); + } +} + +#ifdef USE_ENDOMORPHISM +/***** ENDOMORPHISH TESTS *****/ +void test_scalar_split(void) { + secp256k1_scalar full; + secp256k1_scalar s1, slam; + const unsigned char zero[32] = {0}; + unsigned char tmp[32]; + + random_scalar_order_test(&full); + secp256k1_scalar_split_lambda(&s1, &slam, &full); + + /* check that both are <= 128 bits in size */ + if (secp256k1_scalar_is_high(&s1)) { + secp256k1_scalar_negate(&s1, &s1); + } + if (secp256k1_scalar_is_high(&slam)) { + secp256k1_scalar_negate(&slam, &slam); + } + + secp256k1_scalar_get_b32(tmp, &s1); + CHECK(memcmp(zero, tmp, 16) == 0); + secp256k1_scalar_get_b32(tmp, &slam); + CHECK(memcmp(zero, tmp, 16) == 0); +} + +void run_endomorphism_tests(void) { + test_scalar_split(); +} +#endif + +void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { + unsigned char pubkeyc[65]; + secp256k1_pubkey pubkey; + secp256k1_ge ge; + size_t pubkeyclen; + int32_t ecount; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) { + /* Smaller sizes are tested exhaustively elsewhere. */ + int32_t i; + memcpy(&pubkeyc[1], input, 64); + VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen); + for (i = 0; i < 256; i++) { + /* Try all type bytes. */ + int xpass; + int ypass; + int ysign; + pubkeyc[0] = i; + /* What sign does this point have? */ + ysign = (input[63] & 1) + 2; + /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */ + xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2); + /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */ + ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) && + ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65)); + if (xpass || ypass) { + /* These cases must parse. */ + unsigned char pubkeyo[65]; + size_t outl; + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + ecount = 0; + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 33); + CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0); + CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0])); + if (ypass) { + /* This test isn't always done because we decode with alternative signs, so the y won't match. */ + CHECK(pubkeyo[0] == ysign); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + secp256k1_pubkey_save(&pubkey, &ge); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 65); + CHECK(pubkeyo[0] == 4); + CHECK(memcmp(&pubkeyo[1], input, 64) == 0); + } + CHECK(ecount == 0); + } else { + /* These cases must fail to parse. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + } + } + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void run_ec_pubkey_parse_test(void) { +#define SECP256K1_EC_PARSE_TEST_NVALID (12) + const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { + { + /* Point with leading and trailing zeros in x and y serialization. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83, + 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00 + }, + { + /* Point with x equal to a 3rd root of unity.*/ + 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9, + 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with largest x. (1/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e, + 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d, + }, + { + /* Point with largest x. (2/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1, + 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2, + }, + { + /* Point with smallest x. (1/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with smallest x. (2/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* Point with largest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with smallest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + } + }; +#define SECP256K1_EC_PARSE_TEST_NXVALID (4) + const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = { + { + /* Valid if y overflow ignored (y = 1 mod p). (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* x on curve, y is from y^2 = x^3 + 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + } + }; +#define SECP256K1_EC_PARSE_TEST_NINVALID (7) + const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = { + { + /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */ + 0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, + 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f, + 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0, + 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d, + 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2, + 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53 + } + }; + const unsigned char pubkeyc[66] = { + /* Serialization of G. */ + 0x04, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98, 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, + 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, + 0xB8, 0x00 + }; + unsigned char sout[65]; + unsigned char shortkey[2]; + secp256k1_ge ge; + secp256k1_pubkey pubkey; + size_t len; + int32_t i; + int32_t ecount; + int32_t ecount2; + ecount = 0; + /* Nothing should be reading this far into pubkeyc. */ + VG_UNDEF(&pubkeyc[65], 1); + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + /* Zero length claimed, fail, zeroize, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(shortkey, 2); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Length one claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 256 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i; + VG_UNDEF(&shortkey[1], 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + /* Length two claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 65536 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i & 255; + shortkey[1] = i >> 8; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0); + CHECK(ecount == 2); + /* NULL input string. Illegal arg and zeroize output. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 1); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 2); + /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Valid parse. */ + memset(&pubkey, 0, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + VG_UNDEF(&ge, sizeof(ge)); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + VG_CHECK(&ge.x, sizeof(ge.x)); + VG_CHECK(&ge.y, sizeof(ge.y)); + VG_CHECK(&ge.infinity, sizeof(ge.infinity)); + ge_equals_ge(&secp256k1_ge_const_g, &ge); + CHECK(ecount == 0); + /* secp256k1_ec_pubkey_serialize illegal args. */ + ecount = 0; + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 1); + CHECK(len == 0); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 2); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); + VG_CHECK(sout, 65); + CHECK(ecount == 3); + CHECK(len == 0); + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(ecount == 4); + CHECK(len == 0); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(sout, 65); + CHECK(ecount == 4); + CHECK(len == 65); + /* Multiple illegal args. Should still set arg error only once. */ + ecount = 0; + ecount2 = 11; + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + /* Does the illegal arg callback actually change the behavior? */ + secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2); + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + CHECK(ecount2 == 10); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + /* Try a bunch of prefabbed points with all possible encodings. */ + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) { + ec_pubkey_parse_pointtest(valid[i], 1, 1); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) { + ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) { + ec_pubkey_parse_pointtest(invalid[i], 0, 0); + } +} + +void run_eckey_edge_case_test(void) { + const unsigned char orderc[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; + unsigned char ctmp[33]; + unsigned char ctmp2[33]; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey2; + secp256k1_pubkey pubkey_one; + secp256k1_pubkey pubkey_negone; + const secp256k1_pubkey *pubkeys[3]; + size_t len; + int32_t ecount; + /* Group order is too large, reject. */ + CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Maximum value is too large, reject. */ + memset(ctmp, 255, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Zero is too small, reject. */ + memset(ctmp, 0, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* One must be accepted. */ + ctmp[31] = 0x01; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_one = pubkey; + /* Group order + 1 is too large, reject. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x42; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* -1 must be accepted. */ + ctmp[31] = 0x40; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_negone = pubkey; + /* Tweak of zero leaves the value changed. */ + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); + memcpy(&pubkey2, &pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Multiply tweak of zero zeroizes the output. */ + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Overflowing key tweak zeroizes. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Private key tweaks results in a key of zero. */ + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(memcmp(zeros, ctmp2, 32) == 0); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Tweak computation wraps and results in a key of 1. */ + ctmp2[31] = 2; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1); + CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Tweak mul * 2 = 1+1. */ + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Test argument errors. */ + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(ecount == 0); + /* Zeroize pubkey on parse error. */ + memset(&pubkey, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + memset(&pubkey2, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0); + /* Plain argument errors. */ + ecount = 0; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0); + CHECK(ecount == 1); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0); + CHECK(ecount == 1); + memset(&pubkey, 1, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* secp256k1_ec_pubkey_combine tests. */ + ecount = 0; + pubkeys[0] = &pubkey_one; + VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *)); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 2); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + pubkeys[0] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Result is infinity. */ + pubkeys[0] = &pubkey_one; + pubkeys[1] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + /* Passes through infinity but comes out one. */ + pubkeys[2] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Adds to two. */ + pubkeys[1] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { + secp256k1_scalar nonce; + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); +} + +void test_ecdsa_sign_verify(void) { + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar one; + secp256k1_scalar msg, key; + secp256k1_scalar sigr, sigs; + int recid; + int getrec; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + getrec = secp256k1_rand_bits(1); + random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); + if (getrec) { + CHECK(recid >= 0 && recid < 4); + } + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); +} + +void run_ecdsa_sign_verify(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_sign_verify(); + } +} + +/** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void)msg32; + (void)key32; + (void)algo16; + memcpy(nonce32, data, 32); + return (counter == 0); +} + +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that has a fatal error on the first counter value. */ + if (counter == 0) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 1); +} + +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ + if (counter < 3) { + memset(nonce32, counter==0 ? 0 : 255, 32); + if (counter == 2) { + nonce32[31]--; + } + return 1; + } + if (counter < 5) { + static const unsigned char order[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; + memcpy(nonce32, order, 32); + if (counter == 4) { + nonce32[31]++; + } + return 1; + } + /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */ + /* If someone does fine a case where it retries for secp256k1, we'd like to know. */ + if (counter > 5) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); +} + +int is_empty_signature(const secp256k1_ecdsa_signature *sig) { + static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0}; + return memcmp(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; +} + +void test_ecdsa_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + unsigned char privkey2[32]; + secp256k1_ecdsa_signature signature[6]; + secp256k1_scalar r, s; + unsigned char sig[74]; + size_t siglen = 74; + unsigned char pubkeyc[65]; + size_t pubkeyclen = 65; + secp256k1_pubkey pubkey; + unsigned char seckey[300]; + size_t seckeylen = 300; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Verify exporting and importing public key. */ + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); + memset(&pubkey, 0, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + + /* Verify private key import and export. */ + CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1)); + CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1); + CHECK(memcmp(privkey, privkey2, 32) == 0); + + /* Optionally tweak the keys using addition. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Optionally tweak the keys using multiplication. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Sign. */ + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1); + CHECK(memcmp(&signature[0], &signature[4], sizeof(signature[0])) == 0); + CHECK(memcmp(&signature[0], &signature[1], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[2], &signature[3], sizeof(signature[0])) != 0); + /* Verify. */ + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1); + /* Test lower-S form, malleate, verify and fail, test again, malleate again */ + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0])); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(memcmp(&signature[5], &signature[0], 64) == 0); + + /* Serialize/parse DER and verify again */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + memset(&signature[0], 0, sizeof(signature[0])); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + /* Serialize/destroy/parse DER and verify again. */ + siglen = 74; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 || + secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0); +} + +void test_random_pubkeys(void) { + secp256k1_ge elem; + secp256k1_ge elem2; + unsigned char in[65]; + /* Generate some randomly sized pubkeys. */ + size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33; + if (secp256k1_rand_bits(2) == 0) { + len = secp256k1_rand_bits(6); + } + if (len == 65) { + in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); + } else { + in[0] = secp256k1_rand_bits(1) ? 2 : 3; + } + if (secp256k1_rand_bits(3) == 0) { + in[0] = secp256k1_rand_bits(8); + } + if (len > 1) { + secp256k1_rand256(&in[1]); + } + if (len > 33) { + secp256k1_rand256(&in[33]); + } + if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { + unsigned char out[65]; + unsigned char firstb; + int res; + size_t size = len; + firstb = in[0]; + /* If the pubkey can be parsed, it should round-trip... */ + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33)); + CHECK(size == len); + CHECK(memcmp(&in[1], &out[1], len-1) == 0); + /* ... except for the type of hybrid inputs. */ + if ((in[0] != 6) && (in[0] != 7)) { + CHECK(in[0] == out[0]); + } + size = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&elem, in, &size, 0)); + CHECK(size == 65); + CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); + ge_equals_ge(&elem,&elem2); + /* Check that the X9.62 hybrid type is checked. */ + in[0] = secp256k1_rand_bits(1) ? 6 : 7; + res = secp256k1_eckey_pubkey_parse(&elem2, in, size); + if (firstb == 2 || firstb == 3) { + if (in[0] == firstb + 4) { + CHECK(res); + } else { + CHECK(!res); + } + } + if (res) { + ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0)); + CHECK(memcmp(&in[1], &out[1], 64) == 0); + } + } +} + +void run_random_pubkeys(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_random_pubkeys(); + } +} + +void run_ecdsa_end_to_end(void) { + int i; + for (i = 0; i < 64*count; i++) { + test_ecdsa_end_to_end(); + } +} + +int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { + static const unsigned char zeroes[32] = {0}; +#ifdef ENABLE_OPENSSL_TESTS + static const unsigned char max_scalar[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; +#endif + + int ret = 0; + + secp256k1_ecdsa_signature sig_der; + unsigned char roundtrip_der[2048]; + unsigned char compact_der[64]; + size_t len_der = 2048; + int parsed_der = 0, valid_der = 0, roundtrips_der = 0; + + secp256k1_ecdsa_signature sig_der_lax; + unsigned char roundtrip_der_lax[2048]; + unsigned char compact_der_lax[64]; + size_t len_der_lax = 2048; + int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0; + +#ifdef ENABLE_OPENSSL_TESTS + ECDSA_SIG *sig_openssl; + const unsigned char *sigptr; + unsigned char roundtrip_openssl[2048]; + int len_openssl = 2048; + int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0; +#endif + + parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen); + if (parsed_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0; + valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0); + } + if (valid_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1; + roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0; + } + + parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen); + if (parsed_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10; + valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0); + } + if (valid_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; + roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0; + } + + if (certainly_der) { + ret |= (!parsed_der) << 2; + } + if (certainly_not_der) { + ret |= (parsed_der) << 17; + } + if (valid_der) { + ret |= (!roundtrips_der) << 3; + } + + if (valid_der) { + ret |= (!roundtrips_der_lax) << 12; + ret |= (len_der != len_der_lax) << 13; + ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14; + } + ret |= (roundtrips_der != roundtrips_der_lax) << 15; + if (parsed_der) { + ret |= (!parsed_der_lax) << 16; + } + +#ifdef ENABLE_OPENSSL_TESTS + sig_openssl = ECDSA_SIG_new(); + sigptr = sig; + parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL); + if (parsed_openssl) { + valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256; + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + } + len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL); + if (len_openssl <= 2048) { + unsigned char *ptr = roundtrip_openssl; + CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl); + roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0); + } else { + len_openssl = 0; + } + ECDSA_SIG_free(sig_openssl); + + ret |= (parsed_der && !parsed_openssl) << 4; + ret |= (valid_der && !valid_openssl) << 5; + ret |= (roundtrips_openssl && !parsed_der) << 6; + ret |= (roundtrips_der != roundtrips_openssl) << 7; + if (roundtrips_openssl) { + ret |= (len_der != (size_t)len_openssl) << 8; + ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9; + } +#endif + return ret; +} + +static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { + size_t i; + for (i = 0; i < ptrlen; i++) { + int shift = ptrlen - 1 - i; + if (shift >= 4) { + ptr[i] = 0; + } else { + ptr[i] = (val >> shift) & 0xFF; + } + } +} + +static void damage_array(unsigned char *sig, size_t *len) { + int pos; + int action = secp256k1_rand_bits(3); + if (action < 1 && *len > 3) { + /* Delete a byte. */ + pos = secp256k1_rand_int(*len); + memmove(sig + pos, sig + pos + 1, *len - pos - 1); + (*len)--; + return; + } else if (action < 2 && *len < 2048) { + /* Insert a byte. */ + pos = secp256k1_rand_int(1 + *len); + memmove(sig + pos + 1, sig + pos, *len - pos); + sig[pos] = secp256k1_rand_bits(8); + (*len)++; + return; + } else if (action < 4) { + /* Modify a byte. */ + sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + return; + } else { /* action < 8 */ + /* Modify a bit. */ + sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + return; + } +} + +static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) { + int der; + int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2]; + size_t tlen, elen, glen; + int indet; + int n; + + *len = 0; + der = secp256k1_rand_bits(2) == 0; + *certainly_der = der; + *certainly_not_der = 0; + indet = der ? 0 : secp256k1_rand_int(10) == 0; + + for (n = 0; n < 2; n++) { + /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */ + nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0); + /* The length of the number in bytes (the first byte of which will always be nonzero) */ + nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8; + CHECK(nlen[n] <= 232); + /* The top bit of the number. */ + nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1)); + /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */ + nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127)); + /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */ + nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8); + if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { + *certainly_not_der = 1; + } + CHECK(nlen[n] + nzlen[n] <= 300); + /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */ + nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2); + if (!der) { + /* nlenlen[n] max 127 bytes */ + int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + nlenlen[n] += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427); + } + + /* The total length of the data to go, so far */ + tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1]; + CHECK(tlen <= 856); + + /* The length of the garbage inside the tuple. */ + elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8; + if (elen != 0) { + *certainly_not_der = 1; + } + tlen += elen; + CHECK(tlen <= 980); + + /* The length of the garbage after the end of the tuple. */ + glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8; + if (glen != 0) { + *certainly_not_der = 1; + } + CHECK(tlen + glen <= 990); + + /* Write the tuple header. */ + sig[(*len)++] = 0x30; + if (indet) { + /* Indeterminate length */ + sig[(*len)++] = 0x80; + *certainly_not_der = 1; + } else { + int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2); + if (!der) { + int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + tlenlen += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + if (tlenlen == 0) { + /* Short length notation */ + sig[(*len)++] = tlen; + } else { + /* Long length notation */ + sig[(*len)++] = 128 + tlenlen; + assign_big_endian(sig + *len, tlenlen, tlen); + *len += tlenlen; + } + tlen += tlenlen; + } + tlen += 2; + CHECK(tlen + glen <= 1119); + + for (n = 0; n < 2; n++) { + /* Write the integer header. */ + sig[(*len)++] = 0x02; + if (nlenlen[n] == 0) { + /* Short length notation */ + sig[(*len)++] = nlen[n] + nzlen[n]; + } else { + /* Long length notation. */ + sig[(*len)++] = 128 + nlenlen[n]; + assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]); + *len += nlenlen[n]; + } + /* Write zero padding */ + while (nzlen[n] > 0) { + sig[(*len)++] = 0x00; + nzlen[n]--; + } + if (nlen[n] == 32 && !nlow[n]) { + /* Special extra 16 0xFF bytes in "high" 32-byte numbers */ + int i; + for (i = 0; i < 16; i++) { + sig[(*len)++] = 0xFF; + } + nlen[n] -= 16; + } + /* Write first byte of number */ + if (nlen[n] > 0) { + sig[(*len)++] = nhbyte[n]; + nlen[n]--; + } + /* Generate remaining random bytes of number */ + secp256k1_rand_bytes_test(sig + *len, nlen[n]); + *len += nlen[n]; + nlen[n] = 0; + } + + /* Generate random garbage inside tuple. */ + secp256k1_rand_bytes_test(sig + *len, elen); + *len += elen; + + /* Generate end-of-contents bytes. */ + if (indet) { + sig[(*len)++] = 0; + sig[(*len)++] = 0; + tlen += 2; + } + CHECK(tlen + glen <= 1121); + + /* Generate random garbage outside tuple. */ + secp256k1_rand_bytes_test(sig + *len, glen); + *len += glen; + tlen += glen; + CHECK(tlen <= 1121); + CHECK(tlen == *len); +} + +void run_ecdsa_der_parse(void) { + int i,j; + for (i = 0; i < 200 * count; i++) { + unsigned char buffer[2048]; + size_t buflen = 0; + int certainly_der = 0; + int certainly_not_der = 0; + random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); + for (j = 0; j < 16; j++) { + int ret = 0; + if (j > 0) { + damage_array(buffer, &buflen); + /* We don't know anything anymore about the DERness of the result */ + certainly_der = 0; + certainly_not_der = 0; + } + ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der); + if (ret != 0) { + size_t k; + fprintf(stderr, "Failure %x on ", ret); + for (k = 0; k < buflen; k++) { + fprintf(stderr, "%02x ", buffer[k]); + } + fprintf(stderr, "\n"); + } + CHECK(ret == 0); + } + } +} + +/* Tests several edge cases. */ +void test_ecdsa_edge_cases(void) { + int t; + secp256k1_ecdsa_signature sig; + + /* Test the case where ECDSA recomputes a point that is infinity. */ + { + secp256k1_gej keyj; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_negate(&ss, &ss); + secp256k1_scalar_inverse(&ss, &ss); + secp256k1_scalar_set_int(&sr, 1); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sr); + secp256k1_ge_set_gej(&key, &keyj); + msg = ss; + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with r of zero fails. */ + { + const unsigned char pubkey_mods_zero[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x41 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 0); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey_mods_zero, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with s of zero fails. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 0); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 1); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with message 0 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + const unsigned char pubkey2[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x43 + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 2); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message 1 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22, + 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05, + 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c, + 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76, + 0x25 + }; + const unsigned char pubkey2[33] = { + 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40, + 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae, + 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f, + 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10, + 0x62 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message -1 passes. */ + { + const unsigned char pubkey[33] = { + 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0, + 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52, + 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27, + 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20, + 0xf1 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_negate(&msg, &msg); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_set_int(&ss, 3); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Signature where s would be zero. */ + { + secp256k1_pubkey pubkey; + size_t siglen; + int32_t ecount; + unsigned char signature[72]; + static const unsigned char nonce[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + static const unsigned char nonce2[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 + }; + const unsigned char key[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + unsigned char msg[32] = { + 0x86, 0x41, 0x99, 0x81, 0x06, 0x23, 0x44, 0x53, + 0xaa, 0x5f, 0x9d, 0x6a, 0x31, 0x78, 0xf4, 0xf7, + 0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62, + 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, + }; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); + msg[31] = 0xaa; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 7); + /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */ + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0); + CHECK(ecount == 8); + siglen = 72; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1); + CHECK(ecount == 13); + siglen = 10; + /* Too little room for a signature does not fail via ARGCHECK. */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0); + CHECK(ecount == 13); + ecount = 0; + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1); + CHECK(ecount == 5); + memset(signature, 255, 64); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0); + CHECK(ecount == 5); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + } + + /* Nonce function corner cases. */ + for (t = 0; t < 2; t++) { + static const unsigned char zero[32] = {0x00}; + int i; + unsigned char key[32]; + unsigned char msg[32]; + secp256k1_ecdsa_signature sig2; + secp256k1_scalar sr[512], ss; + const unsigned char *extra; + extra = t == 0 ? NULL : zero; + memset(msg, 0, 32); + msg[31] = 1; + /* High key results in signature failure. */ + memset(key, 0xFF, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Zero key results in signature failure. */ + memset(key, 0, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Nonce function failure results in signature failure. */ + key[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_fail, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* The retry loop successfully makes its way to the first good value. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_retry, extra) == 1); + CHECK(!is_empty_signature(&sig)); + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function is deterministic. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function changes output with different messages. */ + for(i = 0; i < 256; i++) { + int j; + msg[0] = i; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + msg[0] = 0; + msg[31] = 2; + /* The default nonce function changes output with different keys. */ + for(i = 256; i < 512; i++) { + int j; + key[0] = i - 256; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + key[0] = 0; + } + + { + /* Check that optional nonce arguments do not have equivalent effect. */ + const unsigned char zeros[32] = {0}; + unsigned char nonce[32]; + unsigned char nonce2[32]; + unsigned char nonce3[32]; + unsigned char nonce4[32]; + VG_UNDEF(nonce,32); + VG_UNDEF(nonce2,32); + VG_UNDEF(nonce3,32); + VG_UNDEF(nonce4,32); + CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); + VG_CHECK(nonce,32); + CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); + VG_CHECK(nonce2,32); + CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); + VG_CHECK(nonce3,32); + CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1); + VG_CHECK(nonce4,32); + CHECK(memcmp(nonce, nonce2, 32) != 0); + CHECK(memcmp(nonce, nonce3, 32) != 0); + CHECK(memcmp(nonce, nonce4, 32) != 0); + CHECK(memcmp(nonce2, nonce3, 32) != 0); + CHECK(memcmp(nonce2, nonce4, 32) != 0); + CHECK(memcmp(nonce3, nonce4, 32) != 0); + } + + + /* Privkey export where pubkey is the point at infinity. */ + { + unsigned char privkey[300]; + unsigned char seckey[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, + }; + size_t outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + } +} + +void run_ecdsa_edge_cases(void) { + test_ecdsa_edge_cases(); +} + +#ifdef ENABLE_OPENSSL_TESTS +EC_KEY *get_openssl_key(const unsigned char *key32) { + unsigned char privkey[300]; + size_t privkeylen; + const unsigned char* pbegin = privkey; + int compr = secp256k1_rand_bits(1); + EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); + CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr)); + CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen)); + CHECK(EC_KEY_check_key(ec_key)); + return ec_key; +} + +void test_ecdsa_openssl(void) { + secp256k1_gej qj; + secp256k1_ge q; + secp256k1_scalar sigr, sigs; + secp256k1_scalar one; + secp256k1_scalar msg2; + secp256k1_scalar key, msg; + EC_KEY *ec_key; + unsigned int sigsize = 80; + size_t secp_sigsize = 80; + unsigned char message[32]; + unsigned char signature[80]; + unsigned char key32[32]; + secp256k1_rand256_test(message); + secp256k1_scalar_set_b32(&msg, message, NULL); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(key32, &key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &qj, &key); + secp256k1_ge_set_gej(&q, &qj); + ec_key = get_openssl_key(key32); + CHECK(ec_key != NULL); + CHECK(ECDSA_sign(0, message, sizeof(message), signature, &sigsize, ec_key)); + CHECK(secp256k1_ecdsa_sig_parse(&sigr, &sigs, signature, sigsize)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg2, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg2)); + + random_sign(&sigr, &sigs, &key, &msg, NULL); + CHECK(secp256k1_ecdsa_sig_serialize(signature, &secp_sigsize, &sigr, &sigs)); + CHECK(ECDSA_verify(0, message, sizeof(message), signature, secp_sigsize, ec_key) == 1); + + EC_KEY_free(ec_key); +} + +void run_ecdsa_openssl(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_openssl(); + } +} +#endif + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/tests_impl.h" +#endif + +int main(int argc, char **argv) { + unsigned char seed16[16] = {0}; + unsigned char run32[32] = {0}; + /* find iteration count */ + if (argc > 1) { + count = strtol(argv[1], NULL, 0); + } + + /* find random seed */ + if (argc > 2) { + int pos = 0; + const char* ch = argv[2]; + while (pos < 16 && ch[0] != 0 && ch[1] != 0) { + unsigned short sh; + if (sscanf(ch, "%2hx", &sh)) { + seed16[pos] = sh; + } else { + break; + } + ch += 2; + pos++; + } + } else { + FILE *frand = fopen("/dev/urandom", "r"); + if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { + uint64_t t = time(NULL) * (uint64_t)1337; + seed16[0] ^= t; + seed16[1] ^= t >> 8; + seed16[2] ^= t >> 16; + seed16[3] ^= t >> 24; + seed16[4] ^= t >> 32; + seed16[5] ^= t >> 40; + seed16[6] ^= t >> 48; + seed16[7] ^= t >> 56; + } + fclose(frand); + } + secp256k1_rand_seed(seed16); + + printf("test count = %i\n", count); + printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); + + /* initialize */ + run_context_tests(); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (secp256k1_rand_bits(1)) { + secp256k1_rand256(run32); + CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL)); + } + + run_rand_bits(); + run_rand_int(); + + run_sha256_tests(); + run_hmac_sha256_tests(); + run_rfc6979_hmac_sha256_tests(); + +#ifndef USE_NUM_NONE + /* num tests */ + run_num_smalltests(); +#endif + + /* scalar tests */ + run_scalar_tests(); + + /* field tests */ + run_field_inv(); + run_field_inv_var(); + run_field_inv_all_var(); + run_field_misc(); + run_field_convert(); + run_sqr(); + run_sqrt(); + + /* group tests */ + run_ge(); + run_group_decompress(); + + /* ecmult tests */ + run_wnaf(); + run_point_times_order(); + run_ecmult_chain(); + run_ecmult_constants(); + run_ecmult_gen_blind(); + run_ecmult_const_tests(); + run_ec_combine(); + + /* endomorphism tests */ +#ifdef USE_ENDOMORPHISM + run_endomorphism_tests(); +#endif + + /* EC point parser test */ + run_ec_pubkey_parse_test(); + + /* EC key edge cases */ + run_eckey_edge_case_test(); + +#ifdef ENABLE_MODULE_ECDH + /* ecdh tests */ + run_ecdh_tests(); +#endif + + /* ecdsa tests */ + run_random_pubkeys(); + run_ecdsa_der_parse(); + run_ecdsa_sign_verify(); + run_ecdsa_end_to_end(); + run_ecdsa_edge_cases(); +#ifdef ENABLE_OPENSSL_TESTS + run_ecdsa_openssl(); +#endif + +#ifdef ENABLE_MODULE_SCHNORR + /* Schnorr tests */ + run_schnorr_tests(); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + /* ECDSA pubkey recovery tests */ + run_recovery_tests(); +#endif + + secp256k1_rand256(run32); + printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); + + /* shutdown */ + secp256k1_context_destroy(ctx); + + printf("no problems found\n"); + return 0; +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c new file mode 100644 index 000000000000..b040bb0733dd --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c @@ -0,0 +1,470 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#undef USE_ECMULT_STATIC_PRECOMPUTATION + +#ifndef EXHAUSTIVE_TEST_ORDER +/* see group_impl.h for allowable values */ +#define EXHAUSTIVE_TEST_ORDER 13 +#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */ +#endif + +#include "include/secp256k1.h" +#include "group.h" +#include "secp256k1.c" +#include "testrand_impl.h" + +#ifdef ENABLE_MODULE_RECOVERY +#include "src/modules/recovery/main_impl.h" +#include "include/secp256k1_recovery.h" +#endif + +/** stolen from tests.c */ +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} +/** END stolen from tests.c */ + +int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, + const unsigned char *key32, const unsigned char *algo16, + void *data, unsigned int attempt) { + secp256k1_scalar s; + int *idata = data; + (void)msg32; + (void)key32; + (void)algo16; + /* Some nonces cannot be used because they'd cause s and/or r to be zero. + * The signing function has retry logic here that just re-calls the nonce + * function with an increased `attempt`. So if attempt > 0 this means we + * need to change the nonce to avoid an infinite loop. */ + if (attempt > 0) { + *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER; + } + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +#ifdef USE_ENDOMORPHISM +void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { + int i; + for (i = 0; i < order; i++) { + secp256k1_ge res; + secp256k1_ge_mul_lambda(&res, &group[i]); + ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + } +} +#endif + +void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j; + + /* Sanity-check (and check infinity functions) */ + CHECK(secp256k1_ge_is_infinity(&group[0])); + CHECK(secp256k1_gej_is_infinity(&groupj[0])); + for (i = 1; i < order; i++) { + CHECK(!secp256k1_ge_is_infinity(&group[i])); + CHECK(!secp256k1_gej_is_infinity(&groupj[i])); + } + + /* Check all addition formulae */ + for (j = 0; j < order; j++) { + secp256k1_fe fe_inv; + secp256k1_fe_inv(&fe_inv, &groupj[j].z); + for (i = 0; i < order; i++) { + secp256k1_ge zless_gej; + secp256k1_gej tmp; + /* add_var */ + secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_ge */ + if (j > 0) { + secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + /* add_ge_var */ + secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_zinv_var */ + zless_gej.infinity = groupj[j].infinity; + zless_gej.x = groupj[j].x; + zless_gej.y = groupj[j].y; + secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + } + + /* Check doubling */ + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + if (i > 0) { + secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + secp256k1_gej_double_var(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + + /* Check negation */ + for (i = 1; i < order; i++) { + secp256k1_ge tmp; + secp256k1_gej tmpj; + secp256k1_ge_neg(&tmp, &group[i]); + ge_equals_ge(&group[order - i], &tmp); + secp256k1_gej_neg(&tmpj, &groupj[i]); + ge_equals_gej(&group[order - i], &tmpj); + } +} + +void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j, r_log; + for (r_log = 1; r_log < order; r_log++) { + for (j = 0; j < order; j++) { + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + secp256k1_scalar na, ng; + secp256k1_scalar_set_int(&na, i); + secp256k1_scalar_set_int(&ng, j); + + secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng); + ge_equals_gej(&group[(i * r_log + j) % order], &tmp); + + if (i > 0) { + secp256k1_ecmult_const(&tmp, &group[i], &ng); + ge_equals_gej(&group[(i * j) % order], &tmp); + } + } + } + } +} + +void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { + secp256k1_fe x; + unsigned char x_bin[32]; + k %= EXHAUSTIVE_TEST_ORDER; + x = group[k].x; + secp256k1_fe_normalize(&x); + secp256k1_fe_get_b32(x_bin, &x); + secp256k1_scalar_set_b32(r, x_bin, NULL); +} + +void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* Verify by calling verify */ + secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + secp256k1_scalar_get_b32(msg32, &msg_s); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } + + /* We would like to verify zero-knowledge here by counting how often every + * possible (s, r) tuple appears, but because the group order is larger + * than the field order, when coercing the x-values to scalar values, some + * appear more often than others, so we are actually not zero-knowledge. + * (This effect also appears in the real code, but the difference is on the + * order of 1/2^128th the field order, so the deviation is not useful to a + * computationally bounded attacker.) + */ +} + +#ifdef ENABLE_MODULE_RECOVERY +void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_fe r_dot_y_normalized; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + int expected_recid; + int recid; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + /* Check directly */ + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig); + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + /* In computing the recid, there is an overflow condition that is disabled in + * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value + * will exceed the group order, and our signing code always holds out for r + * values that don't overflow, so with a proper overflow check the tests would + * loop indefinitely. */ + r_dot_y_normalized = group[k].y; + secp256k1_fe_normalize(&r_dot_y_normalized); + /* Also the recovery id is flipped depending if we hit the low-s branch */ + if ((k * s) % order == (i + r * j) % order) { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0; + } else { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1; + } + CHECK(recid == expected_recid); + + /* Convert to a standard sig then check */ + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + /* This is essentially a copy of test_exhaustive_verify, with recovery added */ + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int recid = 0; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + secp256k1_scalar_get_b32(msg32, &msg_s); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* We would like to try recovering the pubkey and checking that it matches, + * but pubkey recovery is impossible in the exhaustive tests (the reason + * being that there are 12 nonzero r values, 12 nonzero points, and no + * overlap between the sets, so there are no valid signatures). */ + + /* Verify by converting to a standard signature and calling verify */ + secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid); + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} +#endif + +int main(void) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + + /* Build context */ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* TODO set z = 1, then do num_tests runs with random z values */ + + /* Generate the entire group */ + secp256k1_gej_set_infinity(&groupj[0]); + secp256k1_ge_set_gej(&group[0], &groupj[0]); + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + /* Set a different random z-value for each Jacobian point */ + secp256k1_fe z; + random_fe(&z); + + secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g); + secp256k1_ge_set_gej(&group[i], &groupj[i]); + secp256k1_gej_rescale(&groupj[i], &z); + + /* Verify against ecmult_gen */ + { + secp256k1_scalar scalar_i; + secp256k1_gej generatedj; + secp256k1_ge generated; + + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i); + secp256k1_ge_set_gej(&generated, &generatedj); + + CHECK(group[i].infinity == 0); + CHECK(generated.infinity == 0); + CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x)); + CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y)); + } + } + + /* Run the tests */ +#ifdef USE_ENDOMORPHISM + test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER); +#endif + test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); + +#ifdef ENABLE_MODULE_RECOVERY + test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); +#endif + + secp256k1_context_destroy(ctx); + return 0; +} + diff --git a/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/util.h b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/util.h new file mode 100644 index 000000000000..4092a86c9175 --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/libsecp256k1/src/util.h @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_UTIL_H_ +#define _SECP256K1_UTIL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include +#include + +typedef struct { + void (*fn)(const char *text, void* data); + const void* data; +} secp256k1_callback; + +static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { + cb->fn(text, (void*)cb->data); +} + +#ifdef DETERMINISTIC +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ +} while(0); +#else +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ + abort(); \ +} while(0) +#endif + +#ifdef HAVE_BUILTIN_EXPECT +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +#ifdef DETERMINISTIC +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed"); \ + } \ +} while(0) +#else +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed: " #cond); \ + } \ +} while(0) +#endif + +/* Like assert(), but when VERIFY is defined, and side-effect safe. */ +#if defined(COVERAGE) +#define VERIFY_CHECK(check) +#define VERIFY_SETUP(stmt) +#elif defined(VERIFY) +#define VERIFY_CHECK CHECK +#define VERIFY_SETUP(stmt) do { stmt; } while(0) +#else +#define VERIFY_CHECK(cond) do { (void)(cond); } while(0) +#define VERIFY_SETUP(stmt) +#endif + +static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { + void *ret = malloc(size); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } + return ret; +} + +/* Macro for restrict, when available and not in a VERIFY build. */ +#if defined(SECP256K1_BUILD) && defined(VERIFY) +# define SECP256K1_RESTRICT +#else +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(3,0) +# define SECP256K1_RESTRICT __restrict__ +# elif (defined(_MSC_VER) && _MSC_VER >= 1400) +# define SECP256K1_RESTRICT __restrict +# else +# define SECP256K1_RESTRICT +# endif +# else +# define SECP256K1_RESTRICT restrict +# endif +#endif + +#if defined(_WIN32) +# define I64FORMAT "I64d" +# define I64uFORMAT "I64u" +#else +# define I64FORMAT "lld" +# define I64uFORMAT "llu" +#endif + +#if defined(HAVE___INT128) +# if defined(__GNUC__) +# define SECP256K1_GNUC_EXT __extension__ +# else +# define SECP256K1_GNUC_EXT +# endif +SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; +#endif + +#endif diff --git a/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go b/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go new file mode 100644 index 000000000000..6d59a1d247ea --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/panic_cb.go @@ -0,0 +1,21 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +package secp256k1 + +import "C" +import "unsafe" + +// Callbacks for converting libsecp256k1 internal faults into +// recoverable Go panics. + +//export secp256k1GoPanicIllegal +func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) { + panic("illegal argument: " + C.GoString(msg)) +} + +//export secp256k1GoPanicError +func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) { + panic("internal error: " + C.GoString(msg)) +} diff --git a/crypto/keys/secp256k1/internal/secp256k1/secp256.go b/crypto/keys/secp256k1/internal/secp256k1/secp256.go new file mode 100644 index 000000000000..35d0eef34ace --- /dev/null +++ b/crypto/keys/secp256k1/internal/secp256k1/secp256.go @@ -0,0 +1,167 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// Package secp256k1 wraps the bitcoin secp256k1 C library. +package secp256k1 + +/* +#cgo CFLAGS: -I./libsecp256k1 +#cgo CFLAGS: -I./libsecp256k1/src/ +#define USE_NUM_NONE +#define USE_FIELD_10X26 +#define USE_FIELD_INV_BUILTIN +#define USE_SCALAR_8X32 +#define USE_SCALAR_INV_BUILTIN +#define NDEBUG +#include "./libsecp256k1/src/secp256k1.c" +#include "./libsecp256k1/src/modules/recovery/main_impl.h" +#include "ext.h" + +typedef void (*callbackFunc) (const char* msg, void* data); +extern void secp256k1GoPanicIllegal(const char* msg, void* data); +extern void secp256k1GoPanicError(const char* msg, void* data); +*/ +import "C" + +import ( + "errors" + "math/big" + "unsafe" +) + +var context *C.secp256k1_context + +func init() { + // around 20 ms on a modern CPU. + context = C.secp256k1_context_create_sign_verify() + C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) + C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil) +} + +var ( + ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes") + ErrInvalidSignatureLen = errors.New("invalid signature length") + ErrInvalidRecoveryID = errors.New("invalid signature recovery id") + ErrInvalidKey = errors.New("invalid private key") + ErrInvalidPubkey = errors.New("invalid public key") + ErrSignFailed = errors.New("signing failed") + ErrRecoverFailed = errors.New("recovery failed") +) + +// Sign creates a recoverable ECDSA signature. +// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1. +// +// The caller is responsible for ensuring that msg cannot be chosen +// directly by an attacker. It is usually preferable to use a cryptographic +// hash function on any input before handing it to this function. +func Sign(msg []byte, seckey []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if len(seckey) != 32 { + return nil, ErrInvalidKey + } + seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0])) + if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 { + return nil, ErrInvalidKey + } + + var ( + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + noncefunc = C.secp256k1_nonce_function_rfc6979 + sigstruct C.secp256k1_ecdsa_recoverable_signature + ) + if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, nil) == 0 { + return nil, ErrSignFailed + } + + var ( + sig = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + recid C.int + ) + C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct) + sig[64] = byte(recid) // add back recid to get 65 bytes sig + return sig, nil +} + +// RecoverPubkey returns the public key of the signer. +// msg must be the 32-byte hash of the message to be signed. +// sig must be a 65-byte compact ECDSA signature containing the +// recovery id as the last element. +func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if err := checkSignature(sig); err != nil { + return nil, err + } + + var ( + pubkey = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + ) + if C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 { + return nil, ErrRecoverFailed + } + return pubkey, nil +} + +// VerifySignature checks that the given pubkey created signature over message. +// The signature should be in [R || S] format. +func VerifySignature(pubkey, msg, signature []byte) bool { + if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 { + return false + } + sigdata := (*C.uchar)(unsafe.Pointer(&signature[0])) + msgdata := (*C.uchar)(unsafe.Pointer(&msg[0])) + keydata := (*C.uchar)(unsafe.Pointer(&pubkey[0])) + return C.secp256k1_ext_ecdsa_verify(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0 +} + +// DecompressPubkey parses a public key in the 33-byte compressed format. +// It returns non-nil coordinates if the public key is valid. +func DecompressPubkey(pubkey []byte) (x, y *big.Int) { + if len(pubkey) != 33 { + return nil, nil + } + var ( + pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0])) + pubkeylen = C.size_t(len(pubkey)) + out = make([]byte, 65) + outdata = (*C.uchar)(unsafe.Pointer(&out[0])) + outlen = C.size_t(len(out)) + ) + if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 { + return nil, nil + } + return new(big.Int).SetBytes(out[1:33]), new(big.Int).SetBytes(out[33:]) +} + +// CompressPubkey encodes a public key to 33-byte compressed format. +func CompressPubkey(x, y *big.Int) []byte { + var ( + pubkey = S256().Marshal(x, y) + pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0])) + pubkeylen = C.size_t(len(pubkey)) + out = make([]byte, 33) + outdata = (*C.uchar)(unsafe.Pointer(&out[0])) + outlen = C.size_t(len(out)) + ) + if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 { + panic("libsecp256k1 error") + } + return out +} + +func checkSignature(sig []byte) error { + if len(sig) != 65 { + return ErrInvalidSignatureLen + } + if sig[64] >= 4 { + return ErrInvalidRecoveryID + } + return nil +} diff --git a/crypto/keys/secp256k1/keys.pb.go b/crypto/keys/secp256k1/keys.pb.go new file mode 100644 index 000000000000..afcbab54b05d --- /dev/null +++ b/crypto/keys/secp256k1/keys.pb.go @@ -0,0 +1,498 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/secp256k1/keys.proto + +package secp256k1 + +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 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. +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_e0835e68ebdcb224, []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 secp256k1 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_e0835e68ebdcb224, []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.secp256k1.PubKey") + proto.RegisterType((*PrivKey)(nil), "cosmos.crypto.secp256k1.PrivKey") +} + +func init() { + proto.RegisterFile("cosmos/crypto/secp256k1/keys.proto", fileDescriptor_e0835e68ebdcb224) +} + +var fileDescriptor_e0835e68ebdcb224 = []byte{ + // 185 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2f, 0x4e, 0x4d, 0x2e, 0x30, 0x32, + 0x35, 0xcb, 0x36, 0xd4, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, + 0x87, 0xa8, 0xd1, 0x83, 0xa8, 0xd1, 0x83, 0xab, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, + 0xd1, 0x07, 0xb1, 0x20, 0xca, 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, 0x39, 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, 0xa3, 0xf4, 0xcc, 0x92, + 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0x98, 0xb3, 0xc1, 0x94, 0x6e, 0x71, 0x4a, 0x36, + 0xcc, 0x07, 0x20, 0x77, 0x23, 0xbc, 0x91, 0xc4, 0x06, 0x76, 0x93, 0x31, 0x20, 0x00, 0x00, 0xff, + 0xff, 0x63, 0x00, 0x68, 0x16, 0xe8, 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/secp256k1/secp256k1.go b/crypto/keys/secp256k1/secp256k1.go new file mode 100644 index 000000000000..eebe72a45242 --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1.go @@ -0,0 +1,205 @@ +package secp256k1 + +import ( + "bytes" + "crypto/sha256" + "crypto/subtle" + "fmt" + "io" + "math/big" + + secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/tendermint/tendermint/crypto" + "golang.org/x/crypto/ripemd160" // nolint: staticcheck // necessary for Bitcoin address format + + "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 = "secp256k1" + PrivKeyName = "tendermint/PrivKeySecp256k1" + PubKeyName = "tendermint/PubKeySecp256k1" +) + +// Bytes returns the byte representation of the Private Key. +func (privKey *PrivKey) Bytes() []byte { + return privKey.Key +} + +// PubKey performs the point-scalar multiplication from the privKey on the +// generator point to get the pubkey. +func (privKey *PrivKey) PubKey() cryptotypes.PubKey { + _, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key) + pk := pubkeyObject.SerializeCompressed() + return &PubKey{Key: pk} +} + +// 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 ECDSA private key on curve secp256k1 private key. +// It uses OS randomness to generate the private key. +func GenPrivKey() *PrivKey { + return &PrivKey{Key: genPrivKey(crypto.CReader())} +} + +// genPrivKey generates a new secp256k1 private key using the provided reader. +func genPrivKey(rand io.Reader) []byte { + var privKeyBytes [PrivKeySize]byte + d := new(big.Int) + for { + privKeyBytes = [PrivKeySize]byte{} + _, err := io.ReadFull(rand, privKeyBytes[:]) + if err != nil { + panic(err) + } + + d.SetBytes(privKeyBytes[:]) + // break if we found a valid point (i.e. > 0 and < N == curverOrder) + isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0 + if isValidFieldElement { + break + } + } + + return privKeyBytes[:] +} + +var one = new(big.Int).SetInt64(1) + +// GenPrivKeyFromSecret hashes the secret with SHA2, and uses +// that 32 byte output to create the private key. +// +// It makes sure the private key is a valid field element by setting: +// +// c = sha256(secret) +// k = (c mod (n − 1)) + 1, where n = curve order. +// +// NOTE: secret should be the output of a KDF like bcrypt, +// if it's derived from user input. +func GenPrivKeyFromSecret(secret []byte) *PrivKey { + secHash := sha256.Sum256(secret) + // to guarantee that we have a valid field element, we use the approach of: + // "Suite B Implementer’s Guide to FIPS 186-3", A.2.1 + // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm + // see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101 + fe := new(big.Int).SetBytes(secHash[:]) + n := new(big.Int).Sub(secp256k1.S256().N, one) + fe.Mod(fe, n) + fe.Add(fe, one) + + feB := fe.Bytes() + privKey32 := make([]byte, PrivKeySize) + // copy feB over to fixed 32 byte privKey32 and pad (if necessary) + copy(privKey32[32-len(feB):32], feB) + + return &PrivKey{Key: privKey32} +} + +//------------------------------------- + +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 { + if len(pubKey.Key) != PubKeySize { + panic("length of pubkey is incorrect") + } + + hasherSHA256 := sha256.New() + hasherSHA256.Write(pubKey.Key) // does not error + sha := hasherSHA256.Sum(nil) + + hasherRIPEMD160 := ripemd160.New() + hasherRIPEMD160.Write(sha) // does not error + return crypto.Address(hasherRIPEMD160.Sum(nil)) +} + +// Bytes returns the pubkey byte format. +func (pubKey *PubKey) Bytes() []byte { + return pubKey.Key +} + +func (pubKey *PubKey) String() string { + return fmt.Sprintf("PubKeySecp256k1{%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/secp256k1/secp256k1_cgo.go b/crypto/keys/secp256k1/secp256k1_cgo.go new file mode 100644 index 000000000000..42088483d3f9 --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_cgo.go @@ -0,0 +1,26 @@ +// +build libsecp256k1 + +package secp256k1 + +import ( + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1/internal/secp256k1" +) + +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + rsv, err := secp256k1.Sign(crypto.Sha256(msg), privKey.Key) + if err != nil { + return nil, err + } + // we do not need v in r||s||v: + rs := rsv[:len(rsv)-1] + return rs, nil +} + +// VerifySignature validates the signature. +// The msg will be hashed prior to signature verification. +func (pubKey *PrivKey) VerifySignature(msg []byte, sig []byte) bool { + return secp256k1.VerifySignature(pubKey.Key, crypto.Sha256(msg), sig) +} diff --git a/crypto/keys/secp256k1/secp256k1_cgo_test.go b/crypto/keys/secp256k1/secp256k1_cgo_test.go new file mode 100644 index 000000000000..bfaa76caf9db --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_cgo_test.go @@ -0,0 +1,40 @@ +// +build libsecp256k1 + +package secp256k1 + +import ( + "testing" + + "github.com/magiconair/properties/assert" + + "github.com/stretchr/testify/require" +) + +func TestPrivKeySecp256k1SignVerify(t *testing.T) { + msg := []byte("A.1.2 ECC Key Pair Generation by Testing Candidates") + priv := GenPrivKey() + tests := []struct { + name string + privKey *PrivKey + wantSignErr bool + wantVerifyPasses bool + }{ + {name: "valid sign-verify round", privKey: priv, wantSignErr: false, wantVerifyPasses: true}, + {name: "invalid private key", privKey: &*PrivKey{Key: [32]byte{}}, wantSignErr: true, wantVerifyPasses: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.privKey.Sign(msg) + if tt.wantSignErr { + require.Error(t, err) + t.Logf("Got error: %s", err) + return + } + require.NoError(t, err) + require.NotNil(t, got) + + pub := tt.privKey.PubKey() + assert.Equal(t, tt.wantVerifyPasses, pub.VerifyBytes(msg, got)) + }) + } +} diff --git a/crypto/keys/secp256k1/secp256k1_internal_test.go b/crypto/keys/secp256k1/secp256k1_internal_test.go new file mode 100644 index 000000000000..a2c8e73bcbe7 --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_internal_test.go @@ -0,0 +1,45 @@ +package secp256k1 + +import ( + "bytes" + "math/big" + "testing" + + btcSecp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/stretchr/testify/require" +) + +func Test_genPrivKey(t *testing.T) { + + empty := make([]byte, 32) + oneB := big.NewInt(1).Bytes() + onePadded := make([]byte, 32) + copy(onePadded[32-len(oneB):32], oneB) + t.Logf("one padded: %v, len=%v", onePadded, len(onePadded)) + + validOne := append(empty, onePadded...) + tests := []struct { + name string + notSoRand []byte + shouldPanic bool + }{ + {"empty bytes (panics because 1st 32 bytes are zero and 0 is not a valid field element)", empty, true}, + {"curve order: N", btcSecp256k1.S256().N.Bytes(), true}, + {"valid because 0 < 1 < N", validOne, false}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if tt.shouldPanic { + require.Panics(t, func() { + genPrivKey(bytes.NewReader(tt.notSoRand)) + }) + return + } + got := genPrivKey(bytes.NewReader(tt.notSoRand)) + fe := new(big.Int).SetBytes(got[:]) + require.True(t, fe.Cmp(btcSecp256k1.S256().N) < 0) + require.True(t, fe.Sign() > 0) + }) + } +} diff --git a/crypto/keys/secp256k1/secp256k1_nocgo.go b/crypto/keys/secp256k1/secp256k1_nocgo.go new file mode 100644 index 000000000000..2d605447f421 --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_nocgo.go @@ -0,0 +1,70 @@ +// +build !libsecp256k1 + +package secp256k1 + +import ( + "math/big" + + secp256k1 "github.com/btcsuite/btcd/btcec" + + "github.com/tendermint/tendermint/crypto" +) + +// used to reject malleable signatures +// see: +// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 +// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39 +var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1) + +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +// The returned signature will be of the form R || S (in lower-S form). +func (privKey *PrivKey) Sign(msg []byte) ([]byte, error) { + priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey.Key) + sig, err := priv.Sign(crypto.Sha256(msg)) + if err != nil { + return nil, err + } + sigBytes := serializeSig(sig) + return sigBytes, nil +} + +// VerifyBytes verifies a signature of the form R || S. +// It rejects signatures which are not in lower-S form. +func (pubKey *PubKey) VerifySignature(msg []byte, sigStr []byte) bool { + if len(sigStr) != 64 { + return false + } + pub, err := secp256k1.ParsePubKey(pubKey.Key, secp256k1.S256()) + if err != nil { + return false + } + // parse the signature: + signature := signatureFromBytes(sigStr) + // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + // see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 + if signature.S.Cmp(secp256k1halfN) > 0 { + return false + } + return signature.Verify(crypto.Sha256(msg), pub) +} + +// Read Signature struct from R || S. Caller needs to ensure +// that len(sigStr) == 64. +func signatureFromBytes(sigStr []byte) *secp256k1.Signature { + return &secp256k1.Signature{ + R: new(big.Int).SetBytes(sigStr[:32]), + S: new(big.Int).SetBytes(sigStr[32:64]), + } +} + +// Serialize signature to R || S. +// R, S are padded to 32 bytes respectively. +func serializeSig(sig *secp256k1.Signature) []byte { + rBytes := sig.R.Bytes() + sBytes := sig.S.Bytes() + sigBytes := make([]byte, 64) + // 0 pad the byte arrays from the left if they aren't big enough. + copy(sigBytes[32-len(rBytes):32], rBytes) + copy(sigBytes[64-len(sBytes):64], sBytes) + return sigBytes +} diff --git a/crypto/keys/secp256k1/secp256k1_nocgo_test.go b/crypto/keys/secp256k1/secp256k1_nocgo_test.go new file mode 100644 index 000000000000..4c2c856e13cc --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_nocgo_test.go @@ -0,0 +1,38 @@ +// +build !libsecp256k1 + +package secp256k1 + +import ( + "testing" + + secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/stretchr/testify/require" +) + +// Ensure that signature verification works, and that +// non-canonical signatures fail. +// Note: run with CGO_ENABLED=0 or go test -tags !cgo. +func TestSignatureVerificationAndRejectUpperS(t *testing.T) { + msg := []byte("We have lingered long enough on the shores of the cosmic ocean.") + for i := 0; i < 500; i++ { + priv := GenPrivKey() + sigStr, err := priv.Sign(msg) + require.NoError(t, err) + sig := signatureFromBytes(sigStr) + require.False(t, sig.S.Cmp(secp256k1halfN) > 0) + + pub := priv.PubKey() + require.True(t, pub.VerifySignature(msg, sigStr)) + + // malleate: + sig.S.Sub(secp256k1.S256().CurveParams.N, sig.S) + require.True(t, sig.S.Cmp(secp256k1halfN) > 0) + malSigStr := serializeSig(sig) + + require.False(t, pub.VerifySignature(msg, malSigStr), + "VerifyBytes incorrect with malleated & invalid S. sig=%v, key=%v", + sig, + priv, + ) + } +} diff --git a/crypto/keys/secp256k1/secp256k1_test.go b/crypto/keys/secp256k1/secp256k1_test.go new file mode 100644 index 000000000000..56c67f594b0b --- /dev/null +++ b/crypto/keys/secp256k1/secp256k1_test.go @@ -0,0 +1,323 @@ +package secp256k1_test + +import ( + "crypto/ecdsa" + "encoding/base64" + "encoding/hex" + "math/big" + "testing" + + btcSecp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcutil/base58" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +type keyData struct { + priv string + pub string + addr string +} + +var secpDataTable = []keyData{ + { + priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330", + pub: "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c", + addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3", + }, +} + +func TestPubKeySecp256k1Address(t *testing.T) { + for _, d := range secpDataTable { + privB, _ := hex.DecodeString(d.priv) + pubB, _ := hex.DecodeString(d.pub) + addrBbz, _, _ := base58.CheckDecode(d.addr) + addrB := crypto.Address(addrBbz) + + var priv = secp256k1.PrivKey{Key: privB} + + pubKey := priv.PubKey() + pubT, _ := pubKey.(*secp256k1.PubKey) + + addr := pubKey.Address() + assert.Equal(t, pubT, &secp256k1.PubKey{Key: pubB}, "Expected pub keys to match") + assert.Equal(t, addr, addrB, "Expected addresses to match") + } +} + +func TestSignAndValidateSecp256k1(t *testing.T) { + privKey := secp256k1.GenPrivKey() + pubKey := privKey.PubKey() + + msg := crypto.CRandBytes(1000) + sig, err := privKey.Sign(msg) + require.Nil(t, err) + assert.True(t, pubKey.VerifySignature(msg, sig)) + + // ---- + // Test cross packages verification + msgHash := crypto.Sha256(msg) + btcPrivKey, btcPubKey := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKey.Key) + // This fails: malformed signature: no header magic + // btcSig, err := secp256k1.ParseSignature(sig, secp256k1.S256()) + // require.NoError(t, err) + // assert.True(t, btcSig.Verify(msgHash, btcPubKey)) + // So we do a hacky way: + r := new(big.Int) + s := new(big.Int) + r.SetBytes(sig[:32]) + s.SetBytes(sig[32:]) + ok := ecdsa.Verify(btcPubKey.ToECDSA(), msgHash, r, s) + require.True(t, ok) + + sig2, err := btcPrivKey.Sign(msgHash) + require.NoError(t, err) + pubKey.VerifySignature(msg, sig2.Serialize()) + + // ---- + // Mutate the signature, just one bit. + sig[3] ^= byte(0x01) + assert.False(t, pubKey.VerifySignature(msg, sig)) +} + +// This test is intended to justify the removal of calls to the underlying library +// in creating the privkey. +func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { + numberOfTests := 256 + for i := 0; i < numberOfTests; i++ { + // Seed the test case with some random bytes + privKeyBytes := [32]byte{} + copy(privKeyBytes[:], crypto.CRandBytes(32)) + + // This function creates a private and public key in the underlying libraries format. + // The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes + priv, _ := btcSecp256k1.PrivKeyFromBytes(btcSecp256k1.S256(), privKeyBytes[:]) + // this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes, + // pads the bytes from the left with zero bytes. Therefore these two functions composed + // result in the identity function on privKeyBytes, hence the following equality check + // always returning true. + serializedBytes := priv.Serialize() + require.Equal(t, privKeyBytes[:], serializedBytes) + } +} + +func TestGenPrivKeyFromSecret(t *testing.T) { + // curve oder N + N := btcSecp256k1.S256().N + tests := []struct { + name string + secret []byte + }{ + {"empty secret", []byte{}}, + { + "some long secret", + []byte("We live in a society exquisitely dependent on science and technology, " + + "in which hardly anyone knows anything about science and technology."), + }, + {"another seed used in cosmos tests #1", []byte{0}}, + {"another seed used in cosmos tests #2", []byte("mySecret")}, + {"another seed used in cosmos tests #3", []byte("")}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + gotPrivKey := secp256k1.GenPrivKeyFromSecret(tt.secret) + require.NotNil(t, gotPrivKey) + // interpret as a big.Int and make sure it is a valid field element: + fe := new(big.Int).SetBytes(gotPrivKey.Key[:]) + require.True(t, fe.Cmp(N) < 0) + require.True(t, fe.Sign() > 0) + }) + } +} + +func TestPubKeyEquals(t *testing.T) { + secp256K1PubKey := secp256k1.GenPrivKey().PubKey().(*secp256k1.PubKey) + + testCases := []struct { + msg string + pubKey cryptotypes.PubKey + other cryptotypes.PubKey + expectEq bool + }{ + { + "different bytes", + secp256K1PubKey, + secp256k1.GenPrivKey().PubKey(), + false, + }, + { + "equals", + secp256K1PubKey, + &secp256k1.PubKey{ + Key: secp256K1PubKey.Key, + }, + true, + }, + { + "different types", + secp256K1PubKey, + ed25519.GenPrivKey().PubKey(), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := tc.pubKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + +func TestPrivKeyEquals(t *testing.T) { + secp256K1PrivKey := secp256k1.GenPrivKey() + + testCases := []struct { + msg string + privKey cryptotypes.PrivKey + other cryptotypes.PrivKey + expectEq bool + }{ + { + "different bytes", + secp256K1PrivKey, + secp256k1.GenPrivKey(), + false, + }, + { + "equals", + secp256K1PrivKey, + &secp256k1.PrivKey{ + Key: secp256K1PrivKey.Key, + }, + true, + }, + { + "different types", + secp256K1PrivKey, + ed25519.GenPrivKey(), + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + eq := tc.privKey.Equals(tc.other) + require.Equal(t, eq, tc.expectEq) + }) + } +} + +func TestMarshalAmino(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + privKey := secp256k1.GenPrivKey() + pubKey := privKey.PubKey().(*secp256k1.PubKey) + + testCases := []struct { + desc string + msg codec.AminoMarshaler + typ interface{} + expBinary []byte + expJSON string + }{ + { + "secp256k1 private key", + privKey, + &secp256k1.PrivKey{}, + append([]byte{32}, privKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(privKey.Bytes()) + "\"", + }, + { + "secp256k1 public key", + pubKey, + &secp256k1.PubKey{}, + append([]byte{33}, pubKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(pubKey.Bytes()) + "\"", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Do a round trip of encoding/decoding binary. + bz, err := aminoCdc.MarshalBinaryBare(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expBinary, bz) + + err = aminoCdc.UnmarshalBinaryBare(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + + // Do a round trip of encoding/decoding JSON. + bz, err = aminoCdc.MarshalJSON(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expJSON, string(bz)) + + err = aminoCdc.UnmarshalJSON(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + }) + } +} + +func TestMarshalAmino_BackwardsCompatibility(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + // Create Tendermint keys. + tmPrivKey := tmsecp256k1.GenPrivKey() + tmPubKey := tmPrivKey.PubKey() + // Create our own keys, with the same private key as Tendermint's. + privKey := &secp256k1.PrivKey{Key: []byte(tmPrivKey)} + pubKey := privKey.PubKey().(*secp256k1.PubKey) + + testCases := []struct { + desc string + tmKey interface{} + ourKey interface{} + marshalFn func(o interface{}) ([]byte, error) + }{ + { + "secp256k1 private key, binary", + tmPrivKey, + privKey, + aminoCdc.MarshalBinaryBare, + }, + { + "secp256k1 private key, JSON", + tmPrivKey, + privKey, + aminoCdc.MarshalJSON, + }, + { + "secp256k1 public key, binary", + tmPubKey, + pubKey, + aminoCdc.MarshalBinaryBare, + }, + { + "secp256k1 public key, JSON", + tmPubKey, + pubKey, + aminoCdc.MarshalJSON, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Make sure Amino encoding override is not breaking backwards compatibility. + bz1, err := tc.marshalFn(tc.tmKey) + require.NoError(t, err) + bz2, err := tc.marshalFn(tc.ourKey) + require.NoError(t, err) + require.Equal(t, bz1, bz2) + }) + } +} diff --git a/crypto/keys/types.go b/crypto/keys/types.go deleted file mode 100644 index 89f5cb2d539b..000000000000 --- a/crypto/keys/types.go +++ /dev/null @@ -1,357 +0,0 @@ -package keys - -import ( - "fmt" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/types" -) - -// Keybase exposes operations on a generic keystore -type Keybase interface { - // CRUD on the keystore - List() ([]Info, error) - // Get returns the public information about one key. - Get(name string) (Info, error) - // Get performs a by-address lookup and returns the public - // information about one key if there's any. - GetByAddress(address types.AccAddress) (Info, error) - // Delete removes a key. - Delete(name, passphrase string, skipPass bool) error - // Sign bytes, looking up the private key to use. - Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error) - - // CreateMnemonic generates a new mnemonic, derives a hierarchical deterministic - // key from that. and persists it to storage, encrypted using the provided password. - // It returns the generated mnemonic and the key Info. It returns an error if it fails to - // generate a key for the given algo type, or if another key is already stored under the - // same name. - CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) - - // CreateAccount converts a mnemonic to a private key and BIP 32 HD Path - // and persists it, encrypted with the given password. - CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error) - - // CreateLedger creates, stores, and returns a new Ledger key reference - CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) - - // CreateOffline creates, stores, and returns a new offline key reference - CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error) - - // CreateMulti creates, stores, and returns a new multsig (offline) key reference - CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) - - // The following operations will *only* work on locally-stored keys - Update(name, oldpass string, getNewpass func() (string, error)) error - - // Import imports ASCII armored Info objects. - Import(name string, armor string) (err error) - - // ImportPrivKey imports a private key in ASCII armor format. - // It returns an error if a key with the same name exists or a wrong encryption passphrase is - // supplied. - ImportPrivKey(name, armor, passphrase string) error - - // ImportPubKey imports ASCII-armored public keys. - // Store a new Info object holding a public key only, i.e. it will - // not be possible to sign with it as it lacks the secret key. - ImportPubKey(name string, armor string) (err error) - - // Export exports an Info object in ASCII armored format. - Export(name string) (armor string, err error) - - // ExportPubKey returns public keys in ASCII armored format. - // Retrieve a Info object by its name and return the public key in - // a portable format. - ExportPubKey(name string) (armor string, err error) - - // ExportPrivKey returns a private key in ASCII armored format. - // It returns an error if the key does not exist or a wrong encryption passphrase is supplied. - ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error) - - // ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API - ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) - - // SupportedAlgos returns a list of signing algorithms supported by the keybase - SupportedAlgos() []SigningAlgo - - // SupportedAlgosLedger returns a list of signing algorithms supported by the keybase's ledger integration - SupportedAlgosLedger() []SigningAlgo - - // CloseDB closes the database. - CloseDB() -} - -// KeyType reflects a human-readable type for key listing. -type KeyType uint - -// Info KeyTypes -const ( - TypeLocal KeyType = 0 - TypeLedger KeyType = 1 - TypeOffline KeyType = 2 - TypeMulti KeyType = 3 -) - -var keyTypes = map[KeyType]string{ - TypeLocal: "local", - TypeLedger: "ledger", - TypeOffline: "offline", - TypeMulti: "multi", -} - -// String implements the stringer interface for KeyType. -func (kt KeyType) String() string { - return keyTypes[kt] -} - -// Info is the publicly exposed information about a keypair -type Info interface { - // Human-readable type for key listing - GetType() KeyType - // Name of the key - GetName() string - // Public key - GetPubKey() crypto.PubKey - // Address - GetAddress() types.AccAddress - // Bip44 Path - GetPath() (*hd.BIP44Params, error) - // Algo - GetAlgo() SigningAlgo -} - -var ( - _ Info = &localInfo{} - _ Info = &ledgerInfo{} - _ Info = &offlineInfo{} - _ Info = &multiInfo{} -) - -// localInfo is the public information about a locally stored key -// Note: Algo must be last field in struct for backwards amino compatibility -type localInfo struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` - PrivKeyArmor string `json:"privkey.armor"` - Algo SigningAlgo `json:"algo"` -} - -func newLocalInfo(name string, pub crypto.PubKey, privArmor string, algo SigningAlgo) Info { - return &localInfo{ - Name: name, - PubKey: pub, - PrivKeyArmor: privArmor, - Algo: algo, - } -} - -// GetType implements Info interface -func (i localInfo) GetType() KeyType { - return TypeLocal -} - -// GetType implements Info interface -func (i localInfo) GetName() string { - return i.Name -} - -// GetType implements Info interface -func (i localInfo) GetPubKey() crypto.PubKey { - return i.PubKey -} - -// GetType implements Info interface -func (i localInfo) GetAddress() types.AccAddress { - return i.PubKey.Address().Bytes() -} - -// GetType implements Info interface -func (i localInfo) GetAlgo() SigningAlgo { - return i.Algo -} - -// GetType implements Info interface -func (i localInfo) GetPath() (*hd.BIP44Params, error) { - return nil, fmt.Errorf("BIP44 Paths are not available for this type") -} - -// ledgerInfo is the public information about a Ledger key -// Note: Algo must be last field in struct for backwards amino compatibility -type ledgerInfo struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` - Path hd.BIP44Params `json:"path"` - Algo SigningAlgo `json:"algo"` -} - -func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info { - return &ledgerInfo{ - Name: name, - PubKey: pub, - Path: path, - Algo: algo, - } -} - -// GetType implements Info interface -func (i ledgerInfo) GetType() KeyType { - return TypeLedger -} - -// GetName implements Info interface -func (i ledgerInfo) GetName() string { - return i.Name -} - -// GetPubKey implements Info interface -func (i ledgerInfo) GetPubKey() crypto.PubKey { - return i.PubKey -} - -// GetAddress implements Info interface -func (i ledgerInfo) GetAddress() types.AccAddress { - return i.PubKey.Address().Bytes() -} - -// GetPath implements Info interface -func (i ledgerInfo) GetAlgo() SigningAlgo { - return i.Algo -} - -// GetPath implements Info interface -func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) { - tmp := i.Path - return &tmp, nil -} - -// offlineInfo is the public information about an offline key -// Note: Algo must be last field in struct for backwards amino compatibility -type offlineInfo struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` - Algo SigningAlgo `json:"algo"` -} - -func newOfflineInfo(name string, pub crypto.PubKey, algo SigningAlgo) Info { - return &offlineInfo{ - Name: name, - PubKey: pub, - Algo: algo, - } -} - -// GetType implements Info interface -func (i offlineInfo) GetType() KeyType { - return TypeOffline -} - -// GetName implements Info interface -func (i offlineInfo) GetName() string { - return i.Name -} - -// GetPubKey implements Info interface -func (i offlineInfo) GetPubKey() crypto.PubKey { - return i.PubKey -} - -// GetAlgo returns the signing algorithm for the key -func (i offlineInfo) GetAlgo() SigningAlgo { - return i.Algo -} - -// GetAddress implements Info interface -func (i offlineInfo) GetAddress() types.AccAddress { - return i.PubKey.Address().Bytes() -} - -// GetPath implements Info interface -func (i offlineInfo) GetPath() (*hd.BIP44Params, error) { - return nil, fmt.Errorf("BIP44 Paths are not available for this type") -} - -type multisigPubKeyInfo struct { - PubKey crypto.PubKey `json:"pubkey"` - Weight uint `json:"weight"` -} - -// multiInfo is the public information about a multisig key -type multiInfo struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` - Threshold uint `json:"threshold"` - PubKeys []multisigPubKeyInfo `json:"pubkeys"` -} - -// NewMultiInfo creates a new multiInfo instance -func NewMultiInfo(name string, pub crypto.PubKey) Info { - multiPK := pub.(multisig.PubKeyMultisigThreshold) - - pubKeys := make([]multisigPubKeyInfo, len(multiPK.PubKeys)) - for i, pk := range multiPK.PubKeys { - // TODO: Recursively check pk for total weight? - pubKeys[i] = multisigPubKeyInfo{pk, 1} - } - - return &multiInfo{ - Name: name, - PubKey: pub, - Threshold: multiPK.K, - PubKeys: pubKeys, - } -} - -// GetType implements Info interface -func (i multiInfo) GetType() KeyType { - return TypeMulti -} - -// GetName implements Info interface -func (i multiInfo) GetName() string { - return i.Name -} - -// GetPubKey implements Info interface -func (i multiInfo) GetPubKey() crypto.PubKey { - return i.PubKey -} - -// GetAddress implements Info interface -func (i multiInfo) GetAddress() types.AccAddress { - return i.PubKey.Address().Bytes() -} - -// GetPath implements Info interface -func (i multiInfo) GetAlgo() SigningAlgo { - return MultiAlgo -} - -// GetPath implements Info interface -func (i multiInfo) GetPath() (*hd.BIP44Params, error) { - return nil, fmt.Errorf("BIP44 Paths are not available for this type") -} - -// encoding info -func marshalInfo(i Info) []byte { - return CryptoCdc.MustMarshalBinaryLengthPrefixed(i) -} - -// decoding info -func unmarshalInfo(bz []byte) (info Info, err error) { - err = CryptoCdc.UnmarshalBinaryLengthPrefixed(bz, &info) - return -} - -type ( - // DeriveKeyFunc defines the function to derive a new key from a seed and hd path - DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) - // PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key - PrivKeyGenFunc func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) - - // KeybaseOption overrides options for the db - KeybaseOption func(*kbOptions) -) diff --git a/crypto/keys/types_test.go b/crypto/keys/types_test.go deleted file mode 100644 index 5b234f0db554..000000000000 --- a/crypto/keys/types_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package keys - -import ( - "encoding/hex" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/types" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func Test_writeReadLedgerInfo(t *testing.T) { - var tmpKey secp256k1.PubKeySecp256k1 - bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") - copy(tmpKey[:], bz) - - lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1), Secp256k1) - assert.Equal(t, TypeLedger, lInfo.GetType()) - - path, err := lInfo.GetPath() - assert.NoError(t, err) - assert.Equal(t, "44'/118'/5'/0/1", path.String()) - assert.Equal(t, - "fetchpub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5t35gat", - types.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, lInfo.GetPubKey())) - - // Serialize and restore - serialized := marshalInfo(lInfo) - restoredInfo, err := unmarshalInfo(serialized) - assert.NoError(t, err) - assert.NotNil(t, restoredInfo) - - // Check both keys match - assert.Equal(t, lInfo.GetName(), restoredInfo.GetName()) - assert.Equal(t, lInfo.GetType(), restoredInfo.GetType()) - assert.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey()) - - restoredPath, err := restoredInfo.GetPath() - assert.NoError(t, err) - - assert.Equal(t, path, restoredPath) -} diff --git a/crypto/ledger/amino.go b/crypto/ledger/amino.go new file mode 100644 index 000000000000..0e717a9604f0 --- /dev/null +++ b/crypto/ledger/amino.go @@ -0,0 +1,19 @@ +package ledger + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cryptoAmino "github.com/cosmos/cosmos-sdk/crypto/codec" +) + +var cdc = codec.NewLegacyAmino() + +func init() { + RegisterAmino(cdc) + cryptoAmino.RegisterCrypto(cdc) +} + +// RegisterAmino registers all go-crypto related types in the given (amino) codec. +func RegisterAmino(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(PrivKeyLedgerSecp256k1{}, + "tendermint/PrivKeyLedgerSecp256k1", nil) +} diff --git a/crypto/ledger/encode_test.go b/crypto/ledger/encode_test.go new file mode 100644 index 000000000000..447891fde768 --- /dev/null +++ b/crypto/ledger/encode_test.go @@ -0,0 +1,63 @@ +package ledger + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +type byter interface { + Bytes() []byte +} + +func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) { + // Marshal to JSON bytes. + js, err := cdc.MarshalJSON(src) + require.Nil(t, err, "%+v", err) + if isNil { + require.Equal(t, string(js), `null`) + } else { + require.Contains(t, string(js), `"type":`) + require.Contains(t, string(js), `"value":`) + } + // Unmarshal. + err = cdc.UnmarshalJSON(js, dst) + require.Nil(t, err, "%+v", err) +} + +// nolint: govet +func ExamplePrintRegisteredTypes() { + cdc.PrintTypes(os.Stdout) + // | Type | Name | Prefix | Length | Notes | + // | ---- | ---- | ------ | ----- | ------ | + // | PrivKeyLedgerSecp256k1 | tendermint/PrivKeyLedgerSecp256k1 | 0x10CAB393 | variable | | + // | PubKey | tendermint/PubKeyEd25519 | 0x1624DE64 | variable | | + // | PubKey | tendermint/PubKeySr25519 | 0x0DFB1005 | variable | | + // | PubKey | tendermint/PubKeySecp256k1 | 0xEB5AE987 | variable | | + // | PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | | + // | PrivKey | tendermint/PrivKeyEd25519 | 0xA3288910 | variable | | + // | PrivKey | tendermint/PrivKeySr25519 | 0x2F82D78B | variable | | + // | PrivKey | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | variable | | +} + +func TestNilEncodings(t *testing.T) { + + // Check nil Signature. + var a, b []byte + checkAminoJSON(t, &a, &b, true) + require.EqualValues(t, a, b) + + // Check nil PubKey. + var c, d cryptotypes.PubKey + checkAminoJSON(t, &c, &d, true) + require.EqualValues(t, c, d) + + // Check nil PrivKey. + var e, f cryptotypes.PrivKey + checkAminoJSON(t, &e, &f, true) + require.EqualValues(t, e, f) + +} diff --git a/crypto/ledger_mock.go b/crypto/ledger/ledger_mock.go similarity index 83% rename from crypto/ledger_mock.go rename to crypto/ledger/ledger_mock.go index 21fc8a2b0f43..4f7feb2c52de 100644 --- a/crypto/ledger_mock.go +++ b/crypto/ledger/ledger_mock.go @@ -1,6 +1,6 @@ // +build ledger,test_ledger_mock -package crypto +package ledger import ( "fmt" @@ -10,12 +10,12 @@ import ( secp256k1 "github.com/tendermint/btcd/btcec" "github.com/tendermint/tendermint/crypto" - tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1" - bip39 "github.com/cosmos/go-bip39" + "github.com/cosmos/go-bip39" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/crypto/hd" + csecp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -23,7 +23,7 @@ import ( // set the discoverLedger function which is responsible for loading the Ledger // device at runtime or returning an error. func init() { - discoverLedger = func() (LedgerSECP256K1, error) { + discoverLedger = func() (SECP256K1, error) { return LedgerSECP256K1Mock{}, nil } } @@ -46,7 +46,7 @@ func (mock LedgerSECP256K1Mock) GetPublicKeySECP256K1(derivationPath []uint32) ( return nil, errors.New("Invalid derivation path") } - seed, err := bip39.NewSeedWithErrorChecking(tests.TestMnemonic, "") + seed, err := bip39.NewSeedWithErrorChecking(testutil.TestMnemonic, "") if err != nil { return nil, err } @@ -77,17 +77,18 @@ func (mock LedgerSECP256K1Mock) GetAddressPubKeySECP256K1(derivationPath []uint3 return nil, "", fmt.Errorf("error parsing public key: %v", err) } - var compressedPublicKey tmsecp256k1.PubKeySecp256k1 - copy(compressedPublicKey[:], cmp.SerializeCompressed()) + compressedPublicKey := make([]byte, csecp256k1.PubKeySize) + copy(compressedPublicKey, cmp.SerializeCompressed()) // Generate the bech32 addr using existing tmcrypto/etc. - addr := sdk.AccAddress(compressedPublicKey.Address()).String() + pub := &csecp256k1.PubKey{Key: compressedPublicKey} + addr := sdk.AccAddress(pub.Address()).String() return pk, addr, err } func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message []byte) ([]byte, error) { path := hd.NewParams(derivationPath[0], derivationPath[1], derivationPath[2], derivationPath[3] != 0, derivationPath[4]) - seed, err := bip39.NewSeedWithErrorChecking(tests.TestMnemonic, "") + seed, err := bip39.NewSeedWithErrorChecking(testutil.TestMnemonic, "") if err != nil { return nil, err } diff --git a/crypto/ledger_notavail.go b/crypto/ledger/ledger_notavail.go similarity index 85% rename from crypto/ledger_notavail.go rename to crypto/ledger/ledger_notavail.go index 8ad672720afa..66d16adcc023 100644 --- a/crypto/ledger_notavail.go +++ b/crypto/ledger/ledger_notavail.go @@ -1,7 +1,7 @@ // +build !cgo !ledger // test_ledger_mock -package crypto +package ledger import ( "github.com/pkg/errors" @@ -11,7 +11,7 @@ import ( // set the discoverLedger function which is responsible for loading the Ledger // device at runtime or returning an error. func init() { - discoverLedger = func() (LedgerSECP256K1, error) { + discoverLedger = func() (SECP256K1, error) { return nil, errors.New("support for ledger devices is not available in this executable") } } diff --git a/crypto/ledger_real.go b/crypto/ledger/ledger_real.go similarity index 86% rename from crypto/ledger_real.go rename to crypto/ledger/ledger_real.go index 93837e389aba..07f8a8e3ed6a 100644 --- a/crypto/ledger_real.go +++ b/crypto/ledger/ledger_real.go @@ -1,6 +1,6 @@ // +build cgo,ledger,!test_ledger_mock -package crypto +package ledger import ledger "github.com/cosmos/ledger-cosmos-go" @@ -8,7 +8,7 @@ import ledger "github.com/cosmos/ledger-cosmos-go" // set the discoverLedger function which is responsible for loading the Ledger // device at runtime or returning an error. func init() { - discoverLedger = func() (LedgerSECP256K1, error) { + discoverLedger = func() (SECP256K1, error) { device, err := ledger.FindLedgerCosmosUserApp() if err != nil { return nil, err diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger/ledger_secp256k1.go similarity index 75% rename from crypto/ledger_secp256k1.go rename to crypto/ledger/ledger_secp256k1.go index eb1c9efe51a6..659181d30648 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger/ledger_secp256k1.go @@ -1,4 +1,4 @@ -package crypto +package ledger import ( "fmt" @@ -8,11 +8,10 @@ import ( "github.com/pkg/errors" tmbtcec "github.com/tendermint/btcd/btcec" - tmcrypto "github.com/tendermint/tendermint/crypto" - tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/types" ) var ( @@ -25,10 +24,10 @@ type ( // discoverLedgerFn defines a Ledger discovery function that returns a // connected device or an error upon failure. Its allows a method to avoid CGO // dependencies when Ledger support is potentially not enabled. - discoverLedgerFn func() (LedgerSECP256K1, error) + discoverLedgerFn func() (SECP256K1, error) - // LedgerSECP256K1 reflects an interface a Ledger API must implement for SECP256K1 - LedgerSECP256K1 interface { + // SECP256K1 reflects an interface a Ledger API must implement for SECP256K1 + SECP256K1 interface { Close() error // Returns an uncompressed pubkey GetPublicKeySECP256K1([]uint32) ([]byte, error) @@ -44,18 +43,18 @@ type ( // CachedPubKey should be private, but we want to encode it via // go-amino so we can view the address later, even without having the // ledger attached. - CachedPubKey tmcrypto.PubKey + CachedPubKey types.PubKey Path hd.BIP44Params } ) -// NewPrivKeyLedgerSecp256k1Unsafe will generate a new key and store the public key for later use. +// NewPrivKeySecp256k1Unsafe will generate a new key and store the public key for later use. // // This function is marked as unsafe as it will retrieve a pubkey without user verification. // It can only be used to verify a pubkey but never to create new accounts/keys. In that case, -// please refer to NewPrivKeyLedgerSecp256k1 -func NewPrivKeyLedgerSecp256k1Unsafe(path hd.BIP44Params) (tmcrypto.PrivKey, error) { - device, err := getLedgerDevice() +// please refer to NewPrivKeySecp256k1 +func NewPrivKeySecp256k1Unsafe(path hd.BIP44Params) (types.LedgerPrivKey, error) { + device, err := getDevice() if err != nil { return nil, err } @@ -69,10 +68,10 @@ func NewPrivKeyLedgerSecp256k1Unsafe(path hd.BIP44Params) (tmcrypto.PrivKey, err return PrivKeyLedgerSecp256k1{pubKey, path}, nil } -// NewPrivKeyLedgerSecp256k1 will generate a new key and store the public key for later use. +// NewPrivKeySecp256k1 will generate a new key and store the public key for later use. // The request will require user confirmation and will show account and index in the device -func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params, hrp string) (tmcrypto.PrivKey, string, error) { - device, err := getLedgerDevice() +func NewPrivKeySecp256k1(path hd.BIP44Params, hrp string) (types.LedgerPrivKey, string, error) { + device, err := getDevice() if err != nil { return nil, "", err } @@ -87,13 +86,13 @@ func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params, hrp string) (tmcrypto.PrivKe } // PubKey returns the cached public key. -func (pkl PrivKeyLedgerSecp256k1) PubKey() tmcrypto.PubKey { +func (pkl PrivKeyLedgerSecp256k1) PubKey() types.PubKey { return pkl.CachedPubKey } // Sign returns a secp256k1 signature for the corresponding message func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) { - device, err := getLedgerDevice() + device, err := getDevice() if err != nil { return nil, err } @@ -102,9 +101,10 @@ func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) { return sign(device, pkl, message) } -// LedgerShowAddress triggers a ledger device to show the corresponding address. -func LedgerShowAddress(path hd.BIP44Params, expectedPubKey tmcrypto.PubKey) error { - device, err := getLedgerDevice() +// ShowAddress triggers a ledger device to show the corresponding address. +func ShowAddress(path hd.BIP44Params, expectedPubKey types.PubKey, + accountAddressPrefix string) error { + device, err := getDevice() if err != nil { return err } @@ -115,17 +115,16 @@ func LedgerShowAddress(path hd.BIP44Params, expectedPubKey tmcrypto.PubKey) erro return err } - if pubKey != expectedPubKey { + if !pubKey.Equals(expectedPubKey) { return fmt.Errorf("the key's pubkey does not match with the one retrieved from Ledger. Check that the HD path and device are the correct ones") } - config := sdk.GetConfig() - pubKey2, _, err := getPubKeyAddrSafe(device, path, config.GetBech32AccountAddrPrefix()) + pubKey2, _, err := getPubKeyAddrSafe(device, path, accountAddressPrefix) if err != nil { return err } - if pubKey2 != expectedPubKey { + if !pubKey2.Equals(expectedPubKey) { return fmt.Errorf("the key's pubkey does not match with the one retrieved from Ledger. Check that the HD path and device are the correct ones") } @@ -135,7 +134,7 @@ func LedgerShowAddress(path hd.BIP44Params, expectedPubKey tmcrypto.PubKey) erro // ValidateKey allows us to verify the sanity of a public key after loading it // from disk. func (pkl PrivKeyLedgerSecp256k1) ValidateKey() error { - device, err := getLedgerDevice() + device, err := getDevice() if err != nil { return err } @@ -155,13 +154,15 @@ func (pkl PrivKeyLedgerSecp256k1) Bytes() []byte { // Equals implements the PrivKey interface. It makes sure two private keys // refer to the same public key. -func (pkl PrivKeyLedgerSecp256k1) Equals(other tmcrypto.PrivKey) bool { +func (pkl PrivKeyLedgerSecp256k1) Equals(other types.LedgerPrivKey) bool { if otherKey, ok := other.(PrivKeyLedgerSecp256k1); ok { return pkl.CachedPubKey.Equals(otherKey.CachedPubKey) } return false } +func (pkl PrivKeyLedgerSecp256k1) Type() string { return "PrivKeyLedgerSecp256k1" } + // warnIfErrors wraps a function and writes a warning to stderr. This is required // to avoid ignoring errors when defer is used. Using defer may result in linter warnings. func warnIfErrors(f func() error) { @@ -179,7 +180,7 @@ func convertDERtoBER(signatureDER []byte) ([]byte, error) { return sigBER.Serialize(), nil } -func getLedgerDevice() (LedgerSECP256K1, error) { +func getDevice() (SECP256K1, error) { if discoverLedger == nil { return nil, errors.New("no Ledger discovery function defined") } @@ -192,7 +193,7 @@ func getLedgerDevice() (LedgerSECP256K1, error) { return device, nil } -func validateKey(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1) error { +func validateKey(device SECP256K1, pkl PrivKeyLedgerSecp256k1) error { pub, err := getPubKeyUnsafe(device, pkl.Path) if err != nil { return err @@ -211,7 +212,7 @@ func validateKey(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1) error { // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning // an error, so this should only trigger if the private key is held in memory // for a while before use. -func sign(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byte, error) { +func sign(device SECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byte, error) { err := validateKey(device, pkl) if err != nil { return nil, err @@ -233,7 +234,7 @@ func sign(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byt // // since this involves IO, it may return an error, which is not exposed // in the PubKey interface, so this function allows better error handling -func getPubKeyUnsafe(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubKey, error) { +func getPubKeyUnsafe(device SECP256K1, path hd.BIP44Params) (types.PubKey, error) { publicKey, err := device.GetPublicKeySECP256K1(path.DerivationPath()) if err != nil { return nil, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err) @@ -245,10 +246,10 @@ func getPubKeyUnsafe(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubK return nil, fmt.Errorf("error parsing public key: %v", err) } - var compressedPublicKey tmsecp256k1.PubKeySecp256k1 - copy(compressedPublicKey[:], cmp.SerializeCompressed()) + compressedPublicKey := make([]byte, secp256k1.PubKeySize) + copy(compressedPublicKey, cmp.SerializeCompressed()) - return compressedPublicKey, nil + return &secp256k1.PubKey{Key: compressedPublicKey}, nil } // getPubKeyAddr reads the pubkey and the address from a ledger device. @@ -257,7 +258,7 @@ func getPubKeyUnsafe(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubK // // Since this involves IO, it may return an error, which is not exposed // in the PubKey interface, so this function allows better error handling. -func getPubKeyAddrSafe(device LedgerSECP256K1, path hd.BIP44Params, hrp string) (tmcrypto.PubKey, string, error) { +func getPubKeyAddrSafe(device SECP256K1, path hd.BIP44Params, hrp string) (types.PubKey, string, error) { publicKey, addr, err := device.GetAddressPubKeySECP256K1(path.DerivationPath(), hrp) if err != nil { return nil, "", fmt.Errorf("address %s rejected", addr) @@ -269,8 +270,8 @@ func getPubKeyAddrSafe(device LedgerSECP256K1, path hd.BIP44Params, hrp string) return nil, "", fmt.Errorf("error parsing public key: %v", err) } - var compressedPublicKey tmsecp256k1.PubKeySecp256k1 - copy(compressedPublicKey[:], cmp.SerializeCompressed()) + compressedPublicKey := make([]byte, secp256k1.PubKeySize) + copy(compressedPublicKey, cmp.SerializeCompressed()) - return compressedPublicKey, addr, nil + return &secp256k1.PubKey{Key: compressedPublicKey}, addr, nil } diff --git a/crypto/ledger/ledger_test.go b/crypto/ledger/ledger_test.go new file mode 100644 index 000000000000..ec1b8dbedc3b --- /dev/null +++ b/crypto/ledger/ledger_test.go @@ -0,0 +1,258 @@ +package ledger + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestErrorHandling(t *testing.T) { + // first, try to generate a key, must return an error + // (no panic) + path := *hd.NewParams(44, 555, 0, false, 0) + _, err := NewPrivKeySecp256k1Unsafe(path) + require.Error(t, err) +} + +func TestPublicKeyUnsafe(t *testing.T) { + path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) + priv, err := NewPrivKeySecp256k1Unsafe(path) + require.NoError(t, err) + require.NotNil(t, priv) + + require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87", + fmt.Sprintf("%x", cdc.Amino.MustMarshalBinaryBare(priv.PubKey())), + "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + pubKeyAddr, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + addr := sdk.AccAddress(priv.PubKey().Address()).String() + require.Equal(t, "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + addr, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) +} + +func TestPublicKeyUnsafeHDPath(t *testing.T) { + expectedAnswers := []string{ + "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + "cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65", + "cosmospub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtxmrkh3d", + "cosmospub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyjr4pjs", + "cosmospub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg32rcz7", + "cosmospub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcpk5qns", + "cosmospub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545vuv8hp", + "cosmospub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydjmt66ew", + "cosmospub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzz695nw9", + "cosmospub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkgqch0r", + } + + const numIters = 10 + + privKeys := make([]types.LedgerPrivKey, numIters) + + // Check with device + for i := uint32(0); i < 10; i++ { + path := *hd.NewFundraiserParams(0, sdk.CoinType, i) + t.Logf("Checking keys at %v\n", path) + + priv, err := NewPrivKeySecp256k1Unsafe(path) + require.NoError(t, err) + require.NotNil(t, priv) + + // Check other methods + tmp := priv.(PrivKeyLedgerSecp256k1) + require.NoError(t, tmp.ValidateKey()) + (&tmp).AssertIsPrivKeyInner() + + pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) + require.NoError(t, err) + require.Equal(t, + expectedAnswers[i], pubKeyAddr, + "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + // Store and restore + serializedPk := priv.Bytes() + require.NotNil(t, serializedPk) + require.True(t, len(serializedPk) >= 50) + + privKeys[i] = priv + } + + // Now check equality + for i := 0; i < 10; i++ { + for j := 0; j < 10; j++ { + require.Equal(t, i == j, privKeys[i].Equals(privKeys[j])) + require.Equal(t, i == j, privKeys[j].Equals(privKeys[i])) + } + } +} + +func TestPublicKeySafe(t *testing.T) { + path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) + priv, addr, err := NewPrivKeySecp256k1(path, "cosmos") + + require.NoError(t, err) + require.NotNil(t, priv) + + require.Nil(t, ShowAddress(path, priv.PubKey(), sdk.GetConfig().GetBech32AccountAddrPrefix())) + + require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87", + fmt.Sprintf("%x", cdc.Amino.MustMarshalBinaryBare(priv.PubKey())), + "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + pubKeyAddr, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + require.Equal(t, "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + addr, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + addr2 := sdk.AccAddress(priv.PubKey().Address()).String() + require.Equal(t, addr, addr2) +} + +func TestPublicKeyHDPath(t *testing.T) { + expectedPubKeys := []string{ + "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + "cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65", + "cosmospub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtxmrkh3d", + "cosmospub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyjr4pjs", + "cosmospub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg32rcz7", + "cosmospub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcpk5qns", + "cosmospub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545vuv8hp", + "cosmospub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydjmt66ew", + "cosmospub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzz695nw9", + "cosmospub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkgqch0r", + } + + expectedAddrs := []string{ + "cosmos1w34k53py5v5xyluazqpq65agyajavep2rflq6h", + "cosmos19ewxwemt6uahejvwf44u7dh6tq859tkyvarh2q", + "cosmos1a07dzdjgjsntxpp75zg7cgatgq0udh3pcdcxm3", + "cosmos1qvw52lmn9gpvem8welghrkc52m3zczyhlqjsl7", + "cosmos17m78ka80fqkkw2c4ww0v4xm5nsu2drgrlm8mn2", + "cosmos1ferh9ll9c452d2p8k2v7heq084guygkn43up9e", + "cosmos10vf3sxmjg96rqq36axcphzfsl74dsntuehjlw5", + "cosmos1cq83av8cmnar79h0rg7duh9gnr7wkh228a7fxg", + "cosmos1dszhfrt226jy5rsre7e48vw9tgwe90uerfyefa", + "cosmos1734d7qsylzrdt05muhqqtpd90j8mp4y6rzch8l", + } + + const numIters = 10 + + privKeys := make([]types.LedgerPrivKey, numIters) + + // Check with device + for i := uint32(0); i < 10; i++ { + path := *hd.NewFundraiserParams(0, sdk.CoinType, i) + t.Logf("Checking keys at %s\n", path) + + priv, addr, err := NewPrivKeySecp256k1(path, "cosmos") + require.NoError(t, err) + require.NotNil(t, addr) + require.NotNil(t, priv) + + addr2 := sdk.AccAddress(priv.PubKey().Address()).String() + require.Equal(t, addr2, addr) + require.Equal(t, + expectedAddrs[i], addr, + "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + // Check other methods + tmp := priv.(PrivKeyLedgerSecp256k1) + require.NoError(t, tmp.ValidateKey()) + (&tmp).AssertIsPrivKeyInner() + + pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) + require.NoError(t, err) + require.Equal(t, + expectedPubKeys[i], pubKeyAddr, + "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + + // Store and restore + serializedPk := priv.Bytes() + require.NotNil(t, serializedPk) + require.True(t, len(serializedPk) >= 50) + + privKeys[i] = priv + } + + // Now check equality + for i := 0; i < 10; i++ { + for j := 0; j < 10; j++ { + require.Equal(t, i == j, privKeys[i].Equals(privKeys[j])) + require.Equal(t, i == j, privKeys[j].Equals(privKeys[i])) + } + } +} + +func getFakeTx(accountNumber uint32) []byte { + tmp := fmt.Sprintf( + `{"account_number":"%d","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"5000"},"memo":"memo","msgs":[[""]],"sequence":"6"}`, + accountNumber) + + return []byte(tmp) +} + +func TestSignaturesHD(t *testing.T) { + for account := uint32(0); account < 100; account += 30 { + msg := getFakeTx(account) + + path := *hd.NewFundraiserParams(account, sdk.CoinType, account/5) + t.Logf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path) + + priv, err := NewPrivKeySecp256k1Unsafe(path) + require.NoError(t, err) + + pub := priv.PubKey() + sig, err := priv.Sign(msg) + require.NoError(t, err) + + valid := pub.VerifySignature(msg, sig) + require.True(t, valid, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic) + } +} + +func TestRealDeviceSecp256k1(t *testing.T) { + msg := getFakeTx(50) + path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) + priv, err := NewPrivKeySecp256k1Unsafe(path) + require.NoError(t, err) + + pub := priv.PubKey() + sig, err := priv.Sign(msg) + require.NoError(t, err) + + valid := pub.VerifySignature(msg, sig) + require.True(t, valid) + + // now, let's serialize the public key and make sure it still works + bs := cdc.Amino.MustMarshalBinaryBare(priv.PubKey()) + pub2, err := legacy.PubKeyFromBytes(bs) + require.Nil(t, err, "%+v", err) + + // make sure we get the same pubkey when we load from disk + require.Equal(t, pub, pub2) + + // signing with the loaded key should match the original pubkey + sig, err = priv.Sign(msg) + require.NoError(t, err) + valid = pub.VerifySignature(msg, sig) + require.True(t, valid) + + // make sure pubkeys serialize properly as well + bs = legacy.Cdc.MustMarshalBinaryBare(pub) + bpub, err := legacy.PubKeyFromBytes(bs) + require.NoError(t, err) + require.Equal(t, pub, bpub) +} diff --git a/crypto/ledger_test.go b/crypto/ledger_test.go deleted file mode 100644 index 0cf24329d0e1..000000000000 --- a/crypto/ledger_test.go +++ /dev/null @@ -1,257 +0,0 @@ -package crypto - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - tmcrypto "github.com/tendermint/tendermint/crypto" - cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" - - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/tests" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestLedgerErrorHandling(t *testing.T) { - // first, try to generate a key, must return an error - // (no panic) - path := *hd.NewParams(44, 555, 0, false, 0) - _, err := NewPrivKeyLedgerSecp256k1Unsafe(path) - require.Error(t, err) -} - -func TestPublicKeyUnsafe(t *testing.T) { - path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path) - require.Nil(t, err, "%s", err) - require.NotNil(t, priv) - - require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87", - fmt.Sprintf("%x", priv.PubKey().Bytes()), - "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) - require.NoError(t, err) - require.Equal(t, "fetchpub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw2k3x89", - pubKeyAddr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - addr := sdk.AccAddress(priv.PubKey().Address()).String() - require.Equal(t, "fetch1w34k53py5v5xyluazqpq65agyajavep2s5kycq", - addr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic) -} - -func TestPublicKeyUnsafeHDPath(t *testing.T) { - expectedAnswers := []string{ - "fetchpub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw2k3x89", - "fetchpub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxv627fq7", - "fetchpub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtx7fynt8", - "fetchpub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyhf89g6", - "fetchpub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg5q3uc5", - "fetchpub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcyuxyf6", - "fetchpub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545fk7rdt", - "fetchpub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydj7pg7ry", - "fetchpub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzzl0xh50", - "fetchpub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkd22n4f", - } - - const numIters = 10 - - privKeys := make([]tmcrypto.PrivKey, numIters) - - // Check with device - for i := uint32(0); i < 10; i++ { - path := *hd.NewFundraiserParams(0, sdk.CoinType, i) - fmt.Printf("Checking keys at %v\n", path) - - priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path) - require.Nil(t, err, "%s", err) - require.NotNil(t, priv) - - // Check other methods - require.NoError(t, priv.(PrivKeyLedgerSecp256k1).ValidateKey()) - tmp := priv.(PrivKeyLedgerSecp256k1) - (&tmp).AssertIsPrivKeyInner() - - pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) - require.NoError(t, err) - require.Equal(t, - expectedAnswers[i], pubKeyAddr, - "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - // Store and restore - serializedPk := priv.Bytes() - require.NotNil(t, serializedPk) - require.True(t, len(serializedPk) >= 50) - - privKeys[i] = priv - } - - // Now check equality - for i := 0; i < 10; i++ { - for j := 0; j < 10; j++ { - require.Equal(t, i == j, privKeys[i].Equals(privKeys[j])) - require.Equal(t, i == j, privKeys[j].Equals(privKeys[i])) - } - } -} - -func TestPublicKeySafe(t *testing.T) { - path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos") - - require.Nil(t, err, "%s", err) - require.NotNil(t, priv) - - require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87", - fmt.Sprintf("%x", priv.PubKey().Bytes()), - "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) - require.NoError(t, err) - require.Equal(t, "fetchpub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw2k3x89", - pubKeyAddr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - require.Equal(t, "fetch1w34k53py5v5xyluazqpq65agyajavep2s5kycq", - addr, "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - addr2 := sdk.AccAddress(priv.PubKey().Address()).String() - require.Equal(t, addr, addr2) -} - -func TestPublicKeyHDPath(t *testing.T) { - expectedPubKeys := []string{ - "fetchpub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw2k3x89", - "fetchpub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxv627fq7", - "fetchpub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtx7fynt8", - "fetchpub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyhf89g6", - "fetchpub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg5q3uc5", - "fetchpub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcyuxyf6", - "fetchpub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545fk7rdt", - "fetchpub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydj7pg7ry", - "fetchpub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzzl0xh50", - "fetchpub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkd22n4f", - } - - expectedAddrs := []string{ - "fetch1w34k53py5v5xyluazqpq65agyajavep2s5kycq", - "fetch19ewxwemt6uahejvwf44u7dh6tq859tkylq2ngh", - "fetch1a07dzdjgjsntxpp75zg7cgatgq0udh3pts3zex", - "fetch1qvw52lmn9gpvem8welghrkc52m3zczyhvam5af", - "fetch17m78ka80fqkkw2c4ww0v4xm5nsu2drgrvxwl3a", - "fetch1ferh9ll9c452d2p8k2v7heq084guygknxv498w", - "fetch10vf3sxmjg96rqq36axcphzfsl74dsntu22mmvr", - "fetch1cq83av8cmnar79h0rg7duh9gnr7wkh225qhdyl", - "fetch1dszhfrt226jy5rsre7e48vw9tgwe90ues5dat2", - "fetch1734d7qsylzrdt05muhqqtpd90j8mp4y6sl3n9g", - } - - const numIters = 10 - - privKeys := make([]tmcrypto.PrivKey, numIters) - - // Check with device - for i := uint32(0); i < 10; i++ { - path := *hd.NewFundraiserParams(0, sdk.CoinType, i) - fmt.Printf("Checking keys at %v\n", path) - - priv, addr, err := NewPrivKeyLedgerSecp256k1(path, "cosmos") - require.Nil(t, err, "%s", err) - require.NotNil(t, addr) - require.NotNil(t, priv) - - addr2 := sdk.AccAddress(priv.PubKey().Address()).String() - require.Equal(t, addr2, addr) - require.Equal(t, - expectedAddrs[i], addr, - "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - // Check other methods - require.NoError(t, priv.(PrivKeyLedgerSecp256k1).ValidateKey()) - tmp := priv.(PrivKeyLedgerSecp256k1) - (&tmp).AssertIsPrivKeyInner() - - pubKeyAddr, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, priv.PubKey()) - require.NoError(t, err) - require.Equal(t, - expectedPubKeys[i], pubKeyAddr, - "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - - // Store and restore - serializedPk := priv.Bytes() - require.NotNil(t, serializedPk) - require.True(t, len(serializedPk) >= 50) - - privKeys[i] = priv - } - - // Now check equality - for i := 0; i < 10; i++ { - for j := 0; j < 10; j++ { - require.Equal(t, i == j, privKeys[i].Equals(privKeys[j])) - require.Equal(t, i == j, privKeys[j].Equals(privKeys[i])) - } - } -} - -func getFakeTx(accountNumber uint32) []byte { - tmp := fmt.Sprintf( - `{"account_number":"%d","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"5000"},"memo":"memo","msgs":[[""]],"sequence":"6"}`, - accountNumber) - - return []byte(tmp) -} - -func TestSignaturesHD(t *testing.T) { - for account := uint32(0); account < 100; account += 30 { - msg := getFakeTx(account) - - path := *hd.NewFundraiserParams(account, sdk.CoinType, account/5) - fmt.Printf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path) - - priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path) - require.Nil(t, err, "%s", err) - - pub := priv.PubKey() - sig, err := priv.Sign(msg) - require.Nil(t, err) - - valid := pub.VerifyBytes(msg, sig) - require.True(t, valid, "Is your device using test mnemonic: %s ?", tests.TestMnemonic) - } -} - -func TestRealLedgerSecp256k1(t *testing.T) { - msg := getFakeTx(50) - path := *hd.NewFundraiserParams(0, sdk.CoinType, 0) - priv, err := NewPrivKeyLedgerSecp256k1Unsafe(path) - require.Nil(t, err, "%s", err) - - pub := priv.PubKey() - sig, err := priv.Sign(msg) - require.Nil(t, err) - - valid := pub.VerifyBytes(msg, sig) - require.True(t, valid) - - // now, let's serialize the public key and make sure it still works - bs := priv.PubKey().Bytes() - pub2, err := cryptoAmino.PubKeyFromBytes(bs) - require.Nil(t, err, "%+v", err) - - // make sure we get the same pubkey when we load from disk - require.Equal(t, pub, pub2) - - // signing with the loaded key should match the original pubkey - sig, err = priv.Sign(msg) - require.Nil(t, err) - valid = pub.VerifyBytes(msg, sig) - require.True(t, valid) - - // make sure pubkeys serialize properly as well - bs = pub.Bytes() - bpub, err := cryptoAmino.PubKeyFromBytes(bs) - require.NoError(t, err) - require.Equal(t, pub, bpub) -} diff --git a/crypto/types/compact_bit_array.go b/crypto/types/compact_bit_array.go new file mode 100644 index 000000000000..cf17cefe5851 --- /dev/null +++ b/crypto/types/compact_bit_array.go @@ -0,0 +1,259 @@ +package types + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "math" + "regexp" + "strings" +) + +// 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 amino encoding. +// This is not thread safe, and is not intended for concurrent usage. + +// NewCompactBitArray returns a new compact bit array. +// It returns nil if the number of bits is zero, or if there is any overflow +// in the arithmetic to encounter for the number of its elements: (bits+7)/8, +// or if the number of elements will be an unreasonably large number like +// > maxint32 aka >2**31. +func NewCompactBitArray(bits int) *CompactBitArray { + if bits <= 0 { + return nil + } + nElems := (bits + 7) / 8 + if nElems <= 0 || nElems > math.MaxInt32 { + // We encountered an overflow here, and shouldn't pass negatives + // to make, nor should we allow unreasonable limits > maxint32. + // See https://github.com/cosmos/cosmos-sdk/issues/9162 + return nil + } + return &CompactBitArray{ + ExtraBitsStored: uint32(bits % 8), + Elems: make([]byte, nElems), + } +} + +// Count returns the number of bits in the bitarray +func (bA *CompactBitArray) Count() int { + if bA == nil { + return 0 + } else if bA.ExtraBitsStored == uint32(0) { + return len(bA.Elems) * 8 + } + + return (len(bA.Elems)-1)*8 + int(bA.ExtraBitsStored) +} + +// GetIndex returns the bit at index i within the bit array. +// The behavior is undefined if i >= bA.Count() +func (bA *CompactBitArray) GetIndex(i int) bool { + if bA == nil { + return false + } + if i >= bA.Count() { + return false + } + + return bA.Elems[i>>3]&(uint8(1)< 0 +} + +// SetIndex sets the bit at index i within the bit array. +// The behavior is undefined if i >= bA.Count() +func (bA *CompactBitArray) SetIndex(i int, v bool) bool { + if bA == nil { + return false + } + + if i >= bA.Count() { + return false + } + + if v { + bA.Elems[i>>3] |= (uint8(1) << uint8(7-(i%8))) + } else { + bA.Elems[i>>3] &= ^(uint8(1) << uint8(7-(i%8))) + } + + return true +} + +// NumTrueBitsBefore returns the number of bits set to true before the +// given index. e.g. if bA = _XX__XX, NumOfTrueBitsBefore(4) = 2, since +// there are two bits set to true before index 4. +func (bA *CompactBitArray) NumTrueBitsBefore(index int) int { + numTrueValues := 0 + for i := 0; i < index; i++ { + if bA.GetIndex(i) { + numTrueValues++ + } + } + + return numTrueValues +} + +// Copy returns a copy of the provided bit array. +func (bA *CompactBitArray) Copy() *CompactBitArray { + if bA == nil { + return nil + } + + c := make([]byte, len(bA.Elems)) + copy(c, bA.Elems) + + return &CompactBitArray{ + ExtraBitsStored: bA.ExtraBitsStored, + Elems: c, + } +} + +// String returns a string representation of CompactBitArray: BA{}, +// where is a sequence of 'x' (1) and '_' (0). +// The includes spaces and newlines to help people. +// For a simple sequence of 'x' and '_' characters with no spaces or newlines, +// see the MarshalJSON() method. +// Example: "BA{_x_}" or "nil-BitArray" for nil. +func (bA *CompactBitArray) String() string { return bA.StringIndented("") } + +// StringIndented returns the same thing as String(), but applies the indent +// at every 10th bit, and twice at every 50th bit. +func (bA *CompactBitArray) StringIndented(indent string) string { + if bA == nil { + return "nil-BitArray" + } + lines := []string{} + bits := "" + size := bA.Count() + for i := 0; i < size; i++ { + if bA.GetIndex(i) { + bits += "x" + } else { + bits += "_" + } + + if i%100 == 99 { + lines = append(lines, bits) + bits = "" + } + + if i%10 == 9 { + bits += indent + } + + if i%50 == 49 { + bits += indent + } + } + + if len(bits) > 0 { + lines = append(lines, bits) + } + + return fmt.Sprintf("BA{%v:%v}", size, strings.Join(lines, indent)) +} + +// MarshalJSON implements json.Marshaler interface by marshaling bit array +// using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit. +func (bA *CompactBitArray) MarshalJSON() ([]byte, error) { + if bA == nil { + return []byte("null"), nil + } + + bits := `"` + size := bA.Count() + for i := 0; i < size; i++ { + if bA.GetIndex(i) { + bits += `x` + } else { + bits += `_` + } + } + + bits += `"` + + return []byte(bits), nil +} + +var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`) + +// UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom +// JSON description. +func (bA *CompactBitArray) UnmarshalJSON(bz []byte) error { + b := string(bz) + if b == "null" { + // This is required e.g. for encoding/json when decoding + // into a pointer with pre-allocated BitArray. + bA.ExtraBitsStored = 0 + bA.Elems = nil + + return nil + } + + match := bitArrayJSONRegexp.FindStringSubmatch(b) + if match == nil { + return fmt.Errorf("bitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b) + } + + bits := match[1] + + // Construct new CompactBitArray and copy over. + numBits := len(bits) + bA2 := NewCompactBitArray(numBits) + for i := 0; i < numBits; i++ { + if bits[i] == 'x' { + bA2.SetIndex(i, true) + } + } + *bA = *bA2 + + return nil +} + +// CompactMarshal is a space efficient encoding for CompactBitArray. +// It is not amino compatible. +func (bA *CompactBitArray) CompactMarshal() []byte { + size := bA.Count() + if size <= 0 { + return []byte("null") + } + + bz := make([]byte, 0, size/8) + // length prefix number of bits, not number of bytes. This difference + // takes 3-4 bits in encoding, as opposed to instead encoding the number of + // bytes (saving 3-4 bits) and including the offset as a full byte. + bz = appendUvarint(bz, uint64(size)) + bz = append(bz, bA.Elems...) + + return bz +} + +// CompactUnmarshal is a space efficient decoding for CompactBitArray. +// It is not amino compatible. +func CompactUnmarshal(bz []byte) (*CompactBitArray, error) { + if len(bz) < 2 { + return nil, errors.New("compact bit array: invalid compact unmarshal size") + } else if bytes.Equal(bz, []byte("null")) { + return NewCompactBitArray(0), nil + } + + size, n := binary.Uvarint(bz) + bz = bz[n:] + + if len(bz) != int(size+7)/8 { + return nil, errors.New("compact bit array: invalid compact unmarshal size") + } + + bA := &CompactBitArray{uint32(size % 8), bz} + + return bA, nil +} + +func appendUvarint(b []byte, x uint64) []byte { + var a [binary.MaxVarintLen64]byte + n := binary.PutUvarint(a[:], x) + + return append(b, a[:n]...) +} diff --git a/crypto/types/compact_bit_array_test.go b/crypto/types/compact_bit_array_test.go new file mode 100644 index 000000000000..f108b38129a9 --- /dev/null +++ b/crypto/types/compact_bit_array_test.go @@ -0,0 +1,247 @@ +package types + +import ( + "encoding/json" + "fmt" + "math" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + tmrand "github.com/tendermint/tendermint/libs/rand" +) + +func randCompactBitArray(bits int) (*CompactBitArray, []byte) { + numBytes := (bits + 7) / 8 + src := tmrand.Bytes((bits + 7) / 8) + bA := NewCompactBitArray(bits) + + for i := 0; i < numBytes-1; i++ { + for j := uint8(0); j < 8; j++ { + bA.SetIndex(i*8+int(j), src[i]&(uint8(1)<<(8-j)) > 0) + } + } + // Set remaining bits + for i := uint32(0); i < 8-bA.ExtraBitsStored; i++ { + bA.SetIndex(numBytes*8+int(i), src[numBytes-1]&(uint8(1)<<(8-i)) > 0) + } + return bA, src +} + +func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { + bitList := []int{-127, -128, -1 << 31} + for _, bits := range bitList { + bA := NewCompactBitArray(bits) + require.Nil(t, bA) + } +} + +func TestJSONMarshalUnmarshal(t *testing.T) { + + bA1 := NewCompactBitArray(0) + bA2 := NewCompactBitArray(1) + + bA3 := NewCompactBitArray(1) + bA3.SetIndex(0, true) + + bA4 := NewCompactBitArray(5) + bA4.SetIndex(0, true) + bA4.SetIndex(1, true) + + bA5 := NewCompactBitArray(9) + bA5.SetIndex(0, true) + bA5.SetIndex(1, true) + bA5.SetIndex(8, true) + + bA6 := NewCompactBitArray(16) + bA6.SetIndex(0, true) + bA6.SetIndex(1, true) + bA6.SetIndex(8, false) + bA6.SetIndex(15, true) + + testCases := []struct { + bA *CompactBitArray + marshalledBA string + }{ + {nil, `null`}, + {bA1, `null`}, + {bA2, `"_"`}, + {bA3, `"x"`}, + {bA4, `"xx___"`}, + {bA5, `"xx______x"`}, + {bA6, `"xx_____________x"`}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.bA.String(), func(t *testing.T) { + bz, err := json.Marshal(tc.bA) + require.NoError(t, err) + + assert.Equal(t, tc.marshalledBA, string(bz)) + + var unmarshalledBA *CompactBitArray + err = json.Unmarshal(bz, &unmarshalledBA) + require.NoError(t, err) + + if tc.bA == nil { + require.Nil(t, unmarshalledBA) + } else { + require.NotNil(t, unmarshalledBA) + assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) + if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) { + assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) + } + } + }) + } +} + +func TestCompactMarshalUnmarshal(t *testing.T) { + bA1 := NewCompactBitArray(0) + bA2 := NewCompactBitArray(1) + + bA3 := NewCompactBitArray(1) + bA3.SetIndex(0, true) + + bA4 := NewCompactBitArray(5) + bA4.SetIndex(0, true) + bA4.SetIndex(1, true) + + bA5 := NewCompactBitArray(9) + bA5.SetIndex(0, true) + bA5.SetIndex(1, true) + bA5.SetIndex(8, true) + + bA6 := NewCompactBitArray(16) + bA6.SetIndex(0, true) + bA6.SetIndex(1, true) + bA6.SetIndex(8, false) + bA6.SetIndex(15, true) + + testCases := []struct { + bA *CompactBitArray + marshalledBA []byte + }{ + {nil, []byte("null")}, + {bA1, []byte("null")}, + {bA2, []byte{byte(1), byte(0)}}, + {bA3, []byte{byte(1), byte(128)}}, + {bA4, []byte{byte(5), byte(192)}}, + {bA5, []byte{byte(9), byte(192), byte(128)}}, + {bA6, []byte{byte(16), byte(192), byte(1)}}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.bA.String(), func(t *testing.T) { + bz := tc.bA.CompactMarshal() + + assert.Equal(t, tc.marshalledBA, bz) + + unmarshalledBA, err := CompactUnmarshal(bz) + require.NoError(t, err) + if tc.bA == nil { + require.Nil(t, unmarshalledBA) + } else { + require.NotNil(t, unmarshalledBA) + assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) + if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) { + assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems) + } + } + }) + } +} + +func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) { + testCases := []struct { + marshalledBA string + bAIndex []int + trueValueIndex []int + }{ + {`"_____"`, []int{0, 1, 2, 3, 4}, []int{0, 0, 0, 0, 0}}, + {`"x"`, []int{0}, []int{0}}, + {`"_x"`, []int{1}, []int{0}}, + {`"x___xxxx"`, []int{0, 4, 5, 6, 7}, []int{0, 1, 2, 3, 4}}, + {`"__x_xx_x__x_x___"`, []int{2, 4, 5, 7, 10, 12}, []int{0, 1, 2, 3, 4, 5}}, + {`"______________xx"`, []int{14, 15}, []int{0, 1}}, + } + for tcIndex, tc := range testCases { + tc := tc + tcIndex := tcIndex + t.Run(tc.marshalledBA, func(t *testing.T) { + var bA *CompactBitArray + err := json.Unmarshal([]byte(tc.marshalledBA), &bA) + require.NoError(t, err) + + for i := 0; i < len(tc.bAIndex); i++ { + + require.Equal(t, tc.trueValueIndex[i], bA.NumTrueBitsBefore(tc.bAIndex[i]), "tc %d, i %d", tcIndex, i) + } + }) + } +} + +func TestCompactBitArrayGetSetIndex(t *testing.T) { + r := rand.New(rand.NewSource(100)) + numTests := 10 + numBitsPerArr := 100 + for i := 0; i < numTests; i++ { + bits := r.Intn(1000) + bA, _ := randCompactBitArray(bits) + + for j := 0; j < numBitsPerArr; j++ { + copy := bA.Copy() + index := r.Intn(bits) + val := (r.Int63() % 2) == 0 + bA.SetIndex(index, val) + require.Equal(t, val, bA.GetIndex(index), "bA.SetIndex(%d, %v) failed on bit array: %s", index, val, copy) + } + } +} + +func BenchmarkNumTrueBitsBefore(b *testing.B) { + ba, _ := randCompactBitArray(100) + + b.Run("new", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + ba.NumTrueBitsBefore(90) + } + }) +} + +func TestNewCompactBitArrayCrashWithLimits(t *testing.T) { + if testing.Short() { + t.Skip("This test can be expensive in memory") + } + tests := []struct { + in int + mustPass bool + }{ + {int(^uint(0) >> 30), false}, + {int(^uint(0) >> 1), false}, + {int(^uint(0) >> 2), false}, + {int(math.MaxInt32), true}, + {int(math.MaxInt32) + 1, true}, + {int(math.MaxInt32) + 2, true}, + {int(math.MaxInt32) - 7, true}, + {int(math.MaxInt32) + 24, true}, + {int(math.MaxInt32) * 9, false}, // results in >=maxint after (bits+7)/8 + {1, true}, + {0, false}, + } + + for _, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("%d", tt.in), func(t *testing.T) { + got := NewCompactBitArray(tt.in) + if g := got != nil; g != tt.mustPass { + t.Fatalf("got!=nil=%t, want=%t", g, tt.mustPass) + } + }) + } +} diff --git a/crypto/types/multisig.pb.go b/crypto/types/multisig.pb.go new file mode 100644 index 000000000000..b9c907ade0a4 --- /dev/null +++ b/crypto/types/multisig.pb.go @@ -0,0 +1,550 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crypto/multisig/v1beta1/multisig.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 + +// 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. +type MultiSignature struct { + Signatures [][]byte `protobuf:"bytes,1,rep,name=signatures,proto3" json:"signatures,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *MultiSignature) Reset() { *m = MultiSignature{} } +func (m *MultiSignature) String() string { return proto.CompactTextString(m) } +func (*MultiSignature) ProtoMessage() {} +func (*MultiSignature) Descriptor() ([]byte, []int) { + return fileDescriptor_1177bdf7025769be, []int{0} +} +func (m *MultiSignature) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MultiSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MultiSignature.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 *MultiSignature) XXX_Merge(src proto.Message) { + xxx_messageInfo_MultiSignature.Merge(m, src) +} +func (m *MultiSignature) XXX_Size() int { + return m.Size() +} +func (m *MultiSignature) XXX_DiscardUnknown() { + xxx_messageInfo_MultiSignature.DiscardUnknown(m) +} + +var xxx_messageInfo_MultiSignature proto.InternalMessageInfo + +func (m *MultiSignature) GetSignatures() [][]byte { + if m != nil { + return m.Signatures + } + return nil +} + +// 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. +type CompactBitArray struct { + ExtraBitsStored uint32 `protobuf:"varint,1,opt,name=extra_bits_stored,json=extraBitsStored,proto3" json:"extra_bits_stored,omitempty"` + Elems []byte `protobuf:"bytes,2,opt,name=elems,proto3" json:"elems,omitempty"` +} + +func (m *CompactBitArray) Reset() { *m = CompactBitArray{} } +func (*CompactBitArray) ProtoMessage() {} +func (*CompactBitArray) Descriptor() ([]byte, []int) { + return fileDescriptor_1177bdf7025769be, []int{1} +} +func (m *CompactBitArray) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CompactBitArray) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CompactBitArray.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 *CompactBitArray) XXX_Merge(src proto.Message) { + xxx_messageInfo_CompactBitArray.Merge(m, src) +} +func (m *CompactBitArray) XXX_Size() int { + return m.Size() +} +func (m *CompactBitArray) XXX_DiscardUnknown() { + xxx_messageInfo_CompactBitArray.DiscardUnknown(m) +} + +var xxx_messageInfo_CompactBitArray proto.InternalMessageInfo + +func (m *CompactBitArray) GetExtraBitsStored() uint32 { + if m != nil { + return m.ExtraBitsStored + } + return 0 +} + +func (m *CompactBitArray) GetElems() []byte { + if m != nil { + return m.Elems + } + return nil +} + +func init() { + proto.RegisterType((*MultiSignature)(nil), "cosmos.crypto.multisig.v1beta1.MultiSignature") + proto.RegisterType((*CompactBitArray)(nil), "cosmos.crypto.multisig.v1beta1.CompactBitArray") +} + +func init() { + proto.RegisterFile("cosmos/crypto/multisig/v1beta1/multisig.proto", fileDescriptor_1177bdf7025769be) +} + +var fileDescriptor_1177bdf7025769be = []byte{ + // 269 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0x31, 0x4f, 0x83, 0x50, + 0x14, 0x85, 0x79, 0x5a, 0x1d, 0x5e, 0xaa, 0x8d, 0xa4, 0x03, 0x71, 0x78, 0x25, 0x9d, 0xd0, 0xa4, + 0x90, 0xc6, 0xc4, 0xa1, 0x9b, 0x74, 0x76, 0xa1, 0x93, 0x2e, 0x0d, 0xd0, 0x17, 0x7c, 0xb1, 0x78, + 0xc9, 0xbb, 0x17, 0x23, 0xff, 0xc2, 0xd1, 0x51, 0xff, 0x8d, 0x23, 0xa3, 0xa3, 0x81, 0x3f, 0x62, + 0xfa, 0x90, 0xa6, 0xd3, 0xbd, 0xe7, 0x9c, 0xef, 0x0e, 0xf7, 0xf0, 0x59, 0x0a, 0x98, 0x03, 0x06, + 0xa9, 0xae, 0x0a, 0x82, 0x20, 0x2f, 0xb7, 0xa4, 0x50, 0x65, 0xc1, 0xeb, 0x3c, 0x91, 0x14, 0xcf, + 0xf7, 0x86, 0x5f, 0x68, 0x20, 0xb0, 0x45, 0x87, 0xfb, 0x1d, 0xee, 0xef, 0xd3, 0x7f, 0xfc, 0x72, + 0x9c, 0x41, 0x06, 0x06, 0x0d, 0x76, 0x5b, 0x77, 0x35, 0xbd, 0xe5, 0xe7, 0xf7, 0x3b, 0x72, 0xa5, + 0xb2, 0x97, 0x98, 0x4a, 0x2d, 0x6d, 0xc1, 0x39, 0xf6, 0x02, 0x1d, 0xe6, 0x1e, 0x7b, 0xc3, 0xe8, + 0xc0, 0x59, 0x0c, 0xea, 0xaf, 0x09, 0x9b, 0x3e, 0xf0, 0xd1, 0x12, 0xf2, 0x22, 0x4e, 0x29, 0x54, + 0x74, 0xa7, 0x75, 0x5c, 0xd9, 0xd7, 0xfc, 0x42, 0xbe, 0x91, 0x8e, 0xd7, 0x89, 0x22, 0x5c, 0x23, + 0x81, 0x96, 0x1b, 0x87, 0xb9, 0xcc, 0x3b, 0x8b, 0x46, 0x26, 0x08, 0x15, 0xe1, 0xca, 0xd8, 0xf6, + 0x98, 0x9f, 0xc8, 0xad, 0xcc, 0xd1, 0x39, 0x72, 0x99, 0x37, 0x8c, 0x3a, 0xb1, 0x18, 0x7c, 0x7c, + 0x4e, 0xac, 0x70, 0xf9, 0xdd, 0x08, 0x56, 0x37, 0x82, 0xfd, 0x36, 0x82, 0xbd, 0xb7, 0xc2, 0xaa, + 0x5b, 0x61, 0xfd, 0xb4, 0xc2, 0x7a, 0xbc, 0xca, 0x14, 0x3d, 0x95, 0x89, 0x9f, 0x42, 0x1e, 0xf4, + 0xe5, 0x98, 0x31, 0xc3, 0xcd, 0x73, 0xdf, 0x13, 0x55, 0x85, 0xc4, 0xe4, 0xd4, 0xbc, 0x77, 0xf3, + 0x17, 0x00, 0x00, 0xff, 0xff, 0xba, 0x52, 0xb5, 0x1f, 0x45, 0x01, 0x00, 0x00, +} + +func (m *MultiSignature) 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 *MultiSignature) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MultiSignature) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintMultisig(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *CompactBitArray) 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 *CompactBitArray) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CompactBitArray) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Elems) > 0 { + i -= len(m.Elems) + copy(dAtA[i:], m.Elems) + i = encodeVarintMultisig(dAtA, i, uint64(len(m.Elems))) + i-- + dAtA[i] = 0x12 + } + if m.ExtraBitsStored != 0 { + i = encodeVarintMultisig(dAtA, i, uint64(m.ExtraBitsStored)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMultisig(dAtA []byte, offset int, v uint64) int { + offset -= sovMultisig(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MultiSignature) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovMultisig(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CompactBitArray) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExtraBitsStored != 0 { + n += 1 + sovMultisig(uint64(m.ExtraBitsStored)) + } + l = len(m.Elems) + if l > 0 { + n += 1 + l + sovMultisig(uint64(l)) + } + return n +} + +func sovMultisig(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMultisig(x uint64) (n int) { + return sovMultisig(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MultiSignature) 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 ErrIntOverflowMultisig + } + 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: MultiSignature: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MultiSignature: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultisig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMultisig + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMultisig + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMultisig(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMultisig + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CompactBitArray) 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 ErrIntOverflowMultisig + } + 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: CompactBitArray: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CompactBitArray: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtraBitsStored", wireType) + } + m.ExtraBitsStored = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultisig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExtraBitsStored |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Elems", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMultisig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMultisig + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMultisig + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Elems = append(m.Elems[:0], dAtA[iNdEx:postIndex]...) + if m.Elems == nil { + m.Elems = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMultisig(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMultisig + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMultisig(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, ErrIntOverflowMultisig + } + 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, ErrIntOverflowMultisig + } + 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, ErrIntOverflowMultisig + } + 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, ErrInvalidLengthMultisig + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMultisig + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMultisig + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMultisig = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMultisig = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMultisig = fmt.Errorf("proto: unexpected end of group") +) diff --git a/crypto/types/multisig/multisignature.go b/crypto/types/multisig/multisignature.go new file mode 100644 index 000000000000..20e3ef1129e4 --- /dev/null +++ b/crypto/types/multisig/multisignature.go @@ -0,0 +1,88 @@ +package multisig + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// AminoMultisignature is used to represent amino multi-signatures for StdTx's. +// It is assumed that all signatures were made with SIGN_MODE_LEGACY_AMINO_JSON. +// Sigs is a list of signatures, sorted by corresponding index. +type AminoMultisignature struct { + BitArray *types.CompactBitArray + Sigs [][]byte +} + +// NewMultisig returns a new MultiSignatureData +func NewMultisig(n int) *signing.MultiSignatureData { + return &signing.MultiSignatureData{ + BitArray: types.NewCompactBitArray(n), + Signatures: make([]signing.SignatureData, 0, n), + } +} + +// GetIndex returns the index of pk in keys. Returns -1 if not found +func getIndex(pk types.PubKey, keys []types.PubKey) int { + for i := 0; i < len(keys); i++ { + if pk.Equals(keys[i]) { + return i + } + } + return -1 +} + +// AddSignature adds a signature to the multisig, at the corresponding index. +// If the signature already exists, replace it. +func AddSignature(mSig *signing.MultiSignatureData, sig signing.SignatureData, index int) { + newSigIndex := mSig.BitArray.NumTrueBitsBefore(index) + // Signature already exists, just replace the value there + if mSig.BitArray.GetIndex(index) { + mSig.Signatures[newSigIndex] = sig + return + } + mSig.BitArray.SetIndex(index, true) + // Optimization if the index is the greatest index + if newSigIndex == len(mSig.Signatures) { + mSig.Signatures = append(mSig.Signatures, sig) + return + } + // Expand slice by one with a dummy element, move all elements after i + // over by one, then place the new signature in that gap. + mSig.Signatures = append(mSig.Signatures, &signing.SingleSignatureData{}) + copy(mSig.Signatures[newSigIndex+1:], mSig.Signatures[newSigIndex:]) + mSig.Signatures[newSigIndex] = sig +} + +// AddSignatureFromPubKey adds a signature to the multisig, at the index in +// keys corresponding to the provided pubkey. +func AddSignatureFromPubKey(mSig *signing.MultiSignatureData, sig signing.SignatureData, pubkey types.PubKey, keys []types.PubKey) error { + if mSig == nil { + return fmt.Errorf("value of mSig is nil %v", mSig) + } + if sig == nil { + return fmt.Errorf("value of sig is nil %v", sig) + } + + if pubkey == nil || keys == nil { + return fmt.Errorf("pubkey or keys can't be nil %v %v", pubkey, keys) + } + index := getIndex(pubkey, keys) + if index == -1 { + keysStr := make([]string, len(keys)) + for i, k := range keys { + keysStr[i] = fmt.Sprintf("%X", k.Bytes()) + } + + return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n")) + } + + AddSignature(mSig, sig, index) + return nil +} + +func AddSignatureV2(mSig *signing.MultiSignatureData, sig signing.SignatureV2, keys []types.PubKey) error { + return AddSignatureFromPubKey(mSig, sig.Data, sig.PubKey, keys) +} diff --git a/crypto/types/multisig/pubkey.go b/crypto/types/multisig/pubkey.go new file mode 100644 index 000000000000..2e778bae6fd1 --- /dev/null +++ b/crypto/types/multisig/pubkey.go @@ -0,0 +1,27 @@ +package multisig + +import ( + "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// PubKey defines a type which supports multi-signature verification via MultiSignatureData +// which supports multiple SignMode's. +type PubKey interface { + types.PubKey + + // VerifyMultisignature verifies the provide multi-signature represented by MultiSignatureData + // using getSignBytes to retrieve the sign bytes to verify against for the provided mode. + VerifyMultisignature(getSignBytes GetSignBytesFunc, sig *signing.MultiSignatureData) error + + // GetPubKeys returns the types.PubKey's nested within the multi-sig PubKey + GetPubKeys() []types.PubKey + + // GetThreshold returns the threshold number of signatures that must be obtained to verify a signature. + GetThreshold() uint +} + +// GetSignBytesFunc defines a function type which returns sign bytes for a given SignMode or an error. +// It will generally be implemented as a closure which wraps whatever signable object signatures are +// being verified against. +type GetSignBytesFunc func(mode signing.SignMode) ([]byte, error) diff --git a/crypto/types/types.go b/crypto/types/types.go new file mode 100644 index 000000000000..eccdba73813d --- /dev/null +++ b/crypto/types/types.go @@ -0,0 +1,43 @@ +package types + +import ( + proto "github.com/gogo/protobuf/proto" + tmcrypto "github.com/tendermint/tendermint/crypto" +) + +// PubKey defines a public key and extends proto.Message. +type PubKey interface { + proto.Message + + Address() Address + Bytes() []byte + VerifySignature(msg []byte, sig []byte) bool + Equals(PubKey) bool + Type() string +} + +// LedgerPrivKey defines a private key that is not a proto message. For now, +// LedgerSecp256k1 keys are not converted to proto.Message yet, this is why +// they use LedgerPrivKey instead of PrivKey. All other keys must use PrivKey +// instead of LedgerPrivKey. +// TODO https://github.com/cosmos/cosmos-sdk/issues/7357. +type LedgerPrivKey interface { + Bytes() []byte + Sign(msg []byte) ([]byte, error) + PubKey() PubKey + Equals(LedgerPrivKey) bool + Type() string +} + +// PrivKey defines a private key and extends proto.Message. For now, it extends +// LedgerPrivKey (see godoc for LedgerPrivKey). Ultimately, we should remove +// LedgerPrivKey and add its methods here directly. +// TODO https://github.com/cosmos/cosmos-sdk/issues/7357. +type PrivKey interface { + proto.Message + LedgerPrivKey +} + +type ( + Address = tmcrypto.Address +) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000000..d335b9a06c47 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,74 @@ +version: "3" + +services: + simdnode0: + container_name: simdnode0 + image: "cosmossdk/simd-env" + ports: + - "26656-26657:26656-26657" + - "1317:1317" + - "9090:9090" + environment: + - ID=0 + - LOG=${LOG:-simd.log} + volumes: + - ./build:/simd:Z + networks: + localnet: + ipv4_address: 192.168.10.2 + + simdnode1: + container_name: simdnode1 + image: "cosmossdk/simd-env" + ports: + - "26666-26667:26656-26657" + - "1318:1317" + - "9091:9090" + environment: + - ID=1 + - LOG=${LOG:-simd.log} + volumes: + - ./build:/simd:Z + networks: + localnet: + ipv4_address: 192.168.10.3 + + simdnode2: + container_name: simdnode2 + image: "cosmossdk/simd-env" + environment: + - ID=2 + - LOG=${LOG:-simd.log} + ports: + - "26676-26677:26656-26657" + - "1319:1317" + - "9092:9090" + volumes: + - ./build:/simd:Z + networks: + localnet: + ipv4_address: 192.168.10.4 + + simdnode3: + container_name: simdnode3 + image: "cosmossdk/simd-env" + environment: + - ID=3 + - LOG=${LOG:-simd.log} + ports: + - "26686-26687:26656-26657" + - "1320:1317" + - "9093:9090" + volumes: + - ./build:/simd:Z + networks: + localnet: + ipv4_address: 192.168.10.5 + +networks: + localnet: + driver: bridge + ipam: + driver: default + config: + - subnet: 192.168.10.0/16 diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js deleted file mode 100644 index e25d835a7a39..000000000000 --- a/docs/.vuepress/config.js +++ /dev/null @@ -1,193 +0,0 @@ -module.exports = { - theme: "cosmos", - title: "Cosmos SDK", - markdown: { - anchor: { - permalinkSymbol: "" - } - }, - head: [ - [ - "link", - { - rel: "stylesheet", - type: "text/css", - href: "https://cloud.typography.com/6138116/7255612/css/fonts.css" - } - ], - ], - locales: { - "/": { - lang: "en-US" - }, - kr: { - lang: "kr" - }, - kr: { - lang: "kr" - }, - cn: { - lang: "cn" - }, - ru: { - lang: "ru" - } - }, - base: process.env.VUEPRESS_BASE || "/", - themeConfig: { - repo: "cosmos/cosmos-sdk", - docsRepo: "cosmos/cosmos-sdk", - docsDir: "docs", - editLinks: true, - label: "sdk", - sidebar: [ - { - title: "Using the SDK", - children: [ - { - title: "Modules", - directory: true, - path: "/modules" - } - ] - }, - { - title: "Resources", - children: [ - { - title: "Tutorials", - path: "https://tutorials.cosmos.network" - }, - { - title: "SDK API Reference", - path: "https://godoc.org/github.com/cosmos/cosmos-sdk" - }, - { - title: "REST API Spec", - path: "https://cosmos.network/rpc/" - } - ] - } - ], - gutter: { - title: "Help & Support", - editLink: true, - chat: { - title: "Riot Chat", - text: "Chat with Cosmos developers on Riot Chat.", - url: "https://riot.im/app/#/room/#cosmos-sdk:matrix.org", - bg: "linear-gradient(225.11deg, #2E3148 0%, #161931 95.68%)" - }, - forum: { - title: "Cosmos SDK Forum", - text: "Join the SDK Developer Forum to learn more.", - url: "https://forum.cosmos.network/", - bg: "linear-gradient(225deg, #46509F -1.08%, #2F3564 95.88%)", - logo: "cosmos" - }, - github: { - title: "Found an Issue?", - text: "Help us improve this page by suggesting edits on GitHub." - } - }, - footer: { - logo: "/logo-bw.svg", - textLink: { - text: "cosmos.network", - url: "/" - }, - services: [ - { - service: "medium", - url: "https://blog.cosmos.network/" - }, - { - service: "twitter", - url: "https://twitter.com/cosmos" - }, - { - service: "linkedin", - url: "https://www.linkedin.com/company/tendermint/" - }, - { - service: "reddit", - url: "https://reddit.com/r/cosmosnetwork" - }, - { - service: "telegram", - url: "https://t.me/cosmosproject" - }, - { - service: "youtube", - url: "https://www.youtube.com/c/CosmosProject" - } - ], - smallprint: - "This website is maintained by Tendermint Inc. The contents and opinions of this website are those of Tendermint Inc.", - links: [ - { - title: "Documentation", - children: [ - { - title: "Cosmos SDK", - url: "https://cosmos.network/docs" - }, - { - title: "Cosmos Hub", - url: "https://hub.cosmos.network/" - }, - { - title: "Tendermint Core", - url: "https://docs.tendermint.com/" - } - ] - }, - { - title: "Community", - children: [ - { - title: "Cosmos blog", - url: "https://blog.cosmos.network/" - }, - { - title: "Forum", - url: "https://forum.cosmos.network/" - }, - { - title: "Chat", - url: "https://riot.im/app/#/room/#cosmos-sdk:matrix.org" - } - ] - }, - { - title: "Contributing", - children: [ - { - title: "Contributing to the docs", - url: - "https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOCS_README.md" - }, - { - title: "Source code on GitHub", - url: "https://github.com/cosmos/cosmos-sdk/" - } - ] - } - ] - } - }, - plugins: [ - [ - "@vuepress/google-analytics", - { - ga: "UA-51029217-12" - } - ], - [ - "sitemap", - { - hostname: "https://docs.cosmos.network" - } - ] - ] -}; diff --git a/docs/.vuepress/public/logo-bw.svg b/docs/.vuepress/public/logo-bw.svg deleted file mode 100644 index f2575260a761..000000000000 --- a/docs/.vuepress/public/logo-bw.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/.vuepress/public/logo.svg b/docs/.vuepress/public/logo.svg deleted file mode 100644 index 95ca6d30da5b..000000000000 --- a/docs/.vuepress/public/logo.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl deleted file mode 100644 index 66affa704b7b..000000000000 --- a/docs/.vuepress/styles/index.styl +++ /dev/null @@ -1,3 +0,0 @@ -:root - --accent-color #5064fb - --background #161931 \ No newline at end of file diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md deleted file mode 100644 index 96986bef8030..000000000000 --- a/docs/DOCS_README.md +++ /dev/null @@ -1,121 +0,0 @@ -# Updating the docs - -If you want to open a PR on the Cosmos SDK to update the documentation, please follow the guidelines in the [`CONTRIBUTING.md`](https://github.com/cosmos/cosmos-sdk/tree/master/CONTRIBUTING.md#updating-documentation) - -## Translating - -- Docs translations live in a `docs/country-code/` folder, where `country-code` stands for the country code of the language used (`cn` for Chinese, `kr` for Korea, `fr` for France, ...). -- Always translate content living on `master`. -- Only content under `/docs/intro/`, `/docs/basics/`, `/docs/core/`, `/docs/building-modules/` and `docs/interfaces` needs to be translated, as well as `docs/README.md`. It is also nice (but not mandatory) to translate `/docs/spec/`. -- Specify the release/tag of the translation in the README of your translation folder. Update the release/tag each time you update the translation. - -## Docs Build Workflow - -The documentation for the Cosmos SDK is hosted at https://cosmos.network/docs/ - -built from the files in this (`/docs`) directory for -[master](https://github.com/cosmos/cosmos-sdk/tree/master/docs). - -### How It Works - -There is a CircleCI job listening for changes in the `/docs` directory, on -the `master` branch. Any updates to files in this directory -on that branch will automatically trigger a website deployment. Under the hood, -the private website repository has a `make build-docs` target consumed by a CircleCI job in that repo. - -## README - -The [README.md](./README.md) is also the landing page for the documentation -on the website. During the Jenkins build, the current commit is added to the bottom -of the README. - -## Config.js - -The [config.js](./.vuepress/config.js) generates the sidebar and Table of Contents -on the website docs. Note the use of relative links and the omission of -file extensions. Additional features are available to improve the look -of the sidebar. - -## Links - -**NOTE:** Strongly consider the existing links - both within this directory -and to the website docs - when moving or deleting files. - -Relative links should be used nearly everywhere, having discovered and weighed the following: - -### Relative - -Where is the other file, relative to the current one? - -- works both on GitHub and for the VuePress build -- confusing / annoying to have things like: `../../../../myfile.md` -- requires more updates when files are re-shuffled - -### Absolute - -Where is the other file, given the root of the repo? - -- works on GitHub, doesn't work for the VuePress build -- this is much nicer: `/docs/hereitis/myfile.md` -- if you move that file around, the links inside it are preserved (but not to it, of course) - -### Full - -The full GitHub URL to a file or directory. Used occasionally when it makes sense -to send users to the GitHub. - -## Building Locally - -Make sure you are in the `docs` directory and run the following commands: - -```sh -rm -rf node_modules -``` - -This command will remove old version of the visual theme and required packages. This step is optional. - -```sh -npm install -``` - -Install the theme and all dependencies. - -```sh -npm run serve -``` - -Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often https://localhost:8080). - -To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory. - -## Build RPC Docs - -First, run `make tools` from the root of repo, to install the swagger-ui tool. - -Then, edit the `swagger.yaml` manually; it is found [here](https://github.com/cosmos/cosmos-sdk/blob/master/client/lcd/swagger-ui/swagger.yaml) - -Finally, run `make update_gaia_lite_docs` from the root of the repo. - -## Search - -We are using [Algolia](https://www.algolia.com) to power full-text search. This uses a public API search-only key in the `config.js` as well as a [cosmos_network.json](https://github.com/algolia/docsearch-configs/blob/master/configs/cosmos_network.json) configuration file that we can update with PRs. - -## Consistency - -Because the build processes are identical (as is the information contained herein), this file should be kept in sync as -much as possible with its [counterpart in the Tendermint Core repo](https://github.com/tendermint/tendermint/blob/master/docs/DOCS_README.md). - -### Update and Build the RPC docs - -1. Execute the following command at the root directory to install the swagger-ui generate tool. - ```bash - make tools - ``` -2. Edit API docs - 1. Directly Edit API docs manually: `client/lcd/swagger-ui/swagger.yaml`. - 2. Edit API docs within the [Swagger Editor](https://editor.swagger.io/). Please refer to this [document](https://swagger.io/docs/specification/2-0/basic-structure/) for the correct structure in `.yaml`. -3. Download `swagger.yaml` and replace the old `swagger.yaml` under fold `client/lcd/swagger-ui`. -4. Compile gaiacli - ```bash - make install - ``` diff --git a/docs/README.md b/docs/README.md index 04c306be3d41..18398752a03f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,21 +2,6 @@ layout: homepage title: Cosmos SDK Documentation description: Cosmos SDK is the world’s most popular framework for building application-specific blockchains. -features: - - cta: Read - title: Introduction to Cosmos SDK - desc: Learn about all the parts of the Cosmos SDK. - label: 5 min - url: /intro/overview.html - image: spaceship - - cta: Learn - title: SDK Tutorials - desc: Build a complete blockchain application from scratch. - label: 30-40 min - special: dark - h3: View tutorials - url: https://tutorials.cosmos.network/ - image: window sections: - title: Introduction desc: High-level overview of the Cosmos SDK. @@ -63,7 +48,7 @@ aside: false ## Get Started - **[SDK Intro](./intro/overview.md)**: High-level overview of the Cosmos SDK. -- **[Quick Start Guide](./using-the-sdk/quick-start.md)**: Scaffold a standard Cosmos SDK app and run a node. +- **[Quick Start Guide](./using-the-sdk/quick-start.md)**: Scaffold a standard Cosmos SDK app and run a node. - **[SDK Application Tutorial](https://github.com/cosmos/sdk-application-tutorial)**: A tutorial that showcases how to build an SDK-based blockchain from scratch and explains the basic principles of the SDK in the process. ## Reference @@ -71,7 +56,9 @@ aside: false - **[Basics](./basics/)**: Documentation on the basic concepts of the Cosmos SDK, like the standard anatomy of an application, the transaction lifecycle and accounts management. - **[Core](./core/)**: Documentation on the core concepts of the Cosmos SDK, like `baseapp`, the `store` or the `server`. - **[Building Modules](./building-modules/)**: Important concepts for module developers like `message`s, `keeper`s, `handler`s and `querier`s. -- **[Interfaces](./interfaces/)**: Documentation on building interfaces for Cosmos SDK applications. +- **[IBC](./ibc/)**: Documentation for the IBC protocol integration and concepts. +- **[Running a Node, API, CLI](./run-node/)**: Documentation on how to run a node, and how to interact with it using the CLI and the API. +- **[Migrations](./migrations/)**: Migration guides for updating to Stargate. ## Other Resources diff --git a/docs/Screen Shot 2019-10-17 at 15.07.22.png b/docs/Screen Shot 2019-10-17 at 15.07.22.png deleted file mode 100644 index fc823b91ed22..000000000000 Binary files a/docs/Screen Shot 2019-10-17 at 15.07.22.png and /dev/null differ diff --git a/docs/architecture/PROCESS.md b/docs/architecture/PROCESS.md new file mode 100644 index 000000000000..4ed4621162c3 --- /dev/null +++ b/docs/architecture/PROCESS.md @@ -0,0 +1,60 @@ +# ADR Creation Process + +1. Copy the `adr-template.md` file. Use the following filename pattern: `adr-next_number-title.md` +2. Create a draft Pull Request if you want to get an early feedback. +3. Make sure the context and a solution is clear and well documented. +4. Add an entry to a list in the [README](./README.md) file. +5. Create a Pull Request to propose a new ADR. + + +## ADR life cycle + +ADR creation is an **iterative** process. Instead of trying to solve all decisions in a single ADR pull request, we MUST firstly understand the problem and collect feedback through a GitHub Issue. + +1. Every proposal SHOULD start with a new GitHub Issue or be a result of existing Issues. The Issue should contain just a brief proposal summary. + +2. Once the motivation is validated, a GitHub Pull Request (PR) is created with a new document based on the `adr-template.md`. + +3. An ADR doesn't have to arrive to `master` with an _accepted_ status in a single PR. If the motivation is clear and the solution is sound, we SHOULD be able to merge it and keep a _proposed_ status. It's preferable to have an iterative approach rather than long, not merged Pull Requests. + +4. If a _proposed_ ADR is merged, then it should clearly document outstanding issues either in ADR document notes or in a GitHub Issue. + +5. The PR SHOULD always be merged. In the case of a faulty ADR, we still prefer to merge it with a _rejected_ status. The only time the ADR SHOULD NOT be merged is if the author abandons it. + +6. Merged ADRs SHOULD NOT be pruned. + + +### ADR status + +Status has two components: + +``` +{CONSENSUS STATUS} {IMPLEMENTATION STATUS} +``` + +IMPLEMENTATION STATUS is either `Implemented` or `Not Implemented`. + +#### Consensus Status + +``` +DRAFT -> PROPOSED -> LAST CALL yyyy-mm-dd -> ACCEPTED | REJECTED -> SUPERSEEDED by ADR-xxx + \ | + \ | + v v + ABANDONED +``` + + ++ `DRAFT`: [optional] an ADR which is work in progress, not being ready for a general review. This is to present an early work and get an early feedback in a Draft Pull Request form. ++ `PROPOSED`: an ADR covering a full solution architecture and still in the review - project stakeholders haven't reached an agreed yet. ++ `LAST CALL `: [optional] clear notify that we are close to accept updates. Changing a status to `LAST CALL` means that social consensus (of Cosmos SDK maintainers) has been reached and we still want to give it a time to let the community react or analyze. ++ `ACCEPTED`: ADR which will represent a currently implemented or to be implemented architecture design. ++ `REJECTED`: ADR can go from PROPOSED or ACCEPTED to rejected if the consensus among project stakeholders will decide so. ++ `SUPERSEEDED by ADR-xxx`: ADR which has been superseded by a new ADR. ++ `ABANDONED`: the ADR is no longer pursued by the original authors. + + +## Language used in ADR + ++ The context/background should be written in the present tense. ++ Avoid using a first, personal form. diff --git a/docs/architecture/README.md b/docs/architecture/README.md index e882cb90cab2..54cc9c89e4f7 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -8,8 +8,15 @@ parent: This is a location to record all high-level architecture decisions in the Cosmos-SDK. +An Architectural Decision (**AD**) is a software design choice that addresses a functional or non-functional requirement that is architecturally significant. +An Architecturally Significant Requirement (**ASR**) is a requirement that has a measurable effect on a software system’s architecture and quality. +An Architectural Decision Record (**ADR**) captures a single AD, such as often done when writing personal notes or meeting minutes; the collection of ADRs created and maintained in a project constitute its decision log. All these are within the topic of Architectural Knowledge Management (AKM). + You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t). +## Rationale + +ADRs are intended to be the primary mechanism for proposing new feature designs and new processes, for collecting community input on an issue, and for documenting the design decisions. An ADR should provide: - Context on the relevant goals and the current state @@ -25,20 +32,44 @@ it stands today. If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. -Note the context/background should be written in the present tense. -Please add a entry below in your Pull Request for an ADR. +## Creating new ADR + +Read about the [PROCESS](./PROCESS.md). ## ADR Table of Contents +### Accepted + +- [ADR 001: Coin Source Tracing](./adr-001-coin-source-tracing.md) - [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md) -- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md) - [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md) - [ADR 006: Secret Store Replacement](./adr-006-secret-store-replacement.md) - [ADR 009: Evidence Module](./adr-009-evidence-module.md) - [ADR 010: Modular AnteHandler](./adr-010-modular-antehandler.md) +- [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md) +- [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md) +- [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.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 031: Protobuf Msg Services](./adr-031-msg-service.md) + +### Proposed + +- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md) - [ADR 011: Generalize Genesis Accounts](./adr-011-generalize-genesis-accounts.md) - [ADR 012: State Accessors](./adr-012-state-accessors.md) +- [ADR 013: Metrics](./adr-013-metrics.md) - [ADR 015: IBC Packet Receiver](./adr-015-ibc-packet-receiver.md) - [ADR 016: Validator Consensus Key Rotation](./adr-016-validator-consensus-key-rotation.md) - [ADR 017: Historical Header Module](./adr-017-historical-header-module.md) +- [ADR 018: Extendable Voting Periods](./adr-018-extendable-voting-period.md) +- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md) +- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md) +- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md) +- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md) +- [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md) +- [ADR 032: Typed Events](./adr-032-typed-events.md) +- [ADR 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 diff --git a/docs/architecture/adr-001-coin-source-tracing.md b/docs/architecture/adr-001-coin-source-tracing.md new file mode 100644 index 000000000000..be3ff2386522 --- /dev/null +++ b/docs/architecture/adr-001-coin-source-tracing.md @@ -0,0 +1,376 @@ +# ADR 001: Coin Source Tracing + +## Changelog + +- 2020-07-09: Initial Draft +- 2020-08-11: Implementation changes + +## Status + +Accepted, Implemented + +## Context + +The specification for IBC cross-chain fungible token transfers +([ICS20](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)), needs to +be aware of the origin of any token denomination in order to relay a `Packet` which contains the sender +and recipient addressed in the +[`FungibleTokenPacketData`](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures). + +The Packet relay sending works based in 2 cases (per +[specification](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay) and [Colin Axnér](https://github.com/colin-axner)'s description): + +1. Sender chain is acting as the source zone. The coins are transferred +to an escrow address (i.e locked) on the sender chain and then transferred +to the receiving chain through IBC TAO logic. It is expected that the +receiving chain will mint vouchers to the receiving address. + +2. Sender chain is acting as the sink zone. The coins (vouchers) are burned +on the sender chain and then transferred to the receiving chain though IBC +TAO logic. It is expected that the receiving chain, which had previously +sent the original denomination, will unescrow the fungible token and send +it to the receiving address. + +Another way of thinking of source and sink zones is through the token's +timeline. Each send to any chain other than the one it was previously +received from is a movement forwards in the token's timeline. This causes +trace to be added to the token's history and the destination port and +destination channel to be prefixed to the denomination. In these instances +the sender chain is acting as the source zone. When the token is sent back +to the chain it previously received from, the prefix is removed. This is +a backwards movement in the token's timeline and the sender chain +is acting as the sink zone. + +### Example + +Assume the following channel connections exist and that all channels use the port ID `transfer`: + +- chain `A` has channels with chain `B` and chain `C` with the IDs `channelToB` and `channelToC`, respectively +- chain `B` has channels with chain `A` and chain `C` with the IDs `channelToA` and `channelToC`, respectively +- chain `C` has channels with chain `A` and chain `B` with the IDs `channelToA` and `channelToB`, respectively + +These steps of transfer between chains occur in the following order: `A -> B -> C -> A -> C`. In particular: + +1. `A -> B`: sender chain is source zone. `A` sends packet with `denom` (escrowed on `A`), `B` receives `denom` and mints and sends voucher `transfer/channelToA/denom` to recipient. +2. `B -> C`: sender chain is source zone. `B` sends packet with `transfer/channelToA/denom` (escrowed on `B`), `C` receives `transfer/channelToA/denom` and mints and sends voucher `transfer/channelToB/transfer/channelToA/denom` to recipient. +3. `C -> A`: sender chain is source zone. `C` sends packet with `transfer/channelToB/transfer/channelToA/denom` (escrowed on `C`), `A` receives `transfer/channelToB/transfer/channelToA/denom` and mints and sends voucher `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom` to recipient. +4. `A -> C`: sender chain is sink zone. `A` sends packet with `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom` (burned on `A`), `C` receives `transfer/channelToC/transfer/channelToB/transfer/channelToA/denom`, and unescrows and sends `transfer/channelToB/transfer/channelToA/denom` to recipient. + +The token has a final denomination on chain `C` of `transfer/channelToB/transfer/channelToA/denom`, where `transfer/channelToB/transfer/channelToA` is the trace information. + +In this context, upon a receive of a cross-chain fungible token transfer, if the sender chain is the source of the token, the protocol prefixes the denomination with the port and channel identifiers in the following format: + +```typescript +prefix + denom = {destPortN}/{destChannelN}/.../{destPort0}/{destChannel0}/denom +``` + +Example: transferring `100 uatom` from port `HubPort` and channel `HubChannel` on the Hub to +Ethermint's port `EthermintPort` and channel `EthermintChannel` results in `100 +EthermintPort/EthermintChannel/uatom`, where `EthermintPort/EthermintChannel/uatom` is the new +denomination on the receiving chain. + +In the case those tokens are transferred back to the Hub (i.e the **source** chain), the prefix is +trimmed and the token denomination updated to the original one. + +### Problem + +The problem of adding additional information to the coin denomination is twofold: + +1. The ever increasing length if tokens are transferred to zones other than the source: + +If a token is transferred `n` times via IBC to a sink chain, the token denom will contain `n` pairs +of prefixes, as shown on the format example above. This poses a problem because, while port and +channel identifiers have a maximum length of 64 each, the SDK `Coin` type only accepts denoms up to +64 characters. Thus, a single cross-chain token, which again, is composed by the port and channels +identifiers plus the base denomination, can exceed the length validation for the SDK `Coins`. + +This can result in undesired behaviours such as tokens not being able to be transferred to multiple +sink chains if the denomination exceeds the length or unexpected `panics` due to denomination +validation failing on the receiving chain. + +2. The existence of special characters and uppercase letters on the denomination: + +In the SDK every time a `Coin` is initialized through the constructor function `NewCoin`, a validation +of a coin's denom is performed according to a +[Regex](https://github.com/cosmos/cosmos-sdk/blob/a940214a4923a3bf9a9161cd14bd3072299cd0c9/types/coin.go#L583), +where only lowercase alphanumeric characters are accepted. While this is desirable for native denominations +to keep a clean UX, it presents a challenge for IBC as ports and channels might be randomly +generated with special and uppercase characters as per the [ICS 024 - Host +Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements#paths-identifiers-separators) +specification. + +## Decision + +The issues outlined above, are applicable only to SDK-based chains, and thus the proposed solution +are do not require specification changes that would result in modification to other implementations +of the ICS20 spec. + +Instead of adding the identifiers on the coin denomination directly, the proposed solution hashes +the denomination prefix in order to get a consistent length for all the cross-chain fungible tokens. + +This will be used for internal storage only, and when transferred via IBC to a different chain, the +denomination specified on the packed data will be the full prefix path of the identifiers needed to +trace the token back to the originating chain, as specified on ICS20. + +The new proposed format will be the following: + +```golang +ibcDenom = "ibc/" + hash(trace path + "/" + base denom) +``` + +The hash function will be a SHA256 hash of the fields of the `DenomTrace`: + +```protobuf +// DenomTrace contains the base denomination for ICS20 fungible tokens and the source tracing +// information +message DenomTrace { + // chain of port/channel identifiers used for tracing the source of the fungible token + string path = 1; + // base denomination of the relayed fungible token + string base_denom = 2; +} +``` + +The `IBCDenom` function constructs the `Coin` denomination used when creating the ICS20 fungible token packet data: + +```golang +// Hash returns the hex bytes of the SHA256 hash of the DenomTrace fields using the following formula: +// +// hash = sha256(tracePath + "/" + baseDenom) +func (dt DenomTrace) Hash() tmbytes.HexBytes { + return tmhash.Sum(dt.Path + "/" + dt.BaseDenom) +} + +// IBCDenom a coin denomination for an ICS20 fungible token in the format 'ibc/{hash(tracePath + baseDenom)}'. +// If the trace is empty, it will return the base denomination. +func (dt DenomTrace) IBCDenom() string { + if dt.Path != "" { + return fmt.Sprintf("ibc/%s", dt.Hash()) + } + return dt.BaseDenom +} +``` + +### `x/ibc-transfer` Changes + +In order to retrieve the trace information from an IBC denomination, a lookup table needs to be +added to the `ibc-transfer` module. These values need to also be persisted between upgrades, meaning +that a new `[]DenomTrace` `GenesisState` field state needs to be added to the module: + +```golang +// GetDenomTrace retrieves the full identifiers trace and base denomination from the store. +func (k Keeper) GetDenomTrace(ctx Context, denomTraceHash []byte) (DenomTrace, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.KeyDenomTrace(traceHash)) + if bz == nil { + return &DenomTrace, false + } + + var denomTrace DenomTrace + k.cdc.MustUnmarshalBinaryBare(bz, &denomTrace) + return denomTrace, true +} + +// HasDenomTrace checks if a the key with the given trace hash exists on the store. +func (k Keeper) HasDenomTrace(ctx Context, denomTraceHash []byte) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(types.KeyTrace(denomTraceHash)) +} + +// SetDenomTrace sets a new {trace hash -> trace} pair to the store. +func (k Keeper) SetDenomTrace(ctx Context, denomTrace DenomTrace) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(&denomTrace) + store.Set(types.KeyTrace(denomTrace.Hash()), bz) +} +``` + +The `MsgTransfer` will validate that the `Coin` denomination from the `Token` field contains a valid +hash, if the trace info is provided, or that the base denominations matches: + +```golang +func (msg MsgTransfer) ValidateBasic() error { + // ... + return ValidateIBCDenom(msg.Token.Denom) +} +``` + +```golang +// ValidateIBCDenom validates that the given denomination is either: +// +// - A valid base denomination (eg: 'uatom') +// - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-001-coin-source-tracing.md +func ValidateIBCDenom(denom string) error { + denomSplit := strings.SplitN(denom, "/", 2) + + switch { + case strings.TrimSpace(denom) == "", + len(denomSplit) == 1 && denomSplit[0] == "ibc", + len(denomSplit) == 2 && (denomSplit[0] != "ibc" || strings.TrimSpace(denomSplit[1]) == ""): + return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) + + case denomSplit[0] == denom && strings.TrimSpace(denom) != "": + return sdk.ValidateDenom(denom) + } + + if _, err := ParseHexHash(denomSplit[1]); err != nil { + return Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) + } + + return nil +} +``` + +The denomination trace info only needs to be updated when token is received: + +- Receiver is **source** chain: The receiver created the token and must have the trace lookup already stored (if necessary _ie_ native token case wouldn't need a lookup). +- Receiver is **not source** chain: Store the received info. For example, during step 1, when chain `B` receives `transfer/channelToA/denom`. + +```golang +// SendTransfer +// ... + + fullDenomPath := token.Denom + +// deconstruct the token denomination into the denomination trace info +// to determine if the sender is the source chain +if strings.HasPrefix(token.Denom, "ibc/") { + fullDenomPath, err = k.DenomPathFromHash(ctx, token.Denom) + if err != nil { + return err + } +} + +if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { +//... +``` + +```golang +// DenomPathFromHash returns the full denomination path prefix from an ibc denom with a hash +// component. +func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) { + hexHash := denom[4:] + hash, err := ParseHexHash(hexHash) + if err != nil { + return "", Wrap(ErrInvalidDenomForTransfer, err.Error()) + } + + denomTrace, found := k.GetDenomTrace(ctx, hash) + if !found { + return "", Wrap(ErrTraceNotFound, hexHash) + } + + fullDenomPath := denomTrace.GetFullDenomPath() + return fullDenomPath, nil +} +``` + + +```golang +// OnRecvPacket +// ... + +// This is the prefix that would have been prefixed to the denomination +// on sender chain IF and only if the token originally came from the +// receiving chain. +// +// NOTE: We use SourcePort and SourceChannel here, because the counterparty +// chain would have prefixed with DestPort and DestChannel when originally +// receiving this coin as seen in the "sender chain is the source" condition. +if ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { + // sender chain is not the source, unescrow tokens + + // remove prefix added by sender chain + voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := data.Denom[len(voucherPrefix):] + token := sdk.NewCoin(unprefixedDenom, sdk.NewIntFromUint64(data.Amount)) + + // unescrow tokens + escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) + return k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token)) +} + +// sender chain is the source, mint vouchers + +// since SendPacket did not prefix the denomination, we must prefix denomination here +sourcePrefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) +// NOTE: sourcePrefix contains the trailing "/" +prefixedDenom := sourcePrefix + data.Denom + +// construct the denomination trace from the full raw denomination +denomTrace := types.ParseDenomTrace(prefixedDenom) + +// set the value to the lookup table if not stored already +traceHash := denomTrace.Hash() +if !k.HasDenomTrace(ctx, traceHash) { + k.SetDenomTrace(ctx, traceHash, denomTrace) +} + +voucherDenom := denomTrace.IBCDenom() +voucher := sdk.NewCoin(voucherDenom, sdk.NewIntFromUint64(data.Amount)) + +// mint new tokens if the source of the transfer is the same chain +if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(voucher), +); err != nil { + return err +} + +// send to receiver +return k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, receiver, sdk.NewCoins(voucher), +) +``` + +```golang +func NewDenomTraceFromRawDenom(denom string) DenomTrace{ + denomSplit := strings.Split(denom, "/") + trace := "" + if len(denomSplit) > 1 { + trace = strings.Join(denomSplit[:len(denomSplit)-1], "/") + } + return DenomTrace{ + BaseDenom: denomSplit[len(denomSplit)-1], + Trace: trace, + } +} +``` + +One final remark is that the `FungibleTokenPacketData` will remain the same, i.e with the prefixed full denomination, since the receiving chain may not be an SDK-based chain. + +### Coin Changes + +The coin denomination validation will need to be updated to reflect these changes. In particular, the denomination validation +function will now: + +- Accept slash separators (`"/"`) and uppercase characters (due to the `HexBytes` format) +- Bump the maximum character length to 128, as the hex representation used by Tendermint's + `HexBytes` type contains 64 characters. + +Additional validation logic, such as verifying the length of the hash, the may be added to the bank module in the future if the [custom base denomination validation](https://github.com/cosmos/cosmos-sdk/pull/6755) is integrated into the SDK. + +### Positive + +- Clearer separation of the source tracing behaviour of the token (transfer prefix) from the original + `Coin` denomination +- Consistent validation of `Coin` fields (i.e no special characters, fixed max length) +- Cleaner `Coin` and standard denominations for IBC +- No additional fields to SDK `Coin` + +### Negative + +- Store each set of tracing denomination identifiers on the `ibc-transfer` module store +- Clients will have to fetch the base denomination every time they receive a new relayed fungible token over IBC. This can be mitigated using a map/cache for already seen hashes on the client side. Other forms of mitigation, would be opening a websocket connection subscribe to incoming events. + +### Neutral + +- Slight difference with the ICS20 spec +- Additional validation logic for IBC coins on the `ibc-transfer` module +- Additional genesis fields +- Slightly increases the gas usage on cross-chain transfers due to access to the store. This should + be inter-block cached if transfers are frequent. + +## References + +- [ICS 20 - Fungible token transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer) +- [Custom Coin Denomination validation](https://github.com/cosmos/cosmos-sdk/pull/6755) diff --git a/docs/architecture/adr-003-dynamic-capability-store.md b/docs/architecture/adr-003-dynamic-capability-store.md index 7fd438fd931c..21843649b434 100644 --- a/docs/architecture/adr-003-dynamic-capability-store.md +++ b/docs/architecture/adr-003-dynamic-capability-store.md @@ -3,6 +3,7 @@ ## Changelog - 12 December 2019: Initial version +- 02 April 2020: Memory Store Revisions ## Context @@ -15,33 +16,40 @@ At present, the Cosmos SDK does not have the ability to do this. Object-capabili and passed to Keepers as fixed arguments ([example](https://github.com/cosmos/gaia/blob/dcbddd9f04b3086c0ad07ee65de16e7adedc7da4/app/app.go#L160)). Keepers cannot create or store capability keys during transaction execution — although they could call `NewKVStoreKey` and take the memory address of the returned struct, storing this in the Merklised store would result in a consensus fault, since the memory address will be different on each machine (this is intentional — were this not the case, the keys would be predictable and couldn't serve as object capabilities). -Keepers need a way to keep a private map of store keys which can be altered during transacton execution, along with a suitable mechanism for regenerating the unique memory addresses (capability keys) in this map whenever the application is started or restarted. +Keepers need a way to keep a private map of store keys which can be altered during transaction execution, along with a suitable mechanism for regenerating the unique memory addresses (capability keys) in this map whenever the application is started or restarted, along with a mechanism to revert capability creation on tx failure. This ADR proposes such an interface & mechanism. ## Decision -The SDK will include a new `CapabilityKeeper` abstraction, which is responsible for provisioning, tracking, and authenticating capabilities at runtime. During application initialisation in `app.go`, the `CapabilityKeeper` will -be hooked up to modules through unique function references (by calling `ScopeToModule`, defined below) so that it can identify the calling module when later invoked. When the initial state is loaded from disk, the `CapabilityKeeper`'s `Initialise` function will create new capability keys -for all previously allocated capability identifiers (allocated during execution of past transactions and assigned to particular modes), and keep them in a memory-only store while the chain is running. The SDK will include a new `MemoryStore` store type, similar -to the existing `TransientStore` but without erasure on `Commit()`, which this `CapabilityKeeper` will use to privately store capability keys. +The SDK will include a new `CapabilityKeeper` abstraction, which is responsible for provisioning, +tracking, and authenticating capabilities at runtime. During application initialisation in `app.go`, +the `CapabilityKeeper` will be hooked up to modules through unique function references +(by calling `ScopeToModule`, defined below) so that it can identify the calling module when later +invoked. -The `CapabilityKeeper` will use two stores: a regular, persistent `KVStore`, which will track what capabilities have been created by each module, and an in-memory `MemoryStore` (described below), which will -store the actual capabilities. The `CapabilityKeeper` will define the following types & functions: +When the initial state is loaded from disk, the `CapabilityKeeper`'s `Initialise` function will create +new capability keys for all previously allocated capability identifiers (allocated during execution of +past transactions and assigned to particular modes), and keep them in a memory-only store while the +chain is running. -The `Capability` interface is similar to `StoreKey`, but has a globally unique `Index()` instead of a name. A `String()` method is provided for debugging. +The `CapabilityKeeper` will include a persistent `KVStore`, a `MemoryStore`, and an in-memory map. +The persistent `KVStore` tracks which capability is owned by which modules. +The `MemoryStore` stores a forward mapping that map from module name, capability tuples to capability names and +a reverse mapping that map from module name, capability name to the capability index. +Since we cannot marshal the capability into a `KVStore` and unmarshal without changing the memory location of the capability, +the reverse mapping in the KVStore will simply map to an index. This index can then be used as a key in the ephemeral +go-map to retrieve the capability at the original memory location. -```golang -type Capability interface { - Index() uint64 - String() string -} -``` +The `CapabilityKeeper` will define the following types & functions: + +The `Capability` is similar to `StoreKey`, but has a globally unique `Index()` instead of +a name. A `String()` method is provided for debugging. -A `CapabilityKey` is simply a struct, the address of which is taken for the actual capability. +A `Capability` is simply a struct, the address of which is taken for the actual capability. ```golang -type CapabilityKey struct { - name string +type Capability struct { + index uint64 } ``` @@ -50,63 +58,80 @@ A `CapabilityKeeper` contains a persistent store key, memory store key, and mapp ```golang type CapabilityKeeper struct { persistentKey StoreKey - memoryKey MemoryStoreKey - moduleNames map[string]interface{} - sealed bool + memKey StoreKey + capMap map[uint64]*Capability + moduleNames map[string]interface{} + sealed bool } ``` -The `CapabilityKeeper` provides the ability to create *scoped* sub-keepers which are tied to a particular module name. These `ScopedCapabilityKeeper`s must be created at application -initialisation and passed to modules, which can then use them to claim capabilities they receive and retrieve capabilities which they own by name, in addition -to creating new capabilities & authenticating capabilities passed by other modules. +The `CapabilityKeeper` provides the ability to create *scoped* sub-keepers which are tied to a +particular module name. These `ScopedCapabilityKeeper`s must be created at application initialisation +and passed to modules, which can then use them to claim capabilities they receive and retrieve +capabilities which they own by name, in addition to creating new capabilities & authenticating capabilities +passed by other modules. ```golang type ScopedCapabilityKeeper struct { persistentKey StoreKey - memoryKey MemoryStoreKey - moduleName string + memKey StoreKey + capMap map[uint64]*Capability + moduleName string } ``` -`ScopeToModule` is used to create a scoped sub-keeper with a particular name, which must be unique. It MUST be called before `InitialiseAndSeal`. +`ScopeToModule` is used to create a scoped sub-keeper with a particular name, which must be unique. +It MUST be called before `InitialiseAndSeal`. ```golang func (ck CapabilityKeeper) ScopeToModule(moduleName string) ScopedCapabilityKeeper { - if ck.sealed { - panic("capability keeper is sealed") - } - if _, present := ck.moduleNames[moduleName]; present { - panic("cannot create multiple scoped capability keepers for the same module name") - } - ck.moduleNames[moduleName] = struct{}{} - return ScopedCapabilityKeeper{ - persistentKey: ck.persistentKey, - memoryKey: ck.memoryKey, - moduleName: moduleName - } + if k.sealed { + panic("cannot scope to module via a sealed capability keeper") + } + + if _, ok := k.scopedModules[moduleName]; ok { + panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName)) + } + + k.scopedModules[moduleName] = struct{}{} + + return ScopedKeeper{ + cdc: k.cdc, + storeKey: k.storeKey, + memKey: k.memKey, + capMap: k.capMap, + module: moduleName, + } } ``` -`InitialiseAndSeal` MUST be called exactly once, after loading the initial state and creating all necessary `ScopedCapabilityKeeper`s, -in order to populate the memory store with newly-created capability keys in accordance with the keys previously claimed by particular modules -and prevent the creation of any new `ScopedCapabilityKeeper`s. +`InitialiseAndSeal` MUST be called exactly once, after loading the initial state and creating all +necessary `ScopedCapabilityKeeper`s, in order to populate the memory store with newly-created +capability keys in accordance with the keys previously claimed by particular modules and prevent the +creation of any new `ScopedCapabilityKeeper`s. ```golang func (ck CapabilityKeeper) InitialiseAndSeal(ctx Context) { if ck.sealed { panic("capability keeper is sealed") } + persistentStore := ctx.KVStore(ck.persistentKey) - memoryStore := ctx.KVStore(ck.memoryKey) + map := ctx.KVStore(ck.memKey) + // initialise memory store for all names in persistent store for index, value := range persistentStore.Iter() { capability = &CapabilityKey{index: index} + for moduleAndCapability := range value { moduleName, capabilityName := moduleAndCapability.Split("/") - memoryStore.Set(moduleName + "/fwd/" + capability, capabilityName) - memoryStore.Set(moduleName + "/rev/" + capabilityName, capability) + memStore.Set(moduleName + "/fwd/" + capability, capabilityName) + memStore.Set(moduleName + "/rev/" + capabilityName, index) + + ck.capMap[index] = capability } } + ck.sealed = true } ``` @@ -117,24 +142,33 @@ call `ClaimCapability`. ```golang func (sck ScopedCapabilityKeeper) NewCapability(ctx Context, name string) (Capability, error) { - memoryStore := ctx.KVStore(sck.memoryKey) // check name not taken in memory store - if memoryStore.Get("rev/" + name) != nil { + if capStore.Get("rev/" + name) != nil { return nil, errors.New("name already taken") } + // fetch the current index index := persistentStore.Get("index") + // create a new capability capability := &CapabilityKey{index: index} + // set persistent store persistentStore.Set(index, Set.singleton(sck.moduleName + "/" + name)) + // update the index index++ persistentStore.Set("index", index) + // set forward mapping in memory store from capability to name - memoryStore.Set(sck.moduleName + "/fwd/" + capability, name) - // set reverse mapping in memory store from name to capability - memoryStore.Set(sck.moduleName + "/rev/" + name, capability) + memStore.Set(sck.moduleName + "/fwd/" + capability, name) + + // set reverse mapping in memory store from name to index + memStore.Set(sck.moduleName + "/rev/" + name, index) + + // set the in-memory mapping from index to capability pointer + capMap[index] = capability + // return the newly created capability return capability } @@ -146,24 +180,28 @@ with which the calling module previously associated it. ```golang func (sck ScopedCapabilityKeeper) AuthenticateCapability(name string, capability Capability) bool { - memoryStore := ctx.KVStore(sck.memoryKey) // return whether forward mapping in memory store matches name - return memoryStore.Get(sck.moduleName + "/fwd/" + capability) === name + return memStore.Get(sck.moduleName + "/fwd/" + capability) === name } ``` -`ClaimCapability` allows a module to claim a capability key which it has received from another module so that future `GetCapability` calls will succeed. +`ClaimCapability` allows a module to claim a capability key which it has received from another module +so that future `GetCapability` calls will succeed. -`ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name in the future. Capabilities are multi-owner, so if multiple modules have a single `Capability` reference, they will all own it. +`ClaimCapability` MUST be called if a module which receives a capability wishes to access it by name +in the future. Capabilities are multi-owner, so if multiple modules have a single `Capability` reference, +they will all own it. ```golang func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capability, name string) error { persistentStore := ctx.KVStore(sck.persistentKey) - memoryStore := ctx.KVStore(sck.memoryKey) + // set forward mapping in memory store from capability to name - memoryStore.Set(sck.moduleName + "/fwd/" + capability, name) + memStore.Set(sck.moduleName + "/fwd/" + capability, name) + // set reverse mapping in memory store from name to capability - memoryStore.Set(sck.moduleName + "/rev/" + name, capability) + memStore.Set(sck.moduleName + "/rev/" + name, capability) + // update owner set in persistent store owners := persistentStore.Get(capability.Index()) owners.add(sck.moduleName + "/" + name) @@ -171,26 +209,53 @@ func (sck ScopedCapabilityKeeper) ClaimCapability(ctx Context, capability Capabi } ``` -`GetCapability` allows a module to fetch a capability which it has previously claimed by name. The module is not allowed to retrieve capabilities which it does not own. If another module -claims a capability, the previously owning module will no longer be able to claim it. +`GetCapability` allows a module to fetch a capability which it has previously claimed by name. +The module is not allowed to retrieve capabilities which it does not own. ```golang func (sck ScopedCapabilityKeeper) GetCapability(ctx Context, name string) (Capability, error) { - memoryStore := ctx.KVStore(sck.memoryKey) - // fetch capability from memory store - capability := memoryStore.Get(sck.moduleName + "/rev/" + name) + // fetch the index of capability using reverse mapping in memstore + index := memStore.Get(sck.moduleName + "/rev/" + name) + + // fetch capability from go-map using index + capability := capMap[index] + // return the capability return capability } ``` -### Memory store +`ReleaseCapability` allows a module to release a capability which it had previously claimed. If no +more owners exist, the capability will be deleted globally. -A new store key type, `MemoryStoreKey`, will be added to the `store` package. The `MemoryStoreKey`s work just like `StoreKey`s. +```golang +func (sck ScopedCapabilityKeeper) ReleaseCapability(ctx Context, capability Capability) err { + persistentStore := ctx.KVStore(sck.persistentKey) -The memory store will work just like the current transient store, except that it will not create a new `dbadapter.Store` when `Commit()` is called, but instead retain the current one (so that state will persist across blocks). + name := capStore.Get(sck.moduleName + "/fwd/" + capability) + if name == nil { + return error("capability not owned by module") + } -Initially the memory store will only be used by the `CapabilityKeeper`, but it could be used by other modules in the future. + // delete forward mapping in memory store + memoryStore.Delete(sck.moduleName + "/fwd/" + capability, name) + + // delete reverse mapping in memory store + memoryStore.Delete(sck.moduleName + "/rev/" + name, capability) + + // update owner set in persistent store + owners := persistentStore.Get(capability.Index()) + owners.remove(sck.moduleName + "/" + name) + if owners.size() > 0 { + // there are still other owners, keep the capability around + persistentStore.Set(capability.Index(), owners) + } else { + // no more owners, delete the capability + persistentStore.Delete(capability.Index()) + delete(capMap[capability.Index()]) + } +} +``` ### Usage patterns @@ -262,11 +327,13 @@ Proposed. ### Positive - Dynamic capability support. +- Allows CapabilityKeeper to return same capability pointer from go-map while reverting any writes to the persistent `KVStore` and in-memory `MemoryStore` on tx failure. ### Negative - Requires an additional keeper. - Some overlap with existing `StoreKey` system (in the future they could be combined, since this is a superset functionality-wise). +- Requires an extra level of indirection in the reverse mapping, since MemoryStore must map to index which must then be used as key in a go map to retrieve the actual capability ### Neutral diff --git a/docs/architecture/adr-004-split-denomination-keys.md b/docs/architecture/adr-004-split-denomination-keys.md index 5e91f7da3561..a718d9b0ca45 100644 --- a/docs/architecture/adr-004-split-denomination-keys.md +++ b/docs/architecture/adr-004-split-denomination-keys.md @@ -5,6 +5,13 @@ - 2020-01-08: Initial version - 2020-01-09: Alterations to handle vesting accounts - 2020-01-14: Updates from review feedback +- 2020-01-30: Updates from implementation + + +### Glossary + +* denom / denomination key -- unique token identifier. + ## Context @@ -18,49 +25,91 @@ Balances shall be stored per-account & per-denomination under a denomination- an ### Account interface (x/auth) -`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will now be stored in & managed by the bank module. +`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will +now be stored in & managed by the bank module. -`SpendableCoinsVestingAccount()` and `TrackDelegation()` will be altered to take a bank keeper and a denomination as two additional arguments, which will be used to lookup the balances from the base account as necessary. +The vesting account interface will replace `SpendableCoins` in favor of `LockedCoins` which does +not require the account balance anymore. In addition, `TrackDelegation()` will now accept the +account balance of all tokens denominated in the vesting balance instead of loading the entire +account balance. -Vesting accounts will continue to store original vesting, delegated free, and delegated vesting coins (which is safe since these cannot contain arbitrary denominations). +Vesting accounts will continue to store original vesting, delegated free, and delegated +vesting coins (which is safe since these cannot contain arbitrary denominations). ### Bank keeper (x/bank) -`GetBalance(addr AccAddress, denom string) sdk.Coin` and `SetBalance(addr AccAddress, coin sdk.Coin)` methods will be added to the bank keeper to retrieve & set balances, respectively. +The following APIs will be added to the `x/bank` keeper: + +- `GetAllBalances(ctx Context, addr AccAddress) Coins` +- `GetBalance(ctx Context, addr AccAddress, denom string) Coin` +- `SetBalance(ctx Context, addr AccAddress, coin Coin)` +- `LockedCoins(ctx Context, addr AccAddress) Coins` +- `SpendableCoins(ctx Context, addr AccAddress) Coins` + +Additional APIs may be added to facilitate iteration and auxiliary functionality not essential to +core functionality or persistence. -Balances will be stored first by the address, then by the denomination (the reverse is also possible, but retrieval of all balances for a single account is presumed to be more frequent): +Balances will be stored first by the address, then by the denomination (the reverse is also possible, +but retrieval of all balances for a single account is presumed to be more frequent): ```golang -func BalanceKey(addr sdk.AccAddress, denom string) []byte { - return append(append(BalanceKeyPrefix, addr.Bytes()...), []byte(denom)...) +var BalancesPrefix = []byte("balances") + +func (k Keeper) SetBalance(ctx Context, addr AccAddress, balance Coin) error { + if !balance.IsValid() { + return err + } + + store := ctx.KVStore(k.storeKey) + balancesStore := prefix.NewStore(store, BalancesPrefix) + accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + + bz := Marshal(balance) + accountStore.Set([]byte(balance.Denom), bz) + + return nil } ``` -`DelegateCoins()` and `UndelegateCoins()` will be altered to take a single `sdk.Coin` (one denomination & amount) instead of `sdk.Coins`, since they should only operate on one denomination. They will read balances directly instead of calling `GetCoins()` (which no longer exists). +This will result in the balances being indexed by the byte representation of +`balances/{address}/{denom}`. + +`DelegateCoins()` and `UndelegateCoins()` will be altered to only load each individual +account balance by denomination found in the (un)delegation amount. As a result, +any mutations to the account balance by will made by denomination. -`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist). +`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances +directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist). -`trackDelegation()` and `trackUndelegation()` will be altered to read & write the balances directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist). +`trackDelegation()` and `trackUndelegation()` will be altered to no longer update +account balances. -External APIs will need to scan all balances under an account to retain backwards-compatibility - additional methods should be added to fetch a balance for a single denomination only. +External APIs will need to scan all balances under an account to retain backwards-compatibility. It +is advised that these APIs use `GetBalance` and `SetBalance` instead of `GetAllBalances` when +possible as to not load the entire account balance. ### Supply module -The supply module, in order to implement the total supply invariant, will now need to scan all accounts & call `GetBalance` using the `x/bank` Keeper for the denomination in question, then sum the balances and check that they match the expected total supply. +The supply module, in order to implement the total supply invariant, will now need +to scan all accounts & call `GetAllBalances` using the `x/bank` Keeper, then sum +the balances and check that they match the expected total supply. ## Status -Proposed. +Accepted. ## Consequences ### Positive -- O(1) reads & writes of balances (with respect to the number of denominations for which an account has non-zero balances) +- O(1) reads & writes of balances (with respect to the number of denominations for +which an account has non-zero balances). Note, this does not relate to the actual +I/O cost, rather the total number of direct reads needed. ### Negative -- Slighly less efficient reads/writes when reading & writing all balances of a single account in a transaction. +- Slightly less efficient reads/writes when reading & writing all balances of a +single account in a transaction. ### Neutral @@ -68,6 +117,6 @@ None in particular. ## References -Ref https://github.com/cosmos/cosmos-sdk/issues/4982 -Ref https://github.com/cosmos/cosmos-sdk/issues/5467 -Ref https://github.com/cosmos/cosmos-sdk/issues/5492 +- Ref: https://github.com/cosmos/cosmos-sdk/issues/4982 +- Ref: https://github.com/cosmos/cosmos-sdk/issues/5467 +- Ref: https://github.com/cosmos/cosmos-sdk/issues/5492 diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index 8ef5d6e5cdd6..f6f47170c9f1 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -17,7 +17,7 @@ evidence can be submitted, evaluated and verified resulting in some agreed upon penalty for any misbehavior committed by a validator, such as equivocation (double-voting), signing when unbonded, signing an incorrect state transition (in the future), etc. Furthermore, such a mechanism is paramount for any -[IBC](https://github.com/cosmos/ics/blob/master/ibc/1_IBC_ARCHITECTURE.md) or +[IBC](https://github.com/cosmos/ics/blob/master/ibc/2_IBC_ARCHITECTURE.md) or cross-chain validation protocol implementation in order to support the ability for any misbehavior to be relayed back from a collateralized chain to a primary chain so that the equivocating validator(s) can be slashed. @@ -28,14 +28,14 @@ We will implement an evidence module in the Cosmos SDK supporting the following functionality: - Provide developers with the abstractions and interfaces necessary to define -custom evidence messages, message handlers, and methods to slash and penalize -accordingly for misbehavior. + custom evidence messages, message handlers, and methods to slash and penalize + accordingly for misbehavior. - Support the ability to route evidence messages to handlers in any module to -determine the validity of submitted misbehavior. + determine the validity of submitted misbehavior. - Support the ability, through governance, to modify slashing penalties of any -evidence type. + evidence type. - Querier implementation to support querying params, evidence types, params, and -all submitted valid misbehavior. + all submitted valid misbehavior. ### Types @@ -161,15 +161,15 @@ type GenesisState struct { ### Positive - Allows the state machine to process misbehavior submitted on-chain and penalize -validators based on agreed upon slashing parameters. + validators based on agreed upon slashing parameters. - Allows evidence types to be defined and handled by any module. This further allows -slashing and jailing to be defined by more complex mechanisms. + slashing and jailing to be defined by more complex mechanisms. - Does not solely rely on Tendermint to submit evidence. ### Negative - No easy way to introduce new evidence types through governance on a live chain -due to the inability to introduce the new evidence type's corresponding handler + due to the inability to introduce the new evidence type's corresponding handler ### Neutral diff --git a/docs/architecture/adr-011-generalize-genesis-accounts.md b/docs/architecture/adr-011-generalize-genesis-accounts.md index 94e1dd1447b8..db323966e1f0 100644 --- a/docs/architecture/adr-011-generalize-genesis-accounts.md +++ b/docs/architecture/adr-011-generalize-genesis-accounts.md @@ -77,10 +77,10 @@ type ModuleAccount struct { The `auth` codec definition: ```go -var ModuleCdc *codec.Codec +var ModuleCdc *codec.LegacyAmino func init() { - ModuleCdc = codec.New() + ModuleCdc = codec.NewLegacyAmino() // register module msg's and Account interface ... // leave the codec unsealed diff --git a/docs/architecture/adr-012-state-accessors.md b/docs/architecture/adr-012-state-accessors.md index 61cdbc94fa48..b66e23eb67b2 100644 --- a/docs/architecture/adr-012-state-accessors.md +++ b/docs/architecture/adr-012-state-accessors.md @@ -41,7 +41,7 @@ We will define a type named `Mapping`: ```go type Mapping struct { storeKey sdk.StoreKey - cdc *codec.Codec + cdc *codec.LegacyAmino prefix []byte } ``` diff --git a/docs/architecture/adr-013-metrics.md b/docs/architecture/adr-013-metrics.md new file mode 100644 index 000000000000..cf27d89648ce --- /dev/null +++ b/docs/architecture/adr-013-metrics.md @@ -0,0 +1,157 @@ +# ADR 013: Observability + +## Changelog + +- 20-01-2020: Initial Draft + +## Status + +Proposed + +## Context + +Telemetry is paramount into debugging and understanding what the application is doing and how it is +performing. We aim to expose metrics from modules and other core parts of the Cosmos SDK. + +In addition, we should aim to support multiple configurable sinks that an operator may choose from. +By default, when telemetry is enabled, the application should track and expose metrics that are +stored in-memory. The operator may choose to enable additional sinks, where we support only +[Prometheus](https://prometheus.io/) for now, as it's battle-tested, simple to setup, open source, +and is rich with ecosystem tooling. + +We must also aim to integrate metrics into the Cosmos SDK in the most seamless way possible such that +metrics may be added or removed at will and without much friction. To do this, we will use the +[go-metrics](https://github.com/armon/go-metrics) library. + +Finally, operators may enable telemetry along with specific configuration options. If enabled, metrics +will be exposed via `/metrics?format={text|prometheus}` via the API server. + +## Decision + +We will add an additional configuration block to `app.toml` that defines telemetry settings: + +```toml +############################################################################### +### Telemetry Configuration ### +############################################################################### + +[telemetry] + +# Prefixed with keys to separate services +service-name = {{ .Telemetry.ServiceName }} + +# Enabled enables the application telemetry functionality. When enabled, +# an in-memory sink is also enabled by default. Operators may also enabled +# other sinks such as Prometheus. +enabled = {{ .Telemetry.Enabled }} + +# Enable prefixing gauge values with hostname +enable-hostname = {{ .Telemetry.EnableHostname }} + +# Enable adding hostname to labels +enable-hostname-label = {{ .Telemetry.EnableHostnameLabel }} + +# Enable adding service to labels +enable-service-label = {{ .Telemetry.EnableServiceLabel }} + +# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. +prometheus-retention-time = {{ .Telemetry.PrometheusRetentionTime }} +``` + +The given configuration allows for two sinks -- in-memory and Prometheus. We create a `Metrics` +type that performs all the bootstrapping for the operator, so capturing metrics becomes seamless. + +```go +// Metrics defines a wrapper around application telemetry functionality. It allows +// metrics to be gathered at any point in time. When creating a Metrics object, +// internally, a global metrics is registered with a set of sinks as configured +// by the operator. In addition to the sinks, when a process gets a SIGUSR1, a +// dump of formatted recent metrics will be sent to STDERR. +type Metrics struct { + memSink *metrics.InmemSink + prometheusEnabled bool +} + +// Gather collects all registered metrics and returns a GatherResponse where the +// metrics are encoded depending on the type. Metrics are either encoded via +// Prometheus or JSON if in-memory. +func (m *Metrics) Gather(format string) (GatherResponse, error) { + switch format { + case FormatPrometheus: + return m.gatherPrometheus() + + case FormatText: + return m.gatherGeneric() + + case FormatDefault: + return m.gatherGeneric() + + default: + return GatherResponse{}, fmt.Errorf("unsupported metrics format: %s", format) + } +} +``` + +In addition, `Metrics` allows us to gather the current set of metrics at any given point in time. An +operator may also choose to send a signal, SIGUSR1, to dump and print formatted metrics to STDERR. + +During an application's bootstrapping and construction phase, if `Telemetry.Enabled` is `true`, the +API server will create an instance of a reference to `Metrics` object and will register a metrics +handler accordingly. + +```go +func (s *Server) Start(cfg config.Config) error { + // ... + + if cfg.Telemetry.Enabled { + m, err := telemetry.New(cfg.Telemetry) + if err != nil { + return err + } + + s.metrics = m + s.registerMetrics() + } + + // ... +} + +func (s *Server) registerMetrics() { + metricsHandler := func(w http.ResponseWriter, r *http.Request) { + format := strings.TrimSpace(r.FormValue("format")) + + gr, err := s.metrics.Gather(format) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to gather metrics: %s", err)) + return + } + + w.Header().Set("Content-Type", gr.ContentType) + _, _ = w.Write(gr.Metrics) + } + + s.Router.HandleFunc("/metrics", metricsHandler).Methods("GET") +} +``` + +Application developers may track counters, gauges, summaries, and key/value metrics. There is no +additional lifting required by modules to leverage profiling metrics. To do so, it's as simple as: + +```go +func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { + defer metrics.MeasureSince(time.Now(), "MintCoins") + // ... +} +``` + +## Consequences + +### Positive + +- Exposure into the performance and behavior of an application + +### Negative + +### Neutral + +## References diff --git a/docs/architecture/adr-014-proportional-slashing.md b/docs/architecture/adr-014-proportional-slashing.md new file mode 100644 index 000000000000..d481916343ca --- /dev/null +++ b/docs/architecture/adr-014-proportional-slashing.md @@ -0,0 +1,85 @@ +# ADR 14: Proportional Slashing + +## Changelog + +- 2019-10-15: Initial draft +- 2020-05-25: Removed correlation root slashing +- 2020-07-01: Updated to include S-curve function instead of linear + +## Context + +In Proof of Stake-based chains, centralization of consensus power amongst a small set of validators can cause harm to the network due to increased risk of censorship, liveness failure, fork attacks, etc. However, while this centralization causes a negative externality to the network, it is not directly felt by the delegators contributing towards delegating towards already large validators. We would like a way to pass on the negative externality cost of centralization onto those large validators and their delegators. + +## Decision + +### Design + +To solve this problem, we will implement a procedure called Proportional Slashing. The desire is that the larger a validator is, the more they should be slashed. The first naive attempt is to make a validator's slash percent proportional to their share of consensus voting power. + +``` +slash_amount = k * power // power is the faulting validator's voting power and k is some on-chain constant +``` + +However, this will incentivize validators with large amounts of stake to split up their voting power amongst accounts (sybil attack), so that if they fault, they all get slashed at a lower percent. The solution to this is to take into account not just a validator's own voting percentage, but also the voting percentage of all the other validators who get slashed in a specified time frame. + +``` +slash_amount = k * (power_1 + power_2 + ... + power_n) // where power_i is the voting power of the ith validator faulting in the specified time frame and k is some on-chain constant +``` + +Now, if someone splits a validator of 10% into two validators of 5% each which both fault, then they both fault in the same time frame, they both will get slashed at the sum 10% amount. + +However in practice, we likely don't want a linear relation between amount of stake at fault, and the percentage of stake to slash. In particular, solely 5% of stake double signing effectively did nothing to majorly threaten security, whereas 30% of stake being at fault clearly merits a large slashing factor, due to being very close to the point at which Tendermint security is threatened. A linear relation would require a factor of 6 gap between these two, whereas the difference in risk posed to the network is much larger. We propose using S-curves (formally [logistic functions](https://en.wikipedia.org/wiki/Logistic_function) to solve this). S-Curves capture the desired criterion quite well. They allow the slashing factor to be minimal for small values, and then grow very rapidly near some threshold point where the risk posed becomes notable. + +#### Parameterization + +This requires parameterizing a logistic function. It is very well understood how to parameterize this. It has four parameters: +1) A minimum slashing factor +2) A maximum slashing factor +3) The inflection point of the S-curve (essentially where do you want to center the S) +4) The rate of growth of the S-curve (How elongated is the S) + +#### Correlation across non-sybil validators + +One will note, that this model doesn't differentiate between multiple validators run by the same operators vs validators run by different operators. This can be seen as an additional benefit in fact. It incentivizes validators to differentiate their setups from other validators, to avoid having correlated faults with them or else they risk a higher slash. So for example, operators should avoid using the same popular cloud hosting platforms or using the same Staking as a Service providers. This will lead to a more resilient and decentralized network. + +#### Griefing + +Griefing, the act of intentionally getting oneself slashed in order to make another's slash worse, could be a concern here. However, using the protocol described here, the attacker also gets equally impacted by the grief as the victim, so it would not provide much benefit to the griefer. + +### Implementation + +In the slashing module, we will add two queues that will track all of the recent slash events. For double sign faults, we will define "recent slashes" as ones that have occured within the last `unbonding period`. For liveness faults, we will define "recent slashes" as ones that have occured withing the last `jail period`. + +``` +type SlashEvent struct { + Address sdk.ValAddress + ValidatorVotingPercent sdk.Dec + SlashedSoFar sdk.Dec +} +``` + +These slash events will be pruned from the queue once they are older than their respective "recent slash period". + +Whenever a new slash occurs, a `SlashEvent` struct is created with the faulting validator's voting percent and a `SlashedSoFar` of 0. Because recent slash events are pruned before the unbonding period and unjail period expires, it should not be possible for the same validator to have multiple SlashEvents in the same Queue at the same time. + +We then will iterate over all the SlashEvents in the queue, adding their `ValidatorVotingPercent` to calculate the new percent to slash all the validators in the queue at, using the "Square of Sum of Roots" formula introduced above. + +Once we have the `NewSlashPercent`, we then iterate over all the `SlashEvent`s in the queue once again, and if `NewSlashPercent > SlashedSoFar` for that SlashEvent, we call the `staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)` (we pass in the power of the validator before any slashes occured, so that we slash the right amount of tokens). We then set `SlashEvent.SlashedSoFar` amount to `NewSlashPercent`. + + +## Status + +Proposed + +## Consequences + +### Positive + +- Increases decentralization by disincentivizing delegating to large validators +- Incentivizes Decorrelation of Validators +- More severely punishes attacks than accidental faults +- More flexibility in slashing rates parameterization + +### Negative + +- More computationally expensive than current implementation. Will require more data about "recent slashing events" to be stored on chain. diff --git a/docs/architecture/adr-015-ibc-packet-receiver.md b/docs/architecture/adr-015-ibc-packet-receiver.md index 2ea26dcb5bae..924ddc7eb648 100644 --- a/docs/architecture/adr-015-ibc-packet-receiver.md +++ b/docs/architecture/adr-015-ibc-packet-receiver.md @@ -192,22 +192,7 @@ func NewAnteHandler( } ``` -The implementation of this ADR will also change the `Data` field of the `Packet` type from `[]byte` (i.e. arbitrary data) to `PacketDataI`. We want to make application modules be able to register custom packet data type which is automatically unmarshaled at `TxDecoder` time and can be simply type switched inside the application handler. Also, by having `GetCommitment()` method instead of manually generate the commitment inside the IBC keeper, the applications can define their own commitment method, including bare bytes, hashing, etc. - -This also removes the `Timeout` field from the `Packet` struct. This is because the `PacketDataI` interface now contains this information. You can see details about this in [ICS04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#definitions). - -The `PacketDataI` is the application specific interface that provides information for the execution of the application packet. In the case of ICS20 this would be `denom`, `amount` and `address` - -```go -// PacketDataI defines the standard interface for IBC packet data -type PacketDataI interface { - GetCommitment() []byte // Commitment form that will be stored in the state. - GetTimeoutHeight() uint64 - - ValidateBasic() error - Type() string -} -``` +The implementation of this ADR will also create a `Data` field of the `Packet` of type `[]byte`, which can be deserialised by the receiving module into its own private type. It is up to the application modules to do this according to their own interpretation, not by the IBC keeper. This is crucial for dynamic IBC. Example application-side usage: @@ -234,15 +219,17 @@ func NewHandler(k Keeper) Handler { case MsgTransfer: return handleMsgTransfer(ctx, k, msg) case ibc.MsgPacket: - switch data := msg.Packet.Data.(type) { - case PacketDataTransfer: // i.e fulfills the PacketDataI interface - return handlePacketDataTransfer(ctx, k, msg.Packet, data) + var data PacketDataTransfer + if err := types.ModuleCodec.UnmarshalBinaryBare(msg.GetData(), &data); err != nil { + return err } - case ibc.MsgTimeoutPacket: - switch packet := msg.Packet.Data.(type) { - case PacketDataTransfer: // i.e fulfills the PacketDataI interface - return handleTimeoutPacketDataTransfer(ctx, k, msg.Packet) + return handlePacketDataTransfer(ctx, k, msg, data) + case ibc.MsgTimeoutPacket: + var data PacketDataTransfer + if err := types.ModuleCodec.UnmarshalBinaryBare(msg.GetData(), &data); err != nil { + return err } + return handleTimeoutPacketDataTransfer(ctx, k, packet) // interface { PortID() string; ChannelID() string; Channel() ibc.Channel } // MsgChanInit, MsgChanTry implements ibc.MsgChannelOpen case ibc.MsgChannelOpen: diff --git a/docs/architecture/adr-018-extendable-voting-period.md b/docs/architecture/adr-018-extendable-voting-period.md new file mode 100644 index 000000000000..fdf5bb2584e2 --- /dev/null +++ b/docs/architecture/adr-018-extendable-voting-period.md @@ -0,0 +1,66 @@ +# ADR 18: Extendable Voting Periods + +## Changelog + +- 1 January 2020: Start of first version + +## Context + +Currently the voting period for all governance proposals is the same. However, this is suboptimal as all governance proposals do not require the same time period. For more non-contentious proposals, they can be dealt with more efficently with a faster period, while more contentious or complex proposals may need a longer period for extended discussion/consideration. + +## Decision + +We would like to design a mechanism for making the voting period of a governance proposal variable based on the demand of voters. We would like it to be based on the view of the governance participants, rather than just the proposer of a governance proposal (thus, allowing the proposer to select the voting period length is not sufficient). + +However, we would like to avoid the creation of an entire second voting process to determine the length of the voting period, as it just pushed the problem to determining the length of that first voting period. + +Thus, we propose the following mechanism: + +### Params: + +- The current gov param `VotingPeriod` is to be replaced by a `MinVotingPeriod` param. This is the the default voting period that all governance proposal voting periods start with. +- There is a new gov param called `MaxVotingPeriodExtension`. + +### Mechanism + +There is a new `Msg` type called `MsgExtendVotingPeriod`, which can be sent by any staked account during a proposal's voting period. It allows the sender to unilaterally extend the length of the voting period by `MaxVotingPeriodExtension * sender's share of voting power`. Every address can only call `MsgExtendVotingPeriod` once per proposal. + +So for example, if the `MaxVotingPeriodExtension` is set to 100 Days, then anyone with 1% of voting power can extend the voting power by 1 day. If 33% of voting power has sent the message, the voting period will be extended by 33 days. Thus, if absolutely everyone chooses to extend the voting period, the absolute maximum voting period will be `MinVotingPeriod + MaxVotingPeriodExtension`. + +This system acts as a sort of distributed coordination, where individual stakers choosing to extend or not, allows the system the guage the conentiousness/complexity of the proposal. It is extremely unlikely that many stakers will choose to extend at the exact same time, it allows stakers to view how long others have already extended thus far, to decide whether or not to extend further. + +### Dealing with Unbonding/Redelegation + +There is one thing that needs to be addressed. How to deal with redelegation/unbonding during the voting period. If a staker of 5% calls `MsgExtendVotingPeriod` and then unbonds, does the voting period then decrease by 5 days again? This is not good as it can give people a false sense of how long they have to make their decision. For this reason, we want to design it such that the voting period length can only be extended, not shortened. To do this, the current extension amount is based on the highest percent that voted extension at any time. This is best explained by example: + +1. Let's say 2 stakers of voting power 4% and 3% respectively vote to extend. The voting period will be extended by 7 days. +2. Now the staker of 3% decides to unbond before the end of the voting period. The voting period extension remains 7 days. +3. Now, let's say another staker of 2% voting power decides to extend voting period. There is now 6% of active voting power choosing the extend. The voting power remains 7 days. +4. If a fourth staker of 10% chooses to extend now, there is a total of 16% of active voting power wishing to extend. The voting period will be extended to 16 days. + +### Delegators + +Just like votes in the actual voting period, delegators automatically inherit the extension of their validators. If their validator chooses to extend, their voting power will be used in the validator's extension. However, the delegator is unable to override their validator and "unextend" as that would contradict the "voting power length can only be ratcheted up" principle described in the previous section. However, a delegator may choose the extend using their personal voting power, if their validator has not done so. + +## Status + +Proposed + +## Consequences + +### Positive + +- More complex/contentious governance proposals will have more time to properly digest and deliberate + +### Negative + +- Governance process becomes more complex and requires more understanding to interact with effectively +- Can no longer predict when a governance proposal will end. Can't assume order in which governance proposals will end. + +### Neutral + +- The minimum voting period can be made shorter + +## References + +- [Cosmos Forum post where idea first originated](https://forum.cosmos.network/t/proposal-draft-reduce-governance-voting-period-to-7-days/3032/9) diff --git a/docs/architecture/adr-019-protobuf-state-encoding.md b/docs/architecture/adr-019-protobuf-state-encoding.md new file mode 100644 index 000000000000..2a4d8b046258 --- /dev/null +++ b/docs/architecture/adr-019-protobuf-state-encoding.md @@ -0,0 +1,375 @@ +# ADR 019: Protocol Buffer State Encoding + +## Changelog + +- 2020 Feb 15: Initial Draft +- 2020 Feb 24: Updates to handle messages with interface fields +- 2020 Apr 27: Convert usages of `oneof` for interfaces to `Any` +- 2020 May 15: Describe `cosmos_proto` extensions and amino compatibility +- 2020 Dec 4: Move and rename `MarshalAny` and `UnmarshalAny` into the `codec.Marshaler` interface. + +## Status + +Accepted + +## Context + +Currently, the Cosmos SDK utilizes [go-amino](https://github.com/tendermint/go-amino/) for binary +and JSON object encoding over the wire bringing parity between logical objects and persistence objects. + +From the Amino docs: + +> Amino is an object encoding specification. It is a subset of Proto3 with an extension for interface +> support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) for more +> information on Proto3, which Amino is largely compatible with (but not with Proto2). +> +> The goal of the Amino encoding protocol is to bring parity into logic objects and persistence objects. + +Amino also aims to have the following goals (not a complete list): + +- Binary bytes must be decode-able with a schema. +- Schema must be upgradeable. +- The encoder and decoder logic must be reasonably simple. + +However, we believe that Amino does not fulfill these goals completely and does not fully meet the +needs of a truly flexible cross-language and multi-client compatible encoding protocol in the Cosmos SDK. +Namely, Amino has proven to be a big pain-point in regards to supporting object serialization across +clients written in various languages while providing virtually little in the way of true backwards +compatibility and upgradeability. Furthermore, through profiling and various benchmarks, Amino has +been shown to be an extremely large performance bottleneck in the Cosmos SDK 1. This is +largely reflected in the performance of simulations and application transaction throughput. + +Thus, we need to adopt an encoding protocol that meets the following criteria for state serialization: + +- Language agnostic +- Platform agnostic +- Rich client support and thriving ecosystem +- High performance +- Minimal encoded message size +- Codegen-based over reflection-based +- Supports backward and forward compatibility + +Note, migrating away from Amino should be viewed as a two-pronged approach, state and client encoding. +This ADR focuses on state serialization in the Cosmos SDK state machine. A corresponding ADR will be +made to address client-side encoding. + +## Decision + +We will adopt [Protocol Buffers](https://developers.google.com/protocol-buffers) for serializing +persisted structured data in the Cosmos SDK while providing a clean mechanism and developer UX for +applications wishing to continue to use Amino. We will provide this mechanism by updating modules to +accept a codec interface, `Marshaler`, instead of a concrete Amino codec. Furthermore, the Cosmos SDK +will provide three concrete implementations of the `Marshaler` interface: `AminoCodec`, `ProtoCodec`, +and `HybridCodec`. + +- `AminoCodec`: Uses Amino for both binary and JSON encoding. +- `ProtoCodec`: Uses Protobuf for or both binary and JSON encoding. +- `HybridCodec`: Uses Amino for JSON encoding and Protobuf for binary encoding. + +Until the client migration landscape is fully understood and designed, modules will use a `HybridCodec` +as the concrete codec it accepts and/or extends. This means that all client JSON encoding, including +genesis state, will still use Amino. The ultimate goal will be to replace Amino JSON encoding with +Protbuf encoding and thus have modules accept and/or extend `ProtoCodec`. + +### Module Codecs + +Modules that do not require the ability to work with and serialize interfaces, the path to Protobuf +migration is pretty straightforward. These modules are to simply migrate any existing types that +are encoded and persisted via their concrete Amino codec to Protobuf and have their keeper accept a +`Marshaler` that will be a `HybridCodec`. This migration is simple as things will just work as-is. + +Note, any business logic that needs to encode primitive types like `bool` or `int64` should use +[gogoprotobuf](https://github.com/gogo/protobuf) Value types. + +Example: + +```go + ts, err := gogotypes.TimestampProto(completionTime) + if err != nil { + // ... + } + + bz := cdc.MustMarshalBinaryBare(ts) +``` + +However, modules can vary greatly in purpose and design and so we must support the ability for modules +to be able to encode and work with interfaces (e.g. `Account` or `Content`). For these modules, they +must define their own codec interface that extends `Marshaler`. These specific interfaces are unique +to the module and will contain method contracts that know how to serialize the needed interfaces. + +Example: + +```go +// x/auth/types/codec.go + +type Codec interface { + codec.Marshaler + + MarshalAccount(acc exported.Account) ([]byte, error) + UnmarshalAccount(bz []byte) (exported.Account, error) + + MarshalAccountJSON(acc exported.Account) ([]byte, error) + UnmarshalAccountJSON(bz []byte) (exported.Account, error) +} +``` + +### Usage of `Any` to encode interfaces + +In general, module-level .proto files should define messages which encode interfaces +using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). +After [extension discussion](https://github.com/cosmos/cosmos-sdk/issues/6030), +this was chosen as the preferred alternative to application-level `oneof`s +as in our original protobuf design. The arguments in favor of `Any` can be +summarized as follows: + +* `Any` provides a simpler, more consistent client UX for dealing with +interfaces than app-level `oneof`s that will need to be coordinated more +carefully across applications. Creating a generic transaction +signing library using `oneof`s may be cumbersome and critical logic may need +to be reimplemented for each chain +* `Any` provides more resistance against human error than `oneof` +* `Any` is generally simpler to implement for both modules and apps + +The main counter-argument to using `Any` centers around its additional space +and possibly performance overhead. The space overhead could be dealt with using +compression at the persistence layer in the future and the performance impact +is likely to be small. Thus, not using `Any` is seem as a pre-mature optimization, +with user experience as the higher order concern. + +Note, that given the SDK's decision to adopt the `Codec` interfaces described +above, apps can still choose to use `oneof` to encode state and transactions +but it is not the recommended approach. If apps do choose to use `oneof`s +instead of `Any` they will likely lose compatibility with client apps that +support multiple chains. Thus developers should think carefully about whether +they care more about what is possibly a pre-mature optimization or end-user +and client developer UX. + +### Safe usage of `Any` + +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. + +To prevent this, we introduce a type registration mechanism for decoding `Any` +values into concrete types through the `InterfaceRegistry` interface which +bears some similarity to type registration with Amino: + +```go +type InterfaceRegistry interface { + // RegisterInterface associates protoName as the public name for the + // interface passed in as iface + // Ex: + // registry.RegisterInterface("cosmos_sdk.Msg", (*sdk.Msg)(nil)) + RegisterInterface(protoName string, iface interface{}) + + // RegisterImplementations registers impls as a concrete implementations of + // the interface iface + // Ex: + // registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{}) + RegisterImplementations(iface interface{}, impls ...proto.Message) + +} +``` + +In addition to serving as a whitelist, `InterfaceRegistry` can also serve +to communicate the list of concrete types that satisfy an interface to clients. + +In .proto files: +* fields which accept interfaces should be annotated with `cosmos_proto.accepts_interface` +using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` +* interface implementations should be annotated with `cosmos_proto.implements_interface` +using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface` + +In the future, `protoName`, `cosmos_proto.accepts_interface`, `cosmos_proto.implements_interface` +may be used via code generation, reflection &/or static linting. + +The same struct that implements `InterfaceRegistry` will also implement an +interface `InterfaceUnpacker` to be used for unpacking `Any`s: + +```go +type InterfaceUnpacker interface { + // UnpackAny unpacks the value in any to the interface pointer passed in as + // iface. Note that the type in any must have been registered with + // RegisterImplementations as a concrete type for that interface + // Ex: + // var msg sdk.Msg + // err := ctx.UnpackAny(any, &msg) + // ... + UnpackAny(any *Any, iface interface{}) error +} +``` + +Note that `InterfaceRegistry` usage does not deviate from standard protobuf +usage of `Any`, it just introduces a security and introspection layer for +golang usage. + +`InterfaceRegistry` will be a member of `ProtoCodec` and `HybridCodec` as +described above. In order for modules to register interface types, app modules +can optionally implement the following interface: + +```go +type InterfaceModule interface { + RegisterInterfaceTypes(InterfaceRegistry) +} +``` + +The module manager will include a method to call `RegisterInterfaceTypes` on +every module that implements it in order to populate the `InterfaceRegistry`. + +### Using `Any` to encode state + +The SDK will provide support methods `MarshalInterface` and `UnmarshalInterface` to hide a complexity of wrapping interface types into `Any` and allow easy serialization. + +```go +import "github.com/cosmos/cosmos-sdk/codec" + +// note: eviexported.Evidence is an interface type +func MarshalEvidence(cdc codec.BinaryMarshaler, e eviexported.Evidence) ([]byte, error) { + return cdc.MarshalInterface(e) +} + +func UnmarshalEvidence(cdc codec.BinaryMarshaler, bz []byte) (eviexported.Evidence, error) { + var evi eviexported.Evidence + err := cdc.UnmarshalInterface(&evi, bz) + return err, nil +} +``` + +### Using `Any` in `sdk.Msg`s + +A similar concept is to be applied for messages that contain interfaces fields. +For example, we can define `MsgSubmitEvidence` as follows where `Evidence` is +an interface: + +```protobuf +// x/evidence/types/types.proto + +message MsgSubmitEvidence { + bytes submitter = 1 + [ + (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress" + ]; + google.protobuf.Any evidence = 2; +} +``` + +Note that in order to unpack the evidence from `Any` we do need a reference to +`InterfaceRegistry`. In order to reference evidence in methods like +`ValidateBasic` which shouldn't have to know about the `InterfaceRegistry`, we +introduce an `UnpackInterfaces` phase to deserialization which unpacks +interfaces before they're needed. + +### Unpacking Interfaces + +To implement the `UnpackInterfaces` phase of deserialization which unpacks +interfaces wrapped in `Any` before they're needed, we create an interface +that `sdk.Msg`s and other types can implement: + +```go +type UnpackInterfacesMessage interface { + UnpackInterfaces(InterfaceUnpacker) error +} +``` + +We also introduce a private `cachedValue interface{}` field onto the `Any` +struct itself with a public getter `GetCachedValue() interface{}`. + +The `UnpackInterfaces` method is to be invoked during message deserialization right +after `Unmarshal` and any interface values packed in `Any`s will be decoded +and stored in `cachedValue` for reference later. + +Then unpacked interface values can safely be used in any code afterwards +without knowledge of the `InterfaceRegistry` +and messages can introduce a simple getter to cast the cached value to the +correct interface type. + +This has the added benefit that unmarshaling of `Any` values only happens once +during initial deserialization rather than every time the value is read. Also, +when `Any` values are first packed (for instance in a call to +`NewMsgSubmitEvidence`), the original interface value is cached so that +unmarshaling isn't needed to read it again. + +`MsgSubmitEvidence` could implement `UnpackInterfaces`, plus a convenience getter +`GetEvidence` as follows: + +```go +func (msg MsgSubmitEvidence) UnpackInterfaces(ctx sdk.InterfaceRegistry) error { + var evi eviexported.Evidence + return ctx.UnpackAny(msg.Evidence, *evi) +} + +func (msg MsgSubmitEvidence) GetEvidence() eviexported.Evidence { + return msg.Evidence.GetCachedValue().(eviexported.Evidence) +} +``` + +### Amino Compatibility + +Our custom implementation of `Any` can be used transparently with Amino if used +with the proper codec instance. What this means is that interfaces packed within +`Any`s will be amino marshaled like regular Amino interfaces (assuming they +have been registered properly with Amino). + +In order for this functionality to work: + +- **all legacy code must use `*codec.LegacyAmino` instead of `*amino.Codec` which is + now a wrapper which properly handles `Any`** +- **all new code should use `Marshaler` which is compatible with both amino and + protobuf** +- Also, before v0.39, `codec.LegacyAmino` will be renamed to `codec.LegacyAmino`. + +### Why Wasn't X Chosen Instead + +For a more complete comparison to alternative protocols, see [here](https://codeburst.io/json-vs-protocol-buffers-vs-flatbuffers-a4247f8bda6f). + +### Cap'n Proto + +While [Cap’n Proto](https://capnproto.org/) does seem like an advantageous alternative to Protobuf +due to it's native support for interfaces/generics and built in canonicalization, it does lack the +rich client ecosystem compared to Protobuf and is a bit less mature. + +### FlatBuffers + +[FlatBuffers](https://google.github.io/flatbuffers/) is also a potentially viable alternative, with the +primary difference being that FlatBuffers does not need a parsing/unpacking step to a secondary +representation before you can access data, often coupled with per-object memory allocation. + +However, it would require great efforts into research and full understanding the scope of the migration +and path forward -- which isn't immediately clear. In addition, FlatBuffers aren't designed for +untrusted inputs. + +## Future Improvements & Roadmap + +In the future we may consider a compression layer right above the persistence +layer which doesn't change tx or merkle tree hashes, but reduces the storage +overhead of `Any`. In addition, we may adopt protobuf naming conventions which +make type URLs a bit more concise while remaining descriptive. + +Additional code generation support around the usage of `Any` is something that +could also be explored in the future to make the UX for go developers more +seamless. + +## Consequences + +### Positive + +- Significant performance gains. +- Supports backward and forward type compatibility. +- Better support for cross-language clients. + +### Negative + +- Learning curve required to understand and implement Protobuf messages. +- Slightly larger message size due to use of `Any`, although this could be offset + by a compression layer in the future + +### Neutral + +## References + +1. https://github.com/cosmos/cosmos-sdk/issues/4977 +2. https://github.com/cosmos/cosmos-sdk/issues/5444 diff --git a/docs/architecture/adr-020-protobuf-transaction-encoding.md b/docs/architecture/adr-020-protobuf-transaction-encoding.md new file mode 100644 index 000000000000..0c050c5d14bb --- /dev/null +++ b/docs/architecture/adr-020-protobuf-transaction-encoding.md @@ -0,0 +1,461 @@ +# ADR 020: Protocol Buffer Transaction Encoding + +## Changelog + +- 2020 March 06: Initial Draft +- 2020 March 12: API Updates +- 2020 April 13: Added details on interface `oneof` handling +- 2020 April 30: Switch to `Any` +- 2020 May 14: Describe public key encoding +- 2020 June 08: Store `TxBody` and `AuthInfo` as bytes in `SignDoc`; Document `TxRaw` as broadcast and storage type. +- 2020 August 07: Use ADR 027 for serializing `SignDoc`. +- 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966). +- 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. +- 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. + +## Status + +Accepted + +## Context + +This ADR is a continuation of the motivation, design, and context established in +[ADR 019](./adr-019-protobuf-state-encoding.md), namely, we aim to design the +Protocol Buffer migration path for the client-side of the Cosmos SDK. + +Specifically, the client-side migration path primarily includes tx generation and +signing, message construction and routing, in addition to CLI & REST handlers and +business logic (i.e. queriers). + +With this in mind, we will tackle the migration path via two main areas, txs and +querying. However, this ADR solely focuses on transactions. Querying should be +addressed in a future ADR, but it should build off of these proposals. + +Based on detailed discussions ([\#6030](https://github.com/cosmos/cosmos-sdk/issues/6030) +and [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078)), the original +design for transactions was changed substantially from an `oneof` /JSON-signing +approach to the approach described below. + +## Decision + +### Transactions + +Since interface values are encoded with `google.protobuf.Any` in state (see [ADR 019](adr-019-protobuf-state-encoding.md)), +`sdk.Msg`s are encoding with `Any` in transactions. + +One of the main goals of using `Any` to encode interface values is to have a +core set of types which is reused by apps so that +clients can safely be compatible with as many chains as possible. + +It is one of the goals of this specification to provide a flexible cross-chain transaction +format that can serve a wide variety of use cases without breaking client +compatibility. + +In order to facilitate signing, transactions are separated into `TxBody`, +which will be re-used by `SignDoc` below, and `signatures`: + +```proto +// types/types.proto +package cosmos_sdk.v1; + +message Tx { + TxBody body = 1; + AuthInfo auth_info = 2; + // A list of signatures that matches the length and order of AuthInfo's signer_infos to + // allow connecting signature meta information like public key and signing mode by position. + repeated bytes signatures = 3; +} + +// A variant of Tx that pins the signer's exact binary represenation of body and +// auth_info. This is used for signing, broadcasting and verification. The binary +// `serialize(tx: TxRaw)` is stored in Tendermint and the hash `sha256(serialize(tx: TxRaw))` +// becomes the "txhash", commonly used as the transaction ID. +message TxRaw { + // A protobuf serialization of a TxBody that matches the representation in SignDoc. + bytes body = 1; + // A protobuf serialization of an AuthInfo that matches the representation in SignDoc. + bytes auth_info = 2; + // A list of signatures that matches the length and order of AuthInfo's signer_infos to + // allow connecting signature meta information like public key and signing mode by position. + repeated bytes signatures = 3; +} + +message TxBody { + // A list of messages to be executed. The required signers of those messages define + // the number and order of elements in AuthInfo's signer_infos and Tx's signatures. + // Each required signer address is added to the list only the first time it occurs. + // + // By convention, the first required signer (usually from the first message) is referred + // to as the primary signer and pays the fee for the whole transaction. + repeated google.protobuf.Any messages = 1; + string memo = 2; + int64 timeout_height = 3; + repeated google.protobuf.Any extension_options = 1023; +} + +message AuthInfo { + // This list defines the signing modes for the required signers. The number + // and order of elements must match the required signers from TxBody's messages. + // The first element is the primary signer and the one which pays the fee. + repeated SignerInfo signer_infos = 1; + // The fee can be calculated based on the cost of evaluating the body and doing signature verification of the signers. This can be estimated via simulation. + Fee fee = 2; +} + +message SignerInfo { + // The public key is optional for accounts that already exist in state. If unset, the + // verifier can use the required signer address for this position and lookup the public key. + google.protobuf.Any public_key = 1; + // ModeInfo describes the signing mode of the signer and is a nested + // structure to support nested multisig pubkey's + ModeInfo mode_info = 2; + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to prevent + // replay attacks. + uint64 sequence = 3; +} + +message ModeInfo { + oneof sum { + Single single = 1; + Multi multi = 2; + } + + // Single is the mode info for a single signer. It is structured as a message + // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the future + message Single { + SignMode mode = 1; + } + + // Multi is the mode info for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + CompactBitArray bitarray = 1; + // mode_infos is the corresponding modes of the signers of the multisig + // which could include nested multisig public keys + repeated ModeInfo mode_infos = 2; + } +} + +enum SignMode { + SIGN_MODE_UNSPECIFIED = 0; + + SIGN_MODE_DIRECT = 1; + + SIGN_MODE_TEXTUAL = 2; + + SIGN_MODE_LEGACY_AMINO_JSON = 127; +} +``` + +As will be discussed below, in order to include as much of the `Tx` as possible +in the `SignDoc`, `SignerInfo` is separated from signatures so that only the +raw signatures themselves live outside of what is signed over. + +Because we are aiming for a flexible, extensible cross-chain transaction +format, new transaction processing options should be added to `TxBody` as soon +those use cases are discovered, even if they can't be implemented yet. + +Because there is coordination overhead in this, `TxBody` includes an +`extension_options` field which can be used for any transaction processing +options that are not already covered. App developers should, nevertheless, +attempt to upstream important improvements to `Tx`. + +### Signing + +All of the signing modes below aim to provide the following guarantees: + +- **No Malleability**: `TxBody` and `AuthInfo` cannot change once the transaction + is signed +- **Predictable Gas**: if I am signing a transaction where I am paying a fee, + the final gas is fully dependent on what I am signing + +These guarantees give the maximum amount confidence to message signers that +manipulation of `Tx`s by intermediaries can't result in any meaningful changes. + +#### `SIGN_MODE_DIRECT` + +The "direct" signing behavior is to sign the raw `TxBody` bytes as broadcast over +the wire. This has the advantages of: + +- requiring the minimum additional client capabilities beyond a standard protocol + buffers implementation +- leaving effectively zero holes for transaction malleability (i.e. there are no + subtle differences between the signing and encoding formats which could + potentially be exploited by an attacker) + +Signatures are structured using the `SignDoc` below which reuses the serialization of +`TxBody` and `AuthInfo` and only adds the fields which are needed for signatures: + +```proto +// types/types.proto +message SignDoc { + // A protobuf serialization of a TxBody that matches the representation in TxRaw. + bytes body = 1; + // A protobuf serialization of an AuthInfo that matches the representation in TxRaw. + bytes auth_info = 2; + string chain_id = 3; + uint64 account_number = 4; +} +``` + +In order to sign in the default mode, clients take the following steps: + +1. Serialize `TxBody` and `AuthInfo` using any valid protobuf implementation. +2. Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). +3. Sign the encoded `SignDoc` bytes. +4. Build a `TxRaw` and serialize it for broadcasting. + +Signature verification is based on comparing the raw `TxBody` and `AuthInfo` +bytes encoded in `TxRaw` not based on any ["canonicalization"](https://github.com/regen-network/canonical-proto3) +algorithm which creates added complexity for clients in addition to preventing +some forms of upgradeability (to be addressed later in this document). + +Signature verifiers do: + +1. Deserialize a `TxRaw` and pull out `body` and `auth_info`. +2. Create a list of required signer addresses from the messages. +3. For each required signer: + - Pull account number and sequence from the state. + - Obtain the public key either from state or `AuthInfo`'s `signer_infos`. + - Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). + - Verify the signature at the the same list position against the serialized `SignDoc`. + +#### `SIGN_MODE_LEGACY_AMINO` + +In order to support legacy wallets and exchanges, Amino JSON will be temporarily +supported transaction signing. Once wallets and exchanges have had a +chance to upgrade to protobuf based signing, this option will be disabled. In +the meantime, it is foreseen that disabling the current Amino signing would cause +too much breakage to be feasible. Note that this is mainly a requirement of the +Cosmos Hub and other chains may choose to disable Amino signing immediately. + +Legacy clients will be able to sign a transaction using the current Amino +JSON format and have it encoded to protobuf using the REST `/tx/encode` +endpoint before broadcasting. + +#### `SIGN_MODE_TEXTUAL` + +As was discussed extensively in [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078), +there is a desire for a human-readable signing encoding, especially for hardware +wallets like the [Ledger](https://www.ledger.com) which display +transaction contents to users before signing. JSON was an attempt at this but +falls short of the ideal. + +`SIGN_MODE_TEXTUAL` is intended as a placeholder for a human-readable +encoding which will replace Amino JSON. This new encoding should be even more +focused on readability than JSON, possibly based on formatting strings like +[MessageFormat](http://userguide.icu-project.org/formatparse/messages). + +In order to ensure that the new human-readable format does not suffer from +transaction malleability issues, `SIGN_MODE_TEXTUAL` +requires that the _human-readable bytes are concatenated with the raw `SignDoc`_ +to generate sign bytes. + +Multiple human-readable formats (maybe even localized messages) may be supported +by `SIGN_MODE_TEXTUAL` when it is implemented. + +### Unknown Field Filtering + +Unknown fields in protobuf messages should generally be rejected by transaction +processors because: + +- important data may be present in the unknown fields, that if ignored, will + cause unexpected behavior for clients +- they present a malleability vulnerability where attackers can bloat tx size + by adding random uninterpreted data to unsigned content (i.e. the master `Tx`, + not `TxBody`) + +There are also scenarios where we may choose to safely ignore unknown fields +(https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-624400188) to +provide graceful forwards compatibility with newer clients. + +We propose that field numbers with bit 11 set (for most use cases this is +the range of 1024-2047) be considered non-critical fields that can safely be +ignored if unknown. + +To handle this we will need a unknown field filter that: + +- always rejects unknown fields in unsigned content (i.e. top-level `Tx` and + unsigned parts of `AuthInfo` if present based on the signing mode) +- rejects unknown fields in all messages (including nested `Any`s) other than + fields with bit 11 set + +This will likely need to be a custom protobuf parser pass that takes message bytes +and `FileDescriptor`s and returns a boolean result. + +### Public Key Encoding + +Public keys in the Cosmos SDK implement Tendermint's `crypto.PubKey` interface. +We propose to use `Any` for protobuf encoding as we are doing with other interfaces (e.g. in `BaseAccount` `PubKey` or `SignerInfo` `PublicKey`). +Following public keys are implemented: secp256k1, ed25519 and multisignature. + +Ex: + +```proto +message PubKey { + bytes key = 1; +} +``` + +`multisig.LegacyAminoPubKey` has an array of `Any`'s member to support any +protobuf public key type. + +Apps should only attempt to handle a registered set of public keys that they +have tested. The provided signature verification ante handler decorators will +enforce this. + +### CLI & REST + +Currently, the REST and CLI handlers encode and decode types and txs via Amino +JSON encoding using a concrete Amino codec. Being that some of the types dealt with +in the client can be interfaces, similar to how we described in [ADR 019](./adr-019-protobuf-state-encoding.md), +the client logic will now need to take a codec interface that knows not only how +to handle all the types, but also knows how to generate transactions, signatures, +and messages. + +```go +type AccountRetriever interface { + GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error) + GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error) + EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error + GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) +} + +type Generator interface { + NewTx() TxBuilder + NewFee() ClientFee + NewSignature() ClientSignature + MarshalTx(tx types.Tx) ([]byte, error) +} + +type TxBuilder interface { + GetTx() sdk.Tx + + SetMsgs(...sdk.Msg) error + GetSignatures() []sdk.Signature + SetSignatures(...sdk.Signature) + GetFee() sdk.Fee + SetFee(sdk.Fee) + GetMemo() string + SetMemo(string) +} +``` + +We then update `Context` to have new fields: `JSONMarshaler`, `TxGenerator`, +and `AccountRetriever`, and we update `AppModuleBasic.GetTxCmd` to take +a `Context` which should have all of these fields pre-populated. + +Each client method should then use one of the `Init` methods to re-initialize +the pre-populated `Context`. `tx.GenerateOrBroadcastTx` can be used to +generate or broadcast a transaction. For example: + +```go +import "github.com/spf13/cobra" +import "github.com/cosmos/cosmos-sdk/client" +import "github.com/cosmos/cosmos-sdk/client/tx" + +func NewCmdDoSomething(clientCtx client.Context) *cobra.Command { + return &cobra.Command{ + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := ctx.InitWithInput(cmd.InOrStdin()) + msg := NewSomeMsg{...} + tx.GenerateOrBroadcastTx(clientCtx, msg) + }, + } +} +``` + +## Future Improvements + +### `SIGN_MODE_TEXTUAL` specification + +A concrete specification and implementation of `SIGN_MODE_TEXTUAL` is intended +as a near-term future improvement so that the ledger app and other wallets +can gracefully transition away from Amino JSON. + +### `SIGN_MODE_DIRECT_AUX` + +(\*Documented as option (3) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933) + +We could add a mode `SIGN_MODE_DIRECT_AUX` +to support scenarios where multiple signatures +are being gathered into a single transaction but the message composer does not +yet know which signatures will be included in the final transaction. For instance, +I may have a 3/5 multisig wallet and want to send a `TxBody` to all 5 +signers to see who signs first. As soon as I have 3 signatures then I will go +ahead and build the full transaction. + +With `SIGN_MODE_DIRECT`, each signer needs +to sign the full `AuthInfo` which includes the full list of all signers and +their signing modes, making the above scenario very hard. + +`SIGN_MODE_DIRECT_AUX` would allow "auxiliary" signers to create their signature +using only `TxBody` and their own `PublicKey`. This allows the full list of +signers in `AuthInfo` to be delayed until signatures have been collected. + +An "auxiliary" signer is any signer besides the primary signer who is paying +the fee. For the primary signer, the full `AuthInfo` is actually needed to calculate gas and fees +because that is dependent on how many signers and which key types and signing +modes they are using. Auxiliary signers, however, do not need to worry about +fees or gas and thus can just sign `TxBody`. + +To generate a signature in `SIGN_MODE_DIRECT_AUX` these steps would be followed: + +1. Encode `SignDocAux` (with the same requirement that fields must be serialized + in order): + +```proto +// types/types.proto +message SignDocAux { + bytes body_bytes = 1; + // PublicKey is included in SignDocAux : + // 1. as a special case for multisig public keys. For multisig public keys, + // the signer should use the top-level multisig public key they are signing + // against, not their own public key. This is to prevent against a form + // of malleability where a signature could be taken out of context of the + // multisig key that was intended to be signed for + // 2. to guard against scenario where configuration information is encoded + // in public keys (it has been proposed) such that two keys can generate + // the same signature but have different security properties + // + // By including it here, the composer of AuthInfo cannot reference the + // a public key variant the signer did not intend to use + PublicKey public_key = 2; + string chain_id = 3; + uint64 account_number = 4; +} +``` + +2. Sign the encoded `SignDocAux` bytes +3. Send their signature and `SignerInfo` to primary signer who will then + sign and broadcast the final transaction (with `SIGN_MODE_DIRECT` and `AuthInfo` + added) once enough signatures have been collected + +### `SIGN_MODE_DIRECT_RELAXED` + +(_Documented as option (1)(a) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933_) + +This is a variation of `SIGN_MODE_DIRECT` where multiple signers wouldn't need to +coordinate public keys and signing modes in advance. It would involve an alternate +`SignDoc` similar to `SignDocAux` above with fee. This could be added in the future +if client developers found the burden of collecting public keys and modes in advance +too burdensome. + +## Consequences + +### Positive + +- Significant performance gains. +- Supports backward and forward type compatibility. +- Better support for cross-language clients. +- Multiple signing modes allow for greater protocol evolution + +### Negative + +- `google.protobuf.Any` type URLs increase transaction size although the effect + may be negligible or compression may be able to mitigate it. + +### Neutral + +## References diff --git a/docs/architecture/adr-021-protobuf-query-encoding.md b/docs/architecture/adr-021-protobuf-query-encoding.md new file mode 100644 index 000000000000..60d4d2b2f6ad --- /dev/null +++ b/docs/architecture/adr-021-protobuf-query-encoding.md @@ -0,0 +1,258 @@ +# ADR 021: Protocol Buffer Query Encoding + +## Changelog + +- 2020 March 27: Initial Draft + +## Status + +Accepted + +## Context + +This ADR is a continuation of the motivation, design, and context established in +[ADR 019](./adr-019-protobuf-state-encoding.md) and +[ARD 020](./adr-019-protobuf-transaction-encoding.md), namely, we aim to design the +Protocol Buffer migration path for the client-side of the Cosmos SDK. + +This ADR continues from [ARD 020](./adr-020-protobuf-transaction-encoding.md) +to specify the encoding of queries. + +## Decision + +### Custom Query Definition + +Modules define custom queries through a protocol buffers `service` definition. +These `service` definitions are generally associated with and used by the +GRPC protocol. However, the protocol buffers specification indicates that +they can be used more generically by any request/response protocol that uses +protocol buffer encoding. Thus, we can use `service` definitions for specifying +custom ABCI queries and even reuse a substantial amount of the GRPC infrastructure. + +Each module with custom queries should define a service canonically named `Query`: + +```proto +// x/bank/types/types.proto + +service Query { + rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { } + rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { } +} +``` + +#### Handling of Interface Types + +Modules that use interface types and need true polymorphism generally force a +`oneof` up to the app-level that provides the set of concrete implementations of +that interface that the app supports. While app's are welcome to do the same for +queries and implement an app-level query service, it is recommended that modules +provide query methods that expose these interfaces via `google.protobuf.Any`. +There is a concern on the transaction level that the overhead of `Any` is too +high to justify its usage. However for queries this is not a concern, and +providing generic module-level queries that use `Any` does not preclude apps +from also providing app-level queries that return use the app-level `oneof`s. + + +A hypothetical example for the `gov` module would look something like: + +```proto +// x/gov/types/types.proto + +import "google/protobuf/any.proto"; + +service Query { + rpc GetProposal(GetProposalParams) returns (AnyProposal) { } +} + +message AnyProposal { + ProposalBase base = 1; + google.protobuf.Any content = 2; +} +``` + +### Custom Query Implementation + +In order to implement the query service, we can reuse the existing [gogo protobuf](https://github.com/gogo/protobuf) +grpc plugin, which for a service named `Query` generates an interface named +`QueryServer` as below: + +```go +type QueryServer interface { + QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) + QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) +} +``` + +The custom queries for our module are implemented by implementing this interface. + +The first parameter in this generated interface is a generic `context.Context`, +whereas querier methods generally need an instance of `sdk.Context` to read +from the store. Since arbitrary values can be attached to `context.Context` +using the `WithValue` and `Value` methods, the SDK should provide a function +`sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided +`context.Context`. + +An example implementation of `QueryBalance` for the bank module as above would +look something like: + +```go +type Querier struct { + Keeper +} + +func (q Querier) QueryBalance(ctx context.Context, params *types.QueryBalanceParams) (*sdk.Coin, error) { + balance := q.GetBalance(sdk.UnwrapSDKContext(ctx), params.Address, params.Denom) + return &balance, nil +} +``` + +### Custom Query Registration and Routing + +Query server implementations as above would be registered with `AppModule`s using +a new method `RegisterQueryService(grpc.Server)` which could be implemented simply +as below: + +```go +// x/bank/module.go +func (am AppModule) RegisterQueryService(server grpc.Server) { + types.RegisterQueryServer(server, keeper.Querier{am.keeper}) +} +``` + +Underneath the hood, a new method `RegisterService(sd *grpc.ServiceDesc, handler interface{})` +will be added to the existing `baseapp.QueryRouter` to add the queries to the custom +query routing table (with the routing method being described below). +The signature for this method matches the existing +`RegisterServer` method on the GRPC `Server` type where `handler` is the custom +query server implementation described above. + + +GRPC-like requests are routed by the service name (ex. `cosmos_sdk.x.bank.v1.Query`) +and method name (ex. `QueryBalance`) combined with `/`s to form a full +method name (ex. `/cosmos_sdk.x.bank.v1.Query/QueryBalance`). This gets translated +into an ABCI query as `custom/cosmos_sdk.x.bank.v1.Query/QueryBalance`. Service handlers +registered with `QueryRouter.RegisterService` will be routed this way. + +Beyond the method name, GRPC requests carry a protobuf encoded payload, which maps naturally +to `RequestQuery.Data`, and receive a protobuf encoded response or error. Thus +there is a quite natural mapping of GRPC-like rpc methods to the existing +`sdk.Query` and `QueryRouter` infrastructure. + +This basic specification allows us to reuse protocol buffer `service` definitions +for ABCI custom queries substantially reducing the need for manual decoding and +encoding in query methods. + +### GRPC Protocol Support + +In addition to providing an ABCI query pathway, we can easily provide a GRPC +proxy server that routes requests in the GRPC protocol to ABCI query requests +under the hood. In this way, clients could use their host languages' existing +GRPC implementations to make direct queries against Cosmos SDK app's using +these `service` definitions. In order for this server to work, the `QueryRouter` +on `BaseApp` will need to expose the service handlers registered with +`QueryRouter.RegisterService` to the proxy server implementation. Nodes could +launch the proxy server on a separate port in the same process as the ABCI app +with a command-line flag. + +### REST Queries and Swagger Generation + +[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) is a project that +translates REST calls into GRPC calls using special annotations on service +methods. Modules that want to expose REST queries should add `google.api.http` +annotations to their `rpc` methods as in this example below. + +```proto +// x/bank/types/types.proto + +service Query { + rpc QueryBalance(QueryBalanceParams) returns (cosmos_sdk.v1.Coin) { + option (google.api.http) = { + get: "/x/bank/v1/balance/{address}/{denom}" + }; + } + rpc QueryAllBalances(QueryAllBalancesParams) returns (QueryAllBalancesResponse) { + option (google.api.http) = { + get: "/x/bank/v1/balances/{address}" + }; + } +} +``` + +grpc-gateway will work direcly against the GRPC proxy described above which will +translate requests to ABCI queries under the hood. grpc-gateway can also +generate Swagger definitions automatically. + +In the current implementation of REST queries, each module needs to implement +REST queries manually in addition to ABCI querier methods. Using the grpc-gateway +approach, there will be no need to generate separate REST query handlers, just +query servers as described above as grpc-gateway handles the translation of protobuf +to REST as well as Swagger definitions. + +The SDK should provide CLI commands for apps to start GRPC gateway either in +a separate process or the same process as the ABCI app, as well as provide a +command for generating grpc-gateway proxy `.proto` files and the `swagger.json` +file. + +### Client Usage + +The gogo protobuf grpc plugin generates client interfaces in addition to server +interfaces. For the `Query` service defined above we would get a `QueryClient` +interface like: + +```go +type QueryClient interface { + QueryBalance(ctx context.Context, in *QueryBalanceParams, opts ...grpc.CallOption) (*types.Coin, error) + QueryAllBalances(ctx context.Context, in *QueryAllBalancesParams, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) +} +``` + +Via a small patch to gogo protobuf ([gogo/protobuf#675](https://github.com/gogo/protobuf/pull/675)) +we have tweaked the grpc codegen to use an interface rather than concrete type +for the generated client struct. This allows us to also reuse the GRPC infrastructure +for ABCI client queries. + +1Context` will receive a new method `QueryConn` that returns a `ClientConn` +that routes calls to ABCI queries + +Clients (such as CLI methods) will then be able to call query methods like this: + +```go +clientCtx := client.NewContext() +queryClient := types.NewQueryClient(clientCtx.QueryConn()) +params := &types.QueryBalanceParams{addr, denom} +result, err := queryClient.QueryBalance(gocontext.Background(), params) +``` + +### Testing + +Tests would be able to create a query client directly from keeper and `sdk.Context` +references using a `QueryServerTestHelper` as below: + +```go +queryHelper := baseapp.NewQueryServerTestHelper(ctx) +types.RegisterQueryServer(queryHelper, keeper.Querier{app.BankKeeper}) +queryClient := types.NewQueryClient(queryHelper) +``` + +## Future Improvements + +## Consequences + +### Positive + +* greatly simplified querier implementation (no manual encoding/decoding) +* easy query client generation (can use existing grpc and swagger tools) +* no need for REST query implementations +* type safe query methods (generated via grpc plugin) +* going forward, there will be less breakage of query methods because of the +backwards compatibility guarantees provided by buf + +### Negative + +* all clients using the existing ABCI/REST queries will need to be refactored +for both the new GRPC/REST query paths as well as protobuf/proto-json encoded +data, but this is more or less unavoidable in the protobuf refactoring + +### Neutral + +## References diff --git a/docs/architecture/adr-022-custom-panic-handling.md b/docs/architecture/adr-022-custom-panic-handling.md new file mode 100644 index 000000000000..6b4544730505 --- /dev/null +++ b/docs/architecture/adr-022-custom-panic-handling.md @@ -0,0 +1,214 @@ +# ADR 022: Custom BaseApp panic handling + +## Changelog + +- 2020 Apr 24: Initial Draft + +## Context + +The current implementation of BaseApp does not allow developers to write custom error handlers during panic recovery +[runTx()](https://github.com/cosmos/cosmos-sdk/blob/bad4ca75f58b182f600396ca350ad844c18fc80b/baseapp/baseapp.go#L539) +method. We think that this method can be more flexible and can give SDK users more options for customizations without +the need to rewrite whole BaseApp. Also there's one special case for `sdk.ErrorOutOfGas` error handling, that case +might be handled in a "standard" way (middleware) alongside the others. + +We propose middleware-solution, which could help developers implement the following cases: +* add external logging (let's say sending reports to external services like [Sentry](https://sentry.io)); +* call panic for specific error cases; + +It will also make `OutOfGas` case and `default` case one of the middlewares. +`Default` case wraps recovery object to an error and logs it ([example middleware implementation](#Recovery-middleware)). + +Our project has a sidecar service running alongside the blockchain node (smart contracts virtual machine). It is +essential that node <-> sidecar connectivity stays stable for TXs processing. So when the communication breaks we need +to crash the node and reboot it once the problem is solved. That behaviour makes node's state machine execution +deterministic. As all keeper panics are caught by runTx's `defer()` handler, we have to adjust the BaseApp code +in order to customize it. + +## Decision + +### Design + +#### Overview + +Instead of hardcoding custom error handling into BaseApp we suggest using set of middlewares which can be customized +externally and will allow developers use as many custom error handlers as they want. Implementation with tests +can be found [here](https://github.com/cosmos/cosmos-sdk/pull/6053). + +#### Implementation details + +##### Recovery handler + +New `RecoveryHandler` type added. `recoveryObj` input argument is an object returned by the standard Go function +`recover()` from the `builtin` package. + +```go +type RecoveryHandler func(recoveryObj interface{}) error +``` + +Handler should type assert (or other methods) an object to define if object should be handled. +`nil` should be returned if input object can't be handled by that `RecoveryHandler` (not a handler's target type). +Not `nil` error should be returned if input object was handled and middleware chain execution should be stopped. + +An example: + +```go +func exampleErrHandler(recoveryObj interface{}) error { + err, ok := recoveryObj.(error) + if !ok { return nil } + + if someSpecificError.Is(err) { + panic(customPanicMsg) + } else { + return nil + } +} +``` + +This example breaks the application execution, but it also might enrich the error's context like the `OutOfGas` handler. + +##### Recovery middleware + +We also add a middleware type (decorator). That function type wraps `RecoveryHandler` and returns the next middleware in +execution chain and handler's `error`. Type is used to separate actual `recovery()` object handling from middleware +chain processing. + +```go +type recoveryMiddleware func(recoveryObj interface{}) (recoveryMiddleware, error) + +func newRecoveryMiddleware(handler RecoveryHandler, next recoveryMiddleware) recoveryMiddleware { + return func(recoveryObj interface{}) (recoveryMiddleware, error) { + if err := handler(recoveryObj); err != nil { + return nil, err + } + return next, nil + } +} +``` + +Function receives a `recoveryObj` object and returns: +* (next `recoveryMiddleware`, `nil`) if object wasn't handled (not a target type) by `RecoveryHandler`; +* (`nil`, not nil `error`) if input object was handled and other middlewares in the chain should not be executed; +* (`nil`, `nil`) in case of invalid behavior. Panic recovery might not have been properly handled; +this can be avoided by always using a `default` as a rightmost middleware in the chain (always returns an `error`'); + + +`OutOfGas` middleware example: +```go +func newOutOfGasRecoveryMiddleware(gasWanted uint64, ctx sdk.Context, next recoveryMiddleware) recoveryMiddleware { + handler := func(recoveryObj interface{}) error { + err, ok := recoveryObj.(sdk.ErrorOutOfGas) + if !ok { return nil } + + return sdkerrors.Wrap( + sdkerrors.ErrOutOfGas, fmt.Sprintf( + "out of gas in location: %v; gasWanted: %d, gasUsed: %d", err.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), + ), + ) + } + + return newRecoveryMiddleware(handler, next) +} +``` + +`Default` middleware example: +```go +func newDefaultRecoveryMiddleware() recoveryMiddleware { + handler := func(recoveryObj interface{}) error { + return sdkerrors.Wrap( + sdkerrors.ErrPanic, fmt.Sprintf("recovered: %v\nstack:\n%v", recoveryObj, string(debug.Stack())), + ) + } + + return newRecoveryMiddleware(handler, nil) +} +``` + +##### Recovery processing + +Basic chain of middlewares processing would look like: + +```go +func processRecovery(recoveryObj interface{}, middleware recoveryMiddleware) error { + if middleware == nil { return nil } + + next, err := middleware(recoveryObj) + if err != nil { return err } + if next == nil { return nil } + + return processRecovery(recoveryObj, next) +} +``` + +That way we can create a middleware chain which is executed from left to right, the rightmost middleware is a +`default` handler which must return an `error`. + +##### BaseApp changes + +The `default` middleware chain must exist in a `BaseApp` object. `Baseapp` modifications: + +```go +type BaseApp struct { + // ... + runTxRecoveryMiddleware recoveryMiddleware +} + +func NewBaseApp(...) { + // ... + app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware() +} + +func (app *BaseApp) runTx(...) { + // ... + defer func() { + if r := recover(); r != nil { + recoveryMW := newOutOfGasRecoveryMiddleware(gasWanted, ctx, app.runTxRecoveryMiddleware) + err, result = processRecovery(r, recoveryMW), nil + } + + gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed()} + }() + // ... +} +``` + +Developers can add their custom `RecoveryHandler`s by providing `AddRunTxRecoveryHandler` as a BaseApp option parameter to the `NewBaseapp` constructor: + +```go +func (app *BaseApp) AddRunTxRecoveryHandler(handlers ...RecoveryHandler) { + for _, h := range handlers { + app.runTxRecoveryMiddleware = newRecoveryMiddleware(h, app.runTxRecoveryMiddleware) + } +} +``` + +This method would prepend handlers to an existing chain. + +## Status + +Proposed + +## Consequences + +### Positive + +- Developers of Cosmos SDK based projects can add custom panic handlers to: + * add error context for custom panic sources (panic inside of custom keepers); + * emit `panic()`: passthrough recovery object to the Tendermint core; + * other necessary handling; +- Developers can use standard Cosmos SDK `BaseApp` implementation, rather that rewriting it in their projects; +- Proposed solution doesn't break the current "standard" `runTx()` flow; + +### Negative + +- Introduces changes to the execution model design. + +### Neutral + +- `OutOfGas` error handler becomes one of the middlewares; +- Default panic handler becomes one of the middlewares; + +## References + +- [PR-6053 with proposed solution](https://github.com/cosmos/cosmos-sdk/pull/6053) +- [Similar solution. ADR-010 Modular AnteHandler](https://github.com/cosmos/cosmos-sdk/blob/v0.38.3/docs/architecture/adr-010-modular-antehandler.md) diff --git a/docs/architecture/adr-023-protobuf-naming.md b/docs/architecture/adr-023-protobuf-naming.md new file mode 100644 index 000000000000..1322f097f589 --- /dev/null +++ b/docs/architecture/adr-023-protobuf-naming.md @@ -0,0 +1,258 @@ +# ADR 023: Protocol Buffer Naming and Versioning Conventions + +## Changelog + +- 2020 April 27: Initial Draft +- 2020 August 5: Update guidelines + +## Status + +Accepted + +## Context + +Protocol Buffers provide a basic [style guide](https://developers.google.com/protocol-buffers/docs/style) +and [Buf](https://buf.build/docs/style-guide) builds upon that. To the +extent possible, we want to follow industry accepted guidelines and wisdom for +the effective usage of protobuf, deviating from those only when there is clear +rationale for our use case. + +### Adoption of `Any` + +The adoption of `google.protobuf.Any` as the recommended approach for encoding +interface types (as opposed to `oneof`) makes package naming a central part +of the encoding as fully-qualified message names now appear in encoded +messages. + +### Current Directory Organization + +Thus far we have mostly followed [Buf's](https://buf.build) [DEFAULT](https://buf.build/docs/lint-checkers#default) +recommendations, with the minor deviation of disabling [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout) +which although being convenient for developing code comes with the warning +from Buf that: + +> you will have a very bad time with many Protobuf plugins across various languages if you do not do this + +### Adoption of gRPC Queries + +In [ADR 021](adr-021-protobuf-query-encoding.md), gRPC was adopted for Protobuf +native queries. The full gRPC service path thus becomes a key part of ABCI query +path. In the future, gRPC queries may be allowed from within persistent scripts +by technologies such as CosmWasm and these query routes would be stored within +script binaries. + +## Decision + +The goal of this ADR is to provide thoughtful naming conventions that: + +* encourage a good user experience for when users interact directly with +.proto files and fully-qualified protobuf names +* balance conciseness against the possibility of either over-optimizing (making +names too short and cryptic) or under-optimizing (just accepting bloated names +with lots of redundant information) + +These guidelines are meant to act as a style guide for both the SDK and +third-party modules. + +As a starting point, we should adopt all of the [DEFAULT](https://buf.build/docs/lint-checkers#default) +checkers in [Buf's](https://buf.build) including [`PACKAGE_DIRECTORY_MATCH`](https://buf.build/docs/lint-checkers#file_layout), +except: +* [PACKAGE_VERSION_SUFFIX](https://buf.build/docs/lint-checkers#package_version_suffix) +* [SERVICE_SUFFIX](https://buf.build/docs/lint-checkers#service_suffix) + +Further guidelines to be described below. + +### Principles + +#### Concise and Descriptive Names + +Names should be descriptive enough to convey their meaning and distinguish +them from other names. + +Given that we are using fully-qualifed names within +`google.protobuf.Any` as well as within gRPC query routes, we should aim to +keep names concise, without going overboard. The general rule of thumb should +be if a shorter name would convey more or else the same thing, pick the shorter +name. + +For instance, `cosmos.bank.MsgSend` (19 bytes) conveys roughly the same information +as `cosmos_sdk.x.bank.v1.MsgSend` (28 bytes) but is more concise. + +Such conciseness makes names both more pleasant to work with and take up less +space within transactions and on the wire. + +We should also resist the temptation to over-optimize, by making names +cryptically short with abbreviations. For instance, we shouldn't try to +reduce `cosmos.bank.MsgSend` to `csm.bk.MSnd` just to save a few bytes. + +The goal is to make names **_concise but not cryptic_**. + +#### Names are for Clients First + +Package and type names should be chosen for the benefit of users, not +necessarily because of legacy concerns related to the go code-base. + +#### Plan for Longevity + +In the interests of long-term support, we should plan on the names we do +choose to be in usage for a long time, so now is the opportunity to make +the best choices for the future. + +### Versioning + +#### Guidelines on Stable Package Versions + +In general, schema evolution is the way to update protobuf schemas. That means that new fields, +messages, and RPC methods are _added_ to existing schemas and old fields, messages and RPC methods +are maintained as long as possible. + +Breaking things is often unacceptable in a blockchain scenario. For instance, immutable smart contracts +may depend on certain data schemas on the host chain. If the host chain breaks those schemas, the smart +contract may be irreparably broken. Even when things can be fixed (for instance in client software), +this often comes at a high cost. + +Instead of breaking things, we should make every effort to evolve schemas rather than just breaking them. +[Buf](https://buf.build) breaking change detection should be used on all stable (non-alpha or beta) packages +to prevent such breakage. + +With that in mind, different stable versions (i.e. `v1` or `v2`) of a package should more or less be considered +different packages and this should be last resort approach for upgrading protobuf schemas. Scenarios where creating +a `v2` may make sense are: +* we want to create a new module with similar functionality to an existing module and adding `v2` is the most natural +way to do this. In that case, there are really just two different, but similar modules with different APIs. +* we want to add a new revamped API for an existing module and it's just too cumbersome to add it to the existing package, +so putting it in `v2` is cleaner for users. In this case, care should be made to not deprecate support for +`v1` if it is actively used in immutable smart contracts. + +#### Guidelines on unstable (alpha and beta) package versions + +The following guidelines are recommended for marking packages as alpha or beta: +* marking something as `alpha` or `beta` should be a last resort and just putting something in the +stable package (i.e. `v1` or `v2`) should be preferred +* a package *should* be marked as `alpha` *if and only if* there are active discussions to remove +or significantly alter the package in the near future +* a package *should* be marked as `beta` *if and only if* there is an active discussion to +significantly refactor/rework the functionality in the near future but not remove it +* modules *can and should* have types in both stable (i.e. `v1` or `v2`) and unstable (`alpha` or `beta`) packages. + +*`alpha` and `beta` should not be used to avoid responsibility for maintaining compatibility.* +Whenever code is released into the wild, especially on a blockchain, there is a high cost to changing things. In some +cases, for instance with immutable smart contracts, a breaking change may be impossible to fix. + +When marking something as `alpha` or `beta`, maintainers should ask the questions: +* what is the cost of asking others to change their code vs the benefit of us maintaining the optionality to change it? +* what is the plan for moving this to `v1` and how will that affect users? + +`alpha` or `beta` should really be used to communicate "changes are planned". + +As a case study, gRPC reflection is in the package `grpc.reflection.v1alpha`. It hasn't been changed since +2017 and it is now used in other widely used software like gRPCurl. Some folks probably use it in production services +and so if they actually went and changed the package to `grpc.reflection.v1`, some software would break and +they probably don't want to do that... So now the `v1alpha` package is more or less the de-facto `v1`. Let's not do that. + +The following are guidelines for working with non-stable packages: +* [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix) +(ex. `v1alpha1`) _should_ be used for non-stable packages +* non-stable packages should generally be excluded from breaking change detection +* immutable smart contract modules (i.e. CosmWasm) _should_ block smart contracts/persistent +scripts from interacting with `alpha`/`beta` packages + +#### Omit v1 suffix + +Instead of using [Buf's recommended version suffix](https://buf.build/docs/lint-checkers#package_version_suffix), +we can omit `v1` for packages that don't actually have a second version. This +allows for more concise names for common use cases like `cosmos.bank.Send`. +Packages that do have a second or third version can indicate that with `.v2` +or `.v3`. + +### Package Naming + +#### Adopt a short, unique top-level package name + +Top-level packages should adopt a short name that is known to not collide with +other names in common usage within the Cosmos ecosystem. In the near future, a +registry should be created to reserve and index top-level package names used +within the Cosmos ecosystem. Because the Cosmos SDK is intended to provide +the top-level types for the Cosmos project, the top-level package name `cosmos` +is recommended for usage within the Cosmos SDK instead of the longer `cosmos_sdk`. +[ICS](https://github.com/cosmos/ics) specifications could consider a +short top-level package like `ics23` based upon the standard number. + +#### Limit sub-package depth + +Sub-package depth should be increased with caution. Generally a single +sub-package is needed for a module or a library. Even though `x` or `modules` +is used in source code to denote modules, this is often unnecessary for .proto +files as modules are the primary thing sub-packages are used for. Only items which +are known to be used infrequently should have deep sub-package depths. + +For the Cosmos SDK, it is recommended that that we simply write `cosmos.bank`, +`cosmos.gov`, etc. rather than `cosmos.x.bank`. In practice, most non-module +types can go straight in the `cosmos` package or we can introduce a +`cosmos.base` package if needed. Note that this naming _will not_ change +go package names, i.e. the `cosmos.bank` protobuf package will still live in +`x/bank`. + +### Message Naming + +Message type names should be as concise possible without losing clarity. `sdk.Msg` +types which are used in transactions will retain the `Msg` prefix as that provides +helpful context. + +### Service and RPC Naming + +[ADR 021](adr-021-protobuf-query-encoding.md) specifies that modules should +implement a gRPC query service. We should consider the principle of conciseness +for query service and RPC names as these may be called from persistent script +modules such as CosmWasm. Also, users may use these query paths from tools like +[gRPCurl](https://github.com/fullstorydev/grpcurl). As an example, we can shorten +`/cosmos_sdk.x.bank.v1.QueryService/QueryBalance` to +`/cosmos.bank.Query/Balance` without losing much useful information. + +RPC request and response types _should_ follow the `ServiceNameMethodNameRequest`/ +`ServiceNameMethodNameResponse` naming convention. i.e. for an RPC method named `Balance` +on the `Query` service, the request and response types would be `QueryBalanceRequest` +and `QueryBalanceResponse`. This will be more self-explanatory than `BalanceRequest` +and `BalanceResponse`. + +#### Use just `Query` for the query service + +Instead of [Buf's default service suffix recommendation](https://github.com/cosmos/cosmos-sdk/pull/6033), +we should simply use the shorter `Query` for query services. + +For other types of gRPC services, we should consider sticking with Buf's +default recommendation. + +#### Omit `Get` and `Query` from query service RPC names + +`Get` and `Query` should be omitted from `Query` service names because they are +redundant in the fully-qualified name. For instance, `/cosmos.bank.Query/QueryBalance` +just says `Query` twice without any new information. + +## Future Improvements + +A registry of top-level package names should be created to coordinate naming +across the ecosystem, prevent collisions, and also help developers discover +useful schemas. A simple starting point would be a git repository with +community-based governance. + +## Consequences + +### Positive + +* names will be more concise and easier to read and type +* all transactions using `Any` will be at shorter (`_sdk.x` and `.v1` will be removed) +* `.proto` file imports will be more standard (without `"third_party/proto"` in +the path) +* code generation will be easier for clients because .proto files will be +in a single `proto/` directory which can be copied rather than scattered +throughout the SDK + +### Negative + +### Neutral + +* `.proto` files will need to be reorganized and refactored +* some modules may need to be marked as alpha or beta + +## References diff --git a/docs/architecture/adr-024-coin-metadata.md b/docs/architecture/adr-024-coin-metadata.md new file mode 100644 index 000000000000..5195a9c40008 --- /dev/null +++ b/docs/architecture/adr-024-coin-metadata.md @@ -0,0 +1,139 @@ +# ADR 024: Coin Metadata + +## Changelog + +- 05/19/2020: Initial draft + +## Status + +Proposed + +## Context + +Assets in the Cosmos SDK are represented via a `Coins` type that consists of an `amount` and a `denom`, +where the `amount` can be any arbitrarily large or small value. In addition, the Cosmos SDK uses an +account-based model where there are two types of primary accounts -- basic accounts and module accounts. +All account types have a set of balances that are composed of `Coins`. The `x/bank` module keeps +track of all balances for all accounts and also keeps track of the total supply of balances in an +application. + +With regards to a balance `amount`, the Cosmos SDK assumes a static and fixed unit of denomination, +regardless of the denomination itself. In other words, clients and apps built atop a Cosmos-SDK-based +chain may choose to define and use arbitrary units of denomination to provide a richer UX, however, by +the time a tx or operation reaches the Cosmos SDK state machine, the `amount` is treated as a single +unit. For example, for the Cosmos Hub (Gaia), clients assume 1 ATOM = 10^6 uatom, and so all txs and +operations in the Cosmos SDK work off of units of 10^6. + +This clearly provides a poor and limited UX especially as interoperability of networks increases and +as a result the total amount of asset types increases. We propose to have `x/bank` additionally keep +track of metadata per `denom` in order to help clients, wallet providers, and explorers improve their +UX and remove the requirement for making any assumptions on the unit of denomination. + +## Decision + +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 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`). + +There are two denominations that are of high importance for clients: the `base`, which is the smallest +possible unit and the `display`, which is the unit that is commonly referred to in human communication +and on exchanges. The values in those fields link to an entry in the list of denominations. + +The list in `denom_units` and the `display` entry may be changed via governance. + +As a result, we can define the type as follows: + +```protobuf +message DenomUnit { + string denom = 1; + uint32 exponent = 2; + repeated string aliases = 3; +} + +message Metadata { + string description = 1; + repeated DenomUnit denom_units = 2; + string base = 3; + string display = 4; +} +``` + +As an example, the ATOM's metadata can be defined as follows: + +```json +{ + "description": "The native staking token of the Cosmos Hub.", + "denom_units": [ + { + "denom": "uatom", + "exponent": 0, + "aliases": [ + "microatom" + ], + }, + { + "denom": "matom", + "exponent": 3, + "aliases": [ + "milliatom" + ] + }, + { + "denom": "atom", + "exponent": 6, + } + ], + "base": "uatom", + "display": "atom", +} +``` + +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. + The `display` denomination 4.3atom is a good default if the authors of the client don't make + an explicit decision to choose a different representation. + +A client should be able to query for metadata by denom both via the CLI and REST interfaces. In +addition, we will add handlers to these interfaces to convert from any unit to another given unit, +as the base framework for this already exists in the Cosmos SDK. + +Finally, we need to ensure metadata exists in the `GenesisState` of the `x/bank` module which is also +indexed by the base `denom`. + +```go +type GenesisState struct { + SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` + Balances []Balance `json:"balances" yaml:"balances"` + Supply sdk.Coins `json:"supply" yaml:"supply"` + DenomMetadata []Metadata `json:"denom_metadata" yaml:"denom_metadata"` +} +``` + +## Future Work + +In order for clients to avoid having to convert assets to the base denomination -- either manually or +via an endpoint, we may consider supporting automatic conversion of a given unit input. + +## Consequences + +### Positive + +- Provides clients, wallet providers and block explorers with additional data on + asset denomination to improve UX and remove any need to make assumptions on + denomination units. + +### Negative + +- A small amount of required additional storage in the `x/bank` module. The amount + of additional storage should be minimal as the amount of total assets should not + be large. + +### Neutral + +## References diff --git a/docs/architecture/adr-025-ibc-passive-channels.md b/docs/architecture/adr-025-ibc-passive-channels.md new file mode 100644 index 000000000000..4f12a042ad26 --- /dev/null +++ b/docs/architecture/adr-025-ibc-passive-channels.md @@ -0,0 +1,140 @@ +# ADR 025: IBC Passive Channels + +## Changelog + +- 2020-05-23: Provide sample Go code and more details +- 2020-05-18: Initial Draft + +## Status + +Proposed + +## Context + +The current "naive" IBC Relayer strategy currently establishes a single predetermined IBC channel atop a single connection between two clients (each potentially of a different chain). This strategy then detects packets to be relayed by watching for `send_packet` and `recv_packet` events matching that channel, and sends the necessary transactions to relay those packets. + +We wish to expand this "naive" strategy to a "passive" one which detects and relays both channel handshake messages and packets on a given connection, without the need to know each channel in advance of relaying it. + +In order to accomplish this, we propose adding more comprehensive events to expose channel metadata for each transaction sent from the `x/ibc/core/04-channel/keeper/handshake.go` and `x/ibc/core/04-channel/keeper/packet.go` modules. + +Here is an example of what would be in `ChanOpenInit`: + +```go +const ( + EventTypeChannelMeta = "channel_meta" + AttributeKeyAction = "action" + AttributeKeyHops = "hops" + AttributeKeyOrder = "order" + AttributeKeySrcPort = "src_port" + AttributeKeySrcChannel = "src_channel" + AttributeKeySrcVersion = "src_version" + AttributeKeyDstPort = "dst_port" + AttributeKeyDstChannel = "dst_channel" + AttributeKeyDstVersion = "dst_version" +) +// ... + // Emit Event with Channel metadata for the relayer to pick up and + // relay to the other chain + // This appears immediately before the successful return statement. + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelMeta, + sdk.NewAttribute(types.AttributeKeyAction, "open_init"), + sdk.NewAttribute(types.AttributeKeySrcConnection, connectionHops[0]), + sdk.NewAttribute(types.AttributeKeyHops, strings.Join(connectionHops, ",")), + sdk.NewAttribute(types.AttributeKeyOrder, order.String()), + sdk.NewAttribute(types.AttributeKeySrcPort, portID), + sdk.NewAttribute(types.AttributeKeySrcChannel, chanenlID), + sdk.NewAttribute(types.AttributeKeySrcVersion, version), + sdk.NewAttribute(types.AttributeKeyDstPort, counterparty.GetPortID()), + sdk.NewAttribute(types.AttributeKeyDstChannel, counterparty.GetChannelID()), + // The destination version is not yet known, but a value is necessary to pad + // the event attribute offsets + sdk.NewAttribute(types.AttributeKeyDstVersion, ""), + ), + }) +``` + +These metadata events capture all the "header" information needed to route IBC channel handshake transactions without requiring the client to query any data except that of the connection ID that it is willing to relay. It is intended that `channel_meta.src_connection` is the only event key that needs to be indexed for a passive relayer to function. + +### Handling Channel Open Attempts + +In the case of the passive relayer, when one chain sends a `ChanOpenInit`, the relayer should inform the other chain of this open attempt and allow that chain to decide how (and if) it continues the handshake. Once both chains have actively approved the channel opening, then the rest of the handshake can happen as it does with the current "naive" relayer. + +To implement this behavior, we propose replacing the `cbs.OnChanOpenTry` callback with a new `cbs.OnAttemptChanOpenTry` callback which explicitly handles the `MsgChannelOpenTry`, usually by resulting in a call to `keeper.ChanOpenTry`. The typical implementation, in `x/ibc-transfer/module.go` would be compatible with the current "naive" relayer, as follows: + +```go +func (am AppModule) OnAttemptChanOpenTry( + ctx sdk.Context, + chanKeeper channel.Keeper, + portCap *capability.Capability, + msg channel.MsgChannelOpenTry, +) (*sdk.Result, error) { + // Require portID is the portID transfer module is bound to + boundPort := am.keeper.GetPort(ctx) + if boundPort != msg.PortID { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", msg.PortID, boundPort) + } + + // BEGIN NEW CODE + // Assert our protocol version, overriding the relayer's suggestion. + msg.Version = types.Version + // Continue the ChanOpenTry. + res, chanCap, err := channel.HandleMsgChannelOpenTry(ctx, chanKeeper, portCap, msg) + if err != nil { + return nil, err + } + // END OF NEW CODE + + // ... the rest of the callback is similar to the existing OnChanOpenTry + // but uses msg.* directly. +``` + +Here is how this callback would be used, in the implementation of `x/ibc/handler.go`: + +```go +// ... + case channel.MsgChannelOpenTry: + // Lookup module by port capability + module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortID) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(port.ErrInvalidRoute, "route not found to module: %s", module) + } + // Delegate to the module's OnAttemptChanOpenTry. + return cbs.OnAttemptChanOpenTry(ctx, k.ChannelKeeper, portCap, msg) +``` + +The reason we do not have a more structured interaction between `x/ibc/handler.go` and the port's module (to explicitly negotiate versions, etc) is that we do not wish to constrain the app module to have to finish handling the `MsgChannelOpenTry` during this transaction or even this block. + +## Decision + +- Expose events to allow "passive" connection relayers. +- Enable application-initiated channels via such passive relayers. +- Allow port modules to control how to handle open-try messages. + +## Consequences + +### Positive + +Makes channels into a complete application-level abstraction. + +Applications have full control over initiating and accepting channels, rather than expecting a relayer to tell them when to do so. + +A passive relayer does not have to know what kind of channel (version string, ordering constraints, firewalling logic) the application supports. These are negotiated directly between applications. + +### Negative + +Increased event size for IBC messages. + +### Neutral + +More IBC events are exposed. + +## References + +- The Agoric VM's IBC handler currently [accomodates `attemptChanOpenTry`](https://github.com/Agoric/agoric-sdk/blob/904b3a0423222a1b32893453e44bbde598473960/packages/cosmic-swingset/lib/ag-solo/vats/ibc.js#L546) diff --git a/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md new file mode 100644 index 000000000000..dc34bbf33e9d --- /dev/null +++ b/docs/architecture/adr-026-ibc-client-recovery-mechanisms.md @@ -0,0 +1,75 @@ +# ADR 026: IBC Client Recovery Mechanisms + +## Changelog + +- 2020/06/23: Initial version +- 2020/08/06: Revisions per review & to reference version + +## Status + +*Proposed* + +## Context + +### Summary + +At launch, IBC will be a novel protocol, without an experienced user-base. At the protocol layer, it is not possible to distinguish between client expiry or misbehaviour due to genuine faults (Byzantine behavior) and client expiry or misbehaviour due to user mistakes (failing to update a client, or accidentally double-signing). In the base IBC protocol and ICS 20 fungible token transfer implementation, if a client can no longer be updated, funds in that channel will be permanently locked and can no longer be transferred. To the degree that it is safe to do so, it would be preferable to provide users with a recovery mechanism which can be utilised in these exceptional cases. + +### Exceptional cases + +The state of concern is where a client associated with connection(s) and channel(s) can no longer be updated. This can happen for several reasons: + +1. The chain which the client is following has halted and is no longer producing blocks/headers, so no updates can be made to the client +1. The chain which the client is following has continued to operate, but no relayer has submitted a new header within the unbonding period, and the client has expired + 1. This could be due to real misbehaviour (intentional Byzantine behaviour) or merely a mistake by validators, but the client cannot distinguish these two cases +1. The chain which the client is following has experienced a misbehaviour event, and the client has been frozen & thus can no longer be updated + +### Security model + +Two-thirds of the validator set (the quorum for governance, module participation) can already sign arbitrary data, so allowing governance to manually force-update a client with a new header after a delay period does not substantially alter the security model. + +## Decision + +We elect not to deal with chains which have actually halted, which is necessarily Byzantine behaviour and in which case token recovery is not likely possible anyways (in-flight packets cannot be timed-out, but the relative impact of that is minor). + +1. Require Tendermint light clients (ICS 07) to be created with the following additional flags + 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. 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. `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). + +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). + +## Consequences + +### Positive + +- 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 + +### 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 + +### Neutral + +No neutral consequences. + +## References + +- [Prior discussion](https://github.com/cosmos/ics/issues/421) +- [Epoch number discussion](https://github.com/cosmos/ics/issues/439) +- [Upgrade plan discussion](https://github.com/cosmos/ics/issues/445) diff --git a/docs/architecture/adr-027-deterministic-protobuf-serialization.md b/docs/architecture/adr-027-deterministic-protobuf-serialization.md new file mode 100644 index 000000000000..4de1cafb30c0 --- /dev/null +++ b/docs/architecture/adr-027-deterministic-protobuf-serialization.md @@ -0,0 +1,316 @@ +# ADR 027: Deterministic Protobuf Serialization + +## Changelog + +- 2020-08-07: Initial Draft +- 2020-09-01: Further clarify rules + +## Status + +Proposed + +## Abstract + +Fully deterministic structure serialization, which works across many languages and clients, +is needed when signing messages. We need to be sure that whenever we serialize +a data structure, no matter in which supported language, the raw bytes +will stay the same. +[Protobuf](https://developers.google.com/protocol-buffers/docs/proto3) +serialization is not bijective (i.e. there exist a practically unlimited number of +valid binary representations for a given protobuf document)1. + +This document describes a deterministic serialization scheme for +a subset of protobuf documents, that covers this use case but can be reused in +other cases as well. + +### Context + +For signature verification in Cosmos SDK, the signer and verifier need to agree on +the same serialization of a `SignDoc` as defined in +[ADR-020](./adr-020-protobuf-transaction-encoding.md) without transmitting the +serialization. + +Currently, for block signatures we are using a workaround: we create a new [TxRaw](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L30) +instance (as defined in [adr-020-protobuf-transaction-encoding](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md#transactions)) +by converting all [Tx](https://github.com/cosmos/cosmos-sdk/blob/9e85e81e0e8140067dd893421290c191529c148c/proto/cosmos/tx/v1beta1/tx.proto#L13) +fields to bytes on the client side. This adds an additional manual +step when sending and signing transactions. + +### Decision + +The following encoding scheme is to be used by other ADRs, +and in particular for `SignDoc` serialization. + +## Specification + +### Scope + +This ADR defines a protobuf3 serializer. The output is a valid protobuf +serialization, such that every protobuf parser can parse it. + +No maps are supported in version 1 due to the complexity of defining a +deterministic serialization. This might change in future. Implementations must +reject documents containing maps as invalid input. + +### Background - Protobuf3 Encoding + +Most numeric types in protobuf3 are encoded as +[varints](https://developers.google.com/protocol-buffers/docs/encoding#varints). +Varints are at most 10 bytes, and since each varint byte has 7 bits of data, +varints are a representation of `uint70` (70-bit unsigned integer). When +encoding, numeric values are casted from their base type to `uint70`, and when +decoding, the parsed `uint70` is casted to the appropriate numeric type. + +The maximum valid value for a varint that complies with protobuf3 is +`FF FF FF FF FF FF FF FF FF 7F` (i.e. `2**70 -1`). If the field type is +`{,u,s}int64`, the highest 6 bits of the 70 are dropped during decoding, +introducing 6 bits of malleability. If the field type is `{,u,s}int32`, the +highest 38 bits of the 70 are dropped during decoding, introducing 38 bits of +malleability. + +Among other sources of non-determinism, this ADR eliminates the possibility of +encoding malleability. + + +### Serialization rules + +The serialization is based on the +[protobuf3 encoding](https://developers.google.com/protocol-buffers/docs/encoding) +with the following additions: + +1. Fields must be serialized only once in ascending order +2. Extra fields or any extra data must not be added +3. [Default values](https://developers.google.com/protocol-buffers/docs/proto3#default) + must be omitted +4. `repeated` fields of scalar numeric types must use + [packed encoding](https://developers.google.com/protocol-buffers/docs/encoding#packed) +5. Varint encoding must not be longer than needed: + * No trailing zero bytes (in little endian, i.e. no leading zeroes in big + endian). Per rule 3 above, the default value of `0` must be omitted, so + this rule does not apply in such cases. + * The maximum value for a varint must be `FF FF FF FF FF FF FF FF FF 01`. + In other words, when decoded, the highest 6 bits of the 70-bit unsigned + integer must be `0`. (10-byte varints are 10 groups of 7 bits, i.e. + 70 bits, of which only the lowest 70-6=64 are useful.) + * The maximum value for 32-bit values in varint encoding must be `FF FF FF FF 0F` + with one exception (below). In other words, when decoded, the highest 38 + bits of the 70-bit unsigned integer must be `0`. + * The one exception to the above is _negative_ `int32`, which must be + encoded using the full 10 bytes for sign extension2. + * The maximum value for Boolean values in varint encoding must be `01` (i.e. + it must be `0` or `1`). Per rule 3 above, the default value of `0` must + be omitted, so if a Boolean is included it must have a value of `1`. + +While rule number 1. and 2. should be pretty straight forward and describe the +default behavior of all protobuf encoders the author is aware of, the 3rd rule +is more interesting. After a protobuf3 deserialization you cannot differentiate +between unset fields and fields set to the default value3. At +serialization level however, it is possible to set the fields with an empty +value or omitting them entirely. This is a significant difference to e.g. JSON +where a property can be empty (`""`, `0`), `null` or undefined, leading to 3 +different documents. + +Omitting fields set to default values is valid because the parser must assign +the default value to fields missing in the serialization4. For scalar +types, omitting defaults is required by the spec5. For `repeated` +fields, not serializing them is the only way to express empty lists. Enums must +have a first element of numeric value 0, which is the default6. And +message fields default to unset7. + +Omitting defaults allows for some amount of forward compatibility: users of +newer versions of a protobuf schema produce the same serialization as users of +older versions as long as newly added fields are not used (i.e. set to their +default value). + +### Implementation + +There are three main implementation strategies, ordered from the least to the +most custom development: + +- **Use a protobuf serializer that follows the above rules by default.** E.g. + [gogoproto](https://pkg.go.dev/github.com/gogo/protobuf/gogoproto) is known to + be compliant by in most cases, but not when certain annotations such as + `nullable = false` are used. It might also be an option to configure an + existing serializer accordingly. +- **Normalize default values before encoding them.** If your serializer follows + rule 1. and 2. and allows you to explicitly unset fields for serialization, + you can normalize default values to unset. This can be done when working with + [protobuf.js](https://www.npmjs.com/package/protobufjs): + + ```js + const bytes = SignDoc.encode({ + bodyBytes: body.length > 0 ? body : null, // normalize empty bytes to unset + authInfoBytes: authInfo.length > 0 ? authInfo : null, // normalize empty bytes to unset + chainId: chainId || null, // normalize "" to unset + accountNumber: accountNumber || null, // normalize 0 to unset + accountSequence: accountSequence || null, // normalize 0 to unset + }).finish(); + ``` + +- **Use a hand-written serializer for the types you need.** If none of the above + ways works for you, you can write a serializer yourself. For SignDoc this + would look something like this in Go, building on existing protobuf utilities: + + ```go + if !signDoc.body_bytes.empty() { + buf.WriteUVarInt64(0xA) // wire type and field number for body_bytes + buf.WriteUVarInt64(signDoc.body_bytes.length()) + buf.WriteBytes(signDoc.body_bytes) + } + + if !signDoc.auth_info.empty() { + buf.WriteUVarInt64(0x12) // wire type and field number for auth_info + buf.WriteUVarInt64(signDoc.auth_info.length()) + buf.WriteBytes(signDoc.auth_info) + } + + if !signDoc.chain_id.empty() { + buf.WriteUVarInt64(0x1a) // wire type and field number for chain_id + buf.WriteUVarInt64(signDoc.chain_id.length()) + buf.WriteBytes(signDoc.chain_id) + } + + if signDoc.account_number != 0 { + buf.WriteUVarInt64(0x20) // wire type and field number for account_number + buf.WriteUVarInt(signDoc.account_number) + } + + if signDoc.account_sequence != 0 { + buf.WriteUVarInt64(0x28) // wire type and field number for account_sequence + buf.WriteUVarInt(signDoc.account_sequence) + } + ``` + +### Test vectors + +Given the protobuf definition `Article.proto` + +```protobuf +package blog; +syntax = "proto3"; + +enum Type { + UNSPECIFIED = 0; + IMAGES = 1; + NEWS = 2; +}; + +enum Review { + UNSPECIFIED = 0; + ACCEPTED = 1; + REJECTED = 2; +}; + +message Article { + string title = 1; + string description = 2; + uint64 created = 3; + uint64 updated = 4; + bool public = 5; + bool promoted = 6; + Type type = 7; + Review review = 8; + repeated string comments = 9; + repeated string backlinks = 10; +}; +``` + +serializing the values + +```yaml +title: "The world needs change 🌳" +description: "" +created: 1596806111080 +updated: 0 +public: true +promoted: false +type: Type.NEWS +review: Review.UNSPECIFIED +comments: ["Nice one", "Thank you"] +backlinks: [] +``` + +must result in the serialization + +``` +0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 +``` + +When inspecting the serialized document, you see that every second field is +omitted: + +``` +$ echo 0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75 | xxd -r -p | protoc --decode_raw +1: "The world needs change \360\237\214\263" +3: 1596806111080 +5: 1 +7: 2 +9: "Nice one" +9: "Thank you" +``` + +## Consequences + +Having such an encoding available allows us to get deterministic serialization +for all protobuf documents we need in the context of Cosmos SDK signing. + +### Positive + +- Well defined rules that can be verified independent of a reference + implementation +- Simple enough to keep the barrier to implement transaction signing low +- It allows us to continue to use 0 and other empty values in SignDoc, avoiding + the need to work around 0 sequences. This does not imply the change from + https://github.com/cosmos/cosmos-sdk/pull/6949 should not be merged, but not + too important anymore. + +### Negative + +- When implementing transaction signing, the encoding rules above must be + understood and implemented. +- The need for rule number 3. adds some complexity to implementations. +- Some data structures may require custom code for serialization. Thus + the code is not very portable - it will require additional work for each + client implementing serialization to properly handle custom data structures. + +### Neutral + + +### Usage in SDK + +For the reasons mentioned above ("Negative" section) we prefer to keep workarounds +for shared data structure. Example: the aforementioned `TxRaw` is using raw bytes +as a workaround. This allows them to use any valid Protobuf library without +the need of implementing a custom serializer that adheres to this standard (and related risks of bugs). + +## References + +- 1 _When a message is serialized, there is no guaranteed order for + how its known or unknown fields should be written. Serialization order is an + implementation detail and the details of any particular implementation may + change in the future. Therefore, protocol buffer parsers must be able to parse + fields in any order._ from + https://developers.google.com/protocol-buffers/docs/encoding#order +- 2 https://developers.google.com/protocol-buffers/docs/encoding#signed_integers +- 3 _Note that for scalar message fields, once a message is parsed + there's no way of telling whether a field was explicitly set to the default + value (for example whether a boolean was set to false) or just not set at all: + you should bear this in mind when defining your message types. For example, + don't have a boolean that switches on some behavior when set to false if you + don't want that behavior to also happen by default._ from + https://developers.google.com/protocol-buffers/docs/proto3#default +- 4 _When a message is parsed, if the encoded message does not + contain a particular singular element, the corresponding field in the parsed + object is set to the default value for that field._ from + https://developers.google.com/protocol-buffers/docs/proto3#default +- 5 _Also note that if a scalar message field is set to its default, + the value will not be serialized on the wire._ from + https://developers.google.com/protocol-buffers/docs/proto3#default +- 6 _For enums, the default value is the first defined enum value, + which must be 0._ from + https://developers.google.com/protocol-buffers/docs/proto3#default +- 7 _For message fields, the field is not set. Its exact value is + language-dependent._ from + https://developers.google.com/protocol-buffers/docs/proto3#default +- Encoding rules and parts of the reasoning taken from + [canonical-proto3 Aaron Craelius](https://github.com/regen-network/canonical-proto3) diff --git a/docs/architecture/adr-028-public-key-addresses.md b/docs/architecture/adr-028-public-key-addresses.md new file mode 100644 index 000000000000..00c6dddc0bdb --- /dev/null +++ b/docs/architecture/adr-028-public-key-addresses.md @@ -0,0 +1,168 @@ +# ADR 028: Public Key Addresses + +## Changelog + +- 2020/08/18: Initial version + +## Status + +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. + +## 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. + +@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. + +> 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]`. + +## Decision + +### 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: + +- secp256k1: `ripemd160(sha256(pk_bytes))[:20]` +- legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]` + +We don't want to change existing addresses. So the addresses for these two key types will remain the same. + +The current multisig public keys use amino serialization to generate the address. We will retain +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. + + +### Canonical Address Format + +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 + +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): + +```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] +} +``` + +`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). + +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. + +### Canonical Public Key Address Prefixes + +All public key types will have a unique protobuf message type such as: + +```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. 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)`. + +### 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. + +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; +} +``` + +We define the following `Address()` function for this public key: + +``` +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()) + } + + // then sort them in ascending order + addresses = Sort(addresses) + + // then concatenate them together + var joinedAddresses []byte + for addr := range addresses { + joinedAddresses := append(joinedAddresses, addr...) + } + + // 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) + + // use the standard AddressHash function + return AddressHash(prefix, joinedAddresses) +} +``` + +## Consequences + +### Positive +- a simple algorithm for generating addresses for new public keys and module accounts + +### Negative +- addresses do not communicate key type, a prefixed approach would have done this + +### Neutral +- protobuf message names are used as key type prefixes + +## References diff --git a/docs/architecture/adr-029-fee-grant-module.md b/docs/architecture/adr-029-fee-grant-module.md new file mode 100644 index 000000000000..8ef0f722fab9 --- /dev/null +++ b/docs/architecture/adr-029-fee-grant-module.md @@ -0,0 +1,162 @@ +# ADR 029: Fee Grant Module + +## Changelog + +- 2020/08/18: Initial Draft + +## Status + +Accepted + +## Context + +In order to make blockchain transactions, the signing account must possess a sufficient balance of the right denomination +in order to pay fees. There are classes of transactions where needing to maintain a wallet with sufficient fees is a +barrier to adoption. + +For instance, when proper permissions are setup, someone may temporarily delegate the ability to vote on proposals to +a "burner" account that is stored on a mobile phone with only minimal security. + +Other use cases include workers tracking items in a supply chain or farmers submitting field data for analytics +or compliance purposes. + +For all of these use cases, UX would be significantly enhanced by obviating the need for these accounts to always +maintain the appropriate fee balance. This is especially true if we wanted to achieve enterprise adoption for something +like supply chain tracking. + +While one solution would be to have a service that fills up these accounts automatically with the appropriate fees, a better UX +would be provided by allowing these accounts to pull from a common fee pool account with proper spending limits. +A single pool would reduce the churn of making lots of small "fill up" transactions and also more effectively leverages +the resources of the organization setting up the pool. + +## Decision + +As a solution we propose a module, `x/feegrant` which allows one account, the "granter" to grant another account, the "grantee" +an allowance to spend the granter's account balance for fees within certain well-defined limits. + +Fee allowances are defined by the extensible `FeeAllowanceI` interface: + +```go +type FeeAllowanceI { + // 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) +} +``` + +Two basic fee allowance types, `BasicFeeAllowance` and `PeriodicFeeAllowance` are defined to support known use cases: + +```proto +// 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 { + // 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_sdk.v1.Coin spend_limit = 1; + + // expires_at specifies an optional time when this allowance expires + ExpiresAt expiration = 2; +} + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +message PeriodicFeeAllowance { + BasicFeeAllowance basic = 1; + + // period specifies the time duration in which period_spend_limit coins can + // be spent before that allowance is reset + Duration period = 2; + + // period_spend_limit specifies the maximum number of coins that can be spent + // in the period + repeated cosmos_sdk.v1.Coin period_spend_limit = 3; + + // period_can_spend is the number of coins left to be spent before the period_reset time + repeated cosmos_sdk.v1.Coin period_can_spend = 4; + + // 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; +} + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +message ExpiresAt { + oneof sum { + google.protobuf.Timestamp time = 1; + uint64 height = 2; + } + } + +// Duration is a repeating unit of either clock time or number of blocks. +message Duration { + oneof sum { + google.protobuf.Duration duration = 1; + uint64 blocks = 2; + } +} + +``` + +Allowances can be granted and revoked using `MsgGrantFeeAllowance` and `MsgRevokeFeeAllowance`: + +```proto +message MsgGrantFeeAllowance { + string granter = 1; + string grantee = 2; + google.protobuf.Any allowance = 3; + } + + // MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. + message MsgRevokeFeeAllowance { + string granter = 1; + string grantee = 2; + } +``` + +In order to use allowances in transactions, we add a new field `granter` to the transaction `Fee` type: +```proto +package cosmos.tx.v1beta1; + +message Fee { + repeated cosmos.base.v1beta1.Coin amount = 1; + uint64 gas_limit = 2; + string payer = 3; + string granter = 4; +} +``` + +`granter` must either be left empty or must correspond to an account which has granted +a fee allowance to fee payer (either the first signer or the value of the `payer` field). + +A new `AnteDecorator` named `DeductGrantedFeeDecorator` will be created in order to process transactions with `fee_payer` +set and correctly deduct fees based on fee allowances. + +## Consequences + +### Positive + +- improved UX for use cases where it is cumbersome to maintain an account balance just for fees + +### Negative + +### Neutral + +- a new field must be added to the transaction `Fee` message and a new `AnteDecorator` must be +created to use it + +## References + +- Blog article describing initial work: https://medium.com/regen-network/hacking-the-cosmos-cosmwasm-and-key-management-a08b9f561d1b +- Initial public specification: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56 +- Original subkeys proposal from B-harvest which influenced this design: https://github.com/cosmos/cosmos-sdk/issues/4480 diff --git a/docs/architecture/adr-031-msg-service.md b/docs/architecture/adr-031-msg-service.md new file mode 100644 index 000000000000..a36d86c4312f --- /dev/null +++ b/docs/architecture/adr-031-msg-service.md @@ -0,0 +1,246 @@ +# ADR 031: Protobuf Msg Services + +## Changelog + +- 2020-10-05: Initial Draft + +## Status + +Accepted + +## Abstract + +We want to leverage protobuf `service` definitions for defining `Msg`s which will give us significant developer UX +improvements in terms of the code that is generated and the fact that return types will now be well defined. + +## Context + +Currently `Msg` handlers in the Cosmos SDK do have return values that are placed in the `data` field of the response. +These return values, however, are not specified anywhere except in the golang handler code. + +In early conversations [it was proposed](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc/edit) +that `Msg` return types be captured using a protobuf extension field, ex: + +```protobuf +package cosmos.gov; + +message MsgSubmitProposal + option (cosmos_proto.msg_return) = “uint64”; + string delegator_address = 1; + string validator_address = 2; + repeated sdk.Coin amount = 3; +} +``` + +This was never adopted, however. + +Having a well-specified return value for `Msg`s would improve client UX. For instance, +in `x/gov`, `MsgSubmitProposal` returns the proposal ID as a big-endian `uint64`. +This isn’t really documented anywhere and clients would need to know the internals +of the SDK to parse that value and return it to users. + +Also, there may be cases where we want to use these return values programatically. +For instance, https://github.com/cosmos/cosmos-sdk/issues/7093 proposes a method for +doing inter-module Ocaps using the `Msg` router. A well-defined return type would +improve the developer UX for this approach. + +In addition, handler registration of `Msg` types tends to add a bit of +boilerplate on top of keepers and is usually done through manual type switches. +This isn't necessarily bad, but it does add overhead to creating modules. + +## Decision + +We decide to use protobuf `service` definitions for defining `Msg`s as well as +the code generated by them as a replacement for `Msg` handlers. + +Below we define how this will look for the `SubmitProposal` message from `x/gov` module. +We start with a `Msg` `service` definition: + +```proto +package cosmos.gov; + +service Msg { + rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); +} + +// Note that for backwards compatibility this uses MsgSubmitProposal as the request +// type instead of the more canonical MsgSubmitProposalRequest +message MsgSubmitProposal { + google.protobuf.Any content = 1; + string proposer = 2; +} + +message MsgSubmitProposalResponse { + uint64 proposal_id; +} +``` + +While this is most commonly used for gRPC, overloading protobuf `service` definitions like this does not violate +the intent of the [protobuf spec](https://developers.google.com/protocol-buffers/docs/proto3#services) which says: +> If you don’t want to use gRPC, it’s also possible to use protocol buffers with your own RPC implementation. +With this approach, we would get an auto-generated `MsgServer` interface: + +In addition to clearly specifying return types, this has the benefit of generating client and server code. On the server +side, this is almost like an automatically generated keeper method and could maybe be used intead of keepers eventually +(see [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093)): + +```go +package gov + +type MsgServer interface { + SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) +} +``` + +On the client side, developers could take advantage of this by creating RPC implementations that encapsulate transaction +logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services) +could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s. + +For backwards compatibility, existing `Msg` types should be used as the request parameter +for `service` definitions. Newer `Msg` types which only support `service` definitions +should use the more canonical `Msg...Request` names. + +### Encoding + +Currently, we are encoding `Msg`s as `Any` in `Tx`s which involves packing the +binary-encoded `Msg` with its type URL. + +The type URL for `MsgSubmitProposal` based on the proto3 spec is `/cosmos.gov.MsgSubmitProposal`. + +The fully-qualified name for the `SubmitProposal` service method above (also +based on the proto3 and gRPC specs) is `/cosmos.gov.Msg/SubmitProposal` which varies +by a single `/` character. The generated `.pb.go` files for protobuf `service`s +include names of this form and any compliant protobuf/gRPC code generator will +generate the same name. + +In order to encode service methods in transactions, we encode them as `Any`s in +the same `TxBody.messages` field as other `Msg`s. We simply set `Any.type_url` +to the full-qualified method name (ex. `/cosmos.gov.Msg/SubmitProposal`) and +set `Any.value` to the protobuf encoding of the request message +(`MsgSubmitProposal` in this case). + +### Decoding + +When decoding, `TxBody.UnpackInterfaces` will need a special case +to detect if `Any` type URLs match the service method format (ex. `/cosmos.gov.Msg/SubmitProposal`) +by checking for two `/` characters. Messages that are method names plus request parameters +instead of a normal `Any` messages will get unpacked into the `ServiceMsg` struct: + +```go +type ServiceMsg struct { + // MethodName is the fully-qualified service name + MethodName string + // Request is the request payload + Request MsgRequest +} +``` + +### Routing + +In the future, `service` definitions may become the primary method for defining +`Msg`s. As a starting point, we need to integrate with the SDK's existing routing +and `Msg` interface. + +To do this, `ServiceMsg` implements the `sdk.Msg` interface and its handler does the +actual method routing, allowing this feature to be added incrementally on top of +existing functionality. + +### `MsgRequest` interface + +All request messages will need to implement the `MsgRequest` interface which is a +simplified version of `Msg`, without `Route()`, `Type()` and `GetSignBytes()` which +are no longer needed: + +```go +type MsgRequest interface { + proto.Message + ValidateBasic() error + GetSigners() []AccAddress +} +``` + +`ServiceMsg` will forward its `ValidateBasic` and `GetSigners` methods to the `MsgRequest` +methods. + +### Module Configuration + +In [ADR 021](./adr-021-protobuf-query-encoding.md), we introduced a method `RegisterQueryService` +to `AppModule` which allows for modules to register gRPC queriers. + +To register `Msg` services, we attempt a more extensible approach by converting `RegisterQueryService` +to a more generic `RegisterServices` method: + +```go +type AppModule interface { + RegisterServices(Configurator) + ... +} + +type Configurator interface { + QueryServer() grpc.Server + MsgServer() grpc.Server +} + +// example module: +func (am AppModule) RegisterServices(cfg Configurator) { + types.RegisterQueryServer(cfg.QueryServer(), keeper) + types.RegisterMsgServer(cfg.MsgServer(), keeper) +} +``` + +The `RegisterServices` method and the `Configurator` interface are intended to +evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) +and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421). + +When `Msg` services are registered, the framework _should_ verify that all `Msg...Request` types +implement the `MsgRequest` interface described above and throw an error during initialization rather +than later when transactions are processed. + +### `Msg` Service Implementation + +Just like query services, `Msg` service methods can retrieve the `sdk.Context` +from the `context.Context` parameter method using the `sdk.UnwrapSDKContext` +method: + +```go +package gov + +func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitProposal) (*MsgSubmitProposalResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + ... +} +``` + +The `sdk.Context` should have an `EventManager` already attached by the `ServiceMsg` +router. + +Separate handler definition is no longer needed with this approach. + +## Consequences + +This design changes how a module functionality is exposed and accessed. It deprecates the existing `Handler` interface and `AppModule.Route` in favor of [Protocol Buffer Services](https://developers.google.com/protocol-buffers/docs/proto3#services) and Service Routing described above. This dramatically simplifies the code. We don't need to create handlers and keepers any more. Use of Protocol Buffer auto-generated clients clearly separates the communication interfaces between the module and a modules user. The control logic (aka handlers and keepers) is not exposed any more. A module interface can be seen as a black box accessible through a client API. It's worth to note that the client interfaces are also generated by Protocol Buffers. + +This also allows us to change how we perform functional tests. Instead of mocking AppModules and Router, we will mock a client (server will stay hidden). More specifically: we will never mock `moduleA.MsgServer` in `moduleB`, but rather `moduleA.MsgClient`. One can think about it as working with external services (eg DBs, or online servers...). We assume that the transmission between clients and servers is correctly handled by generated Protocol Buffers. + +Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns. + +### Pros +- communicates return type clearly +- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it +- communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that +- generated client code could be useful for clients and tests +- dramatically reduces and simplifies the code + +### Cons +- supporting both this and the current concrete `Msg` type approach simultaneously could be confusing +(we could choose to deprecate the current approach) +- using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec) + + +## References + +- [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122) +- [proto 3 Language Guide: Defining Services](https://developers.google.com/protocol-buffers/docs/proto3#services) +- [Initial pre-`Any` `Msg` designs](https://docs.google.com/document/d/1eEgYgvgZqLE45vETjhwIw4VOqK-5hwQtZtjVbiXnIGc) +- [ADR 020](./adr-020-protobuf-transaction-encoding.md) +- [ADR 021](./adr-021-protobuf-query-encoding.md) diff --git a/docs/architecture/adr-032-typed-events.md b/docs/architecture/adr-032-typed-events.md new file mode 100644 index 000000000000..33758900d47a --- /dev/null +++ b/docs/architecture/adr-032-typed-events.md @@ -0,0 +1,319 @@ +# ADR 032: Typed Events + +## Changelog + +- 28-Sept-2020: Initial Draft + +## Authors + +- Anil Kumar (@anilcse) +- Jack Zampolin (@jackzampolin) +- Adam Bozanich (@boz) + +## Status + +Proposed + +## Abstract + +Currently in the SDK, events are defined in the handlers for each message as well as `BeginBlock` and `EndBlock`. Each module doesn't have types defined for each event, they are implemented as `map[string]string`. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. + +## Context + +Currently in the SDK, events are defined in the handlers for each message, meaning each module doesn't have a cannonical set of types for each event. Above all else this makes these events difficult to consume as it requires a great deal of raw string matching and parsing. This proposal focuses on updating the events to use **typed events** defined in each module such that emiting and subscribing to events will be much easier. This workflow comes from the experience of the Akash Network team. + +[Our platform](http://github.com/ovrclk/akash) requires a number of programatic on chain interactions both on the provider (datacenter - to bid on new orders and listen for leases created) and user (application developer - to send the app manifest to the provider) side. In addition the Akash team is now maintaining the IBC [`relayer`](https://github.com/ovrclk/relayer), another very event driven process. In working on these core pieces of infrastructure, and integrating lessons learned from Kubernetes developement, our team has developed a standard method for defining and consuming typed events in SDK modules. We have found that it is extremely useful in building this type of event driven application. + +As the SDK gets used more extensively for apps like `peggy`, other peg zones, IBC, DeFi, etc... there will be an exploding demand for event driven applications to support new features desired by users. We propose upstreaming our findings into the SDK to enable all SDK applications to quickly and easily build event driven apps to aid their core application. Wallets, exchanges, explorers, and defi protocols all stand to benefit from this work. + +If this proposal is accepted, users will be able to build event driven SDK apps in go by just writing `EventHandler`s for their specific event types and passing them to `EventEmitters` that are defined in the SDK. + +The end of this proposal contains a detailed example of how to consume events after this refactor. + +This proposal is specifically about how to consume these events as a client of the blockchain, not for intermodule communication. + +## Decision + +__Step-1__: Implement additional functionality in the `types` package: `EmitTypedEvent` and `ParseTypedEvent` functions + +```go +// types/events.go + +// EmitTypedEvent takes typed event and emits converting it into sdk.Event +func (em *EventManager) EmitTypedEvent(event proto.Message) error { + evtType := proto.MessageName(event) + evtJSON, err := codec.ProtoMarshalJSON(event) + if err != nil { + return err + } + + var attrMap map[string]json.RawMessage + err = json.Unmarshal(evtJSON, &attrMap) + if err != nil { + return err + } + + var attrs []abci.EventAttribute + for k, v := range attrMap { + attrs = append(attrs, abci.EventAttribute{ + Key: []byte(k), + Value: v, + }) + } + + em.EmitEvent(Event{ + Type: evtType, + Attributes: attrs, + }) + + return nil +} + +// ParseTypedEvent converts abci.Event back to typed event +func ParseTypedEvent(event abci.Event) (proto.Message, error) { + concreteGoType := proto.MessageType(event.Type) + if concreteGoType == nil { + return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type) + } + + var value reflect.Value + if concreteGoType.Kind() == reflect.Ptr { + value = reflect.New(concreteGoType.Elem()) + } else { + value = reflect.Zero(concreteGoType) + } + + protoMsg, ok := value.Interface().(proto.Message) + if !ok { + return nil, fmt.Errorf("%q does not implement proto.Message", event.Type) + } + + attrMap := make(map[string]json.RawMessage) + for _, attr := range event.Attributes { + attrMap[string(attr.Key)] = attr.Value + } + + attrBytes, err := json.Marshal(attrMap) + if err != nil { + return nil, err + } + + err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg) + if err != nil { + return nil, err + } + + return protoMsg, nil +} +``` + +Here, the `EmitTypedEvent` is a method on `EventManager` which takes typed event as input and apply json serialization on it. Then it maps the JSON key/value pairs to `event.Attributes` and emits it in form of `sdk.Event`. `Event.Type` will be the type URL of the proto message. + +When we subscribe to emitted events on the tendermint websocket, they are emitted in the form of an `abci.Event`. `ParseTypedEvent` parses the event back to it's original proto message. + +__Step-2__: Add proto definitions for typed events for msgs in each module: + +For example, let's take `MsgSubmitProposal` of `gov` module and implement this event's type. + +```protobuf +// proto/cosmos/gov/v1beta1/gov.proto +// Add typed event definition + +package cosmos.gov.v1beta1; + +message EventSubmitProposal { + string from_address = 1; + uint64 proposal_id = 2; + TextProposal proposal = 3; +} +``` + +__Step-3__: Refactor event emission to use the typed event created and emit using `sdk.EmitTypedEvent`: + +```go +// x/gov/handler.go +func handleMsgSubmitProposal(ctx sdk.Context, keeper keeper.Keeper, msg types.MsgSubmitProposalI) (*sdk.Result, error) { + ... + types.Context.EventManager().EmitTypedEvent( + &EventSubmitProposal{ + FromAddress: fromAddress, + ProposalId: id, + Proposal: proposal, + }, + ) + ... +} +``` + +#### How to subscribe to these typed events in `Client` + +> NOTE: Full code example below + +Users will be able to subscribe using `client.Context.Client.Subscribe` and consume events which are emitted using `EventHandler`s. + +Akash Network has built a simple [`pubsub`](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/pubsub/bus.go#L20). This can be used to subscribe to `abci.Events` and [publish](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L21) them as typed events. + +Please see the below code sample for more detail on this flow looks for clients. + +## Consequences + +### Positive + +* Improves consistency of implementation for the events currently in the sdk +* Provides a much more ergonomic way to handle events and facilitates writing event driven applications +* This implementation will support a middleware ecosystem of `EventHandler`s + +### Negative + + +## Detailed code example of publishing events + +This ADR also proposes adding affordances to emit and consume these events. This way developers will only need to write +`EventHandler`s which define the actions they desire to take. + +```go +// EventEmitter is a type that describes event emitter functions +// This should be defined in `types/events.go` +type EventEmitter func(context.Context, client.Context, ...EventHandler) error + +// EventHandler is a type of function that handles events coming out of the event bus +// This should be defined in `types/events.go` +type EventHandler func(proto.Message) error + +// Sample use of the functions below +func main() { + ctx, cancel := context.WithCancel(context.Background()) + + if err := TxEmitter(ctx, client.Context{}.WithNodeURI("tcp://localhost:26657"), SubmitProposalEventHandler); err != nil { + cancel() + panic(err) + } + + return +} + +// SubmitProposalEventHandler is an example of an event handler that prints proposal details +// when any EventSubmitProposal is emitted. +func SubmitProposalEventHandler(ev proto.Message) (err error) { + switch event := ev.(type) { + // Handle governance proposal events creation events + case govtypes.EventSubmitProposal: + // Users define business logic here e.g. + fmt.Println(ev.FromAddress, ev.ProposalId, ev.Proposal) + return nil + default: + return nil + } +} + +// TxEmitter is an example of an event emitter that emits just transaction events. This can and +// should be implemented somewhere in the SDK. The SDK can include an EventEmitters for tm.event='Tx' +// and/or tm.event='NewBlock' (the new block events may contain typed events) +func TxEmitter(ctx context.Context, cliCtx client.Context, ehs ...EventHandler) (err error) { + // Instantiate and start tendermint RPC client + client, err := cliCtx.GetNode() + if err != nil { + return err + } + + if err = client.Start(); err != nil { + return err + } + + // Start the pubsub bus + bus := pubsub.NewBus() + defer bus.Close() + + // Initialize a new error group + eg, ctx := errgroup.WithContext(ctx) + + // Publish chain events to the pubsub bus + eg.Go(func() error { + return PublishChainTxEvents(ctx, client, bus, simapp.ModuleBasics) + }) + + // Subscribe to the bus events + subscriber, err := bus.Subscribe() + if err != nil { + return err + } + + // Handle all the events coming out of the bus + eg.Go(func() error { + var err error + for { + select { + case <-ctx.Done(): + return nil + case <-subscriber.Done(): + return nil + case ev := <-subscriber.Events(): + for _, eh := range ehs { + if err = eh(ev); err != nil { + break + } + } + } + } + return nil + }) + + return group.Wait() +} + +// PublishChainTxEvents events using tmclient. Waits on context shutdown signals to exit. +func PublishChainTxEvents(ctx context.Context, client tmclient.EventsClient, bus pubsub.Bus, mb module.BasicManager) (err error) { + // Subscribe to transaction events + txch, err := client.Subscribe(ctx, "txevents", "tm.event='Tx'", 100) + if err != nil { + return err + } + + // Unsubscribe from transaction events on function exit + defer func() { + err = client.UnsubscribeAll(ctx, "txevents") + }() + + // Use errgroup to manage concurrency + g, ctx := errgroup.WithContext(ctx) + + // Publish transaction events in a goroutine + g.Go(func() error { + var err error + for { + select { + case <-ctx.Done(): + break + case ed := <-ch: + switch evt := ed.Data.(type) { + case tmtypes.EventDataTx: + if !evt.Result.IsOK() { + continue + } + // range over events, parse them using the basic manager and + // send them to the pubsub bus + for _, abciEv := range events { + typedEvent, err := sdk.ParseTypedEvent(abciEv) + if err != nil { + return er + } + if err := bus.Publish(typedEvent); err != nil { + bus.Close() + return + } + continue + } + } + } + } + return err + }) + + // Exit on error or context cancelation + return g.Wait() +} +``` + +## References +- [Publish Custom Events via a bus](https://github.com/ovrclk/akash/blob/90d258caeb933b611d575355b8df281208a214f8/events/publish.go#L19-L58) +- [Consuming the events in `Client`](https://github.com/ovrclk/deploy/blob/bf6c633ab6c68f3026df59efd9982d6ca1bf0561/cmd/event-handlers.go#L57) diff --git a/docs/architecture/adr-034-account-rekeying.md b/docs/architecture/adr-034-account-rekeying.md new file mode 100644 index 000000000000..22cb79e3135f --- /dev/null +++ b/docs/architecture/adr-034-account-rekeying.md @@ -0,0 +1,81 @@ +# ADR 034: Account Rekeying + +## Changelog + +- 30-09-2020: Initial Draft + +## Status + +PROPOSED + +## Abstract + +Account rekeying is a process hat allows an account to replace its authentication pubkey with a new one. + +## Context + +Currently, in the Cosmos SDK, the address of an auth `BaseAccount` is based on the hash of the public key. Once an account is created, the public key for the account is set in stone, and cannot be changed. This can be a problem for users, as key rotation is a useful security practice, but is not possible currently. Furthermore, as multisigs are a type of pubkey, once a multisig for an account is set, it can not be updated. This is problematic, as multisigs are often used by organizations or companies, who may need to change their set of multisig signers for internal reasons. + +Transferring all the assets of an account to a new account with the updated pubkey is not sufficient, because some "engagements" of an account are not easily transferable. For example, in staking, to transfer bonded Atoms, an account would have to unbond all delegations and wait the three week unbonding period. Even more significantly, for validator operators, ownership over a validator is not transferrable at all, meaning that the operator key for a validator can never be updated, leading to poor operational security for validators. + +## Decision + +We propose the addition of a new feature to `x/auth` that allows accounts to update the public key associated with their account, while keeping the address the same. + +This is possible because the Cosmos SDK `BaseAccount` stores the public key for an account in state, instead of making the assumption that the public key is included in the transaction (whether explicitly or implicitly through the signature) as in other blockchains such as Bitcoin and Ethereum. Because the public key is stored on chain, it is okay for the public key to not hash to the address of an account, as the address is not pertinent to the signature checking process. + +To build this system, we design a new Msg type as follows: + +```protobuf +service Msg { + rpc ChangePubKey(MsgChangePubKey) returns (MsgChangePubKeyResponse); +} + +message MsgChangePubKey { + string address = 1; + google.protobuf.Any pub_key = 2; +} + +message MsgChangePubKeyResponse {} +``` + +The MsgChangePubKey transaction needs to be signed by the existing pubkey in state. + +Once, approved, the handler for this message type, which takes in the AccountKeeper, will update the in-state pubkey for the account and replace it with the pubkey from the Msg. + + +An account that has had its pubkey changed cannot be automatically pruned from state. This is because if pruned, the original pubkey of the account would be needed to recreate the same address, but the owner of the address may not have the original pubkey anymore. Currently, we do not automatically prune any accounts anyways, but we would like to keep this option open the road (this is the purpose of account numbers). To resolve this, we charge an additional gas fee for this operation to compensate for this this externality (this bound gas amount is configured as parameter `PubKeyChangeCost`). The bonus gas is charged inside the handler, using the `ConsumeGas` function. Furthermore, in the future, we can allow accounts that have rekeyed manually prune themselves using a new Msg type such as `MsgDeleteAccount`. Manually pruning accounts can give a gas refund as an incentive for performing the action. + + +```go + amount := ak.GetParams(ctx).PubKeyChangeCost + ctx.GasMeter().ConsumeGas(amount, "pubkey change fee") +``` + + +Everytime a key for an address is changed, we will store a log of this change in the state of the chain, thus creating a stack of all previous keys for an address and the time intervals for which they were active. This allows dapps and clients to easily query past keys for an account which may be useful for features such as verifying timestamped off-chain signed messages. + + +## Consequences + +### Positive + +* Will allow users and validator operators to employ better operational security practices with key rotation. +* Will allow organizations or groups to easily change and add/remove multisig signers. + +### Negative + +Breaks the current assumed relationship between address and pubkeys as H(pubkey) = address. This has a couple of consequences. + +* This makes wallets that support this feature more complicated. For example, if an address on chain was updated, the corresponding key in the CLI wallet also needs to be updated. +* Cannot automatically prune accounts with 0 balance that have had their pubkey changed. + + +### Neutral + +* While the purpose of this is intended to allow the owner of an account to update to a new pubkey they own, this could technically also be used to transfer ownership of an account to a new owner. For example, this could be use used to sell a staked position without unbonding or an account that has vesting tokens. However, the friction of this is very high as this would essentially have to be done as a very specific OTC trade. Furthermore, additional constraints could be added to prevent accouns with Vesting tokens to use this feature. +* Will require that PubKeys for an account are included in the genesis exports. + +## References + ++ https://www.algorand.com/resources/blog/announcing-rekeying diff --git a/docs/architecture/adr-035-rosetta-api-support.md b/docs/architecture/adr-035-rosetta-api-support.md new file mode 100644 index 000000000000..2da663f5729d --- /dev/null +++ b/docs/architecture/adr-035-rosetta-api-support.md @@ -0,0 +1,274 @@ +# ADR 035: Rosetta API Support + +## Authors + +- Jonathan Gimeno (@jgimeno) +- David Grierson (@senormonito) +- Alessio Treglia (@alessio) + +## Context + +[Rosetta API](https://www.rosetta-api.org/) is an open-source specification and set of tools developed by Coinbase to +standardise blockchain interactions. + +Through the use of a standard API for integrating blockchain applications it will + +* Be easier for a user to interact with a given blockchain +* Allow exchanges to integrate new blockchains quickly and easily +* Enable application developers to build cross-blockchain applications such as block explorers, wallets and dApps at + considerably lower cost and effort. + +## Decision + +It is clear that adding Rosetta API support to the Cosmos SDK will bring value to all the developers and +Cosmos SDK based chains in the ecosystem. How it is implemented is key. + +The driving principles of the proposed design are: + +1. **Extensibility:** it must be as riskless and painless as possible for application developers to set-up network + configurations to expose Rosetta API-compliant services. +2. **Long term support:** This proposal aims to provide support for all the supported Cosmos SDK release series. +3. **Cost-efficiency:** Backporting changes to Rosetta API specifications from `master` to the various stable + branches of Cosmos SDK is a cost that needs to be reduced. + +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. +3. There will be two options for starting an API service in applications: + a. API shares the application process + b. API-specific process. + + +## Architecture + +### The External Repo + +As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces. + +#### Service + +`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. + +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 +} +``` + +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 +} + +type DataAPI interface { + server.NetworkAPIServicer + server.AccountAPIServicer + server.MempoolAPIServicer + server.BlockAPIServicer + server.ConstructionAPIServicer +} + +type ConstructionAPI interface { + server.ConstructionAPIServicer +} +``` + +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. +``` + +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). + +### 2. Cosmos SDK Implementation + +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, + }, + ), + } +} +``` + +### 3. API service invocation + +As stated at the start, application developers will have two methods for invocation of the Rosetta API service: + +1. Shared process for both application and API +2. Standalone API service + +#### 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 + } + }() + } + +``` + +#### 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() + } + } + ... + +} +``` + +## Status + +Proposed + +## Consequences + +### Positive + +- Out-of-the-box Rosetta API support within Cosmos SDK. +- Blockchain interface standardisation + +## References + +- https://www.rosetta-api.org/ +- https://github.com/tendermint/cosmos-rosetta-gateway diff --git a/docs/architecture/adr-037-gov-split-vote.md b/docs/architecture/adr-037-gov-split-vote.md new file mode 100644 index 000000000000..af9cd8d6f6a9 --- /dev/null +++ b/docs/architecture/adr-037-gov-split-vote.md @@ -0,0 +1,104 @@ +# ADR 037: Governance split votes + +## Changelog + +- 2020/10/28: Intial draft + +## Status + +Proposed + +## Abstract + +This ADR defines a modification to the the governance module that would allow a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No. + +## Context + +Currently, an address can cast a vote with only one options (Yes/No/Abstain/NoWithVeto) and use their full voting power behind that choice. + +However, often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Another example use case is exchanges. Many centralized exchanges often stake a portion of their users' tokens in their custody. Currently, it is not possible for them to do "passthrough voting" and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll. + +## Decision + +We modify the vote structs to be + +``` +type WeightedVoteOption struct { + Option string + Weight sdk.Dec +} + +type Vote struct { + ProposalID int64 + Voter sdk.Address + Options []WeightedVoteOption +} +``` + +And for backwards compatibility, we introduce `MsgWeightedVote` while keeping `MsgVote`. +``` +type MsgVote struct { + ProposalID int64 + Voter sdk.Address + Option Option +} + +type MsgWeightedVote struct { + ProposalID int64 + Voter sdk.Address + Options []WeightedVoteOption +} +``` + +The `ValidateBasic` of a `MsgWeightedVote` struct would require that +1. The sum of all the Rates is equal to 1.0 +2. No Option is repeated + +The governance tally function will iterate over all the options in a vote and add to the tally the result of the voter's voting power * the rate for that option. + +``` +tally() { + results := map[types.VoteOption]sdk.Dec + + for _, vote := range votes { + for i, weightedOption := range vote.Options { + results[weightedOption.Option] += getVotingPower(vote.voter) * weightedOption.Weight + } + } +} +``` + +The CLI command for creating a multi-option vote would be as such: +```sh +simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey +``` + +To create a single-option vote a user can do either +``` +simd tx gov vote 1 "yes=1" --from mykey +``` + +or + +```sh +simd tx gov vote 1 yes --from mykey +``` + +to maintain backwards compatibility. + + +## Consequences + +### Backwards Compatibility +- Previous VoteMsg types will remain the same and so clients will not have to update their procedure unless they want to support the WeightedVoteMsg feature. +- When querying a Vote struct from state, its structure will be different, and so clients wanting to display all voters and their respective votes will have to handle the new format and the fact that a single voter can have split votes. +- The result of querying the tally function should have the same API for clients. + +### Positive +- Can make the voting process more accurate for addresses representing multiple stakeholders, often some of the largest addresses. + +### Negative +- Is more complex than simple voting, and so may be harder to explain to users. However, this is mostly mitigated because the feature is opt-in. + +### Neutral +- Relatively minor change to governance tally function. diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md index 0638a71f4535..5d6a342c2946 100644 --- a/docs/architecture/adr-template.md +++ b/docs/architecture/adr-template.md @@ -4,25 +4,42 @@ - {date}: {changelog} +## Status + +{DRAFT | PROPOSED} Not Implemented + +> Please have a look at the [PROCESS](./PROCESS.md#adr-status) page. +> Use DRAFT if the ADR is in a draft stage (draft PR) or PROPOSED if it's in review. + + +## Abstract + +> "If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the ADR. +> A short (~200 word) description of the issue being addressed. + + ## Context -> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. +> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. It should clearly explain the problem and motivation that the proposal aims to resolve. > {context body} + ## Decision > This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..." > {decision body} -## Status - -> A decision may be "proposed" if the project stakeholders haven't agreed with it yet, or "accepted" once it is agreed. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement. -> {Deprecated|Proposed|Accepted} ## Consequences > This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. + +### Backwards Compatibility + +> All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright. + + ### Positive {positive consequences} @@ -35,6 +52,18 @@ {neutral consequences} + +## Further Discussions + +While an ADR is in the DRAFT or PROPOSED stage, this section should contain a summary of issues to be solved in future iterations (usually referencing comments from a pull-request discussion). +Later, this section can optionally list ideas or improvements the author or reviewers found during the analysis of this ADR. + + +## Test Cases [optional] + +Test cases for an implementation are mandatory for ADRs that are affecting consensus changes. Other ADRs can choose to include links to test cases if applicable. + + ## References -- {reference link} \ No newline at end of file +- {reference link} diff --git a/docs/architecture/adt-014-proportional-slashing.md b/docs/architecture/adt-014-proportional-slashing.md deleted file mode 100644 index 1ad1d6a31599..000000000000 --- a/docs/architecture/adt-014-proportional-slashing.md +++ /dev/null @@ -1,90 +0,0 @@ -# ADR 14: Proportional Slashing - -## Changelog - -- 2019-10-15: Initial draft - -## Context - -In Proof of Stake-based chains, centralization of consensus power amongst a small set of validators can cause harm to the network due to increased risk of censorship, liveness failure, fork attacks, etc. However, while this centralization causes a negative externality to the network, it is not directly felt by the delegators contributing towards delegating towards already large validators. We would like a way to pass on the negative externality cost of centralization onto those large validators and their delegators. - -## Decision - -### Design - -To solve this problem, we will implement a procedure called Proportional Slashing. The desire is that the larger a validator is, the more they should be slashed. The first naive attempt is to make a validator's slash percent proportional to their share of consensus voting power. - -``` -slash_amount = k * power // power is the faulting validator's voting power and k is some on-chain constant -``` - -However, this will incentivize validators with large amounts of stake to split up their voting power amongst accounts, so that if they fault, they all get slashed at a lower percent. The solution to this is to take into account not just a validator's own voting percentage, but also the voting percentage of all the other validators who get slashed in a specified time frame. - -``` -slash_amount = k * (power_1 + power_2 + ... + power_n) // where power_i is the voting power of the ith validator faulting in the specified time frame and k is some on-chain constant -``` - -Now, if someone splits a validator of 10% into two validators of 5% each which both fault, then they both fault in the same time frame, they both will still get slashed at the sum 10% amount. - -However, an operator might still choose to split up their stake across multiple accounts with hopes that if any of them fault independently, they will not get slashed at the full amount. In the case that the validators do fault together, they will get slashed the same amount as if they were one entity. There is no con to splitting up. However, if operators are going to split up their stake without actually decorrelating their setups, this also causes a negative externality to the network as it fills up validator slots that could have gone to others or increases the commit size. In order to disincentivize this, we want it to be the case such that splitting up a validator into multiple validators and they fault together is punished more heavily that keeping it as a single validator that faults. - -We can achieve this by not only taking into account the sum of the percentages of the validators that faulted, but also the *number* of validators that faulted in the window. One general form for an equation that fits this desired property looks like this: - -``` -slash_amount = k * ((power_1)^(1/r) + (power_2)^(1/r) + ... + (power_n)^(1/r))^r // where k and r are both on-chain constants -``` - -So now, for example, assuming k=1 and r=2, if one validator of 10% faults, it gets a 10% slash, while if two validators of 5% each fault together, they both get a 20% slash ((sqrt(0.05)+sqrt(0.05))^2). - -#### Correlation across non-sybil validators - -One will note, that this model doesn't differentiate between multiple validators run by the same operators vs validators run by different operators. This can be seen as an additional benefit in fact. It incentivizes validators to differentiate their setups from other validators, to avoid having correlated faults with them or else they risk a higher slash. So for example, operators should avoid using the same popular cloud hosting platforms or using the same Staking as a Service providers. This will lead to a more resilient and decentralized network. - -#### Parameterization - -The value of k and r can be different for different types of slashable faults. For example, we may want to punish liveness faults 10% as severely as double signs. - -There can also be minimum and maximums put in place in order to bound the size of the slash percent. - -#### Griefing - -Griefing, the act of intentionally being slashed to make another's slash worse, could be a concern here. However, using the protocol described here, the attacker could not substantially grief without getting slashed a substantial amount themselves. The larger the validator is, the more heavily it can impact the slash, it needs to be non-trivial to have a significant impact on the slash percent. Furthermore, the larger the grief, the griefer loses quadratically more. - -It may also be possible to, rather than the k and r factors being constants, perhaps using an inverse gini coefficient may mitigate some griefing attacks, but this an area for future research. - -### Implementation - -In the slashing module, we will add two queues that will track all of the recent slash events. For double sign faults, we will define "recent slashes" as ones that have occured within the last `unbonding period`. For liveness faults, we will define "recent slashes" as ones that have occured withing the last `jail period`. - -``` -type SlashEvent struct { - Address sdk.ValAddress - SqrtValidatorVotingPercent sdk.Dec - SlashedSoFar sdk.Dec -} -``` - -These slash events will be pruned from the queue once they are older than their respective "recent slash period". - -Whenever a new slash occurs, a `SlashEvent` struct is created with the faulting validator's voting percent and a `SlashedSoFar` of 0. Because recent slash events are pruned before the unbonding period and unjail period expires, it should not be possible for the same validator to have multiple SlashEvents in the same Queue at the same time. - -We then will iterate over all the SlashEvents in the queue, adding their `SqrtValidatorVotingPercent` and squaring the result to calculate the new percent to slash all the validators in the queue at, using the "Square of Sum of Roots" formula introduced above. - -Once we have the `NewSlashPercent`, we then iterate over all the `SlashEvent`s in the queue once again, and if `NewSlashPercent > SlashedSoFar` for that SlashEvent, we call the `staking.Slash(slashEvent.Address, slashEvent.Power, Math.Min(Math.Max(minSlashPercent, NewSlashPercent - SlashedSoFar), maxSlashPercent)` (we pass in the power of the validator before any slashes occured, so that we slash the right amount of tokens). We then set `SlashEvent.SlashedSoFar` amount to `NewSlashPercent`. - - -## Status - -Proposed - -## Consequences - -### Positive - -- Increases decentralization by disincentivizing delegating to large validators -- Incentivizes Decorrelation of Validators -- More severely punishes attacks than accidental faults - -### Negative - -- May require computationally expensive root function in state machine diff --git a/docs/basics/README.md b/docs/basics/README.md index 131ee1ba452f..03934dcd5112 100644 --- a/docs/basics/README.md +++ b/docs/basics/README.md @@ -6,11 +6,12 @@ parent: # Basics -This repository contains reference documentation on the basic concepts of the Cosmos SDK. +This repository contains reference documentation on the basic concepts of the Cosmos SDK. 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. \ No newline at end of file +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 f5667a26009e..9b7648b8c7dd 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -1,19 +1,20 @@ -# Accounts +# Accounts -## Pre-requisite Readings {hide} +This document describes the in-built accounts system of the Cosmos SDK. {synopsis} + +### Pre-requisite Readings - [Anatomy of an SDK Application](./app-anatomy.md) {prereq} ## Account Definition -In the Cosmos SDK, an *account* designates a pair of *public key* `PubKey` and *private key* `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. +In the Cosmos SDK, an _account_ designates a pair of _public key_ `PubKey` and _private key_ `PrivKey`. The `PubKey` can be derived to generate various `Addresses`, which are used to identify users (among other parties) in the application. `Addresses` are also associated with [`message`s](../building-modules/messages-and-queries.md#messages) to identify the sender of the `message`. The `PrivKey` is used to generate [digital signatures](#signatures) to prove that an `Address` associated with the `PrivKey` approved of a given `message`. -To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. +To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). This standard defines how to build an HD wallet, where a wallet is a set of accounts. At the core of every account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to derive any number of `PrivKey`s using one-way cryptographic function. Then, a `PubKey` can be derived from the `PrivKey`. Naturally, the mnemonic is the most sensitive information, as private keys can always be re-generated if the mnemonic is preserved. ``` Account 0 Account 1 Account 2 @@ -55,65 +56,66 @@ To derive `PubKey`s and `PrivKey`s, the Cosmos SDK uses a standard called [BIP32 +-------------------+ ``` -In the Cosmos SDK, accounts are stored and managed via an object called a [`Keybase`](#keybase). +In the Cosmos SDK, accounts are stored and managed via an object called a [`Keyring`](#keyring). + +## Keyring -## Keybase +A `Keyring` is an object that stores and manages accounts. In the Cosmos SDK, a `Keyring` implementation follows the `Keyring` interface: -A `Keybase` is an object that stores and manages accounts. In the Cosmos SDK, a `Keybase` implementation follows the `Keybase` interface: ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keyring/keyring.go#L50-L88 -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/types.go#L13-L86 +The default implementation of `Keyring` comes from the third-party [`99designs/keyring`](https://github.com/99designs/keyring) library. -The default implementation of `Keybase` of the Cosmos SDK is `dbKeybase`. +A few notes on the `Keyring` methods: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/keybase.go +- `Sign(uid string, payload []byte) ([]byte, tmcrypto.PubKey, error)` strictly deals with the signature of the `payload` bytes. Some preliminary work should be done beforehand to prepare and encode the transaction into a canonical `[]byte` form. Protobuf being not deterministic, it has been decided in [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) that the canonical `payload` to sign is the `SignDoc` struct, deterministically encoded using [ADR-027](adr-027-deterministic-protobuf-serialization.md). Note that signature verification is not implemented in the SDK by default, it is deferred to the [`anteHandler`](../core/baseapp.md#antehandler). + +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L47-L64 -A few notes on the `Keybase` methods as implemented in `dbKeybase`: +- `NewAccount(uid, mnemonic, bip39Passwd, hdPath string, algo SignatureAlgo) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/armor.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `NewAccount` method derives keys and addresses using the algorithm specified in the last argument `algo`. Currently, the SDK supports two public key algorithms: -- `Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)` strictly deals with the signature of the `message` bytes. Some preliminary work should be done beforehand to prepare and encode the `message` into a canonical `[]byte` form. See an example of `message` preparation from the `auth` module. Note that signature verification is not implemented in the SDK by default. It is deferred to the [`anteHandler`](#antehandler). - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L176-L209 -- `CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)` creates a new mnemonic and prints it in the logs, but it **does not persist it on disk**. -- `CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)` creates a new account based on the [`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and persists it on disk (note that the `PrivKey` is [encrypted with a passphrase before being persisted](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/mintkey/mintkey.go), it is **never stored unencrypted**). In the context of this method, the `account` and `address` parameters refer to the segment of the BIP44 derivation path (e.g. `0`, `1`, `2`, ...) used to derive the `PrivKey` and `PubKey` from the mnemonic (note that given the same mnemonic and `account`, the same `PrivKey` will be generated, and given the same `account` and `address`, the same `PubKey` and `Address` will be generated). Finally, note that the `CreateAccount` method derives keys and addresses using `secp256k1` as implemented in the [Tendermint library](https://github.com/tendermint/tendermint/tree/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1). As a result, it only works for creating account keys and addresses, not consensus keys. See [`Addresses`](#addresses) for more. + - `secp256k1`, as implemented in the [SDK's `crypto/keys/secp256k1` package](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/secp256k1/secp256k1.go), + - `ed25519`, as implemented in the [SDK's `crypto/keys/ed25519` package](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/ed25519/ed25519.go). -The current implementation of `dbKeybase` is basic and does not offer on-demand locking. If an instance of `dbKeybase` is created, the underlying `db` is locked meaning no other process can access it besides the one in which it was instantiated. This is the reason why the default SDK client uses another implementation of the `Keybase` interface called `lazyKeybase`: - - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/lazy_keybase.go +- `ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)` exports a private key in ASCII-armored encrypted format, using the given passphrase. You can then either import it again into the keyring using the `ImportPrivKey(uid, armor, passphrase string)` function, or decrypt it into a raw private key using the `UnarmorDecryptPrivKey(armorStr string, passphrase string)` function. -`lazyKeybase` is simple wrapper around `dbKeybase` which locks the database only when operations are to be performed and unlocks it immediately after. With the `lazyKeybase`, it is possible for the [command-line interface](../interfaces/cli.md) to create a new account while the [rest server](../interfaces/rest.md) is running. It is also possible to pipe multiple CLI commands. +Also see the [`Addresses`](#addresses) section for more information. ## Addresses and PubKeys `Addresses` and `PubKey`s are both public information that identify actors in the application. There are 3 main types of `Addresses`/`PubKeys` available by default in the Cosmos SDK: -- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve. -- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve. +- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`secp256k1`** curve. +- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`secp256k1`** curve. - Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve. | | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length | -|--------------------|-----------------------|----------------------|-------------|---------------------|--------------------| +| ------------------ | --------------------- | -------------------- | ----------- | ------------------- | ------------------ | | Accounts | cosmos | cosmospub | `secp256k1` | `20` | `33` | | Validator Operator | cosmosvaloper | cosmosvaloperpub | `secp256k1` | `20` | `33` | -| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` | +| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` | ### PubKeys -`PubKey`s used in the Cosmos SDK follow the `Pubkey` interface defined in tendermint's `crypto` package: +`PubKey`s used in the Cosmos SDK are Protobuf messages and have the following methods: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/types/types.go#L8-L17 -+++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/crypto.go#L22-L27 +- For `secp256k1` keys, the actual implementation can be found [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/secp256k1/secp256k1.go). +- For `ed25519` keys, it can be found [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/ed25519/ed25519.go). -For `secp256k1` keys, the actual implementation can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1/secp256k1.go#L140). For `ed25519` keys, it can be found [here](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/ed25519/ed25519.go#L135). +In both case, the actual key (as raw bytes) is the compressed form of the pubkey. The first byte 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. -Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw form. Instead, they are double encoded using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`. +Note that in the Cosmos SDK, `Pubkeys` are not manipulated in their raw bytes form. Instead, they are encoded to string using [`Amino`](../core/encoding.md#amino) and [`bech32`](https://en.bitcoin.it/wiki/Bech32). In the SDK, it is done by first calling the `Bytes()` method on the raw `Pubkey` (which applies amino encoding), and then the `ConvertAndEncode` method of `bech32`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L579-L729 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/address.go#L579-L729 ### Addresses The Cosmos SDK comes by default with 3 types of addresses: - `AccAddress` for accounts. -- `ValAddress` for validator operators. -- `ConsAddress` for validator nodes. +- `ValAddress` for validator operators. +- `ConsAddress` for validator nodes. Each of these address types are an alias for an hex-encoded `[]byte` array of length 20. Here is the standard way to obtain an address `aa` from a `Pubkey pub`: @@ -123,12 +125,12 @@ aa := sdk.AccAddress(pub.Address().Bytes()) These addresses implement the `Address` interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L71-L80 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/address.go#L76-L85 -Of note, the `Marhsal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuff compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Next is an example: +Of note, the `Marshal()` and `Bytes()` method both return the same raw `[]byte` form of the address, the former being needed for Protobuf compatibility. Also, the `String()` method is used to return the `bech32` encoded form of the address, which should be the only address format with which end-user interract. Here is an example: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L229-L243 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/address.go#L235-L249 ## Next {hide} -Learn about [gas and fees](./gas-fees.md) {hide} \ No newline at end of file +Learn about [gas and fees](./gas-fees.md) {hide} diff --git a/docs/basics/app-anatomy.md b/docs/basics/app-anatomy.md index 359016b2c8e8..e25c114edf53 100644 --- a/docs/basics/app-anatomy.md +++ b/docs/basics/app-anatomy.md @@ -1,9 +1,10 @@ -# Anatomy of an SDK Application +# Anatomy of an SDK Application + +This document describes the core parts of a Cosmos SDK application. Throughout the document, a placeholder application named `app` will be used. {synopsis} ## Node Client @@ -43,53 +44,54 @@ In general, the core of the state-machine is defined in a file called `app.go`. The first thing defined in `app.go` is the `type` of the application. It is generally comprised of the following parts: -- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing). -- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. -- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. -- **A reference to a [`codec`](../core/encoding.md).** The application's `codec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The `codec` must be deterministic. The default codec is [amino](../core/encoding.md). -- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering [`routes`](../core/baseapp.md#routing), [query routes](../core/baseapp.md#query-routing) or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). +- **A reference to [`baseapp`](../core/baseapp.md).** The custom application defined in `app.go` is an extension of `baseapp`. When a transaction is relayed by Tendermint to the application, `app` uses `baseapp`'s methods to route them to the appropriate module. `baseapp` implements most of the core logic for the application, including all the [ABCI methods](https://tendermint.com/docs/spec/abci/abci.html#overview) and the [routing logic](../core/baseapp.md#routing). +- **A list of store keys**. The [store](../core/store.md), which contains the entire state, is implemented as a [`multistore`](../core/store.md#multistore) (i.e. a store of stores) in the Cosmos SDK. Each module uses one or multiple stores in the multistore to persist their part of the state. These stores can be accessed with specific keys that are declared in the `app` type. These keys, along with the `keepers`, are at the heart of the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. +- **A list of module's `keeper`s.** Each module defines an abstraction called [`keeper`](../building-modules/keeper.md), which handles reads and writes for this module's store(s). The `keeper`'s methods of one module can be called from other modules (if authorized), which is why they are declared in the application's type and exported as interfaces to other modules so that the latter can only access the authorized functions. +- **A reference to an [`appCodec`](../core/encoding.md).** The application's `appCodec` is used to serialize and deserialize data structures in order to store them, as stores can only persist `[]bytes`. The default codec is [Protocol Buffers](../core/encoding.md). +- **A reference to a [`legacyAmino`](../core/encoding.md) codec.** Some parts of the SDK have not been migrated to use the `appCodec` above, and are still hardcoded to use Amino. Other parts explicity use Amino for backwards compatibility. For these reasons, the application still holds a reference to the legacy Amino codec. Please note that the Amino codec will be removed from the SDK in the upcoming releases. +- **A reference to a [module manager](../building-modules/module-manager.md#manager)** and a [basic module manager](../building-modules/module-manager.md#basicmanager). The module manager is an object that contains a list of the application's module. It facilitates operations related to these modules, like registering their [`Msg` service](../core/baseapp.md#msg-services) and [gRPC `Query` service](../core/baseapp.md#grpc-query-services), or setting the order of execution between modules for various functions like [`InitChainer`](#initchainer), [`BeginBlocker` and `EndBlocker`](#beginblocker-and-endblocker). -See an example of application type definition from [`gaia`](https://github.com/cosmos/gaia) +See an example of application type definition from `simapp`, the SDK's own app used for demo and testing purposes: -+++ https://github.com/cosmos/gaia/blob/5bc422e6868d04747e50b467e8eeb31ae2fe98a3/app/app.go#L87-L115 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L145-L187 ### Constructor Function -This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command. +This function constructs a new application of the type defined in the section above. It must fulfill the `AppCreator` signature in order to be used in the [`start` command](../core/node.md#start-command) of the application's daemon command. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L20 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/types/app.go#L48-L50 Here are the main actions performed by this function: - Instantiate a new [`codec`](../core/encoding.md) and initialize the `codec` of each of the application's module using the [basic manager](../building-modules/module-manager.md#basicmanager) - Instantiate a new application with a reference to a `baseapp` instance, a codec and all the appropriate store keys. -- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. -- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. -- With the module manager, initialize the application's [`routes`](../core/baseapp.md#routing) and [query routes](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`handler`](#handler) using the routes defined here. Likewise, when a query is received by the application, it is routed to the appropriate module's [`querier`](#querier) using the query routes defined here. -- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. -- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. +- Instantiate all the [`keeper`s](#keeper) defined in the application's `type` using the `NewKeeper` function of each of the application's modules. Note that `keepers` must be instantiated in the correct order, as the `NewKeeper` of one module might require a reference to another module's `keeper`. +- Instantiate the application's [module manager](../building-modules/module-manager.md#manager) with the [`AppModule`](#application-module-interface) object of each of the application's modules. +- With the module manager, initialize the application's [`Msg` services](../core/baseapp.md#msg-services), [gRPC `Query` services](../core/baseapp.md#grpc-query-services), [legacy `Msg` routes](../core/baseapp.md#routing) and [legacy query routes](../core/baseapp.md#query-routing). When a transaction is relayed to the application by Tendermint via the ABCI, it is routed to the appropriate module's [`Msg` service](#msg-services) using the routes defined here. Likewise, when a gRPC query request is received by the application, it is routed to the appropriate module's [`gRPC query service`](#grpc-query-services) using the gRPC routes defined here. The SDK still supports legacy `Msg`s and legacy Tendermint queries, which are routed using respectively the legacy `Msg` routes and the legacy query routes. +- With the module manager, register the [application's modules' invariants](../building-modules/invariants.md). Invariants are variables (e.g. total supply of a token) that are evaluated at the end of each block. The process of checking invariants is done via a special module called the [`InvariantsRegistry`](../building-modules/invariants.md#invariant-registry). The value of the invariant should be equal to a predicted value defined in the module. Should the value be different than the predicted one, special logic defined in the invariant registry will be triggered (usually the chain is halted). This is useful to make sure no critical bug goes unnoticed and produces long-lasting effects that would be hard to fix. +- With the module manager, set the order of execution between the `InitGenesis`, `BegingBlocker` and `EndBlocker` functions of each of the [application's modules](#application-module-interface). Note that not all modules implement these functions. - Set the remainer of application's parameters: - + [`InitChainer`](#initchainer): used to initialize the application when it is first started. - + [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). - + [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification. -- Mount the stores. -- Return the application. + - [`InitChainer`](#initchainer): used to initialize the application when it is first started. + - [`BeginBlocker`, `EndBlocker`](#beginblocker-and-endlbocker): called at the beginning and the end of every block). + - [`anteHandler`](../core/baseapp.md#antehandler): used to handle fees and signature verification. +- Mount the stores. +- Return the application. Note that this function only creates an instance of the app, while the actual state is either carried over from the `~/.appd/data` folder if the node is restarted, or generated from the genesis file if the node is started for the first time. -See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia): +See an example of application constructor from `simapp`: -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L198-L441 ### InitChainer -The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. +The `InitChainer` is a function that initializes the state of the application from a genesis file (i.e. token balances of genesis accounts). It is called when the application receives the `InitChain` message from the Tendermint engine, which happens when the node is started at `appBlockHeight == 0` (i.e. on genesis). The application must set the `InitChainer` in its [constructor](#constructor-function) via the [`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer) method. In general, the `InitChainer` is mostly composed of the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules. This is done by calling the `InitGenesis` function of the module manager, which in turn will call the `InitGenesis` function of each of the modules it contains. Note that the order in which the modules' `InitGenesis` functions must be called has to be set in the module manager using the [module manager's](../building-modules/module-manager.md) `SetOrderInitGenesis` method. This is done in the [application's constructor](#application-constructor), and the `SetOrderInitGenesis` has to be called before the `SetInitChainer`. -See an example of an `InitChainer` from [`gaia`](https://github.com/cosmos/gaia): - -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L235-L239 +See an example of an `InitChainer` from `simapp`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L464-L471 ### BeginBlocker and EndBlocker @@ -97,77 +99,74 @@ The SDK offers developers the possibility to implement automatic execution of co In general, the `BeginBlocker` and `EndBlocker` functions are mostly composed of the [`BeginBlock` and `EndBlock`](../building-modules/beginblock-endblock.md) functions of each of the application's modules. This is done by calling the `BeginBlock` and `EndBlock` functions of the module manager, which in turn will call the `BeginBLock` and `EndBlock` functions of each of the modules it contains. Note that the order in which the modules' `BegingBlock` and `EndBlock` functions must be called has to be set in the module manager using the `SetOrderBeginBlock` and `SetOrderEndBlock` methods respectively. This is done via the [module manager](../building-modules/module-manager.md) in the [application's constructor](#application-constructor), and the `SetOrderBeginBlock` and `SetOrderEndBlock` methods have to be called before the `SetBeginBlocker` and `SetEndBlocker` functions. -As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. +As a sidenote, it is important to remember that application-specific blockchains are deterministic. Developers must be careful not to introduce non-determinism in `BeginBlocker` or `EndBlocker`, and must also be careful not to make them too computationally expensive, as [gas](./gas-fees.md) does not constrain the cost of `BeginBlocker` and `EndBlocker` execution. -See an example of `BeginBlocker` and `EndBlocker` functions from [`gaia`](https://github.com/cosmos/gaia) +See an example of `BeginBlocker` and `EndBlocker` functions from `simapp` -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L224-L232 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/app.go#L454-L462 ### Register Codec -The `MakeCodec` function is the last important function of the `app.go` file. The goal of this function is to instantiate a [codec `cdc`](../core/encoding.md) (e.g. amino) initialize the codec of the SDK and each of the application's modules using the `RegisterCodec` function. +The `EncodingConfig` structure is the last important part of the `app.go` file. The goal of this structure is to define the codecs that will be used throughout the app. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/params/encoding.go#L9-L16 + +Here are descriptions of what each of the four fields means: -To register the application's modules, the `MakeCodec` function calls `RegisterCodec` on `ModuleBasics`. `ModuleBasics` is a [basic manager](../building-modules/module-manager.md#basicmanager) which lists all of the application's modules. It is instanciated in the `init()` function, and only serves to easily register non-dependant elements of application's modules (such as codec). To learn more about the basic module manager, click [here](../building-modules/module-manager.md#basicmanager). +- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces that 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 that 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 that can be used to register the module's own interfaces and implementations. + - You can read more about Any in [ADR-19](../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`. +- `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. -See an example of a `MakeCodec` from [`gaia`](https://github.com/cosmos/gaia): - -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L64-L70 +The SDK exposes a `MakeTestEncodingConfig` function used to create a `EncodingConfig` for the app constructor (`NewApp`). It uses Protobuf as a 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. +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`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/590358652cc1cbc13872ea1659187e073ea38e75/simapp/encoding.go#L8-L19 ## Modules -[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules). +[Modules](../building-modules/intro.md) are the heart and soul of SDK applications. They can be considered as state-machines within the state-machine. When a transaction is relayed from the underlying Tendermint engine via the ABCI to the application, it is routed by [`baseapp`](../core/baseapp.md) to the appropriate module in order to be processed. This paradigm enables developers to easily build complex state-machines, as most of the modules they need often already exist. For developers, most of the work involved in building an SDK application revolves around building custom modules required by their application that do not exist yet, and integrating them with modules that do already exist into one coherent application. In the application directory, the standard practice is to store modules in the `x/` folder (not to be confused with the SDK's `x/` folder, which contains already-built modules). ### Application Module Interface -Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`. +Modules must implement [interfaces](../building-modules/module-manager.md#application-module-interfaces) defined in the Cosmos SDK, [`AppModuleBasic`](../building-modules/module-manager.md#appmodulebasic) and [`AppModule`](../building-modules/module-manager.md#appmodule). The former implements basic non-dependant elements of the module, such as the `codec`, while the latter handles the bulk of the module methods (including methods that require references to other modules' `keeper`s). Both the `AppModule` and `AppModuleBasic` types are defined in a file called `./module.go`. -`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules. +`AppModule` exposes a collection of useful methods on the module that facilitates the composition of modules into a coherent application. These methods are are called from the `module manager`(../building-modules/module-manager.md#manager), which manages the application's collection of modules. -### Message Types +### `Msg` Services -[`Message`s](../building-modules/messages-and-queries.md#messages) are objects defined by each module that implement the [`message`](../building-modules/messages-and-queries.md#messages) interface. Each [`transaction`](../core/transactions.md) contains one or multiple `messages`. +Each module defines two [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services): one `Msg` service to handle messages, and one gRPC `Query` service to handle queries. If we consider the module as a state-machine, then a `Msg` is a state transition. A `Msg` service is a Protobuf service defining all possible `Msg`s a module exposes. Note that `Msg`s are bundled in [`transactions`](../core/transactions.md), and each transaction contains one or multiple `messages`. When a valid block of transactions is received by the full-node, Tendermint relays each one to the application via [`DeliverTx`](https://tendermint.com/docs/app-dev/abci-spec.html#delivertx). Then, the application handles the transaction: 1. Upon receiving the transaction, the application first unmarshalls it from `[]bytes`. -2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the message(s) contained in the transaction. -3. With the `Type()` method of the `message`, `baseapp` is able to route it to the appropriate module's [`handler`](#handler) in order for it to be processed. -4. If the message is successfully processed, the state is updated. +2. Then, it verifies a few things about the transaction like [fee payment and signatures](#gas-fees.md#antehandler) before extracting the `Msg`(s) contained in the transaction. +3. `Msg`s are encoded as Protobuf [`Any`s](#register-codec) via the `sdk.ServiceMsg` struct. By analyzing each `Any`'s `type_url`, baseapp's `msgServiceRouter` routes the `Msg` to the corresponding module's `Msg` service. +4. If the message is successfully processed, the state is updated. For a more detailed look at a transaction lifecycle, click [here](./tx-lifecycle.md). -Module developers create custom message types when they build their own module. The general practice is to prefix the type declaration of the message with `Msg`. For example, the message type `MsgSend` allows users to transfer tokens: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/bank/internal/types/msgs.go#L10-L15 +Module developers create custom `Msg`s when they build their own module. The general practice is to define all `Msg`s in a Protobuf service called `service Msg {}`, and define each `Msg` as a Protobuf service method, using the `rpc` keyword. These definitions usually reside in a `tx.proto` file. For example, the `x/bank` module defines two `Msg`s to allows users to transfer tokens: -It is processed by the `handler` of the `bank` module, which ultimately calls the `keeper` of the `auth` module in order to update the state. ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/bank/v1beta1/tx.proto#L10-L17 -### Handler +These two `Msg`s are processed by the `Msg` service of the `x/bank` module, which ultimately calls the `keeper` of the `x/auth` module in order to update the state. -The [`handler`](../building-modules/handler.md) refers to the part of the module responsible for processing the `message` after it is routed by `baseapp`. `handler` functions of modules are only executed if the transaction is relayed from Tendermint by the `DeliverTx` ABCI message. If the transaction is relayed by `CheckTx`, only stateless checks and fee-related stateful checks are performed. To better understand the difference between `DeliverTx`and `CheckTx`, as well as the difference between stateful and stateless checks, click [here](./tx-lifecycle.md). +Each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterMsgServer` function provided by the generated Protobuf code. -The `handler` of a module is generally defined in a file called `handler.go` and consists of: +### gRPC `Query` Services -- A **switch function** `NewHandler` to route the message to the appropriate `handler` function. This function returns a `handler` function, and is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's router](../core/baseapp.md#routing). Next is an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) - +++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/handler.go#L12-L26 -- **One handler function for each message type defined by the module**. Developers write the message processing logic in these functions. This generally involves doing stateful checks to ensure the message is valid and calling [`keeper`](#keeper)'s methods to update the state. +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). -Handler functions return a result of type `sdk.Result`, which informs the application on whether the message was successfully processed: +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. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 - -### Querier - -[`Queriers`](../building-modules/querier.md) are very similar to `handlers`, except they serve user queries to the state as opposed to processing transactions. A [query](../building-modules/messages-and-queries.md#queries) is initiated from an [interface](#interfaces) by an end-user who provides a `queryRoute` and some `data`. The query is then routed to the correct application's `querier` by `baseapp`'s `handleQueryCustom` method using `queryRoute`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/abci.go#L395-L453 - -The `Querier` of a module is defined in a file called `querier.go`, and consists of: - -- A **switch function** `NewQuerier` to route the query to the appropriate `querier` function. This function returns a `querier` function, and is is registered in the [`AppModule`](#application-module-interface) to be used in the application's module manager to initialize the [application's query router](../core/baseapp.md#query-routing). See an example of such a switch from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): - +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32 -- **One querier function for each data type defined by the module that needs to be queryable**. Developers write the query processing logic in these functions. This generally involves calling [`keeper`](#keeper)'s methods to query the state and marshalling it to JSON. +Protobuf generates a `QueryServer` interface for each module, containing all the service methods. A module's [`keeper`](#keeper) then needs to implement this `QueryServer` interface, by providing the concrete implementation of each service method. This concrete implementation is the handler of the corresponding gRPC query endpoint. +Finally, each module should also implement the `RegisterServices` method as part of the [`AppModule` interface](#application-module-interface). This method should call the `RegisterQueryServer` function provided by the generated Protobuf code. ### Keeper @@ -183,36 +182,54 @@ The `keeper` type definition generally consists of: Along with the type definition, the next important component of the `keeper.go` file is the `keeper`'s constructor function, `NewKeeper`. This function instantiates a new `keeper` of the type defined above, with a `codec`, store `keys` and potentially references to other modules' `keeper`s as parameters. The `NewKeeper` function is called from the [application's constructor](#constructor-function). The rest of the file defines the `keeper`'s methods, primarily getters and setters. -### Command-Line and REST Interfaces +### Command-Line, gRPC Services and REST Interfaces -Each module defines command-line commands and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. +Each module defines command-line commands, gRPC services and REST routes to be exposed to end-user via the [application's interfaces](#application-interfaces). This enables end-users to create messages of the types defined in the module, or to query the subset of the state managed by the module. #### CLI Generally, the [commands related to a module](../building-modules/module-interfaces.md#cli) are defined in a folder called `client/cli` in the module's folder. The CLI divides commands in two category, transactions and queries, defined in `client/cli/tx.go` and `client/cli/query.go` respectively. Both commands are built on top of the [Cobra Library](https://github.com/spf13/cobra): -- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata. -- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. +- Transactions commands let users generate new transactions so that they can be included in a block and eventually update the state. One command should be created for each [message type](#message-types) defined in the module. The command calls the constructor of the message with the parameters provided by the end-user, and wraps it into a transaction. The SDK handles signing and the addition of other transaction metadata. +- Queries let users query the subset of the state defined by the module. Query commands forward queries to the [application's query router](../core/baseapp.md#query-routing), which routes them to the appropriate [querier](#querier) the `queryRoute` parameter supplied. + +#### gRPC + +[gRPC](https://grpc.io) is a modern open source high performance RPC framework that has support in multiple languages. It is the recommended way for external clients (such as wallets, browsers and other backend services) to interact with a node. -#### REST +Each module can expose gRPC endpoints, called [service methods](https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition) and are defined in the [module's Protobuf `query.proto` file](#grpc-query-services). A service method is defined by its name, input arguments and output response. The module then needs to: -The [module's REST interface](../building-modules/module-interfaces.md#rest) lets users generate transactions and query the state through REST calls to the application's [light client daemon](../core/node.md#lcd) (LCD). REST routes are defined in a file `client/rest/rest.go`, which is composed of: +- define a `RegisterGRPCGatewayRoutes` method on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. +- for each service method, define a corresponding handler. The handler implements the core logic necessary to serve the gRPC request, and is located in the `keeper/grpc_query.go` file. + +#### gRPC-gateway REST Endpoints + +Some external clients may not wish to use gRPC. The SDK provides in this case a gRPC gateway service, which exposes each gRPC service as a correspoding REST endpoint. Please refer to the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) documentation to learn more. + +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`](../run-node/run-node.md#configuring-the-node-using-apptoml) config file, under the `api.swagger` key. + +#### Legacy API REST Endpoints + +The [module's Legacy REST interface](../building-modules/module-interfaces.md#legacy-rest) lets users generate transactions and query the state through REST calls to the application's Legacy API Service. REST routes are defined in a file `client/rest/rest.go`, which is composed of: - A `RegisterRoutes` function, which registers each route defined in the file. This function is called from the [main application's interface](#application-interfaces) for each module used within the application. The router used in the SDK is [Gorilla's mux](https://github.com/gorilla/mux). - Custom request type definitions for each query or transaction creation function that needs to be exposed. These custom request types build on the base `request` type of the Cosmos SDK: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/rest/rest.go#L47-L60 + +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/rest/rest.go#L62-L76 - One handler function for each request that can be routed to the given module. These functions implement the core logic necessary to serve the request. +These Legacy API endpoints are present in the SDK for backward compatibility purposes and will be removed in the next release. + ## Application Interface [Interfaces](../interfaces/interfaces-intro.md) let end-users interact with full-node clients. This means querying data from the full-node or creating and sending new transactions to be relayed by the full-node and eventually included in a block. -The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application generally has the `-cli` suffix (e.g. `appcli`), and defined in a file called `cmd/appcli/main.go`. The file contains: +The main interface is the [Command-Line Interface](../interfaces/cli.md). The CLI of an SDK application is built by aggregating [CLI commands](#cli) defined in each of the modules used by the application. The CLI of an application is the same as the daemon (e.g. `appd`), and defined in a file called `appd/main.go`. The file contains: -- **A `main()` function**, which is executed to build the `appcli` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appCli`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. -- **Query commands** are added by calling the `queryCmd` function, also defined in `appcli/main.go`. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appcli query [query]` of the CLI. -- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appcli tx [tx]` of the CLI. -- **A `registerRoutes` function**, which is called from the `main()` function when initializing the [application's light-client daemon (LCD)](../core/node.md#lcd) (i.e. `rest-server`). `registerRoutes` calls the `RegisterRoutes` function of each of the application's module, thereby registering the routes of the module to the lcd's router. The LCD can be started by running the following command `appcli rest-server`. +- **A `main()` function**, which is executed to build the `appd` interface client. This function prepares each command and adds them to the `rootCmd` before building them. At the root of `appd`, the function adds generic commands like `status`, `keys` and `config`, query commands, tx commands and `rest-server`. +- **Query commands** are added by calling the `queryCmd` function. This function returns a Cobra command that contains the query commands defined in each of the application's modules (passed as an array of `sdk.ModuleClients` from the `main()` function), as well as some other lower level query commands such as block or validator queries. Query command are called by using the command `appd query [query]` of the CLI. +- **Transaction commands** are added by calling the `txCmd` function. Similar to `queryCmd`, the function returns a Cobra command that contains the tx commands defined in each of the application's modules, as well as lower level tx commands like transaction signing or broadcasting. Tx commands are called by using the command `appd tx [tx]` of the CLI. See an example of an application's main command-line file from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) @@ -220,11 +237,23 @@ See an example of an application's main command-line file from the [nameservice ## Dependencies and Makefile -This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + +This section is optional, as developers are free to choose their dependency manager and project building method. That said, the current most used framework for versioning control is [`go.mod`](https://github.com/golang/go/wiki/Modules). It ensures each of the libraries used throughout the application are imported with the correct version. See an example from the [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice): +++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18 -For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appcli`](#application-interface). See an example of Makefile from the [nameservice tutorial]() +For building the application, a [Makefile](https://en.wikipedia.org/wiki/Makefile) is generally used. The Makefile primarily ensures that the `go.mod` is run before building the two entrypoints to the application, [`appd`](#node-client) and [`appd`](#application-interface). See an example of Makefile from the [nameservice tutorial]() +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/Makefile diff --git a/docs/basics/gas-fees.md b/docs/basics/gas-fees.md index 5bb4d779f5c0..2ab586497cd9 100644 --- a/docs/basics/gas-fees.md +++ b/docs/basics/gas-fees.md @@ -1,11 +1,12 @@ -# Gas and Fees +# Gas and Fees -## Pre-requisite Readings {hide} +This document describes the default strategies to handle gas and fees within a Cosmos SDK application. {synopsis} + +### Pre-requisite Readings - [Anatomy of an SDK Application](./app-anatomy.md) {prereq} @@ -14,21 +15,21 @@ synopsis: This document describes the default strategies to handle gas and fees In the Cosmos SDK, `gas` is a special unit that is used to track the consumption of resources during execution. `gas` is typically consumed whenever read and writes are made to the store, but it can also be consumed if expensive computation needs to be done. It serves two main purposes: - Make sure blocks are not consuming too many resources and will be finalized. This is implemented by default in the SDK via the [block gas meter](#block-gas-meter). -- Prevent spam and abuse from end-user. To this end, `gas` consumed during [`message`](../building-modules/messages-and-queries.md#messages) execution is typically priced, resulting in a `fee` (`fees = gas * gas-prices`). `fees` generally have to be paid by the sender of the `message`. Note that the SDK does not enforce `gas` pricing by default, as there may be other ways to prevent spam (e.g. bandwidth schemes). Still, most applications will implement `fee` mechanisms to prevent spam. This is done via the [`AnteHandler`](#antehandler). +- Prevent spam and abuse from end-user. To this end, `gas` consumed during [`message`](../building-modules/messages-and-queries.md#messages) execution is typically priced, resulting in a `fee` (`fees = gas * gas-prices`). `fees` generally have to be paid by the sender of the `message`. Note that the SDK does not enforce `gas` pricing by default, as there may be other ways to prevent spam (e.g. bandwidth schemes). Still, most applications will implement `fee` mechanisms to prevent spam. This is done via the [`AnteHandler`](#antehandler). ## Gas Meter -In the Cosmos SDK, `gas` is a simple alias for `uint64`, and is managed by an object called a *gas meter*. Gas meters implement the `GasMeter` interface +In the Cosmos SDK, `gas` is a simple alias for `uint64`, and is managed by an object called a _gas meter_. Gas meters implement the `GasMeter` interface -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L31-L39 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L34-L43 where: - `GasConsumed()` returns the amount of gas that was consumed by the gas meter instance. - `GasConsumedToLimit()` returns the amount of gas that was consumed by gas meter instance, or the limit if it is reached. -- `Limit()` returns the limit of the gas meter instance. `0` if the gas meter is infinite. -- `ConsumeGas(amount Gas, descriptor string)` consumes the amount of `gas` provided. If the `gas` overflows, it panics with the `descriptor` message. If the gas meter is not infinite, it panics if `gas` consumed goes above the limit. -- `IsPastLimit()` returns `true` if the amount of gas consumed by the gas meter instance is strictly above the limit, `false` otherwise. +- `Limit()` returns the limit of the gas meter instance. `0` if the gas meter is infinite. +- `ConsumeGas(amount Gas, descriptor string)` consumes the amount of `gas` provided. If the `gas` overflows, it panics with the `descriptor` message. If the gas meter is not infinite, it panics if `gas` consumed goes above the limit. +- `IsPastLimit()` returns `true` if the amount of gas consumed by the gas meter instance is strictly above the limit, `false` otherwise. - `IsOutOfGas()` returns `true` if the amount of gas consumed by the gas meter instance is above or equal to the limit, `false` otherwise. The gas meter is generally held in [`ctx`](../core/context.md), and consuming gas is done with the following pattern: @@ -37,19 +38,19 @@ The gas meter is generally held in [`ctx`](../core/context.md), and consuming ga ctx.GasMeter().ConsumeGas(amount, "description") ``` -By default, the Cosmos SDK makes use of two different gas meters, the [main gas meter](#main-gas-metter[) and the [block gas meter](#block-gas-meter). +By default, the Cosmos SDK makes use of two different gas meters, the [main gas meter](#main-gas-metter[) and the [block gas meter](#block-gas-meter). ### Main Gas Meter -`ctx.GasMeter()` is the main gas meter of the application. The main gas meter is initialized in `BeginBlock` via `setDeliverState`, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by [`BeginBlock`](../core/baseapp.md#beginblock), [`DeliverTx`](../core/baseapp.md#delivertx) and [`EndBlock`](../core/baseapp.md#endblock). At the beginning of each `DeliverTx`, the main gas meter **must be set to 0** in the [`AnteHandler`](#antehandler), so that it can track gas comsumption per-transaction. +`ctx.GasMeter()` is the main gas meter of the application. The main gas meter is initialized in `BeginBlock` via `setDeliverState`, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by [`BeginBlock`](../core/baseapp.md#beginblock), [`DeliverTx`](../core/baseapp.md#delivertx) and [`EndBlock`](../core/baseapp.md#endblock). At the beginning of each `DeliverTx`, the main gas meter **must be set to 0** in the [`AnteHandler`](#antehandler), so that it can track gas consumption per-transaction. -Gas comsumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`handler`](../building-modules/handler.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../core/store.md#gaskv-store). +Gas consumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`Msg` service](../building-modules/msg-services.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../core/store.md#gaskv-store). ### Block Gas Meter `ctx.BlockGasMeter()` is the gas meter used to track gas consumption per block and make sure it does not go above a certain limit. A new instance of the `BlockGasMeter` is created each time [`BeginBlock`](../core/baseapp.md#beginblock) is called. The `BlockGasMeter` is finite, and the limit of gas per block is defined in the application's consensus parameters. By default Cosmos SDK applications use the default consensus parameters provided by Tendermint: -+++ https://github.com/tendermint/tendermint/blob/f323c80cb3b78e123ea6238c8e136a30ff749ccc/types/params.go#L65-L72 ++++ https://github.com/tendermint/tendermint/blob/v0.34.0-rc6/types/params.go#L34-L41 When a new [transaction](../core/transactions.md) is being processed via `DeliverTx`, the current value of `BlockGasMeter` is checked to see if it is above the limit. If it is, `DeliverTx` returns immediately. This can happen even with the first transaction in a block, as `BeginBlock` itself can consume gas. If not, the transaction is processed normally. At the end of `DeliverTx`, the gas tracked by `ctx.BlockGasMeter()` is increased by the amount consumed to process the transaction: @@ -62,7 +63,7 @@ ctx.BlockGasMeter().ConsumeGas( ## AnteHandler -The `AnteHandler` is a special `handler` that is run for every transaction during `CheckTx` and `DeliverTx`, before the `handler` of each `message` in the transaction. `AnteHandler`s have a different signature than `handler`s: +The `AnteHandler` is run for every transaction during `CheckTx` and `DeliverTx`, before the `Msg` service of each `Msg` in the transaction. `AnteHandler`s have the following signature: ```go // AnteHandler authenticates transactions, before their internal messages are handled. @@ -70,19 +71,19 @@ The `AnteHandler` is a special `handler` that is run for every transaction durin type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, result Result, abort bool) ``` -The `anteHandler` is not implemented in the core SDK but in a module. This gives the possibility to developers to choose which version of `AnteHandler` fits their application's needs. That said, most applications today use the default implementation defined in the [`auth` module](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth). Here is what the `anteHandler` is intended to do in a normal Cosmos SDK application: +The `anteHandler` is not implemented in the core SDK but in a module. This gives the possibility to developers to choose which version of `AnteHandler` fits their application's needs. That said, most applications today use the default implementation defined in the [`auth` module](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth). Here is what the `anteHandler` is intended to do in a normal Cosmos SDK application: - Verify that the transaction are of the correct type. Transaction types are defined in the module that implements the `anteHandler`, and they follow the transaction interface: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L33-L41 -This enables developers to play with various types for the transaction of their application. In the default `auth` module, the standard transaction type is `StdTx`: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/stdtx.go#L22-L29 -- Verify signatures for each [`message`](../building-modules/messages-and-queries.md#messages) contained in the transaction. Each `message` should be signed by one or multiple sender(s), and these signatures must be verified in the `anteHandler`. -- During `CheckTx`, verify that the gas prices provided with the transaction is greater than the local `min-gas-prices` (as a reminder, gas-prices can be deducted from the following equation: `fees = gas * gas-prices`). `min-gas-prices` is a parameter local to each full-node and used during `CheckTx` to discard transactions that do not provide a minimum amount of fees. This ensure that the mempool cannot be spammed with garbage transactions. -- Verify that the sender of the transaction has enough funds to cover for the `fees`. When the end-user generates a transaction, they must indicate 2 of the 3 following parameters (the third one being implicit): `fees`, `gas` and `gas-prices`. This signals how much they are willing to pay for nodes to execute their transaction. The provided `gas` value is stored in a parameter called `GasWanted` for later use. -- Set `newCtx.GasMeter` to 0, with a limit of `GasWanted`. **This step is extremely important**, as it not only makes sure the transaction cannot consume infinite gas, but also that `ctx.GasMeter` is reset in-between each `DeliverTx` (`ctx` is set to `newCtx` after `anteHandler` is run, and the `anteHandler` is run each time `DeliverTx` is called). + +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/tx_msg.go#L49-L57 + This enables developers to play with various types for the transaction of their application. In the default `auth` module, the default transaction type is `Tx`: + +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L12-L25 +- Verify signatures for each [`message`](../building-modules/messages-and-queries.md#messages) contained in the transaction. Each `message` should be signed by one or multiple sender(s), and these signatures must be verified in the `anteHandler`. +- During `CheckTx`, verify that the gas prices provided with the transaction is greater than the local `min-gas-prices` (as a reminder, gas-prices can be deducted from the following equation: `fees = gas * gas-prices`). `min-gas-prices` is a parameter local to each full-node and used during `CheckTx` to discard transactions that do not provide a minimum amount of fees. This ensure that the mempool cannot be spammed with garbage transactions. +- Verify that the sender of the transaction has enough funds to cover for the `fees`. When the end-user generates a transaction, they must indicate 2 of the 3 following parameters (the third one being implicit): `fees`, `gas` and `gas-prices`. This signals how much they are willing to pay for nodes to execute their transaction. The provided `gas` value is stored in a parameter called `GasWanted` for later use. +- Set `newCtx.GasMeter` to 0, with a limit of `GasWanted`. **This step is extremely important**, as it not only makes sure the transaction cannot consume infinite gas, but also that `ctx.GasMeter` is reset in-between each `DeliverTx` (`ctx` is set to `newCtx` after `anteHandler` is run, and the `anteHandler` is run each time `DeliverTx` is called). -As explained above, the `anteHandler` returns a maximum limit of `gas` the transaction can consume during execution called `GasWanted`. The actual amount consumed in the end is denominated `GasUsed`, and we must therefore have `GasUsed =< GasWanted`. Both `GasWanted` and `GasUsed` are relayed to the underlying consensus engine when [`DeliverTx`](../core/baseapp.md#delivertx) returns. +As explained above, the `anteHandler` returns a maximum limit of `gas` the transaction can consume during execution called `GasWanted`. The actual amount consumed in the end is denominated `GasUsed`, and we must therefore have `GasUsed =< GasWanted`. Both `GasWanted` and `GasUsed` are relayed to the underlying consensus engine when [`DeliverTx`](../core/baseapp.md#delivertx) returns. ## Next {hide} -Learn about [baseapp](../core/baseapp.md) {hide} \ No newline at end of file +Learn about [baseapp](../core/baseapp.md) {hide} diff --git a/docs/basics/query-lifecycle.md b/docs/basics/query-lifecycle.md new file mode 100644 index 000000000000..55902e698d3c --- /dev/null +++ b/docs/basics/query-lifecycle.md @@ -0,0 +1,152 @@ + + +# 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 + +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + +Another interface through which users can make queries, introduced in Cosmos SDK v0.40, is [gRPC](https://grpc.io) requests to a [gRPC server](../core/grpc_rest.md#grpc-server). The endpoints are defined as [Protocol Buffers](https://developers.google.com/protocol-buffers) service methods inside `.proto` files, written in Protobuf's own language-agnostic interface definition language (IDL). The Protobuf ecosystem developed tools for code-generation from `*.proto` files into various languages. These tools allow to build gRPC clients easily. + +One such tool is [grpcurl](https://github.com/fullstorydev/grpcurl), and a gRPC request for `MyQuery` using this client looks like: + +```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/basics/tx-lifecycle.md b/docs/basics/tx-lifecycle.md index 5d99c290e376..daed33f823b2 100644 --- a/docs/basics/tx-lifecycle.md +++ b/docs/basics/tx-lifecycle.md @@ -1,11 +1,12 @@ # Transaction Lifecycle -## Pre-requisite Readings {hide} +This document describes the lifecycle of a transaction from creation to committed state changes. Transaction definition is described in a [different doc](../core/transactions.md). The transaction will be referred to as `Tx`. {synopsis} + +### Pre-requisite Readings - [Anatomy of an SDK Application](./app-anatomy.md) {prereq} @@ -27,10 +28,11 @@ There are several required and optional flags for transaction creation. The `--f Additionally, there are several [flags](../interfaces/cli.md) users can use to indicate how much they are willing to pay in [fees](./gas-fees.md): -* `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. -* `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. -* `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. -* `--fees` specifies how much in fees the user is willing to pay in total. +- `--gas` refers to how much [gas](./gas-fees.md), which represents computational resources, `Tx` consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing `auto` as the value for `--gas`. +- `--gas-adjustment` (optional) can be used to scale `gas` up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. +- `--gas-prices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, `--gas-prices=0.025uatom, 0.025upho` means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. +- `--fees` specifies how much in fees the user is willing to pay in total. +- `--timeout-height` specifies a block timeout height to prevent the tx from being committed past a certain height. The ultimate value of the fees paid is equal to the gas multiplied by the gas prices. In other words, `fees = ceil(gas * gasPrices)`. Thus, since fees can be calculated using gas prices and vice versa, the users specify only one of the two. @@ -41,7 +43,7 @@ Later, validators decide whether or not to include the transaction in their bloc Users of application `app` can enter the following command into their CLI to generate a transaction to send 1000uatom from a `senderAddress` to a `recipientAddress`. It specifies how much gas they are willing to pay: an automatic estimate scaled up by 1.5 times, with a gas price of 0.025uatom per unit gas. ```bash -appcli tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom +appd tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom ``` #### Other Transaction Creation Methods @@ -59,11 +61,11 @@ Each full-node (running Tendermint) that receives a `Tx` sends an [ABCI message] The full-nodes perform stateless, then stateful checks on `Tx` during `CheckTx`, with the goal to identify and reject an invalid transaction as early on as possible to avoid wasted computation. -***Stateless*** checks do not require nodes to access state - light clients or offline nodes can do +**_Stateless_** checks do not require nodes to access state - light clients or offline nodes can do them - and are thus less computationally expensive. Stateless checks include making sure addresses are not empty, enforcing nonnegative numbers, and other logic specified in the definitions. -***Stateful*** checks validate transactions and messages based on a committed state. Examples +**_Stateful_** checks validate transactions and messages based on a committed state. Examples include checking that the relevant values exist and are able to be transacted with, the address has sufficient funds, and the sender is authorized or has the correct ownership to transact. At any given moment, full-nodes typically have [multiple versions](../core/baseapp.md#volatile-states) @@ -81,17 +83,19 @@ When `Tx` is received by the application from the underlying consensus engine (e ### ValidateBasic -[`Message`s](../core/transactions.md#messages) are extracted from `Tx` and `ValidateBasic`, a method of the `Msg` interface implemented by the module developer, is run for each one. It should include basic **stateless** sanity checks. For example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for nonempty addresses and a nonnegative coin amount, but does not require knowledge of state such as account balance of an address. +[`Msg`s](../core/transactions.md#messages) are extracted from `Tx` and `ValidateBasic`, a method of the `Msg` interface implemented by the module developer, is run for each one. It should include basic **stateless** sanity checks. For example, if the message is to send coins from one address to another, `ValidateBasic` likely checks for nonempty addresses and a nonnegative coin amount, but does not require knowledge of state such as account balance of an address. ### AnteHandler -The [`AnteHandler`](../basics/gas-fees.md#antehandler), which is technically optional but should be defined for each application, is run. A deep copy of the internal state, `checkState`, is made and the defined `AnteHandler` performs limited checks specified for the transaction type. Using a copy allows the handler to do stateful checks for `Tx` without modifying the last committed state, and revert back to the original if the execution fails. +After the ValidateBasic checks, the `AnteHandler`s are run. Technically, they are optional, but in practice, they are very often present to perform signature verification, gas calculation, fee deduction and other core operations related to blockchain transactions. + +A copy of the cached context is provided to the `AnteHandler`, which performs limited checks specified for the transaction type. Using a copy allows the AnteHandler to do stateful checks for `Tx` without modifying the last committed state, and revert back to the original if the execution fails. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module `AnteHandler` checks and increments sequence numbers, checks signatures and account numbers, and deducts fees from the first signer of the transaction - all state changes are made using the `checkState`. ### Gas -The [`Context`](../core/context.md), which keeps a `GasMeter` that will track how much gas has been used during the execution of `Tx`, is initialized. The user-provided amount of gas for `Tx` is known as `GasWanted`. If `GasConsumed`, the amount of gas consumed so during execution, ever exceeds `GasWanted`, the execution will stop and the changes made to the cacehd copy of the state won't be committed. Otherwise, `CheckTx` sets `GasUsed` equal to `GasConsumed` and returns it in the result. After calculating the gas and fee values, validator-nodes check that the user-specified `gas-prices` is less than their locally defined `min-gas-prices`. +The [`Context`](../core/context.md), which keeps a `GasMeter` that will track how much gas has been used during the execution of `Tx`, is initialized. The user-provided amount of gas for `Tx` is known as `GasWanted`. If `GasConsumed`, the amount of gas consumed so during execution, ever exceeds `GasWanted`, the execution will stop and the changes made to the cached copy of the state won't be committed. Otherwise, `CheckTx` sets `GasUsed` equal to `GasConsumed` and returns it in the result. After calculating the gas and fee values, validator-nodes check that the user-specified `gas-prices` is less than their locally defined `min-gas-prices`. ### Discard or Addition to Mempool @@ -139,80 +143,77 @@ locally, this process yields a single, unambiguous result, since the messages' s explicitly ordered in the block proposal. ``` - ----------------------- + ----------------------- |Receive Block Proposal| - ----------------------- - | + ----------------------- + | + v + ----------------------- + | BeginBlock | + ----------------------- + | v - ----------------------- - | BeginBlock | - ----------------------- - | - v - ----------------------- - | DeliverTx(tx0) | - | DeliverTx(tx1) | - | DeliverTx(tx2) | - | DeliverTx(tx3) | - | . | + ----------------------- + | DeliverTx(tx0) | + | DeliverTx(tx1) | + | DeliverTx(tx2) | + | DeliverTx(tx3) | + | . | | . | | . | - ----------------------- - | - v ----------------------- - | EndBlock | + | + v + ----------------------- + | EndBlock | ----------------------- - | - v + | + v ----------------------- - | Consensus | + | Consensus | ----------------------- - | - v + | + v ----------------------- - | Commit | + | Commit | ----------------------- ``` ### DeliverTx -The `DeliverTx` ABCI function defined in [`baseapp`](../core/baseapp.md) does the bulk of the +The `DeliverTx` ABCI function defined in [`BaseApp`](../core/baseapp.md) does the bulk of the state transitions: it is run for each transaction in the block in sequential order as committed to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx` but calls the [`runTx`](../core/baseapp.md#runtx) function in deliver mode instead of check mode. Instead of using their `checkState`, full-nodes use `deliverState`: -* **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. -Nodes first unmarshal the transaction, then call `runTx` in `runTxModeDeliver`, which is very -similar to `CheckTx` but also executes and writes state changes. +- **Decoding:** Since `DeliverTx` is an ABCI call, `Tx` is received in the encoded `[]byte` form. + Nodes first unmarshal the transaction, using the [`TxConfig`](./app-anatomy#register-codec) defined in the app, then call `runTx` in `runTxModeDeliver`, which is very similar to `CheckTx` but also executes and writes state changes. -* **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check -happens because they may not have seen the same transactions during the addition to Mempool stage\ -and a malicious proposer may have included invalid ones. One difference here is that the -`AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local -to each node - differing values across nodes would yield nondeterministic results. +- **Checks:** Full-nodes call `validateBasicMsgs` and the `AnteHandler` again. This second check + happens because they may not have seen the same transactions during the addition to Mempool stage\ + and a malicious proposer may have included invalid ones. One difference here is that the + `AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local + to each node - differing values across nodes would yield nondeterministic results. -* **Route and Handler:** While `CheckTx` would have exited, `DeliverTx` continues to run -[`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction. -Since the transaction may have messages from different modules, `baseapp` needs to know which module -to find the appropriate Handler. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to -retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module. +- **`MsgServiceRouter`:** While `CheckTx` would have exited, `DeliverTx` continues to run + [`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction. + Since the transaction may have messages from different modules, `BaseApp` needs to know which module + to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](../building-modules/msg-services.md). + For legacy `Msg` routing, the `Route` function is called via the [module manager](../building-modules/module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/msg-services.md#handler-type) within the module. -* **Handler:** The `handler`, a step up from `AnteHandler`, is responsible for executing each -message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined -within a `Msg`'s module and writes to the appropriate stores within the module. +- **`Msg` service:** The `Msg` service, a step up from `AnteHandler`, is responsible for executing each + message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined + within a module `Msg` protobuf service and writes to the appropriate stores within the module. -* **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much -gas is being used; if execution completes, `GasUsed` is set and returned in the -`abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes -wrong, a deferred function at the end appropriately errors or panics. +- **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much + gas is being used; if execution completes, `GasUsed` is set and returned in the + `abci.ResponseDeliverTx`. If execution halts because `BlockGasMeter` or `GasMeter` has run out or something else goes + wrong, a deferred function at the end appropriately errors or panics. If there are any failed state changes resulting from a `Tx` being invalid or `GasMeter` running out, the transaction processing terminates and any state changes are reverted. Invalid transactions in a -block proposal cause validator nodes to reject the block and vote for a `nil` block instead. If a -`Tx` is delivered successfully, any leftover gas is returned to the user and the transaction is -validated. +block proposal cause validator nodes to reject the block and vote for a `nil` block instead. ### Commit @@ -220,14 +221,14 @@ The final step is for nodes to commit the block and state changes. Validator nod perform the previous step of executing state transitions in order to validate the transactions, then sign the block to confirm it. Full nodes that are not validators do not participate in consensus - i.e. they cannot vote - but listen for votes to understand whether or -not they should commit the state changes. +not they should commit the state changes. -When they receive enough validator votes (2/3+ *precommits* weighted by voting power), full nodes commit to a new block to be added to the blockchain and +When they receive enough validator votes (2/3+ _precommits_ weighted by voting power), full nodes commit to a new block to be added to the blockchain and finalize the state transitions in the application layer. A new state root is generated to serve as a merkle proof for the state transitions. Applications use the [`Commit`](../core/baseapp.md#commit) ABCI method inherited from [Baseapp](../core/baseapp.md); it syncs all the state transitions by writing the `deliverState` into the application's internal state. As soon as the state changes are -committed, `checkState` start afresh from the most recently committed state and `deliverState` +committed, `checkState` start afresh from the most recently committed state and `deliverState` resets to `nil` in order to be consistent and reflect the changes. Note that not all blocks have the same number of transactions and it is possible for consensus to diff --git a/docs/building-modules/README.md b/docs/building-modules/README.md index f23a129e7bb4..22f3ffb75e20 100644 --- a/docs/building-modules/README.md +++ b/docs/building-modules/README.md @@ -1,7 +1,7 @@ # Building Modules @@ -11,11 +11,12 @@ This repository contains documentation on concepts developers need to know in or 1. [Introduction to Cosmos SDK Modules](./intro.md) 2. [`AppModule` Interface and Module Manager](./module-manager.md) 3. [Messages and Queries](./messages-and-queries.md) -4. [`Handler`s - Processing Messages](./handler.md) -5. [`Querier`s - Processing Queries](./querier.md) +4. [`Msg` services - Processing Messages](./msg-services.md) +5. [Query Services - Processing Queries](./query-services.md) 6. [BeginBlocker and EndBlocker](./beginblock-endblock.md) 7. [`Keeper`s](./keeper.md) 8. [Invariants](./invariants.md) 9. [Genesis](./genesis.md) 10. [Module Interfaces](./module-interfaces.md) 11. [Standard Module Structure](./structure.md) +12. [Errors](./errors.md) diff --git a/docs/building-modules/beginblock-endblock.md b/docs/building-modules/beginblock-endblock.md index 578a0f733828..cb8dbba1dda2 100644 --- a/docs/building-modules/beginblock-endblock.md +++ b/docs/building-modules/beginblock-endblock.md @@ -1,11 +1,12 @@ # BeginBlocker and EndBlocker -## Pre-requisite Readings {hide} +`BeginBlocker` and `EndBlocker` are optional methods module developers can implement in their module. They will be triggered at the beginning and at the end of each block respectively, when the [`BeginBlock`](../core/baseapp.md#beginblock) and [`EndBlock`](../core/baseapp.md#endblock) ABCI messages are received from the underlying consensus engine. {synopsis} + +## Pre-requisite Readings - [Module Manager](./module-manager.md) {prereq} @@ -15,7 +16,7 @@ synopsis: "`BeginBlocker` and `EndBlocker` are optional methods module developer When needed, `BeginBlocker` and `EndBlocker` are implemented as part of the [`AppModule` interface](./module-manager.md#appmodule). The `BeginBlock` and `EndBlock` methods of the interface implemented in `module.go` generally defer to `BeginBlocker` and `EndBlocker` methods respectively, which are usually implemented in a **`abci.go`** file. -The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are very similar to that of a [`handler`](./handler.md): +The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are very similar to that of a [`Msg` service](./msg-services.md): - They generally use the [`keeper`](./keeper.md) and [`ctx`](../core/context.md) to retrieve information about the latest state. - If needed, they use the `keeper` and `ctx` to trigger state-transitions. @@ -23,15 +24,15 @@ 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: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/distribution/abci.go#L10-L32 ++++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/distribution/abci.go#L14-L38 and an example implementation of `EndBlocker` from the `staking` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/staking/handler.go#L44-L96 ++++ https://github.com/cosmos/cosmos-sdk/blob/f33749263f4ecc796115ad6e789cb0f7cddf9148/x/staking/abci.go#L22-L27 ## Next {hide} diff --git a/docs/building-modules/errors.md b/docs/building-modules/errors.md new file mode 100644 index 000000000000..3b6d90535e45 --- /dev/null +++ b/docs/building-modules/errors.md @@ -0,0 +1,54 @@ + + +# Errors + +This document outlines the recommended usage and APIs for error handling in Cosmos SDK modules. {synopsis} + +Modules are encouraged to define and register their own errors to provide better +context on failed message or handler execution. Typically, these errors should be +common or general errors which can be further wrapped to provide additional specific +execution context. + +## Registration + +Modules should define and register their custom errors in `x/{module}/types/errors.go`. Registration +of errors is handled via the `types/errors` package. + +Example: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.38.1/x/distribution/types/errors.go#L1-L21 + +Each custom module error must provide the codespace, which is typically the module name +(e.g. "distribution") and is unique per module, and a uint32 code. Together, the codespace and code +provide a globally unique SDK error. Typically, the code is monotonically increasing but does not +necessarily have to be. The only restrictions on error codes are the following: + +* Must be greater than one, as a code value of one is reserved for internal errors. +* Must be unique within the module. + +Note, the SDK provides a core set of *common* errors. These errors are defined in `types/errors/errors.go`. + +## Wrapping + +The custom module errors can be returned as their concrete type as they already fulfill the `error` +interface. However, module errors can be wrapped to provide further context and meaning to failed +execution. + +Example: + ++++ https://github.com/cosmos/cosmos-sdk/blob/b2d48a9e815fe534a7faeec6ca2adb0874252b81/x/bank/keeper/keeper.go#L85-L122 + +Regardless if an error is wrapped or not, the SDK's `errors` package provides an API to determine if +an error is of a particular kind via `Is`. + +## ABCI + +If a module error is registered, the SDK `errors` package allows ABCI information to be extracted +through the `ABCIInfo` API. The package also provides `ResponseCheckTx` and `ResponseDeliverTx` as +auxiliary APIs to automatically get `CheckTx` and `DeliverTx` responses from an error. + +## Next {hide} + +Learn about [interfaces](../interfaces/interfaces-intro.md) {hide} diff --git a/docs/building-modules/genesis.md b/docs/building-modules/genesis.md index 8cb8815b7ce8..7314442969a2 100644 --- a/docs/building-modules/genesis.md +++ b/docs/building-modules/genesis.md @@ -1,36 +1,37 @@ # Module Genesis -## Pre-requisite Readings {hide} +Modules generally handle a subset of the state and, as such, they need to define the related subset of the genesis file as well as methods to initialize, verify and export it. {synopsis} + +## Pre-requisite Readings - [Module Manager](./module-manager.md) {prereq} - [Keepers](./keeper.md) {prereq} ## Type Definition -The subset of the genesis state defined from a given module is generally defined in a `./internal/types/genesis.go` file, along with the `DefaultGenesis` and `ValidateGenesis` methods. The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. +The subset of the genesis state defined from a given module is generally defined in a `genesis.proto` file ([more info](../core/encoding.md#gogoproto) on how to define protobuf messages). The struct defining the module's subset of the genesis state is usually called `GenesisState` and contains all the module-related values that need to be initialized during the genesis process. -See an example of `GenesisState` type definition from the nameservice tutorial +See an example of `GenesisState` protobuf message definition from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L10-L12 ++++ https://github.com/cosmos/cosmos-sdk/blob/a9547b54ffac9729fe1393651126ddfc0d236cff/proto/cosmos/auth/v1beta1/genesis.proto Next we present the main genesis-related methods that need to be implemented by module developers in order for their module to be used in Cosmos SDK applications. ### `DefaultGenesis` -The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `nameservice` module: +The `DefaultGenesis()` method is a simple method that calls the constructor function for `GenesisState` with the default value for each parameter. See an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L33-L37 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L48-L52 ### `ValidateGenesis` -The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `nameservice` module: +The `ValidateGenesis(genesisState GenesisState)` method is called to verify that the provided `genesisState` is correct. It should perform validity checks on each of the parameter listed in `GenesisState`. See an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L18-L31 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/types/genesis.go#L57-L70 ## Other Genesis Methods @@ -42,18 +43,18 @@ The `InitGenesis` method is executed during [`InitChain`](../core/baseapp.md#ini The [module manager](./module-manager.md#manager) of the application is responsible for calling the `InitGenesis` method of each of the application's modules, in order. This order is set by the application developer via the manager's `SetOrderGenesisMethod`, which is called in the [application's constructor function](../basics/app-anatomy.md#constructor-function) -See an example of `InitGenesis` from the nameservice tutorial +See an example of `InitGenesis` from the `auth` module -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L39-L44 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L13-L28 ### `ExportGenesis` The `ExportGenesis` method is executed whenever an export of the state is made. It takes the latest known version of the subset of the state managed by the module and creates a new `GenesisState` out of it. This is mainly used when the chain needs to be upgraded via a hard fork. -See an example of `ExportGenesis` from the nameservice tutorial. +See an example of `ExportGenesis` from the `auth` module. -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/genesis.go#L46-L57 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/genesis.go#L31-L42 ## Next {hide} -Learn about [modules interfaces](#module-interfaces.md) {hide} \ No newline at end of file +Learn about [modules interfaces](module-interfaces.md) {hide} \ No newline at end of file diff --git a/docs/building-modules/handler.md b/docs/building-modules/handler.md deleted file mode 100644 index 37b46511a09a..000000000000 --- a/docs/building-modules/handler.md +++ /dev/null @@ -1,81 +0,0 @@ - - -# Handlers - -## Pre-requisite Readings {hide} - -- [Module Manager](./module-manager.md) {prereq} -- [Messages and Queries](./messages-and-queries.md) {prereq} - -## `handler` type - -The `handler` type defined in the Cosmos SDK specifies the typical structure of a `handler` function. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/handler.go#L4 - -Let us break it down: - -- The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed. -- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state. -- The [`Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler`, [`gas`](../basics/gas-fees.md) consumption and [`events`](../core/events.md). - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 - -## Implementation of a module `handler`s - -Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The -[module manager](./module-manager.md) is used to add the module's `handler`s to the -[application's `router`](../core/baseapp.md#message-routing) via the `NewHandler()` method. Typically, -the manager's `NewHandler()` method simply calls a `NewHandler()` method defined in `handler.go`, -which looks like the following: - -```go -func NewHandler(keeper Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - switch msg := msg.(type) { - case MsgType1: - return handleMsgType1(ctx, keeper, msg) - - case MsgType2: - return handleMsgType2(ctx, keeper, msg) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) - } - } -} -``` - -This simple switch returns a `handler` function specific to the type of the received `message`. These `handler` functions are the ones that actually process `message`s, and usually follow the following 2 steps: - -- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `handler` can be more expensive and require access to the state. For example, a `handler` for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `handler` needs to call the [`keeper`'s](./keeper.md) getter functions. -- Then, if the checks are successfull, the `handler` calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition. - -Before returning, `handler` functions generally emit one or multiple [`events`](../core/events.md) via the `EventManager` held in the `ctx`: - -```go -ctx.EventManager().EmitEvent( - sdk.NewEvent( - eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module - sdk.NewAttribute(attributeKey, attributeValue), - ), - ) -``` - -These `events` are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about `events`. - -Finally, the `handler` function returns a `sdk.Result` which contains the aforementioned `events` and an optional `Data` field. - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 - -Next is an example of how to return a `Result` from the `gov` module: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/gov/handler.go#L59-L62 - -For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/handler.go) from the nameservice tutorial. - -## Next {hide} - -Learn about [queriers](./querier.md) {hide} diff --git a/docs/building-modules/intro.md b/docs/building-modules/intro.md index 1d125b51b8a7..9bec674acc55 100644 --- a/docs/building-modules/intro.md +++ b/docs/building-modules/intro.md @@ -1,22 +1,23 @@ # Introduction to SDK Modules -## Pre-requisite Readings {hide} +Modules define most of the logic of SDK applications. Developers compose module together using the Cosmos SDK to build their custom application-specific blockchains. This document outlines the basic concepts behind SDK modules and how to approach module management. {synopsis} + +## Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} - [Lifecycle of an SDK transaction](../basics/tx-lifecycle.md) {prereq} ## Role of Modules in an SDK Application -The Cosmos SDK can be thought as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../core/baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../core/store.md#multistore) to persist state, a [server](../core/node.md) to form a full-node and [interfaces](../interfaces/interfaces-intro.md) to handle queries. +The Cosmos SDK can be thought as the Ruby-on-Rails of blockchain development. It comes with a core that provides the basic functionalities every blockchain application needs, like a [boilerplate implementation of the ABCI](../core/baseapp.md) to communicate with the underlying consensus engine, a [`multistore`](../core/store.md#multistore) to persist state, a [server](../core/node.md) to form a full-node and [interfaces](./module-interfaces.md) to handle queries. On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source SDK modules, making it increasingly easier to build complex blockchain applications. -SDK Modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one ore multiple `KVStore` in the [main multistore](../core/store.md), as well as a subset of [`message` types](./messages-and-queries.md#messages). These `message`s are routed by one of the main component of SDK core, [`baseapp`](../core/baseapp.md), to the [`handler`](./handler.md) of the module that define them. +SDK Modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../core/store.md), as well as a subset of [message types](./messages-and-queries.md#messages). These messages are routed by one of the main component of SDK core, [`BaseApp`](../core/baseapp.md), to the [`Msg` service](./msg-services.md) of the module that define them. ``` + @@ -67,20 +68,19 @@ As a result of this architecture, building an SDK application usually revolves a ## How to Approach Building Modules as a Developer -While there is no definitive guidelines for writing modules, here are some important design principles developers should keep in mind when building them: +While there are no definitive guidelines for writing modules, here are some important design principles developers should keep in mind when building them: - **Composability**: SDK applications are almost always composed of multiple modules. This means developers need to carefully consider the integration of their module not only with the core of the Cosmos SDK, but also with other modules. The former is achieved by following standard design patterns outlined [here](#main-components-of-sdk-modules), while the latter is achieved by properly exposing the store(s) of the module via the [`keeper`](./keeper.md). - **Specialization**: A direct consequence of the **composability** feature is that modules should be **specialized**. Developers should carefully establish the scope of their module and not batch multiple functionalities into the same module. This separation of concern enables modules to be re-used in other projects and improves the upgradability of the application. **Specialization** also plays an important role in the [object-capabilities model](../core/ocap.md) of the Cosmos SDK. -- **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some module to be malicious. That is why module developers need to carefully think not only about how their module interracts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`. +- **Capabilities**: Most modules need to read and/or write to the store(s) of other modules. However, in an open-source environment, it is possible for some module to be malicious. That is why module developers need to carefully think not only about how their module interacts with other modules, but also about how to give access to the module's store(s). The Cosmos SDK takes a capabilities-oriented approach to inter-module security. This means that each store defined by a module is accessed by a `key`, which is held by the module's [`keeper`](./keeper.md). This `keeper` defines how to access the store(s) and under what conditions. Access to the module's store(s) is done by passing a reference to the module's `keeper`. ## Main Components of SDK Modules -Modules are by convention defined in the `.x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components: +Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components: -- Custom [`message` types](./messages-and-queries.md#messages) to trigger state-transitions. -- A [`handler`](./handler.md) used to process messages when they are routed to the module by [`baseapp`](../core/baseapp.md#message-routing). -- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state. -- A [`querier`](./querier.md), used to process user queries when they are routed to the module by [`baseapp`](../core/baseapp.md#query-routing). +- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state. +- A [`Msg` service](./messages-and-queries.md#messages) used to process messages when they are routed to the module by [`BaseApp`](../core/baseapp.md#message-routing) and trigger state-transitions. +- A [query service](./query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../core/baseapp.md#query-routing). - Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module. In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./module-manager.md). @@ -90,4 +90,3 @@ Please refer to the [structure document](./structure.md) to learn about the reco ## Next {hide} Read more on the [`AppModule` interface and the `module manager`](./module-manager.md) {hide} - diff --git a/docs/building-modules/invariants.md b/docs/building-modules/invariants.md index 64b8e4696dc9..32fd30591b07 100644 --- a/docs/building-modules/invariants.md +++ b/docs/building-modules/invariants.md @@ -1,11 +1,12 @@ # Invariants -## Pre-requisite Readings {hide} +An invariant is a property of the application that should always be true. In the context of the Cosmos SDK, an `Invariant` is a function that checks for a particular invariant. These functions are useful to detect bugs early on and act upon them to limit their potential consequences (e.g. by halting the chain). They are also useful in the development process of the application to detect bugs via simulations. {synopsis} + +## Pre-requisite Readings - [Keepers](./keeper.md) {prereq} @@ -17,7 +18,7 @@ An `Invariant` is a function that checks for a particular invariant within a mod where the `string` return value is the invariant message, which can be used when printing logs, and the `bool` return value is the actual result of the invariant check. -In practice, each module implements `Invariant`s in a `./internal/keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: +In practice, each module implements `Invariant`s in a `./keeper/invariants.go` file within the module's folder. The standard is to implement one `Invariant` function per logical grouping of invariants with the following model: ```go // Example for an Invariant that checks balance-related invariants @@ -73,7 +74,7 @@ At its core, the `InvariantRegistry` is defined in the SDK as an interface: Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/crisis/internal/keeper/keeper.go#L45-L49 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/crisis/keeper/keeper.go#L50-L54 The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../basics/app-anatomy.md#constructor-function). diff --git a/docs/building-modules/keeper.md b/docs/building-modules/keeper.md index a9132bae041c..e84abfb01fd7 100644 --- a/docs/building-modules/keeper.md +++ b/docs/building-modules/keeper.md @@ -1,11 +1,12 @@ # Keepers -## Pre-requisite Readings {hide} +`Keeper`s refer to a Cosmos SDK abstraction whose role is to manage access to the subset of the state defined by various modules. `Keeper`s are module-specific, i.e. the subset of state defined by a module can only be accessed by a `keeper` defined in said module. If a module needs to access the subset of state defined by another module, a reference to the second module's internal `keeper` needs to be passed to the first one. This is done in `app.go` during the instantiation of module keepers. {synopsis} + +## Pre-requisite Readings - [Introduction to SDK Modules](./intro.md) {prereq} @@ -19,7 +20,7 @@ The core idea behind the object-capabilities approach is to only reveal what is ## Type Definition -`keeper`s are generally implemented in a `internal/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: +`keeper`s are generally implemented in a `/keeper/keeper.go` file located in the module's folder. By convention, the type `keeper` of a module is simply named `Keeper` and usually follows the following structure: ```go type Keeper struct { @@ -31,21 +32,21 @@ type Keeper struct { } ``` -For example, here is the [type definition of the `keeper` from the nameservice tutorial: +For example, here is the type definition of the `keeper` from the `staking` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/keeper.go#L10-L17 ++++ https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go#L23-L33 Let us go through the different parameters: -- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `internal/types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. +- An expected `keeper` is a `keeper` external to a module that is required by the internal `keeper` of said module. External `keeper`s are listed in the internal `keeper`'s type definition as interfaces. These interfaces are themselves defined in a `types/expected_keepers.go` file within the module's folder. In this context, interfaces are used to reduce the number of dependencies, as well as to facilitate the maintenance of the module itself. - `storeKey`s grant access to the store(s) of the [multistore](../core/store.md) managed by the module. They should always remain unexposed to external modules. -- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`. +- A [codec `cdc`](../core/encoding.md), used to marshall and unmarshall struct to/from `[]byte`, that can be any of `codec.BinaryMarshaler`,`codec.JSONMarshaler` or `codec.Marshaler` based on your requirements. It can be either a proto or amino codec as long as they implement these interfaces. Of course, it is possible to define different types of internal `keeper`s for the same module (e.g. a read-only `keeper`). Each type of `keeper` comes with its own constructor function, which is called from the [application's constructor function](../basics/app-anatomy.md). This is where `keeper`s are instantiated, and where developers make sure to pass correct instances of modules' `keeper`s to other modules that require it. ## Implementing Methods -`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./messages-and-queries.md#messages) and the [`handler`](./handler.md) when `keeper`s' methods are called. +`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./messages-and-queries.md#messages) and the [`Msg` server](./msg-services.md) when `keeper`s' methods are called. Typically, a *getter* method will present with the following signature @@ -55,7 +56,7 @@ func (k Keeper) Get(ctx sdk.Context, key string) returnType and go through the following steps: -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`. +1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. 2. If it exists, get the `[]byte` value stored at location `[]byte(key)` using the `Get(key []byte)` method of the store. 3. Unmarshall the retrieved value from `[]byte` to `returnType` using the codec `cdc`. Return the value. @@ -67,11 +68,17 @@ func (k Keeper) Set(ctx sdk.Context, key string, value valueType) and go through the following steps: -1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey` method of the `ctx`. -2. Marhsall `value` to `[]byte` using the codec `cdc`. +1. Retrieve the appropriate store from the `ctx` using the `storeKey`. This is done through the `KVStore(storeKey sdk.StoreKey)` method of the `ctx`. Then it's prefered to use the `prefix.Store` to access only the desired limited subset of the store for convenience and safety. +2. Marshal `value` to `[]byte` using the codec `cdc`. 3. Set the encoded value in the store at location `key` using the `Set(key []byte, value []byte)` method of the store. -For more, see an example of `keeper`'s [methods implementation from the nameservice tutorial](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/keeper.go). +For more, see an example of `keeper`'s [methods implementation from the `staking` module](https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/staking/keeper/keeper.go). + +The [module `KVStore`](../core/store.md#kvstore-and-commitkvstore-interfaces) also provides an `Iterator()` method which returns an `Iterator` object to iterate over a domain of keys. + +This is an example from the `auth` module to iterate accounts: + ++++ https://github.com/cosmos/cosmos-sdk/blob/bf8809ef9840b4f5369887a38d8345e2380a567f/x/auth/keeper/account.go#L70-L83 ## Next {hide} diff --git a/docs/building-modules/messages-and-queries.md b/docs/building-modules/messages-and-queries.md index 3c87cc84eeab..33afa51dbbb6 100644 --- a/docs/building-modules/messages-and-queries.md +++ b/docs/building-modules/messages-and-queries.md @@ -1,53 +1,83 @@ # Messages and Queries -## Pre-requisite Readings {hide} +`Msg`s and `Queries` are the two primary objects handled by modules. Most of the core components defined in a module, like `Msg` services, `keeper`s and `Query` services, exist to process `message`s and `queries`. {synopsis} + +## Pre-requisite Readings - [Introduction to SDK Modules](./intro.md) {prereq} ## Messages -`Message`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or multiple of them. +`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or more of them. -When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`baseapp`](../core/baseapp.md). Then, each `message` contained in the transaction is extracted and routed to the appropriate module via `baseapp`'s `router` so that it can be processed by the module's [`handler`](./handler.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md). +When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`BaseApp`](../core/baseapp.md). Then, each message contained in the transaction is extracted and routed to the appropriate module via `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](./msg-services.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md). -Defining `message`s is the responsibility of module developers. Typically, they are defined in a `./internal/types/msgs.go` file inside the module's folder. The `message`'s type definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said `message`. +### `Msg` Services -```go -// Example of a message type definition +Starting from v0.40, defining Protobuf `Msg` services is the recommended way to handle messages. A `Msg` protobuf service should be created per module, typically in `tx.proto` (see more info about [conventions and naming](../core/encoding.md#faq)). It must have an RPC service method defined for each message in the module. -type MsgSubmitProposal struct { - Content Content `json:"content" yaml:"content"` - InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"` - Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` -} -``` +See an example of a `Msg` service definition from `x/bank` module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L10-L17 + +For backwards compatibility with [legacy Amino `Msg`s](#legacy-amino-msgs), existing `Msg` types should be used as the request parameter for `service` definitions. Newer `Msg` types which only support `service` definitions should use the more canonical `Msg...Request` names. + +`Msg` request types need to implement the `MsgRequest` interface which is a simplified version of the `Msg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods. + +Defining such `Msg` services allow to specify return types as part of `Msg` response using the canonical `Msg...Response` names. + +In addition, this generates client and server code. +The generated `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./msg-services.md) documentation. + +A `RegisterMsgServer` method is also generated and should be used to register the module's `MsgServer` implementation in `RegisterServices` method from the [`AppModule` interface](./module-manager.md#appmodule). + +In order for clients (CLI and grpc-gateway) to have these URLs registered, the SDK provides the function `RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)` that should be called inside module's [`RegisterInterfaces`](module-manager.md#appmodulebasic) method, using the proto-generated `&_Msg_serviceDesc` as `*grpc.ServiceDesc` argument. + +### Legacy Amino `Msg`s + +This way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred. + +Legacy `Msg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message. The `Msg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./module-interfaces.md). `message`s also need to implement the [`Msg`] interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L7-L29 ++++ https://github.com/cosmos/cosmos-sdk/blob/4a1b2fba43b1052ca162b3a1e0b6db6db9c26656/types/tx_msg.go#L10-L33 -It contains the following methods: +It extends `proto.Message` and contains the following methods: - `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name. - `Type() string`: Type of the message, used primarly in [events](../core/events.md). This should return a message-specific `string`, typically the denomination of the message itself. -- `ValidateBasic() Error`: This method is called by `baseapp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer). +- `ValidateBasic() error`: This method is called by `BaseApp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer). - `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature. - `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method. -See an example implementation of a `message` from the `nameservice` module: +See an example implementation of a `message` from the `gov` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/types/msgs.go#L10-L51 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/gov/types/msgs.go#L77-L125 ## Queries -A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `baseapp`'s `queryrouter` so that it can be processed by the module's [`querier`](./querier.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md). +A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md). + +### gRPC Queries + +Starting from v0.40, the prefered way to define queries is by using [Protobuf services](https://developers.google.com/protocol-buffers/docs/proto#services). A `Query` service should be created per module in `query.proto`. This service lists endpoints starting with `rpc`. + +Here's an example of such a `Query` service definition: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/auth/v1beta1/query.proto#L12-L23 + +As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). -Contrary to `message`s, there is usually no specific `query` object defined by module developers. Instead, the SDK takes the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following: +A `RegisterQueryServer` method is also generated and should be used to register the module's query server in the `RegisterServices` method from the [`AppModule` interface](./module-manager.md#appmodule). + +### Legacy Queries + +Before the introduction of Protobuf and gRPC in the SDK, there was usually no specific `query` object defined by module developers, contrary to `message`s. Instead, the SDK took the simpler approach of using a simple `path` to define each `query`. The `path` contains the `query` type and all the arguments needed in order to process it. For most module queries, the `path` should look like the following: ``` queryCategory/queryRoute/queryType/arg1/arg2/... @@ -55,21 +85,27 @@ queryCategory/queryRoute/queryType/arg1/arg2/... where: -- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `baseapp`'s [`Query` method](../core/baseapp.md#query). -- `queryRoute` is used by `baseapp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module. -- `queryType` is used by the module's [`querier`](./querier.md) to map the `query` to the appropriate `querier function` within the module. +- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `BaseApp`'s [`Query` method](../core/baseapp.md#query). +- `queryRoute` is used by `BaseApp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module. +- `queryType` is used by the module's [`querier`](./query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module. - `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`. The `path` for each `query` must be defined by the module developer in the module's [command-line interface file](./module-interfaces.md#query-commands).Overall, there are 3 mains components module developers need to implement in order to make the subset of the state defined by their module queryable: -- A [`querier`](./querier.md), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing). +- A [`querier`](./query-services.md#legacy-queriers), to process the `query` once it has been [routed to the module](../core/baseapp.md#query-routing). - [Query commands](./module-interfaces.md#query-commands) in the module's CLI file, where the `path` for each `query` is specified. -- `query` return types. Typically defined in a file `internal/types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). +- `query` return types. Typically defined in a file `types/querier.go`, they specify the result type of each of the module's `queries`. These custom types must implement the `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer). + +### Store Queries + +Store queries query directly for store keys. They use `clientCtx.QueryABCI(req abci.RequestQuery)` to return the full `abci.ResponseQuery` with inclusion Merkle proofs. + +See following examples: -See an example of `query` return types from the `nameservice` module: ++++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/x/ibc/core/client/query.go#L36-L46 -+++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/types/querier.go#L5-L21 ++++ https://github.com/cosmos/cosmos-sdk/blob/080fcf1df25ccdf97f3029b6b6f83caaf5a235e4/baseapp/abci.go#L722-L749 ## Next {hide} -Learn about [`handler`s](./handler.md) {hide} \ No newline at end of file +Learn about [`Msg` services](./msg-services.md) {hide} diff --git a/docs/building-modules/module-interfaces.md b/docs/building-modules/module-interfaces.md index bfb820d75e40..0f811db4bf75 100644 --- a/docs/building-modules/module-interfaces.md +++ b/docs/building-modules/module-interfaces.md @@ -1,79 +1,77 @@ # Module Interfaces -## Pre-requisite Readings {hide} +This document details how to build CLI and REST interfaces for a module. Examples from various SDK modules are included. {synopsis} -* [Building Modules Intro](./intro.md) {prereq} +## Pre-requisite Readings + +- [Building Modules Intro](./intro.md) {prereq} ## CLI -One of the main interfaces for an application is the [command-line interface](../interfaces/cli.md). This entrypoint adds commands from the application's modules to let end-users create [**messages**](./messages-and-queries.md#messages) and [**queries**](./messages-and-queries.md#queries). The CLI files are typically found in the `./x/moduleName/client/cli` folder. +One of the main interfaces for an application is the [command-line interface](../interfaces/cli.md). This entrypoint adds commands from the application's modules to let end-users create [**messages**](./messages-and-queries.md#messages) and [**queries**](./messages-and-queries.md#queries). The CLI files are typically found in the `./x/moduleName/client/cli` folder. ### Transaction Commands -[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions prefixed with `GetCmd` and include the name of the command. +[Transactions](../core/transactions.md) are created by users to wrap messages that trigger state changes when they get included in a valid block. Transaction commands typically have their own `tx.go` file in the module `./x/moduleName/client/cli` folder. The commands are specified in getter functions and include the name of the command. -Here is an example from the `nameservice` module: +Here is an example from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/tx.go#L33-L58 ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/client/cli/tx_sign.go#L160-L194 -This getter function creates the command for the Buy Name transaction. It does the following: +This getter function creates the command for the `Sign` transaction. It does the following: -- **Construct the command:** Read the [Cobra Documentation](https://github.com/spf13/cobra) for details on how to create commands. - + **Use:** Specifies the format of a command-line entry users should type in order to invoke this command. In this case, the user uses `buy-name` as the name of the transaction command and provides the `name` the user wishes to buy and the `amount` the user is willing to pay. - + **Args:** The number of arguments the user provides, in this case exactly two: `name` and `amount`. - + **Short and Long:** A description for the function is provided here. A `Short` description is expected, and `Long` can be used to provide a more detailed description when a user uses the `--help` flag to ask for more information. - + **RunE:** Defines a function that can return an error, called when the command is executed. Using `Run` would do the same thing, but would not allow for errors to be returned. +- **Construct the command:** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) for details on how to create commands. + - **Use:** Specifies the format of a command-line entry users should type in order to invoke this command. In this case, the user uses `buy-name` as the name of the transaction command and provides the `name` the user wishes to buy and the `amount` the user is willing to pay. + - **Args:** The number of arguments the user provides, in this case exactly two: `name` and `amount`. + - **Short and Long:** A description for the function is provided here. A `Short` description is expected, and `Long` can be used to provide a more detailed description when a user uses the `--help` flag to ask for more information. + - **RunE:** Defines a function that can return an error, called when the command is executed. Using `Run` would do the same thing, but would not allow for errors to be returned. - **`RunE` Function Body:** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new transaction that is ready to be relayed to nodes. - + The function should first initialize a [`TxBuilder`](../core/transactions.md#txbuilder) with the application `codec`'s `TxEncoder`, as well as a new [`CLIContext`](../interfaces/query-lifecycle.md#clicontext) with the `codec` and `AccountDecoder`. These contexts contain all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation). - + If applicable, the command's arguments are parsed. Here, the `amount` given by the user is parsed into a denomination of `coins`. - + If applicable, the `CLIContext` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `cliCtx.getFromAddress()`. - + A [message](./messages-and-queries.md) is created using all parameters parsed from the command arguments and `CLIContext`. The constructor function of the specific message type is called directly. It is good practice to call `ValidateBasic()` on the newly created message to run a sanity check and check for invalid arguments. - + Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `GenerateOrBroadcastMsgs()`. -- **Flags.** Add any [flags](#flags) to the command. No flags were specified here, but all transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These *persistent* [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands. + - The function should first get the `clientCtx` with `client.GetClientContextFromCmd(cmd)` and `client.ReadTxCommandFlags(clientCtx, cmd.Flags())`. This context contains all the information provided by the user and will be used to transfer this user-specific information between processes. To learn more about how contexts are used in a transaction, click [here](../core/transactions.md#transaction-generation). + - If applicable, the command's arguments are parsed. + - If applicable, the `Context` is used to retrieve any parameters such as the transaction originator's address to be used in the transaction. Here, the `from` address is retrieved by calling `clientCtx.GetFromAddress()`. + - A [message](./messages-and-queries.md) is created using all parameters parsed from the command arguments and `Context`. The constructor function of the specific message type is called directly. It is good practice to call `ValidateBasic()` on the newly created message to run a sanity check and check for invalid arguments. + - Depending on what the user wants, the transaction is either generated offline or signed and broadcasted to the preconfigured node using `GenerateOrBroadcastMsgs()`. +- **Flags.** Add any [flags](#flags) to the command. All transaction commands have flags to provide additional information from the user (e.g. amount of fees they are willing to pay). These _persistent_ [transaction flags](../interfaces/cli.md#flags) can be added to a higher-level command so that they apply to all transaction commands. -Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign` and `MultiSign` commands. +Finally, the module needs to have a `GetTxCmd()`, which aggregates all of the transaction commands of the module. Often, each command getter function has its own file in the module's `cli` folder, and a separate `tx.go` file contains `GetTxCmd()`. Application developers wishing to include the module's transactions will call this function to add them as subcommands in their CLI. Here is the `auth` `GetTxCmd()` function, which adds the `Sign`, `MultiSign`, `ValidateSignatures` and `SignBatch` commands. -+++ https://github.com/cosmos/cosmos-sdk/blob/67f6b021180c7ef0bcf25b6597a629aca27766b8/x/auth/client/cli/tx.go#L11-L25 ++++ https://github.com/cosmos/cosmos-sdk/blob/351192aa0b52a42b66ff06e81cfa7a9e26667a7f/x/auth/client/cli/tx.go#L10-L26 An application using this module likely adds `auth` module commands to its root `TxCmd` command by calling `txCmd.AddCommand(authModuleClient.GetTxCmd())`. ### Query Commands -[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions and have the prefix `GetCmdQuery`. Here is an example of a query command from the `nameservice` module: +[Queries](./messages-and-queries.md#queries) allow users to gather information about the application or network state; they are routed by the application and processed by the module in which they are defined. Query commands typically have their own `query.go` file in the module `x/moduleName/client/cli` folder. Like transaction commands, they are specified in getter functions. Here is an example of a query command from the `auth` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/client/cli/query.go#L52-L73 ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/client/cli/query.go#L76-L108 -This query returns the address that owns a particular name. The getter function does the following: +This query returns the account at a given address. The getter function does the following: -- **`codec` and `queryRoute`.** In addition to taking in the application `codec`, query command getters also take a `queryRoute` used to construct a path [Baseapp](../core/baseapp.md#query-routing) uses to route the query in the application. -- **Construct the command.** Read the [Cobra Documentation](https://github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `whois` and provide the `name` they are querying for as the only argument. +- **Construct the command.** Read the [Cobra Documentation](https://godoc.org/github.com/spf13/cobra) and the [transaction command](#transaction-commands) example above for more information. The user must type `account` and provide the `address` they are querying for as the only argument. - **`RunE`.** The function should be specified as a `RunE` to allow for errors to be returned. This function encapsulates all of the logic to create a new query that is ready to be relayed to nodes. - + The function should first initialize a new [`CLIContext`](../interfaces/query-lifecycle.md#clicontext) with the application `codec`. - + If applicable, the `CLIContext` is used to retrieve any parameters (e.g. the query originator's address to be used in the query) and marshal them with the query parameter type, in preparation to be relayed to a node. There are no `CLIContext` parameters in this case because the query does not involve any information about the user. - + The `queryRoute` is used to construct a `path` [`baseapp`](../core/baseapp.md) will use to route the query to the appropriate [querier](./querier.md). The expected format for a query `path` is "queryCategory/queryRoute/queryType/arg1/arg2/...", where `queryCategory` can be `p2p`, `store`, `app`, or `custom`, `queryRoute` is the name of the module, and `queryType` is the name of the query type defined within the module. [`baseapp`](../core/baseapp.md) can handle each type of `queryCategory` by routing it to a module querier or retrieving results directly from stores and functions for querying peer nodes. Module queries are `custom` type queries (some SDK modules have exceptions, such as `auth` and `gov` module queries). - + The `CLIContext` `QueryWithData()` function is used to relay the query to a node and retrieve the response. It requires the `path`. It returns the result and height of the query upon success or an error if the query fails. In addition, it will verify the returned proof if `TrustNode` is disabled. If proof verification fails or the query height is invalid, an error will be returned. - + The `codec` is used to nmarshal the response and the `CLIContext` is used to print the output back to the user. + - The function should first initialize a new client [`Context`](../interfaces/query-lifecycle.md#context) as described in the [previous section](#transaction-commands) + - If applicable, the `Context` is used to retrieve any parameters (e.g. the query originator's address to be used in the query) and marshal them with the query parameter type, in preparation to be relayed to a node. There are no `Context` parameters in this case because the query does not involve any information about the user. + - A new `queryClient` should be initialized using `NewQueryClient(clientCtx)`, this method being generated from `query.proto`. Then it can be used to call the appropriate [query](./messages-and-queries.md#grpc-queries). + - The `clientCtx.PrintProto` method is used to format a `proto.Message` object and print it back to the user. - **Flags.** Add any [flags](#flags) to the command. - Finally, the module also needs a `GetQueryCmd`, which aggregates all of the query commands of the module. Application developers wishing to include the module's queries will call this function to add them as subcommands in their CLI. Its structure is identical to the `GetTxCmd` command shown above. ### Flags [Flags](../interfaces/cli.md#flags) are entered by the user and allow for command customizations. Examples include the [fees](../basics/gas-fees.md) or gas prices users are willing to pay for their transactions. -The flags for a module are typically found in a `flags.go` file in the `./x/moduleName/client/cli` folder. Module developers can create a list of possible flags including the value type, default value, and a description displayed if the user uses a `help` command. In each transaction getter function, they can add flags to the commands and, optionally, mark flags as *required* so that an error is thrown if the user does not provide values for them. +The flags for a module are typically found in a `flags.go` file in the `./x/moduleName/client/cli` folder. Module developers can create a list of possible flags including the value type, default value, and a description displayed if the user uses a `help` command. In each transaction getter function, they can add flags to the commands and, optionally, mark flags as _required_ so that an error is thrown if the user does not provide values for them. For full details on flags, visit the [Cobra Documentation](https://github.com/spf13/cobra). -For example, the SDK `./client/flags` package includes a `PostCommands()` function that adds necessary flags to transaction commands, such as the `from` flag to indicate which address the transaction originates from. +For example, the SDK `./client/flags` package includes a `AddTxFlagsToCmd(cmd *cobra.Command)` function that adds necessary flags to a transaction command, such as the `from` flag to indicate which address the transaction originates from. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/client/flags/flags.go#L85-L116 ++++ https://github.com/cosmos/cosmos-sdk/blob/cfb5fc03e5092395403d10156c0ee96e6ff1ddbe/client/flags/flags.go#L85-L112 Here is an example of how to add a flag using the `from` flag from this function. @@ -83,62 +81,105 @@ cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to s The input provided for this flag - called `FlagFrom` is a string with the default value of `""` if none is provided. If the user asks for a description of this flag, the description will be printed. -A flag can be marked as *required* so that an error is automatically thrown if the user does not provide a value: +A flag can be marked as _required_ so that an error is automatically thrown if the user does not provide a value: ```go cmd.MarkFlagRequired(FlagFrom) ``` -Since `PostCommands()` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). For a full list of what flags are included in the `PostCommands()` function, including which are required inputs from users, see the CLI documentation [here](../interfaces/cli.md#transaction-flags). +Since `AddTxFlagsToCmd(cmd *cobra.Command)` includes all of the basic flags required for a transaction command, module developers may choose not to add any of their own (specifying arguments instead may often be more appropriate). + +Similarly, there is a `AddQueryFlagsToCmd(cmd *cobra.Command)` to add common flags to a module query command. + +## gRPC + +[gRPC](https://grpc.io/) is the prefered way for external clients like wallets and exchanges to interact with a node. + +In addition to providing an ABCI query pathway, modules [custom queries](./messages-and-queries.md#grpc-queries) can provide a GRPC proxy server that routes requests in the GRPC protocol to ABCI query requests under the hood. + +In order to do that, module should implement `RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux)` on `AppModuleBasic` to wire the client gRPC requests to the correct handler inside the module. + +Here's an example from the `auth` module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/64b6bb5270e1a3b688c2d98a8f481ae04bb713ca/x/auth/module.go#L69-L72 + +## gRPC-gateway REST + +Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers can also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers. + +[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) translates REST calls into gRPC calls, which might be useful for clients that do not use gRPC. -## REST +Modules that want to expose REST queries should add `google.api.http` annotations to their `rpc` methods, such as in the example below from the `auth` module: -Applications typically support web services that use HTTP requests (e.g. a web wallet like [Lunie.io](https://lunie.io). Thus, application developers will also use REST Routes to route HTTP requests to the application's modules; these routes will be used by service providers. The module developer's responsibility is to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder. +```proto +// Query defines the gRPC querier service. +service Query{ + // Account returns account details based on address. + rpc Account (QueryAccountRequest) returns (QueryAccountResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; + } + + // Params queries all parameters. + rpc Params (QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/params"; + } +} +``` + +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`](../run-node/run-node.md#configuring-the-node-using-apptoml) defines if swagger documentation should be automatically registered. + +## Legacy REST + +Legacy REST endpoints will be deprecated. But developers may choose to keep using legacy REST endpoints for backward compatibility, although the recommended way is to use [gRPC](#grpc) and [gRPC-gateway](#grpc-gateway-rest). + +With this implementation, module developers need to define the REST client by defining [routes](#register-routes) for all possible [requests](#request-types) and [handlers](#request-handlers) for each of them. It's up to the module developer how to organize the REST interface files; there is typically a `rest.go` file found in the module's `./x/moduleName/client/rest` folder. To support HTTP requests, the module developer needs to define possible request types, how to handle them, and provide a way to register them with a provided router. ### Request Types -Request types, which define structured interactions from users, must be defined for all *transaction* requests. Users using this method to interact with an application will send HTTP Requests with the required fields in order to trigger state changes in the application. Conventionally, each request is named with the suffix `Req`, e.g. `SendReq` for a Send transaction. Each struct should include a base request [`baseReq`](../interfaces/rest.md#basereq), the name of the transaction, and all the arguments the user must provide for the transaction. +Request types, which define structured interactions from users, must be defined for all _transaction_ requests. Users using this method to interact with an application will send HTTP Requests with the required fields in order to trigger state changes in the application. Conventionally, each request is named with the suffix `Req`, e.g. `SendReq` for a Send transaction. Each struct should include a base request [`baseReq`](../interfaces/rest.md#basereq), the name of the transaction, and all the arguments the user must provide for the transaction. -Here is an example of a request to buy a name from the `nameservice` module: +Here is an example of a request to send coins from the `bank` module: -+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L14-L19 ++++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L15-L19 -The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the arguments `Name` and `Amount` fields in the body and `Buyer` will be provided by the user's address. +The `BaseReq` includes basic information that every request needs to have, similar to required flags in a CLI. All of these values, including `GasPrices` and `AccountNumber`, will be provided in the request body. The user will also need to specify the argument `Amount` fields in the body. #### BaseReq `BaseReq` is a type defined in the SDK that encapsulates much of the transaction configurations similar to CLI command flags. Users must provide the information in the body of their requests. -* `From` indicates which [account](../basics/accounts.md) the transaction originates from. This account is used to sign the transaction. -* `Memo` sends a memo along with the transaction. -* `ChainID` specifies the unique identifier of the blockchain the transaction pertains to. -* `AccountNumber` is an identifier for the account. -* `Sequence`is the value of a counter measuring how many transactions have been sent from the account. It is used to prevent replay attacks. -* `Gas` refers to how much [gas](../basics/gas-fees.md), which represents computational resources, Tx consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing auto as the value for `Gas`. -* `GasAdjustment` can be used to scale gas up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. -* `GasPrices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, --gas-prices=0.025uatom, 0.025upho means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. -* `Fees` specifies how much in [fees](../basics/gas-fees.md) the user is willing to pay in total. Note that the user only needs to provide either `gas-prices` or `fees`, but not both, because they can be derived from each other. -* `Simulate` instructs the application to ignore gas and simulate the transaction running without broadcasting. +- `From` indicates which [account](../basics/accounts.md) the transaction originates from. This account is used to sign the transaction. +- `Memo` sends a memo along with the transaction. +- `ChainID` specifies the unique identifier of the blockchain the transaction pertains to. +- `AccountNumber` is an identifier for the account. +- `Sequence`is the value of a counter measuring how many transactions have been sent from the account. It is used to prevent replay attacks. +- `TimeoutHeight` allows a transaction to be rejected if it's committed at a height greater than the timeout. +- `Gas` refers to how much [gas](../basics/gas-fees.md), which represents computational resources, Tx consumes. Gas is dependent on the transaction and is not precisely calculated until execution, but can be estimated by providing auto as the value for `Gas`. +- `GasAdjustment` can be used to scale gas up in order to avoid underestimating. For example, users can specify their gas adjustment as 1.5 to use 1.5 times the estimated gas. +- `GasPrices` specifies how much the user is willing pay per unit of gas, which can be one or multiple denominations of tokens. For example, --gas-prices=0.025uatom, 0.025upho means the user is willing to pay 0.025uatom AND 0.025upho per unit of gas. +- `Fees` specifies how much in [fees](../basics/gas-fees.md) the user is willing to pay in total. Note that the user only needs to provide either `gas-prices` or `fees`, but not both, because they can be derived from each other. +- `Simulate` instructs the application to ignore gas and simulate the transaction running without broadcasting. ### Request Handlers -Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the application's `codec` and the [`CLIContext`](../interfaces/query-lifecycle.md#clicontext) created in the user interaction. +Request handlers must be defined for both transaction and query requests. Handlers' arguments include a reference to the [client `Context`](../interfaces/query-lifecycle.md#context). -Here is an example of a request handler for the nameservice module `buyNameReq` request (the same one shown above): +Here is an example of a request handler for the `bank` module `SendReq` request (the same one shown above): -+++ https://github.com/cosmos/sdk-tutorials/blob/master/nameservice/x/nameservice/client/rest/tx.go#L21-L57 ++++ https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/bank/client/rest/tx.go#L21-L51 The request handler can be broken down as follows: -* **Parse Request:** The request handler first attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Next, it attempts to parse the arguments `Buyer` and `Amount` to the types `AccountAddress` and `Coins` respectively. -* **Message:** Then, a [message](./messages-and-queries.md) of the type `MsgBuyName` (defined by the module developer to trigger the state changes for this transaction) is created from the values and another sanity check, `ValidateBasic` is run on it. -* **Generate Transaction:** Finally, the HTTP `ResponseWriter`, application [`codec`](../core/encoding.md), [`CLIContext`](../interfaces/query-lifecycle.md#clicontext), request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGenerateStdTxResponse` to further process the request. +- **Parse Request:** First, it tries to parse the argument `address` into a `AccountAddress`. Then, the request handler attempts to parse the request, and then run `Sanitize` and `ValidateBasic` on the underlying `BaseReq` to check the validity of the request. Finally, it attempts to parse `BaseReq.From` to the type `AccountAddress`. +- **Message:** Then, a [message](./messages-and-queries.md#messages) of the type `MsgSend` (defined by the module developer to trigger the state changes for this transaction) is created from the values. +- **Generate Transaction:** Finally, the HTTP `ResponseWriter`, client `Context`, request [`BaseReq`](../interfaces/rest.md#basereq), and message is passed to `WriteGeneratedTxResponse` to further process the request. To read more about how a transaction is generated, visit the transactions documentation [here](../core/transactions.md#transaction-generation). - ### Register Routes The application CLI entrypoint will have a `RegisterRoutes` function in its `main.go` file, which calls the `registerRoutes` functions of each module utilized by the application. Module developers need to implement `registerRoutes` for their modules so that applications are able to route messages and queries to their corresponding handlers and queriers. @@ -147,8 +188,8 @@ The router used by the SDK is [Gorilla Mux](https://github.com/gorilla/mux). The Here is a `registerRoutes` function with one query route example from the [nameservice tutorial](https://cosmos.network/docs/tutorial/rest.html): -``` go -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) { +```go +func RegisterRoutes(cliCtx client.Context, r *mux.Router, cdc *codec.LegacyAmino, storeName string) { // ResolveName Query r.HandleFunc(fmt.Sprintf("/%s/names/{%s}", storeName, restName), resolveNameHandler(cdc, cliCtx, storeName)).Methods("GET") } @@ -156,11 +197,10 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, A few things to note: -* The router `r` has already been initialized by the application and is passed in here as an argument - this function is able to add on the nameservice module's routes onto any application's router. The application must also provide a [`CLIContext`](../interfaces/query-lifecycle.md#clicontext) that the querier will need to process user requests and the application [`codec`](../core/encoding.md) for encoding and decoding application-specific types. -* `"/%s/names/{%s}", storeName, restName` is the url for the HTTP request. `storeName` is the name of the module, `restName` is a variable provided by the user to specify what kind of query they are making. -* `resolveNameHandler` is the query request handler defined by the module developer. It also takes the application `codec` and `CLIContext` passed in from the user side, as well as the `storeName`. -* `"GET"` is the HTTP Request method. As to be expected, queries are typically GET requests. Transactions are typically POST and PUT requests. - +- The router `r` has already been initialized by the application and is passed in here as an argument - this function is able to add on the nameservice module's routes onto any application's router. The application must also provide a [`Context`](../interfaces/query-lifecycle.md#context) that the querier will need to process user requests and the application [`codec`](../core/encoding.md) for encoding and decoding application-specific types. +- `"/%s/names/{%s}", storeName, restName` is the url for the HTTP request. `storeName` is the name of the module, `restName` is a variable provided by the user to specify what kind of query they are making. +- `resolveNameHandler` is the query request handler defined by the module developer. It also takes the application `codec` and `Context` passed in from the user side, as well as the `storeName`. +- `"GET"` is the HTTP Request method. As to be expected, queries are typically GET requests. Transactions are typically POST and PUT requests. ## Next {hide} diff --git a/docs/building-modules/module-manager.md b/docs/building-modules/module-manager.md index bbafacca29c7..919ec1425c84 100644 --- a/docs/building-modules/module-manager.md +++ b/docs/building-modules/module-manager.md @@ -1,17 +1,18 @@ # Module Manager -## Pre-requisite Readings {hide} +Cosmos SDK modules need to implement the [`AppModule` interfaces](#application-module-interfaces), in order to be managed by the application's [module manager](#module-manager). The module manager plays an important role in [`message` and `query` routing](../core/baseapp.md#routing), and allows application developers to set the order of execution of a variety of functions like [`BeginBlocker` and `EndBlocker`](../basics/app-anatomy.md#begingblocker-and-endblocker). {synopsis} + +## Pre-requisite Readings - [Introduction to SDK Modules](./intro.md) {prereq} ## Application Module Interfaces -Application module interfaces exist to facilitate the composition of modules together to form a functional SDK application. There are 3 main application module interfaces: +Application module interfaces exist to facilitate the composition of modules together to form a functional SDK application. There are 3 main application module interfaces: - [`AppModuleBasic`](#appmodulebasic) for independent module functionalities. - [`AppModule`](#appmodule) for inter-dependent module functionalities (except genesis-related functionalities). @@ -19,65 +20,68 @@ Application module interfaces exist to facilitate the composition of modules tog The `AppModuleBasic` interface exists to define independent methods of the module, i.e. those that do not depend on other modules in the application. This allows for the construction of the basic application structure early in the application definition, generally in the `init()` function of the [main application file](../basics/app-anatomy.md#core-application-file). -The `AppModule` interface exists to define inter-dependent module methods. Many modules need to interract with other modules, typically through [`keeper`s](./keeper.md), which means there is a need for an interface where modules list their `keeper`s and other methods that require a reference to another module's object. `AppModule` interface also enables the module manager to set the order of execution between module's methods like `BeginBlock` and `EndBlock`, which is important in cases where the order of execution between modules matters in the context of the application. +The `AppModule` interface exists to define inter-dependent module methods. Many modules need to interract with other modules, typically through [`keeper`s](./keeper.md), which means there is a need for an interface where modules list their `keeper`s and other methods that require a reference to another module's object. `AppModule` interface also enables the module manager to set the order of execution between module's methods like `BeginBlock` and `EndBlock`, which is important in cases where the order of execution between modules matters in the context of the application. Lastly the interface for genesis functionality `AppModuleGenesis` is separated out from full module functionality `AppModule` so that modules which -are only used for genesis can take advantage of the `Module` patterns without having to define many placeholder functions. +are only used for genesis can take advantage of the `Module` patterns without having to define many placeholder functions. ### `AppModuleBasic` -The `AppModuleBasic` interface defines the independent methods modules need to implement. +The `AppModuleBasic` interface defines the independent methods modules need to implement. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L46-L59 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L49-L63 Let us go through the methods: - `Name()`: Returns the name of the module as a `string`. -- `RegisterCodec(*codec.Codec)`: Registers the `codec` for the module, which is used to marhsal and unmarshal structs to/from `[]byte` in order to persist them in the moduel's `KVStore`. -- `DefaultGenesis()`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. -- `ValidateGenesis(json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer. -- `RegisterRESTRoutes(context.CLIContext, *mux.Router)`: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See [../interfaces/rest.md] for more. -- `GetTxCmd(*codec.Codec)`: Returns the root [`Tx` command](./module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./messages-and-queries.md#queries) defined in the module. -- `GetQueryCmd(*codec.Codec)`: Return the root [`query` command](./module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module. +- `RegisterLegacyAminoCodec(*codec.LegacyAmino)`: Registers the `amino` codec for the module, which is used to marshal and unmarshal structs to/from `[]byte` in order to persist them in the module's `KVStore`. +- `RegisterInterfaces(codectypes.InterfaceRegistry)`: Registers a module's interface types and their concrete implementations as `proto.Message`. +- `DefaultGenesis(codec.JSONMarshaler)`: Returns a default [`GenesisState`](./genesis.md#genesisstate) for the module, marshalled to `json.RawMessage`. The default `GenesisState` need to be defined by the module developer and is primarily used for testing. +- `ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`: Used to validate the `GenesisState` defined by a module, given in its `json.RawMessage` form. It will usually unmarshall the `json` before running a custom [`ValidateGenesis`](./genesis.md#validategenesis) function defined by the module developer. +- `RegisterRESTRoutes(client.Context, *mux.Router)`: Registers the REST routes for the module. These routes will be used to map REST request to the module in order to process them. See [../interfaces/rest.md] for more. +- `RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux)`: Registers gRPC routes for the module. +- `GetTxCmd()`: Returns the root [`Tx` command](./module-interfaces.md#tx) for the module. The subcommands of this root command are used by end-users to generate new transactions containing [`message`s](./messages-and-queries.md#queries) defined in the module. +- `GetQueryCmd()`: Return the root [`query` command](./module-interfaces.md#query) for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module. -All the `AppModuleBasic` of an application are managed by the [`BasicManager`](#basicmanager). +All the `AppModuleBasic` of an application are managed by the [`BasicManager`](#basicmanager). ### `AppModuleGenesis` The `AppModuleGenesis` interface is a simple embedding of the `AppModuleBasic` interface with two added methods. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L126-L131 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L152-L158 Let us go through the two added methods: -- `InitGenesis(sdk.Context, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). -- `ExportGenesis(sdk.Context)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. +- `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)`: Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started). +- `ExportGenesis(sdk.Context, codec.JSONMarshaler)`: Exports the latest subset of the state managed by the module to be used in a new genesis file. `ExportGenesis` is called for each module when a new chain is started from the state of an existing chain. -It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, json.RawMessage)` and `ExportGenesis(sdk.Context)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface. +It does not have its own manager, and exists separately from [`AppModule`](#appmodule) only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of `AppModule`'s methods. If the module is not only used during genesis, `InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage)` and `ExportGenesis(sdk.Context, codec.JSONMarshaler)` will generally be defined as methods of the concrete type implementing hte `AppModule` interface. ### `AppModule` -The `AppModule` interface defines the inter-dependent methods modules need to implement. +The `AppModule` interface defines the inter-dependent methods modules need to implement. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L133-L149 ++++ https://github.com/cosmos/cosmos-sdk/blob/b4cce159bcc6a32ac78245c6866dd87c73f3720d/types/module/module.go#L160-L182 -`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter. +`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter. Let us go through the methods of `AppModule`: -- `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./invariants.md) of the module. If the invariants deviates from its predicted value, the [`InvariantRegistry`](./invariants.md#registry) triggers appropriate logic (most often the chain will be halted). -- `Route()`: Returns the name of the module's route, for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`baseapp`](../core/baseapp.md#message-routing). -- `NewHandler()`: Returns a [`handler`](./handler.md) given the `Type()` of the `message`, in order to process the `message`. -- `QuerierRoute()`: Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing). -- `NewQuerierHandler()`: Returns a [`querier`](./querier.md) given the query `path`, in order to process the `query`. -- `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. +- `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./invariants.md) of the module. If the invariants deviates from its predicted value, the [`InvariantRegistry`](./invariants.md#registry) triggers appropriate logic (most often the chain will be halted). +- `Route()`: Returns the route for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`BaseApp`](../core/baseapp.md#message-routing). +- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`BaseApp`](../core/baseapp.md#query-routing). +- `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 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 -Typically, the various application module interfaces are implemented in a file called `module.go`, located in the module's folder (e.g. `./x/module/module.go`). +Typically, the various application module interfaces are implemented in a file called `module.go`, located in the module's folder (e.g. `./x/module/module.go`). -Almost every module need to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `NewHandler()` function often calls a `NewHandler(k keeper)` function defined in [`handler.go`](./handler.md) and therefore needs to pass the module's [`keeper`](./keeper.md) as parameter. +Almost every module needs to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `Route()` function often calls a `NewHandler(k keeper)` function defined in [`handler.go`](./msg-services.md#handler-type) and therefore needs to pass the module's [`keeper`](./keeper.md) as a parameter. ```go // example @@ -87,7 +91,7 @@ type AppModule struct { } ``` -In the example above, you can see that the `AppModule` concrete type references an `AppModuleBasic`, and not an `AppModuleGenesis`. That is because `AppModuleGenesis` only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete `AppModule` type will have a reference to an `AppModuleBasic` and implement the two added methods of `AppModuleGenesis` directly in the `AppModule` type. +In the example above, you can see that the `AppModule` concrete type references an `AppModuleBasic`, and not an `AppModuleGenesis`. That is because `AppModuleGenesis` only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete `AppModule` type will have a reference to an `AppModuleBasic` and implement the two added methods of `AppModuleGenesis` directly in the `AppModule` type. If no parameter is required (which is often the case for `AppModuleBasic`), just declare an empty concrete type like so: @@ -97,30 +101,31 @@ type AppModuleBasic struct{} ## Module Managers -Module managers are used to manage collections of `AppModuleBasic` and `AppModule`. +Module managers are used to manage collections of `AppModuleBasic` and `AppModule`. ### `BasicManager` -The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application: +The `BasicManager` is a structure that lists all the `AppModuleBasic` of an application: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L61-L63 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L65-L66 It implements the following methods: - `NewBasicManager(modules ...AppModuleBasic)`: Constructor function. It takes a list of the application's `AppModuleBasic` and builds a new `BasicManager`. This function is generally called in the `init()` function of [`app.go`](../basics/app-anatomy.md#core-application-file) to quickly initialize the independent elements of the application's modules (click [here](https://github.com/cosmos/gaia/blob/master/app/app.go#L59-L74) to see an example). -- `RegisterCodec(cdc *codec.Codec)`: Registers the [`codec`s](../core/encoding.md) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor). -- `DefaultGenesis()`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis()`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. -- `ValidateGenesis(genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis()`](./genesis.md#validategenesis) function of each module. -- `RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router)`: Registers REST routes for modules by calling the [`RegisterRESTRoutes`](./module-interfaces.md#register-routes) function of each module. This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). -- `AddTxCommands(rootTxCmd *cobra.Command, cdc *codec.Codec)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../interfaces/cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). -- `AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec.Codec)`: Adds modules' query commands to the application's [`rootQueryCommand`](../interfaces/cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). - +- `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)`: Registers the [`codec.LegacyAmino`s](../core/encoding.md#amino) of each of the application's `AppModuleBasic`. This function is usually called early on in the [application's construction](../basics/app-anatomy.md#constructor). +- `RegisterInterfaces(registry codectypes.InterfaceRegistry)`: Registers interface types and implementations of each of the application's `AppModuleBasic`. +- `DefaultGenesis(cdc codec.JSONMarshaler)`: Provides default genesis information for modules in the application by calling the [`DefaultGenesis(cdc codec.JSONMarshaler)`](./genesis.md#defaultgenesis) function of each module. It is used to construct a default genesis file for the application. +- `ValidateGenesis(cdc codec.JSONMarshaler, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage)`: Validates the genesis information modules by calling the [`ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage)`](./genesis.md#validategenesis) function of each module. +- `RegisterRESTRoutes(ctx client.Context, rtr *mux.Router)`: Registers REST routes for modules by calling the [`RegisterRESTRoutes`](./module-interfaces.md#register-routes) function of each module. This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). +- `RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux)`: Registers gRPC routes for modules. +- `AddTxCommands(rootTxCmd *cobra.Command)`: Adds modules' transaction commands to the application's [`rootTxCommand`](../interfaces/cli.md#transaction-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). +- `AddQueryCommands(rootQueryCmd *cobra.Command)`: Adds modules' query commands to the application's [`rootQueryCommand`](../interfaces/cli.md#query-commands). This function is usually called function from the `main.go` function of the [application's command-line interface](../interfaces/cli.md). ### `Manager` The `Manager` is a structure that holds all the `AppModule` of an application, and defines the order of execution between several key components of these modules: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/module/module.go#L190-L198 ++++ https://github.com/cosmos/cosmos-sdk/blob/325be6ff215db457c6fc7668109640cd7fdac461/types/module/module.go#L223-L231 The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods: @@ -128,13 +133,18 @@ 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)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./querier.md). -- `InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. -- `ExportGenesis(ctx sdk.Context)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. -- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`baseapp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events. -- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`baseapp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any). +- `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. +- `InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates. +- `ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required. +- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`BaseApp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events. +- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`BaseApp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any). + +Here's an example of a concrete integration within an application: + ++++ https://github.com/cosmos/cosmos-sdk/blob/2323f1ac0e9a69a0da6b43693061036134193464/simapp/app.go#L315-L362 ## Next {hide} diff --git a/docs/building-modules/msg-services.md b/docs/building-modules/msg-services.md new file mode 100644 index 000000000000..6941252f3a76 --- /dev/null +++ b/docs/building-modules/msg-services.md @@ -0,0 +1,101 @@ + +# `Msg` Services + +A `Msg` Service processes [messages](./messages-and-queries.md#messages). `Msg` Services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../core/baseapp.md#delivertx). {synopsis} + +## Pre-requisite Readings + +- [Module Manager](./module-manager.md) {prereq} +- [Messages and Queries](./messages-and-queries.md) {prereq} + +## Implementation of a module `Msg` service + +All `Msg` processing is done by a [`Msg`](messages-and-queries.md#msg-services) protobuf service. Each module should define a `Msg` service, which will be responsible for request and response serialization. + +As further described in [ADR 031](../architecture/adr-031-msg-service.md), this approach has the advantage of clearly specifying return types and generating server and client code. + +Based on the definition of the `Msg` service, Protobuf generates a `MsgServer` interface. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `Msg`s: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/bank/types/tx.pb.go#L285-L291 + +When possible, the existing module's [`Keeper`](keeper.md) should implement `MsgServer`, otherwise a `msgServer` struct that embeds the `Keeper` can be created, typically in `./keeper/msg_server.go`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L14-L16 + +`msgServer` methods can retrieve the `sdk.Context` from the `context.Context` parameter method using the `sdk.UnwrapSDKContext`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L27-L28 + +`Msg` processing usually follows these 2 steps: + +- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `msgServer` method can be more expensive and require access to the state. For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `msgServer` method needs to call the [`keeper`'s](./keeper.md) getter functions. +- Then, if the checks are successful, the `msgServer` method calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition. + +Before returning, `msgServer` methods generally emit one or more [events](../core/events.md) via the `EventManager` held in the `ctx`: + +```go +ctx.EventManager().EmitEvent( + sdk.NewEvent( + eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module + sdk.NewAttribute(attributeKey, attributeValue), + ), + ) +``` + +These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about events. + +The invoked `msgServer` method returns a `proto.Message` response and an `error`. These return values are then wrapped into an `*sdk.Result` or an `error` using `sdk.WrapServiceResult(ctx sdk.Context, res proto.Message, err error)`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/baseapp/msg_service_router.go#L104-L104 + +This method takes care of marshaling the `res` parameter to protobuf and attaching any events on the `ctx.EventManager()` to the `sdk.Result`. + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95 + +## Legacy Amino `Msg`s + +### `handler` type + +The `handler` type defined in the Cosmos SDK will be deprecated in favor of [`Msg` Services](#implementation-of-a-module-msg-service). + +Here is the typical structure of a `handler` function: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/types/handler.go#L4-L4 + +Let us break it down: + +- The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed. +- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a branch of the latest state. If the `msg` is successfully processed, the branched version of the state contained in the `ctx` will be written to the main state (branch). +- The [`*Result`] returned to `BaseApp` contains (among other things) information on the execution of the `handler` and [events](../core/events.md). + +Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `handler`s to the +[application's `router`](../core/baseapp.md#message-routing) via the `Route()` method. Typically, +the manager's `Route()` method simply constructs a Route that calls a `NewHandler()` method defined in `handler.go`. + ++++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146 + +### Implementation + +`NewHandler` function dispatches a `Msg` to appropriate handler function, usually by using a switch statement: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/handler.go#L13-L29 + +First, `NewHandler` function sets a new `EventManager` to the context to isolate events per `msg`. +Then, a simple switch calls the appropriate `handler` based on the `Msg` type. + +In this regard, `handler`s functions need to be implemented for each module `Msg`. This will also involve manual handler registration of `Msg` types. +`handler`s functions should return a `*Result` and an `error`. + +## Telemetry + +New [telemetry metrics](../core/telemetry.md) can be created from `msgServer` methods when handling messages. + +This is an example from the `x/auth/vesting` module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/auth/vesting/msg_server.go#L73-L85 + +## Next {hide} + +Learn about [query services](./query-services.md) {hide} diff --git a/docs/building-modules/querier.md b/docs/building-modules/querier.md deleted file mode 100644 index be4750778661..000000000000 --- a/docs/building-modules/querier.md +++ /dev/null @@ -1,55 +0,0 @@ - - -# Queriers - -## Pre-requisite Readings {hide} - -- [Module Manager](./module-manager.md) {prereq} -- [Messages and Queries](./messages-and-queries.md) {prereq} - -## `Querier` type - -The `querier` type defined in the Cosmos SDK specifies the typical structure of a `querier` function: - -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/queryable.go#L6 - -Let us break it down: - -- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information. -- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. -- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. -- The result `res` returned to `baseapp`, marhsalled using the application's [`codec`](../core/encoding.md). - -## Implementation of a module `querier`s - -Module `querier`s are typically implemented in a `./internal/keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: - -```go -func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case QueryType1: - return queryType1(ctx, path[1:], req, keeper) - - case QueryType2: - return queryType2(ctx, path[1:], req, keeper) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} -``` - -This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. - -The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result. - -For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/sdk-application-tutorial/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/x/nameservice/internal/keeper/querier.go) from the nameservice tutorial. - -## Next {hide} - -Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/docs/building-modules/query-services.md b/docs/building-modules/query-services.md new file mode 100644 index 000000000000..e9f7f8c77d59 --- /dev/null +++ b/docs/building-modules/query-services.md @@ -0,0 +1,77 @@ + + +# Query Services + +A query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../core/baseapp.md#query). {synopsis} + +## Pre-requisite Readings + +- [Module Manager](./module-manager.md) {prereq} +- [Messages and Queries](./messages-and-queries.md) {prereq} + +## `Querier` type + +The `querier` type defined in the Cosmos SDK will be deprecated in favor of [gRPC Services](#grpc-service). It specifies the typical structure of a `querier` function: + ++++ https://github.com/cosmos/cosmos-sdk/blob/9a183ffbcc0163c8deb71c7fd5f8089a83e58f05/types/queryable.go#L9 + +Let us break it down: + +- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information. +- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. +- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a branch of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. +- The result `res` returned to `BaseApp`, marshalled using the application's [`codec`](../core/encoding.md). + +## Implementation of a module query service + +### gRPC Service + +When defining a Protobuf `Query` service, a `QueryServer` interface is generated for each module with all the service methods: + +```go +type QueryServer interface { + QueryBalance(context.Context, *QueryBalanceParams) (*types.Coin, error) + QueryAllBalances(context.Context, *QueryAllBalancesParams) (*QueryAllBalancesResponse, error) +} +``` + +These custom queries methods should be implemented by a module's keeper, typically in `./keeper/grpc_query.go`. The first parameter of these methods is a generic `context.Context`, whereas querier methods generally need an instance of `sdk.Context` to read +from the store. Therefore, the SDK provides a function `sdk.UnwrapSDKContext` to retrieve the `sdk.Context` from the provided +`context.Context`. + +Here's an example implementation for the bank module: + ++++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/keeper/grpc_query.go + +### Legacy Queriers + +Module legacy `querier`s are typically implemented in a `./keeper/querier.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `querier`s to the [application's `queryRouter`](../core/baseapp.md#query-routing) via the `NewQuerier()` method. Typically, the manager's `NewQuerier()` method simply calls a `NewQuerier()` method defined in `keeper/querier.go`, which looks like the following: + +```go +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + case QueryType1: + return queryType1(ctx, path[1:], req, keeper) + + case QueryType2: + return queryType2(ctx, path[1:], req, keeper) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + } +} +``` + +This simple switch returns a `querier` function specific to the type of the received `query`. At this point of the [query lifecycle](../interfaces/query-lifecycle.md), the first element of the `path` (`path[0]`) contains the type of the query. The following elements are either empty or contain arguments needed to process the query. + +The `querier` functions themselves are pretty straighforward. They generally fetch a value or values from the state using the [`keeper`](./keeper.md). Then, they marshall the value(s) using the [`codec`](../core/encoding.md) and return the `[]byte` obtained as result. + +For a deeper look at `querier`s, see this [example implementation of a `querier` function](https://github.com/cosmos/cosmos-sdk/blob/7f59723d889b69ca19966167f0b3a7fec7a39e53/x/gov/keeper/querier.go) from the bank module. + +## Next {hide} + +Learn about [`BeginBlocker` and `EndBlocker`](./beginblock-endblock.md) {hide} diff --git a/docs/building-modules/simulator.md b/docs/building-modules/simulator.md new file mode 100644 index 000000000000..7a9e72ca00d6 --- /dev/null +++ b/docs/building-modules/simulator.md @@ -0,0 +1,123 @@ +# Module Simulation + +## Prerequisites + +* [Cosmos Blockchain Simulator](./../using-the-sdk/simulation.md) + +## Synopsis + +This document details how to define each module simulation functions to be +integrated with the application `SimulationManager`. + +* [Simulation package](#simulation-package) + * [Store decoders](#store-decoders) + * [Randomized genesis](#randomized-genesis) + * [Randomized parameters](#randomized-parameters) + * [Random weighted operations](#random-weighted-operations) + * [Random proposal contents](#random-proposal-contents) +* [Registering the module simulation functions](#registering-simulation-functions) +* [App simulator manager](#app-simulator-manager) +* [Simulation tests](#simulation-tests) + +## Simulation package + +Every module that implements the SDK simulator needs to have a `x//simulation` +package which contains the primary functions required by the fuzz tests: store +decoders, randomized genesis state and parameters, weighted operations and proposal +contents. + +### Store decoders + +Registering the store decoders is required for the `AppImportExport`. This allows +for the key-value pairs from the stores to be decoded (_i.e_ unmarshalled) +to their corresponding types. In particular, it matches the key to a concrete type +and then unmarshals the value from the `KVPair` to the type provided. + +You can use the example [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/simulation/decoder.go) from the distribution module to implement your store decoders. + +### Randomized genesis + +The simulator tests different scenarios and values for genesis parameters +in order to fully test the edge cases of specific modules. The `simulator` package from each module must expose a `RandomizedGenState` function to generate the initial random `GenesisState` from a given seed. + +Once the module genesis parameter are generated randomly (or with the key and +values defined in a `params` file), they are marshaled to JSON format and added +to the app genesis JSON to use it on the simulations. + +You can check an example on how to create the randomized genesis [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/staking/simulation/genesis.go). + +### Randomized parameter changes + +The simulator is able to test parameter changes at random. The simulator package from each module must contain a `RandomizedParams` func that will simulate parameter changes of the module throughout the simulations lifespan. + +You can see how an example of what is needed to fully test parameter changes [here](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/staking/simulation/params.go) + +### Random weighted operations + +Operations are one of the crucial parts of the SDK simulation. They are the transactions +(`Msg`) that are simulated with random field values. The sender of the operation +is also assigned randomly. + +Operations on the simulation are simulated using the full [transaction cycle](../core/transactions.md) of a +`ABCI` application that exposes the `BaseApp`. + +Shown below is how weights are set: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/simulation/operations.go#L18-L68 + +As you can see, the weights are predefined in this case. Options exist to override this behavior with different weights. One option is to use `*rand.Rand` to define a random weight for the operation, or you can inject your own predefined weights. + +Here is how one can override the above package `simappparams`. + ++++ https://github.com/cosmos/gaia/blob/master/sims.mk#L9-L22 + +For the last test a tool called runsim is used, this is used to parallelize go test instances, provide info to Github and slack integrations to provide information to your team on how the simulations are running. + +### Random proposal contents + +Randomized governance proposals are also supported on the SDK simulator. Each +module must define the governance proposal `Content`s that they expose and register +them to be used on the parameters. + +## Registering simulation functions + +Now that all the required functions are defined, we need to integrate them into the module pattern within the `module.go`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/x/distribution/module.go + +## App Simulator manager + +The following step is setting up the `SimulatorManager` at the app level. This +is required for the simulation test files on the next step. + +```go +type CustomApp struct { + ... + sm *module.SimulationManager +} +``` + +Then at the instantiation of the application, we create the `SimulationManager` +instance in the same way we create the `ModuleManager` but this time we only pass +the modules that implement the simulation functions from the `AppModuleSimulation` +interface described above. + +```go +func NewCustomApp(...) { + // create the simulation manager and define the order of the modules for deterministic simulations + app.sm = module.NewSimulationManager( + auth.NewAppModule(app.accountKeeper), + bank.NewAppModule(app.bankKeeper, app.accountKeeper), + supply.NewAppModule(app.supplyKeeper, app.accountKeeper), + ov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper), + mint.NewAppModule(app.mintKeeper), + distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper), + staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper), + slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper), + ) + + // register the store decoders for simulation tests + app.sm.RegisterStoreDecoders() + ... +} +``` diff --git a/docs/building-modules/structure.md b/docs/building-modules/structure.md index 6b6d2f889a14..79c353a2cefa 100644 --- a/docs/building-modules/structure.md +++ b/docs/building-modules/structure.md @@ -1,10 +1,11 @@ # Recommended Folder Structure +This document outlines the recommended structure of Cosmos SDK modules. These ideas are meant to be applied as suggestions. Application developers are encouraged to improve upon and contribute to module structure and development design. {synopsis} + ## Structure A typical Cosmos SDK module can be structured as follows: @@ -20,40 +21,43 @@ x/{module} │   └── tx.go ├── exported │   └── exported.go -├── internal -│   ├── keeper -│   │   ├── invariants.go -│   │   ├── keeper.go -│   │   ├── ... -│   │   └── querier.go -│   └── types -│   ├── codec.go -│   ├── errors.go -│   ├── events.go -│   ├── expected_keepers.go -│   ├── genesis.go -│   ├── keys.go -│   ├── msgs.go -│   ├── params.go -│   ├── ... -│   └── querier.go +├── keeper +│   ├── invariants.go +│   ├── genesis.go +│   ├── keeper.go +│   ├── msg_server.go +│   ├── ... +│   └── querier.go +│   └── grpc_query.go +├── types +│ ├── codec.go +│ ├── errors.go +│ ├── events.go +│ ├── expected_keepers.go +│ ├── genesis.go +│ ├── keys.go +│ ├── msgs.go +│ ├── params.go +│ ├── types.proto +│ ├── ... +│ └── querier.go +│ └── {module_name}.pb.go +│ └── query.pb.go +│ └── genesis.pb.go +├── simulation +│   ├── decoder.go +│   ├── genesis.go +│   ├── operations.go +│   ├── params.go +│   └── proposals.go ├── abci.go -├── alias.go -├── genesis.go ├── handler.go -├── module.go ├── ... -└── simulation.go +└── module.go ``` - `abci.go`: The module's `BeginBlocker` and `EndBlocker` implementations (if any). -- `alias.go`: The module's exported types, constants, and variables. These are mainly -to improve developer ergonomics by only needing to import a single package. Note, -there is nothing preventing developers from importing other packages from the module -(excluding`internal/`) but it is recommended that `alias.go` have everything -exposed that other modules may need. The majority of the exported values here will -typically come from `internal/` (see below). -- `client/`: The module's CLI and REST client functionality implementation and +- `client/`: The module's CLI and REST client functionality implementation and testing. - `exported/`: The module's exported types -- typically type interfaces. If a module relies on other module keepers, it is expected to receive them as interface @@ -64,23 +68,17 @@ to the contract's implementing module and this is where `exported/` comes into p Types defined here allow for `expected_keepers.go` in other modules to define contracts that use single canonical types. This pattern allows for code to remain DRY and also alleviates import cycle chaos. -- `genesis.go`: The module's genesis related business logic (e.g. `InitGenesis`). -Note, genesis types are defined in `internal/types`. - `handler.go`: The module's message handlers. -- `internal/`: The module's internal types and implementations. The purpose of -this package is mainly two fold. First, it signals that this package is not -intended to be used or imported anywhere outside the defining module. Secondly, -it goes hand-in-hand with `alias.go` in that it allows public types and functions -to be used internally while not being exposed outside to the outside world. This -allows for greater freedom of development while maintaining API stability. - - `internal/types`: The module's type definitions such as messages, `KVStore` - keys, parameter types, and `expected_keepers.go` contracts. - - `internal/keeper`: The module's keeper implementation along with any auxiliary - implementations such as the querier and invariants. +- `keeper/`: The module's keeper implementation along with any auxiliary +implementations such as the querier and invariants. +- `types/`: The module's type definitions such as messages, `KVStore` keys, +parameter types, Protocol Buffer definitions, and `expected_keepers.go` contracts. - `module.go`: The module's implementation of the `AppModule` and `AppModuleBasic` interfaces. -- `simulation.go`: The module's simulation messages and related types (if any). +- `simulation/`: The module's simulation package defines all the required functions +used on the blockchain simulator: randomized genesis state, parameters, weighted +operations, proposal contents and types decoders. ## Next {hide} -Learn about [interfaces](../interfaces/interfaces-intro.md) {hide} \ No newline at end of file +Learn about [Errors](./errors.md) {hide} diff --git a/docs/cn/README.md b/docs/cn/README.md index bbd14a3109f3..dd30ed000a04 100644 --- a/docs/cn/README.md +++ b/docs/cn/README.md @@ -7,24 +7,25 @@ parent: ## 开始 -- **[SDK 介绍](./intro/README.md)**:从“高层”了解Cosmos SDK. -- **[SDK 开发教程](https://github.com/cosmos/sdk-application-tutorial)**: 一个学习 SDK 的教程。它展示了如何从头开始基于 sdk 构建区块链, 并在此过程中解释了 SDK 的基本原理。 +- **[SDK 介绍](./intro/README.md)**:Cosmos SDK 的总体概览 +- **[快速开始](./using-the-sdk/quick-start.md)**:构建一个标准的基于 cosmos sdk 的 app 并启动节点 +- **[SDK 开发教程](https://github.com/cosmos/sdk-application-tutorial)**: 一个学习 SDK 的教程。它展示了如何从头开始基于 sdk 构建区块链, 并在此过程中解释了 SDK 的基本原理。 +## 索引 -## 开发资源 - -- [规范](./spec/README.md): Cosmos SDK 的模块及其他规范。 -- [SDK API 参考](https://godoc.org/github.com/cosmos/cosmos-sdk): Cosmos SDK Godocs 文档 。 -- [REST API 规范](https://cosmos.network/rpc/): 通过 REST 与 `gaia` 全节点交互的 API 列表。 - -## 创建新的 SDK 项目 +- **[基础文档](./basics/)**:cosmos sdk 的基础概念文档,例如应用结构、交易的生命周期、账户管理等 +- **[核心文档](./core/)**: cosmos sdk 的核心文档,例如`baseapp`,`store`,`server`等 +- **[构建模块](./building-modules/)**: 对于模块开发者来说的一些重要概念,例如`message`,`keeper`,`handler`,`querier` +- **[接口](./interfaces/)**: 为 cosmos 应用设计接口的文档 -若要创建新项目, 以下两个方法任选其一: +## 开发资源 -- 克隆这个 [教程](https://github.com/cosmos/sdk-application-tutorial/),如果不需要, 请不要忘记从各种文件中删除 `nameservice` 模块。 -- 使用社区工具, 如 [chainkit](https://github.com/blocklayerhq/chainkit). +- **[模块目录](../../x/)**:模块的实现和文档 +- **[规范](./spec/):** Cosmos SDK 的模块及其他规范。 +- **[SDK API 参考](https://godoc.org/github.com/cosmos/cosmos-sdk):** Cosmos SDK Godocs 文档 。 +- **[REST API 规范](https://cosmos.network/rpc/):** 通过 REST 与 `gaia` 全节点交互的 API 列表。 -## Cosmos Hub +## Cosmos Hub Cosmos Hub (名为 `gaia`) 文档已经迁移到[这里](https://github.com/cosmos/gaia/tree/master/docs). @@ -35,7 +36,3 @@ Cosmos-SDK 目前是用 [Golang](https://golang.org/)编写的, 尽管该框架 ## 贡献 参考 [文档说明](https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOCS_README.md) 了解构建细节及更新时注意事项。 - -## 版本 - - 这份文档通过以下提交构建: diff --git a/docs/cn/basics/README.md b/docs/cn/basics/README.md new file mode 100644 index 000000000000..5e1ea98ed4ff --- /dev/null +++ b/docs/cn/basics/README.md @@ -0,0 +1,10 @@ +# 基础文档 + +此目录包含对 cosmos sdk 的基础概念介绍 + +1. [SDK 应用解析](./app-anatomy.md) +2. [交易的生命周期](./tx-lifecycle.md) +3. [账户系统](./accounts.md) +4. [Gas 和 Fees](./gas-fees.md) + +阅读完基础文档后,可以阅读 [核心文档](../core/README.md) 进一步加深对 cosmos 的理解。 diff --git a/docs/cn/basics/accounts.md b/docs/cn/basics/accounts.md new file mode 100644 index 000000000000..c37e45aa69b7 --- /dev/null +++ b/docs/cn/basics/accounts.md @@ -0,0 +1,132 @@ +**原文路径:https://github.com/cosmos/cosmos-sdk/blob/master/docs/basics/accounts.md** + +# 账户系统 + +# 必备阅读 {hide} + +- [一个 SDK 程序的剖析](./app-anatomy.md) {prereq} + +## 账户定义 + +在 Cosmos SDK 中,一个账户是指定的一个公私钥对。公钥可以用于派生出 `Addresses`,`Addresses` 可以在程序里面的各个模块间区分不同的用户。`Addresses` 同样可以和[消息](../building-modules/messages-and-queries.md#messages)进行关联用于确定发消息的账户。私钥一般用于生成签名来证明一个消息是被一个 `Addresses`(和私钥关联的`Addresses`) 所发送。 + +Cosmos SDK 使用一套称之为 [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) 的标准来生成公私钥。这个标准定义了怎么去创建一个 HD 钱包(钱包就是一批账户的集合)。每一个账户的核心,都有一个种子,每一个种子都有一个 12 或 24 个字的助记符。使用这个助记符,使用一种单向的加密方法可以派生出任意数量的私钥。公钥可以通过私钥推导出来。当然,助记符是最敏感的信息,因为可以不停通过助记符来重新生成私钥。 + +```md + Account 0 Account 1 Account 2 + ++------------------+ +------------------+ +------------------+ +| | | | | | +| Address 0 | | Address 1 | | Address 2 | +| ^ | | ^ | | ^ | +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +| + | | + | | + | +| Public key 0 | | Public key 1 | | Public key 2 | +| ^ | | ^ | | ^ | +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +| + | | + | | + | +| Private key 0 | | Private key 1 | | Private key 2 | +| ^ | | ^ | | ^ | ++------------------+ +------------------+ +------------------+ + | | | + | | | + | | | + +--------------------------------------------------------------------+ + | + | + +---------+---------+ + | | + | Master PrivKey | + | | + +-------------------+ + | + | + +---------+---------+ + | | + | Mnemonic (Seed) | + | | + +-------------------+ +``` + +在 Cosmos SDK 中,账户可以在 [`Keybase`](#keybase) 中作为一个对象来储存和管理。 + +## Keybase + +`Keybase` 是储存和管理账户的对象,在 Cosmos SDK 中,`Keybase` 要实现以下接口 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/types.go#L13-L86 + +在 Cosmos SDK 中,`Keybase` 接口的默认实现对象是 `dbKeybase`。 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/keybase.go + +`dbKeybase` 上面对 `Keybase` 接口中方法实现的笔记: + +- `Sign(name, passphrase string, msg []byte) ([]byte, crypto.PubKey, error)` 对 `message` 字节进行签名。需要做一些准备工作将 `message` 编码成 []byte 类型,可以参考 `auth` 模块 `message` 准备的例子。注意,SDK 上面没有实现签名的验证,签名验证被推迟到[`anteHandler`](#antehandler)中进行 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L176-L209 + +- `CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)`创建一个新的助记符并打印在日志里,但是**并不保存在磁盘上** +- `CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)` 基于[`bip44 path`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)创建一个新的账户并将其保存在磁盘上。注意私钥在[保存前用密码加密](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/mintkey/mintkey.go),**永远不会储存未加密的私钥**.在这个方法的上下文中, `account`和 `address` 参数指的是 BIP44 派生路径的段(例如`0`, `1`, `2`, ...)用于从助记符派生出私钥和公钥(注意:给相同的助记符和 `account` 将派生出相同的私钥,给相同的 `account` 和 `address` 也会派生出相同的公钥和 `Address`)。最后注意 `CreateAccount` 方法使用在 [Tendermint library](https://github.com/tendermint/tendermint/tree/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1) 中的 `secp256k1` 派生出公私钥和 `Address`。总之,这个方法是用来创建用户的钥匙和地址的,并不是共识秘钥,参见[`Addresses`](#addresses) 获取更多信息 + +`dbKeybase` 的实现是最基本的,并没有根据需求提供锁定功能。锁定功能指如果一个`dbKeybase`实例被创建,底层的`db`就被锁定意味着除了实例化它的程序其他程序无法访问它。这就是 SDK 程序使用另外一套 `Keybase` 接口的实现 `lazyKeybase` 的原因 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/crypto/keys/lazy_keybase.go + +`lazyKeybase` 是 `dbKeybase` 的一个简单包装,它仅在要执行操作时锁定数据库,并在之后立即将其解锁。使用 `lazyKeybase`[命令行界面](../interfaces/cli.md) 可以在 [rest server](../interfaces/rest.md)运行时创建新的账户,它也可以同时传递多个 CLI 命令 + +## 地址和公钥 + +`Addresses` 和 `PubKey` 在程序里面都是标识一个参与者的公共信息。Cosmos SDK 默认提供 3 种类型的 `Addresses`和 `PubKey` + +- 基于用户的 `Addresses` 和 `PubKey`,用于指定用户(例如 `message` 的发送者)。它们通过 **`secp256k1`** 曲线推导出来 +- 基于验证节点的 `Addresses` 和 `PubKey` 用于指定验证者的操作员,它们通过 **`secp256k1`** 曲线推导出来 +- 基于共识节点的 `Addresses` 和 `PubKey` 用于指定参与共识的验证着节点,它们通过 **`ed25519`** 曲线推导出来 + +| | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length | +| ------------------ | --------------------- | -------------------- | ----------- | ------------------- | ------------------ | +| Accounts | cosmos | cosmospub | `secp256k1` | `20` | `33` | +| Validator Operator | cosmosvaloper | cosmosvaloperpub | `secp256k1` | `20` | `33` | +| Consensus Nodes | cosmosvalcons | cosmosvalconspub | `ed25519` | `20` | `32` | + +### 公钥 + +在 Cosmos SDK 里面 `PubKey` 遵循在 tendermint 的 `crypto` 包中定义的 `Pubkey` 接口 + ++++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/crypto.go#L22-L27 + +对于 `secp256k1` 类型的秘钥,具体的实现可以在[这里](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/secp256k1/secp256k1.go#L140)找到。对于`ed25519`类型的密钥,具体实现可以在[这里](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/crypto/ed25519/ed25519.go#L135)找到。 + +请注意,在 Cosmos SDK 中,`Pubkeys` 并非以其原始格式进行操作。它使用 [`Amino`](../core/encoding.md#amino) 和 [`bech32`](https://en.bitcoin.it/wiki/Bech32) 进行 2 次编码。在 SDK 里面,`Pubkeys` 首先调用 `Bytes()` 方法在原始的 `Pubkey` 中(这里面提供 amino 编码),然后使用 `bech32` 的 `ConvertAndEncode` 方法 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L579-L729 + +### 地址 + +在 Cosmos SDK 默认提送 3 种类型的地址 + +- `AccAddress` 用于账户 +- `ValAddress` 用于验证者操作员 +- `ConsAddress` 用于验证者节点 + +这些地址类型都是一种长度为 20 的十六进制编码的 `[]byte` 数组的别名,这里有一种标准方法从`Pubkey pub`中获取到地址`aa`. + +```go +aa := sdk.AccAddress(pub.Address().Bytes()) +``` + +这些地址实现了 `Address` 接口 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L71-L80 + +值得注意的是,`Marhsal()` 和 `Bytes()` 方法都返回相同的 `[]byte` 类型的地址,根据 protobuf 的兼容性要求我们需要前者。同样,`String()` 也被用来返回 `bech32` 编码类型的地址,这个应该是用户看到的最终编码形式。下面是一个例子: + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/address.go#L229-L243 + +## 接下来 {hide} + +学习[gas and fees](./gas-fees.md) {hide} diff --git a/docs/cn/basics/app-anatomy.md b/docs/cn/basics/app-anatomy.md new file mode 100644 index 000000000000..813e9c6c4445 --- /dev/null +++ b/docs/cn/basics/app-anatomy.md @@ -0,0 +1,235 @@ +# SDK 应用程序剖析 + +## Node Client + +全节点的核心进程是基于 SDK 包的。网络中的参与者运行此过程以初始化其状态机,与其他全节点连接并在新块进入时更新其状态机。 + +``` + ^ +-------------------------------+ ^ + | | | | + | | State-machine = Application | | + | | | | Built with Cosmos SDK + | | ^ + | | + | +----------- | ABCI | ----------+ v + | | + v | ^ + | | | | +Blockchain Node | | Consensus | | + | | | | + | +-------------------------------+ | Tendermint Core + | | | | + | | Networking | | + | | | | + v +-------------------------------+ v +``` + +区块链全节点以二进制形式表示,通常以 `-d` 后缀表示`守护程序`(例如,`appd` 表示 `app` 或 `gaiad` 表示 `gaia`)。这个二进制文件是通过编译一个简单的代码文件 main.go 构建的,`main.go` 通常位于`./cmd/appd/`中。 此操作通常通过用 Makefile 编译。 + +编译了二进制文件,就可以通过运行[`start`命令](https://docs.cosmos.network/master/core/node.html#start-command) 来启动节点。 此命令功能主要执行三件事: + +1. [`app.go`] 创建了一个状态机实例。 + +2. 用最新的已知状态初始化状态机,该状态机是从存储在 `~/.appd/data` 文件夹中的 db 中提取的。 此时,状态机的高度为:`appBlockHeight`。 + +3. 创建并启动一个新的 Tendermint 实例。 该节点将与对等节点进行连接交换信息。 它将从他们那里获取最新的 `blockHeight`,如果它大于本地的 `appBlockHeight`,则重播块以同步到该高度。 如果 `appBlockHeight` 为 `0`,则该节点从创世开始,并且 Tendermint 通过 ABCI 接口向 `app` 发送 `InitChain` 初始化链命令,从而触发 [`InitChainer`](https://docs.cosmos.network/master/basics/app-anatomy.html#initchainer)。 + +## Core Application File + +通常,状态机的核心是在名为 `app.go` 的文件中定义的。 它主要包含“应用程序的类型定义”和“创建和初始化它”的功能。 + +### Type Definition of the Application + +在 app.go 中重要的一个是应用程序的 type。 它通常由以下部分组成: + +- 在 `app.go` 中定义的自定义应用程序是 `baseapp` 的扩展。 当事务由 Tendermint 发送到应用程序时,`app` 使用 `baseapp` 的方法将它们转送到对应的模块。 baseapp 为应用程序实现了大多数核心逻辑,包括所有的 [ABCI 方法](https://tendermint.com/docs/spec/abci/abci.html#overview)和转送消息逻辑。 + +- 一条 key 链包含整个状态,他是基于 Cosmos SDK 的 multistore 实现的。 每个模块使用 multistore 的一个或多个存储来存储其状态。可以使用在 `app` 类型中声明的特定键来访问这些存储。这些密钥以及 `keepers` 是 Cosmos SDK 的对象功能模型的核心。 +- 模块 `keeper` 的列表。 每个模块都会抽象定义一个 keeper,该 keeper 实现模块存储的读写。 一个模块的 `keeper` 方法可以从其他模块(如果已授权)中调用,这就是为什么它们在应用程序的类型中声明并作为接口导出到其他模块的原因,以便后者只能访问授权的功能。 +- 应用程序的 `codec` 用于序列化和反序列化数据结构以便存储它们,因为存储只能持久化 `[]bytes`。 `编解码器`必须是确定性的。 默认编解码器为 amino +- 模块管理器是一个对象,其中包含应用程序模块的列表。 它简化了与这些模块相关的操作,例如注册 routes 操作,query route 操作或设置各种功能的模块之间顺序执行情况,例如 InitChainer 操作,BeginBlocke 操作和 EndBlocker 操作 +- 请参阅 [gaia](https://github.com/cosmos/gaia) 中的应用程序类型定义示例 + ++++ https://github.com/cosmos/gaia/blob/5bc422e6868d04747e50b467e8eeb31ae2fe98a3/app/app.go#L87-L115 + +### Constructor Function + +此函数构造了以上部分中定义的类型的新应用程序。在应用程的 start 命令中使用,它必须具有 AppCreator 签名。 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L20 + +以下是此功能执行的主要操作: + +- 创建初始化一个新的 codec 实例,并使用基础模块管理器初始化每个应用程序模块的 `codec`。 +- 使用 baseapp 实例,编解码器和所有适当的存储键的引用实例化一个新应用程序。 +- 使用每个应用程序模块的 `NewKeeper` 功能实例化在应用程序的`类型`中定义的所有 keeper。 注意,所有 keeper 必须以正确的顺序实例化,因为一个模块的 NewKeeper 可能需要引用另一个模块的 `keeper`。 +- 使用每个应用模块的 AppModule 来实例化应用程序的模块管理器 +- 使用模块管理器,初始化应用程序的 routes 和 query route。 当事务由 Tendermint 通过 ABCI 中继到应用程序时,它使用此处定义的路由被路由到相应模块的回调 handler。 同样,当应用程序收到查询时,使用此处定义的查询路由将其路由到适当的模块的 querier。 +- 使用模块管理器,注册应用程序的模块的 invariants。 invariants 是在每个块末尾评估的变量(例如 token 的总供应量)。 检查不变式的过程是通过 InvariantsRegistry 的特殊模块完成的。 invariants 应等于模块中定义的预测值。 如果该值与预测的值不同,则将触发不变注册表中定义的特殊逻辑(通常会中断链)。这对于确保不会发现任何严重错误并产生难以修复的长期影响非常有用。 +- 使用模块管理器,在每个应用程序的模块 的 InitGenesis,BegingBlocker 和 EndBlocker 函数之间设置执行顺序。 请注意,并非所有模块都实现这些功能。 +- 模块实现这些功能。 +- 设置其余的应用程序参数: + - `InitChainer` 于在应用程序首次启动时对其进行初始化。 + - `BeginBlocker`,`EndBlocker`:在每个块的开始和结尾处调用。 + - `anteHandler`:用于处理费用和签名验证。 +- 挂载存储. +- 返回应用实例. + +请注意,此函数仅创建该应用的一个实例,而如果重新启动节点,则状态将从 `〜/.appd/data` 文件夹中保留下来状态加载,如果节点是第一次启动,则从创世文件生成。See an example of application constructor from [`gaia`](https://github.com/cosmos/gaia): + ++++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222 + +### InitChainer + +InitChainer 用于根据创始文件(即创始账户的代币余额)初始化应用程序的状态。 当应用程序从 Tendermint 引擎收到`InitChain`消息时调用该消息,该消息是在节点以`appBlockHeight == 0`(即创世)启动。 应用程序必须通过[`SetInitChainer`](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetInitChainer)方法设置其[constructor](https://docs.cosmos.network/master/basics/app-anatomy.html#constructor-function)中的`Initchainer`。 + +通常,`InitChainer`主要由每个应用程序模块的 InitGenesis 函数组成。 这是通过调用模块管理器的 InitGenesis 函数来完成的,而模块管理器的 InitGenesis 函数将依次调用其包含的每个模块的 InitGenesis 函数。 请注意,必须使用模块管理器的 SetOrderInitGenesis 方法设置模块的 InitGenesis 函数的顺序。 这是在 应用程序的构造函数 application-constructor 中完成的,必须在 SetInitChainer 之前调用 SetOrderInitGenesis。 + +查看来自[gaia](https://github.com/cosmos/gaia)的 InitChainer 的示例: + +See an example of an `InitChainer` from [`gaia`](https://github.com/cosmos/gaia): + +查看来自 [`gaia`](https://github.com/cosmos/gaia)的 `InitChainer` 的示例: ++++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L235-L239 + +### BeginBlocker and EndBlocker + +该 SDK 为开发人员提供了在其应用程序中实现自定义代码可能性。 这是通过两个名为 `BeginBlocker` 和 `EndBlocker` 的函数实现的。当应用程序分别从 Tendermint 引擎接收到 `BeginBlock` 和 `EndBlock` 消息时,将调用它们,它们分别在每个块的开始和结尾处发生。应用程序必须通过 [SetBeginBlocker](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp) 和 [SetEndBlocker](https://godoc.org/github.com/cosmos/cosmos-sdk/baseapp#BaseApp.SetEndBlocker) 方法在其 constructor 中设置 `BeginBlocker` 和 `EndBlocker`。 + +通常,`BeginBlocker` 和 `EndBlocker` 函数主要由每个应用程序模块的 `BeginBlock` 和 `EndBlock` 函数组成。 这是通过调用模块管理器的 BeginBlock 和 EndBlock 函数来完成的,而后者又会调用其包含的每个模块的 BeginBLock 和 EndBlock 函数。 请注意,必须分别在模块管理器中使用 SetOrderBeginBlock 和 SetOrderEndBlock 方法来设置模块的 BegingBlock 和 EndBlock 函数必须调用的顺序。这是通过应用程序的构造函数中的模块管理器完成的,必须调用 SetOrderBeginBlock 和 SetOrderEndBlock 方法。 在 SetBeginBlocker 和 SetEndBlocker 函数之前。 + +附带说明,请记住特定于应用程序的区块链是确定性的,这一点很重要。开发人员必须注意不要在 BeginBlocker 或 EndBlocker 中引入不确定性,还必须注意不要使它们在计算上过于昂贵,因为[gas]不会限制计算代价当调用 BeginBlocker 和 EndBlocker 执行。 + +请参阅 [gaia](https://github.com/cosmos/gaia)中的 `BeginBlocker` 和 `EndBlocker` 函数的示例。 + ++++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L224-L232 + +### Register Codec + +MakeCodec 函数是 app.go 文件的最后一个重要功能。 此函数的目的是使用 RegisterLegacyAminoCodec 函数实例化 codec`cdc`,例如 amino 初始化 SDK 的编解码器以及每个应用程序的模块。 + +为了注册应用程序的模块,`MakeCodec` 函数在 `ModuleBasics` 上调用 `RegisterLegacyAminoCodec`。`ModuleBasics` 是一个基本管理器,其中列出了应用程序的所有模块。 它在`init()`函数中得到实例化,仅用于注册应用程序模块的非依赖元素(例如编解码器)。 要了解有关基本模块管理器的更多信息,请点击[这里](https://docs.cosmos.network/master/building-modules/module-manager.html#basicmanager)。 + +请参阅 [gaia](https://github.com/cosmos/gaia) 中的 `MakeCodec` 示例: + ++++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L64-L70 + +## Modules + +Modules 是 SDK 应用程序的灵魂。它们可以被视为状态机中的状态机。当交易通过 ABCI 从底层的 Tendermint 引擎中继到应用程序时,它由 baseapp 找到对应的模块以便进行处理。这种范例使开发人员可以轻松构建复杂的状态机,因为他们所需的大多数模块通常已经存在。对于开发人员而言,构建 SDK 应用程序所涉及的大部分工作都围绕构建其应用程序尚不存在的自定义模块,并将它们与已经存在的模块集成到一个统一的应用程序中。在应用程序目录中,标准做法是将模块存储在 `x/` 文件夹中(不要与 SDK 的`x/`文件夹混淆,该文件夹包含已构建的模块)。 + +### Application Module Interface + +模块必须实现 Cosmos SDK AppModuleBasic 中的 [interfaces](https://docs.cosmos.network/master/building-modules/module-manager.html#application-module-interfaces) 和 AppModule。 前者实现了模块的基本非依赖性元素,例如`编解码器`,而后者则处理了大部分模块方法(包括需要引用其他模块的`keeper`的方法)。`AppModule` 和 `AppModuleBasic` 类型都在名为 `module.go` 的文件中定义。 + +AppModule 在模块上公开了一组有用的方法,这些方法有助于将模块组合成一个一致的应用程序。 这些方法是从模块管理器中调用的,该模块管理应用程序的模块集合。 + +### Message Types + +每个 `module` 定义 [messages](https://docs.cosmos.network/master/building-modules/messages-and-queries.html#messages) 接口。 每个 `transaction` 包含一个或多个 `messages`。 + +当全节点接收到有效的交易块时,Tendermint 通过 [`DeliverTx`](https://tendermint.com/docs/app-dev/abci-spec.html#delivertx) 将每个交易发到应用程序。然后,应用程序处理事务: + +- 收到交易后,应用程序首先从 `[]bytes` 反序列化得到。 +- 然后,在提取交易中包含的消息之前,它会验证有关交易的一些信息,例如费用支付和签名。 +- 使用 message 的 Type()方法,baseapp 可以将其发到对应模块的回调 handler 以便对其进行处理。 +- 如果消息已成功处理,则状态将更新。 + +有关事务生命周期的更多详细信息,请看[这里](./ tx-lifecycle.md)。 + +模块开发人员在构建自己的模块时会创建自定义消息类型。 通常的做法是在消息的类型声明前加上 `Msg`。 例如,消息类型 `MsgSend` 允许用户传输 tokens: + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/bank/internal/types/msgs.go#L10-L15 + +它由 `bank` 模块的回调 `handler` 处理,最终会调用 `auth` 模块来写 `keeper` 以更新状态。 + +### Handler + +回调 `handler` 是指模块的一部分,负责处理 `baseapp` 传递的 `message` 消息。 仅当通过 ABCI 接口的 DeliverTx 消息从 Tendermint 收到事务时,才执行模块的`处理程序`功能。 如果通过 CheckTx,仅执行无状态检查和与费用相关的有状态检查。为了更好地理解 `DeliverTx` 和 `CheckTx` 之间的区别以及有状态和无状态检查之间的区别,请看[这里](./tx-lifecycle.md)。 + +模块的`处理程序`通常在名为 `handler.go` 的文件中定义,并包括: + +- NewHandler 将消息发到对应的回调 `handler`。 该函数返回一个 `handler` 函数,此前这个函数在 `AppModule` 中注册,以在应用程序的模块管理器中用于初始化应用程序的路由器。接下来是 [nameservice tutorial](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice) 的一个例子。 + ++++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32 + +- 模块定义的每种消息类型的处理函数。开发人员在这些函数中编写消息处理逻辑。这通常包括进行状态检查以确保消息有效,并调用 [`keeper`](https://docs.cosmos.network/master/basics/app-anatomy.html#keeper) 的方法来更新状态。 + +处理程序函数返回结果类型为 sdk.Result,该结果通知应用程序消息是否已成功处理: + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/result.go#L15-L40 + +### Querier + +`Queriers` 与 `handlers` 非常相似,除了它们向状态查询用户而不是处理事务。 最终用户从 interface 发起 query,最终用户会提供 `queryRoute` 和一些 `data`。 然后使用 `queryRoute` 通过 `baseapp` 的 `handleQueryCustom` 方法查询到正确的应用程序的 `querier` 函数 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/abci.go#L395-L453 + +模块的 Querier 是在名为 querier.go 的文件中定义的,包括: + +- NewQuerier 将查找到对应 query 函数。 此函数返回一个 `querier` 函数,此前它在 AppModule 中注册,以在应用程序的模块管理器中用于初始化应用程序的查询路由器。请参阅 [nameservice demo](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice)中的此类切换示例: + +++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/x/nameservice/internal/keeper/querier.go#L19-L32 + +- 对于模块定义的每种需要查询的数据类型,都具有一个查询器功能。开发人员在这些函数中编写查询处理逻辑。这通常涉及调用 `keeper` 的方法来查询状态并将其序列化为 JSON。 + +### Keeper + +[`Keepers`](https://docs.cosmos.network/master/building-modules/keeper.html)是其模块存储器件。要在模块的存储区中进行读取或写入,必须使用其 `keeper` 方法之一。这是由 Cosmos SDK 的 object-capabilities 模型确保的。 只有持有存储密钥的对象才能访问它,只有模块的 `keeper` 才应持有该模块存储的密钥。 + +`Keepers` 通常在名为 `keeper.go` 的文件中定义。 它包含 `keeper` 的类型定义和方法。 + +`keeper` 类型定义通常包括: + +- 多重存储中模块存储的`密钥`。 + - 参考**其他模块的`keepers`**。 仅当 `keeper` 需要访问其他模块的存储(从它们读取或写入)时才需要。 +- 对应用程序的`编解码器`的引用。 `keeper` 需要它在存储结构之前序列化处理,或在检索它们时将反序列化处理,因为存储仅接受 `[]bytes` 作为值。 + +与类型定义一起,keeper.go 文件的一个重要组成部分是 Keeper 的构造函数 NewKeeper。 该函数实例化上面定义的类型的新 `keeper`,并带有 `codec`,存储 `keys` 以及可能引用其他模块的 `keeper` 作为参数。从应用程序的构造函数中调用 `NewKeeper` 函数。文件的其余部分定义了 `keeper` 的方法,主要是 getter 和 setter。 + +### Command-Line and REST Interfaces + +每个模块都定义了 application-interfaces 向用户公开的命令行命令和 REST routes。 用户可以创建模块中定义的类型的消息,或查询模块管理的状态的子集。 + +#### CLI + +通常,与模块有关的命令在模块文件夹中名为 `client/cli` 的文件夹中定义。CLI 将命令分为交易和查询两类,分别在 `client/cli/tx.go` 和 `client/cli/query.go` 中定义。这两个命令基于 [Cobra Library](https://github.com/spf13/cobra)之上: + +- Transactions 命令使用户可以生成新的事务,以便可以将它们包含在块中并更新状态。应该为模块中定义的每个消息类型 message-types 创建一个命令。该命令使用户提供的参数调用消息的构造函数,并将其包装到事务中。SDK 处理签名和其他事务元数据的添加。 +- 用户可以查询模块定义的状态子集。查询命令将查询转发到应用程序的查询路由器,然后将查询路由到提供的`queryRoute`参数的相应 querier。 + +#### REST + +模块的 REST 接口允许用户生成事务并通过对应用程序的 light client daemon(LCD) 查询状态。 REST 路由在 `client/rest/rest.go` 文件中定义,该文件包括: + +- `RegisterRoutes` 函数,用于注册路由。从主应用程序的接口 application-interfaces 中为应用程序内使用的每个模块调用此函数。SDK 中使用的路由器是 [Gorilla's mux](https://github.com/gorilla/mux)。 +- 需要公开的每个查询或事务创建功能的自定义请求类型定义。这些自定义请求类型基于 Cosmos SDK 的基本`请求`类型构建: + +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/rest/rest.go#L47-L60 + +- 每个请求的一个处理函数可以找到给定的模块。 这些功能实现了服务请求所需的核心逻辑。 + +## Application Interface + +Interfaces 允许用户与全节点客户端进行交互。 这意味着从全节点查询数据,或者接受全节点中包含在块中的新事务。 + +通过汇总在应用程序使用的每个模块中定义的 CLI 命令构建 SDK 应用程序的 CLI。 应用程序的 CLI 通常具有后缀-cli(例如 appcli),并在名为`cmd / appcli / main.go`的文件中定义。 该文件包含: + +- main()函数,用于构建 appcli 接口客户端。这个函数准备每个命令,并在构建它们之前将它们添加到`rootCmd`中。在 appCli 的根部,该函数添加了通用命令,例如 status,keys 和 config,查询命令,tx 命令和 rest-server。 +- 查询命令是通过调用`queryCmd`函数添加的,该函数也在 appcli / main.go 中定义。此函数返回一个 Cobra 命令,其中包含在每个应用程序模块中定义的查询命令(从`main()`函数作为`sdk.ModuleClients`数组传递),以及一些其他较低级别的查询命令,例如阻止或验证器查询。查询命令通过使用 CLI 的命令“ appcli query [query]”来调用。 +- 通过调用`txCmd`函数来添加**交易命令**。与`queryCmd`类似,该函数返回一个 Cobra 命令,其中包含在每个应用程序模块中定义的 tx 命令,以及较低级别的 tx 命令,例如事务签名或广播。使用 CLI 的命令`appcli tx [tx]`调用 Tx 命令。 +- registerRoutes 函数,在初始化 轻客户端(LCD)时从 main()函数调用。 “ registerRoutes”调用应用程序每个模块的“ RegisterRoutes”功能,从而注册该模块 routes 到 LCD 的查询路由。可以通过运行以下命令“ appcli rest-server”来启动 LCD。 + +从[nameservice demo](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice)中查看应用程序的主要命令行文件的示例。 + ++++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/cmd/nscli/main.go + +## Dependencies and Makefile + +因为开发人员可以自由选择其依赖项管理器和项目构建方法。 也就是说,当前最常用的版本控制框架是[`go.mod`](https://github.com/golang/go/wiki/Modules)。 它确保在整个应用程序中使用的每个库都以正确的版本导入。 请参阅[demo](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice)中的示例: + ++++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/go.mod#L1-L18 + +为了构建应用程序,通常使用[Makefile](https://en.wikipedia.org/wiki/Makefile)。 Makefile 主要确保在构建应用程序的两个入口点 [`appd`](https://docs.cosmos.network/master/basics/app-anatomy.html#node-client) 和 [`appcli`](https://docs.cosmos.network/master/basics/app-anatomy.html#application-interface) 之前运行 `go.mod`。 请参阅 nameservice demo 中的 Makefile 示例 + ++++ https://github.com/cosmos/sdk-tutorials/blob/86a27321cf89cc637581762e953d0c07f8c78ece/nameservice/Makefile + +## Next + +了解有关[交易生命周期](./ tx-lifecycle.md)的更多信息 diff --git a/docs/cn/basics/gas-fees.md b/docs/cn/basics/gas-fees.md new file mode 100644 index 000000000000..8c8441a760ea --- /dev/null +++ b/docs/cn/basics/gas-fees.md @@ -0,0 +1,84 @@ +**原文路径:https://github.com/cosmos/cosmos-sdk/blob/master/docs/basics/gas-fees.md** + +# Gas and Fees + +## 必备阅读 {hide} + +- [一个 SDK 程序的剖析](./app-anatomy.md) {prereq} + +## `Gas` and `Fees`的介绍 + +在 Cosmos SDK 中,`gas`是一种特殊的单位,用于跟踪执行期间的资源消耗。每当对储存进行读写操作的时候会消耗`gas`,如果要进行比较复杂的计算的话也会消耗`gas`。它主要有两个目的: + +- 确保区块不会消耗太多资源而且能顺利完成。这个默认在 SDK 的 [block gas meter](#block-gas-meter) 中保证 +- 防止来自终端用户的垃圾消息和滥用。为此,通常会为 [`message`](../building-modules/messages-and-queries.md#messages) 执行期间的消耗进行定价,并产生 `fee`(`fees = gas * gas-prices`)。`fees` 通常必须由 `message` 的发送者来支付。请注意,SDK 并没有强制执行对 `gas` 定价,因为可能会有其他的方法来防止垃圾消息(例如带宽方案)。尽管如此,大多数应用程序仍然会使用`fee` 方式来防止垃圾消息。这个机制通过 [`AnteHandler`](#antehandler) 来完成. + +## Gas Meter + +在 Cosmos SDK 中 `gas` 是一种简单的 `uint64` 类型,被称之为 `gas meter` 的对象进行管理,Gas meters 实现了 `GasMeter` 接口 + ++++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L31-L39 + +这里: + +- `GasConsumed()` 返回 `GasMeter` 实例中消耗的 `gas` 的数量 +- `GasConsumedToLimit()` 返回 `GasMeter` 实例消耗的 gas 数量,如果达到上限的话就返回上限 +- `Limit()` 返回 `GasMeter` 实例的上限,如果是 0 则表示对 `gas` 的数量没有限制 +- `ConsumeGas(amount Gas, descriptor string)` 消耗提供的 `gas`,如果 `gas` 溢出了,使用 `descriptor` 内容进行报错,如果 `gas` 并不是无限的,则超过限制就会报错。 +- `IsPastLimit()` 如果 `gas` 消耗超过了 `GasMeter` 的限制则返回 `true`,其它返回 `false` +- `IsOutOfGas()` 如果 `gas` 消耗大于或等于了 `GasMeter` 的限制则返回 `true`,其它返回 `false` + + `GasMeter` 通常保存在 [`ctx`](../core/context.md) 中,`gas` 消耗的方式如下: + +```go +ctx.GasMeter().ConsumeGas(amount, "description") +``` + +通常,Cosmos SDK 使用两种不同的 `GasMeter`,[main gas meter](#main-gas-metter[) 和 [block gas meter](#block-gas-meter)。 + +### 主 Gas Meter + +`ctx.GasMeter()` 是应用程序的主 `GasMeter`,主 `GasMeter` 通过 `BeginBlock` 中的 `setDeliverState` 进行初始化,然后跟踪导致状态转换的执行序列中的 `gas` 消耗。也即是说它的更新由 [`BeginBlock`](../core/baseapp.md#beginblock),[`DeliverTx`](../core/baseapp.md#delivertx) 和 [`EndBlock`](../core/baseapp.md#endblock) 进行操作。主 `GasMeter` 必须在 [`AnteHandler`](#antehandler)中 **设置为 0**,以便它能获取每个 transaction 的 Gas 消耗 + +`gas`消耗可以手工完成,模块开发者通常在 [`BeginBlocker`,`EndBlocker`](../building-modules/beginblock-endblock.md) 或者 [`handler`](../building-modules/handler.md) 上执行,但大多数情况下,只要对储存区进行了读写,它就会自动完成。这种自动消耗的逻辑在[`GasKv`](../core/store.md#gaskv-store)中完成. + +### 块 Gas Meter + +`ctx.BlockGasMeter()` 是跟踪每个区块 `gas` 消耗并保证它没有超过限制的 `GasMeter`。每当 [`BeginBlock`](../core/baseapp.md#beginblock) 被调用的时候一个新的 `BlockGasMeter` 实例将会被创建。`BlockGasMeter` 的 `gas` 是有限的,每个块的 `gas` 限制应该在应用程序的共识参数中定义,Cosmos SDK 应用程序使用 Tendermint 提供的默认共识参数: + ++++ https://github.com/tendermint/tendermint/blob/f323c80cb3b78e123ea6238c8e136a30ff749ccc/types/params.go#L65-L72 + +当通过 `DeliverTx` 处理新的 [transaction](../core/transactions.md) 的时候,`BlockGasMeter` 的当前值会被校验是否超过上限,如果超过上限,`DeliverTx` 直接返回,由于 `BeginBlock` 会消耗 `gas`,这种情况可能会在第一个 `transaction` 到来时发生,如果没有发生这种情况,`transaction`将会被正常的执行。在 `DeliverTx` 的最后,`ctx.BlockGasMeter()` 会追踪 `gas` 消耗并将它增加到处理 `transaction` 的 `gas` 消耗中. + +```go +ctx.BlockGasMeter().ConsumeGas( + ctx.GasMeter().GasConsumedToLimit(), + "block gas meter", +) +``` + +## AnteHandler + +`AnteHandler` 是一个特殊的处理程序,它在 `CheckTx` 和 `DeliverTx` 期间为每一个 `transaction` 的每个 `message` 处理之前执行。`AnteHandler` 相比 `handler` 有不同的签名: + +```go +// AnteHandler authenticates transactions, before their internal messages are handled. +// If newCtx.IsZero(), ctx is used instead. +type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, result Result, abort bool) +``` + +`AnteHandler` 不是在核心 SDK 中实现的,而是在每一个模块中实现的,这使开发者可以使用适合其程序需求的`AnteHandler`版本,也就是说当前大多数应用程序都使用 [`auth` module](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) 中定义的默认实现。下面是 `AnteHandler` 在普通 Cosmos SDK 程序中的作用: + +- 验证事务的类型正确。事务类型在实现 `anteHandler` 的模块中定义,它们遵循事务接口: + + +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L33-L41 + + 这使开发人员可以使用各种类型的应用程序进行交易。 在默认的 auth 模块中,标准事务类型为 StdTx: + + +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/stdtx.go#L22-L29 + +- 验证交易中包含的每个 [`message`](../building-modules/messages-and-queries.md#messages) 的签名,每个 `message` 应该由一个或多个发送者签名,这些签名必须在 `anteHandler` 中进行验证. +- 在 `CheckTx` 期间,验证 `transaction` 提供的 `gas prices` 是否大于本地配置 `min-gas-prices`(提醒一下,`gas-prices` 可以从以下等式中扣除`fees = gas * gas-prices`)`min-gas-prices` 是每个独立节点的本地配置,在`CheckTx`期间用于丢弃未提供最低费用的交易。这确保了内存池不会被垃圾交易填充. +- 设置 `newCtx.GasMeter` 到 0,限制为`GasWanted`。**这一步骤非常重要**,因为它不仅确保交易不会消耗无限的天然气,而且还会在每个 `DeliverTx` 重置 `ctx.GasMeter`(每次 `DeliverTx` 被调用的时候都会执行 `anteHandler`,`anteHandler` 运行之后 `ctx` 将会被设置为 `newCtx`) + +如上所述,`anteHandler` 返回 `transaction` 执行期间所能消耗的最大的 `gas` 数量,称之为 `GasWanted`。最后实际 `gas` 消耗数量记为 `GasUsed`,因此我们必须使 `GasUsed =< GasWanted`。当返回时 `GasWanted` 和 `GasUsed` 都会被中继到共识引擎中. diff --git a/docs/cn/basics/tx-lifecycle.md b/docs/cn/basics/tx-lifecycle.md new file mode 100644 index 000000000000..cae4644fa770 --- /dev/null +++ b/docs/cn/basics/tx-lifecycle.md @@ -0,0 +1,162 @@ +# Transaction 的生命周期 + +本文档描述了 Transaction 从创建到提交的生命周期,Transaction 的定义在[其他文档](https://docs.cosmos.network/master/core/transactions.html)中有详细描述,后文中 Transaction 将统一被称为`Tx`。 + +## 创建 + +### Transaction 的创建 + +命令行界面是主要的应用程序界面之一,`Tx` 可以由用户输入[以下命令](https://docs.cosmos.network/master/interfaces/cli.html)来创建,其中 `[command]` 是 `Tx` 的类型,`[args]` 是相关参数,`[flags]` 是相关配置例如 gas price: + +```bash +[appname] tx [command] [args] [flags] +``` + +此命令将自动**创建** `Tx`,使用帐户的私钥对其进行**签名**,并将其**广播**到其他节点。 + +创建 `Tx` 有一些必需的和可选的参数,其中 `--from` 指定该 `Tx` 的发起[账户](https://docs.cosmos.network/master/basics/accounts.html),例如一个发送代币的`Tx`,则将从 `from` 指定的账户提取资产。 + +#### Gas 和 Fee + +此外,用户可以使用这几个[参数](https://docs.cosmos.network/master/interfaces/cli.html)来表明他们愿意支付多少 [fee](https://docs.cosmos.network/master/basics/gas-fees.html): + +- `--gas` 指的是 [gas](https://docs.cosmos.network/master/basics/gas-fees.html) 的数量,gas 代表 `Tx` 消耗的计算资源,需要消耗多少 gas 取决于具体的 `Tx`,在 `Tx` 执行之前无法被精确计算出来,但可以通过在 `--gas` 后带上参数 `auto` 来进行估算。 +- `--gas-adjustment`(可选)可用于适当的增加 `gas`,以避免其被低估。例如,用户可以将 `gas-adjustment` 设为 1.5,那么被指定的 gas 将是被估算 gas 的 1.5 倍。 +- `--gas-prices` 指定用户愿意为每单位 gas 支付多少 fee,可以是一种或多种代币。例如,`--gas-prices=0.025uatom, 0.025upho` 就表明用户愿意为每单位的 gas 支付 0.025uatom 和 0.025upho。 +- `--fees` 指定用户总共愿意支付的 fee。 + +所支付 fee 的最终价值等于 gas 的数量乘以 gas 的价格。换句话说,`fees = ceil(gas * gasPrices)`。由于可以使用 gas 价格来计算 fee,也可以使用 fee 来计算 gas 价格,因此用户仅指定两者之一即可。 + +随后,验证者通过将给定的或计算出的 `gas-prices` 与他们本地的 `min-gas-prices` 进行比较,来决定是否在其区块中写入该 `Tx`。如果 `gas-prices` 不够高,该 `Tx` 将被拒绝,因此鼓励用户支付更多 fee。 + +#### CLI 示例 + +应用程序的用户可以在其 CLI 中输入以下命令,用来生成一个将 1000uatom 从 `senderAddress` 发送到 `recipientAddress` 的 `Tx`,该命令指定了用户愿意支付的 gas(其中 gas 数量为自动估算的 1.5 倍,每单位 gas 价格为 0.025uatom)。 + +```bash +appcli tx send 1000uatom --from --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom +``` + +#### 其他的 Transaction 创建方法 + +命令行是与应用程序进行交互的一种简便方法,但是 `Tx` 也可以使用 [REST interface](https://docs.cosmos.network/master/interfaces/rest.html) 或应用程序开发人员定义的某些其他入口点来创建命令行。从用户的角度来看,交互方式取决于他们正在使用的是页面还是钱包(例如, `Tx` 使用 [Lunie.io](https://lunie.io/#/) 创建并使用 Ledger Nano S 对其进行签名)。 + +## 添加到交易池 + +每个全节点(Tendermint 节点)接收到 `Tx` 后都会发送一个名为 `CheckTx` 的 [ABCI message](https://tendermint.com/docs/spec/abci/abci.html#messages),用来检查 `Tx` 的有效性,`CheckTx` 会返回 `abci.ResponseCheckTx`。 +如果 `Tx` 通过检查,则将其保留在节点的 [**交易池**](https://tendermint.com/docs/tendermint-core/mempool.html#mempool)(每个节点唯一的内存事务池)中等待出块,`Tx` 如果被发现无效,诚实的节点将丢弃该 `Tx`。在达成共识之前,节点会不断检查传入的 `Tx` 并将其广播出去。 + +### 检查的类型 + +全节点在 `CheckTx` 期间对 `Tx` 先执行无状态检查,然后进行有状态检查,目的是尽早识别并拒绝无效 `Tx`,以免浪费计算资源。 + +**_无状态检查_**不需要知道节点的状态,即轻客户端或脱机节点都可以检查,因此计算开销较小。无状态检查包括确保地址不为空、强制使用非负数、以及定义中指定的其他逻辑。 + +**_状态检查_**根据提交的状态验证 `Tx` 和 `Message`。例如,检查相关值是否存在并能够进行交易,账户是否有足够的资产,发送方是否被授权或拥有正确的交易所有权。在任何时刻,由于不同的原因,全节点通常具有应用程序内部状态的[多种版本](https://docs.cosmos.network/master/core/baseapp.html#volatile-states)。例如,节点将在验证 `Tx` 的过程中执行状态更改,但仍需要最后的提交状态才能响应请求,节点不能使用未提交的状态更改来响应请求。 + +为了验证 `Tx`,全节点调用的 `CheckTx` 包括无状态检查和有状态检查,进一步的验证将在 [`DeliverTx`](#delivertx) 阶段的后期进行。其中 `CheckTx` 从对 `Tx` 进行解码开始。 + +### 解码 + +当 `Tx` 从应用程序底层的共识引擎(如 Tendermint)被接收时,其仍处于 `[]byte`[编码](https://docs.cosmos.network/master/core/encoding.html) 形式,需要将其解码才能进行操作。随后,[`runTx`](https://docs.cosmos.network/master/core/baseapp.html#runtx-and-runmsgs) 函数会被调用,并以 `runTxModeCheck` 模式运行,这意味着该函数将运行所有检查,但是会在执行 `Message` 和写入状态更改之前退出。 + +### ValidateBasic + +[Message](https://docs.cosmos.network/master/core/transactions.html#messages) 是由 module 的开发者实现的 `Msg` 接口中的一个方法。它应包括基本的**无状态**完整性检查。例如,如果 `Message` 是要将代币从一个账户发送到另一个账户,则 `ValidateBasic` 会检查账户是否存在,并确认账户中代币金额为正,但不需要了解状态,例如帐户余额。 + +### AnteHandler + +[`AnteHandler`](https://docs.cosmos.network/master/basics/gas-fees.html#antehandler)是可选的,但每个应用程序都需要定义。`AnteHandler` 使用副本为特定的 `Tx` 执行有限的检查,副本可以使对 `Tx` 进行状态检查时无需修改最后的提交状态,如果执行失败,还可以还原为原始状态。 + +例如,[`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) 模块的 `AnteHandler` 检查并增加序列号,检查签名和帐号,并从 `Tx` 的第一个签名者中扣除费用,这个过程中所有状态更改都使用 `checkState` + +### Gas + +[`Context`](https://docs.cosmos.network/master/core/context.html) 相当于`GasMeter`,会计算出在 `Tx` 的执行过程中多少 `gas` 已被使用。用户提供的 `Tx` 所需的 `gas` 数量称为 `GasWanted`。`Tx` 在实际执行过程中消耗的 `gas` 被称为`GasConsumed`,如果 `GasConsumed` 超过 `GasWanted`,将停止执行,并且对状态副本的修改不会被提交。否则,`CheckTx` 设置 `GasUsed` 等于 `GasConsumed` 并返回结果。在计算完 gas 和 fee 后,验证器节点检查用户指定的值 `gas-prices` 是否小于其本地定义的值 `min-gas-prices`。 + +### 丢弃或添加到交易池 + +如果在 `CheckTx` 期间有任何失败,`Tx` 将被丢弃,并且 `Tx` 的生命周期结束。如果 `CheckTx` 成功,则 `Tx` 将被广播到其他节点,并会被添加到交易池,以便成为待出区块中的候选 `Tx`。 + +**交易池**保存所有全节点可见的 `Tx`,全节点会将其最近的 `Tx` 保留在**交易池缓存**中,作为防止重放攻击的第一道防线。理想情况下,`mempool.cache_size` 的大小足以容纳整个交易池中的所有 `Tx`。如果交易池缓存太小而无法跟踪所有 `Tx`,`CheckTx` 会识别出并拒绝重放的 `Tx`。 + +现有的预防措施包括 fee 和`序列号`计数器,用来区分重放 `Tx` 和相同的 `Tx`。如果攻击者尝试向某个节点发送多个相同的 `Tx`,则保留交易池缓存的完整节点将拒绝相同的 `Tx`,而不是在所有 `Tx` 上运行 `CheckTx`。如果 `Tx` 有不同的`序列号`,攻击者会因为需要支付费用而取消攻击。 + +验证器节点与全节点一样,保留一个交易池以防止重放攻击,但它也用作出块过程中未经验证的交易池。请注意,即使 `Tx` 在此阶段通过了所有检查,仍然可能会被发现无效,因为 `CheckTx` 没有完全验证 `Tx`(`CheckTx` 实际上并未执行 `message`)。 + +## 写入区块 + +共识是验证者节点就接受哪些 `Tx` 达成协议的过程,它是**反复进行**的。每个回合都始于出块节点创建一个包含最近 `Tx` 的区块,并由验证者节点(具有投票权的特殊全节点)负责达成共识,同意接受该区块或出一个空块。验证者节点执行共识算法,例如[Tendermint BFT](https://tendermint.com/docs/spec/consensus/consensus.html#terms),调用 ABCI 请求确认 `Tx`,从而达成共识。 + +达成共识的第一步是**区块提案**,共识算法从验证者节点中选择一个出块节点来创建和提议一个区块,用来写入 `Tx`,`Tx` 必须在该提议者的交易池中。 + +## 状态变更 + +共识的下一步是执行 `Tx` 以完全验证它们,所有的全节点收到出块节点广播的区块并调用 ABCI 函数[`BeginBlock`](https://docs.cosmos.network/master/basics/app-anatomy.html#beginblocker-and-endblocker),`DeliverTx`,和 [`EndBlock`](https://docs.cosmos.network/master/basics/app-anatomy.html#beginblocker-and-endblocker)。全节点在本地运行的每个过程将产生一个明确的结果,因为 `message` 的状态转换是确定性的,并且 `Tx` 在提案中有明确的顺序。 + +``` + ----------------------------- + |Receive Block Proposal| + ----------------------------- + | + v + ----------------------------- + | BeginBlock | + ----------------------------- + | + v + ----------------------------- + | DeliverTx(tx0) | + | DeliverTx(tx1) | + | DeliverTx(tx2) | + | DeliverTx(tx3) | + | . | + | . | + | . | + ----------------------------- + | + v + ----------------------------- + | EndBlock | + ----------------------------- + | + v + ----------------------------- + | Consensus | + ----------------------------- + | + v + ----------------------------- + | Commit | + ----------------------------- +``` + +### DeliverTx + +[`baseapp`](https://docs.cosmos.network/master/core/baseapp.html) 中定义的 ABCI 函数 `DeliverTx` 会执行大部分状态转换,`DeliverTx` 会针对共识中确定的顺序,对块中的每个 `Tx` 按顺序运行。`DeliverTx` 几乎和 `CheckTx` 相同,但是会以 deliver 模式调用[`runTx`](../core/baseapp.md#runtx)函数而不是 check 模式。全节点不使用 `checkState`,而是使用 `deliverState`。 + +- **解码:** 由于 `DeliverTx` 是通过 ABCI 调用的,因此 `Tx` 会以 `[]byte` 的形式被接收。节点首先会对 `Tx` 进行解码,然后在 `runTxModeDeliver` 中调用 `runTx`,`runTx` 除了会执行 `CheckTx` 中的检查外,还会执行 `Tx` 和并写入状态的变化。 + +- **检查:** 全节点会再次调用 `validateBasicMsgs` 和 `AnteHandler`。之所以进行第二次检查,是因为在 `Tx` 进交易池的过程中,可能没有相同的 `Tx`,但恶意出块节点的区块可能包括无效 `Tx`。但是这次检查特殊的地方在于,`AnteHandler` 不会将 `gas-prices` 与节点的 `min-gas-prices` 比较,因为每个节点的 `min-gas-prices` 可能都不同,这样比较的话可能会产生不确定的结果。 + +- **路由和 Handler:** `CheckTx` 退出后,`DeliverTx` 会继续运行 [`runMsgs`](https://docs.cosmos.network/master/core/baseapp.html#runtx-and-runmsgs) 来执行 `Tx` 中的每个 `Msg`。由于 `Tx` 可能具有来自不同模块的 `message`,因此 `baseapp` 需要知道哪个模块可以找到适当的 `Handler`。因此,`路由`通过[模块管理器](https://docs.cosmos.network/master/building-modules/module-manager.html)来检索路由名称并找到对应的[`Handler`](https://docs.cosmos.network/master/building-modules/handler.html)。 + +- **Handler:** `handler` 是用来执行 `Tx` 中的每个 `message`,并且使状态转换到从而保持 `deliverTxState`。`handler` 在 `Msg` 的模块中定义,并写入模块中的适当存储区。 + +- **Gas:** 在 `Tx` 被传递的过程中,`GasMeter` 是用来记录有多少 gas 被使用,如果执行完成,`GasUsed` 会被赋值并返回 `abci.ResponseDeliverTx`。如果由于 `BlockGasMeter` 或者 `GasMeter` 耗尽或其他原因导致执行中断,程序则会报出相应的错误。 + +如果由于 `Tx` 无效或 `GasMeter` 用尽而导致任何状态更改失败,`Tx` 的处理将被终止,并且所有状态更改都将还原。区块提案中无效的 `Tx` 会导致验证者节点拒绝该区块并投票给空块。 + +### 提交 + +最后一步是让节点提交区块和状态更改,在重跑了区块中所有的 `Tx` 之后,验证者节点会验证区块的签名以最终确认它。不是验证者节点的全节点不参与共识(即无法投票),而是接受投票信息以了解是否应提交状态更改。 + +当收到足够的验证者票数(2/3+的加权票数)时,完整的节点将提交一个新的区块,以添加到区块链网络中并最终确定应用程序层中的状态转换。此过程会生成一个新的状态根,用作状态转换的默克尔证明。应用程序使用从[Baseapp](https://docs.cosmos.network/master/core/baseapp.html)继承的 ABCI 方法[`Commit`](https://docs.cosmos.network/master/core/baseapp.html#commit),`Commit` 通过将 `deliverState` 写入应用程序的内部状态来同步所有的状态转换。提交状态更改后,`checkState` 从最近提交的状态重新开始,并将 `deliverState` 重置为空以保持一致并反映更改。 + +请注意,并非所有区块都具有相同数量的 `Tx`,并且共识可能会导致一个空块。在公共区块链网络中,验证者可能是**拜占庭恶意**的,这可能会阻止将 `Tx` 提交到区块链中。可能的恶意行为包括出块节点将某个 `Tx` 排除在区块链之外,或者投票反对某个出块节点。 + +至此,`Tx`的生命周期结束,节点已验证其有效性,并提交了这些更改。`Tx`本身,以 `[]byte` 的形式被存储在区块上进入了区块链网络。 + +## 下一节 + +了解 [accounts](./accounts.md) diff --git a/docs/cn/clients/README.md b/docs/cn/clients/README.md index 866a2d1bc35e..39e8b6e1c2f3 100644 --- a/docs/cn/clients/README.md +++ b/docs/cn/clients/README.md @@ -2,18 +2,17 @@ 本节说明包含区块链 SDK 的客户端的信息。 -> *注*:此部分仍在开发中。 +> _注_:此部分仍在开发中。 -##轻客户端 +## 轻客户端 -轻客户端使用户能够与您的应用程序进行交互,且无需下载整个状态历史记录,但具有良好的安全性。 +轻客户端使用户能够与您的应用程序进行交互,且无需下载整个状态历史记录,并且具有良好的安全性。 - - [轻客户端概述](./lite/README.md) - - [启动轻客户端服务器](./lite/getting_started.md) - - [轻客户端规范](./lite/specification.md) +- [轻客户端概述](./lite/README.md) +- [启动轻客户端服务器](./lite/getting_started.md) +- [轻客户端规范](./lite/specification.md) -##其他客户端 - - - [区块链 SDK 的 CLI](./cli.md) - - [服务提供商文档](./service-providers.md) +## 其他客户端 +- [区块链 SDK 的 CLI](./cli.md) +- [服务提供商文档](./service-providers.md) diff --git a/docs/cn/clients/cli.md b/docs/cn/clients/cli.md index 944e14746e04..5e5c937a860d 100644 --- a/docs/cn/clients/cli.md +++ b/docs/cn/clients/cli.md @@ -1,4 +1,3 @@ # CLI -> TODO: Rewrite this section to explain how CLI works for a generic SDK app. - +> TODO: Rewrite this section to explain how CLI works for a generic SDK app. diff --git a/docs/cn/clients/lite/README.md b/docs/cn/clients/lite/README.md index 976288a853a7..b5eb140672df 100644 --- a/docs/cn/clients/lite/README.md +++ b/docs/cn/clients/lite/README.md @@ -1,6 +1,6 @@ # 轻客户端概览 -**点击[这里](https://cosmos.network/rpc/)查看Cosmos SDK 轻客户端 RPC 文档** +**点击[这里](https://cosmos.network/rpc/)查看 Cosmos SDK 轻客户端 RPC 文档** ## 简介 @@ -10,11 +10,11 @@ ### 什么是轻节点 -Cosmos SDK 轻节点(Gaia-lite)分为两个独立的组件。 第一个组件对于任何基于 Tendermint 的应用程序都是通用的,它处理区块头相关的安全性和连接性,并验证来自全节点的证明与本地可信验证人集合的对比。 此外,它暴露与任何Tendermint 核心节点完全相同的API。 第二个组件专用于 Cosmos Hub(`gaiad`),它作为查询端点工作,并公开特定于应用程序的功能,这些功能可以是任意的。 针对应用程序状态的所有查询都必须通过查询端点。 查询端点的优点是它可以验证应用程序返回的证据。 +Cosmos SDK 轻节点(Gaia-lite)分为两个独立的组件。 第一个组件对于任何基于 Tendermint 的应用程序都是通用的,它处理区块头相关的安全性和连接性,并验证来自全节点的证明与本地可信验证人集合的对比。 此外,它暴露与任何 Tendermint 核心节点完全相同的 API。 第二个组件专用于 Cosmos Hub(`gaiad`),它作为查询端点工作,并公开特定于应用程序的功能,这些功能可以是任意的。 针对应用程序状态的所有查询都必须通过查询端点。 查询端点的优点是它可以验证应用程序返回的证据。 ### 高层体系结构 -想要为 Cosmos Hub(或任何其他 zone)构建第三方客户端应用程序的应用程序开发人员,应根据其规范 API 构建。 该API 是多个部分的组合。 所有 zone 都必须暴露ICS0(TendermintAPI)。 除此之外,任何 zone 都可以自由选择模块 API的任意组合,具体取决于状态机使用的模块。 Cosmos Hub最初将支持[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) 和 [ICS23](https://cosmos.network/rpc/#/ICS23) (SlashingAPI)。 +想要为 Cosmos Hub(或任何其他 zone)构建第三方客户端应用程序的应用程序开发人员,应根据其规范 API 构建。 该 API 是多个部分的组合。 所有 zone 都必须暴露 ICS0(TendermintAPI)。 除此之外,任何 zone 都可以自由选择模块 API 的任意组合,具体取决于状态机使用的模块。 Cosmos Hub 最初将支持[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) 和 [ICS23](https://cosmos.network/rpc/#/ICS23) (SlashingAPI)。 ![high-level](../../../kr/clients/lite/pics/high-level.png) @@ -24,17 +24,17 @@ Cosmos SDK 轻节点(Gaia-lite)分为两个独立的组件。 第一个组 ABCI 的全节点与其轻客户端的区别在于以下方面: -| | Full Node | 轻客户端 | Description | -| ------------------------------- | --------------- | ------------- | ------------------------------------------------------------ | -| 执行并验证交易 | Yes | No | 全节点将执行并验证所有交易,而Gaia-lite则不会 | -| 验证和存储区块 | Yes | No | 全节点将验证并保存所有块,而Gaia-lite则不会 | -| 参与共识 | Yes | No | 只有当全节点是验证人时,它才会参与共识。 Lite节点永远不会参与共识。 | -| 带宽开销 | 巨大 | 很小 | 全节点将接收所有块。 如果带宽有限,它将落后于主网络。 更重要的是,如果碰巧是验证人,它将减缓共识过程。 轻客户端需要很少的带宽, 只有在提供本地请求时,才会占用带宽。 | -| 计算资源 | 巨大 | 很小 | 全节点将执行所有交易并验证所有块,这需要大量的计算资源 | -| 存储资源 | 巨大 | 很小 | 全节点将保存所有块和 ABCI 状态,而Gaia-lite只保存验证人集合和一些检查点。 | -| 电力资源 | 巨大 | 很小 | 全节点必须在具有高性能并能一直运行的机器上部署,因此功耗将是巨大的。 Gaia-lite可以部署在与用户应用程序相同的机器上,也可以部署在独立但性能较差的机器上。 此外,Lite客户端可以在必要时随时关闭。所以Gaia-lite只消耗很少的功率,即使移动设备也能满足功率需求。 | -| 提供的 APIs | 所有cosmos APIs | 部分模块 APIs | 全节点支持所有cosmos API。 Gaia-lite 根据用户的配置提供模块化API。 | -| 安全等级 | 高 | 高 | 全节点将自行验证所有交易和块。 轻型客户端无法执行此操作,但它可以查询来自其他全节点的任何数据并独立验证数据。 因此,全节点和轻型客户端都不需要信任任何第三方节点,它们都可以实现高安全性。 | +| | Full Node | 轻客户端 | Description | +| -------------- | ---------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 执行并验证交易 | Yes | No | 全节点将执行并验证所有交易,而 Gaia-lite 则不会 | +| 验证和存储区块 | Yes | No | 全节点将验证并保存所有块,而 Gaia-lite 则不会 | +| 参与共识 | Yes | No | 只有当全节点是验证人时,它才会参与共识。 Lite 节点永远不会参与共识。 | +| 带宽开销 | 巨大 | 很小 | 全节点将接收所有块。 如果带宽有限,它将落后于主网络。 更重要的是,如果碰巧是验证人,它将减缓共识过程。 轻客户端需要很少的带宽, 只有在提供本地请求时,才会占用带宽。 | +| 计算资源 | 巨大 | 很小 | 全节点将执行所有交易并验证所有块,这需要大量的计算资源 | +| 存储资源 | 巨大 | 很小 | 全节点将保存所有块和 ABCI 状态,而 Gaia-lite 只保存验证人集合和一些检查点。 | +| 电力资源 | 巨大 | 很小 | 全节点必须在具有高性能并能一直运行的机器上部署,因此功耗将是巨大的。 Gaia-lite 可以部署在与用户应用程序相同的机器上,也可以部署在独立但性能较差的机器上。 此外,Lite 客户端可以在必要时随时关闭。所以 Gaia-lite 只消耗很少的功率,即使移动设备也能满足功率需求。 | +| 提供的 APIs | 所有 cosmos APIs | 部分模块 APIs | 全节点支持所有 cosmos API。 Gaia-lite 根据用户的配置提供模块化 API。 | +| 安全等级 | 高 | 高 | 全节点将自行验证所有交易和块。 轻型客户端无法执行此操作,但它可以查询来自其他全节点的任何数据并独立验证数据。 因此,全节点和轻型客户端都不需要信任任何第三方节点,它们都可以实现高安全性。 | 根据上表,Gaia-lite 可以满足所有用户的功能和安全需求,但只需要很少的带宽、计算、存储和电力资源。 @@ -42,7 +42,7 @@ ABCI 的全节点与其轻客户端的区别在于以下方面: ### 可信验证人集合 -Gaia-lite的基本设计理念遵循两个规则: +Gaia-lite 的基本设计理念遵循两个规则: 1. **不信任任何区块链节点,包括验证人节点和其他全节点** 2. **只信任整个验证人集合** @@ -57,4 +57,4 @@ Gaia-lite的基本设计理念遵循两个规则: ![change-process](../../../kr/clients/lite/pics/trustPropagate.png) -通常,通过可信验证人集,轻客户端可以验证包含所有预提交数据和块头数据的每个提交块。 此时块哈希、数据哈希和应用哈希是可信的。 基于此和默克尔证明,所有交易数据和 ABCI 状态也可以被验证。 \ No newline at end of file +通常,通过可信验证人集,轻客户端可以验证包含所有预提交数据和块头数据的每个提交块。 此时块哈希、数据哈希和应用哈希是可信的。 基于此和默克尔证明,所有交易数据和 ABCI 状态也可以被验证。 diff --git a/docs/cn/clients/lite/getting_started.md b/docs/cn/clients/lite/getting_started.md index a6e75acc0c4c..796fb7e44d5d 100644 --- a/docs/cn/clients/lite/getting_started.md +++ b/docs/cn/clients/lite/getting_started.md @@ -7,8 +7,7 @@ | chain-id | string | null | true | 要链接全节点的 chain id | | node | URL | "tcp://localhost:46657" | true | 要链接全节点的地址和端口号 | | laddr | URL | "tcp://localhost:1317" | true | 提供 REST 服务的地址和端口号 | -| trust-node | bool | "false" | true | 是否信任 LCD 连接的全节点 | -| trust-store | DIRECTORY | "$HOME/.lcd" | false | 保存检查点和验证人集的目录 | +| trust-store | DIRECTORY | "\$HOME/.lcd" | false | 保存检查点和验证人集的目录 | 示例: @@ -16,8 +15,6 @@ gaiacli rest-server --chain-id=test \ --laddr=tcp://localhost:1317 \ --node tcp://localhost:26657 \ - --trust-node=false ``` -有关Gaia-Lite RPC的更多信息,请参阅 [swagger documentation](https://cosmos.network/rpc/) - +有关 Gaia-Lite RPC 的更多信息,请参阅 [swagger documentation](https://cosmos.network/rpc/) diff --git a/docs/cn/clients/lite/specification.md b/docs/cn/clients/lite/specification.md index 568247d8c97c..2b8dc0acd0a6 100644 --- a/docs/cn/clients/lite/specification.md +++ b/docs/cn/clients/lite/specification.md @@ -1,6 +1,6 @@ # 规范 -该规范描述了如何实现 LCD。 LCD 支持模块化 API。 目前,仅支持ICS0(TendermintAPI),ICS1(密钥API)和ICS20(Key API)。 如有必要,后续可以包含更多API。 +该规范描述了如何实现 LCD。 LCD 支持模块化 API。 目前,仅支持 ICS0(TendermintAPI),ICS1(密钥 API)和 ICS20(Key API)。 如有必要,后续可以包含更多 API。 ## 构建并验证 ABCI 状态的证明 @@ -8,10 +8,10 @@ ![Simple Merkle Tree](../../../kr/clients/lite/pics/simpleMerkleTree.png) -正如我们在[LCD信任传播](https://github.com/irisnet/cosmos-sdk/tree/bianjie/lcd_spec/docs/spec/lcd#trust-propagation)中所讨论的那样,可以通过检查针对可信验证人集的投票权来验证 AppHash。 这里我们只需要建立从 ABCI 状态到 AppHash 的证明。 证据包含两部分: +正如我们在[LCD 信任传播](https://github.com/irisnet/cosmos-sdk/tree/bianjie/lcd_spec/docs/spec/lcd#trust-propagation)中所讨论的那样,可以通过检查针对可信验证人集的投票权来验证 AppHash。 这里我们只需要建立从 ABCI 状态到 AppHash 的证明。 证据包含两部分: -* IAVL 证明 -* 子库到 AppHash 的证明 +- IAVL 证明 +- 子库到 AppHash 的证明 ### IAVL 证明 @@ -58,23 +58,23 @@ type KeyExistsProof struct { 构建证明的步骤: -* 从根节点访问IAVL树 -* 记录 InnerNodes 中的访问节点 -* 找到目标叶节点后,将叶节点版本赋值给证明版本 -* 将当前 IAVL 树高赋值给证明高度 -* 将当前 IAVL 树根哈希赋值给证明根哈希 -* 将当前的子目录名称赋值给证明 StoreName -* 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo +- 从根节点访问 IAVL 树 +- 记录 InnerNodes 中的访问节点 +- 找到目标叶节点后,将叶节点版本赋值给证明版本 +- 将当前 IAVL 树高赋值给证明高度 +- 将当前 IAVL 树根哈希赋值给证明根哈希 +- 将当前的子目录名称赋值给证明 StoreName +- 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo 验证证明的步骤: -* 使用证明版本中的键、值构建叶节点 -* 计算叶节点哈希 -* 将哈希值分配给第一个 innerNode 的 rightHash,然后计算第一个 innerNode 哈希值 -* 传播哈希计算过程。 如果先前的 innerNode 是下一个 innerNode 的左子节点,则将先前的 innerNode 散列分配给下一个 innerNode 的左散列。否则,将先前的 innerNode 散列分配给下一个innerNode的右散列 -* 最后 innerNode 的哈希应该等于此证明的根哈希, 否则证明无效。 +- 使用证明版本中的键、值构建叶节点 +- 计算叶节点哈希 +- 将哈希值分配给第一个 innerNode 的 rightHash,然后计算第一个 innerNode 哈希值 +- 传播哈希计算过程。 如果先前的 innerNode 是下一个 innerNode 的左子节点,则将先前的 innerNode 散列分配给下一个 innerNode 的左散列。否则,将先前的 innerNode 散列分配给下一个 innerNode 的右散列 +- 最后 innerNode 的哈希应该等于此证明的根哈希, 否则证明无效。 -### IAVL 缺席证明 +### IAVL 缺席证明 众所周知,所有 IAVL 叶节点都按每个叶节点的密钥排序。 因此,我们可以在 IAVL 树的整个密钥集中计算出目标密钥的位置。 如下图所示,我们可以找到左键和右键。 如果我们可以证明左键和右键肯定存在,并且它们是相邻的节点,那么目标密钥肯定不存在。 @@ -108,25 +108,25 @@ type KeyAbsentProof struct { 以上是缺席证明的数据结构。 构建证据的步骤: -* 从根节点访问IAVL树 -* 获取整个密钥集中密钥的对应索引(标记为INDEX) -* 如果返回的索引等于0,则右索引应为0,且左节点不存在 -* 如果返回的索引等于整个密钥集的大小,则左节点索引应为INDEX-1,且右节点不存在 -* 否则,右节点索引应为INDEX,左节点索引应为INDEX-1 -* 将当前 IAVL 树高赋值给证明高度 -* 将当前 IAVL 树根哈希赋值给证明根哈希 -* 将当前的子目录名称赋值给证明 StoreName -* 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo +- 从根节点访问 IAVL 树 +- 获取整个密钥集中密钥的对应索引(标记为 INDEX) +- 如果返回的索引等于 0,则右索引应为 0,且左节点不存在 +- 如果返回的索引等于整个密钥集的大小,则左节点索引应为 INDEX-1,且右节点不存在 +- 否则,右节点索引应为 INDEX,左节点索引应为 INDEX-1 +- 将当前 IAVL 树高赋值给证明高度 +- 将当前 IAVL 树根哈希赋值给证明根哈希 +- 将当前的子目录名称赋值给证明 StoreName +- 从 multistore 读取指定高度的 commitInfo 并将其赋值给证明 StoreCommitInfo 验证证明的步骤: -* 如果只存在右节点,请验证其存在的证明,并验证它是否是最左侧的节点 -* 如果仅存在左节点,请验证其存在的证据,并验证它是否是最右侧的节点 -* 如果右节点和左节点都存在,请验证它们是否相邻 +- 如果只存在右节点,请验证其存在的证明,并验证它是否是最左侧的节点 +- 如果仅存在左节点,请验证其存在的证据,并验证它是否是最右侧的节点 +- 如果右节点和左节点都存在,请验证它们是否相邻 ### Substores 到 AppHash 的证明 -在验证了 IAVL 证明之后,我们就可以开始验证针对 AppHash 的 substore 证明。 首先,迭代 MultiStoreCommitInfo 并通过证明 StoreName 找到 substore commitID。 验证 commitID 中的哈希是否等于证明根哈希,如果不相等则证明无效。 然后通过 substore name 的哈希对 substore commitInfo 数组进行排序。 最后,使用所有 substore commitInfo 数组构建简单的 Merkle 树,并验证 Merkle 根哈希值是否等于appHash。 +在验证了 IAVL 证明之后,我们就可以开始验证针对 AppHash 的 substore 证明。 首先,迭代 MultiStoreCommitInfo 并通过证明 StoreName 找到 substore commitID。 验证 commitID 中的哈希是否等于证明根哈希,如果不相等则证明无效。 然后通过 substore name 的哈希对 substore commitInfo 数组进行排序。 最后,使用所有 substore commitInfo 数组构建简单的 Merkle 树,并验证 Merkle 根哈希值是否等于 appHash。 ![substore proof](../../../kr/clients/lite/pics/substoreProof.png) @@ -164,20 +164,20 @@ func SimpleHashFromHashes(hashes [][]byte) []byte { ## 根据验证人集验证区块头 -上面的小节中经常提到 appHash,但可信的appHash来自哪里? 实际上,appHash 存在于区块头中,因此接下来我们需要针对 LCD 可信验证人集验证特定高度的区块头。 验证流程如下所示: +上面的小节中经常提到 appHash,但可信的 appHash 来自哪里? 实际上,appHash 存在于区块头中,因此接下来我们需要针对 LCD 可信验证人集验证特定高度的区块头。 验证流程如下所示: ![commit verification](../../../kr/clients/lite/pics/commitValidation.png) -当可信验证人集与区块头不匹配时,我们需要尝试将验证人集更新为此块的高度。 LCD 有一条规则,即每个验证人集的变化不应超过1/3投票权。 如果目标验证人集的投票权变化超过1/3,则与可信验证人集进行比较。 我们必须验证,在目标验证人集之前是否存在隐含的验证人集变更。 只有当所有验证人集变更都遵循这条规则时,才能完成验证人集的更新。 +当可信验证人集与区块头不匹配时,我们需要尝试将验证人集更新为此块的高度。 LCD 有一条规则,即每个验证人集的变化不应超过 1/3 投票权。 如果目标验证人集的投票权变化超过 1/3,则与可信验证人集进行比较。 我们必须验证,在目标验证人集之前是否存在隐含的验证人集变更。 只有当所有验证人集变更都遵循这条规则时,才能完成验证人集的更新。 例如: ![Update validator set to height](../../../kr/clients/lite/pics/updateValidatorToHeight.png) -* 更新到 10000,失败,变更太大 -* 更新到 5050,失败,变更太大 -* 更新至 2575,成功 -* 更新至 5050,成功 -* 更新到 10000,失败,变更太大 -* 更新至 7525,成功 -* 更新至 10000,成功 \ No newline at end of file +- 更新到 10000,失败,变更太大 +- 更新到 5050,失败,变更太大 +- 更新至 2575,成功 +- 更新至 5050,成功 +- 更新到 10000,失败,变更太大 +- 更新至 7525,成功 +- 更新至 10000,成功 diff --git a/docs/cn/clients/service-providers.md b/docs/cn/clients/service-providers.md index 8535e6e50a02..7350b98d8110 100644 --- a/docs/cn/clients/service-providers.md +++ b/docs/cn/clients/service-providers.md @@ -1,6 +1,6 @@ # 服务提供商(Service Providers) -我们将“服务提供商”定义为可以为最终用户提供服务的实体,这些实体涉及与基于 Cosmos-SDK 的区块链(包括Cosmos Hub)的某种形式的交互。更具体地说,本文档将集中于与 token 的交互。 +我们将“服务提供商”定义为可以为最终用户提供服务的实体,这些实体涉及与基于 Cosmos-SDK 的区块链(包括 Cosmos Hub)的某种形式的交互。更具体地说,本文档将集中于与 token 的交互。 本节不涉及想要提供[轻客户端](https://github.com/cosmos/cosmos-sdk/tree/master/docs/interfaces/lite)功能的钱包开发者。服务提供商将作为最终用户的区块链的可信接入点。 @@ -9,12 +9,12 @@ 有三个主要部分需要考虑: - 全节点:与区块链交互。 - - Rest Server:它充当 HTTP 调用的中继者。 - - Rest API:为 Rest Server 定义可用端点。 +- Rest Server:它充当 HTTP 调用的中继者。 +- Rest API:为 Rest Server 定义可用端点。 -##运行全节点 +## 运行全节点 -###安装和配置 +### 安装和配置 我们将描述为 Cosmos Hub 运行和交互全节点的步骤。对于其他基于 SDK 的区块链,该过程是类似的。 @@ -28,19 +28,19 @@ #### 创建秘钥对 -生成新秘钥(默认使用 secp256k1椭圆曲线算法): +生成新秘钥(默认使用 secp256k1 椭圆曲线算法): ```bash gaiacli keys add ``` -系统将要求您为此密钥对输入密码(至少8个字符)。该命令返回4个信息: +系统将要求您为此密钥对输入密码(至少 8 个字符)。该命令返回 4 个信息: - `NAME`: 秘钥名称。 - `TYPE`:秘钥类型,总是`local`。 - `ADDRESS`:您的地址,用于接收资金。 -- `PUBKEY`:您的公钥 Your public key. Useful for validators. -- `MNEMONIC`: 由24个单词组成的助记词。 **将这个助记词保存在安全的地方**,它用于在您忘记密码时恢复您的私钥。 +- `PUBKEY`:您的公钥, 用于验证者. +- `MNEMONIC`: 由 24 个单词组成的助记词。 **将这个助记词保存在安全的地方**,它用于在您忘记密码时恢复您的私钥。 您可以输入以下命令查看所有可用密钥: @@ -56,7 +56,7 @@ gaiacli keys list gaiacli account ``` -*注意:当您查询没有 token 帐户的余额时,您将得到以下错误:找不到地址为的帐户。这是预料之中的!我们正在努力改进我们的错误提示信息。* +_注意:当您查询没有 token 帐户的余额时,您将得到以下错误:找不到地址为的帐户。这是预料之中的!我们正在努力改进我们的错误提示信息。_ #### 通过 CLI 发送代币 @@ -64,27 +64,28 @@ gaiacli account ```bash gaiacli tx send \ - --chain-id= + --chain-id= ``` 参数: + - ``: 发送账户的名称或地址。 - ``: 接收者地址。 - ``: 接受``格式的参数,例如 `10faucetToken`。 标识: -- `--chain-id`: 此标志允许您指定链的ID,不同的testnet链和主链会有不同的 id。 +- `--chain-id`: 此标志允许您指定链的 ID,不同的 testnet 链和主链会有不同的 id。 #### 帮助 如果您需要进行其他操作,最合适的命令是: ```bash -gaiacli +gaiacli ``` -它将显示所有可用命令。对于每个命令,您可以使用`--help`标识来获取更多信息。 +它将显示所有可用命令。对于每个命令,您可以使用`--help`标识来获取更多信息。 ## 设置 Rest 服务器 @@ -97,10 +98,9 @@ gaiacli rest-server --node= ``` Flags: -- `--trust-node`: 布尔类型。如果为`true`,轻节点校验将被禁用。如果为`false`, 则会校验返回结果。 对于服务提供商,应将其设置为`true`。默认情况下,它设置为`true`。 -- `--node`: 全节点的IP地址和端口。格式为` `。如果全节点在同一台机器上,则地址应为`tcp:// localhost:26657`。 -- `--laddr`: 此标识允许您指定 Rest 服务器的地址和端口(默认为“1317”)。通常只使用这个标识指定端口,此时只需输入“localhost”作为地址,格式为``。 +- `--node`: 全节点的 IP 地址和端口。格式为 ``。如果全节点在同一台机器上,则地址应为 `tcp:// localhost:26657`。 +- `--laddr`: 此标识允许您指定 Rest 服务器的地址和端口(默认为“1317”)。通常只使用这个标识指定端口,此时只需输入 “localhost” 作为地址,格式为``。 ### 监听入向交易 @@ -112,11 +112,11 @@ Flags: Rest API 记录了可用于与全节点交互的所有可用端点,您可以在[这里](https://cosmos.network/rpc/)查看。 -API 针对每种类别的端点归纳为 ICS 标准。例如,[ICS20](https://cosmos.network/rpc/#/ICS20/)描述了API 与 token 的交互。 +API 针对每种类别的端点归纳为 ICS 标准。例如,[ICS20](https://cosmos.network/rpc/#/ICS20/)描述了 API 与 token 的交互。 为了给开发者提供更大的灵活性,我们提供了生成未签名交易、[签名](https://cosmos.network/rpc/#/ICS20/post_tx_sign)和[广播](https://cosmos.network/rpc/#/ICS20/post_tx_broadcast)等不同的 API 端点。这允许服务提供商使用他们自己的签名机制。 -为了生成一个未签名交易(例如 [coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)),你需要在`base_req`的主体中使用`generate_only`字段。 +为了生成一个未签名交易(例如 [coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)),你需要在 `base_req` 的主体中使用 `generate_only` 字段。 ## Cosmos SDK 交易签名 @@ -144,19 +144,19 @@ Cosmos SDK 签名是一个相当简单的过程。 交易构造接口将生成 `"fee"`、 `"msgs"` 和 `"memo"` 等字段. You can load the mempool of a full node or validator with a sequence of uncommitted transactions with incrementing -sequence numbers and it will mostly do the correct thing. +sequence numbers and it will mostly do the correct thing. -`"account_number"` 和 `"sequence"` 字段可以直接从区块链或本地缓存中查询。 错误的获取了这些数值和chainId,是产生无效签名错误的常见原因。您可以通过加载全节点或验证人中的 mempool 来获取未提交交易的自增序号,这样大大增加成功概率。 +`"account_number"` 和 `"sequence"` 字段可以直接从区块链或本地缓存中查询。 错误的获取了这些数值和 chainId,是产生无效签名错误的常见原因。您可以通过加载全节点或验证人中的 mempool 来获取未提交交易的自增序号,这样大大增加成功概率。 - 您可以使用递增序列号的一系列未提交事务加载完整节点或验证器的mempool,它将主要执行正确的操作。 +您可以使用递增序列号的一系列未提交事务加载完整节点或验证器的 mempool,它将主要执行正确的操作。 在签名之前,所有键都要按字典顺序排序,并从 JSON 输出中删除所有空格。 -签名编码是 ECDSArands 的 64字节连结(即`r || s`),其中`s`按字典顺序小于其反转以防止延展性。 这就像以太坊一样,但没有用户公钥恢复的额外字节,因为 Tendermint 假定公钥一定会提供。 +签名编码是 ECDSArands 的 64 字节连结(即`r || s`),其中`s`按字典顺序小于其反转以防止延展性。 这就像以太坊一样,但没有用户公钥恢复的额外字节,因为 Tendermint 假定公钥一定会提供。 已签名交易中的签名和公钥示例: -``` json +```json { "type": "cosmos-sdk/StdTx", "value": { diff --git a/docs/cn/cn-translation-progress.md b/docs/cn/cn-translation-progress.md index ed1f6be27d39..47362cdb8ed5 100644 --- a/docs/cn/cn-translation-progress.md +++ b/docs/cn/cn-translation-progress.md @@ -6,7 +6,7 @@ Documentation has been translated for **reference use only** and may contain typ Please refer to the official english version of the documentation for the latest and accurate information. -## Cosmos SDK文档翻译 +## Cosmos SDK 文档翻译 本文档跟踪官方 Cosmos SDK 文档的中文翻译进度。 @@ -18,7 +18,7 @@ Please refer to the official english version of the documentation for the latest ### README.md -- Synced until commit [1c326ea5](https://github.com/cosmos/cosmos-sdk/commit/1c326ea524eade1da8771cd7e4343012203a166f) (2019-05-27) +- Synced until commit [b18bd06a](https://github.com/cosmos/cosmos-sdk/commit/b18bd06a364e6ac15f22423e6b66a9feb3eeae93) (2019-12-10) ### [`concepts`](../concepts/) @@ -34,7 +34,7 @@ Please refer to the official english version of the documentation for the latest ### [`intro`](../intro/) -- Synced until commit [1c326ea5](https://github.com/cosmos/cosmos-sdk/commit/1c326ea524eade1da8771cd7e4343012203a166f) (2019-05-27) +- Synced until commit [be194ca1](https://github.com/cosmos/cosmos-sdk/commit/be194ca1b7d159590a0147da3226b7e09eaa3f61) (2020-07-07) ### [`modules`](../modules/) @@ -43,4 +43,3 @@ Please refer to the official english version of the documentation for the latest ### [`clients`](../clients/) - Synced until Commit [7558f760](https://github.com/cosmos/cosmos-sdk/commit/7558f7607918b6337a8b58b8f956d6776f503138) (2019-05-13) - diff --git a/docs/cn/intro/README.md b/docs/cn/intro/README.md index 8181bcda1120..c7438e97c81a 100644 --- a/docs/cn/intro/README.md +++ b/docs/cn/intro/README.md @@ -1,31 +1,13 @@ -# SDK介绍 +# 介绍 -## 什么是Cosmos SDK? +此目录包含对 cosmos sdk 的相关介绍 -[Cosmos-SDK](https://github.com/cosmos/cosmos-sdk) 是一个架构,用于构建多资产股权证明(PoS)的区块链,比如Cosmos Hub,以及权益证明(PoA)的区块链。使用Cosmos SDK构建的区块链通常称为**特定应用区块链**。 +1. [概述](./overview.md) -Cosmos SDK的目标是允许开发者从头开始轻松创建原生就能同其他区块链相互操作的自定义区块链。我们设想SDK类似于Ruby-on-Rails框架之上构建应用一样,可以很方便在[Tendermint](https://github.com/tendermint/tendermint)之上构建安全的区块链应用。 基于SDK的区块链通过可组合的模块构建出来的,大部分模块是开源的,并且可供任何开发人员使用。 任何人都可以为Cosmos-SDK 创建一个模块,集成已经构建的模块就像将它们导入到区块链应用程序一样简单。 更重要的是,Cosmos SDK是一个基于**能力**(capabilities)的系统,开发人员可以更好地了解模块之间交互的安全性。 要深入了解能力,请跳到[OCAP](./ocap.md)。 +2. [基于特定应用的区块链](./why-app-specific.md) -## 什么是特定应用区块链? +3. [应用架构](./sdk-app-architecture.md) +4. [Cosmos SDK 设计概述](./sdk-design.md) -今天区块链的一个发展模式是像以太坊这样的虚拟机区块链,开发通常围绕着在现有区块链之上通过智能合约构建一个去中心化的应用程序。 虽然智能合约对于像单用途应用程序(如ICO)这样的一些场景非常有用,但对于构建复杂的去中心化平台往往是不够的。 更一般地说,智能合约在灵活性、主权和性能方面受到限制。 - -特定应用区块链提供了与虚拟机区块链截然不同的开发模式。 特定应用区块链是一个定制的区块链来服务单个应用程序:开发人员可以自由地做出应用程序运行最佳所需的设计决策。 它们还可以提供更好的主权、安全和性能。 - -要了解有关特定应用区块链的更多信息,可参考[这里](./why-app-specific.md)。 - -## 为什么是 Cosmos SDK? - -Cosmos SDK 是目前用于构建自定义的特定应用区块链的最先进的框架。 以下是一些你可能需要考虑使用 Cosmos SDK 构建去中心化应用的原因: - -* SDK中默认共识引擎是 [Tendermint Core](https://github.com/tendermint/tendermint) 。 Tendermint 是已存在的最成熟(也是唯一的)的BFT共识引擎。 它被广泛应用于行业,被认为是建立股权证明系统(POS)的黄金标准共识引擎。 -* SDK是开源的,旨在使其易于从可组合模块中构建区块链。 随着开源SDK模块生态系统的发展,使用它构建复杂的去中心化平台将变得越来越容易。 -* SDK 受基于能力的安全性启发,及多年来解决区块链状态机的经验。 这使得 Cosmos SDK 成为构建区块链的非常安全的环境。 -* 最重要的是,Cosmos SDK已经被许多特定应用区块链产品所使用。 如:[Cosmos Hub](https://hub.cosmos.network), [Iris](https://irisnet.org), [Binance Chain](https://docs.binance.org/), [Terra](https://terra.money/) or [Lino](https://lino.network/) ,除此之外还有很多建立在Cosmos SDK的项目。 你可以在这里查看[生态系统](https://cosmos.network/ecosystem)。 - - -## 开始使用 Cosmos SDK - -* 了解[SDK 应用体系架构](./sdk-app-architecture.md)的详细信息 -* 了解如何从头构建特定应用区块链,参考[SDK教程](/docs/tutorial) 。 \ No newline at end of file +了解完成相关介绍后,可以前往 [基础文档](../basics/README.md) 了解更多。 diff --git a/docs/cn/intro/ocap.md b/docs/cn/intro/ocap.md index f7f511764d87..72c062f2b32f 100644 --- a/docs/cn/intro/ocap.md +++ b/docs/cn/intro/ocap.md @@ -6,7 +6,7 @@ > 我们假设蓬勃发展的 Cosmos-SDK 模块生态中会包含错误或恶意的模块。 -Cosmos SDK旨在通过以对象能力系统作为基础来解决此威胁。 +Cosmos SDK 旨在通过以对象能力系统作为基础来解决此威胁。 > 对象能力系统的结构特性有利于代码设计模块化,并确保代码实现的可靠封装。 > @@ -15,21 +15,14 @@ Cosmos SDK旨在通过以对象能力系统作为基础来解决此威胁。 > 因此,可以在存在包含未知或(可能)恶意代码的新对象的情况下建立和维护这些安全属性。 > > 这些结构属性源于管理对已存在对象的访问的两个规则: -> 1. 只有在对象A持有对象B的引用,A才可以向B发送一条消息,。 -> 2. 只有对象A收到了一条包含对象C引用的消息,A才可以获得C的引用。 +> +> 1. 只有在对象 A 持有对象 B 的引用,A 才可以向 B 发送一条消息 +> 2. 只有对象 A 收到了一条包含对象 C 引用的消息,A 才可以获得 C 的引用 +> > 根据这两条规则,一个对象只有通过一条先前存在的引用链获得另一个对象的引用,简而言之,“只有连接才能产生连接”。 关于对象能力(object-capabilities),可以阅读这边[文章](http://habitatchronicles.com/2017/05/what-are-capabilities/)了解更多。 -严格来说,Golang 由于几个问题没有完全实现对象能力: - -+ 无处不在地引入原始(基础)模块(比如unsafe, os) -+ 无处不在地重写模块变量 -+ 存在2个以上goroutine时的数据竞态漏洞可以创建非法的接口值 - -第一点很容易通过审计import和使用适当的依赖版本控制系统(如Dep)来捕获。但第二点和第三点就不容易了,需要成本进行代码审核。 - - ## 对象能力模式实践 想法就是只暴露完成工作所需要的部分。 @@ -51,14 +44,6 @@ var sumValue := externalModule.ComputeSumValue(account) var sumValue := externalModule.ComputeSumValue(*account) ``` -在Cosmos SDK中,你可以看到[gaia app](https://github.com/cosmos/cosmos-sdk/blob/master/simapp/app.go)中对该原则的实践。 +在 Cosmos SDK 中,你可以看到[gaia app](https://github.com/cosmos/cosmos-sdk/blob/master/simapp/app.go)中对该原则的实践。 -```go -// register message routes -app.Router(). - AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)). - AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)). - AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)). - AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)). - AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper)) -``` ++++ https://github.com/cosmos/gaia/blob/master/app/app.go#L197-L209 diff --git a/docs/cn/intro/overview.md b/docs/cn/intro/overview.md new file mode 100644 index 000000000000..96da5ebf54af --- /dev/null +++ b/docs/cn/intro/overview.md @@ -0,0 +1,33 @@ +# Cosmos SDK 介绍 + +## 什么是 Cosmos SDK + +[Cosmos SDK](https://github.com/cosmos/cosmos-sdk)是开源框架,用于构建类似 Cosmos Hub 等基于 POS 共识算法的多元资产公有区块链,以及基于权威证明共识算法的许可链。使用 Cosmos SDK 构建的区块链通常被称为特定应用区块链(专用区块链)(application-specific blockchains)。 + +Cosmos SDK 的目标是让开发者可以快速地构建一条能与其他区块链以原生的方式进行互操作的可定制区块链。在我们的设想中,这套 SDK 就像 Web 应用框架一样,可以让开发者迅速构建出基于[Tendermint](https://github.com/tendermint/tendermint)的安全区块链应用程序。 基于 Cosmos SDK 的区块链由组合式[模块](https://docs.cosmos.network/master/building-modules/intro.html)构建,其中大部分模块都是开源的,且任何开发者均可使用。任何人都能为 Cosmos SDK 创建新的模块,集成已经构建的模块就像将他们导入你的区块链应用程序一样简单。还有一点,Cosmos SDK 是基于功能(capabilities)的系统,这允许开发者可以更好地考虑模块之间交互的安全性。更深入地了解功能,请跳至[本节](https://docs.cosmos.network/master/core/ocap.html)。 + +## 什么是特定应用区块链 + +目前在区块链领域中,一种开发模式是通过像以太坊这样的虚拟机区块链展开,即开发者在现有的区块链上通过智能合约的方式去构建去中心化应用。虽然智能合约在单用途应用场景(如 ICO)下非常有用,但在构建复杂的去中心化平台时无法达到要求。更具体地说,智能合约在灵活性、所有权、性能方面会受到限制。 + +特定应用区块链提供了与虚拟机区块链截然不同的开发模式。特定应用区块链是面向单个具体应用程序的高度定制化区块链:开发者可以完全自由地做出让应用程序可以达到最佳运行状态的设计决策。他们也可以提供更好的主导权、安全性和性能。 + +了解更多可参考[特定应用区块链](https://docs.cosmos.network/master/intro/why-app-specific.html)。 + +## 为什么选择 Cosmos SDK? + +Cosmos SDK 是目前最先进的构建可定制化特定应用区块链的框架。以下是一些可能让你希望通过 Cosmos SDK 构建去中心化应用的原因: + +- Cosmos SDK 默认的共识引擎是[Tendermint Core](https://github.com/tendermint/tendermint). Tendermint 是目前最成熟的、唯一的 BFT 共识引擎。它被广泛应用于行业中,被认为是构建 POS 系统的最佳标准共识引擎。 + +- Cosmos SDK 是开源的,你可以通过组合式[modules](https://docs.cosmos.network/master/x/)轻松地构建出区块链。随着 SDK 生态中各种开源模块的发展,通过 Cosmos SDK 构建复杂的去中心化平台会变得越来越容易。 + +- Cosmos SDK 受基于功能的安全性所启发,并受益于多年来在区块链状态机领域的经验。这让 Cosmos SDK 成为一个非常安全的构建区块链的环境。 + +- 最重要的是,Cosmos SDK 已经构建出了多个正在运行中的特定应用区块链。例如,Cosmos HUB,IRIS HUB,Binance Chain, Terra 和 Kava。更多基于 Cosmos SDK 构建的区块链参考[这里](https://cosmos.network/ecosystem)。 + +## 开始使用 Cosmos SDK + +了解更多请参考[SDK 应用架构](https://docs.cosmos.network/master/intro/sdk-app-architecture.html)。 + +了解如何从头建立特定应用区块链,请参考[SDK 教程](https://cosmos.network/docs/tutorial)。 diff --git a/docs/cn/intro/sdk-app-architecture.md b/docs/cn/intro/sdk-app-architecture.md index c31d888e1416..732655dac15d 100644 --- a/docs/cn/intro/sdk-app-architecture.md +++ b/docs/cn/intro/sdk-app-architecture.md @@ -1,12 +1,12 @@ -# SDK 应用程序架构 +# 区块链架构 ## 状态机 -区块链应用的核心是[具有最终确定性的复制状态机](https://en.wikipedia.org/wiki/State_machine_replication)。 +区块链的核心是[复制确定状态机](https://en.wikipedia.org/wiki/State_machine_replication)(replicated deterministic state machine)。 -状态机是计算机科学概念,一台机器可以具有多个状态,但在任何给定时间只有一个`状态`,其描述了系统的当前状态,及触发这些状态转变的交易(译者注:也对应数据库中事务的概念)。 +状态机是计算机科学领域的一个概念,即一台机器可以具有多个状态,但在任意给定时刻只具有一个确定的状态。我们用 `state` 描述系统当前状态,`transactions` 触发状态转换。 -给定一个状态S和交易T,状态机会返回一个新的状态S'。 +给定一个状态 S 和 Transaction T,状态机会返回新的状态 S'。 ``` +--------+ +--------+ @@ -16,7 +16,7 @@ +--------+ +--------+ ``` -实际上,交易以区块的形式打包在一起以提高过程的效率。给定状态S和包含交易的区块B,状态机将返回新状态S'。 +在实际中,Transaction 集会被打包进区块中,以让处理过程更加高效。给定一个状态 S 和一个包含 Transaction 集 B 的区块,状态机就会返回新的状态 S'。 ``` +--------+ +--------+ @@ -26,70 +26,66 @@ +--------+ +--------+ ``` -在区块链上下文环境中,状态机是确定性的。这意味着如果你从一个给定的状态开始,重放相同顺序的交易,将始终以相同的最终状态结束。 +在区块链的上下文环境中,状态机是确定的。这意味着节点从给定状态开始,重放相同的 Transaction 序列,总能得到相同的最终状态。 -Cosmos SDK 为你提供了最大的灵活性用以定义自身应用程序的状态、交易类型和状态转换函数。在接下来的章节中会更深入细致的描述如何使用 SDK 来构建状态机。但首先,让我们看看状态机是如何使用 **Tendermint** 进行复制的。 +Cosmos SDK 为开发者提供了最大程度的灵活性去定义应用程序的状态,Transaction 类型和状态转换功能。接下来的章节中会更详细地介绍使用 SDK 构建状态机的过程。在此之前,先让我们看看如何使用 Tendermint 复制状态机。 -### Tendermint +## Tendermint -作为一个开发者,你只需要使用 Cosmos-SDK 定义状态机,而[Tendermint](https://tendermint.com/docs/introduction/introduction.html)将会为你处理网络层的状态复制。 +得益于 Cosmos SDK,开发者只需要定义好状态机,[Tendermint](https://tendermint.com/docs/introduction/what-is-tendermint.html) 就会处理好状态复制的工作。 ``` ^ +-------------------------------+ ^ - | | | | 通过 Cosmos SDK 构建 - | | 状态机 = 应用(层) | | + | | | | Built with Cosmos SDK + | | State-machine = Application | | | | | v | +-------------------------------+ | | | ^ - 链节点 | | 共识层 | | +Blockchain node | | Consensus | | | | | | | +-------------------------------+ | Tendermint Core | | | | - | | 网络层 | | + | | Networking | | | | | | v +-------------------------------+ v ``` -Tendermint是一个与应用程序无关的引擎,负责处理区块链的*网络层*和*共识层*。实际上,这意味着Tendermint负责传播和排序交易字节。Tendermint Core 依赖于拜占庭容错(BFT)算法来达成交易顺序的共识。要深入了解Tendermint,可点击[这里](https://tendermint.com/docs/introduction/what-is-tendermint.html)。 - -Tendermint一致性算法通过一组称为*验证人*的特殊节点一起运作。验证人负责向区块链添加交易区块。对于任何给定的区块,有一组验证人V。通过算法选择V中的验证人A作为下一个区块的提议人。如果超过三分之二的V签署了[prevote](https://tendermint.com/docs/spec/consensus/consensus.html#state-machine-spec)和[precommit](https://tendermint.com/docs/spec/consensus/consensus.html#state-machine-spec),并且区块包含的所有交易都是有效的,则该区块被认为是有效的。验证人集合可以通过状态机中编写的规则进行更改。要深入了解算法,[点击](https://tendermint.com/docs/introduction/what-is-tendermint.html#consensus-overview)。 - -Cosmos SDK 应用程序的主要部分是一个区块链服务后台(daemon),它在每个网络节点的本地运行。如果验证人集合中三分之一以下的是拜占庭(即恶意的),则每个节点在同时查询状态时应获得相同的结果。 +[Tendermint](https://tendermint.com/docs/introduction/what-is-tendermint.html) 是一个与应用程序无关的引擎,负责处理区块链的网络层和共识层。这意味着 Tendermint 负责对 Transaction 字节进行传播和排序。Tendermint Core 通过同名的拜占庭容错算法来达成 Transaction 顺序的共识。 +Tendermint[共识算法](https://tendermint.com/docs/introduction/what-is-tendermint.html#consensus-overview)与一组被称为 Validator 的特殊节点共同运作。Validator 负责向区块链中添加包含 transaction 的区块。在任何给定的区块中,都有一组 Validator 集合 V。算法会从集合 V 中选出一个 Validator 作为下一个区块的 Proposer。如果一个区块被集合 V 中超过三分之二的 Validator 签署了 [prevote](https://tendermint.com/docs/spec/consensus/consensus.html#prevote-step-height-h-round-r) 和 [precommit](https://tendermint.com/docs/spec/consensus/consensus.html#precommit-step-height-h-round-r),且区块中所有 Transaction 都是有效的,则认为该区块有效。Validator 集合可以按照状态机中写定的规则更改。 ## ABCI -Tendermint通过名为[ABCI](https://github.com/tendermint/tendermint/tree/master/abci)的接口将交易从网络层传递给应用程序,因此应用程序必须要实现 ABCI 。 +Tendermint 通过被称为 [ABCI](https://tendermint.com/docs/spec/abci/) 的接口向应用程序传递 Transactions,该接口必须由应用程序实现。 ``` -+---------------------+ -| | -| 应用 | -| | -+--------+---+--------+ - ^ | - | | ABCI - | v -+--------+---+--------+ -| | -| | -| Tendermint | -| | -| | -+---------------------+ + +---------------------+ + | | + | Application | + | | + +--------+---+--------+ + ^ | + | | ABCI + | v + +--------+---+--------+ + | | + | | + | Tendermint | + | | + | | + +---------------------+ ``` -注意,Tendermint 仅处理交易字节。它不知道这些字节究竟是什么意思。Tendermint 所做的只是对交易确定性地排序。赋予这些字节意义是应用程序的工作。Tendermint通过ABCI将交易字节传递给应用程序,并期望返回代码以知晓消息是否成功。 +需要注意的是,Tendermint 仅处理 transaction 字节,它并不知道这些字节的含义。Tendermint 所做的只是对 transaction 字节进行确定性地排序。Tendermint 通过 ABCI 向应用程序传递字节,并期望返回状态码以获知包含在 transactions 中的 messages 是否成功处理。 -以下是ABCI中最重要的消息类型: +以下是 ABCI 最重要的 Messages: -- `CheckTx` : 当 Tendermint Core 收到交易时,如果符合一些的基本的要求会将其传递给应用程序。`Checkx` 用于保护全节点的交易池免受垃圾邮件的侵害。一个名为“Ante Handler”的特殊处理器用于执行一系列验证步骤,例如检查手续费用是否足够和验证签名是否合法。如果交易有效,则将交易添加到[交易池(mempool)](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality)中并广播到对等节点。注意, `CheckTx` 不会处理交易(即不会对修改状态),因为它们尚未包含在区块中。 -- `DeliverTx` : 当 Tendermint Core 接收到[有效区块](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation)时,块中的每条交易都将通过 `DeliverTx `传递给应用程序进行处理。正是在这一阶段发生了状态转换。“Ante Handler”也将连同实际处理交易中每条消息的handler一起再次执行。 -- `BeginBlock`/`EndBlock` : 无论区块是否包含交易,这两个消息都将在每个区块的开头和结尾执行。触发自动的逻辑执行是很有用的。过程中要足够小心,因为计算成本高昂的循环运算可能会减慢区块链的速度,甚至发生无限循环引起区块链本身停滞。 +`CheckTx`:当 Tendermint Core 接收到一个 Transaction 时,它会传递给应用程序以检查是否满足一些基本要求。`CheckTx` 用于保护全节点的内存池免受垃圾 transactions 攻击。`AnteHandler` 这一特殊处理程序用于执行一系列验证步骤,例如检查手续费是否足够以及验证签名。如果检查通过,该 transaction 会被添加进[mempool](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality),并广播给其他共识节点。请注意,此时 transactions 尚未被 `CheckTx` 处理(即未进行状态修改),因为它们还没有被包含在区块中。 +`DeliverTx`:当 Tendermint Core 收到一个[有效区块](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation)时,区块中的每一个 Transaction 都会通过 `DeliverTx` 传递给应用程序以进行处理。状态转换会在这个阶段中发生。`AnteHandler` 会与 Transaction 中每个 Message 的实际 [`handlers`](https://docs.cosmos.network/master/building-modules/handler.html) 一起再次执行。 -有关 ABCI 方法和类型的详细介绍请[点击](https://tendermint.com/docs/spec/abci/abci.html#overview)。 +`BeginBlock/EndBlock`:无论区块中是否包含 transaction,messages 都会在每个区块的开头和结尾处执行。触发自动执行的逻辑是很有用的。但需要谨慎使用,因为计算量庞大的循环会严重降低区块链的性能,而无限循环甚至会导致区块链宕机。 -在 Tendermint 上构建的任何应用程序都需要实现ABCI接口,来同本地的底层 Tendermint 引擎进行通信。幸运的是,你不用必需实现ABCI接口。Cosmos SDK以[baseapp](./sdk-design.md#baseapp)的形式提供了样板实现。 +获知更多关于 ABCI 的详细内容可以访问 [Tendermint docs](https://tendermint.com/docs/spec/abci/abci.html#overview). -### 接下来,让我们学习[SDK的高级设计原则](./sdk-design.md) \ No newline at end of file +基于 Tendermint 构建的任何程序都需要实现 ABCI 接口,以便和底层的本地 Tendermint 引擎通信。幸运的是,您不需要实现 ABCI 接口,Cosmos SDK 以 [baseapp](https://docs.cosmos.network/master/intro/sdk-design.html#baseapp) 的形式提供了样板实现。 diff --git a/docs/cn/intro/sdk-design.md b/docs/cn/intro/sdk-design.md index 353382fdc256..94af6f87a058 100644 --- a/docs/cn/intro/sdk-design.md +++ b/docs/cn/intro/sdk-design.md @@ -1,48 +1,49 @@ -# Cosmos SDK 设计概览 +# Cosmos SDK 的主要组件 -Cosmos SDK是一个方便开发者开发基于Tendermint的安全可靠状态机的一套框架。其核心是Golang版的ABCI的实现。它附带一个`multistore`来持久化存储数据还有一个`router`来处理交易。 +Cosmos SDK 是一个框架,可以促进基于 Tendermint 的安全状态机的开发。SDK 的核心是一个基于 Golang 的[ABCI](https://docs.cosmos.network/master/intro/sdk-app-architecture.html#abci)样板实现。它带有一个用于存储数据的[`multistore`](https://docs.cosmos.network/master/core/store.html#multistore),和一个用于处理 Transaction 的[`router`](https://docs.cosmos.network/master/core/baseapp.html#routing)。 -下面一个简单的视图展示了当从Tendermint的`DeliverTx`请求(`CheckTx`的处理流程与其相同,除了不会执行状态的改变)中接收到一笔交易时,基于Cosmos SDK构建的应用程序是如何处理交易的: +下面的简化视图展示了当通过 `DeliverTx` 从 Tendermint 转移 transactions 时,基于 Cosmos SDK 构建的应用程序如何处理这些 transactions。 -1. 解码从Tendermint共识引擎接收到的交易(记住Tendermint只处理`[]bytes`) -2. 从交易中提取消息并进行基本的合理性检查。 -3. 将每条消息路由至对应的模块进行处理。 -4. 提交状态变更。 +1. 解码从 Tendermint 共识引擎中接收到的 `transactions`(Tendermint 只能处理 `[]bytes` 类型的数据) +2. 从 `transactions` 中提取 `messages` 并进行基本的健全性检查。 +3. 将每个 Message 路由到对应的模块中,以进行相应处理。 +4. 提交状态更改。 -应用同样可以生成交易,进行编码并传递给底层的Tendermint来进行广播 +## BaseApp -## `baseapp` +`baseapp` 是 Cosmos SDK 应用程序的样本实现,它拥有能够处理和底层共识引擎的连接的 ABCI 实现。通常,Cosmos SDK 应用程序通过嵌入[`app.go`](https://docs.cosmos.network/master/basics/app-anatomy.html#core-application-file)来实现拓展。查看示例请参考 SDK 应用教程: -`baseApp` 是Cosmos SDK的ABCI的实现样板。里面的 `router` 用来把交易路由到对应的模块。我们应用程序的主体文件`app.go` 将自定义`app`类型,它将嵌入`baseapp`。这样,自定义的`app`类型将自动继承`baseapp`的所有方法。阅览[SDK应用教程](https://github.com/cosmos/sdk-application-tutorial/blob/master/app.go#L27)代码示例。 ++++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/app.go -`baseapp`的目的是在存储和可扩展状态机的之间提供安全接口,同时尽可能少地定义该状态机(保持对ABCI的真实性)。 +`baseapp` 的目标是在存储和可拓展状态机之间提供安全的接口,同时尽可能少地定义状态机(对 ABCI 保持不变)。 -有关`baseapp`的更多信息,请点击[这里](../concepts/baseapp.md)。 +更多关于`baseapp`的信息,请点击[这里](https://docs.cosmos.network/master/core/baseapp.html)。 ## Multistore -Cosmos SDK 为状态持久化提供了 multistore 。multistore 允许开发者声明任意数量的[`KVStores`](https://github.com/blocklayerhq/chainkit)。`KVStores`只接受`[]byte`类型作为值,因此任何自定义的类型都需要在存储之前使用[go-amino](https://github.com/tendermint/go-amino)进行编码。 +Cosmos SDK 为状态持久化提供了 `multistore`。Multistore 允许开发者声明任意数量的 `KVStores`。这些 `KVStores` 只接受 `[]byte` 类型的值,因此任何自定义的结构都需要在存储之前使用[codec](https://docs.cosmos.network/master/core/encoding.html)进行编码。 -multistore 抽象用于区分不同的模块的状态,每个都由其自身模块管理。要了解更多关于 multistore 的信息,点击[这里](../concepts/store.md) +Multistore 抽象用于区分不同模块的状态,每个都由其自己的模块管理。更多关于 multistore 的信息请点击[这里](https://docs.cosmos.network/master/core/store.html#multistore)。 ## Modules -Cosmos SDK 的强大之处在于其模块化开发的理念。应用程序通过把一组可以互相操作的模块组合起来进行构建。每个模块定义状态子集,并包含其自己的消息/交易处理器,而SDK负责将每条消息路由到其各自归属的模块。 +Cosmos SDK 的强大之处在于其模块化开发的理念。SDK 应用程序是通过组合一系列可互操作的模块而构建的。每个模块定义了状态子集,并包含其 Messages 与 Transactions 的处理器,同时 SDK 负责将每个 Message 路由到对应的模块中。 -下面是一个简化视图, 旨在说明每个应用链的全节点是如何处理接收的有效块中交易的: +以下的简化视图展示了应用链中的每个全节点如何处理有效区块中的 Transaction。 -``` +```md + | - | 交易通过全节点的 Tendermint 引擎的DeliverTx - | 传递到应用层 + | Transaction relayed from the full-node's Tendermint engine + | to the node's application via DeliverTx + | | | +---------------------v--------------------------+ - | 应用(层) | + | APPLICATION | | | - | 用 baseapp 的方法: 解码 Tx, | - | 提取及路由消息 | + | Using baseapp's methods: Decode the Tx, | + | extract and route the message(s) | | | +---------------------+--------------------------+ | @@ -52,15 +53,16 @@ Cosmos SDK 的强大之处在于其模块化开发的理念。应用程序通过 | | | - | 消息传给相应的模块处理 + | Message routed to the correct + | module to be processed | | +----------------+ +---------------+ +----------------+ +------v----------+ | | | | | | | | | AUTH MODULE | | BANK MODULE | | STAKING MODULE | | GOV MODULE | | | | | | | | | -| | | | | | | 处理消息, 更改状态 | -| | | | | | | | +| | | | | | | Handles message,| +| | | | | | | Updates state | | | | | | | | | +----------------+ +---------------+ +----------------+ +------+----------+ | @@ -69,18 +71,17 @@ Cosmos SDK 的强大之处在于其模块化开发的理念。应用程序通过 | +--------------------------+ | - | 返回结果到 Tendermint + | Return result to Tendermint | (0=Ok, 1=Err) v ``` -每个模块都可以看做一个小型的状态机。开发人员需要定义模块所处理的状态的子集,以及修改状态的message自定义类型(*注意* : message 是由 `baseapp` 的方法从交易中提取的)。通常,每个模块在 multistore 声明它自己的`KVStore` 来持久化保存它所定义的状态子集。大多数开发者在构建自己的模块时也需要访问其他的第三方模块。鉴于Cosmos-SDK是一个开源的框架,一些模块可能是恶意的,这就意味着需要安全原则来合理化模块之间的交互。这些原则基于[object-capabilities](./ocap.md)。实际上,这意味着不是让每个模块保留其他模块的访问控制列表,而是每个模块都实现称作`keeper`的特殊对象,这些对象可以传递给其他模块并授予预先定义的一组能力。 +每个模块都可以看成是一个小的状态机。开发者需要定义由模块处理的状态子集,同时自定义改变状态的 Message 类型(注意:`messages` 是通过 `baseapp` 从 `transactions` 中提取的)。通常,每个模块会在 `multistore` 中声明自己的 `KVStore`,以存储自定义的状态子集。大部分开发者在构建自己的模块时,需要访问其它第三方模块。由于 Cosmos SDK 是一个开放的框架,其中的一些模块可能是恶意的,这意味着需要一套安全原则去考虑模块间的交互。这些原则都基于[object-capabilities](https://docs.cosmos.network/master/core/ocap.html)。事实上,这也意味着,并不是要让每个模块都保留其他模块的访问控制列表,而是每个模块都实现了被称为 `keepers` 的特殊对象,它们可以被传递给其他模块,以授予一组预定义的功能。 -SDK模块在SDK的`x/`目录下定义。一些核心模块包括: -+ `x/auth`: 用于管理账户和签名. -+ `x/bank`: 用于实现token和token转账. -+ `x/staking` + `x/slashing`: 用于构建POS区块链. +SDK 模块被定义在 SDK 的 `x/` 文件夹中,一些核心的模块包括: -除了`x/`中已有的模块,任何人都可以在他们的应用程序中使用它们自己定义的模块。你可以查看[示例教程](https://learnblockchain.cn/docs/cosmos/tutorial/04-keeper.html)。 +- `x/auth`:用于管理账户和签名。 +- `x/bank`:用于启动 tokens 和 token 转账。 +- `x/staking` + `s/slashing`:用于构建 POS 区块链。 -### 接下来 学习 Cosmos SDK 安全模型,[ocap](./ocap.md) +除了 `x/` 文件夹中已经存在的任何人都可以使用的模块,SDK 还允许您构建自己自定义的模块,您可以在[教程中查看示例](https://cosmos.network/docs/tutorial/keeper.html)。 diff --git a/docs/cn/intro/why-app-specific.md b/docs/cn/intro/why-app-specific.md new file mode 100644 index 000000000000..199dbaa17024 --- /dev/null +++ b/docs/cn/intro/why-app-specific.md @@ -0,0 +1,85 @@ +# 特定应用区块链 + +## 概要 + +本文档解释了什么是特定应用区块链,以及为什么开发者更希望构建特定应用区块链,而不是开发智能合约。 + +## 什么是特定应用区块链? + +特定应用区块链是面向单个具体应用程序的高度定制化区块链。与基于像以太坊这样的底层区块链搭建去中心化应用不同,开发者需要从头构建他们自己的区块链。这意味着构建全节点客户端、轻节点客户端和所有必要的接口(CLI, REST, 等等)来和节点交互。 + +``` + ^ +-------------------------------+ ^ + | | | | Built with Cosmos SDK + | | State-machine = Application | | + | | | v + | +-------------------------------+ + | | | ^ +Blockchain node | | Consensus | | + | | | | + | +-------------------------------+ | Tendermint Core + | | | | + | | Networking | | + | | | | + v +-------------------------------+ v +``` + +## 智能合约的局限是什么? + +早在 2014 年,像 Ethereum 这样的虚拟机区块链就满足了可编程性的需求。当时,开发去中心化应用的选项非常有限。许多开发者只能在复杂且有限制的比特币脚本语言上开发,或者 fork 难以运行和定制化的比特币代码。 + +虚拟机区块链在当时提出了新的价值主张。他们的状态机集成了虚拟机,从而能够执行被称为智能合约的图灵完备程序。虽然智能合约在一次性事件(如 ICO)的应用场景下非常有用,但在构建复杂的去中心化平台时无法达到要求。以下是原因: + +- 智能合约通常由可以被底层虚拟机解释的特定编程语言开发。这些编程语言常常并不成熟,并受限于虚拟机本身。例如,以太坊虚拟机并不允许开发者实现代码的自动执行。开发或者也被限制于 EVM 的账户体系,他们只能从一组有限的功能中进行加密操作。虽然这些只是示例,但它们展现了智能合约环境通常缺少**灵活性**。 + +- 智能合约都运行在同一台虚拟机上,这意味着它们会相互争夺资源,并严重影响执行效果。即使状态机切分成多个子集(例如通过分片技术),智能合约依然需要由虚拟机解释,比起在状态机上实现的本地应用程序,这依然限制了合约应用的**性能**。(我们的基准测试表明,在删除虚拟机后,应用程序的性能提高了 10 倍。) + +- 智能合约共享底层环境带来的另一个问题是**主导权**的最终限制。去中心化应用是一个涉及众多参与者的生态系统,如果去中心化应用建立在通用的虚拟机区块链上,利益相关者(stakeholders)对他们的应用程序就只有非常有限的主导权,并最终会被底层区块链的治理所取代。如果该应用程序还存在着漏洞,那任何人都无能为力。 + +特定应用区块链的出现,就是要解决上述问题。 + +## 特定应用区块链的优势 + +### 灵活性 + +特定应用区块链赋予了开发者最大的灵活性: + +- 在 Cosmos 区块链中,状态机通常通过被称为 [ABCI](https://tendermint.com/docs/spec/abci/) 的接口和底层共识引擎连接。该接口可以被包装为任何编程语言,开发者可以自己决定用哪种编程语言来构建状态机。 + +- 开发者在构建状态机时有多种选择,目前最常用的是 Cosmos SDK,但也有其他的框架,如 [Lotion](https://github.com/nomic-io/lotion) 和 [Weave](https://github.com/iov-one/weave) 等。开发者通常都是基于他们使用的编程语言来选择使用哪一种框架(Cosmos SDK 和 Weave 基于 Golang,Lotion 则基于 JavaScript)。 + +- ABCI 允许开发者更换特定应用链的共识引擎。目前只有 Tendermint 共识可以投入使用,但在未来还会有更多共识引擎可被使用。 + +- 即使已经选好了开发框架和共识引擎,但如果他们不能完全符合原始格式的要求,开发者依然可以对其进行调整。 + +- 开发者可以自由探索出最能满足实际需求的方案(如验证人数量 vs Transaction 吞吐量;安全性 vs 异步可用性等)和链的设计选项(如 DB 存储或 IAVL 树;UTXO 或账户模型,等)。 + +- 开发者可以实现代码的自动执行。在 Cosmos SDK 中,每个块的开头和结尾都可以自动触发逻辑。与虚拟机区块链环境下的合约开发者不同,特定应用区块链的开发者可以自由地选择应用程序所需要的加密库,而不会受到底层环境的功能限制。 + +上述的列表展示了特定应用区块链给予开发者的充分灵活性。Cosmos 和 Cosmos SDK 的目标是让开发者工具尽可能的通用化、模块化,从而在保持兼容的情况下对堆栈的每个部分进行分叉、调整和优化。随着社区的不断发展,每个核心构建模块都将有更多可替代方案,为开发者提供更多选项。 + +### 性能 + +基于智能合约的去中心化应用在性能方面会天然地受到底层环境的限制。如果一个去中心化应用要进行性能优化,就需要将其构建为特定应用区块链。以下是特定应用区块链在性能方面的优势: + +- 特定应用区块链开发者可以选择像 Tendermint BFT 这样的新型共识引擎。与目前被大多数虚拟机区块链使用的 POW 共识相比,Tendermint BFT 在吞吐量方面有显著提高。 + +- 一个特定应用区块链只运行单个应用程序,所以该应用程序不需要和其他程序去竞争计算资源和存储资源。这与目前所有非分片虚拟机区块链正好相反,在这些区块链中的智能合约都会争夺计算和存储资源。 + +- 即使某种虚拟机区块链能够提供基于应用程序的分片和高效的共识算法,其性能也依然会被虚拟机本身所限制。真正的吞吐量瓶颈在于状态机,要求 Transaction 由虚拟机解释会大大增加处理它们的计算复杂度。 + +### 安全性 + +安全性很难进行量化,而且不同区块链平台之间存在很大差异。以下是特定应用区块链所能带来的重要优势: + +- 与不成熟的智能合约编程语言相反,开发者可以在构建特定应用区块链时选择像 Golang 这种可靠性已被验证的编程语言。 + +- 开发者不会局限于底层虚拟机所提供的加密功能,他们可以使用自定义的加密技术,也可以依赖经过可靠审核的加密库。 + +- 开发者无需担心底层虚拟机中潜在的漏洞或可被利用的机制,从而可以更容易地确保应用程序的安全性。 + +### 主导权 + +特定应用区块链的一大好处是主导权。去中心化应用是一个涉及众多参与者的生态系统,如用户、开发者、第三方服务,等等。当开发者在多个去中心化应用共存的虚拟机区块链上开发应用程序时,出现的一个问题是围绕应用程序所组成的社区人群和底层链的社区人群并不是一样的,但后者却会在治理的过程中取代前者。如果应用程序中存在一个漏洞,或者需要上线新的功能,应用的 stakeholders 几乎没有任何办法升级代码。如果底层区块链社区拒绝执行,那应用程序就无法升级。 + +根本问题是应用程序的治理和网络治理并不是统一的,而这个问题可以通过特定应用区块链解决。因为特定应用区块链只专门运行单个应用程序,所以应用的 stakeholders 对整条链有完全的主导权。这能确保社区在漏洞被发现时不会卡住,而且有充分的自由去选择链和应用程序的演化方向。 diff --git a/docs/core/README.md b/docs/core/README.md index 6bfd2feb3fb7..94a993f5a556 100644 --- a/docs/core/README.md +++ b/docs/core/README.md @@ -6,15 +6,22 @@ parent: # Core Concepts -This repository contains reference documentation on the core conepts of the Cosmos SDK. +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. [Events](./events.md) -8. [Object-Capabilities](./ocap.md) +1. [`BaseApp`](./baseapp.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, head on to the [Building Modules documentation](../building-modules/README.md) to learn more about the process of building modules. \ No newline at end of file +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/baseapp.md b/docs/core/baseapp.md index 04dbdfc61b47..67dbd557e032 100644 --- a/docs/core/baseapp.md +++ b/docs/core/baseapp.md @@ -1,11 +1,12 @@ -# Baseapp +# BaseApp -## Pre-requisite Readings {hide} +This document describes `BaseApp`, the abstraction that implements the core functionalities of an SDK application. {synopsis} + +## Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} - [Lifecycle of an SDK transaction](../basics/tx-lifecycle.md) {prereq} @@ -14,11 +15,9 @@ synopsis: This document describes `BaseApp`, the abstraction that implements the `BaseApp` is a base type that implements the core of an SDK application, namely: -- The [Application Blockchain Interface](#abci), for the state-machine to communicate with the -underlying consensus engine (e.g. Tendermint). -- A [Router](#routing), to route messages and queries to the appropriate module. -- Different [states](#states), as the state-machine can have different volatile -states updated based on the ABCI message received. +- The [Application Blockchain Interface](#abci), for the state-machine to communicate with the underlying consensus engine (e.g. Tendermint). +- [Service Routers](#service-routers), to route messages and queries to the appropriate module. +- Different [states](#states), as the state-machine can have different volatile states updated based on the ABCI message received. The goal of `BaseApp` is to provide the fundamental layer of an SDK application that developers can easily extend to build their own custom application. Usually, @@ -27,7 +26,7 @@ developers will create a custom type for their application, like so: ```go type App struct { // reference to a BaseApp - *bam.BaseApp + *baseapp.BaseApp // list of application store keys @@ -39,68 +38,67 @@ type App struct { Extending the application with `BaseApp` gives the former access to all of `BaseApp`'s methods. This allows developers to compose their custom application with the modules they want, while not -having to concern themselves with the hard work of implementing the ABCI, the routing and state +having to concern themselves with the hard work of implementing the ABCI, the service routers and state management logic. ## Type Definition -The `BaseApp` type holds many important parameters for any Cosmos SDK based application. +The `BaseApp` type holds many important parameters for any Cosmos SDK based application. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L54-L108 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L46-L131 Let us go through the most important components. -> __Note__: Not all parameters are described, only the most important ones. Refer to the -type definition for the full list. +> **Note**: Not all parameters are described, only the most important ones. Refer to the +> type definition for the full list. First, the important parameters that are initialized during the bootstrapping of the application: - [`CommitMultiStore`](./store.md#commitmultistore): This is the main store of the application, -which holds the canonical state that is committed at the [end of each block](#commit). This store -is **not** cached, meaning it is not used to update the application's volatile (un-committed) states. -The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application -uses one or multiple `KVStores` in the multi-store to persist their subset of the state. + which holds the canonical state that is committed at the [end of each block](#commit). This store + is **not** cached, meaning it is not used to update the application's volatile (un-committed) states. + The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application + uses one or multiple `KVStores` in the multi-store to persist their subset of the state. - Database: The `db` is used by the `CommitMultiStore` to handle data persistence. -- [Router](#message-routing): The `router` facilitates the routing of `messages` to the appropriate -module for it to be processed. Here a `message` refers to the transaction components that need to be -processed by the application in order to update the state, and not to ABCI messages which implement -the interface between the application and the underlying consensus engine. -- [Query Router](#query-routing): The `query router` facilitates the routing of queries to the -appropriate module for it to be processed. These `queries` are not ABCI messages themselves, but they -are relayed to the application from the underlying consensus engine via the ABCI message [`Query`](#query). +- [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of service `Msg`s to the appropriate + module for it to be processed. Here a service `Msg` refers to the transaction components that need to be + processed by the application in order to update the state, and not to ABCI messages which implement + the interface between the application and the underlying consensus engine. +- [gRPC Query Router](#grpc-query-router): The `grpcQueryRouter` facilitates the routing of gRPC queries to the + appropriate module for it to be processed. These queries are not ABCI messages themselves, but they + are relayed to the relevant module's gRPC `Query` service. - [`TxDecoder`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#TxDecoder): It is used to decode -raw transaction bytes relayed by the underlying Tendermint engine. -- `BaseKey`: This key is used to access the main store in the `CommitMultiStore`. The main store is -used to persist data related to the core of the application, like consensus parameters. + raw transaction bytes relayed by the underlying Tendermint engine. +- [`ParamStore`](#paramstore): The parameter store used to get and set application consensus parameters. - [`AnteHandler`](#antehandler): This handler is used to handle signature verification, fee payment, -and other pre-message execution checks when a transaction is received. It's executed during -[`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx). + and other pre-message execution checks when a transaction is received. It's executed during + [`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx). - [`InitChainer`](../basics/app-anatomy.md#initchainer), -[`BeginBlocker` and `EndBlocker`](../basics/app-anatomy.md#beginblocker-and-endblocker): These are -the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock` -ABCI messages from the underlying Tendermint engine. + [`BeginBlocker` and `EndBlocker`](../basics/app-anatomy.md#beginblocker-and-endblocker): These are + the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock` + ABCI messages from the underlying Tendermint engine. Then, parameters used to define [volatile states](#volatile-states) (i.e. cached states): - `checkState`: This state is updated during [`CheckTx`](#checktx), and reset on [`Commit`](#commit). - `deliverState`: This state is updated during [`DeliverTx`](#delivertx), and set to `nil` on -[`Commit`](#commit) and gets re-initialized on BeginBlock. + [`Commit`](#commit) and gets re-initialized on BeginBlock. Finally, a few more important parameterd: - `voteInfos`: This parameter carries the list of validators whose precommit is missing, either -because they did not vote or because the proposer did not include their vote. This information is -carried by the [Context](#context) and can be used by the application for various things like -punishing absent validators. + because they did not vote or because the proposer did not include their vote. This information is + carried by the [Context](#context) and can be used by the application for various things like + punishing absent validators. - `minGasPrices`: This parameter defines the minimum gas prices accepted by the node. This is a -**local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the -`AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction -enters the [mempool](https://tendermint.com/docs/tendermint-core/mempool.html#transaction-ordering) -only if the gas prices of the transaction are greater than one of the minimum gas price in -`minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be -greater than `1uatom` OR `1photon`). + **local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the + `AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction + enters the [mempool](https://tendermint.com/docs/tendermint-core/mempool.html#transaction-ordering) + only if the gas prices of the transaction are greater than one of the minimum gas price in + `minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be + greater than `1uatom` OR `1photon`). - `appVersion`: Version of the application. It is set in the -[application's constructor function](../basics/app-anatomy.md#constructor-function). + [application's constructor function](../basics/app-anatomy.md#constructor-function). ## Constructor @@ -114,38 +112,39 @@ func NewBaseApp( ``` The `BaseApp` constructor function is pretty straightforward. The only thing worth noting is the -possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/options.go) +possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/options.go) to the `BaseApp`, which will execute them in order. The `options` are generally `setter` functions for important parameters, like `SetPruning()` to set pruning options or `SetMinGasPrices()` to set the node's `min-gas-prices`. Naturally, developers can add additional `options` based on their application's needs. -## State Updates +## State Updates The `BaseApp` maintains two primary volatile states and a root or main state. The main state is the canonical state of the application and the volatile states, `checkState` and `deliverState`, are used to handle state transitions in-between the main state made during [`Commit`](#commit). Internally, there is only a single `CommitMultiStore` which we refer to as the main or root state. -From this root state, we derive two volatile state through a mechanism called cache-wrapping. The -types can be illustrated as follows: +From this root state, we derive two volatile states by using a mechanism called _store branching_ (performed by `CacheWrap` function). +The types can be illustrated as follows: ![Types](./baseapp_state_types.png) ### InitChain State Updates -During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by cache-wrapping -the root `CommitMultiStore`. Any subsequent reads and writes happen on cached versions of the `CommitMultiStore`. +During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by branching +the root `CommitMultiStore`. Any subsequent reads and writes happen on branched versions of the `CommitMultiStore`. +To avoid unnecessary roundtrip to the main state, all reads to the branched store are cached. ![InitChain](./baseapp_state-initchain.png) ### CheckTx State Updates During `CheckTx`, the `checkState`, which is based off of the last committed state from the root -store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a router -exists for every message in the transaction. Note, when we execute the `AnteHandler`, we cache-wrap -the already cache-wrapped `checkState`. This has the side effect that if the `AnteHandler` fails, +store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a service router +exists for every message in the transaction. Note, when we execute the `AnteHandler`, we branch +the already branched `checkState`. This has the side effect that if the `AnteHandler` fails, the state transitions won't be reflected in the `checkState` -- i.e. `checkState` is only updated on success. @@ -154,7 +153,7 @@ success. ### BeginBlock State Updates During `BeginBlock`, the `deliverState` is set for use in subsequent `DeliverTx` ABCI messages. The -`deliverState` is based off of the last committed state from the root store and is cache-wrapped. +`deliverState` is based off of the last committed state from the root store and is branched. Note, the `deliverState` is set to `nil` on [`Commit`](#commit). ![BeginBlock](./baseapp_state-begin_block.png) @@ -163,7 +162,7 @@ Note, the `deliverState` is set to `nil` on [`Commit`](#commit). The state flow for `DeliverTx` is nearly identical to `CheckTx` except state transitions occur on the `deliverState` and messages in a transaction are executed. Similarly to `CheckTx`, state transitions -occur on a doubly cache-wrapped state -- `deliverState`. Successful message execution results in +occur on a doubly branched state -- `deliverState`. Successful message execution results in writes being committed to `deliverState`. Note, if message execution fails, state transitions from the AnteHandler are persisted. @@ -178,36 +177,44 @@ newly committed state and `deliverState` is set to `nil` to be reset on `BeginBl ![Commit](./baseapp_state-commit.png) -## Routing +## ParamStore + +During `InitChain`, the `RequestInitChain` provides `ConsensusParams` which contains parameters +related to block execution such as maximum gas and size in addition to evidence parameters. If these +parameters are non-nil, they are set in the BaseApp's `ParamStore`. Behind the scenes, the `ParamStore` +is actually managed by an `x/params` module `Subspace`. This allows the parameters to be tweaked via +on-chain governance. + +## Service Routers -When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `baseapp`, which holds a `router` for messages, and a `query router` for queries. +When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `BaseApp`, which holds a `msgServiceRouter` for messages, and a `grpcQueryRouter` for queries. -### Message Routing +### `Msg` Service Router -[`Message`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `baseapp` holds a `router` which maps `paths` (`string`) to the appropriate module [`handler`](../building-modules/handler.md) using the `.Route(ctx sdk.Context, path string)` function. Usually, the `path` is the name of the module. +[`Msg`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's `Msg` Protobuf service) to the appropriate module's `Msg` server implementation. -The [default router included in baseapp](https://github.com/cosmos/cosmos-sdk/blob/master/baseapp/router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into the `Route` function of the [Router interface](https://github.com/cosmos/cosmos-sdk/blob/master/types/router.go#L12). For a stateless router that doesn't want to make use of this, can just ignore the ctx. +The [default `msgServiceRouter` included in `BaseApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into each [route handler inside `msgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go#L31-L32). For a stateless router that doesn't want to make use of this, you can just ignore the `ctx`. -The application's `router` is initilalized with all the routes using the application's [module manager](../building-modules/module-manager.md#manager), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor). +The application's `msgServiceRouter` is initialized with all the routes using the application's [module manager](../building-modules/module-manager.md#manager) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor). -### Query Routing +### gRPC Query Router -Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/querier.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query). +Similar to `Msg`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `Query` server implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint. -Just like the `router`, the `query router` is initilalized with all the query routes using the application's [module manager](../building-modules/module-manager.md), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor). +Just like the `msgServiceRouter`, the `grpcQueryRouter` is initialized with all the query routes using the application's [module manager](../building-modules/module-manager.md) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor). ## Main ABCI Messages -The [Application-Blockchain Interface](https://tendermint.com/docs/spec/abci/) (ABCI) is a generic interface that connects a state-machine with a consensus engine to form a functional full-node. It can be wrapped in any language, and needs to be implemented by each application-specific blockchain built on top of an ABCI-compatible consensus engine like Tendermint. +The [Application-Blockchain Interface](https://tendermint.com/docs/spec/abci/) (ABCI) is a generic interface that connects a state-machine with a consensus engine to form a functional full-node. It can be wrapped in any language, and needs to be implemented by each application-specific blockchain built on top of an ABCI-compatible consensus engine like Tendermint. The consensus engine handles two main tasks: - The networking logic, which mainly consists in gossiping block parts, transactions and consensus votes. -- The consensus logic, which results in the deterministic ordering of transactions in the form of blocks. +- The consensus logic, which results in the deterministic ordering of transactions in the form of blocks. -It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on. +It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on. -Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `baseapp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `baseapp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) +Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `BaseApp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `BaseApp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ### CheckTx @@ -220,18 +227,18 @@ Unconfirmed transactions are relayed to peers only if they pass `CheckTx`. make them lightweight. In the Cosmos SDK, after [decoding transactions](./encoding.md), `CheckTx()` is implemented to do the following checks: -1. Extract the `message`s from the transaction. -2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `messages`. This is done +1. Extract the `Msg`s from the transaction. +2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `Msg`s. This is done first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If `ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources. 3. Perform non-module related _stateful_ checks on the [account](../basics/accounts.md). This step is mainly about checking - that the `message` signatures are valid, that enough fees are provided and that the sending account + that the `Msg` signatures are valid, that enough fees are provided and that the sending account has enough funds to pay for said fees. Note that no precise [`gas`](../basics/gas-fees.md) counting occurs here, - as `message`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided + as `Msg`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided with the transaction is superior to a minimum reference gas amount based on the raw transaction size, in order to avoid spam with transactions that provide 0 gas. -4. Ensure that a [`Route`](#message-routing) exists for each `message`, but do **not** actually - process `message`s. `Message`s only need to be processed when the canonical state need to be updated, +4. Ensure that each `Msg`'s fully-qualified service method matches on of the routes inside the `msgServiceRouter`, but do **not** actually + process `Msg`s. `Msg`s only need to be processed when the canonical state need to be updated, which happens during `DeliverTx`. Steps 2. and 3. are performed by the [`AnteHandler`](../basics/gas-fees.md#antehandler) in the [`RunTx()`](#runtx-antehandler-and-runmsgs) @@ -249,13 +256,13 @@ is actually included in a block, because `checkState` never gets committed to th `CheckTx` returns a response to the underlying consensus engine of type [`abci.ResponseCheckTx`](https://tendermint.com/docs/spec/abci/abci.html#messages). The response contains: -- `Code (uint32)`: Response Code. `0` if successful. +- `Code (uint32)`: Response Code. `0` if successful. - `Data ([]byte)`: Result bytes, if any. - `Log (string):` The output of the application's logger. May be non-deterministic. - `Info (string):` Additional information. May be non-deterministic. -- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. +- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. - `GasUsed (int64)`: Amount of gas consumed by transaction. During `CheckTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction. Next is an example: - +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/ante/basic.go#L104 + +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/ante/basic.go#L104-L105 - `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./events.md) for more. - `Codespace (string)`: Namespace for the Code. @@ -272,27 +279,27 @@ This allows certain checks like signature verification can be skipped during `Ch When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends a `DeliverTx` message to the application for each transaction in a sequential order. -Before the first transaction of a given block is processed, a [volatile state](#volatile-states) called `deliverState` is intialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what is is set to `nil`. +Before the first transaction of a given block is processed, a [volatile state](#volatile-states) called `deliverState` is intialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what is is set to `nil`. `DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step: 1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose. -2. For each `message` in the transaction, route to the appropriate module's [`handler`](../building-modules/handler.md). Additional _stateful_ checks are performed, and the cache-wrapped multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `handler` returns successfully, the cache-wrapped multistore held in `context` is written to `deliverState` `CacheMultiStore`. +2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`. -During step 5., each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: +During the additional fifth step outlined in (2), each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L142-L150 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L164-L175 -At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails. +At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails. `DeliverTx` returns a response to the underlying consensus engine of type [`abci.ResponseDeliverTx`](https://tendermint.com/docs/spec/abci/abci.html#delivertx). The response contains: -- `Code (uint32)`: Response Code. `0` if successful. +- `Code (uint32)`: Response Code. `0` if successful. - `Data ([]byte)`: Result bytes, if any. - `Log (string):` The output of the application's logger. May be non-deterministic. - `Info (string):` Additional information. May be non-deterministic. -- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. -- `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs. +- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction. +- `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs. - `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./events.md) for more. - `Codespace (string)`: Namespace for the Code. @@ -300,41 +307,41 @@ At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0 ### RunTx -`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded. +`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded. -The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a cached version of the main store instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. +The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. -After that, `RunTx()` calls `ValidateBasic()` on each `message`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `message` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. +After that, `RunTx()` calls `ValidateBasic()` on each `Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. -Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function. +Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L587 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L623-L630 -This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK. +This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK. -Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `messages`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function. +Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. ### AnteHandler -The `AnteHandler` is a special handler that implements the `anteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed. +The `AnteHandler` is a special handler that implements the `AnteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/handler.go#L8 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/handler.go#L6-L8 The `AnteHandler` is theoretically optional, but still a very important component of public blockchain networks. It serves 3 primary purposes: -- Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./transactions.md#transaction-generation) checking. -- Perform preliminary *stateful* validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees. -- Play a role in the incentivisation of stakeholders via the collection of transaction fees. +- Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./transactions.md#transaction-generation) checking. +- Perform preliminary _stateful_ validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees. +- Play a role in the incentivisation of stakeholders via the collection of transaction fees. -`baseapp` holds an `anteHandler` as paraemter, which is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` today is that of the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/ante/ante.go). +`BaseApp` holds an `anteHandler` as parameter that is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` is the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/ante/ante.go). -Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`. +Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`. ### RunMsgs -`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `message`s. +`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `Msg`s. -First, it retreives the `message`'s `route` using the `Msg.Route()` method. Then, using the application's [`router`](#routing) and the `route`, it checks for the existence of a `handler`. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. If instead `mode == runTxModeDeliver`, the [`handler`](../building-modules/handler.md) function for the message is executed, before `RunMsgs` returns. +First, it retrieves the `Msg`'s fully-qualified service method name, by checking the `type_url` of the Protobuf `Any` representing the service `Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of this fully-qualified service method. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. If instead `mode == runTxModeDeliver`, the [`Msg` server](../building-modules/msg-services.md) implementation for the message is executed, before `RunMsgs` returns. ## Other ABCI Messages @@ -342,50 +349,50 @@ First, it retreives the `message`'s `route` using the `Msg.Route()` method. Then The [`InitChain` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#initchain) is sent from the underlying Tendermint engine when the chain is first started. It is mainly used to **initialize** parameters and state like: -- [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`. +- [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`. - [`checkState` and `deliverState`](#volatile-states) via `setCheckState` and `setDeliverState`. -- The [block gas meter](../basics/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions. +- The [block gas meter](../basics/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions. -Finally, the `InitChain(req abci.RequestInitChain)` method of `baseapp` calls the [`initChainer()`](../basics/app-anatomy.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules. +Finally, the `InitChain(req abci.RequestInitChain)` method of `BaseApp` calls the [`initChainer()`](../basics/app-anatomy.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules. ### BeginBlock The [`BeginBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#beginblock) is sent from the underlying Tendermint engine when a block proposal created by the correct proposer is received, before [`DeliverTx`](#delivertx) is run for each transaction in the block. It allows developers to have logic be executed at the beginning of each block. In the Cosmos SDK, the `BeginBlock(req abci.RequestBeginBlock)` method does the following: -- Initialize [`deliverState`](#volatile-states) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function. +- Initialize [`deliverState`](#volatile-states) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function. +++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L387-L397 -This function also resets the [main gas meter](../basics/gas-fees.md#main-gas-meter). -- Initialize the [block gas meter](../basics/gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters. + This function also resets the [main gas meter](../basics/gas-fees.md#main-gas-meter). +- Initialize the [block gas meter](../basics/gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters. - Run the application's [`beginBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. -- Set the [`VoteInfos`](https://tendermint.com/docs/app-dev/abci-spec.html#voteinfo) of the application, i.e. the list of validators whose *precommit* for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./context.md) so that it can be used during `DeliverTx` and `EndBlock`. +- Set the [`VoteInfos`](https://tendermint.com/docs/app-dev/abci-spec.html#voteinfo) of the application, i.e. the list of validators whose _precommit_ for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./context.md) so that it can be used during `DeliverTx` and `EndBlock`. ### EndBlock -The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. +The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules. ### Commit -The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received *precommits* from 2/3+ of validators (weighted by voting power). On the `baseapp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block. +The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received _precommits_ from 2/3+ of validators (weighted by voting power). On the `BaseApp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block. -To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a cached multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. +To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a branched multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. -Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block. +Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block. ### Info -The [`Info` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `baseapp` will return the application's name, version and the hash of the last commit of `app.cms`. +The [`Info` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `BaseApp` will return the application's name, version and the hash of the last commit of `app.cms`. -### Query +### Query -The [`Query` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#query) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It is the main entrypoint to build interfaces with the application. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://tendermint.com/docs/app-dev/abci-spec.html#query). +The [`Query` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#query) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It used to be the main entrypoint to build interfaces with the application, but with the introduction of [gRPC queries](../building-modules/query-services.md) in Cosmos SDK v0.40, its usage is more limited. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://tendermint.com/docs/app-dev/abci-spec.html#query). -Each `query` comes with a `path`, which contains multiple `string`s. By convention, the first element of the `path` (`path[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom`). The `baseapp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries: +Each Tendermint `query` comes with a `path`, which is a `string` which denotes what to query. If the `path` matches a gRPC fully-qualified service method, then `BaseApp` will defer the query to the `grpcQueryRouter` and let it handle it like explained [above](#grpc-query-router). Otherwise, the `path` represents a query that is not (yet) handled by the gRPC router. `BaseApp` splits the `path` string with the `/` delimiter. By convention, the first element of the splitted string (`splitted[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom` ). The `BaseApp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries: - Application-related queries like querying the application's version, which are served via the `handleQueryApp` method. -- Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queryeis are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers. -- P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `baseapp`'s [constructor](#constructor). -- Custom queries, which encompass most queries, are served via the `handleQueryCustom` method. The `handleQueryCustom` cache-wraps the multistore before using the `queryRoute` obtained from [`app.queryRouter`](#query-routing) to map the query to the appropriate module's `querier`. +- Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queryeis are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers. +- P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `BaseApp`'s [constructor](#constructor). +- Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` branches the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/query-services.md#legacy-queriers). ## Next {hide} -Learn more about [transactions](./transactions.md) {hide} \ No newline at end of file +Learn more about [transactions](./transactions.md) {hide} diff --git a/docs/core/cli.md b/docs/core/cli.md new file mode 100644 index 000000000000..364e9714c73a --- /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, the 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/v0.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-L119 + +## 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 f54c9d036d93..c9e89b16a471 100644 --- a/docs/core/context.md +++ b/docs/core/context.md @@ -1,11 +1,12 @@ # Context -## Pre-requisites Readings {hide} +The `context` is a data structure intended to be passed from function to function that carries information about the current state of the application. It provides an access to a branched storage (a safe branch of the entire state) as well as useful objects and information like `gasMeter`, `block height`, `consensus parameters` and more. {synopsis} + +## Pre-requisites Readings - [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq} - [Lifecycle of a Transaction](../basics/tx-lifecycle.md) {prereq} @@ -14,25 +15,9 @@ synopsis: The `context` is a data structure intended to be passed from function 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. -```go -type Context struct { - ctx context.Context - ms MultiStore - header abci.Header - chainID string - txBytes []byte - logger log.Logger - voteInfo []abci.VoteInfo - gasMeter GasMeter - blockGasMeter GasMeter - checkTx bool - minGasPrice DecCoins - consParams *abci.ConsensusParams - eventManager *EventManager -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L16-L39 -- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below. +- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below. - **Multistore:** Every application's `BaseApp` contains a [`CommitMultiStore`](./store.md#multistore) which is provided when a `Context` is created. Calling the `KVStore()` and `TransientStore()` methods allows modules to fetch their respective [`KVStore`](./store.md#base-layer-kvstores) using their unique `StoreKey`. - **ABCI Header:** The [header](https://tendermint.com/docs/spec/abci/abci.html#header) is an ABCI type. It carries important information about the state of the blockchain, such as block height and proposer of the current block. - **Chain ID:** The unique identification number of the blockchain a block pertains to. @@ -41,10 +26,10 @@ type Context struct { - **VoteInfo:** A list of the ABCI type [`VoteInfo`](https://tendermint.com/docs/spec/abci/abci.html#voteinfo), which includes the name of a validator and a boolean indicating whether they have signed the block. - **Gas Meters:** Specifically, a [`gasMeter`](../basics/gas-fees.md#main-gas-meter) for the transaction currently being processed using the context and a [`blockGasMeter`](../basics/gas-fees.md#block-gas-meter) for the entire block it belongs to. Users specify how much in fees they wish to pay for the execution of their transaction; these gas meters keep track of how much [gas](../basics/gas-fees.md) has been used in the transaction or block so far. If the gas meter runs out, execution halts. - **CheckTx Mode:** A boolean value indicating whether a transaction should be processed in `CheckTx` or `DeliverTx` mode. -- **Min Gas Price:** The minimum [gas](../basics/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**. +- **Min Gas Price:** The minimum [gas](../basics/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**. - **Consensus Params:** The ABCI type [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters), which specify certain limits for the blockchain, such as maximum gas for a block. - **Event Manager:** The event manager allows any caller with access to a `Context` to emit [`Events`](./events.md). Modules may define module specific -`Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example: + `Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example: ```go ctx.EventManager().EmitEvent(sdk.NewEvent( @@ -62,28 +47,30 @@ are also designed to enable concurrency and to be used in goroutines. Contexts are intended to be **immutable**; they should never be edited. Instead, the convention is to create a child context from its parent using a `With` function. For example: -``` go +```go childCtx = parentCtx.WithBlockHeader(header) ``` The [Golang Context Package](https://golang.org/pkg/context) documentation instructs developers to explicitly pass a context `ctx` as the first argument of a process. -## Cache Wrapping +## Store branching -The `Context` contains a `MultiStore`, which allows for cache-wrapping functionality: a `CacheMultiStore` -where each `KVStore` is is wrapped with an ephemeral cache. Processes are free to write changes to -the `CacheMultiStore`, then write the changes back to the original state or disregard them if something +The `Context` contains a `MultiStore`, which allows for branchinig and caching functionality using `CacheMultiStore` +(queries in `CacheMultiStore` are cached to avoid future round trips). +Each `KVStore` is branched in a safe and isolated ephemeral storage. Processes are free to write changes to +the `CacheMultiStore`. If a state-transition sequence is performed without issue, the store branch can +be committed to the underlying store at the end of the sequence or disregard them if something goes wrong. The pattern of usage for a Context is as follows: 1. A process receives a Context `ctx` from its parent process, which provides information needed to perform the process. -2. The `ctx.ms` is **cache wrapped**, i.e. a cached copy of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. +2. The `ctx.ms` is a **branched store**, i.e. a branch of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. 3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass -`ctx` to it as needed. + `ctx` to it as needed. 4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing -needs to be done - the cache wrapped `ctx` is simply discarded. If successful, the changes made to -the cache-wrapped `MultiStore` can be committed to the original `ctx.ms` via `Write()`. + needs to be done - the branch `ctx` is simply discarded. If successful, the changes made to + the `CacheMultiStore` can be committed to the original `ctx.ms` via `Write()`. For example, here is a snippet from the [`runTx`](./baseapp.md#runtx-and-runmsgs) function in [`baseapp`](./baseapp.md): @@ -105,12 +92,12 @@ if result.IsOK() { Here is the process: 1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()` -to cache-wrap the context and multistore. -2. The cache-wrapped context, `runMsgCtx`, is used in `runMsgs` to return a result. + to branch and cache the context and multistore. +2. `runMsgCtx` - the context with branched store, is used in `runMsgs` to return a result. 3. If the process is running in [`checkTxMode`](./baseapp.md#checktx), there is no need to write the -changes - the result is returned immediately. + changes - the result is returned immediately. 4. If the process is running in [`deliverTxMode`](./baseapp.md#delivertx) and the result indicates -a successful run over all the messages, the cached multistore is written back to the original. + a successful run over all the messages, the branched multistore is written back to the original. ## Next {hide} diff --git a/docs/core/encoding.md b/docs/core/encoding.md index 6629612a3a24..fc390f79dd5c 100644 --- a/docs/core/encoding.md +++ b/docs/core/encoding.md @@ -1,44 +1,260 @@ # Encoding -## Pre-requisite Readings {hide} +While encoding in the SDK used to be mainly handled by `go-amino` codec, the SDK is moving towards using `gogoprotobuf` for both state and client-side encoding. {synopsis} + +## Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} ## Encoding -Every Cosmos SDK application exposes a global `codec` to marshal/unmarshal structs and interfaces in order to store and/or transfer them. As of now, the `codec` used in the Cosmos SDK is [go-amino](https://github.com/tendermint/go-amino), which possesses the following important properties: +The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) which is an object encoding specification and [Protocol Buffers](https://developers.google.com/protocol-buffers), a subset of Proto3 with an extension for +interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) +for more information on Proto3, which Amino is largely compatible with (but not with Proto2). + +Due to Amino having significant performance drawbacks, being reflection-based, and +not having any meaningful cross-language/client support, Protocol Buffers, specifically +[gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. +Note, this process of using Protocol Buffers over Amino is still an ongoing process. + +Binary wire encoding of types in the Cosmos SDK can be broken down into two main +categories, client encoding and store encoding. Client encoding mainly revolves +around transaction processing and signing, whereas store encoding revolves around +types used in state-machine transitions and what is ultimately stored in the Merkle +tree. + +For store encoding, protobuf definitions can exist for any type and will typically +have an Amino-based "intermediary" type. Specifically, the protobuf-based type +definition is used for serialization and persistence, whereas the Amino-based type +is used for business logic in the state-machine where they may converted back-n-forth. +Note, the Amino-based types may slowly be phased-out in the future so developers +should take note to use the protobuf message definitions where possible. + +In the `codec` package, there exists two core interfaces, `Marshaler` and `ProtoMarshaler`, +where the former encapsulates the current Amino interface except it operates on +types implementing the latter instead of generic `interface{}` types. -- Interface support. -- Deterministic encoding of value (which is required considering that blockchains are deterministic replicated state-machines). -- Upgradeable schemas. +In addition, there exists two implementations of `Marshaler`. The first being +`AminoCodec`, where both binary and JSON serialization is handled via Amino. The +second being `ProtoCodec`, where both binary and JSON serialization is handled +via Protobuf. -The application's `codec` is typically initialized in the [application's constructor function](../basics/app-anatomy.md#constructor-function), where it is also passed to each of the application's modules via the [basic manager](../building-modules/module-manager.md#basic-manager). +This means that modules may use Amino or Protobuf encoding but the types must +implement `ProtoMarshaler`. If modules wish to avoid implementing this interface +for their types, they may use an Amino codec directly. -Among other things, the `codec` is used by module's [`keeper`s](../building-modules/keeper.md) to marshal objects into `[]byte` before storing them in the module's [`KVStore`](./store.md#kvstore), or to unmarshal them from `[]byte` when retrieving them: +### Amino + +Every module uses an Amino codec to serialize types and interfaces. This codec typically +has types and interfaces registered in that module's domain only (e.g. messages), +but there are exceptions like `x/gov`. Each module exposes a `RegisterLegacyAminoCodec` function +that allows a user to provide a codec and have all the types registered. An application +will call this method for each necessary module. + +Where there is no protobuf-based type definition for a module (see below), Amino +is used to encode and decode raw wire bytes to the concrete type or interface: ```go -// typical pattern to marshal an object to []byte before storing it -bz := keeper.cdc.MustMarshalBinaryBare(object) +bz := keeper.cdc.MustMarshalBinaryBare(typeOrInterface) +keeper.cdc.MustUnmarshalBinaryBare(bz, &typeOrInterface) +``` + +Note, there are length-prefixed variants of the above functionality and this is +typically used for when the data needs to be streamed or grouped together +(e.g. `ResponseDeliverTx.Data`) + +### Gogoproto + +Modules are encouraged to utilize Protobuf encoding for their respective types. In the SDK, we use the [Gogoproto](https://github.com/gogo/protobuf) specific implementation of the Protobuf spec that offers speed and DX improvements compared to the official [Google protobuf implementation](https://github.com/protocolbuffers/protobuf). + +### Guidelines for protobuf message definitions + +In addition to [following official Protocol Buffer guidelines](https://developers.google.com/protocol-buffers/docs/proto3#simple), we recommend using these annotations in .proto files when dealing with interfaces: + +- use `cosmos_proto.accepts_interface` to annote fields that accept interfaces +- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` +- annotate interface implementations with `cosmos_proto.implements_interface` +- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` + +### Transaction Encoding + +Another important use of Protobuf is the encoding and decoding of +[transactions](./transactions.md). Transactions are defined by the application or +the SDK but are then passed to the underlying consensus engine to be relayed to +other peers. Since the underlying consensus engine is agnostic to the application, +the consensus engine accepts only transactions in the form of raw bytes. + +- The `TxEncoder` object performs the encoding. +- The `TxDecoder` object performs the decoding. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/types/tx_msg.go#L83-L87 -//typical pattern to unmarshal an object from []byte when retrieving it -keeper.cdc.MustUnmarshalBinaryBare(bz, &object) +A standard implementation of both these objects can be found in the [`auth` module](../../x/auth/spec/README.md): + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/decoder.go + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/encoder.go + +See [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) for details of how a transaction is encoded. + +### Interface Encoding and Usage of `Any` + +The Protobuf DSL is strongly typed, which can make inserting variable-typed fields difficult. Imagine we want to create a `Profile` protobuf message that serves as a wrapper over [an account](../basics/accounts.md): + +```proto +message Profile { + // account is the account associated to a profile. + cosmos.auth.v1beta1.BaseAccount account = 1; + // bio is a short description of the account. + string bio = 4; +} +``` + +In this `Profile` example, we hardcoded `account` as a `BaseAccount`. However, there are several other types of [user accounts related to vesting](../../x/auth/spec/05_vesting.md), such as `BaseVestingAccount` or `ContinuousVestingAccount`. All of these accounts are different, but they all implement the `AccountI` interface. How would you create a `Profile` that allows all these types of accounts with an `account` field that accepts an `AccountI` interface? + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/types/account.go#L307-L330 + +In [ADR-019](../architecture/adr-019-protobuf-state-encoding.md), it has been decided to use [`Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto)s to encode interfaces in protobuf. An `Any` contains an arbitrary serialized message as bytes, along with a URL that acts as a globally unique identifier for and resolves to that message's type. This strategy allows us to pack arbitrary Go types inside protobuf messages. Our new `Profile` then looks like: + +```protobuf +message Profile { + // account is the account associated to a profile. + google.protobuf.Any account = 1 [ + (cosmos_proto.accepts_interface) = "AccountI"; // Asserts that this field only accepts Go types implementing `AccountI`. It is purely informational for now. + ]; + // bio is a short description of the account. + string bio = 4; +} ``` -Alternatively, it is possible to use `MustMarshalBinaryLengthPrefixed`/`MustUnmarshalBinaryLengthPrefixed` instead of `MustMarshalBinaryBare`/`MustUnmarshalBinaryBare` for the same encoding prefixed by a `uvarint` encoding of the object to encode. +To add an account inside a profile, we need to "pack" it inside an `Any` first, using `codectypes.NewAnyWithValue`: -Another important use of the `codec` is the encoding and decoding of [transactions](./transactions.md). Transactions are defined at the Cosmos SDK level, but passed to the underlying consensus engine in order to be relayed to other peers. Since the underlying consensus engine is agnostic to the application, it only accepts transactions in the form of `[]byte`. The encoding is done by an object called `TxEncoder` and the decoding by an object called `TxDecoder`. +```go +var myAccount AccountI +myAccount = ... // Can be a BaseAccount, a ContinuousVestingAccount or any struct implementing `AccountI` -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L45-L49 +// Pack the account into an Any +accAny, err := codectypes.NewAnyWithValue(myAccount) +if err != nil { + return nil, err +} -A standard implementation of both these objects can be found in the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth): +// Create a new Profile with the any. +profile := Profile { + Account: accAny, + Bio: "some bio", +} + +// We can then marshal the profile as usual. +bz, err := cdc.MarshalBinaryBare(profile) +jsonBz, err := cdc.MarshalJSON(profile) +``` + +To summarize, to encode an interface, you must 1/ pack the interface into an `Any` and 2/ marshal the `Any`. For convenience, the SDK provides a `MarshalInterface` method to bundle these two steps. Have a look at [a real-life example in the x/auth module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/keeper/keeper.go#L218-L221). + +The reverse operation of retrieving the concrete Go type from inside an `Any`, called "unpacking", is done with the `GetCachedValue()` on `Any`. + +```go +profileBz := ... // The proto-encoded bytes of a Profile, e.g. retrieved through gRPC. +var myProfile Profile +// Unmarshal the bytes into the myProfile struct. +err := cdc.UnmarshalBinaryBare(profilebz, &myProfile) -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/stdtx.go#L241-L266 +// Let's see the types of the Account field. +fmt.Printf("%T\n", myProfile.Account) // Prints "Any" +fmt.Printf("%T\n", myProfile.Account.GetCachedValue()) // Prints "BaseAccount", "ContinuousVestingAccount" or whatever was initially packed in the Any. + +// Get the address of the accountt. +accAddr := myProfile.Account.GetCachedValue().(AccountI).GetAddress() +``` + +It is important to note that for `GetCachedValue()` to work, `Profile` (and any other structs embedding `Profile`) must implement the `UnpackInterfaces` method: + +```go +func (p *Profile) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + if p.Account != nil { + var account AccountI + return unpacker.UnpackAny(p.Account, &account) + } + + return nil +} +``` + +The `UnpackInterfaces` gets called recursively on all structs implementing this method, to allow all `Any`s to have their `GetCachedValue()` correctly populated. + +For more information about interface encoding, and especially on `UnpackInterfaces` and how the `Any`'s `type_url` gets resolved using the `InterfaceRegistry`, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md). + +#### `Any` Encoding in the SDK + +The above `Profile` example is a fictive example used for educational purposes. In the SDK, we use `Any` encoding in several places (non-exhaustive list): + +- the `cryptotypes.PubKey` interface for encoding different types of public keys, +- the `sdk.Msg` interface for encoding different `Msg`s in a transaction, +- the `AccountI` interface for encodinig different types of accounts (similar to the above example) in the x/auth query responses, +- the `Evidencei` interface for encoding different types of evidences in the x/evidence module, +- the `AuthorizationI` interface for encoding different types of x/authz authorizations. + +A real-life example of encoding the pubkey as `Any` inside the Validator struct in x/staking is shown in the following example: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/types/validator.go#L40-L61 + +## FAQ + +1. How to create modules using protobuf encoding? + +**Defining module types** + +Protobuf types can be defined to encode: + +- state +- [`Msg`s](../building-modules/messages-and-queries.md#messages) +- [Query services](../building-modules/query-services.md) +- [genesis](../building-modules/genesis.md) + +**Naming and conventions** + +We encourage developers to follow industry guidelines: [Protocol Buffers style guide](https://developers.google.com/protocol-buffers/docs/style) +and [Buf](https://buf.build/docs/style-guide), see more details in [ADR 023](../architecture/adr-023-protobuf-naming.md) + +2. How to update modules to protobuf encoding? + +If modules do not contain any interfaces (e.g. `Account` or `Content`), then they +may simply migrate any existing types that +are encoded and persisted via their concrete Amino codec to Protobuf (see 1. for further guidelines) and accept a `Marshaler` as the codec which is implemented via the `ProtoCodec` +without any further customization. + +However, if a module type composes an interface, it must wrap it in the `skd.Any` (from `/types` package) type. To do that, a module-level .proto file must use [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto) for respective message type interface types. + +For example, in the `x/evidence` module defines an `Evidence` interface, which is used by the `MsgSubmitEvidence`. The structure definition must use `sdk.Any` to wrap the evidence file. In the proto file we define it as follows: + +```protobuf +// proto/cosmos/evidence/v1beta1/tx.proto + +message MsgSubmitEvidence { + string submitter = 1; + google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; +} +``` + +The SDK `codec.Marshaler` interface provides support methods `MarshalInterface` and `UnmarshalInterface` to easy encoding of state to `Any`. + +Module should register interfaces using `InterfaceRegistry` which provides a mechanism for registering interfaces: `RegisterInterface(protoName string, iface interface{})` and implementations: `RegisterImplementations(iface interface{}, impls ...proto.Message)` that can be safely unpacked from Any, similarly to type registration with Amino: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/codec/types/interface_registry.go#L25-L66 + +In addition, an `UnpackInterfaces` phase should be introduced to deserialization to unpack interfaces before they're needed. Protobuf types that contain a protobuf `Any` either directly or via one of their members should implement the `UnpackInterfacesMessage` interface: + +```go +type UnpackInterfacesMessage interface { + UnpackInterfaces(InterfaceUnpacker) error +} +``` ## Next {hide} -Learn about [events](./events.md) {hide} \ No newline at end of file +Learn about [gRPC, REST and other endpoints](./grpc_rest.md) {hide} diff --git a/docs/core/events.md b/docs/core/events.md index aeb0efa1dea8..999d2876169e 100644 --- a/docs/core/events.md +++ b/docs/core/events.md @@ -1,29 +1,36 @@ # Events -## Pre-Requisite Readings {hide} +`Event`s are objects that contain information about the execution of the application. They are mainly used by service providers like block explorers and wallet to track the execution of various messages and index transactions. {synopsis} + +## Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} +- [Tendermint Documentation on Events](https://docs.tendermint.com/master/spec/abci/abci.html#events) {prereq} ## Events Events are implemented in the Cosmos SDK as an alias of the ABCI `Event` type and -take the form of: `{eventType}.{eventAttribute}={value}`. +take the form of: `{eventType}.{attributeKey}={attributeValue}`. + ++++ https://github.com/tendermint/tendermint/blob/v0.34.8/proto/tendermint/abci/types.proto#L304-L313 -+++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/abci/types/types.pb.go#L2661-L2667 +An Event contains: -Events contain: +- A `type` to categorize the Event at a high-level; for example, the SDK uses the `"message"` type to filter Events by `Msg`s. +- A list of `attributes` are key-value pairs that give more information about the categorized Event. For example, for the `"message"` type, we can filter Events by key-value pairs using `message.action={some_action}`, `message.module={some_module}` or `message.sender={some_sender}`. -- A `type`, which is meant to categorize an event at a high-level (e.g. by module or action). -- 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 +::: tip +To parse the attribute values as strings, make sure to add `'` (single quotes) around each attribute value. +::: + +Events, the `type` and `attributes` are defined on a **per-module basis** in the module's +`/types/events.go` file, and triggered from the module's [`Msg` service](../building-modules/msg-services.md) +by using the [`EventManager`](#eventmanager). In addition, each module documents its Events under +`spec/xx_events.md`. Events are returned to the underlying consensus engine in the response of the following ABCI messages: @@ -32,28 +39,35 @@ Events are returned to the underlying consensus engine in the response of the fo - [`CheckTx`](./baseapp.md#checktx) - [`DeliverTx`](./baseapp.md#delivertx) -Events, the `type` and `attributes`, are defined on a **per-module basis** in the module's -`/internal/types/events.go` file, and triggered from the module's [`handler`](../building-modules/handler.md) -via the [`EventManager`](#eventmanager). In addition, each module documents its events under -`spec/xx_events.md`. +### Examples + +The following examples show how to query Events using the SDK. + +| Event | Description | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tx.height=23` | Query all transactions at height 23 | +| `message.action='/cosmos.bank.v1beta1.Msg/Send'` | Query all transactions containing a x/bank `Send` [Service `Msg`](../building-modules/msg-services.md). Note the `'`s around the value. | +| `message.action='send'` | Query all transactions containing a x/bank `Send` [legacy `Msg`](../building-modules/msg-services.md#legacy-amino-msgs). Note the `'`s around the value. | +| `message.module='bank'` | Query all transactions containing messages from the x/bank module. Note the `'`s around the value. | +| `create_validator.validator='cosmosval1...'` | x/staking-specific Event, see [x/staking SPEC](../../../cosmos-sdk/x/staking/spec/07_events.md). | ## EventManager -In Cosmos SDK applications, events are managed by an abstraction called the `EventManager`. -Internally, the `EventManager` tracks a list of `Events` for the entire execution flow of a +In Cosmos SDK applications, Events are managed by an abstraction called the `EventManager`. +Internally, the `EventManager` tracks a list of Events for the entire execution flow of a transaction or `BeginBlock`/`EndBlock`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L16-L20 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/events.go#L17-L25 -The `EventManager` comes with a set of useful methods to manage events. Among them, the one that is -used the most by module and application developers is the `EmitEvent` method, which tracks -an `event` in the `EventManager`. +The `EventManager` comes with a set of useful methods to manage Events. The method +that is used most by module and application developers is `EmitEvent` that tracks +an Event in the `EventManager`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/events.go#L29-L31 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/types/events.go#L33-L37 -Module developers should handle event emission via the `EventManager#EmitEvent` in each message +Module developers should handle Event emission via the `EventManager#EmitEvent` in each message `Handler` and in each `BeginBlock`/`EndBlock` handler. The `EventManager` is accessed via -the [`Context`](./context.md), where event emission generally follows this pattern: +the [`Context`](./context.md), where Event emission generally follows this pattern: ```go ctx.EventManager().EmitEvent( @@ -61,49 +75,63 @@ ctx.EventManager().EmitEvent( ) ``` -See the [`Handler`](../building-modules/handler.md) concept doc for a more detailed -view on how to typically implement `Events` and use the `EventManager` in modules. +Module's `handler` function should also set a new `EventManager` to the `context` to isolate emitted Events per `message`: + +```go +func NewHandler(keeper Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { +``` + +See the [`Msg` services](../building-modules/msg-services.md) concept doc for a more detailed +view on how to typically implement Events and use the `EventManager` in modules. ## Subscribing to Events -It is possible to subscribe to `Events` via Tendermint's [Websocket](https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html#subscribing-to-events-via-websocket). -This is done by calling the `subscribe` RPC method via Websocket: +You can use Tendermint's [Websocket](https://docs.tendermint.com/master/tendermint-core/subscription.html#subscribing-to-events-via-websocket) to subscribe to Events by calling the `subscribe` RPC method: ```json { - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" - } + "jsonrpc": "2.0", + "method": "subscribe", + "id": "0", + "params": { + "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" + } } ``` The main `eventCategory` you can subscribe to are: -- `NewBlock`: Contains `events` triggered during `BeginBlock` and `EndBlock`. -- `Tx`: Contains `events` triggered during `DeliverTx` (i.e. transaction processing). +- `NewBlock`: Contains Events triggered during `BeginBlock` and `EndBlock`. +- `Tx`: Contains Events triggered during `DeliverTx` (i.e. transaction processing). - `ValidatorSetUpdates`: Contains validator set updates for the block. -These events are triggered from the `state` package after a block is committed. You can get the -full list of `event` categories [here](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). +These Events are triggered from the `state` package after a block is committed. You can get the +full list of Event categories [on the Tendermint Godoc page](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). -The `type` and `attribute` value of the `query` allow you to filter the specific `event` you are looking for. For example, a `transfer` transaction triggers an `event` of type `Transfer` and has `Recipient` and `Sender` as `attributes` (as defined in the [`events` file of the `bank` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/bank/internal/types/events.go)). Subscribing to this `event` would be done like so: +The `type` and `attribute` value of the `query` allow you to filter the specific Event you are looking for. For example, a `transfer` transaction triggers an Event of type `Transfer` and has `Recipient` and `Sender` as `attributes` (as defined in the [`events.go` file of the `bank` module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/bank/types/events.go)). Subscribing to this Event would be done like so: ```json { - "jsonrpc": "2.0", - "method": "subscribe", - "id": "0", - "params": { - "query": "tm.event='Tx' AND transfer.sender='senderAddress'" - } + "jsonrpc": "2.0", + "method": "subscribe", + "id": "0", + "params": { + "query": "tm.event='Tx' AND transfer.sender='senderAddress'" + } } ``` -where `senderAddress` is an address following the [`AccAddress`](../basics/accounts.md#addresses) format. +where `senderAddress` is an address following the [`AccAddress`](../basics/accounts.md#addresses) format. + +## Typed Events (coming soon) + +As previously described, Events are defined on a per-module basis. It is the responsibility of the module developer to define Event types and Event attributes. Except in the `spec/XX_events.md` file, these Event types and attributes are unfortunately not easily discoverable, so the SDK proposes to use Protobuf-defined [Typed Events](../architecture/adr-032-typed-events.md) for emitting and querying Events. + +The Typed Events proposal has not yet been fully implemented. Documentation is not yet available. ## Next {hide} -Learn about [object-capabilities](./ocap.md) {hide} +Learn about SDK [telemetry](./telemetry.md) {hide} diff --git a/docs/core/grpc_rest.md b/docs/core/grpc_rest.md new file mode 100644 index 000000000000..853fedd11269 --- /dev/null +++ b/docs/core/grpc_rest.md @@ -0,0 +1,113 @@ + + +# gRPC, REST, and Tendermint Endpoints + +This document presents an overview of all the endpoints a node exposes: gRPC, REST as well as some other endpoints. {synopsis} + +## An Overview of All Endpoints + +Each node exposes the following endpoints for users to interact with a node, each endpoint is served on a different port. Details on how to configure each endpoint is provided in the endpoint's own section. + +- the gRPC server (default port: `9090`), +- the REST server (default port: `1317`), +- the Tendermint RPC endpoint (default port: `26657`). + +::: tip +The node also exposes some other endpoints, such as the Tendermint P2P endpoint, or the [Prometheus endpoint](https://docs.tendermint.com/master/nodes/metrics.html#metrics), which are not directly related to the Cosmos SDK. Please refer to the [Tendermint documentation](https://docs.tendermint.com/master/tendermint-core/using-tendermint.html#configuration) for more information about these endpoints. +::: + +## gRPC Server + +::: warning +A patch introduced in `go-grpc v1.34.0` made gRPC incompatible with the `gogoproto` library, making some [gRPC queries](https://github.com/cosmos/cosmos-sdk/issues/8426) panic. As such, the SDK requires that `go-grpc <=v1.33.2` is installed in your `go.mod`. + +To make sure that gRPC is working properly, it is **highly recommended** to add the following line in your application's `go.mod`: + +``` +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 +``` + +Please see [issue #8392](https://github.com/cosmos/cosmos-sdk/issues/8392) for more info. +::: + +Cosmos SDK v0.40 introduced Protobuf as the main [encoding](./encoding) library, and this brings a wide range of Protobuf-based tools that can be plugged into the SDK. One such tool is [gRPC](https://grpc.io), a modern open source high performance RPC framework that has decent client support in several languages. + +Each module exposes [`Msg` and `Query` Protobuf services](../building-modules/messages-and-queries.md) to define state transitions and state queries. These services are hooked up to gRPC via the following function inside the application: + + + +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:9090`. + +:::tip +`~/.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. + +An overview of all available gRPC endpoints shipped with the Cosmos SDK is [Protobuf documention](./proto-docs.md). + +## REST Server + +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 `~/.simapp/config/app.toml`: + +- `api.enable = true|false` field defines if the REST server should be enabled. Defaults to `false`. +- `api.address = {string}` field defines the address (really, the port, since the host should be kept at `0.0.0.0`) the server should bind to. Defaults to `tcp://0.0.0.0:1317`. +- some additional API configuration options are defined in `~/.simapp/config/app.toml`, along with comments, please refer to that file directly. + +### gRPC-gateway REST Routes + +If, for various reasons, you cannot use gRPC (for example, you are building a web application, and browsers don't support HTTP2 on which gRPC is built), then the SDK offers REST routes via gRPC-gateway. + +[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.41.0/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. + +### Legacy REST API Routes + +The REST routes present in Cosmos SDK v0.39 and earlier are marked as deprecated via a [HTTP deprecation header](https://tools.ietf.org/id/draft-dalal-deprecation-header-01.html). They are still maintained to keep backwards compatibility, but will be removed in v0.41. For updating from Legacy REST routes to new gRPC-gateway REST routes, please refer to our [migration guide](../migrations/rest.md). + +For application developers, Legacy REST API routes needs to be wired up to the REST server, this is done by calling the `RegisterRESTRoutes` function on the ModuleManager. + +### Swagger + +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 `~/.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 `~/.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: + +- `/abci_query`: this endpoint will query the application for state. As the `path` parameter, you can send the following strings: + - any Protobuf fully-qualified service method, such as `/cosmos.bank.v1beta1.Query/AllBalances`. The `data` field should then include the method's request parameter(s) encoded as bytes using Protobuf. + - `/app/simulate`: this will simulate a transaction, and return some information such as gas used. + - `/app/version`: this will return the application's version. + - `/store/{path}`: this will query the store directly. + - `/p2p/filter/addr/{port}`: this will return a filtered list of the node's P2P peers by address port. + - `/p2p/filter/id/{id}`: this will return a filtered list of the node's P2P peers by ID. +- `/broadcast_tx_{aync,async,commit}`: these 3 endpoint will broadcast a transaction to other peers. CLI, gRPC and REST expose [a way to broadcast transations](./transactions.md#broadcasting-the-transaction), but they all use these 3 Tendermint RPCs under the hood. + +## Comparison Table + +| Name | Advantages | Disadvantages | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| gRPC | - can use code-generated stubs in various languages
- supports streaming and bidirectional communication (HTTP2)
- small wire binary sizes, faster transmission | - based on HTTP2, not available in browsers
- learning curve (mostly due to Protobuf) | +| REST | - ubiquitous
- client libraries in all languages, faster implementation
| - only supports unary request-response communication (HTTP1.1)
- bigger over-the-wire message sizes (JSON) | +| Tendermint RPC | - easy to use | - bigger over-the-wire message sizes (JSON) | + +## Next {hide} + +Learn about [the CLI](./cli.md) {hide} diff --git a/docs/core/node.md b/docs/core/node.md index 8ca558609504..ee8865f0eb6d 100644 --- a/docs/core/node.md +++ b/docs/core/node.md @@ -1,70 +1,76 @@ # Node Client (Daemon) -## Pre-requisite Readings {hide} +The main endpoint of an SDK application is the daemon client, otherwise known as the full-node client. The full-node runs the state-machine, starting from a genesis file. It connects to peers running the same client in order to receive and relay transactions, block proposals and signatures. The full-node is constituted of the application, defined with the Cosmos SDK, and of a consensus engine connected to the application via the ABCI. {synopsis} + +## Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} ## `main` function -The full-node client of any SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./cmd/appd/main.go` file. Running this function creates an executable `.appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node. +The full-node client of any SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./appd/cmd/main.go` file. Running this function creates an executable `appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node. In general, developers will implement the `main.go` function with the following structure: -- First, a [`codec`](./encoding.md) is instanciated for the application. +- First, an [`appCodec`](./encoding.md) is instanciated 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/7d7821b9af132b0f6131640195326aa02b6751db/types/config.go#L10-L21 -- 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`. -- Add default server commands to `rootCmd` using the `server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)` method. These commands are separated from the ones added above since they are standard and defined at SDK level. They should be shared by all SDK-based applications. They include the most important command: the [`start` command](#start-command). + +++ 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`. +- Add default server commands to `rootCmd` using the `server.AddCommands()` method. These commands are separated from the ones added above since they are standard and defined at SDK level. They should be shared by all SDK-based applications. They include the most important command: the [`start` command](#start-command). - Prepare and execute the `executor`. - +++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/libs/cli/setup.go#L75-L78 + +++ https://github.com/tendermint/tendermint/blob/v0.34.0-rc6/libs/cli/setup.go#L74-L78 -See an example of `main` function from the [`gaia`](https://github.com/cosmos/gaia) application: +See an example of `main` function from the `simapp` application, the SDK's application for demo purposes: -+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/cmd/gaiad/main.go ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/main.go ## `start` command The `start` command is defined in the `/server` folder of the Cosmos SDK. It is added to the root command of the full-node client in the [`main` function](#main-function) and called by the end-user to start their node: -```go -// For an example app named "app", the following command starts the full-node - +```bash +# For an example app named "app", the following command starts the full-node. appd start + +# Using the SDK's own simapp, the following commands start the simapp node. +simd start ``` -As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node. +As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node. -The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time. +The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time. With the `db`, the `start` command creates a new instance of the application using an `appCreator` function: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/start.go#L144 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L227-L228 + +Note that an `appCreator` is a function that fulfills the `AppCreator` signature: ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/types/app.go#L48-L50 -Note that an `appCreator` is a function that fulfills the `AppCreator` signature. In practice, the [constructor the application](../basics/app-anatomy.md#constructor-function) is passed as the `appCreator`. +In practice, the [constructor of the application](../basics/app-anatomy.md#constructor-function) is passed as the `appCreator`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L17-L25 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/cmd/root.go#L170-L215 Then, the instance of `app` is used to instanciate a new Tendermint node: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/start.go#L153-L163 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L235-L244 -The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/abci/types/application.go#L11-L26) (given that `app` extends [`baseapp`](./baseapp.md)). As part of the `NewNode` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `NewNode` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./baseapp.md#initchain) on the application to initialize the state from the genesis file. +The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/v0.34.0/abci/types/application.go#L7-L32) (given that `app` extends [`baseapp`](./baseapp.md)). As part of the `NewNode` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `NewNode` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./baseapp.md#initchain) on the application to initialize the state from the genesis file. Once the Tendermint node is instanciated and in sync with the application, the node can be started: -```go -if err := tmNode.Start(); err != nil { - return nil, err -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L250-L252 + +Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress. + +## Other commands -Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress. +To discover how to concretely run a node and interact with it, please refer to our [Running a Node, API and CLI](../run-node/README.md) guide. ## Next {hide} -Learn about the [store](./store.md) {hide} \ No newline at end of file +Learn about the [store](./store.md) {hide} diff --git a/docs/core/ocap.md b/docs/core/ocap.md index c3a6cf08ee53..db8d0b7e5c86 100644 --- a/docs/core/ocap.md +++ b/docs/core/ocap.md @@ -1,5 +1,5 @@ # Object-Capability Model @@ -39,7 +39,7 @@ foundation of an object capability system. > to another object only through a preexisting chain of references. > In short, "Only connectivity begets connectivity." -For an introduction to object-capabilities, see [this article](https://en.wikipedia.org/wiki/Object-capability_model). +For an introduction to object-capabilities, see this [Wikipedia article](https://en.wikipedia.org/wiki/Object-capability_model). ## Ocaps in practice @@ -50,11 +50,11 @@ principle: ```go type AppAccount struct {...} -var account := &AppAccount{ +account := &AppAccount{ Address: pub.Address(), Coins: sdk.Coins{sdk.NewInt64Coin("ATM", 100)}, } -var sumValue := externalModule.ComputeSumValue(account) +sumValue := externalModule.ComputeSumValue(account) ``` The method `ComputeSumValue` implies a pure function, yet the implied @@ -62,14 +62,14 @@ capability of accepting a pointer value is the capability to modify that value. The preferred method signature should take a copy instead. ```go -var sumValue := externalModule.ComputeSumValue(*account) +sumValue := externalModule.ComputeSumValue(*account) ``` In the Cosmos SDK, you can see the application of this principle in the gaia app. -+++ https://github.com/cosmos/gaia/blob/master/app/app.go#L197-L209 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.41.4/simapp/app.go#L249-L273 -## Next +## Next {hide} -Learn about [building modules](../building-modules/intro.md) {hide} \ No newline at end of file +Learn about the [`runTx` middleware](./runtx_middleware.md) {hide} diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md new file mode 100644 index 000000000000..0a8d35957e6e --- /dev/null +++ b/docs/core/proto-docs.md @@ -0,0 +1,9998 @@ + +# Protobuf Documentation + + +## Table of Contents + +- [cosmos/auth/v1beta1/auth.proto](#cosmos/auth/v1beta1/auth.proto) + - [BaseAccount](#cosmos.auth.v1beta1.BaseAccount) + - [ModuleAccount](#cosmos.auth.v1beta1.ModuleAccount) + - [Params](#cosmos.auth.v1beta1.Params) + +- [cosmos/auth/v1beta1/genesis.proto](#cosmos/auth/v1beta1/genesis.proto) + - [GenesisState](#cosmos.auth.v1beta1.GenesisState) + +- [cosmos/auth/v1beta1/query.proto](#cosmos/auth/v1beta1/query.proto) + - [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) + - [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) + - [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) + + - [Query](#cosmos.auth.v1beta1.Query) + +- [cosmos/base/v1beta1/coin.proto](#cosmos/base/v1beta1/coin.proto) + - [Coin](#cosmos.base.v1beta1.Coin) + - [DecCoin](#cosmos.base.v1beta1.DecCoin) + - [DecProto](#cosmos.base.v1beta1.DecProto) + - [IntProto](#cosmos.base.v1beta1.IntProto) + +- [cosmos/bank/v1beta1/bank.proto](#cosmos/bank/v1beta1/bank.proto) + - [DenomUnit](#cosmos.bank.v1beta1.DenomUnit) + - [Input](#cosmos.bank.v1beta1.Input) + - [Metadata](#cosmos.bank.v1beta1.Metadata) + - [Output](#cosmos.bank.v1beta1.Output) + - [Params](#cosmos.bank.v1beta1.Params) + - [SendEnabled](#cosmos.bank.v1beta1.SendEnabled) + - [Supply](#cosmos.bank.v1beta1.Supply) + +- [cosmos/bank/v1beta1/genesis.proto](#cosmos/bank/v1beta1/genesis.proto) + - [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) + - [QuerySupplyOfResponse](#cosmos.bank.v1beta1.QuerySupplyOfResponse) + - [QueryTotalSupplyRequest](#cosmos.bank.v1beta1.QueryTotalSupplyRequest) + - [QueryTotalSupplyResponse](#cosmos.bank.v1beta1.QueryTotalSupplyResponse) + + - [Query](#cosmos.bank.v1beta1.Query) + +- [cosmos/bank/v1beta1/tx.proto](#cosmos/bank/v1beta1/tx.proto) + - [MsgMultiSend](#cosmos.bank.v1beta1.MsgMultiSend) + - [MsgMultiSendResponse](#cosmos.bank.v1beta1.MsgMultiSendResponse) + - [MsgSend](#cosmos.bank.v1beta1.MsgSend) + - [MsgSendResponse](#cosmos.bank.v1beta1.MsgSendResponse) + + - [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) + +- [cosmos/base/reflection/v1beta1/reflection.proto](#cosmos/base/reflection/v1beta1/reflection.proto) + - [ListAllInterfacesRequest](#cosmos.base.reflection.v1beta1.ListAllInterfacesRequest) + - [ListAllInterfacesResponse](#cosmos.base.reflection.v1beta1.ListAllInterfacesResponse) + - [ListImplementationsRequest](#cosmos.base.reflection.v1beta1.ListImplementationsRequest) + - [ListImplementationsResponse](#cosmos.base.reflection.v1beta1.ListImplementationsResponse) + + - [ReflectionService](#cosmos.base.reflection.v1beta1.ReflectionService) + +- [cosmos/base/snapshots/v1beta1/snapshot.proto](#cosmos/base/snapshots/v1beta1/snapshot.proto) + - [Metadata](#cosmos.base.snapshots.v1beta1.Metadata) + - [Snapshot](#cosmos.base.snapshots.v1beta1.Snapshot) + +- [cosmos/base/store/v1beta1/commit_info.proto](#cosmos/base/store/v1beta1/commit_info.proto) + - [CommitID](#cosmos.base.store.v1beta1.CommitID) + - [CommitInfo](#cosmos.base.store.v1beta1.CommitInfo) + - [StoreInfo](#cosmos.base.store.v1beta1.StoreInfo) + +- [cosmos/base/store/v1beta1/snapshot.proto](#cosmos/base/store/v1beta1/snapshot.proto) + - [SnapshotIAVLItem](#cosmos.base.store.v1beta1.SnapshotIAVLItem) + - [SnapshotItem](#cosmos.base.store.v1beta1.SnapshotItem) + - [SnapshotStoreItem](#cosmos.base.store.v1beta1.SnapshotStoreItem) + +- [cosmos/base/tendermint/v1beta1/query.proto](#cosmos/base/tendermint/v1beta1/query.proto) + - [GetBlockByHeightRequest](#cosmos.base.tendermint.v1beta1.GetBlockByHeightRequest) + - [GetBlockByHeightResponse](#cosmos.base.tendermint.v1beta1.GetBlockByHeightResponse) + - [GetLatestBlockRequest](#cosmos.base.tendermint.v1beta1.GetLatestBlockRequest) + - [GetLatestBlockResponse](#cosmos.base.tendermint.v1beta1.GetLatestBlockResponse) + - [GetLatestValidatorSetRequest](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetRequest) + - [GetLatestValidatorSetResponse](#cosmos.base.tendermint.v1beta1.GetLatestValidatorSetResponse) + - [GetNodeInfoRequest](#cosmos.base.tendermint.v1beta1.GetNodeInfoRequest) + - [GetNodeInfoResponse](#cosmos.base.tendermint.v1beta1.GetNodeInfoResponse) + - [GetSyncingRequest](#cosmos.base.tendermint.v1beta1.GetSyncingRequest) + - [GetSyncingResponse](#cosmos.base.tendermint.v1beta1.GetSyncingResponse) + - [GetValidatorSetByHeightRequest](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightRequest) + - [GetValidatorSetByHeightResponse](#cosmos.base.tendermint.v1beta1.GetValidatorSetByHeightResponse) + - [Module](#cosmos.base.tendermint.v1beta1.Module) + - [Validator](#cosmos.base.tendermint.v1beta1.Validator) + - [VersionInfo](#cosmos.base.tendermint.v1beta1.VersionInfo) + + - [Service](#cosmos.base.tendermint.v1beta1.Service) + +- [cosmos/capability/v1beta1/capability.proto](#cosmos/capability/v1beta1/capability.proto) + - [Capability](#cosmos.capability.v1beta1.Capability) + - [CapabilityOwners](#cosmos.capability.v1beta1.CapabilityOwners) + - [Owner](#cosmos.capability.v1beta1.Owner) + +- [cosmos/capability/v1beta1/genesis.proto](#cosmos/capability/v1beta1/genesis.proto) + - [GenesisOwners](#cosmos.capability.v1beta1.GenesisOwners) + - [GenesisState](#cosmos.capability.v1beta1.GenesisState) + +- [cosmos/crisis/v1beta1/genesis.proto](#cosmos/crisis/v1beta1/genesis.proto) + - [GenesisState](#cosmos.crisis.v1beta1.GenesisState) + +- [cosmos/crisis/v1beta1/tx.proto](#cosmos/crisis/v1beta1/tx.proto) + - [MsgVerifyInvariant](#cosmos.crisis.v1beta1.MsgVerifyInvariant) + - [MsgVerifyInvariantResponse](#cosmos.crisis.v1beta1.MsgVerifyInvariantResponse) + + - [Msg](#cosmos.crisis.v1beta1.Msg) + +- [cosmos/crypto/ed25519/keys.proto](#cosmos/crypto/ed25519/keys.proto) + - [PrivKey](#cosmos.crypto.ed25519.PrivKey) + - [PubKey](#cosmos.crypto.ed25519.PubKey) + +- [cosmos/crypto/multisig/keys.proto](#cosmos/crypto/multisig/keys.proto) + - [LegacyAminoPubKey](#cosmos.crypto.multisig.LegacyAminoPubKey) + +- [cosmos/crypto/multisig/v1beta1/multisig.proto](#cosmos/crypto/multisig/v1beta1/multisig.proto) + - [CompactBitArray](#cosmos.crypto.multisig.v1beta1.CompactBitArray) + - [MultiSignature](#cosmos.crypto.multisig.v1beta1.MultiSignature) + +- [cosmos/crypto/secp256k1/keys.proto](#cosmos/crypto/secp256k1/keys.proto) + - [PrivKey](#cosmos.crypto.secp256k1.PrivKey) + - [PubKey](#cosmos.crypto.secp256k1.PubKey) + +- [cosmos/distribution/v1beta1/distribution.proto](#cosmos/distribution/v1beta1/distribution.proto) + - [CommunityPoolSpendProposal](#cosmos.distribution.v1beta1.CommunityPoolSpendProposal) + - [CommunityPoolSpendProposalWithDeposit](#cosmos.distribution.v1beta1.CommunityPoolSpendProposalWithDeposit) + - [DelegationDelegatorReward](#cosmos.distribution.v1beta1.DelegationDelegatorReward) + - [DelegatorStartingInfo](#cosmos.distribution.v1beta1.DelegatorStartingInfo) + - [FeePool](#cosmos.distribution.v1beta1.FeePool) + - [Params](#cosmos.distribution.v1beta1.Params) + - [ValidatorAccumulatedCommission](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommission) + - [ValidatorCurrentRewards](#cosmos.distribution.v1beta1.ValidatorCurrentRewards) + - [ValidatorHistoricalRewards](#cosmos.distribution.v1beta1.ValidatorHistoricalRewards) + - [ValidatorOutstandingRewards](#cosmos.distribution.v1beta1.ValidatorOutstandingRewards) + - [ValidatorSlashEvent](#cosmos.distribution.v1beta1.ValidatorSlashEvent) + - [ValidatorSlashEvents](#cosmos.distribution.v1beta1.ValidatorSlashEvents) + +- [cosmos/distribution/v1beta1/genesis.proto](#cosmos/distribution/v1beta1/genesis.proto) + - [DelegatorStartingInfoRecord](#cosmos.distribution.v1beta1.DelegatorStartingInfoRecord) + - [DelegatorWithdrawInfo](#cosmos.distribution.v1beta1.DelegatorWithdrawInfo) + - [GenesisState](#cosmos.distribution.v1beta1.GenesisState) + - [ValidatorAccumulatedCommissionRecord](#cosmos.distribution.v1beta1.ValidatorAccumulatedCommissionRecord) + - [ValidatorCurrentRewardsRecord](#cosmos.distribution.v1beta1.ValidatorCurrentRewardsRecord) + - [ValidatorHistoricalRewardsRecord](#cosmos.distribution.v1beta1.ValidatorHistoricalRewardsRecord) + - [ValidatorOutstandingRewardsRecord](#cosmos.distribution.v1beta1.ValidatorOutstandingRewardsRecord) + - [ValidatorSlashEventRecord](#cosmos.distribution.v1beta1.ValidatorSlashEventRecord) + +- [cosmos/distribution/v1beta1/query.proto](#cosmos/distribution/v1beta1/query.proto) + - [QueryCommunityPoolRequest](#cosmos.distribution.v1beta1.QueryCommunityPoolRequest) + - [QueryCommunityPoolResponse](#cosmos.distribution.v1beta1.QueryCommunityPoolResponse) + - [QueryDelegationRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationRewardsRequest) + - [QueryDelegationRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationRewardsResponse) + - [QueryDelegationTotalRewardsRequest](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsRequest) + - [QueryDelegationTotalRewardsResponse](#cosmos.distribution.v1beta1.QueryDelegationTotalRewardsResponse) + - [QueryDelegatorValidatorsRequest](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsRequest) + - [QueryDelegatorValidatorsResponse](#cosmos.distribution.v1beta1.QueryDelegatorValidatorsResponse) + - [QueryDelegatorWithdrawAddressRequest](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressRequest) + - [QueryDelegatorWithdrawAddressResponse](#cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressResponse) + - [QueryParamsRequest](#cosmos.distribution.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.distribution.v1beta1.QueryParamsResponse) + - [QueryValidatorCommissionRequest](#cosmos.distribution.v1beta1.QueryValidatorCommissionRequest) + - [QueryValidatorCommissionResponse](#cosmos.distribution.v1beta1.QueryValidatorCommissionResponse) + - [QueryValidatorOutstandingRewardsRequest](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsRequest) + - [QueryValidatorOutstandingRewardsResponse](#cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse) + - [QueryValidatorSlashesRequest](#cosmos.distribution.v1beta1.QueryValidatorSlashesRequest) + - [QueryValidatorSlashesResponse](#cosmos.distribution.v1beta1.QueryValidatorSlashesResponse) + + - [Query](#cosmos.distribution.v1beta1.Query) + +- [cosmos/distribution/v1beta1/tx.proto](#cosmos/distribution/v1beta1/tx.proto) + - [MsgFundCommunityPool](#cosmos.distribution.v1beta1.MsgFundCommunityPool) + - [MsgFundCommunityPoolResponse](#cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse) + - [MsgSetWithdrawAddress](#cosmos.distribution.v1beta1.MsgSetWithdrawAddress) + - [MsgSetWithdrawAddressResponse](#cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse) + - [MsgWithdrawDelegatorReward](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward) + - [MsgWithdrawDelegatorRewardResponse](#cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse) + - [MsgWithdrawValidatorCommission](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission) + - [MsgWithdrawValidatorCommissionResponse](#cosmos.distribution.v1beta1.MsgWithdrawValidatorCommissionResponse) + + - [Msg](#cosmos.distribution.v1beta1.Msg) + +- [cosmos/evidence/v1beta1/evidence.proto](#cosmos/evidence/v1beta1/evidence.proto) + - [Equivocation](#cosmos.evidence.v1beta1.Equivocation) + +- [cosmos/evidence/v1beta1/genesis.proto](#cosmos/evidence/v1beta1/genesis.proto) + - [GenesisState](#cosmos.evidence.v1beta1.GenesisState) + +- [cosmos/evidence/v1beta1/query.proto](#cosmos/evidence/v1beta1/query.proto) + - [QueryAllEvidenceRequest](#cosmos.evidence.v1beta1.QueryAllEvidenceRequest) + - [QueryAllEvidenceResponse](#cosmos.evidence.v1beta1.QueryAllEvidenceResponse) + - [QueryEvidenceRequest](#cosmos.evidence.v1beta1.QueryEvidenceRequest) + - [QueryEvidenceResponse](#cosmos.evidence.v1beta1.QueryEvidenceResponse) + + - [Query](#cosmos.evidence.v1beta1.Query) + +- [cosmos/evidence/v1beta1/tx.proto](#cosmos/evidence/v1beta1/tx.proto) + - [MsgSubmitEvidence](#cosmos.evidence.v1beta1.MsgSubmitEvidence) + - [MsgSubmitEvidenceResponse](#cosmos.evidence.v1beta1.MsgSubmitEvidenceResponse) + + - [Msg](#cosmos.evidence.v1beta1.Msg) + +- [cosmos/genutil/v1beta1/genesis.proto](#cosmos/genutil/v1beta1/genesis.proto) + - [GenesisState](#cosmos.genutil.v1beta1.GenesisState) + +- [cosmos/gov/v1beta1/gov.proto](#cosmos/gov/v1beta1/gov.proto) + - [Deposit](#cosmos.gov.v1beta1.Deposit) + - [DepositParams](#cosmos.gov.v1beta1.DepositParams) + - [Proposal](#cosmos.gov.v1beta1.Proposal) + - [TallyParams](#cosmos.gov.v1beta1.TallyParams) + - [TallyResult](#cosmos.gov.v1beta1.TallyResult) + - [TextProposal](#cosmos.gov.v1beta1.TextProposal) + - [Vote](#cosmos.gov.v1beta1.Vote) + - [VotingParams](#cosmos.gov.v1beta1.VotingParams) + + - [ProposalStatus](#cosmos.gov.v1beta1.ProposalStatus) + - [VoteOption](#cosmos.gov.v1beta1.VoteOption) + +- [cosmos/gov/v1beta1/genesis.proto](#cosmos/gov/v1beta1/genesis.proto) + - [GenesisState](#cosmos.gov.v1beta1.GenesisState) + +- [cosmos/gov/v1beta1/query.proto](#cosmos/gov/v1beta1/query.proto) + - [QueryDepositRequest](#cosmos.gov.v1beta1.QueryDepositRequest) + - [QueryDepositResponse](#cosmos.gov.v1beta1.QueryDepositResponse) + - [QueryDepositsRequest](#cosmos.gov.v1beta1.QueryDepositsRequest) + - [QueryDepositsResponse](#cosmos.gov.v1beta1.QueryDepositsResponse) + - [QueryParamsRequest](#cosmos.gov.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.gov.v1beta1.QueryParamsResponse) + - [QueryProposalRequest](#cosmos.gov.v1beta1.QueryProposalRequest) + - [QueryProposalResponse](#cosmos.gov.v1beta1.QueryProposalResponse) + - [QueryProposalsRequest](#cosmos.gov.v1beta1.QueryProposalsRequest) + - [QueryProposalsResponse](#cosmos.gov.v1beta1.QueryProposalsResponse) + - [QueryTallyResultRequest](#cosmos.gov.v1beta1.QueryTallyResultRequest) + - [QueryTallyResultResponse](#cosmos.gov.v1beta1.QueryTallyResultResponse) + - [QueryVoteRequest](#cosmos.gov.v1beta1.QueryVoteRequest) + - [QueryVoteResponse](#cosmos.gov.v1beta1.QueryVoteResponse) + - [QueryVotesRequest](#cosmos.gov.v1beta1.QueryVotesRequest) + - [QueryVotesResponse](#cosmos.gov.v1beta1.QueryVotesResponse) + + - [Query](#cosmos.gov.v1beta1.Query) + +- [cosmos/gov/v1beta1/tx.proto](#cosmos/gov/v1beta1/tx.proto) + - [MsgDeposit](#cosmos.gov.v1beta1.MsgDeposit) + - [MsgDepositResponse](#cosmos.gov.v1beta1.MsgDepositResponse) + - [MsgSubmitProposal](#cosmos.gov.v1beta1.MsgSubmitProposal) + - [MsgSubmitProposalResponse](#cosmos.gov.v1beta1.MsgSubmitProposalResponse) + - [MsgVote](#cosmos.gov.v1beta1.MsgVote) + - [MsgVoteResponse](#cosmos.gov.v1beta1.MsgVoteResponse) + + - [Msg](#cosmos.gov.v1beta1.Msg) + +- [cosmos/mint/v1beta1/mint.proto](#cosmos/mint/v1beta1/mint.proto) + - [Minter](#cosmos.mint.v1beta1.Minter) + - [Params](#cosmos.mint.v1beta1.Params) + +- [cosmos/mint/v1beta1/genesis.proto](#cosmos/mint/v1beta1/genesis.proto) + - [GenesisState](#cosmos.mint.v1beta1.GenesisState) + +- [cosmos/mint/v1beta1/query.proto](#cosmos/mint/v1beta1/query.proto) + - [QueryAnnualProvisionsRequest](#cosmos.mint.v1beta1.QueryAnnualProvisionsRequest) + - [QueryAnnualProvisionsResponse](#cosmos.mint.v1beta1.QueryAnnualProvisionsResponse) + - [QueryInflationRequest](#cosmos.mint.v1beta1.QueryInflationRequest) + - [QueryInflationResponse](#cosmos.mint.v1beta1.QueryInflationResponse) + - [QueryParamsRequest](#cosmos.mint.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.mint.v1beta1.QueryParamsResponse) + + - [Query](#cosmos.mint.v1beta1.Query) + +- [cosmos/params/v1beta1/params.proto](#cosmos/params/v1beta1/params.proto) + - [ParamChange](#cosmos.params.v1beta1.ParamChange) + - [ParameterChangeProposal](#cosmos.params.v1beta1.ParameterChangeProposal) + +- [cosmos/params/v1beta1/query.proto](#cosmos/params/v1beta1/query.proto) + - [QueryParamsRequest](#cosmos.params.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.params.v1beta1.QueryParamsResponse) + + - [Query](#cosmos.params.v1beta1.Query) + +- [cosmos/slashing/v1beta1/slashing.proto](#cosmos/slashing/v1beta1/slashing.proto) + - [Params](#cosmos.slashing.v1beta1.Params) + - [ValidatorSigningInfo](#cosmos.slashing.v1beta1.ValidatorSigningInfo) + +- [cosmos/slashing/v1beta1/genesis.proto](#cosmos/slashing/v1beta1/genesis.proto) + - [GenesisState](#cosmos.slashing.v1beta1.GenesisState) + - [MissedBlock](#cosmos.slashing.v1beta1.MissedBlock) + - [SigningInfo](#cosmos.slashing.v1beta1.SigningInfo) + - [ValidatorMissedBlocks](#cosmos.slashing.v1beta1.ValidatorMissedBlocks) + +- [cosmos/slashing/v1beta1/query.proto](#cosmos/slashing/v1beta1/query.proto) + - [QueryParamsRequest](#cosmos.slashing.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.slashing.v1beta1.QueryParamsResponse) + - [QuerySigningInfoRequest](#cosmos.slashing.v1beta1.QuerySigningInfoRequest) + - [QuerySigningInfoResponse](#cosmos.slashing.v1beta1.QuerySigningInfoResponse) + - [QuerySigningInfosRequest](#cosmos.slashing.v1beta1.QuerySigningInfosRequest) + - [QuerySigningInfosResponse](#cosmos.slashing.v1beta1.QuerySigningInfosResponse) + + - [Query](#cosmos.slashing.v1beta1.Query) + +- [cosmos/slashing/v1beta1/tx.proto](#cosmos/slashing/v1beta1/tx.proto) + - [MsgUnjail](#cosmos.slashing.v1beta1.MsgUnjail) + - [MsgUnjailResponse](#cosmos.slashing.v1beta1.MsgUnjailResponse) + + - [Msg](#cosmos.slashing.v1beta1.Msg) + +- [cosmos/staking/v1beta1/staking.proto](#cosmos/staking/v1beta1/staking.proto) + - [Commission](#cosmos.staking.v1beta1.Commission) + - [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) + - [DVPair](#cosmos.staking.v1beta1.DVPair) + - [DVPairs](#cosmos.staking.v1beta1.DVPairs) + - [DVVTriplet](#cosmos.staking.v1beta1.DVVTriplet) + - [DVVTriplets](#cosmos.staking.v1beta1.DVVTriplets) + - [Delegation](#cosmos.staking.v1beta1.Delegation) + - [DelegationResponse](#cosmos.staking.v1beta1.DelegationResponse) + - [Description](#cosmos.staking.v1beta1.Description) + - [HistoricalInfo](#cosmos.staking.v1beta1.HistoricalInfo) + - [Params](#cosmos.staking.v1beta1.Params) + - [Pool](#cosmos.staking.v1beta1.Pool) + - [Redelegation](#cosmos.staking.v1beta1.Redelegation) + - [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) + - [RedelegationEntryResponse](#cosmos.staking.v1beta1.RedelegationEntryResponse) + - [RedelegationResponse](#cosmos.staking.v1beta1.RedelegationResponse) + - [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) + - [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) + - [ValAddresses](#cosmos.staking.v1beta1.ValAddresses) + - [Validator](#cosmos.staking.v1beta1.Validator) + + - [BondStatus](#cosmos.staking.v1beta1.BondStatus) + +- [cosmos/staking/v1beta1/genesis.proto](#cosmos/staking/v1beta1/genesis.proto) + - [GenesisState](#cosmos.staking.v1beta1.GenesisState) + - [LastValidatorPower](#cosmos.staking.v1beta1.LastValidatorPower) + +- [cosmos/staking/v1beta1/query.proto](#cosmos/staking/v1beta1/query.proto) + - [QueryDelegationRequest](#cosmos.staking.v1beta1.QueryDelegationRequest) + - [QueryDelegationResponse](#cosmos.staking.v1beta1.QueryDelegationResponse) + - [QueryDelegatorDelegationsRequest](#cosmos.staking.v1beta1.QueryDelegatorDelegationsRequest) + - [QueryDelegatorDelegationsResponse](#cosmos.staking.v1beta1.QueryDelegatorDelegationsResponse) + - [QueryDelegatorUnbondingDelegationsRequest](#cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsRequest) + - [QueryDelegatorUnbondingDelegationsResponse](#cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsResponse) + - [QueryDelegatorValidatorRequest](#cosmos.staking.v1beta1.QueryDelegatorValidatorRequest) + - [QueryDelegatorValidatorResponse](#cosmos.staking.v1beta1.QueryDelegatorValidatorResponse) + - [QueryDelegatorValidatorsRequest](#cosmos.staking.v1beta1.QueryDelegatorValidatorsRequest) + - [QueryDelegatorValidatorsResponse](#cosmos.staking.v1beta1.QueryDelegatorValidatorsResponse) + - [QueryHistoricalInfoRequest](#cosmos.staking.v1beta1.QueryHistoricalInfoRequest) + - [QueryHistoricalInfoResponse](#cosmos.staking.v1beta1.QueryHistoricalInfoResponse) + - [QueryParamsRequest](#cosmos.staking.v1beta1.QueryParamsRequest) + - [QueryParamsResponse](#cosmos.staking.v1beta1.QueryParamsResponse) + - [QueryPoolRequest](#cosmos.staking.v1beta1.QueryPoolRequest) + - [QueryPoolResponse](#cosmos.staking.v1beta1.QueryPoolResponse) + - [QueryRedelegationsRequest](#cosmos.staking.v1beta1.QueryRedelegationsRequest) + - [QueryRedelegationsResponse](#cosmos.staking.v1beta1.QueryRedelegationsResponse) + - [QueryUnbondingDelegationRequest](#cosmos.staking.v1beta1.QueryUnbondingDelegationRequest) + - [QueryUnbondingDelegationResponse](#cosmos.staking.v1beta1.QueryUnbondingDelegationResponse) + - [QueryValidatorDelegationsRequest](#cosmos.staking.v1beta1.QueryValidatorDelegationsRequest) + - [QueryValidatorDelegationsResponse](#cosmos.staking.v1beta1.QueryValidatorDelegationsResponse) + - [QueryValidatorRequest](#cosmos.staking.v1beta1.QueryValidatorRequest) + - [QueryValidatorResponse](#cosmos.staking.v1beta1.QueryValidatorResponse) + - [QueryValidatorUnbondingDelegationsRequest](#cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsRequest) + - [QueryValidatorUnbondingDelegationsResponse](#cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsResponse) + - [QueryValidatorsRequest](#cosmos.staking.v1beta1.QueryValidatorsRequest) + - [QueryValidatorsResponse](#cosmos.staking.v1beta1.QueryValidatorsResponse) + + - [Query](#cosmos.staking.v1beta1.Query) + +- [cosmos/staking/v1beta1/tx.proto](#cosmos/staking/v1beta1/tx.proto) + - [MsgBeginRedelegate](#cosmos.staking.v1beta1.MsgBeginRedelegate) + - [MsgBeginRedelegateResponse](#cosmos.staking.v1beta1.MsgBeginRedelegateResponse) + - [MsgCreateValidator](#cosmos.staking.v1beta1.MsgCreateValidator) + - [MsgCreateValidatorResponse](#cosmos.staking.v1beta1.MsgCreateValidatorResponse) + - [MsgDelegate](#cosmos.staking.v1beta1.MsgDelegate) + - [MsgDelegateResponse](#cosmos.staking.v1beta1.MsgDelegateResponse) + - [MsgEditValidator](#cosmos.staking.v1beta1.MsgEditValidator) + - [MsgEditValidatorResponse](#cosmos.staking.v1beta1.MsgEditValidatorResponse) + - [MsgUndelegate](#cosmos.staking.v1beta1.MsgUndelegate) + - [MsgUndelegateResponse](#cosmos.staking.v1beta1.MsgUndelegateResponse) + + - [Msg](#cosmos.staking.v1beta1.Msg) + +- [cosmos/tx/signing/v1beta1/signing.proto](#cosmos/tx/signing/v1beta1/signing.proto) + - [SignatureDescriptor](#cosmos.tx.signing.v1beta1.SignatureDescriptor) + - [SignatureDescriptor.Data](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data) + - [SignatureDescriptor.Data.Multi](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Multi) + - [SignatureDescriptor.Data.Single](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Single) + - [SignatureDescriptors](#cosmos.tx.signing.v1beta1.SignatureDescriptors) + + - [SignMode](#cosmos.tx.signing.v1beta1.SignMode) + +- [cosmos/tx/v1beta1/tx.proto](#cosmos/tx/v1beta1/tx.proto) + - [AuthInfo](#cosmos.tx.v1beta1.AuthInfo) + - [Fee](#cosmos.tx.v1beta1.Fee) + - [ModeInfo](#cosmos.tx.v1beta1.ModeInfo) + - [ModeInfo.Multi](#cosmos.tx.v1beta1.ModeInfo.Multi) + - [ModeInfo.Single](#cosmos.tx.v1beta1.ModeInfo.Single) + - [SignDoc](#cosmos.tx.v1beta1.SignDoc) + - [SignerInfo](#cosmos.tx.v1beta1.SignerInfo) + - [Tx](#cosmos.tx.v1beta1.Tx) + - [TxBody](#cosmos.tx.v1beta1.TxBody) + - [TxRaw](#cosmos.tx.v1beta1.TxRaw) + +- [cosmos/tx/v1beta1/service.proto](#cosmos/tx/v1beta1/service.proto) + - [BroadcastTxRequest](#cosmos.tx.v1beta1.BroadcastTxRequest) + - [BroadcastTxResponse](#cosmos.tx.v1beta1.BroadcastTxResponse) + - [GetTxRequest](#cosmos.tx.v1beta1.GetTxRequest) + - [GetTxResponse](#cosmos.tx.v1beta1.GetTxResponse) + - [GetTxsEventRequest](#cosmos.tx.v1beta1.GetTxsEventRequest) + - [GetTxsEventResponse](#cosmos.tx.v1beta1.GetTxsEventResponse) + - [SimulateRequest](#cosmos.tx.v1beta1.SimulateRequest) + - [SimulateResponse](#cosmos.tx.v1beta1.SimulateResponse) + + - [BroadcastMode](#cosmos.tx.v1beta1.BroadcastMode) + - [OrderBy](#cosmos.tx.v1beta1.OrderBy) + + - [Service](#cosmos.tx.v1beta1.Service) + +- [cosmos/upgrade/v1beta1/upgrade.proto](#cosmos/upgrade/v1beta1/upgrade.proto) + - [CancelSoftwareUpgradeProposal](#cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal) + - [Plan](#cosmos.upgrade.v1beta1.Plan) + - [SoftwareUpgradeProposal](#cosmos.upgrade.v1beta1.SoftwareUpgradeProposal) + +- [cosmos/upgrade/v1beta1/query.proto](#cosmos/upgrade/v1beta1/query.proto) + - [QueryAppliedPlanRequest](#cosmos.upgrade.v1beta1.QueryAppliedPlanRequest) + - [QueryAppliedPlanResponse](#cosmos.upgrade.v1beta1.QueryAppliedPlanResponse) + - [QueryCurrentPlanRequest](#cosmos.upgrade.v1beta1.QueryCurrentPlanRequest) + - [QueryCurrentPlanResponse](#cosmos.upgrade.v1beta1.QueryCurrentPlanResponse) + - [QueryUpgradedConsensusStateRequest](#cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateRequest) + - [QueryUpgradedConsensusStateResponse](#cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateResponse) + + - [Query](#cosmos.upgrade.v1beta1.Query) + +- [cosmos/vesting/v1beta1/tx.proto](#cosmos/vesting/v1beta1/tx.proto) + - [MsgCreateVestingAccount](#cosmos.vesting.v1beta1.MsgCreateVestingAccount) + - [MsgCreateVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse) + + - [Msg](#cosmos.vesting.v1beta1.Msg) + +- [cosmos/vesting/v1beta1/vesting.proto](#cosmos/vesting/v1beta1/vesting.proto) + - [BaseVestingAccount](#cosmos.vesting.v1beta1.BaseVestingAccount) + - [ContinuousVestingAccount](#cosmos.vesting.v1beta1.ContinuousVestingAccount) + - [DelayedVestingAccount](#cosmos.vesting.v1beta1.DelayedVestingAccount) + - [Period](#cosmos.vesting.v1beta1.Period) + - [PeriodicVestingAccount](#cosmos.vesting.v1beta1.PeriodicVestingAccount) + +- [ibc/applications/transfer/v1/transfer.proto](#ibc/applications/transfer/v1/transfer.proto) + - [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) + - [FungibleTokenPacketData](#ibc.applications.transfer.v1.FungibleTokenPacketData) + - [Params](#ibc.applications.transfer.v1.Params) + +- [ibc/applications/transfer/v1/genesis.proto](#ibc/applications/transfer/v1/genesis.proto) + - [GenesisState](#ibc.applications.transfer.v1.GenesisState) + +- [ibc/applications/transfer/v1/query.proto](#ibc/applications/transfer/v1/query.proto) + - [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) + - [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) + - [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) + - [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) + - [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) + - [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) + + - [Query](#ibc.applications.transfer.v1.Query) + +- [ibc/core/client/v1/client.proto](#ibc/core/client/v1/client.proto) + - [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) + - [ClientUpdateProposal](#ibc.core.client.v1.ClientUpdateProposal) + - [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) + - [Height](#ibc.core.client.v1.Height) + - [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) + - [Params](#ibc.core.client.v1.Params) + +- [ibc/applications/transfer/v1/tx.proto](#ibc/applications/transfer/v1/tx.proto) + - [MsgTransfer](#ibc.applications.transfer.v1.MsgTransfer) + - [MsgTransferResponse](#ibc.applications.transfer.v1.MsgTransferResponse) + + - [Msg](#ibc.applications.transfer.v1.Msg) + +- [ibc/core/channel/v1/channel.proto](#ibc/core/channel/v1/channel.proto) + - [Acknowledgement](#ibc.core.channel.v1.Acknowledgement) + - [Channel](#ibc.core.channel.v1.Channel) + - [Counterparty](#ibc.core.channel.v1.Counterparty) + - [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) + - [Packet](#ibc.core.channel.v1.Packet) + - [PacketState](#ibc.core.channel.v1.PacketState) + + - [Order](#ibc.core.channel.v1.Order) + - [State](#ibc.core.channel.v1.State) + +- [ibc/core/channel/v1/genesis.proto](#ibc/core/channel/v1/genesis.proto) + - [GenesisState](#ibc.core.channel.v1.GenesisState) + - [PacketSequence](#ibc.core.channel.v1.PacketSequence) + +- [ibc/core/channel/v1/query.proto](#ibc/core/channel/v1/query.proto) + - [QueryChannelClientStateRequest](#ibc.core.channel.v1.QueryChannelClientStateRequest) + - [QueryChannelClientStateResponse](#ibc.core.channel.v1.QueryChannelClientStateResponse) + - [QueryChannelConsensusStateRequest](#ibc.core.channel.v1.QueryChannelConsensusStateRequest) + - [QueryChannelConsensusStateResponse](#ibc.core.channel.v1.QueryChannelConsensusStateResponse) + - [QueryChannelRequest](#ibc.core.channel.v1.QueryChannelRequest) + - [QueryChannelResponse](#ibc.core.channel.v1.QueryChannelResponse) + - [QueryChannelsRequest](#ibc.core.channel.v1.QueryChannelsRequest) + - [QueryChannelsResponse](#ibc.core.channel.v1.QueryChannelsResponse) + - [QueryConnectionChannelsRequest](#ibc.core.channel.v1.QueryConnectionChannelsRequest) + - [QueryConnectionChannelsResponse](#ibc.core.channel.v1.QueryConnectionChannelsResponse) + - [QueryNextSequenceReceiveRequest](#ibc.core.channel.v1.QueryNextSequenceReceiveRequest) + - [QueryNextSequenceReceiveResponse](#ibc.core.channel.v1.QueryNextSequenceReceiveResponse) + - [QueryPacketAcknowledgementRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementRequest) + - [QueryPacketAcknowledgementResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementResponse) + - [QueryPacketAcknowledgementsRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementsRequest) + - [QueryPacketAcknowledgementsResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementsResponse) + - [QueryPacketCommitmentRequest](#ibc.core.channel.v1.QueryPacketCommitmentRequest) + - [QueryPacketCommitmentResponse](#ibc.core.channel.v1.QueryPacketCommitmentResponse) + - [QueryPacketCommitmentsRequest](#ibc.core.channel.v1.QueryPacketCommitmentsRequest) + - [QueryPacketCommitmentsResponse](#ibc.core.channel.v1.QueryPacketCommitmentsResponse) + - [QueryPacketReceiptRequest](#ibc.core.channel.v1.QueryPacketReceiptRequest) + - [QueryPacketReceiptResponse](#ibc.core.channel.v1.QueryPacketReceiptResponse) + - [QueryUnreceivedAcksRequest](#ibc.core.channel.v1.QueryUnreceivedAcksRequest) + - [QueryUnreceivedAcksResponse](#ibc.core.channel.v1.QueryUnreceivedAcksResponse) + - [QueryUnreceivedPacketsRequest](#ibc.core.channel.v1.QueryUnreceivedPacketsRequest) + - [QueryUnreceivedPacketsResponse](#ibc.core.channel.v1.QueryUnreceivedPacketsResponse) + + - [Query](#ibc.core.channel.v1.Query) + +- [ibc/core/channel/v1/tx.proto](#ibc/core/channel/v1/tx.proto) + - [MsgAcknowledgement](#ibc.core.channel.v1.MsgAcknowledgement) + - [MsgAcknowledgementResponse](#ibc.core.channel.v1.MsgAcknowledgementResponse) + - [MsgChannelCloseConfirm](#ibc.core.channel.v1.MsgChannelCloseConfirm) + - [MsgChannelCloseConfirmResponse](#ibc.core.channel.v1.MsgChannelCloseConfirmResponse) + - [MsgChannelCloseInit](#ibc.core.channel.v1.MsgChannelCloseInit) + - [MsgChannelCloseInitResponse](#ibc.core.channel.v1.MsgChannelCloseInitResponse) + - [MsgChannelOpenAck](#ibc.core.channel.v1.MsgChannelOpenAck) + - [MsgChannelOpenAckResponse](#ibc.core.channel.v1.MsgChannelOpenAckResponse) + - [MsgChannelOpenConfirm](#ibc.core.channel.v1.MsgChannelOpenConfirm) + - [MsgChannelOpenConfirmResponse](#ibc.core.channel.v1.MsgChannelOpenConfirmResponse) + - [MsgChannelOpenInit](#ibc.core.channel.v1.MsgChannelOpenInit) + - [MsgChannelOpenInitResponse](#ibc.core.channel.v1.MsgChannelOpenInitResponse) + - [MsgChannelOpenTry](#ibc.core.channel.v1.MsgChannelOpenTry) + - [MsgChannelOpenTryResponse](#ibc.core.channel.v1.MsgChannelOpenTryResponse) + - [MsgRecvPacket](#ibc.core.channel.v1.MsgRecvPacket) + - [MsgRecvPacketResponse](#ibc.core.channel.v1.MsgRecvPacketResponse) + - [MsgTimeout](#ibc.core.channel.v1.MsgTimeout) + - [MsgTimeoutOnClose](#ibc.core.channel.v1.MsgTimeoutOnClose) + - [MsgTimeoutOnCloseResponse](#ibc.core.channel.v1.MsgTimeoutOnCloseResponse) + - [MsgTimeoutResponse](#ibc.core.channel.v1.MsgTimeoutResponse) + + - [Msg](#ibc.core.channel.v1.Msg) + +- [ibc/core/client/v1/genesis.proto](#ibc/core/client/v1/genesis.proto) + - [GenesisMetadata](#ibc.core.client.v1.GenesisMetadata) + - [GenesisState](#ibc.core.client.v1.GenesisState) + - [IdentifiedGenesisMetadata](#ibc.core.client.v1.IdentifiedGenesisMetadata) + +- [ibc/core/client/v1/query.proto](#ibc/core/client/v1/query.proto) + - [QueryClientParamsRequest](#ibc.core.client.v1.QueryClientParamsRequest) + - [QueryClientParamsResponse](#ibc.core.client.v1.QueryClientParamsResponse) + - [QueryClientStateRequest](#ibc.core.client.v1.QueryClientStateRequest) + - [QueryClientStateResponse](#ibc.core.client.v1.QueryClientStateResponse) + - [QueryClientStatesRequest](#ibc.core.client.v1.QueryClientStatesRequest) + - [QueryClientStatesResponse](#ibc.core.client.v1.QueryClientStatesResponse) + - [QueryConsensusStateRequest](#ibc.core.client.v1.QueryConsensusStateRequest) + - [QueryConsensusStateResponse](#ibc.core.client.v1.QueryConsensusStateResponse) + - [QueryConsensusStatesRequest](#ibc.core.client.v1.QueryConsensusStatesRequest) + - [QueryConsensusStatesResponse](#ibc.core.client.v1.QueryConsensusStatesResponse) + + - [Query](#ibc.core.client.v1.Query) + +- [ibc/core/client/v1/tx.proto](#ibc/core/client/v1/tx.proto) + - [MsgCreateClient](#ibc.core.client.v1.MsgCreateClient) + - [MsgCreateClientResponse](#ibc.core.client.v1.MsgCreateClientResponse) + - [MsgSubmitMisbehaviour](#ibc.core.client.v1.MsgSubmitMisbehaviour) + - [MsgSubmitMisbehaviourResponse](#ibc.core.client.v1.MsgSubmitMisbehaviourResponse) + - [MsgUpdateClient](#ibc.core.client.v1.MsgUpdateClient) + - [MsgUpdateClientResponse](#ibc.core.client.v1.MsgUpdateClientResponse) + - [MsgUpgradeClient](#ibc.core.client.v1.MsgUpgradeClient) + - [MsgUpgradeClientResponse](#ibc.core.client.v1.MsgUpgradeClientResponse) + + - [Msg](#ibc.core.client.v1.Msg) + +- [ibc/core/commitment/v1/commitment.proto](#ibc/core/commitment/v1/commitment.proto) + - [MerklePath](#ibc.core.commitment.v1.MerklePath) + - [MerklePrefix](#ibc.core.commitment.v1.MerklePrefix) + - [MerkleProof](#ibc.core.commitment.v1.MerkleProof) + - [MerkleRoot](#ibc.core.commitment.v1.MerkleRoot) + +- [ibc/core/connection/v1/connection.proto](#ibc/core/connection/v1/connection.proto) + - [ClientPaths](#ibc.core.connection.v1.ClientPaths) + - [ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) + - [ConnectionPaths](#ibc.core.connection.v1.ConnectionPaths) + - [Counterparty](#ibc.core.connection.v1.Counterparty) + - [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) + - [Version](#ibc.core.connection.v1.Version) + + - [State](#ibc.core.connection.v1.State) + +- [ibc/core/connection/v1/genesis.proto](#ibc/core/connection/v1/genesis.proto) + - [GenesisState](#ibc.core.connection.v1.GenesisState) + +- [ibc/core/connection/v1/query.proto](#ibc/core/connection/v1/query.proto) + - [QueryClientConnectionsRequest](#ibc.core.connection.v1.QueryClientConnectionsRequest) + - [QueryClientConnectionsResponse](#ibc.core.connection.v1.QueryClientConnectionsResponse) + - [QueryConnectionClientStateRequest](#ibc.core.connection.v1.QueryConnectionClientStateRequest) + - [QueryConnectionClientStateResponse](#ibc.core.connection.v1.QueryConnectionClientStateResponse) + - [QueryConnectionConsensusStateRequest](#ibc.core.connection.v1.QueryConnectionConsensusStateRequest) + - [QueryConnectionConsensusStateResponse](#ibc.core.connection.v1.QueryConnectionConsensusStateResponse) + - [QueryConnectionRequest](#ibc.core.connection.v1.QueryConnectionRequest) + - [QueryConnectionResponse](#ibc.core.connection.v1.QueryConnectionResponse) + - [QueryConnectionsRequest](#ibc.core.connection.v1.QueryConnectionsRequest) + - [QueryConnectionsResponse](#ibc.core.connection.v1.QueryConnectionsResponse) + + - [Query](#ibc.core.connection.v1.Query) + +- [ibc/core/connection/v1/tx.proto](#ibc/core/connection/v1/tx.proto) + - [MsgConnectionOpenAck](#ibc.core.connection.v1.MsgConnectionOpenAck) + - [MsgConnectionOpenAckResponse](#ibc.core.connection.v1.MsgConnectionOpenAckResponse) + - [MsgConnectionOpenConfirm](#ibc.core.connection.v1.MsgConnectionOpenConfirm) + - [MsgConnectionOpenConfirmResponse](#ibc.core.connection.v1.MsgConnectionOpenConfirmResponse) + - [MsgConnectionOpenInit](#ibc.core.connection.v1.MsgConnectionOpenInit) + - [MsgConnectionOpenInitResponse](#ibc.core.connection.v1.MsgConnectionOpenInitResponse) + - [MsgConnectionOpenTry](#ibc.core.connection.v1.MsgConnectionOpenTry) + - [MsgConnectionOpenTryResponse](#ibc.core.connection.v1.MsgConnectionOpenTryResponse) + + - [Msg](#ibc.core.connection.v1.Msg) + +- [ibc/core/types/v1/genesis.proto](#ibc/core/types/v1/genesis.proto) + - [GenesisState](#ibc.core.types.v1.GenesisState) + +- [ibc/lightclients/localhost/v1/localhost.proto](#ibc/lightclients/localhost/v1/localhost.proto) + - [ClientState](#ibc.lightclients.localhost.v1.ClientState) + +- [ibc/lightclients/solomachine/v1/solomachine.proto](#ibc/lightclients/solomachine/v1/solomachine.proto) + - [ChannelStateData](#ibc.lightclients.solomachine.v1.ChannelStateData) + - [ClientState](#ibc.lightclients.solomachine.v1.ClientState) + - [ClientStateData](#ibc.lightclients.solomachine.v1.ClientStateData) + - [ConnectionStateData](#ibc.lightclients.solomachine.v1.ConnectionStateData) + - [ConsensusState](#ibc.lightclients.solomachine.v1.ConsensusState) + - [ConsensusStateData](#ibc.lightclients.solomachine.v1.ConsensusStateData) + - [Header](#ibc.lightclients.solomachine.v1.Header) + - [HeaderData](#ibc.lightclients.solomachine.v1.HeaderData) + - [Misbehaviour](#ibc.lightclients.solomachine.v1.Misbehaviour) + - [NextSequenceRecvData](#ibc.lightclients.solomachine.v1.NextSequenceRecvData) + - [PacketAcknowledgementData](#ibc.lightclients.solomachine.v1.PacketAcknowledgementData) + - [PacketCommitmentData](#ibc.lightclients.solomachine.v1.PacketCommitmentData) + - [PacketReceiptAbsenceData](#ibc.lightclients.solomachine.v1.PacketReceiptAbsenceData) + - [SignBytes](#ibc.lightclients.solomachine.v1.SignBytes) + - [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) + - [TimestampedSignatureData](#ibc.lightclients.solomachine.v1.TimestampedSignatureData) + + - [DataType](#ibc.lightclients.solomachine.v1.DataType) + +- [ibc/lightclients/tendermint/v1/tendermint.proto](#ibc/lightclients/tendermint/v1/tendermint.proto) + - [ClientState](#ibc.lightclients.tendermint.v1.ClientState) + - [ConsensusState](#ibc.lightclients.tendermint.v1.ConsensusState) + - [Fraction](#ibc.lightclients.tendermint.v1.Fraction) + - [Header](#ibc.lightclients.tendermint.v1.Header) + - [Misbehaviour](#ibc.lightclients.tendermint.v1.Misbehaviour) + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## cosmos/auth/v1beta1/auth.proto + + + + + +### BaseAccount +BaseAccount defines a base account type. It contains all the necessary fields +for basic account functionality. Any custom account type should extend this +type for additional functionality (e.g. vesting). + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | | +| `pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `account_number` | [uint64](#uint64) | | | +| `sequence` | [uint64](#uint64) | | | + + + + + + + + +### ModuleAccount +ModuleAccount defines an account for modules that holds coins on a pool. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_account` | [BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | | +| `name` | [string](#string) | | | +| `permissions` | [string](#string) | repeated | | + + + + + + + + +### Params +Params defines the parameters for the auth module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `max_memo_characters` | [uint64](#uint64) | | | +| `tx_sig_limit` | [uint64](#uint64) | | | +| `tx_size_cost_per_byte` | [uint64](#uint64) | | | +| `sig_verify_cost_ed25519` | [uint64](#uint64) | | | +| `sig_verify_cost_secp256k1` | [uint64](#uint64) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/auth/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the auth module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.auth.v1beta1.Params) | | params defines all the paramaters of the module. | +| `accounts` | [google.protobuf.Any](#google.protobuf.Any) | repeated | accounts are the accounts present at genesis. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/auth/v1beta1/query.proto + + + + + +### QueryAccountRequest +QueryAccountRequest is the request type for the Query/Account RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address defines the address to query for. | + + + + + + + + +### QueryAccountResponse +QueryAccountResponse is the response type for the Query/Account RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `account` | [google.protobuf.Any](#google.protobuf.Any) | | account defines the account of the corresponding address. | + + + + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.auth.v1beta1.Params) | | params defines the parameters of the module. | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Account` | [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) | [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) | Account returns account details based on address. | GET|/cosmos/auth/v1beta1/accounts/{address}| +| `Params` | [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) | Params queries all parameters. | GET|/cosmos/auth/v1beta1/params| + + + + + + +

Top

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

Top

+ +## cosmos/bank/v1beta1/bank.proto + + + + + +### DenomUnit +DenomUnit represents a struct that describes a given +denomination unit of the basic token. + + +| 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 | + + + + + + + + +### Input +Input models transaction input. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | | +| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### Metadata +Metadata represents a struct that describes +a basic token. + + +| 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. | + + + + + + + + +### Output +Output models transaction outputs. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | | +| `coins` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### Params +Params defines the parameters for the bank module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `send_enabled` | [SendEnabled](#cosmos.bank.v1beta1.SendEnabled) | repeated | | +| `default_send_enabled` | [bool](#bool) | | | + + + + + + + + +### SendEnabled +SendEnabled maps coin denom to a send_enabled status (whether a denom is +sendable). + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | | +| `enabled` | [bool](#bool) | | | + + + + + + + + +### Supply +Supply represents a struct that passively keeps track of the total supply +amounts in the network. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `total` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/bank/v1beta1/genesis.proto + + + + + +### Balance +Balance defines an account address and balance pair used in the bank module's +genesis state. + + +| 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. | + + + + + + + + +### GenesisState +GenesisState defines the bank module's genesis state. + + +| 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. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/query/v1beta1/pagination.proto + + + + + +### PageRequest +PageRequest is to be embedded in gRPC request messages for efficient +pagination. Ex: + + message SomeRequest { + Foo some_parameter = 1; + PageRequest pagination = 2; + } + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | key is a value returned in PageResponse.next_key to begin querying the next page most efficiently. Only one of offset or key should be set. | +| `offset` | [uint64](#uint64) | | offset is a numeric offset that can be used when key is unavailable. It is less efficient than using key. Only one of offset or key should be set. | +| `limit` | [uint64](#uint64) | | limit is the total number of results to be returned in the result page. If left empty it will default to a value to be set by each app. | +| `count_total` | [bool](#bool) | | count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. count_total is only respected when offset is used. It is ignored when key is set. | + + + + + + + + +### PageResponse +PageResponse is to be embedded in gRPC response messages where the +corresponding request message has used PageRequest. + + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `next_key` | [bytes](#bytes) | | next_key is the key to be passed to PageRequest.key to query the next page most efficiently | +| `total` | [uint64](#uint64) | | total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/bank/v1beta1/query.proto + + + + + +### QueryAllBalancesRequest +QueryBalanceRequest is the request type for the Query/AllBalances RPC method. + + +| 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. | + + + + + + + + +### QueryAllBalancesResponse +QueryAllBalancesResponse is the response type for the Query/AllBalances RPC +method. + + +| 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. | + + + + + + + + +### 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. | + + + + + + + + +### QueryBalanceResponse +QueryBalanceResponse is the response type for the Query/Balance RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `balance` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | balance is the balance of the coin. | + + + + + + + + +### QueryDenomMetadataRequest +QueryDenomMetadataRequest is the request type for the Query/DenomMetadata RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | denom is the coin denom to query the metadata for. | + + + + + + + + +### QueryDenomMetadataResponse +QueryDenomMetadataResponse is the response type for the Query/DenomMetadata RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `metadata` | [Metadata](#cosmos.bank.v1beta1.Metadata) | | metadata describes and provides all the client information for the requested token. | + + + + + + + + +### QueryDenomsMetadataRequest +QueryDenomsMetadataRequest is the request type for the Query/DenomsMetadata 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. | + + + + + + + + +### QueryDenomsMetadataResponse +QueryDenomsMetadataResponse is the response type for the Query/DenomsMetadata RPC +method. + + +| 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. | + + + + + + + + +### QueryParamsRequest +QueryParamsRequest defines the request type for querying x/bank parameters. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse defines the response type for querying x/bank parameters. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### QuerySupplyOfResponse +QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the supply of the coin. | + + + + + + + + +### QueryTotalSupplyRequest +QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC +method. + + + + + + + + +### QueryTotalSupplyResponse +QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `supply` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | supply is the supply of the coins | + + + + + + + + + + + + + + +### 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/bank/v1beta1/tx.proto + + + + + +### MsgMultiSend +MsgMultiSend represents an arbitrary multi-in, multi-out send message. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `inputs` | [Input](#cosmos.bank.v1beta1.Input) | repeated | | +| `outputs` | [Output](#cosmos.bank.v1beta1.Output) | repeated | | + + + + + + + + +### MsgMultiSendResponse +MsgMultiSendResponse defines the Msg/MultiSend response type. + + + + + + + + +### MsgSend +MsgSend represents a message to send coins from one account to another. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. + + + + + + + + + + + + + + +### Msg +Msg defines the bank Msg 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. | | + + + + + + +

Top

+ +## cosmos/base/abci/v1beta1/abci.proto + + + + + +### ABCIMessageLog +ABCIMessageLog defines a structure containing an indexed tx ABCI message log. + + +| 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. | + + + + + + + + +### Attribute +Attribute defines an attribute wrapper where the key and value are +strings instead of raw bytes. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [string](#string) | | | +| `value` | [string](#string) | | | + + + + + + + + +### GasInfo +GasInfo defines tx execution gas context. + + +| 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. | + + + + + + + + +### MsgData +MsgData defines the data returned in a Result object during message +execution. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### SearchTxsResult +SearchTxsResult defines a structure for querying txs pageable + + +| 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 | + + + + + + + + +### SimulationResponse +SimulationResponse defines the response generated when a transaction is +successfully simulated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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 | | + + + + + + + + +### TxMsgData +TxMsgData defines a list of MsgData. A transaction will have a MsgData object +for each message. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data` | [MsgData](#cosmos.base.abci.v1beta1.MsgData) | repeated | | + + + + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/kv/v1beta1/kv.proto + + + + + +### Pair +Pair defines a key/value bytes tuple. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | +| `value` | [bytes](#bytes) | | | + + + + + + + + +### Pairs +Pairs defines a repeated slice of Pair objects. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pairs` | [Pair](#cosmos.base.kv.v1beta1.Pair) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/reflection/v1beta1/reflection.proto + + + + + +### ListAllInterfacesRequest +ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. + + + + + + + + +### ListAllInterfacesResponse +ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `interface_names` | [string](#string) | repeated | interface_names is an array of all the registered interfaces. | + + + + + + + + +### 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. | + + + + + + + + +### ListImplementationsResponse +ListImplementationsResponse is the response type of the ListImplementations +RPC. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `implementation_message_names` | [string](#string) | repeated | | + + + + + + + + + + + + + + +### 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| + + + + + + +

Top

+ +## 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 | + + + + + + + + +### Snapshot +Snapshot contains Tendermint state sync snapshot info. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [uint64](#uint64) | | | +| `format` | [uint32](#uint32) | | | +| `chunks` | [uint32](#uint32) | | | +| `hash` | [bytes](#bytes) | | | +| `metadata` | [Metadata](#cosmos.base.snapshots.v1beta1.Metadata) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/store/v1beta1/commit_info.proto + + + + + +### CommitID +CommitID defines the committment information when a specific store is +committed. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version` | [int64](#int64) | | | +| `hash` | [bytes](#bytes) | | | + + + + + + + + +### 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 | | + + + + + + + + +### 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) | | | +| `commit_id` | [CommitID](#cosmos.base.store.v1beta1.CommitID) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/store/v1beta1/snapshot.proto + + + + + +### SnapshotIAVLItem +SnapshotIAVLItem is an exported IAVL node. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | +| `value` | [bytes](#bytes) | | | +| `version` | [int64](#int64) | | | +| `height` | [int32](#int32) | | | + + + + + + + + +### 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) | | | + + + + + + + + +### SnapshotStoreItem +SnapshotStoreItem contains metadata about a snapshotted store. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `name` | [string](#string) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/base/tendermint/v1beta1/query.proto + + + + + +### GetBlockByHeightRequest +GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [int64](#int64) | | | + + + + + + + + +### GetBlockByHeightResponse +GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. + + + + + + + + +### 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) | | | + + + + + + + + +### 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. | + + + + + + + + +### 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. | + + + + + + + + +### GetNodeInfoRequest +GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. + + + + + + + + +### GetNodeInfoResponse +GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `default_node_info` | [tendermint.p2p.DefaultNodeInfo](#tendermint.p2p.DefaultNodeInfo) | | | +| `application_version` | [VersionInfo](#cosmos.base.tendermint.v1beta1.VersionInfo) | | | + + + + + + + + +### 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) | | | + + + + + + + + +### 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. | + + + + + + + + +### 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. | + + + + + + + + +### Module +Module is the type for VersionInfo + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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) | | | + + + + + + + + +### VersionInfo +VersionInfo is the type for the GetNodeInfoResponse message. + + +| 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 | | + + + + + + + + + + + + + + +### 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/capability/v1beta1/capability.proto + + + + + +### Capability +Capability defines an implementation of an object capability. The index +provided to a Capability must be globally unique. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `index` | [uint64](#uint64) | | | + + + + + + + + +### CapabilityOwners +CapabilityOwners defines a set of owners of a single Capability. The set of +owners must be unique. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/capability/v1beta1/genesis.proto + + + + + +### GenesisOwners +GenesisOwners defines the capability owners with their corresponding index. + + +| 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. | + + + + + + + + +### GenesisState +GenesisState defines the capability module's genesis state. + + +| 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. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/crisis/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the crisis module's genesis state. + + +| 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. | + + + + + + + + + + + + + + + + +

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 +PrivKey defines a ed25519 private key. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | | + + + + + + + + +### 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. + + +| 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) | | | + + + + + + + + +### 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. | + + + + + + + + +### ValidatorHistoricalRewardsRecord +ValidatorHistoricalRewardsRecord is used for import / export via genesis +json. + + +| 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. | + + + + + + + + +### ValidatorOutstandingRewardsRecord +ValidatorOutstandingRewardsRecord is used for import/export via genesis json. + + +| 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. | + + + + + + + + +### ValidatorSlashEventRecord +ValidatorSlashEventRecord is used for import / export via genesis json. + + +| 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. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/distribution/v1beta1/query.proto + + + + + +### QueryCommunityPoolRequest +QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC +method. + + + + + + + + +### QueryCommunityPoolResponse +QueryCommunityPoolResponse is the response type for the Query/CommunityPool +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pool` | [cosmos.base.v1beta1.DecCoin](#cosmos.base.v1beta1.DecCoin) | repeated | pool defines community pool's coins. | + + + + + + + + +### 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. | + + + + + + + + +### 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 | rewards defines the rewards accrued by a delegation. | + + + + + + + + +### 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. | + + + + + + + + +### QueryDelegationTotalRewardsResponse +QueryDelegationTotalRewardsResponse is the response type for the +Query/DelegationTotalRewards RPC method. + + +| 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. | + + + + + + + + +### 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. | + + + + + + + + +### QueryDelegatorValidatorsResponse +QueryDelegatorValidatorsResponse is the response type for the +Query/DelegatorValidators RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### QueryDelegatorWithdrawAddressResponse +QueryDelegatorWithdrawAddressResponse is the response type for the +Query/DelegatorWithdrawAddress RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `withdraw_address` | [string](#string) | | withdraw_address defines the delegator address to query for. | + + + + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.distribution.v1beta1.Params) | | params defines the parameters of the module. | + + + + + + + + +### QueryValidatorCommissionRequest +QueryValidatorCommissionRequest is the request type for the +Query/ValidatorCommission RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | + + + + + + + + +### 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. | + + + + + + + + +### QueryValidatorOutstandingRewardsRequest +QueryValidatorOutstandingRewardsRequest is the request type for the +Query/ValidatorOutstandingRewards RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | validator_address defines the validator address to query for. | + + + + + + + + +### QueryValidatorOutstandingRewardsResponse +QueryValidatorOutstandingRewardsResponse is the response type for the +Query/ValidatorOutstandingRewards RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `rewards` | [ValidatorOutstandingRewards](#cosmos.distribution.v1beta1.ValidatorOutstandingRewards) | | | + + + + + + + + +### QueryValidatorSlashesRequest +QueryValidatorSlashesRequest is the request type for the +Query/ValidatorSlashes 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. | + + + + + + + + +### QueryValidatorSlashesResponse +QueryValidatorSlashesResponse is the response type for the +Query/ValidatorSlashes 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. | + + + + + + + + + + + + + + +### 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| + + + + + + +

Top

+ +## cosmos/distribution/v1beta1/tx.proto + + + + + +### MsgFundCommunityPool +MsgFundCommunityPool allows an account to directly +fund the community pool. + + +| 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. + + + + + + + + +### MsgSetWithdrawAddress +MsgSetWithdrawAddress sets the withdraw address for +a delegator (or validator self-delegation). + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `withdraw_address` | [string](#string) | | | + + + + + + + + +### MsgSetWithdrawAddressResponse +MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. + + + + + + + + +### MsgWithdrawDelegatorReward +MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +from a single validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | + + + + + + + + +### MsgWithdrawDelegatorRewardResponse +MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. + + + + + + + + +### MsgWithdrawValidatorCommission +MsgWithdrawValidatorCommission withdraws the full commission to the validator +address. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_address` | [string](#string) | | | + + + + + + + + +### MsgWithdrawValidatorCommissionResponse +MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. + + + + + + + + + + + + + + +### 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. | | + + + + + + +

Top

+ +## cosmos/evidence/v1beta1/evidence.proto + + + + + +### 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) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/evidence/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the evidence module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | repeated | evidence defines all the evidence at genesis. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/evidence/v1beta1/query.proto + + + + + +### QueryAllEvidenceRequest +QueryEvidenceRequest is the request type for the Query/AllEvidence 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. | + + + + + + + + +### QueryAllEvidenceResponse +QueryAllEvidenceResponse is the response type for the Query/AllEvidence 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. | + + + + + + + + +### QueryEvidenceRequest +QueryEvidenceRequest is the request type for the Query/Evidence RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `evidence_hash` | [bytes](#bytes) | | evidence_hash defines the hash of the requested evidence. | + + + + + + + + +### QueryEvidenceResponse +QueryEvidenceResponse is the response type for the Query/Evidence RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | evidence returns the requested evidence. | + + + + + + + + + + + + + + +### 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| + + + + + + +

Top

+ +## cosmos/evidence/v1beta1/tx.proto + + + + + +### MsgSubmitEvidence +MsgSubmitEvidence represents a message that supports submitting arbitrary +Evidence of misbehavior such as equivocation or counterfactual signing. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `submitter` | [string](#string) | | | +| `evidence` | [google.protobuf.Any](#google.protobuf.Any) | | | + + + + + + + + +### MsgSubmitEvidenceResponse +MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hash` | [bytes](#bytes) | | hash defines the hash of the evidence. | + + + + + + + + + + + + + + +### 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. | | + + + + + + +

Top

+ +## cosmos/genutil/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the raw genesis transaction in JSON. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gen_txs` | [bytes](#bytes) | repeated | gen_txs defines the genesis transactions. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/gov/v1beta1/gov.proto + + + + + +### Deposit +Deposit defines an amount deposited by an account address to an active +proposal. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `depositor` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### DepositParams +DepositParams defines the params for deposits on governance proposals. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `min_deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | Minimum deposit for a proposal to enter voting period. | +| `max_deposit_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months. | + + + + + + + + +### Proposal +Proposal defines the core field members of a governance proposal. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `content` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `status` | [ProposalStatus](#cosmos.gov.v1beta1.ProposalStatus) | | | +| `final_tally_result` | [TallyResult](#cosmos.gov.v1beta1.TallyResult) | | | +| `submit_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `deposit_end_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `total_deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `voting_start_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `voting_end_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | + + + + + + + + +### TallyParams +TallyParams defines the params for tallying votes on governance proposals. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `quorum` | [bytes](#bytes) | | Minimum percentage of total stake needed to vote for a result to be considered valid. | +| `threshold` | [bytes](#bytes) | | Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. | +| `veto_threshold` | [bytes](#bytes) | | Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Default value: 1/3. | + + + + + + + + +### TallyResult +TallyResult defines a standard tally for a governance proposal. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `yes` | [string](#string) | | | +| `abstain` | [string](#string) | | | +| `no` | [string](#string) | | | +| `no_with_veto` | [string](#string) | | | + + + + + + + + +### TextProposal +TextProposal defines a standard text proposal whose changes need to be +manually updated in case of approval. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | + + + + + + + + +### Vote +Vote defines a vote on a governance proposal. +A Vote consists of a proposal ID, the voter, and the vote option. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `voter` | [string](#string) | | | +| `option` | [VoteOption](#cosmos.gov.v1beta1.VoteOption) | | | + + + + + + + + +### VotingParams +VotingParams defines the params for voting on governance proposals. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `voting_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | Length of the voting period. | + + + + + + + + + + +### ProposalStatus +ProposalStatus enumerates the valid statuses of a proposal. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| PROPOSAL_STATUS_UNSPECIFIED | 0 | PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. | +| PROPOSAL_STATUS_DEPOSIT_PERIOD | 1 | PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit period. | +| PROPOSAL_STATUS_VOTING_PERIOD | 2 | PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting period. | +| PROPOSAL_STATUS_PASSED | 3 | PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has passed. | +| PROPOSAL_STATUS_REJECTED | 4 | PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has been rejected. | +| PROPOSAL_STATUS_FAILED | 5 | PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has failed. | + + + + + +### VoteOption +VoteOption enumerates the valid vote options for a given governance proposal. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| VOTE_OPTION_UNSPECIFIED | 0 | VOTE_OPTION_UNSPECIFIED defines a no-op vote option. | +| VOTE_OPTION_YES | 1 | VOTE_OPTION_YES defines a yes vote option. | +| VOTE_OPTION_ABSTAIN | 2 | VOTE_OPTION_ABSTAIN defines an abstain vote option. | +| VOTE_OPTION_NO | 3 | VOTE_OPTION_NO defines a no vote option. | +| VOTE_OPTION_NO_WITH_VETO | 4 | VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. | + + + + + + + + + + + +

Top

+ +## cosmos/gov/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the gov module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `starting_proposal_id` | [uint64](#uint64) | | starting_proposal_id is the ID of the starting proposal. | +| `deposits` | [Deposit](#cosmos.gov.v1beta1.Deposit) | repeated | deposits defines all the deposits present at genesis. | +| `votes` | [Vote](#cosmos.gov.v1beta1.Vote) | repeated | votes defines all the votes present at genesis. | +| `proposals` | [Proposal](#cosmos.gov.v1beta1.Proposal) | repeated | proposals defines all the proposals present at genesis. | +| `deposit_params` | [DepositParams](#cosmos.gov.v1beta1.DepositParams) | | params defines all the paramaters of related to deposit. | +| `voting_params` | [VotingParams](#cosmos.gov.v1beta1.VotingParams) | | params defines all the paramaters of related to voting. | +| `tally_params` | [TallyParams](#cosmos.gov.v1beta1.TallyParams) | | params defines all the paramaters of related to tally. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/gov/v1beta1/query.proto + + + + + +### QueryDepositRequest +QueryDepositRequest is the request type for the Query/Deposit RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | +| `depositor` | [string](#string) | | depositor defines the deposit addresses from the proposals. | + + + + + + + + +### QueryDepositResponse +QueryDepositResponse is the response type for the Query/Deposit RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `deposit` | [Deposit](#cosmos.gov.v1beta1.Deposit) | | deposit defines the requested deposit. | + + + + + + + + +### QueryDepositsRequest +QueryDepositsRequest is the request type for the Query/Deposits RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryDepositsResponse +QueryDepositsResponse is the response type for the Query/Deposits RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `deposits` | [Deposit](#cosmos.gov.v1beta1.Deposit) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryParamsRequest +QueryParamsRequest is the request type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params_type` | [string](#string) | | params_type defines which parameters to query for, can be one of "voting", "tallying" or "deposit". | + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is the response type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `voting_params` | [VotingParams](#cosmos.gov.v1beta1.VotingParams) | | voting_params defines the parameters related to voting. | +| `deposit_params` | [DepositParams](#cosmos.gov.v1beta1.DepositParams) | | deposit_params defines the parameters related to deposit. | +| `tally_params` | [TallyParams](#cosmos.gov.v1beta1.TallyParams) | | tally_params defines the parameters related to tally. | + + + + + + + + +### QueryProposalRequest +QueryProposalRequest is the request type for the Query/Proposal RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | + + + + + + + + +### QueryProposalResponse +QueryProposalResponse is the response type for the Query/Proposal RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal` | [Proposal](#cosmos.gov.v1beta1.Proposal) | | | + + + + + + + + +### QueryProposalsRequest +QueryProposalsRequest is the request type for the Query/Proposals RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_status` | [ProposalStatus](#cosmos.gov.v1beta1.ProposalStatus) | | proposal_status defines the status of the proposals. | +| `voter` | [string](#string) | | voter defines the voter address for the proposals. | +| `depositor` | [string](#string) | | depositor defines the deposit addresses from the proposals. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryProposalsResponse +QueryProposalsResponse is the response type for the Query/Proposals RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposals` | [Proposal](#cosmos.gov.v1beta1.Proposal) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryTallyResultRequest +QueryTallyResultRequest is the request type for the Query/Tally RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | + + + + + + + + +### QueryTallyResultResponse +QueryTallyResultResponse is the response type for the Query/Tally RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `tally` | [TallyResult](#cosmos.gov.v1beta1.TallyResult) | | tally defines the requested tally. | + + + + + + + + +### QueryVoteRequest +QueryVoteRequest is the request type for the Query/Vote RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | +| `voter` | [string](#string) | | voter defines the oter address for the proposals. | + + + + + + + + +### QueryVoteResponse +QueryVoteResponse is the response type for the Query/Vote RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `vote` | [Vote](#cosmos.gov.v1beta1.Vote) | | vote defined the queried vote. | + + + + + + + + +### QueryVotesRequest +QueryVotesRequest is the request type for the Query/Votes RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | proposal_id defines the unique id of the proposal. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryVotesResponse +QueryVotesResponse is the response type for the Query/Votes RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `votes` | [Vote](#cosmos.gov.v1beta1.Vote) | repeated | votes defined the queried votes. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service for gov module + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Proposal` | [QueryProposalRequest](#cosmos.gov.v1beta1.QueryProposalRequest) | [QueryProposalResponse](#cosmos.gov.v1beta1.QueryProposalResponse) | Proposal queries proposal details based on ProposalID. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}| +| `Proposals` | [QueryProposalsRequest](#cosmos.gov.v1beta1.QueryProposalsRequest) | [QueryProposalsResponse](#cosmos.gov.v1beta1.QueryProposalsResponse) | Proposals queries all proposals based on given status. | GET|/cosmos/gov/v1beta1/proposals| +| `Vote` | [QueryVoteRequest](#cosmos.gov.v1beta1.QueryVoteRequest) | [QueryVoteResponse](#cosmos.gov.v1beta1.QueryVoteResponse) | Vote queries voted information based on proposalID, voterAddr. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}| +| `Votes` | [QueryVotesRequest](#cosmos.gov.v1beta1.QueryVotesRequest) | [QueryVotesResponse](#cosmos.gov.v1beta1.QueryVotesResponse) | Votes queries votes of a given proposal. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}/votes| +| `Params` | [QueryParamsRequest](#cosmos.gov.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.gov.v1beta1.QueryParamsResponse) | Params queries all parameters of the gov module. | GET|/cosmos/gov/v1beta1/params/{params_type}| +| `Deposit` | [QueryDepositRequest](#cosmos.gov.v1beta1.QueryDepositRequest) | [QueryDepositResponse](#cosmos.gov.v1beta1.QueryDepositResponse) | Deposit queries single deposit information based proposalID, depositAddr. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}| +| `Deposits` | [QueryDepositsRequest](#cosmos.gov.v1beta1.QueryDepositsRequest) | [QueryDepositsResponse](#cosmos.gov.v1beta1.QueryDepositsResponse) | Deposits queries all deposits of a single proposal. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits| +| `TallyResult` | [QueryTallyResultRequest](#cosmos.gov.v1beta1.QueryTallyResultRequest) | [QueryTallyResultResponse](#cosmos.gov.v1beta1.QueryTallyResultResponse) | TallyResult queries the tally of a proposal vote. | GET|/cosmos/gov/v1beta1/proposals/{proposal_id}/tally| + + + + + + +

Top

+ +## cosmos/gov/v1beta1/tx.proto + + + + + +### MsgDeposit +MsgDeposit defines a message to submit a deposit to an existing proposal. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `depositor` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### MsgDepositResponse +MsgDepositResponse defines the Msg/Deposit response type. + + + + + + + + +### MsgSubmitProposal +MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +proposal Content. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `content` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `initial_deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `proposer` | [string](#string) | | | + + + + + + + + +### MsgSubmitProposalResponse +MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | + + + + + + + + +### MsgVote +MsgVote defines a message to cast a vote. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proposal_id` | [uint64](#uint64) | | | +| `voter` | [string](#string) | | | +| `option` | [VoteOption](#cosmos.gov.v1beta1.VoteOption) | | | + + + + + + + + +### MsgVoteResponse +MsgVoteResponse defines the Msg/Vote response type. + + + + + + + + + + + + + + +### Msg +Msg defines the bank Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `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. | | +| `Deposit` | [MsgDeposit](#cosmos.gov.v1beta1.MsgDeposit) | [MsgDepositResponse](#cosmos.gov.v1beta1.MsgDepositResponse) | Deposit defines a method to add deposit on a specific proposal. | | + + + + + + +

Top

+ +## cosmos/mint/v1beta1/mint.proto + + + + + +### Minter +Minter represents the minting state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `inflation` | [string](#string) | | current annual inflation rate | +| `annual_provisions` | [string](#string) | | current annual expected provisions | + + + + + + + + +### Params +Params holds parameters for the mint module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mint_denom` | [string](#string) | | type of coin to mint | +| `inflation_rate` | [string](#string) | | maximum annual change in inflation rate | +| `blocks_per_year` | [uint64](#uint64) | | expected blocks per year | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/mint/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the mint module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `minter` | [Minter](#cosmos.mint.v1beta1.Minter) | | minter is a space for holding current inflation information. | +| `params` | [Params](#cosmos.mint.v1beta1.Params) | | params defines all the paramaters of the module. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/mint/v1beta1/query.proto + + + + + +### QueryAnnualProvisionsRequest +QueryAnnualProvisionsRequest is the request type for the +Query/AnnualProvisions RPC method. + + + + + + + + +### QueryAnnualProvisionsResponse +QueryAnnualProvisionsResponse is the response type for the +Query/AnnualProvisions RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `annual_provisions` | [bytes](#bytes) | | annual_provisions is the current minting annual provisions value. | + + + + + + + + +### QueryInflationRequest +QueryInflationRequest is the request type for the Query/Inflation RPC method. + + + + + + + + +### QueryInflationResponse +QueryInflationResponse is the response type for the Query/Inflation RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `inflation` | [bytes](#bytes) | | inflation is the current minting inflation value. | + + + + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.mint.v1beta1.Params) | | params defines the parameters of the module. | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#cosmos.mint.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.mint.v1beta1.QueryParamsResponse) | Params returns the total set of minting parameters. | GET|/cosmos/mint/v1beta1/params| +| `Inflation` | [QueryInflationRequest](#cosmos.mint.v1beta1.QueryInflationRequest) | [QueryInflationResponse](#cosmos.mint.v1beta1.QueryInflationResponse) | Inflation returns the current minting inflation value. | GET|/cosmos/mint/v1beta1/inflation| +| `AnnualProvisions` | [QueryAnnualProvisionsRequest](#cosmos.mint.v1beta1.QueryAnnualProvisionsRequest) | [QueryAnnualProvisionsResponse](#cosmos.mint.v1beta1.QueryAnnualProvisionsResponse) | AnnualProvisions current minting annual provisions value. | GET|/cosmos/mint/v1beta1/annual_provisions| + + + + + + +

Top

+ +## cosmos/params/v1beta1/params.proto + + + + + +### ParamChange +ParamChange defines an individual parameter change, for use in +ParameterChangeProposal. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `subspace` | [string](#string) | | | +| `key` | [string](#string) | | | +| `value` | [string](#string) | | | + + + + + + + + +### ParameterChangeProposal +ParameterChangeProposal defines a proposal to change one or more parameters. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `changes` | [ParamChange](#cosmos.params.v1beta1.ParamChange) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/params/v1beta1/query.proto + + + + + +### QueryParamsRequest +QueryParamsRequest is request type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `subspace` | [string](#string) | | subspace defines the module to query the parameter for. | +| `key` | [string](#string) | | key defines the key of the parameter in the subspace. | + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is response type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `param` | [ParamChange](#cosmos.params.v1beta1.ParamChange) | | param defines the queried parameter. | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#cosmos.params.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.params.v1beta1.QueryParamsResponse) | Params queries a specific parameter of a module, given its subspace and key. | GET|/cosmos/params/v1beta1/params| + + + + + + +

Top

+ +## cosmos/slashing/v1beta1/slashing.proto + + + + + +### Params +Params represents the parameters used for by the slashing module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signed_blocks_window` | [int64](#int64) | | | +| `min_signed_per_window` | [bytes](#bytes) | | | +| `downtime_jail_duration` | [google.protobuf.Duration](#google.protobuf.Duration) | | | +| `slash_fraction_double_sign` | [bytes](#bytes) | | | +| `slash_fraction_downtime` | [bytes](#bytes) | | | + + + + + + + + +### ValidatorSigningInfo +ValidatorSigningInfo defines a validator's signing info for monitoring their +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) | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/slashing/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the slashing module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.slashing.v1beta1.Params) | | params defines all the paramaters of related to deposit. | +| `signing_infos` | [SigningInfo](#cosmos.slashing.v1beta1.SigningInfo) | repeated | signing_infos represents a map between validator addresses and their signing infos. | +| `missed_blocks` | [ValidatorMissedBlocks](#cosmos.slashing.v1beta1.ValidatorMissedBlocks) | repeated | signing_infos represents a map between validator addresses and their missed blocks. | + + + + + + + + +### MissedBlock +MissedBlock contains height and missed status as boolean. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `index` | [int64](#int64) | | index is the height at which the block was missed. | +| `missed` | [bool](#bool) | | missed is the missed status. | + + + + + + + + +### SigningInfo +SigningInfo stores validator signing info of corresponding address. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the validator address. | +| `validator_signing_info` | [ValidatorSigningInfo](#cosmos.slashing.v1beta1.ValidatorSigningInfo) | | validator_signing_info represents the signing info of this validator. | + + + + + + + + +### ValidatorMissedBlocks +ValidatorMissedBlocks contains array of missed blocks of corresponding +address. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the validator address. | +| `missed_blocks` | [MissedBlock](#cosmos.slashing.v1beta1.MissedBlock) | repeated | missed_blocks is an array of missed blocks by the validator. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/slashing/v1beta1/query.proto + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.slashing.v1beta1.Params) | | | + + + + + + + + +### QuerySigningInfoRequest +QuerySigningInfoRequest is the request type for the Query/SigningInfo RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `cons_address` | [string](#string) | | cons_address is the address to query signing info of | + + + + + + + + +### QuerySigningInfoResponse +QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `val_signing_info` | [ValidatorSigningInfo](#cosmos.slashing.v1beta1.ValidatorSigningInfo) | | val_signing_info is the signing info of requested val cons address | + + + + + + + + +### QuerySigningInfosRequest +QuerySigningInfosRequest is the request type for the Query/SigningInfos RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | | + + + + + + + + +### QuerySigningInfosResponse +QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `info` | [ValidatorSigningInfo](#cosmos.slashing.v1beta1.ValidatorSigningInfo) | repeated | info is the signing info of all validators | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Params` | [QueryParamsRequest](#cosmos.slashing.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.slashing.v1beta1.QueryParamsResponse) | Params queries the parameters of slashing module | GET|/cosmos/slashing/v1beta1/params| +| `SigningInfo` | [QuerySigningInfoRequest](#cosmos.slashing.v1beta1.QuerySigningInfoRequest) | [QuerySigningInfoResponse](#cosmos.slashing.v1beta1.QuerySigningInfoResponse) | SigningInfo queries the signing info of given cons address | GET|/cosmos/slashing/v1beta1/signing_infos/{cons_address}| +| `SigningInfos` | [QuerySigningInfosRequest](#cosmos.slashing.v1beta1.QuerySigningInfosRequest) | [QuerySigningInfosResponse](#cosmos.slashing.v1beta1.QuerySigningInfosResponse) | SigningInfos queries signing info of all validators | GET|/cosmos/slashing/v1beta1/signing_infos| + + + + + + +

Top

+ +## cosmos/slashing/v1beta1/tx.proto + + + + + +### MsgUnjail +MsgUnjail defines the Msg/Unjail request type + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_addr` | [string](#string) | | | + + + + + + + + +### MsgUnjailResponse +MsgUnjailResponse defines the Msg/Unjail response type + + + + + + + + + + + + + + +### Msg +Msg defines the slashing Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Unjail` | [MsgUnjail](#cosmos.slashing.v1beta1.MsgUnjail) | [MsgUnjailResponse](#cosmos.slashing.v1beta1.MsgUnjailResponse) | Unjail defines a method for unjailing a jailed validator, thus returning them into the bonded validator set, so they can begin receiving provisions and rewards again. | | + + + + + + +

Top

+ +## cosmos/staking/v1beta1/staking.proto + + + + + +### Commission +Commission defines commission parameters for a given validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### CommissionRates +CommissionRates defines the initial commission rates to be used for creating +a validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### DVPair +DVPair is struct that just has a delegator-validator pair with no other data. +It is intended to be used as a marshalable pointer. For example, a DVPair can +be used to construct the key to getting an UnbondingDelegation from state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | + + + + + + + + +### DVPairs +DVPairs defines an array of DVPair objects. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pairs` | [DVPair](#cosmos.staking.v1beta1.DVPair) | repeated | | + + + + + + + + +### DVVTriplet +DVVTriplet is struct that just has a delegator-validator-validator triplet +with no other data. It is intended to be used as a marshalable pointer. For +example, a DVVTriplet can be used to construct the key to getting a +Redelegation from state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_src_address` | [string](#string) | | | +| `validator_dst_address` | [string](#string) | | | + + + + + + + + +### DVVTriplets +DVVTriplets defines an array of DVVTriplet objects. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `triplets` | [DVVTriplet](#cosmos.staking.v1beta1.DVVTriplet) | repeated | | + + + + + + + + +### Delegation +Delegation represents the bond with tokens held by an account. It is +owned by one delegator, and is associated with the voting power of one +validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### DelegationResponse +DelegationResponse is equivalent to Delegation except that it contains a +balance in addition to shares which is more suitable for client responses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegation` | [Delegation](#cosmos.staking.v1beta1.Delegation) | | | +| `balance` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### Description +Description defines a validator description. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### HistoricalInfo +HistoricalInfo contains header and validator information for a given block. +It is stored as part of staking module's state, which persists the `n` most +recent HistoricalInfo +(`n` is set by the staking module's `historical_entries` parameter). + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `header` | [tendermint.types.Header](#tendermint.types.Header) | | | +| `valset` | [Validator](#cosmos.staking.v1beta1.Validator) | repeated | | + + + + + + + + +### Params +Params defines the parameters for the staking module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### Pool +Pool is used for tracking bonded and not-bonded token supply of the bond +denomination. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `not_bonded_tokens` | [string](#string) | | | +| `bonded_tokens` | [string](#string) | | | + + + + + + + + +### Redelegation +Redelegation contains the list of a particular delegator's redelegating bonds +from a particular source validator to a particular destination validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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 | + + + + + + + + +### RedelegationEntry +RedelegationEntry defines a redelegation object with relevant metadata. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### RedelegationEntryResponse +RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +contains a balance in addition to shares which is more suitable for client +responses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `redelegation_entry` | [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) | | | +| `balance` | [string](#string) | | | + + + + + + + + +### RedelegationResponse +RedelegationResponse is equivalent to a Redelegation except that its entries +contain a balance in addition to shares which is more suitable for client +responses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `redelegation` | [Redelegation](#cosmos.staking.v1beta1.Redelegation) | | | +| `entries` | [RedelegationEntryResponse](#cosmos.staking.v1beta1.RedelegationEntryResponse) | repeated | | + + + + + + + + +### UnbondingDelegation +UnbondingDelegation stores all of a single delegator's unbonding bonds +for a single validator in an time-ordered list. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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 | + + + + + + + + +### UnbondingDelegationEntry +UnbondingDelegationEntry defines an unbonding object with relevant metadata. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + +### ValAddresses +ValAddresses defines a repeated set of validator addresses. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `addresses` | [string](#string) | repeated | | + + + + + + + + +### Validator +Validator defines a validator, together with the total amount of the +Validator's bond shares and their exchange rate to coins. Slashing results in +a decrease in the exchange rate, allowing correct calculation of future +undelegations without iterating over delegators. When coins are delegated to +this validator, the validator is credited with a delegation whose number of +bond shares is based on the amount of coins delegated divided by the current +exchange rate. Voting power can be calculated as total bonded shares +multiplied by exchange rate. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `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. | + + + + + + + + + + +### BondStatus +BondStatus is the status of a validator. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| BOND_STATUS_UNSPECIFIED | 0 | UNSPECIFIED defines an invalid validator status. | +| BOND_STATUS_UNBONDED | 1 | UNBONDED defines a validator that is not bonded. | +| BOND_STATUS_UNBONDING | 2 | UNBONDING defines a validator that is unbonding. | +| BOND_STATUS_BONDED | 3 | BONDED defines a validator that is bonded. | + + + + + + + + + + + +

Top

+ +## cosmos/staking/v1beta1/genesis.proto + + + + + +### GenesisState +GenesisState defines the staking module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.staking.v1beta1.Params) | | params defines all the paramaters of related to deposit. | +| `last_total_power` | [bytes](#bytes) | | last_total_power tracks the total amounts of bonded tokens recorded during the previous end block. | +| `last_validator_powers` | [LastValidatorPower](#cosmos.staking.v1beta1.LastValidatorPower) | repeated | last_validator_powers is a special index that provides a historical list of the last-block's bonded validators. | +| `validators` | [Validator](#cosmos.staking.v1beta1.Validator) | repeated | delegations defines the validator set at genesis. | +| `delegations` | [Delegation](#cosmos.staking.v1beta1.Delegation) | repeated | delegations defines the delegations active at genesis. | +| `unbonding_delegations` | [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) | repeated | unbonding_delegations defines the unbonding delegations active at genesis. | +| `redelegations` | [Redelegation](#cosmos.staking.v1beta1.Redelegation) | repeated | redelegations defines the redelegations active at genesis. | +| `exported` | [bool](#bool) | | | + + + + + + + + +### LastValidatorPower +LastValidatorPower required for validator set update logic. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `address` | [string](#string) | | address is the address of the validator. | +| `power` | [int64](#int64) | | power defines the power of the validator. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/staking/v1beta1/query.proto + + + + + +### QueryDelegationRequest +QueryDelegationRequest is request type for the Query/Delegation RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `validator_addr` | [string](#string) | | validator_addr defines the validator address to query for. | + + + + + + + + +### QueryDelegationResponse +QueryDelegationResponse is response type for the Query/Delegation RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegation_response` | [DelegationResponse](#cosmos.staking.v1beta1.DelegationResponse) | | delegation_responses defines the delegation info of a delegation. | + + + + + + + + +### QueryDelegatorDelegationsRequest +QueryDelegatorDelegationsRequest is request type for the +Query/DelegatorDelegations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryDelegatorDelegationsResponse +QueryDelegatorDelegationsResponse is response type for the +Query/DelegatorDelegations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegation_responses` | [DelegationResponse](#cosmos.staking.v1beta1.DelegationResponse) | repeated | delegation_responses defines all the delegations' info of a delegator. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryDelegatorUnbondingDelegationsRequest +QueryDelegatorUnbondingDelegationsRequest is request type for the +Query/DelegatorUnbondingDelegations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryDelegatorUnbondingDelegationsResponse +QueryUnbondingDelegatorDelegationsResponse is response type for the +Query/UnbondingDelegatorDelegations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `unbonding_responses` | [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryDelegatorValidatorRequest +QueryDelegatorValidatorRequest is request type for the +Query/DelegatorValidator RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `validator_addr` | [string](#string) | | validator_addr defines the validator address to query for. | + + + + + + + + +### QueryDelegatorValidatorResponse +QueryDelegatorValidatorResponse response type for the +Query/DelegatorValidator RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator` | [Validator](#cosmos.staking.v1beta1.Validator) | | validator defines the the validator info. | + + + + + + + + +### QueryDelegatorValidatorsRequest +QueryDelegatorValidatorsRequest is request type for the +Query/DelegatorValidators RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryDelegatorValidatorsResponse +QueryDelegatorValidatorsResponse is response type for the +Query/DelegatorValidators RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validators` | [Validator](#cosmos.staking.v1beta1.Validator) | repeated | validators defines the the validators' info of a delegator. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryHistoricalInfoRequest +QueryHistoricalInfoRequest is request type for the Query/HistoricalInfo RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [int64](#int64) | | height defines at which height to query the historical info. | + + + + + + + + +### QueryHistoricalInfoResponse +QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hist` | [HistoricalInfo](#cosmos.staking.v1beta1.HistoricalInfo) | | hist defines the historical info at the given height. | + + + + + + + + +### QueryParamsRequest +QueryParamsRequest is request type for the Query/Params RPC method. + + + + + + + + +### QueryParamsResponse +QueryParamsResponse is response type for the Query/Params RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#cosmos.staking.v1beta1.Params) | | params holds all the parameters of this module. | + + + + + + + + +### QueryPoolRequest +QueryPoolRequest is request type for the Query/Pool RPC method. + + + + + + + + +### QueryPoolResponse +QueryPoolResponse is response type for the Query/Pool RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pool` | [Pool](#cosmos.staking.v1beta1.Pool) | | pool defines the pool info. | + + + + + + + + +### QueryRedelegationsRequest +QueryRedelegationsRequest is request type for the Query/Redelegations RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `src_validator_addr` | [string](#string) | | src_validator_addr defines the validator address to redelegate from. | +| `dst_validator_addr` | [string](#string) | | dst_validator_addr defines the validator address to redelegate to. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryRedelegationsResponse +QueryRedelegationsResponse is response type for the Query/Redelegations RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `redelegation_responses` | [RedelegationResponse](#cosmos.staking.v1beta1.RedelegationResponse) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryUnbondingDelegationRequest +QueryUnbondingDelegationRequest is request type for the +Query/UnbondingDelegation RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_addr` | [string](#string) | | delegator_addr defines the delegator address to query for. | +| `validator_addr` | [string](#string) | | validator_addr defines the validator address to query for. | + + + + + + + + +### QueryUnbondingDelegationResponse +QueryDelegationResponse is response type for the Query/UnbondingDelegation +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `unbond` | [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) | | unbond defines the unbonding information of a delegation. | + + + + + + + + +### QueryValidatorDelegationsRequest +QueryValidatorDelegationsRequest is request type for the +Query/ValidatorDelegations RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_addr` | [string](#string) | | validator_addr 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. | + + + + + + + + +### QueryValidatorDelegationsResponse +QueryValidatorDelegationsResponse is response type for the +Query/ValidatorDelegations RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegation_responses` | [DelegationResponse](#cosmos.staking.v1beta1.DelegationResponse) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryValidatorRequest +QueryValidatorRequest is response type for the Query/Validator RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_addr` | [string](#string) | | validator_addr defines the validator address to query for. | + + + + + + + + +### QueryValidatorResponse +QueryValidatorResponse is response type for the Query/Validator RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator` | [Validator](#cosmos.staking.v1beta1.Validator) | | validator defines the the validator info. | + + + + + + + + +### QueryValidatorUnbondingDelegationsRequest +QueryValidatorUnbondingDelegationsRequest is required type for the +Query/ValidatorUnbondingDelegations RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validator_addr` | [string](#string) | | validator_addr 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. | + + + + + + + + +### QueryValidatorUnbondingDelegationsResponse +QueryValidatorUnbondingDelegationsResponse is response type for the +Query/ValidatorUnbondingDelegations RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `unbonding_responses` | [UnbondingDelegation](#cosmos.staking.v1beta1.UnbondingDelegation) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### QueryValidatorsRequest +QueryValidatorsRequest is request type for Query/Validators RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `status` | [string](#string) | | status enables to query for validators matching a given status. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an optional pagination for the request. | + + + + + + + + +### QueryValidatorsResponse +QueryValidatorsResponse is response type for the Query/Validators RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `validators` | [Validator](#cosmos.staking.v1beta1.Validator) | repeated | validators contains all the queried validators. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + + + + + + + +### Query +Query defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Validators` | [QueryValidatorsRequest](#cosmos.staking.v1beta1.QueryValidatorsRequest) | [QueryValidatorsResponse](#cosmos.staking.v1beta1.QueryValidatorsResponse) | Validators queries all validators that match the given status. | GET|/cosmos/staking/v1beta1/validators| +| `Validator` | [QueryValidatorRequest](#cosmos.staking.v1beta1.QueryValidatorRequest) | [QueryValidatorResponse](#cosmos.staking.v1beta1.QueryValidatorResponse) | Validator queries validator info for given validator address. | GET|/cosmos/staking/v1beta1/validators/{validator_addr}| +| `ValidatorDelegations` | [QueryValidatorDelegationsRequest](#cosmos.staking.v1beta1.QueryValidatorDelegationsRequest) | [QueryValidatorDelegationsResponse](#cosmos.staking.v1beta1.QueryValidatorDelegationsResponse) | ValidatorDelegations queries delegate info for given validator. | GET|/cosmos/staking/v1beta1/validators/{validator_addr}/delegations| +| `ValidatorUnbondingDelegations` | [QueryValidatorUnbondingDelegationsRequest](#cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsRequest) | [QueryValidatorUnbondingDelegationsResponse](#cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsResponse) | ValidatorUnbondingDelegations queries unbonding delegations of a validator. | GET|/cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations| +| `Delegation` | [QueryDelegationRequest](#cosmos.staking.v1beta1.QueryDelegationRequest) | [QueryDelegationResponse](#cosmos.staking.v1beta1.QueryDelegationResponse) | Delegation queries delegate info for given validator delegator pair. | GET|/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}| +| `UnbondingDelegation` | [QueryUnbondingDelegationRequest](#cosmos.staking.v1beta1.QueryUnbondingDelegationRequest) | [QueryUnbondingDelegationResponse](#cosmos.staking.v1beta1.QueryUnbondingDelegationResponse) | UnbondingDelegation queries unbonding info for given validator delegator pair. | GET|/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation| +| `DelegatorDelegations` | [QueryDelegatorDelegationsRequest](#cosmos.staking.v1beta1.QueryDelegatorDelegationsRequest) | [QueryDelegatorDelegationsResponse](#cosmos.staking.v1beta1.QueryDelegatorDelegationsResponse) | DelegatorDelegations queries all delegations of a given delegator address. | GET|/cosmos/staking/v1beta1/delegations/{delegator_addr}| +| `DelegatorUnbondingDelegations` | [QueryDelegatorUnbondingDelegationsRequest](#cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsRequest) | [QueryDelegatorUnbondingDelegationsResponse](#cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsResponse) | DelegatorUnbondingDelegations queries all unbonding delegations of a given delegator address. | GET|/cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations| +| `Redelegations` | [QueryRedelegationsRequest](#cosmos.staking.v1beta1.QueryRedelegationsRequest) | [QueryRedelegationsResponse](#cosmos.staking.v1beta1.QueryRedelegationsResponse) | Redelegations queries redelegations of given address. | GET|/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations| +| `DelegatorValidators` | [QueryDelegatorValidatorsRequest](#cosmos.staking.v1beta1.QueryDelegatorValidatorsRequest) | [QueryDelegatorValidatorsResponse](#cosmos.staking.v1beta1.QueryDelegatorValidatorsResponse) | DelegatorValidators queries all validators info for given delegator address. | GET|/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators| +| `DelegatorValidator` | [QueryDelegatorValidatorRequest](#cosmos.staking.v1beta1.QueryDelegatorValidatorRequest) | [QueryDelegatorValidatorResponse](#cosmos.staking.v1beta1.QueryDelegatorValidatorResponse) | DelegatorValidator queries validator info for given delegator validator pair. | GET|/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}| +| `HistoricalInfo` | [QueryHistoricalInfoRequest](#cosmos.staking.v1beta1.QueryHistoricalInfoRequest) | [QueryHistoricalInfoResponse](#cosmos.staking.v1beta1.QueryHistoricalInfoResponse) | HistoricalInfo queries the historical info for given height. | GET|/cosmos/staking/v1beta1/historical_info/{height}| +| `Pool` | [QueryPoolRequest](#cosmos.staking.v1beta1.QueryPoolRequest) | [QueryPoolResponse](#cosmos.staking.v1beta1.QueryPoolResponse) | Pool queries the pool info. | GET|/cosmos/staking/v1beta1/pool| +| `Params` | [QueryParamsRequest](#cosmos.staking.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.staking.v1beta1.QueryParamsResponse) | Parameters queries the staking parameters. | GET|/cosmos/staking/v1beta1/params| + + + + + + +

Top

+ +## cosmos/staking/v1beta1/tx.proto + + + + + +### MsgBeginRedelegate +MsgBeginRedelegate defines a SDK message for performing a redelegation +of coins from a delegator and source validator to a destination validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_src_address` | [string](#string) | | | +| `validator_dst_address` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### MsgBeginRedelegateResponse +MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | + + + + + + + + +### MsgCreateValidator +MsgCreateValidator defines a SDK message for creating a new validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `description` | [Description](#cosmos.staking.v1beta1.Description) | | | +| `commission` | [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) | | | +| `min_self_delegation` | [string](#string) | | | +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | +| `pubkey` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `value` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### MsgCreateValidatorResponse +MsgCreateValidatorResponse defines the Msg/CreateValidator response type. + + + + + + + + +### MsgDelegate +MsgDelegate defines a SDK message for performing a delegation of coins +from a delegator to a validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### MsgDelegateResponse +MsgDelegateResponse defines the Msg/Delegate response type. + + + + + + + + +### MsgEditValidator +MsgEditValidator defines a SDK message for editing an existing validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `description` | [Description](#cosmos.staking.v1beta1.Description) | | | +| `validator_address` | [string](#string) | | | +| `commission_rate` | [string](#string) | | We pass a reference to the new commission rate and min self delegation as it's not mandatory to update. If not updated, the deserialized rate will be zero with no way to distinguish if an update was intended. REF: #2373 | +| `min_self_delegation` | [string](#string) | | | + + + + + + + + +### MsgEditValidatorResponse +MsgEditValidatorResponse defines the Msg/EditValidator response type. + + + + + + + + +### MsgUndelegate +MsgUndelegate defines a SDK message for performing an undelegation from a +delegate and a validator. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `delegator_address` | [string](#string) | | | +| `validator_address` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | | + + + + + + + + +### MsgUndelegateResponse +MsgUndelegateResponse defines the Msg/Undelegate response type. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | + + + + + + + + + + + + + + +### Msg +Msg defines the staking Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CreateValidator` | [MsgCreateValidator](#cosmos.staking.v1beta1.MsgCreateValidator) | [MsgCreateValidatorResponse](#cosmos.staking.v1beta1.MsgCreateValidatorResponse) | CreateValidator defines a method for creating a new validator. | | +| `EditValidator` | [MsgEditValidator](#cosmos.staking.v1beta1.MsgEditValidator) | [MsgEditValidatorResponse](#cosmos.staking.v1beta1.MsgEditValidatorResponse) | EditValidator defines a method for editing an existing validator. | | +| `Delegate` | [MsgDelegate](#cosmos.staking.v1beta1.MsgDelegate) | [MsgDelegateResponse](#cosmos.staking.v1beta1.MsgDelegateResponse) | Delegate defines a method for performing a delegation of coins from a delegator to a validator. | | +| `BeginRedelegate` | [MsgBeginRedelegate](#cosmos.staking.v1beta1.MsgBeginRedelegate) | [MsgBeginRedelegateResponse](#cosmos.staking.v1beta1.MsgBeginRedelegateResponse) | BeginRedelegate defines a method for performing a redelegation of coins from a delegator and source validator to a destination validator. | | +| `Undelegate` | [MsgUndelegate](#cosmos.staking.v1beta1.MsgUndelegate) | [MsgUndelegateResponse](#cosmos.staking.v1beta1.MsgUndelegateResponse) | Undelegate defines a method for performing an undelegation from a delegate and a validator. | | + + + + + + +

Top

+ +## cosmos/tx/signing/v1beta1/signing.proto + + + + + +### SignatureDescriptor +SignatureDescriptor is a convenience type which represents the full data for +a signature including the public key of the signer, signing modes and the +signature itself. It is primarily used for coordinating signatures between +clients. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public_key is the public key of the signer | +| `data` | [SignatureDescriptor.Data](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data) | | | +| `sequence` | [uint64](#uint64) | | sequence is the sequence of the account, which describes the number of committed transactions signed by a given address. It is used to prevent replay attacks. | + + + + + + + + +### SignatureDescriptor.Data +Data represents signature data + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `single` | [SignatureDescriptor.Data.Single](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Single) | | single represents a single signer | +| `multi` | [SignatureDescriptor.Data.Multi](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Multi) | | multi represents a multisig signer | + + + + + + + + +### SignatureDescriptor.Data.Multi +Multi is the signature data for a multisig public key + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `bitarray` | [cosmos.crypto.multisig.v1beta1.CompactBitArray](#cosmos.crypto.multisig.v1beta1.CompactBitArray) | | bitarray specifies which keys within the multisig are signing | +| `signatures` | [SignatureDescriptor.Data](#cosmos.tx.signing.v1beta1.SignatureDescriptor.Data) | repeated | signatures is the signatures of the multi-signature | + + + + + + + + +### SignatureDescriptor.Data.Single +Single is the signature data for a single signer + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mode` | [SignMode](#cosmos.tx.signing.v1beta1.SignMode) | | mode is the signing mode of the single signer | +| `signature` | [bytes](#bytes) | | signature is the raw signature bytes | + + + + + + + + +### SignatureDescriptors +SignatureDescriptors wraps multiple SignatureDescriptor's. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signatures` | [SignatureDescriptor](#cosmos.tx.signing.v1beta1.SignatureDescriptor) | repeated | signatures are the signature descriptors | + + + + + + + + + + +### SignMode +SignMode represents a signing mode with its own security guarantees. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| SIGN_MODE_UNSPECIFIED | 0 | SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be rejected | +| SIGN_MODE_DIRECT | 1 | SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is verified with raw bytes from Tx | +| SIGN_MODE_TEXTUAL | 2 | SIGN_MODE_TEXTUAL is a future signing mode that will verify some human-readable textual representation on top of the binary representation from SIGN_MODE_DIRECT | +| SIGN_MODE_LEGACY_AMINO_JSON | 127 | SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses Amino JSON and will be removed in the future | + + + + + + + + + + + +

Top

+ +## cosmos/tx/v1beta1/tx.proto + + + + + +### AuthInfo +AuthInfo describes the fee and signer modes that are used to sign a +transaction. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signer_infos` | [SignerInfo](#cosmos.tx.v1beta1.SignerInfo) | repeated | signer_infos defines the signing modes for the required signers. The number and order of elements must match the required signers from TxBody's messages. The first element is the primary signer and the one which pays the fee. | +| `fee` | [Fee](#cosmos.tx.v1beta1.Fee) | | Fee is the fee and gas limit for the transaction. The first signer is the primary signer and the one which pays the fee. The fee can be calculated based on the cost of evaluating the body and doing signature verification of the signers. This can be estimated via simulation. | + + + + + + + + +### Fee +Fee includes the amount of coins paid in fees and the maximum +gas to be used by the transaction. The ratio yields an effective "gasprice", +which must be above some miminum to be accepted into the mempool. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | amount is the amount of coins to be paid as a fee | +| `gas_limit` | [uint64](#uint64) | | gas_limit is the maximum gas that can be used in transaction processing before an out of gas error occurs | +| `payer` | [string](#string) | | if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. the payer must be a tx signer (and thus have signed this field in AuthInfo). setting this field does *not* change the ordering of required signers for the transaction. | +| `granter` | [string](#string) | | if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does not support fee grants, this will fail | + + + + + + + + +### ModeInfo +ModeInfo describes the signing mode of a single or nested multisig signer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `single` | [ModeInfo.Single](#cosmos.tx.v1beta1.ModeInfo.Single) | | single represents a single signer | +| `multi` | [ModeInfo.Multi](#cosmos.tx.v1beta1.ModeInfo.Multi) | | multi represents a nested multisig signer | + + + + + + + + +### ModeInfo.Multi +Multi is the mode info for a multisig public key + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `bitarray` | [cosmos.crypto.multisig.v1beta1.CompactBitArray](#cosmos.crypto.multisig.v1beta1.CompactBitArray) | | bitarray specifies which keys within the multisig are signing | +| `mode_infos` | [ModeInfo](#cosmos.tx.v1beta1.ModeInfo) | repeated | mode_infos is the corresponding modes of the signers of the multisig which could include nested multisig public keys | + + + + + + + + +### ModeInfo.Single +Single is the mode info for a single signer. It is structured as a message +to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the +future + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mode` | [cosmos.tx.signing.v1beta1.SignMode](#cosmos.tx.signing.v1beta1.SignMode) | | mode is the signing mode of the single signer | + + + + + + + + +### SignDoc +SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `body_bytes` | [bytes](#bytes) | | body_bytes is protobuf serialization of a TxBody that matches the representation in TxRaw. | +| `auth_info_bytes` | [bytes](#bytes) | | auth_info_bytes is a protobuf serialization of an AuthInfo that matches the representation in TxRaw. | +| `chain_id` | [string](#string) | | chain_id is the unique identifier of the chain this transaction targets. It prevents signed transactions from being used on another chain by an attacker | +| `account_number` | [uint64](#uint64) | | account_number is the account number of the account in state | + + + + + + + + +### SignerInfo +SignerInfo describes the public key and signing mode of a single top-level +signer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public_key is the public key of the signer. It is optional for accounts that already exist in state. If unset, the verifier can use the required \ signer address for this position and lookup the public key. | +| `mode_info` | [ModeInfo](#cosmos.tx.v1beta1.ModeInfo) | | mode_info describes the signing mode of the signer and is a nested structure to support nested multisig pubkey's | +| `sequence` | [uint64](#uint64) | | sequence is the sequence of the account, which describes the number of committed transactions signed by a given address. It is used to prevent replay attacks. | + + + + + + + + +### Tx +Tx is the standard type used for broadcasting transactions. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `body` | [TxBody](#cosmos.tx.v1beta1.TxBody) | | body is the processable content of the transaction | +| `auth_info` | [AuthInfo](#cosmos.tx.v1beta1.AuthInfo) | | auth_info is the authorization related content of the transaction, specifically signers, signer modes and fee | +| `signatures` | [bytes](#bytes) | repeated | signatures is a list of signatures that matches the length and order of AuthInfo's signer_infos to allow connecting signature meta information like public key and signing mode by position. | + + + + + + + + +### TxBody +TxBody is the body of a transaction that all signers sign over. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `messages` | [google.protobuf.Any](#google.protobuf.Any) | repeated | messages is a list of messages to be executed. The required signers of those messages define the number and order of elements in AuthInfo's signer_infos and Tx's signatures. Each required signer address is added to the list only the first time it occurs. By convention, the first required signer (usually from the first message) is referred to as the primary signer and pays the fee for the whole transaction. | +| `memo` | [string](#string) | | memo is any arbitrary memo to be added to the transaction | +| `timeout_height` | [uint64](#uint64) | | timeout is the block height after which this transaction will not be processed by the chain | +| `extension_options` | [google.protobuf.Any](#google.protobuf.Any) | repeated | extension_options are arbitrary options that can be added by chains when the default options are not sufficient. If any of these are present and can't be handled, the transaction will be rejected | +| `non_critical_extension_options` | [google.protobuf.Any](#google.protobuf.Any) | repeated | extension_options are arbitrary options that can be added by chains when the default options are not sufficient. If any of these are present and can't be handled, they will be ignored | + + + + + + + + +### TxRaw +TxRaw is a variant of Tx that pins the signer's exact binary representation +of body and auth_info. This is used for signing, broadcasting and +verification. The binary `serialize(tx: TxRaw)` is stored in Tendermint and +the hash `sha256(serialize(tx: TxRaw))` becomes the "txhash", commonly used +as the transaction ID. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `body_bytes` | [bytes](#bytes) | | body_bytes is a protobuf serialization of a TxBody that matches the representation in SignDoc. | +| `auth_info_bytes` | [bytes](#bytes) | | auth_info_bytes is a protobuf serialization of an AuthInfo that matches the representation in SignDoc. | +| `signatures` | [bytes](#bytes) | repeated | signatures is a list of signatures that matches the length and order of AuthInfo's signer_infos to allow connecting signature meta information like public key and signing mode by position. | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/tx/v1beta1/service.proto + + + + + +### BroadcastTxRequest +BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `tx_bytes` | [bytes](#bytes) | | tx_bytes is the raw transaction. | +| `mode` | [BroadcastMode](#cosmos.tx.v1beta1.BroadcastMode) | | | + + + + + + + + +### BroadcastTxResponse +BroadcastTxResponse is the response type for the +Service.BroadcastTx method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `tx_response` | [cosmos.base.abci.v1beta1.TxResponse](#cosmos.base.abci.v1beta1.TxResponse) | | tx_response is the queried TxResponses. | + + + + + + + + +### GetTxRequest +GetTxRequest is the request type for the Service.GetTx +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hash` | [string](#string) | | hash is the tx hash to query, encoded as a hex string. | + + + + + + + + +### GetTxResponse +GetTxResponse is the response type for the Service.GetTx method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `tx` | [Tx](#cosmos.tx.v1beta1.Tx) | | tx is the queried transaction. | +| `tx_response` | [cosmos.base.abci.v1beta1.TxResponse](#cosmos.base.abci.v1beta1.TxResponse) | | tx_response is the queried TxResponses. | + + + + + + + + +### GetTxsEventRequest +GetTxsEventRequest is the request type for the Service.TxsByEvents +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `events` | [string](#string) | repeated | events is the list of transaction event type. | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. | +| `order_by` | [OrderBy](#cosmos.tx.v1beta1.OrderBy) | | | + + + + + + + + +### GetTxsEventResponse +GetTxsEventResponse is the response type for the Service.TxsByEvents +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `txs` | [Tx](#cosmos.tx.v1beta1.Tx) | repeated | txs is the list of queried transactions. | +| `tx_responses` | [cosmos.base.abci.v1beta1.TxResponse](#cosmos.base.abci.v1beta1.TxResponse) | repeated | tx_responses is the list of queried TxResponses. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. | + + + + + + + + +### SimulateRequest +SimulateRequest is the request type for the Service.Simulate +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `tx` | [Tx](#cosmos.tx.v1beta1.Tx) | | tx is the transaction to simulate. | + + + + + + + + +### SimulateResponse +SimulateResponse is the response type for the +Service.SimulateRPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gas_info` | [cosmos.base.abci.v1beta1.GasInfo](#cosmos.base.abci.v1beta1.GasInfo) | | gas_info is the information about gas used in the simulation. | +| `result` | [cosmos.base.abci.v1beta1.Result](#cosmos.base.abci.v1beta1.Result) | | result is the result of the simulation. | + + + + + + + + + + +### BroadcastMode +BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| BROADCAST_MODE_UNSPECIFIED | 0 | zero-value for mode ordering | +| BROADCAST_MODE_BLOCK | 1 | BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for the tx to be committed in a block. | +| BROADCAST_MODE_SYNC | 2 | BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for a CheckTx execution response only. | +| BROADCAST_MODE_ASYNC | 3 | BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns immediately. | + + + + + +### OrderBy +OrderBy defines the sorting order + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ORDER_BY_UNSPECIFIED | 0 | ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults to ASC in this case. | +| ORDER_BY_ASC | 1 | ORDER_BY_ASC defines ascending order | +| ORDER_BY_DESC | 2 | ORDER_BY_DESC defines descending order | + + + + + + + + + +### Service +Service defines a gRPC service for interacting with transactions. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Simulate` | [SimulateRequest](#cosmos.tx.v1beta1.SimulateRequest) | [SimulateResponse](#cosmos.tx.v1beta1.SimulateResponse) | Simulate simulates executing a transaction for estimating gas usage. | POST|/cosmos/tx/v1beta1/simulate| +| `GetTx` | [GetTxRequest](#cosmos.tx.v1beta1.GetTxRequest) | [GetTxResponse](#cosmos.tx.v1beta1.GetTxResponse) | GetTx fetches a tx by hash. | GET|/cosmos/tx/v1beta1/txs/{hash}| +| `BroadcastTx` | [BroadcastTxRequest](#cosmos.tx.v1beta1.BroadcastTxRequest) | [BroadcastTxResponse](#cosmos.tx.v1beta1.BroadcastTxResponse) | BroadcastTx broadcast transaction. | POST|/cosmos/tx/v1beta1/txs| +| `GetTxsEvent` | [GetTxsEventRequest](#cosmos.tx.v1beta1.GetTxsEventRequest) | [GetTxsEventResponse](#cosmos.tx.v1beta1.GetTxsEventResponse) | GetTxsEvent fetches txs by event. | GET|/cosmos/tx/v1beta1/txs| + + + + + + +

Top

+ +## cosmos/upgrade/v1beta1/upgrade.proto + + + + + +### CancelSoftwareUpgradeProposal +CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software +upgrade. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | + + + + + + + + +### Plan +Plan specifies information about a planned upgrade and when it should occur. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `name` | [string](#string) | | Sets the name for the upgrade. This name will be used by the upgraded version of the software to apply any special "on-upgrade" commands during the first BeginBlock method after the upgrade is applied. It is also used to detect whether a software version can handle a given upgrade. If no upgrade handler with this name has been set in the software, it will be assumed that the software is out-of-date when the upgrade Time or Height is reached and the software will exit. | +| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | The time after which the upgrade must be performed. Leave set to its zero value to use a pre-defined Height instead. | +| `height` | [int64](#int64) | | The height at which the upgrade must be performed. Only used if Time is not set. | +| `info` | [string](#string) | | Any application specific upgrade info to be included on-chain such as a git commit that validators could automatically upgrade to | +| `upgraded_client_state` | [google.protobuf.Any](#google.protobuf.Any) | | IBC-enabled chains can opt-in to including the upgraded client state in its upgrade plan This will make the chain commit to the correct upgraded (self) client state before the upgrade occurs, so that connecting chains can verify that the new upgraded client is valid by verifying a proof on the previous version of the chain. This will allow IBC connections to persist smoothly across planned chain upgrades | + + + + + + + + +### SoftwareUpgradeProposal +SoftwareUpgradeProposal is a gov Content type for initiating a software +upgrade. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `title` | [string](#string) | | | +| `description` | [string](#string) | | | +| `plan` | [Plan](#cosmos.upgrade.v1beta1.Plan) | | | + + + + + + + + + + + + + + + + +

Top

+ +## cosmos/upgrade/v1beta1/query.proto + + + + + +### QueryAppliedPlanRequest +QueryCurrentPlanRequest is the request type for the Query/AppliedPlan RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `name` | [string](#string) | | name is the name of the applied plan to query for. | + + + + + + + + +### QueryAppliedPlanResponse +QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [int64](#int64) | | height is the block height at which the plan was applied. | + + + + + + + + +### QueryCurrentPlanRequest +QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC +method. + + + + + + + + +### QueryCurrentPlanResponse +QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `plan` | [Plan](#cosmos.upgrade.v1beta1.Plan) | | plan is the current upgrade plan. | + + + + + + + + +### QueryUpgradedConsensusStateRequest +QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `last_height` | [int64](#int64) | | last height of the current chain must be sent in request as this is the height under which next consensus state is stored | + + + + + + + + +### QueryUpgradedConsensusStateResponse +QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `upgraded_consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | | + + + + + + + + + + + + + + +### Query +Query defines the gRPC upgrade querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CurrentPlan` | [QueryCurrentPlanRequest](#cosmos.upgrade.v1beta1.QueryCurrentPlanRequest) | [QueryCurrentPlanResponse](#cosmos.upgrade.v1beta1.QueryCurrentPlanResponse) | CurrentPlan queries the current upgrade plan. | GET|/cosmos/upgrade/v1beta1/current_plan| +| `AppliedPlan` | [QueryAppliedPlanRequest](#cosmos.upgrade.v1beta1.QueryAppliedPlanRequest) | [QueryAppliedPlanResponse](#cosmos.upgrade.v1beta1.QueryAppliedPlanResponse) | AppliedPlan queries a previously applied upgrade plan by its name. | GET|/cosmos/upgrade/v1beta1/applied_plan/{name}| +| `UpgradedConsensusState` | [QueryUpgradedConsensusStateRequest](#cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateRequest) | [QueryUpgradedConsensusStateResponse](#cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateResponse) | UpgradedConsensusState queries the consensus state that will serve as a trusted kernel for the next version of this chain. It will only be stored at the last height of this chain. UpgradedConsensusState RPC not supported with legacy querier | GET|/cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}| + + + + + + +

Top

+ +## cosmos/vesting/v1beta1/tx.proto + + + + + +### MsgCreateVestingAccount +MsgCreateVestingAccount defines a message that enables creating a vesting +account. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `from_address` | [string](#string) | | | +| `to_address` | [string](#string) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `end_time` | [int64](#int64) | | | +| `delayed` | [bool](#bool) | | | + + + + + + + + +### MsgCreateVestingAccountResponse +MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type. + + + + + + + + + + + + + + +### Msg +Msg defines the bank Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CreateVestingAccount` | [MsgCreateVestingAccount](#cosmos.vesting.v1beta1.MsgCreateVestingAccount) | [MsgCreateVestingAccountResponse](#cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse) | CreateVestingAccount defines a method that enables creating a vesting account. | | + + + + + + +

Top

+ +## cosmos/vesting/v1beta1/vesting.proto + + + + + +### BaseVestingAccount +BaseVestingAccount implements the VestingAccount interface. It contains all +the necessary fields needed for any vesting account implementation. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_account` | [cosmos.auth.v1beta1.BaseAccount](#cosmos.auth.v1beta1.BaseAccount) | | | +| `original_vesting` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `delegated_free` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `delegated_vesting` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | +| `end_time` | [int64](#int64) | | | + + + + + + + + +### ContinuousVestingAccount +ContinuousVestingAccount implements the VestingAccount interface. It +continuously vests by unlocking coins linearly with respect to time. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_vesting_account` | [BaseVestingAccount](#cosmos.vesting.v1beta1.BaseVestingAccount) | | | +| `start_time` | [int64](#int64) | | | + + + + + + + + +### DelayedVestingAccount +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. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_vesting_account` | [BaseVestingAccount](#cosmos.vesting.v1beta1.BaseVestingAccount) | | | + + + + + + + + +### Period +Period defines a length of time and amount of coins that will vest. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `length` | [int64](#int64) | | | +| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | | + + + + + + + + +### PeriodicVestingAccount +PeriodicVestingAccount implements the VestingAccount interface. It +periodically vests by unlocking coins during each specified period. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `base_vesting_account` | [BaseVestingAccount](#cosmos.vesting.v1beta1.BaseVestingAccount) | | | +| `start_time` | [int64](#int64) | | | +| `vesting_periods` | [Period](#cosmos.vesting.v1beta1.Period) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/applications/transfer/v1/transfer.proto + + + + + +### DenomTrace +DenomTrace contains the base denomination for ICS20 fungible tokens and the +source tracing information path. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [string](#string) | | path defines the chain of port/channel identifiers used for tracing the source of the fungible token. | +| `base_denom` | [string](#string) | | base denomination of the relayed fungible token. | + + + + + + + + +### FungibleTokenPacketData +FungibleTokenPacketData defines a struct for the packet payload +See FungibleTokenPacketData spec: +https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom` | [string](#string) | | the token denomination to be transferred | +| `amount` | [uint64](#uint64) | | the token amount to be transferred | +| `sender` | [string](#string) | | the sender address | +| `receiver` | [string](#string) | | the recipient address on the destination chain | + + + + + + + + +### Params +Params defines the set of IBC transfer parameters. +NOTE: To prevent a single token from being transferred, set the +TransfersEnabled parameter to true and then set the bank module's SendEnabled +parameter for the denomination to false. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `send_enabled` | [bool](#bool) | | send_enabled enables or disables all cross-chain token transfers from this chain. | +| `receive_enabled` | [bool](#bool) | | receive_enabled enables or disables all cross-chain token transfers to this chain. | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/applications/transfer/v1/genesis.proto + + + + + +### GenesisState +GenesisState defines the ibc-transfer genesis state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | | +| `params` | [Params](#ibc.applications.transfer.v1.Params) | | | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/applications/transfer/v1/query.proto + + + + + +### QueryDenomTraceRequest +QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hash` | [string](#string) | | hash (in hex format) of the denomination trace information. | + + + + + + + + +### QueryDenomTraceResponse +QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom_trace` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | | denom_trace returns the requested denomination trace information. | + + + + + + + + +### QueryDenomTracesRequest +QueryConnectionsRequest is the request type for the Query/DenomTraces 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. | + + + + + + + + +### QueryDenomTracesResponse +QueryConnectionsResponse is the response type for the Query/DenomTraces RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `denom_traces` | [DenomTrace](#ibc.applications.transfer.v1.DenomTrace) | repeated | denom_traces returns all denominations trace information. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. | + + + + + + + + +### 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 | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#ibc.applications.transfer.v1.Params) | | params defines the parameters of the module. | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `DenomTrace` | [QueryDenomTraceRequest](#ibc.applications.transfer.v1.QueryDenomTraceRequest) | [QueryDenomTraceResponse](#ibc.applications.transfer.v1.QueryDenomTraceResponse) | DenomTrace queries a denomination trace information. | GET|/ibc/applications/transfer/v1beta1/denom_traces/{hash}| +| `DenomTraces` | [QueryDenomTracesRequest](#ibc.applications.transfer.v1.QueryDenomTracesRequest) | [QueryDenomTracesResponse](#ibc.applications.transfer.v1.QueryDenomTracesResponse) | DenomTraces queries all denomination traces. | GET|/ibc/applications/transfer/v1beta1/denom_traces| +| `Params` | [QueryParamsRequest](#ibc.applications.transfer.v1.QueryParamsRequest) | [QueryParamsResponse](#ibc.applications.transfer.v1.QueryParamsResponse) | Params queries all parameters of the ibc-transfer module. | GET|/ibc/applications/transfer/v1beta1/params| + + + + + + +

Top

+ +## ibc/core/client/v1/client.proto + + + + + +### ClientConsensusStates +ClientConsensusStates defines all the stored consensus states for a given +client. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client identifier | +| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states and their heights associated with the 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. + + +| 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 | + + + + + + + + +### ConsensusStateWithHeight +ConsensusStateWithHeight defines a consensus state with an additional height field. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `height` | [Height](#ibc.core.client.v1.Height) | | consensus state height | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state | + + + + + + + + +### Height +Height is a monotonically increasing data type +that can be compared against another Height for the purposes of updating and +freezing clients + +Normally the RevisionHeight is incremented at each height while keeping RevisionNumber +the same. However some consensus algorithms may choose to reset the +height in certain conditions e.g. hard forks, state-machine breaking changes +In these cases, the RevisionNumber is incremented so that height continues to +be monitonically increasing even as the RevisionHeight gets reset + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `revision_number` | [uint64](#uint64) | | the revision that the client is currently on | +| `revision_height` | [uint64](#uint64) | | the height within the given revision | + + + + + + + + +### IdentifiedClientState +IdentifiedClientState defines a client state with an additional client +identifier field. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client identifier | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state | + + + + + + + + +### Params +Params defines the set of IBC light client parameters. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `allowed_clients` | [string](#string) | repeated | allowed_clients defines the list of allowed client state types. | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/applications/transfer/v1/tx.proto + + + + + +### MsgTransfer +MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between +ICS20 enabled chains. See ICS Spec here: +https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `source_port` | [string](#string) | | the port on which the packet will be sent | +| `source_channel` | [string](#string) | | the channel by which the packet will be sent | +| `token` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | the tokens to be transferred | +| `sender` | [string](#string) | | the sender address | +| `receiver` | [string](#string) | | the recipient address on the destination chain | +| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Timeout height relative to the current block height. The timeout is disabled when set to 0. | +| `timeout_timestamp` | [uint64](#uint64) | | Timeout timestamp (in nanoseconds) relative to the current block timestamp. The timeout is disabled when set to 0. | + + + + + + + + +### MsgTransferResponse +MsgTransferResponse defines the Msg/Transfer response type. + + + + + + + + + + + + + + +### Msg +Msg defines the ibc/transfer Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Transfer` | [MsgTransfer](#ibc.applications.transfer.v1.MsgTransfer) | [MsgTransferResponse](#ibc.applications.transfer.v1.MsgTransferResponse) | Transfer defines a rpc handler method for MsgTransfer. | | + + + + + + +

Top

+ +## ibc/core/channel/v1/channel.proto + + + + + +### Acknowledgement +Acknowledgement is the recommended acknowledgement format to be used by +app-specific protocols. +NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +conflicts with other protobuf message formats used for acknowledgements. +The first byte of any message with this format will be the non-ASCII values +`0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `result` | [bytes](#bytes) | | | +| `error` | [string](#string) | | | + + + + + + + + +### Channel +Channel defines pipeline for exactly-once packet delivery between specific +modules on separate blockchains, which has at least one end capable of +sending packets and one end capable of receiving packets. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | +| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | +| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | +| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | +| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | + + + + + + + + +### Counterparty +Counterparty defines a channel end counterparty + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port on the counterparty chain which owns the other end of the channel. | +| `channel_id` | [string](#string) | | channel end on the counterparty chain | + + + + + + + + +### IdentifiedChannel +IdentifiedChannel defines a channel with additional port and channel +identifier fields. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `state` | [State](#ibc.core.channel.v1.State) | | current state of the channel end | +| `ordering` | [Order](#ibc.core.channel.v1.Order) | | whether the channel is ordered or unordered | +| `counterparty` | [Counterparty](#ibc.core.channel.v1.Counterparty) | | counterparty channel end | +| `connection_hops` | [string](#string) | repeated | list of connection identifiers, in order, along which packets sent on this channel will travel | +| `version` | [string](#string) | | opaque channel version, which is agreed upon during the handshake | +| `port_id` | [string](#string) | | port identifier | +| `channel_id` | [string](#string) | | channel identifier | + + + + + + + + +### Packet +Packet defines a type that carries data across different chains through IBC + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | number corresponds to the order of sends and receives, where a Packet with an earlier sequence number must be sent and received before a Packet with a later sequence number. | +| `source_port` | [string](#string) | | identifies the port on the sending chain. | +| `source_channel` | [string](#string) | | identifies the channel end on the sending chain. | +| `destination_port` | [string](#string) | | identifies the port on the receiving chain. | +| `destination_channel` | [string](#string) | | identifies the channel end on the receiving chain. | +| `data` | [bytes](#bytes) | | actual opaque bytes transferred directly to the application module | +| `timeout_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | block height after which the packet times out | +| `timeout_timestamp` | [uint64](#uint64) | | block timestamp (in nanoseconds) after which the packet times out | + + + + + + + + +### PacketState +PacketState defines the generic type necessary to retrieve and store +packet commitments, acknowledgements, and receipts. +Caller is responsible for knowing the context necessary to interpret this +state as a commitment, acknowledgement, or a receipt. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | channel port identifier. | +| `channel_id` | [string](#string) | | channel unique identifier. | +| `sequence` | [uint64](#uint64) | | packet sequence. | +| `data` | [bytes](#bytes) | | embedded data that represents packet state. | + + + + + + + + + + +### Order +Order defines if a channel is ORDERED or UNORDERED + +| Name | Number | Description | +| ---- | ------ | ----------- | +| ORDER_NONE_UNSPECIFIED | 0 | zero-value for channel ordering | +| ORDER_UNORDERED | 1 | packets can be delivered in any order, which may differ from the order in which they were sent. | +| ORDER_ORDERED | 2 | packets are delivered exactly in the order which they were sent | + + + + + +### State +State defines if a channel is in one of the following states: +CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | +| STATE_INIT | 1 | A channel has just started the opening handshake. | +| STATE_TRYOPEN | 2 | A channel has acknowledged the handshake step on the counterparty chain. | +| STATE_OPEN | 3 | A channel has completed the handshake. Open channels are ready to send and receive packets. | +| STATE_CLOSED | 4 | A channel has been closed and can no longer be used to send or receive packets. | + + + + + + + + + + + +

Top

+ +## ibc/core/channel/v1/genesis.proto + + + + + +### GenesisState +GenesisState defines the ibc channel submodule's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | | +| `acknowledgements` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | +| `commitments` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | +| `receipts` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | +| `send_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | +| `recv_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | +| `ack_sequences` | [PacketSequence](#ibc.core.channel.v1.PacketSequence) | repeated | | +| `next_channel_sequence` | [uint64](#uint64) | | the sequence for the next generated channel identifier | + + + + + + + + +### PacketSequence +PacketSequence defines the genesis type necessary to retrieve and store +next send and receive sequences. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `sequence` | [uint64](#uint64) | | | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/core/channel/v1/query.proto + + + + + +### QueryChannelClientStateRequest +QueryChannelClientStateRequest is the request type for the Query/ClientState +RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | + + + + + + + + +### QueryChannelClientStateResponse +QueryChannelClientStateResponse is the Response type for the +Query/QueryChannelClientState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `identified_client_state` | [ibc.core.client.v1.IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | | client state associated with the channel | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryChannelConsensusStateRequest +QueryChannelConsensusStateRequest is the request type for the +Query/ConsensusState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `revision_number` | [uint64](#uint64) | | revision number of the consensus state | +| `revision_height` | [uint64](#uint64) | | revision height of the consensus state | + + + + + + + + +### QueryChannelConsensusStateResponse +QueryChannelClientStateResponse is the Response type for the +Query/QueryChannelClientState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the channel | +| `client_id` | [string](#string) | | client ID associated with the consensus state | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryChannelRequest +QueryChannelRequest is the request type for the Query/Channel RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | + + + + + + + + +### QueryChannelResponse +QueryChannelResponse is the response type for the Query/Channel RPC method. +Besides the Channel end, it includes a proof and the height from which the +proof was retrieved. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `channel` | [Channel](#ibc.core.channel.v1.Channel) | | channel associated with the request identifiers | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryChannelsRequest +QueryChannelsRequest is the request type for the Query/Channels RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryChannelsResponse +QueryChannelsResponse is the response type for the Query/Channels RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | list of stored channels of the chain. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + +### QueryConnectionChannelsRequest +QueryConnectionChannelsRequest is the request type for the +Query/QueryConnectionChannels RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection` | [string](#string) | | connection unique identifier | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryConnectionChannelsResponse +QueryConnectionChannelsResponse is the Response type for the +Query/QueryConnectionChannels RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `channels` | [IdentifiedChannel](#ibc.core.channel.v1.IdentifiedChannel) | repeated | list of channels associated with a connection. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + +### QueryNextSequenceReceiveRequest +QueryNextSequenceReceiveRequest is the request type for the +Query/QueryNextSequenceReceiveRequest RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | + + + + + + + + +### QueryNextSequenceReceiveResponse +QuerySequenceResponse is the request type for the +Query/QueryNextSequenceReceiveResponse RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `next_sequence_receive` | [uint64](#uint64) | | next sequence receive number | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryPacketAcknowledgementRequest +QueryPacketAcknowledgementRequest is the request type for the +Query/PacketAcknowledgement RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `sequence` | [uint64](#uint64) | | packet sequence | + + + + + + + + +### QueryPacketAcknowledgementResponse +QueryPacketAcknowledgementResponse defines the client query response for a +packet which also includes a proof and the height from which the +proof was retrieved + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `acknowledgement` | [bytes](#bytes) | | packet associated with the request fields | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryPacketAcknowledgementsRequest +QueryPacketAcknowledgementsRequest is the request type for the +Query/QueryPacketCommitments RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryPacketAcknowledgementsResponse +QueryPacketAcknowledgemetsResponse is the request type for the +Query/QueryPacketAcknowledgements RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `acknowledgements` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + +### QueryPacketCommitmentRequest +QueryPacketCommitmentRequest is the request type for the +Query/PacketCommitment RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `sequence` | [uint64](#uint64) | | packet sequence | + + + + + + + + +### QueryPacketCommitmentResponse +QueryPacketCommitmentResponse defines the client query response for a packet +which also includes a proof and the height from which the proof was +retrieved + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `commitment` | [bytes](#bytes) | | packet associated with the request fields | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryPacketCommitmentsRequest +QueryPacketCommitmentsRequest is the request type for the +Query/QueryPacketCommitments RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryPacketCommitmentsResponse +QueryPacketCommitmentsResponse is the request type for the +Query/QueryPacketCommitments RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `commitments` | [PacketState](#ibc.core.channel.v1.PacketState) | repeated | | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + +### QueryPacketReceiptRequest +QueryPacketReceiptRequest is the request type for the +Query/PacketReceipt RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `sequence` | [uint64](#uint64) | | packet sequence | + + + + + + + + +### QueryPacketReceiptResponse +QueryPacketReceiptResponse defines the client query response for a packet receipt +which also includes a proof, and the height from which the proof was +retrieved + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `received` | [bool](#bool) | | success flag for if receipt exists | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryUnreceivedAcksRequest +QueryUnreceivedAcks is the request type for the +Query/UnreceivedAcks RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `packet_ack_sequences` | [uint64](#uint64) | repeated | list of acknowledgement sequences | + + + + + + + + +### QueryUnreceivedAcksResponse +QueryUnreceivedAcksResponse is the response type for the +Query/UnreceivedAcks RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequences` | [uint64](#uint64) | repeated | list of unreceived acknowledgement sequences | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + +### QueryUnreceivedPacketsRequest +QueryUnreceivedPacketsRequest is the request type for the +Query/UnreceivedPackets RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | port unique identifier | +| `channel_id` | [string](#string) | | channel unique identifier | +| `packet_commitment_sequences` | [uint64](#uint64) | repeated | list of packet sequences | + + + + + + + + +### QueryUnreceivedPacketsResponse +QueryUnreceivedPacketsResponse is the response type for the +Query/UnreceivedPacketCommitments RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequences` | [uint64](#uint64) | repeated | list of unreceived packet sequences | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Channel` | [QueryChannelRequest](#ibc.core.channel.v1.QueryChannelRequest) | [QueryChannelResponse](#ibc.core.channel.v1.QueryChannelResponse) | Channel queries an IBC Channel. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}| +| `Channels` | [QueryChannelsRequest](#ibc.core.channel.v1.QueryChannelsRequest) | [QueryChannelsResponse](#ibc.core.channel.v1.QueryChannelsResponse) | Channels queries all the IBC channels of a chain. | GET|/ibc/core/channel/v1beta1/channels| +| `ConnectionChannels` | [QueryConnectionChannelsRequest](#ibc.core.channel.v1.QueryConnectionChannelsRequest) | [QueryConnectionChannelsResponse](#ibc.core.channel.v1.QueryConnectionChannelsResponse) | ConnectionChannels queries all the channels associated with a connection end. | GET|/ibc/core/channel/v1beta1/connections/{connection}/channels| +| `ChannelClientState` | [QueryChannelClientStateRequest](#ibc.core.channel.v1.QueryChannelClientStateRequest) | [QueryChannelClientStateResponse](#ibc.core.channel.v1.QueryChannelClientStateResponse) | ChannelClientState queries for the client state for the channel associated with the provided channel identifiers. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/client_state| +| `ChannelConsensusState` | [QueryChannelConsensusStateRequest](#ibc.core.channel.v1.QueryChannelConsensusStateRequest) | [QueryChannelConsensusStateResponse](#ibc.core.channel.v1.QueryChannelConsensusStateResponse) | ChannelConsensusState queries for the consensus state for the channel associated with the provided channel identifiers. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/{revision_number}/height/{revision_height}| +| `PacketCommitment` | [QueryPacketCommitmentRequest](#ibc.core.channel.v1.QueryPacketCommitmentRequest) | [QueryPacketCommitmentResponse](#ibc.core.channel.v1.QueryPacketCommitmentResponse) | PacketCommitment queries a stored packet commitment hash. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}| +| `PacketCommitments` | [QueryPacketCommitmentsRequest](#ibc.core.channel.v1.QueryPacketCommitmentsRequest) | [QueryPacketCommitmentsResponse](#ibc.core.channel.v1.QueryPacketCommitmentsResponse) | PacketCommitments returns all the packet commitments hashes associated with a channel. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments| +| `PacketReceipt` | [QueryPacketReceiptRequest](#ibc.core.channel.v1.QueryPacketReceiptRequest) | [QueryPacketReceiptResponse](#ibc.core.channel.v1.QueryPacketReceiptResponse) | PacketReceipt queries if a given packet sequence has been received on the queried chain | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}| +| `PacketAcknowledgement` | [QueryPacketAcknowledgementRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementRequest) | [QueryPacketAcknowledgementResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementResponse) | PacketAcknowledgement queries a stored packet acknowledgement hash. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_acks/{sequence}| +| `PacketAcknowledgements` | [QueryPacketAcknowledgementsRequest](#ibc.core.channel.v1.QueryPacketAcknowledgementsRequest) | [QueryPacketAcknowledgementsResponse](#ibc.core.channel.v1.QueryPacketAcknowledgementsResponse) | PacketAcknowledgements returns all the packet acknowledgements associated with a channel. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements| +| `UnreceivedPackets` | [QueryUnreceivedPacketsRequest](#ibc.core.channel.v1.QueryUnreceivedPacketsRequest) | [QueryUnreceivedPacketsResponse](#ibc.core.channel.v1.QueryUnreceivedPacketsResponse) | UnreceivedPackets returns all the unreceived IBC packets associated with a channel and sequences. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_commitment_sequences}/unreceived_packets| +| `UnreceivedAcks` | [QueryUnreceivedAcksRequest](#ibc.core.channel.v1.QueryUnreceivedAcksRequest) | [QueryUnreceivedAcksResponse](#ibc.core.channel.v1.QueryUnreceivedAcksResponse) | UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/{packet_ack_sequences}/unreceived_acks| +| `NextSequenceReceive` | [QueryNextSequenceReceiveRequest](#ibc.core.channel.v1.QueryNextSequenceReceiveRequest) | [QueryNextSequenceReceiveResponse](#ibc.core.channel.v1.QueryNextSequenceReceiveResponse) | NextSequenceReceive returns the next receive sequence for a given channel. | GET|/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/next_sequence| + + + + + + +

Top

+ +## ibc/core/channel/v1/tx.proto + + + + + +### MsgAcknowledgement +MsgAcknowledgement receives incoming IBC acknowledgement + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | +| `acknowledgement` | [bytes](#bytes) | | | +| `proof_acked` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgAcknowledgementResponse +MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. + + + + + + + + +### MsgChannelCloseConfirm +MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B +to acknowledge the change of channel state to CLOSED on Chain A. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `proof_init` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelCloseConfirmResponse +MsgChannelCloseConfirmResponse defines the Msg/ChannelCloseConfirm response type. + + + + + + + + +### MsgChannelCloseInit +MsgChannelCloseInit defines a msg sent by a Relayer to Chain A +to close a channel with Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelCloseInitResponse +MsgChannelCloseInitResponse defines the Msg/ChannelCloseInit response type. + + + + + + + + +### MsgChannelOpenAck +MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge +the change of channel state to TRYOPEN on Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `counterparty_channel_id` | [string](#string) | | | +| `counterparty_version` | [string](#string) | | | +| `proof_try` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelOpenAckResponse +MsgChannelOpenAckResponse defines the Msg/ChannelOpenAck response type. + + + + + + + + +### MsgChannelOpenConfirm +MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to +acknowledge the change of channel state to OPEN on Chain A. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel_id` | [string](#string) | | | +| `proof_ack` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelOpenConfirmResponse +MsgChannelOpenConfirmResponse defines the Msg/ChannelOpenConfirm response type. + + + + + + + + +### MsgChannelOpenInit +MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It +is called by a relayer on Chain A. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `channel` | [Channel](#ibc.core.channel.v1.Channel) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelOpenInitResponse +MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. + + + + + + + + +### MsgChannelOpenTry +MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel +on Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `port_id` | [string](#string) | | | +| `previous_channel_id` | [string](#string) | | in the case of crossing hello's, when both chains call OpenInit, we need the channel identifier of the previous channel in state INIT | +| `channel` | [Channel](#ibc.core.channel.v1.Channel) | | | +| `counterparty_version` | [string](#string) | | | +| `proof_init` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgChannelOpenTryResponse +MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. + + + + + + + + +### MsgRecvPacket +MsgRecvPacket receives incoming IBC packet + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | +| `proof_commitment` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgRecvPacketResponse +MsgRecvPacketResponse defines the Msg/RecvPacket response type. + + + + + + + + +### MsgTimeout +MsgTimeout receives timed-out packet + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | +| `proof_unreceived` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `next_sequence_recv` | [uint64](#uint64) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgTimeoutOnClose +MsgTimeoutOnClose timed-out packet upon counterparty channel closure. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `packet` | [Packet](#ibc.core.channel.v1.Packet) | | | +| `proof_unreceived` | [bytes](#bytes) | | | +| `proof_close` | [bytes](#bytes) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `next_sequence_recv` | [uint64](#uint64) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgTimeoutOnCloseResponse +MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. + + + + + + + + +### MsgTimeoutResponse +MsgTimeoutResponse defines the Msg/Timeout response type. + + + + + + + + + + + + + + +### Msg +Msg defines the ibc/channel Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `ChannelOpenInit` | [MsgChannelOpenInit](#ibc.core.channel.v1.MsgChannelOpenInit) | [MsgChannelOpenInitResponse](#ibc.core.channel.v1.MsgChannelOpenInitResponse) | ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. | | +| `ChannelOpenTry` | [MsgChannelOpenTry](#ibc.core.channel.v1.MsgChannelOpenTry) | [MsgChannelOpenTryResponse](#ibc.core.channel.v1.MsgChannelOpenTryResponse) | ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. | | +| `ChannelOpenAck` | [MsgChannelOpenAck](#ibc.core.channel.v1.MsgChannelOpenAck) | [MsgChannelOpenAckResponse](#ibc.core.channel.v1.MsgChannelOpenAckResponse) | ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. | | +| `ChannelOpenConfirm` | [MsgChannelOpenConfirm](#ibc.core.channel.v1.MsgChannelOpenConfirm) | [MsgChannelOpenConfirmResponse](#ibc.core.channel.v1.MsgChannelOpenConfirmResponse) | ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. | | +| `ChannelCloseInit` | [MsgChannelCloseInit](#ibc.core.channel.v1.MsgChannelCloseInit) | [MsgChannelCloseInitResponse](#ibc.core.channel.v1.MsgChannelCloseInitResponse) | ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. | | +| `ChannelCloseConfirm` | [MsgChannelCloseConfirm](#ibc.core.channel.v1.MsgChannelCloseConfirm) | [MsgChannelCloseConfirmResponse](#ibc.core.channel.v1.MsgChannelCloseConfirmResponse) | ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. | | +| `RecvPacket` | [MsgRecvPacket](#ibc.core.channel.v1.MsgRecvPacket) | [MsgRecvPacketResponse](#ibc.core.channel.v1.MsgRecvPacketResponse) | RecvPacket defines a rpc handler method for MsgRecvPacket. | | +| `Timeout` | [MsgTimeout](#ibc.core.channel.v1.MsgTimeout) | [MsgTimeoutResponse](#ibc.core.channel.v1.MsgTimeoutResponse) | Timeout defines a rpc handler method for MsgTimeout. | | +| `TimeoutOnClose` | [MsgTimeoutOnClose](#ibc.core.channel.v1.MsgTimeoutOnClose) | [MsgTimeoutOnCloseResponse](#ibc.core.channel.v1.MsgTimeoutOnCloseResponse) | TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. | | +| `Acknowledgement` | [MsgAcknowledgement](#ibc.core.channel.v1.MsgAcknowledgement) | [MsgAcknowledgementResponse](#ibc.core.channel.v1.MsgAcknowledgementResponse) | Acknowledgement defines a rpc handler method for MsgAcknowledgement. | | + + + + + + +

Top

+ +## ibc/core/client/v1/genesis.proto + + + + + +### GenesisMetadata +GenesisMetadata defines the genesis type for metadata that clients may return +with ExportMetadata + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [bytes](#bytes) | | store key of metadata without clientID-prefix | +| `value` | [bytes](#bytes) | | metadata value | + + + + + + + + +### GenesisState +GenesisState defines the ibc client submodule's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `clients` | [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | repeated | client states with their corresponding identifiers | +| `clients_consensus` | [ClientConsensusStates](#ibc.core.client.v1.ClientConsensusStates) | repeated | consensus states from each client | +| `clients_metadata` | [IdentifiedGenesisMetadata](#ibc.core.client.v1.IdentifiedGenesisMetadata) | repeated | metadata from each client | +| `params` | [Params](#ibc.core.client.v1.Params) | | | +| `create_localhost` | [bool](#bool) | | create localhost on initialization | +| `next_client_sequence` | [uint64](#uint64) | | the sequence for the next generated client identifier | + + + + + + + + +### IdentifiedGenesisMetadata +IdentifiedGenesisMetadata has the client metadata with the corresponding client id. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `client_metadata` | [GenesisMetadata](#ibc.core.client.v1.GenesisMetadata) | repeated | | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/core/client/v1/query.proto + + + + + +### QueryClientParamsRequest +QueryClientParamsRequest is the request type for the Query/ClientParams RPC method. + + + + + + + + +### QueryClientParamsResponse +QueryClientParamsResponse is the response type for the Query/ClientParams RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `params` | [Params](#ibc.core.client.v1.Params) | | params defines the parameters of the module. | + + + + + + + + +### QueryClientStateRequest +QueryClientStateRequest is the request type for the Query/ClientState RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client state unique identifier | + + + + + + + + +### QueryClientStateResponse +QueryClientStateResponse is the response type for the Query/ClientState RPC +method. Besides the client state, it includes a proof and the height from +which the proof was retrieved. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | client state associated with the request identifier | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryClientStatesRequest +QueryClientStatesRequest is the request type for the Query/ClientStates RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryClientStatesResponse +QueryClientStatesResponse is the response type for the Query/ClientStates RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_states` | [IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | repeated | list of stored ClientStates of the chain. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | + + + + + + + + +### QueryConsensusStateRequest +QueryConsensusStateRequest is the request type for the Query/ConsensusState +RPC method. Besides the consensus state, it includes a proof and the height +from which the proof was retrieved. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client identifier | +| `revision_number` | [uint64](#uint64) | | consensus state revision number | +| `revision_height` | [uint64](#uint64) | | consensus state revision height | +| `latest_height` | [bool](#bool) | | latest_height overrrides the height field and queries the latest stored ConsensusState | + + + + + + + + +### QueryConsensusStateResponse +QueryConsensusStateResponse is the response type for the Query/ConsensusState +RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the client identifier at the given height | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryConsensusStatesRequest +QueryConsensusStatesRequest is the request type for the Query/ConsensusStates +RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client identifier | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination request | + + + + + + + + +### QueryConsensusStatesResponse +QueryConsensusStatesResponse is the response type for the +Query/ConsensusStates RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `consensus_states` | [ConsensusStateWithHeight](#ibc.core.client.v1.ConsensusStateWithHeight) | repeated | consensus states associated with the identifier | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `ClientState` | [QueryClientStateRequest](#ibc.core.client.v1.QueryClientStateRequest) | [QueryClientStateResponse](#ibc.core.client.v1.QueryClientStateResponse) | ClientState queries an IBC light client. | GET|/ibc/core/client/v1beta1/client_states/{client_id}| +| `ClientStates` | [QueryClientStatesRequest](#ibc.core.client.v1.QueryClientStatesRequest) | [QueryClientStatesResponse](#ibc.core.client.v1.QueryClientStatesResponse) | ClientStates queries all the IBC light clients of a chain. | GET|/ibc/core/client/v1beta1/client_states| +| `ConsensusState` | [QueryConsensusStateRequest](#ibc.core.client.v1.QueryConsensusStateRequest) | [QueryConsensusStateResponse](#ibc.core.client.v1.QueryConsensusStateResponse) | ConsensusState queries a consensus state associated with a client state at a given height. | GET|/ibc/core/client/v1beta1/consensus_states/{client_id}/revision/{revision_number}/height/{revision_height}| +| `ConsensusStates` | [QueryConsensusStatesRequest](#ibc.core.client.v1.QueryConsensusStatesRequest) | [QueryConsensusStatesResponse](#ibc.core.client.v1.QueryConsensusStatesResponse) | ConsensusStates queries all the consensus state associated with a given client. | GET|/ibc/core/client/v1beta1/consensus_states/{client_id}| +| `ClientParams` | [QueryClientParamsRequest](#ibc.core.client.v1.QueryClientParamsRequest) | [QueryClientParamsResponse](#ibc.core.client.v1.QueryClientParamsResponse) | ClientParams queries all parameters of the ibc client. | GET|/ibc/client/v1beta1/params| + + + + + + +

Top

+ +## ibc/core/client/v1/tx.proto + + + + + +### MsgCreateClient +MsgCreateClient defines a message to create an IBC client + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | light client state | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the client that corresponds to a given height. | +| `signer` | [string](#string) | | signer address | + + + + + + + + +### MsgCreateClientResponse +MsgCreateClientResponse defines the Msg/CreateClient response type. + + + + + + + + +### MsgSubmitMisbehaviour +MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for +light client misbehaviour. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client unique identifier | +| `misbehaviour` | [google.protobuf.Any](#google.protobuf.Any) | | misbehaviour used for freezing the light client | +| `signer` | [string](#string) | | signer address | + + + + + + + + +### MsgSubmitMisbehaviourResponse +MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response type. + + + + + + + + +### MsgUpdateClient +MsgUpdateClient defines an sdk.Msg to update a IBC client state using +the given header. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client unique identifier | +| `header` | [google.protobuf.Any](#google.protobuf.Any) | | header to update the light client | +| `signer` | [string](#string) | | signer address | + + + + + + + + +### MsgUpdateClientResponse +MsgUpdateClientResponse defines the Msg/UpdateClient response type. + + + + + + + + +### MsgUpgradeClient +MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client state + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client unique identifier | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | upgraded client state | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | upgraded consensus state, only contains enough information to serve as a basis of trust in update logic | +| `proof_upgrade_client` | [bytes](#bytes) | | proof that old chain committed to new client | +| `proof_upgrade_consensus_state` | [bytes](#bytes) | | proof that old chain committed to new consensus state | +| `signer` | [string](#string) | | signer address | + + + + + + + + +### MsgUpgradeClientResponse +MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. + + + + + + + + + + + + + + +### Msg +Msg defines the ibc/client Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `CreateClient` | [MsgCreateClient](#ibc.core.client.v1.MsgCreateClient) | [MsgCreateClientResponse](#ibc.core.client.v1.MsgCreateClientResponse) | CreateClient defines a rpc handler method for MsgCreateClient. | | +| `UpdateClient` | [MsgUpdateClient](#ibc.core.client.v1.MsgUpdateClient) | [MsgUpdateClientResponse](#ibc.core.client.v1.MsgUpdateClientResponse) | UpdateClient defines a rpc handler method for MsgUpdateClient. | | +| `UpgradeClient` | [MsgUpgradeClient](#ibc.core.client.v1.MsgUpgradeClient) | [MsgUpgradeClientResponse](#ibc.core.client.v1.MsgUpgradeClientResponse) | UpgradeClient defines a rpc handler method for MsgUpgradeClient. | | +| `SubmitMisbehaviour` | [MsgSubmitMisbehaviour](#ibc.core.client.v1.MsgSubmitMisbehaviour) | [MsgSubmitMisbehaviourResponse](#ibc.core.client.v1.MsgSubmitMisbehaviourResponse) | SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. | | + + + + + + +

Top

+ +## ibc/core/commitment/v1/commitment.proto + + + + + +### MerklePath +MerklePath is the path used to verify commitment proofs, which can be an +arbitrary structured object (defined by a commitment type). +MerklePath is represented from root-to-leaf + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key_path` | [string](#string) | repeated | | + + + + + + + + +### MerklePrefix +MerklePrefix is merkle path prefixed to the key. +The constructed key from the Path and the key will be append(Path.KeyPath, +append(Path.KeyPrefix, key...)) + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key_prefix` | [bytes](#bytes) | | | + + + + + + + + +### MerkleProof +MerkleProof is a wrapper type over a chain of CommitmentProofs. +It demonstrates membership or non-membership for an element or set of +elements, verifiable in conjunction with a known commitment root. Proofs +should be succinct. +MerkleProofs are ordered from leaf-to-root + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `proofs` | [ics23.CommitmentProof](#ics23.CommitmentProof) | repeated | | + + + + + + + + +### MerkleRoot +MerkleRoot defines a merkle root hash. +In the Cosmos SDK, the AppHash of a block header becomes the root. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `hash` | [bytes](#bytes) | | | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/core/connection/v1/connection.proto + + + + + +### ClientPaths +ClientPaths define all the connection paths for a client state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `paths` | [string](#string) | repeated | list of connection paths | + + + + + + + + +### ConnectionEnd +ConnectionEnd defines a stateful object on a chain connected to another +separate one. +NOTE: there must only be 2 defined ConnectionEnds to establish +a connection between two chains. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client associated with this connection. | +| `versions` | [Version](#ibc.core.connection.v1.Version) | repeated | IBC version which can be utilised to determine encodings or protocols for channels or packets utilising this connection. | +| `state` | [State](#ibc.core.connection.v1.State) | | current state of the connection end. | +| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | counterparty chain associated with this connection. | +| `delay_period` | [uint64](#uint64) | | delay period that must pass before a consensus state can be used for packet-verification NOTE: delay period logic is only implemented by some clients. | + + + + + + + + +### ConnectionPaths +ConnectionPaths define all the connection paths for a given client state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client state unique identifier | +| `paths` | [string](#string) | repeated | list of connection paths | + + + + + + + + +### Counterparty +Counterparty defines the counterparty chain associated with a connection end. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | identifies the client on the counterparty chain associated with a given connection. | +| `connection_id` | [string](#string) | | identifies the connection end on the counterparty chain associated with a given connection. | +| `prefix` | [ibc.core.commitment.v1.MerklePrefix](#ibc.core.commitment.v1.MerklePrefix) | | commitment merkle prefix of the counterparty chain. | + + + + + + + + +### IdentifiedConnection +IdentifiedConnection defines a connection with additional connection +identifier field. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [string](#string) | | connection identifier. | +| `client_id` | [string](#string) | | client associated with this connection. | +| `versions` | [Version](#ibc.core.connection.v1.Version) | repeated | IBC version which can be utilised to determine encodings or protocols for channels or packets utilising this connection | +| `state` | [State](#ibc.core.connection.v1.State) | | current state of the connection end. | +| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | counterparty chain associated with this connection. | +| `delay_period` | [uint64](#uint64) | | delay period associated with this connection. | + + + + + + + + +### Version +Version defines the versioning scheme used to negotiate the IBC verison in +the connection handshake. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `identifier` | [string](#string) | | unique version identifier | +| `features` | [string](#string) | repeated | list of features compatible with the specified identifier | + + + + + + + + + + +### State +State defines if a connection is in one of the following states: +INIT, TRYOPEN, OPEN or UNINITIALIZED. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| STATE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | +| STATE_INIT | 1 | A connection end has just started the opening handshake. | +| STATE_TRYOPEN | 2 | A connection end has acknowledged the handshake step on the counterparty chain. | +| STATE_OPEN | 3 | A connection end has completed the handshake. | + + + + + + + + + + + +

Top

+ +## ibc/core/connection/v1/genesis.proto + + + + + +### GenesisState +GenesisState defines the ibc connection submodule's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connections` | [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) | repeated | | +| `client_connection_paths` | [ConnectionPaths](#ibc.core.connection.v1.ConnectionPaths) | repeated | | +| `next_connection_sequence` | [uint64](#uint64) | | the sequence for the next generated connection identifier | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/core/connection/v1/query.proto + + + + + +### QueryClientConnectionsRequest +QueryClientConnectionsRequest is the request type for the +Query/ClientConnections RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | client identifier associated with a connection | + + + + + + + + +### QueryClientConnectionsResponse +QueryClientConnectionsResponse is the response type for the +Query/ClientConnections RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_paths` | [string](#string) | repeated | slice of all the connection paths associated with a client. | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was generated | + + + + + + + + +### QueryConnectionClientStateRequest +QueryConnectionClientStateRequest is the request type for the +Query/ConnectionClientState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | connection identifier | + + + + + + + + +### QueryConnectionClientStateResponse +QueryConnectionClientStateResponse is the response type for the +Query/ConnectionClientState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `identified_client_state` | [ibc.core.client.v1.IdentifiedClientState](#ibc.core.client.v1.IdentifiedClientState) | | client state associated with the channel | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryConnectionConsensusStateRequest +QueryConnectionConsensusStateRequest is the request type for the +Query/ConnectionConsensusState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | connection identifier | +| `revision_number` | [uint64](#uint64) | | | +| `revision_height` | [uint64](#uint64) | | | + + + + + + + + +### QueryConnectionConsensusStateResponse +QueryConnectionConsensusStateResponse is the response type for the +Query/ConnectionConsensusState RPC method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | consensus state associated with the channel | +| `client_id` | [string](#string) | | client ID associated with the consensus state | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryConnectionRequest +QueryConnectionRequest is the request type for the Query/Connection RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | connection unique identifier | + + + + + + + + +### QueryConnectionResponse +QueryConnectionResponse is the response type for the Query/Connection RPC +method. Besides the connection end, it includes a proof and the height from +which the proof was retrieved. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection` | [ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) | | connection associated with the request identifier | +| `proof` | [bytes](#bytes) | | merkle proof of existence | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | height at which the proof was retrieved | + + + + + + + + +### QueryConnectionsRequest +QueryConnectionsRequest is the request type for the Query/Connections RPC +method + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | | + + + + + + + + +### QueryConnectionsResponse +QueryConnectionsResponse is the response type for the Query/Connections RPC +method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connections` | [IdentifiedConnection](#ibc.core.connection.v1.IdentifiedConnection) | repeated | list of stored connections of the chain. | +| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination response | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | query block height | + + + + + + + + + + + + + + +### Query +Query provides defines the gRPC querier service + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `Connection` | [QueryConnectionRequest](#ibc.core.connection.v1.QueryConnectionRequest) | [QueryConnectionResponse](#ibc.core.connection.v1.QueryConnectionResponse) | Connection queries an IBC connection end. | GET|/ibc/core/connection/v1beta1/connections/{connection_id}| +| `Connections` | [QueryConnectionsRequest](#ibc.core.connection.v1.QueryConnectionsRequest) | [QueryConnectionsResponse](#ibc.core.connection.v1.QueryConnectionsResponse) | Connections queries all the IBC connections of a chain. | GET|/ibc/core/connection/v1beta1/connections| +| `ClientConnections` | [QueryClientConnectionsRequest](#ibc.core.connection.v1.QueryClientConnectionsRequest) | [QueryClientConnectionsResponse](#ibc.core.connection.v1.QueryClientConnectionsResponse) | ClientConnections queries the connection paths associated with a client state. | GET|/ibc/core/connection/v1beta1/client_connections/{client_id}| +| `ConnectionClientState` | [QueryConnectionClientStateRequest](#ibc.core.connection.v1.QueryConnectionClientStateRequest) | [QueryConnectionClientStateResponse](#ibc.core.connection.v1.QueryConnectionClientStateResponse) | ConnectionClientState queries the client state associated with the connection. | GET|/ibc/core/connection/v1beta1/connections/{connection_id}/client_state| +| `ConnectionConsensusState` | [QueryConnectionConsensusStateRequest](#ibc.core.connection.v1.QueryConnectionConsensusStateRequest) | [QueryConnectionConsensusStateResponse](#ibc.core.connection.v1.QueryConnectionConsensusStateResponse) | ConnectionConsensusState queries the consensus state associated with the connection. | GET|/ibc/core/connection/v1beta1/connections/{connection_id}/consensus_state/revision/{revision_number}/height/{revision_height}| + + + + + + +

Top

+ +## ibc/core/connection/v1/tx.proto + + + + + +### MsgConnectionOpenAck +MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to +acknowledge the change of connection state to TRYOPEN on Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | | +| `counterparty_connection_id` | [string](#string) | | | +| `version` | [Version](#ibc.core.connection.v1.Version) | | | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `proof_try` | [bytes](#bytes) | | proof of the initialization the connection on Chain B: `UNITIALIZED -> TRYOPEN` | +| `proof_client` | [bytes](#bytes) | | proof of client state included in message | +| `proof_consensus` | [bytes](#bytes) | | proof of client consensus state | +| `consensus_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgConnectionOpenAckResponse +MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. + + + + + + + + +### MsgConnectionOpenConfirm +MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to +acknowledge the change of connection state to OPEN on Chain A. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `connection_id` | [string](#string) | | | +| `proof_ack` | [bytes](#bytes) | | proof for the change of the connection state on Chain A: `INIT -> OPEN` | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgConnectionOpenConfirmResponse +MsgConnectionOpenConfirmResponse defines the Msg/ConnectionOpenConfirm response type. + + + + + + + + +### MsgConnectionOpenInit +MsgConnectionOpenInit defines the msg sent by an account on Chain A to +initialize a connection with Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | | +| `version` | [Version](#ibc.core.connection.v1.Version) | | | +| `delay_period` | [uint64](#uint64) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgConnectionOpenInitResponse +MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response type. + + + + + + + + +### MsgConnectionOpenTry +MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a +connection on Chain B. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `previous_connection_id` | [string](#string) | | in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier of the previous connection in state INIT | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `counterparty` | [Counterparty](#ibc.core.connection.v1.Counterparty) | | | +| `delay_period` | [uint64](#uint64) | | | +| `counterparty_versions` | [Version](#ibc.core.connection.v1.Version) | repeated | | +| `proof_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `proof_init` | [bytes](#bytes) | | proof of the initialization the connection on Chain A: `UNITIALIZED -> INIT` | +| `proof_client` | [bytes](#bytes) | | proof of client state included in message | +| `proof_consensus` | [bytes](#bytes) | | proof of client consensus state | +| `consensus_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `signer` | [string](#string) | | | + + + + + + + + +### MsgConnectionOpenTryResponse +MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. + + + + + + + + + + + + + + +### Msg +Msg defines the ibc/connection Msg service. + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +| `ConnectionOpenInit` | [MsgConnectionOpenInit](#ibc.core.connection.v1.MsgConnectionOpenInit) | [MsgConnectionOpenInitResponse](#ibc.core.connection.v1.MsgConnectionOpenInitResponse) | ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. | | +| `ConnectionOpenTry` | [MsgConnectionOpenTry](#ibc.core.connection.v1.MsgConnectionOpenTry) | [MsgConnectionOpenTryResponse](#ibc.core.connection.v1.MsgConnectionOpenTryResponse) | ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. | | +| `ConnectionOpenAck` | [MsgConnectionOpenAck](#ibc.core.connection.v1.MsgConnectionOpenAck) | [MsgConnectionOpenAckResponse](#ibc.core.connection.v1.MsgConnectionOpenAckResponse) | ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. | | +| `ConnectionOpenConfirm` | [MsgConnectionOpenConfirm](#ibc.core.connection.v1.MsgConnectionOpenConfirm) | [MsgConnectionOpenConfirmResponse](#ibc.core.connection.v1.MsgConnectionOpenConfirmResponse) | ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. | | + + + + + + +

Top

+ +## ibc/core/types/v1/genesis.proto + + + + + +### GenesisState +GenesisState defines the ibc module's genesis state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_genesis` | [ibc.core.client.v1.GenesisState](#ibc.core.client.v1.GenesisState) | | ICS002 - Clients genesis state | +| `connection_genesis` | [ibc.core.connection.v1.GenesisState](#ibc.core.connection.v1.GenesisState) | | ICS003 - Connections genesis state | +| `channel_genesis` | [ibc.core.channel.v1.GenesisState](#ibc.core.channel.v1.GenesisState) | | ICS004 - Channel genesis state | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/lightclients/localhost/v1/localhost.proto + + + + + +### ClientState +ClientState defines a loopback (localhost) client. It requires (read-only) +access to keys outside the client prefix. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `chain_id` | [string](#string) | | self chain ID | +| `height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | self latest block height | + + + + + + + + + + + + + + + + +

Top

+ +## ibc/lightclients/solomachine/v1/solomachine.proto + + + + + +### ChannelStateData +ChannelStateData returns the SignBytes data for channel state +verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `channel` | [ibc.core.channel.v1.Channel](#ibc.core.channel.v1.Channel) | | | + + + + + + + + +### ClientState +ClientState defines a solo machine client that tracks the current consensus +state and if the client is frozen. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | latest sequence of the client state | +| `frozen_sequence` | [uint64](#uint64) | | frozen sequence of the solo machine | +| `consensus_state` | [ConsensusState](#ibc.lightclients.solomachine.v1.ConsensusState) | | | +| `allow_update_after_proposal` | [bool](#bool) | | when set to true, will allow governance to update a solo machine client. The client will be unfrozen if it is frozen. | + + + + + + + + +### ClientStateData +ClientStateData returns the SignBytes data for client state verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `client_state` | [google.protobuf.Any](#google.protobuf.Any) | | | + + + + + + + + +### ConnectionStateData +ConnectionStateData returns the SignBytes data for connection state +verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `connection` | [ibc.core.connection.v1.ConnectionEnd](#ibc.core.connection.v1.ConnectionEnd) | | | + + + + + + + + +### ConsensusState +ConsensusState defines a solo machine consensus state. The sequence of a consensus state +is contained in the "height" key used in storing the consensus state. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `public_key` | [google.protobuf.Any](#google.protobuf.Any) | | public key of the solo machine | +| `diversifier` | [string](#string) | | diversifier allows the same public key to be re-used across different solo machine clients (potentially on different chains) without being considered misbehaviour. | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + +### ConsensusStateData +ConsensusStateData returns the SignBytes data for consensus state +verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `consensus_state` | [google.protobuf.Any](#google.protobuf.Any) | | | + + + + + + + + +### Header +Header defines a solo machine consensus header + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | sequence to update solo machine public key at | +| `timestamp` | [uint64](#uint64) | | | +| `signature` | [bytes](#bytes) | | | +| `new_public_key` | [google.protobuf.Any](#google.protobuf.Any) | | | +| `new_diversifier` | [string](#string) | | | + + + + + + + + +### HeaderData +HeaderData returns the SignBytes data for update verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `new_pub_key` | [google.protobuf.Any](#google.protobuf.Any) | | header public key | +| `new_diversifier` | [string](#string) | | header diversifier | + + + + + + + + +### Misbehaviour +Misbehaviour defines misbehaviour for a solo machine which consists +of a sequence and two signatures over different messages at that sequence. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `sequence` | [uint64](#uint64) | | | +| `signature_one` | [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) | | | +| `signature_two` | [SignatureAndData](#ibc.lightclients.solomachine.v1.SignatureAndData) | | | + + + + + + + + +### NextSequenceRecvData +NextSequenceRecvData returns the SignBytes data for verification of the next +sequence to be received. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `next_seq_recv` | [uint64](#uint64) | | | + + + + + + + + +### PacketAcknowledgementData +PacketAcknowledgementData returns the SignBytes data for acknowledgement +verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `acknowledgement` | [bytes](#bytes) | | | + + + + + + + + +### PacketCommitmentData +PacketCommitmentData returns the SignBytes data for packet commitment +verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | +| `commitment` | [bytes](#bytes) | | | + + + + + + + + +### PacketReceiptAbsenceData +PacketReceiptAbsenceData returns the SignBytes data for +packet receipt absence verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `path` | [bytes](#bytes) | | | + + + + + + + + +### SignBytes +SignBytes defines the signed bytes used for signature verification. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `sequence` | [uint64](#uint64) | | | +| `timestamp` | [uint64](#uint64) | | | +| `diversifier` | [string](#string) | | | +| `data_type` | [DataType](#ibc.lightclients.solomachine.v1.DataType) | | type of the data used | +| `data` | [bytes](#bytes) | | marshaled data | + + + + + + + + +### SignatureAndData +SignatureAndData contains a signature and the data signed over to create that +signature. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signature` | [bytes](#bytes) | | | +| `data_type` | [DataType](#ibc.lightclients.solomachine.v1.DataType) | | | +| `data` | [bytes](#bytes) | | | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + +### TimestampedSignatureData +TimestampedSignatureData contains the signature data and the timestamp of the +signature. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signature_data` | [bytes](#bytes) | | | +| `timestamp` | [uint64](#uint64) | | | + + + + + + + + + + +### DataType +DataType defines the type of solo machine proof being created. This is done to preserve uniqueness of different +data sign byte encodings. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| DATA_TYPE_UNINITIALIZED_UNSPECIFIED | 0 | Default State | +| DATA_TYPE_CLIENT_STATE | 1 | Data type for client state verification | +| DATA_TYPE_CONSENSUS_STATE | 2 | Data type for consensus state verification | +| DATA_TYPE_CONNECTION_STATE | 3 | Data type for connection state verification | +| DATA_TYPE_CHANNEL_STATE | 4 | Data type for channel state verification | +| DATA_TYPE_PACKET_COMMITMENT | 5 | Data type for packet commitment verification | +| DATA_TYPE_PACKET_ACKNOWLEDGEMENT | 6 | Data type for packet acknowledgement verification | +| DATA_TYPE_PACKET_RECEIPT_ABSENCE | 7 | Data type for packet receipt absence verification | +| DATA_TYPE_NEXT_SEQUENCE_RECV | 8 | Data type for next sequence recv verification | +| DATA_TYPE_HEADER | 9 | Data type for header verification | + + + + + + + + + + + +

Top

+ +## ibc/lightclients/tendermint/v1/tendermint.proto + + + + + +### ClientState +ClientState from Tendermint tracks the current validator set, latest height, +and a possible frozen height. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `chain_id` | [string](#string) | | | +| `trust_level` | [Fraction](#ibc.lightclients.tendermint.v1.Fraction) | | | +| `trusting_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | duration of the period since the LastestTimestamp during which the submitted headers are valid for upgrade | +| `unbonding_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | duration of the staking unbonding period | +| `max_clock_drift` | [google.protobuf.Duration](#google.protobuf.Duration) | | defines how much new (untrusted) header's Time can drift into the future. | +| `frozen_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Block height when the client was frozen due to a misbehaviour | +| `latest_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | Latest height the client was updated to | +| `proof_specs` | [ics23.ProofSpec](#ics23.ProofSpec) | repeated | Proof specifications used in verifying counterparty state | +| `upgrade_path` | [string](#string) | repeated | Path at which next upgraded client will be committed. Each element corresponds to the key for a single CommitmentProof in the chained proof. NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}` | +| `allow_update_after_expiry` | [bool](#bool) | | This flag, when set to true, will allow governance to recover a client which has expired | +| `allow_update_after_misbehaviour` | [bool](#bool) | | This flag, when set to true, will allow governance to unfreeze a client whose chain has experienced a misbehaviour event | + + + + + + + + +### ConsensusState +ConsensusState defines the consensus state from Tendermint. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `timestamp` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | timestamp that corresponds to the block height in which the ConsensusState was stored. | +| `root` | [ibc.core.commitment.v1.MerkleRoot](#ibc.core.commitment.v1.MerkleRoot) | | commitment root (i.e app hash) | +| `next_validators_hash` | [bytes](#bytes) | | | + + + + + + + + +### Fraction +Fraction defines the protobuf message type for tmmath.Fraction that only supports positive values. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `numerator` | [uint64](#uint64) | | | +| `denominator` | [uint64](#uint64) | | | + + + + + + + + +### Header +Header defines the Tendermint client consensus Header. +It encapsulates all the information necessary to update from a trusted +Tendermint ConsensusState. The inclusion of TrustedHeight and +TrustedValidators allows this update to process correctly, so long as the +ConsensusState for the TrustedHeight exists, this removes race conditions +among relayers The SignedHeader and ValidatorSet are the new untrusted update +fields for the client. The TrustedHeight is the height of a stored +ConsensusState on the client that will be used to verify the new untrusted +header. The Trusted ConsensusState must be within the unbonding period of +current time in order to correctly verify, and the TrustedValidators must +hash to TrustedConsensusState.NextValidatorsHash since that is the last +trusted validator set at the TrustedHeight. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `signed_header` | [tendermint.types.SignedHeader](#tendermint.types.SignedHeader) | | | +| `validator_set` | [tendermint.types.ValidatorSet](#tendermint.types.ValidatorSet) | | | +| `trusted_height` | [ibc.core.client.v1.Height](#ibc.core.client.v1.Height) | | | +| `trusted_validators` | [tendermint.types.ValidatorSet](#tendermint.types.ValidatorSet) | | | + + + + + + + + +### Misbehaviour +Misbehaviour is a wrapper over two conflicting Headers +that implements Misbehaviour interface expected by ICS-02 + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `client_id` | [string](#string) | | | +| `header_1` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | +| `header_2` | [Header](#ibc.lightclients.tendermint.v1.Header) | | | + + + + + + + + + + + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/docs/core/runtx_middleware.md b/docs/core/runtx_middleware.md new file mode 100644 index 000000000000..9c978fbbe00b --- /dev/null +++ b/docs/core/runtx_middleware.md @@ -0,0 +1,73 @@ + + +# RunTx recovery middleware + +`BaseApp.runTx()` function handles Golang panics that might occur during transactions execution, for example, keeper has faced an invalid state and paniced. +Depending on the panic type different handler is used, for instance the default one prints an error log message. +Recovery middleware is used to add custom panic recovery for SDK application developers. + +More context could be found in the corresponding [ADR-022](../architecture/adr-022-custom-panic-handling.md). + +Implementation could be found in the [recovery.go](../../baseapp/recovery.go) file. + +## Interface + +```go +type RecoveryHandler func(recoveryObj interface{}) error +``` + +`recoveryObj` is a return value for `recover()` function from the `buildin` Golang package. + +**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; + +## Custom RecoveryHandler register + +`BaseApp.AddRunTxRecoveryHandler(handlers ...RecoveryHandler)` + +BaseApp method adds recovery middleware to the default recovery chain. + +## Example + +Lets assume we want to emit the "Consensus failure" chain state if some particular error occurred. + +We have a module keeper that panics: + +```go +func (k FooKeeper) Do(obj interface{}) { + if obj == nil { + // that shouldn't happen, we need to crash the app + err := sdkErrors.Wrap(fooTypes.InternalError, "obj is nil") + panic(err) + } +} +``` + +By default that panic would be recovered and an error message will be printed to log. To override that behaviour we should register a custom RecoveryHandler: + +```go +// SDK application constructor +customHandler := func(recoveryObj interface{}) error { + err, ok := recoveryObj.(error) + if !ok { + return nil + } + + if fooTypes.InternalError.Is(err) { + panic(fmt.Errorf("FooKeeper did panic with error: %w", err)) + } + + return nil +} + +baseApp := baseapp.NewBaseApp(...) +baseApp.AddRunTxRecoveryHandler(customHandler) +``` + +## Next {hide} + +Learn about the [IBC](./../ibc/README.md) protocol {hide} diff --git a/docs/core/simulation.md b/docs/core/simulation.md new file mode 100644 index 000000000000..9b838b40b526 --- /dev/null +++ b/docs/core/simulation.md @@ -0,0 +1,101 @@ + + +# 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/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. + +## Goals + +The blockchain simulator tests how the blockchain application would behave under +real life circumstances by generating and sending randomized messages. +The goal of this is to detect and debug failures that could halt a live chain, +by providing logs and statistics about the operations run by the simulator as +well as exporting the latest application state when a failure was found. + +Its main difference with integration testing is that the simulator app allows +you to pass parameters to customize the chain that's being simulated. +This comes in handy when trying to reproduce bugs that were generated in the +provided operations (randomized or not). + +## Simulation commands + +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. + +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/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**. +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. +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/v0.40.0/x/simulation/params.go#L44-L52). + +::: tip +These modes are not mutually exclusive. So you can for example run a randomly +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/v0.40.0/Makefile#L251-L287). + +```bash + $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ + -run=TestApp \ + ... + -v -timeout 24h +``` + +## Debugging Tips + +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/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 diff --git a/docs/core/store.md b/docs/core/store.md index 35b3d0f2a2e1..a469b77ebfd3 100644 --- a/docs/core/store.md +++ b/docs/core/store.md @@ -1,17 +1,18 @@ # Store -## Pre-requisite Readings {hide} +A store is a data structure that holds the state of the application. {synopsis} + +### Pre-requisite Readings - [Anatomy of an SDK application](../basics/app-anatomy.md) {prereq} ## Introduction to SDK Stores -The Cosmos SDK comes with a large set of stores to persist the state of applications. By default, the main store of SDK applications is a `multistore`, i.e. a store of stores. Developers can add any number of key-value stores to the multistore, depending on their application needs. The multistore exists to support the modularity of the Cosmos SDK, as it lets each module declare and manage their own subset of the state. Key-value stores in the multistore can only be accessed with a specific capability `key`, which is typically held in the [`keeper`](../building-modules/keeper.md) of the module that declared the store. +The Cosmos SDK comes with a large set of stores to persist the state of applications. By default, the main store of SDK applications is a `multistore`, i.e. a store of stores. Developers can add any number of key-value stores to the multistore, depending on their application needs. The multistore exists to support the modularity of the Cosmos SDK, as it lets each module declare and manage their own subset of the state. Key-value stores in the multistore can only be accessed with a specific capability `key`, which is typically held in the [`keeper`](../building-modules/keeper.md) of the module that declared the store. ``` +-----------------------------------------------------+ @@ -55,29 +56,29 @@ The Cosmos SDK comes with a large set of stores to persist the state of applicat ### Store Interface -At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and implements a `GetStoreType()` method: +At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and has a `GetStoreType()` method: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L12-L15 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L15-L18 -The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that specifies cache-wrapping and `Write` methods: +The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that implements store read caching and write branching through `Write` method: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L217-L238 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L240-L264 -Cache-wrapping is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A cache-wrapper creates a light snapshot of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. If a state-transition sequence is performed without issue, the cached store can be comitted to the underlying store at the end of the sequence. +Branching and cache is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A storage branch creates an isolated, ephemeral branch of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. Read more about it in [context](./context.md#Store-branching) ### Commit Store A commit store is a store that has the ability to commit changes made to the underlying tree or db. The Cosmos SDK differentiates simple stores from commit stores by extending the basic store interfaces with a `Committer`: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L24-L28 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L29-L33 The `Committer` is an interface that defines methods to persist changes to disk: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L17-L22 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L20-L27 -The `CommitID` is a deterministic commit of the state tree. Its hash is returned to the underlying consensus engine and stored in the block header. Note that commit store interfaces exist for various purposes, one of which is to make sure not every object can commit the store. As part of the [object-capabilities model](./ocap.md) of the Cosmos SDK, only `baseapp` should have the ability to commit stores. For example, this is the reason why the `ctx.KVStore()` method by which modules typically access stores returns a `KVStore` and not a `CommitKVStore`. +The `CommitID` is a deterministic commit of the state tree. Its hash is returned to the underlying consensus engine and stored in the block header. Note that commit store interfaces exist for various purposes, one of which is to make sure not every object can commit the store. As part of the [object-capabilities model](./ocap.md) of the Cosmos SDK, only `baseapp` should have the ability to commit stores. For example, this is the reason why the `ctx.KVStore()` method by which modules typically access stores returns a `KVStore` and not a `CommitKVStore`. -The Cosmos SDK comes with many types of stores, the most used being [`CommitMultiStore`](#multistore), [`KVStore`](#kvstore) and [`GasKv` store](#gaskv-store). [Other types of stores](#other-stores) include `Transient` and `TraceKV` stores. +The Cosmos SDK comes with many types of stores, the most used being [`CommitMultiStore`](#multistore), [`KVStore`](#kvstore) and [`GasKv` store](#gaskv-store). [Other types of stores](#other-stores) include `Transient` and `TraceKV` stores. ## Multistore @@ -85,78 +86,65 @@ The Cosmos SDK comes with many types of stores, the most used being [`CommitMult Each Cosmos SDK application holds a multistore at its root to persist its state. The multistore is a store of `KVStores` that follows the `Multistore` interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L83-L112 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L104-L133 -If tracing is enabled, then cache-wrapping the multistore will wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store) before caching them. +If tracing is enabled, then branching the multistore will firstly wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store). ### CommitMultiStore The main type of `Multistore` used in the Cosmos SDK is `CommitMultiStore`, which is an extension of the `Multistore` interface: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L120-L158 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L141-L184 -As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface. +As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/rootmulti/store.go#L27-L43 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/rootmulti/store.go#L43-L61 -The `rootMulti.Store` is a base-layer multistore built around a `db` on top of which multiple `KVStores` can be mounted, and is the default multistore store used in [`baseapp`](./baseapp.md). +The `rootMulti.Store` is a base-layer multistore built around a `db` on top of which multiple `KVStores` can be mounted, and is the default multistore store used in [`baseapp`](./baseapp.md). ### CacheMultiStore -Whenever the `rootMulti.Store` needs to be cached-wrapped, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/cachemulti/store.go) is used. +Whenever the `rootMulti.Store` needs to be branched, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/store/cachemulti/store.go) is used. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/cachemulti/store.go#L17-L28 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/cachemulti/store.go#L17-L28 -`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. +`cachemulti.Store` branches all substores (creates a virtual store for each substore) in its constructor and hold them in `Store.stores`. Moreover caches all read queries. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. ## Base-layer KVStores ### `KVStore` and `CommitKVStore` Interfaces -A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer. - -Individual `KVStore`s are used by modules to manage a subset of the global state. `KVStores` can be accessed by objects that hold a specific key. This `key` should only be exposed to the [`keeper`](../building-modules/keeper.md) of the module that defines the store. +A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer. -`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../basics/app-anatomy.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store. +Individual `KVStore`s are used by modules to manage a subset of the global state. `KVStores` can be accessed by objects that hold a specific key. This `key` should only be exposed to the [`keeper`](../building-modules/keeper.md) of the module that defines the store. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L163-L193 +`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../basics/app-anatomy.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store. -Apart from the traditional `Get` and `Set` methods, a `KVStore` is expected to implement an `Iterator()` method which returns an `Iterator` object. The `Iterator()` method is used to iterate over a domain of keys, typically keys that share a common prefix. Here is a common pattern of using an `Iterator` that might be found in a module's `keeper`: ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/store.go#L189-L219 -```go -store := ctx.KVStore(keeper.storeKey) -iterator := sdk.KVStorePrefixIterator(store, prefix) // proxy for store.Iterator +Apart from the traditional `Get` and `Set` methods, a `KVStore` must provide an `Iterator(start, end)` method which returns an `Iterator` object. It is used to iterate over a range of keys, typically keys that share a common prefix. Below is an example from the bank's module keeper, used to iterate over all account balances: -defer iterator.Close() -for ; iterator.Valid(); iterator.Next() { - var object types.Object - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &object) - - if cb(object) { - break - } -} -``` ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/keeper/view.go#L115-L134 ### `IAVL` Store The default implementation of `KVStore` and `CommitKVStore` used in `baseapp` is the `iavl.Store`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/iavl/store.go#L32-L47 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/iavl/store.go#L37-L40 - `iavl` stores are based around an [IAVL Tree](https://github.com/tendermint/iavl), a self-balancing binary tree which guarantees that: +`iavl` stores are based around an [IAVL Tree](https://github.com/tendermint/iavl), a self-balancing binary tree which guarantees that: - `Get` and `Set` operations are O(log n), where n is the number of elements in the tree. - Iteration efficiently returns the sorted elements within the range. -- Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings). +- Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings). -The documentation on the IAVL Tree is located [here](https://github.com/tendermint/iavl/blob/f9d4b446a226948ed19286354f0d433a887cc4a3/docs/overview.md). +The documentation on the IAVL Tree is located [here](https://github.com/cosmos/iavl/blob/v0.15.0-rc5/docs/overview.md). ### `DbAdapter` Store `dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/dbadapter/store.go#L13-L16 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/dbadapter/store.go#L13-L16 `dbadapter.Store` embeds `dbm.DB`, meaning most of the `KVStore` interface functions are implemented. The other functions (mostly miscellaneous) are manually implemented. This store is primarily used within [Transient Stores](#transient-stores) @@ -164,17 +152,17 @@ The documentation on the IAVL Tree is located [here](https://github.com/tendermi `Transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/transient/store.go#L14-L17 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/transient/store.go#L13-L16 `Transient.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, a new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. -This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block). +This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block). -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/params/subspace/subspace.go#L24-L32 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/params/types/subspace.go#L20-L30 Transient stores are typically accessed via the [`context`](./context.md) via the `TransientStore()` method: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/context.go#L215-L218 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/context.go#L232-L235 ## KVStore Wrappers @@ -182,45 +170,45 @@ Transient stores are typically accessed via the [`context`](./context.md) via th `cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/cachekv/store.go#L26-L33 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/cachekv/store.go#L27-L34 -This is the type used whenever an IAVL Store needs to be cache-wrapped (typically when setting value that might be reverted later). +This is the type used whenever an IAVL Store needs to be branched to create an isolated store (typically when we need to mutate a state that might be reverted later). #### `Get` -`Store.Get()` checks `Store.cache` first in order to find if there is any cached value associated with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, sets the key-value pair to the `Store.cache`, and returns it. +`Store.Get()` firstly checks if `Store.cache` has an associated value with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, caches the result in `Store.cache`, and returns it. #### `Set` -`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` cache new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. +`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` caches a new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. #### `Iterator` -`Store.Iterator()` have to traverse on both caches items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. +`Store.Iterator()` have to traverse on both cached items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. ### `GasKv` Store -Cosmos SDK applications use [`gas`](../basics/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications. +Cosmos SDK applications use [`gas`](../basics/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/gaskv/store.go#L11-L17 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/gaskv/store.go#L13-L19 When methods of the parent `KVStore` are called, `GasKv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L141-L150 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/gas.go#L153-L162 By default, all `KVStores` are wrapped in `GasKv.Stores` when retrieved. This is done in the `KVStore()` method of the [`context`](./context.md): -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/context.go#L210-L213 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/context.go#L227-L230 In this case, the default gas configuration is used: -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L152-L163 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/types/gas.go#L164-L175 ### `TraceKv` Store -`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`. +`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/tracekv/store.go#L20-L43 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/tracekv/store.go#L20-L43 When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. `traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. @@ -228,7 +216,7 @@ When each `KVStore` methods are called, `tracekv.Store` automatically logs `trac `prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/prefix/store.go#L17-L20 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/store/prefix/store.go#L15-L21 When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`. @@ -236,4 +224,4 @@ When `Store.Iterator()` is called, it does not simply prefix the `Store.prefix`, ## Next {hide} -Learn about [encoding](./encoding.md) {hide} \ No newline at end of file +Learn about [encoding](./encoding.md) {hide} diff --git a/docs/core/telemetry.md b/docs/core/telemetry.md new file mode 100644 index 000000000000..9e434eef2bec --- /dev/null +++ b/docs/core/telemetry.md @@ -0,0 +1,145 @@ + + +# Telemetry + +Gather relevant insights about your application and modules with custom metrics and telemetry. {synopsis} + +The Cosmos SDK enables operators and developers to gain insight into the performance and behavior of +their application through the use of the `telemetry` package. The Cosmos SDK currently supports +enabling in-memory and prometheus as telemetry sinks. This allows the ability to query for and scrape +metrics from a single exposed API endpoint -- `/metrics?format={text|prometheus}`, the default being +`text`. + +If telemetry is enabled via configuration, a single global metrics collector is registered via the +[go-metrics](https://github.com/armon/go-metrics) library. This allows emitting and collecting +metrics through simple API calls. + +Example: + +```go +func EndBlocker(ctx sdk.Context, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + + // ... +} +``` + +Developers may use the `telemetry` package directly, which provides wrappers around metric APIs +that include adding useful labels, or they must use the `go-metrics` library directly. It is preferable +to add as much context and adequate dimensionality to metrics as possible, so the `telemetry` package +is advised. Regardless of the package or method used, the Cosmos SDK supports the following metrics +types: + +* gauges +* summaries +* counters + +## Labels + +Certain components of modules will have their name automatically added as a label (e.g. `BeginBlock`). +Operators may also supply the application with a global set of labels that will be applied to all +metrics emitted using the `telemetry` package (e.g. chain-id). Global labels are supplied as a list +of [name, value] tuples. + +Example: + +```toml +global-labels = [ + ["chain_id", "chain-OfXo4V"], +] +``` + +## Cardinality + +Cardinality is key, specifically label and key cardinality. Cardinality is how many unique values of +something there are. So there is naturally a tradeoff between granularity and how much stress is put +on the telemetry sink in terms of indexing, scrape, and query performance. + +Developers should take care to support metrics with enough dimensionality and granularity to be +useful, but not increase the cardinality beyond the sink's limits. A general rule of thumb is to not +exceed a cardinality of 10. + +Consider the following examples with enough granularity and adequate cardinality: + +* begin/end blocker time +* tx gas used +* block gas used +* amount of tokens minted +* amount of accounts created + +The following examples expose too much cardinality and may not even prove to be useful: + +* transfers between accounts with amount +* voting/deposit amount from unique addresses + +## Supported Metrics + +| Metric | Description | Unit | Type | +|:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------| +| `tx_count` | Total number of txs processed via `DeliverTx` | tx | counter | +| `tx_successful` | Total number of successful txs processed via `DeliverTx` | tx | counter | +| `tx_failed` | Total number of failed txs processed via `DeliverTx` | tx | counter | +| `tx_gas_used` | The total amount of gas used by a tx | gas | gauge | +| `tx_gas_wanted` | The total amount of gas requested by a tx | gas | gauge | +| `tx_msg_send` | The total amount of tokens sent in a `MsgSend` (per denom) | token | gauge | +| `tx_msg_withdraw_reward` | The total amount of tokens withdrawn in a `MsgWithdrawDelegatorReward` (per denom) | token | gauge | +| `tx_msg_withdraw_commission` | The total amount of tokens withdrawn in a `MsgWithdrawValidatorCommission` (per denom) | token | gauge | +| `tx_msg_delegate` | The total amount of tokens delegated in a `MsgDelegate` | token | gauge | +| `tx_msg_begin_unbonding` | The total amount of tokens undelegated in a `MsgUndelegate` | token | gauge | +| `tx_msg_begin_begin_redelegate` | The total amount of tokens redelegated in a `MsgBeginRedelegate` | token | gauge | +| `tx_msg_ibc_transfer` | The total amount of tokens transferred via IBC in a `MsgTransfer` (source or sink chain) | token | gauge | +| `ibc_transfer_packet_receive` | The total amount of tokens received in a `FungibleTokenPacketData` (source or sink chain) | token | gauge | +| `new_account` | Total number of new accounts created | account | counter | +| `gov_proposal` | Total number of governance proposals | proposal | counter | +| `gov_vote` | Total number of governance votes for a proposal | vote | counter | +| `gov_deposit` | Total number of governance deposits for a proposal | deposit | counter | +| `staking_delegate` | Total number of delegations | delegation | counter | +| `staking_undelegate` | Total number of undelegations | undelegation | counter | +| `staking_redelegate` | Total number of redelegations | redelegation | counter | +| `ibc_transfer_send` | Total number of IBC transfers sent from a chain (source or sink) | transfer | counter | +| `ibc_transfer_receive` | Total number of IBC transfers received to a chain (source or sink) | transfer | counter | +| `ibc_client_create` | Total number of clients created | create | counter | +| `ibc_client_update` | Total number of client updates | update | counter | +| `ibc_client_upgrade` | Total number of client upgrades | upgrade | counter | +| `ibc_client_misbehaviour` | Total number of client misbehaviours | misbehaviour | counter | +| `ibc_connection_open-init` | Total number of connection `OpenInit` handshakes | handshake | counter | +| `ibc_connection_open-try` | Total number of connection `OpenTry` handshakes | handshake | counter | +| `ibc_connection_open-ack` | Total number of connection `OpenAck` handshakes | handshake | counter | +| `ibc_connection_open-confirm` | Total number of connection `OpenConfirm` handshakes | handshake | counter | +| `ibc_channel_open-init` | Total number of channel `OpenInit` handshakes | handshake | counter | +| `ibc_channel_open-try` | Total number of channel `OpenTry` handshakes | handshake | counter | +| `ibc_channel_open-ack` | Total number of channel `OpenAck` handshakes | handshake | counter | +| `ibc_channel_open-confirm` | Total number of channel `OpenConfirm` handshakes | handshake | counter | +| `ibc_channel_close-init` | Total number of channel `CloseInit` handshakes | handshake | counter | +| `ibc_channel_close-confirm` | Total number of channel `CloseConfirm` handshakes | handshake | counter | +| `tx_msg_ibc_recv_packet` | Total number of IBC packets received | packet | counter | +| `tx_msg_ibc_acknowledge_packet` | Total number of IBC packets acknowledged | acknowledgement | counter | +| `ibc_timeout_packet` | Total number of IBC timeout packets | timeout | counter | +| `abci_check_tx` | Duration of ABCI `CheckTx` | ms | summary | +| `abci_deliver_tx` | Duration of ABCI `DeliverTx` | ms | summary | +| `abci_commit` | Duration of ABCI `Commit` | ms | summary | +| `abci_query` | Duration of ABCI `Query` | ms | summary | +| `abci_begin_block` | Duration of ABCI `BeginBlock` | ms | summary | +| `abci_end_block` | Duration of ABCI `EndBlock` | ms | summary | +| `begin_blocker` | Duration of `BeginBlock` for a given module | ms | summary | +| `end_blocker` | Duration of `EndBlock` for a given module | ms | summary | +| `store_iavl_get` | Duration of an IAVL `Store#Get` call | ms | summary | +| `store_iavl_set` | Duration of an IAVL `Store#Set` call | ms | summary | +| `store_iavl_has` | Duration of an IAVL `Store#Has` call | ms | summary | +| `store_iavl_delete` | Duration of an IAVL `Store#Delete` call | ms | summary | +| `store_iavl_commit` | Duration of an IAVL `Store#Commit` call | ms | summary | +| `store_iavl_query` | Duration of an IAVL `Store#Query` call | ms | summary | +| `store_gaskv_get` | Duration of a GasKV `Store#Get` call | ms | summary | +| `store_gaskv_set` | Duration of a GasKV `Store#Set` call | ms | summary | +| `store_gaskv_has` | Duration of a GasKV `Store#Has` call | ms | summary | +| `store_gaskv_delete` | Duration of a GasKV `Store#Delete` call | ms | summary | +| `store_cachekv_get` | Duration of a CacheKV `Store#Get` call | ms | summary | +| `store_cachekv_set` | Duration of a CacheKV `Store#Set` call | ms | summary | +| `store_cachekv_write` | Duration of a CacheKV `Store#Write` call | ms | summary | +| `store_cachekv_delete` | Duration of a CacheKV `Store#Delete` call | ms | summary | + +## Next {hide} + +Learn about the [object-capability](./ocap.md) model {hide} diff --git a/docs/core/transactions.md b/docs/core/transactions.md index 3023d6fce8dd..529fd0d17ab8 100644 --- a/docs/core/transactions.md +++ b/docs/core/transactions.md @@ -1,91 +1,158 @@ # Transactions -## Pre-requisite Readings {hide} +`Transactions` are objects created by end-users to trigger state changes in the application. {synopsis} -* [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq} +## Pre-requisite Readings + +- [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq} ## Transactions -Transactions are comprised of metadata held in [contexts](./context.md) and [messages](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's [Handler](../building-modules/handler.md). +Transactions are comprised of metadata held in [contexts](./context.md) and [`Msg`s](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's [`Msg` service](../building-modules/msg-services.md). -When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `message`s must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md). +When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `Msg`s must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md). ## Type Definition Transaction objects are SDK types that implement the `Tx` interface -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L34-L41 ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/tx_msg.go#L49-L57 It contains the following methods: -* **GetMsgs:** unwraps the transaction and returns a list of its message(s) - one transaction may have one or multiple [messages](../building-modules/messages-and-queries.md#messages), which are defined by module developers. -* **ValidateBasic:** includes lightweight, [*stateless*](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for *`messages`*, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. -* **TxEncoder:** Nodes running the consensus engine (e.g. Tendermint Core) are responsible for gossiping transactions and ordering them into blocks, but only handle them in the generic `[]byte` form. Transactions are always [marshaled](./encoding.md) (encoded) before they are relayed to nodes, which compacts them to facilitate gossiping and helps maintain the consensus engine's separation from from application logic. The Cosmos SDK allows developers to specify any deterministic encoding format for their applications; the default is Amino. -* **TxDecoder:** [ABCI](https://tendermint.com/docs/spec/abci/) calls from the consensus engine to the application, such as `CheckTx` and `DeliverTx`, are used to process transaction data to determine validity and state changes. Since transactions are passed in as `txBytes []byte`, they need to first be unmarshaled (decoded) using `TxDecoder` before any logic is applied. +- **GetMsgs:** unwraps the transaction and returns a list of its `Msg`s - one transaction may have one or multiple [`Msg`s](../building-modules/messages-and-queries.md#messages), which are defined by module developers. +- **ValidateBasic:** includes lightweight, [_stateless_](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for `Msg`s, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself. -The most used implementation of the `Tx` interface is [`StdTx` from the `auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/types/stdtx.go). As a developer, using `StdTx` as your transaction format is as simple as importing the `auth` module in your application (which can be done in the [constructor of the application](../basics/app-anatomy.md#constructor-function)) +As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation). -## Transaction Process +### Signing Transactions -A transaction is created by an end-user through one of the possible [interfaces](#interfaces). In the process, two contexts and an array of [messages](#messages) are created, which are then used to [generate](#transaction-generation) the transaction itself. The actual state changes triggered by transactions are enabled by the [handlers](#handlers). The rest of the document will describe each of these components, in this order. +Every message in a transaction must be signed by the addresses specified by its `GetSigners`. The SDK currently allows signing transactions in two different ways. -### CLI and REST Interfaces +#### `SIGN_MODE_DIRECT` (preferred) -Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line or through HTTP requests. +The most used implementation of the `Tx` interface is the Protobuf `Tx` message, which is used in `SIGN_MODE_DIRECT`: -For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers. ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L12-L25 -When users interact with the application's interfaces, they invoke the underlying modules' handlers or command functions, directly creating messages. +Because Protobuf serialization is not deterministic, the SDK uses an additional `TxRaw` type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid `body` and `auth_info` for a transaction, and serialize these two messages using Protobuf. `TxRaw` then pins the user's exact binary representation of `body` and `auth_info`, called respectively `body_bytes` and `auth_info_bytes`. The document that is signed by all signers of the transaction is `SignDoc` (deterministically serialized using [ADR-027](../architecture/adr-027-deterministic-protobuf-serialization.md)): -### Messages ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L47-L64 + +Once signed by all signers, the `body_bytes`, `auth_info_bytes` and `signatures` are gathered into `TxRaw`, whose serialized bytes are broadcasted over the network. + +#### `SIGN_MODE_LEGACY_AMINO_JSON` + +The legacy implemention of the `Tx` interface is the `StdTx` struct from `x/auth`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx.go#L120-L130 + +The document signed by all signers is `StdSignDoc`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdsign.go#L20-L33 -**`Message`s** are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the `message`s for their module by implementing the `Msg` interface, and also define a [`Handler`](../building-modules/handler.md) to process them. +which is encoded into bytes using Amino JSON. Once all signatures are gathered into `StdTx`, `StdTx` is serialized using Amino JSON, and these bytes are broadcasted over the network. + +#### Other Sign Modes + +Other sign modes, most notably `SIGN_MODE_TEXTUAL`, are being discussed. If you wish to learn more about them, please refer to [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md). + +## Transaction Process + +The process of an end-user sending a transaction is: + +- decide on the messages to put into the transaction, +- generate the transaction using the SDK's `TxBuilder`, +- broadcast the transaction using one of the available interfaces. + +The next paragraphs will describe each of these components, in this order. + +### Messages -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L8-L29 +::: tip +Module `Msg`s are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers. +::: -`Message`s in a module are typically defined in a `msgs.go` file (though not always), and one handler with multiple functions to handle each of the module's `message`s is defined in a `handler.go` file. +**Messages** (or `Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/msg-services.md), and also implement the corresponding `MsgServer`. -Note: module `messages` are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers. +`Msg`s in a module are defined as methods in the [`Msg` service] inside each module's `tx.proto` file. Since `Msg`s are module-specific, each module needs a to process all of its message types and trigger state changes within the module's scope. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To achieve this, Protobuf generates a `MsgServer` interface for each module, and the module developer needs to implement this interface. The methods on the `MsgServer` interface corresponds on how to handle each of the different `Msg`. -To learn more about `message`s, click [here](../building-modules/messages-and-queries.md#messages). +To learn more about `Msg` services and how to implement `MsgServer`, click [here](../building-modules/msg-services.md). -While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `CLIContext`. +While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `Context`. ### Transaction Generation -Transactions are first created by end-users through an `appcli tx` command through the command-line or a POST request to an HTTPS server. For details about transaction creation, click [here](../basics/tx-lifecycle.md#transaction-creation). +The `TxBuilder` interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L32-L45 + +- `Msg`s, the array of [messages](#messages) included in the transaction. +- `GasLimit`, option chosen by the users for how to calculate how much gas they will need to pay. +- `Memo`, to send with the transaction. +- `FeeAmount`, the maximum amount the user is willing to pay in fees. +- `TimeoutHeight`, block height until which the transaction is valid. +- `Signatures`, the array of signatures from all signers of the transaction. + +As there are currently two sign modes for signing transactions, there are also two implementations of `TxBuilder`: + +- [wrapper](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/tx/builder.go#L19-L33) for creating transactions for `SIGN_MODE_DIRECT`, +- [StdTxBuilder](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx_builder.go#L14-L20) for `SIGN_MODE_LEGACY_AMINO_JSON`. + +However, the two implementation of `TxBuilder` should be hidden away from end-users, as they should prefer using the overarching `TxConfig` interface: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L21-L30 + +`TxConfig` is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. By calling `txBuilder := txConfig.NewTxBuilder()`, a new `TxBuilder` will be created with the appropriate sign mode. + +Once `TxBuilder` is correctly populated with the setters exposed above, `TxConfig` will also take care of correctly encoding the bytes (again, either using `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`). Here's a pseudo-code snippet of how to generate and encode a transaction, using the `TxEncoder()` method: + +```go +txBuilder := txConfig.NewTxBuilder() +txBuilder.SetMsgs(...) // and other setters on txBuilder + +bz, err := txConfig.TxEncoder()(txBuilder.GetTx()) +// bz are bytes to be broadcasted over the network +``` + +### Broadcasting the Transaction + +Once the transaction bytes are generated, there are currently three ways of broadcasting it. + +#### CLI + +Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line. + +For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the [Interacting with a Node](../run-node/interact-node.md) section. An example transaction made using CLI looks like: + +```bash +simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake +``` + +#### gRPC + +[gRPC](https://grpc.io) is introduced in Cosmos SDK 0.40 as the main component for the SDK's RPC layer. The principal usage of gRPC is in the context of modules' [`Query` services](../building-modules). However, the SDK also exposes a few other module-agnostic gRPC services, one of them being the `Tx` service: -[`Contexts`](https://godoc.org/context) are immutable objects that contain all the information needed to process a request. In the process of creating a transaction through the `auth` module (though it is not mandatory to create transactions this way), two contexts are created: the [`CLIContext`](../interfaces/query-lifecycle.md#clicontext) and `TxBuilder`. Both are automatically generated and do not need to be defined by application developers, but do require input from the transaction creator (e.g. using flags through the CLI). ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/service.proto -The `TxBuilder` contains data closely related with the processing of transactions. +The `Tx` service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions. -+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L18-L31 +Examples of broadcasting and simulating a transaction are shown [here](../run-node/txs.md#programmatically-with-go). -- `TxEncoder` defined by the developer for this type of transaction. Used to encode messages before being processed by nodes running Tendermint. -- `Keybase` that manages the user's keys and is used to perform signing operations. -- `AccountNumber` from which this transaction originated. -- `Sequence`, the number of transactions that the user has sent out, used to prevent replay attacks. -- `Gas` option chosen by the users for how to calculate how much gas they will need to pay. A common option is "auto" which generates an automatic estimate. -- `GasAdjustment` to adjust the estimate of gas by a scalar value, used to avoid underestimating the amount of gas required. -- `SimulateAndExecute` option to simply simulate the transaction execution without broadcasting. -- `ChainID` representing which blockchain this transaction pertains to. -- `Memo` to send with the transaction. -- `Fees`, the maximum amount the user is willing to pay in fees. Alternative to specifying gas prices. -- `GasPrices`, the amount per unit of gas the user is willing to pay in fees. Alternative to specifying fees. +#### REST -The `CLIContext` is initialized using the application's `codec` and data more closely related to the user interaction with the interface, holding data such as the output to the user and the broadcast mode. Read more about `CLIContext` [here](../interfaces/query-lifecycle.md#clicontext). +Each gRPC method has its corresponding REST endpoint, generated using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the `POST /cosmos/tx/v1beta1/txs` endpoint. -Every message in a transaction must be signed by the addresses specified by `GetSigners`. The signing process must be handled by a module, and the most widely used one is the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module. Signing is automatically performed when the transaction is created, unless the user choses to generate and sign separately. The `TxBuilder` (namely, the `KeyBase`) is used to perform the signing operations, and the `CLIContext` is used to broadcast transactions. +An example can be seen [here](../run-node/txs.md#using-rest) -### Handlers +#### Tendermint RPC -Since `message`s are module-specific types, each module needs a [`handler`](../building-modules/handler.md) to process all of its `message` types and trigger state changes within the module's scope. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To read more about `handler`s, click [here](../building-modules/handler.md). +The three methods presented above are actually higher abstractions over the Tendermint RPC `/broadcast_tx_{async,sync,commit}` endpoints, documented [here](https://docs.tendermint.com/master/rpc/#/Tx). This means that you can use the Tendermint RPC endpoints directly to broadcast the transaction, if you wish so. ## Next {hide} -Learn about the [context](./context.md) {hide} \ No newline at end of file +Learn about the [context](./context.md) {hide} diff --git a/docs/ibc/README.md b/docs/ibc/README.md new file mode 100644 index 000000000000..22061db94583 --- /dev/null +++ b/docs/ibc/README.md @@ -0,0 +1,17 @@ + + +# IBC + +This repository contains reference documentation for the IBC protocol integration and concepts: + +1. [Overview](./overview.md) +2. [Integration](./integration.md) +3. [Customization](./custom.md) +4. [Relayer](./relayer.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/custom.md b/docs/ibc/custom.md new file mode 100644 index 000000000000..a7b74027dadf --- /dev/null +++ b/docs/ibc/custom.md @@ -0,0 +1,468 @@ + + +# Customization + +Learn how to configure your application to use IBC and send data packets to other chains. {synopsis} + +This document serves as a guide for developers who want to write their own Inter-blockchain +Communication Protocol (IBC) applications for custom [use-cases](https://github.com/cosmos/ics/blob/master/ibc/4_IBC_USECASES.md). + +Due to the modular design of the IBC protocol, IBC +application developers do not need to concern themselves with the low-level details of clients, +connections, and proof verification. Nevertheless a brief explanation of the lower levels of the +stack is given so that application developers may have a high-level understanding of the IBC +protocol. Then the document goes into detail on the abstraction layer most relevant for application +developers (channels and ports), and describes how to define your own custom packets, and +`IBCModule` callbacks. + +To have your module interact over IBC you must: bind to a port(s), define your own packet data and acknolwedgement structs as well as how to encode/decode them, and implement the +`IBCModule` interface. Below is a more detailed explanation of how to write an IBC application +module correctly. + +## Pre-requisites Readings + +- [IBC Overview](./overview.md)) {prereq} +- [IBC default integration](./integration.md) {prereq} + +## Create a custom IBC application module + +### Implement `IBCModule` Interface and callbacks + +The Cosmos SDK expects all IBC modules to implement the [`IBCModule` +interface](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/05-port/types/module.go). This +interface contains all of the callbacks IBC expects modules to implement. This section will describe +the callbacks that are called during channel handshake execution. + +Here are the channel handshake callbacks that modules are expected to implement: + +```go +// Called by IBC Handler on MsgOpenInit +func (k Keeper) OnChanOpenInit(ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) error { + // OpenInit must claim the channelCapability that IBC passes into the callback + if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + // Examples: Abort if order == UNORDERED, + // Abort if version is unsupported + err := checkArguments(args) + return err +} + +// Called by IBC Handler on MsgOpenTry +OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version, + counterpartyVersion string, +) error { + // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos + // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) + // If the module can already authenticate the capability then the module already owns it so we don't need to claim + // Otherwise, module does not have channel capability and we must claim it from IBC + if !k.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + // Only claim channel capability passed back by IBC module if we do not already own it + if err := k.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + } + + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} + +// Called by IBC Handler on MsgOpenAck +OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} + +// Called by IBC Handler on MsgOpenConfirm +OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // ... do custom initialization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} +``` + +The channel closing handshake will also invoke module callbacks that can return errors to abort the +closing handshake. Closing a channel is a 2-step handshake, the initiating chain calls +`ChanCloseInit` and the finalizing chain calls `ChanCloseConfirm`. + +```go +// Called by IBC Handler on MsgCloseInit +OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // ... do custom finalization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} + +// Called by IBC Handler on MsgCloseConfirm +OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + // ... do custom finalization logic + + // Use above arguments to determine if we want to abort handshake + err := checkArguments(args) + return err +} +``` + +#### Channel Handshake Version Negotiation + +Application modules are expected to verify versioning used during the channel handshake procedure. + +* `ChanOpenInit` callback should verify that the `MsgChanOpenInit.Version` is valid +* `ChanOpenTry` callback should verify that the `MsgChanOpenTry.Version` is valid and that `MsgChanOpenTry.CounterpartyVersion` is valid. +* `ChanOpenAck` callback should verify that the `MsgChanOpenAck.CounterpartyVersion` is valid and supported. + +Versions must be strings but can implement any versioning structure. If your application plans to +have linear releases then semantic versioning is recommended. If your application plans to release +various features in between major releases then it is advised to use the same versioning scheme +as IBC. This versioning scheme specifies a version identifier and compatible feature set with +that identifier. Valid version selection includes selecting a compatible version identifier with +a subset of features supported by your application for that version. The struct is used for this +scheme can be found in `03-connection/types`. + +Since the version type is a string, applications have the ability to do simple version verification +via string matching or they can use the already impelemented versioning system and pass the proto +encoded version into each handhshake call as necessary. + +ICS20 currently implements basic string matching with a single supported version. + +### Bind Ports + +Currently, ports must be bound on app initialization. A module may bind to ports in `InitGenesis` +like so: + +```go +func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, state types.GenesisState) { + // ... other initialization logic + + // Only try to bind to port if it is not already bound, since we may already own + // port capability from capability InitGenesis + if !isBound(ctx, state.PortID) { + // module binds to desired ports on InitChain + // and claims returned capabilities + cap1 := keeper.IBCPortKeeper.BindPort(ctx, port1) + cap2 := keeper.IBCPortKeeper.BindPort(ctx, port2) + cap3 := keeper.IBCPortKeeper.BindPort(ctx, port3) + + // NOTE: The module's scoped capability keeper must be private + keeper.scopedKeeper.ClaimCapability(cap1) + keeper.scopedKeeper.ClaimCapability(cap2) + keeper.scopedKeeper.ClaimCapability(cap3) + } + + // ... more initialization logic +} +``` + +### Custom Packets + +Modules connected by a channel must agree on what application data they are sending over the +channel, as well as how they will encode/decode it. This process is not specified by IBC as it is up +to each application module to determine how to implement this agreement. However, for most +applications this will happen as a version negotiation during the channel handshake. While more +complex version negotiation is possible to implement inside the channel opening handshake, a very +simple version negotation is implemented in the [ibc-transfer module](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/applications/transfer/module.go). + +Thus, a module must define its a custom packet data structure, along with a well-defined way to +encode and decode it to and from `[]byte`. + +```go +// Custom packet data defined in application module +type CustomPacketData struct { + // Custom fields ... +} + +EncodePacketData(packetData CustomPacketData) []byte { + // encode packetData to bytes +} + +DecodePacketData(encoded []byte) (CustomPacketData) { + // decode from bytes to packet data +} +``` + +Then a module must encode its packet data before sending it through IBC. + +```go +// Sending custom application packet data +data := EncodePacketData(customPacketData) +packet.Data = data +IBCChannelKeeper.SendPacket(ctx, packet) +``` + +A module receiving a packet must decode the `PacketData` into a structure it expects so that it can +act on it. + +```go +// Receiving custom application packet data (in OnRecvPacket) +packetData := DecodePacketData(packet.Data) +// handle received custom packet data +``` + +#### Packet Flow Handling + +Just as IBC expected modules to implement callbacks for channel handshakes, IBC also expects modules +to implement callbacks for handling the packet flow through a channel. + +Once a module A and module B are connected to each other, relayers can start relaying packets and +acknowledgements back and forth on the channel. + +![IBC packet flow diagram](https://media.githubusercontent.com/media/cosmos/ics/master/spec/ics-004-channel-and-packet-semantics/packet-state-machine.png) + +Briefly, a successful packet flow works as follows: + +1. module A sends a packet through the IBC module +2. the packet is received by module B +3. if module B writes an acknowledgement of the packet then module A will process the + acknowledgement +4. if the packet is not successfully received before the timeout, then module A processes the + packet's timeout. + +##### Sending Packets + +Modules do not send packets through callbacks, since the modules initiate the action of sending +packets to the IBC module, as opposed to other parts of the packet flow where msgs sent to the IBC +module must trigger execution on the port-bound module through the use of callbacks. Thus, to send a +packet a module simply needs to call `SendPacket` on the `IBCChannelKeeper`. + +```go +// retrieve the dynamic capability for this channel +channelCap := scopedKeeper.GetCapability(ctx, channelCapName) +// Sending custom application packet data +data := EncodePacketData(customPacketData) +packet.Data = data +// Send packet to IBC, authenticating with channelCap +IBCChannelKeeper.SendPacket(ctx, channelCap, packet) +``` + +::: warning +In order to prevent modules from sending packets on channels they do not own, IBC expects +modules to pass in the correct channel capability for the packet's source channel. +::: + +##### Receiving Packets + +To handle receiving packets, the module must implement the `OnRecvPacket` callback. This gets +invoked by the IBC module after the packet has been proved valid and correctly processed by the IBC +keepers. Thus, the `OnRecvPacket` callback only needs to worry about making the appropriate state +changes given the packet data without worrying about whether the packet is valid or not. + +Modules may return an acknowledgement as a byte string and return it to the IBC handler. +The IBC handler will then commit this acknowledgement of the packet so that a relayer may relay the +acknowledgement back to the sender module. + +```go +OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) (res *sdk.Result, ack []byte, abort error) { + // Decode the packet data + packetData := DecodePacketData(packet.Data) + + // do application state changes based on packet data + // and return result, acknowledgement and abortErr + // Note: abortErr is only not nil if we need to abort the entire receive packet, and allow a replay of the receive. + // If the application state change failed but we do not want to replay the packet, + // simply encode this failure with relevant information in ack and return nil error + res, ack, abortErr := processPacket(ctx, packet, packetData) + + // if we need to abort the entire receive packet, return error + if abortErr != nil { + return nil, nil, abortErr + } + + // Encode the ack since IBC expects acknowledgement bytes + ackBytes := EncodeAcknowledgement(ack) + + return res, ackBytes, nil +} +``` + +::: warning +`OnRecvPacket` should **only** return an error if we want the entire receive packet execution +(including the IBC handling) to be reverted. This will allow the packet to be replayed in the case +that some mistake in the relaying caused the packet processing to fail. + +If some application-level error happened while processing the packet data, in most cases, we will +not want the packet processing to revert. Instead, we may want to encode this failure into the +acknowledgement and finish processing the packet. This will ensure the packet cannot be replayed, +and will also allow the sender module to potentially remediate the situation upon receiving the +acknowledgement. An example of this technique is in the `ibc-transfer` module's +[`OnRecvPacket`](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/applications/transfer/module.go). +::: + +### Acknowledgements + +Modules may commit an acknowledgement upon receiving and processing a packet in the case of synchronous packet processing. +In the case where a packet is processed at some later point after the packet has been received (asynchronous execution), the acknowledgement +will be written once the packet has been processed by the application which may be well after the packet receipt. + +NOTE: Most blockchain modules will want to use the synchronous execution model in which the module processes and writes the acknowledgement +for a packet as soon as it has been received from the IBC module. + +This acknowledgement can then be relayed back to the original sender chain, which can take action +depending on the contents of the acknowledgement. + +Just as packet data was opaque to IBC, acknowledgements are similarly opaque. Modules must pass and +receive acknowledegments with the IBC modules as byte strings. + +Thus, modules must agree on how to encode/decode acknowledgements. The process of creating an +acknowledgement struct along with encoding and decoding it, is very similar to the packet data +example above. [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope) +specifies a recommended format for acknowledgements. This acknowledgement type can be imported from +[channel types](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/04-channel/types). + +While modules may choose arbitrary acknowledgement structs, a default acknowledgement types is provided by IBC [here](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/proto/ibc/core/channel/v1/channel.proto): + +```proto +// Acknowledgement is the recommended acknowledgement format to be used by +// app-specific protocols. +// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +// conflicts with other protobuf message formats used for acknowledgements. +// The first byte of any message with this format will be the non-ASCII values +// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +message Acknowledgement { + // response contains either a result or an error and must be non-empty + oneof response { + bytes result = 21; + string error = 22; + } +} +``` + +#### Acknowledging Packets + +After a module writes an acknowledgement, a relayer can relay back the acknowledgement to the sender module. The sender module can +then process the acknowledgement using the `OnAcknowledgementPacket` callback. The contents of the +acknowledgement is entirely upto the modules on the channel (just like the packet data); however, it +may often contain information on whether the packet was successfully processed along +with some additional data that could be useful for remediation if the packet processing failed. + +Since the modules are responsible for agreeing on an encoding/decoding standard for packet data and +acknowledgements, IBC will pass in the acknowledgements as `[]byte` to this callback. The callback +is responsible for decoding the acknowledgement and processing it. + +```go +OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, +) (*sdk.Result, error) { + // Decode acknowledgement + ack := DecodeAcknowledgement(acknowledgement) + + // process ack + res, err := processAck(ack) + return res, err +} +``` + +#### Timeout Packets + +If the timeout for a packet is reached before the packet is successfully received or the +counterparty channel end is closed before the packet is successfully received, then the receiving +chain can no longer process it. Thus, the sending chain must process the timeout using +`OnTimeoutPacket` to handle this situation. Again the IBC module will verify that the timeout is +indeed valid, so our module only needs to implement the state machine logic for what to do once a +timeout is reached and the packet can no longer be received. + +```go +OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) (*sdk.Result, error) { + // do custom timeout logic +} +``` + +### Routing + +As mentioned above, modules must implement the IBC module interface (which contains both channel +handshake callbacks and packet handling callbacks). The concrete implementation of this interface +must be registered with the module name as a route on the IBC `Router`. + +```go +// app.go +func NewApp(...args) *App { +// ... + +// Create static IBC router, add module routes, then set and seal it +ibcRouter := port.NewRouter() + +ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) +// Note: moduleCallbacks must implement IBCModule interface +ibcRouter.AddRoute(moduleName, moduleCallbacks) + +// Setting Router will finalize all routes by sealing router +// No more routes can be added +app.IBCKeeper.SetRouter(ibcRouter) +``` + +## Working Example + +For a real working example of an IBC application, you can look through the `ibc-transfer` module +which implements everything discussed above. + +Here are the useful parts of the module to look at: + +[Binding to transfer +port](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/applications/transfer/genesis.go) + +[Sending transfer +packets](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/applications/transfer/keeper/relay.go) + +[Implementing IBC +callbacks](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/applications/transfer/module.go) + +## Next {hide} + +Learn about [building modules](../building-modules/intro.md) {hide} diff --git a/docs/ibc/integration.md b/docs/ibc/integration.md new file mode 100644 index 000000000000..d1ba9d745548 --- /dev/null +++ b/docs/ibc/integration.md @@ -0,0 +1,252 @@ + + +# Integration + +Learn how to integrate IBC to your application and send data packets to other chains. {synopsis} + +This document outlines the required steps to integrate and configure the [IBC +module](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc) to your Cosmos SDK application and +send fungible token transfers to other chains. + +## Integrating the IBC module + +Integrating the IBC module to your SDK-based application is straighforward. The general changes can be summarized in the following steps: + +- Add required modules to the `module.BasicManager` +- Define additional `Keeper` fields for the new modules on the `App` type +- Add the module's `StoreKeys` and initialize their `Keepers` +- Set up corresponding routers and routes for the `ibc` and `evidence` modules +- Add the modules to the module `Manager` +- Add modules to `Begin/EndBlockers` and `InitGenesis` +- Update the module `SimulationManager` to enable simulations + +### Module `BasicManager` and `ModuleAccount` permissions + +The first step is to add the following modules to the `BasicManager`: `x/capability`, `x/ibc`, +`x/evidence` and `x/ibc/applications/transfer`. After that, we need to grant `Minter` and `Burner` permissions to +the `ibc-transfer` `ModuleAccount` to mint and burn relayed tokens. + +```go +// app.go +var ( + + ModuleBasics = module.NewBasicManager( + // ... + capability.AppModuleBasic{}, + ibc.AppModuleBasic{}, + evidence.AppModuleBasic{}, + transfer.AppModuleBasic{}, // i.e ibc-transfer module + ) + + // module account permissions + maccPerms = map[string][]string{ + // other module accounts permissions + // ... + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, +) +``` + +### Application fields + +Then, we need to register the `Keepers` as follows: + +```go +// app.go +type App struct { + // baseapp, keys and subspaces definitions + + // other keepers + // ... + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + EvidenceKeeper evidencekeeper.Keeper // required to set up the client misbehaviour route + TransferKeeper ibctransferkeeper.Keeper // for cross-chain fungible token transfers + + // make scoped keepers public for test purposes + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + + /// ... + /// module and simulation manager definitions +} +``` + +### Configure the `Keepers` + +During initialization, besides initializing the IBC `Keepers` (for the `x/ibc`, and +`x/ibc/applications/transfer` modules), we need to grant specific capabilities through the capability module +`ScopedKeepers` so that we can authenticate the object-capability permissions for each of the IBC +channels. + +```go +func NewApp(...args) *App { + // define codecs and baseapp + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + + // grant capabilities for the ibc and ibc-transfer modules + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) + scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + + // ... other modules keepers + + // Create IBC Keeper + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, keys[ibchost.StoreKey], app.StakingKeeper, scopedIBCKeeper, + ) + + // Create Transfer Keepers + app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, keys[ibctransfertypes.StoreKey], + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, + ) + transferModule := transfer.NewAppModule(app.TransferKeeper) + + // Create evidence Keeper for to register the IBC light client misbehaviour evidence route + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper, + ) + + // .. continues +} +``` + +### Register `Routers` + +IBC needs to know which module is bound to which port so that it can route packets to the +appropriate module and call the appropriate callbacks. The port to module name mapping is handled by +IBC's port `Keeper`. However, the mapping from module name to the relevant callbacks is accomplished +by the port +[`Router`](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc//core/05-port/types/router.go) on the +IBC module. + +Adding the module routes allows the IBC handler to call the appropriate callback when processing a +channel handshake or a packet. + +The second `Router` that is required is the evidence module router. This router handles general +evidence submission and routes the business logic to each registered evidence handler. In the case +of IBC, it is required to submit evidence for [light client +misbehaviour](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#misbehaviour) +in order to freeze a client and prevent further data packets from being sent/received. + +Currently, a `Router` is static so it must be initialized and set correctly on app initialization. +Once the `Router` has been set, no new routes can be added. + +```go +// app.go +func NewApp(...args) *App { + // .. continuation from above + + // Create static IBC router, add ibc-tranfer module route, then set and seal it + ibcRouter := port.NewRouter() + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) + // Setting Router will finalize all routes by sealing router + // No more routes can be added + app.IBCKeeper.SetRouter(ibcRouter) + + // create static Evidence routers + + evidenceRouter := evidencetypes.NewRouter(). + // add IBC ClientMisbehaviour evidence handler + AddRoute(ibcclient.RouterKey, ibcclient.HandlerClientMisbehaviour(app.IBCKeeper.ClientKeeper)) + + // Setting Router will finalize all routes by sealing router + // No more routes can be added + evidenceKeeper.SetRouter(evidenceRouter) + + // set the evidence keeper from the section above + app.EvidenceKeeper = *evidenceKeeper + + // .. continues +``` + +### Module Managers + +In order to use IBC, we need to add the new modules to the module `Manager` and to the `SimulationManager` in case your application supports [simulations](./../building-modules/simulator.md). + +```go +// app.go +func NewApp(...args) *App { + // .. continuation from above + + app.mm = module.NewManager( + // other modules + // ... + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + transferModule, + ) + + // ... + + app.sm = module.NewSimulationManager( + // other modules + // ... + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + transferModule, + ) + + // .. continues +``` + +### Application ABCI Ordering + +One addition from IBC is the concept of `HistoricalEntries` which are stored on the staking module. +Each entry contains the historical information for the `Header` and `ValidatorSet` of this chain which is stored +at each height during the `BeginBlock` call. The historical info is required to introspect the +past historical info at any given height in order to verify the light client `ConsensusState` during the +connection handhake. + +The IBC module also has +[`BeginBlock`](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/02-client/abci.go) logic as +well. This is optional as it is only required if your application uses the [localhost +client](https://github.com/cosmos/ics/blob/master/spec/ics-009-loopback-client) to connect two +different modules from the same chain. + +::: tip +Only register the ibc module to the `SetOrderBeginBlockers` if your application will use the +localhost (_aka_ loopback) client. +::: + +```go +// app.go +func NewApp(...args) *App { + // .. continuation from above + + // add evidence, staking and ibc modules to BeginBlockers + app.mm.SetOrderBeginBlockers( + // other modules ... + evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, + ) + + // ... + + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + app.mm.SetOrderInitGenesis( + capabilitytypes.ModuleName, + // other modules ... + ibchost.ModuleName, evidencetypes.ModuleName, ibctransfertypes.ModuleName, + ) + + // .. continues +``` + +::: warning +**IMPORTANT**: The capability module **must** be declared first in `SetOrderInitGenesis` +::: + +That's it! You have now wired up the IBC module and are now able to send fungible tokens across +different chains. If you want to have a broader view of the changes take a look into the SDK's +[`SimApp`](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/simapp/app.go). + +## Next {hide} + +Learn about how to create [custom IBC modules](./custom.md) for your application {hide} diff --git a/docs/ibc/overview.md b/docs/ibc/overview.md new file mode 100644 index 000000000000..b594006d848b --- /dev/null +++ b/docs/ibc/overview.md @@ -0,0 +1,174 @@ + + +# Overview + +Learn what IBC is, its components and use cases. {synopsis} + +## What is the Interblockchain Communication Protocol (IBC)? + +This document serves as a guide for developers who want to write their own Inter-blockchain +Communication Protocol (IBC) applications for custom [use-cases](https://github.com/cosmos/ics/blob/master/ibc/4_IBC_USECASES.md). + +Due to the modular design of the IBC protocol, IBC +application developers do not need to concern themselves with the low-level details of clients, +connections, and proof verification. Nevertheless a brief explanation of the lower levels of the +stack is given so that application developers may have a high-level understanding of the IBC +protocol. Then the document goes into detail on the abstraction layer most relevant for application +developers (channels and ports), and describes how to define your own custom packets, and +`IBCModule` callbacks. + +To have your module interact over IBC you must: bind to a port(s), define your own packet data (and +optionally acknowledgement) structs as well as how to encode/decode them, and implement the +`IBCModule` interface. Below is a more detailed explanation of how to write an IBC application +module correctly. + +## Components Overview + +This section describes the IBC components and links to the repos. + +### [Clients](https://github.com/cosmos/ibc-go/blob/main/modules/core/02-client) + +IBC clients are light clients that are identified by a unique client id. IBC clients track the consensus states of other blockchains and the proof specs of those blockchains that are required to properly verify proofs against the client's consensus state. A client can be associated with any number of connections to multiple chains. The supported IBC clients are: + +- [Solo Machine light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/06-solomachine): devices such as phones, browsers, or laptops. +- [Tendermint light client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint): The default for Cosmos SDK-based chains. +- [Localhost (loopback) client](https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/09-localhost): Useful for testing, simulation, and relaying packets to modules on the same application. + +### [Connections](https://github.com/cosmos/ibc-go/blob/main/modules/core/03-connection) + +Connections encapsulate two `ConnectionEnd` objects on two separate blockchains. Each `ConnectionEnd` is associated with a client of the other blockchain (the counterparty blockchain). The connection handshake is responsible for verifying that the light clients on each chain are correct for their respective counterparties. Connections, once established, are responsible for facilitating all cross-chain verification of IBC state. A connection can be associated with any number of channels. + +### [Proofs](https://github.com/cosmos/ibc-go/blob/main/modules/core/23-commitment) and [Paths](https://github.com/cosmos/ibc-go/blob/main/modules/core/24-host) + +In IBC, blockchains do not directly pass messages to each other over the network. + +- To communicate, a blockchain commits some state to a precisely defined path reserved for a specific message type and a specific counterparty. For example, a blockchain that stores a specific connectionEnd as part of a handshake or a packet intended to be relayed to a module on the counterparty chain. + +- A relayer process monitors for updates to these paths and relays messages by submitting the data stored under the path along with a proof of that data to the counterparty chain. + +- The paths that all IBC implementations must support for committing IBC messages are defined in [ICS-24 host requirements](https://github.com/cosmos/ics/tree/master/spec/core/ics-024-host-requirements). + +- The proof format that all implementations must produce and verify is defined in [ICS-23 implementation](https://github.com/confio/ics23). + +### [Capabilities](./ocap.md) + +IBC is intended to work in execution environements where modules do not necessarily trust each +other. Thus IBC must authenticate module actions on ports and channels so that only modules with the +appropriate permissions can use them. This is accomplished using [dynamic +capabilities](../architecture/adr-003-dynamic-capability-store.md). Upon binding to a port or +creating a channel for a module, IBC will return a dynamic capability that the module must claim in +order to use that port or channel. This prevents other modules from using that port or channel since +they will not own the appropriate capability. + +While the above is useful background information, IBC modules do not need to interact at all with +these lower-level abstractions. The relevant abstraction layer for IBC application developers is +that of channels and ports. IBC applications should be written as self-contained **modules**. A +module on one blockchain can thus communicate with other modules on other blockchains by sending, +receiving and acknowledging packets through channels, which are uniquely identified by the +`(channelID, portID)` tuple. A useful analogy is to consider IBC modules as internet applications on +a computer. A channel can then be conceptualized as an IP connection, with the IBC portID being +analogous to a IP port and the IBC channelID being analogous to an IP address. Thus, a single +instance of an IBC module may communicate on the same port with any number of other modules and and +IBC will correctly route all packets to the relevant module using the (channelID, portID tuple). An +IBC module may also communicate with another IBC module over multiple ports, with each +`(portID<->portID)` packet stream being sent on a different unique channel. + +### [Ports](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/05-port) + +An IBC module may bind to any number of ports. Each port must be identified by a unique `portID`. +Since IBC is designed to be secure with mutually-distrusted modules operating on the same ledger, +binding a port will return a dynamic object capability. In order to take action on a particular port +(eg open a channel with its portID), a module must provide the dynamic object capability to the IBC +handler. This prevents a malicious module from opening channels with ports it does not own. Thus, +IBC modules are responsible for claiming the capability that is returned on `BindPort`. + +### [Channels](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/04-channel) + +An IBC channel can be established between 2 IBC ports. Currently, a port is exclusively owned by a +single module. IBC packets are sent over channels. Just as IP packets contain the destination IP +address and IP port as well as the source IP address and source IP port, IBC packets will contain +the destination portID and channelID as well as the source portID and channelID. This enables IBC to +correctly route packets to the destination module, while also allowing modules receiving packets to +know the sender module. + +A channel may be `ORDERED`, in which case, packets from a sending module must be processed by the +receiving module in the order they were sent. Or a channel may be `UNORDERED`, in which case packets +from a sending module are processed in the order they arrive (may not be the order they were sent). + +Modules may choose which channels they wish to communicate over with, thus IBC expects modules to +implement callbacks that are called during the channel handshake. These callbacks may do custom +channel initialization logic, if any return an error, the channel handshake will fail. Thus, by +returning errors on callbacks, modules can programatically reject and accept channels. + +The channel handshake is a 4 step handshake. Briefly, if a given chain A wants to open a channel with +chain B using an already established connection: + +1. chain A sends a `ChanOpenInit` message to signal a channel initialization attempt with chain B. +2. chain B sends a `ChanOpenTry` message to try opening the channel on chain A. +3. chain A sends a `ChanOpenAck` message to mark its channel end status as open. +4. chain B sends a `ChanOpenConfirm` message to mark its channel end status as open. + +If all this happens successfully, the channel will be open on both sides. At each step in the handshake, the module +associated with the `ChannelEnd` will have it's callback executed for that step of the handshake. So +on `ChanOpenInit`, the module on chain A will have its callback `OnChanOpenInit` executed. + +Just as ports came with dynamic capabilites, channel initialization will return a dynamic capability +that the module **must** claim so that they can pass in a capability to authenticate channel actions +like sending packets. The channel capability is passed into the callback on the first parts of the +handshake; either `OnChanOpenInit` on the initializing chain or `OnChanOpenTry` on the other chain. + +### [Packets](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/04-channel) + +Modules communicate with each other by sending packets over IBC channels. As mentioned above, all +IBC packets contain the destination `portID` and `channelID` along with the source `portID` and +`channelID`, this allows modules to know the sender module of a given packet. IBC packets also +contain a sequence to optionally enforce ordering. IBC packets also contain a `TimeoutTimestamp` and +`TimeoutHeight`, which when non-zero, will determine the deadline before which the receiving module +must process a packet. If the timeout passes without the packet being successfully received, the +sending module can timeout the packet and take appropriate actions. + +Modules send custom application data to each other inside the `Data []byte` field of the IBC packet. +Thus, packet data is completely opaque to IBC handlers. It is incumbent on a sender module to encode +their application-specific packet information into the `Data` field of packets, and the receiver +module to decode that `Data` back to the original application data. + +### [Receipts and Timeouts](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/04-channel) + +Since IBC works over a distributed network and relies on potentially faulty relayers to relay messages between ledgers, +IBC must handle the case where a packet does not get sent to its destination in a timely manner or at all. Thus, packets must +specify a timeout height or timeout timestamp after which a packet can no longer be successfully received on the destination chain. + +If the timeout does get reached, then a proof of packet timeout can be submitted to the original chain which can then perform +application-specific logic to timeout the packet, perhaps by rolling back the packet send changes (refunding senders any locked funds, etc). + +In ORDERED channels, a timeout of a single packet in the channel will cause the channel to close. If packet sequence `n` times out, +then no packet at sequence `k > n` can be successfully received without violating the contract of ORDERED channels that packets are processed in the order that they are sent. Since ORDERED channels enforce this invariant, a proof that sequence `n` hasn't been received on the destination chain by packet `n`'s specified timeout is sufficient to timeout packet `n` and close the channel. + +In the UNORDERED case, packets may be received in any order. Thus, IBC will write a packet receipt for each sequence it has received in the UNORDERED channel. This receipt contains no information, it is simply a marker intended to signify that the UNORDERED channel has received a packet at the specified sequence. To timeout a packet on an UNORDERED channel, one must provide a proof that a packet receipt does not exist for the packet's sequence by the specified timeout. Of course, timing out a packet on an UNORDERED channel will simply trigger the application specific timeout logic for that packet, and will not close the channel. + +For this reason, most modules should use UNORDERED channels as they require less liveness guarantees to function effectively for users of that channel. + +### [Acknowledgements](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/04-channel) + +Modules may also choose to write application-specific acknowledgements upon processing a packet. This may either be done synchronously on `OnRecvPacket`, if the module processes packets as soon as they are received from IBC module. Or they may be done asynchronously if module processes packets at some later point after receiving the packet. + +Regardless, this acknowledgement data is opaque to IBC much like the packet `Data` and will be treated by IBC as a simple byte string `[]byte`. It is incumbent on receiver modules to encode their acknowledgemnet in such a way that the sender module can decode it correctly. This should be decided through version negotiation during the channel handshake. + +The acknowledgement may encode whether the packet processing succeeded or failed, along with additional information that will allow the sender module to take appropriate action. + +Once the acknowledgement has been written by the receiving chain, a relayer will relay the acknowledgement back to the original sender module which will then execute application-specific acknowledgment logic using the contents of the acknowledgement. This may involve rolling back packet-send changes in the case of a failed acknowledgement (refunding senders). + +Once an acknowledgement is received successfully on the original sender the chain, the IBC module deletes the corresponding packet commitment as it is no longer needed. + +## Further Readings and Specs + +If you want to learn more about IBC, check the following specifications: + +* [IBC specification overview](https://github.com/cosmos/ics/blob/master/ibc/README.md) +* [IBC SDK specification](../../x/ibc/spec/README.md) + +## Next {hide} + +Learn about how to [integrate](./integration.md) IBC to your application {hide} diff --git a/docs/ibc/relayer.md b/docs/ibc/relayer.md new file mode 100644 index 000000000000..a7ccb9fa21db --- /dev/null +++ b/docs/ibc/relayer.md @@ -0,0 +1,45 @@ + + +# Relayer + +## Pre-requisites Readings + +- [IBC Overview](./overview.md) {prereq} +- [Events](../core/events.md) {prereq} + +## Events + +Events are emitted for every transaction processed by the base application to indicate the execution +of some logic clients may want to be aware of. This is extremely useful when relaying IBC packets. +Any message that uses IBC will emit events for the corresponding TAO logic executed as defined in +the [IBC events spec](https://github.com/cosmos/cosmos-sdk/tree/release/v0.42.x/x/ibc/core/spec/06_events.md). + +In the SDK, it can be assumed that for every message there is an event emitted with the type `message`, +attribute key `action`, and an attribute value representing the type of message sent +(`channel_open_init` would be the attribute value for `MsgChannelOpenInit`). If a relayer queries +for transaction events, it can split message events using this event Type/Attribute Key pair. + +The Event Type `message` with the Attribute Key `module` may be emitted multiple times for a single +message due to application callbacks. It can be assumed that any TAO logic executed will result in +a module event emission with the attribute value `ibc_` (02-client emits `ibc_client`). + +### Subscribing with Tendermint + +Calling the Tendermint RPC method `Subscribe` via [Tendermint's Websocket](https://docs.tendermint.com/master/rpc/) will return events using +Tendermint's internal representation of them. Instead of receiving back a list of events as they +were emitted, Tendermint will return the type `map[string][]string` which maps a string in the +form `.` to `attribute_value`. This causes extraction of the event +ordering to be non-trivial, but still possible. + +A relayer should use the `message.action` key to extract the number of messages in the transaction +and the type of IBC transactions sent. For every IBC transaction within the string array for +`message.action`, the necessary information should be extracted from the other event fields. If +`send_packet` appears at index 2 in the value for `message.action`, a relayer will need to use the +value at index 2 of the key `send_packet.packet_sequence`. This process should be repeated for each +piece of information needed to relay a packet. + +## Example Implementations + +- [Golang Relayer](https://github.com/iqlusioninc/relayer) diff --git a/docs/interfaces/README.md b/docs/interfaces/README.md deleted file mode 100644 index ea94cb9a6fc3..000000000000 --- a/docs/interfaces/README.md +++ /dev/null @@ -1,15 +0,0 @@ - - -# Interfaces - -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 0e7c786ed1f9..000000000000 --- a/docs/interfaces/cli.md +++ /dev/null @@ -1,137 +0,0 @@ - - -# Command-Line Interface - -## Pre-requisite Readings {hide} - -* [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). -* [**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 029ed39accb5..000000000000 --- a/docs/interfaces/interfaces-intro.md +++ /dev/null @@ -1,43 +0,0 @@ - - -# Interfaces - -## Pre-requisite Readings {hide} - -* [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 f20441c6c471..000000000000 --- a/docs/interfaces/lite/getting_started.md +++ /dev/null @@ -1,22 +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-node | bool | "false" | true | Whether this LCD is connected to a trusted full node | -| 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 \ - --trust-node=false -``` - -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 5d1eab955af2..000000000000 --- a/docs/interfaces/query-lifecycle.md +++ /dev/null @@ -1,161 +0,0 @@ - - -# Query Lifecycle - -## Pre-requisite Readings {hide} - -* [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 [`CLIContext`](#clicontext) 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 `CLIContext` 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 `CLIContext`. - -### CLIContext - -The first thing that is created in the execution of a CLI command is a `CLIContext`, while the REST Server directly provides a `CLIContext` 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 `CLIContext` 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 `CLIContext` 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 `CLIContext`'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 `CLIContext` 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 `CLIContext`, which knows which node the user CLI is connected to. The query is relayed to this full-node to be processed. Finally, the `CLIContext` 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 `CLIContext` 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 `CLIContext` 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/querier.md), 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 `CLIContext` 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 [querier](../building-modules/querier.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 queriers [here](../building-modules/querier.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 `CLIContext` `Query()` routine receives the response and, if `--trust-node` is toggled to `false` and a proof needs to be verified, the response is verified with the `CLIContext` `verifyProof()` function before the response is returned. - -+++ 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 `CLIContext` 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 `CLIContext` 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 75e5a78dc3d8..000000000000 --- a/docs/interfaces/rest.md +++ /dev/null @@ -1,60 +0,0 @@ - - -# REST Interface - -## Prerequisites {hide} - -* [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 --trust-node -``` - -Note that if `trust-node` is set to `false`, the REST server will verify the query proof against the merkle root (contained in the block header). - -## 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). -* **CLIContext:** A [`CLIContext`](./query-lifecycle.md#clicontext) 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#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) { - client.RegisterRoutes(rs.CliCtx, rs.Mux) - app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, 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)) -``` diff --git a/docs/intro/overview.md b/docs/intro/overview.md index f7e2242afcfa..b6f1ebce97c3 100644 --- a/docs/intro/overview.md +++ b/docs/intro/overview.md @@ -25,7 +25,7 @@ The Cosmos SDK is the most advanced framework for building custom application-sp - The default consensus engine available within the SDK is [Tendermint Core](https://github.com/tendermint/tendermint). Tendermint is the most (and only) mature BFT consensus engine in existence. It is widely used across the industry and is considered the gold standard consensus engine for building Proof-of-Stake systems. - The SDK is open source and designed to make it easy to build blockchains out of composable [modules](../../x/). As the ecosystem of open source SDK modules grows, it will become increasingly easier to build complex decentralised platforms with it. - The SDK is inspired by capabilities-based security, and informed by years of wrestling with blockchain state-machines. This makes the Cosmos SDK a very secure environment to build blockchains. -- Most importantly, the Cosmos SDK has already been used to build many application-specific blockchains that are already in production. Among others, we can cite [Cosmos Hub](https://hub.cosmos.network), [IRIS Hub](https://irisnet.org), [Binance Chain](https://docs.binance.org/), [Terra](https://terra.money/) or [Lino](https://lino.network/). [Many more](https://cosmos.network/ecosystem) are building on the Cosmos SDK. +- Most importantly, the Cosmos SDK has already been used to build many application-specific blockchains that are already in production. Among others, we can cite [Cosmos Hub](https://hub.cosmos.network), [IRIS Hub](https://irisnet.org), [Binance Chain](https://docs.binance.org/), [Terra](https://terra.money/) or [Kava](https://www.kava.io/). [Many more](https://cosmos.network/ecosystem) are building on the Cosmos SDK. ## Getting started with the Cosmos SDK @@ -34,4 +34,4 @@ The Cosmos SDK is the most advanced framework for building custom application-sp ## Next {hide} -Learn about [application-specific blockchains](./why-app-specific.md) {hide} \ No newline at end of file +Learn about [application-specific blockchains](./why-app-specific.md) {hide} diff --git a/docs/intro/sdk-app-architecture.md b/docs/intro/sdk-app-architecture.md index 68785906eff2..ccd17118574c 100644 --- a/docs/intro/sdk-app-architecture.md +++ b/docs/intro/sdk-app-architecture.md @@ -36,7 +36,7 @@ The Cosmos SDK gives developers maximum flexibility to define the state of their ## Tendermint -Thanks to the Cosmos SDK, develepers just have to define the state machine, and [*Tendermint*](https://tendermint.com/docs/introduction/what-is-tendermint.html) will handle replication over the network for them. +Thanks to the Cosmos SDK, developers just have to define the state machine, and [*Tendermint*](https://tendermint.com/docs/introduction/what-is-tendermint.html) will handle replication over the network for them. ``` @@ -56,13 +56,13 @@ Blockchain node | | Consensus | | ``` -[Tendermint](https://tendermint.com/docs/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions. +[Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) is an application-agnostic engine that is responsible for handling the *networking* and *consensus* layers of a blockchain. In practice, this means that Tendermint is responsible for propagating and ordering transaction bytes. Tendermint Core relies on an eponymous Byzantine-Fault-Tolerant (BFT) algorithm to reach consensus on the order of transactions. -The Tendermint [consensus algorithm](https://tendermint.com/docs/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://tendermint.com/docs/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://tendermint.com/docs/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine. +The Tendermint [consensus algorithm](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html#consensus-overview) works with a set of special nodes called *Validators*. Validators are responsible for adding blocks of transactions to the blockchain. At any given block, there is a validator set V. A validator in V is chosen by the algorithm to be the proposer of the next block. This block is considered valid if more than two thirds of V signed a *[prevote](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#prevote-step-height-h-round-r)* and a *[precommit](https://docs.tendermint.com/v0.34/spec/consensus/consensus.html#precommit-step-height-h-round-r)* on it, and if all the transactions that it contains are valid. The validator set can be changed by rules written in the state-machine. ## ABCI -Tendermint passes transactions to the application through an interface called the [ABCI](https://tendermint.com/docs/spec/abci/), which the application must implement. +Tendermint passes transactions to the application through an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/), which the application must implement. ``` +---------------------+ @@ -86,15 +86,15 @@ Note that **Tendermint only handles transaction bytes**. It has no knowledge of Here are the most important messages of the ABCI: -- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet. -- `DeliverTx`: When a [valid block](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`handler`s](../building-modules/handler.md) for each message in the transaction. +- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://docs.tendermint.com/v0.34/tendermint-core/mempool.html#mempool) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet. +- `DeliverTx`: When a [valid block](https://docs.tendermint.com/v0.34/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service methods](../building-modules/msg-services.md) for each message in the transaction. - `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite. -Find a more detailed view of the ABCI methods from the [Tendermint docs](https://tendermint.com/docs/spec/abci/abci.html#overview). +Find a more detailed view of the ABCI methods from the [Tendermint docs](https://docs.tendermint.com/v0.34/spec/abci/abci.html#overview). Any application built on Tendermint needs to implement the ABCI interface in order to communicate with the underlying local Tendermint engine. Fortunately, you do not have to implement the ABCI interface. The Cosmos SDK provides a boilerplate implementation of it in the form of [baseapp](./sdk-design.md#baseapp). ## Next {hide} -Read about the [high-level design principles of the SDK](./sdk-design.md) {hide} \ No newline at end of file +Read about the [high-level design principles of the SDK](./sdk-design.md) {hide} diff --git a/docs/intro/sdk-design.md b/docs/intro/sdk-design.md index 00a5f2a4b875..d9629a3abdaf 100644 --- a/docs/intro/sdk-design.md +++ b/docs/intro/sdk-design.md @@ -25,7 +25,7 @@ For more on `baseapp`, please click [here](../core/baseapp.md). ## Multistore -The Cosmos SDK provides a [`multistore`](../core/store.md#multisotre) for persisting state. The multistore allows developers to declare any number of [`KVStores`](../core/store.md#base-layer-kvstores). These `KVStores` only accept the `[]byte` type as value and therefore any custom structure needs to be marshalled using [a codec](../core/encoding.md) before being stored. +The Cosmos SDK provides a [`multistore`](../core/store.md#multistore) for persisting state. The multistore allows developers to declare any number of [`KVStores`](../core/store.md#base-layer-kvstores). These `KVStores` only accept the `[]byte` type as value and therefore any custom structure needs to be marshalled using [a codec](../core/encoding.md) before being stored. The multistore abstraction is used to divide the state in distinct compartments, each managed by its own module. For more on the multistore, click [here](../core/store.md#multistore) diff --git a/docs/intro/why-app-specific.md b/docs/intro/why-app-specific.md index a3645f9e3095..14f6eea03d7c 100644 --- a/docs/intro/why-app-specific.md +++ b/docs/intro/why-app-specific.md @@ -1,10 +1,11 @@ # Application-Specific Blockchains +This document explains what application-specific blockchains are, and why developers would want to build one as opposed to writing Smart Contracts. {synopsis} + ## What are application-specific blockchains? Application-specific blockchains are blockchains customized to operate a single application. Instead of building a decentralised application on top of an underlying blockchain like Ethereum, developers build their own blockchain from the ground up. This means building a full-node client, a light-client, and all the necessary interfaces (CLI, REST, ...) to interract with the nodes. @@ -43,20 +44,21 @@ Application-Specific Blockchains are designed to address these shortcomings. Application-specific blockchains give maximum flexibility to developers: -- In Cosmos blockchains, the state-machine is typically connected to the underlying consensus engine via an interface called the [ABCI](https://tendermint.com/docs/spec/abci/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice. +- In Cosmos blockchains, the state-machine is typically connected to the underlying consensus engine via an interface called the [ABCI](https://docs.tendermint.com/v0.34/spec/abci/). This interface can be wrapped in any programming language, meaning developers can build their state-machine in the programming language of their choice. + - Developers can choose among multiple frameworks to build their state-machine. The most widely used today is the Cosmos SDK, but others exist (e.g. [Lotion](https://github.com/nomic-io/lotion), [Weave](https://github.com/iov-one/weave), ...). The choice will most of the time be done based on the programming language they want to use (Cosmos SDK and Weave are in Golang, Lotion is in Javascript, ...). - The ABCI also allows developers to swap the consensus engine of their application-specific blockchain. Today, only Tendermint is production-ready, but in the future other consensus engines are expected to emerge. - Even when they settle for a framework and consensus engine, developers still have the freedom to tweak them if they don't perfectly match their requirements in their pristine forms. - Developers are free to explore the full spectrum of tradeoffs (e.g. number of validators vs transaction throughput, safety vs availability in asynchrony, ...) and design choices (DB or IAVL tree for storage, UTXO or account model, ...). - Developers can implement automatic execution of code. In the Cosmos SDK, logic can be automatically triggered at the beginning and the end of each block. They are also free to choose the cryptographic library used in their application, as opposed to being constrained by what is made available by the underlying environment in the case of virtual-machine blockchains. -The list above contains a few examples that show how much flexibility application-specific blockchains give to developers. The goal of Cosmos and the Cosmos SDK is to make developer tooling as generic and composable as possible, so that each part of the stack can be forked, tweaked and improved without losing compatibility. As the community grows, more alternative for each of the core building blocks will emerge, giving more options to developers. +The list above contains a few examples that show how much flexibility application-specific blockchains give to developers. The goal of Cosmos and the Cosmos SDK is to make developer tooling as generic and composable as possible, so that each part of the stack can be forked, tweaked and improved without losing compatibility. As the community grows, more alternatives for each of the core building blocks will emerge, giving more options to developers. ### Performance -Decentralised applications built with Smart Contracts are inherently capped in performance by the underlying environment. For a decentralised application to optimise performance, it needs to be built as an application-specific blockchains. Next are some of the benefits an application-specific blockchains brings in terms of performance: +Decentralised applications built with Smart Contracts are inherently capped in performance by the underlying environment. For a decentralised application to optimise performance, it needs to be built as an application-specific blockchains. Next are some of the benefits an application-specific blockchain brings in terms of performance: -- Developers of application-specific blockchains can choose to operate with novel consensus engine such as Tendermint BFT. Compared to Proof-of-Work (used by most virtual-machine blockchains today), it offers significant gains in throuhgput. +- Developers of application-specific blockchains can choose to operate with a novel consensus engine such as Tendermint BFT. Compared to Proof-of-Work (used by most virtual-machine blockchains today), it offers significant gains in throuhgput. - An application-specific blockchain only operates a single application, so that the application does not compete with others for computation and storage. This is the opposite of most non-sharded virtual-machine blockchains today, where smart contracts all compete for computation and storage. - Even if a virtual-machine blockchain offered application-based sharding coupled with an efficient consensus algorithm, performance would still be limited by the virtual-machine itself. The real throughput bottleneck is the state-machine, and requiring transactions to be interpreted by a virtual-machine significantly increases the computational complexity of processing them. diff --git a/docs/kr/clients/lite/getting_started.md b/docs/kr/clients/lite/getting_started.md index 44ca6a628b9c..303d0fe132f2 100755 --- a/docs/kr/clients/lite/getting_started.md +++ b/docs/kr/clients/lite/getting_started.md @@ -8,7 +8,6 @@ REST 서버를 가동하기 위해서는 다음과 같은 파라미터 값을 | chain-id | string | null | 필수 | 연결할 체인의 chain-id | | node | URL | "tcp://localhost:46657" | 필수 | 연결할 풀노드의 주소 | | laddr | URL | "tcp://localhost:1317" | 필수 | REST 서버를 가동할 주소 | -| trust-node | bool | "false" | 필수 | 연결할 풀노드의 신뢰가능 여부 | | trust-store | DIRECTORY | "$HOME/.lcd" | 선택 | 체크포인트와 검증인 세트를 저장할 디렉터리 | 예를 들어:: @@ -16,8 +15,7 @@ REST 서버를 가동하기 위해서는 다음과 같은 파라미터 값을 ```bash gaiacli rest-server --chain-id=test \ --laddr=tcp://localhost:1317 \ - --node tcp://localhost:26657 \ - --trust-node=false + --node tcp://localhost:26657 ``` Gaia-Lite RPC에 대한 추가적인 정보를 원하시면 [Swagger 문서](https://cosmos.network/rpc/)를 확인하세요. diff --git a/docs/kr/clients/service-providers.md b/docs/kr/clients/service-providers.md index 1f904473b7b4..2c9b5aaea2c9 100755 --- a/docs/kr/clients/service-providers.md +++ b/docs/kr/clients/service-providers.md @@ -93,7 +93,6 @@ gaiacli advanced rest-server --node= ``` 플래그: -- `--trust-node`: 불리언 값. `true`일 경우, 라이트 클라이언트 검증 절차가 비활성화 됩니다. 만약 `false`일 경우, 절차가 활성화됩니다. 서비스 제공자의 경우 `true` 값을 이용하시면 됩니다. 기본 값은 `true`입니다. - `--node`: 플노드의 주소와 포트를 입력하시면 됩니다. 만약 풀노드와 REST 서버가 동일한 머신에서 운영될 경우 주소 값은 `tcp://localhost:26657`로 설정하시면 됩니다. - `--laddr`: REST 서버의 주소와 포트를 정하는 플래그입니다(기본 값 `1317`). 대다수의 경우에는 포트를 정하기 위해서 사용됩니다, 이 경우 주소는 "localhost"로 입력하시면 됩니다. 포맷은 입니다. diff --git a/docs/kr/intro/README.md b/docs/kr/intro/README.md index 058d0e57f09b..2dbf83838db1 100755 --- a/docs/kr/intro/README.md +++ b/docs/kr/intro/README.md @@ -1,15 +1,16 @@ -# SDK 소개 + +# 소개 +이 폴더에는 Cosmos-SDK에 대한 소개 자료가 포함되어 있습니다. -[Cosmos-SDK](https://github.com/cosmos/cosmos-sdk)는 코스모스 허브 같은 멀티애셋 지분증명(Proof-of-Stake) 블록체인 또는 권한증명(Proof-of-Authority) 같은 블록체인들을 만들 수 있는 하나의 프레임워크입니다. +1. [SDK 소개](./overview.md) +2. [애플리케이션 특화 블록체인](./why-app-specific.md) +3. [SDK 애플리케이션의 아키텍쳐](./sdk-app-architecture.md) +4. [코스모스 SDK 디자인 소개](./sdk-design.md) -코스모스 SDK의 목표는 개발자들이 본인만의 커스텀 블록체인을 처음부터 쉽게 만들고, 이런 블록체인들이 상호호환성을 가질 수 있게 하는 것입니다. 우리는 코스모스 SDK가 [Tendermint](https://github.com/tendermint/tendermint) 기반의 안전한 블록체인 애플리케이션의 npm 프레임워크 같은 역할을 할수 있을 것을 기대하고 있습니다. - -코스모스 SDK는 다음과 같은 원칙에 의거합니다: - -- **구성성:** 누구나 코스모스-SDK 모듈을 만들 수 있다. 또한 이미 만들어진 기존 모듈을 내가 만드려는 블록체인 애플리케이션에 손쉽게 도입할 수 있다. - -- **능력성:** 코스모스-SDK는 능력기반 보안(capabilities-based security)과 수년의 블록체인 스테이트 머신에 대한 고민을 기반으로 만들어졌습니다. 대다수의 개발자들은 본인들의 모듈을 개발할때 제3자의 모듈을 사용해야되는 경우가 많습니다. 코스모스-SDK는 오픈 프레임워크이기 때문에, 일부 악의적인 모듈이 존재할 수 있다는 것을 배제할 수 없습니다. 이런 환경에서는 보안과 안전성을 확보하기 위해 모듈간 인터랙션에서 기본적인 보안 원칙이 존재해야 합니다. 이런 원칙은 오브젝트-능력(object-capabilities) 기반으로 만들어졌습니다. 각 모듈이 다른 모든 모듈들의 액세스 권한 리스트를 관리하기 보다, 각 모듈은 키퍼(Keeper)라는 특수 오브젝트를 도입할 수 있습니다. 이런 '키퍼' 들은 다른 모듈에게 전달되어 사전에 정의된 능력(기능)을 수행할 수 있는 권한을 부여합니다. 예를 들어, 모듈 A의 키퍼가 모듈 B에게 전달되었을 경우, 모듈 B는 사전에 정의되어있는 한정된 모듈 A의 기능을 호출할 수 있습니다. 각 키퍼가 수행할 수 있는 기능은 해당 모듈의 개발자가 정의할 수 있으며, 모듈 기능으로 발생할 수 있는 안전/보안 문제를 감사하는 책임은 각 개발자에게 있습니다. 능력(capability)에 대한 더 자세한 설명은 다음 [링크](./ocap.md)에서 확인하실 수 있습니다. - -### 다음은 [SDK 애플리케이션 아키텍쳐](./sdk-app-architecture.md)에 대해서 알아보겠습니다 \ No newline at end of file +해당 기본 자료를 읽으신 후 [basics](../basics/README.md) 폴더에 있는 자료를 읽어보시는 것을 추천드립니다. \ No newline at end of file diff --git a/docs/kr/intro/overview.md b/docs/kr/intro/overview.md new file mode 100644 index 000000000000..1056410c0a5b --- /dev/null +++ b/docs/kr/intro/overview.md @@ -0,0 +1,33 @@ +--- +order: 1 +--- + +# SDK 소개 + +## 코스모스 SDK는 무엇인가? + +[코스모스 SDK](https://github.com/cosmos/cosmos-sdk)는 코스모스 허브와 같은 다수 자산(multi-asset) 퍼블릭 지분증명 블록체인과 권한증명(PoA, Proof-of-Authority) 블록체인을 만들 수 있는 오픈소스 프레임워크입니다. 코스모스 SDK를 사용하여 만들어진 블록체인은 통상 **애플리케이션 특화 블록체인(application-specific blockchain)**이라 불립니다. + +코스모스 SDK의 목적은 개발자가 간편하게 처음부터 다른 블록체인과 상호환이 가능한 블록체인을 만들 수 있게 하는 것이 목적입니다. 코스모스 SDK는 npm과 같은 프레임워크로 자리잡으며 [텐더민트](https://github.com/tendermint/tendermint) 상의 안전한 블록체인 애플리케이션을 만들 수 있게 자리잡는 것이 목표입니다. SDK 기반 블록체인은 구성적(composable) 모듈을 기반으로 만들어지며, 대다수의 모듈은 오픈 소스로 모든 개발자가 사용할 수 있습니다. 누구나 코스모스 SDK 모듈을 만들 수 있으며, 단순히 블록체인 애플리케이션에 모듈을 불러와 이미 개발된 모듈을 간편하게 사용할 수 있습니다. 또한, 코스모스 SDK는 능력성 기반(capabilities-based) 시스템으로 모듈 간 인터랙션의 보안성을 더욱 직관적으로 설계할 수 있습니다. 능력성 기반 시스템에 대해 더 알고싶으시다면 [이 항목](./ocap.md)을 참고하세요. + +## 애플리케이션 특화 블록체인은 무엇인가? + +최근 블록체인 업계의 개발 패러다임 중 하나는 이더리움과 같은 버추얼 머신(virtual-machine) 기반 블록체인입니다. 이런 시스템에서는 기존 블록체인 상위에 스마트 컨트랙트 기반의 탈중앙화 애플리케이션을 만드는 형태로 개발됩니다. 스마트 컨트랙트는 단순 애플리케이션 (예, ICO) 용도로 적합하지만, 복잡한 탈중앙화 플랫폼을 개발하는 목적에는 적합하지 않습니다. 스마트 컨트랙트는 유연성, 존엄성 그리고 성능 측면에서 한계가 존재합니다. + +애플리케이션 특화 블록체인은 기존 버추얼 머신 기반 블록체인과는 획기적으로 다른 개발 패러다임을 제공합니다. 애플리케이션 특화 블록체인은 하나의 애플리케이션을 구동하기 위해 특화되며, 개발자는 애플리케이션이 최적한 환경에서 구동될 수 있는 디자인 결정을 내릴 수 있습니다. 또한 애플리케이션 특화 블록체인은 애플리케이션 존엄성, 보안 그리고 성능 측면에서 장점을 가집니다. + +애플리케이션 특화 블록체인에 대한 더 자세한 정보는 [여기](./why-app-specific.md)를 참고하세요. + +## 왜 코스모스 SDK인가? + +코스모스 SDK는 고유 애플리케이션 특화 블록체인을 만들기 위한 가장 우수한 프레임워크입니다. 다음은 코스모스 SDK가 탈중앙화 애플리케이션을 개발하는데 제공하는 장점들입니다: + +- 코스모스 SDK가 기본적으로 제공하는 합의 엔진은 [텐더민트 코어](https://github.com/tendermint/tendermint)입니다. 텐더민트는 가장 오랜 기간 증명된 BFT 기반 컨센서스 엔진입니다. 텐더민트 코어는 업계 내의 수 많은 환경에서 지분증명 시스템을 개발하는데 사용되고 있습니다. +- 코스모스 SDK는 오픈소스이며 구성적(composable) 모듈을 기반으로 간편하게 블록체인을 만들 수 있습니다. 블록체인 생태계가 성장하며 더욱 다양한 코스모스 SDK 모듈들이 개발되고 있으며, 이런 모듈들을 기반으로 복잡한 탈중앙화 플랫폼을 개발하는 과정이 간소화됩니다. +- 코스모스 SDK는 능력성 기반(capabilities-based) 보안 아키텍쳐를 기반으로 설계되었습니다. 이런 디자인 결정은 수년간 블록체인 스테이트 머신(state machine)에 대한 고민을 기반으로 설계되었으며, 더욱 안전한 블록체인을 개발할 수 있는 환경을 제공합니다. +- 가장 중요한 것은 바로 코스모스 SDK가 실제 작동하고 있는 다수의 애플리케이션 특화 블록체인을 개발하는데 사용되고 있다는 점입니다. [코스모스 허브](https://hub.cosmos.network), [아이리스 허브](https://irisnet.org), [바이낸스 체인](https://docs.binance.org), [테라](https://terra.money), [Lino](https://lino.network) 등의 프로젝트가 코스모스 SDK 기반으로 개발되었습니다. 코스모스 기반 프로젝트 목록은 [여기](https://cosmos.network/ecosystem)에서 확인하실 수 있습니다. + +## 코스모스 SDK 입문하기 + +- [코스모스 SDK 기반 애플리케이션의 구조](./sdk-app-architecture.md)에 대해서 더 알아보세요 +- 애플리케이션 특화 블록체인을 처음부터 개발하는 방법을 [코스모스 SDK 투토리얼](https://cosmos.network/docs/tutorial)을 통해 배워보세요 diff --git a/docs/kr/intro/sdk-app-architecture.md b/docs/kr/intro/sdk-app-architecture.md index 05d63900ce30..bb564354464a 100755 --- a/docs/kr/intro/sdk-app-architecture.md +++ b/docs/kr/intro/sdk-app-architecture.md @@ -1,71 +1,74 @@ + + + # SDK 애플리케이션 아키텍쳐 -## 스테이트 머신 (상태 기계, State machine) +## 상태 기계 (state machine) 블록체인 애플리케이션은 근본적으로 [결정론적 복제 상태 기계(replicated deterministic state machine)](https://ko.wikipedia.org/wiki/%EC%83%81%ED%83%9C_%EA%B8%B0%EA%B3%84_%EB%B3%B5%EC%A0%9C)입니다. -스테이트 머신은 특정 기계(머신, machine)이 오랜 기간동안 다수의 상태(스테이트, state)를 보유할 수 있지만, 특정 시점에 오직 하나의 상태를 유지하는 있는 컴퓨터 공학 개념입니다. 여기서 '스테이트 머신' 개념에는 시스템의 현 상태를 뜻하는 '스테이트(state)'가 있으며, 스테이트의 변경을 유발하는 +상태 기계는 특정 시점에 오직 하나의 상태를 유지하는 있는 컴퓨터 공학 개념입니다. 여기서 '상태 기계' 개념에는 시스템의 현 상태를 뜻하는 '상태(state)'가 있으며, 상태의 변경을 유발하는 트랜잭션(transaction)'이 있습니다. -`S` 라는 스테이트와 `T` 라는 트랜잭션이 있는 경우, 스테이트 머신은 `S'`라는 새로운 스테이트를 리턴합니다. +`S` 라는 상태와 `T` 라는 트랜잭션이 있는 경우, 상태 기계는 `S'`라는 새로운 상태를 리턴합니다. ``` +--------+ +--------+ | | | | | S +---------------->+ S' | -| | apply(T) | | +| | (T)적용 | | +--------+ +--------+ ``` -실전에서는 트랜잭션들이 일종의 '블록' 단위로 묶여 스테이트 변경 과정을 더 효율적일 수 있게 합니다. `S`라는 스테이트와 `B`라는 트랜잭션들이 있는 경우, 스테이트 머신은 `S'`라는 새로운 스테이트를 리턴합니다. +실질적으로는 트랜잭션은 블록 단위로 묶여 상태 변경 과정을 더 효율적일 수 있게 합니다. `S`라는 상태와 `B`라는 트랜잭션이 있는 경우, 상태 기계는 `S'`라는 새로운 상태를 리턴합니다. ``` +--------+ +--------+ | | | | | S +----------------------------> | S' | -| | For each T in B: apply(T) | | +| | B의 T 수만큼: (T)적용 | | +--------+ +--------+ ``` -블록체인이라는 시스템 내의 개념으로 볼때 스테이트 머신은 결정론적(deterministic)입니다. 즉, 특정 스테이트에서 시작한 후 동일한 트랜잭션들을 반복할 경우, 결과 스테이트는 언제나 동일합니다. +블록체인 시스템 개념으로 점그할때, 상태 기계는 결정론적(deterministic)입니다. 즉, 노드가 특정 상태에서 시작된 후 동일한 트랜잭션들을 반복할 경우, 결과 상태는 언제나 동일합니다. -코스모스 SDK는 애플리케이션의 스테이트, 트랜잭션 형태 그리고 스테이트 변경 기능(state-transition function)을 정의하는데 최대한의 유연성을 제공합니다. 코스모스 SDK를 이용해 스테이트 머신을 만드는 과정은 이후 항목에서 다루겠습니다. 우선 이 시스템 내에서 어떻게 **텐더민트**를 사용해 '스테이트'가 복제되는지 알아보겠습니다. +코스모스 SDK는 애플리케이션의 상태, 트랜잭션 형태 그리고 상태 변경 기능(state-transition function)을 정의하는데 최대한의 유연성을 제공합니다. 코스모스 SDK를 이용해 상태 기계를 만드는 과정은 이후 항목에서 다루겠습니다. 우선 이 시스템 내에서 어떻게 **텐더민트**를 사용해 '상태'가 복제되는지 알아보겠습니다. ## 텐더민트 -개발자는 코스모스 SDK를 사용하여 스테이트 머신만을 정의하면 되며, 해당 스테이트를 네트워크에 복제하는 기능은 [*텐더민트*](https://tendermint.com/docs/introduction/introduction.html)가 제공합니다. +개발자는 코스모스 SDK를 사용하여 상태 기계만을 정의하면 되며, 해당 상태를 네트워크에 복제하는 기능은 [*텐더민트*](https://tendermint.com/docs/introduction/introduction.html)가 제공합니다. ``` ^ +-------------------------------+ ^ | | | | 코스모스 SDK로 개발 - | | 스테이트 머신 = 애플리케이션 | | + | | 상태 기계 = 애플리케이션 | | | | | v | +-------------------------------+ | | | ^ - 블록체인 노드 | | 컨센서스 | | + 블록체인 노드 | | 컨센서스 | | | | | | | +-------------------------------+ | 텐더민트 코어 | | | | - | | 네트워킹 | | + | | 네트워킹 | | | | | | v +-------------------------------+ v ``` -텐더민트는 오직 블록체인의 *네트워킹*과 *합의(컨센서스)* 계층을 처리하는 애플리케이션-불가지성(application-agnostic) 엔진입니다. 실전적으로 볼때, 텐더민트는 단순히 트랜잭션의 바이트를 나열하고, 해당 트랜잭션을 네트워크에 전파하는 역할만을 한다는 것입니다. 텐더민트 코어는 텐더민트 비잔틴 결함 감내(BFT, Byzantine-fault tolerant) 알고리즘을 사용하여 트랜잭션 순서에 대한 합의를 합니다. 텐더민트에 대해서 더 알고싶으시다면 [여기](https://tendermint.com/docs/introduction/introduction.html)를 확인하세요. - -텐더민트 합의 알고리즘은 '검증인(Validators)'이라는 특정 노드 세트를 기반으로 작동합니다. 검증인의 주 역할은 트랜잭션을 블록 단위로 블록체인에 추가하는 것입니다. 특정 블록에는 V 검증인 세트 검증인 존재하며, 이 V 검증인 세트 안에 있는 검증인 중 하나의 검증인이 다음 블록 생성자로 알고리즘에 의해 선택됩니다. 만약 블록이 V 검증인 세트 2/3 이상의 [프리보트(prevote)](https://tendermint.com/docs/spec/consensus/consensus.html#prevote-step-height-h-round-r)와 [프리커밋(precommit)](https://tendermint.com/docs/spec/consensus/consensus.html#precommit-step-height-h-round-r)을 받고 내용 트랜잭션이 유효한 경우 해당 블록은 '유효(valid)'한 것으로 간주됩니다. 검증인 세트는 스테이트 머신에 작성된 규칙에 따라서 바뀔 수 있습니다. 알고리즘에 대해 더 자세한 정보는 [여기](https://tendermint.com/docs/introduction/what-is-tendermint.html#consensus-overview)를 참고하세요. +텐더민트는 오직 블록체인의 *네트워킹*과 *합의(컨센서스)* 계층을 처리하는 애플리케이션-불가지성(application-agnostic) 엔진입니다. 실질적으로 텐더민트는 단순히 트랜잭션의 바이트를 나열하고, 해당 트랜잭션을 네트워크에 전파하는 역할만을 한는 것입니다. 텐더민트 코어는 텐더민트 비잔틴 결함 감내(BFT, Byzantine-fault tolerant) 알고리즘을 사용하여 트랜잭션 순서에 대한 합의를 합니다. -코스모스 SDK 애플리케이션의 주요 파트는 네트워크에 포함되어있는 노드가 각자 로컬 환경에서 운영하는 블록체인 데몬(daemon)입니다. 만약 *검증인 세트*의 1/3 이하가 악의적(byzantine)인 경우, 각 노드는 동시에 스테이트를 조회할때 동일한 결과를 받게됩니다. +텐더민트 합의 알고리즘은 '검증인(Validators)'이라는 특정 노드 세트를 기반으로 작동합니다. 특정 블록에는 V 검증인 세트 검증인 존재하며, 이 V 검증인 세트 안에 있는 검증인 중 하나의 검증인이 다음 블록 생성자로 알고리즘에 의해 선택됩니다. 만약 블록이 V 검증인 세트 2/3 이상의 [프리보트(prevote)](https://tendermint.com/docs/spec/consensus/consensus.html#prevote-step-height-h-round-r)와 [프리커밋(precommit)](https://tendermint.com/docs/spec/consensus/consensus.html#precommit-step-height-h-round-r)을 받고 내용 트랜잭션이 유효한 경우 해당 블록은 '유효(valid)'한 것으로 간주됩니다. 검증인 세트는 상태 기계에 작성된 규칙에 따라서 바뀔 수 있습니다. ## ABCI -텐더민트는 네트워크에서 어플리케이션으로 거래를 넘겨줍니다. 이때, ABCI 라고 불리우는 인터페이스를 사용합니다. 그리고 이는 어플리케이션이 반드시 구현해야하는 부분입니다. +텐더민트는 ABCI라를 인터페이스를 사용해 트랜잭션을 애플리케이션에게 전달합니다. 이는 어플리케이션이 반드시 구현해야하는 부분입니다. ``` +---------------------+ | | -| Application | +| 애플리케이션 | | | +--------+---+--------+ ^ | @@ -74,7 +77,7 @@ +--------+---+--------+ | | | | -| Tendermint | +| 텐더민트 | | | | | +---------------------+ @@ -84,14 +87,14 @@ 아래에 ABCI 의 메세지들 중 가장 중요한 것들을 나열해놓았습니다: -- `CheckTx`: 텐더민트 코어로부터 거래를 받게 될 때, 이 거래는 어플리케이션에 넘겨져서 몇가지 기본 요건을 잘 만족했는지를 확인합니다. `CheckTx` 는 풀노드의 mempool을 스팸행위로 부터 보호하는데 사용됩니다. "Ante Handler" 라고 불리우는 특별한 handler 는 일련의 검증 과정을 실행하는데 사용됩니다. 예를 들면, 충분한 수수료가 있는지와 서명이 유효한지를 확인합니다. 만약 검사 결과가 유효한 것으로 나오게 되면, 그 거래는 [mempool](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality) 에 더해지게 됩니다. 그리고 피어 노드들에게도 전달됩니다. 거래가 블록에 담기기 전까지는 `CheckTx` 과정은 진행되지 않는다. (즉, 상태의 변경이 일어나지 않는다.) +- `CheckTx`: 텐더민트 코어로부터 거래를 받게 될 때, 이 거래는 어플리케이션에 넘겨져서 몇 가지 기본 요건을 충족하는지 확인합니다. `CheckTx` 는 풀노드의 mempool을 스팸행위로 부터 보호하는데 사용됩니다. "Ante Handler" 라고 불리우는 특별한 handler 는 일련의 검증 과정을 실행하는데 사용됩니다. 예를 들면, 충분한 수수료가 있는지, 그리고 서명이 유효한지 확인합니다. 만약 검사 결과가 유효한 경우 되면, 해당 거래는 [mempool](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality)에 추가되고 피어 노드에게 전달됩니다. 참고로 트랜잭션이 블록에 추가되기 전까지는 `CheckTx` 과정이 진행되지 않습니다. (즉, 상태의 변경이 일어나지 않습니다.) -- `DelieverTx` : [유효한 블록](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation)이 텐더민트 코어에 의해서 전달되었을 때, 해당 블록에 있는 모든 개별 거래들은 `DeliverTx`에 의해서 어플리케이션에게 전달됩니다. 상태 변환이 일어나는 단계가 이 단계이다. "Ante Handler" 는 실제 handler 와 함께 거래 내의 각 메세지에 대한 검증을 위해 다시 실행한다. +- `DeliverTx` : 텐더민트 코어가 [유효한 블록](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation)을 전달받는 경우, 각 블록의 트래잭션은 `DeliverTx`를 통해 애플리케이션에 전달합니다. 이 단계에서 상태 변경이 일어납니다. `AnteHandler`는 트랜잭션에 포함된 각 메세지를 검증하기 위해 다시 실행됩니다. -- `BeginBlock`/`EndBlock` : 이 메세지들은 블록이 거래를 담고 있던 아니던 각 블록의 시작과 끝에 실행됩니다. 과정을 자동화 해놓으면 상당히 유용할 것입니다. 하지만, 진행 중 주의해야 할 사안이 하나 있습니다. 과도하게 컴퓨팅 자원이 많이 필요로 하는 루프는 블록체인의 속도 면에서 악영향을 끼칠 수 있고 만약 무한 루프라면 작동을 멈출 수도 있습니다. +- `BeginBlock`/`EndBlock` : 해당 메세지는 블록내 트랜잭션 유뮤와는 별개로 블록 시작과 끝 단계에서 실행됩니다. 여기에서 로직의 자동 실행을 설정하는 것이 유용합니다. 하지만 복잡한 연산 또는 루프는 블록체인의 속도를 저하할 수 있으며, 무한 루프의 경우 블록체인을 멈출 수 있습니다. -ABCI 메소드와 타입에 대해서 더 자세하게 싶다면, [여기](https://tendermint.com/docs/spec/abci/abci.html#overview)를 클릭하십시오. +ABCI 메소드와 타입에 대해서 더 자세하게 싶다면, [텐더민트 문서](https://tendermint.com/docs/spec/abci/abci.html#overview)를 참고하세요. -텐더민트 상에서 구현된 모든 어플리케이션은 ABCI 인터페이스를 구현해야만 합니다. 그래야 로컬 텐더민트 엔진과 통신할 수 있습니다. 다행스럽게도, 당신이 스스로 직접 ABCI 인터페이스를 구현할 필요는 없습니다. Cosmos SDK 가 [baseapp](https://cosmos.network/docs/intro/sdk-design.html#baseapp) 의 형태로 boilerplate 를 제공합니다. +텐더민트 위에 구현된 모든 어플리케이션은 하위 텐더민트 엔진과 소통하기 위해 ABCI 인터페이스를 구현해야만 합니다. 물론 코스모스 SDK를 사용하는 경우, 코스모스 SDK가 [baseapp](https://cosmos.network/docs/intro/sdk-design.html#baseapp) 의 형태로 일종의 템플릿를 제공합니다. -### 자 이제 다음단계로 [SDK 고수준 설계 원칙에 대해서 알아보자.](https://cosmos.network/docs/intro/sdk-design.html#baseapp) +### 다음은 [SDK 설계 원칙에 대해서 알아보세요](https://cosmos.network/docs/intro/sdk-design.html#baseapp) diff --git a/docs/kr/intro/sdk-design.md b/docs/kr/intro/sdk-design.md new file mode 100644 index 000000000000..ef0cf63b80d5 --- /dev/null +++ b/docs/kr/intro/sdk-design.md @@ -0,0 +1,95 @@ + + +# 코스모스 SDK의 주요 구성 요소 + +코스모스 SDK는 텐더민트 위에서 안전한 상태 기계를 구현하는 프레임워크입니다. 핵심적으로, 코스모스 SDK는 일종의 Go 언어로 구현된 [ABCI](./sdk-app-architecture.md#abci) 구현체 템플릿입니다. 코스모스 SDK에는 데이터를 지속하는 [`multistore`](../core/store.md#multistore)와 트래잭션을 처리하는 [`router`](../core/baseapp.md#routing)가 내장되어 있습니다. + +다음은 코스모스 SDK로 만들어진 애플리케이션에서 텐더민트에서 `DeliverTx`로 전달된 트랜잭션이 처리되는 과정을 간소화한 설명입니다: + +1. 텐더민트 컨센서스 엔진(텐더민트는 `[]bytes`만을 다룬다는 것을 참고하세요)에서 전달된 `transaction`을 디코딩 +2. `transaction`에서 `messages`를 추출한 후 기본적인 타당성 검사를 진행 +3. 각 메시지가 처리될 수 있게 올바른 모듈에게 라우팅 +4. 상태 변경 실행 + +## `baseapp` + +`baseapp`은 코스모스 SDK 애플리케이션 구현체 템플릿입니다. 하위 컨센서스 엔진과 연결을 처리하기 위한 구현체가 포함 되어있습니다. 통상, 코스모스 SDK 애플리케이션에서는 [`app.go`](../basics/app-anatomy.md#core-application-file)내에 `baseapp`을 임베딩합니다. 이에 대한 예시는 코스모스 SDK 애플리케이션 튜토리얼을 참고하세요: + ++++ https://github.com/cosmos/sdk-tutorials/blob/c6754a1e313eb1ed973c5c91dcc606f2fd288811/app.go#L72-L92 + +`baseapp`의 목표는 스토어와 확장 가능한 상태 기계간의 안전한 인터페이스를 제공함과 동시에 상태 기계를 최소한으로 정의하는(ABCI 디자인 목적에 따라) 것입니다. + +`baseapp`에 대한 추가 정보는 [여기](../core/baseapp.md)를 참조하세요. + +## 멀티스토어 + +코스모스 SDK는 지속되는 상태를 위해 [`멀티스토어/multistore`](../core/store.md#multistore)를 제공합니다.멀티스토어는 개발자가 원하는 수량의 [`KVtore`](../core/store.md#base-layer-kvtores)를 선언할 수 있도록 합니다. `KVStore`는 오직 `[]byte` 타입만을 유효한 값으로 받기 때문에 자체 스트럭쳐는 [코덱](../core/encoding.md)을 사용해 마셜된 후 저장되어야 합니다. + +멀티스토어를 추상화한 이유는 상태를 구획화하기 위한 목적이 있으며, 각자 해당되는 모듈에 의해 관리됩니다. 멀티스토어 대한 추가 정보는 [여기](../core/store.md#multistore)를 확인하세요. + +## 모듈 + +코스모스 SDK의 강점은 바로 모듈성에 있습니다. SDK 기반 애플리케이션은 다수의 상호 호환되는 모듈을 모아서 개발됩니다. 각 모듈은 상태의 특정 분야를 정의하고 자체적인 메시지/트랜잭션 처리 기능을 가지고 있으며, 코스모스 SDK는 각 메시지를 해당되는 모듈에 라우팅하는 역할을 합니다. + +다음은 어떻게 트랜잭션이 각 풀노드가 유효한 블록을 받았을때 애플리케이션에 의해 처리되는지를 설명합니다: + +``` + + + | + | DeliverTx를 통해 풀노드의 텐더민트에서 + | 노드의 애플리케이션으로 전달된 트랜잭션 + | + | + | + +---------------------v--------------------------+ + | 애플리케이션 | + | | + | 베이스앱의 메서드를 사용해: Tx 디코딩, | + | 메시지 추출 후 라우팅 | + | | + +---------------------+--------------------------+ + | + | + | + +---------------------------+ + | + | + | + | 처리를 위해 올바른 + | 모듈로 라우팅된 메시지 + | + | ++----------------+ +---------------+ +----------------+ +------v----------+ +| | | | | | | | +| AUTH 모듈 | | BANK 모듈 | | STAKING 모듈 | | GOV 모듈 | +| | | | | | | | +| | | | | | | 메시지 처리, | +| | | | | | | 상태 업데이트 | +| | | | | | | | ++----------------+ +---------------+ +----------------+ +------+----------+ + | + | + | + | + +--------------------------+ + | + | 결과 값 텐더민트로 전달 + | (0=Ok, 1=Err) + v +``` + +각 모듈은 미니 상태 기계로 볼 쑤 있습니다. 개발자는 각 모듈이 처리하는 상태의 부분과 상태를 바꾸는 고유 메시지 타입을 정의해야합니다(*참고*: 메시지는 트랜잭션에서 `baseapp`을 통해 추출됩니다). 통상적으로, 각 모듈은 각자의 `KVStore`를 `multistore`에 선언하여 해당 모듈이 정의하는 상태의 부분을 지속합니다. 개발자는 본인의 모듈을 만들때 제 삼자의 모듈을 액세스해야할 수 있습니다. 코스모스-SDK는 오픈 프레임워크이기 때문에, 일부 모듈은 악성 모듈일 가능성이 존재하며 이런 인터-모듈 소통에서 보안 원칙이 존재합니다. 이런 원칙은 [오브젝트-가능성(object-capabilities)](../core/ocap.md)를 따릅니다. 실전에서는, 각 모듈이 다른 모듈의 액세스 권한 리스트를 관리하는 것이 아니라, 각 모듈이 `keeper`라는 특별 오브젝트를 구현하여 접근 가능한 권한을 사전에 정의합니다. + +SDK의 모듈은 `x/` 폴더 내에 정의되며, 핵심 모듈 중 일부는 다음과 같습니다: + +- `x/auth`: 계정과 서명 관리에 사용됨 +- `x/bank`: 토큰과 토큰 전송 기능에 사용됨 +- `x/staking` + `x/slashing`: 지분증명 블록체인을 만들기 위해 사용됨 + +모두가 본인의 앱을 만들기 위해 사용할 수 있는 `x/` 내 존재하는 모듈 외에도, 코스모스 SDK는 자체 모듈을 개발할 수 있도록합니다. 이에 대한 예시는 [이 튜토리얼](https://cosmos.network/docs/tutorial/keeper.html)을 참고하세요. + +## 다음 {hide} + +[코스모스 SDK 애플리케이션 해부학](../basics/app-anatomy.md)에 대해서 알아보세요 {hide} diff --git a/docs/kr/intro/why-app-specific.md b/docs/kr/intro/why-app-specific.md new file mode 100644 index 000000000000..e52efcd83d7c --- /dev/null +++ b/docs/kr/intro/why-app-specific.md @@ -0,0 +1,80 @@ + + +# 애플리케이션 특화 블록체인 + +이 문서는 애플리케이션 특화 블록체인(application-specific blockchain)을 정의하고, 왜 개발자들이 스마트 컨트랙트 대신 애플리케이션 특화 블록체인을 사용하는데 장점이 있는지 설명합니다. {synopsis} + +## 애플리케이션 특화 블록체인은 무엇인가? + +애플리케이션 특화 블록체인은 하나의 애플리케이션을 운영하는데 특화된 블록체인입니다. 이더리움과 같이 하나의 블록체인 플랫폼 위에서 탈중앙화 애플리케이션을 만드는 것이 아니라, 애플리케이션 특화 블록체인은 블록체인의 모든 계층을 직접 만드는 형태입니다. 즉, 풀 노드 클라이언트, 라이트 클라이언트, 필수 인터페이스(CLI, REST, 등)를 노드와 소통하도록 개발하게 됩니다 + +``` + ^ +-------------------------------+ ^ + | | | | 코스모스 SDK로 개발 + | | 상태 기계 = 애플리케이션 | | + | | | v + | +-------------------------------+ + | | | ^ + 블록체인 노드 | | 컨센서스 | | + | | | | + | +-------------------------------+ | 텐더민트 코어 + | | | | + | | 네트워킹 | | + | | | | + v +-------------------------------+ v +``` + +## 스마트 컨트랙트의 한계는 무엇인가? + +이더리움과 같은 버추얼 머신(virtual machine) 기반 블록체인은 2014년 당시 블록체인의 프로그램화에 대한 수요를 충족했습니다. 다수의 개발자는 복잡하고 한계가 있는 비트코인 스크립트 언어로 애플리케이션을 개발하거나, 수정하기 어려운 비트코인 코드 포크해야하는 어려움이 있었기 때문입니다. + +이런 시기에, 버추얼 머신 기반 블록체인은 새로운 가치 제안을 하게됩니다. 상태 기계(state machine)가 일종의 버추얼 머신을 운용하여 '스마트 컨트랙트'라는 튜링 완전한 프로그램을 실행할 수 있도록 했습니다. 이런 스마트 컨트랙트는 일시적 이벤트에 매우 적절하지만 (예, ICO) 복잡한 탈중앙화 플랫폼을 개발하는대에는 한계가 있습니다. 이유는 다음과 같습니다: + +- 스마트 컨트랙트는 통상 해당 버추얼 머신이 이해할 수 있는 특정 언어만으로만 개발될 수 있습니다. 이런 언어들은 다른 언어에 비교해 미숙한 부분이 많으며, 근본적으로 버추얼 머신의 한계에 의해 제한됩니다. 예를 들어, 이더리움 버추얼 머신(Ethereum Virtual Machine, EVM)은 개발자의 코드의 자동 실행을 허용하지 않습니다. 또한 개발자는 EVM의 계정 시스템을 사용해야 하며, 제한된 함수만으로 암호학 연산을 할 수 있습니다. 위 예시들은 통상 스마트 컨트랙트 플랫폼의 근본적인 **유연성의 부족함**을 보여줍니다. +- 모든 스마트 컨트랙트는 동일한 버추얼 머신에서 실행됩니다. 이는 근본적으로 모든 스마트 컨트랙트가 한정된 자원을 위해 경쟁해야하는 구조며 **성능**의 한계를 불러옵니다. 상태 기계가 (샤딩 같은 기술을 통해) 다수의 서브 세트(subset)로 나눈다는 가정에서도, 스마트 컨트랙트는 동일한 버추얼 머신에서 실행되어야 하기 때문에 상태 기계단에서 구현된 네이티브 애플리케이션 보다 많은 성능 제한이 있습니다(내부적인 벤치마크 테스트에 따르면, 버추얼 머신을 제거함으로 약 10배 가량의 성능 증가가 있었습니다). +- 또 다른 문제는 모든 스마트 컨트랙트가 근본적으로 동일한 환경을 공유하기 때문에 **독립성(sovereignty)**에 대한 한계가 존재합니다. 탈중앙화 애플리케이션은 다수의 이해관계자들이 존재하는 생태계입니다. 만약 애플리케이션이 범용적 버추얼 머신 블록체인에서 개발되는 경우, 애플리케이션의 이해관계자는 본인 애플리케이션에 대한 주권이 제한되며 하위 블록체인 거버넌스를 따라야합니다. 만약 애플리케이션에 버그가 존재하는 경우, 조치할 수 있는 방법이 많이 존재하지 않습니다. + +애플리케이션 특화 블록체인은 이런 한계점을 극복하기 위해 설계되었습니다. + +## 애플리케이션 특화 블록체인의 장점 + +### 유연성 + +애플리케이션 특화 블록체인은 개발자에게 최고의 유연성을 제공합니다: + +- 코스모스 블록체인의 경우, 상태 기계는 [ABCI](https://tendermint.com/docs/spec/abci/)라는 인터페이스를 통해 하위 컨센서스 엔진과 연결됩니다. 이런 인터페이스는 어떤 프로그램 언어로 구현될 수 있으며, 이는 개발자가 본인이 선호하는 프로그램 언어로 상태 기계를 구현할 수 있다는 것을 의미합니다. +- 개발자는 본인의 상태 기계를 구현하기 위해서 다수의 프레임워크 중 하나를 선택할 수 있습니다. 현재 가장 많이 사용되는 프레임워크는 코스모스 SDK이지만, 다른 프레임워크(예, [Lotion](https://github.com/nomic-io/lotion), [Weave](https://github.com/iov-one/weave), 등)의 프레임워크가 존재합니다. 통상 프레임워크는 개발자가 선호하는 언어에 따라 결정됩니다 (예, 코스모스 SDK와 Weave는 Go언어, Lotion은 자바스크립트 등). +- ABCI는 개발자가 본인의 애플리케이션 특화 블록체인의 컨센서스 엔진을 변경할 수 있게 합니다. 현재로써는 텐더민트만이 검증된 프레임워크이지만, 앞으로 더 많은 컨센서스 엔진이 존재할 것으로 예상됩니다. +- 개발자가 특정 프레임워크와 컨센서스 엔진을 선택하는 경우에도, 해당 개발자는 해당 프레임워크와 엔진을 본인이 필요하는대로 변경할 수 있는 자유가 있습니다. +- 개발자는 다수의 트레이오프(예, 검증인의 숫자 vs 트랜잭션 처리량, 안전성 vs 비동기적 환경에서의 생존성 등)와 디자인 아키텍쳐(스토리지에서 DB 또는 IAVL 트리를 사용할지, UTXO 또는 계정 모델을 사용할지)를 선택할 수 있습니다. +- 개발자는 코드의 자동 실행을 구현할 수 이습니다. 코스모스 SDK에서는 매 블록의 시작과 끝에서 로직이 자동으로 실행될 수 있게 설계할 수 있습니다. 또한 사용하는 버추얼 머신의 환경에 극한되지 않게 애플리케이션에서 사용될 암호학을 직접 선택할 수 있습니다. + +위 항목들은 애플리케이션 특화 블록체인이 개발자에게 주는 유연성의 예시를 보여줍니다. 코스모스와 코스모스 SDK의 목표는 개발자 툴링이 최대한 일반적(generic)이고 구성성(composable)있도록 만드는 것에 있기때문에, 스택의 각 컴포넌트가 포크되고, 변경되고, 개선되어도 호환성을 지킬 수 있도록 설계되었습니다. 커뮤니티가 성장할수록 코어 모듈들에 대한 대안이 나올 것으로 기대되며, 이는 개발자들에게 더 많은 선택권을 제공하게 됩니다. + +### 성능 + +스마트 컨트랙트의 형태로 구현되는 탈중앙화 애플리케이션은 하위 환경의 성능에 제한될 수밖에 없습니다. 탈중앙화 애플리케이션이 성능을 최적화하기 위해서는 애플리케이션 특화 블록체인으로 구성되어야 합니다. 다음은 애플리케이션 특화 블록체인의 성능적인 장점을 설명합니다: + +- 애플리케이션 특화 블록체인 개발자는 텐더민트 같은 새로운 컨센서스 엔진을 사용할 수 있으며, 이는 작업증명(Proof-of-work, PoW)에 비해서 상당한 성능 개선을 제공합니다. +- 애플리케이션 특화 블록체인은 하나의 애플리케이션만을 실행하기 때문에 해당 애플리케이션은 다른 애플리케이션과 스토리지와 연산력에 대한 경쟁을 하지 않습니다. 이는 연산력과 스토리지를 위해 다른 애플리케이션과 경쟁해야하는 기존 (샤딩을 도입하지 않은) 버추얼 머신 기반 블록체인 시스템과 대치합니다. +- 만약 버추얼 머신 기바의 블록체인이 애플리케이션 기반 샤딩과 효율적인 컨센서스 알고리즘을 제공한다고 해도, 애플리케이션의 성능은 버추얼 머신 자체에 의해 제한됩니다. 처리량(throughput)에 대한 한계는 상태 기계이며, 트랜잭션이 버추얼 머신을 통해 처리되어야 하는 것 자체가 트랜잭션 처리의 연산 복잡성을 인상하게 됩니다. + +### 보안 + +보안을 수치화하는 것은 쉽지 않으며, 플랫폼의 특성마다 다를 수 있습니다. 다만, 애플리케이션 특화 블록체인이 보안적으로 제공하는 특정 장점은 존재합니다: + +- 개발자는 Go 언어같은 검증된 언어로 애플리케이션 특화 블록체인을 개발할 수 있는 반면, 스마트 컨트랙트는 보통 미성숙한 언어로 개발해야 합니다. +- 개발자는 하위 버추얼 머신이 제공하는 암호학 함수를 사용해야하는 제한이 없으며, 검증된 암호학 라이브러리를 사용하거나 자체적인 암호학 알고리즘을 적용할 수 있습니다. +- 개발자는 하위 버추얼 머신에서 존재할 수 있는 버그 또는 메커니즘에 대한 걱정을 하지 않아도 되며, 애플리케이션 보안에만 집중할 수 있습니다. + +### 독립성 + +애플리케이션 특화 블록체인이 제공하는 가장 큰 장점 중 하나는 독립성(sovereignty, 존엄성)입니다. 탈중앙화 애플리케이션에는 유저, 개발자, 서비스 제공자 등 다수의 이해 관계자가 존재합니다. 개발자가 다수의 애플리케이션이 공존하는 버추얼 머신 기반 블록체인에서 본인의 애플리케이션을 개발하는 경우 본인 애플리케이션 커뮤니티와 하위 블록체인 플랫폼 커뮤니티가 다를 수 있으며, 하위 블록체인 플랫폼 커뮤니티가 애플리케이션 커뮤니티의 거버넌스를 지배하는 관계가 형성됩니다. 버그가 존재하거나 새로운 기능이 추가되는 경우, 애플리케이션의 이해관계자는 업그레이드를 실행하는데 제한되며 하위 플랫폼의 커뮤니티가 해당 과정을 받아드리지 않는 경우, 할 수 있는 것이 없게됩니다. + +여기서 근본적으로 존재하는 문제는 애플리케이션의 거버넌스와 플랫폼의 거버넌스가 일치하지 않는다는 것입니다. 애플리케이션 특화 블록체인에서는 블록체인이 한 애플리케이션을 위해 존재하기 때문에 애플리케이션의 이해관계자가 블록체인 전체에 대한 통제권을 부여받게 됩니다. 이는 커뮤니티가 버그를 발견하는 경우 조치를 할 수 있는 권한을 가질 수 있으며, 자체적으로 진화하는 방향을 정할 수도 있는 장점이 있습니다. + +## 다음 {hide} + +코스모스 SDK [애플리케이션의 구조](./sdk-app-architecture.md)에 대해 알아보세요{hide} diff --git a/docs/migrations/README.md b/docs/migrations/README.md new file mode 100644 index 000000000000..9b122044ec7b --- /dev/null +++ b/docs/migrations/README.md @@ -0,0 +1,14 @@ + + +# Migrations + +This folder contains all the migration guides to update your app and modules to Cosmos v0.40 Stargate. + +1. [App and Modules Migration](./app_and_modules.md) +1. [Chain Upgrade Guide to v0.40](./chain-upgrade-guide-040.md) +1. [REST Endpoints Migration](./rest.md) +1. [Keyring Migration](./keyring.md) diff --git a/docs/migrations/app_and_modules.md b/docs/migrations/app_and_modules.md new file mode 100644 index 000000000000..454ecdab2a0d --- /dev/null +++ b/docs/migrations/app_and_modules.md @@ -0,0 +1,239 @@ + + +# App and Modules Migration + +The following document describes the changes to update your app and modules from Cosmos SDK v0.39 to v0.40, +a.k.a. Stargate release. {synopsis} + +## Update Tooling + +Make sure to have the following dependencies before updating your app to v0.40: + +- Go 1.15+ +- Docker +- Node.js v12.0+ (optional, for generating Swagger docs) + +In Cosmos-SDK we manage the project using Makefile. Your own app can use a similar Makefile to the [Cosmos SDK's one](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/Makefile). More specifically, below are some _make_ commands that might be useful for your own app, related to the introduction of Protocol Buffers: + +- `proto-update-deps` - To download/update the required thirdparty `proto` definitions. +- `proto-gen` - To auto generate proto code. +- `proto-check-breaking` - To check proto breaking changes. +- `proto-format` - To format proto files. + +## Updating Modules + +This section outlines how to upgrade your module to v0.40. There is also a whole section about [building modules](../building-modules/README.md) from scratch, it might serve as a useful guide. + +### Protocol Buffers + +As outlined in our [encoding guide](../core/encoding.md), one of the most significant improvements introduced in Cosmos SDK v0.40 is Protobuf. + +The rule of thumb is that any object that needs to be serialized (into binary or JSON) must implement `proto.Message` and must be serializable into Protobuf format. The easiest way to do it is to use Protobuf type definition and `protoc` compiler to generate the structures and functions for you. In practice, the three following categories of types must be converted to Protobuf messages: + +- client-facing types: `Msg`s, query requests and responses. This is because client will send these types over the wire to your app. +- objects that are stored in state. This is because the SDK stores the binary representation of these types in state. +- genesis types. These are used when importing and exporting state snapshots during chain upgrades. + +Let's have a look at [x/auth's](../../x/auth/spec/README.md) `BaseAccount` objects, which are stored in a state. The migration looks like: + +```diff +// We were definining `MsgSend` as a Go struct in v0.39. +- // https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/x/bank/internal/types/msgs.go#L12-L16 +- type BaseAccount struct { +- Address sdk.AccAddress `json:"address" yaml:"address"` +- Coins sdk.Coins `json:"coins" yaml:"coins"` +- PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` +- AccountNumber uint64 `json:"account_number" yaml:"account_number"` +- Sequence uint64 `json:"sequence" yaml:"sequence"` +- } + +// And it should be converted to a Protobuf message in v0.40. ++ // https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/auth/v1beta1/auth.proto#L13-L25 ++ message BaseAccount { ++ string address = 1; ++ google.protobuf.Any pub_key = 2 ++ [(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; ++ uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; ++ uint64 sequence = 4; ++ } +} +``` + +In general, we recommend to put all the Protobuf definitions in your module's subdirectory under a root `proto/` folder, as described in [ADR-023](../architecture/adr-023-protobuf-naming.md). This ADR also contains other useful information on naming conventions. + +You might have noticed that the `PubKey` interface in v0.39's `BaseAccount` has been transformed into an `Any`. For storing interfaces, we use Protobuf's `Any` message, which is a struct that can hold arbitrary content. Please refer to the [encoding FAQ](../core/encoding.md#faq) to learn how to handle interfaces and `Any`s. + +Once all your Protobuf messages are defined, use the `make proto-gen` command defined in the [tooling section](#tooling) to generate Go structs. These structs will be generated into `*.pb.go` files. As a quick example, here is the generated Go struct for the Protobuf BaseAccount we defined above: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/auth/types/auth.pb.go#L28-L36 + +There might be some back and forth removing old Go structs/interfaces and defining new Protobuf messages before your Go app compiles and your tests pass. + +### Create `Msg` and `Query` Services + +Cosmos SDK v0.40 uses Protobuf services to define state transitions (`Msg`s) and state queries, please read [the building modules guide on those services](../building-modules/messages-and-queries.md) for an overview. + +#### `Msg` Service + +For migrating `Msg`s, the handler pattern (inside the `handler.go` file) is deprecated. You may still keep it if you wish to support `Msg`s defined in older versions of the SDK. However, it is strongly recommended to add a `Msg` service to your Protobuf files, and each old `Msg` should be converted into a service method. Taking [x/bank's](../../x/bank/spec/README.md) `MsgSend` as an example, we have a corresponding `cosmos.bank.v1beta1.Msg/Send` service method: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/bank/v1beta1/tx.proto#L10-L31 + +A state transition is therefore modelized as a Protobuf service method, with a method request, and an (optionally empty) method response. + +After defining your `Msg` service, run the `make proto-gen` script again to generate `Msg` server interfaces. The name of this interface is simply `MsgServer`. The implementation of this interface should follow exactly the implementation of the old `Msg` handlers, which, in most cases, defers the actual state transition logic to the [keeper](../building-modules/keeper.md). You may implement a `MsgServer` directly on the keeper, or you can do it using a new struct (e.g. called `msgServer`) that references the module's keeper. + +For more information, please check our [`Msg` service guide](../building-modules/msg-services.md). + +#### `Query` Service + +For migrating state queries, the querier pattern (inside the `querier.go` file) is deprecated. You may still keep this file to support legacy queries, but it is strongly recommended to use a Protobuf `Query` service to handle state queries. + +Each query endpoint is now defined as a separate service method in the `Query` service. Still taking `x/bank` as an example, here are the queries to fetch an account's balances: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/bank/v1beta1/query.proto#L12-L23 + +Each query has its own `Request` and `Response` types. Please also note the `google.api.http` option (coming from [`grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway)) on each service method. `grpc-gateway` is a tool that exposes `Query` service methods as REST endpoints. Adding this annotation will expose these endpoints not only as gRPC endpoints, but also as REST endpoints. An overview of gRPC versus REST can be found [here](../core/grpc_rest.md). + +After defining the `Query` Protobuf service, run the `make proto-gen` command to generate corresponding interfaces. The interface that needs to be implemented by your module is `QueryServer`. This interface can be implemented on the [keeper](../building-modules/keeper.md) directly, or on a struct (e.g. called `queryServer`) that references the module's keeper. The logic of the implementation, namely the logic that fetches data from the module's store and performs unmarshalling, can be deferred to the keeper. + +Cosmos SDK v0.40 also comes with an efficient pagination, it now uses `Prefix` stores to make queries. There are 2 helpers for pagination, [`Paginate`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/query/pagination.go#L40-L42), [`FilteredPaginate`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/query/filtered_pagination.go#L9-L17). + +For more information, please check our [`Query` service guide](../building-modules/query-services.md). + +#### Wiring up `Msg` and `Query` Services + +We added a new `RegisterServices` method that registers a module's `Msg` service and a `Query` service. It should be implemented by all modules, using a `Configurator` object: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L99-L103 + +If you wish to expose your `Query` endpoints as REST endpoints (as proposed in the [`Query` Services paragraph](#query-services)), make sure to also implement the `RegisterGRPCGatewayRoutes` method: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L69-L72 + +### Codec + +If you still use Amino (which is deprecated since Stargate), you must register related types using the `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)` method (previously it was called `RegisterCodec(cdc *codec.Codec)`). + +Moreover, a new `RegisterInterfaces` method has been added to the `AppModule` interface that all modules must implement. This method must register the interfaces that Protobuf messages implement, as well as the service `Msg`s used in the module. An example from x/bank is given below: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/types/codec.go#L21-L34 + +### Keeper + +The `Keeper` constructor now takes a `codec.Marshaler` instead of a concrete Amino codec. This `codec.Marshaler` is used to encode types as binary and save the bytes into the state. With an interface, you can define `codec.Marshaler` to be Amino or Protobuf on an app level, and keepers will use that encoding library to encode state. Please note that Amino is deprecated in v0.40, and we strongly recommend to use Protobuf. Internally, the SDK still uses Amino in a couple of places (legacy REST API, x/params, keyring...), but Amino is planned to be removed in a future release. + +Keeping Amino for now can be useful if you wish to update to SDK v0.40 without doing a chain upgrade with a genesis migration. This will not require updating the stores, so you can migrate to Cosmos SDK v0.40 with less effort. However, as Amino will be removed in a future release, the chain migration with genesis export/import will need to be performed then. + +Related to the keepers, each module's `AppModuleBasic` now also includes this `codec.Marshaler`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L35-L38 + +### CLI + +Each modules may optionally expose CLI commands, and some changes are needed in these commands. + +First, `context.CLIContext` is renamed to `client.Context` and moved to `github.com/cosmos/cosmos-sdk/client`. + +Second, the global `viper` usage is removed from client and is replaced with Cobra' `cmd.Flags()`. There are two helpers to read common flags for CLI txs and queries: + +```go +clientCtx, err := client.GetClientQueryContext(cmd) +clientCtx, err := client.GetClientTxContext(cmd) +``` + +Some other flags helper functions are transformed: `flags.PostCommands(cmds ...*cobra.Command) []*cobra.Command` and `flags.GetCommands(...)` usage is now replaced by `flags.AddTxFlagsToCmd(cmd *cobra.Command)` and `flags.AddQueryFlagsToCmd(cmd *cobra.Command)` respectively. + +Moreover, new CLI commands don't take any codec as input anymore. Instead, the `clientCtx` can be retrieved from the `cmd` itself using the `GetClient{Query,Tx}Context` function above, and the codec as `clientCtx.JSONMarshaler`. + +```diff +// v0.39 +- func SendTxCmd(cdc *codec.Codec) *cobra.Command { +- cdc.MarshalJSON(...) +- } + +// v0.40 ++ func NewSendTxCmd() *cobra.Command { ++ clientCtx, err := client.GetClientTxContext(cmd) ++ clientCtx.JSONMarshaler.MarshalJSON(...) ++} +``` + +Finally, once your [`Query` services](#query-service) are wired up, the CLI commands should preferably use gRPC to communicate with the node. The gist is to create a `Query` or `Msg` client using the command's `clientCtx`, and perform the request using Protobuf's generated code. An example for querying x/bank balances is given here: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/client/cli/query.go#L66-L94 + +### Miscelleanous + +A number of other smaller breaking changes are also noteworthy. + +| Before | After | Comment | +| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `alias.go` file | Removed | `alias` usage is removed, please see [#6311](https://github.com/cosmos/cosmos-sdk/issues/6311) for details. | +| `codec.New()` | `codec.NewLegacyAmino()` | Simple rename. | +| `DefaultGenesis()` | `DefaultGenesis(cdc codec.JSONMarshaler)` | `DefaultGenesis` takes a codec argument now | +| `ValidateGenesis()` | `ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage)` | `ValidateGenesis` now requires `Marshaler`, `TxEncodingConfig`, `json.RawMessage` as input. | +| `Route() string` | `Route() sdk.Route` | For legacy handlers, return type of `Route()` method is changed from `string` to `"github.com/cosmos/cosmos-sdk/types".Route`. It should return a `NewRoute()` which includes `RouterKey` and `NewHandler` as params. | +| `QuerierHandler` | `LegacyQuerierHandler` | Simple rename. | +| `InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate` | InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate | `InitGenesis` now takes a codec input. | +| `ExportGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate` | ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate | `ExportGenesis` now takes a codec input. | + +## Updating Your App + +For a reference implementation used for demo purposes, you can refer to the SDK's [SimApp](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc6/simapp) for your app's migration. The most important changes are described in this section. + +### Creating Codecs + +With the introduction of Protobuf, each app needs to define the encoding library (Amino or Protobuf) to be used throughout the app. There is a central struct, [`EncodingConfig`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/params/encoding.go#L9-L11), which defines all information necessary for codecs. In your app, an example `EncodingConfig` with Protobuf as default codec might look like: + +```go +// MakeEncodingConfig creates an EncodingConfig +func MakeEncodingConfig() params.EncodingConfig { + amino := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) + + encodingConfig := params.EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: txCfg, + Amino: amino, + } + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + return encodingConfig +} +``` + +These codecs are used to populate the following fields on your app (again, we are using SimApp for demo purposes): + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L146-L153 + +As explained in the [modules migration section](#updating-modules), some functions and structs in modules require an additional `codec.Marshaler` argument. You should pass `app.appCodec` in these cases, and this will be the default codec used throughout the app. + +### Registering Non-Module Protobuf Services + +We described in the [modules migration section](#updating-modules) `Query` and `Msg` services defined in each module. The SDK also exposes two more module-agnostic services: + +- the [Tx Service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/tx/v1beta1/service.proto), to perform operations on transactions, +- the [Tendermint service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/base/tendermint/v1beta1/query.proto), to have a more idiomatic interface to the [Tendermint RPC](https://docs.tendermint.com/master/rpc/). + +These services are optional, if you wish to use them, or if you wish to add more module-agnostic Protobuf services into your app, then you need to add them inside `app.go`: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L577-L585 + +### Registering `grpc-gateway` Routes + +The exising `RegisterAPIRoutes` method on the `app` only registers [Legacy API routes](../core/grpc_rest.md#legacy-rest-api-routes). If you are using `grpc-gateway` REST endpoints as described [above](#query-service), then these endpoints need to be wired up to a HTTP server: + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L555-L575 + +## Next {hide} + +Learn how to perform a [chain upgrade](./chain-upgrade-guide-040.md) to 0.40. diff --git a/docs/migrations/chain-upgrade-guide-040.md b/docs/migrations/chain-upgrade-guide-040.md new file mode 100644 index 000000000000..7a2848d3e7d3 --- /dev/null +++ b/docs/migrations/chain-upgrade-guide-040.md @@ -0,0 +1,166 @@ + + +# Chain Upgrade Guide to v0.42 + +This document explains how to perform a chain upgrade from v0.39 to v0.42. {synopsis} + +::: tip +Please note that the three SDK versions v0.40, v0.41 and v0.42 are functionally equivalent, together called the "Stargate" series. The version bumps are consequences of post-release state-breaking bugfixes. +::: + +## Risks + +As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and +being slashed: if your validator node votes for a block, and, in the same block time, restarts the upgraded node, this may lead to double-voting on a block. + +The riskiest thing a validator can do is to discover that they made a mistake and repeat the upgrade procedure again during +the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start +before correcting it. If the network is halted and you have started with a different genesis file than the expected one, +seek advice from the validator community. + +## Recovery + +- Prior to exporting the state, the validators are encouraged to take a full data snapshot at exported height. Exported + height will be determined by a governance proposal. Data backup is usually done by copying daemon home directory, + e.g.: `~/.simd` + +**Note:** we use "simd" as our app throughout this doc, be sure to replace with the name of your own binary. + +It is critically important to back-up the validator state file, e.g.: `~/.simd/data/priv_validator_state.json` file +after stopping your daemon process. This file is updated every block as your validator participates in a consensus +rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails, and the previous chain needs +to be restarted. + +In the event that the upgrade does not succeed, validators and operators must downgrade back to old version of the +software and restore to their latest snapshot before restarting their nodes. + +## Upgrade procedure + +1. The procedure is to export the state from the old binary, and import it with the new binary. First, verify your old binary version (which should use `github.com/cosmos/cosmos-sdk@0.39.*`) before exporting the state. + + ```shell + simd version --long + ``` + +1. Export the state from existing chain using the old binary. + + ```shell + simd export --for-zero-height --height > v039_exported_state.json + ``` + +1. Verify the SHA256 of the (sorted) exported genesis file: + + ```shell + $ jq -S -c -M '' v039_exported_state.json | shasum -a 256 + [SHASUM_PLACEHOLDER] v039_exported_state.json + ``` + +1. Cross check the hash with other peers (other validators) in the chat rooms. + +1. Install the latest binary (which uses `github.com/cosmos/cosmos-sdk@0.40.*`). + +1. Migrate the exported state to `github.com/cosmos/cosmos-sdk@0.40.*` compatible genesis state. + + ```shell + simd migrate v0.42 v039_exported_state.json --chain-id --genesis-time > new_v042_genesis.json + ``` + + **Note:** The migrate command takes an input genesis state and migrates it to a targeted version. New `genesis-time` is usually mentioned in the governance proposal, and should be passed as flag argument. If the flag is omitted, then the genesis time of the upgraded chain will be the same as the old one, which may cause confusion. + +1. All the necessary state changes are handled in the `simd migrate v0.42` migration command. However, Tendermint parameters are **not** handled in this command. You might need to update these parameters manually. + + In the recent versions of Tendermint, the following changes have been made: + + - `consensus_params.evidence.max_num` has been renamed to `consensus_params.evidence.max_bytes`. + - `consensus_params.evidence.max_age` has been removed, and replaced by `consensus_params.evidence.max_age_duration` and `consensus_params.evidence.max_age_num_blocks`. + + Make sure that your genesis JSON files contains the correct values specific to your chain. If the `simd migrate` errors with a message saying that the genesis file cannot be parsed, these are the fields to check first. + +1. Verify the SHA256 of the migrated genesis file with other validators to make sure there are no manual errors in the process. + + ```shell + $ jq -S -c -M '' new_v042_genesis.json | shasum -a 256 + [SHASUM_PLACEHOLDER] new_v042_genesis.json + ``` + +1. Make sure to update the genesis parameters in the new genesis if any. All these details will be generally present in + the governance proposal. + +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_v042_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_v042_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. + +1) Reset the old state. + + **Note:** Be sure you have a complete backed up state of your node before proceeding with this step. + See Recovery for details on how to proceed. + + ```shell + simd unsafe-reset-all + ``` + +1) Move the new genesis.json to your daemon config directory. Ex + + ```shell + cp new_v042_genesis.json ~/.simd/config/genesis.json + ``` + +1) Update `~/.simd/config/app.toml` to include latest app configurations. [Here is the link](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0-rc6/server/config/toml.go#L11-L164) to the default template for v0.42's `app.toml`. Make sure to + update your custom configurations as per your validator design, e.g. `gas_price`. + + Compared to v0.39, some notable updates to `app.toml` are: + + - API server is now configured to run in-process with daemon, previously it was a separate process, invoked by running rest-server + command i.e., `gaiacli rest-server`. Now it is in-process with daemon and can be enabled/disabled by API configuration: + + ```yaml + [api] + # Enable defines if the API server should be enabled. + enable = false + # Swagger defines if swagger documentation should automatically be registered. + swagger = false + ``` + + `swagger` setting refers to enabling/disabling swagger docs API, i.e, `/swagger/` API endpoint. + + - gRPC Configuration + + ```yaml + [grpc] + # Enable defines if the gRPC server should be enabled. + enable = true + # Address defines the gRPC server address to bind to. + address = "0.0.0.0:9090" + ``` + + - State Sync Configuration + + ```yaml + # State sync snapshots allow other nodes to rapidly join the network without replaying historical + # blocks, instead downloading and applying a snapshot of the application state at a given height. + [state-sync] + # snapshot-interval specifies the block interval at which local state sync snapshots are + # taken (0 to disable). Must be a multiple of pruning-keep-every. + snapshot-interval = 0 + # snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). + snapshot-keep-recent = 2 + ``` + +1) Kill if any external `rest-server` process is running. + +1) All set now! You can (re)start your daemon to validate on the upgraded network. Make sure to check your binary version + before starting the daemon: + + ``` + simd version --long + ``` + +## Next {hide} + +Once your chain is upgraded, make sure to [update your clients' REST endpoints](./rest.md). diff --git a/docs/migrations/keyring.md b/docs/migrations/keyring.md new file mode 100644 index 000000000000..4798dd5af0ec --- /dev/null +++ b/docs/migrations/keyring.md @@ -0,0 +1,31 @@ + +# Keyring Migrate Quick Start + +`keyring` is the Cosmos SDK mechanism to manage the public/private keypair. Cosmos SDK v0.42 (Stargate) introduced breaking changes in the keyring. + +To upgrade your chain from v0.39 (Launchpad) and earlier to Stargate, you must migrate your keys inside the keyring to the latest version. For details on configuring and using the keyring, see [Setting up the keyring](../run-node/keyring.md). + +This guide describes how to migrate your keyrings. + +The following command migrates your keyrings: + +```bash +Usage +simd keys migrate +``` + +The migration process moves key information from the legacy db-based Keybase to the [keyring](https://github.com/99designs/keyring)-based Keyring. The legacy Keybase persists keys in a LevelDB database in a 'keys' sub-directory of the client application home directory (`old_home_dir`). For example, `$HOME/.gaiacli/keys/` for [Gaia](https://github.com/cosmos/gaia). + +You can migrate or skip the migration for each key entry found in the specified `old_home_dir` directory. Each key migration requires a valid passphrase. If an invalid passphrase is entered, the command exits. Run the command again to restart the keyring migration. + +The `migrate` command takes the following flags: +- `--dry-run` boolean + + - true - run the migration but do not persist changes to the new Keybase. + - false - run the migration and persist keys to the new Keybase. + +Recommended: Use `--dry-run true` to test the migration without persisting changes before you migrate and persist keys. + +- `--keyring-backend` string flag. It allows you to select a backend. For more detailed information about the available backends, you can read [the keyring guide](../run-node/keyring.md). diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md new file mode 100644 index 000000000000..b3ff87ba90ec --- /dev/null +++ b/docs/migrations/rest.md @@ -0,0 +1,101 @@ + + +# REST Endpoints Migration + +Migrate your REST endpoints to the Stargate ones. {synopsis} + +## Deprecation of Legacy REST Endpoints + +The Cosmos SDK versions v0.39 and earlier provided REST endpoints to query the state and broadcast transactions. These endpoints are kept in Cosmos SDK v0.40 (Stargate), but they are marked as deprecated, and will be removed in v0.41. We therefore call these endpoints legacy REST endpoints. + +Some important information concerning all legacy REST endpoints: + +- Most of these endpoints are backwards-comptatible. All breaking changes are described in the next section. +- In particular, these endpoints still output Amino JSON. Cosmos v0.40 introduced Protobuf as the default encoding library throughout the codebase, but legacy REST endpoints are one of the few places where the encoding is hardcoded to Amino. For more information about Protobuf and Amino, please read our [encoding guide](../core/encoding.md). +- All legacy REST endpoints include a [HTTP deprecation header](https://tools.ietf.org/id/draft-dalal-deprecation-header-01.html) which links to this document. + +## Breaking Changes in Legacy REST Endpoints + +| Legacy REST Endpoint | Description | Breaking Change | +| ------------------------------------------------------------------------ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `POST /txs` | Broadcast tx | Endpoint will error when trying to broadcast transactions that don't support Amino serialization (e.g. IBC txs)1. | +| `POST /txs/encode`, `POST /txs/decode` | Encode/decode Amino txs from JSON to binary | Endpoint will error when trying to encode/decode transactions that don't support Amino serialization (e.g. IBC txs)1. | +| `GET /txs/{hash}` | Query tx by hash | Endpoint will error when trying to output transactions that don't support Amino serialization (e.g. IBC txs)1. | +| `GET /txs` | Query tx by events | Endpoint will error when trying to output transactions that don't support Amino serialization (e.g. IBC txs)1. | +| `GET /gov/proposals/{id}/votes`, `GET /gov/proposals/{id}/votes/{voter}` | Gov endpoints for querying votes | All gov endpoints which return votes return int32 in the `option` field instead of string: `1=VOTE_OPTION_YES, 2=VOTE_OPTION_ABSTAIN, 3=VOTE_OPTION_NO, 4=VOTE_OPTION_NO_WITH_VETO`. | +| `GET /staking/*` | Staking query endpoints | All staking endpoints which return validators have two breaking changes. First, the validator's `consensus_pubkey` field returns an Amino-encoded struct representing an `Any` instead of a bech32-encoded string representing the pubkey. The `value` field of the `Any` is the pubkey's raw key as base64-encoded bytes. Second, the validator's `status` field now returns an int32 instead of string: `1=BOND_STATUS_UNBONDED`, `2=BOND_STATUS_UNBONDING`, `3=BOND_STATUS_BONDED`. | +| `GET /staking/validators` | Get all validators | BondStatus is now a protobuf enum instead of an int32, and JSON serialized using its protobuf name, so expect query parameters like `?status=BOND_STATUS_{BONDED,UNBONDED,UNBONDING}` as opposed to `?status={bonded,unbonded,unbonding}`. | + +1: Transactions that don't support Amino serialization are the ones that contain one or more `Msg`s that are not registered with the Amino codec. Currently in the SDK, only IBC `Msg`s fall into this case. + +## 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 _gRPC-gateway REST endpoints_. + +Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here](../run-node/txs.md). + +| Legacy REST Endpoint | Description | New gRPC-gateway REST Endpoint | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `GET /txs/{hash}` | Query tx by hash | `GET /cosmos/tx/v1beta1/txs/{hash}` | +| `GET /txs` | Query tx by events | `GET /cosmos/tx/v1beta1/txs` | +| `POST /txs` | Broadcast tx | `POST /cosmos/tx/v1beta1/txs` | +| `POST /txs/encode` | Encodes an Amino JSON tx to an Amino binary tx | N/A, use Protobuf directly | +| `POST /txs/decode` | Decodes an Amino binary tx into an Amino JSON tx | N/A, use Protobuf directly | +| `POST /bank/*` | Create unsigned `Msg`s for bank tx | N/A, use Protobuf directly | +| `GET /bank/balances/{address}` | Get the balance of an address | `GET /cosmos/bank/v1beta1/balances/{address}` | +| `GET /bank/total` | Get the total supply of all coins | `GET /cosmos/bank/v1beta1/supply` | +| `GET /bank/total/{denom}` | Get the total supply of one coin | `GET /cosmos/bank/v1beta1/supply/{denom}` | +| `POST /distribution/delegators/{delegatorAddr}/rewards` | Withdraw all delegator rewards | N/A, use Protobuf directly | +| `POST /distribution/*` | Create unsigned `Msg`s for distribution | N/A, use Protobuf directly | +| `GET /distribution/delegators/{delegatorAddr}/rewards` | Get the total rewards balance from all delegations | `GET /cosmos/distribution/v1beta1/v1beta1/delegators/{delegator_address}/rewards` | +| `GET /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}` | Query a delegation reward | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/rewards/{validatorAddr}` | +| `GET /distribution/delegators/{delegatorAddr}/withdraw_address` | Get the rewards withdrawal address | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/withdraw_address` | +| `GET /distribution/validators/{validatorAddr}` | Validator distribution information | N/A | +| `GET /distribution/validators/{validatorAddr}/rewards` | Commission and outstanding rewards of a single a validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/commission`
`GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/outstanding_rewards` | +| `GET /distribution/validators/{validatorAddr}/outstanding_rewards` | Outstanding rewards of a single validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/outstanding_rewards` | +| `GET /distribution/parameters` | Get the current distribution parameter values | `GET /cosmos/distribution/v1beta1/params` | +| `GET /distribution/community_pool` | Get the amount held in the community pool | `GET /cosmos/distribution/v1beta1/community_pool` | +| `GET /evidence/{evidence-hash}` | Get evidence by hash | `GET /cosmos/evidence/v1beta1/evidence/{evidence_hash}` | +| `GET /evidence` | Get all evidence | `GET /cosmos/evidence/v1beta1/evidence` | +| `POST /gov/*` | Create unsigned `Msg`s for gov | N/A, use Protobuf directly | +| `GET /gov/parameters/{type}` | Get government parameters | `GET /cosmos/gov/v1beta1/params/{type}` | +| `GET /gov/proposals` | Get all proposals | `GET /cosmos/gov/v1beta1/proposals` | +| `GET /gov/proposals/{proposal-id}` | Get proposal by id | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}` | +| `GET /gov/proposals/{proposal-id}/proposer` | Get proposer of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}` (Get proposer from `Proposal` struct) | +| `GET /gov/proposals/{proposal-id}/deposits` | Get deposits of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/deposits` | +| `GET /gov/proposals/{proposal-id}/deposits/{depositor}` | Get depositor a of deposit | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/deposits/{depositor}` | +| `GET /gov/proposals/{proposal-id}/tally` | Get tally of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/tally` | +| `GET /gov/proposals/{proposal-id}/votes` | Get votes of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/votes` | +| `GET /gov/proposals/{proposal-id}/votes/{vote}` | Get a particular vote | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/votes/{vote}` | +| `GET /minting/parameters` | Get parameters for minting | `GET /cosmos/minting/v1beta1/params` | +| `GET /minting/inflation` | Get minting inflation | `GET /cosmos/minting/v1beta1/inflation` | +| `GET /minting/annual-provisions` | Get minting annual provisions | `GET /cosmos/minting/v1beta1/annual_provisions` | +| `POST /slashing/*` | Create unsigned `Msg`s for slashing | N/A, use Protobuf directly | +| `GET /slashing/validators/{validatorPubKey}/signing_info` | Get validator signing info | `GET /cosmos/slashing/v1beta1/signing_infos/{cons_address}` (Use consensus address instead of pubkey) | +| `GET /slashing/signing_infos` | Get all signing infos | `GET /cosmos/slashing/v1beta1/signing_infos` | +| `GET /slashing/parameters` | Get slashing parameters | `GET /cosmos/slashing/v1beta1/params` | +| `POST /staking/*` | Create unsigned `Msg`s for staking | N/A, use Protobuf directly | +| `GET /staking/delegators/{delegatorAddr}/delegations` | Get all delegations from a delegator | `GET /cosmos/staking/v1beta1/delegations/{delegatorAddr}` | +| `GET /staking/delegators/{delegatorAddr}/unbonding_delegations` | Get all unbonding delegations from a delegator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations` | +| `GET /staking/delegators/{delegatorAddr}/txs` | Get all staking txs (i.e msgs) from a delegator | Removed | +| `GET /staking/delegators/{delegatorAddr}/validators` | Query all validators that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators` | +| `GET /staking/delegators/{delegatorAddr}/validators/{validatorAddr}` | Query a validator that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators/{validatorAddr}` | +| `GET /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}` | Query a delegation between a delegator and a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/delegations/{delegatorAddr}` | +| `GET /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` | Query all unbonding delegations between a delegator and a validator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` | +| `GET /staking/redelegations` | Query redelegations | `GET /cosmos/staking/v1beta1/v1beta1/delegators/{delegator_addr}/redelegations` | +| `GET /staking/validators` | Get all validators | `GET /cosmos/staking/v1beta1/validators` | +| `GET /staking/validators/{validatorAddr}` | Get a single validator info | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}` | +| `GET /staking/validators/{validatorAddr}/delegations` | Get all delegations to a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/delegations` | +| `GET /staking/validators/{validatorAddr}/unbonding_delegations` | Get all unbonding delegations from a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/unbonding_delegations` | +| `GET /staking/historical_info/{height}` | Get HistoricalInfo at a given height | `GET /cosmos/staking/v1beta1/historical_info/{height}` | +| `GET /staking/pool` | Get the current state of the staking pool | `GET /cosmos/staking/v1beta1/pool` | +| `GET /staking/parameters` | Get the current staking parameter values | `GET /cosmos/staking/v1beta1/params` | +| `POST /upgrade/*` | Create unsigned `Msg`s for upgrade | N/A, use Protobuf directly | +| `GET /upgrade/current` | Get the current plan | `GET /cosmos/upgrade/v1beta1/current_plan` | +| `GET /upgrade/applied_plan/{name}` | Get a previously applied plan | `GET /cosmos/upgrade/v1beta1/applied/{name}` | + +## Migrating to gRPC + +Instead of hitting REST endpoints as described in the previous paragraph, the SDK also exposes a gRPC server. Any client can use gRPC instead of REST to interact with the node. An overview of different ways to communicate with a node can be found [here](../core/grpc_rest.md), and a concrete tutorial for setting up a gRPC client [here](../run-node/txs.md#programmatically-with-go). diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 3eee6f418b9a..000000000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,9811 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/core": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.5.tgz", - "integrity": "sha512-M42+ScN4+1S9iB6f+TL7QBpoQETxbclx+KNoKJABghnKYE+fMzSGqst0BZJc8CpI625bwPwYgUyRvxZ+0mZzpw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", - "requires": { - "minimist": "^1.2.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz", - "integrity": "sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz", - "integrity": "sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==", - "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-call-delegate": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz", - "integrity": "sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==", - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz", - "integrity": "sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz", - "integrity": "sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==", - "requires": { - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" - } - }, - "@babel/helper-define-map": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz", - "integrity": "sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz", - "integrity": "sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==", - "requires": { - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz", - "integrity": "sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz", - "integrity": "sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-module-imports": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz", - "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.5.tgz", - "integrity": "sha512-A7pSxyJf1gN5qXVcidwLWydjftUN878VkalhXX5iQDuGyiGK3sOrrKKHF4/A4fwHtnsotv/NipwAeLzY4KQPvw==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-simple-access": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz", - "integrity": "sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" - }, - "@babel/helper-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", - "requires": { - "lodash": "^4.17.13" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz", - "integrity": "sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-wrap-function": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz", - "integrity": "sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz", - "integrity": "sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==", - "requires": { - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-wrap-function": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz", - "integrity": "sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helpers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", - "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", - "requires": { - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.5.tgz", - "integrity": "sha512-KNlOe9+/nk4i29g0VXgl8PEXIRms5xKLJeuZ6UptN0fHv+jDiriG+y94X6qAgWTR0h3KaoM1wK5G5h7MHFRSig==" - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz", - "integrity": "sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", - "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.7.4.tgz", - "integrity": "sha512-GftcVDcLCwVdzKmwOBDjATd548+IE+mBo7ttgatqNDR7VG7GqIuZPtRWlMLHbhTXhcnFZiGER8iIYl1n/imtsg==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-decorators": "^7.7.4" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz", - "integrity": "sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.7.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz", - "integrity": "sha512-cHgqHgYvffluZk85dJ02vloErm3Y6xtH+2noOBOJ2kXOJH3aVCDnj5eR/lVNlTnYu4hndAPJD3rTFjW3qee0PA==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz", - "integrity": "sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.7.4.tgz", - "integrity": "sha512-0oNLWNH4k5ZbBVfAwiTU53rKFWIeTh6ZlaWOXWJc4ywxs0tjz5fc3uZ6jKAnZSxN98eXVgg7bJIuzjX+3SXY+A==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz", - "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz", - "integrity": "sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz", - "integrity": "sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz", - "integrity": "sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz", - "integrity": "sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz", - "integrity": "sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz", - "integrity": "sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.13" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz", - "integrity": "sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-define-map": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz", - "integrity": "sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", - "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz", - "integrity": "sha512-mk0cH1zyMa/XHeb6LOTXTbG7uIJ8Rrjlzu91pUx/KS3JpcgaTDwMS8kM+ar8SLOvlL2Lofi4CGBAjCo3a2x+lw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz", - "integrity": "sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz", - "integrity": "sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz", - "integrity": "sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz", - "integrity": "sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz", - "integrity": "sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.5.tgz", - "integrity": "sha512-CT57FG4A2ZUNU1v+HdvDSDrjNWBrtCmSH6YbbgN3Lrf0Di/q/lWRxZrE72p3+HCCz9UjfZOEBdphgC0nzOS6DQ==", - "requires": { - "@babel/helper-module-transforms": "^7.7.5", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.5.tgz", - "integrity": "sha512-9Cq4zTFExwFhQI6MT1aFxgqhIsMWQWDVwOgLzl7PTWJHsNaqFvklAU+Oz6AQLAS0dJKTwZSOCo20INwktxpi3Q==", - "requires": { - "@babel/helper-module-transforms": "^7.7.5", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.4", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz", - "integrity": "sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw==", - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz", - "integrity": "sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw==", - "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz", - "integrity": "sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz", - "integrity": "sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz", - "integrity": "sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz", - "integrity": "sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw==", - "requires": { - "@babel/helper-call-delegate": "^7.7.4", - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.7.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.5.tgz", - "integrity": "sha512-/8I8tPvX2FkuEyWbjRCt4qTAgZK0DVy8QRguhA524UH48RfGJy94On2ri+dCuwOpcerPRl9O4ebQkRcVzIaGBw==", - "requires": { - "regenerator-transform": "^0.14.0" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.6.tgz", - "integrity": "sha512-tajQY+YmXR7JjTwRvwL4HePqoL3DYxpYXIHKVvrOIvJmeHe2y1w4tz5qz9ObUDC9m76rCzIMPyn4eERuwA4a4A==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz", - "integrity": "sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz", - "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz", - "integrity": "sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz", - "integrity": "sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz", - "integrity": "sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz", - "integrity": "sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/preset-env": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz", - "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.3.4", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.3.4", - "@babel/plugin-transform-classes": "^7.3.4", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.2.0", - "@babel/plugin-transform-dotall-regex": "^7.2.0", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.2.0", - "@babel/plugin-transform-function-name": "^7.2.0", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.2.0", - "@babel/plugin-transform-modules-systemjs": "^7.3.4", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0", - "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.3.4", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.2.0", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.2.0", - "browserslist": "^4.3.4", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.3.0" - } - }, - "@babel/runtime": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz", - "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==", - "requires": { - "regenerator-runtime": "^0.13.2" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - } - } - }, - "@babel/runtime-corejs2": { - "version": "7.7.6", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.6.tgz", - "integrity": "sha512-QYp/8xdH8iMin3pH5gtT/rUuttVfIcOhWBC3wh9Eh/qs4jEe39+3DpCDLgWXhMQgiCTOH8mrLSvQ0OHOCcox9g==", - "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - } - } - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - } - } - }, - "@cosmos-ui/vue": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.5.17.tgz", - "integrity": "sha512-x9ruudnnUCDxFQZRRMBzmVYM1s7ukmGuyCd0M+fMo3kAwg/IePlyq3aA4aZXD411NvBFNG1A2BxWQAWd09hT0g==", - "requires": { - "vue": "^2.6.10" - } - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" - }, - "@types/babel-types": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", - "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" - }, - "@types/babylon": { - "version": "6.16.5", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", - "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", - "requires": { - "@types/babel-types": "*" - } - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" - }, - "@types/node": { - "version": "12.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.18.tgz", - "integrity": "sha512-DBkZuIMFuAfjJHiunyRc+aNvmXYNwV1IPMgGKGlwCp6zh6MKrVtmvjSWK/axWcD25KJffkXgkfvFra8ndenXAw==" - }, - "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" - }, - "@vue/babel-helper-vue-jsx-merge-props": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", - "integrity": "sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw==" - }, - "@vue/babel-plugin-transform-vue-jsx": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz", - "integrity": "sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", - "html-tags": "^2.0.0", - "lodash.kebabcase": "^4.1.1", - "svg-tags": "^1.0.0" - } - }, - "@vue/babel-preset-app": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-3.12.1.tgz", - "integrity": "sha512-Zjy5jQaikV1Pz+ri0YgXFS7q4/5wCxB5tRkDOEIt5+4105u0Feb/pvH20nVL6nx9GyXrECFfcm7Yxr/z++OaPQ==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-decorators": "^7.1.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-transform-runtime": "^7.4.0", - "@babel/preset-env": "^7.0.0 < 7.4.0", - "@babel/runtime": "^7.0.0", - "@babel/runtime-corejs2": "^7.2.0", - "@vue/babel-preset-jsx": "^1.0.0", - "babel-plugin-dynamic-import-node": "^2.2.0", - "babel-plugin-module-resolver": "3.2.0", - "core-js": "^2.6.5" - } - }, - "@vue/babel-preset-jsx": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz", - "integrity": "sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ==", - "requires": { - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", - "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", - "@vue/babel-sugar-functional-vue": "^1.1.2", - "@vue/babel-sugar-inject-h": "^1.1.2", - "@vue/babel-sugar-v-model": "^1.1.2", - "@vue/babel-sugar-v-on": "^1.1.2" - } - }, - "@vue/babel-sugar-functional-vue": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz", - "integrity": "sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ==", - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-inject-h": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz", - "integrity": "sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w==", - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0" - } - }, - "@vue/babel-sugar-v-model": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz", - "integrity": "sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg==", - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", - "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", - "camelcase": "^5.0.0", - "html-tags": "^2.0.0", - "svg-tags": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "@vue/babel-sugar-v-on": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz", - "integrity": "sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ==", - "requires": { - "@babel/plugin-syntax-jsx": "^7.2.0", - "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", - "camelcase": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "@vue/component-compiler-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.0.tgz", - "integrity": "sha512-OJ7swvl8LtKtX5aYP8jHhO6fQBIRIGkU6rvWzK+CGJiNOnvg16nzcBkd9qMZzW8trI2AsqAKx263nv7kb5rhZw==", - "requires": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.14", - "postcss-selector-parser": "^5.0.0", - "prettier": "^1.18.2", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, - "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - } - } - }, - "@vuepress/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.2.0.tgz", - "integrity": "sha512-ZIsUkQIF+h4Yk6q4okoRnRwRhcYePu/kNiL0WWPDGycjai8cFqFjLDP/tJjfTKXmn9A62j2ETjSwaiMxCtDkyw==", - "requires": { - "@babel/core": "^7.0.0", - "@vue/babel-preset-app": "^3.1.1", - "@vuepress/markdown": "^1.2.0", - "@vuepress/markdown-loader": "^1.2.0", - "@vuepress/plugin-last-updated": "^1.2.0", - "@vuepress/plugin-register-components": "^1.2.0", - "@vuepress/shared-utils": "^1.2.0", - "autoprefixer": "^9.5.1", - "babel-loader": "^8.0.4", - "cache-loader": "^3.0.0", - "chokidar": "^2.0.3", - "connect-history-api-fallback": "^1.5.0", - "copy-webpack-plugin": "^5.0.2", - "cross-spawn": "^6.0.5", - "css-loader": "^2.1.1", - "file-loader": "^3.0.1", - "js-yaml": "^3.13.1", - "lru-cache": "^5.1.1", - "mini-css-extract-plugin": "0.6.0", - "optimize-css-assets-webpack-plugin": "^5.0.1", - "portfinder": "^1.0.13", - "postcss-loader": "^3.0.0", - "postcss-safe-parser": "^4.0.1", - "toml": "^3.0.0", - "url-loader": "^1.0.1", - "vue": "^2.6.10", - "vue-loader": "^15.7.1", - "vue-router": "^3.1.3", - "vue-server-renderer": "^2.6.10", - "vue-template-compiler": "^2.6.10", - "vuepress-html-webpack-plugin": "^3.2.0", - "vuepress-plugin-container": "^2.0.2", - "webpack": "^4.8.1", - "webpack-chain": "^4.6.0", - "webpack-dev-server": "^3.5.1", - "webpack-merge": "^4.1.2", - "webpackbar": "3.2.0" - } - }, - "@vuepress/markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.2.0.tgz", - "integrity": "sha512-RLRQmTu5wJbCO4Qv+J0K53o5Ew7nAGItLwWyzCbIUB6pRsya3kqSCViWQVlKlS53zFTmRHuAC9tJMRdzly3mCA==", - "requires": { - "@vuepress/shared-utils": "^1.2.0", - "markdown-it": "^8.4.1", - "markdown-it-anchor": "^5.0.2", - "markdown-it-chain": "^1.3.0", - "markdown-it-emoji": "^1.4.0", - "markdown-it-table-of-contents": "^0.4.0", - "prismjs": "^1.13.0" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", - "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - } - } - }, - "@vuepress/markdown-loader": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.2.0.tgz", - "integrity": "sha512-gOZzoHjfp/W6t+qKBRdbHS/9TwRnNuhY7V+yFzxofNONFHQULofIN/arG+ptYc2SuqJ541jqudNQW+ldHNMC2w==", - "requires": { - "@vuepress/markdown": "^1.2.0", - "loader-utils": "^1.1.0", - "lru-cache": "^5.1.1" - } - }, - "@vuepress/plugin-active-header-links": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.2.0.tgz", - "integrity": "sha512-vdi7l96pElJvEmcx6t9DWJNH25TIurS8acjN3+b7o4NzdaszFn5j6klN6WtI4Z+5BVDrxHP5W1F3Ebw8SZyupA==", - "requires": { - "lodash.debounce": "^4.0.8" - } - }, - "@vuepress/plugin-google-analytics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.2.0.tgz", - "integrity": "sha512-0zol5D4Efb5GKel7ADO/s65MLtKSLnOEGkeWzuipkWomSQPzP7TJ3+/RcYBnGdyBFHd1BSpTUHGK0b/IGwM3UA==" - }, - "@vuepress/plugin-last-updated": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.2.0.tgz", - "integrity": "sha512-j4uZb/MXDyG+v9QCG3T/rkiaOhC/ib7NKCt1cjn3GOwvWTDmB5UZm9EBhUpbDNrBgxW+SaHOe3kMVNO8bGOTGw==", - "requires": { - "cross-spawn": "^6.0.5" - } - }, - "@vuepress/plugin-nprogress": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.2.0.tgz", - "integrity": "sha512-0apt3Dp6XVCOkLViX6seKSEJgARihi+pX3/r8j8ndFp9Y+vmgLFZnQnGE5iKNi1ty+A6PZOK0RQcBjwTAU4pAw==", - "requires": { - "nprogress": "^0.2.0" - } - }, - "@vuepress/plugin-register-components": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.2.0.tgz", - "integrity": "sha512-C32b8sbGtDEX8I3SUUKS/w2rThiRFiKxmzNcJD996me7VY/4rgmZ8CxGtb6G9wByVoK0UdG1SOkrgOPdSCm80A==", - "requires": { - "@vuepress/shared-utils": "^1.2.0" - } - }, - "@vuepress/plugin-search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.2.0.tgz", - "integrity": "sha512-QU3JfnMfImDAArbJOVH1q1iCDE5QrT99GLpNGo6KQYZWqY1TWAbgyf8C2hQdaI03co1lkU2Wn/iqzHJ5WHlueg==" - }, - "@vuepress/shared-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.2.0.tgz", - "integrity": "sha512-wo5Ng2/xzsmIYCzvWxgLFlDBp7FkmJp2shAkbSurLNAh1vixhs0+LyDxsk01+m34ktJSp9rTUUsm6khw/Fvo0w==", - "requires": { - "chalk": "^2.3.2", - "diacritics": "^1.3.0", - "escape-html": "^1.0.3", - "fs-extra": "^7.0.1", - "globby": "^9.2.0", - "gray-matter": "^4.0.1", - "hash-sum": "^1.0.2", - "semver": "^6.0.0", - "upath": "^1.1.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@vuepress/theme-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.2.0.tgz", - "integrity": "sha512-mJxAMYQQv4OrGFsArMlONu8RpCzPUVx81dumkyTT4ay5PXAWTj+WDeFQLOT3j0g9QrDJGnHhbiw2aS+R/0WUyQ==", - "requires": { - "@vuepress/plugin-active-header-links": "^1.2.0", - "@vuepress/plugin-nprogress": "^1.2.0", - "@vuepress/plugin-search": "^1.2.0", - "docsearch.js": "^2.5.2", - "lodash": "^4.17.15", - "stylus": "^0.54.5", - "stylus-loader": "^3.0.2", - "vuepress-plugin-container": "^2.0.2", - "vuepress-plugin-smooth-scroll": "^0.0.3" - }, - "dependencies": { - "vuepress-plugin-smooth-scroll": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.3.tgz", - "integrity": "sha512-qsQkDftLVFLe8BiviIHaLV0Ea38YLZKKonDGsNQy1IE0wllFpFIEldWD8frWZtDFdx6b/O3KDMgVQ0qp5NjJCg==", - "requires": { - "smoothscroll-polyfill": "^0.4.3" - } - } - } - }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" - }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "requires": { - "acorn": "^4.0.4" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } - }, - "agentkeepalive": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", - "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=" - }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" - }, - "algoliasearch": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-3.35.1.tgz", - "integrity": "sha512-K4yKVhaHkXfJ/xcUnil04xiSrB8B8yHZoFEhWNpXg23eiCnqvTZw1tn/SqvdsANlYHLJlKl0qi3I/Q2Sqo7LwQ==", - "requires": { - "agentkeepalive": "^2.2.0", - "debug": "^2.6.9", - "envify": "^4.0.0", - "es6-promise": "^4.1.0", - "events": "^1.1.0", - "foreach": "^2.0.5", - "global": "^4.3.2", - "inherits": "^2.0.1", - "isarray": "^2.0.1", - "load-script": "^1.0.0", - "object-keys": "^1.0.11", - "querystring-es3": "^0.2.1", - "reduce": "^1.0.1", - "semver": "^5.1.0", - "tunnel-agent": "^0.6.0" - } - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" - }, - "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", - "requires": { - "type-fest": "^0.8.1" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "autocomplete.js": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.36.0.tgz", - "integrity": "sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q==", - "requires": { - "immediate": "^3.2.3" - } - }, - "autoprefixer": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.3.tgz", - "integrity": "sha512-8T5Y1C5Iyj6PgkPSFd0ODvK9DIleuPKUPYniNxybS47g2k2wFgLZ46lGQHlBuGKIAEV8fbCDfKCCRS1tvOgc3Q==", - "requires": { - "browserslist": "^4.8.0", - "caniuse-lite": "^1.0.30001012", - "chalk": "^2.4.2", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.23", - "postcss-value-parser": "^4.0.2" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", - "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" - }, - "axios": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", - "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - }, - "babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", - "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-module-resolver": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz", - "integrity": "sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==", - "requires": { - "find-babel-config": "^1.1.0", - "glob": "^7.1.2", - "pkg-up": "^2.0.0", - "reselect": "^3.0.1", - "resolve": "^1.4.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz", - "integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==", - "requires": { - "caniuse-lite": "^1.0.30001015", - "electron-to-chromium": "^1.3.322", - "node-releases": "^1.1.42" - } - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - } - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" - }, - "buffer-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-json/-/buffer-json-2.0.0.tgz", - "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==" - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "cac": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.5.3.tgz", - "integrity": "sha512-wZfzSWVXuue1H3J7TDNjbzg4KTqPXCmh7F3QIzEYXfnhMCcOUrx99M7rpO2UDVJA9dqv3butGj2nHvCV47CmPg==" - }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cache-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cache-loader/-/cache-loader-3.0.1.tgz", - "integrity": "sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw==", - "requires": { - "buffer-json": "^2.0.0", - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.2.3", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001016", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz", - "integrity": "sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "requires": { - "is-regex": "^1.0.3" - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "requires": { - "source-map": "~0.6.0" - } - }, - "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, - "clipboard-copy": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.1.0.tgz", - "integrity": "sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA==" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "compressible": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", - "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", - "requires": { - "mime-db": ">= 1.40.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.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==" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" - }, - "consola": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.11.0.tgz", - "integrity": "sha512-2bcAqHastlPSCvZ+ur8bgHInGAWvUnysWz3h3xRX+/XZoCY7avolJJnVXOPGoVoyCcg1b231XixonoArmgxaoA==" - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" - }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "requires": { - "bluebird": "^3.1.1" - } - }, - "constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", - "requires": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "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": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "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": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "copy-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", - "requires": { - "cacache": "^12.0.3", - "find-cache-dir": "^2.1.0", - "glob-parent": "^3.1.0", - "globby": "^7.1.1", - "is-glob": "^4.0.1", - "loader-utils": "^1.2.3", - "minimatch": "^3.0.4", - "normalize-path": "^3.0.0", - "p-limit": "^2.2.1", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" - } - } - }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - } - }, - "css-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz", - "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==", - "requires": { - "camelcase": "^5.2.0", - "icss-utils": "^4.1.0", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.14", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^2.0.6", - "postcss-modules-scope": "^2.1.0", - "postcss-modules-values": "^2.0.0", - "postcss-value-parser": "^3.3.0", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "requires": { - "css": "^2.0.0" - } - }, - "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-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - } - }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" - }, - "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=" - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=" - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "requires": { - "postcss": "^7.0.0" - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==" - }, - "csso": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.2.tgz", - "integrity": "sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==", - "requires": { - "css-tree": "1.0.0-alpha.37" - } - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "optional": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" - }, - "diacritics": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", - "integrity": "sha1-PvqHMj67hj5mls67AILUj/PW96E=" - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "requires": { - "path-type": "^3.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "docsearch.js": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/docsearch.js/-/docsearch.js-2.6.3.tgz", - "integrity": "sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A==", - "requires": { - "algoliasearch": "^3.24.5", - "autocomplete.js": "0.36.0", - "hogan.js": "^3.0.2", - "request": "^2.87.0", - "stack-utils": "^1.0.1", - "to-factory": "^1.0.0", - "zepto": "^1.2.0" - } - }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "requires": { - "utila": "~0.4" - } - }, - "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.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" - } - } - }, - "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "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==" - }, - "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" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "requires": { - "is-obj": "^1.0.0" - } - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "electron-to-chromium": { - "version": "1.3.322", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz", - "integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==" - }, - "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" - }, - "envify": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/envify/-/envify-4.1.0.tgz", - "integrity": "sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw==", - "requires": { - "esprima": "^4.0.0", - "through": "~2.3.4" - } - }, - "envinfo": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.5.0.tgz", - "integrity": "sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==" - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz", - "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "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==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "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=", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" - }, - "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz", - "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==", - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^1.0.0" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-babel-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz", - "integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==", - "requires": { - "json5": "^0.5.1", - "path-exists": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", - "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.3", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "3.2.6", - "bundled": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.7", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "bundled": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "optional": true - }, - "minipass": { - "version": "2.9.0", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.4.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.7", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "optional": true, - "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" - } - }, - "rimraf": { - "version": "2.7.1", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.7.1", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.13", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "bundled": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "fuse.js": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.6.tgz", - "integrity": "sha512-H6aJY4UpLFwxj1+5nAvufom5b2BT2v45P1MkPvdGIK8fWjQx/7o6tTT1+ALV0yawQvbmvCF0ufl2et8eJ7v7Cg==" - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" - }, - "global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "requires": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "globby": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", - "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^1.0.2", - "dir-glob": "^2.2.2", - "fast-glob": "^2.2.6", - "glob": "^7.1.3", - "ignore": "^4.0.3", - "pify": "^4.0.1", - "slash": "^2.0.0" - } - }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "optional": true, - "requires": { - "delegate": "^3.1.2" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "gray-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.2.tgz", - "integrity": "sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw==", - "requires": { - "js-yaml": "^3.11.0", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - } - }, - "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=" - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hogan.js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", - "integrity": "sha1-TNnhq9QpQUbnZ55B14mHMrAse/0=", - "requires": { - "mkdirp": "0.3.0", - "nopt": "1.0.10" - } - }, - "hotkeys-js": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.7.3.tgz", - "integrity": "sha512-CSaeVPAKEEYNexYR35znMJnCqoofk7oqG/AOOqWow1qDT0Yxy+g+Y8Hs/LhGlsZaSJ7973YN6/N41LAr3t30QQ==" - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", - "requires": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" - } - } - } - } - }, - "html-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", - "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=" - }, - "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==" - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" - }, - "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" - }, - "icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "requires": { - "postcss": "^7.0.14" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" - }, - "immediate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", - "integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=" - }, - "import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "requires": { - "import-from": "^2.1.0" - } - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "requires": { - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "intersection-observer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz", - "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg==" - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" - }, - "is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", - "requires": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "requires": { - "path-is-inside": "^1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "requires": { - "has": "^1.0.3" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "javascript-stringify": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", - "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=" - }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" - }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "last-call-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", - "requires": { - "lodash": "^4.17.5", - "webpack-sources": "^1.1.0" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, - "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "requires": { - "uc.micro": "^1.0.1" - } - }, - "load-script": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", - "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=" - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash.chunk": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", - "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "loglevel": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", - "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==" - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "lunr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", - "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==" - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", - "requires": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "markdown-it-anchor": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz", - "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==" - }, - "markdown-it-attrs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-3.0.1.tgz", - "integrity": "sha512-fcpdmxdEsctDVJEunPyrirVtU/6zcTMxPxAu4Ofz51PKAa8vRMpmGQXsmXx1HTdIdUPoDonm/RhS/+jTNywj/Q==" - }, - "markdown-it-chain": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz", - "integrity": "sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ==", - "requires": { - "webpack-chain": "^4.9.0" - } - }, - "markdown-it-container": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-2.0.0.tgz", - "integrity": "sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU=" - }, - "markdown-it-emoji": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", - "integrity": "sha1-m+4OmpkKljupbfaYDE/dsF37Tcw=" - }, - "markdown-it-ins": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.0.tgz", - "integrity": "sha512-+vyAdBuMGwmT2yMlAFJSx2VR/0QZ1onQ/Mkkmr4l9tDFOh5sVoAgRbkgbuSsk+sxJ9vaMH/IQ323ydfvQrPO/Q==" - }, - "markdown-it-table-of-contents": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz", - "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==" - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "requires": { - "source-map": "^0.6.1" - } - }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" - }, - "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", - "requires": { - "mime-db": "1.42.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "requires": { - "dom-walk": "^0.1.0" - } - }, - "mini-css-extract-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", - "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", - "requires": { - "loader-utils": "^1.1.0", - "normalize-url": "^2.0.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==" - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "node-releases": { - "version": "1.1.42", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.42.tgz", - "integrity": "sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==", - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "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==", - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "object-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", - "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "opencollective-postinstall": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", - "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==" - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "requires": { - "is-wsl": "^1.1.0" - } - }, - "optimize-css-assets-webpack-plugin": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", - "integrity": "sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA==", - "requires": { - "cssnano": "^4.1.10", - "last-call-webpack-plugin": "^3.0.0" - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "requires": { - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "requires": { - "no-case": "^2.2.0" - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - } - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "requires": { - "find-up": "^2.1.0" - } - }, - "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "postcss": { - "version": "7.0.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.24.tgz", - "integrity": "sha512-Xl0XvdNWg+CblAXzNvbSOUvgJXwSjmbAKORqyw9V2AlHrm1js2gFw9y3jibBAhpKZi8b5JzJCVh/FyzPsTtgTA==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", - "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - }, - "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-load-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", - "requires": { - "cosmiconfig": "^5.0.0", - "import-cwd": "^2.0.0" - } - }, - "postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "requires": { - "postcss": "^7.0.5" - } - }, - "postcss-modules-local-by-default": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", - "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0", - "postcss-value-parser": "^3.3.1" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-modules-scope": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz", - "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==", - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - } - }, - "postcss-modules-values": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", - "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^7.0.6" - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-safe-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", - "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, - "pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" - }, - "prismjs": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", - "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", - "requires": { - "clipboard": "^2.0.0" - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "psl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", - "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pug": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", - "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", - "requires": { - "pug-code-gen": "^2.0.2", - "pug-filters": "^3.1.1", - "pug-lexer": "^4.1.0", - "pug-linker": "^3.0.6", - "pug-load": "^2.0.12", - "pug-parser": "^5.0.1", - "pug-runtime": "^2.0.5", - "pug-strip-comments": "^1.0.4" - } - }, - "pug-attrs": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", - "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", - "requires": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.5" - } - }, - "pug-code-gen": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", - "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", - "requires": { - "constantinople": "^3.1.2", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.4", - "pug-error": "^1.3.3", - "pug-runtime": "^2.0.5", - "void-elements": "^2.0.1", - "with": "^5.0.0" - } - }, - "pug-error": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", - "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" - }, - "pug-filters": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", - "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", - "requires": { - "clean-css": "^4.1.11", - "constantinople": "^3.0.1", - "jstransformer": "1.0.0", - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" - } - }, - "pug-lexer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", - "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", - "requires": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.3" - } - }, - "pug-linker": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", - "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", - "requires": { - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8" - } - }, - "pug-load": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", - "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", - "requires": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.8" - } - }, - "pug-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", - "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", - "requires": { - "pug-error": "^1.3.3", - "token-stream": "0.0.1" - } - }, - "pug-plain-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pug-plain-loader/-/pug-plain-loader-1.0.0.tgz", - "integrity": "sha512-mDfq/qvJJ0xdug38mZ1ObW0BQTx9kAHnKqotXC+C00XQkKmsWaMe90JUg/kN4lS6MU7tpVsMZ+rmcnBSPfDtHA==", - "requires": { - "loader-utils": "^1.1.0" - } - }, - "pug-runtime": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", - "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" - }, - "pug-strip-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", - "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", - "requires": { - "pug-error": "^1.3.3" - } - }, - "pug-walk": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", - "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "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": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "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==" - } - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "reduce": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.2.tgz", - "integrity": "sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ==", - "requires": { - "object-keys": "^1.1.0" - } - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" - }, - "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regenerator-transform": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", - "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", - "requires": { - "private": "^0.1.6" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "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==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" - }, - "regjsparser": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.1.tgz", - "integrity": "sha512-7LutE94sz/NKSYegK+/4E77+8DipxF+Qn2Tmu362AcmsF2NYq/wx3+ObvU90TKEhjf7hQoFXo23ajjrXP7eUgg==", - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", - "requires": { - "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" - }, - "dependencies": { - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "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==" - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - } - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" - }, - "reselect": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz", - "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=" - }, - "resolve": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", - "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "requires": { - "aproba": "^1.1.1" - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "requires": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - } - }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "optional": true - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" - }, - "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", - "requires": { - "node-forge": "0.9.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==" - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - } - } - }, - "sitemap": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz", - "integrity": "sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg==", - "requires": { - "lodash.chunk": "^4.2.0", - "lodash.padstart": "^4.6.1", - "whatwg-url": "^7.0.0", - "xmlbuilder": "^13.0.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" - }, - "smoothscroll-polyfill": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz", - "integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - } - }, - "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==", - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "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.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "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.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "std-env": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz", - "integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==", - "requires": { - "ci-info": "^1.6.0" - } - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "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" - }, - "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==" - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "stylus": { - "version": "0.54.7", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.7.tgz", - "integrity": "sha512-Yw3WMTzVwevT6ZTrLCYNHAFmanMxdylelL3hkWNgPMeTCpMwpV3nXjpOHuBXtFv7aiO2xRuQS6OoAdgkNcSNug==", - "requires": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.3", - "mkdirp": "~0.5.x", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.0.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - } - } - }, - "stylus-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", - "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", - "requires": { - "loader-utils": "^1.0.2", - "lodash.clonedeep": "^4.5.0", - "when": "~3.6.x" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=" - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - }, - "terser": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", - "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } - }, - "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "tiny-cookie": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tiny-cookie/-/tiny-cookie-2.3.1.tgz", - "integrity": "sha512-C4x1e8dHfKf03ewuN9aIZzzOfN2a6QKhYlnHdzJxmmjMTLqcskI20F+EplszjODQ4SHmIGFJrvUUnBMS/bJbOA==" - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "optional": true - }, - "tm-tooltip": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/tm-tooltip/-/tm-tooltip-0.0.10.tgz", - "integrity": "sha512-ud+LicFlyhwLwO/nfGvtASg3TyUCjaEQC5GCZaE/JzXQmwK0ndb+4F0/ek8xr07rOENFUIT9N+1tmUDqCAtv1g==", - "requires": { - "markdown-it": "^9.1.0" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "markdown-it": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-9.1.0.tgz", - "integrity": "sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w==", - "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - } - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" - }, - "to-factory": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-factory/-/to-factory-1.0.0.tgz", - "integrity": "sha1-hzivi9lxIK0dQEeXKtpVY7+UebE=" - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" - }, - "toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "requires": { - "punycode": "^2.1.0" - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==" - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "url-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", - "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" - } - }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" - }, - "v-runtime-template": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/v-runtime-template/-/v-runtime-template-1.10.0.tgz", - "integrity": "sha512-WLlq9jUepSfUrMEenw3mn7FDXX6hhbl11JjC1OKhwLzifHzVrY5a696TUHDPyj9jke3GGnR7b+2T3od/RL5cww==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "vendors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz", - "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" - }, - "vue": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", - "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" - }, - "vue-hot-reload-api": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", - "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==" - }, - "vue-loader": { - "version": "15.8.3", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.8.3.tgz", - "integrity": "sha512-yFksTFbhp+lxlm92DrKdpVIWMpranXnTEuGSc0oW+Gk43M9LWaAmBTnfj5+FCdve715mTHvo78IdaXf5TbiTJg==", - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - } - }, - "vue-router": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.3.tgz", - "integrity": "sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ==" - }, - "vue-server-renderer": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.11.tgz", - "integrity": "sha512-V3faFJHr2KYfdSIalL+JjinZSHYUhlrvJ9pzCIjjwSh77+pkrsXpK4PucdPcng57+N77pd1LrKqwbqjQdktU1A==", - "requires": { - "chalk": "^1.1.3", - "hash-sum": "^1.0.2", - "he": "^1.1.0", - "lodash.template": "^4.5.0", - "lodash.uniq": "^4.5.0", - "resolve": "^1.2.0", - "serialize-javascript": "^2.1.2", - "source-map": "0.5.6" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "vue-style-loader": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", - "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==", - "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - } - }, - "vue-template-compiler": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", - "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", - "requires": { - "de-indent": "^1.0.2", - "he": "^1.1.0" - } - }, - "vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" - }, - "vuepress": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.2.0.tgz", - "integrity": "sha512-EfHo8Cc73qo+1Pm18hM0qOGynmDr8q5fu2664obynsdCJ1zpvoShVnA0Msraw4SI2xDc0iAoIb3dTwxUIM8DAw==", - "requires": { - "@vuepress/core": "^1.2.0", - "@vuepress/theme-default": "^1.2.0", - "cac": "^6.3.9", - "envinfo": "^7.2.0", - "opencollective-postinstall": "^2.0.2" - } - }, - "vuepress-html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A==", - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "vuepress-plugin-container": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.2.tgz", - "integrity": "sha512-Df5KoIDMYiFg45GTfFw2hIiLGSsjhms4f3ppl2UIBf5nWMxi2lfifcoo8MooMSfxboxRZjoDccqQfu0fypaKrQ==", - "requires": { - "markdown-it-container": "^2.0.0" - } - }, - "vuepress-plugin-sitemap": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/vuepress-plugin-sitemap/-/vuepress-plugin-sitemap-2.3.1.tgz", - "integrity": "sha512-n+8lbukhrKrsI9H/EX0EBgkE1pn85LAQFvQ5dIvrZP4Kz6JxPOPPNTQmZMhahQV1tXbLZQCEN7A1WZH4x+arJQ==", - "requires": { - "sitemap": "^3.0.0" - } - }, - "vuepress-plugin-smooth-scroll": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.9.tgz", - "integrity": "sha512-UXX+HLZO1NKVwyiOJlj0smh8F9dKnwybjEi7w/Mj9EYLhKrNYr5uXs+N+OTt8VwKCn3f0vZ1XAwFIjsPlD7GJA==", - "requires": { - "smoothscroll-polyfill": "^0.4.4" - } - }, - "vuepress-theme-cosmos": { -<<<<<<< HEAD - "version": "1.0.113", - "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.113.tgz", - "integrity": "sha512-gSr9Hqp42Ursw7G9N7UG5KQMEVxEsFlPnfb5VlDo3k/s1mruSsZvXczfGEkno0U+lATGROWWU+No4xpOqMADiA==", -======= - "version": "1.0.105", - "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.105.tgz", - "integrity": "sha512-UZOPEnNuz5r6cB/o75Qj81/n5BOlpDkf+lGpNxo70C1Eqqg/pjzgj3phhxzSvgXbY5bpdCyeXWySQ8zZnnYtPA==", ->>>>>>> master - "requires": { - "@cosmos-ui/vue": "^0.5.16", - "@vuepress/plugin-last-updated": "^1.2.0", - "@vuepress/plugin-search": "^1.1.0", - "algoliasearch": "^3.35.1", - "axios": "^0.19.0", - "clipboard-copy": "^3.1.0", - "docsearch.js": "^2.6.3", - "fuse.js": "^3.4.6", - "gray-matter": "^4.0.2", - "hotkeys-js": "^3.7.3", - "intersection-observer": "^0.7.0", - "lunr": "^2.3.8", - "markdown-it": "^10.0.0", - "markdown-it-attrs": "^3.0.1", - "markdown-it-ins": "^3.0.0", - "prismjs": "^1.17.1", - "pug": "^2.0.4", - "pug-plain-loader": "^1.0.0", - "stylus": "^0.54.7", - "stylus-loader": "^3.0.2", - "tm-tooltip": "0.0.10", - "v-runtime-template": "^1.10.0", - "vuepress": "^1.2.0", - "vuepress-plugin-sitemap": "^2.3.1", - "vuepress-plugin-smooth-scroll": "0.0.9" - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" - }, - "webpack": { - "version": "4.41.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.3.tgz", - "integrity": "sha512-EcNzP9jGoxpQAXq1VOoTet0ik7/VVU1MovIfcUSAjLowc7GhcQku/sOXALvq5nPpSei2HF6VRhibeJSC3i/Law==", - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "webpack-chain": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", - "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", - "requires": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^1.6.0" - } - }, - "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==", - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } - } - }, - "webpack-dev-server": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", - "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.4", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "webpack-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", - "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", - "requires": { - "lodash": "^4.17.15" - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "webpackbar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-3.2.0.tgz", - "integrity": "sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw==", - "requires": { - "ansi-escapes": "^4.1.0", - "chalk": "^2.4.1", - "consola": "^2.6.0", - "figures": "^3.0.0", - "pretty-time": "^1.1.0", - "std-env": "^2.2.1", - "text-table": "^0.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", - "requires": { - "http-parser-js": ">=0.4.0 <0.4.11", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "when": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", - "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", - "requires": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xmlbuilder": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", - "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "zepto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", - "integrity": "sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g=" - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 06255f68eb6a..000000000000 --- a/docs/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "description": "## Get Started", - "main": "index.js", - "scripts": { - "preserve": "./pre.sh", - "serve": "trap 'exit 0' SIGINT; vuepress dev --no-cache", - "postserve": "./post.sh", - "prebuild": "./pre.sh", - "build": "trap 'exit 0' SIGINT; vuepress build --no-cache", - "postbuild": "./post.sh" - }, - "author": "", - "license": "ISC", - "dependencies": { - "@vuepress/plugin-google-analytics": "^1.2.0", - "tiny-cookie": "^2.3.1", - "vuepress-plugin-smooth-scroll": "0.0.9", - "vuepress-theme-cosmos": "^1.0.113" - } -} \ No newline at end of file diff --git a/docs/protodoc-markdown.tmpl b/docs/protodoc-markdown.tmpl new file mode 100644 index 000000000000..28201837e5ec --- /dev/null +++ b/docs/protodoc-markdown.tmpl @@ -0,0 +1,105 @@ + +# Protobuf Documentation +
+ +## Table of Contents +{{range .Files}} +{{$file_name := .Name}}- [{{.Name}}](#{{.Name}}) + {{- if .Messages }} + {{range .Messages}} - [{{.LongName}}](#{{.FullName}}) + {{end}} + {{- end -}} + {{- if .Enums }} + {{range .Enums}} - [{{.LongName}}](#{{.FullName}}) + {{end}} + {{- end -}} + {{- if .Extensions }} + {{range .Extensions}} - [File-level Extensions](#{{$file_name}}-extensions) + {{end}} + {{- end -}} + {{- if .Services }} + {{range .Services}} - [{{.Name}}](#{{.FullName}}) + {{end}} + {{- end -}} +{{end}} +- [Scalar Value Types](#scalar-value-types) + +{{range .Files}} +{{$file_name := .Name}} + +

Top

+ +## {{.Name}} +{{.Description}} + +{{range .Messages}} + + +### {{.LongName}} +{{.Description}} + +{{if .HasFields}} +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +{{range .Fields -}} + | `{{.Name}}` | [{{.LongType}}](#{{.FullType}}) | {{.Label}} | {{if (index .Options "deprecated"|default false)}}**Deprecated.** {{end}}{{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | +{{end}} +{{end}} + +{{if .HasExtensions}} +| Extension | Type | Base | Number | Description | +| --------- | ---- | ---- | ------ | ----------- | +{{range .Extensions -}} + | `{{.Name}}` | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | +{{end}} +{{end}} + +{{end}} + +{{range .Enums}} + + +### {{.LongName}} +{{.Description}} + +| Name | Number | Description | +| ---- | ------ | ----------- | +{{range .Values -}} + | {{.Name}} | {{.Number}} | {{nobr .Description}} | +{{end}} + +{{end}} + +{{if .HasExtensions}} + + +### File-level Extensions +| Extension | Type | Base | Number | Description | +| --------- | ---- | ---- | ------ | ----------- | +{{range .Extensions -}} + | `{{.Name}}` | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | +{{end}} +{{end}} + +{{range .Services}} + + +### {{.Name}} +{{.Description}} + +| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | +| ----------- | ------------ | ------------- | ------------| ------- | -------- | +{{range .Methods -}} + | `{{.Name}}` | [{{.RequestLongType}}](#{{.RequestFullType}}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseFullType}}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description}} | {{with (index .Options "google.api.http")}}{{range .Rules}}{{.Method}}|{{.Pattern}}{{end}}{{end}}| +{{end}} +{{end}} + +{{end}} + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +{{range .Scalars -}} + | {{.ProtoType}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | {{.PythonType}} | {{.GoType}} | {{.CSharp}} | {{.PhpType}} | {{.RubyType}} | +{{end}} diff --git a/docs/run-node/README.md b/docs/run-node/README.md new file mode 100644 index 000000000000..0a2f24b87939 --- /dev/null +++ b/docs/run-node/README.md @@ -0,0 +1,15 @@ + + +# Running a Node, API and CLI + +This folder contains documentation on how to run a node and interact with it. + +1. [Setting up the keyring](./keyring.md) +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) diff --git a/docs/run-node/cosmovisor.md b/docs/run-node/cosmovisor.md new file mode 100644 index 000000000000..99cb81e0cc45 --- /dev/null +++ b/docs/run-node/cosmovisor.md @@ -0,0 +1,170 @@ +# Cosmosvisor Quick Start + +`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 + +Run: + +`go get github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor` + +## Command Line Arguments And Environment Variables + +All arguments passed to the `cosmovisor` program will be passed to the current daemon binary (as a subprocess). +It will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. Because of that, it cannot accept +any command line arguments, nor print anything to output (unless it terminates unexpectedly before executing a +binary). + +`cosmovisor` reads its configuration from environment variables: + +* `DAEMON_HOME` is the location where upgrade binaries should be kept (e.g. `$HOME/.gaiad` or `$HOME/.xrnd`). +* `DAEMON_NAME` is the name of the binary itself (eg. `xrnd`, `gaiad`, `simd`, etc). +* `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*) if set to `true` will enable auto-downloading of new binaries +(for security reasons, this is intended for full nodes rather than validators). +* `DAEMON_RESTART_AFTER_UPGRADE` (*optional*) if set to `true` it will restart the sub-process with the same +command line arguments and flags (but new binary) after a successful upgrade. By default, `cosmovisor` dies +afterwards and allows the supervisor to restart it if needed. Note that this will not auto-restart the child +if there was an error. + +## Data Folder Layout + +`$DAEMON_HOME/cosmovisor` is expected to belong completely to `cosmovisor` and +subprocesses that are controlled by it. The folder content is organised as follows: + +``` +. +├── current -> genesis or upgrades/ +├── genesis +│   └── bin +│   └── $DAEMON_NAME +└── upgrades + └── + └── bin + └── $DAEMON_NAME +``` + +Each version of the Cosmos SDK application is stored under either `genesis` or `upgrades/`, which holds `bin/$DAEMON_NAME` +along with any other needed files such as auxiliary client programs or libraries. `current` is a symbolic link to the currently +active folder (so `current/bin/$DAEMON_NAME` is the currently active binary). + +*Note: the `name` variable in `upgrades/` holds 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 their default data directory (e.g. `$HOME/.gaiad`) which is independent of +the `$DAEMON_HOME`. You can choose to set `$DAEMON_HOME` to the actual binary's home directory and then end up +with a configuation like the following, but this is left as a choice to the system admininstrator for best +directory layout: + +``` +.gaiad +├── config +├── data +└── cosmovisor +``` + +## Usage + +The system administrator admin is responsible for: +* installing the `cosmovisor` binary and configure the host's init system (e.g. `systemd`, `launchd`, etc) along with the environmental variables appropriately; +* installing the `genesis` folder manually; +* installing the `upgrades/` folders manually. + +`cosmovisor` will set the `current` link to point to `genesis` at first start (when no `current` link exists) and handles +binaries switch overs at the correct points in time, so that the system administrator can prepare days in advance and relax at upgrade time. + +Note that blockchain applications that wish to support upgrades may package up a genesis `cosmovisor` tarball with this information, +just as they prepare the genesis binary tarball. In fact, they may offer a tarball will all upgrades up to current point for easy download +for those who wish to sync a fullnode from start. + +The `DAEMON` specific code and operations (e.g. tendermint config, the application db, syncing blocks, etc) are performed as normal. +Application binaries' directives such as command-line flags and environment variables work normally. + +## Example: simd + +The following instructions provide a demonstration of `cosmovisor`'s integration with the `simd` application +shipped along the Cosmos SDK's source code. + +First compile `simd`: + +``` +cd cosmos-sdk/ +make build +``` + +Create a new key and setup the `simd` node: + +``` +rm -rf $HOME/.simapp +./build/simd keys --keyring-backend=test add validator +./build/simd init testing --chain-id test +./build/simd add-genesis-account --keyring-backend=test $(./build/simd keys --keyring-backend=test show validator -a) 1000000000stake,1000000000validatortoken +./build/simd gentx --keyring-backend test --chain-id test validator 100000stake +./build/simd collect-gentxs +``` + +Set the required environment variables: + +``` +export DAEMON_NAME=simd # binary name +export DAEMON_HOME=$HOME/.simapp # daemon's home directory +``` + +Create the `cosmovisor`’s genesis folders and deploy the binary: + +``` +mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin +cp ./build/simd $DAEMON_HOME/cosmovisor/genesis/bin +``` + +For the sake of this demonstration, we would amend `voting_params.voting_period` in `.simapp/config/genesis.json` to a reduced time ~5 minutes (300s) and eventually launch `cosmosvisor`: + +``` +cosmovisor start +``` + +Submit a software upgrade proposal: + +``` +./build/simd tx gov submit-proposal software-upgrade test1 --title "upgrade-demo" --description "upgrade" --from validator --upgrade-height 100 --deposit 10000000stake --chain-id test --keyring-backend test -y +``` + +Query the proposal to ensure it was correctly broadcast and added to a block: + +``` +./build/simd query gov proposal 1 +``` + +Submit a `Yes` vote for the upgrade proposal: + +``` +./build/simd tx gov vote 1 yes --from validator --keyring-backend test --chain-id test -y +``` + +For the sake of this demonstration, we will hardcode a modification in `simapp` to simulate a code change. +In `simapp/app.go`, find the line containing the upgrade Keeper initialisation, it should look like +`app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath)`. +After that line, add the following snippet: + + ``` + app.UpgradeKeeper.SetUpgradeHandler("test1", func(ctx sdk.Context, plan upgradetypes.Plan) { + // Add some coins to a random account + addr, err := sdk.AccAddressFromBech32("cosmos18cgkqduwuh253twzmhedesw3l7v3fm37sppt58") + if err != nil { + panic(err) + } + err = app.BankKeeper.AddCoins(ctx, addr, sdk.Coins{sdk.Coin{Denom: "stake", Amount: sdk.NewInt(345600000)}}) + if err != nil { + panic(err) + } + }) +``` + +Now recompile a new binary and place it in `$DAEMON_HOME/cosmosvisor/upgrades/test1/bin`: + +``` +make build +cp ./build/simd $DAEMON_HOME/cosmovisor/upgrades/test1/bin +``` + +The upgrade will occur automatically at height 100. diff --git a/docs/run-node/interact-node.md b/docs/run-node/interact-node.md new file mode 100644 index 000000000000..8cfea2527a41 --- /dev/null +++ b/docs/run-node/interact-node.md @@ -0,0 +1,242 @@ + + +# Interacting with the Node + +There are multiple ways to interact with a node: using the CLI, using gRPC or using the REST endpoints. {synopsis} + +## Pre-requisite Readings + +- [gRPC, REST and Tendermint Endpoints](../core/grpc_rest.md) {prereq} +- [Running a Node](./run-node.md) {prereq} + +## Using the CLI + +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 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: + +```bash +simd keys add recipient --keyring-backend test + +# Put the generated address in a variable for later use. +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 1000000stake --chain-id my-test-chain --keyring-backend test + +# Check that the recipient account did receive the tokens. +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 --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 +``` + +You should see two delegations, the first one made from the `gentx`, and the second one you just performed from the `recipient` account. + +## Using gRPC + +The Protobuf ecosystem developed tools for different use cases, including code-generation from `*.proto` files into various languages. These tools allow the building of clients easily. Often, the client connection (i.e. the transport) can be plugged and replaced very easily. Let's explore one of the most popular transport: [gRPC](../core/grpc_rest.md). + +Since the code generation library largely depends on your own tech stack, we will only present three alternatives: + +- `grpcurl` for generic debugging and testing, +- programmatically via Go, +- CosmJS for JavaScript/TypeScript developers. + +### grpcurl + +[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`](../run-node/run-node.md#configuring-the-node-using-apptoml)): + +```bash +grpcurl -plaintext localhost:9090 list +``` + +You should see a list of gRPC services, like `cosmos.bank.v1beta1.Query`. This is called reflection, which is a Protobuf endpoint returning a description of all available endpoints. Each of these represents a different Protobuf service, and each service exposes multiple RPC methods you can query against. + +In the Cosmos SDK, we use [gogoprotobuf](https://github.com/gogo/protobuf) for code generation, and [grpc-go](https://github.com/grpc/grpc-go) for creating the gRPC server. Unfortunately, these two don't play well together, and more in-depth reflection (such as using grpcurl's `describe`) is not possible. See [this issue](https://github.com/grpc/grpc-go/issues/1873) for more info. + +Instead, we need to manually pass the reference to relevant `.proto` files. For example: + +```bash +grpcurl \ + -import-path ./proto \ # Import these proto files too + -import-path ./third_party/proto \ # Import these proto files too + -proto ./proto/cosmos/bank/v1beta1/query.proto \ # That's the proto file with the description of your service + localhost:9090 \ + describe cosmos.bank.v1beta1.Query # Service we want to inspect +``` + +Once the Protobuf definitions are given, making a gRPC query is then straightforward, by calling the correct `Query` service RPC method, and by passing the request argument as data (`-d` flag): + +```bash +grpcurl \ + -plaintext + -import-path ./proto \ + -import-path ./third_party/proto \ + -proto ./proto/cosmos/bank/v1beta1/query.proto \ + -d '{"address":"$MY_VALIDATOR"}' \ + localhost:9090 \ + cosmos.bank.v1beta1.Query/AllBalances +``` + +The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). + +#### Query for historical state using grpcurl + +You may also query for historical data by passing some [gRPC metadata](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) to the query: the `x-cosmos-block-height` metadata should contain the block to query. Using grpcurl as above, the command looks like: + +```bash +grpcurl \ + -plaintext + -import-path ./proto \ + -import-path ./third_party/proto \ + -proto ./proto/cosmos/bank/v1beta1/query.proto \ + -H "x-cosmos-block-height: 279256" \ + -d '{"address":"$MY_VALIDATOR"}' \ + localhost:9090 \ + cosmos.bank.v1beta1.Query/AllBalances +``` + +Assuming the state at that block has not yet been pruned by the node, this query should return a non-empty response. + +### Programmatically via Go + +The following snippet shows how to query the state using gRPC inside a Go program. The idea is to create a gRPC connection, and use the Protobuf-generated client code to query the gRPC server. + +```go +import ( + "context" + "fmt" + + "google.golang.org/grpc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +func queryState() error { + myAddress, err := sdk.AccAddressFromBech32("cosmos1...") + if err != nil { + return err + } + + // Create a connection to the gRPC server. + grpcConn := grpc.Dial( + "127.0.0.1:9090", // Or your gRPC server address. + grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. + ) + defer grpcConn.Close() + + // This creates a gRPC client to query the x/bank service. + bankClient := banktypes.NewQueryClient(grpcConn) + bankRes, err := bankClient.Balance( + context.Background(), + &banktypes.QueryBalanceRequest{Address: myAddress, Denom: "atom"}, + ) + if err != nil { + return err + } + + fmt.Println(bankRes.GetBalance()) // Prints the account balance + + return nil +} +``` + +You can replace the query client (here we are using `x/bank`'s) with one generated from any other Protobuf service. The list of all available gRPC query endpoints is [coming soon](https://github.com/cosmos/cosmos-sdk/issues/7786). + +#### Query for historical state using Go + +Querying for historical blocks is done by adding the block height metadata in the gRPC request. + +```go +import ( + "context" + "fmt" + + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +func queryState() error { + // --snip-- + + var header metadata.MD + bankRes, err = bankClient.Balance( + metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "12"), // Add metadata to request + &banktypes.QueryBalanceRequest{Address: myAddress, Denom: denom}, + grpc.Header(&header), // Retrieve header from response + ) + if err != nil { + return err + } + blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) + + fmt.Println(blockHeight) // Prints the block height (12) + + return nil +} +``` + +### CosmJS + +CosmJS documentation can be found at [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs). As of January 2021, CosmJS documentation is still work in progress. + +## Using the REST Endpoints + +As described in the [gRPC guide](../core/grpc_rest.md), all gRPC services on the Cosmos SDK are made available for more convenient REST-based queries through gRPC-gateway. The format of the URL path is based on the Protobuf service method's full-qualified name, but may contain small customizations so that final URLs look more idiomatic. For example, the REST endpoint for the `cosmos.bank.v1beta1.Query/AllBalances` method is `GET /cosmos/bank/v1beta1/balances/{address}`. Request arguments are passed as query parameters. + +As a concrete example, the `curl` command to make balances request is: + +```bash +curl \ + -X GET \ + -H "Content-Type: application/json" \ + http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR +``` + +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`](../run-node/run-node.md#configuring-the-node-using-apptoml) file. + +### Query for historical state using REST + +Querying for historical state is done using the HTTP header `x-cosmos-block-height`. For example, a curl command would look like: + +```bash +curl \ + -X GET \ + -H "Content-Type: application/json" \ + -H "x-cosmos-block-height: 279256" + http://localhost:1317/cosmos/bank/v1beta1/balances/$MY_VALIDATOR +``` + +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 new file mode 100644 index 000000000000..4051bfd5ed8c --- /dev/null +++ b/docs/run-node/keyring.md @@ -0,0 +1,136 @@ + + +# Setting up the keyring + +This document describes how to configure and use the keyring and its various backends for an [**application**](../basics/app-anatomy.md). {synopsis} + +The keyring holds the private/public keypairs used to interact with a node. For instance, a validator key needs to be set up before running the blockchain node, so that blocks can be correctly signed. The private key can be stored in different locations, called "backends", such as a file or the operating system's own key storage. + +## Available backends for the keyring + +Starting with the v0.38.0 release, Cosmos SDK comes with a new keyring implementation +that provides a set of commands to manage cryptographic keys in a secure fashion. The +new keyring supports multiple storage backends, some of which may not be available on +all operating systems. + +### The `os` backend + +The `os` backend relies on operating system-specific defaults to handle key storage +securely. Typically, an operating system's credential sub-system handles password prompts, +private keys storage, and user sessions according to the user's password policies. Here +is a list of the most popular operating systems and their respective passwords manager: + +- macOS (since Mac OS 8.6): [Keychain](https://support.apple.com/en-gb/guide/keychain-access/welcome/mac) +- Windows: [Credentials Management API](https://docs.microsoft.com/en-us/windows/win32/secauthn/credentials-management) +- GNU/Linux: + - [libsecret](https://gitlab.gnome.org/GNOME/libsecret) + - [kwallet](https://api.kde.org/frameworks/kwallet/html/index.html) + +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 latter is a `kwallet` +client. + +`os` is the default option since operating system's default credentials managers are +designed to meet users' most common needs and provide them with a comfortable +experience without compromising on security. + +The recommended backends for headless environments are `file` and `pass`. + +### The `file` backend + +The `file` backend more closely resembles the keybase implementation used prior to +v0.38.1. It stores the keyring encrypted within the app's configuration directory. This +keyring will request a password each time it is accessed, which may occur multiple +times in a single command resulting in repeated password prompts. If using bash scripts +to execute commands using the `file` option you may want to utilize the following format +for multiple prompts: + +```sh +# assuming that KEYPASSWD is set in the environment +$ gaiacli config keyring-backend file # use file backend +$ (echo $KEYPASSWD; echo $KEYPASSWD) | gaiacli keys add me # multiple prompts +$ echo $KEYPASSWD | gaiacli keys show me # single prompt +``` + +::: tip +The first time you add a key to an empty keyring, you will be prompted to type the password twice. +::: + +### The `pass` backend + +The `pass` backend uses the [pass](https://www.passwordstore.org/) utility to manage on-disk +encryption of keys' sensitive data and metadata. Keys are stored inside `gpg` encrypted files +within app-specific directories. `pass` is available for the most popular UNIX +operating systems as well as GNU/Linux distributions. Please refer to its manual page for +information on how to download and install it. + +::: tip +**pass** uses [GnuPG](https://gnupg.org/) for encryption. `gpg` automatically invokes the `gpg-agent` +daemon upon execution, which handles the caching of GnuPG credentials. Please refer to `gpg-agent` +man page for more information on how to configure cache parameters such as credentials TTL and +passphrase expiration. +::: + +The password store must be set up prior to first use: + +```sh +$ pass init +``` + +Replace `` with your GPG key ID. You can use your personal GPG key or an alternative +one you may want to use specifically to encrypt the password store. + +### The `kwallet` backend + +The `kwallet` backend uses `KDE Wallet Manager`, which comes installed by default on the +GNU/Linux distributions that ships KDE as default desktop environment. Please refer to +[KWallet Handbook](https://docs.kde.org/stable5/en/kdeutils/kwallet5/index.html) for more +information. + +### The `test` backend + +The `test` backend is a password-less variation of the `file` backend. Keys are stored +unencrypted on disk. + +**Provided for testing purposes only. The `test` backend is not recommended for use in production environments**. + +### The `memory` backend + +The `memory` backend stores keys in memory. The keys are immediately deleted after the program has exited. + +**Provided for testing purposes only. The `memory` backend is not recommended for use in production environments**. + +## Adding keys to the keyring + +::: warning +Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. +::: + +Applications developed using the Cosmos SDK come with the `keys` subcommand. For the purpose of this tutorial, we're running the `simd` CLI, which is an application built using the Cosmos SDK for testing and educational purposes. For more information, see [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). + +You can use `simd keys` for help about the keys command and `simd keys [command] --help` for more information about a particular subcommand. + +::: tip +You can also enable auto-completion with the `simd completion` command. For example, at the start of a bash session, run `. <(simd completion)`, and all `simd` subcommands will be auto-completed. +::: + +To create a new key in the keyring, run the `add` subcommand with a `` argument. For the purpose of this tutorial, we will solely use the `test` backend, and call our new key `my_validator`. This key will be used in the next section. + +```bash +$ simd keys add my_validator --keyring-backend test + +# Put the generated address in a variable for later use. +MY_VALIDATOR_ADDRESS=$(simd keys show my_validator -a --keyring-backend test) +``` + +This command generates a new 24-word mnemonic phrase, persists it to the relevant backend, and outputs information about the keypair. If this keypair will be used to hold value-bearing tokens, be sure to write down the mnemonic phrase somewhere safe! + +By default, the keyring generates a `secp256k1` keypair. The keyring also supports `ed25519` keys, which may be created by passing the `--algo ed25519` flag. A keyring can of course hold both types of keys simultaneously, and the Cosmos SDK's `x/auth` module (in particular its [AnteHandlers](../core/baseapp.md#antehandler)) supports natively these two public key algorithms. + +## Next {hide} + +Read about [running a node](./run-node.md) {hide} diff --git a/docs/run-node/run-node.md b/docs/run-node/run-node.md new file mode 100644 index 000000000000..d7aaf8f5822d --- /dev/null +++ b/docs/run-node/run-node.md @@ -0,0 +1,96 @@ + + +# Running a Node + +Now that the application is ready and the keyring populated, it's time to see how to run the blockchain node. In this section, the application we are running is called [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp), and its corresponding CLI binary `simd`. {synopsis} + +## Pre-requisite Readings + +- [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq} +- [Setting up the keyring](./keyring.md) {prereq} + +## Initialize the Chain + +::: warning +Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets. +::: + +Before actually running the node, we need to initialize the chain, and most importantly its genesis file. This is done with the `init` subcommand: + +```bash +# The argument is the custom username of your node, it should be human-readable. +simd init --chain-id my-test-chain +``` + +The command above creates all the configuration files needed for your node to run, as well as a default genesis file, which defines the initial state of the network. All these configuration files are in `~/.simapp` by default, but you can overwrite the location of this folder by passing the `--home` flag. + +The `~/.simapp` folder has the following structure: + +```bash +. # ~/.simapp + |- data # Contains the databases used by the node. + |- config/ + |- app.toml # Application-related configuration file. + |- config.toml # Tendermint-related configuration file. + |- genesis.json # The genesis file. + |- node_key.json # Private key to use for node authentication in the p2p protocol. + |- priv_validator_key.json # Private key to use as a validator in the consensus protocol. +``` + +Before starting the chain, you need to populate the state with at least one account. To do so, first [create a new account in the keyring](./keyring.md#adding-keys-to-the-keyring) named `my_validator` under the `test` keyring backend (feel free to choose another name and another backend). + +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 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. + +Now that your account has some tokens, you need to add a validator to your chain. Validators are special full-nodes that participate in the consensus process (implemented in the [underlying consensus engine](../intro/sdk-app-architecture.md#tendermint)) in order to add new blocks to the chain. Any account can declare its intention to become a validator operator, but only those with sufficient delegation get to enter the active set (for example, only the top 125 validator candidates with the most delegation get to be validators in the Cosmos Hub). For this guide, you will add your local node (created via the `init` command above) as a validator of your chain. Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`: + +```bash +# Create a gentx. +simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test + +# Add the gentx to the genesis file. +simd collect-gentxs +``` + +A `gentx` does three things: + +1. Registers the `validator` account you created as a validator operator account (i.e. the account that controls the validator). +2. Self-delegates the provided `amount` of staking tokens. +3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks. If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `simd init` command above. + +For more information on `gentx`, use the following command: + +```bash +simd gentx --help +``` + +## Run a Localnet + +Now that everything is set up, you can finally start your node: + +```bash +simd start +``` + +You should see blocks come in. + +The previous command allow you to run a single node. This is enough for the next section on interacting with this node, but you may wish to run multiple nodes at the same time, and see how consensus happens between them. + +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 new file mode 100644 index 000000000000..040adf77e2bf --- /dev/null +++ b/docs/run-node/txs.md @@ -0,0 +1,357 @@ + + +# Generating, Signing and Broadcasting Transactions + +This document describes how to generate an (unsigned) transaction, signing it (with one or multiple keys), and broadcasting it to the network. {synopsis} + +## Using the CLI + +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 --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. +- 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. + +The CLI bundles all the necessary steps into a simple-to-use user experience. However, it's possible to run all the steps individually too. + +### Generating a Transaction + +Generating a transaction can simply be done by appending the `--generate-only` flag on any `tx` command, e.g.: + +```bash +simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --generate-only +``` + +This will output the unsigned transaction as JSON in the console. We can also save the unsigned transaction to a file (to be passed around between signers more easily) by appending `> unsigned_tx.json` to the above command. + +### Signing a Transaction + +Signing a transaction using the CLI requires the unsigned transaction to be saved in a file. Let's assume the unsigned transaction is in a file called `unsigned_tx.json` in the current directory (see previous paragraph on how to do that). Then, simply run the following command: + +```bash +simd tx sign unsigned_tx.json --chain-id my-test-chain --keyring-backend test --from $MY_VALIDATOR_ADDRESS +``` + +This command will decode the unsigned transaction and sign it with `SIGN_MODE_DIRECT` with `$MY_VALIDATOR_ADDRESS`'s key, which we already set up in the keyring. The signed transaction will be output as JSON to the console, and, as above, we can save it to a file by appending `> signed_tx.json`. + +Some useful flags to consider in the `tx sign` command: + +- `--sign-mode`: you may use `amino-json` to sign the transaction using `SIGN_MODE_LEGACY_AMINO_JSON`, +- `--offline`: sign in offline mode. This means that the `tx sign` command doesn't connect to the node to retrieve the signer's account number and sequence, both needed for signing. In this case, you must manually supply the `--account-number` and `--sequence` flags. This is useful for offline signing, i.e. signing in a secure environment which doesn't have access to the internet. + +#### 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. +::: + +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. + +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. +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. +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. +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 + +Broadcasting a transaction is done using the following command: + +```bash +simd tx broadcast tx_signed.json +``` + +You may optionally pass the `--broadcast-mode` flag to specify which response to receive from the node: + +- `block`: the CLI waits for the tx to be committed in a block. +- `sync`: the CLI waits for a CheckTx execution response only. +- `async`: the CLI returns immediately (transaction might fail). + +## Programmatically with Go + +It is possible to manipulate transactions programmatically via Go using the Cosmos SDK's `TxBuilder` interface. + +### Generating a Transaction + +Before generating a transaction, a new instance of a `TxBuilder` needs to be created. Since the SDK supports both Amino and Protobuf transactions, the first step would be to decide which encoding scheme to use. All the subsequent steps remain unchanged, whether you're using Amino or Protobuf, as `TxBuilder` abstracts the encoding mechanisms. In the following snippet, we will use Protobuf. + +```go +import ( + "github.com/cosmos/cosmos-sdk/simapp" +) + +func sendTx() error { + // Choose your codec: Amino or Protobuf. Here, we use Protobuf, given by the + // following function. + encCfg := simapp.MakeTestEncodingConfig() + + // Create a new TxBuilder. + txBuilder := encCfg.TxConfig.NewTxBuilder() + + // --snip-- +} +``` + +We can also set up some keys and addresses that will send and receive the transactions. Here, for the purpose of the tutorial, we will be using some dummy data to create keys. + +```go +import ( + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +priv1, _, addr1 := testdata.KeyTestPubAddr() +priv2, _, addr2 := testdata.KeyTestPubAddr() +priv3, _, addr3 := testdata.KeyTestPubAddr() +``` + +Populating the `TxBuilder` can be done via its [methods](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/client/tx_config.go#L32-L45): + +```go +import ( + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func sendTx() error { + // --snip-- + + // Define two x/bank MsgSend messages: + // - from addr1 to addr3, + // - from addr2 to addr3. + // This means that the transactions needs two signers: addr1 and addr2. + msg1 := banktypes.NewMsgSend(addr1, addr3, types.NewCoins(types.NewInt64Coin("atom", 12))) + msg2 := banktypes.NewMsgSend(addr2, addr3, types.NewCoins(types.NewInt64Coin("atom", 34))) + + err := txBuilder.SetMsgs(msg1, msg2) + if err != nil { + return err + } + + txBuilder.SetGasLimit(...) + txBuilder.SetFeeAmount(...) + txBuilder.SetMemo(...) + txBuilder.SetTimeoutHeight(...) +} +``` + +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: + +- 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). + +In the current `TxBuilder`'s API, both steps are done using the same method: `SetSignatures()`. The current API requires us to first perform a round of `SetSignatures()` _with empty signatures_, only to populate `SignerInfo`s, and a second round of `SetSignatures()` to actually sign the correct payload. + +```go +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func sendTx() error { + // --snip-- + + privs := []cryptotypes.PrivKey{priv1, priv2} + accNums:= []uint64{..., ...} // The accounts' account numbers + accSeqs:= []uint64{..., ...} // The accounts' sequence numbers + + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: encCfg.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + err := txBuilder.SetSignatures(sigsV2...) + if err != nil { + return err + } + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := tx.SignWithPrivKey( + encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData, + txBuilder, priv, encCfg.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = txBuilder.SetSignatures(sigsV2...) + if err != nil { + return err + } +} +``` + +The `TxBuilder` is now correctly populated. To print it, you can use the `TxConfig` interface from the initial encoding config `encCfg`: + +```go +func sendTx() error { + // --snip-- + + // Generated Protobuf-encoded bytes. + txBytes, err := encCfg.TxConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return err + } + + // Generate a JSON string. + txJSONBytes, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return err + } + txJSON := string(txJSONBytes) +} +``` + +### Broadcasting a Transaction + +The preferred way to broadcast a transaction is to use gRPC, though using REST (via `gRPC-gateway`) or the Tendermint RPC is also posible. An overview of the differences between these methods is exposed [here](../core/grpc_rest.md). For this tutorial, we will only describe the gRPC method. + +```go +import ( + "context" + "fmt" + + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/types/tx" +) + +func sendTx() error { + // --snip-- + + // Create a connection to the gRPC server. + grpcConn := grpc.Dial( + "127.0.0.1:9090", // Or your gRPC server address. + grpc.WithInsecure(), // The SDK doesn't support any transport security mechanism. + ) + defer grpcConn.Close() + + // Broadcast the tx via gRPC. We create a new client for the Protobuf Tx + // service. + txClient := tx.NewServiceClient(grpcConn) + // We then call the BroadcastTx method on this client. + grpcRes, err := txClient.BroadcastTx( + context.Background(), + &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, // Proto-binary of the signed transaction, see previous step. + }, + ) + if err != nil { + return err + } + + fmt.Println(grpcRes.TxResponse.Code) // Should be `0` if the tx is successful + + return nil +} +``` + +#### Simulating a Transaction + +Before broadcasting a transaction, we sometimes may want to dry-run the transaction, to estimate some information about the transaction without actually committing it. This is called simulating a transaction, and can be done as follows: + +```go +import ( + "context" + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/types/tx" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +func simulateTx() error { + // --snip-- + + // Simulate the tx via gRPC. We create a new client for the Protobuf Tx + // service. + txClient := tx.NewServiceClient(grpcConn) + // We then call the BroadcastTx method on this client. + protoTx := txBuilderToProtoTx(txBuilder) + if err != nil { + return err + } + grpcRes, err := txClient.Simulate( + context.Background(), + &tx.SimulateRequest{ + Tx: protoTx, + }, + ) + if err != nil { + return err + } + + fmt.Println(grpcRes.GasInfo) // Prints estimated gas used. + + return nil +} + +// txBuilderToProtoTx converts a txBuilder into a proto tx.Tx. +func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint + protoProvider, ok := txBuilder.(authtx.ProtoTxProvider) + if !ok { + return nil, fmt.Errorf("expected proto tx builder, got %T", txBuilder) + } + + return protoProvider.GetProtoTx(), nil +} +``` + +## Using REST + +It is not possible to generate or sign a transaction using REST, only to broadcast one. + +### Broadcasting a Transaction + +Broadcasting a transaction using the REST endpoint (served by `gRPC-gateway`) can be done by sending a POST request as follows, where the `txBytes` are the protobuf-encoded bytes of a signed transaction: + +```bash +curl -X POST \ + -H "Content-Type: application/json" + -d'{"tx_bytes":"{{txBytes}}","mode":"BROADCAST_MODE_SYNC"}' + localhost:1317/cosmos/tx/v1beta1/txs +``` + +## Using CosmJS (JavaScript & TypeScript) + +CosmJS aims to build client libraries in JavaScript that can be embedded in web applications. Please see [https://cosmos.github.io/cosmjs](https://cosmos.github.io/cosmjs) for more information. As of January 2021, CosmJS documentation is still work in progress. diff --git a/docs/spec/_ics/ics-030-signed-messages.md b/docs/spec/_ics/ics-030-signed-messages.md index 7ade016bf433..fee459a7938c 100644 --- a/docs/spec/_ics/ics-030-signed-messages.md +++ b/docs/spec/_ics/ics-030-signed-messages.md @@ -186,7 +186,7 @@ data = { "text": "I hereby claim I am ABC on Keybase!" } -cosmosSignBytes(data, "fetch1pvsch6cddahhrn5e8ekw0us50dpnugwnlfngt3") +cosmosSignBytes(data, "cosmos1pvsch6cddahhrn5e8ekw0us50dpnugwnlfngt3") > "0x7fc4a495473045022100dec81a9820df0102381cdbf7e8b0f1e2cb64c58e0ecda1324543742e0388e41a02200df37905a6505c1b56a404e23b7473d2c0bc5bcda96771d2dda59df6ed2b98f8" ``` diff --git a/docs/spec/addresses/bech32.md b/docs/spec/addresses/bech32.md index e2f0ef1ba744..7f0babb144b3 100644 --- a/docs/spec/addresses/bech32.md +++ b/docs/spec/addresses/bech32.md @@ -6,14 +6,14 @@ In the Cosmos network, keys and addresses may refer to a number of different rol ## HRP table -| HRP | Definition | -|-------------------|---------------------------------------| -| cosmos | Cosmos Account Address | -| cosmospub | Cosmos Account Public Key | -| cosmosvalcons | Cosmos Validator Consensus Address | -| cosmosvalconspub | Cosmos Validator Consensus Public Key | -| cosmosvaloper | Cosmos Validator Operator Address | -| cosmosvaloperpub | Cosmos Validator Operator Public Key | +| HRP | Definition | +| ---------------- | ------------------------------------- | +| cosmos | Cosmos Account Address | +| cosmospub | Cosmos Account Public Key | +| cosmosvalcons | Cosmos Validator Consensus Address | +| cosmosvalconspub | Cosmos Validator Consensus Public Key | +| cosmosvaloper | Cosmos Validator Operator Address | +| cosmosvaloperpub | Cosmos Validator Operator Public Key | ## Encoding @@ -21,4 +21,4 @@ While all user facing interfaces to Cosmos software should exposed Bech32 interf To covert between other binary representation of addresses and keys, it is important to first apply the Amino encoding process before bech32 encoding. -A complete implementation of the Amino serialization format is unnecessary in most cases. Simply prepending bytes from this [table](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography) to the byte string payload before bech32 encoding will sufficient for compatible representation. +A complete implementation of the Amino serialization format is unnecessary in most cases. Simply prepending bytes from this [table](https://github.com/tendermint/spec/blob/master/spec/blockchain/encoding.md#public-key-cryptography) to the byte string payload before bech32 encoding will sufficient for compatible representation. diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md deleted file mode 100644 index e763c2760caa..000000000000 --- a/docs/spec/ibc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Cosmos Inter-Blockchain Communication (IBC) Protocol - -__Disclaimer__ This module has been removed from the repository for the time being. If you would like to follow the process of IBC, go to [ICS repo](https://github.com/cosmos/ics), there you will find the standard and updates. \ No newline at end of file diff --git a/docs/using-the-sdk/README.md b/docs/using-the-sdk/README.md index 5254bf991ae5..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) - diff --git a/docs/using-the-sdk/quick-start.md b/docs/using-the-sdk/quick-start.md deleted file mode 100644 index 4b2bf548b2e4..000000000000 --- a/docs/using-the-sdk/quick-start.md +++ /dev/null @@ -1,184 +0,0 @@ -# Quick Start - -This guide serves as a practical introduction to building blockchains with the Cosmos SDK. It shows how to scaffold the code for a basic blockchain node, build and run it. Several important concepts of the Cosmos SDK are introduced along the way. - -## Setup - -::: tip -To follow this guide, you need to [install golang](https://golang.org/doc/install) and set [your $GOPATH environment variable](https://golang.org/doc/code.html#GOPATH) -::: - -::: warning -Make sure you are using the latest stable version of golang available on https://golang.org/dl/ -::: - -First, download the [`scaffold`](https://github.com/cosmos/scaffold) tool: - -```bash -git clone https://github.com/cosmos/scaffold -``` - -The `scaffold` tool lets you easily scaffold boilerplate Cosmos SDK applications. Once you have downloaded it, simply install it on your machine: - -```bash -cd scaffold -make -``` - -## Create a Basic Cosmos SDK Blockchain - -To create a basic Cosmos SDK application, simply type in the following command: - -```bash -scaffold app lvl-1 -``` - -where `username|org` is the name of your github/gitlab/atlassian username or organisation, and `repo` the name of the distant repository you would push your application too. These arguments are used to configure the imports so that people can easily download and install your application once (if) you upload it. - -The command above creates a starter application in a new folder named after the `repo` argument. This application contains the [basic logic most SDK applications](../intro/sdk-app-architecture.md) need as well as a set of standard [modules](../building-modules/intro.md) already hooked up. These include: - -- [`auth`](../../x/auth/spec/): Accounts, signatures and fees. -- [`bank`](../../x/bank/spec/): Token transfers. -- [`staking`](../../x/staking/spec/): Proof-of-Stake logic, which is a way of managing validator set changes in public decentralised networks. Also includes delegation logic. -- [`slashing`](../../x/slashing/spec/): Slash validators that misebehave. Complementary to the `staking` module. -- [`distribution`](../../x/distribution/spec/): Distribution of rewards and fees earned by participants in the Proof-of-Stake system (delegators and validators). -- [`params`](../../x/params/spec/): Global parameter store of the application. -- [`supply`](../../x/supply/spec/): Handles global token supply of the application. Enables modules to hold tokens. -- [`genutil`](../../x/genutil) and [`genaccounts`](../../x/genaccounts): Utility modules to facilitate creation of genesis file. - -Now, go into the application's folder. The structure should look like the following: - -``` -├── app/ -│   ├── app.go -│   └── export.go -├── cmd/ -│ ├── acli/ -│ │ └── main.go -│   ├── aud/ -│ │ └── main.go -├── Makefile -├── go.mod -└── x/ -``` - -where: - -- `app.go` is the [main file](../basics/app-anatomy.md#core-application-file) defining the application logic. This is where the state is intantiated and modules are declared. This is also where the Cosmos SDK is imported as a dependency to help build the application. -- `export.go` is a helper file used to export the state of the application into a new genesis file. It is helpful when you want to upgrade your chain to a new (breaking) version. -- `acli/main.go` builds the command-line interface for your blockchain application. It enables end-users to create transactions and query the chain for information. -- `aud/main.go` builds the main [daemon client](../basics/app-anatomy.md#node-client) of the chain. It is used to run a full-node that will connect to peers and sync its local application state with the latest state of the network. -- `go.mod` helps manage dependencies. The two main dependencies used are the Cosmos SDK to help build the application, and Tendermint to replicate it. -- `x/` is the folder to place all the custom modules built specifically for the application. In general, most of the modules used in an application have already been built by third-party developers and only need to be imported in `app.go`. These modules do not need to be cloned into the application's `x/` folder. This is why the basic application shown above, which uses several modules, works despite having an empty `x/` folder. - -## Run your Blockchain - -First, install the two main entrypoints of your blockchain, `aud` and `acli`: - -```bash -go mod tidy -make install -``` - -Make sure the clients are properly installed: - -```bash -acli --help -aud --help -``` - -Now that you have your daemon client `aud` and your command-line interface `acli` installed, go ahead and initialize your chain: - -```bash -aud init --chain-id test -``` - -The command above creates all the configuration files needed for your node to run, as well as a default genesis file, which defines the initial state of the network. Before starting the chain, you need to populate the state with at least one account. To do so, first create a new [account](../basics/accounts.md) named `validator` (feel free to choose another name): - -```bash -acli keys add validator -``` - -Now that you have created a local account, go ahead and grant it `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence: - -```bash -aud add-genesis-account $(acli keys show validator -a) 100000000stake -``` - -Now that your account has some tokens, you need to add a validator to your chain. Validators are special full-nodes that participate in the consensus process (implemented in the [underlying consensus engine](../intro/sdk-app-architecture.md#tendermint)) in order to add new blocks to the chain. Any account can declare its intention to become a validator operator, but only those with sufficient delegation get to enter the active set (for example, only the top 125 validator candidates with the most delegation get to be validators in the Cosmos Hub). For this guide, you will add your local node (created via the `init` command above) as a validator of your chain. Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`: - -```bash -// create a gentx -aud gentx --name validator --amount 100000stake - -// add the gentx to the genesis file -aud collect-gentxs -``` - -A `gentx` does three things: - - 1. Makes the `validator` account you created into a validator operator account (i.e. the account that controls the validator). - 2. Self-delegates the provided `amount` of staking tokens. - 3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks. If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `aud init` command above. - -For more on `gentx`, use the following command: - -```bash -aud gentx --help -``` - -Now that everyting is set up, you can finally start your node: - -```bash -aud start -``` - -You should see blocks come in. - -## Send Tokens and Increase Delegation - -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 -acli query account $(acli keys show validator -a) --chain-id test -``` - -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: - -```bash -acli keys add receiver -``` - -The command above creates a local key-pair that is not yet registered on the chain. An account is registered the first time it receives tokens from another account. Now, run the following command to send tokens to the second account: - -```bash -acli tx send $(acli keys show validator -a) $(acli keys show receiver -a) 1000stake --chain-id test -``` - -Check that the second account did receive the tokens: - -```bash -acli query account $(acli keys show receiver -a) --chain-id test -``` - -Finally, delegate some of the stake tokens sent to the `receiver` account to the validator: - -```bash -acli tx staking delegate $(acli keys show validator --bech val -a) 500stake --from receiver --chain-id test -``` - -Try to query the total delegations to `validator`: - -```bash -acli query staking delegations-to $(acli keys show validator --bech val -a) --chain-id test -``` - -You should see two delegations, the first one made from the `gentx`, and the second one you just performed from the `receiver` account. - -## Next - -Congratulations on making it to the end of this short introduction guide! If you want to learn more, check out the following resources: - -- [How to build a full SDK application from scratch](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html). -- [Read the Cosmos SDK Documentation](../intro/overview.md). - diff --git a/docs/using-the-sdk/simulation.md b/docs/using-the-sdk/simulation.md deleted file mode 100644 index 05147050933e..000000000000 --- a/docs/using-the-sdk/simulation.md +++ /dev/null @@ -1,70 +0,0 @@ -# Cosmos Blockchain Simulator - -The Cosmos SDK offers a full fledged simulation framework to fuzz test every message defined by a module. - -This functionality is provided by the[`SimApp`](https://github.com/cosmos/cosmos-sdk/blob/master/simapp/app.go), -which is a dummy application that is used for running the [`simulation`](https://github.com/cosmos/cosmos-sdk/tree/master/x/simulation) module. -This module defines all the simulation logic as well as the operations for randomized parameters like accounts, balances etc. - -## Goals - -The blockchain simulator tests how the blockchain application would behave under real life circumstances by generating and sending randomized messages. -The goal of this is to detect and debug failures that could halt a live chain, by providing logs and statistics about the operations run by the simulator as well as exporting the latest application state when a failure was found. - -Its main difference with integration testing is that the simulator app allows you to pass parameters to customize the chain that's being simulated. -This comes in handy when trying to reproduce bugs that were generated in the provided operations (randomized or not). - -## Simulation commands - -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`: Analyses the performance of running all the modules' invariants (_i.e_ secuentially 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). - -## 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**. -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. -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 is listed [here](https://github.com/cosmos/cosmos-sdk/blob/adf6ddd4a807c8363e33083a3281f6a5e112ab89/x/simulation/params.go#L170-L178). - -::: tip -These modes are not mutually exclusive. So you can for example run a randomly 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). - -```bash - $ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \ - -run=TestApp \ - ... - -v -timeout 24h -``` - -## Debugging Tips - -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`. - - - diff --git a/docs/versions b/docs/versions index 1f7391f92b6a..d5dc85eb286f 100644 --- a/docs/versions +++ b/docs/versions @@ -1 +1,2 @@ -master +master master +launchpad/backports v0.39 diff --git a/docs/yarn.lock b/docs/yarn.lock deleted file mode 100644 index 977ae12f6248..000000000000 --- a/docs/yarn.lock +++ /dev/null @@ -1,7290 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" - integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== - dependencies: - "@babel/highlight" "^7.0.0" - -"@babel/core@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30" - integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helpers" "^7.5.5" - "@babel/parser" "^7.5.5" - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - convert-source-map "^1.1.0" - debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf" - integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ== - dependencies: - "@babel/types" "^7.5.5" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - trim-right "^1.0.1" - -"@babel/helper-annotate-as-pure@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" - integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== - dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" - integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-call-delegate@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" - integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== - dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/traverse" "^7.4.4" - "@babel/types" "^7.4.4" - -"@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4" - integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-member-expression-to-functions" "^7.5.5" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - "@babel/helper-split-export-declaration" "^7.4.4" - -"@babel/helper-define-map@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz#3dec32c2046f37e09b28c93eb0b103fd2a25d369" - integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/types" "^7.5.5" - lodash "^4.17.13" - -"@babel/helper-explode-assignable-expression@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" - integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== - dependencies: - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" - integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== - dependencies: - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-get-function-arity@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" - integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== - dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-hoist-variables@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a" - integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== - dependencies: - "@babel/types" "^7.4.4" - -"@babel/helper-member-expression-to-functions@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz#1fb5b8ec4453a93c439ee9fe3aeea4a84b76b590" - integrity sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA== - dependencies: - "@babel/types" "^7.5.5" - -"@babel/helper-module-imports@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" - integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A== - dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz#f84ff8a09038dcbca1fd4355661a500937165b4a" - integrity sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/template" "^7.4.4" - "@babel/types" "^7.5.5" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" - integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g== - dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-plugin-utils@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" - integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== - -"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.5.5.tgz#0aa6824f7100a2e0e89c1527c23936c152cab351" - integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== - dependencies: - lodash "^4.17.13" - -"@babel/helper-remap-async-to-generator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" - integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-wrap-function" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-replace-supers@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz#f84ce43df031222d2bad068d2626cb5799c34bc2" - integrity sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.5.5" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - -"@babel/helper-simple-access@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" - integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w== - dependencies: - "@babel/template" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-split-export-declaration@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" - integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== - dependencies: - "@babel/types" "^7.4.4" - -"@babel/helper-wrap-function@^7.1.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" - integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.2.0" - -"@babel/helpers@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e" - integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g== - dependencies: - "@babel/template" "^7.4.4" - "@babel/traverse" "^7.5.5" - "@babel/types" "^7.5.5" - -"@babel/highlight@^7.0.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" - integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.4.4", "@babel/parser@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" - integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== - -"@babel/plugin-proposal-async-generator-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" - integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.1.0" - "@babel/plugin-syntax-async-generators" "^7.2.0" - -"@babel/plugin-proposal-class-properties@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" - integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.5.5" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-proposal-decorators@^7.1.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.4.tgz#de9b2a1a8ab0196f378e2a82f10b6e2a36f21cc0" - integrity sha512-z7MpQz3XC/iQJWXH9y+MaWcLPNSMY9RQSthrLzak8R8hCj0fuyNk+Dzi9kfNe/JxxlWQ2g7wkABbgWjW36MTcw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-decorators" "^7.2.0" - -"@babel/plugin-proposal-json-strings@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" - integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.2.0" - -"@babel/plugin-proposal-object-rest-spread@^7.3.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" - integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" - -"@babel/plugin-proposal-optional-catch-binding@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" - integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" - -"@babel/plugin-proposal-unicode-property-regex@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" - integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/plugin-syntax-async-generators@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" - integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-decorators@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b" - integrity sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-dynamic-import@^7.0.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" - integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-json-strings@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" - integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" - integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-object-rest-spread@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" - integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" - integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-arrow-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" - integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-async-to-generator@^7.3.4": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz#89a3848a0166623b5bc481164b5936ab947e887e" - integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.1.0" - -"@babel/plugin-transform-block-scoped-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" - integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-block-scoping@^7.3.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce" - integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" - -"@babel/plugin-transform-classes@^7.3.4": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9" - integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-define-map" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - "@babel/helper-split-export-declaration" "^7.4.4" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" - integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-destructuring@^7.2.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz#f6c09fdfe3f94516ff074fe877db7bc9ef05855a" - integrity sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-dotall-regex@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" - integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/plugin-transform-duplicate-keys@^7.2.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz#c5dbf5106bf84cdf691222c0974c12b1df931853" - integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-exponentiation-operator@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" - integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-for-of@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" - integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-function-name@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" - integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-literals@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" - integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-modules-amd@^7.2.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz#ef00435d46da0a5961aa728a1d2ecff063e4fb91" - integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-commonjs@^7.2.0": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz#425127e6045231360858eeaa47a71d75eded7a74" - integrity sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ== - dependencies: - "@babel/helper-module-transforms" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-systemjs@^7.3.4": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz#e75266a13ef94202db2a0620977756f51d52d249" - integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg== - dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-umd@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" - integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz#9d269fd28a370258199b4294736813a60bbdd106" - integrity sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg== - dependencies: - regexp-tree "^0.1.6" - -"@babel/plugin-transform-new-target@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" - integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-object-super@^7.2.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz#c70021df834073c65eb613b8679cc4a381d1a9f9" - integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - -"@babel/plugin-transform-parameters@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" - integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== - dependencies: - "@babel/helper-call-delegate" "^7.4.4" - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-regenerator@^7.3.4": - version "7.4.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz#629dc82512c55cee01341fb27bdfcb210354680f" - integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA== - dependencies: - regenerator-transform "^0.14.0" - -"@babel/plugin-transform-runtime@^7.4.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.5.5.tgz#a6331afbfc59189d2135b2e09474457a8e3d28bc" - integrity sha512-6Xmeidsun5rkwnGfMOp6/z9nSzWpHFNVr2Jx7kwoq4mVatQfQx5S56drBgEHF+XQbKOdIaOiMIINvp/kAwMN+w== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - resolve "^1.8.1" - semver "^5.5.1" - -"@babel/plugin-transform-shorthand-properties@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" - integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-spread@^7.2.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" - integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-sticky-regex@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" - integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" - -"@babel/plugin-transform-template-literals@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" - integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-typeof-symbol@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" - integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-unicode-regex@^7.2.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" - integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/preset-env@^7.0.0 < 7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" - integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.2.0" - "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.3.4" - "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" - "@babel/plugin-syntax-async-generators" "^7.2.0" - "@babel/plugin-syntax-json-strings" "^7.2.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" - "@babel/plugin-transform-arrow-functions" "^7.2.0" - "@babel/plugin-transform-async-to-generator" "^7.3.4" - "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.3.4" - "@babel/plugin-transform-classes" "^7.3.4" - "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.2.0" - "@babel/plugin-transform-dotall-regex" "^7.2.0" - "@babel/plugin-transform-duplicate-keys" "^7.2.0" - "@babel/plugin-transform-exponentiation-operator" "^7.2.0" - "@babel/plugin-transform-for-of" "^7.2.0" - "@babel/plugin-transform-function-name" "^7.2.0" - "@babel/plugin-transform-literals" "^7.2.0" - "@babel/plugin-transform-modules-amd" "^7.2.0" - "@babel/plugin-transform-modules-commonjs" "^7.2.0" - "@babel/plugin-transform-modules-systemjs" "^7.3.4" - "@babel/plugin-transform-modules-umd" "^7.2.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0" - "@babel/plugin-transform-new-target" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.2.0" - "@babel/plugin-transform-parameters" "^7.2.0" - "@babel/plugin-transform-regenerator" "^7.3.4" - "@babel/plugin-transform-shorthand-properties" "^7.2.0" - "@babel/plugin-transform-spread" "^7.2.0" - "@babel/plugin-transform-sticky-regex" "^7.2.0" - "@babel/plugin-transform-template-literals" "^7.2.0" - "@babel/plugin-transform-typeof-symbol" "^7.2.0" - "@babel/plugin-transform-unicode-regex" "^7.2.0" - browserslist "^4.3.4" - invariant "^2.2.2" - js-levenshtein "^1.1.3" - semver "^5.3.0" - -"@babel/runtime-corejs2@^7.2.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2" - integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.2" - -"@babel/runtime@^7.0.0": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" - integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== - dependencies: - regenerator-runtime "^0.13.2" - -"@babel/template@^7.1.0", "@babel/template@^7.4.4": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" - integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.4.4" - "@babel/types" "^7.4.4" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb" - integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.5.5" - "@babel/types" "^7.5.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a" - integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw== - dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" - integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== - dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" - -"@nodelib/fs.stat@^1.1.2": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" - integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== - -"@types/events@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== - -"@types/glob@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== - dependencies: - "@types/events" "*" - "@types/minimatch" "*" - "@types/node" "*" - -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/node@*": - version "12.6.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c" - integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg== - -"@types/q@^1.5.1": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" - integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== - -"@vue/babel-helper-vue-jsx-merge-props@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040" - integrity sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw== - -"@vue/babel-plugin-transform-vue-jsx@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.0.0.tgz#ebcbf39c312c94114c8c4f407ee4f6c97aa45432" - integrity sha512-U+JNwVQSmaLKjO3lzCUC3cNXxprgezV1N+jOdqbP4xWNaqtWUCJnkjTVcgECM18A/AinDKPcUUeoyhU7yxUxXQ== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" - html-tags "^2.0.0" - lodash.kebabcase "^4.1.1" - svg-tags "^1.0.0" - -"@vue/babel-preset-app@^3.1.1": - version "3.9.2" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-3.9.2.tgz#b72a9b06abbe3f8f272783be13951271277be338" - integrity sha512-0suuCbu4jkVcVYBjPmuKxeDbrhwThYZHu3DUmtsVuOzFEGeXmco60VmXveniL/bnDUdZyknSuYP4FxgS34gw9w== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-decorators" "^7.1.0" - "@babel/plugin-syntax-dynamic-import" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.0.0" - "@babel/plugin-transform-runtime" "^7.4.0" - "@babel/preset-env" "^7.0.0 < 7.4.0" - "@babel/runtime" "^7.0.0" - "@babel/runtime-corejs2" "^7.2.0" - "@vue/babel-preset-jsx" "^1.0.0" - babel-plugin-dynamic-import-node "^2.2.0" - babel-plugin-module-resolver "3.2.0" - core-js "^2.6.5" - -"@vue/babel-preset-jsx@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.0.0.tgz#e515cd453a5a8ea6b0f30b2bb92f266d8ab4e9f5" - integrity sha512-5CbDu/QHS+TtQNw5aYAffiMxBBB2Eo9+RJpS8X+6FJbdG5Rvc4TVipEqkrg0pJviWadNg7TEy0Uz4o7VNXeIZw== - dependencies: - "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" - "@vue/babel-sugar-functional-vue" "^1.0.0" - "@vue/babel-sugar-inject-h" "^1.0.0" - "@vue/babel-sugar-v-model" "^1.0.0" - "@vue/babel-sugar-v-on" "^1.0.0" - -"@vue/babel-sugar-functional-vue@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.0.0.tgz#17e2c4ca27b74b244da3b923240ec91d10048cb3" - integrity sha512-XE/jNaaorTuhWayCz+QClk5AB9OV5HzrwbzEC6sIUY0J60A28ONQKeTwxfidW42egOkqNH/UU6eE3KLfmiDj0Q== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-inject-h@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.0.0.tgz#e5efb6c5b5b7988dc03831af6d133bf7bcde6347" - integrity sha512-NxWU+DqtbZgfGvd25GPoFMj+rvyQ8ZA1pHj8vIeqRij+vx3sXoKkObjA9ulZunvWw5F6uG9xYy4ytpxab/X+Hg== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-v-model@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.0.0.tgz#f4da56aa67f65a349bd2c269a95e72e601af4613" - integrity sha512-Pfg2Al0io66P1eO6zUbRIgpyKCU2qTnumiE0lao/wA/uNdb7Dx5Tfd1W6tO5SsByETPnEs8i8+gawRIXX40rFw== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" - camelcase "^5.0.0" - html-tags "^2.0.0" - svg-tags "^1.0.0" - -"@vue/babel-sugar-v-on@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.0.0.tgz#a633ee8fe205763e865b011246981b7f89668033" - integrity sha512-2aqJaDLKdSSGlxZU+GjFERaSNUaa6DQreV+V/K4W/6Lxj8520/r1lChWEa/zuAoPD2Vhy0D2QrqqO+I0D6CkKw== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.0.0" - camelcase "^5.0.0" - -"@vue/component-compiler-utils@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.0.0.tgz#d16fa26b836c06df5baaeb45f3d80afc47e35634" - integrity sha512-am+04/0UX7ektcmvhYmrf84BDVAD8afFOf4asZjN84q8xzxFclbk5x0MtxuKGfp+zjN5WWPJn3fjFAWtDdIGSw== - dependencies: - consolidate "^0.15.1" - hash-sum "^1.0.2" - lru-cache "^4.1.2" - merge-source-map "^1.1.0" - postcss "^7.0.14" - postcss-selector-parser "^5.0.0" - prettier "1.16.3" - source-map "~0.6.1" - vue-template-es2015-compiler "^1.9.0" - -"@vuepress/core@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-1.0.2.tgz#75d0c6ccb4be92c6674c3bbfbe25639c8239921b" - integrity sha512-PUMaxq44wEuqXHutcmxj6q9cCRS4kZ1nyBvvHr9AIuxJflgYDw/k8wxhYuZjsxVWhpJjsPywLGNRyLN88vJcqQ== - dependencies: - "@babel/core" "^7.0.0" - "@vue/babel-preset-app" "^3.1.1" - "@vuepress/markdown" "^1.0.2" - "@vuepress/markdown-loader" "^1.0.2" - "@vuepress/plugin-last-updated" "^1.0.2" - "@vuepress/plugin-register-components" "^1.0.2" - "@vuepress/shared-utils" "^1.0.2" - autoprefixer "^9.5.1" - babel-loader "^8.0.4" - cache-loader "^3.0.0" - chokidar "^2.0.3" - connect-history-api-fallback "^1.5.0" - copy-webpack-plugin "^5.0.2" - cross-spawn "^6.0.5" - css-loader "^2.1.1" - file-loader "^3.0.1" - js-yaml "^3.11.0" - lru-cache "^5.1.1" - mini-css-extract-plugin "0.6.0" - optimize-css-assets-webpack-plugin "^5.0.1" - portfinder "^1.0.13" - postcss-loader "^3.0.0" - postcss-safe-parser "^4.0.1" - toml "^3.0.0" - url-loader "^1.0.1" - vue "^2.5.16" - vue-loader "^15.2.4" - vue-router "^3.0.2" - vue-server-renderer "^2.5.16" - vue-template-compiler "^2.5.16" - vuepress-html-webpack-plugin "^3.2.0" - vuepress-plugin-container "^2.0.0" - webpack "^4.8.1" - webpack-chain "^4.6.0" - webpack-dev-server "^3.5.1" - webpack-merge "^4.1.2" - webpackbar "3.2.0" - -"@vuepress/markdown-loader@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/markdown-loader/-/markdown-loader-1.0.2.tgz#b068df3049f6b63cfee329f85aed3bb0aa9e7ab0" - integrity sha512-ljD2mVDpeq0VvCHMHfemGW+0fhLmOMldtWIAYQ/I8LjLuV2qknAwjzZ4tEAqveaVIFMUBRP3V6d8YGIK9dr6kg== - dependencies: - "@vuepress/markdown" "^1.0.2" - loader-utils "^1.1.0" - lru-cache "^5.1.1" - -"@vuepress/markdown@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-1.0.2.tgz#436c5aa74e22cf7f6705b99c8892b6ba2d84cd0a" - integrity sha512-ddl0FG11aeidjcFYYNU53xZ1WLEYb3g5/hijfSCEQKyMv+gDXy7Z3/uc4I4oH2UNtB2Wpo3pQoeKGY56edcBuA== - dependencies: - "@vuepress/shared-utils" "^1.0.2" - markdown-it "^8.4.1" - markdown-it-anchor "^5.0.2" - markdown-it-chain "^1.3.0" - markdown-it-emoji "^1.4.0" - markdown-it-table-of-contents "^0.4.0" - prismjs "^1.13.0" - -"@vuepress/plugin-active-header-links@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.0.2.tgz#df04f7fc21640d3e0a0b704037e360e75974d1a2" - integrity sha512-Wo9NP55OOJ/vGFnYwStZcDBJMnf1gNDL159K7oiiEltuz8kjZBqmtsIjQXOzXFjA8voYh/RVL48Sr4eGCDd7LQ== - dependencies: - lodash.throttle "^4.1.1" - -"@vuepress/plugin-last-updated@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-last-updated/-/plugin-last-updated-1.0.2.tgz#c839c5fb585c469a8c2ff70c16204dd72478545a" - integrity sha512-SwugVHcllUwy9WPqtWWM+hUEvH6SDPFJAHnpIs0kXJmaxIIipqF/9+CokT5QzxzGVHeYPU4YKtLadEIXdRcXsw== - dependencies: - cross-spawn "^6.0.5" - -"@vuepress/plugin-nprogress@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-1.0.2.tgz#3fae13c8af23292cf324d159c77e4d0ffc3133ab" - integrity sha512-degCJe2Z0eHbYblUGQXuDMIZSwH7twQcbWtkuH8goxXNilvtVxtWvBkUJouJ9RN3RuSk7EfPT171mrwq9yqbUg== - dependencies: - nprogress "^0.2.0" - -"@vuepress/plugin-register-components@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-register-components/-/plugin-register-components-1.0.2.tgz#504c190b1c1836e3428d90749a2dbd59f6e596b9" - integrity sha512-iqUq4kgNdVHba0cZJLv81DfB9ZsTdJY7gynN0NYHFwDEjsdOh1cRMgteuWa/mmK9XfopMO5oysD9WDhzCiIjfQ== - dependencies: - "@vuepress/shared-utils" "^1.0.2" - -"@vuepress/plugin-search@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-1.0.2.tgz#6d43fb46b207d48b797a5bc5b01824662db4684d" - integrity sha512-LCFZLp+adppdHETIEARwQhczj+mdpa+D25qL9RNmYxzU9mF6qItYNLl57P6omGU2Vr8frAc+rWgjbi4cjkbCvQ== - -"@vuepress/shared-utils@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/shared-utils/-/shared-utils-1.0.2.tgz#4e1342748b7594fe4fde9dce3bf201538fa5ca67" - integrity sha512-QyNV76Dn0u2ooXbC3AXJZrQLuTNS4i8xSmJqZWsel2ooJKknXP3UIMIENcK1QFHnlIACznyV53u9hRAYBaZEWQ== - dependencies: - chalk "^2.3.2" - diacritics "^1.3.0" - escape-html "^1.0.3" - fs-extra "^7.0.1" - globby "^9.2.0" - gray-matter "^4.0.1" - hash-sum "^1.0.2" - semver "^6.0.0" - upath "^1.1.0" - -"@vuepress/theme-default@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-1.0.2.tgz#7678c4755db9d891effee838991287d31ae9c5ed" - integrity sha512-fawiYshvQWXyaEgMXcyqj7j0atHLysIA2AzFt4K6y29WaMfiIAPE9lsxymTzT4zkc/T6nRP/TqwiuUaOK12wkw== - dependencies: - "@vuepress/plugin-active-header-links" "^1.0.2" - "@vuepress/plugin-nprogress" "^1.0.2" - "@vuepress/plugin-search" "^1.0.2" - docsearch.js "^2.5.2" - stylus "^0.54.5" - stylus-loader "^3.0.2" - vuepress-plugin-container "^2.0.0" - -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== - dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== - -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== - -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== - -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== - dependencies: - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== - -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== - dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" - -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== - -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" - integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" - integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== - -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" - integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.0.tgz#67f0da2fc339d6cfb5d6fb244fd449f33cd8bbe3" - integrity sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw== - -agentkeepalive@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" - integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" - integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== - -ajv@^6.1.0, ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -algoliasearch@^3.24.5: - version "3.33.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.33.0.tgz#83b541124ebb0db54643009d4e660866b3177cdf" - integrity sha512-9DaVmOd7cvcZeYyV0BWAeJHVWJmgOL2DNUEBY/DTR4MzD1wCWs4Djl7LAlfvkGwGBdRHZCG+l0HA1572w3T8zg== - dependencies: - agentkeepalive "^2.2.0" - debug "^2.6.9" - envify "^4.0.0" - es6-promise "^4.1.0" - events "^1.1.0" - foreach "^2.0.5" - global "^4.3.2" - inherits "^2.0.1" - isarray "^2.0.1" - load-script "^1.0.0" - object-keys "^1.0.11" - querystring-es3 "^0.2.1" - reduce "^1.0.1" - semver "^5.1.0" - tunnel-agent "^0.6.0" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-colors@^3.0.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - -ansi-escapes@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.0.tgz#c38600259cefba178ee3f7166c5ea3a5dd2e88fc" - integrity sha512-0+VX4uhi8m3aNbzoqKmkAVOEj6uQzcUHXoFPkKjhZPTpGRUBqVh930KbB6PS4zIyDZccphlLIYlu8nsjFzkXwg== - dependencies: - type-fest "^0.5.2" - -ansi-html@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -aproba@^1.0.3, aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-union@^1.0.1, array-union@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -autocomplete.js@0.36.0: - version "0.36.0" - resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.36.0.tgz#94fe775fe64b6cd42e622d076dc7fd26bedd837b" - integrity sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q== - dependencies: - immediate "^3.2.3" - -autoprefixer@^9.5.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" - integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== - dependencies: - browserslist "^4.6.3" - caniuse-lite "^1.0.30000980" - chalk "^2.4.2" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.17" - postcss-value-parser "^4.0.0" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -axios@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" - integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== - dependencies: - follow-redirects "1.5.10" - is-buffer "^2.0.2" - -babel-loader@^8.0.4: - version "8.0.6" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" - integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== - dependencies: - find-cache-dir "^2.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" - pify "^4.0.1" - -babel-plugin-dynamic-import-node@^2.2.0, babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-module-resolver@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz#ddfa5e301e3b9aa12d852a9979f18b37881ff5a7" - integrity sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA== - dependencies: - find-babel-config "^1.1.0" - glob "^7.1.2" - pkg-up "^2.0.0" - reselect "^3.0.1" - resolve "^1.4.0" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -bluebird@^3.1.1, bluebird@^3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" - integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== - -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" - integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= - dependencies: - bn.js "^4.1.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" - integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.6.3: - version "4.6.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.6.6.tgz#6e4bf467cde520bc9dbdf3747dafa03531cec453" - integrity sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA== - dependencies: - caniuse-lite "^1.0.30000984" - electron-to-chromium "^1.3.191" - node-releases "^1.1.25" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - -buffer-json@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/buffer-json/-/buffer-json-2.0.0.tgz#f73e13b1e42f196fe2fd67d001c7d7107edd7c23" - integrity sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" - integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cac@^6.3.9: - version "6.5.2" - resolved "https://registry.yarnpkg.com/cac/-/cac-6.5.2.tgz#92ef1490b9ffde5f0be7eeadec5ea926f0e78ef6" - integrity sha512-8JdiD9/ZLsG418j/chyZQ3VWuhFELSGlH4EUxzNKgIH8wK8dO0j5Pqu6Pk7B/RP3kX9aasyQhPrrUjYO5e0w7w== - -cacache@^11.3.2: - version "11.3.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" - integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cache-loader@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-3.0.1.tgz#cee6cf4b3cdc7c610905b26bad6c2fc439c821af" - integrity sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw== - dependencies: - buffer-json "^2.0.0" - find-cache-dir "^2.1.0" - loader-utils "^1.2.3" - mkdirp "^0.5.1" - neo-async "^2.6.1" - schema-utils "^1.0.0" - -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase@^5.0.0, camelcase@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984: - version "1.0.30000984" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz#dc96c3c469e9bcfc6ad5bdd24c77ec918ea76fe0" - integrity sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" - integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chownr@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== - -chrome-trace-event@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - -ci-info@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-css@4.2.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" - integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== - dependencies: - source-map "~0.6.0" - -clipboard@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" - integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ== - dependencies: - good-listener "^1.2.2" - select "^1.1.2" - tiny-emitter "^2.0.0" - -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - -commander@^2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compressible@~2.0.16: - version "2.0.17" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" - integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== - dependencies: - mime-db ">= 1.40.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -connect-history-api-fallback@^1.5.0, connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== - -consola@^2.6.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.9.0.tgz#57760e3a65a53ec27337f4add31505802d902278" - integrity sha512-34Iue+LRcWbndFIfZc5boNizWlsrRjqIBJZTe591vImgbnq7nx2EzlrLtANj9TH2Fxm7puFJBJAOk5BhvZOddQ== - -console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= - dependencies: - date-now "^0.1.4" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -consolidate@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== - dependencies: - bluebird "^3.1.1" - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@^1.1.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" - integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copy-webpack-plugin@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.3.tgz#2179e3c8fd69f13afe74da338896f1f01a875b5c" - integrity sha512-PlZRs9CUMnAVylZq+vg2Juew662jWtwOXOqH4lbQD9ZFhRG9R7tVStOgHt21CBGVq7k5yIJaz8TXDLSjV+Lj8Q== - dependencies: - cacache "^11.3.2" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" - normalize-path "^3.0.0" - p-limit "^2.2.0" - schema-utils "^1.0.0" - serialize-javascript "^1.7.0" - webpack-log "^2.0.0" - -core-js@^2.6.5: - version "2.6.9" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" - integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" - integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== - dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" - -create-hash@^1.1.0, create-hash@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-loader@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" - integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== - dependencies: - camelcase "^5.2.0" - icss-utils "^4.1.0" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.14" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^2.0.6" - postcss-modules-scope "^2.1.0" - postcss-modules-values "^2.0.0" - postcss-value-parser "^3.3.0" - schema-utils "^1.0.0" - -css-parse@1.7.x: - version "1.7.0" - resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" - integrity sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs= - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-select@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" - integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== - dependencies: - boolbase "^1.0.0" - css-what "^2.1.2" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-tree@1.0.0-alpha.29: - version "1.0.0-alpha.29" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" - integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== - dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" - -css-tree@1.0.0-alpha.33: - version "1.0.0-alpha.33" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.33.tgz#970e20e5a91f7a378ddd0fc58d0b6c8d4f3be93e" - integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w== - dependencies: - mdn-data "2.0.4" - source-map "^0.5.3" - -css-unit-converter@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" - integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= - -css-what@2.1, css-what@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" - integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== - dependencies: - css-tree "1.0.0-alpha.29" - -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= - -de-indent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" - integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= - -debug@*, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@=3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^3.2.5, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -deep-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deepmerge@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -delegate@^3.1.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" - integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - -diacritics@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/diacritics/-/diacritics-1.3.0.tgz#3efa87323ebb863e6696cebb0082d48ff3d6f7a1" - integrity sha1-PvqHMj67hj5mls67AILUj/PW96E= - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^2.0.0, dir-glob@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= - dependencies: - buffer-indexof "^1.0.0" - -docsearch.js@^2.5.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/docsearch.js/-/docsearch.js-2.6.3.tgz#57cb4600d3b6553c677e7cbbe6a734593e38625d" - integrity sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A== - dependencies: - algoliasearch "^3.24.5" - autocomplete.js "0.36.0" - hogan.js "^3.0.2" - request "^2.87.0" - stack-utils "^1.0.1" - to-factory "^1.0.0" - zepto "^1.2.0" - -dom-converter@^0.2: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== - dependencies: - utila "~0.4" - -dom-serializer@0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1, domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" - integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== - dependencies: - is-obj "^1.0.0" - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-to-chromium@^1.3.191: - version "1.3.194" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.194.tgz#a96452a96d4539131957aade9f634a45721f2819" - integrity sha512-w0LHR2YD9Ex1o+Sz4IN2hYzCB8vaFtMNW+yJcBf6SZlVqgFahkne/4rGVJdk4fPF98Gch9snY7PiabOh+vqHNg== - -elliptic@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.0.tgz#2b8ed4c891b7de3200e14412a5b8248c7af505ca" - integrity sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg== - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" - integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - tapable "^1.0.0" - -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -envify@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" - integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== - dependencies: - esprima "^4.0.0" - through "~2.3.4" - -envinfo@^7.2.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.3.1.tgz#892e42f7bf858b3446d9414ad240dbaf8da52f09" - integrity sha512-GvXiDTqLYrORVSCuJCsWHPXF5BFvoWMQA9xX4YVjPT1jyS3aZEHUBwjzxU/6LTPF9ReHgVEbX7IEN5UvSXHw/A== - -errno@^0.1.3, errno@~0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== - dependencies: - prr "~1.0.1" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.12.0, es-abstract@^1.5.1: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es6-promise@^4.1.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -escape-html@^1.0.3, escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-scope@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.1.0, estraverse@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -eventemitter3@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - -events@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= - -events@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== - -eventsource@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" - integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== - dependencies: - original "^1.0.0" - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-glob@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" - integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.1.2" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.3" - micromatch "^3.1.10" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: - version "0.11.3" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" - integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== - dependencies: - websocket-driver ">=0.5.1" - -figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== - -figures@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9" - integrity sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g== - dependencies: - escape-string-regexp "^1.0.5" - -file-loader@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" - integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== - dependencies: - loader-utils "^1.0.2" - schema-utils "^1.0.0" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-babel-config@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.0.tgz#a9b7b317eb5b9860cda9d54740a8c8337a2283a2" - integrity sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA== - dependencies: - json5 "^0.5.1" - path-exists "^3.0.0" - -find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" - integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== - dependencies: - debug "=3.1.0" - -follow-redirects@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" - integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== - dependencies: - debug "^3.2.6" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== - dependencies: - minipass "^2.2.1" - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.9" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" - integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== - dependencies: - nan "^2.12.1" - node-pre-gyp "^0.12.0" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= - -glob@7.0.x: - version "7.0.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" - integrity sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo= - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global@^4.3.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - -globby@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" - integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^1.0.2" - dir-glob "^2.2.2" - fast-glob "^2.2.6" - glob "^7.1.3" - ignore "^4.0.3" - pify "^4.0.1" - slash "^2.0.0" - -good-listener@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" - integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= - dependencies: - delegate "^3.1.2" - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.2.0" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" - integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== - -gray-matter@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.2.tgz#9aa379e3acaf421193fce7d2a28cebd4518ac454" - integrity sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw== - dependencies: - js-yaml "^3.11.0" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" - -handle-thing@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" - integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.0, has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -hash-sum@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" - integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ= - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.x, he@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hmac-drbg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hogan.js@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" - integrity sha1-TNnhq9QpQUbnZ55B14mHMrAse/0= - dependencies: - mkdirp "0.3.0" - nopt "1.0.10" - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -html-entities@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" - integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= - -html-minifier@^3.2.3: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -html-tags@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" - integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= - -htmlparser2@^3.3.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - 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" - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -"http-parser-js@>=0.4.0 <0.4.11": - version "0.4.10" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" - integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= - -http-proxy-middleware@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - -http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== - dependencies: - eventemitter3 "^3.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -iconv-lite@0.4.24, iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= - -icss-utils@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - -ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== - dependencies: - minimatch "^3.0.4" - -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.3: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -immediate@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" - integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= - -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= - dependencies: - resolve-from "^3.0.0" - -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -ip@^1.1.0, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - -ipaddr.js@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" - integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== - -ipaddr.js@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-buffer@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== - -is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-path-cwd@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= - dependencies: - has "^1.0.1" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isarray@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -javascript-stringify@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" - integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= - -js-levenshtein@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.11.0, js-yaml@^3.13.1, js-yaml@^3.8.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json3@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" - integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== - dependencies: - minimist "^1.2.0" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== - -last-call-webpack-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" - integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== - dependencies: - lodash "^4.17.5" - webpack-sources "^1.1.0" - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - -linkify-it@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" - integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== - dependencies: - uc.micro "^1.0.1" - -load-script@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" - integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= - -loader-runner@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.kebabcase@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" - integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.template@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.throttle@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" - integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.5: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -lodash@^4.17.13: - version "4.17.13" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93" - integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA== - -loglevel@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280" - integrity sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA== - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - -lru-cache@^4.1.2: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -markdown-it-anchor@^5.0.2: - version "5.2.4" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz#d39306fe4c199705b4479d3036842cf34dcba24f" - integrity sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A== - -markdown-it-chain@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz#ccf6fe86c10266bafb4e547380dfd7f277cc17bc" - integrity sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ== - dependencies: - webpack-chain "^4.9.0" - -markdown-it-container@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695" - integrity sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU= - -markdown-it-emoji@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz#9bee0e9a990a963ba96df6980c4fddb05dfb4dcc" - integrity sha1-m+4OmpkKljupbfaYDE/dsF37Tcw= - -markdown-it-meta@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/markdown-it-meta/-/markdown-it-meta-0.0.1.tgz#d75b68f115659caf568e4ad43cb460a471248c39" - integrity sha1-11to8RVlnK9WjkrUPLRgpHEkjDk= - dependencies: - js-yaml "^3.8.1" - -markdown-it-table-of-contents@^0.4.0: - version "0.4.4" - resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc" - integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw== - -markdown-it@^8.4.1, markdown-it@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" - integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== - dependencies: - argparse "^1.0.7" - entities "~1.1.1" - linkify-it "^2.0.0" - mdurl "^1.0.1" - uc.micro "^1.0.5" - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdn-data@~1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" - integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== - -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - -merge2@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" - integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.0.3, mime@^2.4.2: - version "2.4.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== - -mimic-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= - dependencies: - dom-walk "^0.1.0" - -mini-css-extract-plugin@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz#a3f13372d6fcde912f3ee4cd039665704801e3b9" - integrity sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw== - dependencies: - loader-utils "^1.1.0" - normalize-url "^2.0.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minipass@^2.2.1, minipass@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" - integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== - dependencies: - minipass "^2.2.1" - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= - -mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - -nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-forge@0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" - integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== - -node-libs-browser@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-pre-gyp@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" - integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -node-releases@^1.1.25: - version "1.1.25" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.25.tgz#0c2d7dbc7fed30fbe02a9ee3007b8c90bf0133d3" - integrity sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ== - dependencies: - semver "^5.3.0" - -nopt@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= - dependencies: - abbrev "1" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== - -npm-packlist@^1.1.6: - version "1.4.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -nprogress@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" - integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= - -nth-check@^1.0.2, nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -optimize-css-assets-webpack-plugin@^5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" - integrity sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA== - dependencies: - cssnano "^4.1.10" - last-call-webpack-plugin "^3.0.0" - -original@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" - integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== - dependencies: - url-parse "^1.4.3" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" - integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== - dependencies: - p-try "^2.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@~1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== - -parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= - dependencies: - cyclist "~0.2.2" - inherits "^2.0.3" - readable-stream "^2.1.5" - -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - -parse-asn1@^5.0.0: - version "5.1.4" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" - integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - -pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" - integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - -portfinder@^1.0.13, portfinder@^1.0.20: - version "1.0.21" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.21.tgz#60e1397b95ac170749db70034ece306b9a27e324" - integrity sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA== - dependencies: - async "^1.5.2" - debug "^2.2.0" - mkdirp "0.5.x" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-calc@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" - integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== - dependencies: - css-unit-converter "^1.1.1" - postcss "^7.0.5" - postcss-selector-parser "^5.0.0-rc.4" - postcss-value-parser "^3.3.1" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-load-config@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" - integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-loader@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== - dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - -postcss-modules-local-by-default@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" - integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-value-parser "^3.3.1" - -postcss-modules-scope@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb" - integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" - integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== - dependencies: - icss-replace-symbols "^1.1.0" - postcss "^7.0.6" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-safe-parser@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea" - integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ== - dependencies: - postcss "^7.0.0" - -postcss-selector-parser@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" - integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= - dependencies: - dot-prop "^4.1.1" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d" - integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ== - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.17" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" - integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -prettier@1.16.3: - version "1.16.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d" - integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw== - -pretty-error@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" - integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= - dependencies: - renderkid "^2.0.1" - utila "~0.4" - -pretty-time@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" - integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== - -prismjs@^1.13.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.16.0.tgz#406eb2c8aacb0f5f0f1167930cb83835d10a4308" - integrity sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA== - optionalDependencies: - clipboard "^2.0.0" - -private@^0.1.6: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -proxy-addr@~2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" - integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.0" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -psl@^1.1.24: - version "1.2.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6" - integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -querystring-es3@^0.2.0, querystring-es3@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - 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" - -readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -reduce@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" - integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== - dependencies: - object-keys "^1.1.0" - -regenerate-unicode-properties@^8.0.2: - version "8.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== - -regenerator-runtime@^0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" - integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== - -regenerator-transform@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.0.tgz#2ca9aaf7a2c239dd32e4761218425b8c7a86ecaf" - integrity sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w== - dependencies: - private "^0.1.6" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp-tree@^0.1.6: - version "0.1.11" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.11.tgz#c9c7f00fcf722e0a56c7390983a7a63dd6c272f3" - integrity sha512-7/l/DgapVVDzZobwMCCgMlqiqyLFJ0cduo/j+3BcDJIB+yJdsYCfKuI3l/04NV+H/rfNRdPIDbXNZHM9XvQatg== - -regexpu-core@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" - integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.0.2" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" - -regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== - -regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== - dependencies: - jsesc "~0.5.0" - -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -renderkid@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" - integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== - dependencies: - css-select "^1.1.0" - dom-converter "^0.2" - htmlparser2 "^3.3.0" - strip-ansi "^3.0.0" - utila "^0.4.0" - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -request@^2.87.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -reselect@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" - integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.2.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" - integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== - dependencies: - path-parse "^1.0.6" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@0.5.x: - version "0.5.8" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" - integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= - -sax@^1.2.4, sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== - dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -select@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" - integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= - -selfsigned@^1.10.4: - version "1.10.4" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" - integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== - dependencies: - node-forge "0.7.5" - -semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" - integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== - -semver@^6.0.0, semver@^6.1.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" - integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serialize-javascript@^1.3.0, serialize-javascript@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" - integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -sockjs-client@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" - integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg== - dependencies: - debug "^3.2.5" - eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" - -sockjs@0.3.19: - version "0.3.19" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" - integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== - dependencies: - faye-websocket "^0.10.0" - uuid "^3.0.1" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== - dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@~0.5.12: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@0.1.x: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= - dependencies: - amdefine ">=0.0.4" - -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= - -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52" - integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -std-env@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" - integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== - dependencies: - ci-info "^1.6.0" - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" - integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== - dependencies: - safe-buffer "~5.1.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -stylus-loader@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" - integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== - dependencies: - loader-utils "^1.0.2" - lodash.clonedeep "^4.5.0" - when "~3.6.x" - -stylus@^0.54.5: - version "0.54.5" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" - integrity sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk= - dependencies: - css-parse "1.7.x" - debug "*" - glob "7.0.x" - mkdirp "0.5.x" - sax "0.5.x" - source-map "0.1.x" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -svg-tags@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" - integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= - -svgo@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.0.tgz#bae51ba95ded9a33a36b7c46ce9c359ae9154313" - integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.33" - csso "^3.5.1" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -tapable@^1.0.0, tapable@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tar@^4: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.3.5" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - -terser-webpack-plugin@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz#69aa22426299f4b5b3775cbed8cb2c5d419aa1d4" - integrity sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg== - dependencies: - cacache "^11.3.2" - find-cache-dir "^2.0.0" - is-wsl "^1.1.0" - loader-utils "^1.2.3" - schema-utils "^1.0.0" - serialize-javascript "^1.7.0" - source-map "^0.6.1" - terser "^4.0.0" - webpack-sources "^1.3.0" - worker-farm "^1.7.0" - -terser@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.2.tgz#b2656c8a506f7ce805a3f300a2ff48db022fa391" - integrity sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -thunky@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" - integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== - -timers-browserify@^2.0.4: - version "2.0.10" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" - integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== - dependencies: - setimmediate "^1.0.4" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tiny-emitter@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" - integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-factory@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-factory/-/to-factory-1.0.0.tgz#8738af8bd97120ad1d4047972ada5563bf9479b1" - integrity sha1-hzivi9lxIK0dQEeXKtpVY7+UebE= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -toml@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - -toposort@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" - integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-fest@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" - integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" - integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.0, upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== - -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-loader@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" - integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== - dependencies: - loader-utils "^1.1.0" - mime "^2.0.3" - schema-utils "^1.0.0" - -url-parse@^1.4.3: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@1.0.0, util.promisify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utila@^0.4.0, utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.0.1, uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -vendors@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.3.tgz#a6467781abd366217c050f8202e7e50cc9eef8c0" - integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vm-browserify@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" - integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== - -vue-hot-reload-api@^2.3.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf" - integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g== - -vue-loader@^15.2.4: - version "15.7.1" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.7.1.tgz#6ccacd4122aa80f69baaac08ff295a62e3aefcfd" - integrity sha512-fwIKtA23Pl/rqfYP5TSGK7gkEuLhoTvRYW+TU7ER3q9GpNLt/PjG5NLv3XHRDiTg7OPM1JcckBgds+VnAc+HbA== - dependencies: - "@vue/component-compiler-utils" "^3.0.0" - hash-sum "^1.0.2" - loader-utils "^1.1.0" - vue-hot-reload-api "^2.3.0" - vue-style-loader "^4.1.0" - -vue-router@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.7.tgz#b36ca107b4acb8ff5bc4ff824584059c23fcb87b" - integrity sha512-utJ+QR3YlIC/6x6xq17UMXeAfxEvXA0VKD3PiSio7hBOZNusA1jXcbxZxVEfJunLp48oonjTepY8ORoIlRx/EQ== - -vue-server-renderer@^2.5.16: - version "2.6.10" - resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375" - integrity sha512-UYoCEutBpKzL2fKCwx8zlRtRtwxbPZXKTqbl2iIF4yRZUNO/ovrHyDAJDljft0kd+K0tZhN53XRHkgvCZoIhug== - dependencies: - chalk "^1.1.3" - hash-sum "^1.0.2" - he "^1.1.0" - lodash.template "^4.4.0" - lodash.uniq "^4.5.0" - resolve "^1.2.0" - serialize-javascript "^1.3.0" - source-map "0.5.6" - -vue-style-loader@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" - integrity sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ== - dependencies: - hash-sum "^1.0.2" - loader-utils "^1.0.2" - -vue-template-compiler@^2.5.16: - version "2.6.10" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz#323b4f3495f04faa3503337a82f5d6507799c9cc" - integrity sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg== - dependencies: - de-indent "^1.0.2" - he "^1.1.0" - -vue-template-es2015-compiler@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" - integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== - -vue@^2.5.16: - version "2.6.10" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637" - integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ== - -vuepress-html-webpack-plugin@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz#219be272ad510faa8750d2d4e70fd028bfd1c16e" - integrity sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A== - dependencies: - html-minifier "^3.2.3" - loader-utils "^0.2.16" - lodash "^4.17.3" - pretty-error "^2.0.2" - tapable "^1.0.0" - toposort "^1.0.0" - util.promisify "1.0.0" - -vuepress-plugin-container@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vuepress-plugin-container/-/vuepress-plugin-container-2.0.2.tgz#3489cc732c7a210b31f202556e1346125dffeb73" - integrity sha512-SrGYYT7lkie7xlIlAVhn+9sDW42MytNCoxWL/2uDr+q9wZA4h1uYlQvfc2DVjy+FsM9PPPSslkeo/zCpYVY82g== - dependencies: - markdown-it-container "^2.0.0" - -vuepress@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-1.0.2.tgz#da62d6e43faca0b8af0bcffff6975fa27dbfdea3" - integrity sha512-HPRWxrq6D+S9uCR3oJ8/OTCy8GcYm9l1HxHb44rvaN2gyySVXIiqSkCwzd9r2PY8+pwvrfYE4rcY1RsTTpJ25g== - dependencies: - "@vuepress/core" "^1.0.2" - "@vuepress/theme-default" "^1.0.2" - cac "^6.3.9" - envinfo "^7.2.0" - -watchpack@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" - integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== - dependencies: - chokidar "^2.0.2" - graceful-fs "^4.1.2" - neo-async "^2.5.0" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webpack-chain@^4.6.0, webpack-chain@^4.9.0: - version "4.12.1" - resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6" - integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ== - dependencies: - deepmerge "^1.5.2" - javascript-stringify "^1.6.0" - -webpack-dev-middleware@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz#ef751d25f4e9a5c8a35da600c5fda3582b5c6cff" - integrity sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.2" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@^3.5.1: - version "3.7.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.7.2.tgz#f79caa5974b7f8b63268ef5421222a8486d792f5" - integrity sha512-mjWtrKJW2T9SsjJ4/dxDC2fkFVUw8jlpemDERqV0ZJIkjjjamR2AbQlr3oz+j4JLhYCHImHnXZK5H06P2wvUew== - dependencies: - ansi-html "0.0.7" - bonjour "^3.5.0" - chokidar "^2.1.6" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.2.1" - http-proxy-middleware "^0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - killable "^1.0.1" - loglevel "^1.6.3" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.20" - schema-utils "^1.0.0" - selfsigned "^1.10.4" - semver "^6.1.1" - serve-index "^1.9.1" - sockjs "0.3.19" - sockjs-client "1.3.0" - spdy "^4.0.0" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.0" - webpack-log "^2.0.0" - yargs "12.0.5" - -webpack-log@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" - integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== - dependencies: - ansi-colors "^3.0.0" - uuid "^3.3.2" - -webpack-merge@^4.1.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4" - integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw== - dependencies: - lodash "^4.17.5" - -webpack-sources@^1.1.0, webpack-sources@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" - integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.8.1: - version "4.36.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.36.1.tgz#f546fda7a403a76faeaaa7196c50d12370ed18a9" - integrity sha512-Ej01/N9W8DVyhEpeQnbUdGvOECw0L46FxS12cCOs8gSK7bhUlrbHRnWkjiXckGlHjUrmL89kDpTRIkUk6Y+fKg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - chrome-trace-event "^1.0.0" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.0" - json-parse-better-errors "^1.0.2" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - micromatch "^3.1.8" - mkdirp "~0.5.0" - neo-async "^2.5.0" - node-libs-browser "^2.0.0" - schema-utils "^1.0.0" - tapable "^1.1.0" - terser-webpack-plugin "^1.1.0" - watchpack "^1.5.0" - webpack-sources "^1.3.0" - -webpackbar@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.2.0.tgz#bdaad103fad11a4e612500e72aaae98b08ba493f" - integrity sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw== - dependencies: - ansi-escapes "^4.1.0" - chalk "^2.4.1" - consola "^2.6.0" - figures "^3.0.0" - pretty-time "^1.1.0" - std-env "^2.2.1" - text-table "^0.2.0" - wrap-ansi "^5.1.0" - -websocket-driver@>=0.5.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" - integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== - dependencies: - http-parser-js ">=0.4.0 <0.4.11" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" - integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== - -when@~3.6.x: - version "3.6.4" - resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" - integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -zepto@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" - integrity sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g= diff --git a/go.mod b/go.mod index 6a99af9e7880..1319726ed288 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,66 @@ +go 1.16 + module github.com/cosmos/cosmos-sdk require ( github.com/99designs/keyring v1.1.6 - github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d + github.com/DataDog/zstd v1.4.5 // indirect + github.com/armon/go-metrics v0.3.8 github.com/bgentry/speakeasy v0.1.0 - github.com/btcsuite/btcd v0.20.1-beta - github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d + github.com/btcsuite/btcd v0.21.0-beta + github.com/btcsuite/btcutil v1.0.2 + github.com/confio/ics23/go v0.6.6 + github.com/cosmos/go-bip39 v1.0.0 + github.com/cosmos/iavl v0.16.0 github.com/cosmos/ledger-cosmos-go v0.11.1 - github.com/gogo/protobuf v1.3.1 - github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 - github.com/gorilla/handlers v1.4.2 - github.com/gorilla/mux v1.7.3 + github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 + github.com/gogo/gateway v1.1.0 + github.com/gogo/protobuf v1.3.3 + github.com/golang/mock v1.4.4 + github.com/golang/protobuf v1.5.2 + github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 // indirect + github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa // indirect + github.com/gorilla/handlers v1.5.1 + github.com/gorilla/mux v1.8.0 + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/hashicorp/golang-lru v0.5.4 + github.com/magiconair/properties v1.8.5 github.com/mattn/go-isatty v0.0.12 - github.com/pelletier/go-toml v1.6.0 + github.com/mitchellh/mapstructure v1.3.3 // indirect + github.com/otiai10/copy v1.6.0 + github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 - github.com/rakyll/statik v0.1.6 - github.com/spf13/afero v1.2.1 // indirect - github.com/spf13/cobra v1.0.0 + github.com/prometheus/client_golang v1.10.0 + github.com/prometheus/common v0.23.0 + github.com/rakyll/statik v0.1.7 + github.com/regen-network/cosmos-proto v0.3.1 + github.com/rs/zerolog v1.21.0 + github.com/spf13/afero v1.3.4 // indirect + github.com/spf13/cast v1.3.1 + github.com/spf13/cobra v1.1.3 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.6.3 - github.com/stretchr/testify v1.5.1 + github.com/spf13/viper v1.7.1 + github.com/stretchr/testify v1.7.0 github.com/tendermint/btcd v0.1.1 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 - github.com/tendermint/go-amino v0.15.1 - github.com/tendermint/iavl v0.13.2 - github.com/tendermint/tendermint v0.33.9 - github.com/tendermint/tm-db v0.5.1 - gopkg.in/yaml.v2 v2.2.8 + github.com/tendermint/go-amino v0.16.0 + github.com/tendermint/tendermint v0.34.10 + github.com/tendermint/tm-db v0.6.4 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect + google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f + google.golang.org/grpc v1.37.0 + google.golang.org/protobuf v1.26.0 + gopkg.in/ini.v1 v1.61.0 // indirect + gopkg.in/yaml.v2 v2.4.0 ) -go 1.15 +replace google.golang.org/grpc => google.golang.org/grpc v1.33.2 -replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 -replace github.com/tendermint/tendermint => github.com/fetchai/cosmos-consensus v0.16.3 +replace github.com/tendermint/tendermint => github.com/fetchai/tendermint v1.0.0 diff --git a/go.sum b/go.sum index 9d4d18d58245..d8b2cd040014 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,27 @@ -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= -github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= -github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +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= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= -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-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= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +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= @@ -22,27 +35,31 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/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/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.8 h1:oOxq3KPj0WhCuy50EhzwiyMyG2ovRQZpZLXQuOh2a/M= +github.com/armon/go-metrics v0.3.8/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.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 h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= -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= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= 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= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -50,7 +67,9 @@ github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2ut github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -60,25 +79,36 @@ 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= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/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/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= +github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8= +github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 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/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4= +github.com/cosmos/iavl v0.16.0 h1:ICIOB8xysirTX27GmVAaoeSpeozzgSu9d49w36xkVJA= +github.com/cosmos/iavl v0.16.0/go.mod h1:2A8O/Jz9YwtjqXMO0CjnnbTYEEaovE8jWcwrakH3PoE= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -88,21 +118,30 @@ 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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= +github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/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 v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -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/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.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= 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= @@ -110,15 +149,20 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL 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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fetchai/cosmos-consensus v0.16.3 h1:c+r5dUUs2uX7qp/6X0SPAbXkIY84WpZhDAn7KoLy7HA= -github.com/fetchai/cosmos-consensus v0.16.3/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fetchai/tendermint v1.0.0 h1:O88hdRfO1BZn0J5+ifcAGjIm89PGdZj6uSXMhzjsECc= +github.com/fetchai/tendermint v1.0.0/go.mod h1:aeHL7alPh4uTBIJQ8mgFEE8VwJLXI1VD3rVOmH2Mcy0= 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= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/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= @@ -132,63 +176,85 @@ 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/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/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= +github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= 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.3.1-0.20190508161146-9fa652df1129 h1:tT8iWCYw4uOem71yYA3htfH+LNopJvcqZQshm56G5L4= -github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +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= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg= +github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/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/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= 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= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -196,17 +262,23 @@ github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -220,30 +292,38 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 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/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/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.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -254,10 +334,10 @@ github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOS 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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 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= @@ -269,8 +349,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= -github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= +github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= +github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -279,8 +359,9 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -288,6 +369,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb 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/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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= @@ -295,15 +377,22 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +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.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= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -313,13 +402,24 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= +github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E= +github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 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 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.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= -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/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= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -327,6 +427,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -335,8 +436,11 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -349,31 +453,50 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= 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.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.23.0 h1:GXWvPYuTUenIa+BhOq/x+L/QZzCqASkVRny5KTlPDGM= +github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= 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= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= -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-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +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/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 v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.21.0 h1:Q3vdXlfLNT+OftyBHsU0Y445MD+8m8axjKgf2si0QcM= +github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +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= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -382,14 +505,19 @@ github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPH github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +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 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= -github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +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= @@ -397,9 +525,10 @@ 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= -github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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/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= @@ -410,41 +539,45 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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/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.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= -github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/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/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 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= -github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/iavl v0.13.2 h1:O1m08/Ciy53l9IYmf75uIRVvrNsfjEbre8u/yCu/oqk= -github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA= -github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= -github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= -github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= +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/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/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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 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/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= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -457,24 +590,46 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y= -golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/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= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad 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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +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-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-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= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/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.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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -485,98 +640,160 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= +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-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= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-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= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-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= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-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/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +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.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-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= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/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.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.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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k= -google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +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= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -584,11 +801,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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/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= @@ -596,13 +813,20 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN 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= 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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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-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= +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 new file mode 100644 index 000000000000..a0da4739d2b9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,137 @@ +{ + "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/auth/v1beta1/auth.proto b/proto/cosmos/auth/v1beta1/auth.proto new file mode 100644 index 000000000000..72e1d9ec285b --- /dev/null +++ b/proto/cosmos/auth/v1beta1/auth.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// BaseAccount defines a base account type. It contains all the necessary fields +// for basic account functionality. Any custom account type should extend this +// type for additional functionality (e.g. vesting). +message BaseAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.equal) = false; + + option (cosmos_proto.implements_interface) = "AccountI"; + + string address = 1; + google.protobuf.Any pub_key = 2 + [(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; + uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; + uint64 sequence = 4; +} + +// ModuleAccount defines an account for modules that holds coins on a pool. +message ModuleAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + option (cosmos_proto.implements_interface) = "ModuleAccountI"; + + BaseAccount base_account = 1 [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + string name = 2; + repeated string permissions = 3; +} + +// Params defines the parameters for the auth module. +message Params { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + uint64 max_memo_characters = 1 [(gogoproto.moretags) = "yaml:\"max_memo_characters\""]; + uint64 tx_sig_limit = 2 [(gogoproto.moretags) = "yaml:\"tx_sig_limit\""]; + uint64 tx_size_cost_per_byte = 3 [(gogoproto.moretags) = "yaml:\"tx_size_cost_per_byte\""]; + uint64 sig_verify_cost_ed25519 = 4 + [(gogoproto.customname) = "SigVerifyCostED25519", (gogoproto.moretags) = "yaml:\"sig_verify_cost_ed25519\""]; + uint64 sig_verify_cost_secp256k1 = 5 + [(gogoproto.customname) = "SigVerifyCostSecp256k1", (gogoproto.moretags) = "yaml:\"sig_verify_cost_secp256k1\""]; +} diff --git a/proto/cosmos/auth/v1beta1/genesis.proto b/proto/cosmos/auth/v1beta1/genesis.proto new file mode 100644 index 000000000000..c88b94ee4ece --- /dev/null +++ b/proto/cosmos/auth/v1beta1/genesis.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/auth/v1beta1/auth.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// GenesisState defines the auth module's genesis state. +message GenesisState { + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false]; + + // accounts are the accounts present at genesis. + repeated google.protobuf.Any accounts = 2; +} diff --git a/proto/cosmos/auth/v1beta1/query.proto b/proto/cosmos/auth/v1beta1/query.proto new file mode 100644 index 000000000000..a8857926833f --- /dev/null +++ b/proto/cosmos/auth/v1beta1/query.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package cosmos.auth.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "cosmos/auth/v1beta1/auth.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types"; + +// Query defines the gRPC querier service. +service Query { + // Account returns account details based on address. + rpc Account(QueryAccountRequest) returns (QueryAccountResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}"; + } + + // Params queries all parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/params"; + } +} + +// QueryAccountRequest is the request type for the Query/Account RPC method. +message QueryAccountRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address defines the address to query for. + string address = 1; +} + +// QueryAccountResponse is the response type for the Query/Account RPC method. +message QueryAccountResponse { + // account defines the account of the corresponding address. + google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "AccountI"]; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/bank/v1beta1/bank.proto b/proto/cosmos/bank/v1beta1/bank.proto new file mode 100644 index 000000000000..5a9383362e42 --- /dev/null +++ b/proto/cosmos/bank/v1beta1/bank.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Params defines the parameters for the bank module. +message Params { + option (gogoproto.goproto_stringer) = false; + repeated SendEnabled send_enabled = 1 [(gogoproto.moretags) = "yaml:\"send_enabled,omitempty\""]; + bool default_send_enabled = 2 [(gogoproto.moretags) = "yaml:\"default_send_enabled,omitempty\""]; +} + +// SendEnabled maps coin denom to a send_enabled status (whether a denom is +// sendable). +message SendEnabled { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + string denom = 1; + bool enabled = 2; +} + +// Input models transaction input. +message Input { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Output models transaction outputs. +message Output { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Supply represents a struct that passively keeps track of the total supply +// amounts in the network. +message Supply { + option (gogoproto.equal) = true; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + option (cosmos_proto.implements_interface) = "*github.com/cosmos/cosmos-sdk/x/bank/exported.SupplyI"; + + repeated cosmos.base.v1beta1.Coin total = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// DenomUnit represents a struct that describes a given +// denomination unit of the basic token. +message DenomUnit { + // denom represents the string name of the given denom unit (e.g uatom). + string denom = 1; + // 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). + uint32 exponent = 2; + // aliases is a list of string aliases for the given denom + repeated string aliases = 3; +} + +// Metadata represents a struct that describes +// a basic token. +message Metadata { + string description = 1; + // denom_units represents the list of DenomUnit's for a given coin + repeated DenomUnit denom_units = 2; + // base represents the base denom (should be the DenomUnit with exponent = 0). + string base = 3; + // display indicates the suggested denom that should be + // displayed in clients. + string display = 4; +} diff --git a/proto/cosmos/bank/v1beta1/genesis.proto b/proto/cosmos/bank/v1beta1/genesis.proto new file mode 100644 index 000000000000..25c80a38b5ab --- /dev/null +++ b/proto/cosmos/bank/v1beta1/genesis.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// GenesisState defines the bank module's genesis state. +message GenesisState { + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false]; + + // balances is an array containing the balances of all the accounts. + repeated Balance balances = 2 [(gogoproto.nullable) = false]; + + // supply represents the total supply. + repeated cosmos.base.v1beta1.Coin supply = 3 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; + + // denom_metadata defines the metadata of the differents coins. + repeated Metadata denom_metadata = 4 [(gogoproto.moretags) = "yaml:\"denom_metadata\"", (gogoproto.nullable) = false]; +} + +// Balance defines an account address and balance pair used in the bank module's +// genesis state. +message Balance { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address of the balance holder. + string address = 1; + + // coins defines the different coins this balance holds. + repeated cosmos.base.v1beta1.Coin coins = 2 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/bank/v1beta1/query.proto b/proto/cosmos/bank/v1beta1/query.proto new file mode 100644 index 000000000000..bc5e29137a95 --- /dev/null +++ b/proto/cosmos/bank/v1beta1/query.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Query defines the gRPC querier service. +service Query { + // Balance queries the balance of a single coin for a single account. + rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/balances/{address}/{denom}"; + } + + // AllBalances queries the balance of all coins for a single account. + rpc AllBalances(QueryAllBalancesRequest) returns (QueryAllBalancesResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/balances/{address}"; + } + + // TotalSupply queries the total supply of all coins. + rpc TotalSupply(QueryTotalSupplyRequest) returns (QueryTotalSupplyResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/supply"; + } + + // SupplyOf queries the supply of a single coin. + rpc SupplyOf(QuerySupplyOfRequest) returns (QuerySupplyOfResponse) { + option (google.api.http).get = "/cosmos/bank/v1beta1/supply/{denom}"; + } + + // Params queries the parameters of x/bank module. + 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. +message QueryBalanceRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address to query balances for. + string address = 1; + + // denom is the coin denom to query balances for. + string denom = 2; +} + +// QueryBalanceResponse is the response type for the Query/Balance RPC method. +message QueryBalanceResponse { + // balance is the balance of the coin. + cosmos.base.v1beta1.Coin balance = 1; +} + +// QueryBalanceRequest is the request type for the Query/AllBalances RPC method. +message QueryAllBalancesRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address to query balances for. + string address = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryAllBalancesResponse is the response type for the Query/AllBalances RPC +// method. +message QueryAllBalancesResponse { + // balances is the balances of all the coins. + repeated cosmos.base.v1beta1.Coin balances = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC +// method. +message QueryTotalSupplyRequest {} + +// QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC +// method +message QueryTotalSupplyResponse { + // supply is the supply of the coins + repeated cosmos.base.v1beta1.Coin supply = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// QuerySupplyOfRequest is the request type for the Query/SupplyOf RPC method. +message QuerySupplyOfRequest { + // denom is the coin denom to query balances for. + string denom = 1; +} + +// QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. +message QuerySupplyOfResponse { + // amount is the supply of the coin. + cosmos.base.v1beta1.Coin amount = 1 [(gogoproto.nullable) = false]; +} + +// QueryParamsRequest defines the request type for querying x/bank parameters. +message QueryParamsRequest {} + +// QueryParamsResponse defines the response type for querying x/bank parameters. +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/bank/v1beta1/tx.proto b/proto/cosmos/bank/v1beta1/tx.proto new file mode 100644 index 000000000000..26b2ab41f4c3 --- /dev/null +++ b/proto/cosmos/bank/v1beta1/tx.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/bank/v1beta1/bank.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/bank/types"; + +// Msg defines the bank Msg service. +service Msg { + // Send defines a method for sending coins from one account to another account. + rpc Send(MsgSend) returns (MsgSendResponse); + + // MultiSend defines a method for sending coins from some accounts to other accounts. + rpc MultiSend(MsgMultiSend) returns (MsgMultiSendResponse); +} + +// MsgSend represents a message to send coins from one account to another. +message MsgSend { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""]; + string to_address = 2 [(gogoproto.moretags) = "yaml:\"to_address\""]; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// MsgSendResponse defines the Msg/Send response type. +message MsgSendResponse {} + +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. +message MsgMultiSend { + option (gogoproto.equal) = false; + + repeated Input inputs = 1 [(gogoproto.nullable) = false]; + repeated Output outputs = 2 [(gogoproto.nullable) = false]; +} + +// MsgMultiSendResponse defines the Msg/MultiSend response type. +message MsgMultiSendResponse {} diff --git a/proto/cosmos/base/abci/v1beta1/abci.proto b/proto/cosmos/base/abci/v1beta1/abci.proto new file mode 100644 index 000000000000..72da2aacc36e --- /dev/null +++ b/proto/cosmos/base/abci/v1beta1/abci.proto @@ -0,0 +1,137 @@ +syntax = "proto3"; +package cosmos.base.abci.v1beta1; + +import "gogoproto/gogo.proto"; +import "tendermint/abci/types.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types"; +option (gogoproto.goproto_stringer_all) = false; + +// TxResponse defines a structure containing relevant tx data and metadata. The +// tags are stringified and the log is JSON decoded. +message TxResponse { + option (gogoproto.goproto_getters) = false; + // The block height + int64 height = 1; + // The transaction hash. + string txhash = 2 [(gogoproto.customname) = "TxHash"]; + // Namespace for the Code + string codespace = 3; + // Response code. + uint32 code = 4; + // Result bytes, if any. + string data = 5; + // The output of the application's logger (raw string). May be + // non-deterministic. + string raw_log = 6; + // The output of the application's logger (typed). May be non-deterministic. + repeated ABCIMessageLog logs = 7 [(gogoproto.castrepeated) = "ABCIMessageLogs", (gogoproto.nullable) = false]; + // Additional information. May be non-deterministic. + string info = 8; + // Amount of gas requested for transaction. + int64 gas_wanted = 9; + // Amount of gas consumed by transaction. + int64 gas_used = 10; + // The request transaction bytes. + google.protobuf.Any tx = 11; + // 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. + string timestamp = 12; +} + +// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +message ABCIMessageLog { + option (gogoproto.stringer) = true; + + uint32 msg_index = 1; + string log = 2; + + // Events contains a slice of Event objects that were emitted during some + // execution. + repeated StringEvent events = 3 [(gogoproto.castrepeated) = "StringEvents", (gogoproto.nullable) = false]; +} + +// StringEvent defines en Event object wrapper where all the attributes +// contain key/value pairs that are strings instead of raw bytes. +message StringEvent { + option (gogoproto.stringer) = true; + + string type = 1; + repeated Attribute attributes = 2 [(gogoproto.nullable) = false]; +} + +// Attribute defines an attribute wrapper where the key and value are +// strings instead of raw bytes. +message Attribute { + string key = 1; + string value = 2; +} + +// GasInfo defines tx execution gas context. +message GasInfo { + // GasWanted is the maximum units of work we allow this tx to perform. + uint64 gas_wanted = 1 [(gogoproto.moretags) = "yaml:\"gas_wanted\""]; + + // GasUsed is the amount of gas actually consumed. + uint64 gas_used = 2 [(gogoproto.moretags) = "yaml:\"gas_used\""]; +} + +// Result is the union of ResponseFormat and ResponseCheckTx. +message Result { + option (gogoproto.goproto_getters) = false; + + // Data is any data returned from message or handler execution. It MUST be + // length prefixed in order to separate data from multiple message executions. + bytes data = 1; + + // Log contains the log information from message or handler execution. + string log = 2; + + // Events contains a slice of Event objects that were emitted during message + // or handler execution. + repeated tendermint.abci.Event events = 3 [(gogoproto.nullable) = false]; +} + +// SimulationResponse defines the response generated when a transaction is +// successfully simulated. +message SimulationResponse { + GasInfo gas_info = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + Result result = 2; +} + +// MsgData defines the data returned in a Result object during message +// execution. +message MsgData { + option (gogoproto.stringer) = true; + + string msg_type = 1; + bytes data = 2; +} + +// TxMsgData defines a list of MsgData. A transaction will have a MsgData object +// for each message. +message TxMsgData { + option (gogoproto.stringer) = true; + + repeated MsgData data = 1; +} + +// SearchTxsResult defines a structure for querying txs pageable +message SearchTxsResult { + option (gogoproto.stringer) = true; + + // Count of all txs + uint64 total_count = 1 [(gogoproto.moretags) = "yaml:\"total_count\"", (gogoproto.jsontag) = "total_count"]; + // Count of txs in current page + uint64 count = 2; + // Index of current page, start from 1 + uint64 page_number = 3 [(gogoproto.moretags) = "yaml:\"page_number\"", (gogoproto.jsontag) = "page_number"]; + // Count of total pages + uint64 page_total = 4 [(gogoproto.moretags) = "yaml:\"page_total\"", (gogoproto.jsontag) = "page_total"]; + // Max count txs per page + uint64 limit = 5; + // List of txs in current page + repeated TxResponse txs = 6; +} diff --git a/proto/cosmos/base/kv/v1beta1/kv.proto b/proto/cosmos/base/kv/v1beta1/kv.proto new file mode 100644 index 000000000000..4e9b8d285031 --- /dev/null +++ b/proto/cosmos/base/kv/v1beta1/kv.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package cosmos.base.kv.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/kv"; + +// Pairs defines a repeated slice of Pair objects. +message Pairs { + repeated Pair pairs = 1 [(gogoproto.nullable) = false]; +} + +// Pair defines a key/value bytes tuple. +message Pair { + bytes key = 1; + bytes value = 2; +} diff --git a/proto/cosmos/base/query/v1beta1/pagination.proto b/proto/cosmos/base/query/v1beta1/pagination.proto new file mode 100644 index 000000000000..2a8cbccedd87 --- /dev/null +++ b/proto/cosmos/base/query/v1beta1/pagination.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package cosmos.base.query.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/types/query"; + +// PageRequest is to be embedded in gRPC request messages for efficient +// pagination. Ex: +// +// message SomeRequest { +// Foo some_parameter = 1; +// PageRequest pagination = 2; +// } +message PageRequest { + // 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. + bytes key = 1; + + // 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. + uint64 offset = 2; + + // 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. + uint64 limit = 3; + + // 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. + bool count_total = 4; +} + +// 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; +// } +message PageResponse { + // next_key is the key to be passed to PageRequest.key to + // query the next page most efficiently + bytes next_key = 1; + + // total is total number of results available if PageRequest.count_total + // was set, its value is undefined otherwise + uint64 total = 2; +} diff --git a/proto/cosmos/base/reflection/v1beta1/reflection.proto b/proto/cosmos/base/reflection/v1beta1/reflection.proto new file mode 100644 index 000000000000..22670e72b883 --- /dev/null +++ b/proto/cosmos/base/reflection/v1beta1/reflection.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; +package cosmos.base.reflection.v1beta1; + +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/client/grpc/reflection"; + +// ReflectionService defines a service for interface reflection. +service ReflectionService { + // ListAllInterfaces lists all the interfaces registered in the interface + // registry. + rpc ListAllInterfaces(ListAllInterfacesRequest) returns (ListAllInterfacesResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/interfaces"; + }; + + // ListImplementations list all the concrete types that implement a given + // interface. + rpc ListImplementations(ListImplementationsRequest) returns (ListImplementationsResponse) { + option (google.api.http).get = "/cosmos/base/reflection/v1beta1/interfaces/" + "{interface_name}/implementations"; + }; +} + +// ListAllInterfacesRequest is the request type of the ListAllInterfaces RPC. +message ListAllInterfacesRequest {} + +// ListAllInterfacesResponse is the response type of the ListAllInterfaces RPC. +message ListAllInterfacesResponse { + // interface_names is an array of all the registered interfaces. + repeated string interface_names = 1; +} + +// ListImplementationsRequest is the request type of the ListImplementations +// RPC. +message ListImplementationsRequest { + // interface_name defines the interface to query the implementations for. + string interface_name = 1; +} + +// ListImplementationsResponse is the response type of the ListImplementations +// RPC. +message ListImplementationsResponse { + repeated string implementation_message_names = 1; +} diff --git a/proto/cosmos/base/snapshots/v1beta1/snapshot.proto b/proto/cosmos/base/snapshots/v1beta1/snapshot.proto new file mode 100644 index 000000000000..9ac5a7c31be4 --- /dev/null +++ b/proto/cosmos/base/snapshots/v1beta1/snapshot.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package cosmos.base.snapshots.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/snapshots/types"; + +// Snapshot contains Tendermint state sync snapshot info. +message Snapshot { + uint64 height = 1; + uint32 format = 2; + uint32 chunks = 3; + bytes hash = 4; + Metadata metadata = 5 [(gogoproto.nullable) = false]; +} + +// Metadata contains SDK-specific snapshot metadata. +message Metadata { + repeated bytes chunk_hashes = 1; // SHA-256 chunk hashes +} \ No newline at end of file diff --git a/proto/cosmos/base/store/v1beta1/commit_info.proto b/proto/cosmos/base/store/v1beta1/commit_info.proto new file mode 100644 index 000000000000..98a33d30e754 --- /dev/null +++ b/proto/cosmos/base/store/v1beta1/commit_info.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package cosmos.base.store.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/store/types"; + +// CommitInfo defines commit information used by the multi-store when committing +// a version/height. +message CommitInfo { + int64 version = 1; + repeated StoreInfo store_infos = 2 [(gogoproto.nullable) = false]; +} + +// StoreInfo defines store-specific commit information. It contains a reference +// between a store name and the commit ID. +message StoreInfo { + string name = 1; + CommitID commit_id = 2 [(gogoproto.nullable) = false]; +} + +// CommitID defines the committment information when a specific store is +// committed. +message CommitID { + option (gogoproto.goproto_stringer) = false; + + int64 version = 1; + bytes hash = 2; +} diff --git a/proto/cosmos/base/store/v1beta1/snapshot.proto b/proto/cosmos/base/store/v1beta1/snapshot.proto new file mode 100644 index 000000000000..834855093b24 --- /dev/null +++ b/proto/cosmos/base/store/v1beta1/snapshot.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package cosmos.base.store.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/store/types"; + +// SnapshotItem is an item contained in a rootmulti.Store snapshot. +message SnapshotItem { + // item is the specific type of snapshot item. + oneof item { + SnapshotStoreItem store = 1; + SnapshotIAVLItem iavl = 2 [(gogoproto.customname) = "IAVL"]; + } +} + +// SnapshotStoreItem contains metadata about a snapshotted store. +message SnapshotStoreItem { + string name = 1; +} + +// SnapshotIAVLItem is an exported IAVL node. +message SnapshotIAVLItem { + bytes key = 1; + bytes value = 2; + int64 version = 3; + int32 height = 4; +} \ No newline at end of file diff --git a/proto/cosmos/base/tendermint/v1beta1/query.proto b/proto/cosmos/base/tendermint/v1beta1/query.proto new file mode 100644 index 000000000000..50cb5852cdf2 --- /dev/null +++ b/proto/cosmos/base/tendermint/v1beta1/query.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; +package cosmos.base.tendermint.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "tendermint/p2p/types.proto"; +import "tendermint/types/block.proto"; +import "tendermint/types/types.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/client/grpc/tmservice"; + +// Service defines the gRPC querier service for tendermint queries. +service Service { + // GetNodeInfo queries the current node info. + rpc GetNodeInfo(GetNodeInfoRequest) returns (GetNodeInfoResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/node_info"; + } + // GetSyncing queries node syncing. + rpc GetSyncing(GetSyncingRequest) returns (GetSyncingResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/syncing"; + } + // GetLatestBlock returns the latest block. + rpc GetLatestBlock(GetLatestBlockRequest) returns (GetLatestBlockResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/latest"; + } + // GetBlockByHeight queries block for given height. + rpc GetBlockByHeight(GetBlockByHeightRequest) returns (GetBlockByHeightResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/{height}"; + } + + // GetLatestValidatorSet queries latest validator-set. + rpc GetLatestValidatorSet(GetLatestValidatorSetRequest) returns (GetLatestValidatorSetResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validatorsets/latest"; + } + // GetValidatorSetByHeight queries validator-set at a given height. + rpc GetValidatorSetByHeight(GetValidatorSetByHeightRequest) returns (GetValidatorSetByHeightResponse) { + option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validatorsets/{height}"; + } +} + +// GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +message GetValidatorSetByHeightRequest { + int64 height = 1; + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +message GetValidatorSetByHeightResponse { + int64 block_height = 1; + repeated Validator validators = 2; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method. +message GetLatestValidatorSetRequest { + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method. +message GetLatestValidatorSetResponse { + int64 block_height = 1; + repeated Validator validators = 2; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// Validator is the type for the validator-set. +message Validator { + string address = 1; + google.protobuf.Any pub_key = 2; + int64 voting_power = 3; + int64 proposer_priority = 4; +} + +// GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method. +message GetBlockByHeightRequest { + int64 height = 1; +} + +// GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method. +message GetBlockByHeightResponse { + .tendermint.types.BlockID block_id = 1; + .tendermint.types.Block block = 2; +} + +// GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method. +message GetLatestBlockRequest {} + +// GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method. +message GetLatestBlockResponse { + .tendermint.types.BlockID block_id = 1; + .tendermint.types.Block block = 2; +} + +// GetSyncingRequest is the request type for the Query/GetSyncing RPC method. +message GetSyncingRequest {} + +// GetSyncingResponse is the response type for the Query/GetSyncing RPC method. +message GetSyncingResponse { + bool syncing = 1; +} + +// GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method. +message GetNodeInfoRequest {} + +// GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method. +message GetNodeInfoResponse { + .tendermint.p2p.DefaultNodeInfo default_node_info = 1; + VersionInfo application_version = 2; +} + +// VersionInfo is the type for the GetNodeInfoResponse message. +message VersionInfo { + string name = 1; + string app_name = 2; + string version = 3; + string git_commit = 4; + string build_tags = 5; + string go_version = 6; + repeated Module build_deps = 7; +} + +// Module is the type for VersionInfo +message Module { + // module path + string path = 1; + // module version + string version = 2; + // checksum + string sum = 3; +} diff --git a/proto/cosmos/base/v1beta1/coin.proto b/proto/cosmos/base/v1beta1/coin.proto new file mode 100644 index 000000000000..fab75284b7f6 --- /dev/null +++ b/proto/cosmos/base/v1beta1/coin.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package cosmos.base.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = false; + +// Coin defines a token with a denomination and an amount. +// +// NOTE: The amount field is an Int which implements the custom method +// signatures required by gogoproto. +message Coin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecCoin defines a token with a denomination and a decimal amount. +// +// NOTE: The amount field is an Dec which implements the custom method +// signatures required by gogoproto. +message DecCoin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} + +// IntProto defines a Protobuf wrapper around an Int object. +message IntProto { + string int = 1 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecProto defines a Protobuf wrapper around a Dec object. +message DecProto { + string dec = 1 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/capability/v1beta1/capability.proto b/proto/cosmos/capability/v1beta1/capability.proto new file mode 100644 index 000000000000..1c8332f34163 --- /dev/null +++ b/proto/cosmos/capability/v1beta1/capability.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; +package cosmos.capability.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/capability/types"; + +import "gogoproto/gogo.proto"; + +// Capability defines an implementation of an object capability. The index +// provided to a Capability must be globally unique. +message Capability { + option (gogoproto.goproto_stringer) = false; + + uint64 index = 1 [(gogoproto.moretags) = "yaml:\"index\""]; +} + +// Owner defines a single capability owner. An owner is defined by the name of +// capability and the module name. +message Owner { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + + string module = 1 [(gogoproto.moretags) = "yaml:\"module\""]; + string name = 2 [(gogoproto.moretags) = "yaml:\"name\""]; +} + +// CapabilityOwners defines a set of owners of a single Capability. The set of +// owners must be unique. +message CapabilityOwners { + repeated Owner owners = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/capability/v1beta1/genesis.proto b/proto/cosmos/capability/v1beta1/genesis.proto new file mode 100644 index 000000000000..05bb0afc4a1f --- /dev/null +++ b/proto/cosmos/capability/v1beta1/genesis.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package cosmos.capability.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/capability/v1beta1/capability.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/capability/types"; + +// GenesisOwners defines the capability owners with their corresponding index. +message GenesisOwners { + // index is the index of the capability owner. + uint64 index = 1; + + // index_owners are the owners at the given index. + CapabilityOwners index_owners = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"index_owners\""]; +} + +// GenesisState defines the capability module's genesis state. +message GenesisState { + // index is the capability global index. + uint64 index = 1; + + // owners represents a map from index to owners of the capability index + // index key is string to allow amino marshalling. + repeated GenesisOwners owners = 2 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/crisis/v1beta1/genesis.proto b/proto/cosmos/crisis/v1beta1/genesis.proto new file mode 100644 index 000000000000..5b0ff7ec72a0 --- /dev/null +++ b/proto/cosmos/crisis/v1beta1/genesis.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cosmos.crisis.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/crisis/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// GenesisState defines the crisis module's genesis state. +message GenesisState { + // constant_fee is the fee used to verify the invariant in the crisis + // module. + cosmos.base.v1beta1.Coin constant_fee = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"constant_fee\""]; +} diff --git a/proto/cosmos/crisis/v1beta1/tx.proto b/proto/cosmos/crisis/v1beta1/tx.proto new file mode 100644 index 000000000000..26457ad6d52a --- /dev/null +++ b/proto/cosmos/crisis/v1beta1/tx.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package cosmos.crisis.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/crisis/types"; + +import "gogoproto/gogo.proto"; + +// Msg defines the bank Msg service. +service Msg { + // VerifyInvariant defines a method to verify a particular invariance. + rpc VerifyInvariant(MsgVerifyInvariant) returns (MsgVerifyInvariantResponse); +} + +// MsgVerifyInvariant represents a message to verify a particular invariance. +message MsgVerifyInvariant { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string sender = 1; + string invariant_module_name = 2 [(gogoproto.moretags) = "yaml:\"invariant_module_name\""]; + string invariant_route = 3 [(gogoproto.moretags) = "yaml:\"invariant_route\""]; +} + +// MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type. +message MsgVerifyInvariantResponse {} diff --git a/proto/cosmos/crypto/ed25519/keys.proto b/proto/cosmos/crypto/ed25519/keys.proto new file mode 100644 index 000000000000..bed9c29cc797 --- /dev/null +++ b/proto/cosmos/crypto/ed25519/keys.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package cosmos.crypto.ed25519; + +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. +message PubKey { + option (gogoproto.goproto_stringer) = false; + + bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PublicKey"]; +} + +// PrivKey defines a ed25519 private key. +message PrivKey { + bytes key = 1 [(gogoproto.casttype) = "crypto/ed25519.PrivateKey"]; +} diff --git a/proto/cosmos/crypto/multisig/keys.proto b/proto/cosmos/crypto/multisig/keys.proto new file mode 100644 index 000000000000..f8398e8052ac --- /dev/null +++ b/proto/cosmos/crypto/multisig/keys.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package cosmos.crypto.multisig; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"; + +// LegacyAminoPubKey specifies a public key type +// which nests multiple public keys and a threshold, +// it uses legacy amino address rules. +message LegacyAminoPubKey { + option (gogoproto.goproto_getters) = false; + + uint32 threshold = 1 [(gogoproto.moretags) = "yaml:\"threshold\""]; + repeated google.protobuf.Any public_keys = 2 + [(gogoproto.customname) = "PubKeys", (gogoproto.moretags) = "yaml:\"pubkeys\""]; +} diff --git a/proto/cosmos/crypto/multisig/v1beta1/multisig.proto b/proto/cosmos/crypto/multisig/v1beta1/multisig.proto new file mode 100644 index 000000000000..bf671f171143 --- /dev/null +++ b/proto/cosmos/crypto/multisig/v1beta1/multisig.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package cosmos.crypto.multisig.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/types"; + +// 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. +message MultiSignature { + option (gogoproto.goproto_unrecognized) = true; + repeated bytes signatures = 1; +} + +// 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. +message CompactBitArray { + option (gogoproto.goproto_stringer) = false; + + uint32 extra_bits_stored = 1; + bytes elems = 2; +} diff --git a/proto/cosmos/crypto/secp256k1/keys.proto b/proto/cosmos/crypto/secp256k1/keys.proto new file mode 100644 index 000000000000..a22725713a59 --- /dev/null +++ b/proto/cosmos/crypto/secp256k1/keys.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package cosmos.crypto.secp256k1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"; + +// 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. +message PubKey { + option (gogoproto.goproto_stringer) = false; + + bytes key = 1; +} + +// PrivKey defines a secp256k1 private key. +message PrivKey { + bytes key = 1; +} diff --git a/proto/cosmos/distribution/v1beta1/distribution.proto b/proto/cosmos/distribution/v1beta1/distribution.proto new file mode 100644 index 000000000000..ae98ec0b98f2 --- /dev/null +++ b/proto/cosmos/distribution/v1beta1/distribution.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Params defines the set of params for the distribution module. +message Params { + option (gogoproto.goproto_stringer) = false; + string community_tax = 1 [ + (gogoproto.moretags) = "yaml:\"community_tax\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string base_proposer_reward = 2 [ + (gogoproto.moretags) = "yaml:\"base_proposer_reward\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + string bonus_proposer_reward = 3 [ + (gogoproto.moretags) = "yaml:\"bonus_proposer_reward\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bool withdraw_addr_enabled = 4 [(gogoproto.moretags) = "yaml:\"withdraw_addr_enabled\""]; +} + +// 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 +message ValidatorHistoricalRewards { + repeated cosmos.base.v1beta1.DecCoin cumulative_reward_ratio = 1 [ + (gogoproto.moretags) = "yaml:\"cumulative_reward_ratio\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; + uint32 reference_count = 2 [(gogoproto.moretags) = "yaml:\"reference_count\""]; +} + +// 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. +message ValidatorCurrentRewards { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; + uint64 period = 2; +} + +// ValidatorAccumulatedCommission represents accumulated commission +// for a validator kept as a running counter, can be withdrawn at any time. +message ValidatorAccumulatedCommission { + repeated cosmos.base.v1beta1.DecCoin commission = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} + +// ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards +// for a validator inexpensive to track, allows simple sanity checks. +message ValidatorOutstandingRewards { + repeated cosmos.base.v1beta1.DecCoin rewards = 1 [ + (gogoproto.moretags) = "yaml:\"rewards\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false + ]; +} + +// 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. +message ValidatorSlashEvent { + uint64 validator_period = 1 [(gogoproto.moretags) = "yaml:\"validator_period\""]; + string fraction = 2 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// ValidatorSlashEvents is a collection of ValidatorSlashEvent messages. +message ValidatorSlashEvents { + option (gogoproto.goproto_stringer) = false; + repeated ValidatorSlashEvent validator_slash_events = 1 + [(gogoproto.moretags) = "yaml:\"validator_slash_events\"", (gogoproto.nullable) = false]; +} + +// FeePool is the global fee pool for distribution. +message FeePool { + repeated cosmos.base.v1beta1.DecCoin community_pool = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.moretags) = "yaml:\"community_pool\"" + ]; +} + +// CommunityPoolSpendProposal details a proposal for use of community funds, +// together with how many coins are proposed to be spent, and to which +// recipient account. +message CommunityPoolSpendProposal { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + string recipient = 3; + repeated cosmos.base.v1beta1.Coin amount = 4 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// 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. +message DelegatorStartingInfo { + uint64 previous_period = 1 [(gogoproto.moretags) = "yaml:\"previous_period\""]; + string stake = 2 [ + (gogoproto.moretags) = "yaml:\"stake\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + uint64 height = 3 [(gogoproto.moretags) = "yaml:\"creation_height\"", (gogoproto.jsontag) = "creation_height"]; +} + +// DelegationDelegatorReward represents the properties +// of a delegator's delegation reward. +message DelegationDelegatorReward { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + repeated cosmos.base.v1beta1.DecCoin reward = 2 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} + +// CommunityPoolSpendProposalWithDeposit defines a CommunityPoolSpendProposal +// with a deposit +message CommunityPoolSpendProposalWithDeposit { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string title = 1 [(gogoproto.moretags) = "yaml:\"title\""]; + string description = 2 [(gogoproto.moretags) = "yaml:\"description\""]; + string recipient = 3 [(gogoproto.moretags) = "yaml:\"recipient\""]; + string amount = 4 [(gogoproto.moretags) = "yaml:\"amount\""]; + string deposit = 5 [(gogoproto.moretags) = "yaml:\"deposit\""]; +} diff --git a/proto/cosmos/distribution/v1beta1/genesis.proto b/proto/cosmos/distribution/v1beta1/genesis.proto new file mode 100644 index 000000000000..c0b17cdf1149 --- /dev/null +++ b/proto/cosmos/distribution/v1beta1/genesis.proto @@ -0,0 +1,155 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/distribution/v1beta1/distribution.proto"; + +// 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. +message DelegatorWithdrawInfo { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address is the address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + + // withdraw_address is the address to withdraw the delegation rewards to. + string withdraw_address = 2 [(gogoproto.moretags) = "yaml:\"withdraw_address\""]; +} + +// ValidatorOutstandingRewardsRecord is used for import/export via genesis json. +message ValidatorOutstandingRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // outstanding_rewards represents the oustanding rewards of a validator. + repeated cosmos.base.v1beta1.DecCoin outstanding_rewards = 2 [ + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"outstanding_rewards\"" + ]; +} + +// ValidatorAccumulatedCommissionRecord is used for import / export via genesis +// json. +message ValidatorAccumulatedCommissionRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // accumulated is the accumulated commission of a validator. + ValidatorAccumulatedCommission accumulated = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"accumulated\""]; +} + +// ValidatorHistoricalRewardsRecord is used for import / export via genesis +// json. +message ValidatorHistoricalRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // period defines the period the historical rewards apply to. + uint64 period = 2; + + // rewards defines the historical rewards of a validator. + ValidatorHistoricalRewards rewards = 3 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"rewards\""]; +} + +// ValidatorCurrentRewardsRecord is used for import / export via genesis json. +message ValidatorCurrentRewardsRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // rewards defines the current rewards of a validator. + ValidatorCurrentRewards rewards = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"rewards\""]; +} + +// DelegatorStartingInfoRecord used for import / export via genesis json. +message DelegatorStartingInfoRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address is the address of the delegator. + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + + // validator_address is the address of the validator. + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + + // starting_info defines the starting info of a delegator. + DelegatorStartingInfo starting_info = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"starting_info\""]; +} + +// ValidatorSlashEventRecord is used for import / export via genesis json. +message ValidatorSlashEventRecord { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validator_address is the address of the validator. + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + // height defines the block height at which the slash event occured. + uint64 height = 2; + // period is the period of the slash event. + uint64 period = 3; + // validator_slash_event describes the slash event. + ValidatorSlashEvent validator_slash_event = 4 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"event\""]; +} + +// GenesisState defines the distribution module's genesis state. +message GenesisState { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // params defines all the paramaters of the module. + Params params = 1 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"params\""]; + + // fee_pool defines the fee pool at genesis. + FeePool fee_pool = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"fee_pool\""]; + + // fee_pool defines the delegator withdraw infos at genesis. + repeated DelegatorWithdrawInfo delegator_withdraw_infos = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"delegator_withdraw_infos\""]; + + // fee_pool defines the previous proposer at genesis. + string previous_proposer = 4 [(gogoproto.moretags) = "yaml:\"previous_proposer\""]; + + // fee_pool defines the outstanding rewards of all validators at genesis. + repeated ValidatorOutstandingRewardsRecord outstanding_rewards = 5 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"outstanding_rewards\""]; + + // fee_pool defines the accumulated commisions of all validators at genesis. + repeated ValidatorAccumulatedCommissionRecord validator_accumulated_commissions = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_accumulated_commissions\""]; + + // fee_pool defines the historical rewards of all validators at genesis. + repeated ValidatorHistoricalRewardsRecord validator_historical_rewards = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_historical_rewards\""]; + + // fee_pool defines the current rewards of all validators at genesis. + repeated ValidatorCurrentRewardsRecord validator_current_rewards = 8 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_current_rewards\""]; + + // fee_pool defines the delegator starting infos at genesis. + repeated DelegatorStartingInfoRecord delegator_starting_infos = 9 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"delegator_starting_infos\""]; + + // fee_pool defines the validator slash events at genesis. + repeated ValidatorSlashEventRecord validator_slash_events = 10 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_slash_events\""]; +} diff --git a/proto/cosmos/distribution/v1beta1/query.proto b/proto/cosmos/distribution/v1beta1/query.proto new file mode 100644 index 000000000000..2991218d8049 --- /dev/null +++ b/proto/cosmos/distribution/v1beta1/query.proto @@ -0,0 +1,218 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/distribution/v1beta1/distribution.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; + +// Query defines the gRPC querier service for distribution module. +service Query { + // Params queries params of the distribution module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/params"; + } + + // ValidatorOutstandingRewards queries rewards of a validator address. + rpc ValidatorOutstandingRewards(QueryValidatorOutstandingRewardsRequest) + returns (QueryValidatorOutstandingRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/" + "{validator_address}/outstanding_rewards"; + } + + // ValidatorCommission queries accumulated commission for a validator. + rpc ValidatorCommission(QueryValidatorCommissionRequest) returns (QueryValidatorCommissionResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/" + "{validator_address}/commission"; + } + + // ValidatorSlashes queries slash events of a validator. + rpc ValidatorSlashes(QueryValidatorSlashesRequest) returns (QueryValidatorSlashesResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/validators/{validator_address}/slashes"; + } + + // DelegationRewards queries the total rewards accrued by a delegation. + rpc DelegationRewards(QueryDelegationRewardsRequest) returns (QueryDelegationRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards/" + "{validator_address}"; + } + + // DelegationTotalRewards queries the total rewards accrued by a each + // validator. + rpc DelegationTotalRewards(QueryDelegationTotalRewardsRequest) returns (QueryDelegationTotalRewardsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/{delegator_address}/rewards"; + } + + // DelegatorValidators queries the validators of a delegator. + rpc DelegatorValidators(QueryDelegatorValidatorsRequest) returns (QueryDelegatorValidatorsResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/" + "{delegator_address}/validators"; + } + + // DelegatorWithdrawAddress queries withdraw address of a delegator. + rpc DelegatorWithdrawAddress(QueryDelegatorWithdrawAddressRequest) returns (QueryDelegatorWithdrawAddressResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/delegators/" + "{delegator_address}/withdraw_address"; + } + + // CommunityPool queries the community pool coins. + rpc CommunityPool(QueryCommunityPoolRequest) returns (QueryCommunityPoolResponse) { + option (google.api.http).get = "/cosmos/distribution/v1beta1/community_pool"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorOutstandingRewardsRequest is the request type for the +// Query/ValidatorOutstandingRewards RPC method. +message QueryValidatorOutstandingRewardsRequest { + // validator_address defines the validator address to query for. + string validator_address = 1; +} + +// QueryValidatorOutstandingRewardsResponse is the response type for the +// Query/ValidatorOutstandingRewards RPC method. +message QueryValidatorOutstandingRewardsResponse { + ValidatorOutstandingRewards rewards = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorCommissionRequest is the request type for the +// Query/ValidatorCommission RPC method +message QueryValidatorCommissionRequest { + // validator_address defines the validator address to query for. + string validator_address = 1; +} + +// QueryValidatorCommissionResponse is the response type for the +// Query/ValidatorCommission RPC method +message QueryValidatorCommissionResponse { + // commission defines the commision the validator received. + ValidatorAccumulatedCommission commission = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorSlashesRequest is the request type for the +// Query/ValidatorSlashes RPC method +message QueryValidatorSlashesRequest { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + // validator_address defines the validator address to query for. + string validator_address = 1; + // starting_height defines the optional starting height to query the slashes. + uint64 starting_height = 2; + // starting_height defines the optional ending height to query the slashes. + uint64 ending_height = 3; + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryValidatorSlashesResponse is the response type for the +// Query/ValidatorSlashes RPC method. +message QueryValidatorSlashesResponse { + // slashes defines the slashes the validator received. + repeated ValidatorSlashEvent slashes = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegationRewardsRequest is the request type for the +// Query/DelegationRewards RPC method. +message QueryDelegationRewardsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; + // validator_address defines the validator address to query for. + string validator_address = 2; +} + +// QueryDelegationRewardsResponse is the response type for the +// Query/DelegationRewards RPC method. +message QueryDelegationRewardsResponse { + // rewards defines the rewards accrued by a delegation. + repeated cosmos.base.v1beta1.DecCoin rewards = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"]; +} + +// QueryDelegationTotalRewardsRequest is the request type for the +// Query/DelegationTotalRewards RPC method. +message QueryDelegationTotalRewardsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegationTotalRewardsResponse is the response type for the +// Query/DelegationTotalRewards RPC method. +message QueryDelegationTotalRewardsResponse { + // rewards defines all the rewards accrued by a delegator. + repeated DelegationDelegatorReward rewards = 1 [(gogoproto.nullable) = false]; + // total defines the sum of all the rewards. + repeated cosmos.base.v1beta1.DecCoin total = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"]; +} + +// QueryDelegatorValidatorsRequest is the request type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegatorValidatorsResponse is the response type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // validators defines the validators a delegator is delegating for. + repeated string validators = 1; +} + +// QueryDelegatorWithdrawAddressRequest is the request type for the +// Query/DelegatorWithdrawAddress RPC method. +message QueryDelegatorWithdrawAddressRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_address defines the delegator address to query for. + string delegator_address = 1; +} + +// QueryDelegatorWithdrawAddressResponse is the response type for the +// Query/DelegatorWithdrawAddress RPC method. +message QueryDelegatorWithdrawAddressResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // withdraw_address defines the delegator address to query for. + string withdraw_address = 1; +} + +// QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC +// method. +message QueryCommunityPoolRequest {} + +// QueryCommunityPoolResponse is the response type for the Query/CommunityPool +// RPC method. +message QueryCommunityPoolResponse { + // pool defines community pool's coins. + repeated cosmos.base.v1beta1.DecCoin pool = 1 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", (gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/distribution/v1beta1/tx.proto b/proto/cosmos/distribution/v1beta1/tx.proto new file mode 100644 index 000000000000..e6ce478bcdc4 --- /dev/null +++ b/proto/cosmos/distribution/v1beta1/tx.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +// Msg defines the distribution Msg service. +service Msg { + // SetWithdrawAddress defines a method to change the withdraw address + // for a delegator (or validator self-delegation). + rpc SetWithdrawAddress(MsgSetWithdrawAddress) returns (MsgSetWithdrawAddressResponse); + + // WithdrawDelegatorReward defines a method to withdraw rewards of delegator + // from a single validator. + rpc WithdrawDelegatorReward(MsgWithdrawDelegatorReward) returns (MsgWithdrawDelegatorRewardResponse); + + // WithdrawValidatorCommission defines a method to withdraw the + // full commission to the validator address. + rpc WithdrawValidatorCommission(MsgWithdrawValidatorCommission) returns (MsgWithdrawValidatorCommissionResponse); + + // FundCommunityPool defines a method to allow an account to directly + // fund the community pool. + rpc FundCommunityPool(MsgFundCommunityPool) returns (MsgFundCommunityPoolResponse); +} + +// MsgSetWithdrawAddress sets the withdraw address for +// a delegator (or validator self-delegation). +message MsgSetWithdrawAddress { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string withdraw_address = 2 [(gogoproto.moretags) = "yaml:\"withdraw_address\""]; +} + +// MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. +message MsgSetWithdrawAddressResponse {} + +// MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +// from a single validator. +message MsgWithdrawDelegatorReward { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. +message MsgWithdrawDelegatorRewardResponse {} + +// MsgWithdrawValidatorCommission withdraws the full commission to the validator +// address. +message MsgWithdrawValidatorCommission { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string validator_address = 1 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. +message MsgWithdrawValidatorCommissionResponse {} + +// MsgFundCommunityPool allows an account to directly +// fund the community pool. +message MsgFundCommunityPool { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + repeated cosmos.base.v1beta1.Coin amount = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + string depositor = 2; +} + +// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type. +message MsgFundCommunityPoolResponse {} diff --git a/proto/cosmos/evidence/v1beta1/evidence.proto b/proto/cosmos/evidence/v1beta1/evidence.proto new file mode 100644 index 000000000000..14612c314f01 --- /dev/null +++ b/proto/cosmos/evidence/v1beta1/evidence.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; + +// Equivocation implements the Evidence interface and defines evidence of double +// signing misbehavior. +message Equivocation { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + int64 height = 1; + google.protobuf.Timestamp time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + int64 power = 3; + string consensus_address = 4 [(gogoproto.moretags) = "yaml:\"consensus_address\""]; +} \ No newline at end of file diff --git a/proto/cosmos/evidence/v1beta1/genesis.proto b/proto/cosmos/evidence/v1beta1/genesis.proto new file mode 100644 index 000000000000..199f446f7e6c --- /dev/null +++ b/proto/cosmos/evidence/v1beta1/genesis.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; + +import "google/protobuf/any.proto"; + +// GenesisState defines the evidence module's genesis state. +message GenesisState { + // evidence defines all the evidence at genesis. + repeated google.protobuf.Any evidence = 1; +} diff --git a/proto/cosmos/evidence/v1beta1/query.proto b/proto/cosmos/evidence/v1beta1/query.proto new file mode 100644 index 000000000000..eda00544c725 --- /dev/null +++ b/proto/cosmos/evidence/v1beta1/query.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; + +// Query defines the gRPC querier service. +service Query { + // Evidence queries evidence based on evidence hash. + rpc Evidence(QueryEvidenceRequest) returns (QueryEvidenceResponse) { + option (google.api.http).get = "/cosmos/evidence/v1beta1/evidence/{evidence_hash}"; + } + + // AllEvidence queries all evidence. + rpc AllEvidence(QueryAllEvidenceRequest) returns (QueryAllEvidenceResponse) { + option (google.api.http).get = "/cosmos/evidence/v1beta1/evidence"; + } +} + +// QueryEvidenceRequest is the request type for the Query/Evidence RPC method. +message QueryEvidenceRequest { + // evidence_hash defines the hash of the requested evidence. + bytes evidence_hash = 1 [(gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes"]; +} + +// QueryEvidenceResponse is the response type for the Query/Evidence RPC method. +message QueryEvidenceResponse { + // evidence returns the requested evidence. + google.protobuf.Any evidence = 1; +} + +// QueryEvidenceRequest is the request type for the Query/AllEvidence RPC +// method. +message QueryAllEvidenceRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC +// method. +message QueryAllEvidenceResponse { + // evidence returns all evidences. + repeated google.protobuf.Any evidence = 1; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/cosmos/evidence/v1beta1/tx.proto b/proto/cosmos/evidence/v1beta1/tx.proto new file mode 100644 index 000000000000..38795f25d4e3 --- /dev/null +++ b/proto/cosmos/evidence/v1beta1/tx.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package cosmos.evidence.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/evidence/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos_proto/cosmos.proto"; + +// Msg defines the evidence Msg service. +service Msg { + // SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or + // counterfactual signing. + rpc SubmitEvidence(MsgSubmitEvidence) returns (MsgSubmitEvidenceResponse); +} + +// MsgSubmitEvidence represents a message that supports submitting arbitrary +// Evidence of misbehavior such as equivocation or counterfactual signing. +message MsgSubmitEvidence { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string submitter = 1; + google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; +} + +// MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. +message MsgSubmitEvidenceResponse { + // hash defines the hash of the evidence. + bytes hash = 4; +} diff --git a/proto/cosmos/genutil/v1beta1/genesis.proto b/proto/cosmos/genutil/v1beta1/genesis.proto new file mode 100644 index 000000000000..a0207793d9fc --- /dev/null +++ b/proto/cosmos/genutil/v1beta1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos.genutil.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/genutil/types"; + +// GenesisState defines the raw genesis transaction in JSON. +message GenesisState { + // gen_txs defines the genesis transactions. + repeated bytes gen_txs = 1 [ + (gogoproto.casttype) = "encoding/json.RawMessage", + (gogoproto.jsontag) = "gentxs", + (gogoproto.moretags) = "yaml:\"gentxs\"" + ]; +} diff --git a/proto/cosmos/gov/v1beta1/genesis.proto b/proto/cosmos/gov/v1beta1/genesis.proto new file mode 100644 index 000000000000..a999500449fa --- /dev/null +++ b/proto/cosmos/gov/v1beta1/genesis.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package cosmos.gov.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/gov/v1beta1/gov.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// GenesisState defines the gov module's genesis state. +message GenesisState { + // starting_proposal_id is the ID of the starting proposal. + uint64 starting_proposal_id = 1 [(gogoproto.moretags) = "yaml:\"starting_proposal_id\""]; + // deposits defines all the deposits present at genesis. + repeated Deposit deposits = 2 [(gogoproto.castrepeated) = "Deposits", (gogoproto.nullable) = false]; + // votes defines all the votes present at genesis. + repeated Vote votes = 3 [(gogoproto.castrepeated) = "Votes", (gogoproto.nullable) = false]; + // proposals defines all the proposals present at genesis. + repeated Proposal proposals = 4 [(gogoproto.castrepeated) = "Proposals", (gogoproto.nullable) = false]; + // params defines all the paramaters of related to deposit. + DepositParams deposit_params = 5 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"deposit_params\""]; + // params defines all the paramaters of related to voting. + VotingParams voting_params = 6 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_params\""]; + // params defines all the paramaters of related to tally. + TallyParams tally_params = 7 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"tally_params\""]; +} diff --git a/proto/cosmos/gov/v1beta1/gov.proto b/proto/cosmos/gov/v1beta1/gov.proto new file mode 100644 index 000000000000..1d72e643212c --- /dev/null +++ b/proto/cosmos/gov/v1beta1/gov.proto @@ -0,0 +1,183 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = false; +option (gogoproto.goproto_getters_all) = false; + +// VoteOption enumerates the valid vote options for a given governance proposal. +enum VoteOption { + option (gogoproto.goproto_enum_prefix) = false; + + // VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + VOTE_OPTION_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "OptionEmpty"]; + // VOTE_OPTION_YES defines a yes vote option. + VOTE_OPTION_YES = 1 [(gogoproto.enumvalue_customname) = "OptionYes"]; + // VOTE_OPTION_ABSTAIN defines an abstain vote option. + VOTE_OPTION_ABSTAIN = 2 [(gogoproto.enumvalue_customname) = "OptionAbstain"]; + // VOTE_OPTION_NO defines a no vote option. + VOTE_OPTION_NO = 3 [(gogoproto.enumvalue_customname) = "OptionNo"]; + // VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + VOTE_OPTION_NO_WITH_VETO = 4 [(gogoproto.enumvalue_customname) = "OptionNoWithVeto"]; +} + +// TextProposal defines a standard text proposal whose changes need to be +// manually updated in case of approval. +message TextProposal { + option (cosmos_proto.implements_interface) = "Content"; + + option (gogoproto.equal) = true; + + string title = 1; + string description = 2; +} + +// Deposit defines an amount deposited by an account address to an active +// proposal. +message Deposit { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + uint64 proposal_id = 1 [(gogoproto.moretags) = "yaml:\"proposal_id\""]; + string depositor = 2; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// Proposal defines the core field members of a governance proposal. +message Proposal { + option (gogoproto.equal) = true; + + uint64 proposal_id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = "yaml:\"id\""]; + google.protobuf.Any content = 2 [(cosmos_proto.accepts_interface) = "Content"]; + ProposalStatus status = 3 [(gogoproto.moretags) = "yaml:\"proposal_status\""]; + TallyResult final_tally_result = 4 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"final_tally_result\""]; + google.protobuf.Timestamp submit_time = 5 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"submit_time\""]; + google.protobuf.Timestamp deposit_end_time = 6 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"deposit_end_time\""]; + repeated cosmos.base.v1beta1.Coin total_deposit = 7 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"total_deposit\"" + ]; + google.protobuf.Timestamp voting_start_time = 8 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_start_time\""]; + google.protobuf.Timestamp voting_end_time = 9 + [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voting_end_time\""]; +} + +// ProposalStatus enumerates the valid statuses of a proposal. +enum ProposalStatus { + option (gogoproto.goproto_enum_prefix) = false; + + // PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + PROPOSAL_STATUS_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "StatusNil"]; + // PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + // period. + PROPOSAL_STATUS_DEPOSIT_PERIOD = 1 [(gogoproto.enumvalue_customname) = "StatusDepositPeriod"]; + // PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + // period. + PROPOSAL_STATUS_VOTING_PERIOD = 2 [(gogoproto.enumvalue_customname) = "StatusVotingPeriod"]; + // PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + // passed. + PROPOSAL_STATUS_PASSED = 3 [(gogoproto.enumvalue_customname) = "StatusPassed"]; + // PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + // been rejected. + PROPOSAL_STATUS_REJECTED = 4 [(gogoproto.enumvalue_customname) = "StatusRejected"]; + // PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + // failed. + PROPOSAL_STATUS_FAILED = 5 [(gogoproto.enumvalue_customname) = "StatusFailed"]; +} + +// TallyResult defines a standard tally for a governance proposal. +message TallyResult { + option (gogoproto.equal) = true; + + string yes = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string abstain = 2 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string no = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; + string no_with_veto = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"no_with_veto\"" + ]; +} + +// Vote defines a vote on a governance proposal. +// A Vote consists of a proposal ID, the voter, and the vote option. +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; +} + +// DepositParams defines the params for deposits on governance proposals. +message DepositParams { + // Minimum deposit for a proposal to enter voting period. + repeated cosmos.base.v1beta1.Coin min_deposit = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"min_deposit\"", + (gogoproto.jsontag) = "min_deposit,omitempty" + ]; + + // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + // months. + google.protobuf.Duration max_deposit_period = 2 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.jsontag) = "max_deposit_period,omitempty", + (gogoproto.moretags) = "yaml:\"max_deposit_period\"" + ]; +} + +// VotingParams defines the params for voting on governance proposals. +message VotingParams { + // Length of the voting period. + google.protobuf.Duration voting_period = 1 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.jsontag) = "voting_period,omitempty", + (gogoproto.moretags) = "yaml:\"voting_period\"" + ]; +} + +// TallyParams defines the params for tallying votes on governance proposals. +message TallyParams { + // Minimum percentage of total stake needed to vote for a result to be + // considered valid. + bytes quorum = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "quorum,omitempty" + ]; + + // Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + bytes threshold = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "threshold,omitempty" + ]; + + // Minimum value of Veto votes to Total votes ratio for proposal to be + // vetoed. Default value: 1/3. + bytes veto_threshold = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "veto_threshold,omitempty", + (gogoproto.moretags) = "yaml:\"veto_threshold\"" + ]; +} diff --git a/proto/cosmos/gov/v1beta1/query.proto b/proto/cosmos/gov/v1beta1/query.proto new file mode 100644 index 000000000000..da62bdbad16b --- /dev/null +++ b/proto/cosmos/gov/v1beta1/query.proto @@ -0,0 +1,190 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/gov/v1beta1/gov.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// Query defines the gRPC querier service for gov module +service Query { + // Proposal queries proposal details based on ProposalID. + rpc Proposal(QueryProposalRequest) returns (QueryProposalResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}"; + } + + // Proposals queries all proposals based on given status. + rpc Proposals(QueryProposalsRequest) returns (QueryProposalsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals"; + } + + // Vote queries voted information based on proposalID, voterAddr. + rpc Vote(QueryVoteRequest) returns (QueryVoteResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}"; + } + + // Votes queries votes of a given proposal. + rpc Votes(QueryVotesRequest) returns (QueryVotesResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/votes"; + } + + // Params queries all parameters of the gov module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/params/{params_type}"; + } + + // Deposit queries single deposit information based proposalID, depositAddr. + rpc Deposit(QueryDepositRequest) returns (QueryDepositResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}"; + } + + // Deposits queries all deposits of a single proposal. + rpc Deposits(QueryDepositsRequest) returns (QueryDepositsResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/deposits"; + } + + // TallyResult queries the tally of a proposal vote. + rpc TallyResult(QueryTallyResultRequest) returns (QueryTallyResultResponse) { + option (google.api.http).get = "/cosmos/gov/v1beta1/proposals/{proposal_id}/tally"; + } +} + +// QueryProposalRequest is the request type for the Query/Proposal RPC method. +message QueryProposalRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; +} + +// QueryProposalResponse is the response type for the Query/Proposal RPC method. +message QueryProposalResponse { + Proposal proposal = 1 [(gogoproto.nullable) = false]; +} + +// QueryProposalsRequest is the request type for the Query/Proposals RPC method. +message QueryProposalsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // proposal_status defines the status of the proposals. + ProposalStatus proposal_status = 1; + + // voter defines the voter address for the proposals. + string voter = 2; + + // depositor defines the deposit addresses from the proposals. + string depositor = 3; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryProposalsResponse is the response type for the Query/Proposals RPC +// method. +message QueryProposalsResponse { + repeated Proposal proposals = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryVoteRequest is the request type for the Query/Vote RPC method. +message QueryVoteRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // voter defines the oter address for the proposals. + string voter = 2; +} + +// QueryVoteResponse is the response type for the Query/Vote RPC method. +message QueryVoteResponse { + // vote defined the queried vote. + Vote vote = 1 [(gogoproto.nullable) = false]; +} + +// QueryVotesRequest is the request type for the Query/Votes RPC method. +message QueryVotesRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryVotesResponse is the response type for the Query/Votes RPC method. +message QueryVotesResponse { + // votes defined the queried votes. + repeated Vote votes = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest { + // params_type defines which parameters to query for, can be one of "voting", + // "tallying" or "deposit". + string params_type = 1; +} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // voting_params defines the parameters related to voting. + VotingParams voting_params = 1 [(gogoproto.nullable) = false]; + // deposit_params defines the parameters related to deposit. + DepositParams deposit_params = 2 [(gogoproto.nullable) = false]; + // tally_params defines the parameters related to tally. + TallyParams tally_params = 3 [(gogoproto.nullable) = false]; +} + +// QueryDepositRequest is the request type for the Query/Deposit RPC method. +message QueryDepositRequest { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // depositor defines the deposit addresses from the proposals. + string depositor = 2; +} + +// QueryDepositResponse is the response type for the Query/Deposit RPC method. +message QueryDepositResponse { + // deposit defines the requested deposit. + Deposit deposit = 1 [(gogoproto.nullable) = false]; +} + +// QueryDepositsRequest is the request type for the Query/Deposits RPC method. +message QueryDepositsRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDepositsResponse is the response type for the Query/Deposits RPC method. +message QueryDepositsResponse { + repeated Deposit deposits = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryTallyResultRequest is the request type for the Query/Tally RPC method. +message QueryTallyResultRequest { + // proposal_id defines the unique id of the proposal. + uint64 proposal_id = 1; +} + +// QueryTallyResultResponse is the response type for the Query/Tally RPC method. +message QueryTallyResultResponse { + // tally defines the requested tally. + TallyResult tally = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/gov/v1beta1/tx.proto b/proto/cosmos/gov/v1beta1/tx.proto new file mode 100644 index 000000000000..d4f0c1f99a1d --- /dev/null +++ b/proto/cosmos/gov/v1beta1/tx.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; +package cosmos.gov.v1beta1; + +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/gov/v1beta1/gov.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/gov/types"; + +// Msg defines the bank Msg service. +service Msg { + // SubmitProposal defines a method to create new proposal given a content. + rpc SubmitProposal(MsgSubmitProposal) returns (MsgSubmitProposalResponse); + + // Vote defines a method to add a vote on a specific proposal. + rpc Vote(MsgVote) returns (MsgVoteResponse); + + // Deposit defines a method to add deposit on a specific proposal. + rpc Deposit(MsgDeposit) returns (MsgDepositResponse); +} + +// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +// proposal Content. +message MsgSubmitProposal { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + option (gogoproto.goproto_getters) = false; + + google.protobuf.Any content = 1 [(cosmos_proto.accepts_interface) = "Content"]; + repeated cosmos.base.v1beta1.Coin initial_deposit = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"initial_deposit\"" + ]; + string proposer = 3; +} + +// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. +message MsgSubmitProposalResponse { + uint64 proposal_id = 1 [(gogoproto.jsontag) = "proposal_id", (gogoproto.moretags) = "yaml:\"proposal_id\""]; +} + +// MsgVote defines a message to cast a vote. +message MsgVote { + 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; + VoteOption option = 3; +} + +// MsgVoteResponse defines the Msg/Vote response type. +message MsgVoteResponse {} + +// MsgDeposit defines a message to submit a deposit to an existing proposal. +message MsgDeposit { + 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 depositor = 2; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// MsgDepositResponse defines the Msg/Deposit response type. +message MsgDepositResponse {} diff --git a/proto/cosmos/mint/v1beta1/genesis.proto b/proto/cosmos/mint/v1beta1/genesis.proto new file mode 100644 index 000000000000..4e783fb5448a --- /dev/null +++ b/proto/cosmos/mint/v1beta1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/mint/v1beta1/mint.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +// GenesisState defines the mint module's genesis state. +message GenesisState { + // minter is a space for holding current inflation information. + Minter minter = 1 [(gogoproto.nullable) = false]; + + // params defines all the paramaters of the module. + Params params = 2 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/mint/v1beta1/mint.proto b/proto/cosmos/mint/v1beta1/mint.proto new file mode 100644 index 000000000000..a119b5f5162c --- /dev/null +++ b/proto/cosmos/mint/v1beta1/mint.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +import "gogoproto/gogo.proto"; + +// Minter represents the minting state. +message Minter { + // current annual inflation rate + string inflation = 1 + [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; + // current annual expected provisions + string annual_provisions = 2 [ + (gogoproto.moretags) = "yaml:\"annual_provisions\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +// Params holds parameters for the mint module. +message Params { + option (gogoproto.goproto_stringer) = false; + + // type of coin to mint + string mint_denom = 1; + // maximum annual change in inflation rate + string inflation_rate = 2 [ + (gogoproto.moretags) = "yaml:\"inflation_rate\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + // expected blocks per year + uint64 blocks_per_year = 6 [(gogoproto.moretags) = "yaml:\"blocks_per_year\""]; +} diff --git a/proto/cosmos/mint/v1beta1/query.proto b/proto/cosmos/mint/v1beta1/query.proto new file mode 100644 index 000000000000..acd341d777d5 --- /dev/null +++ b/proto/cosmos/mint/v1beta1/query.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; +package cosmos.mint.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/mint/v1beta1/mint.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/mint/types"; + +// Query provides defines the gRPC querier service. +service Query { + // Params returns the total set of minting parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/params"; + } + + // Inflation returns the current minting inflation value. + rpc Inflation(QueryInflationRequest) returns (QueryInflationResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/inflation"; + } + + // AnnualProvisions current minting annual provisions value. + rpc AnnualProvisions(QueryAnnualProvisionsRequest) returns (QueryAnnualProvisionsResponse) { + option (google.api.http).get = "/cosmos/mint/v1beta1/annual_provisions"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QueryInflationRequest is the request type for the Query/Inflation RPC method. +message QueryInflationRequest {} + +// QueryInflationResponse is the response type for the Query/Inflation RPC +// method. +message QueryInflationResponse { + // inflation is the current minting inflation value. + bytes inflation = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} + +// QueryAnnualProvisionsRequest is the request type for the +// Query/AnnualProvisions RPC method. +message QueryAnnualProvisionsRequest {} + +// QueryAnnualProvisionsResponse is the response type for the +// Query/AnnualProvisions RPC method. +message QueryAnnualProvisionsResponse { + // annual_provisions is the current minting annual provisions value. + bytes annual_provisions = 1 + [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/params/v1beta1/params.proto b/proto/cosmos/params/v1beta1/params.proto new file mode 100644 index 000000000000..5382fd799964 --- /dev/null +++ b/proto/cosmos/params/v1beta1/params.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package cosmos.params.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/params/types/proposal"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; + +// ParameterChangeProposal defines a proposal to change one or more parameters. +message ParameterChangeProposal { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string title = 1; + string description = 2; + repeated ParamChange changes = 3 [(gogoproto.nullable) = false]; +} + +// ParamChange defines an individual parameter change, for use in +// ParameterChangeProposal. +message ParamChange { + option (gogoproto.goproto_stringer) = false; + + string subspace = 1; + string key = 2; + string value = 3; +} diff --git a/proto/cosmos/params/v1beta1/query.proto b/proto/cosmos/params/v1beta1/query.proto new file mode 100644 index 000000000000..1078e02ae3e6 --- /dev/null +++ b/proto/cosmos/params/v1beta1/query.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package cosmos.params.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/params/v1beta1/params.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/params/types/proposal"; + +// Query defines the gRPC querier service. +service Query { + // Params queries a specific parameter of a module, given its subspace and + // key. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/params/v1beta1/params"; + } +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest { + // subspace defines the module to query the parameter for. + string subspace = 1; + + // key defines the key of the parameter in the subspace. + string key = 2; +} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // param defines the queried parameter. + ParamChange param = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/slashing/v1beta1/genesis.proto b/proto/cosmos/slashing/v1beta1/genesis.proto new file mode 100644 index 000000000000..c81356134337 --- /dev/null +++ b/proto/cosmos/slashing/v1beta1/genesis.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/slashing/v1beta1/slashing.proto"; + +// GenesisState defines the slashing module's genesis state. +message GenesisState { + // params defines all the paramaters of related to deposit. + Params params = 1 [(gogoproto.nullable) = false]; + + // signing_infos represents a map between validator addresses and their + // signing infos. + repeated SigningInfo signing_infos = 2 + [(gogoproto.moretags) = "yaml:\"signing_infos\"", (gogoproto.nullable) = false]; + + // signing_infos represents a map between validator addresses and their + // missed blocks. + repeated ValidatorMissedBlocks missed_blocks = 3 + [(gogoproto.moretags) = "yaml:\"missed_blocks\"", (gogoproto.nullable) = false]; +} + +// SigningInfo stores validator signing info of corresponding address. +message SigningInfo { + // address is the validator address. + string address = 1; + // validator_signing_info represents the signing info of this validator. + ValidatorSigningInfo validator_signing_info = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator_signing_info\""]; +} + +// ValidatorMissedBlocks contains array of missed blocks of corresponding +// address. +message ValidatorMissedBlocks { + // address is the validator address. + string address = 1; + // missed_blocks is an array of missed blocks by the validator. + repeated MissedBlock missed_blocks = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"missed_blocks\""]; +} + +// MissedBlock contains height and missed status as boolean. +message MissedBlock { + // index is the height at which the block was missed. + int64 index = 1; + // missed is the missed status. + bool missed = 2; +} diff --git a/proto/cosmos/slashing/v1beta1/query.proto b/proto/cosmos/slashing/v1beta1/query.proto new file mode 100644 index 000000000000..869049a0edfd --- /dev/null +++ b/proto/cosmos/slashing/v1beta1/query.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/slashing/v1beta1/slashing.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; + +// Query provides defines the gRPC querier service +service Query { + // Params queries the parameters of slashing module + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/params"; + } + + // SigningInfo queries the signing info of given cons address + rpc SigningInfo(QuerySigningInfoRequest) returns (QuerySigningInfoResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/signing_infos/{cons_address}"; + } + + // SigningInfos queries signing info of all validators + rpc SigningInfos(QuerySigningInfosRequest) returns (QuerySigningInfosResponse) { + option (google.api.http).get = "/cosmos/slashing/v1beta1/signing_infos"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} + +// QuerySigningInfoRequest is the request type for the Query/SigningInfo RPC +// method +message QuerySigningInfoRequest { + // cons_address is the address to query signing info of + string cons_address = 1; +} + +// QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC +// method +message QuerySigningInfoResponse { + // val_signing_info is the signing info of requested val cons address + ValidatorSigningInfo val_signing_info = 1 [(gogoproto.nullable) = false]; +} + +// QuerySigningInfosRequest is the request type for the Query/SigningInfos RPC +// method +message QuerySigningInfosRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC +// method +message QuerySigningInfosResponse { + // info is the signing info of all validators + repeated cosmos.slashing.v1beta1.ValidatorSigningInfo info = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/cosmos/slashing/v1beta1/slashing.proto b/proto/cosmos/slashing/v1beta1/slashing.proto new file mode 100644 index 000000000000..657a90f1bd05 --- /dev/null +++ b/proto/cosmos/slashing/v1beta1/slashing.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// ValidatorSigningInfo defines a validator's signing info for monitoring their +// liveness activity. +message ValidatorSigningInfo { + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + string address = 1; + // 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 + int64 index_offset = 3 [(gogoproto.moretags) = "yaml:\"index_offset\""]; + // timestamp validator cannot be unjailed until + 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) + bool tombstoned = 5; + // missed blocks counter (to avoid scanning the array every time) + int64 missed_blocks_counter = 6 [(gogoproto.moretags) = "yaml:\"missed_blocks_counter\""]; +} + +// Params represents the parameters used for by the slashing module. +message Params { + int64 signed_blocks_window = 1 [(gogoproto.moretags) = "yaml:\"signed_blocks_window\""]; + bytes min_signed_per_window = 2 [ + (gogoproto.moretags) = "yaml:\"min_signed_per_window\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + google.protobuf.Duration downtime_jail_duration = 3 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"downtime_jail_duration\"" + ]; + bytes slash_fraction_double_sign = 4 [ + (gogoproto.moretags) = "yaml:\"slash_fraction_double_sign\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + bytes slash_fraction_downtime = 5 [ + (gogoproto.moretags) = "yaml:\"slash_fraction_downtime\"", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} diff --git a/proto/cosmos/slashing/v1beta1/tx.proto b/proto/cosmos/slashing/v1beta1/tx.proto new file mode 100644 index 000000000000..4d63370ecca8 --- /dev/null +++ b/proto/cosmos/slashing/v1beta1/tx.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; +package cosmos.slashing.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/slashing/types"; +option (gogoproto.equal_all) = true; + +import "gogoproto/gogo.proto"; + +// Msg defines the slashing Msg service. +service Msg { + // Unjail defines a method for unjailing a jailed validator, thus returning + // them into the bonded validator set, so they can begin receiving provisions + // and rewards again. + rpc Unjail(MsgUnjail) returns (MsgUnjailResponse); +} + +// MsgUnjail defines the Msg/Unjail request type +message MsgUnjail { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = true; + + string validator_addr = 1 [(gogoproto.moretags) = "yaml:\"address\"", (gogoproto.jsontag) = "address"]; +} + +// MsgUnjailResponse defines the Msg/Unjail response type +message MsgUnjailResponse {} \ No newline at end of file diff --git a/proto/cosmos/staking/v1beta1/genesis.proto b/proto/cosmos/staking/v1beta1/genesis.proto new file mode 100644 index 000000000000..d1563dbc5470 --- /dev/null +++ b/proto/cosmos/staking/v1beta1/genesis.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +// GenesisState defines the staking module's genesis state. +message GenesisState { + // params defines all the paramaters of related to deposit. + Params params = 1 [(gogoproto.nullable) = false]; + + // last_total_power tracks the total amounts of bonded tokens recorded during + // the previous end block. + bytes last_total_power = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"last_total_power\"", + (gogoproto.nullable) = false + ]; + + // last_validator_powers is a special index that provides a historical list + // of the last-block's bonded validators. + repeated LastValidatorPower last_validator_powers = 3 + [(gogoproto.moretags) = "yaml:\"last_validator_powers\"", (gogoproto.nullable) = false]; + + // delegations defines the validator set at genesis. + repeated Validator validators = 4 [(gogoproto.nullable) = false]; + + // delegations defines the delegations active at genesis. + repeated Delegation delegations = 5 [(gogoproto.nullable) = false]; + + // unbonding_delegations defines the unbonding delegations active at genesis. + repeated UnbondingDelegation unbonding_delegations = 6 + [(gogoproto.moretags) = "yaml:\"unbonding_delegations\"", (gogoproto.nullable) = false]; + + // redelegations defines the redelegations active at genesis. + repeated Redelegation redelegations = 7 [(gogoproto.nullable) = false]; + + bool exported = 8; +} + +// LastValidatorPower required for validator set update logic. +message LastValidatorPower { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the address of the validator. + string address = 1; + + // power defines the power of the validator. + int64 power = 2; +} diff --git a/proto/cosmos/staking/v1beta1/query.proto b/proto/cosmos/staking/v1beta1/query.proto new file mode 100644 index 000000000000..4852c5353584 --- /dev/null +++ b/proto/cosmos/staking/v1beta1/query.proto @@ -0,0 +1,348 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// Query defines the gRPC querier service. +service Query { + // Validators queries all validators that match the given status. + rpc Validators(QueryValidatorsRequest) returns (QueryValidatorsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators"; + } + + // Validator queries validator info for given validator address. + rpc Validator(QueryValidatorRequest) returns (QueryValidatorResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}"; + } + + // ValidatorDelegations queries delegate info for given validator. + rpc ValidatorDelegations(QueryValidatorDelegationsRequest) returns (QueryValidatorDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations"; + } + + // ValidatorUnbondingDelegations queries unbonding delegations of a validator. + rpc ValidatorUnbondingDelegations(QueryValidatorUnbondingDelegationsRequest) + returns (QueryValidatorUnbondingDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/" + "{validator_addr}/unbonding_delegations"; + } + + // Delegation queries delegate info for given validator delegator pair. + rpc Delegation(QueryDelegationRequest) returns (QueryDelegationResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/" + "{delegator_addr}"; + } + + // UnbondingDelegation queries unbonding info for given validator delegator + // pair. + rpc UnbondingDelegation(QueryUnbondingDelegationRequest) returns (QueryUnbondingDelegationResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/validators/{validator_addr}/delegations/" + "{delegator_addr}/unbonding_delegation"; + } + + // DelegatorDelegations queries all delegations of a given delegator address. + rpc DelegatorDelegations(QueryDelegatorDelegationsRequest) returns (QueryDelegatorDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegations/{delegator_addr}"; + } + + // DelegatorUnbondingDelegations queries all unbonding delegations of a given + // delegator address. + rpc DelegatorUnbondingDelegations(QueryDelegatorUnbondingDelegationsRequest) + returns (QueryDelegatorUnbondingDelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/" + "{delegator_addr}/unbonding_delegations"; + } + + // Redelegations queries redelegations of given address. + rpc Redelegations(QueryRedelegationsRequest) returns (QueryRedelegationsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations"; + } + + // DelegatorValidators queries all validators info for given delegator + // address. + rpc DelegatorValidators(QueryDelegatorValidatorsRequest) returns (QueryDelegatorValidatorsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators"; + } + + // DelegatorValidator queries validator info for given delegator validator + // pair. + rpc DelegatorValidator(QueryDelegatorValidatorRequest) returns (QueryDelegatorValidatorResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/" + "{validator_addr}"; + } + + // HistoricalInfo queries the historical info for given height. + rpc HistoricalInfo(QueryHistoricalInfoRequest) returns (QueryHistoricalInfoResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/historical_info/{height}"; + } + + // Pool queries the pool info. + rpc Pool(QueryPoolRequest) returns (QueryPoolResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/pool"; + } + + // Parameters queries the staking parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/cosmos/staking/v1beta1/params"; + } +} + +// QueryValidatorsRequest is request type for Query/Validators RPC method. +message QueryValidatorsRequest { + // status enables to query for validators matching a given status. + string status = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorsResponse is response type for the Query/Validators RPC method +message QueryValidatorsResponse { + // validators contains all the queried validators. + repeated Validator validators = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryValidatorRequest is response type for the Query/Validator RPC method +message QueryValidatorRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; +} + +// QueryValidatorResponse is response type for the Query/Validator RPC method +message QueryValidatorResponse { + // validator defines the the validator info. + Validator validator = 1 [(gogoproto.nullable) = false]; +} + +// QueryValidatorDelegationsRequest is request type for the +// Query/ValidatorDelegations RPC method +message QueryValidatorDelegationsRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorDelegationsResponse is response type for the +// Query/ValidatorDelegations RPC method +message QueryValidatorDelegationsResponse { + repeated DelegationResponse delegation_responses = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "DelegationResponses"]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryValidatorUnbondingDelegationsRequest is required type for the +// Query/ValidatorUnbondingDelegations RPC method +message QueryValidatorUnbondingDelegationsRequest { + // validator_addr defines the validator address to query for. + string validator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryValidatorUnbondingDelegationsResponse is response type for the +// Query/ValidatorUnbondingDelegations RPC method. +message QueryValidatorUnbondingDelegationsResponse { + repeated UnbondingDelegation unbonding_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegationRequest is request type for the Query/Delegation RPC method. +message QueryDelegationRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegationResponse is response type for the Query/Delegation RPC method. +message QueryDelegationResponse { + // delegation_responses defines the delegation info of a delegation. + DelegationResponse delegation_response = 1; +} + +// QueryUnbondingDelegationRequest is request type for the +// Query/UnbondingDelegation RPC method. +message QueryUnbondingDelegationRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegationResponse is response type for the Query/UnbondingDelegation +// RPC method. +message QueryUnbondingDelegationResponse { + // unbond defines the unbonding information of a delegation. + UnbondingDelegation unbond = 1 [(gogoproto.nullable) = false]; +} + +// QueryDelegatorDelegationsRequest is request type for the +// Query/DelegatorDelegations RPC method. +message QueryDelegatorDelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDelegatorDelegationsResponse is response type for the +// Query/DelegatorDelegations RPC method. +message QueryDelegatorDelegationsResponse { + // delegation_responses defines all the delegations' info of a delegator. + repeated DelegationResponse delegation_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorUnbondingDelegationsRequest is request type for the +// Query/DelegatorUnbondingDelegations RPC method. +message QueryDelegatorUnbondingDelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryUnbondingDelegatorDelegationsResponse is response type for the +// Query/UnbondingDelegatorDelegations RPC method. +message QueryDelegatorUnbondingDelegationsResponse { + repeated UnbondingDelegation unbonding_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryRedelegationsRequest is request type for the Query/Redelegations RPC +// method. +message QueryRedelegationsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // src_validator_addr defines the validator address to redelegate from. + string src_validator_addr = 2; + + // dst_validator_addr defines the validator address to redelegate to. + string dst_validator_addr = 3; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 4; +} + +// QueryRedelegationsResponse is response type for the Query/Redelegations RPC +// method. +message QueryRedelegationsResponse { + repeated RedelegationResponse redelegation_responses = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorValidatorsRequest is request type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryDelegatorValidatorsResponse is response type for the +// Query/DelegatorValidators RPC method. +message QueryDelegatorValidatorsResponse { + // validators defines the the validators' info of a delegator. + repeated Validator validators = 1 [(gogoproto.nullable) = false]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryDelegatorValidatorRequest is request type for the +// Query/DelegatorValidator RPC method. +message QueryDelegatorValidatorRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // delegator_addr defines the delegator address to query for. + string delegator_addr = 1; + + // validator_addr defines the validator address to query for. + string validator_addr = 2; +} + +// QueryDelegatorValidatorResponse response type for the +// Query/DelegatorValidator RPC method. +message QueryDelegatorValidatorResponse { + // validator defines the the validator info. + Validator validator = 1 [(gogoproto.nullable) = false]; +} + +// QueryHistoricalInfoRequest is request type for the Query/HistoricalInfo RPC +// method. +message QueryHistoricalInfoRequest { + // height defines at which height to query the historical info. + int64 height = 1; +} + +// QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC +// method. +message QueryHistoricalInfoResponse { + // hist defines the historical info at the given height. + HistoricalInfo hist = 1; +} + +// QueryPoolRequest is request type for the Query/Pool RPC method. +message QueryPoolRequest {} + +// QueryPoolResponse is response type for the Query/Pool RPC method. +message QueryPoolResponse { + // pool defines the pool info. + Pool pool = 1 [(gogoproto.nullable) = false]; +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is response type for the Query/Params RPC method. +message QueryParamsResponse { + // params holds all the parameters of this module. + Params params = 1 [(gogoproto.nullable) = false]; +} diff --git a/proto/cosmos/staking/v1beta1/staking.proto b/proto/cosmos/staking/v1beta1/staking.proto new file mode 100644 index 000000000000..e37b28b6d098 --- /dev/null +++ b/proto/cosmos/staking/v1beta1/staking.proto @@ -0,0 +1,334 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "tendermint/types/types.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// HistoricalInfo contains header and validator information for a given block. +// It is stored as part of staking module's state, which persists the `n` most +// recent HistoricalInfo +// (`n` is set by the staking module's `historical_entries` parameter). +message HistoricalInfo { + tendermint.types.Header header = 1 [(gogoproto.nullable) = false]; + repeated Validator valset = 2 [(gogoproto.nullable) = false]; +} + +// CommissionRates defines the initial commission rates to be used for creating +// a validator. +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", + (gogoproto.nullable) = false + ]; +} + +// Commission defines commission parameters for a given validator. +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\""]; +} + +// Description defines a validator description. +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; +} + +// Validator defines a validator, together with the total amount of the +// Validator's bond shares and their exchange rate to coins. Slashing results in +// a decrease in the exchange rate, allowing correct calculation of future +// undelegations without iterating over delegators. When coins are delegated to +// this validator, the validator is credited with a delegation whose number of +// bond shares is based on the amount of coins delegated divided by the current +// exchange rate. Voting power can be calculated as total bonded shares +// multiplied by exchange rate. +message Validator { + option (gogoproto.equal) = false; + 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", + (gogoproto.nullable) = false + ]; +} + +// BondStatus is the status of a validator. +enum BondStatus { + option (gogoproto.goproto_enum_prefix) = false; + + // UNSPECIFIED defines an invalid validator status. + BOND_STATUS_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "Unspecified"]; + // UNBONDED defines a validator that is not bonded. + BOND_STATUS_UNBONDED = 1 [(gogoproto.enumvalue_customname) = "Unbonded"]; + // UNBONDING defines a validator that is unbonding. + BOND_STATUS_UNBONDING = 2 [(gogoproto.enumvalue_customname) = "Unbonding"]; + // BONDED defines a validator that is bonded. + BOND_STATUS_BONDED = 3 [(gogoproto.enumvalue_customname) = "Bonded"]; +} + +// ValAddresses defines a repeated set of validator addresses. +message ValAddresses { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = true; + + repeated string addresses = 1; +} + +// DVPair is struct that just has a delegator-validator pair with no other data. +// It is intended to be used as a marshalable pointer. For example, a DVPair can +// be used to construct the key to getting an UnbondingDelegation from state. +message DVPair { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; +} + +// DVPairs defines an array of DVPair objects. +message DVPairs { + repeated DVPair pairs = 1 [(gogoproto.nullable) = false]; +} + +// DVVTriplet is struct that just has a delegator-validator-validator triplet +// with no other data. It is intended to be used as a marshalable pointer. For +// example, a DVVTriplet can be used to construct the key to getting a +// Redelegation from state. +message DVVTriplet { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; +} + +// DVVTriplets defines an array of DVVTriplet objects. +message DVVTriplets { + repeated DVVTriplet triplets = 1 [(gogoproto.nullable) = false]; +} + +// Delegation represents the bond with tokens held by an account. It is +// owned by one delegator, and is associated with the voting power of one +// validator. +message Delegation { + option (gogoproto.equal) = false; + 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]; +} + +// UnbondingDelegation stores all of a single delegator's unbonding bonds +// for a single validator in an time-ordered list. +message UnbondingDelegation { + option (gogoproto.equal) = false; + 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 +} + +// UnbondingDelegationEntry defines an unbonding object with relevant metadata. +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]; +} + +// RedelegationEntry defines a redelegation object with relevant metadata. +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]; +} + +// Redelegation contains the list of a particular delegator's redelegating bonds +// from a particular source validator to a particular destination validator. +message Redelegation { + option (gogoproto.equal) = false; + 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 +} + +// Params defines the parameters for the staking module. +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\""]; +} + +// DelegationResponse is equivalent to Delegation except that it contains a +// balance in addition to shares which is more suitable for client responses. +message DelegationResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_stringer) = false; + + Delegation delegation = 1 [(gogoproto.nullable) = false]; + + cosmos.base.v1beta1.Coin balance = 2 [(gogoproto.nullable) = false]; +} + +// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +// contains a balance in addition to shares which is more suitable for client +// responses. +message RedelegationEntryResponse { + option (gogoproto.equal) = true; + + RedelegationEntry redelegation_entry = 1 [(gogoproto.nullable) = false]; + string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false]; +} + +// RedelegationResponse is equivalent to a Redelegation except that its entries +// contain a balance in addition to shares which is more suitable for client +// responses. +message RedelegationResponse { + option (gogoproto.equal) = false; + + Redelegation redelegation = 1 [(gogoproto.nullable) = false]; + repeated RedelegationEntryResponse entries = 2 [(gogoproto.nullable) = false]; +} + +// Pool is used for tracking bonded and not-bonded token supply of the bond +// denomination. +message Pool { + option (gogoproto.description) = true; + option (gogoproto.equal) = true; + string not_bonded_tokens = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.jsontag) = "not_bonded_tokens", + (gogoproto.nullable) = false + ]; + string bonded_tokens = 2 [ + (gogoproto.jsontag) = "bonded_tokens", + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"bonded_tokens\"" + ]; +} diff --git a/proto/cosmos/staking/v1beta1/tx.proto b/proto/cosmos/staking/v1beta1/tx.proto new file mode 100644 index 000000000000..7b05d89eea22 --- /dev/null +++ b/proto/cosmos/staking/v1beta1/tx.proto @@ -0,0 +1,126 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "gogoproto/gogo.proto"; + +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/staking/v1beta1/staking.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types"; + +// Msg defines the staking Msg service. +service Msg { + // CreateValidator defines a method for creating a new validator. + rpc CreateValidator(MsgCreateValidator) returns (MsgCreateValidatorResponse); + + // EditValidator defines a method for editing an existing validator. + rpc EditValidator(MsgEditValidator) returns (MsgEditValidatorResponse); + + // Delegate defines a method for performing a delegation of coins + // from a delegator to a validator. + rpc Delegate(MsgDelegate) returns (MsgDelegateResponse); + + // BeginRedelegate defines a method for performing a redelegation + // of coins from a delegator and source validator to a destination validator. + rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse); + + // Undelegate defines a method for performing an undelegation from a + // delegate and a validator. + rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse); +} + +// MsgCreateValidator defines a SDK message for creating a new validator. +message MsgCreateValidator { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Description description = 1 [(gogoproto.nullable) = false]; + CommissionRates commission = 2 [(gogoproto.nullable) = false]; + string min_self_delegation = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_self_delegation\"", + (gogoproto.nullable) = false + ]; + string delegator_address = 4 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 5 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + google.protobuf.Any pubkey = 6 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"]; + cosmos.base.v1beta1.Coin value = 7 [(gogoproto.nullable) = false]; +} + +// MsgCreateValidatorResponse defines the Msg/CreateValidator response type. +message MsgCreateValidatorResponse {} + +// MsgEditValidator defines a SDK message for editing an existing validator. +message MsgEditValidator { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Description description = 1 [(gogoproto.nullable) = false]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"address\""]; + + // We pass a reference to the new commission rate and min self delegation as + // it's not mandatory to update. If not updated, the deserialized rate will be + // zero with no way to distinguish if an update was intended. + // REF: #2373 + string commission_rate = 3 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.moretags) = "yaml:\"commission_rate\"" + ]; + string min_self_delegation = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_self_delegation\"" + ]; +} + +// MsgEditValidatorResponse defines the Msg/EditValidator response type. +message MsgEditValidatorResponse {} + +// MsgDelegate defines a SDK message for performing a delegation of coins +// from a delegator to a validator. +message MsgDelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// MsgDelegateResponse defines the Msg/Delegate response type. +message MsgDelegateResponse {} + +// MsgBeginRedelegate defines a SDK message for performing a redelegation +// of coins from a delegator and source validator to a destination validator. +message MsgBeginRedelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""]; + string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""]; + cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false]; +} + +// MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type. +message MsgBeginRedelegateResponse { + google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// MsgUndelegate defines a SDK message for performing an undelegation from a +// delegate and a validator. +message MsgUndelegate { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""]; + string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""]; + cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false]; +} + +// MsgUndelegateResponse defines the Msg/Undelegate response type. +message MsgUndelegateResponse { + google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} diff --git a/proto/cosmos/tx/signing/v1beta1/signing.proto b/proto/cosmos/tx/signing/v1beta1/signing.proto new file mode 100644 index 000000000000..4c1be40594d1 --- /dev/null +++ b/proto/cosmos/tx/signing/v1beta1/signing.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; +package cosmos.tx.signing.v1beta1; + +import "cosmos/crypto/multisig/v1beta1/multisig.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/tx/signing"; + +// SignMode represents a signing mode with its own security guarantees. +enum SignMode { + // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + // rejected + SIGN_MODE_UNSPECIFIED = 0; + + // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + // verified with raw bytes from Tx + SIGN_MODE_DIRECT = 1; + + // SIGN_MODE_TEXTUAL is a future signing mode that will verify some + // human-readable textual representation on top of the binary representation + // from SIGN_MODE_DIRECT + SIGN_MODE_TEXTUAL = 2; + + // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + // Amino JSON and will be removed in the future + SIGN_MODE_LEGACY_AMINO_JSON = 127; +} + +// SignatureDescriptors wraps multiple SignatureDescriptor's. +message SignatureDescriptors { + // signatures are the signature descriptors + repeated SignatureDescriptor signatures = 1; +} + +// SignatureDescriptor is a convenience type which represents the full data for +// a signature including the public key of the signer, signing modes and the +// signature itself. It is primarily used for coordinating signatures between +// clients. +message SignatureDescriptor { + // public_key is the public key of the signer + google.protobuf.Any public_key = 1; + + Data data = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to prevent + // replay attacks. + uint64 sequence = 3; + + // Data represents signature data + message Data { + // sum is the oneof that specifies whether this represents single or multi-signature data + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a multisig signer + Multi multi = 2; + } + + // Single is the signature data for a single signer + message Single { + // mode is the signing mode of the single signer + SignMode mode = 1; + + // signature is the raw signature bytes + bytes signature = 2; + } + + // Multi is the signature data for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + cosmos.crypto.multisig.v1beta1.CompactBitArray bitarray = 1; + + // signatures is the signatures of the multi-signature + repeated Data signatures = 2; + } + } +} diff --git a/proto/cosmos/tx/v1beta1/service.proto b/proto/cosmos/tx/v1beta1/service.proto new file mode 100644 index 000000000000..25214c4374e9 --- /dev/null +++ b/proto/cosmos/tx/v1beta1/service.proto @@ -0,0 +1,129 @@ +syntax = "proto3"; +package cosmos.tx.v1beta1; + +import "google/api/annotations.proto"; +import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos/tx/v1beta1/tx.proto"; +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; + +option (gogoproto.goproto_registration) = true; +option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; + +// Service defines a gRPC service for interacting with transactions. +service Service { + // Simulate simulates executing a transaction for estimating gas usage. + rpc Simulate(SimulateRequest) returns (SimulateResponse) { + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/simulate" + body: "*" + }; + } + // GetTx fetches a tx by hash. + rpc GetTx(GetTxRequest) returns (GetTxResponse) { + option (google.api.http).get = "/cosmos/tx/v1beta1/txs/{hash}"; + } + // BroadcastTx broadcast transaction. + rpc BroadcastTx(BroadcastTxRequest) returns (BroadcastTxResponse) { + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/txs" + body: "*" + }; + } + // GetTxsEvent fetches txs by event. + rpc GetTxsEvent(GetTxsEventRequest) returns (GetTxsEventResponse) { + option (google.api.http).get = "/cosmos/tx/v1beta1/txs"; + } +} + +// GetTxsEventRequest is the request type for the Service.TxsByEvents +// RPC method. +message GetTxsEventRequest { + // events is the list of transaction event type. + repeated string events = 1; + // pagination defines an pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; + OrderBy order_by = 3; +} + +// OrderBy defines the sorting order +enum OrderBy { + // ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults to ASC in this case. + ORDER_BY_UNSPECIFIED = 0; + // ORDER_BY_ASC defines ascending order + ORDER_BY_ASC = 1; + // ORDER_BY_DESC defines descending order + ORDER_BY_DESC = 2; +} + +// GetTxsEventResponse is the response type for the Service.TxsByEvents +// RPC method. +message GetTxsEventResponse { + // txs is the list of queried transactions. + repeated cosmos.tx.v1beta1.Tx txs = 1; + // tx_responses is the list of queried TxResponses. + repeated cosmos.base.abci.v1beta1.TxResponse tx_responses = 2; + // pagination defines an pagination for the response. + cosmos.base.query.v1beta1.PageResponse pagination = 3; +} + +// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +// RPC method. +message BroadcastTxRequest { + // tx_bytes is the raw transaction. + bytes tx_bytes = 1; + BroadcastMode mode = 2; +} + +// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +enum BroadcastMode { + // zero-value for mode ordering + BROADCAST_MODE_UNSPECIFIED = 0; + // BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BROADCAST_MODE_BLOCK = 1; + // BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BROADCAST_MODE_SYNC = 2; + // BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + // immediately. + BROADCAST_MODE_ASYNC = 3; +} + +// BroadcastTxResponse is the response type for the +// Service.BroadcastTx method. +message BroadcastTxResponse { + // tx_response is the queried TxResponses. + cosmos.base.abci.v1beta1.TxResponse tx_response = 1; +} + +// SimulateRequest is the request type for the Service.Simulate +// RPC method. +message SimulateRequest { + // tx is the transaction to simulate. + cosmos.tx.v1beta1.Tx tx = 1; +} + +// SimulateResponse is the response type for the +// Service.SimulateRPC method. +message SimulateResponse { + // gas_info is the information about gas used in the simulation. + cosmos.base.abci.v1beta1.GasInfo gas_info = 1; + // result is the result of the simulation. + cosmos.base.abci.v1beta1.Result result = 2; +} + +// GetTxRequest is the request type for the Service.GetTx +// RPC method. +message GetTxRequest { + // hash is the tx hash to query, encoded as a hex string. + string hash = 1; +} + +// GetTxResponse is the response type for the Service.GetTx method. +message GetTxResponse { + // tx is the queried transaction. + cosmos.tx.v1beta1.Tx tx = 1; + // tx_response is the queried TxResponses. + cosmos.base.abci.v1beta1.TxResponse tx_response = 2; +} \ No newline at end of file diff --git a/proto/cosmos/tx/v1beta1/tx.proto b/proto/cosmos/tx/v1beta1/tx.proto new file mode 100644 index 000000000000..2b02874cc183 --- /dev/null +++ b/proto/cosmos/tx/v1beta1/tx.proto @@ -0,0 +1,181 @@ +syntax = "proto3"; +package cosmos.tx.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/crypto/multisig/v1beta1/multisig.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/tx/signing/v1beta1/signing.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; + +// Tx is the standard type used for broadcasting transactions. +message Tx { + // body is the processable content of the transaction + TxBody body = 1; + + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo auth_info = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// TxRaw is a variant of Tx that pins the signer's exact binary representation +// of body and auth_info. This is used for signing, broadcasting and +// verification. The binary `serialize(tx: TxRaw)` is stored in Tendermint and +// the hash `sha256(serialize(tx: TxRaw))` becomes the "txhash", commonly used +// as the transaction ID. +message TxRaw { + // body_bytes is a protobuf serialization of a TxBody that matches the + // representation in SignDoc. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in SignDoc. + bytes auth_info_bytes = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. +message SignDoc { + // body_bytes is protobuf serialization of a TxBody that matches the + // representation in TxRaw. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in TxRaw. + bytes auth_info_bytes = 2; + + // chain_id is the unique identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + string chain_id = 3; + + // account_number is the account number of the account in state + uint64 account_number = 4; +} + +// TxBody is the body of a transaction that all signers sign over. +message TxBody { + // messages is a list of messages to be executed. The required signers of + // those messages define the number and order of elements in AuthInfo's + // signer_infos and Tx's signatures. Each required signer address is added to + // the list only the first time it occurs. + // By convention, the first required signer (usually from the first message) + // is referred to as the primary signer and pays the fee for the whole + // transaction. + repeated google.protobuf.Any messages = 1; + + // memo is any arbitrary memo to be added to the transaction + string memo = 2; + + // timeout is the block height after which this transaction will not + // be processed by the chain + uint64 timeout_height = 3; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, the transaction will be rejected + repeated google.protobuf.Any extension_options = 1023; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, they will be ignored + repeated google.protobuf.Any non_critical_extension_options = 2047; +} + +// AuthInfo describes the fee and signer modes that are used to sign a +// transaction. +message AuthInfo { + // signer_infos defines the signing modes for the required signers. The number + // and order of elements must match the required signers from TxBody's + // messages. The first element is the primary signer and the one which pays + // the fee. + repeated SignerInfo signer_infos = 1; + + // Fee is the fee and gas limit for the transaction. The first signer is the + // primary signer and the one which pays the fee. The fee can be calculated + // based on the cost of evaluating the body and doing signature verification + // of the signers. This can be estimated via simulation. + Fee fee = 2; +} + +// SignerInfo describes the public key and signing mode of a single top-level +// signer. +message SignerInfo { + // public_key is the public key of the signer. It is optional for accounts + // that already exist in state. If unset, the verifier can use the required \ + // signer address for this position and lookup the public key. + google.protobuf.Any public_key = 1; + + // mode_info describes the signing mode of the signer and is a nested + // structure to support nested multisig pubkey's + ModeInfo mode_info = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to + // prevent replay attacks. + uint64 sequence = 3; +} + +// ModeInfo describes the signing mode of a single or nested multisig signer. +message ModeInfo { + // sum is the oneof that specifies whether this represents a single or nested + // multisig signer + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a nested multisig signer + Multi multi = 2; + } + + // Single is the mode info for a single signer. It is structured as a message + // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the + // future + message Single { + // mode is the signing mode of the single signer + cosmos.tx.signing.v1beta1.SignMode mode = 1; + } + + // Multi is the mode info for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + cosmos.crypto.multisig.v1beta1.CompactBitArray bitarray = 1; + + // mode_infos is the corresponding modes of the signers of the multisig + // which could include nested multisig public keys + repeated ModeInfo mode_infos = 2; + } +} + +// Fee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +message Fee { + // amount is the amount of coins to be paid as a fee + repeated cosmos.base.v1beta1.Coin amount = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + // gas_limit is the maximum gas that can be used in transaction processing + // before an out of gas error occurs + uint64 gas_limit = 2; + + // if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + // the payer must be a tx signer (and thus have signed this field in AuthInfo). + // setting this field does *not* change the ordering of required signers for the transaction. + string payer = 3; + + // if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + // to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + // not support fee grants, this will fail + string granter = 4; +} diff --git a/proto/cosmos/upgrade/v1beta1/query.proto b/proto/cosmos/upgrade/v1beta1/query.proto new file mode 100644 index 000000000000..9eab27e76b44 --- /dev/null +++ b/proto/cosmos/upgrade/v1beta1/query.proto @@ -0,0 +1,68 @@ +syntax = "proto3"; +package cosmos.upgrade.v1beta1; + +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "cosmos/upgrade/v1beta1/upgrade.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/upgrade/types"; + +// Query defines the gRPC upgrade querier service. +service Query { + // CurrentPlan queries the current upgrade plan. + rpc CurrentPlan(QueryCurrentPlanRequest) returns (QueryCurrentPlanResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/current_plan"; + } + + // AppliedPlan queries a previously applied upgrade plan by its name. + rpc AppliedPlan(QueryAppliedPlanRequest) returns (QueryAppliedPlanResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/applied_plan/{name}"; + } + + // UpgradedConsensusState queries the consensus state that will serve + // as a trusted kernel for the next version of this chain. It will only be + // stored at the last height of this chain. + // UpgradedConsensusState RPC not supported with legacy querier + rpc UpgradedConsensusState(QueryUpgradedConsensusStateRequest) returns (QueryUpgradedConsensusStateResponse) { + option (google.api.http).get = "/cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}"; + } +} + +// QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC +// method. +message QueryCurrentPlanRequest {} + +// QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC +// method. +message QueryCurrentPlanResponse { + // plan is the current upgrade plan. + Plan plan = 1; +} + +// QueryCurrentPlanRequest is the request type for the Query/AppliedPlan RPC +// method. +message QueryAppliedPlanRequest { + // name is the name of the applied plan to query for. + string name = 1; +} + +// QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC +// method. +message QueryAppliedPlanResponse { + // height is the block height at which the plan was applied. + int64 height = 1; +} + +// QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState +// RPC method. +message QueryUpgradedConsensusStateRequest { + // last height of the current chain must be sent in request + // as this is the height under which next consensus state is stored + int64 last_height = 1; +} + +// QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState +// RPC method. +message QueryUpgradedConsensusStateResponse { + google.protobuf.Any upgraded_consensus_state = 1; +} diff --git a/proto/cosmos/upgrade/v1beta1/upgrade.proto b/proto/cosmos/upgrade/v1beta1/upgrade.proto new file mode 100644 index 000000000000..6d6839ca56c5 --- /dev/null +++ b/proto/cosmos/upgrade/v1beta1/upgrade.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package cosmos.upgrade.v1beta1; + +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/upgrade/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.goproto_getters_all) = false; + +// Plan specifies information about a planned upgrade and when it should occur. +message Plan { + option (gogoproto.equal) = true; + + // Sets the name for the upgrade. This name will be used by the upgraded + // version of the software to apply any special "on-upgrade" commands during + // the first BeginBlock method after the upgrade is applied. It is also used + // to detect whether a software version can handle a given upgrade. If no + // upgrade handler with this name has been set in the software, it will be + // assumed that the software is out-of-date when the upgrade Time or Height is + // reached and the software will exit. + string name = 1; + + // The time after which the upgrade must be performed. + // Leave set to its zero value to use a pre-defined Height instead. + google.protobuf.Timestamp time = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + + // The height at which the upgrade must be performed. + // Only used if Time is not set. + int64 height = 3; + + // Any application specific upgrade info to be included on-chain + // such as a git commit that validators could automatically upgrade to + string info = 4; + + // IBC-enabled chains can opt-in to including the upgraded client state in its upgrade plan + // This will make the chain commit to the correct upgraded (self) client state before the upgrade occurs, + // so that connecting chains can verify that the new upgraded client is valid by verifying a proof on the + // previous version of the chain. + // This will allow IBC connections to persist smoothly across planned chain upgrades + google.protobuf.Any upgraded_client_state = 5 [(gogoproto.moretags) = "yaml:\"upgraded_client_state\""]; +} + +// SoftwareUpgradeProposal is a gov Content type for initiating a software +// upgrade. +message SoftwareUpgradeProposal { + option (gogoproto.equal) = true; + + string title = 1; + string description = 2; + Plan plan = 3 [(gogoproto.nullable) = false]; +} + +// CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software +// upgrade. +message CancelSoftwareUpgradeProposal { + option (gogoproto.equal) = true; + + string title = 1; + string description = 2; +} diff --git a/proto/cosmos/vesting/v1beta1/tx.proto b/proto/cosmos/vesting/v1beta1/tx.proto new file mode 100644 index 000000000000..c49be802a76e --- /dev/null +++ b/proto/cosmos/vesting/v1beta1/tx.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package cosmos.vesting.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"; + +// Msg defines the bank Msg service. +service Msg { + // CreateVestingAccount defines a method that enables creating a vesting + // account. + rpc CreateVestingAccount(MsgCreateVestingAccount) returns (MsgCreateVestingAccountResponse); +} + +// MsgCreateVestingAccount defines a message that enables creating a vesting +// account. +message MsgCreateVestingAccount { + option (gogoproto.equal) = true; + + string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""]; + string to_address = 2 [(gogoproto.moretags) = "yaml:\"to_address\""]; + repeated cosmos.base.v1beta1.Coin amount = 3 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; + + int64 end_time = 4 [(gogoproto.moretags) = "yaml:\"end_time\""]; + bool delayed = 5; +} + +// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type. +message MsgCreateVestingAccountResponse {} \ No newline at end of file diff --git a/proto/cosmos/vesting/v1beta1/vesting.proto b/proto/cosmos/vesting/v1beta1/vesting.proto new file mode 100644 index 000000000000..6bdbbf08e4de --- /dev/null +++ b/proto/cosmos/vesting/v1beta1/vesting.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; +package cosmos.vesting.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/auth/v1beta1/auth.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"; + +// BaseVestingAccount implements the VestingAccount interface. It contains all +// the necessary fields needed for any vesting account implementation. +message BaseVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true]; + repeated cosmos.base.v1beta1.Coin original_vesting = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"original_vesting\"" + ]; + repeated cosmos.base.v1beta1.Coin delegated_free = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"delegated_free\"" + ]; + repeated cosmos.base.v1beta1.Coin delegated_vesting = 4 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"delegated_vesting\"" + ]; + int64 end_time = 5 [(gogoproto.moretags) = "yaml:\"end_time\""]; +} + +// ContinuousVestingAccount implements the VestingAccount interface. It +// continuously vests by unlocking coins linearly with respect to time. +message ContinuousVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; + int64 start_time = 2 [(gogoproto.moretags) = "yaml:\"start_time\""]; +} + +// 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. +message DelayedVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; +} + +// Period defines a length of time and amount of coins that will vest. +message Period { + option (gogoproto.goproto_stringer) = false; + + int64 length = 1; + repeated cosmos.base.v1beta1.Coin amount = 2 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// PeriodicVestingAccount implements the VestingAccount interface. It +// periodically vests by unlocking coins during each specified period. +message PeriodicVestingAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + BaseVestingAccount base_vesting_account = 1 [(gogoproto.embed) = true]; + int64 start_time = 2 [(gogoproto.moretags) = "yaml:\"start_time\""]; + repeated Period vesting_periods = 3 [(gogoproto.moretags) = "yaml:\"vesting_periods\"", (gogoproto.nullable) = false]; +} diff --git a/proto/ibc/applications/transfer/v1/genesis.proto b/proto/ibc/applications/transfer/v1/genesis.proto new file mode 100644 index 000000000000..98cf2296d2e9 --- /dev/null +++ b/proto/ibc/applications/transfer/v1/genesis.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types"; + +import "gogoproto/gogo.proto"; +import "ibc/applications/transfer/v1/transfer.proto"; + +// GenesisState defines the ibc-transfer genesis state +message GenesisState { + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + repeated DenomTrace denom_traces = 2 [ + (gogoproto.castrepeated) = "Traces", + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"denom_traces\"" + ]; + Params params = 3 [(gogoproto.nullable) = false]; +} diff --git a/proto/ibc/applications/transfer/v1/query.proto b/proto/ibc/applications/transfer/v1/query.proto new file mode 100644 index 000000000000..e9cbd02a31ce --- /dev/null +++ b/proto/ibc/applications/transfer/v1/query.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; +package ibc.applications.transfer.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/applications/transfer/v1/transfer.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types"; + +// Query provides defines the gRPC querier service. +service Query { + // DenomTrace queries a denomination trace information. + rpc DenomTrace(QueryDenomTraceRequest) returns (QueryDenomTraceResponse) { + option (google.api.http).get = "/ibc/applications/transfer/v1beta1/denom_traces/{hash}"; + } + + // DenomTraces queries all denomination traces. + rpc DenomTraces(QueryDenomTracesRequest) returns (QueryDenomTracesResponse) { + option (google.api.http).get = "/ibc/applications/transfer/v1beta1/denom_traces"; + } + + // Params queries all parameters of the ibc-transfer module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/ibc/applications/transfer/v1beta1/params"; + } +} + +// QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC +// method +message QueryDenomTraceRequest { + // hash (in hex format) of the denomination trace information. + string hash = 1; +} + +// QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC +// method. +message QueryDenomTraceResponse { + // denom_trace returns the requested denomination trace information. + DenomTrace denom_trace = 1; +} + +// QueryConnectionsRequest is the request type for the Query/DenomTraces RPC +// method +message QueryDenomTracesRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryConnectionsResponse is the response type for the Query/DenomTraces RPC +// method. +message QueryDenomTracesResponse { + // denom_traces returns all denominations trace information. + repeated DenomTrace denom_traces = 1 [(gogoproto.castrepeated) = "Traces", (gogoproto.nullable) = false]; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} diff --git a/proto/ibc/applications/transfer/v1/transfer.proto b/proto/ibc/applications/transfer/v1/transfer.proto new file mode 100644 index 000000000000..b388c3b879c2 --- /dev/null +++ b/proto/ibc/applications/transfer/v1/transfer.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types"; + +import "gogoproto/gogo.proto"; + +// FungibleTokenPacketData defines a struct for the packet payload +// See FungibleTokenPacketData spec: +// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures +message FungibleTokenPacketData { + // the token denomination to be transferred + string denom = 1; + // the token amount to be transferred + uint64 amount = 2; + // the sender address + string sender = 3; + // the recipient address on the destination chain + string receiver = 4; +} + +// DenomTrace contains the base denomination for ICS20 fungible tokens and the +// source tracing information path. +message DenomTrace { + // path defines the chain of port/channel identifiers used for tracing the + // source of the fungible token. + string path = 1; + // base denomination of the relayed fungible token. + string base_denom = 2; +} + +// Params defines the set of IBC transfer parameters. +// NOTE: To prevent a single token from being transferred, set the +// TransfersEnabled parameter to true and then set the bank module's SendEnabled +// parameter for the denomination to false. +message Params { + // send_enabled enables or disables all cross-chain token transfers from this + // chain. + bool send_enabled = 1 [(gogoproto.moretags) = "yaml:\"send_enabled\""]; + // receive_enabled enables or disables all cross-chain token transfers to this + // chain. + bool receive_enabled = 2 [(gogoproto.moretags) = "yaml:\"receive_enabled\""]; +} diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto new file mode 100644 index 000000000000..a0f0827aaf6a --- /dev/null +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; +package ibc.applications.transfer.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types"; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "ibc/core/client/v1/client.proto"; + +// Msg defines the ibc/transfer Msg service. +service Msg { + // Transfer defines a rpc handler method for MsgTransfer. + rpc Transfer(MsgTransfer) returns (MsgTransferResponse); +} + +// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between +// ICS20 enabled chains. See ICS Spec here: +// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures +message MsgTransfer { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // the port on which the packet will be sent + string source_port = 1 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // the channel by which the packet will be sent + string source_channel = 2 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // the tokens to be transferred + cosmos.base.v1beta1.Coin token = 3 [(gogoproto.nullable) = false]; + // the sender address + string sender = 4; + // the recipient address on the destination chain + string receiver = 5; + // Timeout height relative to the current block height. + // The timeout is disabled when set to 0. + ibc.core.client.v1.Height timeout_height = 6 + [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false]; + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. + // The timeout is disabled when set to 0. + uint64 timeout_timestamp = 7 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; +} + +// MsgTransferResponse defines the Msg/Transfer response type. +message MsgTransferResponse {} diff --git a/proto/ibc/core/channel/v1/channel.proto b/proto/ibc/core/channel/v1/channel.proto new file mode 100644 index 000000000000..302a48068953 --- /dev/null +++ b/proto/ibc/core/channel/v1/channel.proto @@ -0,0 +1,147 @@ +syntax = "proto3"; +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; + +// Channel defines pipeline for exactly-once packet delivery between specific +// modules on separate blockchains, which has at least one end capable of +// sending packets and one end capable of receiving packets. +message Channel { + option (gogoproto.goproto_getters) = false; + + // current state of the channel end + State state = 1; + // whether the channel is ordered or unordered + Order ordering = 2; + // counterparty channel end + Counterparty counterparty = 3 [(gogoproto.nullable) = false]; + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""]; + // opaque channel version, which is agreed upon during the handshake + string version = 5; +} + +// IdentifiedChannel defines a channel with additional port and channel +// identifier fields. +message IdentifiedChannel { + option (gogoproto.goproto_getters) = false; + + // current state of the channel end + State state = 1; + // whether the channel is ordered or unordered + Order ordering = 2; + // counterparty channel end + Counterparty counterparty = 3 [(gogoproto.nullable) = false]; + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + repeated string connection_hops = 4 [(gogoproto.moretags) = "yaml:\"connection_hops\""]; + // opaque channel version, which is agreed upon during the handshake + string version = 5; + // port identifier + string port_id = 6; + // channel identifier + string channel_id = 7; +} + +// State defines if a channel is in one of the following states: +// CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. +enum State { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + STATE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNINITIALIZED"]; + // A channel has just started the opening handshake. + STATE_INIT = 1 [(gogoproto.enumvalue_customname) = "INIT"]; + // A channel has acknowledged the handshake step on the counterparty chain. + STATE_TRYOPEN = 2 [(gogoproto.enumvalue_customname) = "TRYOPEN"]; + // A channel has completed the handshake. Open channels are + // ready to send and receive packets. + STATE_OPEN = 3 [(gogoproto.enumvalue_customname) = "OPEN"]; + // A channel has been closed and can no longer be used to send or receive + // packets. + STATE_CLOSED = 4 [(gogoproto.enumvalue_customname) = "CLOSED"]; +} + +// Order defines if a channel is ORDERED or UNORDERED +enum Order { + option (gogoproto.goproto_enum_prefix) = false; + + // zero-value for channel ordering + ORDER_NONE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "NONE"]; + // packets can be delivered in any order, which may differ from the order in + // which they were sent. + ORDER_UNORDERED = 1 [(gogoproto.enumvalue_customname) = "UNORDERED"]; + // packets are delivered exactly in the order which they were sent + ORDER_ORDERED = 2 [(gogoproto.enumvalue_customname) = "ORDERED"]; +} + +// Counterparty defines a channel end counterparty +message Counterparty { + option (gogoproto.goproto_getters) = false; + + // port on the counterparty chain which owns the other end of the channel. + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel end on the counterparty chain + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; +} + +// Packet defines a type that carries data across different chains through IBC +message Packet { + option (gogoproto.goproto_getters) = false; + + // number corresponds to the order of sends and receives, where a Packet + // with an earlier sequence number must be sent and received before a Packet + // with a later sequence number. + uint64 sequence = 1; + // identifies the port on the sending chain. + string source_port = 2 [(gogoproto.moretags) = "yaml:\"source_port\""]; + // identifies the channel end on the sending chain. + string source_channel = 3 [(gogoproto.moretags) = "yaml:\"source_channel\""]; + // identifies the port on the receiving chain. + string destination_port = 4 [(gogoproto.moretags) = "yaml:\"destination_port\""]; + // identifies the channel end on the receiving chain. + string destination_channel = 5 [(gogoproto.moretags) = "yaml:\"destination_channel\""]; + // actual opaque bytes transferred directly to the application module + bytes data = 6; + // block height after which the packet times out + ibc.core.client.v1.Height timeout_height = 7 + [(gogoproto.moretags) = "yaml:\"timeout_height\"", (gogoproto.nullable) = false]; + // block timestamp (in nanoseconds) after which the packet times out + uint64 timeout_timestamp = 8 [(gogoproto.moretags) = "yaml:\"timeout_timestamp\""]; +} + +// PacketState defines the generic type necessary to retrieve and store +// packet commitments, acknowledgements, and receipts. +// Caller is responsible for knowing the context necessary to interpret this +// state as a commitment, acknowledgement, or a receipt. +message PacketState { + option (gogoproto.goproto_getters) = false; + + // channel port identifier. + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // channel unique identifier. + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + // packet sequence. + uint64 sequence = 3; + // embedded data that represents packet state. + bytes data = 4; +} + +// Acknowledgement is the recommended acknowledgement format to be used by +// app-specific protocols. +// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +// conflicts with other protobuf message formats used for acknowledgements. +// The first byte of any message with this format will be the non-ASCII values +// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +message Acknowledgement { + // response contains either a result or an error and must be non-empty + oneof response { + bytes result = 21; + string error = 22; + } +} diff --git a/proto/ibc/core/channel/v1/genesis.proto b/proto/ibc/core/channel/v1/genesis.proto new file mode 100644 index 000000000000..d3b2c0424eaa --- /dev/null +++ b/proto/ibc/core/channel/v1/genesis.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// GenesisState defines the ibc channel submodule's genesis state. +message GenesisState { + repeated IdentifiedChannel channels = 1 [(gogoproto.casttype) = "IdentifiedChannel", (gogoproto.nullable) = false]; + repeated PacketState acknowledgements = 2 [(gogoproto.nullable) = false]; + repeated PacketState commitments = 3 [(gogoproto.nullable) = false]; + repeated PacketState receipts = 4 [(gogoproto.nullable) = false]; + repeated PacketSequence send_sequences = 5 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"send_sequences\""]; + repeated PacketSequence recv_sequences = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"recv_sequences\""]; + repeated PacketSequence ack_sequences = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"ack_sequences\""]; + // the sequence for the next generated channel identifier + uint64 next_channel_sequence = 8 [(gogoproto.moretags) = "yaml:\"next_channel_sequence\""]; +} + +// PacketSequence defines the genesis type necessary to retrieve and store +// next send and receive sequences. +message PacketSequence { + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + uint64 sequence = 3; +} diff --git a/proto/ibc/core/channel/v1/query.proto b/proto/ibc/core/channel/v1/query.proto new file mode 100644 index 000000000000..d9e3ceb8a646 --- /dev/null +++ b/proto/ibc/core/channel/v1/query.proto @@ -0,0 +1,367 @@ +syntax = "proto3"; +package ibc.core.channel.v1; + +import "ibc/core/client/v1/client.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/channel/v1/channel.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"; + +// Query provides defines the gRPC querier service +service Query { + // Channel queries an IBC Channel. + rpc Channel(QueryChannelRequest) returns (QueryChannelResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}"; + } + + // Channels queries all the IBC channels of a chain. + rpc Channels(QueryChannelsRequest) returns (QueryChannelsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels"; + } + + // ConnectionChannels queries all the channels associated with a connection + // end. + rpc ConnectionChannels(QueryConnectionChannelsRequest) returns (QueryConnectionChannelsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/connections/{connection}/channels"; + } + + // ChannelClientState queries for the client state for the channel associated + // with the provided channel identifiers. + rpc ChannelClientState(QueryChannelClientStateRequest) returns (QueryChannelClientStateResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/client_state"; + } + + // ChannelConsensusState queries for the consensus state for the channel + // associated with the provided channel identifiers. + rpc ChannelConsensusState(QueryChannelConsensusStateRequest) returns (QueryChannelConsensusStateResponse) { + option (google.api.http).get = + "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/" + "{revision_number}/height/{revision_height}"; + } + + // PacketCommitment queries a stored packet commitment hash. + rpc PacketCommitment(QueryPacketCommitmentRequest) returns (QueryPacketCommitmentResponse) { + option (google.api.http).get = + "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/{sequence}"; + } + + // PacketCommitments returns all the packet commitments hashes associated + // with a channel. + rpc PacketCommitments(QueryPacketCommitmentsRequest) returns (QueryPacketCommitmentsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments"; + } + + // PacketReceipt queries if a given packet sequence has been received on the queried chain + rpc PacketReceipt(QueryPacketReceiptRequest) returns (QueryPacketReceiptResponse) { + option (google.api.http).get = + "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_receipts/{sequence}"; + } + + // PacketAcknowledgement queries a stored packet acknowledgement hash. + rpc PacketAcknowledgement(QueryPacketAcknowledgementRequest) returns (QueryPacketAcknowledgementResponse) { + option (google.api.http).get = + "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_acks/{sequence}"; + } + + // PacketAcknowledgements returns all the packet acknowledgements associated + // with a channel. + rpc PacketAcknowledgements(QueryPacketAcknowledgementsRequest) returns (QueryPacketAcknowledgementsResponse) { + option (google.api.http).get = + "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_acknowledgements"; + } + + // UnreceivedPackets returns all the unreceived IBC packets associated with a + // channel and sequences. + rpc UnreceivedPackets(QueryUnreceivedPacketsRequest) returns (QueryUnreceivedPacketsResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/" + "{packet_commitment_sequences}/unreceived_packets"; + } + + // UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a + // channel and sequences. + rpc UnreceivedAcks(QueryUnreceivedAcksRequest) returns (QueryUnreceivedAcksResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/packet_commitments/" + "{packet_ack_sequences}/unreceived_acks"; + } + + // NextSequenceReceive returns the next receive sequence for a given channel. + rpc NextSequenceReceive(QueryNextSequenceReceiveRequest) returns (QueryNextSequenceReceiveResponse) { + option (google.api.http).get = "/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/next_sequence"; + } +} + +// QueryChannelRequest is the request type for the Query/Channel RPC method +message QueryChannelRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QueryChannelResponse is the response type for the Query/Channel RPC method. +// Besides the Channel end, it includes a proof and the height from which the +// proof was retrieved. +message QueryChannelResponse { + // channel associated with the request identifiers + ibc.core.channel.v1.Channel channel = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelsRequest is the request type for the Query/Channels RPC method +message QueryChannelsRequest { + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryChannelsResponse is the response type for the Query/Channels RPC method. +message QueryChannelsResponse { + // list of stored channels of the chain. + repeated ibc.core.channel.v1.IdentifiedChannel channels = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionChannelsRequest is the request type for the +// Query/QueryConnectionChannels RPC method +message QueryConnectionChannelsRequest { + // connection unique identifier + string connection = 1; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryConnectionChannelsResponse is the Response type for the +// Query/QueryConnectionChannels RPC method +message QueryConnectionChannelsResponse { + // list of channels associated with a connection. + repeated ibc.core.channel.v1.IdentifiedChannel channels = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelClientStateRequest is the request type for the Query/ClientState +// RPC method +message QueryChannelClientStateRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +message QueryChannelClientStateResponse { + // client state associated with the channel + ibc.core.client.v1.IdentifiedClientState identified_client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryChannelConsensusStateRequest is the request type for the +// Query/ConsensusState RPC method +message QueryChannelConsensusStateRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // revision number of the consensus state + uint64 revision_number = 3; + // revision height of the consensus state + uint64 revision_height = 4; +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +message QueryChannelConsensusStateResponse { + // consensus state associated with the channel + google.protobuf.Any consensus_state = 1; + // client ID associated with the consensus state + string client_id = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} + +// QueryPacketCommitmentRequest is the request type for the +// Query/PacketCommitment RPC method +message QueryPacketCommitmentRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketCommitmentResponse defines the client query response for a packet +// which also includes a proof and the height from which the proof was +// retrieved +message QueryPacketCommitmentResponse { + // packet associated with the request fields + bytes commitment = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketCommitmentsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketCommitmentsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 3; +} + +// QueryPacketCommitmentsResponse is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketCommitmentsResponse { + repeated ibc.core.channel.v1.PacketState commitments = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketReceiptRequest is the request type for the +// Query/PacketReceipt RPC method +message QueryPacketReceiptRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketReceiptResponse defines the client query response for a packet receipt +// which also includes a proof, and the height from which the proof was +// retrieved +message QueryPacketReceiptResponse { + // success flag for if receipt exists + bool received = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} + +// QueryPacketAcknowledgementRequest is the request type for the +// Query/PacketAcknowledgement RPC method +message QueryPacketAcknowledgementRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // packet sequence + uint64 sequence = 3; +} + +// QueryPacketAcknowledgementResponse defines the client query response for a +// packet which also includes a proof and the height from which the +// proof was retrieved +message QueryPacketAcknowledgementResponse { + // packet associated with the request fields + bytes acknowledgement = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryPacketAcknowledgementsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +message QueryPacketAcknowledgementsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 3; +} + +// QueryPacketAcknowledgemetsResponse is the request type for the +// Query/QueryPacketAcknowledgements RPC method +message QueryPacketAcknowledgementsResponse { + repeated ibc.core.channel.v1.PacketState acknowledgements = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryUnreceivedPacketsRequest is the request type for the +// Query/UnreceivedPackets RPC method +message QueryUnreceivedPacketsRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // list of packet sequences + repeated uint64 packet_commitment_sequences = 3; +} + +// QueryUnreceivedPacketsResponse is the response type for the +// Query/UnreceivedPacketCommitments RPC method +message QueryUnreceivedPacketsResponse { + // list of unreceived packet sequences + repeated uint64 sequences = 1; + // query block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} + +// QueryUnreceivedAcks is the request type for the +// Query/UnreceivedAcks RPC method +message QueryUnreceivedAcksRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; + // list of acknowledgement sequences + repeated uint64 packet_ack_sequences = 3; +} + +// QueryUnreceivedAcksResponse is the response type for the +// Query/UnreceivedAcks RPC method +message QueryUnreceivedAcksResponse { + // list of unreceived acknowledgement sequences + repeated uint64 sequences = 1; + // query block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} + +// QueryNextSequenceReceiveRequest is the request type for the +// Query/QueryNextSequenceReceiveRequest RPC method +message QueryNextSequenceReceiveRequest { + // port unique identifier + string port_id = 1; + // channel unique identifier + string channel_id = 2; +} + +// QuerySequenceResponse is the request type for the +// Query/QueryNextSequenceReceiveResponse RPC method +message QueryNextSequenceReceiveResponse { + // next sequence receive number + uint64 next_sequence_receive = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} diff --git a/proto/ibc/core/channel/v1/tx.proto b/proto/ibc/core/channel/v1/tx.proto new file mode 100644 index 000000000000..5f8426412415 --- /dev/null +++ b/proto/ibc/core/channel/v1/tx.proto @@ -0,0 +1,207 @@ +syntax = "proto3"; +package ibc.core.channel.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/channel/v1/channel.proto"; + +// Msg defines the ibc/channel Msg service. +service Msg { + // ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. + rpc ChannelOpenInit(MsgChannelOpenInit) returns (MsgChannelOpenInitResponse); + + // ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. + rpc ChannelOpenTry(MsgChannelOpenTry) returns (MsgChannelOpenTryResponse); + + // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. + rpc ChannelOpenAck(MsgChannelOpenAck) returns (MsgChannelOpenAckResponse); + + // ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. + rpc ChannelOpenConfirm(MsgChannelOpenConfirm) returns (MsgChannelOpenConfirmResponse); + + // ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. + rpc ChannelCloseInit(MsgChannelCloseInit) returns (MsgChannelCloseInitResponse); + + // ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. + rpc ChannelCloseConfirm(MsgChannelCloseConfirm) returns (MsgChannelCloseConfirmResponse); + + // RecvPacket defines a rpc handler method for MsgRecvPacket. + rpc RecvPacket(MsgRecvPacket) returns (MsgRecvPacketResponse); + + // Timeout defines a rpc handler method for MsgTimeout. + rpc Timeout(MsgTimeout) returns (MsgTimeoutResponse); + + // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. + rpc TimeoutOnClose(MsgTimeoutOnClose) returns (MsgTimeoutOnCloseResponse); + + // Acknowledgement defines a rpc handler method for MsgAcknowledgement. + rpc Acknowledgement(MsgAcknowledgement) returns (MsgAcknowledgementResponse); +} + +// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It +// is called by a relayer on Chain A. +message MsgChannelOpenInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + Channel channel = 2 [(gogoproto.nullable) = false]; + string signer = 3; +} + +// MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. +message MsgChannelOpenInitResponse {} + +// MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel +// on Chain B. +message MsgChannelOpenTry { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + // in the case of crossing hello's, when both chains call OpenInit, we need the channel identifier + // of the previous channel in state INIT + string previous_channel_id = 2 [(gogoproto.moretags) = "yaml:\"previous_channel_id\""]; + Channel channel = 3 [(gogoproto.nullable) = false]; + string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""]; + bytes proof_init = 5 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + ibc.core.client.v1.Height proof_height = 6 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 7; +} + +// MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. +message MsgChannelOpenTryResponse {} + +// MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge +// the change of channel state to TRYOPEN on Chain B. +message MsgChannelOpenAck { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string counterparty_channel_id = 3 [(gogoproto.moretags) = "yaml:\"counterparty_channel_id\""]; + string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""]; + bytes proof_try = 5 [(gogoproto.moretags) = "yaml:\"proof_try\""]; + ibc.core.client.v1.Height proof_height = 6 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 7; +} + +// MsgChannelOpenAckResponse defines the Msg/ChannelOpenAck response type. +message MsgChannelOpenAckResponse {} + +// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of channel state to OPEN on Chain A. +message MsgChannelOpenConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + bytes proof_ack = 3 [(gogoproto.moretags) = "yaml:\"proof_ack\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgChannelOpenConfirmResponse defines the Msg/ChannelOpenConfirm response type. +message MsgChannelOpenConfirmResponse {} + +// MsgChannelCloseInit defines a msg sent by a Relayer to Chain A +// to close a channel with Chain B. +message MsgChannelCloseInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + string signer = 3; +} + +// MsgChannelCloseInitResponse defines the Msg/ChannelCloseInit response type. +message MsgChannelCloseInitResponse {} + +// MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B +// to acknowledge the change of channel state to CLOSED on Chain A. +message MsgChannelCloseConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""]; + string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""]; + bytes proof_init = 3 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgChannelCloseConfirmResponse defines the Msg/ChannelCloseConfirm response type. +message MsgChannelCloseConfirmResponse {} + +// MsgRecvPacket receives incoming IBC packet +message MsgRecvPacket { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_commitment = 2 [(gogoproto.moretags) = "yaml:\"proof_commitment\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 4; +} + +// MsgRecvPacketResponse defines the Msg/RecvPacket response type. +message MsgRecvPacketResponse {} + +// MsgTimeout receives timed-out packet +message MsgTimeout { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + uint64 next_sequence_recv = 4 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""]; + string signer = 5; +} + +// MsgTimeoutResponse defines the Msg/Timeout response type. +message MsgTimeoutResponse {} + +// MsgTimeoutOnClose timed-out packet upon counterparty channel closure. +message MsgTimeoutOnClose { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""]; + bytes proof_close = 3 [(gogoproto.moretags) = "yaml:\"proof_close\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + uint64 next_sequence_recv = 5 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""]; + string signer = 6; +} + +// MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. +message MsgTimeoutOnCloseResponse {} + +// MsgAcknowledgement receives incoming IBC acknowledgement +message MsgAcknowledgement { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + Packet packet = 1 [(gogoproto.nullable) = false]; + bytes acknowledgement = 2; + bytes proof_acked = 3 [(gogoproto.moretags) = "yaml:\"proof_acked\""]; + ibc.core.client.v1.Height proof_height = 4 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 5; +} + +// MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. +message MsgAcknowledgementResponse {} diff --git a/proto/ibc/core/client/v1/client.proto b/proto/ibc/core/client/v1/client.proto new file mode 100644 index 000000000000..11d2195aaf61 --- /dev/null +++ b/proto/ibc/core/client/v1/client.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// IdentifiedClientState defines a client state with an additional client +// identifier field. +message IdentifiedClientState { + // client identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // client state + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; +} + +// ConsensusStateWithHeight defines a consensus state with an additional height field. +message ConsensusStateWithHeight { + // consensus state height + Height height = 1 [(gogoproto.nullable) = false]; + // consensus state + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml\"consensus_state\""]; +} + +// ClientConsensusStates defines all the stored consensus states for a given +// client. +message ClientConsensusStates { + // client identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // consensus states and their heights associated with the client + repeated ConsensusStateWithHeight consensus_states = 2 + [(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. +message ClientUpdateProposal { + option (gogoproto.goproto_getters) = false; + // the title of the update proposal + string title = 1; + // 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; +} + +// Height is a monotonically increasing data type +// that can be compared against another Height for the purposes of updating and +// freezing clients +// +// Normally the RevisionHeight is incremented at each height while keeping RevisionNumber +// the same. However some consensus algorithms may choose to reset the +// height in certain conditions e.g. hard forks, state-machine breaking changes +// In these cases, the RevisionNumber is incremented so that height continues to +// be monitonically increasing even as the RevisionHeight gets reset +message Height { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + // the revision that the client is currently on + uint64 revision_number = 1 [(gogoproto.moretags) = "yaml:\"revision_number\""]; + // the height within the given revision + uint64 revision_height = 2 [(gogoproto.moretags) = "yaml:\"revision_height\""]; +} + +// Params defines the set of IBC light client parameters. +message Params { + // allowed_clients defines the list of allowed client state types. + repeated string allowed_clients = 1 [(gogoproto.moretags) = "yaml:\"allowed_clients\""]; +} diff --git a/proto/ibc/core/client/v1/genesis.proto b/proto/ibc/core/client/v1/genesis.proto new file mode 100644 index 000000000000..16febbceef8b --- /dev/null +++ b/proto/ibc/core/client/v1/genesis.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"; + +import "ibc/core/client/v1/client.proto"; +import "gogoproto/gogo.proto"; + +// GenesisState defines the ibc client submodule's genesis state. +message GenesisState { + // client states with their corresponding identifiers + repeated IdentifiedClientState clients = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "IdentifiedClientStates"]; + // consensus states from each client + repeated ClientConsensusStates clients_consensus = 2 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "ClientsConsensusStates", + (gogoproto.moretags) = "yaml:\"clients_consensus\"" + ]; + // metadata from each client + repeated IdentifiedGenesisMetadata clients_metadata = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"clients_metadata\""]; + Params params = 4 [(gogoproto.nullable) = false]; + // create localhost on initialization + bool create_localhost = 5 [(gogoproto.moretags) = "yaml:\"create_localhost\""]; + // the sequence for the next generated client identifier + uint64 next_client_sequence = 6 [(gogoproto.moretags) = "yaml:\"next_client_sequence\""]; +} + +// GenesisMetadata defines the genesis type for metadata that clients may return +// with ExportMetadata +message GenesisMetadata { + option (gogoproto.goproto_getters) = false; + + // store key of metadata without clientID-prefix + bytes key = 1; + // metadata value + bytes value = 2; +} + +// IdentifiedGenesisMetadata has the client metadata with the corresponding client id. +message IdentifiedGenesisMetadata { + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + repeated GenesisMetadata client_metadata = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_metadata\""]; +} \ No newline at end of file diff --git a/proto/ibc/core/client/v1/query.proto b/proto/ibc/core/client/v1/query.proto new file mode 100644 index 000000000000..97f3acd62752 --- /dev/null +++ b/proto/ibc/core/client/v1/query.proto @@ -0,0 +1,130 @@ +syntax = "proto3"; +package ibc.core.client.v1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/client/v1/client.proto"; +import "google/protobuf/any.proto"; +import "google/api/annotations.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"; + +// Query provides defines the gRPC querier service +service Query { + // ClientState queries an IBC light client. + rpc ClientState(QueryClientStateRequest) returns (QueryClientStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1beta1/client_states/{client_id}"; + } + + // ClientStates queries all the IBC light clients of a chain. + rpc ClientStates(QueryClientStatesRequest) returns (QueryClientStatesResponse) { + option (google.api.http).get = "/ibc/core/client/v1beta1/client_states"; + } + + // ConsensusState queries a consensus state associated with a client state at + // a given height. + rpc ConsensusState(QueryConsensusStateRequest) returns (QueryConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/client/v1beta1/consensus_states/{client_id}/revision/{revision_number}/" + "height/{revision_height}"; + } + + // ConsensusStates queries all the consensus state associated with a given + // client. + rpc ConsensusStates(QueryConsensusStatesRequest) returns (QueryConsensusStatesResponse) { + option (google.api.http).get = "/ibc/core/client/v1beta1/consensus_states/{client_id}"; + } + + // ClientParams queries all parameters of the ibc client. + rpc ClientParams(QueryClientParamsRequest) returns (QueryClientParamsResponse) { + option (google.api.http).get = "/ibc/client/v1beta1/params"; + } +} + +// QueryClientStateRequest is the request type for the Query/ClientState RPC +// method +message QueryClientStateRequest { + // client state unique identifier + string client_id = 1; +} + +// QueryClientStateResponse is the response type for the Query/ClientState RPC +// method. Besides the client state, it includes a proof and the height from +// which the proof was retrieved. +message QueryClientStateResponse { + // client state associated with the request identifier + google.protobuf.Any client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryClientStatesRequest is the request type for the Query/ClientStates RPC +// method +message QueryClientStatesRequest { + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryClientStatesResponse is the response type for the Query/ClientStates RPC +// method. +message QueryClientStatesResponse { + // list of stored ClientStates of the chain. + repeated IdentifiedClientState client_states = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "IdentifiedClientStates"]; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryConsensusStateRequest is the request type for the Query/ConsensusState +// RPC method. Besides the consensus state, it includes a proof and the height +// from which the proof was retrieved. +message QueryConsensusStateRequest { + // client identifier + string client_id = 1; + // consensus state revision number + uint64 revision_number = 2; + // consensus state revision height + uint64 revision_height = 3; + // latest_height overrrides the height field and queries the latest stored + // ConsensusState + bool latest_height = 4; +} + +// QueryConsensusStateResponse is the response type for the Query/ConsensusState +// RPC method +message QueryConsensusStateResponse { + // consensus state associated with the client identifier at the given height + google.protobuf.Any consensus_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConsensusStatesRequest is the request type for the Query/ConsensusStates +// RPC method. +message QueryConsensusStatesRequest { + // client identifier + string client_id = 1; + // pagination request + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryConsensusStatesResponse is the response type for the +// Query/ConsensusStates RPC method +message QueryConsensusStatesResponse { + // consensus states associated with the identifier + repeated ConsensusStateWithHeight consensus_states = 1 [(gogoproto.nullable) = false]; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryClientParamsRequest is the request type for the Query/ClientParams RPC method. +message QueryClientParamsRequest {} + +// QueryClientParamsResponse is the response type for the Query/ClientParams RPC method. +message QueryClientParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} diff --git a/proto/ibc/core/client/v1/tx.proto b/proto/ibc/core/client/v1/tx.proto new file mode 100644 index 000000000000..a30ec8bbf10f --- /dev/null +++ b/proto/ibc/core/client/v1/tx.proto @@ -0,0 +1,96 @@ +syntax = "proto3"; +package ibc.core.client.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "ibc/core/client/v1/client.proto"; + +// Msg defines the ibc/client Msg service. +service Msg { + // CreateClient defines a rpc handler method for MsgCreateClient. + rpc CreateClient(MsgCreateClient) returns (MsgCreateClientResponse); + + // UpdateClient defines a rpc handler method for MsgUpdateClient. + rpc UpdateClient(MsgUpdateClient) returns (MsgUpdateClientResponse); + + // UpgradeClient defines a rpc handler method for MsgUpgradeClient. + rpc UpgradeClient(MsgUpgradeClient) returns (MsgUpgradeClientResponse); + + // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. + rpc SubmitMisbehaviour(MsgSubmitMisbehaviour) returns (MsgSubmitMisbehaviourResponse); +} + +// MsgCreateClient defines a message to create an IBC client +message MsgCreateClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // light client state + google.protobuf.Any client_state = 1 [(gogoproto.moretags) = "yaml:\"client_state\""]; + // consensus state associated with the client that corresponds to a given + // height. + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // signer address + string signer = 3; +} + +// MsgCreateClientResponse defines the Msg/CreateClient response type. +message MsgCreateClientResponse {} + +// MsgUpdateClient defines an sdk.Msg to update a IBC client state using +// the given header. +message MsgUpdateClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // header to update the light client + google.protobuf.Any header = 2; + // signer address + string signer = 3; +} + +// MsgUpdateClientResponse defines the Msg/UpdateClient response type. +message MsgUpdateClientResponse {} + +// MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client state +message MsgUpgradeClient { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // upgraded client state + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; + // upgraded consensus state, only contains enough information to serve as a basis of trust in update logic + google.protobuf.Any consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // proof that old chain committed to new client + bytes proof_upgrade_client = 4 [(gogoproto.moretags) = "yaml:\"proof_upgrade_client\""]; + // proof that old chain committed to new consensus state + bytes proof_upgrade_consensus_state = 5 [(gogoproto.moretags) = "yaml:\"proof_upgrade_consensus_state\""]; + // signer address + string signer = 6; +} + +// MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. +message MsgUpgradeClientResponse {} + +// MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for +// light client misbehaviour. +message MsgSubmitMisbehaviour { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // client unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // misbehaviour used for freezing the light client + google.protobuf.Any misbehaviour = 2; + // signer address + string signer = 3; +} + +// MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response type. +message MsgSubmitMisbehaviourResponse {} diff --git a/proto/ibc/core/commitment/v1/commitment.proto b/proto/ibc/core/commitment/v1/commitment.proto new file mode 100644 index 000000000000..51c10273166c --- /dev/null +++ b/proto/ibc/core/commitment/v1/commitment.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package ibc.core.commitment.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types"; + +import "gogoproto/gogo.proto"; +import "confio/proofs.proto"; + +// MerkleRoot defines a merkle root hash. +// In the Cosmos SDK, the AppHash of a block header becomes the root. +message MerkleRoot { + option (gogoproto.goproto_getters) = false; + + bytes hash = 1; +} + +// MerklePrefix is merkle path prefixed to the key. +// The constructed key from the Path and the key will be append(Path.KeyPath, +// append(Path.KeyPrefix, key...)) +message MerklePrefix { + bytes key_prefix = 1 [(gogoproto.moretags) = "yaml:\"key_prefix\""]; +} + +// MerklePath is the path used to verify commitment proofs, which can be an +// arbitrary structured object (defined by a commitment type). +// MerklePath is represented from root-to-leaf +message MerklePath { + option (gogoproto.goproto_stringer) = false; + + repeated string key_path = 1 [(gogoproto.moretags) = "yaml:\"key_path\""]; +} + +// MerkleProof is a wrapper type over a chain of CommitmentProofs. +// It demonstrates membership or non-membership for an element or set of +// elements, verifiable in conjunction with a known commitment root. Proofs +// should be succinct. +// MerkleProofs are ordered from leaf-to-root +message MerkleProof { + repeated ics23.CommitmentProof proofs = 1; +} \ No newline at end of file diff --git a/proto/ibc/core/connection/v1/connection.proto b/proto/ibc/core/connection/v1/connection.proto new file mode 100644 index 000000000000..d21e59516533 --- /dev/null +++ b/proto/ibc/core/connection/v1/connection.proto @@ -0,0 +1,104 @@ +syntax = "proto3"; +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/commitment/v1/commitment.proto"; + +// ICS03 - Connection Data Structures as defined in +// https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#data-structures + +// ConnectionEnd defines a stateful object on a chain connected to another +// separate one. +// NOTE: there must only be 2 defined ConnectionEnds to establish +// a connection between two chains. +message ConnectionEnd { + option (gogoproto.goproto_getters) = false; + // client associated with this connection. + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection. + repeated Version versions = 2; + // current state of the connection end. + State state = 3; + // counterparty chain associated with this connection. + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + // delay period that must pass before a consensus state can be used for packet-verification + // NOTE: delay period logic is only implemented by some clients. + uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; +} + +// IdentifiedConnection defines a connection with additional connection +// identifier field. +message IdentifiedConnection { + option (gogoproto.goproto_getters) = false; + // connection identifier. + string id = 1 [(gogoproto.moretags) = "yaml:\"id\""]; + // client associated with this connection. + string client_id = 2 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection + repeated Version versions = 3; + // current state of the connection end. + State state = 4; + // counterparty chain associated with this connection. + Counterparty counterparty = 5 [(gogoproto.nullable) = false]; + // delay period associated with this connection. + uint64 delay_period = 6 [(gogoproto.moretags) = "yaml:\"delay_period\""]; +} + +// State defines if a connection is in one of the following states: +// INIT, TRYOPEN, OPEN or UNINITIALIZED. +enum State { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + STATE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNINITIALIZED"]; + // A connection end has just started the opening handshake. + STATE_INIT = 1 [(gogoproto.enumvalue_customname) = "INIT"]; + // A connection end has acknowledged the handshake step on the counterparty + // chain. + STATE_TRYOPEN = 2 [(gogoproto.enumvalue_customname) = "TRYOPEN"]; + // A connection end has completed the handshake. + STATE_OPEN = 3 [(gogoproto.enumvalue_customname) = "OPEN"]; +} + +// Counterparty defines the counterparty chain associated with a connection end. +message Counterparty { + option (gogoproto.goproto_getters) = false; + + // identifies the client on the counterparty chain associated with a given + // connection. + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // identifies the connection end on the counterparty chain associated with a + // given connection. + string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + // commitment merkle prefix of the counterparty chain. + ibc.core.commitment.v1.MerklePrefix prefix = 3 [(gogoproto.nullable) = false]; +} + +// ClientPaths define all the connection paths for a client state. +message ClientPaths { + // list of connection paths + repeated string paths = 1; +} + +// ConnectionPaths define all the connection paths for a given client state. +message ConnectionPaths { + // client state unique identifier + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // list of connection paths + repeated string paths = 2; +} + +// Version defines the versioning scheme used to negotiate the IBC verison in +// the connection handshake. +message Version { + option (gogoproto.goproto_getters) = false; + + // unique version identifier + string identifier = 1; + // list of features compatible with the specified identifier + repeated string features = 2; +} diff --git a/proto/ibc/core/connection/v1/genesis.proto b/proto/ibc/core/connection/v1/genesis.proto new file mode 100644 index 000000000000..11df4ba1801f --- /dev/null +++ b/proto/ibc/core/connection/v1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/connection/v1/connection.proto"; + +// GenesisState defines the ibc connection submodule's genesis state. +message GenesisState { + repeated IdentifiedConnection connections = 1 [(gogoproto.nullable) = false]; + repeated ConnectionPaths client_connection_paths = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_connection_paths\""]; + // the sequence for the next generated connection identifier + uint64 next_connection_sequence = 3 [(gogoproto.moretags) = "yaml:\"next_connection_sequence\""]; +} diff --git a/proto/ibc/core/connection/v1/query.proto b/proto/ibc/core/connection/v1/query.proto new file mode 100644 index 000000000000..c5085a131f34 --- /dev/null +++ b/proto/ibc/core/connection/v1/query.proto @@ -0,0 +1,137 @@ +syntax = "proto3"; +package ibc.core.connection.v1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/connection/v1/connection.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types"; + +// Query provides defines the gRPC querier service +service Query { + // Connection queries an IBC connection end. + rpc Connection(QueryConnectionRequest) returns (QueryConnectionResponse) { + option (google.api.http).get = "/ibc/core/connection/v1beta1/connections/{connection_id}"; + } + + // Connections queries all the IBC connections of a chain. + rpc Connections(QueryConnectionsRequest) returns (QueryConnectionsResponse) { + option (google.api.http).get = "/ibc/core/connection/v1beta1/connections"; + } + + // ClientConnections queries the connection paths associated with a client + // state. + rpc ClientConnections(QueryClientConnectionsRequest) returns (QueryClientConnectionsResponse) { + option (google.api.http).get = "/ibc/core/connection/v1beta1/client_connections/{client_id}"; + } + + // ConnectionClientState queries the client state associated with the + // connection. + rpc ConnectionClientState(QueryConnectionClientStateRequest) returns (QueryConnectionClientStateResponse) { + option (google.api.http).get = "/ibc/core/connection/v1beta1/connections/{connection_id}/client_state"; + } + + // ConnectionConsensusState queries the consensus state associated with the + // connection. + rpc ConnectionConsensusState(QueryConnectionConsensusStateRequest) returns (QueryConnectionConsensusStateResponse) { + option (google.api.http).get = "/ibc/core/connection/v1beta1/connections/{connection_id}/consensus_state/" + "revision/{revision_number}/height/{revision_height}"; + } +} + +// QueryConnectionRequest is the request type for the Query/Connection RPC +// method +message QueryConnectionRequest { + // connection unique identifier + string connection_id = 1; +} + +// QueryConnectionResponse is the response type for the Query/Connection RPC +// method. Besides the connection end, it includes a proof and the height from +// which the proof was retrieved. +message QueryConnectionResponse { + // connection associated with the request identifier + ibc.core.connection.v1.ConnectionEnd connection = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionsRequest is the request type for the Query/Connections RPC +// method +message QueryConnectionsRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryConnectionsResponse is the response type for the Query/Connections RPC +// method. +message QueryConnectionsResponse { + // list of stored connections of the chain. + repeated ibc.core.connection.v1.IdentifiedConnection connections = 1; + // pagination response + cosmos.base.query.v1beta1.PageResponse pagination = 2; + // query block height + ibc.core.client.v1.Height height = 3 [(gogoproto.nullable) = false]; +} + +// QueryClientConnectionsRequest is the request type for the +// Query/ClientConnections RPC method +message QueryClientConnectionsRequest { + // client identifier associated with a connection + string client_id = 1; +} + +// QueryClientConnectionsResponse is the response type for the +// Query/ClientConnections RPC method +message QueryClientConnectionsResponse { + // slice of all the connection paths associated with a client. + repeated string connection_paths = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was generated + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionClientStateRequest is the request type for the +// Query/ConnectionClientState RPC method +message QueryConnectionClientStateRequest { + // connection identifier + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; +} + +// QueryConnectionClientStateResponse is the response type for the +// Query/ConnectionClientState RPC method +message QueryConnectionClientStateResponse { + // client state associated with the channel + ibc.core.client.v1.IdentifiedClientState identified_client_state = 1; + // merkle proof of existence + bytes proof = 2; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 3 [(gogoproto.nullable) = false]; +} + +// QueryConnectionConsensusStateRequest is the request type for the +// Query/ConnectionConsensusState RPC method +message QueryConnectionConsensusStateRequest { + // connection identifier + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + uint64 revision_number = 2; + uint64 revision_height = 3; +} + +// QueryConnectionConsensusStateResponse is the response type for the +// Query/ConnectionConsensusState RPC method +message QueryConnectionConsensusStateResponse { + // consensus state associated with the channel + google.protobuf.Any consensus_state = 1; + // client ID associated with the consensus state + string client_id = 2; + // merkle proof of existence + bytes proof = 3; + // height at which the proof was retrieved + ibc.core.client.v1.Height proof_height = 4 [(gogoproto.nullable) = false]; +} diff --git a/proto/ibc/core/connection/v1/tx.proto b/proto/ibc/core/connection/v1/tx.proto new file mode 100644 index 000000000000..19b40c69c30c --- /dev/null +++ b/proto/ibc/core/connection/v1/tx.proto @@ -0,0 +1,115 @@ +syntax = "proto3"; +package ibc.core.connection.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/connection/v1/connection.proto"; + +// Msg defines the ibc/connection Msg service. +service Msg { + // ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. + rpc ConnectionOpenInit(MsgConnectionOpenInit) returns (MsgConnectionOpenInitResponse); + + // ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. + rpc ConnectionOpenTry(MsgConnectionOpenTry) returns (MsgConnectionOpenTryResponse); + + // ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. + rpc ConnectionOpenAck(MsgConnectionOpenAck) returns (MsgConnectionOpenAckResponse); + + // ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. + rpc ConnectionOpenConfirm(MsgConnectionOpenConfirm) returns (MsgConnectionOpenConfirmResponse); +} + +// MsgConnectionOpenInit defines the msg sent by an account on Chain A to +// initialize a connection with Chain B. +message MsgConnectionOpenInit { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + Counterparty counterparty = 2 [(gogoproto.nullable) = false]; + Version version = 3; + uint64 delay_period = 4 [(gogoproto.moretags) = "yaml:\"delay_period\""]; + string signer = 5; +} + +// MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response type. +message MsgConnectionOpenInitResponse {} + +// MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a +// connection on Chain B. +message MsgConnectionOpenTry { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + // in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier + // of the previous connection in state INIT + string previous_connection_id = 2 [(gogoproto.moretags) = "yaml:\"previous_connection_id\""]; + google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""]; + Counterparty counterparty = 4 [(gogoproto.nullable) = false]; + uint64 delay_period = 5 [(gogoproto.moretags) = "yaml:\"delay_period\""]; + repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""]; + ibc.core.client.v1.Height proof_height = 7 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + // proof of the initialization the connection on Chain A: `UNITIALIZED -> + // INIT` + bytes proof_init = 8 [(gogoproto.moretags) = "yaml:\"proof_init\""]; + // proof of client state included in message + bytes proof_client = 9 [(gogoproto.moretags) = "yaml:\"proof_client\""]; + // proof of client consensus state + bytes proof_consensus = 10 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + ibc.core.client.v1.Height consensus_height = 11 + [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; + string signer = 12; +} + +// MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. +message MsgConnectionOpenTryResponse {} + +// MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to +// acknowledge the change of connection state to TRYOPEN on Chain B. +message MsgConnectionOpenAck { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + string counterparty_connection_id = 2 [(gogoproto.moretags) = "yaml:\"counterparty_connection_id\""]; + Version version = 3; + google.protobuf.Any client_state = 4 [(gogoproto.moretags) = "yaml:\"client_state\""]; + ibc.core.client.v1.Height proof_height = 5 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + // proof of the initialization the connection on Chain B: `UNITIALIZED -> + // TRYOPEN` + bytes proof_try = 6 [(gogoproto.moretags) = "yaml:\"proof_try\""]; + // proof of client state included in message + bytes proof_client = 7 [(gogoproto.moretags) = "yaml:\"proof_client\""]; + // proof of client consensus state + bytes proof_consensus = 8 [(gogoproto.moretags) = "yaml:\"proof_consensus\""]; + ibc.core.client.v1.Height consensus_height = 9 + [(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false]; + string signer = 10; +} + +// MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. +message MsgConnectionOpenAckResponse {} + +// MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of connection state to OPEN on Chain A. +message MsgConnectionOpenConfirm { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""]; + // proof for the change of the connection state on Chain A: `INIT -> OPEN` + bytes proof_ack = 2 [(gogoproto.moretags) = "yaml:\"proof_ack\""]; + ibc.core.client.v1.Height proof_height = 3 + [(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false]; + string signer = 4; +} + +// MsgConnectionOpenConfirmResponse defines the Msg/ConnectionOpenConfirm response type. +message MsgConnectionOpenConfirmResponse {} diff --git a/proto/ibc/core/types/v1/genesis.proto b/proto/ibc/core/types/v1/genesis.proto new file mode 100644 index 000000000000..ace508599308 --- /dev/null +++ b/proto/ibc/core/types/v1/genesis.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package ibc.core.types.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/core/types"; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/genesis.proto"; +import "ibc/core/connection/v1/genesis.proto"; +import "ibc/core/channel/v1/genesis.proto"; + +// GenesisState defines the ibc module's genesis state. +message GenesisState { + // ICS002 - Clients genesis state + ibc.core.client.v1.GenesisState client_genesis = 1 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_genesis\""]; + // ICS003 - Connections genesis state + ibc.core.connection.v1.GenesisState connection_genesis = 2 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"connection_genesis\""]; + // ICS004 - Channel genesis state + ibc.core.channel.v1.GenesisState channel_genesis = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"channel_genesis\""]; +} diff --git a/proto/ibc/lightclients/localhost/v1/localhost.proto b/proto/ibc/lightclients/localhost/v1/localhost.proto new file mode 100644 index 000000000000..d48a1c0076be --- /dev/null +++ b/proto/ibc/lightclients/localhost/v1/localhost.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package ibc.lightclients.localhost.v1; + +import "gogoproto/gogo.proto"; +import "ibc/core/client/v1/client.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types"; + +// ClientState defines a loopback (localhost) client. It requires (read-only) +// access to keys outside the client prefix. +message ClientState { + option (gogoproto.goproto_getters) = false; + // self chain ID + string chain_id = 1 [(gogoproto.moretags) = "yaml:\"chain_id\""]; + // self latest block height + ibc.core.client.v1.Height height = 2 [(gogoproto.nullable) = false]; +} diff --git a/proto/ibc/lightclients/solomachine/v1/solomachine.proto b/proto/ibc/lightclients/solomachine/v1/solomachine.proto new file mode 100644 index 000000000000..89686f3b7f5b --- /dev/null +++ b/proto/ibc/lightclients/solomachine/v1/solomachine.proto @@ -0,0 +1,186 @@ +syntax = "proto3"; +package ibc.lightclients.solomachine.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types"; + +import "ibc/core/connection/v1/connection.proto"; +import "ibc/core/channel/v1/channel.proto"; +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +message ClientState { + option (gogoproto.goproto_getters) = false; + // latest sequence of the client state + uint64 sequence = 1; + // frozen sequence of the solo machine + uint64 frozen_sequence = 2 [(gogoproto.moretags) = "yaml:\"frozen_sequence\""]; + ConsensusState consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + bool allow_update_after_proposal = 4 [(gogoproto.moretags) = "yaml:\"allow_update_after_proposal\""]; +} + +// ConsensusState defines a solo machine consensus state. The sequence of a consensus state +// is contained in the "height" key used in storing the consensus state. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // public key of the solo machine + google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""]; + // diversifier allows the same public key to be re-used across different solo machine clients + // (potentially on different chains) without being considered misbehaviour. + string diversifier = 2; + uint64 timestamp = 3; +} + +// Header defines a solo machine consensus header +message Header { + option (gogoproto.goproto_getters) = false; + // sequence to update solo machine public key at + uint64 sequence = 1; + uint64 timestamp = 2; + bytes signature = 3; + google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""]; + string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + uint64 sequence = 2; + SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""]; + SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""]; +} + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +message SignatureAndData { + option (gogoproto.goproto_getters) = false; + bytes signature = 1; + DataType data_type = 2 [(gogoproto.moretags) = "yaml:\"data_type\""]; + bytes data = 3; + uint64 timestamp = 4; +} + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +message TimestampedSignatureData { + option (gogoproto.goproto_getters) = false; + bytes signature_data = 1 [(gogoproto.moretags) = "yaml:\"signature_data\""]; + uint64 timestamp = 2; +} + +// SignBytes defines the signed bytes used for signature verification. +message SignBytes { + option (gogoproto.goproto_getters) = false; + + uint64 sequence = 1; + uint64 timestamp = 2; + string diversifier = 3; + // type of the data used + DataType data_type = 4 [(gogoproto.moretags) = "yaml:\"data_type\""]; + // marshaled data + bytes data = 5; +} + +// DataType defines the type of solo machine proof being created. This is done to preserve uniqueness of different +// data sign byte encodings. +enum DataType { + option (gogoproto.goproto_enum_prefix) = false; + + // Default State + DATA_TYPE_UNINITIALIZED_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "UNSPECIFIED"]; + // Data type for client state verification + DATA_TYPE_CLIENT_STATE = 1 [(gogoproto.enumvalue_customname) = "CLIENT"]; + // Data type for consensus state verification + DATA_TYPE_CONSENSUS_STATE = 2 [(gogoproto.enumvalue_customname) = "CONSENSUS"]; + // Data type for connection state verification + DATA_TYPE_CONNECTION_STATE = 3 [(gogoproto.enumvalue_customname) = "CONNECTION"]; + // Data type for channel state verification + DATA_TYPE_CHANNEL_STATE = 4 [(gogoproto.enumvalue_customname) = "CHANNEL"]; + // Data type for packet commitment verification + DATA_TYPE_PACKET_COMMITMENT = 5 [(gogoproto.enumvalue_customname) = "PACKETCOMMITMENT"]; + // Data type for packet acknowledgement verification + DATA_TYPE_PACKET_ACKNOWLEDGEMENT = 6 [(gogoproto.enumvalue_customname) = "PACKETACKNOWLEDGEMENT"]; + // Data type for packet receipt absence verification + DATA_TYPE_PACKET_RECEIPT_ABSENCE = 7 [(gogoproto.enumvalue_customname) = "PACKETRECEIPTABSENCE"]; + // Data type for next sequence recv verification + DATA_TYPE_NEXT_SEQUENCE_RECV = 8 [(gogoproto.enumvalue_customname) = "NEXTSEQUENCERECV"]; + // Data type for header verification + DATA_TYPE_HEADER = 9 [(gogoproto.enumvalue_customname) = "HEADER"]; +} + +// HeaderData returns the SignBytes data for update verification. +message HeaderData { + option (gogoproto.goproto_getters) = false; + + // header public key + google.protobuf.Any new_pub_key = 1 [(gogoproto.moretags) = "yaml:\"new_pub_key\""]; + // header diversifier + string new_diversifier = 2 [(gogoproto.moretags) = "yaml:\"new_diversifier\""]; +} + +// ClientStateData returns the SignBytes data for client state verification. +message ClientStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""]; +} + +// ConsensusStateData returns the SignBytes data for consensus state +// verification. +message ConsensusStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""]; +} + +// ConnectionStateData returns the SignBytes data for connection state +// verification. +message ConnectionStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.connection.v1.ConnectionEnd connection = 2; +} + +// ChannelStateData returns the SignBytes data for channel state +// verification. +message ChannelStateData { + option (gogoproto.goproto_getters) = false; + + bytes path = 1; + ibc.core.channel.v1.Channel channel = 2; +} + +// PacketCommitmentData returns the SignBytes data for packet commitment +// verification. +message PacketCommitmentData { + bytes path = 1; + bytes commitment = 2; +} + +// PacketAcknowledgementData returns the SignBytes data for acknowledgement +// verification. +message PacketAcknowledgementData { + bytes path = 1; + bytes acknowledgement = 2; +} + +// PacketReceiptAbsenceData returns the SignBytes data for +// packet receipt absence verification. +message PacketReceiptAbsenceData { + bytes path = 1; +} + +// NextSequenceRecvData returns the SignBytes data for verification of the next +// sequence to be received. +message NextSequenceRecvData { + bytes path = 1; + uint64 next_seq_recv = 2 [(gogoproto.moretags) = "yaml:\"next_seq_recv\""]; +} diff --git a/proto/ibc/lightclients/tendermint/v1/tendermint.proto b/proto/ibc/lightclients/tendermint/v1/tendermint.proto new file mode 100644 index 000000000000..a6882bf432bd --- /dev/null +++ b/proto/ibc/lightclients/tendermint/v1/tendermint.proto @@ -0,0 +1,111 @@ +syntax = "proto3"; +package ibc.lightclients.tendermint.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types"; + +import "tendermint/types/validator.proto"; +import "tendermint/types/types.proto"; +import "confio/proofs.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; +import "ibc/core/client/v1/client.proto"; +import "ibc/core/commitment/v1/commitment.proto"; +import "gogoproto/gogo.proto"; + +// ClientState from Tendermint tracks the current validator set, latest height, +// and a possible frozen height. +message ClientState { + option (gogoproto.goproto_getters) = false; + + string chain_id = 1; + Fraction trust_level = 2 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"trust_level\""]; + // duration of the period since the LastestTimestamp during which the + // submitted headers are valid for upgrade + google.protobuf.Duration trusting_period = 3 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"trusting_period\""]; + // duration of the staking unbonding period + google.protobuf.Duration unbonding_period = 4 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true, + (gogoproto.moretags) = "yaml:\"unbonding_period\"" + ]; + // defines how much new (untrusted) header's Time can drift into the future. + google.protobuf.Duration max_clock_drift = 5 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"max_clock_drift\""]; + // Block height when the client was frozen due to a misbehaviour + ibc.core.client.v1.Height frozen_height = 6 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"frozen_height\""]; + // Latest height the client was updated to + ibc.core.client.v1.Height latest_height = 7 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"latest_height\""]; + + // Proof specifications used in verifying counterparty state + repeated ics23.ProofSpec proof_specs = 8 [(gogoproto.moretags) = "yaml:\"proof_specs\""]; + + // Path at which next upgraded client will be committed. + // Each element corresponds to the key for a single CommitmentProof in the chained proof. + // NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState` + // ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState` + // For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}` + repeated string upgrade_path = 9 [(gogoproto.moretags) = "yaml:\"upgrade_path\""]; + + // This flag, when set to true, will allow governance to recover a client + // which has expired + bool allow_update_after_expiry = 10 [(gogoproto.moretags) = "yaml:\"allow_update_after_expiry\""]; + // This flag, when set to true, will allow governance to unfreeze a client + // whose chain has experienced a misbehaviour event + bool allow_update_after_misbehaviour = 11 [(gogoproto.moretags) = "yaml:\"allow_update_after_misbehaviour\""]; +} + +// ConsensusState defines the consensus state from Tendermint. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + + // timestamp that corresponds to the block height in which the ConsensusState + // was stored. + google.protobuf.Timestamp timestamp = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // commitment root (i.e app hash) + ibc.core.commitment.v1.MerkleRoot root = 2 [(gogoproto.nullable) = false]; + bytes next_validators_hash = 3 [ + (gogoproto.casttype) = "github.com/tendermint/tendermint/libs/bytes.HexBytes", + (gogoproto.moretags) = "yaml:\"next_validators_hash\"" + ]; +} + +// Misbehaviour is a wrapper over two conflicting Headers +// that implements Misbehaviour interface expected by ICS-02 +message Misbehaviour { + option (gogoproto.goproto_getters) = false; + + string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""]; + Header header_1 = 2 [(gogoproto.customname) = "Header1", (gogoproto.moretags) = "yaml:\"header_1\""]; + Header header_2 = 3 [(gogoproto.customname) = "Header2", (gogoproto.moretags) = "yaml:\"header_2\""]; +} + +// Header defines the Tendermint client consensus Header. +// It encapsulates all the information necessary to update from a trusted +// Tendermint ConsensusState. The inclusion of TrustedHeight and +// TrustedValidators allows this update to process correctly, so long as the +// ConsensusState for the TrustedHeight exists, this removes race conditions +// among relayers The SignedHeader and ValidatorSet are the new untrusted update +// fields for the client. The TrustedHeight is the height of a stored +// ConsensusState on the client that will be used to verify the new untrusted +// header. The Trusted ConsensusState must be within the unbonding period of +// current time in order to correctly verify, and the TrustedValidators must +// hash to TrustedConsensusState.NextValidatorsHash since that is the last +// trusted validator set at the TrustedHeight. +message Header { + .tendermint.types.SignedHeader signed_header = 1 + [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"signed_header\""]; + + .tendermint.types.ValidatorSet validator_set = 2 [(gogoproto.moretags) = "yaml:\"validator_set\""]; + ibc.core.client.v1.Height trusted_height = 3 + [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"trusted_height\""]; + .tendermint.types.ValidatorSet trusted_validators = 4 [(gogoproto.moretags) = "yaml:\"trusted_validators\""]; +} + +// Fraction defines the protobuf message type for tmmath.Fraction that only supports positive values. +message Fraction { + uint64 numerator = 1; + uint64 denominator = 2; +} diff --git a/scripts/install/install_sdk_arm.sh b/scripts/install/install_sdk_arm.sh deleted file mode 100644 index 27c9dc52c508..000000000000 --- a/scripts/install/install_sdk_arm.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# change this to a specific release or branch -BRANCH=master -REPO=github.com/cosmos/cosmos-sdk - -GO_VERSION=1.12.5 - -sudo apt-get update -y -sudo apt-get upgrade -y -sudo apt-get install -y make - -# get and unpack golang -curl -O https://dl.google.com/go/go$GO_VERSION.linux-armv6l.tar.gz -tar -xvf go$GO_VERSION.linux-armv6l.tar.gz - -# move go binary and add to path -sudo mv go /usr/local -echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile - -# create the go directory, set GOPATH, and put it on PATH -mkdir go -echo "export GOPATH=$HOME/go" >> ~/.profile -echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile -echo "export GO111MODULE=on" >> ~/.profile -source ~/.profile - -# get the code and move into repo -go get $REPO -cd $GOPATH/src/$REPO - -# build & install master -git checkout $BRANCH -LEDGER_ENABLED=false make tools -LEDGER_ENABLED=false make install - -source ~/.profile diff --git a/scripts/install/install_sdk_bsd.sh b/scripts/install/install_sdk_bsd.sh deleted file mode 100644 index f4b99fb6fdbc..000000000000 --- a/scripts/install/install_sdk_bsd.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/tcsh - -# Just run tcsh install_sdk_bsd.sh -# XXX: this script is intended to be run from -# a fresh Digital Ocean droplet with FreeBSD - -# upon its completion, you must either reset -# your terminal or run `source ~/.tcshrc` - -# This assumes your installing it through tcsh as root. -# Change the relevant lines from tcsh to csh if your -# installing as a different user, along with changing the -# gopath. - -# change this to a specific release or branch -set BRANCH=master -set REPO=github.com/cosmos/cosmos-sdk - -set GO_VERSION=1.12.5 - -sudo pkg update - -sudo pkg upgrade -y -sudo pkg install -y gmake -sudo pkg install -y git - -# get and unpack golang -curl -O https://storage.googleapis.com/golang/go$GO_VERSION.freebsd-amd64.tar.gz -tar -xvf go$GO_VERSION.freebsd-amd64.tar.gz - -# move go binary and add to path -mv go /usr/local -set path=($path /usr/local/go/bin) - - -# create the go directory, set GOPATH, and put it on PATH -mkdir go -echo "setenv GOPATH /root/go" >> ~/.tcshrc -setenv GOPATH /root/go -echo "set path=($path $GOPATH/bin)" >> ~/.tcshrc - -source ~/.tcshrc - -# get the code and move into repo -go get $REPO -cd $GOPATH/src/$REPO - -# build & install master -git checkout $BRANCH -gmake tools -gmake install -gmake install_examples diff --git a/scripts/install/install_sdk_ubuntu.sh b/scripts/install/install_sdk_ubuntu.sh deleted file mode 100644 index 4ba70fb78a48..000000000000 --- a/scripts/install/install_sdk_ubuntu.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -# XXX: this script is intended to be run from -# a fresh Digital Ocean droplet with Ubuntu - -# change this to a specific release or branch -BRANCH=master -REPO=github.com/cosmos/cosmos-sdk - -GO_VERSION=1.12.5 - -sudo apt-get update -y -sudo apt-get upgrade -y -sudo apt-get install -y make - -# get and unpack golang -curl -O https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz -tar -xvf go$GO_VERSION.linux-amd64.tar.gz - -# move go binary and add to path -mv go /usr/local -echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile - -# create the go directory, set GOPATH, and put it on PATH -mkdir go -echo "export GOPATH=$HOME/go" >> ~/.profile -echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile -echo "export GO111MODULE=on" >> ~/.profile -source ~/.profile - -# get the code and move into repo -go get $REPO -cd $GOPATH/src/$REPO - -# build & install master -git checkout $BRANCH -LEDGER_ENABLED=false make tools -LEDGER_ENABLED=false make install - -source ~/.profile diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh new file mode 100755 index 000000000000..bf62cd31e57c --- /dev/null +++ b/scripts/protoc-swagger-gen.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -eo pipefail + +mkdir -p ./tmp-swagger-gen +proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +for dir in $proto_dirs; do + + # generate swagger files (filter query files) + query_file=$(find "${dir}" -maxdepth 1 \( -name 'query.proto' -o -name 'service.proto' \)) + if [[ ! -z "$query_file" ]]; then + buf protoc \ + -I "proto" \ + -I "third_party/proto" \ + "$query_file" \ + --swagger_out=./tmp-swagger-gen \ + --swagger_opt=logtostderr=true --swagger_opt=fqn_for_swagger_name=true --swagger_opt=simple_operation_ids=true + fi +done + +# combine swagger files +# uses nodejs package `swagger-combine`. +# all the individual swagger files need to be configured in `config.json` for merging +swagger-combine ./client/docs/config.json -o ./client/docs/swagger-ui/swagger.yaml -f yaml --continueOnConflictingPaths true --includeDefinitions true + +# clean swagger files +rm -rf ./tmp-swagger-gen diff --git a/scripts/protocgen-any.sh b/scripts/protocgen-any.sh new file mode 100755 index 000000000000..2a094265d3f9 --- /dev/null +++ b/scripts/protocgen-any.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# This script generates a custom wrapper for google.protobuf.Any in +# codec/types/any.pb.go with a custom generated struct that lives in +# codec/types/any.go + +set -eo pipefail + +go install github.com/gogo/protobuf/protoc-gen-gogotypes + +buf protoc -I "third_party/proto" --gogotypes_out=./codec/types third_party/proto/google/protobuf/any.proto +mv codec/types/google/protobuf/any.pb.go codec/types +rm -rf codec/types/third_party + +# This removes the call to RegisterType in the custom generated Any wrapper +# so that only the Any type provided by gogo protobuf is registered in the +# global gogo protobuf type registry, which we aren't actually using +sed '/proto\.RegisterType/d' codec/types/any.pb.go > tmp && mv tmp codec/types/any.pb.go diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh new file mode 100755 index 000000000000..bd3e4009712b --- /dev/null +++ b/scripts/protocgen.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -eo pipefail + +protoc_gen_gocosmos() { + if ! grep "github.com/gogo/protobuf => github.com/regen-network/protobuf" go.mod &>/dev/null ; then + echo -e "\tPlease run this command from somewhere inside the cosmos-sdk folder." + return 1 + fi + + go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest 2>/dev/null +} + +protoc_gen_gocosmos + +proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +for dir in $proto_dirs; do + buf protoc \ + -I "proto" \ + -I "third_party/proto" \ + --gocosmos_out=plugins=interfacetype+grpc,\ +Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ + --grpc-gateway_out=logtostderr=true:. \ + $(find "${dir}" -maxdepth 1 -name '*.proto') + +done + +# command to generate docs using protoc-gen-doc +buf protoc \ +-I "proto" \ +-I "third_party/proto" \ +--doc_out=./docs/core \ +--doc_opt=./docs/protodoc-markdown.tmpl,proto-docs.md \ +$(find "$(pwd)/proto" -maxdepth 5 -name '*.proto') +go mod tidy + +# generate codec/testdata proto code +buf protoc -I "proto" -I "third_party/proto" -I "testutil/testdata" --gocosmos_out=plugins=interfacetype+grpc,\ +Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. ./testutil/testdata/*.proto + +# move proto files to the right places +cp -r github.com/cosmos/cosmos-sdk/* ./ +rm -rf github.com diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000000..79e46a3ae799 --- /dev/null +++ b/server/README.md @@ -0,0 +1,110 @@ +# Server + +The `server` package is 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. + +## Preliminary + +The root command of an application typically is constructed with: ++ command to start an application binary ++ three meta commands: `query`, `tx`, and a few auxiliary commands such as `genesis`. +utilities. + +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. + +Example: + +```go +var ( + initClientCtx = client.Context{...} + + rootCmd = &cobra.Command{ + Use: "simd", + Short: "simulation app", + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { + return err + } + + return server.InterceptConfigsPreRunHandler(cmd) + }, + } + // add root sub-commands ... +) +``` + +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. + +## `StartCmd` + +The `StartCmd` accepts an `AppCreator` function which returns an `Application`. +The `AppCreator` is responsible for constructing the application based on the +options provided to it via `AppOptions`. The `AppOptions` interface type defines +a single method, `Get() interface{}`, and is implemented as a [viper](https://github.com/spf13/viper) +literal that exists in the `server.Context`. All the possible options an application +may use and provide to the construction process are defined by the `StartCmd` +and by the application's config file, `app.toml`. + +The application can either be started in-process or as an external process. The +former creates a Tendermint service and the latter creates a Tendermint Node. + +Under the hood, `StartCmd` will call `GetServerContextFromCmd`, which provides +the command access to a `server.Context`. This context provides access to the +viper literal, the Tendermint config and logger. This allows flags to be bound +the viper literal and passed to the application construction. + +Example: + +```go +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts server.AppOptions) server.Application { + var cache sdk.MultiStorePersistentCache + + if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { + cache = store.NewCommitKVStoreCacheManager() + } + + skipUpgradeHeights := make(map[int64]bool) + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + + pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) + if err != nil { + panic(err) + } + + return simapp.NewSimApp( + logger, db, traceStore, true, skipUpgradeHeights, + cast.ToString(appOpts.Get(flags.FlagHome)), + cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), + baseapp.SetPruning(pruningOpts), + baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), + baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), + baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), + baseapp.SetInterBlockCache(cache), + baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), + ) +} +``` + +Note, some of the options provided are exposed via CLI flags in the start command +and some are also allowed to be set in the application's `app.toml`. It is recommend +to use the `cast` package for type safety guarantees and due to the limitations of +CLI flag types. diff --git a/server/api/server.go b/server/api/server.go new file mode 100644 index 000000000000..c2987f860d1a --- /dev/null +++ b/server/api/server.go @@ -0,0 +1,145 @@ +package api + +import ( + "fmt" + "net" + "net/http" + "strings" + "time" + + "github.com/gogo/gateway" + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/tendermint/tendermint/libs/log" + tmrpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/telemetry" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/rest" + + // unnamed import of statik for swagger UI support + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" +) + +// Server defines the server's API interface. +type Server struct { + Router *mux.Router + GRPCGatewayRouter *runtime.ServeMux + ClientCtx client.Context + + logger log.Logger + metrics *telemetry.Metrics + listener net.Listener +} + +// CustomGRPCHeaderMatcher for mapping request headers to +// GRPC metadata. +// HTTP headers that start with 'Grpc-Metadata-' are automatically mapped to +// gRPC metadata after removing prefix 'Grpc-Metadata-'. We can use this +// CustomGRPCHeaderMatcher if headers don't start with `Grpc-Metadata-` +func CustomGRPCHeaderMatcher(key string) (string, bool) { + switch strings.ToLower(key) { + case grpctypes.GRPCBlockHeightHeader: + return grpctypes.GRPCBlockHeightHeader, true + default: + return runtime.DefaultHeaderMatcher(key) + } +} + +func New(clientCtx client.Context, logger log.Logger) *Server { + // The default JSON marshaller used by the gRPC-Gateway is unable to marshal non-nullable non-scalar fields. + // Using the gogo/gateway package with the gRPC-Gateway WithMarshaler option fixes the scalar field marshalling issue. + marshalerOption := &gateway.JSONPb{ + EmitDefaults: true, + Indent: " ", + OrigName: true, + AnyResolver: clientCtx.InterfaceRegistry, + } + + return &Server{ + Router: mux.NewRouter(), + ClientCtx: clientCtx, + logger: logger, + GRPCGatewayRouter: runtime.NewServeMux( + // Custom marshaler option is required for gogo proto + runtime.WithMarshalerOption(runtime.MIMEWildcard, marshalerOption), + + // This is necessary to get error details properly + // marshalled in unary requests. + runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler), + + // Custom header matcher for mapping request headers to + // GRPC metadata + runtime.WithIncomingHeaderMatcher(CustomGRPCHeaderMatcher), + ), + } +} + +// Start starts the API server. Internally, the API server leverages Tendermint's +// JSON RPC server. Configuration options are provided via config.APIConfig +// and are delegated to the Tendermint JSON RPC server. The process is +// non-blocking, so an external signal handler must be used. +func (s *Server) Start(cfg config.Config) error { + if cfg.Telemetry.Enabled { + m, err := telemetry.New(cfg.Telemetry) + if err != nil { + return err + } + + s.metrics = m + s.registerMetrics() + } + + tmCfg := tmrpcserver.DefaultConfig() + tmCfg.MaxOpenConnections = int(cfg.API.MaxOpenConnections) + tmCfg.ReadTimeout = time.Duration(cfg.API.RPCReadTimeout) * time.Second + tmCfg.WriteTimeout = time.Duration(cfg.API.RPCWriteTimeout) * time.Second + tmCfg.MaxBodyBytes = int64(cfg.API.RPCMaxBodyBytes) + + listener, err := tmrpcserver.Listen(cfg.API.Address, tmCfg) + if err != nil { + return err + } + + s.registerGRPCGatewayRoutes() + + s.listener = listener + var h http.Handler = s.Router + + if cfg.API.EnableUnsafeCORS { + allowAllCORS := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"})) + return tmrpcserver.Serve(s.listener, allowAllCORS(h), s.logger, tmCfg) + } + + s.logger.Info("starting API server...") + return tmrpcserver.Serve(s.listener, s.Router, s.logger, tmCfg) +} + +// Close closes the API server. +func (s *Server) Close() error { + return s.listener.Close() +} + +func (s *Server) registerGRPCGatewayRoutes() { + s.Router.PathPrefix("/").Handler(s.GRPCGatewayRouter) +} + +func (s *Server) registerMetrics() { + metricsHandler := func(w http.ResponseWriter, r *http.Request) { + format := strings.TrimSpace(r.FormValue("format")) + + gr, err := s.metrics.Gather(format) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to gather metrics: %s", err)) + return + } + + w.Header().Set("Content-Type", gr.ContentType) + _, _ = w.Write(gr.Metrics) + } + + s.Router.HandleFunc("/metrics", metricsHandler).Methods("GET") +} diff --git a/server/cmd/execute.go b/server/cmd/execute.go new file mode 100644 index 000000000000..83e9bc0b25a8 --- /dev/null +++ b/server/cmd/execute.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "context" + + "github.com/rs/zerolog" + "github.com/spf13/cobra" + tmcfg "github.com/tendermint/tendermint/config" + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" +) + +// Execute executes the root command of an application. It handles creating a +// server context object with the appropriate server and client objects injected +// into the underlying stdlib Context. It also handles adding core CLI flags, +// specifically the logging flags. It returns an error upon execution failure. +func Execute(rootCmd *cobra.Command, defaultHome string) error { + // Create and set a client.Context on the command's Context. During the pre-run + // of the root command, a default initialized client.Context is provided to + // seed child command execution with values such as AccountRetriver, Keyring, + // and a Tendermint RPC. This requires the use of a pointer reference when + // getting and setting the client.Context. Ideally, we utilize + // https://github.com/spf13/cobra/pull/1118. + srvCtx := server.NewDefaultContext() + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) + ctx = context.WithValue(ctx, server.ServerContextKey, srvCtx) + + rootCmd.PersistentFlags().String(flags.FlagLogLevel, zerolog.InfoLevel.String(), "The logging level (trace|debug|info|warn|error|fatal|panic)") + rootCmd.PersistentFlags().String(flags.FlagLogFormat, tmcfg.LogFormatPlain, "The logging format (json|plain)") + + executor := tmcli.PrepareBaseCmd(rootCmd, "", defaultHome) + return executor.ExecuteContext(ctx) +} diff --git a/server/config/config.go b/server/config/config.go index 9752ae85273a..80e79f04558d 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -4,12 +4,18 @@ import ( "fmt" "strings" - "github.com/cosmos/cosmos-sdk/store" + "github.com/spf13/viper" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" ) const ( defaultMinGasPrices = "" + + // DefaultGRPCAddress is the default address the gRPC server binds to. + DefaultGRPCAddress = "0.0.0.0:9090" ) // BaseConfig defines the server's basic configuration @@ -19,6 +25,11 @@ type BaseConfig struct { // specified in this config (e.g. 0.25token1;0.0001token2). MinGasPrices string `mapstructure:"minimum-gas-prices"` + Pruning string `mapstructure:"pruning"` + PruningKeepRecent string `mapstructure:"pruning-keep-recent"` + PruningKeepEvery string `mapstructure:"pruning-keep-every"` + PruningInterval string `mapstructure:"pruning-interval"` + // HaltHeight contains a non-zero block height at which a node will gracefully // halt and shutdown that can be used to assist upgrades and testing. // @@ -32,15 +43,90 @@ type BaseConfig struct { // Note: Commitment of state will be attempted on the corresponding block. HaltTime uint64 `mapstructure:"halt-time"` + // MinRetainBlocks defines the minimum block height offset from the current + // block being committed, such that blocks past this offset may be pruned + // from Tendermint. It is used as part of the process of determining the + // ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates + // that no blocks should be pruned. + // + // This configuration value is only responsible for pruning Tendermint blocks. + // It has no bearing on application state pruning which is determined by the + // "pruning-*" configurations. + // + // Note: Tendermint block pruning is dependant on this parameter in conunction + // with the unbonding (safety threshold) period, state pruning and state sync + // snapshot parameters to determine the correct minimum value of + // ResponseCommit.RetainHeight. + MinRetainBlocks uint64 `mapstructure:"min-retain-blocks"` + // InterBlockCache enables inter-block caching. InterBlockCache bool `mapstructure:"inter-block-cache"` - Pruning string `mapstructure:"pruning"` + // IndexEvents defines the set of events in the form {eventType}.{attributeKey}, + // which informs Tendermint what to index. If empty, all events will be indexed. + IndexEvents []string `mapstructure:"index-events"` +} + +// APIConfig defines the API listener configuration. +type APIConfig struct { + // Enable defines if the API server should be enabled. + Enable bool `mapstructure:"enable"` + + // Swagger defines if swagger documentation should automatically be registered. + Swagger bool `mapstructure:"swagger"` + + // EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) + EnableUnsafeCORS bool `mapstructure:"enabled-unsafe-cors"` + + // Address defines the API server to listen on + Address string `mapstructure:"address"` + + // MaxOpenConnections defines the number of maximum open connections + MaxOpenConnections uint `mapstructure:"max-open-connections"` + + // RPCReadTimeout defines the Tendermint RPC read timeout (in seconds) + RPCReadTimeout uint `mapstructure:"rpc-read-timeout"` + + // RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds) + RPCWriteTimeout uint `mapstructure:"rpc-write-timeout"` + + // RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes) + RPCMaxBodyBytes uint `mapstructure:"rpc-max-body-bytes"` + + // TODO: TLS/Proxy configuration. + // + // Ref: https://github.com/cosmos/cosmos-sdk/issues/6420 +} + +// GRPCConfig defines configuration for the gRPC server. +type GRPCConfig struct { + // Enable defines if the gRPC server should be enabled. + Enable bool `mapstructure:"enable"` + + // Address defines the API 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. + // 0 disables snapshots. Must be a multiple of PruningKeepEvery. + SnapshotInterval uint64 `mapstructure:"snapshot-interval"` + + // SnapshotKeepRecent sets the number of recent state sync snapshots to keep. + // 0 keeps all snapshots. + SnapshotKeepRecent uint32 `mapstructure:"snapshot-keep-recent"` } // Config defines the server's top level configuration type Config struct { BaseConfig `mapstructure:",squash"` + + // Telemetry defines the application telemetry configuration + Telemetry telemetry.Config `mapstructure:"telemetry"` + API APIConfig `mapstructure:"api"` + GRPC GRPCConfig `mapstructure:"grpc"` + StateSync StateSyncConfig `mapstructure:"state-sync"` } // SetMinGasPrices sets the validator's minimum gas prices. @@ -73,10 +159,89 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins { // DefaultConfig returns server's default configuration. func DefaultConfig() *Config { return &Config{ - BaseConfig{ - MinGasPrices: defaultMinGasPrices, - InterBlockCache: true, - Pruning: store.PruningStrategySyncable, + BaseConfig: BaseConfig{ + MinGasPrices: defaultMinGasPrices, + InterBlockCache: true, + Pruning: storetypes.PruningOptionDefault, + PruningKeepRecent: "0", + PruningKeepEvery: "0", + PruningInterval: "0", + MinRetainBlocks: 0, + IndexEvents: make([]string, 0), + }, + Telemetry: telemetry.Config{ + Enabled: false, + GlobalLabels: [][]string{}, + }, + API: APIConfig{ + Enable: false, + Swagger: false, + Address: "tcp://0.0.0.0:1317", + MaxOpenConnections: 1000, + RPCReadTimeout: 10, + RPCMaxBodyBytes: 1000000, + }, + GRPC: GRPCConfig{ + Enable: true, + Address: DefaultGRPCAddress, + }, + StateSync: StateSyncConfig{ + SnapshotInterval: 0, + SnapshotKeepRecent: 2, + }, + } +} + +// GetConfig returns a fully parsed Config object. +func GetConfig(v *viper.Viper) Config { + globalLabelsRaw := v.Get("telemetry.global-labels").([]interface{}) + globalLabels := make([][]string, 0, len(globalLabelsRaw)) + for _, glr := range globalLabelsRaw { + labelsRaw := glr.([]interface{}) + if len(labelsRaw) == 2 { + globalLabels = append(globalLabels, []string{labelsRaw[0].(string), labelsRaw[1].(string)}) + } + } + + return Config{ + BaseConfig: BaseConfig{ + MinGasPrices: v.GetString("minimum-gas-prices"), + InterBlockCache: v.GetBool("inter-block-cache"), + Pruning: v.GetString("pruning"), + PruningKeepRecent: v.GetString("pruning-keep-recent"), + PruningKeepEvery: v.GetString("pruning-keep-every"), + PruningInterval: v.GetString("pruning-interval"), + HaltHeight: v.GetUint64("halt-height"), + HaltTime: v.GetUint64("halt-time"), + IndexEvents: v.GetStringSlice("index-events"), + MinRetainBlocks: v.GetUint64("min-retain-blocks"), + }, + Telemetry: telemetry.Config{ + ServiceName: v.GetString("telemetry.service-name"), + Enabled: v.GetBool("telemetry.enabled"), + EnableHostname: v.GetBool("telemetry.enable-hostname"), + EnableHostnameLabel: v.GetBool("telemetry.enable-hostname-label"), + EnableServiceLabel: v.GetBool("telemetry.enable-service-label"), + PrometheusRetentionTime: v.GetInt64("telemetry.prometheus-retention-time"), + GlobalLabels: globalLabels, + }, + API: APIConfig{ + Enable: v.GetBool("api.enable"), + Swagger: v.GetBool("api.swagger"), + Address: v.GetString("api.address"), + MaxOpenConnections: v.GetUint("api.max-open-connections"), + RPCReadTimeout: v.GetUint("api.rpc-read-timeout"), + RPCWriteTimeout: v.GetUint("api.rpc-write-timeout"), + RPCMaxBodyBytes: v.GetUint("api.rpc-max-body-bytes"), + EnableUnsafeCORS: v.GetBool("api.enabled-unsafe-cors"), + }, + GRPC: GRPCConfig{ + Enable: v.GetBool("grpc.enable"), + Address: v.GetString("grpc.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 e556ed57656b..b042da74293d 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -11,13 +11,26 @@ import ( const defaultConfigTemplate = `# This is a TOML config file. # For more information, see https://github.com/toml-lang/toml -##### main base config options ##### +############################################################################### +### Base Configuration ### +############################################################################### # The minimum gas prices a validator is willing to accept for processing a # transaction. A transaction's fees must meet the minimum of any denomination # specified in this config (e.g. 0.25token1;0.0001token2). minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}" +# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals +# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) +# everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals +# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' +pruning = "{{ .BaseConfig.Pruning }}" + +# These are applied if and only if the pruning strategy is custom. +pruning-keep-recent = "{{ .BaseConfig.PruningKeepRecent }}" +pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}" +pruning-interval = "{{ .BaseConfig.PruningInterval }}" + # HaltHeight contains a non-zero block height at which a node will gracefully # halt and shutdown that can be used to assist upgrades and testing. # @@ -31,21 +44,132 @@ halt-height = {{ .BaseConfig.HaltHeight }} # Note: Commitment of state will be attempted on the corresponding block. halt-time = {{ .BaseConfig.HaltTime }} +# MinRetainBlocks defines the minimum block height offset from the current +# block being committed, such that all blocks past this offset are pruned +# from Tendermint. It is used as part of the process of determining the +# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates +# that no blocks should be pruned. +# +# This configuration value is only responsible for pruning Tendermint blocks. +# It has no bearing on application state pruning which is determined by the +# "pruning-*" configurations. +# +# Note: Tendermint block pruning is dependant on this parameter in conunction +# with the unbonding (safety threshold) period, state pruning and state sync +# snapshot parameters to determine the correct minimum value of +# ResponseCommit.RetainHeight. +min-retain-blocks = {{ .BaseConfig.MinRetainBlocks }} + # InterBlockCache enables inter-block caching. inter-block-cache = {{ .BaseConfig.InterBlockCache }} -# Pruning sets the pruning strategy: syncable, nothing, everything -# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) -# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -# everything: all saved states will be deleted, storing only the current state -pruning = "{{ .BaseConfig.Pruning }}" +# IndexEvents defines the set of events in the form {eventType}.{attributeKey}, +# which informs Tendermint what to index. If empty, all events will be indexed. +# +# Example: +# ["message.sender", "message.recipient"] +index-events = {{ .BaseConfig.IndexEvents }} + +############################################################################### +### Telemetry Configuration ### +############################################################################### + +[telemetry] + +# Prefixed with keys to separate services. +service-name = "{{ .Telemetry.ServiceName }}" + +# Enabled enables the application telemetry functionality. When enabled, +# an in-memory sink is also enabled by default. Operators may also enabled +# other sinks such as Prometheus. +enabled = {{ .Telemetry.Enabled }} + +# Enable prefixing gauge values with hostname. +enable-hostname = {{ .Telemetry.EnableHostname }} + +# Enable adding hostname to labels. +enable-hostname-label = {{ .Telemetry.EnableHostnameLabel }} + +# Enable adding service to labels. +enable-service-label = {{ .Telemetry.EnableServiceLabel }} + +# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. +prometheus-retention-time = {{ .Telemetry.PrometheusRetentionTime }} + +# GlobalLabels defines a global set of name/value label tuples applied to all +# metrics emitted using the wrapper functions defined in telemetry package. +# +# Example: +# [["chain_id", "cosmoshub-1"]] +global-labels = [{{ range $k, $v := .Telemetry.GlobalLabels }} + ["{{index $v 0 }}", "{{ index $v 1}}"],{{ end }} +] + +############################################################################### +### API Configuration ### +############################################################################### + +[api] + +# Enable defines if the API server should be enabled. +enable = {{ .API.Enable }} + +# Swagger defines if swagger documentation should automatically be registered. +swagger = {{ .API.Swagger }} + +# Address defines the API server to listen on. +address = "{{ .API.Address }}" + +# MaxOpenConnections defines the number of maximum open connections. +max-open-connections = {{ .API.MaxOpenConnections }} + +# RPCReadTimeout defines the Tendermint RPC read timeout (in seconds). +rpc-read-timeout = {{ .API.RPCReadTimeout }} + +# RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds). +rpc-write-timeout = {{ .API.RPCWriteTimeout }} + +# RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes). +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 }} + +############################################################################### +### gRPC Configuration ### +############################################################################### + +[grpc] + +# Enable defines if the gRPC server should be enabled. +enable = {{ .GRPC.Enable }} + +# Address defines the gRPC server address to bind to. +address = "{{ .GRPC.Address }}" + +############################################################################### +### State Sync Configuration ### +############################################################################### + +# State sync snapshots allow other nodes to rapidly join the network without replaying historical +# blocks, instead downloading and applying a snapshot of the application state at a given height. +[state-sync] + +# snapshot-interval specifies the block interval at which local state sync snapshots are +# taken (0 to disable). Must be a multiple of pruning-keep-every. +snapshot-interval = {{ .StateSync.SnapshotInterval }} + +# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). +snapshot-keep-recent = {{ .StateSync.SnapshotKeepRecent }} ` var configTemplate *template.Template func init() { var err error + tmpl := template.New("appConfigFileTemplate") + if configTemplate, err = tmpl.Parse(defaultConfigTemplate); err != nil { panic(err) } @@ -53,9 +177,10 @@ func init() { // ParseConfig retrieves the default environment configuration for the // application. -func ParseConfig() (*Config, error) { +func ParseConfig(v *viper.Viper) (*Config, error) { conf := DefaultConfig() - err := viper.Unmarshal(conf) + err := v.Unmarshal(conf) + return conf, err } diff --git a/server/constructors.go b/server/constructors.go deleted file mode 100644 index 341cb34eba27..000000000000 --- a/server/constructors.go +++ /dev/null @@ -1,43 +0,0 @@ -package server - -import ( - "encoding/json" - "io" - "os" - "path/filepath" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type ( - // AppCreator is a function that allows us to lazily initialize an - // application using various configurations. - AppCreator func(log.Logger, dbm.DB, io.Writer) abci.Application - - // AppExporter is a function that dumps all app state to - // JSON-serializable structure and returns the current validator set. - AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string) (json.RawMessage, []tmtypes.GenesisValidator, error) -) - -func openDB(rootDir string) (dbm.DB, error) { - dataDir := filepath.Join(rootDir, "data") - db, err := sdk.NewLevelDB("application", dataDir) - return db, err -} - -func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { - if traceWriterFile != "" { - w, err = os.OpenFile( - traceWriterFile, - os.O_WRONLY|os.O_APPEND|os.O_CREATE, - 0666, - ) - return - } - return -} diff --git a/server/constructors_test.go b/server/constructors_test.go index e1da004a63f9..c131c2c987de 100644 --- a/server/constructors_test.go +++ b/server/constructors_test.go @@ -5,23 +5,18 @@ import ( "testing" "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/tests" ) func Test_openDB(t *testing.T) { t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - _, err := openDB(dir) + _, err := openDB(t.TempDir()) require.NoError(t, err) } func Test_openTraceWriter(t *testing.T) { t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - fname := filepath.Join(dir, "logfile") + + fname := filepath.Join(t.TempDir(), "logfile") w, err := openTraceWriter(fname) require.NoError(t, err) require.NotNil(t, w) diff --git a/server/doc.go b/server/doc.go new file mode 100644 index 000000000000..befac1302ec3 --- /dev/null +++ b/server/doc.go @@ -0,0 +1,27 @@ +/* +The commands from the SDK are defined with `cobra` and configured with the +`viper` package. + +This takes place in the `InterceptConfigsPreRunHandler` function. +Since the `viper` package is used for configuration the precedence is dictated +by that package. That is + +1. Command line switches +2. Environment variables +3. Files from configuration values +4. Default values + +The global configuration instance exposed by the `viper` package is not +used by Cosmos SDK in this function. A new instance of `viper.Viper` is created +and the following is performed. The environmental variable prefix is set +to the current program name. Environmental variables consider the underscore +to be equivalent to the `.` or `-` character. This means that an configuration +value called `rpc.laddr` would be read from an environmental variable called +`MYTOOL_RPC_LADDR` if the current program name is `mytool`. + +Running the `InterceptConfigsPreRunHandler` also reads `app.toml` +and `config.toml` from the home directory under the `config` directory. +If `config.toml` or `app.toml` do not exist then those files are created +and populated with default values. +*/ +package server diff --git a/server/export.go b/server/export.go index e2e74943bec4..8fd618028ba0 100644 --- a/server/export.go +++ b/server/export.go @@ -8,39 +8,44 @@ import ( "os" "github.com/spf13/cobra" - "github.com/spf13/viper" + tmjson "github.com/tendermint/tendermint/libs/json" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" ) const ( - flagHeight = "height" - flagForZeroHeight = "for-zero-height" - flagJailWhitelist = "jail-whitelist" + FlagHeight = "height" + FlagForZeroHeight = "for-zero-height" + FlagJailAllowedAddrs = "jail-allowed-addrs" ) // ExportCmd dumps app state to JSON. -func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.Command { +func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "export", Short: "Export state to JSON", RunE: func(cmd *cobra.Command, args []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(flags.FlagHome)) + serverCtx := GetServerContextFromCmd(cmd) + config := serverCtx.Config - traceWriterFile := viper.GetString(flagTraceStore) + homeDir, _ := cmd.Flags().GetString(flags.FlagHome) + config.SetRoot(homeDir) + + if _, err := os.Stat(config.GenesisFile()); os.IsNotExist(err) { + return err + } db, err := openDB(config.RootDir) if err != nil { return err } - if isEmptyState(db) || appExporter == nil { - if _, err := fmt.Fprintln(os.Stderr, "WARNING: State is not initialized. Returning genesis file."); err != nil { + if appExporter == nil { + if _, err := fmt.Fprintln(os.Stderr, "WARNING: App exporter not defined. Returning genesis file."); err != nil { return err } @@ -53,44 +58,63 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C return nil } + traceWriterFile, _ := cmd.Flags().GetString(flagTraceStore) traceWriter, err := openTraceWriter(traceWriterFile) if err != nil { return err } - height := viper.GetInt64(flagHeight) - forZeroHeight := viper.GetBool(flagForZeroHeight) - jailWhiteList := viper.GetStringSlice(flagJailWhitelist) + height, _ := cmd.Flags().GetInt64(FlagHeight) + forZeroHeight, _ := cmd.Flags().GetBool(FlagForZeroHeight) + jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(FlagJailAllowedAddrs) - appState, validators, err := appExporter(ctx.Logger, db, traceWriter, height, forZeroHeight, jailWhiteList) + exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs, serverCtx.Viper) if err != nil { return fmt.Errorf("error exporting state: %v", err) } - doc, err := tmtypes.GenesisDocFromFile(ctx.Config.GenesisFile()) + doc, err := tmtypes.GenesisDocFromFile(serverCtx.Config.GenesisFile()) if err != nil { return err } - doc.AppState = appState - doc.Validators = validators + doc.AppState = exported.AppState + doc.Validators = exported.Validators + doc.InitialHeight = exported.Height + doc.ConsensusParams = &tmproto.ConsensusParams{ + Block: tmproto.BlockParams{ + MaxBytes: exported.ConsensusParams.Block.MaxBytes, + MaxGas: exported.ConsensusParams.Block.MaxGas, + TimeIotaMs: doc.ConsensusParams.Block.TimeIotaMs, + }, + Evidence: tmproto.EvidenceParams{ + MaxAgeNumBlocks: exported.ConsensusParams.Evidence.MaxAgeNumBlocks, + MaxAgeDuration: exported.ConsensusParams.Evidence.MaxAgeDuration, + MaxBytes: exported.ConsensusParams.Evidence.MaxBytes, + }, + Validator: tmproto.ValidatorParams{ + PubKeyTypes: exported.ConsensusParams.Validator.PubKeyTypes, + }, + } - encoded, err := codec.MarshalJSONIndent(cdc, doc) + // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc + // (except for stuff inside AppState). Inside AppState, we're free + // to encode as protobuf or amino. + encoded, err := tmjson.Marshal(doc) if err != nil { return err } - fmt.Println(string(sdk.MustSortJSON(encoded))) + cmd.Println(string(sdk.MustSortJSON(encoded))) return nil }, } + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().Int64(FlagHeight, -1, "Export state from a particular height (-1 means latest height)") + cmd.Flags().Bool(FlagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") + cmd.Flags().StringSlice(FlagJailAllowedAddrs, []string{}, "Comma-separated list of operator addresses of jailed validators to unjail") - cmd.Flags().Int64(flagHeight, -1, "Export state from a particular height (-1 means latest height)") - cmd.Flags().Bool(flagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") - cmd.Flags().StringSlice(flagJailWhitelist, []string{}, "List of validators to not jail state export") return cmd } - -func isEmptyState(db dbm.DB) bool { - return db.Stats()["leveldb.sstables"] == "" -} diff --git a/server/export_test.go b/server/export_test.go new file mode 100644 index 000000000000..d4e41ef8e886 --- /dev/null +++ b/server/export_test.go @@ -0,0 +1,203 @@ +package server_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "path" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/genutil" +) + +func TestExportCmd_ConsensusParams(t *testing.T) { + tempDir := t.TempDir() + + _, ctx, genDoc, cmd := setupApp(t, tempDir) + + output := &bytes.Buffer{} + cmd.SetOut(output) + cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)}) + require.NoError(t, cmd.ExecuteContext(ctx)) + + var exportedGenDoc tmtypes.GenesisDoc + err := tmjson.Unmarshal(output.Bytes(), &exportedGenDoc) + if err != nil { + t.Fatalf("error unmarshaling exported genesis doc: %s", err) + } + + require.Equal(t, genDoc.ConsensusParams.Block.TimeIotaMs, exportedGenDoc.ConsensusParams.Block.TimeIotaMs) + require.Equal(t, simapp.DefaultConsensusParams.Block.MaxBytes, exportedGenDoc.ConsensusParams.Block.MaxBytes) + require.Equal(t, simapp.DefaultConsensusParams.Block.MaxGas, exportedGenDoc.ConsensusParams.Block.MaxGas) + + require.Equal(t, simapp.DefaultConsensusParams.Evidence.MaxAgeDuration, exportedGenDoc.ConsensusParams.Evidence.MaxAgeDuration) + require.Equal(t, simapp.DefaultConsensusParams.Evidence.MaxAgeNumBlocks, exportedGenDoc.ConsensusParams.Evidence.MaxAgeNumBlocks) + + require.Equal(t, simapp.DefaultConsensusParams.Validator.PubKeyTypes, exportedGenDoc.ConsensusParams.Validator.PubKeyTypes) +} + +func TestExportCmd_HomeDir(t *testing.T) { + _, ctx, _, cmd := setupApp(t, t.TempDir()) + + cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, "foobar")}) + + err := cmd.ExecuteContext(ctx) + require.EqualError(t, err, "stat foobar/config/genesis.json: no such file or directory") +} + +func TestExportCmd_Height(t *testing.T) { + testCases := []struct { + name string + flags []string + fastForward int64 + expHeight int64 + }{ + { + "should export correct height", + []string{}, + 5, 6, + }, + { + "should export correct height with --height", + []string{ + fmt.Sprintf("--%s=%d", server.FlagHeight, 3), + }, + 5, 4, + }, + { + "should export height 0 with --for-zero-height", + []string{ + fmt.Sprintf("--%s=%s", server.FlagForZeroHeight, "true"), + }, + 2, 0, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tempDir := t.TempDir() + app, ctx, _, cmd := setupApp(t, tempDir) + + // Fast forward to block `tc.fastForward`. + for i := int64(2); i <= tc.fastForward; i++ { + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}}) + app.Commit() + } + + output := &bytes.Buffer{} + cmd.SetOut(output) + args := append(tc.flags, fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)) + cmd.SetArgs(args) + require.NoError(t, cmd.ExecuteContext(ctx)) + + var exportedGenDoc tmtypes.GenesisDoc + err := tmjson.Unmarshal(output.Bytes(), &exportedGenDoc) + if err != nil { + t.Fatalf("error unmarshaling exported genesis doc: %s", err) + } + + require.Equal(t, tc.expHeight, exportedGenDoc.InitialHeight) + }) + } + +} + +func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, *tmtypes.GenesisDoc, *cobra.Command) { + if err := createConfigFolder(tempDir); err != nil { + t.Fatalf("error creating config folder: %s", err) + } + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + db := dbm.NewMemDB() + encCfg := simapp.MakeTestEncodingConfig() + app := simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, tempDir, 0, encCfg, simapp.EmptyAppOptions{}) + + serverCtx := server.NewDefaultContext() + serverCtx.Config.RootDir = tempDir + + clientCtx := client.Context{}.WithJSONMarshaler(app.AppCodec()) + genDoc := newDefaultGenesisDoc(encCfg.Marshaler) + + require.NoError(t, saveGenesisFile(genDoc, serverCtx.Config.GenesisFile())) + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simapp.DefaultConsensusParams, + AppStateBytes: genDoc.AppState, + }, + ) + app.Commit() + + cmd := server.ExportCmd( + func(_ log.Logger, _ dbm.DB, _ io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOptons types.AppOptions) (types.ExportedApp, error) { + encCfg := simapp.MakeTestEncodingConfig() + + var simApp *simapp.SimApp + if height != -1 { + simApp = simapp.NewSimApp(logger, db, nil, false, map[int64]bool{}, "", 0, encCfg, appOptons) + + if err := simApp.LoadHeight(height); err != nil { + return types.ExportedApp{}, err + } + } else { + simApp = simapp.NewSimApp(logger, db, nil, true, map[int64]bool{}, "", 0, encCfg, appOptons) + } + + return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) + }, tempDir) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + return app, ctx, genDoc, cmd +} + +func createConfigFolder(dir string) error { + return os.Mkdir(path.Join(dir, "config"), 0700) +} + +func newDefaultGenesisDoc(cdc codec.Marshaler) *tmtypes.GenesisDoc { + genesisState := simapp.NewDefaultGenesisState(cdc) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + if err != nil { + panic(err) + } + + genDoc := &tmtypes.GenesisDoc{} + genDoc.ChainID = "theChainId" + genDoc.Validators = nil + genDoc.AppState = stateBytes + + return genDoc +} + +func saveGenesisFile(genDoc *tmtypes.GenesisDoc, dir string) error { + err := genutil.ExportGenesisFile(genDoc, dir) + if err != nil { + return errors.Wrap(err, "error creating file") + } + + return nil +} diff --git a/server/grpc/server.go b/server/grpc/server.go new file mode 100644 index 000000000000..d9d0be1a032c --- /dev/null +++ b/server/grpc/server.go @@ -0,0 +1,43 @@ +package grpc + +import ( + "fmt" + "net" + "time" + + "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(clientCtx client.Context, app types.Application, address string) (*grpc.Server, error) { + grpcSrv := grpc.NewServer() + app.RegisterGRPCServer(clientCtx, grpcSrv) + + // Reflection allows external clients to see what services and methods + // the gRPC server exposes. + reflection.Register(grpcSrv) + + listener, err := net.Listen("tcp", address) + if err != nil { + return nil, err + } + + errCh := make(chan error) + go func() { + err = grpcSrv.Serve(listener) + if err != nil { + errCh <- fmt.Errorf("failed to serve: %w", err) + } + }() + + select { + case err := <-errCh: + return nil, err + case <-time.After(5 * time.Second): // assume server started successfully + return grpcSrv, nil + } +} diff --git a/server/grpc/server_test.go b/server/grpc/server_test.go new file mode 100644 index 000000000000..7a58bef00dbe --- /dev/null +++ b/server/grpc/server_test.go @@ -0,0 +1,212 @@ +// +build norace + +package grpc_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + conn *grpc.ClientConn +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.cfg = network.DefaultConfig() + s.network = network.New(s.T(), s.cfg) + s.Require().NotNil(s.network) + + _, err := s.network.WaitForHeight(2) + s.Require().NoError(err) + + val0 := s.network.Validators[0] + s.conn, err = grpc.Dial( + val0.AppConfig.GRPC.Address, + grpc.WithInsecure(), // Or else we get "no transport security set" + ) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.conn.Close() + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGRPCServer_TestService() { + // gRPC query to test service should work + testClient := testdata.NewQueryClient(s.conn) + testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + s.Require().NoError(err) + s.Require().Equal("hello", testRes.Message) +} + +func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() { + val0 := s.network.Validators[0] + + // gRPC query to bank service should work + denom := fmt.Sprintf("%stoken", val0.Moniker) + bankClient := banktypes.NewQueryClient(s.conn) + var header metadata.MD + bankRes, err := bankClient.Balance( + context.Background(), + &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, + grpc.Header(&header), // Also fetch grpc header + ) + s.Require().NoError(err) + s.Require().Equal( + sdk.NewCoin(denom, s.network.Config.AccountTokens), + *bankRes.GetBalance(), + ) + blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) + s.Require().NotEmpty(blockHeight[0]) // Should contain the block height + + // Request metadata should work + bankRes, err = bankClient.Balance( + metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, "1"), // Add metadata to request + &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, + grpc.Header(&header), + ) + blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) + s.Require().NotEmpty(blockHeight[0]) // blockHeight is []string, first element is block height. +} + +func (s *IntegrationTestSuite) TestGRPCServer_Reflection() { + // Test server reflection + reflectClient := rpb.NewServerReflectionClient(s.conn) + stream, err := reflectClient.ServerReflectionInfo(context.Background(), grpc.WaitForReady(true)) + s.Require().NoError(err) + s.Require().NoError(stream.Send(&rpb.ServerReflectionRequest{ + MessageRequest: &rpb.ServerReflectionRequest_ListServices{}, + })) + res, err := stream.Recv() + s.Require().NoError(err) + services := res.GetListServicesResponse().Service + servicesMap := make(map[string]bool) + for _, s := range services { + servicesMap[s.Name] = true + } + // Make sure the following services are present + s.Require().True(servicesMap["cosmos.bank.v1beta1.Query"]) +} + +func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() { + // Query the tx via gRPC without pagination. This used to panic, see + // https://github.com/cosmos/cosmos-sdk/issues/8038. + txServiceClient := txtypes.NewServiceClient(s.conn) + _, err := txServiceClient.GetTxsEvent( + context.Background(), + &tx.GetTxsEventRequest{ + Events: []string{"message.action='send'"}, + }, + ) + 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, + }, + ) + s.Require().NoError(err) + s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) +} + +// Test and enforce that we upfront reject any connections to baseapp containing +// invalid initial x-cosmos-block-height that aren't positive and in the range [0, max(int64)] +// See issue https://github.com/cosmos/cosmos-sdk/issues/7662. +func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { + t := s.T() + val0 := s.network.Validators[0] + + // We should reject connections with invalid block heights off the bat. + invalidHeightStrs := []struct { + value string + wantErr string + }{ + {"-1", "\"x-cosmos-block-height\" must be >= 0"}, + {"9223372036854775808", "value out of range"}, // > max(int64) by 1 + {"-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 + } + for _, tt := range invalidHeightStrs { + t.Run(tt.value, func(t *testing.T) { + conn, err := grpc.Dial( + val0.AppConfig.GRPC.Address, + grpc.WithInsecure(), // Or else we get "no transport security set" + ) + defer conn.Close() + + testClient := testdata.NewQueryClient(conn) + ctx := metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, tt.value) + testRes, err := testClient.Echo(ctx, &testdata.EchoRequest{Message: "hello"}) + require.Error(t, err) + require.Nil(t, testRes) + require.Contains(t, err.Error(), tt.wantErr) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/server/init.go b/server/init.go index c9a95d7ab7e9..f14e236cdf8b 100644 --- a/server/init.go +++ b/server/init.go @@ -3,40 +3,47 @@ package server import ( "fmt" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keyring" - clkeys "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" ) // GenerateCoinKey returns the address of a public key, along with the secret // phrase to recover the private key. -func GenerateCoinKey() (sdk.AccAddress, string, error) { - +func GenerateCoinKey(algo keyring.SignatureAlgo) (sdk.AccAddress, string, error) { // generate a private key, with recovery phrase - info, secret, err := clkeys.NewInMemoryKeyBase().CreateMnemonic( - "name", keys.English, "pass", keys.Secp256k1) + info, secret, err := keyring.NewInMemory().NewMnemonic("name", keyring.English, sdk.FullFundraiserPath, algo) if err != nil { return sdk.AccAddress([]byte{}), "", err } - addr := info.GetPubKey().Address() - return sdk.AccAddress(addr), secret, nil + return sdk.AccAddress(info.GetPubKey().Address()), secret, nil } // GenerateSaveCoinKey returns the address of a public key, along with the secret // phrase to recover the private key. -func GenerateSaveCoinKey(keybase keys.Keybase, keyName, keyPass string, overwrite bool) (sdk.AccAddress, string, error) { +func GenerateSaveCoinKey(keybase keyring.Keyring, keyName string, overwrite bool, algo keyring.SignatureAlgo) (sdk.AccAddress, string, error) { + exists := false + _, err := keybase.Key(keyName) + if err == nil { + exists = true + } + // ensure no overwrite - if !overwrite { - _, err := keybase.Get(keyName) - if err == nil { + if !overwrite && exists { + return sdk.AccAddress([]byte{}), "", fmt.Errorf( + "key already exists, overwrite is disabled") + } + + // generate a private key, with recovery phrase + if exists { + err = keybase.Delete(keyName) + if err != nil { return sdk.AccAddress([]byte{}), "", fmt.Errorf( - "key already exists, overwrite is disabled") + "failed to overwrite key") } } - // generate a private key, with recovery phrase - info, secret, err := keybase.CreateMnemonic(keyName, keys.English, keyPass, keys.Secp256k1) + info, secret, err := keybase.NewMnemonic(keyName, keyring.English, sdk.FullFundraiserPath, algo) if err != nil { return sdk.AccAddress([]byte{}), "", err } diff --git a/server/init_test.go b/server/init_test.go index 5d353ddd3540..d7439fe115f4 100644 --- a/server/init_test.go +++ b/server/init_test.go @@ -5,63 +5,59 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/client/keys" - crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/types" ) func TestGenerateCoinKey(t *testing.T) { t.Parallel() - addr, mnemonic, err := server.GenerateCoinKey() + addr, mnemonic, err := server.GenerateCoinKey(hd.Secp256k1) require.NoError(t, err) // Test creation - info, err := keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", crkeys.CreateHDPath(0, 0).String(), crkeys.Secp256k1) + info, err := keyring.NewInMemory().NewAccount("xxx", mnemonic, "", hd.NewFundraiserParams(0, types.GetConfig().GetCoinType(), 0).String(), hd.Secp256k1) require.NoError(t, err) require.Equal(t, addr, info.GetAddress()) } func TestGenerateSaveCoinKey(t *testing.T) { t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() // clean after itself - kb, err := crkeys.NewKeyring(t.Name(), "test", dir, nil) + kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil) require.NoError(t, err) - addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", "012345678", false) + addr, mnemonic, err := server.GenerateSaveCoinKey(kb, "keyname", false, hd.Secp256k1) require.NoError(t, err) // Test key was actually saved - info, err := kb.Get("keyname") + info, err := kb.Key("keyname") require.NoError(t, err) require.Equal(t, addr, info.GetAddress()) // Test in-memory recovery - info, err = keys.NewInMemoryKeyBase().CreateAccount("xxx", mnemonic, "", "012345678", crkeys.CreateHDPath(0, 0).String(), crkeys.Secp256k1) + info, err = keyring.NewInMemory().NewAccount("xxx", mnemonic, "", hd.NewFundraiserParams(0, types.GetConfig().GetCoinType(), 0).String(), hd.Secp256k1) require.NoError(t, err) require.Equal(t, addr, info.GetAddress()) } func TestGenerateSaveCoinKeyOverwriteFlag(t *testing.T) { t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() // clean after itself - kb, err := crkeys.NewKeyring(t.Name(), "test", dir, nil) + kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil) require.NoError(t, err) keyname := "justakey" - addr1, _, err := server.GenerateSaveCoinKey(kb, keyname, "012345678", false) + addr1, _, err := server.GenerateSaveCoinKey(kb, keyname, false, hd.Secp256k1) require.NoError(t, err) // Test overwrite with overwrite=false - _, _, err = server.GenerateSaveCoinKey(kb, keyname, "012345678", false) + _, _, err = server.GenerateSaveCoinKey(kb, keyname, false, hd.Secp256k1) require.Error(t, err) // Test overwrite with overwrite=true - addr2, _, err := server.GenerateSaveCoinKey(kb, keyname, "012345678", true) + addr2, _, err := server.GenerateSaveCoinKey(kb, keyname, true, hd.Secp256k1) require.NoError(t, err) require.NotEqual(t, addr1, addr2) diff --git a/server/logger.go b/server/logger.go new file mode 100644 index 000000000000..e6f6f8c1110f --- /dev/null +++ b/server/logger.go @@ -0,0 +1,55 @@ +package server + +import ( + "github.com/rs/zerolog" + tmlog "github.com/tendermint/tendermint/libs/log" +) + +var _ tmlog.Logger = (*ZeroLogWrapper)(nil) + +// ZeroLogWrapper provides a wrapper around a zerolog.Logger instance. It implements +// Tendermint's Logger interface. +type ZeroLogWrapper struct { + zerolog.Logger +} + +// Info implements Tendermint's Logger interface and logs with level INFO. A set +// of key/value tuples may be provided to add context to the log. The number of +// tuples must be even and the key of the tuple must be a string. +func (z ZeroLogWrapper) Info(msg string, keyVals ...interface{}) { + z.Logger.Info().Fields(getLogFields(keyVals...)).Msg(msg) +} + +// Error implements Tendermint's Logger interface and logs with level ERR. A set +// of key/value tuples may be provided to add context to the log. The number of +// tuples must be even and the key of the tuple must be a string. +func (z ZeroLogWrapper) Error(msg string, keyVals ...interface{}) { + z.Logger.Error().Fields(getLogFields(keyVals...)).Msg(msg) +} + +// Debug implements Tendermint's Logger interface and logs with level DEBUG. A set +// of key/value tuples may be provided to add context to the log. The number of +// tuples must be even and the key of the tuple must be a string. +func (z ZeroLogWrapper) Debug(msg string, keyVals ...interface{}) { + z.Logger.Debug().Fields(getLogFields(keyVals...)).Msg(msg) +} + +// With returns a new wrapped logger with additional context provided by a set +// of key/value tuples. The number of tuples must be even and the key of the +// tuple must be a string. +func (z ZeroLogWrapper) With(keyVals ...interface{}) tmlog.Logger { + return ZeroLogWrapper{z.Logger.With().Fields(getLogFields(keyVals...)).Logger()} +} + +func getLogFields(keyVals ...interface{}) map[string]interface{} { + if len(keyVals)%2 != 0 { + return nil + } + + fields := make(map[string]interface{}) + for i := 0; i < len(keyVals); i += 2 { + fields[keyVals[i].(string)] = keyVals[i+1] + } + + return fields +} diff --git a/server/mock/app.go b/server/mock/app.go index d1e1146d6a57..e3fdd2c91477 100644 --- a/server/mock/app.go +++ b/server/mock/app.go @@ -26,7 +26,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { } // Capabilities key to access the main KVStore. - capKeyMainStore := sdk.NewKVStoreKey(bam.MainStoreKey) + capKeyMainStore := sdk.NewKVStoreKey("main") // Create BaseApp. baseApp := bam.NewBaseApp("kvstore", logger, db, decodeTx) @@ -36,11 +36,11 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { baseApp.SetInitChainer(InitChainer(capKeyMainStore)) - // Set a handler Route. - baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) + // Set a Route. + baseApp.Router().AddRoute(sdk.NewRoute("kvstore", KVStoreHandler(capKeyMainStore))) // Load latest version. - if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { + if err := baseApp.LoadLatestVersion(); err != nil { return nil, err } @@ -103,7 +103,7 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci // AppGenState can be passed into InitCmd, returns a static string of a few // key-values that can be parsed by InitChainer -func AppGenState(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (appState json. +func AppGenState(_ *codec.LegacyAmino, _ types.GenesisDoc, _ []json.RawMessage) (appState json. RawMessage, err error) { appState = json.RawMessage(`{ "values": [ @@ -121,7 +121,7 @@ func AppGenState(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) (appSt } // AppGenStateEmpty returns an empty transaction state for mocking. -func AppGenStateEmpty(_ *codec.Codec, _ types.GenesisDoc, _ []json.RawMessage) ( +func AppGenStateEmpty(_ *codec.LegacyAmino, _ types.GenesisDoc, _ []json.RawMessage) ( appState json.RawMessage, err error) { appState = json.RawMessage(``) return diff --git a/server/mock/app_test.go b/server/mock/app_test.go index 3dd451970ccd..2741925df026 100644 --- a/server/mock/app_test.go +++ b/server/mock/app_test.go @@ -3,11 +3,10 @@ package mock import ( "testing" - "github.com/tendermint/tendermint/types" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/types" ) // TestInitApp makes sure we can initialize this thing without an error @@ -57,7 +56,7 @@ func TestDeliverTx(t *testing.T) { tx := NewTx(key, value) txBytes := tx.GetSignBytes() - header := abci.Header{ + header := tmproto.Header{ AppHash: []byte("apphash"), Height: 1, } diff --git a/server/mock/store.go b/server/mock/store.go index bf8e66a4dc07..d731767f192a 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -55,6 +55,10 @@ func (ms multiStore) SetPruning(opts sdk.PruningOptions) { panic("not implemented") } +func (ms multiStore) GetPruning() sdk.PruningOptions { + panic("not implemented") +} + func (ms multiStore) GetCommitKVStore(key sdk.StoreKey) sdk.CommitKVStore { panic("not implemented") } @@ -99,6 +103,20 @@ func (ms multiStore) SetInterBlockCache(_ sdk.MultiStorePersistentCache) { panic("not implemented") } +func (ms multiStore) SetInitialVersion(version int64) error { + panic("not implemented") +} + +func (ms multiStore) Snapshot(height uint64, format uint32) (<-chan io.ReadCloser, error) { + panic("not implemented") +} + +func (ms multiStore) Restore( + height uint64, format uint32, chunks <-chan io.ReadCloser, ready chan<- struct{}, +) error { + panic("not implemented") +} + var _ sdk.KVStore = kvStore{} type kvStore struct { @@ -131,6 +149,7 @@ func (kv kvStore) Has(key []byte) bool { } func (kv kvStore) Set(key, value []byte) { + store.AssertValidKey(key) kv.store[string(key)] = value } diff --git a/server/mock/store_test.go b/server/mock/store_test.go index 28959f03bef7..b945cd30599f 100644 --- a/server/mock/store_test.go +++ b/server/mock/store_test.go @@ -30,4 +30,6 @@ func TestStore(t *testing.T) { require.Equal(t, v, store.Get(k)) store.Delete(k) require.False(t, store.Has(k)) + require.Panics(t, func() { store.Set([]byte(""), v) }, "setting an empty key should panic") + require.Panics(t, func() { store.Set(nil, v) }, "setting a nil key should panic") } diff --git a/server/mock/tx.go b/server/mock/tx.go index 27441051ce8a..0cb79c28986f 100644 --- a/server/mock/tx.go +++ b/server/mock/tx.go @@ -16,7 +16,13 @@ type kvstoreTx struct { bytes []byte } +// dummy implementation of proto.Message +func (msg kvstoreTx) Reset() {} +func (msg kvstoreTx) String() string { return "TODO" } +func (msg kvstoreTx) ProtoMessage() {} + var _ sdk.Tx = kvstoreTx{} +var _ sdk.Msg = kvstoreTx{} func NewTx(key, value string) kvstoreTx { bytes := fmt.Sprintf("%s=%s", key, value) diff --git a/server/pruning.go b/server/pruning.go new file mode 100644 index 000000000000..44aa4ba2d1e6 --- /dev/null +++ b/server/pruning.go @@ -0,0 +1,40 @@ +package server + +import ( + "fmt" + "strings" + + "github.com/spf13/cast" + + "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" +) + +// GetPruningOptionsFromFlags parses command flags and returns the correct +// PruningOptions. If a pruning strategy is provided, that will be parsed and +// returned, otherwise, it is assumed custom pruning options are provided. +func GetPruningOptionsFromFlags(appOpts types.AppOptions) (storetypes.PruningOptions, error) { + strategy := strings.ToLower(cast.ToString(appOpts.Get(FlagPruning))) + + switch strategy { + case storetypes.PruningOptionDefault, storetypes.PruningOptionNothing, storetypes.PruningOptionEverything: + return storetypes.NewPruningOptionsFromString(strategy), nil + + case storetypes.PruningOptionCustom: + opts := storetypes.NewPruningOptions( + cast.ToUint64(appOpts.Get(FlagPruningKeepRecent)), + cast.ToUint64(appOpts.Get(FlagPruningKeepEvery)), + cast.ToUint64(appOpts.Get(FlagPruningInterval)), + ) + + if err := opts.Validate(); err != nil { + return opts, fmt.Errorf("invalid custom pruning options: %w", err) + } + + return opts, nil + + default: + return store.PruningOptions{}, fmt.Errorf("unknown pruning strategy %s", strategy) + } +} diff --git a/server/pruning_test.go b/server/pruning_test.go new file mode 100644 index 000000000000..baef9b59163c --- /dev/null +++ b/server/pruning_test.go @@ -0,0 +1,73 @@ +package server + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func TestGetPruningOptionsFromFlags(t *testing.T) { + tests := []struct { + name string + initParams func() *viper.Viper + expectedOptions types.PruningOptions + wantErr bool + }{ + { + name: FlagPruning, + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionNothing) + return v + }, + expectedOptions: types.PruneNothing, + }, + { + name: "custom pruning options", + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionCustom) + v.Set(FlagPruningKeepRecent, 1234) + v.Set(FlagPruningKeepEvery, 4321) + v.Set(FlagPruningInterval, 10) + + return v + }, + expectedOptions: types.PruningOptions{ + KeepRecent: 1234, + KeepEvery: 4321, + Interval: 10, + }, + }, + { + name: types.PruningOptionDefault, + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionDefault) + return v + }, + expectedOptions: types.PruneDefault, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(j *testing.T) { + viper.Reset() + viper.SetDefault(FlagPruning, types.PruningOptionDefault) + v := tt.initParams() + + opts, err := GetPruningOptionsFromFlags(v) + if tt.wantErr { + require.Error(t, err) + return + } + + require.Equal(t, tt.expectedOptions, opts) + }) + } +} diff --git a/server/start.go b/server/start.go index fc8aa0219f3a..0763996d89b0 100644 --- a/server/start.go +++ b/server/start.go @@ -6,9 +6,9 @@ import ( "fmt" "os" "runtime/pprof" + "time" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tmos "github.com/tendermint/tendermint/libs/os" @@ -16,36 +16,71 @@ import ( "github.com/tendermint/tendermint/p2p" 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" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + "github.com/cosmos/cosmos-sdk/server/types" + storetypes "github.com/cosmos/cosmos-sdk/store/types" ) // Tendermint full-node start flags const ( flagWithTendermint = "with-tendermint" flagAddress = "address" + flagTransport = "transport" flagTraceStore = "trace-store" - flagPruning = "pruning" flagCPUProfile = "cpu-profile" FlagMinGasPrices = "minimum-gas-prices" FlagHaltHeight = "halt-height" FlagHaltTime = "halt-time" FlagInterBlockCache = "inter-block-cache" FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades" + FlagTrace = "trace" + FlagInvCheckPeriod = "inv-check-period" + + FlagPruning = "pruning" + FlagPruningKeepRecent = "pruning-keep-recent" + FlagPruningKeepEvery = "pruning-keep-every" + FlagPruningInterval = "pruning-interval" + FlagIndexEvents = "index-events" + FlagMinRetainBlocks = "min-retain-blocks" +) + +// GRPC-related flags. +const ( + flagGRPCEnable = "grpc.enable" + flagGRPCAddress = "grpc.address" +) + +// State sync-related flags. +const ( + FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval" + FlagStateSyncSnapshotKeepRecent = "state-sync.snapshot-keep-recent" ) // StartCmd runs the service passed in, either stand-alone or in-process with // Tendermint. -func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { +func StartCmd(appCreator types.AppCreator, defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Run the full node", Long: `Run the full node application with Tendermint in or out of process. By default, the application will run with Tendermint in process. -Pruning options can be provided via the '--pruning' flag. The options are as follows: +Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent', +'pruning-keep-every', and 'pruning-interval' together. + +For '--pruning' the options are as follows: -syncable: only those states not needed for state syncing will be deleted (flushes every 100th to disk and keeps every 10000th) +default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -everything: all saved states will be deleted, storing only the current state +everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals +custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During the ABCI Commit phase, the node will check if the current block height is greater than or equal to @@ -56,56 +91,92 @@ will not be able to commit subsequent blocks. For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag which accepts a path for the resulting pprof file. `, - RunE: func(cmd *cobra.Command, args []string) error { - if !viper.GetBool(flagWithTendermint) { - ctx.Logger.Info("starting ABCI without Tendermint") - return startStandAlone(ctx, appCreator) - } + PreRunE: func(cmd *cobra.Command, _ []string) error { + serverCtx := GetServerContextFromCmd(cmd) - ctx.Logger.Info("starting ABCI with Tendermint") + // Bind flags to the Context's Viper so the app construction can set + // options accordingly. + serverCtx.Viper.BindPFlags(cmd.Flags()) - _, err := startInProcess(ctx, appCreator) + _, err := GetPruningOptionsFromFlags(serverCtx.Viper) return err }, + RunE: func(cmd *cobra.Command, _ []string) error { + serverCtx := GetServerContextFromCmd(cmd) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + withTM, _ := cmd.Flags().GetBool(flagWithTendermint) + if !withTM { + serverCtx.Logger.Info("starting ABCI without Tendermint") + return startStandAlone(serverCtx, appCreator) + } + + serverCtx.Logger.Info("starting ABCI with Tendermint") + + // amino is needed here for backwards compatibility of REST routes + err = startInProcess(serverCtx, clientCtx, appCreator) + errCode, ok := err.(ErrorCode) + if !ok { + return err + } + + serverCtx.Logger.Debug(fmt.Sprintf("received quit signal: %d", errCode.Code)) + return nil + }, } - // core flags for the ABCI application + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint") cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") + cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc") cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") - cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything") - cmd.Flags().String( - FlagMinGasPrices, "", - "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)", - ) + cmd.Flags().String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)") cmd.Flags().IntSlice(FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") cmd.Flags().Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node") cmd.Flags().Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node") cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching") cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") + cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log") + cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") + cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") + cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')") + cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") + cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") + cmd.Flags().Uint64(FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks") + + 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().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval") + cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") // add support for all Tendermint-specific command line options tcmd.AddNodeFlags(cmd) return cmd } -func startStandAlone(ctx *Context, appCreator AppCreator) error { - addr := viper.GetString(flagAddress) - home := viper.GetString("home") - traceWriterFile := viper.GetString(flagTraceStore) +func startStandAlone(ctx *Context, appCreator types.AppCreator) error { + addr := ctx.Viper.GetString(flagAddress) + transport := ctx.Viper.GetString(flagTransport) + home := ctx.Viper.GetString(flags.FlagHome) db, err := openDB(home) if err != nil { return err } + + traceWriterFile := ctx.Viper.GetString(flagTraceStore) traceWriter, err := openTraceWriter(traceWriterFile) if err != nil { return err } - app := appCreator(ctx.Logger, db, traceWriter) + app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) - svr, err := server.NewServer(addr, "socket", app) + svr, err := server.NewServer(addr, transport, app) if err != nil { return fmt.Errorf("error creating listener: %v", err) } @@ -117,80 +188,129 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { tmos.Exit(err.Error()) } - tmos.TrapSignal(ctx.Logger, func() { - // cleanup - err = svr.Stop() - if err != nil { + defer func() { + if err = svr.Stop(); err != nil { tmos.Exit(err.Error()) } - }) + }() - // run forever (the node will not be returned) - select {} + // Wait for SIGINT or SIGTERM signal + return WaitForQuitSignals() } -func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { +// legacyAminoCdc is used for the legacy REST API +func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.AppCreator) error { cfg := ctx.Config home := cfg.RootDir + var cpuProfileCleanup func() + + if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { + f, err := os.Create(cpuProfile) + if err != nil { + return err + } - traceWriterFile := viper.GetString(flagTraceStore) + ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) + if err := pprof.StartCPUProfile(f); err != nil { + return err + } + + cpuProfileCleanup = func() { + ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) + pprof.StopCPUProfile() + f.Close() + } + } + + traceWriterFile := ctx.Viper.GetString(flagTraceStore) db, err := openDB(home) if err != nil { - return nil, err + return err } traceWriter, err := openTraceWriter(traceWriterFile) if err != nil { - return nil, err + return err } - app := appCreator(ctx.Logger, db, traceWriter) + app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) if err != nil { - return nil, err + return err } - // create & start tendermint node + genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) tmNode, err := node.NewNode( cfg, pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), nodeKey, proxy.NewLocalClientCreator(app), - node.DefaultGenesisDocProviderFunc(cfg), + genDocProvider, node.DefaultDBProvider, node.DefaultMetricsProvider(cfg.Instrumentation), - ctx.Logger.With("module", "node"), + ctx.Logger, ) if err != nil { - return nil, err + return err } + ctx.Logger.Debug("initialization: tmNode created") if err := tmNode.Start(); err != nil { - return nil, err + return err } + ctx.Logger.Debug("initialization: tmNode started") - var cpuProfileCleanup func() + config := config.GetConfig(ctx.Viper) - if cpuProfile := viper.GetString(flagCPUProfile); cpuProfile != "" { - f, err := os.Create(cpuProfile) + // Add the tx service to the gRPC router. We only need to register this + // service if API or gRPC is enabled, and avoid doing so in the general + // case, because it spawns a new local tendermint RPC client. + if config.API.Enable || config.GRPC.Enable { + clientCtx = clientCtx.WithClient(local.New(tmNode)) + + app.RegisterTxService(clientCtx) + app.RegisterTendermintService(clientCtx) + } + + var apiSrv *api.Server + + if config.API.Enable { + genDoc, err := genDocProvider() if err != nil { - return nil, err + return err } - ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) - if err := pprof.StartCPUProfile(f); err != nil { - return nil, err + clientCtx := clientCtx. + WithHomeDir(home). + WithChainID(genDoc.ChainID) + + apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server")) + app.RegisterAPIRoutes(apiSrv, config.API) + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(config); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(5 * time.Second): // assume server started successfully } + } - cpuProfileCleanup = func() { - ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) - pprof.StopCPUProfile() - f.Close() + var grpcSrv *grpc.Server + if config.GRPC.Enable { + grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address) + if err != nil { + return err } } - TrapSignal(func() { + defer func() { if tmNode.IsRunning() { _ = tmNode.Stop() } @@ -199,9 +319,17 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { cpuProfileCleanup() } + if apiSrv != nil { + _ = apiSrv.Close() + } + + if grpcSrv != nil { + grpcSrv.Stop() + } + ctx.Logger.Info("exiting...") - }) + }() - // run forever (the node will not be returned) - select {} + // Wait for SIGINT or SIGTERM signal + return WaitForQuitSignals() } diff --git a/server/test_helpers.go b/server/test_helpers.go index dc52f0c1f96a..0bc6bac8014e 100644 --- a/server/test_helpers.go +++ b/server/test_helpers.go @@ -2,15 +2,7 @@ package server import ( "fmt" - "io/ioutil" "net" - "os" - "testing" - - "github.com/cosmos/cosmos-sdk/client/flags" - - "github.com/spf13/viper" - "github.com/stretchr/testify/require" ) // Get a free address for a test tendermint server @@ -36,21 +28,3 @@ func FreeTCPAddr() (addr, port string, err error) { addr = fmt.Sprintf("tcp://0.0.0.0:%s", port) return } - -// SetupViper creates a homedir to run inside, -// and returns a cleanup function to defer -func SetupViper(t *testing.T) func() { - rootDir, err := ioutil.TempDir("", "mock-sdk-cmd") - require.Nil(t, err) - viper.Set(flags.FlagHome, rootDir) - viper.Set(flags.FlagName, "moniker") - return func() { - err := os.RemoveAll(rootDir) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - } -} - -// DONTCOVER diff --git a/server/tm_cmds.go b/server/tm_cmds.go index f52e1459ab4a..854d4597fee8 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -4,65 +4,71 @@ package server import ( "fmt" - - "github.com/cosmos/cosmos-sdk/client/attestation" + "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" tversion "github.com/tendermint/tendermint/version" + yaml "gopkg.in/yaml.v2" "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) // ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout -func ShowNodeIDCmd(ctx *Context) *cobra.Command { +func ShowNodeIDCmd() *cobra.Command { return &cobra.Command{ Use: "show-node-id", Short: "Show this node's ID", RunE: func(cmd *cobra.Command, args []string) error { - cfg := ctx.Config - nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) + serverCtx := GetServerContextFromCmd(cmd) + cfg := serverCtx.Config + + nodeKey, err := p2p.LoadNodeKey(cfg.NodeKeyFile()) if err != nil { return err } + fmt.Println(nodeKey.ID()) return nil }, } } -// ShowValidator - ported from Tendermint, show this node's validator info -func ShowValidatorCmd(ctx *Context) *cobra.Command { +// ShowValidatorCmd - ported from Tendermint, show this node's validator info +func ShowValidatorCmd() *cobra.Command { cmd := cobra.Command{ Use: "show-validator", Short: "Show this node's tendermint validator info", RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := GetServerContextFromCmd(cmd) + cfg := serverCtx.Config - cfg := ctx.Config - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valPubKey, err := privValidator.GetPubKey() if err != nil { return err } - if viper.GetString(cli.OutputFlag) == "json" { + output, _ := cmd.Flags().GetString(cli.OutputFlag) + if strings.ToLower(output) == "json" { return printlnJSON(valPubKey) } - pubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey) + pubkey, err := cryptocodec.FromTmPubKeyInterface(valPubKey) + if err != nil { + return err + } + pubkeyBech32, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, pubkey) if err != nil { return err } - fmt.Println(pubkey) + fmt.Println(pubkeyBech32) return nil }, } @@ -72,18 +78,19 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { } // ShowAddressCmd - show this node's validator address -func ShowAddressCmd(ctx *Context) *cobra.Command { +func ShowAddressCmd() *cobra.Command { cmd := &cobra.Command{ Use: "show-address", Short: "Shows this node's tendermint validator consensus address", RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := GetServerContextFromCmd(cmd) + cfg := serverCtx.Config - cfg := ctx.Config - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress()) - if viper.GetString(cli.OutputFlag) == "json" { + output, _ := cmd.Flags().GetString(cli.OutputFlag) + if strings.ToLower(output) == "json" { return printlnJSON(valConsAddr) } @@ -97,25 +104,24 @@ func ShowAddressCmd(ctx *Context) *cobra.Command { } // VersionCmd prints tendermint and ABCI version numbers. -func VersionCmd(ctx *Context) *cobra.Command { - cmd := &cobra.Command{ +func VersionCmd() *cobra.Command { + return &cobra.Command{ Use: "version", Short: "Print tendermint libraries' version", Long: `Print protocols' and libraries' version numbers against which this app has been compiled. `, RunE: func(cmd *cobra.Command, args []string) error { - bs, err := yaml.Marshal(&struct { Tendermint string ABCI string BlockProtocol uint64 P2PProtocol uint64 }{ - Tendermint: tversion.Version, + Tendermint: tversion.TMCoreSemVer, ABCI: tversion.ABCIVersion, - BlockProtocol: tversion.BlockProtocol.Uint64(), - P2PProtocol: tversion.P2PProtocol.Uint64(), + BlockProtocol: tversion.BlockProtocol, + P2PProtocol: tversion.P2PProtocol, }) if err != nil { return err @@ -125,52 +131,32 @@ against which this app has been compiled. return nil }, } - return cmd } func printlnJSON(v interface{}) error { - cdc := codec.New() - codec.RegisterCrypto(cdc) + cdc := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(cdc) + marshalled, err := cdc.MarshalJSON(v) if err != nil { return err } + fmt.Println(string(marshalled)) return nil } // UnsafeResetAllCmd - extension of the tendermint command, resets initialization -func UnsafeResetAllCmd(ctx *Context) *cobra.Command { +func UnsafeResetAllCmd() *cobra.Command { return &cobra.Command{ Use: "unsafe-reset-all", - Short: "Resets the blockchain database, removes address book files, and resets priv_validator.json to the genesis state", - RunE: func(cmd *cobra.Command, args []string) error { - cfg := ctx.Config - tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile(), ctx.Logger) - return nil - }, - } -} - -func AttestationCmd(ctx *Context) *cobra.Command { - cmd := &cobra.Command{ - Use: "attestation", - Short: "Create an offline proof that you are in control of this validator address", + Short: "Resets the blockchain database, removes address book files, and resets data/priv_validator_state.json to the genesis state", RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := GetServerContextFromCmd(cmd) + cfg := serverCtx.Config - cfg := ctx.Config - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) - - att, err := attestation.NewAttestation(privValidator.Key.PrivKey) - if err != nil { - return err - } - - fmt.Println(att.String()) + tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile(), serverCtx.Logger) return nil }, } - - return cmd } diff --git a/server/types/app.go b/server/types/app.go new file mode 100644 index 000000000000..ba702074b2a6 --- /dev/null +++ b/server/types/app.go @@ -0,0 +1,74 @@ +package types + +import ( + "encoding/json" + "io" + + "github.com/gogo/protobuf/grpc" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" +) + +type ( + // AppOptions defines an interface that is passed into an application + // constructor, typically used to set BaseApp options that are either supplied + // via config file or through CLI arguments/flags. The underlying implementation + // is defined by the server package and is typically implemented via a Viper + // literal defined on the server Context. Note, casting Get calls may not yield + // the expected types and could result in type assertion errors. It is recommend + // to either use the cast package or perform manual conversion for safety. + AppOptions interface { + Get(string) interface{} + } + + // Application defines an application interface that wraps abci.Application. + // The interface defines the necessary contracts to be implemented in order + // to fully bootstrap and start an application. + Application interface { + abci.Application + + RegisterAPIRoutes(*api.Server, config.APIConfig) + + // RegisterGRPCServer registers gRPC services directly with the gRPC + // server. + RegisterGRPCServer(client.Context, grpc.Server) + + // RegisterTxService registers the gRPC Query service for tx (such as tx + // simulation, fetching txs by hash...). + RegisterTxService(clientCtx client.Context) + + // RegisterTendermintService registers the gRPC Query service for tendermint queries. + RegisterTendermintService(clientCtx client.Context) + } + + // AppCreator is a function that allows us to lazily initialize an + // application using various configurations. + AppCreator func(log.Logger, dbm.DB, io.Writer, AppOptions) Application + + // ModuleInitFlags takes a start command and adds modules specific init flags. + ModuleInitFlags func(startCmd *cobra.Command) + + // ExportedApp represents an exported app state, along with + // validators, consensus params and latest app height. + ExportedApp struct { + // AppState is the application state as JSON. + AppState json.RawMessage + // Validators is the exported validator set. + Validators []tmtypes.GenesisValidator + // Height is the app's latest block height. + Height int64 + // ConsensusParams are the exported consensus params for ABCI. + ConsensusParams *abci.ConsensusParams + } + + // AppExporter is a function that dumps all app state to + // JSON-serializable structure and returns the current validator set. + AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string, AppOptions) (ExportedApp, error) +) diff --git a/server/util.go b/server/util.go index 656b4c56dbc1..68f1c4828166 100644 --- a/server/util.go +++ b/server/util.go @@ -1,172 +1,277 @@ package server import ( - "encoding/json" + "errors" + "fmt" + "io" "net" "os" "os/signal" + "path" "path/filepath" + "strconv" + "strings" "syscall" "time" - "errors" - + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/spf13/viper" - - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/libs/cli" - tmflags "github.com/tendermint/tendermint/libs/cli/flags" - "github.com/tendermint/tendermint/libs/log" + tmcfg "github.com/tendermint/tendermint/config" + tmlog "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" ) +// DONTCOVER + +// ServerContextKey defines the context key used to retrieve a server.Context from +// a command's Context. +const ServerContextKey = sdk.ContextKey("server.context") + // server context type Context struct { - Config *cfg.Config - Logger log.Logger + Viper *viper.Viper + Config *tmcfg.Config + Logger tmlog.Logger +} + +// ErrorCode contains the exit code for server exit. +type ErrorCode struct { + Code int +} + +func (e ErrorCode) Error() string { + return strconv.Itoa(e.Code) } func NewDefaultContext() *Context { return NewContext( - cfg.DefaultConfig(), - log.NewTMLogger(log.NewSyncWriter(os.Stdout)), + viper.New(), + tmcfg.DefaultConfig(), + ZeroLogWrapper{log.Logger}, ) } -func NewContext(config *cfg.Config, logger log.Logger) *Context { - return &Context{config, logger} +func NewContext(v *viper.Viper, config *tmcfg.Config, logger tmlog.Logger) *Context { + return &Context{v, config, logger} } -//___________________________________________________________________________________ +func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) { + defer func() { + recover() + }() -// PersistentPreRunEFn returns a PersistentPreRunE function for cobra -// that initailizes the passed in context with a properly configured -// logger and config object. -func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - if cmd.Name() == version.Cmd.Name() { - return nil - } - config, err := interceptLoadConfig() + 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 { - return err + panic(err) } - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) + + err = v.BindPFlag(f.Name, f) if err != nil { - return err + panic(err) } - if viper.GetBool(cli.TraceFlag) { - logger = log.NewTracingLogger(logger) + + // 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) + } } - logger = logger.With("module", "main") - context.Config = config - context.Logger = logger - return nil - } + }) + + return } -// If a new config is created, change some of the default tendermint settings -func interceptLoadConfig() (conf *cfg.Config, err error) { - tmpConf := cfg.DefaultConfig() - err = viper.Unmarshal(tmpConf) +// 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 +// or created and saved to disk, where the server Context is updated to reflect +// the Tendermint configuration. The Viper literal is used to read and parse +// the application configuration. Command handlers can fetch the server Context +// to get the Tendermint configuration or to get access to Viper. +func InterceptConfigsPreRunHandler(cmd *cobra.Command) error { + serverCtx := NewDefaultContext() + + // Get the executable name and configure the viper instance so that environmental + // variables are checked based off that name. The underscore character is used + // as a separator + executableName, err := os.Executable() + if err != nil { + return err + } + + basename := path.Base(executableName) + + // Configure the viper instance + serverCtx.Viper.BindPFlags(cmd.Flags()) + serverCtx.Viper.BindPFlags(cmd.PersistentFlags()) + serverCtx.Viper.SetEnvPrefix(basename) + serverCtx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) + serverCtx.Viper.AutomaticEnv() + + // intercept configuration files, using both Viper instances separately + config, err := interceptConfigs(serverCtx.Viper) + if err != nil { + return err + } + + // 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 { + logWriter = zerolog.ConsoleWriter{Out: os.Stderr} + } else { + logWriter = os.Stderr + } + + logLvlStr := serverCtx.Viper.GetString(flags.FlagLogLevel) + logLvl, err := zerolog.ParseLevel(logLvlStr) if err != nil { - // TODO: Handle with #870 - panic(err) + return fmt.Errorf("failed to parse log level (%s): %w", logLvlStr, err) } - rootDir := tmpConf.RootDir - configFilePath := filepath.Join(rootDir, "config/config.toml") - // Intercept only if the file doesn't already exist - if _, err := os.Stat(configFilePath); os.IsNotExist(err) { - // the following parse config is needed to create directories - conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. - conf.ProfListenAddress = "localhost:6060" + serverCtx.Logger = ZeroLogWrapper{zerolog.New(logWriter).Level(logLvl).With().Timestamp().Logger()} + + return SetCmdServerContext(cmd, serverCtx) +} + +// GetServerContextFromCmd returns a Context from a command or an empty Context +// if it has not been set. +func GetServerContextFromCmd(cmd *cobra.Command) *Context { + if v := cmd.Context().Value(ServerContextKey); v != nil { + serverCtxPtr := v.(*Context) + return serverCtxPtr + } + + return NewDefaultContext() +} + +// SetCmdServerContext sets a command's Context value to the provided argument. +func SetCmdServerContext(cmd *cobra.Command, serverCtx *Context) error { + v := cmd.Context().Value(ServerContextKey) + if v == nil { + return errors.New("server context not set") + } + + serverCtxPtr := v.(*Context) + *serverCtxPtr = *serverCtx + + return nil +} + +// interceptConfigs parses and updates a Tendermint configuration file or +// creates a new one and saves it. It also parses and saves the application +// configuration file. The Tendermint configuration file is parsed given a root +// Viper object, whereas the application is parsed with the private package-aware +// viperCfg object. +func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) { + rootDir := rootViper.GetString(flags.FlagHome) + configPath := filepath.Join(rootDir, "config") + tmCfgFile := filepath.Join(configPath, "config.toml") + + conf := tmcfg.DefaultConfig() + + switch _, err := os.Stat(tmCfgFile); { + case os.IsNotExist(err): + tmcfg.EnsureRoot(rootDir) + + if err = conf.ValidateBasic(); err != nil { + return nil, fmt.Errorf("error in config file: %v", err) + } + + conf.RPC.PprofListenAddress = "localhost:6060" conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 - conf.TxIndex.IndexAllKeys = true conf.Consensus.TimeoutCommit = 5 * time.Second - cfg.WriteConfigFile(configFilePath, conf) - // Fall through, just so that its parsed into memory. + tmcfg.WriteConfigFile(tmCfgFile, conf) + + case err != nil: + return nil, err + + default: + rootViper.SetConfigType("toml") + rootViper.SetConfigName("config") + rootViper.AddConfigPath(configPath) + + if err := rootViper.ReadInConfig(); err != nil { + return nil, fmt.Errorf("failed to read in %s: %w", tmCfgFile, err) + } + } + + // Read into the configuration whatever data the viper instance has for it. + // This may come from the configuration file above but also any of the other + // sources viper uses. + if err := rootViper.Unmarshal(conf); err != nil { + return nil, err } - if conf == nil { - conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. + conf.SetRoot(rootDir) + + appCfgFilePath := filepath.Join(configPath, "app.toml") + if _, err := os.Stat(appCfgFilePath); os.IsNotExist(err) { + appConf, err := config.ParseConfig(rootViper) if err != nil { - panic(err) + return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err) } - } - appConfigFilePath := filepath.Join(rootDir, "config/app.toml") - if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) { - appConf, _ := config.ParseConfig() - config.WriteConfigFile(appConfigFilePath, appConf) + config.WriteConfigFile(appCfgFilePath, appConf) } - viper.SetConfigName("app") - err = viper.MergeInConfig() + rootViper.SetConfigType("toml") + rootViper.SetConfigName("app") + rootViper.AddConfigPath(configPath) - return conf, err + if err := rootViper.MergeInConfig(); err != nil { + return nil, fmt.Errorf("failed to merge configuration: %w", err) + } + + return conf, nil } // add server commands -func AddCommands( - ctx *Context, cdc *codec.Codec, - rootCmd *cobra.Command, - appCreator AppCreator, appExport AppExporter) { - - rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") - +func AddCommands(rootCmd *cobra.Command, defaultNodeHome string, appCreator types.AppCreator, appExport types.AppExporter, addStartFlags types.ModuleInitFlags) { tendermintCmd := &cobra.Command{ Use: "tendermint", Short: "Tendermint subcommands", } tendermintCmd.AddCommand( - ShowNodeIDCmd(ctx), - ShowValidatorCmd(ctx), - ShowAddressCmd(ctx), - VersionCmd(ctx), - AttestationCmd(ctx), + ShowNodeIDCmd(), + ShowValidatorCmd(), + ShowAddressCmd(), + VersionCmd(), ) + startCmd := StartCmd(appCreator, defaultNodeHome) + addStartFlags(startCmd) rootCmd.AddCommand( - StartCmd(ctx, appCreator), - UnsafeResetAllCmd(ctx), + startCmd, + UnsafeResetAllCmd(), flags.LineBreak, tendermintCmd, - ExportCmd(ctx, cdc, appExport), + ExportCmd(appExport, defaultNodeHome), flags.LineBreak, - version.Cmd, + version.NewVersionCommand(), ) } -//___________________________________________________________________________________ - -// InsertKeyJSON inserts a new JSON field/key with a given value to an existing -// JSON message. An error is returned if any serialization operation fails. -// -// NOTE: The ordering of the keys returned as the resulting JSON message is -// non-deterministic, so the client should not rely on key ordering. -func InsertKeyJSON(cdc *codec.Codec, baseJSON []byte, key string, value json.RawMessage) ([]byte, error) { - var jsonMap map[string]json.RawMessage - - if err := cdc.UnmarshalJSON(baseJSON, &jsonMap); err != nil { - return nil, err - } - - jsonMap[key] = value - bz, err := codec.MarshalJSONIndent(cdc, jsonMap) - - return json.RawMessage(bz), err -} - // https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go // TODO there must be a better way to get external IP func ExternalIP() (string, error) { @@ -174,6 +279,7 @@ func ExternalIP() (string, error) { if err != nil { return "", err } + for _, iface := range ifaces { if skipInterface(iface) { continue @@ -182,6 +288,7 @@ func ExternalIP() (string, error) { if err != nil { return "", err } + for _, addr := range addrs { ip := addrToIP(addr) if ip == nil || ip.IsLoopback() { @@ -201,34 +308,49 @@ func ExternalIP() (string, error) { func TrapSignal(cleanupFunc func()) { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { sig := <-sigs + if cleanupFunc != nil { cleanupFunc() } exitCode := 128 + switch sig { case syscall.SIGINT: exitCode += int(syscall.SIGINT) case syscall.SIGTERM: exitCode += int(syscall.SIGTERM) } + os.Exit(exitCode) }() } +// WaitForQuitSignals waits for SIGINT and SIGTERM and returns. +func WaitForQuitSignals() ErrorCode { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + sig := <-sigs + return ErrorCode{Code: int(sig.(syscall.Signal)) + 128} +} + func skipInterface(iface net.Interface) bool { if iface.Flags&net.FlagUp == 0 { return true // interface down } + if iface.Flags&net.FlagLoopback != 0 { return true // loopback interface } + return false } func addrToIP(addr net.Addr) net.IP { var ip net.IP + switch v := addr.(type) { case *net.IPNet: ip = v.IP @@ -238,4 +360,18 @@ func addrToIP(addr net.Addr) net.IP { return ip } -// DONTCOVER +func openDB(rootDir string) (dbm.DB, error) { + dataDir := filepath.Join(rootDir, "data") + return sdk.NewLevelDB("application", dataDir) +} + +func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { + if traceWriterFile == "" { + return + } + return os.OpenFile( + traceWriterFile, + os.O_WRONLY|os.O_APPEND|os.O_CREATE, + 0666, + ) +} diff --git a/server/util_test.go b/server/util_test.go index fb4e7325974f..0800b59a30c1 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -1,41 +1,401 @@ package server import ( - "encoding/json" + "context" + "errors" + "fmt" + "os" + "path" + "path/filepath" + "strings" "testing" - "github.com/stretchr/testify/require" + "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/flags" ) -func TestInsertKeyJSON(t *testing.T) { - cdc := codec.New() +var CancelledInPreRun = errors.New("Canelled in prerun") - foo := map[string]string{"foo": "foofoo"} - bar := map[string]string{"barInner": "barbar"} +// Used in each test to run the function under test via Cobra +// but to always halt the command +func preRunETestImpl(cmd *cobra.Command, args []string) error { + err := InterceptConfigsPreRunHandler(cmd) + if err != nil { + return err + } - // create raw messages - bz, err := cdc.MarshalJSON(foo) - require.NoError(t, err) - fooRaw := json.RawMessage(bz) + return CancelledInPreRun +} + +func TestInterceptConfigsPreRunHandlerCreatesConfigFilesWhenMissing(t *testing.T) { + tempDir := t.TempDir() + cmd := StartCmd(nil, "/foobar") + if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil { + t.Fatalf("Could not set home flag [%T] %v", err, err) + } + + cmd.PreRunE = preRunETestImpl + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + // Test that config.toml is created + configTomlPath := path.Join(tempDir, "config", "config.toml") + s, err := os.Stat(configTomlPath) + if err != nil { + t.Fatalf("Could not stat config.toml after run %v", err) + } + + if !s.Mode().IsRegular() { + t.Fatal("config.toml not created as regular file") + } + + if s.Size() == 0 { + t.Fatal("config.toml created as empty file") + } + + // Test that tendermint config is initialized + if serverCtx.Config == nil { + t.Fatal("tendermint config not created") + } + + // Test that app.toml is created + appTomlPath := path.Join(tempDir, "config", "app.toml") + s, err = os.Stat(appTomlPath) + if err != nil { + t.Fatalf("Could not stat app.toml after run %v", err) + } + + if !s.Mode().IsRegular() { + t.Fatal("appp.toml not created as regular file") + } + + if s.Size() == 0 { + t.Fatal("config.toml created as empty file") + } + + // Test that the config for use in server/start.go is created + if serverCtx.Viper == nil { + t.Error("app config Viper instance not created") + } +} + +func TestInterceptConfigsPreRunHandlerReadsConfigToml(t *testing.T) { + const testDbBackend = "awesome_test_db" + tempDir := t.TempDir() + err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm) + if err != nil { + t.Fatalf("creating config dir failed: %v", err) + } + configTomlPath := path.Join(tempDir, "config", "config.toml") + writer, err := os.Create(configTomlPath) + if err != nil { + t.Fatalf("creating config.toml file failed: %v", err) + } + + _, err = writer.WriteString(fmt.Sprintf("db_backend = '%s'\n", testDbBackend)) + if err != nil { + t.Fatalf("Failed writing string to config.toml: %v", err) + } + + if err := writer.Close(); err != nil { + t.Fatalf("Failed closing config.toml: %v", err) + } + + cmd := StartCmd(nil, "/foobar") + if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil { + t.Fatalf("Could not set home flag [%T] %v", err, err) + } + + cmd.PreRunE = preRunETestImpl + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if testDbBackend != serverCtx.Config.DBBackend { + t.Error("DBPath was not set from config.toml") + } +} + +func TestInterceptConfigsPreRunHandlerReadsAppToml(t *testing.T) { + const testHaltTime = 1337 + tempDir := t.TempDir() + err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm) + if err != nil { + t.Fatalf("creating config dir failed: %v", err) + } + appTomlPath := path.Join(tempDir, "config", "app.toml") + writer, err := os.Create(appTomlPath) + if err != nil { + t.Fatalf("creating app.toml file failed: %v", err) + } + + _, err = writer.WriteString(fmt.Sprintf("halt-time = %d\n", testHaltTime)) + if err != nil { + t.Fatalf("Failed writing string to app.toml: %v", err) + } + + if err := writer.Close(); err != nil { + t.Fatalf("Failed closing app.toml: %v", err) + } + cmd := StartCmd(nil, tempDir) + + cmd.PreRunE = preRunETestImpl + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if testHaltTime != serverCtx.Viper.GetInt("halt-time") { + t.Error("Halt time was not set from app.toml") + } +} + +func TestInterceptConfigsPreRunHandlerReadsFlags(t *testing.T) { + const testAddr = "tcp://127.1.2.3:12345" + tempDir := t.TempDir() + cmd := StartCmd(nil, "/foobar") + + if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil { + t.Fatalf("Could not set home flag [%T] %v", err, err) + } + + // This flag is added by tendermint + if err := cmd.Flags().Set("rpc.laddr", testAddr); err != nil { + t.Fatalf("Could not set address flag [%T] %v", err, err) + } + + cmd.PreRunE = preRunETestImpl + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if testAddr != serverCtx.Config.RPC.ListenAddress { + t.Error("RPCListenAddress was not set from command flags") + } +} + +func TestInterceptConfigsPreRunHandlerReadsEnvVars(t *testing.T) { + const testAddr = "tcp://127.1.2.3:12345" + tempDir := t.TempDir() + cmd := StartCmd(nil, "/foobar") + if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil { + t.Fatalf("Could not set home flag [%T] %v", err, err) + } + + executableName, err := os.Executable() + if err != nil { + t.Fatalf("Could not get executable name: %v", err) + } + basename := path.Base(executableName) + basename = strings.ReplaceAll(basename, ".", "_") + // This is added by tendermint + envVarName := fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename)) + os.Setenv(envVarName, testAddr) + t.Cleanup(func() { + os.Unsetenv(envVarName) + }) + + cmd.PreRunE = preRunETestImpl + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) - bz, err = cdc.MarshalJSON(bar) - require.NoError(t, err) - barRaw := json.RawMessage(bz) + if err := cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } - // make the append - appBz, err := InsertKeyJSON(cdc, fooRaw, "barOuter", barRaw) - require.NoError(t, err) + if testAddr != serverCtx.Config.RPC.ListenAddress { + t.Errorf("RPCListenAddress was not set from env. var. %q", envVarName) + } +} + +/* + The following tests are here to check the precedence of each + of the configuration sources. A common setup functionality is used + to avoid duplication of code between tests. +*/ + +var ( + TestAddrExpected = "tcp://127.126.125.124:12345" // expected to be used in test + TestAddrNotExpected = "tcp://127.127.127.127:11111" // not expected to be used in test +) + +type precedenceCommon struct { + envVarName string + flagName string + configTomlPath string + + cmd *cobra.Command +} + +func newPrecedenceCommon(t *testing.T) precedenceCommon { + retval := precedenceCommon{} + + // Determine the env. var. name based off the executable name + executableName, err := os.Executable() + if err != nil { + t.Fatalf("Could not get executable name: %v", err) + } + basename := path.Base(executableName) + basename = strings.ReplaceAll(basename, ".", "_") + basename = strings.ReplaceAll(basename, "-", "_") + // Store the name of the env. var. + retval.envVarName = fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename)) + + // Store the flag name. This flag is added by tendermint + retval.flagName = "rpc.laddr" + + // Create a tempdir and create './config' under that + tempDir := t.TempDir() + err = os.Mkdir(path.Join(tempDir, "config"), os.ModePerm) + if err != nil { + t.Fatalf("creating config dir failed: %v", err) + } + // Store the path for config.toml + retval.configTomlPath = path.Join(tempDir, "config", "config.toml") + + // always remove the env. var. after each test execution + t.Cleanup(func() { + // This should not fail but if it does just panic + if err := os.Unsetenv(retval.envVarName); err != nil { + panic("Could not clear configuration env. var. used in test") + } + }) + + // Set up the command object that is used in this test + retval.cmd = StartCmd(nil, tempDir) + retval.cmd.PreRunE = preRunETestImpl + + return retval +} + +func (v precedenceCommon) setAll(t *testing.T, setFlag *string, setEnvVar *string, setConfigFile *string) { + if setFlag != nil { + if err := v.cmd.Flags().Set(v.flagName, *setFlag); err != nil { + t.Fatalf("Failed setting flag %q", v.flagName) + } + } + + if setEnvVar != nil { + os.Setenv(v.envVarName, *setEnvVar) + } + + if setConfigFile != nil { + writer, err := os.Create(v.configTomlPath) + if err != nil { + t.Fatalf("creating config.toml file failed: %v", err) + } + + _, err = writer.WriteString(fmt.Sprintf("[rpc]\nladdr = \"%s\"\n", *setConfigFile)) + if err != nil { + t.Fatalf("Failed writing string to config.toml: %v", err) + } + + if err := writer.Close(); err != nil { + t.Fatalf("Failed closing config.toml: %v", err) + } + } +} + +func TestInterceptConfigsPreRunHandlerPrecedenceFlag(t *testing.T) { + testCommon := newPrecedenceCommon(t) + testCommon.setAll(t, &TestAddrExpected, &TestAddrNotExpected, &TestAddrNotExpected) + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := testCommon.cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if TestAddrExpected != serverCtx.Config.RPC.ListenAddress { + t.Fatalf("RPCListenAddress was not set from flag %q", testCommon.flagName) + } +} + +func TestInterceptConfigsPreRunHandlerPrecedenceEnvVar(t *testing.T) { + testCommon := newPrecedenceCommon(t) + testCommon.setAll(t, nil, &TestAddrExpected, &TestAddrNotExpected) + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := testCommon.cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if TestAddrExpected != serverCtx.Config.RPC.ListenAddress { + t.Errorf("RPCListenAddress was not set from env. var. %q", testCommon.envVarName) + } +} + +func TestInterceptConfigsPreRunHandlerPrecedenceConfigFile(t *testing.T) { + testCommon := newPrecedenceCommon(t) + testCommon.setAll(t, nil, nil, &TestAddrExpected) + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := testCommon.cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if TestAddrExpected != serverCtx.Config.RPC.ListenAddress { + t.Errorf("RPCListenAddress was not read from file %q", testCommon.configTomlPath) + } +} + +func TestInterceptConfigsPreRunHandlerPrecedenceConfigDefault(t *testing.T) { + testCommon := newPrecedenceCommon(t) + // Do not set anything + + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + + if err := testCommon.cmd.ExecuteContext(ctx); err != CancelledInPreRun { + t.Fatalf("function failed with [%T] %v", err, err) + } + + if "tcp://127.0.0.1:26657" != serverCtx.Config.RPC.ListenAddress { + t.Error("RPCListenAddress is not using default") + } +} - // test the append - var appended map[string]json.RawMessage - err = cdc.UnmarshalJSON(appBz, &appended) - require.NoError(t, err) +// Ensure that if interceptConfigs encounters any error other than non-existen errors +// that we correctly return the offending error, for example a permission error. +// See https://github.com/cosmos/cosmos-sdk/issues/7578 +func TestInterceptConfigsWithBadPermissions(t *testing.T) { + tempDir := t.TempDir() + subDir := filepath.Join(tempDir, "nonPerms") + if err := os.Mkdir(subDir, 0600); err != nil { + t.Fatalf("Failed to create sub directory: %v", err) + } + cmd := StartCmd(nil, "/foobar") + if err := cmd.Flags().Set(flags.FlagHome, subDir); err != nil { + t.Fatalf("Could not set home flag [%T] %v", err, err) + } - var resBar map[string]string - err = cdc.UnmarshalJSON(appended["barOuter"], &resBar) - require.NoError(t, err) + cmd.PreRunE = preRunETestImpl - require.Equal(t, bar, resBar, "appended: %v", appended) + serverCtx := &Context{} + ctx := context.WithValue(context.Background(), ServerContextKey, serverCtx) + if err := cmd.ExecuteContext(ctx); !os.IsPermission(err) { + t.Fatalf("Failed to catch permissions error, got: [%T] %v", err, err) + } } diff --git a/simapp/app.go b/simapp/app.go index c074f8ccce05..5eebfebb0f48 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -2,125 +2,178 @@ package simapp import ( "io" + "net/http" "os" + "path/filepath" + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "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" distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "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" "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + transfer "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer" + ibctransferkeeper "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/keeper" + 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" + 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" + ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + // unnamed import of statik for swagger UI support + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" ) const appName = "SimApp" var ( - // DefaultCLIHome default home directories for the application CLI - DefaultCLIHome = os.ExpandEnv("$HOME/.simapp") - // DefaultNodeHome default home directories for the application daemon - DefaultNodeHome = os.ExpandEnv("$HOME/.simapp") + DefaultNodeHome string // ModuleBasics defines the module BasicManager is in charge of setting up basic, // non-dependant module elements, such as codec registration // and genesis verification. ModuleBasics = module.NewBasicManager( auth.AppModuleBasic{}, - supply.AppModuleBasic{}, genutil.AppModuleBasic{}, bank.AppModuleBasic{}, + capability.AppModuleBasic{}, staking.AppModuleBasic{}, mint.AppModuleBasic{}, distr.AppModuleBasic{}, gov.NewAppModuleBasic( - paramsclient.ProposalHandler, distr.ProposalHandler, upgradeclient.ProposalHandler, + paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, ), params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, + ibc.AppModuleBasic{}, upgrade.AppModuleBasic{}, evidence.AppModuleBasic{}, + transfer.AppModuleBasic{}, + vesting.AppModuleBasic{}, ) // module account permissions maccPerms = map[string][]string{ - auth.FeeCollectorName: nil, - distr.ModuleName: nil, - mint.ModuleName: {supply.Minter}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - gov.ModuleName: {supply.Burner}, - } - - // module accounts that are allowed to receive tokens - allowedReceivingModAcc = map[string]bool{ - distr.ModuleName: true, + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, } ) -// MakeCodec - custom tx codec -func MakeCodec() *codec.Codec { - var cdc = codec.New() - ModuleBasics.RegisterCodec(cdc) - vesting.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - return cdc -} - -// Verify app interface at compile time -var _ App = (*SimApp)(nil) +var ( + _ App = (*SimApp)(nil) + _ servertypes.Application = (*SimApp)(nil) +) // SimApp extends an ABCI application, but with most of its parameters exported. // They are exported for convenience in creating helper functions, as object // capabilities aren't needed for testing. type SimApp struct { - *bam.BaseApp - cdc *codec.Codec + *baseapp.BaseApp + legacyAmino *codec.LegacyAmino + appCodec codec.Marshaler + interfaceRegistry types.InterfaceRegistry invCheckPeriod uint // keys to access the substores - keys map[string]*sdk.KVStoreKey - tkeys map[string]*sdk.TransientStoreKey - - // subspaces - subspaces map[string]params.Subspace + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + memKeys map[string]*sdk.MemoryStoreKey // keepers - AccountKeeper auth.AccountKeeper - BankKeeper bank.Keeper - SupplyKeeper supply.Keeper - StakingKeeper staking.Keeper - SlashingKeeper slashing.Keeper - MintKeeper mint.Keeper - DistrKeeper distr.Keeper - GovKeeper gov.Keeper - CrisisKeeper crisis.Keeper - UpgradeKeeper upgrade.Keeper - ParamsKeeper params.Keeper - EvidenceKeeper evidence.Keeper + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + CrisisKeeper crisiskeeper.Keeper + UpgradeKeeper upgradekeeper.Keeper + ParamsKeeper paramskeeper.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 + + // make scoped keepers public for test purposes + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + ScopedIBCMockKeeper capabilitykeeper.ScopedKeeper // the module manager mm *module.Manager @@ -129,149 +182,214 @@ type SimApp struct { sm *module.SimulationManager } +func init() { + userHomeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + DefaultNodeHome = filepath.Join(userHomeDir, ".simapp") +} + // NewSimApp returns a reference to an initialized SimApp. func NewSimApp( logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool, - invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp), + homePath string, invCheckPeriod uint, encodingConfig simappparams.EncodingConfig, + appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *SimApp { - cdc := MakeCodec() + // TODO: Remove cdc in favor of appCodec once all modules are migrated. + appCodec := encodingConfig.Marshaler + legacyAmino := encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry - bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) + bApp := baseapp.NewBaseApp(appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetAppVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) keys := sdk.NewKVStoreKeys( - bam.MainStoreKey, auth.StoreKey, staking.StoreKey, - supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey, upgrade.StoreKey, evidence.StoreKey, + 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, ) - tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) app := &SimApp{ - BaseApp: bApp, - cdc: cdc, - invCheckPeriod: invCheckPeriod, - keys: keys, - tkeys: tkeys, - subspaces: make(map[string]params.Subspace), + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + interfaceRegistry: interfaceRegistry, + invCheckPeriod: invCheckPeriod, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, } - // init params keeper and subspaces - app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey]) - app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace) - app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace) - app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace) - app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace) - app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace) - app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace) - app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()) - app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace) - app.subspaces[evidence.ModuleName] = app.ParamsKeeper.Subspace(evidence.DefaultParamspace) + app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) + + // set the BaseApp's parameter store + bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramskeeper.ConsensusParamsKeyTable())) + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName) + scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // note replicate if you do not need to test core IBC or light clients. + scopedIBCMockKeeper := app.CapabilityKeeper.ScopeToModule(ibcmock.ModuleName) // add keepers - app.AccountKeeper = auth.NewAccountKeeper( - app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], auth.ProtoBaseAccount, + app.AccountKeeper = authkeeper.NewAccountKeeper( + appCodec, keys[authtypes.StoreKey], app.GetSubspace(authtypes.ModuleName), authtypes.ProtoBaseAccount, maccPerms, ) - app.BankKeeper = bank.NewBaseKeeper( - app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(), + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.ModuleAccountAddrs(), ) - app.SupplyKeeper = supply.NewKeeper( - app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms, + stakingKeeper := stakingkeeper.NewKeeper( + appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName), ) - stakingKeeper := staking.NewKeeper( - app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName], + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &stakingKeeper, + app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, ) - app.MintKeeper = mint.NewKeeper( - app.cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper, - app.SupplyKeeper, auth.FeeCollectorName, + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, keys[distrtypes.StoreKey], app.GetSubspace(distrtypes.ModuleName), app.AccountKeeper, app.BankKeeper, + &stakingKeeper, authtypes.FeeCollectorName, app.ModuleAccountAddrs(), ) - app.DistrKeeper = distr.NewKeeper( - app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper, - app.SupplyKeeper, auth.FeeCollectorName, app.ModuleAccountAddrs(), + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, keys[slashingtypes.StoreKey], &stakingKeeper, app.GetSubspace(slashingtypes.ModuleName), ) - app.SlashingKeeper = slashing.NewKeeper( - app.cdc, keys[slashing.StoreKey], &stakingKeeper, app.subspaces[slashing.ModuleName], + app.CrisisKeeper = crisiskeeper.NewKeeper( + app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName, ) - app.CrisisKeeper = crisis.NewKeeper( - app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName, + app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper = *stakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), ) - app.UpgradeKeeper = upgrade.NewKeeper(skipUpgradeHeights, keys[upgrade.StoreKey], app.cdc) - // create evidence keeper with router - evidenceKeeper := evidence.NewKeeper( - app.cdc, keys[evidence.StoreKey], app.subspaces[evidence.ModuleName], &app.StakingKeeper, app.SlashingKeeper, + // Create IBC Keeper + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, scopedIBCKeeper, ) - evidenceRouter := evidence.NewRouter() - // TODO: Register evidence routes. - evidenceKeeper.SetRouter(evidenceRouter) - app.EvidenceKeeper = *evidenceKeeper // register the proposal types - govRouter := gov.NewRouter() - govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler). - AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). - AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). - AddRoute(upgrade.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) - app.GovKeeper = gov.NewKeeper( - app.cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper, + 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)) + app.GovKeeper = govkeeper.NewKeeper( + appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, &stakingKeeper, govRouter, ) - // register the staking hooks - // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.StakingKeeper = *stakingKeeper.SetHooks( - staking.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), + // Create Transfer Keepers + app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, keys[ibctransfertypes.StoreKey], app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, + app.AccountKeeper, app.BankKeeper, scopedTransferKeeper, ) + transferModule := transfer.NewAppModule(app.TransferKeeper) + + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // note replicate if you do not need to test core IBC or light clients. + mockModule := ibcmock.NewAppModule(scopedIBCMockKeeper) + + // Create static IBC router, add transfer route, then set and seal it + ibcRouter := porttypes.NewRouter() + ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferModule) + ibcRouter.AddRoute(ibcmock.ModuleName, mockModule) + app.IBCKeeper.SetRouter(ibcRouter) + + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, keys[evidencetypes.StoreKey], &app.StakingKeeper, app.SlashingKeeper, + ) + // If evidence needs to be handled for the app, set routes in router here and seal + app.EvidenceKeeper = *evidenceKeeper + + /**** Module Options ****/ + + // NOTE: we may consider parsing `appOpts` inside module constructors. For the moment + // we prefer to be more strict in what arguments the modules expect. + var skipGenesisInvariants = cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. app.mm = module.NewManager( - genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx), - auth.NewAppModule(app.AccountKeeper), - bank.NewAppModule(app.BankKeeper, app.AccountKeeper), - crisis.NewAppModule(&app.CrisisKeeper), - supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), - gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper), - mint.NewAppModule(app.MintKeeper), - slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper), - distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper), - staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + genutil.NewAppModule( + app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, + encodingConfig.TxConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + 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), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + params.NewAppModule(app.ParamsKeeper), + transferModule, ) // During begin block slashing happens after distr.BeginBlocker so that // there is nothing left over in the validator fee pool, so as to keep the // CanWithdrawInvariant invariant. - app.mm.SetOrderBeginBlockers(upgrade.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName, evidence.ModuleName) - app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName) + // NOTE: staking module is required if HistoricalEntries param > 0 + app.mm.SetOrderBeginBlockers( + upgradetypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, ibchost.ModuleName, + ) + app.mm.SetOrderEndBlockers(crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName) - // NOTE: The genutils moodule must occur after staking so that pools are + // NOTE: The genutils module must occur after staking so that pools are // properly initialized with tokens from genesis accounts. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. app.mm.SetOrderInitGenesis( - auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, - slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, - crisis.ModuleName, genutil.ModuleName, evidence.ModuleName, + 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, ) app.mm.RegisterInvariants(&app.CrisisKeeper) - app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) + app.mm.RegisterServices(module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())) + + // add test gRPC service for testing gRPC queries in isolation + testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) // create the simulation manager and define the order of the modules for deterministic simulations // // NOTE: this is not required apps that don't use the simulator for fuzz testing // transactions app.sm = module.NewSimulationManager( - auth.NewAppModule(app.AccountKeeper), - bank.NewAppModule(app.BankKeeper, app.AccountKeeper), - supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), - gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper), - mint.NewAppModule(app.MintKeeper), - staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper), - slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper), - params.NewAppModule(), // NOTE: only used for simulation to generate randomized param change proposals + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), + capability.NewAppModule(appCodec, *app.CapabilityKeeper), + 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), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + params.NewAppModule(app.ParamsKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + ibc.NewAppModule(app.IBCKeeper), + transferModule, ) app.sm.RegisterStoreDecoders() @@ -279,23 +397,53 @@ func NewSimApp( // initialize stores app.MountKVStores(keys) app.MountTransientStores(tkeys) + app.MountMemoryStores(memKeys) // initialize BaseApp app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) - app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, auth.DefaultSigVerificationGasConsumer)) + app.SetAnteHandler( + ante.NewAnteHandler( + app.AccountKeeper, app.BankKeeper, ante.DefaultSigVerificationGasConsumer, + encodingConfig.TxConfig.SignModeHandler(), + ), + ) app.SetEndBlocker(app.EndBlocker) if loadLatest { - err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) - if err != nil { + if err := app.LoadLatestVersion(); err != nil { tmos.Exit(err.Error()) } + + // Initialize and seal the capability keeper so all persistent capabilities + // are loaded in-memory and prevent any further modules from creating scoped + // sub-keepers. + // This must be done during creation of baseapp rather than in InitChain so + // that in-memory capabilities get regenerated on app restart. + // Note that since this reads from the store, we can only perform it when + // `loadLatest` is set to true. + ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + app.CapabilityKeeper.InitializeAndSeal(ctx) } + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedTransferKeeper = scopedTransferKeeper + + // NOTE: the IBC mock keeper and application module is used only for testing core IBC. Do + // note replicate if you do not need to test core IBC or light clients. + app.ScopedIBCMockKeeper = scopedIBCMockKeeper + 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() } @@ -312,41 +460,46 @@ func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Re // InitChainer application update at chain initialization func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { var genesisState GenesisState - app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState) - return app.mm.InitGenesis(ctx, genesisState) + if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + return app.mm.InitGenesis(ctx, app.appCodec, genesisState) } // LoadHeight loads a particular height func (app *SimApp) LoadHeight(height int64) error { - return app.LoadVersion(height, app.keys[bam.MainStoreKey]) + return app.LoadVersion(height) } // ModuleAccountAddrs returns all the app's module account addresses. func (app *SimApp) ModuleAccountAddrs() map[string]bool { modAccAddrs := make(map[string]bool) for acc := range maccPerms { - modAccAddrs[supply.NewModuleAddress(acc).String()] = true + modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true } return modAccAddrs } -// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens. -func (app *SimApp) BlacklistedAccAddrs() map[string]bool { - blacklistedAddrs := make(map[string]bool) - for acc := range maccPerms { - blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc] - } - - return blacklistedAddrs +// LegacyAmino returns SimApp's amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *SimApp) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino } -// Codec returns SimApp's codec. +// AppCodec returns SimApp's app codec. // // NOTE: This is solely to be used for testing purposes as it may be desirable // for modules to register their own custom testing types. -func (app *SimApp) Codec() *codec.Codec { - return app.cdc +func (app *SimApp) AppCodec() codec.Marshaler { + return app.appCodec +} + +// InterfaceRegistry returns SimApp's InterfaceRegistry +func (app *SimApp) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry } // GetKey returns the KVStoreKey for the provided store key. @@ -363,11 +516,19 @@ func (app *SimApp) GetTKey(storeKey string) *sdk.TransientStoreKey { return app.tkeys[storeKey] } +// GetMemKey returns the MemStoreKey for the provided mem key. +// +// NOTE: This is solely used for testing purposes. +func (app *SimApp) GetMemKey(storeKey string) *sdk.MemoryStoreKey { + return app.memKeys[storeKey] +} + // GetSubspace returns a param subspace for a given module name. // // NOTE: This is solely to be used for testing purposes. -func (app *SimApp) GetSubspace(moduleName string) params.Subspace { - return app.subspaces[moduleName] +func (app *SimApp) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace } // SimulationManager implements the SimulationApp interface @@ -375,6 +536,49 @@ func (app *SimApp) SimulationManager() *module.SimulationManager { return app.sm } +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + rpc.RegisterRoutes(clientCtx, apiSvr.Router) + // Register legacy tx routes. + authrest.RegisterTxRoutes(clientCtx, apiSvr.Router) + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + // Register new tendermint queries routes from grpc-gateway. + tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register legacy and grpc-gateway routes for all modules. + ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router) + ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if apiConfig.Swagger { + RegisterSwaggerAPI(clientCtx, apiSvr.Router) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *SimApp) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { + tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +} + +// RegisterSwaggerAPI registers swagger route with API Server +func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + + staticServer := http.FileServer(statikFS) + rtr.PathPrefix("/swagger/").Handler(http.StripPrefix("/swagger/", staticServer)) +} + // GetMaccPerms returns a copy of the module account permissions func GetMaccPerms() map[string][]string { dupMaccPerms := make(map[string][]string) @@ -383,3 +587,21 @@ func GetMaccPerms() map[string][]string { } return dupMaccPerms } + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryMarshaler, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + paramsKeeper.Subspace(authtypes.ModuleName) + paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(stakingtypes.ModuleName) + paramsKeeper.Subspace(minttypes.ModuleName) + paramsKeeper.Subspace(distrtypes.ModuleName) + paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govtypes.ParamKeyTable()) + paramsKeeper.Subspace(crisistypes.ModuleName) + paramsKeeper.Subspace(ibctransfertypes.ModuleName) + paramsKeeper.Subspace(ibchost.ModuleName) + + return paramsKeeper +} diff --git a/simapp/app_test.go b/simapp/app_test.go index 9cf211a94176..da5d8c8ec18a 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -1,6 +1,7 @@ package simapp import ( + "encoding/json" "os" "testing" @@ -8,17 +9,24 @@ import ( "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/codec" - abci "github.com/tendermint/tendermint/abci/types" ) -func TestSimAppExport(t *testing.T) { +func TestSimAppExportAndBlockedAddrs(t *testing.T) { + encCfg := MakeTestEncodingConfig() db := dbm.NewMemDB() - app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) - genesisState := NewDefaultGenesisState() - stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + for acc := range maccPerms { + require.True( + t, + app.BankKeeper.BlockedAddr(app.AccountKeeper.GetModuleAddress(acc)), + "ensure that blocked addresses are properly set in bank keeper", + ) + } + + genesisState := NewDefaultGenesisState(encCfg.Marshaler) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") require.NoError(t, err) // Initialize the chain @@ -31,21 +39,11 @@ func TestSimAppExport(t *testing.T) { app.Commit() // Making a new app object with the db, so that initchain hasn't been called - app2 := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) - _, _, err = app2.ExportAppStateAndValidators(false, []string{}) + app2 := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{}) + _, err = app2.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } -// ensure that black listed addresses are properly set in bank keeper -func TestBlackListedAddrs(t *testing.T) { - db := dbm.NewMemDB() - app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) - - for acc := range maccPerms { - require.Equal(t, !allowedReceivingModAcc[acc], app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc))) - } -} - func TestGetMaccPerms(t *testing.T) { dup := GetMaccPerms() require.Equal(t, maccPerms, dup, "duplicated module account permissions differed from actual module account permissions") diff --git a/simapp/config.go b/simapp/config.go index d42527d3dc6f..98df982bd304 100644 --- a/simapp/config.go +++ b/simapp/config.go @@ -3,7 +3,7 @@ package simapp import ( "flag" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" ) // List of available flags for the simulator diff --git a/simapp/encoding.go b/simapp/encoding.go new file mode 100644 index 000000000000..954f4a1175eb --- /dev/null +++ b/simapp/encoding.go @@ -0,0 +1,19 @@ +package simapp + +import ( + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + "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. +// [DEPRECATED] +func MakeTestEncodingConfig() simappparams.EncodingConfig { + encodingConfig := simappparams.MakeTestEncodingConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} diff --git a/simapp/export.go b/simapp/export.go index 01b1424032cf..887308d30a94 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -4,58 +4,65 @@ import ( "encoding/json" "log" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" + servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // ExportAppStateAndValidators exports the state of the application for a genesis // file. func (app *SimApp) ExportAppStateAndValidators( - forZeroHeight bool, jailWhiteList []string, -) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { - + forZeroHeight bool, jailAllowedAddrs []string, +) (servertypes.ExportedApp, error) { // as if they could withdraw from the start of the next block - ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 if forZeroHeight { - app.prepForZeroHeightGenesis(ctx, jailWhiteList) + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) } - genState := app.mm.ExportGenesis(ctx) - appState, err = codec.MarshalJSONIndent(app.cdc, genState) + genState := app.mm.ExportGenesis(ctx, app.appCodec) + appState, err := json.MarshalIndent(genState, "", " ") if err != nil { - return nil, nil, err + return servertypes.ExportedApp{}, err } - validators = staking.WriteValidators(ctx, app.StakingKeeper) - return appState, validators, nil + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, err } // prepare for fresh start at zero height // NOTE zero height genesis is a temporary feature which will be deprecated // in favour of export at a block height -func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) { - applyWhiteList := false +func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + applyAllowedAddrs := false - //Check if there is a whitelist - if len(jailWhiteList) > 0 { - applyWhiteList = true + // check if there is a allowed address list + if len(jailAllowedAddrs) > 0 { + applyAllowedAddrs = true } - whiteListMap := make(map[string]bool) + allowedAddrsMap := make(map[string]bool) - for _, addr := range jailWhiteList { + for _, addr := range jailAllowedAddrs { _, err := sdk.ValAddressFromBech32(addr) if err != nil { log.Fatal(err) } - whiteListMap[addr] = true + allowedAddrsMap[addr] = true } /* Just to be safe, assert the invariants on current state. */ @@ -64,7 +71,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str /* Handle fee distribution state. */ // withdraw all validator commission - app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) return false }) @@ -72,7 +79,16 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str // withdraw all delegator rewards dels := app.StakingKeeper.GetAllDelegations(ctx) for _, delegation := range dels { - _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + panic(err) + } + + delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) + } + _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) } // clear validator slash events @@ -86,10 +102,9 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str ctx = ctx.WithBlockHeight(0) // reinitialize all validators - app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { - + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { // donate any unwithdrawn outstanding reward fraction tokens to the community pool - scraps := app.DistrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator()) + scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) feePool := app.DistrKeeper.GetFeePool(ctx) feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) app.DistrKeeper.SetFeePool(ctx, feePool) @@ -100,8 +115,16 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str // reinitialize all delegations for _, del := range dels { - app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress) - app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress) + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delAddr, err := sdk.AccAddressFromBech32(del.DelegatorAddress) + if err != nil { + panic(err) + } + app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr) + app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr) } // reset context height @@ -110,7 +133,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str /* Handle staking state. */ // iterate through redelegations, reset creation height - app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) { + app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { for i := range red.Entries { red.Entries[i].CreationHeight = 0 } @@ -119,7 +142,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str }) // iterate through unbonding delegations, reset creation height - app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) { + app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { for i := range ubd.Entries { ubd.Entries[i].CreationHeight = 0 } @@ -129,8 +152,8 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str // Iterate through validators by power descending, reset bond heights, and // update bond intra-tx counters. - store := ctx.KVStore(app.keys[staking.StoreKey]) - iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey) + store := ctx.KVStore(app.keys[stakingtypes.StoreKey]) + iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) counter := int16(0) for ; iter.Valid(); iter.Next() { @@ -141,7 +164,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str } validator.UnbondingHeight = 0 - if applyWhiteList && !whiteListMap[addr.String()] { + if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { validator.Jailed = true } @@ -151,14 +174,17 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []str iter.Close() - _ = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } /* Handle slashing state. */ // reset start height on signing infos app.SlashingKeeper.IterateValidatorSigningInfos( ctx, - func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) { + func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { info.StartHeight = 0 app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) return false diff --git a/simapp/genesis.go b/simapp/genesis.go index d96c6feec954..dbb4e01c7690 100644 --- a/simapp/genesis.go +++ b/simapp/genesis.go @@ -2,6 +2,8 @@ package simapp import ( "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" ) // The genesis state of the blockchain is represented here as a map of raw json @@ -14,6 +16,6 @@ import ( type GenesisState map[string]json.RawMessage // NewDefaultGenesisState generates the default state for the application. -func NewDefaultGenesisState() GenesisState { - return ModuleBasics.DefaultGenesis() +func NewDefaultGenesisState(cdc codec.JSONMarshaler) GenesisState { + return ModuleBasics.DefaultGenesis(cdc) } diff --git a/simapp/genesis_account.go b/simapp/genesis_account.go index e6bc7f97c04d..5c9c7f9a03f8 100644 --- a/simapp/genesis_account.go +++ b/simapp/genesis_account.go @@ -4,12 +4,10 @@ import ( "errors" sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/supply" ) -var _ authexported.GenesisAccount = (*SimGenesisAccount)(nil) +var _ authtypes.GenesisAccount = (*SimGenesisAccount)(nil) // SimGenesisAccount defines a type that implements the GenesisAccount interface // to be used for simulation accounts in the genesis state. @@ -31,16 +29,13 @@ type SimGenesisAccount struct { // Validate checks for errors on the vesting and module account parameters func (sga SimGenesisAccount) Validate() error { if !sga.OriginalVesting.IsZero() { - if sga.OriginalVesting.IsAnyGT(sga.Coins) { - return errors.New("vesting amount cannot be greater than total amount") - } if sga.StartTime >= sga.EndTime { return errors.New("vesting start-time cannot be before end-time") } } if sga.ModuleName != "" { - ma := supply.ModuleAccount{ + ma := authtypes.ModuleAccount{ BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions, } if err := ma.Validate(); err != nil { diff --git a/simapp/genesis_account_test.go b/simapp/genesis_account_test.go index 6c0c6d6775c5..5d5c59003e6b 100644 --- a/simapp/genesis_account_test.go +++ b/simapp/genesis_account_test.go @@ -4,13 +4,13 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" ) func TestSimGenesisAccountValidate(t *testing.T) { @@ -20,8 +20,7 @@ func TestSimGenesisAccountValidate(t *testing.T) { vestingStart := time.Now().UTC() coins := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) - baseAcc := authtypes.NewBaseAccount(addr, nil, pubkey, 0, 0) - require.NoError(t, baseAcc.SetCoins(coins)) + baseAcc := authtypes.NewBaseAccount(addr, pubkey, 0, 0) testCases := []struct { name string @@ -38,14 +37,14 @@ func TestSimGenesisAccountValidate(t *testing.T) { { "invalid basic account with mismatching address/pubkey", simapp.SimGenesisAccount{ - BaseAccount: authtypes.NewBaseAccount(addr, nil, secp256k1.GenPrivKey().PubKey(), 0, 0), + BaseAccount: authtypes.NewBaseAccount(addr, secp256k1.GenPrivKey().PubKey(), 0, 0), }, true, }, { "valid basic account with module name", simapp.SimGenesisAccount{ - BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(crypto.AddressHash([]byte("testmod"))), nil, nil, 0, 0), + BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(crypto.AddressHash([]byte("testmod"))), nil, 0, 0), ModuleName: "testmod", }, false, @@ -78,16 +77,6 @@ func TestSimGenesisAccountValidate(t *testing.T) { }, true, }, - { - "valid basic account with invalid original vesting coins", - simapp.SimGenesisAccount{ - BaseAccount: baseAcc, - OriginalVesting: coins.Add(coins...), - StartTime: vestingStart.Unix(), - EndTime: vestingStart.Add(1 * time.Hour).Unix(), - }, - true, - }, } for _, tc := range testCases { diff --git a/simapp/helpers/test_helpers.go b/simapp/helpers/test_helpers.go index 2341635eeea8..9ccecbd976c4 100644 --- a/simapp/helpers/test_helpers.go +++ b/simapp/helpers/test_helpers.go @@ -4,11 +4,12 @@ import ( "math/rand" "time" - "github.com/tendermint/tendermint/crypto" - + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsign "github.com/cosmos/cosmos-sdk/x/auth/signing" ) // SimAppChainID hardcoded chainID for simulation @@ -18,31 +19,62 @@ const ( ) // GenTx generates a signed mock transaction. -func GenTx(msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accnums []uint64, seq []uint64, priv ...crypto.PrivKey) auth.StdTx { - fee := auth.StdFee{ - Amount: feeAmt, - Gas: gas, - } - - sigs := make([]auth.StdSignature, len(priv)) +func GenTx(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums, accSeqs []uint64, 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) + + // 2nd round: once all signer infos are set, every signer can sign. for i, p := range priv { - // use a empty chainID for ease of testing - sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo)) + 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) } - - sigs[i] = auth.StdSignature{ - PubKey: p.PubKey(), - Signature: sig, + 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 auth.NewStdTx(msgs, fee, sigs, memo) + return tx.GetTx(), nil } diff --git a/simapp/params/amino.go b/simapp/params/amino.go new file mode 100644 index 000000000000..440c29f817ea --- /dev/null +++ b/simapp/params/amino.go @@ -0,0 +1,26 @@ +// +build test_amino + +package params + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" +) + +// MakeTestEncodingConfig creates an EncodingConfig for an amino based test configuration. +// This function should be used only internally (in the SDK). +// App user should'nt create new codecs - use the app.AppCodec instead. +// [DEPRECATED] +func MakeTestEncodingConfig() EncodingConfig { + cdc := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewAminoCodec(cdc) + + return EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: legacytx.StdTxConfig{Cdc: cdc}, + Amino: cdc, + } +} diff --git a/simapp/params/doc.go b/simapp/params/doc.go index 1c721342a9c9..a2f3620a8351 100644 --- a/simapp/params/doc.go +++ b/simapp/params/doc.go @@ -3,9 +3,9 @@ Package params defines the simulation parameters in the simapp. It contains the default weights used for each transaction used on the module's simulation. These weights define the chance for a transaction to be simulated at -any gived operation. +any given operation. -You can repace the default values for the weights by providing a params.json +You can replace the default values for the weights by providing a params.json file with the weights defined for each of the transaction operations: { diff --git a/simapp/params/encoding.go b/simapp/params/encoding.go new file mode 100644 index 000000000000..698408dafdcb --- /dev/null +++ b/simapp/params/encoding.go @@ -0,0 +1,16 @@ +package params + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" +) + +// EncodingConfig specifies the concrete encoding types to use for a given app. +// This is provided for compatibility between protobuf and amino implementations. +type EncodingConfig struct { + InterfaceRegistry types.InterfaceRegistry + Marshaler codec.Marshaler + TxConfig client.TxConfig + Amino *codec.LegacyAmino +} diff --git a/simapp/params/proto.go b/simapp/params/proto.go new file mode 100644 index 000000000000..04aa524b9004 --- /dev/null +++ b/simapp/params/proto.go @@ -0,0 +1,26 @@ +// +build !test_amino + +package params + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +// MakeTestEncodingConfig creates an EncodingConfig for a non-amino based test configuration. +// This function should be used only internally (in the SDK). +// App user should'nt create new codecs - use the app.AppCodec instead. +// [DEPRECATED] +func MakeTestEncodingConfig() EncodingConfig { + cdc := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + + return EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: tx.NewTxConfig(marshaler, tx.DefaultSignModes), + Amino: cdc, + } +} diff --git a/simapp/sim_bench_test.go b/simapp/sim_bench_test.go index b09a3a9090fb..7c22dcfe8a44 100644 --- a/simapp/sim_bench_test.go +++ b/simapp/sim_bench_test.go @@ -5,8 +5,9 @@ import ( "os" "testing" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -26,13 +27,19 @@ func BenchmarkFullAppSimulation(b *testing.B) { } }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + b, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) // export state and simParams before the simulation error is checked @@ -65,13 +72,19 @@ func BenchmarkInvariants(b *testing.B) { } }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + b, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) // export state and simParams before the simulation error is checked @@ -87,7 +100,7 @@ func BenchmarkInvariants(b *testing.B) { PrintStats(db) } - ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1}) + ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight() + 1}) // 3. Benchmark each invariant separately // diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 3d41fa3b8467..cc1a7cada4f5 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -10,21 +10,27 @@ import ( "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" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/simapp/helpers" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/mint" - "github.com/cosmos/cosmos-sdk/x/params" + 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" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + ibchost "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Get flags every time the simulator is run @@ -62,14 +68,20 @@ func TestFullAppSimulation(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + t, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) // export state and simParams before the simulation error is checked @@ -94,14 +106,20 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + t, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) // export state and simParams before the simulation error is checked @@ -115,7 +133,7 @@ func TestAppImportExport(t *testing.T) { fmt.Printf("exporting genesis...\n") - appState, _, err := app.ExportAppStateAndValidators(false, []string{}) + exported, err := app.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err) fmt.Printf("importing genesis...\n") @@ -128,32 +146,37 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) var genesisState GenesisState - err = app.Codec().UnmarshalJSON(appState, &genesisState) + err = json.Unmarshal(exported.AppState, &genesisState) require.NoError(t, err) - ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) - ctxB := newApp.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) - newApp.mm.InitGenesis(ctxB, genesisState) + ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState) + newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) fmt.Printf("comparing stores...\n") storeKeysPrefixes := []StoreKeysPrefixes{ - {app.keys[baseapp.MainStoreKey], newApp.keys[baseapp.MainStoreKey], [][]byte{}}, - {app.keys[auth.StoreKey], newApp.keys[auth.StoreKey], [][]byte{}}, - {app.keys[staking.StoreKey], newApp.keys[staking.StoreKey], + {app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}}, + {app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey], [][]byte{ - staking.UnbondingQueueKey, staking.RedelegationQueueKey, staking.ValidatorQueueKey, + stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, + stakingtypes.HistoricalInfoKey, }}, // ordering may change but it doesn't matter - {app.keys[slashing.StoreKey], newApp.keys[slashing.StoreKey], [][]byte{}}, - {app.keys[mint.StoreKey], newApp.keys[mint.StoreKey], [][]byte{}}, - {app.keys[distr.StoreKey], newApp.keys[distr.StoreKey], [][]byte{}}, - {app.keys[supply.StoreKey], newApp.keys[supply.StoreKey], [][]byte{}}, - {app.keys[params.StoreKey], newApp.keys[params.StoreKey], [][]byte{}}, - {app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}}, + {app.keys[slashingtypes.StoreKey], newApp.keys[slashingtypes.StoreKey], [][]byte{}}, + {app.keys[minttypes.StoreKey], newApp.keys[minttypes.StoreKey], [][]byte{}}, + {app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}}, + {app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}}, + {app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}}, + {app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}}, + {app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}}, + {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{}}, } for _, skp := range storeKeysPrefixes { @@ -163,8 +186,8 @@ func TestAppImportExport(t *testing.T) { failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") - fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) - require.Equal(t, len(failedKVAs), 0, GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, app.Codec(), failedKVAs, failedKVBs)) + fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) + require.Equal(t, len(failedKVAs), 0, GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs)) } } @@ -180,14 +203,20 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(dir)) }() - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", app.Name()) // Run randomized simulation stopEarly, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + t, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) // export state and simParams before the simulation error is checked @@ -206,7 +235,7 @@ func TestAppSimulationAfterImport(t *testing.T) { fmt.Printf("exporting genesis...\n") - appState, _, err := app.ExportAppStateAndValidators(true, []string{}) + exported, err := app.ExportAppStateAndValidators(true, []string{}) require.NoError(t, err) fmt.Printf("importing genesis...\n") @@ -219,17 +248,23 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt) + newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) require.Equal(t, "SimApp", newApp.Name()) newApp.InitChain(abci.RequestInitChain{ - AppStateBytes: appState, + AppStateBytes: exported.AppState, }) _, _, err = simulation.SimulateFromSeed( - t, os.Stdout, newApp.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(newApp, newApp.Codec(), config), - newApp.ModuleAccountAddrs(), config, + t, + os.Stdout, + newApp.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(newApp, newApp.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) require.NoError(t, err) } @@ -264,8 +299,7 @@ func TestAppStateDeterminism(t *testing.T) { } db := dbm.NewMemDB() - - app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt()) + app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", @@ -273,9 +307,15 @@ func TestAppStateDeterminism(t *testing.T) { ) _, _, err := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()), - SimulationOperations(app, app.Codec(), config), - app.ModuleAccountAddrs(), config, + t, + os.Stdout, + app.BaseApp, + AppStateFn(app.AppCodec(), app.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + SimulationOperations(app, app.AppCodec(), config), + app.ModuleAccountAddrs(), + config, + app.AppCodec(), ) require.NoError(t, err) @@ -288,7 +328,7 @@ func TestAppStateDeterminism(t *testing.T) { if j != 0 { require.Equal( - t, appHashList[0], appHashList[j], + t, string(appHashList[0]), string(appHashList[j]), "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, ) } diff --git a/simapp/simd/cmd/cmd_test.go b/simapp/simd/cmd/cmd_test.go new file mode 100644 index 000000000000..1a9183d33e08 --- /dev/null +++ b/simapp/simd/cmd/cmd_test.go @@ -0,0 +1,24 @@ +package cmd_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/simd/cmd" + "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" +) + +func TestInitCmd(t *testing.T) { + rootCmd, _ := cmd.NewRootCmd() + rootCmd.SetArgs([]string{ + "init", // Test the init cmd + "simapp-test", // Moniker + fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists + }) + + require.NoError(t, svrcmd.Execute(rootCmd, simapp.DefaultNodeHome)) +} diff --git a/simapp/simd/cmd/genaccounts.go b/simapp/simd/cmd/genaccounts.go new file mode 100644 index 000000000000..57de144c36b4 --- /dev/null +++ b/simapp/simd/cmd/genaccounts.go @@ -0,0 +1,181 @@ +package cmd + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +const ( + flagVestingStart = "vesting-start-time" + flagVestingEnd = "vesting-end-time" + flagVestingAmt = "vesting-amount" +) + +// AddGenesisAccountCmd returns add-genesis-account cobra Command. +func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", + Short: "Add a genesis account to genesis.json", + Long: `Add a genesis account to genesis.json. The provided account must specify +the account address or key name and a list of initial coins. If a key name is given, +the address will be looked up in the local Keybase. The list of initial tokens must +contain valid denominations. Accounts may optionally be supplied with vesting parameters. +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + depCdc := clientCtx.JSONMarshaler + cdc := depCdc.(codec.Marshaler) + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + config.SetRoot(clientCtx.HomeDir) + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + inBuf := bufio.NewReader(cmd.InOrStdin()) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + + // attempt to lookup address from Keybase if no address was provided + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf) + if err != nil { + return err + } + + info, err := kb.Key(args[0]) + if err != nil { + return fmt.Errorf("failed to get address from Keybase: %w", err) + } + + addr = info.GetAddress() + } + + coins, err := sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return fmt.Errorf("failed to parse coins: %w", err) + } + + vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart) + vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd) + vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt) + + vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) + if err != nil { + return fmt.Errorf("failed to parse vesting amount: %w", err) + } + + // create concrete account type based on input parameters + var genAccount authtypes.GenesisAccount + + balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} + baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) + + if !vestingAmt.IsZero() { + baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) + + if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || + baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { + return errors.New("vesting amount cannot be greater than total amount") + } + + switch { + case vestingStart != 0 && vestingEnd != 0: + genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) + + case vestingEnd != 0: + genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) + + default: + return errors.New("invalid vesting parameters; must supply start and end time or end time") + } + } else { + genAccount = baseAccount + } + + if err := genAccount.Validate(); err != nil { + return fmt.Errorf("failed to validate new genesis account: %w", err) + } + + genFile := config.GenesisFile() + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + + authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) + + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + if err != nil { + return fmt.Errorf("failed to get accounts from any: %w", err) + } + + if accs.Contains(addr) { + return fmt.Errorf("cannot add account at existing address %s", addr) + } + + // Add the new account to the set of genesis accounts and sanitize the + // accounts afterwards. + accs = append(accs, genAccount) + accs = authtypes.SanitizeGenesisAccounts(accs) + + genAccs, err := authtypes.PackAccounts(accs) + if err != nil { + return fmt.Errorf("failed to convert accounts into any's: %w", err) + } + authGenState.Accounts = genAccs + + authGenStateBz, err := cdc.MarshalJSON(&authGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + + appState[authtypes.ModuleName] = authGenStateBz + + 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 { + return fmt.Errorf("failed to marshal bank genesis state: %w", err) + } + + appState[banktypes.ModuleName] = bankGenStateBz + + appStateJSON, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal application genesis state: %w", err) + } + + genDoc.AppState = appStateJSON + return genutil.ExportGenesisFile(genDoc, genFile) + }, + } + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") + cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/simapp/simd/cmd/genaccounts_test.go b/simapp/simd/cmd/genaccounts_test.go new file mode 100644 index 000000000000..9efc5343e7a3 --- /dev/null +++ b/simapp/simd/cmd/genaccounts_test.go @@ -0,0 +1,85 @@ +package cmd_test + +import ( + "context" + "fmt" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/simapp" + simcmd "github.com/cosmos/cosmos-sdk/simapp/simd/cmd" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" +) + +var testMbm = module.NewBasicManager(genutil.AppModuleBasic{}) + +func TestAddGenesisAccountCmd(t *testing.T) { + _, _, addr1 := testdata.KeyTestPubAddr() + tests := []struct { + name string + addr string + denom string + expectErr bool + }{ + { + name: "invalid address", + addr: "", + denom: "1000atom", + expectErr: true, + }, + { + name: "valid address", + addr: addr1.String(), + denom: "1000atom", + expectErr: false, + }, + { + name: "multiple denoms", + addr: addr1.String(), + denom: "1000atom, 2000stake", + expectErr: false, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + appCodec, _ := simapp.MakeCodecs() + err = genutiltest.ExecInitCmd(testMbm, home, appCodec) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + clientCtx := client.Context{}.WithJSONMarshaler(appCodec).WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := simcmd.AddGenesisAccountCmd(home) + cmd.SetArgs([]string{ + tc.addr, + tc.denom, + fmt.Sprintf("--%s=home", flags.FlagHome)}) + + if tc.expectErr { + require.Error(t, cmd.ExecuteContext(ctx)) + } else { + require.NoError(t, cmd.ExecuteContext(ctx)) + } + }) + } +} diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go new file mode 100644 index 000000000000..b9027938be26 --- /dev/null +++ b/simapp/simd/cmd/root.go @@ -0,0 +1,237 @@ +package cmd + +import ( + "errors" + "io" + "os" + "path/filepath" + + "github.com/spf13/cast" + "github.com/spf13/cobra" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + config "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/debug" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/server" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/snapshots" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingcli "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" +) + +// NewRootCmd creates a new root command for simd. It is called once in the +// main function. +func NewRootCmd() (*cobra.Command, params.EncodingConfig) { + encodingConfig := simapp.MakeTestEncodingConfig() + initClientCtx := client.Context{}. + WithJSONMarshaler(encodingConfig.Marshaler). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithInput(os.Stdin). + WithAccountRetriever(types.AccountRetriever{}). + WithBroadcastMode(flags.BroadcastBlock). + WithHomeDir(simapp.DefaultNodeHome). + WithViper("") // In simapp, we don't use any prefix for env variables. + + rootCmd := &cobra.Command{ + Use: "simd", + Short: "simulation app", + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + initClientCtx = client.ReadHomeFlag(initClientCtx, cmd) + + initClientCtx, err := config.ReadFromClientConfig(initClientCtx) + if err != nil { + return err + } + + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { + return err + } + + return server.InterceptConfigsPreRunHandler(cmd) + }, + } + + initRootCmd(rootCmd, encodingConfig) + + return rootCmd, encodingConfig +} + +func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { + authclient.Codec = encodingConfig.Marshaler + + rootCmd.AddCommand( + genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), + genutilcli.MigrateGenesisCmd(), + genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), + genutilcli.ValidateGenesisCmd(simapp.ModuleBasics), + AddGenesisAccountCmd(simapp.DefaultNodeHome), + tmcli.NewCompletionCmd(rootCmd, true), + testnetCmd(simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), + debug.Cmd(), + config.Cmd(), + ) + + a := appCreator{encodingConfig} + server.AddCommands(rootCmd, simapp.DefaultNodeHome, a.newApp, a.appExport, addModuleInitFlags) + + // add keybase, auxiliary RPC, query, and tx child commands + rootCmd.AddCommand( + rpc.StatusCommand(), + queryCommand(), + txCommand(), + keys.Commands(simapp.DefaultNodeHome), + ) +} + +func addModuleInitFlags(startCmd *cobra.Command) { + crisis.AddModuleInitFlags(startCmd) +} + +func queryCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + authcmd.GetAccountCmd(), + rpc.ValidatorCommand(), + rpc.BlockCommand(), + authcmd.QueryTxsByEventsCmd(), + authcmd.QueryTxCmd(), + ) + + simapp.ModuleBasics.AddQueryCommands(cmd) + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + + return cmd +} + +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + authcmd.GetSignCommand(), + authcmd.GetSignBatchCommand(), + authcmd.GetMultiSignCommand(), + authcmd.GetMultiSignBatchCmd(), + authcmd.GetValidateSignaturesCommand(), + flags.LineBreak, + authcmd.GetBroadcastCommand(), + authcmd.GetEncodeCommand(), + authcmd.GetDecodeCommand(), + flags.LineBreak, + vestingcli.GetTxCmd(), + ) + + simapp.ModuleBasics.AddTxCommands(cmd) + cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") + + return cmd +} + +type appCreator struct { + encCfg params.EncodingConfig +} + +// newApp is an AppCreator +func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { + var cache sdk.MultiStorePersistentCache + + if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { + cache = store.NewCommitKVStoreCacheManager() + } + + skipUpgradeHeights := make(map[int64]bool) + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + + pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) + if err != nil { + panic(err) + } + + snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") + snapshotDB, err := sdk.NewLevelDB("metadata", snapshotDir) + if err != nil { + panic(err) + } + snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) + if err != nil { + panic(err) + } + + return simapp.NewSimApp( + logger, db, traceStore, true, skipUpgradeHeights, + cast.ToString(appOpts.Get(flags.FlagHome)), + cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), + a.encCfg, + appOpts, + baseapp.SetPruning(pruningOpts), + baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), + baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), + baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), + baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), + baseapp.SetInterBlockCache(cache), + baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), + baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), + baseapp.SetSnapshotStore(snapshotStore), + baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))), + baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))), + ) +} + +// appExport creates a new simapp (optionally at a given height) +// and exports state. +func (a appCreator) appExport( + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, + appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) { + + var simApp *simapp.SimApp + homePath, ok := appOpts.Get(flags.FlagHome).(string) + if !ok || homePath == "" { + return servertypes.ExportedApp{}, errors.New("application home not set") + } + + if height != -1 { + simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) + + if err := simApp.LoadHeight(height); err != nil { + return servertypes.ExportedApp{}, err + } + } else { + simApp = simapp.NewSimApp(logger, db, traceStore, true, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) + } + + return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) +} diff --git a/simapp/simd/cmd/testnet.go b/simapp/simd/cmd/testnet.go new file mode 100644 index 000000000000..0717b398427c --- /dev/null +++ b/simapp/simd/cmd/testnet.go @@ -0,0 +1,400 @@ +package cmd + +// DONTCOVER + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/spf13/cobra" + tmconfig "github.com/tendermint/tendermint/config" + tmos "github.com/tendermint/tendermint/libs/os" + tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "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/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagStartingIPAddress = "starting-ip-address" +) + +// get cmd to initialize all files for tendermint testnet and application +func testnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command { + cmd := &cobra.Command{ + Use: "testnet", + Short: "Initialize files for a simapp testnet", + Long: `testnet will create "v" number of directories and populate each with +necessary files (private validator, genesis, config, etc.). + +Note, strict routability for addresses is turned off in the config file. + +Example: + simd testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 + `, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + outputDir, _ := cmd.Flags().GetString(flagOutputDir) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) + minGasPrices, _ := cmd.Flags().GetString(server.FlagMinGasPrices) + nodeDirPrefix, _ := cmd.Flags().GetString(flagNodeDirPrefix) + nodeDaemonHome, _ := cmd.Flags().GetString(flagNodeDaemonHome) + startingIPAddress, _ := cmd.Flags().GetString(flagStartingIPAddress) + numValidators, _ := cmd.Flags().GetInt(flagNumValidators) + algo, _ := cmd.Flags().GetString(flags.FlagKeyAlgorithm) + + return InitTestnet( + clientCtx, cmd, config, mbm, genBalIterator, outputDir, chainID, minGasPrices, + nodeDirPrefix, nodeDaemonHome, startingIPAddress, keyringBackend, algo, numValidators, + ) + }, + } + + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", "Directory to store initialization data for the testnet") + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "simd", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.Flags().String(flags.FlagKeyAlgorithm, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for") + + return cmd +} + +const nodeDirPerm = 0755 + +// Initialize the testnet +func InitTestnet( + clientCtx client.Context, + cmd *cobra.Command, + nodeConfig *tmconfig.Config, + mbm module.BasicManager, + genBalIterator banktypes.GenesisBalancesIterator, + outputDir, + chainID, + minGasPrices, + nodeDirPrefix, + nodeDaemonHome, + startingIPAddress, + keyringBackend, + algoStr string, + numValidators int, +) error { + + if chainID == "" { + chainID = "chain-" + tmrand.NewRand().Str(6) + } + + nodeIDs := make([]string, numValidators) + valPubKeys := make([]cryptotypes.PubKey, numValidators) + + simappConfig := srvconfig.DefaultConfig() + simappConfig.MinGasPrices = minGasPrices + simappConfig.API.Enable = true + simappConfig.Telemetry.Enabled = true + simappConfig.Telemetry.PrometheusRetentionTime = 60 + simappConfig.Telemetry.EnableHostnameLabel = false + simappConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", chainID}} + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + inBuf := bufio.NewReader(cmd.InOrStdin()) + // generate private keys, node IDs, and initial transactions + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + + nodeConfig.SetRoot(nodeDir) + nodeConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + nodeConfig.Moniker = nodeDirName + + ip, err := getIP(i, startingIPAddress) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + genFiles = append(genFiles, nodeConfig.GenesisFile()) + + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, nodeDir, inBuf) + if err != nil { + return err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(algoStr, keyringAlgos) + if err != nil { + return err + } + + addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, true, algo) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + info := map[string]string{"secret": secret} + + cliPrint, err := json.Marshal(info) + if err != nil { + return err + } + + // save private key seed words + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil { + return err + } + + accTokens := sdk.TokensFromConsensusPower(1000) + accStakingTokens := sdk.TokensFromConsensusPower(500) + coins := sdk.Coins{ + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), + sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + } + + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + valTokens := sdk.TokensFromConsensusPower(100) + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + if err != nil { + return err + } + + txBuilder := clientCtx.TxConfig.NewTxBuilder() + if err := txBuilder.SetMsgs(createValMsg); err != nil { + return err + } + + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(chainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(clientCtx.TxConfig) + + if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { + return err + } + + txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return err + } + + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { + return err + } + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), simappConfig) + } + + if err := initGenFiles(clientCtx, mbm, chainID, genAccounts, genBalances, genFiles, numValidators); err != nil { + return err + } + + err := collectGenFiles( + clientCtx, nodeConfig, chainID, nodeIDs, valPubKeys, numValidators, + outputDir, nodeDirPrefix, nodeDaemonHome, genBalIterator, + ) + if err != nil { + return err + } + + cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators) + return nil +} + +func initGenFiles( + clientCtx client.Context, mbm module.BasicManager, chainID string, + genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, + genFiles []string, numValidators int, +) error { + + appGenState := mbm.DefaultGenesis(clientCtx.JSONMarshaler) + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + clientCtx.JSONMarshaler.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = accounts + appGenState[authtypes.ModuleName] = clientCtx.JSONMarshaler.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + clientCtx.JSONMarshaler.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = genBalances + appGenState[banktypes.ModuleName] = clientCtx.JSONMarshaler.MustMarshalJSON(&bankGenState) + + appGenStateJSON, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return err + } + + genDoc := types.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + return nil +} + +func collectGenFiles( + clientCtx client.Context, nodeConfig *tmconfig.Config, chainID string, + nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int, + outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, +) error { + + var appState json.RawMessage + genTime := tmtime.Now() + + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + nodeConfig.Moniker = nodeDirName + + nodeConfig.SetRoot(nodeDir) + + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] + initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) + + genDoc, err := types.GenesisDocFromFile(nodeConfig.GenesisFile()) + if err != nil { + return err + } + + nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.JSONMarshaler, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := nodeConfig.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func getIP(i int, startingIPAddr string) (ip string, err error) { + if len(startingIPAddr) == 0 { + ip, err = server.ExternalIP() + if err != nil { + return "", err + } + return ip, nil + } + return calculateIP(startingIPAddr, i) +} + +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0755) + if err != nil { + return err + } + + err = tmos.WriteFile(file, contents, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/simapp/simd/main.go b/simapp/simd/main.go new file mode 100644 index 000000000000..3e744360f468 --- /dev/null +++ b/simapp/simd/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/simd/cmd" +) + +func main() { + rootCmd, _ := cmd.NewRootCmd() + + if err := svrcmd.Execute(rootCmd, simapp.DefaultNodeHome); err != nil { + switch e := err.(type) { + case server.ErrorCode: + os.Exit(e.Code) + + default: + os.Exit(1) + } + } +} diff --git a/simapp/state.go b/simapp/state.go index 942ec784681d..4c3773813a04 100644 --- a/simapp/state.go +++ b/simapp/state.go @@ -8,25 +8,26 @@ import ( "math/rand" "time" - "github.com/tendermint/tendermint/crypto/secp256k1" + tmjson "github.com/tendermint/tendermint/libs/json" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" - simapparams "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/simulation" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. // It panics if the user provides files for both of them. // If a file is not given for the genesis or the sim params, it creates a randomized one. -func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulation.AppStateFn { - return func(r *rand.Rand, accs []simulation.Account, config simulation.Config, - ) (appState json.RawMessage, simAccs []simulation.Account, chainID string, genesisTimestamp time.Time) { +func AppStateFn(cdc codec.JSONMarshaler, simManager *module.SimulationManager) simtypes.AppStateFn { + return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config, + ) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) { if FlagGenesisTimeValue == 0 { - genesisTimestamp = simulation.RandTimestamp(r) + genesisTimestamp = simtypes.RandTimestamp(r) } else { genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0) } @@ -50,17 +51,20 @@ func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulati simAccs = accounts case config.ParamsFile != "": - appParams := make(simulation.AppParams) + appParams := make(simtypes.AppParams) bz, err := ioutil.ReadFile(config.ParamsFile) if err != nil { panic(err) } - cdc.MustUnmarshalJSON(bz, &appParams) + err = json.Unmarshal(bz, &appParams) + if err != nil { + panic(err) + } appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) default: - appParams := make(simulation.AppParams) + appParams := make(simtypes.AppParams) appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) } @@ -71,21 +75,21 @@ func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulati // AppStateRandomizedFn creates calls each module's GenesisState generator function // and creates the simulation params func AppStateRandomizedFn( - simManager *module.SimulationManager, r *rand.Rand, cdc *codec.Codec, - accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams, -) (json.RawMessage, []simulation.Account) { + simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONMarshaler, + accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams, +) (json.RawMessage, []simtypes.Account) { numAccs := int64(len(accs)) - genesisState := NewDefaultGenesisState() + genesisState := NewDefaultGenesisState(cdc) // generate a random amount of initial stake coins and a random initial // number of bonded accounts var initialStake, numInitiallyBonded int64 appParams.GetOrGenerate( - cdc, simapparams.StakePerAccount, &initialStake, r, + cdc, simappparams.StakePerAccount, &initialStake, r, func(r *rand.Rand) { initialStake = r.Int63n(1e12) }, ) appParams.GetOrGenerate( - cdc, simapparams.InitiallyBondedValidators, &numInitiallyBonded, r, + cdc, simappparams.InitiallyBondedValidators, &numInitiallyBonded, r, func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) }, ) @@ -115,7 +119,7 @@ func AppStateRandomizedFn( simManager.GenerateGenesisStates(simState) - appState, err := cdc.MarshalJSON(genesisState) + appState, err := json.Marshal(genesisState) if err != nil { panic(err) } @@ -125,24 +129,31 @@ func AppStateRandomizedFn( // AppStateFromGenesisFileFn util function to generate the genesis AppState // from a genesis.json file. -func AppStateFromGenesisFileFn(r io.Reader, cdc *codec.Codec, genesisFile string) (tmtypes.GenesisDoc, []simulation.Account) { +func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONMarshaler, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) { bytes, err := ioutil.ReadFile(genesisFile) if err != nil { panic(err) } var genesis tmtypes.GenesisDoc - cdc.MustUnmarshalJSON(bytes, &genesis) + // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc + err = tmjson.Unmarshal(bytes, &genesis) + if err != nil { + panic(err) + } var appState GenesisState - cdc.MustUnmarshalJSON(genesis.AppState, &appState) + err = json.Unmarshal(genesis.AppState, &appState) + if err != nil { + panic(err) + } - var authGenesis auth.GenesisState - if appState[auth.ModuleName] != nil { - cdc.MustUnmarshalJSON(appState[auth.ModuleName], &authGenesis) + var authGenesis authtypes.GenesisState + if appState[authtypes.ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis) } - newAccs := make([]simulation.Account, len(authGenesis.Accounts)) + newAccs := make([]simtypes.Account, len(authGenesis.Accounts)) for i, acc := range authGenesis.Accounts { // Pick a random private key, since we don't know the actual key // This should be fine as it's only used for mock Tendermint validators @@ -152,10 +163,15 @@ func AppStateFromGenesisFileFn(r io.Reader, cdc *codec.Codec, genesisFile string panic(err) } - privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) + privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed) + + a, ok := acc.GetCachedValue().(authtypes.AccountI) + if !ok { + panic("expected account") + } // create simulator accounts - simAcc := simulation.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: acc.GetAddress()} + simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()} newAccs[i] = simAcc } diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 5192e59feab0..83f2ea45351f 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -1,33 +1,70 @@ package simapp import ( - "os" + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" "testing" + "time" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" bam "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/supply" + "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" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) +// DefaultConsensusParams defines the default Tendermint consensus params used in +// SimApp testing. +var DefaultConsensusParams = &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: 200000, + MaxGas: 2000000, + }, + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: 302400, + MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration + MaxBytes: 10000, + }, + Validator: &tmproto.ValidatorParams{ + PubKeyTypes: []string{ + tmtypes.ABCIPubKeyTypeEd25519, + }, + }, +} + +func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { + db := dbm.NewMemDB() + encCdc := MakeTestEncodingConfig() + app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}) + if withGenesis { + return app, NewDefaultGenesisState(encCdc.Marshaler) + } + return app, GenesisState{} +} + // Setup initializes a new SimApp. A Nop logger is set in SimApp. func Setup(isCheckTx bool) *SimApp { - db := dbm.NewMemDB() - app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0) + app, genesisState := setup(!isCheckTx, 5) if !isCheckTx { // init chain must be called to stop deliverState from being nil - genesisState := NewDefaultGenesisState() - stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") if err != nil { panic(err) } @@ -35,8 +72,9 @@ func Setup(isCheckTx bool) *SimApp { // Initialize the chain app.InitChain( abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - AppStateBytes: stateBytes, + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, }, ) } @@ -44,68 +82,242 @@ func Setup(isCheckTx bool) *SimApp { return app } -// SetupWithGenesisAccounts initializes a new SimApp with the passed in -// genesis accounts. -func SetupWithGenesisAccounts(genAccs []authexported.GenesisAccount) *SimApp { - db := dbm.NewMemDB() - app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) +// SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit (10^6) in the default token of the simapp from first genesis +// account. A Nop logger is set in SimApp. +func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + app, genesisState := setup(true, 5) + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) + + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.NewInt(1000000) - // initialize the chain with the passed in genesis accounts - genesisState := NewDefaultGenesisState() + for _, val := range valSet.Validators { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) + require.NoError(t, err) + pkAny, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + validator := stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondAmt, + DelegatorShares: sdk.OneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.ZeroInt(), + } + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) - authGenesis := auth.NewGenesisState(auth.DefaultParams(), genAccs) - genesisStateBz := app.Codec().MustMarshalJSON(authGenesis) - genesisState[auth.ModuleName] = genesisStateBz + } + + // set validators and delegations + stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) + + totalSupply := sdk.NewCoins() + for _, b := range balances { + // add genesis acc tokens and delegated tokens to total supply + totalSupply = totalSupply.Add(b.Coins.Add(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) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + + // commit genesis changes + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }}) - stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + return app +} + +// SetupWithGenesisAccounts initializes a new SimApp with the provided genesis +// accounts and possible balances. +func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { + app, genesisState := setup(true, 0) + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) + + totalSupply := sdk.NewCoins() + for _, b := range balances { + totalSupply = totalSupply.Add(b.Coins...) + } + + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") if err != nil { panic(err) } - // Initialize the chain app.InitChain( abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - AppStateBytes: stateBytes, + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: DefaultConsensusParams, + AppStateBytes: stateBytes, }, ) app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: app.LastBlockHeight() + 1}}) + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) return app } -// AddTestAddrs constructs and returns accNum amount of accounts with an -// initial balance of accAmt -func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { +type GenerateAccountStrategy func(int) []sdk.AccAddress + +// createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. +func createRandomAccounts(accNum int) []sdk.AccAddress { testAddrs := make([]sdk.AccAddress, accNum) for i := 0; i < accNum; i++ { pk := ed25519.GenPrivKey().PubKey() testAddrs[i] = sdk.AccAddress(pk.Address()) } + return testAddrs +} + +// createIncrementalAccounts is a strategy used by addTestAddrs() in order to generated addresses in ascending order. +func createIncrementalAccounts(accNum int) []sdk.AccAddress { + var addresses []sdk.AccAddress + var buffer bytes.Buffer + + // start at 100 so we can make up to 999 test addresses with valid test addresses + for i := 100; i < (accNum + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string + + buffer.WriteString(numString) //adding on final two digits to make addresses unique + res, _ := sdk.AccAddressFromHex(buffer.String()) + bech := res.String() + addr, _ := TestAddr(buffer.String(), bech) + + addresses = append(addresses, addr) + buffer.Reset() + } + + return addresses +} + +// AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. +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) + } +} + +// 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 { + return addTestAddrs(app, ctx, accNum, accAmt, createRandomAccounts) +} + +// AddTestAddrs constructs and returns accNum amount of accounts with an +// initial balance of accAmt in random order +func AddTestAddrsIncremental(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { + return addTestAddrs(app, ctx, accNum, accAmt, createIncrementalAccounts) +} + +func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { + testAddrs := strategy(accNum) + initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt.MulRaw(int64(len(testAddrs))))) - prevSupply := app.SupplyKeeper.GetSupply(ctx) - app.SupplyKeeper.SetSupply(ctx, supply.NewSupply(prevSupply.GetTotal().Add(totalSupply...))) + setTotalSupply(app, ctx, accAmt, accNum) // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range testAddrs { - _, err := app.BankKeeper.AddCoins(ctx, addr, initCoins) - if err != nil { - panic(err) - } + saveAccount(app, ctx, addr, initCoins) } + return testAddrs } -// CheckBalance checks the balance of an account. -func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins) { - ctxCheck := app.BaseApp.NewContext(true, abci.Header{}) - res := app.AccountKeeper.GetAccount(ctxCheck, addr) +// 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) + if err != nil { + panic(err) + } +} + +// ConvertAddrsToValAddrs converts the provided addresses to ValAddress. +func ConvertAddrsToValAddrs(addrs []sdk.AccAddress) []sdk.ValAddress { + valAddrs := make([]sdk.ValAddress, len(addrs)) - require.True(t, exp.IsEqual(res.GetCoins())) + for i, addr := range addrs { + valAddrs[i] = sdk.ValAddress(addr) + } + + return valAddrs +} + +func TestAddr(addr string, bech string) (sdk.AccAddress, error) { + res, err := sdk.AccAddressFromHex(addr) + if err != nil { + return nil, err + } + bechexpected := res.String() + if bech != bechexpected { + return nil, fmt.Errorf("bech encoding doesn't match reference") + } + + bechres, err := sdk.AccAddressFromBech32(bech) + if err != nil { + return nil, err + } + if !bytes.Equal(bechres, res) { + return nil, err + } + + return res, nil +} + +// CheckBalance checks the balance of an account. +func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) { + ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) + require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr))) } // SignCheckDeliver checks a generated signed transaction and simulates a @@ -113,25 +325,26 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins) // the parameter 'expPass' against the result. A corresponding result is // returned. func SignCheckDeliver( - t *testing.T, cdc *codec.Codec, app *bam.BaseApp, header abci.Header, msgs []sdk.Msg, - accNums, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, + t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, + chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, ) (sdk.GasInfo, *sdk.Result, error) { - tx := helpers.GenTx( + tx, err := helpers.GenTx( + txCfg, msgs, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, helpers.DefaultGenTxGas, - "", + chainID, accNums, - seq, + accSeqs, priv..., ) - - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + txBytes, err := txCfg.TxEncoder()(tx) require.Nil(t, err) // Must simulate now as CheckTx doesn't run Msgs anymore - _, res, err := app.Simulate(txBytes, tx) + _, res, err := app.Simulate(txBytes) if expSimPass { require.NoError(t, err) @@ -143,7 +356,7 @@ func SignCheckDeliver( // Simulate a sending a transaction and committing a block app.BeginBlock(abci.RequestBeginBlock{Header: header}) - gInfo, res, err := app.Deliver(tx) + gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) if expPass { require.NoError(t, err) @@ -162,10 +375,12 @@ func SignCheckDeliver( // GenSequenceOfTxs generates a set of signed transactions of messages, such // that they differ only by having the sequence numbers incremented between // every transaction. -func GenSequenceOfTxs(msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx { - txs := make([]auth.StdTx, numToGenerate) +func GenSequenceOfTxs(txGen client.TxConfig, msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...cryptotypes.PrivKey) ([]sdk.Tx, error) { + txs := make([]sdk.Tx, numToGenerate) + var err error for i := 0; i < numToGenerate; i++ { - txs[i] = helpers.GenTx( + txs[i], err = helpers.GenTx( + txGen, msgs, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, helpers.DefaultGenTxGas, @@ -174,10 +389,13 @@ func GenSequenceOfTxs(msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, nu initSeqNums, priv..., ) + if err != nil { + break + } incrementAllSequenceNumbers(initSeqNums) } - return txs + return txs, err } func incrementAllSequenceNumbers(initSeqNums []uint64) { @@ -185,3 +403,40 @@ func incrementAllSequenceNumbers(initSeqNums []uint64) { initSeqNums[i]++ } } + +// CreateTestPubKeys returns a total of numPubKeys public keys in ascending order. +func CreateTestPubKeys(numPubKeys int) []cryptotypes.PubKey { + var publicKeys []cryptotypes.PubKey + var buffer bytes.Buffer + + // start at 10 to avoid changing 1 to 01, 2 to 02, etc + for i := 100; i < (numPubKeys + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") // base pubkey string + buffer.WriteString(numString) // adding on final two digits to make pubkeys unique + publicKeys = append(publicKeys, NewPubKeyFromHex(buffer.String())) + buffer.Reset() + } + + return publicKeys +} + +// NewPubKeyFromHex returns a PubKey from a hex string. +func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { + pkBytes, err := hex.DecodeString(pk) + if err != nil { + panic(err) + } + if len(pkBytes) != ed25519.PubKeySize { + panic(errors.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")) + } + return &ed25519.PubKey{Key: pkBytes} +} + +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} diff --git a/simapp/types.go b/simapp/types.go index a3f146f67ceb..0e190af1bc93 100644 --- a/simapp/types.go +++ b/simapp/types.go @@ -1,12 +1,10 @@ package simapp import ( - "encoding/json" - abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" ) @@ -19,7 +17,7 @@ type App interface { // The application types codec. // NOTE: This shoult be sealed before being returned. - Codec() *codec.Codec + LegacyAmino() *codec.LegacyAmino // Application updates every begin block. BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock @@ -35,8 +33,8 @@ type App interface { // Exports the state of the application for a genesis file. ExportAppStateAndValidators( - forZeroHeight bool, jailWhiteList []string, - ) (json.RawMessage, []tmtypes.GenesisValidator, error) + forZeroHeight bool, jailAllowedAddrs []string, + ) (types.ExportedApp, error) // All the registered module account addreses. ModuleAccountAddrs() map[string]bool diff --git a/simapp/utils.go b/simapp/utils.go index bc3e889fe68c..2067ed54b108 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -5,23 +5,23 @@ import ( "fmt" "io/ioutil" - tmkv "github.com/tendermint/tendermint/libs/kv" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/simulation" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" ) // SetupSimulation creates the config, db (levelDB), temporary directory and logger for // the simulation tests. If `FlagEnabledValue` is false it skips the current test. // Returns error on an invalid db intantiation or temp dir creation. -func SetupSimulation(dirPrefix, dbName string) (simulation.Config, dbm.DB, string, log.Logger, bool, error) { +func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, dbm.DB, string, log.Logger, bool, error) { if !FlagEnabledValue { - return simulation.Config{}, nil, "", nil, true, nil + return simtypes.Config{}, nil, "", nil, true, nil } config := NewConfigFromFlags() @@ -36,12 +36,12 @@ func SetupSimulation(dirPrefix, dbName string) (simulation.Config, dbm.DB, strin dir, err := ioutil.TempDir("", dirPrefix) if err != nil { - return simulation.Config{}, nil, "", nil, false, err + return simtypes.Config{}, nil, "", nil, false, err } db, err := sdk.NewLevelDB(dbName, dir) if err != nil { - return simulation.Config{}, nil, "", nil, false, err + return simtypes.Config{}, nil, "", nil, false, err } return config, db, dir, logger, false, nil @@ -49,9 +49,9 @@ func SetupSimulation(dirPrefix, dbName string) (simulation.Config, dbm.DB, strin // SimulationOperations retrieves the simulation params from the provided file path // and returns all the modules weighted operations -func SimulationOperations(app App, cdc *codec.Codec, config simulation.Config) []simulation.WeightedOperation { +func SimulationOperations(app App, cdc codec.JSONMarshaler, config simtypes.Config) []simtypes.WeightedOperation { simState := module.SimulationState{ - AppParams: make(simulation.AppParams), + AppParams: make(simtypes.AppParams), Cdc: cdc, } @@ -61,7 +61,10 @@ func SimulationOperations(app App, cdc *codec.Codec, config simulation.Config) [ panic(err) } - app.Codec().MustUnmarshalJSON(bz, &simState.AppParams) + err = json.Unmarshal(bz, &simState.AppParams) + if err != nil { + panic(err) + } } simState.ParamChanges = app.SimulationManager().GenerateParamChanges(config.Seed) @@ -72,16 +75,16 @@ func SimulationOperations(app App, cdc *codec.Codec, config simulation.Config) [ // CheckExportSimulation exports the app state and simulation parameters to JSON // if the export paths are defined. func CheckExportSimulation( - app App, config simulation.Config, params simulation.Params, + app App, config simtypes.Config, params simtypes.Params, ) error { if config.ExportStatePath != "" { fmt.Println("exporting app state...") - appState, _, err := app.ExportAppStateAndValidators(false, nil) + exported, err := app.ExportAppStateAndValidators(false, nil) if err != nil { return err } - if err := ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644); err != nil { + if err := ioutil.WriteFile(config.ExportStatePath, []byte(exported.AppState), 0600); err != nil { return err } } @@ -93,7 +96,7 @@ func CheckExportSimulation( return err } - if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644); err != nil { + if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0600); err != nil { return err } } @@ -109,9 +112,8 @@ func PrintStats(db dbm.DB) { // GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the // each's module store key and the prefix bytes of the KVPair's key. -func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, cdc *codec.Codec, kvAs, kvBs []tmkv.Pair) (log string) { +func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, kvAs, kvBs []kv.Pair) (log string) { for i := 0; i < len(kvAs); i++ { - if len(kvAs[i].Value) == 0 && len(kvBs[i].Value) == 0 { // skip if the value doesn't have any bytes continue @@ -119,11 +121,11 @@ func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, cdc *codec decoder, ok := sdr[storeName] if ok { - log += decoder(cdc, kvAs[i], kvBs[i]) + log += decoder(kvAs[i], kvBs[i]) } else { log += fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", kvAs[i].Key, kvAs[i].Value, kvBs[i].Key, kvBs[i].Value) } } - return + return log } diff --git a/simapp/utils_test.go b/simapp/utils_test.go index 9cadfff0da40..6d8bb21f3f08 100644 --- a/simapp/utils_test.go +++ b/simapp/utils_test.go @@ -5,37 +5,48 @@ import ( "testing" "github.com/stretchr/testify/require" - tmkv "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/std" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) +func makeCodec(bm module.BasicManager) *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + + bm.RegisterLegacyAminoCodec(cdc) + std.RegisterLegacyAminoCodec(cdc) + + return cdc +} + func TestGetSimulationLog(t *testing.T) { - cdc := MakeCodec() + cdc := makeCodec(ModuleBasics) decoders := make(sdk.StoreDecoderRegistry) - decoders[auth.StoreKey] = func(cdc *codec.Codec, kvAs, kvBs tmkv.Pair) string { return "10" } + decoders[authtypes.StoreKey] = func(kvAs, kvBs kv.Pair) string { return "10" } tests := []struct { store string - kvPairs []tmkv.Pair + kvPairs []kv.Pair expectedLog string }{ { "Empty", - []tmkv.Pair{{}}, + []kv.Pair{{}}, "", }, { - auth.StoreKey, - []tmkv.Pair{{Key: auth.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryLengthPrefixed(uint64(10))}}, + authtypes.StoreKey, + []kv.Pair{{Key: authtypes.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryBare(uint64(10))}}, "10", }, { "OtherStore", - []tmkv.Pair{{Key: []byte("key"), Value: []byte("value")}}, + []kv.Pair{{Key: []byte("key"), Value: []byte("value")}}, fmt.Sprintf("store A %X => %X\nstore B %X => %X\n", []byte("key"), []byte("value"), []byte("key"), []byte("value")), }, } @@ -43,7 +54,7 @@ func TestGetSimulationLog(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.store, func(t *testing.T) { - require.Equal(t, tt.expectedLog, GetSimulationLog(tt.store, decoders, cdc, tt.kvPairs, tt.kvPairs), tt.store) + require.Equal(t, tt.expectedLog, GetSimulationLog(tt.store, decoders, tt.kvPairs, tt.kvPairs), tt.store) }) } } diff --git a/snapcraft.yaml b/snapcraft.yaml deleted file mode 100644 index 912aa50f69bc..000000000000 --- a/snapcraft.yaml +++ /dev/null @@ -1,46 +0,0 @@ -name: gaia -version: git -summary: Gaia Daemon # 79 char long summary -description: | - This snap provides the Gaia daemon gaiad and the command line - tool gaiacli. -grade: stable -confinement: strict - -apps: - gaiad: - command: bin/gaiad - plugs: [home,network,network-bind] - gaiacli: - command: bin/gaiacli - plugs: [home,network,network-bind,raw-usb] - -parts: - gaia: - plugin: dump - source: ./ - override-pull: | - rootdir=$(pwd) - gitroot=$(git rev-parse --show-toplevel) - cd ${gitroot} && git archive \ - -o ${rootdir}/gaia-git.tar.gz \ - --format tar.gz -9 --prefix gaia-git/ HEAD - cd ${rootdir} - tar xf gaia-git.tar.gz ; rm -f gaia-git.tar.gz - mkdir -p go/src/github.com/cosmos bin - mv gaia-git/ go/src/github.com/cosmos/cosmos-sdk/ - - build-snaps: [go] - override-build: | - base=`pwd` - export GO111MODULE=on - export GOPATH=`pwd`/go - export GOBIN=$GOPATH/bin - export PATH=$GOBIN:$PATH - cd $GOPATH/src/github.com/cosmos/cosmos-sdk - make go-mod-cache - make tools - make install - mkdir $SNAPCRAFT_PART_INSTALL/bin - cp $GOPATH/bin/gaiad $SNAPCRAFT_PART_INSTALL/bin - cp $GOPATH/bin/gaiacli $SNAPCRAFT_PART_INSTALL/bin diff --git a/snapcraft.yaml.in b/snapcraft.yaml.in deleted file mode 100644 index 8bb2ad73d72d..000000000000 --- a/snapcraft.yaml.in +++ /dev/null @@ -1,35 +0,0 @@ -name: gaia # you probably want to 'snapcraft register ' -# base: core18 # the base snap is the execution environment for this snap -version: '@VERSION@' # just for humans, typically '1.2+git' or '1.3.2' -summary: Gaia Daemon # 79 char long summary -description: | - This snap provides the Gaia daemon gaiad and the command line - tool gaiacli. -grade: devel # must be 'stable' to release into candidate/stable channels -confinement: strict # use 'strict' once you have the right plugs and slots - -apps: - gaiad: - command: bin/gaiad - plugs: [home,network,network-bind] - gaiacli: - command: bin/gaiacli - plugs: [home,network,network-bind] - -parts: - gaia: - plugin: dump - source: ./ - override-pull: | - echo "Installing files from $GOBIN ..." - - # Use the following instructions to build a package from a release. - # wget https://github.com/cosmos/cosmos-sdk/archive/v@VERSION@.tar.gz - # tar xvf v@VERSION@.tar.gz - # rm v@VERSION@.tar.gz - - build-snaps: [go] - override-build: | - mkdir -p $SNAPCRAFT_PART_INSTALL/bin - cp $GOBIN/gaiad $SNAPCRAFT_PART_INSTALL/bin - cp $GOBIN/gaiacli $SNAPCRAFT_PART_INSTALL/bin diff --git a/snapshots/helpers_test.go b/snapshots/helpers_test.go new file mode 100644 index 000000000000..751ac212d352 --- /dev/null +++ b/snapshots/helpers_test.go @@ -0,0 +1,149 @@ +package snapshots_test + +import ( + "bytes" + "crypto/sha256" + "errors" + "io" + "io/ioutil" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + db "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/snapshots" + "github.com/cosmos/cosmos-sdk/snapshots/types" +) + +func checksums(slice [][]byte) [][]byte { + hasher := sha256.New() + checksums := make([][]byte, len(slice)) + for i, chunk := range slice { + hasher.Write(chunk) + checksums[i] = hasher.Sum(nil) + hasher.Reset() + } + return checksums +} + +func hash(chunks [][]byte) []byte { + hasher := sha256.New() + for _, chunk := range chunks { + hasher.Write(chunk) + } + return hasher.Sum(nil) +} + +func makeChunks(chunks [][]byte) <-chan io.ReadCloser { + ch := make(chan io.ReadCloser, len(chunks)) + for _, chunk := range chunks { + ch <- ioutil.NopCloser(bytes.NewReader(chunk)) + } + close(ch) + return ch +} + +func readChunks(chunks <-chan io.ReadCloser) [][]byte { + bodies := [][]byte{} + for chunk := range chunks { + body, err := ioutil.ReadAll(chunk) + if err != nil { + panic(err) + } + bodies = append(bodies, body) + } + return bodies +} + +type mockSnapshotter struct { + chunks [][]byte +} + +func (m *mockSnapshotter) Restore( + height uint64, format uint32, chunks <-chan io.ReadCloser, ready chan<- struct{}, +) error { + if format == 0 { + return types.ErrUnknownFormat + } + if m.chunks != nil { + return errors.New("already has contents") + } + if ready != nil { + close(ready) + } + + m.chunks = [][]byte{} + for reader := range chunks { + chunk, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + m.chunks = append(m.chunks, chunk) + } + + return nil +} + +func (m *mockSnapshotter) Snapshot(height uint64, format uint32) (<-chan io.ReadCloser, error) { + if format == 0 { + return nil, types.ErrUnknownFormat + } + ch := make(chan io.ReadCloser, len(m.chunks)) + for _, chunk := range m.chunks { + ch <- ioutil.NopCloser(bytes.NewReader(chunk)) + } + close(ch) + return ch, nil +} + +// 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, 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() + mgr := snapshots.NewManager(store, hung) + + go func() { + _, err := mgr.Create(1) + require.NoError(t, err) + }() + time.Sleep(10 * time.Millisecond) + t.Cleanup(hung.Close) + + return mgr +} + +// hungSnapshotter can be used to test operations in progress. Call close to end the snapshot. +type hungSnapshotter struct { + ch chan struct{} +} + +func newHungSnapshotter() *hungSnapshotter { + return &hungSnapshotter{ + ch: make(chan struct{}), + } +} + +func (m *hungSnapshotter) Close() { + close(m.ch) +} + +func (m *hungSnapshotter) Snapshot(height uint64, format uint32) (<-chan io.ReadCloser, error) { + <-m.ch + ch := make(chan io.ReadCloser, 1) + ch <- ioutil.NopCloser(bytes.NewReader([]byte{})) + return ch, nil +} + +func (m *hungSnapshotter) Restore( + height uint64, format uint32, chunks <-chan io.ReadCloser, ready chan<- struct{}, +) error { + panic("not implemented") +} diff --git a/snapshots/manager.go b/snapshots/manager.go new file mode 100644 index 000000000000..3cb96e65f42f --- /dev/null +++ b/snapshots/manager.go @@ -0,0 +1,259 @@ +package snapshots + +import ( + "bytes" + "crypto/sha256" + "io" + "io/ioutil" + "sync" + + "github.com/cosmos/cosmos-sdk/snapshots/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + opNone operation = "" + opSnapshot operation = "snapshot" + opPrune operation = "prune" + opRestore operation = "restore" + + chunkBufferSize = 4 +) + +// operation represents a Manager operation. Only one operation can be in progress at a time. +type operation string + +// restoreDone represents the result of a restore operation. +type restoreDone struct { + complete bool // if true, restore completed successfully (not prematurely) + err error // if non-nil, restore errored +} + +// Manager manages snapshot and restore operations for an app, making sure only a single +// long-running operation is in progress at any given time, and provides convenience methods +// mirroring the ABCI interface. +// +// Although the ABCI interface (and this manager) passes chunks as byte slices, the internal +// snapshot/restore APIs use IO streams (i.e. chan io.ReadCloser), for two reasons: +// +// 1) In the future, ABCI should support streaming. Consider e.g. InitChain during chain +// upgrades, which currently passes the entire chain state as an in-memory byte slice. +// https://github.com/tendermint/tendermint/issues/5184 +// +// 2) io.ReadCloser streams automatically propagate IO errors, and can pass arbitrary +// errors via io.Pipe.CloseWithError(). +type Manager struct { + store *Store + target types.Snapshotter + + mtx sync.Mutex + operation operation + chRestore chan<- io.ReadCloser + chRestoreDone <-chan restoreDone + restoreChunkHashes [][]byte + restoreChunkIndex uint32 +} + +// NewManager creates a new manager. +func NewManager(store *Store, target types.Snapshotter) *Manager { + return &Manager{ + store: store, + target: target, + } +} + +// begin starts an operation, or errors if one is in progress. It manages the mutex itself. +func (m *Manager) begin(op operation) error { + m.mtx.Lock() + defer m.mtx.Unlock() + return m.beginLocked(op) +} + +// beginLocked begins an operation while already holding the mutex. +func (m *Manager) beginLocked(op operation) error { + if op == opNone { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "can't begin a none operation") + } + if m.operation != opNone { + return sdkerrors.Wrapf(sdkerrors.ErrConflict, "a %v operation is in progress", m.operation) + } + m.operation = op + return nil +} + +// end ends the current operation. +func (m *Manager) end() { + m.mtx.Lock() + defer m.mtx.Unlock() + m.endLocked() +} + +// endLocked ends the current operation while already holding the mutex. +func (m *Manager) endLocked() { + m.operation = opNone + if m.chRestore != nil { + close(m.chRestore) + m.chRestore = nil + } + m.chRestoreDone = nil + m.restoreChunkHashes = nil + m.restoreChunkIndex = 0 +} + +// Create creates a snapshot and returns its metadata. +func (m *Manager) Create(height uint64) (*types.Snapshot, error) { + if m == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "no snapshot store configured") + } + err := m.begin(opSnapshot) + if err != nil { + return nil, err + } + defer m.end() + + latest, err := m.store.GetLatest() + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to examine latest snapshot") + } + if latest != nil && latest.Height >= height { + return nil, sdkerrors.Wrapf(sdkerrors.ErrConflict, + "a more recent snapshot already exists at height %v", latest.Height) + } + + chunks, err := m.target.Snapshot(height, types.CurrentFormat) + if err != nil { + return nil, err + } + return m.store.Save(height, types.CurrentFormat, chunks) +} + +// List lists snapshots, mirroring ABCI ListSnapshots. It can be concurrent with other operations. +func (m *Manager) List() ([]*types.Snapshot, error) { + return m.store.List() +} + +// LoadChunk loads a chunk into a byte slice, mirroring ABCI LoadChunk. It can be called +// concurrently with other operations. If the chunk does not exist, nil is returned. +func (m *Manager) LoadChunk(height uint64, format uint32, chunk uint32) ([]byte, error) { + reader, err := m.store.LoadChunk(height, format, chunk) + if err != nil { + return nil, err + } + if reader == nil { + return nil, nil + } + defer reader.Close() + + return ioutil.ReadAll(reader) +} + +// Prune prunes snapshots, if no other operations are in progress. +func (m *Manager) Prune(retain uint32) (uint64, error) { + err := m.begin(opPrune) + if err != nil { + return 0, err + } + defer m.end() + return m.store.Prune(retain) +} + +// Restore begins an async snapshot restoration, mirroring ABCI OfferSnapshot. Chunks must be fed +// via RestoreChunk() until the restore is complete or a chunk fails. +func (m *Manager) Restore(snapshot types.Snapshot) error { + if snapshot.Chunks == 0 { + return sdkerrors.Wrap(types.ErrInvalidMetadata, "no chunks") + } + if uint32(len(snapshot.Metadata.ChunkHashes)) != snapshot.Chunks { + return sdkerrors.Wrapf(types.ErrInvalidMetadata, "snapshot has %v chunk hashes, but %v chunks", + uint32(len(snapshot.Metadata.ChunkHashes)), + snapshot.Chunks) + } + m.mtx.Lock() + defer m.mtx.Unlock() + err := m.beginLocked(opRestore) + if err != nil { + return err + } + + // Start an asynchronous snapshot restoration, passing chunks and completion status via channels. + chChunks := make(chan io.ReadCloser, chunkBufferSize) + chReady := make(chan struct{}, 1) + chDone := make(chan restoreDone, 1) + go func() { + err := m.target.Restore(snapshot.Height, snapshot.Format, chChunks, chReady) + chDone <- restoreDone{ + complete: err == nil, + err: err, + } + close(chDone) + }() + + // Check for any initial errors from the restore, before any chunks are fed. + select { + case done := <-chDone: + m.endLocked() + if done.err != nil { + return done.err + } + return sdkerrors.Wrap(sdkerrors.ErrLogic, "restore ended unexpectedly") + case <-chReady: + } + + m.chRestore = chChunks + m.chRestoreDone = chDone + m.restoreChunkHashes = snapshot.Metadata.ChunkHashes + m.restoreChunkIndex = 0 + return nil +} + +// RestoreChunk adds a chunk to an active snapshot restoration, mirroring ABCI ApplySnapshotChunk. +// Chunks must be given until the restore is complete, returning true, or a chunk errors. +func (m *Manager) RestoreChunk(chunk []byte) (bool, error) { + m.mtx.Lock() + defer m.mtx.Unlock() + if m.operation != opRestore { + return false, sdkerrors.Wrap(sdkerrors.ErrLogic, "no restore operation in progress") + } + + if int(m.restoreChunkIndex) >= len(m.restoreChunkHashes) { + return false, sdkerrors.Wrap(sdkerrors.ErrLogic, "received unexpected chunk") + } + + // Check if any errors have occurred yet. + select { + case done := <-m.chRestoreDone: + m.endLocked() + if done.err != nil { + return false, done.err + } + return false, sdkerrors.Wrap(sdkerrors.ErrLogic, "restore ended unexpectedly") + default: + } + + // Verify the chunk hash. + hash := sha256.Sum256(chunk) + expected := m.restoreChunkHashes[m.restoreChunkIndex] + if !bytes.Equal(hash[:], expected) { + return false, sdkerrors.Wrapf(types.ErrChunkHashMismatch, + "expected %x, got %x", hash, expected) + } + + // Pass the chunk to the restore, and wait for completion if it was the final one. + m.chRestore <- ioutil.NopCloser(bytes.NewReader(chunk)) + m.restoreChunkIndex++ + + if int(m.restoreChunkIndex) >= len(m.restoreChunkHashes) { + close(m.chRestore) + m.chRestore = nil + done := <-m.chRestoreDone + m.endLocked() + if done.err != nil { + return false, done.err + } + if !done.complete { + return false, sdkerrors.Wrap(sdkerrors.ErrLogic, "restore ended prematurely") + } + return true, nil + } + return false, nil +} diff --git a/snapshots/manager_test.go b/snapshots/manager_test.go new file mode 100644 index 000000000000..6069666f14f5 --- /dev/null +++ b/snapshots/manager_test.go @@ -0,0 +1,209 @@ +package snapshots_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/snapshots" + "github.com/cosmos/cosmos-sdk/snapshots/types" +) + +func TestManager_List(t *testing.T) { + store := setupStore(t) + manager := snapshots.NewManager(store, nil) + + mgrList, err := manager.List() + require.NoError(t, err) + storeList, err := store.List() + require.NoError(t, err) + + require.NotEmpty(t, storeList) + assert.Equal(t, storeList, mgrList) + + // list should not block or error on busy managers + manager = setupBusyManager(t) + list, err := manager.List() + require.NoError(t, err) + assert.Equal(t, []*types.Snapshot{}, list) +} + +func TestManager_LoadChunk(t *testing.T) { + store := setupStore(t) + manager := snapshots.NewManager(store, nil) + + // Existing chunk should return body + chunk, err := manager.LoadChunk(2, 1, 1) + require.NoError(t, err) + assert.Equal(t, []byte{2, 1, 1}, chunk) + + // Missing chunk should return nil + chunk, err = manager.LoadChunk(2, 1, 9) + require.NoError(t, err) + assert.Nil(t, chunk) + + // LoadChunk should not block or error on busy managers + manager = setupBusyManager(t) + chunk, err = manager.LoadChunk(2, 1, 0) + require.NoError(t, err) + assert.Nil(t, chunk) +} + +func TestManager_Take(t *testing.T) { + store := setupStore(t) + snapshotter := &mockSnapshotter{ + chunks: [][]byte{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + }, + } + manager := snapshots.NewManager(store, snapshotter) + + // nil manager should return error + _, err := (*snapshots.Manager)(nil).Create(1) + require.Error(t, err) + + // creating a snapshot at a lower height than the latest should error + _, err = manager.Create(3) + require.Error(t, err) + + // creating a snapshot at a higher height should be fine, and should return it + snapshot, err := manager.Create(5) + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{ + Height: 5, + Format: types.CurrentFormat, + Chunks: 3, + Hash: []uint8{0x47, 0xe4, 0xee, 0x7f, 0x21, 0x1f, 0x73, 0x26, 0x5d, 0xd1, 0x76, 0x58, 0xf6, 0xe2, 0x1c, 0x13, 0x18, 0xbd, 0x6c, 0x81, 0xf3, 0x75, 0x98, 0xe2, 0xa, 0x27, 0x56, 0x29, 0x95, 0x42, 0xef, 0xcf}, + Metadata: types.Metadata{ + ChunkHashes: checksums([][]byte{ + {1, 2, 3}, {4, 5, 6}, {7, 8, 9}}), + }, + }, snapshot) + + storeSnapshot, chunks, err := store.Load(snapshot.Height, snapshot.Format) + require.NoError(t, err) + assert.Equal(t, snapshot, storeSnapshot) + assert.Equal(t, [][]byte{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, readChunks(chunks)) + + // creating a snapshot while a different snapshot is being created should error + manager = setupBusyManager(t) + _, err = manager.Create(9) + require.Error(t, err) +} + +func TestManager_Prune(t *testing.T) { + store := setupStore(t) + manager := snapshots.NewManager(store, nil) + + pruned, err := manager.Prune(2) + require.NoError(t, err) + assert.EqualValues(t, 1, pruned) + + list, err := manager.List() + require.NoError(t, err) + assert.Len(t, list, 3) + + // Prune should error while a snapshot is being taken + manager = setupBusyManager(t) + _, err = manager.Prune(2) + require.Error(t, err) +} + +func TestManager_Restore(t *testing.T) { + store := setupStore(t) + target := &mockSnapshotter{} + manager := snapshots.NewManager(store, target) + + chunks := [][]byte{ + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9}, + } + + // Restore errors on invalid format + err := manager.Restore(types.Snapshot{ + Height: 3, + Format: 0, + Hash: []byte{1, 2, 3}, + Chunks: uint32(len(chunks)), + Metadata: types.Metadata{ChunkHashes: checksums(chunks)}, + }) + require.Error(t, err) + require.Equal(t, types.ErrUnknownFormat, err) + + // Restore errors on no chunks + err = manager.Restore(types.Snapshot{Height: 3, Format: 1, Hash: []byte{1, 2, 3}}) + require.Error(t, err) + + // Restore errors on chunk and chunkhashes mismatch + err = manager.Restore(types.Snapshot{ + Height: 3, + Format: 1, + Hash: []byte{1, 2, 3}, + Chunks: 4, + Metadata: types.Metadata{ChunkHashes: checksums(chunks)}, + }) + require.Error(t, err) + + // Starting a restore works + err = manager.Restore(types.Snapshot{ + Height: 3, + Format: 1, + Hash: []byte{1, 2, 3}, + Chunks: 3, + Metadata: types.Metadata{ChunkHashes: checksums(chunks)}, + }) + require.NoError(t, err) + + // While the restore is in progress, any other operations fail + _, err = manager.Create(4) + require.Error(t, err) + + _, err = manager.Prune(1) + require.Error(t, err) + + // Feeding an invalid chunk should error due to invalid checksum, but not abort restoration. + _, err = manager.RestoreChunk([]byte{9, 9, 9}) + require.Error(t, err) + require.True(t, errors.Is(err, types.ErrChunkHashMismatch)) + + // Feeding the chunks should work + for i, chunk := range chunks { + done, err := manager.RestoreChunk(chunk) + require.NoError(t, err) + if i == len(chunks)-1 { + assert.True(t, done) + } else { + assert.False(t, done) + } + } + + assert.Equal(t, chunks, target.chunks) + + // Starting a new restore should fail now, because the target already has contents. + err = manager.Restore(types.Snapshot{ + Height: 3, + Format: 1, + Hash: []byte{1, 2, 3}, + Chunks: 3, + Metadata: types.Metadata{ChunkHashes: checksums(chunks)}, + }) + require.Error(t, err) + + // But if we clear out the target we should be able to start a new restore. This time we'll + // fail it with a checksum error. That error should stop the operation, so that we can do + // a prune operation right after. + target.chunks = nil + err = manager.Restore(types.Snapshot{ + Height: 3, + Format: 1, + Hash: []byte{1, 2, 3}, + Chunks: 3, + Metadata: types.Metadata{ChunkHashes: checksums(chunks)}, + }) + require.NoError(t, err) +} diff --git a/snapshots/store.go b/snapshots/store.go new file mode 100644 index 000000000000..77ff58e22ff6 --- /dev/null +++ b/snapshots/store.go @@ -0,0 +1,341 @@ +package snapshots + +import ( + "crypto/sha256" + "encoding/binary" + "io" + "math" + "os" + "path/filepath" + "strconv" + "sync" + + "github.com/gogo/protobuf/proto" + db "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/snapshots/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + // keyPrefixSnapshot is the prefix for snapshot database keys + keyPrefixSnapshot byte = 0x01 +) + +// Store is a snapshot store, containing snapshot metadata and binary chunks. +type Store struct { + db db.DB + dir string + + mtx sync.Mutex + saving map[uint64]bool // heights currently being saved +} + +// NewStore creates a new snapshot store. +func NewStore(db db.DB, dir string) (*Store, error) { + if dir == "" { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "snapshot directory not given") + } + err := os.MkdirAll(dir, 0755) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to create snapshot directory %q", dir) + } + + return &Store{ + db: db, + dir: dir, + saving: make(map[uint64]bool), + }, nil +} + +// Delete deletes a snapshot. +func (s *Store) Delete(height uint64, format uint32) error { + s.mtx.Lock() + saving := s.saving[height] + s.mtx.Unlock() + if saving { + return sdkerrors.Wrapf(sdkerrors.ErrConflict, + "snapshot for height %v format %v is currently being saved", height, format) + } + err := s.db.DeleteSync(encodeKey(height, format)) + if err != nil { + return sdkerrors.Wrapf(err, "failed to delete snapshot for height %v format %v", + height, format) + } + err = os.RemoveAll(s.pathSnapshot(height, format)) + return sdkerrors.Wrapf(err, "failed to delete snapshot chunks for height %v format %v", + height, format) +} + +// Get fetches snapshot info from the database. +func (s *Store) Get(height uint64, format uint32) (*types.Snapshot, error) { + bytes, err := s.db.Get(encodeKey(height, format)) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to fetch snapshot metadata for height %v format %v", + height, format) + } + if bytes == nil { + return nil, nil + } + snapshot := &types.Snapshot{} + err = proto.Unmarshal(bytes, snapshot) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to decode snapshot metadata for height %v format %v", + height, format) + } + if snapshot.Metadata.ChunkHashes == nil { + snapshot.Metadata.ChunkHashes = [][]byte{} + } + return snapshot, nil +} + +// 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(uint64(math.MaxUint64), math.MaxUint32)) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to find latest snapshot") + } + defer iter.Close() + + var snapshot *types.Snapshot + if iter.Valid() { + snapshot = &types.Snapshot{} + err := proto.Unmarshal(iter.Value(), snapshot) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to decode latest snapshot") + } + } + err = iter.Error() + return snapshot, sdkerrors.Wrap(err, "failed to find latest snapshot") +} + +// List lists snapshots, in reverse order (newest first). +func (s *Store) List() ([]*types.Snapshot, error) { + 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") + } + defer iter.Close() + + snapshots := make([]*types.Snapshot, 0) + for ; iter.Valid(); iter.Next() { + snapshot := &types.Snapshot{} + err := proto.Unmarshal(iter.Value(), snapshot) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to decode snapshot info") + } + snapshots = append(snapshots, snapshot) + } + return snapshots, iter.Error() +} + +// Load loads a snapshot (both metadata and binary chunks). The chunks must be consumed and closed. +// Returns nil if the snapshot does not exist. +func (s *Store) Load(height uint64, format uint32) (*types.Snapshot, <-chan io.ReadCloser, error) { + snapshot, err := s.Get(height, format) + if snapshot == nil || err != nil { + return nil, nil, err + } + + ch := make(chan io.ReadCloser) + go func() { + defer close(ch) + for i := uint32(0); i < snapshot.Chunks; i++ { + pr, pw := io.Pipe() + ch <- pr + chunk, err := s.loadChunkFile(height, format, i) + if err != nil { + pw.CloseWithError(err) + return + } + defer chunk.Close() + _, err = io.Copy(pw, chunk) + if err != nil { + pw.CloseWithError(err) + return + } + chunk.Close() + pw.Close() + } + }() + + return snapshot, ch, nil +} + +// LoadChunk loads a chunk from disk, or returns nil if it does not exist. The caller must call +// Close() on it when done. +func (s *Store) LoadChunk(height uint64, format uint32, chunk uint32) (io.ReadCloser, error) { + path := s.pathChunk(height, format, chunk) + file, err := os.Open(path) + if os.IsNotExist(err) { + return nil, nil + } + return file, err +} + +// loadChunkFile loads a chunk from disk, and errors if it does not exist. +func (s *Store) loadChunkFile(height uint64, format uint32, chunk uint32) (io.ReadCloser, error) { + path := s.pathChunk(height, format, chunk) + return os.Open(path) +} + +// 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(uint64(math.MaxUint64), math.MaxUint32)) + if err != nil { + return 0, sdkerrors.Wrap(err, "failed to prune snapshots") + } + defer iter.Close() + + pruned := uint64(0) + prunedHeights := make(map[uint64]bool) + skip := make(map[uint64]bool) + for ; iter.Valid(); iter.Next() { + height, format, err := decodeKey(iter.Key()) + if err != nil { + return 0, sdkerrors.Wrap(err, "failed to prune snapshots") + } + if skip[height] || uint32(len(skip)) < retain { + skip[height] = true + continue + } + err = s.Delete(height, format) + if err != nil { + return 0, sdkerrors.Wrap(err, "failed to prune snapshots") + } + pruned++ + prunedHeights[height] = true + } + // Since Delete() deletes a specific format, while we want to prune a height, we clean up + // the height directory as well + for height, ok := range prunedHeights { + if ok { + err = os.Remove(s.pathHeight(height)) + if err != nil { + return 0, sdkerrors.Wrapf(err, "failed to remove snapshot directory for height %v", height) + } + } + } + return pruned, iter.Error() +} + +// Save saves a snapshot to disk, returning it. +func (s *Store) Save( + height uint64, format uint32, chunks <-chan io.ReadCloser, +) (*types.Snapshot, error) { + defer DrainChunks(chunks) + if height == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "snapshot height cannot be 0") + } + + s.mtx.Lock() + saving := s.saving[height] + s.saving[height] = true + s.mtx.Unlock() + if saving { + return nil, sdkerrors.Wrapf(sdkerrors.ErrConflict, + "a snapshot for height %v is already being saved", height) + } + defer func() { + s.mtx.Lock() + delete(s.saving, height) + s.mtx.Unlock() + }() + + exists, err := s.db.Has(encodeKey(height, format)) + if err != nil { + return nil, err + } + if exists { + return nil, sdkerrors.Wrapf(sdkerrors.ErrConflict, + "snapshot already exists for height %v format %v", height, format) + } + + snapshot := &types.Snapshot{ + Height: height, + Format: format, + } + index := uint32(0) + snapshotHasher := sha256.New() + chunkHasher := sha256.New() + for chunkBody := range chunks { + defer chunkBody.Close() // nolint: staticcheck + dir := s.pathSnapshot(height, format) + err = os.MkdirAll(dir, 0755) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to create snapshot directory %q", dir) + } + path := s.pathChunk(height, format, index) + file, err := os.Create(path) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to create snapshot chunk file %q", path) + } + defer file.Close() // nolint: staticcheck + + chunkHasher.Reset() + _, err = io.Copy(io.MultiWriter(file, chunkHasher, snapshotHasher), chunkBody) + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to generate snapshot chunk %v", index) + } + err = file.Close() + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to close snapshot chunk %v", index) + } + err = chunkBody.Close() + if err != nil { + return nil, sdkerrors.Wrapf(err, "failed to close snapshot chunk %v", index) + } + snapshot.Metadata.ChunkHashes = append(snapshot.Metadata.ChunkHashes, chunkHasher.Sum(nil)) + index++ + } + snapshot.Chunks = index + snapshot.Hash = snapshotHasher.Sum(nil) + return snapshot, s.saveSnapshot(snapshot) +} + +// saveSnapshot saves snapshot metadata to the database. +func (s *Store) saveSnapshot(snapshot *types.Snapshot) error { + value, err := proto.Marshal(snapshot) + if err != nil { + return sdkerrors.Wrap(err, "failed to encode snapshot metadata") + } + err = s.db.SetSync(encodeKey(snapshot.Height, snapshot.Format), value) + return sdkerrors.Wrap(err, "failed to store snapshot") +} + +// pathHeight generates the path to a height, containing multiple snapshot formats. +func (s *Store) pathHeight(height uint64) string { + return filepath.Join(s.dir, strconv.FormatUint(height, 10)) +} + +// pathSnapshot generates a snapshot path, as a specific format under a height. +func (s *Store) pathSnapshot(height uint64, format uint32) string { + return filepath.Join(s.pathHeight(height), strconv.FormatUint(uint64(format), 10)) +} + +// pathChunk generates a snapshot chunk path. +func (s *Store) pathChunk(height uint64, format uint32, chunk uint32) string { + return filepath.Join(s.pathSnapshot(height, format), strconv.FormatUint(uint64(chunk), 10)) +} + +// decodeKey decodes a snapshot key. +func decodeKey(k []byte) (uint64, uint32, error) { + if len(k) != 13 { + return 0, 0, sdkerrors.Wrapf(sdkerrors.ErrLogic, "invalid snapshot key with length %v", len(k)) + } + if k[0] != keyPrefixSnapshot { + return 0, 0, sdkerrors.Wrapf(sdkerrors.ErrLogic, "invalid snapshot key prefix %x", k[0]) + } + height := binary.BigEndian.Uint64(k[1:9]) + format := binary.BigEndian.Uint32(k[9:13]) + return height, format, nil +} + +// encodeKey encodes a snapshot key. +func encodeKey(height uint64, format uint32) []byte { + k := make([]byte, 13) + k[0] = keyPrefixSnapshot + binary.BigEndian.PutUint64(k[1:], height) + binary.BigEndian.PutUint32(k[9:], format) + return k +} diff --git a/snapshots/store_test.go b/snapshots/store_test.go new file mode 100644 index 000000000000..325fd3410bea --- /dev/null +++ b/snapshots/store_test.go @@ -0,0 +1,332 @@ +package snapshots_test + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + db "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/snapshots" + "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/testutil" +) + +func setupStore(t *testing.T) *snapshots.Store { + 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) + + _, err = store.Save(1, 1, makeChunks([][]byte{ + {1, 1, 0}, {1, 1, 1}, + })) + require.NoError(t, err) + _, err = store.Save(2, 1, makeChunks([][]byte{ + {2, 1, 0}, {2, 1, 1}, + })) + require.NoError(t, err) + _, err = store.Save(2, 2, makeChunks([][]byte{ + {2, 2, 0}, {2, 2, 1}, {2, 2, 2}, + })) + require.NoError(t, err) + _, err = store.Save(3, 2, makeChunks([][]byte{ + {3, 2, 0}, {3, 2, 1}, {3, 2, 2}, + })) + require.NoError(t, err) + + return store +} + +func TestNewStore(t *testing.T) { + tempdir := t.TempDir() + _, err := snapshots.NewStore(db.NewMemDB(), tempdir) + + require.NoError(t, err) +} + +func TestNewStore_ErrNoDir(t *testing.T) { + _, err := snapshots.NewStore(db.NewMemDB(), "") + require.Error(t, err) +} + +func TestNewStore_ErrDirFailure(t *testing.T) { + notADir := filepath.Join(testutil.TempFile(t).Name(), "subdir") + + _, err := snapshots.NewStore(db.NewMemDB(), notADir) + require.Error(t, err) +} + +func TestStore_Delete(t *testing.T) { + store := setupStore(t) + // Deleting a snapshot should remove it + err := store.Delete(2, 2) + require.NoError(t, err) + + snapshot, err := store.Get(2, 2) + require.NoError(t, err) + assert.Nil(t, snapshot) + + snapshots, err := store.List() + require.NoError(t, err) + assert.Len(t, snapshots, 3) + + // Deleting it again should not error + err = store.Delete(2, 2) + require.NoError(t, err) + + // Deleting a snapshot being saved should error + ch := make(chan io.ReadCloser) + go store.Save(9, 1, ch) + + time.Sleep(10 * time.Millisecond) + err = store.Delete(9, 1) + require.Error(t, err) + + // But after it's saved it should work + close(ch) + time.Sleep(10 * time.Millisecond) + err = store.Delete(9, 1) + require.NoError(t, err) +} + +func TestStore_Get(t *testing.T) { + store := setupStore(t) + + // Loading a missing snapshot should return nil + snapshot, err := store.Get(9, 9) + require.NoError(t, err) + assert.Nil(t, snapshot) + + // Loading a snapshot should returns its metadata + snapshot, err = store.Get(2, 1) + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{ + Height: 2, + Format: 1, + Chunks: 2, + Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}), + Metadata: types.Metadata{ + ChunkHashes: checksums([][]byte{ + {2, 1, 0}, {2, 1, 1}}), + }, + }, snapshot) +} + +func TestStore_GetLatest(t *testing.T) { + store := setupStore(t) + // Loading a missing snapshot should return nil + snapshot, err := store.GetLatest() + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{ + Height: 3, + Format: 2, + Chunks: 3, + Hash: hash([][]byte{ + {3, 2, 0}, + {3, 2, 1}, + {3, 2, 2}, + }), + Metadata: types.Metadata{ + ChunkHashes: checksums([][]byte{ + {3, 2, 0}, + {3, 2, 1}, + {3, 2, 2}, + }), + }, + }, snapshot) +} + +func TestStore_List(t *testing.T) { + store := setupStore(t) + snapshots, err := store.List() + require.NoError(t, err) + + require.Equal(t, []*types.Snapshot{ + {Height: 3, Format: 2, Chunks: 3, Hash: hash([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}})}, + }, + {Height: 2, Format: 2, Chunks: 3, Hash: hash([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}})}, + }, + {Height: 2, Format: 1, Chunks: 2, Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 1, 0}, {2, 1, 1}})}, + }, + {Height: 1, Format: 1, Chunks: 2, Hash: hash([][]byte{{1, 1, 0}, {1, 1, 1}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{1, 1, 0}, {1, 1, 1}})}, + }, + }, snapshots) +} + +func TestStore_Load(t *testing.T) { + store := setupStore(t) + // Loading a missing snapshot should return nil + snapshot, chunks, err := store.Load(9, 9) + require.NoError(t, err) + assert.Nil(t, snapshot) + assert.Nil(t, chunks) + + // Loading a snapshot should returns its metadata and chunks + snapshot, chunks, err = store.Load(2, 1) + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{ + Height: 2, + Format: 1, + Chunks: 2, + Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}), + Metadata: types.Metadata{ + ChunkHashes: checksums([][]byte{ + {2, 1, 0}, {2, 1, 1}}), + }, + }, snapshot) + + for i := uint32(0); i < snapshot.Chunks; i++ { + reader, ok := <-chunks + require.True(t, ok) + chunk, err := ioutil.ReadAll(reader) + require.NoError(t, err) + err = reader.Close() + require.NoError(t, err) + assert.Equal(t, []byte{2, 1, byte(i)}, chunk) + } + assert.Empty(t, chunks) +} + +func TestStore_LoadChunk(t *testing.T) { + store := setupStore(t) + // Loading a missing snapshot should return nil + chunk, err := store.LoadChunk(9, 9, 0) + require.NoError(t, err) + assert.Nil(t, chunk) + + // Loading a missing chunk index should return nil + chunk, err = store.LoadChunk(2, 1, 2) + require.NoError(t, err) + require.Nil(t, chunk) + + // Loading a chunk should returns a content reader + chunk, err = store.LoadChunk(2, 1, 0) + require.NoError(t, err) + require.NotNil(t, chunk) + body, err := ioutil.ReadAll(chunk) + require.NoError(t, err) + assert.Equal(t, []byte{2, 1, 0}, body) + err = chunk.Close() + require.NoError(t, err) +} + +func TestStore_Prune(t *testing.T) { + store := setupStore(t) + // Pruning too many snapshots should be fine + pruned, err := store.Prune(4) + require.NoError(t, err) + assert.EqualValues(t, 0, pruned) + + snapshots, err := store.List() + require.NoError(t, err) + assert.Len(t, snapshots, 4) + + // Pruning until the last two heights should leave three snapshots (for two heights) + pruned, err = store.Prune(2) + require.NoError(t, err) + assert.EqualValues(t, 1, pruned) + + snapshots, err = store.List() + require.NoError(t, err) + require.Equal(t, []*types.Snapshot{ + {Height: 3, Format: 2, Chunks: 3, Hash: hash([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{3, 2, 0}, {3, 2, 1}, {3, 2, 2}})}, + }, + {Height: 2, Format: 2, Chunks: 3, Hash: hash([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 2, 0}, {2, 2, 1}, {2, 2, 2}})}, + }, + {Height: 2, Format: 1, Chunks: 2, Hash: hash([][]byte{{2, 1, 0}, {2, 1, 1}}), + Metadata: types.Metadata{ChunkHashes: checksums([][]byte{{2, 1, 0}, {2, 1, 1}})}, + }, + }, snapshots) + + // Pruning all heights should also be fine + pruned, err = store.Prune(0) + require.NoError(t, err) + assert.EqualValues(t, 3, pruned) + + snapshots, err = store.List() + require.NoError(t, err) + assert.Empty(t, snapshots) +} + +func TestStore_Save(t *testing.T) { + store := setupStore(t) + // Saving a snapshot should work + snapshot, err := store.Save(4, 1, makeChunks([][]byte{{1}, {2}})) + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{ + Height: 4, + Format: 1, + Chunks: 2, + Hash: hash([][]byte{{1}, {2}}), + Metadata: types.Metadata{ + ChunkHashes: checksums([][]byte{{1}, {2}}), + }, + }, snapshot) + loaded, err := store.Get(snapshot.Height, snapshot.Format) + require.NoError(t, err) + assert.Equal(t, snapshot, loaded) + + // Saving an existing snapshot should error + _, err = store.Save(4, 1, makeChunks([][]byte{{1}, {2}})) + require.Error(t, err) + + // Saving at height 0 should error + _, err = store.Save(0, 1, makeChunks([][]byte{{1}, {2}})) + require.Error(t, err) + + // Saving at format 0 should be fine + _, err = store.Save(1, 0, makeChunks([][]byte{{1}, {2}})) + require.NoError(t, err) + + // Saving a snapshot with no chunks should be fine, as should loading it + _, err = store.Save(5, 1, makeChunks([][]byte{})) + require.NoError(t, err) + snapshot, chunks, err := store.Load(5, 1) + require.NoError(t, err) + assert.Equal(t, &types.Snapshot{Height: 5, Format: 1, Hash: hash([][]byte{}), Metadata: types.Metadata{ChunkHashes: [][]byte{}}}, snapshot) + assert.Empty(t, chunks) + + // Saving a snapshot should error if a chunk reader returns an error, and it should empty out + // the channel + someErr := errors.New("boom") + pr, pw := io.Pipe() + err = pw.CloseWithError(someErr) + require.NoError(t, err) + + ch := make(chan io.ReadCloser, 2) + ch <- pr + ch <- ioutil.NopCloser(bytes.NewBuffer([]byte{0xff})) + close(ch) + + _, err = store.Save(6, 1, ch) + require.Error(t, err) + require.True(t, errors.Is(err, someErr)) + assert.Empty(t, ch) + + // Saving a snapshot should error if a snapshot is already in progress for the same height, + // regardless of format. However, a different height should succeed. + ch = make(chan io.ReadCloser) + go store.Save(7, 1, ch) + time.Sleep(10 * time.Millisecond) + _, err = store.Save(7, 2, makeChunks(nil)) + require.Error(t, err) + _, err = store.Save(8, 1, makeChunks(nil)) + require.NoError(t, err) + close(ch) +} diff --git a/snapshots/types/convert.go b/snapshots/types/convert.go new file mode 100644 index 000000000000..d0db6e3dc170 --- /dev/null +++ b/snapshots/types/convert.go @@ -0,0 +1,39 @@ +package types + +import ( + proto "github.com/gogo/protobuf/proto" + abci "github.com/tendermint/tendermint/abci/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Converts an ABCI snapshot to a snapshot. Mainly to decode the SDK metadata. +func SnapshotFromABCI(in *abci.Snapshot) (Snapshot, error) { + snapshot := Snapshot{ + Height: in.Height, + Format: in.Format, + Chunks: in.Chunks, + Hash: in.Hash, + } + err := proto.Unmarshal(in.Metadata, &snapshot.Metadata) + if err != nil { + return Snapshot{}, sdkerrors.Wrap(err, "failed to unmarshal snapshot metadata") + } + return snapshot, nil +} + +// Converts a Snapshot to its ABCI representation. Mainly to encode the SDK metadata. +func (s Snapshot) ToABCI() (abci.Snapshot, error) { + out := abci.Snapshot{ + Height: s.Height, + Format: s.Format, + Chunks: s.Chunks, + Hash: s.Hash, + } + var err error + out.Metadata, err = proto.Marshal(&s.Metadata) + if err != nil { + return abci.Snapshot{}, sdkerrors.Wrap(err, "failed to marshal snapshot metadata") + } + return out, nil +} diff --git a/snapshots/types/errors.go b/snapshots/types/errors.go new file mode 100644 index 000000000000..c98dd055ed88 --- /dev/null +++ b/snapshots/types/errors.go @@ -0,0 +1,16 @@ +package types + +import ( + "errors" +) + +var ( + // ErrUnknownFormat is returned when an unknown format is used. + ErrUnknownFormat = errors.New("unknown snapshot format") + + // ErrChunkHashMismatch is returned when chunk hash verification failed. + ErrChunkHashMismatch = errors.New("chunk hash verification failed") + + // ErrInvalidMetadata is returned when the snapshot metadata is invalid. + ErrInvalidMetadata = errors.New("invalid snapshot metadata") +) diff --git a/snapshots/types/format.go b/snapshots/types/format.go new file mode 100644 index 000000000000..edfdb36d7bfc --- /dev/null +++ b/snapshots/types/format.go @@ -0,0 +1,6 @@ +package types + +// CurrentFormat is the currently used format for snapshots. Snapshots using the same format +// must be identical across all nodes for a given height, so this must be bumped when the binary +// snapshot output changes. +const CurrentFormat uint32 = 1 diff --git a/snapshots/types/snapshot.pb.go b/snapshots/types/snapshot.pb.go new file mode 100644 index 000000000000..f37192f9c569 --- /dev/null +++ b/snapshots/types/snapshot.pb.go @@ -0,0 +1,661 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/snapshots/v1beta1/snapshot.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 + +// Snapshot contains Tendermint state sync snapshot info. +type Snapshot struct { + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Format uint32 `protobuf:"varint,2,opt,name=format,proto3" json:"format,omitempty"` + Chunks uint32 `protobuf:"varint,3,opt,name=chunks,proto3" json:"chunks,omitempty"` + Hash []byte `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash,omitempty"` + Metadata Metadata `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_dd7a3c9b0a19e1ee, []int{0} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.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 *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_Snapshot proto.InternalMessageInfo + +func (m *Snapshot) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *Snapshot) GetFormat() uint32 { + if m != nil { + return m.Format + } + return 0 +} + +func (m *Snapshot) GetChunks() uint32 { + if m != nil { + return m.Chunks + } + return 0 +} + +func (m *Snapshot) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *Snapshot) GetMetadata() Metadata { + if m != nil { + return m.Metadata + } + return Metadata{} +} + +// Metadata contains SDK-specific snapshot metadata. +type Metadata struct { + ChunkHashes [][]byte `protobuf:"bytes,1,rep,name=chunk_hashes,json=chunkHashes,proto3" json:"chunk_hashes,omitempty"` +} + +func (m *Metadata) Reset() { *m = Metadata{} } +func (m *Metadata) String() string { return proto.CompactTextString(m) } +func (*Metadata) ProtoMessage() {} +func (*Metadata) Descriptor() ([]byte, []int) { + return fileDescriptor_dd7a3c9b0a19e1ee, []int{1} +} +func (m *Metadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metadata.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 *Metadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metadata.Merge(m, src) +} +func (m *Metadata) XXX_Size() int { + return m.Size() +} +func (m *Metadata) XXX_DiscardUnknown() { + xxx_messageInfo_Metadata.DiscardUnknown(m) +} + +var xxx_messageInfo_Metadata proto.InternalMessageInfo + +func (m *Metadata) GetChunkHashes() [][]byte { + if m != nil { + return m.ChunkHashes + } + return nil +} + +func init() { + proto.RegisterType((*Snapshot)(nil), "cosmos.base.snapshots.v1beta1.Snapshot") + proto.RegisterType((*Metadata)(nil), "cosmos.base.snapshots.v1beta1.Metadata") +} + +func init() { + proto.RegisterFile("cosmos/base/snapshots/v1beta1/snapshot.proto", fileDescriptor_dd7a3c9b0a19e1ee) +} + +var fileDescriptor_dd7a3c9b0a19e1ee = []byte{ + // 288 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0x31, 0x4e, 0xc3, 0x30, + 0x14, 0x86, 0x63, 0x1a, 0xaa, 0xc8, 0x0d, 0x8b, 0x85, 0x90, 0x85, 0x84, 0x09, 0x5d, 0xc8, 0xd0, + 0x3a, 0x2a, 0xdc, 0xa0, 0x03, 0x82, 0x81, 0x25, 0x6c, 0x2c, 0xc8, 0x49, 0x4d, 0x5c, 0x55, 0xa9, + 0xa3, 0x3e, 0x17, 0x89, 0x5b, 0x70, 0x15, 0x6e, 0xd1, 0xb1, 0x23, 0x13, 0x42, 0xc9, 0x45, 0x50, + 0x1c, 0x13, 0x31, 0x75, 0xca, 0xfb, 0xbf, 0x7c, 0x4f, 0xcf, 0xfa, 0xf1, 0x24, 0xd7, 0x50, 0x6a, + 0x48, 0x32, 0x01, 0x32, 0x81, 0xb5, 0xa8, 0x40, 0x69, 0x03, 0xc9, 0xdb, 0x2c, 0x93, 0x46, 0xcc, + 0x7a, 0xc2, 0xab, 0x8d, 0x36, 0x9a, 0x5c, 0x74, 0x36, 0x6f, 0x6d, 0xde, 0xdb, 0xdc, 0xd9, 0xe7, + 0xa7, 0x85, 0x2e, 0xb4, 0x35, 0x93, 0x76, 0xea, 0x96, 0xc6, 0x9f, 0x08, 0x07, 0x4f, 0xce, 0x25, + 0x67, 0x78, 0xa8, 0xe4, 0xb2, 0x50, 0x86, 0xa2, 0x08, 0xc5, 0x7e, 0xea, 0x52, 0xcb, 0x5f, 0xf5, + 0xa6, 0x14, 0x86, 0x1e, 0x45, 0x28, 0x3e, 0x49, 0x5d, 0x6a, 0x79, 0xae, 0xb6, 0xeb, 0x15, 0xd0, + 0x41, 0xc7, 0xbb, 0x44, 0x08, 0xf6, 0x95, 0x00, 0x45, 0xfd, 0x08, 0xc5, 0x61, 0x6a, 0x67, 0xf2, + 0x80, 0x83, 0x52, 0x1a, 0xb1, 0x10, 0x46, 0xd0, 0xe3, 0x08, 0xc5, 0xa3, 0x9b, 0x6b, 0x7e, 0xf0, + 0xc1, 0xfc, 0xd1, 0xe9, 0x73, 0x7f, 0xf7, 0x7d, 0xe9, 0xa5, 0xfd, 0xfa, 0x78, 0x8a, 0x83, 0xbf, + 0x7f, 0xe4, 0x0a, 0x87, 0xf6, 0xe8, 0x4b, 0x7b, 0x44, 0x02, 0x45, 0xd1, 0x20, 0x0e, 0xd3, 0x91, + 0x65, 0xf7, 0x16, 0xcd, 0xef, 0x76, 0x35, 0x43, 0xfb, 0x9a, 0xa1, 0x9f, 0x9a, 0xa1, 0x8f, 0x86, + 0x79, 0xfb, 0x86, 0x79, 0x5f, 0x0d, 0xf3, 0x9e, 0x27, 0xc5, 0xd2, 0xa8, 0x6d, 0xc6, 0x73, 0x5d, + 0x26, 0xae, 0xea, 0xee, 0x33, 0x85, 0xc5, 0xea, 0x5f, 0xe1, 0xe6, 0xbd, 0x92, 0x90, 0x0d, 0x6d, + 0x63, 0xb7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x89, 0x2e, 0x8f, 0x96, 0x01, 0x00, 0x00, +} + +func (m *Snapshot) 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 *Snapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) 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 = encodeVarintSnapshot(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x22 + } + if m.Chunks != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Chunks)) + i-- + dAtA[i] = 0x18 + } + if m.Format != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Format)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Metadata) 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 *Metadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChunkHashes) > 0 { + for iNdEx := len(m.ChunkHashes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ChunkHashes[iNdEx]) + copy(dAtA[i:], m.ChunkHashes[iNdEx]) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.ChunkHashes[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintSnapshot(dAtA []byte, offset int, v uint64) int { + offset -= sovSnapshot(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovSnapshot(uint64(m.Height)) + } + if m.Format != 0 { + n += 1 + sovSnapshot(uint64(m.Format)) + } + if m.Chunks != 0 { + n += 1 + sovSnapshot(uint64(m.Chunks)) + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + l = m.Metadata.Size() + n += 1 + l + sovSnapshot(uint64(l)) + return n +} + +func (m *Metadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ChunkHashes) > 0 { + for _, b := range m.ChunkHashes { + l = len(b) + n += 1 + l + sovSnapshot(uint64(l)) + } + } + return n +} + +func sovSnapshot(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSnapshot(x uint64) (n int) { + return sovSnapshot(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Snapshot) 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 ErrIntOverflowSnapshot + } + 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: Snapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Format", wireType) + } + m.Format = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Format |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Chunks", wireType) + } + m.Chunks = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Chunks |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Metadata) 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 ErrIntOverflowSnapshot + } + 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: Metadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChunkHashes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChunkHashes = append(m.ChunkHashes, make([]byte, postIndex-iNdEx)) + copy(m.ChunkHashes[len(m.ChunkHashes)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSnapshot(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, ErrIntOverflowSnapshot + } + 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, ErrIntOverflowSnapshot + } + 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, ErrIntOverflowSnapshot + } + 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, ErrInvalidLengthSnapshot + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSnapshot + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSnapshot + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSnapshot = fmt.Errorf("proto: unexpected end of group") +) diff --git a/snapshots/types/snapshotter.go b/snapshots/types/snapshotter.go new file mode 100644 index 000000000000..1ebd763b5d75 --- /dev/null +++ b/snapshots/types/snapshotter.go @@ -0,0 +1,16 @@ +package types + +import "io" + +// Snapshotter is something that can create and restore snapshots, consisting of streamed binary +// chunks - all of which must be read from the channel and closed. If an unsupported format is +// given, it must return ErrUnknownFormat (possibly wrapped with fmt.Errorf). +type Snapshotter interface { + // Snapshot creates a state snapshot, returning a channel of snapshot chunk readers. + Snapshot(height uint64, format uint32) (<-chan io.ReadCloser, error) + + // Restore restores a state snapshot, taking snapshot chunk readers as input. + // If the ready channel is non-nil, it returns a ready signal (by being closed) once the + // restorer is ready to accept chunks. + Restore(height uint64, format uint32, chunks <-chan io.ReadCloser, ready chan<- struct{}) error +} diff --git a/snapshots/util.go b/snapshots/util.go new file mode 100644 index 000000000000..674bd49d9278 --- /dev/null +++ b/snapshots/util.go @@ -0,0 +1,163 @@ +package snapshots + +import ( + "io" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ChunkWriter reads an input stream, splits it into fixed-size chunks, and writes them to a +// sequence of io.ReadClosers via a channel. +type ChunkWriter struct { + ch chan<- io.ReadCloser + pipe *io.PipeWriter + chunkSize uint64 + written uint64 + closed bool +} + +// NewChunkWriter creates a new ChunkWriter. If chunkSize is 0, no chunking will be done. +func NewChunkWriter(ch chan<- io.ReadCloser, chunkSize uint64) *ChunkWriter { + return &ChunkWriter{ + ch: ch, + chunkSize: chunkSize, + } +} + +// chunk creates a new chunk. +func (w *ChunkWriter) chunk() error { + if w.pipe != nil { + err := w.pipe.Close() + if err != nil { + return err + } + } + pr, pw := io.Pipe() + w.ch <- pr + w.pipe = pw + w.written = 0 + return nil +} + +// Close implements io.Closer. +func (w *ChunkWriter) Close() error { + if !w.closed { + w.closed = true + close(w.ch) + var err error + if w.pipe != nil { + err = w.pipe.Close() + } + return err + } + return nil +} + +// CloseWithError closes the writer and sends an error to the reader. +func (w *ChunkWriter) CloseWithError(err error) { + if !w.closed { + w.closed = true + close(w.ch) + if w.pipe != nil { + w.pipe.CloseWithError(err) + } + } +} + +// Write implements io.Writer. +func (w *ChunkWriter) Write(data []byte) (int, error) { + if w.closed { + return 0, sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot write to closed ChunkWriter") + } + nTotal := 0 + for len(data) > 0 { + if w.pipe == nil || (w.written >= w.chunkSize && w.chunkSize > 0) { + err := w.chunk() + if err != nil { + return nTotal, err + } + } + + var writeSize uint64 + if w.chunkSize == 0 { + writeSize = uint64(len(data)) + } else { + writeSize = w.chunkSize - w.written + } + if writeSize > uint64(len(data)) { + writeSize = uint64(len(data)) + } + + n, err := w.pipe.Write(data[:writeSize]) + w.written += uint64(n) + nTotal += n + if err != nil { + return nTotal, err + } + data = data[writeSize:] + } + return nTotal, nil +} + +// ChunkReader reads chunks from a channel of io.ReadClosers and outputs them as an io.Reader +type ChunkReader struct { + ch <-chan io.ReadCloser + reader io.ReadCloser +} + +// NewChunkReader creates a new ChunkReader. +func NewChunkReader(ch <-chan io.ReadCloser) *ChunkReader { + return &ChunkReader{ch: ch} +} + +// next fetches the next chunk from the channel, or returns io.EOF if there are no more chunks. +func (r *ChunkReader) next() error { + reader, ok := <-r.ch + if !ok { + return io.EOF + } + r.reader = reader + return nil +} + +// Close implements io.ReadCloser. +func (r *ChunkReader) Close() error { + var err error + if r.reader != nil { + err = r.reader.Close() + r.reader = nil + } + for reader := range r.ch { + if e := reader.Close(); e != nil && err == nil { + err = e + } + } + return err +} + +// Read implements io.Reader. +func (r *ChunkReader) Read(p []byte) (int, error) { + if r.reader == nil { + err := r.next() + if err != nil { + return 0, err + } + } + n, err := r.reader.Read(p) + if err == io.EOF { + err = r.reader.Close() + r.reader = nil + if err != nil { + return 0, err + } + return r.Read(p) + } + return n, err +} + +// DrainChunks drains and closes all remaining chunks from a chunk channel. +func DrainChunks(chunks <-chan io.ReadCloser) { + for chunk := range chunks { + _ = chunk.Close() + } +} diff --git a/snapshots/util_test.go b/snapshots/util_test.go new file mode 100644 index 000000000000..78c870b90a1b --- /dev/null +++ b/snapshots/util_test.go @@ -0,0 +1,165 @@ +package snapshots_test + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/snapshots" +) + +func TestChunkWriter(t *testing.T) { + ch := make(chan io.ReadCloser, 100) + go func() { + chunkWriter := snapshots.NewChunkWriter(ch, 2) + + n, err := chunkWriter.Write([]byte{1, 2, 3}) + require.NoError(t, err) + assert.Equal(t, 3, n) + + n, err = chunkWriter.Write([]byte{4, 5, 6}) + require.NoError(t, err) + assert.Equal(t, 3, n) + + n, err = chunkWriter.Write([]byte{7, 8, 9}) + require.NoError(t, err) + assert.Equal(t, 3, n) + + err = chunkWriter.Close() + require.NoError(t, err) + + // closed writer should error + _, err = chunkWriter.Write([]byte{10}) + require.Error(t, err) + + // closing again should be fine + err = chunkWriter.Close() + require.NoError(t, err) + }() + + assert.Equal(t, [][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9}}, readChunks(ch)) + + // 0-sized chunks should return the whole body as one chunk + ch = make(chan io.ReadCloser, 100) + go func() { + chunkWriter := snapshots.NewChunkWriter(ch, 0) + _, err := chunkWriter.Write([]byte{1, 2, 3}) + require.NoError(t, err) + _, err = chunkWriter.Write([]byte{4, 5, 6}) + require.NoError(t, err) + err = chunkWriter.Close() + require.NoError(t, err) + }() + assert.Equal(t, [][]byte{{1, 2, 3, 4, 5, 6}}, readChunks(ch)) + + // closing with error should return the error + theErr := errors.New("boom") + ch = make(chan io.ReadCloser, 100) + go func() { + chunkWriter := snapshots.NewChunkWriter(ch, 2) + _, err := chunkWriter.Write([]byte{1, 2, 3}) + require.NoError(t, err) + chunkWriter.CloseWithError(theErr) + }() + chunk, err := ioutil.ReadAll(<-ch) + require.NoError(t, err) + assert.Equal(t, []byte{1, 2}, chunk) + _, err = ioutil.ReadAll(<-ch) + require.Error(t, err) + assert.Equal(t, theErr, err) + assert.Empty(t, ch) + + // closing immediately should return no chunks + ch = make(chan io.ReadCloser, 100) + chunkWriter := snapshots.NewChunkWriter(ch, 2) + err = chunkWriter.Close() + require.NoError(t, err) + assert.Empty(t, ch) +} + +func TestChunkReader(t *testing.T) { + ch := makeChunks([][]byte{ + {1, 2, 3}, + {4}, + {}, + {5, 6}, + }) + chunkReader := snapshots.NewChunkReader(ch) + + buf := []byte{0, 0, 0, 0} + n, err := chunkReader.Read(buf) + require.NoError(t, err) + assert.Equal(t, 3, n) + assert.Equal(t, []byte{1, 2, 3, 0}, buf) + + buf = []byte{0, 0, 0, 0} + n, err = chunkReader.Read(buf) + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, []byte{4, 0, 0, 0}, buf) + + buf = []byte{0, 0, 0, 0} + n, err = chunkReader.Read(buf) + require.NoError(t, err) + assert.Equal(t, 2, n) + assert.Equal(t, []byte{5, 6, 0, 0}, buf) + + buf = []byte{0, 0, 0, 0} + _, err = chunkReader.Read(buf) + require.Error(t, err) + assert.Equal(t, io.EOF, err) + + err = chunkReader.Close() + require.NoError(t, err) + + err = chunkReader.Close() // closing twice should be fine + require.NoError(t, err) + + // Empty channel should be fine + ch = makeChunks(nil) + chunkReader = snapshots.NewChunkReader(ch) + buf = make([]byte, 4) + _, err = chunkReader.Read(buf) + require.Error(t, err) + assert.Equal(t, io.EOF, err) + + // Using a pipe that closes with an error should return the error + theErr := errors.New("boom") + pr, pw := io.Pipe() + pch := make(chan io.ReadCloser, 1) + pch <- pr + pw.CloseWithError(theErr) + + chunkReader = snapshots.NewChunkReader(pch) + buf = make([]byte, 4) + _, err = chunkReader.Read(buf) + require.Error(t, err) + assert.Equal(t, theErr, err) + + // Closing the reader should close the writer + pr, pw = io.Pipe() + pch = make(chan io.ReadCloser, 2) + pch <- ioutil.NopCloser(bytes.NewBuffer([]byte{1, 2, 3})) + pch <- pr + close(pch) + + go func() { + chunkReader := snapshots.NewChunkReader(pch) + buf := []byte{0, 0, 0, 0} + _, err := chunkReader.Read(buf) + require.NoError(t, err) + assert.Equal(t, []byte{1, 2, 3, 0}, buf) + + err = chunkReader.Close() + require.NoError(t, err) + }() + + _, err = pw.Write([]byte{9, 9, 9}) + require.Error(t, err) + assert.Equal(t, err, io.ErrClosedPipe) +} diff --git a/std/codec.go b/std/codec.go new file mode 100644 index 000000000000..7310d75a25fd --- /dev/null +++ b/std/codec.go @@ -0,0 +1,22 @@ +package std + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" +) + +// RegisterLegacyAminoCodec registers types with the Amino codec. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) +} + +// RegisterInterfaces registers Interfaces from sdk/types, vesting, crypto, tx. +func RegisterInterfaces(interfaceRegistry types.InterfaceRegistry) { + sdk.RegisterInterfaces(interfaceRegistry) + txtypes.RegisterInterfaces(interfaceRegistry) + cryptocodec.RegisterInterfaces(interfaceRegistry) +} diff --git a/std/doc.go b/std/doc.go new file mode 100644 index 000000000000..eef18646c9d0 --- /dev/null +++ b/std/doc.go @@ -0,0 +1,12 @@ +/* +Package std defines all the common and standard inter-module Cosmos SDK types and definitions +modules and applications can depend on. These types and definitions serve as +convenient starting point and also to act as an example or template of how said +types and definitions can be overridden/redefined. + +Typically these will be used in application-specific units of business logic. These +types and definitions are different from the types you may find in a "core" or "types" +package as those should be considered the defacto types to be used and are not +application-specific. +*/ +package std diff --git a/store/README.md b/store/README.md index 54994374bcd6..3607cbb71bb8 100644 --- a/store/README.md +++ b/store/README.md @@ -34,7 +34,7 @@ type Store struct { } ``` -`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. +`cachemulti.Store` branches all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. ## DBAdapter diff --git a/store/cache/benchmark_test.go b/store/cache/benchmark_test.go new file mode 100644 index 000000000000..cf8206272c41 --- /dev/null +++ b/store/cache/benchmark_test.go @@ -0,0 +1,48 @@ +package cache + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func freshMgr() *CommitKVStoreCacheManager { + return &CommitKVStoreCacheManager{ + caches: map[string]types.CommitKVStore{ + "a1": nil, + "alalalalalal": nil, + }, + } +} + +func populate(mgr *CommitKVStoreCacheManager) { + mgr.caches["this one"] = (types.CommitKVStore)(nil) + mgr.caches["those ones are the ones"] = (types.CommitKVStore)(nil) + mgr.caches["very huge key right here and there are we going to ones are the ones"] = (types.CommitKVStore)(nil) +} + +func BenchmarkReset(b *testing.B) { + mgr := freshMgr() + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + mgr.Reset() + if len(mgr.caches) != 0 { + b.Fatal("Reset failed") + } + populate(mgr) + if len(mgr.caches) == 0 { + b.Fatal("populate failed") + } + mgr.Reset() + if len(mgr.caches) != 0 { + b.Fatal("Reset failed") + } + } + + if mgr == nil { + b.Fatal("Impossible condition") + } +} diff --git a/store/cache/cache.go b/store/cache/cache.go index 9eea90ad942e..62bed91d978e 100644 --- a/store/cache/cache.go +++ b/store/cache/cache.go @@ -81,10 +81,15 @@ func (cmgr *CommitKVStoreCacheManager) Unwrap(key types.StoreKey) types.CommitKV // Reset resets in the internal caches. func (cmgr *CommitKVStoreCacheManager) Reset() { - cmgr.caches = make(map[string]types.CommitKVStore) + // Clear the map. + // Please note that we are purposefully using the map clearing idiom. + // See https://github.com/cosmos/cosmos-sdk/issues/6681. + for key := range cmgr.caches { + delete(cmgr.caches, key) + } } -// CacheWrap returns the inter-block cache as a cache-wrapped CommitKVStore. +// CacheWrap implements the CacheWrapper interface func (ckv *CommitKVStoreCache) CacheWrap() types.CacheWrap { return cachekv.NewStore(ckv) } diff --git a/store/cache/cache_test.go b/store/cache/cache_test.go index 3fc5f9157dbc..45c5d147a4ca 100644 --- a/store/cache/cache_test.go +++ b/store/cache/cache_test.go @@ -4,13 +4,13 @@ import ( "fmt" "testing" + "github.com/cosmos/iavl" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/store/cache" iavlstore "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/types" - - "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" - dbm "github.com/tendermint/tm-db" ) func TestGetOrSetStoreCache(t *testing.T) { @@ -20,7 +20,7 @@ func TestGetOrSetStoreCache(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) store2 := mngr.GetStoreCache(sKey, store) require.NotNil(t, store2) @@ -34,7 +34,7 @@ func TestUnwrap(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) _ = mngr.GetStoreCache(sKey, store) require.Equal(t, store, mngr.Unwrap(sKey)) @@ -48,7 +48,7 @@ func TestStoreCache(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) kvStore := mngr.GetStoreCache(sKey, store) for i := uint(0); i < cache.DefaultCommitKVStoreCacheSize*2; i++ { diff --git a/store/cachekv/memiterator.go b/store/cachekv/memiterator.go index d0930b5df83c..b197ac141660 100644 --- a/store/cachekv/memiterator.go +++ b/store/cachekv/memiterator.go @@ -1,11 +1,11 @@ package cachekv import ( - "container/list" "errors" - tmkv "github.com/tendermint/tendermint/libs/kv" dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/types/kv" ) // Iterates over iterKVCache items. @@ -13,21 +13,25 @@ import ( // Implements Iterator. type memIterator struct { start, end []byte - items []*tmkv.Pair + items []*kv.Pair ascending bool } -func newMemIterator(start, end []byte, items *list.List, ascending bool) *memIterator { - itemsInDomain := make([]*tmkv.Pair, 0) +func newMemIterator(start, end []byte, items *kv.List, ascending bool) *memIterator { + itemsInDomain := make([]*kv.Pair, 0, items.Len()) + var entered bool + for e := items.Front(); e != nil; e = e.Next() { - item := e.Value.(*tmkv.Pair) + item := e.Value if !dbm.IsKeyInDomain(item.Key, start, end) { if entered { break } + continue } + itemsInDomain = append(itemsInDomain, item) entered = true } @@ -56,6 +60,7 @@ func (mi *memIterator) assertValid() { func (mi *memIterator) Next() { mi.assertValid() + if mi.ascending { mi.items = mi.items[1:] } else { @@ -65,24 +70,30 @@ func (mi *memIterator) Next() { func (mi *memIterator) Key() []byte { mi.assertValid() + if mi.ascending { return mi.items[0].Key } + return mi.items[len(mi.items)-1].Key } func (mi *memIterator) Value() []byte { mi.assertValid() + if mi.ascending { return mi.items[0].Value } + return mi.items[len(mi.items)-1].Value } -func (mi *memIterator) Close() { +func (mi *memIterator) Close() error { mi.start = nil mi.end = nil mi.items = nil + + return nil } // Error returns an error if the memIterator is invalid defined by the Valid diff --git a/store/cachekv/mergeiterator.go b/store/cachekv/mergeiterator.go index 2a85a5461fb4..25dfac803329 100644 --- a/store/cachekv/mergeiterator.go +++ b/store/cachekv/mergeiterator.go @@ -28,6 +28,7 @@ func newCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheM cache: cache, ascending: ascending, } + return iter } @@ -36,16 +37,19 @@ func newCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheM func (iter *cacheMergeIterator) Domain() (start, end []byte) { startP, endP := iter.parent.Domain() startC, endC := iter.cache.Domain() + if iter.compare(startP, startC) < 0 { start = startP } else { start = startC } + if iter.compare(endP, endC) < 0 { end = endC } else { end = endP } + return start, end } @@ -101,6 +105,7 @@ func (iter *cacheMergeIterator) Key() []byte { // Both are valid. Compare keys. keyP, keyC := iter.parent.Key(), iter.cache.Key() + cmp := iter.compare(keyP, keyC) switch cmp { case -1: // parent < cache @@ -131,6 +136,7 @@ func (iter *cacheMergeIterator) Value() []byte { // Both are valid. Compare keys. keyP, keyC := iter.parent.Key(), iter.cache.Key() + cmp := iter.compare(keyP, keyC) switch cmp { case -1: // parent < cache @@ -145,9 +151,12 @@ func (iter *cacheMergeIterator) Value() []byte { } // Close implements Iterator -func (iter *cacheMergeIterator) Close() { - iter.parent.Close() - iter.cache.Close() +func (iter *cacheMergeIterator) Close() error { + if err := iter.parent.Close(); err != nil { + return err + } + + return iter.cache.Close() } // Error returns an error if the cacheMergeIterator is invalid defined by the @@ -173,6 +182,7 @@ func (iter *cacheMergeIterator) compare(a, b []byte) int { if iter.ascending { return bytes.Compare(a, b) } + return bytes.Compare(a, b) * -1 } @@ -185,7 +195,6 @@ func (iter *cacheMergeIterator) skipCacheDeletes(until []byte) { for iter.cache.Valid() && iter.cache.Value() == nil && (until == nil || iter.compare(iter.cache.Key(), until) < 0) { - iter.cache.Next() } } @@ -210,26 +219,24 @@ func (iter *cacheMergeIterator) skipUntilExistsOrInvalid() bool { // Compare parent and cache. keyP := iter.parent.Key() keyC := iter.cache.Key() - switch iter.compare(keyP, keyC) { + switch iter.compare(keyP, keyC) { case -1: // parent < cache. return true case 0: // parent == cache. - // Skip over if cache item is a delete. valueC := iter.cache.Value() if valueC == nil { iter.parent.Next() iter.cache.Next() + continue } // Cache is not a delete. return true // cache exists. - case 1: // cache < parent - // Skip over if cache item is a delete. valueC := iter.cache.Value() if valueC == nil { diff --git a/store/cachekv/store.go b/store/cachekv/store.go index fa9b42d60002..cd7087c5dc7b 100644 --- a/store/cachekv/store.go +++ b/store/cachekv/store.go @@ -2,16 +2,19 @@ package cachekv import ( "bytes" - "container/list" "io" + "reflect" "sort" "sync" + "time" + "unsafe" - tmkv "github.com/tendermint/tendermint/libs/kv" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/telemetry" + "github.com/cosmos/cosmos-sdk/types/kv" ) // If value is nil but deleted is false, it means the parent doesn't have the @@ -27,30 +30,32 @@ type Store struct { mtx sync.Mutex cache map[string]*cValue unsortedCache map[string]struct{} - sortedCache *list.List // always ascending sorted + sortedCache *kv.List // always ascending sorted parent types.KVStore } var _ types.CacheKVStore = (*Store)(nil) +// NewStore creates a new Store object func NewStore(parent types.KVStore) *Store { return &Store{ cache: make(map[string]*cValue), unsortedCache: make(map[string]struct{}), - sortedCache: list.New(), + sortedCache: kv.NewList(), parent: parent, } } -// Implements Store. +// GetStoreType implements Store. func (store *Store) GetStoreType() types.StoreType { return store.parent.GetStoreType() } -// Implements types.KVStore. +// Get implements types.KVStore. func (store *Store) Get(key []byte) (value []byte) { store.mtx.Lock() defer store.mtx.Unlock() + defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "get") types.AssertValidKey(key) @@ -65,10 +70,11 @@ func (store *Store) Get(key []byte) (value []byte) { return value } -// Implements types.KVStore. +// Set implements types.KVStore. func (store *Store) Set(key []byte, value []byte) { store.mtx.Lock() defer store.mtx.Unlock() + defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "set") types.AssertValidKey(key) types.AssertValidValue(value) @@ -76,19 +82,19 @@ func (store *Store) Set(key []byte, value []byte) { store.setCacheValue(key, value, false, true) } -// Implements types.KVStore. +// Has implements types.KVStore. func (store *Store) Has(key []byte) bool { value := store.Get(key) return value != nil } -// Implements types.KVStore. +// Delete implements types.KVStore. func (store *Store) Delete(key []byte) { store.mtx.Lock() defer store.mtx.Unlock() + defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "delete") types.AssertValidKey(key) - store.setCacheValue(key, nil, true, true) } @@ -96,10 +102,12 @@ func (store *Store) Delete(key []byte) { func (store *Store) Write() { store.mtx.Lock() defer store.mtx.Unlock() + defer telemetry.MeasureSince(time.Now(), "store", "cachekv", "write") // We need a copy of all of the keys. // Not the best, but probably not a bottleneck depending. keys := make([]string, 0, len(store.cache)) + for key, dbValue := range store.cache { if dbValue.dirty { keys = append(keys, key) @@ -112,6 +120,7 @@ func (store *Store) Write() { // at least happen atomically. for _, key := range keys { cacheValue := store.cache[key] + switch { case cacheValue.deleted: store.parent.Delete([]byte(key)) @@ -125,13 +134,10 @@ func (store *Store) Write() { // Clear the cache store.cache = make(map[string]*cValue) store.unsortedCache = make(map[string]struct{}) - store.sortedCache = list.New() + store.sortedCache = kv.NewList() } -//---------------------------------------- -// To cache-wrap this Store further. - -// Implements CacheWrapper. +// CacheWrap implements CacheWrapper. func (store *Store) CacheWrap() types.CacheWrap { return NewStore(store) } @@ -144,12 +150,12 @@ func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types //---------------------------------------- // Iteration -// Implements types.KVStore. +// Iterator implements types.KVStore. func (store *Store) Iterator(start, end []byte) types.Iterator { return store.iterator(start, end, true) } -// Implements types.KVStore. +// ReverseIterator implements types.KVStore. func (store *Store) ReverseIterator(start, end []byte) types.Iterator { return store.iterator(start, end, false) } @@ -172,16 +178,48 @@ func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator { return newCacheMergeIterator(parent, cache, ascending) } +// strToByte is meant to make a zero allocation conversion +// from string -> []byte to speed up operations, it is not meant +// to be used generally, but for a specific pattern to check for available +// keys within a domain. +func strToByte(s string) []byte { + var b []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + hdr.Cap = len(s) + hdr.Len = len(s) + hdr.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + return b +} + +// byteSliceToStr is meant to make a zero allocation conversion +// from []byte -> string to speed up operations, it is not meant +// to be used generally, but for a specific pattern to delete keys +// from a map. +func byteSliceToStr(b []byte) string { + hdr := (*reflect.StringHeader)(unsafe.Pointer(&b)) + return *(*string)(unsafe.Pointer(hdr)) +} + // Constructs a slice of dirty items, to use w/ memIterator. func (store *Store) dirtyItems(start, end []byte) { - unsorted := make([]*tmkv.Pair, 0) + unsorted := make([]*kv.Pair, 0) + n := len(store.unsortedCache) for key := range store.unsortedCache { - cacheValue := store.cache[key] - if dbm.IsKeyInDomain([]byte(key), start, end) { - unsorted = append(unsorted, &tmkv.Pair{Key: []byte(key), Value: cacheValue.value}) + if dbm.IsKeyInDomain(strToByte(key), start, end) { + cacheValue := store.cache[key] + unsorted = append(unsorted, &kv.Pair{Key: []byte(key), Value: cacheValue.value}) + } + } + + if len(unsorted) == n { // This pattern allows the Go compiler to emit the map clearing idiom for the entire map. + for key := range store.unsortedCache { delete(store.unsortedCache, key) } + } else { // Otherwise, normally delete the unsorted keys from the map. + for _, kv := range unsorted { + delete(store.unsortedCache, byteSliceToStr(kv.Key)) + } } sort.Slice(unsorted, func(i, j int) bool { @@ -190,11 +228,13 @@ func (store *Store) dirtyItems(start, end []byte) { for e := store.sortedCache.Front(); e != nil && len(unsorted) != 0; { uitem := unsorted[0] - sitem := e.Value.(*tmkv.Pair) + sitem := e.Value comp := bytes.Compare(uitem.Key, sitem.Key) + switch comp { case -1: unsorted = unsorted[1:] + store.sortedCache.InsertBefore(uitem, e) case 1: e = e.Next() @@ -208,7 +248,6 @@ func (store *Store) dirtyItems(start, end []byte) { for _, kvp := range unsorted { store.sortedCache.PushBack(kvp) } - } //---------------------------------------- diff --git a/store/cachekv/store_test.go b/store/cachekv/store_test.go index f53940d9c47a..e3b33341b8c4 100644 --- a/store/cachekv/store_test.go +++ b/store/cachekv/store_test.go @@ -68,6 +68,8 @@ func TestCacheKVStoreNoNilSet(t *testing.T) { mem := dbadapter.Store{DB: dbm.NewMemDB()} st := cachekv.NewStore(mem) require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") + require.Panics(t, func() { st.Set(nil, []byte("value")) }, "setting a nil key should panic") + require.Panics(t, func() { st.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") } func TestCacheKVStoreNested(t *testing.T) { @@ -231,13 +233,13 @@ func TestCacheKVMergeIteratorDeletes(t *testing.T) { // set some items and write them nItems := 10 for i := 0; i < nItems; i++ { - doOp(st, truth, opSet, i) + doOp(t, st, truth, opSet, i) } st.Write() // delete every other item, starting from 0 for i := 0; i < nItems; i += 2 { - doOp(st, truth, opDel, i) + doOp(t, st, truth, opDel, i) assertIterateDomainCompare(t, st, truth) } @@ -247,13 +249,13 @@ func TestCacheKVMergeIteratorDeletes(t *testing.T) { // set some items and write them for i := 0; i < nItems; i++ { - doOp(st, truth, opSet, i) + doOp(t, st, truth, opSet, i) } st.Write() // delete every other item, starting from 1 for i := 1; i < nItems; i += 2 { - doOp(st, truth, opDel, i) + doOp(t, st, truth, opDel, i) assertIterateDomainCompare(t, st, truth) } } @@ -265,27 +267,27 @@ func TestCacheKVMergeIteratorChunks(t *testing.T) { truth := dbm.NewMemDB() // sets to the parent - setRange(st, truth, 0, 20) - setRange(st, truth, 40, 60) + setRange(t, st, truth, 0, 20) + setRange(t, st, truth, 40, 60) st.Write() // sets to the cache - setRange(st, truth, 20, 40) - setRange(st, truth, 60, 80) + setRange(t, st, truth, 20, 40) + setRange(t, st, truth, 60, 80) assertIterateDomainCheck(t, st, truth, []keyRange{{0, 80}}) // remove some parents and some cache - deleteRange(st, truth, 15, 25) + deleteRange(t, st, truth, 15, 25) assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 80}}) // remove some parents and some cache - deleteRange(st, truth, 35, 45) + deleteRange(t, st, truth, 35, 45) assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {45, 80}}) // write, add more to the cache, and delete some cache st.Write() - setRange(st, truth, 38, 42) - deleteRange(st, truth, 40, 43) + setRange(t, st, truth, 38, 42) + deleteRange(t, st, truth, 40, 43) assertIterateDomainCheck(t, st, truth, []keyRange{{0, 15}, {25, 35}, {38, 40}, {45, 80}}) } @@ -295,11 +297,11 @@ func TestCacheKVMergeIteratorRandom(t *testing.T) { start, end := 25, 975 max := 1000 - setRange(st, truth, start, end) + setRange(t, st, truth, start, end) // do an op, test the iterator for i := 0; i < 2000; i++ { - doRandomOp(st, truth, max) + doRandomOp(t, st, truth, max) assertIterateDomainCompare(t, st, truth) } } @@ -322,48 +324,52 @@ func randInt(n int) int { } // useful for replaying a error case if we find one -func doOp(st types.CacheKVStore, truth dbm.DB, op int, args ...int) { +func doOp(t *testing.T, st types.CacheKVStore, truth dbm.DB, op int, args ...int) { switch op { case opSet: k := args[0] st.Set(keyFmt(k), valFmt(k)) - truth.Set(keyFmt(k), valFmt(k)) + err := truth.Set(keyFmt(k), valFmt(k)) + require.NoError(t, err) case opSetRange: start := args[0] end := args[1] - setRange(st, truth, start, end) + setRange(t, st, truth, start, end) case opDel: k := args[0] st.Delete(keyFmt(k)) - truth.Delete(keyFmt(k)) + err := truth.Delete(keyFmt(k)) + require.NoError(t, err) case opDelRange: start := args[0] end := args[1] - deleteRange(st, truth, start, end) + deleteRange(t, st, truth, start, end) case opWrite: st.Write() } } -func doRandomOp(st types.CacheKVStore, truth dbm.DB, maxKey int) { +func doRandomOp(t *testing.T, st types.CacheKVStore, truth dbm.DB, maxKey int) { r := randInt(totalOps) switch r { case opSet: k := randInt(maxKey) st.Set(keyFmt(k), valFmt(k)) - truth.Set(keyFmt(k), valFmt(k)) + err := truth.Set(keyFmt(k), valFmt(k)) + require.NoError(t, err) case opSetRange: start := randInt(maxKey - 2) end := randInt(maxKey-start) + start - setRange(st, truth, start, end) + setRange(t, st, truth, start, end) case opDel: k := randInt(maxKey) st.Delete(keyFmt(k)) - truth.Delete(keyFmt(k)) + err := truth.Delete(keyFmt(k)) + require.NoError(t, err) case opDelRange: start := randInt(maxKey - 2) end := randInt(maxKey-start) + start - deleteRange(st, truth, start, end) + deleteRange(t, st, truth, start, end) case opWrite: st.Write() } @@ -439,17 +445,19 @@ func checkIterators(t *testing.T, itr, itr2 types.Iterator) { //-------------------------------------------------------- -func setRange(st types.KVStore, mem dbm.DB, start, end int) { +func setRange(t *testing.T, st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Set(keyFmt(i), valFmt(i)) - mem.Set(keyFmt(i), valFmt(i)) + err := mem.Set(keyFmt(i), valFmt(i)) + require.NoError(t, err) } } -func deleteRange(st types.KVStore, mem dbm.DB, start, end int) { +func deleteRange(t *testing.T, st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Delete(keyFmt(i)) - mem.Delete(keyFmt(i)) + err := mem.Delete(keyFmt(i)) + require.NoError(t, err) } } diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go index 463781656b44..43bb40ae76cd 100644 --- a/store/cachemulti/store.go +++ b/store/cachemulti/store.go @@ -14,7 +14,7 @@ import ( //---------------------------------------- // Store -// Store holds many cache-wrapped stores. +// Store holds many branched stores. // Implements MultiStore. // NOTE: a Store (and MultiStores in general) should never expose the // keys for the substores. @@ -31,7 +31,7 @@ var _ types.CacheMultiStore = Store{} // NewFromKVStore creates a new Store object from a mapping of store keys to // CacheWrapper objects and a KVStore as the database. Each CacheWrapper store -// is cache-wrapped. +// is a branched store. func NewFromKVStore( store types.KVStore, stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, traceWriter io.Writer, traceContext types.TraceContext, @@ -56,7 +56,7 @@ func NewFromKVStore( } // NewStore creates a new Store object from a mapping of store keys to -// CacheWrapper objects. Each CacheWrapper store is cache-wrapped. +// CacheWrapper objects. Each CacheWrapper store is a branched store. func NewStore( db dbm.DB, stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, traceWriter io.Writer, traceContext types.TraceContext, @@ -136,18 +136,22 @@ func (cms Store) CacheMultiStore() types.CacheMultiStore { // TODO: The store implementation can possibly be modified to support this as it // seems safe to load previous versions (heights). func (cms Store) CacheMultiStoreWithVersion(_ int64) (types.CacheMultiStore, error) { - panic("cannot cache-wrap cached multi-store with a version") + panic("cannot branch cached multi-store with a version") } // GetStore returns an underlying Store by key. func (cms Store) GetStore(key types.StoreKey) types.Store { - return cms.stores[key].(types.Store) + s := cms.stores[key] + if key == nil || s == nil { + panic(fmt.Sprintf("kv store with key %v has not been registered in stores", key)) + } + return s.(types.Store) } // GetKVStore returns an underlying KVStore by key. func (cms Store) GetKVStore(key types.StoreKey) types.KVStore { store := cms.stores[key] - if key == nil { + if key == nil || store == nil { panic(fmt.Sprintf("kv store with key %v has not been registered in stores", key)) } return store.(types.KVStore) diff --git a/store/cachemulti/store_test.go b/store/cachemulti/store_test.go new file mode 100644 index 000000000000..8747df9ef966 --- /dev/null +++ b/store/cachemulti/store_test.go @@ -0,0 +1,23 @@ +package cachemulti + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/store/types" + "github.com/stretchr/testify/require" +) + +func TestStoreGetKVStore(t *testing.T) { + require := require.New(t) + + s := Store{stores: map[types.StoreKey]types.CacheWrap{}} + key := types.NewKVStoreKey("abc") + errMsg := fmt.Sprintf("kv store with key %v has not been registered in stores", key) + + require.PanicsWithValue(errMsg, + func() { s.GetStore(key) }) + + require.PanicsWithValue(errMsg, + func() { s.GetKVStore(key) }) +} diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go index 0c281c272a59..e9ea4f847d14 100644 --- a/store/dbadapter/store.go +++ b/store/dbadapter/store.go @@ -37,6 +37,7 @@ func (dsa Store) Has(key []byte) bool { // Set wraps the underlying DB's Set method panicing on error. func (dsa Store) Set(key, value []byte) { + types.AssertValidKey(key) if err := dsa.DB.Set(key, value); err != nil { panic(err) } @@ -74,7 +75,7 @@ func (Store) GetStoreType() types.StoreType { return types.StoreTypeDB } -// CacheWrap cache wraps the underlying store. +// CacheWrap branches the underlying store. func (dsa Store) CacheWrap() types.CacheWrap { return cachekv.NewStore(dsa) } diff --git a/store/dbadapter/store_test.go b/store/dbadapter/store_test.go new file mode 100644 index 000000000000..c09f09331630 --- /dev/null +++ b/store/dbadapter/store_test.go @@ -0,0 +1,73 @@ +package dbadapter_test + +import ( + "bytes" + "errors" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/tests/mocks" +) + +var errFoo = errors.New("dummy") + +func TestAccessors(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockDB := mocks.NewMockDB(mockCtrl) + store := dbadapter.Store{mockDB} + key := []byte("test") + value := []byte("testvalue") + + require.Panics(t, func() { store.Set(nil, []byte("value")) }, "setting a nil key should panic") + require.Panics(t, func() { store.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") + + require.Equal(t, types.StoreTypeDB, store.GetStoreType()) + store.GetStoreType() + + retFoo := []byte("xxx") + mockDB.EXPECT().Get(gomock.Eq(key)).Times(1).Return(retFoo, nil) + require.True(t, bytes.Equal(retFoo, store.Get(key))) + + mockDB.EXPECT().Get(gomock.Eq(key)).Times(1).Return(nil, errFoo) + require.Panics(t, func() { store.Get(key) }) + + mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(true, nil) + require.True(t, store.Has(key)) + + mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(false, nil) + require.False(t, store.Has(key)) + + mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(false, errFoo) + require.Panics(t, func() { store.Has(key) }) + + mockDB.EXPECT().Set(gomock.Eq(key), gomock.Eq(value)).Times(1).Return(nil) + require.NotPanics(t, func() { store.Set(key, value) }) + + mockDB.EXPECT().Set(gomock.Eq(key), gomock.Eq(value)).Times(1).Return(errFoo) + require.Panics(t, func() { store.Set(key, value) }) + + mockDB.EXPECT().Delete(gomock.Eq(key)).Times(1).Return(nil) + require.NotPanics(t, func() { store.Delete(key) }) + + mockDB.EXPECT().Delete(gomock.Eq(key)).Times(1).Return(errFoo) + require.Panics(t, func() { store.Delete(key) }) + + start, end := []byte("start"), []byte("end") + mockDB.EXPECT().Iterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, nil) + require.NotPanics(t, func() { store.Iterator(start, end) }) + + mockDB.EXPECT().Iterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, errFoo) + require.Panics(t, func() { store.Iterator(start, end) }) + + mockDB.EXPECT().ReverseIterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, nil) + require.NotPanics(t, func() { store.ReverseIterator(start, end) }) + + mockDB.EXPECT().ReverseIterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, errFoo) + require.Panics(t, func() { store.ReverseIterator(start, end) }) +} diff --git a/store/firstlast.go b/store/firstlast.go index 05a9a5fd85e2..307f932fb0c8 100644 --- a/store/firstlast.go +++ b/store/firstlast.go @@ -3,28 +3,27 @@ package store import ( "bytes" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkkv "github.com/cosmos/cosmos-sdk/types/kv" ) // Gets the first item. -func First(st KVStore, start, end []byte) (kv tmkv.Pair, ok bool) { +func First(st KVStore, start, end []byte) (kv sdkkv.Pair, ok bool) { iter := st.Iterator(start, end) if !iter.Valid() { return kv, false } defer iter.Close() - return tmkv.Pair{Key: iter.Key(), Value: iter.Value()}, true + return sdkkv.Pair{Key: iter.Key(), Value: iter.Value()}, true } // Gets the last item. `end` is exclusive. -func Last(st KVStore, start, end []byte) (kv tmkv.Pair, ok bool) { +func Last(st KVStore, start, end []byte) (kv sdkkv.Pair, ok bool) { iter := st.ReverseIterator(end, start) if !iter.Valid() { if v := st.Get(start); v != nil { - return tmkv.Pair{Key: types.Cp(start), Value: types.Cp(v)}, true + return sdkkv.Pair{Key: sdk.CopyBytes(start), Value: sdk.CopyBytes(v)}, true } return kv, false } @@ -38,5 +37,5 @@ func Last(st KVStore, start, end []byte) (kv tmkv.Pair, ok bool) { } } - return tmkv.Pair{Key: iter.Key(), Value: iter.Value()}, true + return sdkkv.Pair{Key: iter.Key(), Value: iter.Value()}, true } diff --git a/store/gaskv/store.go b/store/gaskv/store.go index 3b611a4ed114..d5c1f86c20aa 100644 --- a/store/gaskv/store.go +++ b/store/gaskv/store.go @@ -2,8 +2,10 @@ package gaskv import ( "io" + "time" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/telemetry" ) var _ types.KVStore = &Store{} @@ -34,6 +36,8 @@ func (gs *Store) GetStoreType() types.StoreType { // Implements KVStore. func (gs *Store) Get(key []byte) (value []byte) { + defer telemetry.MeasureSince(time.Now(), "store", "gaskv", "get") + gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, types.GasReadCostFlatDesc) value = gs.parent.Get(key) @@ -45,6 +49,9 @@ func (gs *Store) Get(key []byte) (value []byte) { // Implements KVStore. func (gs *Store) Set(key []byte, value []byte) { + defer telemetry.MeasureSince(time.Now(), "store", "gaskv", "set") + + types.AssertValidKey(key) types.AssertValidValue(value) gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, types.GasWriteCostFlatDesc) // TODO overflow-safe math? @@ -54,12 +61,14 @@ func (gs *Store) Set(key []byte, value []byte) { // Implements KVStore. func (gs *Store) Has(key []byte) bool { + defer telemetry.MeasureSince(time.Now(), "store", "gaskv", "has") gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, types.GasHasDesc) return gs.parent.Has(key) } // Implements KVStore. func (gs *Store) Delete(key []byte) { + defer telemetry.MeasureSince(time.Now(), "store", "gaskv", "delete") // charge gas to prevent certain attack vectors even though space is being freed gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, types.GasDeleteDesc) gs.parent.Delete(key) @@ -156,8 +165,8 @@ func (gi *gasIterator) Value() (value []byte) { } // Implements Iterator. -func (gi *gasIterator) Close() { - gi.parent.Close() +func (gi *gasIterator) Close() error { + return gi.parent.Close() } // Error delegates the Error call to the parent iterator. @@ -172,5 +181,4 @@ func (gi *gasIterator) consumeSeekGas() { gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasValuePerByteDesc) gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, types.GasIterNextCostFlatDesc) - } diff --git a/store/gaskv/store_test.go b/store/gaskv/store_test.go index 6fab9ebf835b..432fbe376343 100644 --- a/store/gaskv/store_test.go +++ b/store/gaskv/store_test.go @@ -22,6 +22,14 @@ func TestGasKVStoreBasic(t *testing.T) { mem := dbadapter.Store{DB: dbm.NewMemDB()} meter := types.NewGasMeter(10000) st := gaskv.NewStore(mem, meter, types.KVGasConfig()) + + require.Equal(t, types.StoreTypeDB, st.GetStoreType()) + require.Panics(t, func() { st.CacheWrap() }) + require.Panics(t, func() { st.CacheWrapWithTrace(nil, nil) }) + + require.Panics(t, func() { st.Set(nil, []byte("value")) }, "setting a nil key should panic") + require.Panics(t, func() { st.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") st.Set(keyFmt(1), valFmt(1)) require.Equal(t, valFmt(1), st.Get(keyFmt(1))) @@ -34,11 +42,24 @@ func TestGasKVStoreIterator(t *testing.T) { mem := dbadapter.Store{DB: dbm.NewMemDB()} meter := types.NewGasMeter(10000) st := gaskv.NewStore(mem, meter, types.KVGasConfig()) + require.False(t, st.Has(keyFmt(1))) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") st.Set(keyFmt(1), valFmt(1)) + require.True(t, st.Has(keyFmt(1))) st.Set(keyFmt(2), valFmt(2)) + iterator := st.Iterator(nil, nil) + start, end := iterator.Domain() + require.Nil(t, start) + require.Nil(t, end) + require.NoError(t, iterator.Error()) + + t.Cleanup(func() { + if err := iterator.Close(); err != nil { + t.Fatal(err) + } + }) ka := iterator.Key() require.Equal(t, ka, keyFmt(1)) va := iterator.Value() @@ -51,7 +72,22 @@ func TestGasKVStoreIterator(t *testing.T) { iterator.Next() require.False(t, iterator.Valid()) require.Panics(t, iterator.Next) - require.Equal(t, meter.GasConsumed(), types.Gas(6987)) + require.NoError(t, iterator.Error()) + + reverseIterator := st.ReverseIterator(nil, nil) + t.Cleanup(func() { + if err := reverseIterator.Close(); err != nil { + t.Fatal(err) + } + }) + require.Equal(t, reverseIterator.Key(), keyFmt(2)) + reverseIterator.Next() + require.Equal(t, reverseIterator.Key(), keyFmt(1)) + reverseIterator.Next() + require.False(t, reverseIterator.Valid()) + require.Panics(t, reverseIterator.Next) + + require.Equal(t, types.Gas(9194), meter.GasConsumed()) } func TestGasKVStoreOutOfGasSet(t *testing.T) { diff --git a/store/iavl/store.go b/store/iavl/store.go index d1e1d9c4cae0..e3a3f897d70f 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -1,21 +1,25 @@ package iavl import ( + "errors" "fmt" "io" "sync" + "time" - "github.com/pkg/errors" - "github.com/tendermint/iavl" + ics23 "github.com/confio/ics23/go" + "github.com/cosmos/iavl" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" - tmkv "github.com/tendermint/tendermint/libs/kv" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/store/cachekv" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/kv" ) const ( @@ -23,47 +27,31 @@ const ( ) var ( - _ types.KVStore = (*Store)(nil) - _ types.CommitStore = (*Store)(nil) - _ types.CommitKVStore = (*Store)(nil) - _ types.Queryable = (*Store)(nil) + _ types.KVStore = (*Store)(nil) + _ types.CommitStore = (*Store)(nil) + _ types.CommitKVStore = (*Store)(nil) + _ types.Queryable = (*Store)(nil) + _ types.StoreWithInitialVersion = (*Store)(nil) ) // Store Implements types.KVStore and CommitKVStore. type Store struct { - tree Tree - pruning types.PruningOptions + tree Tree } // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the // store's version (id) from the provided DB. An error is returned if the version -// fails to load. -func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyLoading bool) (types.CommitKVStore, error) { - if !pruning.IsValid() { - return nil, fmt.Errorf("pruning options are invalid: %v", pruning) - } - - var keepRecent int64 - - // Determine the value of keepRecent based on the following: - // - // If KeepEvery = 1, keepRecent should be 0 since there is no need to keep - // latest version in a in-memory cache. - // - // If KeepEvery > 1, keepRecent should be 1 so that state changes in between - // flushed states can be saved in the in-memory latest tree. - if pruning.KeepEvery == 1 { - keepRecent = 0 - } else { - keepRecent = 1 - } +// fails to load, or if called with a positive version on an empty tree. +func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) { + return LoadStoreWithInitialVersion(db, id, lazyLoading, 0) +} - tree, err := iavl.NewMutableTreeWithOpts( - db, - dbm.NewMemDB(), - defaultIAVLCacheSize, - iavl.PruningOptions(pruning.KeepEvery, keepRecent), - ) +// LoadStoreWithInitialVersion returns an IAVL Store as a CommitKVStore setting its initialVersion +// to the one given. Internally, it will load the store's version (id) from the +// provided DB. An error is returned if the version fails to load, or if called with a positive +// version on an empty tree. +func LoadStoreWithInitialVersion(db dbm.DB, id types.CommitID, lazyLoading bool, initialVersion uint64) (types.CommitKVStore, error) { + tree, err := iavl.NewMutableTreeWithOpts(db, defaultIAVLCacheSize, &iavl.Options{InitialVersion: initialVersion}) if err != nil { return nil, err } @@ -79,8 +67,7 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL } return &Store{ - tree: tree, - pruning: pruning, + tree: tree, }, nil } @@ -90,21 +77,20 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL // CONTRACT: The IAVL tree should be fully loaded. // CONTRACT: PruningOptions passed in as argument must be the same as pruning options // passed into iavl.MutableTree -func UnsafeNewStore(tree *iavl.MutableTree, po types.PruningOptions) *Store { +func UnsafeNewStore(tree *iavl.MutableTree) *Store { return &Store{ - tree: tree, - pruning: po, + tree: tree, } } // GetImmutable returns a reference to a new store backed by an immutable IAVL // tree at a specific version (height) without any pruning options. This should // be used for querying and iteration only. If the version does not exist or has -// been pruned, an error will be returned. Any mutable operations executed will -// result in a panic. +// been pruned, an empty immutable IAVL tree will be used. +// Any mutable operations executed will result in a panic. func (st *Store) GetImmutable(version int64) (*Store, error) { if !st.VersionExists(version) { - return nil, iavl.ErrVersionDoesNotExist + return &Store{tree: &immutableTree{&iavl.ImmutableTree{}}}, nil } iTree, err := st.tree.GetImmutable(version) @@ -113,42 +99,27 @@ func (st *Store) GetImmutable(version int64) (*Store, error) { } return &Store{ - tree: &immutableTree{iTree}, - pruning: st.pruning, + tree: &immutableTree{iTree}, }, nil } // Commit commits the current store state and returns a CommitID with the new // version and hash. func (st *Store) Commit() types.CommitID { + defer telemetry.MeasureSince(time.Now(), "store", "iavl", "commit") + hash, version, err := st.tree.SaveVersion() if err != nil { - // TODO: Do we want to extend Commit to allow returning errors? panic(err) } - // If the version we saved got flushed to disk, check if previous flushed - // version should be deleted. - if st.pruning.FlushVersion(version) { - previous := version - st.pruning.KeepEvery - - // Previous flushed version should only be pruned if the previous version is - // not a snapshot version OR if snapshotting is disabled (SnapshotEvery == 0). - if previous != 0 && !st.pruning.SnapshotVersion(previous) { - err := st.tree.DeleteVersion(previous) - if errCause := errors.Cause(err); errCause != nil && errCause != iavl.ErrVersionDoesNotExist { - panic(err) - } - } - } - return types.CommitID{ Version: version, Hash: hash, } } -// Implements Committer. +// LastCommitID implements Committer. func (st *Store) LastCommitID() types.CommitID { return types.CommitID{ Version: st.tree.Version(), @@ -162,6 +133,12 @@ func (st *Store) SetPruning(_ types.PruningOptions) { panic("cannot set pruning options on an initialized IAVL store") } +// SetPruning panics as pruning options should be provided at initialization +// since IAVl accepts pruning options directly. +func (st *Store) GetPruning() types.PruningOptions { + panic("cannot get pruning options on an initialized IAVL store") +} + // VersionExists returns whether or not a given version is stored. func (st *Store) VersionExists(version int64) bool { return st.tree.VersionExists(version) @@ -184,26 +161,37 @@ func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.Ca // Implements types.KVStore. func (st *Store) Set(key, value []byte) { + types.AssertValidKey(key) types.AssertValidValue(value) st.tree.Set(key, value) } // Implements types.KVStore. func (st *Store) Get(key []byte) []byte { + defer telemetry.MeasureSince(time.Now(), "store", "iavl", "get") _, value := st.tree.Get(key) return value } // Implements types.KVStore. func (st *Store) Has(key []byte) (exists bool) { + defer telemetry.MeasureSince(time.Now(), "store", "iavl", "has") return st.tree.Has(key) } // Implements types.KVStore. func (st *Store) Delete(key []byte) { + defer telemetry.MeasureSince(time.Now(), "store", "iavl", "delete") st.tree.Remove(key) } +// DeleteVersions deletes a series of versions from the MutableTree. An error +// is returned if any single version is invalid or the delete fails. All writes +// happen in a single batch with a single commit. +func (st *Store) DeleteVersions(versions ...int64) error { + return st.tree.DeleteVersions(versions...) +} + // Implements types.KVStore. func (st *Store) Iterator(start, end []byte) types.Iterator { var iTree *iavl.ImmutableTree @@ -232,6 +220,34 @@ func (st *Store) ReverseIterator(start, end []byte) types.Iterator { return newIAVLIterator(iTree, start, end, false) } +// SetInitialVersion sets the initial version of the IAVL tree. It is used when +// starting a new chain at an arbitrary height. +func (st *Store) SetInitialVersion(version int64) { + st.tree.SetInitialVersion(uint64(version)) +} + +// Exports the IAVL store at the given version, returning an iavl.Exporter for the tree. +func (st *Store) Export(version int64) (*iavl.Exporter, error) { + istore, err := st.GetImmutable(version) + if err != nil { + return nil, fmt.Errorf("iavl export failed for version %v: %w", version, err) + } + tree, ok := istore.tree.(*immutableTree) + if !ok || tree == nil { + return nil, fmt.Errorf("iavl export failed: unable to fetch tree for version %v", version) + } + return tree.Export(), nil +} + +// Import imports an IAVL tree at the given version, returning an iavl.Importer for importing. +func (st *Store) Import(version int64) (*iavl.Importer, error) { + tree, ok := st.tree.(*iavl.MutableTree) + if !ok { + return nil, errors.New("iavl import failed: unable to find mutable tree") + } + return tree.Import(version) +} + // Handle gatest the latest height, if height is 0 func getHeight(tree Tree, req abci.RequestQuery) int64 { height := req.Height @@ -254,6 +270,8 @@ func getHeight(tree Tree, req abci.RequestQuery) int64 { // if you care to have the latest data to see a tx results, you must // explicitly set the height you want to see func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { + defer telemetry.MeasureSince(time.Now(), "store", "iavl", "query") + if len(req.Data) == 0 { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrTxDecode, "query cannot be zero length")) } @@ -274,44 +292,45 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { break } - if req.Prove { - value, proof, err := tree.GetVersionedWithProof(key, res.Height) - if err != nil { - res.Log = err.Error() - break - } - if proof == nil { - // Proof == nil implies that the store is empty. - if value != nil { - panic("unexpected value for an empty proof") - } - } - if value != nil { - // value was found - res.Value = value - res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewValueOp(key, proof).ProofOp()}} - } else { - // value wasn't found - res.Value = nil - res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewAbsenceOp(key, proof).ProofOp()}} - } - } else { - _, res.Value = tree.GetVersioned(key, res.Height) + _, res.Value = tree.GetVersioned(key, res.Height) + if !req.Prove { + break } + // Continue to prove existence/absence of value + // Must convert store.Tree to iavl.MutableTree with given version to use in CreateProof + iTree, err := tree.GetImmutable(res.Height) + if err != nil { + // sanity check: If value for given version was retrieved, immutable tree must also be retrievable + panic(fmt.Sprintf("version exists in store but could not retrieve corresponding versioned tree in store, %s", err.Error())) + } + mtree := &iavl.MutableTree{ + ImmutableTree: iTree, + } + + // get proof from tree and convert to merkle.Proof before adding to result + res.ProofOps = getProofFromTree(mtree, req.Data, res.Value != nil) + case "/subspace": - var KVs []types.KVPair + pairs := kv.Pairs{ + Pairs: make([]kv.Pair, 0), + } subspace := req.Data res.Key = subspace iterator := types.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { - KVs = append(KVs, types.KVPair{Key: iterator.Key(), Value: iterator.Value()}) + pairs.Pairs = append(pairs.Pairs, kv.Pair{Key: iterator.Key(), Value: iterator.Value()}) } - iterator.Close() - res.Value = cdc.MustMarshalBinaryLengthPrefixed(KVs) + + bz, err := pairs.Marshal() + if err != nil { + panic(fmt.Errorf("failed to marshal KV pairs: %w", err)) + } + + res.Value = bz default: return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path)) @@ -320,6 +339,35 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return res } +// Takes a MutableTree, a key, and a flag for creating existence or absence proof and returns the +// appropriate merkle.Proof. Since this must be called after querying for the value, this function should never error +// Thus, it will panic on error rather than returning it +func getProofFromTree(tree *iavl.MutableTree, key []byte, exists bool) *tmcrypto.ProofOps { + var ( + commitmentProof *ics23.CommitmentProof + err error + ) + + if exists { + // value was found + commitmentProof, err = tree.GetMembershipProof(key) + if err != nil { + // sanity check: If value was found, membership proof must be creatable + panic(fmt.Sprintf("unexpected value for empty proof: %s", err.Error())) + } + } else { + // value wasn't found + commitmentProof, err = tree.GetNonMembershipProof(key) + if err != nil { + // sanity check: If value wasn't found, nonmembership proof must be creatable + panic(fmt.Sprintf("unexpected error for nonexistence proof: %s", err.Error())) + } + } + + op := types.NewIavlCommitmentOp(key, commitmentProof) + return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}} +} + //---------------------------------------- // Implements types.Iterator. @@ -334,7 +382,7 @@ type iavlIterator struct { tree *iavl.ImmutableTree // Channel to push iteration values. - iterCh chan tmkv.Pair + iterCh chan kv.Pair // Close this to release goroutine. quitCh chan struct{} @@ -357,10 +405,10 @@ var _ types.Iterator = (*iavlIterator)(nil) func newIAVLIterator(tree *iavl.ImmutableTree, start, end []byte, ascending bool) *iavlIterator { iter := &iavlIterator{ tree: tree, - start: types.Cp(start), - end: types.Cp(end), + start: sdk.CopyBytes(start), + end: sdk.CopyBytes(end), ascending: ascending, - iterCh: make(chan tmkv.Pair), // Set capacity > 0? + iterCh: make(chan kv.Pair), // Set capacity > 0? quitCh: make(chan struct{}), initCh: make(chan struct{}), } @@ -377,7 +425,7 @@ func (iter *iavlIterator) iterateRoutine() { select { case <-iter.quitCh: return true // done with iteration. - case iter.iterCh <- tmkv.Pair{Key: key, Value: value}: + case iter.iterCh <- kv.Pair{Key: key, Value: value}: return false // yay. } }, @@ -440,11 +488,13 @@ func (iter *iavlIterator) Value() []byte { // Close closes the IAVL iterator by closing the quit channel and waiting for // the iterCh to finish/close. -func (iter *iavlIterator) Close() { +func (iter *iavlIterator) Close() error { close(iter.quitCh) // wait iterCh to close for range iter.iterCh { } + + return nil } // Error performs a no-op. diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index ed138c84964f..790830038ba9 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/cosmos/iavl" "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types/kv" ) var ( @@ -49,10 +50,66 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { return tree, types.CommitID{Version: ver, Hash: hash} } +func TestLoadStore(t *testing.T) { + db := dbm.NewMemDB() + tree, _ := newAlohaTree(t, db) + store := UnsafeNewStore(tree) + + // Create non-pruned height H + require.True(t, tree.Set([]byte("hello"), []byte("hallo"))) + hash, verH, err := tree.SaveVersion() + cIDH := types.CommitID{Version: verH, Hash: hash} + require.Nil(t, err) + + // Create pruned height Hp + require.True(t, tree.Set([]byte("hello"), []byte("hola"))) + hash, verHp, err := tree.SaveVersion() + cIDHp := types.CommitID{Version: verHp, Hash: hash} + require.Nil(t, err) + + // TODO: Prune this height + + // Create current height Hc + require.True(t, tree.Set([]byte("hello"), []byte("ciao"))) + hash, verHc, err := tree.SaveVersion() + cIDHc := types.CommitID{Version: verHc, Hash: hash} + require.Nil(t, err) + + // Querying an existing store at some previous non-pruned height H + hStore, err := store.GetImmutable(verH) + require.NoError(t, err) + require.Equal(t, string(hStore.Get([]byte("hello"))), "hallo") + + // Querying an existing store at some previous pruned height Hp + hpStore, err := store.GetImmutable(verHp) + require.NoError(t, err) + require.Equal(t, string(hpStore.Get([]byte("hello"))), "hola") + + // Querying an existing store at current height Hc + hcStore, err := store.GetImmutable(verHc) + require.NoError(t, err) + require.Equal(t, string(hcStore.Get([]byte("hello"))), "ciao") + + // Querying a new store at some previous non-pruned height H + newHStore, err := LoadStore(db, cIDH, false) + require.NoError(t, err) + require.Equal(t, string(newHStore.Get([]byte("hello"))), "hallo") + + // Querying a new store at some previous pruned height Hp + newHpStore, err := LoadStore(db, cIDHp, false) + require.NoError(t, err) + require.Equal(t, string(newHpStore.Get([]byte("hello"))), "hola") + + // Querying a new store at current height H + newHcStore, err := LoadStore(db, cIDHc, false) + require.NoError(t, err) + require.Equal(t, string(newHcStore.Get([]byte("hello"))), "ciao") +} + func TestGetImmutable(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) - store := UnsafeNewStore(tree, types.PruneNothing) + store := UnsafeNewStore(tree) require.True(t, tree.Set([]byte("hello"), []byte("adios"))) hash, ver, err := tree.SaveVersion() @@ -60,7 +117,7 @@ func TestGetImmutable(t *testing.T) { require.Nil(t, err) _, err = store.GetImmutable(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) newStore, err := store.GetImmutable(cID.Version - 1) require.NoError(t, err) @@ -72,7 +129,7 @@ func TestGetImmutable(t *testing.T) { res := newStore.Query(abci.RequestQuery{Data: []byte("hello"), Height: cID.Version, Path: "/key", Prove: true}) require.Equal(t, res.Value, []byte("adios")) - require.NotNil(t, res.Proof) + require.NotNil(t, res.ProofOps) require.Panics(t, func() { newStore.Set(nil, nil) }) require.Panics(t, func() { newStore.Delete(nil) }) @@ -82,7 +139,7 @@ func TestGetImmutable(t *testing.T) { func TestTestGetImmutableIterator(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) - store := UnsafeNewStore(tree, types.PruneNothing) + store := UnsafeNewStore(tree) newStore, err := store.GetImmutable(cID.Version) require.NoError(t, err) @@ -105,7 +162,7 @@ func TestTestGetImmutableIterator(t *testing.T) { func TestIAVLStoreGetSetHasDelete(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) key := "hello" @@ -130,14 +187,18 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { func TestIAVLStoreNoNilSet(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) + + require.Panics(t, func() { iavlStore.Set(nil, []byte("value")) }, "setting a nil key should panic") + require.Panics(t, func() { iavlStore.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") + require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz")) expected := []string{"aloha", "hello"} var i int @@ -213,7 +274,7 @@ func TestIAVLReverseIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte{0x00}, []byte("0")) iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0")) @@ -246,7 +307,7 @@ func TestIAVLPrefixIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -310,7 +371,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -373,95 +434,12 @@ func nextVersion(iavl *Store) { iavl.Commit() } -func TestIAVLDefaultPruning(t *testing.T) { - //Expected stored / deleted version numbers for: - //numRecent = 5, storeEvery = 3, snapshotEvery = 5 - var states = []pruneState{ - {[]int64{}, []int64{}}, - {[]int64{1}, []int64{}}, - {[]int64{1, 2}, []int64{}}, - {[]int64{1, 2, 3}, []int64{}}, - {[]int64{1, 2, 3, 4}, []int64{}}, - {[]int64{1, 2, 3, 4, 5}, []int64{}}, - {[]int64{2, 4, 5, 6}, []int64{1, 3}}, - {[]int64{4, 5, 6, 7}, []int64{1, 2, 3}}, - {[]int64{4, 5, 6, 7, 8}, []int64{1, 2, 3}}, - {[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}}, - {[]int64{6, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 5}}, - {[]int64{6, 7, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 5}}, - {[]int64{6, 8, 10, 11, 12}, []int64{1, 2, 3, 4, 5, 7, 9}}, - {[]int64{6, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 7, 8, 9}}, - {[]int64{6, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 7, 8, 9}}, - {[]int64{6, 11, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 7, 8, 9, 10}}, - } - testPruning(t, int64(5), int64(3), int64(6), states) -} - -func TestIAVLAlternativePruning(t *testing.T) { - //Expected stored / deleted version numbers for: - //numRecent = 3, storeEvery = 5, snapshotEvery = 10 - var states = []pruneState{ - {[]int64{}, []int64{}}, - {[]int64{1}, []int64{}}, - {[]int64{1, 2}, []int64{}}, - {[]int64{1, 2, 3}, []int64{}}, - {[]int64{2, 3, 4}, []int64{1}}, - {[]int64{3, 4, 5}, []int64{1, 2}}, - {[]int64{4, 5, 6}, []int64{1, 2, 3}}, - {[]int64{5, 6, 7}, []int64{1, 2, 3, 4}}, - {[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}}, - {[]int64{5, 7, 8, 9}, []int64{1, 2, 3, 4, 6}}, - {[]int64{8, 9, 10}, []int64{1, 2, 3, 4, 6, 7}}, - {[]int64{9, 10, 11}, []int64{1, 2, 3, 4, 6, 7, 8}}, - {[]int64{10, 11, 12}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}}, - {[]int64{10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}}, - {[]int64{10, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11}}, - {[]int64{10, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12}}, - } - testPruning(t, int64(3), int64(5), int64(10), states) -} - -type pruneState struct { - stored []int64 - deleted []int64 -} - -func testPruning(t *testing.T, numRecent int64, storeEvery int64, snapshotEvery int64, states []pruneState) { - db := dbm.NewMemDB() - pruningOpts := types.PruningOptions{ - KeepEvery: storeEvery, - SnapshotEvery: snapshotEvery, - } - iavlOpts := iavl.PruningOptions(storeEvery, numRecent) - - tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts) - require.NoError(t, err) - - iavlStore := UnsafeNewStore(tree, pruningOpts) - - for step, state := range states { - for _, ver := range state.stored { - require.True(t, iavlStore.VersionExists(ver), - "missing version %d with latest version %d; should save last %d, store every %d, and snapshot every %d", - ver, step, numRecent, storeEvery, snapshotEvery) - } - - for _, ver := range state.deleted { - require.False(t, iavlStore.VersionExists(ver), - "not pruned version %d with latest version %d; should prune all but last %d and every %d with intermediate flush interval %d", - ver, step, numRecent, snapshotEvery, storeEvery) - } - - nextVersion(iavlStore) - } -} - func TestIAVLNoPrune(t *testing.T) { db := dbm.NewMemDB() tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) nextVersion(iavlStore) for i := 1; i < 100; i++ { @@ -475,55 +453,40 @@ func TestIAVLNoPrune(t *testing.T) { } } -func TestIAVLPruneEverything(t *testing.T) { - db := dbm.NewMemDB() - iavlOpts := iavl.PruningOptions(0, 1) // only store latest version in memory - - tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts) - require.NoError(t, err) - - iavlStore := UnsafeNewStore(tree, types.PruneEverything) - nextVersion(iavlStore) - - for i := 1; i < 100; i++ { - for j := 1; j < i; j++ { - require.False(t, iavlStore.VersionExists(int64(j)), - "not pruned version %d with latest version %d; should prune all old versions", - j, i) - } - - require.True(t, iavlStore.VersionExists(int64(i)), - "missing current version on step %d; should not prune current state tree", - i) - - nextVersion(iavlStore) - } -} - func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) k1, v1 := []byte("key1"), []byte("val1") k2, v2 := []byte("key2"), []byte("val2") v3 := []byte("val3") ksub := []byte("key") - KVs0 := []types.KVPair{} - KVs1 := []types.KVPair{ - {Key: k1, Value: v1}, - {Key: k2, Value: v2}, + KVs0 := kv.Pairs{} + KVs1 := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: k1, Value: v1}, + {Key: k2, Value: v2}, + }, } - KVs2 := []types.KVPair{ - {Key: k1, Value: v3}, - {Key: k2, Value: v2}, + KVs2 := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: k1, Value: v3}, + {Key: k2, Value: v2}, + }, } - valExpSubEmpty := cdc.MustMarshalBinaryLengthPrefixed(KVs0) - valExpSub1 := cdc.MustMarshalBinaryLengthPrefixed(KVs1) - valExpSub2 := cdc.MustMarshalBinaryLengthPrefixed(KVs2) + + valExpSubEmpty, err := KVs0.Marshal() + require.NoError(t, err) + + valExpSub1, err := KVs1.Marshal() + require.NoError(t, err) + + valExpSub2, err := KVs2.Marshal() + require.NoError(t, err) cid := iavlStore.Commit() ver := cid.Version @@ -604,7 +567,7 @@ func BenchmarkIAVLIteratorNext(b *testing.B) { tree.Set(key, value) } - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iterators := make([]types.Iterator, b.N/treeSize) for i := 0; i < len(iterators); i++ { @@ -619,3 +582,54 @@ func BenchmarkIAVLIteratorNext(b *testing.B) { } } } + +func TestSetInitialVersion(t *testing.T) { + testCases := []struct { + name string + storeFn func(db *dbm.MemDB) *Store + expPanic bool + }{ + { + "works with a mutable tree", + func(db *dbm.MemDB) *Store { + tree, err := iavl.NewMutableTree(db, cacheSize) + require.NoError(t, err) + store := UnsafeNewStore(tree) + + return store + }, false, + }, + { + "throws error on immutable tree", + func(db *dbm.MemDB) *Store { + tree, err := iavl.NewMutableTree(db, cacheSize) + require.NoError(t, err) + store := UnsafeNewStore(tree) + _, version, err := store.tree.SaveVersion() + require.NoError(t, err) + require.Equal(t, int64(1), version) + store, err = store.GetImmutable(1) + require.NoError(t, err) + + return store + }, true, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + db := dbm.NewMemDB() + store := tc.storeFn(db) + + if tc.expPanic { + require.Panics(t, func() { store.SetInitialVersion(5) }) + } else { + store.SetInitialVersion(5) + cid := store.Commit() + require.Equal(t, int64(5), cid.GetVersion()) + } + }) + } +} diff --git a/store/iavl/tree.go b/store/iavl/tree.go index deae294b2c5d..83d1ada301fd 100644 --- a/store/iavl/tree.go +++ b/store/iavl/tree.go @@ -3,7 +3,7 @@ package iavl import ( "fmt" - "github.com/tendermint/iavl" + "github.com/cosmos/iavl" ) var ( @@ -23,12 +23,14 @@ type ( Remove(key []byte) ([]byte, bool) SaveVersion() ([]byte, int64, error) DeleteVersion(version int64) error + DeleteVersions(versions ...int64) error Version() int64 Hash() []byte VersionExists(version int64) bool GetVersioned(key []byte, version int64) (int64, []byte) GetVersionedWithProof(key []byte, version int64) ([]byte, *iavl.RangeProof, error) GetImmutable(version int64) (*iavl.ImmutableTree, error) + SetInitialVersion(version uint64) } // immutableTree is a simple wrapper around a reference to an iavl.ImmutableTree @@ -55,6 +57,14 @@ func (it *immutableTree) DeleteVersion(_ int64) error { panic("cannot call 'DeleteVersion' on an immutable IAVL tree") } +func (it *immutableTree) DeleteVersions(_ ...int64) error { + panic("cannot call 'DeleteVersions' on an immutable IAVL tree") +} + +func (it *immutableTree) SetInitialVersion(_ uint64) { + panic("cannot call 'SetInitialVersion' on an immutable IAVL tree") +} + func (it *immutableTree) VersionExists(version int64) bool { return it.Version() == version } diff --git a/store/iavl/tree_test.go b/store/iavl/tree_test.go new file mode 100644 index 000000000000..24332b42e06a --- /dev/null +++ b/store/iavl/tree_test.go @@ -0,0 +1,37 @@ +package iavl + +import ( + "testing" + + "github.com/cosmos/iavl" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" +) + +func TestImmutableTreePanics(t *testing.T) { + t.Parallel() + immTree := iavl.NewImmutableTree(dbm.NewMemDB(), 100) + it := &immutableTree{immTree} + require.Panics(t, func() { it.Set([]byte{}, []byte{}) }) + require.Panics(t, func() { it.Remove([]byte{}) }) + require.Panics(t, func() { it.SaveVersion() }) // nolint:errcheck + require.Panics(t, func() { it.DeleteVersion(int64(1)) }) // nolint:errcheck + v, _ := it.GetVersioned([]byte{0x01}, 1) + require.Equal(t, int64(-1), v) + v, _ = it.GetVersioned([]byte{0x01}, 0) + require.Equal(t, int64(0), v) + + val, proof, err := it.GetVersionedWithProof(nil, 1) + require.Error(t, err) + require.Nil(t, val) + require.Nil(t, proof) + + imm, err := it.GetImmutable(1) + require.Error(t, err) + require.Nil(t, imm) + + imm, err = it.GetImmutable(0) + require.NoError(t, err) + require.NotNil(t, imm) + require.Equal(t, immTree, imm) +} diff --git a/store/iavl/wire.go b/store/iavl/wire.go deleted file mode 100644 index 43173f3e7895..000000000000 --- a/store/iavl/wire.go +++ /dev/null @@ -1,7 +0,0 @@ -package iavl - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var cdc = codec.New() diff --git a/store/internal/maps/bench_test.go b/store/internal/maps/bench_test.go new file mode 100644 index 000000000000..4d7f680c70c1 --- /dev/null +++ b/store/internal/maps/bench_test.go @@ -0,0 +1,13 @@ +package maps + +import "testing" + +func BenchmarkKVPairBytes(b *testing.B) { + kvp := NewKVPair(make([]byte, 128), make([]byte, 1e6)) + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + b.SetBytes(int64(len(kvp.Bytes()))) + } +} diff --git a/store/internal/maps/maps.go b/store/internal/maps/maps.go new file mode 100644 index 000000000000..778f1c84ed98 --- /dev/null +++ b/store/internal/maps/maps.go @@ -0,0 +1,215 @@ +package maps + +import ( + "encoding/binary" + + "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/crypto/tmhash" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/types/kv" +) + +// merkleMap defines a merkle-ized tree from a map. Leave values are treated as +// hash(key) | hash(value). Leaves are sorted before Merkle hashing. +type merkleMap struct { + kvs kv.Pairs + sorted bool +} + +func newMerkleMap() *merkleMap { + return &merkleMap{ + kvs: kv.Pairs{}, + sorted: false, + } +} + +// Set creates a kv.Pair from the provided key and value. The value is hashed prior +// to creating a kv.Pair. The created kv.Pair is appended to the MerkleMap's slice +// of kv.Pairs. Whenever called, the MerkleMap must be resorted. +func (sm *merkleMap) set(key string, value []byte) { + byteKey := []byte(key) + assertValidKey(byteKey) + + sm.sorted = false + + // The value is hashed, so you can check for equality with a cached value (say) + // and make a determination to fetch or not. + vhash := tmhash.Sum(value) + + sm.kvs.Pairs = append(sm.kvs.Pairs, kv.Pair{ + Key: byteKey, + Value: vhash, + }) +} + +// Hash returns the merkle root of items sorted by key. Note, it is unstable. +func (sm *merkleMap) hash() []byte { + sm.sort() + return hashKVPairs(sm.kvs) +} + +func (sm *merkleMap) sort() { + if sm.sorted { + return + } + + sm.kvs.Sort() + sm.sorted = true +} + +// hashKVPairs hashes a kvPair and creates a merkle tree where the leaves are +// byte slices. +func hashKVPairs(kvs kv.Pairs) []byte { + kvsH := make([][]byte, len(kvs.Pairs)) + for i, kvp := range kvs.Pairs { + kvsH[i] = KVPair(kvp).Bytes() + } + + return merkle.HashFromByteSlices(kvsH) +} + +// --------------------------------------------- + +// Merkle tree from a map. +// Leaves are `hash(key) | hash(value)`. +// Leaves are sorted before Merkle hashing. +type simpleMap struct { + Kvs kv.Pairs + sorted bool +} + +func newSimpleMap() *simpleMap { + return &simpleMap{ + Kvs: kv.Pairs{}, + sorted: false, + } +} + +// Set creates a kv pair of the key and the hash of the value, +// and then appends it to SimpleMap's kv pairs. +func (sm *simpleMap) Set(key string, value []byte) { + byteKey := []byte(key) + assertValidKey(byteKey) + sm.sorted = false + + // The value is hashed, so you can + // check for equality with a cached value (say) + // and make a determination to fetch or not. + vhash := tmhash.Sum(value) + + sm.Kvs.Pairs = append(sm.Kvs.Pairs, kv.Pair{ + Key: byteKey, + Value: vhash, + }) +} + +// Hash Merkle root hash of items sorted by key +// (UNSTABLE: and by value too if duplicate key). +func (sm *simpleMap) Hash() []byte { + sm.Sort() + return hashKVPairs(sm.Kvs) +} + +func (sm *simpleMap) Sort() { + if sm.sorted { + return + } + sm.Kvs.Sort() + sm.sorted = true +} + +// Returns a copy of sorted KVPairs. +// NOTE these contain the hashed key and value. +func (sm *simpleMap) KVPairs() kv.Pairs { + sm.Sort() + kvs := kv.Pairs{ + Pairs: make([]kv.Pair, len(sm.Kvs.Pairs)), + } + + copy(kvs.Pairs, sm.Kvs.Pairs) + return kvs +} + +//---------------------------------------- + +// A local extension to KVPair that can be hashed. +// Key and value are length prefixed and concatenated, +// then hashed. +type KVPair kv.Pair + +// NewKVPair takes in a key and value and creates a kv.Pair +// wrapped in the local extension KVPair +func NewKVPair(key, value []byte) KVPair { + return KVPair(kv.Pair{ + Key: key, + Value: value, + }) +} + +// Bytes returns key || value, with both the +// key and value length prefixed. +func (kv KVPair) Bytes() []byte { + // In the worst case: + // * 8 bytes to Uvarint encode the length of the key + // * 8 bytes to Uvarint encode the length of the value + // So preallocate for the worst case, which will in total + // be a maximum of 14 bytes wasted, if len(key)=1, len(value)=1, + // but that's going to rare. + buf := make([]byte, 8+len(kv.Key)+8+len(kv.Value)) + + // Encode the key, prefixed with its length. + nlk := binary.PutUvarint(buf, uint64(len(kv.Key))) + nk := copy(buf[nlk:], kv.Key) + + // Encode the value, prefixing with its length. + nlv := binary.PutUvarint(buf[nlk+nk:], uint64(len(kv.Value))) + nv := copy(buf[nlk+nk+nlv:], kv.Value) + + return buf[:nlk+nk+nlv+nv] +} + +// HashFromMap computes a merkle tree from sorted map and returns the merkle +// root. +func HashFromMap(m map[string][]byte) []byte { + mm := newMerkleMap() + for k, v := range m { + mm.set(k, v) + } + + return mm.hash() +} + +// ProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values +// in the underlying key-value pairs. +// The keys are sorted before the proofs are computed. +func ProofsFromMap(m map[string][]byte) ([]byte, map[string]*tmcrypto.Proof, []string) { + sm := newSimpleMap() + for k, v := range m { + sm.Set(k, v) + } + + sm.Sort() + kvs := sm.Kvs + kvsBytes := make([][]byte, len(kvs.Pairs)) + for i, kvp := range kvs.Pairs { + kvsBytes[i] = KVPair(kvp).Bytes() + } + + rootHash, proofList := merkle.ProofsFromByteSlices(kvsBytes) + proofs := make(map[string]*tmcrypto.Proof) + keys := make([]string, len(proofList)) + + for i, kvp := range kvs.Pairs { + proofs[string(kvp.Key)] = proofList[i].ToProto() + keys[i] = string(kvp.Key) + } + + return rootHash, proofs, keys +} + +func assertValidKey(key []byte) { + if len(key) == 0 { + panic("key is nil") + } +} diff --git a/store/internal/maps/maps_test.go b/store/internal/maps/maps_test.go new file mode 100644 index 000000000000..8f6b1163eaec --- /dev/null +++ b/store/internal/maps/maps_test.go @@ -0,0 +1,102 @@ +package maps + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEmptyKeyMerkleMap(t *testing.T) { + db := newMerkleMap() + require.Panics(t, func() { db.set("", []byte("value")) }, "setting an empty key should panic") +} +func TestMerkleMap(t *testing.T) { + tests := []struct { + keys []string + values []string // each string gets converted to []byte in test + want string + }{ + {[]string{}, []string{}, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {[]string{"key1"}, []string{"value1"}, "a44d3cc7daba1a4600b00a2434b30f8b970652169810d6dfa9fb1793a2189324"}, + {[]string{"key1"}, []string{"value2"}, "0638e99b3445caec9d95c05e1a3fc1487b4ddec6a952ff337080360b0dcc078c"}, + // swap order with 2 keys + { + []string{"key1", "key2"}, + []string{"value1", "value2"}, + "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3", + }, + { + []string{"key2", "key1"}, + []string{"value2", "value1"}, + "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3", + }, + // swap order with 3 keys + { + []string{"key1", "key2", "key3"}, + []string{"value1", "value2", "value3"}, + "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc", + }, + { + []string{"key1", "key3", "key2"}, + []string{"value1", "value3", "value2"}, + "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc", + }, + } + for i, tc := range tests { + db := newMerkleMap() + for i := 0; i < len(tc.keys); i++ { + db.set(tc.keys[i], []byte(tc.values[i])) + } + + got := db.hash() + assert.Equal(t, tc.want, fmt.Sprintf("%x", got), "Hash didn't match on tc %d", i) + } +} + +func TestEmptyKeySimpleMap(t *testing.T) { + db := newSimpleMap() + require.Panics(t, func() { db.Set("", []byte("value")) }, "setting an empty key should panic") +} +func TestSimpleMap(t *testing.T) { + tests := []struct { + keys []string + values []string // each string gets converted to []byte in test + want string + }{ + {[]string{}, []string{}, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {[]string{"key1"}, []string{"value1"}, "a44d3cc7daba1a4600b00a2434b30f8b970652169810d6dfa9fb1793a2189324"}, + {[]string{"key1"}, []string{"value2"}, "0638e99b3445caec9d95c05e1a3fc1487b4ddec6a952ff337080360b0dcc078c"}, + // swap order with 2 keys + { + []string{"key1", "key2"}, + []string{"value1", "value2"}, + "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3", + }, + { + []string{"key2", "key1"}, + []string{"value2", "value1"}, + "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3", + }, + // swap order with 3 keys + { + []string{"key1", "key2", "key3"}, + []string{"value1", "value2", "value3"}, + "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc", + }, + { + []string{"key1", "key3", "key2"}, + []string{"value1", "value3", "value2"}, + "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc", + }, + } + for i, tc := range tests { + db := newSimpleMap() + for i := 0; i < len(tc.keys); i++ { + db.Set(tc.keys[i], []byte(tc.values[i])) + } + got := db.Hash() + assert.Equal(t, tc.want, fmt.Sprintf("%x", got), "Hash didn't match on tc %d", i) + } +} diff --git a/store/internal/proofs/convert.go b/store/internal/proofs/convert.go new file mode 100644 index 000000000000..7e827ced5fac --- /dev/null +++ b/store/internal/proofs/convert.go @@ -0,0 +1,98 @@ +package proofs + +import ( + "fmt" + "math/bits" + + ics23 "github.com/confio/ics23/go" + "github.com/tendermint/tendermint/proto/tendermint/crypto" +) + +// ConvertExistenceProof will convert the given proof into a valid +// existence proof, if that's what it is. +// +// This is the simplest case of the range proof and we will focus on +// demoing compatibility here +func ConvertExistenceProof(p *crypto.Proof, key, value []byte) (*ics23.ExistenceProof, error) { + path, err := convertInnerOps(p) + if err != nil { + return nil, err + } + + proof := &ics23.ExistenceProof{ + Key: key, + Value: value, + Leaf: convertLeafOp(), + Path: path, + } + return proof, nil +} + +// this is adapted from merkle/hash.go:leafHash() +// and merkle/simple_map.go:KVPair.Bytes() +func convertLeafOp() *ics23.LeafOp { + prefix := []byte{0} + + return &ics23.LeafOp{ + Hash: ics23.HashOp_SHA256, + PrehashKey: ics23.HashOp_NO_HASH, + PrehashValue: ics23.HashOp_SHA256, + Length: ics23.LengthOp_VAR_PROTO, + Prefix: prefix, + } +} + +func convertInnerOps(p *crypto.Proof) ([]*ics23.InnerOp, error) { + inners := make([]*ics23.InnerOp, 0, len(p.Aunts)) + path := buildPath(p.Index, p.Total) + + if len(p.Aunts) != len(path) { + return nil, fmt.Errorf("calculated a path different length (%d) than provided by SimpleProof (%d)", len(path), len(p.Aunts)) + } + + for i, aunt := range p.Aunts { + auntRight := path[i] + + // combine with: 0x01 || lefthash || righthash + inner := &ics23.InnerOp{Hash: ics23.HashOp_SHA256} + if auntRight { + inner.Prefix = []byte{1} + inner.Suffix = aunt + } else { + inner.Prefix = append([]byte{1}, aunt...) + } + inners = append(inners, inner) + } + return inners, nil +} + +// buildPath returns a list of steps from leaf to root +// in each step, true means index is left side, false index is right side +// code adapted from merkle/simple_proof.go:computeHashFromAunts +func buildPath(idx, total int64) []bool { + if total < 2 { + return nil + } + numLeft := getSplitPoint(total) + goLeft := idx < numLeft + + // we put goLeft at the end of the array, as we recurse from top to bottom, + // and want the leaf to be first in array, root last + if goLeft { + return append(buildPath(idx, numLeft), goLeft) + } + return append(buildPath(idx-numLeft, total-numLeft), goLeft) +} + +func getSplitPoint(length int64) int64 { + if length < 1 { + panic("Trying to split a tree with size < 1") + } + uLength := uint(length) + bitlen := bits.Len(uLength) + k := int64(1 << uint(bitlen-1)) + if k == length { + k >>= 1 + } + return k +} diff --git a/store/internal/proofs/convert_test.go b/store/internal/proofs/convert_test.go new file mode 100644 index 000000000000..19c5a6761507 --- /dev/null +++ b/store/internal/proofs/convert_test.go @@ -0,0 +1,105 @@ +package proofs + +import ( + "bytes" + "fmt" + "testing" +) + +func TestLeafOp(t *testing.T) { + proof := GenerateRangeProof(20, Middle) + + converted, err := ConvertExistenceProof(proof.Proof, proof.Key, proof.Value) + if err != nil { + t.Fatal(err) + } + + leaf := converted.GetLeaf() + if leaf == nil { + t.Fatalf("Missing leaf node") + } + + hash, err := leaf.Apply(converted.Key, converted.Value) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(hash, proof.Proof.LeafHash) { + t.Errorf("Calculated: %X\nExpected: %X", hash, proof.Proof.LeafHash) + } +} + +func TestBuildPath(t *testing.T) { + cases := map[string]struct { + idx int64 + total int64 + expected []bool + }{ + "pair left": { + idx: 0, + total: 2, + expected: []bool{true}, + }, + "pair right": { + idx: 1, + total: 2, + expected: []bool{false}, + }, + "power of 2": { + idx: 3, + total: 8, + expected: []bool{false, false, true}, + }, + "size of 7 right most": { + idx: 6, + total: 7, + expected: []bool{false, false}, + }, + "size of 6 right-left (from top)": { + idx: 4, + total: 6, + expected: []bool{true, false}, + }, + "size of 6 left-right-left (from top)": { + idx: 2, + total: 7, + expected: []bool{true, false, true}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + path := buildPath(tc.idx, tc.total) + if len(path) != len(tc.expected) { + t.Fatalf("Got %v\nExpected %v", path, tc.expected) + } + for i := range path { + if path[i] != tc.expected[i] { + t.Fatalf("Differ at %d\nGot %v\nExpected %v", i, path, tc.expected) + } + } + }) + } +} + +func TestConvertProof(t *testing.T) { + for i := 0; i < 100; i++ { + t.Run(fmt.Sprintf("Run %d", i), func(t *testing.T) { + proof := GenerateRangeProof(57, Left) + + converted, err := ConvertExistenceProof(proof.Proof, proof.Key, proof.Value) + if err != nil { + t.Fatal(err) + } + + calc, err := converted.Calculate() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(calc, proof.RootHash) { + t.Errorf("Calculated: %X\nExpected: %X", calc, proof.RootHash) + } + }) + } +} diff --git a/store/internal/proofs/create.go b/store/internal/proofs/create.go new file mode 100644 index 000000000000..63b6bf69b149 --- /dev/null +++ b/store/internal/proofs/create.go @@ -0,0 +1,121 @@ +package proofs + +import ( + "errors" + "fmt" + "sort" + + ics23 "github.com/confio/ics23/go" + + sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" +) + +var ( + ErrEmptyKey = errors.New("key is empty") + ErrEmptyKeyInData = errors.New("data contains empty key") +) + +// TendermintSpec constrains the format from ics23-tendermint (crypto/merkle SimpleProof) +var TendermintSpec = &ics23.ProofSpec{ + LeafSpec: &ics23.LeafOp{ + Prefix: []byte{0}, + Hash: ics23.HashOp_SHA256, + PrehashValue: ics23.HashOp_SHA256, + Length: ics23.LengthOp_VAR_PROTO, + }, + InnerSpec: &ics23.InnerSpec{ + ChildOrder: []int32{0, 1}, + MinPrefixLength: 1, + MaxPrefixLength: 1, // fixed prefix + one child + ChildSize: 32, // (no length byte) + Hash: ics23.HashOp_SHA256, + }, +} + +/* +CreateMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the iavl tree. +If the key doesn't exist in the tree, this will return an error. +*/ +func CreateMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) { + if len(key) == 0 { + return nil, ErrEmptyKey + } + exist, err := createExistenceProof(data, key) + if err != nil { + return nil, err + } + proof := &ics23.CommitmentProof{ + Proof: &ics23.CommitmentProof_Exist{ + Exist: exist, + }, + } + return proof, nil +} + +/* +CreateNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the iavl tree. +If the key exists in the tree, this will return an error. +*/ +func CreateNonMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) { + if len(key) == 0 { + return nil, ErrEmptyKey + } + // ensure this key is not in the store + if _, ok := data[string(key)]; ok { + return nil, fmt.Errorf("cannot create non-membership proof if key is in map") + } + + keys := SortedKeys(data) + rightidx := sort.SearchStrings(keys, string(key)) + + var err error + nonexist := &ics23.NonExistenceProof{ + Key: key, + } + + // include left proof unless key is left of entire map + if rightidx >= 1 { + leftkey := keys[rightidx-1] + nonexist.Left, err = createExistenceProof(data, []byte(leftkey)) + if err != nil { + return nil, err + } + } + + // include right proof unless key is right of entire map + if rightidx < len(keys) { + rightkey := keys[rightidx] + nonexist.Right, err = createExistenceProof(data, []byte(rightkey)) + if err != nil { + return nil, err + } + + } + + proof := &ics23.CommitmentProof{ + Proof: &ics23.CommitmentProof_Nonexist{ + Nonexist: nonexist, + }, + } + return proof, nil +} + +func createExistenceProof(data map[string][]byte, key []byte) (*ics23.ExistenceProof, error) { + for k := range data { + if k == "" { + return nil, ErrEmptyKeyInData + } + } + value, ok := data[string(key)] + if !ok { + return nil, fmt.Errorf("cannot make existence proof if key is not in map") + } + + _, ics23, _ := sdkmaps.ProofsFromMap(data) + proof := ics23[string(key)] + if proof == nil { + return nil, fmt.Errorf("returned no proof for key") + } + + return ConvertExistenceProof(proof, key, value) +} diff --git a/store/internal/proofs/create_test.go b/store/internal/proofs/create_test.go new file mode 100644 index 000000000000..dc95ee3d9697 --- /dev/null +++ b/store/internal/proofs/create_test.go @@ -0,0 +1,112 @@ +package proofs + +import ( + "errors" + "testing" + + ics23 "github.com/confio/ics23/go" + "github.com/stretchr/testify/assert" +) + +func TestCreateMembership(t *testing.T) { + cases := map[string]struct { + size int + loc Where + }{ + "small left": {size: 100, loc: Left}, + "small middle": {size: 100, loc: Middle}, + "small right": {size: 100, loc: Right}, + "big left": {size: 5431, loc: Left}, + "big middle": {size: 5431, loc: Middle}, + "big right": {size: 5431, loc: Right}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + data := BuildMap(tc.size) + allkeys := SortedKeys(data) + key := GetKey(allkeys, tc.loc) + val := data[key] + proof, err := CreateMembershipProof(data, []byte(key)) + if err != nil { + t.Fatalf("Creating Proof: %+v", err) + } + if proof.GetExist() == nil { + t.Fatal("Unexpected proof format") + } + + root := CalcRoot(data) + err = proof.GetExist().Verify(TendermintSpec, root, []byte(key), val) + if err != nil { + t.Fatalf("Verifying Proof: %+v", err) + } + + valid := ics23.VerifyMembership(TendermintSpec, root, proof, []byte(key), val) + if !valid { + t.Fatalf("Membership Proof Invalid") + } + }) + } +} + +func TestCreateNonMembership(t *testing.T) { + cases := map[string]struct { + size int + loc Where + }{ + "small left": {size: 100, loc: Left}, + "small middle": {size: 100, loc: Middle}, + "small right": {size: 100, loc: Right}, + "big left": {size: 5431, loc: Left}, + "big middle": {size: 5431, loc: Middle}, + "big right": {size: 5431, loc: Right}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + data := BuildMap(tc.size) + allkeys := SortedKeys(data) + key := GetNonKey(allkeys, tc.loc) + + proof, err := CreateNonMembershipProof(data, []byte(key)) + if err != nil { + t.Fatalf("Creating Proof: %+v", err) + } + if proof.GetNonexist() == nil { + t.Fatal("Unexpected proof format") + } + + root := CalcRoot(data) + err = proof.GetNonexist().Verify(TendermintSpec, root, []byte(key)) + if err != nil { + t.Fatalf("Verifying Proof: %+v", err) + } + + valid := ics23.VerifyNonMembership(TendermintSpec, root, proof, []byte(key)) + if !valid { + t.Fatalf("Non Membership Proof Invalid") + } + }) + } +} + +func TestInvalidKey(t *testing.T) { + tests := []struct { + name string + f func(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) + data map[string][]byte + key []byte + err error + }{ + {"CreateMembershipProof empty key", CreateMembershipProof, map[string][]byte{"": nil}, []byte(""), ErrEmptyKey}, + {"CreateMembershipProof empty key in data", CreateMembershipProof, map[string][]byte{"": nil, " ": nil}, []byte(" "), ErrEmptyKeyInData}, + {"CreateNonMembershipProof empty key", CreateNonMembershipProof, map[string][]byte{" ": nil}, []byte(""), ErrEmptyKey}, + {"CreateNonMembershipProof empty key in data", CreateNonMembershipProof, map[string][]byte{"": nil}, []byte(" "), ErrEmptyKeyInData}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.f(tc.data, tc.key) + assert.True(t, errors.Is(err, tc.err)) + }) + } +} diff --git a/store/internal/proofs/helpers.go b/store/internal/proofs/helpers.go new file mode 100644 index 000000000000..98b04ef5eff7 --- /dev/null +++ b/store/internal/proofs/helpers.go @@ -0,0 +1,105 @@ +package proofs + +import ( + "sort" + + "github.com/tendermint/tendermint/libs/rand" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" +) + +// SimpleResult contains a merkle.SimpleProof along with all data needed to build the confio/proof +type SimpleResult struct { + Key []byte + Value []byte + Proof *tmcrypto.Proof + RootHash []byte +} + +// GenerateRangeProof makes a tree of size and returns a range proof for one random element +// +// returns a range proof and the root hash of the tree +func GenerateRangeProof(size int, loc Where) *SimpleResult { + data := BuildMap(size) + root, proofs, allkeys := sdkmaps.ProofsFromMap(data) + + key := GetKey(allkeys, loc) + proof := proofs[key] + + res := &SimpleResult{ + Key: []byte(key), + Value: toValue(key), + Proof: proof, + RootHash: root, + } + return res +} + +// Where selects a location for a key - Left, Right, or Middle +type Where int + +const ( + Left Where = iota + Right + Middle +) + +func SortedKeys(data map[string][]byte) []string { + keys := make([]string, len(data)) + i := 0 + for k := range data { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +func CalcRoot(data map[string][]byte) []byte { + root, _, _ := sdkmaps.ProofsFromMap(data) + return root +} + +// GetKey this returns a key, on Left/Right/Middle +func GetKey(allkeys []string, loc Where) string { + if loc == Left { + return allkeys[0] + } + if loc == Right { + return allkeys[len(allkeys)-1] + } + // select a random index between 1 and allkeys-2 + idx := rand.Int()%(len(allkeys)-2) + 1 + return allkeys[idx] +} + +// GetNonKey returns a missing key - Left of all, Right of all, or in the Middle +func GetNonKey(allkeys []string, loc Where) string { + if loc == Left { + return string([]byte{1, 1, 1, 1}) + } + if loc == Right { + return string([]byte{0xff, 0xff, 0xff, 0xff}) + } + // otherwise, next to an existing key (copy before mod) + key := GetKey(allkeys, loc) + key = key[:len(key)-2] + string([]byte{255, 255}) + return key +} + +func toValue(key string) []byte { + return []byte("value_for_" + key) +} + +// BuildMap creates random key/values and stores in a map, +// returns a list of all keys in sorted order +func BuildMap(size int) map[string][]byte { + data := make(map[string][]byte) + // insert lots of info and store the bytes + for i := 0; i < size; i++ { + key := rand.Str(20) + data[key] = toValue(key) + } + return data +} diff --git a/store/list/list.go b/store/list/list.go deleted file mode 100644 index 96193adf85d9..000000000000 --- a/store/list/list.go +++ /dev/null @@ -1,105 +0,0 @@ -package list - -import ( - "fmt" - "strconv" - - "github.com/cosmos/cosmos-sdk/codec" - - "github.com/cosmos/cosmos-sdk/store/types" -) - -// Key for the length of the list -func LengthKey() []byte { - return []byte{0x00} -} - -// Key for the elements of the list -func ElemKey(index uint64) []byte { - return append([]byte{0x01}, []byte(fmt.Sprintf("%020d", index))...) -} - -// List defines an integer indexable mapper -// It panics when the element type cannot be (un/)marshalled by the codec -type List struct { - cdc *codec.Codec - store types.KVStore -} - -// NewList constructs new List -func NewList(cdc *codec.Codec, store types.KVStore) List { - return List{ - cdc: cdc, - store: store, - } -} - -// Len() returns the length of the list -// The length is only increased by Push() and not decreased -// List dosen't check if an index is in bounds -// The user should check Len() before doing any actions -func (m List) Len() (res uint64) { - bz := m.store.Get(LengthKey()) - if bz == nil { - return 0 - } - - m.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) - return -} - -// Get() returns the element by its index -func (m List) Get(index uint64, ptr interface{}) error { - bz := m.store.Get(ElemKey(index)) - return m.cdc.UnmarshalBinaryLengthPrefixed(bz, ptr) -} - -// Set() stores the element to the given position -// Setting element out of range will break length counting -// Use Push() instead of Set() to append a new element -func (m List) Set(index uint64, value interface{}) { - bz := m.cdc.MustMarshalBinaryLengthPrefixed(value) - m.store.Set(ElemKey(index), bz) -} - -// Delete() deletes the element in the given position -// Other elements' indices are preserved after deletion -// Panics when the index is out of range -func (m List) Delete(index uint64) { - m.store.Delete(ElemKey(index)) -} - -// Push() inserts the element to the end of the list -// It will increase the length when it is called -func (m List) Push(value interface{}) { - length := m.Len() - m.Set(length, value) - m.store.Set(LengthKey(), m.cdc.MustMarshalBinaryLengthPrefixed(length+1)) -} - -// Iterate() is used to iterate over all existing elements in the list -// Return true in the continuation to break -// The second element of the continuation will indicate the position of the element -// Using it with Get() will return the same one with the provided element - -// CONTRACT: No writes may happen within a domain while iterating over it. -func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { - iter := types.KVStorePrefixIterator(m.store, []byte{0x01}) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - v := iter.Value() - m.cdc.MustUnmarshalBinaryLengthPrefixed(v, ptr) - - k := iter.Key() - s := string(k[len(k)-20:]) - - index, err := strconv.ParseUint(s, 10, 64) - if err != nil { - panic(err) - } - - if fn(index) { - break - } - } -} diff --git a/store/list/list_test.go b/store/list/list_test.go deleted file mode 100644 index 88eccd6c144b..000000000000 --- a/store/list/list_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package list - -import ( - "math/rand" - "testing" - - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type TestStruct struct { - I uint64 - B bool -} - -func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { - db := dbm.NewMemDB() - cms := rootmulti.NewStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - return ctx, cdc -} -func TestList(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - lm := NewList(cdc, store) - - val := TestStruct{1, true} - var res TestStruct - - lm.Push(val) - require.Equal(t, uint64(1), lm.Len()) - lm.Get(uint64(0), &res) - require.Equal(t, val, res) - - val = TestStruct{2, false} - lm.Set(uint64(0), val) - lm.Get(uint64(0), &res) - require.Equal(t, val, res) - - val = TestStruct{100, false} - lm.Push(val) - require.Equal(t, uint64(2), lm.Len()) - lm.Get(uint64(1), &res) - require.Equal(t, val, res) - - lm.Delete(uint64(1)) - require.Equal(t, uint64(2), lm.Len()) - - lm.Iterate(&res, func(index uint64) (brk bool) { - var temp TestStruct - lm.Get(index, &temp) - require.Equal(t, temp, res) - - require.True(t, index != 1) - return - }) - - lm.Iterate(&res, func(index uint64) (brk bool) { - lm.Set(index, TestStruct{res.I + 1, !res.B}) - return - }) - - lm.Get(uint64(0), &res) - require.Equal(t, TestStruct{3, true}, res) -} - -func TestListRandom(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - list := NewList(cdc, store) - mocklist := []uint32{} - - for i := 0; i < 100; i++ { - item := rand.Uint32() - list.Push(item) - mocklist = append(mocklist, item) - } - - for k, v := range mocklist { - k := k - var i uint32 - require.NotPanics(t, func() { list.Get(uint64(k), &i) }) - require.Equal(t, v, i) - } -} diff --git a/store/mem/mem_test.go b/store/mem/mem_test.go new file mode 100644 index 000000000000..cff4c37da7dc --- /dev/null +++ b/store/mem/mem_test.go @@ -0,0 +1,39 @@ +package mem_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/mem" + "github.com/cosmos/cosmos-sdk/store/types" +) + +func TestStore(t *testing.T) { + db := mem.NewStore() + key, value := []byte("key"), []byte("value") + + require.Equal(t, types.StoreTypeMemory, db.GetStoreType()) + + require.Nil(t, db.Get(key)) + db.Set(key, value) + require.Equal(t, value, db.Get(key)) + + newValue := []byte("newValue") + db.Set(key, newValue) + require.Equal(t, newValue, db.Get(key)) + + db.Delete(key) + require.Nil(t, db.Get(key)) +} + +func TestCommit(t *testing.T) { + db := mem.NewStore() + key, value := []byte("key"), []byte("value") + + db.Set(key, value) + id := db.Commit() + require.True(t, id.IsZero()) + require.True(t, db.LastCommitID().IsZero()) + require.Equal(t, value, db.Get(key)) +} diff --git a/store/mem/store.go b/store/mem/store.go new file mode 100644 index 000000000000..66591b645f59 --- /dev/null +++ b/store/mem/store.go @@ -0,0 +1,57 @@ +package mem + +import ( + "io" + + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" +) + +var ( + _ types.KVStore = (*Store)(nil) + _ types.Committer = (*Store)(nil) +) + +// Store implements an in-memory only KVStore. Entries are persisted between +// commits and thus between blocks. State in Memory store is not committed as part of app state but maintained privately by each node +type Store struct { + dbadapter.Store +} + +func NewStore() *Store { + return NewStoreWithDB(dbm.NewMemDB()) +} + +func NewStoreWithDB(db *dbm.MemDB) *Store { // nolint: interfacer + return &Store{Store: dbadapter.Store{DB: db}} +} + +// GetStoreType returns the Store's type. +func (s Store) GetStoreType() types.StoreType { + return types.StoreTypeMemory +} + +// CacheWrap branches the underlying store. +func (s Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(s) +} + +// CacheWrapWithTrace implements KVStore. +func (s Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(s, w, tc)) +} + +// Commit performs a no-op as entries are persistent between commitments. +func (s *Store) Commit() (id types.CommitID) { return } + +func (s *Store) SetPruning(pruning types.PruningOptions) {} + +// GetPruning is a no-op as pruning options cannot be directly set on this store. +// They must be set on the root commit multi-store. +func (s *Store) GetPruning() types.PruningOptions { return types.PruningOptions{} } + +func (s Store) LastCommitID() (id types.CommitID) { return } diff --git a/store/prefix/store.go b/store/prefix/store.go index 81a42e42f003..4f9d5a75e087 100644 --- a/store/prefix/store.go +++ b/store/prefix/store.go @@ -97,7 +97,7 @@ func (s Store) Iterator(start, end []byte) types.Iterator { return newPrefixIterator(s.prefix, start, end, iter) } -// Implements KVStore +// ReverseIterator implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L129 func (s Store) ReverseIterator(start, end []byte) types.Iterator { newstart := cloneAppend(s.prefix, start) @@ -117,10 +117,11 @@ func (s Store) ReverseIterator(start, end []byte) types.Iterator { var _ types.Iterator = (*prefixIterator)(nil) type prefixIterator struct { - prefix []byte - start, end []byte - iter types.Iterator - valid bool + prefix []byte + start []byte + end []byte + iter types.Iterator + valid bool } func newPrefixIterator(prefix, start, end []byte, parent types.Iterator) *prefixIterator { @@ -134,53 +135,57 @@ func newPrefixIterator(prefix, start, end []byte, parent types.Iterator) *prefix } // Implements Iterator -func (iter *prefixIterator) Domain() ([]byte, []byte) { - return iter.start, iter.end +func (pi *prefixIterator) Domain() ([]byte, []byte) { + return pi.start, pi.end } // Implements Iterator -func (iter *prefixIterator) Valid() bool { - return iter.valid && iter.iter.Valid() +func (pi *prefixIterator) Valid() bool { + return pi.valid && pi.iter.Valid() } // Implements Iterator -func (iter *prefixIterator) Next() { - if !iter.valid { +func (pi *prefixIterator) Next() { + if !pi.valid { panic("prefixIterator invalid, cannot call Next()") } - iter.iter.Next() - if !iter.iter.Valid() || !bytes.HasPrefix(iter.iter.Key(), iter.prefix) { - iter.valid = false + + if pi.iter.Next(); !pi.iter.Valid() || !bytes.HasPrefix(pi.iter.Key(), pi.prefix) { + // TODO: shouldn't pi be set to nil instead? + pi.valid = false } } // Implements Iterator -func (iter *prefixIterator) Key() (key []byte) { - if !iter.valid { +func (pi *prefixIterator) Key() (key []byte) { + if !pi.valid { panic("prefixIterator invalid, cannot call Key()") } - key = iter.iter.Key() - key = stripPrefix(key, iter.prefix) + + key = pi.iter.Key() + key = stripPrefix(key, pi.prefix) + return } // Implements Iterator -func (iter *prefixIterator) Value() []byte { - if !iter.valid { +func (pi *prefixIterator) Value() []byte { + if !pi.valid { panic("prefixIterator invalid, cannot call Value()") } - return iter.iter.Value() + + return pi.iter.Value() } // Implements Iterator -func (iter *prefixIterator) Close() { - iter.iter.Close() +func (pi *prefixIterator) Close() error { + return pi.iter.Close() } // Error returns an error if the prefixIterator is invalid defined by the Valid // method. -func (iter *prefixIterator) Error() error { - if !iter.Valid() { +func (pi *prefixIterator) Error() error { + if !pi.Valid() { return errors.New("invalid prefixIterator") } @@ -192,6 +197,7 @@ func stripPrefix(key []byte, prefix []byte) []byte { if len(key) < len(prefix) || !bytes.Equal(key[:len(prefix)], prefix) { panic("should not happen") } + return key[len(prefix):] } diff --git a/store/prefix/store_test.go b/store/prefix/store_test.go index 427423340d8f..b6f32b45f961 100644 --- a/store/prefix/store_test.go +++ b/store/prefix/store_test.go @@ -4,23 +4,21 @@ import ( "crypto/rand" "testing" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + + tiavl "github.com/cosmos/iavl" + "github.com/cosmos/cosmos-sdk/store/dbadapter" "github.com/cosmos/cosmos-sdk/store/gaskv" "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/stretchr/testify/require" - - tiavl "github.com/tendermint/iavl" - dbm "github.com/tendermint/tm-db" ) // copied from iavl/store_test.go var ( - cacheSize = 100 - numRecent int64 = 5 - storeEvery int64 = 3 + cacheSize = 100 ) func bz(s string) []byte { return []byte(s) } @@ -35,9 +33,11 @@ func genRandomKVPairs(t *testing.T) []kvpair { for i := 0; i < 20; i++ { kvps[i].key = make([]byte, 32) - rand.Read(kvps[i].key) + _, err := rand.Read(kvps[i].key) + require.NoError(t, err) kvps[i].value = make([]byte, 32) - rand.Read(kvps[i].value) + _, err = rand.Read(kvps[i].value) + require.NoError(t, err) } return kvps @@ -90,7 +90,7 @@ func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() tree, err := tiavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := iavl.UnsafeNewStore(tree, types.PruneNothing) + iavlStore := iavl.UnsafeNewStore(tree) testPrefixStore(t, iavlStore, []byte("test")) } @@ -246,7 +246,6 @@ func mockStoreWithStuff() types.KVStore { store.Set(bz("key2"), bz("value2")) store.Set(bz("key3"), bz("value3")) store.Set(bz("something"), bz("else")) - store.Set(bz(""), bz("")) store.Set(bz("k"), bz(sdk.PrefixValidator)) store.Set(bz("ke"), bz("valu")) store.Set(bz("kee"), bz("valuu")) diff --git a/store/queue/queue.go b/store/queue/queue.go deleted file mode 100644 index 0f65d0375ba3..000000000000 --- a/store/queue/queue.go +++ /dev/null @@ -1,93 +0,0 @@ -package store - -// TODO: make it independent from list -/* -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/cosmos-sdk/store/list" -) - -// Key for the top element position in the queue -func TopKey() []byte { - return []byte{0x02} -} - -// Queue is a List wrapper that provides queue-like functions -// It panics when the element type cannot be (un/)marshalled by the codec -type Queue struct { - List list.List -} - -// NewQueue constructs new Queue -func NewQueue(cdc *codec.Codec, store sdk.KVStore) Queue { - return Queue{NewList(cdc, store)} -} - -func (m Queue) getTop() (res uint64) { - bz := m.List.store.Get(TopKey()) - if bz == nil { - return 0 - } - - m.List.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res) - return -} - -func (m Queue) setTop(top uint64) { - bz := m.List.cdc.MustMarshalBinaryLengthPrefixed(top) - m.List.store.Set(TopKey(), bz) -} - -// Push() inserts the elements to the rear of the queue -func (m Queue) Push(value interface{}) { - m.List.Push(value) -} - -// Popping/Peeking on an empty queue will cause panic -// The user should check IsEmpty() before doing any actions -// Peek() returns the element at the front of the queue without removing it -func (m Queue) Peek(ptr interface{}) error { - top := m.getTop() - return m.List.Get(top, ptr) -} - -// Pop() returns the element at the front of the queue and removes it -func (m Queue) Pop() { - top := m.getTop() - m.List.Delete(top) - m.setTop(top + 1) -} - -// IsEmpty() checks if the queue is empty -func (m Queue) IsEmpty() bool { - top := m.getTop() - length := m.List.Len() - return top >= length -} - -// Flush() removes elements it processed -// Return true in the continuation to break -// The interface{} is unmarshalled before the continuation is called -// Starts from the top(head) of the queue -// CONTRACT: Pop() or Push() should not be performed while flushing -func (m Queue) Flush(ptr interface{}, fn func() bool) { - top := m.getTop() - length := m.List.Len() - - var i uint64 - for i = top; i < length; i++ { - err := m.List.Get(i, ptr) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - m.List.Delete(i) - if fn() { - break - } - } - m.setTop(i) -} -*/ diff --git a/store/reexport.go b/store/reexport.go index 913a8c55a8b3..8a365ab758fd 100644 --- a/store/reexport.go +++ b/store/reexport.go @@ -2,11 +2,9 @@ package store import ( "github.com/cosmos/cosmos-sdk/store/types" - stypes "github.com/cosmos/cosmos-sdk/store/types" ) // Import cosmos-sdk/types/store.go for convenience. -// nolint type ( PruningOptions = types.PruningOptions Store = types.Store @@ -23,18 +21,11 @@ type ( CacheWrapper = types.CacheWrapper CacheWrap = types.CacheWrap CommitID = types.CommitID - StoreKey = types.StoreKey - StoreType = types.StoreType + Key = types.StoreKey + Type = types.StoreType Queryable = types.Queryable TraceContext = types.TraceContext - Gas = stypes.Gas + Gas = types.Gas GasMeter = types.GasMeter - GasConfig = stypes.GasConfig -) - -// nolint - reexport -var ( - PruneNothing = types.PruneNothing - PruneEverything = types.PruneEverything - PruneSyncable = types.PruneSyncable + GasConfig = types.GasConfig ) diff --git a/store/rootmulti/dbadapter.go b/store/rootmulti/dbadapter.go index cda6e62ba721..4d6e5afeb875 100644 --- a/store/rootmulti/dbadapter.go +++ b/store/rootmulti/dbadapter.go @@ -31,3 +31,7 @@ func (cdsa commitDBStoreAdapter) LastCommitID() types.CommitID { } func (cdsa commitDBStoreAdapter) SetPruning(_ types.PruningOptions) {} + +// GetPruning is a no-op as pruning options cannot be directly set on this store. +// They must be set on the root commit multi-store. +func (cdsa commitDBStoreAdapter) GetPruning() types.PruningOptions { return types.PruningOptions{} } diff --git a/store/rootmulti/proof.go b/store/rootmulti/proof.go index ad090b706a63..fc8925b7f20d 100644 --- a/store/rootmulti/proof.go +++ b/store/rootmulti/proof.go @@ -1,31 +1,10 @@ package rootmulti import ( - "bytes" - "errors" - "fmt" - - "github.com/tendermint/iavl" "github.com/tendermint/tendermint/crypto/merkle" -) -// MultiStoreProof defines a collection of store proofs in a multi-store -type MultiStoreProof struct { - StoreInfos []storeInfo -} - -func NewMultiStoreProof(storeInfos []storeInfo) *MultiStoreProof { - return &MultiStoreProof{StoreInfos: storeInfos} -} - -// ComputeRootHash returns the root hash for a given multi-store proof. -func (proof *MultiStoreProof) ComputeRootHash() []byte { - ci := commitInfo{ - Version: -1, // TODO: Not needed; improve code. - StoreInfos: proof.StoreInfos, - } - return ci.Hash() -} + storetypes "github.com/cosmos/cosmos-sdk/store/types" +) // RequireProof returns whether proof is required for the subpath. func RequireProof(subpath string) bool { @@ -38,99 +17,11 @@ func RequireProof(subpath string) bool { //----------------------------------------------------------------------------- -var _ merkle.ProofOperator = MultiStoreProofOp{} - -// the multi-store proof operation constant value -const ProofOpMultiStore = "multistore" - -// TODO: document -type MultiStoreProofOp struct { - // Encoded in ProofOp.Key - key []byte - - // To encode in ProofOp.Data. - Proof *MultiStoreProof `json:"proof"` -} - -func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp { - return MultiStoreProofOp{ - key: key, - Proof: proof, - } -} - -// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a -// given proof operation. -func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) { - if pop.Type != ProofOpMultiStore { - return nil, fmt.Errorf("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore) - } - - // XXX: a bit strange as we'll discard this, but it works - var op MultiStoreProofOp - - err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op) - if err != nil { - return nil, fmt.Errorf("decoding ProofOp.Data into MultiStoreProofOp: %w", err) - } - - return NewMultiStoreProofOp(pop.Key, op.Proof), nil -} - -// ProofOp return a merkle proof operation from a given multi-store proof -// operation. -func (op MultiStoreProofOp) ProofOp() merkle.ProofOp { - bz := cdc.MustMarshalBinaryLengthPrefixed(op) - return merkle.ProofOp{ - Type: ProofOpMultiStore, - Key: op.key, - Data: bz, - } -} - -// String implements the Stringer interface for a mult-store proof operation. -func (op MultiStoreProofOp) String() string { - return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey()) -} - -// GetKey returns the key for a multi-store proof operation. -func (op MultiStoreProofOp) GetKey() []byte { - return op.key -} - -// Run executes a multi-store proof operation for a given value. It returns -// the root hash if the value matches all the store's commitID's hash or an -// error otherwise. -func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) { - if len(args) != 1 { - return nil, errors.New("value size is not 1") - } - - value := args[0] - root := op.Proof.ComputeRootHash() - - for _, si := range op.Proof.StoreInfos { - if si.Name == string(op.key) { - if bytes.Equal(value, si.Core.CommitID.Hash) { - return [][]byte{root}, nil - } - - return nil, fmt.Errorf("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value) - } - } - - return nil, fmt.Errorf("key %v not found in multistore proof", op.key) -} - -//----------------------------------------------------------------------------- - // XXX: This should be managed by the rootMultiStore which may want to register // more proof ops? func DefaultProofRuntime() (prt *merkle.ProofRuntime) { prt = merkle.NewProofRuntime() - prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) - prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.ValueOpDecoder) - prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.AbsenceOpDecoder) - prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) + prt.RegisterOpDecoder(storetypes.ProofOpIAVLCommitment, storetypes.CommitmentOpDecoder) + prt.RegisterOpDecoder(storetypes.ProofOpSimpleMerkleCommitment, storetypes.CommitmentOpDecoder) return } diff --git a/store/rootmulti/proof_test.go b/store/rootmulti/proof_test.go index 4d316f2c85aa..f0bd29063ad1 100644 --- a/store/rootmulti/proof_test.go +++ b/store/rootmulti/proof_test.go @@ -14,7 +14,7 @@ import ( func TestVerifyIAVLStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - iStore, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false) + iStore, err := iavl.LoadStore(db, types.CommitID{}, false) store := iStore.(*iavl.Store) require.Nil(t, err) store.Set([]byte("MYKEY"), []byte("MYVALUE")) @@ -26,31 +26,31 @@ func TestVerifyIAVLStoreQueryProof(t *testing.T) { Data: []byte("MYKEY"), Prove: true, }) - require.NotNil(t, res.Proof) + require.NotNil(t, res.ProofOps) // Verify proof. prt := DefaultProofRuntime() - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY", []byte("MYVALUE")) require.Nil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY_NOT", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY_NOT", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY/MYKEY", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "MYKEY", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE_NOT")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY", []byte("MYVALUE_NOT")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte(nil)) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY", []byte(nil)) require.NotNil(t, err) } @@ -73,68 +73,39 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { Data: []byte("MYKEY"), Prove: true, }) - require.NotNil(t, res.Proof) + require.NotNil(t, res.ProofOps) // Verify proof. prt := DefaultProofRuntime() - err := prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + err := prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) require.Nil(t, err) // Verify proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) require.Nil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY_NOT", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY_NOT", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY/MYKEY", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "iavlStoreKey/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "iavlStoreKey/MYKEY", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/MYKEY", []byte("MYVALUE")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE_NOT")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE_NOT")) require.NotNil(t, err) // Verify (bad) proof. - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) - require.NotNil(t, err) -} - -func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { - // Create main tree for testing. - db := dbm.NewMemDB() - store := NewStore(db) - iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - - store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) - store.LoadVersion(0) - cid := store.Commit() // Commit with empty iavl store. - - // Get Proof - res := store.Query(abci.RequestQuery{ - Path: "/iavlStoreKey/key", // required path to get key/value+proof - Data: []byte("MYKEY"), - Prove: true, - }) - require.NotNil(t, res.Proof) - - // Verify proof. - prt := DefaultProofRuntime() - err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY") - require.Nil(t, err) - - // Verify (bad) proof. - prt = DefaultProofRuntime() - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYKEY", []byte("MYVALUE")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYKEY", []byte(nil)) require.NotNil(t, err) } @@ -145,7 +116,8 @@ func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) - store.LoadVersion(0) + err := store.LoadVersion(0) + require.NoError(t, err) iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) @@ -157,20 +129,20 @@ func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { Data: []byte("MYABSENTKEY"), Prove: true, }) - require.NotNil(t, res.Proof) + require.NotNil(t, res.ProofOps) // Verify proof. prt := DefaultProofRuntime() - err := prt.VerifyAbsence(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY") + err = prt.VerifyAbsence(res.ProofOps, cid.Hash, "/iavlStoreKey/MYABSENTKEY") require.Nil(t, err) // Verify (bad) proof. prt = DefaultProofRuntime() - err = prt.VerifyAbsence(res.Proof, cid.Hash, "/MYABSENTKEY") + err = prt.VerifyAbsence(res.ProofOps, cid.Hash, "/MYABSENTKEY") require.NotNil(t, err) // Verify (bad) proof. prt = DefaultProofRuntime() - err = prt.VerifyValue(res.Proof, cid.Hash, "/iavlStoreKey/MYABSENTKEY", []byte("")) + err = prt.VerifyValue(res.ProofOps, cid.Hash, "/iavlStoreKey/MYABSENTKEY", []byte("")) require.NotNil(t, err) } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 42c7b05ab589..536510914399 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -1,18 +1,28 @@ package rootmulti import ( + "bufio" + "compress/zlib" + "encoding/binary" "fmt" "io" + "math" + "sort" "strings" + iavltree "github.com/cosmos/iavl" + protoio "github.com/gogo/protobuf/io" + gogotypes "github.com/gogo/protobuf/types" + "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" - "github.com/tendermint/tendermint/crypto/tmhash" dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/snapshots" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/cachemulti" "github.com/cosmos/cosmos-sdk/store/dbadapter" "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/mem" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/transient" "github.com/cosmos/cosmos-sdk/store/types" @@ -21,20 +31,28 @@ import ( const ( latestVersionKey = "s/latest" + pruneHeightsKey = "s/pruneheights" commitInfoKeyFmt = "s/%d" // s/ + + // Do not change chunk size without new snapshot format (must be uniform across nodes) + snapshotChunkSize = uint64(10e6) + snapshotBufferSize = int(snapshotChunkSize) + snapshotMaxItemSize = int(64e6) // SDK has no key/value size limit, so we set an arbitrary limit ) // Store is composed of many CommitStores. Name contrasts with -// cacheMultiStore which is for cache-wrapping other MultiStores. It implements +// cacheMultiStore which is used for branching other MultiStores. It implements // the CommitMultiStore interface. type Store struct { db dbm.DB - lastCommitInfo commitInfo + lastCommitInfo *types.CommitInfo pruningOpts types.PruningOptions storesParams map[types.StoreKey]storeParams stores map[types.StoreKey]types.CommitKVStore keysByName map[string]types.StoreKey lazyLoading bool + pruneHeights []int64 + initialVersion int64 traceWriter io.Writer traceContext types.TraceContext @@ -42,8 +60,10 @@ type Store struct { interBlockCache types.MultiStorePersistentCache } -var _ types.CommitMultiStore = (*Store)(nil) -var _ types.Queryable = (*Store)(nil) +var ( + _ types.CommitMultiStore = (*Store)(nil) + _ types.Queryable = (*Store)(nil) +) // NewStore returns a reference to a new Store object with the provided DB. The // store will be created with a PruneNothing pruning strategy by default. After @@ -56,20 +76,20 @@ func NewStore(db dbm.DB) *Store { storesParams: make(map[types.StoreKey]storeParams), stores: make(map[types.StoreKey]types.CommitKVStore), keysByName: make(map[string]types.StoreKey), + pruneHeights: make([]int64, 0), } } +// GetPruning fetches the pruning strategy from the root store. +func (rs *Store) GetPruning() types.PruningOptions { + return rs.pruningOpts +} + // SetPruning sets the pruning strategy on the root store and all the sub-stores. // Note, calling SetPruning on the root store prior to LoadVersion or // LoadLatestVersion performs a no-op as the stores aren't mounted yet. -// -// TODO: Consider removing this API altogether on sub-stores as a pruning -// strategy should only be provided on initialization. func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { rs.pruningOpts = pruningOpts - for _, substore := range rs.stores { - substore.SetPruning(pruningOpts) - } } // SetLazyLoading sets if the iavl store should be loaded lazily or not @@ -77,21 +97,21 @@ func (rs *Store) SetLazyLoading(lazyLoading bool) { rs.lazyLoading = lazyLoading } -// Implements Store. +// GetStoreType implements Store. func (rs *Store) GetStoreType() types.StoreType { return types.StoreTypeMulti } -// Implements CommitMultiStore. +// MountStoreWithDB implements CommitMultiStore. func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db dbm.DB) { if key == nil { panic("MountIAVLStore() key cannot be nil") } if _, ok := rs.storesParams[key]; ok { - panic(fmt.Sprintf("Store duplicate store key %v", key)) + panic(fmt.Sprintf("store duplicate store key %v", key)) } if _, ok := rs.keysByName[key.Name()]; ok { - panic(fmt.Sprintf("Store duplicate store key name %v", key)) + panic(fmt.Sprintf("store duplicate store key name %v", key)) } rs.storesParams[key] = storeParams{ key: key, @@ -145,8 +165,9 @@ func (rs *Store) LoadVersion(ver int64) error { } func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { - infos := make(map[string]storeInfo) - var cInfo commitInfo + infos := make(map[string]types.StoreInfo) + + cInfo := &types.CommitInfo{} // load old data if we are not version 0 if ver != 0 { @@ -164,18 +185,26 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { // load each Store (note this doesn't panic on unmounted keys now) var newStores = make(map[types.StoreKey]types.CommitKVStore) + for key, storeParams := range rs.storesParams { - // Load it - store, err := rs.loadCommitStoreFromParams(key, rs.getCommitID(infos, key.Name()), storeParams) + commitID := rs.getCommitID(infos, key.Name()) + + // If it has been added, set the initial version + if upgrades.IsAdded(key.Name()) { + storeParams.initialVersion = uint64(ver) + 1 + } + + store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) if err != nil { - return fmt.Errorf("failed to load Store: %v", err) + return errors.Wrap(err, "failed to load store") } + newStores[key] = store // If it was deleted, remove all data if upgrades.IsDeleted(key.Name()) { if err := deleteKVStore(store.(types.KVStore)); err != nil { - return fmt.Errorf("failed to delete store %s: %v", key.Name(), err) + return errors.Wrapf(err, "failed to delete store %s", key.Name()) } } else if oldName := upgrades.RenamedFrom(key.Name()); oldName != "" { // handle renames specially @@ -187,12 +216,12 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { // load from the old name oldStore, err := rs.loadCommitStoreFromParams(oldKey, rs.getCommitID(infos, oldName), oldParams) if err != nil { - return fmt.Errorf("failed to load old Store '%s': %v", oldName, err) + return errors.Wrapf(err, "failed to load old store %s", oldName) } // move all data if err := moveKVStoreData(oldStore.(types.KVStore), store.(types.KVStore)); err != nil { - return fmt.Errorf("failed to move store %s -> %s: %v", oldName, key.Name(), err) + return errors.Wrapf(err, "failed to move store %s -> %s", oldName, key.Name()) } } } @@ -200,15 +229,22 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { rs.lastCommitInfo = cInfo rs.stores = newStores + // load any pruned heights we missed from disk to be pruned on the next run + ph, err := getPruningHeights(rs.db) + if err == nil && len(ph) > 0 { + rs.pruneHeights = ph + } + return nil } -func (rs *Store) getCommitID(infos map[string]storeInfo, name string) types.CommitID { +func (rs *Store) getCommitID(infos map[string]types.StoreInfo, name string) types.CommitID { info, ok := infos[name] if !ok { return types.CommitID{} } - return info.Core.CommitID + + return info.CommitId } func deleteKVStore(kv types.KVStore) error { @@ -276,35 +312,89 @@ func (rs *Store) TracingEnabled() bool { return rs.traceWriter != nil } -//---------------------------------------- -// +CommitStore - -// Implements Committer/CommitStore. +// LastCommitID implements Committer/CommitStore. func (rs *Store) LastCommitID() types.CommitID { + if rs.lastCommitInfo == nil { + return types.CommitID{ + Version: getLatestVersion(rs.db), + } + } + return rs.lastCommitInfo.CommitID() } -// Implements Committer/CommitStore. +// Commit implements Committer/CommitStore. func (rs *Store) Commit() types.CommitID { + var previousHeight, version int64 + if rs.lastCommitInfo.GetVersion() == 0 && rs.initialVersion > 1 { + // This case means that no commit has been made in the store, we + // start from initialVersion. + version = rs.initialVersion + + } else { + // This case can means two things: + // - either there was already a previous commit in the store, in which + // case we increment the version from there, + // - or there was no previous commit, and initial version was not set, + // in which case we start at version 1. + previousHeight = rs.lastCommitInfo.GetVersion() + version = previousHeight + 1 + } - // Commit stores. - version := rs.lastCommitInfo.Version + 1 rs.lastCommitInfo = commitStores(version, rs.stores) - // write CommitInfo to disk only if this version was flushed to disk - if rs.pruningOpts.FlushVersion(version) { - flushCommitInfo(rs.db, version, rs.lastCommitInfo) + // Determine if pruneHeight height needs to be added to the list of heights to + // be pruned, where pruneHeight = (commitHeight - 1) - KeepRecent. + if int64(rs.pruningOpts.KeepRecent) < previousHeight { + pruneHeight := previousHeight - int64(rs.pruningOpts.KeepRecent) + // We consider this height to be pruned iff: + // + // - KeepEvery is zero as that means that all heights should be pruned. + // - KeepEvery % (height - KeepRecent) != 0 as that means the height is not + // a 'snapshot' height. + if rs.pruningOpts.KeepEvery == 0 || pruneHeight%int64(rs.pruningOpts.KeepEvery) != 0 { + rs.pruneHeights = append(rs.pruneHeights, pruneHeight) + } } - // Prepare for next version. - commitID := types.CommitID{ + // batch prune if the current height is a pruning interval height + if rs.pruningOpts.Interval > 0 && version%int64(rs.pruningOpts.Interval) == 0 { + rs.pruneStores() + } + + flushMetadata(rs.db, version, rs.lastCommitInfo, rs.pruneHeights) + + return types.CommitID{ Version: version, Hash: rs.lastCommitInfo.Hash(), } - return commitID } -// Implements CacheWrapper/Store/CommitStore. +// pruneStores will batch delete a list of heights from each mounted sub-store. +// Afterwards, pruneHeights is reset. +func (rs *Store) pruneStores() { + if len(rs.pruneHeights) == 0 { + return + } + + for key, store := range rs.stores { + if store.GetStoreType() == types.StoreTypeIAVL { + // If the store is wrapped with an inter-block cache, we must first unwrap + // it to get the underlying IAVL store. + store = rs.GetCommitKVStore(key) + + if err := store.(*iavl.Store).DeleteVersions(rs.pruneHeights...); err != nil { + if errCause := errors.Cause(err); errCause != nil && errCause != iavltree.ErrVersionDoesNotExist { + panic(err) + } + } + } + } + + rs.pruneHeights = make([]int64, 0) +} + +// CacheWrap implements CacheWrapper/Store/CommitStore. func (rs *Store) CacheWrap() types.CacheWrap { return rs.CacheMultiStore().(types.CacheWrap) } @@ -314,10 +404,7 @@ func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.Cac return rs.CacheWrap() } -//---------------------------------------- -// +MultiStore - -// CacheMultiStore cache-wraps the multi-store and returns a CacheMultiStore. +// CacheMultiStore creates ephemeral branch of the multi-store and returns a CacheMultiStore. // It implements the MultiStore interface. func (rs *Store) CacheMultiStore() types.CacheMultiStore { stores := make(map[types.StoreKey]types.CacheWrapper) @@ -380,7 +467,11 @@ func (rs *Store) GetStore(key types.StoreKey) types.Store { // NOTE: The returned KVStore may be wrapped in an inter-block cache if it is // set on the root store. func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { - store := rs.stores[key].(types.KVStore) + s := rs.stores[key] + if s == nil { + panic(fmt.Sprintf("store does not exist for key: %s", key.Name())) + } + store := s.(types.KVStore) if rs.TracingEnabled() { store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) @@ -402,8 +493,6 @@ func (rs *Store) getStoreByName(name string) types.Store { return rs.GetCommitKVStore(key) } -//---------------------- Query ------------------ - // Query calls substore.Query with the same `req` where `req.Path` is // modified to remove the substore prefix. // Ie. `req.Path` here is `//`, and trimmed to `/` for the substore. @@ -433,14 +522,14 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { return res } - if res.Proof == nil || len(res.Proof.Ops) == 0 { + if res.ProofOps == nil || len(res.ProofOps.Ops) == 0 { return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proof is unexpectedly empty; ensure height has not been pruned")) } // If the request's height is the latest height we've committed, then utilize // the store's lastCommitInfo as this commit info may not be flushed to disk. // Otherwise, we query for the commit info from disk. - var commitInfo commitInfo + var commitInfo *types.CommitInfo if res.Height == rs.lastCommitInfo.Version { commitInfo = rs.lastCommitInfo @@ -452,16 +541,30 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { } // Restore origin path and append proof op. - res.Proof.Ops = append(res.Proof.Ops, NewMultiStoreProofOp( - []byte(storeName), - NewMultiStoreProof(commitInfo.StoreInfos), - ).ProofOp()) + res.ProofOps.Ops = append(res.ProofOps.Ops, commitInfo.ProofOp(storeName)) - // TODO: handle in another TM v0.26 update PR - // res.Proof = buildMultiStoreProof(res.Proof, storeName, commitInfo.StoreInfos) return res } +// SetInitialVersion sets the initial version of the IAVL tree. It is used when +// starting a new chain at an arbitrary height. +func (rs *Store) SetInitialVersion(version int64) error { + rs.initialVersion = version + + // Loop through all the stores, if it's an IAVL store, then set initial + // version on it. + for key, store := range rs.stores { + if store.GetStoreType() == types.StoreTypeIAVL { + // If the store is wrapped with an inter-block cache, we must first unwrap + // it to get the underlying IAVL store. + store = rs.GetCommitKVStore(key) + store.(*iavl.Store).SetInitialVersion(version) + } + } + + return nil +} + // parsePath expects a format like /[/] // Must start with /, subpath may be empty // Returns error if it doesn't start with / @@ -480,8 +583,237 @@ func parsePath(path string) (storeName string, subpath string, err error) { return storeName, subpath, nil } -//---------------------------------------- -// Note: why do we use key and params.key in different places. Seems like there should be only one key used. +//---------------------- Snapshotting ------------------ + +// Snapshot implements snapshottypes.Snapshotter. The snapshot output for a given format must be +// identical across nodes such that chunks from different sources fit together. If the output for a +// given format changes (at the byte level), the snapshot format must be bumped - see +// TestMultistoreSnapshot_Checksum test. +func (rs *Store) Snapshot(height uint64, format uint32) (<-chan io.ReadCloser, error) { + if format != snapshottypes.CurrentFormat { + return nil, sdkerrors.Wrapf(snapshottypes.ErrUnknownFormat, "format %v", format) + } + if height == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot snapshot height 0") + } + if height > uint64(rs.LastCommitID().Version) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot snapshot future height %v", height) + } + + // Collect stores to snapshot (only IAVL stores are supported) + type namedStore struct { + *iavl.Store + name string + } + stores := []namedStore{} + for key := range rs.stores { + switch store := rs.GetCommitKVStore(key).(type) { + case *iavl.Store: + stores = append(stores, namedStore{name: key.Name(), Store: store}) + case *transient.Store, *mem.Store: + // Non-persisted stores shouldn't be snapshotted + continue + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, + "don't know how to snapshot store %q of type %T", key.Name(), store) + } + } + sort.Slice(stores, func(i, j int) bool { + return strings.Compare(stores[i].name, stores[j].name) == -1 + }) + + // Spawn goroutine to generate snapshot chunks and pass their io.ReadClosers through a channel + ch := make(chan io.ReadCloser) + go func() { + // Set up a stream pipeline to serialize snapshot nodes: + // ExportNode -> delimited Protobuf -> zlib -> buffer -> chunkWriter -> chan io.ReadCloser + chunkWriter := snapshots.NewChunkWriter(ch, snapshotChunkSize) + defer chunkWriter.Close() + bufWriter := bufio.NewWriterSize(chunkWriter, snapshotBufferSize) + defer func() { + if err := bufWriter.Flush(); err != nil { + chunkWriter.CloseWithError(err) + } + }() + zWriter, err := zlib.NewWriterLevel(bufWriter, 7) + if err != nil { + chunkWriter.CloseWithError(sdkerrors.Wrap(err, "zlib failure")) + return + } + defer func() { + if err := zWriter.Close(); err != nil { + chunkWriter.CloseWithError(err) + } + }() + protoWriter := protoio.NewDelimitedWriter(zWriter) + defer func() { + if err := protoWriter.Close(); err != nil { + chunkWriter.CloseWithError(err) + } + }() + + // Export each IAVL store. Stores are serialized as a stream of SnapshotItem Protobuf + // messages. The first item contains a SnapshotStore with store metadata (i.e. name), + // and the following messages contain a SnapshotNode (i.e. an ExportNode). Store changes + // are demarcated by new SnapshotStore items. + for _, store := range stores { + exporter, err := store.Export(int64(height)) + if err != nil { + chunkWriter.CloseWithError(err) + return + } + defer exporter.Close() + err = protoWriter.WriteMsg(&types.SnapshotItem{ + Item: &types.SnapshotItem_Store{ + Store: &types.SnapshotStoreItem{ + Name: store.name, + }, + }, + }) + if err != nil { + chunkWriter.CloseWithError(err) + return + } + + for { + node, err := exporter.Next() + if err == iavltree.ExportDone { + break + } else if err != nil { + chunkWriter.CloseWithError(err) + return + } + err = protoWriter.WriteMsg(&types.SnapshotItem{ + Item: &types.SnapshotItem_IAVL{ + IAVL: &types.SnapshotIAVLItem{ + Key: node.Key, + Value: node.Value, + Height: int32(node.Height), + Version: node.Version, + }, + }, + }) + if err != nil { + chunkWriter.CloseWithError(err) + return + } + } + exporter.Close() + } + }() + + return ch, nil +} + +// Restore implements snapshottypes.Snapshotter. +func (rs *Store) Restore( + height uint64, format uint32, chunks <-chan io.ReadCloser, ready chan<- struct{}, +) error { + if format != snapshottypes.CurrentFormat { + return sdkerrors.Wrapf(snapshottypes.ErrUnknownFormat, "format %v", format) + } + if height == 0 { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot restore snapshot at height 0") + } + if height > uint64(math.MaxInt64) { + return sdkerrors.Wrapf(snapshottypes.ErrInvalidMetadata, + "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 + // reader reads from the stream on initialization, potentially causing deadlocks. + if ready != nil { + close(ready) + } + + // Set up a restore stream pipeline + // chan io.ReadCloser -> chunkReader -> zlib -> delimited Protobuf -> ExportNode + chunkReader := snapshots.NewChunkReader(chunks) + defer chunkReader.Close() + zReader, err := zlib.NewReader(chunkReader) + if err != nil { + return sdkerrors.Wrap(err, "zlib failure") + } + defer zReader.Close() + protoReader := protoio.NewDelimitedReader(zReader, snapshotMaxItemSize) + defer protoReader.Close() + + // Import nodes into stores. The first item is expected to be a SnapshotItem containing + // a SnapshotStoreItem, telling us which store to import into. The following items will contain + // SnapshotNodeItem (i.e. ExportNode) until we reach the next SnapshotStoreItem or EOF. + var importer *iavltree.Importer + for { + item := &types.SnapshotItem{} + err := protoReader.ReadMsg(item) + if err == io.EOF { + break + } else if err != nil { + return sdkerrors.Wrap(err, "invalid protobuf message") + } + + switch item := item.Item.(type) { + case *types.SnapshotItem_Store: + if importer != nil { + err = importer.Commit() + if err != nil { + return sdkerrors.Wrap(err, "IAVL commit failed") + } + importer.Close() + } + store, ok := rs.getStoreByName(item.Store.Name).(*iavl.Store) + if !ok || store == nil { + return sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot import into non-IAVL store %q", item.Store.Name) + } + importer, err = store.Import(int64(height)) + if err != nil { + return sdkerrors.Wrap(err, "import failed") + } + defer importer.Close() + + case *types.SnapshotItem_IAVL: + if importer == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "received IAVL node item before store item") + } + if item.IAVL.Height > math.MaxInt8 { + return sdkerrors.Wrapf(sdkerrors.ErrLogic, "node height %v cannot exceed %v", + item.IAVL.Height, math.MaxInt8) + } + node := &iavltree.ExportNode{ + Key: item.IAVL.Key, + Value: item.IAVL.Value, + Height: int8(item.IAVL.Height), + Version: item.IAVL.Version, + } + // Protobuf does not differentiate between []byte{} as nil, but fortunately IAVL does + // not allow nil keys nor nil values for leaf nodes, so we can always set them to empty. + if node.Key == nil { + node.Key = []byte{} + } + if node.Height == 0 && node.Value == nil { + node.Value = []byte{} + } + err := importer.Add(node) + if err != nil { + return sdkerrors.Wrap(err, "IAVL node import failed") + } + + default: + return sdkerrors.Wrapf(sdkerrors.ErrLogic, "unknown snapshot item %T", item) + } + } + + if importer != nil { + err := importer.Commit() + if err != nil { + return sdkerrors.Wrap(err, "IAVL commit failed") + } + importer.Close() + } + + flushMetadata(rs.db, int64(height), rs.buildCommitInfo(int64(height)), []int64{}) + return rs.LoadLatestVersion() +} + func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (types.CommitKVStore, error) { var db dbm.DB @@ -497,7 +829,15 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID panic("recursive MultiStores not yet supported") case types.StoreTypeIAVL: - store, err := iavl.LoadStore(db, id, rs.pruningOpts, rs.lazyLoading) + var store types.CommitKVStore + var err error + + if params.initialVersion == 0 { + store, err = iavl.LoadStore(db, id, rs.lazyLoading) + } else { + store, err = iavl.LoadStoreWithInitialVersion(db, id, rs.lazyLoading, params.initialVersion) + } + if err != nil { return nil, err } @@ -505,7 +845,7 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID if rs.interBlockCache != nil { // Wrap and get a CommitKVStore with inter-block caching. Note, this should // only wrap the primary CommitKVStore, not any store that is already - // cache-wrapped as that will create unexpected behavior. + // branched as that will create unexpected behavior. store = rs.interBlockCache.GetStoreCache(key, store) } @@ -522,113 +862,62 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID return transient.NewStore(), nil + case types.StoreTypeMemory: + if _, ok := key.(*types.MemoryStoreKey); !ok { + return nil, fmt.Errorf("unexpected key type for a MemoryStoreKey; got: %s", key.String()) + } + + return mem.NewStore(), nil + default: panic(fmt.Sprintf("unrecognized store type %v", params.typ)) } } -//---------------------------------------- -// storeParams - -type storeParams struct { - key types.StoreKey - db dbm.DB - typ types.StoreType -} - -//---------------------------------------- -// commitInfo - -// NOTE: Keep commitInfo a simple immutable struct. -type commitInfo struct { - - // Version - Version int64 - - // Store info for - StoreInfos []storeInfo -} - -// Hash returns the simple merkle root hash of the stores sorted by name. -func (ci commitInfo) Hash() []byte { - // TODO: cache to ci.hash []byte - m := make(map[string][]byte, len(ci.StoreInfos)) - for _, storeInfo := range ci.StoreInfos { - m[storeInfo.Name] = storeInfo.Hash() +func (rs *Store) buildCommitInfo(version int64) *types.CommitInfo { + storeInfos := []types.StoreInfo{} + for key, store := range rs.stores { + if store.GetStoreType() == types.StoreTypeTransient { + continue + } + storeInfos = append(storeInfos, types.StoreInfo{ + Name: key.Name(), + CommitId: store.LastCommitID(), + }) } - - return merkle.SimpleHashFromMap(m) -} - -func (ci commitInfo) CommitID() types.CommitID { - return types.CommitID{ - Version: ci.Version, - Hash: ci.Hash(), + return &types.CommitInfo{ + Version: version, + StoreInfos: storeInfos, } } -//---------------------------------------- -// storeInfo - -// storeInfo contains the name and core reference for an -// underlying store. It is the leaf of the Stores top -// level simple merkle tree. -type storeInfo struct { - Name string - Core storeCore -} - -type storeCore struct { - // StoreType StoreType - CommitID types.CommitID - // ... maybe add more state -} - -// Implements merkle.Hasher. -func (si storeInfo) Hash() []byte { - // Doesn't write Name, since merkle.SimpleHashFromMap() will - // include them via the keys. - bz := si.Core.CommitID.Hash - hasher := tmhash.New() - - _, err := hasher.Write(bz) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - - return hasher.Sum(nil) +type storeParams struct { + key types.StoreKey + db dbm.DB + typ types.StoreType + initialVersion uint64 } -//---------------------------------------- -// Misc. - func getLatestVersion(db dbm.DB) int64 { - var latest int64 - latestBytes, err := db.Get([]byte(latestVersionKey)) + bz, err := db.Get([]byte(latestVersionKey)) if err != nil { panic(err) - } else if latestBytes == nil { + } else if bz == nil { return 0 } - err = cdc.UnmarshalBinaryLengthPrefixed(latestBytes, &latest) - if err != nil { + var latestVersion int64 + + if err := gogotypes.StdInt64Unmarshal(&latestVersion, bz); err != nil { panic(err) } - return latest -} - -// Set the latest version. -func setLatestVersion(batch dbm.Batch, version int64) { - latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version) - batch.Set([]byte(latestVersionKey), latestBytes) + return latestVersion } // Commits each store and returns a new commitInfo. -func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) commitInfo { - storeInfos := make([]storeInfo, 0, len(storeMap)) +func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) *types.CommitInfo { + storeInfos := make([]types.StoreInfo, 0, len(storeMap)) for key, store := range storeMap { commitID := store.Commit() @@ -637,54 +926,96 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore continue } - si := storeInfo{} + si := types.StoreInfo{} si.Name = key.Name() - si.Core.CommitID = commitID + si.CommitId = commitID storeInfos = append(storeInfos, si) } - return commitInfo{ + return &types.CommitInfo{ Version: version, StoreInfos: storeInfos, } } // Gets commitInfo from disk. -func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { - - // Get from DB. +func getCommitInfo(db dbm.DB, ver int64) (*types.CommitInfo, error) { cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) - cInfoBytes, err := db.Get([]byte(cInfoKey)) + + bz, err := db.Get([]byte(cInfoKey)) if err != nil { - return commitInfo{}, fmt.Errorf("failed to get commit info: %v", err) - } else if cInfoBytes == nil { - return commitInfo{}, fmt.Errorf("failed to get commit info: no data") + return nil, errors.Wrap(err, "failed to get commit info") + } else if bz == nil { + return nil, errors.New("no commit info found") } - var cInfo commitInfo - - err = cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) - if err != nil { - return commitInfo{}, fmt.Errorf("failed to get Store: %v", err) + cInfo := &types.CommitInfo{} + if err = cInfo.Unmarshal(bz); err != nil { + return nil, errors.Wrap(err, "failed unmarshal commit info") } return cInfo, nil } -// Set a commitInfo for given version. -func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { - cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo) +func setCommitInfo(batch dbm.Batch, version int64, cInfo *types.CommitInfo) { + bz, err := cInfo.Marshal() + if err != nil { + panic(err) + } + cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) - batch.Set([]byte(cInfoKey), cInfoBytes) + batch.Set([]byte(cInfoKey), bz) +} + +func setLatestVersion(batch dbm.Batch, version int64) { + bz, err := gogotypes.StdInt64Marshal(version) + if err != nil { + panic(err) + } + + batch.Set([]byte(latestVersionKey), bz) } -// flushCommitInfo flushes a commitInfo for given version to the DB. Note, this -// needs to happen atomically. -func flushCommitInfo(db dbm.DB, version int64, cInfo commitInfo) { +func setPruningHeights(batch dbm.Batch, pruneHeights []int64) { + bz := make([]byte, 0) + for _, ph := range pruneHeights { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(ph)) + bz = append(bz, buf...) + } + + batch.Set([]byte(pruneHeightsKey), bz) +} + +func getPruningHeights(db dbm.DB) ([]int64, error) { + bz, err := db.Get([]byte(pruneHeightsKey)) + if err != nil { + return nil, fmt.Errorf("failed to get pruned heights: %w", err) + } + if len(bz) == 0 { + return nil, errors.New("no pruned heights found") + } + + prunedHeights := make([]int64, len(bz)/8) + i, offset := 0, 0 + for offset < len(bz) { + prunedHeights[i] = int64(binary.BigEndian.Uint64(bz[offset : offset+8])) + i++ + offset += 8 + } + + return prunedHeights, nil +} + +func flushMetadata(db dbm.DB, version int64, cInfo *types.CommitInfo, pruneHeights []int64) { batch := db.NewBatch() defer batch.Close() setCommitInfo(batch, version, cInfo) setLatestVersion(batch, version) - batch.Write() + setPruningHeights(batch, pruneHeights) + + if err := batch.Write(); err != nil { + panic(fmt.Errorf("error on batch write %w", err)) + } } diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 164f231a2ac2..4b4028c27237 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -1,15 +1,24 @@ package rootmulti import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "errors" "fmt" + "io" + "io/ioutil" + "math/rand" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tm-db" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store/iavl" + sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" "github.com/cosmos/cosmos-sdk/store/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -22,7 +31,7 @@ func TestStoreType(t *testing.T) { func TestGetCommitKVStore(t *testing.T) { var db dbm.DB = dbm.NewMemDB() - ms := newMultiStoreWithMounts(db, types.PruneSyncable) + ms := newMultiStoreWithMounts(db, types.PruneDefault) err := ms.LoadLatestVersion() require.Nil(t, err) @@ -49,6 +58,7 @@ func TestStoreMount(t *testing.T) { require.NotPanics(t, func() { store.MountStoreWithDB(key2, types.StoreTypeIAVL, db) }) require.Panics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.Panics(t, func() { store.MountStoreWithDB(nil, types.StoreTypeIAVL, db) }) require.Panics(t, func() { store.MountStoreWithDB(dup1, types.StoreTypeIAVL, db) }) } @@ -69,9 +79,9 @@ func TestCacheMultiStoreWithVersion(t *testing.T) { cID := ms.Commit() require.Equal(t, int64(1), cID.Version) - // require failure when given an invalid or pruned version + // require no failure when given an invalid or pruned version _, err = ms.CacheMultiStoreWithVersion(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) // require a valid version can be cache-loaded cms, err := ms.CacheMultiStoreWithVersion(cID.Version) @@ -158,19 +168,6 @@ func TestMultistoreCommitLoad(t *testing.T) { require.Nil(t, err) commitID = getExpectedCommitID(store, ver) checkStore(t, store, commitID, commitID) - - // XXX: commit this older version - commitID = store.Commit() - expectedCommitID = getExpectedCommitID(store, ver+1) - checkStore(t, store, expectedCommitID, commitID) - - // XXX: confirm old commit is overwritten and we have rolled back - // LatestVersion - store = newMultiStoreWithMounts(db, types.PruneSyncable) - err = store.LoadLatestVersion() - require.Nil(t, err) - commitID = getExpectedCommitID(store, ver+1) - checkStore(t, store, commitID, commitID) } func TestMultistoreLoadWithUpgrade(t *testing.T) { @@ -195,6 +192,9 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) s3.Set(k3, v3) + s4, _ := store.getStoreByName("store4").(types.KVStore) + require.Nil(t, s4) + // do one commit commitID := store.Commit() expectedCommitID := getExpectedCommitID(store, 1) @@ -234,6 +234,24 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) require.Nil(t, s3.Get(k3)) // data was deleted + // store4 is mounted, with empty data + s4, _ = restore.getStoreByName("store4").(types.KVStore) + require.NotNil(t, s4) + + iterator := s4.Iterator(nil, nil) + + values := 0 + for ; iterator.Valid(); iterator.Next() { + values += 1 + } + require.Zero(t, values) + + require.NoError(t, iterator.Close()) + + // write something inside store4 + k4, v4 := []byte("fourth"), []byte("created") + s4.Set(k4, v4) + // store2 is no longer mounted st2 := restore.getStoreByName("store2") require.Nil(t, st2) @@ -261,12 +279,16 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, rl2) require.Equal(t, v2, rl2.Get(k2)) + rl4, _ := reload.getStoreByName("store4").(types.KVStore) + require.NotNil(t, rl4) + require.Equal(t, v4, rl4.Get(k4)) + // check commitInfo in storage ci, err = getCommitInfo(db, 2) require.NoError(t, err) require.Equal(t, int64(2), ci.Version) - require.Equal(t, 3, len(ci.StoreInfos), ci.StoreInfos) - checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3"}) + require.Equal(t, 4, len(ci.StoreInfos), ci.StoreInfos) + checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3", "store4"}) } func TestParsePath(t *testing.T) { @@ -293,8 +315,9 @@ func TestParsePath(t *testing.T) { func TestMultiStoreRestart(t *testing.T) { db := dbm.NewMemDB() pruning := types.PruningOptions{ - KeepEvery: 3, - SnapshotEvery: 6, + KeepRecent: 2, + KeepEvery: 3, + Interval: 1, } multi := newMultiStoreWithMounts(db, pruning) err := multi.LoadLatestVersion() @@ -322,8 +345,8 @@ func TestMultiStoreRestart(t *testing.T) { multi.Commit() cinfo, err := getCommitInfo(multi.db, int64(i)) - require.NotNil(t, err) - require.Equal(t, commitInfo{}, cinfo) + require.NoError(t, err) + require.Equal(t, int64(i), cinfo.Version) } // Set and commit data in one store. @@ -347,15 +370,15 @@ func TestMultiStoreRestart(t *testing.T) { multi.Commit() postFlushCinfo, err := getCommitInfo(multi.db, 4) - require.NotNil(t, err) - require.Equal(t, commitInfo{}, postFlushCinfo, "Commit changed after in-memory commit") + require.NoError(t, err) + require.Equal(t, int64(4), postFlushCinfo.Version, "Commit changed after in-memory commit") multi = newMultiStoreWithMounts(db, pruning) err = multi.LoadLatestVersion() require.Nil(t, err) reloadedCid := multi.LastCommitID() - require.Equal(t, flushedCinfo.CommitID(), reloadedCid, "Reloaded CID is not the same as last flushed CID") + require.Equal(t, int64(4), reloadedCid.Version, "Reloaded CID is not the same as last flushed CID") // Check that store1 and store2 retained date from 3rd commit store1 = multi.getStoreByName("store1").(types.KVStore) @@ -369,7 +392,7 @@ func TestMultiStoreRestart(t *testing.T) { // Check that store3 still has data from last commit even though update happened on 2nd commit store3 = multi.getStoreByName("store3").(types.KVStore) val3 := store3.Get([]byte(k3)) - require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 2)), val3, "Reloaded value not the same as last flushed value") + require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 3)), val3, "Reloaded value not the same as last flushed value") } func TestMultiStoreQuery(t *testing.T) { @@ -442,6 +465,286 @@ func TestMultiStoreQuery(t *testing.T) { require.Equal(t, v2, qres.Value) } +func TestMultiStore_Pruning(t *testing.T) { + testCases := []struct { + name string + numVersions int64 + po types.PruningOptions + deleted []int64 + saved []int64 + }{ + {"prune nothing", 10, types.PruneNothing, nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + {"prune everything", 10, types.PruneEverything, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int64{10}}, + {"prune some; no batch", 10, types.NewPruningOptions(2, 3, 1), []int64{1, 2, 4, 5, 7}, []int64{3, 6, 8, 9, 10}}, + {"prune some; small batch", 10, types.NewPruningOptions(2, 3, 3), []int64{1, 2, 4, 5}, []int64{3, 6, 7, 8, 9, 10}}, + {"prune some; large batch", 10, types.NewPruningOptions(2, 3, 11), nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + db := dbm.NewMemDB() + ms := newMultiStoreWithMounts(db, tc.po) + require.NoError(t, ms.LoadLatestVersion()) + + for i := int64(0); i < tc.numVersions; i++ { + ms.Commit() + } + + for _, v := range tc.saved { + _, err := ms.CacheMultiStoreWithVersion(v) + require.NoError(t, err, "expected error when loading height: %d", v) + } + + for _, v := range tc.deleted { + _, err := ms.CacheMultiStoreWithVersion(v) + require.NoError(t, err, "expected error when loading height: %d", v) + } + }) + } +} + +func TestMultiStore_PruningRestart(t *testing.T) { + db := dbm.NewMemDB() + ms := newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11)) + require.NoError(t, ms.LoadLatestVersion()) + + // Commit enough to build up heights to prune, where on the next block we should + // batch delete. + for i := int64(0); i < 10; i++ { + ms.Commit() + } + + pruneHeights := []int64{1, 2, 4, 5, 7} + + // ensure we've persisted the current batch of heights to prune to the store's DB + ph, err := getPruningHeights(ms.db) + require.NoError(t, err) + require.Equal(t, pruneHeights, ph) + + // "restart" + ms = newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11)) + err = ms.LoadLatestVersion() + require.NoError(t, err) + require.Equal(t, pruneHeights, ms.pruneHeights) + + // commit one more block and ensure the heights have been pruned + ms.Commit() + require.Empty(t, ms.pruneHeights) + + for _, v := range pruneHeights { + _, err := ms.CacheMultiStoreWithVersion(v) + require.NoError(t, err, "expected error when loading height: %d", v) + } +} + +func TestMultistoreSnapshot_Checksum(t *testing.T) { + // Chunks from different nodes must fit together, so all nodes must produce identical chunks. + // This checksum test makes sure that the byte stream remains identical. If the test fails + // without having changed the data (e.g. because the Protobuf or zlib encoding changes), + // snapshottypes.CurrentFormat must be bumped. + store := newMultiStoreWithGeneratedData(dbm.NewMemDB(), 5, 10000) + version := uint64(store.LastCommitID().Version) + + testcases := []struct { + format uint32 + chunkHashes []string + }{ + {1, []string{ + "503e5b51b657055b77e88169fadae543619368744ad15f1de0736c0a20482f24", + "e1a0daaa738eeb43e778aefd2805e3dd720798288a410b06da4b8459c4d8f72e", + "aa048b4ee0f484965d7b3b06822cf0772cdcaad02f3b1b9055e69f2cb365ef3c", + "7921eaa3ed4921341e504d9308a9877986a879fe216a099c86e8db66fcba4c63", + "a4a864e6c02c9fca5837ec80dc84f650b25276ed7e4820cf7516ced9f9901b86", + "ca2879ac6e7205d257440131ba7e72bef784cd61642e32b847729e543c1928b9", + }}, + } + for _, tc := range testcases { + tc := tc + t.Run(fmt.Sprintf("Format %v", tc.format), func(t *testing.T) { + chunks, err := store.Snapshot(version, tc.format) + require.NoError(t, err) + hashes := []string{} + hasher := sha256.New() + for chunk := range chunks { + hasher.Reset() + _, err := io.Copy(hasher, chunk) + require.NoError(t, err) + hashes = append(hashes, hex.EncodeToString(hasher.Sum(nil))) + } + assert.Equal(t, tc.chunkHashes, hashes, + "Snapshot output for format %v has changed", tc.format) + }) + } +} + +func TestMultistoreSnapshot_Errors(t *testing.T) { + store := newMultiStoreWithMixedMountsAndBasicData(dbm.NewMemDB()) + + testcases := map[string]struct { + height uint64 + format uint32 + expectType error + }{ + "0 height": {0, snapshottypes.CurrentFormat, nil}, + "0 format": {1, 0, snapshottypes.ErrUnknownFormat}, + "unknown height": {9, snapshottypes.CurrentFormat, nil}, + "unknown format": {1, 9, snapshottypes.ErrUnknownFormat}, + } + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + _, err := store.Snapshot(tc.height, tc.format) + require.Error(t, err) + if tc.expectType != nil { + assert.True(t, errors.Is(err, tc.expectType)) + } + }) + } +} + +func TestMultistoreRestore_Errors(t *testing.T) { + store := newMultiStoreWithMixedMounts(dbm.NewMemDB()) + + testcases := map[string]struct { + height uint64 + format uint32 + expectType error + }{ + "0 height": {0, snapshottypes.CurrentFormat, nil}, + "0 format": {1, 0, snapshottypes.ErrUnknownFormat}, + "unknown format": {1, 9, snapshottypes.ErrUnknownFormat}, + } + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + err := store.Restore(tc.height, tc.format, nil, nil) + require.Error(t, err) + if tc.expectType != nil { + assert.True(t, errors.Is(err, tc.expectType)) + } + }) + } +} + +func TestMultistoreSnapshotRestore(t *testing.T) { + source := newMultiStoreWithMixedMountsAndBasicData(dbm.NewMemDB()) + target := newMultiStoreWithMixedMounts(dbm.NewMemDB()) + version := uint64(source.LastCommitID().Version) + require.EqualValues(t, 3, version) + + chunks, err := source.Snapshot(version, snapshottypes.CurrentFormat) + require.NoError(t, err) + ready := make(chan struct{}) + err = target.Restore(version, snapshottypes.CurrentFormat, chunks, ready) + require.NoError(t, err) + assert.EqualValues(t, struct{}{}, <-ready) + + assert.Equal(t, source.LastCommitID(), target.LastCommitID()) + for key, sourceStore := range source.stores { + targetStore := target.getStoreByName(key.Name()).(types.CommitKVStore) + switch sourceStore.GetStoreType() { + case types.StoreTypeTransient: + assert.False(t, targetStore.Iterator(nil, nil).Valid(), + "transient store %v not empty", key.Name()) + default: + assertStoresEqual(t, sourceStore, targetStore, "store %q not equal", key.Name()) + } + } +} + +func TestSetInitialVersion(t *testing.T) { + db := dbm.NewMemDB() + multi := newMultiStoreWithMounts(db, types.PruneNothing) + + require.NoError(t, multi.LoadLatestVersion()) + + multi.SetInitialVersion(5) + require.Equal(t, int64(5), multi.initialVersion) + + multi.Commit() + require.Equal(t, int64(5), multi.LastCommitID().Version) + + ckvs := multi.GetCommitKVStore(multi.keysByName["store1"]) + iavlStore, ok := ckvs.(*iavl.Store) + require.True(t, ok) + require.True(t, iavlStore.VersionExists(5)) +} + +func BenchmarkMultistoreSnapshot100K(b *testing.B) { + benchmarkMultistoreSnapshot(b, 10, 10000) +} + +func BenchmarkMultistoreSnapshot1M(b *testing.B) { + benchmarkMultistoreSnapshot(b, 10, 100000) +} + +func BenchmarkMultistoreSnapshotRestore100K(b *testing.B) { + benchmarkMultistoreSnapshotRestore(b, 10, 10000) +} + +func BenchmarkMultistoreSnapshotRestore1M(b *testing.B) { + benchmarkMultistoreSnapshotRestore(b, 10, 100000) +} + +func benchmarkMultistoreSnapshot(b *testing.B, stores uint8, storeKeys uint64) { + b.Skip("Noisy with slow setup time, please see https://github.com/cosmos/cosmos-sdk/issues/8855.") + + b.ReportAllocs() + b.StopTimer() + source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys) + version := source.LastCommitID().Version + require.EqualValues(b, 1, version) + b.StartTimer() + + for i := 0; i < b.N; i++ { + target := NewStore(dbm.NewMemDB()) + for key := range source.stores { + target.MountStoreWithDB(key, types.StoreTypeIAVL, nil) + } + err := target.LoadLatestVersion() + require.NoError(b, err) + require.EqualValues(b, 0, target.LastCommitID().Version) + + chunks, err := source.Snapshot(uint64(version), snapshottypes.CurrentFormat) + require.NoError(b, err) + for reader := range chunks { + _, err := io.Copy(ioutil.Discard, reader) + require.NoError(b, err) + err = reader.Close() + require.NoError(b, err) + } + } +} + +func benchmarkMultistoreSnapshotRestore(b *testing.B, stores uint8, storeKeys uint64) { + b.Skip("Noisy with slow setup time, please see https://github.com/cosmos/cosmos-sdk/issues/8855.") + + b.ReportAllocs() + b.StopTimer() + source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys) + version := uint64(source.LastCommitID().Version) + require.EqualValues(b, 1, version) + b.StartTimer() + + for i := 0; i < b.N; i++ { + target := NewStore(dbm.NewMemDB()) + for key := range source.stores { + target.MountStoreWithDB(key, types.StoreTypeIAVL, nil) + } + err := target.LoadLatestVersion() + require.NoError(b, err) + require.EqualValues(b, 0, target.LastCommitID().Version) + + chunks, err := source.Snapshot(version, snapshottypes.CurrentFormat) + require.NoError(b, err) + err = target.Restore(version, snapshottypes.CurrentFormat, chunks, nil) + require.NoError(b, err) + require.Equal(b, source.LastCommitID(), target.LastCommitID()) + } +} + //----------------------------------------------------------------------- // utils @@ -456,6 +759,75 @@ func newMultiStoreWithMounts(db dbm.DB, pruningOpts types.PruningOptions) *Store return store } +func newMultiStoreWithMixedMounts(db dbm.DB) *Store { + store := NewStore(db) + store.MountStoreWithDB(types.NewKVStoreKey("iavl1"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewKVStoreKey("iavl2"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewKVStoreKey("iavl3"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewTransientStoreKey("trans1"), types.StoreTypeTransient, nil) + store.LoadLatestVersion() + + return store +} + +func newMultiStoreWithMixedMountsAndBasicData(db dbm.DB) *Store { + store := newMultiStoreWithMixedMounts(db) + store1 := store.getStoreByName("iavl1").(types.CommitKVStore) + store2 := store.getStoreByName("iavl2").(types.CommitKVStore) + trans1 := store.getStoreByName("trans1").(types.KVStore) + + store1.Set([]byte("a"), []byte{1}) + store1.Set([]byte("b"), []byte{1}) + store2.Set([]byte("X"), []byte{255}) + store2.Set([]byte("A"), []byte{101}) + trans1.Set([]byte("x1"), []byte{91}) + store.Commit() + + store1.Set([]byte("b"), []byte{2}) + store1.Set([]byte("c"), []byte{3}) + store2.Set([]byte("B"), []byte{102}) + store.Commit() + + store2.Set([]byte("C"), []byte{103}) + store2.Delete([]byte("X")) + trans1.Set([]byte("x2"), []byte{92}) + store.Commit() + + return store +} + +func newMultiStoreWithGeneratedData(db dbm.DB, stores uint8, storeKeys uint64) *Store { + multiStore := NewStore(db) + r := rand.New(rand.NewSource(49872768940)) // Fixed seed for deterministic tests + + keys := []*types.KVStoreKey{} + for i := uint8(0); i < stores; i++ { + key := types.NewKVStoreKey(fmt.Sprintf("store%v", i)) + multiStore.MountStoreWithDB(key, types.StoreTypeIAVL, nil) + keys = append(keys, key) + } + multiStore.LoadLatestVersion() + + for _, key := range keys { + store := multiStore.stores[key].(*iavl.Store) + for i := uint64(0); i < storeKeys; i++ { + k := make([]byte, 8) + v := make([]byte, 1024) + binary.BigEndian.PutUint64(k, i) + _, err := r.Read(v) + if err != nil { + panic(err) + } + store.Set(k, v) + } + } + + multiStore.Commit() + multiStore.LoadLatestVersion() + + return multiStore +} + func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions) (*Store, *types.StoreUpgrades) { store := NewStore(db) store.pruningOpts = pruningOpts @@ -463,8 +835,10 @@ func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions store.MountStoreWithDB(types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("restore2"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewKVStoreKey("store4"), types.StoreTypeIAVL, nil) upgrades := &types.StoreUpgrades{ + Added: []string{"store4"}, Renamed: []types.StoreRename{{ OldKey: "store2", NewKey: "restore2", @@ -475,13 +849,31 @@ func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions return store, upgrades } +func assertStoresEqual(t *testing.T, expect, actual types.CommitKVStore, msgAndArgs ...interface{}) { + assert.Equal(t, expect.LastCommitID(), actual.LastCommitID()) + expectIter := expect.Iterator(nil, nil) + expectMap := map[string][]byte{} + for ; expectIter.Valid(); expectIter.Next() { + expectMap[string(expectIter.Key())] = expectIter.Value() + } + require.NoError(t, expectIter.Error()) + + actualIter := expect.Iterator(nil, nil) + actualMap := map[string][]byte{} + for ; actualIter.Valid(); actualIter.Next() { + actualMap[string(actualIter.Key())] = actualIter.Value() + } + require.NoError(t, actualIter.Error()) + + assert.Equal(t, expectMap, actualMap, msgAndArgs...) +} + func checkStore(t *testing.T, store *Store, expect, got types.CommitID) { require.Equal(t, expect, got) require.Equal(t, expect, store.LastCommitID()) - } -func checkContains(t testing.TB, info []storeInfo, wanted []string) { +func checkContains(t testing.TB, info []types.StoreInfo, wanted []string) { t.Helper() for _, want := range wanted { @@ -489,7 +881,7 @@ func checkContains(t testing.TB, info []storeInfo, wanted []string) { } } -func checkHas(t testing.TB, info []storeInfo, want string) { +func checkHas(t testing.TB, info []types.StoreInfo, want string) { t.Helper() for _, i := range info { if i.Name == want { @@ -510,13 +902,10 @@ func hashStores(stores map[types.StoreKey]types.CommitKVStore) []byte { m := make(map[string][]byte, len(stores)) for key, store := range stores { name := key.Name() - m[name] = storeInfo{ - Name: name, - Core: storeCore{ - CommitID: store.LastCommitID(), - // StoreType: store.GetStoreType(), - }, - }.Hash() + m[name] = types.StoreInfo{ + Name: name, + CommitId: store.LastCommitID(), + }.GetHash() } - return merkle.SimpleHashFromMap(m) + return sdkmaps.HashFromMap(m) } diff --git a/store/rootmulti/wire.go b/store/rootmulti/wire.go deleted file mode 100644 index 8d6d936160ff..000000000000 --- a/store/rootmulti/wire.go +++ /dev/null @@ -1,7 +0,0 @@ -package rootmulti - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -var cdc = codec.New() diff --git a/store/store.go b/store/store.go index ba0311763814..2c068c413f47 100644 --- a/store/store.go +++ b/store/store.go @@ -8,13 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -// Pruning strategies that may be provided to a KVStore to enable pruning. -const ( - PruningStrategyNothing = "nothing" - PruningStrategyEverything = "everything" - PruningStrategySyncable = "syncable" -) - func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { return rootmulti.NewStore(db) } @@ -22,17 +15,3 @@ func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { func NewCommitKVStoreCacheManager() types.MultiStorePersistentCache { return cache.NewCommitKVStoreCacheManager(cache.DefaultCommitKVStoreCacheSize) } - -func NewPruningOptionsFromString(strategy string) (opt PruningOptions) { - switch strategy { - case PruningStrategyNothing: - opt = PruneNothing - case PruningStrategyEverything: - opt = PruneEverything - case PruningStrategySyncable: - opt = PruneSyncable - default: - opt = PruneSyncable - } - return -} diff --git a/store/tracekv/store.go b/store/tracekv/store.go index ef891526fc23..2958d968267a 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -3,10 +3,10 @@ package tracekv import ( "encoding/base64" "encoding/json" - "fmt" "io" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -60,6 +60,7 @@ func (tkv *Store) Get(key []byte) []byte { // Set implements the KVStore interface. It traces a write operation and // delegates the Set call to the parent KVStore. func (tkv *Store) Set(key []byte, value []byte) { + types.AssertValidKey(key) writeOperation(tkv.writer, writeOp, tkv.context, key, value) tkv.parent.Set(key, value) } @@ -145,8 +146,8 @@ func (ti *traceIterator) Value() []byte { } // Close implements the Iterator interface. -func (ti *traceIterator) Close() { - ti.parent.Close() +func (ti *traceIterator) Close() error { + return ti.parent.Close() } // Error delegates the Error call to the parent iterator. @@ -160,14 +161,14 @@ func (tkv *Store) GetStoreType() types.StoreType { return tkv.parent.GetStoreType() } -// CacheWrap implements the KVStore interface. It panics as a Store -// cannot be cache wrapped. +// CacheWrap implements the KVStore interface. It panics because a Store +// cannot be branched. func (tkv *Store) CacheWrap() types.CacheWrap { panic("cannot CacheWrap a Store") } // CacheWrapWithTrace implements the KVStore interface. It panics as a -// Store cannot be cache wrapped. +// Store cannot be branched. func (tkv *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { panic("cannot CacheWrapWithTrace a Store") } @@ -187,11 +188,11 @@ func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value raw, err := json.Marshal(traceOp) if err != nil { - panic(fmt.Sprintf("failed to serialize trace operation: %v", err)) + panic(errors.Wrap(err, "failed to serialize trace operation")) } if _, err := w.Write(raw); err != nil { - panic(fmt.Sprintf("failed to write trace operation: %v", err)) + panic(errors.Wrap(err, "failed to write trace operation")) } io.WriteString(w, "\n") diff --git a/store/tracekv/store_test.go b/store/tracekv/store_test.go index 99b17233ea26..e2c4e2a0fe5d 100644 --- a/store/tracekv/store_test.go +++ b/store/tracekv/store_test.go @@ -50,11 +50,6 @@ func TestTraceKVStoreGet(t *testing.T) { expectedValue []byte expectedOut string }{ - { - key: []byte{}, - expectedValue: nil, - expectedOut: "{\"operation\":\"read\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n", - }, { key: kvPairs[0].Key, expectedValue: kvPairs[0].Value, @@ -85,16 +80,21 @@ func TestTraceKVStoreSet(t *testing.T) { value []byte expectedOut string }{ - { - key: []byte{}, - value: nil, - expectedOut: "{\"operation\":\"write\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n", - }, { key: kvPairs[0].Key, value: kvPairs[0].Value, expectedOut: "{\"operation\":\"write\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"dmFsdWUwMDAwMDAwMQ==\",\"metadata\":{\"blockHeight\":64}}\n", }, + { + key: kvPairs[1].Key, + value: kvPairs[1].Value, + expectedOut: "{\"operation\":\"write\",\"key\":\"a2V5MDAwMDAwMDI=\",\"value\":\"dmFsdWUwMDAwMDAwMg==\",\"metadata\":{\"blockHeight\":64}}\n", + }, + { + key: kvPairs[2].Key, + value: kvPairs[2].Value, + expectedOut: "{\"operation\":\"write\",\"key\":\"a2V5MDAwMDAwMDM=\",\"value\":\"dmFsdWUwMDAwMDAwMw==\",\"metadata\":{\"blockHeight\":64}}\n", + }, } for _, tc := range testCases { @@ -106,6 +106,12 @@ func TestTraceKVStoreSet(t *testing.T) { require.Equal(t, tc.expectedOut, buf.String()) } + + var buf bytes.Buffer + store := newEmptyTraceKVStore(&buf) + require.Panics(t, func() { store.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") + require.Panics(t, func() { store.Set(nil, []byte("value")) }, "setting a nil key should panic") + } func TestTraceKVStoreDelete(t *testing.T) { @@ -113,10 +119,6 @@ func TestTraceKVStoreDelete(t *testing.T) { key []byte expectedOut string }{ - { - key: []byte{}, - expectedOut: "{\"operation\":\"delete\",\"key\":\"\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n", - }, { key: kvPairs[0].Key, expectedOut: "{\"operation\":\"delete\",\"key\":\"a2V5MDAwMDAwMDE=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n", @@ -139,10 +141,6 @@ func TestTraceKVStoreHas(t *testing.T) { key []byte expected bool }{ - { - key: []byte{}, - expected: false, - }, { key: kvPairs[0].Key, expected: true, @@ -213,7 +211,7 @@ func TestTestTraceKVStoreIterator(t *testing.T) { require.False(t, iterator.Valid()) require.Panics(t, iterator.Next) - require.NotPanics(t, iterator.Close) + require.NoError(t, iterator.Close()) } func TestTestTraceKVStoreReverseIterator(t *testing.T) { @@ -269,7 +267,7 @@ func TestTestTraceKVStoreReverseIterator(t *testing.T) { require.False(t, iterator.Valid()) require.Panics(t, iterator.Next) - require.NotPanics(t, iterator.Close) + require.NoError(t, iterator.Close()) } func TestTraceKVStorePrefix(t *testing.T) { diff --git a/store/transient/store.go b/store/transient/store.go index 488965a43ac5..572ab55f7697 100644 --- a/store/transient/store.go +++ b/store/transient/store.go @@ -3,9 +3,8 @@ package transient import ( dbm "github.com/tendermint/tm-db" - "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" ) var _ types.Committer = (*Store)(nil) @@ -28,9 +27,11 @@ func (ts *Store) Commit() (id types.CommitID) { return } -// Implements CommitStore -func (ts *Store) SetPruning(pruning types.PruningOptions) { -} +func (ts *Store) SetPruning(_ types.PruningOptions) {} + +// GetPruning is a no-op as pruning options cannot be directly set on this store. +// They must be set on the root commit multi-store. +func (ts *Store) GetPruning() types.PruningOptions { return types.PruningOptions{} } // Implements CommitStore func (ts *Store) LastCommitID() (id types.CommitID) { diff --git a/store/transient/store_test.go b/store/transient/store_test.go index 846c8a3a43ca..632b561b618a 100644 --- a/store/transient/store_test.go +++ b/store/transient/store_test.go @@ -1,15 +1,19 @@ -package transient +package transient_test import ( + "bytes" "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/transient" + "github.com/cosmos/cosmos-sdk/store/types" ) var k, v = []byte("hello"), []byte("world") func TestTransientStore(t *testing.T) { - tstore := NewStore() + tstore := transient.NewStore() require.Nil(t, tstore.Get(k)) @@ -20,4 +24,12 @@ func TestTransientStore(t *testing.T) { tstore.Commit() require.Nil(t, tstore.Get(k)) + + // no-op + tstore.SetPruning(types.PruningOptions{}) + + emptyCommitID := tstore.LastCommitID() + require.Equal(t, emptyCommitID.Version, int64(0)) + require.True(t, bytes.Equal(emptyCommitID.Hash, nil)) + require.Equal(t, types.StoreTypeTransient, tstore.GetStoreType()) } diff --git a/store/types/commit_info.go b/store/types/commit_info.go new file mode 100644 index 000000000000..e713040739f8 --- /dev/null +++ b/store/types/commit_info.go @@ -0,0 +1,73 @@ +package types + +import ( + fmt "fmt" + + ics23 "github.com/confio/ics23/go" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" + sdkproofs "github.com/cosmos/cosmos-sdk/store/internal/proofs" +) + +// GetHash returns the GetHash from the CommitID. +// This is used in CommitInfo.Hash() +// +// When we commit to this in a merkle proof, we create a map of storeInfo.Name -> storeInfo.GetHash() +// and build a merkle proof from that. +// This is then chained with the substore proof, so we prove the root hash from the substore before this +// and need to pass that (unmodified) as the leaf value of the multistore proof. +func (si StoreInfo) GetHash() []byte { + return si.CommitId.Hash +} + +func (ci CommitInfo) toMap() map[string][]byte { + m := make(map[string][]byte, len(ci.StoreInfos)) + for _, storeInfo := range ci.StoreInfos { + m[storeInfo.Name] = storeInfo.GetHash() + } + + return m +} + +// Hash returns the simple merkle root hash of the stores sorted by name. +func (ci CommitInfo) Hash() []byte { + // we need a special case for empty set, as SimpleProofsFromMap requires at least one entry + if len(ci.StoreInfos) == 0 { + return nil + } + + rootHash, _, _ := sdkmaps.ProofsFromMap(ci.toMap()) + return rootHash +} + +func (ci CommitInfo) ProofOp(storeName string) tmcrypto.ProofOp { + cmap := ci.toMap() + _, proofs, _ := sdkmaps.ProofsFromMap(cmap) + + proof := proofs[storeName] + if proof == nil { + panic(fmt.Sprintf("ProofOp for %s but not registered store name", storeName)) + } + + // convert merkle.SimpleProof to CommitmentProof + existProof, err := sdkproofs.ConvertExistenceProof(proof, []byte(storeName), cmap[storeName]) + if err != nil { + panic(fmt.Errorf("could not convert simple proof to existence proof: %w", err)) + } + + commitmentProof := &ics23.CommitmentProof{ + Proof: &ics23.CommitmentProof_Exist{ + Exist: existProof, + }, + } + + return NewSimpleMerkleCommitmentOp([]byte(storeName), commitmentProof).ProofOp() +} + +func (ci CommitInfo) CommitID() CommitID { + return CommitID{ + Version: ci.Version, + Hash: ci.Hash(), + } +} diff --git a/store/types/commit_info.pb.go b/store/types/commit_info.pb.go new file mode 100644 index 000000000000..988804d2f307 --- /dev/null +++ b/store/types/commit_info.pb.go @@ -0,0 +1,807 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/store/v1beta1/commit_info.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 + +// CommitInfo defines commit information used by the multi-store when committing +// a version/height. +type CommitInfo struct { + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + StoreInfos []StoreInfo `protobuf:"bytes,2,rep,name=store_infos,json=storeInfos,proto3" json:"store_infos"` +} + +func (m *CommitInfo) Reset() { *m = CommitInfo{} } +func (m *CommitInfo) String() string { return proto.CompactTextString(m) } +func (*CommitInfo) ProtoMessage() {} +func (*CommitInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_83f4097f6265b52f, []int{0} +} +func (m *CommitInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommitInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommitInfo.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 *CommitInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommitInfo.Merge(m, src) +} +func (m *CommitInfo) XXX_Size() int { + return m.Size() +} +func (m *CommitInfo) XXX_DiscardUnknown() { + xxx_messageInfo_CommitInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_CommitInfo proto.InternalMessageInfo + +func (m *CommitInfo) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *CommitInfo) GetStoreInfos() []StoreInfo { + if m != nil { + return m.StoreInfos + } + return nil +} + +// StoreInfo defines store-specific commit information. It contains a reference +// between a store name and the commit ID. +type StoreInfo struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + CommitId CommitID `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id"` +} + +func (m *StoreInfo) Reset() { *m = StoreInfo{} } +func (m *StoreInfo) String() string { return proto.CompactTextString(m) } +func (*StoreInfo) ProtoMessage() {} +func (*StoreInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_83f4097f6265b52f, []int{1} +} +func (m *StoreInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoreInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoreInfo.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 *StoreInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoreInfo.Merge(m, src) +} +func (m *StoreInfo) XXX_Size() int { + return m.Size() +} +func (m *StoreInfo) XXX_DiscardUnknown() { + xxx_messageInfo_StoreInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_StoreInfo proto.InternalMessageInfo + +func (m *StoreInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *StoreInfo) GetCommitId() CommitID { + if m != nil { + return m.CommitId + } + return CommitID{} +} + +// CommitID defines the committment information when a specific store is +// committed. +type CommitID struct { + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *CommitID) Reset() { *m = CommitID{} } +func (*CommitID) ProtoMessage() {} +func (*CommitID) Descriptor() ([]byte, []int) { + return fileDescriptor_83f4097f6265b52f, []int{2} +} +func (m *CommitID) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommitID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommitID.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 *CommitID) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommitID.Merge(m, src) +} +func (m *CommitID) XXX_Size() int { + return m.Size() +} +func (m *CommitID) XXX_DiscardUnknown() { + xxx_messageInfo_CommitID.DiscardUnknown(m) +} + +var xxx_messageInfo_CommitID proto.InternalMessageInfo + +func (m *CommitID) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *CommitID) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func init() { + proto.RegisterType((*CommitInfo)(nil), "cosmos.base.store.v1beta1.CommitInfo") + proto.RegisterType((*StoreInfo)(nil), "cosmos.base.store.v1beta1.StoreInfo") + proto.RegisterType((*CommitID)(nil), "cosmos.base.store.v1beta1.CommitID") +} + +func init() { + proto.RegisterFile("cosmos/base/store/v1beta1/commit_info.proto", fileDescriptor_83f4097f6265b52f) +} + +var fileDescriptor_83f4097f6265b52f = []byte{ + // 301 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0x2f, 0x2e, 0xc9, 0x2f, 0x4a, 0xd5, 0x2f, 0x33, + 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0xce, 0xcf, 0xcd, 0xcd, 0x2c, 0x89, 0xcf, 0xcc, 0x4b, + 0xcb, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x84, 0x28, 0xd6, 0x03, 0x29, 0xd6, 0x03, + 0x2b, 0xd6, 0x83, 0x2a, 0x96, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd2, 0x07, 0xb1, 0x20, + 0x1a, 0x94, 0x8a, 0xb9, 0xb8, 0x9c, 0xc1, 0xa6, 0x78, 0xe6, 0xa5, 0xe5, 0x0b, 0x49, 0x70, 0xb1, + 0x97, 0xa5, 0x16, 0x15, 0x67, 0xe6, 0xe7, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, + 0x42, 0xde, 0x5c, 0xdc, 0x60, 0xe3, 0xc0, 0x96, 0x15, 0x4b, 0x30, 0x29, 0x30, 0x6b, 0x70, 0x1b, + 0xa9, 0xe8, 0xe1, 0xb4, 0x4e, 0x2f, 0x18, 0xc4, 0x03, 0x19, 0xea, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, + 0x43, 0x10, 0x57, 0x31, 0x4c, 0xa0, 0x58, 0x29, 0x9d, 0x8b, 0x13, 0x2e, 0x2d, 0x24, 0xc4, 0xc5, + 0x92, 0x97, 0x98, 0x9b, 0x0a, 0xb6, 0x90, 0x33, 0x08, 0xcc, 0x16, 0x72, 0xe3, 0xe2, 0x84, 0xf9, + 0x2d, 0x45, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x19, 0x8f, 0x5d, 0x50, 0x1f, 0xb8, 0x40, + 0xad, 0xe2, 0x80, 0xe8, 0xf5, 0x4c, 0x51, 0xb2, 0xe3, 0xe2, 0x80, 0xc9, 0xe1, 0xf1, 0x9b, 0x10, + 0x17, 0x4b, 0x46, 0x62, 0x71, 0x06, 0xd8, 0x22, 0x9e, 0x20, 0x30, 0xdb, 0x8a, 0x65, 0xc6, 0x02, + 0x79, 0x06, 0x27, 0xa7, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, + 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xd2, 0x48, + 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x87, 0x46, 0x10, 0x84, 0xd2, 0x2d, + 0x4e, 0xc9, 0x86, 0x46, 0x53, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0xa0, 0x8d, 0x01, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0xc0, 0xc7, 0x12, 0xc8, 0x01, 0x00, 0x00, +} + +func (m *CommitInfo) 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 *CommitInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommitInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StoreInfos) > 0 { + for iNdEx := len(m.StoreInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.StoreInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommitInfo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Version != 0 { + i = encodeVarintCommitInfo(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *StoreInfo) 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 *StoreInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.CommitId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommitInfo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintCommitInfo(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommitID) 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 *CommitID) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommitID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintCommitInfo(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x12 + } + if m.Version != 0 { + i = encodeVarintCommitInfo(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintCommitInfo(dAtA []byte, offset int, v uint64) int { + offset -= sovCommitInfo(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *CommitInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovCommitInfo(uint64(m.Version)) + } + if len(m.StoreInfos) > 0 { + for _, e := range m.StoreInfos { + l = e.Size() + n += 1 + l + sovCommitInfo(uint64(l)) + } + } + return n +} + +func (m *StoreInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovCommitInfo(uint64(l)) + } + l = m.CommitId.Size() + n += 1 + l + sovCommitInfo(uint64(l)) + return n +} + +func (m *CommitID) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovCommitInfo(uint64(m.Version)) + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovCommitInfo(uint64(l)) + } + return n +} + +func sovCommitInfo(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCommitInfo(x uint64) (n int) { + return sovCommitInfo(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *CommitInfo) 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 ErrIntOverflowCommitInfo + } + 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: CommitInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommitInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StoreInfos", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StoreInfos = append(m.StoreInfos, StoreInfo{}) + if err := m.StoreInfos[len(m.StoreInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StoreInfo) 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 ErrIntOverflowCommitInfo + } + 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: StoreInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StoreInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + 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 ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommitId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CommitId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommitID) 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 ErrIntOverflowCommitInfo + } + 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: CommitID: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommitID: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCommitInfo + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCommitInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCommitInfo(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, ErrIntOverflowCommitInfo + } + 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, ErrIntOverflowCommitInfo + } + 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, ErrIntOverflowCommitInfo + } + 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, ErrInvalidLengthCommitInfo + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCommitInfo + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCommitInfo + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCommitInfo = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCommitInfo = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCommitInfo = fmt.Errorf("proto: unexpected end of group") +) diff --git a/store/types/errors.go b/store/types/errors.go new file mode 100644 index 000000000000..780fcdef37ab --- /dev/null +++ b/store/types/errors.go @@ -0,0 +1,11 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const StoreCodespace = "store" + +var ( + ErrInvalidProof = sdkerrors.Register(StoreCodespace, 2, "invalid proof") +) diff --git a/store/types/gas.go b/store/types/gas.go index a5a2c5e1d19b..c6c94f859e02 100644 --- a/store/types/gas.go +++ b/store/types/gas.go @@ -1,6 +1,9 @@ package types -import "math" +import ( + "fmt" + "math" +) // Gas consumption descriptors. const ( @@ -36,6 +39,7 @@ type GasMeter interface { ConsumeGas(amount Gas, descriptor string) IsPastLimit() bool IsOutOfGas() bool + String() string } type basicGasMeter struct { @@ -98,6 +102,10 @@ func (g *basicGasMeter) IsOutOfGas() bool { return g.consumed >= g.limit } +func (g *basicGasMeter) String() string { + return fmt.Sprintf("BasicGasMeter:\n limit: %d\n consumed: %d", g.limit, g.consumed) +} + type infiniteGasMeter struct { consumed Gas } @@ -138,6 +146,10 @@ func (g *infiniteGasMeter) IsOutOfGas() bool { return false } +func (g *infiniteGasMeter) String() string { + return fmt.Sprintf("InfiniteGasMeter:\n consumed: %d", g.consumed) +} + // GasConfig defines gas cost for each operation on KVStores type GasConfig struct { HasCost Gas diff --git a/store/types/gas_test.go b/store/types/gas_test.go index 00d3d834db2d..bd62d463cd67 100644 --- a/store/types/gas_test.go +++ b/store/types/gas_test.go @@ -7,7 +7,23 @@ import ( "github.com/stretchr/testify/require" ) +func TestInfiniteGasMeter(t *testing.T) { + t.Parallel() + meter := NewInfiniteGasMeter() + require.Equal(t, uint64(0), meter.Limit()) + require.Equal(t, uint64(0), meter.GasConsumed()) + require.Equal(t, uint64(0), meter.GasConsumedToLimit()) + meter.ConsumeGas(10, "consume 10") + require.Equal(t, uint64(10), meter.GasConsumed()) + require.Equal(t, uint64(10), meter.GasConsumedToLimit()) + require.False(t, meter.IsPastLimit()) + require.False(t, meter.IsOutOfGas()) + meter.ConsumeGas(Gas(math.MaxUint64/2), "consume half max uint64") + require.Panics(t, func() { meter.ConsumeGas(Gas(math.MaxUint64/2)+2, "panic") }) +} + func TestGasMeter(t *testing.T) { + t.Parallel() cases := []struct { limit Gas usage []Gas @@ -41,11 +57,14 @@ func TestGasMeter(t *testing.T) { require.Panics(t, func() { meter.ConsumeGas(1, "") }, "Exceeded but not panicked. tc #%d", tcnum) require.Equal(t, meter.GasConsumedToLimit(), meter.Limit(), "Gas consumption (to limit) not match limit") require.Equal(t, meter.GasConsumed(), meter.Limit()+1, "Gas consumption not match limit+1") - + meter2 := NewGasMeter(math.MaxUint64) + meter2.ConsumeGas(Gas(math.MaxUint64/2), "consume half max uint64") + require.Panics(t, func() { meter2.ConsumeGas(Gas(math.MaxUint64/2)+2, "panic") }) } } func TestAddUint64Overflow(t *testing.T) { + t.Parallel() testCases := []struct { a, b uint64 result uint64 @@ -69,3 +88,17 @@ func TestAddUint64Overflow(t *testing.T) { ) } } + +func TestTransientGasConfig(t *testing.T) { + t.Parallel() + config := TransientGasConfig() + require.Equal(t, config, GasConfig{ + HasCost: 1000, + DeleteCost: 1000, + ReadCostFlat: 1000, + ReadCostPerByte: 3, + WriteCostFlat: 2000, + WriteCostPerByte: 30, + IterNextCostFlat: 30, + }) +} diff --git a/store/types/iterator_test.go b/store/types/iterator_test.go index e081de80a3d5..3086917b605b 100644 --- a/store/types/iterator_test.go +++ b/store/types/iterator_test.go @@ -12,7 +12,7 @@ import ( func newMemTestKVStore(t *testing.T) types.KVStore { db := dbm.NewMemDB() - store, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false) + store, err := iavl.LoadStore(db, types.CommitID{}, false) require.NoError(t, err) return store } diff --git a/store/types/proof.go b/store/types/proof.go new file mode 100644 index 000000000000..db8f673f46cd --- /dev/null +++ b/store/types/proof.go @@ -0,0 +1,131 @@ +package types + +import ( + ics23 "github.com/confio/ics23/go" + "github.com/tendermint/tendermint/crypto/merkle" + tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + ProofOpIAVLCommitment = "ics23:iavl" + ProofOpSimpleMerkleCommitment = "ics23:simple" +) + +// CommitmentOp implements merkle.ProofOperator by wrapping an ics23 CommitmentProof +// It also contains a Key field to determine which key the proof is proving. +// NOTE: CommitmentProof currently can either be ExistenceProof or NonexistenceProof +// +// Type and Spec are classified by the kind of merkle proof it represents allowing +// the code to be reused by more types. Spec is never on the wire, but mapped from type in the code. +type CommitmentOp struct { + Type string + Spec *ics23.ProofSpec + Key []byte + Proof *ics23.CommitmentProof +} + +var _ merkle.ProofOperator = CommitmentOp{} + +func NewIavlCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp { + return CommitmentOp{ + Type: ProofOpIAVLCommitment, + Spec: ics23.IavlSpec, + Key: key, + Proof: proof, + } +} + +func NewSimpleMerkleCommitmentOp(key []byte, proof *ics23.CommitmentProof) CommitmentOp { + return CommitmentOp{ + Type: ProofOpSimpleMerkleCommitment, + Spec: ics23.TendermintSpec, + Key: key, + Proof: proof, + } +} + +// CommitmentOpDecoder takes a merkle.ProofOp and attempts to decode it into a CommitmentOp ProofOperator +// The proofOp.Data is just a marshalled CommitmentProof. The Key of the CommitmentOp is extracted +// from the unmarshalled proof. +func CommitmentOpDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) { + var spec *ics23.ProofSpec + switch pop.Type { + case ProofOpIAVLCommitment: + spec = ics23.IavlSpec + case ProofOpSimpleMerkleCommitment: + spec = ics23.TendermintSpec + default: + return nil, sdkerrors.Wrapf(ErrInvalidProof, "unexpected ProofOp.Type; got %s, want supported ics23 subtypes 'ProofOpIAVLCommitment' or 'ProofOpSimpleMerkleCommitment'", pop.Type) + } + + proof := &ics23.CommitmentProof{} + err := proof.Unmarshal(pop.Data) + if err != nil { + return nil, err + } + + op := CommitmentOp{ + Type: pop.Type, + Key: pop.Key, + Spec: spec, + Proof: proof, + } + return op, nil +} + +func (op CommitmentOp) GetKey() []byte { + return op.Key +} + +// Run takes in a list of arguments and attempts to run the proof op against these arguments +// Returns the root wrapped in [][]byte if the proof op succeeds with given args. If not, +// it will return an error. +// +// CommitmentOp will accept args of length 1 or length 0 +// If length 1 args is passed in, then CommitmentOp will attempt to prove the existence of the key +// with the value provided by args[0] using the embedded CommitmentProof and return the CommitmentRoot of the proof +// If length 0 args is passed in, then CommitmentOp will attempt to prove the absence of the key +// in the CommitmentOp and return the CommitmentRoot of the proof +func (op CommitmentOp) Run(args [][]byte) ([][]byte, error) { + // calculate root from proof + root, err := op.Proof.Calculate() + if err != nil { + return nil, sdkerrors.Wrapf(ErrInvalidProof, "could not calculate root for proof: %v", err) + } + // Only support an existence proof or nonexistence proof (batch proofs currently unsupported) + switch len(args) { + case 0: + // Args are nil, so we verify the absence of the key. + absent := ics23.VerifyNonMembership(op.Spec, root, op.Proof, op.Key) + if !absent { + return nil, sdkerrors.Wrapf(ErrInvalidProof, "proof did not verify absence of key: %s", string(op.Key)) + } + + case 1: + // Args is length 1, verify existence of key with value args[0] + if !ics23.VerifyMembership(op.Spec, root, op.Proof, op.Key, args[0]) { + return nil, sdkerrors.Wrapf(ErrInvalidProof, "proof did not verify existence of key %s with given value %x", op.Key, args[0]) + } + default: + return nil, sdkerrors.Wrapf(ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args)) + } + + return [][]byte{root}, nil +} + +// ProofOp implements ProofOperator interface and converts a CommitmentOp +// into a merkle.ProofOp format that can later be decoded by CommitmentOpDecoder +// back into a CommitmentOp for proof verification +func (op CommitmentOp) ProofOp() tmmerkle.ProofOp { + bz, err := op.Proof.Marshal() + if err != nil { + panic(err.Error()) + } + return tmmerkle.ProofOp{ + Type: op.Type, + Key: op.Key, + Data: bz, + } +} diff --git a/store/types/pruning.go b/store/types/pruning.go index c540443521ff..823da76c6585 100644 --- a/store/types/pruning.go +++ b/store/types/pruning.go @@ -1,66 +1,77 @@ package types +import "fmt" + +// Pruning option string constants +const ( + PruningOptionDefault = "default" + PruningOptionEverything = "everything" + PruningOptionNothing = "nothing" + PruningOptionCustom = "custom" +) + var ( - // PruneEverything defines a pruning strategy where all committed states will - // be deleted, persisting only the current state. - PruneEverything = PruningOptions{ - KeepEvery: 1, - SnapshotEvery: 0, - } + // PruneDefault defines a pruning strategy where the last 100 heights are kept + // in addition to every 100th and where to-be pruned heights are pruned at + // every 10th height. + PruneDefault = NewPruningOptions(100, 100, 10) - // PruneNothing defines a pruning strategy where all committed states will be - // kept on disk, i.e. no states will be pruned. - PruneNothing = PruningOptions{ - KeepEvery: 1, - SnapshotEvery: 1, - } + // PruneEverything defines a pruning strategy where all committed heights are + // deleted, storing only the current height and where to-be pruned heights are + // pruned at every 10th height. + PruneEverything = NewPruningOptions(0, 0, 10) - // PruneSyncable defines a pruning strategy where only those states not needed - // for state syncing will be pruned. It flushes every 100th state to disk and - // keeps every 10000th. - PruneSyncable = PruningOptions{ - KeepEvery: 100, - SnapshotEvery: 10000, - } + // PruneNothing defines a pruning strategy where all heights are kept on disk. + PruneNothing = NewPruningOptions(0, 1, 0) ) -// PruningOptions defines the specific pruning strategy every store in a multi-store -// will use when committing state, where keepEvery determines which committed -// heights are flushed to disk and snapshotEvery determines which of these heights -// are kept after pruning. +// PruningOptions defines the pruning strategy used when determining which +// heights are removed from disk when committing state. type PruningOptions struct { - KeepEvery int64 - SnapshotEvery int64 + // KeepRecent defines how many recent heights to keep on disk. + KeepRecent uint64 + + // KeepEvery defines how many offset heights are kept on disk past KeepRecent. + KeepEvery uint64 + + // Interval defines when the pruned heights are removed from disk. + Interval uint64 } -// IsValid verifies if the pruning options are valid. It returns false if invalid -// and true otherwise. Pruning options are considered valid iff: -// -// - KeepEvery > 0 -// - SnapshotEvery >= 0 -// - SnapshotEvery % KeepEvery = 0 -func (po PruningOptions) IsValid() bool { - // must flush at positive block interval - if po.KeepEvery <= 0 { - return false +func NewPruningOptions(keepRecent, keepEvery, interval uint64) PruningOptions { + return PruningOptions{ + KeepRecent: keepRecent, + KeepEvery: keepEvery, + Interval: interval, } +} - // cannot snapshot negative intervals - if po.SnapshotEvery < 0 { - return false +func (po PruningOptions) Validate() error { + if po.KeepEvery == 0 && po.Interval == 0 { + return fmt.Errorf("invalid 'Interval' when pruning everything: %d", po.Interval) + } + if po.KeepEvery == 1 && po.Interval != 0 { // prune nothing + return fmt.Errorf("invalid 'Interval' when pruning nothing: %d", po.Interval) + } + if po.KeepEvery > 1 && po.Interval == 0 { + return fmt.Errorf("invalid 'Interval' when pruning: %d", po.Interval) } - return po.SnapshotEvery%po.KeepEvery == 0 + return nil } -// FlushVersion returns a boolean signaling if the provided version/height should -// be flushed to disk. -func (po PruningOptions) FlushVersion(ver int64) bool { - return po.KeepEvery != 0 && ver%po.KeepEvery == 0 -} +func NewPruningOptionsFromString(strategy string) PruningOptions { + switch strategy { + case PruningOptionEverything: + return PruneEverything -// SnapshotVersion returns a boolean signaling if the provided version/height -// should be snapshotted (kept on disk). -func (po PruningOptions) SnapshotVersion(ver int64) bool { - return po.SnapshotEvery != 0 && ver%po.SnapshotEvery == 0 + case PruningOptionNothing: + return PruneNothing + + case PruningOptionDefault: + return PruneDefault + + default: + return PruneDefault + } } diff --git a/store/types/pruning_test.go b/store/types/pruning_test.go new file mode 100644 index 000000000000..2b88905ee8d2 --- /dev/null +++ b/store/types/pruning_test.go @@ -0,0 +1,29 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPruningOptions_Validate(t *testing.T) { + testCases := []struct { + keepRecent uint64 + keepEvery uint64 + interval uint64 + expectErr bool + }{ + {100, 500, 10, false}, // default + {0, 0, 10, false}, // everything + {0, 1, 0, false}, // nothing + {0, 10, 10, false}, + {100, 0, 0, true}, // invalid interval + {0, 1, 5, true}, // invalid interval + } + + for _, tc := range testCases { + po := NewPruningOptions(tc.keepRecent, tc.keepEvery, tc.interval) + err := po.Validate() + require.Equal(t, tc.expectErr, err != nil, "options: %v, err: %s", po, err) + } +} diff --git a/store/types/snapshot.pb.go b/store/types/snapshot.pb.go new file mode 100644 index 000000000000..6f001da1bf28 --- /dev/null +++ b/store/types/snapshot.pb.go @@ -0,0 +1,944 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/store/v1beta1/snapshot.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 + +// SnapshotItem is an item contained in a rootmulti.Store snapshot. +type SnapshotItem struct { + // item is the specific type of snapshot item. + // + // Types that are valid to be assigned to Item: + // *SnapshotItem_Store + // *SnapshotItem_IAVL + Item isSnapshotItem_Item `protobuf_oneof:"item"` +} + +func (m *SnapshotItem) Reset() { *m = SnapshotItem{} } +func (m *SnapshotItem) String() string { return proto.CompactTextString(m) } +func (*SnapshotItem) ProtoMessage() {} +func (*SnapshotItem) Descriptor() ([]byte, []int) { + return fileDescriptor_9c55879db4cc4502, []int{0} +} +func (m *SnapshotItem) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotItem.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 *SnapshotItem) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotItem.Merge(m, src) +} +func (m *SnapshotItem) XXX_Size() int { + return m.Size() +} +func (m *SnapshotItem) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotItem.DiscardUnknown(m) +} + +var xxx_messageInfo_SnapshotItem proto.InternalMessageInfo + +type isSnapshotItem_Item interface { + isSnapshotItem_Item() + MarshalTo([]byte) (int, error) + Size() int +} + +type SnapshotItem_Store struct { + Store *SnapshotStoreItem `protobuf:"bytes,1,opt,name=store,proto3,oneof" json:"store,omitempty"` +} +type SnapshotItem_IAVL struct { + IAVL *SnapshotIAVLItem `protobuf:"bytes,2,opt,name=iavl,proto3,oneof" json:"iavl,omitempty"` +} + +func (*SnapshotItem_Store) isSnapshotItem_Item() {} +func (*SnapshotItem_IAVL) isSnapshotItem_Item() {} + +func (m *SnapshotItem) GetItem() isSnapshotItem_Item { + if m != nil { + return m.Item + } + return nil +} + +func (m *SnapshotItem) GetStore() *SnapshotStoreItem { + if x, ok := m.GetItem().(*SnapshotItem_Store); ok { + return x.Store + } + return nil +} + +func (m *SnapshotItem) GetIAVL() *SnapshotIAVLItem { + if x, ok := m.GetItem().(*SnapshotItem_IAVL); ok { + return x.IAVL + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SnapshotItem) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SnapshotItem_Store)(nil), + (*SnapshotItem_IAVL)(nil), + } +} + +// SnapshotStoreItem contains metadata about a snapshotted store. +type SnapshotStoreItem struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *SnapshotStoreItem) Reset() { *m = SnapshotStoreItem{} } +func (m *SnapshotStoreItem) String() string { return proto.CompactTextString(m) } +func (*SnapshotStoreItem) ProtoMessage() {} +func (*SnapshotStoreItem) Descriptor() ([]byte, []int) { + return fileDescriptor_9c55879db4cc4502, []int{1} +} +func (m *SnapshotStoreItem) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotStoreItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotStoreItem.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 *SnapshotStoreItem) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotStoreItem.Merge(m, src) +} +func (m *SnapshotStoreItem) XXX_Size() int { + return m.Size() +} +func (m *SnapshotStoreItem) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotStoreItem.DiscardUnknown(m) +} + +var xxx_messageInfo_SnapshotStoreItem proto.InternalMessageInfo + +func (m *SnapshotStoreItem) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// SnapshotIAVLItem is an exported IAVL node. +type SnapshotIAVLItem struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Version int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` + Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *SnapshotIAVLItem) Reset() { *m = SnapshotIAVLItem{} } +func (m *SnapshotIAVLItem) String() string { return proto.CompactTextString(m) } +func (*SnapshotIAVLItem) ProtoMessage() {} +func (*SnapshotIAVLItem) Descriptor() ([]byte, []int) { + return fileDescriptor_9c55879db4cc4502, []int{2} +} +func (m *SnapshotIAVLItem) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotIAVLItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotIAVLItem.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 *SnapshotIAVLItem) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotIAVLItem.Merge(m, src) +} +func (m *SnapshotIAVLItem) XXX_Size() int { + return m.Size() +} +func (m *SnapshotIAVLItem) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotIAVLItem.DiscardUnknown(m) +} + +var xxx_messageInfo_SnapshotIAVLItem proto.InternalMessageInfo + +func (m *SnapshotIAVLItem) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *SnapshotIAVLItem) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *SnapshotIAVLItem) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *SnapshotIAVLItem) GetHeight() int32 { + if m != nil { + return m.Height + } + return 0 +} + +func init() { + proto.RegisterType((*SnapshotItem)(nil), "cosmos.base.store.v1beta1.SnapshotItem") + proto.RegisterType((*SnapshotStoreItem)(nil), "cosmos.base.store.v1beta1.SnapshotStoreItem") + proto.RegisterType((*SnapshotIAVLItem)(nil), "cosmos.base.store.v1beta1.SnapshotIAVLItem") +} + +func init() { + proto.RegisterFile("cosmos/base/store/v1beta1/snapshot.proto", fileDescriptor_9c55879db4cc4502) +} + +var fileDescriptor_9c55879db4cc4502 = []byte{ + // 324 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4a, 0xc3, 0x30, + 0x18, 0xc7, 0x1b, 0xd7, 0x4d, 0xfd, 0xdc, 0x61, 0x86, 0x21, 0xd5, 0x43, 0x1d, 0xbb, 0x58, 0x50, + 0x13, 0xa6, 0x4f, 0x60, 0xf1, 0xb0, 0xa1, 0xa7, 0x0c, 0x3c, 0x78, 0x4b, 0x67, 0x68, 0xcb, 0xd6, + 0x65, 0x2c, 0x59, 0x61, 0x6f, 0xe1, 0x6b, 0xf8, 0x26, 0x1e, 0x77, 0xf4, 0x24, 0xd2, 0xbd, 0x88, + 0x24, 0xe9, 0x2e, 0x8a, 0xe0, 0xa9, 0xdf, 0xbf, 0xfc, 0xfe, 0xbf, 0x7c, 0xf0, 0x41, 0x34, 0x91, + 0xaa, 0x90, 0x8a, 0x26, 0x5c, 0x09, 0xaa, 0xb4, 0x5c, 0x0a, 0x5a, 0x0e, 0x12, 0xa1, 0xf9, 0x80, + 0xaa, 0x39, 0x5f, 0xa8, 0x4c, 0x6a, 0xb2, 0x58, 0x4a, 0x2d, 0xf1, 0xa9, 0x23, 0x89, 0x21, 0x89, + 0x25, 0x49, 0x4d, 0x9e, 0x75, 0x53, 0x99, 0x4a, 0x4b, 0x51, 0x33, 0xb9, 0x42, 0xff, 0x0d, 0x41, + 0x7b, 0x5c, 0x3b, 0x46, 0x5a, 0x14, 0xf8, 0x1e, 0x9a, 0xb6, 0x17, 0xa0, 0x1e, 0x8a, 0x8e, 0x6e, + 0xae, 0xc8, 0x9f, 0x46, 0xb2, 0xeb, 0x8d, 0xcd, 0x5f, 0x53, 0x1e, 0x7a, 0xcc, 0x95, 0xf1, 0x03, + 0xf8, 0x39, 0x2f, 0x67, 0xc1, 0x9e, 0x95, 0x5c, 0xfe, 0x43, 0x32, 0xba, 0x7b, 0x7a, 0x34, 0x8e, + 0xf8, 0xa0, 0xfa, 0x3c, 0xf7, 0x4d, 0x1a, 0x7a, 0xcc, 0x4a, 0xe2, 0x16, 0xf8, 0xb9, 0x16, 0x45, + 0xff, 0x02, 0x8e, 0x7f, 0x3d, 0x89, 0x31, 0xf8, 0x73, 0x5e, 0xb8, 0x75, 0x0f, 0x99, 0x9d, 0xfb, + 0x33, 0xe8, 0xfc, 0xd4, 0xe2, 0x0e, 0x34, 0xa6, 0x62, 0x6d, 0xb1, 0x36, 0x33, 0x23, 0xee, 0x42, + 0xb3, 0xe4, 0xb3, 0x95, 0xb0, 0x4b, 0xb6, 0x99, 0x0b, 0x38, 0x80, 0xfd, 0x52, 0x2c, 0x55, 0x2e, + 0xe7, 0x41, 0xa3, 0x87, 0xa2, 0x06, 0xdb, 0x45, 0x7c, 0x02, 0xad, 0x4c, 0xe4, 0x69, 0xa6, 0x03, + 0xbf, 0x87, 0xa2, 0x26, 0xab, 0x53, 0x1c, 0xbf, 0x57, 0x21, 0xda, 0x54, 0x21, 0xfa, 0xaa, 0x42, + 0xf4, 0xba, 0x0d, 0xbd, 0xcd, 0x36, 0xf4, 0x3e, 0xb6, 0xa1, 0xf7, 0x1c, 0xa5, 0xb9, 0xce, 0x56, + 0x09, 0x99, 0xc8, 0x82, 0xd6, 0x27, 0x74, 0x9f, 0x6b, 0xf5, 0x32, 0xad, 0x0f, 0xa9, 0xd7, 0x0b, + 0xa1, 0x92, 0x96, 0xbd, 0xc6, 0xed, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x75, 0x87, 0x24, 0x7b, + 0xea, 0x01, 0x00, 0x00, +} + +func (m *SnapshotItem) 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 *SnapshotItem) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotItem) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Item != nil { + { + size := m.Item.Size() + i -= size + if _, err := m.Item.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *SnapshotItem_Store) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotItem_Store) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Store != nil { + { + size, err := m.Store.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *SnapshotItem_IAVL) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotItem_IAVL) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.IAVL != nil { + { + size, err := m.IAVL.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *SnapshotStoreItem) 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 *SnapshotStoreItem) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotStoreItem) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SnapshotIAVLItem) 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 *SnapshotIAVLItem) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotIAVLItem) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x20 + } + if m.Version != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x18 + } + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSnapshot(dAtA []byte, offset int, v uint64) int { + offset -= sovSnapshot(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SnapshotItem) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Item != nil { + n += m.Item.Size() + } + return n +} + +func (m *SnapshotItem_Store) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Store != nil { + l = m.Store.Size() + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} +func (m *SnapshotItem_IAVL) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IAVL != nil { + l = m.IAVL.Size() + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} +func (m *SnapshotStoreItem) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} + +func (m *SnapshotIAVLItem) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + if m.Version != 0 { + n += 1 + sovSnapshot(uint64(m.Version)) + } + if m.Height != 0 { + n += 1 + sovSnapshot(uint64(m.Height)) + } + return n +} + +func sovSnapshot(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSnapshot(x uint64) (n int) { + return sovSnapshot(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SnapshotItem) 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 ErrIntOverflowSnapshot + } + 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: SnapshotItem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotItem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Store", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SnapshotStoreItem{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Item = &SnapshotItem_Store{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IAVL", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SnapshotIAVLItem{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Item = &SnapshotItem_IAVL{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SnapshotStoreItem) 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 ErrIntOverflowSnapshot + } + 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: SnapshotStoreItem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotStoreItem: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + 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 ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SnapshotIAVLItem) 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 ErrIntOverflowSnapshot + } + 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: SnapshotIAVLItem: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotIAVLItem: 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 ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSnapshot(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, ErrIntOverflowSnapshot + } + 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, ErrIntOverflowSnapshot + } + 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, ErrIntOverflowSnapshot + } + 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, ErrInvalidLengthSnapshot + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSnapshot + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSnapshot + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSnapshot = fmt.Errorf("proto: unexpected end of group") +) diff --git a/store/types/store.go b/store/types/store.go index 377b43909e11..8da2b26fd1f8 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -5,11 +5,14 @@ import ( "io" abci "github.com/tendermint/tendermint/abci/types" - tmkv "github.com/tendermint/tendermint/libs/kv" + tmstrings "github.com/tendermint/tendermint/libs/strings" dbm "github.com/tendermint/tm-db" + + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/types/kv" ) -type Store interface { //nolint +type Store interface { GetStoreType() StoreType CacheWrapper } @@ -18,7 +21,9 @@ type Store interface { //nolint type Committer interface { Commit() CommitID LastCommitID() CommitID + SetPruning(PruningOptions) + GetPruning() PruningOptions } // Stores of MultiStore must implement CommitStore. @@ -40,10 +45,18 @@ type Queryable interface { // StoreUpgrades defines a series of transformations to apply the multistore db upon load type StoreUpgrades struct { + Added []string `json:"added"` Renamed []StoreRename `json:"renamed"` Deleted []string `json:"deleted"` } +// UpgradeInfo defines height and name of the upgrade +// to ensure multistore upgrades happen only at matching height. +type UpgradeInfo struct { + Name string `json:"name"` + Height int64 `json:"height"` +} + // StoreRename defines a name change of a sub-store. // All data previously under a PrefixStore with OldKey will be copied // to a PrefixStore with NewKey, then deleted from OldKey store. @@ -52,6 +65,14 @@ type StoreRename struct { NewKey string `json:"new_key"` } +// IsDeleted returns true if the given key should be added +func (s *StoreUpgrades) IsAdded(key string) bool { + if s == nil { + return false + } + return tmstrings.StringInSlice(key, s.Added) +} + // IsDeleted returns true if the given key should be deleted func (s *StoreUpgrades) IsDeleted(key string) bool { if s == nil { @@ -80,15 +101,15 @@ func (s *StoreUpgrades) RenamedFrom(key string) string { } -type MultiStore interface { //nolint +type MultiStore interface { Store - // Cache wrap MultiStore. + // Branches MultiStore into a cached storage object. // NOTE: Caller should probably not call .Write() on each, but // call CacheMultiStore.Write(). CacheMultiStore() CacheMultiStore - // CacheMultiStoreWithVersion cache-wraps the underlying MultiStore where + // CacheMultiStoreWithVersion branches the underlying MultiStore where // each stored is loaded at a specific version (height). CacheMultiStoreWithVersion(version int64) (CacheMultiStore, error) @@ -117,10 +138,11 @@ type CacheMultiStore interface { Write() // Writes operations to underlying KVStore } -// A non-cache MultiStore. +// CommitMultiStore is an interface for a MultiStore without cache capabilities. type CommitMultiStore interface { Committer MultiStore + snapshottypes.Snapshotter // Mount a store of type using the given db. // If db == nil, the new store will use the CommitMultiStore db. @@ -155,6 +177,10 @@ type CommitMultiStore interface { // Set an inter-block (persistent) cache that maintains a mapping from // StoreKeys to CommitKVStores. SetInterBlockCache(MultiStorePersistentCache) + + // SetInitialVersion sets the initial version of the IAVL tree. It is used when + // starting a new chain at an arbitrary height. + SetInitialVersion(version int64) error } //---------subsp------------------------------- @@ -192,12 +218,12 @@ type KVStore interface { ReverseIterator(start, end []byte) Iterator } -// Alias iterator to db's Iterator for convenience. +// Iterator is an alias db's Iterator for convenience. type Iterator = dbm.Iterator -// CacheKVStore cache-wraps a KVStore. After calling .Write() on -// the CacheKVStore, all previously created CacheKVStores on the -// object expire. +// CacheKVStore branches a KVStore and provides read cache functionality. +// After calling .Write() on the CacheKVStore, all previously created +// CacheKVStores on the object expire. type CacheKVStore interface { KVStore @@ -205,7 +231,7 @@ type CacheKVStore interface { Write() } -// Stores of MultiStore must implement CommitStore. +// CommitKVStore is an interface for MultiStore. type CommitKVStore interface { Committer KVStore @@ -214,9 +240,9 @@ type CommitKVStore interface { //---------------------------------------- // CacheWrap -// CacheWrap makes the most appropriate cache-wrap. For example, -// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return -// a Committer, since Commit cache-wraps make no sense. It can return KVStore, +// CacheWrap is the most appropriate interface for store ephemeral branching and cache. +// For example, IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return +// a Committer, since Commit ephemeral store make no sense. It can return KVStore, // HeapStore, SpaceStore, etc. type CacheWrap interface { // Write syncs with the underlying store. @@ -229,24 +255,15 @@ type CacheWrap interface { CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap } -type CacheWrapper interface { //nolint - // CacheWrap cache wraps. +type CacheWrapper interface { + // CacheWrap branches a store. CacheWrap() CacheWrap - // CacheWrapWithTrace cache wraps with tracing enabled. + // CacheWrapWithTrace branches a store with tracing enabled. CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap } -//---------------------------------------- -// CommitID - -// CommitID contains the tree version number and its merkle root. -type CommitID struct { - Version int64 - Hash []byte -} - -func (cid CommitID) IsZero() bool { //nolint +func (cid CommitID) IsZero() bool { return cid.Version == 0 && len(cid.Hash) == 0 } @@ -261,13 +278,34 @@ func (cid CommitID) String() string { type StoreType int const ( - //nolint StoreTypeMulti StoreType = iota StoreTypeDB StoreTypeIAVL StoreTypeTransient + StoreTypeMemory ) +func (st StoreType) String() string { + switch st { + case StoreTypeMulti: + return "StoreTypeMulti" + + case StoreTypeDB: + return "StoreTypeDB" + + case StoreTypeIAVL: + return "StoreTypeIAVL" + + case StoreTypeTransient: + return "StoreTypeTransient" + + case StoreTypeMemory: + return "StoreTypeMemory" + } + + return "unknown store type" +} + //---------------------------------------- // Keys for accessing substores @@ -277,6 +315,10 @@ type StoreKey interface { String() string } +// CapabilityKey represent the Cosmos SDK keys for object-capability +// generation in the IBC protocol as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#data-structures +type CapabilityKey StoreKey + // KVStoreKey is used for accessing substores. // Only the pointer value should ever be used - it functions as a capabilities key. type KVStoreKey struct { @@ -286,6 +328,9 @@ type KVStoreKey struct { // NewKVStoreKey returns a new pointer to a KVStoreKey. // Use a pointer so keys don't collide. func NewKVStoreKey(name string) *KVStoreKey { + if name == "" { + panic("empty key name not allowed") + } return &KVStoreKey{ name: name, } @@ -322,10 +367,29 @@ func (key *TransientStoreKey) String() string { return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) } +// MemoryStoreKey defines a typed key to be used with an in-memory KVStore. +type MemoryStoreKey struct { + name string +} + +func NewMemoryStoreKey(name string) *MemoryStoreKey { + return &MemoryStoreKey{name: name} +} + +// Name returns the name of the MemoryStoreKey. +func (key *MemoryStoreKey) Name() string { + return key.name +} + +// String returns a stringified representation of the MemoryStoreKey. +func (key *MemoryStoreKey) String() string { + return fmt.Sprintf("MemoryStoreKey{%p, %s}", key, key.name) +} + //---------------------------------------- // key-value result for iterator queries -type KVPair tmkv.Pair +type KVPair kv.Pair //---------------------------------------- @@ -346,3 +410,11 @@ type MultiStorePersistentCache interface { // Reset the entire set of internal caches. Reset() } + +// StoreWithInitialVersion is a store that can have an arbitrary initial +// version. +type StoreWithInitialVersion interface { + // SetInitialVersion sets the initial version of the IAVL tree. It is used when + // starting a new chain at an arbitrary height. + SetInitialVersion(version int64) +} diff --git a/store/types/store_test.go b/store/types/store_test.go index 7ef1d6e8896f..f86144af7fbd 100644 --- a/store/types/store_test.go +++ b/store/types/store_test.go @@ -1,12 +1,15 @@ package types import ( + "fmt" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestStoreUpgrades(t *testing.T) { + t.Parallel() type toDelete struct { key string delete bool @@ -55,3 +58,35 @@ func TestStoreUpgrades(t *testing.T) { }) } } + +func TestCommitID(t *testing.T) { + t.Parallel() + require.True(t, CommitID{}.IsZero()) + require.False(t, CommitID{Version: int64(1)}.IsZero()) + require.False(t, CommitID{Hash: []byte("x")}.IsZero()) + require.Equal(t, "CommitID{[120 120 120 120]:64}", CommitID{Version: int64(100), Hash: []byte("xxxx")}.String()) +} + +func TestKVStoreKey(t *testing.T) { + t.Parallel() + key := NewKVStoreKey("test") + require.Equal(t, "test", key.name) + require.Equal(t, key.name, key.Name()) + require.Equal(t, fmt.Sprintf("KVStoreKey{%p, test}", key), key.String()) +} + +func TestNilKVStoreKey(t *testing.T) { + t.Parallel() + + require.Panics(t, func() { + _ = NewKVStoreKey("") + }, "setting an empty key should panic") +} + +func TestTransientStoreKey(t *testing.T) { + t.Parallel() + key := NewTransientStoreKey("test") + require.Equal(t, "test", key.name) + require.Equal(t, key.name, key.Name()) + require.Equal(t, fmt.Sprintf("TransientStoreKey{%p, test}", key), key.String()) +} diff --git a/store/types/utils.go b/store/types/utils.go index 3149c07fb61a..22c8ca0761f9 100644 --- a/store/types/utils.go +++ b/store/types/utils.go @@ -3,7 +3,7 @@ package types import ( "bytes" - tmkv "github.com/tendermint/tendermint/libs/kv" + "github.com/cosmos/cosmos-sdk/types/kv" ) // Iterator over all the keys with a certain prefix in ascending order @@ -17,41 +17,49 @@ func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { } // DiffKVStores compares two KVstores and returns all the key/value pairs -// that differ from one another. It also skips value comparison for a set of provided prefixes -func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []tmkv.Pair) { +// that differ from one another. It also skips value comparison for a set of provided prefixes. +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []kv.Pair) { iterA := a.Iterator(nil, nil) + + defer iterA.Close() + iterB := b.Iterator(nil, nil) + defer iterB.Close() + for { if !iterA.Valid() && !iterB.Valid() { - break + return kvAs, kvBs } - var kvA, kvB tmkv.Pair + + var kvA, kvB kv.Pair if iterA.Valid() { - kvA = tmkv.Pair{Key: iterA.Key(), Value: iterA.Value()} + kvA = kv.Pair{Key: iterA.Key(), Value: iterA.Value()} + iterA.Next() } + if iterB.Valid() { - kvB = tmkv.Pair{Key: iterB.Key(), Value: iterB.Value()} + kvB = kv.Pair{Key: iterB.Key(), Value: iterB.Value()} + iterB.Next() } - if !bytes.Equal(kvA.Key, kvB.Key) { - kvAs = append(kvAs, kvA) - kvBs = append(kvBs, kvB) - } + compareValue := true + for _, prefix := range prefixesToSkip { // Skip value comparison if we matched a prefix - if bytes.Equal(kvA.Key[:len(prefix)], prefix) { + if bytes.HasPrefix(kvA.Key, prefix) || bytes.HasPrefix(kvB.Key, prefix) { compareValue = false + break } } - if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + + if compareValue && (!bytes.Equal(kvA.Key, kvB.Key) || !bytes.Equal(kvA.Value, kvB.Value)) { kvAs = append(kvAs, kvA) kvBs = append(kvBs, kvB) } } - return kvAs, kvBs } // PrefixEndBytes returns the []byte that would end a @@ -69,14 +77,16 @@ func PrefixEndBytes(prefix []byte) []byte { if end[len(end)-1] != byte(255) { end[len(end)-1]++ break - } else { - end = end[:len(end)-1] - if len(end) == 0 { - end = nil - break - } + } + + end = end[:len(end)-1] + + if len(end) == 0 { + end = nil + break } } + return end } @@ -85,13 +95,3 @@ func PrefixEndBytes(prefix []byte) []byte { func InclusiveEndBytes(inclusiveBytes []byte) []byte { return append(inclusiveBytes, byte(0x00)) } - -//---------------------------------------- -func Cp(bz []byte) (ret []byte) { - if bz == nil { - return nil - } - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} diff --git a/store/types/utils_test.go b/store/types/utils_test.go new file mode 100644 index 000000000000..32064d7e1821 --- /dev/null +++ b/store/types/utils_test.go @@ -0,0 +1,88 @@ +package types_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/types" +) + +func initTestStores(t *testing.T) (types.KVStore, types.KVStore) { + db := dbm.NewMemDB() + ms := rootmulti.NewStore(db) + + key1 := types.NewKVStoreKey("store1") + key2 := types.NewKVStoreKey("store2") + require.NotPanics(t, func() { ms.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.NotPanics(t, func() { ms.MountStoreWithDB(key2, types.StoreTypeIAVL, db) }) + require.NoError(t, ms.LoadLatestVersion()) + return ms.GetKVStore(key1), ms.GetKVStore(key2) +} + +func TestDiffKVStores(t *testing.T) { + t.Parallel() + store1, store2 := initTestStores(t) + // Two equal stores + k1, v1 := []byte("k1"), []byte("v1") + store1.Set(k1, v1) + store2.Set(k1, v1) + + kvAs, kvBs := types.DiffKVStores(store1, store2, nil) + require.Equal(t, 0, len(kvAs)) + require.Equal(t, len(kvAs), len(kvBs)) + + // delete k1 from store2, which is now empty + store2.Delete(k1) + kvAs, kvBs = types.DiffKVStores(store1, store2, nil) + require.Equal(t, 1, len(kvAs)) + require.Equal(t, len(kvAs), len(kvBs)) + + // set k1 in store2, different value than what store1 holds for k1 + v2 := []byte("v2") + store2.Set(k1, v2) + kvAs, kvBs = types.DiffKVStores(store1, store2, nil) + require.Equal(t, 1, len(kvAs)) + require.Equal(t, len(kvAs), len(kvBs)) + + // add k2 to store2 + k2 := []byte("k2") + store2.Set(k2, v2) + kvAs, kvBs = types.DiffKVStores(store1, store2, nil) + require.Equal(t, 2, len(kvAs)) + require.Equal(t, len(kvAs), len(kvBs)) + + // Reset stores + store1.Delete(k1) + store2.Delete(k1) + store2.Delete(k2) + + // Same keys, different value. Comparisons will be nil as prefixes are skipped. + prefix := []byte("prefix:") + k1Prefixed := append(prefix, k1...) + store1.Set(k1Prefixed, v1) + store2.Set(k1Prefixed, v2) + kvAs, kvBs = types.DiffKVStores(store1, store2, [][]byte{prefix}) + require.Equal(t, 0, len(kvAs)) + require.Equal(t, len(kvAs), len(kvBs)) +} + +func TestPrefixEndBytes(t *testing.T) { + t.Parallel() + bs1 := []byte{0x23, 0xA5, 0x06} + require.True(t, bytes.Equal([]byte{0x23, 0xA5, 0x07}, types.PrefixEndBytes(bs1))) + bs2 := []byte{0x23, 0xA5, 0xFF} + require.True(t, bytes.Equal([]byte{0x23, 0xA6}, types.PrefixEndBytes(bs2))) + require.Nil(t, types.PrefixEndBytes([]byte{0xFF})) + require.Nil(t, types.PrefixEndBytes(nil)) +} + +func TestInclusiveEndBytes(t *testing.T) { + t.Parallel() + require.True(t, bytes.Equal([]byte{0x00}, types.InclusiveEndBytes(nil))) + bs := []byte("test") + require.True(t, bytes.Equal(append(bs, byte(0x00)), types.InclusiveEndBytes(bs))) +} diff --git a/store/types/validity.go b/store/types/validity.go index 59dbd0c032bd..ef2387a69269 100644 --- a/store/types/validity.go +++ b/store/types/validity.go @@ -2,7 +2,7 @@ package types // Check if the key is valid(key is not nil) func AssertValidKey(key []byte) { - if key == nil { + if len(key) == 0 { panic("key is nil") } } diff --git a/store/types/validity_test.go b/store/types/validity_test.go new file mode 100644 index 000000000000..0f5d0466a346 --- /dev/null +++ b/store/types/validity_test.go @@ -0,0 +1,23 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func TestAssertValidKey(t *testing.T) { + t.Parallel() + require.NotPanics(t, func() { types.AssertValidKey([]byte{0x01}) }) + require.Panics(t, func() { types.AssertValidKey([]byte{}) }) + require.Panics(t, func() { types.AssertValidKey(nil) }) +} + +func TestAssertValidValue(t *testing.T) { + t.Parallel() + require.NotPanics(t, func() { types.AssertValidValue([]byte{}) }) + require.NotPanics(t, func() { types.AssertValidValue([]byte{0x01}) }) + require.Panics(t, func() { types.AssertValidValue(nil) }) +} diff --git a/telemetry/metrics.go b/telemetry/metrics.go new file mode 100644 index 000000000000..ca9bf7890883 --- /dev/null +++ b/telemetry/metrics.go @@ -0,0 +1,171 @@ +package telemetry + +import ( + "bytes" + "encoding/json" + "fmt" + "time" + + metrics "github.com/armon/go-metrics" + metricsprom "github.com/armon/go-metrics/prometheus" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/expfmt" +) + +// globalLabels defines the set of global labels that will be applied to all +// metrics emitted using the telemetry package function wrappers. +var globalLabels = []metrics.Label{} + +// Metrics supported format types. +const ( + FormatDefault = "" + FormatPrometheus = "prometheus" + FormatText = "text" +) + +// Config defines the configuration options for application telemetry. +type Config struct { + // Prefixed with keys to separate services + ServiceName string `mapstructure:"service-name"` + + // Enabled enables the application telemetry functionality. When enabled, + // an in-memory sink is also enabled by default. Operators may also enabled + // other sinks such as Prometheus. + Enabled bool `mapstructure:"enabled"` + + // Enable prefixing gauge values with hostname + EnableHostname bool `mapstructure:"enable-hostname"` + + // Enable adding hostname to labels + EnableHostnameLabel bool `mapstructure:"enable-hostname-label"` + + // Enable adding service to labels + EnableServiceLabel bool `mapstructure:"enable-service-label"` + + // PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. + // It defines the retention duration in seconds. + PrometheusRetentionTime int64 `mapstructure:"prometheus-retention-time"` + + // GlobalLabels defines a global set of name/value label tuples applied to all + // metrics emitted using the wrapper functions defined in telemetry package. + // + // Example: + // [["chain_id", "cosmoshub-1"]] + GlobalLabels [][]string `mapstructure:"global-labels"` +} + +// Metrics defines a wrapper around application telemetry functionality. It allows +// metrics to be gathered at any point in time. When creating a Metrics object, +// internally, a global metrics is registered with a set of sinks as configured +// by the operator. In addition to the sinks, when a process gets a SIGUSR1, a +// dump of formatted recent metrics will be sent to STDERR. +type Metrics struct { + memSink *metrics.InmemSink + prometheusEnabled bool +} + +type GatherResponse struct { + Metrics []byte + ContentType string +} + +func New(cfg Config) (*Metrics, error) { + if !cfg.Enabled { + return nil, nil + } + + if numGlobalLables := len(cfg.GlobalLabels); numGlobalLables > 0 { + parsedGlobalLabels := make([]metrics.Label, numGlobalLables) + for i, gl := range cfg.GlobalLabels { + parsedGlobalLabels[i] = NewLabel(gl[0], gl[1]) + } + + globalLabels = parsedGlobalLabels + } + + metricsConf := metrics.DefaultConfig(cfg.ServiceName) + metricsConf.EnableHostname = cfg.EnableHostname + metricsConf.EnableHostnameLabel = cfg.EnableHostnameLabel + + memSink := metrics.NewInmemSink(10*time.Second, time.Minute) + metrics.DefaultInmemSignal(memSink) + + m := &Metrics{memSink: memSink} + fanout := metrics.FanoutSink{memSink} + + if cfg.PrometheusRetentionTime > 0 { + m.prometheusEnabled = true + prometheusOpts := metricsprom.PrometheusOpts{ + Expiration: time.Duration(cfg.PrometheusRetentionTime) * time.Second, + } + + promSink, err := metricsprom.NewPrometheusSinkFrom(prometheusOpts) + if err != nil { + return nil, err + } + + fanout = append(fanout, promSink) + } + + if _, err := metrics.NewGlobal(metricsConf, fanout); err != nil { + return nil, err + } + + return m, nil +} + +// Gather collects all registered metrics and returns a GatherResponse where the +// metrics are encoded depending on the type. Metrics are either encoded via +// Prometheus or JSON if in-memory. +func (m *Metrics) Gather(format string) (GatherResponse, error) { + switch format { + case FormatPrometheus: + return m.gatherPrometheus() + + case FormatText: + return m.gatherGeneric() + + case FormatDefault: + return m.gatherGeneric() + + default: + return GatherResponse{}, fmt.Errorf("unsupported metrics format: %s", format) + } +} + +func (m *Metrics) gatherPrometheus() (GatherResponse, error) { + if !m.prometheusEnabled { + return GatherResponse{}, fmt.Errorf("prometheus metrics are not enabled") + } + + metricsFamilies, err := prometheus.DefaultGatherer.Gather() + if err != nil { + return GatherResponse{}, fmt.Errorf("failed to gather prometheus metrics: %w", err) + } + + buf := &bytes.Buffer{} + defer buf.Reset() + + e := expfmt.NewEncoder(buf, expfmt.FmtText) + for _, mf := range metricsFamilies { + if err := e.Encode(mf); err != nil { + return GatherResponse{}, fmt.Errorf("failed to encode prometheus metrics: %w", err) + } + } + + return GatherResponse{ContentType: string(expfmt.FmtText), Metrics: buf.Bytes()}, nil +} + +func (m *Metrics) gatherGeneric() (GatherResponse, error) { + summary, err := m.memSink.DisplayMetrics(nil, nil) + if err != nil { + return GatherResponse{}, fmt.Errorf("failed to gather in-memory metrics: %w", err) + } + + content, err := json.Marshal(summary) + if err != nil { + return GatherResponse{}, fmt.Errorf("failed to encode in-memory metrics: %w", err) + } + + return GatherResponse{ContentType: "application/json", Metrics: content}, nil +} diff --git a/telemetry/metrics_test.go b/telemetry/metrics_test.go new file mode 100644 index 000000000000..39ffd85b9017 --- /dev/null +++ b/telemetry/metrics_test.go @@ -0,0 +1,76 @@ +package telemetry + +import ( + "encoding/json" + "strings" + "testing" + "time" + + metrics "github.com/armon/go-metrics" + "github.com/prometheus/common/expfmt" + "github.com/stretchr/testify/require" +) + +func TestMetrics_Disabled(t *testing.T) { + m, err := New(Config{Enabled: false}) + require.Nil(t, m) + require.Nil(t, err) +} + +func TestMetrics_InMem(t *testing.T) { + m, err := New(Config{ + Enabled: true, + EnableHostname: false, + ServiceName: "test", + }) + require.NoError(t, err) + require.NotNil(t, m) + + emitMetrics() + + gr, err := m.Gather(FormatText) + require.NoError(t, err) + require.Equal(t, gr.ContentType, "application/json") + + jsonMetrics := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(gr.Metrics, &jsonMetrics)) + + counters := jsonMetrics["Counters"].([]interface{}) + require.Equal(t, counters[0].(map[string]interface{})["Count"].(float64), 10.0) + require.Equal(t, counters[0].(map[string]interface{})["Name"].(string), "test.dummy_counter") +} + +func TestMetrics_Prom(t *testing.T) { + m, err := New(Config{ + Enabled: true, + EnableHostname: false, + ServiceName: "test", + PrometheusRetentionTime: 60, + EnableHostnameLabel: false, + }) + require.NoError(t, err) + require.NotNil(t, m) + require.True(t, m.prometheusEnabled) + + emitMetrics() + + gr, err := m.Gather(FormatPrometheus) + require.NoError(t, err) + require.Equal(t, gr.ContentType, string(expfmt.FmtText)) + + require.True(t, strings.Contains(string(gr.Metrics), "test_dummy_counter 30")) +} + +func emitMetrics() { + ticker := time.NewTicker(time.Second) + timeout := time.After(30 * time.Second) + + for { + select { + case <-ticker.C: + metrics.IncrCounter([]string{"dummy_counter"}, 1.0) + case <-timeout: + return + } + } +} diff --git a/telemetry/wrapper.go b/telemetry/wrapper.go new file mode 100644 index 000000000000..62f50eeacb93 --- /dev/null +++ b/telemetry/wrapper.go @@ -0,0 +1,70 @@ +package telemetry + +import ( + "time" + + metrics "github.com/armon/go-metrics" +) + +// Common metric key constants +const ( + MetricKeyBeginBlocker = "begin_blocker" + MetricKeyEndBlocker = "end_blocker" + MetricLabelNameModule = "module" +) + +func NewLabel(name, value string) metrics.Label { + return metrics.Label{Name: name, Value: value} +} + +// ModuleMeasureSince provides a short hand method for emitting a time measure +// metric for a module with a given set of keys. If any global labels are defined, +// they will be added to the module label. +func ModuleMeasureSince(module string, start time.Time, keys ...string) { + metrics.MeasureSinceWithLabels( + keys, + start.UTC(), + append([]metrics.Label{NewLabel(MetricLabelNameModule, module)}, globalLabels...), + ) +} + +// ModuleSetGauge provides a short hand method for emitting a gauge metric for a +// module with a given set of keys. If any global labels are defined, they will +// be added to the module label. +func ModuleSetGauge(module string, val float32, keys ...string) { + metrics.SetGaugeWithLabels( + keys, + val, + append([]metrics.Label{NewLabel(MetricLabelNameModule, module)}, globalLabels...), + ) +} + +// IncrCounter provides a wrapper functionality for emitting a counter metric with +// global labels (if any). +func IncrCounter(val float32, keys ...string) { + metrics.IncrCounterWithLabels(keys, val, globalLabels) +} + +// IncrCounterWithLabels provides a wrapper functionality for emitting a counter +// metric with global labels (if any) along with the provided labels. +func IncrCounterWithLabels(keys []string, val float32, labels []metrics.Label) { + metrics.IncrCounterWithLabels(keys, val, append(labels, globalLabels...)) +} + +// SetGauge provides a wrapper functionality for emitting a gauge metric with +// global labels (if any). +func SetGauge(val float32, keys ...string) { + metrics.SetGaugeWithLabels(keys, val, globalLabels) +} + +// SetGaugeWithLabels provides a wrapper functionality for emitting a gauge +// metric with global labels (if any) along with the provided labels. +func SetGaugeWithLabels(keys []string, val float32, labels []metrics.Label) { + metrics.SetGaugeWithLabels(keys, val, append(labels, globalLabels...)) +} + +// MeasureSince provides a wrapper functionality for emitting a a time measure +// metric with global labels (if any). +func MeasureSince(start time.Time, keys ...string) { + metrics.MeasureSinceWithLabels(keys, start.UTC(), globalLabels) +} diff --git a/tests/fixtures/adr-024-coin-metadata_genesis.json b/tests/fixtures/adr-024-coin-metadata_genesis.json new file mode 100644 index 000000000000..d56ed836fa89 --- /dev/null +++ b/tests/fixtures/adr-024-coin-metadata_genesis.json @@ -0,0 +1,219 @@ +{ + "genesis_time": "2020-07-18T22:22:09.286222793Z", + "chain_id": "123", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_num": 50, + "proof_trial_period": "50000" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + } + }, + "app_hash": "", + "app_state": { + "bank": { + "params": { + "default_send_enabled": true + }, + "balances": [ + { + "address": "cosmos106vrzv5xkheqhjm023pxcxlqmcjvuhtfyachz4", + "coins": [ + { + "denom": "nametoken", + "amount": "1000" + }, + { + "denom": "stake", + "amount": "100000000" + } + ] + }, + { + "address": "cosmos1xztun2634zplhajda7tmjaxy488qj44n765t58", + "coins": [ + { + "denom": "nametoken", + "amount": "1000" + }, + { + "denom": "stake", + "amount": "100000000" + } + ] + } + ], + "supply": [], + "denom_metadata": [ + { + "description": "The native staking token of the Cosmos Hub.", + "denom_units": [ + { + "denom": "uatom", + "exponent": 0, + "aliases": [ + "microatom" + ] + }, + { + "denom": "matom", + "exponent": 3, + "aliases": [ + "milliatom" + ] + }, + { + "denom": "atom", + "exponent": 6 + } + ], + "base": "uatom", + "display": "atom" + } + ] + }, + "genutil": { + "gentxs": [] + }, + "capability": { + "index": "1", + "owners": [] + }, + "mint": { + "minter": { + "inflation": "0.130000000000000000", + "annual_provisions": "0.000000000000000000" + }, + "params": { + "mint_denom": "stake", + "inflation_rate_change": "0.130000000000000000", + "inflation_max": "0.200000000000000000", + "inflation_min": "0.070000000000000000", + "goal_bonded": "0.670000000000000000", + "blocks_per_year": "6311520" + } + }, + "ibc": { + "client_genesis": { + "clients": [], + "clients_consensus": [], + "create_localhost": true + }, + "connection_genesis": { + "connections": [], + "client_connection_paths": [] + }, + "channel_genesis": { + "channels": [], + "acknowledgements": [], + "commitments": [], + "send_sequences": [], + "recv_sequences": [], + "ack_sequences": [] + } + }, + "upgrade": {}, + "evidence": { + "evidence": [] + }, + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [] + }, + "gov": { + "starting_proposal_id": "1", + "deposits": null, + "votes": null, + "proposals": null, + "deposit_params": { + "min_deposit": [ + { + "denom": "stake", + "amount": "10000000" + } + ], + "max_deposit_period": "172800000000000" + }, + "voting_params": { + "voting_period": "172800000000000" + }, + "tally_params": { + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto": "0.334000000000000000" + } + }, + "params": null, + "transfer": { + "port_id": "transfer" + }, + "crisis": { + "constant_fee": { + "denom": "stake", + "amount": "1000" + } + }, + "distribution": { + "params": { + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": true + }, + "fee_pool": { + "community_pool": [] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "", + "outstanding_rewards": [], + "validator_accumulated_commissions": [], + "validator_historical_rewards": [], + "validator_current_rewards": [], + "delegator_starting_infos": [], + "validator_slash_events": [] + }, + "slashing": { + "params": { + "signed_blocks_window": "100", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "600000000000", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": {}, + "missed_blocks": {} + }, + "staking": { + "params": { + "unbonding_time": "1814400000000000", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 100, + "bond_denom": "stake" + }, + "last_total_power": "0", + "last_validator_powers": null, + "validators": null, + "delegations": null, + "unbonding_delegations": null, + "redelegations": null, + "exported": false + } + } +} diff --git a/tests/gobash.go b/tests/gobash.go deleted file mode 100644 index 3d4166dd30d6..000000000000 --- a/tests/gobash.go +++ /dev/null @@ -1,114 +0,0 @@ -package tests - -import ( - "fmt" - "io" - "io/ioutil" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -// ExecuteT executes the command, pipes any input to STDIN and return STDOUT, -// logging STDOUT/STDERR to t. -func ExecuteT(t *testing.T, cmd, input string) (stdout, stderr string) { - t.Log("Running", cmd) - - // split cmd to name and args - split := strings.Split(cmd, " ") - require.True(t, len(split) > 0, "no command provided") - name, args := split[0], []string(nil) - if len(split) > 1 { - args = split[1:] - } - - proc, err := StartProcess("", name, args) - require.NoError(t, err) - - // if input is provided, pass it to STDIN and close the pipe - if input != "" { - _, err = io.WriteString(proc.StdinPipe, input) - require.NoError(t, err) - proc.StdinPipe.Close() - } - - outbz, errbz, err := proc.ReadAll() - if err != nil { - fmt.Println("Err on proc.ReadAll()", err, args) - } - - proc.Wait() - - if len(outbz) > 0 { - t.Log("Stdout:", string(outbz)) - } - - if len(errbz) > 0 { - t.Log("Stderr:", string(errbz)) - } - - stdout = strings.Trim(string(outbz), "\n") - stderr = strings.Trim(string(errbz), "\n") - - return stdout, stderr -} - -// Execute the command, launch goroutines to log stdout/err to t. -// Caller should wait for .Wait() or .Stop() to terminate. -func GoExecuteT(t *testing.T, cmd string) (proc *Process) { - t.Log("Running", cmd) - - // Split cmd to name and args. - split := strings.Split(cmd, " ") - require.True(t, len(split) > 0, "no command provided") - name, args := split[0], []string(nil) - if len(split) > 1 { - args = split[1:] - } - - // Start process. - proc, err := StartProcess("", name, args) - require.NoError(t, err) - return proc -} - -// Same as GoExecuteT but spawns a go routine to ReadAll off stdout. -func GoExecuteTWithStdout(t *testing.T, cmd string) (proc *Process) { - t.Log("Running", cmd) - - // Split cmd to name and args. - split := strings.Split(cmd, " ") - require.True(t, len(split) > 0, "no command provided") - name, args := split[0], []string(nil) - if len(split) > 1 { - args = split[1:] - } - - // Start process. - proc, err := CreateProcess("", name, args) - require.NoError(t, err) - - // Without this, the test halts ?! - // (theory: because stdout and/or err aren't connected to anything the process halts) - go func(proc *Process) { - _, err := ioutil.ReadAll(proc.StdoutPipe) - if err != nil { - fmt.Println("-------------ERR-----------------------", err) - return - } - }(proc) - - go func(proc *Process) { - _, err := ioutil.ReadAll(proc.StderrPipe) - if err != nil { - fmt.Println("-------------ERR-----------------------", err) - return - } - }(proc) - - err = proc.Cmd.Start() - require.NoError(t, err) - proc.Pid = proc.Cmd.Process.Pid - return proc -} diff --git a/tests/io.go b/tests/io.go deleted file mode 100644 index b1aa30634a67..000000000000 --- a/tests/io.go +++ /dev/null @@ -1,21 +0,0 @@ -package tests - -import ( - "bytes" - "strings" - - "github.com/spf13/cobra" -) - -// ApplyMockIO replaces stdin/out/err with buffers that can be used during testing. -func ApplyMockIO(c *cobra.Command) (*strings.Reader, *bytes.Buffer, *bytes.Buffer) { - mockIn := strings.NewReader("") - mockOut := bytes.NewBufferString("") - mockErr := bytes.NewBufferString("") - c.SetIn(mockIn) - c.SetOut(mockOut) - c.SetErr(mockErr) - return mockIn, mockOut, mockErr -} - -// DONTCOVER diff --git a/tests/knownValues.go b/tests/knownValues.go deleted file mode 100644 index eb2063fa1666..000000000000 --- a/tests/knownValues.go +++ /dev/null @@ -1,6 +0,0 @@ -package tests - -const ( - // Tests expect a ledger device initialized to the following mnemonic - TestMnemonic = "equip will roof matter pink blind book anxiety banner elbow sun young" -) diff --git a/tests/mocks/account_retriever.go b/tests/mocks/account_retriever.go index 9f8ee69282a2..37fa1aa64da0 100644 --- a/tests/mocks/account_retriever.go +++ b/tests/mocks/account_retriever.go @@ -1,40 +1,176 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: client/account_retriever.go + +// Package mocks is a generated GoMock package. package mocks import ( - reflect "reflect" - + client "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + types "github.com/cosmos/cosmos-sdk/types" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) -type MockNodeQuerier struct { +// MockAccount is a mock of Account interface +type MockAccount struct { ctrl *gomock.Controller - recorder *MockNodeQuerierMockRecorder + recorder *MockAccountMockRecorder } -type MockNodeQuerierMockRecorder struct { - mock *MockNodeQuerier +// MockAccountMockRecorder is the mock recorder for MockAccount +type MockAccountMockRecorder struct { + mock *MockAccount } -func NewMockNodeQuerier(ctrl *gomock.Controller) *MockNodeQuerier { - mock := &MockNodeQuerier{ctrl: ctrl} - mock.recorder = &MockNodeQuerierMockRecorder{mock} +// NewMockAccount creates a new mock instance +func NewMockAccount(ctrl *gomock.Controller) *MockAccount { + mock := &MockAccount{ctrl: ctrl} + mock.recorder = &MockAccountMockRecorder{mock} return mock } -func (m *MockNodeQuerier) EXPECT() *MockNodeQuerierMockRecorder { +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAccount) EXPECT() *MockAccountMockRecorder { return m.recorder } -func (m *MockNodeQuerier) QueryWithData(path string, data []byte) ([]byte, int64, error) { +// GetAddress mocks base method +func (m *MockAccount) GetAddress() types.AccAddress { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAddress") + ret0, _ := ret[0].(types.AccAddress) + return ret0 +} + +// GetAddress indicates an expected call of GetAddress +func (mr *MockAccountMockRecorder) GetAddress() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddress", reflect.TypeOf((*MockAccount)(nil).GetAddress)) +} + +// GetPubKey mocks base method +func (m *MockAccount) GetPubKey() cryptotypes.PubKey { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPubKey") + ret0, _ := ret[0].(cryptotypes.PubKey) + return ret0 +} + +// GetPubKey indicates an expected call of GetPubKey +func (mr *MockAccountMockRecorder) GetPubKey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPubKey", reflect.TypeOf((*MockAccount)(nil).GetPubKey)) +} + +// GetAccountNumber mocks base method +func (m *MockAccount) GetAccountNumber() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountNumber") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetAccountNumber indicates an expected call of GetAccountNumber +func (mr *MockAccountMockRecorder) GetAccountNumber() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountNumber", reflect.TypeOf((*MockAccount)(nil).GetAccountNumber)) +} + +// GetSequence mocks base method +func (m *MockAccount) GetSequence() uint64 { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryWithData", path, data) - ret0, _ := ret[0].([]byte) + ret := m.ctrl.Call(m, "GetSequence") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetSequence indicates an expected call of GetSequence +func (mr *MockAccountMockRecorder) GetSequence() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSequence", reflect.TypeOf((*MockAccount)(nil).GetSequence)) +} + +// MockAccountRetriever is a mock of AccountRetriever interface +type MockAccountRetriever struct { + ctrl *gomock.Controller + recorder *MockAccountRetrieverMockRecorder +} + +// MockAccountRetrieverMockRecorder is the mock recorder for MockAccountRetriever +type MockAccountRetrieverMockRecorder struct { + mock *MockAccountRetriever +} + +// NewMockAccountRetriever creates a new mock instance +func NewMockAccountRetriever(ctrl *gomock.Controller) *MockAccountRetriever { + mock := &MockAccountRetriever{ctrl: ctrl} + mock.recorder = &MockAccountRetrieverMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAccountRetriever) EXPECT() *MockAccountRetrieverMockRecorder { + return m.recorder +} + +// GetAccount mocks base method +func (m *MockAccountRetriever) GetAccount(clientCtx client.Context, addr types.AccAddress) (client.Account, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", clientCtx, addr) + ret0, _ := ret[0].(client.Account) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAccount indicates an expected call of GetAccount +func (mr *MockAccountRetrieverMockRecorder) GetAccount(clientCtx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountRetriever)(nil).GetAccount), clientCtx, addr) +} + +// GetAccountWithHeight mocks base method +func (m *MockAccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr types.AccAddress) (client.Account, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountWithHeight", clientCtx, addr) + ret0, _ := ret[0].(client.Account) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -func (mr *MockNodeQuerierMockRecorder) QueryWithData(path, data interface{}) *gomock.Call { +// GetAccountWithHeight indicates an expected call of GetAccountWithHeight +func (mr *MockAccountRetrieverMockRecorder) GetAccountWithHeight(clientCtx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountWithHeight", reflect.TypeOf((*MockAccountRetriever)(nil).GetAccountWithHeight), clientCtx, addr) +} + +// EnsureExists mocks base method +func (m *MockAccountRetriever) EnsureExists(clientCtx client.Context, addr types.AccAddress) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureExists", clientCtx, addr) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnsureExists indicates an expected call of EnsureExists +func (mr *MockAccountRetrieverMockRecorder) EnsureExists(clientCtx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureExists", reflect.TypeOf((*MockAccountRetriever)(nil).EnsureExists), clientCtx, addr) +} + +// GetAccountNumberSequence mocks base method +func (m *MockAccountRetriever) GetAccountNumberSequence(clientCtx client.Context, addr types.AccAddress) (uint64, uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccountNumberSequence", clientCtx, addr) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(uint64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetAccountNumberSequence indicates an expected call of GetAccountNumberSequence +func (mr *MockAccountRetrieverMockRecorder) GetAccountNumberSequence(clientCtx, addr interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWithData", reflect.TypeOf((*MockNodeQuerier)(nil).QueryWithData), path, data) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountNumberSequence", reflect.TypeOf((*MockAccountRetriever)(nil).GetAccountNumberSequence), clientCtx, addr) } diff --git a/tests/mocks/grpc_server.go b/tests/mocks/grpc_server.go new file mode 100644 index 000000000000..91fedb7ae560 --- /dev/null +++ b/tests/mocks/grpc_server.go @@ -0,0 +1,46 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/gogo/protobuf/grpc (interfaces: Server) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + grpc "google.golang.org/grpc" + reflect "reflect" +) + +// MockServer is a mock of Server interface +type MockServer struct { + ctrl *gomock.Controller + recorder *MockServerMockRecorder +} + +// MockServerMockRecorder is the mock recorder for MockServer +type MockServerMockRecorder struct { + mock *MockServer +} + +// NewMockServer creates a new mock instance +func NewMockServer(ctrl *gomock.Controller) *MockServer { + mock := &MockServer{ctrl: ctrl} + mock.recorder = &MockServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockServer) EXPECT() *MockServerMockRecorder { + return m.recorder +} + +// RegisterService mocks base method +func (m *MockServer) RegisterService(arg0 *grpc.ServiceDesc, arg1 interface{}) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterService", arg0, arg1) +} + +// RegisterService indicates an expected call of RegisterService +func (mr *MockServerMockRecorder) RegisterService(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterService", reflect.TypeOf((*MockServer)(nil).RegisterService), arg0, arg1) +} diff --git a/tests/mocks/tendermint_tendermint_libs_log_DB.go b/tests/mocks/tendermint_tendermint_libs_log_DB.go new file mode 100644 index 000000000000..454fd9c7600d --- /dev/null +++ b/tests/mocks/tendermint_tendermint_libs_log_DB.go @@ -0,0 +1,103 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/tendermint/tendermint/libs/log (interfaces: Logger) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + log "github.com/tendermint/tendermint/libs/log" + reflect "reflect" +) + +// MockLogger is a mock of Logger interface +type MockLogger struct { + ctrl *gomock.Controller + recorder *MockLoggerMockRecorder +} + +// MockLoggerMockRecorder is the mock recorder for MockLogger +type MockLoggerMockRecorder struct { + mock *MockLogger +} + +// NewMockLogger creates a new mock instance +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { + return m.recorder +} + +// Debug mocks base method +func (m *MockLogger) Debug(arg0 string, arg1 ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debug", varargs...) +} + +// Debug indicates an expected call of Debug +func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), varargs...) +} + +// Error mocks base method +func (m *MockLogger) Error(arg0 string, arg1 ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Error", varargs...) +} + +// Error indicates an expected call of Error +func (mr *MockLoggerMockRecorder) Error(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), varargs...) +} + +// Info mocks base method +func (m *MockLogger) Info(arg0 string, arg1 ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Info", varargs...) +} + +// Info indicates an expected call of Info +func (mr *MockLoggerMockRecorder) Info(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), varargs...) +} + +// With mocks base method +func (m *MockLogger) With(arg0 ...interface{}) log.Logger { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range arg0 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "With", varargs...) + ret0, _ := ret[0].(log.Logger) + return ret0 +} + +// With indicates an expected call of With +func (mr *MockLoggerMockRecorder) With(arg0 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "With", reflect.TypeOf((*MockLogger)(nil).With), arg0...) +} diff --git a/tests/mocks/tendermint_tm_db_DB.go b/tests/mocks/tendermint_tm_db_DB.go new file mode 100644 index 000000000000..cdf5e5ab0a27 --- /dev/null +++ b/tests/mocks/tendermint_tm_db_DB.go @@ -0,0 +1,206 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/tendermint/tm-db (interfaces: DB) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + db "github.com/tendermint/tm-db" + reflect "reflect" +) + +// MockDB is a mock of DB interface +type MockDB struct { + ctrl *gomock.Controller + recorder *MockDBMockRecorder +} + +// MockDBMockRecorder is the mock recorder for MockDB +type MockDBMockRecorder struct { + mock *MockDB +} + +// NewMockDB creates a new mock instance +func NewMockDB(ctrl *gomock.Controller) *MockDB { + mock := &MockDB{ctrl: ctrl} + mock.recorder = &MockDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockDB) EXPECT() *MockDBMockRecorder { + return m.recorder +} + +// Close mocks base method +func (m *MockDB) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close +func (mr *MockDBMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDB)(nil).Close)) +} + +// Delete mocks base method +func (m *MockDB) Delete(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockDBMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDB)(nil).Delete), arg0) +} + +// DeleteSync mocks base method +func (m *MockDB) DeleteSync(arg0 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteSync", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteSync indicates an expected call of DeleteSync +func (mr *MockDBMockRecorder) DeleteSync(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSync", reflect.TypeOf((*MockDB)(nil).DeleteSync), arg0) +} + +// Get mocks base method +func (m *MockDB) Get(arg0 []byte) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get +func (mr *MockDBMockRecorder) Get(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockDB)(nil).Get), arg0) +} + +// Has mocks base method +func (m *MockDB) Has(arg0 []byte) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Has", arg0) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Has indicates an expected call of Has +func (mr *MockDBMockRecorder) Has(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockDB)(nil).Has), arg0) +} + +// Iterator mocks base method +func (m *MockDB) Iterator(arg0, arg1 []byte) (db.Iterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Iterator", arg0, arg1) + ret0, _ := ret[0].(db.Iterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Iterator indicates an expected call of Iterator +func (mr *MockDBMockRecorder) Iterator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterator", reflect.TypeOf((*MockDB)(nil).Iterator), arg0, arg1) +} + +// NewBatch mocks base method +func (m *MockDB) NewBatch() db.Batch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewBatch") + ret0, _ := ret[0].(db.Batch) + return ret0 +} + +// NewBatch indicates an expected call of NewBatch +func (mr *MockDBMockRecorder) NewBatch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBatch", reflect.TypeOf((*MockDB)(nil).NewBatch)) +} + +// Print mocks base method +func (m *MockDB) Print() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Print") + ret0, _ := ret[0].(error) + return ret0 +} + +// Print indicates an expected call of Print +func (mr *MockDBMockRecorder) Print() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Print", reflect.TypeOf((*MockDB)(nil).Print)) +} + +// ReverseIterator mocks base method +func (m *MockDB) ReverseIterator(arg0, arg1 []byte) (db.Iterator, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReverseIterator", arg0, arg1) + ret0, _ := ret[0].(db.Iterator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReverseIterator indicates an expected call of ReverseIterator +func (mr *MockDBMockRecorder) ReverseIterator(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReverseIterator", reflect.TypeOf((*MockDB)(nil).ReverseIterator), arg0, arg1) +} + +// Set mocks base method +func (m *MockDB) Set(arg0, arg1 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Set indicates an expected call of Set +func (mr *MockDBMockRecorder) Set(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockDB)(nil).Set), arg0, arg1) +} + +// SetSync mocks base method +func (m *MockDB) SetSync(arg0, arg1 []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetSync", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetSync indicates an expected call of SetSync +func (mr *MockDBMockRecorder) SetSync(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSync", reflect.TypeOf((*MockDB)(nil).SetSync), arg0, arg1) +} + +// Stats mocks base method +func (m *MockDB) Stats() map[string]string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stats") + ret0, _ := ret[0].(map[string]string) + return ret0 +} + +// Stats indicates an expected call of Stats +func (mr *MockDBMockRecorder) Stats() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stats", reflect.TypeOf((*MockDB)(nil).Stats)) +} diff --git a/tests/mocks/types_handler.go b/tests/mocks/types_handler.go new file mode 100644 index 000000000000..da80614ae884 --- /dev/null +++ b/tests/mocks/types_handler.go @@ -0,0 +1,49 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: types/handler.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + types "github.com/cosmos/cosmos-sdk/types" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockAnteDecorator is a mock of AnteDecorator interface +type MockAnteDecorator struct { + ctrl *gomock.Controller + recorder *MockAnteDecoratorMockRecorder +} + +// MockAnteDecoratorMockRecorder is the mock recorder for MockAnteDecorator +type MockAnteDecoratorMockRecorder struct { + mock *MockAnteDecorator +} + +// NewMockAnteDecorator creates a new mock instance +func NewMockAnteDecorator(ctrl *gomock.Controller) *MockAnteDecorator { + mock := &MockAnteDecorator{ctrl: ctrl} + mock.recorder = &MockAnteDecoratorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAnteDecorator) EXPECT() *MockAnteDecoratorMockRecorder { + return m.recorder +} + +// AnteHandle mocks base method +func (m *MockAnteDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (types.Context, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnteHandle", ctx, tx, simulate, next) + ret0, _ := ret[0].(types.Context) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AnteHandle indicates an expected call of AnteHandle +func (mr *MockAnteDecoratorMockRecorder) AnteHandle(ctx, tx, simulate, next interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnteHandle", reflect.TypeOf((*MockAnteDecorator)(nil).AnteHandle), ctx, tx, simulate, next) +} diff --git a/tests/mocks/types_invariant.go b/tests/mocks/types_invariant.go new file mode 100644 index 000000000000..325b108b20bb --- /dev/null +++ b/tests/mocks/types_invariant.go @@ -0,0 +1,46 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: types/invariant.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + types "github.com/cosmos/cosmos-sdk/types" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockInvariantRegistry is a mock of InvariantRegistry interface +type MockInvariantRegistry struct { + ctrl *gomock.Controller + recorder *MockInvariantRegistryMockRecorder +} + +// MockInvariantRegistryMockRecorder is the mock recorder for MockInvariantRegistry +type MockInvariantRegistryMockRecorder struct { + mock *MockInvariantRegistry +} + +// NewMockInvariantRegistry creates a new mock instance +func NewMockInvariantRegistry(ctrl *gomock.Controller) *MockInvariantRegistry { + mock := &MockInvariantRegistry{ctrl: ctrl} + mock.recorder = &MockInvariantRegistryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockInvariantRegistry) EXPECT() *MockInvariantRegistryMockRecorder { + return m.recorder +} + +// RegisterRoute mocks base method +func (m *MockInvariantRegistry) RegisterRoute(moduleName, route string, invar types.Invariant) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterRoute", moduleName, route, invar) +} + +// RegisterRoute indicates an expected call of RegisterRoute +func (mr *MockInvariantRegistryMockRecorder) RegisterRoute(moduleName, route, invar interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRoute", reflect.TypeOf((*MockInvariantRegistry)(nil).RegisterRoute), moduleName, route, invar) +} diff --git a/tests/mocks/types_module_module.go b/tests/mocks/types_module_module.go new file mode 100644 index 000000000000..41ded4d7a2c0 --- /dev/null +++ b/tests/mocks/types_module_module.go @@ -0,0 +1,591 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: types/module/module.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + json "encoding/json" + client "github.com/cosmos/cosmos-sdk/client" + codec "github.com/cosmos/cosmos-sdk/codec" + types "github.com/cosmos/cosmos-sdk/codec/types" + types0 "github.com/cosmos/cosmos-sdk/types" + module "github.com/cosmos/cosmos-sdk/types/module" + gomock "github.com/golang/mock/gomock" + mux "github.com/gorilla/mux" + runtime "github.com/grpc-ecosystem/grpc-gateway/runtime" + cobra "github.com/spf13/cobra" + types1 "github.com/tendermint/tendermint/abci/types" + reflect "reflect" +) + +// MockAppModuleBasic is a mock of AppModuleBasic interface +type MockAppModuleBasic struct { + ctrl *gomock.Controller + recorder *MockAppModuleBasicMockRecorder +} + +// MockAppModuleBasicMockRecorder is the mock recorder for MockAppModuleBasic +type MockAppModuleBasicMockRecorder struct { + mock *MockAppModuleBasic +} + +// NewMockAppModuleBasic creates a new mock instance +func NewMockAppModuleBasic(ctrl *gomock.Controller) *MockAppModuleBasic { + mock := &MockAppModuleBasic{ctrl: ctrl} + mock.recorder = &MockAppModuleBasicMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAppModuleBasic) EXPECT() *MockAppModuleBasicMockRecorder { + return m.recorder +} + +// Name mocks base method +func (m *MockAppModuleBasic) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name +func (mr *MockAppModuleBasicMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockAppModuleBasic)(nil).Name)) +} + +// RegisterLegacyAminoCodec mocks base method +func (m *MockAppModuleBasic) RegisterLegacyAminoCodec(arg0 *codec.LegacyAmino) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterLegacyAminoCodec", arg0) +} + +// RegisterLegacyAminoCodec indicates an expected call of RegisterLegacyAminoCodec +func (mr *MockAppModuleBasicMockRecorder) RegisterLegacyAminoCodec(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterLegacyAminoCodec", reflect.TypeOf((*MockAppModuleBasic)(nil).RegisterLegacyAminoCodec), arg0) +} + +// RegisterInterfaces mocks base method +func (m *MockAppModuleBasic) RegisterInterfaces(arg0 types.InterfaceRegistry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterInterfaces", arg0) +} + +// RegisterInterfaces indicates an expected call of RegisterInterfaces +func (mr *MockAppModuleBasicMockRecorder) RegisterInterfaces(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInterfaces", reflect.TypeOf((*MockAppModuleBasic)(nil).RegisterInterfaces), arg0) +} + +// DefaultGenesis mocks base method +func (m *MockAppModuleBasic) DefaultGenesis(arg0 codec.JSONMarshaler) json.RawMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DefaultGenesis", arg0) + ret0, _ := ret[0].(json.RawMessage) + return ret0 +} + +// DefaultGenesis indicates an expected call of DefaultGenesis +func (mr *MockAppModuleBasicMockRecorder) DefaultGenesis(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultGenesis", reflect.TypeOf((*MockAppModuleBasic)(nil).DefaultGenesis), arg0) +} + +// ValidateGenesis mocks base method +func (m *MockAppModuleBasic) ValidateGenesis(arg0 codec.JSONMarshaler, arg1 client.TxEncodingConfig, arg2 json.RawMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateGenesis", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateGenesis indicates an expected call of ValidateGenesis +func (mr *MockAppModuleBasicMockRecorder) ValidateGenesis(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGenesis", reflect.TypeOf((*MockAppModuleBasic)(nil).ValidateGenesis), arg0, arg1, arg2) +} + +// RegisterRESTRoutes mocks base method +func (m *MockAppModuleBasic) RegisterRESTRoutes(arg0 client.Context, arg1 *mux.Router) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterRESTRoutes", arg0, arg1) +} + +// RegisterRESTRoutes indicates an expected call of RegisterRESTRoutes +func (mr *MockAppModuleBasicMockRecorder) RegisterRESTRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRESTRoutes", reflect.TypeOf((*MockAppModuleBasic)(nil).RegisterRESTRoutes), arg0, arg1) +} + +// RegisterGRPCGatewayRoutes mocks base method +func (m *MockAppModuleBasic) RegisterGRPCGatewayRoutes(arg0 client.Context, arg1 *runtime.ServeMux) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterGRPCGatewayRoutes", arg0, arg1) +} + +// RegisterGRPCGatewayRoutes indicates an expected call of RegisterGRPCGatewayRoutes +func (mr *MockAppModuleBasicMockRecorder) RegisterGRPCGatewayRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterGRPCGatewayRoutes", reflect.TypeOf((*MockAppModuleBasic)(nil).RegisterGRPCGatewayRoutes), arg0, arg1) +} + +// GetTxCmd mocks base method +func (m *MockAppModuleBasic) GetTxCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTxCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetTxCmd indicates an expected call of GetTxCmd +func (mr *MockAppModuleBasicMockRecorder) GetTxCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxCmd", reflect.TypeOf((*MockAppModuleBasic)(nil).GetTxCmd)) +} + +// GetQueryCmd mocks base method +func (m *MockAppModuleBasic) GetQueryCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQueryCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetQueryCmd indicates an expected call of GetQueryCmd +func (mr *MockAppModuleBasicMockRecorder) GetQueryCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryCmd", reflect.TypeOf((*MockAppModuleBasic)(nil).GetQueryCmd)) +} + +// MockAppModuleGenesis is a mock of AppModuleGenesis interface +type MockAppModuleGenesis struct { + ctrl *gomock.Controller + recorder *MockAppModuleGenesisMockRecorder +} + +// MockAppModuleGenesisMockRecorder is the mock recorder for MockAppModuleGenesis +type MockAppModuleGenesisMockRecorder struct { + mock *MockAppModuleGenesis +} + +// NewMockAppModuleGenesis creates a new mock instance +func NewMockAppModuleGenesis(ctrl *gomock.Controller) *MockAppModuleGenesis { + mock := &MockAppModuleGenesis{ctrl: ctrl} + mock.recorder = &MockAppModuleGenesisMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAppModuleGenesis) EXPECT() *MockAppModuleGenesisMockRecorder { + return m.recorder +} + +// Name mocks base method +func (m *MockAppModuleGenesis) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name +func (mr *MockAppModuleGenesisMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockAppModuleGenesis)(nil).Name)) +} + +// RegisterLegacyAminoCodec mocks base method +func (m *MockAppModuleGenesis) RegisterLegacyAminoCodec(arg0 *codec.LegacyAmino) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterLegacyAminoCodec", arg0) +} + +// RegisterLegacyAminoCodec indicates an expected call of RegisterLegacyAminoCodec +func (mr *MockAppModuleGenesisMockRecorder) RegisterLegacyAminoCodec(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterLegacyAminoCodec", reflect.TypeOf((*MockAppModuleGenesis)(nil).RegisterLegacyAminoCodec), arg0) +} + +// RegisterInterfaces mocks base method +func (m *MockAppModuleGenesis) RegisterInterfaces(arg0 types.InterfaceRegistry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterInterfaces", arg0) +} + +// RegisterInterfaces indicates an expected call of RegisterInterfaces +func (mr *MockAppModuleGenesisMockRecorder) RegisterInterfaces(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInterfaces", reflect.TypeOf((*MockAppModuleGenesis)(nil).RegisterInterfaces), arg0) +} + +// DefaultGenesis mocks base method +func (m *MockAppModuleGenesis) DefaultGenesis(arg0 codec.JSONMarshaler) json.RawMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DefaultGenesis", arg0) + ret0, _ := ret[0].(json.RawMessage) + return ret0 +} + +// DefaultGenesis indicates an expected call of DefaultGenesis +func (mr *MockAppModuleGenesisMockRecorder) DefaultGenesis(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultGenesis", reflect.TypeOf((*MockAppModuleGenesis)(nil).DefaultGenesis), arg0) +} + +// ValidateGenesis mocks base method +func (m *MockAppModuleGenesis) ValidateGenesis(arg0 codec.JSONMarshaler, arg1 client.TxEncodingConfig, arg2 json.RawMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateGenesis", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateGenesis indicates an expected call of ValidateGenesis +func (mr *MockAppModuleGenesisMockRecorder) ValidateGenesis(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGenesis", reflect.TypeOf((*MockAppModuleGenesis)(nil).ValidateGenesis), arg0, arg1, arg2) +} + +// RegisterRESTRoutes mocks base method +func (m *MockAppModuleGenesis) RegisterRESTRoutes(arg0 client.Context, arg1 *mux.Router) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterRESTRoutes", arg0, arg1) +} + +// RegisterRESTRoutes indicates an expected call of RegisterRESTRoutes +func (mr *MockAppModuleGenesisMockRecorder) RegisterRESTRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRESTRoutes", reflect.TypeOf((*MockAppModuleGenesis)(nil).RegisterRESTRoutes), arg0, arg1) +} + +// RegisterGRPCGatewayRoutes mocks base method +func (m *MockAppModuleGenesis) RegisterGRPCGatewayRoutes(arg0 client.Context, arg1 *runtime.ServeMux) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterGRPCGatewayRoutes", arg0, arg1) +} + +// RegisterGRPCGatewayRoutes indicates an expected call of RegisterGRPCGatewayRoutes +func (mr *MockAppModuleGenesisMockRecorder) RegisterGRPCGatewayRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterGRPCGatewayRoutes", reflect.TypeOf((*MockAppModuleGenesis)(nil).RegisterGRPCGatewayRoutes), arg0, arg1) +} + +// GetTxCmd mocks base method +func (m *MockAppModuleGenesis) GetTxCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTxCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetTxCmd indicates an expected call of GetTxCmd +func (mr *MockAppModuleGenesisMockRecorder) GetTxCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxCmd", reflect.TypeOf((*MockAppModuleGenesis)(nil).GetTxCmd)) +} + +// GetQueryCmd mocks base method +func (m *MockAppModuleGenesis) GetQueryCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQueryCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetQueryCmd indicates an expected call of GetQueryCmd +func (mr *MockAppModuleGenesisMockRecorder) GetQueryCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryCmd", reflect.TypeOf((*MockAppModuleGenesis)(nil).GetQueryCmd)) +} + +// InitGenesis mocks base method +func (m *MockAppModuleGenesis) InitGenesis(arg0 types0.Context, arg1 codec.JSONMarshaler, arg2 json.RawMessage) []types1.ValidatorUpdate { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InitGenesis", arg0, arg1, arg2) + ret0, _ := ret[0].([]types1.ValidatorUpdate) + return ret0 +} + +// InitGenesis indicates an expected call of InitGenesis +func (mr *MockAppModuleGenesisMockRecorder) InitGenesis(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitGenesis", reflect.TypeOf((*MockAppModuleGenesis)(nil).InitGenesis), arg0, arg1, arg2) +} + +// ExportGenesis mocks base method +func (m *MockAppModuleGenesis) ExportGenesis(arg0 types0.Context, arg1 codec.JSONMarshaler) json.RawMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExportGenesis", arg0, arg1) + ret0, _ := ret[0].(json.RawMessage) + return ret0 +} + +// ExportGenesis indicates an expected call of ExportGenesis +func (mr *MockAppModuleGenesisMockRecorder) ExportGenesis(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExportGenesis", reflect.TypeOf((*MockAppModuleGenesis)(nil).ExportGenesis), arg0, arg1) +} + +// MockAppModule is a mock of AppModule interface +type MockAppModule struct { + ctrl *gomock.Controller + recorder *MockAppModuleMockRecorder +} + +// MockAppModuleMockRecorder is the mock recorder for MockAppModule +type MockAppModuleMockRecorder struct { + mock *MockAppModule +} + +// NewMockAppModule creates a new mock instance +func NewMockAppModule(ctrl *gomock.Controller) *MockAppModule { + mock := &MockAppModule{ctrl: ctrl} + mock.recorder = &MockAppModuleMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAppModule) EXPECT() *MockAppModuleMockRecorder { + return m.recorder +} + +// Name mocks base method +func (m *MockAppModule) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name +func (mr *MockAppModuleMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockAppModule)(nil).Name)) +} + +// RegisterLegacyAminoCodec mocks base method +func (m *MockAppModule) RegisterLegacyAminoCodec(arg0 *codec.LegacyAmino) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterLegacyAminoCodec", arg0) +} + +// RegisterLegacyAminoCodec indicates an expected call of RegisterLegacyAminoCodec +func (mr *MockAppModuleMockRecorder) RegisterLegacyAminoCodec(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterLegacyAminoCodec", reflect.TypeOf((*MockAppModule)(nil).RegisterLegacyAminoCodec), arg0) +} + +// RegisterInterfaces mocks base method +func (m *MockAppModule) RegisterInterfaces(arg0 types.InterfaceRegistry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterInterfaces", arg0) +} + +// RegisterInterfaces indicates an expected call of RegisterInterfaces +func (mr *MockAppModuleMockRecorder) RegisterInterfaces(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInterfaces", reflect.TypeOf((*MockAppModule)(nil).RegisterInterfaces), arg0) +} + +// DefaultGenesis mocks base method +func (m *MockAppModule) DefaultGenesis(arg0 codec.JSONMarshaler) json.RawMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DefaultGenesis", arg0) + ret0, _ := ret[0].(json.RawMessage) + return ret0 +} + +// DefaultGenesis indicates an expected call of DefaultGenesis +func (mr *MockAppModuleMockRecorder) DefaultGenesis(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultGenesis", reflect.TypeOf((*MockAppModule)(nil).DefaultGenesis), arg0) +} + +// ValidateGenesis mocks base method +func (m *MockAppModule) ValidateGenesis(arg0 codec.JSONMarshaler, arg1 client.TxEncodingConfig, arg2 json.RawMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateGenesis", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateGenesis indicates an expected call of ValidateGenesis +func (mr *MockAppModuleMockRecorder) ValidateGenesis(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateGenesis", reflect.TypeOf((*MockAppModule)(nil).ValidateGenesis), arg0, arg1, arg2) +} + +// RegisterRESTRoutes mocks base method +func (m *MockAppModule) RegisterRESTRoutes(arg0 client.Context, arg1 *mux.Router) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterRESTRoutes", arg0, arg1) +} + +// RegisterRESTRoutes indicates an expected call of RegisterRESTRoutes +func (mr *MockAppModuleMockRecorder) RegisterRESTRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRESTRoutes", reflect.TypeOf((*MockAppModule)(nil).RegisterRESTRoutes), arg0, arg1) +} + +// RegisterGRPCGatewayRoutes mocks base method +func (m *MockAppModule) RegisterGRPCGatewayRoutes(arg0 client.Context, arg1 *runtime.ServeMux) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterGRPCGatewayRoutes", arg0, arg1) +} + +// RegisterGRPCGatewayRoutes indicates an expected call of RegisterGRPCGatewayRoutes +func (mr *MockAppModuleMockRecorder) RegisterGRPCGatewayRoutes(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterGRPCGatewayRoutes", reflect.TypeOf((*MockAppModule)(nil).RegisterGRPCGatewayRoutes), arg0, arg1) +} + +// GetTxCmd mocks base method +func (m *MockAppModule) GetTxCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTxCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetTxCmd indicates an expected call of GetTxCmd +func (mr *MockAppModuleMockRecorder) GetTxCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTxCmd", reflect.TypeOf((*MockAppModule)(nil).GetTxCmd)) +} + +// GetQueryCmd mocks base method +func (m *MockAppModule) GetQueryCmd() *cobra.Command { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQueryCmd") + ret0, _ := ret[0].(*cobra.Command) + return ret0 +} + +// GetQueryCmd indicates an expected call of GetQueryCmd +func (mr *MockAppModuleMockRecorder) GetQueryCmd() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryCmd", reflect.TypeOf((*MockAppModule)(nil).GetQueryCmd)) +} + +// InitGenesis mocks base method +func (m *MockAppModule) InitGenesis(arg0 types0.Context, arg1 codec.JSONMarshaler, arg2 json.RawMessage) []types1.ValidatorUpdate { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InitGenesis", arg0, arg1, arg2) + ret0, _ := ret[0].([]types1.ValidatorUpdate) + return ret0 +} + +// InitGenesis indicates an expected call of InitGenesis +func (mr *MockAppModuleMockRecorder) InitGenesis(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitGenesis", reflect.TypeOf((*MockAppModule)(nil).InitGenesis), arg0, arg1, arg2) +} + +// ExportGenesis mocks base method +func (m *MockAppModule) ExportGenesis(arg0 types0.Context, arg1 codec.JSONMarshaler) json.RawMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExportGenesis", arg0, arg1) + ret0, _ := ret[0].(json.RawMessage) + return ret0 +} + +// ExportGenesis indicates an expected call of ExportGenesis +func (mr *MockAppModuleMockRecorder) ExportGenesis(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExportGenesis", reflect.TypeOf((*MockAppModule)(nil).ExportGenesis), arg0, arg1) +} + +// RegisterInvariants mocks base method +func (m *MockAppModule) RegisterInvariants(arg0 types0.InvariantRegistry) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterInvariants", arg0) +} + +// RegisterInvariants indicates an expected call of RegisterInvariants +func (mr *MockAppModuleMockRecorder) RegisterInvariants(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterInvariants", reflect.TypeOf((*MockAppModule)(nil).RegisterInvariants), arg0) +} + +// Route mocks base method +func (m *MockAppModule) Route() types0.Route { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Route") + ret0, _ := ret[0].(types0.Route) + return ret0 +} + +// Route indicates an expected call of Route +func (mr *MockAppModuleMockRecorder) Route() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Route", reflect.TypeOf((*MockAppModule)(nil).Route)) +} + +// QuerierRoute mocks base method +func (m *MockAppModule) QuerierRoute() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QuerierRoute") + ret0, _ := ret[0].(string) + return ret0 +} + +// QuerierRoute indicates an expected call of QuerierRoute +func (mr *MockAppModuleMockRecorder) QuerierRoute() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QuerierRoute", reflect.TypeOf((*MockAppModule)(nil).QuerierRoute)) +} + +// LegacyQuerierHandler mocks base method +func (m *MockAppModule) LegacyQuerierHandler(arg0 *codec.LegacyAmino) types0.Querier { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LegacyQuerierHandler", arg0) + ret0, _ := ret[0].(types0.Querier) + return ret0 +} + +// LegacyQuerierHandler indicates an expected call of LegacyQuerierHandler +func (mr *MockAppModuleMockRecorder) LegacyQuerierHandler(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LegacyQuerierHandler", reflect.TypeOf((*MockAppModule)(nil).LegacyQuerierHandler), arg0) +} + +// RegisterServices mocks base method +func (m *MockAppModule) RegisterServices(arg0 module.Configurator) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterServices", arg0) +} + +// RegisterServices indicates an expected call of RegisterServices +func (mr *MockAppModuleMockRecorder) RegisterServices(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterServices", reflect.TypeOf((*MockAppModule)(nil).RegisterServices), arg0) +} + +// BeginBlock mocks base method +func (m *MockAppModule) BeginBlock(arg0 types0.Context, arg1 types1.RequestBeginBlock) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "BeginBlock", arg0, arg1) +} + +// BeginBlock indicates an expected call of BeginBlock +func (mr *MockAppModuleMockRecorder) BeginBlock(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginBlock", reflect.TypeOf((*MockAppModule)(nil).BeginBlock), arg0, arg1) +} + +// EndBlock mocks base method +func (m *MockAppModule) EndBlock(arg0 types0.Context, arg1 types1.RequestEndBlock) []types1.ValidatorUpdate { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EndBlock", arg0, arg1) + ret0, _ := ret[0].([]types1.ValidatorUpdate) + return ret0 +} + +// EndBlock indicates an expected call of EndBlock +func (mr *MockAppModuleMockRecorder) EndBlock(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EndBlock", reflect.TypeOf((*MockAppModule)(nil).EndBlock), arg0, arg1) +} diff --git a/tests/mocks/types_router.go b/tests/mocks/types_router.go new file mode 100644 index 000000000000..48f96b0b162a --- /dev/null +++ b/tests/mocks/types_router.go @@ -0,0 +1,113 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: types/router.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + types "github.com/cosmos/cosmos-sdk/types" + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockRouter is a mock of Router interface +type MockRouter struct { + ctrl *gomock.Controller + recorder *MockRouterMockRecorder +} + +// MockRouterMockRecorder is the mock recorder for MockRouter +type MockRouterMockRecorder struct { + mock *MockRouter +} + +// NewMockRouter creates a new mock instance +func NewMockRouter(ctrl *gomock.Controller) *MockRouter { + mock := &MockRouter{ctrl: ctrl} + mock.recorder = &MockRouterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockRouter) EXPECT() *MockRouterMockRecorder { + return m.recorder +} + +// AddRoute mocks base method +func (m *MockRouter) AddRoute(r types.Route) types.Router { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddRoute", r) + ret0, _ := ret[0].(types.Router) + return ret0 +} + +// AddRoute indicates an expected call of AddRoute +func (mr *MockRouterMockRecorder) AddRoute(r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRoute", reflect.TypeOf((*MockRouter)(nil).AddRoute), r) +} + +// Route mocks base method +func (m *MockRouter) Route(ctx types.Context, path string) types.Handler { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Route", ctx, path) + ret0, _ := ret[0].(types.Handler) + return ret0 +} + +// Route indicates an expected call of Route +func (mr *MockRouterMockRecorder) Route(ctx, path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Route", reflect.TypeOf((*MockRouter)(nil).Route), ctx, path) +} + +// MockQueryRouter is a mock of QueryRouter interface +type MockQueryRouter struct { + ctrl *gomock.Controller + recorder *MockQueryRouterMockRecorder +} + +// MockQueryRouterMockRecorder is the mock recorder for MockQueryRouter +type MockQueryRouterMockRecorder struct { + mock *MockQueryRouter +} + +// NewMockQueryRouter creates a new mock instance +func NewMockQueryRouter(ctrl *gomock.Controller) *MockQueryRouter { + mock := &MockQueryRouter{ctrl: ctrl} + mock.recorder = &MockQueryRouterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockQueryRouter) EXPECT() *MockQueryRouterMockRecorder { + return m.recorder +} + +// AddRoute mocks base method +func (m *MockQueryRouter) AddRoute(r string, h types.Querier) types.QueryRouter { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddRoute", r, h) + ret0, _ := ret[0].(types.QueryRouter) + return ret0 +} + +// AddRoute indicates an expected call of AddRoute +func (mr *MockQueryRouterMockRecorder) AddRoute(r, h interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRoute", reflect.TypeOf((*MockQueryRouter)(nil).AddRoute), r, h) +} + +// Route mocks base method +func (m *MockQueryRouter) Route(path string) types.Querier { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Route", path) + ret0, _ := ret[0].(types.Querier) + return ret0 +} + +// Route indicates an expected call of Route +func (mr *MockQueryRouterMockRecorder) Route(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Route", reflect.TypeOf((*MockQueryRouter)(nil).Route), path) +} diff --git a/tests/process.go b/tests/process.go deleted file mode 100644 index 08f975cf8291..000000000000 --- a/tests/process.go +++ /dev/null @@ -1,117 +0,0 @@ -package tests - -import ( - "io" - "io/ioutil" - "os" - "os/exec" - "time" -) - -// execution process -type Process struct { - ExecPath string - Args []string - Pid int - StartTime time.Time - EndTime time.Time - Cmd *exec.Cmd `json:"-"` - ExitState *os.ProcessState `json:"-"` - StdinPipe io.WriteCloser `json:"-"` - StdoutPipe io.ReadCloser `json:"-"` - StderrPipe io.ReadCloser `json:"-"` -} - -// dir: The working directory. If "", os.Getwd() is used. -// name: Command name -// args: Args to command. (should not include name) -func StartProcess(dir string, name string, args []string) (*Process, error) { - proc, err := CreateProcess(dir, name, args) - if err != nil { - return nil, err - } - // cmd start - if err := proc.Cmd.Start(); err != nil { - return nil, err - } - proc.Pid = proc.Cmd.Process.Pid - - return proc, nil -} - -// Same as StartProcess but doesn't start the process -func CreateProcess(dir string, name string, args []string) (*Process, error) { - var cmd = exec.Command(name, args...) // is not yet started. - // cmd dir - if dir == "" { - pwd, err := os.Getwd() - if err != nil { - panic(err) - } - cmd.Dir = pwd - } else { - cmd.Dir = dir - } - // cmd stdin - stdin, err := cmd.StdinPipe() - if err != nil { - return nil, err - } - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - - stderr, err := cmd.StderrPipe() - if err != nil { - return nil, err - } - - proc := &Process{ - ExecPath: name, - Args: args, - StartTime: time.Now(), - Cmd: cmd, - ExitState: nil, - StdinPipe: stdin, - StdoutPipe: stdout, - StderrPipe: stderr, - } - return proc, nil -} - -// stop the process -func (proc *Process) Stop(kill bool) error { - if kill { - // fmt.Printf("Killing process %v\n", proc.Cmd.Process) - return proc.Cmd.Process.Kill() - } - return proc.Cmd.Process.Signal(os.Interrupt) -} - -// wait for the process -func (proc *Process) Wait() { - err := proc.Cmd.Wait() - if err != nil { - // fmt.Printf("Process exit: %v\n", err) - if exitError, ok := err.(*exec.ExitError); ok { - proc.ExitState = exitError.ProcessState - } - } - proc.ExitState = proc.Cmd.ProcessState - proc.EndTime = time.Now() // TODO make this goroutine-safe -} - -// ReadAll calls ioutil.ReadAll on the StdoutPipe and StderrPipe. -func (proc *Process) ReadAll() (stdout []byte, stderr []byte, err error) { - outbz, err := ioutil.ReadAll(proc.StdoutPipe) - if err != nil { - return nil, nil, err - } - errbz, err := ioutil.ReadAll(proc.StderrPipe) - if err != nil { - return nil, nil, err - } - return outbz, errbz, nil -} diff --git a/tests/util.go b/tests/util.go deleted file mode 100644 index e8479475921f..000000000000 --- a/tests/util.go +++ /dev/null @@ -1,231 +0,0 @@ -package tests - -import ( - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/codec" - rpchttp "github.com/tendermint/tendermint/rpc/client/http" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - tmjsonrpc "github.com/tendermint/tendermint/rpc/jsonrpc/client" -) - -// Wait for the next tendermint block from the Tendermint RPC -// on localhost -func WaitForNextHeightTM(port string) { - WaitForNextNBlocksTM(1, port) -} - -// Wait for N tendermint blocks to pass using the Tendermint RPC -// on localhost -func WaitForNextNBlocksTM(n int64, port string) { - // get the latest block and wait for n more - url := fmt.Sprintf("http://localhost:%v", port) - cl, err := rpchttp.New(url, "/websocket") - if err != nil { - panic(fmt.Sprintf("failed to create Tendermint HTTP client: %s", err)) - } - - var height int64 - - resBlock, err := cl.Block(nil) - if err != nil || resBlock.Block == nil { - // wait for the first block to exist - WaitForHeightTM(1, port) - height = 1 + n - } else { - height = resBlock.Block.Height + n - } - - waitForHeightTM(height, url) -} - -// Wait for the given height from the Tendermint RPC -// on localhost -func WaitForHeightTM(height int64, port string) { - url := fmt.Sprintf("http://localhost:%v", port) - waitForHeightTM(height, url) -} - -func waitForHeightTM(height int64, url string) { - cl, err := rpchttp.New(url, "/websocket") - if err != nil { - panic(fmt.Sprintf("failed to create Tendermint HTTP client: %s", err)) - } - - for { - // get url, try a few times - var resBlock *ctypes.ResultBlock - var err error - INNER: - for i := 0; i < 5; i++ { - resBlock, err = cl.Block(nil) - if err == nil { - break INNER - } - time.Sleep(time.Millisecond * 200) - } - if err != nil { - panic(err) - } - - if resBlock.Block != nil && resBlock.Block.Height >= height { - return - } - - time.Sleep(time.Millisecond * 100) - } -} - -// Wait for height from the LCD API on localhost -func WaitForHeight(height int64, port string) { - url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) - waitForHeight(height, url) -} - -// Whether or not an HTTP status code was "successful" -func StatusOK(statusCode int) bool { - switch statusCode { - case http.StatusOK: - case http.StatusCreated: - case http.StatusNoContent: - return true - } - return false -} - -func waitForHeight(height int64, url string) { - var res *http.Response - var err error - - for { - // Since this is in a testing file we are accepting nolint to be passed - res, err = http.Get(url) // nolint:gosec - if err != nil { - panic(err) - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - panic(err) - } - - if err = res.Body.Close(); err != nil { - panic(err) - } - - var resultBlock ctypes.ResultBlock - if err = cdc.UnmarshalJSON(body, &resultBlock); err != nil { - panic(err) - } - - if resultBlock.Block != nil && resultBlock.Block.Height >= height { - return - } - - time.Sleep(time.Millisecond * 100) - } -} - -// wait for tendermint to start by querying the LCD -func WaitForLCDStart(port string) { - url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) - WaitForStart(url) -} - -// wait for tendermint to start by querying tendermint -func WaitForTMStart(port string) { - url := fmt.Sprintf("http://localhost:%v/block", port) - WaitForStart(url) -} - -// WaitForStart waits for the node to start by pinging the url -// every 100ms for 10s until it returns 200. If it takes longer than 5s, -// it panics. -func WaitForStart(url string) { - var err error - - // ping the status endpoint a few times a second - // for a few seconds until we get a good response. - // otherwise something probably went wrong - for i := 0; i < 100; i++ { - time.Sleep(time.Millisecond * 100) - - var res *http.Response - res, err = http.Get(url) // nolint:gosec - if err != nil || res == nil { - continue - } - // body, _ := ioutil.ReadAll(res.Body) - // fmt.Println("BODY", string(body)) - err = res.Body.Close() - if err != nil { - panic(err) - } - - if res.StatusCode == http.StatusOK { - // good! - return - } - } - // still haven't started up?! panic! - panic(err) -} - -// TODO: these functions just print to Stdout. -// consider using the logger. - -// Wait for the RPC server to respond to /status -func WaitForRPC(laddr string) { - fmt.Println("LADDR", laddr) - client, err := tmjsonrpc.New(laddr) - if err != nil { - panic(fmt.Sprintf("failed to create Tendermint RPC client: %s", err)) - } - - ctypes.RegisterAmino(client.Codec()) - result := new(ctypes.ResultStatus) - for { - _, err := client.Call("status", map[string]interface{}{}, result) - if err == nil { - return - } - fmt.Printf("Waiting for RPC server to start on %s:%v\n", laddr, err) - time.Sleep(time.Millisecond) - } -} - -// ExtractPortFromAddress extract port from listenAddress -// The listenAddress must be some strings like tcp://0.0.0.0:12345 -func ExtractPortFromAddress(listenAddress string) string { - stringList := strings.Split(listenAddress, ":") - length := len(stringList) - if length != 3 { - panic(fmt.Errorf("expected listen address: tcp://0.0.0.0:12345, got %s", listenAddress)) - } - return stringList[2] -} - -// NewTestCaseDir creates a new temporary directory for a test case. -// Returns the directory path and a cleanup function. -// nolint: errcheck -func NewTestCaseDir(t *testing.T) (string, func()) { - dir, err := ioutil.TempDir("", t.Name()+"_") - require.NoError(t, err) - return dir, func() { os.RemoveAll(dir) } -} - -var cdc = codec.New() - -func init() { - ctypes.RegisterAmino(cdc) -} - -//DONTCOVER diff --git a/testutil/cli/cmd.go b/testutil/cli/cmd.go new file mode 100644 index 000000000000..1f09ae92966c --- /dev/null +++ b/testutil/cli/cmd.go @@ -0,0 +1,27 @@ +package cli + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/testutil" +) + +// ExecTestCLICmd builds the client context, mocks the output and executes the command. +func ExecTestCLICmd(clientCtx client.Context, cmd *cobra.Command, extraArgs []string) (testutil.BufferWriter, error) { + cmd.SetArgs(extraArgs) + + _, out := testutil.ApplyMockIO(cmd) + clientCtx = clientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + if err := cmd.ExecuteContext(ctx); err != nil { + return out, err + } + + return out, nil +} diff --git a/testutil/ioutil.go b/testutil/ioutil.go new file mode 100644 index 000000000000..6a7e4fff9cee --- /dev/null +++ b/testutil/ioutil.go @@ -0,0 +1,75 @@ +package testutil + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" +) + +// BufferReader is implemented by types that read from a string buffer. +type BufferReader interface { + io.Reader + Reset(string) +} + +// BufferWriter is implemented by types that write to a buffer. +type BufferWriter interface { + io.Writer + Reset() + Bytes() []byte + String() string +} + +// ApplyMockIO replaces stdin/out/err with buffers that can be used during testing. +// Returns an input BufferReader and an output BufferWriter. +func ApplyMockIO(c *cobra.Command) (BufferReader, BufferWriter) { + mockIn := strings.NewReader("") + mockOut := bytes.NewBufferString("") + + c.SetIn(mockIn) + c.SetOut(mockOut) + c.SetErr(mockOut) + + return mockIn, mockOut +} + +// ApplyMockIODiscardOutputs replaces a cobra.Command output and error streams with a dummy io.Writer. +// Replaces and returns the io.Reader associated to the cobra.Command input stream. +func ApplyMockIODiscardOutErr(c *cobra.Command) BufferReader { + mockIn := strings.NewReader("") + + c.SetIn(mockIn) + c.SetOut(ioutil.Discard) + c.SetErr(ioutil.Discard) + + return mockIn +} + +// Write the given string to a new temporary file. +// Returns an open file for the test to use. +func WriteToNewTempFile(t testing.TB, s string) *os.File { + t.Helper() + + fp := TempFile(t) + _, err := fp.WriteString(s) + + require.Nil(t, err) + + return fp +} + +// TempFile returns a writable temporary file for the test to use. +func TempFile(t testing.TB) *os.File { + t.Helper() + + fp, err := ioutil.TempFile(t.TempDir(), "") + require.NoError(t, err) + + return fp +} diff --git a/testutil/ioutil_test.go b/testutil/ioutil_test.go new file mode 100644 index 000000000000..415e7842c15d --- /dev/null +++ b/testutil/ioutil_test.go @@ -0,0 +1,44 @@ +package testutil_test + +import ( + "io/ioutil" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" +) + +func TestApplyMockIO(t *testing.T) { + cmd := &cobra.Command{} + oldStdin := cmd.InOrStdin() + oldStdout := cmd.OutOrStdout() + oldStderr := cmd.ErrOrStderr() + + testutil.ApplyMockIO(cmd) + + require.NotEqual(t, cmd.InOrStdin(), oldStdin) + require.NotEqual(t, cmd.OutOrStdout(), oldStdout) + require.NotEqual(t, cmd.ErrOrStderr(), oldStderr) + require.Equal(t, cmd.ErrOrStderr(), cmd.OutOrStdout()) +} + +func TestWriteToNewTempFile(t *testing.T) { + tempfile := testutil.WriteToNewTempFile(t, "test string") + tempfile.Close() + + bs, err := ioutil.ReadFile(tempfile.Name()) + require.NoError(t, err) + require.Equal(t, "test string", string(bs)) +} + +func TestApplyMockIODiscardOutErr(t *testing.T) { + cmd := &cobra.Command{} + oldStdin := cmd.InOrStdin() + + testutil.ApplyMockIODiscardOutErr(cmd) + require.NotEqual(t, cmd.InOrStdin(), oldStdin) + require.Equal(t, cmd.OutOrStdout(), ioutil.Discard) + require.Equal(t, cmd.ErrOrStderr(), ioutil.Discard) +} diff --git a/testutil/known_values.go b/testutil/known_values.go new file mode 100644 index 000000000000..a87ccd2af0cc --- /dev/null +++ b/testutil/known_values.go @@ -0,0 +1,6 @@ +package testutil + +const ( + // Tests expect a ledger device initialized to the following mnemonic + TestMnemonic = "equip will roof matter pink blind book anxiety banner elbow sun young" +) diff --git a/testutil/network/doc.go b/testutil/network/doc.go new file mode 100644 index 000000000000..9e44f371ab6c --- /dev/null +++ b/testutil/network/doc.go @@ -0,0 +1,65 @@ +/* +Package network implements and exposes a fully operational in-process Tendermint +test network that consists of at least one or potentially many validators. This +test network can be used primarily for integration tests or unit test suites. + +The test network utilizes SimApp as the ABCI application and uses all the modules +defined in the Cosmos SDK. An in-process test network can be configured with any +number of validators as well as account funds and even custom genesis state. + +When creating a test network, a series of Validator objects are returned. Each +Validator object has useful information such as their address and public key. A +Validator will also provide its RPC, P2P, and API addresses that can be useful +for integration testing. In addition, a Tendermint local RPC client is also provided +which can be handy for making direct RPC calls to Tendermint. + +Note, due to limitations in concurrency and the design of the RPC layer in +Tendermint, only the first Validator object will have an RPC and API client +exposed. Due to this exact same limitation, only a single test network can exist +at a time. A caller must be certain it calls Cleanup after it no longer needs +the network. + +A typical testing flow might look like the following: + + type IntegrationTestSuite struct { + suite.Suite + + cfg testutil.Config + network *testutil.Network + } + + func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testutil.DefaultConfig() + cfg.NumValidators = 1 + + s.cfg = cfg + s.network = testutil.New(s.T(), cfg) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + } + + func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + + // This is important and must be called to ensure other tests can create + // a network! + s.network.Cleanup() + } + + func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + // Use baseURL to make API HTTP requests or use val.RPCClient to make direct + // Tendermint RPC calls. + // ... + } + + func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) + } +*/ +package network diff --git a/testutil/network/network.go b/testutil/network/network.go new file mode 100644 index 000000000000..3beb1b90c0ca --- /dev/null +++ b/testutil/network/network.go @@ -0,0 +1,478 @@ +package network + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" + tmcfg "github.com/tendermint/tendermint/config" + tmflags "github.com/tendermint/tendermint/libs/cli/flags" + "github.com/tendermint/tendermint/libs/log" + tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/node" + tmclient "github.com/tendermint/tendermint/rpc/client" + dbm "github.com/tendermint/tm-db" + "google.golang.org/grpc" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/params" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + 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/genutil" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// package-wide network lock to only allow one test network at a time +var lock = new(sync.Mutex) + +// AppConstructor defines a function which accepts a network configuration and +// creates an ABCI Application to provide to Tendermint. +type AppConstructor = func(val Validator) servertypes.Application + +// NewAppConstructor returns a new simapp AppConstructor +func NewAppConstructor(encodingCfg params.EncodingConfig) AppConstructor { + return func(val Validator) servertypes.Application { + return simapp.NewSimApp( + val.Ctx.Logger, dbm.NewMemDB(), nil, true, make(map[int64]bool), val.Ctx.Config.RootDir, 0, + encodingCfg, + simapp.EmptyAppOptions{}, + baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)), + baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices), + ) + } +} + +// Config defines the necessary configuration used to bootstrap and start an +// in-process local testing network. +type Config struct { + Codec codec.Marshaler + LegacyAmino *codec.LegacyAmino // TODO: Remove! + InterfaceRegistry codectypes.InterfaceRegistry + + TxConfig client.TxConfig + AccountRetriever client.AccountRetriever + AppConstructor AppConstructor // the ABCI application constructor + GenesisState map[string]json.RawMessage // custom gensis state to provide + TimeoutCommit time.Duration // the consensus commitment timeout + ChainID string // the network chain-id + NumValidators int // the total number of validators to create and bond + BondDenom string // the staking bond denomination + MinGasPrices string // the minimum gas prices each validator will accept + AccountTokens sdk.Int // the amount of unique validator tokens (e.g. 1000node0) + StakingTokens sdk.Int // the amount of tokens each validator has available to stake + BondedTokens sdk.Int // the amount of tokens each validator stakes + PruningStrategy string // the pruning strategy each validator will have + EnableLogging bool // enable Tendermint logging to STDOUT + CleanupDir bool // remove base temporary directory during cleanup + SigningAlgo string // signing algorithm for keys + KeyringOptions []keyring.Option +} + +// DefaultConfig returns a sane default configuration suitable for nearly all +// testing requirements. +func DefaultConfig() Config { + encCfg := simapp.MakeTestEncodingConfig() + + return Config{ + Codec: encCfg.Marshaler, + TxConfig: encCfg.TxConfig, + LegacyAmino: encCfg.Amino, + InterfaceRegistry: encCfg.InterfaceRegistry, + AccountRetriever: authtypes.AccountRetriever{}, + AppConstructor: NewAppConstructor(encCfg), + GenesisState: simapp.ModuleBasics.DefaultGenesis(encCfg.Marshaler), + TimeoutCommit: 2 * time.Second, + ChainID: "chain-" + tmrand.NewRand().Str(6), + NumValidators: 4, + BondDenom: sdk.DefaultBondDenom, + MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), + AccountTokens: sdk.TokensFromConsensusPower(1000), + StakingTokens: sdk.TokensFromConsensusPower(500), + BondedTokens: sdk.TokensFromConsensusPower(100), + PruningStrategy: storetypes.PruningOptionNothing, + CleanupDir: true, + SigningAlgo: string(hd.Secp256k1Type), + KeyringOptions: []keyring.Option{}, + } +} + +type ( + // Network defines a local in-process testing network using SimApp. It can be + // configured to start any number of validators, each with its own RPC and API + // clients. Typically, this test network would be used in client and integration + // testing where user input is expected. + // + // Note, due to Tendermint constraints in regards to RPC functionality, there + // may only be one test network running at a time. Thus, any caller must be + // sure to Cleanup after testing is finished in order to allow other tests + // to create networks. In addition, only the first validator will have a valid + // RPC and API server/client. + Network struct { + T *testing.T + BaseDir string + Validators []*Validator + + Config Config + } + + // Validator defines an in-process Tendermint validator node. Through this object, + // a client can make RPC and API calls and interact with any client command + // or handler. + Validator struct { + AppConfig *srvconfig.Config + ClientCtx client.Context + Ctx *server.Context + Dir string + NodeID string + PubKey cryptotypes.PubKey + Moniker string + APIAddress string + RPCAddress string + P2PAddress string + Address sdk.AccAddress + ValAddress sdk.ValAddress + RPCClient tmclient.Client + + tmNode *node.Node + api *api.Server + grpc *grpc.Server + } +) + +// New creates a new Network for integration tests. +func New(t *testing.T, cfg Config) *Network { + // only one caller/test can create and use a network at a time + t.Log("acquiring test network lock") + lock.Lock() + + baseDir, err := ioutil.TempDir(t.TempDir(), cfg.ChainID) + require.NoError(t, err) + t.Logf("created temporary directory: %s", baseDir) + + network := &Network{ + T: t, + BaseDir: baseDir, + Validators: make([]*Validator, cfg.NumValidators), + Config: cfg, + } + + t.Log("preparing test network...") + + monikers := make([]string, cfg.NumValidators) + nodeIDs := make([]string, cfg.NumValidators) + valPubKeys := make([]cryptotypes.PubKey, cfg.NumValidators) + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + buf := bufio.NewReader(os.Stdin) + + // generate private keys, node IDs, and initial transactions + for i := 0; i < cfg.NumValidators; i++ { + appCfg := srvconfig.DefaultConfig() + appCfg.Pruning = cfg.PruningStrategy + appCfg.MinGasPrices = cfg.MinGasPrices + appCfg.API.Enable = true + appCfg.API.Swagger = false + appCfg.Telemetry.Enabled = false + + ctx := server.NewDefaultContext() + tmCfg := ctx.Config + tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + + // Only allow the first validator to expose an RPC, API and gRPC + // server/client due to Tendermint in-process constraints. + apiAddr := "" + tmCfg.RPC.ListenAddress = "" + appCfg.GRPC.Enable = false + if i == 0 { + apiListenAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + appCfg.API.Address = apiListenAddr + + apiURL, err := url.Parse(apiListenAddr) + require.NoError(t, err) + apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) + + rpcAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.RPC.ListenAddress = rpcAddr + + _, grpcPort, err := server.FreeTCPAddr() + require.NoError(t, err) + appCfg.GRPC.Address = fmt.Sprintf("0.0.0.0:%s", grpcPort) + appCfg.GRPC.Enable = true + } + + logger := log.NewNopLogger() + if cfg.EnableLogging { + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger, _ = tmflags.ParseLogLevel("info", logger, tmcfg.DefaultLogLevel) + } + + ctx.Logger = logger + + nodeDirName := fmt.Sprintf("node%d", i) + nodeDir := filepath.Join(network.BaseDir, nodeDirName, "simd") + clientDir := filepath.Join(network.BaseDir, nodeDirName, "simcli") + gentxsDir := filepath.Join(network.BaseDir, "gentxs") + + require.NoError(t, os.MkdirAll(filepath.Join(nodeDir, "config"), 0755)) + require.NoError(t, os.MkdirAll(clientDir, 0755)) + + tmCfg.SetRoot(nodeDir) + tmCfg.Moniker = nodeDirName + monikers[i] = nodeDirName + + proxyAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.ProxyApp = proxyAddr + + p2pAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + tmCfg.P2P.ListenAddress = p2pAddr + tmCfg.P2P.AddrBookStrict = false + tmCfg.P2P.AllowDuplicateIP = true + + nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) + require.NoError(t, err) + nodeIDs[i] = nodeID + valPubKeys[i] = pubKey + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.KeyringOptions...) + require.NoError(t, err) + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos) + require.NoError(t, err) + + addr, secret, err := server.GenerateSaveCoinKey(kb, nodeDirName, true, algo) + require.NoError(t, err) + + info := map[string]string{"secret": secret} + infoBz, err := json.Marshal(info) + require.NoError(t, err) + + // save private key seed words + require.NoError(t, writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, infoBz)) + + balances := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), cfg.AccountTokens), + sdk.NewCoin(cfg.BondDenom, cfg.StakingTokens), + ) + + genFiles = append(genFiles, tmCfg.GenesisFile()) + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + commission, err := sdk.NewDecFromStr("0.5") + require.NoError(t, err) + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + require.NoError(t, err) + + p2pURL, err := url.Parse(p2pAddr) + require.NoError(t, err) + + memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) + fee := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), sdk.NewInt(0))) + txBuilder := cfg.TxConfig.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(createValMsg)) + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(cfg.ChainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(cfg.TxConfig) + + err = tx.Sign(txFactory, nodeDirName, txBuilder, true) + require.NoError(t, err) + + txBz, err := cfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + require.NoError(t, writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz)) + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), appCfg) + + clientCtx := client.Context{}. + WithKeyringDir(clientDir). + WithKeyring(kb). + WithHomeDir(tmCfg.RootDir). + WithChainID(cfg.ChainID). + WithInterfaceRegistry(cfg.InterfaceRegistry). + WithJSONMarshaler(cfg.Codec). + WithLegacyAmino(cfg.LegacyAmino). + WithTxConfig(cfg.TxConfig). + WithAccountRetriever(cfg.AccountRetriever) + + network.Validators[i] = &Validator{ + AppConfig: appCfg, + ClientCtx: clientCtx, + Ctx: ctx, + Dir: filepath.Join(network.BaseDir, nodeDirName), + NodeID: nodeID, + PubKey: pubKey, + Moniker: nodeDirName, + RPCAddress: tmCfg.RPC.ListenAddress, + P2PAddress: tmCfg.P2P.ListenAddress, + APIAddress: apiAddr, + Address: addr, + ValAddress: sdk.ValAddress(addr), + } + } + + require.NoError(t, initGenFiles(cfg, genAccounts, genBalances, genFiles)) + require.NoError(t, collectGenFiles(cfg, network.Validators, network.BaseDir)) + + t.Log("starting test network...") + for _, v := range network.Validators { + require.NoError(t, startInProcess(cfg, v)) + } + + t.Log("started test network") + + // Ensure we cleanup incase any test was abruptly halted (e.g. SIGINT) as any + // defer in a test would not be called. + server.TrapSignal(network.Cleanup) + + return network +} + +// LatestHeight returns the latest height of the network or an error if the +// query fails or no validators exist. +func (n *Network) LatestHeight() (int64, error) { + if len(n.Validators) == 0 { + return 0, errors.New("no validators available") + } + + status, err := n.Validators[0].RPCClient.Status(context.Background()) + if err != nil { + return 0, err + } + + return status.SyncInfo.LatestBlockHeight, nil +} + +// WaitForHeight performs a blocking check where it waits for a block to be +// committed after a given block. If that height is not reached within a timeout, +// an error is returned. Regardless, the latest height queried is returned. +func (n *Network) WaitForHeight(h int64) (int64, error) { + return n.WaitForHeightWithTimeout(h, 10*time.Second) +} + +// WaitForHeightWithTimeout is the same as WaitForHeight except the caller can +// provide a custom timeout. +func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, error) { + ticker := time.NewTicker(time.Second) + timeout := time.After(t) + + if len(n.Validators) == 0 { + return 0, errors.New("no validators available") + } + + var latestHeight int64 + val := n.Validators[0] + + for { + select { + case <-timeout: + ticker.Stop() + return latestHeight, errors.New("timeout exceeded waiting for block") + case <-ticker.C: + status, err := val.RPCClient.Status(context.Background()) + if err == nil && status != nil { + latestHeight = status.SyncInfo.LatestBlockHeight + if latestHeight >= h { + return latestHeight, nil + } + } + } + } +} + +// WaitForNextBlock waits for the next block to be committed, returning an error +// upon failure. +func (n *Network) WaitForNextBlock() error { + lastBlock, err := n.LatestHeight() + if err != nil { + return err + } + + _, err = n.WaitForHeight(lastBlock + 1) + if err != nil { + return err + } + + return err +} + +// Cleanup removes the root testing (temporary) directory and stops both the +// Tendermint and API services. It allows other callers to create and start +// test networks. This method must be called when a test is finished, typically +// in a defer. +func (n *Network) Cleanup() { + defer func() { + lock.Unlock() + n.T.Log("released test network lock") + }() + + n.T.Log("cleaning up test network...") + + for _, v := range n.Validators { + if v.tmNode != nil && v.tmNode.IsRunning() { + _ = v.tmNode.Stop() + } + + if v.api != nil { + _ = v.api.Close() + } + + if v.grpc != nil { + v.grpc.Stop() + } + } + + if n.Config.CleanupDir { + _ = os.RemoveAll(n.BaseDir) + } + + n.T.Log("finished cleaning up test network") +} diff --git a/testutil/network/network_test.go b/testutil/network/network_test.go new file mode 100644 index 000000000000..3e45309f2f12 --- /dev/null +++ b/testutil/network/network_test.go @@ -0,0 +1,42 @@ +// +build norace + +package network_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil/network" +) + +type IntegrationTestSuite struct { + suite.Suite + + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + s.network = network.New(s.T(), network.DefaultConfig()) + s.Require().NotNil(s.network) + + _, 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) TestNetwork_Liveness() { + h, err := s.network.WaitForHeightWithTimeout(10, time.Minute) + s.Require().NoError(err, "expected to reach 10 blocks; got %d", h) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/testutil/network/util.go b/testutil/network/util.go new file mode 100644 index 000000000000..7b09ae6a1d04 --- /dev/null +++ b/testutil/network/util.go @@ -0,0 +1,199 @@ +package network + +import ( + "encoding/json" + "path/filepath" + "time" + + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/p2p" + pvm "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/rpc/client/local" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/cosmos/cosmos-sdk/server/api" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +func startInProcess(cfg Config, val *Validator) error { + logger := val.Ctx.Logger + tmCfg := val.Ctx.Config + tmCfg.Instrumentation.Prometheus = false + + nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) + if err != nil { + return err + } + + app := cfg.AppConstructor(*val) + + genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) + tmNode, err := node.NewNode( + tmCfg, + pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(tmCfg.Instrumentation), + logger.With("module", val.Moniker), + ) + if err != nil { + return err + } + + if err := tmNode.Start(); err != nil { + return err + } + + val.tmNode = tmNode + + if val.RPCAddress != "" { + val.RPCClient = local.New(tmNode) + } + + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + val.ClientCtx = val.ClientCtx. + WithClient(val.RPCClient) + + // Add the tx service in the gRPC router. + app.RegisterTxService(val.ClientCtx) + + // Add the tendermint queries service in the gRPC router. + app.RegisterTendermintService(val.ClientCtx) + } + + if val.APIAddress != "" { + apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) + app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) + + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(*val.AppConfig); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(5 * time.Second): // assume server started successfully + } + + val.api = apiSrv + } + + if val.AppConfig.GRPC.Enable { + grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC.Address) + if err != nil { + return err + } + + val.grpc = grpcSrv + } + + return nil +} + +func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { + genTime := tmtime.Now() + + for i := 0; i < cfg.NumValidators; i++ { + tmCfg := vals[i].Ctx.Config + + nodeDir := filepath.Join(outputDir, vals[i].Moniker, "simd") + gentxsDir := filepath.Join(outputDir, "gentxs") + + tmCfg.Moniker = vals[i].Moniker + tmCfg.SetRoot(nodeDir) + + initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) + + genFile := tmCfg.GenesisFile() + genDoc, err := types.GenesisDocFromFile(genFile) + if err != nil { + return err + } + + appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, + tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}) + if err != nil { + return err + } + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = accounts + cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = genBalances + cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState) + + appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", " ") + if err != nil { + return err + } + + genDoc := types.GenesisDoc{ + ChainID: cfg.ChainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < cfg.NumValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + + return nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0755) + if err != nil { + return err + } + + err = tmos.WriteFile(file, contents, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/testutil/rest.go b/testutil/rest.go new file mode 100644 index 000000000000..b468b16bedd2 --- /dev/null +++ b/testutil/rest.go @@ -0,0 +1,38 @@ +package testutil + +import ( + "io/ioutil" + "net/http" +) + +// GetRequestWithHeaders defines a wrapper around an HTTP GET request with a provided URL +// and custom headers +// An error is returned if the request or reading the body fails. +func GetRequestWithHeaders(url string, headers map[string]string) ([]byte, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + client := &http.Client{} + + for key, value := range headers { + req.Header.Set(key, value) + } + + res, err := client.Do(req) + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if err = res.Body.Close(); err != nil { + return nil, err + } + + return body, nil +} diff --git a/testutil/testdata/animal.go b/testutil/testdata/animal.go new file mode 100644 index 000000000000..96981a40b9b4 --- /dev/null +++ b/testutil/testdata/animal.go @@ -0,0 +1,77 @@ +package testdata + +// DONTCOVER +// nolint + +import ( + "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +type Animal interface { + proto.Message + + Greet() string +} + +func (c Cat) Greet() string { + return fmt.Sprintf("Meow, my name is %s", c.Moniker) +} + +func (d Dog) Greet() string { + return fmt.Sprintf("Roof, my name is %s", d.Name) +} + +var _ types.UnpackInterfacesMessage = HasAnimal{} + +func (m HasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var animal Animal + return unpacker.UnpackAny(m.Animal, &animal) +} + +type HasAnimalI interface { + TheAnimal() Animal +} + +var _ HasAnimalI = &HasAnimal{} + +func (m HasAnimal) TheAnimal() Animal { + return m.Animal.GetCachedValue().(Animal) +} + +type HasHasAnimalI interface { + TheHasAnimal() HasAnimalI +} + +var _ HasHasAnimalI = &HasHasAnimal{} + +func (m HasHasAnimal) TheHasAnimal() HasAnimalI { + return m.HasAnimal.GetCachedValue().(HasAnimalI) +} + +var _ types.UnpackInterfacesMessage = HasHasAnimal{} + +func (m HasHasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var animal HasAnimalI + return unpacker.UnpackAny(m.HasAnimal, &animal) +} + +type HasHasHasAnimalI interface { + TheHasHasAnimal() HasHasAnimalI +} + +var _ HasHasAnimalI = &HasHasAnimal{} + +func (m HasHasHasAnimal) TheHasHasAnimal() HasHasAnimalI { + return m.HasHasAnimal.GetCachedValue().(HasHasAnimalI) +} + +var _ types.UnpackInterfacesMessage = HasHasHasAnimal{} + +func (m HasHasHasAnimal) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var animal HasHasAnimalI + return unpacker.UnpackAny(m.HasHasAnimal, &animal) +} diff --git a/testutil/testdata/codec.go b/testutil/testdata/codec.go new file mode 100644 index 000000000000..d5c6e8abd2f2 --- /dev/null +++ b/testutil/testdata/codec.go @@ -0,0 +1,51 @@ +package testdata + +import ( + amino "github.com/tendermint/go-amino" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func NewTestInterfaceRegistry() types.InterfaceRegistry { + registry := types.NewInterfaceRegistry() + RegisterInterfaces(registry) + return registry +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), &TestMsg{}) + + registry.RegisterInterface("Animal", (*Animal)(nil)) + registry.RegisterImplementations( + (*Animal)(nil), + &Dog{}, + &Cat{}, + ) + registry.RegisterImplementations( + (*HasAnimalI)(nil), + &HasAnimal{}, + ) + registry.RegisterImplementations( + (*HasHasAnimalI)(nil), + &HasHasAnimal{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +func NewTestAmino() *amino.Codec { + cdc := amino.NewCodec() + cdc.RegisterInterface((*Animal)(nil), nil) + cdc.RegisterConcrete(&Dog{}, "testdata/Dog", nil) + cdc.RegisterConcrete(&Cat{}, "testdata/Cat", nil) + + cdc.RegisterInterface((*HasAnimalI)(nil), nil) + cdc.RegisterConcrete(&HasAnimal{}, "testdata/HasAnimal", nil) + + cdc.RegisterInterface((*HasHasAnimalI)(nil), nil) + cdc.RegisterConcrete(&HasHasAnimal{}, "testdata/HasHasAnimal", nil) + + return cdc +} diff --git a/testutil/testdata/grpc_query.go b/testutil/testdata/grpc_query.go new file mode 100644 index 000000000000..6e2b64152995 --- /dev/null +++ b/testutil/testdata/grpc_query.go @@ -0,0 +1,53 @@ +package testdata + +import ( + "context" + "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" +) + +type QueryImpl struct{} + +var _ QueryServer = QueryImpl{} + +func (e QueryImpl) TestAny(_ context.Context, request *TestAnyRequest) (*TestAnyResponse, error) { + animal, ok := request.AnyAnimal.GetCachedValue().(Animal) + if !ok { + return nil, fmt.Errorf("expected Animal") + } + + any, err := types.NewAnyWithValue(animal.(proto.Message)) + if err != nil { + return nil, err + } + + return &TestAnyResponse{HasAnimal: &HasAnimal{ + Animal: any, + X: 10, + }}, nil +} + +func (e QueryImpl) Echo(_ context.Context, req *EchoRequest) (*EchoResponse, error) { + return &EchoResponse{Message: req.Message}, nil +} + +func (e QueryImpl) SayHello(_ context.Context, request *SayHelloRequest) (*SayHelloResponse, error) { + greeting := fmt.Sprintf("Hello %s!", request.Name) + return &SayHelloResponse{Greeting: greeting}, nil +} + +var _ types.UnpackInterfacesMessage = &TestAnyRequest{} + +func (m *TestAnyRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var animal Animal + return unpacker.UnpackAny(m.AnyAnimal, &animal) +} + +var _ types.UnpackInterfacesMessage = &TestAnyResponse{} + +func (m *TestAnyResponse) UnpackInterfaces(unpacker types.AnyUnpacker) error { + return m.HasAnimal.UnpackInterfaces(unpacker) +} diff --git a/testutil/testdata/msg_server.go b/testutil/testdata/msg_server.go new file mode 100644 index 000000000000..3b434c68c86c --- /dev/null +++ b/testutil/testdata/msg_server.go @@ -0,0 +1,16 @@ +package testdata + +import ( + "context" +) + +type MsgServerImpl struct{} + +var _ MsgServer = MsgServerImpl{} + +// CreateDog implements the MsgServer interface. +func (m MsgServerImpl) CreateDog(_ context.Context, msg *MsgCreateDog) (*MsgCreateDogResponse, error) { + return &MsgCreateDogResponse{ + Name: msg.Dog.Name, + }, nil +} diff --git a/testutil/testdata/query.pb.go b/testutil/testdata/query.pb.go new file mode 100644 index 000000000000..e7d38dc7ceb8 --- /dev/null +++ b/testutil/testdata/query.pb.go @@ -0,0 +1,1353 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: query.proto + +package testdata + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +type EchoRequest struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *EchoRequest) Reset() { *m = EchoRequest{} } +func (m *EchoRequest) String() string { return proto.CompactTextString(m) } +func (*EchoRequest) ProtoMessage() {} +func (*EchoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{0} +} +func (m *EchoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EchoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EchoRequest.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 *EchoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EchoRequest.Merge(m, src) +} +func (m *EchoRequest) XXX_Size() int { + return m.Size() +} +func (m *EchoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EchoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EchoRequest proto.InternalMessageInfo + +func (m *EchoRequest) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type EchoResponse struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *EchoResponse) Reset() { *m = EchoResponse{} } +func (m *EchoResponse) String() string { return proto.CompactTextString(m) } +func (*EchoResponse) ProtoMessage() {} +func (*EchoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{1} +} +func (m *EchoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EchoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EchoResponse.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 *EchoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EchoResponse.Merge(m, src) +} +func (m *EchoResponse) XXX_Size() int { + return m.Size() +} +func (m *EchoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EchoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EchoResponse proto.InternalMessageInfo + +func (m *EchoResponse) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type SayHelloRequest struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *SayHelloRequest) Reset() { *m = SayHelloRequest{} } +func (m *SayHelloRequest) String() string { return proto.CompactTextString(m) } +func (*SayHelloRequest) ProtoMessage() {} +func (*SayHelloRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{2} +} +func (m *SayHelloRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SayHelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SayHelloRequest.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 *SayHelloRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SayHelloRequest.Merge(m, src) +} +func (m *SayHelloRequest) XXX_Size() int { + return m.Size() +} +func (m *SayHelloRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SayHelloRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SayHelloRequest proto.InternalMessageInfo + +func (m *SayHelloRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type SayHelloResponse struct { + Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"` +} + +func (m *SayHelloResponse) Reset() { *m = SayHelloResponse{} } +func (m *SayHelloResponse) String() string { return proto.CompactTextString(m) } +func (*SayHelloResponse) ProtoMessage() {} +func (*SayHelloResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{3} +} +func (m *SayHelloResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SayHelloResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SayHelloResponse.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 *SayHelloResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SayHelloResponse.Merge(m, src) +} +func (m *SayHelloResponse) XXX_Size() int { + return m.Size() +} +func (m *SayHelloResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SayHelloResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SayHelloResponse proto.InternalMessageInfo + +func (m *SayHelloResponse) GetGreeting() string { + if m != nil { + return m.Greeting + } + return "" +} + +type TestAnyRequest struct { + AnyAnimal *types.Any `protobuf:"bytes,1,opt,name=any_animal,json=anyAnimal,proto3" json:"any_animal,omitempty"` +} + +func (m *TestAnyRequest) Reset() { *m = TestAnyRequest{} } +func (m *TestAnyRequest) String() string { return proto.CompactTextString(m) } +func (*TestAnyRequest) ProtoMessage() {} +func (*TestAnyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{4} +} +func (m *TestAnyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestAnyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestAnyRequest.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 *TestAnyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestAnyRequest.Merge(m, src) +} +func (m *TestAnyRequest) XXX_Size() int { + return m.Size() +} +func (m *TestAnyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TestAnyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TestAnyRequest proto.InternalMessageInfo + +func (m *TestAnyRequest) GetAnyAnimal() *types.Any { + if m != nil { + return m.AnyAnimal + } + return nil +} + +type TestAnyResponse struct { + HasAnimal *HasAnimal `protobuf:"bytes,1,opt,name=has_animal,json=hasAnimal,proto3" json:"has_animal,omitempty"` +} + +func (m *TestAnyResponse) Reset() { *m = TestAnyResponse{} } +func (m *TestAnyResponse) String() string { return proto.CompactTextString(m) } +func (*TestAnyResponse) ProtoMessage() {} +func (*TestAnyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5c6ac9b241082464, []int{5} +} +func (m *TestAnyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestAnyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestAnyResponse.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 *TestAnyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestAnyResponse.Merge(m, src) +} +func (m *TestAnyResponse) XXX_Size() int { + return m.Size() +} +func (m *TestAnyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TestAnyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TestAnyResponse proto.InternalMessageInfo + +func (m *TestAnyResponse) GetHasAnimal() *HasAnimal { + if m != nil { + return m.HasAnimal + } + return nil +} + +func init() { + proto.RegisterType((*EchoRequest)(nil), "testdata.EchoRequest") + proto.RegisterType((*EchoResponse)(nil), "testdata.EchoResponse") + proto.RegisterType((*SayHelloRequest)(nil), "testdata.SayHelloRequest") + proto.RegisterType((*SayHelloResponse)(nil), "testdata.SayHelloResponse") + proto.RegisterType((*TestAnyRequest)(nil), "testdata.TestAnyRequest") + proto.RegisterType((*TestAnyResponse)(nil), "testdata.TestAnyResponse") +} + +func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } + +var fileDescriptor_5c6ac9b241082464 = []byte{ + // 359 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcf, 0x4f, 0xc2, 0x30, + 0x14, 0xc7, 0x59, 0x82, 0x02, 0x0f, 0x03, 0xa6, 0xfe, 0x08, 0xf4, 0xb0, 0x98, 0x25, 0x46, 0x2e, + 0x76, 0x09, 0xc4, 0xab, 0x09, 0x26, 0x24, 0x5c, 0x45, 0x4f, 0x5e, 0x4c, 0x81, 0xba, 0x2d, 0x6e, + 0x2d, 0xd0, 0xee, 0xb0, 0xff, 0xc2, 0x7f, 0xc9, 0x9b, 0x47, 0x8e, 0x1e, 0x0d, 0xfc, 0x23, 0x66, + 0x5b, 0xbb, 0x09, 0x21, 0x9e, 0xda, 0xd7, 0x7e, 0xde, 0xe7, 0xe5, 0x7d, 0xa1, 0xb9, 0x8c, 0xd9, + 0x2a, 0x21, 0x8b, 0x95, 0x50, 0x02, 0xd5, 0x15, 0x93, 0x6a, 0x4e, 0x15, 0xc5, 0x5d, 0x4f, 0x08, + 0x2f, 0x64, 0x6e, 0xf6, 0x3e, 0x8d, 0xdf, 0x5c, 0xca, 0x35, 0x84, 0x5b, 0x06, 0xca, 0x6b, 0xe7, + 0x06, 0x9a, 0xa3, 0x99, 0x2f, 0x26, 0x6c, 0x19, 0x33, 0xa9, 0x50, 0x07, 0x6a, 0x11, 0x93, 0x92, + 0x7a, 0xac, 0x63, 0x5d, 0x59, 0xbd, 0xc6, 0xc4, 0x94, 0x4e, 0x0f, 0x4e, 0x72, 0x50, 0x2e, 0x04, + 0x97, 0xec, 0x1f, 0xf2, 0x1a, 0xda, 0x4f, 0x34, 0x19, 0xb3, 0x30, 0x2c, 0xb4, 0x08, 0xaa, 0x9c, + 0x46, 0x86, 0xcc, 0xee, 0x0e, 0x81, 0xd3, 0x12, 0xd3, 0x52, 0x0c, 0x75, 0x6f, 0xc5, 0x98, 0x0a, + 0xb8, 0xa7, 0xd9, 0xa2, 0x76, 0x46, 0xd0, 0x7a, 0x66, 0x52, 0x0d, 0x79, 0x62, 0xac, 0x03, 0x00, + 0xca, 0x93, 0x57, 0xca, 0x83, 0x88, 0x86, 0x19, 0xdf, 0xec, 0x9f, 0x93, 0x7c, 0x77, 0x62, 0x76, + 0x27, 0x69, 0x43, 0x83, 0xf2, 0x64, 0x98, 0x61, 0xce, 0x08, 0xda, 0x85, 0x46, 0x4f, 0xed, 0x03, + 0xf8, 0x54, 0xee, 0x7a, 0xce, 0x48, 0x11, 0xd4, 0x98, 0xca, 0xbc, 0x77, 0xd2, 0xf0, 0xcd, 0xb5, + 0xff, 0x69, 0xc1, 0xd1, 0x63, 0x1a, 0x3e, 0xba, 0x83, 0x6a, 0x1a, 0x0c, 0xba, 0x28, 0x3b, 0xfe, + 0x24, 0x8a, 0x2f, 0xf7, 0x9f, 0xf5, 0xd0, 0x21, 0xd4, 0xcd, 0xfa, 0xa8, 0x5b, 0x32, 0x7b, 0xc9, + 0x61, 0x7c, 0xe8, 0x4b, 0x2b, 0xee, 0xa1, 0xa6, 0x57, 0x41, 0x9d, 0x12, 0xdb, 0x0d, 0x09, 0x77, + 0x0f, 0xfc, 0xe4, 0xfd, 0x0f, 0xe3, 0xaf, 0x8d, 0x6d, 0xad, 0x37, 0xb6, 0xf5, 0xb3, 0xb1, 0xad, + 0x8f, 0xad, 0x5d, 0x59, 0x6f, 0xed, 0xca, 0xf7, 0xd6, 0xae, 0xbc, 0x10, 0x2f, 0x50, 0x7e, 0x3c, + 0x25, 0x33, 0x11, 0xb9, 0x33, 0x21, 0x23, 0x21, 0xf5, 0x71, 0x2b, 0xe7, 0xef, 0x6e, 0x2a, 0x8c, + 0x55, 0x10, 0xba, 0xc6, 0x3c, 0x3d, 0xce, 0xd2, 0x1e, 0xfc, 0x06, 0x00, 0x00, 0xff, 0xff, 0xe8, + 0xb4, 0x42, 0x4e, 0x90, 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 + +// 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 { + Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) + SayHello(ctx context.Context, in *SayHelloRequest, opts ...grpc.CallOption) (*SayHelloResponse, error) + TestAny(ctx context.Context, in *TestAnyRequest, opts ...grpc.CallOption) (*TestAnyResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { + out := new(EchoResponse) + err := c.cc.Invoke(ctx, "/testdata.Query/Echo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) SayHello(ctx context.Context, in *SayHelloRequest, opts ...grpc.CallOption) (*SayHelloResponse, error) { + out := new(SayHelloResponse) + err := c.cc.Invoke(ctx, "/testdata.Query/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TestAny(ctx context.Context, in *TestAnyRequest, opts ...grpc.CallOption) (*TestAnyResponse, error) { + out := new(TestAnyResponse) + err := c.cc.Invoke(ctx, "/testdata.Query/TestAny", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + Echo(context.Context, *EchoRequest) (*EchoResponse, error) + SayHello(context.Context, *SayHelloRequest) (*SayHelloResponse, error) + TestAny(context.Context, *TestAnyRequest) (*TestAnyResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Echo(ctx context.Context, req *EchoRequest) (*EchoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") +} +func (*UnimplementedQueryServer) SayHello(ctx context.Context, req *SayHelloRequest) (*SayHelloResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (*UnimplementedQueryServer) TestAny(ctx context.Context, req *TestAnyRequest) (*TestAnyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TestAny not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EchoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Echo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.Query/Echo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Echo(ctx, req.(*EchoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SayHelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.Query/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).SayHello(ctx, req.(*SayHelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TestAny_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TestAnyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TestAny(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.Query/TestAny", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TestAny(ctx, req.(*TestAnyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "testdata.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Echo", + Handler: _Query_Echo_Handler, + }, + { + MethodName: "SayHello", + Handler: _Query_SayHello_Handler, + }, + { + MethodName: "TestAny", + Handler: _Query_TestAny_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "query.proto", +} + +func (m *EchoRequest) 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 *EchoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EchoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EchoResponse) 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 *EchoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EchoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SayHelloRequest) 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 *SayHelloRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SayHelloRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SayHelloResponse) 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 *SayHelloResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SayHelloResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Greeting) > 0 { + i -= len(m.Greeting) + copy(dAtA[i:], m.Greeting) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Greeting))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestAnyRequest) 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 *TestAnyRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestAnyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AnyAnimal != nil { + { + size, err := m.AnyAnimal.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 *TestAnyResponse) 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 *TestAnyResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestAnyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HasAnimal != nil { + { + size, err := m.HasAnimal.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 *EchoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *EchoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Message) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *SayHelloRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *SayHelloResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Greeting) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *TestAnyRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AnyAnimal != nil { + l = m.AnyAnimal.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *TestAnyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasAnimal != nil { + l = m.HasAnimal.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 *EchoRequest) 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: EchoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EchoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", 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.Message = 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 *EchoResponse) 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: EchoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EchoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", 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.Message = 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 *SayHelloRequest) 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: SayHelloRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SayHelloRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.Name = 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 *SayHelloResponse) 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: SayHelloResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SayHelloResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Greeting", 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.Greeting = 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 *TestAnyRequest) 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: TestAnyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestAnyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AnyAnimal", 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.AnyAnimal == nil { + m.AnyAnimal = &types.Any{} + } + if err := m.AnyAnimal.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 *TestAnyResponse) 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: TestAnyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestAnyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasAnimal", 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.HasAnimal == nil { + m.HasAnimal = &HasAnimal{} + } + if err := m.HasAnimal.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/testutil/testdata/query.proto b/testutil/testdata/query.proto new file mode 100644 index 000000000000..3a60acaad1f8 --- /dev/null +++ b/testutil/testdata/query.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; +package testdata; + +import "google/protobuf/any.proto"; +import "testdata.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/testutil/testdata"; + +// Query tests the protobuf Query service as defined in +// https://github.com/cosmos/cosmos-sdk/issues/5921. +service Query { + rpc Echo(EchoRequest) returns (EchoResponse); + rpc SayHello(SayHelloRequest) returns (SayHelloResponse); + rpc TestAny(TestAnyRequest) returns (TestAnyResponse); +} + +message EchoRequest { + string message = 1; +} + +message EchoResponse { + string message = 1; +} + +message SayHelloRequest { + string name = 1; +} + +message SayHelloResponse { + string greeting = 1; +} + +message TestAnyRequest { + google.protobuf.Any any_animal = 1; +} + +message TestAnyResponse { + testdata.HasAnimal has_animal = 1; +} diff --git a/testutil/testdata/testdata.pb.go b/testutil/testdata/testdata.pb.go new file mode 100644 index 000000000000..400e64936810 --- /dev/null +++ b/testutil/testdata/testdata.pb.go @@ -0,0 +1,1394 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: testdata.proto + +package testdata + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + _ "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 + +type Dog struct { + Size_ string `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *Dog) Reset() { *m = Dog{} } +func (m *Dog) String() string { return proto.CompactTextString(m) } +func (*Dog) ProtoMessage() {} +func (*Dog) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{0} +} +func (m *Dog) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Dog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Dog.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 *Dog) XXX_Merge(src proto.Message) { + xxx_messageInfo_Dog.Merge(m, src) +} +func (m *Dog) XXX_Size() int { + return m.Size() +} +func (m *Dog) XXX_DiscardUnknown() { + xxx_messageInfo_Dog.DiscardUnknown(m) +} + +var xxx_messageInfo_Dog proto.InternalMessageInfo + +func (m *Dog) GetSize_() string { + if m != nil { + return m.Size_ + } + return "" +} + +func (m *Dog) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type Cat struct { + Moniker string `protobuf:"bytes,1,opt,name=moniker,proto3" json:"moniker,omitempty"` + Lives int32 `protobuf:"varint,2,opt,name=lives,proto3" json:"lives,omitempty"` +} + +func (m *Cat) Reset() { *m = Cat{} } +func (m *Cat) String() string { return proto.CompactTextString(m) } +func (*Cat) ProtoMessage() {} +func (*Cat) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{1} +} +func (m *Cat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Cat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Cat.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 *Cat) XXX_Merge(src proto.Message) { + xxx_messageInfo_Cat.Merge(m, src) +} +func (m *Cat) XXX_Size() int { + return m.Size() +} +func (m *Cat) XXX_DiscardUnknown() { + xxx_messageInfo_Cat.DiscardUnknown(m) +} + +var xxx_messageInfo_Cat proto.InternalMessageInfo + +func (m *Cat) GetMoniker() string { + if m != nil { + return m.Moniker + } + return "" +} + +func (m *Cat) GetLives() int32 { + if m != nil { + return m.Lives + } + return 0 +} + +type HasAnimal struct { + Animal *types.Any `protobuf:"bytes,1,opt,name=animal,proto3" json:"animal,omitempty"` + X int64 `protobuf:"varint,2,opt,name=x,proto3" json:"x,omitempty"` +} + +func (m *HasAnimal) Reset() { *m = HasAnimal{} } +func (m *HasAnimal) String() string { return proto.CompactTextString(m) } +func (*HasAnimal) ProtoMessage() {} +func (*HasAnimal) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{2} +} +func (m *HasAnimal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HasAnimal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HasAnimal.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 *HasAnimal) XXX_Merge(src proto.Message) { + xxx_messageInfo_HasAnimal.Merge(m, src) +} +func (m *HasAnimal) XXX_Size() int { + return m.Size() +} +func (m *HasAnimal) XXX_DiscardUnknown() { + xxx_messageInfo_HasAnimal.DiscardUnknown(m) +} + +var xxx_messageInfo_HasAnimal proto.InternalMessageInfo + +func (m *HasAnimal) GetAnimal() *types.Any { + if m != nil { + return m.Animal + } + return nil +} + +func (m *HasAnimal) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +type HasHasAnimal struct { + HasAnimal *types.Any `protobuf:"bytes,1,opt,name=has_animal,json=hasAnimal,proto3" json:"has_animal,omitempty"` +} + +func (m *HasHasAnimal) Reset() { *m = HasHasAnimal{} } +func (m *HasHasAnimal) String() string { return proto.CompactTextString(m) } +func (*HasHasAnimal) ProtoMessage() {} +func (*HasHasAnimal) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{3} +} +func (m *HasHasAnimal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HasHasAnimal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HasHasAnimal.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 *HasHasAnimal) XXX_Merge(src proto.Message) { + xxx_messageInfo_HasHasAnimal.Merge(m, src) +} +func (m *HasHasAnimal) XXX_Size() int { + return m.Size() +} +func (m *HasHasAnimal) XXX_DiscardUnknown() { + xxx_messageInfo_HasHasAnimal.DiscardUnknown(m) +} + +var xxx_messageInfo_HasHasAnimal proto.InternalMessageInfo + +func (m *HasHasAnimal) GetHasAnimal() *types.Any { + if m != nil { + return m.HasAnimal + } + return nil +} + +type HasHasHasAnimal struct { + HasHasAnimal *types.Any `protobuf:"bytes,1,opt,name=has_has_animal,json=hasHasAnimal,proto3" json:"has_has_animal,omitempty"` +} + +func (m *HasHasHasAnimal) Reset() { *m = HasHasHasAnimal{} } +func (m *HasHasHasAnimal) String() string { return proto.CompactTextString(m) } +func (*HasHasHasAnimal) ProtoMessage() {} +func (*HasHasHasAnimal) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{4} +} +func (m *HasHasHasAnimal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HasHasHasAnimal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HasHasHasAnimal.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 *HasHasHasAnimal) XXX_Merge(src proto.Message) { + xxx_messageInfo_HasHasHasAnimal.Merge(m, src) +} +func (m *HasHasHasAnimal) XXX_Size() int { + return m.Size() +} +func (m *HasHasHasAnimal) XXX_DiscardUnknown() { + xxx_messageInfo_HasHasHasAnimal.DiscardUnknown(m) +} + +var xxx_messageInfo_HasHasHasAnimal proto.InternalMessageInfo + +func (m *HasHasHasAnimal) GetHasHasAnimal() *types.Any { + if m != nil { + return m.HasHasAnimal + } + return nil +} + +// bad MultiSignature with extra fields +type BadMultiSignature struct { + Signatures [][]byte `protobuf:"bytes,1,rep,name=signatures,proto3" json:"signatures,omitempty"` + MaliciousField []byte `protobuf:"bytes,5,opt,name=malicious_field,json=maliciousField,proto3" json:"malicious_field,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *BadMultiSignature) Reset() { *m = BadMultiSignature{} } +func (m *BadMultiSignature) String() string { return proto.CompactTextString(m) } +func (*BadMultiSignature) ProtoMessage() {} +func (*BadMultiSignature) Descriptor() ([]byte, []int) { + return fileDescriptor_40c4782d007dfce9, []int{5} +} +func (m *BadMultiSignature) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BadMultiSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BadMultiSignature.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 *BadMultiSignature) XXX_Merge(src proto.Message) { + xxx_messageInfo_BadMultiSignature.Merge(m, src) +} +func (m *BadMultiSignature) XXX_Size() int { + return m.Size() +} +func (m *BadMultiSignature) XXX_DiscardUnknown() { + xxx_messageInfo_BadMultiSignature.DiscardUnknown(m) +} + +var xxx_messageInfo_BadMultiSignature proto.InternalMessageInfo + +func (m *BadMultiSignature) GetSignatures() [][]byte { + if m != nil { + return m.Signatures + } + return nil +} + +func (m *BadMultiSignature) GetMaliciousField() []byte { + if m != nil { + return m.MaliciousField + } + return nil +} + +func init() { + proto.RegisterType((*Dog)(nil), "testdata.Dog") + proto.RegisterType((*Cat)(nil), "testdata.Cat") + proto.RegisterType((*HasAnimal)(nil), "testdata.HasAnimal") + proto.RegisterType((*HasHasAnimal)(nil), "testdata.HasHasAnimal") + proto.RegisterType((*HasHasHasAnimal)(nil), "testdata.HasHasHasAnimal") + proto.RegisterType((*BadMultiSignature)(nil), "testdata.BadMultiSignature") +} + +func init() { proto.RegisterFile("testdata.proto", fileDescriptor_40c4782d007dfce9) } + +var fileDescriptor_40c4782d007dfce9 = []byte{ + // 365 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x31, 0x4f, 0xc2, 0x40, + 0x18, 0x86, 0x39, 0x0b, 0x28, 0x9f, 0x0d, 0xc4, 0x0b, 0x43, 0x65, 0xa8, 0xa4, 0x8b, 0x0c, 0xd2, + 0x26, 0x12, 0x17, 0x36, 0xc0, 0x28, 0x0b, 0x4b, 0xdd, 0x5c, 0xc8, 0x95, 0x1e, 0xed, 0x85, 0xb6, + 0x67, 0xb8, 0xab, 0x01, 0x7f, 0x85, 0x7f, 0xc1, 0x7f, 0xe3, 0xc8, 0xe8, 0x68, 0xe0, 0x8f, 0x98, + 0x5e, 0xa9, 0x30, 0x32, 0xf5, 0x7d, 0xdf, 0xaf, 0xef, 0x93, 0xef, 0x92, 0x0f, 0xea, 0x92, 0x0a, + 0xe9, 0x13, 0x49, 0xec, 0xb7, 0x25, 0x97, 0x1c, 0x5f, 0x14, 0xbe, 0xd5, 0x0c, 0x78, 0xc0, 0x55, + 0xe8, 0x64, 0x2a, 0x9f, 0xb7, 0xae, 0x03, 0xce, 0x83, 0x88, 0x3a, 0xca, 0x79, 0xe9, 0xdc, 0x21, + 0xc9, 0x3a, 0x1f, 0x59, 0x5d, 0xd0, 0x1e, 0x79, 0x80, 0x31, 0x94, 0x05, 0xfb, 0xa0, 0x06, 0x6a, + 0xa3, 0x4e, 0xcd, 0x55, 0x3a, 0xcb, 0x12, 0x12, 0x53, 0xe3, 0x2c, 0xcf, 0x32, 0x6d, 0x3d, 0x80, + 0x36, 0x22, 0x12, 0x1b, 0x70, 0x1e, 0xf3, 0x84, 0x2d, 0xe8, 0x72, 0xdf, 0x28, 0x2c, 0x6e, 0x42, + 0x25, 0x62, 0xef, 0x54, 0xa8, 0x56, 0xc5, 0xcd, 0x8d, 0xf5, 0x0c, 0xb5, 0x31, 0x11, 0x83, 0x84, + 0xc5, 0x24, 0xc2, 0x77, 0x50, 0x25, 0x4a, 0xa9, 0xee, 0xe5, 0x7d, 0xd3, 0xce, 0xd7, 0xb3, 0x8b, + 0xf5, 0xec, 0x41, 0xb2, 0x76, 0xf7, 0xff, 0x60, 0x1d, 0xd0, 0x4a, 0xc1, 0x34, 0x17, 0xad, 0xac, + 0x11, 0xe8, 0x63, 0x22, 0x0e, 0xac, 0x1e, 0x40, 0x48, 0xc4, 0xf4, 0x04, 0x5e, 0x2d, 0x2c, 0x4a, + 0xd6, 0x04, 0x1a, 0x39, 0xe4, 0xc0, 0xe9, 0x43, 0x3d, 0xe3, 0x9c, 0xc8, 0xd2, 0xc3, 0xa3, 0xae, + 0xe5, 0xc1, 0xd5, 0x90, 0xf8, 0x93, 0x34, 0x92, 0xec, 0x85, 0x05, 0x09, 0x91, 0xe9, 0x92, 0x62, + 0x13, 0x40, 0x14, 0x46, 0x18, 0xa8, 0xad, 0x75, 0x74, 0xf7, 0x28, 0xc1, 0xb7, 0xd0, 0x88, 0x49, + 0xc4, 0x66, 0x8c, 0xa7, 0x62, 0x3a, 0x67, 0x34, 0xf2, 0x8d, 0x4a, 0x1b, 0x75, 0x74, 0xb7, 0xfe, + 0x1f, 0x3f, 0x65, 0x69, 0xbf, 0xbc, 0xf9, 0xba, 0x41, 0xc3, 0xf1, 0xf7, 0xd6, 0x44, 0x9b, 0xad, + 0x89, 0x7e, 0xb7, 0x26, 0xfa, 0xdc, 0x99, 0xa5, 0xcd, 0xce, 0x2c, 0xfd, 0xec, 0xcc, 0xd2, 0xab, + 0x1d, 0x30, 0x19, 0xa6, 0x9e, 0x3d, 0xe3, 0xb1, 0x33, 0xe3, 0x22, 0xe6, 0x62, 0xff, 0xe9, 0x0a, + 0x7f, 0xe1, 0x64, 0x87, 0x91, 0x4a, 0x16, 0x39, 0xc5, 0x85, 0x78, 0x55, 0xf5, 0x92, 0xde, 0x5f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x51, 0x62, 0x40, 0x44, 0x02, 0x00, 0x00, +} + +func (m *Dog) 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 *Dog) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Dog) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTestdata(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Size_) > 0 { + i -= len(m.Size_) + copy(dAtA[i:], m.Size_) + i = encodeVarintTestdata(dAtA, i, uint64(len(m.Size_))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Cat) 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 *Cat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Cat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Lives != 0 { + i = encodeVarintTestdata(dAtA, i, uint64(m.Lives)) + i-- + dAtA[i] = 0x10 + } + if len(m.Moniker) > 0 { + i -= len(m.Moniker) + copy(dAtA[i:], m.Moniker) + i = encodeVarintTestdata(dAtA, i, uint64(len(m.Moniker))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *HasAnimal) 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 *HasAnimal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HasAnimal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.X != 0 { + i = encodeVarintTestdata(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x10 + } + if m.Animal != nil { + { + size, err := m.Animal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTestdata(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *HasHasAnimal) 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 *HasHasAnimal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HasHasAnimal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HasAnimal != nil { + { + size, err := m.HasAnimal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTestdata(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *HasHasHasAnimal) 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 *HasHasHasAnimal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HasHasHasAnimal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.HasHasAnimal != nil { + { + size, err := m.HasHasAnimal.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTestdata(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BadMultiSignature) 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 *BadMultiSignature) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BadMultiSignature) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.MaliciousField) > 0 { + i -= len(m.MaliciousField) + copy(dAtA[i:], m.MaliciousField) + i = encodeVarintTestdata(dAtA, i, uint64(len(m.MaliciousField))) + i-- + dAtA[i] = 0x2a + } + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintTestdata(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintTestdata(dAtA []byte, offset int, v uint64) int { + offset -= sovTestdata(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Dog) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Size_) + if l > 0 { + n += 1 + l + sovTestdata(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTestdata(uint64(l)) + } + return n +} + +func (m *Cat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Moniker) + if l > 0 { + n += 1 + l + sovTestdata(uint64(l)) + } + if m.Lives != 0 { + n += 1 + sovTestdata(uint64(m.Lives)) + } + return n +} + +func (m *HasAnimal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Animal != nil { + l = m.Animal.Size() + n += 1 + l + sovTestdata(uint64(l)) + } + if m.X != 0 { + n += 1 + sovTestdata(uint64(m.X)) + } + return n +} + +func (m *HasHasAnimal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasAnimal != nil { + l = m.HasAnimal.Size() + n += 1 + l + sovTestdata(uint64(l)) + } + return n +} + +func (m *HasHasHasAnimal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasHasAnimal != nil { + l = m.HasHasAnimal.Size() + n += 1 + l + sovTestdata(uint64(l)) + } + return n +} + +func (m *BadMultiSignature) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovTestdata(uint64(l)) + } + } + l = len(m.MaliciousField) + if l > 0 { + n += 1 + l + sovTestdata(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovTestdata(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTestdata(x uint64) (n int) { + return sovTestdata(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Dog) 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 ErrIntOverflowTestdata + } + 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: Dog: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Dog: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + 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 ErrInvalidLengthTestdata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Size_ = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + 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 ErrInvalidLengthTestdata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Cat) 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 ErrIntOverflowTestdata + } + 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: Cat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Cat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Moniker", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + 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 ErrInvalidLengthTestdata + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Moniker = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Lives", wireType) + } + m.Lives = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Lives |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HasAnimal) 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 ErrIntOverflowTestdata + } + 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: HasAnimal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HasAnimal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Animal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTestdata + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Animal == nil { + m.Animal = &types.Any{} + } + if err := m.Animal.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HasHasAnimal) 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 ErrIntOverflowTestdata + } + 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: HasHasAnimal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HasHasAnimal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasAnimal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTestdata + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.HasAnimal == nil { + m.HasAnimal = &types.Any{} + } + if err := m.HasAnimal.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HasHasHasAnimal) 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 ErrIntOverflowTestdata + } + 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: HasHasHasAnimal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HasHasHasAnimal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasHasAnimal", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTestdata + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.HasHasAnimal == nil { + m.HasHasAnimal = &types.Any{} + } + if err := m.HasHasAnimal.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BadMultiSignature) 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 ErrIntOverflowTestdata + } + 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: BadMultiSignature: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BadMultiSignature: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTestdata + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaliciousField", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTestdata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTestdata + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTestdata + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MaliciousField = append(m.MaliciousField[:0], dAtA[iNdEx:postIndex]...) + if m.MaliciousField == nil { + m.MaliciousField = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTestdata(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTestdata + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTestdata(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, ErrIntOverflowTestdata + } + 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, ErrIntOverflowTestdata + } + 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, ErrIntOverflowTestdata + } + 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, ErrInvalidLengthTestdata + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTestdata + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTestdata + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTestdata = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTestdata = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTestdata = fmt.Errorf("proto: unexpected end of group") +) diff --git a/testutil/testdata/testdata.proto b/testutil/testdata/testdata.proto new file mode 100644 index 000000000000..585c2303c139 --- /dev/null +++ b/testutil/testdata/testdata.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +package testdata; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/testutil/testdata"; + +message Dog { + string size = 1; + string name = 2; +} + +message Cat { + string moniker = 1; + int32 lives = 2; +} + +message HasAnimal { + google.protobuf.Any animal = 1; + int64 x = 2; +} + +message HasHasAnimal { + google.protobuf.Any has_animal = 1; +} + +message HasHasHasAnimal { + google.protobuf.Any has_has_animal = 1; +} + +// bad MultiSignature with extra fields +message BadMultiSignature { + option (gogoproto.goproto_unrecognized) = true; + repeated bytes signatures = 1; + bytes malicious_field = 5; +} diff --git a/testutil/testdata/tx.go b/testutil/testdata/tx.go new file mode 100644 index 000000000000..153846083074 --- /dev/null +++ b/testutil/testdata/tx.go @@ -0,0 +1,78 @@ +package testdata + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// KeyTestPubAddr generates a new secp256k1 keypair. +func KeyTestPubAddr() (cryptotypes.PrivKey, cryptotypes.PubKey, sdk.AccAddress) { + key := secp256k1.GenPrivKey() + pub := key.PubKey() + addr := sdk.AccAddress(pub.Address()) + return key, pub, addr +} + +// NewTestFeeAmount is a test fee amount. +func NewTestFeeAmount() sdk.Coins { + return sdk.NewCoins(sdk.NewInt64Coin("atom", 150)) +} + +// NewTestGasLimit is a test fee gas limit. +func NewTestGasLimit() uint64 { + return 100000 +} + +// NewTestMsg creates a message for testing with the given signers. +func NewTestMsg(addrs ...sdk.AccAddress) *TestMsg { + var accAddresses []string + + for _, addr := range addrs { + accAddresses = append(accAddresses, addr.String()) + } + + return &TestMsg{ + Signers: accAddresses, + } +} + +var _ sdk.Msg = (*TestMsg)(nil) + +func (msg *TestMsg) Route() string { return "TestMsg" } +func (msg *TestMsg) Type() string { return "Test message" } +func (msg *TestMsg) GetSignBytes() []byte { + bz, err := json.Marshal(msg.Signers) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(bz) +} +func (msg *TestMsg) GetSigners() []sdk.AccAddress { + addrs := make([]sdk.AccAddress, len(msg.Signers)) + for i, in := range msg.Signers { + addr, err := sdk.AccAddressFromBech32(in) + if err != nil { + panic(err) + } + + addrs[i] = addr + } + + return addrs +} +func (msg *TestMsg) ValidateBasic() error { return nil } + +var _ sdk.MsgRequest = &MsgCreateDog{} + +func (msg *MsgCreateDog) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{} } +func (msg *MsgCreateDog) ValidateBasic() error { return nil } + +func NewServiceMsgCreateDog(msg *MsgCreateDog) sdk.Msg { + return sdk.ServiceMsg{ + MethodName: "/testdata.Msg/CreateDog", + Request: msg, + } +} diff --git a/testutil/testdata/tx.pb.go b/testutil/testdata/tx.pb.go new file mode 100644 index 000000000000..694a045fffae --- /dev/null +++ b/testutil/testdata/tx.pb.go @@ -0,0 +1,755 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: tx.proto + +package testdata + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +type MsgCreateDog struct { + Dog *Dog `protobuf:"bytes,1,opt,name=dog,proto3" json:"dog,omitempty"` +} + +func (m *MsgCreateDog) Reset() { *m = MsgCreateDog{} } +func (m *MsgCreateDog) String() string { return proto.CompactTextString(m) } +func (*MsgCreateDog) ProtoMessage() {} +func (*MsgCreateDog) Descriptor() ([]byte, []int) { + return fileDescriptor_0fd2153dc07d3b5c, []int{0} +} +func (m *MsgCreateDog) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateDog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateDog.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 *MsgCreateDog) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateDog.Merge(m, src) +} +func (m *MsgCreateDog) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateDog) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateDog.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateDog proto.InternalMessageInfo + +func (m *MsgCreateDog) GetDog() *Dog { + if m != nil { + return m.Dog + } + return nil +} + +type MsgCreateDogResponse struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *MsgCreateDogResponse) Reset() { *m = MsgCreateDogResponse{} } +func (m *MsgCreateDogResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateDogResponse) ProtoMessage() {} +func (*MsgCreateDogResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0fd2153dc07d3b5c, []int{1} +} +func (m *MsgCreateDogResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateDogResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateDogResponse.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 *MsgCreateDogResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateDogResponse.Merge(m, src) +} +func (m *MsgCreateDogResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateDogResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateDogResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateDogResponse proto.InternalMessageInfo + +func (m *MsgCreateDogResponse) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// TestMsg is msg type for testing protobuf message using any, as defined in +// https://github.com/cosmos/cosmos-sdk/issues/6213. +type TestMsg struct { + Signers []string `protobuf:"bytes,1,rep,name=signers,proto3" json:"signers,omitempty"` +} + +func (m *TestMsg) Reset() { *m = TestMsg{} } +func (m *TestMsg) String() string { return proto.CompactTextString(m) } +func (*TestMsg) ProtoMessage() {} +func (*TestMsg) Descriptor() ([]byte, []int) { + return fileDescriptor_0fd2153dc07d3b5c, []int{2} +} +func (m *TestMsg) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestMsg.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 *TestMsg) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestMsg.Merge(m, src) +} +func (m *TestMsg) XXX_Size() int { + return m.Size() +} +func (m *TestMsg) XXX_DiscardUnknown() { + xxx_messageInfo_TestMsg.DiscardUnknown(m) +} + +var xxx_messageInfo_TestMsg proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateDog)(nil), "testdata.MsgCreateDog") + proto.RegisterType((*MsgCreateDogResponse)(nil), "testdata.MsgCreateDogResponse") + proto.RegisterType((*TestMsg)(nil), "testdata.TestMsg") +} + +func init() { proto.RegisterFile("tx.proto", fileDescriptor_0fd2153dc07d3b5c) } + +var fileDescriptor_0fd2153dc07d3b5c = []byte{ + // 258 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x28, 0xa9, 0xd0, 0x2b, + 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x28, 0x49, 0x2d, 0x2e, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x12, + 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x0b, 0xea, 0x83, 0x58, 0x10, 0x79, 0x29, 0x3e, 0x98, 0x3c, 0x84, + 0xaf, 0xa4, 0xcf, 0xc5, 0xe3, 0x5b, 0x9c, 0xee, 0x5c, 0x94, 0x9a, 0x58, 0x92, 0xea, 0x92, 0x9f, + 0x2e, 0x24, 0xcf, 0xc5, 0x9c, 0x92, 0x9f, 0x2e, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0xc4, 0xab, + 0x07, 0x57, 0xed, 0x92, 0x9f, 0x1e, 0x04, 0x92, 0x51, 0xd2, 0xe2, 0x12, 0x41, 0xd6, 0x10, 0x94, + 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x2a, 0x24, 0xc4, 0xc5, 0x92, 0x97, 0x98, 0x9b, 0x0a, 0xd6, + 0xc9, 0x19, 0x04, 0x66, 0x2b, 0x69, 0x72, 0xb1, 0x87, 0xa4, 0x16, 0x97, 0xf8, 0x16, 0xa7, 0x0b, + 0x49, 0x70, 0xb1, 0x17, 0x67, 0xa6, 0xe7, 0xa5, 0x16, 0x15, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, + 0x06, 0xc1, 0xb8, 0x56, 0x2c, 0x1d, 0x0b, 0xe4, 0x19, 0x8c, 0xbc, 0xb8, 0x98, 0x41, 0xca, 0x9c, + 0xb9, 0x38, 0x11, 0x6e, 0x11, 0x43, 0x58, 0x8f, 0x6c, 0xa5, 0x94, 0x1c, 0x76, 0x71, 0x98, 0x53, + 0x9c, 0x3c, 0x4e, 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, 0x2f, 0x3d, 0xb3, + 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x3f, 0x39, 0xbf, 0x38, 0x37, 0xbf, 0x18, 0x4a, + 0xe9, 0x16, 0xa7, 0x64, 0xeb, 0x83, 0x4c, 0x2d, 0x2d, 0xc9, 0xcc, 0xd1, 0x87, 0x19, 0x9f, 0xc4, + 0x06, 0x0e, 0x24, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x15, 0x63, 0x9a, 0x1b, 0x60, 0x01, + 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 { + CreateDog(ctx context.Context, in *MsgCreateDog, opts ...grpc.CallOption) (*MsgCreateDogResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateDog(ctx context.Context, in *MsgCreateDog, opts ...grpc.CallOption) (*MsgCreateDogResponse, error) { + out := new(MsgCreateDogResponse) + err := c.cc.Invoke(ctx, "/testdata.Msg/CreateDog", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + CreateDog(context.Context, *MsgCreateDog) (*MsgCreateDogResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateDog(ctx context.Context, req *MsgCreateDog) (*MsgCreateDogResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateDog not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateDog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateDog) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateDog(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testdata.Msg/CreateDog", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateDog(ctx, req.(*MsgCreateDog)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "testdata.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateDog", + Handler: _Msg_CreateDog_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "tx.proto", +} + +func (m *MsgCreateDog) 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 *MsgCreateDog) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateDog) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Dog != nil { + { + size, err := m.Dog.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 *MsgCreateDogResponse) 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 *MsgCreateDogResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateDogResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTx(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestMsg) 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 *TestMsg) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestMsg) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signers) > 0 { + for iNdEx := len(m.Signers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signers[iNdEx]) + copy(dAtA[i:], m.Signers[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signers[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + 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 *MsgCreateDog) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Dog != nil { + l = m.Dog.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateDogResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *TestMsg) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Signers) > 0 { + for _, s := range m.Signers { + l = len(s) + n += 1 + l + sovTx(uint64(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 *MsgCreateDog) 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: MsgCreateDog: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateDog: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dog", 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.Dog == nil { + m.Dog = &Dog{} + } + if err := m.Dog.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 *MsgCreateDogResponse) 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: MsgCreateDogResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateDogResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.Name = 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 *TestMsg) 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: TestMsg: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestMsg: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signers", 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.Signers = append(m.Signers, 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 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/testutil/testdata/tx.proto b/testutil/testdata/tx.proto new file mode 100644 index 000000000000..eaeb9580e5e2 --- /dev/null +++ b/testutil/testdata/tx.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package testdata; + +import "gogoproto/gogo.proto"; +import "testdata.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/testutil/testdata"; + +// Msg tests the Protobuf message service as defined in +// https://github.com/cosmos/cosmos-sdk/issues/7500. +service Msg { + rpc CreateDog(MsgCreateDog) returns (MsgCreateDogResponse); +} + +message MsgCreateDog { + testdata.Dog dog = 1; +} + +message MsgCreateDogResponse { + string name = 1; +} + +// TestMsg is msg type for testing protobuf message using any, as defined in +// https://github.com/cosmos/cosmos-sdk/issues/6213. +message TestMsg { + option (gogoproto.goproto_getters) = false; + repeated string signers = 1; +} diff --git a/testutil/testdata/unknonwnproto.pb.go b/testutil/testdata/unknonwnproto.pb.go new file mode 100644 index 000000000000..eb4abf1fff10 --- /dev/null +++ b/testutil/testdata/unknonwnproto.pb.go @@ -0,0 +1,13133 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: unknonwnproto.proto + +package testdata + +import ( + encoding_binary "encoding/binary" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + tx "github.com/cosmos/cosmos-sdk/types/tx" + _ "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 + +type Customer2_City int32 + +const ( + Customer2_Laos Customer2_City = 0 + Customer2_LosAngeles Customer2_City = 1 + Customer2_PaloAlto Customer2_City = 2 + Customer2_Moscow Customer2_City = 3 + Customer2_Nairobi Customer2_City = 4 +) + +var Customer2_City_name = map[int32]string{ + 0: "Laos", + 1: "LosAngeles", + 2: "PaloAlto", + 3: "Moscow", + 4: "Nairobi", +} + +var Customer2_City_value = map[string]int32{ + "Laos": 0, + "LosAngeles": 1, + "PaloAlto": 2, + "Moscow": 3, + "Nairobi": 4, +} + +func (x Customer2_City) String() string { + return proto.EnumName(Customer2_City_name, int32(x)) +} + +func (Customer2_City) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{1, 0} +} + +type Customer1 struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + SubscriptionFee float32 `protobuf:"fixed32,3,opt,name=subscription_fee,json=subscriptionFee,proto3" json:"subscription_fee,omitempty"` + Payment string `protobuf:"bytes,7,opt,name=payment,proto3" json:"payment,omitempty"` +} + +func (m *Customer1) Reset() { *m = Customer1{} } +func (m *Customer1) String() string { return proto.CompactTextString(m) } +func (*Customer1) ProtoMessage() {} +func (*Customer1) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{0} +} +func (m *Customer1) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Customer1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Customer1.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 *Customer1) XXX_Merge(src proto.Message) { + xxx_messageInfo_Customer1.Merge(m, src) +} +func (m *Customer1) XXX_Size() int { + return m.Size() +} +func (m *Customer1) XXX_DiscardUnknown() { + xxx_messageInfo_Customer1.DiscardUnknown(m) +} + +var xxx_messageInfo_Customer1 proto.InternalMessageInfo + +func (m *Customer1) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Customer1) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Customer1) GetSubscriptionFee() float32 { + if m != nil { + return m.SubscriptionFee + } + return 0 +} + +func (m *Customer1) GetPayment() string { + if m != nil { + return m.Payment + } + return "" +} + +type Customer2 struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Industry int32 `protobuf:"varint,2,opt,name=industry,proto3" json:"industry,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Fewer float32 `protobuf:"fixed32,4,opt,name=fewer,proto3" json:"fewer,omitempty"` + Reserved int64 `protobuf:"varint,1047,opt,name=reserved,proto3" json:"reserved,omitempty"` + City Customer2_City `protobuf:"varint,6,opt,name=city,proto3,enum=testdata.Customer2_City" json:"city,omitempty"` + Miscellaneous *types.Any `protobuf:"bytes,10,opt,name=miscellaneous,proto3" json:"miscellaneous,omitempty"` +} + +func (m *Customer2) Reset() { *m = Customer2{} } +func (m *Customer2) String() string { return proto.CompactTextString(m) } +func (*Customer2) ProtoMessage() {} +func (*Customer2) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{1} +} +func (m *Customer2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Customer2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Customer2.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 *Customer2) XXX_Merge(src proto.Message) { + xxx_messageInfo_Customer2.Merge(m, src) +} +func (m *Customer2) XXX_Size() int { + return m.Size() +} +func (m *Customer2) XXX_DiscardUnknown() { + xxx_messageInfo_Customer2.DiscardUnknown(m) +} + +var xxx_messageInfo_Customer2 proto.InternalMessageInfo + +func (m *Customer2) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Customer2) GetIndustry() int32 { + if m != nil { + return m.Industry + } + return 0 +} + +func (m *Customer2) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Customer2) GetFewer() float32 { + if m != nil { + return m.Fewer + } + return 0 +} + +func (m *Customer2) GetReserved() int64 { + if m != nil { + return m.Reserved + } + return 0 +} + +func (m *Customer2) GetCity() Customer2_City { + if m != nil { + return m.City + } + return Customer2_Laos +} + +func (m *Customer2) GetMiscellaneous() *types.Any { + if m != nil { + return m.Miscellaneous + } + return nil +} + +type Nested4A struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *Nested4A) Reset() { *m = Nested4A{} } +func (m *Nested4A) String() string { return proto.CompactTextString(m) } +func (*Nested4A) ProtoMessage() {} +func (*Nested4A) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{2} +} +func (m *Nested4A) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested4A) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested4A.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 *Nested4A) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested4A.Merge(m, src) +} +func (m *Nested4A) XXX_Size() int { + return m.Size() +} +func (m *Nested4A) XXX_DiscardUnknown() { + xxx_messageInfo_Nested4A.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested4A proto.InternalMessageInfo + +func (m *Nested4A) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested4A) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type Nested3A struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + A4 []*Nested4A `protobuf:"bytes,4,rep,name=a4,proto3" json:"a4,omitempty"` + Index map[int64]*Nested4A `protobuf:"bytes,5,rep,name=index,proto3" json:"index,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *Nested3A) Reset() { *m = Nested3A{} } +func (m *Nested3A) String() string { return proto.CompactTextString(m) } +func (*Nested3A) ProtoMessage() {} +func (*Nested3A) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{3} +} +func (m *Nested3A) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested3A) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested3A.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 *Nested3A) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested3A.Merge(m, src) +} +func (m *Nested3A) XXX_Size() int { + return m.Size() +} +func (m *Nested3A) XXX_DiscardUnknown() { + xxx_messageInfo_Nested3A.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested3A proto.InternalMessageInfo + +func (m *Nested3A) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested3A) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Nested3A) GetA4() []*Nested4A { + if m != nil { + return m.A4 + } + return nil +} + +func (m *Nested3A) GetIndex() map[int64]*Nested4A { + if m != nil { + return m.Index + } + return nil +} + +type Nested2A struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Nested *Nested3A `protobuf:"bytes,3,opt,name=nested,proto3" json:"nested,omitempty"` +} + +func (m *Nested2A) Reset() { *m = Nested2A{} } +func (m *Nested2A) String() string { return proto.CompactTextString(m) } +func (*Nested2A) ProtoMessage() {} +func (*Nested2A) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{4} +} +func (m *Nested2A) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested2A) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested2A.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 *Nested2A) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested2A.Merge(m, src) +} +func (m *Nested2A) XXX_Size() int { + return m.Size() +} +func (m *Nested2A) XXX_DiscardUnknown() { + xxx_messageInfo_Nested2A.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested2A proto.InternalMessageInfo + +func (m *Nested2A) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested2A) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Nested2A) GetNested() *Nested3A { + if m != nil { + return m.Nested + } + return nil +} + +type Nested1A struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Nested *Nested2A `protobuf:"bytes,2,opt,name=nested,proto3" json:"nested,omitempty"` +} + +func (m *Nested1A) Reset() { *m = Nested1A{} } +func (m *Nested1A) String() string { return proto.CompactTextString(m) } +func (*Nested1A) ProtoMessage() {} +func (*Nested1A) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{5} +} +func (m *Nested1A) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested1A) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested1A.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 *Nested1A) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested1A.Merge(m, src) +} +func (m *Nested1A) XXX_Size() int { + return m.Size() +} +func (m *Nested1A) XXX_DiscardUnknown() { + xxx_messageInfo_Nested1A.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested1A proto.InternalMessageInfo + +func (m *Nested1A) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested1A) GetNested() *Nested2A { + if m != nil { + return m.Nested + } + return nil +} + +type Nested4B struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *Nested4B) Reset() { *m = Nested4B{} } +func (m *Nested4B) String() string { return proto.CompactTextString(m) } +func (*Nested4B) ProtoMessage() {} +func (*Nested4B) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{6} +} +func (m *Nested4B) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested4B) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested4B.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 *Nested4B) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested4B.Merge(m, src) +} +func (m *Nested4B) XXX_Size() int { + return m.Size() +} +func (m *Nested4B) XXX_DiscardUnknown() { + xxx_messageInfo_Nested4B.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested4B proto.InternalMessageInfo + +func (m *Nested4B) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested4B) GetAge() int32 { + if m != nil { + return m.Age + } + return 0 +} + +func (m *Nested4B) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type Nested3B struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + B4 []*Nested4B `protobuf:"bytes,4,rep,name=b4,proto3" json:"b4,omitempty"` +} + +func (m *Nested3B) Reset() { *m = Nested3B{} } +func (m *Nested3B) String() string { return proto.CompactTextString(m) } +func (*Nested3B) ProtoMessage() {} +func (*Nested3B) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{7} +} +func (m *Nested3B) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested3B) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested3B.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 *Nested3B) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested3B.Merge(m, src) +} +func (m *Nested3B) XXX_Size() int { + return m.Size() +} +func (m *Nested3B) XXX_DiscardUnknown() { + xxx_messageInfo_Nested3B.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested3B proto.InternalMessageInfo + +func (m *Nested3B) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested3B) GetAge() int32 { + if m != nil { + return m.Age + } + return 0 +} + +func (m *Nested3B) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Nested3B) GetB4() []*Nested4B { + if m != nil { + return m.B4 + } + return nil +} + +type Nested2B struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Fee float64 `protobuf:"fixed64,2,opt,name=fee,proto3" json:"fee,omitempty"` + Nested *Nested3B `protobuf:"bytes,3,opt,name=nested,proto3" json:"nested,omitempty"` + Route string `protobuf:"bytes,4,opt,name=route,proto3" json:"route,omitempty"` +} + +func (m *Nested2B) Reset() { *m = Nested2B{} } +func (m *Nested2B) String() string { return proto.CompactTextString(m) } +func (*Nested2B) ProtoMessage() {} +func (*Nested2B) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{8} +} +func (m *Nested2B) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested2B) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested2B.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 *Nested2B) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested2B.Merge(m, src) +} +func (m *Nested2B) XXX_Size() int { + return m.Size() +} +func (m *Nested2B) XXX_DiscardUnknown() { + xxx_messageInfo_Nested2B.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested2B proto.InternalMessageInfo + +func (m *Nested2B) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested2B) GetFee() float64 { + if m != nil { + return m.Fee + } + return 0 +} + +func (m *Nested2B) GetNested() *Nested3B { + if m != nil { + return m.Nested + } + return nil +} + +func (m *Nested2B) GetRoute() string { + if m != nil { + return m.Route + } + return "" +} + +type Nested1B struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Nested *Nested2B `protobuf:"bytes,2,opt,name=nested,proto3" json:"nested,omitempty"` + Age int32 `protobuf:"varint,3,opt,name=age,proto3" json:"age,omitempty"` +} + +func (m *Nested1B) Reset() { *m = Nested1B{} } +func (m *Nested1B) String() string { return proto.CompactTextString(m) } +func (*Nested1B) ProtoMessage() {} +func (*Nested1B) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{9} +} +func (m *Nested1B) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nested1B) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nested1B.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 *Nested1B) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nested1B.Merge(m, src) +} +func (m *Nested1B) XXX_Size() int { + return m.Size() +} +func (m *Nested1B) XXX_DiscardUnknown() { + xxx_messageInfo_Nested1B.DiscardUnknown(m) +} + +var xxx_messageInfo_Nested1B proto.InternalMessageInfo + +func (m *Nested1B) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Nested1B) GetNested() *Nested2B { + if m != nil { + return m.Nested + } + return nil +} + +func (m *Nested1B) GetAge() int32 { + if m != nil { + return m.Age + } + return 0 +} + +type Customer3 struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Sf float32 `protobuf:"fixed32,3,opt,name=sf,proto3" json:"sf,omitempty"` + Surcharge float32 `protobuf:"fixed32,4,opt,name=surcharge,proto3" json:"surcharge,omitempty"` + Destination string `protobuf:"bytes,5,opt,name=destination,proto3" json:"destination,omitempty"` + // Types that are valid to be assigned to Payment: + // *Customer3_CreditCardNo + // *Customer3_ChequeNo + Payment isCustomer3_Payment `protobuf_oneof:"payment"` + Original *Customer1 `protobuf:"bytes,9,opt,name=original,proto3" json:"original,omitempty"` +} + +func (m *Customer3) Reset() { *m = Customer3{} } +func (m *Customer3) String() string { return proto.CompactTextString(m) } +func (*Customer3) ProtoMessage() {} +func (*Customer3) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{10} +} +func (m *Customer3) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Customer3) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Customer3.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 *Customer3) XXX_Merge(src proto.Message) { + xxx_messageInfo_Customer3.Merge(m, src) +} +func (m *Customer3) XXX_Size() int { + return m.Size() +} +func (m *Customer3) XXX_DiscardUnknown() { + xxx_messageInfo_Customer3.DiscardUnknown(m) +} + +var xxx_messageInfo_Customer3 proto.InternalMessageInfo + +type isCustomer3_Payment interface { + isCustomer3_Payment() + MarshalTo([]byte) (int, error) + Size() int +} + +type Customer3_CreditCardNo struct { + CreditCardNo string `protobuf:"bytes,7,opt,name=credit_card_no,json=creditCardNo,proto3,oneof" json:"credit_card_no,omitempty"` +} +type Customer3_ChequeNo struct { + ChequeNo string `protobuf:"bytes,8,opt,name=cheque_no,json=chequeNo,proto3,oneof" json:"cheque_no,omitempty"` +} + +func (*Customer3_CreditCardNo) isCustomer3_Payment() {} +func (*Customer3_ChequeNo) isCustomer3_Payment() {} + +func (m *Customer3) GetPayment() isCustomer3_Payment { + if m != nil { + return m.Payment + } + return nil +} + +func (m *Customer3) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *Customer3) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Customer3) GetSf() float32 { + if m != nil { + return m.Sf + } + return 0 +} + +func (m *Customer3) GetSurcharge() float32 { + if m != nil { + return m.Surcharge + } + return 0 +} + +func (m *Customer3) GetDestination() string { + if m != nil { + return m.Destination + } + return "" +} + +func (m *Customer3) GetCreditCardNo() string { + if x, ok := m.GetPayment().(*Customer3_CreditCardNo); ok { + return x.CreditCardNo + } + return "" +} + +func (m *Customer3) GetChequeNo() string { + if x, ok := m.GetPayment().(*Customer3_ChequeNo); ok { + return x.ChequeNo + } + return "" +} + +func (m *Customer3) GetOriginal() *Customer1 { + if m != nil { + return m.Original + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Customer3) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Customer3_CreditCardNo)(nil), + (*Customer3_ChequeNo)(nil), + } +} + +type TestVersion1 struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion1 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion1 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion1 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []TestVersion1 `protobuf:"bytes,5,rep,name=d,proto3" json:"d"` + // Types that are valid to be assigned to Sum: + // *TestVersion1_E + // *TestVersion1_F + Sum isTestVersion1_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` +} + +func (m *TestVersion1) Reset() { *m = TestVersion1{} } +func (m *TestVersion1) String() string { return proto.CompactTextString(m) } +func (*TestVersion1) ProtoMessage() {} +func (*TestVersion1) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{11} +} +func (m *TestVersion1) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion1.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 *TestVersion1) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion1.Merge(m, src) +} +func (m *TestVersion1) XXX_Size() int { + return m.Size() +} +func (m *TestVersion1) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion1.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion1 proto.InternalMessageInfo + +type isTestVersion1_Sum interface { + isTestVersion1_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion1_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} +type TestVersion1_F struct { + F *TestVersion1 `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersion1_E) isTestVersion1_Sum() {} +func (*TestVersion1_F) isTestVersion1_Sum() {} + +func (m *TestVersion1) GetSum() isTestVersion1_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion1) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion1) GetA() *TestVersion1 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion1) GetB() *TestVersion1 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion1) GetC() []*TestVersion1 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion1) GetD() []TestVersion1 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion1) GetE() int32 { + if x, ok := m.GetSum().(*TestVersion1_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersion1) GetF() *TestVersion1 { + if x, ok := m.GetSum().(*TestVersion1_F); ok { + return x.F + } + return nil +} + +func (m *TestVersion1) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion1) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion1) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion1_E)(nil), + (*TestVersion1_F)(nil), + } +} + +type TestVersion2 struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion2 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion2 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion2 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []*TestVersion2 `protobuf:"bytes,5,rep,name=d,proto3" json:"d,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersion2_E + // *TestVersion2_F + Sum isTestVersion2_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` + NewField uint64 `protobuf:"varint,25,opt,name=new_field,json=newField,proto3" json:"new_field,omitempty"` +} + +func (m *TestVersion2) Reset() { *m = TestVersion2{} } +func (m *TestVersion2) String() string { return proto.CompactTextString(m) } +func (*TestVersion2) ProtoMessage() {} +func (*TestVersion2) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{12} +} +func (m *TestVersion2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion2.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 *TestVersion2) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion2.Merge(m, src) +} +func (m *TestVersion2) XXX_Size() int { + return m.Size() +} +func (m *TestVersion2) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion2.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion2 proto.InternalMessageInfo + +type isTestVersion2_Sum interface { + isTestVersion2_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion2_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} +type TestVersion2_F struct { + F *TestVersion2 `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersion2_E) isTestVersion2_Sum() {} +func (*TestVersion2_F) isTestVersion2_Sum() {} + +func (m *TestVersion2) GetSum() isTestVersion2_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion2) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion2) GetA() *TestVersion2 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion2) GetB() *TestVersion2 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion2) GetC() []*TestVersion2 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion2) GetD() []*TestVersion2 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion2) GetE() int32 { + if x, ok := m.GetSum().(*TestVersion2_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersion2) GetF() *TestVersion2 { + if x, ok := m.GetSum().(*TestVersion2_F); ok { + return x.F + } + return nil +} + +func (m *TestVersion2) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion2) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +func (m *TestVersion2) GetNewField() uint64 { + if m != nil { + return m.NewField + } + return 0 +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion2) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion2_E)(nil), + (*TestVersion2_F)(nil), + } +} + +type TestVersion3 struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion3 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion3 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion3 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []*TestVersion3 `protobuf:"bytes,5,rep,name=d,proto3" json:"d,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersion3_E + // *TestVersion3_F + Sum isTestVersion3_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` + NonCriticalField string `protobuf:"bytes,1031,opt,name=non_critical_field,json=nonCriticalField,proto3" json:"non_critical_field,omitempty"` +} + +func (m *TestVersion3) Reset() { *m = TestVersion3{} } +func (m *TestVersion3) String() string { return proto.CompactTextString(m) } +func (*TestVersion3) ProtoMessage() {} +func (*TestVersion3) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{13} +} +func (m *TestVersion3) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3.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 *TestVersion3) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3.Merge(m, src) +} +func (m *TestVersion3) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3 proto.InternalMessageInfo + +type isTestVersion3_Sum interface { + isTestVersion3_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion3_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} +type TestVersion3_F struct { + F *TestVersion3 `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersion3_E) isTestVersion3_Sum() {} +func (*TestVersion3_F) isTestVersion3_Sum() {} + +func (m *TestVersion3) GetSum() isTestVersion3_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion3) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion3) GetA() *TestVersion3 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion3) GetB() *TestVersion3 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion3) GetC() []*TestVersion3 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion3) GetD() []*TestVersion3 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion3) GetE() int32 { + if x, ok := m.GetSum().(*TestVersion3_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersion3) GetF() *TestVersion3 { + if x, ok := m.GetSum().(*TestVersion3_F); ok { + return x.F + } + return nil +} + +func (m *TestVersion3) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion3) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +func (m *TestVersion3) GetNonCriticalField() string { + if m != nil { + return m.NonCriticalField + } + return "" +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion3) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion3_E)(nil), + (*TestVersion3_F)(nil), + } +} + +type TestVersion3LoneOneOfValue struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion3 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion3 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion3 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []*TestVersion3 `protobuf:"bytes,5,rep,name=d,proto3" json:"d,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersion3LoneOneOfValue_E + Sum isTestVersion3LoneOneOfValue_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` + NonCriticalField string `protobuf:"bytes,1031,opt,name=non_critical_field,json=nonCriticalField,proto3" json:"non_critical_field,omitempty"` +} + +func (m *TestVersion3LoneOneOfValue) Reset() { *m = TestVersion3LoneOneOfValue{} } +func (m *TestVersion3LoneOneOfValue) String() string { return proto.CompactTextString(m) } +func (*TestVersion3LoneOneOfValue) ProtoMessage() {} +func (*TestVersion3LoneOneOfValue) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{14} +} +func (m *TestVersion3LoneOneOfValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneOneOfValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneOneOfValue.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 *TestVersion3LoneOneOfValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneOneOfValue.Merge(m, src) +} +func (m *TestVersion3LoneOneOfValue) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneOneOfValue) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneOneOfValue.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneOneOfValue proto.InternalMessageInfo + +type isTestVersion3LoneOneOfValue_Sum interface { + isTestVersion3LoneOneOfValue_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion3LoneOneOfValue_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} + +func (*TestVersion3LoneOneOfValue_E) isTestVersion3LoneOneOfValue_Sum() {} + +func (m *TestVersion3LoneOneOfValue) GetSum() isTestVersion3LoneOneOfValue_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion3LoneOneOfValue) GetA() *TestVersion3 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetB() *TestVersion3 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetC() []*TestVersion3 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetD() []*TestVersion3 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetE() int32 { + if x, ok := m.GetSum().(*TestVersion3LoneOneOfValue_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersion3LoneOneOfValue) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +func (m *TestVersion3LoneOneOfValue) GetNonCriticalField() string { + if m != nil { + return m.NonCriticalField + } + return "" +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion3LoneOneOfValue) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion3LoneOneOfValue_E)(nil), + } +} + +type TestVersion3LoneNesting struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion3 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion3 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion3 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []*TestVersion3 `protobuf:"bytes,5,rep,name=d,proto3" json:"d,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersion3LoneNesting_F + Sum isTestVersion3LoneNesting_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` + NonCriticalField string `protobuf:"bytes,1031,opt,name=non_critical_field,json=nonCriticalField,proto3" json:"non_critical_field,omitempty"` + Inner1 *TestVersion3LoneNesting_Inner1 `protobuf:"bytes,14,opt,name=inner1,proto3" json:"inner1,omitempty"` + Inner2 *TestVersion3LoneNesting_Inner2 `protobuf:"bytes,15,opt,name=inner2,proto3" json:"inner2,omitempty"` +} + +func (m *TestVersion3LoneNesting) Reset() { *m = TestVersion3LoneNesting{} } +func (m *TestVersion3LoneNesting) String() string { return proto.CompactTextString(m) } +func (*TestVersion3LoneNesting) ProtoMessage() {} +func (*TestVersion3LoneNesting) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{15} +} +func (m *TestVersion3LoneNesting) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneNesting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneNesting.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 *TestVersion3LoneNesting) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneNesting.Merge(m, src) +} +func (m *TestVersion3LoneNesting) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneNesting) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneNesting.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneNesting proto.InternalMessageInfo + +type isTestVersion3LoneNesting_Sum interface { + isTestVersion3LoneNesting_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion3LoneNesting_F struct { + F *TestVersion3LoneNesting `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersion3LoneNesting_F) isTestVersion3LoneNesting_Sum() {} + +func (m *TestVersion3LoneNesting) GetSum() isTestVersion3LoneNesting_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion3LoneNesting) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion3LoneNesting) GetA() *TestVersion3 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion3LoneNesting) GetB() *TestVersion3 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion3LoneNesting) GetC() []*TestVersion3 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion3LoneNesting) GetD() []*TestVersion3 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion3LoneNesting) GetF() *TestVersion3LoneNesting { + if x, ok := m.GetSum().(*TestVersion3LoneNesting_F); ok { + return x.F + } + return nil +} + +func (m *TestVersion3LoneNesting) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion3LoneNesting) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +func (m *TestVersion3LoneNesting) GetNonCriticalField() string { + if m != nil { + return m.NonCriticalField + } + return "" +} + +func (m *TestVersion3LoneNesting) GetInner1() *TestVersion3LoneNesting_Inner1 { + if m != nil { + return m.Inner1 + } + return nil +} + +func (m *TestVersion3LoneNesting) GetInner2() *TestVersion3LoneNesting_Inner2 { + if m != nil { + return m.Inner2 + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion3LoneNesting) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion3LoneNesting_F)(nil), + } +} + +type TestVersion3LoneNesting_Inner1 struct { + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Inner *TestVersion3LoneNesting_Inner1_InnerInner `protobuf:"bytes,3,opt,name=inner,proto3" json:"inner,omitempty"` +} + +func (m *TestVersion3LoneNesting_Inner1) Reset() { *m = TestVersion3LoneNesting_Inner1{} } +func (m *TestVersion3LoneNesting_Inner1) String() string { return proto.CompactTextString(m) } +func (*TestVersion3LoneNesting_Inner1) ProtoMessage() {} +func (*TestVersion3LoneNesting_Inner1) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{15, 0} +} +func (m *TestVersion3LoneNesting_Inner1) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneNesting_Inner1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneNesting_Inner1.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 *TestVersion3LoneNesting_Inner1) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneNesting_Inner1.Merge(m, src) +} +func (m *TestVersion3LoneNesting_Inner1) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneNesting_Inner1) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneNesting_Inner1.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneNesting_Inner1 proto.InternalMessageInfo + +func (m *TestVersion3LoneNesting_Inner1) GetId() int64 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *TestVersion3LoneNesting_Inner1) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *TestVersion3LoneNesting_Inner1) GetInner() *TestVersion3LoneNesting_Inner1_InnerInner { + if m != nil { + return m.Inner + } + return nil +} + +type TestVersion3LoneNesting_Inner1_InnerInner struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + City string `protobuf:"bytes,2,opt,name=city,proto3" json:"city,omitempty"` +} + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) Reset() { + *m = TestVersion3LoneNesting_Inner1_InnerInner{} +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) String() string { + return proto.CompactTextString(m) +} +func (*TestVersion3LoneNesting_Inner1_InnerInner) ProtoMessage() {} +func (*TestVersion3LoneNesting_Inner1_InnerInner) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{15, 0, 0} +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneNesting_Inner1_InnerInner.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 *TestVersion3LoneNesting_Inner1_InnerInner) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneNesting_Inner1_InnerInner.Merge(m, src) +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneNesting_Inner1_InnerInner.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneNesting_Inner1_InnerInner proto.InternalMessageInfo + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) GetCity() string { + if m != nil { + return m.City + } + return "" +} + +type TestVersion3LoneNesting_Inner2 struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"` + Inner *TestVersion3LoneNesting_Inner2_InnerInner `protobuf:"bytes,3,opt,name=inner,proto3" json:"inner,omitempty"` +} + +func (m *TestVersion3LoneNesting_Inner2) Reset() { *m = TestVersion3LoneNesting_Inner2{} } +func (m *TestVersion3LoneNesting_Inner2) String() string { return proto.CompactTextString(m) } +func (*TestVersion3LoneNesting_Inner2) ProtoMessage() {} +func (*TestVersion3LoneNesting_Inner2) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{15, 1} +} +func (m *TestVersion3LoneNesting_Inner2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneNesting_Inner2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneNesting_Inner2.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 *TestVersion3LoneNesting_Inner2) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneNesting_Inner2.Merge(m, src) +} +func (m *TestVersion3LoneNesting_Inner2) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneNesting_Inner2) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneNesting_Inner2.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneNesting_Inner2 proto.InternalMessageInfo + +func (m *TestVersion3LoneNesting_Inner2) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *TestVersion3LoneNesting_Inner2) GetCountry() string { + if m != nil { + return m.Country + } + return "" +} + +func (m *TestVersion3LoneNesting_Inner2) GetInner() *TestVersion3LoneNesting_Inner2_InnerInner { + if m != nil { + return m.Inner + } + return nil +} + +type TestVersion3LoneNesting_Inner2_InnerInner struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + City string `protobuf:"bytes,2,opt,name=city,proto3" json:"city,omitempty"` +} + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) Reset() { + *m = TestVersion3LoneNesting_Inner2_InnerInner{} +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) String() string { + return proto.CompactTextString(m) +} +func (*TestVersion3LoneNesting_Inner2_InnerInner) ProtoMessage() {} +func (*TestVersion3LoneNesting_Inner2_InnerInner) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{15, 1, 0} +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion3LoneNesting_Inner2_InnerInner.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 *TestVersion3LoneNesting_Inner2_InnerInner) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion3LoneNesting_Inner2_InnerInner.Merge(m, src) +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) XXX_Size() int { + return m.Size() +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion3LoneNesting_Inner2_InnerInner.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion3LoneNesting_Inner2_InnerInner proto.InternalMessageInfo + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) GetCity() string { + if m != nil { + return m.City + } + return "" +} + +type TestVersion4LoneNesting struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion3 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + B *TestVersion3 `protobuf:"bytes,3,opt,name=b,proto3" json:"b,omitempty"` + C []*TestVersion3 `protobuf:"bytes,4,rep,name=c,proto3" json:"c,omitempty"` + D []*TestVersion3 `protobuf:"bytes,5,rep,name=d,proto3" json:"d,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersion4LoneNesting_F + Sum isTestVersion4LoneNesting_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + *Customer1 `protobuf:"bytes,12,opt,name=k,proto3,embedded=k" json:"k,omitempty"` + NonCriticalField string `protobuf:"bytes,1031,opt,name=non_critical_field,json=nonCriticalField,proto3" json:"non_critical_field,omitempty"` + Inner1 *TestVersion4LoneNesting_Inner1 `protobuf:"bytes,14,opt,name=inner1,proto3" json:"inner1,omitempty"` + Inner2 *TestVersion4LoneNesting_Inner2 `protobuf:"bytes,15,opt,name=inner2,proto3" json:"inner2,omitempty"` +} + +func (m *TestVersion4LoneNesting) Reset() { *m = TestVersion4LoneNesting{} } +func (m *TestVersion4LoneNesting) String() string { return proto.CompactTextString(m) } +func (*TestVersion4LoneNesting) ProtoMessage() {} +func (*TestVersion4LoneNesting) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{16} +} +func (m *TestVersion4LoneNesting) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion4LoneNesting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion4LoneNesting.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 *TestVersion4LoneNesting) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion4LoneNesting.Merge(m, src) +} +func (m *TestVersion4LoneNesting) XXX_Size() int { + return m.Size() +} +func (m *TestVersion4LoneNesting) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion4LoneNesting.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion4LoneNesting proto.InternalMessageInfo + +type isTestVersion4LoneNesting_Sum interface { + isTestVersion4LoneNesting_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersion4LoneNesting_F struct { + F *TestVersion3LoneNesting `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersion4LoneNesting_F) isTestVersion4LoneNesting_Sum() {} + +func (m *TestVersion4LoneNesting) GetSum() isTestVersion4LoneNesting_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersion4LoneNesting) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersion4LoneNesting) GetA() *TestVersion3 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersion4LoneNesting) GetB() *TestVersion3 { + if m != nil { + return m.B + } + return nil +} + +func (m *TestVersion4LoneNesting) GetC() []*TestVersion3 { + if m != nil { + return m.C + } + return nil +} + +func (m *TestVersion4LoneNesting) GetD() []*TestVersion3 { + if m != nil { + return m.D + } + return nil +} + +func (m *TestVersion4LoneNesting) GetF() *TestVersion3LoneNesting { + if x, ok := m.GetSum().(*TestVersion4LoneNesting_F); ok { + return x.F + } + return nil +} + +func (m *TestVersion4LoneNesting) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersion4LoneNesting) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +func (m *TestVersion4LoneNesting) GetNonCriticalField() string { + if m != nil { + return m.NonCriticalField + } + return "" +} + +func (m *TestVersion4LoneNesting) GetInner1() *TestVersion4LoneNesting_Inner1 { + if m != nil { + return m.Inner1 + } + return nil +} + +func (m *TestVersion4LoneNesting) GetInner2() *TestVersion4LoneNesting_Inner2 { + if m != nil { + return m.Inner2 + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersion4LoneNesting) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersion4LoneNesting_F)(nil), + } +} + +type TestVersion4LoneNesting_Inner1 struct { + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Inner *TestVersion4LoneNesting_Inner1_InnerInner `protobuf:"bytes,3,opt,name=inner,proto3" json:"inner,omitempty"` +} + +func (m *TestVersion4LoneNesting_Inner1) Reset() { *m = TestVersion4LoneNesting_Inner1{} } +func (m *TestVersion4LoneNesting_Inner1) String() string { return proto.CompactTextString(m) } +func (*TestVersion4LoneNesting_Inner1) ProtoMessage() {} +func (*TestVersion4LoneNesting_Inner1) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{16, 0} +} +func (m *TestVersion4LoneNesting_Inner1) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion4LoneNesting_Inner1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion4LoneNesting_Inner1.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 *TestVersion4LoneNesting_Inner1) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion4LoneNesting_Inner1.Merge(m, src) +} +func (m *TestVersion4LoneNesting_Inner1) XXX_Size() int { + return m.Size() +} +func (m *TestVersion4LoneNesting_Inner1) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion4LoneNesting_Inner1.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion4LoneNesting_Inner1 proto.InternalMessageInfo + +func (m *TestVersion4LoneNesting_Inner1) GetId() int64 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *TestVersion4LoneNesting_Inner1) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *TestVersion4LoneNesting_Inner1) GetInner() *TestVersion4LoneNesting_Inner1_InnerInner { + if m != nil { + return m.Inner + } + return nil +} + +type TestVersion4LoneNesting_Inner1_InnerInner struct { + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + City string `protobuf:"bytes,2,opt,name=city,proto3" json:"city,omitempty"` +} + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) Reset() { + *m = TestVersion4LoneNesting_Inner1_InnerInner{} +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) String() string { + return proto.CompactTextString(m) +} +func (*TestVersion4LoneNesting_Inner1_InnerInner) ProtoMessage() {} +func (*TestVersion4LoneNesting_Inner1_InnerInner) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{16, 0, 0} +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion4LoneNesting_Inner1_InnerInner.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 *TestVersion4LoneNesting_Inner1_InnerInner) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion4LoneNesting_Inner1_InnerInner.Merge(m, src) +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) XXX_Size() int { + return m.Size() +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion4LoneNesting_Inner1_InnerInner.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion4LoneNesting_Inner1_InnerInner proto.InternalMessageInfo + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) GetId() int64 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) GetCity() string { + if m != nil { + return m.City + } + return "" +} + +type TestVersion4LoneNesting_Inner2 struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"` + Inner *TestVersion4LoneNesting_Inner2_InnerInner `protobuf:"bytes,3,opt,name=inner,proto3" json:"inner,omitempty"` +} + +func (m *TestVersion4LoneNesting_Inner2) Reset() { *m = TestVersion4LoneNesting_Inner2{} } +func (m *TestVersion4LoneNesting_Inner2) String() string { return proto.CompactTextString(m) } +func (*TestVersion4LoneNesting_Inner2) ProtoMessage() {} +func (*TestVersion4LoneNesting_Inner2) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{16, 1} +} +func (m *TestVersion4LoneNesting_Inner2) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion4LoneNesting_Inner2) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion4LoneNesting_Inner2.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 *TestVersion4LoneNesting_Inner2) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion4LoneNesting_Inner2.Merge(m, src) +} +func (m *TestVersion4LoneNesting_Inner2) XXX_Size() int { + return m.Size() +} +func (m *TestVersion4LoneNesting_Inner2) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion4LoneNesting_Inner2.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion4LoneNesting_Inner2 proto.InternalMessageInfo + +func (m *TestVersion4LoneNesting_Inner2) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *TestVersion4LoneNesting_Inner2) GetCountry() string { + if m != nil { + return m.Country + } + return "" +} + +func (m *TestVersion4LoneNesting_Inner2) GetInner() *TestVersion4LoneNesting_Inner2_InnerInner { + if m != nil { + return m.Inner + } + return nil +} + +type TestVersion4LoneNesting_Inner2_InnerInner struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) Reset() { + *m = TestVersion4LoneNesting_Inner2_InnerInner{} +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) String() string { + return proto.CompactTextString(m) +} +func (*TestVersion4LoneNesting_Inner2_InnerInner) ProtoMessage() {} +func (*TestVersion4LoneNesting_Inner2_InnerInner) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{16, 1, 0} +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersion4LoneNesting_Inner2_InnerInner.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 *TestVersion4LoneNesting_Inner2_InnerInner) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersion4LoneNesting_Inner2_InnerInner.Merge(m, src) +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) XXX_Size() int { + return m.Size() +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersion4LoneNesting_Inner2_InnerInner.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersion4LoneNesting_Inner2_InnerInner proto.InternalMessageInfo + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) GetValue() int64 { + if m != nil { + return m.Value + } + return 0 +} + +type TestVersionFD1 struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion1 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersionFD1_E + // *TestVersionFD1_F + Sum isTestVersionFD1_Sum `protobuf_oneof:"sum"` + G *types.Any `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` +} + +func (m *TestVersionFD1) Reset() { *m = TestVersionFD1{} } +func (m *TestVersionFD1) String() string { return proto.CompactTextString(m) } +func (*TestVersionFD1) ProtoMessage() {} +func (*TestVersionFD1) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{17} +} +func (m *TestVersionFD1) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersionFD1) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersionFD1.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 *TestVersionFD1) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersionFD1.Merge(m, src) +} +func (m *TestVersionFD1) XXX_Size() int { + return m.Size() +} +func (m *TestVersionFD1) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersionFD1.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersionFD1 proto.InternalMessageInfo + +type isTestVersionFD1_Sum interface { + isTestVersionFD1_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersionFD1_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} +type TestVersionFD1_F struct { + F *TestVersion1 `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersionFD1_E) isTestVersionFD1_Sum() {} +func (*TestVersionFD1_F) isTestVersionFD1_Sum() {} + +func (m *TestVersionFD1) GetSum() isTestVersionFD1_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersionFD1) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersionFD1) GetA() *TestVersion1 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersionFD1) GetE() int32 { + if x, ok := m.GetSum().(*TestVersionFD1_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersionFD1) GetF() *TestVersion1 { + if x, ok := m.GetSum().(*TestVersionFD1_F); ok { + return x.F + } + return nil +} + +func (m *TestVersionFD1) GetG() *types.Any { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersionFD1) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersionFD1) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersionFD1_E)(nil), + (*TestVersionFD1_F)(nil), + } +} + +type TestVersionFD1WithExtraAny struct { + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + A *TestVersion1 `protobuf:"bytes,2,opt,name=a,proto3" json:"a,omitempty"` + // Types that are valid to be assigned to Sum: + // *TestVersionFD1WithExtraAny_E + // *TestVersionFD1WithExtraAny_F + Sum isTestVersionFD1WithExtraAny_Sum `protobuf_oneof:"sum"` + G *AnyWithExtra `protobuf:"bytes,8,opt,name=g,proto3" json:"g,omitempty"` + H []*TestVersion1 `protobuf:"bytes,9,rep,name=h,proto3" json:"h,omitempty"` +} + +func (m *TestVersionFD1WithExtraAny) Reset() { *m = TestVersionFD1WithExtraAny{} } +func (m *TestVersionFD1WithExtraAny) String() string { return proto.CompactTextString(m) } +func (*TestVersionFD1WithExtraAny) ProtoMessage() {} +func (*TestVersionFD1WithExtraAny) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{18} +} +func (m *TestVersionFD1WithExtraAny) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestVersionFD1WithExtraAny) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestVersionFD1WithExtraAny.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 *TestVersionFD1WithExtraAny) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestVersionFD1WithExtraAny.Merge(m, src) +} +func (m *TestVersionFD1WithExtraAny) XXX_Size() int { + return m.Size() +} +func (m *TestVersionFD1WithExtraAny) XXX_DiscardUnknown() { + xxx_messageInfo_TestVersionFD1WithExtraAny.DiscardUnknown(m) +} + +var xxx_messageInfo_TestVersionFD1WithExtraAny proto.InternalMessageInfo + +type isTestVersionFD1WithExtraAny_Sum interface { + isTestVersionFD1WithExtraAny_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type TestVersionFD1WithExtraAny_E struct { + E int32 `protobuf:"varint,6,opt,name=e,proto3,oneof" json:"e,omitempty"` +} +type TestVersionFD1WithExtraAny_F struct { + F *TestVersion1 `protobuf:"bytes,7,opt,name=f,proto3,oneof" json:"f,omitempty"` +} + +func (*TestVersionFD1WithExtraAny_E) isTestVersionFD1WithExtraAny_Sum() {} +func (*TestVersionFD1WithExtraAny_F) isTestVersionFD1WithExtraAny_Sum() {} + +func (m *TestVersionFD1WithExtraAny) GetSum() isTestVersionFD1WithExtraAny_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *TestVersionFD1WithExtraAny) GetX() int64 { + if m != nil { + return m.X + } + return 0 +} + +func (m *TestVersionFD1WithExtraAny) GetA() *TestVersion1 { + if m != nil { + return m.A + } + return nil +} + +func (m *TestVersionFD1WithExtraAny) GetE() int32 { + if x, ok := m.GetSum().(*TestVersionFD1WithExtraAny_E); ok { + return x.E + } + return 0 +} + +func (m *TestVersionFD1WithExtraAny) GetF() *TestVersion1 { + if x, ok := m.GetSum().(*TestVersionFD1WithExtraAny_F); ok { + return x.F + } + return nil +} + +func (m *TestVersionFD1WithExtraAny) GetG() *AnyWithExtra { + if m != nil { + return m.G + } + return nil +} + +func (m *TestVersionFD1WithExtraAny) GetH() []*TestVersion1 { + if m != nil { + return m.H + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TestVersionFD1WithExtraAny) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*TestVersionFD1WithExtraAny_E)(nil), + (*TestVersionFD1WithExtraAny_F)(nil), + } +} + +type AnyWithExtra struct { + *types.Any `protobuf:"bytes,1,opt,name=a,proto3,embedded=a" json:"a,omitempty"` + B int64 `protobuf:"varint,3,opt,name=b,proto3" json:"b,omitempty"` + C int64 `protobuf:"varint,4,opt,name=c,proto3" json:"c,omitempty"` +} + +func (m *AnyWithExtra) Reset() { *m = AnyWithExtra{} } +func (m *AnyWithExtra) String() string { return proto.CompactTextString(m) } +func (*AnyWithExtra) ProtoMessage() {} +func (*AnyWithExtra) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{19} +} +func (m *AnyWithExtra) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AnyWithExtra) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AnyWithExtra.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 *AnyWithExtra) XXX_Merge(src proto.Message) { + xxx_messageInfo_AnyWithExtra.Merge(m, src) +} +func (m *AnyWithExtra) XXX_Size() int { + return m.Size() +} +func (m *AnyWithExtra) XXX_DiscardUnknown() { + xxx_messageInfo_AnyWithExtra.DiscardUnknown(m) +} + +var xxx_messageInfo_AnyWithExtra proto.InternalMessageInfo + +func (m *AnyWithExtra) GetB() int64 { + if m != nil { + return m.B + } + return 0 +} + +func (m *AnyWithExtra) GetC() int64 { + if m != nil { + return m.C + } + return 0 +} + +type TestUpdatedTxRaw struct { + BodyBytes []byte `protobuf:"bytes,1,opt,name=body_bytes,json=bodyBytes,proto3" json:"body_bytes,omitempty"` + AuthInfoBytes []byte `protobuf:"bytes,2,opt,name=auth_info_bytes,json=authInfoBytes,proto3" json:"auth_info_bytes,omitempty"` + Signatures [][]byte `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"` + NewField_5 []byte `protobuf:"bytes,5,opt,name=new_field_5,json=newField5,proto3" json:"new_field_5,omitempty"` + NewField_1024 []byte `protobuf:"bytes,1024,opt,name=new_field_1024,json=newField1024,proto3" json:"new_field_1024,omitempty"` +} + +func (m *TestUpdatedTxRaw) Reset() { *m = TestUpdatedTxRaw{} } +func (m *TestUpdatedTxRaw) String() string { return proto.CompactTextString(m) } +func (*TestUpdatedTxRaw) ProtoMessage() {} +func (*TestUpdatedTxRaw) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{20} +} +func (m *TestUpdatedTxRaw) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestUpdatedTxRaw) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestUpdatedTxRaw.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 *TestUpdatedTxRaw) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestUpdatedTxRaw.Merge(m, src) +} +func (m *TestUpdatedTxRaw) XXX_Size() int { + return m.Size() +} +func (m *TestUpdatedTxRaw) XXX_DiscardUnknown() { + xxx_messageInfo_TestUpdatedTxRaw.DiscardUnknown(m) +} + +var xxx_messageInfo_TestUpdatedTxRaw proto.InternalMessageInfo + +func (m *TestUpdatedTxRaw) GetBodyBytes() []byte { + if m != nil { + return m.BodyBytes + } + return nil +} + +func (m *TestUpdatedTxRaw) GetAuthInfoBytes() []byte { + if m != nil { + return m.AuthInfoBytes + } + return nil +} + +func (m *TestUpdatedTxRaw) GetSignatures() [][]byte { + if m != nil { + return m.Signatures + } + return nil +} + +func (m *TestUpdatedTxRaw) GetNewField_5() []byte { + if m != nil { + return m.NewField_5 + } + return nil +} + +func (m *TestUpdatedTxRaw) GetNewField_1024() []byte { + if m != nil { + return m.NewField_1024 + } + return nil +} + +type TestUpdatedTxBody struct { + Messages []*types.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"` + TimeoutHeight int64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"` + SomeNewField uint64 `protobuf:"varint,4,opt,name=some_new_field,json=someNewField,proto3" json:"some_new_field,omitempty"` + SomeNewFieldNonCriticalField string `protobuf:"bytes,1050,opt,name=some_new_field_non_critical_field,json=someNewFieldNonCriticalField,proto3" json:"some_new_field_non_critical_field,omitempty"` + ExtensionOptions []*types.Any `protobuf:"bytes,1023,rep,name=extension_options,json=extensionOptions,proto3" json:"extension_options,omitempty"` + NonCriticalExtensionOptions []*types.Any `protobuf:"bytes,2047,rep,name=non_critical_extension_options,json=nonCriticalExtensionOptions,proto3" json:"non_critical_extension_options,omitempty"` +} + +func (m *TestUpdatedTxBody) Reset() { *m = TestUpdatedTxBody{} } +func (m *TestUpdatedTxBody) String() string { return proto.CompactTextString(m) } +func (*TestUpdatedTxBody) ProtoMessage() {} +func (*TestUpdatedTxBody) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{21} +} +func (m *TestUpdatedTxBody) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestUpdatedTxBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestUpdatedTxBody.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 *TestUpdatedTxBody) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestUpdatedTxBody.Merge(m, src) +} +func (m *TestUpdatedTxBody) XXX_Size() int { + return m.Size() +} +func (m *TestUpdatedTxBody) XXX_DiscardUnknown() { + xxx_messageInfo_TestUpdatedTxBody.DiscardUnknown(m) +} + +var xxx_messageInfo_TestUpdatedTxBody proto.InternalMessageInfo + +func (m *TestUpdatedTxBody) GetMessages() []*types.Any { + if m != nil { + return m.Messages + } + return nil +} + +func (m *TestUpdatedTxBody) GetMemo() string { + if m != nil { + return m.Memo + } + return "" +} + +func (m *TestUpdatedTxBody) GetTimeoutHeight() int64 { + if m != nil { + return m.TimeoutHeight + } + return 0 +} + +func (m *TestUpdatedTxBody) GetSomeNewField() uint64 { + if m != nil { + return m.SomeNewField + } + return 0 +} + +func (m *TestUpdatedTxBody) GetSomeNewFieldNonCriticalField() string { + if m != nil { + return m.SomeNewFieldNonCriticalField + } + return "" +} + +func (m *TestUpdatedTxBody) GetExtensionOptions() []*types.Any { + if m != nil { + return m.ExtensionOptions + } + return nil +} + +func (m *TestUpdatedTxBody) GetNonCriticalExtensionOptions() []*types.Any { + if m != nil { + return m.NonCriticalExtensionOptions + } + return nil +} + +type TestUpdatedAuthInfo struct { + SignerInfos []*tx.SignerInfo `protobuf:"bytes,1,rep,name=signer_infos,json=signerInfos,proto3" json:"signer_infos,omitempty"` + Fee *tx.Fee `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee,omitempty"` + NewField_3 []byte `protobuf:"bytes,3,opt,name=new_field_3,json=newField3,proto3" json:"new_field_3,omitempty"` + NewField_1024 []byte `protobuf:"bytes,1024,opt,name=new_field_1024,json=newField1024,proto3" json:"new_field_1024,omitempty"` +} + +func (m *TestUpdatedAuthInfo) Reset() { *m = TestUpdatedAuthInfo{} } +func (m *TestUpdatedAuthInfo) String() string { return proto.CompactTextString(m) } +func (*TestUpdatedAuthInfo) ProtoMessage() {} +func (*TestUpdatedAuthInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{22} +} +func (m *TestUpdatedAuthInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestUpdatedAuthInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestUpdatedAuthInfo.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 *TestUpdatedAuthInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestUpdatedAuthInfo.Merge(m, src) +} +func (m *TestUpdatedAuthInfo) XXX_Size() int { + return m.Size() +} +func (m *TestUpdatedAuthInfo) XXX_DiscardUnknown() { + xxx_messageInfo_TestUpdatedAuthInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_TestUpdatedAuthInfo proto.InternalMessageInfo + +func (m *TestUpdatedAuthInfo) GetSignerInfos() []*tx.SignerInfo { + if m != nil { + return m.SignerInfos + } + return nil +} + +func (m *TestUpdatedAuthInfo) GetFee() *tx.Fee { + if m != nil { + return m.Fee + } + return nil +} + +func (m *TestUpdatedAuthInfo) GetNewField_3() []byte { + if m != nil { + return m.NewField_3 + } + return nil +} + +func (m *TestUpdatedAuthInfo) GetNewField_1024() []byte { + if m != nil { + return m.NewField_1024 + } + return nil +} + +type TestRepeatedUints struct { + Nums []uint64 `protobuf:"varint,1,rep,packed,name=nums,proto3" json:"nums,omitempty"` +} + +func (m *TestRepeatedUints) Reset() { *m = TestRepeatedUints{} } +func (m *TestRepeatedUints) String() string { return proto.CompactTextString(m) } +func (*TestRepeatedUints) ProtoMessage() {} +func (*TestRepeatedUints) Descriptor() ([]byte, []int) { + return fileDescriptor_448ea787339d1228, []int{23} +} +func (m *TestRepeatedUints) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TestRepeatedUints) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TestRepeatedUints.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 *TestRepeatedUints) XXX_Merge(src proto.Message) { + xxx_messageInfo_TestRepeatedUints.Merge(m, src) +} +func (m *TestRepeatedUints) XXX_Size() int { + return m.Size() +} +func (m *TestRepeatedUints) XXX_DiscardUnknown() { + xxx_messageInfo_TestRepeatedUints.DiscardUnknown(m) +} + +var xxx_messageInfo_TestRepeatedUints proto.InternalMessageInfo + +func (m *TestRepeatedUints) GetNums() []uint64 { + if m != nil { + return m.Nums + } + return nil +} + +func init() { + proto.RegisterEnum("testdata.Customer2_City", Customer2_City_name, Customer2_City_value) + proto.RegisterType((*Customer1)(nil), "testdata.Customer1") + proto.RegisterType((*Customer2)(nil), "testdata.Customer2") + proto.RegisterType((*Nested4A)(nil), "testdata.Nested4A") + proto.RegisterType((*Nested3A)(nil), "testdata.Nested3A") + proto.RegisterMapType((map[int64]*Nested4A)(nil), "testdata.Nested3A.IndexEntry") + proto.RegisterType((*Nested2A)(nil), "testdata.Nested2A") + proto.RegisterType((*Nested1A)(nil), "testdata.Nested1A") + proto.RegisterType((*Nested4B)(nil), "testdata.Nested4B") + proto.RegisterType((*Nested3B)(nil), "testdata.Nested3B") + proto.RegisterType((*Nested2B)(nil), "testdata.Nested2B") + proto.RegisterType((*Nested1B)(nil), "testdata.Nested1B") + proto.RegisterType((*Customer3)(nil), "testdata.Customer3") + proto.RegisterType((*TestVersion1)(nil), "testdata.TestVersion1") + proto.RegisterType((*TestVersion2)(nil), "testdata.TestVersion2") + proto.RegisterType((*TestVersion3)(nil), "testdata.TestVersion3") + proto.RegisterType((*TestVersion3LoneOneOfValue)(nil), "testdata.TestVersion3LoneOneOfValue") + proto.RegisterType((*TestVersion3LoneNesting)(nil), "testdata.TestVersion3LoneNesting") + proto.RegisterType((*TestVersion3LoneNesting_Inner1)(nil), "testdata.TestVersion3LoneNesting.Inner1") + proto.RegisterType((*TestVersion3LoneNesting_Inner1_InnerInner)(nil), "testdata.TestVersion3LoneNesting.Inner1.InnerInner") + proto.RegisterType((*TestVersion3LoneNesting_Inner2)(nil), "testdata.TestVersion3LoneNesting.Inner2") + proto.RegisterType((*TestVersion3LoneNesting_Inner2_InnerInner)(nil), "testdata.TestVersion3LoneNesting.Inner2.InnerInner") + proto.RegisterType((*TestVersion4LoneNesting)(nil), "testdata.TestVersion4LoneNesting") + proto.RegisterType((*TestVersion4LoneNesting_Inner1)(nil), "testdata.TestVersion4LoneNesting.Inner1") + proto.RegisterType((*TestVersion4LoneNesting_Inner1_InnerInner)(nil), "testdata.TestVersion4LoneNesting.Inner1.InnerInner") + proto.RegisterType((*TestVersion4LoneNesting_Inner2)(nil), "testdata.TestVersion4LoneNesting.Inner2") + proto.RegisterType((*TestVersion4LoneNesting_Inner2_InnerInner)(nil), "testdata.TestVersion4LoneNesting.Inner2.InnerInner") + proto.RegisterType((*TestVersionFD1)(nil), "testdata.TestVersionFD1") + proto.RegisterType((*TestVersionFD1WithExtraAny)(nil), "testdata.TestVersionFD1WithExtraAny") + proto.RegisterType((*AnyWithExtra)(nil), "testdata.AnyWithExtra") + proto.RegisterType((*TestUpdatedTxRaw)(nil), "testdata.TestUpdatedTxRaw") + proto.RegisterType((*TestUpdatedTxBody)(nil), "testdata.TestUpdatedTxBody") + proto.RegisterType((*TestUpdatedAuthInfo)(nil), "testdata.TestUpdatedAuthInfo") + proto.RegisterType((*TestRepeatedUints)(nil), "testdata.TestRepeatedUints") +} + +func init() { proto.RegisterFile("unknonwnproto.proto", fileDescriptor_448ea787339d1228) } + +var fileDescriptor_448ea787339d1228 = []byte{ + // 1644 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0x4f, 0x6f, 0x1b, 0xc7, + 0x15, 0xd7, 0x70, 0x49, 0x89, 0x7c, 0xa2, 0x69, 0x66, 0x6c, 0xb4, 0x1b, 0x3a, 0x66, 0x98, 0x85, + 0xeb, 0xb0, 0x41, 0x43, 0x9a, 0x4b, 0x06, 0x28, 0x72, 0x32, 0xe9, 0x58, 0x95, 0x01, 0x57, 0x2e, + 0xa6, 0x4e, 0x5a, 0xf8, 0x42, 0x2c, 0xb9, 0x43, 0x72, 0x21, 0x72, 0x46, 0xdd, 0x99, 0xb5, 0xc8, + 0x5b, 0xd1, 0x1e, 0x7a, 0xcd, 0xa5, 0x28, 0xd0, 0x6f, 0xd0, 0x53, 0x91, 0x6f, 0xd0, 0xa3, 0x2f, + 0x05, 0x7c, 0x29, 0x50, 0xa0, 0x40, 0x50, 0xd8, 0xd7, 0x7e, 0x83, 0xa2, 0x48, 0x31, 0xb3, 0x7f, + 0xb8, 0x94, 0x44, 0x85, 0x52, 0xda, 0x18, 0x02, 0x72, 0x11, 0x67, 0xde, 0xfe, 0xe6, 0xcd, 0x7b, + 0xbf, 0xf7, 0x67, 0x77, 0x46, 0x70, 0x23, 0x60, 0x87, 0x8c, 0xb3, 0x63, 0x76, 0xe4, 0x73, 0xc9, + 0x1b, 0xfa, 0x2f, 0xce, 0x4b, 0x2a, 0xa4, 0xeb, 0x48, 0xa7, 0x72, 0x73, 0xcc, 0xc7, 0x5c, 0x0b, + 0x9b, 0x6a, 0x14, 0x3e, 0xaf, 0xbc, 0x3d, 0xe6, 0x7c, 0x3c, 0xa5, 0x4d, 0x3d, 0x1b, 0x04, 0xa3, + 0xa6, 0xc3, 0x16, 0xd1, 0xa3, 0xca, 0x90, 0x8b, 0x19, 0x17, 0x4d, 0x39, 0x6f, 0x3e, 0x6f, 0x0d, + 0xa8, 0x74, 0x5a, 0x4d, 0x39, 0x0f, 0x9f, 0x59, 0x12, 0x0a, 0x0f, 0x02, 0x21, 0xf9, 0x8c, 0xfa, + 0x2d, 0x5c, 0x82, 0x8c, 0xe7, 0x9a, 0xa8, 0x86, 0xea, 0x39, 0x92, 0xf1, 0x5c, 0x8c, 0x21, 0xcb, + 0x9c, 0x19, 0x35, 0x33, 0x35, 0x54, 0x2f, 0x10, 0x3d, 0xc6, 0x3f, 0x84, 0xb2, 0x08, 0x06, 0x62, + 0xe8, 0x7b, 0x47, 0xd2, 0xe3, 0xac, 0x3f, 0xa2, 0xd4, 0x34, 0x6a, 0xa8, 0x9e, 0x21, 0xd7, 0xd3, + 0xf2, 0x3d, 0x4a, 0xb1, 0x09, 0x3b, 0x47, 0xce, 0x62, 0x46, 0x99, 0x34, 0x77, 0xb4, 0x86, 0x78, + 0x6a, 0x7d, 0x91, 0x59, 0x6e, 0x6b, 0x9f, 0xda, 0xb6, 0x02, 0x79, 0x8f, 0xb9, 0x81, 0x90, 0xfe, + 0x42, 0x6f, 0x9d, 0x23, 0xc9, 0x3c, 0x31, 0xc9, 0x48, 0x99, 0x74, 0x13, 0x72, 0x23, 0x7a, 0x4c, + 0x7d, 0x33, 0xab, 0xed, 0x08, 0x27, 0xf8, 0x16, 0xe4, 0x7d, 0x2a, 0xa8, 0xff, 0x9c, 0xba, 0xe6, + 0x1f, 0xf2, 0x35, 0x54, 0x37, 0x48, 0x22, 0xc0, 0x3f, 0x82, 0xec, 0xd0, 0x93, 0x0b, 0x73, 0xbb, + 0x86, 0xea, 0x25, 0xdb, 0x6c, 0xc4, 0xe4, 0x36, 0x12, 0xab, 0x1a, 0x0f, 0x3c, 0xb9, 0x20, 0x1a, + 0x85, 0x3f, 0x86, 0x6b, 0x33, 0x4f, 0x0c, 0xe9, 0x74, 0xea, 0x30, 0xca, 0x03, 0x61, 0x42, 0x0d, + 0xd5, 0x77, 0xed, 0x9b, 0x8d, 0x90, 0xf3, 0x46, 0xcc, 0x79, 0xa3, 0xcb, 0x16, 0x64, 0x15, 0x6a, + 0xfd, 0x04, 0xb2, 0x4a, 0x13, 0xce, 0x43, 0xf6, 0xb1, 0xc3, 0x45, 0x79, 0x0b, 0x97, 0x00, 0x1e, + 0x73, 0xd1, 0x65, 0x63, 0x3a, 0xa5, 0xa2, 0x8c, 0x70, 0x11, 0xf2, 0x3f, 0x73, 0xa6, 0xbc, 0x3b, + 0x95, 0xbc, 0x9c, 0xc1, 0x00, 0xdb, 0x3f, 0xe5, 0x62, 0xc8, 0x8f, 0xcb, 0x06, 0xde, 0x85, 0x9d, + 0x03, 0xc7, 0xf3, 0xf9, 0xc0, 0x2b, 0x67, 0xad, 0x06, 0xe4, 0x0f, 0xa8, 0x90, 0xd4, 0xed, 0x74, + 0x37, 0x09, 0x94, 0xf5, 0x37, 0x14, 0x2f, 0x68, 0x6f, 0xb4, 0x00, 0x5b, 0x90, 0x71, 0x3a, 0x66, + 0xb6, 0x66, 0xd4, 0x77, 0x6d, 0xbc, 0x64, 0x24, 0xde, 0x94, 0x64, 0x9c, 0x0e, 0x6e, 0x43, 0xce, + 0x63, 0x2e, 0x9d, 0x9b, 0x39, 0x0d, 0xbb, 0x7d, 0x12, 0xd6, 0xee, 0x36, 0x1e, 0xa9, 0xe7, 0x0f, + 0x99, 0xf4, 0x17, 0x24, 0xc4, 0x56, 0x1e, 0x03, 0x2c, 0x85, 0xb8, 0x0c, 0xc6, 0x21, 0x5d, 0x68, + 0x5b, 0x0c, 0xa2, 0x86, 0xb8, 0x0e, 0xb9, 0xe7, 0xce, 0x34, 0x08, 0xad, 0x39, 0x7b, 0xef, 0x10, + 0xf0, 0x71, 0xe6, 0xc7, 0xc8, 0x7a, 0x16, 0xbb, 0x65, 0x6f, 0xe6, 0xd6, 0x07, 0xb0, 0xcd, 0x34, + 0x5e, 0xe7, 0xcc, 0x19, 0xea, 0xdb, 0x5d, 0x12, 0x21, 0xac, 0xbd, 0x58, 0x77, 0xeb, 0xb4, 0xee, + 0xa5, 0x9e, 0x35, 0x66, 0xda, 0x4b, 0x3d, 0xf7, 0x93, 0x58, 0xf5, 0x4e, 0xe9, 0x29, 0x83, 0xe1, + 0x8c, 0x69, 0x94, 0xd8, 0x6a, 0x78, 0x56, 0x4e, 0x5b, 0x6e, 0x12, 0xbc, 0x4b, 0x6a, 0x50, 0xe1, + 0x1c, 0xac, 0x0f, 0x67, 0x8f, 0x64, 0x06, 0x1d, 0x8b, 0x25, 0x5c, 0x9e, 0xb9, 0x8b, 0xaa, 0x6d, + 0xb5, 0x0b, 0x22, 0x6a, 0xb8, 0x01, 0x93, 0xbd, 0x98, 0x01, 0x55, 0x93, 0x3e, 0x0f, 0x24, 0xd5, + 0x35, 0x59, 0x20, 0xe1, 0xc4, 0xfa, 0x65, 0xc2, 0x6f, 0xef, 0x12, 0xfc, 0x2e, 0xb5, 0x47, 0x0c, + 0x18, 0x09, 0x03, 0xd6, 0x6f, 0x52, 0x1d, 0xa5, 0xbd, 0x51, 0x5e, 0x94, 0x20, 0x23, 0x46, 0x51, + 0xeb, 0xca, 0x88, 0x11, 0x7e, 0x07, 0x0a, 0x22, 0xf0, 0x87, 0x13, 0xc7, 0x1f, 0xd3, 0xa8, 0x93, + 0x2c, 0x05, 0xb8, 0x06, 0xbb, 0x2e, 0x15, 0xd2, 0x63, 0x8e, 0xea, 0x6e, 0x66, 0x4e, 0x2b, 0x4a, + 0x8b, 0xf0, 0x5d, 0x28, 0x0d, 0x7d, 0xea, 0x7a, 0xb2, 0x3f, 0x74, 0x7c, 0xb7, 0xcf, 0x78, 0xd8, + 0xf4, 0xf6, 0xb7, 0x48, 0x31, 0x94, 0x3f, 0x70, 0x7c, 0xf7, 0x80, 0xe3, 0xdb, 0x50, 0x18, 0x4e, + 0xe8, 0xaf, 0x02, 0xaa, 0x20, 0xf9, 0x08, 0x92, 0x0f, 0x45, 0x07, 0x1c, 0x37, 0x21, 0xcf, 0x7d, + 0x6f, 0xec, 0x31, 0x67, 0x6a, 0x16, 0x34, 0x11, 0x37, 0x4e, 0x77, 0xa7, 0x16, 0x49, 0x40, 0xbd, + 0x42, 0xd2, 0x65, 0xad, 0x7f, 0x65, 0xa0, 0xf8, 0x94, 0x0a, 0xf9, 0x19, 0xf5, 0x85, 0xc7, 0x59, + 0x0b, 0x17, 0x01, 0xcd, 0xa3, 0x4a, 0x43, 0x73, 0x7c, 0x07, 0x90, 0x13, 0x91, 0xfb, 0xbd, 0xa5, + 0xce, 0xf4, 0x02, 0x82, 0x1c, 0x85, 0x1a, 0x44, 0x01, 0x5e, 0x8b, 0x1a, 0x28, 0xd4, 0x30, 0x4a, + 0xae, 0xb5, 0xa8, 0x21, 0xfe, 0x00, 0x90, 0x1b, 0xb5, 0x8a, 0x35, 0xa8, 0x5e, 0xf6, 0xc5, 0x97, + 0xef, 0x6e, 0x11, 0xe4, 0xe2, 0x12, 0x20, 0xaa, 0xfb, 0x71, 0x6e, 0x7f, 0x8b, 0x20, 0x8a, 0xef, + 0x02, 0x1a, 0x69, 0x0a, 0xd7, 0xae, 0x55, 0xb8, 0x11, 0xb6, 0x00, 0x8d, 0x35, 0x8f, 0xeb, 0x1a, + 0x32, 0x1a, 0x2b, 0x6b, 0x27, 0x66, 0xe1, 0x7c, 0x6b, 0x27, 0xf8, 0x7d, 0x40, 0x87, 0x66, 0x71, + 0x2d, 0xe7, 0xbd, 0xec, 0xcb, 0x2f, 0xdf, 0x45, 0x04, 0x1d, 0xf6, 0x72, 0x60, 0x88, 0x60, 0x66, + 0xfd, 0xd6, 0x58, 0xa1, 0xdb, 0xbe, 0x28, 0xdd, 0xf6, 0x46, 0x74, 0xdb, 0x1b, 0xd1, 0x6d, 0x2b, + 0xba, 0xef, 0x7c, 0x1d, 0xdd, 0xf6, 0xa5, 0x88, 0xb6, 0xdf, 0x14, 0xd1, 0xf8, 0x16, 0x14, 0x18, + 0x3d, 0xee, 0x8f, 0x3c, 0x3a, 0x75, 0xcd, 0xb7, 0x6b, 0xa8, 0x9e, 0x25, 0x79, 0x46, 0x8f, 0xf7, + 0xd4, 0x3c, 0x8e, 0xc2, 0xef, 0x57, 0xa3, 0xd0, 0xbe, 0x68, 0x14, 0xda, 0x1b, 0x45, 0xa1, 0xbd, + 0x51, 0x14, 0xda, 0x1b, 0x45, 0xa1, 0x7d, 0xa9, 0x28, 0xb4, 0xdf, 0x58, 0x14, 0x3e, 0x04, 0xcc, + 0x38, 0xeb, 0x0f, 0x7d, 0x4f, 0x7a, 0x43, 0x67, 0x1a, 0x85, 0xe3, 0x77, 0xba, 0x77, 0x91, 0x32, + 0xe3, 0xec, 0x41, 0xf4, 0x64, 0x25, 0x2e, 0xff, 0xce, 0x40, 0x25, 0x6d, 0xfe, 0x63, 0xce, 0xe8, + 0x13, 0x46, 0x9f, 0x8c, 0x3e, 0x53, 0xaf, 0xf2, 0x2b, 0x1a, 0xa5, 0x2b, 0xc3, 0xfe, 0x7f, 0xb6, + 0xe1, 0xfb, 0x27, 0xd9, 0x3f, 0xd0, 0x6f, 0xab, 0xf1, 0x15, 0xa1, 0xbe, 0xb5, 0x2c, 0x88, 0xf7, + 0xce, 0x46, 0xa5, 0x7c, 0xba, 0x22, 0xb5, 0x81, 0xef, 0xc3, 0xb6, 0xc7, 0x18, 0xf5, 0x5b, 0x66, + 0x49, 0x2b, 0xaf, 0x7f, 0xad, 0x67, 0x8d, 0x47, 0x1a, 0x4f, 0xa2, 0x75, 0x89, 0x06, 0xdb, 0xbc, + 0x7e, 0x21, 0x0d, 0x76, 0xa4, 0xc1, 0xae, 0xfc, 0x09, 0xc1, 0x76, 0xa8, 0x34, 0xf5, 0x9d, 0x64, + 0xac, 0xfd, 0x4e, 0x7a, 0xa4, 0x3e, 0xf9, 0x19, 0xf5, 0xa3, 0xe8, 0xb7, 0x37, 0xb5, 0x38, 0xfc, + 0xd1, 0x7f, 0x48, 0xa8, 0xa1, 0x72, 0x4f, 0x1d, 0x04, 0x62, 0x61, 0x6a, 0xf3, 0x42, 0xbc, 0xb9, + 0x3e, 0x93, 0x45, 0x9b, 0xab, 0x71, 0xe5, 0xcf, 0xb1, 0xad, 0xf6, 0x29, 0xb8, 0x09, 0x3b, 0x43, + 0x1e, 0xb0, 0xf8, 0x90, 0x58, 0x20, 0xf1, 0xf4, 0xb2, 0x16, 0xdb, 0xff, 0x0b, 0x8b, 0xe3, 0xfa, + 0xfb, 0x6a, 0xb5, 0xfe, 0x3a, 0xdf, 0xd5, 0xdf, 0x15, 0xaa, 0xbf, 0xce, 0x37, 0xae, 0xbf, 0xce, + 0xb7, 0x5c, 0x7f, 0x9d, 0x6f, 0x54, 0x7f, 0xc6, 0xda, 0xfa, 0xfb, 0xe2, 0xff, 0x56, 0x7f, 0x9d, + 0x8d, 0xea, 0xcf, 0x3e, 0xb7, 0xfe, 0x6e, 0xa6, 0x2f, 0x0e, 0x8c, 0xe8, 0x92, 0x20, 0xae, 0xc0, + 0xbf, 0x22, 0x28, 0xa5, 0xf6, 0xdb, 0xfb, 0xe4, 0x72, 0xc7, 0xa1, 0x37, 0x7e, 0x2c, 0x89, 0xfd, + 0xf9, 0x07, 0x5a, 0xf9, 0x9e, 0xda, 0xfb, 0xa4, 0xf5, 0x0b, 0x4f, 0x4e, 0x1e, 0xce, 0xa5, 0xef, + 0x74, 0xd9, 0xe2, 0x5b, 0xf5, 0xed, 0xce, 0xd2, 0xb7, 0x14, 0xae, 0xcb, 0x16, 0x89, 0x45, 0x17, + 0xf6, 0xee, 0x29, 0x14, 0xd3, 0xeb, 0x71, 0x5d, 0x39, 0x80, 0xd6, 0xd3, 0x17, 0x77, 0x00, 0x47, + 0x39, 0x1e, 0x76, 0x46, 0x43, 0x75, 0xc0, 0x62, 0xd8, 0x01, 0xf5, 0x6c, 0x68, 0xfd, 0x05, 0x41, + 0x59, 0x6d, 0xf8, 0xe9, 0x91, 0xeb, 0x48, 0xea, 0x3e, 0x9d, 0x13, 0xe7, 0x18, 0xdf, 0x06, 0x18, + 0x70, 0x77, 0xd1, 0x1f, 0x2c, 0x24, 0x15, 0x7a, 0x8f, 0x22, 0x29, 0x28, 0x49, 0x4f, 0x09, 0xf0, + 0x5d, 0xb8, 0xee, 0x04, 0x72, 0xd2, 0xf7, 0xd8, 0x88, 0x47, 0x98, 0x8c, 0xc6, 0x5c, 0x53, 0xe2, + 0x47, 0x6c, 0xc4, 0x43, 0x5c, 0x15, 0x40, 0x78, 0x63, 0xe6, 0xc8, 0xc0, 0xa7, 0xc2, 0x34, 0x6a, + 0x46, 0xbd, 0x48, 0x52, 0x12, 0x5c, 0x85, 0xdd, 0xe4, 0xec, 0xd2, 0xff, 0x48, 0xdf, 0x18, 0x14, + 0x49, 0x21, 0x3e, 0xbd, 0x7c, 0x84, 0x7f, 0x00, 0xa5, 0xe5, 0xf3, 0xd6, 0x3d, 0xbb, 0x63, 0xfe, + 0x3a, 0xaf, 0x31, 0xc5, 0x18, 0xa3, 0x84, 0xd6, 0xe7, 0x06, 0xbc, 0xb5, 0xe2, 0x42, 0x8f, 0xbb, + 0x0b, 0x7c, 0x0f, 0xf2, 0x33, 0x2a, 0x84, 0x33, 0xd6, 0x1e, 0x18, 0x6b, 0x93, 0x2c, 0x41, 0xa9, + 0xea, 0x9e, 0xd1, 0x19, 0x8f, 0xab, 0x5b, 0x8d, 0x95, 0x09, 0xd2, 0x9b, 0x51, 0x1e, 0xc8, 0xfe, + 0x84, 0x7a, 0xe3, 0x89, 0x8c, 0x78, 0xbc, 0x16, 0x49, 0xf7, 0xb5, 0x10, 0xdf, 0x81, 0x92, 0xe0, + 0x33, 0xda, 0x5f, 0x1e, 0xc5, 0xb2, 0xfa, 0x28, 0x56, 0x54, 0xd2, 0x83, 0xc8, 0x58, 0xbc, 0x0f, + 0xef, 0xad, 0xa2, 0xfa, 0x67, 0x34, 0xe6, 0x3f, 0x86, 0x8d, 0xf9, 0x9d, 0xf4, 0xca, 0x83, 0x93, + 0x4d, 0xba, 0x07, 0x6f, 0xd1, 0xb9, 0xa4, 0x4c, 0xe5, 0x48, 0x9f, 0xeb, 0xeb, 0x64, 0x61, 0x7e, + 0xb5, 0x73, 0x8e, 0x9b, 0xe5, 0x04, 0xff, 0x24, 0x84, 0xe3, 0x67, 0x50, 0x5d, 0xd9, 0xfe, 0x0c, + 0x85, 0xd7, 0xcf, 0x51, 0x78, 0x2b, 0xf5, 0xe6, 0x78, 0x78, 0x42, 0xb7, 0xf5, 0x02, 0xc1, 0x8d, + 0x54, 0x48, 0xba, 0x51, 0x5a, 0xe0, 0xfb, 0x50, 0x54, 0xf1, 0xa7, 0xbe, 0xce, 0x9d, 0x38, 0x30, + 0xb7, 0x1b, 0xe1, 0xf5, 0x7b, 0x43, 0xce, 0x1b, 0xd1, 0xf5, 0x7b, 0xe3, 0xe7, 0x1a, 0xa6, 0x16, + 0x91, 0x5d, 0x91, 0x8c, 0x05, 0xae, 0x2f, 0xef, 0xdc, 0x54, 0xd1, 0x9c, 0x5e, 0xb8, 0x47, 0x69, + 0x78, 0x17, 0xb7, 0x92, 0x5d, 0x6d, 0x1d, 0xb7, 0x54, 0x76, 0xb5, 0x37, 0xcd, 0xae, 0xf7, 0xc3, + 0xe4, 0x22, 0xf4, 0x88, 0x2a, 0x57, 0x3e, 0xf5, 0x98, 0xd4, 0xa9, 0xc2, 0x82, 0x59, 0x68, 0x7f, + 0x96, 0xe8, 0x71, 0x6f, 0xff, 0xc5, 0xab, 0x2a, 0x7a, 0xf9, 0xaa, 0x8a, 0xfe, 0xf9, 0xaa, 0x8a, + 0x3e, 0x7f, 0x5d, 0xdd, 0x7a, 0xf9, 0xba, 0xba, 0xf5, 0xf7, 0xd7, 0xd5, 0xad, 0x67, 0x8d, 0xb1, + 0x27, 0x27, 0xc1, 0xa0, 0x31, 0xe4, 0xb3, 0x66, 0xf4, 0x8f, 0x86, 0xf0, 0xe7, 0x43, 0xe1, 0x1e, + 0x36, 0x55, 0xdd, 0x07, 0xd2, 0x9b, 0x36, 0xe3, 0x06, 0x30, 0xd8, 0xd6, 0x44, 0xb7, 0xff, 0x1b, + 0x00, 0x00, 0xff, 0xff, 0xa6, 0x69, 0x10, 0x33, 0xe6, 0x18, 0x00, 0x00, +} + +func (m *Customer1) 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 *Customer1) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Customer1) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Payment) > 0 { + i -= len(m.Payment) + copy(dAtA[i:], m.Payment) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Payment))) + i-- + dAtA[i] = 0x3a + } + if m.SubscriptionFee != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.SubscriptionFee)))) + i-- + dAtA[i] = 0x1d + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Customer2) 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 *Customer2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Customer2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Reserved != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Reserved)) + i-- + dAtA[i] = 0x41 + i-- + dAtA[i] = 0xb8 + } + if m.Miscellaneous != nil { + { + size, err := m.Miscellaneous.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + if m.City != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.City)) + i-- + dAtA[i] = 0x30 + } + if m.Fewer != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.Fewer)))) + i-- + dAtA[i] = 0x25 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x1a + } + if m.Industry != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Industry)) + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested4A) 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 *Nested4A) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested4A) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested3A) 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 *Nested3A) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested3A) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Index) > 0 { + for k := range m.Index { + v := m.Index[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i = encodeVarintUnknonwnproto(dAtA, i, uint64(k)) + i-- + dAtA[i] = 0x8 + i = encodeVarintUnknonwnproto(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } + } + if len(m.A4) > 0 { + for iNdEx := len(m.A4) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.A4[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested2A) 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 *Nested2A) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested2A) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Nested != nil { + { + size, err := m.Nested.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested1A) 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 *Nested1A) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested1A) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Nested != nil { + { + size, err := m.Nested.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested4B) 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 *Nested4B) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested4B) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x1a + } + if m.Age != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Age)) + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested3B) 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 *Nested3B) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested3B) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.B4) > 0 { + for iNdEx := len(m.B4) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.B4[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x1a + } + if m.Age != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Age)) + i-- + dAtA[i] = 0x10 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested2B) 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 *Nested2B) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested2B) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Route) > 0 { + i -= len(m.Route) + copy(dAtA[i:], m.Route) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Route))) + i-- + dAtA[i] = 0x22 + } + if m.Nested != nil { + { + size, err := m.Nested.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Fee != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Fee)))) + i-- + dAtA[i] = 0x11 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Nested1B) 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 *Nested1B) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nested1B) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Age != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Age)) + i-- + dAtA[i] = 0x18 + } + if m.Nested != nil { + { + size, err := m.Nested.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Customer3) 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 *Customer3) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Customer3) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Original != nil { + { + size, err := m.Original.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.Payment != nil { + { + size := m.Payment.Size() + i -= size + if _, err := m.Payment.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.Destination) > 0 { + i -= len(m.Destination) + copy(dAtA[i:], m.Destination) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Destination))) + i-- + dAtA[i] = 0x2a + } + if m.Surcharge != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.Surcharge)))) + i-- + dAtA[i] = 0x25 + } + if m.Sf != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.Sf)))) + i-- + dAtA[i] = 0x1d + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Customer3_CreditCardNo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Customer3_CreditCardNo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.CreditCardNo) + copy(dAtA[i:], m.CreditCardNo) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.CreditCardNo))) + i-- + dAtA[i] = 0x3a + return len(dAtA) - i, nil +} +func (m *Customer3_ChequeNo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Customer3_ChequeNo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ChequeNo) + copy(dAtA[i:], m.ChequeNo) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.ChequeNo))) + i-- + dAtA[i] = 0x42 + return len(dAtA) - i, nil +} +func (m *TestVersion1) 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 *TestVersion1) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion1) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion1_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion1_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersion1_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion1_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersion2) 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 *TestVersion2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NewField != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.NewField)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion2_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion2_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersion2_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion2_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersion3) 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 *TestVersion3) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalField) > 0 { + i -= len(m.NonCriticalField) + copy(dAtA[i:], m.NonCriticalField) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NonCriticalField))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0xba + } + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersion3_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersion3LoneOneOfValue) 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 *TestVersion3LoneOneOfValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneOneOfValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalField) > 0 { + i -= len(m.NonCriticalField) + copy(dAtA[i:], m.NonCriticalField) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NonCriticalField))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0xba + } + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3LoneOneOfValue_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneOneOfValue_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersion3LoneNesting) 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 *TestVersion3LoneNesting) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalField) > 0 { + i -= len(m.NonCriticalField) + copy(dAtA[i:], m.NonCriticalField) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NonCriticalField))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0xba + } + if m.Inner2 != nil { + { + size, err := m.Inner2.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7a + } + if m.Inner1 != nil { + { + size, err := m.Inner1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x72 + } + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3LoneNesting_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersion3LoneNesting_Inner1) 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 *TestVersion3LoneNesting_Inner1) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting_Inner1) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Inner != nil { + { + size, err := m.Inner.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) 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 *TestVersion3LoneNesting_Inner1_InnerInner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.City) > 0 { + i -= len(m.City) + copy(dAtA[i:], m.City) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.City))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3LoneNesting_Inner2) 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 *TestVersion3LoneNesting_Inner2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting_Inner2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Inner != nil { + { + size, err := m.Inner.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Country) > 0 { + i -= len(m.Country) + copy(dAtA[i:], m.Country) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Country))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) 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 *TestVersion3LoneNesting_Inner2_InnerInner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.City) > 0 { + i -= len(m.City) + copy(dAtA[i:], m.City) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.City))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestVersion4LoneNesting) 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 *TestVersion4LoneNesting) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalField) > 0 { + i -= len(m.NonCriticalField) + copy(dAtA[i:], m.NonCriticalField) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NonCriticalField))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0xba + } + if m.Inner2 != nil { + { + size, err := m.Inner2.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7a + } + if m.Inner1 != nil { + { + size, err := m.Inner1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x72 + } + if m.Customer1 != nil { + { + size, err := m.Customer1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + } + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.D) > 0 { + for iNdEx := len(m.D) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.D[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.C) > 0 { + for iNdEx := len(m.C) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.C[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.B != nil { + { + size, err := m.B.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion4LoneNesting_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersion4LoneNesting_Inner1) 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 *TestVersion4LoneNesting_Inner1) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting_Inner1) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Inner != nil { + { + size, err := m.Inner.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) 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 *TestVersion4LoneNesting_Inner1_InnerInner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.City) > 0 { + i -= len(m.City) + copy(dAtA[i:], m.City) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.City))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersion4LoneNesting_Inner2) 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 *TestVersion4LoneNesting_Inner2) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting_Inner2) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Inner != nil { + { + size, err := m.Inner.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Country) > 0 { + i -= len(m.Country) + copy(dAtA[i:], m.Country) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Country))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) 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 *TestVersion4LoneNesting_Inner2_InnerInner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Value != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x10 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestVersionFD1) 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 *TestVersionFD1) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersionFD1_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersionFD1_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *TestVersionFD1WithExtraAny) 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 *TestVersionFD1WithExtraAny) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1WithExtraAny) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.H) > 0 { + for iNdEx := len(m.H) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.H[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.G != nil { + { + size, err := m.G.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if m.A != nil { + { + size, err := m.A.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.X != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.X)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TestVersionFD1WithExtraAny_E) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1WithExtraAny_E) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.E)) + i-- + dAtA[i] = 0x30 + return len(dAtA) - i, nil +} +func (m *TestVersionFD1WithExtraAny_F) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestVersionFD1WithExtraAny_F) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.F != nil { + { + size, err := m.F.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *AnyWithExtra) 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 *AnyWithExtra) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AnyWithExtra) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.C != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.C)) + i-- + dAtA[i] = 0x20 + } + if m.B != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.B)) + i-- + dAtA[i] = 0x18 + } + if m.Any != nil { + { + size, err := m.Any.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestUpdatedTxRaw) 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 *TestUpdatedTxRaw) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestUpdatedTxRaw) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewField_1024) > 0 { + i -= len(m.NewField_1024) + copy(dAtA[i:], m.NewField_1024) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NewField_1024))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0x82 + } + if len(m.NewField_5) > 0 { + i -= len(m.NewField_5) + copy(dAtA[i:], m.NewField_5) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NewField_5))) + i-- + dAtA[i] = 0x2a + } + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.AuthInfoBytes) > 0 { + i -= len(m.AuthInfoBytes) + copy(dAtA[i:], m.AuthInfoBytes) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.AuthInfoBytes))) + i-- + dAtA[i] = 0x12 + } + if len(m.BodyBytes) > 0 { + i -= len(m.BodyBytes) + copy(dAtA[i:], m.BodyBytes) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.BodyBytes))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TestUpdatedTxBody) 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 *TestUpdatedTxBody) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestUpdatedTxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalExtensionOptions) > 0 { + for iNdEx := len(m.NonCriticalExtensionOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.NonCriticalExtensionOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7f + i-- + dAtA[i] = 0xfa + } + } + if len(m.SomeNewFieldNonCriticalField) > 0 { + i -= len(m.SomeNewFieldNonCriticalField) + copy(dAtA[i:], m.SomeNewFieldNonCriticalField) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.SomeNewFieldNonCriticalField))) + i-- + dAtA[i] = 0x41 + i-- + dAtA[i] = 0xd2 + } + if len(m.ExtensionOptions) > 0 { + for iNdEx := len(m.ExtensionOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExtensionOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3f + i-- + dAtA[i] = 0xfa + } + } + if m.SomeNewField != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.SomeNewField)) + i-- + dAtA[i] = 0x20 + } + if m.TimeoutHeight != 0 { + i = encodeVarintUnknonwnproto(dAtA, i, uint64(m.TimeoutHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x12 + } + if len(m.Messages) > 0 { + for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Messages[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TestUpdatedAuthInfo) 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 *TestUpdatedAuthInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestUpdatedAuthInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewField_1024) > 0 { + i -= len(m.NewField_1024) + copy(dAtA[i:], m.NewField_1024) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NewField_1024))) + i-- + dAtA[i] = 0x40 + i-- + dAtA[i] = 0x82 + } + if len(m.NewField_3) > 0 { + i -= len(m.NewField_3) + copy(dAtA[i:], m.NewField_3) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(len(m.NewField_3))) + i-- + dAtA[i] = 0x1a + } + if m.Fee != nil { + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.SignerInfos) > 0 { + for iNdEx := len(m.SignerInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SignerInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUnknonwnproto(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TestRepeatedUints) 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 *TestRepeatedUints) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TestRepeatedUints) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nums) > 0 { + dAtA54 := make([]byte, len(m.Nums)*10) + var j53 int + for _, num := range m.Nums { + for num >= 1<<7 { + dAtA54[j53] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j53++ + } + dAtA54[j53] = uint8(num) + j53++ + } + i -= j53 + copy(dAtA[i:], dAtA54[:j53]) + i = encodeVarintUnknonwnproto(dAtA, i, uint64(j53)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintUnknonwnproto(dAtA []byte, offset int, v uint64) int { + offset -= sovUnknonwnproto(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Customer1) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.SubscriptionFee != 0 { + n += 5 + } + l = len(m.Payment) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Customer2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Industry != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Industry)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Fewer != 0 { + n += 5 + } + if m.City != 0 { + n += 1 + sovUnknonwnproto(uint64(m.City)) + } + if m.Miscellaneous != nil { + l = m.Miscellaneous.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Reserved != 0 { + n += 2 + sovUnknonwnproto(uint64(m.Reserved)) + } + return n +} + +func (m *Nested4A) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Nested3A) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.A4) > 0 { + for _, e := range m.A4 { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.Index) > 0 { + for k, v := range m.Index { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovUnknonwnproto(uint64(l)) + } + mapEntrySize := 1 + sovUnknonwnproto(uint64(k)) + l + n += mapEntrySize + 1 + sovUnknonwnproto(uint64(mapEntrySize)) + } + } + return n +} + +func (m *Nested2A) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Nested != nil { + l = m.Nested.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Nested1A) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Nested != nil { + l = m.Nested.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Nested4B) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Age != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Age)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Nested3B) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Age != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Age)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.B4) > 0 { + for _, e := range m.B4 { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + return n +} + +func (m *Nested2B) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Fee != 0 { + n += 9 + } + if m.Nested != nil { + l = m.Nested.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.Route) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Nested1B) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + if m.Nested != nil { + l = m.Nested.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Age != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Age)) + } + return n +} + +func (m *Customer3) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Sf != 0 { + n += 5 + } + if m.Surcharge != 0 { + n += 5 + } + l = len(m.Destination) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Payment != nil { + n += m.Payment.Size() + } + if m.Original != nil { + l = m.Original.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *Customer3_CreditCardNo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CreditCardNo) + n += 1 + l + sovUnknonwnproto(uint64(l)) + return n +} +func (m *Customer3_ChequeNo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChequeNo) + n += 1 + l + sovUnknonwnproto(uint64(l)) + return n +} +func (m *TestVersion1) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion1_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersion1_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersion2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.NewField != 0 { + n += 2 + sovUnknonwnproto(uint64(m.NewField)) + } + return n +} + +func (m *TestVersion2_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersion2_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersion3) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NonCriticalField) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersion3_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersion3LoneOneOfValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NonCriticalField) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3LoneOneOfValue_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersion3LoneNesting) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner1 != nil { + l = m.Inner1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner2 != nil { + l = m.Inner2.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NonCriticalField) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3LoneNesting_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersion3LoneNesting_Inner1) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner != nil { + l = m.Inner.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3LoneNesting_Inner1_InnerInner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.City) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3LoneNesting_Inner2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.Country) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner != nil { + l = m.Inner.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion3LoneNesting_Inner2_InnerInner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.City) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion4LoneNesting) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != nil { + l = m.B.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.C) > 0 { + for _, e := range m.C { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if len(m.D) > 0 { + for _, e := range m.D { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Customer1 != nil { + l = m.Customer1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner1 != nil { + l = m.Inner1.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner2 != nil { + l = m.Inner2.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NonCriticalField) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion4LoneNesting_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersion4LoneNesting_Inner1) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner != nil { + l = m.Inner.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion4LoneNesting_Inner1_InnerInner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Id)) + } + l = len(m.City) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion4LoneNesting_Inner2) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.Country) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Inner != nil { + l = m.Inner.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestVersion4LoneNesting_Inner2_InnerInner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Value != 0 { + n += 1 + sovUnknonwnproto(uint64(m.Value)) + } + return n +} + +func (m *TestVersionFD1) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + return n +} + +func (m *TestVersionFD1_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersionFD1_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *TestVersionFD1WithExtraAny) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.X != 0 { + n += 1 + sovUnknonwnproto(uint64(m.X)) + } + if m.A != nil { + l = m.A.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.Sum != nil { + n += m.Sum.Size() + } + if m.G != nil { + l = m.G.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.H) > 0 { + for _, e := range m.H { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + return n +} + +func (m *TestVersionFD1WithExtraAny_E) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += 1 + sovUnknonwnproto(uint64(m.E)) + return n +} +func (m *TestVersionFD1WithExtraAny_F) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.F != nil { + l = m.F.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + return n +} +func (m *AnyWithExtra) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Any != nil { + l = m.Any.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.B != 0 { + n += 1 + sovUnknonwnproto(uint64(m.B)) + } + if m.C != 0 { + n += 1 + sovUnknonwnproto(uint64(m.C)) + } + return n +} + +func (m *TestUpdatedTxRaw) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.BodyBytes) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.AuthInfoBytes) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + l = len(m.NewField_5) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NewField_1024) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestUpdatedTxBody) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Messages) > 0 { + for _, e := range m.Messages { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + if m.TimeoutHeight != 0 { + n += 1 + sovUnknonwnproto(uint64(m.TimeoutHeight)) + } + if m.SomeNewField != 0 { + n += 1 + sovUnknonwnproto(uint64(m.SomeNewField)) + } + if len(m.ExtensionOptions) > 0 { + for _, e := range m.ExtensionOptions { + l = e.Size() + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + } + l = len(m.SomeNewFieldNonCriticalField) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + if len(m.NonCriticalExtensionOptions) > 0 { + for _, e := range m.NonCriticalExtensionOptions { + l = e.Size() + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + } + return n +} + +func (m *TestUpdatedAuthInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SignerInfos) > 0 { + for _, e := range m.SignerInfos { + l = e.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + } + if m.Fee != nil { + l = m.Fee.Size() + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NewField_3) + if l > 0 { + n += 1 + l + sovUnknonwnproto(uint64(l)) + } + l = len(m.NewField_1024) + if l > 0 { + n += 2 + l + sovUnknonwnproto(uint64(l)) + } + return n +} + +func (m *TestRepeatedUints) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nums) > 0 { + l = 0 + for _, e := range m.Nums { + l += sovUnknonwnproto(uint64(e)) + } + n += 1 + sovUnknonwnproto(uint64(l)) + l + } + return n +} + +func sovUnknonwnproto(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozUnknonwnproto(x uint64) (n int) { + return sovUnknonwnproto(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Customer1) 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 ErrIntOverflowUnknonwnproto + } + 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: Customer1: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Customer1: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionFee", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) + iNdEx += 4 + m.SubscriptionFee = float32(math.Float32frombits(v)) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payment", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payment = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Customer2) 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 ErrIntOverflowUnknonwnproto + } + 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: Customer2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Customer2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Industry", wireType) + } + m.Industry = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Industry |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Fewer", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) + iNdEx += 4 + m.Fewer = float32(math.Float32frombits(v)) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field City", wireType) + } + m.City = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.City |= Customer2_City(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Miscellaneous", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Miscellaneous == nil { + m.Miscellaneous = &types.Any{} + } + if err := m.Miscellaneous.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1047: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType) + } + m.Reserved = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Reserved |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested4A) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested4A: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested4A: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested3A) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested3A: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested3A: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A4", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.A4 = append(m.A4, &Nested4A{}) + if err := m.A4[len(m.A4)-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 Index", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Index == nil { + m.Index = make(map[int64]*Nested4A) + } + var mapkey int64 + var mapvalue *Nested4A + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapkey |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &Nested4A{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Index[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested2A) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested2A: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested2A: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nested", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Nested == nil { + m.Nested = &Nested3A{} + } + if err := m.Nested.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested1A) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested1A: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested1A: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nested", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Nested == nil { + m.Nested = &Nested2A{} + } + if err := m.Nested.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested4B) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested4B: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested4B: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Age", wireType) + } + m.Age = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Age |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested3B) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested3B: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested3B: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Age", wireType) + } + m.Age = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Age |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B4", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.B4 = append(m.B4, &Nested4B{}) + if err := m.B4[len(m.B4)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested2B) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested2B: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested2B: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Fee", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.Fee = float64(math.Float64frombits(v)) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nested", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Nested == nil { + m.Nested = &Nested3B{} + } + if err := m.Nested.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Route", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Route = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nested1B) 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 ErrIntOverflowUnknonwnproto + } + 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: Nested1B: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nested1B: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nested", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Nested == nil { + m.Nested = &Nested2B{} + } + if err := m.Nested.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Age", wireType) + } + m.Age = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Age |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Customer3) 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 ErrIntOverflowUnknonwnproto + } + 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: Customer3: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Customer3: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Sf", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) + iNdEx += 4 + m.Sf = float32(math.Float32frombits(v)) + case 4: + if wireType != 5 { + return fmt.Errorf("proto: wrong wireType = %d for field Surcharge", wireType) + } + var v uint32 + if (iNdEx + 4) > l { + return io.ErrUnexpectedEOF + } + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) + iNdEx += 4 + m.Surcharge = float32(math.Float32frombits(v)) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Destination = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreditCardNo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payment = &Customer3_CreditCardNo{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChequeNo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Payment = &Customer3_ChequeNo{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Original", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Original == nil { + m.Original = &Customer1{} + } + if err := m.Original.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion1) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion1: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion1: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion1{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion1{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion1{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, TestVersion1{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersion1_E{v} + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion1{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersion1_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion2) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion2{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion2{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion2{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, &TestVersion2{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersion2_E{v} + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion2{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersion2_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NewField", wireType) + } + m.NewField = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NewField |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion3: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion3: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion3{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion3{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion3{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, &TestVersion3{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersion3_E{v} + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion3{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersion3_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1031: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalField", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NonCriticalField = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneOneOfValue) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion3LoneOneOfValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion3LoneOneOfValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion3{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion3{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion3{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, &TestVersion3{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersion3LoneOneOfValue_E{v} + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1031: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalField", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NonCriticalField = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneNesting) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion3LoneNesting: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion3LoneNesting: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion3{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion3{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion3{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, &TestVersion3{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion3LoneNesting{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersion3LoneNesting_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner1 == nil { + m.Inner1 = &TestVersion3LoneNesting_Inner1{} + } + if err := m.Inner1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner2", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner2 == nil { + m.Inner2 = &TestVersion3LoneNesting_Inner2{} + } + if err := m.Inner2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1031: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalField", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NonCriticalField = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneNesting_Inner1) 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 ErrIntOverflowUnknonwnproto + } + 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: Inner1: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inner1: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner == nil { + m.Inner = &TestVersion3LoneNesting_Inner1_InnerInner{} + } + if err := m.Inner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneNesting_Inner1_InnerInner) 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 ErrIntOverflowUnknonwnproto + } + 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: InnerInner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InnerInner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field City", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.City = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneNesting_Inner2) 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 ErrIntOverflowUnknonwnproto + } + 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: Inner2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inner2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Country", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Country = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner == nil { + m.Inner = &TestVersion3LoneNesting_Inner2_InnerInner{} + } + if err := m.Inner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion3LoneNesting_Inner2_InnerInner) 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 ErrIntOverflowUnknonwnproto + } + 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: InnerInner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InnerInner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field City", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.City = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion4LoneNesting) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersion4LoneNesting: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersion4LoneNesting: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion3{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.B == nil { + m.B = &TestVersion3{} + } + if err := m.B.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.C = append(m.C, &TestVersion3{}) + if err := m.C[len(m.C)-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 D", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.D = append(m.D, &TestVersion3{}) + if err := m.D[len(m.D)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion3LoneNesting{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersion4LoneNesting_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Customer1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Customer1 == nil { + m.Customer1 = &Customer1{} + } + if err := m.Customer1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner1 == nil { + m.Inner1 = &TestVersion4LoneNesting_Inner1{} + } + if err := m.Inner1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner2", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner2 == nil { + m.Inner2 = &TestVersion4LoneNesting_Inner2{} + } + if err := m.Inner2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1031: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalField", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NonCriticalField = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion4LoneNesting_Inner1) 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 ErrIntOverflowUnknonwnproto + } + 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: Inner1: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inner1: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner == nil { + m.Inner = &TestVersion4LoneNesting_Inner1_InnerInner{} + } + if err := m.Inner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion4LoneNesting_Inner1_InnerInner) 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 ErrIntOverflowUnknonwnproto + } + 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: InnerInner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InnerInner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field City", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.City = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion4LoneNesting_Inner2) 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 ErrIntOverflowUnknonwnproto + } + 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: Inner2: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Inner2: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Country", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Country = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inner", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Inner == nil { + m.Inner = &TestVersion4LoneNesting_Inner2_InnerInner{} + } + if err := m.Inner.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersion4LoneNesting_Inner2_InnerInner) 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 ErrIntOverflowUnknonwnproto + } + 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: InnerInner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InnerInner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + m.Value = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Value |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersionFD1) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersionFD1: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersionFD1: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion1{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersionFD1_E{v} + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion1{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersionFD1_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &types.Any{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestVersionFD1WithExtraAny) 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 ErrIntOverflowUnknonwnproto + } + 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: TestVersionFD1WithExtraAny: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestVersionFD1WithExtraAny: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + m.X = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.X |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field A", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.A == nil { + m.A = &TestVersion1{} + } + if err := m.A.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field E", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sum = &TestVersionFD1WithExtraAny_E{v} + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field F", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &TestVersion1{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &TestVersionFD1WithExtraAny_F{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field G", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.G == nil { + m.G = &AnyWithExtra{} + } + if err := m.G.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field H", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.H = append(m.H, &TestVersion1{}) + if err := m.H[len(m.H)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AnyWithExtra) 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 ErrIntOverflowUnknonwnproto + } + 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: AnyWithExtra: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AnyWithExtra: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Any", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Any == nil { + m.Any = &types.Any{} + } + if err := m.Any.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field B", wireType) + } + m.B = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.B |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field C", wireType) + } + m.C = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.C |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestUpdatedTxRaw) 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 ErrIntOverflowUnknonwnproto + } + 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: TestUpdatedTxRaw: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestUpdatedTxRaw: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BodyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BodyBytes = append(m.BodyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.BodyBytes == nil { + m.BodyBytes = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthInfoBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthInfoBytes = append(m.AuthInfoBytes[:0], dAtA[iNdEx:postIndex]...) + if m.AuthInfoBytes == nil { + m.AuthInfoBytes = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewField_5", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewField_5 = append(m.NewField_5[:0], dAtA[iNdEx:postIndex]...) + if m.NewField_5 == nil { + m.NewField_5 = []byte{} + } + iNdEx = postIndex + case 1024: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewField_1024", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewField_1024 = append(m.NewField_1024[:0], dAtA[iNdEx:postIndex]...) + if m.NewField_1024 == nil { + m.NewField_1024 = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestUpdatedTxBody) 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 ErrIntOverflowUnknonwnproto + } + 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: TestUpdatedTxBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestUpdatedTxBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Messages = append(m.Messages, &types.Any{}) + if err := m.Messages[len(m.Messages)-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 Memo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutHeight", wireType) + } + m.TimeoutHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeoutHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SomeNewField", wireType) + } + m.SomeNewField = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SomeNewField |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 1023: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExtensionOptions = append(m.ExtensionOptions, &types.Any{}) + if err := m.ExtensionOptions[len(m.ExtensionOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 1050: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SomeNewFieldNonCriticalField", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + 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 ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SomeNewFieldNonCriticalField = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2047: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalExtensionOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NonCriticalExtensionOptions = append(m.NonCriticalExtensionOptions, &types.Any{}) + if err := m.NonCriticalExtensionOptions[len(m.NonCriticalExtensionOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestUpdatedAuthInfo) 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 ErrIntOverflowUnknonwnproto + } + 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: TestUpdatedAuthInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestUpdatedAuthInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignerInfos", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignerInfos = append(m.SignerInfos, &tx.SignerInfo{}) + if err := m.SignerInfos[len(m.SignerInfos)-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 Fee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Fee == nil { + m.Fee = &tx.Fee{} + } + if err := m.Fee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewField_3", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewField_3 = append(m.NewField_3[:0], dAtA[iNdEx:postIndex]...) + if m.NewField_3 == nil { + m.NewField_3 = []byte{} + } + iNdEx = postIndex + case 1024: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewField_1024", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewField_1024 = append(m.NewField_1024[:0], dAtA[iNdEx:postIndex]...) + if m.NewField_1024 == nil { + m.NewField_1024 = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TestRepeatedUints) 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 ErrIntOverflowUnknonwnproto + } + 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: TestRepeatedUints: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TestRepeatedUints: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Nums = append(m.Nums, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthUnknonwnproto + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthUnknonwnproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Nums) == 0 { + m.Nums = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUnknonwnproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Nums = append(m.Nums, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Nums", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipUnknonwnproto(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUnknonwnproto + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipUnknonwnproto(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, ErrIntOverflowUnknonwnproto + } + 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, ErrIntOverflowUnknonwnproto + } + 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, ErrIntOverflowUnknonwnproto + } + 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, ErrInvalidLengthUnknonwnproto + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupUnknonwnproto + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthUnknonwnproto + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthUnknonwnproto = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowUnknonwnproto = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupUnknonwnproto = fmt.Errorf("proto: unexpected end of group") +) diff --git a/testutil/testdata/unknonwnproto.proto b/testutil/testdata/unknonwnproto.proto new file mode 100644 index 000000000000..7bf1ce6bba71 --- /dev/null +++ b/testutil/testdata/unknonwnproto.proto @@ -0,0 +1,308 @@ +syntax = "proto3"; +package testdata; + +import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "cosmos/tx/v1beta1/tx.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/testutil/testdata"; + +message Customer1 { + int32 id = 1; + string name = 2; + float subscription_fee = 3; + + string payment = 7; +} + +message Customer2 { + int32 id = 1; + int32 industry = 2; + string name = 3; + float fewer = 4; + + int64 reserved = 1047; + + enum City { + Laos = 0; + LosAngeles = 1; + PaloAlto = 2; + Moscow = 3; + Nairobi = 4; + } + + City city = 6; + + google.protobuf.Any miscellaneous = 10; +} + +message Nested4A { + int32 id = 1; + string name = 2; +} + +message Nested3A { + int32 id = 1; + string name = 2; + repeated Nested4A a4 = 4; + map index = 5; +} + +message Nested2A { + int32 id = 1; + string name = 2; + Nested3A nested = 3; +} + +message Nested1A { + int32 id = 1; + Nested2A nested = 2; +} + +message Nested4B { + int32 id = 1; + int32 age = 2; + string name = 3; +} + +message Nested3B { + int32 id = 1; + int32 age = 2; + string name = 3; + repeated Nested4B b4 = 4; +} + +message Nested2B { + int32 id = 1; + double fee = 2; + Nested3B nested = 3; + string route = 4; +} + +message Nested1B { + int32 id = 1; + Nested2B nested = 2; + int32 age = 3; +} + +message Customer3 { + int32 id = 1; + string name = 2; + float sf = 3; + float surcharge = 4; + string destination = 5; + + oneof payment { + string credit_card_no = 7; + string cheque_no = 8; + } + + Customer1 original = 9; +} + +message TestVersion1 { + int64 x = 1; + TestVersion1 a = 2; + TestVersion1 b = 3; // [(gogoproto.nullable) = false] generates invalid recursive structs; + repeated TestVersion1 c = 4; + repeated TestVersion1 d = 5 [(gogoproto.nullable) = false]; + oneof sum { + int32 e = 6; + TestVersion1 f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; +} +message TestVersion2 { + int64 x = 1; + TestVersion2 a = 2; + TestVersion2 b = 3; // [(gogoproto.nullable) = false]; + repeated TestVersion2 c = 4; + repeated TestVersion2 d = 5; // [(gogoproto.nullable) = false]; + oneof sum { + int32 e = 6; + TestVersion2 f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; + uint64 new_field = 25; +} +message TestVersion3 { + int64 x = 1; + TestVersion3 a = 2; + TestVersion3 b = 3; // [(gogoproto.nullable) = false]; + repeated TestVersion3 c = 4; + repeated TestVersion3 d = 5; // [(gogoproto.nullable) = false]; + oneof sum { + int32 e = 6; + TestVersion3 f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; + string non_critical_field = 1031; +} + +message TestVersion3LoneOneOfValue { + int64 x = 1; + TestVersion3 a = 2; + TestVersion3 b = 3; // [(gogoproto.nullable) = false]; + repeated TestVersion3 c = 4; + repeated TestVersion3 d = 5; // [(gogoproto.nullable) = false]; + oneof sum { + int32 e = 6; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; + string non_critical_field = 1031; +} + +message TestVersion3LoneNesting { + int64 x = 1; + TestVersion3 a = 2; + TestVersion3 b = 3; // [(gogoproto.nullable) = false]; + repeated TestVersion3 c = 4; + repeated TestVersion3 d = 5; // [(gogoproto.nullable) = false]; + oneof sum { + TestVersion3LoneNesting f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; + string non_critical_field = 1031; + + message Inner1 { + int64 id = 1; + string name = 2; + message InnerInner { + string id = 1; + string city = 2; + } + InnerInner inner = 3; + } + + Inner1 inner1 = 14; + + message Inner2 { + string id = 1; + string country = 2; + message InnerInner { + string id = 1; + string city = 2; + } + InnerInner inner = 3; + } + + Inner2 inner2 = 15; +} + +message TestVersion4LoneNesting { + int64 x = 1; + TestVersion3 a = 2; + TestVersion3 b = 3; // [(gogoproto.nullable) = false]; + repeated TestVersion3 c = 4; + repeated TestVersion3 d = 5; // [(gogoproto.nullable) = false]; + oneof sum { + TestVersion3LoneNesting f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"]; + // google.protobuf.Timestamp i = 10; + // google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true]; + Customer1 k = 12 [(gogoproto.embed) = true]; + string non_critical_field = 1031; + + message Inner1 { + int64 id = 1; + string name = 2; + message InnerInner { + int64 id = 1; + string city = 2; + } + InnerInner inner = 3; + } + + Inner1 inner1 = 14; + + message Inner2 { + string id = 1; + string country = 2; + message InnerInner { + string id = 1; + int64 value = 2; + } + InnerInner inner = 3; + } + + Inner2 inner2 = 15; +} + +message TestVersionFD1 { + int64 x = 1; + TestVersion1 a = 2; + oneof sum { + int32 e = 6; + TestVersion1 f = 7; + } + google.protobuf.Any g = 8; + repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"]; +} + +message TestVersionFD1WithExtraAny { + int64 x = 1; + TestVersion1 a = 2; + oneof sum { + int32 e = 6; + TestVersion1 f = 7; + } + AnyWithExtra g = 8; + repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"]; +} + +message AnyWithExtra { + google.protobuf.Any a = 1 [(gogoproto.embed) = true]; + int64 b = 3; + int64 c = 4; +} + +message TestUpdatedTxRaw { + bytes body_bytes = 1; + bytes auth_info_bytes = 2; + repeated bytes signatures = 3; + bytes new_field_5 = 5; + bytes new_field_1024 = 1024; +} + +message TestUpdatedTxBody { + repeated google.protobuf.Any messages = 1; + string memo = 2; + int64 timeout_height = 3; + uint64 some_new_field = 4; + string some_new_field_non_critical_field = 1050; + repeated google.protobuf.Any extension_options = 1023; + repeated google.protobuf.Any non_critical_extension_options = 2047; +} + +message TestUpdatedAuthInfo { + repeated cosmos.tx.v1beta1.SignerInfo signer_infos = 1; + cosmos.tx.v1beta1.Fee fee = 2; + bytes new_field_3 = 3; + bytes new_field_1024 = 1024; +} + +message TestRepeatedUints { + repeated uint64 nums = 1; +} diff --git a/third_party/proto/confio/proofs.proto b/third_party/proto/confio/proofs.proto new file mode 100644 index 000000000000..da43503ecbd6 --- /dev/null +++ b/third_party/proto/confio/proofs.proto @@ -0,0 +1,234 @@ +syntax = "proto3"; + +package ics23; +option go_package = "github.com/confio/ics23/go"; + +enum HashOp { + // NO_HASH is the default if no data passed. Note this is an illegal argument some places. + NO_HASH = 0; + SHA256 = 1; + SHA512 = 2; + KECCAK = 3; + RIPEMD160 = 4; + BITCOIN = 5; // ripemd160(sha256(x)) +} + +/** +LengthOp defines how to process the key and value of the LeafOp +to include length information. After encoding the length with the given +algorithm, the length will be prepended to the key and value bytes. +(Each one with it's own encoded length) +*/ +enum LengthOp { + // NO_PREFIX don't include any length info + NO_PREFIX = 0; + // VAR_PROTO uses protobuf (and go-amino) varint encoding of the length + VAR_PROTO = 1; + // VAR_RLP uses rlp int encoding of the length + VAR_RLP = 2; + // FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer + FIXED32_BIG = 3; + // FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer + FIXED32_LITTLE = 4; + // FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer + FIXED64_BIG = 5; + // FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer + FIXED64_LITTLE = 6; + // REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output) + REQUIRE_32_BYTES = 7; + // REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output) + REQUIRE_64_BYTES = 8; +} + +/** +ExistenceProof takes a key and a value and a set of steps to perform on it. +The result of peforming all these steps will provide a "root hash", which can +be compared to the value in a header. + +Since it is computationally infeasible to produce a hash collission for any of the used +cryptographic hash functions, if someone can provide a series of operations to transform +a given key and value into a root hash that matches some trusted root, these key and values +must be in the referenced merkle tree. + +The only possible issue is maliablity in LeafOp, such as providing extra prefix data, +which should be controlled by a spec. Eg. with lengthOp as NONE, + prefix = FOO, key = BAR, value = CHOICE +and + prefix = F, key = OOBAR, value = CHOICE +would produce the same value. + +With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field +in the ProofSpec is valuable to prevent this mutability. And why all trees should +length-prefix the data before hashing it. +*/ +message ExistenceProof { + bytes key = 1; + bytes value = 2; + LeafOp leaf = 3; + repeated InnerOp path = 4; +} + +/* +NonExistenceProof takes a proof of two neighbors, one left of the desired key, +one right of the desired key. If both proofs are valid AND they are neighbors, +then there is no valid proof for the given key. +*/ +message NonExistenceProof { + bytes key = 1; // TODO: remove this as unnecessary??? we prove a range + ExistenceProof left = 2; + ExistenceProof right = 3; +} + +/* +CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages +*/ +message CommitmentProof { + oneof proof { + ExistenceProof exist = 1; + NonExistenceProof nonexist = 2; + BatchProof batch = 3; + CompressedBatchProof compressed = 4; + } +} + +/** +LeafOp represents the raw key-value data we wish to prove, and +must be flexible to represent the internal transformation from +the original key-value pairs into the basis hash, for many existing +merkle trees. + +key and value are passed in. So that the signature of this operation is: + leafOp(key, value) -> output + +To process this, first prehash the keys and values if needed (ANY means no hash in this case): + hkey = prehashKey(key) + hvalue = prehashValue(value) + +Then combine the bytes, and hash it + output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue) +*/ +message LeafOp { + HashOp hash = 1; + HashOp prehash_key = 2; + HashOp prehash_value = 3; + LengthOp length = 4; + // prefix is a fixed bytes that may optionally be included at the beginning to differentiate + // a leaf node from an inner node. + bytes prefix = 5; +} + +/** +InnerOp represents a merkle-proof step that is not a leaf. +It represents concatenating two children and hashing them to provide the next result. + +The result of the previous step is passed in, so the signature of this op is: + innerOp(child) -> output + +The result of applying InnerOp should be: + output = op.hash(op.prefix || child || op.suffix) + + where the || operator is concatenation of binary data, +and child is the result of hashing all the tree below this step. + +Any special data, like prepending child with the length, or prepending the entire operation with +some value to differentiate from leaf nodes, should be included in prefix and suffix. +If either of prefix or suffix is empty, we just treat it as an empty string +*/ +message InnerOp { + HashOp hash = 1; + bytes prefix = 2; + bytes suffix = 3; +} + + +/** +ProofSpec defines what the expected parameters are for a given proof type. +This can be stored in the client and used to validate any incoming proofs. + + verify(ProofSpec, Proof) -> Proof | Error + +As demonstrated in tests, if we don't fix the algorithm used to calculate the +LeafHash for a given tree, there are many possible key-value pairs that can +generate a given hash (by interpretting the preimage differently). +We need this for proper security, requires client knows a priori what +tree format server uses. But not in code, rather a configuration object. +*/ +message ProofSpec { + // any field in the ExistenceProof must be the same as in this spec. + // except Prefix, which is just the first bytes of prefix (spec can be longer) + LeafOp leaf_spec = 1; + InnerSpec inner_spec = 2; + // max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries) + int32 max_depth = 3; + // min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries) + int32 min_depth = 4; +} + +/* +InnerSpec contains all store-specific structure info to determine if two proofs from a +given store are neighbors. + +This enables: + + isLeftMost(spec: InnerSpec, op: InnerOp) + isRightMost(spec: InnerSpec, op: InnerOp) + isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp) +*/ +message InnerSpec { + // Child order is the ordering of the children node, must count from 0 + // iavl tree is [0, 1] (left then right) + // merk is [0, 2, 1] (left, right, here) + repeated int32 child_order = 1; + int32 child_size = 2; + int32 min_prefix_length = 3; + int32 max_prefix_length = 4; + // empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0) + bytes empty_child = 5; + // hash is the algorithm that must be used for each InnerOp + HashOp hash = 6; +} + +/* +BatchProof is a group of multiple proof types than can be compressed +*/ +message BatchProof { + repeated BatchEntry entries = 1; +} + +// Use BatchEntry not CommitmentProof, to avoid recursion +message BatchEntry { + oneof proof { + ExistenceProof exist = 1; + NonExistenceProof nonexist = 2; + } +} + + +/****** all items here are compressed forms *******/ + +message CompressedBatchProof { + repeated CompressedBatchEntry entries = 1; + repeated InnerOp lookup_inners = 2; +} + +// Use BatchEntry not CommitmentProof, to avoid recursion +message CompressedBatchEntry { + oneof proof { + CompressedExistenceProof exist = 1; + CompressedNonExistenceProof nonexist = 2; + } +} + +message CompressedExistenceProof { + bytes key = 1; + bytes value = 2; + LeafOp leaf = 3; + // these are indexes into the lookup_inners table in CompressedBatchProof + repeated int32 path = 4; +} + +message CompressedNonExistenceProof { + bytes key = 1; // TODO: remove this as unnecessary??? we prove a range + CompressedExistenceProof left = 2; + CompressedExistenceProof right = 3; +} diff --git a/third_party/proto/cosmos_proto/cosmos.proto b/third_party/proto/cosmos_proto/cosmos.proto new file mode 100644 index 000000000000..167b170757bc --- /dev/null +++ b/third_party/proto/cosmos_proto/cosmos.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos_proto; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/regen-network/cosmos-proto"; + +extend google.protobuf.MessageOptions { + string interface_type = 93001; + + string implements_interface = 93002; +} + +extend google.protobuf.FieldOptions { + string accepts_interface = 93001; +} diff --git a/third_party/proto/gogoproto/gogo.proto b/third_party/proto/gogoproto/gogo.proto new file mode 100644 index 000000000000..49e78f99fe57 --- /dev/null +++ b/third_party/proto/gogoproto/gogo.proto @@ -0,0 +1,145 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; +package gogoproto; + +import "google/protobuf/descriptor.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "GoGoProtos"; +option go_package = "github.com/gogo/protobuf/gogoproto"; + +extend google.protobuf.EnumOptions { + optional bool goproto_enum_prefix = 62001; + optional bool goproto_enum_stringer = 62021; + optional bool enum_stringer = 62022; + optional string enum_customname = 62023; + optional bool enumdecl = 62024; +} + +extend google.protobuf.EnumValueOptions { + optional string enumvalue_customname = 66001; +} + +extend google.protobuf.FileOptions { + optional bool goproto_getters_all = 63001; + optional bool goproto_enum_prefix_all = 63002; + optional bool goproto_stringer_all = 63003; + optional bool verbose_equal_all = 63004; + optional bool face_all = 63005; + optional bool gostring_all = 63006; + optional bool populate_all = 63007; + optional bool stringer_all = 63008; + optional bool onlyone_all = 63009; + + optional bool equal_all = 63013; + optional bool description_all = 63014; + optional bool testgen_all = 63015; + optional bool benchgen_all = 63016; + optional bool marshaler_all = 63017; + optional bool unmarshaler_all = 63018; + optional bool stable_marshaler_all = 63019; + + optional bool sizer_all = 63020; + + optional bool goproto_enum_stringer_all = 63021; + optional bool enum_stringer_all = 63022; + + optional bool unsafe_marshaler_all = 63023; + optional bool unsafe_unmarshaler_all = 63024; + + optional bool goproto_extensions_map_all = 63025; + optional bool goproto_unrecognized_all = 63026; + optional bool gogoproto_import = 63027; + optional bool protosizer_all = 63028; + optional bool compare_all = 63029; + optional bool typedecl_all = 63030; + optional bool enumdecl_all = 63031; + + optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; +} + +extend google.protobuf.MessageOptions { + optional bool goproto_getters = 64001; + optional bool goproto_stringer = 64003; + optional bool verbose_equal = 64004; + optional bool face = 64005; + optional bool gostring = 64006; + optional bool populate = 64007; + optional bool stringer = 67008; + optional bool onlyone = 64009; + + optional bool equal = 64013; + optional bool description = 64014; + optional bool testgen = 64015; + optional bool benchgen = 64016; + optional bool marshaler = 64017; + optional bool unmarshaler = 64018; + optional bool stable_marshaler = 64019; + + optional bool sizer = 64020; + + optional bool unsafe_marshaler = 64023; + optional bool unsafe_unmarshaler = 64024; + + optional bool goproto_extensions_map = 64025; + optional bool goproto_unrecognized = 64026; + + optional bool protosizer = 64028; + optional bool compare = 64029; + + optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; +} + +extend google.protobuf.FieldOptions { + optional bool nullable = 65001; + optional bool embed = 65002; + optional string customtype = 65003; + optional string customname = 65004; + optional string jsontag = 65005; + optional string moretags = 65006; + optional string casttype = 65007; + optional string castkey = 65008; + optional string castvalue = 65009; + + optional bool stdtime = 65010; + optional bool stdduration = 65011; + optional bool wktpointer = 65012; + + optional string castrepeated = 65013; +} diff --git a/third_party/proto/google/api/annotations.proto b/third_party/proto/google/api/annotations.proto new file mode 100644 index 000000000000..85c361b47fed --- /dev/null +++ b/third_party/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/third_party/proto/google/api/http.proto b/third_party/proto/google/api/http.proto new file mode 100644 index 000000000000..2bd3a19bfa54 --- /dev/null +++ b/third_party/proto/google/api/http.proto @@ -0,0 +1,318 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parmeters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// `HttpRule` defines the mapping of an RPC method to one or more HTTP +// REST API methods. The mapping specifies how different portions of the RPC +// request message are mapped to URL path, URL query parameters, and +// HTTP request body. The mapping is typically specified as an +// `google.api.http` annotation on the RPC method, +// see "google/api/annotations.proto" for details. +// +// The mapping consists of a field specifying the path template and +// method kind. The path template can refer to fields in the request +// message, as in the example below which describes a REST GET +// operation on a resource collection of messages: +// +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // mapped to the URL +// SubMessage sub = 2; // `sub.subfield` is url-mapped +// } +// message Message { +// string text = 1; // content of the resource +// } +// +// The same http annotation can alternatively be expressed inside the +// `GRPC API Configuration` YAML file. +// +// http: +// rules: +// - selector: .Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// This definition enables an automatic, bidrectional mapping of HTTP +// JSON to RPC. Example: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` +// +// In general, not only fields but also field paths can be referenced +// from a path pattern. Fields mapped to the path pattern cannot be +// repeated and must have a primitive (non-message) type. +// +// Any fields in the request message which are not bound by the path +// pattern automatically become (optional) HTTP query +// parameters. Assume the following definition of the request message: +// +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http).get = "/v1/messages/{message_id}"; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // mapped to the URL +// int64 revision = 2; // becomes a parameter +// SubMessage sub = 3; // `sub.subfield` becomes a parameter +// } +// +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` +// +// Note that fields which are mapped to HTTP parameters must have a +// primitive type or a repeated primitive type. Message types are not +// allowed. In the case of a repeated type, the parameter can be +// repeated in the URL, as in `...?param=A¶m=B`. +// +// For HTTP method kinds which allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// put: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | RPC +// -----|----- +// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// put: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | RPC +// -----|----- +// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice of +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// +// This enables the following two alternative HTTP JSON to RPC +// mappings: +// +// HTTP | RPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` +// +// # Rules for HTTP mapping +// +// The rules for mapping HTTP path, query parameters, and body fields +// to the request message are as follows: +// +// 1. The `body` field specifies either `*` or a field path, or is +// omitted. If omitted, it indicates there is no HTTP request body. +// 2. Leaf fields (recursive expansion of nested messages in the +// request) can be classified into three types: +// (a) Matched in the URL template. +// (b) Covered by body (if body is `*`, everything except (a) fields; +// else everything under the body field) +// (c) All other fields. +// 3. URL query parameters found in the HTTP request are mapped to (c) fields. +// 4. Any body sent with an HTTP request can contain only (b) fields. +// +// The syntax of the path template is as follows: +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single path segment. The syntax `**` matches zero +// or more path segments, which must be the last part of the path except the +// `Verb`. The syntax `LITERAL` matches literal text in the path. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path, all characters +// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the +// Discovery Document as `{var}`. +// +// If a variable contains one or more path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path, all +// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables +// show up in the Discovery Document as `{+var}`. +// +// NOTE: While the single segment variable matches the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 +// Simple String Expansion, the multi segment variable **does not** match +// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. +// +// NOTE: the field paths in variables and in the `body` must not refer to +// repeated fields or map fields. +message HttpRule { + // Selects methods to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Used for listing and getting information about resources. + string get = 2; + + // Used for updating a resource. + string put = 3; + + // Used for creating a resource. + string post = 4; + + // Used for deleting a resource. + string delete = 5; + + // Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP body, or + // `*` for mapping all fields not captured by the path pattern to the HTTP + // body. NOTE: the referred field must not be a repeated field and must be + // present at the top-level of request message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // body of response. Other response fields are ignored. When + // not set, the response message will be used as HTTP body of response. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/third_party/proto/google/api/httpbody.proto b/third_party/proto/google/api/httpbody.proto new file mode 100644 index 000000000000..4428515c1209 --- /dev/null +++ b/third_party/proto/google/api/httpbody.proto @@ -0,0 +1,78 @@ +// Copyright 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) returns +// (google.protobuf.Empty); +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} \ No newline at end of file diff --git a/third_party/proto/google/protobuf/any.proto b/third_party/proto/google/protobuf/any.proto new file mode 100644 index 000000000000..1431810ea45f --- /dev/null +++ b/third_party/proto/google/protobuf/any.proto @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +import "gogoproto/gogo.proto"; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "types"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := ptypes.MarshalAny(foo) +// ... +// foo := &pb.Foo{} +// if err := ptypes.UnmarshalAny(any, foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; + + option (gogoproto.typedecl) = false; +} + +option (gogoproto.goproto_registration) = false; diff --git a/third_party/proto/tendermint/abci/types.proto b/third_party/proto/tendermint/abci/types.proto new file mode 100644 index 000000000000..2cbcabb29b33 --- /dev/null +++ b/third_party/proto/tendermint/abci/types.proto @@ -0,0 +1,407 @@ +syntax = "proto3"; +package tendermint.abci; + +option go_package = "github.com/tendermint/tendermint/abci/types"; + +// For more information on gogo.proto, see: +// https://github.com/gogo/protobuf/blob/master/extensions.md +import "tendermint/crypto/proof.proto"; +import "tendermint/types/types.proto"; +import "tendermint/crypto/keys.proto"; +import "tendermint/types/params.proto"; +import "google/protobuf/timestamp.proto"; +import "gogoproto/gogo.proto"; + +// This file is copied from http://github.com/tendermint/abci +// NOTE: When using custom types, mind the warnings. +// https://github.com/gogo/protobuf/blob/master/custom_types.md#warnings-and-issues + +//---------------------------------------- +// Request types + +message Request { + oneof value { + RequestEcho echo = 1; + RequestFlush flush = 2; + RequestInfo info = 3; + RequestSetOption set_option = 4; + RequestInitChain init_chain = 5; + RequestQuery query = 6; + RequestBeginBlock begin_block = 7; + RequestCheckTx check_tx = 8; + RequestDeliverTx deliver_tx = 9; + RequestEndBlock end_block = 10; + RequestCommit commit = 11; + RequestListSnapshots list_snapshots = 12; + RequestOfferSnapshot offer_snapshot = 13; + RequestLoadSnapshotChunk load_snapshot_chunk = 14; + RequestApplySnapshotChunk apply_snapshot_chunk = 15; + } +} + +message RequestEcho { + string message = 1; +} + +message RequestFlush {} + +message RequestInfo { + string version = 1; + uint64 block_version = 2; + uint64 p2p_version = 3; +} + +// nondeterministic +message RequestSetOption { + string key = 1; + string value = 2; +} + +message RequestInitChain { + google.protobuf.Timestamp time = 1 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + string chain_id = 2; + ConsensusParams consensus_params = 3; + repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; + bytes app_state_bytes = 5; + int64 initial_height = 6; +} + +message RequestQuery { + bytes data = 1; + string path = 2; + int64 height = 3; + bool prove = 4; +} + +message RequestBeginBlock { + bytes hash = 1; + tendermint.types.Header header = 2 [(gogoproto.nullable) = false]; + LastCommitInfo last_commit_info = 3 [(gogoproto.nullable) = false]; + repeated Evidence byzantine_validators = 4 [(gogoproto.nullable) = false]; +} + +enum CheckTxType { + NEW = 0 [(gogoproto.enumvalue_customname) = "New"]; + RECHECK = 1 [(gogoproto.enumvalue_customname) = "Recheck"]; +} + +message RequestCheckTx { + bytes tx = 1; + CheckTxType type = 2; +} + +message RequestDeliverTx { + bytes tx = 1; +} + +message RequestEndBlock { + int64 height = 1; +} + +message RequestCommit {} + +// lists available snapshots +message RequestListSnapshots { +} + +// offers a snapshot to the application +message RequestOfferSnapshot { + Snapshot snapshot = 1; // snapshot offered by peers + bytes app_hash = 2; // light client-verified app hash for snapshot height +} + +// loads a snapshot chunk +message RequestLoadSnapshotChunk { + uint64 height = 1; + uint32 format = 2; + uint32 chunk = 3; +} + +// Applies a snapshot chunk +message RequestApplySnapshotChunk { + uint32 index = 1; + bytes chunk = 2; + string sender = 3; +} + +//---------------------------------------- +// Response types + +message Response { + oneof value { + ResponseException exception = 1; + ResponseEcho echo = 2; + ResponseFlush flush = 3; + ResponseInfo info = 4; + ResponseSetOption set_option = 5; + ResponseInitChain init_chain = 6; + ResponseQuery query = 7; + ResponseBeginBlock begin_block = 8; + ResponseCheckTx check_tx = 9; + ResponseDeliverTx deliver_tx = 10; + ResponseEndBlock end_block = 11; + ResponseCommit commit = 12; + ResponseListSnapshots list_snapshots = 13; + ResponseOfferSnapshot offer_snapshot = 14; + ResponseLoadSnapshotChunk load_snapshot_chunk = 15; + ResponseApplySnapshotChunk apply_snapshot_chunk = 16; + } +} + +// nondeterministic +message ResponseException { + string error = 1; +} + +message ResponseEcho { + string message = 1; +} + +message ResponseFlush {} + +message ResponseInfo { + string data = 1; + + string version = 2; + uint64 app_version = 3; + + int64 last_block_height = 4; + bytes last_block_app_hash = 5; +} + +// nondeterministic +message ResponseSetOption { + uint32 code = 1; + // bytes data = 2; + string log = 3; + string info = 4; +} + +message ResponseInitChain { + ConsensusParams consensus_params = 1; + repeated ValidatorUpdate validators = 2 [(gogoproto.nullable) = false]; + bytes app_hash = 3; +} + +message ResponseQuery { + uint32 code = 1; + // bytes data = 2; // use "value" instead. + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 index = 5; + bytes key = 6; + bytes value = 7; + tendermint.crypto.ProofOps proof_ops = 8; + int64 height = 9; + string codespace = 10; +} + +message ResponseBeginBlock { + repeated Event events = 1 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; +} + +message ResponseCheckTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5 [json_name = "gas_wanted"]; + int64 gas_used = 6 [json_name = "gas_used"]; + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; + string codespace = 8; +} + +message ResponseDeliverTx { + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5 [json_name = "gas_wanted"]; + int64 gas_used = 6 [json_name = "gas_used"]; + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; + string codespace = 8; +} + +message ResponseEndBlock { + repeated ValidatorUpdate validator_updates = 1 + [(gogoproto.nullable) = false]; + ConsensusParams consensus_param_updates = 2; + repeated Event events = 3 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; +} + +message ResponseCommit { + // reserve 1 + bytes data = 2; + int64 retain_height = 3; +} + +message ResponseListSnapshots { + repeated Snapshot snapshots = 1; +} + +message ResponseOfferSnapshot { + Result result = 1; + + enum Result { + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Snapshot accepted, apply chunks + ABORT = 2; // Abort all snapshot restoration + REJECT = 3; // Reject this specific snapshot, try others + REJECT_FORMAT = 4; // Reject all snapshots of this format, try others + REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others + } +} + +message ResponseLoadSnapshotChunk { + bytes chunk = 1; +} + +message ResponseApplySnapshotChunk { + Result result = 1; + repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply + repeated string reject_senders = 3; // Chunk senders to reject and ban + + enum Result { + UNKNOWN = 0; // Unknown result, abort all snapshot restoration + ACCEPT = 1; // Chunk successfully accepted + ABORT = 2; // Abort all snapshot restoration + RETRY = 3; // Retry chunk (combine with refetch and reject) + RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) + REJECT_SNAPSHOT = 5; // Reject this snapshot, try others + } +} + +//---------------------------------------- +// Misc. + +// ConsensusParams contains all consensus-relevant parameters +// that can be adjusted by the abci app +message ConsensusParams { + BlockParams block = 1; + tendermint.types.EvidenceParams evidence = 2; + tendermint.types.ValidatorParams validator = 3; + tendermint.types.VersionParams version = 4; +} + +// BlockParams contains limits on the block size. +message BlockParams { + // Note: must be greater than 0 + int64 max_bytes = 1; + // Note: must be greater or equal to -1 + int64 max_gas = 2; +} + +message LastCommitInfo { + int32 round = 1; + repeated VoteInfo votes = 2 [(gogoproto.nullable) = false]; +} + +// Event allows application developers to attach additional information to +// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx. +// Later, transactions may be queried using these events. +message Event { + string type = 1; + repeated EventAttribute attributes = 2 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "attributes,omitempty" + ]; +} + +// EventAttribute is a single key-value pair, associated with an event. +message EventAttribute { + bytes key = 1; + bytes value = 2; + bool index = 3; // nondeterministic +} + +// TxResult contains results of executing the transaction. +// +// One usage is indexing transaction results. +message TxResult { + int64 height = 1; + uint32 index = 2; + bytes tx = 3; + ResponseDeliverTx result = 4 [(gogoproto.nullable) = false]; +} + +//---------------------------------------- +// Blockchain Types + +// Validator +message Validator { + bytes address = 1; // The first 20 bytes of SHA256(public key) + // PubKey pub_key = 2 [(gogoproto.nullable)=false]; + int64 power = 3; // The voting power +} + +// ValidatorUpdate +message ValidatorUpdate { + tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = false]; + int64 power = 2; +} + +// VoteInfo +message VoteInfo { + Validator validator = 1 [(gogoproto.nullable) = false]; + bool signed_last_block = 2; +} + +enum EvidenceType { + UNKNOWN = 0; + DUPLICATE_VOTE = 1; + LIGHT_CLIENT_ATTACK = 2; +} + +message Evidence { + EvidenceType type = 1; + // The offending validator + Validator validator = 2 [(gogoproto.nullable) = false]; + // The height when the offense occurred + int64 height = 3; + // The corresponding time where the offense occurred + google.protobuf.Timestamp time = 4 [ + (gogoproto.nullable) = false, + (gogoproto.stdtime) = true + ]; + // Total voting power of the validator set in case the ABCI application does + // not store historical validators. + // https://github.com/tendermint/tendermint/issues/4581 + int64 total_voting_power = 5; +} + +//---------------------------------------- +// State Sync Types + +message Snapshot { + uint64 height = 1; // The height at which the snapshot was taken + uint32 format = 2; // The application-specific snapshot format + uint32 chunks = 3; // Number of chunks in the snapshot + bytes hash = 4; // Arbitrary snapshot hash, equal only if identical + bytes metadata = 5; // Arbitrary application metadata +} + +//---------------------------------------- +// Service Definition + +service ABCIApplication { + rpc Echo(RequestEcho) returns (ResponseEcho); + rpc Flush(RequestFlush) returns (ResponseFlush); + rpc Info(RequestInfo) returns (ResponseInfo); + rpc SetOption(RequestSetOption) returns (ResponseSetOption); + rpc DeliverTx(RequestDeliverTx) returns (ResponseDeliverTx); + rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx); + rpc Query(RequestQuery) returns (ResponseQuery); + rpc Commit(RequestCommit) returns (ResponseCommit); + rpc InitChain(RequestInitChain) returns (ResponseInitChain); + rpc BeginBlock(RequestBeginBlock) returns (ResponseBeginBlock); + rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); + rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); + rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); + rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); + rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); +} diff --git a/third_party/proto/tendermint/crypto/keys.proto b/third_party/proto/tendermint/crypto/keys.proto new file mode 100644 index 000000000000..16fd7adf3eef --- /dev/null +++ b/third_party/proto/tendermint/crypto/keys.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; +package tendermint.crypto; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; + +import "gogoproto/gogo.proto"; + +// PublicKey defines the keys available for use with Tendermint Validators +message PublicKey { + option (gogoproto.compare) = true; + option (gogoproto.equal) = true; + + oneof sum { + bytes ed25519 = 1; + bytes secp256k1 = 2; + } +} diff --git a/third_party/proto/tendermint/crypto/proof.proto b/third_party/proto/tendermint/crypto/proof.proto new file mode 100644 index 000000000000..975df7685397 --- /dev/null +++ b/third_party/proto/tendermint/crypto/proof.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package tendermint.crypto; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/crypto"; + +import "gogoproto/gogo.proto"; + +message Proof { + int64 total = 1; + int64 index = 2; + bytes leaf_hash = 3; + repeated bytes aunts = 4; +} + +message ValueOp { + // Encoded in ProofOp.Key. + bytes key = 1; + + // To encode in ProofOp.Data + Proof proof = 2; +} + +message DominoOp { + string key = 1; + string input = 2; + string output = 3; +} + +// ProofOp defines an operation used for calculating Merkle root +// The data could be arbitrary format, providing nessecary data +// for example neighbouring node hash +message ProofOp { + string type = 1; + bytes key = 2; + bytes data = 3; +} + +// ProofOps is Merkle proof defined by the list of ProofOps +message ProofOps { + repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; +} diff --git a/third_party/proto/tendermint/libs/bits/types.proto b/third_party/proto/tendermint/libs/bits/types.proto new file mode 100644 index 000000000000..3111d113a5b1 --- /dev/null +++ b/third_party/proto/tendermint/libs/bits/types.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package tendermint.libs.bits; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/libs/bits"; + +message BitArray { + int64 bits = 1; + repeated uint64 elems = 2; +} diff --git a/third_party/proto/tendermint/p2p/types.proto b/third_party/proto/tendermint/p2p/types.proto new file mode 100644 index 000000000000..0d42ea400299 --- /dev/null +++ b/third_party/proto/tendermint/p2p/types.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package tendermint.p2p; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/p2p"; + +import "gogoproto/gogo.proto"; + +message NetAddress { + string id = 1 [(gogoproto.customname) = "ID"]; + string ip = 2 [(gogoproto.customname) = "IP"]; + uint32 port = 3; +} + +message ProtocolVersion { + uint64 p2p = 1 [(gogoproto.customname) = "P2P"]; + uint64 block = 2; + uint64 app = 3; +} + +message DefaultNodeInfo { + ProtocolVersion protocol_version = 1 [(gogoproto.nullable) = false]; + string default_node_id = 2 [(gogoproto.customname) = "DefaultNodeID"]; + string listen_addr = 3; + string network = 4; + string version = 5; + bytes channels = 6; + string moniker = 7; + DefaultNodeInfoOther other = 8 [(gogoproto.nullable) = false]; +} + +message DefaultNodeInfoOther { + string tx_index = 1; + string rpc_address = 2 [(gogoproto.customname) = "RPCAddress"]; +} diff --git a/third_party/proto/tendermint/types/block.proto b/third_party/proto/tendermint/types/block.proto new file mode 100644 index 000000000000..84e9bb15d86a --- /dev/null +++ b/third_party/proto/tendermint/types/block.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "tendermint/types/types.proto"; +import "tendermint/types/evidence.proto"; + +message Block { + Header header = 1 [(gogoproto.nullable) = false]; + Data data = 2 [(gogoproto.nullable) = false]; + tendermint.types.EvidenceList evidence = 3 [(gogoproto.nullable) = false]; + Commit last_commit = 4; +} diff --git a/third_party/proto/tendermint/types/evidence.proto b/third_party/proto/tendermint/types/evidence.proto new file mode 100644 index 000000000000..3b234571ba67 --- /dev/null +++ b/third_party/proto/tendermint/types/evidence.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "tendermint/types/types.proto"; +import "tendermint/types/validator.proto"; + +message Evidence { + oneof sum { + DuplicateVoteEvidence duplicate_vote_evidence = 1; + LightClientAttackEvidence light_client_attack_evidence = 2; + } +} + +// DuplicateVoteEvidence contains evidence of a validator signed two conflicting votes. +message DuplicateVoteEvidence { + tendermint.types.Vote vote_a = 1; + tendermint.types.Vote vote_b = 2; + int64 total_voting_power = 3; + int64 validator_power = 4; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// LightClientAttackEvidence contains evidence of a set of validators attempting to mislead a light client. +message LightClientAttackEvidence { + tendermint.types.LightBlock conflicting_block = 1; + int64 common_height = 2; + repeated tendermint.types.Validator byzantine_validators = 3; + int64 total_voting_power = 4; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +message EvidenceList { + repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; +} diff --git a/third_party/proto/tendermint/types/params.proto b/third_party/proto/tendermint/types/params.proto new file mode 100644 index 000000000000..0de7d846fbd3 --- /dev/null +++ b/third_party/proto/tendermint/types/params.proto @@ -0,0 +1,80 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; + +option (gogoproto.equal_all) = true; + +// ConsensusParams contains consensus critical parameters that determine the +// validity of blocks. +message ConsensusParams { + BlockParams block = 1 [(gogoproto.nullable) = false]; + EvidenceParams evidence = 2 [(gogoproto.nullable) = false]; + ValidatorParams validator = 3 [(gogoproto.nullable) = false]; + VersionParams version = 4 [(gogoproto.nullable) = false]; +} + +// BlockParams contains limits on the block size. +message BlockParams { + // Max block size, in bytes. + // Note: must be greater than 0 + int64 max_bytes = 1; + // Max gas per block. + // Note: must be greater or equal to -1 + int64 max_gas = 2; + // Minimum time increment between consecutive blocks (in milliseconds) If the + // block header timestamp is ahead of the system clock, decrease this value. + // + // Not exposed to the application. + int64 time_iota_ms = 3; +} + +// EvidenceParams determine how we handle evidence of malfeasance. +message EvidenceParams { + // Max age of evidence, in blocks. + // + // The basic formula for calculating this is: MaxAgeDuration / {average block + // time}. + int64 max_age_num_blocks = 1; + + // Max age of evidence, in time. + // + // It should correspond with an app's "unbonding period" or other similar + // mechanism for handling [Nothing-At-Stake + // attacks](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQ#what-is-the-nothing-at-stake-problem-and-how-can-it-be-fixed). + google.protobuf.Duration max_age_duration = 2 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; + + // This sets the maximum size of total evidence in bytes that can be committed in a single block. + // and should fall comfortably under the max block bytes. + // Default is 1048576 or 1MB + int64 max_bytes = 3; +} + +// ValidatorParams restrict the public key types validators can use. +// NOTE: uses ABCI pubkey naming, not Amino names. +message ValidatorParams { + option (gogoproto.populate) = true; + option (gogoproto.equal) = true; + + repeated string pub_key_types = 1; +} + +// VersionParams contains the ABCI application version. +message VersionParams { + option (gogoproto.populate) = true; + option (gogoproto.equal) = true; + + uint64 app_version = 1; +} + +// HashedParams is a subset of ConsensusParams. +// +// It is hashed into the Header.ConsensusHash. +message HashedParams { + int64 block_max_bytes = 1; + int64 block_max_gas = 2; +} diff --git a/third_party/proto/tendermint/types/types.proto b/third_party/proto/tendermint/types/types.proto new file mode 100644 index 000000000000..7f7ea74cac21 --- /dev/null +++ b/third_party/proto/tendermint/types/types.proto @@ -0,0 +1,157 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "tendermint/crypto/proof.proto"; +import "tendermint/version/types.proto"; +import "tendermint/types/validator.proto"; + +// BlockIdFlag indicates which BlcokID the signature is for +enum BlockIDFlag { + option (gogoproto.goproto_enum_stringer) = true; + option (gogoproto.goproto_enum_prefix) = false; + + BLOCK_ID_FLAG_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; + BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; + BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; + BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; +} + +// SignedMsgType is a type of signed message in the consensus. +enum SignedMsgType { + option (gogoproto.goproto_enum_stringer) = true; + option (gogoproto.goproto_enum_prefix) = false; + + SIGNED_MSG_TYPE_UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "UnknownType"]; + // Votes + SIGNED_MSG_TYPE_PREVOTE = 1 [(gogoproto.enumvalue_customname) = "PrevoteType"]; + SIGNED_MSG_TYPE_PRECOMMIT = 2 [(gogoproto.enumvalue_customname) = "PrecommitType"]; + + // Proposals + SIGNED_MSG_TYPE_PROPOSAL = 32 [(gogoproto.enumvalue_customname) = "ProposalType"]; +} + +// PartsetHeader +message PartSetHeader { + uint32 total = 1; + bytes hash = 2; +} + +message Part { + uint32 index = 1; + bytes bytes = 2; + tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; +} + +// BlockID +message BlockID { + bytes hash = 1; + PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; +} + +// -------------------------------- + +// Header defines the structure of a Tendermint block header. +message Header { + // basic block info + tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; + string chain_id = 2 [(gogoproto.customname) = "ChainID"]; + int64 height = 3; + google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + + // prev block info + BlockID last_block_id = 5 [(gogoproto.nullable) = false]; + + // hashes of block data + bytes last_commit_hash = 6; // commit from validators from the last block + bytes data_hash = 7; // transactions + + // hashes from the app output from the prev block + bytes validators_hash = 8; // validators for the current block + bytes next_validators_hash = 9; // validators for the next block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block + + // consensus info + bytes evidence_hash = 13; // evidence included in the block + bytes proposer_address = 14; // original proposer of the block +} + +// Data contains the set of transactions included in the block +message Data { + // Txs that will be applied by state @ block.Height+1. + // NOTE: not all txs here are valid. We're just agreeing on the order first. + // This means that block.AppHash does not include these txs. + repeated bytes txs = 1; +} + +// Vote represents a prevote, precommit, or commit vote from validators for +// consensus. +message Vote { + SignedMsgType type = 1; + int64 height = 2; + int32 round = 3; + BlockID block_id = 4 + [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. + google.protobuf.Timestamp timestamp = 5 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes validator_address = 6; + int32 validator_index = 7; + bytes signature = 8; +} + +// Commit contains the evidence that a block was committed by a set of validators. +message Commit { + int64 height = 1; + int32 round = 2; + BlockID block_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; + repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; +} + +// CommitSig is a part of the Vote included in a Commit. +message CommitSig { + BlockIDFlag block_id_flag = 1; + bytes validator_address = 2; + google.protobuf.Timestamp timestamp = 3 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 4; +} + +message Proposal { + SignedMsgType type = 1; + int64 height = 2; + int32 round = 3; + int32 pol_round = 4; + BlockID block_id = 5 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; + google.protobuf.Timestamp timestamp = 6 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + bytes signature = 7; +} + +message SignedHeader { + Header header = 1; + Commit commit = 2; +} + +message LightBlock { + SignedHeader signed_header = 1; + tendermint.types.ValidatorSet validator_set = 2; +} + +message BlockMeta { + BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; + int64 block_size = 2; + Header header = 3 [(gogoproto.nullable) = false]; + int64 num_txs = 4; +} + +// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. +message TxProof { + bytes root_hash = 1; + bytes data = 2; + tendermint.crypto.Proof proof = 3; +} diff --git a/third_party/proto/tendermint/types/validator.proto b/third_party/proto/tendermint/types/validator.proto new file mode 100644 index 000000000000..49860b96d6b3 --- /dev/null +++ b/third_party/proto/tendermint/types/validator.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; +package tendermint.types; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; + +import "gogoproto/gogo.proto"; +import "tendermint/crypto/keys.proto"; + +message ValidatorSet { + repeated Validator validators = 1; + Validator proposer = 2; + int64 total_voting_power = 3; +} + +message Validator { + bytes address = 1; + tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; + int64 voting_power = 3; + int64 proposer_priority = 4; +} + +message SimpleValidator { + tendermint.crypto.PublicKey pub_key = 1; + int64 voting_power = 2; +} diff --git a/third_party/proto/tendermint/version/types.proto b/third_party/proto/tendermint/version/types.proto new file mode 100644 index 000000000000..6061868bd439 --- /dev/null +++ b/third_party/proto/tendermint/version/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package tendermint.version; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; + +import "gogoproto/gogo.proto"; + +// App includes the protocol and software version for the application. +// This information is included in ResponseInfo. The App.Protocol can be +// updated in ResponseEndBlock. +message App { + uint64 protocol = 1; + string software = 2; +} + +// Consensus captures the consensus rules for processing a block in the blockchain, +// including all blockchain data structures and the rules of the application's +// state transition machine. +message Consensus { + option (gogoproto.equal) = true; + + uint64 block = 1; + uint64 app = 2; +} diff --git a/types/abci.pb.go b/types/abci.pb.go new file mode 100644 index 000000000000..44b35c3536d8 --- /dev/null +++ b/types/abci.pb.go @@ -0,0 +1,3083 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/abci/v1beta1/abci.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" + types1 "github.com/tendermint/tendermint/abci/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// 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 + +// TxResponse defines a structure containing relevant tx data and metadata. The +// tags are stringified and the log is JSON decoded. +type TxResponse struct { + // The block height + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // The transaction hash. + TxHash string `protobuf:"bytes,2,opt,name=txhash,proto3" json:"txhash,omitempty"` + // Namespace for the Code + Codespace string `protobuf:"bytes,3,opt,name=codespace,proto3" json:"codespace,omitempty"` + // Response code. + Code uint32 `protobuf:"varint,4,opt,name=code,proto3" json:"code,omitempty"` + // Result bytes, if any. + Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + // The output of the application's logger (raw string). May be + // non-deterministic. + RawLog string `protobuf:"bytes,6,opt,name=raw_log,json=rawLog,proto3" json:"raw_log,omitempty"` + // The output of the application's logger (typed). May be non-deterministic. + Logs ABCIMessageLogs `protobuf:"bytes,7,rep,name=logs,proto3,castrepeated=ABCIMessageLogs" json:"logs"` + // Additional information. May be non-deterministic. + Info string `protobuf:"bytes,8,opt,name=info,proto3" json:"info,omitempty"` + // Amount of gas requested for transaction. + GasWanted int64 `protobuf:"varint,9,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` + // Amount of gas consumed by transaction. + GasUsed int64 `protobuf:"varint,10,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // The request transaction bytes. + Tx *types.Any `protobuf:"bytes,11,opt,name=tx,proto3" json:"tx,omitempty"` + // 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. + Timestamp string `protobuf:"bytes,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *TxResponse) Reset() { *m = TxResponse{} } +func (*TxResponse) ProtoMessage() {} +func (*TxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{0} +} +func (m *TxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxResponse.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 *TxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxResponse.Merge(m, src) +} +func (m *TxResponse) XXX_Size() int { + return m.Size() +} +func (m *TxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TxResponse proto.InternalMessageInfo + +// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +type ABCIMessageLog struct { + MsgIndex uint32 `protobuf:"varint,1,opt,name=msg_index,json=msgIndex,proto3" json:"msg_index,omitempty"` + Log string `protobuf:"bytes,2,opt,name=log,proto3" json:"log,omitempty"` + // Events contains a slice of Event objects that were emitted during some + // execution. + Events StringEvents `protobuf:"bytes,3,rep,name=events,proto3,castrepeated=StringEvents" json:"events"` +} + +func (m *ABCIMessageLog) Reset() { *m = ABCIMessageLog{} } +func (*ABCIMessageLog) ProtoMessage() {} +func (*ABCIMessageLog) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{1} +} +func (m *ABCIMessageLog) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ABCIMessageLog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ABCIMessageLog.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 *ABCIMessageLog) XXX_Merge(src proto.Message) { + xxx_messageInfo_ABCIMessageLog.Merge(m, src) +} +func (m *ABCIMessageLog) XXX_Size() int { + return m.Size() +} +func (m *ABCIMessageLog) XXX_DiscardUnknown() { + xxx_messageInfo_ABCIMessageLog.DiscardUnknown(m) +} + +var xxx_messageInfo_ABCIMessageLog proto.InternalMessageInfo + +func (m *ABCIMessageLog) GetMsgIndex() uint32 { + if m != nil { + return m.MsgIndex + } + return 0 +} + +func (m *ABCIMessageLog) GetLog() string { + if m != nil { + return m.Log + } + return "" +} + +func (m *ABCIMessageLog) GetEvents() StringEvents { + if m != nil { + return m.Events + } + return nil +} + +// StringEvent defines en Event object wrapper where all the attributes +// contain key/value pairs that are strings instead of raw bytes. +type StringEvent struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Attributes []Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes"` +} + +func (m *StringEvent) Reset() { *m = StringEvent{} } +func (*StringEvent) ProtoMessage() {} +func (*StringEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{2} +} +func (m *StringEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StringEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StringEvent.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 *StringEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_StringEvent.Merge(m, src) +} +func (m *StringEvent) XXX_Size() int { + return m.Size() +} +func (m *StringEvent) XXX_DiscardUnknown() { + xxx_messageInfo_StringEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_StringEvent proto.InternalMessageInfo + +func (m *StringEvent) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *StringEvent) GetAttributes() []Attribute { + if m != nil { + return m.Attributes + } + return nil +} + +// Attribute defines an attribute wrapper where the key and value are +// strings instead of raw bytes. +type Attribute struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *Attribute) Reset() { *m = Attribute{} } +func (*Attribute) ProtoMessage() {} +func (*Attribute) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{3} +} +func (m *Attribute) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Attribute.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 *Attribute) XXX_Merge(src proto.Message) { + xxx_messageInfo_Attribute.Merge(m, src) +} +func (m *Attribute) XXX_Size() int { + return m.Size() +} +func (m *Attribute) XXX_DiscardUnknown() { + xxx_messageInfo_Attribute.DiscardUnknown(m) +} + +var xxx_messageInfo_Attribute proto.InternalMessageInfo + +func (m *Attribute) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *Attribute) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +// GasInfo defines tx execution gas context. +type GasInfo struct { + // GasWanted is the maximum units of work we allow this tx to perform. + GasWanted uint64 `protobuf:"varint,1,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty" yaml:"gas_wanted"` + // GasUsed is the amount of gas actually consumed. + GasUsed uint64 `protobuf:"varint,2,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty" yaml:"gas_used"` +} + +func (m *GasInfo) Reset() { *m = GasInfo{} } +func (*GasInfo) ProtoMessage() {} +func (*GasInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{4} +} +func (m *GasInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GasInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GasInfo.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 *GasInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GasInfo.Merge(m, src) +} +func (m *GasInfo) XXX_Size() int { + return m.Size() +} +func (m *GasInfo) XXX_DiscardUnknown() { + xxx_messageInfo_GasInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_GasInfo proto.InternalMessageInfo + +func (m *GasInfo) GetGasWanted() uint64 { + if m != nil { + return m.GasWanted + } + return 0 +} + +func (m *GasInfo) GetGasUsed() uint64 { + if m != nil { + return m.GasUsed + } + return 0 +} + +// Result is the union of ResponseFormat and ResponseCheckTx. +type Result struct { + // Data is any data returned from message or handler execution. It MUST be + // length prefixed in order to separate data from multiple message executions. + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // Log contains the log information from message or handler execution. + Log string `protobuf:"bytes,2,opt,name=log,proto3" json:"log,omitempty"` + // Events contains a slice of Event objects that were emitted during message + // or handler execution. + Events []types1.Event `protobuf:"bytes,3,rep,name=events,proto3" json:"events"` +} + +func (m *Result) Reset() { *m = Result{} } +func (*Result) ProtoMessage() {} +func (*Result) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{5} +} +func (m *Result) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Result.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 *Result) XXX_Merge(src proto.Message) { + xxx_messageInfo_Result.Merge(m, src) +} +func (m *Result) XXX_Size() int { + return m.Size() +} +func (m *Result) XXX_DiscardUnknown() { + xxx_messageInfo_Result.DiscardUnknown(m) +} + +var xxx_messageInfo_Result proto.InternalMessageInfo + +// SimulationResponse defines the response generated when a transaction is +// successfully simulated. +type SimulationResponse struct { + GasInfo `protobuf:"bytes,1,opt,name=gas_info,json=gasInfo,proto3,embedded=gas_info" json:"gas_info"` + Result *Result `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` +} + +func (m *SimulationResponse) Reset() { *m = SimulationResponse{} } +func (*SimulationResponse) ProtoMessage() {} +func (*SimulationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{6} +} +func (m *SimulationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SimulationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SimulationResponse.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 *SimulationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulationResponse.Merge(m, src) +} +func (m *SimulationResponse) XXX_Size() int { + return m.Size() +} +func (m *SimulationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SimulationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulationResponse proto.InternalMessageInfo + +func (m *SimulationResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +// MsgData defines the data returned in a Result object during message +// execution. +type MsgData struct { + MsgType string `protobuf:"bytes,1,opt,name=msg_type,json=msgType,proto3" json:"msg_type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *MsgData) Reset() { *m = MsgData{} } +func (*MsgData) ProtoMessage() {} +func (*MsgData) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{7} +} +func (m *MsgData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgData.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 *MsgData) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgData.Merge(m, src) +} +func (m *MsgData) XXX_Size() int { + return m.Size() +} +func (m *MsgData) XXX_DiscardUnknown() { + xxx_messageInfo_MsgData.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgData proto.InternalMessageInfo + +func (m *MsgData) GetMsgType() string { + if m != nil { + return m.MsgType + } + return "" +} + +func (m *MsgData) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +// TxMsgData defines a list of MsgData. A transaction will have a MsgData object +// for each message. +type TxMsgData struct { + Data []*MsgData `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"` +} + +func (m *TxMsgData) Reset() { *m = TxMsgData{} } +func (*TxMsgData) ProtoMessage() {} +func (*TxMsgData) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{8} +} +func (m *TxMsgData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxMsgData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxMsgData.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 *TxMsgData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxMsgData.Merge(m, src) +} +func (m *TxMsgData) XXX_Size() int { + return m.Size() +} +func (m *TxMsgData) XXX_DiscardUnknown() { + xxx_messageInfo_TxMsgData.DiscardUnknown(m) +} + +var xxx_messageInfo_TxMsgData proto.InternalMessageInfo + +func (m *TxMsgData) GetData() []*MsgData { + if m != nil { + return m.Data + } + return nil +} + +// SearchTxsResult defines a structure for querying txs pageable +type SearchTxsResult struct { + // Count of all txs + TotalCount uint64 `protobuf:"varint,1,opt,name=total_count,json=totalCount,proto3" json:"total_count" yaml:"total_count"` + // Count of txs in current page + Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + // Index of current page, start from 1 + PageNumber uint64 `protobuf:"varint,3,opt,name=page_number,json=pageNumber,proto3" json:"page_number" yaml:"page_number"` + // Count of total pages + PageTotal uint64 `protobuf:"varint,4,opt,name=page_total,json=pageTotal,proto3" json:"page_total" yaml:"page_total"` + // Max count txs per page + Limit uint64 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` + // List of txs in current page + Txs []*TxResponse `protobuf:"bytes,6,rep,name=txs,proto3" json:"txs,omitempty"` +} + +func (m *SearchTxsResult) Reset() { *m = SearchTxsResult{} } +func (*SearchTxsResult) ProtoMessage() {} +func (*SearchTxsResult) Descriptor() ([]byte, []int) { + return fileDescriptor_4e37629bc7eb0df8, []int{9} +} +func (m *SearchTxsResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SearchTxsResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SearchTxsResult.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 *SearchTxsResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_SearchTxsResult.Merge(m, src) +} +func (m *SearchTxsResult) XXX_Size() int { + return m.Size() +} +func (m *SearchTxsResult) XXX_DiscardUnknown() { + xxx_messageInfo_SearchTxsResult.DiscardUnknown(m) +} + +var xxx_messageInfo_SearchTxsResult proto.InternalMessageInfo + +func (m *SearchTxsResult) GetTotalCount() uint64 { + if m != nil { + return m.TotalCount + } + return 0 +} + +func (m *SearchTxsResult) GetCount() uint64 { + if m != nil { + return m.Count + } + return 0 +} + +func (m *SearchTxsResult) GetPageNumber() uint64 { + if m != nil { + return m.PageNumber + } + return 0 +} + +func (m *SearchTxsResult) GetPageTotal() uint64 { + if m != nil { + return m.PageTotal + } + return 0 +} + +func (m *SearchTxsResult) GetLimit() uint64 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *SearchTxsResult) GetTxs() []*TxResponse { + if m != nil { + return m.Txs + } + return nil +} + +func init() { + proto.RegisterType((*TxResponse)(nil), "cosmos.base.abci.v1beta1.TxResponse") + proto.RegisterType((*ABCIMessageLog)(nil), "cosmos.base.abci.v1beta1.ABCIMessageLog") + proto.RegisterType((*StringEvent)(nil), "cosmos.base.abci.v1beta1.StringEvent") + proto.RegisterType((*Attribute)(nil), "cosmos.base.abci.v1beta1.Attribute") + proto.RegisterType((*GasInfo)(nil), "cosmos.base.abci.v1beta1.GasInfo") + proto.RegisterType((*Result)(nil), "cosmos.base.abci.v1beta1.Result") + proto.RegisterType((*SimulationResponse)(nil), "cosmos.base.abci.v1beta1.SimulationResponse") + proto.RegisterType((*MsgData)(nil), "cosmos.base.abci.v1beta1.MsgData") + proto.RegisterType((*TxMsgData)(nil), "cosmos.base.abci.v1beta1.TxMsgData") + proto.RegisterType((*SearchTxsResult)(nil), "cosmos.base.abci.v1beta1.SearchTxsResult") +} + +func init() { + proto.RegisterFile("cosmos/base/abci/v1beta1/abci.proto", fileDescriptor_4e37629bc7eb0df8) +} + +var fileDescriptor_4e37629bc7eb0df8 = []byte{ + // 922 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0x31, 0x73, 0x1b, 0x45, + 0x14, 0xd6, 0x49, 0xca, 0xc9, 0x7a, 0x72, 0x30, 0x2c, 0x26, 0x39, 0x27, 0xa0, 0x13, 0xe7, 0x64, + 0x46, 0x0d, 0xa7, 0x89, 0x13, 0x18, 0xc6, 0x05, 0x43, 0x2e, 0x10, 0xe2, 0x99, 0x84, 0x62, 0xad, + 0x0c, 0x33, 0x34, 0x9a, 0x95, 0xb4, 0x59, 0x1d, 0xd1, 0xdd, 0x6a, 0x6e, 0x57, 0xb6, 0xd4, 0x51, + 0x52, 0x52, 0xa5, 0xa0, 0xa2, 0xe6, 0x97, 0xa4, 0xc3, 0x65, 0x0a, 0x46, 0x80, 0xdd, 0xa5, 0xf4, + 0x2f, 0x60, 0xf6, 0xed, 0x59, 0x3a, 0x93, 0x91, 0x2b, 0xed, 0xfb, 0xde, 0xdb, 0xb7, 0x6f, 0xbf, + 0xef, 0xd3, 0x1e, 0xec, 0x0e, 0xa4, 0x4a, 0xa4, 0xea, 0xf4, 0x99, 0xe2, 0x1d, 0xd6, 0x1f, 0xc4, + 0x9d, 0xa3, 0x7b, 0x7d, 0xae, 0xd9, 0x3d, 0x0c, 0xc2, 0x49, 0x26, 0xb5, 0x24, 0x9e, 0x2d, 0x0a, + 0x4d, 0x51, 0x88, 0x78, 0x5e, 0x74, 0x6b, 0x5b, 0x48, 0x21, 0xb1, 0xa8, 0x63, 0x56, 0xb6, 0xfe, + 0xd6, 0x6d, 0xcd, 0xd3, 0x21, 0xcf, 0x92, 0x38, 0xd5, 0xb6, 0xa7, 0x9e, 0x4f, 0xb8, 0xca, 0x93, + 0x3b, 0x42, 0x4a, 0x31, 0xe6, 0x1d, 0x8c, 0xfa, 0xd3, 0x17, 0x1d, 0x96, 0xce, 0x6d, 0x2a, 0x78, + 0x55, 0x01, 0xe8, 0xce, 0x28, 0x57, 0x13, 0x99, 0x2a, 0x4e, 0x6e, 0x80, 0x3b, 0xe2, 0xb1, 0x18, + 0x69, 0xcf, 0x69, 0x39, 0xed, 0x0a, 0xcd, 0x23, 0x12, 0x80, 0xab, 0x67, 0x23, 0xa6, 0x46, 0x5e, + 0xb9, 0xe5, 0xb4, 0xeb, 0x11, 0x9c, 0x2e, 0x7c, 0xb7, 0x3b, 0x7b, 0xc2, 0xd4, 0x88, 0xe6, 0x19, + 0xf2, 0x31, 0xd4, 0x07, 0x72, 0xc8, 0xd5, 0x84, 0x0d, 0xb8, 0x57, 0x31, 0x65, 0x74, 0x05, 0x10, + 0x02, 0x55, 0x13, 0x78, 0xd5, 0x96, 0xd3, 0xbe, 0x4e, 0x71, 0x6d, 0xb0, 0x21, 0xd3, 0xcc, 0xbb, + 0x86, 0xc5, 0xb8, 0x26, 0x37, 0xa1, 0x96, 0xb1, 0xe3, 0xde, 0x58, 0x0a, 0xcf, 0x45, 0xd8, 0xcd, + 0xd8, 0xf1, 0x53, 0x29, 0xc8, 0x73, 0xa8, 0x8e, 0xa5, 0x50, 0x5e, 0xad, 0x55, 0x69, 0x37, 0xf6, + 0xda, 0xe1, 0x3a, 0x82, 0xc2, 0x87, 0xd1, 0xa3, 0x83, 0x67, 0x5c, 0x29, 0x26, 0xf8, 0x53, 0x29, + 0xa2, 0x9b, 0xaf, 0x17, 0x7e, 0xe9, 0x8f, 0xbf, 0xfd, 0xad, 0xcb, 0xb8, 0xa2, 0xd8, 0xce, 0xcc, + 0x10, 0xa7, 0x2f, 0xa4, 0xb7, 0x61, 0x67, 0x30, 0x6b, 0xf2, 0x09, 0x80, 0x60, 0xaa, 0x77, 0xcc, + 0x52, 0xcd, 0x87, 0x5e, 0x1d, 0x99, 0xa8, 0x0b, 0xa6, 0x7e, 0x40, 0x80, 0xec, 0xc0, 0x86, 0x49, + 0x4f, 0x15, 0x1f, 0x7a, 0x80, 0xc9, 0x9a, 0x60, 0xea, 0xb9, 0xe2, 0x43, 0x72, 0x07, 0xca, 0x7a, + 0xe6, 0x35, 0x5a, 0x4e, 0xbb, 0xb1, 0xb7, 0x1d, 0x5a, 0xda, 0xc3, 0x0b, 0xda, 0xc3, 0x87, 0xe9, + 0x9c, 0x96, 0xf5, 0xcc, 0x30, 0xa5, 0xe3, 0x84, 0x2b, 0xcd, 0x92, 0x89, 0xb7, 0x69, 0x99, 0x5a, + 0x02, 0xfb, 0xd5, 0x5f, 0x7e, 0xf7, 0x4b, 0xc1, 0x6f, 0x0e, 0xbc, 0x77, 0x79, 0x62, 0x72, 0x1b, + 0xea, 0x89, 0x12, 0xbd, 0x38, 0x1d, 0xf2, 0x19, 0xea, 0x73, 0x9d, 0x6e, 0x24, 0x4a, 0x1c, 0x98, + 0x98, 0xbc, 0x0f, 0x15, 0xc3, 0x19, 0xca, 0x43, 0xcd, 0x92, 0x1c, 0x82, 0xcb, 0x8f, 0x78, 0xaa, + 0x95, 0x57, 0x41, 0xca, 0xee, 0xae, 0xa7, 0xec, 0x50, 0x67, 0x71, 0x2a, 0xbe, 0x35, 0xd5, 0xd1, + 0x76, 0xce, 0xd7, 0x66, 0x01, 0x54, 0x34, 0x6f, 0xb5, 0x5f, 0xfd, 0xf9, 0xaf, 0x96, 0x13, 0x64, + 0xd0, 0x28, 0x64, 0x0d, 0x87, 0xc6, 0x6e, 0x38, 0x53, 0x9d, 0xe2, 0x9a, 0x1c, 0x00, 0x30, 0xad, + 0xb3, 0xb8, 0x3f, 0xd5, 0x5c, 0x79, 0x65, 0x9c, 0x60, 0xf7, 0x0a, 0xd1, 0x2e, 0x6a, 0xa3, 0xaa, + 0x39, 0x9f, 0x16, 0x36, 0xe7, 0x67, 0xde, 0x87, 0xfa, 0xb2, 0xc8, 0xdc, 0xf6, 0x25, 0x9f, 0xe7, + 0x07, 0x9a, 0x25, 0xd9, 0x86, 0x6b, 0x47, 0x6c, 0x3c, 0xe5, 0x39, 0x03, 0x36, 0x08, 0x24, 0xd4, + 0xbe, 0x63, 0xea, 0xc0, 0x88, 0xfa, 0xe0, 0x92, 0xa8, 0x66, 0x67, 0x35, 0xfa, 0xe8, 0x7c, 0xe1, + 0x7f, 0x30, 0x67, 0xc9, 0x78, 0x3f, 0x58, 0xe5, 0x82, 0xa2, 0xd6, 0x61, 0x41, 0xeb, 0x32, 0xee, + 0xf9, 0xf0, 0x7c, 0xe1, 0x6f, 0xad, 0xf6, 0x98, 0x4c, 0xb0, 0x34, 0x40, 0xf0, 0x13, 0xb8, 0x94, + 0xab, 0xe9, 0x58, 0x2f, 0xcd, 0x6d, 0x4e, 0xda, 0xcc, 0xcd, 0xfd, 0xae, 0x48, 0x0f, 0xfe, 0x27, + 0xd2, 0x8d, 0x70, 0xf5, 0x47, 0xb6, 0x0c, 0x59, 0x55, 0x2c, 0x2b, 0x4b, 0x15, 0xd0, 0x22, 0xaf, + 0x1c, 0x20, 0x87, 0x71, 0x32, 0x1d, 0x33, 0x1d, 0xcb, 0x74, 0xf9, 0x1f, 0x7e, 0x6c, 0x47, 0x46, + 0x57, 0x3b, 0xe8, 0xc4, 0x4f, 0xd7, 0xf3, 0x9e, 0xb3, 0x13, 0x6d, 0x98, 0xfe, 0x27, 0x0b, 0xdf, + 0xc1, 0xab, 0x20, 0x61, 0x5f, 0x82, 0x9b, 0xe1, 0x55, 0x70, 0xde, 0xc6, 0x5e, 0x6b, 0x7d, 0x17, + 0x7b, 0x65, 0x9a, 0xd7, 0x07, 0x5f, 0x41, 0xed, 0x99, 0x12, 0xdf, 0x98, 0x1b, 0xef, 0x80, 0xb1, + 0x68, 0xaf, 0x60, 0x8f, 0x5a, 0xa2, 0x44, 0xd7, 0x38, 0xe4, 0x82, 0xa0, 0xf2, 0x8a, 0xa0, 0x5c, + 0xea, 0x27, 0x50, 0xef, 0xce, 0x2e, 0x3a, 0x7c, 0xbe, 0xe4, 0xb1, 0x72, 0xf5, 0x55, 0xf2, 0x0d, + 0x97, 0x3a, 0xfd, 0x59, 0x86, 0xad, 0x43, 0xce, 0xb2, 0xc1, 0xa8, 0x3b, 0x53, 0xb9, 0x30, 0x8f, + 0xa1, 0xa1, 0xa5, 0x66, 0xe3, 0xde, 0x40, 0x4e, 0x53, 0x9d, 0x3b, 0xe1, 0xee, 0xdb, 0x85, 0x5f, + 0x84, 0xcf, 0x17, 0x3e, 0xb1, 0x22, 0x17, 0xc0, 0x80, 0x02, 0x46, 0x8f, 0x4c, 0x60, 0x1c, 0x67, + 0x3b, 0xa0, 0x2f, 0xa8, 0x0d, 0x4c, 0xf7, 0x09, 0x13, 0xbc, 0x97, 0x4e, 0x93, 0x3e, 0xcf, 0xf0, + 0x1d, 0xcc, 0xbb, 0x17, 0xe0, 0x55, 0xf7, 0x02, 0x18, 0x50, 0x30, 0xd1, 0xf7, 0x18, 0x90, 0x08, + 0x30, 0xea, 0xe1, 0x81, 0xf8, 0x6a, 0x56, 0xa3, 0xdd, 0xb7, 0x0b, 0xbf, 0x80, 0xae, 0xcc, 0xbb, + 0xc2, 0x02, 0x5a, 0x37, 0x41, 0xd7, 0xac, 0xcd, 0x84, 0xe3, 0x38, 0x89, 0x35, 0x3e, 0xb0, 0x55, + 0x6a, 0x03, 0xf2, 0x05, 0x54, 0xf4, 0x4c, 0x79, 0x2e, 0xf2, 0x79, 0x67, 0x3d, 0x9f, 0xab, 0xcf, + 0x02, 0x35, 0x1b, 0x2c, 0xa3, 0xd1, 0xd7, 0x6f, 0xfe, 0x6d, 0x96, 0x5e, 0x9f, 0x36, 0x9d, 0x93, + 0xd3, 0xa6, 0xf3, 0xcf, 0x69, 0xd3, 0xf9, 0xf5, 0xac, 0x59, 0x3a, 0x39, 0x6b, 0x96, 0xde, 0x9c, + 0x35, 0x4b, 0x3f, 0x06, 0x22, 0xd6, 0xa3, 0x69, 0x3f, 0x1c, 0xc8, 0xa4, 0x93, 0x7f, 0xe6, 0xec, + 0xcf, 0x67, 0x6a, 0xf8, 0xd2, 0x7e, 0x93, 0xfa, 0x2e, 0xbe, 0x87, 0xf7, 0xff, 0x0b, 0x00, 0x00, + 0xff, 0xff, 0xd8, 0xa9, 0x21, 0xb7, 0x08, 0x07, 0x00, 0x00, +} + +func (m *TxResponse) 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 *TxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Timestamp) > 0 { + i -= len(m.Timestamp) + copy(dAtA[i:], m.Timestamp) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Timestamp))) + i-- + dAtA[i] = 0x62 + } + if m.Tx != nil { + { + size, err := m.Tx.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + if m.GasUsed != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x50 + } + if m.GasWanted != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.GasWanted)) + i-- + dAtA[i] = 0x48 + } + if len(m.Info) > 0 { + i -= len(m.Info) + copy(dAtA[i:], m.Info) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Info))) + i-- + dAtA[i] = 0x42 + } + if len(m.Logs) > 0 { + for iNdEx := len(m.Logs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Logs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.RawLog) > 0 { + i -= len(m.RawLog) + copy(dAtA[i:], m.RawLog) + i = encodeVarintAbci(dAtA, i, uint64(len(m.RawLog))) + i-- + dAtA[i] = 0x32 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x2a + } + if m.Code != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x20 + } + if len(m.Codespace) > 0 { + i -= len(m.Codespace) + copy(dAtA[i:], m.Codespace) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Codespace))) + i-- + dAtA[i] = 0x1a + } + if len(m.TxHash) > 0 { + i -= len(m.TxHash) + copy(dAtA[i:], m.TxHash) + i = encodeVarintAbci(dAtA, i, uint64(len(m.TxHash))) + i-- + dAtA[i] = 0x12 + } + if m.Height != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ABCIMessageLog) 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 *ABCIMessageLog) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ABCIMessageLog) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Log) > 0 { + i -= len(m.Log) + copy(dAtA[i:], m.Log) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Log))) + i-- + dAtA[i] = 0x12 + } + if m.MsgIndex != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.MsgIndex)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *StringEvent) 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 *StringEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StringEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Attributes) > 0 { + for iNdEx := len(m.Attributes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Attributes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Attribute) 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 *Attribute) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Attribute) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GasInfo) 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 *GasInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GasInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.GasUsed != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x10 + } + if m.GasWanted != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.GasWanted)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Result) 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 *Result) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Result) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Log) > 0 { + i -= len(m.Log) + copy(dAtA[i:], m.Log) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Log))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SimulationResponse) 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 *SimulationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SimulationResponse) 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 = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.GasInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgData) 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 *MsgData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintAbci(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + if len(m.MsgType) > 0 { + i -= len(m.MsgType) + copy(dAtA[i:], m.MsgType) + i = encodeVarintAbci(dAtA, i, uint64(len(m.MsgType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TxMsgData) 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 *TxMsgData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxMsgData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + for iNdEx := len(m.Data) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Data[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SearchTxsResult) 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 *SearchTxsResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SearchTxsResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Txs) > 0 { + for iNdEx := len(m.Txs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Txs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAbci(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if m.Limit != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x28 + } + if m.PageTotal != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.PageTotal)) + i-- + dAtA[i] = 0x20 + } + if m.PageNumber != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.PageNumber)) + i-- + dAtA[i] = 0x18 + } + if m.Count != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.Count)) + i-- + dAtA[i] = 0x10 + } + if m.TotalCount != 0 { + i = encodeVarintAbci(dAtA, i, uint64(m.TotalCount)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintAbci(dAtA []byte, offset int, v uint64) int { + offset -= sovAbci(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovAbci(uint64(m.Height)) + } + l = len(m.TxHash) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.Codespace) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if m.Code != 0 { + n += 1 + sovAbci(uint64(m.Code)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.RawLog) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + l = len(m.Info) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if m.GasWanted != 0 { + n += 1 + sovAbci(uint64(m.GasWanted)) + } + if m.GasUsed != 0 { + n += 1 + sovAbci(uint64(m.GasUsed)) + } + if m.Tx != nil { + l = m.Tx.Size() + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.Timestamp) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + return n +} + +func (m *ABCIMessageLog) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MsgIndex != 0 { + n += 1 + sovAbci(uint64(m.MsgIndex)) + } + l = len(m.Log) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if len(m.Events) > 0 { + for _, e := range m.Events { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + return n +} + +func (m *StringEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if len(m.Attributes) > 0 { + for _, e := range m.Attributes { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + return n +} + +func (m *Attribute) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + return n +} + +func (m *GasInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GasWanted != 0 { + n += 1 + sovAbci(uint64(m.GasWanted)) + } + if m.GasUsed != 0 { + n += 1 + sovAbci(uint64(m.GasUsed)) + } + return n +} + +func (m *Result) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.Log) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + if len(m.Events) > 0 { + for _, e := range m.Events { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + return n +} + +func (m *SimulationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.GasInfo.Size() + n += 1 + l + sovAbci(uint64(l)) + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovAbci(uint64(l)) + } + return n +} + +func (m *MsgData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.MsgType) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovAbci(uint64(l)) + } + return n +} + +func (m *TxMsgData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Data) > 0 { + for _, e := range m.Data { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + return n +} + +func (m *SearchTxsResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TotalCount != 0 { + n += 1 + sovAbci(uint64(m.TotalCount)) + } + if m.Count != 0 { + n += 1 + sovAbci(uint64(m.Count)) + } + if m.PageNumber != 0 { + n += 1 + sovAbci(uint64(m.PageNumber)) + } + if m.PageTotal != 0 { + n += 1 + sovAbci(uint64(m.PageTotal)) + } + if m.Limit != 0 { + n += 1 + sovAbci(uint64(m.Limit)) + } + if len(m.Txs) > 0 { + for _, e := range m.Txs { + l = e.Size() + n += 1 + l + sovAbci(uint64(l)) + } + } + return n +} + +func sovAbci(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAbci(x uint64) (n int) { + return sovAbci(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ABCIMessageLog) String() string { + if this == nil { + return "nil" + } + repeatedStringForEvents := "[]StringEvent{" + for _, f := range this.Events { + repeatedStringForEvents += strings.Replace(strings.Replace(f.String(), "StringEvent", "StringEvent", 1), `&`, ``, 1) + "," + } + repeatedStringForEvents += "}" + s := strings.Join([]string{`&ABCIMessageLog{`, + `MsgIndex:` + fmt.Sprintf("%v", this.MsgIndex) + `,`, + `Log:` + fmt.Sprintf("%v", this.Log) + `,`, + `Events:` + repeatedStringForEvents + `,`, + `}`, + }, "") + return s +} +func (this *StringEvent) String() string { + if this == nil { + return "nil" + } + repeatedStringForAttributes := "[]Attribute{" + for _, f := range this.Attributes { + repeatedStringForAttributes += fmt.Sprintf("%v", f) + "," + } + repeatedStringForAttributes += "}" + s := strings.Join([]string{`&StringEvent{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Attributes:` + repeatedStringForAttributes + `,`, + `}`, + }, "") + return s +} +func (this *MsgData) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MsgData{`, + `MsgType:` + fmt.Sprintf("%v", this.MsgType) + `,`, + `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `}`, + }, "") + return s +} +func (this *TxMsgData) String() string { + if this == nil { + return "nil" + } + repeatedStringForData := "[]*MsgData{" + for _, f := range this.Data { + repeatedStringForData += strings.Replace(f.String(), "MsgData", "MsgData", 1) + "," + } + repeatedStringForData += "}" + s := strings.Join([]string{`&TxMsgData{`, + `Data:` + repeatedStringForData + `,`, + `}`, + }, "") + return s +} +func (this *SearchTxsResult) String() string { + if this == nil { + return "nil" + } + repeatedStringForTxs := "[]*TxResponse{" + for _, f := range this.Txs { + repeatedStringForTxs += strings.Replace(fmt.Sprintf("%v", f), "TxResponse", "TxResponse", 1) + "," + } + repeatedStringForTxs += "}" + s := strings.Join([]string{`&SearchTxsResult{`, + `TotalCount:` + fmt.Sprintf("%v", this.TotalCount) + `,`, + `Count:` + fmt.Sprintf("%v", this.Count) + `,`, + `PageNumber:` + fmt.Sprintf("%v", this.PageNumber) + `,`, + `PageTotal:` + fmt.Sprintf("%v", this.PageTotal) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `Txs:` + repeatedStringForTxs + `,`, + `}`, + }, "") + return s +} +func valueToStringAbci(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *TxResponse) 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 ErrIntOverflowAbci + } + 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: TxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Codespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Codespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + m.Code = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Code |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RawLog", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RawLog = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, ABCIMessageLog{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Info = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasWanted", wireType) + } + m.GasWanted = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasWanted |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Tx == nil { + m.Tx = &types.Any{} + } + if err := m.Tx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Timestamp = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ABCIMessageLog) 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 ErrIntOverflowAbci + } + 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: ABCIMessageLog: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ABCIMessageLog: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MsgIndex", wireType) + } + m.MsgIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MsgIndex |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Log", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Log = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Events = append(m.Events, StringEvent{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StringEvent) 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 ErrIntOverflowAbci + } + 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: StringEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StringEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Attributes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Attributes = append(m.Attributes, Attribute{}) + if err := m.Attributes[len(m.Attributes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Attribute) 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 ErrIntOverflowAbci + } + 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: Attribute: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Attribute: 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 stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GasInfo) 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 ErrIntOverflowAbci + } + 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: GasInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GasInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasWanted", wireType) + } + m.GasWanted = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasWanted |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Result) 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 ErrIntOverflowAbci + } + 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: Result: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Result: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Log", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Log = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Events = append(m.Events, types1.Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SimulationResponse) 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 ErrIntOverflowAbci + } + 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: SimulationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SimulationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.GasInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + 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 ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &Result{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgData) 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 ErrIntOverflowAbci + } + 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: MsgData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MsgType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + 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 ErrInvalidLengthAbci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MsgType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TxMsgData) 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 ErrIntOverflowAbci + } + 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: TxMsgData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxMsgData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data, &MsgData{}) + if err := m.Data[len(m.Data)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SearchTxsResult) 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 ErrIntOverflowAbci + } + 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: SearchTxsResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SearchTxsResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalCount", wireType) + } + m.TotalCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalCount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + } + m.Count = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Count |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PageNumber", wireType) + } + m.PageNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PageNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PageTotal", wireType) + } + m.PageTotal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PageTotal |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Txs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAbci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAbci + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAbci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Txs = append(m.Txs, &TxResponse{}) + if err := m.Txs[len(m.Txs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAbci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAbci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAbci(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, ErrIntOverflowAbci + } + 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, ErrIntOverflowAbci + } + 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, ErrIntOverflowAbci + } + 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, ErrInvalidLengthAbci + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAbci + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAbci + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAbci = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAbci = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAbci = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/address.go b/types/address.go index 2d6dbe8a3d3a..ba9e5b303b6a 100644 --- a/types/address.go +++ b/types/address.go @@ -8,11 +8,11 @@ import ( "fmt" "strings" - "github.com/tendermint/tendermint/crypto" - tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" yaml "gopkg.in/yaml.v2" - "github.com/tendermint/tendermint/libs/bech32" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/bech32" ) const ( @@ -30,15 +30,15 @@ const ( // AddrLen defines a valid address length AddrLen = 20 - // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address - Bech32MainPrefix = "fetch" + // Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address + Bech32MainPrefix = "cosmos" - // Atom in https://github.com/satoshilabs/slips/blob/master/slip-0044.md + // CoinType is the ATOM coin type as defined in SLIP44 (https://github.com/satoshilabs/slips/blob/master/slip-0044.md) CoinType = 118 - // BIP44Prefix is the parts of the BIP44 HD path that are fixed by - // what we used during the fundraiser. - FullFundraiserPath = "44'/118'/0'/0/0" + // FullFundraiserPath is the parts of the BIP44 HD path that are fixed by + // what we used during the ATOM fundraiser. + FullFundraiserPath = "m/44'/118'/0'/0/0" // PrefixAccount is the prefix for account keys PrefixAccount = "acc" @@ -98,16 +98,8 @@ type AccAddress []byte // AccAddressFromHex creates an AccAddress from a hex string. func AccAddressFromHex(address string) (addr AccAddress, err error) { - if len(address) == 0 { - return addr, errors.New("decoding Bech32 address failed: must provide an address") - } - - bz, err := hex.DecodeString(address) - if err != nil { - return nil, err - } - - return AccAddress(bz), nil + bz, err := addressBytesFromHexString(address) + return AccAddress(bz), err } // VerifyAddressFormat verifies that the provided bytes form a valid address @@ -119,7 +111,7 @@ func VerifyAddressFormat(bz []byte) error { return verifier(bz) } if len(bz) != AddrLen { - return errors.New("incorrect address length") + return fmt.Errorf("incorrect address length (expected: %d, actual: %d)", AddrLen, len(bz)) } return nil } @@ -127,7 +119,7 @@ func VerifyAddressFormat(bz []byte) error { // AccAddressFromBech32 creates an AccAddress from a Bech32 string. func AccAddressFromBech32(address string) (addr AccAddress, err error) { if len(strings.TrimSpace(address)) == 0 { - return AccAddress{}, nil + return AccAddress{}, errors.New("empty address string is not allowed") } bech32PrefixAccAddr := GetConfig().GetBech32AccountAddrPrefix() @@ -191,9 +183,14 @@ func (aa AccAddress) MarshalYAML() (interface{}, error) { func (aa *AccAddress) UnmarshalJSON(data []byte) error { var s string err := json.Unmarshal(data, &s) + if err != nil { return err } + if s == "" { + *aa = AccAddress{} + return nil + } aa2, err := AccAddressFromBech32(s) if err != nil { @@ -211,6 +208,10 @@ func (aa *AccAddress) UnmarshalYAML(data []byte) error { if err != nil { return err } + if s == "" { + *aa = AccAddress{} + return nil + } aa2, err := AccAddressFromBech32(s) if err != nil { @@ -265,22 +266,14 @@ type ValAddress []byte // ValAddressFromHex creates a ValAddress from a hex string. func ValAddressFromHex(address string) (addr ValAddress, err error) { - if len(address) == 0 { - return addr, errors.New("decoding Bech32 address failed: must provide an address") - } - - bz, err := hex.DecodeString(address) - if err != nil { - return nil, err - } - - return ValAddress(bz), nil + bz, err := addressBytesFromHexString(address) + return ValAddress(bz), err } // ValAddressFromBech32 creates a ValAddress from a Bech32 string. func ValAddressFromBech32(address string) (addr ValAddress, err error) { if len(strings.TrimSpace(address)) == 0 { - return ValAddress{}, nil + return ValAddress{}, errors.New("empty address string is not allowed") } bech32PrefixValAddr := GetConfig().GetBech32ValidatorAddrPrefix() @@ -348,6 +341,10 @@ func (va *ValAddress) UnmarshalJSON(data []byte) error { if err != nil { return err } + if s == "" { + *va = ValAddress{} + return nil + } va2, err := ValAddressFromBech32(s) if err != nil { @@ -366,6 +363,10 @@ func (va *ValAddress) UnmarshalYAML(data []byte) error { if err != nil { return err } + if s == "" { + *va = ValAddress{} + return nil + } va2, err := ValAddressFromBech32(s) if err != nil { @@ -420,22 +421,14 @@ type ConsAddress []byte // ConsAddressFromHex creates a ConsAddress from a hex string. func ConsAddressFromHex(address string) (addr ConsAddress, err error) { - if len(address) == 0 { - return addr, errors.New("decoding Bech32 address failed: must provide an address") - } - - bz, err := hex.DecodeString(address) - if err != nil { - return nil, err - } - - return ConsAddress(bz), nil + bz, err := addressBytesFromHexString(address) + return ConsAddress(bz), err } // ConsAddressFromBech32 creates a ConsAddress from a Bech32 string. func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { if len(strings.TrimSpace(address)) == 0 { - return ConsAddress{}, nil + return ConsAddress{}, errors.New("empty address string is not allowed") } bech32PrefixConsAddr := GetConfig().GetBech32ConsensusAddrPrefix() @@ -454,7 +447,7 @@ func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { } // get ConsAddress from pubkey -func GetConsAddress(pubkey crypto.PubKey) ConsAddress { +func GetConsAddress(pubkey cryptotypes.PubKey) ConsAddress { return ConsAddress(pubkey.Address()) } @@ -508,6 +501,10 @@ func (ca *ConsAddress) UnmarshalJSON(data []byte) error { if err != nil { return err } + if s == "" { + *ca = ConsAddress{} + return nil + } ca2, err := ConsAddressFromBech32(s) if err != nil { @@ -526,6 +523,10 @@ func (ca *ConsAddress) UnmarshalYAML(data []byte) error { if err != nil { return err } + if s == "" { + *ca = ConsAddress{} + return nil + } ca2, err := ConsAddressFromBech32(s) if err != nil { @@ -557,6 +558,30 @@ func (ca ConsAddress) String() string { return bech32Addr } +// Bech32ifyAddressBytes returns a bech32 representation of address bytes. +// Returns an empty sting if the byte slice is 0-length. Returns an error if the bech32 conversion +// fails or the prefix is empty. +func Bech32ifyAddressBytes(prefix string, bs []byte) (string, error) { + if len(bs) == 0 { + return "", nil + } + if len(prefix) == 0 { + return "", errors.New("prefix cannot be empty") + } + return bech32.ConvertAndEncode(prefix, bs) +} + +// MustBech32ifyAddressBytes returns a bech32 representation of address bytes. +// Returns an empty sting if the byte slice is 0-length. It panics if the bech32 conversion +// fails or the prefix is empty. +func MustBech32ifyAddressBytes(prefix string, bs []byte) string { + s, err := Bech32ifyAddressBytes(prefix, bs) + if err != nil { + panic(err) + } + return s +} + // Format implements the fmt.Formatter interface. // nolint: errcheck func (ca ConsAddress) Format(s fmt.State, verb rune) { @@ -586,7 +611,8 @@ const ( // Bech32ifyPubKey returns a Bech32 encoded string containing the appropriate // prefix based on the key type provided for a given PublicKey. -func Bech32ifyPubKey(pkt Bech32PubKeyType, pubkey crypto.PubKey) (string, error) { +// TODO: Remove Bech32ifyPubKey and all usages (cosmos/cosmos-sdk/issues/#7357) +func Bech32ifyPubKey(pkt Bech32PubKeyType, pubkey cryptotypes.PubKey) (string, error) { var bech32Prefix string switch pkt { @@ -601,11 +627,11 @@ func Bech32ifyPubKey(pkt Bech32PubKeyType, pubkey crypto.PubKey) (string, error) } - return bech32.ConvertAndEncode(bech32Prefix, pubkey.Bytes()) + return bech32.ConvertAndEncode(bech32Prefix, legacy.Cdc.MustMarshalBinaryBare(pubkey)) } // MustBech32ifyPubKey calls Bech32ifyPubKey except it panics on error. -func MustBech32ifyPubKey(pkt Bech32PubKeyType, pubkey crypto.PubKey) string { +func MustBech32ifyPubKey(pkt Bech32PubKeyType, pubkey cryptotypes.PubKey) string { res, err := Bech32ifyPubKey(pkt, pubkey) if err != nil { panic(err) @@ -616,7 +642,7 @@ func MustBech32ifyPubKey(pkt Bech32PubKeyType, pubkey crypto.PubKey) string { // GetPubKeyFromBech32 returns a PublicKey from a bech32-encoded PublicKey with // a given key type. -func GetPubKeyFromBech32(pkt Bech32PubKeyType, pubkeyStr string) (crypto.PubKey, error) { +func GetPubKeyFromBech32(pkt Bech32PubKeyType, pubkeyStr string) (cryptotypes.PubKey, error) { var bech32Prefix string switch pkt { @@ -636,16 +662,11 @@ func GetPubKeyFromBech32(pkt Bech32PubKeyType, pubkeyStr string) (crypto.PubKey, return nil, err } - pk, err := tmamino.PubKeyFromBytes(bz) - if err != nil { - return nil, err - } - - return pk, nil + return legacy.PubKeyFromBytes(bz) } // MustGetPubKeyFromBech32 calls GetPubKeyFromBech32 except it panics on error. -func MustGetPubKeyFromBech32(pkt Bech32PubKeyType, pubkeyStr string) crypto.PubKey { +func MustGetPubKeyFromBech32(pkt Bech32PubKeyType, pubkeyStr string) cryptotypes.PubKey { res, err := GetPubKeyFromBech32(pkt, pubkeyStr) if err != nil { panic(err) @@ -671,3 +692,11 @@ func GetFromBech32(bech32str, prefix string) ([]byte, error) { return bz, nil } + +func addressBytesFromHexString(address string) ([]byte, error) { + if len(address) == 0 { + return nil, errors.New("decoding Bech32 address failed: must provide an address") + } + + return hex.DecodeString(address) +} diff --git a/types/address_bench_test.go b/types/address_bench_test.go new file mode 100644 index 000000000000..59222dacf9b7 --- /dev/null +++ b/types/address_bench_test.go @@ -0,0 +1,50 @@ +package types_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/types" +) + +func BenchmarkBech32ifyPubKey(b *testing.B) { + pkBz := make([]byte, ed25519.PubKeySize) + pk := &ed25519.PubKey{Key: pkBz} + rng := rand.New(rand.NewSource(time.Now().Unix())) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + rng.Read(pk.Key) + b.StartTimer() + + _, err := types.Bech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pk) + require.NoError(b, err) + } +} + +func BenchmarkGetPubKeyFromBech32(b *testing.B) { + pkBz := make([]byte, ed25519.PubKeySize) + pk := &ed25519.PubKey{Key: pkBz} + rng := rand.New(rand.NewSource(time.Now().Unix())) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + b.StopTimer() + rng.Read(pk.Key) + + pkStr, err := types.Bech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pk) + require.NoError(b, err) + + b.StartTimer() + pk2, err := types.GetPubKeyFromBech32(types.Bech32PubKeyTypeConsPub, pkStr) + require.NoError(b, err) + require.Equal(b, pk, pk2) + } +} diff --git a/types/address_test.go b/types/address_test.go index 7326ab784293..5796c891acb0 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -1,6 +1,7 @@ package types_test import ( + "bytes" "encoding/hex" "fmt" "math/rand" @@ -8,14 +9,27 @@ import ( "testing" "github.com/stretchr/testify/require" - yaml "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/stretchr/testify/suite" + "gopkg.in/yaml.v2" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/types" ) +type addressTestSuite struct { + suite.Suite +} + +func TestAddressTestSuite(t *testing.T) { + suite.Run(t, new(addressTestSuite)) +} + +func (s *addressTestSuite) SetupSuite() { + s.T().Parallel() +} + var invalidStrs = []string{ "hello, world!", "0xAA", @@ -28,74 +42,75 @@ var invalidStrs = []string{ types.Bech32PrefixConsPub + "6789", } -func testMarshal(t *testing.T, original interface{}, res interface{}, marshal func() ([]byte, error), unmarshal func([]byte) error) { +func (s *addressTestSuite) testMarshal(original interface{}, res interface{}, marshal func() ([]byte, error), unmarshal func([]byte) error) { bz, err := marshal() - require.Nil(t, err) - err = unmarshal(bz) - require.Nil(t, err) - require.Equal(t, original, res) + s.Require().Nil(err) + s.Require().Nil(unmarshal(bz)) + s.Require().Equal(original, res) } -func TestEmptyAddresses(t *testing.T) { - require.Equal(t, (types.AccAddress{}).String(), "") - require.Equal(t, (types.ValAddress{}).String(), "") - require.Equal(t, (types.ConsAddress{}).String(), "") +func (s *addressTestSuite) TestEmptyAddresses() { + s.T().Parallel() + s.Require().Equal((types.AccAddress{}).String(), "") + s.Require().Equal((types.ValAddress{}).String(), "") + s.Require().Equal((types.ConsAddress{}).String(), "") accAddr, err := types.AccAddressFromBech32("") - require.True(t, accAddr.Empty()) - require.Nil(t, err) + s.Require().True(accAddr.Empty()) + s.Require().Error(err) valAddr, err := types.ValAddressFromBech32("") - require.True(t, valAddr.Empty()) - require.Nil(t, err) + s.Require().True(valAddr.Empty()) + s.Require().Error(err) consAddr, err := types.ConsAddressFromBech32("") - require.True(t, consAddr.Empty()) - require.Nil(t, err) + s.Require().True(consAddr.Empty()) + s.Require().Error(err) } -func TestRandBech32PubkeyConsistency(t *testing.T) { - var pub ed25519.PubKeyEd25519 +func (s *addressTestSuite) TestRandBech32PubkeyConsistency() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} for i := 0; i < 1000; i++ { - rand.Read(pub[:]) + rand.Read(pub.Key) mustBech32AccPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeAccPub, pub) bech32AccPub, err := types.Bech32ifyPubKey(types.Bech32PubKeyTypeAccPub, pub) - require.Nil(t, err) - require.Equal(t, bech32AccPub, mustBech32AccPub) + s.Require().Nil(err) + s.Require().Equal(bech32AccPub, mustBech32AccPub) mustBech32ValPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeValPub, pub) bech32ValPub, err := types.Bech32ifyPubKey(types.Bech32PubKeyTypeValPub, pub) - require.Nil(t, err) - require.Equal(t, bech32ValPub, mustBech32ValPub) + s.Require().Nil(err) + s.Require().Equal(bech32ValPub, mustBech32ValPub) mustBech32ConsPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pub) bech32ConsPub, err := types.Bech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pub) - require.Nil(t, err) - require.Equal(t, bech32ConsPub, mustBech32ConsPub) + s.Require().Nil(err) + s.Require().Equal(bech32ConsPub, mustBech32ConsPub) mustAccPub := types.MustGetPubKeyFromBech32(types.Bech32PubKeyTypeAccPub, bech32AccPub) accPub, err := types.GetPubKeyFromBech32(types.Bech32PubKeyTypeAccPub, bech32AccPub) - require.Nil(t, err) - require.Equal(t, accPub, mustAccPub) + s.Require().Nil(err) + s.Require().Equal(accPub, mustAccPub) mustValPub := types.MustGetPubKeyFromBech32(types.Bech32PubKeyTypeValPub, bech32ValPub) valPub, err := types.GetPubKeyFromBech32(types.Bech32PubKeyTypeValPub, bech32ValPub) - require.Nil(t, err) - require.Equal(t, valPub, mustValPub) + s.Require().Nil(err) + s.Require().Equal(valPub, mustValPub) mustConsPub := types.MustGetPubKeyFromBech32(types.Bech32PubKeyTypeConsPub, bech32ConsPub) consPub, err := types.GetPubKeyFromBech32(types.Bech32PubKeyTypeConsPub, bech32ConsPub) - require.Nil(t, err) - require.Equal(t, consPub, mustConsPub) + s.Require().Nil(err) + s.Require().Equal(consPub, mustConsPub) - require.Equal(t, valPub, accPub) - require.Equal(t, valPub, consPub) + s.Require().Equal(valPub, accPub) + s.Require().Equal(valPub, consPub) } } -func TestYAMLMarshalers(t *testing.T) { +func (s *addressTestSuite) TestYAMLMarshalers() { addr := secp256k1.GenPrivKey().PubKey().Address() acc := types.AccAddress(addr) @@ -103,118 +118,133 @@ func TestYAMLMarshalers(t *testing.T) { cons := types.ConsAddress(addr) got, _ := yaml.Marshal(&acc) - require.Equal(t, acc.String()+"\n", string(got)) + s.Require().Equal(acc.String()+"\n", string(got)) got, _ = yaml.Marshal(&val) - require.Equal(t, val.String()+"\n", string(got)) + s.Require().Equal(val.String()+"\n", string(got)) got, _ = yaml.Marshal(&cons) - require.Equal(t, cons.String()+"\n", string(got)) + s.Require().Equal(cons.String()+"\n", string(got)) } -func TestRandBech32AccAddrConsistency(t *testing.T) { - var pub ed25519.PubKeyEd25519 +func (s *addressTestSuite) TestRandBech32AccAddrConsistency() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} for i := 0; i < 1000; i++ { - rand.Read(pub[:]) + rand.Read(pub.Key) acc := types.AccAddress(pub.Address()) res := types.AccAddress{} - testMarshal(t, &acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) - testMarshal(t, &acc, &res, acc.Marshal, (&res).Unmarshal) + s.testMarshal(&acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) + s.testMarshal(&acc, &res, acc.Marshal, (&res).Unmarshal) str := acc.String() res, err := types.AccAddressFromBech32(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) str = hex.EncodeToString(acc) res, err = types.AccAddressFromHex(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) } for _, str := range invalidStrs { _, err := types.AccAddressFromHex(str) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.AccAddressFromBech32(str) - require.NotNil(t, err) + s.Require().NotNil(err) err = (*types.AccAddress)(nil).UnmarshalJSON([]byte("\"" + str + "\"")) - require.NotNil(t, err) + s.Require().NotNil(err) } + + _, err := types.AccAddressFromHex("") + s.Require().Equal("decoding Bech32 address failed: must provide an address", err.Error()) } -func TestValAddr(t *testing.T) { - var pub ed25519.PubKeyEd25519 +func (s *addressTestSuite) TestValAddr() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} for i := 0; i < 20; i++ { - rand.Read(pub[:]) + rand.Read(pub.Key) acc := types.ValAddress(pub.Address()) res := types.ValAddress{} - testMarshal(t, &acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) - testMarshal(t, &acc, &res, acc.Marshal, (&res).Unmarshal) + s.testMarshal(&acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) + s.testMarshal(&acc, &res, acc.Marshal, (&res).Unmarshal) str := acc.String() res, err := types.ValAddressFromBech32(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) str = hex.EncodeToString(acc) res, err = types.ValAddressFromHex(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) + } for _, str := range invalidStrs { _, err := types.ValAddressFromHex(str) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.ValAddressFromBech32(str) - require.NotNil(t, err) + s.Require().NotNil(err) err = (*types.ValAddress)(nil).UnmarshalJSON([]byte("\"" + str + "\"")) - require.NotNil(t, err) + s.Require().NotNil(err) } + + // test empty string + _, err := types.ValAddressFromHex("") + s.Require().Equal("decoding Bech32 address failed: must provide an address", err.Error()) } -func TestConsAddress(t *testing.T) { - var pub ed25519.PubKeyEd25519 +func (s *addressTestSuite) TestConsAddress() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} for i := 0; i < 20; i++ { - rand.Read(pub[:]) + rand.Read(pub.Key[:]) acc := types.ConsAddress(pub.Address()) res := types.ConsAddress{} - testMarshal(t, &acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) - testMarshal(t, &acc, &res, acc.Marshal, (&res).Unmarshal) + s.testMarshal(&acc, &res, acc.MarshalJSON, (&res).UnmarshalJSON) + s.testMarshal(&acc, &res, acc.Marshal, (&res).Unmarshal) str := acc.String() res, err := types.ConsAddressFromBech32(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) str = hex.EncodeToString(acc) res, err = types.ConsAddressFromHex(str) - require.Nil(t, err) - require.Equal(t, acc, res) + s.Require().Nil(err) + s.Require().Equal(acc, res) } for _, str := range invalidStrs { _, err := types.ConsAddressFromHex(str) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.ConsAddressFromBech32(str) - require.NotNil(t, err) + s.Require().NotNil(err) err = (*types.ConsAddress)(nil).UnmarshalJSON([]byte("\"" + str + "\"")) - require.NotNil(t, err) + s.Require().NotNil(err) } + + // test empty string + _, err := types.ConsAddressFromHex("") + s.Require().Equal("decoding Bech32 address failed: must provide an address", err.Error()) } const letterBytes = "abcdefghijklmnopqrstuvwxyz" @@ -227,11 +257,12 @@ func RandString(n int) string { return string(b) } -func TestConfiguredPrefix(t *testing.T) { - var pub ed25519.PubKeyEd25519 +func (s *addressTestSuite) TestConfiguredPrefix() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} for length := 1; length < 10; length++ { for times := 1; times < 20; times++ { - rand.Read(pub[:]) + rand.Read(pub.Key[:]) // Test if randomly generated prefix of a given length works prefix := RandString(length) @@ -242,12 +273,12 @@ func TestConfiguredPrefix(t *testing.T) { prefix+types.PrefixPublic) acc := types.AccAddress(pub.Address()) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( acc.String(), prefix+types.PrefixAccount), acc.String()) bech32Pub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeAccPub, pub) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( bech32Pub, prefix+types.PrefixPublic)) @@ -256,12 +287,12 @@ func TestConfiguredPrefix(t *testing.T) { prefix+types.PrefixValidator+types.PrefixPublic) val := types.ValAddress(pub.Address()) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( val.String(), prefix+types.PrefixValidator+types.PrefixAddress)) bech32ValPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeValPub, pub) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( bech32ValPub, prefix+types.PrefixValidator+types.PrefixPublic)) @@ -270,21 +301,22 @@ func TestConfiguredPrefix(t *testing.T) { prefix+types.PrefixConsensus+types.PrefixPublic) cons := types.ConsAddress(pub.Address()) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( cons.String(), prefix+types.PrefixConsensus+types.PrefixAddress)) bech32ConsPub := types.MustBech32ifyPubKey(types.Bech32PubKeyTypeConsPub, pub) - require.True(t, strings.HasPrefix( + s.Require().True(strings.HasPrefix( bech32ConsPub, prefix+types.PrefixConsensus+types.PrefixPublic)) } } } -func TestAddressInterface(t *testing.T) { - var pub ed25519.PubKeyEd25519 - rand.Read(pub[:]) +func (s *addressTestSuite) TestAddressInterface() { + pubBz := make([]byte, ed25519.PubKeySize) + pub := &ed25519.PubKey{Key: pubBz} + rand.Read(pub.Key) addrs := []types.Address{ types.ConsAddress(pub.Address()), @@ -296,21 +328,37 @@ func TestAddressInterface(t *testing.T) { switch addr := addr.(type) { case types.AccAddress: _, err := types.AccAddressFromBech32(addr.String()) - require.Nil(t, err) + s.Require().Nil(err) case types.ValAddress: _, err := types.ValAddressFromBech32(addr.String()) - require.Nil(t, err) + s.Require().Nil(err) case types.ConsAddress: _, err := types.ConsAddressFromBech32(addr.String()) - require.Nil(t, err) + s.Require().Nil(err) default: - t.Fail() + s.T().Fail() } } } -func TestCustomAddressVerifier(t *testing.T) { +func (s *addressTestSuite) TestVerifyAddressFormat() { + addr0 := make([]byte, 0) + addr5 := make([]byte, 5) + addr20 := make([]byte, 20) + addr32 := make([]byte, 32) + + err := types.VerifyAddressFormat(addr0) + s.Require().EqualError(err, "incorrect address length 0") + err = types.VerifyAddressFormat(addr5) + s.Require().EqualError(err, "incorrect address length 5") + err = types.VerifyAddressFormat(addr20) + s.Require().Nil(err) + err = types.VerifyAddressFormat(addr32) + s.Require().EqualError(err, "incorrect address length 32") +} + +func (s *addressTestSuite) TestCustomAddressVerifier() { // Create a 10 byte address addr := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} accBech := types.AccAddress(addr).String() @@ -318,13 +366,13 @@ func TestCustomAddressVerifier(t *testing.T) { consBech := types.ConsAddress(addr).String() // Verifiy that the default logic rejects this 10 byte address err := types.VerifyAddressFormat(addr) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.AccAddressFromBech32(accBech) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.ValAddressFromBech32(valBech) - require.NotNil(t, err) + s.Require().NotNil(err) _, err = types.ConsAddressFromBech32(consBech) - require.NotNil(t, err) + s.Require().NotNil(err) // Set a custom address verifier that accepts 10 or 20 byte addresses types.GetConfig().SetAddressVerifier(func(bz []byte) error { @@ -337,11 +385,138 @@ func TestCustomAddressVerifier(t *testing.T) { // Verifiy that the custom logic accepts this 10 byte address err = types.VerifyAddressFormat(addr) - require.Nil(t, err) + s.Require().Nil(err) _, err = types.AccAddressFromBech32(accBech) - require.Nil(t, err) + s.Require().Nil(err) _, err = types.ValAddressFromBech32(valBech) - require.Nil(t, err) + s.Require().Nil(err) _, err = types.ConsAddressFromBech32(consBech) - require.Nil(t, err) + s.Require().Nil(err) +} + +func (s *addressTestSuite) TestBech32ifyAddressBytes() { + 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} + type args struct { + prefix string + bs []byte + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + {"empty address", args{"prefixA", []byte{}}, "", false}, + {"empty prefix", args{"", addr20byte}, "", true}, + {"10-byte address", args{"prefixA", addr10byte}, "prefixA1qqqsyqcyq5rqwzqfwvmuzx", false}, + {"10-byte address", args{"prefixB", addr10byte}, "prefixB1qqqsyqcyq5rqwzqf4xftmx", false}, + {"20-byte address", args{"prefixA", addr20byte}, "prefixA1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn6j4npq", false}, + {"20-byte address", args{"prefixB", addr20byte}, "prefixB1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn8e9wka", false}, + } + for _, tt := range tests { + tt := tt + s.T().Run(tt.name, func(t *testing.T) { + got, err := types.Bech32ifyAddressBytes(tt.args.prefix, tt.args.bs) + if (err != nil) != tt.wantErr { + t.Errorf("Bech32ifyBytes() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.Equal(t, tt.want, got) + }) + } +} + +func (s *addressTestSuite) TestMustBech32ifyAddressBytes() { + 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} + type args struct { + prefix string + bs []byte + } + tests := []struct { + name string + args args + want string + wantPanic bool + }{ + {"empty address", args{"prefixA", []byte{}}, "", false}, + {"empty prefix", args{"", addr20byte}, "", true}, + {"10-byte address", args{"prefixA", addr10byte}, "prefixA1qqqsyqcyq5rqwzqfwvmuzx", false}, + {"10-byte address", args{"prefixB", addr10byte}, "prefixB1qqqsyqcyq5rqwzqf4xftmx", false}, + {"20-byte address", args{"prefixA", addr20byte}, "prefixA1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn6j4npq", false}, + {"20-byte address", args{"prefixB", addr20byte}, "prefixB1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn8e9wka", false}, + } + for _, tt := range tests { + tt := tt + s.T().Run(tt.name, func(t *testing.T) { + if tt.wantPanic { + require.Panics(t, func() { types.MustBech32ifyAddressBytes(tt.args.prefix, tt.args.bs) }) + return + } + require.Equal(t, tt.want, types.MustBech32ifyAddressBytes(tt.args.prefix, tt.args.bs)) + }) + } +} + +func (s *addressTestSuite) TestAddressTypesEquals() { + addr1 := secp256k1.GenPrivKey().PubKey().Address() + accAddr1 := types.AccAddress(addr1) + consAddr1 := types.ConsAddress(addr1) + valAddr1 := types.ValAddress(addr1) + + addr2 := secp256k1.GenPrivKey().PubKey().Address() + accAddr2 := types.AccAddress(addr2) + consAddr2 := types.ConsAddress(addr2) + valAddr2 := types.ValAddress(addr2) + + // equality + s.Require().True(accAddr1.Equals(accAddr1)) + s.Require().True(consAddr1.Equals(consAddr1)) + s.Require().True(valAddr1.Equals(valAddr1)) + + // emptiness + s.Require().True(types.AccAddress{}.Equals(types.AccAddress{})) + s.Require().True(types.AccAddress{}.Equals(types.AccAddress(nil))) + s.Require().True(types.AccAddress(nil).Equals(types.AccAddress{})) + s.Require().True(types.AccAddress(nil).Equals(types.AccAddress(nil))) + + s.Require().True(types.ConsAddress{}.Equals(types.ConsAddress{})) + s.Require().True(types.ConsAddress{}.Equals(types.ConsAddress(nil))) + s.Require().True(types.ConsAddress(nil).Equals(types.ConsAddress{})) + s.Require().True(types.ConsAddress(nil).Equals(types.ConsAddress(nil))) + + s.Require().True(types.ValAddress{}.Equals(types.ValAddress{})) + s.Require().True(types.ValAddress{}.Equals(types.ValAddress(nil))) + s.Require().True(types.ValAddress(nil).Equals(types.ValAddress{})) + s.Require().True(types.ValAddress(nil).Equals(types.ValAddress(nil))) + + s.Require().False(accAddr1.Equals(accAddr2)) + s.Require().Equal(accAddr1.Equals(accAddr2), accAddr2.Equals(accAddr1)) + s.Require().False(consAddr1.Equals(consAddr2)) + s.Require().Equal(consAddr1.Equals(consAddr2), consAddr2.Equals(consAddr1)) + s.Require().False(valAddr1.Equals(valAddr2)) + s.Require().Equal(valAddr1.Equals(valAddr2), valAddr2.Equals(valAddr1)) +} + +func (s *addressTestSuite) TestNilAddressTypesEmpty() { + s.Require().True(types.AccAddress(nil).Empty()) + s.Require().True(types.ConsAddress(nil).Empty()) + s.Require().True(types.ValAddress(nil).Empty()) +} + +func (s *addressTestSuite) TestGetConsAddress() { + pk := secp256k1.GenPrivKey().PubKey() + s.Require().NotEqual(types.GetConsAddress(pk), pk.Address()) + s.Require().True(bytes.Equal(types.GetConsAddress(pk).Bytes(), pk.Address().Bytes())) + s.Require().Panics(func() { types.GetConsAddress(cryptotypes.PubKey(nil)) }) +} + +func (s *addressTestSuite) TestGetFromBech32() { + _, err := types.GetFromBech32("", "prefix") + s.Require().Error(err) + s.Require().Equal("decoding Bech32 address failed: must provide an address", err.Error()) + _, err = types.GetFromBech32("cosmos1qqqsyqcyq5rqwzqfys8f67", "x") + s.Require().Error(err) + s.Require().Equal("invalid Bech32 prefix; expected x, got cosmos", err.Error()) } diff --git a/types/bech32/bech32.go b/types/bech32/bech32.go new file mode 100644 index 000000000000..22a177663eae --- /dev/null +++ b/types/bech32/bech32.go @@ -0,0 +1,32 @@ +package bech32 + +import ( + "fmt" + + "github.com/enigmampc/btcutil/bech32" +) + +// ConvertAndEncode converts from a base64 encoded byte string to base32 encoded byte string and then to bech32. +func ConvertAndEncode(hrp string, data []byte) (string, error) { + converted, err := bech32.ConvertBits(data, 8, 5, true) + if err != nil { + return "", fmt.Errorf("encoding bech32 failed: %w", err) + } + + return bech32.Encode(hrp, converted) +} + +// DecodeAndConvert decodes a bech32 encoded string and converts to base64 encoded bytes. +func DecodeAndConvert(bech string) (string, []byte, error) { + hrp, data, err := bech32.Decode(bech, 1023) + if err != nil { + return "", nil, fmt.Errorf("decoding bech32 failed: %w", err) + } + + converted, err := bech32.ConvertBits(data, 5, 8, false) + if err != nil { + return "", nil, fmt.Errorf("decoding bech32 failed: %w", err) + } + + return hrp, converted, nil +} diff --git a/types/bech32/bech32_test.go b/types/bech32/bech32_test.go new file mode 100644 index 000000000000..ba2beb783220 --- /dev/null +++ b/types/bech32/bech32_test.go @@ -0,0 +1,25 @@ +package bech32_test + +import ( + "bytes" + "crypto/sha256" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/types/bech32" +) + +func TestEncodeAndDecode(t *testing.T) { + sum := sha256.Sum256([]byte("hello world\n")) + ss := "shasum" + + bech, err := bech32.ConvertAndEncode(ss, sum[:]) + require.NoError(t, err) + + hrp, data, err := bech32.DecodeAndConvert(bech) + require.NoError(t, err) + + require.Equal(t, hrp, ss, "Invalid hrp") + require.True(t, bytes.Equal(data, sum[:]), "Invalid decode") +} diff --git a/types/bench_test.go b/types/bench_test.go new file mode 100644 index 000000000000..6c1cbc88cb09 --- /dev/null +++ b/types/bench_test.go @@ -0,0 +1,30 @@ +package types_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/types" +) + +var coinStrs = []string{ + "2000ATM", + "5000AMX", + "192XXX", + "1e9BTC", +} + +func BenchmarkParseCoin(b *testing.B) { + var blankCoin types.Coin + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for _, coinStr := range coinStrs { + coin, err := types.ParseCoinNormalized(coinStr) + if err != nil { + b.Fatal(err) + } + if coin == blankCoin { + b.Fatal("Unexpectedly returned a blank coin") + } + } + } +} diff --git a/types/bytes.go b/types/bytes.go deleted file mode 100644 index 600af9f04c39..000000000000 --- a/types/bytes.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -// copy bytes -func CopyBytes(bz []byte) (ret []byte) { - if bz == nil { - return nil - } - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} diff --git a/types/codec.go b/types/codec.go index 3d789afe9f0a..152bb9d724f5 100644 --- a/types/codec.go +++ b/types/codec.go @@ -1,9 +1,20 @@ package types -import "github.com/cosmos/cosmos-sdk/codec" +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" +) -// Register the sdk message type -func RegisterCodec(cdc *codec.Codec) { +// RegisterLegacyAminoCodec registers the sdk message type. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterInterface((*Msg)(nil), nil) cdc.RegisterInterface((*Tx)(nil), nil) } + +// RegisterInterfaces registers the sdk message type. +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterInterface("cosmos.base.v1beta1.Msg", (*Msg)(nil)) + // the interface name for MsgRequest is ServiceMsg because this is most useful for clients + // to understand - it will be the way for clients to introspect on available Msg service methods + registry.RegisterInterface("cosmos.base.v1beta1.ServiceMsg", (*MsgRequest)(nil)) +} diff --git a/types/coin.go b/types/coin.go index 98eaa6b806df..b86cff51865e 100644 --- a/types/coin.go +++ b/types/coin.go @@ -11,31 +11,19 @@ import ( //----------------------------------------------------------------------------- // Coin -// Coin hold some amount of one currency. -// -// CONTRACT: A coin will never hold a negative amount of any denomination. -// -// TODO: Make field members private for further safety. -type Coin struct { - Denom string `json:"denom"` - - // To allow the use of unsigned integers (see: #1273) a larger refactor will - // need to be made. So we use signed integers for now with safety measures in - // place preventing negative values being used. - Amount Int `json:"amount"` -} - // NewCoin returns a new coin with a denomination and amount. It will panic if -// the amount is negative. +// the amount is negative or if the denomination is invalid. func NewCoin(denom string, amount Int) Coin { - if err := validate(denom, amount); err != nil { - panic(err) - } - - return Coin{ + coin := Coin{ Denom: denom, Amount: amount, } + + if err := coin.Validate(); err != nil { + panic(err) + } + + return coin } // NewInt64Coin returns a new coin with a denomination and amount. It will panic @@ -49,26 +37,23 @@ func (coin Coin) String() string { return fmt.Sprintf("%v%v", coin.Amount, coin.Denom) } -// validate returns an error if the Coin has a negative amount or if +// Validate returns an error if the Coin has a negative amount or if // the denom is invalid. -func validate(denom string, amount Int) error { - if err := ValidateDenom(denom); err != nil { +func (coin Coin) Validate() error { + if err := ValidateDenom(coin.Denom); err != nil { return err } - if amount.IsNegative() { - return fmt.Errorf("negative coin amount: %v", amount) + if coin.Amount.IsNegative() { + return fmt.Errorf("negative coin amount: %v", coin.Amount) } return nil } -// IsValid returns true if the Coin has a non-negative amount and the denom is vaild. +// IsValid returns true if the Coin has a non-negative amount and the denom is valid. func (coin Coin) IsValid() bool { - if err := validate(coin.Denom, coin.Amount); err != nil { - return false - } - return true + return coin.Validate() == nil } // IsZero returns if this represents no money @@ -105,7 +90,7 @@ func (coin Coin) IsEqual(other Coin) bool { return coin.Amount.Equal(other.Amount) } -// Adds amounts of two coins with same denom. If the coins differ in denom then +// Add adds amounts of two coins with same denom. If the coins differ in denom then // it panics. func (coin Coin) Add(coinB Coin) Coin { if coin.Denom != coinB.Denom { @@ -115,7 +100,7 @@ func (coin Coin) Add(coinB Coin) Coin { return Coin{coin.Denom, coin.Amount.Add(coinB.Amount)} } -// Subtracts amounts of two coins with same denom. If the coins differ in denom +// Sub subtracts amounts of two coins with same denom. If the coins differ in denom // then it panics. func (coin Coin) Sub(coinB Coin) Coin { if coin.Denom != coinB.Denom { @@ -150,26 +135,24 @@ func (coin Coin) IsNegative() bool { // Coins is a set of Coin, one per currency type Coins []Coin -// NewCoins constructs a new coin set. +// NewCoins constructs a new coin set. The provided coins will be sanitized by removing +// zero coins and sorting the coin set. A panic will occur if the coin set is not valid. func NewCoins(coins ...Coin) Coins { - // remove zeroes - newCoins := removeZeroCoins(Coins(coins)) - if len(newCoins) == 0 { - return Coins{} + newCoins := sanitizeCoins(coins) + if err := newCoins.Validate(); err != nil { + panic(fmt.Errorf("invalid coin set %s: %w", newCoins, err)) } - newCoins.Sort() - - // detect duplicate Denoms - if dupIndex := findDup(newCoins); dupIndex != -1 { - panic(fmt.Errorf("find duplicate denom: %s", newCoins[dupIndex])) - } + return newCoins +} - if !newCoins.IsValid() { - panic(fmt.Errorf("invalid coin set: %s", newCoins)) +func sanitizeCoins(coins []Coin) Coins { + newCoins := removeZeroCoins(coins) + if len(newCoins) == 0 { + return Coins{} } - return newCoins + return newCoins.Sort() } type coinsJSON Coins @@ -196,43 +179,61 @@ func (coins Coins) String() string { return out[:len(out)-1] } -// IsValid asserts the Coins are sorted, have positive amount, -// and Denom does not contain upper case characters. -func (coins Coins) IsValid() bool { +// Validate checks that the Coins are sorted, have positive amount, with a valid and unique +// denomination (i.e no duplicates). Otherwise, it returns an error. +func (coins Coins) Validate() error { switch len(coins) { case 0: - return true + return nil + case 1: if err := ValidateDenom(coins[0].Denom); err != nil { - return false + return err + } + if !coins[0].IsPositive() { + return fmt.Errorf("coin %s amount is not positive", coins[0]) } - return coins[0].IsPositive() + return nil + default: // check single coin case - if !(Coins{coins[0]}).IsValid() { - return false + if err := (Coins{coins[0]}).Validate(); err != nil { + return err } lowDenom := coins[0].Denom + seenDenoms := make(map[string]bool) + seenDenoms[lowDenom] = true + for _, coin := range coins[1:] { - if strings.ToLower(coin.Denom) != coin.Denom { - return false + if seenDenoms[coin.Denom] { + return fmt.Errorf("duplicate denomination %s", coin.Denom) + } + if err := ValidateDenom(coin.Denom); err != nil { + return err } if coin.Denom <= lowDenom { - return false + return fmt.Errorf("denomination %s is not sorted", coin.Denom) } if !coin.IsPositive() { - return false + return fmt.Errorf("coin %s amount is not positive", coin.Denom) } // we compare each coin against the last denom lowDenom = coin.Denom + seenDenoms[coin.Denom] = true } - return true + return nil } } +// IsValid calls Validate and returns true when the Coins are sorted, have positive amount, with a +// valid and unique denomination (i.e no duplicates). +func (coins Coins) IsValid() bool { + return coins.Validate() == nil +} + // Add adds two sets of coins. // // e.g. @@ -477,7 +478,7 @@ func (coins Coins) Empty() bool { return len(coins) == 0 } -// Returns the amount of a denom from coins +// AmountOf returns the amount of a denom from coins func (coins Coins) AmountOf(denom string) Int { mustValidateDenom(denom) @@ -560,31 +561,32 @@ func (coins Coins) negative() Coins { // removeZeroCoins removes all zero coins from the given coin set in-place. func removeZeroCoins(coins Coins) Coins { - i, l := 0, len(coins) - for i < l { - if coins[i].IsZero() { - // remove coin - coins = append(coins[:i], coins[i+1:]...) - l-- - } else { - i++ + result := make([]Coin, 0, len(coins)) + + for _, coin := range coins { + if !coin.IsZero() { + result = append(result, coin) } } - return coins[:i] + return result } //----------------------------------------------------------------------------- // Sort interface -//nolint -func (coins Coins) Len() int { return len(coins) } +// Len implements sort.Interface for Coins +func (coins Coins) Len() int { return len(coins) } + +// Less implements sort.Interface for Coins func (coins Coins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom } -func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } + +// Swap implements sort.Interface for Coins +func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } var _ sort.Interface = Coins{} -// Sort is a helper function to sort the set of coins inplace +// Sort is a helper function to sort the set of coins in-place func (coins Coins) Sort() Coins { sort.Sort(coins) return coins @@ -594,18 +596,37 @@ func (coins Coins) Sort() Coins { // Parsing var ( - // Denominations can be 3 ~ 16 characters long. - reDnmString = `[a-z][a-z0-9]{2,15}` - reAmt = `[[:digit:]]+` - reDecAmt = `[[:digit:]]*\.[[:digit:]]+` + // Denominations can be 3 ~ 128 characters long and support letters, followed by either + // a letter, a number or a separator ('/'). + reDnmString = `[a-zA-Z][a-zA-Z0-9/]{2,127}` + reDecAmt = `[[:digit:]]+(?:\.[[:digit:]]+)?|\.[[:digit:]]+` reSpc = `[[:space:]]*` - reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDnmString)) - reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString)) - reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, reDnmString)) + reDnm *regexp.Regexp + reDecCoin *regexp.Regexp ) -// ValidateDenom validates a denomination string returning an error if it is -// invalid. +func init() { + SetCoinDenomRegex(DefaultCoinDenomRegex) +} + +// DefaultCoinDenomRegex returns the default regex string +func DefaultCoinDenomRegex() string { + return reDnmString +} + +// coinDenomRegex returns the current regex string and can be overwritten for custom validation +var coinDenomRegex = DefaultCoinDenomRegex + +// SetCoinDenomRegex allows for coin's custom validation by overriding the regular +// expression string used for denom validation. +func SetCoinDenomRegex(reFn func() string) { + coinDenomRegex = reFn + + reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, coinDenomRegex())) + reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, coinDenomRegex())) +} + +// ValidateDenom is the default validation function for Coin.Denom. func ValidateDenom(denom string) error { if !reDnm.MatchString(denom) { return fmt.Errorf("invalid denom: %s", denom) @@ -619,79 +640,31 @@ func mustValidateDenom(denom string) { } } -// ParseCoin parses a cli input for one coin type, returning errors if invalid. -// This returns an error on an empty string as well. -func ParseCoin(coinStr string) (coin Coin, err error) { - coinStr = strings.TrimSpace(coinStr) - - matches := reCoin.FindStringSubmatch(coinStr) - if matches == nil { - return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr) +// ParseCoinNormalized parses and normalize a cli input for one coin type, returning errors if invalid or on an empty string +// as well. +// Expected format: "{amount}{denomination}" +func ParseCoinNormalized(coinStr string) (coin Coin, err error) { + decCoin, err := ParseDecCoin(coinStr) + if err != nil { + return Coin{}, err } - denomStr, amountStr := matches[2], matches[1] - - amount, ok := NewIntFromString(amountStr) - if !ok { - return Coin{}, fmt.Errorf("failed to parse coin amount: %s", amountStr) - } - - if err := ValidateDenom(denomStr); err != nil { - return Coin{}, fmt.Errorf("invalid denom cannot contain upper case characters or spaces: %s", err) - } - - return NewCoin(denomStr, amount), nil -} - -// ParseCoins will parse out a list of coins separated by commas. -// If nothing is provided, it returns nil Coins. -// Returned coins are sorted. -func ParseCoins(coinsStr string) (Coins, error) { - coinsStr = strings.TrimSpace(coinsStr) - if len(coinsStr) == 0 { - return nil, nil - } - - coinStrs := strings.Split(coinsStr, ",") - coins := make(Coins, len(coinStrs)) - for i, coinStr := range coinStrs { - coin, err := ParseCoin(coinStr) - if err != nil { - return nil, err - } - - coins[i] = coin - } - - // sort coins for determinism - coins.Sort() - - // validate coins before returning - if !coins.IsValid() { - return nil, fmt.Errorf("parseCoins invalid: %#v", coins) - } - - return coins, nil -} - -type findDupDescriptor interface { - GetDenomByIndex(int) string - Len() int + coin, _ = NormalizeDecCoin(decCoin).TruncateDecimal() + return coin, nil } -// findDup works on the assumption that coins is sorted -func findDup(coins findDupDescriptor) int { - if coins.Len() <= 1 { - return -1 +// ParseCoinsNormalized will parse out a list of coins separated by commas, and normalize them by converting to smallest +// unit. If the parsing is successuful, the provided coins will be sanitized by removing zero coins and sorting the coin +// set. Lastly a validation of the coin set is executed. If the check passes, ParseCoinsNormalized will return the +// sanitized coins. +// Otherwise it will return an error. +// If an empty string is provided to ParseCoinsNormalized, it returns nil Coins. +// ParseCoinsNormalized supports decimal coins as inputs, and truncate them to int after converted to smallest unit. +// Expected format: "{amount0}{denomination},...,{amountN}{denominationN}" +func ParseCoinsNormalized(coinStr string) (Coins, error) { + coins, err := ParseDecCoins(coinStr) + if err != nil { + return Coins{}, err } - - prevDenom := coins.GetDenomByIndex(0) - for i := 1; i < coins.Len(); i++ { - if coins.GetDenomByIndex(i) == prevDenom { - return i - } - prevDenom = coins.GetDenomByIndex(i) - } - - return -1 + return NormalizeCoins(coins), nil } diff --git a/types/coin.pb.go b/types/coin.pb.go new file mode 100644 index 000000000000..df8d34d4f9d5 --- /dev/null +++ b/types/coin.pb.go @@ -0,0 +1,978 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/v1beta1/coin.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 + +// Coin defines a token with a denomination and an amount. +// +// NOTE: The amount field is an Int which implements the custom method +// signatures required by gogoproto. +type Coin struct { + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Amount Int `protobuf:"bytes,2,opt,name=amount,proto3,customtype=Int" json:"amount"` +} + +func (m *Coin) Reset() { *m = Coin{} } +func (*Coin) ProtoMessage() {} +func (*Coin) Descriptor() ([]byte, []int) { + return fileDescriptor_189a96714eafc2df, []int{0} +} +func (m *Coin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Coin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Coin.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 *Coin) XXX_Merge(src proto.Message) { + xxx_messageInfo_Coin.Merge(m, src) +} +func (m *Coin) XXX_Size() int { + return m.Size() +} +func (m *Coin) XXX_DiscardUnknown() { + xxx_messageInfo_Coin.DiscardUnknown(m) +} + +var xxx_messageInfo_Coin proto.InternalMessageInfo + +func (m *Coin) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +// DecCoin defines a token with a denomination and a decimal amount. +// +// NOTE: The amount field is an Dec which implements the custom method +// signatures required by gogoproto. +type DecCoin struct { + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Amount Dec `protobuf:"bytes,2,opt,name=amount,proto3,customtype=Dec" json:"amount"` +} + +func (m *DecCoin) Reset() { *m = DecCoin{} } +func (*DecCoin) ProtoMessage() {} +func (*DecCoin) Descriptor() ([]byte, []int) { + return fileDescriptor_189a96714eafc2df, []int{1} +} +func (m *DecCoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DecCoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DecCoin.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 *DecCoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_DecCoin.Merge(m, src) +} +func (m *DecCoin) XXX_Size() int { + return m.Size() +} +func (m *DecCoin) XXX_DiscardUnknown() { + xxx_messageInfo_DecCoin.DiscardUnknown(m) +} + +var xxx_messageInfo_DecCoin proto.InternalMessageInfo + +func (m *DecCoin) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +// IntProto defines a Protobuf wrapper around an Int object. +type IntProto struct { + Int Int `protobuf:"bytes,1,opt,name=int,proto3,customtype=Int" json:"int"` +} + +func (m *IntProto) Reset() { *m = IntProto{} } +func (*IntProto) ProtoMessage() {} +func (*IntProto) Descriptor() ([]byte, []int) { + return fileDescriptor_189a96714eafc2df, []int{2} +} +func (m *IntProto) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IntProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IntProto.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 *IntProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_IntProto.Merge(m, src) +} +func (m *IntProto) XXX_Size() int { + return m.Size() +} +func (m *IntProto) XXX_DiscardUnknown() { + xxx_messageInfo_IntProto.DiscardUnknown(m) +} + +var xxx_messageInfo_IntProto proto.InternalMessageInfo + +// DecProto defines a Protobuf wrapper around a Dec object. +type DecProto struct { + Dec Dec `protobuf:"bytes,1,opt,name=dec,proto3,customtype=Dec" json:"dec"` +} + +func (m *DecProto) Reset() { *m = DecProto{} } +func (*DecProto) ProtoMessage() {} +func (*DecProto) Descriptor() ([]byte, []int) { + return fileDescriptor_189a96714eafc2df, []int{3} +} +func (m *DecProto) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DecProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DecProto.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 *DecProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_DecProto.Merge(m, src) +} +func (m *DecProto) XXX_Size() int { + return m.Size() +} +func (m *DecProto) XXX_DiscardUnknown() { + xxx_messageInfo_DecProto.DiscardUnknown(m) +} + +var xxx_messageInfo_DecProto proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Coin)(nil), "cosmos.base.v1beta1.Coin") + proto.RegisterType((*DecCoin)(nil), "cosmos.base.v1beta1.DecCoin") + proto.RegisterType((*IntProto)(nil), "cosmos.base.v1beta1.IntProto") + proto.RegisterType((*DecProto)(nil), "cosmos.base.v1beta1.DecProto") +} + +func init() { proto.RegisterFile("cosmos/base/v1beta1/coin.proto", fileDescriptor_189a96714eafc2df) } + +var fileDescriptor_189a96714eafc2df = []byte{ + // 261 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, + 0x4f, 0xce, 0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0xc8, 0xeb, 0x81, + 0xe4, 0xf5, 0xa0, 0xf2, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x79, 0x7d, 0x10, 0x0b, 0xa2, + 0x54, 0xc9, 0x9d, 0x8b, 0xc5, 0x39, 0x3f, 0x33, 0x4f, 0x48, 0x84, 0x8b, 0x35, 0x25, 0x35, 0x2f, + 0x3f, 0x57, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc2, 0x11, 0x52, 0xe6, 0x62, 0x4b, 0xcc, + 0xcd, 0x2f, 0xcd, 0x2b, 0x91, 0x60, 0x02, 0x09, 0x3b, 0x71, 0x9f, 0xb8, 0x27, 0xcf, 0x70, 0xeb, + 0x9e, 0x3c, 0xb3, 0x67, 0x5e, 0x49, 0x10, 0x54, 0xca, 0x8a, 0xe5, 0xc5, 0x02, 0x79, 0x46, 0x25, + 0x2f, 0x2e, 0x76, 0x97, 0xd4, 0x64, 0x72, 0xcc, 0x72, 0x49, 0x4d, 0x46, 0x33, 0x4b, 0x93, 0x8b, + 0xc3, 0x33, 0xaf, 0x24, 0x00, 0xec, 0x17, 0x59, 0x2e, 0xe6, 0xcc, 0xbc, 0x12, 0x88, 0x51, 0xa8, + 0xf6, 0x83, 0xc4, 0x41, 0x4a, 0x5d, 0x52, 0x93, 0xe1, 0x4a, 0x53, 0x52, 0x93, 0xd1, 0x95, 0x82, + 0x8c, 0x07, 0x89, 0x3b, 0xb9, 0xdc, 0x78, 0x28, 0xc7, 0xd0, 0xf0, 0x48, 0x8e, 0xe1, 0xc4, 0x23, + 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, + 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x94, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, + 0x92, 0xf3, 0x73, 0xf5, 0xa1, 0x41, 0x0c, 0xa1, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x4b, 0x2a, 0x0b, + 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0xe1, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x57, 0xb3, 0x7a, + 0x93, 0x84, 0x01, 0x00, 0x00, +} + +func (this *Coin) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Coin) + if !ok { + that2, ok := that.(Coin) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Denom != that1.Denom { + return false + } + if !this.Amount.Equal(that1.Amount) { + return false + } + return true +} +func (this *DecCoin) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*DecCoin) + if !ok { + that2, ok := that.(DecCoin) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Denom != that1.Denom { + return false + } + if !this.Amount.Equal(that1.Amount) { + return false + } + return true +} +func (m *Coin) 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 *Coin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Coin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCoin(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintCoin(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DecCoin) 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 *DecCoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DecCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCoin(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintCoin(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IntProto) 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 *IntProto) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IntProto) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Int.Size() + i -= size + if _, err := m.Int.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCoin(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *DecProto) 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 *DecProto) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DecProto) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Dec.Size() + i -= size + if _, err := m.Dec.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCoin(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintCoin(dAtA []byte, offset int, v uint64) int { + offset -= sovCoin(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Coin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovCoin(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovCoin(uint64(l)) + return n +} + +func (m *DecCoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovCoin(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovCoin(uint64(l)) + return n +} + +func (m *IntProto) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Int.Size() + n += 1 + l + sovCoin(uint64(l)) + return n +} + +func (m *DecProto) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Dec.Size() + n += 1 + l + sovCoin(uint64(l)) + return n +} + +func sovCoin(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCoin(x uint64) (n int) { + return sovCoin(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Coin) 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 ErrIntOverflowCoin + } + 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: Coin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Coin: 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 ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCoin(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCoin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DecCoin) 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 ErrIntOverflowCoin + } + 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: DecCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DecCoin: 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 ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCoin(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCoin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IntProto) 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 ErrIntOverflowCoin + } + 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: IntProto: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IntProto: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Int", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Int.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCoin(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCoin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DecProto) 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 ErrIntOverflowCoin + } + 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: DecProto: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DecProto: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dec", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCoin + } + 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 ErrInvalidLengthCoin + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCoin + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Dec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCoin(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCoin + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCoin(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, ErrIntOverflowCoin + } + 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, ErrIntOverflowCoin + } + 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, ErrIntOverflowCoin + } + 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, ErrInvalidLengthCoin + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCoin + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCoin + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCoin = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCoin = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCoin = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/coin_benchmark_test.go b/types/coin_benchmark_test.go index e99120cf5a67..51df291655bf 100644 --- a/types/coin_benchmark_test.go +++ b/types/coin_benchmark_test.go @@ -5,17 +5,22 @@ import ( "testing" ) +func coinName(suffix int) string { + return fmt.Sprintf("COINZ_%d", suffix) +} + func BenchmarkCoinsAdditionIntersect(b *testing.B) { benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { return func(b *testing.B) { + b.ReportAllocs() coinsA := Coins(make([]Coin, numCoinsA)) coinsB := Coins(make([]Coin, numCoinsB)) for i := 0; i < numCoinsA; i++ { - coinsA[i] = NewCoin(fmt.Sprintf("COINZ_%d", i), NewInt(int64(i))) + coinsA[i] = NewCoin(coinName(i), NewInt(int64(i))) } for i := 0; i < numCoinsB; i++ { - coinsB[i] = NewCoin(fmt.Sprintf("COINZ_%d", i), NewInt(int64(i))) + coinsB[i] = NewCoin(coinName(i), NewInt(int64(i))) } b.ResetTimer() @@ -37,14 +42,15 @@ func BenchmarkCoinsAdditionIntersect(b *testing.B) { func BenchmarkCoinsAdditionNoIntersect(b *testing.B) { benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { return func(b *testing.B) { + b.ReportAllocs() coinsA := Coins(make([]Coin, numCoinsA)) coinsB := Coins(make([]Coin, numCoinsB)) for i := 0; i < numCoinsA; i++ { - coinsA[i] = NewCoin(fmt.Sprintf("COINZ_%d", numCoinsB + i), NewInt(int64(i))) + coinsA[i] = NewCoin(coinName(numCoinsB+i), NewInt(int64(i))) } for i := 0; i < numCoinsB; i++ { - coinsB[i] = NewCoin(fmt.Sprintf("COINZ_%d", i), NewInt(int64(i))) + coinsB[i] = NewCoin(coinName(i), NewInt(int64(i))) } b.ResetTimer() diff --git a/types/coin_test.go b/types/coin_test.go index a090deb01518..ab69f16a17db 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -1,13 +1,14 @@ -package types +package types_test import ( + "fmt" "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( @@ -15,483 +16,753 @@ var ( testDenom2 = "muon" ) +type coinTestSuite struct { + suite.Suite +} + +func TestCoinTestSuite(t *testing.T) { + suite.Run(t, new(coinTestSuite)) +} + +func (s *coinTestSuite) SetupSuite() { + s.T().Parallel() +} + // ---------------------------------------------------------------------------- // Coin tests -func TestCoin(t *testing.T) { - require.Panics(t, func() { NewInt64Coin(testDenom1, -1) }) - require.Panics(t, func() { NewCoin(testDenom1, NewInt(-1)) }) - require.Panics(t, func() { NewInt64Coin(strings.ToUpper(testDenom1), 10) }) - require.Panics(t, func() { NewCoin(strings.ToUpper(testDenom1), NewInt(10)) }) - require.Equal(t, NewInt(5), NewInt64Coin(testDenom1, 5).Amount) - require.Equal(t, NewInt(5), NewCoin(testDenom1, NewInt(5)).Amount) +func (s *coinTestSuite) TestCoin() { + s.Require().Panics(func() { sdk.NewInt64Coin(testDenom1, -1) }) + s.Require().Panics(func() { sdk.NewCoin(testDenom1, sdk.NewInt(-1)) }) + s.Require().Equal(sdk.NewInt(10), sdk.NewInt64Coin(strings.ToUpper(testDenom1), 10).Amount) + s.Require().Equal(sdk.NewInt(10), sdk.NewCoin(strings.ToUpper(testDenom1), sdk.NewInt(10)).Amount) + s.Require().Equal(sdk.NewInt(5), sdk.NewInt64Coin(testDenom1, 5).Amount) + s.Require().Equal(sdk.NewInt(5), sdk.NewCoin(testDenom1, sdk.NewInt(5)).Amount) } -func TestIsEqualCoin(t *testing.T) { +func (s *coinTestSuite) TestCoin_String() { + coin := sdk.NewCoin(testDenom1, sdk.NewInt(10)) + s.Require().Equal(fmt.Sprintf("10%s", testDenom1), coin.String()) +} + +func (s *coinTestSuite) TestIsEqualCoin() { cases := []struct { - inputOne Coin - inputTwo Coin + inputOne sdk.Coin + inputTwo sdk.Coin expected bool panics bool }{ - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, - {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), false, false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, + {sdk.NewInt64Coin("stake", 1), sdk.NewInt64Coin("stake", 10), false, false}, } for tcIndex, tc := range cases { tc := tc if tc.panics { - require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) }) } else { res := tc.inputOne.IsEqual(tc.inputTwo) - require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) } } } -func TestCoinIsValid(t *testing.T) { +func (s *coinTestSuite) TestCoinIsValid() { + loremIpsum := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra dui vel nulla aliquet, non dictum elit aliquam. Proin consequat leo in consectetur mattis. Phasellus eget odio luctus, rutrum dolor at, venenatis ante. Praesent metus erat, sodales vitae sagittis eget, commodo non ipsum. Duis eget urna quis erat mattis pulvinar. Vivamus egestas imperdiet sem, porttitor hendrerit lorem pulvinar in. Vivamus laoreet sapien eget libero euismod tristique. Suspendisse tincidunt nulla quis luctus mattis. + Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed id turpis at erat placerat fermentum id sed sapien. Fusce mattis enim id nulla viverra, eget placerat eros aliquet. Nunc fringilla urna ac condimentum ultricies. Praesent in eros ac neque fringilla sodales. Donec ut venenatis eros. Quisque iaculis lectus neque, a varius sem ullamcorper nec. Cras tincidunt dignissim libero nec volutpat. Donec molestie enim sed metus venenatis, quis elementum sem varius. Curabitur eu venenatis nulla. + Cras sit amet ligula vel turpis placerat sollicitudin. Nunc massa odio, eleifend id lacus nec, ultricies elementum arcu. Donec imperdiet nulla lacus, a venenatis lacus fermentum nec. Proin vestibulum dolor enim, vitae posuere velit aliquet non. Suspendisse pharetra condimentum nunc tincidunt viverra. Etiam posuere, ligula ut maximus congue, mauris orci consectetur velit, vel finibus eros metus non tellus. Nullam et dictum metus. Aliquam maximus fermentum mauris elementum aliquet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam dapibus lectus sed tellus rutrum tincidunt. Nulla at dolor sem. Ut non dictum arcu, eget congue sem.` + + loremIpsum = strings.ReplaceAll(loremIpsum, " ", "") + loremIpsum = strings.ReplaceAll(loremIpsum, ".", "") + loremIpsum = strings.ReplaceAll(loremIpsum, ",", "") + + cases := []struct { + coin sdk.Coin + expectPass bool + }{ + {sdk.Coin{testDenom1, sdk.NewInt(-1)}, false}, + {sdk.Coin{testDenom1, sdk.NewInt(0)}, true}, + {sdk.Coin{testDenom1, sdk.OneInt()}, true}, + {sdk.Coin{"Atom", sdk.OneInt()}, true}, + {sdk.Coin{"ATOM", sdk.OneInt()}, true}, + {sdk.Coin{"a", sdk.OneInt()}, false}, + {sdk.Coin{loremIpsum, sdk.OneInt()}, false}, + {sdk.Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()}, true}, + {sdk.Coin{"atOm", sdk.OneInt()}, true}, + {sdk.Coin{" ", sdk.OneInt()}, false}, + } + + for i, tc := range cases { + s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) + } +} + +func (s *coinTestSuite) TestCustomValidation() { + + newDnmRegex := `[\x{1F600}-\x{1F6FF}]` + sdk.SetCoinDenomRegex(func() string { + return newDnmRegex + }) + cases := []struct { - coin Coin + coin sdk.Coin expectPass bool }{ - {Coin{testDenom1, NewInt(-1)}, false}, - {Coin{testDenom1, NewInt(0)}, true}, - {Coin{testDenom1, NewInt(1)}, true}, - {Coin{"Atom", NewInt(1)}, false}, - {Coin{"a", NewInt(1)}, false}, - {Coin{"a very long coin denom", NewInt(1)}, false}, - {Coin{"atOm", NewInt(1)}, false}, - {Coin{" ", NewInt(1)}, false}, + {sdk.Coin{"🙂", sdk.NewInt(1)}, true}, + {sdk.Coin{"🙁", sdk.NewInt(1)}, true}, + {sdk.Coin{"🌶", sdk.NewInt(1)}, false}, // outside the unicode range listed above + {sdk.Coin{"asdf", sdk.NewInt(1)}, false}, + {sdk.Coin{"", sdk.NewInt(1)}, false}, } for i, tc := range cases { - require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) + s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) } + sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex) } -func TestAddCoin(t *testing.T) { +func (s *coinTestSuite) TestAddCoin() { cases := []struct { - inputOne Coin - inputTwo Coin - expected Coin + inputOne sdk.Coin + inputTwo sdk.Coin + expected sdk.Coin shouldPanic bool }{ - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 1), false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 1), false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, } for tcIndex, tc := range cases { tc := tc if tc.shouldPanic { - require.Panics(t, func() { tc.inputOne.Add(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.Add(tc.inputTwo) }) } else { res := tc.inputOne.Add(tc.inputTwo) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) } } } -func TestSubCoin(t *testing.T) { +func (s *coinTestSuite) TestSubCoin() { cases := []struct { - inputOne Coin - inputTwo Coin - expected Coin + inputOne sdk.Coin + inputTwo sdk.Coin + expected sdk.Coin shouldPanic bool }{ - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true}, - {NewInt64Coin(testDenom1, 10), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 9), false}, - {NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 3), NewInt64Coin(testDenom1, 2), false}, - {NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 5), false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 5), Coin{}, true}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, + {sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), false}, + {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 3), sdk.NewInt64Coin(testDenom1, 2), false}, + {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, true}, } for tcIndex, tc := range cases { tc := tc if tc.shouldPanic { - require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.Sub(tc.inputTwo) }) } else { res := tc.inputOne.Sub(tc.inputTwo) - require.Equal(t, tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) } } tc := struct { - inputOne Coin - inputTwo Coin + inputOne sdk.Coin + inputTwo sdk.Coin expected int64 - }{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), 0} + }{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), 0} res := tc.inputOne.Sub(tc.inputTwo) - require.Equal(t, tc.expected, res.Amount.Int64()) + s.Require().Equal(tc.expected, res.Amount.Int64()) } -func TestIsGTECoin(t *testing.T) { +func (s *coinTestSuite) TestIsGTECoin() { cases := []struct { - inputOne Coin - inputTwo Coin + inputOne sdk.Coin + inputTwo sdk.Coin expected bool panics bool }{ - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false}, - {NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), true, false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, + {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), true, false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, } for tcIndex, tc := range cases { tc := tc if tc.panics { - require.Panics(t, func() { tc.inputOne.IsGTE(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.IsGTE(tc.inputTwo) }) } else { res := tc.inputOne.IsGTE(tc.inputTwo) - require.Equal(t, tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex) } } } -func TestIsLTCoin(t *testing.T) { +func (s *coinTestSuite) TestIsLTCoin() { cases := []struct { - inputOne Coin - inputTwo Coin + inputOne sdk.Coin + inputTwo sdk.Coin expected bool panics bool }{ - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false}, - {NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), false, false}, - {NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1), false, true}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false}, - {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), true, false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, + {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), false, false}, + {sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1), false, true}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, + {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), true, false}, } for tcIndex, tc := range cases { tc := tc if tc.panics { - require.Panics(t, func() { tc.inputOne.IsLT(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.IsLT(tc.inputTwo) }) } else { res := tc.inputOne.IsLT(tc.inputTwo) - require.Equal(t, tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex) } } } -func TestCoinIsZero(t *testing.T) { - coin := NewInt64Coin(testDenom1, 0) +func (s *coinTestSuite) TestCoinIsZero() { + coin := sdk.NewInt64Coin(testDenom1, 0) res := coin.IsZero() - require.True(t, res) + s.Require().True(res) - coin = NewInt64Coin(testDenom1, 1) + coin = sdk.NewInt64Coin(testDenom1, 1) res = coin.IsZero() - require.False(t, res) + s.Require().False(res) +} + +func (s *coinTestSuite) TestFilteredZeroCoins() { + cases := []struct { + name string + input sdk.Coins + original string + expected string + }{ + { + name: "all greater than zero", + input: sdk.Coins{ + {"testa", sdk.OneInt()}, + {"testb", sdk.NewInt(2)}, + {"testc", sdk.NewInt(3)}, + {"testd", sdk.NewInt(4)}, + {"teste", sdk.NewInt(5)}, + }, + original: "1testa,2testb,3testc,4testd,5teste", + expected: "1testa,2testb,3testc,4testd,5teste", + }, + { + name: "zero coin in middle", + input: sdk.Coins{ + {"testa", sdk.OneInt()}, + {"testb", sdk.NewInt(2)}, + {"testc", sdk.NewInt(0)}, + {"testd", sdk.NewInt(4)}, + {"teste", sdk.NewInt(5)}, + }, + original: "1testa,2testb,0testc,4testd,5teste", + expected: "1testa,2testb,4testd,5teste", + }, + { + name: "zero coin end (unordered)", + input: sdk.Coins{ + {"teste", sdk.NewInt(5)}, + {"testc", sdk.NewInt(3)}, + {"testa", sdk.OneInt()}, + {"testd", sdk.NewInt(4)}, + {"testb", sdk.NewInt(0)}, + }, + original: "5teste,3testc,1testa,4testd,0testb", + expected: "1testa,3testc,4testd,5teste", + }, + } + + for _, tt := range cases { + undertest := sdk.NewCoins(tt.input...) + s.Require().Equal(tt.expected, undertest.String(), "NewCoins must return expected results") + s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original") + } } // ---------------------------------------------------------------------------- // Coins tests -func TestIsZeroCoins(t *testing.T) { +func (s *coinTestSuite) TestCoins_String() { cases := []struct { - inputOne Coins + name string + input sdk.Coins + expected string + }{ + { + "empty coins", + sdk.Coins{}, + "", + }, + { + "single coin", + sdk.Coins{{"tree", sdk.OneInt()}}, + "1tree", + }, + { + "multiple coins", + sdk.Coins{ + {"tree", sdk.OneInt()}, + {"gas", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + "1tree,1gas,1mineral", + }, + } + + for _, tt := range cases { + s.Require().Equal(tt.expected, tt.input.String()) + } +} + +func (s *coinTestSuite) TestIsZeroCoins() { + cases := []struct { + inputOne sdk.Coins expected bool }{ - {Coins{}, true}, - {Coins{NewInt64Coin(testDenom1, 0)}, true}, - {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 0)}, true}, - {Coins{NewInt64Coin(testDenom1, 1)}, false}, - {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false}, + {sdk.Coins{}, true}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 0)}, true}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false}, } for _, tc := range cases { res := tc.inputOne.IsZero() - require.Equal(t, tc.expected, res) + s.Require().Equal(tc.expected, res) } } -func TestEqualCoins(t *testing.T) { +func (s *coinTestSuite) TestEqualCoins() { cases := []struct { - inputOne Coins - inputTwo Coins + inputOne sdk.Coins + inputTwo sdk.Coins expected bool panics bool }{ - {Coins{}, Coins{}, true, false}, - {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0)}, true, false}, - {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false}, - {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom2, 0)}, false, true}, - {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 1)}, false, false}, - {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false, false}, - {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false}, + {sdk.Coins{}, sdk.Coins{}, true, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom2, 0)}, false, true}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false, false}, + {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false}, } for tcnum, tc := range cases { tc := tc if tc.panics { - require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) }) } else { res := tc.inputOne.IsEqual(tc.inputTwo) - require.Equal(t, tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res) + s.Require().Equal(tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res) } } } -func TestAddCoins(t *testing.T) { - zero := NewInt(0) - one := NewInt(1) - two := NewInt(2) +func (s *coinTestSuite) TestAddCoins() { + zero := sdk.NewInt(0) + one := sdk.OneInt() + two := sdk.NewInt(2) cases := []struct { - inputOne Coins - inputTwo Coins - expected Coins + inputOne sdk.Coins + inputTwo sdk.Coins + expected sdk.Coins }{ - {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}, {testDenom2, two}}}, - {Coins{{testDenom1, zero}, {testDenom2, one}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom2, one}}}, - {Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}}, - {Coins{{testDenom1, one}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, two}, {testDenom2, two}}}, - {Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins(nil)}, + {sdk.Coins{{testDenom1, one}, {testDenom2, one}}, sdk.Coins{{testDenom1, one}, {testDenom2, one}}, sdk.Coins{{testDenom1, two}, {testDenom2, two}}}, + {sdk.Coins{{testDenom1, zero}, {testDenom2, one}}, sdk.Coins{{testDenom1, zero}, {testDenom2, zero}}, sdk.Coins{{testDenom2, one}}}, + {sdk.Coins{{testDenom1, two}}, sdk.Coins{{testDenom2, zero}}, sdk.Coins{{testDenom1, two}}}, + {sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}, sdk.Coins{{testDenom1, two}, {testDenom2, two}}}, + {sdk.Coins{{testDenom1, zero}, {testDenom2, zero}}, sdk.Coins{{testDenom1, zero}, {testDenom2, zero}}, sdk.Coins(nil)}, } for tcIndex, tc := range cases { res := tc.inputOne.Add(tc.inputTwo...) - assert.True(t, res.IsValid()) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) + s.Require().True(res.IsValid()) + s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) } } -func TestSubCoins(t *testing.T) { - zero := NewInt(0) - one := NewInt(1) - two := NewInt(2) +func (s *coinTestSuite) TestSubCoins() { + zero := sdk.NewInt(0) + one := sdk.OneInt() + two := sdk.NewInt(2) testCases := []struct { - inputOne Coins - inputTwo Coins - expected Coins + inputOne sdk.Coins + inputTwo sdk.Coins + expected sdk.Coins shouldPanic bool }{ - {Coins{{testDenom1, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, true}, - {Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}, false}, - {Coins{{testDenom1, one}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, one}}, false}, - {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}}, Coins{{testDenom2, one}}, false}, - {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}}, Coins{}, true}, + {sdk.Coins{{testDenom1, two}}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}, true}, + {sdk.Coins{{testDenom1, two}}, sdk.Coins{{testDenom2, zero}}, sdk.Coins{{testDenom1, two}}, false}, + {sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom2, zero}}, sdk.Coins{{testDenom1, one}}, false}, + {sdk.Coins{{testDenom1, one}, {testDenom2, one}}, sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom2, one}}, false}, + {sdk.Coins{{testDenom1, one}, {testDenom2, one}}, sdk.Coins{{testDenom1, two}}, sdk.Coins{}, true}, } for i, tc := range testCases { tc := tc if tc.shouldPanic { - require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) }) + s.Require().Panics(func() { tc.inputOne.Sub(tc.inputTwo) }) } else { res := tc.inputOne.Sub(tc.inputTwo) - assert.True(t, res.IsValid()) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", i) + s.Require().True(res.IsValid()) + s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", i) } } } -func TestCoins(t *testing.T) { - good := Coins{ - {"gas", NewInt(1)}, - {"mineral", NewInt(1)}, - {"tree", NewInt(1)}, - } - mixedCase1 := Coins{ - {"gAs", NewInt(1)}, - {"MineraL", NewInt(1)}, - {"TREE", NewInt(1)}, - } - mixedCase2 := Coins{ - {"gAs", NewInt(1)}, - {"mineral", NewInt(1)}, - } - mixedCase3 := Coins{ - {"gAs", NewInt(1)}, - } - empty := NewCoins() - badSort1 := Coins{ - {"tree", NewInt(1)}, - {"gas", NewInt(1)}, - {"mineral", NewInt(1)}, - } - - // both are after the first one, but the second and third are in the wrong order - badSort2 := Coins{ - {"gas", NewInt(1)}, - {"tree", NewInt(1)}, - {"mineral", NewInt(1)}, - } - badAmt := Coins{ - {"gas", NewInt(1)}, - {"tree", NewInt(0)}, - {"mineral", NewInt(1)}, - } - dup := Coins{ - {"gas", NewInt(1)}, - {"gas", NewInt(1)}, - {"mineral", NewInt(1)}, - } - neg := Coins{ - {"gas", NewInt(-1)}, - {"mineral", NewInt(1)}, - } - - assert.True(t, good.IsValid(), "Coins are valid") - assert.False(t, mixedCase1.IsValid(), "Coins denoms contain upper case characters") - assert.False(t, mixedCase2.IsValid(), "First Coins denoms contain upper case characters") - assert.False(t, mixedCase3.IsValid(), "Single denom in Coins contains upper case characters") - assert.True(t, good.IsAllPositive(), "Expected coins to be positive: %v", good) - assert.False(t, empty.IsAllPositive(), "Expected coins to not be positive: %v", empty) - assert.True(t, good.IsAllGTE(empty), "Expected %v to be >= %v", good, empty) - assert.False(t, good.IsAllLT(empty), "Expected %v to be < %v", good, empty) - assert.True(t, empty.IsAllLT(good), "Expected %v to be < %v", empty, good) - assert.False(t, badSort1.IsValid(), "Coins are not sorted") - assert.False(t, badSort2.IsValid(), "Coins are not sorted") - assert.False(t, badAmt.IsValid(), "Coins cannot include 0 amounts") - assert.False(t, dup.IsValid(), "Duplicate coin") - assert.False(t, neg.IsValid(), "Negative first-denom coin") -} - -func TestCoinsGT(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.False(t, Coins{}.IsAllGT(Coins{})) - assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom2, two}})) -} - -func TestCoinsLT(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.False(t, Coins{}.IsAllLT(Coins{})) - assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{})) - assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, two}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, one}, {testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, two}, {testDenom2, two}})) - assert.True(t, Coins{}.IsAllLT(Coins{{testDenom1, one}})) -} - -func TestCoinsLTE(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.True(t, Coins{}.IsAllLTE(Coins{})) - assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{})) - assert.True(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, two}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.True(t, Coins{}.IsAllLTE(Coins{{testDenom1, one}})) -} - -func TestParse(t *testing.T) { - one := NewInt(1) +func (s *coinTestSuite) TestCoins_Validate() { + testCases := []struct { + name string + coins sdk.Coins + expPass bool + }{ + { + "valid lowercase coins", + sdk.Coins{ + {"gas", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + {"tree", sdk.OneInt()}, + }, + true, + }, + { + "valid uppercase coins", + sdk.Coins{ + {"GAS", sdk.OneInt()}, + {"MINERAL", sdk.OneInt()}, + {"TREE", sdk.OneInt()}, + }, + true, + }, + { + "valid uppercase coin", + sdk.Coins{ + {"ATOM", sdk.OneInt()}, + }, + true, + }, + { + "valid lower and uppercase coins (1)", + sdk.Coins{ + {"GAS", sdk.OneInt()}, + {"gAs", sdk.OneInt()}, + }, + true, + }, + { + "valid lower and uppercase coins (2)", + sdk.Coins{ + {"ATOM", sdk.OneInt()}, + {"Atom", sdk.OneInt()}, + {"atom", sdk.OneInt()}, + }, + true, + }, + { + "mixed case (1)", + sdk.Coins{ + {"MineraL", sdk.OneInt()}, + {"TREE", sdk.OneInt()}, + {"gAs", sdk.OneInt()}, + }, + true, + }, + { + "mixed case (2)", + sdk.Coins{ + {"gAs", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + true, + }, + { + "mixed case (3)", + sdk.Coins{ + {"gAs", sdk.OneInt()}, + }, + true, + }, + { + "unicode letters and numbers", + sdk.Coins{ + {"𐀀𐀆𐀉Ⅲ", sdk.OneInt()}, + }, + false, + }, + { + "emojis", + sdk.Coins{ + {"🤑😋🤔", sdk.OneInt()}, + }, + false, + }, + { + "IBC denominations (ADR 001)", + sdk.Coins{ + {"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()}, + {"ibc/876563AAAACF739EB061C67CDB5EDF2B7C9FD4AA9D876450CC21210807C2820A", sdk.NewInt(2)}, + }, + true, + }, + { + "empty (1)", + sdk.NewCoins(), + true, + }, + { + "empty (2)", + sdk.Coins{}, + true, + }, + { + "invalid denomination (1)", + sdk.Coins{ + {"MineraL", sdk.OneInt()}, + {"0TREE", sdk.OneInt()}, + {"gAs", sdk.OneInt()}, + }, + false, + }, + { + "invalid denomination (2)", + sdk.Coins{ + {"-GAS", sdk.OneInt()}, + {"gAs", sdk.OneInt()}, + }, + false, + }, + { + "bad sort (1)", + sdk.Coins{ + {"tree", sdk.OneInt()}, + {"gas", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + false, + }, + { + "bad sort (2)", + sdk.Coins{ + {"gas", sdk.OneInt()}, + {"tree", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + false, + }, + { + "non-positive amount (1)", + sdk.Coins{ + {"gas", sdk.OneInt()}, + {"tree", sdk.NewInt(0)}, + {"mineral", sdk.OneInt()}, + }, + false, + }, + { + "non-positive amount (2)", + sdk.Coins{ + {"gas", sdk.NewInt(-1)}, + {"tree", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + false, + }, { + "duplicate denomination", + sdk.Coins{ + {"gas", sdk.OneInt()}, + {"gas", sdk.OneInt()}, + {"mineral", sdk.OneInt()}, + }, + false, + }, + } + + for _, tc := range testCases { + err := tc.coins.Validate() + if tc.expPass { + s.Require().NoError(err, tc.name) + } else { + s.Require().Error(err, tc.name) + } + } +} + +func (s *coinTestSuite) TestCoinsGT() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom2, two}})) +} + +func (s *coinTestSuite) TestCoinsLT() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().False(sdk.Coins{}.IsAllLT(sdk.Coins{})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, two}, {testDenom2, two}})) + s.Require().True(sdk.Coins{}.IsAllLT(sdk.Coins{{testDenom1, one}})) +} + +func (s *coinTestSuite) TestCoinsLTE() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, two}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{{testDenom1, one}})) +} + +func (s *coinTestSuite) TestParseCoins() { + one := sdk.OneInt() cases := []struct { input string - valid bool // if false, we expect an error on parse - expected Coins // if valid is true, make sure this is returned + valid bool // if false, we expect an error on parse + expected sdk.Coins // if valid is true, make sure this is returned }{ {"", true, nil}, - {"1foo", true, Coins{{"foo", one}}}, - {"10bar", true, Coins{{"bar", NewInt(10)}}}, - {"99bar,1foo", true, Coins{{"bar", NewInt(99)}, {"foo", one}}}, - {"98 bar , 1 foo ", true, Coins{{"bar", NewInt(98)}, {"foo", one}}}, - {" 55\t \t bling\n", true, Coins{{"bling", NewInt(55)}}}, - {"2foo, 97 bar", true, Coins{{"bar", NewInt(97)}, {"foo", NewInt(2)}}}, - {"5 mycoin,", false, nil}, // no empty coins in a list - {"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name - {"11me coin, 12you coin", false, nil}, // no spaces in coin names - {"1.2btc", false, nil}, // amount must be integer - {"5foo-bar", false, nil}, // once more, only letters in coin name + {"0stake", true, sdk.Coins{}}, // remove zero coins + {"0stake,1foo,99bar", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}}, // remove zero coins + {"1foo", true, sdk.Coins{{"foo", one}}}, + {"10btc,1atom,20btc", false, nil}, + {"10bar", true, sdk.Coins{{"bar", sdk.NewInt(10)}}}, + {"99bar,1foo", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}}, + {"98 bar , 1 foo ", true, sdk.Coins{{"bar", sdk.NewInt(98)}, {"foo", one}}}, + {" 55\t \t bling\n", true, sdk.Coins{{"bling", sdk.NewInt(55)}}}, + {"2foo, 97 bar", true, sdk.Coins{{"bar", sdk.NewInt(97)}, {"foo", sdk.NewInt(2)}}}, + {"5 mycoin,", false, nil}, // no empty coins in a list + {"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name + {"11me coin, 12you coin", false, nil}, // no spaces in coin names + {"1.2btc", true, sdk.Coins{{"btc", sdk.NewInt(1)}}}, // amount can be decimal, will get truncated + {"5foo:bar", false, nil}, // invalid separator + {"10atom10", true, sdk.Coins{{"atom10", sdk.NewInt(10)}}}, + {"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", sdk.NewInt(200)}}}, + {"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(50)}}}, } for tcIndex, tc := range cases { - res, err := ParseCoins(tc.input) + res, err := sdk.ParseCoinsNormalized(tc.input) if !tc.valid { - require.NotNil(t, err, "%s: %#v. tc #%d", tc.input, res, tcIndex) - } else if assert.Nil(t, err, "%s: %+v", tc.input, err) { - require.Equal(t, tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) + s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex) + } else if s.Assert().Nil(err, "%s: %+v", tc.input, err) { + s.Require().Equal(tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) } } } -func TestSortCoins(t *testing.T) { - good := Coins{ - NewInt64Coin("gas", 1), - NewInt64Coin("mineral", 1), - NewInt64Coin("tree", 1), +func (s *coinTestSuite) TestSortCoins() { + good := sdk.Coins{ + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("mineral", 1), + sdk.NewInt64Coin("tree", 1), } - empty := Coins{ - NewInt64Coin("gold", 0), + empty := sdk.Coins{ + sdk.NewInt64Coin("gold", 0), } - badSort1 := Coins{ - NewInt64Coin("tree", 1), - NewInt64Coin("gas", 1), - NewInt64Coin("mineral", 1), + badSort1 := sdk.Coins{ + sdk.NewInt64Coin("tree", 1), + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("mineral", 1), } - badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order - NewInt64Coin("gas", 1), - NewInt64Coin("tree", 1), - NewInt64Coin("mineral", 1), + badSort2 := sdk.Coins{ // both are after the first one, but the second and third are in the wrong order + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("tree", 1), + sdk.NewInt64Coin("mineral", 1), } - badAmt := Coins{ - NewInt64Coin("gas", 1), - NewInt64Coin("tree", 0), - NewInt64Coin("mineral", 1), + badAmt := sdk.Coins{ + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("tree", 0), + sdk.NewInt64Coin("mineral", 1), } - dup := Coins{ - NewInt64Coin("gas", 1), - NewInt64Coin("gas", 1), - NewInt64Coin("mineral", 1), + dup := sdk.Coins{ + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("mineral", 1), } cases := []struct { - coins Coins - before, after bool // valid before/after sort + name string + coins sdk.Coins + validBefore, + validAfter bool }{ - {good, true, true}, - {empty, false, false}, - {badSort1, false, true}, - {badSort2, false, true}, - {badAmt, false, false}, - {dup, false, false}, + {"valid coins", good, true, true}, + {"empty coins", empty, false, false}, + {"bad sort (1)", badSort1, false, true}, + {"bad sort (2)", badSort2, false, true}, + {"zero value coin", badAmt, false, false}, + {"duplicate coins", dup, false, false}, } - for tcIndex, tc := range cases { - require.Equal(t, tc.before, tc.coins.IsValid(), "coin validity is incorrect before sorting, tc #%d", tcIndex) + for _, tc := range cases { + err := tc.coins.Validate() + if tc.validBefore { + s.Require().NoError(err, tc.name) + } else { + s.Require().Error(err, tc.name) + } + tc.coins.Sort() - require.Equal(t, tc.after, tc.coins.IsValid(), "coin validity is incorrect after sorting, tc #%d", tcIndex) + + err = tc.coins.Validate() + if tc.validAfter { + s.Require().NoError(err, tc.name) + } else { + s.Require().Error(err, tc.name) + } } } -func TestAmountOf(t *testing.T) { - case0 := Coins{} - case1 := Coins{ - NewInt64Coin("gold", 0), +func (s *coinTestSuite) TestAmountOf() { + case0 := sdk.Coins{} + case1 := sdk.Coins{ + sdk.NewInt64Coin("gold", 0), } - case2 := Coins{ - NewInt64Coin("gas", 1), - NewInt64Coin("mineral", 1), - NewInt64Coin("tree", 1), + case2 := sdk.Coins{ + sdk.NewInt64Coin("gas", 1), + sdk.NewInt64Coin("mineral", 1), + sdk.NewInt64Coin("tree", 1), } - case3 := Coins{ - NewInt64Coin("mineral", 1), - NewInt64Coin("tree", 1), + case3 := sdk.Coins{ + sdk.NewInt64Coin("mineral", 1), + sdk.NewInt64Coin("tree", 1), } - case4 := Coins{ - NewInt64Coin("gas", 8), + case4 := sdk.Coins{ + sdk.NewInt64Coin("gas", 8), } cases := []struct { - coins Coins + coins sdk.Coins amountOf int64 amountOfSpace int64 amountOfGAS int64 @@ -506,182 +777,173 @@ func TestAmountOf(t *testing.T) { } for _, tc := range cases { - assert.Equal(t, NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas")) - assert.Equal(t, NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral")) - assert.Equal(t, NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree")) - } - - assert.Panics(t, func() { cases[0].coins.AmountOf("Invalid") }) -} - -func TestCoinsIsAnyGTE(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.False(t, Coins{}.IsAnyGTE(Coins{})) - assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{})) - assert.False(t, Coins{}.IsAnyGTE(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, two}})) - assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, two}, {testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}})) - assert.True(t, Coins{{testDenom1, two}}.IsAnyGTE(Coins{{testDenom1, one}})) - assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.True(t, Coins{{testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.True(t, Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) -} - -func TestCoinsIsAllGT(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.False(t, Coins{}.IsAllGT(Coins{})) - assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{})) - assert.False(t, Coins{}.IsAllGT(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, two}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, two}, {testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}})) - assert.True(t, Coins{{testDenom1, two}}.IsAllGT(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) -} - -func TestCoinsIsAllGTE(t *testing.T) { - one := NewInt(1) - two := NewInt(2) - - assert.True(t, Coins{}.IsAllGTE(Coins{})) - assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, two}})) - assert.False(t, Coins{}.IsAllGTE(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, two}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, two}, {testDenom2, one}})) - assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}})) - assert.True(t, Coins{{testDenom1, two}}.IsAllGTE(Coins{{testDenom1, one}})) - assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, one}})) - assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) - assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) -} - -func TestNewCoins(t *testing.T) { - tenatom := NewInt64Coin("atom", 10) - tenbtc := NewInt64Coin("btc", 10) - zeroeth := NewInt64Coin("eth", 0) + s.Require().Equal(sdk.NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas")) + s.Require().Equal(sdk.NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral")) + s.Require().Equal(sdk.NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree")) + } + + s.Require().Panics(func() { cases[0].coins.AmountOf("10Invalid") }) +} + +func (s *coinTestSuite) TestCoinsIsAnyGTE() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{})) + s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, two}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) + s.Require().True(sdk.Coins{{testDenom1, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().True(sdk.Coins{{testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().True(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) +} + +func (s *coinTestSuite) TestCoinsIsAllGT() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) + s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, two}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) + s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGT(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) +} + +func (s *coinTestSuite) TestCoinsIsAllGTE() { + one := sdk.OneInt() + two := sdk.NewInt(2) + + s.Require().True(sdk.Coins{}.IsAllGTE(sdk.Coins{})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, two}})) + s.Require().False(sdk.Coins{}.IsAllGTE(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, two}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) + s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) + s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) + s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) + s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) +} + +func (s *coinTestSuite) TestNewCoins() { + tenatom := sdk.NewInt64Coin("atom", 10) + tenbtc := sdk.NewInt64Coin("btc", 10) + zeroeth := sdk.NewInt64Coin("eth", 0) + invalidCoin := sdk.Coin{"0ETH", sdk.OneInt()} tests := []struct { name string - coins Coins - want Coins + coins sdk.Coins + want sdk.Coins wantPanic bool }{ - {"empty args", []Coin{}, Coins{}, false}, - {"one coin", []Coin{tenatom}, Coins{tenatom}, false}, - {"sort after create", []Coin{tenbtc, tenatom}, Coins{tenatom, tenbtc}, false}, - {"sort and remove zeroes", []Coin{zeroeth, tenbtc, tenatom}, Coins{tenatom, tenbtc}, false}, - {"panic on dups", []Coin{tenatom, tenatom}, Coins{}, true}, + {"empty args", []sdk.Coin{}, sdk.Coins{}, false}, + {"one coin", []sdk.Coin{tenatom}, sdk.Coins{tenatom}, false}, + {"sort after create", []sdk.Coin{tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, + {"sort and remove zeroes", []sdk.Coin{zeroeth, tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, + {"panic on dups", []sdk.Coin{tenatom, tenatom}, sdk.Coins{}, true}, + {"panic on invalid coin", []sdk.Coin{invalidCoin, tenatom}, sdk.Coins{}, true}, } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - if tt.wantPanic { - require.Panics(t, func() { NewCoins(tt.coins...) }) - return - } - got := NewCoins(tt.coins...) - require.True(t, got.IsEqual(tt.want)) - }) + if tt.wantPanic { + s.Require().Panics(func() { sdk.NewCoins(tt.coins...) }) + continue + } + got := sdk.NewCoins(tt.coins...) + s.Require().True(got.IsEqual(tt.want)) } } -func TestCoinsIsAnyGT(t *testing.T) { - twoAtom := NewInt64Coin("atom", 2) - fiveAtom := NewInt64Coin("atom", 5) - threeEth := NewInt64Coin("eth", 3) - sixEth := NewInt64Coin("eth", 6) - twoBtc := NewInt64Coin("btc", 2) - - require.False(t, Coins{}.IsAnyGT(Coins{})) - - require.False(t, Coins{fiveAtom}.IsAnyGT(Coins{})) - require.False(t, Coins{}.IsAnyGT(Coins{fiveAtom})) - require.True(t, Coins{fiveAtom}.IsAnyGT(Coins{twoAtom})) - require.False(t, Coins{twoAtom}.IsAnyGT(Coins{fiveAtom})) - - require.True(t, Coins{twoAtom, sixEth}.IsAnyGT(Coins{twoBtc, fiveAtom, threeEth})) - require.False(t, Coins{twoBtc, twoAtom, threeEth}.IsAnyGT(Coins{fiveAtom, sixEth})) - require.False(t, Coins{twoAtom, sixEth}.IsAnyGT(Coins{twoBtc, fiveAtom})) -} - -func TestFindDup(t *testing.T) { - abc := NewInt64Coin("abc", 10) - def := NewInt64Coin("def", 10) - ghi := NewInt64Coin("ghi", 10) +func (s *coinTestSuite) TestCoinsIsAnyGT() { + twoAtom := sdk.NewInt64Coin("atom", 2) + fiveAtom := sdk.NewInt64Coin("atom", 5) + threeEth := sdk.NewInt64Coin("eth", 3) + sixEth := sdk.NewInt64Coin("eth", 6) + twoBtc := sdk.NewInt64Coin("btc", 2) - type args struct { - coins Coins - } tests := []struct { - name string - args args - want int + name string + coinsA sdk.Coins + coinsB sdk.Coins + expPass bool }{ - {"empty", args{NewCoins()}, -1}, - {"one coin", args{NewCoins(NewInt64Coin("xyz", 10))}, -1}, - {"no dups", args{Coins{abc, def, ghi}}, -1}, - {"dup at first position", args{Coins{abc, abc, def}}, 1}, - {"dup after first position", args{Coins{abc, def, def}}, 2}, + {"{} ≤ {}", sdk.Coins{}, sdk.Coins{}, false}, + {"{} ≤ 5atom", sdk.Coins{}, sdk.Coins{fiveAtom}, false}, + {"5atom > 2atom", sdk.Coins{fiveAtom}, sdk.Coins{twoAtom}, true}, + {"2atom ≤ 5atom", sdk.Coins{twoAtom}, sdk.Coins{fiveAtom}, false}, + {"2atom,6eth > 2btc,5atom,3eth", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom, threeEth}, true}, + {"2btc,2atom,3eth ≤ 5atom,6eth", sdk.Coins{twoBtc, twoAtom, threeEth}, sdk.Coins{fiveAtom, sixEth}, false}, + {"2atom,6eth ≤ 2btc,5atom", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom}, false}, } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - if got := findDup(tt.args.coins); got != tt.want { - t.Errorf("findDup() = %v, want %v", got, tt.want) - } - }) + + for _, tc := range tests { + s.Require().True(tc.expPass == tc.coinsA.IsAnyGT(tc.coinsB), tc.name) } } -func TestMarshalJSONCoins(t *testing.T) { - cdc := codec.New() - RegisterCodec(cdc) +func (s *coinTestSuite) TestMarshalJSONCoins() { + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) testCases := []struct { name string - input Coins + input sdk.Coins strOutput string }{ {"nil coins", nil, `[]`}, - {"empty coins", Coins{}, `[]`}, - {"non-empty coins", NewCoins(NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`}, + {"empty coins", sdk.Coins{}, `[]`}, + {"non-empty coins", sdk.NewCoins(sdk.NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`}, } 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)) + bz, err := cdc.MarshalJSON(tc.input) + s.Require().NoError(err) + s.Require().Equal(tc.strOutput, string(bz)) - var newCoins Coins - require.NoError(t, cdc.UnmarshalJSON(bz, &newCoins)) + var newCoins sdk.Coins + s.Require().NoError(cdc.UnmarshalJSON(bz, &newCoins)) - if tc.input.Empty() { - require.Nil(t, newCoins) - } else { - require.Equal(t, tc.input, newCoins) - } - }) + if tc.input.Empty() { + s.Require().Nil(newCoins) + } else { + s.Require().Equal(tc.input, newCoins) + } } } + +func (s *coinTestSuite) TestCoinAminoEncoding() { + cdc := codec.NewLegacyAmino() + c := sdk.NewInt64Coin(testDenom1, 5) + + bz1, err := cdc.MarshalBinaryBare(c) + s.Require().NoError(err) + + bz2, err := cdc.MarshalBinaryLengthPrefixed(c) + s.Require().NoError(err) + + bz3, err := c.Marshal() + s.Require().NoError(err) + s.Require().Equal(bz1, bz3) + s.Require().Equal(bz2[1:], bz3) +} diff --git a/types/config.go b/types/config.go index 3e39fe30c272..a3181703df1e 100644 --- a/types/config.go +++ b/types/config.go @@ -1,6 +1,7 @@ package types import ( + "context" "sync" "github.com/cosmos/cosmos-sdk/version" @@ -19,19 +20,19 @@ type Config struct { mtx sync.RWMutex coinType uint32 sealed bool + sealedch chan struct{} } // cosmos-sdk wide global singleton -var sdkConfig *Config - -// GetConfig returns the config instance for the SDK. -func GetConfig() *Config { - if sdkConfig != nil { - return sdkConfig - } +var ( + sdkConfig *Config + initConfig sync.Once +) - sdkConfig = &Config{ - sealed: false, +// New returns a new Config with default values. +func NewConfig() *Config { + return &Config{ + sealedch: make(chan struct{}), bech32AddressPrefix: map[string]string{ "account_addr": Bech32PrefixAccAddr, "validator_addr": Bech32PrefixValAddr, @@ -44,9 +45,27 @@ func GetConfig() *Config { fullFundraiserPath: FullFundraiserPath, txEncoder: nil, } +} + +// GetConfig returns the config instance for the SDK. +func GetConfig() *Config { + initConfig.Do(func() { + sdkConfig = NewConfig() + }) return sdkConfig } +// GetSealedConfig returns the config instance for the SDK if/once it is sealed. +func GetSealedConfig(ctx context.Context) (*Config, error) { + config := GetConfig() + select { + case <-config.sealedch: + return config, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + func (config *Config) assertNotSealed() { config.mtx.Lock() defer config.mtx.Unlock() @@ -108,9 +127,17 @@ func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) { // Seal seals the config such that the config state could not be modified further func (config *Config) Seal() *Config { config.mtx.Lock() - defer config.mtx.Unlock() + if config.sealed { + config.mtx.Unlock() + return config + } + + // signal sealed after state exposed/unlocked config.sealed = true + config.mtx.Unlock() + close(config.sealedch) + return config } diff --git a/types/config_test.go b/types/config_test.go new file mode 100644 index 000000000000..e2027f38569f --- /dev/null +++ b/types/config_test.go @@ -0,0 +1,58 @@ +package types_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type configTestSuite struct { + suite.Suite +} + +func TestConfigTestSuite(t *testing.T) { + suite.Run(t, new(configTestSuite)) +} + +func (s *configTestSuite) TestConfig_SetCoinType() { + config := sdk.NewConfig() + config.SetCoinType(1) + s.Require().Equal(uint32(1), config.GetCoinType()) + config.SetCoinType(99) + s.Require().Equal(uint32(99), config.GetCoinType()) + + config.Seal() + s.Require().Panics(func() { config.SetCoinType(99) }) +} + +func (s *configTestSuite) TestConfig_SetTxEncoder() { + mockErr := errors.New("test") + config := sdk.NewConfig() + s.Require().Nil(config.GetTxEncoder()) + encFunc := sdk.TxEncoder(func(tx sdk.Tx) ([]byte, error) { return nil, nil }) + config.SetTxEncoder(encFunc) + _, err := config.GetTxEncoder()(sdk.Tx(nil)) + s.Require().Error(mockErr, err) + + config.Seal() + s.Require().Panics(func() { config.SetTxEncoder(encFunc) }) +} + +func (s *configTestSuite) TestConfig_SetFullFundraiserPath() { + config := sdk.NewConfig() + config.SetFullFundraiserPath("test/path") + s.Require().Equal("test/path", config.GetFullFundraiserPath()) + + config.SetFullFundraiserPath("test/poth") + s.Require().Equal("test/poth", config.GetFullFundraiserPath()) + + config.Seal() + s.Require().Panics(func() { config.SetFullFundraiserPath("x/test/path") }) +} + +func (s *configTestSuite) TestKeyringServiceName() { + s.Require().Equal(sdk.DefaultKeyringServiceName, sdk.KeyringServiceName()) +} diff --git a/types/context.go b/types/context.go index ed4b693c9770..6d326fb73f19 100644 --- a/types/context.go +++ b/types/context.go @@ -7,6 +7,7 @@ import ( "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/store/gaskv" stypes "github.com/cosmos/cosmos-sdk/store/types" @@ -23,7 +24,7 @@ and standard additions here would be better just to add to the Context struct type Context struct { ctx context.Context ms MultiStore - header abci.Header + header tmproto.Header chainID string txBytes []byte logger log.Logger @@ -57,8 +58,8 @@ func (c Context) MinGasPrices() DecCoins { return c.minGasPrice } func (c Context) EventManager() *EventManager { return c.eventManager } // clone the header before returning -func (c Context) BlockHeader() abci.Header { - var msg = proto.Clone(&c.header).(*abci.Header) +func (c Context) BlockHeader() tmproto.Header { + var msg = proto.Clone(&c.header).(*tmproto.Header) return *msg } @@ -67,7 +68,7 @@ func (c Context) ConsensusParams() *abci.ConsensusParams { } // create a new context -func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Logger) Context { +func NewContext(ms MultiStore, header tmproto.Header, isCheckTx bool, logger log.Logger) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() return Context{ @@ -83,23 +84,27 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo } } +// WithContext returns a Context with an updated context.Context. func (c Context) WithContext(ctx context.Context) Context { c.ctx = ctx return c } +// WithMultiStore returns a Context with an updated MultiStore. func (c Context) WithMultiStore(ms MultiStore) Context { c.ms = ms return c } -func (c Context) WithBlockHeader(header abci.Header) Context { +// WithBlockHeader returns a Context with an updated tendermint block header in UTC time. +func (c Context) WithBlockHeader(header tmproto.Header) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() c.header = header return c } +// WithBlockTime returns a Context with an updated tendermint block header time in UTC time func (c Context) WithBlockTime(newTime time.Time) Context { newHeader := c.BlockHeader() // https://github.com/gogo/protobuf/issues/519 @@ -107,48 +112,57 @@ func (c Context) WithBlockTime(newTime time.Time) Context { return c.WithBlockHeader(newHeader) } +// WithProposer returns a Context with an updated proposer consensus address. func (c Context) WithProposer(addr ConsAddress) Context { newHeader := c.BlockHeader() newHeader.ProposerAddress = addr.Bytes() return c.WithBlockHeader(newHeader) } +// WithBlockHeight returns a Context with an updated block height. func (c Context) WithBlockHeight(height int64) Context { newHeader := c.BlockHeader() newHeader.Height = height return c.WithBlockHeader(newHeader) } +// WithChainID returns a Context with an updated chain identifier. func (c Context) WithChainID(chainID string) Context { c.chainID = chainID return c } +// WithTxBytes returns a Context with an updated txBytes. func (c Context) WithTxBytes(txBytes []byte) Context { c.txBytes = txBytes return c } +// WithLogger returns a Context with an updated logger. func (c Context) WithLogger(logger log.Logger) Context { c.logger = logger return c } +// WithVoteInfos returns a Context with an updated consensus VoteInfo. func (c Context) WithVoteInfos(voteInfo []abci.VoteInfo) Context { c.voteInfo = voteInfo return c } +// WithGasMeter returns a Context with an updated transaction GasMeter. func (c Context) WithGasMeter(meter GasMeter) Context { c.gasMeter = meter return c } +// WithBlockGasMeter returns a Context with an updated block GasMeter func (c Context) WithBlockGasMeter(meter GasMeter) Context { c.blockGasMeter = meter return c } +// WithIsCheckTx enables or disables CheckTx value for verifying transactions and returns an updated Context func (c Context) WithIsCheckTx(isCheckTx bool) Context { c.checkTx = isCheckTx return c @@ -164,16 +178,19 @@ func (c Context) WithIsReCheckTx(isRecheckTx bool) Context { return c } +// WithMinGasPrices returns a Context with an updated minimum gas price value func (c Context) WithMinGasPrices(gasPrices DecCoins) Context { c.minGasPrice = gasPrices return c } +// WithConsensusParams returns a Context with an updated consensus params func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context { c.consParams = params return c } +// WithEventManager returns a Context with an updated event manager func (c Context) WithEventManager(em *EventManager) Context { c.eventManager = em return c @@ -225,3 +242,24 @@ func (c Context) CacheContext() (cc Context, writeCache func()) { cc = c.WithMultiStore(cms).WithEventManager(NewEventManager()) return cc, cms.Write } + +// ContextKey defines a type alias for a stdlib Context key. +type ContextKey string + +// SdkContextKey is the key in the context.Context which holds the sdk.Context. +const SdkContextKey ContextKey = "sdk-context" + +// WrapSDKContext returns a stdlib context.Context with the provided sdk.Context's internal +// context as a value. It is useful for passing an sdk.Context through methods that take a +// stdlib context.Context parameter such as generated gRPC methods. To get the original +// sdk.Context back, call UnwrapSDKContext. +func WrapSDKContext(ctx Context) context.Context { + return context.WithValue(ctx.ctx, SdkContextKey, ctx) +} + +// UnwrapSDKContext retrieves a Context from a context.Context instance +// attached with WrapSDKContext. It panics if a Context was not properly +// attached +func UnwrapSDKContext(ctx context.Context) Context { + return ctx.Value(SdkContextKey).(Context) +} diff --git a/types/context_test.go b/types/context_test.go index d8e893364d7d..018bd6a25792 100644 --- a/types/context_test.go +++ b/types/context_test.go @@ -1,94 +1,82 @@ package types_test import ( + "context" "testing" "time" - "github.com/stretchr/testify/require" - + "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" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/tendermint/tendermint/crypto/secp256k1" - + "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/types" ) -type MockLogger struct { - logs *[]string -} - -func NewMockLogger() MockLogger { - logs := make([]string, 0) - return MockLogger{ - &logs, - } -} - -func (l MockLogger) Debug(msg string, kvs ...interface{}) { - *l.logs = append(*l.logs, msg) +type contextTestSuite struct { + suite.Suite } -func (l MockLogger) Info(msg string, kvs ...interface{}) { - *l.logs = append(*l.logs, msg) +func TestContextTestSuite(t *testing.T) { + suite.Run(t, new(contextTestSuite)) } -func (l MockLogger) Error(msg string, kvs ...interface{}) { - *l.logs = append(*l.logs, msg) -} - -func (l MockLogger) With(kvs ...interface{}) log.Logger { - panic("not implemented") -} - -func defaultContext(key types.StoreKey) types.Context { +func (s *contextTestSuite) defaultContext(key types.StoreKey) types.Context { db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) cms.MountStoreWithDB(key, types.StoreTypeIAVL, db) - cms.LoadLatestVersion() - ctx := types.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + s.Require().NoError(cms.LoadLatestVersion()) + ctx := types.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) return ctx } -func TestCacheContext(t *testing.T) { - key := types.NewKVStoreKey(t.Name()) +func (s *contextTestSuite) TestCacheContext() { + key := types.NewKVStoreKey(s.T().Name() + "_TestCacheContext") k1 := []byte("hello") v1 := []byte("world") k2 := []byte("key") v2 := []byte("value") - ctx := defaultContext(key) + ctx := s.defaultContext(key) store := ctx.KVStore(key) store.Set(k1, v1) - require.Equal(t, v1, store.Get(k1)) - require.Nil(t, store.Get(k2)) + s.Require().Equal(v1, store.Get(k1)) + s.Require().Nil(store.Get(k2)) cctx, write := ctx.CacheContext() cstore := cctx.KVStore(key) - require.Equal(t, v1, cstore.Get(k1)) - require.Nil(t, cstore.Get(k2)) + s.Require().Equal(v1, cstore.Get(k1)) + s.Require().Nil(cstore.Get(k2)) cstore.Set(k2, v2) - require.Equal(t, v2, cstore.Get(k2)) - require.Nil(t, store.Get(k2)) + s.Require().Equal(v2, cstore.Get(k2)) + s.Require().Nil(store.Get(k2)) write() - require.Equal(t, v2, store.Get(k2)) + s.Require().Equal(v2, store.Get(k2)) } -func TestLogContext(t *testing.T) { - key := types.NewKVStoreKey(t.Name()) - ctx := defaultContext(key) - logger := NewMockLogger() +func (s *contextTestSuite) TestLogContext() { + key := types.NewKVStoreKey(s.T().Name()) + ctx := s.defaultContext(key) + ctrl := gomock.NewController(s.T()) + s.T().Cleanup(ctrl.Finish) + + logger := mocks.NewMockLogger(ctrl) + logger.EXPECT().Debug("debug") + logger.EXPECT().Info("info") + logger.EXPECT().Error("error") + ctx = ctx.WithLogger(logger) ctx.Logger().Debug("debug") ctx.Logger().Info("info") ctx.Logger().Error("error") - require.Equal(t, *logger.logs, []string{"debug", "info", "error"}) } type dummy int64 //nolint:unused @@ -98,22 +86,26 @@ func (d dummy) Clone() interface{} { } // Testing saving/loading sdk type values to/from the context -func TestContextWithCustom(t *testing.T) { +func (s *contextTestSuite) TestContextWithCustom() { var ctx types.Context - require.True(t, ctx.IsZero()) + s.Require().True(ctx.IsZero()) + + ctrl := gomock.NewController(s.T()) + s.T().Cleanup(ctrl.Finish) - header := abci.Header{} + header := tmproto.Header{} height := int64(1) chainid := "chainid" ischeck := true txbytes := []byte("txbytes") - logger := NewMockLogger() + logger := mocks.NewMockLogger(ctrl) voteinfos := []abci.VoteInfo{{}} meter := types.NewGasMeter(10000) + blockGasMeter := types.NewGasMeter(20000) minGasPrices := types.DecCoins{types.NewInt64DecCoin("feetoken", 1)} ctx = types.NewContext(nil, header, ischeck, logger) - require.Equal(t, header, ctx.BlockHeader()) + s.Require().Equal(header, ctx.BlockHeader()) ctx = ctx. WithBlockHeight(height). @@ -121,19 +113,38 @@ func TestContextWithCustom(t *testing.T) { WithTxBytes(txbytes). WithVoteInfos(voteinfos). WithGasMeter(meter). - WithMinGasPrices(minGasPrices) - require.Equal(t, height, ctx.BlockHeight()) - require.Equal(t, chainid, ctx.ChainID()) - require.Equal(t, ischeck, ctx.IsCheckTx()) - require.Equal(t, txbytes, ctx.TxBytes()) - require.Equal(t, logger, ctx.Logger()) - require.Equal(t, voteinfos, ctx.VoteInfos()) - require.Equal(t, meter, ctx.GasMeter()) - require.Equal(t, minGasPrices, ctx.MinGasPrices()) + WithMinGasPrices(minGasPrices). + WithBlockGasMeter(blockGasMeter) + s.Require().Equal(height, ctx.BlockHeight()) + s.Require().Equal(chainid, ctx.ChainID()) + s.Require().Equal(ischeck, ctx.IsCheckTx()) + s.Require().Equal(txbytes, ctx.TxBytes()) + s.Require().Equal(logger, ctx.Logger()) + s.Require().Equal(voteinfos, ctx.VoteInfos()) + s.Require().Equal(meter, ctx.GasMeter()) + s.Require().Equal(minGasPrices, ctx.MinGasPrices()) + s.Require().Equal(blockGasMeter, ctx.BlockGasMeter()) + s.Require().False(ctx.WithIsCheckTx(false).IsCheckTx()) + + // test IsReCheckTx + s.Require().False(ctx.IsReCheckTx()) + ctx = ctx.WithIsCheckTx(false) + ctx = ctx.WithIsReCheckTx(true) + s.Require().True(ctx.IsCheckTx()) + s.Require().True(ctx.IsReCheckTx()) + + // test consensus param + s.Require().Nil(ctx.ConsensusParams()) + cp := &abci.ConsensusParams{} + s.Require().Equal(cp, ctx.WithConsensusParams(cp).ConsensusParams()) + + // test inner context + newContext := context.WithValue(ctx.Context(), "key", "value") //nolint:golint,staticcheck + s.Require().NotEqual(ctx.Context(), ctx.WithContext(newContext).Context()) } // Testing saving/loading of header fields to/from the context -func TestContextHeader(t *testing.T) { +func (s *contextTestSuite) TestContextHeader() { var ctx types.Context height := int64(5) @@ -141,49 +152,49 @@ func TestContextHeader(t *testing.T) { addr := secp256k1.GenPrivKey().PubKey().Address() proposer := types.ConsAddress(addr) - ctx = types.NewContext(nil, abci.Header{}, false, nil) + ctx = types.NewContext(nil, tmproto.Header{}, false, nil) ctx = ctx. WithBlockHeight(height). WithBlockTime(time). WithProposer(proposer) - require.Equal(t, height, ctx.BlockHeight()) - require.Equal(t, height, ctx.BlockHeader().Height) - require.Equal(t, time.UTC(), ctx.BlockHeader().Time) - require.Equal(t, proposer.Bytes(), ctx.BlockHeader().ProposerAddress) + s.Require().Equal(height, ctx.BlockHeight()) + s.Require().Equal(height, ctx.BlockHeader().Height) + s.Require().Equal(time.UTC(), ctx.BlockHeader().Time) + s.Require().Equal(proposer.Bytes(), ctx.BlockHeader().ProposerAddress) } -func TestContextHeaderClone(t *testing.T) { +func (s *contextTestSuite) TestContextHeaderClone() { cases := map[string]struct { - h abci.Header + h tmproto.Header }{ "empty": { - h: abci.Header{}, + h: tmproto.Header{}, }, "height": { - h: abci.Header{ + h: tmproto.Header{ Height: 77, }, }, "time": { - h: abci.Header{ + h: tmproto.Header{ Time: time.Unix(12345677, 12345), }, }, "zero time": { - h: abci.Header{ + h: tmproto.Header{ Time: time.Unix(0, 0), }, }, "many items": { - h: abci.Header{ + h: tmproto.Header{ Height: 823, Time: time.Unix(9999999999, 0), ChainID: "silly-demo", }, }, "many items with hash": { - h: abci.Header{ + h: tmproto.Header{ Height: 823, Time: time.Unix(9999999999, 0), ChainID: "silly-demo", @@ -195,16 +206,26 @@ func TestContextHeaderClone(t *testing.T) { for name, tc := range cases { tc := tc - t.Run(name, func(t *testing.T) { + s.T().Run(name, func(t *testing.T) { ctx := types.NewContext(nil, tc.h, false, nil) - require.Equal(t, tc.h.Height, ctx.BlockHeight()) - require.Equal(t, tc.h.Time.UTC(), ctx.BlockTime()) + s.Require().Equal(tc.h.Height, ctx.BlockHeight()) + s.Require().Equal(tc.h.Time.UTC(), ctx.BlockTime()) // update only changes one field var newHeight int64 = 17 ctx = ctx.WithBlockHeight(newHeight) - require.Equal(t, newHeight, ctx.BlockHeight()) - require.Equal(t, tc.h.Time.UTC(), ctx.BlockTime()) + s.Require().Equal(newHeight, ctx.BlockHeight()) + s.Require().Equal(tc.h.Time.UTC(), ctx.BlockTime()) }) } } + +func (s *contextTestSuite) TestUnwrapSDKContext() { + sdkCtx := types.NewContext(nil, tmproto.Header{}, false, nil) + ctx := types.WrapSDKContext(sdkCtx) + sdkCtx2 := types.UnwrapSDKContext(ctx) + s.Require().Equal(sdkCtx, sdkCtx2) + + ctx = context.Background() + s.Require().Panics(func() { types.UnwrapSDKContext(ctx) }) +} diff --git a/types/dec_coin.go b/types/dec_coin.go index 9b6e700104e7..6e400d7a0557 100644 --- a/types/dec_coin.go +++ b/types/dec_coin.go @@ -11,21 +11,13 @@ import ( // ---------------------------------------------------------------------------- // Decimal Coin -// DecCoin defines a coin which can have additional decimal points -type DecCoin struct { - Denom string `json:"denom"` - Amount Dec `json:"amount"` -} - // NewDecCoin creates a new DecCoin instance from an Int. func NewDecCoin(denom string, amount Int) DecCoin { - if err := validate(denom, amount); err != nil { - panic(err) - } + coin := NewCoin(denom, amount) return DecCoin{ - Denom: denom, - Amount: amount.ToDec(), + Denom: coin.Denom, + Amount: coin.Amount.ToDec(), } } @@ -45,7 +37,7 @@ func NewDecCoinFromDec(denom string, amount Dec) DecCoin { // NewDecCoinFromCoin creates a new DecCoin from a Coin. func NewDecCoinFromCoin(coin Coin) DecCoin { - if err := validate(coin.Denom, coin.Amount); err != nil { + if err := coin.Validate(); err != nil { panic(err) } @@ -134,7 +126,7 @@ func (coin DecCoin) IsPositive() bool { // // TODO: Remove once unsigned integers are used. func (coin DecCoin) IsNegative() bool { - return coin.Amount.Sign() == -1 + return coin.Amount.IsNegative() } // String implements the Stringer interface for DecCoin. It returns a @@ -143,12 +135,20 @@ func (coin DecCoin) String() string { return fmt.Sprintf("%v%v", coin.Amount, coin.Denom) } -// IsValid returns true if the DecCoin has a non-negative amount and the denom is vaild. -func (coin DecCoin) IsValid() bool { +// Validate returns an error if the DecCoin has a negative amount or if the denom is invalid. +func (coin DecCoin) Validate() error { if err := ValidateDenom(coin.Denom); err != nil { - return false + return err + } + if coin.IsNegative() { + return fmt.Errorf("decimal coin %s amount cannot be negative", coin) } - return !coin.IsNegative() + return nil +} + +// IsValid returns true if the DecCoin has a non-negative amount and the denom is valid. +func (coin DecCoin) IsValid() bool { + return coin.Validate() == nil } // ---------------------------------------------------------------------------- @@ -158,29 +158,28 @@ func (coin DecCoin) IsValid() bool { type DecCoins []DecCoin // NewDecCoins constructs a new coin set with with decimal values -// from DecCoins. +// from DecCoins. The provided coins will be sanitized by removing +// zero coins and sorting the coin set. A panic will occur if the coin set is not valid. func NewDecCoins(decCoins ...DecCoin) DecCoins { - // remove zeroes - newDecCoins := removeZeroDecCoins(DecCoins(decCoins)) - if len(newDecCoins) == 0 { - return DecCoins{} + newDecCoins := sanitizeDecCoins(decCoins) + if err := newDecCoins.Validate(); err != nil { + panic(fmt.Errorf("invalid coin set %s: %w", newDecCoins, err)) } - newDecCoins.Sort() - - // detect duplicate Denoms - if dupIndex := findDup(newDecCoins); dupIndex != -1 { - panic(fmt.Errorf("find duplicate denom: %s", newDecCoins[dupIndex])) - } + return newDecCoins +} - if !newDecCoins.IsValid() { - panic(fmt.Errorf("invalid coin set: %s", newDecCoins)) +func sanitizeDecCoins(decCoins []DecCoin) DecCoins { + // remove zeroes + newDecCoins := removeZeroDecCoins(decCoins) + if len(newDecCoins) == 0 { + return DecCoins{} } - return newDecCoins + return newDecCoins.Sort() } -// NewDecCoinsFromCoin constructs a new coin set with decimal values +// NewDecCoinsFromCoins constructs a new coin set with decimal values // from regular Coins. func NewDecCoinsFromCoins(coins ...Coin) DecCoins { decCoins := make(DecCoins, len(coins)) @@ -504,45 +503,60 @@ func (coins DecCoins) IsZero() bool { return true } -// IsValid asserts the DecCoins are sorted, have positive amount, and Denom -// does not contain upper case characters. -func (coins DecCoins) IsValid() bool { +// Validate checks that the DecCoins are sorted, have positive amount, with a valid and unique +// denomination (i.e no duplicates). Otherwise, it returns an error. +func (coins DecCoins) Validate() error { switch len(coins) { case 0: - return true + return nil case 1: if err := ValidateDenom(coins[0].Denom); err != nil { - return false + return err } - return coins[0].IsPositive() - + if !coins[0].IsPositive() { + return fmt.Errorf("coin %s amount is not positive", coins[0]) + } + return nil default: // check single coin case - if !(DecCoins{coins[0]}).IsValid() { - return false + if err := (DecCoins{coins[0]}).Validate(); err != nil { + return err } lowDenom := coins[0].Denom + seenDenoms := make(map[string]bool) + seenDenoms[lowDenom] = true + for _, coin := range coins[1:] { - if strings.ToLower(coin.Denom) != coin.Denom { - return false + if seenDenoms[coin.Denom] { + return fmt.Errorf("duplicate denomination %s", coin.Denom) + } + if err := ValidateDenom(coin.Denom); err != nil { + return err } if coin.Denom <= lowDenom { - return false + return fmt.Errorf("denomination %s is not sorted", coin.Denom) } if !coin.IsPositive() { - return false + return fmt.Errorf("coin %s amount is not positive", coin.Denom) } // we compare each coin against the last denom lowDenom = coin.Denom + seenDenoms[coin.Denom] = true } - return true + return nil } } +// IsValid calls Validate and returns true when the DecCoins are sorted, have positive amount, with a +// valid and unique denomination (i.e no duplicates). +func (coins DecCoins) IsValid() bool { + return coins.Validate() == nil +} + // IsAllPositive returns true if there is at least one coin and all currencies // have a positive value. // @@ -562,29 +576,30 @@ func (coins DecCoins) IsAllPositive() bool { } func removeZeroDecCoins(coins DecCoins) DecCoins { - i, l := 0, len(coins) - for i < l { - if coins[i].IsZero() { - // remove coin - coins = append(coins[:i], coins[i+1:]...) - l-- - } else { - i++ + result := make([]DecCoin, 0, len(coins)) + + for _, coin := range coins { + if !coin.IsZero() { + result = append(result, coin) } } - return coins[:i] + return result } //----------------------------------------------------------------------------- // Sorting -var _ sort.Interface = Coins{} +var _ sort.Interface = DecCoins{} + +// Len implements sort.Interface for DecCoins +func (coins DecCoins) Len() int { return len(coins) } -//nolint -func (coins DecCoins) Len() int { return len(coins) } +// Less implements sort.Interface for DecCoins func (coins DecCoins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom } -func (coins DecCoins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } + +// Swap implements sort.Interface for DecCoins +func (coins DecCoins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } // Sort is a helper function to sort the set of decimal coins in-place. func (coins DecCoins) Sort() DecCoins { @@ -619,9 +634,12 @@ func ParseDecCoin(coinStr string) (coin DecCoin, err error) { return NewDecCoinFromDec(denomStr, amount), nil } -// ParseDecCoins will parse out a list of decimal coins separated by commas. -// If nothing is provided, it returns nil DecCoins. Returned decimal coins are -// sorted. +// ParseDecCoins will parse out a list of decimal coins separated by commas. If the parsing is successuful, +// the provided coins will be sanitized by removing zero coins and sorting the coin set. Lastly +// a validation of the coin set is executed. If the check passes, ParseDecCoins will return the sanitized coins. +// Otherwise it will return an error. +// If an empty string is provided to ParseDecCoins, it returns nil Coins. +// Expected format: "{amount0}{denomination},...,{amountN}{denominationN}" func ParseDecCoins(coinsStr string) (DecCoins, error) { coinsStr = strings.TrimSpace(coinsStr) if len(coinsStr) == 0 { @@ -629,23 +647,20 @@ func ParseDecCoins(coinsStr string) (DecCoins, error) { } coinStrs := strings.Split(coinsStr, ",") - coins := make(DecCoins, len(coinStrs)) + decCoins := make(DecCoins, len(coinStrs)) for i, coinStr := range coinStrs { coin, err := ParseDecCoin(coinStr) if err != nil { return nil, err } - coins[i] = coin + decCoins[i] = coin } - // sort coins for determinism - coins.Sort() - - // validate coins before returning - if !coins.IsValid() { - return nil, fmt.Errorf("parsed decimal coins are invalid: %#v", coins) + newDecCoins := sanitizeDecCoins(decCoins) + if err := newDecCoins.Validate(); err != nil { + return nil, err } - return coins, nil + return newDecCoins, nil } diff --git a/types/dec_coin_test.go b/types/dec_coin_test.go index 2a20bd6469f4..938f7dddffb4 100644 --- a/types/dec_coin_test.go +++ b/types/dec_coin_test.go @@ -1,119 +1,186 @@ -package types +package types_test import ( "strings" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestNewDecCoin(t *testing.T) { - require.NotPanics(t, func() { - NewInt64DecCoin(testDenom1, 5) +type decCoinTestSuite struct { + suite.Suite +} + +func TestDecCoinTestSuite(t *testing.T) { + suite.Run(t, new(decCoinTestSuite)) +} + +func (s *decCoinTestSuite) TestNewDecCoin() { + s.Require().NotPanics(func() { + sdk.NewInt64DecCoin(testDenom1, 5) }) - require.NotPanics(t, func() { - NewInt64DecCoin(testDenom1, 0) + s.Require().NotPanics(func() { + sdk.NewInt64DecCoin(testDenom1, 0) }) - require.Panics(t, func() { - NewInt64DecCoin(strings.ToUpper(testDenom1), 5) + s.Require().NotPanics(func() { + sdk.NewInt64DecCoin(strings.ToUpper(testDenom1), 5) }) - require.Panics(t, func() { - NewInt64DecCoin(testDenom1, -5) + s.Require().Panics(func() { + sdk.NewInt64DecCoin(testDenom1, -5) }) } -func TestNewDecCoinFromDec(t *testing.T) { - require.NotPanics(t, func() { - NewDecCoinFromDec(testDenom1, NewDec(5)) +func (s *decCoinTestSuite) TestNewDecCoinFromDec() { + s.Require().NotPanics(func() { + sdk.NewDecCoinFromDec(testDenom1, sdk.NewDec(5)) }) - require.NotPanics(t, func() { - NewDecCoinFromDec(testDenom1, ZeroDec()) + s.Require().NotPanics(func() { + sdk.NewDecCoinFromDec(testDenom1, sdk.ZeroDec()) }) - require.Panics(t, func() { - NewDecCoinFromDec(strings.ToUpper(testDenom1), NewDec(5)) + s.Require().NotPanics(func() { + sdk.NewDecCoinFromDec(strings.ToUpper(testDenom1), sdk.NewDec(5)) }) - require.Panics(t, func() { - NewDecCoinFromDec(testDenom1, NewDec(-5)) + s.Require().Panics(func() { + sdk.NewDecCoinFromDec(testDenom1, sdk.NewDec(-5)) }) } -func TestNewDecCoinFromCoin(t *testing.T) { - require.NotPanics(t, func() { - NewDecCoinFromCoin(Coin{testDenom1, NewInt(5)}) +func (s *decCoinTestSuite) TestNewDecCoinFromCoin() { + s.Require().NotPanics(func() { + sdk.NewDecCoinFromCoin(sdk.Coin{testDenom1, sdk.NewInt(5)}) }) - require.NotPanics(t, func() { - NewDecCoinFromCoin(Coin{testDenom1, NewInt(0)}) + s.Require().NotPanics(func() { + sdk.NewDecCoinFromCoin(sdk.Coin{testDenom1, sdk.NewInt(0)}) }) - require.Panics(t, func() { - NewDecCoinFromCoin(Coin{strings.ToUpper(testDenom1), NewInt(5)}) + s.Require().NotPanics(func() { + sdk.NewDecCoinFromCoin(sdk.Coin{strings.ToUpper(testDenom1), sdk.NewInt(5)}) }) - require.Panics(t, func() { - NewDecCoinFromCoin(Coin{testDenom1, NewInt(-5)}) + s.Require().Panics(func() { + sdk.NewDecCoinFromCoin(sdk.Coin{testDenom1, sdk.NewInt(-5)}) }) } -func TestDecCoinIsPositive(t *testing.T) { - dc := NewInt64DecCoin(testDenom1, 5) - require.True(t, dc.IsPositive()) +func (s *decCoinTestSuite) TestDecCoinIsPositive() { + dc := sdk.NewInt64DecCoin(testDenom1, 5) + s.Require().True(dc.IsPositive()) - dc = NewInt64DecCoin(testDenom1, 0) - require.False(t, dc.IsPositive()) + dc = sdk.NewInt64DecCoin(testDenom1, 0) + s.Require().False(dc.IsPositive()) } -func TestAddDecCoin(t *testing.T) { - decCoinA1 := NewDecCoinFromDec(testDenom1, NewDecWithPrec(11, 1)) - decCoinA2 := NewDecCoinFromDec(testDenom1, NewDecWithPrec(22, 1)) - decCoinB1 := NewDecCoinFromDec(testDenom2, NewDecWithPrec(11, 1)) +func (s *decCoinTestSuite) TestAddDecCoin() { + decCoinA1 := sdk.NewDecCoinFromDec(testDenom1, sdk.NewDecWithPrec(11, 1)) + decCoinA2 := sdk.NewDecCoinFromDec(testDenom1, sdk.NewDecWithPrec(22, 1)) + decCoinB1 := sdk.NewDecCoinFromDec(testDenom2, sdk.NewDecWithPrec(11, 1)) // regular add res := decCoinA1.Add(decCoinA1) - require.Equal(t, decCoinA2, res, "sum of coins is incorrect") + s.Require().Equal(decCoinA2, res, "sum of coins is incorrect") // bad denom add - require.Panics(t, func() { + s.Require().Panics(func() { decCoinA1.Add(decCoinB1) }, "expected panic on sum of different denoms") } -func TestAddDecCoins(t *testing.T) { - one := NewDec(1) - zero := NewDec(0) - two := NewDec(2) +func (s *decCoinTestSuite) TestAddDecCoins() { + one := sdk.NewDec(1) + zero := sdk.NewDec(0) + two := sdk.NewDec(2) cases := []struct { - inputOne DecCoins - inputTwo DecCoins - expected DecCoins + inputOne sdk.DecCoins + inputTwo sdk.DecCoins + expected sdk.DecCoins }{ - {DecCoins{{testDenom1, one}, {testDenom2, one}}, DecCoins{{testDenom1, one}, {testDenom2, one}}, DecCoins{{testDenom1, two}, {testDenom2, two}}}, - {DecCoins{{testDenom1, zero}, {testDenom2, one}}, DecCoins{{testDenom1, zero}, {testDenom2, zero}}, DecCoins{{testDenom2, one}}}, - {DecCoins{{testDenom1, zero}, {testDenom2, zero}}, DecCoins{{testDenom1, zero}, {testDenom2, zero}}, DecCoins(nil)}, + {sdk.DecCoins{{testDenom1, one}, {testDenom2, one}}, sdk.DecCoins{{testDenom1, one}, {testDenom2, one}}, sdk.DecCoins{{testDenom1, two}, {testDenom2, two}}}, + {sdk.DecCoins{{testDenom1, zero}, {testDenom2, one}}, sdk.DecCoins{{testDenom1, zero}, {testDenom2, zero}}, sdk.DecCoins{{testDenom2, one}}}, + {sdk.DecCoins{{testDenom1, zero}, {testDenom2, zero}}, sdk.DecCoins{{testDenom1, zero}, {testDenom2, zero}}, sdk.DecCoins(nil)}, } for tcIndex, tc := range cases { res := tc.inputOne.Add(tc.inputTwo...) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) + s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) } } -func TestIsValid(t *testing.T) { +func (s *decCoinTestSuite) TestFilteredZeroDecCoins() { + cases := []struct { + name string + input sdk.DecCoins + original string + expected string + }{ + { + name: "all greater than zero", + input: sdk.DecCoins{ + {"testa", sdk.NewDec(1)}, + {"testb", sdk.NewDec(2)}, + {"testc", sdk.NewDec(3)}, + {"testd", sdk.NewDec(4)}, + {"teste", sdk.NewDec(5)}, + }, + original: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + expected: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + }, + { + name: "zero coin in middle", + input: sdk.DecCoins{ + {"testa", sdk.NewDec(1)}, + {"testb", sdk.NewDec(2)}, + {"testc", sdk.NewDec(0)}, + {"testd", sdk.NewDec(4)}, + {"teste", sdk.NewDec(5)}, + }, + original: "1.000000000000000000testa,2.000000000000000000testb,0.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + expected: "1.000000000000000000testa,2.000000000000000000testb,4.000000000000000000testd,5.000000000000000000teste", + }, + { + name: "zero coin end (unordered)", + input: sdk.DecCoins{ + {"teste", sdk.NewDec(5)}, + {"testc", sdk.NewDec(3)}, + {"testa", sdk.NewDec(1)}, + {"testd", sdk.NewDec(4)}, + {"testb", sdk.NewDec(0)}, + }, + original: "5.000000000000000000teste,3.000000000000000000testc,1.000000000000000000testa,4.000000000000000000testd,0.000000000000000000testb", + expected: "1.000000000000000000testa,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", + }, + } + + for _, tt := range cases { + undertest := sdk.NewDecCoins(tt.input...) + s.Require().Equal(tt.expected, undertest.String(), "NewDecCoins must return expected results") + s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original") + } +} + +func (s *decCoinTestSuite) TestIsValid() { tests := []struct { - coin DecCoin + coin sdk.DecCoin expectPass bool msg string }{ { - NewDecCoin("mytoken", NewInt(10)), + sdk.NewDecCoin("mytoken", sdk.NewInt(10)), true, "valid coins should have passed", }, { - DecCoin{Denom: "BTC", Amount: NewDec(10)}, - false, - "invalid denoms", + sdk.DecCoin{Denom: "BTC", Amount: sdk.NewDec(10)}, + true, + "valid uppercase denom", }, { - DecCoin{Denom: "BTC", Amount: NewDec(-10)}, + sdk.DecCoin{Denom: "Bitcoin", Amount: sdk.NewDec(10)}, + true, + "valid mixed case denom", + }, + { + sdk.DecCoin{Denom: "btc", Amount: sdk.NewDec(-10)}, false, "negative amount", }, @@ -122,203 +189,224 @@ func TestIsValid(t *testing.T) { for _, tc := range tests { tc := tc if tc.expectPass { - require.True(t, tc.coin.IsValid(), tc.msg) + s.Require().True(tc.coin.IsValid(), tc.msg) } else { - require.False(t, tc.coin.IsValid(), tc.msg) + s.Require().False(tc.coin.IsValid(), tc.msg) } } } -func TestSubDecCoin(t *testing.T) { +func (s *decCoinTestSuite) TestSubDecCoin() { tests := []struct { - coin DecCoin + coin sdk.DecCoin expectPass bool msg string }{ { - NewDecCoin("mytoken", NewInt(20)), + sdk.NewDecCoin("mytoken", sdk.NewInt(20)), true, "valid coins should have passed", }, { - NewDecCoin("othertoken", NewInt(20)), + sdk.NewDecCoin("othertoken", sdk.NewInt(20)), false, "denom mismatch", }, { - NewDecCoin("mytoken", NewInt(9)), + sdk.NewDecCoin("mytoken", sdk.NewInt(9)), false, "negative amount", }, } - decCoin := NewDecCoin("mytoken", NewInt(10)) + decCoin := sdk.NewDecCoin("mytoken", sdk.NewInt(10)) for _, tc := range tests { tc := tc if tc.expectPass { equal := tc.coin.Sub(decCoin) - require.Equal(t, equal, decCoin, tc.msg) + s.Require().Equal(equal, decCoin, tc.msg) } else { - require.Panics(t, func() { tc.coin.Sub(decCoin) }, tc.msg) + s.Require().Panics(func() { tc.coin.Sub(decCoin) }, tc.msg) } } } -func TestSubDecCoins(t *testing.T) { +func (s *decCoinTestSuite) TestSubDecCoins() { tests := []struct { - coins DecCoins + coins sdk.DecCoins expectPass bool msg string }{ { - NewDecCoinsFromCoins(NewCoin("mytoken", NewInt(10)), NewCoin("btc", NewInt(20)), NewCoin("eth", NewInt(30))), + sdk.NewDecCoinsFromCoins(sdk.NewCoin("mytoken", sdk.NewInt(10)), sdk.NewCoin("btc", sdk.NewInt(20)), sdk.NewCoin("eth", sdk.NewInt(30))), true, "sorted coins should have passed", }, { - DecCoins{NewDecCoin("mytoken", NewInt(10)), NewDecCoin("btc", NewInt(20)), NewDecCoin("eth", NewInt(30))}, + sdk.DecCoins{sdk.NewDecCoin("mytoken", sdk.NewInt(10)), sdk.NewDecCoin("btc", sdk.NewInt(20)), sdk.NewDecCoin("eth", sdk.NewInt(30))}, false, "unorted coins should panic", }, { - DecCoins{DecCoin{Denom: "BTC", Amount: NewDec(10)}, NewDecCoin("eth", NewInt(15)), NewDecCoin("mytoken", NewInt(5))}, + sdk.DecCoins{sdk.DecCoin{Denom: "BTC", Amount: sdk.NewDec(10)}, sdk.NewDecCoin("eth", sdk.NewInt(15)), sdk.NewDecCoin("mytoken", sdk.NewInt(5))}, false, "invalid denoms", }, } - decCoins := NewDecCoinsFromCoins(NewCoin("btc", NewInt(10)), NewCoin("eth", NewInt(15)), NewCoin("mytoken", NewInt(5))) + decCoins := sdk.NewDecCoinsFromCoins(sdk.NewCoin("btc", sdk.NewInt(10)), sdk.NewCoin("eth", sdk.NewInt(15)), sdk.NewCoin("mytoken", sdk.NewInt(5))) for _, tc := range tests { tc := tc if tc.expectPass { equal := tc.coins.Sub(decCoins) - require.Equal(t, equal, decCoins, tc.msg) + s.Require().Equal(equal, decCoins, tc.msg) } else { - require.Panics(t, func() { tc.coins.Sub(decCoins) }, tc.msg) + s.Require().Panics(func() { tc.coins.Sub(decCoins) }, tc.msg) } } } -func TestSortDecCoins(t *testing.T) { - good := DecCoins{ - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("mineral", 1), - NewInt64DecCoin("tree", 1), +func (s *decCoinTestSuite) TestSortDecCoins() { + good := sdk.DecCoins{ + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("mineral", 1), + sdk.NewInt64DecCoin("tree", 1), } - empty := DecCoins{ - NewInt64DecCoin("gold", 0), + empty := sdk.DecCoins{ + sdk.NewInt64DecCoin("gold", 0), } - badSort1 := DecCoins{ - NewInt64DecCoin("tree", 1), - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("mineral", 1), + badSort1 := sdk.DecCoins{ + sdk.NewInt64DecCoin("tree", 1), + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("mineral", 1), } - badSort2 := DecCoins{ // both are after the first one, but the second and third are in the wrong order - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("tree", 1), - NewInt64DecCoin("mineral", 1), + badSort2 := sdk.DecCoins{ // both are after the first one, but the second and third are in the wrong order + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("tree", 1), + sdk.NewInt64DecCoin("mineral", 1), } - badAmt := DecCoins{ - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("tree", 0), - NewInt64DecCoin("mineral", 1), + badAmt := sdk.DecCoins{ + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("tree", 0), + sdk.NewInt64DecCoin("mineral", 1), } - dup := DecCoins{ - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("gas", 1), - NewInt64DecCoin("mineral", 1), + dup := sdk.DecCoins{ + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("gas", 1), + sdk.NewInt64DecCoin("mineral", 1), } - cases := []struct { - coins DecCoins + name string + coins sdk.DecCoins before, after bool // valid before/after sort }{ - {good, true, true}, - {empty, false, false}, - {badSort1, false, true}, - {badSort2, false, true}, - {badAmt, false, false}, - {dup, false, false}, + {"valid coins", good, true, true}, + {"empty coins", empty, false, false}, + {"unsorted coins (1)", badSort1, false, true}, + {"unsorted coins (2)", badSort2, false, true}, + {"zero amount coins", badAmt, false, false}, + {"duplicate coins", dup, false, false}, } - for tcIndex, tc := range cases { - require.Equal(t, tc.before, tc.coins.IsValid(), "coin validity is incorrect before sorting, tc #%d", tcIndex) + for _, tc := range cases { + s.Require().Equal(tc.before, tc.coins.IsValid(), "coin validity is incorrect before sorting; %s", tc.name) tc.coins.Sort() - require.Equal(t, tc.after, tc.coins.IsValid(), "coin validity is incorrect after sorting, tc #%d", tcIndex) + s.Require().Equal(tc.after, tc.coins.IsValid(), "coin validity is incorrect after sorting; %s", tc.name) } } -func TestDecCoinsIsValid(t *testing.T) { +func (s *decCoinTestSuite) TestDecCoinsValidate() { testCases := []struct { - input DecCoins - expected bool + input sdk.DecCoins + expectedPass bool }{ - {DecCoins{}, true}, - {DecCoins{DecCoin{testDenom1, NewDec(5)}}, true}, - {DecCoins{DecCoin{testDenom1, NewDec(5)}, DecCoin{testDenom2, NewDec(100000)}}, true}, - {DecCoins{DecCoin{testDenom1, NewDec(-5)}}, false}, - {DecCoins{DecCoin{"AAA", NewDec(5)}}, false}, - {DecCoins{DecCoin{testDenom1, NewDec(5)}, DecCoin{"B", NewDec(100000)}}, false}, - {DecCoins{DecCoin{testDenom1, NewDec(5)}, DecCoin{testDenom2, NewDec(-100000)}}, false}, - {DecCoins{DecCoin{testDenom1, NewDec(-5)}, DecCoin{testDenom2, NewDec(100000)}}, false}, - {DecCoins{DecCoin{"AAA", NewDec(5)}, DecCoin{testDenom2, NewDec(100000)}}, false}, + {sdk.DecCoins{}, true}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(5)}}, true}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(5)}, sdk.DecCoin{testDenom2, sdk.NewDec(100000)}}, true}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(-5)}}, false}, + {sdk.DecCoins{sdk.DecCoin{"BTC", sdk.NewDec(5)}}, true}, + {sdk.DecCoins{sdk.DecCoin{"0BTC", sdk.NewDec(5)}}, false}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(5)}, sdk.DecCoin{"B", sdk.NewDec(100000)}}, false}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(5)}, sdk.DecCoin{testDenom2, sdk.NewDec(-100000)}}, false}, + {sdk.DecCoins{sdk.DecCoin{testDenom1, sdk.NewDec(-5)}, sdk.DecCoin{testDenom2, sdk.NewDec(100000)}}, false}, + {sdk.DecCoins{sdk.DecCoin{"BTC", sdk.NewDec(5)}, sdk.DecCoin{testDenom2, sdk.NewDec(100000)}}, true}, + {sdk.DecCoins{sdk.DecCoin{"0BTC", sdk.NewDec(5)}, sdk.DecCoin{testDenom2, sdk.NewDec(100000)}}, false}, } for i, tc := range testCases { - res := tc.input.IsValid() - require.Equal(t, tc.expected, res, "unexpected result for test case #%d, input: %v", i, tc.input) + err := tc.input.Validate() + if tc.expectedPass { + s.Require().NoError(err, "unexpected result for test case #%d, input: %v", i, tc.input) + } else { + s.Require().Error(err, "unexpected result for test case #%d, input: %v", i, tc.input) + } } } -func TestParseDecCoins(t *testing.T) { +func (s *decCoinTestSuite) TestParseDecCoins() { testCases := []struct { input string - expectedResult DecCoins + expectedResult sdk.DecCoins expectedErr bool }{ {"", nil, false}, - {"4stake", nil, true}, - {"5.5atom,4stake", nil, true}, - {"0.0stake", nil, true}, - {"0.004STAKE", nil, true}, + {"4stake", sdk.DecCoins{sdk.NewDecCoinFromDec("stake", sdk.NewDecFromInt(sdk.NewInt(4)))}, false}, + {"5.5atom,4stake", sdk.DecCoins{ + sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5500000000000000000, sdk.Precision)), + sdk.NewDecCoinFromDec("stake", sdk.NewDec(4)), + }, false}, + {"0.0stake", sdk.DecCoins{}, false}, // remove zero coins + {"10.0btc,1.0atom,20.0btc", nil, true}, + { + "0.004STAKE", + sdk.DecCoins{sdk.NewDecCoinFromDec("STAKE", sdk.NewDecWithPrec(4000000000000000, sdk.Precision))}, + false, + }, { "0.004stake", - DecCoins{NewDecCoinFromDec("stake", NewDecWithPrec(4000000000000000, Precision))}, + sdk.DecCoins{sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(4000000000000000, sdk.Precision))}, false, }, { "5.04atom,0.004stake", - DecCoins{ - NewDecCoinFromDec("atom", NewDecWithPrec(5040000000000000000, Precision)), - NewDecCoinFromDec("stake", NewDecWithPrec(4000000000000000, Precision)), + sdk.DecCoins{ + sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5040000000000000000, sdk.Precision)), + sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(4000000000000000, sdk.Precision)), + }, + false, + }, + {"0.0stake,0.004stake,5.04atom", // remove zero coins + sdk.DecCoins{ + sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5040000000000000000, sdk.Precision)), + sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(4000000000000000, sdk.Precision)), }, false, }, } for i, tc := range testCases { - res, err := ParseDecCoins(tc.input) + res, err := sdk.ParseDecCoins(tc.input) if tc.expectedErr { - require.Error(t, err, "expected error for test case #%d, input: %v", i, tc.input) + s.Require().Error(err, "expected error for test case #%d, input: %v", i, tc.input) } else { - require.NoError(t, err, "unexpected error for test case #%d, input: %v", i, tc.input) - require.Equal(t, tc.expectedResult, res, "unexpected result for test case #%d, input: %v", i, tc.input) + s.Require().NoError(err, "unexpected error for test case #%d, input: %v", i, tc.input) + s.Require().Equal(tc.expectedResult, res, "unexpected result for test case #%d, input: %v", i, tc.input) } } } -func TestDecCoinsString(t *testing.T) { +func (s *decCoinTestSuite) TestDecCoinsString() { testCases := []struct { - input DecCoins + input sdk.DecCoins expected string }{ - {DecCoins{}, ""}, + {sdk.DecCoins{}, ""}, { - DecCoins{ - NewDecCoinFromDec("atom", NewDecWithPrec(5040000000000000000, Precision)), - NewDecCoinFromDec("stake", NewDecWithPrec(4000000000000000, Precision)), + sdk.DecCoins{ + sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5040000000000000000, sdk.Precision)), + sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(4000000000000000, sdk.Precision)), }, "5.040000000000000000atom,0.004000000000000000stake", }, @@ -326,11 +414,11 @@ func TestDecCoinsString(t *testing.T) { for i, tc := range testCases { out := tc.input.String() - require.Equal(t, tc.expected, out, "unexpected result for test case #%d, input: %v", i, tc.input) + s.Require().Equal(tc.expected, out, "unexpected result for test case #%d, input: %v", i, tc.input) } } -func TestDecCoinsIntersect(t *testing.T) { +func (s *decCoinTestSuite) TestDecCoinsIntersect() { testCases := []struct { input1 string input2 string @@ -350,89 +438,88 @@ func TestDecCoinsIntersect(t *testing.T) { } for i, tc := range testCases { - in1, err := ParseDecCoins(tc.input1) - require.NoError(t, err, "unexpected parse error in %v", i) - in2, err := ParseDecCoins(tc.input2) - require.NoError(t, err, "unexpected parse error in %v", i) - exr, err := ParseDecCoins(tc.expectedResult) - require.NoError(t, err, "unexpected parse error in %v", i) - - require.True(t, in1.Intersect(in2).IsEqual(exr), "in1.cap(in2) != exr in %v", i) + in1, err := sdk.ParseDecCoins(tc.input1) + s.Require().NoError(err, "unexpected parse error in %v", i) + in2, err := sdk.ParseDecCoins(tc.input2) + s.Require().NoError(err, "unexpected parse error in %v", i) + exr, err := sdk.ParseDecCoins(tc.expectedResult) + s.Require().NoError(err, "unexpected parse error in %v", i) + s.Require().True(in1.Intersect(in2).IsEqual(exr), "in1.cap(in2) != exr in %v", i) } } -func TestDecCoinsTruncateDecimal(t *testing.T) { - decCoinA := NewDecCoinFromDec("bar", MustNewDecFromStr("5.41")) - decCoinB := NewDecCoinFromDec("foo", MustNewDecFromStr("6.00")) +func (s *decCoinTestSuite) TestDecCoinsTruncateDecimal() { + decCoinA := sdk.NewDecCoinFromDec("bar", sdk.MustNewDecFromStr("5.41")) + decCoinB := sdk.NewDecCoinFromDec("foo", sdk.MustNewDecFromStr("6.00")) testCases := []struct { - input DecCoins - truncatedCoins Coins - changeCoins DecCoins + input sdk.DecCoins + truncatedCoins sdk.Coins + changeCoins sdk.DecCoins }{ - {DecCoins{}, Coins(nil), DecCoins(nil)}, + {sdk.DecCoins{}, sdk.Coins(nil), sdk.DecCoins(nil)}, { - DecCoins{decCoinA, decCoinB}, - Coins{NewInt64Coin(decCoinA.Denom, 5), NewInt64Coin(decCoinB.Denom, 6)}, - DecCoins{NewDecCoinFromDec(decCoinA.Denom, MustNewDecFromStr("0.41"))}, + sdk.DecCoins{decCoinA, decCoinB}, + sdk.Coins{sdk.NewInt64Coin(decCoinA.Denom, 5), sdk.NewInt64Coin(decCoinB.Denom, 6)}, + sdk.DecCoins{sdk.NewDecCoinFromDec(decCoinA.Denom, sdk.MustNewDecFromStr("0.41"))}, }, { - DecCoins{decCoinB}, - Coins{NewInt64Coin(decCoinB.Denom, 6)}, - DecCoins(nil), + sdk.DecCoins{decCoinB}, + sdk.Coins{sdk.NewInt64Coin(decCoinB.Denom, 6)}, + sdk.DecCoins(nil), }, } for i, tc := range testCases { truncatedCoins, changeCoins := tc.input.TruncateDecimal() - require.Equal( - t, tc.truncatedCoins, truncatedCoins, + s.Require().Equal( + tc.truncatedCoins, truncatedCoins, "unexpected truncated coins; tc #%d, input: %s", i, tc.input, ) - require.Equal( - t, tc.changeCoins, changeCoins, + s.Require().Equal( + tc.changeCoins, changeCoins, "unexpected change coins; tc #%d, input: %s", i, tc.input, ) } } -func TestDecCoinsQuoDecTruncate(t *testing.T) { - x := MustNewDecFromStr("1.00") - y := MustNewDecFromStr("10000000000000000000.00") +func (s *decCoinTestSuite) TestDecCoinsQuoDecTruncate() { + x := sdk.MustNewDecFromStr("1.00") + y := sdk.MustNewDecFromStr("10000000000000000000.00") testCases := []struct { - coins DecCoins - input Dec - result DecCoins + coins sdk.DecCoins + input sdk.Dec + result sdk.DecCoins panics bool }{ - {DecCoins{}, ZeroDec(), DecCoins(nil), true}, - {DecCoins{NewDecCoinFromDec("foo", x)}, y, DecCoins(nil), false}, - {DecCoins{NewInt64DecCoin("foo", 5)}, NewDec(2), DecCoins{NewDecCoinFromDec("foo", MustNewDecFromStr("2.5"))}, false}, + {sdk.DecCoins{}, sdk.ZeroDec(), sdk.DecCoins(nil), true}, + {sdk.DecCoins{sdk.NewDecCoinFromDec("foo", x)}, y, sdk.DecCoins(nil), false}, + {sdk.DecCoins{sdk.NewInt64DecCoin("foo", 5)}, sdk.NewDec(2), sdk.DecCoins{sdk.NewDecCoinFromDec("foo", sdk.MustNewDecFromStr("2.5"))}, false}, } for i, tc := range testCases { tc := tc if tc.panics { - require.Panics(t, func() { tc.coins.QuoDecTruncate(tc.input) }) + s.Require().Panics(func() { tc.coins.QuoDecTruncate(tc.input) }) } else { res := tc.coins.QuoDecTruncate(tc.input) - require.Equal(t, tc.result, res, "unexpected result; tc #%d, coins: %s, input: %s", i, tc.coins, tc.input) + s.Require().Equal(tc.result, res, "unexpected result; tc #%d, coins: %s, input: %s", i, tc.coins, tc.input) } } } -func TestNewDecCoinsWithIsValid(t *testing.T) { - fake1 := append(NewDecCoins(NewDecCoin("mytoken", NewInt(10))), DecCoin{Denom: "BTC", Amount: NewDec(10)}) - fake2 := append(NewDecCoins(NewDecCoin("mytoken", NewInt(10))), DecCoin{Denom: "BTC", Amount: NewDec(-10)}) +func (s *decCoinTestSuite) TestNewDecCoinsWithIsValid() { + fake1 := append(sdk.NewDecCoins(sdk.NewDecCoin("mytoken", sdk.NewInt(10))), sdk.DecCoin{Denom: "10BTC", Amount: sdk.NewDec(10)}) + fake2 := append(sdk.NewDecCoins(sdk.NewDecCoin("mytoken", sdk.NewInt(10))), sdk.DecCoin{Denom: "BTC", Amount: sdk.NewDec(-10)}) tests := []struct { - coin DecCoins + coin sdk.DecCoins expectPass bool msg string }{ { - NewDecCoins(NewDecCoin("mytoken", NewInt(10))), + sdk.NewDecCoins(sdk.NewDecCoin("mytoken", sdk.NewInt(10))), true, "valid coins should have passed", }, @@ -451,34 +538,34 @@ func TestNewDecCoinsWithIsValid(t *testing.T) { for _, tc := range tests { tc := tc if tc.expectPass { - require.True(t, tc.coin.IsValid(), tc.msg) + s.Require().True(tc.coin.IsValid(), tc.msg) } else { - require.False(t, tc.coin.IsValid(), tc.msg) + s.Require().False(tc.coin.IsValid(), tc.msg) } } } -func TestDecCoins_AddDecCoinWithIsValid(t *testing.T) { - lengthTestDecCoins := NewDecCoins().Add(NewDecCoin("mytoken", NewInt(10))).Add(DecCoin{Denom: "BTC", Amount: NewDec(10)}) - require.Equal(t, 2, len(lengthTestDecCoins), "should be 2") +func (s *decCoinTestSuite) TestDecCoins_AddDecCoinWithIsValid() { + lengthTestDecCoins := sdk.NewDecCoins().Add(sdk.NewDecCoin("mytoken", sdk.NewInt(10))).Add(sdk.DecCoin{Denom: "BTC", Amount: sdk.NewDec(10)}) + s.Require().Equal(2, len(lengthTestDecCoins), "should be 2") tests := []struct { - coin DecCoins + coin sdk.DecCoins expectPass bool msg string }{ { - NewDecCoins().Add(NewDecCoin("mytoken", NewInt(10))), + sdk.NewDecCoins().Add(sdk.NewDecCoin("mytoken", sdk.NewInt(10))), true, "valid coins should have passed", }, { - NewDecCoins().Add(NewDecCoin("mytoken", NewInt(10))).Add(DecCoin{Denom: "BTC", Amount: NewDec(10)}), + sdk.NewDecCoins().Add(sdk.NewDecCoin("mytoken", sdk.NewInt(10))).Add(sdk.DecCoin{Denom: "0BTC", Amount: sdk.NewDec(10)}), false, "invalid denoms", }, { - NewDecCoins().Add(NewDecCoin("mytoken", NewInt(10))).Add(DecCoin{Denom: "BTC", Amount: NewDec(-10)}), + sdk.NewDecCoins().Add(sdk.NewDecCoin("mytoken", sdk.NewInt(10))).Add(sdk.DecCoin{Denom: "BTC", Amount: sdk.NewDec(-10)}), false, "negative amount", }, @@ -487,9 +574,9 @@ func TestDecCoins_AddDecCoinWithIsValid(t *testing.T) { for _, tc := range tests { tc := tc if tc.expectPass { - require.True(t, tc.coin.IsValid(), tc.msg) + s.Require().True(tc.coin.IsValid(), tc.msg) } else { - require.False(t, tc.coin.IsValid(), tc.msg) + s.Require().False(tc.coin.IsValid(), tc.msg) } } } diff --git a/types/decimal.go b/types/decimal.go index 839523080656..6db1f587de7a 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -10,19 +10,24 @@ import ( "testing" ) +var _ CustomProtobufType = (*Dec)(nil) + // NOTE: never use new(Dec) or else we will panic unmarshalling into the // nil embedded big.Int type Dec struct { - *big.Int `json:"int"` + i *big.Int } -// number of decimal places const ( + // number of decimal places Precision = 18 // bytes required to represent the above precision // Ceiling[Log2[999 999 999 999 999 999]] DecimalPrecisionBits = 60 + + // max number of iterations in ApproxRoot function + maxApproxRootIterations = 100 ) var ( @@ -157,7 +162,6 @@ func NewDecFromStr(str string) (Dec, error) { return Dec{}, ErrInvalidDecimalLength } combinedStr += strs[1] - } else if len(strs) > 2 { return Dec{}, ErrInvalidDecimalStr } @@ -175,6 +179,9 @@ func NewDecFromStr(str string) (Dec, error) { if !ok { return Dec{}, fmt.Errorf("failed to set decimal string: %s", combinedStr) } + if combined.BitLen() > maxBitLen { + return Dec{}, fmt.Errorf("decimal out of range; bitLen: got %d, max %d", combined.BitLen(), maxBitLen) + } if neg { combined = new(big.Int).Neg(combined) } @@ -193,21 +200,31 @@ func MustNewDecFromStr(s string) Dec { //______________________________________________________________________________________________ //nolint -func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil -func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero -func (d Dec) IsNegative() bool { return (d.Int).Sign() == -1 } // is negative -func (d Dec) IsPositive() bool { return (d.Int).Sign() == 1 } // is positive -func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals -func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than -func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal -func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than -func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal -func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign -func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.Int)} } // absolute value +func (d Dec) IsNil() bool { return d.i == nil } // is decimal nil +func (d Dec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero +func (d Dec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative +func (d Dec) IsPositive() bool { return (d.i).Sign() == 1 } // is positive +func (d Dec) Equal(d2 Dec) bool { return (d.i).Cmp(d2.i) == 0 } // equal decimals +func (d Dec) GT(d2 Dec) bool { return (d.i).Cmp(d2.i) > 0 } // greater than +func (d Dec) GTE(d2 Dec) bool { return (d.i).Cmp(d2.i) >= 0 } // greater than or equal +func (d Dec) LT(d2 Dec) bool { return (d.i).Cmp(d2.i) < 0 } // less than +func (d Dec) LTE(d2 Dec) bool { return (d.i).Cmp(d2.i) <= 0 } // less than or equal +func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.i)} } // reverse the decimal sign +func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.i)} } // absolute value + +// BigInt returns a copy of the underlying big.Int. +func (d Dec) BigInt() *big.Int { + if d.IsNil() { + return nil + } + + copy := new(big.Int) + return copy.Set(d.i) +} // addition func (d Dec) Add(d2 Dec) Dec { - res := new(big.Int).Add(d.Int, d2.Int) + res := new(big.Int).Add(d.i, d2.i) if res.BitLen() > 255+DecimalPrecisionBits { panic("Int overflow") @@ -217,7 +234,7 @@ func (d Dec) Add(d2 Dec) Dec { // subtraction func (d Dec) Sub(d2 Dec) Dec { - res := new(big.Int).Sub(d.Int, d2.Int) + res := new(big.Int).Sub(d.i, d2.i) if res.BitLen() > 255+DecimalPrecisionBits { panic("Int overflow") @@ -227,7 +244,7 @@ func (d Dec) Sub(d2 Dec) Dec { // multiplication func (d Dec) Mul(d2 Dec) Dec { - mul := new(big.Int).Mul(d.Int, d2.Int) + mul := new(big.Int).Mul(d.i, d2.i) chopped := chopPrecisionAndRound(mul) if chopped.BitLen() > 255+DecimalPrecisionBits { @@ -238,7 +255,7 @@ func (d Dec) Mul(d2 Dec) Dec { // multiplication truncate func (d Dec) MulTruncate(d2 Dec) Dec { - mul := new(big.Int).Mul(d.Int, d2.Int) + mul := new(big.Int).Mul(d.i, d2.i) chopped := chopPrecisionAndTruncate(mul) if chopped.BitLen() > 255+DecimalPrecisionBits { @@ -249,7 +266,7 @@ func (d Dec) MulTruncate(d2 Dec) Dec { // multiplication func (d Dec) MulInt(i Int) Dec { - mul := new(big.Int).Mul(d.Int, i.i) + mul := new(big.Int).Mul(d.i, i.i) if mul.BitLen() > 255+DecimalPrecisionBits { panic("Int overflow") @@ -259,7 +276,7 @@ func (d Dec) MulInt(i Int) Dec { // MulInt64 - multiplication with int64 func (d Dec) MulInt64(i int64) Dec { - mul := new(big.Int).Mul(d.Int, big.NewInt(i)) + mul := new(big.Int).Mul(d.i, big.NewInt(i)) if mul.BitLen() > 255+DecimalPrecisionBits { panic("Int overflow") @@ -270,10 +287,10 @@ func (d Dec) MulInt64(i int64) Dec { // quotient func (d Dec) Quo(d2 Dec) Dec { // multiply precision twice - mul := new(big.Int).Mul(d.Int, precisionReuse) + mul := new(big.Int).Mul(d.i, precisionReuse) mul.Mul(mul, precisionReuse) - quo := new(big.Int).Quo(mul, d2.Int) + quo := new(big.Int).Quo(mul, d2.i) chopped := chopPrecisionAndRound(quo) if chopped.BitLen() > 255+DecimalPrecisionBits { @@ -284,12 +301,11 @@ func (d Dec) Quo(d2 Dec) Dec { // quotient truncate func (d Dec) QuoTruncate(d2 Dec) Dec { - // multiply precision twice - mul := new(big.Int).Mul(d.Int, precisionReuse) + mul := new(big.Int).Mul(d.i, precisionReuse) mul.Mul(mul, precisionReuse) - quo := new(big.Int).Quo(mul, d2.Int) + quo := new(big.Int).Quo(mul, d2.i) chopped := chopPrecisionAndTruncate(quo) if chopped.BitLen() > 255+DecimalPrecisionBits { @@ -301,10 +317,10 @@ func (d Dec) QuoTruncate(d2 Dec) Dec { // quotient, round up func (d Dec) QuoRoundUp(d2 Dec) Dec { // multiply precision twice - mul := new(big.Int).Mul(d.Int, precisionReuse) + mul := new(big.Int).Mul(d.i, precisionReuse) mul.Mul(mul, precisionReuse) - quo := new(big.Int).Quo(mul, d2.Int) + quo := new(big.Int).Quo(mul, d2.i) chopped := chopPrecisionAndRoundUp(quo) if chopped.BitLen() > 255+DecimalPrecisionBits { @@ -315,13 +331,13 @@ func (d Dec) QuoRoundUp(d2 Dec) Dec { // quotient func (d Dec) QuoInt(i Int) Dec { - mul := new(big.Int).Quo(d.Int, i.i) + mul := new(big.Int).Quo(d.i, i.i) return Dec{mul} } // QuoInt64 - quotient with int64 func (d Dec) QuoInt64(i int64) Dec { - mul := new(big.Int).Quo(d.Int, big.NewInt(i)) + mul := new(big.Int).Quo(d.i, big.NewInt(i)) return Dec{mul} } @@ -329,6 +345,8 @@ func (d Dec) QuoInt64(i int64) Dec { // using Newton's method (where n is positive). The algorithm starts with some guess and // computes the sequence of improved guesses until an answer converges to an // approximate answer. It returns `|d|.ApproxRoot() * -1` if input is negative. +// A maximum number of 100 iterations is used a backup boundary condition for +// cases where the answer never converges enough to satisfy the main condition. func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) { defer func() { if r := recover(); r != nil { @@ -356,7 +374,7 @@ func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) { rootInt := NewIntFromUint64(root) guess, delta := OneDec(), OneDec() - for delta.Abs().GT(SmallestDec()) { + for iter := 0; delta.Abs().GT(SmallestDec()) && iter < maxApproxRootIterations; iter++ { prev := guess.Power(root - 1) if prev.IsZero() { prev = SmallestDec() @@ -377,15 +395,15 @@ func (d Dec) Power(power uint64) Dec { return OneDec() } tmp := OneDec() + for i := power; i > 1; { - if i%2 == 0 { - i /= 2 - } else { + if i%2 != 0 { tmp = tmp.Mul(d) - i = (i - 1) / 2 } + i /= 2 d = d.Mul(d) } + return d.Mul(tmp) } @@ -397,7 +415,7 @@ func (d Dec) ApproxSqrt() (Dec, error) { // is integer, e.g. decimals are zero func (d Dec) IsInteger() bool { - return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 + return new(big.Int).Rem(d.i, precisionReuse).Sign() == 0 } // format decimal state @@ -409,16 +427,17 @@ func (d Dec) Format(s fmt.State, verb rune) { } func (d Dec) String() string { - if d.Int == nil { - return d.Int.String() + if d.i == nil { + return d.i.String() } isNeg := d.IsNegative() - if d.IsNegative() { + + if isNeg { d = d.Neg() } - bzInt, err := d.Int.MarshalText() + bzInt, err := d.i.MarshalText() if err != nil { return "" } @@ -442,9 +461,7 @@ func (d Dec) String() string { // set final digits copy(bzStr[2+(Precision-inputSize):], bzInt) - } else { - // inputSize + 1 to account for the decimal point that is being added bzStr = make([]byte, inputSize+1) decPointPlace := inputSize - Precision @@ -475,7 +492,6 @@ func (d Dec) String() string { // // Mutates the input. Use the non-mutative version if that is undesired func chopPrecisionAndRound(d *big.Int) *big.Int { - // remove the negative and add it back when returning if d.Sign() == -1 { // make d positive, compute chopped value, and then un-mutate d @@ -508,7 +524,6 @@ func chopPrecisionAndRound(d *big.Int) *big.Int { } func chopPrecisionAndRoundUp(d *big.Int) *big.Int { - // remove the negative and add it back when returning if d.Sign() == -1 { // make d positive, compute chopped value, and then un-mutate d @@ -537,7 +552,7 @@ func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { // RoundInt64 rounds the decimal using bankers rounding func (d Dec) RoundInt64() int64 { - chopped := chopPrecisionAndRoundNonMutative(d.Int) + chopped := chopPrecisionAndRoundNonMutative(d.i) if !chopped.IsInt64() { panic("Int64() out of bound") } @@ -546,7 +561,7 @@ func (d Dec) RoundInt64() int64 { // RoundInt round the decimal using bankers rounding func (d Dec) RoundInt() Int { - return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.Int)) + return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.i)) } //___________________________________________________________________________________ @@ -563,7 +578,7 @@ func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { // TruncateInt64 truncates the decimals from the number and returns an int64 func (d Dec) TruncateInt64() int64 { - chopped := chopPrecisionAndTruncateNonMutative(d.Int) + chopped := chopPrecisionAndTruncateNonMutative(d.i) if !chopped.IsInt64() { panic("Int64() out of bound") } @@ -572,18 +587,18 @@ func (d Dec) TruncateInt64() int64 { // TruncateInt truncates the decimals from the number and returns an Int func (d Dec) TruncateInt() Int { - return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) + return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.i)) } // TruncateDec truncates the decimals from the number and returns a Dec func (d Dec) TruncateDec() Dec { - return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) + return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.i)) } // Ceil returns the smallest interger value (as a decimal) that is greater than // or equal to the given decimal. func (d Dec) Ceil() Dec { - tmp := new(big.Int).Set(d.Int) + tmp := new(big.Int).Set(d.i) quo, rem := tmp, big.NewInt(0) quo, rem = quo.QuoRem(tmp, precisionReuse, rem) @@ -639,58 +654,26 @@ func SortableDecBytes(dec Dec) []byte { //___________________________________________________________________________________ // reuse nil values -var ( - nilAmino string - nilJSON []byte -) +var nilJSON []byte func init() { empty := new(big.Int) - bz, err := empty.MarshalText() - if err != nil { - panic("bad nil amino init") - } - nilAmino = string(bz) - - nilJSON, err = json.Marshal(string(bz)) - if err != nil { - panic("bad nil json init") - } -} - -// wraps d.MarshalText() -func (d Dec) MarshalAmino() (string, error) { - if d.Int == nil { - return nilAmino, nil - } - bz, err := d.Int.MarshalText() - return string(bz), err -} - -// requires a valid JSON string - strings quotes and calls UnmarshalText -func (d *Dec) UnmarshalAmino(text string) (err error) { - tempInt := new(big.Int) - err = tempInt.UnmarshalText([]byte(text)) - if err != nil { - return err - } - d.Int = tempInt - return nil + bz, _ := empty.MarshalText() + nilJSON, _ = json.Marshal(string(bz)) } // MarshalJSON marshals the decimal func (d Dec) MarshalJSON() ([]byte, error) { - if d.Int == nil { + if d.i == nil { return nilJSON, nil } - return json.Marshal(d.String()) } // UnmarshalJSON defines custom decoding scheme func (d *Dec) UnmarshalJSON(bz []byte) error { - if d.Int == nil { - d.Int = new(big.Int) + if d.i == nil { + d.i = new(big.Int) } var text string @@ -698,17 +681,85 @@ func (d *Dec) UnmarshalJSON(bz []byte) error { if err != nil { return err } + // TODO: Reuse dec allocation newDec, err := NewDecFromStr(text) if err != nil { return err } - d.Int = newDec.Int + + d.i = newDec.i return nil } -// MarshalYAML returns Ythe AML representation. -func (d Dec) MarshalYAML() (interface{}, error) { return d.String(), nil } +// MarshalYAML returns the YAML representation. +func (d Dec) MarshalYAML() (interface{}, error) { + return d.String(), nil +} + +// Marshal implements the gogo proto custom type interface. +func (d Dec) Marshal() ([]byte, error) { + if d.i == nil { + d.i = new(big.Int) + } + return d.i.MarshalText() +} + +// MarshalTo implements the gogo proto custom type interface. +func (d *Dec) MarshalTo(data []byte) (n int, err error) { + if d.i == nil { + d.i = new(big.Int) + } + + if d.i.Cmp(zeroInt) == 0 { + copy(data, []byte{0x30}) + return 1, nil + } + + bz, err := d.Marshal() + if err != nil { + return 0, err + } + + copy(data, bz) + return len(bz), nil +} + +// Unmarshal implements the gogo proto custom type interface. +func (d *Dec) Unmarshal(data []byte) error { + if len(data) == 0 { + d = nil + return nil + } + + if d.i == nil { + d.i = new(big.Int) + } + + if err := d.i.UnmarshalText(data); err != nil { + return err + } + + if d.i.BitLen() > maxBitLen { + return fmt.Errorf("decimal out of range; got: %d, max: %d", d.i.BitLen(), maxBitLen) + } + + return nil +} + +// Size implements the gogo proto custom type interface. +func (d *Dec) Size() int { + bz, _ := d.Marshal() + return len(bz) +} + +// Override Amino binary serialization by proxying to protobuf. +func (d Dec) MarshalAmino() ([]byte, error) { return d.Marshal() } +func (d *Dec) UnmarshalAmino(bz []byte) error { return d.Unmarshal(bz) } + +func (dp DecProto) String() string { + return dp.Dec.String() +} //___________________________________________________________________________________ // helpers diff --git a/types/decimal_internal_test.go b/types/decimal_internal_test.go new file mode 100644 index 000000000000..6127267853ba --- /dev/null +++ b/types/decimal_internal_test.go @@ -0,0 +1,82 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/suite" +) + +type decimalInternalTestSuite struct { + suite.Suite +} + +func TestDecimalInternalTestSuite(t *testing.T) { + suite.Run(t, new(decimalInternalTestSuite)) +} + +func (s *decimalInternalTestSuite) TestPrecisionMultiplier() { + res := precisionMultiplier(5) + exp := big.NewInt(10000000000000) + s.Require().Equal(0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) +} + +func (s *decimalInternalTestSuite) TestZeroDeserializationJSON() { + d := Dec{new(big.Int)} + err := cdc.UnmarshalJSON([]byte(`"0"`), &d) + s.Require().Nil(err) + err = cdc.UnmarshalJSON([]byte(`"{}"`), &d) + s.Require().NotNil(err) +} + +func (s *decimalInternalTestSuite) TestSerializationGocodecJSON() { + d := MustNewDecFromStr("0.333") + + bz, err := cdc.MarshalJSON(d) + s.Require().NoError(err) + + d2 := Dec{new(big.Int)} + err = cdc.UnmarshalJSON(bz, &d2) + s.Require().NoError(err) + s.Require().True(d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) +} + +func (s *decimalInternalTestSuite) TestDecMarshalJSON() { + decimal := func(i int64) Dec { + d := NewDec(0) + d.i = new(big.Int).SetInt64(i) + return d + } + tests := []struct { + name string + d Dec + want string + wantErr bool // if wantErr = false, will also attempt unmarshaling + }{ + {"zero", decimal(0), "\"0.000000000000000000\"", false}, + {"one", decimal(1), "\"0.000000000000000001\"", false}, + {"ten", decimal(10), "\"0.000000000000000010\"", false}, + {"12340", decimal(12340), "\"0.000000000000012340\"", false}, + {"zeroInt", NewDec(0), "\"0.000000000000000000\"", false}, + {"oneInt", NewDec(1), "\"1.000000000000000000\"", false}, + {"tenInt", NewDec(10), "\"10.000000000000000000\"", false}, + {"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false}, + } + for _, tt := range tests { + tt := tt + s.T().Run(tt.name, func(t *testing.T) { + got, err := tt.d.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("Dec.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + s.Require().Equal(tt.want, string(got), "incorrect marshalled value") + unmarshalledDec := NewDec(0) + err := unmarshalledDec.UnmarshalJSON(got) + s.Require().NoError(err) + s.Require().Equal(tt.d, unmarshalledDec, "incorrect unmarshalled value") + } + }) + } +} diff --git a/types/decimal_test.go b/types/decimal_test.go index 48c691a100dc..3eb86d8ec6ae 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -1,191 +1,198 @@ -package types +package types_test import ( + "bytes" + "encoding/json" + "fmt" "math/big" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "gopkg.in/yaml.v2" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" ) +type decimalTestSuite struct { + suite.Suite +} + +func TestDecimalTestSuite(t *testing.T) { + suite.Run(t, new(decimalTestSuite)) +} + // create a decimal from a decimal string (ex. "1234.5678") -func mustNewDecFromStr(t *testing.T, str string) (d Dec) { - d, err := NewDecFromStr(str) - require.NoError(t, err) +func (s *decimalTestSuite) mustNewDecFromStr(str string) (d sdk.Dec) { + d, err := sdk.NewDecFromStr(str) + s.Require().NoError(err) + return d } //_______________________________________ -func TestPrecisionMultiplier(t *testing.T) { - res := precisionMultiplier(5) - exp := big.NewInt(10000000000000) - require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) -} - -func TestNewDecFromStr(t *testing.T) { +func (s *decimalTestSuite) TestNewDecFromStr() { largeBigInt, success := new(big.Int).SetString("3144605511029693144278234343371835", 10) - require.True(t, success) + s.Require().True(success) + tests := []struct { decimalStr string expErr bool - exp Dec + exp sdk.Dec }{ - {"", true, Dec{}}, - {"0.-75", true, Dec{}}, - {"0", false, NewDec(0)}, - {"1", false, NewDec(1)}, - {"1.1", false, NewDecWithPrec(11, 1)}, - {"0.75", false, NewDecWithPrec(75, 2)}, - {"0.8", false, NewDecWithPrec(8, 1)}, - {"0.11111", false, NewDecWithPrec(11111, 5)}, - {"314460551102969.3144278234343371835", true, NewDec(3141203149163817869)}, + {"", true, sdk.Dec{}}, + {"0.-75", true, sdk.Dec{}}, + {"0", false, sdk.NewDec(0)}, + {"1", false, sdk.NewDec(1)}, + {"1.1", false, sdk.NewDecWithPrec(11, 1)}, + {"0.75", false, sdk.NewDecWithPrec(75, 2)}, + {"0.8", false, sdk.NewDecWithPrec(8, 1)}, + {"0.11111", false, sdk.NewDecWithPrec(11111, 5)}, + {"314460551102969.3144278234343371835", true, sdk.NewDec(3141203149163817869)}, {"314460551102969314427823434337.1835718092488231350", - true, NewDecFromBigIntWithPrec(largeBigInt, 4)}, + true, sdk.NewDecFromBigIntWithPrec(largeBigInt, 4)}, {"314460551102969314427823434337.1835", - false, NewDecFromBigIntWithPrec(largeBigInt, 4)}, - {".", true, Dec{}}, - {".0", true, NewDec(0)}, - {"1.", true, NewDec(1)}, - {"foobar", true, Dec{}}, - {"0.foobar", true, Dec{}}, - {"0.foobar.", true, Dec{}}, + false, sdk.NewDecFromBigIntWithPrec(largeBigInt, 4)}, + {".", true, sdk.Dec{}}, + {".0", true, sdk.NewDec(0)}, + {"1.", true, sdk.NewDec(1)}, + {"foobar", true, sdk.Dec{}}, + {"0.foobar", true, sdk.Dec{}}, + {"0.foobar.", true, sdk.Dec{}}, + {"88888888888888888888888888888888888888888888888888888888888888888888844444440", true, sdk.Dec{}}, } for tcIndex, tc := range tests { - res, err := NewDecFromStr(tc.decimalStr) + res, err := sdk.NewDecFromStr(tc.decimalStr) if tc.expErr { - require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) } else { - require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) - require.True(t, res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex) + s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + s.Require().True(res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex) } // negative tc - res, err = NewDecFromStr("-" + tc.decimalStr) + res, err = sdk.NewDecFromStr("-" + tc.decimalStr) if tc.expErr { - require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + s.Require().NotNil(err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) } else { - require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) - exp := tc.exp.Mul(NewDec(-1)) - require.True(t, res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex) + s.Require().Nil(err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) + exp := tc.exp.Mul(sdk.NewDec(-1)) + s.Require().True(res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex) } } } -func TestDecString(t *testing.T) { +func (s *decimalTestSuite) TestDecString() { tests := []struct { - d Dec + d sdk.Dec want string }{ - {NewDec(0), "0.000000000000000000"}, - {NewDec(1), "1.000000000000000000"}, - {NewDec(10), "10.000000000000000000"}, - {NewDec(12340), "12340.000000000000000000"}, - {NewDecWithPrec(12340, 4), "1.234000000000000000"}, - {NewDecWithPrec(12340, 5), "0.123400000000000000"}, - {NewDecWithPrec(12340, 8), "0.000123400000000000"}, - {NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"}, + {sdk.NewDec(0), "0.000000000000000000"}, + {sdk.NewDec(1), "1.000000000000000000"}, + {sdk.NewDec(10), "10.000000000000000000"}, + {sdk.NewDec(12340), "12340.000000000000000000"}, + {sdk.NewDecWithPrec(12340, 4), "1.234000000000000000"}, + {sdk.NewDecWithPrec(12340, 5), "0.123400000000000000"}, + {sdk.NewDecWithPrec(12340, 8), "0.000123400000000000"}, + {sdk.NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"}, } for tcIndex, tc := range tests { - assert.Equal(t, tc.want, tc.d.String(), "bad String(), index: %v", tcIndex) + s.Require().Equal(tc.want, tc.d.String(), "bad String(), index: %v", tcIndex) } } -func TestEqualities(t *testing.T) { +func (s *decimalTestSuite) TestEqualities() { tests := []struct { - d1, d2 Dec + d1, d2 sdk.Dec gt, lt, eq bool }{ - {NewDec(0), NewDec(0), false, false, true}, - {NewDecWithPrec(0, 2), NewDecWithPrec(0, 4), false, false, true}, - {NewDecWithPrec(100, 0), NewDecWithPrec(100, 0), false, false, true}, - {NewDecWithPrec(-100, 0), NewDecWithPrec(-100, 0), false, false, true}, - {NewDecWithPrec(-1, 1), NewDecWithPrec(-1, 1), false, false, true}, - {NewDecWithPrec(3333, 3), NewDecWithPrec(3333, 3), false, false, true}, - - {NewDecWithPrec(0, 0), NewDecWithPrec(3333, 3), false, true, false}, - {NewDecWithPrec(0, 0), NewDecWithPrec(100, 0), false, true, false}, - {NewDecWithPrec(-1, 0), NewDecWithPrec(3333, 3), false, true, false}, - {NewDecWithPrec(-1, 0), NewDecWithPrec(100, 0), false, true, false}, - {NewDecWithPrec(1111, 3), NewDecWithPrec(100, 0), false, true, false}, - {NewDecWithPrec(1111, 3), NewDecWithPrec(3333, 3), false, true, false}, - {NewDecWithPrec(-3333, 3), NewDecWithPrec(-1111, 3), false, true, false}, - - {NewDecWithPrec(3333, 3), NewDecWithPrec(0, 0), true, false, false}, - {NewDecWithPrec(100, 0), NewDecWithPrec(0, 0), true, false, false}, - {NewDecWithPrec(3333, 3), NewDecWithPrec(-1, 0), true, false, false}, - {NewDecWithPrec(100, 0), NewDecWithPrec(-1, 0), true, false, false}, - {NewDecWithPrec(100, 0), NewDecWithPrec(1111, 3), true, false, false}, - {NewDecWithPrec(3333, 3), NewDecWithPrec(1111, 3), true, false, false}, - {NewDecWithPrec(-1111, 3), NewDecWithPrec(-3333, 3), true, false, false}, + {sdk.NewDec(0), sdk.NewDec(0), false, false, true}, + {sdk.NewDecWithPrec(0, 2), sdk.NewDecWithPrec(0, 4), false, false, true}, + {sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(100, 0), false, false, true}, + {sdk.NewDecWithPrec(-100, 0), sdk.NewDecWithPrec(-100, 0), false, false, true}, + {sdk.NewDecWithPrec(-1, 1), sdk.NewDecWithPrec(-1, 1), false, false, true}, + {sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(3333, 3), false, false, true}, + + {sdk.NewDecWithPrec(0, 0), sdk.NewDecWithPrec(3333, 3), false, true, false}, + {sdk.NewDecWithPrec(0, 0), sdk.NewDecWithPrec(100, 0), false, true, false}, + {sdk.NewDecWithPrec(-1, 0), sdk.NewDecWithPrec(3333, 3), false, true, false}, + {sdk.NewDecWithPrec(-1, 0), sdk.NewDecWithPrec(100, 0), false, true, false}, + {sdk.NewDecWithPrec(1111, 3), sdk.NewDecWithPrec(100, 0), false, true, false}, + {sdk.NewDecWithPrec(1111, 3), sdk.NewDecWithPrec(3333, 3), false, true, false}, + {sdk.NewDecWithPrec(-3333, 3), sdk.NewDecWithPrec(-1111, 3), false, true, false}, + + {sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(0, 0), true, false, false}, + {sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(0, 0), true, false, false}, + {sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(-1, 0), true, false, false}, + {sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(-1, 0), true, false, false}, + {sdk.NewDecWithPrec(100, 0), sdk.NewDecWithPrec(1111, 3), true, false, false}, + {sdk.NewDecWithPrec(3333, 3), sdk.NewDecWithPrec(1111, 3), true, false, false}, + {sdk.NewDecWithPrec(-1111, 3), sdk.NewDecWithPrec(-3333, 3), true, false, false}, } for tcIndex, tc := range tests { - require.Equal(t, tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex) - require.Equal(t, tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex) - require.Equal(t, tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex) + s.Require().Equal(tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex) + s.Require().Equal(tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex) + s.Require().Equal(tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex) } } -func TestDecsEqual(t *testing.T) { +func (s *decimalTestSuite) TestDecsEqual() { tests := []struct { - d1s, d2s []Dec + d1s, d2s []sdk.Dec eq bool }{ - {[]Dec{NewDec(0)}, []Dec{NewDec(0)}, true}, - {[]Dec{NewDec(0)}, []Dec{NewDec(1)}, false}, - {[]Dec{NewDec(0)}, []Dec{}, false}, - {[]Dec{NewDec(0), NewDec(1)}, []Dec{NewDec(0), NewDec(1)}, true}, - {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1), NewDec(0)}, true}, - {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(0), NewDec(1)}, false}, - {[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1)}, false}, - {[]Dec{NewDec(1), NewDec(2)}, []Dec{NewDec(2), NewDec(4)}, false}, - {[]Dec{NewDec(3), NewDec(18)}, []Dec{NewDec(1), NewDec(6)}, false}, + {[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(0)}, true}, + {[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1)}, false}, + {[]sdk.Dec{sdk.NewDec(0)}, []sdk.Dec{}, false}, + {[]sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, []sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, true}, + {[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, true}, + {[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(0), sdk.NewDec(1)}, false}, + {[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(0)}, []sdk.Dec{sdk.NewDec(1)}, false}, + {[]sdk.Dec{sdk.NewDec(1), sdk.NewDec(2)}, []sdk.Dec{sdk.NewDec(2), sdk.NewDec(4)}, false}, + {[]sdk.Dec{sdk.NewDec(3), sdk.NewDec(18)}, []sdk.Dec{sdk.NewDec(1), sdk.NewDec(6)}, false}, } for tcIndex, tc := range tests { - require.Equal(t, tc.eq, DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex) - require.Equal(t, tc.eq, DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex) + s.Require().Equal(tc.eq, sdk.DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex) + s.Require().Equal(tc.eq, sdk.DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex) } } -func TestArithmetic(t *testing.T) { +func (s *decimalTestSuite) TestArithmetic() { tests := []struct { - d1, d2 Dec - expMul, expMulTruncate Dec - expQuo, expQuoRoundUp, expQuoTruncate Dec - expAdd, expSub Dec + d1, d2 sdk.Dec + expMul, expMulTruncate sdk.Dec + expQuo, expQuoRoundUp, expQuoTruncate sdk.Dec + expAdd, expSub sdk.Dec }{ // d1 d2 MUL MulTruncate QUO QUORoundUp QUOTrunctate ADD SUB - {NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, - {NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, - {NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, - {NewDec(0), NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(1)}, - {NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(-1)}, - - {NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(2), NewDec(0)}, - {NewDec(-1), NewDec(-1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(-2), NewDec(0)}, - {NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, - {NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, - - {NewDec(3), NewDec(7), NewDec(21), NewDec(21), - NewDecWithPrec(428571428571428571, 18), NewDecWithPrec(428571428571428572, 18), NewDecWithPrec(428571428571428571, 18), - NewDec(10), NewDec(-4)}, - {NewDec(2), NewDec(4), NewDec(8), NewDec(8), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), - NewDec(6), NewDec(-2)}, - - {NewDec(100), NewDec(100), NewDec(10000), NewDec(10000), NewDec(1), NewDec(1), NewDec(1), NewDec(200), NewDec(0)}, - - {NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), NewDecWithPrec(225, 2), - NewDec(1), NewDec(1), NewDec(1), NewDec(3), NewDec(0)}, - {NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), NewDecWithPrec(1109889, 8), - MustNewDecFromStr("10.009009009009009009"), MustNewDecFromStr("10.009009009009009010"), MustNewDecFromStr("10.009009009009009009"), - NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, + {sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)}, + {sdk.NewDec(1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(1)}, + {sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(1), sdk.NewDec(-1)}, + {sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(1)}, + {sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(-1), sdk.NewDec(-1)}, + + {sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(2), sdk.NewDec(0)}, + {sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(-2), sdk.NewDec(0)}, + {sdk.NewDec(1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(2)}, + {sdk.NewDec(-1), sdk.NewDec(1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(-1), sdk.NewDec(0), sdk.NewDec(-2)}, + + {sdk.NewDec(3), sdk.NewDec(7), sdk.NewDec(21), sdk.NewDec(21), + sdk.NewDecWithPrec(428571428571428571, 18), sdk.NewDecWithPrec(428571428571428572, 18), sdk.NewDecWithPrec(428571428571428571, 18), + sdk.NewDec(10), sdk.NewDec(-4)}, + {sdk.NewDec(2), sdk.NewDec(4), sdk.NewDec(8), sdk.NewDec(8), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), + sdk.NewDec(6), sdk.NewDec(-2)}, + + {sdk.NewDec(100), sdk.NewDec(100), sdk.NewDec(10000), sdk.NewDec(10000), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(200), sdk.NewDec(0)}, + + {sdk.NewDecWithPrec(15, 1), sdk.NewDecWithPrec(15, 1), sdk.NewDecWithPrec(225, 2), sdk.NewDecWithPrec(225, 2), + sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(3), sdk.NewDec(0)}, + {sdk.NewDecWithPrec(3333, 4), sdk.NewDecWithPrec(333, 4), sdk.NewDecWithPrec(1109889, 8), sdk.NewDecWithPrec(1109889, 8), + sdk.MustNewDecFromStr("10.009009009009009009"), sdk.MustNewDecFromStr("10.009009009009009010"), sdk.MustNewDecFromStr("10.009009009009009009"), + sdk.NewDecWithPrec(3666, 4), sdk.NewDecWithPrec(3, 1)}, } for tcIndex, tc := range tests { @@ -194,320 +201,320 @@ func TestArithmetic(t *testing.T) { resSub := tc.d1.Sub(tc.d2) resMul := tc.d1.Mul(tc.d2) resMulTruncate := tc.d1.MulTruncate(tc.d2) - require.True(t, tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex) - require.True(t, tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex) - require.True(t, tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex) - require.True(t, tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex) + s.Require().True(tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex) + s.Require().True(tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex) + s.Require().True(tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex) + s.Require().True(tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex) if tc.d2.IsZero() { // panic for divide by zero - require.Panics(t, func() { tc.d1.Quo(tc.d2) }) + s.Require().Panics(func() { tc.d1.Quo(tc.d2) }) } else { resQuo := tc.d1.Quo(tc.d2) - require.True(t, tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex) + s.Require().True(tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex) resQuoRoundUp := tc.d1.QuoRoundUp(tc.d2) - require.True(t, tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d", + s.Require().True(tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d", tc.expQuoRoundUp.String(), resQuoRoundUp.String(), tcIndex) resQuoTruncate := tc.d1.QuoTruncate(tc.d2) - require.True(t, tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d", + s.Require().True(tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d", tc.expQuoTruncate.String(), resQuoTruncate.String(), tcIndex) } } } -func TestBankerRoundChop(t *testing.T) { +func (s *decimalTestSuite) TestBankerRoundChop() { tests := []struct { - d1 Dec + d1 sdk.Dec exp int64 }{ - {mustNewDecFromStr(t, "0.25"), 0}, - {mustNewDecFromStr(t, "0"), 0}, - {mustNewDecFromStr(t, "1"), 1}, - {mustNewDecFromStr(t, "0.75"), 1}, - {mustNewDecFromStr(t, "0.5"), 0}, - {mustNewDecFromStr(t, "7.5"), 8}, - {mustNewDecFromStr(t, "1.5"), 2}, - {mustNewDecFromStr(t, "2.5"), 2}, - {mustNewDecFromStr(t, "0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even - {mustNewDecFromStr(t, "1.545"), 2}, + {s.mustNewDecFromStr("0.25"), 0}, + {s.mustNewDecFromStr("0"), 0}, + {s.mustNewDecFromStr("1"), 1}, + {s.mustNewDecFromStr("0.75"), 1}, + {s.mustNewDecFromStr("0.5"), 0}, + {s.mustNewDecFromStr("7.5"), 8}, + {s.mustNewDecFromStr("1.5"), 2}, + {s.mustNewDecFromStr("2.5"), 2}, + {s.mustNewDecFromStr("0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even + {s.mustNewDecFromStr("1.545"), 2}, } for tcIndex, tc := range tests { resNeg := tc.d1.Neg().RoundInt64() - require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex) resPos := tc.d1.RoundInt64() - require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex) } } -func TestTruncate(t *testing.T) { +func (s *decimalTestSuite) TestTruncate() { tests := []struct { - d1 Dec + d1 sdk.Dec exp int64 }{ - {mustNewDecFromStr(t, "0"), 0}, - {mustNewDecFromStr(t, "0.25"), 0}, - {mustNewDecFromStr(t, "0.75"), 0}, - {mustNewDecFromStr(t, "1"), 1}, - {mustNewDecFromStr(t, "1.5"), 1}, - {mustNewDecFromStr(t, "7.5"), 7}, - {mustNewDecFromStr(t, "7.6"), 7}, - {mustNewDecFromStr(t, "7.4"), 7}, - {mustNewDecFromStr(t, "100.1"), 100}, - {mustNewDecFromStr(t, "1000.1"), 1000}, + {s.mustNewDecFromStr("0"), 0}, + {s.mustNewDecFromStr("0.25"), 0}, + {s.mustNewDecFromStr("0.75"), 0}, + {s.mustNewDecFromStr("1"), 1}, + {s.mustNewDecFromStr("1.5"), 1}, + {s.mustNewDecFromStr("7.5"), 7}, + {s.mustNewDecFromStr("7.6"), 7}, + {s.mustNewDecFromStr("7.4"), 7}, + {s.mustNewDecFromStr("100.1"), 100}, + {s.mustNewDecFromStr("1000.1"), 1000}, } for tcIndex, tc := range tests { resNeg := tc.d1.Neg().TruncateInt64() - require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + s.Require().Equal(-1*tc.exp, resNeg, "negative tc %d", tcIndex) resPos := tc.d1.TruncateInt64() - require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) - } -} - -var cdc = codec.New() - -func TestDecMarshalJSON(t *testing.T) { - decimal := func(i int64) Dec { - d := NewDec(0) - d.Int = new(big.Int).SetInt64(i) - return d - } - tests := []struct { - name string - d Dec - want string - wantErr bool // if wantErr = false, will also attempt unmarshaling - }{ - {"zero", decimal(0), "\"0.000000000000000000\"", false}, - {"one", decimal(1), "\"0.000000000000000001\"", false}, - {"ten", decimal(10), "\"0.000000000000000010\"", false}, - {"12340", decimal(12340), "\"0.000000000000012340\"", false}, - {"zeroInt", NewDec(0), "\"0.000000000000000000\"", false}, - {"oneInt", NewDec(1), "\"1.000000000000000000\"", false}, - {"tenInt", NewDec(10), "\"10.000000000000000000\"", false}, - {"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false}, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := tt.d.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Dec.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !tt.wantErr { - assert.Equal(t, tt.want, string(got), "incorrect marshalled value") - unmarshalledDec := NewDec(0) - unmarshalledDec.UnmarshalJSON(got) - assert.Equal(t, tt.d, unmarshalledDec, "incorrect unmarshalled value") - } - }) + s.Require().Equal(tc.exp, resPos, "positive tc %d", tcIndex) } } -func TestZeroDeserializationJSON(t *testing.T) { - d := Dec{new(big.Int)} - err := cdc.UnmarshalJSON([]byte(`"0"`), &d) - require.Nil(t, err) - err = cdc.UnmarshalJSON([]byte(`"{}"`), &d) - require.NotNil(t, err) -} - -func TestSerializationText(t *testing.T) { - d := mustNewDecFromStr(t, "0.333") - - bz, err := d.MarshalText() - require.NoError(t, err) - - d2 := Dec{new(big.Int)} - err = d2.UnmarshalText(bz) - require.NoError(t, err) - require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) -} - -func TestSerializationGocodecJSON(t *testing.T) { - d := mustNewDecFromStr(t, "0.333") - - bz, err := cdc.MarshalJSON(d) - require.NoError(t, err) - - d2 := Dec{new(big.Int)} - err = cdc.UnmarshalJSON(bz, &d2) - require.NoError(t, err) - require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) -} - -func TestSerializationGocodecBinary(t *testing.T) { - d := mustNewDecFromStr(t, "0.333") - - bz, err := cdc.MarshalBinaryLengthPrefixed(d) - require.NoError(t, err) - - var d2 Dec - err = cdc.UnmarshalBinaryLengthPrefixed(bz, &d2) - require.NoError(t, err) - require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) -} - -type testDEmbedStruct struct { - Field1 string `json:"f1"` - Field2 int `json:"f2"` - Field3 Dec `json:"f3"` -} - -// TODO make work for UnmarshalJSON -func TestEmbeddedStructSerializationGocodec(t *testing.T) { - obj := testDEmbedStruct{"foo", 10, NewDecWithPrec(1, 3)} - bz, err := cdc.MarshalBinaryLengthPrefixed(obj) - require.Nil(t, err) - - var obj2 testDEmbedStruct - err = cdc.UnmarshalBinaryLengthPrefixed(bz, &obj2) - require.Nil(t, err) - - require.Equal(t, obj.Field1, obj2.Field1) - require.Equal(t, obj.Field2, obj2.Field2) - require.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2) -} - -func TestStringOverflow(t *testing.T) { +func (s *decimalTestSuite) TestStringOverflow() { // two random 64 bit primes - dec1, err := NewDecFromStr("51643150036226787134389711697696177267") - require.NoError(t, err) - dec2, err := NewDecFromStr("-31798496660535729618459429845579852627") - require.NoError(t, err) + dec1, err := sdk.NewDecFromStr("51643150036226787134389711697696177267") + s.Require().NoError(err) + dec2, err := sdk.NewDecFromStr("-31798496660535729618459429845579852627") + s.Require().NoError(err) dec3 := dec1.Add(dec2) - require.Equal(t, + s.Require().Equal( "19844653375691057515930281852116324640.000000000000000000", dec3.String(), ) } -func TestDecMulInt(t *testing.T) { +func (s *decimalTestSuite) TestDecMulInt() { tests := []struct { - sdkDec Dec - sdkInt Int - want Dec + sdkDec sdk.Dec + sdkInt sdk.Int + want sdk.Dec }{ - {NewDec(10), NewInt(2), NewDec(20)}, - {NewDec(1000000), NewInt(100), NewDec(100000000)}, - {NewDecWithPrec(1, 1), NewInt(10), NewDec(1)}, - {NewDecWithPrec(1, 5), NewInt(20), NewDecWithPrec(2, 4)}, + {sdk.NewDec(10), sdk.NewInt(2), sdk.NewDec(20)}, + {sdk.NewDec(1000000), sdk.NewInt(100), sdk.NewDec(100000000)}, + {sdk.NewDecWithPrec(1, 1), sdk.NewInt(10), sdk.NewDec(1)}, + {sdk.NewDecWithPrec(1, 5), sdk.NewInt(20), sdk.NewDecWithPrec(2, 4)}, } for i, tc := range tests { got := tc.sdkDec.MulInt(tc.sdkInt) - require.Equal(t, tc.want, got, "Incorrect result on test case %d", i) + s.Require().Equal(tc.want, got, "Incorrect result on test case %d", i) } } -func TestDecCeil(t *testing.T) { +func (s *decimalTestSuite) TestDecCeil() { testCases := []struct { - input Dec - expected Dec + input sdk.Dec + expected sdk.Dec }{ - {NewDecWithPrec(1000000000000000, Precision), NewDec(1)}, // 0.001 => 1.0 - {NewDecWithPrec(-1000000000000000, Precision), ZeroDec()}, // -0.001 => 0.0 - {ZeroDec(), ZeroDec()}, // 0.0 => 0.0 - {NewDecWithPrec(900000000000000000, Precision), NewDec(1)}, // 0.9 => 1.0 - {NewDecWithPrec(4001000000000000000, Precision), NewDec(5)}, // 4.001 => 5.0 - {NewDecWithPrec(-4001000000000000000, Precision), NewDec(-4)}, // -4.001 => -4.0 - {NewDecWithPrec(4700000000000000000, Precision), NewDec(5)}, // 4.7 => 5.0 - {NewDecWithPrec(-4700000000000000000, Precision), NewDec(-4)}, // -4.7 => -4.0 + {sdk.NewDecWithPrec(1000000000000000, sdk.Precision), sdk.NewDec(1)}, // 0.001 => 1.0 + {sdk.NewDecWithPrec(-1000000000000000, sdk.Precision), sdk.ZeroDec()}, // -0.001 => 0.0 + {sdk.ZeroDec(), sdk.ZeroDec()}, // 0.0 => 0.0 + {sdk.NewDecWithPrec(900000000000000000, sdk.Precision), sdk.NewDec(1)}, // 0.9 => 1.0 + {sdk.NewDecWithPrec(4001000000000000000, sdk.Precision), sdk.NewDec(5)}, // 4.001 => 5.0 + {sdk.NewDecWithPrec(-4001000000000000000, sdk.Precision), sdk.NewDec(-4)}, // -4.001 => -4.0 + {sdk.NewDecWithPrec(4700000000000000000, sdk.Precision), sdk.NewDec(5)}, // 4.7 => 5.0 + {sdk.NewDecWithPrec(-4700000000000000000, sdk.Precision), sdk.NewDec(-4)}, // -4.7 => -4.0 } for i, tc := range testCases { res := tc.input.Ceil() - require.Equal(t, tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) + s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) } } -func TestPower(t *testing.T) { +func (s *decimalTestSuite) TestPower() { testCases := []struct { - input Dec + input sdk.Dec power uint64 - expected Dec + expected sdk.Dec }{ - {OneDec(), 10, OneDec()}, // 1.0 ^ (10) => 1.0 - {NewDecWithPrec(5, 1), 2, NewDecWithPrec(25, 2)}, // 0.5 ^ 2 => 0.25 - {NewDecWithPrec(2, 1), 2, NewDecWithPrec(4, 2)}, // 0.2 ^ 2 => 0.04 - {NewDecFromInt(NewInt(3)), 3, NewDecFromInt(NewInt(27))}, // 3 ^ 3 => 27 - {NewDecFromInt(NewInt(-3)), 4, NewDecFromInt(NewInt(81))}, // -3 ^ 4 = 81 - {NewDecWithPrec(1414213562373095049, 18), 2, NewDecFromInt(NewInt(2))}, // 1.414213562373095049 ^ 2 = 2 + {sdk.OneDec(), 10, sdk.OneDec()}, // 1.0 ^ (10) => 1.0 + {sdk.NewDecWithPrec(5, 1), 2, sdk.NewDecWithPrec(25, 2)}, // 0.5 ^ 2 => 0.25 + {sdk.NewDecWithPrec(2, 1), 2, sdk.NewDecWithPrec(4, 2)}, // 0.2 ^ 2 => 0.04 + {sdk.NewDecFromInt(sdk.NewInt(3)), 3, sdk.NewDecFromInt(sdk.NewInt(27))}, // 3 ^ 3 => 27 + {sdk.NewDecFromInt(sdk.NewInt(-3)), 4, sdk.NewDecFromInt(sdk.NewInt(81))}, // -3 ^ 4 = 81 + {sdk.NewDecWithPrec(1414213562373095049, 18), 2, sdk.NewDecFromInt(sdk.NewInt(2))}, // 1.414213562373095049 ^ 2 = 2 } for i, tc := range testCases { res := tc.input.Power(tc.power) - require.True(t, tc.expected.Sub(res).Abs().LTE(SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input) + s.Require().True(tc.expected.Sub(res).Abs().LTE(sdk.SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input) } } -func TestApproxRoot(t *testing.T) { +func (s *decimalTestSuite) TestApproxRoot() { testCases := []struct { - input Dec + input sdk.Dec root uint64 - expected Dec + expected sdk.Dec }{ - {OneDec(), 10, OneDec()}, // 1.0 ^ (0.1) => 1.0 - {NewDecWithPrec(25, 2), 2, NewDecWithPrec(5, 1)}, // 0.25 ^ (0.5) => 0.5 - {NewDecWithPrec(4, 2), 2, NewDecWithPrec(2, 1)}, // 0.04 => 0.2 - {NewDecFromInt(NewInt(27)), 3, NewDecFromInt(NewInt(3))}, // 27 ^ (1/3) => 3 - {NewDecFromInt(NewInt(-81)), 4, NewDecFromInt(NewInt(-3))}, // -81 ^ (0.25) => -3 - {NewDecFromInt(NewInt(2)), 2, NewDecWithPrec(1414213562373095049, 18)}, // 2 ^ (0.5) => 1.414213562373095049 - {NewDecWithPrec(1005, 3), 31536000, MustNewDecFromStr("1.000000000158153904")}, + {sdk.OneDec(), 10, sdk.OneDec()}, // 1.0 ^ (0.1) => 1.0 + {sdk.NewDecWithPrec(25, 2), 2, sdk.NewDecWithPrec(5, 1)}, // 0.25 ^ (0.5) => 0.5 + {sdk.NewDecWithPrec(4, 2), 2, sdk.NewDecWithPrec(2, 1)}, // 0.04 ^ (0.5) => 0.2 + {sdk.NewDecFromInt(sdk.NewInt(27)), 3, sdk.NewDecFromInt(sdk.NewInt(3))}, // 27 ^ (1/3) => 3 + {sdk.NewDecFromInt(sdk.NewInt(-81)), 4, sdk.NewDecFromInt(sdk.NewInt(-3))}, // -81 ^ (0.25) => -3 + {sdk.NewDecFromInt(sdk.NewInt(2)), 2, sdk.NewDecWithPrec(1414213562373095049, 18)}, // 2 ^ (0.5) => 1.414213562373095049 + {sdk.NewDecWithPrec(1005, 3), 31536000, sdk.MustNewDecFromStr("1.000000000158153904")}, // 1.005 ^ (1/31536000) ≈ 1.00000000016 + {sdk.SmallestDec(), 2, sdk.NewDecWithPrec(1, 9)}, // 1e-18 ^ (0.5) => 1e-9 + {sdk.SmallestDec(), 3, sdk.MustNewDecFromStr("0.000000999999999997")}, // 1e-18 ^ (1/3) => 1e-6 + {sdk.NewDecWithPrec(1, 8), 3, sdk.MustNewDecFromStr("0.002154434690031900")}, // 1e-8 ^ (1/3) ≈ 0.00215443469 } + // In the case of 1e-8 ^ (1/3), the result repeats every 5 iterations starting from iteration 24 + // (i.e. 24, 29, 34, ... give the same result) and never converges enough. The maximum number of + // iterations (100) causes the result at iteration 100 to be returned, regardless of convergence. + for i, tc := range testCases { res, err := tc.input.ApproxRoot(tc.root) - require.NoError(t, err) - require.True(t, tc.expected.Sub(res).Abs().LTE(SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input) + s.Require().NoError(err) + s.Require().True(tc.expected.Sub(res).Abs().LTE(sdk.SmallestDec()), "unexpected result for test case %d, input: %v", i, tc.input) } } -func TestApproxSqrt(t *testing.T) { +func (s *decimalTestSuite) TestApproxSqrt() { testCases := []struct { - input Dec - expected Dec + input sdk.Dec + expected sdk.Dec }{ - {OneDec(), OneDec()}, // 1.0 => 1.0 - {NewDecWithPrec(25, 2), NewDecWithPrec(5, 1)}, // 0.25 => 0.5 - {NewDecWithPrec(4, 2), NewDecWithPrec(2, 1)}, // 0.09 => 0.3 - {NewDecFromInt(NewInt(9)), NewDecFromInt(NewInt(3))}, // 9 => 3 - {NewDecFromInt(NewInt(-9)), NewDecFromInt(NewInt(-3))}, // -9 => -3 - {NewDecFromInt(NewInt(2)), NewDecWithPrec(1414213562373095049, 18)}, // 2 => 1.414213562373095049 + {sdk.OneDec(), sdk.OneDec()}, // 1.0 => 1.0 + {sdk.NewDecWithPrec(25, 2), sdk.NewDecWithPrec(5, 1)}, // 0.25 => 0.5 + {sdk.NewDecWithPrec(4, 2), sdk.NewDecWithPrec(2, 1)}, // 0.09 => 0.3 + {sdk.NewDecFromInt(sdk.NewInt(9)), sdk.NewDecFromInt(sdk.NewInt(3))}, // 9 => 3 + {sdk.NewDecFromInt(sdk.NewInt(-9)), sdk.NewDecFromInt(sdk.NewInt(-3))}, // -9 => -3 + {sdk.NewDecFromInt(sdk.NewInt(2)), sdk.NewDecWithPrec(1414213562373095049, 18)}, // 2 => 1.414213562373095049 } for i, tc := range testCases { res, err := tc.input.ApproxSqrt() - require.NoError(t, err) - require.Equal(t, tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) + s.Require().NoError(err) + s.Require().Equal(tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) } } -func TestDecSortableBytes(t *testing.T) { +func (s *decimalTestSuite) TestDecSortableBytes() { tests := []struct { - d Dec + d sdk.Dec want []byte }{ - {NewDec(0), []byte("000000000000000000.000000000000000000")}, - {NewDec(1), []byte("000000000000000001.000000000000000000")}, - {NewDec(10), []byte("000000000000000010.000000000000000000")}, - {NewDec(12340), []byte("000000000000012340.000000000000000000")}, - {NewDecWithPrec(12340, 4), []byte("000000000000000001.234000000000000000")}, - {NewDecWithPrec(12340, 5), []byte("000000000000000000.123400000000000000")}, - {NewDecWithPrec(12340, 8), []byte("000000000000000000.000123400000000000")}, - {NewDecWithPrec(1009009009009009009, 17), []byte("000000000000000010.090090090090090090")}, - {NewDecWithPrec(-1009009009009009009, 17), []byte("-000000000000000010.090090090090090090")}, - {NewDec(1000000000000000000), []byte("max")}, - {NewDec(-1000000000000000000), []byte("--")}, + {sdk.NewDec(0), []byte("000000000000000000.000000000000000000")}, + {sdk.NewDec(1), []byte("000000000000000001.000000000000000000")}, + {sdk.NewDec(10), []byte("000000000000000010.000000000000000000")}, + {sdk.NewDec(12340), []byte("000000000000012340.000000000000000000")}, + {sdk.NewDecWithPrec(12340, 4), []byte("000000000000000001.234000000000000000")}, + {sdk.NewDecWithPrec(12340, 5), []byte("000000000000000000.123400000000000000")}, + {sdk.NewDecWithPrec(12340, 8), []byte("000000000000000000.000123400000000000")}, + {sdk.NewDecWithPrec(1009009009009009009, 17), []byte("000000000000000010.090090090090090090")}, + {sdk.NewDecWithPrec(-1009009009009009009, 17), []byte("-000000000000000010.090090090090090090")}, + {sdk.NewDec(1000000000000000000), []byte("max")}, + {sdk.NewDec(-1000000000000000000), []byte("--")}, } for tcIndex, tc := range tests { - assert.Equal(t, tc.want, SortableDecBytes(tc.d), "bad String(), index: %v", tcIndex) + s.Require().Equal(tc.want, sdk.SortableDecBytes(tc.d), "bad String(), index: %v", tcIndex) + } + + s.Require().Panics(func() { sdk.SortableDecBytes(sdk.NewDec(1000000000000000001)) }) + s.Require().Panics(func() { sdk.SortableDecBytes(sdk.NewDec(-1000000000000000001)) }) +} + +func (s *decimalTestSuite) TestDecEncoding() { + testCases := []struct { + input sdk.Dec + rawBz string + jsonStr string + yamlStr string + }{ + { + sdk.NewDec(0), "30", + "\"0.000000000000000000\"", + "\"0.000000000000000000\"\n", + }, + { + sdk.NewDecWithPrec(4, 2), + "3430303030303030303030303030303030", + "\"0.040000000000000000\"", + "\"0.040000000000000000\"\n", + }, + { + sdk.NewDecWithPrec(-4, 2), + "2D3430303030303030303030303030303030", + "\"-0.040000000000000000\"", + "\"-0.040000000000000000\"\n", + }, + { + sdk.NewDecWithPrec(1414213562373095049, 18), + "31343134323133353632333733303935303439", + "\"1.414213562373095049\"", + "\"1.414213562373095049\"\n", + }, + { + sdk.NewDecWithPrec(-1414213562373095049, 18), + "2D31343134323133353632333733303935303439", + "\"-1.414213562373095049\"", + "\"-1.414213562373095049\"\n", + }, + } + + for _, tc := range testCases { + bz, err := tc.input.Marshal() + s.Require().NoError(err) + s.Require().Equal(tc.rawBz, fmt.Sprintf("%X", bz)) + + var other sdk.Dec + s.Require().NoError((&other).Unmarshal(bz)) + s.Require().True(tc.input.Equal(other)) + + bz, err = json.Marshal(tc.input) + s.Require().NoError(err) + s.Require().Equal(tc.jsonStr, string(bz)) + s.Require().NoError(json.Unmarshal(bz, &other)) + s.Require().True(tc.input.Equal(other)) + + bz, err = yaml.Marshal(tc.input) + s.Require().NoError(err) + s.Require().Equal(tc.yamlStr, string(bz)) } +} - assert.Panics(t, func() { SortableDecBytes(NewDec(1000000000000000001)) }) - assert.Panics(t, func() { SortableDecBytes(NewDec(-1000000000000000001)) }) +// Showcase that different orders of operations causes different results. +func (s *decimalTestSuite) TestOperationOrders() { + n1 := sdk.NewDec(10) + n2 := sdk.NewDec(1000000010) + s.Require().Equal(n1.Mul(n2).Quo(n2), sdk.NewDec(10)) + s.Require().NotEqual(n1.Mul(n2).Quo(n2), n1.Quo(n2).Mul(n2)) +} + +func BenchmarkMarshalTo(b *testing.B) { + bis := []struct { + in sdk.Dec + want []byte + }{ + { + sdk.NewDec(1e8), []byte{ + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + }, + }, + {sdk.NewDec(0), []byte{0x30}}, + } + data := make([]byte, 100) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for _, bi := range bis { + if n, err := bi.in.MarshalTo(data); err != nil { + b.Fatal(err) + } else { + if !bytes.Equal(data[:n], bi.want) { + b.Fatalf("Mismatch\nGot: % x\nWant: % x\n", data[:n], bi.want) + } + } + } + } } diff --git a/types/denom.go b/types/denom.go index f79bef8b9459..160e2806e1df 100644 --- a/types/denom.go +++ b/types/denom.go @@ -8,6 +8,9 @@ import ( // multipliers (e.g. 1atom = 10^-6uatom). var denomUnits = map[string]Dec{} +// baseDenom is the denom of smallest unit registered +var baseDenom string = "" + // RegisterDenom registers a denomination with a corresponding unit. If the // denomination is already registered, an error will be returned. func RegisterDenom(denom string, unit Dec) error { @@ -20,6 +23,10 @@ func RegisterDenom(denom string, unit Dec) error { } denomUnits[denom] = unit + + if baseDenom == "" || unit.LT(denomUnits[baseDenom]) { + baseDenom = denom + } return nil } @@ -38,6 +45,14 @@ func GetDenomUnit(denom string) (Dec, bool) { return unit, true } +// GetBaseDenom returns the denom of smallest unit registered +func GetBaseDenom() (string, error) { + if baseDenom == "" { + return "", fmt.Errorf("no denom is registered") + } + return baseDenom, nil +} + // ConvertCoin attempts to convert a coin to a given denomination. If the given // denomination is invalid or if neither denomination is registered, an error // is returned. @@ -60,5 +75,73 @@ func ConvertCoin(coin Coin, denom string) (Coin, error) { return NewCoin(denom, coin.Amount), nil } - return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit.Quo(dstUnit)).TruncateInt()), nil + return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit).Quo(dstUnit).TruncateInt()), nil +} + +// ConvertDecCoin attempts to convert a decimal coin to a given denomination. If the given +// denomination is invalid or if neither denomination is registered, an error +// is returned. +func ConvertDecCoin(coin DecCoin, denom string) (DecCoin, error) { + if err := ValidateDenom(denom); err != nil { + return DecCoin{}, err + } + + srcUnit, ok := GetDenomUnit(coin.Denom) + if !ok { + return DecCoin{}, fmt.Errorf("source denom not registered: %s", coin.Denom) + } + + dstUnit, ok := GetDenomUnit(denom) + if !ok { + return DecCoin{}, fmt.Errorf("destination denom not registered: %s", denom) + } + + if srcUnit.Equal(dstUnit) { + return NewDecCoinFromDec(denom, coin.Amount), nil + } + + return NewDecCoinFromDec(denom, coin.Amount.Mul(srcUnit).Quo(dstUnit)), nil +} + +// NormalizeCoin try to convert a coin to the smallest unit registered, +// returns original one if failed. +func NormalizeCoin(coin Coin) Coin { + base, err := GetBaseDenom() + if err != nil { + return coin + } + newCoin, err := ConvertCoin(coin, base) + if err != nil { + return coin + } + return newCoin +} + +// NormalizeDecCoin try to convert a decimal coin to the smallest unit registered, +// returns original one if failed. +func NormalizeDecCoin(coin DecCoin) DecCoin { + base, err := GetBaseDenom() + if err != nil { + return coin + } + newCoin, err := ConvertDecCoin(coin, base) + if err != nil { + return coin + } + return newCoin +} + +// NormalizeCoins normalize and truncate a list of decimal coins +func NormalizeCoins(coins []DecCoin) Coins { + if coins == nil { + return nil + } + result := make([]Coin, 0, len(coins)) + + for _, coin := range coins { + newCoin, _ := NormalizeDecCoin(coin).TruncateDecimal() + result = append(result, newCoin) + } + + return result } diff --git a/types/denom_internal_test.go b/types/denom_internal_test.go new file mode 100644 index 000000000000..8c957353ebc7 --- /dev/null +++ b/types/denom_internal_test.go @@ -0,0 +1,192 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +var ( + atom = "atom" // 1 (base denom unit) + matom = "matom" // 10^-3 (milli) + uatom = "uatom" // 10^-6 (micro) + natom = "natom" // 10^-9 (nano) +) + +type internalDenomTestSuite struct { + suite.Suite +} + +func TestInternalDenomTestSuite(t *testing.T) { + suite.Run(t, new(internalDenomTestSuite)) +} + +func (s *internalDenomTestSuite) TestRegisterDenom() { + atomUnit := OneDec() // 1 (base denom unit) + + s.Require().NoError(RegisterDenom(atom, atomUnit)) + s.Require().Error(RegisterDenom(atom, atomUnit)) + + res, ok := GetDenomUnit(atom) + s.Require().True(ok) + s.Require().Equal(atomUnit, res) + + res, ok = GetDenomUnit(matom) + s.Require().False(ok) + s.Require().Equal(ZeroDec(), res) + + // reset registration + baseDenom = "" + denomUnits = map[string]Dec{} +} + +func (s *internalDenomTestSuite) TestConvertCoins() { + atomUnit := OneDec() // 1 (base denom unit) + s.Require().NoError(RegisterDenom(atom, atomUnit)) + + matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli) + s.Require().NoError(RegisterDenom(matom, matomUnit)) + + uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro) + s.Require().NoError(RegisterDenom(uatom, uatomUnit)) + + natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano) + s.Require().NoError(RegisterDenom(natom, natomUnit)) + + res, err := GetBaseDenom() + s.Require().NoError(err) + s.Require().Equal(res, natom) + s.Require().Equal(NormalizeCoin(NewCoin(uatom, NewInt(1))), NewCoin(natom, NewInt(1000))) + s.Require().Equal(NormalizeCoin(NewCoin(matom, NewInt(1))), NewCoin(natom, NewInt(1000000))) + s.Require().Equal(NormalizeCoin(NewCoin(atom, NewInt(1))), NewCoin(natom, NewInt(1000000000))) + + coins, err := ParseCoinsNormalized("1atom,1matom,1uatom") + s.Require().NoError(err) + s.Require().Equal(coins, Coins{ + Coin{natom, NewInt(1000000000)}, + Coin{natom, NewInt(1000000)}, + Coin{natom, NewInt(1000)}, + }) + + testCases := []struct { + input Coin + denom string + result Coin + expErr bool + }{ + {NewCoin("foo", ZeroInt()), atom, Coin{}, true}, + {NewCoin(atom, ZeroInt()), "foo", Coin{}, true}, + {NewCoin(atom, ZeroInt()), "FOO", Coin{}, true}, + + {NewCoin(atom, NewInt(5)), matom, NewCoin(matom, NewInt(5000)), false}, // atom => matom + {NewCoin(atom, NewInt(5)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // atom => uatom + {NewCoin(atom, NewInt(5)), natom, NewCoin(natom, NewInt(5000000000)), false}, // atom => natom + + {NewCoin(uatom, NewInt(5000000)), matom, NewCoin(matom, NewInt(5000)), false}, // uatom => matom + {NewCoin(uatom, NewInt(5000000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // uatom => natom + {NewCoin(uatom, NewInt(5000000)), atom, NewCoin(atom, NewInt(5)), false}, // uatom => atom + + {NewCoin(matom, NewInt(5000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // matom => natom + {NewCoin(matom, NewInt(5000)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // matom => uatom + } + + for i, tc := range testCases { + res, err := ConvertCoin(tc.input, tc.denom) + s.Require().Equal( + tc.expErr, err != nil, + "unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, + ) + s.Require().Equal( + tc.result, res, + "invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, + ) + } + + // reset registration + baseDenom = "" + denomUnits = map[string]Dec{} +} + +func (s *internalDenomTestSuite) TestConvertDecCoins() { + atomUnit := OneDec() // 1 (base denom unit) + s.Require().NoError(RegisterDenom(atom, atomUnit)) + + matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli) + s.Require().NoError(RegisterDenom(matom, matomUnit)) + + uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro) + s.Require().NoError(RegisterDenom(uatom, uatomUnit)) + + natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano) + s.Require().NoError(RegisterDenom(natom, natomUnit)) + + res, err := GetBaseDenom() + s.Require().NoError(err) + s.Require().Equal(res, natom) + s.Require().Equal(NormalizeDecCoin(NewDecCoin(uatom, NewInt(1))), NewDecCoin(natom, NewInt(1000))) + s.Require().Equal(NormalizeDecCoin(NewDecCoin(matom, NewInt(1))), NewDecCoin(natom, NewInt(1000000))) + s.Require().Equal(NormalizeDecCoin(NewDecCoin(atom, NewInt(1))), NewDecCoin(natom, NewInt(1000000000))) + + coins, err := ParseCoinsNormalized("0.1atom,0.1matom,0.1uatom") + s.Require().NoError(err) + s.Require().Equal(coins, Coins{ + Coin{natom, NewInt(100000000)}, + Coin{natom, NewInt(100000)}, + Coin{natom, NewInt(100)}, + }) + + testCases := []struct { + input DecCoin + denom string + result DecCoin + expErr bool + }{ + {NewDecCoin("foo", ZeroInt()), atom, DecCoin{}, true}, + {NewDecCoin(atom, ZeroInt()), "foo", DecCoin{}, true}, + {NewDecCoin(atom, ZeroInt()), "FOO", DecCoin{}, true}, + + // 0.5atom + {NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), matom, NewDecCoin(matom, NewInt(500)), false}, // atom => matom + {NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), uatom, NewDecCoin(uatom, NewInt(500000)), false}, // atom => uatom + {NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), natom, NewDecCoin(natom, NewInt(500000000)), false}, // atom => natom + + {NewDecCoin(uatom, NewInt(5000000)), matom, NewDecCoin(matom, NewInt(5000)), false}, // uatom => matom + {NewDecCoin(uatom, NewInt(5000000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // uatom => natom + {NewDecCoin(uatom, NewInt(5000000)), atom, NewDecCoin(atom, NewInt(5)), false}, // uatom => atom + + {NewDecCoin(matom, NewInt(5000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // matom => natom + {NewDecCoin(matom, NewInt(5000)), uatom, NewDecCoin(uatom, NewInt(5000000)), false}, // matom => uatom + } + + for i, tc := range testCases { + res, err := ConvertDecCoin(tc.input, tc.denom) + s.Require().Equal( + tc.expErr, err != nil, + "unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, + ) + s.Require().Equal( + tc.result, res, + "invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, + ) + } + + // reset registration + baseDenom = "" + denomUnits = map[string]Dec{} +} + +func (s *internalDenomTestSuite) TestDecOperationOrder() { + dec, err := NewDecFromStr("11") + s.Require().NoError(err) + s.Require().NoError(RegisterDenom("unit1", dec)) + dec, err = NewDecFromStr("100000011") + s.Require().NoError(RegisterDenom("unit2", dec)) + + coin, err := ConvertCoin(NewCoin("unit1", NewInt(100000011)), "unit2") + s.Require().NoError(err) + s.Require().Equal(coin, NewCoin("unit2", NewInt(11))) + + // reset registration + baseDenom = "" + denomUnits = map[string]Dec{} +} diff --git a/types/denom_test.go b/types/denom_test.go deleted file mode 100644 index de6437994bef..000000000000 --- a/types/denom_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -var ( - atom = "atom" // 1 (base denom unit) - matom = "matom" // 10^-3 (milli) - uatom = "uatom" // 10^-6 (micro) - natom = "natom" // 10^-9 (nano) -) - -func TestRegisterDenom(t *testing.T) { - atomUnit := OneDec() // 1 (base denom unit) - - require.NoError(t, RegisterDenom(atom, atomUnit)) - require.Error(t, RegisterDenom(atom, atomUnit)) - - res, ok := GetDenomUnit(atom) - require.True(t, ok) - require.Equal(t, atomUnit, res) - - res, ok = GetDenomUnit(matom) - require.False(t, ok) - require.Equal(t, ZeroDec(), res) - - // reset registration - denomUnits = map[string]Dec{} -} - -func TestConvertCoins(t *testing.T) { - atomUnit := OneDec() // 1 (base denom unit) - require.NoError(t, RegisterDenom(atom, atomUnit)) - - matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli) - require.NoError(t, RegisterDenom(matom, matomUnit)) - - uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro) - require.NoError(t, RegisterDenom(uatom, uatomUnit)) - - natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano) - require.NoError(t, RegisterDenom(natom, natomUnit)) - - testCases := []struct { - input Coin - denom string - result Coin - expErr bool - }{ - {NewCoin("foo", ZeroInt()), atom, Coin{}, true}, - {NewCoin(atom, ZeroInt()), "foo", Coin{}, true}, - {NewCoin(atom, ZeroInt()), "FOO", Coin{}, true}, - - {NewCoin(atom, NewInt(5)), matom, NewCoin(matom, NewInt(5000)), false}, // atom => matom - {NewCoin(atom, NewInt(5)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // atom => uatom - {NewCoin(atom, NewInt(5)), natom, NewCoin(natom, NewInt(5000000000)), false}, // atom => natom - - {NewCoin(uatom, NewInt(5000000)), matom, NewCoin(matom, NewInt(5000)), false}, // uatom => matom - {NewCoin(uatom, NewInt(5000000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // uatom => natom - {NewCoin(uatom, NewInt(5000000)), atom, NewCoin(atom, NewInt(5)), false}, // uatom => atom - - {NewCoin(matom, NewInt(5000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // matom => natom - {NewCoin(matom, NewInt(5000)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // matom => uatom - } - - for i, tc := range testCases { - res, err := ConvertCoin(tc.input, tc.denom) - require.Equal( - t, tc.expErr, err != nil, - "unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - require.Equal( - t, tc.result, res, - "invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, - ) - } - - // reset registration - denomUnits = map[string]Dec{} -} diff --git a/types/errors/abci.go b/types/errors/abci.go index 9015e6b36486..df85f6bc89df 100644 --- a/types/errors/abci.go +++ b/types/errors/abci.go @@ -1,7 +1,6 @@ package errors import ( - "errors" "fmt" "reflect" @@ -18,8 +17,6 @@ const ( // detailed error string. internalABCICodespace = UndefinedCodespace internalABCICode uint32 = 1 - internalABCILog string = "internal error" - // multiErrorABCICode uint32 = 1000 ) // ABCIInfo returns the ABCI error information as consumed by the tendermint @@ -154,16 +151,18 @@ func errIsNil(err error) bool { return false } -// Redact replace all errors that do not initialize with a weave error with a -// generic internal error instance. This function is supposed to hide -// implementation details errors and leave only those that weave framework -// originates. +var errPanicWithMsg = Wrapf(ErrPanic, "panic message redacted to hide potentially sensitive system info") + +// Redact replaces an error that is not initialized as an ABCI Error with a +// generic internal error instance. If the error is an ABCI Error, that error is +// simply returned. func Redact(err error) error { if ErrPanic.Is(err) { - return errors.New(internalABCILog) + return errPanicWithMsg } if abciCode(err) == internalABCICode { - return errors.New(internalABCILog) + return errInternal } + return err } diff --git a/types/errors/abci_test.go b/types/errors/abci_test.go index c098811ea474..02c12e7bbdd6 100644 --- a/types/errors/abci_test.go +++ b/types/errors/abci_test.go @@ -5,9 +5,23 @@ import ( "io" "strings" "testing" + + "github.com/stretchr/testify/suite" ) -func TestABCInfo(t *testing.T) { +type abciTestSuite struct { + suite.Suite +} + +func TestABCITestSuite(t *testing.T) { + suite.Run(t, new(abciTestSuite)) +} + +func (s *abciTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *abciTestSuite) TestABCInfo() { cases := map[string]struct { err error debug bool @@ -25,7 +39,7 @@ func TestABCInfo(t *testing.T) { "wrapped SDK error": { err: Wrap(Wrap(ErrUnauthorized, "foo"), "bar"), debug: false, - wantLog: "unauthorized: foo: bar", + wantLog: "bar: foo: unauthorized", wantCode: ErrUnauthorized.code, wantSpace: RootCodespace, }, @@ -46,7 +60,7 @@ func TestABCInfo(t *testing.T) { "stdlib is generic message": { err: io.EOF, debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -60,7 +74,7 @@ func TestABCInfo(t *testing.T) { "wrapped stdlib is only a generic message": { err: Wrap(io.EOF, "cannot read file"), debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -89,23 +103,14 @@ func TestABCInfo(t *testing.T) { } for testName, tc := range cases { - tc := tc - t.Run(testName, func(t *testing.T) { - space, code, log := ABCIInfo(tc.err, tc.debug) - if space != tc.wantSpace { - t.Errorf("want %s space, got %s", tc.wantSpace, space) - } - if code != tc.wantCode { - t.Errorf("want %d code, got %d", tc.wantCode, code) - } - if log != tc.wantLog { - t.Errorf("want %q log, got %q", tc.wantLog, log) - } - }) + space, code, log := ABCIInfo(tc.err, tc.debug) + s.Require().Equal(tc.wantSpace, space, testName) + s.Require().Equal(tc.wantCode, code, testName) + s.Require().Equal(tc.wantLog, log, testName) } } -func TestABCIInfoStacktrace(t *testing.T) { +func (s *abciTestSuite) TestABCIInfoStacktrace() { cases := map[string]struct { err error debug bool @@ -116,78 +121,89 @@ func TestABCIInfoStacktrace(t *testing.T) { err: Wrap(ErrUnauthorized, "wrapped"), debug: true, wantStacktrace: true, - wantErrMsg: "unauthorized: wrapped", + wantErrMsg: "wrapped: unauthorized", }, "wrapped SDK error in non-debug mode does not have stacktrace": { err: Wrap(ErrUnauthorized, "wrapped"), debug: false, wantStacktrace: false, - wantErrMsg: "unauthorized: wrapped", + wantErrMsg: "wrapped: unauthorized", }, "wrapped stdlib error in debug mode provides stacktrace": { err: Wrap(fmt.Errorf("stdlib"), "wrapped"), debug: true, wantStacktrace: true, - wantErrMsg: "stdlib: wrapped", + wantErrMsg: "wrapped: stdlib", }, "wrapped stdlib error in non-debug mode does not have stacktrace": { err: Wrap(fmt.Errorf("stdlib"), "wrapped"), debug: false, wantStacktrace: false, - wantErrMsg: "internal error", + wantErrMsg: "internal", }, } - const thisTestSrc = "github.com/cosmos/cosmos-sdk/types/errors.TestABCIInfoStacktrace" + const thisTestSrc = "github.com/cosmos/cosmos-sdk/types/errors.(*abciTestSuite).TestABCIInfoStacktrace" for testName, tc := range cases { - tc := tc - t.Run(testName, func(t *testing.T) { - _, _, log := ABCIInfo(tc.err, tc.debug) - if tc.wantStacktrace { - if !strings.Contains(log, thisTestSrc) { - t.Errorf("log does not contain this file stack trace: %s", log) - } + _, _, log := ABCIInfo(tc.err, tc.debug) + if !tc.wantStacktrace { + s.Require().Equal(tc.wantErrMsg, log, testName) + continue + } - if !strings.Contains(log, tc.wantErrMsg) { - t.Errorf("log does not contain expected error message: %s", log) - } - } else if log != tc.wantErrMsg { - t.Fatalf("unexpected log message: %s", log) - } - }) + s.Require().True(strings.Contains(log, thisTestSrc), testName) + s.Require().True(strings.Contains(log, tc.wantErrMsg), testName) } } -func TestABCIInfoHidesStacktrace(t *testing.T) { +func (s *abciTestSuite) TestABCIInfoHidesStacktrace() { err := Wrap(ErrUnauthorized, "wrapped") _, _, log := ABCIInfo(err, false) - - if log != "unauthorized: wrapped" { - t.Fatalf("unexpected message in non debug mode: %s", log) - } + s.Require().Equal("wrapped: unauthorized", log) } -func TestRedact(t *testing.T) { - if err := Redact(ErrPanic); ErrPanic.Is(err) { - t.Error("reduct must not pass through panic error") - } - if err := Redact(ErrUnauthorized); !ErrUnauthorized.Is(err) { - t.Error("reduct should pass through SDK error") +func (s *abciTestSuite) TestRedact() { + cases := map[string]struct { + err error + untouched bool // if true we expect the same error after redact + changed error // if untouched == false, expect this error + }{ + "panic looses message": { + err: Wrap(ErrPanic, "some secret stack trace"), + changed: errPanicWithMsg, + }, + "sdk errors untouched": { + err: Wrap(ErrUnauthorized, "cannot drop db"), + untouched: true, + }, + "pass though custom errors with ABCI code": { + err: customErr{}, + untouched: true, + }, + "redact stdlib error": { + err: fmt.Errorf("stdlib error"), + changed: errInternal, + }, } - var cerr customErr - if err := Redact(cerr); err != cerr { - t.Error("reduct should pass through ABCI code error") - } + for name, tc := range cases { + spec := tc + redacted := Redact(spec.err) + if spec.untouched { + s.Require().Equal(spec.err, redacted, name) + continue + } + + // see if we got the expected redact + s.Require().Equal(spec.changed, redacted, name) + // make sure the ABCI code did not change + s.Require().Equal(abciCode(spec.err), abciCode(redacted), name) - serr := fmt.Errorf("stdlib error") - if err := Redact(serr); err == serr { - t.Error("reduct must not pass through a stdlib error") } } -func TestABCIInfoSerializeErr(t *testing.T) { +func (s *abciTestSuite) TestABCIInfoSerializeErr() { var ( // Create errors with stacktrace for equal comparison. myErrDecode = Wrap(ErrTxDecode, "test") @@ -203,62 +219,32 @@ func TestABCIInfoSerializeErr(t *testing.T) { "single error": { src: myErrDecode, debug: false, - exp: "tx parse error: test", + exp: "test: tx parse error", }, "second error": { src: myErrAddr, debug: false, - exp: "invalid address: tester", + exp: "tester: invalid address", }, "single error with debug": { src: myErrDecode, debug: true, exp: fmt.Sprintf("%+v", myErrDecode), }, - // "multi error default encoder": { - // src: Append(myErrMsg, myErrAddr), - // exp: Append(myErrMsg, myErrAddr).Error(), - // }, - // "multi error default with internal": { - // src: Append(myErrMsg, myPanic), - // exp: "internal error", - // }, "redact in default encoder": { src: myPanic, - exp: "internal error", + exp: "panic message redacted to hide potentially sensitive system info: panic", }, "do not redact in debug encoder": { src: myPanic, debug: true, exp: fmt.Sprintf("%+v", myPanic), }, - // "redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: false, - // exp: "internal error", - // }, - // "no redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: true, - // exp: `2 errors occurred: - // * panic - // * test: invalid message - // `, - // }, - // "wrapped multi error with redact": { - // src: Wrap(Append(myPanic, myErrMsg), "wrap"), - // debug: false, - // exp: "internal error", - // }, } for msg, spec := range specs { spec := spec - t.Run(msg, func(t *testing.T) { - _, _, log := ABCIInfo(spec.src, spec.debug) - if exp, got := spec.exp, log; exp != got { - t.Errorf("expected %v but got %v", exp, got) - } - }) + _, _, log := ABCIInfo(spec.src, spec.debug) + s.Require().Equal(spec.exp, log, msg) } } diff --git a/types/errors/errors.go b/types/errors/errors.go index 3179aed26369..1aa68816ccbf 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -82,6 +82,59 @@ var ( // ErrTxTooLarge defines an ABCI typed error where tx is too large. ErrTxTooLarge = Register(RootCodespace, 21, "tx too large") + // ErrKeyNotFound defines an error when the key doesn't exist + ErrKeyNotFound = Register(RootCodespace, 22, "key not found") + + // ErrWrongPassword defines an error when the key password is invalid. + ErrWrongPassword = Register(RootCodespace, 23, "invalid account password") + + // ErrorInvalidSigner defines an error when the tx intended signer does not match the given signer. + ErrorInvalidSigner = Register(RootCodespace, 24, "tx intended signer does not match the given signer") + + // ErrorInvalidGasAdjustment defines an error for an invalid gas adjustment + ErrorInvalidGasAdjustment = Register(RootCodespace, 25, "invalid gas adjustment") + + // ErrInvalidHeight defines an error for an invalid height + ErrInvalidHeight = Register(RootCodespace, 26, "invalid height") + + // ErrInvalidVersion defines a general error for an invalid version + ErrInvalidVersion = Register(RootCodespace, 27, "invalid version") + + // ErrInvalidChainID defines an error when the chain-id is invalid. + ErrInvalidChainID = Register(RootCodespace, 28, "invalid chain-id") + + // ErrInvalidType defines an error an invalid type. + ErrInvalidType = Register(RootCodespace, 29, "invalid type") + + // ErrTxTimeoutHeight defines an error for when a tx is rejected out due to an + // explicitly set timeout height. + ErrTxTimeoutHeight = Register(RootCodespace, 30, "tx timeout height") + + // ErrUnknownExtensionOptions defines an error for unknown extension options. + ErrUnknownExtensionOptions = Register(RootCodespace, 31, "unknown extension options") + + // ErrWrongSequence defines an error where the account sequence defined in + // the signer info doesn't match the account's actual sequence number. + ErrWrongSequence = Register(RootCodespace, 32, "incorrect account sequence") + + // ErrPackAny defines an error when packing a protobuf message to Any fails. + ErrPackAny = Register(RootCodespace, 33, "failed packing protobuf message to Any") + + // ErrUnpackAny defines an error when unpacking a protobuf message from Any fails. + ErrUnpackAny = Register(RootCodespace, 34, "failed unpacking protobuf message from Any") + + // ErrLogic defines an internal logic error, e.g. an invariant or assertion + // that is violated. It is a programmer error, not a user-facing error. + ErrLogic = Register(RootCodespace, 35, "internal logic error") + + // ErrConflict defines a conflict error, e.g. when two goroutines try to access + // the same resource and one of them fails. + ErrConflict = Register(RootCodespace, 36, "conflict") + + // ErrNotSupported is returned when we call a branch of a code which is currently not + // supported. + ErrNotSupported = Register(RootCodespace, 37, "feature not supported") + // ErrPanic is only set when we recover from a panic, so we know to // redact potentially sensitive system info ErrPanic = Register(UndefinedCodespace, 111222, "panic") @@ -256,7 +309,7 @@ type wrappedError struct { } func (e *wrappedError) Error() string { - return fmt.Sprintf("%s: %s", e.parent.Error(), e.msg) + return fmt.Sprintf("%s: %s", e.msg, e.parent.Error()) } func (e *wrappedError) Cause() error { @@ -265,12 +318,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 f6e6e8cd7f52..ea0e063c3a52 100644 --- a/types/errors/errors_test.go +++ b/types/errors/errors_test.go @@ -5,12 +5,23 @@ import ( "fmt" "testing" - "github.com/stretchr/testify/require" - "github.com/pkg/errors" + "github.com/stretchr/testify/suite" ) -func TestCause(t *testing.T) { +type errorsTestSuite struct { + suite.Suite +} + +func TestErrorsTestSuite(t *testing.T) { + suite.Run(t, new(errorsTestSuite)) +} + +func (s *errorsTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *errorsTestSuite) TestCause() { std := stdlib.New("this is a stdlib error") cases := map[string]struct { @@ -32,16 +43,11 @@ func TestCause(t *testing.T) { } for testName, tc := range cases { - tc := tc - t.Run(testName, func(t *testing.T) { - if got := errors.Cause(tc.err); got != tc.root { - t.Fatal("unexpected result") - } - }) + s.Require().Equal(tc.root, errors.Cause(tc.err), testName) } } -func TestErrorIs(t *testing.T) { +func (s *errorsTestSuite) TestErrorIs() { cases := map[string]struct { a *Error b error @@ -139,12 +145,7 @@ func TestErrorIs(t *testing.T) { // }, } for testName, tc := range cases { - tc := tc - t.Run(testName, func(t *testing.T) { - if got := tc.a.Is(tc.b); got != tc.wantIs { - t.Fatalf("unexpected result - got:%v want: %v", got, tc.wantIs) - } - }) + s.Require().Equal(tc.wantIs, tc.a.Is(tc.b), testName) } } @@ -155,56 +156,86 @@ func (customError) Error() string { return "custom error" } -func TestWrapEmpty(t *testing.T) { - if err := Wrap(nil, "wrapping "); err != nil { - t.Fatal(err) - } +func (s *errorsTestSuite) TestWrapEmpty() { + s.Require().Nil(Wrap(nil, "wrapping ")) } -func TestWrappedIs(t *testing.T) { +func (s *errorsTestSuite) TestWrappedIs() { + require := s.Require() err := Wrap(ErrTxTooLarge, "context") - require.True(t, stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(err, "more context") - require.True(t, stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(err, "even more context") - require.True(t, stdlib.Is(err, ErrTxTooLarge)) + require.True(stdlib.Is(err, ErrTxTooLarge)) err = Wrap(ErrInsufficientFee, "...") - require.False(t, 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 TestWrappedIsMultiple(t *testing.T) { +func (s *errorsTestSuite) TestWrappedIsMultiple() { var errTest = errors.New("test error") var errTest2 = errors.New("test error 2") err := Wrap(errTest2, Wrap(errTest, "some random description").Error()) - require.True(t, stdlib.Is(err, errTest2)) + s.Require().True(stdlib.Is(err, errTest2)) } -func TestWrappedIsFail(t *testing.T) { +func (s *errorsTestSuite) TestWrappedIsFail() { var errTest = errors.New("test error") var errTest2 = errors.New("test error 2") err := Wrap(errTest2, Wrap(errTest, "some random description").Error()) - require.False(t, stdlib.Is(err, errTest)) + s.Require().False(stdlib.Is(err, errTest)) } -func TestWrappedUnwrap(t *testing.T) { +func (s *errorsTestSuite) TestWrappedUnwrap() { var errTest = errors.New("test error") err := Wrap(errTest, "some random description") - require.Equal(t, errTest, stdlib.Unwrap(err)) + s.Require().Equal(errTest, stdlib.Unwrap(err)) } -func TestWrappedUnwrapMultiple(t *testing.T) { +func (s *errorsTestSuite) TestWrappedUnwrapMultiple() { var errTest = errors.New("test error") var errTest2 = errors.New("test error 2") err := Wrap(errTest2, Wrap(errTest, "some random description").Error()) - require.Equal(t, errTest2, stdlib.Unwrap(err)) + s.Require().Equal(errTest2, stdlib.Unwrap(err)) } -func TestWrappedUnwrapFail(t *testing.T) { +func (s *errorsTestSuite) TestWrappedUnwrapFail() { var errTest = errors.New("test error") var errTest2 = errors.New("test error 2") err := Wrap(errTest2, Wrap(errTest, "some random description").Error()) - require.NotEqual(t, errTest, stdlib.Unwrap(err)) + s.Require().NotEqual(errTest, stdlib.Unwrap(err)) +} + +func (s *errorsTestSuite) TestABCIError() { + s.Require().Equal("custom: tx parse error", ABCIError(RootCodespace, 2, "custom").Error()) + s.Require().Equal("custom: unknown", ABCIError("unknown", 1, "custom").Error()) +} + +func ExampleWrap() { + err1 := Wrap(ErrInsufficientFunds, "90 is smaller than 100") + err2 := errors.Wrap(ErrInsufficientFunds, "90 is smaller than 100") + fmt.Println(err1.Error()) + fmt.Println(err2.Error()) + // Output: + // 90 is smaller than 100: insufficient funds + // 90 is smaller than 100: insufficient funds +} + +func ExampleWrapf() { + err1 := Wrap(ErrInsufficientFunds, "90 is smaller than 100") + err2 := errors.Wrap(ErrInsufficientFunds, "90 is smaller than 100") + fmt.Println(err1.Error()) + fmt.Println(err2.Error()) + // Output: + // 90 is smaller than 100: insufficient funds + // 90 is smaller than 100: insufficient funds } diff --git a/types/errors/stacktrace_test.go b/types/errors/stacktrace_test.go index 042edc0b6919..06eb829f0f44 100644 --- a/types/errors/stacktrace_test.go +++ b/types/errors/stacktrace_test.go @@ -5,29 +5,28 @@ import ( "fmt" "reflect" "strings" - "testing" ) -func TestStackTrace(t *testing.T) { +func (s *errorsTestSuite) TestStackTrace() { cases := map[string]struct { err error wantError string }{ "New gives us a stacktrace": { err: Wrap(ErrNoSignatures, "name"), - wantError: "no signatures supplied: name", + wantError: "name: no signatures supplied", }, "Wrapping stderr gives us a stacktrace": { err: Wrap(fmt.Errorf("foo"), "standard"), - wantError: "foo: standard", + wantError: "standard: foo", }, "Wrapping pkg/errors gives us clean stacktrace": { err: Wrap(errors.New("bar"), "pkg"), - wantError: "bar: pkg", + wantError: "pkg: bar", }, "Wrapping inside another function is still clean": { err: Wrap(fmt.Errorf("indirect"), "do the do"), - wantError: "indirect: do the do", + wantError: "do the do: indirect", }, } @@ -39,45 +38,25 @@ func TestStackTrace(t *testing.T) { } const thisTestSrc = "types/errors/stacktrace_test.go" - for testName, tc := range cases { - tc := tc - t.Run(testName, func(t *testing.T) { - if !reflect.DeepEqual(tc.err.Error(), tc.wantError) { - t.Fatalf("errors not equal, got '%s', want '%s'", tc.err.Error(), tc.wantError) - } - - if stackTrace(tc.err) == nil { - t.Fatal("expected a stack trace to be present") - } + for _, tc := range cases { + s.Require().True(reflect.DeepEqual(tc.err.Error(), tc.wantError)) + s.Require().NotNil(stackTrace(tc.err)) + fullStack := fmt.Sprintf("%+v", tc.err) + s.Require().True(strings.Contains(fullStack, thisTestSrc)) + s.Require().True(strings.Contains(fullStack, tc.wantError)) - fullStack := fmt.Sprintf("%+v", tc.err) - if !strings.Contains(fullStack, thisTestSrc) { - t.Logf("Stack trace below\n----%s\n----", fullStack) - t.Error("full stack trace should contain this test source code information") - } - if !strings.Contains(fullStack, tc.wantError) { - t.Logf("Stack trace below\n----%s\n----", fullStack) - t.Error("full stack trace should contain the error description") - } - for _, src := range unwantedSrc { - if strings.Contains(fullStack, src) { - t.Logf("Stack trace below\n----%s\n----", fullStack) - t.Logf("full stack contains unwanted source file path: %q", src) - } + for _, src := range unwantedSrc { + if strings.Contains(fullStack, src) { + s.T().Logf("Stack trace below\n----%s\n----", fullStack) + s.T().Logf("full stack contains unwanted source file path: %q", src) } + } - tinyStack := fmt.Sprintf("%v", tc.err) - if !strings.HasPrefix(tinyStack, tc.wantError) { - t.Fatalf("prefix mimssing: %s", tinyStack) - } - if strings.Contains(tinyStack, "\n") { - t.Fatal("only one stack line is expected") - } - // contains a link to where it was created, which must - // be here, not the Wrap() function - if !strings.Contains(tinyStack, thisTestSrc) { - t.Fatalf("this file missing in stack info:\n %s", tinyStack) - } - }) + tinyStack := fmt.Sprintf("%v", tc.err) + s.Require().True(strings.HasPrefix(tinyStack, tc.wantError)) + s.Require().False(strings.Contains(tinyStack, "\n")) + // contains a link to where it was created, which must + // be here, not the Wrap() function + s.Require().True(strings.Contains(tinyStack, thisTestSrc)) } } diff --git a/types/events.go b/types/events.go index 10723931e9cd..5a2bf3af4b08 100644 --- a/types/events.go +++ b/types/events.go @@ -1,12 +1,17 @@ package types import ( + "encoding/json" "fmt" + "reflect" "sort" "strings" + "github.com/gogo/protobuf/jsonpb" + proto "github.com/gogo/protobuf/proto" abci "github.com/tendermint/tendermint/abci/types" - tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/codec" ) // ---------------------------------------------------------------------------- @@ -26,11 +31,13 @@ func NewEventManager() *EventManager { func (em *EventManager) Events() Events { return em.events } // EmitEvent stores a single Event object. +// Deprecated: Use EmitTypedEvent func (em *EventManager) EmitEvent(event Event) { em.events = em.events.AppendEvent(event) } // EmitEvents stores a series of Event objects. +// Deprecated: Use EmitTypedEvents func (em *EventManager) EmitEvents(events Events) { em.events = em.events.AppendEvents(events) } @@ -40,6 +47,97 @@ func (em EventManager) ABCIEvents() []abci.Event { return em.events.ToABCIEvents() } +// EmitTypedEvent takes typed event and emits converting it into Event +func (em *EventManager) EmitTypedEvent(tev proto.Message) error { + event, err := TypedEventToEvent(tev) + if err != nil { + return err + } + + em.EmitEvent(event) + return nil +} + +// EmitTypedEvents takes series of typed events and emit +func (em *EventManager) EmitTypedEvents(tevs ...proto.Message) error { + events := make(Events, len(tevs)) + for i, tev := range tevs { + res, err := TypedEventToEvent(tev) + if err != nil { + return err + } + events[i] = res + } + + em.EmitEvents(events) + return nil +} + +// TypedEventToEvent takes typed event and converts to Event object +func TypedEventToEvent(tev proto.Message) (Event, error) { + evtType := proto.MessageName(tev) + evtJSON, err := codec.ProtoMarshalJSON(tev, nil) + if err != nil { + return Event{}, err + } + + var attrMap map[string]json.RawMessage + err = json.Unmarshal(evtJSON, &attrMap) + if err != nil { + return Event{}, err + } + + attrs := make([]abci.EventAttribute, 0, len(attrMap)) + for k, v := range attrMap { + attrs = append(attrs, abci.EventAttribute{ + Key: []byte(k), + Value: v, + }) + } + + return Event{ + Type: evtType, + Attributes: attrs, + }, nil +} + +// ParseTypedEvent converts abci.Event back to typed event +func ParseTypedEvent(event abci.Event) (proto.Message, error) { + concreteGoType := proto.MessageType(event.Type) + if concreteGoType == nil { + return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type) + } + + var value reflect.Value + if concreteGoType.Kind() == reflect.Ptr { + value = reflect.New(concreteGoType.Elem()) + } else { + value = reflect.Zero(concreteGoType) + } + + protoMsg, ok := value.Interface().(proto.Message) + if !ok { + return nil, fmt.Errorf("%q does not implement proto.Message", event.Type) + } + + attrMap := make(map[string]json.RawMessage) + for _, attr := range event.Attributes { + attrMap[string(attr.Key)] = attr.Value + } + + attrBytes, err := json.Marshal(attrMap) + if err != nil { + return nil, err + } + + err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg) + if err != nil { + return nil, err + } + + return protoMsg, nil +} + // ---------------------------------------------------------------------------- // Events // ---------------------------------------------------------------------------- @@ -48,13 +146,6 @@ type ( // Event is a type alias for an ABCI Event Event abci.Event - // Attribute defines an attribute wrapper where the key and value are - // strings instead of raw bytes. - Attribute struct { - Key string `json:"key"` - Value string `json:"value,omitempty"` - } - // Events defines a slice of Event objects Events []Event ) @@ -86,8 +177,8 @@ func (a Attribute) String() string { } // ToKVPair converts an Attribute object into a Tendermint key/value pair. -func (a Attribute) ToKVPair() tmkv.Pair { - return tmkv.Pair{Key: toBytes(a.Key), Value: toBytes(a.Value)} +func (a Attribute) ToKVPair() abci.EventAttribute { + return abci.EventAttribute{Key: toBytes(a.Key), Value: toBytes(a.Value)} } // AppendAttributes adds one or more attributes to an Event. @@ -141,13 +232,6 @@ var ( ) type ( - // StringAttribute defines en Event object wrapper where all the attributes - // contain key/value pairs that are strings instead of raw bytes. - StringEvent struct { - Type string `json:"type,omitempty"` - Attributes []Attribute `json:"attributes,omitempty"` - } - // StringAttributes defines a slice of StringEvents objects. StringEvents []StringEvent ) @@ -214,3 +298,32 @@ func StringifyEvents(events []abci.Event) StringEvents { return res.Flatten() } + +// MarkEventsToIndex returns the set of ABCI events, where each event's attribute +// has it's index value marked based on the provided set of events to index. +func MarkEventsToIndex(events []abci.Event, indexSet map[string]struct{}) []abci.Event { + indexAll := len(indexSet) == 0 + updatedEvents := make([]abci.Event, len(events)) + + for i, e := range events { + updatedEvent := abci.Event{ + Type: e.Type, + Attributes: make([]abci.EventAttribute, len(e.Attributes)), + } + + for j, attr := range e.Attributes { + _, index := indexSet[fmt.Sprintf("%s.%s", e.Type, attr.Key)] + updatedAttr := abci.EventAttribute{ + Key: attr.Key, + Value: attr.Value, + Index: index || indexAll, + } + + updatedEvent.Attributes[j] = updatedAttr + } + + updatedEvents[i] = updatedEvent + } + + return updatedEvents +} diff --git a/types/events_test.go b/types/events_test.go index d00364eac0ba..7363355fb1b8 100644 --- a/types/events_test.go +++ b/types/events_test.go @@ -1,71 +1,221 @@ -package types +package types_test import ( "encoding/json" + "reflect" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + testdata "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestAppendEvents(t *testing.T) { - e1 := NewEvent("transfer", NewAttribute("sender", "foo")) - e2 := NewEvent("transfer", NewAttribute("sender", "bar")) - a := Events{e1} - b := Events{e2} +type eventsTestSuite struct { + suite.Suite +} + +func TestEventsTestSuite(t *testing.T) { + suite.Run(t, new(eventsTestSuite)) +} + +func (s *eventsTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *eventsTestSuite) TestAppendEvents() { + e1 := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo")) + e2 := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "bar")) + a := sdk.Events{e1} + b := sdk.Events{e2} c := a.AppendEvents(b) - require.Equal(t, c, Events{e1, e2}) - require.Equal(t, c, Events{e1}.AppendEvent(NewEvent("transfer", NewAttribute("sender", "bar")))) - require.Equal(t, c, Events{e1}.AppendEvents(Events{e2})) + s.Require().Equal(c, sdk.Events{e1, e2}) + s.Require().Equal(c, sdk.Events{e1}.AppendEvent(sdk.NewEvent("transfer", sdk.NewAttribute("sender", "bar")))) + s.Require().Equal(c, sdk.Events{e1}.AppendEvents(sdk.Events{e2})) } -func TestAppendAttributes(t *testing.T) { - e := NewEvent("transfer", NewAttribute("sender", "foo")) - e = e.AppendAttributes(NewAttribute("recipient", "bar")) - require.Len(t, e.Attributes, 2) - require.Equal(t, e, NewEvent("transfer", NewAttribute("sender", "foo"), NewAttribute("recipient", "bar"))) +func (s *eventsTestSuite) TestAppendAttributes() { + e := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo")) + e = e.AppendAttributes(sdk.NewAttribute("recipient", "bar")) + s.Require().Len(e.Attributes, 2) + s.Require().Equal(e, sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"), sdk.NewAttribute("recipient", "bar"))) } -func TestEmptyEvents(t *testing.T) { - require.Equal(t, EmptyEvents(), Events{}) +func (s *eventsTestSuite) TestEmptyEvents() { + s.Require().Equal(sdk.EmptyEvents(), sdk.Events{}) } -func TestAttributeString(t *testing.T) { - require.Equal(t, "foo: bar", NewAttribute("foo", "bar").String()) +func (s *eventsTestSuite) TestAttributeString() { + s.Require().Equal("foo: bar", sdk.NewAttribute("foo", "bar").String()) } -func TestToABCIEvents(t *testing.T) { - e := Events{NewEvent("transfer", NewAttribute("sender", "foo"))} +func (s *eventsTestSuite) TestToABCIEvents() { + e := sdk.Events{sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"))} abciEvents := e.ToABCIEvents() - require.Len(t, abciEvents, 1) - require.Equal(t, abciEvents[0].Type, e[0].Type) - require.Equal(t, abciEvents[0].Attributes, e[0].Attributes) + s.Require().Len(abciEvents, 1) + s.Require().Equal(abciEvents[0].Type, e[0].Type) + s.Require().Equal(abciEvents[0].Attributes, e[0].Attributes) } -func TestEventManager(t *testing.T) { - em := NewEventManager() - event := NewEvent("reward", NewAttribute("x", "y")) - events := Events{NewEvent("transfer", NewAttribute("sender", "foo"))} +func (s *eventsTestSuite) TestEventManager() { + em := sdk.NewEventManager() + event := sdk.NewEvent("reward", sdk.NewAttribute("x", "y")) + events := sdk.Events{sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"))} em.EmitEvents(events) em.EmitEvent(event) - require.Len(t, em.Events(), 2) - require.Equal(t, em.Events(), events.AppendEvent(event)) + s.Require().Len(em.Events(), 2) + s.Require().Equal(em.Events(), events.AppendEvent(event)) +} + +func (s *eventsTestSuite) TestEventManagerTypedEvents() { + em := sdk.NewEventManager() + + coin := sdk.NewCoin("fakedenom", sdk.NewInt(1999999)) + cat := testdata.Cat{ + Moniker: "Garfield", + Lives: 6, + } + animal, err := codectypes.NewAnyWithValue(&cat) + s.Require().NoError(err) + hasAnimal := testdata.HasAnimal{ + X: 1000, + Animal: animal, + } + + s.Require().NoError(em.EmitTypedEvents(&coin)) + s.Require().NoError(em.EmitTypedEvent(&hasAnimal)) + s.Require().Len(em.Events(), 2) + + msg1, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[0]) + s.Require().NoError(err) + s.Require().Equal(coin.String(), msg1.String()) + s.Require().Equal(reflect.TypeOf(&coin), reflect.TypeOf(msg1)) + + msg2, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[1]) + s.Require().NoError(err) + s.Require().Equal(reflect.TypeOf(&hasAnimal), reflect.TypeOf(msg2)) + response := msg2.(*testdata.HasAnimal) + s.Require().Equal(hasAnimal.Animal.String(), response.Animal.String()) } -func TestStringifyEvents(t *testing.T) { - e := Events{ - NewEvent("message", NewAttribute("sender", "foo")), - NewEvent("message", NewAttribute("module", "bank")), +func (s *eventsTestSuite) TestStringifyEvents() { + e := sdk.Events{ + sdk.NewEvent("message", sdk.NewAttribute("sender", "foo")), + sdk.NewEvent("message", sdk.NewAttribute("module", "bank")), } - se := StringifyEvents(e.ToABCIEvents()) + se := sdk.StringifyEvents(e.ToABCIEvents()) expectedTxtStr := "\t\t- message\n\t\t\t- sender: foo\n\t\t\t- module: bank" - require.Equal(t, expectedTxtStr, se.String()) + s.Require().Equal(expectedTxtStr, se.String()) bz, err := json.Marshal(se) - require.NoError(t, err) + s.Require().NoError(err) expectedJSONStr := "[{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"foo\"},{\"key\":\"module\",\"value\":\"bank\"}]}]" - require.Equal(t, expectedJSONStr, string(bz)) + s.Require().Equal(expectedJSONStr, string(bz)) +} + +func (s *eventsTestSuite) TestMarkEventsToIndex() { + events := []abci.Event{ + { + Type: "message", + Attributes: []abci.EventAttribute{ + {Key: []byte("sender"), Value: []byte("foo")}, + {Key: []byte("recipient"), Value: []byte("bar")}, + }, + }, + { + Type: "staking", + Attributes: []abci.EventAttribute{ + {Key: []byte("deposit"), Value: []byte("5")}, + {Key: []byte("unbond"), Value: []byte("10")}, + }, + }, + } + + testCases := map[string]struct { + events []abci.Event + indexSet map[string]struct{} + expected []abci.Event + }{ + "empty index set": { + events: events, + expected: []abci.Event{ + { + Type: "message", + Attributes: []abci.EventAttribute{ + {Key: []byte("sender"), Value: []byte("foo"), Index: true}, + {Key: []byte("recipient"), Value: []byte("bar"), Index: true}, + }, + }, + { + Type: "staking", + Attributes: []abci.EventAttribute{ + {Key: []byte("deposit"), Value: []byte("5"), Index: true}, + {Key: []byte("unbond"), Value: []byte("10"), Index: true}, + }, + }, + }, + indexSet: map[string]struct{}{}, + }, + "index some events": { + events: events, + expected: []abci.Event{ + { + Type: "message", + Attributes: []abci.EventAttribute{ + {Key: []byte("sender"), Value: []byte("foo"), Index: true}, + {Key: []byte("recipient"), Value: []byte("bar")}, + }, + }, + { + Type: "staking", + Attributes: []abci.EventAttribute{ + {Key: []byte("deposit"), Value: []byte("5"), Index: true}, + {Key: []byte("unbond"), Value: []byte("10")}, + }, + }, + }, + indexSet: map[string]struct{}{ + "message.sender": {}, + "staking.deposit": {}, + }, + }, + "index all events": { + events: events, + expected: []abci.Event{ + { + Type: "message", + Attributes: []abci.EventAttribute{ + {Key: []byte("sender"), Value: []byte("foo"), Index: true}, + {Key: []byte("recipient"), Value: []byte("bar"), Index: true}, + }, + }, + { + Type: "staking", + Attributes: []abci.EventAttribute{ + {Key: []byte("deposit"), Value: []byte("5"), Index: true}, + {Key: []byte("unbond"), Value: []byte("10"), Index: true}, + }, + }, + }, + indexSet: map[string]struct{}{ + "message.sender": {}, + "message.recipient": {}, + "staking.deposit": {}, + "staking.unbond": {}, + }, + }, + } + + for name, tc := range testCases { + tc := tc + s.T().Run(name, func(_ *testing.T) { + s.Require().Equal(tc.expected, sdk.MarkEventsToIndex(tc.events, tc.indexSet)) + }) + } } diff --git a/types/grpc/headers.go b/types/grpc/headers.go new file mode 100644 index 000000000000..091530795807 --- /dev/null +++ b/types/grpc/headers.go @@ -0,0 +1,6 @@ +package grpc + +const ( + // GRPCBlockHeightHeader is the gRPC header for block height. + GRPCBlockHeightHeader = "x-cosmos-block-height" +) diff --git a/types/handler.go b/types/handler.go index 6815230a861f..03d1f02d5806 100644 --- a/types/handler.go +++ b/types/handler.go @@ -25,15 +25,15 @@ type AnteDecorator interface { // MUST set GasMeter with the FIRST AnteDecorator. Failing to do so will cause // transactions to be processed with an infinite gasmeter and open a DOS attack vector. // Use `ante.SetUpContextDecorator` or a custom Decorator with similar functionality. +// Returns nil when no AnteDecorator are supplied. func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { - if (chain[len(chain)-1] != Terminator{}) { - chain = append(chain, Terminator{}) + if len(chain) == 0 { + return nil } - if len(chain) == 1 { - return func(ctx Context, tx Tx, simulate bool) (Context, error) { - return chain[0].AnteHandle(ctx, tx, simulate, nil) - } + // handle non-terminated decorators chain + if (chain[len(chain)-1] != Terminator{}) { + chain = append(chain, Terminator{}) } return func(ctx Context, tx Tx, simulate bool) (Context, error) { diff --git a/types/handler_test.go b/types/handler_test.go new file mode 100644 index 000000000000..b71980fd060d --- /dev/null +++ b/types/handler_test.go @@ -0,0 +1,39 @@ +package types_test + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/tests/mocks" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type handlerTestSuite struct { + suite.Suite +} + +func TestHandlerTestSuite(t *testing.T) { + suite.Run(t, new(handlerTestSuite)) +} + +func (s *handlerTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *handlerTestSuite) TestChainAnteDecorators() { + // test panic + s.Require().Nil(sdk.ChainAnteDecorators([]sdk.AnteDecorator{}...)) + + ctx, tx := sdk.Context{}, sdk.Tx(nil) + mockCtrl := gomock.NewController(s.T()) + mockAnteDecorator1 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1)(ctx, tx, true) //nolint:errcheck + + mockAnteDecorator2 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, mockAnteDecorator2).Times(1) + mockAnteDecorator2.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, nil).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1, mockAnteDecorator2) +} diff --git a/types/int.go b/types/int.go index caabdb11318c..81f6b1c04ddb 100644 --- a/types/int.go +++ b/types/int.go @@ -1,6 +1,7 @@ package types import ( + "encoding" "encoding/json" "fmt" "testing" @@ -52,12 +53,6 @@ func max(i *big.Int, i2 *big.Int) *big.Int { return new(big.Int).Set(i) } -// MarshalAmino for custom encoding scheme -func marshalAmino(i *big.Int) (string, error) { - bz, err := i.MarshalText() - return string(bz), err -} - func unmarshalText(i *big.Int, text string) error { if err := i.UnmarshalText([]byte(text)); err != nil { return err @@ -70,32 +65,7 @@ func unmarshalText(i *big.Int, text string) error { return nil } -// UnmarshalAmino for custom decoding scheme -func unmarshalAmino(i *big.Int, text string) (err error) { - return unmarshalText(i, text) -} - -// MarshalJSON for custom encoding scheme -// Must be encoded as a string for JSON precision -func marshalJSON(i *big.Int) ([]byte, error) { - text, err := i.MarshalText() - if err != nil { - return nil, err - } - return json.Marshal(string(text)) -} - -// UnmarshalJSON for custom decoding scheme -// Must be encoded as a string for JSON precision -func unmarshalJSON(i *big.Int, bz []byte) error { - var text string - err := json.Unmarshal(bz, &text) - if err != nil { - return err - } - - return unmarshalText(i, text) -} +var _ CustomProtobufType = (*Int)(nil) // Int wraps integer with 256 bit range bound // Checks overflow, underflow and division by zero @@ -106,9 +76,17 @@ type Int struct { // BigInt converts Int to big.Int func (i Int) BigInt() *big.Int { + if i.IsNil() { + return nil + } return new(big.Int).Set(i.i) } +// IsNil returns true if Int is uninitialized +func (i Int) IsNil() bool { + return i.i == nil +} + // NewInt constructs Int from int64 func NewInt(n int64) Int { return Int{big.NewInt(n)} @@ -341,42 +319,113 @@ func (i Int) String() string { return i.i.String() } -// MarshalAmino defines custom encoding scheme -func (i Int) MarshalAmino() (string, error) { +// MarshalJSON defines custom encoding scheme +func (i Int) MarshalJSON() ([]byte, error) { if i.i == nil { // Necessary since default Uint initialization has i.i as nil i.i = new(big.Int) } - return marshalAmino(i.i) + return marshalJSON(i.i) } -// UnmarshalAmino defines custom decoding scheme -func (i *Int) UnmarshalAmino(text string) error { +// UnmarshalJSON defines custom decoding scheme +func (i *Int) UnmarshalJSON(bz []byte) error { if i.i == nil { // Necessary since default Int initialization has i.i as nil i.i = new(big.Int) } - return unmarshalAmino(i.i, text) + return unmarshalJSON(i.i, bz) } -// MarshalJSON defines custom encoding scheme -func (i Int) MarshalJSON() ([]byte, error) { - if i.i == nil { // Necessary since default Uint initialization has i.i as nil +// MarshalJSON for custom encoding scheme +// Must be encoded as a string for JSON precision +func marshalJSON(i encoding.TextMarshaler) ([]byte, error) { + text, err := i.MarshalText() + if err != nil { + return nil, err + } + + return json.Marshal(string(text)) +} + +// UnmarshalJSON for custom decoding scheme +// Must be encoded as a string for JSON precision +func unmarshalJSON(i *big.Int, bz []byte) error { + var text string + if err := json.Unmarshal(bz, &text); err != nil { + return err + } + + return unmarshalText(i, text) +} + +// MarshalYAML returns the YAML representation. +func (i Int) MarshalYAML() (interface{}, error) { + return i.String(), nil +} + +// Marshal implements the gogo proto custom type interface. +func (i Int) Marshal() ([]byte, error) { + if i.i == nil { i.i = new(big.Int) } - return marshalJSON(i.i) + return i.i.MarshalText() } -// UnmarshalJSON defines custom decoding scheme -func (i *Int) UnmarshalJSON(bz []byte) error { - if i.i == nil { // Necessary since default Int initialization has i.i as nil +// MarshalTo implements the gogo proto custom type interface. +func (i *Int) MarshalTo(data []byte) (n int, err error) { + if i.i == nil { i.i = new(big.Int) } - return unmarshalJSON(i.i, bz) + if len(i.i.Bytes()) == 0 { + copy(data, []byte{0x30}) + return 1, nil + } + + bz, err := i.Marshal() + if err != nil { + return 0, err + } + + copy(data, bz) + return len(bz), nil } -// MarshalYAML returns Ythe AML representation. -func (i Int) MarshalYAML() (interface{}, error) { return i.String(), nil } +// Unmarshal implements the gogo proto custom type interface. +func (i *Int) Unmarshal(data []byte) error { + if len(data) == 0 { + i = nil + return nil + } + + if i.i == nil { + i.i = new(big.Int) + } + + if err := i.i.UnmarshalText(data); err != nil { + return err + } + + if i.i.BitLen() > maxBitLen { + return fmt.Errorf("integer out of range; got: %d, max: %d", i.i.BitLen(), maxBitLen) + } + + return nil +} + +// Size implements the gogo proto custom type interface. +func (i *Int) Size() int { + bz, _ := i.Marshal() + return len(bz) +} + +// Override Amino binary serialization by proxying to protobuf. +func (i Int) MarshalAmino() ([]byte, error) { return i.Marshal() } +func (i *Int) UnmarshalAmino(bz []byte) error { return i.Unmarshal(bz) } // intended to be used with require/assert: require.True(IntEq(...)) func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() } + +func (ip IntProto) String() string { + return ip.Int.String() +} diff --git a/types/int_internal_test.go b/types/int_internal_test.go new file mode 100644 index 000000000000..c6b039948e69 --- /dev/null +++ b/types/int_internal_test.go @@ -0,0 +1,137 @@ +package types + +import ( + "math/big" + "math/rand" + "testing" + + "github.com/stretchr/testify/suite" +) + +type internalIntTestSuite struct { + suite.Suite +} + +func TestInternalIntTestSuite(t *testing.T) { + suite.Run(t, new(internalIntTestSuite)) +} + +func (s *internalIntTestSuite) TestEncodingRandom() { + for i := 0; i < 1000; i++ { + n := rand.Int63() + ni := NewInt(n) + var ri Int + + str, err := ni.Marshal() + s.Require().Nil(err) + err = (&ri).Unmarshal(str) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + + bz, err := ni.MarshalJSON() + s.Require().Nil(err) + err = (&ri).UnmarshalJSON(bz) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + } + + for i := 0; i < 1000; i++ { + n := rand.Uint64() + ni := NewUint(n) + var ri Uint + + str, err := ni.Marshal() + s.Require().Nil(err) + err = (&ri).Unmarshal(str) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "binary mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + + bz, err := ni.MarshalJSON() + s.Require().Nil(err) + err = (&ri).UnmarshalJSON(bz) + s.Require().Nil(err) + + s.Require().Equal(ni, ri, "json mismatch; tc #%d, expected %s, actual %s", i, ni.String(), ri.String()) + s.Require().True(ni.i != ri.i, "pointer addresses are equal; tc #%d", i) + } +} + +func (s *internalIntTestSuite) TestSerializationOverflow() { + bx, _ := new(big.Int).SetString("91888242871839275229946405745257275988696311157297823662689937894645226298583", 10) + x := Int{bx} + y := new(Int) + + bz, err := x.Marshal() + s.Require().NoError(err) + + // require deserialization to fail due to overflow + s.Require().Error(y.Unmarshal(bz)) + + // require JSON deserialization to fail due to overflow + bz, err = x.MarshalJSON() + s.Require().NoError(err) + + s.Require().Error(y.UnmarshalJSON(bz)) +} + +func (s *internalIntTestSuite) TestImmutabilityArithInt() { + size := 500 + + ops := []intOp{ + applyWithRand(Int.Add, (*big.Int).Add), + applyWithRand(Int.Sub, (*big.Int).Sub), + applyWithRand(Int.Mul, (*big.Int).Mul), + applyWithRand(Int.Quo, (*big.Int).Quo), + applyRawWithRand(Int.AddRaw, (*big.Int).Add), + applyRawWithRand(Int.SubRaw, (*big.Int).Sub), + applyRawWithRand(Int.MulRaw, (*big.Int).Mul), + applyRawWithRand(Int.QuoRaw, (*big.Int).Quo), + } + + for i := 0; i < 100; i++ { + uis := make([]Int, size) + bis := make([]*big.Int, size) + + n := rand.Int63() + ui := NewInt(n) + bi := new(big.Int).SetInt64(n) + + for j := 0; j < size; j++ { + op := ops[rand.Intn(len(ops))] + uis[j], bis[j] = op(ui, bi) + } + + for j := 0; j < size; j++ { + s.Require().Equal(0, bis[j].Cmp(uis[j].BigInt()), "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) + s.Require().Equal(NewIntFromBigInt(bis[j]), uis[j], "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) + s.Require().True(uis[j].i != bis[j], "Pointer addresses are equal. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) + } + } +} + +type ( + intOp func(Int, *big.Int) (Int, *big.Int) + bigIntFunc func(*big.Int, *big.Int, *big.Int) *big.Int +) + +func applyWithRand(intFn func(Int, Int) Int, bigIntFn bigIntFunc) intOp { + return func(integer Int, bigInteger *big.Int) (Int, *big.Int) { + r := rand.Int63() + br := new(big.Int).SetInt64(r) + return intFn(integer, NewInt(r)), bigIntFn(new(big.Int), bigInteger, br) + } +} + +func applyRawWithRand(intFn func(Int, int64) Int, bigIntFn bigIntFunc) intOp { + return func(integer Int, bigInteger *big.Int) (Int, *big.Int) { + r := rand.Int63() + br := new(big.Int).SetInt64(r) + return intFn(integer, r), bigIntFn(new(big.Int), bigInteger, br) + } +} diff --git a/types/int_test.go b/types/int_test.go index 072b2f47bb9a..4b26ddb8ada5 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "math/big" @@ -6,102 +6,118 @@ import ( "strconv" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestFromInt64(t *testing.T) { +type intTestSuite struct { + suite.Suite +} + +func TestIntTestSuite(t *testing.T) { + suite.Run(t, new(intTestSuite)) +} + +func (s *intTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *intTestSuite) TestFromInt64() { for n := 0; n < 20; n++ { r := rand.Int63() - require.Equal(t, r, NewInt(r).Int64()) + s.Require().Equal(r, sdk.NewInt(r).Int64()) } } -func TestFromUint64(t *testing.T) { +func (s *intTestSuite) TestFromUint64() { for n := 0; n < 20; n++ { r := rand.Uint64() - require.True(t, NewIntFromUint64(r).IsUint64()) - require.Equal(t, r, NewIntFromUint64(r).Uint64()) + s.Require().True(sdk.NewIntFromUint64(r).IsUint64()) + s.Require().Equal(r, sdk.NewIntFromUint64(r).Uint64()) } } -func TestIntPanic(t *testing.T) { +func (s *intTestSuite) TestIntPanic() { // Max Int = 2^255-1 = 5.789e+76 // Min Int = -(2^255-1) = -5.789e+76 - require.NotPanics(t, func() { NewIntWithDecimal(1, 76) }) - i1 := NewIntWithDecimal(1, 76) - require.NotPanics(t, func() { NewIntWithDecimal(2, 76) }) - i2 := NewIntWithDecimal(2, 76) - require.NotPanics(t, func() { NewIntWithDecimal(3, 76) }) - i3 := NewIntWithDecimal(3, 76) + s.Require().NotPanics(func() { sdk.NewIntWithDecimal(1, 76) }) + i1 := sdk.NewIntWithDecimal(1, 76) + s.Require().NotPanics(func() { sdk.NewIntWithDecimal(2, 76) }) + i2 := sdk.NewIntWithDecimal(2, 76) + s.Require().NotPanics(func() { sdk.NewIntWithDecimal(3, 76) }) + i3 := sdk.NewIntWithDecimal(3, 76) - require.Panics(t, func() { NewIntWithDecimal(6, 76) }) - require.Panics(t, func() { NewIntWithDecimal(9, 80) }) + s.Require().Panics(func() { sdk.NewIntWithDecimal(6, 76) }) + s.Require().Panics(func() { sdk.NewIntWithDecimal(9, 80) }) // Overflow check - require.NotPanics(t, func() { i1.Add(i1) }) - require.NotPanics(t, func() { i2.Add(i2) }) - require.Panics(t, func() { i3.Add(i3) }) + s.Require().NotPanics(func() { i1.Add(i1) }) + s.Require().NotPanics(func() { i2.Add(i2) }) + s.Require().Panics(func() { i3.Add(i3) }) - require.NotPanics(t, func() { i1.Sub(i1.Neg()) }) - require.NotPanics(t, func() { i2.Sub(i2.Neg()) }) - require.Panics(t, func() { i3.Sub(i3.Neg()) }) + s.Require().NotPanics(func() { i1.Sub(i1.Neg()) }) + s.Require().NotPanics(func() { i2.Sub(i2.Neg()) }) + s.Require().Panics(func() { i3.Sub(i3.Neg()) }) - require.Panics(t, func() { i1.Mul(i1) }) - require.Panics(t, func() { i2.Mul(i2) }) - require.Panics(t, func() { i3.Mul(i3) }) + s.Require().Panics(func() { i1.Mul(i1) }) + s.Require().Panics(func() { i2.Mul(i2) }) + s.Require().Panics(func() { i3.Mul(i3) }) - require.Panics(t, func() { i1.Neg().Mul(i1.Neg()) }) - require.Panics(t, func() { i2.Neg().Mul(i2.Neg()) }) - require.Panics(t, func() { i3.Neg().Mul(i3.Neg()) }) + s.Require().Panics(func() { i1.Neg().Mul(i1.Neg()) }) + s.Require().Panics(func() { i2.Neg().Mul(i2.Neg()) }) + s.Require().Panics(func() { i3.Neg().Mul(i3.Neg()) }) // Underflow check i3n := i3.Neg() - require.NotPanics(t, func() { i3n.Sub(i1) }) - require.NotPanics(t, func() { i3n.Sub(i2) }) - require.Panics(t, func() { i3n.Sub(i3) }) + s.Require().NotPanics(func() { i3n.Sub(i1) }) + s.Require().NotPanics(func() { i3n.Sub(i2) }) + s.Require().Panics(func() { i3n.Sub(i3) }) - require.NotPanics(t, func() { i3n.Add(i1.Neg()) }) - require.NotPanics(t, func() { i3n.Add(i2.Neg()) }) - require.Panics(t, func() { i3n.Add(i3.Neg()) }) + s.Require().NotPanics(func() { i3n.Add(i1.Neg()) }) + s.Require().NotPanics(func() { i3n.Add(i2.Neg()) }) + s.Require().Panics(func() { i3n.Add(i3.Neg()) }) - require.Panics(t, func() { i1.Mul(i1.Neg()) }) - require.Panics(t, func() { i2.Mul(i2.Neg()) }) - require.Panics(t, func() { i3.Mul(i3.Neg()) }) + s.Require().Panics(func() { i1.Mul(i1.Neg()) }) + s.Require().Panics(func() { i2.Mul(i2.Neg()) }) + s.Require().Panics(func() { i3.Mul(i3.Neg()) }) // Bound check - intmax := NewIntFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil), big.NewInt(1))) + intmax := sdk.NewIntFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil), big.NewInt(1))) intmin := intmax.Neg() - require.NotPanics(t, func() { intmax.Add(ZeroInt()) }) - require.NotPanics(t, func() { intmin.Sub(ZeroInt()) }) - require.Panics(t, func() { intmax.Add(OneInt()) }) - require.Panics(t, func() { intmin.Sub(OneInt()) }) + s.Require().NotPanics(func() { intmax.Add(sdk.ZeroInt()) }) + s.Require().NotPanics(func() { intmin.Sub(sdk.ZeroInt()) }) + s.Require().Panics(func() { intmax.Add(sdk.OneInt()) }) + s.Require().Panics(func() { intmin.Sub(sdk.OneInt()) }) // Division-by-zero check - require.Panics(t, func() { i1.Quo(NewInt(0)) }) + s.Require().Panics(func() { i1.Quo(sdk.NewInt(0)) }) + + s.Require().NotPanics(func() { sdk.Int{}.BigInt() }) } // Tests below uses randomness // Since we are using *big.Int as underlying value // and (U/)Int is immutable value(see TestImmutability(U/)Int) // it is safe to use randomness in the tests -func TestIdentInt(t *testing.T) { +func (s *intTestSuite) TestIdentInt() { for d := 0; d < 1000; d++ { n := rand.Int63() - i := NewInt(n) + i := sdk.NewInt(n) - ifromstr, ok := NewIntFromString(strconv.FormatInt(n, 10)) - require.True(t, ok) + ifromstr, ok := sdk.NewIntFromString(strconv.FormatInt(n, 10)) + s.Require().True(ok) cases := []int64{ i.Int64(), i.BigInt().Int64(), ifromstr.Int64(), - NewIntFromBigInt(big.NewInt(n)).Int64(), - NewIntWithDecimal(n, 0).Int64(), + sdk.NewIntFromBigInt(big.NewInt(n)).Int64(), + sdk.NewIntWithDecimal(n, 0).Int64(), } for tcnum, tc := range cases { - require.Equal(t, n, tc, "Int is modified during conversion. tc #%d", tcnum) + s.Require().Equal(n, tc, "Int is modified during conversion. tc #%d", tcnum) } } } @@ -120,15 +136,15 @@ func maxint(i1, i2 int64) int64 { return i2 } -func TestArithInt(t *testing.T) { +func (s *intTestSuite) TestArithInt() { for d := 0; d < 1000; d++ { n1 := int64(rand.Int31()) - i1 := NewInt(n1) + i1 := sdk.NewInt(n1) n2 := int64(rand.Int31()) - i2 := NewInt(n2) + i2 := sdk.NewInt(n2) cases := []struct { - ires Int + ires sdk.Int nres int64 }{ {i1.Add(i2), n1 + n2}, @@ -139,24 +155,24 @@ func TestArithInt(t *testing.T) { {i1.SubRaw(n2), n1 - n2}, {i1.MulRaw(n2), n1 * n2}, {i1.QuoRaw(n2), n1 / n2}, - {MinInt(i1, i2), minint(n1, n2)}, - {MaxInt(i1, i2), maxint(n1, n2)}, + {sdk.MinInt(i1, i2), minint(n1, n2)}, + {sdk.MaxInt(i1, i2), maxint(n1, n2)}, {i1.Neg(), -n1}, } for tcnum, tc := range cases { - require.Equal(t, tc.nres, tc.ires.Int64(), "Int arithmetic operation does not match with int64 operation. tc #%d", tcnum) + s.Require().Equal(tc.nres, tc.ires.Int64(), "Int arithmetic operation does not match with int64 operation. tc #%d", tcnum) } } } -func TestCompInt(t *testing.T) { +func (s *intTestSuite) TestCompInt() { for d := 0; d < 1000; d++ { n1 := int64(rand.Int31()) - i1 := NewInt(n1) + i1 := sdk.NewInt(n1) n2 := int64(rand.Int31()) - i2 := NewInt(n2) + i2 := sdk.NewInt(n2) cases := []struct { ires bool @@ -165,243 +181,207 @@ func TestCompInt(t *testing.T) { {i1.Equal(i2), n1 == n2}, {i1.GT(i2), n1 > n2}, {i1.LT(i2), n1 < n2}, + {i1.LTE(i2), n1 <= n2}, } for tcnum, tc := range cases { - require.Equal(t, tc.nres, tc.ires, "Int comparison operation does not match with int64 operation. tc #%d", tcnum) + s.Require().Equal(tc.nres, tc.ires, "Int comparison operation does not match with int64 operation. tc #%d", tcnum) } } } -func minuint(i1, i2 uint64) uint64 { - if i1 < i2 { - return i1 - } - return i2 +func randint() sdk.Int { + return sdk.NewInt(rand.Int63()) } -func maxuint(i1, i2 uint64) uint64 { - if i1 > i2 { - return i1 - } - return i2 -} - -func randint() Int { - return NewInt(rand.Int63()) -} - -func TestImmutabilityAllInt(t *testing.T) { - ops := []func(*Int){ - func(i *Int) { _ = i.Add(randint()) }, - func(i *Int) { _ = i.Sub(randint()) }, - func(i *Int) { _ = i.Mul(randint()) }, - func(i *Int) { _ = i.Quo(randint()) }, - func(i *Int) { _ = i.AddRaw(rand.Int63()) }, - func(i *Int) { _ = i.SubRaw(rand.Int63()) }, - func(i *Int) { _ = i.MulRaw(rand.Int63()) }, - func(i *Int) { _ = i.QuoRaw(rand.Int63()) }, - func(i *Int) { _ = i.Neg() }, - func(i *Int) { _ = i.IsZero() }, - func(i *Int) { _ = i.Sign() }, - func(i *Int) { _ = i.Equal(randint()) }, - func(i *Int) { _ = i.GT(randint()) }, - func(i *Int) { _ = i.LT(randint()) }, - func(i *Int) { _ = i.String() }, +func (s *intTestSuite) TestImmutabilityAllInt() { + ops := []func(*sdk.Int){ + func(i *sdk.Int) { _ = i.Add(randint()) }, + func(i *sdk.Int) { _ = i.Sub(randint()) }, + func(i *sdk.Int) { _ = i.Mul(randint()) }, + func(i *sdk.Int) { _ = i.Quo(randint()) }, + func(i *sdk.Int) { _ = i.AddRaw(rand.Int63()) }, + func(i *sdk.Int) { _ = i.SubRaw(rand.Int63()) }, + func(i *sdk.Int) { _ = i.MulRaw(rand.Int63()) }, + func(i *sdk.Int) { _ = i.QuoRaw(rand.Int63()) }, + func(i *sdk.Int) { _ = i.Neg() }, + func(i *sdk.Int) { _ = i.IsZero() }, + func(i *sdk.Int) { _ = i.Sign() }, + func(i *sdk.Int) { _ = i.Equal(randint()) }, + func(i *sdk.Int) { _ = i.GT(randint()) }, + func(i *sdk.Int) { _ = i.LT(randint()) }, + func(i *sdk.Int) { _ = i.String() }, } for i := 0; i < 1000; i++ { n := rand.Int63() - ni := NewInt(n) + ni := sdk.NewInt(n) for opnum, op := range ops { op(&ni) - require.Equal(t, n, ni.Int64(), "Int is modified by operation. tc #%d", opnum) - require.Equal(t, NewInt(n), ni, "Int is modified by operation. tc #%d", opnum) + s.Require().Equal(n, ni.Int64(), "Int is modified by operation. tc #%d", opnum) + s.Require().Equal(sdk.NewInt(n), ni, "Int is modified by operation. tc #%d", opnum) } } } -type intop func(Int, *big.Int) (Int, *big.Int) - -func intarith(uifn func(Int, Int) Int, bifn func(*big.Int, *big.Int, *big.Int) *big.Int) intop { - return func(ui Int, bi *big.Int) (Int, *big.Int) { - r := rand.Int63() - br := new(big.Int).SetInt64(r) - return uifn(ui, NewInt(r)), bifn(new(big.Int), bi, br) - } -} - -func intarithraw(uifn func(Int, int64) Int, bifn func(*big.Int, *big.Int, *big.Int) *big.Int) intop { - return func(ui Int, bi *big.Int) (Int, *big.Int) { - r := rand.Int63() - br := new(big.Int).SetInt64(r) - return uifn(ui, r), bifn(new(big.Int), bi, br) - } -} - -func TestImmutabilityArithInt(t *testing.T) { - size := 500 - - ops := []intop{ - intarith(Int.Add, (*big.Int).Add), - intarith(Int.Sub, (*big.Int).Sub), - intarith(Int.Mul, (*big.Int).Mul), - intarith(Int.Quo, (*big.Int).Quo), - intarithraw(Int.AddRaw, (*big.Int).Add), - intarithraw(Int.SubRaw, (*big.Int).Sub), - intarithraw(Int.MulRaw, (*big.Int).Mul), - intarithraw(Int.QuoRaw, (*big.Int).Quo), - } - - for i := 0; i < 100; i++ { - uis := make([]Int, size) - bis := make([]*big.Int, size) - - n := rand.Int63() - ui := NewInt(n) - bi := new(big.Int).SetInt64(n) +func (s *intTestSuite) TestEncodingTableInt() { + var i sdk.Int - for j := 0; j < size; j++ { - op := ops[rand.Intn(len(ops))] - uis[j], bis[j] = op(ui, bi) - } - - for j := 0; j < size; j++ { - require.Equal(t, 0, bis[j].Cmp(uis[j].BigInt()), "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) - require.Equal(t, NewIntFromBigInt(bis[j]), uis[j], "Int is different from *big.Int. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) - require.True(t, uis[j].i != bis[j], "Pointer addresses are equal. tc #%d, Int %s, *big.Int %s", j, uis[j].String(), bis[j].String()) - } - } -} - -func TestEncodingRandom(t *testing.T) { - for i := 0; i < 1000; i++ { - n := rand.Int63() - ni := NewInt(n) - var ri Int - - str, err := ni.MarshalAmino() - require.Nil(t, err) - err = (&ri).UnmarshalAmino(str) - require.Nil(t, err) - - require.Equal(t, ni, ri, "MarshalAmino * UnmarshalAmino is not identity. tc #%d, Expected %s, Actual %s", i, ni.String(), ri.String()) - require.True(t, ni.i != ri.i, "Pointer addresses are equal. tc #%d", i) - - bz, err := ni.MarshalJSON() - require.Nil(t, err) - err = (&ri).UnmarshalJSON(bz) - require.Nil(t, err) - - require.Equal(t, ni, ri, "MarshalJSON * UnmarshalJSON is not identity. tc #%d, Expected %s, Actual %s", i, ni.String(), ri.String()) - require.True(t, ni.i != ri.i, "Pointer addresses are equal. tc #%d", i) + cases := []struct { + i sdk.Int + jsonBz []byte + rawBz []byte + }{ + { + sdk.NewInt(0), + []byte("\"0\""), + []byte{0x30}, + }, + { + sdk.NewInt(100), + []byte("\"100\""), + []byte{0x31, 0x30, 0x30}, + }, + { + sdk.NewInt(-100), + []byte("\"-100\""), + []byte{0x2d, 0x31, 0x30, 0x30}, + }, + { + sdk.NewInt(51842), + []byte("\"51842\""), + []byte{0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + sdk.NewInt(-51842), + []byte("\"-51842\""), + []byte{0x2d, 0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + sdk.NewInt(19513368), + []byte("\"19513368\""), + []byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + sdk.NewInt(-19513368), + []byte("\"-19513368\""), + []byte{0x2d, 0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + sdk.NewInt(999999999999), + []byte("\"999999999999\""), + []byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, + { + sdk.NewInt(-999999999999), + []byte("\"-999999999999\""), + []byte{0x2d, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, } - for i := 0; i < 1000; i++ { - n := rand.Uint64() - ni := NewUint(n) - var ri Uint - - str, err := ni.MarshalAmino() - require.Nil(t, err) - err = (&ri).UnmarshalAmino(str) - require.Nil(t, err) + for tcnum, tc := range cases { + bz, err := tc.i.MarshalJSON() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) - require.Equal(t, ni, ri, "MarshalAmino * UnmarshalAmino is not identity. tc #%d, Expected %s, Actual %s", i, ni.String(), ri.String()) - require.True(t, ni.i != ri.i, "Pointer addresses are equal. tc #%d", i) + err = (&i).UnmarshalJSON(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) - bz, err := ni.MarshalJSON() - require.Nil(t, err) - err = (&ri).UnmarshalJSON(bz) - require.Nil(t, err) + bz, err = tc.i.Marshal() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) - require.Equal(t, ni, ri, "MarshalJSON * UnmarshalJSON is not identity. tc #%d, Expected %s, Actual %s", i, ni.String(), ri.String()) - require.True(t, ni.i != ri.i, "Pointer addresses are equal. tc #%d", i) + err = (&i).Unmarshal(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) } } -func TestEncodingTableInt(t *testing.T) { - var i Int +func (s *intTestSuite) TestEncodingTableUint() { + var i sdk.Uint cases := []struct { - i Int - bz []byte - str string + i sdk.Uint + jsonBz []byte + rawBz []byte }{ - {NewInt(0), []byte("\"0\""), "0"}, - {NewInt(100), []byte("\"100\""), "100"}, - {NewInt(51842), []byte("\"51842\""), "51842"}, - {NewInt(19513368), []byte("\"19513368\""), "19513368"}, - {NewInt(999999999999), []byte("\"999999999999\""), "999999999999"}, + { + sdk.NewUint(0), + []byte("\"0\""), + []byte{0x30}, + }, + { + sdk.NewUint(100), + []byte("\"100\""), + []byte{0x31, 0x30, 0x30}, + }, + { + sdk.NewUint(51842), + []byte("\"51842\""), + []byte{0x35, 0x31, 0x38, 0x34, 0x32}, + }, + { + sdk.NewUint(19513368), + []byte("\"19513368\""), + []byte{0x31, 0x39, 0x35, 0x31, 0x33, 0x33, 0x36, 0x38}, + }, + { + sdk.NewUint(999999999999), + []byte("\"999999999999\""), + []byte{0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39}, + }, } for tcnum, tc := range cases { bz, err := tc.i.MarshalJSON() - require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.bz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.jsonBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + err = (&i).UnmarshalJSON(bz) - require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) - - str, err := tc.i.MarshalAmino() - require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.str, str, "Marshaled value is different from exported. tc #%d", tcnum) - err = (&i).UnmarshalAmino(str) - require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + + bz, err = tc.i.Marshal() + s.Require().Nil(err, "Error marshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.rawBz, bz, "Marshaled value is different from exported. tc #%d", tcnum) + + err = (&i).Unmarshal(bz) + s.Require().Nil(err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) + s.Require().Equal(tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) } } -func TestEncodingTableUint(t *testing.T) { - var i Uint - - cases := []struct { - i Uint - bz []byte - str string +func (s *intTestSuite) TestIntMod() { + tests := []struct { + name string + x int64 + y int64 + ret int64 + wantPanic bool }{ - {NewUint(0), []byte("\"0\""), "0"}, - {NewUint(100), []byte("\"100\""), "100"}, - {NewUint(51842), []byte("\"51842\""), "51842"}, - {NewUint(19513368), []byte("\"19513368\""), "19513368"}, - {NewUint(999999999999), []byte("\"999999999999\""), "999999999999"}, + {"3 % 10", 3, 10, 3, false}, + {"10 % 3", 10, 3, 1, false}, + {"4 % 2", 4, 2, 0, false}, + {"2 % 0", 2, 0, 0, true}, } - for tcnum, tc := range cases { - bz, err := tc.i.MarshalJSON() - require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.bz, bz, "Marshaled value is different from exported. tc #%d", tcnum) - err = (&i).UnmarshalJSON(bz) - require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) - - str, err := tc.i.MarshalAmino() - require.Nil(t, err, "Error marshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.str, str, "Marshaled value is different from exported. tc #%d", tcnum) - err = (&i).UnmarshalAmino(str) - require.Nil(t, err, "Error unmarshaling Int. tc #%d, err %s", tcnum, err) - require.Equal(t, tc.i, i, "Unmarshaled value is different from exported. tc #%d", tcnum) + for _, tt := range tests { + if tt.wantPanic { + s.Require().Panics(func() { sdk.NewInt(tt.x).Mod(sdk.NewInt(tt.y)) }) + s.Require().Panics(func() { sdk.NewInt(tt.x).ModRaw(tt.y) }) + return + } + s.Require().True(sdk.NewInt(tt.x).Mod(sdk.NewInt(tt.y)).Equal(sdk.NewInt(tt.ret))) + s.Require().True(sdk.NewInt(tt.x).ModRaw(tt.y).Equal(sdk.NewInt(tt.ret))) } } -func TestSerializationOverflow(t *testing.T) { - bx, _ := new(big.Int).SetString("91888242871839275229946405745257275988696311157297823662689937894645226298583", 10) - x := Int{bx} - y := new(Int) - - // require amino deserialization to fail due to overflow - xStr, err := x.MarshalAmino() - require.NoError(t, err) - - err = y.UnmarshalAmino(xStr) - require.Error(t, err) - - // require JSON deserialization to fail due to overflow - bz, err := x.MarshalJSON() - require.NoError(t, err) - - err = y.UnmarshalJSON(bz) - require.Error(t, err) +func (s *intTestSuite) TestIntEq() { + _, resp, _, _, _ := sdk.IntEq(s.T(), sdk.ZeroInt(), sdk.ZeroInt()) + s.Require().True(resp) + _, resp, _, _, _ = sdk.IntEq(s.T(), sdk.OneInt(), sdk.ZeroInt()) + s.Require().False(resp) } diff --git a/types/invariant_test.go b/types/invariant_test.go new file mode 100644 index 000000000000..dc6fbcf6a9c9 --- /dev/null +++ b/types/invariant_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type invariantTestSuite struct { + suite.Suite +} + +func TestInvariantTestSuite(t *testing.T) { + suite.Run(t, new(invariantTestSuite)) +} + +func (s *invariantTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *invariantTestSuite) TestFormatInvariant() { + s.Require().Equal(": invariant\n\n", sdk.FormatInvariant("", "", "")) + s.Require().Equal("module: name invariant\nmsg\n", sdk.FormatInvariant("module", "name", "msg")) +} diff --git a/types/kv/kv.go b/types/kv/kv.go new file mode 100644 index 000000000000..1f3da91cc2c5 --- /dev/null +++ b/types/kv/kv.go @@ -0,0 +1,28 @@ +package kv + +import ( + "bytes" + "sort" +) + +func (kvs Pairs) Len() int { return len(kvs.Pairs) } +func (kvs Pairs) Less(i, j int) bool { + switch bytes.Compare(kvs.Pairs[i].Key, kvs.Pairs[j].Key) { + case -1: + return true + + case 0: + return bytes.Compare(kvs.Pairs[i].Value, kvs.Pairs[j].Value) < 0 + + case 1: + return false + + default: + panic("invalid comparison result") + } +} + +func (kvs Pairs) Swap(i, j int) { kvs.Pairs[i], kvs.Pairs[j] = kvs.Pairs[j], kvs.Pairs[i] } + +// Sort invokes sort.Sort on kvs. +func (kvs Pairs) Sort() { sort.Sort(kvs) } diff --git a/types/kv/kv.pb.go b/types/kv/kv.pb.go new file mode 100644 index 000000000000..eedb948bf5ae --- /dev/null +++ b/types/kv/kv.pb.go @@ -0,0 +1,557 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/kv/v1beta1/kv.proto + +package kv + +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 + +// Pairs defines a repeated slice of Pair objects. +type Pairs struct { + Pairs []Pair `protobuf:"bytes,1,rep,name=pairs,proto3" json:"pairs"` +} + +func (m *Pairs) Reset() { *m = Pairs{} } +func (m *Pairs) String() string { return proto.CompactTextString(m) } +func (*Pairs) ProtoMessage() {} +func (*Pairs) Descriptor() ([]byte, []int) { + return fileDescriptor_a44e87fe7182bb73, []int{0} +} +func (m *Pairs) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pairs) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pairs.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 *Pairs) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pairs.Merge(m, src) +} +func (m *Pairs) XXX_Size() int { + return m.Size() +} +func (m *Pairs) XXX_DiscardUnknown() { + xxx_messageInfo_Pairs.DiscardUnknown(m) +} + +var xxx_messageInfo_Pairs proto.InternalMessageInfo + +func (m *Pairs) GetPairs() []Pair { + if m != nil { + return m.Pairs + } + return nil +} + +// Pair defines a key/value bytes tuple. +type Pair struct { + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *Pair) Reset() { *m = Pair{} } +func (m *Pair) String() string { return proto.CompactTextString(m) } +func (*Pair) ProtoMessage() {} +func (*Pair) Descriptor() ([]byte, []int) { + return fileDescriptor_a44e87fe7182bb73, []int{1} +} +func (m *Pair) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pair.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 *Pair) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pair.Merge(m, src) +} +func (m *Pair) XXX_Size() int { + return m.Size() +} +func (m *Pair) XXX_DiscardUnknown() { + xxx_messageInfo_Pair.DiscardUnknown(m) +} + +var xxx_messageInfo_Pair proto.InternalMessageInfo + +func (m *Pair) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *Pair) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func init() { + proto.RegisterType((*Pairs)(nil), "cosmos.base.kv.v1beta1.Pairs") + proto.RegisterType((*Pair)(nil), "cosmos.base.kv.v1beta1.Pair") +} + +func init() { proto.RegisterFile("cosmos/base/kv/v1beta1/kv.proto", fileDescriptor_a44e87fe7182bb73) } + +var fileDescriptor_a44e87fe7182bb73 = []byte{ + // 221 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4a, 0x2c, 0x4e, 0xd5, 0xcf, 0x2e, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, + 0x49, 0x34, 0xd4, 0xcf, 0x2e, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x83, 0x28, 0xd0, + 0x03, 0x29, 0xd0, 0xcb, 0x2e, 0xd3, 0x83, 0x2a, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, + 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0x95, 0x1c, 0xb9, 0x58, 0x03, 0x12, 0x33, 0x8b, 0x8a, 0x85, 0x2c, + 0xb8, 0x58, 0x0b, 0x40, 0x0c, 0x09, 0x46, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x19, 0x3d, 0xec, 0xc6, + 0xe8, 0x81, 0x54, 0x3b, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0x04, 0xd1, 0xa0, 0xa4, 0xc7, 0xc5, + 0x02, 0x12, 0x14, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, + 0x02, 0x31, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, + 0x8e, 0x93, 0xfd, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, + 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xa9, 0xa6, 0x67, + 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0xbd, 0x09, 0xa1, 0x74, 0x8b, 0x53, + 0xb2, 0xf5, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0xf5, 0xb3, 0xcb, 0x92, 0xd8, 0xc0, 0x4e, 0x37, 0x06, + 0x04, 0x00, 0x00, 0xff, 0xff, 0x15, 0x18, 0x16, 0xcf, 0x0b, 0x01, 0x00, 0x00, +} + +func (m *Pairs) 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 *Pairs) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pairs) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pairs) > 0 { + for iNdEx := len(m.Pairs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pairs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintKv(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Pair) 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 *Pair) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintKv(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKv(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKv(dAtA []byte, offset int, v uint64) int { + offset -= sovKv(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Pairs) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pairs) > 0 { + for _, e := range m.Pairs { + l = e.Size() + n += 1 + l + sovKv(uint64(l)) + } + } + return n +} + +func (m *Pair) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKv(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovKv(uint64(l)) + } + return n +} + +func sovKv(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKv(x uint64) (n int) { + return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Pairs) 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 ErrIntOverflowKv + } + 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: Pairs: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pairs: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pairs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pairs = append(m.Pairs, Pair{}) + if err := m.Pairs[len(m.Pairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKv(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKv + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pair) 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 ErrIntOverflowKv + } + 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: Pair: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pair: 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 ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKv + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKv + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKv + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKv(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKv + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKv(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, ErrIntOverflowKv + } + 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, ErrIntOverflowKv + } + 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, ErrIntOverflowKv + } + 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, ErrInvalidLengthKv + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKv + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKv + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKv = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKv = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/kv/list.go b/types/kv/list.go new file mode 100644 index 000000000000..9e928c84912b --- /dev/null +++ b/types/kv/list.go @@ -0,0 +1,236 @@ +package kv + +// This code was copied from golang.org/pkg/container/list, but specially adapted +// for use with kv.Pair to avoid the type assertion CPU expense of using Value with +// an interface, per https://github.com/cosmos/cosmos-sdk/issues/8810 +// +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Element is an element of a linked list. +type Element struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Element + + // The list to which this element belongs. + list *List + + // The value stored with this element. + Value *Pair +} + +// Next returns the next list element or nil. +func (e *Element) Next() *Element { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *Element) Prev() *Element { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// List represents a doubly linked list. +// The zero value for List is an empty list ready to use. +type List struct { + root Element // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element +} + +// Init initializes or clears list l. +func (l *List) Init() *List { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// NewList returns an initialized list. +func NewList() *List { return new(List).Init() } + +// Len returns the number of elements of list l. +// The complexity is O(1). +func (l *List) Len() int { return l.len } + +// Front returns the first element of list l or nil if the list is empty. +func (l *List) Front() *Element { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *List) Back() *Element { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List value. +func (l *List) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *List) insert(e, at *Element) *Element { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *List) insertValue(v *Pair, at *Element) *Element { + return l.insert(&Element{Value: v}, at) +} + +// remove removes e from its list, decrements l.len, and returns e. +func (l *List) remove(e *Element) *Element { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- + return e +} + +// move moves e to next to at and returns e. +// nolint: unparam +func (l *List) move(e, at *Element) *Element { + if e == at { + return e + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + + return e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +// The element must not be nil. +func (l *List) Remove(e *Element) *Pair { + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) + } + return e.Value +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List) PushFront(v *Pair) *Element { + l.lazyInit() + return l.insertValue(v, &l.root) +} + +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List) PushBack(v *Pair) *Element { + l.lazyInit() + return l.insertValue(v, l.root.prev) +} + +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List) InsertBefore(v *Pair, mark *Element) *Element { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) +} + +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List) InsertAfter(v *Pair, mark *Element) *Element { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List) MoveToFront(e *Element) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List) MoveToBack(e *Element) { + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// MoveBefore moves element e to its new position before mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List) MoveBefore(e, mark *Element) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List) MoveAfter(e, mark *Element) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark) +} + +// PushBackList inserts a copy of another list at the back of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List) PushBackList(other *List) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of another list at the front of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List) PushFrontList(other *List) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} diff --git a/types/module/configurator.go b/types/module/configurator.go new file mode 100644 index 000000000000..d561dd9eef0d --- /dev/null +++ b/types/module/configurator.go @@ -0,0 +1,40 @@ +package module + +import "github.com/gogo/protobuf/grpc" + +// Configurator provides the hooks to allow modules to configure and register +// their services in the RegisterServices method. It is designed to eventually +// support module object capabilities isolation as described in +// https://github.com/cosmos/cosmos-sdk/issues/7093 +type Configurator interface { + // MsgServer returns a grpc.Server instance which allows registering services + // that will handle TxBody.messages in transactions. These Msg's WILL NOT + // be exposed as gRPC services. + MsgServer() grpc.Server + + // 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 +} + +type configurator struct { + msgServer grpc.Server + queryServer grpc.Server +} + +// NewConfigurator returns a new Configurator instance +func NewConfigurator(msgServer grpc.Server, queryServer grpc.Server) Configurator { + return configurator{msgServer: msgServer, queryServer: queryServer} +} + +var _ Configurator = configurator{} + +// MsgServer implements the Configurator.MsgServer method +func (c configurator) MsgServer() grpc.Server { + return c.msgServer +} + +// QueryServer implements the Configurator.QueryServer method +func (c configurator) QueryServer() grpc.Server { + return c.queryServer +} diff --git a/types/module/module.go b/types/module/module.go index 924c99834250..2379c93d5ebd 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -32,12 +32,13 @@ import ( "encoding/json" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -46,16 +47,17 @@ import ( // AppModuleBasic is the standard form for basic non-dependant elements of an application module. type AppModuleBasic interface { Name() string - RegisterCodec(*codec.Codec) + RegisterLegacyAminoCodec(*codec.LegacyAmino) + RegisterInterfaces(codectypes.InterfaceRegistry) - // genesis - DefaultGenesis() json.RawMessage - ValidateGenesis(json.RawMessage) error + DefaultGenesis(codec.JSONMarshaler) json.RawMessage + ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage) error // client functionality - RegisterRESTRoutes(context.CLIContext, *mux.Router) - GetTxCmd(*codec.Codec) *cobra.Command - GetQueryCmd(*codec.Codec) *cobra.Command + RegisterRESTRoutes(client.Context, *mux.Router) + RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux) + GetTxCmd() *cobra.Command + GetQueryCmd() *cobra.Command } // BasicManager is a collection of AppModuleBasic @@ -70,52 +72,74 @@ func NewBasicManager(modules ...AppModuleBasic) BasicManager { return moduleMap } -// RegisterCodec registers all module codecs -func (bm BasicManager) RegisterCodec(cdc *codec.Codec) { +// RegisterLegacyAminoCodec registers all module codecs +func (bm BasicManager) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { for _, b := range bm { - b.RegisterCodec(cdc) + b.RegisterLegacyAminoCodec(cdc) + } +} + +// RegisterInterfaces registers all module interface types +func (bm BasicManager) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + for _, m := range bm { + m.RegisterInterfaces(registry) } } // DefaultGenesis provides default genesis information for all modules -func (bm BasicManager) DefaultGenesis() map[string]json.RawMessage { +func (bm BasicManager) DefaultGenesis(cdc codec.JSONMarshaler) map[string]json.RawMessage { genesis := make(map[string]json.RawMessage) for _, b := range bm { - genesis[b.Name()] = b.DefaultGenesis() + genesis[b.Name()] = b.DefaultGenesis(cdc) } + return genesis } // ValidateGenesis performs genesis state validation for all modules -func (bm BasicManager) ValidateGenesis(genesis map[string]json.RawMessage) error { +func (bm BasicManager) ValidateGenesis(cdc codec.JSONMarshaler, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage) error { for _, b := range bm { - if err := b.ValidateGenesis(genesis[b.Name()]); err != nil { + if err := b.ValidateGenesis(cdc, txEncCfg, genesis[b.Name()]); err != nil { return err } } + return nil } // RegisterRESTRoutes registers all module rest routes -func (bm BasicManager) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { +func (bm BasicManager) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { for _, b := range bm { - b.RegisterRESTRoutes(ctx, rtr) + b.RegisterRESTRoutes(clientCtx, rtr) } } -// AddTxCommands adds all tx commands to the rootTxCmd -func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command, cdc *codec.Codec) { +// RegisterGRPCGatewayRoutes registers all module rest routes +func (bm BasicManager) RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux) { for _, b := range bm { - if cmd := b.GetTxCmd(cdc); cmd != nil { + b.RegisterGRPCGatewayRoutes(clientCtx, rtr) + } +} + +// AddTxCommands adds all tx commands to the rootTxCmd. +// +// TODO: Remove clientCtx argument. +// REF: https://github.com/cosmos/cosmos-sdk/issues/6571 +func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command) { + for _, b := range bm { + if cmd := b.GetTxCmd(); cmd != nil { rootTxCmd.AddCommand(cmd) } } } -// AddQueryCommands adds all query commands to the rootQueryCmd -func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec.Codec) { +// AddQueryCommands adds all query commands to the rootQueryCmd. +// +// TODO: Remove clientCtx argument. +// REF: https://github.com/cosmos/cosmos-sdk/issues/6571 +func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command) { for _, b := range bm { - if cmd := b.GetQueryCmd(cdc); cmd != nil { + if cmd := b.GetQueryCmd(); cmd != nil { rootQueryCmd.AddCommand(cmd) } } @@ -126,8 +150,9 @@ func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command, cdc *codec. // AppModuleGenesis is the standard form for an application module genesis functions type AppModuleGenesis interface { AppModuleBasic - InitGenesis(sdk.Context, json.RawMessage) []abci.ValidatorUpdate - ExportGenesis(sdk.Context) json.RawMessage + + InitGenesis(sdk.Context, codec.JSONMarshaler, json.RawMessage) []abci.ValidatorUpdate + ExportGenesis(sdk.Context, codec.JSONMarshaler) json.RawMessage } // AppModule is the standard form for an application module @@ -137,11 +162,17 @@ type AppModule interface { // registers RegisterInvariants(sdk.InvariantRegistry) - // routes - Route() string - NewHandler() sdk.Handler + // Deprecated: use RegisterServices + Route() sdk.Route + + // Deprecated: use RegisterServices QuerierRoute() string - NewQuerierHandler() sdk.Querier + + // Deprecated: use RegisterServices + LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier + + // RegisterServices allows a module to register services + RegisterServices(Configurator) // ABCI BeginBlock(sdk.Context, abci.RequestBeginBlock) @@ -166,16 +197,16 @@ func NewGenesisOnlyAppModule(amg AppModuleGenesis) AppModule { func (GenesisOnlyAppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route empty module message route -func (GenesisOnlyAppModule) Route() string { return "" } - -// NewHandler returns an empty module handler -func (GenesisOnlyAppModule) NewHandler() sdk.Handler { return nil } +func (GenesisOnlyAppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns an empty module querier route func (GenesisOnlyAppModule) QuerierRoute() string { return "" } -// NewQuerierHandler returns an empty module querier -func (gam GenesisOnlyAppModule) NewQuerierHandler() sdk.Querier { return nil } +// LegacyQuerierHandler returns an empty module querier +func (gam GenesisOnlyAppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { return nil } + +// RegisterServices registers all services. +func (gam GenesisOnlyAppModule) RegisterServices(Configurator) {} // BeginBlock returns an empty module begin-block func (gam GenesisOnlyAppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {} @@ -244,25 +275,33 @@ func (m *Manager) RegisterInvariants(ir sdk.InvariantRegistry) { } // RegisterRoutes registers all module routes and module querier routes -func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter) { +func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino) { for _, module := range m.Modules { - if module.Route() != "" { - router.AddRoute(module.Route(), module.NewHandler()) + if r := module.Route(); !r.Empty() { + router.AddRoute(r) } - if module.QuerierRoute() != "" { - queryRouter.AddRoute(module.QuerierRoute(), module.NewQuerierHandler()) + if r := module.QuerierRoute(); r != "" { + queryRouter.AddRoute(r, module.LegacyQuerierHandler(legacyQuerierCdc)) } } } +// RegisterServices registers all module services +func (m *Manager) RegisterServices(cfg Configurator) { + for _, module := range m.Modules { + module.RegisterServices(cfg) + } +} + // InitGenesis performs init genesis functionality for modules -func (m *Manager) InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage) abci.ResponseInitChain { +func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage) abci.ResponseInitChain { var validatorUpdates []abci.ValidatorUpdate for _, moduleName := range m.OrderInitGenesis { if genesisData[moduleName] == nil { continue } - moduleValUpdates := m.Modules[moduleName].InitGenesis(ctx, genesisData[moduleName]) + + moduleValUpdates := m.Modules[moduleName].InitGenesis(ctx, cdc, genesisData[moduleName]) // use these validator updates if provided, the module manager assumes // only one module will update the validator set @@ -273,17 +312,19 @@ func (m *Manager) InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMe validatorUpdates = moduleValUpdates } } + return abci.ResponseInitChain{ Validators: validatorUpdates, } } // ExportGenesis performs export genesis functionality for modules -func (m *Manager) ExportGenesis(ctx sdk.Context) map[string]json.RawMessage { +func (m *Manager) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) map[string]json.RawMessage { genesisData := make(map[string]json.RawMessage) for _, moduleName := range m.OrderExportGenesis { - genesisData[moduleName] = m.Modules[moduleName].ExportGenesis(ctx) + genesisData[moduleName] = m.Modules[moduleName].ExportGenesis(ctx, cdc) } + return genesisData } diff --git a/types/module/module_test.go b/types/module/module_test.go index 4f24de5a185a..630c57619245 100644 --- a/types/module/module_test.go +++ b/types/module/module_test.go @@ -1,16 +1,284 @@ -package module +package module_test import ( + "encoding/json" + "errors" "testing" - "github.com/stretchr/testify/assert" + "github.com/cosmos/cosmos-sdk/codec/types" + + "github.com/golang/mock/gomock" + "github.com/gorilla/mux" + "github.com/spf13/cobra" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/tests/mocks" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" ) -func TestSetOrderBeginBlockers(t *testing.T) { - mm := NewManager() - mm.SetOrderBeginBlockers("a", "b", "c") - obb := mm.OrderBeginBlockers - require.Equal(t, 3, len(obb)) - assert.Equal(t, []string{"a", "b", "c"}, obb) +var errFoo = errors.New("dummy") + +func TestBasicManager(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + legacyAmino := codec.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + clientCtx := client.Context{} + clientCtx = clientCtx.WithLegacyAmino(legacyAmino) + wantDefaultGenesis := map[string]json.RawMessage{"mockAppModuleBasic1": json.RawMessage(``)} + + mockAppModuleBasic1 := mocks.NewMockAppModuleBasic(mockCtrl) + + mockAppModuleBasic1.EXPECT().Name().AnyTimes().Return("mockAppModuleBasic1") + mockAppModuleBasic1.EXPECT().DefaultGenesis(gomock.Eq(cdc)).Times(1).Return(json.RawMessage(``)) + mockAppModuleBasic1.EXPECT().ValidateGenesis(gomock.Eq(cdc), gomock.Eq(nil), gomock.Eq(wantDefaultGenesis["mockAppModuleBasic1"])).Times(1).Return(errFoo) + mockAppModuleBasic1.EXPECT().RegisterRESTRoutes(gomock.Eq(client.Context{}), gomock.Eq(&mux.Router{})).Times(1) + mockAppModuleBasic1.EXPECT().RegisterLegacyAminoCodec(gomock.Eq(legacyAmino)).Times(1) + mockAppModuleBasic1.EXPECT().RegisterInterfaces(gomock.Eq(interfaceRegistry)).Times(1) + mockAppModuleBasic1.EXPECT().GetTxCmd().Times(1).Return(nil) + mockAppModuleBasic1.EXPECT().GetQueryCmd().Times(1).Return(nil) + + mm := module.NewBasicManager(mockAppModuleBasic1) + require.Equal(t, mm["mockAppModuleBasic1"], mockAppModuleBasic1) + + mm.RegisterLegacyAminoCodec(legacyAmino) + mm.RegisterInterfaces(interfaceRegistry) + + require.Equal(t, wantDefaultGenesis, mm.DefaultGenesis(cdc)) + + var data map[string]string + require.Equal(t, map[string]string(nil), data) + + require.True(t, errors.Is(errFoo, mm.ValidateGenesis(cdc, nil, wantDefaultGenesis))) + + mm.RegisterRESTRoutes(client.Context{}, &mux.Router{}) + + mockCmd := &cobra.Command{Use: "root"} + mm.AddTxCommands(mockCmd) + + mm.AddQueryCommands(mockCmd) + + // validate genesis returns nil + require.Nil(t, module.NewBasicManager().ValidateGenesis(cdc, nil, wantDefaultGenesis)) +} + +func TestGenesisOnlyAppModule(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockModule := mocks.NewMockAppModuleGenesis(mockCtrl) + mockInvariantRegistry := mocks.NewMockInvariantRegistry(mockCtrl) + goam := module.NewGenesisOnlyAppModule(mockModule) + + require.True(t, goam.Route().Empty()) + require.Empty(t, goam.QuerierRoute()) + require.Nil(t, goam.LegacyQuerierHandler(nil)) + + // no-op + goam.RegisterInvariants(mockInvariantRegistry) + goam.BeginBlock(sdk.Context{}, abci.RequestBeginBlock{}) + require.Equal(t, []abci.ValidatorUpdate{}, goam.EndBlock(sdk.Context{}, abci.RequestEndBlock{})) +} + +func TestManagerOrderSetters(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + require.Equal(t, []string{"module1", "module2"}, mm.OrderInitGenesis) + mm.SetOrderInitGenesis("module2", "module1") + require.Equal(t, []string{"module2", "module1"}, mm.OrderInitGenesis) + + require.Equal(t, []string{"module1", "module2"}, mm.OrderExportGenesis) + mm.SetOrderExportGenesis("module2", "module1") + require.Equal(t, []string{"module2", "module1"}, mm.OrderExportGenesis) + + require.Equal(t, []string{"module1", "module2"}, mm.OrderBeginBlockers) + mm.SetOrderBeginBlockers("module2", "module1") + require.Equal(t, []string{"module2", "module1"}, mm.OrderBeginBlockers) + + require.Equal(t, []string{"module1", "module2"}, mm.OrderEndBlockers) + mm.SetOrderEndBlockers("module2", "module1") + require.Equal(t, []string{"module2", "module1"}, mm.OrderEndBlockers) +} + +func TestManager_RegisterInvariants(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + // test RegisterInvariants + mockInvariantRegistry := mocks.NewMockInvariantRegistry(mockCtrl) + mockAppModule1.EXPECT().RegisterInvariants(gomock.Eq(mockInvariantRegistry)).Times(1) + mockAppModule2.EXPECT().RegisterInvariants(gomock.Eq(mockInvariantRegistry)).Times(1) + mm.RegisterInvariants(mockInvariantRegistry) +} + +func TestManager_RegisterRoutes(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + router := mocks.NewMockRouter(mockCtrl) + noopHandler := sdk.Handler(func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { return nil, nil }) + route1 := sdk.NewRoute("route1", noopHandler) + route2 := sdk.NewRoute("", noopHandler) + mockAppModule1.EXPECT().Route().Times(1).Return(route1) + mockAppModule2.EXPECT().Route().Times(1).Return(route2) + router.EXPECT().AddRoute(gomock.Any()).Times(1) // Use of Any due to limitations to compare Functions as the sdk.Handler + + queryRouter := mocks.NewMockQueryRouter(mockCtrl) + mockAppModule1.EXPECT().QuerierRoute().Times(1).Return("querierRoute1") + mockAppModule2.EXPECT().QuerierRoute().Times(1).Return("") + handler3 := sdk.Querier(nil) + amino := codec.NewLegacyAmino() + mockAppModule1.EXPECT().LegacyQuerierHandler(amino).Times(1).Return(handler3) + queryRouter.EXPECT().AddRoute(gomock.Eq("querierRoute1"), gomock.Eq(handler3)).Times(1) + + mm.RegisterRoutes(router, queryRouter, amino) +} + +func TestManager_RegisterQueryServices(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + msgRouter := mocks.NewMockServer(mockCtrl) + queryRouter := mocks.NewMockServer(mockCtrl) + cfg := module.NewConfigurator(msgRouter, queryRouter) + mockAppModule1.EXPECT().RegisterServices(cfg).Times(1) + mockAppModule2.EXPECT().RegisterServices(cfg).Times(1) + + mm.RegisterServices(cfg) +} + +func TestManager_InitGenesis(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + ctx := sdk.Context{} + interfaceRegistry := types.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + genesisData := map[string]json.RawMessage{"module1": json.RawMessage(`{"key": "value"}`)} + + mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return(nil) + require.Equal(t, abci.ResponseInitChain{Validators: []abci.ValidatorUpdate(nil)}, mm.InitGenesis(ctx, cdc, genesisData)) + + // test panic + genesisData = map[string]json.RawMessage{ + "module1": json.RawMessage(`{"key": "value"}`), + "module2": json.RawMessage(`{"key": "value"}`)} + mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}}) + mockAppModule2.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module2"])).Times(1).Return([]abci.ValidatorUpdate{{}}) + require.Panics(t, func() { mm.InitGenesis(ctx, cdc, genesisData) }) +} + +func TestManager_ExportGenesis(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + ctx := sdk.Context{} + interfaceRegistry := types.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + mockAppModule1.EXPECT().ExportGenesis(gomock.Eq(ctx), gomock.Eq(cdc)).Times(1).Return(json.RawMessage(`{"key1": "value1"}`)) + mockAppModule2.EXPECT().ExportGenesis(gomock.Eq(ctx), gomock.Eq(cdc)).Times(1).Return(json.RawMessage(`{"key2": "value2"}`)) + + want := map[string]json.RawMessage{ + "module1": json.RawMessage(`{"key1": "value1"}`), + "module2": json.RawMessage(`{"key2": "value2"}`)} + require.Equal(t, want, mm.ExportGenesis(ctx, cdc)) +} + +func TestManager_BeginBlock(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + req := abci.RequestBeginBlock{Hash: []byte("test")} + + mockAppModule1.EXPECT().BeginBlock(gomock.Any(), gomock.Eq(req)).Times(1) + mockAppModule2.EXPECT().BeginBlock(gomock.Any(), gomock.Eq(req)).Times(1) + mm.BeginBlock(sdk.Context{}, req) +} + +func TestManager_EndBlock(t *testing.T) { + mockCtrl := gomock.NewController(t) + t.Cleanup(mockCtrl.Finish) + + mockAppModule1 := mocks.NewMockAppModule(mockCtrl) + mockAppModule2 := mocks.NewMockAppModule(mockCtrl) + mockAppModule1.EXPECT().Name().Times(2).Return("module1") + mockAppModule2.EXPECT().Name().Times(2).Return("module2") + mm := module.NewManager(mockAppModule1, mockAppModule2) + require.NotNil(t, mm) + require.Equal(t, 2, len(mm.Modules)) + + req := abci.RequestEndBlock{Height: 10} + + mockAppModule1.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}}) + mockAppModule2.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1) + ret := mm.EndBlock(sdk.Context{}, req) + require.Equal(t, []abci.ValidatorUpdate{{}}, ret.ValidatorUpdates) + + // test panic + mockAppModule1.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}}) + mockAppModule2.EXPECT().EndBlock(gomock.Any(), gomock.Eq(req)).Times(1).Return([]abci.ValidatorUpdate{{}}) + require.Panics(t, func() { mm.EndBlock(sdk.Context{}, req) }) } diff --git a/types/module/simulation.go b/types/module/simulation.go index fab432c8a57e..e01277f457a4 100644 --- a/types/module/simulation.go +++ b/types/module/simulation.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" ) // AppModuleSimulation defines the standard functions that every module should expose @@ -50,7 +50,7 @@ func NewSimulationManager(modules ...AppModuleSimulation) *SimulationManager { // GetProposalContents returns each module's proposal content generator function // with their default operation weight and key. func (sm *SimulationManager) GetProposalContents(simState SimulationState) []simulation.WeightedProposalContent { - var wContents []simulation.WeightedProposalContent + wContents := make([]simulation.WeightedProposalContent, 0, len(sm.Modules)) for _, module := range sm.Modules { wContents = append(wContents, module.ProposalContents(simState)...) } @@ -87,7 +87,7 @@ func (sm *SimulationManager) GenerateParamChanges(seed int64) (paramChanges []si // WeightedOperations returns all the modules' weighted operations of an application func (sm *SimulationManager) WeightedOperations(simState SimulationState) []simulation.WeightedOperation { - var wOps []simulation.WeightedOperation + wOps := make([]simulation.WeightedOperation, 0, len(sm.Modules)) for _, module := range sm.Modules { wOps = append(wOps, module.WeightedOperations(simState)...) } @@ -99,7 +99,7 @@ func (sm *SimulationManager) WeightedOperations(simState SimulationState) []simu // GenesisState generator function type SimulationState struct { AppParams simulation.AppParams - Cdc *codec.Codec // application codec + Cdc codec.JSONMarshaler // application codec Rand *rand.Rand // random number GenState map[string]json.RawMessage // genesis state Accounts []simulation.Account // simulation accounts diff --git a/types/msgservice/msg_service.go b/types/msgservice/msg_service.go new file mode 100644 index 000000000000..277ee3d157c9 --- /dev/null +++ b/types/msgservice/msg_service.go @@ -0,0 +1,44 @@ +package msgservice + +import ( + "context" + "fmt" + + "github.com/gogo/protobuf/proto" + "google.golang.org/grpc" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RegisterMsgServiceDesc registers all type_urls from Msg services described +// in `sd` into the registry. +func RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc) { + // Adds a top-level type_url based on the Msg service name. + for _, method := range sd.Methods { + fqMethod := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) + methodHandler := method.Handler + + // NOTE: This is how we pull the concrete request type for each handler for registering in the InterfaceRegistry. + // This approach is maybe a bit hacky, but less hacky than reflecting on the handler object itself. + // We use a no-op interceptor to avoid actually calling into the handler itself. + _, _ = methodHandler(nil, context.Background(), func(i interface{}) error { + msg, ok := i.(proto.Message) + if !ok { + // We panic here because there is no other alternative and the app cannot be initialized correctly + // this should only happen if there is a problem with code generation in which case the app won't + // work correctly anyway. + panic(fmt.Errorf("can't register request type %T for service method %s", i, fqMethod)) + } + + registry.RegisterCustomTypeURL((*sdk.MsgRequest)(nil), fqMethod, msg) + return nil + }, noopInterceptor) + + } +} + +// gRPC NOOP interceptor +func noopInterceptor(_ context.Context, _ interface{}, _ *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { + return nil, nil +} diff --git a/types/proto.go b/types/proto.go new file mode 100644 index 000000000000..5f2df428d27d --- /dev/null +++ b/types/proto.go @@ -0,0 +1,15 @@ +package types + +// CustomProtobufType defines the interface custom gogo proto types must implement +// in order to be used as a "customtype" extension. +// +// ref: https://github.com/gogo/protobuf/blob/master/custom_types.md +type CustomProtobufType interface { + Marshal() ([]byte, error) + MarshalTo(data []byte) (n int, err error) + Unmarshal(data []byte) error + Size() int + + MarshalJSON() ([]byte, error) + UnmarshalJSON(data []byte) error +} diff --git a/types/query/filtered_pagination.go b/types/query/filtered_pagination.go new file mode 100644 index 000000000000..0ab29a4acded --- /dev/null +++ b/types/query/filtered_pagination.go @@ -0,0 +1,114 @@ +package query + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +// FilteredPaginate does pagination of all the results in the PrefixStore based on the +// provided PageRequest. onResult should be used to do actual unmarshaling and filter the results. +// If key is provided, the pagination uses the optimized querying. +// If offset is used, the pagination uses lazy filtering i.e., searches through all the records. +// The accumulate parameter represents if the response is valid based on the offset given. +// It will be false for the results (filtered) < offset and true for `offset > accumulate <= end`. +// When accumulate is set to true the current result should be appended to the result set returned +// to the client. +func FilteredPaginate( + prefixStore types.KVStore, + pageRequest *PageRequest, + onResult func(key []byte, value []byte, accumulate bool) (bool, error), +) (*PageResponse, error) { + + // if the PageRequest is nil, use default PageRequest + if pageRequest == nil { + pageRequest = &PageRequest{} + } + + offset := pageRequest.Offset + key := pageRequest.Key + limit := pageRequest.Limit + countTotal := pageRequest.CountTotal + + if offset > 0 && key != nil { + return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") + } + + if limit == 0 { + limit = DefaultLimit + + // count total results when the limit is zero/not supplied + countTotal = true + } + + if len(key) != 0 { + iterator := prefixStore.Iterator(key, nil) + defer iterator.Close() + + var numHits uint64 + var nextKey []byte + + for ; iterator.Valid(); iterator.Next() { + if numHits == limit { + nextKey = iterator.Key() + break + } + + if iterator.Error() != nil { + return nil, iterator.Error() + } + + hit, err := onResult(iterator.Key(), iterator.Value(), true) + if err != nil { + return nil, err + } + + if hit { + numHits++ + } + } + + return &PageResponse{ + NextKey: nextKey, + }, nil + } + + iterator := prefixStore.Iterator(nil, nil) + defer iterator.Close() + + end := offset + limit + + var numHits uint64 + var nextKey []byte + + for ; iterator.Valid(); iterator.Next() { + if iterator.Error() != nil { + return nil, iterator.Error() + } + + accumulate := numHits >= offset && numHits < end + hit, err := onResult(iterator.Key(), iterator.Value(), accumulate) + if err != nil { + return nil, err + } + + if hit { + numHits++ + } + + if numHits == end+1 { + nextKey = iterator.Key() + + if !countTotal { + break + } + } + } + + res := &PageResponse{NextKey: nextKey} + if countTotal { + res.Total = numHits + } + + return res, nil +} diff --git a/types/query/filtered_pagination_test.go b/types/query/filtered_pagination_test.go new file mode 100644 index 000000000000..dbd2057da473 --- /dev/null +++ b/types/query/filtered_pagination_test.go @@ -0,0 +1,169 @@ +package query_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +var addr1 = sdk.AccAddress([]byte("addr1")) + +func (s *paginationTestSuite) TestFilteredPaginations() { + app, ctx, appCodec := setupTest() + + var balances sdk.Coins + for i := 0; i < numBalances; i++ { + denom := fmt.Sprintf("foo%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 100)) + } + + for i := 0; i < 4; i++ { + denom := fmt.Sprintf("test%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 250)) + } + + 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)) + + // verify pagination with limit > total values + pageReq := &query.PageRequest{Key: nil, Limit: 5, CountTotal: true} + balances, res, err := execFilterPaginate(store, pageReq, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(4, len(balances)) + + s.T().Log("verify empty request") + balances, res, err = execFilterPaginate(store, nil, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(4, len(balances)) + s.Require().Equal(uint64(4), res.Total) + s.Require().Nil(res.NextKey) + + s.T().Log("verify nextKey is returned if there are more results") + pageReq = &query.PageRequest{Key: nil, Limit: 2, CountTotal: true} + balances, res, err = execFilterPaginate(store, pageReq, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(2, len(balances)) + s.Require().NotNil(res.NextKey) + s.Require().Equal(string(res.NextKey), fmt.Sprintf("test2denom")) + s.Require().Equal(uint64(4), res.Total) + + s.T().Log("verify both key and offset can't be given") + pageReq = &query.PageRequest{Key: res.NextKey, Limit: 1, Offset: 2, CountTotal: true} + _, _, err = execFilterPaginate(store, pageReq, appCodec) + s.Require().Error(err) + + s.T().Log("use nextKey for query") + pageReq = &query.PageRequest{Key: res.NextKey, Limit: 2, CountTotal: true} + balances, res, err = execFilterPaginate(store, pageReq, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(2, len(balances)) + s.Require().Nil(res.NextKey) + + s.T().Log("verify default limit") + pageReq = &query.PageRequest{Key: nil, Limit: 0} + balances, res, err = execFilterPaginate(store, pageReq, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Equal(4, len(balances)) + s.Require().Equal(uint64(4), res.Total) + + s.T().Log("verify with offset") + pageReq = &query.PageRequest{Offset: 2, Limit: 2} + balances, res, err = execFilterPaginate(store, pageReq, appCodec) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().LessOrEqual(len(balances), 2) +} + +func ExampleFilteredPaginate() { + app, ctx, appCodec := setupTest() + + var balances sdk.Coins + for i := 0; i < numBalances; i++ { + denom := fmt.Sprintf("foo%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 100)) + } + + for i := 0; i < 5; i++ { + denom := fmt.Sprintf("test%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 250)) + } + 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 { // should return no error + fmt.Println(err) + } + + pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true} + store := ctx.KVStore(app.GetKey(authtypes.StoreKey)) + balancesStore := prefix.NewStore(store, types.BalancesPrefix) + accountStore := prefix.NewStore(balancesStore, addr1.Bytes()) + + var balResult sdk.Coins + pageRes, err := query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { + var bal sdk.Coin + err := appCodec.UnmarshalBinaryBare(value, &bal) + if err != nil { + return false, err + } + + // filter balances with amount greater than 100 + if bal.Amount.Int64() > int64(100) { + if accumulate { + balResult = append(balResult, bal) + } + + return true, nil + } + + return false, nil + }) + + if err != nil { // should return no error + fmt.Println(err) + } + fmt.Println(&types.QueryAllBalancesResponse{Balances: balResult, Pagination: pageRes}) + // Output: + // balances: pagination: +} + +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()) + + var balResult sdk.Coins + res, err = query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { + var bal sdk.Coin + err := appCodec.UnmarshalBinaryBare(value, &bal) + if err != nil { + return false, err + } + + // filter balances with amount greater than 100 + if bal.Amount.Int64() > int64(100) { + if accumulate { + balResult = append(balResult, bal) + } + + return true, nil + } + + return false, nil + }) + + return balResult, res, err +} diff --git a/types/query/pagination.go b/types/query/pagination.go new file mode 100644 index 000000000000..9a0999fe61d6 --- /dev/null +++ b/types/query/pagination.go @@ -0,0 +1,134 @@ +package query + +import ( + "fmt" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +// DefaultLimit is the default `limit` for queries +// if the `limit` is not supplied, paginate will use `DefaultLimit` +const DefaultLimit = 100 + +// ParsePagination validate PageRequest and returns page number & limit. +func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { + offset := 0 + limit = DefaultLimit + + if pageReq != nil { + offset = int(pageReq.Offset) + limit = int(pageReq.Limit) + } + if offset < 0 { + return 1, 0, status.Error(codes.InvalidArgument, "offset must greater than 0") + } + + if limit < 0 { + return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0") + } else if limit == 0 { + limit = DefaultLimit + } + + page = offset/limit + 1 + + return page, limit, nil +} + +// Paginate does pagination of all the results in the PrefixStore based on the +// provided PageRequest. onResult should be used to do actual unmarshaling. +func Paginate( + prefixStore types.KVStore, + pageRequest *PageRequest, + onResult func(key []byte, value []byte) error, +) (*PageResponse, error) { + + // if the PageRequest is nil, use default PageRequest + if pageRequest == nil { + pageRequest = &PageRequest{} + } + + offset := pageRequest.Offset + key := pageRequest.Key + limit := pageRequest.Limit + countTotal := pageRequest.CountTotal + + if offset > 0 && key != nil { + return nil, fmt.Errorf("invalid request, either offset or key is expected, got both") + } + + if limit == 0 { + limit = DefaultLimit + + // count total results when the limit is zero/not supplied + countTotal = true + } + + if len(key) != 0 { + iterator := prefixStore.Iterator(key, nil) + defer iterator.Close() + + var count uint64 + var nextKey []byte + + for ; iterator.Valid(); iterator.Next() { + if count == limit { + nextKey = iterator.Key() + break + } + if iterator.Error() != nil { + return nil, iterator.Error() + } + err := onResult(iterator.Key(), iterator.Value()) + if err != nil { + return nil, err + } + + count++ + } + + return &PageResponse{ + NextKey: nextKey, + }, nil + } + + iterator := prefixStore.Iterator(nil, nil) + defer iterator.Close() + + end := offset + limit + + var count uint64 + var nextKey []byte + + for ; iterator.Valid(); iterator.Next() { + count++ + + if count <= offset { + continue + } + if count <= end { + err := onResult(iterator.Key(), iterator.Value()) + if err != nil { + return nil, err + } + } else if count == end+1 { + nextKey = iterator.Key() + + if !countTotal { + break + } + } + if iterator.Error() != nil { + return nil, iterator.Error() + } + } + + res := &PageResponse{NextKey: nextKey} + if countTotal { + res.Total = count + } + + return res, nil +} diff --git a/types/query/pagination.pb.go b/types/query/pagination.pb.go new file mode 100644 index 000000000000..b27db91746a5 --- /dev/null +++ b/types/query/pagination.pb.go @@ -0,0 +1,673 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/base/query/v1beta1/pagination.proto + +package query + +import ( + fmt "fmt" + 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 + +// PageRequest is to be embedded in gRPC request messages for efficient +// pagination. Ex: +// +// message SomeRequest { +// Foo some_parameter = 1; +// PageRequest pagination = 2; +// } +type PageRequest struct { + // 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. + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // 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. + Offset uint64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + // 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. + Limit uint64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + // 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. + CountTotal bool `protobuf:"varint,4,opt,name=count_total,json=countTotal,proto3" json:"count_total,omitempty"` +} + +func (m *PageRequest) Reset() { *m = PageRequest{} } +func (m *PageRequest) String() string { return proto.CompactTextString(m) } +func (*PageRequest) ProtoMessage() {} +func (*PageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_53d6d609fe6828af, []int{0} +} +func (m *PageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PageRequest.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 *PageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PageRequest.Merge(m, src) +} +func (m *PageRequest) XXX_Size() int { + return m.Size() +} +func (m *PageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PageRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PageRequest proto.InternalMessageInfo + +func (m *PageRequest) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func (m *PageRequest) GetOffset() uint64 { + if m != nil { + return m.Offset + } + return 0 +} + +func (m *PageRequest) GetLimit() uint64 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *PageRequest) GetCountTotal() bool { + if m != nil { + return m.CountTotal + } + return false +} + +// 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; +// } +type PageResponse struct { + // next_key is the key to be passed to PageRequest.key to + // query the next page most efficiently + NextKey []byte `protobuf:"bytes,1,opt,name=next_key,json=nextKey,proto3" json:"next_key,omitempty"` + // total is total number of results available if PageRequest.count_total + // was set, its value is undefined otherwise + Total uint64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` +} + +func (m *PageResponse) Reset() { *m = PageResponse{} } +func (m *PageResponse) String() string { return proto.CompactTextString(m) } +func (*PageResponse) ProtoMessage() {} +func (*PageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_53d6d609fe6828af, []int{1} +} +func (m *PageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PageResponse.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 *PageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PageResponse.Merge(m, src) +} +func (m *PageResponse) XXX_Size() int { + return m.Size() +} +func (m *PageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PageResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PageResponse proto.InternalMessageInfo + +func (m *PageResponse) GetNextKey() []byte { + if m != nil { + return m.NextKey + } + return nil +} + +func (m *PageResponse) GetTotal() uint64 { + if m != nil { + return m.Total + } + return 0 +} + +func init() { + proto.RegisterType((*PageRequest)(nil), "cosmos.base.query.v1beta1.PageRequest") + proto.RegisterType((*PageResponse)(nil), "cosmos.base.query.v1beta1.PageResponse") +} + +func init() { + proto.RegisterFile("cosmos/base/query/v1beta1/pagination.proto", fileDescriptor_53d6d609fe6828af) +} + +var fileDescriptor_53d6d609fe6828af = []byte{ + // 266 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0xc1, 0x4a, 0xc3, 0x40, + 0x14, 0x45, 0x33, 0xb6, 0xd6, 0x32, 0xed, 0x42, 0x06, 0x91, 0x74, 0x33, 0x86, 0xae, 0x82, 0x60, + 0x86, 0xe2, 0x07, 0x08, 0xdd, 0xba, 0x91, 0xe0, 0xca, 0x4d, 0x99, 0xc4, 0xd7, 0x18, 0xda, 0xcc, + 0x4b, 0x3b, 0x2f, 0x62, 0xfe, 0xc2, 0xcf, 0x72, 0xd9, 0xa5, 0x4b, 0x49, 0x7e, 0x44, 0x92, 0x09, + 0x74, 0x35, 0x73, 0x2f, 0x87, 0x77, 0xe0, 0xf2, 0xfb, 0x14, 0x6d, 0x81, 0x56, 0x25, 0xda, 0x82, + 0x3a, 0x54, 0x70, 0xac, 0xd5, 0xe7, 0x2a, 0x01, 0xd2, 0x2b, 0x55, 0xea, 0x2c, 0x37, 0x9a, 0x72, + 0x34, 0x51, 0x79, 0x44, 0x42, 0xb1, 0x70, 0x6c, 0xd4, 0xb1, 0x51, 0xcf, 0x46, 0x03, 0xbb, 0x34, + 0x7c, 0xf6, 0xa2, 0x33, 0x88, 0xe1, 0x50, 0x81, 0x25, 0x71, 0xcd, 0x47, 0x3b, 0xa8, 0x7d, 0x16, + 0xb0, 0x70, 0x1e, 0x77, 0x5f, 0x71, 0xcb, 0x27, 0xb8, 0xdd, 0x5a, 0x20, 0xff, 0x22, 0x60, 0xe1, + 0x38, 0x1e, 0x92, 0xb8, 0xe1, 0x97, 0xfb, 0xbc, 0xc8, 0xc9, 0x1f, 0xf5, 0xb5, 0x0b, 0xe2, 0x8e, + 0xcf, 0x52, 0xac, 0x0c, 0x6d, 0x08, 0x49, 0xef, 0xfd, 0x71, 0xc0, 0xc2, 0x69, 0xcc, 0xfb, 0xea, + 0xb5, 0x6b, 0x96, 0x4f, 0x7c, 0xee, 0x7c, 0xb6, 0x44, 0x63, 0x41, 0x2c, 0xf8, 0xd4, 0xc0, 0x17, + 0x6d, 0xce, 0xd6, 0xab, 0x2e, 0x3f, 0x43, 0xdd, 0x19, 0xdc, 0x15, 0x27, 0x76, 0x61, 0xbd, 0xfe, + 0x69, 0x24, 0x3b, 0x35, 0x92, 0xfd, 0x35, 0x92, 0x7d, 0xb7, 0xd2, 0x3b, 0xb5, 0xd2, 0xfb, 0x6d, + 0xa5, 0xf7, 0x16, 0x66, 0x39, 0x7d, 0x54, 0x49, 0x94, 0x62, 0xa1, 0x86, 0x71, 0xdc, 0xf3, 0x60, + 0xdf, 0x77, 0x8a, 0xea, 0x12, 0xac, 0x1b, 0x2a, 0x99, 0xf4, 0xb3, 0x3c, 0xfe, 0x07, 0x00, 0x00, + 0xff, 0xff, 0xb5, 0x65, 0x82, 0x18, 0x44, 0x01, 0x00, 0x00, +} + +func (m *PageRequest) 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 *PageRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CountTotal { + i-- + if m.CountTotal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.Limit != 0 { + i = encodeVarintPagination(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x18 + } + if m.Offset != 0 { + i = encodeVarintPagination(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x10 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintPagination(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PageResponse) 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 *PageResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Total != 0 { + i = encodeVarintPagination(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x10 + } + if len(m.NextKey) > 0 { + i -= len(m.NextKey) + copy(dAtA[i:], m.NextKey) + i = encodeVarintPagination(dAtA, i, uint64(len(m.NextKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintPagination(dAtA []byte, offset int, v uint64) int { + offset -= sovPagination(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PageRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovPagination(uint64(l)) + } + if m.Offset != 0 { + n += 1 + sovPagination(uint64(m.Offset)) + } + if m.Limit != 0 { + n += 1 + sovPagination(uint64(m.Limit)) + } + if m.CountTotal { + n += 2 + } + return n +} + +func (m *PageResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NextKey) + if l > 0 { + n += 1 + l + sovPagination(uint64(l)) + } + if m.Total != 0 { + n += 1 + sovPagination(uint64(m.Total)) + } + return n +} + +func sovPagination(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozPagination(x uint64) (n int) { + return sovPagination(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PageRequest) 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 ErrIntOverflowPagination + } + 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: PageRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PageRequest: 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 ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPagination + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPagination + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) + } + m.Offset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Offset |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CountTotal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CountTotal = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipPagination(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPagination + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PageResponse) 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 ErrIntOverflowPagination + } + 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: PageResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PageResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPagination + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPagination + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextKey = append(m.NextKey[:0], dAtA[iNdEx:postIndex]...) + if m.NextKey == nil { + m.NextKey = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + m.Total = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPagination + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Total |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipPagination(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPagination + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPagination(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, ErrIntOverflowPagination + } + 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, ErrIntOverflowPagination + } + 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, ErrIntOverflowPagination + } + 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, ErrInvalidLengthPagination + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPagination + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthPagination + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthPagination = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPagination = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPagination = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go new file mode 100644 index 000000000000..f0e1377a1e91 --- /dev/null +++ b/types/query/pagination_test.go @@ -0,0 +1,240 @@ +package query_test + +import ( + gocontext "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + "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/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" +) + +const ( + holder = "holder" + multiPerm = "multiple permissions account" + randomPerm = "random permission" + numBalances = 235 + defaultLimit = 100 + overLimit = 101 + underLimit = 10 + lastPageRecords = 35 +) + +type paginationTestSuite struct { + suite.Suite +} + +func TestPaginationTestSuite(t *testing.T) { + suite.Run(t, new(paginationTestSuite)) +} + +func (s *paginationTestSuite) TestParsePagination() { + s.T().Log("verify default values for empty page request") + pageReq := &query.PageRequest{} + page, limit, err := query.ParsePagination(pageReq) + s.Require().NoError(err) + s.Require().Equal(limit, query.DefaultLimit) + s.Require().Equal(page, 1) + + s.T().Log("verify with custom values") + pageReq = &query.PageRequest{ + Offset: 0, + Limit: 10, + } + page, limit, err = query.ParsePagination(pageReq) + s.Require().NoError(err) + s.Require().Equal(page, 1) + s.Require().Equal(limit, 10) +} + +func (s *paginationTestSuite) TestPagination() { + app, ctx, _ := setupTest() + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.BankKeeper) + queryClient := types.NewQueryClient(queryHelper) + + var balances sdk.Coins + + for i := 0; i < numBalances; i++ { + denom := fmt.Sprintf("foo%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 100)) + } + + 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.T().Log("verify empty page request results a max of defaultLimit records and counts total records") + pageReq := &query.PageRequest{} + request := types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err := queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res.Pagination.Total, uint64(numBalances)) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) + + s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records") + pageReq = &query.PageRequest{Limit: overLimit} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res.Pagination.Total, uint64(0)) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().LessOrEqual(res.Balances.Len(), overLimit) + + s.T().Log("verify paginate with custom limit and countTotal true") + pageReq = &query.PageRequest{Limit: underLimit, CountTotal: true} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res.Balances.Len(), underLimit) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(numBalances)) + + s.T().Log("verify paginate with custom limit and countTotal false") + pageReq = &query.PageRequest{Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res.Balances.Len(), defaultLimit) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate with custom limit, key and countTotal false") + pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res.Balances.Len(), defaultLimit) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate for last page, results in records less than max limit") + pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) + s.Require().Equal(res.Balances.Len(), lastPageRecords) + s.Require().Nil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate with offset and limit") + pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) + s.Require().Equal(res.Balances.Len(), lastPageRecords) + s.Require().Nil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate with offset and limit") + pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) + s.Require().NotNil(res.Pagination.NextKey) + s.Require().Equal(res.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate with offset and key - error") + pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().Error(err) + s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error()) + + s.T().Log("verify paginate with offset greater than total results") + pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().LessOrEqual(res.Balances.Len(), 0) + s.Require().Nil(res.Pagination.NextKey) +} + +func ExamplePaginate() { + app, ctx, _ := setupTest() + + var balances sdk.Coins + + for i := 0; i < 2; i++ { + denom := fmt.Sprintf("foo%ddenom", i) + balances = append(balances, sdk.NewInt64Coin(denom, 100)) + } + + 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 { + 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)) + balancesStore := prefix.NewStore(authStore, types.BalancesPrefix) + accountStore := prefix.NewStore(balancesStore, addr1.Bytes()) + pageRes, err := query.Paginate(accountStore, request.Pagination, func(key []byte, value []byte) error { + var tempRes sdk.Coin + err := app.AppCodec().UnmarshalBinaryBare(value, &tempRes) + if err != nil { + return err + } + balResult = append(balResult, tempRes) + return nil + }) + if err != nil { // should return no error + fmt.Println(err) + } + fmt.Println(&types.QueryAllBalancesResponse{Balances: balResult, Pagination: pageRes}) + // Output: + // balances: pagination: +} + +func setupTest() (*simapp.SimApp, sdk.Context, codec.Marshaler) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) + appCodec := app.AppCodec() + + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + + 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/rest/rest.go b/types/rest/rest.go index 65c1bc094e8a..80afe2fb61d2 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -3,6 +3,7 @@ package rest import ( + "bytes" "encoding/json" "errors" "fmt" @@ -14,14 +15,15 @@ import ( "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" sdk "github.com/cosmos/cosmos-sdk/types" ) const ( - DefaultPage = 1 - DefaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 + DefaultPage = 1 + DefaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 TxMinHeightKey = "tx.minheight" // Inclusive minimum height filter TxMaxHeightKey = "tx.maxheight" // Inclusive maximum height filter ) @@ -41,6 +43,17 @@ func NewResponseWithHeight(height int64, result json.RawMessage) ResponseWithHei } } +// ParseResponseWithHeight returns the raw result from a JSON-encoded +// ResponseWithHeight object. +func ParseResponseWithHeight(cdc *codec.LegacyAmino, bz []byte) ([]byte, error) { + r := ResponseWithHeight{} + if err := cdc.UnmarshalJSON(bz, &r); err != nil { + return nil, err + } + + return r.Result, nil +} + // GasEstimateResponse defines a response definition for tx gas estimation. type GasEstimateResponse struct { GasEstimate uint64 `json:"gas_estimate"` @@ -54,6 +67,7 @@ type BaseReq struct { ChainID string `json:"chain_id"` AccountNumber uint64 `json:"account_number"` Sequence uint64 `json:"sequence"` + TimeoutHeight uint64 `json:"timeout_height"` Fees sdk.Coins `json:"fees"` GasPrices sdk.DecCoins `json:"gas_prices"` Gas string `json:"gas"` @@ -66,7 +80,6 @@ func NewBaseReq( from, memo, chainID string, gas, gasAdjustment string, accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, simulate bool, ) BaseReq { - return BaseReq{ From: strings.TrimSpace(from), Memo: strings.TrimSpace(memo), @@ -119,12 +132,11 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { return true } -// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq stuct. +// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq struct. // Writes an error response to ResponseWriter and returns true if errors occurred. -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) bool { +func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.LegacyAmino, req interface{}) bool { body, err := ioutil.ReadAll(r.Body) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if CheckBadRequestError(w, err) { return false } @@ -148,21 +160,50 @@ func NewErrorResponse(code int, err string) ErrorResponse { return ErrorResponse{Code: code, Error: err} } +// CheckError takes care of writing an error response if err is not nil. +// Returns false when err is nil; it returns true otherwise. +func CheckError(w http.ResponseWriter, status int, err error) bool { + if err != nil { + WriteErrorResponse(w, status, err.Error()) + return true + } + + return false +} + +// CheckBadRequestError attaches an error message to an HTTP 400 BAD REQUEST response. +// Returns false when err is nil; it returns true otherwise. +func CheckBadRequestError(w http.ResponseWriter, err error) bool { + return CheckError(w, http.StatusBadRequest, err) +} + +// CheckInternalServerError attaches an error message to an HTTP 500 INTERNAL SERVER ERROR response. +// Returns false when err is nil; it returns true otherwise. +func CheckInternalServerError(w http.ResponseWriter, err error) bool { + return CheckError(w, http.StatusInternalServerError, err) +} + +// CheckNotFoundError attaches an error message to an HTTP 404 NOT FOUND response. +// Returns false when err is nil; it returns true otherwise. +func CheckNotFoundError(w http.ResponseWriter, err error) bool { + return CheckError(w, http.StatusNotFound, err) +} + // WriteErrorResponse prepares and writes a HTTP error // given a status code and an error message. func WriteErrorResponse(w http.ResponseWriter, status int, err string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) - _, _ = w.Write(codec.Cdc.MustMarshalJSON(NewErrorResponse(0, err))) + _, _ = w.Write(legacy.Cdc.MustMarshalJSON(NewErrorResponse(0, err))) } // WriteSimulationResponse prepares and writes an HTTP // response for transactions simulations. -func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.Codec, gas uint64) { +func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.LegacyAmino, gas uint64) { gasEst := GasEstimateResponse{GasEstimate: gas} + resp, err := cdc.MarshalJSON(gasEst) - if err != nil { - WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if CheckInternalServerError(w, err) { return } @@ -171,28 +212,14 @@ func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.Codec, gas uint64 _, _ = w.Write(resp) } -// ParseInt64OrReturnBadRequest converts s to a int64 value. -func ParseInt64OrReturnBadRequest(w http.ResponseWriter, s string) (n int64, ok bool) { - var err error - - n, err = strconv.ParseInt(s, 10, 64) - if err != nil { - err := fmt.Errorf("'%s' is not a valid int64", s) - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return n, false - } - - return n, true -} - // ParseUint64OrReturnBadRequest converts s to a uint64 value. func ParseUint64OrReturnBadRequest(w http.ResponseWriter, s string) (n uint64, ok bool) { var err error n, err = strconv.ParseUint(s, 10, 64) if err != nil { - err := fmt.Errorf("'%s' is not a valid uint64", s) - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("'%s' is not a valid uint64", s)) + return n, false } @@ -207,8 +234,7 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm } n, err := strconv.ParseFloat(s, 64) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if CheckBadRequestError(w, err) { return n, false } @@ -217,33 +243,32 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm // ParseQueryHeightOrReturnBadRequest sets the height to execute a query if set by the http request. // It returns false if there was an error parsing the height. -func ParseQueryHeightOrReturnBadRequest(w http.ResponseWriter, cliCtx context.CLIContext, r *http.Request) (context.CLIContext, bool) { +func ParseQueryHeightOrReturnBadRequest(w http.ResponseWriter, clientCtx client.Context, r *http.Request) (client.Context, bool) { heightStr := r.FormValue("height") if heightStr != "" { height, err := strconv.ParseInt(heightStr, 10, 64) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return cliCtx, false + if CheckBadRequestError(w, err) { + return clientCtx, false } if height < 0 { WriteErrorResponse(w, http.StatusBadRequest, "height must be equal or greater than zero") - return cliCtx, false + return clientCtx, false } if height > 0 { - cliCtx = cliCtx.WithHeight(height) + clientCtx = clientCtx.WithHeight(height) } } else { - cliCtx = cliCtx.WithHeight(0) + clientCtx = clientCtx.WithHeight(0) } - return cliCtx, true + return clientCtx, true } // PostProcessResponseBare post processes a body similar to PostProcessResponse // except it does not wrap the body and inject the height. -func PostProcessResponseBare(w http.ResponseWriter, cliCtx context.CLIContext, body interface{}) { +func PostProcessResponseBare(w http.ResponseWriter, ctx client.Context, body interface{}) { var ( resp []byte err error @@ -254,14 +279,8 @@ func PostProcessResponseBare(w http.ResponseWriter, cliCtx context.CLIContext, b resp = b default: - if cliCtx.Indent { - resp, err = cliCtx.Codec.MarshalJSONIndent(body, "", " ") - } else { - resp, err = cliCtx.Codec.MarshalJSON(body) - } - - if err != nil { - WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + resp, err = ctx.LegacyAmino.MarshalJSON(body) + if CheckInternalServerError(w, err) { return } } @@ -273,47 +292,35 @@ func PostProcessResponseBare(w http.ResponseWriter, cliCtx context.CLIContext, b // PostProcessResponse performs post processing for a REST response. The result // returned to clients will contain two fields, the height at which the resource // was queried at and the original result. -func PostProcessResponse(w http.ResponseWriter, cliCtx context.CLIContext, resp interface{}) { - var result []byte +func PostProcessResponse(w http.ResponseWriter, ctx client.Context, resp interface{}) { + var ( + result []byte + err error + ) - if cliCtx.Height < 0 { + if ctx.Height < 0 { WriteErrorResponse(w, http.StatusInternalServerError, fmt.Errorf("negative height in response").Error()) return } + // LegacyAmino used intentionally for REST + marshaler := ctx.LegacyAmino + switch res := resp.(type) { case []byte: result = res default: - var err error - if cliCtx.Indent { - result, err = cliCtx.Codec.MarshalJSONIndent(resp, "", " ") - } else { - result, err = cliCtx.Codec.MarshalJSON(resp) - } - - if err != nil { - WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + result, err = marshaler.MarshalJSON(resp) + if CheckInternalServerError(w, err) { return } } - wrappedResp := NewResponseWithHeight(cliCtx.Height, result) + wrappedResp := NewResponseWithHeight(ctx.Height, result) - var ( - output []byte - err error - ) - - if cliCtx.Indent { - output, err = cliCtx.Codec.MarshalJSONIndent(wrappedResp, "", " ") - } else { - output, err = cliCtx.Codec.MarshalJSON(wrappedResp) - } - - if err != nil { - WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + output, err := marshaler.MarshalJSON(wrappedResp) + if CheckInternalServerError(w, err) { return } @@ -326,26 +333,35 @@ func PostProcessResponse(w http.ResponseWriter, cliCtx context.CLIContext, resp // default limit can be provided. func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, page, limit int, err error) { tags = make([]string, 0, len(r.Form)) + for key, values := range r.Form { if key == "page" || key == "limit" { continue } + var value string value, err = url.QueryUnescape(values[0]) + if err != nil { return tags, page, limit, err } var tag string - if key == types.TxHeightKey { + + switch key { + case types.TxHeightKey: tag = fmt.Sprintf("%s=%s", key, value) - } else if key == TxMinHeightKey { + + case TxMinHeightKey: tag = fmt.Sprintf("%s>=%s", types.TxHeightKey, value) - } else if key == TxMaxHeightKey { + + case TxMaxHeightKey: tag = fmt.Sprintf("%s<=%s", types.TxHeightKey, value) - } else { + + default: tag = fmt.Sprintf("%s='%s'", key, value) } + tags = append(tags, tag) } @@ -381,3 +397,53 @@ func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, p func ParseHTTPArgs(r *http.Request) (tags []string, page, limit int, err error) { return ParseHTTPArgsWithLimit(r, DefaultLimit) } + +// ParseQueryParamBool parses the given param to a boolean. It returns false by +// default if the string is not parseable to bool. +func ParseQueryParamBool(r *http.Request, param string) bool { + if value, err := strconv.ParseBool(r.FormValue(param)); err == nil { + return value + } + + return false +} + +// GetRequest defines a wrapper around an HTTP GET request with a provided URL. +// An error is returned if the request or reading the body fails. +func GetRequest(url string) ([]byte, error) { + res, err := http.Get(url) // nolint:gosec + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if err = res.Body.Close(); err != nil { + return nil, err + } + + return body, nil +} + +// PostRequest defines a wrapper around an HTTP POST request with a provided URL and data. +// An error is returned if the request or reading the body fails. +func PostRequest(url string, contentType string, data []byte) ([]byte, error) { + res, err := http.Post(url, contentType, bytes.NewBuffer(data)) // nolint:gosec + if err != nil { + return nil, fmt.Errorf("error while sending post request: %w", err) + } + + bz, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + + if err = res.Body.Close(); err != nil { + return nil, err + } + + return bz, nil +} diff --git a/types/rest/rest_test.go b/types/rest/rest_test.go index 23492acf7226..8f873645d64c 100644 --- a/types/rest/rest_test.go +++ b/types/rest/rest_test.go @@ -1,50 +1,71 @@ -// Package rest provides HTTP types and primitives for REST -// requests validation and responses handling. -package rest +package rest_test import ( + "errors" "io" "io/ioutil" "net/http" "net/http/httptest" "sort" + "strings" "testing" + "github.com/spf13/viper" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" ) -func TestBaseReqValidateBasic(t *testing.T) { - fromAddr := "fetch1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jc435mc" - tenstakes, err := types.ParseCoins("10stake") +func TestBaseReq_Sanitize(t *testing.T) { + t.Parallel() + sanitized := rest.BaseReq{ChainID: " test", + Memo: "memo ", + From: " cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0 ", + Gas: " ", + GasAdjustment: " 0.3", + }.Sanitize() + require.Equal(t, rest.BaseReq{ChainID: "test", + Memo: "memo", + From: "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0", + Gas: "", + GasAdjustment: "0.3", + }, sanitized) +} + +func TestBaseReq_ValidateBasic(t *testing.T) { + fromAddr := "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0" + tenstakes, err := types.ParseCoinsNormalized("10stake") require.NoError(t, err) onestake, err := types.ParseDecCoins("1.0stake") require.NoError(t, err) - req1 := NewBaseReq( + req1 := rest.NewBaseReq( fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, nil, false, ) - req2 := NewBaseReq( + req2 := rest.NewBaseReq( "", "", "nonempty", "", "", 0, 0, tenstakes, nil, false, ) - req3 := NewBaseReq( + req3 := rest.NewBaseReq( fromAddr, "", "", "", "", 0, 0, tenstakes, nil, false, ) - req4 := NewBaseReq( + req4 := rest.NewBaseReq( fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, onestake, false, ) - req5 := NewBaseReq( + req5 := rest.NewBaseReq( fromAddr, "", "nonempty", "", "", 0, 0, types.Coins{}, types.DecCoins{}, false, ) tests := []struct { name string - req BaseReq + req rest.BaseReq w http.ResponseWriter want bool }{ @@ -63,6 +84,7 @@ func TestBaseReqValidateBasic(t *testing.T) { } func TestParseHTTPArgs(t *testing.T) { + t.Parallel() req0 := mustNewRequest(t, "", "/", nil) req1 := mustNewRequest(t, "", "/?limit=5", nil) req2 := mustNewRequest(t, "", "/?page=5", nil) @@ -83,21 +105,21 @@ func TestParseHTTPArgs(t *testing.T) { limit int err bool }{ - {"no params", req0, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, false}, - {"Limit", req1, httptest.NewRecorder(), []string{}, DefaultPage, 5, false}, - {"Page", req2, httptest.NewRecorder(), []string{}, 5, DefaultLimit, false}, + {"no params", req0, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, false}, + {"Limit", req1, httptest.NewRecorder(), []string{}, rest.DefaultPage, 5, false}, + {"Page", req2, httptest.NewRecorder(), []string{}, 5, rest.DefaultLimit, false}, {"Page and limit", req3, httptest.NewRecorder(), []string{}, 5, 5, false}, - {"error page 0", reqE1, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, true}, - {"error limit 0", reqE2, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, true}, + {"error page 0", reqE1, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, true}, + {"error limit 0", reqE2, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, true}, - {"tags", req4, httptest.NewRecorder(), []string{"foo='faa'"}, DefaultPage, DefaultLimit, false}, - {"tags", reqTxH, httptest.NewRecorder(), []string{"tx.height<=14", "tx.height>=12"}, DefaultPage, DefaultLimit, false}, + {"tags", req4, httptest.NewRecorder(), []string{"foo='faa'"}, rest.DefaultPage, rest.DefaultLimit, false}, + {"tags", reqTxH, httptest.NewRecorder(), []string{"tx.height<=14", "tx.height>=12"}, rest.DefaultPage, rest.DefaultLimit, false}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - tags, page, limit, err := ParseHTTPArgs(tt.req) + tags, page, limit, err := rest.ParseHTTPArgs(tt.req) sort.Strings(tags) @@ -114,6 +136,7 @@ func TestParseHTTPArgs(t *testing.T) { } func TestParseQueryHeight(t *testing.T) { + t.Parallel() var emptyHeight int64 height := int64(1256756) @@ -126,25 +149,25 @@ func TestParseQueryHeight(t *testing.T) { name string req *http.Request w http.ResponseWriter - cliCtx context.CLIContext + clientCtx client.Context expectedHeight int64 expectedOk bool }{ - {"no height", req0, httptest.NewRecorder(), context.CLIContext{}, emptyHeight, true}, - {"height", req1, httptest.NewRecorder(), context.CLIContext{}, height, true}, - {"invalid height", req2, httptest.NewRecorder(), context.CLIContext{}, emptyHeight, false}, - {"negative height", req3, httptest.NewRecorder(), context.CLIContext{}, emptyHeight, false}, + {"no height", req0, httptest.NewRecorder(), client.Context{}, emptyHeight, true}, + {"height", req1, httptest.NewRecorder(), client.Context{}, height, true}, + {"invalid height", req2, httptest.NewRecorder(), client.Context{}, emptyHeight, false}, + {"negative height", req3, httptest.NewRecorder(), client.Context{}, emptyHeight, false}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - cliCtx, ok := ParseQueryHeightOrReturnBadRequest(tt.w, tt.cliCtx, tt.req) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(tt.w, tt.clientCtx, tt.req) if tt.expectedOk { require.True(t, ok) - require.Equal(t, tt.expectedHeight, cliCtx.Height) + require.Equal(t, tt.expectedHeight, clientCtx.Height) } else { require.False(t, ok) - require.Empty(t, tt.expectedHeight, cliCtx.Height) + require.Empty(t, tt.expectedHeight, clientCtx.Height) } }) } @@ -153,18 +176,20 @@ func TestParseQueryHeight(t *testing.T) { func TestProcessPostResponse(t *testing.T) { // mock account // PubKey field ensures amino encoding is used first since standard - // JSON encoding will panic on crypto.PubKey + // JSON encoding will panic on cryptotypes.PubKey + t.Parallel() type mockAccount struct { - Address types.AccAddress `json:"address"` - Coins types.Coins `json:"coins"` - PubKey crypto.PubKey `json:"public_key"` - AccountNumber uint64 `json:"account_number"` - Sequence uint64 `json:"sequence"` + Address types.AccAddress `json:"address"` + Coins types.Coins `json:"coins"` + PubKey cryptotypes.PubKey `json:"public_key"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` } // setup - ctx := context.NewCLIContext() + viper.Set(flags.FlagOffline, true) + ctx := client.Context{} height := int64(194423) privKey := secp256k1.GenPrivKey() @@ -175,71 +200,223 @@ func TestProcessPostResponse(t *testing.T) { sequence := uint64(32) acc := mockAccount{addr, coins, pubKey, accNumber, sequence} - cdc := codec.New() - codec.RegisterCrypto(cdc) + cdc := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(cdc) cdc.RegisterConcrete(&mockAccount{}, "cosmos-sdk/mockAccount", nil) - ctx = ctx.WithCodec(cdc) + ctx = ctx.WithLegacyAmino(cdc) // setup expected results - jsonNoIndent, err := ctx.Codec.MarshalJSON(acc) - require.Nil(t, err) - jsonWithIndent, err := ctx.Codec.MarshalJSONIndent(acc, "", " ") + jsonNoIndent, err := ctx.LegacyAmino.MarshalJSON(acc) require.Nil(t, err) - respNoIndent := NewResponseWithHeight(height, jsonNoIndent) - respWithIndent := NewResponseWithHeight(height, jsonWithIndent) - expectedNoIndent, err := ctx.Codec.MarshalJSON(respNoIndent) - require.Nil(t, err) - expectedWithIndent, err := ctx.Codec.MarshalJSONIndent(respWithIndent, "", " ") + + respNoIndent := rest.NewResponseWithHeight(height, jsonNoIndent) + expectedNoIndent, err := ctx.LegacyAmino.MarshalJSON(respNoIndent) require.Nil(t, err) // check that negative height writes an error w := httptest.NewRecorder() ctx = ctx.WithHeight(-1) - PostProcessResponse(w, ctx, acc) + rest.PostProcessResponse(w, ctx, acc) require.Equal(t, http.StatusInternalServerError, w.Code) // check that height returns expected response ctx = ctx.WithHeight(height) - runPostProcessResponse(t, ctx, acc, expectedNoIndent, false) - // check height with indent - runPostProcessResponse(t, ctx, acc, expectedWithIndent, true) + runPostProcessResponse(t, ctx, acc, expectedNoIndent) +} + +func TestReadRESTReq(t *testing.T) { + t.Parallel() + reqBody := ioutil.NopCloser(strings.NewReader(`{"chain_id":"alessio","memo":"text"}`)) + req := &http.Request{Body: reqBody} + w := httptest.NewRecorder() + var br rest.BaseReq + + // test OK + rest.ReadRESTReq(w, req, codec.NewLegacyAmino(), &br) + res := w.Result() //nolint:bodyclose + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, rest.BaseReq{ChainID: "alessio", Memo: "text"}, br) + require.Equal(t, http.StatusOK, res.StatusCode) + + // test non valid JSON + reqBody = ioutil.NopCloser(strings.NewReader(`MALFORMED`)) + req = &http.Request{Body: reqBody} + br = rest.BaseReq{} + w = httptest.NewRecorder() + rest.ReadRESTReq(w, req, codec.NewLegacyAmino(), &br) + require.Equal(t, br, br) + res = w.Result() //nolint:bodyclose + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, http.StatusBadRequest, res.StatusCode) +} + +func TestWriteSimulationResponse(t *testing.T) { + t.Parallel() + w := httptest.NewRecorder() + rest.WriteSimulationResponse(w, codec.NewLegacyAmino(), 10) + res := w.Result() //nolint:bodyclose + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, http.StatusOK, res.StatusCode) + bs, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, `{"gas_estimate":"10"}`, string(bs)) +} + +func TestParseUint64OrReturnBadRequest(t *testing.T) { + t.Parallel() + w := httptest.NewRecorder() + _, ok := rest.ParseUint64OrReturnBadRequest(w, "100") + require.True(t, ok) + require.Equal(t, http.StatusOK, w.Result().StatusCode) //nolint:bodyclose + + w = httptest.NewRecorder() + _, ok = rest.ParseUint64OrReturnBadRequest(w, "-100") + require.False(t, ok) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) //nolint:bodyclose +} + +func TestParseFloat64OrReturnBadRequest(t *testing.T) { + t.Parallel() + w := httptest.NewRecorder() + _, ok := rest.ParseFloat64OrReturnBadRequest(w, "100", 0) + require.True(t, ok) + require.Equal(t, http.StatusOK, w.Result().StatusCode) //nolint:bodyclose + + w = httptest.NewRecorder() + _, ok = rest.ParseFloat64OrReturnBadRequest(w, "bad request", 0) + require.False(t, ok) + require.Equal(t, http.StatusBadRequest, w.Result().StatusCode) //nolint:bodyclose + + w = httptest.NewRecorder() + ret, ok := rest.ParseFloat64OrReturnBadRequest(w, "", 9.0) + require.Equal(t, float64(9), ret) + require.True(t, ok) + require.Equal(t, http.StatusOK, w.Result().StatusCode) //nolint:bodyclose +} + +func TestParseQueryParamBool(t *testing.T) { + req := httptest.NewRequest("GET", "/target?boolean=true", nil) + require.True(t, rest.ParseQueryParamBool(req, "boolean")) + require.False(t, rest.ParseQueryParamBool(req, "nokey")) + req = httptest.NewRequest("GET", "/target?boolean=false", nil) + require.False(t, rest.ParseQueryParamBool(req, "boolean")) + require.False(t, rest.ParseQueryParamBool(req, "")) +} + +func TestPostProcessResponseBare(t *testing.T) { + t.Parallel() + + encodingConfig := simappparams.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino) // amino used intentionally here + // write bytes + w := httptest.NewRecorder() + bs := []byte("text string") + + rest.PostProcessResponseBare(w, clientCtx, bs) + + res := w.Result() //nolint:bodyclose + require.Equal(t, http.StatusOK, res.StatusCode) + + got, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, "text string", string(got)) + + // write struct and indent response + w = httptest.NewRecorder() + data := struct { + X int `json:"x"` + S string `json:"s"` + }{X: 10, S: "test"} + + rest.PostProcessResponseBare(w, clientCtx, data) + + res = w.Result() //nolint:bodyclose + require.Equal(t, http.StatusOK, res.StatusCode) + + got, err = ioutil.ReadAll(res.Body) + require.NoError(t, err) + + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, "{\"x\":\"10\",\"s\":\"test\"}", string(got)) + + // write struct, don't indent response + w = httptest.NewRecorder() + data = struct { + X int `json:"x"` + S string `json:"s"` + }{X: 10, S: "test"} + + rest.PostProcessResponseBare(w, clientCtx, data) + + res = w.Result() //nolint:bodyclose + require.Equal(t, http.StatusOK, res.StatusCode) + + got, err = ioutil.ReadAll(res.Body) + require.NoError(t, err) + + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, `{"x":"10","s":"test"}`, string(got)) + + // test marshalling failure + w = httptest.NewRecorder() + data2 := badJSONMarshaller{} + + rest.PostProcessResponseBare(w, clientCtx, data2) + + res = w.Result() //nolint:bodyclose + require.Equal(t, http.StatusInternalServerError, res.StatusCode) + + got, err = ioutil.ReadAll(res.Body) + require.NoError(t, err) + + t.Cleanup(func() { res.Body.Close() }) + require.Equal(t, []string{"application/json"}, res.Header["Content-Type"]) + require.Equal(t, `{"error":"couldn't marshal"}`, string(got)) +} + +type badJSONMarshaller struct{} + +func (badJSONMarshaller) MarshalJSON() ([]byte, error) { + return nil, errors.New("couldn't marshal") } // asserts that ResponseRecorder returns the expected code and body // runs PostProcessResponse on the objects regular interface and on // the marshalled struct. -func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{}, expectedBody []byte, indent bool) { - if indent { - ctx.Indent = indent - } - +func runPostProcessResponse(t *testing.T, ctx client.Context, obj interface{}, expectedBody []byte) { // test using regular struct w := httptest.NewRecorder() - PostProcessResponse(w, ctx, obj) + + rest.PostProcessResponse(w, ctx, obj) require.Equal(t, http.StatusOK, w.Code, w.Body) - resp := w.Result() - defer resp.Body.Close() + + resp := w.Result() //nolint:bodyclose + t.Cleanup(func() { resp.Body.Close() }) + body, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) require.Equal(t, expectedBody, body) - var marshalled []byte - if indent { - marshalled, err = ctx.Codec.MarshalJSONIndent(obj, "", " ") - } else { - marshalled, err = ctx.Codec.MarshalJSON(obj) - } - require.Nil(t, err) + marshalled, err := ctx.LegacyAmino.MarshalJSON(obj) + require.NoError(t, err) // test using marshalled struct w = httptest.NewRecorder() - PostProcessResponse(w, ctx, marshalled) + rest.PostProcessResponse(w, ctx, marshalled) + require.Equal(t, http.StatusOK, w.Code, w.Body) - resp = w.Result() - defer resp.Body.Close() + resp = w.Result() //nolint:bodyclose + + t.Cleanup(func() { resp.Body.Close() }) body, err = ioutil.ReadAll(resp.Body) + require.Nil(t, err) - require.Equal(t, expectedBody, body) + require.Equal(t, string(expectedBody), string(body)) } func mustNewRequest(t *testing.T, method, url string, body io.Reader) *http.Request { @@ -249,3 +426,35 @@ func mustNewRequest(t *testing.T, method, url string, body io.Reader) *http.Requ require.NoError(t, err) return req } + +func TestCheckErrors(t *testing.T) { + t.Parallel() + err := errors.New("ERROR") + tests := []struct { + name string + checkerFn func(w http.ResponseWriter, err error) bool + error error + wantErr bool + wantString string + wantStatus int + }{ + {"500", rest.CheckInternalServerError, err, true, `{"error":"ERROR"}`, http.StatusInternalServerError}, + {"500 (no error)", rest.CheckInternalServerError, nil, false, ``, http.StatusInternalServerError}, + {"400", rest.CheckBadRequestError, err, true, `{"error":"ERROR"}`, http.StatusBadRequest}, + {"400 (no error)", rest.CheckBadRequestError, nil, false, ``, http.StatusBadRequest}, + {"404", rest.CheckNotFoundError, err, true, `{"error":"ERROR"}`, http.StatusNotFound}, + {"404 (no error)", rest.CheckNotFoundError, nil, false, ``, http.StatusNotFound}, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + w := httptest.NewRecorder() + require.Equal(t, tt.wantErr, tt.checkerFn(w, tt.error)) + if tt.wantErr { + require.Equal(t, w.Body.String(), tt.wantString) + require.Equal(t, w.Code, tt.wantStatus) + } + }) + } +} diff --git a/types/result.go b/types/result.go index 9530be01d25c..6a50ecffbacd 100644 --- a/types/result.go +++ b/types/result.go @@ -7,48 +7,42 @@ import ( "math" "strings" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/gogo/protobuf/proto" + + yaml "gopkg.in/yaml.v2" + abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" ) -// GasInfo defines tx execution gas context. -type GasInfo struct { - // GasWanted is the maximum units of work we allow this tx to perform. - GasWanted uint64 +var cdc = codec.NewLegacyAmino() - // GasUsed is the amount of gas actually consumed. NOTE: unimplemented - GasUsed uint64 +func (gi GasInfo) String() string { + bz, _ := yaml.Marshal(gi) + return string(bz) } -// Result is the union of ResponseFormat and ResponseCheckTx. -type Result struct { - // Data is any data returned from message or handler execution. It MUST be length - // prefixed in order to separate data from multiple message executions. - Data []byte +func (r Result) String() string { + bz, _ := yaml.Marshal(r) + return string(bz) +} - // Log contains the log information from message or handler execution. - Log string +func (r Result) GetEvents() Events { + events := make(Events, len(r.Events)) + for i, e := range r.Events { + events[i] = Event(e) + } - // Events contains a slice of Event objects that were emitted during message or - // handler execution. - Events Events + return events } // ABCIMessageLogs represents a slice of ABCIMessageLog. type ABCIMessageLogs []ABCIMessageLog -// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. -type ABCIMessageLog struct { - MsgIndex uint16 `json:"msg_index"` - Log string `json:"log"` - - // Events contains a slice of Event objects that were emitted during some - // execution. - Events StringEvents `json:"events"` -} - -func NewABCIMessageLog(i uint16, log string, events Events) ABCIMessageLog { +func NewABCIMessageLog(i uint32, log string, events Events) ABCIMessageLog { return ABCIMessageLog{ MsgIndex: i, Log: log, @@ -59,7 +53,7 @@ func NewABCIMessageLog(i uint16, log string, events Events) ABCIMessageLog { // String implements the fmt.Stringer interface for the ABCIMessageLogs type. func (logs ABCIMessageLogs) String() (str string) { if logs != nil { - raw, err := codec.Cdc.MarshalJSON(logs) + raw, err := cdc.MarshalJSON(logs) if err == nil { str = string(raw) } @@ -68,32 +62,15 @@ func (logs ABCIMessageLogs) String() (str string) { return str } -// TxResponse defines a structure containing relevant tx data and metadata. The -// tags are stringified and the log is JSON decoded. -type TxResponse struct { - Height int64 `json:"height"` - TxHash string `json:"txhash"` - Codespace string `json:"codespace,omitempty"` - Code uint32 `json:"code,omitempty"` - Data string `json:"data,omitempty"` - RawLog string `json:"raw_log,omitempty"` - Logs ABCIMessageLogs `json:"logs,omitempty"` - Info string `json:"info,omitempty"` - GasWanted int64 `json:"gas_wanted,omitempty"` - GasUsed int64 `json:"gas_used,omitempty"` - Tx Tx `json:"tx,omitempty"` - Timestamp string `json:"timestamp,omitempty"` -} - // NewResponseResultTx returns a TxResponse given a ResultTx from tendermint -func NewResponseResultTx(res *ctypes.ResultTx, tx Tx, timestamp string) TxResponse { +func NewResponseResultTx(res *ctypes.ResultTx, anyTx *codectypes.Any, timestamp string) *TxResponse { if res == nil { - return TxResponse{} + return nil } parsedLogs, _ := ParseABCILogs(res.TxResult.Log) - return TxResponse{ + return &TxResponse{ TxHash: res.Hash.String(), Height: res.Height, Codespace: res.TxResult.Codespace, @@ -104,16 +81,16 @@ func NewResponseResultTx(res *ctypes.ResultTx, tx Tx, timestamp string) TxRespon Info: res.TxResult.Info, GasWanted: res.TxResult.GasWanted, GasUsed: res.TxResult.GasUsed, - Tx: tx, + Tx: anyTx, Timestamp: timestamp, } } // NewResponseFormatBroadcastTxCommit returns a TxResponse given a // ResultBroadcastTxCommit from tendermint. -func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) TxResponse { +func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { - return TxResponse{} + return nil } if !res.CheckTx.IsOK() { @@ -123,9 +100,9 @@ func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) TxR return newTxResponseDeliverTx(res) } -func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { +func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { - return TxResponse{} + return nil } var txHash string @@ -135,7 +112,7 @@ func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { parsedLogs, _ := ParseABCILogs(res.CheckTx.Log) - return TxResponse{ + return &TxResponse{ Height: res.Height, TxHash: txHash, Codespace: res.CheckTx.Codespace, @@ -149,9 +126,9 @@ func newTxResponseCheckTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { } } -func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { +func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) *TxResponse { if res == nil { - return TxResponse{} + return nil } var txHash string @@ -161,7 +138,7 @@ func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { parsedLogs, _ := ParseABCILogs(res.DeliverTx.Log) - return TxResponse{ + return &TxResponse{ Height: res.Height, TxHash: txHash, Codespace: res.DeliverTx.Codespace, @@ -176,19 +153,20 @@ func newTxResponseDeliverTx(res *ctypes.ResultBroadcastTxCommit) TxResponse { } // NewResponseFormatBroadcastTx returns a TxResponse given a ResultBroadcastTx from tendermint -func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) TxResponse { +func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) *TxResponse { if res == nil { - return TxResponse{} + return nil } parsedLogs, _ := ParseABCILogs(res.Log) - return TxResponse{ - Code: res.Code, - Data: res.Data.String(), - RawLog: res.Log, - Logs: parsedLogs, - TxHash: res.Hash.String(), + return &TxResponse{ + Code: res.Code, + Codespace: res.Codespace, + Data: res.Data.String(), + RawLog: res.Log, + Logs: parsedLogs, + TxHash: res.Hash.String(), } } @@ -238,22 +216,12 @@ func (r TxResponse) Empty() bool { return r.TxHash == "" && r.Logs == nil } -// SearchTxsResult defines a structure for querying txs pageable -type SearchTxsResult struct { - TotalCount int `json:"total_count"` // Count of all txs - Count int `json:"count"` // Count of txs in current page - PageNumber int `json:"page_number"` // Index of current page, start from 1 - PageTotal int `json:"page_total"` // Count of total pages - Limit int `json:"limit"` // Max count txs per page - Txs []TxResponse `json:"txs"` // List of txs in current page -} - -func NewSearchTxsResult(totalCount, count, page, limit int, txs []TxResponse) SearchTxsResult { - return SearchTxsResult{ +func NewSearchTxsResult(totalCount, count, page, limit uint64, txs []*TxResponse) *SearchTxsResult { + return &SearchTxsResult{ TotalCount: totalCount, Count: count, PageNumber: page, - PageTotal: int(math.Ceil(float64(totalCount) / float64(limit))), + PageTotal: uint64(math.Ceil(float64(totalCount) / float64(limit))), Limit: limit, Txs: txs, } @@ -265,3 +233,63 @@ func ParseABCILogs(logs string) (res ABCIMessageLogs, err error) { err = json.Unmarshal([]byte(logs), &res) return res, err } + +var _, _ codectypes.UnpackInterfacesMessage = SearchTxsResult{}, TxResponse{} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +// +// types.UnpackInterfaces needs to be called for each nested Tx because +// there are generally interfaces to unpack in Tx's +func (s SearchTxsResult) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, tx := range s.Txs { + err := codectypes.UnpackInterfaces(tx, unpacker) + if err != nil { + return err + } + } + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (r TxResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + if r.Tx != nil { + var tx Tx + return unpacker.UnpackAny(r.Tx, &tx) + } + return nil +} + +// GetTx unpacks the Tx from within a TxResponse and returns it +func (r TxResponse) GetTx() Tx { + if tx, ok := r.Tx.GetCachedValue().(Tx); ok { + return tx + } + return nil +} + +// WrapServiceResult wraps a result from a protobuf RPC service method call in +// a Result object or error. This method takes care of marshaling the res param to +// protobuf and attaching any events on the ctx.EventManager() to the Result. +func WrapServiceResult(ctx Context, res proto.Message, err error) (*Result, error) { + if err != nil { + return nil, err + } + + var data []byte + if res != nil { + data, err = proto.Marshal(res) + if err != nil { + return nil, err + } + } + + var events []abci.Event + if evtMgr := ctx.EventManager(); evtMgr != nil { + events = evtMgr.ABCIEvents() + } + + return &Result{ + Data: data, + Events: events, + }, nil +} diff --git a/types/result_test.go b/types/result_test.go index 992045778654..6ca9731f8cc4 100644 --- a/types/result_test.go +++ b/types/result_test.go @@ -1,29 +1,217 @@ -package types +package types_test import ( + "encoding/hex" + "fmt" + "strings" "testing" + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/bytes" + ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestParseABCILog(t *testing.T) { +type resultTestSuite struct { + suite.Suite +} + +func TestResultTestSuite(t *testing.T) { + suite.Run(t, new(resultTestSuite)) +} + +func (s *resultTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *resultTestSuite) TestParseABCILog() { logs := `[{"log":"","msg_index":1,"success":true}]` + res, err := sdk.ParseABCILogs(logs) - res, err := ParseABCILogs(logs) - require.NoError(t, err) - require.Len(t, res, 1) - require.Equal(t, res[0].Log, "") - require.Equal(t, res[0].MsgIndex, uint16(1)) + s.Require().NoError(err) + s.Require().Len(res, 1) + s.Require().Equal(res[0].Log, "") + s.Require().Equal(res[0].MsgIndex, uint32(1)) +} + +func (s *resultTestSuite) TestABCIMessageLog() { + cdc := codec.NewLegacyAmino() + events := sdk.Events{sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"))} + msgLog := sdk.NewABCIMessageLog(0, "", events) + msgLogs := sdk.ABCIMessageLogs{msgLog} + bz, err := cdc.MarshalJSON(msgLogs) + + s.Require().NoError(err) + s.Require().Equal(string(bz), msgLogs.String()) } -func TestABCIMessageLog(t *testing.T) { - events := Events{NewEvent("transfer", NewAttribute("sender", "foo"))} - msgLog := NewABCIMessageLog(0, "", events) +func (s *resultTestSuite) TestNewSearchTxsResult() { + got := sdk.NewSearchTxsResult(150, 20, 2, 20, []*sdk.TxResponse{}) + s.Require().Equal(&sdk.SearchTxsResult{ + TotalCount: 150, + Count: 20, + PageNumber: 2, + PageTotal: 8, + Limit: 20, + Txs: []*sdk.TxResponse{}, + }, got) +} + +func (s *resultTestSuite) TestResponseResultTx() { + deliverTxResult := abci.ResponseDeliverTx{ + Codespace: "codespace", + Code: 1, + Data: []byte("data"), + Log: `[]`, + Info: "info", + GasWanted: 100, + GasUsed: 90, + } + resultTx := &ctypes.ResultTx{ + Hash: bytes.HexBytes([]byte("test")), + Height: 10, + TxResult: deliverTxResult, + } + logs, err := sdk.ParseABCILogs(`[]`) + + s.Require().NoError(err) + + want := &sdk.TxResponse{ + TxHash: "74657374", + Height: 10, + Codespace: "codespace", + Code: 1, + Data: strings.ToUpper(hex.EncodeToString([]byte("data"))), + RawLog: `[]`, + Logs: logs, + Info: "info", + GasWanted: 100, + GasUsed: 90, + Tx: nil, + Timestamp: "timestamp", + } + + s.Require().Equal(want, sdk.NewResponseResultTx(resultTx, nil, "timestamp")) + s.Require().Equal((*sdk.TxResponse)(nil), sdk.NewResponseResultTx(nil, nil, "timestamp")) + s.Require().Equal(`Response: + Height: 10 + TxHash: 74657374 + Code: 1 + Data: 64617461 + Raw Log: [] + Logs: [] + Info: info + GasWanted: 100 + GasUsed: 90 + Codespace: codespace + Timestamp: timestamp`, sdk.NewResponseResultTx(resultTx, nil, "timestamp").String()) + s.Require().True(sdk.TxResponse{}.Empty()) + s.Require().False(want.Empty()) + + resultBroadcastTx := &ctypes.ResultBroadcastTx{ + Code: 1, + Codespace: "codespace", + Data: []byte("data"), + Log: `[]`, + Hash: bytes.HexBytes([]byte("test")), + } + + s.Require().Equal(&sdk.TxResponse{ + Code: 1, + Codespace: "codespace", + Data: "64617461", + RawLog: `[]`, + Logs: logs, + TxHash: "74657374", + }, sdk.NewResponseFormatBroadcastTx(resultBroadcastTx)) + s.Require().Equal((*sdk.TxResponse)(nil), sdk.NewResponseFormatBroadcastTx(nil)) +} + +func (s *resultTestSuite) TestResponseFormatBroadcastTxCommit() { + // test nil + s.Require().Equal((*sdk.TxResponse)(nil), sdk.NewResponseFormatBroadcastTxCommit(nil)) + + logs, err := sdk.ParseABCILogs(`[]`) + s.Require().NoError(err) + + // test checkTx + checkTxResult := &ctypes.ResultBroadcastTxCommit{ + Height: 10, + Hash: bytes.HexBytes([]byte("test")), + CheckTx: abci.ResponseCheckTx{ + Code: 90, + Data: nil, + Log: `[]`, + Info: "info", + GasWanted: 99, + GasUsed: 100, + Codespace: "codespace", + }, + } + deliverTxResult := &ctypes.ResultBroadcastTxCommit{ + Height: 10, + Hash: bytes.HexBytes([]byte("test")), + DeliverTx: abci.ResponseDeliverTx{ + Code: 90, + Data: nil, + Log: `[]`, + Info: "info", + GasWanted: 99, + GasUsed: 100, + Codespace: "codespace", + }, + } + want := &sdk.TxResponse{ + Height: 10, + TxHash: "74657374", + Codespace: "codespace", + Code: 90, + Data: "", + RawLog: `[]`, + Logs: logs, + Info: "info", + GasWanted: 99, + GasUsed: 100, + } + + s.Require().Equal(want, sdk.NewResponseFormatBroadcastTxCommit(checkTxResult)) + s.Require().Equal(want, sdk.NewResponseFormatBroadcastTxCommit(deliverTxResult)) +} + +func TestWrapServiceResult(t *testing.T) { + ctx := sdk.Context{} + + res, err := sdk.WrapServiceResult(ctx, nil, fmt.Errorf("test")) + require.Nil(t, res) + require.NotNil(t, err) + + res, err = sdk.WrapServiceResult(ctx, nil, nil) + require.NotNil(t, res) + require.Nil(t, err) + require.Empty(t, res.Events) + + ctx = ctx.WithEventManager(sdk.NewEventManager()) + ctx.EventManager().EmitEvent(sdk.NewEvent("test")) + res, err = sdk.WrapServiceResult(ctx, nil, nil) + require.NotNil(t, res) + require.Nil(t, err) + require.Len(t, res.Events, 1) - msgLogs := ABCIMessageLogs{msgLog} - bz, err := codec.Cdc.MarshalJSON(msgLogs) + spot := testdata.Dog{Name: "spot"} + res, err = sdk.WrapServiceResult(ctx, &spot, nil) + require.NotNil(t, res) + require.Nil(t, err) + require.Len(t, res.Events, 1) + var spot2 testdata.Dog + err = proto.Unmarshal(res.Data, &spot2) require.NoError(t, err) - require.Equal(t, string(bz), msgLogs.String()) + require.Equal(t, spot, spot2) } diff --git a/types/router.go b/types/router.go index f3593f5530ff..9dab11f606c0 100644 --- a/types/router.go +++ b/types/router.go @@ -1,17 +1,63 @@ package types -import "regexp" +import ( + "regexp" + "strings" +) -// IsAlphaNumeric defines a regular expression for matching against alpha-numeric -// values. -var IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString +var ( + // IsAlphaNumeric defines a regular expression for matching against alpha-numeric + // values. + IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString + + // IsAlphaLower defines regular expression to check if the string has lowercase + // alphabetic characters only. + IsAlphaLower = regexp.MustCompile(`^[a-z]+$`).MatchString + + // IsAlphaUpper defines regular expression to check if the string has uppercase + // alphabetic characters only. + IsAlphaUpper = regexp.MustCompile(`^[A-Z]+$`).MatchString + + // IsAlpha defines regular expression to check if the string has alphabetic + // characters only. + IsAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString + + // IsNumeric defines regular expression to check if the string has numeric + // characters only. + IsNumeric = regexp.MustCompile(`^[0-9]+$`).MatchString +) // Router provides handlers for each transaction type. type Router interface { - AddRoute(r string, h Handler) Router + AddRoute(r Route) Router Route(ctx Context, path string) Handler } +type Route struct { + path string + handler Handler +} + +// NewRoute returns an instance of Route. +func NewRoute(p string, h Handler) Route { + return Route{path: strings.TrimSpace(p), handler: h} +} + +// Path returns the path the route has assigned. +func (r Route) Path() string { + return r.path +} + +// Handler returns the handler that handles the route. +func (r Route) Handler() Handler { + return r.handler +} + +// Empty returns true only if both handler and path are not empty. +func (r Route) Empty() bool { + return r.handler == nil || r.path == "" +} + // QueryRouter provides queryables for each query path. type QueryRouter interface { AddRoute(r string, h Querier) QueryRouter diff --git a/types/router_test.go b/types/router_test.go new file mode 100644 index 000000000000..7387e9b94506 --- /dev/null +++ b/types/router_test.go @@ -0,0 +1,58 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type routeTestSuite struct { + suite.Suite +} + +func TestRouteTestSuite(t *testing.T) { + suite.Run(t, new(routeTestSuite)) +} + +func (s *routeTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *routeTestSuite) TestNilRoute() { + tests := []struct { + name string + route sdk.Route + expected bool + }{ + { + name: "all empty", + route: sdk.NewRoute("", nil), + expected: true, + }, + { + name: "only path", + route: sdk.NewRoute("some", nil), + expected: true, + }, + { + name: "only handler", + route: sdk.NewRoute("", func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + return nil, nil + }), + expected: true, + }, + { + name: "correct route", + route: sdk.NewRoute("some", func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + return nil, nil + }), + expected: false, + }, + } + + for _, tt := range tests { + s.Require().Equal(tt.expected, tt.route.Empty()) + } +} diff --git a/types/service_msg.go b/types/service_msg.go new file mode 100644 index 000000000000..61c0f15ab12a --- /dev/null +++ b/types/service_msg.go @@ -0,0 +1,58 @@ +package types + +import ( + "github.com/gogo/protobuf/proto" +) + +// MsgRequest is the interface a transaction message, defined as a proto +// service method, must fulfill. +type MsgRequest interface { + proto.Message + // ValidateBasic does a simple validation check that + // doesn't require access to any other information. + ValidateBasic() error + // Signers returns the addrs of signers that must sign. + // CONTRACT: All signatures must be present to be valid. + // CONTRACT: Returns addrs in some deterministic order. + GetSigners() []AccAddress +} + +// ServiceMsg is the struct into which an Any whose typeUrl matches a service +// method format (ex. `/cosmos.gov.Msg/SubmitProposal`) unpacks. +type ServiceMsg struct { + // MethodName is the fully-qualified service method name. + MethodName string + // Request is the request payload. + Request MsgRequest +} + +var _ Msg = ServiceMsg{} + +func (msg ServiceMsg) ProtoMessage() {} +func (msg ServiceMsg) Reset() {} +func (msg ServiceMsg) String() string { return "ServiceMsg" } + +// Route implements Msg.Route method. +func (msg ServiceMsg) Route() string { + return msg.MethodName +} + +// ValidateBasic implements Msg.ValidateBasic method. +func (msg ServiceMsg) ValidateBasic() error { + return msg.Request.ValidateBasic() +} + +// GetSignBytes implements Msg.GetSignBytes method. +func (msg ServiceMsg) GetSignBytes() []byte { + panic("ServiceMsg does not have a GetSignBytes method") +} + +// GetSigners implements Msg.GetSigners method. +func (msg ServiceMsg) GetSigners() []AccAddress { + return msg.Request.GetSigners() +} + +// Type implements Msg.Type method. +func (msg ServiceMsg) Type() string { + return msg.MethodName +} diff --git a/types/simulation/account.go b/types/simulation/account.go new file mode 100644 index 000000000000..5bc5bfe14dc6 --- /dev/null +++ b/types/simulation/account.go @@ -0,0 +1,97 @@ +package simulation + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Account contains a privkey, pubkey, address tuple +// eventually more useful data can be placed in here. +// (e.g. number of coins) +type Account struct { + PrivKey cryptotypes.PrivKey + PubKey cryptotypes.PubKey + Address sdk.AccAddress + ConsKey cryptotypes.PrivKey +} + +// Equals returns true if two accounts are equal +func (acc Account) Equals(acc2 Account) bool { + return acc.Address.Equals(acc2.Address) +} + +// RandomAcc picks and returns a random account from an array and returs its +// position in the array. +func RandomAcc(r *rand.Rand, accs []Account) (Account, int) { + idx := r.Intn(len(accs)) + return accs[idx], idx +} + +// RandomAccounts generates n random accounts +func RandomAccounts(r *rand.Rand, n int) []Account { + accs := make([]Account, n) + + for i := 0; i < n; i++ { + // don't need that much entropy for simulation + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + + accs[i].PrivKey = secp256k1.GenPrivKeyFromSecret(privkeySeed) + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + + accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + + return accs +} + +// FindAccount iterates over all the simulation accounts to find the one that matches +// the given address +func FindAccount(accs []Account, address sdk.Address) (Account, bool) { + for _, acc := range accs { + if acc.Address.Equals(address) { + return acc, true + } + } + + return Account{}, false +} + +// RandomFees returns a random fee by selecting a random coin denomination and +// amount from the account's available balance. If the user doesn't have enough +// funds for paying fees, it returns empty coins. +func RandomFees(r *rand.Rand, ctx sdk.Context, spendableCoins sdk.Coins) (sdk.Coins, error) { + if spendableCoins.Empty() { + return nil, nil + } + + perm := r.Perm(len(spendableCoins)) + var randCoin sdk.Coin + for _, index := range perm { + randCoin = spendableCoins[index] + if !randCoin.Amount.IsZero() { + break + } + } + + if randCoin.Amount.IsZero() { + return nil, fmt.Errorf("no coins found for random fees") + } + + amt, err := RandPositiveInt(r, randCoin.Amount) + if err != nil { + return nil, err + } + + // Create a random fee and verify the fees are within the account's spendable + // balance. + fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt)) + + return fees, nil +} diff --git a/types/simulation/account_test.go b/types/simulation/account_test.go new file mode 100644 index 000000000000..85034a0d24e2 --- /dev/null +++ b/types/simulation/account_test.go @@ -0,0 +1,79 @@ +package simulation_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/simulation" +) + +func TestRandomAccounts(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(time.Now().Unix())) + tests := []struct { + name string + n int + want int + }{ + {"0-accounts", 0, 0}, + {"1-accounts", 1, 1}, + {"100-accounts", 100, 100}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got := simulation.RandomAccounts(r, tt.n) + require.Equal(t, tt.want, len(got)) + if tt.n == 0 { + return + } + acc, i := simulation.RandomAcc(r, got) + require.True(t, acc.Equals(got[i])) + accFound, found := simulation.FindAccount(got, acc.Address) + require.True(t, found) + require.True(t, accFound.Equals(acc)) + }) + } +} + +func TestFindAccountEmptySlice(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(time.Now().Unix())) + accs := simulation.RandomAccounts(r, 1) + require.Equal(t, 1, len(accs)) + acc, found := simulation.FindAccount(nil, accs[0].Address) + require.False(t, found) + require.Nil(t, acc.Address) + require.Nil(t, acc.PrivKey) + require.Nil(t, acc.PubKey) +} + +func TestRandomFees(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(time.Now().Unix())) + tests := []struct { + name string + spendableCoins sdk.Coins + wantEmpty bool + wantErr bool + }{ + {"0 coins", sdk.Coins{}, true, false}, + {"0 coins", sdk.NewCoins(sdk.NewInt64Coin("aaa", 10), sdk.NewInt64Coin("bbb", 5)), false, false}, + } + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + got, err := simulation.RandomFees(r, sdk.Context{}, tt.spendableCoins) + if (err != nil) != tt.wantErr { + t.Errorf("RandomFees() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.Equal(t, tt.wantEmpty, got.Empty()) + }) + } +} diff --git a/x/simulation/config.go b/types/simulation/config.go similarity index 100% rename from x/simulation/config.go rename to types/simulation/config.go diff --git a/x/simulation/rand_util.go b/types/simulation/rand_util.go similarity index 95% rename from x/simulation/rand_util.go rename to types/simulation/rand_util.go index b494b0fb8aab..84cd4492c8a7 100644 --- a/x/simulation/rand_util.go +++ b/types/simulation/rand_util.go @@ -5,6 +5,7 @@ import ( "math/big" "math/rand" "time" + "unsafe" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -34,7 +35,8 @@ func RandStringOfLength(r *rand.Rand, n int) string { cache >>= letterIdxBits remain-- } - return string(b) + + return *(*string)(unsafe.Pointer(&b)) } // RandPositiveInt get a rand positive sdk.Int @@ -42,7 +44,9 @@ func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { if !max.GTE(sdk.OneInt()) { return sdk.Int{}, errors.New("max too small") } + max = max.Sub(sdk.OneInt()) + return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil } @@ -50,6 +54,7 @@ func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) { // Note: The range of RandomAmount includes max, and is, in fact, biased to return max as well as 0. func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { var randInt = big.NewInt(0) + switch r.Intn(10) { case 0: // randInt = big.NewInt(0) @@ -58,6 +63,7 @@ func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { default: // NOTE: there are 10 total cases. randInt = big.NewInt(0).Rand(r, max.BigInt()) // up to max - 1 } + return sdk.NewIntFromBigInt(randInt) } @@ -65,14 +71,16 @@ func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { // Note: The range of RandomDecAmount includes max, and is, in fact, biased to return max as well as 0. func RandomDecAmount(r *rand.Rand, max sdk.Dec) sdk.Dec { var randInt = big.NewInt(0) + switch r.Intn(10) { case 0: // randInt = big.NewInt(0) case 1: - randInt = max.Int // the underlying big int with all precision bits. + randInt = max.BigInt() // the underlying big int with all precision bits. default: // NOTE: there are 10 total cases. - randInt = big.NewInt(0).Rand(r, max.Int) + randInt = big.NewInt(0).Rand(r, max.BigInt()) } + return sdk.NewDecFromBigIntWithPrec(randInt, sdk.Precision) } @@ -103,7 +111,9 @@ func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins { if err != nil { return sdk.Coins{} } + subset := sdk.Coins{sdk.NewCoin(coin.Denom, amt)} + for i, c := range coins { // skip denom that we already chose earlier if i == denomIdx { @@ -120,8 +130,10 @@ func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins { if err != nil { continue } + subset = append(subset, sdk.NewCoin(c.Denom, amt)) } + return subset.Sort() } @@ -133,9 +145,11 @@ func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins { func DeriveRand(r *rand.Rand) *rand.Rand { const num = 8 // TODO what's a good number? Too large is too slow. ms := multiSource(make([]rand.Source, num)) + for i := 0; i < num; i++ { ms[i] = rand.NewSource(r.Int63()) } + return rand.New(ms) } @@ -145,6 +159,7 @@ func (ms multiSource) Int63() (r int64) { for _, source := range ms { r ^= source.Int63() } + return r } diff --git a/x/simulation/rand_util_test.go b/types/simulation/rand_util_test.go similarity index 89% rename from x/simulation/rand_util_test.go rename to types/simulation/rand_util_test.go index 2128de4050aa..c487625ed042 100644 --- a/x/simulation/rand_util_test.go +++ b/types/simulation/rand_util_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" ) func TestRandSubsetCoins(t *testing.T) { @@ -43,9 +43,11 @@ func TestRandStringOfLength(t *testing.T) { }{ {"0-size", 0, 0}, {"10-size", 10, 10}, - {"10-size", 1_000_000_000, 1_000_000_000}, + {"1_000_000-size", 1_000_000, 1_000_000}, } for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { got := simulation.RandStringOfLength(r, tt.n) require.Equal(t, tt.want, len(got)) @@ -54,7 +56,7 @@ func TestRandStringOfLength(t *testing.T) { } func mustParseCoins(s string) sdk.Coins { - coins, err := sdk.ParseCoins(s) + coins, err := sdk.ParseCoinsNormalized(s) if err != nil { panic(err) } diff --git a/types/simulation/transition_matrix.go b/types/simulation/transition_matrix.go new file mode 100644 index 000000000000..f2759df3dee9 --- /dev/null +++ b/types/simulation/transition_matrix.go @@ -0,0 +1,12 @@ +package simulation + +import "math/rand" + +// TransitionMatrix is _almost_ a left stochastic matrix. It is technically +// not one due to not normalizing the column values. In the future, if we want +// to find the steady state distribution, it will be quite easy to normalize +// these values to get a stochastic matrix. Floats aren't currently used as +// the default due to non-determinism across architectures +type TransitionMatrix interface { + NextState(r *rand.Rand, i int) int +} diff --git a/types/simulation/types.go b/types/simulation/types.go new file mode 100644 index 000000000000..f541b1d764de --- /dev/null +++ b/types/simulation/types.go @@ -0,0 +1,169 @@ +package simulation + +import ( + "encoding/json" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type WeightedProposalContent interface { + AppParamsKey() string // key used to retrieve the value of the weight from the simulation application params + DefaultWeight() int // default weight + ContentSimulatorFn() ContentSimulatorFn // content simulator function +} + +type ContentSimulatorFn func(r *rand.Rand, ctx sdk.Context, accs []Account) Content + +type Content interface { + GetTitle() string + GetDescription() string + ProposalRoute() string + ProposalType() string + ValidateBasic() error + String() string +} + +type SimValFn func(r *rand.Rand) string + +type ParamChange interface { + Subspace() string + Key() string + SimValue() SimValFn + ComposedKey() string +} + +type WeightedOperation interface { + Weight() int + Op() Operation +} + +// Operation runs a state machine transition, and ensures the transition +// happened as expected. The operation could be running and testing a fuzzed +// transaction, or doing the same for a message. +// +// For ease of debugging, an operation returns a descriptive message "action", +// which details what this fuzzed state machine transition actually did. +// +// Operations can optionally provide a list of "FutureOperations" to run later +// These will be ran at the beginning of the corresponding block. +type Operation func(r *rand.Rand, app *baseapp.BaseApp, + ctx sdk.Context, accounts []Account, chainID string) ( + OperationMsg OperationMsg, futureOps []FutureOperation, err error) + +// OperationMsg - structure for operation output +type OperationMsg struct { + Route string `json:"route" yaml:"route"` // msg route (i.e module name) + Name string `json:"name" yaml:"name"` // operation name (msg Type or "no-operation") + Comment string `json:"comment" yaml:"comment"` // additional comment + OK bool `json:"ok" yaml:"ok"` // success + Msg json.RawMessage `json:"msg" yaml:"msg"` // JSON encoded msg +} + +// NewOperationMsgBasic creates a new operation message from raw input. +func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) OperationMsg { + return OperationMsg{ + Route: route, + Name: name, + Comment: comment, + OK: ok, + Msg: msg, + } +} + +// NewOperationMsg - create a new operation message from sdk.Msg +func NewOperationMsg(msg sdk.Msg, ok bool, comment string) OperationMsg { + return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes()) +} + +// NoOpMsg - create a no-operation message +func NoOpMsg(route, msgType, comment string) OperationMsg { + return NewOperationMsgBasic(route, msgType, comment, false, nil) +} + +// log entry text for this operation msg +func (om OperationMsg) String() string { + out, err := json.Marshal(om) + if err != nil { + panic(err) + } + + return string(out) +} + +// MustMarshal Marshals the operation msg, panic on error +func (om OperationMsg) MustMarshal() json.RawMessage { + out, err := json.Marshal(om) + if err != nil { + panic(err) + } + + return out +} + +// LogEvent adds an event for the events stats +func (om OperationMsg) LogEvent(eventLogger func(route, op, evResult string)) { + pass := "ok" + if !om.OK { + pass = "failure" + } + + eventLogger(om.Route, om.Name, pass) +} + +//________________________________________________________________________ + +// FutureOperation is an operation which will be ran at the beginning of the +// provided BlockHeight. If both a BlockHeight and BlockTime are specified, it +// will use the BlockHeight. In the (likely) event that multiple operations +// are queued at the same block height, they will execute in a FIFO pattern. +type FutureOperation struct { + BlockHeight int + BlockTime time.Time + Op Operation +} + +// AppParams defines a flat JSON of key/values for all possible configurable +// simulation parameters. It might contain: operation weights, simulation parameters +// and flattened module state parameters (i.e not stored under it's respective module name). +type AppParams map[string]json.RawMessage + +// GetOrGenerate attempts to get a given parameter by key from the AppParams +// object. If it exists, it'll be decoded and returned. Otherwise, the provided +// ParamSimulator is used to generate a random value or default value (eg: in the +// case of operation weights where Rand is not used). +func (sp AppParams) GetOrGenerate(_ codec.JSONMarshaler, key string, ptr interface{}, r *rand.Rand, ps ParamSimulator) { + if v, ok := sp[key]; ok && v != nil { + err := json.Unmarshal(v, ptr) + if err != nil { + panic(err) + } + return + } + + ps(r) +} + +type ParamSimulator func(r *rand.Rand) + +type SelectOpFn func(r *rand.Rand) Operation + +// AppStateFn returns the app state json bytes and the genesis accounts +type AppStateFn func(r *rand.Rand, accs []Account, config Config) ( + appState json.RawMessage, accounts []Account, chainId string, genesisTimestamp time.Time, +) + +// RandomAccountFn returns a slice of n random simulation accounts +type RandomAccountFn func(r *rand.Rand, n int) []Account + +type Params interface { + PastEvidenceFraction() float64 + NumKeys() int + EvidenceFraction() float64 + InitialLivenessWeightings() []int + LivenessTransitionMatrix() TransitionMatrix + BlockSizeTransitionMatrix() TransitionMatrix +} diff --git a/types/staking.go b/types/staking.go index af76a2dd24ce..0753d808b2e8 100644 --- a/types/staking.go +++ b/types/staking.go @@ -23,7 +23,7 @@ const ( ) // PowerReduction is the amount of staking tokens required for 1 unit of consensus-engine power -var PowerReduction = NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) +var PowerReduction = NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(6), nil)) // TokensToConsensusPower - convert input tokens to potential consensus-engine power func TokensToConsensusPower(tokens Int) int64 { @@ -34,36 +34,3 @@ func TokensToConsensusPower(tokens Int) int64 { func TokensFromConsensusPower(power int64) Int { return NewInt(power).Mul(PowerReduction) } - -// BondStatus is the status of a validator -type BondStatus byte - -// staking constants -const ( - Unbonded BondStatus = 0x00 - Unbonding BondStatus = 0x01 - Bonded BondStatus = 0x02 - - BondStatusUnbonded = "Unbonded" - BondStatusUnbonding = "Unbonding" - BondStatusBonded = "Bonded" -) - -// Equal compares two BondStatus instances -func (b BondStatus) Equal(b2 BondStatus) bool { - return byte(b) == byte(b2) -} - -// String implements the Stringer interface for BondStatus. -func (b BondStatus) String() string { - switch b { - case 0x00: - return BondStatusUnbonded - case 0x01: - return BondStatusUnbonding - case 0x02: - return BondStatusBonded - default: - panic("invalid bond status") - } -} diff --git a/types/staking_test.go b/types/staking_test.go new file mode 100644 index 000000000000..e307230cd990 --- /dev/null +++ b/types/staking_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type stakingTestSuite struct { + suite.Suite +} + +func TestStakingTestSuite(t *testing.T) { + suite.Run(t, new(stakingTestSuite)) +} + +func (s *stakingTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *stakingTestSuite) TestTokensToConsensusPower() { + s.Require().Equal(int64(0), sdk.TokensToConsensusPower(sdk.NewInt(999_999))) + s.Require().Equal(int64(1), sdk.TokensToConsensusPower(sdk.NewInt(1_000_000))) +} diff --git a/types/store.go b/types/store.go index ce890f2f6f75..bd1d1ec4f8e3 100644 --- a/types/store.go +++ b/types/store.go @@ -1,18 +1,14 @@ package types import ( - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types/kv" ) -// nolint - reexport type ( PruningOptions = types.PruningOptions ) -// nolint - reexport type ( Store = types.Store Committer = types.Committer @@ -28,7 +24,7 @@ type ( // StoreDecoderRegistry defines each of the modules store decoders. Used for ImportExport // simulation. -type StoreDecoderRegistry map[string]func(cdc *codec.Codec, kvA, kvB tmkv.Pair) string +type StoreDecoderRegistry map[string]func(kvA, kvB kv.Pair) string // Iterator over all the keys with a certain prefix in ascending order func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { @@ -49,16 +45,15 @@ func KVStorePrefixIteratorPaginated(kvs KVStore, prefix []byte, page, limit uint // KVStoreReversePrefixIteratorPaginated returns iterator over items in the selected page. // Items iterated and skipped in descending order. func KVStoreReversePrefixIteratorPaginated(kvs KVStore, prefix []byte, page, limit uint) Iterator { - return types.KVStorePrefixIteratorPaginated(kvs, prefix, page, limit) + return types.KVStoreReversePrefixIteratorPaginated(kvs, prefix, page, limit) } // DiffKVStores compares two KVstores and returns all the key/value pairs // that differ from one another. It also skips value comparison for a set of provided prefixes -func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []tmkv.Pair) { +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvAs, kvBs []kv.Pair) { return types.DiffKVStores(a, b, prefixesToSkip) } -// nolint - reexport type ( CacheKVStore = types.CacheKVStore CommitKVStore = types.CommitKVStore @@ -67,22 +62,22 @@ type ( CommitID = types.CommitID ) -// nolint - reexport type StoreType = types.StoreType -// nolint - reexport const ( StoreTypeMulti = types.StoreTypeMulti StoreTypeDB = types.StoreTypeDB StoreTypeIAVL = types.StoreTypeIAVL StoreTypeTransient = types.StoreTypeTransient + StoreTypeMemory = types.StoreTypeMemory ) -// nolint - reexport type ( StoreKey = types.StoreKey + CapabilityKey = types.CapabilityKey KVStoreKey = types.KVStoreKey TransientStoreKey = types.TransientStoreKey + MemoryStoreKey = types.MemoryStoreKey ) // NewKVStoreKey returns a new pointer to a KVStoreKey. @@ -98,6 +93,7 @@ func NewKVStoreKeys(names ...string) map[string]*KVStoreKey { for _, name := range names { keys[name] = NewKVStoreKey(name) } + return keys } @@ -114,6 +110,18 @@ func NewTransientStoreKeys(names ...string) map[string]*TransientStoreKey { for _, name := range names { keys[name] = NewTransientStoreKey(name) } + + return keys +} + +// NewMemoryStoreKeys constructs a new map matching store key names to their +// respective MemoryStoreKey references. +func NewMemoryStoreKeys(names ...string) map[string]*MemoryStoreKey { + keys := make(map[string]*MemoryStoreKey) + for _, name := range names { + keys[name] = types.NewMemoryStoreKey(name) + } + return keys } @@ -143,25 +151,21 @@ type TraceContext = types.TraceContext // -------------------------------------- -// nolint - reexport type ( Gas = types.Gas GasMeter = types.GasMeter GasConfig = types.GasConfig ) -// nolint - reexport func NewGasMeter(limit Gas) GasMeter { return types.NewGasMeter(limit) } -// nolint - reexport type ( ErrorOutOfGas = types.ErrorOutOfGas ErrorGasOverflow = types.ErrorGasOverflow ) -// nolint - reexport func NewInfiniteGasMeter() GasMeter { return types.NewInfiniteGasMeter() } diff --git a/types/store_test.go b/types/store_test.go index 06f609d17471..02f71bbf4138 100644 --- a/types/store_test.go +++ b/types/store_test.go @@ -1,12 +1,29 @@ -package types +package types_test import ( "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestPrefixEndBytes(t *testing.T) { +type storeTestSuite struct { + suite.Suite +} + +func TestStoreTestSuite(t *testing.T) { + suite.Run(t, new(storeTestSuite)) +} + +func (s *storeTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *storeTestSuite) TestPrefixEndBytes() { var testCases = []struct { prefix []byte expected []byte @@ -21,18 +38,94 @@ func TestPrefixEndBytes(t *testing.T) { } for _, test := range testCases { - end := PrefixEndBytes(test.prefix) - require.Equal(t, test.expected, end) + end := sdk.PrefixEndBytes(test.prefix) + s.Require().Equal(test.expected, end) } } -func TestCommitID(t *testing.T) { - var empty CommitID - require.True(t, empty.IsZero()) +func (s *storeTestSuite) TestCommitID() { + var empty sdk.CommitID + s.Require().True(empty.IsZero()) - var nonempty = CommitID{ + var nonempty = sdk.CommitID{ Version: 1, Hash: []byte("testhash"), } - require.False(t, nonempty.IsZero()) + s.Require().False(nonempty.IsZero()) +} + +func (s *storeTestSuite) TestNewKVStoreKeys() { + s.Require().Equal(map[string]*sdk.KVStoreKey{}, sdk.NewKVStoreKeys()) + s.Require().Equal(1, len(sdk.NewKVStoreKeys("one"))) +} + +func (s *storeTestSuite) TestNewTransientStoreKeys() { + s.Require().Equal(map[string]*sdk.TransientStoreKey{}, sdk.NewTransientStoreKeys()) + s.Require().Equal(1, len(sdk.NewTransientStoreKeys("one"))) +} + +func (s *storeTestSuite) TestNewInfiniteGasMeter() { + gm := sdk.NewInfiniteGasMeter() + s.Require().NotNil(gm) + _, ok := gm.(types.GasMeter) + s.Require().True(ok) +} + +func (s *storeTestSuite) TestStoreTypes() { + s.Require().Equal(sdk.InclusiveEndBytes([]byte("endbytes")), types.InclusiveEndBytes([]byte("endbytes"))) +} + +func (s *storeTestSuite) TestDiffKVStores() { + store1, store2 := s.initTestStores() + // Two equal stores + k1, v1 := []byte("k1"), []byte("v1") + store1.Set(k1, v1) + store2.Set(k1, v1) + + s.checkDiffResults(store1, store2) + + // delete k1 from store2, which is now empty + store2.Delete(k1) + s.checkDiffResults(store1, store2) + + // set k1 in store2, different value than what store1 holds for k1 + v2 := []byte("v2") + store2.Set(k1, v2) + s.checkDiffResults(store1, store2) + + // add k2 to store2 + k2 := []byte("k2") + store2.Set(k2, v2) + s.checkDiffResults(store1, store2) + + // Reset stores + store1.Delete(k1) + store2.Delete(k1) + store2.Delete(k2) + + // Same keys, different value. Comparisons will be nil as prefixes are skipped. + prefix := []byte("prefix:") + k1Prefixed := append(prefix, k1...) + store1.Set(k1Prefixed, v1) + store2.Set(k1Prefixed, v2) + s.checkDiffResults(store1, store2) +} + +func (s *storeTestSuite) initTestStores() (types.KVStore, types.KVStore) { + db := dbm.NewMemDB() + ms := rootmulti.NewStore(db) + + key1 := types.NewKVStoreKey("store1") + key2 := types.NewKVStoreKey("store2") + s.Require().NotPanics(func() { ms.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + s.Require().NotPanics(func() { ms.MountStoreWithDB(key2, types.StoreTypeIAVL, db) }) + s.Require().NoError(ms.LoadLatestVersion()) + return ms.GetKVStore(key1), ms.GetKVStore(key2) +} + +func (s *storeTestSuite) checkDiffResults(store1, store2 types.KVStore) { + kvAs1, kvBs1 := sdk.DiffKVStores(store1, store2, nil) + kvAs2, kvBs2 := types.DiffKVStores(store1, store2, nil) + s.Require().Equal(kvAs1, kvAs2) + s.Require().Equal(kvBs1, kvBs2) } diff --git a/types/tx/service.pb.go b/types/tx/service.pb.go new file mode 100644 index 000000000000..f0eabcaca9a4 --- /dev/null +++ b/types/tx/service.pb.go @@ -0,0 +1,2292 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/tx/v1beta1/service.proto + +package tx + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/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" + golang_proto "github.com/golang/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 _ = golang_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 + +// OrderBy defines the sorting order +type OrderBy int32 + +const ( + // ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults to ASC in this case. + OrderBy_ORDER_BY_UNSPECIFIED OrderBy = 0 + // ORDER_BY_ASC defines ascending order + OrderBy_ORDER_BY_ASC OrderBy = 1 + // ORDER_BY_DESC defines descending order + OrderBy_ORDER_BY_DESC OrderBy = 2 +) + +var OrderBy_name = map[int32]string{ + 0: "ORDER_BY_UNSPECIFIED", + 1: "ORDER_BY_ASC", + 2: "ORDER_BY_DESC", +} + +var OrderBy_value = map[string]int32{ + "ORDER_BY_UNSPECIFIED": 0, + "ORDER_BY_ASC": 1, + "ORDER_BY_DESC": 2, +} + +func (x OrderBy) String() string { + return proto.EnumName(OrderBy_name, int32(x)) +} + +func (OrderBy) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{0} +} + +// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +type BroadcastMode int32 + +const ( + // zero-value for mode ordering + BroadcastMode_BROADCAST_MODE_UNSPECIFIED BroadcastMode = 0 + // BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BroadcastMode_BROADCAST_MODE_BLOCK BroadcastMode = 1 + // BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BroadcastMode_BROADCAST_MODE_SYNC BroadcastMode = 2 + // BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + // immediately. + BroadcastMode_BROADCAST_MODE_ASYNC BroadcastMode = 3 +) + +var BroadcastMode_name = map[int32]string{ + 0: "BROADCAST_MODE_UNSPECIFIED", + 1: "BROADCAST_MODE_BLOCK", + 2: "BROADCAST_MODE_SYNC", + 3: "BROADCAST_MODE_ASYNC", +} + +var BroadcastMode_value = map[string]int32{ + "BROADCAST_MODE_UNSPECIFIED": 0, + "BROADCAST_MODE_BLOCK": 1, + "BROADCAST_MODE_SYNC": 2, + "BROADCAST_MODE_ASYNC": 3, +} + +func (x BroadcastMode) String() string { + return proto.EnumName(BroadcastMode_name, int32(x)) +} + +func (BroadcastMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{1} +} + +// GetTxsEventRequest is the request type for the Service.TxsByEvents +// RPC method. +type GetTxsEventRequest struct { + // events is the list of transaction event type. + Events []string `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + // pagination defines an pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + OrderBy OrderBy `protobuf:"varint,3,opt,name=order_by,json=orderBy,proto3,enum=cosmos.tx.v1beta1.OrderBy" json:"order_by,omitempty"` +} + +func (m *GetTxsEventRequest) Reset() { *m = GetTxsEventRequest{} } +func (m *GetTxsEventRequest) String() string { return proto.CompactTextString(m) } +func (*GetTxsEventRequest) ProtoMessage() {} +func (*GetTxsEventRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{0} +} +func (m *GetTxsEventRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxsEventRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxsEventRequest.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 *GetTxsEventRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxsEventRequest.Merge(m, src) +} +func (m *GetTxsEventRequest) XXX_Size() int { + return m.Size() +} +func (m *GetTxsEventRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxsEventRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxsEventRequest proto.InternalMessageInfo + +func (m *GetTxsEventRequest) GetEvents() []string { + if m != nil { + return m.Events + } + return nil +} + +func (m *GetTxsEventRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *GetTxsEventRequest) GetOrderBy() OrderBy { + if m != nil { + return m.OrderBy + } + return OrderBy_ORDER_BY_UNSPECIFIED +} + +// GetTxsEventResponse is the response type for the Service.TxsByEvents +// RPC method. +type GetTxsEventResponse struct { + // txs is the list of queried transactions. + Txs []*Tx `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` + // tx_responses is the list of queried TxResponses. + TxResponses []*types.TxResponse `protobuf:"bytes,2,rep,name=tx_responses,json=txResponses,proto3" json:"tx_responses,omitempty"` + // pagination defines an pagination for the response. + Pagination *query.PageResponse `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *GetTxsEventResponse) Reset() { *m = GetTxsEventResponse{} } +func (m *GetTxsEventResponse) String() string { return proto.CompactTextString(m) } +func (*GetTxsEventResponse) ProtoMessage() {} +func (*GetTxsEventResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{1} +} +func (m *GetTxsEventResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxsEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxsEventResponse.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 *GetTxsEventResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxsEventResponse.Merge(m, src) +} +func (m *GetTxsEventResponse) XXX_Size() int { + return m.Size() +} +func (m *GetTxsEventResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxsEventResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxsEventResponse proto.InternalMessageInfo + +func (m *GetTxsEventResponse) GetTxs() []*Tx { + if m != nil { + return m.Txs + } + return nil +} + +func (m *GetTxsEventResponse) GetTxResponses() []*types.TxResponse { + if m != nil { + return m.TxResponses + } + return nil +} + +func (m *GetTxsEventResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +// RPC method. +type BroadcastTxRequest struct { + // tx_bytes is the raw transaction. + TxBytes []byte `protobuf:"bytes,1,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` + Mode BroadcastMode `protobuf:"varint,2,opt,name=mode,proto3,enum=cosmos.tx.v1beta1.BroadcastMode" json:"mode,omitempty"` +} + +func (m *BroadcastTxRequest) Reset() { *m = BroadcastTxRequest{} } +func (m *BroadcastTxRequest) String() string { return proto.CompactTextString(m) } +func (*BroadcastTxRequest) ProtoMessage() {} +func (*BroadcastTxRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{2} +} +func (m *BroadcastTxRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BroadcastTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BroadcastTxRequest.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 *BroadcastTxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_BroadcastTxRequest.Merge(m, src) +} +func (m *BroadcastTxRequest) XXX_Size() int { + return m.Size() +} +func (m *BroadcastTxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_BroadcastTxRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_BroadcastTxRequest proto.InternalMessageInfo + +func (m *BroadcastTxRequest) GetTxBytes() []byte { + if m != nil { + return m.TxBytes + } + return nil +} + +func (m *BroadcastTxRequest) GetMode() BroadcastMode { + if m != nil { + return m.Mode + } + return BroadcastMode_BROADCAST_MODE_UNSPECIFIED +} + +// BroadcastTxResponse is the response type for the +// Service.BroadcastTx method. +type BroadcastTxResponse struct { + // tx_response is the queried TxResponses. + TxResponse *types.TxResponse `protobuf:"bytes,1,opt,name=tx_response,json=txResponse,proto3" json:"tx_response,omitempty"` +} + +func (m *BroadcastTxResponse) Reset() { *m = BroadcastTxResponse{} } +func (m *BroadcastTxResponse) String() string { return proto.CompactTextString(m) } +func (*BroadcastTxResponse) ProtoMessage() {} +func (*BroadcastTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{3} +} +func (m *BroadcastTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BroadcastTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BroadcastTxResponse.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 *BroadcastTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_BroadcastTxResponse.Merge(m, src) +} +func (m *BroadcastTxResponse) XXX_Size() int { + return m.Size() +} +func (m *BroadcastTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_BroadcastTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_BroadcastTxResponse proto.InternalMessageInfo + +func (m *BroadcastTxResponse) GetTxResponse() *types.TxResponse { + if m != nil { + return m.TxResponse + } + return nil +} + +// SimulateRequest is the request type for the Service.Simulate +// RPC method. +type SimulateRequest struct { + // tx is the transaction to simulate. + Tx *Tx `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` +} + +func (m *SimulateRequest) Reset() { *m = SimulateRequest{} } +func (m *SimulateRequest) String() string { return proto.CompactTextString(m) } +func (*SimulateRequest) ProtoMessage() {} +func (*SimulateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{4} +} +func (m *SimulateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SimulateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SimulateRequest.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 *SimulateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulateRequest.Merge(m, src) +} +func (m *SimulateRequest) XXX_Size() int { + return m.Size() +} +func (m *SimulateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SimulateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulateRequest proto.InternalMessageInfo + +func (m *SimulateRequest) GetTx() *Tx { + if m != nil { + return m.Tx + } + return nil +} + +// SimulateResponse is the response type for the +// Service.SimulateRPC method. +type SimulateResponse struct { + // gas_info is the information about gas used in the simulation. + GasInfo *types.GasInfo `protobuf:"bytes,1,opt,name=gas_info,json=gasInfo,proto3" json:"gas_info,omitempty"` + // result is the result of the simulation. + Result *types.Result `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` +} + +func (m *SimulateResponse) Reset() { *m = SimulateResponse{} } +func (m *SimulateResponse) String() string { return proto.CompactTextString(m) } +func (*SimulateResponse) ProtoMessage() {} +func (*SimulateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{5} +} +func (m *SimulateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SimulateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SimulateResponse.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 *SimulateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulateResponse.Merge(m, src) +} +func (m *SimulateResponse) XXX_Size() int { + return m.Size() +} +func (m *SimulateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SimulateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulateResponse proto.InternalMessageInfo + +func (m *SimulateResponse) GetGasInfo() *types.GasInfo { + if m != nil { + return m.GasInfo + } + return nil +} + +func (m *SimulateResponse) GetResult() *types.Result { + if m != nil { + return m.Result + } + return nil +} + +// GetTxRequest is the request type for the Service.GetTx +// RPC method. +type GetTxRequest struct { + // hash is the tx hash to query, encoded as a hex string. + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *GetTxRequest) Reset() { *m = GetTxRequest{} } +func (m *GetTxRequest) String() string { return proto.CompactTextString(m) } +func (*GetTxRequest) ProtoMessage() {} +func (*GetTxRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{6} +} +func (m *GetTxRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxRequest.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 *GetTxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxRequest.Merge(m, src) +} +func (m *GetTxRequest) XXX_Size() int { + return m.Size() +} +func (m *GetTxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxRequest proto.InternalMessageInfo + +func (m *GetTxRequest) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +// GetTxResponse is the response type for the Service.GetTx method. +type GetTxResponse struct { + // tx is the queried transaction. + Tx *Tx `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` + // tx_response is the queried TxResponses. + TxResponse *types.TxResponse `protobuf:"bytes,2,opt,name=tx_response,json=txResponse,proto3" json:"tx_response,omitempty"` +} + +func (m *GetTxResponse) Reset() { *m = GetTxResponse{} } +func (m *GetTxResponse) String() string { return proto.CompactTextString(m) } +func (*GetTxResponse) ProtoMessage() {} +func (*GetTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{7} +} +func (m *GetTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxResponse.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 *GetTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxResponse.Merge(m, src) +} +func (m *GetTxResponse) XXX_Size() int { + return m.Size() +} +func (m *GetTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxResponse proto.InternalMessageInfo + +func (m *GetTxResponse) GetTx() *Tx { + if m != nil { + return m.Tx + } + return nil +} + +func (m *GetTxResponse) GetTxResponse() *types.TxResponse { + if m != nil { + return m.TxResponse + } + return nil +} + +func init() { + proto.RegisterEnum("cosmos.tx.v1beta1.OrderBy", OrderBy_name, OrderBy_value) + golang_proto.RegisterEnum("cosmos.tx.v1beta1.OrderBy", OrderBy_name, OrderBy_value) + proto.RegisterEnum("cosmos.tx.v1beta1.BroadcastMode", BroadcastMode_name, BroadcastMode_value) + golang_proto.RegisterEnum("cosmos.tx.v1beta1.BroadcastMode", BroadcastMode_name, BroadcastMode_value) + proto.RegisterType((*GetTxsEventRequest)(nil), "cosmos.tx.v1beta1.GetTxsEventRequest") + golang_proto.RegisterType((*GetTxsEventRequest)(nil), "cosmos.tx.v1beta1.GetTxsEventRequest") + proto.RegisterType((*GetTxsEventResponse)(nil), "cosmos.tx.v1beta1.GetTxsEventResponse") + golang_proto.RegisterType((*GetTxsEventResponse)(nil), "cosmos.tx.v1beta1.GetTxsEventResponse") + proto.RegisterType((*BroadcastTxRequest)(nil), "cosmos.tx.v1beta1.BroadcastTxRequest") + golang_proto.RegisterType((*BroadcastTxRequest)(nil), "cosmos.tx.v1beta1.BroadcastTxRequest") + proto.RegisterType((*BroadcastTxResponse)(nil), "cosmos.tx.v1beta1.BroadcastTxResponse") + golang_proto.RegisterType((*BroadcastTxResponse)(nil), "cosmos.tx.v1beta1.BroadcastTxResponse") + proto.RegisterType((*SimulateRequest)(nil), "cosmos.tx.v1beta1.SimulateRequest") + golang_proto.RegisterType((*SimulateRequest)(nil), "cosmos.tx.v1beta1.SimulateRequest") + proto.RegisterType((*SimulateResponse)(nil), "cosmos.tx.v1beta1.SimulateResponse") + golang_proto.RegisterType((*SimulateResponse)(nil), "cosmos.tx.v1beta1.SimulateResponse") + proto.RegisterType((*GetTxRequest)(nil), "cosmos.tx.v1beta1.GetTxRequest") + golang_proto.RegisterType((*GetTxRequest)(nil), "cosmos.tx.v1beta1.GetTxRequest") + proto.RegisterType((*GetTxResponse)(nil), "cosmos.tx.v1beta1.GetTxResponse") + golang_proto.RegisterType((*GetTxResponse)(nil), "cosmos.tx.v1beta1.GetTxResponse") +} + +func init() { proto.RegisterFile("cosmos/tx/v1beta1/service.proto", fileDescriptor_e0b00a618705eca7) } +func init() { + golang_proto.RegisterFile("cosmos/tx/v1beta1/service.proto", fileDescriptor_e0b00a618705eca7) +} + +var fileDescriptor_e0b00a618705eca7 = []byte{ + // 817 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcf, 0x6f, 0xe3, 0x44, + 0x14, 0x8e, 0x9d, 0xa5, 0xc9, 0xbe, 0xa4, 0x4b, 0x76, 0x5a, 0x96, 0x90, 0x05, 0x37, 0xeb, 0x25, + 0x6d, 0x14, 0x09, 0x5b, 0x0d, 0x20, 0x55, 0x88, 0x4b, 0x7e, 0xb5, 0x54, 0xd0, 0xa6, 0x72, 0xca, + 0xa1, 0x08, 0x29, 0x72, 0x92, 0xa9, 0x6b, 0xd1, 0x78, 0x52, 0xcf, 0xa4, 0x72, 0xd4, 0x56, 0x48, + 0x1c, 0x39, 0x21, 0xf1, 0x67, 0xf0, 0x4f, 0x70, 0xe4, 0x58, 0x89, 0x0b, 0x47, 0xd4, 0xf0, 0x47, + 0x70, 0x44, 0x1e, 0x4f, 0x12, 0x27, 0x75, 0xda, 0x8a, 0x53, 0xde, 0x64, 0xbe, 0xf7, 0xbd, 0xef, + 0x7d, 0xf3, 0x66, 0x0c, 0x1b, 0x5d, 0x42, 0xfb, 0x84, 0xea, 0xcc, 0xd3, 0x2f, 0xb7, 0x3b, 0x98, + 0x99, 0xdb, 0x3a, 0xc5, 0xee, 0xa5, 0xdd, 0xc5, 0xda, 0xc0, 0x25, 0x8c, 0xa0, 0x97, 0x01, 0x40, + 0x63, 0x9e, 0x26, 0x00, 0xb9, 0x0f, 0x2d, 0x42, 0xac, 0x73, 0xac, 0x9b, 0x03, 0x5b, 0x37, 0x1d, + 0x87, 0x30, 0x93, 0xd9, 0xc4, 0xa1, 0x41, 0x42, 0xee, 0xad, 0x60, 0xec, 0x98, 0x14, 0xeb, 0x66, + 0xa7, 0x6b, 0x4f, 0x89, 0xfd, 0x85, 0x00, 0xe5, 0xee, 0x97, 0x65, 0x9e, 0xd8, 0x5b, 0xb7, 0x88, + 0x45, 0x78, 0xa8, 0xfb, 0x91, 0xf8, 0xb7, 0x14, 0xa6, 0xbd, 0x18, 0x62, 0x77, 0x34, 0xcd, 0x1c, + 0x98, 0x96, 0xed, 0x70, 0x0d, 0x01, 0x56, 0xfd, 0x4d, 0x02, 0xb4, 0x87, 0xd9, 0xb1, 0x47, 0x1b, + 0x97, 0xd8, 0x61, 0x06, 0xbe, 0x18, 0x62, 0xca, 0xd0, 0x2b, 0x58, 0xc1, 0xfe, 0x9a, 0x66, 0xa5, + 0x7c, 0xbc, 0xf8, 0xdc, 0x10, 0x2b, 0xb4, 0x0b, 0x30, 0xa3, 0xc8, 0xca, 0x79, 0xa9, 0x98, 0x2a, + 0x6f, 0x6a, 0xa2, 0x6f, 0xbf, 0x9e, 0xc6, 0xeb, 0x4d, 0xfa, 0xd7, 0x8e, 0x4c, 0x0b, 0x0b, 0x4e, + 0x23, 0x94, 0x89, 0x3e, 0x87, 0x24, 0x71, 0x7b, 0xd8, 0x6d, 0x77, 0x46, 0xd9, 0x78, 0x5e, 0x2a, + 0xbe, 0x28, 0xe7, 0xb4, 0x7b, 0xee, 0x69, 0x4d, 0x1f, 0x52, 0x1d, 0x19, 0x09, 0x12, 0x04, 0xea, + 0xad, 0x04, 0x6b, 0x73, 0x6a, 0xe9, 0x80, 0x38, 0x14, 0xa3, 0x2d, 0x88, 0x33, 0x2f, 0xd0, 0x9a, + 0x2a, 0xbf, 0x17, 0xc1, 0x74, 0xec, 0x19, 0x3e, 0x02, 0xed, 0x41, 0x9a, 0x79, 0x6d, 0x57, 0xe4, + 0xd1, 0xac, 0xcc, 0x33, 0x3e, 0x9e, 0xeb, 0x80, 0x7b, 0x1f, 0x4a, 0x14, 0x60, 0x23, 0xc5, 0xa6, + 0xb1, 0x4f, 0x14, 0x36, 0x22, 0xce, 0x8d, 0xd8, 0x7a, 0xd4, 0x08, 0xc1, 0x14, 0x4a, 0x55, 0x31, + 0xa0, 0xaa, 0x4b, 0xcc, 0x5e, 0xd7, 0xa4, 0xcc, 0x2f, 0x16, 0xf8, 0xff, 0x01, 0x24, 0x99, 0xd7, + 0xee, 0x8c, 0x18, 0xf6, 0xbb, 0x92, 0x8a, 0x69, 0x23, 0xc1, 0xbc, 0xaa, 0xbf, 0x44, 0x9f, 0xc1, + 0xb3, 0x3e, 0xe9, 0x61, 0x6e, 0xfe, 0x8b, 0x72, 0x3e, 0xa2, 0xd9, 0x29, 0xdf, 0x01, 0xe9, 0x61, + 0x83, 0xa3, 0xd5, 0xef, 0x61, 0x6d, 0xae, 0x8c, 0x30, 0xae, 0x01, 0xa9, 0x90, 0x1f, 0xbc, 0xd4, + 0x53, 0xed, 0x80, 0x99, 0x1d, 0xea, 0x0e, 0xbc, 0xdb, 0xb2, 0xfb, 0xc3, 0x73, 0x93, 0x4d, 0x4e, + 0x1b, 0x15, 0x40, 0x66, 0x9e, 0x20, 0x5c, 0x72, 0x22, 0x32, 0xf3, 0xd4, 0x9f, 0x25, 0xc8, 0xcc, + 0x52, 0x85, 0xaa, 0x2f, 0x21, 0x69, 0x99, 0xb4, 0x6d, 0x3b, 0xa7, 0x44, 0x30, 0xbc, 0x59, 0x2e, + 0x69, 0xcf, 0xa4, 0xfb, 0xce, 0x29, 0x31, 0x12, 0x56, 0x10, 0xa0, 0x1d, 0x58, 0x71, 0x31, 0x1d, + 0x9e, 0x33, 0x31, 0x9f, 0xf9, 0xe5, 0xb9, 0x06, 0xc7, 0x19, 0x02, 0xaf, 0xaa, 0x90, 0xe6, 0xd3, + 0x35, 0xe9, 0x01, 0xc1, 0xb3, 0x33, 0x93, 0x9e, 0x71, 0x0d, 0xcf, 0x0d, 0x1e, 0xab, 0x37, 0xb0, + 0x2a, 0x30, 0x42, 0xec, 0xd3, 0x1a, 0x5d, 0x74, 0x5a, 0xfe, 0x7f, 0x4e, 0x97, 0xbe, 0x82, 0x84, + 0xb8, 0x15, 0x28, 0x0b, 0xeb, 0x4d, 0xa3, 0xde, 0x30, 0xda, 0xd5, 0x93, 0xf6, 0xb7, 0x87, 0xad, + 0xa3, 0x46, 0x6d, 0x7f, 0x77, 0xbf, 0x51, 0xcf, 0xc4, 0x50, 0x06, 0xd2, 0xd3, 0x9d, 0x4a, 0xab, + 0x96, 0x91, 0xd0, 0x4b, 0x58, 0x9d, 0xfe, 0x53, 0x6f, 0xb4, 0x6a, 0x19, 0xb9, 0x74, 0x0d, 0xab, + 0x73, 0x83, 0x82, 0x14, 0xc8, 0x55, 0x8d, 0x66, 0xa5, 0x5e, 0xab, 0xb4, 0x8e, 0xdb, 0x07, 0xcd, + 0x7a, 0x63, 0x81, 0x35, 0x0b, 0xeb, 0x0b, 0xfb, 0xd5, 0x6f, 0x9a, 0xb5, 0xaf, 0x33, 0x12, 0x7a, + 0x1f, 0xd6, 0x16, 0x76, 0x5a, 0x27, 0x87, 0xb5, 0x8c, 0x1c, 0x91, 0x52, 0xe1, 0x3b, 0xf1, 0xf2, + 0xbf, 0x71, 0x48, 0xb4, 0x82, 0xd7, 0x13, 0x5d, 0x41, 0x72, 0x32, 0x02, 0x48, 0x8d, 0x70, 0x70, + 0x61, 0xb4, 0x72, 0x6f, 0x1f, 0xc4, 0x88, 0x91, 0xdc, 0xfc, 0xe9, 0xcf, 0x7f, 0x7e, 0x95, 0xf3, + 0xea, 0x6b, 0x3d, 0xe2, 0xd9, 0x16, 0xe0, 0x2f, 0xa4, 0x12, 0xba, 0x80, 0x77, 0xf8, 0x79, 0xa2, + 0x8d, 0x08, 0xd6, 0xf0, 0x34, 0xe4, 0xf2, 0xcb, 0x01, 0xa2, 0x66, 0x81, 0xd7, 0xdc, 0x40, 0x1f, + 0xe9, 0x51, 0x6f, 0x36, 0xd5, 0xaf, 0xfc, 0x09, 0xba, 0x41, 0x3f, 0x42, 0x2a, 0x74, 0x17, 0x51, + 0xe1, 0xa1, 0x2b, 0x3c, 0x2b, 0xbf, 0xf9, 0x18, 0x4c, 0x88, 0x78, 0xc3, 0x45, 0xbc, 0x56, 0x5f, + 0x45, 0x8b, 0xf0, 0x7b, 0xbe, 0x86, 0x54, 0xe8, 0x15, 0x8d, 0x14, 0x70, 0xff, 0x9b, 0x10, 0x29, + 0x20, 0xe2, 0x31, 0x56, 0x15, 0x2e, 0x20, 0x8b, 0x96, 0x08, 0xa8, 0xd6, 0xfe, 0xb8, 0x53, 0xa4, + 0xdb, 0x3b, 0x45, 0xfa, 0xfb, 0x4e, 0x91, 0x7e, 0x19, 0x2b, 0xb1, 0xdf, 0xc7, 0x8a, 0x74, 0x3b, + 0x56, 0x62, 0x7f, 0x8d, 0x95, 0xd8, 0x77, 0x05, 0xcb, 0x66, 0x67, 0xc3, 0x8e, 0xd6, 0x25, 0xfd, + 0x49, 0x7e, 0xf0, 0xf3, 0x09, 0xed, 0xfd, 0xa0, 0xb3, 0xd1, 0x00, 0xfb, 0x84, 0x9d, 0x15, 0xfe, + 0xf9, 0xfa, 0xf4, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x64, 0xf5, 0xff, 0x95, 0x07, 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 + +// ServiceClient is the client API for Service service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ServiceClient interface { + // Simulate simulates executing a transaction for estimating gas usage. + Simulate(ctx context.Context, in *SimulateRequest, opts ...grpc.CallOption) (*SimulateResponse, error) + // GetTx fetches a tx by hash. + GetTx(ctx context.Context, in *GetTxRequest, opts ...grpc.CallOption) (*GetTxResponse, error) + // BroadcastTx broadcast transaction. + BroadcastTx(ctx context.Context, in *BroadcastTxRequest, opts ...grpc.CallOption) (*BroadcastTxResponse, error) + // GetTxsEvent fetches txs by event. + GetTxsEvent(ctx context.Context, in *GetTxsEventRequest, opts ...grpc.CallOption) (*GetTxsEventResponse, error) +} + +type serviceClient struct { + cc grpc1.ClientConn +} + +func NewServiceClient(cc grpc1.ClientConn) ServiceClient { + return &serviceClient{cc} +} + +func (c *serviceClient) Simulate(ctx context.Context, in *SimulateRequest, opts ...grpc.CallOption) (*SimulateResponse, error) { + out := new(SimulateResponse) + err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/Simulate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetTx(ctx context.Context, in *GetTxRequest, opts ...grpc.CallOption) (*GetTxResponse, error) { + out := new(GetTxResponse) + err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/GetTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) BroadcastTx(ctx context.Context, in *BroadcastTxRequest, opts ...grpc.CallOption) (*BroadcastTxResponse, error) { + out := new(BroadcastTxResponse) + err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/BroadcastTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceClient) GetTxsEvent(ctx context.Context, in *GetTxsEventRequest, opts ...grpc.CallOption) (*GetTxsEventResponse, error) { + out := new(GetTxsEventResponse) + err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/GetTxsEvent", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ServiceServer is the server API for Service service. +type ServiceServer interface { + // Simulate simulates executing a transaction for estimating gas usage. + Simulate(context.Context, *SimulateRequest) (*SimulateResponse, error) + // GetTx fetches a tx by hash. + GetTx(context.Context, *GetTxRequest) (*GetTxResponse, error) + // BroadcastTx broadcast transaction. + BroadcastTx(context.Context, *BroadcastTxRequest) (*BroadcastTxResponse, error) + // GetTxsEvent fetches txs by event. + GetTxsEvent(context.Context, *GetTxsEventRequest) (*GetTxsEventResponse, error) +} + +// UnimplementedServiceServer can be embedded to have forward compatible implementations. +type UnimplementedServiceServer struct { +} + +func (*UnimplementedServiceServer) Simulate(ctx context.Context, req *SimulateRequest) (*SimulateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Simulate not implemented") +} +func (*UnimplementedServiceServer) GetTx(ctx context.Context, req *GetTxRequest) (*GetTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTx not implemented") +} +func (*UnimplementedServiceServer) BroadcastTx(ctx context.Context, req *BroadcastTxRequest) (*BroadcastTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BroadcastTx not implemented") +} +func (*UnimplementedServiceServer) GetTxsEvent(ctx context.Context, req *GetTxsEventRequest) (*GetTxsEventResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTxsEvent not implemented") +} + +func RegisterServiceServer(s grpc1.Server, srv ServiceServer) { + s.RegisterService(&_Service_serviceDesc, srv) +} + +func _Service_Simulate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SimulateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).Simulate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.tx.v1beta1.Service/Simulate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).Simulate(ctx, req.(*SimulateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.tx.v1beta1.Service/GetTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetTx(ctx, req.(*GetTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_BroadcastTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BroadcastTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).BroadcastTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.tx.v1beta1.Service/BroadcastTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).BroadcastTx(ctx, req.(*BroadcastTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Service_GetTxsEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTxsEventRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetTxsEvent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.tx.v1beta1.Service/GetTxsEvent", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetTxsEvent(ctx, req.(*GetTxsEventRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Service_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.tx.v1beta1.Service", + HandlerType: (*ServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Simulate", + Handler: _Service_Simulate_Handler, + }, + { + MethodName: "GetTx", + Handler: _Service_GetTx_Handler, + }, + { + MethodName: "BroadcastTx", + Handler: _Service_BroadcastTx_Handler, + }, + { + MethodName: "GetTxsEvent", + Handler: _Service_GetTxsEvent_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/tx/v1beta1/service.proto", +} + +func (m *GetTxsEventRequest) 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 *GetTxsEventRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxsEventRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.OrderBy != 0 { + i = encodeVarintService(dAtA, i, uint64(m.OrderBy)) + i-- + dAtA[i] = 0x18 + } + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Events[iNdEx]) + copy(dAtA[i:], m.Events[iNdEx]) + i = encodeVarintService(dAtA, i, uint64(len(m.Events[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetTxsEventResponse) 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 *GetTxsEventResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxsEventResponse) 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 = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.TxResponses) > 0 { + for iNdEx := len(m.TxResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TxResponses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Txs) > 0 { + for iNdEx := len(m.Txs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Txs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *BroadcastTxRequest) 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 *BroadcastTxRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BroadcastTxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Mode != 0 { + i = encodeVarintService(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x10 + } + if len(m.TxBytes) > 0 { + i -= len(m.TxBytes) + copy(dAtA[i:], m.TxBytes) + i = encodeVarintService(dAtA, i, uint64(len(m.TxBytes))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BroadcastTxResponse) 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 *BroadcastTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BroadcastTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TxResponse != nil { + { + size, err := m.TxResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SimulateRequest) 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 *SimulateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SimulateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Tx != nil { + { + size, err := m.Tx.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SimulateResponse) 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 *SimulateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SimulateResponse) 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 = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.GasInfo != nil { + { + size, err := m.GasInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetTxRequest) 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 *GetTxRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintService(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetTxResponse) 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 *GetTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TxResponse != nil { + { + size, err := m.TxResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Tx != nil { + { + size, err := m.Tx.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintService(dAtA []byte, offset int, v uint64) int { + offset -= sovService(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GetTxsEventRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Events) > 0 { + for _, s := range m.Events { + l = len(s) + n += 1 + l + sovService(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.OrderBy != 0 { + n += 1 + sovService(uint64(m.OrderBy)) + } + return n +} + +func (m *GetTxsEventResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Txs) > 0 { + for _, e := range m.Txs { + l = e.Size() + n += 1 + l + sovService(uint64(l)) + } + } + if len(m.TxResponses) > 0 { + for _, e := range m.TxResponses { + l = e.Size() + n += 1 + l + sovService(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func (m *BroadcastTxRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TxBytes) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + if m.Mode != 0 { + n += 1 + sovService(uint64(m.Mode)) + } + return n +} + +func (m *BroadcastTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TxResponse != nil { + l = m.TxResponse.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func (m *SimulateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Tx != nil { + l = m.Tx.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func (m *SimulateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GasInfo != nil { + l = m.GasInfo.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func (m *GetTxRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func (m *GetTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Tx != nil { + l = m.Tx.Size() + n += 1 + l + sovService(uint64(l)) + } + if m.TxResponse != nil { + l = m.TxResponse.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + +func sovService(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozService(x uint64) (n int) { + return sovService(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GetTxsEventRequest) 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 ErrIntOverflowService + } + 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: GetTxsEventRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxsEventRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + 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 ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Events = append(m.Events, 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 ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + 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 + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OrderBy", wireType) + } + m.OrderBy = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OrderBy |= OrderBy(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTxsEventResponse) 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 ErrIntOverflowService + } + 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: GetTxsEventResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxsEventResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Txs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Txs = append(m.Txs, &Tx{}) + if err := m.Txs[len(m.Txs)-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 TxResponses", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxResponses = append(m.TxResponses, &types.TxResponse{}) + if err := m.TxResponses[len(m.TxResponses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + 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 ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + 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 := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BroadcastTxRequest) 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 ErrIntOverflowService + } + 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: BroadcastTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BroadcastTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxBytes = append(m.TxBytes[:0], dAtA[iNdEx:postIndex]...) + if m.TxBytes == nil { + m.TxBytes = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= BroadcastMode(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BroadcastTxResponse) 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 ErrIntOverflowService + } + 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: BroadcastTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BroadcastTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TxResponse == nil { + m.TxResponse = &types.TxResponse{} + } + if err := m.TxResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SimulateRequest) 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 ErrIntOverflowService + } + 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: SimulateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SimulateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Tx == nil { + m.Tx = &Tx{} + } + if err := m.Tx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SimulateResponse) 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 ErrIntOverflowService + } + 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: SimulateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SimulateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasInfo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GasInfo == nil { + m.GasInfo = &types.GasInfo{} + } + if err := m.GasInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + 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 ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &types.Result{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTxRequest) 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 ErrIntOverflowService + } + 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: GetTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + 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 ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTxResponse) 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 ErrIntOverflowService + } + 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: GetTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Tx == nil { + m.Tx = &Tx{} + } + if err := m.Tx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TxResponse == nil { + m.TxResponse = &types.TxResponse{} + } + if err := m.TxResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipService(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, ErrIntOverflowService + } + 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, ErrIntOverflowService + } + 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, ErrIntOverflowService + } + 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, ErrInvalidLengthService + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupService + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthService + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthService = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowService = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupService = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/tx/service.pb.gw.go b/types/tx/service.pb.gw.go new file mode 100644 index 000000000000..6d94c423ecbd --- /dev/null +++ b/types/tx/service.pb.gw.go @@ -0,0 +1,420 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/tx/v1beta1/service.proto + +/* +Package tx is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package tx + +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_Service_Simulate_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SimulateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Simulate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_Simulate_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SimulateRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Simulate(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Service_GetTx_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := client.GetTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetTx_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := server.GetTx(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Service_BroadcastTx_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BroadcastTxRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.BroadcastTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_BroadcastTx_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BroadcastTxRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.BroadcastTx(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Service_GetTxsEvent_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Service_GetTxsEvent_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxsEventRequest + 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_Service_GetTxsEvent_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetTxsEvent(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetTxsEvent_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxsEventRequest + 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_Service_GetTxsEvent_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetTxsEvent(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterServiceHandlerServer registers the http handlers for service Service to "mux". +// UnaryRPC :call ServiceServer 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 RegisterServiceHandlerFromEndpoint instead. +func RegisterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ServiceServer) error { + + mux.Handle("POST", pattern_Service_Simulate_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_Service_Simulate_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_Simulate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetTx_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_Service_GetTx_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_Service_BroadcastTx_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_Service_BroadcastTx_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_BroadcastTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetTxsEvent_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_Service_GetTxsEvent_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetTxsEvent_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterServiceHandlerFromEndpoint is same as RegisterServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterServiceHandlerFromEndpoint(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 RegisterServiceHandler(ctx, mux, conn) +} + +// RegisterServiceHandler registers the http handlers for service Service to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterServiceHandlerClient(ctx, mux, NewServiceClient(conn)) +} + +// RegisterServiceHandlerClient registers the http handlers for service Service +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ServiceClient" to call the correct interceptors. +func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ServiceClient) error { + + mux.Handle("POST", pattern_Service_Simulate_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_Service_Simulate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_Simulate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetTx_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_Service_GetTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_Service_BroadcastTx_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_Service_BroadcastTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_BroadcastTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Service_GetTxsEvent_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_Service_GetTxsEvent_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetTxsEvent_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Service_Simulate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "simulate"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "tx", "v1beta1", "txs", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_BroadcastTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_GetTxsEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Service_Simulate_0 = runtime.ForwardResponseMessage + + forward_Service_GetTx_0 = runtime.ForwardResponseMessage + + forward_Service_BroadcastTx_0 = runtime.ForwardResponseMessage + + forward_Service_GetTxsEvent_0 = runtime.ForwardResponseMessage +) diff --git a/types/tx/signing/signature.go b/types/tx/signing/signature.go new file mode 100644 index 000000000000..1323543f023a --- /dev/null +++ b/types/tx/signing/signature.go @@ -0,0 +1,107 @@ +package signing + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// SignatureV2 is a convenience type that is easier to use in application logic +// than the protobuf SignerInfo's and raw signature bytes. It goes beyond the +// first sdk.Signature types by supporting sign modes and explicitly nested +// multi-signatures. It is intended to be used for both building and verifying +// signatures. +type SignatureV2 struct { + // PubKey is the public key to use for verifying the signature + PubKey cryptotypes.PubKey + + // Data is the actual data of the signature which includes SignMode's and + // the signatures themselves for either single or multi-signatures. + Data SignatureData + + // Sequence is the sequence of this account. Only populated in + // SIGN_MODE_DIRECT. + Sequence uint64 +} + +// SignatureDataToProto converts a SignatureData to SignatureDescriptor_Data. +// SignatureDescriptor_Data is considered an encoding type whereas SignatureData is used for +// business logic. +func SignatureDataToProto(data SignatureData) *SignatureDescriptor_Data { + switch data := data.(type) { + case *SingleSignatureData: + return &SignatureDescriptor_Data{ + Sum: &SignatureDescriptor_Data_Single_{ + Single: &SignatureDescriptor_Data_Single{ + Mode: data.SignMode, + Signature: data.Signature, + }, + }, + } + case *MultiSignatureData: + descDatas := make([]*SignatureDescriptor_Data, len(data.Signatures)) + + for j, d := range data.Signatures { + descDatas[j] = SignatureDataToProto(d) + } + + return &SignatureDescriptor_Data{ + Sum: &SignatureDescriptor_Data_Multi_{ + Multi: &SignatureDescriptor_Data_Multi{ + Bitarray: data.BitArray, + Signatures: descDatas, + }, + }, + } + default: + panic(fmt.Errorf("unexpected case %+v", data)) + } +} + +// SignatureDataFromProto converts a SignatureDescriptor_Data to SignatureData. +// SignatureDescriptor_Data is considered an encoding type whereas SignatureData is used for +// business logic. +func SignatureDataFromProto(descData *SignatureDescriptor_Data) SignatureData { + switch descData := descData.Sum.(type) { + case *SignatureDescriptor_Data_Single_: + return &SingleSignatureData{ + SignMode: descData.Single.Mode, + Signature: descData.Single.Signature, + } + case *SignatureDescriptor_Data_Multi_: + multi := descData.Multi + datas := make([]SignatureData, len(multi.Signatures)) + + for j, d := range multi.Signatures { + datas[j] = SignatureDataFromProto(d) + } + + return &MultiSignatureData{ + BitArray: multi.Bitarray, + Signatures: datas, + } + default: + panic(fmt.Errorf("unexpected case %+v", descData)) + } +} + +var _, _ codectypes.UnpackInterfacesMessage = &SignatureDescriptors{}, &SignatureDescriptor{} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (sds *SignatureDescriptors) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, sig := range sds.Signatures { + err := sig.UnpackInterfaces(unpacker) + + if err != nil { + return err + } + } + + return nil +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (sd *SignatureDescriptor) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(sd.PublicKey, new(cryptotypes.PubKey)) +} diff --git a/types/tx/signing/signature_data.go b/types/tx/signing/signature_data.go new file mode 100644 index 000000000000..0dd61fe63459 --- /dev/null +++ b/types/tx/signing/signature_data.go @@ -0,0 +1,36 @@ +package signing + +import ( + "github.com/cosmos/cosmos-sdk/crypto/types" +) + +// SignatureData represents either a *SingleSignatureData or *MultiSignatureData. +// It is a convenience type that is easier to use in business logic than the encoded +// protobuf ModeInfo's and raw signatures. +type SignatureData interface { + isSignatureData() +} + +// SingleSignatureData represents the signature and SignMode of a single (non-multisig) signer +type SingleSignatureData struct { + // SignMode represents the SignMode of the signature + SignMode SignMode + + // SignMode represents the SignMode of the signature + Signature []byte +} + +// MultiSignatureData represents the nested SignatureData of a multisig signature +type MultiSignatureData struct { + // BitArray is a compact way of indicating which signers from the multisig key + // have signed + BitArray *types.CompactBitArray + + // Signatures is the nested SignatureData's for each signer + Signatures []SignatureData +} + +var _, _ SignatureData = &SingleSignatureData{}, &MultiSignatureData{} + +func (m *SingleSignatureData) isSignatureData() {} +func (m *MultiSignatureData) isSignatureData() {} diff --git a/types/tx/signing/signing.pb.go b/types/tx/signing/signing.pb.go new file mode 100644 index 000000000000..f06a270c7cbd --- /dev/null +++ b/types/tx/signing/signing.pb.go @@ -0,0 +1,1452 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/tx/signing/v1beta1/signing.proto + +package signing + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/crypto/types" + 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 + +// SignMode represents a signing mode with its own security guarantees. +type SignMode int32 + +const ( + // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + // rejected + SignMode_SIGN_MODE_UNSPECIFIED SignMode = 0 + // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + // verified with raw bytes from Tx + SignMode_SIGN_MODE_DIRECT SignMode = 1 + // SIGN_MODE_TEXTUAL is a future signing mode that will verify some + // human-readable textual representation on top of the binary representation + // from SIGN_MODE_DIRECT + SignMode_SIGN_MODE_TEXTUAL SignMode = 2 + // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + // Amino JSON and will be removed in the future + SignMode_SIGN_MODE_LEGACY_AMINO_JSON SignMode = 127 +) + +var SignMode_name = map[int32]string{ + 0: "SIGN_MODE_UNSPECIFIED", + 1: "SIGN_MODE_DIRECT", + 2: "SIGN_MODE_TEXTUAL", + 127: "SIGN_MODE_LEGACY_AMINO_JSON", +} + +var SignMode_value = map[string]int32{ + "SIGN_MODE_UNSPECIFIED": 0, + "SIGN_MODE_DIRECT": 1, + "SIGN_MODE_TEXTUAL": 2, + "SIGN_MODE_LEGACY_AMINO_JSON": 127, +} + +func (x SignMode) String() string { + return proto.EnumName(SignMode_name, int32(x)) +} + +func (SignMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{0} +} + +// SignatureDescriptors wraps multiple SignatureDescriptor's. +type SignatureDescriptors struct { + // signatures are the signature descriptors + Signatures []*SignatureDescriptor `protobuf:"bytes,1,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (m *SignatureDescriptors) Reset() { *m = SignatureDescriptors{} } +func (m *SignatureDescriptors) String() string { return proto.CompactTextString(m) } +func (*SignatureDescriptors) ProtoMessage() {} +func (*SignatureDescriptors) Descriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{0} +} +func (m *SignatureDescriptors) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureDescriptors) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureDescriptors.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 *SignatureDescriptors) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureDescriptors.Merge(m, src) +} +func (m *SignatureDescriptors) XXX_Size() int { + return m.Size() +} +func (m *SignatureDescriptors) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureDescriptors.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureDescriptors proto.InternalMessageInfo + +func (m *SignatureDescriptors) GetSignatures() []*SignatureDescriptor { + if m != nil { + return m.Signatures + } + return nil +} + +// SignatureDescriptor is a convenience type which represents the full data for +// a signature including the public key of the signer, signing modes and the +// signature itself. It is primarily used for coordinating signatures between +// clients. +type SignatureDescriptor struct { + // public_key is the public key of the signer + PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + Data *SignatureDescriptor_Data `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to prevent + // replay attacks. + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *SignatureDescriptor) Reset() { *m = SignatureDescriptor{} } +func (m *SignatureDescriptor) String() string { return proto.CompactTextString(m) } +func (*SignatureDescriptor) ProtoMessage() {} +func (*SignatureDescriptor) Descriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{1} +} +func (m *SignatureDescriptor) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureDescriptor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureDescriptor.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 *SignatureDescriptor) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureDescriptor.Merge(m, src) +} +func (m *SignatureDescriptor) XXX_Size() int { + return m.Size() +} +func (m *SignatureDescriptor) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureDescriptor.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureDescriptor proto.InternalMessageInfo + +func (m *SignatureDescriptor) GetPublicKey() *types.Any { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *SignatureDescriptor) GetData() *SignatureDescriptor_Data { + if m != nil { + return m.Data + } + return nil +} + +func (m *SignatureDescriptor) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// Data represents signature data +type SignatureDescriptor_Data struct { + // sum is the oneof that specifies whether this represents single or multi-signature data + // + // Types that are valid to be assigned to Sum: + // *SignatureDescriptor_Data_Single_ + // *SignatureDescriptor_Data_Multi_ + Sum isSignatureDescriptor_Data_Sum `protobuf_oneof:"sum"` +} + +func (m *SignatureDescriptor_Data) Reset() { *m = SignatureDescriptor_Data{} } +func (m *SignatureDescriptor_Data) String() string { return proto.CompactTextString(m) } +func (*SignatureDescriptor_Data) ProtoMessage() {} +func (*SignatureDescriptor_Data) Descriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{1, 0} +} +func (m *SignatureDescriptor_Data) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureDescriptor_Data) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureDescriptor_Data.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 *SignatureDescriptor_Data) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureDescriptor_Data.Merge(m, src) +} +func (m *SignatureDescriptor_Data) XXX_Size() int { + return m.Size() +} +func (m *SignatureDescriptor_Data) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureDescriptor_Data.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureDescriptor_Data proto.InternalMessageInfo + +type isSignatureDescriptor_Data_Sum interface { + isSignatureDescriptor_Data_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type SignatureDescriptor_Data_Single_ struct { + Single *SignatureDescriptor_Data_Single `protobuf:"bytes,1,opt,name=single,proto3,oneof" json:"single,omitempty"` +} +type SignatureDescriptor_Data_Multi_ struct { + Multi *SignatureDescriptor_Data_Multi `protobuf:"bytes,2,opt,name=multi,proto3,oneof" json:"multi,omitempty"` +} + +func (*SignatureDescriptor_Data_Single_) isSignatureDescriptor_Data_Sum() {} +func (*SignatureDescriptor_Data_Multi_) isSignatureDescriptor_Data_Sum() {} + +func (m *SignatureDescriptor_Data) GetSum() isSignatureDescriptor_Data_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *SignatureDescriptor_Data) GetSingle() *SignatureDescriptor_Data_Single { + if x, ok := m.GetSum().(*SignatureDescriptor_Data_Single_); ok { + return x.Single + } + return nil +} + +func (m *SignatureDescriptor_Data) GetMulti() *SignatureDescriptor_Data_Multi { + if x, ok := m.GetSum().(*SignatureDescriptor_Data_Multi_); ok { + return x.Multi + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SignatureDescriptor_Data) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SignatureDescriptor_Data_Single_)(nil), + (*SignatureDescriptor_Data_Multi_)(nil), + } +} + +// Single is the signature data for a single signer +type SignatureDescriptor_Data_Single struct { + // mode is the signing mode of the single signer + Mode SignMode `protobuf:"varint,1,opt,name=mode,proto3,enum=cosmos.tx.signing.v1beta1.SignMode" json:"mode,omitempty"` + // signature is the raw signature bytes + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (m *SignatureDescriptor_Data_Single) Reset() { *m = SignatureDescriptor_Data_Single{} } +func (m *SignatureDescriptor_Data_Single) String() string { return proto.CompactTextString(m) } +func (*SignatureDescriptor_Data_Single) ProtoMessage() {} +func (*SignatureDescriptor_Data_Single) Descriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{1, 0, 0} +} +func (m *SignatureDescriptor_Data_Single) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureDescriptor_Data_Single) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureDescriptor_Data_Single.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 *SignatureDescriptor_Data_Single) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureDescriptor_Data_Single.Merge(m, src) +} +func (m *SignatureDescriptor_Data_Single) XXX_Size() int { + return m.Size() +} +func (m *SignatureDescriptor_Data_Single) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureDescriptor_Data_Single.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureDescriptor_Data_Single proto.InternalMessageInfo + +func (m *SignatureDescriptor_Data_Single) GetMode() SignMode { + if m != nil { + return m.Mode + } + return SignMode_SIGN_MODE_UNSPECIFIED +} + +func (m *SignatureDescriptor_Data_Single) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +// Multi is the signature data for a multisig public key +type SignatureDescriptor_Data_Multi struct { + // bitarray specifies which keys within the multisig are signing + Bitarray *types1.CompactBitArray `protobuf:"bytes,1,opt,name=bitarray,proto3" json:"bitarray,omitempty"` + // signatures is the signatures of the multi-signature + Signatures []*SignatureDescriptor_Data `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (m *SignatureDescriptor_Data_Multi) Reset() { *m = SignatureDescriptor_Data_Multi{} } +func (m *SignatureDescriptor_Data_Multi) String() string { return proto.CompactTextString(m) } +func (*SignatureDescriptor_Data_Multi) ProtoMessage() {} +func (*SignatureDescriptor_Data_Multi) Descriptor() ([]byte, []int) { + return fileDescriptor_9a54958ff3d0b1b9, []int{1, 0, 1} +} +func (m *SignatureDescriptor_Data_Multi) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureDescriptor_Data_Multi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureDescriptor_Data_Multi.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 *SignatureDescriptor_Data_Multi) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureDescriptor_Data_Multi.Merge(m, src) +} +func (m *SignatureDescriptor_Data_Multi) XXX_Size() int { + return m.Size() +} +func (m *SignatureDescriptor_Data_Multi) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureDescriptor_Data_Multi.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureDescriptor_Data_Multi proto.InternalMessageInfo + +func (m *SignatureDescriptor_Data_Multi) GetBitarray() *types1.CompactBitArray { + if m != nil { + return m.Bitarray + } + return nil +} + +func (m *SignatureDescriptor_Data_Multi) GetSignatures() []*SignatureDescriptor_Data { + if m != nil { + return m.Signatures + } + return nil +} + +func init() { + proto.RegisterEnum("cosmos.tx.signing.v1beta1.SignMode", SignMode_name, SignMode_value) + proto.RegisterType((*SignatureDescriptors)(nil), "cosmos.tx.signing.v1beta1.SignatureDescriptors") + proto.RegisterType((*SignatureDescriptor)(nil), "cosmos.tx.signing.v1beta1.SignatureDescriptor") + proto.RegisterType((*SignatureDescriptor_Data)(nil), "cosmos.tx.signing.v1beta1.SignatureDescriptor.Data") + proto.RegisterType((*SignatureDescriptor_Data_Single)(nil), "cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Single") + proto.RegisterType((*SignatureDescriptor_Data_Multi)(nil), "cosmos.tx.signing.v1beta1.SignatureDescriptor.Data.Multi") +} + +func init() { + proto.RegisterFile("cosmos/tx/signing/v1beta1/signing.proto", fileDescriptor_9a54958ff3d0b1b9) +} + +var fileDescriptor_9a54958ff3d0b1b9 = []byte{ + // 544 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0xed, 0x26, 0x8d, 0xd2, 0x29, 0x42, 0x61, 0x49, 0xa5, 0xc4, 0x20, 0x13, 0x95, 0x03, + 0x11, 0x52, 0xd6, 0x6a, 0x72, 0x40, 0x70, 0xcb, 0x1f, 0x93, 0x86, 0x36, 0x09, 0xd8, 0xa9, 0x04, + 0x5c, 0x2c, 0xdb, 0xd9, 0x1a, 0xab, 0xb1, 0xd7, 0x78, 0xd7, 0xa8, 0x3e, 0xf1, 0x0a, 0xbc, 0x06, + 0xcf, 0xc1, 0x85, 0x63, 0x8f, 0x1c, 0x51, 0xf2, 0x0c, 0xdc, 0x51, 0xec, 0x38, 0x09, 0x52, 0x11, + 0x22, 0x27, 0x6b, 0x66, 0xbe, 0xfd, 0xcd, 0xb7, 0x9a, 0x59, 0xc3, 0x13, 0x9b, 0x32, 0x8f, 0x32, + 0x85, 0x5f, 0x2b, 0xcc, 0x75, 0x7c, 0xd7, 0x77, 0x94, 0x4f, 0x27, 0x16, 0xe1, 0xe6, 0x49, 0x16, + 0xe3, 0x20, 0xa4, 0x9c, 0xa2, 0x6a, 0x2a, 0xc4, 0xfc, 0x1a, 0x67, 0x85, 0x95, 0x50, 0x6a, 0xac, + 0x18, 0x76, 0x18, 0x07, 0x9c, 0x2a, 0x5e, 0x34, 0xe3, 0x2e, 0x73, 0x37, 0xa0, 0x2c, 0x91, 0x92, + 0xa4, 0xaa, 0x43, 0xa9, 0x33, 0x23, 0x4a, 0x12, 0x59, 0xd1, 0xa5, 0x62, 0xfa, 0x71, 0x5a, 0x3a, + 0xbe, 0x84, 0xb2, 0xee, 0x3a, 0xbe, 0xc9, 0xa3, 0x90, 0xf4, 0x08, 0xb3, 0x43, 0x37, 0xe0, 0x34, + 0x64, 0x68, 0x04, 0xc0, 0xb2, 0x3c, 0xab, 0x88, 0xb5, 0x5c, 0xfd, 0xb0, 0x89, 0xf1, 0x5f, 0x1d, + 0xe1, 0x5b, 0x20, 0xda, 0x16, 0xe1, 0xf8, 0x57, 0x1e, 0xee, 0xdf, 0xa2, 0x41, 0x2d, 0x80, 0x20, + 0xb2, 0x66, 0xae, 0x6d, 0x5c, 0x91, 0xb8, 0x22, 0xd6, 0xc4, 0xfa, 0x61, 0xb3, 0x8c, 0x53, 0xbf, + 0x38, 0xf3, 0x8b, 0xdb, 0x7e, 0xac, 0x1d, 0xa4, 0xba, 0x33, 0x12, 0xa3, 0x3e, 0xe4, 0xa7, 0x26, + 0x37, 0x2b, 0x7b, 0x89, 0xbc, 0xf5, 0x7f, 0xb6, 0x70, 0xcf, 0xe4, 0xa6, 0x96, 0x00, 0x90, 0x04, + 0x45, 0x46, 0x3e, 0x46, 0xc4, 0xb7, 0x49, 0x25, 0x57, 0x13, 0xeb, 0x79, 0x6d, 0x1d, 0x4b, 0xdf, + 0x72, 0x90, 0x5f, 0x4a, 0xd1, 0x04, 0x0a, 0xcc, 0xf5, 0x9d, 0x19, 0x59, 0xd9, 0x7b, 0xb1, 0x43, + 0x3f, 0xac, 0x27, 0x84, 0x53, 0x41, 0x5b, 0xb1, 0xd0, 0x1b, 0xd8, 0x4f, 0xa6, 0xb4, 0xba, 0xc4, + 0xf3, 0x5d, 0xa0, 0xc3, 0x25, 0xe0, 0x54, 0xd0, 0x52, 0x92, 0x64, 0x40, 0x21, 0x6d, 0x83, 0x9e, + 0x41, 0xde, 0xa3, 0xd3, 0xd4, 0xf0, 0xdd, 0xe6, 0xe3, 0x7f, 0xb0, 0x87, 0x74, 0x4a, 0xb4, 0xe4, + 0x00, 0x7a, 0x08, 0x07, 0xeb, 0xa1, 0x25, 0xce, 0xee, 0x68, 0x9b, 0x84, 0xf4, 0x55, 0x84, 0xfd, + 0xa4, 0x27, 0x3a, 0x83, 0xa2, 0xe5, 0x72, 0x33, 0x0c, 0xcd, 0x6c, 0x68, 0x4a, 0xd6, 0x24, 0xdd, + 0x49, 0xbc, 0x5e, 0xc1, 0xac, 0x53, 0x97, 0x7a, 0x81, 0x69, 0xf3, 0x8e, 0xcb, 0xdb, 0xcb, 0x63, + 0xda, 0x1a, 0x80, 0xf4, 0x3f, 0x76, 0x6d, 0x2f, 0xd9, 0xb5, 0x9d, 0x86, 0xba, 0x85, 0xe9, 0xec, + 0x43, 0x8e, 0x45, 0xde, 0x53, 0x06, 0xc5, 0xec, 0x8a, 0xa8, 0x0a, 0x47, 0xfa, 0xa0, 0x3f, 0x32, + 0x86, 0xe3, 0x9e, 0x6a, 0x5c, 0x8c, 0xf4, 0xd7, 0x6a, 0x77, 0xf0, 0x72, 0xa0, 0xf6, 0x4a, 0x02, + 0x2a, 0x43, 0x69, 0x53, 0xea, 0x0d, 0x34, 0xb5, 0x3b, 0x29, 0x89, 0xe8, 0x08, 0xee, 0x6d, 0xb2, + 0x13, 0xf5, 0xed, 0xe4, 0xa2, 0x7d, 0x5e, 0xda, 0x43, 0x8f, 0xe0, 0xc1, 0x26, 0x7d, 0xae, 0xf6, + 0xdb, 0xdd, 0x77, 0x46, 0x7b, 0x38, 0x18, 0x8d, 0x8d, 0x57, 0xfa, 0x78, 0x54, 0xfa, 0xdc, 0xe9, + 0x7f, 0x9f, 0xcb, 0xe2, 0xcd, 0x5c, 0x16, 0x7f, 0xce, 0x65, 0xf1, 0xcb, 0x42, 0x16, 0x6e, 0x16, + 0xb2, 0xf0, 0x63, 0x21, 0x0b, 0xef, 0x1b, 0x8e, 0xcb, 0x3f, 0x44, 0x16, 0xb6, 0xa9, 0xa7, 0x64, + 0x6f, 0x38, 0xf9, 0x34, 0xd8, 0xf4, 0x4a, 0xe1, 0x71, 0x40, 0xb6, 0x7f, 0x0c, 0x56, 0x21, 0x79, + 0x01, 0xad, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x86, 0x87, 0x55, 0xbf, 0x34, 0x04, 0x00, 0x00, +} + +func (m *SignatureDescriptors) 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 *SignatureDescriptors) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptors) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Signatures[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SignatureDescriptor) 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 *SignatureDescriptor) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintSigning(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if m.Data != nil { + { + size, err := m.Data.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.PublicKey != nil { + { + size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignatureDescriptor_Data) 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 *SignatureDescriptor_Data) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor_Data) 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 *SignatureDescriptor_Data_Single_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor_Data_Single_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Single != nil { + { + size, err := m.Single.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *SignatureDescriptor_Data_Multi_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor_Data_Multi_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Multi != nil { + { + size, err := m.Multi.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *SignatureDescriptor_Data_Single) 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 *SignatureDescriptor_Data_Single) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor_Data_Single) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSigning(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x12 + } + if m.Mode != 0 { + i = encodeVarintSigning(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SignatureDescriptor_Data_Multi) 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 *SignatureDescriptor_Data_Multi) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureDescriptor_Data_Multi) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Signatures[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Bitarray != nil { + { + size, err := m.Bitarray.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSigning(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSigning(dAtA []byte, offset int, v uint64) int { + offset -= sovSigning(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SignatureDescriptors) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Signatures) > 0 { + for _, e := range m.Signatures { + l = e.Size() + n += 1 + l + sovSigning(uint64(l)) + } + } + return n +} + +func (m *SignatureDescriptor) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovSigning(uint64(l)) + } + if m.Data != nil { + l = m.Data.Size() + n += 1 + l + sovSigning(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovSigning(uint64(m.Sequence)) + } + return n +} + +func (m *SignatureDescriptor_Data) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *SignatureDescriptor_Data_Single_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Single != nil { + l = m.Single.Size() + n += 1 + l + sovSigning(uint64(l)) + } + return n +} +func (m *SignatureDescriptor_Data_Multi_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Multi != nil { + l = m.Multi.Size() + n += 1 + l + sovSigning(uint64(l)) + } + return n +} +func (m *SignatureDescriptor_Data_Single) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Mode != 0 { + n += 1 + sovSigning(uint64(m.Mode)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSigning(uint64(l)) + } + return n +} + +func (m *SignatureDescriptor_Data_Multi) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Bitarray != nil { + l = m.Bitarray.Size() + n += 1 + l + sovSigning(uint64(l)) + } + if len(m.Signatures) > 0 { + for _, e := range m.Signatures { + l = e.Size() + n += 1 + l + sovSigning(uint64(l)) + } + } + return n +} + +func sovSigning(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSigning(x uint64) (n int) { + return sovSigning(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SignatureDescriptors) 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 ErrIntOverflowSigning + } + 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: SignatureDescriptors: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureDescriptors: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, &SignatureDescriptor{}) + if err := m.Signatures[len(m.Signatures)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSigning(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSigning + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureDescriptor) 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 ErrIntOverflowSigning + } + 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: SignatureDescriptor: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureDescriptor: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Data == nil { + m.Data = &SignatureDescriptor_Data{} + } + if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSigning(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSigning + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureDescriptor_Data) 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 ErrIntOverflowSigning + } + 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: Data: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Data: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Single", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignatureDescriptor_Data_Single{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &SignatureDescriptor_Data_Single_{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Multi", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignatureDescriptor_Data_Multi{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &SignatureDescriptor_Data_Multi_{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSigning(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSigning + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureDescriptor_Data_Single) 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 ErrIntOverflowSigning + } + 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: Single: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Single: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= SignMode(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSigning(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSigning + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureDescriptor_Data_Multi) 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 ErrIntOverflowSigning + } + 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: Multi: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Multi: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bitarray", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Bitarray == nil { + m.Bitarray = &types1.CompactBitArray{} + } + if err := m.Bitarray.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSigning + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSigning + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSigning + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, &SignatureDescriptor_Data{}) + if err := m.Signatures[len(m.Signatures)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSigning(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSigning + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSigning(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, ErrIntOverflowSigning + } + 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, ErrIntOverflowSigning + } + 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, ErrIntOverflowSigning + } + 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, ErrInvalidLengthSigning + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSigning + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSigning + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSigning = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSigning = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSigning = fmt.Errorf("proto: unexpected end of group") +) diff --git a/types/tx/tx.pb.go b/types/tx/tx.pb.go new file mode 100644 index 000000000000..a552c7a083e1 --- /dev/null +++ b/types/tx/tx.pb.go @@ -0,0 +1,3106 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/tx/v1beta1/tx.proto + +package tx + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/crypto/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types2 "github.com/cosmos/cosmos-sdk/types" + signing "github.com/cosmos/cosmos-sdk/types/tx/signing" + _ "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 + +// Tx is the standard type used for broadcasting transactions. +type Tx struct { + // body is the processable content of the transaction + Body *TxBody `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo *AuthInfo `protobuf:"bytes,2,opt,name=auth_info,json=authInfo,proto3" json:"auth_info,omitempty"` + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + Signatures [][]byte `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (m *Tx) Reset() { *m = Tx{} } +func (m *Tx) String() string { return proto.CompactTextString(m) } +func (*Tx) ProtoMessage() {} +func (*Tx) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{0} +} +func (m *Tx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Tx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Tx.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 *Tx) XXX_Merge(src proto.Message) { + xxx_messageInfo_Tx.Merge(m, src) +} +func (m *Tx) XXX_Size() int { + return m.Size() +} +func (m *Tx) XXX_DiscardUnknown() { + xxx_messageInfo_Tx.DiscardUnknown(m) +} + +var xxx_messageInfo_Tx proto.InternalMessageInfo + +func (m *Tx) GetBody() *TxBody { + if m != nil { + return m.Body + } + return nil +} + +func (m *Tx) GetAuthInfo() *AuthInfo { + if m != nil { + return m.AuthInfo + } + return nil +} + +func (m *Tx) GetSignatures() [][]byte { + if m != nil { + return m.Signatures + } + return nil +} + +// TxRaw is a variant of Tx that pins the signer's exact binary representation +// of body and auth_info. This is used for signing, broadcasting and +// verification. The binary `serialize(tx: TxRaw)` is stored in Tendermint and +// the hash `sha256(serialize(tx: TxRaw))` becomes the "txhash", commonly used +// as the transaction ID. +type TxRaw struct { + // body_bytes is a protobuf serialization of a TxBody that matches the + // representation in SignDoc. + BodyBytes []byte `protobuf:"bytes,1,opt,name=body_bytes,json=bodyBytes,proto3" json:"body_bytes,omitempty"` + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in SignDoc. + AuthInfoBytes []byte `protobuf:"bytes,2,opt,name=auth_info_bytes,json=authInfoBytes,proto3" json:"auth_info_bytes,omitempty"` + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + Signatures [][]byte `protobuf:"bytes,3,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (m *TxRaw) Reset() { *m = TxRaw{} } +func (m *TxRaw) String() string { return proto.CompactTextString(m) } +func (*TxRaw) ProtoMessage() {} +func (*TxRaw) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{1} +} +func (m *TxRaw) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxRaw) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxRaw.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 *TxRaw) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxRaw.Merge(m, src) +} +func (m *TxRaw) XXX_Size() int { + return m.Size() +} +func (m *TxRaw) XXX_DiscardUnknown() { + xxx_messageInfo_TxRaw.DiscardUnknown(m) +} + +var xxx_messageInfo_TxRaw proto.InternalMessageInfo + +func (m *TxRaw) GetBodyBytes() []byte { + if m != nil { + return m.BodyBytes + } + return nil +} + +func (m *TxRaw) GetAuthInfoBytes() []byte { + if m != nil { + return m.AuthInfoBytes + } + return nil +} + +func (m *TxRaw) GetSignatures() [][]byte { + if m != nil { + return m.Signatures + } + return nil +} + +// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. +type SignDoc struct { + // body_bytes is protobuf serialization of a TxBody that matches the + // representation in TxRaw. + BodyBytes []byte `protobuf:"bytes,1,opt,name=body_bytes,json=bodyBytes,proto3" json:"body_bytes,omitempty"` + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in TxRaw. + AuthInfoBytes []byte `protobuf:"bytes,2,opt,name=auth_info_bytes,json=authInfoBytes,proto3" json:"auth_info_bytes,omitempty"` + // chain_id is the unique identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // account_number is the account number of the account in state + AccountNumber uint64 `protobuf:"varint,4,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` +} + +func (m *SignDoc) Reset() { *m = SignDoc{} } +func (m *SignDoc) String() string { return proto.CompactTextString(m) } +func (*SignDoc) ProtoMessage() {} +func (*SignDoc) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{2} +} +func (m *SignDoc) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignDoc) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignDoc.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 *SignDoc) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignDoc.Merge(m, src) +} +func (m *SignDoc) XXX_Size() int { + return m.Size() +} +func (m *SignDoc) XXX_DiscardUnknown() { + xxx_messageInfo_SignDoc.DiscardUnknown(m) +} + +var xxx_messageInfo_SignDoc proto.InternalMessageInfo + +func (m *SignDoc) GetBodyBytes() []byte { + if m != nil { + return m.BodyBytes + } + return nil +} + +func (m *SignDoc) GetAuthInfoBytes() []byte { + if m != nil { + return m.AuthInfoBytes + } + return nil +} + +func (m *SignDoc) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *SignDoc) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +// TxBody is the body of a transaction that all signers sign over. +type TxBody struct { + // messages is a list of messages to be executed. The required signers of + // those messages define the number and order of elements in AuthInfo's + // signer_infos and Tx's signatures. Each required signer address is added to + // the list only the first time it occurs. + // By convention, the first required signer (usually from the first message) + // is referred to as the primary signer and pays the fee for the whole + // transaction. + Messages []*types.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + // memo is any arbitrary memo to be added to the transaction + Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"` + // timeout is the block height after which this transaction will not + // be processed by the chain + TimeoutHeight uint64 `protobuf:"varint,3,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height,omitempty"` + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, the transaction will be rejected + ExtensionOptions []*types.Any `protobuf:"bytes,1023,rep,name=extension_options,json=extensionOptions,proto3" json:"extension_options,omitempty"` + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, they will be ignored + NonCriticalExtensionOptions []*types.Any `protobuf:"bytes,2047,rep,name=non_critical_extension_options,json=nonCriticalExtensionOptions,proto3" json:"non_critical_extension_options,omitempty"` +} + +func (m *TxBody) Reset() { *m = TxBody{} } +func (m *TxBody) String() string { return proto.CompactTextString(m) } +func (*TxBody) ProtoMessage() {} +func (*TxBody) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{3} +} +func (m *TxBody) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxBody) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxBody.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 *TxBody) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxBody.Merge(m, src) +} +func (m *TxBody) XXX_Size() int { + return m.Size() +} +func (m *TxBody) XXX_DiscardUnknown() { + xxx_messageInfo_TxBody.DiscardUnknown(m) +} + +var xxx_messageInfo_TxBody proto.InternalMessageInfo + +func (m *TxBody) GetMessages() []*types.Any { + if m != nil { + return m.Messages + } + return nil +} + +func (m *TxBody) GetMemo() string { + if m != nil { + return m.Memo + } + return "" +} + +func (m *TxBody) GetTimeoutHeight() uint64 { + if m != nil { + return m.TimeoutHeight + } + return 0 +} + +func (m *TxBody) GetExtensionOptions() []*types.Any { + if m != nil { + return m.ExtensionOptions + } + return nil +} + +func (m *TxBody) GetNonCriticalExtensionOptions() []*types.Any { + if m != nil { + return m.NonCriticalExtensionOptions + } + return nil +} + +// AuthInfo describes the fee and signer modes that are used to sign a +// transaction. +type AuthInfo struct { + // signer_infos defines the signing modes for the required signers. The number + // and order of elements must match the required signers from TxBody's + // messages. The first element is the primary signer and the one which pays + // the fee. + SignerInfos []*SignerInfo `protobuf:"bytes,1,rep,name=signer_infos,json=signerInfos,proto3" json:"signer_infos,omitempty"` + // Fee is the fee and gas limit for the transaction. The first signer is the + // primary signer and the one which pays the fee. The fee can be calculated + // based on the cost of evaluating the body and doing signature verification + // of the signers. This can be estimated via simulation. + Fee *Fee `protobuf:"bytes,2,opt,name=fee,proto3" json:"fee,omitempty"` +} + +func (m *AuthInfo) Reset() { *m = AuthInfo{} } +func (m *AuthInfo) String() string { return proto.CompactTextString(m) } +func (*AuthInfo) ProtoMessage() {} +func (*AuthInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{4} +} +func (m *AuthInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AuthInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AuthInfo.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 *AuthInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthInfo.Merge(m, src) +} +func (m *AuthInfo) XXX_Size() int { + return m.Size() +} +func (m *AuthInfo) XXX_DiscardUnknown() { + xxx_messageInfo_AuthInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthInfo proto.InternalMessageInfo + +func (m *AuthInfo) GetSignerInfos() []*SignerInfo { + if m != nil { + return m.SignerInfos + } + return nil +} + +func (m *AuthInfo) GetFee() *Fee { + if m != nil { + return m.Fee + } + return nil +} + +// SignerInfo describes the public key and signing mode of a single top-level +// signer. +type SignerInfo struct { + // public_key is the public key of the signer. It is optional for accounts + // that already exist in state. If unset, the verifier can use the required \ + // signer address for this position and lookup the public key. + PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + // mode_info describes the signing mode of the signer and is a nested + // structure to support nested multisig pubkey's + ModeInfo *ModeInfo `protobuf:"bytes,2,opt,name=mode_info,json=modeInfo,proto3" json:"mode_info,omitempty"` + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to + // prevent replay attacks. + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *SignerInfo) Reset() { *m = SignerInfo{} } +func (m *SignerInfo) String() string { return proto.CompactTextString(m) } +func (*SignerInfo) ProtoMessage() {} +func (*SignerInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{5} +} +func (m *SignerInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignerInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignerInfo.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 *SignerInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignerInfo.Merge(m, src) +} +func (m *SignerInfo) XXX_Size() int { + return m.Size() +} +func (m *SignerInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SignerInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SignerInfo proto.InternalMessageInfo + +func (m *SignerInfo) GetPublicKey() *types.Any { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *SignerInfo) GetModeInfo() *ModeInfo { + if m != nil { + return m.ModeInfo + } + return nil +} + +func (m *SignerInfo) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// ModeInfo describes the signing mode of a single or nested multisig signer. +type ModeInfo struct { + // sum is the oneof that specifies whether this represents a single or nested + // multisig signer + // + // Types that are valid to be assigned to Sum: + // *ModeInfo_Single_ + // *ModeInfo_Multi_ + Sum isModeInfo_Sum `protobuf_oneof:"sum"` +} + +func (m *ModeInfo) Reset() { *m = ModeInfo{} } +func (m *ModeInfo) String() string { return proto.CompactTextString(m) } +func (*ModeInfo) ProtoMessage() {} +func (*ModeInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{6} +} +func (m *ModeInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModeInfo.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 *ModeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModeInfo.Merge(m, src) +} +func (m *ModeInfo) XXX_Size() int { + return m.Size() +} +func (m *ModeInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ModeInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ModeInfo proto.InternalMessageInfo + +type isModeInfo_Sum interface { + isModeInfo_Sum() + MarshalTo([]byte) (int, error) + Size() int +} + +type ModeInfo_Single_ struct { + Single *ModeInfo_Single `protobuf:"bytes,1,opt,name=single,proto3,oneof" json:"single,omitempty"` +} +type ModeInfo_Multi_ struct { + Multi *ModeInfo_Multi `protobuf:"bytes,2,opt,name=multi,proto3,oneof" json:"multi,omitempty"` +} + +func (*ModeInfo_Single_) isModeInfo_Sum() {} +func (*ModeInfo_Multi_) isModeInfo_Sum() {} + +func (m *ModeInfo) GetSum() isModeInfo_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *ModeInfo) GetSingle() *ModeInfo_Single { + if x, ok := m.GetSum().(*ModeInfo_Single_); ok { + return x.Single + } + return nil +} + +func (m *ModeInfo) GetMulti() *ModeInfo_Multi { + if x, ok := m.GetSum().(*ModeInfo_Multi_); ok { + return x.Multi + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ModeInfo) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ModeInfo_Single_)(nil), + (*ModeInfo_Multi_)(nil), + } +} + +// Single is the mode info for a single signer. It is structured as a message +// to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the +// future +type ModeInfo_Single struct { + // mode is the signing mode of the single signer + Mode signing.SignMode `protobuf:"varint,1,opt,name=mode,proto3,enum=cosmos.tx.signing.v1beta1.SignMode" json:"mode,omitempty"` +} + +func (m *ModeInfo_Single) Reset() { *m = ModeInfo_Single{} } +func (m *ModeInfo_Single) String() string { return proto.CompactTextString(m) } +func (*ModeInfo_Single) ProtoMessage() {} +func (*ModeInfo_Single) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{6, 0} +} +func (m *ModeInfo_Single) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModeInfo_Single) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModeInfo_Single.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 *ModeInfo_Single) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModeInfo_Single.Merge(m, src) +} +func (m *ModeInfo_Single) XXX_Size() int { + return m.Size() +} +func (m *ModeInfo_Single) XXX_DiscardUnknown() { + xxx_messageInfo_ModeInfo_Single.DiscardUnknown(m) +} + +var xxx_messageInfo_ModeInfo_Single proto.InternalMessageInfo + +func (m *ModeInfo_Single) GetMode() signing.SignMode { + if m != nil { + return m.Mode + } + return signing.SignMode_SIGN_MODE_UNSPECIFIED +} + +// Multi is the mode info for a multisig public key +type ModeInfo_Multi struct { + // bitarray specifies which keys within the multisig are signing + Bitarray *types1.CompactBitArray `protobuf:"bytes,1,opt,name=bitarray,proto3" json:"bitarray,omitempty"` + // mode_infos is the corresponding modes of the signers of the multisig + // which could include nested multisig public keys + ModeInfos []*ModeInfo `protobuf:"bytes,2,rep,name=mode_infos,json=modeInfos,proto3" json:"mode_infos,omitempty"` +} + +func (m *ModeInfo_Multi) Reset() { *m = ModeInfo_Multi{} } +func (m *ModeInfo_Multi) String() string { return proto.CompactTextString(m) } +func (*ModeInfo_Multi) ProtoMessage() {} +func (*ModeInfo_Multi) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{6, 1} +} +func (m *ModeInfo_Multi) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModeInfo_Multi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModeInfo_Multi.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 *ModeInfo_Multi) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModeInfo_Multi.Merge(m, src) +} +func (m *ModeInfo_Multi) XXX_Size() int { + return m.Size() +} +func (m *ModeInfo_Multi) XXX_DiscardUnknown() { + xxx_messageInfo_ModeInfo_Multi.DiscardUnknown(m) +} + +var xxx_messageInfo_ModeInfo_Multi proto.InternalMessageInfo + +func (m *ModeInfo_Multi) GetBitarray() *types1.CompactBitArray { + if m != nil { + return m.Bitarray + } + return nil +} + +func (m *ModeInfo_Multi) GetModeInfos() []*ModeInfo { + if m != nil { + return m.ModeInfos + } + return nil +} + +// Fee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +type Fee struct { + // amount is the amount of coins to be paid as a fee + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` + // gas_limit is the maximum gas that can be used in transaction processing + // before an out of gas error occurs + GasLimit uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + // if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + // the payer must be a tx signer (and thus have signed this field in AuthInfo). + // setting this field does *not* change the ordering of required signers for the transaction. + Payer string `protobuf:"bytes,3,opt,name=payer,proto3" json:"payer,omitempty"` + // if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + // to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + // not support fee grants, this will fail + Granter string `protobuf:"bytes,4,opt,name=granter,proto3" json:"granter,omitempty"` +} + +func (m *Fee) Reset() { *m = Fee{} } +func (m *Fee) String() string { return proto.CompactTextString(m) } +func (*Fee) ProtoMessage() {} +func (*Fee) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{7} +} +func (m *Fee) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Fee) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Fee.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 *Fee) XXX_Merge(src proto.Message) { + xxx_messageInfo_Fee.Merge(m, src) +} +func (m *Fee) XXX_Size() int { + return m.Size() +} +func (m *Fee) XXX_DiscardUnknown() { + xxx_messageInfo_Fee.DiscardUnknown(m) +} + +var xxx_messageInfo_Fee proto.InternalMessageInfo + +func (m *Fee) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +func (m *Fee) GetGasLimit() uint64 { + if m != nil { + return m.GasLimit + } + return 0 +} + +func (m *Fee) GetPayer() string { + if m != nil { + return m.Payer + } + return "" +} + +func (m *Fee) GetGranter() string { + if m != nil { + return m.Granter + } + return "" +} + +func init() { + proto.RegisterType((*Tx)(nil), "cosmos.tx.v1beta1.Tx") + proto.RegisterType((*TxRaw)(nil), "cosmos.tx.v1beta1.TxRaw") + proto.RegisterType((*SignDoc)(nil), "cosmos.tx.v1beta1.SignDoc") + proto.RegisterType((*TxBody)(nil), "cosmos.tx.v1beta1.TxBody") + proto.RegisterType((*AuthInfo)(nil), "cosmos.tx.v1beta1.AuthInfo") + proto.RegisterType((*SignerInfo)(nil), "cosmos.tx.v1beta1.SignerInfo") + proto.RegisterType((*ModeInfo)(nil), "cosmos.tx.v1beta1.ModeInfo") + proto.RegisterType((*ModeInfo_Single)(nil), "cosmos.tx.v1beta1.ModeInfo.Single") + proto.RegisterType((*ModeInfo_Multi)(nil), "cosmos.tx.v1beta1.ModeInfo.Multi") + proto.RegisterType((*Fee)(nil), "cosmos.tx.v1beta1.Fee") +} + +func init() { proto.RegisterFile("cosmos/tx/v1beta1/tx.proto", fileDescriptor_96d1575ffde80842) } + +var fileDescriptor_96d1575ffde80842 = []byte{ + // 843 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xdd, 0x6e, 0xdc, 0x44, + 0x14, 0x5e, 0xef, 0x5f, 0xd6, 0x27, 0x49, 0x4b, 0x47, 0x11, 0xda, 0x6c, 0x54, 0x37, 0x18, 0x15, + 0xf6, 0x26, 0x76, 0x9b, 0x5e, 0xf0, 0x23, 0x24, 0xc8, 0x16, 0xaa, 0x54, 0xa5, 0x20, 0x4d, 0x72, + 0xd5, 0x1b, 0x6b, 0xec, 0x9d, 0x78, 0x47, 0x5d, 0xcf, 0x2c, 0x9e, 0x71, 0xb1, 0x1f, 0x02, 0xa9, + 0x42, 0x42, 0xbc, 0x03, 0x2f, 0xc0, 0x2b, 0xf4, 0xb2, 0x97, 0x5c, 0x41, 0x95, 0x3c, 0x08, 0x68, + 0xc6, 0x63, 0x27, 0x82, 0x55, 0x72, 0xc3, 0x95, 0xe7, 0x9c, 0xf9, 0xce, 0x37, 0x9f, 0xcf, 0x1f, + 0x4c, 0x12, 0x21, 0x33, 0x21, 0x43, 0x55, 0x86, 0xaf, 0x1e, 0xc6, 0x54, 0x91, 0x87, 0xa1, 0x2a, + 0x83, 0x55, 0x2e, 0x94, 0x40, 0x77, 0xea, 0xbb, 0x40, 0x95, 0x81, 0xbd, 0x9b, 0xec, 0xa4, 0x22, + 0x15, 0xe6, 0x36, 0xd4, 0xa7, 0x1a, 0x38, 0x39, 0xb0, 0x24, 0x49, 0x5e, 0xad, 0x94, 0x08, 0xb3, + 0x62, 0xa9, 0x98, 0x64, 0x69, 0xcb, 0xd8, 0x38, 0x2c, 0xdc, 0xb3, 0xf0, 0x98, 0x48, 0xda, 0x62, + 0x12, 0xc1, 0xb8, 0xbd, 0xff, 0xf8, 0x52, 0x93, 0x64, 0x29, 0x67, 0xfc, 0x92, 0xc9, 0xda, 0x16, + 0xb8, 0x9b, 0x0a, 0x91, 0x2e, 0x69, 0x68, 0xac, 0xb8, 0x38, 0x0b, 0x09, 0xaf, 0xea, 0x2b, 0xff, + 0x27, 0x07, 0xba, 0xa7, 0x25, 0x3a, 0x80, 0x7e, 0x2c, 0xe6, 0xd5, 0xd8, 0xd9, 0x77, 0xa6, 0x9b, + 0x87, 0xbb, 0xc1, 0x7f, 0xfe, 0x28, 0x38, 0x2d, 0x67, 0x62, 0x5e, 0x61, 0x03, 0x43, 0x9f, 0x82, + 0x4b, 0x0a, 0xb5, 0x88, 0x18, 0x3f, 0x13, 0xe3, 0xae, 0x89, 0xd9, 0x5b, 0x13, 0x73, 0x54, 0xa8, + 0xc5, 0x53, 0x7e, 0x26, 0xf0, 0x88, 0xd8, 0x13, 0xf2, 0x00, 0xb4, 0x36, 0xa2, 0x8a, 0x9c, 0xca, + 0x71, 0x6f, 0xbf, 0x37, 0xdd, 0xc2, 0x57, 0x3c, 0x3e, 0x87, 0xc1, 0x69, 0x89, 0xc9, 0x8f, 0xe8, + 0x2e, 0x80, 0x7e, 0x2a, 0x8a, 0x2b, 0x45, 0xa5, 0xd1, 0xb5, 0x85, 0x5d, 0xed, 0x99, 0x69, 0x07, + 0xfa, 0x08, 0x6e, 0xb7, 0x0a, 0x2c, 0xa6, 0x6b, 0x30, 0xdb, 0xcd, 0x53, 0x35, 0xee, 0xa6, 0xf7, + 0x7e, 0x76, 0x60, 0xe3, 0x84, 0xa5, 0xfc, 0x6b, 0x91, 0xfc, 0x5f, 0x4f, 0xee, 0xc2, 0x28, 0x59, + 0x10, 0xc6, 0x23, 0x36, 0x1f, 0xf7, 0xf6, 0x9d, 0xa9, 0x8b, 0x37, 0x8c, 0xfd, 0x74, 0x8e, 0xee, + 0xc3, 0x2d, 0x92, 0x24, 0xa2, 0xe0, 0x2a, 0xe2, 0x45, 0x16, 0xd3, 0x7c, 0xdc, 0xdf, 0x77, 0xa6, + 0x7d, 0xbc, 0x6d, 0xbd, 0xdf, 0x19, 0xa7, 0xff, 0x4b, 0x17, 0x86, 0x75, 0xbe, 0xd1, 0x03, 0x18, + 0x65, 0x54, 0x4a, 0x92, 0x1a, 0x45, 0xbd, 0xe9, 0xe6, 0xe1, 0x4e, 0x50, 0x57, 0x33, 0x68, 0xaa, + 0x19, 0x1c, 0xf1, 0x0a, 0xb7, 0x28, 0x84, 0xa0, 0x9f, 0xd1, 0xac, 0x2e, 0x8b, 0x8b, 0xcd, 0x59, + 0xbf, 0xab, 0x58, 0x46, 0x45, 0xa1, 0xa2, 0x05, 0x65, 0xe9, 0x42, 0x19, 0x61, 0x7d, 0xbc, 0x6d, + 0xbd, 0xc7, 0xc6, 0x89, 0x66, 0x70, 0x87, 0x96, 0x8a, 0x72, 0xc9, 0x04, 0x8f, 0xc4, 0x4a, 0x31, + 0xc1, 0xe5, 0xf8, 0xef, 0x8d, 0x6b, 0x9e, 0x7d, 0xaf, 0xc5, 0x7f, 0x5f, 0xc3, 0xd1, 0x0b, 0xf0, + 0xb8, 0xe0, 0x51, 0x92, 0x33, 0xc5, 0x12, 0xb2, 0x8c, 0xd6, 0x10, 0xde, 0xbe, 0x86, 0x70, 0x8f, + 0x0b, 0xfe, 0xd8, 0xc6, 0x7e, 0xf3, 0x2f, 0x6e, 0xff, 0x15, 0x8c, 0x9a, 0x96, 0x42, 0x5f, 0xc1, + 0x96, 0x2e, 0x23, 0xcd, 0x4d, 0x3d, 0x9a, 0xe4, 0xdc, 0x5d, 0xd3, 0x85, 0x27, 0x06, 0x66, 0xfa, + 0x70, 0x53, 0xb6, 0x67, 0x89, 0xa6, 0xd0, 0x3b, 0xa3, 0xd4, 0xb6, 0xef, 0xfb, 0x6b, 0x02, 0x9f, + 0x50, 0x8a, 0x35, 0xc4, 0xff, 0xd5, 0x01, 0xb8, 0x64, 0x41, 0x8f, 0x00, 0x56, 0x45, 0xbc, 0x64, + 0x49, 0xf4, 0x92, 0x36, 0x23, 0xb3, 0xfe, 0x6f, 0xdc, 0x1a, 0xf7, 0x8c, 0x9a, 0x91, 0xc9, 0xc4, + 0x9c, 0xde, 0x34, 0x32, 0xcf, 0xc5, 0x9c, 0xd6, 0x23, 0x93, 0xd9, 0x13, 0x9a, 0xc0, 0x48, 0xd2, + 0x1f, 0x0a, 0xca, 0x13, 0x6a, 0xcb, 0xd6, 0xda, 0xfe, 0xbb, 0x2e, 0x8c, 0x9a, 0x10, 0xf4, 0x05, + 0x0c, 0x25, 0xe3, 0xe9, 0x92, 0x5a, 0x4d, 0xfe, 0x35, 0xfc, 0xc1, 0x89, 0x41, 0x1e, 0x77, 0xb0, + 0x8d, 0x41, 0x9f, 0xc1, 0xc0, 0xec, 0x1f, 0x2b, 0xee, 0x83, 0xeb, 0x82, 0x9f, 0x6b, 0xe0, 0x71, + 0x07, 0xd7, 0x11, 0x93, 0x23, 0x18, 0xd6, 0x74, 0xe8, 0x13, 0xe8, 0x6b, 0xdd, 0x46, 0xc0, 0xad, + 0xc3, 0x0f, 0xaf, 0x70, 0x34, 0x1b, 0xe9, 0x6a, 0x55, 0x34, 0x1f, 0x36, 0x01, 0x93, 0xd7, 0x0e, + 0x0c, 0x0c, 0x2b, 0x7a, 0x06, 0xa3, 0x98, 0x29, 0x92, 0xe7, 0xa4, 0xc9, 0x6d, 0xd8, 0xd0, 0xd4, + 0x7b, 0x33, 0x68, 0xd7, 0x64, 0xc3, 0xf5, 0x58, 0x64, 0x2b, 0x92, 0xa8, 0x19, 0x53, 0x47, 0x3a, + 0x0c, 0xb7, 0x04, 0xe8, 0x73, 0x80, 0x36, 0xeb, 0x7a, 0x5c, 0x7b, 0x37, 0xa5, 0xdd, 0x6d, 0xd2, + 0x2e, 0x67, 0x03, 0xe8, 0xc9, 0x22, 0xf3, 0x7f, 0x77, 0xa0, 0xf7, 0x84, 0x52, 0x94, 0xc0, 0x90, + 0x64, 0x7a, 0x48, 0x6d, 0xab, 0xb5, 0x4b, 0x52, 0xaf, 0xe7, 0x2b, 0x52, 0x18, 0x9f, 0x3d, 0x78, + 0xf3, 0xe7, 0xbd, 0xce, 0x6f, 0x7f, 0xdd, 0x9b, 0xa6, 0x4c, 0x2d, 0x8a, 0x38, 0x48, 0x44, 0x16, + 0x36, 0xab, 0xdf, 0x7c, 0x0e, 0xe4, 0xfc, 0x65, 0xa8, 0xaa, 0x15, 0x95, 0x26, 0x40, 0x62, 0x4b, + 0x8d, 0xf6, 0xc0, 0x4d, 0x89, 0x8c, 0x96, 0x2c, 0x63, 0xca, 0x14, 0xa2, 0x8f, 0x47, 0x29, 0x91, + 0xdf, 0x6a, 0x1b, 0xed, 0xc0, 0x60, 0x45, 0x2a, 0x9a, 0xdb, 0xad, 0x52, 0x1b, 0x68, 0x0c, 0x1b, + 0x69, 0x4e, 0xb8, 0xb2, 0xcb, 0xc4, 0xc5, 0x8d, 0x39, 0xfb, 0xf2, 0xcd, 0xb9, 0xe7, 0xbc, 0x3d, + 0xf7, 0x9c, 0x77, 0xe7, 0x9e, 0xf3, 0xfa, 0xc2, 0xeb, 0xbc, 0xbd, 0xf0, 0x3a, 0x7f, 0x5c, 0x78, + 0x9d, 0x17, 0xf7, 0x6f, 0x16, 0x16, 0xaa, 0x32, 0x1e, 0x9a, 0x66, 0x7e, 0xf4, 0x4f, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xd4, 0xb7, 0x75, 0x7d, 0xfd, 0x06, 0x00, 0x00, +} + +func (m *Tx) 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 *Tx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Tx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.AuthInfo != nil { + { + size, err := m.AuthInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Body != nil { + { + size, err := m.Body.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 *TxRaw) 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 *TxRaw) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxRaw) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signatures) > 0 { + for iNdEx := len(m.Signatures) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Signatures[iNdEx]) + copy(dAtA[i:], m.Signatures[iNdEx]) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signatures[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.AuthInfoBytes) > 0 { + i -= len(m.AuthInfoBytes) + copy(dAtA[i:], m.AuthInfoBytes) + i = encodeVarintTx(dAtA, i, uint64(len(m.AuthInfoBytes))) + i-- + dAtA[i] = 0x12 + } + if len(m.BodyBytes) > 0 { + i -= len(m.BodyBytes) + copy(dAtA[i:], m.BodyBytes) + i = encodeVarintTx(dAtA, i, uint64(len(m.BodyBytes))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignDoc) 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 *SignDoc) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignDoc) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AccountNumber != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AccountNumber)) + i-- + dAtA[i] = 0x20 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x1a + } + if len(m.AuthInfoBytes) > 0 { + i -= len(m.AuthInfoBytes) + copy(dAtA[i:], m.AuthInfoBytes) + i = encodeVarintTx(dAtA, i, uint64(len(m.AuthInfoBytes))) + i-- + dAtA[i] = 0x12 + } + if len(m.BodyBytes) > 0 { + i -= len(m.BodyBytes) + copy(dAtA[i:], m.BodyBytes) + i = encodeVarintTx(dAtA, i, uint64(len(m.BodyBytes))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TxBody) 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 *TxBody) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxBody) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NonCriticalExtensionOptions) > 0 { + for iNdEx := len(m.NonCriticalExtensionOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.NonCriticalExtensionOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7f + i-- + dAtA[i] = 0xfa + } + } + if len(m.ExtensionOptions) > 0 { + for iNdEx := len(m.ExtensionOptions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExtensionOptions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3f + i-- + dAtA[i] = 0xfa + } + } + if m.TimeoutHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.TimeoutHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.Memo) > 0 { + i -= len(m.Memo) + copy(dAtA[i:], m.Memo) + i = encodeVarintTx(dAtA, i, uint64(len(m.Memo))) + i-- + dAtA[i] = 0x12 + } + if len(m.Messages) > 0 { + for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Messages[iNdEx].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 *AuthInfo) 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 *AuthInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AuthInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Fee != nil { + { + size, err := m.Fee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.SignerInfos) > 0 { + for iNdEx := len(m.SignerInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SignerInfos[iNdEx].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 *SignerInfo) 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 *SignerInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignerInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if m.ModeInfo != nil { + { + size, err := m.ModeInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.PublicKey != nil { + { + size, err := m.PublicKey.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 *ModeInfo) 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 *ModeInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModeInfo) 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 *ModeInfo_Single_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModeInfo_Single_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Single != nil { + { + size, err := m.Single.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 *ModeInfo_Multi_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModeInfo_Multi_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Multi != nil { + { + size, err := m.Multi.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *ModeInfo_Single) 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 *ModeInfo_Single) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModeInfo_Single) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Mode != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ModeInfo_Multi) 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 *ModeInfo_Multi) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModeInfo_Multi) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ModeInfos) > 0 { + for iNdEx := len(m.ModeInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ModeInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Bitarray != nil { + { + size, err := m.Bitarray.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 *Fee) 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 *Fee) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Fee) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + 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] = 0x22 + } + if len(m.Payer) > 0 { + i -= len(m.Payer) + copy(dAtA[i:], m.Payer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Payer))) + i-- + dAtA[i] = 0x1a + } + if m.GasLimit != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.GasLimit)) + i-- + dAtA[i] = 0x10 + } + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].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 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 *Tx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Body != nil { + l = m.Body.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.AuthInfo != nil { + l = m.AuthInfo.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *TxRaw) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.BodyBytes) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.AuthInfoBytes) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Signatures) > 0 { + for _, b := range m.Signatures { + l = len(b) + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *SignDoc) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.BodyBytes) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.AuthInfoBytes) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AccountNumber != 0 { + n += 1 + sovTx(uint64(m.AccountNumber)) + } + return n +} + +func (m *TxBody) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Messages) > 0 { + for _, e := range m.Messages { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Memo) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.TimeoutHeight != 0 { + n += 1 + sovTx(uint64(m.TimeoutHeight)) + } + if len(m.ExtensionOptions) > 0 { + for _, e := range m.ExtensionOptions { + l = e.Size() + n += 2 + l + sovTx(uint64(l)) + } + } + if len(m.NonCriticalExtensionOptions) > 0 { + for _, e := range m.NonCriticalExtensionOptions { + l = e.Size() + n += 2 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *AuthInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SignerInfos) > 0 { + for _, e := range m.SignerInfos { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.Fee != nil { + l = m.Fee.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *SignerInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ModeInfo != nil { + l = m.ModeInfo.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovTx(uint64(m.Sequence)) + } + return n +} + +func (m *ModeInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *ModeInfo_Single_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Single != nil { + l = m.Single.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *ModeInfo_Multi_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Multi != nil { + l = m.Multi.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} +func (m *ModeInfo_Single) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Mode != 0 { + n += 1 + sovTx(uint64(m.Mode)) + } + return n +} + +func (m *ModeInfo_Multi) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Bitarray != nil { + l = m.Bitarray.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.ModeInfos) > 0 { + for _, e := range m.ModeInfos { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *Fee) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.GasLimit != 0 { + n += 1 + sovTx(uint64(m.GasLimit)) + } + l = len(m.Payer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTx(uint64(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 *Tx) 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: Tx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Tx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", 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.Body == nil { + m.Body = &TxBody{} + } + if err := m.Body.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthInfo", 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.AuthInfo == nil { + m.AuthInfo = &AuthInfo{} + } + if err := m.AuthInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], 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 *TxRaw) 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: TxRaw: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxRaw: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BodyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BodyBytes = append(m.BodyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.BodyBytes == nil { + m.BodyBytes = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthInfoBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthInfoBytes = append(m.AuthInfoBytes[:0], dAtA[iNdEx:postIndex]...) + if m.AuthInfoBytes == nil { + m.AuthInfoBytes = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signatures", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signatures = append(m.Signatures, make([]byte, postIndex-iNdEx)) + copy(m.Signatures[len(m.Signatures)-1], 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 *SignDoc) 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: SignDoc: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignDoc: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BodyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BodyBytes = append(m.BodyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.BodyBytes == nil { + m.BodyBytes = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthInfoBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthInfoBytes = append(m.AuthInfoBytes[:0], dAtA[iNdEx:postIndex]...) + if m.AuthInfoBytes == nil { + m.AuthInfoBytes = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountNumber", wireType) + } + m.AccountNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AccountNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *TxBody) 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: TxBody: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxBody: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Messages", 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.Messages = append(m.Messages, &types.Any{}) + if err := m.Messages[len(m.Messages)-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 Memo", 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.Memo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutHeight", wireType) + } + m.TimeoutHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeoutHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 1023: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionOptions", 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.ExtensionOptions = append(m.ExtensionOptions, &types.Any{}) + if err := m.ExtensionOptions[len(m.ExtensionOptions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2047: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NonCriticalExtensionOptions", 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.NonCriticalExtensionOptions = append(m.NonCriticalExtensionOptions, &types.Any{}) + if err := m.NonCriticalExtensionOptions[len(m.NonCriticalExtensionOptions)-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 *AuthInfo) 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: AuthInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AuthInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignerInfos", 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.SignerInfos = append(m.SignerInfos, &SignerInfo{}) + if err := m.SignerInfos[len(m.SignerInfos)-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 Fee", 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.Fee == nil { + m.Fee = &Fee{} + } + if err := m.Fee.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 *SignerInfo) 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: SignerInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignerInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", 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.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModeInfo", 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.ModeInfo == nil { + m.ModeInfo = &ModeInfo{} + } + if err := m.ModeInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *ModeInfo) 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: ModeInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ModeInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Single", 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 + } + v := &ModeInfo_Single{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &ModeInfo_Single_{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Multi", 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 + } + v := &ModeInfo_Multi{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &ModeInfo_Multi_{v} + 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 *ModeInfo_Single) 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: Single: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Single: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= signing.SignMode(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *ModeInfo_Multi) 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: Multi: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Multi: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bitarray", 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.Bitarray == nil { + m.Bitarray = &types1.CompactBitArray{} + } + if err := m.Bitarray.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModeInfos", 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.ModeInfos = append(m.ModeInfos, &ModeInfo{}) + if err := m.ModeInfos[len(m.ModeInfos)-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 *Fee) 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: Fee: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Fee: 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) + } + 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.Amount = append(m.Amount, types2.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasLimit", wireType) + } + m.GasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payer", 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.Payer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + 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 + 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/types/tx/types.go b/types/tx/types.go new file mode 100644 index 000000000000..0392b2fcfd08 --- /dev/null +++ b/types/tx/types.go @@ -0,0 +1,199 @@ +package tx + +import ( + "fmt" + "strings" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// MaxGasWanted defines the max gas allowed. +const MaxGasWanted = uint64((1 << 63) - 1) + +// Interface implementation checks. +var _, _, _, _ codectypes.UnpackInterfacesMessage = &Tx{}, &TxBody{}, &AuthInfo{}, &SignerInfo{} +var _ sdk.Tx = &Tx{} + +// GetMsgs implements the GetMsgs method on sdk.Tx. +func (t *Tx) GetMsgs() []sdk.Msg { + if t == nil || t.Body == nil { + return nil + } + + anys := t.Body.Messages + res := make([]sdk.Msg, len(anys)) + for i, any := range anys { + var msg sdk.Msg + if isServiceMsg(any.TypeUrl) { + req := any.GetCachedValue() + if req == nil { + panic("Any cached value is nil. Transaction messages must be correctly packed Any values.") + } + msg = sdk.ServiceMsg{ + MethodName: any.TypeUrl, + Request: any.GetCachedValue().(sdk.MsgRequest), + } + } else { + msg = any.GetCachedValue().(sdk.Msg) + } + res[i] = msg + } + return res +} + +// ValidateBasic implements the ValidateBasic method on sdk.Tx. +func (t *Tx) ValidateBasic() error { + if t == nil { + return fmt.Errorf("bad Tx") + } + + body := t.Body + if body == nil { + return fmt.Errorf("missing TxBody") + } + + authInfo := t.AuthInfo + if authInfo == nil { + return fmt.Errorf("missing AuthInfo") + } + + fee := authInfo.Fee + if fee == nil { + return fmt.Errorf("missing fee") + } + + if fee.GasLimit > MaxGasWanted { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidRequest, + "invalid gas supplied; %d > %d", fee.GasLimit, MaxGasWanted, + ) + } + + if fee.Amount.IsAnyNegative() { + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFee, + "invalid fee provided: %s", fee.Amount, + ) + } + + if fee.Payer != "" { + _, err := sdk.AccAddressFromBech32(fee.Payer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid fee payer address (%s)", err) + } + } + + sigs := t.Signatures + + if len(sigs) == 0 { + return sdkerrors.ErrNoSignatures + } + + if len(sigs) != len(t.GetSigners()) { + return sdkerrors.Wrapf( + sdkerrors.ErrUnauthorized, + "wrong number of signers; expected %d, got %d", len(t.GetSigners()), len(sigs), + ) + } + + return nil +} + +// GetSigners retrieves all the signers of a tx. +// This includes all unique signers of the messages (in order), +// as well as the FeePayer (if specified and not already included). +func (t *Tx) GetSigners() []sdk.AccAddress { + var signers []sdk.AccAddress + seen := map[string]bool{} + + for _, msg := range t.GetMsgs() { + for _, addr := range msg.GetSigners() { + if !seen[addr.String()] { + signers = append(signers, addr) + seen[addr.String()] = true + } + } + } + + // ensure any specified fee payer is included in the required signers (at the end) + feePayer := t.AuthInfo.Fee.Payer + if feePayer != "" && !seen[feePayer] { + payerAddr, err := sdk.AccAddressFromBech32(feePayer) + if err != nil { + panic(err) + } + signers = append(signers, payerAddr) + seen[feePayer] = true + } + + return signers +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (t *Tx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + if t.Body != nil { + if err := t.Body.UnpackInterfaces(unpacker); err != nil { + return err + } + } + + if t.AuthInfo != nil { + return t.AuthInfo.UnpackInterfaces(unpacker) + } + + return nil +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (m *TxBody) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, any := range m.Messages { + // If the any's typeUrl contains 2 slashes, then we unpack the any into + // a ServiceMsg struct as per ADR-031. + if isServiceMsg(any.TypeUrl) { + var req sdk.MsgRequest + err := unpacker.UnpackAny(any, &req) + if err != nil { + return err + } + } else { + var msg sdk.Msg + err := unpacker.UnpackAny(any, &msg) + if err != nil { + return err + } + } + } + + return nil +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (m *AuthInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, signerInfo := range m.SignerInfos { + err := signerInfo.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (m *SignerInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(m.PublicKey, new(cryptotypes.PubKey)) +} + +// RegisterInterfaces registers the sdk.Tx interface. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface("cosmos.tx.v1beta1.Tx", (*sdk.Tx)(nil)) + registry.RegisterImplementations((*sdk.Tx)(nil), &Tx{}) +} + +// isServiceMsg checks if a type URL corresponds to a service method name, +// i.e. /cosmos.bank.Msg/Send vs /cosmos.bank.MsgSend +func isServiceMsg(typeURL string) bool { + return strings.Count(typeURL, "/") >= 2 +} diff --git a/types/tx_msg.go b/types/tx_msg.go index 1b0da612b736..b8a602d88c05 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -1,79 +1,87 @@ package types import ( - "encoding/json" + "github.com/gogo/protobuf/proto" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) -// Transactions messages must fulfill the Msg -type Msg interface { +type ( + // Msg defines the interface a transaction message must fulfill. + Msg interface { + proto.Message - // Return the message type. - // Must be alphanumeric or empty. - Route() string + // Return the message type. + // Must be alphanumeric or empty. + Route() string - // Returns a human-readable string for the message, intended for utilization - // within tags - Type() string + // Returns a human-readable string for the message, intended for utilization + // within tags + Type() string - // ValidateBasic does a simple validation check that - // doesn't require access to any other information. - ValidateBasic() error + // ValidateBasic does a simple validation check that + // doesn't require access to any other information. + ValidateBasic() error - // Get the canonical byte representation of the Msg. - GetSignBytes() []byte + // Get the canonical byte representation of the Msg. + GetSignBytes() []byte - // Signers returns the addrs of signers that must sign. - // CONTRACT: All signatures must be present to be valid. - // CONTRACT: Returns addrs in some deterministic order. - GetSigners() []AccAddress -} + // Signers returns the addrs of signers that must sign. + // CONTRACT: All signatures must be present to be valid. + // CONTRACT: Returns addrs in some deterministic order. + GetSigners() []AccAddress + } -//__________________________________________________________ + // Fee defines an interface for an application application-defined concrete + // transaction type to be able to set and return the transaction fee. + Fee interface { + GetGas() uint64 + GetAmount() Coins + } -// Transactions objects must fulfill the Tx -type Tx interface { - // Gets the all the transaction's messages. - GetMsgs() []Msg + // Signature defines an interface for an application application-defined + // concrete transaction type to be able to set and return transaction signatures. + Signature interface { + GetPubKey() cryptotypes.PubKey + GetSignature() []byte + } - // ValidateBasic does a simple and lightweight validation check that doesn't - // require access to any other information. - ValidateBasic() error -} + // Tx defines the interface a transaction must fulfill. + Tx interface { + // Gets the all the transaction's messages. + GetMsgs() []Msg -//__________________________________________________________ + // ValidateBasic does a simple and lightweight validation check that doesn't + // require access to any other information. + ValidateBasic() error + } -// TxDecoder unmarshals transaction bytes -type TxDecoder func(txBytes []byte) (Tx, error) + // FeeTx defines the interface to be implemented by Tx to use the FeeDecorators + FeeTx interface { + Tx + GetGas() uint64 + GetFee() Coins + FeePayer() AccAddress + FeeGranter() AccAddress + } -// TxEncoder marshals transaction to bytes -type TxEncoder func(tx Tx) ([]byte, error) + // Tx must have GetMemo() method to use ValidateMemoDecorator + TxWithMemo interface { + Tx + GetMemo() string + } -//__________________________________________________________ + // TxWithTimeoutHeight extends the Tx interface by allowing a transaction to + // set a height timeout. + TxWithTimeoutHeight interface { + Tx -var _ Msg = (*TestMsg)(nil) + GetTimeoutHeight() uint64 + } +) -// msg type for testing -type TestMsg struct { - signers []AccAddress -} +// TxDecoder unmarshals transaction bytes +type TxDecoder func(txBytes []byte) (Tx, error) -func NewTestMsg(addrs ...AccAddress) *TestMsg { - return &TestMsg{ - signers: addrs, - } -} - -//nolint -func (msg *TestMsg) Route() string { return "TestMsg" } -func (msg *TestMsg) Type() string { return "Test message" } -func (msg *TestMsg) GetSignBytes() []byte { - bz, err := json.Marshal(msg.signers) - if err != nil { - panic(err) - } - return MustSortJSON(bz) -} -func (msg *TestMsg) ValidateBasic() error { return nil } -func (msg *TestMsg) GetSigners() []AccAddress { - return msg.signers -} +// TxEncoder marshals transaction to bytes +type TxEncoder func(tx Tx) ([]byte, error) diff --git a/types/tx_msg_test.go b/types/tx_msg_test.go new file mode 100644 index 000000000000..7a55c7d63e64 --- /dev/null +++ b/types/tx_msg_test.go @@ -0,0 +1,31 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type testMsgSuite struct { + suite.Suite +} + +func TestMsgTestSuite(t *testing.T) { + suite.Run(t, new(testMsgSuite)) +} + +func (s *testMsgSuite) TestMsg() { + addr := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} + accAddr := sdk.AccAddress(addr) + + msg := testdata.NewTestMsg(accAddr) + s.Require().NotNil(msg) + s.Require().Equal([]sdk.AccAddress{accAddr}, msg.GetSigners()) + s.Require().Equal("TestMsg", msg.Route()) + s.Require().Equal("Test message", msg.Type()) + s.Require().Nil(msg.ValidateBasic()) + s.Require().NotPanics(func() { msg.GetSignBytes() }) +} diff --git a/types/uint.go b/types/uint.go index 976b6414a93f..2305d7386728 100644 --- a/types/uint.go +++ b/types/uint.go @@ -49,6 +49,8 @@ func ZeroUint() Uint { return Uint{big.NewInt(0)} } // OneUint returns Uint value with one. func OneUint() Uint { return Uint{big.NewInt(1)} } +var _ CustomProtobufType = (*Uint)(nil) + // Uint64 converts Uint to uint64 // Panics if the value is out of range func (u Uint) Uint64() uint64 { @@ -130,38 +132,81 @@ func MaxUint(u1, u2 Uint) Uint { return NewUintFromBigInt(max(u1.i, u2.i)) } // Human readable string func (u Uint) String() string { return u.i.String() } -// MarshalAmino defines custom encoding scheme -func (u Uint) MarshalAmino() (string, error) { +// MarshalJSON defines custom encoding scheme +func (u Uint) MarshalJSON() ([]byte, error) { if u.i == nil { // Necessary since default Uint initialization has i.i as nil u.i = new(big.Int) } - return marshalAmino(u.i) + return marshalJSON(u.i) } -// UnmarshalAmino defines custom decoding scheme -func (u *Uint) UnmarshalAmino(text string) error { +// UnmarshalJSON defines custom decoding scheme +func (u *Uint) UnmarshalJSON(bz []byte) error { if u.i == nil { // Necessary since default Uint initialization has i.i as nil u.i = new(big.Int) } - return unmarshalAmino(u.i, text) + return unmarshalJSON(u.i, bz) } -// MarshalJSON defines custom encoding scheme -func (u Uint) MarshalJSON() ([]byte, error) { - if u.i == nil { // Necessary since default Uint initialization has i.i as nil +// Marshal implements the gogo proto custom type interface. +func (u Uint) Marshal() ([]byte, error) { + if u.i == nil { u.i = new(big.Int) } - return marshalJSON(u.i) + return u.i.MarshalText() } -// UnmarshalJSON defines custom decoding scheme -func (u *Uint) UnmarshalJSON(bz []byte) error { - if u.i == nil { // Necessary since default Uint initialization has i.i as nil +// MarshalTo implements the gogo proto custom type interface. +func (u *Uint) MarshalTo(data []byte) (n int, err error) { + if u.i == nil { u.i = new(big.Int) } - return unmarshalJSON(u.i, bz) + if len(u.i.Bytes()) == 0 { + copy(data, []byte{0x30}) + return 1, nil + } + + bz, err := u.Marshal() + if err != nil { + return 0, err + } + + copy(data, bz) + return len(bz), nil } +// Unmarshal implements the gogo proto custom type interface. +func (u *Uint) Unmarshal(data []byte) error { + if len(data) == 0 { + u = nil + return nil + } + + if u.i == nil { + u.i = new(big.Int) + } + + if err := u.i.UnmarshalText(data); err != nil { + return err + } + + if u.i.BitLen() > maxBitLen { + return fmt.Errorf("integer out of range; got: %d, max: %d", u.i.BitLen(), maxBitLen) + } + + return nil +} + +// Size implements the gogo proto custom type interface. +func (u *Uint) Size() int { + bz, _ := u.Marshal() + return len(bz) +} + +// Override Amino binary serialization by proxying to protobuf. +func (u Uint) MarshalAmino() ([]byte, error) { return u.Marshal() } +func (u *Uint) UnmarshalAmino(bz []byte) error { return u.Unmarshal(bz) } + //__________________________________________________________________________ // UintOverflow returns true if a given unsigned integer overflows and false diff --git a/types/uint_internal_test.go b/types/uint_internal_test.go new file mode 100644 index 000000000000..a39869cac759 --- /dev/null +++ b/types/uint_internal_test.go @@ -0,0 +1,55 @@ +package types + +import ( + "math/big" + "math/rand" + "strconv" + "testing" + + "github.com/stretchr/testify/suite" +) + +type uintInternalTestSuite struct { + suite.Suite +} + +func TestUintInternalTestSuite(t *testing.T) { + suite.Run(t, new(uintInternalTestSuite)) +} + +func (s *uintInternalTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *uintInternalTestSuite) TestIdentUint() { + for d := 0; d < 1000; d++ { + n := rand.Uint64() + i := NewUint(n) + + ifromstr := NewUintFromString(strconv.FormatUint(n, 10)) + + cases := []uint64{ + i.Uint64(), + i.BigInt().Uint64(), + i.i.Uint64(), + ifromstr.Uint64(), + NewUintFromBigInt(new(big.Int).SetUint64(n)).Uint64(), + } + + for tcnum, tc := range cases { + s.Require().Equal(n, tc, "Uint is modified during conversion. tc #%d", tcnum) + } + } +} + +func (s *uintInternalTestSuite) TestUintSize() { + x := Uint{i: nil} + s.Require().Equal(1, x.Size()) + x = NewUint(0) + s.Require().Equal(1, x.Size()) + x = NewUint(10) + s.Require().Equal(2, x.Size()) + x = NewUint(100) + s.Require().Equal(3, x.Size()) + +} diff --git a/types/uint_test.go b/types/uint_test.go index 548dea5064eb..36705c890a19 100644 --- a/types/uint_test.go +++ b/types/uint_test.go @@ -1,112 +1,104 @@ -package types +package types_test import ( "math" "math/big" "math/rand" - "strconv" "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestUintPanics(t *testing.T) { +type uintTestSuite struct { + suite.Suite +} + +func TestUnitTestSuite(t *testing.T) { + suite.Run(t, new(uintTestSuite)) +} + +func (s *uintTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *uintTestSuite) TestUintPanics() { // Max Uint = 1.15e+77 // Min Uint = 0 - u1 := NewUint(0) - u2 := OneUint() + u1 := sdk.NewUint(0) + u2 := sdk.OneUint() - require.Equal(t, uint64(0), u1.Uint64()) - require.Equal(t, uint64(1), u2.Uint64()) + s.Require().Equal(uint64(0), u1.Uint64()) + s.Require().Equal(uint64(1), u2.Uint64()) - require.Panics(t, func() { NewUintFromBigInt(big.NewInt(-5)) }) - require.Panics(t, func() { NewUintFromString("-1") }) - require.NotPanics(t, func() { - require.True(t, NewUintFromString("0").Equal(ZeroUint())) - require.True(t, NewUintFromString("5").Equal(NewUint(5))) + s.Require().Panics(func() { sdk.NewUintFromBigInt(big.NewInt(-5)) }) + s.Require().Panics(func() { sdk.NewUintFromString("-1") }) + s.Require().NotPanics(func() { + s.Require().True(sdk.NewUintFromString("0").Equal(sdk.ZeroUint())) + s.Require().True(sdk.NewUintFromString("5").Equal(sdk.NewUint(5))) }) // Overflow check - require.True(t, u1.Add(u1).Equal(ZeroUint())) - require.True(t, u1.Add(OneUint()).Equal(OneUint())) - require.Equal(t, uint64(0), u1.Uint64()) - require.Equal(t, uint64(1), OneUint().Uint64()) - require.Panics(t, func() { u1.SubUint64(2) }) - require.True(t, u1.SubUint64(0).Equal(ZeroUint())) - require.True(t, u2.Add(OneUint()).Sub(OneUint()).Equal(OneUint())) // i2 == 1 - require.True(t, u2.Add(OneUint()).Mul(NewUint(5)).Equal(NewUint(10))) // i2 == 10 - require.True(t, NewUint(7).Quo(NewUint(2)).Equal(NewUint(3))) - require.True(t, NewUint(0).Quo(NewUint(2)).Equal(ZeroUint())) - require.True(t, NewUint(5).MulUint64(4).Equal(NewUint(20))) - require.True(t, NewUint(5).MulUint64(0).Equal(ZeroUint())) - - uintmax := NewUintFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(1))) - uintmin := ZeroUint() + s.Require().True(u1.Add(u1).Equal(sdk.ZeroUint())) + s.Require().True(u1.Add(sdk.OneUint()).Equal(sdk.OneUint())) + s.Require().Equal(uint64(0), u1.Uint64()) + s.Require().Equal(uint64(1), sdk.OneUint().Uint64()) + s.Require().Panics(func() { u1.SubUint64(2) }) + s.Require().True(u1.SubUint64(0).Equal(sdk.ZeroUint())) + s.Require().True(u2.Add(sdk.OneUint()).Sub(sdk.OneUint()).Equal(sdk.OneUint())) // i2 == 1 + s.Require().True(u2.Add(sdk.OneUint()).Mul(sdk.NewUint(5)).Equal(sdk.NewUint(10))) // i2 == 10 + s.Require().True(sdk.NewUint(7).Quo(sdk.NewUint(2)).Equal(sdk.NewUint(3))) + s.Require().True(sdk.NewUint(0).Quo(sdk.NewUint(2)).Equal(sdk.ZeroUint())) + s.Require().True(sdk.NewUint(5).MulUint64(4).Equal(sdk.NewUint(20))) + s.Require().True(sdk.NewUint(5).MulUint64(0).Equal(sdk.ZeroUint())) + + uintmax := sdk.NewUintFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(1))) + uintmin := sdk.ZeroUint() // divs by zero - require.Panics(t, func() { OneUint().Mul(ZeroUint().SubUint64(uint64(1))) }) - require.Panics(t, func() { OneUint().QuoUint64(0) }) - require.Panics(t, func() { OneUint().Quo(ZeroUint()) }) - require.Panics(t, func() { ZeroUint().QuoUint64(0) }) - require.Panics(t, func() { OneUint().Quo(ZeroUint().Sub(OneUint())) }) - require.Panics(t, func() { uintmax.Add(OneUint()) }) - require.Panics(t, func() { uintmax.Incr() }) - require.Panics(t, func() { uintmin.Sub(OneUint()) }) - require.Panics(t, func() { uintmin.Decr() }) - - require.Equal(t, uint64(0), MinUint(ZeroUint(), OneUint()).Uint64()) - require.Equal(t, uint64(1), MaxUint(ZeroUint(), OneUint()).Uint64()) + s.Require().Panics(func() { sdk.OneUint().Mul(sdk.ZeroUint().SubUint64(uint64(1))) }) + s.Require().Panics(func() { sdk.OneUint().QuoUint64(0) }) + s.Require().Panics(func() { sdk.OneUint().Quo(sdk.ZeroUint()) }) + s.Require().Panics(func() { sdk.ZeroUint().QuoUint64(0) }) + s.Require().Panics(func() { sdk.OneUint().Quo(sdk.ZeroUint().Sub(sdk.OneUint())) }) + s.Require().Panics(func() { uintmax.Add(sdk.OneUint()) }) + s.Require().Panics(func() { uintmax.Incr() }) + s.Require().Panics(func() { uintmin.Sub(sdk.OneUint()) }) + s.Require().Panics(func() { uintmin.Decr() }) + + s.Require().Equal(uint64(0), sdk.MinUint(sdk.ZeroUint(), sdk.OneUint()).Uint64()) + s.Require().Equal(uint64(1), sdk.MaxUint(sdk.ZeroUint(), sdk.OneUint()).Uint64()) // comparison ops - require.True(t, - OneUint().GT(ZeroUint()), + s.Require().True( + sdk.OneUint().GT(sdk.ZeroUint()), ) - require.False(t, - OneUint().LT(ZeroUint()), + s.Require().False( + sdk.OneUint().LT(sdk.ZeroUint()), ) - require.True(t, - OneUint().GTE(ZeroUint()), + s.Require().True( + sdk.OneUint().GTE(sdk.ZeroUint()), ) - require.False(t, - OneUint().LTE(ZeroUint()), + s.Require().False( + sdk.OneUint().LTE(sdk.ZeroUint()), ) - require.False(t, ZeroUint().GT(OneUint())) - require.True(t, ZeroUint().LT(OneUint())) - require.False(t, ZeroUint().GTE(OneUint())) - require.True(t, ZeroUint().LTE(OneUint())) -} - -func TestIdentUint(t *testing.T) { - for d := 0; d < 1000; d++ { - n := rand.Uint64() - i := NewUint(n) - - ifromstr := NewUintFromString(strconv.FormatUint(n, 10)) - - cases := []uint64{ - i.Uint64(), - i.BigInt().Uint64(), - i.i.Uint64(), - ifromstr.Uint64(), - NewUintFromBigInt(new(big.Int).SetUint64(n)).Uint64(), - } - - for tcnum, tc := range cases { - require.Equal(t, n, tc, "Uint is modified during conversion. tc #%d", tcnum) - } - } + s.Require().False(sdk.ZeroUint().GT(sdk.OneUint())) + s.Require().True(sdk.ZeroUint().LT(sdk.OneUint())) + s.Require().False(sdk.ZeroUint().GTE(sdk.OneUint())) + s.Require().True(sdk.ZeroUint().LTE(sdk.OneUint())) } -func TestArithUint(t *testing.T) { +func (s *uintTestSuite) TestArithUint() { for d := 0; d < 1000; d++ { n1 := uint64(rand.Uint32()) - u1 := NewUint(n1) + u1 := sdk.NewUint(n1) n2 := uint64(rand.Uint32()) - u2 := NewUint(n2) + u2 := sdk.NewUint(n2) cases := []struct { - ures Uint + ures sdk.Uint nres uint64 }{ {u1.Add(u2), n1 + n2}, @@ -115,22 +107,22 @@ func TestArithUint(t *testing.T) { {u1.AddUint64(n2), n1 + n2}, {u1.MulUint64(n2), n1 * n2}, {u1.QuoUint64(n2), n1 / n2}, - {MinUint(u1, u2), minuint(n1, n2)}, - {MaxUint(u1, u2), maxuint(n1, n2)}, + {sdk.MinUint(u1, u2), minuint(n1, n2)}, + {sdk.MaxUint(u1, u2), maxuint(n1, n2)}, {u1.Incr(), n1 + 1}, } for tcnum, tc := range cases { - require.Equal(t, tc.nres, tc.ures.Uint64(), "Uint arithmetic operation does not match with uint64 operation. tc #%d", tcnum) + s.Require().Equal(tc.nres, tc.ures.Uint64(), "Uint arithmetic operation does not match with uint64 operation. tc #%d", tcnum) } if n2 > n1 { n1, n2 = n2, n1 - u1, u2 = NewUint(n1), NewUint(n2) + u1, u2 = sdk.NewUint(n1), sdk.NewUint(n2) } subs := []struct { - ures Uint + ures sdk.Uint nres uint64 }{ {u1.Sub(u2), n1 - n2}, @@ -139,17 +131,17 @@ func TestArithUint(t *testing.T) { } for tcnum, tc := range subs { - require.Equal(t, tc.nres, tc.ures.Uint64(), "Uint subtraction does not match with uint64 operation. tc #%d", tcnum) + s.Require().Equal(tc.nres, tc.ures.Uint64(), "Uint subtraction does not match with uint64 operation. tc #%d", tcnum) } } } -func TestCompUint(t *testing.T) { +func (s *uintTestSuite) TestCompUint() { for d := 0; d < 10000; d++ { n1 := rand.Uint64() - i1 := NewUint(n1) + i1 := sdk.NewUint(n1) n2 := rand.Uint64() - i2 := NewUint(n2) + i2 := sdk.NewUint(n2) cases := []struct { ires bool @@ -165,30 +157,30 @@ func TestCompUint(t *testing.T) { } for tcnum, tc := range cases { - require.Equal(t, tc.nres, tc.ires, "Uint comparison operation does not match with uint64 operation. tc #%d", tcnum) + s.Require().Equal(tc.nres, tc.ires, "Uint comparison operation does not match with uint64 operation. tc #%d", tcnum) } } } -func TestImmutabilityAllUint(t *testing.T) { - ops := []func(*Uint){ - func(i *Uint) { _ = i.Add(NewUint(rand.Uint64())) }, - func(i *Uint) { _ = i.Sub(NewUint(rand.Uint64() % i.Uint64())) }, - func(i *Uint) { _ = i.Mul(randuint()) }, - func(i *Uint) { _ = i.Quo(randuint()) }, - func(i *Uint) { _ = i.AddUint64(rand.Uint64()) }, - func(i *Uint) { _ = i.SubUint64(rand.Uint64() % i.Uint64()) }, - func(i *Uint) { _ = i.MulUint64(rand.Uint64()) }, - func(i *Uint) { _ = i.QuoUint64(rand.Uint64()) }, - func(i *Uint) { _ = i.IsZero() }, - func(i *Uint) { _ = i.Equal(randuint()) }, - func(i *Uint) { _ = i.GT(randuint()) }, - func(i *Uint) { _ = i.GTE(randuint()) }, - func(i *Uint) { _ = i.LT(randuint()) }, - func(i *Uint) { _ = i.LTE(randuint()) }, - func(i *Uint) { _ = i.String() }, - func(i *Uint) { _ = i.Incr() }, - func(i *Uint) { +func (s *uintTestSuite) TestImmutabilityAllUint() { + ops := []func(*sdk.Uint){ + func(i *sdk.Uint) { _ = i.Add(sdk.NewUint(rand.Uint64())) }, + func(i *sdk.Uint) { _ = i.Sub(sdk.NewUint(rand.Uint64() % i.Uint64())) }, + func(i *sdk.Uint) { _ = i.Mul(randuint()) }, + func(i *sdk.Uint) { _ = i.Quo(randuint()) }, + func(i *sdk.Uint) { _ = i.AddUint64(rand.Uint64()) }, + func(i *sdk.Uint) { _ = i.SubUint64(rand.Uint64() % i.Uint64()) }, + func(i *sdk.Uint) { _ = i.MulUint64(rand.Uint64()) }, + func(i *sdk.Uint) { _ = i.QuoUint64(rand.Uint64()) }, + func(i *sdk.Uint) { _ = i.IsZero() }, + func(i *sdk.Uint) { _ = i.Equal(randuint()) }, + func(i *sdk.Uint) { _ = i.GT(randuint()) }, + func(i *sdk.Uint) { _ = i.GTE(randuint()) }, + func(i *sdk.Uint) { _ = i.LT(randuint()) }, + func(i *sdk.Uint) { _ = i.LTE(randuint()) }, + func(i *sdk.Uint) { _ = i.String() }, + func(i *sdk.Uint) { _ = i.Incr() }, + func(i *sdk.Uint) { if i.IsZero() { return } @@ -199,91 +191,102 @@ func TestImmutabilityAllUint(t *testing.T) { for i := 0; i < 1000; i++ { n := rand.Uint64() - ni := NewUint(n) + ni := sdk.NewUint(n) for opnum, op := range ops { op(&ni) - require.Equal(t, n, ni.Uint64(), "Uint is modified by operation. #%d", opnum) - require.Equal(t, NewUint(n), ni, "Uint is modified by operation. #%d", opnum) + s.Require().Equal(n, ni.Uint64(), "Uint is modified by operation. #%d", opnum) + s.Require().Equal(sdk.NewUint(n), ni, "Uint is modified by operation. #%d", opnum) } } } -func TestSafeSub(t *testing.T) { +func (s *uintTestSuite) TestSafeSub() { testCases := []struct { - x, y Uint + x, y sdk.Uint expected uint64 panic bool }{ - {NewUint(0), NewUint(0), 0, false}, - {NewUint(10), NewUint(5), 5, false}, - {NewUint(5), NewUint(10), 5, true}, - {NewUint(math.MaxUint64), NewUint(0), math.MaxUint64, false}, + {sdk.NewUint(0), sdk.NewUint(0), 0, false}, + {sdk.NewUint(10), sdk.NewUint(5), 5, false}, + {sdk.NewUint(5), sdk.NewUint(10), 5, true}, + {sdk.NewUint(math.MaxUint64), sdk.NewUint(0), math.MaxUint64, false}, } for i, tc := range testCases { tc := tc if tc.panic { - require.Panics(t, func() { tc.x.Sub(tc.y) }) + s.Require().Panics(func() { tc.x.Sub(tc.y) }) continue } - require.Equal( - t, tc.expected, tc.x.Sub(tc.y).Uint64(), + s.Require().Equal( + tc.expected, tc.x.Sub(tc.y).Uint64(), "invalid subtraction result; x: %s, y: %s, tc: #%d", tc.x, tc.y, i, ) } } -func TestParseUint(t *testing.T) { +func (s *uintTestSuite) TestParseUint() { type args struct { s string } tests := []struct { name string args args - want Uint + want sdk.Uint wantErr bool }{ - {"malformed", args{"malformed"}, Uint{}, true}, - {"empty", args{""}, Uint{}, true}, - {"positive", args{"50"}, NewUint(uint64(50)), false}, - {"negative", args{"-1"}, Uint{}, true}, - {"zero", args{"0"}, ZeroUint(), false}, + {"malformed", args{"malformed"}, sdk.Uint{}, true}, + {"empty", args{""}, sdk.Uint{}, true}, + {"positive", args{"50"}, sdk.NewUint(uint64(50)), false}, + {"negative", args{"-1"}, sdk.Uint{}, true}, + {"zero", args{"0"}, sdk.ZeroUint(), false}, } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := ParseUint(tt.args.s) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.True(t, got.Equal(tt.want)) - }) + got, err := sdk.ParseUint(tt.args.s) + if tt.wantErr { + s.Require().Error(err) + continue + } + s.Require().NoError(err) + s.Require().True(got.Equal(tt.want)) } } -func randuint() Uint { - return NewUint(rand.Uint64()) +func randuint() sdk.Uint { + return sdk.NewUint(rand.Uint64()) } -func TestRelativePow(t *testing.T) { +func (s *uintTestSuite) TestRelativePow() { tests := []struct { - args []Uint - want Uint + args []sdk.Uint + want sdk.Uint }{ - {[]Uint{ZeroUint(), ZeroUint(), OneUint()}, OneUint()}, - {[]Uint{ZeroUint(), ZeroUint(), NewUint(10)}, NewUint(10)}, - {[]Uint{ZeroUint(), OneUint(), NewUint(10)}, ZeroUint()}, - {[]Uint{NewUint(10), NewUint(2), OneUint()}, NewUint(100)}, - {[]Uint{NewUint(210), NewUint(2), NewUint(100)}, NewUint(441)}, - {[]Uint{NewUint(2100), NewUint(2), NewUint(1000)}, NewUint(4410)}, - {[]Uint{NewUint(1000000001547125958), NewUint(600), NewUint(1000000000000000000)}, NewUint(1000000928276004850)}, + {[]sdk.Uint{sdk.ZeroUint(), sdk.ZeroUint(), sdk.OneUint()}, sdk.OneUint()}, + {[]sdk.Uint{sdk.ZeroUint(), sdk.ZeroUint(), sdk.NewUint(10)}, sdk.NewUint(10)}, + {[]sdk.Uint{sdk.ZeroUint(), sdk.OneUint(), sdk.NewUint(10)}, sdk.ZeroUint()}, + {[]sdk.Uint{sdk.NewUint(10), sdk.NewUint(2), sdk.OneUint()}, sdk.NewUint(100)}, + {[]sdk.Uint{sdk.NewUint(210), sdk.NewUint(2), sdk.NewUint(100)}, sdk.NewUint(441)}, + {[]sdk.Uint{sdk.NewUint(2100), sdk.NewUint(2), sdk.NewUint(1000)}, sdk.NewUint(4410)}, + {[]sdk.Uint{sdk.NewUint(1000000001547125958), sdk.NewUint(600), sdk.NewUint(1000000000000000000)}, sdk.NewUint(1000000928276004850)}, } for i, tc := range tests { - res := RelativePow(tc.args[0], tc.args[1], tc.args[2]) - require.Equal(t, tc.want, res, "unexpected result for test case %d, input: %v, got: %v", i, tc.args, res) + res := sdk.RelativePow(tc.args[0], tc.args[1], tc.args[2]) + s.Require().Equal(tc.want, res, "unexpected result for test case %d, input: %v, got: %v", i, tc.args, res) + } +} + +func minuint(i1, i2 uint64) uint64 { + if i1 < i2 { + return i1 + } + return i2 +} + +func maxuint(i1, i2 uint64) uint64 { + if i1 > i2 { + return i1 } + return i2 } diff --git a/types/utils.go b/types/utils.go index 2a6add6b45b4..cca98edf0ab7 100644 --- a/types/utils.go +++ b/types/utils.go @@ -12,8 +12,15 @@ import ( var ( // This is set at compile time. Could be cleveldb, defaults is goleveldb. DBBackend = "" + backend = dbm.GoLevelDBBackend ) +func init() { + if len(DBBackend) != 0 { + backend = dbm.BackendType(DBBackend) + } +} + // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. // This method can be used to canonicalize JSON to be returned by GetSignBytes, @@ -49,6 +56,16 @@ func Uint64ToBigEndian(i uint64) []byte { return b } +// BigEndianToUint64 returns an uint64 from big endian encoded bytes. If encoding +// is empty, zero is returned. +func BigEndianToUint64(bz []byte) uint64 { + if len(bz) == 0 { + return 0 + } + + return binary.BigEndian.Uint64(bz) +} + // Slight modification of the RFC3339Nano but it right pads all zeros and drops the time zone info const SortableTimeFormat = "2006-01-02T15:04:05.000000000" @@ -69,14 +86,21 @@ func ParseTimeBytes(bz []byte) (time.Time, error) { // NewLevelDB instantiate a new LevelDB instance according to DBBackend. func NewLevelDB(name, dir string) (db dbm.DB, err error) { - backend := dbm.GoLevelDBBackend - if DBBackend == string(dbm.CLevelDBBackend) { - backend = dbm.CLevelDBBackend - } defer func() { if r := recover(); r != nil { err = fmt.Errorf("couldn't create db: %v", r) } }() - return dbm.NewDB(name, backend, dir), err + + return dbm.NewDB(name, backend, dir) +} + +// copy bytes +func CopyBytes(bz []byte) (ret []byte) { + if bz == nil { + return nil + } + ret = make([]byte, len(bz)) + copy(ret, bz) + return ret } diff --git a/types/utils_test.go b/types/utils_test.go index 352beccd4259..c04c9c81f3e1 100644 --- a/types/utils_test.go +++ b/types/utils_test.go @@ -1,13 +1,28 @@ -package types +package types_test import ( + "bytes" "testing" "time" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestSortJSON(t *testing.T) { +type utilsTestSuite struct { + suite.Suite +} + +func TestUtilsTestSuite(t *testing.T) { + suite.Run(t, new(utilsTestSuite)) +} + +func (s *utilsTestSuite) SetupSuite() { + s.T().Parallel() +} + +func (s *utilsTestSuite) TestSortJSON() { cases := []struct { unsortedJSON string want string @@ -32,21 +47,21 @@ func TestSortJSON(t *testing.T) { for tcIndex, tc := range cases { tc := tc - got, err := SortJSON([]byte(tc.unsortedJSON)) + got, err := sdk.SortJSON([]byte(tc.unsortedJSON)) if tc.wantErr { - require.NotNil(t, err, "tc #%d", tcIndex) - require.Panics(t, func() { MustSortJSON([]byte(tc.unsortedJSON)) }) + s.Require().NotNil(err, "tc #%d", tcIndex) + s.Require().Panics(func() { sdk.MustSortJSON([]byte(tc.unsortedJSON)) }) } else { - require.Nil(t, err, "tc #%d, err=%s", tcIndex, err) - require.NotPanics(t, func() { MustSortJSON([]byte(tc.unsortedJSON)) }) - require.Equal(t, got, MustSortJSON([]byte(tc.unsortedJSON))) + s.Require().Nil(err, "tc #%d, err=%s", tcIndex, err) + s.Require().NotPanics(func() { sdk.MustSortJSON([]byte(tc.unsortedJSON)) }) + s.Require().Equal(got, sdk.MustSortJSON([]byte(tc.unsortedJSON))) } - require.Equal(t, string(got), tc.want) + s.Require().Equal(string(got), tc.want) } } -func TestTimeFormatAndParse(t *testing.T) { +func (s *utilsTestSuite) TestTimeFormatAndParse() { cases := []struct { RFC3339NanoStr string SDKSortableTimeStr string @@ -58,11 +73,39 @@ func TestTimeFormatAndParse(t *testing.T) { for _, tc := range cases { tc := tc timeFromRFC, err := time.Parse(time.RFC3339Nano, tc.RFC3339NanoStr) - require.Nil(t, err) - timeFromSDKFormat, err := time.Parse(SortableTimeFormat, tc.SDKSortableTimeStr) - require.Nil(t, err) + s.Require().Nil(err) + timeFromSDKFormat, err := time.Parse(sdk.SortableTimeFormat, tc.SDKSortableTimeStr) + s.Require().Nil(err) - require.True(t, timeFromRFC.Equal(timeFromSDKFormat)) - require.Equal(t, timeFromRFC.Format(SortableTimeFormat), tc.SDKSortableTimeStr) + s.Require().True(timeFromRFC.Equal(timeFromSDKFormat)) + s.Require().Equal(timeFromRFC.Format(sdk.SortableTimeFormat), tc.SDKSortableTimeStr) } } + +func (s *utilsTestSuite) TestCopyBytes() { + s.Require().Nil(sdk.CopyBytes(nil)) + s.Require().Equal(0, len(sdk.CopyBytes([]byte{}))) + bs := []byte("test") + bsCopy := sdk.CopyBytes(bs) + s.Require().True(bytes.Equal(bs, bsCopy)) +} + +func (s *utilsTestSuite) TestUint64ToBigEndian() { + s.Require().Equal([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, sdk.Uint64ToBigEndian(uint64(0))) + s.Require().Equal([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa}, sdk.Uint64ToBigEndian(uint64(10))) +} + +func (s *utilsTestSuite) TestFormatTimeBytes() { + tm, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Mar 3, 2020 at 7:54pm (UTC)") + s.Require().NoError(err) + s.Require().Equal("2020-03-03T19:54:00.000000000", string(sdk.FormatTimeBytes(tm))) +} + +func (s *utilsTestSuite) TestParseTimeBytes() { + tm, err := sdk.ParseTimeBytes([]byte("2020-03-03T19:54:00.000000000")) + s.Require().NoError(err) + s.Require().True(tm.Equal(time.Date(2020, 3, 3, 19, 54, 0, 0, time.UTC))) + + _, err = sdk.ParseTimeBytes([]byte{}) + s.Require().Error(err) +} diff --git a/version/command.go b/version/command.go index 9c3b0cfda285..bb632aea5b18 100644 --- a/version/command.go +++ b/version/command.go @@ -2,48 +2,53 @@ package version import ( "encoding/json" - "fmt" + "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - "github.com/tendermint/tendermint/libs/cli" + yaml "gopkg.in/yaml.v2" ) const flagLong = "long" -func init() { - Cmd.Flags().Bool(flagLong, false, "Print long version information") -} - -// Cmd prints out the application's version information passed via build flags. -var Cmd = &cobra.Command{ - Use: "version", - Short: "Print the app version", - RunE: func(_ *cobra.Command, _ []string) error { - verInfo := NewInfo() - - if !viper.GetBool(flagLong) { - fmt.Println(verInfo.Version) +func NewVersionCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Short: "Print the application binary version information", + RunE: func(cmd *cobra.Command, _ []string) error { + verInfo := NewInfo() + cmd.SetOut(cmd.OutOrStdout()) + + if long, _ := cmd.Flags().GetBool(flagLong); !long { + cmd.Println(verInfo.Version) + return nil + } + + var ( + bz []byte + err error + ) + + output, _ := cmd.Flags().GetString(cli.OutputFlag) + switch strings.ToLower(output) { + case "json": + bz, err = json.Marshal(verInfo) + + default: + bz, err = yaml.Marshal(&verInfo) + } + + if err != nil { + return err + } + + cmd.Println(string(bz)) return nil - } - - var bz []byte - var err error - - switch viper.GetString(cli.OutputFlag) { - case "json": - bz, err = json.Marshal(verInfo) - default: - bz, err = yaml.Marshal(&verInfo) - } + }, + } - if err != nil { - return err - } + cmd.Flags().Bool(flagLong, false, "Print long version information") + cmd.Flags().StringP(cli.OutputFlag, "o", "text", "Output format (text|json)") - _, err = fmt.Println(string(bz)) - return err - }, + return cmd } diff --git a/version/version.go b/version/version.go index 9429a9c1f9f1..a179b37fe31b 100644 --- a/version/version.go +++ b/version/version.go @@ -10,25 +10,24 @@ // can be passed as build flags as shown in the following example: // // go build -X github.com/cosmos/cosmos-sdk/version.Name=gaia \ -// -X github.com/cosmos/cosmos-sdk/version.ServerName=gaiad \ -// -X github.com/cosmos/cosmos-sdk/version.ClientName=gaiacli \ +// -X github.com/cosmos/cosmos-sdk/version.AppName=gaiad \ // -X github.com/cosmos/cosmos-sdk/version.Version=1.0 \ // -X github.com/cosmos/cosmos-sdk/version.Commit=f0f7b7dab7e36c20b757cebce0e8f4fc5b95de60 \ // -X "github.com/cosmos/cosmos-sdk/version.BuildTags=linux darwin amd64" package version import ( + "encoding/json" "fmt" "runtime" + "runtime/debug" ) var ( // application's name Name = "" - // server binary name - ServerName = "" - // client binary name - ClientName = "" + // application binary name + AppName = "" // application's version string Version = "" // commit @@ -39,24 +38,24 @@ var ( // Info defines the application version information. type Info struct { - Name string `json:"name" yaml:"name"` - ServerName string `json:"server_name" yaml:"server_name"` - ClientName string `json:"client_name" yaml:"client_name"` - Version string `json:"version" yaml:"version"` - GitCommit string `json:"commit" yaml:"commit"` - BuildTags string `json:"build_tags" yaml:"build_tags"` - GoVersion string `json:"go" yaml:"go"` + Name string `json:"name" yaml:"name"` + AppName string `json:"server_name" yaml:"server_name"` + Version string `json:"version" yaml:"version"` + GitCommit string `json:"commit" yaml:"commit"` + BuildTags string `json:"build_tags" yaml:"build_tags"` + GoVersion string `json:"go" yaml:"go"` + BuildDeps []buildDep `json:"build_deps" yaml:"build_deps"` } func NewInfo() Info { return Info{ - Name: Name, - ServerName: ServerName, - ClientName: ClientName, - Version: Version, - GitCommit: Commit, - BuildTags: BuildTags, - GoVersion: fmt.Sprintf("go version %s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH), + Name: Name, + AppName: AppName, + Version: Version, + GitCommit: Commit, + BuildTags: BuildTags, + GoVersion: fmt.Sprintf("go version %s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH), + BuildDeps: depsFromBuildInfo(), } } @@ -68,3 +67,31 @@ build tags: %s vi.Name, vi.Version, vi.GitCommit, vi.BuildTags, vi.GoVersion, ) } + +func depsFromBuildInfo() (deps []buildDep) { + buildInfo, ok := debug.ReadBuildInfo() + if !ok { + return nil + } + + for _, dep := range buildInfo.Deps { + deps = append(deps, buildDep{dep}) + } + + return +} + +type buildDep struct { + *debug.Module +} + +func (d buildDep) String() string { + if d.Replace != nil { + return fmt.Sprintf("%s@%s => %s@%s", d.Path, d.Version, d.Replace.Path, d.Replace.Version) + } + + return fmt.Sprintf("%s@%s", d.Path, d.Version) +} + +func (d buildDep) MarshalJSON() ([]byte, error) { return json.Marshal(d.String()) } +func (d buildDep) MarshalYAML() (interface{}, error) { return d.String(), nil } diff --git a/version/version_test.go b/version/version_test.go new file mode 100644 index 000000000000..87608cfdbb97 --- /dev/null +++ b/version/version_test.go @@ -0,0 +1,64 @@ +package version_test + +import ( + "encoding/json" + "fmt" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/version" +) + +func TestNewInfo(t *testing.T) { + info := version.NewInfo() + want := fmt.Sprintf(`: +git commit: +build tags: +%s`, fmt.Sprintf("go version %s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)) + require.Equal(t, want, info.String()) +} + +func TestInfo_String(t *testing.T) { + info := version.Info{ + Name: "testapp", + AppName: "testappd", + Version: "1.0.0", + GitCommit: "1b78457135a4104bc3af97f20654d49e2ea87454", + BuildTags: "netgo,ledger", + GoVersion: "go version go1.14 linux/amd64", + } + want := `testapp: 1.0.0 +git commit: 1b78457135a4104bc3af97f20654d49e2ea87454 +build tags: netgo,ledger +go version go1.14 linux/amd64` + require.Equal(t, want, info.String()) +} + +func Test_runVersionCmd(t *testing.T) { + cmd := version.NewVersionCommand() + _, mockOut := testutil.ApplyMockIO(cmd) + + cmd.SetArgs([]string{ + fmt.Sprintf("--%s=''", cli.OutputFlag), + "--long=false", + }) + + require.NoError(t, cmd.Execute()) + assert.Equal(t, "\n", mockOut.String()) + mockOut.Reset() + + cmd.SetArgs([]string{ + fmt.Sprintf("--%s=json", cli.OutputFlag), "--long=true", + }) + + info := version.NewInfo() + stringInfo, err := json.Marshal(info) + require.NoError(t, err) + require.NoError(t, cmd.Execute()) + assert.Equal(t, string(stringInfo)+"\n", mockOut.String()) +} diff --git a/x/README.md b/x/README.md index e123223f4a80..3be56a3de605 100644 --- a/x/README.md +++ b/x/README.md @@ -9,13 +9,17 @@ Here are some production-grade modules that can be used in Cosmos SDK applicatio - [Auth](auth/spec/README.md) - Authentication of accounts and transactions for Cosmos SDK application. - [Bank](bank/spec/README.md) - Token transfer functionalities. -- [Governance](gov/spec/README.md) - On-chain proposals and voting. -- [Staking](staking/spec/README.md) - Proof-of-stake layer for public blockchains. -- [Slashing](slashing/spec/README.md) - Validator punishment mechanisms. -- [Distribution](distribution/spec/README.md) - Fee distribution, and staking token provision distribution. +- [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). +- [Distribution](distribution/spec/README.md) - Fee distribution, and staking token provision distribution. +- [Evidence](evidence/spec/README.md) - Evidence handling for double signing, misbehaviour, etc. +- [Governance](gov/spec/README.md) - On-chain proposals and voting. +- [IBC](ibc/spec/README.md) - IBC protocol for transport, authentication adn ordering. +- [IBC Transfer](ibc/spec/README.md) - Cross-chain fungible token transfer implementation through IBC. - [Mint](mint/spec/README.md) - Creation of new units of staking token. - [Params](params/spec/README.md) - Globally available parameter store. -- [Supply](supply/spec/README.md) - Total token supply of the chain. +- [Slashing](slashing/spec/README.md) - Validator punishment mechanisms. +- [Staking](staking/spec/README.md) - Proof-of-Stake layer for public blockchains. +- [Upgrade](upgrade/spec/README.md) - Software upgrades handling and coordination. To learn more about the process of building modules, visit the [building modules reference documentation](../docs/building-modules/README.md). diff --git a/x/auth/alias.go b/x/auth/alias.go deleted file mode 100644 index 01b20a68f3d7..000000000000 --- a/x/auth/alias.go +++ /dev/null @@ -1,92 +0,0 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/auth/ante -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/auth/keeper -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/auth/types -package auth - -import ( - "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - FeeCollectorName = types.FeeCollectorName - QuerierRoute = types.QuerierRoute - DefaultParamspace = types.DefaultParamspace - DefaultMaxMemoCharacters = types.DefaultMaxMemoCharacters - DefaultTxSigLimit = types.DefaultTxSigLimit - DefaultTxSizeCostPerByte = types.DefaultTxSizeCostPerByte - DefaultSigVerifyCostED25519 = types.DefaultSigVerifyCostED25519 - DefaultSigVerifyCostSecp256k1 = types.DefaultSigVerifyCostSecp256k1 - QueryAccount = types.QueryAccount -) - -var ( - // functions aliases - NewAnteHandler = ante.NewAnteHandler - GetSignerAcc = ante.GetSignerAcc - DefaultSigVerificationGasConsumer = ante.DefaultSigVerificationGasConsumer - DeductFees = ante.DeductFees - SetGasMeter = ante.SetGasMeter - NewAccountKeeper = keeper.NewAccountKeeper - NewQuerier = keeper.NewQuerier - NewBaseAccount = types.NewBaseAccount - ProtoBaseAccount = types.ProtoBaseAccount - NewBaseAccountWithAddress = types.NewBaseAccountWithAddress - NewAccountRetriever = types.NewAccountRetriever - RegisterCodec = types.RegisterCodec - RegisterAccountTypeCodec = types.RegisterAccountTypeCodec - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - SanitizeGenesisAccounts = types.SanitizeGenesisAccounts - AddressStoreKey = types.AddressStoreKey - NewParams = types.NewParams - ParamKeyTable = types.ParamKeyTable - DefaultParams = types.DefaultParams - NewQueryAccountParams = types.NewQueryAccountParams - NewStdTx = types.NewStdTx - CountSubKeys = types.CountSubKeys - NewStdFee = types.NewStdFee - StdSignBytes = types.StdSignBytes - DefaultTxDecoder = types.DefaultTxDecoder - DefaultTxEncoder = types.DefaultTxEncoder - NewTxBuilder = types.NewTxBuilder - NewTxBuilderFromCLI = types.NewTxBuilderFromCLI - MakeSignature = types.MakeSignature - ValidateGenAccounts = types.ValidateGenAccounts - GetGenesisStateFromAppState = types.GetGenesisStateFromAppState - - // variable aliases - ModuleCdc = types.ModuleCdc - AddressStoreKeyPrefix = types.AddressStoreKeyPrefix - GlobalAccountNumberKey = types.GlobalAccountNumberKey - KeyMaxMemoCharacters = types.KeyMaxMemoCharacters - KeyTxSigLimit = types.KeyTxSigLimit - KeyTxSizeCostPerByte = types.KeyTxSizeCostPerByte - KeySigVerifyCostED25519 = types.KeySigVerifyCostED25519 - KeySigVerifyCostSecp256k1 = types.KeySigVerifyCostSecp256k1 -) - -type ( - SignatureVerificationGasConsumer = ante.SignatureVerificationGasConsumer - AccountKeeper = keeper.AccountKeeper - BaseAccount = types.BaseAccount - NodeQuerier = types.NodeQuerier - AccountRetriever = types.AccountRetriever - GenesisState = types.GenesisState - Params = types.Params - QueryAccountParams = types.QueryAccountParams - StdSignMsg = types.StdSignMsg - StdTx = types.StdTx - StdFee = types.StdFee - StdSignDoc = types.StdSignDoc - StdSignature = types.StdSignature - TxBuilder = types.TxBuilder - GenesisAccountIterator = types.GenesisAccountIterator -) diff --git a/x/auth/ante/ante.go b/x/auth/ante/ante.go index bdf24823f137..8cc025bad2d2 100644 --- a/x/auth/ante/ante.go +++ b/x/auth/ante/ante.go @@ -2,25 +2,32 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/auth/types" ) // NewAnteHandler returns an AnteHandler that checks and increments sequence // numbers, checks signatures & account numbers, and deducts fees from the first // signer. -func NewAnteHandler(ak keeper.AccountKeeper, supplyKeeper types.SupplyKeeper, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler { +func NewAnteHandler( + ak AccountKeeper, bankKeeper types.BankKeeper, + sigGasConsumer SignatureVerificationGasConsumer, + signModeHandler signing.SignModeHandler, +) sdk.AnteHandler { return sdk.ChainAnteDecorators( NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + NewRejectExtensionOptionsDecorator(), NewMempoolFeeDecorator(), NewValidateBasicDecorator(), + TxTimeoutHeightDecorator{}, NewValidateMemoDecorator(ak), NewConsumeGasForTxSizeDecorator(ak), + NewRejectFeeGranterDecorator(), NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators NewValidateSigCountDecorator(ak), - NewDeductFeeDecorator(ak, supplyKeeper), + NewDeductFeeDecorator(ak, bankKeeper), NewSigGasConsumeDecorator(ak, sigGasConsumer), - NewSigVerificationDecorator(ak), - NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + NewSigVerificationDecorator(ak, signModeHandler), + NewIncrementSequenceDecorator(ak), ) } diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 1d55b4d320c3..7659f3e1043d 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -4,601 +4,913 @@ import ( "encoding/json" "errors" "fmt" - "math/rand" "strings" "testing" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// run the tx through the anteHandler and ensure its valid -func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) { - _, err := anteHandler(ctx, tx, simulate) - require.Nil(t, err) -} +// Test that simulate transaction accurately estimates gas cost +func (suite *AnteTestSuite) TestSimulateGasCost() { + suite.SetupTest(true) // reset + + // Same data for every test cases + accounts := suite.CreateTestAccounts(3) + msgs := []sdk.Msg{ + testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()), + testdata.NewTestMsg(accounts[2].acc.GetAddress(), accounts[0].acc.GetAddress()), + testdata.NewTestMsg(accounts[1].acc.GetAddress(), accounts[2].acc.GetAddress()), + } + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + accSeqs := []uint64{0, 0, 0} + privs := []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv, accounts[2].priv} + accNums := []uint64{0, 1, 2} + + testCases := []TestCase{ + { + "tx with 150atom fee", + func() { + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + }, + true, + true, + nil, + }, + { + "with previously estimated gas", + func() { + simulatedGas := suite.ctx.GasMeter().GasConsumed() + + accSeqs = []uint64{1, 1, 1} + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(simulatedGas) + }, + false, + true, + nil, + }, + } -// run the tx through the anteHandler and ensure it fails with the given code -func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool, expErr error) { - _, err := anteHandler(ctx, tx, simulate) - require.NotNil(t, err) - require.True(t, errors.Is(expErr, err)) -} + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() -// Test that simulate transaction accurately estimates gas cost -func TestSimulateGasCost(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) - acc3.SetCoins(types.NewTestCoins()) - require.NoError(t, acc3.SetAccountNumber(2)) - app.AccountKeeper.SetAccount(ctx, acc3) - - // set up msgs and fee - var tx sdk.Tx - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr3, addr1) - msg3 := types.NewTestMsg(addr2, addr3) - msgs := []sdk.Msg{msg1, msg2, msg3} - fee := types.NewTestStdFee() - - // signers in order. accnums are all 0 because it is in genesis block - privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - - cc, _ := ctx.CacheContext() - newCtx, err := anteHandler(cc, tx, true) - require.Nil(t, err, "transaction failed on simulate mode") - - simulatedGas := newCtx.GasMeter().GasConsumed() - fee.Gas = simulatedGas - - // update tx with simulated gas estimate - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - _, err = anteHandler(ctx, tx, false) - - require.Nil(t, err, "transaction failed with gas estimate") + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test various error cases in the AnteHandler control flow. -func TestAnteHandlerSigErrors(t *testing.T) { - // setup - app, ctx := createTestApp(true) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() - - // msg and signatures - var tx sdk.Tx - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr1, addr3) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1, msg2} - - // test no signatures - privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} - tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - // tx.GetSigners returns addresses in correct order: addr1, addr2, addr3 - expectedSigners := []sdk.AccAddress{addr1, addr2, addr3} - stdTx := tx.(types.StdTx) - require.Equal(t, expectedSigners, stdTx.GetSigners()) - - // Check no signatures fails - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrNoSignatures) - - // test num sigs dont match GetSigners - privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // test an unrecognized account - privs, accNums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} - tx = types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnknownAddress) - - // save the first account, but second is still unrecognized - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(fee.Amount) - app.AccountKeeper.SetAccount(ctx, acc1) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnknownAddress) +func (suite *AnteTestSuite) TestAnteHandlerSigErrors() { + suite.SetupTest(true) // reset + + // Same data for every test cases + priv0, _, addr0 := testdata.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, addr2 := testdata.KeyTestPubAddr() + msgs := []sdk.Msg{ + testdata.NewTestMsg(addr0, addr1), + testdata.NewTestMsg(addr0, addr2), + } + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + // Variable data per test case + var ( + privs []cryptotypes.PrivKey + accNums []uint64 + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "check no signatures fails", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{}, []uint64{}, []uint64{} + + // Create tx manually to test the tx's signers + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + // tx.GetSigners returns addresses in correct order: addr1, addr2, addr3 + expectedSigners := []sdk.AccAddress{addr0, addr1, addr2} + suite.Require().Equal(expectedSigners, tx.GetSigners()) + }, + false, + false, + sdkerrors.ErrNoSignatures, + }, + { + "num sigs dont match GetSigners", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{priv0}, []uint64{0}, []uint64{0} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "unrecognized account", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{priv0, priv1, priv2}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + }, + false, + false, + sdkerrors.ErrUnknownAddress, + }, + { + "save the first account, but second is still unrecognized", + 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) + suite.Require().NoError(err) + }, + false, + false, + sdkerrors.ErrUnknownAddress, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test logic around account number checking with one signer and many signers. -func TestAnteHandlerAccountNumbers(t *testing.T) { - // setup - app, ctx := createTestApp(false) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - - // msg and signatures - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() +func (suite *AnteTestSuite) TestAnteHandlerAccountNumbers() { + suite.SetupTest(false) // reset - msgs := []sdk.Msg{msg} + // Same data for every test cases + accounts := suite.CreateTestAccounts(2) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() - // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // new tx from wrong account number - seqs = []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // from correct account number - seqs = []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // new tx with another signer and incorrect account numbers - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr2, addr1) - msgs = []sdk.Msg{msg1, msg2} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // correct account numbers - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{2, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) + // Variable data per test case + var ( + accNums []uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "good tx from one signer", + func() { + msg := testdata.NewTestMsg(accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + true, + nil, + }, + { + "new tx from wrong account number", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{1}, []uint64{1} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "new tx from correct account number", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{1} + }, + false, + true, + nil, + }, + { + "new tx with another signer and incorrect account numbers", + func() { + msg1 := testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()) + msg2 := testdata.NewTestMsg(accounts[1].acc.GetAddress(), accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg1, msg2} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{1, 0}, []uint64{2, 0} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "new tx with correct account numbers", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{0, 1}, []uint64{2, 0} + }, + false, + true, + nil, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test logic around account number checking with many signers when BlockHeight is 0. -func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { - // setup - app, ctx := createTestApp(false) - ctx = ctx.WithBlockHeight(0) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - - // set the accounts, we don't need the acc numbers as it is in the genesis block - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - - // msg and signatures - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() +func (suite *AnteTestSuite) TestAnteHandlerAccountNumbersAtBlockHeightZero() { + suite.SetupTest(false) // setup + suite.ctx = suite.ctx.WithBlockHeight(0) + + // Same data for every test cases + accounts := suite.CreateTestAccounts(2) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + // Variable data per test case + var ( + accNums []uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "good tx from one signer", + func() { + msg := testdata.NewTestMsg(accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + true, + nil, + }, + { + "new tx from wrong account number", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{1}, []uint64{1} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "new tx from correct account number", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{1} + }, + false, + true, + nil, + }, + { + "new tx with another signer and incorrect account numbers", + func() { + msg1 := testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()) + msg2 := testdata.NewTestMsg(accounts[1].acc.GetAddress(), accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg1, msg2} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{1, 0}, []uint64{2, 0} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "new tx with another signer and correct account numbers", + func() { + // Note that accNums is [0,0] at block 0. + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{0, 0}, []uint64{2, 0} + }, + false, + true, + nil, + }, + } - msgs := []sdk.Msg{msg} + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() - // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // new tx from wrong account number - seqs = []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // from correct account number - seqs = []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // new tx with another signer and incorrect account numbers - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr2, addr1) - msgs = []sdk.Msg{msg1, msg2} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // correct account numbers - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{2, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test logic around sequence checking with one signer and many signers. -func TestAnteHandlerSequences(t *testing.T) { - // setup - app, ctx := createTestApp(false) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) - acc3.SetCoins(types.NewTestCoins()) - require.NoError(t, acc3.SetAccountNumber(2)) - app.AccountKeeper.SetAccount(ctx, acc3) - - // msg and signatures - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() +func (suite *AnteTestSuite) TestAnteHandlerSequences() { + suite.SetupTest(false) // setup - msgs := []sdk.Msg{msg} + // Same data for every test cases + accounts := suite.CreateTestAccounts(3) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + // Variable data per test case + var ( + accNums []uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "good tx from one signer", + func() { + msg := testdata.NewTestMsg(accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + true, + nil, + }, + { + "test sending it again fails (replay protection)", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "fix sequence, should pass", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{1} + }, + false, + true, + nil, + }, + { + "new tx with another signer and correct sequences", + func() { + msg1 := testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()) + msg2 := testdata.NewTestMsg(accounts[2].acc.GetAddress(), accounts[0].acc.GetAddress()) + msgs = []sdk.Msg{msg1, msg2} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv, accounts[2].priv}, []uint64{0, 1, 2}, []uint64{2, 0, 0} + }, + false, + true, + nil, + }, + { + "replay fails", + func() {}, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "tx from just second signer with incorrect sequence fails", + func() { + msg := testdata.NewTestMsg(accounts[1].acc.GetAddress()) + msgs = []sdk.Msg{msg} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[1].priv}, []uint64{1}, []uint64{0} + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "fix the sequence and it passes", + func() { + accSeqs = []uint64{1} + }, + false, + true, + nil, + }, + { + "fix the sequence and it passes", + func() { + msg := testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()) + msgs = []sdk.Msg{msg} + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{0, 1}, []uint64{3, 2} + }, + false, + true, + nil, + }, + } - // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // test sending it again fails (replay protection) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // fix sequence, should pass - seqs = []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // new tx with another signer and correct sequences - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr3, addr1) - msgs = []sdk.Msg{msg1, msg2} - - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{2, 0, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // replay fails - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // tx from just second signer with incorrect sequence fails - msg = types.NewTestMsg(addr2) - msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrUnauthorized) - - // fix the sequence and it passes - tx = types.NewTestTx(ctx, msgs, []crypto.PrivKey{priv2}, []uint64{1}, []uint64{1}, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // another tx from both of them that passes - msg = types.NewTestMsg(addr1, addr2) - msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{3, 2} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test logic around fee deduction. -func TestAnteHandlerFees(t *testing.T) { - // setup - app, ctx := createTestApp(true) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - app.AccountKeeper.SetAccount(ctx, acc1) - - // msg and signatures - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - fee := types.NewTestStdFee() - msgs := []sdk.Msg{msg} +func (suite *AnteTestSuite) TestAnteHandlerFees() { + suite.SetupTest(true) // setup - // signer does not have enough funds to pay the fee - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInsufficientFunds) + // Same data for every test cases + priv0, _, addr0 := testdata.KeyTestPubAddr() - acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 149))) - app.AccountKeeper.SetAccount(ctx, acc1) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInsufficientFunds) + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr0) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) + msgs := []sdk.Msg{testdata.NewTestMsg(addr0)} + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv0}, []uint64{0}, []uint64{0} - require.True(t, app.SupplyKeeper.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().Empty()) - require.True(sdk.IntEq(t, app.AccountKeeper.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), sdk.NewInt(149))) + testCases := []struct { + desc string + malleate func() + simulate bool + expPass bool + expErr error + }{ + { + "signer has no funds", + func() { + accSeqs = []uint64{0} + }, + false, + false, + sdkerrors.ErrInsufficientFunds, + }, + { + "signer does not have enough funds to pay the fee", + func() { + suite.app.BankKeeper.SetBalances(suite.ctx, addr0, sdk.NewCoins(sdk.NewInt64Coin("atom", 149))) + }, + false, + false, + sdkerrors.ErrInsufficientFunds, + }, + { + "signer as enough funds, should pass", + func() { + 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))) + }, + false, + true, + nil, + }, + { + "signer doesn't have any more funds", + func() { + modAcc := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.FeeCollectorName) + + require.True(sdk.IntEq(suite.T(), suite.app.BankKeeper.GetAllBalances(suite.ctx, modAcc.GetAddress()).AmountOf("atom"), sdk.NewInt(150))) + require.True(sdk.IntEq(suite.T(), suite.app.BankKeeper.GetAllBalances(suite.ctx, addr0).AmountOf("atom"), sdk.NewInt(0))) + }, + false, + false, + sdkerrors.ErrInsufficientFunds, + }, + } - acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))) - app.AccountKeeper.SetAccount(ctx, acc1) - checkValidTx(t, anteHandler, ctx, tx, false) + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() - require.True(sdk.IntEq(t, app.SupplyKeeper.GetModuleAccount(ctx, types.FeeCollectorName).GetCoins().AmountOf("atom"), sdk.NewInt(150))) - require.True(sdk.IntEq(t, app.AccountKeeper.GetAccount(ctx, addr1).GetCoins().AmountOf("atom"), sdk.NewInt(0))) + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test logic around memo gas consumption. -func TestAnteHandlerMemoGas(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - - // msg and signatures - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - fee := types.NewStdFee(0, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) - - // tx does not have enough gas - tx = types.NewTestTx(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrOutOfGas) - - // tx with memo doesn't have enough gas - fee = types.NewStdFee(801, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) - tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsd") - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrOutOfGas) - - // memo too large - fee = types.NewStdFee(50000, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) - tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, strings.Repeat("01234567890", 500)) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrMemoTooLarge) - - // tx with memo has enough gas - fee = types.NewStdFee(50000, sdk.NewCoins(sdk.NewInt64Coin("atom", 0))) - tx = types.NewTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, strings.Repeat("0123456789", 10)) - checkValidTx(t, anteHandler, ctx, tx, false) -} +func (suite *AnteTestSuite) TestAnteHandlerMemoGas() { + suite.SetupTest(false) // setup + + // Same data for every test cases + accounts := suite.CreateTestAccounts(1) + msgs := []sdk.Msg{testdata.NewTestMsg(accounts[0].acc.GetAddress())} + privs, accNums, accSeqs := []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + + // Variable data per test case + var ( + feeAmount sdk.Coins + gasLimit uint64 + ) + + testCases := []TestCase{ + { + "tx does not have enough gas", + func() { + feeAmount = sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) + gasLimit = 0 + }, + false, + false, + sdkerrors.ErrOutOfGas, + }, + { + "tx with memo doesn't have enough gas", + func() { + feeAmount = sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) + gasLimit = 801 + suite.txBuilder.SetMemo("abcininasidniandsinasindiansdiansdinaisndiasndiadninsd") + }, + false, + false, + sdkerrors.ErrOutOfGas, + }, + { + "memo too large", + func() { + feeAmount = sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) + gasLimit = 50000 + suite.txBuilder.SetMemo(strings.Repeat("01234567890", 500)) + }, + false, + false, + sdkerrors.ErrMemoTooLarge, + }, + { + "tx with memo has enough gas", + func() { + feeAmount = sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) + gasLimit = 50000 + suite.txBuilder.SetMemo(strings.Repeat("0123456789", 10)) + }, + false, + true, + nil, + }, + } -func TestAnteHandlerMultiSigner(t *testing.T) { - // setup - app, ctx := createTestApp(false) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) - acc3.SetCoins(types.NewTestCoins()) - require.NoError(t, acc3.SetAccountNumber(2)) - app.AccountKeeper.SetAccount(ctx, acc3) - - // set up msgs and fee - var tx sdk.Tx - msg1 := types.NewTestMsg(addr1, addr2) - msg2 := types.NewTestMsg(addr3, addr1) - msg3 := types.NewTestMsg(addr2, addr3) - msgs := []sdk.Msg{msg1, msg2, msg3} - fee := types.NewTestStdFee() - - // signers in order - privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} - tx = types.NewTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "Check signers are in expected order and different account numbers works") - - checkValidTx(t, anteHandler, ctx, tx, false) - - // change sequence numbers - tx = types.NewTestTx(ctx, []sdk.Msg{msg1}, []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{1, 1}, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - tx = types.NewTestTx(ctx, []sdk.Msg{msg2}, []crypto.PrivKey{priv3, priv1}, []uint64{2, 0}, []uint64{1, 2}, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - // expected seqs = [3, 2, 2] - tx = types.NewTestTxWithMemo(ctx, msgs, privs, accnums, []uint64{3, 2, 2}, fee, "Check signers are in expected order and different account numbers and sequence numbers works") - checkValidTx(t, anteHandler, ctx, tx, false) + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } -func TestAnteHandlerBadSignBytes(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - msgs := []sdk.Msg{msg} - fee := types.NewTestStdFee() - fee2 := types.NewTestStdFee() - fee2.Gas += 100 - fee3 := types.NewTestStdFee() - fee3.Amount[0].Amount = fee3.Amount[0].Amount.AddRaw(100) - - // test good tx and signBytes - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - chainID := ctx.ChainID() - chainID2 := chainID + "somemorestuff" - errUnauth := sdkerrors.ErrUnauthorized - - cases := []struct { - chainID string - accnum uint64 - seq uint64 - fee types.StdFee +func (suite *AnteTestSuite) TestAnteHandlerMultiSigner() { + suite.SetupTest(false) // setup + + // Same data for every test cases + accounts := suite.CreateTestAccounts(3) + msg1 := testdata.NewTestMsg(accounts[0].acc.GetAddress(), accounts[1].acc.GetAddress()) + msg2 := testdata.NewTestMsg(accounts[2].acc.GetAddress(), accounts[0].acc.GetAddress()) + msg3 := testdata.NewTestMsg(accounts[1].acc.GetAddress(), accounts[2].acc.GetAddress()) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + // Variable data per test case + var ( + accNums []uint64 msgs []sdk.Msg - err error - }{ - {chainID2, 0, 1, fee, msgs, errUnauth}, // test wrong chain_id - {chainID, 0, 2, fee, msgs, errUnauth}, // test wrong seqs - {chainID, 1, 1, fee, msgs, errUnauth}, // test wrong accnum - {chainID, 0, 1, fee, []sdk.Msg{types.NewTestMsg(addr2)}, errUnauth}, // test wrong msg - {chainID, 0, 1, fee2, msgs, errUnauth}, // test wrong fee - {chainID, 0, 1, fee3, msgs, errUnauth}, // test wrong fee - } - - privs, seqs = []crypto.PrivKey{priv1}, []uint64{1} - for _, cs := range cases { - tx := types.NewTestTxWithSignBytes( - msgs, privs, accnums, seqs, fee, - types.StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, ""), - "", - ) - checkInvalidTx(t, anteHandler, ctx, tx, false, cs.err) - } - - // test wrong signer if public key exist - privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{0}, []uint64{1} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInvalidPubKey) - - // test wrong signer if public doesn't exist - msg = types.NewTestMsg(addr2) - msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv1}, []uint64{1}, []uint64{0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInvalidPubKey) + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "signers in order", + func() { + msgs = []sdk.Msg{msg1, msg2, msg3} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv, accounts[2].priv}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + suite.txBuilder.SetMemo("Check signers are in expected order and different account numbers works") + }, + false, + true, + nil, + }, + { + "change sequence numbers (only accounts 0 and 1 sign)", + func() { + msgs = []sdk.Msg{msg1} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv}, []uint64{0, 1}, []uint64{1, 1} + }, + false, + true, + nil, + }, + { + "change sequence numbers (only accounts 1 and 2 sign)", + func() { + msgs = []sdk.Msg{msg2} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[2].priv, accounts[0].priv}, []uint64{2, 0}, []uint64{1, 2} + }, + false, + true, + nil, + }, + { + "everyone signs again", + func() { + msgs = []sdk.Msg{msg1, msg2, msg3} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv, accounts[1].priv, accounts[2].priv}, []uint64{0, 1, 2}, []uint64{3, 2, 2} + }, + false, + true, + nil, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } -func TestAnteHandlerSetPubKey(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - _, _, addr2 := types.KeyTestPubAddr() - - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - acc2.SetCoins(types.NewTestCoins()) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - - var tx sdk.Tx - - // test good tx and set public key - msg := types.NewTestMsg(addr1) - msgs := []sdk.Msg{msg} - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - fee := types.NewTestStdFee() - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) - - acc1 = app.AccountKeeper.GetAccount(ctx, addr1) - require.Equal(t, acc1.GetPubKey(), priv1.PubKey()) - - // test public key not found - msg = types.NewTestMsg(addr2) - msgs = []sdk.Msg{msg} - tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) - sigs := tx.(types.StdTx).Signatures - sigs[0].PubKey = nil - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInvalidPubKey) - - acc2 = app.AccountKeeper.GetAccount(ctx, addr2) - require.Nil(t, acc2.GetPubKey()) - - // test invalid signature and public key - tx = types.NewTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInvalidPubKey) - - acc2 = app.AccountKeeper.GetAccount(ctx, addr2) - require.Nil(t, acc2.GetPubKey()) +func (suite *AnteTestSuite) TestAnteHandlerBadSignBytes() { + suite.SetupTest(false) // setup + + // Same data for every test cases + accounts := suite.CreateTestAccounts(2) + msg0 := testdata.NewTestMsg(accounts[0].acc.GetAddress()) + + // Variable data per test case + var ( + accNums []uint64 + chainID string + feeAmount sdk.Coins + gasLimit uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "test good tx and signBytes", + func() { + chainID = suite.ctx.ChainID() + feeAmount = testdata.NewTestFeeAmount() + gasLimit = testdata.NewTestGasLimit() + msgs = []sdk.Msg{msg0} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + true, + nil, + }, + { + "test wrong chainID", + func() { + accSeqs = []uint64{1} // Back to correct accSeqs + chainID = "chain-foo" + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "test wrong accSeqs", + func() { + chainID = suite.ctx.ChainID() // Back to correct chainID + accSeqs = []uint64{2} + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "test wrong accNums", + func() { + accSeqs = []uint64{1} // Back to correct accSeqs + accNums = []uint64{1} + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "test wrong msg", + func() { + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[1].acc.GetAddress())} + }, + false, + false, + sdkerrors.ErrInvalidPubKey, + }, + { + "test wrong fee gas", + func() { + msgs = []sdk.Msg{msg0} // Back to correct msgs + feeAmount = testdata.NewTestFeeAmount() + gasLimit = testdata.NewTestGasLimit() + 100 + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "test wrong fee amount", + func() { + feeAmount = testdata.NewTestFeeAmount() + feeAmount[0].Amount = feeAmount[0].Amount.AddRaw(100) + gasLimit = testdata.NewTestGasLimit() + }, + false, + false, + sdkerrors.ErrUnauthorized, + }, + { + "test wrong signer if public key exist", + func() { + feeAmount = testdata.NewTestFeeAmount() + gasLimit = testdata.NewTestGasLimit() + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[1].priv}, []uint64{0}, []uint64{1} + }, + false, + false, + sdkerrors.ErrInvalidPubKey, + }, + { + "test wrong signer if public doesn't exist", + func() { + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[1].acc.GetAddress())} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{1}, []uint64{0} + }, + false, + false, + sdkerrors.ErrInvalidPubKey, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, chainID, tc) + }) + } +} + +func (suite *AnteTestSuite) TestAnteHandlerSetPubKey() { + suite.SetupTest(false) // setup + + // Same data for every test cases + accounts := suite.CreateTestAccounts(2) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + // Variable data per test case + var ( + accNums []uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "test good tx", + func() { + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[0].acc.GetAddress())} + }, + false, + true, + nil, + }, + { + "make sure public key has been set (tx itself should fail because of replay protection)", + func() { + // Make sure public key has been set from previous test. + acc0 := suite.app.AccountKeeper.GetAccount(suite.ctx, accounts[0].acc.GetAddress()) + suite.Require().Equal(acc0.GetPubKey(), accounts[0].priv.PubKey()) + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "test public key not found", + func() { + // See above, `privs` still holds the private key of accounts[0]. + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[1].acc.GetAddress())} + }, + false, + false, + sdkerrors.ErrInvalidPubKey, + }, + { + "make sure public key is not set, when tx has no pubkey or signature", + func() { + // Make sure public key has not been set from previous test. + acc1 := suite.app.AccountKeeper.GetAccount(suite.ctx, accounts[1].acc.GetAddress()) + suite.Require().Nil(acc1.GetPubKey()) + + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[1].priv}, []uint64{1}, []uint64{0} + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[1].acc.GetAddress())} + suite.txBuilder.SetMsgs(msgs...) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + // Manually create tx, and remove signature. + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + txBuilder, err := suite.clientCtx.TxConfig.WrapTxBuilder(tx) + suite.Require().NoError(err) + suite.Require().NoError(txBuilder.SetSignatures()) + + // Run anteHandler manually, expect ErrNoSignatures. + _, err = suite.anteHandler(suite.ctx, txBuilder.GetTx(), false) + suite.Require().Error(err) + suite.Require().True(errors.Is(err, sdkerrors.ErrNoSignatures)) + + // Make sure public key has not been set. + acc1 = suite.app.AccountKeeper.GetAccount(suite.ctx, accounts[1].acc.GetAddress()) + suite.Require().Nil(acc1.GetPubKey()) + + // Set incorrect accSeq, to generate incorrect signature. + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[1].priv}, []uint64{1}, []uint64{1} + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + { + "make sure previous public key has been set after wrong signature", + func() { + // Make sure public key has been set, as SetPubKeyDecorator + // is called before all signature verification decorators. + acc1 := suite.app.AccountKeeper.GetAccount(suite.ctx, accounts[1].acc.GetAddress()) + suite.Require().Equal(acc1.GetPubKey(), accounts[1].priv.PubKey()) + }, + false, + false, + sdkerrors.ErrWrongSequence, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } -func generatePubKeysAndSignatures(n int, msg []byte, keyTypeed25519 bool) (pubkeys []crypto.PubKey, signatures [][]byte) { - pubkeys = make([]crypto.PubKey, n) +func generatePubKeysAndSignatures(n int, msg []byte, _ bool) (pubkeys []cryptotypes.PubKey, signatures [][]byte) { + pubkeys = make([]cryptotypes.PubKey, n) signatures = make([][]byte, n) for i := 0; i < n; i++ { - var privkey crypto.PrivKey - if rand.Int63()%2 == 0 { - privkey = ed25519.GenPrivKey() - } else { - privkey = secp256k1.GenPrivKey() - } + var privkey cryptotypes.PrivKey + privkey = secp256k1.GenPrivKey() + + // TODO: also generate ed25519 keys as below when ed25519 keys are + // actually supported, https://github.com/cosmos/cosmos-sdk/issues/4789 + // for now this fails: + //if rand.Int63()%2 == 0 { + // privkey = ed25519.GenPrivKey() + //} else { + // privkey = secp256k1.GenPrivKey() + //} + pubkeys[i] = privkey.PubKey() signatures[i], _ = privkey.Sign(msg) } return } -func expectedGasCostByKeys(pubkeys []crypto.PubKey) uint64 { +func expectedGasCostByKeys(pubkeys []cryptotypes.PubKey) uint64 { cost := uint64(0) for _, pubkey := range pubkeys { pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey)) @@ -615,23 +927,23 @@ func expectedGasCostByKeys(pubkeys []crypto.PubKey) uint64 { } func TestCountSubkeys(t *testing.T) { - genPubKeys := func(n int) []crypto.PubKey { - var ret []crypto.PubKey + genPubKeys := func(n int) []cryptotypes.PubKey { + var ret []cryptotypes.PubKey for i := 0; i < n; i++ { ret = append(ret, secp256k1.GenPrivKey().PubKey()) } return ret } singleKey := secp256k1.GenPrivKey().PubKey() - singleLevelMultiKey := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) - multiLevelSubKey1 := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) - multiLevelSubKey2 := multisig.NewPubKeyMultisigThreshold(4, genPubKeys(5)) - multiLevelMultiKey := multisig.NewPubKeyMultisigThreshold(2, []crypto.PubKey{ + singleLevelMultiKey := kmultisig.NewLegacyAminoPubKey(4, genPubKeys(5)) + multiLevelSubKey1 := kmultisig.NewLegacyAminoPubKey(4, genPubKeys(5)) + multiLevelSubKey2 := kmultisig.NewLegacyAminoPubKey(4, genPubKeys(5)) + multiLevelMultiKey := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{ multiLevelSubKey1, multiLevelSubKey2, secp256k1.GenPrivKey().PubKey()}) type args struct { - pub crypto.PubKey + pub cryptotypes.PubKey } - tests := []struct { + testCases := []struct { name string args args want int @@ -640,138 +952,139 @@ func TestCountSubkeys(t *testing.T) { {"single level multikey", args{singleLevelMultiKey}, 5}, {"multi level multikey", args{multiLevelMultiKey}, 11}, } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(T *testing.T) { - require.Equal(t, tt.want, types.CountSubKeys(tt.args.pub)) + for _, tc := range testCases { + t.Run(tc.name, func(T *testing.T) { + require.Equal(t, tc.want, ante.CountSubKeys(tc.args.pub)) }) } } -func TestAnteHandlerSigLimitExceeded(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) - - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() - priv4, _, addr4 := types.KeyTestPubAddr() - priv5, _, addr5 := types.KeyTestPubAddr() - priv6, _, addr6 := types.KeyTestPubAddr() - priv7, _, addr7 := types.KeyTestPubAddr() - priv8, _, addr8 := types.KeyTestPubAddr() - - addrs := []sdk.AccAddress{addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8} - - // set the accounts - for i, addr := range addrs { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - acc.SetCoins(types.NewTestCoins()) - acc.SetAccountNumber(uint64(i)) - app.AccountKeeper.SetAccount(ctx, acc) - } - - var tx sdk.Tx - msg := types.NewTestMsg(addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8) - msgs := []sdk.Msg{msg} - fee := types.NewTestStdFee() +func (suite *AnteTestSuite) TestAnteHandlerSigLimitExceeded() { + suite.SetupTest(true) // setup - // test rejection logic - privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8}, - []uint64{0, 1, 2, 3, 4, 5, 6, 7}, []uint64{0, 0, 0, 0, 0, 0, 0, 0} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrTooManySignatures) + // Same data for every test cases + accounts := suite.CreateTestAccounts(8) + var addrs []sdk.AccAddress + var privs []cryptotypes.PrivKey + for i := 0; i < 8; i++ { + addrs = append(addrs, accounts[i].acc.GetAddress()) + privs = append(privs, accounts[i].priv) + } + msgs := []sdk.Msg{testdata.NewTestMsg(addrs...)} + accNums, accSeqs := []uint64{0, 1, 2, 3, 4, 5, 6, 7}, []uint64{0, 0, 0, 0, 0, 0, 0, 0} + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + testCases := []TestCase{ + { + "test rejection logic", + func() {}, + false, + false, + sdkerrors.ErrTooManySignatures, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() + + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } } // Test custom SignatureVerificationGasConsumer -func TestCustomSignatureVerificationGasConsumer(t *testing.T) { - // setup - app, ctx := createTestApp(true) - ctx = ctx.WithBlockHeight(1) +func (suite *AnteTestSuite) TestCustomSignatureVerificationGasConsumer() { + suite.SetupTest(true) // setup + // setup an ante handler that only accepts PubKeyEd25519 - anteHandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params types.Params) error { - switch pubkey := pubkey.(type) { - case ed25519.PubKeyEd25519: + suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error { + switch pubkey := sig.PubKey.(type) { + case *ed25519.PubKey: meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") return nil default: return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey) } - }) - - // verify that an secp256k1 account gets rejected - priv1, _, addr1 := types.KeyTestPubAddr() - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - _ = acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))) - app.AccountKeeper.SetAccount(ctx, acc1) - - var tx sdk.Tx - msg := types.NewTestMsg(addr1) - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - fee := types.NewTestStdFee() - msgs := []sdk.Msg{msg} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, false, sdkerrors.ErrInvalidPubKey) - - // verify that an ed25519 account gets accepted - priv2 := ed25519.GenPrivKey() - pub2 := priv2.PubKey() - addr2 := sdk.AccAddress(pub2.Address()) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - require.NoError(t, acc2.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("atom", 150)))) - require.NoError(t, acc2.SetAccountNumber(1)) - app.AccountKeeper.SetAccount(ctx, acc2) - msg = types.NewTestMsg(addr2) - privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0} - fee = types.NewTestStdFee() - msgs = []sdk.Msg{msg} - tx = types.NewTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx, false) -} + }, suite.clientCtx.TxConfig.SignModeHandler()) -func TestAnteHandlerReCheck(t *testing.T) { - // setup - app, ctx := createTestApp(true) - // set blockheight and recheck=true - ctx = ctx.WithBlockHeight(1) - ctx = ctx.WithIsReCheckTx(true) + // Same data for every test cases + accounts := suite.CreateTestAccounts(1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() - // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - // priv2, _, addr2 := types.KeyTestPubAddr() + // Variable data per test case + var ( + accNums []uint64 + msgs []sdk.Msg + privs []cryptotypes.PrivKey + accSeqs []uint64 + ) + + testCases := []TestCase{ + { + "verify that an secp256k1 account gets rejected", + func() { + msgs = []sdk.Msg{testdata.NewTestMsg(accounts[0].acc.GetAddress())} + privs, accNums, accSeqs = []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + }, + false, + false, + sdkerrors.ErrInvalidPubKey, + }, + } - // set the accounts - acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc1.SetCoins(types.NewTestCoins()) - require.NoError(t, acc1.SetAccountNumber(0)) - app.AccountKeeper.SetAccount(ctx, acc1) + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + tc.malleate() - antehandler := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, ante.DefaultSigVerificationGasConsumer) + suite.RunTestCase(privs, msgs, feeAmount, gasLimit, accNums, accSeqs, suite.ctx.ChainID(), tc) + }) + } +} - // test that operations skipped on recheck do not run +func (suite *AnteTestSuite) TestAnteHandlerReCheck() { + suite.SetupTest(true) // setup + // Set recheck=true + suite.ctx = suite.ctx.WithIsReCheckTx(true) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + // Same data for every test cases + accounts := suite.CreateTestAccounts(1) - msg := types.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + msg := testdata.NewTestMsg(accounts[0].acc.GetAddress()) msgs := []sdk.Msg{msg} - fee := types.NewTestStdFee() + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + + suite.txBuilder.SetMemo("thisisatestmemo") - privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "thisisatestmemo") + // test that operations skipped on recheck do not run + privs, accNums, accSeqs := []cryptotypes.PrivKey{accounts[0].priv}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) // make signature array empty which would normally cause ValidateBasicDecorator and SigVerificationDecorator fail // since these decorators don't run on recheck, the tx should pass the antehandler - stdTx := tx.(types.StdTx) - stdTx.Signatures = []types.StdSignature{} + txBuilder, err := suite.clientCtx.TxConfig.WrapTxBuilder(tx) + suite.Require().NoError(err) + suite.Require().NoError(txBuilder.SetSignatures()) - _, err := antehandler(ctx, stdTx, false) - require.Nil(t, err, "AnteHandler errored on recheck unexpectedly: %v", err) + _, err = suite.anteHandler(suite.ctx, txBuilder.GetTx(), false) + suite.Require().Nil(err, "AnteHandler errored on recheck unexpectedly: %v", err) - tx = types.NewTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "thisisatestmemo") + tx, err = suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) txBytes, err := json.Marshal(tx) - require.Nil(t, err, "Error marshalling tx: %v", err) - ctx = ctx.WithTxBytes(txBytes) + suite.Require().Nil(err, "Error marshalling tx: %v", err) + suite.ctx = suite.ctx.WithTxBytes(txBytes) // require that state machine param-dependent checking is still run on recheck since parameters can change between check and recheck testCases := []struct { @@ -784,31 +1097,31 @@ func TestAnteHandlerReCheck(t *testing.T) { } for _, tc := range testCases { // set testcase parameters - app.AccountKeeper.SetParams(ctx, tc.params) + suite.app.AccountKeeper.SetParams(suite.ctx, tc.params) - _, err := antehandler(ctx, tx, false) + _, err := suite.anteHandler(suite.ctx, tx, false) - require.NotNil(t, err, "tx does not fail on recheck with updated params in test case: %s", tc.name) + suite.Require().NotNil(err, "tx does not fail on recheck with updated params in test case: %s", tc.name) // reset parameters to default values - app.AccountKeeper.SetParams(ctx, types.DefaultParams()) + suite.app.AccountKeeper.SetParams(suite.ctx, types.DefaultParams()) } // require that local mempool fee check is still run on recheck since validator may change minFee between check and recheck // create new minimum gas price so antehandler fails on recheck - ctx = ctx.WithMinGasPrices([]sdk.DecCoin{{ + suite.ctx = suite.ctx.WithMinGasPrices([]sdk.DecCoin{{ Denom: "dnecoin", // fee does not have this denom Amount: sdk.NewDec(5), }}) - _, err = antehandler(ctx, tx, false) - require.NotNil(t, err, "antehandler on recheck did not fail when mingasPrice was changed") + _, err = suite.anteHandler(suite.ctx, tx, false) + suite.Require().NotNil(err, "antehandler on recheck did not fail when mingasPrice was changed") // reset min gasprice - ctx = ctx.WithMinGasPrices(sdk.DecCoins{}) + suite.ctx = suite.ctx.WithMinGasPrices(sdk.DecCoins{}) // remove funds for account so antehandler fails on recheck - acc1.SetCoins(sdk.Coins{}) - app.AccountKeeper.SetAccount(ctx, acc1) + suite.app.AccountKeeper.SetAccount(suite.ctx, accounts[0].acc) + suite.app.BankKeeper.SetBalances(suite.ctx, accounts[0].acc.GetAddress(), sdk.NewCoins()) - _, err = antehandler(ctx, tx, false) - require.NotNil(t, err, "antehandler on recheck did not fail once feePayer no longer has sufficient funds") + _, 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/basic.go b/x/auth/ante/basic.go index 0e999a9d4781..7ed834ebaa37 100644 --- a/x/auth/ante/basic.go +++ b/x/auth/ante/basic.go @@ -1,19 +1,14 @@ package ante import ( + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - err "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - - "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -var ( - _ TxWithMemo = (*types.StdTx)(nil) // assert StdTx implements TxWithMemo + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) // ValidateBasicDecorator will call tx.ValidateBasic and return any non-nil error. @@ -31,6 +26,7 @@ func (vbd ValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat if ctx.IsReCheckTx() { return next(ctx, tx, simulate) } + if err := tx.ValidateBasic(); err != nil { return ctx, err } @@ -38,27 +34,21 @@ func (vbd ValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat return next(ctx, tx, simulate) } -// Tx must have GetMemo() method to use ValidateMemoDecorator -type TxWithMemo interface { - sdk.Tx - GetMemo() string -} - // ValidateMemoDecorator will validate memo given the parameters passed in // If memo is too large decorator returns with error, otherwise call next AnteHandler // CONTRACT: Tx must implement TxWithMemo interface type ValidateMemoDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper } -func NewValidateMemoDecorator(ak keeper.AccountKeeper) ValidateMemoDecorator { +func NewValidateMemoDecorator(ak AccountKeeper) ValidateMemoDecorator { return ValidateMemoDecorator{ ak: ak, } } func (vmd ValidateMemoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - memoTx, ok := tx.(TxWithMemo) + memoTx, ok := tx.(sdk.TxWithMemo) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } @@ -67,7 +57,7 @@ func (vmd ValidateMemoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate memoLength := len(memoTx.GetMemo()) if uint64(memoLength) > params.MaxMemoCharacters { - return ctx, err.Wrapf(err.ErrMemoTooLarge, + return ctx, sdkerrors.Wrapf(sdkerrors.ErrMemoTooLarge, "maximum number of characters is %d but received %d characters", params.MaxMemoCharacters, memoLength, ) @@ -84,54 +74,64 @@ func (vmd ValidateMemoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate // CONTRACT: If simulate=true, then signatures must either be completely filled // in or empty. // CONTRACT: To use this decorator, signatures of transaction must be represented -// as types.StdSignature otherwise simulate mode will incorrectly estimate gas cost. +// as legacytx.StdSignature otherwise simulate mode will incorrectly estimate gas cost. type ConsumeTxSizeGasDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper } -func NewConsumeGasForTxSizeDecorator(ak keeper.AccountKeeper) ConsumeTxSizeGasDecorator { +func NewConsumeGasForTxSizeDecorator(ak AccountKeeper) ConsumeTxSizeGasDecorator { return ConsumeTxSizeGasDecorator{ ak: ak, } } func (cgts ConsumeTxSizeGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") } params := cgts.ak.GetParams(ctx) + ctx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*sdk.Gas(len(ctx.TxBytes())), "txSize") // simulate gas cost for signatures in simulate mode if simulate { // in simulate mode, each element should be a nil signature - sigs := sigTx.GetSignatures() + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return ctx, err + } + n := len(sigs) + for i, signer := range sigTx.GetSigners() { // if signature is already filled in, no need to simulate gas cost - if sigs[i] != nil { + if i < n && !isIncompleteSignature(sigs[i].Data) { continue } + + var pubkey cryptotypes.PubKey + acc := cgts.ak.GetAccount(ctx, signer) - var pubkey crypto.PubKey // use placeholder simSecp256k1Pubkey if sig is nil if acc == nil || acc.GetPubKey() == nil { pubkey = simSecp256k1Pubkey } else { pubkey = acc.GetPubKey() } + // use stdsignature to mock the size of a full signature - simSig := types.StdSignature{ + simSig := legacytx.StdSignature{ //nolint:staticcheck // this will be removed when proto is ready Signature: simSecp256k1Sig[:], PubKey: pubkey, } - sigBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(simSig) + + sigBz := legacy.Cdc.MustMarshalBinaryBare(simSig) cost := sdk.Gas(len(sigBz) + 6) // If the pubkey is a multi-signature pubkey, then we estimate for the maximum // number of signers. - if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok { + if _, ok := pubkey.(*multisig.LegacyAminoPubKey); ok { cost *= params.TxSigLimit } @@ -141,3 +141,60 @@ func (cgts ConsumeTxSizeGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim return next(ctx, tx, simulate) } + +// isIncompleteSignature tests whether SignatureData is fully filled in for simulation purposes +func isIncompleteSignature(data signing.SignatureData) bool { + if data == nil { + return true + } + + switch data := data.(type) { + case *signing.SingleSignatureData: + return len(data.Signature) == 0 + case *signing.MultiSignatureData: + if len(data.Signatures) == 0 { + return true + } + for _, s := range data.Signatures { + if isIncompleteSignature(s) { + return true + } + } + } + + return false +} + +type ( + // TxTimeoutHeightDecorator defines an AnteHandler decorator that checks for a + // tx height timeout. + TxTimeoutHeightDecorator struct{} + + // TxWithTimeoutHeight defines the interface a tx must implement in order for + // TxHeightTimeoutDecorator to process the tx. + TxWithTimeoutHeight interface { + sdk.Tx + + GetTimeoutHeight() uint64 + } +) + +// AnteHandle implements an AnteHandler decorator for the TxHeightTimeoutDecorator +// type where the current block height is checked against the tx's height timeout. +// If a height timeout is provided (non-zero) and is less than the current block +// height, then an error is returned. +func (txh TxTimeoutHeightDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + timeoutTx, ok := tx.(TxWithTimeoutHeight) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "expected tx to implement TxWithTimeoutHeight") + } + + timeoutHeight := timeoutTx.GetTimeoutHeight() + if timeoutHeight > 0 && uint64(ctx.BlockHeight()) > timeoutHeight { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrTxTimeoutHeight, "block height: %d, timeout height: %d", ctx.BlockHeight(), timeoutHeight, + ) + } + + return next(ctx, tx, simulate) +} diff --git a/x/auth/ante/basic_test.go b/x/auth/ante/basic_test.go index 145137f4cb9f..08567348857e 100644 --- a/x/auth/ante/basic_test.go +++ b/x/auth/ante/basic_test.go @@ -1,145 +1,224 @@ package ante_test import ( - "encoding/json" "strings" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/types" ) -func TestValidateBasic(t *testing.T) { - // setup - _, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestValidateBasic() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1} + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} - invalidTx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{}, []uint64{}, []uint64{} + invalidTx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) vbd := ante.NewValidateBasicDecorator() antehandler := sdk.ChainAnteDecorators(vbd) - _, err := antehandler(ctx, invalidTx, false) + _, err = antehandler(suite.ctx, invalidTx, false) - require.NotNil(t, err, "Did not error on invalid tx") + suite.Require().NotNil(err, "Did not error on invalid tx") - privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - validTx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs = []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + validTx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) - _, err = antehandler(ctx, validTx, false) - require.Nil(t, err, "ValidateBasicDecorator returned error on valid tx. err: %v", err) + _, err = antehandler(suite.ctx, validTx, false) + suite.Require().Nil(err, "ValidateBasicDecorator returned error on valid tx. err: %v", err) // test decorator skips on recheck - ctx = ctx.WithIsReCheckTx(true) + suite.ctx = suite.ctx.WithIsReCheckTx(true) // decorator should skip processing invalidTx on recheck and thus return nil-error - _, err = antehandler(ctx, invalidTx, false) + _, err = antehandler(suite.ctx, invalidTx, false) - require.Nil(t, err, "ValidateBasicDecorator ran on ReCheck") + suite.Require().Nil(err, "ValidateBasicDecorator ran on ReCheck") } -func TestValidateMemo(t *testing.T) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestValidateMemo() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1} - - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - invalidTx := types.NewTestTxWithMemo(ctx, msgs, privs, accNums, seqs, fee, strings.Repeat("01234567890", 500)) + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + suite.txBuilder.SetMemo(strings.Repeat("01234567890", 500)) + invalidTx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) // require that long memos get rejected - vmd := ante.NewValidateMemoDecorator(app.AccountKeeper) + vmd := ante.NewValidateMemoDecorator(suite.app.AccountKeeper) antehandler := sdk.ChainAnteDecorators(vmd) - _, err := antehandler(ctx, invalidTx, false) + _, err = antehandler(suite.ctx, invalidTx, false) - require.NotNil(t, err, "Did not error on tx with high memo") + suite.Require().NotNil(err, "Did not error on tx with high memo") - validTx := types.NewTestTxWithMemo(ctx, msgs, privs, accNums, seqs, fee, strings.Repeat("01234567890", 10)) + suite.txBuilder.SetMemo(strings.Repeat("01234567890", 10)) + validTx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) // require small memos pass ValidateMemo Decorator - _, err = antehandler(ctx, validTx, false) - require.Nil(t, err, "ValidateBasicDecorator returned error on valid tx. err: %v", err) + _, err = antehandler(suite.ctx, validTx, false) + suite.Require().Nil(err, "ValidateBasicDecorator returned error on valid tx. err: %v", err) } -func TestConsumeGasForTxSize(t *testing.T) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestConsumeGasForTxSize() { + suite.SetupTest(true) // setup // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() - msgs := []sdk.Msg{msg1} + cgtsd := ante.NewConsumeGasForTxSizeDecorator(suite.app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(cgtsd) - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTxWithMemo(ctx, msgs, privs, accNums, seqs, fee, strings.Repeat("01234567890", 10)) - txBytes, err := json.Marshal(tx) - require.Nil(t, err, "Cannot marshal tx: %v", err) + testCases := []struct { + name string + sigV2 signing.SignatureV2 + }{ + {"SingleSignatureData", signing.SignatureV2{PubKey: priv1.PubKey()}}, + {"MultiSignatureData", signing.SignatureV2{PubKey: priv1.PubKey(), Data: multisig.NewMultisig(2)}}, + } - cgtsd := ante.NewConsumeGasForTxSizeDecorator(app.AccountKeeper) - antehandler := sdk.ChainAnteDecorators(cgtsd) + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + suite.txBuilder.SetMemo(strings.Repeat("01234567890", 10)) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + txBytes, err := suite.clientCtx.TxConfig.TxJSONEncoder()(tx) + suite.Require().Nil(err, "Cannot marshal tx: %v", err) + + params := suite.app.AccountKeeper.GetParams(suite.ctx) + expectedGas := sdk.Gas(len(txBytes)) * params.TxSizeCostPerByte - params := app.AccountKeeper.GetParams(ctx) - expectedGas := sdk.Gas(len(txBytes)) * params.TxSizeCostPerByte + // Set suite.ctx with TxBytes manually + suite.ctx = suite.ctx.WithTxBytes(txBytes) - // Set ctx with TxBytes manually - ctx = ctx.WithTxBytes(txBytes) + // track how much gas is necessary to retrieve parameters + beforeGas := suite.ctx.GasMeter().GasConsumed() + suite.app.AccountKeeper.GetParams(suite.ctx) + afterGas := suite.ctx.GasMeter().GasConsumed() + expectedGas += afterGas - beforeGas - // track how much gas is necessary to retrieve parameters - beforeGas := ctx.GasMeter().GasConsumed() - app.AccountKeeper.GetParams(ctx) - afterGas := ctx.GasMeter().GasConsumed() - expectedGas += afterGas - beforeGas + beforeGas = suite.ctx.GasMeter().GasConsumed() + suite.ctx, err = antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "ConsumeTxSizeGasDecorator returned error: %v", err) - beforeGas = ctx.GasMeter().GasConsumed() - ctx, err = antehandler(ctx, tx, false) - require.Nil(t, err, "ConsumeTxSizeGasDecorator returned error: %v", err) + // require that decorator consumes expected amount of gas + consumedGas := suite.ctx.GasMeter().GasConsumed() - beforeGas + suite.Require().Equal(expectedGas, consumedGas, "Decorator did not consume the correct amount of gas") - // require that decorator consumes expected amount of gas - consumedGas := ctx.GasMeter().GasConsumed() - beforeGas - require.Equal(t, expectedGas, consumedGas, "Decorator did not consume the correct amount of gas") + // simulation must not underestimate gas of this decorator even with nil signatures + txBuilder, err := suite.clientCtx.TxConfig.WrapTxBuilder(tx) + suite.Require().NoError(err) + suite.Require().NoError(txBuilder.SetSignatures(tc.sigV2)) + tx = txBuilder.GetTx() - // simulation must not underestimate gas of this decorator even with nil signatures - sigTx := tx.(types.StdTx) - sigTx.Signatures = []types.StdSignature{{}} + simTxBytes, err := suite.clientCtx.TxConfig.TxJSONEncoder()(tx) + suite.Require().Nil(err, "Cannot marshal tx: %v", err) + // require that simulated tx is smaller than tx with signatures + suite.Require().True(len(simTxBytes) < len(txBytes), "simulated tx still has signatures") - simTxBytes, err := json.Marshal(sigTx) - require.Nil(t, err) - // require that simulated tx is smaller than tx with signatures - require.True(t, len(simTxBytes) < len(txBytes), "simulated tx still has signatures") + // Set suite.ctx with smaller simulated TxBytes manually + suite.ctx = suite.ctx.WithTxBytes(simTxBytes) - // Set ctx with smaller simulated TxBytes manually - ctx = ctx.WithTxBytes(txBytes) + beforeSimGas := suite.ctx.GasMeter().GasConsumed() - beforeSimGas := ctx.GasMeter().GasConsumed() + // run antehandler with simulate=true + suite.ctx, err = antehandler(suite.ctx, tx, true) + consumedSimGas := suite.ctx.GasMeter().GasConsumed() - beforeSimGas - // run antehandler with simulate=true - ctx, err = antehandler(ctx, sigTx, true) - consumedSimGas := ctx.GasMeter().GasConsumed() - beforeSimGas + // require that antehandler passes and does not underestimate decorator cost + suite.Require().Nil(err, "ConsumeTxSizeGasDecorator returned error: %v", err) + suite.Require().True(consumedSimGas >= expectedGas, "Simulate mode underestimates gas on AnteDecorator. Simulated cost: %d, expected cost: %d", consumedSimGas, expectedGas) - // require that antehandler passes and does not underestimate decorator cost - require.Nil(t, err, "ConsumeTxSizeGasDecorator returned error: %v", err) - require.True(t, consumedSimGas >= expectedGas, "Simulate mode underestimates gas on AnteDecorator. Simulated cost: %d, expected cost: %d", consumedSimGas, expectedGas) + }) + } + +} + +func (suite *AnteTestSuite) TestTxHeightTimeoutDecorator() { + suite.SetupTest(true) + + antehandler := sdk.ChainAnteDecorators(ante.TxTimeoutHeightDecorator{}) + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + + // msg and signatures + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + testCases := []struct { + name string + timeout uint64 + height int64 + expectErr bool + }{ + {"default value", 0, 10, false}, + {"no timeout (greater height)", 15, 10, false}, + {"no timeout (same height)", 10, 10, false}, + {"timeout (smaller height)", 9, 10, true}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + suite.txBuilder.SetMemo(strings.Repeat("01234567890", 10)) + suite.txBuilder.SetTimeoutHeight(tc.timeout) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + ctx := suite.ctx.WithBlockHeight(tc.height) + _, err = antehandler(ctx, tx, true) + suite.Require().Equal(tc.expectErr, err != nil, err) + }) + } } diff --git a/x/auth/ante/expected_keepers.go b/x/auth/ante/expected_keepers.go new file mode 100644 index 000000000000..d8be4312e5cf --- /dev/null +++ b/x/auth/ante/expected_keepers.go @@ -0,0 +1,15 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// AccountKeeper defines the contract needed for AccountKeeper related APIs. +// Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators. +type AccountKeeper interface { + GetParams(ctx sdk.Context) (params types.Params) + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + SetAccount(ctx sdk.Context, acc types.AccountI) + GetModuleAddress(moduleName string) sdk.AccAddress +} diff --git a/x/auth/ante/ext.go b/x/auth/ante/ext.go new file mode 100644 index 000000000000..362b8d32a971 --- /dev/null +++ b/x/auth/ante/ext.go @@ -0,0 +1,36 @@ +package ante + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type HasExtensionOptionsTx interface { + GetExtensionOptions() []*codectypes.Any + GetNonCriticalExtensionOptions() []*codectypes.Any +} + +// RejectExtensionOptionsDecorator is an AnteDecorator that rejects all extension +// options which can optionally be included in protobuf transactions. Users that +// need extension options should create a custom AnteHandler chain that handles +// needed extension options properly and rejects unknown ones. +type RejectExtensionOptionsDecorator struct{} + +// NewRejectExtensionOptionsDecorator creates a new RejectExtensionOptionsDecorator +func NewRejectExtensionOptionsDecorator() RejectExtensionOptionsDecorator { + return RejectExtensionOptionsDecorator{} +} + +var _ types.AnteDecorator = RejectExtensionOptionsDecorator{} + +// AnteHandle implements the AnteDecorator.AnteHandle method +func (r RejectExtensionOptionsDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (newCtx types.Context, err error) { + if hasExtOptsTx, ok := tx.(HasExtensionOptionsTx); ok { + if len(hasExtOptsTx.GetExtensionOptions()) != 0 { + return ctx, sdkerrors.ErrUnknownExtensionOptions + } + } + + return next(ctx, tx, simulate) +} diff --git a/x/auth/ante/ext_test.go b/x/auth/ante/ext_test.go new file mode 100644 index 000000000000..89ce6a7d649f --- /dev/null +++ b/x/auth/ante/ext_test.go @@ -0,0 +1,36 @@ +package ante_test + +import ( + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +func (suite *AnteTestSuite) TestRejectExtensionOptionsDecorator() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + reod := ante.NewRejectExtensionOptionsDecorator() + antehandler := sdk.ChainAnteDecorators(reod) + + // no extension options should not trigger an error + theTx := suite.txBuilder.GetTx() + _, err := antehandler(suite.ctx, theTx, false) + suite.Require().NoError(err) + + extOptsTxBldr, ok := suite.txBuilder.(tx.ExtensionOptionsTxBuilder) + if !ok { + // if we can't set extension options, this decorator doesn't apply and we're done + return + } + + // setting any extension option should cause an error + any, err := types.NewAnyWithValue(testdata.NewTestMsg()) + suite.Require().NoError(err) + extOptsTxBldr.SetExtensionOptions(any) + theTx = suite.txBuilder.GetTx() + _, err = antehandler(suite.ctx, theTx, false) + suite.Require().EqualError(err, "unknown extension options") +} diff --git a/x/auth/ante/fee.go b/x/auth/ante/fee.go index 3d81808b2d0c..5da4dbbaf5d5 100644 --- a/x/auth/ante/fee.go +++ b/x/auth/ante/fee.go @@ -4,25 +4,10 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) -var ( - _ FeeTx = (*types.StdTx)(nil) // assert StdTx implements FeeTx -) - -// FeeTx defines the interface to be implemented by Tx to use the FeeDecorators -type FeeTx interface { - sdk.Tx - GetGas() uint64 - GetFee() sdk.Coins - FeePayer() sdk.AccAddress -} - // MempoolFeeDecorator will check if the transaction's fee is at least as large // as the local validator's minimum gasFee (defined in validator config). // If fee is too low, decorator returns error and tx is rejected from mempool. @@ -36,10 +21,11 @@ func NewMempoolFeeDecorator() MempoolFeeDecorator { } func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(FeeTx) + feeTx, ok := tx.(sdk.FeeTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") } + feeCoins := feeTx.GetFee() gas := feeTx.GetGas() @@ -73,24 +59,24 @@ func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator type DeductFeeDecorator struct { - ak keeper.AccountKeeper - supplyKeeper types.SupplyKeeper + ak AccountKeeper + bankKeeper types.BankKeeper } -func NewDeductFeeDecorator(ak keeper.AccountKeeper, sk types.SupplyKeeper) DeductFeeDecorator { +func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper) DeductFeeDecorator { return DeductFeeDecorator{ - ak: ak, - supplyKeeper: sk, + ak: ak, + bankKeeper: bk, } } func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(FeeTx) + feeTx, ok := tx.(sdk.FeeTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") } - if addr := dfd.supplyKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil { + if addr := dfd.ak.GetModuleAddress(types.FeeCollectorName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName)) } @@ -103,7 +89,7 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo // deduct the fees if !feeTx.GetFee().IsZero() { - err = DeductFees(dfd.supplyKeeper, ctx, feePayerAcc, feeTx.GetFee()) + err = DeductFees(dfd.bankKeeper, ctx, feePayerAcc, feeTx.GetFee()) if err != nil { return ctx, err } @@ -113,33 +99,12 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo } // DeductFees deducts fees from the given account. -// -// NOTE: We could use the BankKeeper (in addition to the AccountKeeper, because -// the BankKeeper doesn't give us accounts), but it seems easier to do this. -func DeductFees(supplyKeeper types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error { - blockTime := ctx.BlockHeader().Time - coins := acc.GetCoins() - +func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error { if !fees.IsValid() { return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) } - // verify the account has enough funds to pay for fees - _, hasNeg := coins.SafeSub(fees) - if hasNeg { - return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, - "insufficient funds to pay for fees; %s < %s", coins, fees) - } - - // Validate the account has enough "spendable" coins as this will cover cases - // such as vesting accounts. - spendableCoins := acc.SpendableCoins(blockTime) - if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { - return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, - "insufficient funds to pay for fees; %s < %s", spendableCoins, fees) - } - - err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) + err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) if err != nil { return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) } diff --git a/x/auth/ante/fee_grant.go b/x/auth/ante/fee_grant.go new file mode 100644 index 000000000000..2c37fffa098a --- /dev/null +++ b/x/auth/ante/fee_grant.go @@ -0,0 +1,27 @@ +package ante + +import ( + "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// RejectFeeGranterDecorator is an AnteDecorator which rejects transactions which +// have the Fee.granter field set. It is to be used by chains which do not support +// fee grants. +type RejectFeeGranterDecorator struct{} + +// NewRejectFeeGranterDecorator returns a new RejectFeeGranterDecorator. +func NewRejectFeeGranterDecorator() RejectFeeGranterDecorator { + return RejectFeeGranterDecorator{} +} + +var _ types.AnteDecorator = RejectFeeGranterDecorator{} + +func (d RejectFeeGranterDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (newCtx types.Context, err error) { + feeTx, ok := tx.(types.FeeTx) + if ok && len(feeTx.FeeGranter()) != 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not supported") + } + + return next(ctx, tx, simulate) +} diff --git a/x/auth/ante/fee_grant_test.go b/x/auth/ante/fee_grant_test.go new file mode 100644 index 000000000000..2f07300c7fc4 --- /dev/null +++ b/x/auth/ante/fee_grant_test.go @@ -0,0 +1,32 @@ +package ante_test + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/tx" +) + +type setFeeGranter interface { + SetFeeGranter(feeGranter sdk.AccAddress) +} + +func (suite *AnteTestSuite) TestRejectFeeGranter() { + suite.SetupTest(true) // setup + txConfig := tx.NewTxConfig(codec.NewProtoCodec(types.NewInterfaceRegistry()), tx.DefaultSignModes) + txBuilder := txConfig.NewTxBuilder() + d := ante.NewRejectFeeGranterDecorator() + antehandler := sdk.ChainAnteDecorators(d) + + _, err := antehandler(suite.ctx, txBuilder.GetTx(), false) + suite.Require().NoError(err) + + setGranterTx := txBuilder.(setFeeGranter) + _, _, addr := testdata.KeyTestPubAddr() + setGranterTx.SetFeeGranter(addr) + + _, err = antehandler(suite.ctx, txBuilder.GetTx(), false) + suite.Require().Error(err) +} diff --git a/x/auth/ante/fee_test.go b/x/auth/ante/fee_test.go index b088c1961951..84e5adb60db7 100644 --- a/x/auth/ante/fee_test.go +++ b/x/auth/ante/fee_test.go @@ -1,98 +1,101 @@ package ante_test import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/types" ) -func TestEnsureMempoolFees(t *testing.T) { - // setup - _, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestEnsureMempoolFees() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() mfd := ante.NewMempoolFeeDecorator() antehandler := sdk.ChainAnteDecorators(mfd) // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1} + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) // Set high gas price so standard test fee fails atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) highGasPrice := []sdk.DecCoin{atomPrice} - ctx = ctx.WithMinGasPrices(highGasPrice) + suite.ctx = suite.ctx.WithMinGasPrices(highGasPrice) // Set IsCheckTx to true - ctx = ctx.WithIsCheckTx(true) + suite.ctx = suite.ctx.WithIsCheckTx(true) // antehandler errors with insufficient fees - _, err := antehandler(ctx, tx, false) - require.NotNil(t, err, "Decorator should have errored on too low fee for local gasPrice") + _, err = antehandler(suite.ctx, tx, false) + suite.Require().NotNil(err, "Decorator should have errored on too low fee for local gasPrice") // Set IsCheckTx to false - ctx = ctx.WithIsCheckTx(false) + suite.ctx = suite.ctx.WithIsCheckTx(false) // antehandler should not error since we do not check minGasPrice in DeliverTx - _, err = antehandler(ctx, tx, false) - require.Nil(t, err, "MempoolFeeDecorator returned error in DeliverTx") + _, err = antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "MempoolFeeDecorator returned error in DeliverTx") // Set IsCheckTx back to true for testing sufficient mempool fee - ctx = ctx.WithIsCheckTx(true) + suite.ctx = suite.ctx.WithIsCheckTx(true) atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) lowGasPrice := []sdk.DecCoin{atomPrice} - ctx = ctx.WithMinGasPrices(lowGasPrice) + suite.ctx = suite.ctx.WithMinGasPrices(lowGasPrice) - _, err = antehandler(ctx, tx, false) - require.Nil(t, err, "Decorator should not have errored on fee higher than local gasPrice") + _, err = antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "Decorator should not have errored on fee higher than local gasPrice") } -func TestDeductFees(t *testing.T) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestDeductFees() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1} + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) // Set account with insufficient funds - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) - app.AccountKeeper.SetAccount(ctx, acc) + 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)))) - dfd := ante.NewDeductFeeDecorator(app.AccountKeeper, app.SupplyKeeper) + dfd := ante.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper) antehandler := sdk.ChainAnteDecorators(dfd) - _, err := antehandler(ctx, tx, false) + _, err = antehandler(suite.ctx, tx, false) - require.NotNil(t, err, "Tx did not error when fee payer had insufficient funds") + suite.Require().NotNil(err, "Tx did not error when fee payer had insufficient funds") // Set account with sufficient funds - acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(200))}) - app.AccountKeeper.SetAccount(ctx, acc) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.app.BankKeeper.SetBalances(suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) - _, err = antehandler(ctx, tx, false) + _, err = antehandler(suite.ctx, tx, false) - require.Nil(t, err, "Tx errored after account has been set with sufficient funds") + suite.Require().Nil(err, "Tx errored after account has been set with sufficient funds") } diff --git a/x/auth/ante/integration_test.go b/x/auth/ante/integration_test.go deleted file mode 100644 index 4c232b2bbea8..000000000000 --- a/x/auth/ante/integration_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package ante_test - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// 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, abci.Header{}) - app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) - - return app, ctx -} diff --git a/x/auth/ante/setup.go b/x/auth/ante/setup.go index 1e3e8eb68d67..5f21aba8bd5c 100644 --- a/x/auth/ante/setup.go +++ b/x/auth/ante/setup.go @@ -5,11 +5,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) var ( - _ GasTx = (*types.StdTx)(nil) // assert StdTx implements GasTx + _ GasTx = (*legacytx.StdTx)(nil) // assert StdTx implements GasTx ) // GasTx defines a Tx with a GetGas() method which is needed to use SetUpContextDecorator diff --git a/x/auth/ante/setup_test.go b/x/auth/ante/setup_test.go index 7b7352dbbd26..4942665cac04 100644 --- a/x/auth/ante/setup_test.go +++ b/x/auth/ante/setup_test.go @@ -1,80 +1,82 @@ package ante_test import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/types" ) -func TestSetup(t *testing.T) { - // setup - _, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestSetup() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - msgs := []sdk.Msg{msg1} - - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) sud := ante.NewSetUpContextDecorator() antehandler := sdk.ChainAnteDecorators(sud) // Set height to non-zero value for GasMeter to be set - ctx = ctx.WithBlockHeight(1) + suite.ctx = suite.ctx.WithBlockHeight(1) // Context GasMeter Limit not set - require.Equal(t, uint64(0), ctx.GasMeter().Limit(), "GasMeter set with limit before setup") + suite.Require().Equal(uint64(0), suite.ctx.GasMeter().Limit(), "GasMeter set with limit before setup") - newCtx, err := antehandler(ctx, tx, false) - require.Nil(t, err, "SetUpContextDecorator returned error") + newCtx, err := antehandler(suite.ctx, tx, false) + suite.Require().Nil(err, "SetUpContextDecorator returned error") // Context GasMeter Limit should be set after SetUpContextDecorator runs - require.Equal(t, fee.Gas, newCtx.GasMeter().Limit(), "GasMeter not set correctly") + suite.Require().Equal(gasLimit, newCtx.GasMeter().Limit(), "GasMeter not set correctly") } -func TestRecoverPanic(t *testing.T) { - // setup - _, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestRecoverPanic() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() // msg and signatures - msg1 := types.NewTestMsg(addr1) - fee := types.NewTestStdFee() - - msgs := []sdk.Msg{msg1} + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) sud := ante.NewSetUpContextDecorator() antehandler := sdk.ChainAnteDecorators(sud, OutOfGasDecorator{}) // Set height to non-zero value for GasMeter to be set - ctx = ctx.WithBlockHeight(1) + suite.ctx = suite.ctx.WithBlockHeight(1) - newCtx, err := antehandler(ctx, tx, false) + newCtx, err := antehandler(suite.ctx, tx, false) - require.NotNil(t, err, "Did not return error on OutOfGas panic") + suite.Require().NotNil(err, "Did not return error on OutOfGas panic") - require.True(t, sdkerrors.ErrOutOfGas.Is(err), "Returned error is not an out of gas error") - require.Equal(t, fee.Gas, newCtx.GasMeter().Limit()) + suite.Require().True(sdkerrors.ErrOutOfGas.Is(err), "Returned error is not an out of gas error") + suite.Require().Equal(gasLimit, newCtx.GasMeter().Limit()) antehandler = sdk.ChainAnteDecorators(sud, PanicDecorator{}) - require.Panics(t, func() { antehandler(ctx, tx, false) }, "Recovered from non-Out-of-Gas panic") + suite.Require().Panics(func() { antehandler(suite.ctx, tx, false) }, "Recovered from non-Out-of-Gas panic") // nolint:errcheck } type OutOfGasDecorator struct{} diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 4498b432d73d..79fbbcdc676a 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -3,63 +3,57 @@ package ante import ( "bytes" "encoding/hex" + "fmt" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/auth/types" ) var ( // simulation signature values used to estimate gas consumption - simSecp256k1Pubkey secp256k1.PubKeySecp256k1 + key = make([]byte, secp256k1.PubKeySize) + simSecp256k1Pubkey = &secp256k1.PubKey{Key: key} simSecp256k1Sig [64]byte - _ SigVerifiableTx = (*types.StdTx)(nil) // assert StdTx implements SigVerifiableTx + _ authsigning.SigVerifiableTx = (*legacytx.StdTx)(nil) // assert StdTx implements SigVerifiableTx ) func init() { // This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") - copy(simSecp256k1Pubkey[:], bz) + copy(key, bz) + simSecp256k1Pubkey.Key = key } // SignatureVerificationGasConsumer is the type of function that is used to both // consume gas when verifying signatures and also to accept or reject different types of pubkeys // This is where apps can define their own PubKey -type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params types.Params) error - -// SigVerifiableTx defines a Tx interface for all signature verification decorators -type SigVerifiableTx interface { - sdk.Tx - GetSignatures() [][]byte - GetSigners() []sdk.AccAddress - GetPubKeys() []crypto.PubKey // If signer already has pubkey in context, this list will have nil in its place - GetSignBytes(ctx sdk.Context, acc exported.Account) []byte -} +type SignatureVerificationGasConsumer = func(meter sdk.GasMeter, sig signing.SignatureV2, params types.Params) error // SetPubKeyDecorator sets PubKeys in context for any signer which does not already have pubkey set // PubKeys must be set in context for all signers before any other sigverify decorators run // CONTRACT: Tx must implement SigVerifiableTx interface type SetPubKeyDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper } -func NewSetPubKeyDecorator(ak keeper.AccountKeeper) SetPubKeyDecorator { +func NewSetPubKeyDecorator(ak AccountKeeper) SetPubKeyDecorator { return SetPubKeyDecorator{ ak: ak, } } func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") } @@ -104,11 +98,11 @@ func (spkd SetPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b // CONTRACT: Pubkeys are set in context for all signers before this decorator runs // CONTRACT: Tx must implement SigVerifiableTx interface type SigGasConsumeDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper sigGasConsumer SignatureVerificationGasConsumer } -func NewSigGasConsumeDecorator(ak keeper.AccountKeeper, sigGasConsumer SignatureVerificationGasConsumer) SigGasConsumeDecorator { +func NewSigGasConsumeDecorator(ak AccountKeeper, sigGasConsumer SignatureVerificationGasConsumer) SigGasConsumeDecorator { return SigGasConsumeDecorator{ ak: ak, sigGasConsumer: sigGasConsumer, @@ -116,13 +110,16 @@ func NewSigGasConsumeDecorator(ak keeper.AccountKeeper, sigGasConsumer Signature } func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } params := sgcd.ak.GetParams(ctx) - sigs := sigTx.GetSignatures() + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return ctx, err + } // stdSigs contains the sequence number, account number, and signatures. // When simulating, this would just be a 0-length slice. @@ -133,18 +130,25 @@ func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula if err != nil { return ctx, err } + pubKey := signerAcc.GetPubKey() + // In simulate mode the transaction comes with no signatures, thus if the + // account's pubkey is nil, both signature verification and gasKVStore.Set() + // shall consume the largest amount, i.e. it takes more gas to verify + // secp256k1 keys than ed25519 ones. if simulate && pubKey == nil { - // In simulate mode the transaction comes with no signatures, thus if the - // account's pubkey is nil, both signature verification and gasKVStore.Set() - // shall consume the largest amount, i.e. it takes more gas to verify - // secp256k1 keys than ed25519 ones. - if pubKey == nil { - pubKey = simSecp256k1Pubkey - } + pubKey = simSecp256k1Pubkey + } + + // make a SignatureV2 with PubKey filled in from above + sig = signing.SignatureV2{ + PubKey: pubKey, + Data: sig.Data, + Sequence: sig.Sequence, } - err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, pubKey, params) + + err = sgcd.sigGasConsumer(ctx.GasMeter(), sig, params) if err != nil { return ctx, err } @@ -159,12 +163,35 @@ func (sgcd SigGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // CONTRACT: Pubkeys are set in context for all signers before this decorator runs // CONTRACT: Tx must implement SigVerifiableTx interface type SigVerificationDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper + signModeHandler authsigning.SignModeHandler } -func NewSigVerificationDecorator(ak keeper.AccountKeeper) SigVerificationDecorator { +func NewSigVerificationDecorator(ak AccountKeeper, signModeHandler authsigning.SignModeHandler) SigVerificationDecorator { return SigVerificationDecorator{ - ak: ak, + ak: ak, + signModeHandler: signModeHandler, + } +} + +// OnlyLegacyAminoSigners checks SignatureData to see if all +// signers are using SIGN_MODE_LEGACY_AMINO_JSON. If this is the case +// then the corresponding SignatureV2 struct will not have account sequence +// explicitly set, and we should skip the explicit verification of sig.Sequence +// in the SigVerificationDecorator's AnteHandler function. +func OnlyLegacyAminoSigners(sigData signing.SignatureData) bool { + switch v := sigData.(type) { + case *signing.SingleSignatureData: + return v.SignMode == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON + case *signing.MultiSignatureData: + for _, s := range v.Signatures { + if !OnlyLegacyAminoSigners(s) { + return false + } + } + return true + default: + return false } } @@ -173,19 +200,19 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul if ctx.IsReCheckTx() { return next(ctx, tx, simulate) } - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } // stdSigs contains the sequence number, account number, and signatures. // When simulating, this would just be a 0-length slice. - sigs := sigTx.GetSignatures() + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return ctx, err + } - // stdSigs contains the sequence number, account number, and signatures. - // When simulating, this would just be a 0-length slice. signerAddrs := sigTx.GetSigners() - signerAccs := make([]exported.Account, len(signerAddrs)) // check that signer length and signature length are the same if len(sigs) != len(signerAddrs) { @@ -193,23 +220,59 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul } for i, sig := range sigs { - signerAccs[i], err = GetSignerAcc(ctx, svd.ak, signerAddrs[i]) + acc, err := GetSignerAcc(ctx, svd.ak, signerAddrs[i]) if err != nil { return ctx, err } - // retrieve signBytes of tx - signBytes := sigTx.GetSignBytes(ctx, signerAccs[i]) - // retrieve pubkey - pubKey := signerAccs[i].GetPubKey() + pubKey := acc.GetPubKey() if !simulate && pubKey == nil { return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "pubkey on account is not set") } - // verify signature - if !simulate && !pubKey.VerifyBytes(signBytes, sig) { - return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "signature verification failed; verify correct account sequence and chain-id") + // Check account sequence number. + // When using Amino StdSignatures, we actually don't have the Sequence in + // the SignatureV2 struct (it's only in the SignDoc). In this case, we + // cannot check sequence directly, and must do it via signature + // verification (in the VerifySignature call below). + onlyAminoSigners := OnlyLegacyAminoSigners(sig.Data) + if !onlyAminoSigners { + if sig.Sequence != acc.GetSequence() { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrWrongSequence, + "account sequence mismatch, expected %d, got %d", acc.GetSequence(), sig.Sequence, + ) + } + } + + // retrieve signer data + genesis := ctx.BlockHeight() == 0 + chainID := ctx.ChainID() + var accNum uint64 + if !genesis { + accNum = acc.GetAccountNumber() + } + signerData := authsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNum, + Sequence: acc.GetSequence(), + } + + if !simulate { + err := authsigning.VerifySignature(pubKey, signerData, sig.Data, svd.signModeHandler, tx) + if err != nil { + var errMsg string + if onlyAminoSigners { + // If all signers are using SIGN_MODE_LEGACY_AMINO, we rely on VerifySignature to check account sequence number, + // and therefore communicate sequence number as a potential cause of error. + errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d), sequence (%d) and chain-id (%s)", accNum, acc.GetSequence(), chainID) + } else { + errMsg = fmt.Sprintf("signature verification failed; please verify account number (%d) and chain-id (%s)", accNum, chainID) + } + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, errMsg) + + } } } @@ -218,30 +281,25 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul // IncrementSequenceDecorator handles incrementing sequences of all signers. // Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note, -// there is no need to execute IncrementSequenceDecorator on CheckTx or RecheckTX -// since it is merely updating the nonce. As a result, this has the side effect -// that subsequent and sequential txs orginating from the same account cannot be -// handled correctly in a reliable way. To send sequential txs orginating from the -// same account, it is recommended to instead use multiple messages in a tx. +// there is no need to execute IncrementSequenceDecorator on RecheckTX since +// CheckTx would already bump the sequence number. // -// CONTRACT: The tx must implement the SigVerifiableTx interface. +// NOTE: Since CheckTx and DeliverTx state are managed separately, subsequent and +// sequential txs orginating from the same account cannot be handled correctly in +// a reliable way unless sequence numbers are managed and tracked manually by a +// client. It is recommended to instead use multiple messages in a tx. type IncrementSequenceDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper } -func NewIncrementSequenceDecorator(ak keeper.AccountKeeper) IncrementSequenceDecorator { +func NewIncrementSequenceDecorator(ak AccountKeeper) IncrementSequenceDecorator { return IncrementSequenceDecorator{ ak: ak, } } func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - // no need to increment sequence on CheckTx or RecheckTx - if ctx.IsCheckTx() && !simulate { - return next(ctx, tx, simulate) - } - - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") } @@ -252,6 +310,7 @@ func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { panic(err) } + isd.ak.SetAccount(ctx, acc) } @@ -263,17 +322,17 @@ func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim // Use this decorator to set parameterized limit on number of signatures in tx // CONTRACT: Tx must implement SigVerifiableTx interface type ValidateSigCountDecorator struct { - ak keeper.AccountKeeper + ak AccountKeeper } -func NewValidateSigCountDecorator(ak keeper.AccountKeeper) ValidateSigCountDecorator { +func NewValidateSigCountDecorator(ak AccountKeeper) ValidateSigCountDecorator { return ValidateSigCountDecorator{ ak: ak, } } func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - sigTx, ok := tx.(SigVerifiableTx) + sigTx, ok := tx.(authsigning.SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a sigTx") } @@ -283,7 +342,7 @@ func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim sigCount := 0 for _, pk := range pubKeys { - sigCount += types.CountSubKeys(pk) + sigCount += CountSubKeys(pk) if uint64(sigCount) > params.TxSigLimit { return ctx, sdkerrors.Wrapf(sdkerrors.ErrTooManySignatures, "signatures: %d, limit: %d", sigCount, params.TxSigLimit) @@ -297,22 +356,27 @@ func (vscd ValidateSigCountDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim // for signature verification based upon the public key type. The cost is fetched from the given params and is matched // by the concrete type. func DefaultSigVerificationGasConsumer( - meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params types.Params, + meter sdk.GasMeter, sig signing.SignatureV2, params types.Params, ) error { + pubkey := sig.PubKey switch pubkey := pubkey.(type) { - case ed25519.PubKeyEd25519: + case *ed25519.PubKey: meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") return sdkerrors.Wrap(sdkerrors.ErrInvalidPubKey, "ED25519 public keys are unsupported") - case secp256k1.PubKeySecp256k1: + case *secp256k1.PubKey: meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") return nil - case multisig.PubKeyMultisigThreshold: - var multisignature multisig.Multisignature - codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature) - - ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params) + case multisig.PubKey: + multisignature, ok := sig.Data.(*signing.MultiSignatureData) + if !ok { + return fmt.Errorf("expected %T, got, %T", &signing.MultiSignatureData{}, sig.Data) + } + err := ConsumeMultisignatureVerificationGas(meter, multisignature, pubkey, params, sig.Sequence) + if err != nil { + return err + } return nil default: @@ -321,24 +385,54 @@ func DefaultSigVerificationGasConsumer( } // ConsumeMultisignatureVerificationGas consumes gas from a GasMeter for verifying a multisig pubkey signature -func ConsumeMultisignatureVerificationGas(meter sdk.GasMeter, - sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold, - params types.Params) { - size := sig.BitArray.Size() +func ConsumeMultisignatureVerificationGas( + meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey, + params types.Params, accSeq uint64, +) error { + + size := sig.BitArray.Count() sigIndex := 0 + for i := 0; i < size; i++ { - if sig.BitArray.GetIndex(i) { - DefaultSigVerificationGasConsumer(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params) - sigIndex++ + if !sig.BitArray.GetIndex(i) { + continue } + sigV2 := signing.SignatureV2{ + PubKey: pubkey.GetPubKeys()[i], + Data: sig.Signatures[sigIndex], + Sequence: accSeq, + } + err := DefaultSigVerificationGasConsumer(meter, sigV2, params) + if err != nil { + return err + } + sigIndex++ } + + return nil } // GetSignerAcc returns an account for a given address that is expected to sign // a transaction. -func GetSignerAcc(ctx sdk.Context, ak keeper.AccountKeeper, addr sdk.AccAddress) (exported.Account, error) { +func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) (types.AccountI, error) { if acc := ak.GetAccount(ctx, addr); acc != nil { return acc, nil } + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) } + +// CountSubKeys counts the total number of keys for a multi-sig public key. +func CountSubKeys(pub cryptotypes.PubKey) int { + v, ok := pub.(*kmultisig.LegacyAminoPubKey) + if !ok { + return 1 + } + + numKeys := 0 + for _, subkey := range v.GetPubKeys() { + numKeys += CountSubKeys(subkey) + } + + return numKeys +} diff --git a/x/auth/ante/sigverify_test.go b/x/auth/ante/sigverify_test.go index 58bcef1e2811..337618fab50f 100644 --- a/x/auth/ante/sigverify_test.go +++ b/x/auth/ante/sigverify_test.go @@ -2,75 +2,89 @@ package ante_test import ( "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "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/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/auth/types" ) -func TestSetPubKey(t *testing.T) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestSetPubKey() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, pub1, addr1 := types.KeyTestPubAddr() - priv2, pub2, addr2 := types.KeyTestPubAddr() - priv3, pub3, addr3 := types.KeyTestPubAddr() + priv1, pub1, addr1 := testdata.KeyTestPubAddr() + priv2, pub2, addr2 := testdata.KeyTestPubAddr() + priv3, pub3, addr3 := testdata.KeyTestPubAddr() addrs := []sdk.AccAddress{addr1, addr2, addr3} - pubs := []crypto.PubKey{pub1, pub2, pub3} + pubs := []cryptotypes.PubKey{pub1, pub2, pub3} msgs := make([]sdk.Msg, len(addrs)) // set accounts and create msg for each address for i, addr := range addrs { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - require.NoError(t, acc.SetAccountNumber(uint64(i))) - app.AccountKeeper.SetAccount(ctx, acc) - msgs[i] = types.NewTestMsg(addr) + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.Require().NoError(acc.SetAccountNumber(uint64(i))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + msgs[i] = testdata.NewTestMsg(addr) } + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) - fee := types.NewTestStdFee() + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - privs, accNums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) - spkd := ante.NewSetPubKeyDecorator(app.AccountKeeper) + spkd := ante.NewSetPubKeyDecorator(suite.app.AccountKeeper) antehandler := sdk.ChainAnteDecorators(spkd) - ctx, err := antehandler(ctx, tx, false) - require.Nil(t, err) + ctx, err := antehandler(suite.ctx, tx, false) + suite.Require().Nil(err) // Require that all accounts have pubkey set after Decorator runs for i, addr := range addrs { - pk, err := app.AccountKeeper.GetPubKey(ctx, addr) - require.Nil(t, err, "Error on retrieving pubkey from account") - require.Equal(t, pubs[i], pk, "Pubkey retrieved from account is unexpected") + pk, err := suite.app.AccountKeeper.GetPubKey(ctx, addr) + suite.Require().Nil(err, "Error on retrieving pubkey from account") + suite.Require().Equal(pubs[i], pk, "Pubkey retrieved from account is unexpected") } } -func TestConsumeSignatureVerificationGas(t *testing.T) { +func (suite *AnteTestSuite) TestConsumeSignatureVerificationGas() { params := types.DefaultParams() msg := []byte{1, 2, 3, 4} + _, cdc := simapp.MakeCodecs() pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false) - multisigKey1 := multisig.NewPubKeyMultisigThreshold(2, pkSet1) + multisigKey1 := kmultisig.NewLegacyAminoPubKey(2, pkSet1) multisignature1 := multisig.NewMultisig(len(pkSet1)) expectedCost1 := expectedGasCostByKeys(pkSet1) for i := 0; i < len(pkSet1); i++ { - multisignature1.AddSignatureFromPubKey(sigSet1[i], pkSet1[i], pkSet1) + stdSig := legacytx.StdSignature{PubKey: pkSet1[i], Signature: sigSet1[i]} + sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig) + suite.Require().NoError(err) + err = multisig.AddSignatureV2(multisignature1, sigV2, pkSet1) + suite.Require().NoError(err) } type args struct { meter sdk.GasMeter - sig []byte - pubkey crypto.PubKey + sig signing.SignatureData + pubkey cryptotypes.PubKey params types.Params } tests := []struct { @@ -81,133 +95,282 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { }{ {"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, types.DefaultSigVerifyCostED25519, true}, {"PubKeySecp256k1", args{sdk.NewInfiniteGasMeter(), nil, secp256k1.GenPrivKey().PubKey(), params}, types.DefaultSigVerifyCostSecp256k1, false}, - {"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1.Marshal(), multisigKey1, params}, expectedCost1, false}, + {"Multisig", args{sdk.NewInfiniteGasMeter(), multisignature1, multisigKey1, params}, expectedCost1, false}, {"unknown key", args{sdk.NewInfiniteGasMeter(), nil, nil, params}, 0, true}, } for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - err := ante.DefaultSigVerificationGasConsumer(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) - - if tt.shouldErr { - require.NotNil(t, err) - } else { - require.Nil(t, err) - require.Equal(t, tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed())) - } - }) + sigV2 := signing.SignatureV2{ + PubKey: tt.args.pubkey, + Data: tt.args.sig, + Sequence: 0, // Arbitrary account sequence + } + err := ante.DefaultSigVerificationGasConsumer(tt.args.meter, sigV2, tt.args.params) + + if tt.shouldErr { + suite.Require().NotNil(err) + } else { + suite.Require().Nil(err) + suite.Require().Equal(tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed())) + } } } -func TestSigVerification(t *testing.T) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) TestSigVerification() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + // make block height non-zero to ensure account numbers part of signBytes - ctx = ctx.WithBlockHeight(1) + suite.ctx = suite.ctx.WithBlockHeight(1) // keys and addresses - priv1, _, addr1 := types.KeyTestPubAddr() - priv2, _, addr2 := types.KeyTestPubAddr() - priv3, _, addr3 := types.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, addr2 := testdata.KeyTestPubAddr() + priv3, _, addr3 := testdata.KeyTestPubAddr() addrs := []sdk.AccAddress{addr1, addr2, addr3} msgs := make([]sdk.Msg, len(addrs)) // set accounts and create msg for each address for i, addr := range addrs { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - require.NoError(t, acc.SetAccountNumber(uint64(i))) - app.AccountKeeper.SetAccount(ctx, acc) - msgs[i] = types.NewTestMsg(addr) + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.Require().NoError(acc.SetAccountNumber(uint64(i))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + msgs[i] = testdata.NewTestMsg(addr) } - fee := types.NewTestStdFee() + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() - spkd := ante.NewSetPubKeyDecorator(app.AccountKeeper) - svd := ante.NewSigVerificationDecorator(app.AccountKeeper) + spkd := ante.NewSetPubKeyDecorator(suite.app.AccountKeeper) + svd := ante.NewSigVerificationDecorator(suite.app.AccountKeeper, suite.clientCtx.TxConfig.SignModeHandler()) antehandler := sdk.ChainAnteDecorators(spkd, svd) type testCase struct { name string - privs []crypto.PrivKey + privs []cryptotypes.PrivKey accNums []uint64 - seqs []uint64 + accSeqs []uint64 recheck bool shouldErr bool } testCases := []testCase{ - {"no signers", []crypto.PrivKey{}, []uint64{}, []uint64{}, false, true}, - {"not enough signers", []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0}, false, true}, - {"wrong order signers", []crypto.PrivKey{priv3, priv2, priv1}, []uint64{2, 1, 0}, []uint64{0, 0, 0}, false, true}, - {"wrong accnums", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, false, true}, - {"wrong sequences", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{3, 4, 5}, false, true}, - {"valid tx", []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0}, false, false}, - {"no err on recheck", []crypto.PrivKey{}, []uint64{}, []uint64{}, true, false}, + {"no signers", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, false, true}, + {"not enough signers", []cryptotypes.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0}, false, true}, + {"wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{2, 1, 0}, []uint64{0, 0, 0}, false, true}, + {"wrong accnums", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, false, true}, + {"wrong sequences", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{3, 4, 5}, false, true}, + {"valid tx", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0}, false, false}, + {"no err on recheck", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, true, false}, } for i, tc := range testCases { - ctx = ctx.WithIsReCheckTx(tc.recheck) + suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test + + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - tx := types.NewTestTx(ctx, msgs, tc.privs, tc.accNums, tc.seqs, fee) + tx, err := suite.CreateTestTx(tc.privs, tc.accNums, tc.accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) - _, err := antehandler(ctx, tx, false) + _, err = antehandler(suite.ctx, tx, false) if tc.shouldErr { - require.NotNil(t, err, "TestCase %d: %s did not error as expected", i, tc.name) + suite.Require().NotNil(err, "TestCase %d: %s did not error as expected", i, tc.name) } else { - require.Nil(t, err, "TestCase %d: %s errored unexpectedly. Err: %v", i, tc.name, err) + suite.Require().Nil(err, "TestCase %d: %s errored unexpectedly. Err: %v", i, tc.name, err) } } } -func TestSigIntegration(t *testing.T) { +// This test is exactly like the one above, but we set the codec explicitly to +// Amino. +// Once https://github.com/cosmos/cosmos-sdk/issues/6190 is in, we can remove +// this, since it'll be handled by the test matrix. +// In the meantime, we want to make double-sure amino compatibility works. +// ref: https://github.com/cosmos/cosmos-sdk/issues/7229 +func (suite *AnteTestSuite) TestSigVerification_ExplicitAmino() { + suite.app, suite.ctx = createTestApp(true) + suite.ctx = suite.ctx.WithBlockHeight(1) + + // Set up TxConfig. + aminoCdc := codec.NewLegacyAmino() + // We're using TestMsg amino encoding in some tests, so register it here. + txConfig := legacytx.StdTxConfig{Cdc: aminoCdc} + + suite.clientCtx = client.Context{}. + WithTxConfig(txConfig) + + suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, ante.DefaultSigVerificationGasConsumer, txConfig.SignModeHandler()) + + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + // make block height non-zero to ensure account numbers part of signBytes + suite.ctx = suite.ctx.WithBlockHeight(1) + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, addr2 := testdata.KeyTestPubAddr() + priv3, _, addr3 := testdata.KeyTestPubAddr() + + addrs := []sdk.AccAddress{addr1, addr2, addr3} + + msgs := make([]sdk.Msg, len(addrs)) + // set accounts and create msg for each address + for i, addr := range addrs { + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.Require().NoError(acc.SetAccountNumber(uint64(i))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + msgs[i] = testdata.NewTestMsg(addr) + } + + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + spkd := ante.NewSetPubKeyDecorator(suite.app.AccountKeeper) + svd := ante.NewSigVerificationDecorator(suite.app.AccountKeeper, suite.clientCtx.TxConfig.SignModeHandler()) + antehandler := sdk.ChainAnteDecorators(spkd, svd) + + type testCase struct { + name string + privs []cryptotypes.PrivKey + accNums []uint64 + accSeqs []uint64 + recheck bool + shouldErr bool + } + testCases := []testCase{ + {"no signers", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, false, true}, + {"not enough signers", []cryptotypes.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0}, false, true}, + {"wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{2, 1, 0}, []uint64{0, 0, 0}, false, true}, + {"wrong accnums", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, false, true}, + {"wrong sequences", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{3, 4, 5}, false, true}, + {"valid tx", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0}, false, false}, + {"no err on recheck", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, true, false}, + } + for i, tc := range testCases { + suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck) + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test + + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + tx, err := suite.CreateTestTx(tc.privs, tc.accNums, tc.accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + _, err = antehandler(suite.ctx, tx, false) + if tc.shouldErr { + suite.Require().NotNil(err, "TestCase %d: %s did not error as expected", i, tc.name) + } else { + suite.Require().Nil(err, "TestCase %d: %s errored unexpectedly. Err: %v", i, tc.name, err) + } + } +} + +func (suite *AnteTestSuite) TestSigIntegration() { // generate private keys - privs := []crypto.PrivKey{secp256k1.GenPrivKey(), secp256k1.GenPrivKey(), secp256k1.GenPrivKey()} + privs := []cryptotypes.PrivKey{ + secp256k1.GenPrivKey(), + secp256k1.GenPrivKey(), + secp256k1.GenPrivKey(), + } params := types.DefaultParams() initialSigCost := params.SigVerifyCostSecp256k1 - initialCost, err := runSigDecorators(t, params, false, privs...) - require.Nil(t, err) + initialCost, err := suite.runSigDecorators(params, false, privs...) + suite.Require().Nil(err) params.SigVerifyCostSecp256k1 *= 2 - doubleCost, err := runSigDecorators(t, params, false, privs...) - require.Nil(t, err) + doubleCost, err := suite.runSigDecorators(params, false, privs...) + suite.Require().Nil(err) - require.Equal(t, initialSigCost*uint64(len(privs)), doubleCost-initialCost) + suite.Require().Equal(initialSigCost*uint64(len(privs)), doubleCost-initialCost) } -func runSigDecorators(t *testing.T, params types.Params, multisig bool, privs ...crypto.PrivKey) (sdk.Gas, error) { - // setup - app, ctx := createTestApp(true) +func (suite *AnteTestSuite) runSigDecorators(params types.Params, _ bool, privs ...cryptotypes.PrivKey) (sdk.Gas, error) { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + // Make block-height non-zero to include accNum in SignBytes - ctx = ctx.WithBlockHeight(1) - app.AccountKeeper.SetParams(ctx, params) + suite.ctx = suite.ctx.WithBlockHeight(1) + suite.app.AccountKeeper.SetParams(suite.ctx, params) msgs := make([]sdk.Msg, len(privs)) accNums := make([]uint64, len(privs)) - seqs := make([]uint64, len(privs)) + accSeqs := make([]uint64, len(privs)) // set accounts and create msg for each address for i, priv := range privs { addr := sdk.AccAddress(priv.PubKey().Address()) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - require.NoError(t, acc.SetAccountNumber(uint64(i))) - app.AccountKeeper.SetAccount(ctx, acc) - msgs[i] = types.NewTestMsg(addr) + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.Require().NoError(acc.SetAccountNumber(uint64(i))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + msgs[i] = testdata.NewTestMsg(addr) accNums[i] = uint64(i) - seqs[i] = uint64(0) + accSeqs[i] = uint64(0) } + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) - fee := types.NewTestStdFee() + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) - tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) - spkd := ante.NewSetPubKeyDecorator(app.AccountKeeper) - svgc := ante.NewSigGasConsumeDecorator(app.AccountKeeper, ante.DefaultSigVerificationGasConsumer) - svd := ante.NewSigVerificationDecorator(app.AccountKeeper) + spkd := ante.NewSetPubKeyDecorator(suite.app.AccountKeeper) + svgc := ante.NewSigGasConsumeDecorator(suite.app.AccountKeeper, ante.DefaultSigVerificationGasConsumer) + svd := ante.NewSigVerificationDecorator(suite.app.AccountKeeper, suite.clientCtx.TxConfig.SignModeHandler()) antehandler := sdk.ChainAnteDecorators(spkd, svgc, svd) // Determine gas consumption of antehandler with default params - before := ctx.GasMeter().GasConsumed() - ctx, err := antehandler(ctx, tx, false) + before := suite.ctx.GasMeter().GasConsumed() + ctx, err := antehandler(suite.ctx, tx, false) after := ctx.GasMeter().GasConsumed() return after - before, err } + +func (suite *AnteTestSuite) TestIncrementSequenceDecorator() { + suite.SetupTest(true) // setup + suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() + + priv, _, addr := testdata.KeyTestPubAddr() + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + suite.Require().NoError(acc.SetAccountNumber(uint64(50))) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + privs := []cryptotypes.PrivKey{priv} + accNums := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetAccountNumber()} + accSeqs := []uint64{suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()} + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) + suite.Require().NoError(err) + + isd := ante.NewIncrementSequenceDecorator(suite.app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(isd) + + testCases := []struct { + ctx sdk.Context + simulate bool + expectedSeq uint64 + }{ + {suite.ctx.WithIsReCheckTx(true), false, 1}, + {suite.ctx.WithIsCheckTx(true).WithIsReCheckTx(false), false, 2}, + {suite.ctx.WithIsReCheckTx(true), false, 3}, + {suite.ctx.WithIsReCheckTx(true), false, 4}, + {suite.ctx.WithIsReCheckTx(true), true, 5}, + } + + for i, tc := range testCases { + _, err := antehandler(tc.ctx, tx, tc.simulate) + suite.Require().NoError(err, "unexpected error; tc #%d, %v", i, tc) + suite.Require().Equal(tc.expectedSeq, suite.app.AccountKeeper.GetAccount(suite.ctx, addr).GetSequence()) + } +} diff --git a/x/auth/ante/testutil_test.go b/x/auth/ante/testutil_test.go new file mode 100644 index 000000000000..5a1cfc4ec44c --- /dev/null +++ b/x/auth/ante/testutil_test.go @@ -0,0 +1,182 @@ +package ante_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + 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/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// TestAccount represents an account used in the tests in x/auth/ante. +type TestAccount struct { + acc types.AccountI + priv cryptotypes.PrivKey +} + +// 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 +} + +// 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 +} + +// 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, ante.DefaultSigVerificationGasConsumer, encodingConfig.TxConfig.SignModeHandler()) +} + +// CreateTestAccounts creates `numAccs` accounts, and return all relevant +// information about them including their private keys. +func (suite *AnteTestSuite) CreateTestAccounts(numAccs int) []TestAccount { + var accounts []TestAccount + + for i := 0; i < numAccs; i++ { + priv, _, addr := testdata.KeyTestPubAddr() + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr) + 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{ + sdk.NewInt64Coin("atom", 10000000), + }) + + accounts = append(accounts, TestAccount{acc, priv}) + } + + return accounts +} + +// CreateTestTx is a helper function to create a tx given multiple inputs. +func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + err := suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := tx.SignWithPrivKey( + suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData, + suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + return suite.txBuilder.GetTx(), nil +} + +// TestCase represents a test case used in test tables. +type TestCase struct { + desc string + malleate func() + simulate bool + expPass bool + expErr error +} + +// CreateTestTx is a helper function to create a tx given multiple inputs. +func (suite *AnteTestSuite) RunTestCase(privs []cryptotypes.PrivKey, msgs []sdk.Msg, feeAmount sdk.Coins, gasLimit uint64, accNums, accSeqs []uint64, chainID string, tc TestCase) { + suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + suite.Require().NoError(suite.txBuilder.SetMsgs(msgs...)) + suite.txBuilder.SetFeeAmount(feeAmount) + suite.txBuilder.SetGasLimit(gasLimit) + + // Theoretically speaking, ante handler unit tests should only test + // ante handlers, but here we sometimes also test the tx creation + // process. + tx, txErr := suite.CreateTestTx(privs, accNums, accSeqs, chainID) + newCtx, anteErr := suite.anteHandler(suite.ctx, tx, tc.simulate) + + if tc.expPass { + suite.Require().NoError(txErr) + suite.Require().NoError(anteErr) + suite.Require().NotNil(newCtx) + + suite.ctx = newCtx + } else { + switch { + case txErr != nil: + suite.Require().Error(txErr) + suite.Require().True(errors.Is(txErr, tc.expErr)) + + case anteErr != nil: + suite.Require().Error(anteErr) + suite.Require().True(errors.Is(anteErr, tc.expErr)) + + default: + suite.Fail("expected one of txErr,anteErr to be an error") + } + } + }) +} + +func TestAnteTestSuite(t *testing.T) { + suite.Run(t, new(AnteTestSuite)) +} diff --git a/x/auth/atlas/atlas-v0.39.1.md b/x/auth/atlas/atlas-v0.39.1.md new file mode 100644 index 000000000000..486e814d3141 --- /dev/null +++ b/x/auth/atlas/atlas-v0.39.1.md @@ -0,0 +1,194 @@ +# x/auth + +The `x/auth` module is responsible for specifying the base transaction and +account types for an application, as well as AnteHandler and authentication logic. + +## Usage + +1. Import the module. + + ```go + import ( + "github.com/cosmos/cosmos-sdk/x/auth" + ) + ``` + +2. Add `AppModuleBasic` to your `ModuleBasics`. + + ```go + var ( + ModuleBasics = module.NewBasicManager( + // ... + auth.AppModuleBasic{}, + } + ) + ``` + +3. Create the module's parameter subspace in your application constructor. + + ```go + func NewApp(...) *App { + // ... + app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace) + } + ``` + +4. Create the keeper. + + ```go + func NewApp(...) *App { + // ... + app.AccountKeeper = auth.NewAccountKeeper( + app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], auth.ProtoBaseAccount, + ) + } + ``` + +5. Add the `x/auth` module to the app's `ModuleManager`. + + ```go + func NewApp(...) *App { + // ... + app.mm = module.NewManager( + // ... + auth.NewAppModule(app.AccountKeeper), + // ... + ) + } + ``` + +6. Set the `x/auth` module genesis order. + + ```go + func NewApp(...) *App { + // ... + app.mm.SetOrderInitGenesis(..., auth.ModuleName, ...) + } + ``` + +7. Add the `x/auth` module to the simulation manager (if you have one set). + + ```go + func NewApp(...) *App { + // ... + app.sm = module.NewSimulationManager( + // ... + auth.NewAppModule(app.AccountKeeper), + // ... + ) + } + +8. Set the `AnteHandler` if you're using the default provided by `x/auth`. Note, +the default `AnteHandler` provided by the `x/auth` module depends on the `x/supply` +module. + + ```go + func NewApp(...) *App { + app.SetAnteHandler(ante.NewAnteHandler( + app.AccountKeeper, + app.SupplyKeeper, + auth.DefaultSigVerificationGasConsumer, + )) + } + ``` + +### Vesting Accounts + +The `x/auth` modules also defines a few standard vesting account types under the +`vesting` sub-package. In order to get your application to automatically support +these in terms of encoding and decoding, you must register the types with your +application Amino codec. + +Where ever you define the application `Codec`, be sure to register types via: + +```go +import ( + "github.com/cosmos/cosmos-sdk/x/auth/vesting" +) + +func MakeCodec() *codec.Codec { + var cdc = codec.New() + + // ... + vesting.RegisterCodec(cdc) + // ... + + return cdc +} +``` + +## Genesis + +The `x/auth` module defines its genesis state as follows: + +```go +type GenesisState struct { + Params Params `json:"params" yaml:"params"` + Accounts exported.GenesisAccounts `json:"accounts" yaml:"accounts"` +} +``` + +Which relies on the following types: + +```go +type Account interface { + GetAddress() sdk.AccAddress + SetAddress(sdk.AccAddress) error + GetPubKey() crypto.PubKey + SetPubKey(crypto.PubKey) error + GetAccountNumber() uint64 + SetAccountNumber(uint64) error + GetSequence() uint64 + SetSequence(uint64) error + GetCoins() sdk.Coins + SetCoins(sdk.Coins) error + SpendableCoins(blockTime time.Time) sdk.Coins + String() string +} + +type Params struct { + MaxMemoCharacters uint64 `json:"max_memo_characters" yaml:"max_memo_characters"` + TxSigLimit uint64 `json:"tx_sig_limit" yaml:"tx_sig_limit"` + TxSizeCostPerByte uint64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"` + SigVerifyCostED25519 uint64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"` + SigVerifyCostSecp256k1 uint64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"` +} +``` + +## Client + +### CLI + +The `x/auth` module provides various auxiliary CLI commands and a few that are +part of the module itself via the `ModuleManager`. The commands that are part of +the module itself are defined below: + +1. Query an account. + + ```shell + $ app q auth account [address] [...flags] + ``` + +2. Sign an unsigned transaction using a single signature. + + ```shell + $ app tx auth sign [file] + ``` + +3. Sign an unsigned transaction using a multisig. + + ```shell + $ app tx auth multisign [file] [name] [[signature]...] + ``` + +### REST + +The `x/auth` module provides various auxiliary REST handlers and a few that are +part of the module itself via the `ModuleManager`. The endpoints that are part of +the module itself are defined below: + +1. Query an account. + + | Method | Path | + | :----- | :----------------------- | + | `GET` | `/auth/accounts/{address}` | diff --git a/x/auth/atlas/atlas.toml b/x/auth/atlas/atlas.toml new file mode 100644 index 000000000000..2476ba3e02b9 --- /dev/null +++ b/x/auth/atlas/atlas.toml @@ -0,0 +1,33 @@ +[module] +description = "The auth module is responsible for specifying the base transaction and account types for an application, as well as AnteHandler and authentication logic." +homepage = "https://github.com/cosmos/cosmos-sdk" +keywords = [ + "authentication", + "signatures", + "ante", + "transactions", + "accounts", +] + +name = "x/auth" + +[bug_tracker] +url = "https://github.com/cosmos/cosmos-sdk/issues" + +[[authors]] +name = "alexanderbez" + +[[authors]] +name = "fedekunze" + +[[authors]] +name = "cwgoes" + +[[authors]] +name = "alessio" + +[version] +documentation = "https://raw.githubusercontent.com/cosmos/cosmos-sdk/master/x/auth/atlas/atlas-v0.39.1.md" +repo = "https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.2" +sdk_compat = "v0.39.x" +version = "v0.39.2" diff --git a/x/auth/client/cli/broadcast.go b/x/auth/client/cli/broadcast.go index 37590787b4dd..ef5eefc410b2 100644 --- a/x/auth/client/cli/broadcast.go +++ b/x/auth/client/cli/broadcast.go @@ -1,18 +1,18 @@ package cli import ( + "errors" "strings" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" ) // GetBroadcastCommand returns the tx broadcast command. -func GetBroadcastCommand(cdc *codec.Codec) *cobra.Command { +func GetBroadcastCommand() *cobra.Command { cmd := &cobra.Command{ Use: "broadcast [file_path]", Short: "Broadcast transactions generated offline", @@ -21,27 +21,39 @@ flag and signed with the sign command. Read a transaction from [file_path] and broadcast it to a node. If you supply a dash (-) argument in place of an input filename, the command reads from standard input. -$ tx broadcast ./mytxn.json +$ tx broadcast ./mytxn.json `), Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - cliCtx := context.NewCLIContext().WithCodec(cdc) - stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0]) + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { - return + return err } - txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx) + if offline, _ := cmd.Flags().GetBool(flags.FlagOffline); offline { + return errors.New("cannot broadcast tx during offline mode") + } + + stdTx, err := authclient.ReadTxFromFile(clientCtx, args[0]) + if err != nil { + return err + } + + txBytes, err := clientCtx.TxConfig.TxEncoder()(stdTx) if err != nil { - return + return err } - res, err := cliCtx.BroadcastTx(txBytes) - cliCtx.PrintOutput(res) + res, err := clientCtx.BroadcastTx(txBytes) + if err != nil { + return err + } - return err + return clientCtx.PrintProto(res) }, } - return flags.PostCommands(cmd)[0] + flags.AddTxFlagsToCmd(cmd) + + return cmd } diff --git a/x/auth/client/cli/cli_test.go b/x/auth/client/cli/cli_test.go new file mode 100644 index 000000000000..2b305e809256 --- /dev/null +++ b/x/auth/client/cli/cli_test.go @@ -0,0 +1,1159 @@ +// +build norace + +package cli_test + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + cfg.NumValidators = 2 + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + kb := s.network.Validators[0].ClientCtx.Keyring + _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) + _, err = kb.SaveMultisig("multi", multi) + 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() +} + +func (s *IntegrationTestSuite) TestCLIValidateSignatures() { + val := s.network.Validators[0] + res := s.createBankMsg(val, val.Address) + + // write unsigned tx to file + unsignedTx := testutil.WriteToNewTempFile(s.T(), res.String()) + res, err := authtest.TxSignExec(val.ClientCtx, val.Address, unsignedTx.Name()) + s.Require().NoError(err) + signedTx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(res.Bytes()) + s.Require().NoError(err) + + signedTxFile := testutil.WriteToNewTempFile(s.T(), res.String()) + txBuilder, err := val.ClientCtx.TxConfig.WrapTxBuilder(signedTx) + res, err = authtest.TxValidateSignaturesExec(val.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + txBuilder.SetMemo("MODIFIED TX") + bz, err := val.ClientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + modifiedTxFile := testutil.WriteToNewTempFile(s.T(), string(bz)) + + res, err = authtest.TxValidateSignaturesExec(val.ClientCtx, modifiedTxFile.Name()) + s.Require().EqualError(err, "signatures validation failed") +} + +func (s *IntegrationTestSuite) TestCLISignBatch() { + val := s.network.Validators[0] + generatedStd := s.createBankMsg(val, val.Address) + outputFile := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) + val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) + + // sign-batch file - offline is set but account-number and sequence are not + res, err := authtest.TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--offline") + s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") + + // sign-batch file + res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + + // sign-batch file + res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only") + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + + // Sign batch malformed tx file. + malformedFile := testutil.WriteToNewTempFile(s.T(), fmt.Sprintf("%smalformed", generatedStd)) + res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) + s.Require().Error(err) + + // Sign batch malformed tx file signature only. + res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only") + s.Require().Error(err) +} + +func (s *IntegrationTestSuite) TestCLISign_AminoJSON() { + require := s.Require() + val1 := s.network.Validators[0] + txCfg := val1.ClientCtx.TxConfig + txBz := s.createBankMsg(val1, val1.Address) + fileUnsigned := testutil.WriteToNewTempFile(s.T(), txBz.String()) + chainFlag := fmt.Sprintf("--%s=%s", flags.FlagChainID, val1.ClientCtx.ChainID) + sigOnlyFlag := "--signature-only" + signModeAminoFlag := "--sign-mode=amino-json" + + // SIC! validators have same key names and same adddresses as those registered in the keyring, + // BUT the keys are different! + 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, signModeAminoFlag) + 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, signModeAminoFlag) + require.NoError(err) + + // txCfg.UnmarshalSignatureJSON can't unmarshal a fragment of the signature, so we create this structure. + type txFragment struct { + Signatures []json.RawMessage + } + var txOut txFragment + err = json.Unmarshal(res.Bytes(), &txOut) + require.NoError(err) + require.Len(txOut.Signatures, 1) + + /**** test file output ****/ + filenameSigned := filepath.Join(s.T().TempDir(), "test_sign_out.json") + fileFlag := fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, filenameSigned) + _, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, fileFlag, signModeAminoFlag) + require.NoError(err) + fContent, err := ioutil.ReadFile(filenameSigned) + require.NoError(err) + require.Equal(res.String(), string(fContent)) + + /**** try to append to the previously signed transaction ****/ + res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, + sigOnlyFlag, signModeAminoFlag) + require.NoError(err) + checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey(), valInfo.GetPubKey()) + + /**** try to overwrite the previously signed transaction ****/ + + // We can't sign with other address, because the bank send message supports only one signer for a simple + // account. Changing the file is too much hacking, because TxDecoder returns sdk.Tx, which doesn't + // provide functionality to check / manage `auth_info`. + // Cases with different keys are are covered in unit tests of `tx.Sign`. + res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, + sigOnlyFlag, "--overwrite", signModeAminoFlag) + checkSignatures(require, txCfg, res.Bytes(), valInfo.GetPubKey()) + + /**** test flagAmino ****/ + res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, filenameSigned, chainFlag, + "--amino=true", signModeAminoFlag) + require.NoError(err) + + var txAmino authrest.BroadcastReq + err = val1.ClientCtx.LegacyAmino.UnmarshalJSON(res.Bytes(), &txAmino) + require.NoError(err) + require.Len(txAmino.Tx.Signatures, 2) + require.Equal(txAmino.Tx.Signatures[0].PubKey, valInfo.GetPubKey()) + require.Equal(txAmino.Tx.Signatures[1].PubKey, valInfo.GetPubKey()) +} + +func checkSignatures(require *require.Assertions, txCfg client.TxConfig, output []byte, pks ...cryptotypes.PubKey) { + sigs, err := txCfg.UnmarshalSignatureJSON(output) + require.NoError(err, string(output)) + require.Len(sigs, len(pks)) + for i := range pks { + require.True(sigs[i].PubKey.Equals(pks[i]), "Pub key doesn't match. Got: %s, expected: %s, idx: %d", sigs[i].PubKey, pks[i], i) + require.NotEmpty(sigs[i].Data) + } +} + +func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { + val := s.network.Validators[0] + + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + 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, + val.Address, + account2.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + var legacyMsgTxRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(legacyMsgOut.Bytes(), &legacyMsgTxRes)) + + // Service Msg. + out, err := bankcli.ServiceMsgSendExec( + val.ClientCtx, + val.Address, + account2.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + var txRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) + + s.Require().NoError(s.network.WaitForNextBlock()) + + testCases := []struct { + name string + args []string + expectErr bool + rawLogContains string + }{ + { + "with invalid hash", + []string{"somethinginvalid", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + true, + "", + }, + { + "with valid and not existing hash", + []string{"C7E7D3A86A17AB3A321172239F3B61357937AF0F25D9FA4D2F4DCCAD9B0D7747", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + true, + "", + }, + { + "happy case (legacy Msg)", + []string{legacyMsgTxRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + "", + }, + { + "happy case (service Msg)", + []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + "/cosmos.bank.v1beta1.Msg/Send", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := authcli.QueryTxCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var result sdk.TxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &result)) + s.Require().NotNil(result.Height) + s.Require().Contains(result.RawLog, tc.rawLogContains) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { + val1 := s.network.Validators[0] + + account, err := val1.ClientCtx.Keyring.Key("newAccount") + s.Require().NoError(err) + + sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10)) + + normalGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + account.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + txCfg := val1.ClientCtx.TxConfig + + normalGeneratedStdTx, err := txCfg.TxJSONDecoder()(normalGeneratedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err := txCfg.WrapTxBuilder(normalGeneratedStdTx) + s.Require().NoError(err) + s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(flags.DefaultGasLimit)) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + sigs, err := txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(0, len(sigs)) + + // Test generate sendTx with --gas=$amount + limitedGasGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + account.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", 100), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + limitedGasStdTx, err := txCfg.TxJSONDecoder()(limitedGasGeneratedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err = txCfg.WrapTxBuilder(limitedGasStdTx) + s.Require().NoError(err) + s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(100)) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + sigs, err = txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(0, len(sigs)) + + resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) + s.Require().NoError(err) + + var balRes banktypes.QueryAllBalancesResponse + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + startTokens := balRes.Balances.AmountOf(s.cfg.BondDenom) + + // Test generate sendTx, estimate gas + finalGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + account.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err = txCfg.WrapTxBuilder(finalStdTx) + s.Require().NoError(err) + s.Require().Equal(uint64(flags.DefaultGasLimit), txBuilder.GetTx().GetGas()) + s.Require().Equal(len(finalStdTx.GetMsgs()), 1) + + // Write the output to disk + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), finalGeneratedTx.String()) + + // Test validate-signatures + res, err := authtest.TxValidateSignaturesExec(val1.ClientCtx, unsignedTxFile.Name()) + s.Require().EqualError(err, "signatures validation failed") + s.Require().True(strings.Contains(res.String(), fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", val1.Address.String()))) + + // Test sign + + // Does not work in offline mode + res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name(), "--offline") + s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") + + // But works offline if we set account number and sequence + val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) + res, err = authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name(), "--offline", "--account-number", "1", "--sequence", "1") + s.Require().NoError(err) + + // Sign transaction + signedTx, err := authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name()) + s.Require().NoError(err) + signedFinalTx, err := txCfg.TxJSONDecoder()(signedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err = val1.ClientCtx.TxConfig.WrapTxBuilder(signedFinalTx) + s.Require().NoError(err) + s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1) + sigs, err = txBuilder.GetTx().GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal(1, len(sigs)) + s.Require().Equal(val1.Address.String(), txBuilder.GetTx().GetSigners()[0].String()) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) + + // Validate Signature + res, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + s.Require().True(strings.Contains(res.String(), "[OK]")) + + // Ensure foo has right amount of funds + resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) + s.Require().NoError(err) + + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + s.Require().Equal(startTokens, balRes.Balances.AmountOf(s.cfg.BondDenom)) + + // Test broadcast + + // Does not work in offline mode + res, err = authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name(), "--offline") + s.Require().EqualError(err, "cannot broadcast tx during offline mode") + + s.Require().NoError(s.network.WaitForNextBlock()) + + // Broadcast correct transaction. + val1.ClientCtx.BroadcastMode = flags.BroadcastBlock + res, err = authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) + + // Ensure destiny account state + resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, account.GetAddress()) + s.Require().NoError(err) + + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + s.Require().Equal(sendTokens.Amount, balRes.Balances.AmountOf(s.cfg.BondDenom)) + + // Ensure origin account state + resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address) + s.Require().NoError(err) + + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() { + val1 := *s.network.Validators[0] + + // Generate 2 accounts and a multisig. + account1, err := val1.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") + s.Require().NoError(err) + + // Send coins from validator to multisig. + _, err = bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + multisigInfo.GetAddress(), + sdk.NewCoins( + sdk.NewInt64Coin(s.cfg.BondDenom, 10), + ), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) + + // Generate multisig transaction. + multiGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + multisigInfo.GetAddress(), + val1.Address, + sdk.NewCoins( + sdk.NewInt64Coin(s.cfg.BondDenom, 5), + ), + 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) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + + // Multisign, sign with one signature + val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) + account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + + multiSigWith1Signature, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name()) + s.Require().NoError(err) + + // Save tx to file + multiSigWith1SignatureFile := testutil.WriteToNewTempFile(s.T(), multiSigWith1Signature.String()) + + _, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, multiSigWith1SignatureFile.Name()) + s.Require().Error(err) +} + +func (s *IntegrationTestSuite) TestCLIEncode() { + val1 := s.network.Validators[0] + + sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10)) + + normalGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + val1.Address, + 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("--%s=true", flags.FlagGenerateOnly), "--memo", "deadbeef", + ) + s.Require().NoError(err) + savedTxFile := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) + + // Encode + encodeExec, err := authtest.TxEncodeExec(val1.ClientCtx, savedTxFile.Name()) + s.Require().NoError(err) + trimmedBase64 := strings.Trim(encodeExec.String(), "\"\n") + + // Check that the transaction decodes as expected + decodedTx, err := authtest.TxDecodeExec(val1.ClientCtx, trimmedBase64) + s.Require().NoError(err) + + txCfg := val1.ClientCtx.TxConfig + theTx, err := txCfg.TxJSONDecoder()(decodedTx.Bytes()) + s.Require().NoError(err) + txBuilder, err := val1.ClientCtx.TxConfig.WrapTxBuilder(theTx) + s.Require().NoError(err) + s.Require().Equal("deadbeef", txBuilder.GetTx().GetMemo()) +} + +func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() { + val1 := *s.network.Validators[0] + + // Generate 2 accounts and a multisig. + account1, err := val1.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + account2, err := val1.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") + s.Require().NoError(err) + + resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) + s.Require().NoError(err) + + var balRes banktypes.QueryAllBalancesResponse + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + intialCoins := balRes.Balances + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + _, err = bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) + + resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) + s.Require().NoError(err) + + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + diff, _ := balRes.Balances.SafeSub(intialCoins) + s.Require().Equal(sendTokens.Amount, diff.AmountOf(s.cfg.BondDenom)) + + // Generate multisig transaction. + multiGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + multisigInfo.GetAddress(), + val1.Address, + sdk.NewCoins( + sdk.NewInt64Coin(s.cfg.BondDenom, 5), + ), + 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) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + + // Sign with account1 + val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) + account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + + // Sign with account1 + account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) + + multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) + s.Require().NoError(err) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) + + _, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + val1.ClientCtx.BroadcastMode = flags.BroadcastBlock + _, err = authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *IntegrationTestSuite) TestCLIMultisign() { + val1 := *s.network.Validators[0] + + // Generate 2 accounts and a multisig. + account1, err := val1.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + account2, err := val1.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") + s.Require().NoError(err) + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + _, err = bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + + s.Require().NoError(s.network.WaitForNextBlock()) + + resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress()) + s.Require().NoError(err) + + var balRes banktypes.QueryAllBalancesResponse + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &balRes) + s.Require().NoError(err) + s.Require().Equal(sendTokens.Amount, balRes.Balances.AmountOf(s.cfg.BondDenom)) + + // Generate multisig transaction. + multiGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + multisigInfo.GetAddress(), + val1.Address, + sdk.NewCoins( + sdk.NewInt64Coin(s.cfg.BondDenom, 5), + ), + 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) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + + // Sign with account1 + val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) + account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + + // Sign with account1 + account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) + + // Does not work in offline mode. + _, err = authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), "--offline", sign1File.Name(), sign2File.Name()) + s.Require().EqualError(err, "couldn't verify signature: unable to verify single signer signature") + + val1.ClientCtx.Offline = false + multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) + s.Require().NoError(err) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) + + _, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + val1.ClientCtx.BroadcastMode = flags.BroadcastBlock + _, err = authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *IntegrationTestSuite) TestSignBatchMultisig() { + val := s.network.Validators[0] + + // Fetch 2 accounts and a multisig. + account1, err := val.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + multisigInfo, err := val.ClientCtx.Keyring.Key("multi") + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + _, err = bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + s.Require().NoError(s.network.WaitForNextBlock()) + + generatedStd, err := bankcli.MsgSendExec( + val.ClientCtx, + multisigInfo.GetAddress(), + val.Address, + sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), + ), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + // Write the output to disk + filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 1)) + val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) + + // sign-batch file + res, err := authtest.TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // write sigs to file + file1 := testutil.WriteToNewTempFile(s.T(), res.String()) + + // sign-batch file with account2 + res, err = authtest.TxSignBatchExec(val.ClientCtx, account2.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + + // write sigs to file2 + file2 := testutil.WriteToNewTempFile(s.T(), res.String()) + res, err = authtest.TxMultiSignExec(val.ClientCtx, multisigInfo.GetName(), filename.Name(), file1.Name(), file2.Name()) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TestMultisignBatch() { + val := s.network.Validators[0] + + // Fetch 2 accounts and a multisig. + account1, err := val.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + multisigInfo, err := val.ClientCtx.Keyring.Key("multi") + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) + _, err = bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + s.Require().NoError(err) + s.Require().NoError(s.network.WaitForNextBlock()) + + generatedStd, err := bankcli.MsgSendExec( + val.ClientCtx, + multisigInfo.GetAddress(), + val.Address, + sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)), + ), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + ) + s.Require().NoError(err) + + // Write the output to disk + filename := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) + val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) + + queryResJSON, err := authtest.QueryAccountExec(val.ClientCtx, multisigInfo.GetAddress()) + s.Require().NoError(err) + var account authtypes.AccountI + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account)) + + // sign-batch file + res, err := authtest.TxSignBatchExec(val.ClientCtx, account1.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // write sigs to file + file1 := testutil.WriteToNewTempFile(s.T(), res.String()) + + // sign-batch file with account2 + res, err = authtest.TxSignBatchExec(val.ClientCtx, account2.GetAddress(), filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", multisigInfo.GetAddress().String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + + // multisign the file + file2 := testutil.WriteToNewTempFile(s.T(), res.String()) + res, err = authtest.TxMultiSignBatchExec(val.ClientCtx, filename.Name(), multisigInfo.GetName(), file1.Name(), file2.Name()) + s.Require().NoError(err) + signedTxs := strings.Split(strings.Trim(res.String(), "\n"), "\n") + + // Broadcast transactions. + for _, signedTx := range signedTxs { + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx) + val.ClientCtx.BroadcastMode = flags.BroadcastBlock + res, err = authtest.TxBroadcastExec(val.ClientCtx, signedTxFile.Name()) + s.T().Log(res) + s.Require().NoError(err) + s.Require().NoError(s.network.WaitForNextBlock()) + } +} + +func (s *IntegrationTestSuite) TestGetAccountCmd() { + val := s.network.Validators[0] + _, _, addr1 := testdata.KeyTestPubAddr() + + testCases := []struct { + name string + address sdk.AccAddress + expectErr bool + }{ + { + "invalid address", + addr1, + true, + }, + { + "valid address", + val.Address, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + clientCtx := val.ClientCtx + + out, err := authtest.QueryAccountExec(clientCtx, tc.address) + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var acc authtypes.AccountI + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalInterfaceJSON(out.Bytes(), &acc)) + s.Require().Equal(val.Address, acc.GetAddress()) + } + }) + } +} + +func TestGetBroadcastCommand_OfflineFlag(t *testing.T) { + clientCtx := client.Context{}.WithOffline(true) + clientCtx = clientCtx.WithTxConfig(simapp.MakeTestEncodingConfig().TxConfig) + + cmd := authcli.GetBroadcastCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + cmd.SetArgs([]string{fmt.Sprintf("--%s=true", flags.FlagOffline), ""}) + + require.EqualError(t, cmd.Execute(), "cannot broadcast tx during offline mode") +} + +func TestGetBroadcastCommand_WithoutOfflineFlag(t *testing.T) { + clientCtx := client.Context{} + txCfg := simapp.MakeTestEncodingConfig().TxConfig + clientCtx = clientCtx.WithTxConfig(txCfg) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + cmd := authcli.GetBroadcastCommand() + _, out := testutil.ApplyMockIO(cmd) + + // Create new file with tx + builder := txCfg.NewTxBuilder() + builder.SetGasLimit(200000) + from, err := sdk.AccAddressFromBech32("cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw") + require.NoError(t, err) + to, err := sdk.AccAddressFromBech32("cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw") + require.NoError(t, err) + err = builder.SetMsgs(banktypes.NewMsgSend(from, to, sdk.Coins{sdk.NewInt64Coin("stake", 10000)})) + require.NoError(t, err) + txContents, err := txCfg.TxJSONEncoder()(builder.GetTx()) + require.NoError(t, err) + txFile := testutil.WriteToNewTempFile(t, string(txContents)) + + cmd.SetArgs([]string{txFile.Name()}) + err = cmd.ExecuteContext(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "connect: connection refused") + require.Contains(t, out.String(), "connect: connection refused") +} + +func (s *IntegrationTestSuite) TestQueryParamsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "happy case", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + }, + { + "with specific height", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := authcli.QueryParamsCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var authParams authtypes.Params + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &authParams)) + s.Require().NotNil(authParams.MaxMemoCharacters) + } + }) + } +} + +// TestTxWithoutPublicKey makes sure sending a proto tx message without the +// public key doesn't cause any error in the RPC layer (broadcast). +// See https://github.com/cosmos/cosmos-sdk/issues/7585 for more details. +func (s *IntegrationTestSuite) TestTxWithoutPublicKey() { + val1 := s.network.Validators[0] + txCfg := val1.ClientCtx.TxConfig + + // Create a txBuilder with an unsigned tx. + txBuilder := txCfg.NewTxBuilder() + msg := banktypes.NewMsgSend(val1.Address, val1.Address, sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + )) + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)))) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + // Set empty signature to set signer infos. + sigV2 := signing.SignatureV2{ + PubKey: val1.PubKey, + Data: &signing.SingleSignatureData{ + SignMode: txCfg.SignModeHandler().DefaultMode(), + Signature: nil, + }, + } + err = txBuilder.SetSignatures(sigV2) + s.Require().NoError(err) + + // Create a file with the unsigned tx. + txJSON, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + + // Sign the file with the unsignedTx. + signedTx, err := authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name()) + s.Require().NoError(err) + + // Remove the signerInfo's `public_key` field manually from the signedTx. + // Note: this method is only used for test purposes! In general, one should + // use txBuilder and TxEncoder/TxDecoder to manipulate txs. + var tx tx.Tx + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(signedTx.Bytes(), &tx) + s.Require().NoError(err) + tx.AuthInfo.SignerInfos[0].PublicKey = nil + // Re-encode the tx again, to another file. + txJSON, err = val1.ClientCtx.JSONMarshaler.MarshalJSON(&tx) + s.Require().NoError(err) + signedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + s.Require().True(strings.Contains(string(txJSON), "\"public_key\":null")) + + // Broadcast tx, test that it shouldn't panic. + val1.ClientCtx.BroadcastMode = flags.BroadcastSync + out, err := authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + var res sdk.TxResponse + s.Require().NoError(val1.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &res)) + s.Require().NotEqual(0, res.Code) +} + +func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() { + // test case: + // Create a transaction with 2 messages which has to be signed with 2 different keys + // Sign and append the signatures using the CLI with Amino signing mode. + // Finally send the transaction to the blockchain. It should work. + + require := s.Require() + val0, val1 := s.network.Validators[0], s.network.Validators[1] + val0Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val0.Moniker), sdk.NewInt(10)) + val1Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val1.Moniker), sdk.NewInt(10)) + _, _, addr1 := testdata.KeyTestPubAddr() + + // Creating a tx with 2 msgs from 2 signers: val0 and val1. + // The validators need to sign with SIGN_MODE_LEGACY_AMINO_JSON, + // because DIRECT doesn't support multi signers via the CLI. + // Since we we amino, we don't need to pre-populate signer_infos. + txBuilder := val0.ClientCtx.TxConfig.NewTxBuilder() + txBuilder.SetMsgs( + banktypes.NewMsgSend(val0.Address, addr1, sdk.NewCoins(val0Coin)), + banktypes.NewMsgSend(val1.Address, addr1, sdk.NewCoins(val1Coin)), + ) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)))) + txBuilder.SetGasLimit(testdata.NewTestGasLimit()) + require.Equal([]sdk.AccAddress{val0.Address, val1.Address}, txBuilder.GetTx().GetSigners()) + + // Write the unsigned tx into a file. + txJSON, err := val0.ClientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + require.NoError(err) + unsignedTxFile := testutil.WriteToNewTempFile(s.T(), string(txJSON)) + + // Let val0 sign first the file with the unsignedTx. + signedByVal0, err := authtest.TxSignExec(val0.ClientCtx, val0.Address, unsignedTxFile.Name(), "--overwrite", "--sign-mode=amino-json") + require.NoError(err) + signedByVal0File := testutil.WriteToNewTempFile(s.T(), signedByVal0.String()) + + // Then let val1 sign the file with signedByVal0. + val1AccNum, val1Seq, err := val0.ClientCtx.AccountRetriever.GetAccountNumberSequence(val0.ClientCtx, val1.Address) + require.NoError(err) + signedTx, err := authtest.TxSignExec( + val1.ClientCtx, val1.Address, signedByVal0File.Name(), + "--offline", fmt.Sprintf("--account-number=%d", val1AccNum), fmt.Sprintf("--sequence=%d", val1Seq), "--sign-mode=amino-json", + ) + require.NoError(err) + signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String()) + + // Now let's try to send this tx. + res, err := authtest.TxBroadcastExec(val0.ClientCtx, signedTxFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock)) + require.NoError(err) + var txRes sdk.TxResponse + require.NoError(val0.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &txRes)) + require.Equal(uint32(0), txRes.Code) + + // Make sure the addr1's balance got funded. + queryResJSON, err := bankcli.QueryBalancesExec(val0.ClientCtx, addr1) + require.NoError(err) + var queryRes banktypes.QueryAllBalancesResponse + err = val0.ClientCtx.JSONMarshaler.UnmarshalJSON(queryResJSON.Bytes(), &queryRes) + require.NoError(err) + require.Equal(sdk.NewCoins(val0Coin, val1Coin), queryRes.Balances) +} + +func (s *IntegrationTestSuite) createBankMsg(val *network.Validator, toAddr sdk.AccAddress) testutil.BufferWriter { + res, err := bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + toAddr, + sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + ), + 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) + return res +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/auth/client/cli/decode.go b/x/auth/client/cli/decode.go index c9253d8fb4a3..e1807efb099e 100644 --- a/x/auth/client/cli/decode.go +++ b/x/auth/client/cli/decode.go @@ -5,50 +5,49 @@ import ( "encoding/hex" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/go-amino" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) const flagHex = "hex" -// GetDecodeCommand returns the decode command to take Amino-serialized bytes -// and turn it into a JSONified transaction. -func GetDecodeCommand(codec *amino.Codec) *cobra.Command { +// GetDecodeCommand returns the decode command to take serialized bytes and turn +// it into a JSON-encoded transaction. +func GetDecodeCommand() *cobra.Command { cmd := &cobra.Command{ Use: "decode [amino-byte-string]", - Short: "Decode an amino-encoded transaction string.", + Short: "Decode an binary encoded transaction string.", Args: cobra.ExactArgs(1), - RunE: runDecodeTxString(codec), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx := client.GetClientContextFromCmd(cmd) + var txBytes []byte + + if useHex, _ := cmd.Flags().GetBool(flagHex); useHex { + txBytes, err = hex.DecodeString(args[0]) + } else { + txBytes, err = base64.StdEncoding.DecodeString(args[0]) + } + if err != nil { + return err + } + + tx, err := clientCtx.TxConfig.TxDecoder()(txBytes) + if err != nil { + return err + } + + json, err := clientCtx.TxConfig.TxJSONEncoder()(tx) + if err != nil { + return err + } + + return clientCtx.PrintBytes(json) + }, } cmd.Flags().BoolP(flagHex, "x", false, "Treat input as hexadecimal instead of base64") - return flags.PostCommands(cmd)[0] -} + flags.AddTxFlagsToCmd(cmd) -func runDecodeTxString(codec *amino.Codec) func(cmd *cobra.Command, args []string) (err error) { - return func(cmd *cobra.Command, args []string) (err error) { - cliCtx := context.NewCLIContext().WithCodec(codec).WithOutput(cmd.OutOrStdout()) - var txBytes []byte - - if viper.GetBool(flagHex) { - txBytes, err = hex.DecodeString(args[0]) - } else { - txBytes, err = base64.StdEncoding.DecodeString(args[0]) - } - if err != nil { - return err - } - - var stdTx authtypes.StdTx - err = cliCtx.Codec.UnmarshalBinaryLengthPrefixed(txBytes, &stdTx) - if err != nil { - return err - } - - return cliCtx.PrintOutput(stdTx) - } + return cmd } diff --git a/x/auth/client/cli/encode.go b/x/auth/client/cli/encode.go index 304411dba8a1..c883980de956 100644 --- a/x/auth/client/cli/encode.go +++ b/x/auth/client/cli/encode.go @@ -5,22 +5,14 @@ import ( "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" ) -// txEncodeRespStr implements a simple Stringer wrapper for a encoded tx. -type txEncodeRespStr string - -func (txr txEncodeRespStr) String() string { - return string(txr) -} - // GetEncodeCommand returns the encode command to take a JSONified transaction and turn it into // Amino-serialized bytes -func GetEncodeCommand(cdc *codec.Codec) *cobra.Command { +func GetEncodeCommand() *cobra.Command { cmd := &cobra.Command{ Use: "encode [file]", Short: "Encode transactions generated offline", @@ -28,16 +20,16 @@ func GetEncodeCommand(cdc *codec.Codec) *cobra.Command { Read a transaction from , serialize it to the Amino wire protocol, and output it as base64. If you supply a dash (-) argument in place of an input filename, the command reads from standard input.`, Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - cliCtx := context.NewCLIContext().WithCodec(cdc) + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) - stdTx, err := utils.ReadStdTxFromFile(cliCtx.Codec, args[0]) + tx, err := authclient.ReadTxFromFile(clientCtx, args[0]) if err != nil { - return + return err } - // re-encode it via the Amino wire protocol - txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(stdTx) + // re-encode it + txBytes, err := clientCtx.TxConfig.TxEncoder()(tx) if err != nil { return err } @@ -45,10 +37,11 @@ If you supply a dash (-) argument in place of an input filename, the command rea // base64 encode the encoded tx bytes txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes) - response := txEncodeRespStr(txBytesBase64) - return cliCtx.PrintOutput(response) + return clientCtx.PrintString(txBytesBase64 + "\n") }, } - return flags.PostCommands(cmd)[0] + flags.AddTxFlagsToCmd(cmd) + + return cmd } diff --git a/x/auth/client/cli/encode_test.go b/x/auth/client/cli/encode_test.go new file mode 100644 index 000000000000..44648758abba --- /dev/null +++ b/x/auth/client/cli/encode_test.go @@ -0,0 +1,84 @@ +package cli + +import ( + "context" + "encoding/base64" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func TestGetCommandEncode(t *testing.T) { + encodingConfig := simappparams.MakeTestEncodingConfig() + + cmd := GetEncodeCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + + authtypes.RegisterLegacyAminoCodec(encodingConfig.Amino) + sdk.RegisterLegacyAminoCodec(encodingConfig.Amino) + + txCfg := encodingConfig.TxConfig + + // Build a test transaction + builder := txCfg.NewTxBuilder() + builder.SetGasLimit(50000) + builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)}) + builder.SetMemo("foomemo") + jsonEncoded, err := txCfg.TxJSONEncoder()(builder.GetTx()) + require.NoError(t, err) + + txFile := testutil.WriteToNewTempFile(t, string(jsonEncoded)) + txFileName := txFile.Name() + + ctx := context.Background() + clientCtx := client.Context{}. + WithTxConfig(encodingConfig.TxConfig). + WithJSONMarshaler(encodingConfig.Marshaler) + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + cmd.SetArgs([]string{txFileName}) + err = cmd.ExecuteContext(ctx) + require.NoError(t, err) +} + +func TestGetCommandDecode(t *testing.T) { + encodingConfig := simappparams.MakeTestEncodingConfig() + + clientCtx := client.Context{}. + WithTxConfig(encodingConfig.TxConfig). + WithJSONMarshaler(encodingConfig.Marshaler) + + cmd := GetDecodeCommand() + _ = testutil.ApplyMockIODiscardOutErr(cmd) + + sdk.RegisterLegacyAminoCodec(encodingConfig.Amino) + + txCfg := encodingConfig.TxConfig + clientCtx = clientCtx.WithTxConfig(txCfg) + + // Build a test transaction + builder := txCfg.NewTxBuilder() + builder.SetGasLimit(50000) + builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)}) + builder.SetMemo("foomemo") + + // Encode transaction + txBytes, err := clientCtx.TxConfig.TxEncoder()(builder.GetTx()) + require.NoError(t, err) + + // Convert the transaction into base64 encoded string + base64Encoded := base64.StdEncoding.EncodeToString(txBytes) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + // Execute the command + cmd.SetArgs([]string{base64Encoded}) + require.NoError(t, cmd.ExecuteContext(ctx)) +} diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index 08e796218bb0..2590858b4c20 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -1,23 +1,20 @@ package cli import ( + "context" "fmt" "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/auth/types" - - tmtypes "github.com/tendermint/tendermint/types" ) const ( @@ -27,7 +24,7 @@ const ( ) // GetQueryCmd returns the transaction commands for this module -func GetQueryCmd(cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the auth module", @@ -36,41 +33,79 @@ func GetQueryCmd(cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - cmd.AddCommand(GetAccountCmd(cdc)) + cmd.AddCommand( + GetAccountCmd(), + QueryParamsCmd(), + ) + + return cmd +} + +// QueryParamsCmd returns the command handler for evidence parameter querying. +func QueryParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current auth parameters", + Args: cobra.NoArgs, + Long: strings.TrimSpace(`Query the current auth parameters: + +$ query auth params +`), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) return cmd } // GetAccountCmd returns a query account that will display the state of the // account at a given address. -func GetAccountCmd(cdc *codec.Codec) *cobra.Command { +func GetAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "account [address]", - Short: "Query account balance", + Short: "Query for account by address", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - accGetter := types.NewAccountRetriever(cliCtx) - + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } key, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - acc, err := accGetter.GetAccount(key) + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Account(context.Background(), &types.QueryAccountRequest{Address: key.String()}) if err != nil { return err } - return cliCtx.PrintOutput(acc) + return clientCtx.PrintProto(res.Account) }, } - return flags.GetCommands(cmd)[0] + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // QueryTxsByEventsCmd returns a command to search through transactions by events. -func QueryTxsByEventsCmd(cdc *codec.Codec) *cobra.Command { +func QueryTxsByEventsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "txs", Short: "Query for paginated transactions that match a set of events", @@ -83,10 +118,15 @@ documents its respective events under 'xx_events.md'. Example: $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator_reward' --page 1 --limit 30 -`, eventFormat, version.ClientName, flagEvents), +`, eventFormat, version.AppName, flagEvents), ), RunE: func(cmd *cobra.Command, args []string) error { - eventsStr := strings.Trim(viper.GetString(flagEvents), "'") + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + eventsRaw, _ := cmd.Flags().GetString(flagEvents) + eventsStr := strings.Trim(eventsRaw, "'") var events []string if strings.Contains(eventsStr, "&") { @@ -114,55 +154,40 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator tmEvents = append(tmEvents, event) } - page := viper.GetInt(flags.FlagPage) - limit := viper.GetInt(flags.FlagLimit) - - cliCtx := context.NewCLIContext().WithCodec(cdc) - txs, err := utils.QueryTxsByEvents(cliCtx, tmEvents, page, limit) - if err != nil { - return err - } - - var output []byte - if cliCtx.Indent { - output, err = cdc.MarshalJSONIndent(txs, "", " ") - } else { - output, err = cdc.MarshalJSON(txs) - } + page, _ := cmd.Flags().GetInt(flags.FlagPage) + limit, _ := cmd.Flags().GetInt(flags.FlagLimit) + txs, err := authclient.QueryTxsByEvents(clientCtx, tmEvents, page, limit, "") if err != nil { return err } - fmt.Println(string(output)) - return nil + return clientCtx.PrintProto(txs) }, } cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) - - cmd.Flags().Bool(flags.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) - + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.Flags().Int(flags.FlagPage, rest.DefaultPage, "Query a specific page of paginated results") + cmd.Flags().Int(flags.FlagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") cmd.Flags().String(flagEvents, "", fmt.Sprintf("list of transaction events in the form of %s", eventFormat)) - cmd.Flags().Uint32(flags.FlagPage, rest.DefaultPage, "Query a specific page of paginated results") - cmd.Flags().Uint32(flags.FlagLimit, rest.DefaultLimit, "Query number of transactions results per page returned") cmd.MarkFlagRequired(flagEvents) return cmd } // QueryTxCmd implements the default command for a tx query. -func QueryTxCmd(cdc *codec.Codec) *cobra.Command { +func QueryTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "tx [hash]", Short: "Query for a transaction by hash in a committed block", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - output, err := utils.QueryTx(cliCtx, args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + output, err := authclient.QueryTx(clientCtx, args[0]) if err != nil { return err } @@ -171,14 +196,11 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { return fmt.Errorf("no transaction found with hash %s", args[0]) } - return cliCtx.PrintOutput(output) + return clientCtx.PrintProto(output) }, } - cmd.Flags().StringP(flags.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - viper.BindPFlag(flags.FlagNode, cmd.Flags().Lookup(flags.FlagNode)) - cmd.Flags().Bool(flags.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) + flags.AddQueryFlagsToCmd(cmd) return cmd } diff --git a/x/auth/client/cli/tx.go b/x/auth/client/cli/tx.go deleted file mode 100644 index 7ee246c38e6f..000000000000 --- a/x/auth/client/cli/tx.go +++ /dev/null @@ -1,25 +0,0 @@ -package cli - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(cdc *codec.Codec) *cobra.Command { - txCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Auth transaction subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - txCmd.AddCommand( - GetMultiSignCommand(cdc), - GetSignCommand(cdc), - ) - return txCmd -} diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index 9d67f26ef9ba..cd5c687eac6b 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -1,7 +1,6 @@ package cli import ( - "bufio" "fmt" "io/ioutil" "os" @@ -10,155 +9,396 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto/multisig" - - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/types/errors" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/auth/types" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/auth/signing" ) // GetSignCommand returns the sign command -func GetMultiSignCommand(cdc *codec.Codec) *cobra.Command { +func GetMultiSignCommand() *cobra.Command { cmd := &cobra.Command{ Use: "multisign [file] [name] [[signature]...]", Short: "Generate multisig signatures for transactions generated offline", Long: strings.TrimSpace( fmt.Sprintf(`Sign transactions created with the --generate-only flag that require multisig signatures. -Read signature(s) from [signature] file(s), generate a multisig signature compliant to the -multisig key [name], and attach it to the transaction read from [file]. +Read one or more signatures from one or more [signature] file, generate a multisig signature compliant to the +multisig key [name], and attach the key name to the transaction read from [file]. Example: -$ %s multisign transaction.json k1k2k3 k1sig.json k2sig.json k3sig.json +$ %s tx multisign transaction.json k1k2k3 k1sig.json k2sig.json k3sig.json + +If --signature-only flag is on, output a JSON representation +of only the generated signature. -If the flag --signature-only flag is on, it outputs a JSON representation -of the generated signature only. +If the --offline flag is on, the client will not reach out to an external node. +Account number or sequence number lookups are not performed so you must +set these parameters manually. -The --offline flag makes sure that the client will not reach out to an external node. -Thus account number or sequence number lookups will not be performed and it is -recommended to set such parameters manually. +The current multisig implementation defaults to amino-json sign mode. +The SIGN_MODE_DIRECT sign mode is not supported.' `, - version.ClientName, + version.AppName, ), ), - RunE: makeMultiSignCmd(cdc), + RunE: makeMultiSignCmd(), Args: cobra.MinimumNArgs(3), } cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") - cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query a full node") - cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().String(flags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") + cmd.Flags().Bool(flagAmino, false, "Generate Amino-encoded JSON suitable for submitting to the txs REST endpoint") + flags.AddTxFlagsToCmd(cmd) + cmd.Flags().String(flags.FlagChainID, "", "network chain ID") - // Add the flags here and return the command - return flags.PostCommands(cmd)[0] + return cmd } -func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error { +func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { return func(cmd *cobra.Command, args []string) (err error) { - stdTx, err := utils.ReadStdTxFromFile(cdc, args[0]) + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { - return + return err } - - inBuf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), inBuf) + parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[0]) if err != nil { return } - multisigInfo, err := kb.Get(args[1]) + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if txFactory.SignMode() == signingtypes.SignMode_SIGN_MODE_UNSPECIFIED { + txFactory = txFactory.WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + txCfg := clientCtx.TxConfig + txBuilder, err := txCfg.WrapTxBuilder(parsedTx) if err != nil { - return + return err } - if multisigInfo.GetType() != keys.TypeMulti { - return fmt.Errorf("%q must be of type %s: %s", args[1], keys.TypeMulti, multisigInfo.GetType()) + + multisigInfo, err := getMultisigInfo(clientCtx, args[1]) + if err != nil { + return err } - multisigPub := multisigInfo.GetPubKey().(multisig.PubKeyMultisigThreshold) + multisigPub := multisigInfo.GetPubKey().(*kmultisig.LegacyAminoPubKey) multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - txBldr := types.NewTxBuilderFromCLI(inBuf) - - if !viper.GetBool(flagOffline) { - accnum, seq, err := types.NewAccountRetriever(cliCtx).GetAccountNumberSequence(multisigInfo.GetAddress()) + if !clientCtx.Offline { + accnum, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, multisigInfo.GetAddress()) if err != nil { return err } - txBldr = txBldr.WithAccountNumber(accnum).WithSequence(seq) + txFactory = txFactory.WithAccountNumber(accnum).WithSequence(seq) } // read each signature and add it to the multisig if valid for i := 2; i < len(args); i++ { - stdSig, err := readAndUnmarshalStdSignature(cdc, args[i]) + sigs, err := unmarshalSignatureJSON(clientCtx, args[i]) if err != nil { return err } - // Validate each signature - sigBytes := types.StdSignBytes( - txBldr.ChainID(), txBldr.AccountNumber(), txBldr.Sequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), - ) - if ok := stdSig.PubKey.VerifyBytes(sigBytes, stdSig.Signature); !ok { - return fmt.Errorf("couldn't verify signature") + signingData := signing.SignerData{ + ChainID: txFactory.ChainID(), + AccountNumber: txFactory.AccountNumber(), + Sequence: txFactory.Sequence(), } - if err := multisigSig.AddSignatureFromPubKey(stdSig.Signature, stdSig.PubKey, multisigPub.PubKeys); err != nil { - return err + + for _, sig := range sigs { + err = signing.VerifySignature(sig.PubKey, signingData, sig.Data, txCfg.SignModeHandler(), txBuilder.GetTx()) + if err != nil { + return fmt.Errorf("couldn't verify signature: %w", err) + } + + if err := multisig.AddSignatureV2(multisigSig, sig, multisigPub.GetPubKeys()); err != nil { + return err + } } } - newStdSig := types.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub} - newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) - - sigOnly := viper.GetBool(flagSigOnly) - var json []byte - switch { - case sigOnly && cliCtx.Indent: - json, err = cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") - case sigOnly && !cliCtx.Indent: - json, err = cdc.MarshalJSON(newTx.Signatures[0]) - case !sigOnly && cliCtx.Indent: - json, err = cdc.MarshalJSONIndent(newTx, "", " ") - default: - json, err = cdc.MarshalJSON(newTx) + sigV2 := signingtypes.SignatureV2{ + PubKey: multisigPub, + Data: multisigSig, + Sequence: txFactory.Sequence(), } + + err = txBuilder.SetSignatures(sigV2) if err != nil { return err } - if viper.GetString(flagOutfile) == "" { - fmt.Printf("%s\n", json) + sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) + + aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + + var json []byte + + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) + if err != nil { + return err + } + + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + + json, _ = clientCtx.LegacyAmino.MarshalJSON(req) + + } else { + json, err = marshalSignatureJSON(txCfg, txBuilder, sigOnly) + if err != nil { + return err + } + } + + outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) + if outputDoc == "" { + cmd.Printf("%s\n", json) return } - fp, err := os.OpenFile( - viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644, - ) + fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return err } - defer fp.Close() - fmt.Fprintf(fp, "%s\n", json) + defer func() { + err2 := fp.Close() + if err == nil { + err = err2 + } + }() + + err = clientCtx.PrintBytes(json) return } } -func readAndUnmarshalStdSignature(cdc *codec.Codec, filename string) (stdSig types.StdSignature, err error) { +func GetMultiSignBatchCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "multisign-batch [file] [name] [[signature-file]...]", + Short: "Assemble multisig transactions in batch from batch signatures", + Long: strings.TrimSpace( + fmt.Sprintf(`Assemble a batch of multisig transactions generated by batch sign command. + +Read one or more signatures from one or more [signature] file, generate a multisig signature compliant to the +multisig key [name], and attach the key name to the transaction read from [file]. + +Example: +$ %s tx multisign-batch transactions.json multisigk1k2k3 k1sigs.json k2sigs.json k3sig.json + +The current multisig implementation defaults to amino-json sign mode. +The SIGN_MODE_DIRECT sign mode is not supported.' +`, version.AppName, + ), + ), + PreRun: preSignCmd, + RunE: makeBatchMultisignCmd(), + Args: cobra.MinimumNArgs(3), + } + + cmd.Flags().Bool(flagNoAutoIncrement, false, "disable sequence auto increment") + cmd.Flags().String( + flagMultisig, "", + "Address of the multisig account that the transaction signs on behalf of", + ) + cmd.Flags().String(flags.FlagOutputDocument, "", "The document is written to the given file instead of STDOUT") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) (err error) { + var clientCtx client.Context + + clientCtx, err = client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txCfg := clientCtx.TxConfig + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if txFactory.SignMode() == signingtypes.SignMode_SIGN_MODE_UNSPECIFIED { + txFactory = txFactory.WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + var infile = os.Stdin + if args[0] != "-" { + infile, err = os.Open(args[0]) + defer func() { + err2 := infile.Close() + if err == nil { + err = err2 + } + }() + + if err != nil { + return fmt.Errorf("couldn't open %s: %w", args[0], err) + } + } + scanner := authclient.NewBatchScanner(txCfg, infile) + + multisigInfo, err := getMultisigInfo(clientCtx, args[1]) + if err != nil { + return err + } + + var signatureBatch [][]signingtypes.SignatureV2 + for i := 2; i < len(args); i++ { + sigs, err := readSignaturesFromFile(clientCtx, args[i]) + if err != nil { + return err + } + + signatureBatch = append(signatureBatch, sigs) + } + + if !clientCtx.Offline { + accnum, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, multisigInfo.GetAddress()) + if err != nil { + return err + } + + txFactory = txFactory.WithAccountNumber(accnum).WithSequence(seq) + } + + // prepare output document + closeFunc, err := setOutputFile(cmd) + if err != nil { + return err + } + + defer closeFunc() + clientCtx.WithOutput(cmd.OutOrStdout()) + + for i := 0; scanner.Scan(); i++ { + txBldr, err := txCfg.WrapTxBuilder(scanner.Tx()) + if err != nil { + return err + } + + multisigPub := multisigInfo.GetPubKey().(*kmultisig.LegacyAminoPubKey) + multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys)) + signingData := signing.SignerData{ + ChainID: txFactory.ChainID(), + AccountNumber: txFactory.AccountNumber(), + Sequence: txFactory.Sequence(), + } + + for _, sig := range signatureBatch { + err = signing.VerifySignature(sig[i].PubKey, signingData, sig[i].Data, txCfg.SignModeHandler(), txBldr.GetTx()) + if err != nil { + return fmt.Errorf("couldn't verify signature: %w %v", err, sig) + } + + if err := multisig.AddSignatureV2(multisigSig, sig[i], multisigPub.GetPubKeys()); err != nil { + return err + } + } + + sigV2 := signingtypes.SignatureV2{ + PubKey: multisigPub, + Data: multisigSig, + Sequence: txFactory.Sequence(), + } + + err = txBldr.SetSignatures(sigV2) + if err != nil { + return err + } + + sigOnly, _ := cmd.Flags().GetBool(flagSigOnly) + aminoJSON, _ := cmd.Flags().GetBool(flagAmino) + + var json []byte + + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBldr.GetTx()) + if err != nil { + return err + } + + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + + json, _ = clientCtx.LegacyAmino.MarshalJSON(req) + + } else { + json, err = marshalSignatureJSON(txCfg, txBldr, sigOnly) + if err != nil { + return err + } + } + + err = clientCtx.PrintString(fmt.Sprintf("%s\n", json)) + if err != nil { + return err + } + + if viper.GetBool(flagNoAutoIncrement) { + continue + } + sequence := txFactory.Sequence() + 1 + txFactory = txFactory.WithSequence(sequence) + } + + return scanner.UnmarshalErr() + } +} + +func unmarshalSignatureJSON(clientCtx client.Context, filename string) (sigs []signingtypes.SignatureV2, err error) { var bytes []byte if bytes, err = ioutil.ReadFile(filename); err != nil { return } - if err = cdc.UnmarshalJSON(bytes, &stdSig); err != nil { - return + return clientCtx.TxConfig.UnmarshalSignatureJSON(bytes) +} + +func readSignaturesFromFile(ctx client.Context, filename string) (sigs []signingtypes.SignatureV2, err error) { + bz, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + newString := strings.TrimSuffix(string(bz), "\n") + lines := strings.Split(newString, "\n") + + for _, bz := range lines { + sig, err := ctx.TxConfig.UnmarshalSignatureJSON([]byte(bz)) + if err != nil { + return nil, err + } + + sigs = append(sigs, sig...) + } + return sigs, nil +} + +func getMultisigInfo(clientCtx client.Context, name string) (keyring.Info, error) { + kb := clientCtx.Keyring + multisigInfo, err := kb.Key(name) + if err != nil { + return nil, errors.Wrap(err, "error getting keybase multisig account") } - return + if multisigInfo.GetType() != keyring.TypeMulti { + return nil, fmt.Errorf("%q must be of type %s: %s", name, keyring.TypeMulti, multisigInfo.GetType()) + } + + return multisigInfo, nil } diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index f2e72cb4e3a9..087018bd6b04 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -1,268 +1,306 @@ package cli import ( - "bufio" "fmt" "os" - "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto/multisig" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/auth/types" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/client/rest" ) const ( - flagMultisig = "multisig" - flagAppend = "append" - flagValidateSigs = "validate-signatures" - flagOffline = "offline" - flagSigOnly = "signature-only" - flagOutfile = "output-document" + flagMultisig = "multisig" + flagOverwrite = "overwrite" + flagSigOnly = "signature-only" + flagAmino = "amino" + flagNoAutoIncrement = "no-auto-increment" ) -// GetSignCommand returns the transaction sign command. -func GetSignCommand(codec *codec.Codec) *cobra.Command { +// GetSignBatchCommand returns the transaction sign-batch command. +func GetSignBatchCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "sign [file]", - Short: "Sign transactions generated offline", - Long: `Sign transactions created with the --generate-only flag. -It will read a transaction from [file], sign it, and print its JSON encoding. - -If the flag --signature-only flag is set, it will output a JSON representation -of the generated signature only. + Use: "sign-batch [file]", + Short: "Sign transaction batch files", + Long: `Sign batch files of transactions generated with --generate-only. +The command processes list of transactions from file (one StdTx each line), generate +signed transactions or signatures and print their JSON encoding, delimited by '\n'. +As the signatures are generated, the command updates the account sequence number accordingly. -If the flag --validate-signatures is set, then the command would check whether all required -signers have signed the transactions, whether the signatures were collected in the right -order, and if the signature is valid over the given transaction. If the --offline -flag is also set, signature validation over the transaction will be not be -performed as that will require RPC communication with a full node. +If the --signature-only flag is set, it will output the signature parts only. The --offline flag makes sure that the client will not reach out to full node. -As a result, the account and sequence number queries will not be performed and +As a result, the account and the sequence number queries will not be performed and it is required to set such parameters manually. Note, invalid values will cause -the transaction to fail. +the transaction to fail. The sequence will be incremented automatically for each +transaction that is signed. -The --multisig= flag generates a signature on behalf of a multisig account -key. It implies --signature-only. Full multisig signed transactions may eventually -be generated via the 'multisign' command. +The --multisig= flag generates a signature on behalf of a multisig +account key. It implies --signature-only. `, PreRun: preSignCmd, - RunE: makeSignCmd(codec), + RunE: makeSignBatchCmd(), Args: cobra.ExactArgs(1), } - cmd.Flags().String( - flagMultisig, "", - "Address of the multisig account on behalf of which the transaction shall be signed", - ) - cmd.Flags().Bool( - flagAppend, true, - "Append the signature to the existing ones. If disabled, old signatures would be overwritten. Ignored if --multisig is on", - ) - cmd.Flags().Bool( - flagValidateSigs, false, - "Print the addresses that must sign the transaction, those who have already signed it, and make sure that signatures are in the correct order", - ) - cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") - cmd.Flags().Bool( - flagOffline, false, - "Offline mode; Do not query a full node. --account and --sequence options would be required if offline is set", - ) - cmd.Flags().String(flagOutfile, "", "The document will be written to the given file instead of STDOUT") - - cmd = flags.PostCommands(cmd)[0] + cmd.Flags().String(flagMultisig, "", "Address of the multisig account on behalf of which the transaction shall be signed") + cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().Bool(flagSigOnly, true, "Print only the generated signature, then exit") + cmd.Flags().String(flags.FlagChainID, "", "network chain ID") cmd.MarkFlagRequired(flags.FlagFrom) + flags.AddTxFlagsToCmd(cmd) return cmd } -func preSignCmd(cmd *cobra.Command, _ []string) { - // Conditionally mark the account and sequence numbers required as no RPC - // query will be done. - if viper.GetBool(flagOffline) { - cmd.MarkFlagRequired(flags.FlagAccountNumber) - cmd.MarkFlagRequired(flags.FlagSequence) - } -} - -func makeSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) error { +func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { - stdTx, err := utils.ReadStdTxFromFile(cdc, args[0]) + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } - - inBuf := bufio.NewReader(cmd.InOrStdin()) - offline := viper.GetBool(flagOffline) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - txBldr := types.NewTxBuilderFromCLI(inBuf) - - if viper.GetBool(flagValidateSigs) { - if !printAndValidateSigs(cliCtx, txBldr.ChainID(), stdTx, offline) { - return fmt.Errorf("signatures validation failed") + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + txCfg := clientCtx.TxConfig + printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) + infile := os.Stdin + var multisigAddr sdk.AccAddress + + // validate multisig address if there's any + if ms, _ := cmd.Flags().GetString(flagMultisig); ms != "" { + multisigAddr, err = sdk.AccAddressFromBech32(ms) + if err != nil { + return err } - - return nil } - // if --signature-only is on, then override --append - var newTx types.StdTx - generateSignatureOnly := viper.GetBool(flagSigOnly) - multisigAddrStr := viper.GetString(flagMultisig) + // prepare output document + closeFunc, err := setOutputFile(cmd) + if err != nil { + return err + } - if multisigAddrStr != "" { - var multisigAddr sdk.AccAddress + defer closeFunc() + clientCtx.WithOutput(cmd.OutOrStdout()) - multisigAddr, err = sdk.AccAddressFromBech32(multisigAddrStr) + if args[0] != "-" { + infile, err = os.Open(args[0]) if err != nil { return err } - - newTx, err = utils.SignStdTxWithSignerAddress( - txBldr, cliCtx, multisigAddr, cliCtx.GetFromName(), stdTx, offline, - ) - generateSignatureOnly = true - } else { - appendSig := viper.GetBool(flagAppend) && !generateSignatureOnly - newTx, err = utils.SignStdTx(txBldr, cliCtx, cliCtx.GetFromName(), stdTx, appendSig, offline) } + scanner := authclient.NewBatchScanner(txCfg, infile) - if err != nil { - return err - } + for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ { + unsignedStdTx := scanner.Tx() + txFactory = txFactory.WithSequence(sequence) + txBuilder, err := txCfg.WrapTxBuilder(unsignedStdTx) + if err != nil { + return err + } + if multisigAddr.Empty() { + from, _ := cmd.Flags().GetString(flags.FlagFrom) + _, fromName, _, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) + } + err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true) + if err != nil { + return err + } + } else { + err = authclient.SignTxWithSignerAddress( + txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, true) + } - json, err := getSignatureJSON(cdc, newTx, cliCtx.Indent, generateSignatureOnly) - if err != nil { - return err - } + if err != nil { + return err + } - if viper.GetString(flagOutfile) == "" { - fmt.Printf("%s\n", json) - return nil + json, err := marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) + if err != nil { + return err + } + + cmd.Printf("%s\n", json) } - fp, err := os.OpenFile( - viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644, - ) - if err != nil { + if err := scanner.UnmarshalErr(); err != nil { return err } - defer fp.Close() - fmt.Fprintf(fp, "%s\n", json) - - return nil + return scanner.UnmarshalErr() } } -func getSignatureJSON(cdc *codec.Codec, newTx types.StdTx, indent, generateSignatureOnly bool) ([]byte, error) { - switch generateSignatureOnly { - case true: - switch indent { - case true: - return cdc.MarshalJSONIndent(newTx.Signatures[0], "", " ") - - default: - return cdc.MarshalJSON(newTx.Signatures[0]) - } - default: - switch indent { - case true: - return cdc.MarshalJSONIndent(newTx, "", " ") +func setOutputFile(cmd *cobra.Command) (func(), error) { + outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) + if outputDoc == "" { + cmd.SetOut(cmd.OutOrStdout()) + return func() {}, nil + } - default: - return cdc.MarshalJSON(newTx) - } + fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return func() {}, err } + + cmd.SetOut(fp) + + return func() { fp.Close() }, nil } -// printAndValidateSigs will validate the signatures of a given transaction over -// its expected signers. In addition, if offline has not been supplied, the -// signature is verified over the transaction sign bytes. -func printAndValidateSigs( - cliCtx context.CLIContext, chainID string, stdTx types.StdTx, offline bool, -) bool { +// GetSignCommand returns the transaction sign command. +func GetSignCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "sign [file]", + Short: "Sign a transaction generated offline", + Long: `Sign a transaction created with the --generate-only flag. +It will read a transaction from [file], sign it, and print its JSON encoding. + +If the --signature-only flag is set, it will output the signature parts only. - fmt.Println("Signers:") +The --offline flag makes sure that the client will not reach out to full node. +As a result, the account and sequence number queries will not be performed and +it is required to set such parameters manually. Note, invalid values will cause +the transaction to fail. - signers := stdTx.GetSigners() - for i, signer := range signers { - fmt.Printf(" %v: %v\n", i, signer.String()) +The --multisig= flag generates a signature on behalf of a multisig account +key. It implies --signature-only. Full multisig signed transactions may eventually +be generated via the 'multisign' command. +`, + PreRun: preSignCmd, + RunE: makeSignCmd(), + Args: cobra.ExactArgs(1), } - success := true - sigs := stdTx.Signatures + cmd.Flags().String(flagMultisig, "", "Address of the multisig account on behalf of which the transaction shall be signed") + cmd.Flags().Bool(flagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") + cmd.Flags().Bool(flagSigOnly, false, "Print only the signatures") + cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") + cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") + cmd.MarkFlagRequired(flags.FlagFrom) + flags.AddTxFlagsToCmd(cmd) - fmt.Println("") - fmt.Println("Signatures:") + return cmd +} - if len(sigs) != len(signers) { - success = false +func preSignCmd(cmd *cobra.Command, _ []string) { + // Conditionally mark the account and sequence numbers required as no RPC + // query will be done. + if offline, _ := cmd.Flags().GetBool(flags.FlagOffline); offline { + cmd.MarkFlagRequired(flags.FlagAccountNumber) + cmd.MarkFlagRequired(flags.FlagSequence) } +} + +func makeSignCmd() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) (err error) { + var clientCtx client.Context - for i, sig := range sigs { - sigAddr := sdk.AccAddress(sig.Address()) - sigSanity := "OK" + clientCtx, err = client.GetClientTxContext(cmd) + if err != nil { + return err + } + f := cmd.Flags() - var ( - multiSigHeader string - multiSigMsg string - ) + clientCtx, txF, newTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) + if err != nil { + return err + } + txCfg := clientCtx.TxConfig + txBuilder, err := txCfg.WrapTxBuilder(newTx) + if err != nil { + return err + } - if i >= len(signers) || !sigAddr.Equals(signers[i]) { - sigSanity = "ERROR: signature does not match its respective signer" - success = false + printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) + multisigAddrStr, _ := cmd.Flags().GetString(flagMultisig) + from, _ := cmd.Flags().GetString(flags.FlagFrom) + _, fromName, _, err := client.GetFromFields(txF.Keybase(), from, clientCtx.GenerateOnly) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) } - // Validate the actual signature over the transaction bytes since we can - // reach out to a full node to query accounts. - if !offline && success { - acc, err := types.NewAccountRetriever(cliCtx).GetAccount(sigAddr) + overwrite, _ := f.GetBool(flagOverwrite) + if multisigAddrStr != "" { + var multisigAddr sdk.AccAddress + multisigAddr, err = sdk.AccAddressFromBech32(multisigAddrStr) if err != nil { - fmt.Printf("failed to get account: %s\n", sigAddr) - return false + return err } + err = authclient.SignTxWithSignerAddress( + txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline, overwrite) + printSignatureOnly = true + } else { + err = authclient.SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, overwrite) + } + if err != nil { + return err + } - sigBytes := types.StdSignBytes( - chainID, acc.GetAccountNumber(), acc.GetSequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), - ) + aminoJSON, err := f.GetBool(flagAmino) + if err != nil { + return err + } - if ok := sig.VerifyBytes(sigBytes, sig.Signature); !ok { - sigSanity = "ERROR: signature invalid" - success = false + var json []byte + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) + if err != nil { + return err + } + req := rest.BroadcastReq{ + Tx: stdTx, + Mode: "block|sync|async", + } + json, err = clientCtx.LegacyAmino.MarshalJSON(req) + if err != nil { + return err + } + } else { + json, err = marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) + if err != nil { + return err } } - multiPK, ok := sig.PubKey.(multisig.PubKeyMultisigThreshold) - if ok { - var multiSig multisig.Multisignature - cliCtx.Codec.MustUnmarshalBinaryBare(sig.Signature, &multiSig) - - var b strings.Builder - b.WriteString("\n MultiSig Signatures:\n") + outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) + if outputDoc == "" { + cmd.Printf("%s\n", json) + return nil + } - for i := 0; i < multiSig.BitArray.Size(); i++ { - if multiSig.BitArray.GetIndex(i) { - addr := sdk.AccAddress(multiPK.PubKeys[i].Address().Bytes()) - b.WriteString(fmt.Sprintf(" %d: %s (weight: %d)\n", i, addr, 1)) - } + fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer func() { + err2 := fp.Close() + if err == nil { + err = err2 } + }() - multiSigHeader = fmt.Sprintf(" [multisig threshold: %d/%d]", multiPK.K, len(multiPK.PubKeys)) - multiSigMsg = b.String() - } + _, err = fp.Write(append(json, '\n')) + return err + } +} - fmt.Printf(" %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg) +func marshalSignatureJSON(txConfig client.TxConfig, txBldr client.TxBuilder, signatureOnly bool) ([]byte, error) { + parsedTx := txBldr.GetTx() + if signatureOnly { + sigs, err := parsedTx.GetSignaturesV2() + if err != nil { + return nil, err + } + return txConfig.MarshalSignatureJSON(sigs) } - fmt.Println("") - return success + return txConfig.TxJSONEncoder()(parsedTx) } diff --git a/x/auth/client/cli/validate_sigs.go b/x/auth/client/cli/validate_sigs.go new file mode 100644 index 000000000000..52290ed8e95f --- /dev/null +++ b/x/auth/client/cli/validate_sigs.go @@ -0,0 +1,136 @@ +package cli + +import ( + "fmt" + + "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" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func GetValidateSignaturesCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "validate-signatures [file]", + 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. + +The command would check whether all required signers have signed the transactions, whether +the signatures were collected in the right order, and if the signature is valid over the +given transaction. If the --offline flag is also set, signature validation over the +transaction will be not be performed as that will require RPC communication with a full node. +`, + PreRun: preSignCmd, + RunE: makeValidateSignaturesCmd(), + Args: cobra.ExactArgs(1), + } + + cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + clientCtx, txBldr, stdTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) + if err != nil { + return err + } + + if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) { + return fmt.Errorf("signatures validation failed") + } + + return nil + } +} + +// printAndValidateSigs will validate the signatures of a given transaction over its +// expected signers. In addition, if offline has not been supplied, the signature is +// verified over the transaction sign bytes. Returns false if the validation fails. +func printAndValidateSigs( + cmd *cobra.Command, clientCtx client.Context, chainID string, tx sdk.Tx, offline bool, +) bool { + sigTx := tx.(authsigning.SigVerifiableTx) + signModeHandler := clientCtx.TxConfig.SignModeHandler() + + cmd.Println("Signers:") + signers := sigTx.GetSigners() + for i, signer := range signers { + cmd.Printf(" %v: %v\n", i, signer.String()) + } + + success := true + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + panic(err) + } + cmd.Println("") + cmd.Println("Signatures:") + + if len(sigs) != len(signers) { + success = false + } + + for i, sig := range sigs { + var ( + pubKey = sig.PubKey + multiSigHeader string + multiSigMsg string + sigAddr = sdk.AccAddress(pubKey.Address()) + sigSanity = "OK" + ) + + if i >= len(signers) || !sigAddr.Equals(signers[i]) { + sigSanity = "ERROR: signature does not match its respective signer" + success = false + } + + // 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) + if err != nil { + cmd.Printf("failed to get account: %s\n", sigAddr) + return false + } + + signingData := authsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNum, + Sequence: accSeq, + } + err = authsigning.VerifySignature(pubKey, signingData, sig.Data, signModeHandler, sigTx) + if err != nil { + return false + } + } + + cmd.Printf(" %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg) + } + + cmd.Println("") + + return success +} + +func readTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (client.Context, tx.Factory, sdk.Tx, error) { + stdTx, err := authclient.ReadTxFromFile(clientCtx, filename) + if err != nil { + return clientCtx, tx.Factory{}, nil, err + } + + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + + return clientCtx, txFactory, stdTx, nil +} diff --git a/x/auth/client/query.go b/x/auth/client/query.go new file mode 100644 index 000000000000..bc1b7f6f4512 --- /dev/null +++ b/x/auth/client/query.go @@ -0,0 +1,153 @@ +package client + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "strings" + "time" + + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryTxsByEvents performs a search for transactions for a given set of events +// via the Tendermint RPC. An event takes the form of: +// "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is +// concatenated with an 'AND' operand. It returns a slice of Info object +// containing txs and metadata. An error is returned if the query fails. +// If an empty string is provided it will order txs by asc +func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) { + if len(events) == 0 { + return nil, errors.New("must declare at least one event to search") + } + + if page <= 0 { + return nil, errors.New("page must greater than 0") + } + + if limit <= 0 { + return nil, errors.New("limit must greater than 0") + } + + // XXX: implement ANY + query := strings.Join(events, " AND ") + + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + // TODO: this may not always need to be proven + // https://github.com/cosmos/cosmos-sdk/issues/6807 + resTxs, err := node.TxSearch(context.Background(), query, true, &page, &limit, orderBy) + if err != nil { + return nil, err + } + + resBlocks, err := getBlocksForTxResults(clientCtx, resTxs.Txs) + if err != nil { + return nil, err + } + + txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks) + if err != nil { + return nil, err + } + + result := sdk.NewSearchTxsResult(uint64(resTxs.TotalCount), uint64(len(txs)), uint64(page), uint64(limit), txs) + + return result, nil +} + +// QueryTx queries for a single transaction by a hash string in hex format. An +// error is returned if the transaction does not exist or cannot be queried. +func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) { + hash, err := hex.DecodeString(hashHexStr) + if err != nil { + return nil, err + } + + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + //TODO: this may not always need to be proven + // https://github.com/cosmos/cosmos-sdk/issues/6807 + resTx, err := node.Tx(context.Background(), hash, true) + if err != nil { + return nil, err + } + + resBlocks, err := getBlocksForTxResults(clientCtx, []*ctypes.ResultTx{resTx}) + if err != nil { + return nil, err + } + + out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height]) + if err != nil { + return out, err + } + + return out, nil +} + +// formatTxResults parses the indexed txs into a slice of TxResponse objects. +func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]*sdk.TxResponse, error) { + var err error + out := make([]*sdk.TxResponse, len(resTxs)) + for i := range resTxs { + out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height]) + if err != nil { + return nil, err + } + } + + return out, nil +} + +func getBlocksForTxResults(clientCtx client.Context, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + resBlocks := make(map[int64]*ctypes.ResultBlock) + + for _, resTx := range resTxs { + if _, ok := resBlocks[resTx.Height]; !ok { + resBlock, err := node.Block(context.Background(), &resTx.Height) + if err != nil { + return nil, err + } + + resBlocks[resTx.Height] = resBlock + } + } + + return resBlocks, nil +} + +func mkTxResult(txConfig client.TxConfig, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (*sdk.TxResponse, error) { + txb, err := txConfig.TxDecoder()(resTx.Tx) + if err != nil { + return nil, err + } + p, ok := txb.(intoAny) + if !ok { + return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb) + } + any := p.AsAny() + return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil +} + +// Deprecated: this interface is used only internally for scenario we are +// deprecating (StdTxConfig support) +type intoAny interface { + AsAny() *codectypes.Any +} diff --git a/x/auth/client/rest/broadcast.go b/x/auth/client/rest/broadcast.go index e7609194c390..9f7d6fc559a5 100644 --- a/x/auth/client/rest/broadcast.go +++ b/x/auth/client/rest/broadcast.go @@ -1,53 +1,67 @@ package rest import ( + "fmt" "io/ioutil" "net/http" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/tx" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) // BroadcastReq defines a tx broadcasting request. type BroadcastReq struct { - Tx types.StdTx `json:"tx" yaml:"tx"` - Mode string `json:"mode" yaml:"mode"` + Tx legacytx.StdTx `json:"tx" yaml:"tx"` + Mode string `json:"mode" yaml:"mode"` +} + +var _ codectypes.UnpackInterfacesMessage = BroadcastReq{} + +// UnpackInterfaces implements the UnpackInterfacesMessage interface. +func (m BroadcastReq) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return m.Tx.UnpackInterfaces(unpacker) } // BroadcastTxRequest implements a tx broadcasting handler that is responsible // for broadcasting a valid and signed tx to a full node. The tx can be // broadcasted via a sync|async|block mechanism. -func BroadcastTxRequest(cliCtx context.CLIContext) http.HandlerFunc { +func BroadcastTxRequest(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req BroadcastReq body, err := ioutil.ReadAll(r.Body) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - err = cliCtx.Codec.UnmarshalJSON(body, &req) + // NOTE: amino is used intentionally here, don't migrate it! + err = clientCtx.LegacyAmino.UnmarshalJSON(body, &req) if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return + err := fmt.Errorf("this transaction cannot be broadcasted via legacy REST endpoints, because it does not support"+ + " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC"+ + " endpoint to broadcast this transaction. The new REST endpoint (via gRPC-gateway) is POST /cosmos/tx/v1beta1/txs."+ + " Please also see the REST endpoints migration guide at %s for more info", clientrest.DeprecationURL) + if rest.CheckBadRequestError(w, err) { + return + } } - txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(req.Tx) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + txBytes, err := tx.ConvertAndEncodeStdTx(clientCtx.TxConfig, req.Tx) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithBroadcastMode(req.Mode) + clientCtx = clientCtx.WithBroadcastMode(req.Mode) - res, err := cliCtx.BroadcastTx(txBytes) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, err := clientCtx.BroadcastTx(txBytes) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, cliCtx, res) + rest.PostProcessResponseBare(w, clientCtx, res) } } diff --git a/x/auth/client/rest/decode.go b/x/auth/client/rest/decode.go index 98c4f0c6da04..5b732fa0a19d 100644 --- a/x/auth/client/rest/decode.go +++ b/x/auth/client/rest/decode.go @@ -2,12 +2,16 @@ package rest import ( "encoding/base64" + "fmt" "io/ioutil" "net/http" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/rest" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/cosmos/cosmos-sdk/x/auth/signing" ) type ( @@ -17,42 +21,70 @@ type ( } // DecodeResp defines a tx decoding response. - DecodeResp authtypes.StdTx + DecodeResp legacytx.StdTx ) // DecodeTxRequestHandlerFn returns the decode tx REST handler. In particular, // it takes base64-decoded bytes, decodes it from the Amino wire protocol, // and responds with a json-formatted transaction. -func DecodeTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func DecodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req DecodeReq body, err := ioutil.ReadAll(r.Body) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - err = cliCtx.Codec.UnmarshalJSON(body, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + // NOTE: amino is used intentionally here, don't migrate it + err = clientCtx.LegacyAmino.UnmarshalJSON(body, &req) + if rest.CheckBadRequestError(w, err) { return } txBytes, err := base64.StdEncoding.DecodeString(req.Tx) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - var stdTx authtypes.StdTx - err = cliCtx.Codec.UnmarshalBinaryLengthPrefixed(txBytes, &stdTx) + stdTx, err := convertToStdTx(w, clientCtx, txBytes) if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + // Error is already returned by convertToStdTx. return } response := DecodeResp(stdTx) - rest.PostProcessResponse(w, cliCtx, response) + + err = checkAminoMarshalError(clientCtx, response, "/cosmos/tx/v1beta1/txs/decode") + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + + return + } + + rest.PostProcessResponse(w, clientCtx, response) } } + +// convertToStdTx converts tx proto binary bytes retrieved from Tendermint into +// a StdTx. Returns the StdTx, as well as a flag denoting if the function +// successfully converted or not. +func convertToStdTx(w http.ResponseWriter, clientCtx client.Context, txBytes []byte) (legacytx.StdTx, error) { + txI, err := clientCtx.TxConfig.TxDecoder()(txBytes) + if rest.CheckBadRequestError(w, err) { + return legacytx.StdTx{}, err + } + + tx, ok := txI.(signing.Tx) + if !ok { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("%+v is not backwards compatible with %T", tx, legacytx.StdTx{})) + return legacytx.StdTx{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (signing.Tx)(nil), txI) + } + + stdTx, err := clienttx.ConvertTxToStdTx(clientCtx.LegacyAmino, tx) + if rest.CheckBadRequestError(w, err) { + return legacytx.StdTx{}, err + } + + return stdTx, nil +} diff --git a/x/auth/client/rest/encode.go b/x/auth/client/rest/encode.go index e2d9f4fea319..638817801533 100644 --- a/x/auth/client/rest/encode.go +++ b/x/auth/client/rest/encode.go @@ -2,12 +2,15 @@ package rest import ( "encoding/base64" + "fmt" "io/ioutil" "net/http" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) // EncodeResp defines a tx encoding response. @@ -15,29 +18,37 @@ type EncodeResp struct { Tx string `json:"tx" yaml:"tx"` } +// ErrEncodeDecode is the error to show when encoding/decoding txs that are not +// amino-serializable (e.g. IBC txs). +var ErrEncodeDecode error = fmt.Errorf("this endpoint does not support txs that are not serializable"+ + " via Amino, such as txs that contain IBC `Msg`s. For more info, please refer to our"+ + " REST migration guide at %s", clientrest.DeprecationURL) + // EncodeTxRequestHandlerFn returns the encode tx REST handler. In particular, // it takes a json-formatted transaction, encodes it to the Amino wire protocol, // and responds with base64-encoded bytes. -func EncodeTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func EncodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req types.StdTx + var req legacytx.StdTx body, err := ioutil.ReadAll(r.Body) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - err = cliCtx.Codec.UnmarshalJSON(body, &req) + // NOTE: amino is used intentionally here, don't migrate it + err = clientCtx.LegacyAmino.UnmarshalJSON(body, &req) + // If there's an unmarshalling error, we assume that it's because we're + // using amino to unmarshal a non-amino tx. if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return + if rest.CheckBadRequestError(w, ErrEncodeDecode) { + return + } } - // re-encode it via the Amino wire protocol - txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + // re-encode it in the chain's native binary format + txBytes, err := tx.ConvertAndEncodeStdTx(clientCtx.TxConfig, req) + if rest.CheckInternalServerError(w, err) { return } @@ -45,6 +56,8 @@ func EncodeTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes) response := EncodeResp{Tx: txBytesBase64} - rest.PostProcessResponseBare(w, cliCtx, response) + + // NOTE: amino is set intentionally here, don't migrate it + rest.PostProcessResponseBare(w, clientCtx, response) } } diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index 77cba3e59c1e..d11d4b3416cc 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -8,40 +8,41 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/auth/types" genutilrest "github.com/cosmos/cosmos-sdk/x/genutil/client/rest" ) -// query accountREST Handler -func QueryAccountRequestHandlerFn(storeName string, cliCtx context.CLIContext) http.HandlerFunc { +// QueryAccountRequestHandlerFn is the query accountREST Handler. +func QueryAccountRequestHandlerFn(storeName string, clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32addr := vars["address"] addr, err := sdk.AccAddressFromBech32(bech32addr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - accGetter := types.NewAccountRetriever(cliCtx) + accGetter := types.AccountRetriever{} - account, height, err := accGetter.GetAccountWithHeight(addr) + account, height, err := accGetter.GetAccountWithHeight(clientCtx, addr) if err != nil { // TODO: Handle more appropriately based on the error type. // Ref: https://github.com/cosmos/cosmos-sdk/issues/4923 - if err := accGetter.EnsureExists(addr); err != nil { - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, types.BaseAccount{}) + if err := accGetter.EnsureExists(clientCtx, addr); err != nil { + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, types.BaseAccount{}) return } @@ -49,15 +50,15 @@ func QueryAccountRequestHandlerFn(storeName string, cliCtx context.CLIContext) h return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, account) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, account) } } -// QueryTxsHandlerFn implements a REST handler that searches for transactions. +// QueryTxsRequestHandlerFn implements a REST handler that searches for transactions. // Genesis transactions are returned if the height parameter is set to zero, // otherwise the transactions are searched for by events. -func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func QueryTxsRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { @@ -72,7 +73,7 @@ func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { heightStr := r.FormValue("height") if heightStr != "" { if height, err := strconv.ParseInt(heightStr, 10, 64); err == nil && height == 0 { - genutilrest.QueryGenesisTxs(cliCtx, w) + genutilrest.QueryGenesisTxs(clientCtx, w) return } } @@ -83,45 +84,54 @@ func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { page, limit int ) - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } if len(r.Form) == 0 { - rest.PostProcessResponseBare(w, cliCtx, txs) + rest.PostProcessResponseBare(w, clientCtx, txs) return } events, page, limit, err = rest.ParseHTTPArgs(r) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { + return + } + + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, page, limit, "") + if rest.CheckInternalServerError(w, err) { return } - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, page, limit) + for _, txRes := range searchResult.Txs { + packStdTxResponse(w, clientCtx, txRes) + } + + err = checkAminoMarshalError(clientCtx, searchResult, "/cosmos/tx/v1beta1/txs") if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return } - rest.PostProcessResponseBare(w, cliCtx, searchResult) + rest.PostProcessResponseBare(w, clientCtx, searchResult) } } // QueryTxRequestHandlerFn implements a REST handler that queries a transaction // by hash in a committed block. -func QueryTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func QueryTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) hashHexStr := vars["hash"] - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - output, err := utils.QueryTx(cliCtx, hashHexStr) + output, err := authclient.QueryTx(clientCtx, hashHexStr) if err != nil { if strings.Contains(err.Error(), hashHexStr) { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) @@ -131,10 +141,80 @@ func QueryTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } + err = packStdTxResponse(w, clientCtx, output) + if err != nil { + // Error is already returned by packStdTxResponse. + return + } + if output.Empty() { rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr)) } - rest.PostProcessResponseBare(w, cliCtx, output) + err = checkAminoMarshalError(clientCtx, output, "/cosmos/tx/v1beta1/txs/{txhash}") + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + + return + } + + rest.PostProcessResponseBare(w, clientCtx, output) + } +} + +func queryParamsHandler(clientCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) + if !ok { + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParams) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { + return + } + + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } + +// packStdTxResponse takes a sdk.TxResponse, converts the Tx into a StdTx, and +// packs the StdTx again into the sdk.TxResponse Any. Amino then takes care of +// seamlessly JSON-outputting the Any. +func packStdTxResponse(w http.ResponseWriter, clientCtx client.Context, txRes *sdk.TxResponse) error { + // We just unmarshalled from Tendermint, we take the proto Tx's raw + // bytes, and convert them into a StdTx to be displayed. + txBytes := txRes.Tx.Value + stdTx, err := convertToStdTx(w, clientCtx, txBytes) + if err != nil { + return err + } + + // Pack the amino stdTx into the TxResponse's Any. + txRes.Tx = codectypes.UnsafePackAny(stdTx) + + return nil +} + +// checkAminoMarshalError checks if there are errors with marshalling non-amino +// txs with amino. +func checkAminoMarshalError(ctx client.Context, resp interface{}, grpcEndPoint string) error { + // LegacyAmino used intentionally here to handle the SignMode errors + marshaler := ctx.LegacyAmino + + _, err := marshaler.MarshalJSON(resp) + if err != nil { + + // If there's an unmarshalling error, we assume that it's because we're + // using amino to unmarshal a non-amino tx. + return fmt.Errorf("this transaction cannot be displayed via legacy REST endpoints, because it does not support"+ + " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC"+ + " endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is %s. Please also see the"+ + "REST endpoints migration guide at %s for more info", grpcEndPoint, clientrest.DeprecationURL) + + } + + return nil +} diff --git a/x/auth/client/rest/rest.go b/x/auth/client/rest/rest.go index 98dc8134127a..77f8f4896465 100644 --- a/x/auth/client/rest/rest.go +++ b/x/auth/client/rest/rest.go @@ -3,21 +3,34 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" +) + +// REST query and parameter values +const ( + MethodGet = "GET" ) // RegisterRoutes registers the auth module REST routes. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { +func RegisterRoutes(clientCtx client.Context, rtr *mux.Router, storeName string) { + r := rest.WithHTTPDeprecationHeaders(rtr) + r.HandleFunc( + "/auth/accounts/{address}", QueryAccountRequestHandlerFn(storeName, clientCtx), + ).Methods(MethodGet) + r.HandleFunc( - "/auth/accounts/{address}", QueryAccountRequestHandlerFn(storeName, cliCtx), - ).Methods("GET") + "/auth/params", + queryParamsHandler(clientCtx), + ).Methods(MethodGet) } // RegisterTxRoutes registers all transaction routes on the provided router. -func RegisterTxRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/txs", QueryTxsRequestHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/txs", BroadcastTxRequest(cliCtx)).Methods("POST") - r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cliCtx)).Methods("POST") - r.HandleFunc("/txs/decode", DecodeTxRequestHandlerFn(cliCtx)).Methods("POST") +func RegisterTxRoutes(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/txs", QueryTxsRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/txs", BroadcastTxRequest(clientCtx)).Methods("POST") + r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(clientCtx)).Methods("POST") + r.HandleFunc("/txs/decode", DecodeTxRequestHandlerFn(clientCtx)).Methods("POST") } diff --git a/x/auth/client/rest/rest_test.go b/x/auth/client/rest/rest_test.go new file mode 100644 index 000000000000..454dc562a873 --- /dev/null +++ b/x/auth/client/rest/rest_test.go @@ -0,0 +1,650 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + 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" + authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil" + "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" + 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 { + suite.Suite + + cfg network.Config + network *network.Network + + stdTx legacytx.StdTx + stdTxRes sdk.TxResponse +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + cfg.NumValidators = 2 + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + kb := s.network.Validators[0].ClientCtx.Keyring + _, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()}) + _, err = kb.SaveMultisig("multi", multi) + s.Require().NoError(err) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // Broadcast a StdTx used for tests. + s.stdTx = s.createTestStdTx(s.network.Validators[0], 0, 1) + res, err := s.broadcastReq(s.stdTx, "block") + s.Require().NoError(err) + + // NOTE: this uses amino explicitly, don't migrate it! + s.Require().NoError(s.cfg.LegacyAmino.UnmarshalJSON(res, &s.stdTxRes)) + s.Require().Equal(uint32(0), s.stdTxRes.Code) + + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func mkStdTx() legacytx.StdTx { + // NOTE: this uses StdTx explicitly, don't migrate it! + return legacytx.StdTx{ + Msgs: []sdk.Msg{&types.MsgSend{}}, + Fee: legacytx.StdFee{ + Amount: sdk.Coins{sdk.NewInt64Coin("foo", 10)}, + Gas: 10000, + }, + Memo: "FOOBAR", + } +} + +// Create an IBC tx that's encoded as amino-JSON. Since we can't amino-marshal +// a tx with "cosmos-sdk/MsgTransfer" using the SDK, we just hardcode the tx +// here. But external clients might, see https://github.com/cosmos/cosmos-sdk/issues/8022. +func mkIBCStdTx() []byte { + ibcTx := `{ + "account_number": "68", + "chain_id": "stargate-4", + "fee": { + "amount": [ + { + "amount": "3500", + "denom": "umuon" + } + ], + "gas": "350000" + }, + "memo": "", + "msg": [ + { + "type": "cosmos-sdk/MsgTransfer", + "value": { + "receiver": "cosmos1q9wtnlwdjrhwtcjmt2uq77jrgx7z3usrq2yz7z", + "sender": "cosmos1q9wtnlwdjrhwtcjmt2uq77jrgx7z3usrq2yz7z", + "source_channel": "THEslipperCHANNEL", + "source_port": "transfer", + "token": { + "amount": "1000000", + "denom": "umuon" + } + } + } + ], + "sequence": "24" + }` + req := fmt.Sprintf(`{"tx":%s,"mode":"async"}`, ibcTx) + + return []byte(req) +} + +func (s *IntegrationTestSuite) TestEncodeDecode() { + var require = s.Require() + val := s.network.Validators[0] + stdTx := mkStdTx() + + // NOTE: this uses amino explicitly, don't migrate it! + cdc := val.ClientCtx.LegacyAmino + + bz, err := cdc.MarshalJSON(stdTx) + require.NoError(err) + + res, err := rest.PostRequest(fmt.Sprintf("%s/txs/encode", val.APIAddress), "application/json", bz) + require.NoError(err) + + var encodeResp authrest.EncodeResp + err = cdc.UnmarshalJSON(res, &encodeResp) + require.NoError(err) + + bz, err = cdc.MarshalJSON(authrest.DecodeReq{Tx: encodeResp.Tx}) + require.NoError(err) + + res, err = rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz) + require.NoError(err) + + var respWithHeight rest.ResponseWithHeight + err = cdc.UnmarshalJSON(res, &respWithHeight) + require.NoError(err) + var decodeResp authrest.DecodeResp + err = cdc.UnmarshalJSON(respWithHeight.Result, &decodeResp) + require.NoError(err) + require.Equal(stdTx, legacytx.StdTx(decodeResp)) +} + +func (s *IntegrationTestSuite) TestEncodeIBCTx() { + val := s.network.Validators[0] + + req := mkIBCStdTx() + res, err := rest.PostRequest(fmt.Sprintf("%s/txs/encode", val.APIAddress), "application/json", []byte(req)) + s.Require().NoError(err) + + s.Require().Contains(string(res), authrest.ErrEncodeDecode.Error()) +} + +func (s *IntegrationTestSuite) TestBroadcastTxRequest() { + stdTx := mkStdTx() + + // we just test with async mode because this tx will fail - all we care about is that it got encoded and broadcast correctly + res, err := s.broadcastReq(stdTx, "async") + s.Require().NoError(err) + var txRes sdk.TxResponse + // NOTE: this uses amino explicitly, don't migrate it! + s.Require().NoError(s.cfg.LegacyAmino.UnmarshalJSON(res, &txRes)) + // we just check for a non-empty TxHash here, the actual hash will depend on the underlying tx configuration + s.Require().NotEmpty(txRes.TxHash) +} + +func (s *IntegrationTestSuite) TestBroadcastIBCTxRequest() { + val := s.network.Validators[0] + + req := mkIBCStdTx() + res, err := rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", []byte(req)) + s.Require().NoError(err) + + 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. +func (s *IntegrationTestSuite) testQueryTx(txHeight int64, txHash, txRecipient string) { + val0 := s.network.Validators[0] + + testCases := []struct { + desc string + malleate func() *sdk.TxResponse + }{ + { + "Query by hash", + func() *sdk.TxResponse { + txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs/%s", val0.APIAddress, txHash)) + s.Require().NoError(err) + + var txResAmino sdk.TxResponse + s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &txResAmino)) + return &txResAmino + }, + }, + { + "Query by height", + func() *sdk.TxResponse { + txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs?limit=10&page=1&tx.height=%d", val0.APIAddress, txHeight)) + s.Require().NoError(err) + + var searchtxResult sdk.SearchTxsResult + s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &searchtxResult)) + s.Require().Len(searchtxResult.Txs, 1) + return searchtxResult.Txs[0] + }, + }, + { + "Query by event (transfer.recipient)", + func() *sdk.TxResponse { + txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs?transfer.recipient=%s", val0.APIAddress, txRecipient)) + s.Require().NoError(err) + + var searchtxResult sdk.SearchTxsResult + s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &searchtxResult)) + s.Require().Len(searchtxResult.Txs, 1) + return searchtxResult.Txs[0] + }, + }, + } + + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.desc), func() { + txResponse := tc.malleate() + + // Check that the height is correct. + s.Require().Equal(txHeight, txResponse.Height) + + // Check that the events are correct. + s.Require().Contains( + txResponse.RawLog, + fmt.Sprintf("{\"key\":\"recipient\",\"value\":\"%s\"}", txRecipient), + ) + + // Check that the Msg is correct. + stdTx, ok := txResponse.Tx.GetCachedValue().(legacytx.StdTx) + s.Require().True(ok) + msgs := stdTx.GetMsgs() + s.Require().Equal(len(msgs), 1) + msg, ok := msgs[0].(*types.MsgSend) + s.Require().True(ok) + s.Require().Equal(txRecipient, msg.ToAddress) + }) + } +} + +func (s *IntegrationTestSuite) TestQueryTxWithStdTx() { + val0 := s.network.Validators[0] + + // We broadcasted a StdTx in SetupSuite. + // We just check for a non-empty TxHash here, the actual hash will depend on the underlying tx configuration + s.Require().NotEmpty(s.stdTxRes.TxHash) + + s.testQueryTx(s.stdTxRes.Height, s.stdTxRes.TxHash, val0.Address.String()) +} + +func (s *IntegrationTestSuite) TestQueryTxWithServiceMessage() { + val := s.network.Validators[0] + + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + _, _, addr := testdata.KeyTestPubAddr() + + // Might need to wait a block to refresh sequences from previous setups. + s.Require().NoError(s.network.WaitForNextBlock()) + + out, err := bankcli.ServiceMsgSendExec( + val.ClientCtx, + val.Address, + addr, + 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 txRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) + s.Require().Equal(uint32(0), txRes.Code) + + s.Require().NoError(s.network.WaitForNextBlock()) + + s.testQueryTx(txRes.Height, txRes.TxHash, addr.String()) +} + +func (s *IntegrationTestSuite) TestMultipleSyncBroadcastTxRequests() { + // First test transaction from validator should have sequence=1 (non-genesis tx) + testCases := []struct { + desc string + sequence uint64 + shouldErr bool + }{ + { + "First tx (correct sequence)", + 1, + false, + }, + { + "Second tx (correct sequence)", + 2, + false, + }, + { + "Third tx (incorrect sequence)", + 9, + true, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.desc), func() { + // broadcast test with sync mode, as we want to run CheckTx to verify account sequence is correct + stdTx := s.createTestStdTx(s.network.Validators[1], 1, tc.sequence) + res, err := s.broadcastReq(stdTx, "sync") + s.Require().NoError(err) + + var txRes sdk.TxResponse + // NOTE: this uses amino explicitly, don't migrate it! + s.Require().NoError(s.cfg.LegacyAmino.UnmarshalJSON(res, &txRes)) + // we check for a exitCode=0, indicating a successful broadcast + if tc.shouldErr { + var sigVerifyFailureCode uint32 = 4 + s.Require().Equal(sigVerifyFailureCode, txRes.Code, + "Testcase '%s': Expected signature verification failure {Code: %d} from TxResponse. "+ + "Found {Code: %d, RawLog: '%v'}", + tc.desc, sigVerifyFailureCode, txRes.Code, txRes.RawLog, + ) + } else { + s.Require().Equal(uint32(0), txRes.Code, + "Testcase '%s': TxResponse errored unexpectedly. Err: {Code: %d, RawLog: '%v'}", + tc.desc, txRes.Code, txRes.RawLog, + ) + } + }) + } +} + +func (s *IntegrationTestSuite) createTestStdTx(val *network.Validator, accNum, sequence uint64) legacytx.StdTx { + txConfig := legacytx.StdTxConfig{Cdc: s.cfg.LegacyAmino} + + msg := &types.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(fmt.Sprintf("%stoken", val.Moniker), 100)}, + } + + // prepare txBuilder with msg + txBuilder := txConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + txBuilder.SetMsgs(msg) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo("foobar") + + // setup txFactory + txFactory := tx.Factory{}. + WithChainID(val.ClientCtx.ChainID). + WithKeybase(val.ClientCtx.Keyring). + WithTxConfig(txConfig). + WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON). + WithAccountNumber(accNum). + WithSequence(sequence) + + // sign Tx (offline mode so we can manually set sequence number) + err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, true, true) + s.Require().NoError(err) + + stdTx := txBuilder.GetTx().(legacytx.StdTx) + + return stdTx +} + +func (s *IntegrationTestSuite) broadcastReq(stdTx legacytx.StdTx, mode string) ([]byte, error) { + val := s.network.Validators[0] + + // NOTE: this uses amino explicitly, don't migrate it! + cdc := val.ClientCtx.LegacyAmino + req := authrest.BroadcastReq{ + Tx: stdTx, + Mode: mode, + } + bz, err := cdc.MarshalJSON(req) + s.Require().NoError(err) + + return rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", bz) +} + +// testQueryIBCTx is a helper function to test querying txs which: +// - show an error message on legacy REST endpoints +// - succeed using gRPC +// In practice, we call this function on IBC txs. +func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.Command, args []string) { + val := s.network.Validators[0] + + errMsg := "this transaction cannot be displayed via legacy REST endpoints, because it does not support" + + " Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC" + + " endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is " + + // Test that legacy endpoint return the above error message on IBC txs. + testCases := []struct { + desc string + url string + }{ + { + "Query by hash", + fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash), + }, + { + "Query by height", + fmt.Sprintf("%s/txs?tx.height=%d", val.APIAddress, txRes.Height), + }, + } + + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.desc), func() { + txJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &errResp)) + + s.Require().Contains(errResp.Error, errMsg) + }) + } + + // try fetching the txn using gRPC req, it will fetch info since it has proto codec. + grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, txRes.TxHash)) + s.Require().NoError(err) + + var getTxRes txtypes.GetTxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(grpcJSON, &getTxRes)) + s.Require().Equal(getTxRes.Tx.Body.Memo, "foobar") + + // generate broadcast only txn. + args = append(args, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + s.Require().NoError(err) + + txFile := testutil.WriteToNewTempFile(s.T(), string(out.Bytes())) + txFileName := txFile.Name() + + // encode the generated txn. + out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.GetEncodeCommand(), []string{txFileName}) + s.Require().NoError(err) + + bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(authrest.DecodeReq{Tx: string(out.Bytes())}) + s.Require().NoError(err) + + // try to decode the txn using legacy rest, it fails. + res, err := rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz) + s.Require().NoError(err) + + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &errResp)) + s.Require().Contains(errResp.Error, errMsg) +} + +// TestLegacyRestErrMessages creates two IBC txs, one that fails, one that +// succeeds, and make sure we cannot query any of them (with pretty error msg). +// Our intension is to test the error message of querying a message which is +// signed with proto, since IBC won't support legacy amino at all we are +// considering a message from IBC module. +func (s *IntegrationTestSuite) TestLegacyRestErrMessages() { + val := s.network.Validators[0] + + // Write consensus json to temp file, used for an IBC message. + consensusJSON := testutil.WriteToNewTempFile( + s.T(), + `{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/3SXL2ONYaOkxpdR5P8tHTlSlPv1AwQwSFxKRee5JQW"},"diversifier":"diversifier","timestamp":"10"}`, + ) + + testCases := []struct { + desc string + cmd *cobra.Command + args []string + code uint32 + }{ + { + "Failing IBC message", + ibccli.NewChannelCloseInitCmd(), + []string{ + "121", // dummy port-id + "channel-0", // dummy channel-id + 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), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=foobar", flags.FlagMemo), + }, + uint32(7), + }, + { + "Successful IBC message", + ibcsolomachinecli.NewCreateClientCmd(), + []string{ + "1", // dummy sequence + 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()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=foobar", flags.FlagMemo), + }, + uint32(0), + }, + } + + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.desc), func() { + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, tc.cmd, tc.args) + s.Require().NoError(err) + var txRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) + s.Require().Equal(tc.code, txRes.Code) + + s.Require().NoError(s.network.WaitForNextBlock()) + + s.testQueryIBCTx(txRes, tc.cmd, tc.args) + }) + } +} + +// TestLegacyMultiSig creates a legacy multisig transaction, and makes sure +// we can query it via the legacy REST endpoint. +// ref: https://github.com/cosmos/cosmos-sdk/issues/8679 +func (s *IntegrationTestSuite) TestLegacyMultisig() { + val1 := *s.network.Validators[0] + + // Generate 2 accounts and a multisig. + account1, err := val1.ClientCtx.Keyring.Key("newAccount1") + s.Require().NoError(err) + + account2, err := val1.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + multisigInfo, err := val1.ClientCtx.Keyring.Key("multi") + s.Require().NoError(err) + + // Send coins from validator to multisig. + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) + _, err = bankcli.MsgSendExec( + val1.ClientCtx, + val1.Address, + multisigInfo.GetAddress(), + sdk.NewCoins(sendTokens), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + ) + + s.Require().NoError(s.network.WaitForNextBlock()) + + // Generate multisig transaction to a random address. + _, _, recipient := testdata.KeyTestPubAddr() + multiGeneratedTx, err := bankcli.MsgSendExec( + val1.ClientCtx, + multisigInfo.GetAddress(), + recipient, + sdk.NewCoins( + sdk.NewInt64Coin(s.cfg.BondDenom, 5), + ), + 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) + + // Save tx to file + multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) + + // Sign with account1 + val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1) + account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) + + // Sign with account1 + account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String()) + s.Require().NoError(err) + + sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) + + // Does not work in offline mode. + _, err = authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), "--offline", sign1File.Name(), sign2File.Name()) + s.Require().EqualError(err, fmt.Sprintf("couldn't verify signature: unable to verify single signer signature")) + + val1.ClientCtx.Offline = false + multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) + s.Require().NoError(err) + + // Write the output to disk + signedTxFile := testutil.WriteToNewTempFile(s.T(), multiSigWith2Signatures.String()) + + _, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + val1.ClientCtx.BroadcastMode = flags.BroadcastBlock + out, err := authtest.TxBroadcastExec(val1.ClientCtx, signedTxFile.Name()) + s.Require().NoError(err) + + s.Require().NoError(s.network.WaitForNextBlock()) + + var txRes sdk.TxResponse + err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes) + s.Require().NoError(err) + s.Require().Equal(uint32(0), txRes.Code) + + s.testQueryTx(txRes.Height, txRes.TxHash, recipient.String()) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/auth/client/testutil/helpers.go b/x/auth/client/testutil/helpers.go new file mode 100644 index 000000000000..6d68ef9236f2 --- /dev/null +++ b/x/auth/client/testutil/helpers.go @@ -0,0 +1,109 @@ +package testutil + +import ( + "fmt" + "strings" + + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/x/auth/client/cli" +) + +func TxSignExec(clientCtx client.Context, from fmt.Stringer, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--from=%s", from.String()), + fmt.Sprintf("--%s=%s", flags.FlagHome, strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1)), + fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), + filename, + } + + cmd := cli.GetSignCommand() + tmcli.PrepareBaseCmd(cmd, "", "") + + return clitestutil.ExecTestCLICmd(clientCtx, cmd, append(args, extraArgs...)) +} + +func TxBroadcastExec(clientCtx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + filename, + } + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetBroadcastCommand(), append(args, extraArgs...)) +} + +func TxEncodeExec(clientCtx client.Context, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + filename, + } + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetEncodeCommand(), append(args, extraArgs...)) +} + +func TxValidateSignaturesExec(clientCtx client.Context, filename string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), + filename, + } + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetValidateSignaturesCommand(), args) +} + +func TxMultiSignExec(clientCtx client.Context, from string, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), + filename, + from, + } + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetMultiSignCommand(), append(args, extraArgs...)) +} + +func TxSignBatchExec(clientCtx client.Context, from fmt.Stringer, filename string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--from=%s", from.String()), + filename, + } + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetSignBatchCommand(), append(args, extraArgs...)) +} + +func TxDecodeExec(clientCtx client.Context, encodedTx string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + encodedTx, + } + + 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.GetAccountCmd(), append(args, extraArgs...)) +} + +func TxMultiSignBatchExec(clientCtx client.Context, filename string, from string, sigFile1 string, sigFile2 string, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + filename, + from, + sigFile1, + sigFile2, + } + + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, cli.GetMultiSignBatchCmd(), args) +} + +// DONTCOVER diff --git a/x/auth/client/tx.go b/x/auth/client/tx.go new file mode 100644 index 000000000000..ebaac117d651 --- /dev/null +++ b/x/auth/client/tx.go @@ -0,0 +1,197 @@ +package client + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + "github.com/gogo/protobuf/jsonpb" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" +) + +// Codec defines the x/auth account codec to be used for use with the +// AccountRetriever. The application must be sure to set this to their respective +// codec that implements the Codec interface and must be the same codec that +// passed to the x/auth module. +// +// TODO:/XXX: Using a package-level global isn't ideal and we should consider +// refactoring the module manager to allow passing in the correct module codec. +var Codec codec.Marshaler + +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"` +} + +func (gr GasEstimateResponse) String() string { + return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) +} + +// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. +func PrintUnsignedStdTx(txBldr tx.Factory, clientCtx client.Context, msgs []sdk.Msg) error { + err := tx.GenerateTx(clientCtx, txBldr, msgs...) + return err +} + +// SignTx signs a transaction managed by the TxBuilder using a `name` key stored in Keybase. +// The new signature is appended to the TxBuilder when overwrite=false or overwritten otherwise. +// Don't perform online validation or lookups if offline is true. +func SignTx(txFactory tx.Factory, clientCtx client.Context, name string, txBuilder client.TxBuilder, offline, overwriteSig bool) error { + info, err := txFactory.Keybase().Key(name) + if err != nil { + return err + } + + // Ledger and Multisigs only support LEGACY_AMINO_JSON signing. + if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED && + (info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeMulti) { + txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + addr := sdk.AccAddress(info.GetPubKey().Address()) + if !isTxSigner(addr, txBuilder.GetTx().GetSigners()) { + return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name) + } + if !offline { + txFactory, err = populateAccountFromState(txFactory, clientCtx, addr) + if err != nil { + return err + } + } + + return tx.Sign(txFactory, name, txBuilder, overwriteSig) +} + +// SignTxWithSignerAddress attaches a signature to a transaction. +// Don't perform online validation or lookups if offline is true, else +// populate account and sequence numbers from a foreign account. +// This function should only be used when signing with a multisig. For +// normal keys, please use SignTx directly. +func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, addr sdk.AccAddress, + name string, txBuilder client.TxBuilder, offline, overwrite bool) (err error) { + // Multisigs only support LEGACY_AMINO_JSON signing. + if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED { + txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + } + + // check whether the address is a signer + if !isTxSigner(addr, txBuilder.GetTx().GetSigners()) { + return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name) + } + + if !offline { + txFactory, err = populateAccountFromState(txFactory, clientCtx, addr) + if err != nil { + return err + } + } + + return tx.Sign(txFactory, name, txBuilder, overwrite) +} + +// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin. +func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error) { + var bytes []byte + + if filename == "-" { + bytes, err = ioutil.ReadAll(os.Stdin) + } else { + bytes, err = ioutil.ReadFile(filename) + } + + if err != nil { + return + } + + return ctx.TxConfig.TxJSONDecoder()(bytes) +} + +// NewBatchScanner returns a new BatchScanner to read newline-delimited StdTx transactions from r. +func NewBatchScanner(cfg client.TxConfig, r io.Reader) *BatchScanner { + return &BatchScanner{Scanner: bufio.NewScanner(r), cfg: cfg} +} + +// BatchScanner provides a convenient interface for reading batch data such as a file +// of newline-delimited JSON encoded StdTx. +type BatchScanner struct { + *bufio.Scanner + theTx sdk.Tx + cfg client.TxConfig + unmarshalErr error +} + +// Tx returns the most recent Tx unmarshalled by a call to Scan. +func (bs BatchScanner) Tx() sdk.Tx { return bs.theTx } + +// UnmarshalErr returns the first unmarshalling error that was encountered by the scanner. +func (bs BatchScanner) UnmarshalErr() error { return bs.unmarshalErr } + +// Scan advances the Scanner to the next line. +func (bs *BatchScanner) Scan() bool { + if !bs.Scanner.Scan() { + return false + } + + tx, err := bs.cfg.TxJSONDecoder()(bs.Bytes()) + bs.theTx = tx + if err != nil && bs.unmarshalErr == nil { + bs.unmarshalErr = err + return false + } + + return true +} + +func populateAccountFromState( + txBldr tx.Factory, clientCtx client.Context, addr sdk.AccAddress, +) (tx.Factory, error) { + + num, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr) + if err != nil { + return txBldr, err + } + + return txBldr.WithAccountNumber(num).WithSequence(seq), nil +} + +// GetTxEncoder return tx encoder from global sdk configuration if ones is defined. +// Otherwise returns encoder with default logic. +func GetTxEncoder(cdc *codec.LegacyAmino) (encoder sdk.TxEncoder) { + encoder = sdk.GetConfig().GetTxEncoder() + if encoder == nil { + encoder = legacytx.DefaultTxEncoder(cdc) + } + + return encoder +} + +func ParseQueryResponse(bz []byte) (sdk.SimulationResponse, error) { + var simRes sdk.SimulationResponse + if err := jsonpb.Unmarshal(strings.NewReader(string(bz)), &simRes); err != nil { + return sdk.SimulationResponse{}, err + } + + return simRes, nil +} + +func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { + for _, s := range signers { + if bytes.Equal(user.Bytes(), s.Bytes()) { + return true + } + } + + return false +} diff --git a/x/auth/client/tx_test.go b/x/auth/client/tx_test.go new file mode 100644 index 000000000000..3e1d31383ddc --- /dev/null +++ b/x/auth/client/tx_test.go @@ -0,0 +1,158 @@ +package client_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var ( + priv = ed25519.GenPrivKey() + addr = sdk.AccAddress(priv.PubKey().Address()) +) + +func TestParseQueryResponse(t *testing.T) { + simRes := &sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: 10, GasWanted: 20}, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, + } + + bz, err := codec.ProtoMarshalJSON(simRes, nil) + require.NoError(t, err) + + res, err := authclient.ParseQueryResponse(bz) + require.NoError(t, err) + require.Equal(t, 10, int(res.GasInfo.GasUsed)) + require.NotNil(t, res.Result) + + res, err = authclient.ParseQueryResponse([]byte("fuzzy")) + require.Error(t, err) +} + +// TODO: remove this and authclient.GetTxEncoder after the proto tx migration is complete +func TestDefaultTxEncoder(t *testing.T) { + cdc := makeCodec() + + defaultEncoder := legacytx.DefaultTxEncoder(cdc) + encoder := authclient.GetTxEncoder(cdc) + + compareEncoders(t, defaultEncoder, encoder) +} + +func TestReadTxFromFile(t *testing.T) { + t.Parallel() + encodingConfig := simapp.MakeTestEncodingConfig() + + txCfg := encodingConfig.TxConfig + clientCtx := client.Context{} + clientCtx = clientCtx.WithInterfaceRegistry(encodingConfig.InterfaceRegistry) + clientCtx = clientCtx.WithTxConfig(txCfg) + + feeAmount := sdk.Coins{sdk.NewInt64Coin("atom", 150)} + gasLimit := uint64(50000) + memo := "foomemo" + + txBuilder := txCfg.NewTxBuilder() + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo(memo) + + // Write it to the file + encodedTx, err := txCfg.TxJSONEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + + jsonTxFile := testutil.WriteToNewTempFile(t, string(encodedTx)) + // Read it back + decodedTx, err := authclient.ReadTxFromFile(clientCtx, jsonTxFile.Name()) + require.NoError(t, err) + txBldr, err := txCfg.WrapTxBuilder(decodedTx) + require.NoError(t, err) + t.Log(txBuilder.GetTx()) + t.Log(txBldr.GetTx()) + require.Equal(t, txBuilder.GetTx().GetMemo(), txBldr.GetTx().GetMemo()) + require.Equal(t, txBuilder.GetTx().GetFee(), txBldr.GetTx().GetFee()) +} + +func TestBatchScanner_Scan(t *testing.T) { + t.Parallel() + encodingConfig := simapp.MakeTestEncodingConfig() + + txGen := encodingConfig.TxConfig + clientCtx := client.Context{} + clientCtx = clientCtx.WithTxConfig(txGen) + + // generate some tx JSON + bldr := txGen.NewTxBuilder() + bldr.SetGasLimit(50000) + bldr.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("atom", 150))) + bldr.SetMemo("foomemo") + txJson, err := txGen.TxJSONEncoder()(bldr.GetTx()) + require.NoError(t, err) + + // use the tx JSON to generate some tx batches (it doesn't matter that we use the same JSON because we don't care about the actual context) + goodBatchOf3Txs := fmt.Sprintf("%s\n%s\n%s\n", txJson, txJson, txJson) + malformedBatch := fmt.Sprintf("%s\nmalformed\n%s\n", txJson, txJson) + batchOf2TxsWithNoNewline := fmt.Sprintf("%s\n%s", txJson, txJson) + batchWithEmptyLine := fmt.Sprintf("%s\n\n%s", txJson, txJson) + + tests := []struct { + name string + batch string + wantScannerError bool + wantUnmarshalError bool + numTxs int + }{ + {"good batch", goodBatchOf3Txs, false, false, 3}, + {"malformed", malformedBatch, false, true, 1}, + {"missing trailing newline", batchOf2TxsWithNoNewline, false, false, 2}, + {"empty line", batchWithEmptyLine, false, true, 1}, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + scanner, i := authclient.NewBatchScanner(clientCtx.TxConfig, strings.NewReader(tt.batch)), 0 + for scanner.Scan() { + _ = scanner.Tx() + i++ + } + require.Equal(t, tt.wantScannerError, scanner.Err() != nil) + require.Equal(t, tt.wantUnmarshalError, scanner.UnmarshalErr() != nil) + require.Equal(t, tt.numTxs, i) + }) + } +} + +func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder) { + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + tx := legacytx.NewStdTx(msgs, legacytx.StdFee{}, []legacytx.StdSignature{}, "") + + defaultEncoderBytes, err := expected(tx) + require.NoError(t, err) + encoderBytes, err := actual(tx) + require.NoError(t, err) + require.Equal(t, defaultEncoderBytes, encoderBytes) +} + +func makeCodec() *codec.LegacyAmino { + var cdc = codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + authtypes.RegisterLegacyAminoCodec(cdc) + cdc.RegisterConcrete(testdata.TestMsg{}, "cosmos-sdk/Test", nil) + return cdc +} diff --git a/x/auth/client/utils/errors.go b/x/auth/client/utils/errors.go deleted file mode 100644 index 0a0fc7a0f657..000000000000 --- a/x/auth/client/utils/errors.go +++ /dev/null @@ -1,8 +0,0 @@ -package utils - -import "errors" - -var ( - errInvalidSigner = errors.New("tx intended signer does not match the given signer") - errInvalidGasAdjustment = errors.New("invalid gas adjustment") -) diff --git a/x/auth/client/utils/query.go b/x/auth/client/utils/query.go deleted file mode 100644 index 6c2e5ff30688..000000000000 --- a/x/auth/client/utils/query.go +++ /dev/null @@ -1,180 +0,0 @@ -package utils - -import ( - "encoding/hex" - "errors" - "strings" - "time" - - ctypes "github.com/tendermint/tendermint/rpc/core/types" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// QueryTxsByEvents performs a search for transactions for a given set of events -// via the Tendermint RPC. An event takes the form of: -// "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is -// concatenated with an 'AND' operand. It returns a slice of Info object -// containing txs and metadata. An error is returned if the query fails. -func QueryTxsByEvents(cliCtx context.CLIContext, events []string, page, limit int) (*sdk.SearchTxsResult, error) { - if len(events) == 0 { - return nil, errors.New("must declare at least one event to search") - } - - if page <= 0 { - return nil, errors.New("page must greater than 0") - } - - if limit <= 0 { - return nil, errors.New("limit must greater than 0") - } - - // XXX: implement ANY - query := strings.Join(events, " AND ") - - node, err := cliCtx.GetNode() - if err != nil { - return nil, err - } - - prove := !cliCtx.TrustNode - - resTxs, err := node.TxSearch(query, prove, page, limit, "") - if err != nil { - return nil, err - } - - if prove { - for _, tx := range resTxs.Txs { - err := ValidateTxResult(cliCtx, tx) - if err != nil { - return nil, err - } - } - } - - resBlocks, err := getBlocksForTxResults(cliCtx, resTxs.Txs) - if err != nil { - return nil, err - } - - txs, err := formatTxResults(cliCtx.Codec, resTxs.Txs, resBlocks) - if err != nil { - return nil, err - } - - result := sdk.NewSearchTxsResult(resTxs.TotalCount, len(txs), page, limit, txs) - - return &result, nil -} - -// QueryTx queries for a single transaction by a hash string in hex format. An -// error is returned if the transaction does not exist or cannot be queried. -func QueryTx(cliCtx context.CLIContext, hashHexStr string) (sdk.TxResponse, error) { - hash, err := hex.DecodeString(hashHexStr) - if err != nil { - return sdk.TxResponse{}, err - } - - node, err := cliCtx.GetNode() - if err != nil { - return sdk.TxResponse{}, err - } - - resTx, err := node.Tx(hash, !cliCtx.TrustNode) - if err != nil { - return sdk.TxResponse{}, err - } - - if !cliCtx.TrustNode { - if err = ValidateTxResult(cliCtx, resTx); err != nil { - return sdk.TxResponse{}, err - } - } - - resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx}) - if err != nil { - return sdk.TxResponse{}, err - } - - out, err := formatTxResult(cliCtx.Codec, resTx, resBlocks[resTx.Height]) - if err != nil { - return out, err - } - - return out, nil -} - -// formatTxResults parses the indexed txs into a slice of TxResponse objects. -func formatTxResults(cdc *codec.Codec, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]sdk.TxResponse, error) { - var err error - out := make([]sdk.TxResponse, len(resTxs)) - for i := range resTxs { - out[i], err = formatTxResult(cdc, resTxs[i], resBlocks[resTxs[i].Height]) - if err != nil { - return nil, err - } - } - - return out, nil -} - -// ValidateTxResult performs transaction verification. -func ValidateTxResult(cliCtx context.CLIContext, resTx *ctypes.ResultTx) error { - if !cliCtx.TrustNode { - check, err := cliCtx.Verify(resTx.Height) - if err != nil { - return err - } - err = resTx.Proof.Validate(check.Header.DataHash) - if err != nil { - return err - } - } - return nil -} - -func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { - node, err := cliCtx.GetNode() - if err != nil { - return nil, err - } - - resBlocks := make(map[int64]*ctypes.ResultBlock) - - for _, resTx := range resTxs { - if _, ok := resBlocks[resTx.Height]; !ok { - resBlock, err := node.Block(&resTx.Height) - if err != nil { - return nil, err - } - - resBlocks[resTx.Height] = resBlock - } - } - - return resBlocks, nil -} - -func formatTxResult(cdc *codec.Codec, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) { - tx, err := parseTx(cdc, resTx.Tx) - if err != nil { - return sdk.TxResponse{}, err - } - - return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil -} - -func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { - var tx types.StdTx - - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) - if err != nil { - return nil, err - } - - return tx, nil -} diff --git a/x/auth/client/utils/rest.go b/x/auth/client/utils/rest.go deleted file mode 100644 index 9423fe629ee4..000000000000 --- a/x/auth/client/utils/rest.go +++ /dev/null @@ -1,67 +0,0 @@ -package utils - -import ( - "log" - "net/http" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/flags" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// WriteGenerateStdTxResponse writes response for the generate only mode. -func WriteGenerateStdTxResponse(w http.ResponseWriter, cliCtx context.CLIContext, br rest.BaseReq, msgs []sdk.Msg) { - gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment) - if !ok { - return - } - - simAndExec, gas, err := flags.ParseGas(br.Gas) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - txBldr := types.NewTxBuilder( - GetTxEncoder(cliCtx.Codec), br.AccountNumber, br.Sequence, gas, gasAdj, - br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, - ) - - if br.Simulate || simAndExec { - if gasAdj < 0 { - rest.WriteErrorResponse(w, http.StatusBadRequest, errInvalidGasAdjustment.Error()) - return - } - - txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if br.Simulate { - rest.WriteSimulationResponse(w, cliCtx.Codec, txBldr.Gas()) - return - } - } - - stdMsg, err := txBldr.BuildSignMsg(msgs) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - output, err := cliCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(output); err != nil { - log.Printf("could not write response: %v", err) - } - -} diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go deleted file mode 100644 index 55b7007002d9..000000000000 --- a/x/auth/client/utils/tx.go +++ /dev/null @@ -1,348 +0,0 @@ -package utils - -import ( - "bufio" - "bytes" - "fmt" - "io/ioutil" - "os" - - "github.com/pkg/errors" - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// GasEstimateResponse defines a response definition for tx gas estimation. -type GasEstimateResponse struct { - GasEstimate uint64 `json:"gas_estimate" yaml:"gas_estimate"` -} - -func (gr GasEstimateResponse) String() string { - return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) -} - -// GenerateOrBroadcastMsgs creates a StdTx given a series of messages. If -// the provided context has generate-only enabled, the tx will only be printed -// to STDOUT in a fully offline manner. Otherwise, the tx will be signed and -// broadcasted. -func GenerateOrBroadcastMsgs(cliCtx context.CLIContext, txBldr authtypes.TxBuilder, msgs []sdk.Msg) error { - if cliCtx.GenerateOnly { - return PrintUnsignedStdTx(txBldr, cliCtx, msgs) - } - - return CompleteAndBroadcastTxCLI(txBldr, cliCtx, msgs) -} - -// CompleteAndBroadcastTxCLI implements a utility function that facilitates -// sending a series of messages in a signed transaction given a TxBuilder and a -// QueryContext. It ensures that the account exists, has a proper number and -// sequence set. In addition, it builds and signs a transaction with the -// supplied messages. Finally, it broadcasts the signed transaction to a node. -func CompleteAndBroadcastTxCLI(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error { - txBldr, err := PrepareTxBuilder(txBldr, cliCtx) - if err != nil { - return err - } - - fromName := cliCtx.GetFromName() - - if txBldr.SimulateAndExecute() || cliCtx.Simulate { - txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) - if err != nil { - return err - } - - gasEst := GasEstimateResponse{GasEstimate: txBldr.Gas()} - _, _ = fmt.Fprintf(os.Stderr, "%s\n", gasEst.String()) - } - - if cliCtx.Simulate { - return nil - } - - if !cliCtx.SkipConfirm { - stdSignMsg, err := txBldr.BuildSignMsg(msgs) - if err != nil { - return err - } - - var json []byte - if viper.GetBool(flags.FlagIndentResponse) { - json, err = cliCtx.Codec.MarshalJSONIndent(stdSignMsg, "", " ") - if err != nil { - panic(err) - } - } else { - json = cliCtx.Codec.MustMarshalJSON(stdSignMsg) - } - - _, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json) - - buf := bufio.NewReader(os.Stdin) - ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf) - if err != nil || !ok { - _, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction") - return err - } - } - - // build and sign the transaction - txBytes, err := txBldr.BuildAndSign(fromName, keys.DefaultKeyPass, msgs) - if err != nil { - return err - } - - // broadcast to a Tendermint node - res, err := cliCtx.BroadcastTx(txBytes) - if err != nil { - return err - } - - return cliCtx.PrintOutput(res) -} - -// EnrichWithGas calculates the gas estimate that would be consumed by the -// transaction and set the transaction's respective value accordingly. -func EnrichWithGas(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (authtypes.TxBuilder, error) { - _, adjusted, err := simulateMsgs(txBldr, cliCtx, msgs) - if err != nil { - return txBldr, err - } - - return txBldr.WithGas(adjusted), nil -} - -// CalculateGas simulates the execution of a transaction and returns -// both the estimate obtained by the query and the adjusted amount. -func CalculateGas( - queryFunc func(string, []byte) ([]byte, int64, error), cdc *codec.Codec, - txBytes []byte, adjustment float64, -) (estimate, adjusted uint64, err error) { - - // run a simulation (via /app/simulate query) to - // estimate gas and update TxBuilder accordingly - rawRes, _, err := queryFunc("/app/simulate", txBytes) - if err != nil { - return estimate, adjusted, err - } - - estimate, err = parseQueryResponse(cdc, rawRes) - if err != nil { - return - } - - adjusted = adjustGasEstimate(estimate, adjustment) - return estimate, adjusted, nil -} - -// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. -func PrintUnsignedStdTx(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error { - stdTx, err := buildUnsignedStdTxOffline(txBldr, cliCtx, msgs) - if err != nil { - return err - } - - var json []byte - if viper.GetBool(flags.FlagIndentResponse) { - json, err = cliCtx.Codec.MarshalJSONIndent(stdTx, "", " ") - } else { - json, err = cliCtx.Codec.MarshalJSON(stdTx) - } - if err != nil { - return err - } - - _, _ = fmt.Fprintf(cliCtx.Output, "%s\n", json) - return nil -} - -// SignStdTx appends a signature to a StdTx and returns a copy of it. If appendSig -// is false, it replaces the signatures already attached with the new signature. -// Don't perform online validation or lookups if offline is true. -func SignStdTx( - txBldr authtypes.TxBuilder, cliCtx context.CLIContext, name string, - stdTx authtypes.StdTx, appendSig bool, offline bool, -) (authtypes.StdTx, error) { - - var signedStdTx authtypes.StdTx - - info, err := txBldr.Keybase().Get(name) - if err != nil { - return signedStdTx, err - } - - addr := info.GetPubKey().Address() - - // check whether the address is a signer - if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { - return signedStdTx, fmt.Errorf("%s: %s", errInvalidSigner, name) - } - - if !offline { - txBldr, err = populateAccountFromState(txBldr, cliCtx, sdk.AccAddress(addr)) - if err != nil { - return signedStdTx, err - } - } - - return txBldr.SignStdTx(name, keys.DefaultKeyPass, stdTx, appendSig) -} - -// SignStdTxWithSignerAddress attaches a signature to a StdTx and returns a copy of a it. -// Don't perform online validation or lookups if offline is true, else -// populate account and sequence numbers from a foreign account. -func SignStdTxWithSignerAddress(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, - addr sdk.AccAddress, name string, stdTx authtypes.StdTx, - offline bool) (signedStdTx authtypes.StdTx, err error) { - - // check whether the address is a signer - if !isTxSigner(addr, stdTx.GetSigners()) { - return signedStdTx, fmt.Errorf("%s: %s", errInvalidSigner, name) - } - - if !offline { - txBldr, err = populateAccountFromState(txBldr, cliCtx, addr) - if err != nil { - return signedStdTx, err - } - } - - return txBldr.SignStdTx(name, keys.DefaultKeyPass, stdTx, false) -} - -// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin. -func ReadStdTxFromFile(cdc *codec.Codec, filename string) (stdTx authtypes.StdTx, err error) { - var bytes []byte - - if filename == "-" { - bytes, err = ioutil.ReadAll(os.Stdin) - } else { - bytes, err = ioutil.ReadFile(filename) - } - - if err != nil { - return - } - - if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil { - return - } - - return -} - -func populateAccountFromState( - txBldr authtypes.TxBuilder, cliCtx context.CLIContext, addr sdk.AccAddress, -) (authtypes.TxBuilder, error) { - - num, seq, err := authtypes.NewAccountRetriever(cliCtx).GetAccountNumberSequence(addr) - if err != nil { - return txBldr, err - } - - return txBldr.WithAccountNumber(num).WithSequence(seq), nil -} - -// GetTxEncoder return tx encoder from global sdk configuration if ones is defined. -// Otherwise returns encoder with default logic. -func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) { - encoder = sdk.GetConfig().GetTxEncoder() - if encoder == nil { - encoder = authtypes.DefaultTxEncoder(cdc) - } - - return encoder -} - -// nolint -// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { - txBytes, err := txBldr.BuildTxForSim(msgs) - if err != nil { - return - } - - estimated, adjusted, err = CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment()) - return -} - -func adjustGasEstimate(estimate uint64, adjustment float64) uint64 { - return uint64(adjustment * float64(estimate)) -} - -func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (uint64, error) { - var gasUsed uint64 - if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &gasUsed); err != nil { - return 0, err - } - - return gasUsed, nil -} - -// PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx. -func PrepareTxBuilder(txBldr authtypes.TxBuilder, cliCtx context.CLIContext) (authtypes.TxBuilder, error) { - from := cliCtx.GetFromAddress() - - accGetter := authtypes.NewAccountRetriever(cliCtx) - if err := accGetter.EnsureExists(from); err != nil { - return txBldr, err - } - - txbldrAccNum, txbldrAccSeq := txBldr.AccountNumber(), txBldr.Sequence() - // TODO: (ref #1903) Allow for user supplied account number without - // automatically doing a manual lookup. - if txbldrAccNum == 0 || txbldrAccSeq == 0 { - num, seq, err := authtypes.NewAccountRetriever(cliCtx).GetAccountNumberSequence(from) - if err != nil { - return txBldr, err - } - - if txbldrAccNum == 0 { - txBldr = txBldr.WithAccountNumber(num) - } - if txbldrAccSeq == 0 { - txBldr = txBldr.WithSequence(seq) - } - } - - return txBldr, nil -} - -func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx authtypes.StdTx, err error) { - if txBldr.SimulateAndExecute() { - if cliCtx.GenerateOnly { - return stdTx, errors.New("cannot estimate gas with generate-only") - } - - txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) - if err != nil { - return stdTx, err - } - - _, _ = fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas()) - } - - stdSignMsg, err := txBldr.BuildSignMsg(msgs) - if err != nil { - return stdTx, err - } - - return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil -} - -func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { - for _, s := range signers { - if bytes.Equal(user.Bytes(), s.Bytes()) { - return true - } - } - - return false -} diff --git a/x/auth/client/utils/tx_test.go b/x/auth/client/utils/tx_test.go deleted file mode 100644 index a9c33b232053..000000000000 --- a/x/auth/client/utils/tx_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package utils - -import ( - "encoding/json" - "errors" - "io/ioutil" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -var ( - priv = ed25519.GenPrivKey() - addr = sdk.AccAddress(priv.PubKey().Address()) -) - -func TestParseQueryResponse(t *testing.T) { - cdc := makeCodec() - sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(uint64(10)) - gas, err := parseQueryResponse(cdc, sdkResBytes) - assert.Equal(t, gas, uint64(10)) - assert.Nil(t, err) - gas, err = parseQueryResponse(cdc, []byte("fuzzy")) - assert.Equal(t, gas, uint64(0)) - assert.Error(t, err) -} - -func TestCalculateGas(t *testing.T) { - cdc := makeCodec() - makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) { - return func(string, []byte) ([]byte, int64, error) { - if wantErr { - return nil, 0, errors.New("") - } - return cdc.MustMarshalBinaryLengthPrefixed(gasUsed), 0, nil - } - } - - type args struct { - queryFuncGasUsed uint64 - queryFuncWantErr bool - adjustment float64 - } - - tests := []struct { - name string - args args - wantEstimate uint64 - wantAdjusted uint64 - wantErr bool - }{ - {"error", args{0, true, 1.2}, 0, 0, true}, - {"adjusted gas", args{10, false, 1.2}, 10, 12, false}, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - queryFunc := makeQueryFunc(tt.args.queryFuncGasUsed, tt.args.queryFuncWantErr) - gotEstimate, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) - assert.Equal(t, err != nil, tt.wantErr) - assert.Equal(t, gotEstimate, tt.wantEstimate) - assert.Equal(t, gotAdjusted, tt.wantAdjusted) - }) - } -} - -func TestDefaultTxEncoder(t *testing.T) { - cdc := makeCodec() - - defaultEncoder := authtypes.DefaultTxEncoder(cdc) - encoder := GetTxEncoder(cdc) - - compareEncoders(t, defaultEncoder, encoder) -} - -func TestConfiguredTxEncoder(t *testing.T) { - cdc := makeCodec() - - customEncoder := func(tx sdk.Tx) ([]byte, error) { - return json.Marshal(tx) - } - - config := sdk.GetConfig() - config.SetTxEncoder(customEncoder) - - encoder := GetTxEncoder(cdc) - - compareEncoders(t, customEncoder, encoder) -} - -func TestReadStdTxFromFile(t *testing.T) { - cdc := codec.New() - sdk.RegisterCodec(cdc) - - // Build a test transaction - fee := authtypes.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) - stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo") - - // Write it to the file - encodedTx, _ := cdc.MarshalJSON(stdTx) - jsonTxFile := writeToNewTempFile(t, string(encodedTx)) - defer os.Remove(jsonTxFile.Name()) - - // Read it back - decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name()) - require.NoError(t, err) - require.Equal(t, decodedTx.Memo, "foomemo") -} - -func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder) { - msgs := []sdk.Msg{sdk.NewTestMsg(addr)} - tx := authtypes.NewStdTx(msgs, authtypes.StdFee{}, []authtypes.StdSignature{}, "") - - defaultEncoderBytes, err := expected(tx) - require.NoError(t, err) - encoderBytes, err := actual(tx) - require.NoError(t, err) - require.Equal(t, defaultEncoderBytes, encoderBytes) -} - -func writeToNewTempFile(t *testing.T, data string) *os.File { - fp, err := ioutil.TempFile(os.TempDir(), "client_tx_test") - require.NoError(t, err) - - _, err = fp.WriteString(data) - require.NoError(t, err) - - return fp -} - -func makeCodec() *codec.Codec { - var cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - authtypes.RegisterCodec(cdc) - cdc.RegisterConcrete(sdk.TestMsg{}, "cosmos-sdk/Test", nil) - return cdc -} diff --git a/x/auth/exported/exported.go b/x/auth/exported/exported.go deleted file mode 100644 index fe4fb16d8336..000000000000 --- a/x/auth/exported/exported.go +++ /dev/null @@ -1,60 +0,0 @@ -package exported - -import ( - "time" - - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Account is an interface used to store coins at a given address within state. -// It presumes a notion of sequence numbers for replay protection, -// a notion of account numbers for replay protection for previously pruned accounts, -// and a pubkey for authentication purposes. -// -// Many complex conditions can be used in the concrete struct which implements Account. -type Account interface { - GetAddress() sdk.AccAddress - SetAddress(sdk.AccAddress) error // errors if already set. - - GetPubKey() crypto.PubKey // can return nil. - SetPubKey(crypto.PubKey) error - - GetAccountNumber() uint64 - SetAccountNumber(uint64) error - - GetSequence() uint64 - SetSequence(uint64) error - - GetCoins() sdk.Coins - SetCoins(sdk.Coins) error - - // Calculates the amount of coins that can be sent to other accounts given - // the current time. - SpendableCoins(blockTime time.Time) sdk.Coins - - // Ensure that account implements stringer - String() string -} - -// GenesisAccounts defines a slice of GenesisAccount objects -type GenesisAccounts []GenesisAccount - -// Contains returns true if the given address exists in a slice of GenesisAccount -// objects. -func (ga GenesisAccounts) Contains(addr sdk.Address) bool { - for _, acc := range ga { - if acc.GetAddress().Equals(addr) { - return true - } - } - - return false -} - -// GenesisAccount defines a genesis account that embeds an Account with validation capabilities. -type GenesisAccount interface { - Account - Validate() error -} diff --git a/x/auth/exported/exported_test.go b/x/auth/exported/exported_test.go deleted file mode 100644 index 8eaab048a8ae..000000000000 --- a/x/auth/exported/exported_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package exported_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -func TestGenesisAccountsContains(t *testing.T) { - pubkey := secp256k1.GenPrivKey().PubKey() - addr := sdk.AccAddress(pubkey.Address()) - acc := authtypes.NewBaseAccount(addr, nil, secp256k1.GenPrivKey().PubKey(), 0, 0) - - genAccounts := exported.GenesisAccounts{} - require.False(t, genAccounts.Contains(acc.GetAddress())) - - genAccounts = append(genAccounts, acc) - require.True(t, genAccounts.Contains(acc.GetAddress())) -} diff --git a/x/auth/genesis.go b/x/auth/genesis.go index 22db3557d7f7..851b588083d4 100644 --- a/x/auth/genesis.go +++ b/x/auth/genesis.go @@ -2,33 +2,41 @@ package auth import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) // InitGenesis - Init store state from genesis data // // CONTRACT: old coins from the FeeCollectionKeeper need to be transferred through // a genesis port script to the new fee collector account -func InitGenesis(ctx sdk.Context, ak AccountKeeper, data GenesisState) { +func InitGenesis(ctx sdk.Context, ak keeper.AccountKeeper, data types.GenesisState) { ak.SetParams(ctx, data.Params) - data.Accounts = SanitizeGenesisAccounts(data.Accounts) - for _, a := range data.Accounts { + accounts, err := types.UnpackAccounts(data.Accounts) + if err != nil { + panic(err) + } + accounts = types.SanitizeGenesisAccounts(accounts) + + for _, a := range accounts { acc := ak.NewAccount(ctx, a) ak.SetAccount(ctx, acc) } + + ak.GetModuleAccount(ctx, types.FeeCollectorName) } // ExportGenesis returns a GenesisState for a given context and keeper -func ExportGenesis(ctx sdk.Context, ak AccountKeeper) GenesisState { +func ExportGenesis(ctx sdk.Context, ak keeper.AccountKeeper) *types.GenesisState { params := ak.GetParams(ctx) - var genAccounts exported.GenesisAccounts - ak.IterateAccounts(ctx, func(account exported.Account) bool { - genAccount := account.(exported.GenesisAccount) + var genAccounts types.GenesisAccounts + ak.IterateAccounts(ctx, func(account types.AccountI) bool { + genAccount := account.(types.GenesisAccount) genAccounts = append(genAccounts, genAccount) return false }) - return NewGenesisState(params, genAccounts) + return types.NewGenesisState(params, genAccounts) } diff --git a/x/auth/keeper/account.go b/x/auth/keeper/account.go index 8417a4a34d2b..9921bb96f945 100644 --- a/x/auth/keeper/account.go +++ b/x/auth/keeper/account.go @@ -2,70 +2,73 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// NewAccountWithAddress implements sdk.AccountKeeper. -func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) exported.Account { +// NewAccountWithAddress implements AccountKeeperI. +func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) types.AccountI { acc := ak.proto() err := acc.SetAddress(addr) if err != nil { panic(err) } + return ak.NewAccount(ctx, acc) } // NewAccount sets the next account number to a given account interface -func (ak AccountKeeper) NewAccount(ctx sdk.Context, acc exported.Account) exported.Account { +func (ak AccountKeeper) NewAccount(ctx sdk.Context, acc types.AccountI) types.AccountI { if err := acc.SetAccountNumber(ak.GetNextAccountNumber(ctx)); err != nil { panic(err) } + return acc } -// GetAccount implements sdk.AccountKeeper. -func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) exported.Account { +// GetAccount implements AccountKeeperI. +func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI { store := ctx.KVStore(ak.key) bz := store.Get(types.AddressStoreKey(addr)) if bz == nil { return nil } - acc := ak.decodeAccount(bz) - return acc + + return ak.decodeAccount(bz) } // GetAllAccounts returns all accounts in the accountKeeper. -func (ak AccountKeeper) GetAllAccounts(ctx sdk.Context) (accounts []exported.Account) { - ak.IterateAccounts(ctx, - func(acc exported.Account) (stop bool) { - accounts = append(accounts, acc) - return false - }) +func (ak AccountKeeper) GetAllAccounts(ctx sdk.Context) (accounts []types.AccountI) { + ak.IterateAccounts(ctx, func(acc types.AccountI) (stop bool) { + accounts = append(accounts, acc) + return false + }) + return accounts } -// SetAccount implements sdk.AccountKeeper. -func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc exported.Account) { +// SetAccount implements AccountKeeperI. +func (ak AccountKeeper) SetAccount(ctx sdk.Context, acc types.AccountI) { addr := acc.GetAddress() store := ctx.KVStore(ak.key) - bz, err := ak.cdc.MarshalBinaryBare(acc) + + bz, err := ak.MarshalAccount(acc) if err != nil { panic(err) } + store.Set(types.AddressStoreKey(addr), bz) } // RemoveAccount removes an account for the account mapper store. // NOTE: this will cause supply invariant violation if called -func (ak AccountKeeper) RemoveAccount(ctx sdk.Context, acc exported.Account) { +func (ak AccountKeeper) RemoveAccount(ctx sdk.Context, acc types.AccountI) { addr := acc.GetAddress() store := ctx.KVStore(ak.key) store.Delete(types.AddressStoreKey(addr)) } // IterateAccounts iterates over all the stored accounts and performs a callback function -func (ak AccountKeeper) IterateAccounts(ctx sdk.Context, cb func(account exported.Account) (stop bool)) { +func (ak AccountKeeper) IterateAccounts(ctx sdk.Context, cb func(account types.AccountI) (stop bool)) { store := ctx.KVStore(ak.key) iterator := sdk.KVStorePrefixIterator(store, types.AddressStoreKeyPrefix) diff --git a/x/auth/keeper/grpc_query.go b/x/auth/keeper/grpc_query.go new file mode 100644 index 000000000000..cf3c7b844362 --- /dev/null +++ b/x/auth/keeper/grpc_query.go @@ -0,0 +1,54 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var _ types.QueryServer = AccountKeeper{} + +// Account returns account details based on address +func (ak AccountKeeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.Address == "" { + return nil, status.Error(codes.InvalidArgument, "Address cannot be empty") + } + + ctx := sdk.UnwrapSDKContext(c) + addr, err := sdk.AccAddressFromBech32(req.Address) + + if err != nil { + return nil, err + } + account := ak.GetAccount(ctx, addr) + if account == nil { + return nil, status.Errorf(codes.NotFound, "account %s not found", req.Address) + } + + any, err := codectypes.NewAnyWithValue(account) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &types.QueryAccountResponse{Account: any}, nil +} + +// Params returns parameters of auth module +func (ak AccountKeeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(c) + params := ak.GetParams(ctx) + + return &types.QueryParamsResponse{Params: params}, nil +} diff --git a/x/auth/keeper/grpc_query_test.go b/x/auth/keeper/grpc_query_test.go new file mode 100644 index 000000000000..47c48f027629 --- /dev/null +++ b/x/auth/keeper/grpc_query_test.go @@ -0,0 +1,135 @@ +package keeper_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func (suite *KeeperTestSuite) TestGRPCQueryAccount() { + var ( + req *types.QueryAccountRequest + ) + _, _, addr := testdata.KeyTestPubAddr() + + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func(res *types.QueryAccountResponse) + }{ + { + "empty request", + func() { + req = &types.QueryAccountRequest{} + }, + false, + func(res *types.QueryAccountResponse) {}, + }, + { + "invalid request", + func() { + req = &types.QueryAccountRequest{Address: ""} + }, + false, + func(res *types.QueryAccountResponse) {}, + }, + { + "invalid request with empty byte array", + func() { + req = &types.QueryAccountRequest{Address: ""} + }, + false, + func(res *types.QueryAccountResponse) {}, + }, + { + "account not found", + func() { + req = &types.QueryAccountRequest{Address: addr.String()} + }, + false, + func(res *types.QueryAccountResponse) {}, + }, + { + "success", + func() { + suite.app.AccountKeeper.SetAccount(suite.ctx, + suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr)) + req = &types.QueryAccountRequest{Address: addr.String()} + }, + true, + func(res *types.QueryAccountResponse) { + var newAccount types.AccountI + err := suite.app.InterfaceRegistry().UnpackAny(res.Account, &newAccount) + suite.Require().NoError(err) + suite.Require().NotNil(newAccount) + suite.Require().True(addr.Equals(newAccount.GetAddress())) + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.ctx) + + res, err := suite.queryClient.Account(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + + tc.posttests(res) + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryParameters() { + var ( + req *types.QueryParamsRequest + expParams types.Params + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "success", + func() { + req = &types.QueryParamsRequest{} + expParams = suite.app.AccountKeeper.GetParams(suite.ctx) + }, + 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.Params(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expParams, res.Params) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +} diff --git a/x/auth/keeper/integration_test.go b/x/auth/keeper/integration_test.go index 0b7714453071..d2069d6d3586 100644 --- a/x/auth/keeper/integration_test.go +++ b/x/auth/keeper/integration_test.go @@ -1,7 +1,7 @@ package keeper_test import ( - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,7 +11,7 @@ import ( // 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, abci.Header{}) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) return app, ctx diff --git a/x/auth/keeper/keeper.go b/x/auth/keeper/keeper.go index 244dec7c193b..92d9f9b76ecb 100644 --- a/x/auth/keeper/keeper.go +++ b/x/auth/keeper/keeper.go @@ -3,58 +3,99 @@ package keeper import ( "fmt" - "github.com/tendermint/tendermint/crypto" + gogotypes "github.com/gogo/protobuf/types" "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/params/subspace" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) +// AccountKeeperI is the interface contract that x/auth's keeper implements. +type AccountKeeperI interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI + + // Return a new account with the next account number. Does not save the new account to the store. + NewAccount(sdk.Context, types.AccountI) types.AccountI + + // Retrieve an account from the store. + GetAccount(sdk.Context, sdk.AccAddress) types.AccountI + + // Set an account in the store. + SetAccount(sdk.Context, types.AccountI) + + // Remove an account from the store. + RemoveAccount(sdk.Context, types.AccountI) + + // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. + IterateAccounts(sdk.Context, func(types.AccountI) bool) + + // Fetch the public key of an account at a specified address + GetPubKey(sdk.Context, sdk.AccAddress) (cryptotypes.PubKey, error) + + // Fetch the sequence of an account at a specified address. + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) + + // Fetch the next account number, and increment the internal counter. + GetNextAccountNumber(sdk.Context) uint64 +} + // AccountKeeper encodes/decodes accounts using the go-amino (binary) // encoding/decoding library. type AccountKeeper struct { - // The (unexposed) key used to access the store from the Context. - key sdk.StoreKey - - // The prototypical Account constructor. - proto func() exported.Account + key sdk.StoreKey + cdc codec.BinaryMarshaler + paramSubspace paramtypes.Subspace + permAddrs map[string]types.PermissionsForAddress - // The codec codec for binary encoding/decoding of accounts. - cdc *codec.Codec - - paramSubspace subspace.Subspace + // The prototypical AccountI constructor. + proto func() types.AccountI } -// NewAccountKeeper returns a new sdk.AccountKeeper that uses go-amino to +var _ AccountKeeperI = &AccountKeeper{} + +// NewAccountKeeper returns a new AccountKeeperI that uses go-amino to // (binary) encode and decode concrete sdk.Accounts. -// nolint func NewAccountKeeper( - cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, proto func() exported.Account, + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramstore paramtypes.Subspace, proto func() types.AccountI, + maccPerms map[string][]string, ) AccountKeeper { + // set KeyTable if it has not already been set + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } + + permAddrs := make(map[string]types.PermissionsForAddress) + for name, perms := range maccPerms { + permAddrs[name] = types.NewPermissionsForAddress(name, perms) + } + return AccountKeeper{ key: key, proto: proto, cdc: cdc, - paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()), + paramSubspace: paramstore, + permAddrs: permAddrs, } } // Logger returns a module-specific logger. func (ak AccountKeeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) + return ctx.Logger().With("module", "x/"+types.ModuleName) } // GetPubKey Returns the PubKey of the account at address -func (ak AccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (crypto.PubKey, error) { +func (ak AccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) { acc := ak.GetAccount(ctx, addr) if acc == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) } + return acc.GetPubKey(), nil } @@ -64,6 +105,7 @@ func (ak AccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (uint6 if acc == nil { return 0, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) } + return acc.GetSequence(), nil } @@ -72,30 +114,118 @@ func (ak AccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (uint6 func (ak AccountKeeper) GetNextAccountNumber(ctx sdk.Context) uint64 { var accNumber uint64 store := ctx.KVStore(ak.key) + bz := store.Get(types.GlobalAccountNumberKey) if bz == nil { // initialize the account numbers accNumber = 0 } else { - err := ak.cdc.UnmarshalBinaryLengthPrefixed(bz, &accNumber) + val := gogotypes.UInt64Value{} + + err := ak.cdc.UnmarshalBinaryBare(bz, &val) if err != nil { panic(err) } + + accNumber = val.GetValue() } - bz = ak.cdc.MustMarshalBinaryLengthPrefixed(accNumber + 1) + bz = ak.cdc.MustMarshalBinaryBare(&gogotypes.UInt64Value{Value: accNumber + 1}) store.Set(types.GlobalAccountNumberKey, bz) return accNumber } -// ----------------------------------------------------------------------------- -// Misc. +// ValidatePermissions validates that the module account has been granted +// permissions within its set of allowed permissions. +func (ak AccountKeeper) ValidatePermissions(macc types.ModuleAccountI) error { + permAddr := ak.permAddrs[macc.GetName()] + for _, perm := range macc.GetPermissions() { + if !permAddr.HasPermission(perm) { + return fmt.Errorf("invalid module permission %s", perm) + } + } + + return nil +} -func (ak AccountKeeper) decodeAccount(bz []byte) (acc exported.Account) { - err := ak.cdc.UnmarshalBinaryBare(bz, &acc) +// GetModuleAddress returns an address based on the module name +func (ak AccountKeeper) GetModuleAddress(moduleName string) sdk.AccAddress { + permAddr, ok := ak.permAddrs[moduleName] + if !ok { + return nil + } + + return permAddr.GetAddress() +} + +// GetModuleAddressAndPermissions returns an address and permissions based on the module name +func (ak AccountKeeper) GetModuleAddressAndPermissions(moduleName string) (addr sdk.AccAddress, permissions []string) { + permAddr, ok := ak.permAddrs[moduleName] + if !ok { + return addr, permissions + } + + return permAddr.GetAddress(), permAddr.GetPermissions() +} + +// GetModuleAccountAndPermissions gets the module account from the auth account store and its +// registered permissions +func (ak AccountKeeper) GetModuleAccountAndPermissions(ctx sdk.Context, moduleName string) (types.ModuleAccountI, []string) { + addr, perms := ak.GetModuleAddressAndPermissions(moduleName) + if addr == nil { + return nil, []string{} + } + + acc := ak.GetAccount(ctx, addr) + if acc != nil { + macc, ok := acc.(types.ModuleAccountI) + if !ok { + panic("account is not a module account") + } + return macc, perms + } + + // create a new module account + macc := types.NewEmptyModuleAccount(moduleName, perms...) + maccI := (ak.NewAccount(ctx, macc)).(types.ModuleAccountI) // set the account number + ak.SetModuleAccount(ctx, maccI) + + return maccI, perms +} + +// GetModuleAccount gets the module account from the auth account store, if the account does not +// exist in the AccountKeeper, then it is created. +func (ak AccountKeeper) GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI { + acc, _ := ak.GetModuleAccountAndPermissions(ctx, moduleName) + return acc +} + +// SetModuleAccount sets the module account to the auth account store +func (ak AccountKeeper) SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) { //nolint:interfacer + ak.SetAccount(ctx, macc) +} + +func (ak AccountKeeper) decodeAccount(bz []byte) types.AccountI { + acc, err := ak.UnmarshalAccount(bz) if err != nil { panic(err) } - return + + return acc +} + +// MarshalAccount protobuf serializes an Account interface +func (ak AccountKeeper) MarshalAccount(accountI types.AccountI) ([]byte, error) { // nolint:interfacer + return ak.cdc.MarshalInterface(accountI) } + +// UnmarshalAccount returns an Account interface from raw encoded account +// bytes of a Proto-based Account type +func (ak AccountKeeper) UnmarshalAccount(bz []byte) (types.AccountI, error) { + var acc types.AccountI + return acc, ak.cdc.UnmarshalInterface(bz, &acc) +} + +// GetCodec return codec.Marshaler object used by the keeper +func (ak AccountKeeper) GetCodec() codec.BinaryMarshaler { return ak.cdc } diff --git a/x/auth/keeper/keeper_bench_test.go b/x/auth/keeper/keeper_bench_test.go index 818c36a7a949..e057db4aefbf 100644 --- a/x/auth/keeper/keeper_bench_test.go +++ b/x/auth/keeper/keeper_bench_test.go @@ -24,35 +24,8 @@ func BenchmarkAccountMapperGetAccountFound(b *testing.B) { } } -func BenchmarkAccountMapperGetAccountFoundWithCoins(b *testing.B) { - app, ctx := createTestApp(false) - - coins := sdk.Coins{ - sdk.NewCoin("ltc", sdk.NewInt(1000)), - sdk.NewCoin("btc", sdk.NewInt(1000)), - sdk.NewCoin("eth", sdk.NewInt(1000)), - sdk.NewCoin("xrp", sdk.NewInt(1000)), - sdk.NewCoin("bch", sdk.NewInt(1000)), - sdk.NewCoin("eos", sdk.NewInt(1000)), - } - - // assumes b.N < 2**24 - for i := 0; i < b.N; i++ { - arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} - addr := sdk.AccAddress(arr) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - acc.SetCoins(coins) - app.AccountKeeper.SetAccount(ctx, acc) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} - app.AccountKeeper.GetAccount(ctx, sdk.AccAddress(arr)) - } -} - func BenchmarkAccountMapperSetAccount(b *testing.B) { + b.ReportAllocs() app, ctx := createTestApp(false) b.ResetTimer() @@ -65,27 +38,3 @@ func BenchmarkAccountMapperSetAccount(b *testing.B) { app.AccountKeeper.SetAccount(ctx, acc) } } - -func BenchmarkAccountMapperSetAccountWithCoins(b *testing.B) { - app, ctx := createTestApp(false) - - coins := sdk.Coins{ - sdk.NewCoin("ltc", sdk.NewInt(1000)), - sdk.NewCoin("btc", sdk.NewInt(1000)), - sdk.NewCoin("eth", sdk.NewInt(1000)), - sdk.NewCoin("xrp", sdk.NewInt(1000)), - sdk.NewCoin("bch", sdk.NewInt(1000)), - sdk.NewCoin("eos", sdk.NewInt(1000)), - } - - b.ResetTimer() - - // assumes b.N < 2**24 - for i := 0; i < b.N; i++ { - arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)} - addr := sdk.AccAddress(arr) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - acc.SetCoins(coins) - app.AccountKeeper.SetAccount(ctx, acc) - } -} diff --git a/x/auth/keeper/keeper_test.go b/x/auth/keeper/keeper_test.go index 8f15f3d93668..87744d58c5bb 100644 --- a/x/auth/keeper/keeper_test.go +++ b/x/auth/keeper/keeper_test.go @@ -4,14 +4,50 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/types" ) +const ( + holder = "holder" + multiPerm = "multiple permissions account" + randomPerm = "random permission" +) + +var ( + multiPermAcc = types.NewEmptyModuleAccount(multiPerm, types.Burner, types.Minter, types.Staking) + randomPermAcc = types.NewEmptyModuleAccount(randomPerm, "random") +) + +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + + queryClient types.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.app, suite.ctx = createTestApp(true) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.app.AccountKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + func TestAccountMapperGetSet(t *testing.T) { app, ctx := createTestApp(true) - addr := sdk.AccAddress([]byte("some-address")) + addr := sdk.AccAddress([]byte("some---------address")) // no account before its created acc := app.AccountKeeper.GetAccount(ctx, addr) @@ -29,7 +65,8 @@ func TestAccountMapperGetSet(t *testing.T) { // set some values on the account and save it newSequence := uint64(20) - acc.SetSequence(newSequence) + err := acc.SetSequence(newSequence) + require.NoError(t, err) app.AccountKeeper.SetAccount(ctx, acc) // check the new values @@ -40,8 +77,8 @@ func TestAccountMapperGetSet(t *testing.T) { func TestAccountMapperRemoveAccount(t *testing.T) { app, ctx := createTestApp(true) - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) + addr1 := sdk.AccAddress([]byte("addr1---------------")) + addr2 := sdk.AccAddress([]byte("addr2---------------")) // create accounts acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) @@ -50,8 +87,10 @@ func TestAccountMapperRemoveAccount(t *testing.T) { accSeq1 := uint64(20) accSeq2 := uint64(40) - acc1.SetSequence(accSeq1) - acc2.SetSequence(accSeq2) + err := acc1.SetSequence(accSeq1) + require.NoError(t, err) + err = acc2.SetSequence(accSeq2) + require.NoError(t, err) app.AccountKeeper.SetAccount(ctx, acc1) app.AccountKeeper.SetAccount(ctx, acc2) @@ -78,3 +117,32 @@ func TestGetSetParams(t *testing.T) { actualParams := app.AccountKeeper.GetParams(ctx) require.Equal(t, params, actualParams) } + +func TestSupply_ValidatePermissions(t *testing.T) { + app, _ := createTestApp(true) + + // add module accounts to supply keeper + maccPerms := simapp.GetMaccPerms() + maccPerms[holder] = nil + maccPerms[types.Burner] = []string{types.Burner} + maccPerms[types.Minter] = []string{types.Minter} + maccPerms[multiPerm] = []string{types.Burner, types.Minter, types.Staking} + maccPerms[randomPerm] = []string{"random"} + + cdc, _ := simapp.MakeCodecs() + keeper := keeper.NewAccountKeeper( + cdc, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), + types.ProtoBaseAccount, maccPerms, + ) + + err := keeper.ValidatePermissions(multiPermAcc) + require.NoError(t, err) + + err = keeper.ValidatePermissions(randomPermAcc) + require.NoError(t, err) + + // unregistered permissions + otherAcc := types.NewEmptyModuleAccount("other", "other") + err = app.AccountKeeper.ValidatePermissions(otherAcc) + require.Error(t, err) +} diff --git a/x/auth/keeper/querier.go b/x/auth/keeper/querier.go index 28e505970b45..130aaacda9e9 100644 --- a/x/auth/keeper/querier.go +++ b/x/auth/keeper/querier.go @@ -10,32 +10,52 @@ import ( ) // NewQuerier creates a querier for auth REST endpoints -func NewQuerier(keeper AccountKeeper) sdk.Querier { +func NewQuerier(k AccountKeeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { switch path[0] { case types.QueryAccount: - return queryAccount(ctx, req, keeper) + return queryAccount(ctx, req, k, legacyQuerierCdc) + + case types.QueryParams: + return queryParams(ctx, k, legacyQuerierCdc) + default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) } } } -func queryAccount(ctx sdk.Context, req abci.RequestQuery, keeper AccountKeeper) ([]byte, error) { - var params types.QueryAccountParams - if err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { +func queryAccount(ctx sdk.Context, req abci.RequestQuery, k AccountKeeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryAccountRequest + if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - account := keeper.GetAccount(ctx, params.Address) + addr, err := sdk.AccAddressFromBech32(params.Address) + if err != nil { + return nil, err + } + + account := k.GetAccount(ctx, addr) if account == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", params.Address) } - bz, err := codec.MarshalJSONIndent(keeper.cdc, account) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, account) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } + +func queryParams(ctx sdk.Context, k AccountKeeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + params := k.GetParams(ctx) + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} diff --git a/x/auth/keeper/querier_test.go b/x/auth/keeper/querier_test.go index 0306965c8a63..b64f971b3c6c 100644 --- a/x/auth/keeper/querier_test.go +++ b/x/auth/keeper/querier_test.go @@ -4,18 +4,20 @@ import ( "fmt" "testing" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/testutil/testdata" keep "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestQueryAccount(t *testing.T) { app, ctx := createTestApp(true) - cdc := app.Codec() + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) req := abci.RequestQuery{ Path: "", @@ -23,7 +25,7 @@ func TestQueryAccount(t *testing.T) { } path := []string{types.QueryAccount} - querier := keep.NewQuerier(app.AccountKeeper) + querier := keep.NewQuerier(app.AccountKeeper, legacyQuerierCdc.LegacyAmino) bz, err := querier(ctx, []string{"other"}, req) require.Error(t, err) @@ -37,13 +39,13 @@ func TestQueryAccount(t *testing.T) { require.Error(t, err) require.Nil(t, res) - req.Data = cdc.MustMarshalJSON(types.NewQueryAccountParams([]byte(""))) + req.Data = legacyQuerierCdc.MustMarshalJSON(&types.QueryAccountRequest{Address: ""}) res, err = querier(ctx, path, req) require.Error(t, err) require.Nil(t, res) - _, _, addr := types.KeyTestPubAddr() - req.Data = cdc.MustMarshalJSON(types.NewQueryAccountParams(addr)) + _, _, addr := testdata.KeyTestPubAddr() + req.Data = legacyQuerierCdc.MustMarshalJSON(&types.QueryAccountRequest{Address: addr.String()}) res, err = querier(ctx, path, req) require.Error(t, err) require.Nil(t, res) @@ -57,7 +59,7 @@ func TestQueryAccount(t *testing.T) { require.NoError(t, err) require.NotNil(t, res) - var account exported.Account - err2 := cdc.UnmarshalJSON(res, &account) + var account types.AccountI + err2 := legacyQuerierCdc.LegacyAmino.UnmarshalJSON(res, &account) require.Nil(t, err2) } diff --git a/x/auth/legacy/legacytx/amino_signing.go b/x/auth/legacy/legacytx/amino_signing.go new file mode 100644 index 000000000000..d09b1b30ac47 --- /dev/null +++ b/x/auth/legacy/legacytx/amino_signing.go @@ -0,0 +1,91 @@ +package legacytx + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// stdTxSignModeHandler is a SignModeHandler that handles SIGN_MODE_LEGACY_AMINO_JSON +type stdTxSignModeHandler struct{} + +func NewStdTxSignModeHandler() signing.SignModeHandler { + return &stdTxSignModeHandler{} +} + +// assert interface implementation +var _ signing.SignModeHandler = stdTxSignModeHandler{} + +// DefaultMode implements SignModeHandler.DefaultMode +func (h stdTxSignModeHandler) DefaultMode() signingtypes.SignMode { + return signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON +} + +// Modes implements SignModeHandler.Modes +func (stdTxSignModeHandler) Modes() []signingtypes.SignMode { + return []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON} +} + +// DefaultMode implements SignModeHandler.GetSignBytes +func (stdTxSignModeHandler) GetSignBytes(mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) { + if mode != signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON { + return nil, fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode) + } + + stdTx, ok := tx.(StdTx) + if !ok { + return nil, fmt.Errorf("expected %T, got %T", StdTx{}, tx) + } + + return StdSignBytes( + data.ChainID, data.AccountNumber, data.Sequence, stdTx.GetTimeoutHeight(), StdFee{Amount: stdTx.GetFee(), Gas: stdTx.GetGas()}, tx.GetMsgs(), stdTx.GetMemo(), + ), nil +} + +// SignatureDataToAminoSignature converts a SignatureData to amino-encoded signature bytes. +// Only SIGN_MODE_LEGACY_AMINO_JSON is supported. +func SignatureDataToAminoSignature(cdc *codec.LegacyAmino, data signingtypes.SignatureData) ([]byte, error) { + switch data := data.(type) { + case *signingtypes.SingleSignatureData: + if data.SignMode != signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON { + return nil, fmt.Errorf("wrong SignMode. Expected %s, got %s", + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, data.SignMode) + } + + return data.Signature, nil + case *signingtypes.MultiSignatureData: + aminoMSig, err := MultiSignatureDataToAminoMultisignature(cdc, data) + if err != nil { + return nil, err + } + + return cdc.MarshalBinaryBare(aminoMSig) + default: + return nil, fmt.Errorf("unexpected signature data %T", data) + } +} + +// MultiSignatureDataToAminoMultisignature converts a MultiSignatureData to an AminoMultisignature. +// Only SIGN_MODE_LEGACY_AMINO_JSON is supported. +func MultiSignatureDataToAminoMultisignature(cdc *codec.LegacyAmino, mSig *signingtypes.MultiSignatureData) (multisig.AminoMultisignature, error) { + n := len(mSig.Signatures) + sigs := make([][]byte, n) + + for i := 0; i < n; i++ { + var err error + sigs[i], err = SignatureDataToAminoSignature(cdc, mSig.Signatures[i]) + if err != nil { + return multisig.AminoMultisignature{}, sdkerrors.Wrapf(err, "Unable to convert Signature Data to signature %d", i) + } + } + + return multisig.AminoMultisignature{ + BitArray: mSig.BitArray, + Sigs: sigs, + }, nil +} diff --git a/x/auth/legacy/legacytx/amino_signing_test.go b/x/auth/legacy/legacytx/amino_signing_test.go new file mode 100644 index 000000000000..7abccbfd8206 --- /dev/null +++ b/x/auth/legacy/legacytx/amino_signing_test.go @@ -0,0 +1,73 @@ +package legacytx + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) { + priv1 := secp256k1.GenPrivKey() + addr1 := sdk.AccAddress(priv1.PubKey().Address()) + priv2 := secp256k1.GenPrivKey() + addr2 := sdk.AccAddress(priv2.PubKey().Address()) + + coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} + + fee := StdFee{ + Amount: coins, + Gas: 10000, + } + memo := "foo" + msgs := []sdk.Msg{ + testdata.NewTestMsg(addr1, addr2), + } + + var ( + chainId = "test-chain" + accNum uint64 = 7 + seqNum uint64 = 7 + timeoutHeight uint64 = 10 + ) + + tx := StdTx{ + Msgs: msgs, + Fee: fee, + Signatures: nil, + Memo: memo, + TimeoutHeight: timeoutHeight, + } + + handler := stdTxSignModeHandler{} + signingData := signing.SignerData{ + ChainID: chainId, + AccountNumber: accNum, + Sequence: seqNum, + } + signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.NoError(t, err) + + expectedSignBz := StdSignBytes(chainId, accNum, seqNum, timeoutHeight, fee, msgs, memo) + + require.Equal(t, expectedSignBz, signBz) + + // expect error with wrong sign mode + _, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx) + require.Error(t, err) +} + +func TestLegacyAminoJSONHandler_DefaultMode(t *testing.T) { + handler := stdTxSignModeHandler{} + require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode()) +} + +func TestLegacyAminoJSONHandler_Modes(t *testing.T) { + handler := stdTxSignModeHandler{} + require.Equal(t, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes()) +} diff --git a/x/auth/legacy/legacytx/codec.go b/x/auth/legacy/legacytx/codec.go new file mode 100644 index 000000000000..2bad4718e1e6 --- /dev/null +++ b/x/auth/legacy/legacytx/codec.go @@ -0,0 +1,9 @@ +package legacytx + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(StdTx{}, "cosmos-sdk/StdTx", nil) +} diff --git a/x/auth/legacy/legacytx/config_test.go b/x/auth/legacy/legacytx/config_test.go new file mode 100644 index 000000000000..95a37b1e4d43 --- /dev/null +++ b/x/auth/legacy/legacytx/config_test.go @@ -0,0 +1,28 @@ +package legacytx_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/codec" + cryptoAmino "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/cosmos/cosmos-sdk/x/auth/testutil" +) + +func testCodec() *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cryptoAmino.RegisterCrypto(cdc) + cdc.RegisterConcrete(&testdata.TestMsg{}, "cosmos-sdk/Test", nil) + return cdc +} + +func TestStdTxConfig(t *testing.T) { + cdc := testCodec() + txGen := legacytx.StdTxConfig{Cdc: cdc} + suite.Run(t, testutil.NewTxConfigTestSuite(txGen)) +} diff --git a/x/auth/legacy/legacytx/stdsign.go b/x/auth/legacy/legacytx/stdsign.go new file mode 100644 index 000000000000..5803190523fb --- /dev/null +++ b/x/auth/legacy/legacytx/stdsign.go @@ -0,0 +1,108 @@ +package legacytx + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// StdSignDoc is replay-prevention structure. +// It includes the result of msg.GetSignBytes(), +// as well as the ChainID (prevent cross chain replay) +// and the Sequence numbers for each signature (prevent +// inchain replay and enforce tx ordering per account). +type StdSignDoc struct { + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + TimeoutHeight uint64 `json:"timeout_height,omitempty" yaml:"timeout_height"` + ChainID string `json:"chain_id" yaml:"chain_id"` + Memo string `json:"memo" yaml:"memo"` + Fee json.RawMessage `json:"fee" yaml:"fee"` + Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` +} + +// StdSignBytes returns the bytes to sign for a transaction. +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 { + msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) + } + + bz, err := legacy.Cdc.MarshalJSON(StdSignDoc{ + AccountNumber: accnum, + ChainID: chainID, + Fee: json.RawMessage(fee.Bytes()), + Memo: memo, + Msgs: msgsBytes, + Sequence: sequence, + TimeoutHeight: timeout, + }) + if err != nil { + panic(err) + } + + return sdk.MustSortJSON(bz) +} + +// Deprecated: StdSignature represents a sig +type StdSignature struct { + cryptotypes.PubKey `json:"pub_key" yaml:"pub_key"` // optional + Signature []byte `json:"signature" yaml:"signature"` +} + +// StdSignatureToSignatureV2 converts a StdSignature to a SignatureV2 +func StdSignatureToSignatureV2(cdc *codec.LegacyAmino, sig StdSignature) (signing.SignatureV2, error) { + pk := sig.GetPubKey() + data, err := pubKeySigToSigData(cdc, pk, sig.Signature) + if err != nil { + return signing.SignatureV2{}, err + } + + return signing.SignatureV2{ + PubKey: pk, + Data: data, + }, nil +} + +func pubKeySigToSigData(cdc *codec.LegacyAmino, key cryptotypes.PubKey, sig []byte) (signing.SignatureData, error) { + multiPK, ok := key.(multisig.PubKey) + if !ok { + return &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: sig, + }, nil + } + var multiSig multisig.AminoMultisignature + err := cdc.UnmarshalBinaryBare(sig, &multiSig) + if err != nil { + return nil, err + } + + sigs := multiSig.Sigs + sigDatas := make([]signing.SignatureData, len(sigs)) + pubKeys := multiPK.GetPubKeys() + bitArray := multiSig.BitArray + n := multiSig.BitArray.Count() + signatures := multisig.NewMultisig(n) + sigIdx := 0 + for i := 0; i < n; i++ { + if bitArray.GetIndex(i) { + data, err := pubKeySigToSigData(cdc, pubKeys[i], multiSig.Sigs[sigIdx]) + if err != nil { + return nil, sdkerrors.Wrapf(err, "Unable to convert Signature to SigData %d", sigIdx) + } + + sigDatas[sigIdx] = data + multisig.AddSignature(signatures, data, sigIdx) + sigIdx++ + } + } + + return signatures, nil +} diff --git a/x/auth/legacy/legacytx/stdsignmsg.go b/x/auth/legacy/legacytx/stdsignmsg.go new file mode 100644 index 000000000000..07ee29a06324 --- /dev/null +++ b/x/auth/legacy/legacytx/stdsignmsg.go @@ -0,0 +1,36 @@ +package legacytx + +import ( + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ types.UnpackInterfacesMessage = StdSignMsg{} + +// StdSignMsg is a convenience structure for passing along a Msg with the other +// requirements for a StdSignDoc before it is signed. For use in the CLI. +type StdSignMsg struct { + ChainID string `json:"chain_id" yaml:"chain_id"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + TimeoutHeight uint64 `json:"timeout_height" yaml:"timeout_height"` + Fee StdFee `json:"fee" yaml:"fee"` + Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` + Memo string `json:"memo" yaml:"memo"` +} + +// get message bytes +func (msg StdSignMsg) Bytes() []byte { + return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.TimeoutHeight, msg.Fee, msg.Msgs, msg.Memo) +} + +func (msg StdSignMsg) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, m := range msg.Msgs { + err := types.UnpackInterfaces(m, unpacker) + if err != nil { + return err + } + } + + return nil +} diff --git a/x/auth/legacy/legacytx/stdtx.go b/x/auth/legacy/legacytx/stdtx.go new file mode 100644 index 000000000000..0aa06da06d26 --- /dev/null +++ b/x/auth/legacy/legacytx/stdtx.go @@ -0,0 +1,297 @@ +package legacytx + +import ( + "fmt" + + "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// Interface implementation checks +var ( + _ sdk.Tx = (*StdTx)(nil) + _ sdk.TxWithMemo = (*StdTx)(nil) + _ sdk.FeeTx = (*StdTx)(nil) + _ codectypes.UnpackInterfacesMessage = (*StdTx)(nil) + + _ codectypes.UnpackInterfacesMessage = (*StdSignature)(nil) +) + +// StdFee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +// [Deprecated] +type StdFee struct { + Amount sdk.Coins `json:"amount" yaml:"amount"` + Gas uint64 `json:"gas" yaml:"gas"` +} + +// Deprecated: NewStdFee returns a new instance of StdFee +func NewStdFee(gas uint64, amount sdk.Coins) StdFee { + return StdFee{ + Amount: amount, + Gas: gas, + } +} + +// GetGas returns the fee's (wanted) gas. +func (fee StdFee) GetGas() uint64 { + return fee.Gas +} + +// GetAmount returns the fee's amount. +func (fee StdFee) GetAmount() sdk.Coins { + return fee.Amount +} + +// Bytes returns the encoded bytes of a StdFee. +func (fee StdFee) Bytes() []byte { + if len(fee.Amount) == 0 { + fee.Amount = sdk.NewCoins() + } + + bz, err := legacy.Cdc.MarshalJSON(fee) + if err != nil { + panic(err) + } + + return bz +} + +// GasPrices returns the gas prices for a StdFee. +// +// NOTE: The gas prices returned are not the true gas prices that were +// originally part of the submitted transaction because the fee is computed +// as fee = ceil(gasWanted * gasPrices). +func (fee StdFee) GasPrices() sdk.DecCoins { + return sdk.NewDecCoinsFromCoins(fee.Amount...).QuoDec(sdk.NewDec(int64(fee.Gas))) +} + +// Deprecated +func NewStdSignature(pk cryptotypes.PubKey, sig []byte) StdSignature { + return StdSignature{PubKey: pk, Signature: sig} +} + +// GetSignature returns the raw signature bytes. +func (ss StdSignature) GetSignature() []byte { + return ss.Signature +} + +// GetPubKey returns the public key of a signature as a cryptotypes.PubKey using the +// Amino codec. +func (ss StdSignature) GetPubKey() cryptotypes.PubKey { + return ss.PubKey +} + +// MarshalYAML returns the YAML representation of the signature. +func (ss StdSignature) MarshalYAML() (interface{}, error) { + var ( + bz []byte + pubkey string + err error + ) + + if ss.PubKey != nil { + pubkey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, ss.GetPubKey()) + if err != nil { + return nil, err + } + } + + bz, err = yaml.Marshal(struct { + PubKey string + Signature string + }{ + PubKey: pubkey, + Signature: fmt.Sprintf("%X", ss.Signature), + }) + if err != nil { + return nil, err + } + + return string(bz), err +} + +func (ss StdSignature) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return codectypes.UnpackInterfaces(ss.PubKey, unpacker) +} + +// StdTx is the legacy transaction format for wrapping a Msg with Fee and Signatures. +// It only works with Amino, please prefer the new protobuf Tx in types/tx. +// NOTE: the first signature is the fee payer (Signatures must not be nil). +// Deprecated +type StdTx struct { + Msgs []sdk.Msg `json:"msg" yaml:"msg"` + Fee StdFee `json:"fee" yaml:"fee"` + Signatures []StdSignature `json:"signatures" yaml:"signatures"` + Memo string `json:"memo" yaml:"memo"` + TimeoutHeight uint64 `json:"timeout_height" yaml:"timeout_height"` +} + +// Deprecated +func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdTx { + return StdTx{ + Msgs: msgs, + Fee: fee, + Signatures: sigs, + Memo: memo, + } +} + +// GetMsgs returns the all the transaction's messages. +func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } + +// ValidateBasic does a simple and lightweight validation check that doesn't +// require access to any other information. +func (tx StdTx) ValidateBasic() error { + stdSigs := tx.GetSignatures() + + if tx.Fee.Gas > txtypes.MaxGasWanted { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidRequest, + "invalid gas supplied; %d > %d", tx.Fee.Gas, txtypes.MaxGasWanted, + ) + } + if tx.Fee.Amount.IsAnyNegative() { + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFee, + "invalid fee provided: %s", tx.Fee.Amount, + ) + } + if len(stdSigs) == 0 { + return sdkerrors.ErrNoSignatures + } + if len(stdSigs) != len(tx.GetSigners()) { + return sdkerrors.Wrapf( + sdkerrors.ErrUnauthorized, + "wrong number of signers; expected %d, got %d", len(tx.GetSigners()), len(stdSigs), + ) + } + + return nil +} + +// Deprecated: AsAny implements intoAny. It doesn't work for protobuf serialization, +// so it can't be saved into protobuf configured storage. We are using it only for API +// compatibility. +func (tx *StdTx) AsAny() *codectypes.Any { + return codectypes.UnsafePackAny(tx) +} + +// GetSigners returns the addresses that must sign the transaction. +// Addresses are returned in a deterministic order. +// They are accumulated from the GetSigners method for each Msg +// in the order they appear in tx.GetMsgs(). +// Duplicate addresses will be omitted. +func (tx StdTx) GetSigners() []sdk.AccAddress { + var signers []sdk.AccAddress + seen := map[string]bool{} + + for _, msg := range tx.GetMsgs() { + for _, addr := range msg.GetSigners() { + if !seen[addr.String()] { + signers = append(signers, addr) + seen[addr.String()] = true + } + } + } + + return signers +} + +// GetMemo returns the memo +func (tx StdTx) GetMemo() string { return tx.Memo } + +// GetTimeoutHeight returns the transaction's timeout height (if set). +func (tx StdTx) GetTimeoutHeight() uint64 { + return tx.TimeoutHeight +} + +// GetSignatures returns the signature of signers who signed the Msg. +// CONTRACT: Length returned is same as length of +// pubkeys returned from MsgKeySigners, and the order +// matches. +// CONTRACT: If the signature is missing (ie the Msg is +// invalid), then the corresponding signature is +// .Empty(). +func (tx StdTx) GetSignatures() [][]byte { + sigs := make([][]byte, len(tx.Signatures)) + for i, stdSig := range tx.Signatures { + sigs[i] = stdSig.Signature + } + return sigs +} + +// GetSignaturesV2 implements SigVerifiableTx.GetSignaturesV2 +func (tx StdTx) GetSignaturesV2() ([]signing.SignatureV2, error) { + res := make([]signing.SignatureV2, len(tx.Signatures)) + + for i, sig := range tx.Signatures { + var err error + res[i], err = StdSignatureToSignatureV2(legacy.Cdc, sig) + if err != nil { + return nil, sdkerrors.Wrapf(err, "Unable to convert signature %v to V2", sig) + } + } + + return res, nil +} + +// GetPubkeys returns the pubkeys of signers if the pubkey is included in the signature +// If pubkey is not included in the signature, then nil is in the slice instead +func (tx StdTx) GetPubKeys() []cryptotypes.PubKey { + pks := make([]cryptotypes.PubKey, len(tx.Signatures)) + + for i, stdSig := range tx.Signatures { + pks[i] = stdSig.GetPubKey() + } + + return pks +} + +// GetGas returns the Gas in StdFee +func (tx StdTx) GetGas() uint64 { return tx.Fee.Gas } + +// GetFee returns the FeeAmount in StdFee +func (tx StdTx) GetFee() sdk.Coins { return tx.Fee.Amount } + +// FeePayer returns the address that is responsible for paying fee +// StdTx returns the first signer as the fee payer +// If no signers for tx, return empty address +func (tx StdTx) FeePayer() sdk.AccAddress { + if tx.GetSigners() != nil { + return tx.GetSigners()[0] + } + return sdk.AccAddress{} +} + +// FeeGranter always returns nil for StdTx +func (tx StdTx) FeeGranter() sdk.AccAddress { + return nil +} + +func (tx StdTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, m := range tx.Msgs { + err := codectypes.UnpackInterfaces(m, unpacker) + if err != nil { + return err + } + } + + // Signatures contain PubKeys, which need to be unpacked. + for _, s := range tx.Signatures { + err := s.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + + return nil +} diff --git a/x/auth/legacy/legacytx/stdtx_builder.go b/x/auth/legacy/legacytx/stdtx_builder.go new file mode 100644 index 000000000000..153c4b2bbbc1 --- /dev/null +++ b/x/auth/legacy/legacytx/stdtx_builder.go @@ -0,0 +1,211 @@ +package legacytx + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "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/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// StdTxBuilder wraps StdTx to implement to the context.TxBuilder interface. +// Note that this type just exists for backwards compatibility with amino StdTx +// and will not work for protobuf transactions. +type StdTxBuilder struct { + StdTx + cdc *codec.LegacyAmino +} + +// ensure interface implementation +var _ client.TxBuilder = &StdTxBuilder{} + +// GetTx implements TxBuilder.GetTx +func (s *StdTxBuilder) GetTx() authsigning.Tx { + return s.StdTx +} + +// SetMsgs implements TxBuilder.SetMsgs +func (s *StdTxBuilder) SetMsgs(msgs ...sdk.Msg) error { + stdTxMsgs := make([]sdk.Msg, len(msgs)) + + for i, msg := range msgs { + switch msg := msg.(type) { + case sdk.ServiceMsg: + // Since ServiceMsg isn't registered with amino, we unpack msg.Request + // into a Msg so that it's handled gracefully for the legacy + // GET /txs/{hash} and /txs endpoints. + sdkMsg, ok := msg.Request.(sdk.Msg) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting %T at %d to implement sdk.Msg", msg.Request, i) + } + stdTxMsgs[i] = sdkMsg + default: + // legacy sdk.Msg + stdTxMsgs[i] = msg + } + } + s.Msgs = stdTxMsgs + return nil +} + +// SetSignatures implements TxBuilder.SetSignatures. +func (s *StdTxBuilder) SetSignatures(signatures ...signing.SignatureV2) error { + sigs := make([]StdSignature, len(signatures)) + var err error + for i, sig := range signatures { + sigs[i], err = SignatureV2ToStdSignature(s.cdc, sig) + if err != nil { + return err + } + } + + s.Signatures = sigs + return nil +} + +func (s *StdTxBuilder) SetFeeAmount(amount sdk.Coins) { + s.StdTx.Fee.Amount = amount +} + +func (s *StdTxBuilder) SetGasLimit(limit uint64) { + s.StdTx.Fee.Gas = limit +} + +// SetMemo implements TxBuilder.SetMemo +func (s *StdTxBuilder) SetMemo(memo string) { + s.Memo = memo +} + +// SetTimeoutHeight sets the transaction's height timeout. +func (s *StdTxBuilder) SetTimeoutHeight(height uint64) { + s.TimeoutHeight = height +} + +// StdTxConfig is a context.TxConfig for StdTx +type StdTxConfig struct { + Cdc *codec.LegacyAmino +} + +var _ client.TxConfig = StdTxConfig{} + +// NewTxBuilder implements TxConfig.NewTxBuilder +func (s StdTxConfig) NewTxBuilder() client.TxBuilder { + return &StdTxBuilder{ + StdTx: StdTx{}, + cdc: s.Cdc, + } +} + +// WrapTxBuilder returns a StdTxBuilder from provided transaction +func (s StdTxConfig) WrapTxBuilder(newTx sdk.Tx) (client.TxBuilder, error) { + stdTx, ok := newTx.(StdTx) + if !ok { + return nil, fmt.Errorf("wrong type, expected %T, got %T", stdTx, newTx) + } + return &StdTxBuilder{StdTx: stdTx, cdc: s.Cdc}, nil +} + +// MarshalTx implements TxConfig.MarshalTx +func (s StdTxConfig) TxEncoder() sdk.TxEncoder { + return DefaultTxEncoder(s.Cdc) +} + +func (s StdTxConfig) TxDecoder() sdk.TxDecoder { + return mkDecoder(s.Cdc.UnmarshalBinaryBare) +} + +func (s StdTxConfig) TxJSONEncoder() sdk.TxEncoder { + return func(tx sdk.Tx) ([]byte, error) { + return s.Cdc.MarshalJSON(tx) + } +} + +func (s StdTxConfig) TxJSONDecoder() sdk.TxDecoder { + return mkDecoder(s.Cdc.UnmarshalJSON) +} + +func (s StdTxConfig) MarshalSignatureJSON(sigs []signing.SignatureV2) ([]byte, error) { + stdSigs := make([]StdSignature, len(sigs)) + for i, sig := range sigs { + stdSig, err := SignatureV2ToStdSignature(s.Cdc, sig) + if err != nil { + return nil, err + } + + stdSigs[i] = stdSig + } + return s.Cdc.MarshalJSON(stdSigs) +} + +func (s StdTxConfig) UnmarshalSignatureJSON(bz []byte) ([]signing.SignatureV2, error) { + var stdSigs []StdSignature + err := s.Cdc.UnmarshalJSON(bz, &stdSigs) + if err != nil { + return nil, err + } + + sigs := make([]signing.SignatureV2, len(stdSigs)) + for i, stdSig := range stdSigs { + sig, err := StdSignatureToSignatureV2(s.Cdc, stdSig) + if err != nil { + return nil, err + } + sigs[i] = sig + } + + return sigs, nil +} + +func (s StdTxConfig) SignModeHandler() authsigning.SignModeHandler { + return stdTxSignModeHandler{} +} + +// SignatureV2ToStdSignature converts a SignatureV2 to a StdSignature +// [Deprecated] +func SignatureV2ToStdSignature(cdc *codec.LegacyAmino, sig signing.SignatureV2) (StdSignature, error) { + var ( + sigBz []byte + err error + ) + + if sig.Data != nil { + sigBz, err = SignatureDataToAminoSignature(cdc, sig.Data) + if err != nil { + return StdSignature{}, err + } + } + + return StdSignature{ + PubKey: sig.PubKey, + Signature: sigBz, + }, nil +} + +// Unmarshaler is a generic type for Unmarshal functions +type Unmarshaler func(bytes []byte, ptr interface{}) error + +func mkDecoder(unmarshaler Unmarshaler) sdk.TxDecoder { + return func(txBytes []byte) (sdk.Tx, error) { + if len(txBytes) == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty") + } + var tx = StdTx{} + // StdTx.Msg is an interface. The concrete types + // are registered by MakeTxCodec + err := unmarshaler(txBytes, &tx) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + return tx, nil + } +} + +// DefaultTxEncoder logic for standard transaction encoding +func DefaultTxEncoder(cdc *codec.LegacyAmino) sdk.TxEncoder { + return func(tx sdk.Tx) ([]byte, error) { + return cdc.MarshalBinaryBare(tx) + } +} diff --git a/x/auth/legacy/legacytx/stdtx_test.go b/x/auth/legacy/legacytx/stdtx_test.go new file mode 100644 index 000000000000..a702a82841d0 --- /dev/null +++ b/x/auth/legacy/legacytx/stdtx_test.go @@ -0,0 +1,288 @@ +package legacytx + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + yaml "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +var ( + priv = ed25519.GenPrivKey() + addr = sdk.AccAddress(priv.PubKey().Address()) +) + +func init() { + var amino = codec.NewLegacyAmino() + RegisterLegacyAminoCodec(amino) +} + +// Deprecated, use fee amount and gas limit separately on TxBuilder. +func NewTestStdFee() StdFee { + return NewStdFee(100000, + sdk.NewCoins(sdk.NewInt64Coin("atom", 150)), + ) +} + +// Deprecated, use TxBuilder. +func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []cryptotypes.PrivKey, accNums []uint64, seqs []uint64, timeout uint64, fee StdFee) sdk.Tx { + sigs := make([]StdSignature, len(privs)) + for i, priv := range privs { + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], timeout, fee, msgs, "") + + sig, err := priv.Sign(signBytes) + if err != nil { + panic(err) + } + + sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} + } + + tx := NewStdTx(msgs, fee, sigs, "") + return tx +} + +func TestStdTx(t *testing.T) { + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + fee := NewTestStdFee() + sigs := []StdSignature{} + + tx := NewStdTx(msgs, fee, sigs, "") + require.Equal(t, msgs, tx.GetMsgs()) + require.Equal(t, sigs, tx.Signatures) + + feePayer := tx.GetSigners()[0] + require.Equal(t, addr, feePayer) + + feeGranter := tx.FeeGranter() + require.Empty(t, feeGranter) +} + +func TestStdSignBytes(t *testing.T) { + type args struct { + chainID string + accnum uint64 + sequence uint64 + timeoutHeight uint64 + fee StdFee + msgs []sdk.Msg + memo string + } + defaultFee := NewTestStdFee() + tests := []struct { + args args + want string + }{ + { + args{"1234", 3, 6, 10, defaultFee, []sdk.Msg{testdata.NewTestMsg(addr)}, "memo"}, + fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"100000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\",\"timeout_height\":\"10\"}", addr), + }, + { + args{"1234", 3, 6, 0, defaultFee, []sdk.Msg{testdata.NewTestMsg(addr)}, "memo"}, + fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"100000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}", addr), + }, + } + for i, tc := range tests { + got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.timeoutHeight, tc.args.fee, tc.args.msgs, tc.args.memo)) + require.Equal(t, tc.want, got, "Got unexpected result on test case i: %d", i) + } +} + +func TestTxValidateBasic(t *testing.T) { + ctx := sdk.NewContext(nil, tmproto.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, addr2 := testdata.KeyTestPubAddr() + + // msg and signatures + msg1 := testdata.NewTestMsg(addr1, addr2) + fee := NewTestStdFee() + + msgs := []sdk.Msg{msg1} + + // require to fail validation upon invalid fee + badFee := NewTestStdFee() + badFee.Amount[0].Amount = sdk.NewInt(-5) + tx := NewTestTx(ctx, nil, nil, nil, nil, 0, badFee) + + err := tx.ValidateBasic() + require.Error(t, err) + _, code, _ := sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrInsufficientFee.ABCICode(), code) + + // require to fail validation when no signatures exist + privs, accNums, seqs := []cryptotypes.PrivKey{}, []uint64{}, []uint64{} + tx = NewTestTx(ctx, msgs, privs, accNums, seqs, 0, fee) + + err = tx.ValidateBasic() + require.Error(t, err) + _, code, _ = sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrNoSignatures.ABCICode(), code) + + // require to fail validation when signatures do not match expected signers + privs, accNums, seqs = []cryptotypes.PrivKey{priv1}, []uint64{0, 1}, []uint64{0, 0} + tx = NewTestTx(ctx, msgs, privs, accNums, seqs, 0, fee) + + err = tx.ValidateBasic() + require.Error(t, err) + _, code, _ = sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrUnauthorized.ABCICode(), code) + + // require to fail with invalid gas supplied + badFee = NewTestStdFee() + badFee.Gas = 9223372036854775808 + tx = NewTestTx(ctx, nil, nil, nil, nil, 0, badFee) + + err = tx.ValidateBasic() + require.Error(t, err) + _, code, _ = sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrInvalidRequest.ABCICode(), code) + + // require to pass when above criteria are matched + privs, accNums, seqs = []cryptotypes.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} + tx = NewTestTx(ctx, msgs, privs, accNums, seqs, 0, fee) + + err = tx.ValidateBasic() + require.NoError(t, err) +} + +func TestDefaultTxEncoder(t *testing.T) { + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cdc.RegisterConcrete(testdata.TestMsg{}, "cosmos-sdk/Test", nil) + encoder := DefaultTxEncoder(cdc) + + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + fee := NewTestStdFee() + sigs := []StdSignature{} + + tx := NewStdTx(msgs, fee, sigs, "") + + cdcBytes, err := cdc.MarshalBinaryBare(tx) + + require.NoError(t, err) + encoderBytes, err := encoder(tx) + + require.NoError(t, err) + require.Equal(t, cdcBytes, encoderBytes) +} + +func TestStdSignatureMarshalYAML(t *testing.T) { + _, pubKey, _ := testdata.KeyTestPubAddr() + + testCases := []struct { + sig StdSignature + output string + }{ + { + StdSignature{}, + "|\n pubkey: \"\"\n signature: \"\"\n", + }, + { + StdSignature{PubKey: pubKey, Signature: []byte("dummySig")}, + fmt.Sprintf("|\n pubkey: %s\n signature: 64756D6D79536967\n", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)), + }, + { + StdSignature{PubKey: pubKey, Signature: nil}, + fmt.Sprintf("|\n pubkey: %s\n signature: \"\"\n", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)), + }, + } + + for i, tc := range testCases { + bz, err := yaml.Marshal(tc.sig) + require.NoError(t, err) + require.Equal(t, tc.output, string(bz), "test case #%d", i) + } +} + +func TestSignatureV2Conversions(t *testing.T) { + _, pubKey, _ := testdata.KeyTestPubAddr() + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + dummy := []byte("dummySig") + sig := StdSignature{PubKey: pubKey, Signature: dummy} + + sigV2, err := StdSignatureToSignatureV2(cdc, sig) + require.NoError(t, err) + require.Equal(t, pubKey, sigV2.PubKey) + require.Equal(t, &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: dummy, + }, sigV2.Data) + + sigBz, err := SignatureDataToAminoSignature(cdc, sigV2.Data) + require.NoError(t, err) + require.Equal(t, dummy, sigBz) + + // multisigs + _, pubKey2, _ := testdata.KeyTestPubAddr() + multiPK := kmultisig.NewLegacyAminoPubKey(1, []cryptotypes.PubKey{ + pubKey, pubKey2, + }) + dummy2 := []byte("dummySig2") + bitArray := types.NewCompactBitArray(2) + bitArray.SetIndex(0, true) + bitArray.SetIndex(1, true) + msigData := &signing.MultiSignatureData{ + BitArray: bitArray, + Signatures: []signing.SignatureData{ + &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: dummy, + }, + &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: dummy2, + }, + }, + } + + msig, err := SignatureDataToAminoSignature(cdc, msigData) + require.NoError(t, err) + + sigV2, err = StdSignatureToSignatureV2(cdc, StdSignature{ + PubKey: multiPK, + Signature: msig, + }) + require.NoError(t, err) + require.Equal(t, multiPK, sigV2.PubKey) + require.Equal(t, msigData, sigV2.Data) +} + +func TestGetSignaturesV2(t *testing.T) { + _, pubKey, _ := testdata.KeyTestPubAddr() + dummy := []byte("dummySig") + + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + + fee := NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) + sig := StdSignature{PubKey: pubKey, Signature: dummy} + stdTx := NewStdTx([]sdk.Msg{testdata.NewTestMsg()}, fee, []StdSignature{sig}, "testsigs") + + sigs, err := stdTx.GetSignaturesV2() + require.Nil(t, err) + require.Equal(t, len(sigs), 1) + + require.Equal(t, cdc.MustMarshalBinaryBare(sigs[0].PubKey), cdc.MustMarshalBinaryBare(sig.GetPubKey())) + require.Equal(t, sigs[0].Data, &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: sig.GetSignature(), + }) +} diff --git a/x/auth/legacy/v034/types.go b/x/auth/legacy/v034/types.go new file mode 100644 index 000000000000..c5df671017b2 --- /dev/null +++ b/x/auth/legacy/v034/types.go @@ -0,0 +1,26 @@ +// DONTCOVER +// nolint +package v034 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "auth" +) + +type ( + Params struct { + MaxMemoCharacters uint64 `json:"max_memo_characters"` + TxSigLimit uint64 `json:"tx_sig_limit"` + TxSizeCostPerByte uint64 `json:"tx_size_cost_per_byte"` + SigVerifyCostED25519 uint64 `json:"sig_verify_cost_ed25519"` + SigVerifyCostSecp256k1 uint64 `json:"sig_verify_cost_secp256k1"` + } + + GenesisState struct { + CollectedFees sdk.Coins `json:"collected_fees"` + Params Params `json:"params"` + } +) diff --git a/x/auth/legacy/v036/migrate.go b/x/auth/legacy/v036/migrate.go new file mode 100644 index 000000000000..c651df6994d8 --- /dev/null +++ b/x/auth/legacy/v036/migrate.go @@ -0,0 +1,14 @@ +// DONTCOVER +// nolint +package v036 + +import ( + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. This migration removes the CollectedFees coins from the old +// FeeCollectorKeeper. +func Migrate(oldGenState v034auth.GenesisState) GenesisState { + return NewGenesisState(oldGenState.Params) +} diff --git a/x/auth/legacy/v036/migrate_test.go b/x/auth/legacy/v036/migrate_test.go new file mode 100644 index 000000000000..739c43223f6b --- /dev/null +++ b/x/auth/legacy/v036/migrate_test.go @@ -0,0 +1,26 @@ +package v036 + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/types" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + + "github.com/stretchr/testify/require" +) + +func TestMigrate(t *testing.T) { + var genesisState GenesisState + require.NotPanics(t, func() { + genesisState = Migrate(v034auth.GenesisState{ + CollectedFees: types.Coins{ + { + Amount: types.NewInt(10), + Denom: "stake", + }, + }, + Params: v034auth.Params{}, // forwarded structure: filling and checking will be testing a no-op + }) + }) + require.Equal(t, genesisState, GenesisState{Params: v034auth.Params{}}) +} diff --git a/x/auth/legacy/v036/types.go b/x/auth/legacy/v036/types.go new file mode 100644 index 000000000000..2490f4baa22e --- /dev/null +++ b/x/auth/legacy/v036/types.go @@ -0,0 +1,19 @@ +// DONTCOVER +// nolint +package v036 + +import v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + +const ( + ModuleName = "auth" +) + +type ( + GenesisState struct { + Params v034auth.Params `json:"params"` + } +) + +func NewGenesisState(params v034auth.Params) GenesisState { + return GenesisState{params} +} diff --git a/x/auth/legacy/v038/migrate.go b/x/auth/legacy/v038/migrate.go new file mode 100644 index 000000000000..46947075c8b5 --- /dev/null +++ b/x/auth/legacy/v038/migrate.go @@ -0,0 +1,52 @@ +package v038 + +import ( + v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036" + v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.38 +// genesis state. +func Migrate(authGenState v036auth.GenesisState, genAccountsGenState v036genaccounts.GenesisState) GenesisState { + accounts := make(GenesisAccounts, len(genAccountsGenState)) + + for i, acc := range genAccountsGenState { + var genAccount GenesisAccount + + baseAccount := NewBaseAccount(acc.Address, acc.Coins.Sort(), nil, acc.AccountNumber, acc.Sequence) + + switch { + case !acc.OriginalVesting.IsZero(): + baseVestingAccount := NewBaseVestingAccount( + baseAccount, acc.OriginalVesting.Sort(), acc.DelegatedFree.Sort(), + acc.DelegatedVesting.Sort(), acc.EndTime, + ) + + if acc.StartTime != 0 && acc.EndTime != 0 { + // continuous vesting account type + genAccount = NewContinuousVestingAccountRaw(baseVestingAccount, acc.StartTime) + } else if acc.EndTime != 0 { + // delayed vesting account type + genAccount = NewDelayedVestingAccountRaw(baseVestingAccount) + } + + case acc.ModuleName != "": + // module account type + genAccount = NewModuleAccount(baseAccount, acc.ModuleName, acc.ModulePermissions...) + + default: + // standard account type + genAccount = baseAccount + } + + accounts[i] = genAccount + } + + accounts = SanitizeGenesisAccounts(accounts) + + if err := ValidateGenAccounts(accounts); err != nil { + panic(err) + } + + return NewGenesisState(authGenState.Params, accounts) +} diff --git a/x/auth/legacy/v038/migrate_test.go b/x/auth/legacy/v038/migrate_test.go new file mode 100644 index 000000000000..e83ccfe8e20f --- /dev/null +++ b/x/auth/legacy/v038/migrate_test.go @@ -0,0 +1,159 @@ +package v038 + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036" + v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" + + "github.com/stretchr/testify/require" +) + +func accAddressFromBech32(t *testing.T, addrStr string) sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(addrStr) + require.NoError(t, err) + return addr +} + +func TestMigrate(t *testing.T) { + var genesisState GenesisState + + params := v034auth.Params{ + MaxMemoCharacters: 10, + TxSigLimit: 10, + TxSizeCostPerByte: 10, + SigVerifyCostED25519: 10, + SigVerifyCostSecp256k1: 10, + } + + acc1 := v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)), + Sequence: 1, + AccountNumber: 1, + } + acc2 := v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), + Sequence: 4, + AccountNumber: 2, + ModuleName: "bonded_tokens_pool", + ModulePermissions: []string{"burner", "staking"}, + } + acc3 := v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos17n9sztlhx32tfy0tg0zc2ttmkeeth50yyuv9he"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), + OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), + StartTime: time.Now().Unix(), + EndTime: time.Now().Add(48 * time.Hour).Unix(), + Sequence: 5, + AccountNumber: 3, + } + acc4 := v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos1fmk5elg4r62mlexd36tqjcwyafs7mek0js5m4d"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), + OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), + EndTime: time.Now().Add(48 * time.Hour).Unix(), + Sequence: 15, + AccountNumber: 4, + } + + require.NotPanics(t, func() { + genesisState = Migrate( + v036auth.GenesisState{ + Params: params, + }, + v036genaccounts.GenesisState{acc1, acc2, acc3, acc4}, + ) + }) + + expectedAcc1 := NewBaseAccount(acc1.Address, acc1.Coins, nil, acc1.AccountNumber, acc1.Sequence) + expectedAcc2 := NewModuleAccount( + NewBaseAccount(acc2.Address, acc2.Coins, nil, acc2.AccountNumber, acc2.Sequence), + acc2.ModuleName, acc2.ModulePermissions..., + ) + expectedAcc3 := NewContinuousVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(acc3.Address, acc3.Coins, nil, acc3.AccountNumber, acc3.Sequence), + acc3.OriginalVesting, acc3.DelegatedFree, acc3.DelegatedVesting, acc3.EndTime, + ), + acc3.StartTime, + ) + expectedAcc4 := NewDelayedVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(acc4.Address, acc4.Coins, nil, acc4.AccountNumber, acc4.Sequence), + acc4.OriginalVesting, acc4.DelegatedFree, acc4.DelegatedVesting, acc4.EndTime, + ), + ) + + require.Equal( + t, genesisState, GenesisState{ + Params: params, + Accounts: GenesisAccounts{expectedAcc1, expectedAcc2, expectedAcc3, expectedAcc4}, + }, + ) +} + +func TestMigrateInvalid(t *testing.T) { + testCases := []struct { + name string + acc v036genaccounts.GenesisAccount + }{ + { + "module account with invalid name", + v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), + Sequence: 4, + AccountNumber: 2, + ModuleName: " ", + ModulePermissions: []string{"burner", "staking"}, + }, + }, + { + "module account with invalid permissions", + v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), + Sequence: 4, + AccountNumber: 2, + ModuleName: "bonded_tokens_pool", + ModulePermissions: []string{""}, + }, + }, + { + "module account with invalid address", + v036genaccounts.GenesisAccount{ + Address: accAddressFromBech32(t, "cosmos17n9sztlhx32tfy0tg0zc2ttmkeeth50yyuv9he"), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), + Sequence: 4, + AccountNumber: 2, + ModuleName: "bonded_tokens_pool", + ModulePermissions: []string{"burner", "staking"}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require.Panics(t, func() { + Migrate( + v036auth.GenesisState{ + Params: v034auth.Params{ + MaxMemoCharacters: 10, + TxSigLimit: 10, + TxSizeCostPerByte: 10, + SigVerifyCostED25519: 10, + SigVerifyCostSecp256k1: 10, + }, + }, + v036genaccounts.GenesisState{tc.acc}, + ) + }) + }) + } +} diff --git a/x/auth/legacy/v038/types.go b/x/auth/legacy/v038/types.go new file mode 100644 index 000000000000..e9977c1d2966 --- /dev/null +++ b/x/auth/legacy/v038/types.go @@ -0,0 +1,530 @@ +package v038 + +// DONTCOVER +// nolint + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "sort" + "strings" + + tmcrypto "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" +) + +const ( + ModuleName = "auth" +) + +type ( + // partial interface needed only for amino encoding and sanitization + Account interface { + GetAddress() sdk.AccAddress + GetAccountNumber() uint64 + GetCoins() sdk.Coins + SetCoins(sdk.Coins) error + } + + GenesisAccount interface { + Account + + Validate() error + } + + GenesisAccounts []GenesisAccount + + GenesisState struct { + Params v034auth.Params `json:"params" yaml:"params"` + Accounts GenesisAccounts `json:"accounts" yaml:"accounts"` + } + + BaseAccount struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey cryptotypes.PubKey `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + } + + baseAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + } + + BaseVestingAccount struct { + *BaseAccount + + OriginalVesting sdk.Coins `json:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting"` + + EndTime int64 `json:"end_time"` + } + + vestingAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + // custom fields based on concrete vesting type which can be omitted + StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` + } + + ContinuousVestingAccount struct { + *BaseVestingAccount + + StartTime int64 `json:"start_time"` + } + + DelayedVestingAccount struct { + *BaseVestingAccount + } + + ModuleAccount struct { + *BaseAccount + + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } + + moduleAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } +) + +func NewGenesisState(params v034auth.Params, accounts GenesisAccounts) GenesisState { + return GenesisState{ + Params: params, + Accounts: accounts, + } +} + +func NewBaseAccountWithAddress(addr sdk.AccAddress) BaseAccount { + return BaseAccount{ + Address: addr, + } +} + +func NewBaseAccount( + address sdk.AccAddress, coins sdk.Coins, pk cryptotypes.PubKey, accountNumber, sequence uint64, +) *BaseAccount { + + return &BaseAccount{ + Address: address, + Coins: coins, + PubKey: pk, + AccountNumber: accountNumber, + Sequence: sequence, + } +} + +func (acc BaseAccount) GetAddress() sdk.AccAddress { + return acc.Address +} + +func (acc *BaseAccount) GetAccountNumber() uint64 { + return acc.AccountNumber +} + +func (acc *BaseAccount) GetCoins() sdk.Coins { + return acc.Coins +} + +func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { + acc.Coins = coins + return nil +} + +func (acc BaseAccount) Validate() error { + if acc.PubKey != nil && acc.Address != nil && + !bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) { + return errors.New("pubkey and address pair is invalid") + } + + return nil +} + +func (acc BaseAccount) MarshalJSON() ([]byte, error) { + alias := baseAccountPretty{ + Address: acc.Address, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + } + + if acc.PubKey != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount. +func (acc *BaseAccount) UnmarshalJSON(bz []byte) error { + var alias baseAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + if alias.PubKey != "" { + pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + + acc.PubKey = pk + } + + acc.Address = alias.Address + acc.Coins = alias.Coins + acc.AccountNumber = alias.AccountNumber + acc.Sequence = alias.Sequence + + return nil +} + +func NewBaseVestingAccount( + baseAccount *BaseAccount, originalVesting, delegatedFree, delegatedVesting sdk.Coins, endTime int64, +) *BaseVestingAccount { + + return &BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: originalVesting, + DelegatedFree: delegatedFree, + DelegatedVesting: delegatedVesting, + EndTime: endTime, + } +} + +func (bva BaseVestingAccount) Validate() error { + return bva.BaseAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a BaseVestingAccount. +func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: bva.Address, + Coins: bva.Coins, + AccountNumber: bva.AccountNumber, + Sequence: bva.Sequence, + OriginalVesting: bva.OriginalVesting, + DelegatedFree: bva.DelegatedFree, + DelegatedVesting: bva.DelegatedVesting, + EndTime: bva.EndTime, + } + + if bva.PubKey != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a BaseVestingAccount. +func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk cryptotypes.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + } + + bva.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) + bva.OriginalVesting = alias.OriginalVesting + bva.DelegatedFree = alias.DelegatedFree + bva.DelegatedVesting = alias.DelegatedVesting + bva.EndTime = alias.EndTime + + return nil +} + +func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *ContinuousVestingAccount { + return &ContinuousVestingAccount{ + BaseVestingAccount: bva, + StartTime: startTime, + } +} + +func (cva ContinuousVestingAccount) Validate() error { + if cva.StartTime >= cva.EndTime { + return errors.New("vesting start-time cannot be before end-time") + } + + return cva.BaseVestingAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a ContinuousVestingAccount. +func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: cva.Address, + Coins: cva.Coins, + AccountNumber: cva.AccountNumber, + Sequence: cva.Sequence, + OriginalVesting: cva.OriginalVesting, + DelegatedFree: cva.DelegatedFree, + DelegatedVesting: cva.DelegatedVesting, + EndTime: cva.EndTime, + StartTime: cva.StartTime, + } + + if cva.PubKey != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ContinuousVestingAccount. +func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk cryptotypes.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + } + + cva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + cva.StartTime = alias.StartTime + + return nil +} + +func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount { + return &DelayedVestingAccount{ + BaseVestingAccount: bva, + } +} + +func (dva DelayedVestingAccount) Validate() error { + return dva.BaseVestingAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a DelayedVestingAccount. +func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountPretty{ + Address: dva.Address, + Coins: dva.Coins, + AccountNumber: dva.AccountNumber, + Sequence: dva.Sequence, + OriginalVesting: dva.OriginalVesting, + DelegatedFree: dva.DelegatedFree, + DelegatedVesting: dva.DelegatedVesting, + EndTime: dva.EndTime, + } + + if dva.PubKey != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, dva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. +func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk cryptotypes.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + } + + dva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + + return nil +} + +func NewModuleAddress(name string) sdk.AccAddress { + return sdk.AccAddress(tmcrypto.AddressHash([]byte(name))) +} + +func NewModuleAccount(baseAccount *BaseAccount, name string, permissions ...string) *ModuleAccount { + return &ModuleAccount{ + BaseAccount: baseAccount, + Name: name, + Permissions: permissions, + } +} + +func (ma ModuleAccount) Validate() error { + if err := ValidatePermissions(ma.Permissions...); err != nil { + return err + } + + if strings.TrimSpace(ma.Name) == "" { + return errors.New("module account name cannot be blank") + } + + if !ma.Address.Equals(sdk.AccAddress(tmcrypto.AddressHash([]byte(ma.Name)))) { + return fmt.Errorf("address %s cannot be derived from the module name '%s'", ma.Address, ma.Name) + } + + return ma.BaseAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a ModuleAccount. +func (ma ModuleAccount) MarshalJSON() ([]byte, error) { + return json.Marshal(moduleAccountPretty{ + Address: ma.Address, + Coins: ma.Coins, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. +func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { + var alias moduleAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + ma.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) + ma.Name = alias.Name + ma.Permissions = alias.Permissions + + return nil +} + +func ValidatePermissions(permissions ...string) error { + for _, perm := range permissions { + if strings.TrimSpace(perm) == "" { + return fmt.Errorf("module permission is empty") + } + } + + return nil +} + +func SanitizeGenesisAccounts(genAccounts GenesisAccounts) GenesisAccounts { + sort.Slice(genAccounts, func(i, j int) bool { + return genAccounts[i].GetAccountNumber() < genAccounts[j].GetAccountNumber() + }) + + for _, acc := range genAccounts { + if err := acc.SetCoins(acc.GetCoins().Sort()); err != nil { + panic(err) + } + } + + return genAccounts +} + +func ValidateGenAccounts(genAccounts GenesisAccounts) error { + addrMap := make(map[string]bool, len(genAccounts)) + for _, acc := range genAccounts { + + // check for duplicated accounts + addrStr := acc.GetAddress().String() + if _, ok := addrMap[addrStr]; ok { + return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) + } + + addrMap[addrStr] = true + + // check account specific validation + if err := acc.Validate(); err != nil { + return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error()) + } + } + + return nil +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cryptocodec.RegisterCrypto(cdc) + cdc.RegisterInterface((*GenesisAccount)(nil), nil) + cdc.RegisterInterface((*Account)(nil), nil) + cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil) + cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil) + cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil) + cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil) + cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) +} diff --git a/x/auth/legacy/v039/migrate.go b/x/auth/legacy/v039/migrate.go new file mode 100644 index 000000000000..c29048fca517 --- /dev/null +++ b/x/auth/legacy/v039/migrate.go @@ -0,0 +1,60 @@ +package v039 + +import ( + "fmt" + + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" +) + +// Migrate accepts exported genesis state from v0.38 and migrates it to v0.39 +// genesis state. +func Migrate(oldAuthGenState v038auth.GenesisState) GenesisState { + accounts := make(v038auth.GenesisAccounts, len(oldAuthGenState.Accounts)) + + for i, acc := range oldAuthGenState.Accounts { + switch t := acc.(type) { + case *v038auth.BaseAccount: + accounts[i] = NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence) + + case *v038auth.BaseVestingAccount: + accounts[i] = NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ) + + case *v038auth.ContinuousVestingAccount: + accounts[i] = NewContinuousVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ), + t.StartTime, + ) + + case *v038auth.DelayedVestingAccount: + accounts[i] = NewDelayedVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ), + ) + + case *v038auth.ModuleAccount: + accounts[i] = NewModuleAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.Name, t.Permissions..., + ) + + default: + panic(fmt.Sprintf("unexpected account type: %T", acc)) + } + } + + accounts = v038auth.SanitizeGenesisAccounts(accounts) + + if err := v038auth.ValidateGenAccounts(accounts); err != nil { + panic(err) + } + + return NewGenesisState(oldAuthGenState.Params, accounts) +} diff --git a/x/auth/legacy/v039/migrate_test.go b/x/auth/legacy/v039/migrate_test.go new file mode 100644 index 000000000000..6972789c0099 --- /dev/null +++ b/x/auth/legacy/v039/migrate_test.go @@ -0,0 +1,104 @@ +package v039_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" +) + +func TestMigrate(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + v039auth.RegisterLegacyAminoCodec(aminoCdc) + + pub1 := ed25519.GenPrivKeyFromSecret([]byte("acc1")).PubKey() + pub2 := secp256k1.GenPrivKeyFromSecret([]byte("acc2")).PubKey() + + acc1 := v038auth.BaseAccount{ + Address: sdk.AccAddress(pub1.Address()), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)), + Sequence: 1, + AccountNumber: 1, + PubKey: pub1, + } + acc2 := v038auth.BaseAccount{ + Address: sdk.AccAddress(pub2.Address()), + Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)), + Sequence: 2, + AccountNumber: 2, + PubKey: pub2, + } + + migrated := v039auth.Migrate( + v038auth.GenesisState{ + Accounts: v038auth.GenesisAccounts{&acc1, &acc2}, + }, + ) + + expectedAcc1 := v039auth.NewBaseAccount(acc1.Address, acc1.Coins, acc1.PubKey, acc1.AccountNumber, acc1.Sequence) + expectedAcc2 := v039auth.NewBaseAccount(acc2.Address, acc2.Coins, acc2.PubKey, acc2.AccountNumber, acc2.Sequence) + + require.Equal( + t, migrated, v039auth.GenesisState{ + Accounts: v038auth.GenesisAccounts{expectedAcc1, expectedAcc2}, + }, + ) + + json, err := aminoCdc.MarshalJSONIndent(migrated, "", " ") + require.NoError(t, err) + + expectedJSON := `{ + "params": { + "max_memo_characters": "0", + "tx_sig_limit": "0", + "tx_size_cost_per_byte": "0", + "sig_verify_cost_ed25519": "0", + "sig_verify_cost_secp256k1": "0" + }, + "accounts": [ + { + "type": "cosmos-sdk/Account", + "value": { + "address": "cosmos1j7skdhh9raxdmfhmcy2gxz8hgn0jnhfmujjsfe", + "coins": [ + { + "denom": "stake", + "amount": "400000" + } + ], + "public_key": { + "type": "tendermint/PubKeyEd25519", + "value": "eB0AcLMLKFRNFfh4XAAMstexfAIUQQCDnfjLZ2KJg+A=" + }, + "account_number": "1", + "sequence": "1" + } + }, + { + "type": "cosmos-sdk/Account", + "value": { + "address": "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", + "coins": [ + { + "denom": "stake", + "amount": "400000" + } + ], + "public_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "AruDygh5HprMOpHOEato85dLgAsybMJVyxBGUa3KuWCr" + }, + "account_number": "2", + "sequence": "2" + } + } + ] +}` + require.Equal(t, expectedJSON, string(json)) +} diff --git a/x/auth/legacy/v039/types.go b/x/auth/legacy/v039/types.go new file mode 100644 index 000000000000..55c3014eb7f0 --- /dev/null +++ b/x/auth/legacy/v039/types.go @@ -0,0 +1,429 @@ +package v039 + +// DONTCOVER +// nolint + +import ( + "bytes" + "errors" + "fmt" + "strings" + + tmcrypto "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" +) + +const ( + ModuleName = "auth" +) + +type ( + GenesisState struct { + Params v034auth.Params `json:"params" yaml:"params"` + Accounts v038auth.GenesisAccounts `json:"accounts" yaml:"accounts"` + } + + BaseAccount struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey cryptotypes.PubKey `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + } + + BaseVestingAccount struct { + *BaseAccount + + OriginalVesting sdk.Coins `json:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting"` + + EndTime int64 `json:"end_time"` + } + + vestingAccountJSON struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins"` + PubKey cryptotypes.PubKey `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + // custom fields based on concrete vesting type which can be omitted + StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` + VestingPeriods Periods `json:"vesting_periods,omitempty" yaml:"vesting_periods,omitempty"` + } + + ContinuousVestingAccount struct { + *BaseVestingAccount + + StartTime int64 `json:"start_time"` + } + + DelayedVestingAccount struct { + *BaseVestingAccount + } + + Period struct { + Length int64 `json:"length" yaml:"length"` // length of the period, in seconds + Amount sdk.Coins `json:"amount" yaml:"amount"` // amount of coins vesting during this period + } + + Periods []Period + + PeriodicVestingAccount struct { + *BaseVestingAccount + StartTime int64 `json:"start_time" yaml:"start_time"` // when the coins start to vest + VestingPeriods Periods `json:"vesting_periods" yaml:"vesting_periods"` // the vesting schedule + } + + ModuleAccount struct { + *BaseAccount + + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } + + moduleAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } +) + +func NewGenesisState(params v034auth.Params, accounts v038auth.GenesisAccounts) GenesisState { + return GenesisState{ + Params: params, + Accounts: accounts, + } +} + +func NewBaseAccountWithAddress(addr sdk.AccAddress) BaseAccount { + return BaseAccount{ + Address: addr, + } +} + +func NewBaseAccount( + address sdk.AccAddress, coins sdk.Coins, pk cryptotypes.PubKey, accountNumber, sequence uint64, +) *BaseAccount { + + return &BaseAccount{ + Address: address, + Coins: coins, + PubKey: pk, + AccountNumber: accountNumber, + Sequence: sequence, + } +} + +func (acc BaseAccount) GetAddress() sdk.AccAddress { + return acc.Address +} + +func (acc *BaseAccount) GetAccountNumber() uint64 { + return acc.AccountNumber +} + +func (acc *BaseAccount) GetCoins() sdk.Coins { + return acc.Coins +} + +func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { + acc.Coins = coins + return nil +} + +func (acc BaseAccount) Validate() error { + if acc.PubKey != nil && acc.Address != nil && + !bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) { + return errors.New("pubkey and address pair is invalid") + } + + return nil +} + +func NewBaseVestingAccount( + baseAccount *BaseAccount, originalVesting, delegatedFree, delegatedVesting sdk.Coins, endTime int64, +) *BaseVestingAccount { + + return &BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: originalVesting, + DelegatedFree: delegatedFree, + DelegatedVesting: delegatedVesting, + EndTime: endTime, + } +} + +func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: bva.Address, + Coins: bva.Coins, + PubKey: bva.PubKey, + AccountNumber: bva.AccountNumber, + Sequence: bva.Sequence, + OriginalVesting: bva.OriginalVesting, + DelegatedFree: bva.DelegatedFree, + DelegatedVesting: bva.DelegatedVesting, + EndTime: bva.EndTime, + } + + return legacy.Cdc.MarshalJSON(alias) +} + +func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := legacy.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + bva.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence) + bva.OriginalVesting = alias.OriginalVesting + bva.DelegatedFree = alias.DelegatedFree + bva.DelegatedVesting = alias.DelegatedVesting + bva.EndTime = alias.EndTime + + return nil +} + +func (bva BaseVestingAccount) GetEndTime() int64 { + return bva.EndTime +} + +func (bva BaseVestingAccount) Validate() error { + return bva.BaseAccount.Validate() +} + +func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *ContinuousVestingAccount { + return &ContinuousVestingAccount{ + BaseVestingAccount: bva, + StartTime: startTime, + } +} + +func (cva ContinuousVestingAccount) Validate() error { + if cva.StartTime >= cva.EndTime { + return errors.New("vesting start-time cannot be before end-time") + } + + return cva.BaseVestingAccount.Validate() +} + +func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: cva.Address, + Coins: cva.Coins, + PubKey: cva.PubKey, + AccountNumber: cva.AccountNumber, + Sequence: cva.Sequence, + OriginalVesting: cva.OriginalVesting, + DelegatedFree: cva.DelegatedFree, + DelegatedVesting: cva.DelegatedVesting, + EndTime: cva.EndTime, + StartTime: cva.StartTime, + } + + return legacy.Cdc.MarshalJSON(alias) +} + +func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := legacy.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + cva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + cva.StartTime = alias.StartTime + + return nil +} + +func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount { + return &DelayedVestingAccount{ + BaseVestingAccount: bva, + } +} + +func (dva DelayedVestingAccount) Validate() error { + return dva.BaseVestingAccount.Validate() +} + +func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: dva.Address, + Coins: dva.Coins, + PubKey: dva.PubKey, + AccountNumber: dva.AccountNumber, + Sequence: dva.Sequence, + OriginalVesting: dva.OriginalVesting, + DelegatedFree: dva.DelegatedFree, + DelegatedVesting: dva.DelegatedVesting, + EndTime: dva.EndTime, + } + + return legacy.Cdc.MarshalJSON(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. +func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := legacy.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + dva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + + return nil +} + +func (pva PeriodicVestingAccount) GetStartTime() int64 { + return pva.StartTime +} + +func (pva PeriodicVestingAccount) Validate() error { + if pva.GetStartTime() >= pva.GetEndTime() { + return errors.New("vesting start-time cannot be before end-time") + } + endTime := pva.StartTime + originalVesting := sdk.NewCoins() + for _, p := range pva.VestingPeriods { + endTime += p.Length + originalVesting = originalVesting.Add(p.Amount...) + } + if endTime != pva.EndTime { + return errors.New("vesting end time does not match length of all vesting periods") + } + if !originalVesting.IsEqual(pva.OriginalVesting) { + return errors.New("original vesting coins does not match the sum of all coins in vesting periods") + } + + return pva.BaseVestingAccount.Validate() +} + +func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: pva.Address, + Coins: pva.Coins, + PubKey: pva.PubKey, + AccountNumber: pva.AccountNumber, + Sequence: pva.Sequence, + OriginalVesting: pva.OriginalVesting, + DelegatedFree: pva.DelegatedFree, + DelegatedVesting: pva.DelegatedVesting, + EndTime: pva.EndTime, + StartTime: pva.StartTime, + VestingPeriods: pva.VestingPeriods, + } + + return legacy.Cdc.MarshalJSON(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. +func (pva *PeriodicVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := legacy.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + pva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + pva.StartTime = alias.StartTime + pva.VestingPeriods = alias.VestingPeriods + + return nil +} + +func NewModuleAccount(baseAccount *BaseAccount, name string, permissions ...string) *ModuleAccount { + return &ModuleAccount{ + BaseAccount: baseAccount, + Name: name, + Permissions: permissions, + } +} + +func (ma ModuleAccount) Validate() error { + if err := v038auth.ValidatePermissions(ma.Permissions...); err != nil { + return err + } + + if strings.TrimSpace(ma.Name) == "" { + return errors.New("module account name cannot be blank") + } + + if x := sdk.AccAddress(tmcrypto.AddressHash([]byte(ma.Name))); !ma.Address.Equals(x) { + return fmt.Errorf("address %s cannot be derived from the module name '%s'; expected: %s", ma.Address, ma.Name, x) + } + + return ma.BaseAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a ModuleAccount. +func (ma ModuleAccount) MarshalJSON() ([]byte, error) { + return legacy.Cdc.MarshalJSON(moduleAccountPretty{ + Address: ma.Address, + Coins: ma.Coins, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. +func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { + var alias moduleAccountPretty + if err := legacy.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + ma.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) + ma.Name = alias.Name + ma.Permissions = alias.Permissions + + return nil +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cryptocodec.RegisterCrypto(cdc) + cdc.RegisterInterface((*v038auth.GenesisAccount)(nil), nil) + cdc.RegisterInterface((*v038auth.Account)(nil), nil) + cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil) + cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil) + cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil) + cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil) + cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil) + cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) +} diff --git a/x/auth/legacy/v040/migrate.go b/x/auth/legacy/v040/migrate.go new file mode 100644 index 000000000000..363ec7ba82df --- /dev/null +++ b/x/auth/legacy/v040/migrate.go @@ -0,0 +1,126 @@ +package v040 + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/types" + v040vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +// convertBaseAccount converts a 0.39 BaseAccount to a 0.40 BaseAccount. +func convertBaseAccount(old *v039auth.BaseAccount) *v040auth.BaseAccount { + var any *codectypes.Any + // If the old genesis had a pubkey, we pack it inside an Any. Or else, we + // just leave it nil. + if old.PubKey != nil { + var err error + any, err = codectypes.NewAnyWithValue(old.PubKey) + if err != nil { + panic(err) + } + } + + return &v040auth.BaseAccount{ + Address: old.Address.String(), + PubKey: any, + AccountNumber: old.AccountNumber, + Sequence: old.Sequence, + } +} + +// convertBaseVestingAccount converts a 0.39 BaseVestingAccount to a 0.40 BaseVestingAccount. +func convertBaseVestingAccount(old *v039auth.BaseVestingAccount) *v040vesting.BaseVestingAccount { + baseAccount := convertBaseAccount(old.BaseAccount) + + return &v040vesting.BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: old.OriginalVesting, + DelegatedFree: old.DelegatedFree, + DelegatedVesting: old.DelegatedVesting, + EndTime: old.EndTime, + } +} + +// Migrate accepts exported x/auth genesis state from v0.38/v0.39 and migrates +// it to v0.40 x/auth genesis state. The migration includes: +// +// - Removing coins from account encoding. +// - Re-encode in v0.40 GenesisState. +func Migrate(authGenState v039auth.GenesisState) *v040auth.GenesisState { + // Convert v0.39 accounts to v0.40 ones. + var v040Accounts = make([]v040auth.GenesisAccount, len(authGenState.Accounts)) + for i, v039Account := range authGenState.Accounts { + switch v039Account := v039Account.(type) { + case *v039auth.BaseAccount: + { + v040Accounts[i] = convertBaseAccount(v039Account) + } + case *v039auth.ModuleAccount: + { + v040Accounts[i] = &v040auth.ModuleAccount{ + BaseAccount: convertBaseAccount(v039Account.BaseAccount), + Name: v039Account.Name, + Permissions: v039Account.Permissions, + } + } + case *v039auth.BaseVestingAccount: + { + v040Accounts[i] = convertBaseVestingAccount(v039Account) + } + case *v039auth.ContinuousVestingAccount: + { + v040Accounts[i] = &v040vesting.ContinuousVestingAccount{ + BaseVestingAccount: convertBaseVestingAccount(v039Account.BaseVestingAccount), + StartTime: v039Account.StartTime, + } + } + case *v039auth.DelayedVestingAccount: + { + v040Accounts[i] = &v040vesting.DelayedVestingAccount{ + BaseVestingAccount: convertBaseVestingAccount(v039Account.BaseVestingAccount), + } + } + case *v039auth.PeriodicVestingAccount: + { + vestingPeriods := make([]v040vesting.Period, len(v039Account.VestingPeriods)) + for j, period := range v039Account.VestingPeriods { + vestingPeriods[j] = v040vesting.Period{ + Length: period.Length, + Amount: period.Amount, + } + } + v040Accounts[i] = &v040vesting.PeriodicVestingAccount{ + BaseVestingAccount: convertBaseVestingAccount(v039Account.BaseVestingAccount), + StartTime: v039Account.StartTime, + VestingPeriods: vestingPeriods, + } + } + default: + panic(sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "got invalid type %T", v039Account)) + } + + } + + // Convert v0.40 accounts into Anys. + anys := make([]*codectypes.Any, len(v040Accounts)) + for i, v040Account := range v040Accounts { + any, err := codectypes.NewAnyWithValue(v040Account) + if err != nil { + panic(err) + } + + anys[i] = any + } + + return &v040auth.GenesisState{ + Params: v040auth.Params{ + MaxMemoCharacters: authGenState.Params.MaxMemoCharacters, + TxSigLimit: authGenState.Params.TxSigLimit, + TxSizeCostPerByte: authGenState.Params.TxSizeCostPerByte, + SigVerifyCostED25519: authGenState.Params.SigVerifyCostED25519, + SigVerifyCostSecp256k1: authGenState.Params.SigVerifyCostSecp256k1, + }, + Accounts: anys, + } +} diff --git a/x/auth/legacy/v040/migrate_test.go b/x/auth/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..e24104609547 --- /dev/null +++ b/x/auth/legacy/v040/migrate_test.go @@ -0,0 +1,255 @@ +package v040_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + v034 "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + coins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + // BaseAccount + pk1 := secp256k1.GenPrivKeyFromSecret([]byte("acc1")).PubKey() + acc1 := v039auth.NewBaseAccount(sdk.AccAddress(pk1.Address()), coins, pk1, 1, 0) + + // ModuleAccount + pk2 := secp256k1.GenPrivKeyFromSecret([]byte("acc2")).PubKey() + acc2 := v039auth.NewModuleAccount( + v039auth.NewBaseAccount(sdk.AccAddress(pk2.Address()), coins, pk2, 1, 0), + "module2", + "permission2", + ) + + // BaseVestingAccount + pk3 := secp256k1.GenPrivKeyFromSecret([]byte("acc3")).PubKey() + acc3 := v039auth.NewBaseVestingAccount( + v039auth.NewBaseAccount(sdk.AccAddress(pk3.Address()), coins, pk3, 1, 0), + coins, coins, coins, + 1580309973, + ) + + // ContinuousVestingAccount + pk4 := secp256k1.GenPrivKeyFromSecret([]byte("acc4")).PubKey() + acc4 := v039auth.NewContinuousVestingAccountRaw( + v039auth.NewBaseVestingAccount(v039auth.NewBaseAccount(sdk.AccAddress(pk4.Address()), coins, pk4, 1, 0), coins, nil, nil, 3160620846), + 1580309974, + ) + + // PeriodicVestingAccount + pk5 := secp256k1.GenPrivKeyFromSecret([]byte("acc5")).PubKey() + acc5 := &v039auth.PeriodicVestingAccount{ + BaseVestingAccount: v039auth.NewBaseVestingAccount(v039auth.NewBaseAccount(sdk.AccAddress(pk5.Address()), coins, pk5, 1, 0), coins, nil, nil, 3160620846), + StartTime: 1580309975, + VestingPeriods: v039auth.Periods{v039auth.Period{Length: 32, Amount: coins}}, + } + + // DelayedVestingAccount + pk6 := secp256k1.GenPrivKeyFromSecret([]byte("acc6")).PubKey() + acc6 := &v039auth.DelayedVestingAccount{ + BaseVestingAccount: v039auth.NewBaseVestingAccount(v039auth.NewBaseAccount(sdk.AccAddress(pk6.Address()), coins, pk6, 1, 0), coins, nil, nil, 3160620846), + } + + // BaseAccount with nil pubkey (coming from older genesis). + pk7 := secp256k1.GenPrivKeyFromSecret([]byte("acc7")).PubKey() + acc7 := v039auth.NewBaseAccount(sdk.AccAddress(pk7.Address()), coins, nil, 1, 0) + + gs := v039auth.GenesisState{ + Params: v034.Params{ + MaxMemoCharacters: 10, + TxSigLimit: 20, + TxSizeCostPerByte: 30, + SigVerifyCostED25519: 40, + SigVerifyCostSecp256k1: 50, + }, + Accounts: v038auth.GenesisAccounts{acc1, acc2, acc3, acc4, acc5, acc6, acc7}, + } + + migrated := v040auth.Migrate(gs) + expected := `{ + "accounts": [ + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "account_number": "1", + "address": "cosmos13syh7de9xndv9wmklccpfvc0d8dcyvay4s6z6l", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A8oWyJkohwy8XZ0Df92jFMBTtTPMvYJplYIrlEHTKPYk" + }, + "sequence": "0" + }, + { + "@type": "/cosmos.auth.v1beta1.ModuleAccount", + "base_account": { + "account_number": "1", + "address": "cosmos1v57fx2l2rt6ehujuu99u2fw05779m5e2ux4z2h", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AruDygh5HprMOpHOEato85dLgAsybMJVyxBGUa3KuWCr" + }, + "sequence": "0" + }, + "name": "module2", + "permissions": [ + "permission2" + ] + }, + { + "@type": "/cosmos.vesting.v1beta1.BaseVestingAccount", + "base_account": { + "account_number": "1", + "address": "cosmos18hnp9fjflrkeeqn4gmhjhzljusxzmjeartdckw", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A5aEFDIdQHh0OYmNXNv1sHBNURDWWgVkXC2IALcWLLwJ" + }, + "sequence": "0" + }, + "delegated_free": [ + { + "amount": "50", + "denom": "stake" + } + ], + "delegated_vesting": [ + { + "amount": "50", + "denom": "stake" + } + ], + "end_time": "1580309973", + "original_vesting": [ + { + "amount": "50", + "denom": "stake" + } + ] + }, + { + "@type": "/cosmos.vesting.v1beta1.ContinuousVestingAccount", + "base_vesting_account": { + "base_account": { + "account_number": "1", + "address": "cosmos1t9kvvejvk6hjtddx6antck39s206csqduq3ke3", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "AoXDzxwTnljemHxfnJcwrKqODBP6Q2l3K3U3UhVDzyah" + }, + "sequence": "0" + }, + "delegated_free": [], + "delegated_vesting": [], + "end_time": "3160620846", + "original_vesting": [ + { + "amount": "50", + "denom": "stake" + } + ] + }, + "start_time": "1580309974" + }, + { + "@type": "/cosmos.vesting.v1beta1.PeriodicVestingAccount", + "base_vesting_account": { + "base_account": { + "account_number": "1", + "address": "cosmos1s4ss9zquz7skvguechzlk3na635jdrecl0sgy2", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A2a4P4TQ1OKzpfu0eKnCoEtmTvoiclSx0G9higenUGws" + }, + "sequence": "0" + }, + "delegated_free": [], + "delegated_vesting": [], + "end_time": "3160620846", + "original_vesting": [ + { + "amount": "50", + "denom": "stake" + } + ] + }, + "start_time": "1580309975", + "vesting_periods": [ + { + "amount": [ + { + "amount": "50", + "denom": "stake" + } + ], + "length": "32" + } + ] + }, + { + "@type": "/cosmos.vesting.v1beta1.DelayedVestingAccount", + "base_vesting_account": { + "base_account": { + "account_number": "1", + "address": "cosmos1mcc6rwrj4hswf8p9ct82c7lmf77w9tuk07rha4", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "A4tuAfmZlhjK5cjp6ImR704miybHnITVNOyJORdDPFu3" + }, + "sequence": "0" + }, + "delegated_free": [], + "delegated_vesting": [], + "end_time": "3160620846", + "original_vesting": [ + { + "amount": "50", + "denom": "stake" + } + ] + } + }, + { + "@type": "/cosmos.auth.v1beta1.BaseAccount", + "account_number": "1", + "address": "cosmos16ydaqh0fcnh4qt7a3jme4mmztm2qel5axcpw00", + "pub_key": null, + "sequence": "0" + } + ], + "params": { + "max_memo_characters": "10", + "sig_verify_cost_ed25519": "40", + "sig_verify_cost_secp256k1": "50", + "tx_sig_limit": "20", + "tx_size_cost_per_byte": "30" + } +}` + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + require.NoError(t, err) + indentedBz, err := json.MarshalIndent(jsonObj, "", " ") + require.NoError(t, err) + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/auth/legacy/v040/types.go b/x/auth/legacy/v040/types.go new file mode 100644 index 000000000000..1e1f3eee9791 --- /dev/null +++ b/x/auth/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "auth" +) diff --git a/x/auth/legacy/v0_34/types.go b/x/auth/legacy/v0_34/types.go deleted file mode 100644 index b5699838eed1..000000000000 --- a/x/auth/legacy/v0_34/types.go +++ /dev/null @@ -1,26 +0,0 @@ -// DONTCOVER -// nolint -package v0_34 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - ModuleName = "auth" -) - -type ( - Params struct { - MaxMemoCharacters uint64 `json:"max_memo_characters"` - TxSigLimit uint64 `json:"tx_sig_limit"` - TxSizeCostPerByte uint64 `json:"tx_size_cost_per_byte"` - SigVerifyCostED25519 uint64 `json:"sig_verify_cost_ed25519"` - SigVerifyCostSecp256k1 uint64 `json:"sig_verify_cost_secp256k1"` - } - - GenesisState struct { - CollectedFees sdk.Coins `json:"collected_fees"` - Params Params `json:"params"` - } -) diff --git a/x/auth/legacy/v0_36/migrate.go b/x/auth/legacy/v0_36/migrate.go deleted file mode 100644 index 32003f1473f8..000000000000 --- a/x/auth/legacy/v0_36/migrate.go +++ /dev/null @@ -1,14 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 -// genesis state. This migration removes the CollectedFees coins from the old -// FeeCollectorKeeper. -func Migrate(oldGenState v034auth.GenesisState) GenesisState { - return NewGenesisState(oldGenState.Params) -} diff --git a/x/auth/legacy/v0_36/migrate_test.go b/x/auth/legacy/v0_36/migrate_test.go deleted file mode 100644 index faa7238ffa66..000000000000 --- a/x/auth/legacy/v0_36/migrate_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package v0_36 - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/types" - v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" - - "github.com/stretchr/testify/require" -) - -func TestMigrate(t *testing.T) { - var genesisState GenesisState - require.NotPanics(t, func() { - genesisState = Migrate(v034auth.GenesisState{ - CollectedFees: types.Coins{ - { - Amount: types.NewInt(10), - Denom: "stake", - }, - }, - Params: v034auth.Params{}, // forwarded structure: filling and checking will be testing a no-op - }) - }) - require.Equal(t, genesisState, GenesisState{Params: v034auth.Params{}}) -} diff --git a/x/auth/legacy/v0_36/types.go b/x/auth/legacy/v0_36/types.go deleted file mode 100644 index 1b720dff2e1a..000000000000 --- a/x/auth/legacy/v0_36/types.go +++ /dev/null @@ -1,19 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" - -const ( - ModuleName = "auth" -) - -type ( - GenesisState struct { - Params v034auth.Params `json:"params"` - } -) - -func NewGenesisState(params v034auth.Params) GenesisState { - return GenesisState{params} -} diff --git a/x/auth/legacy/v0_38/migrate.go b/x/auth/legacy/v0_38/migrate.go deleted file mode 100644 index e76937777678..000000000000 --- a/x/auth/legacy/v0_38/migrate.go +++ /dev/null @@ -1,52 +0,0 @@ -package v038 - -import ( - v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" - v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.38 -// genesis state. -func Migrate(authGenState v036auth.GenesisState, genAccountsGenState v036genaccounts.GenesisState) GenesisState { - accounts := make(GenesisAccounts, len(genAccountsGenState)) - - for i, acc := range genAccountsGenState { - var genAccount GenesisAccount - - baseAccount := NewBaseAccount(acc.Address, acc.Coins.Sort(), nil, acc.AccountNumber, acc.Sequence) - - switch { - case !acc.OriginalVesting.IsZero(): - baseVestingAccount := NewBaseVestingAccount( - baseAccount, acc.OriginalVesting.Sort(), acc.DelegatedFree.Sort(), - acc.DelegatedVesting.Sort(), acc.EndTime, - ) - - if acc.StartTime != 0 && acc.EndTime != 0 { - // continuous vesting account type - genAccount = NewContinuousVestingAccountRaw(baseVestingAccount, acc.StartTime) - } else if acc.EndTime != 0 { - // delayed vesting account type - genAccount = NewDelayedVestingAccountRaw(baseVestingAccount) - } - - case acc.ModuleName != "": - // module account type - genAccount = NewModuleAccount(baseAccount, acc.ModuleName, acc.ModulePermissions...) - - default: - // standard account type - genAccount = baseAccount - } - - accounts[i] = genAccount - } - - accounts = sanitizeGenesisAccounts(accounts) - - if err := validateGenAccounts(accounts); err != nil { - panic(err) - } - - return NewGenesisState(authGenState.Params, accounts) -} diff --git a/x/auth/legacy/v0_38/migrate_test.go b/x/auth/legacy/v0_38/migrate_test.go deleted file mode 100644 index e7717a70e11d..000000000000 --- a/x/auth/legacy/v0_38/migrate_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package v038 - -import ( - "testing" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" - v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" - v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - - "github.com/stretchr/testify/require" -) - -func accAddressFromBech32(t *testing.T, addrStr string) sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(addrStr) - require.NoError(t, err) - return addr -} - -func TestMigrate(t *testing.T) { - var genesisState GenesisState - - params := v034auth.Params{ - MaxMemoCharacters: 10, - TxSigLimit: 10, - TxSizeCostPerByte: 10, - SigVerifyCostED25519: 10, - SigVerifyCostSecp256k1: 10, - } - - acc1 := v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch1f9xjhxm0plzrh9cskf4qee4pc2xwp0n08fnv4c"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000)), - Sequence: 1, - AccountNumber: 1, - } - acc2 := v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3xxqtmq"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), - Sequence: 4, - AccountNumber: 2, - ModuleName: "bonded_tokens_pool", - ModulePermissions: []string{"burner", "staking"}, - } - acc3 := v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch17n9sztlhx32tfy0tg0zc2ttmkeeth50yhp9p4w"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - StartTime: time.Now().Unix(), - EndTime: time.Now().Add(48 * time.Hour).Unix(), - Sequence: 5, - AccountNumber: 3, - } - acc4 := v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch1fmk5elg4r62mlexd36tqjcwyafs7mek0pdalh6"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - EndTime: time.Now().Add(48 * time.Hour).Unix(), - Sequence: 15, - AccountNumber: 4, - } - - require.NotPanics(t, func() { - genesisState = Migrate( - v036auth.GenesisState{ - Params: params, - }, - v036genaccounts.GenesisState{acc1, acc2, acc3, acc4}, - ) - }) - - expectedAcc1 := NewBaseAccount(acc1.Address, acc1.Coins, nil, acc1.AccountNumber, acc1.Sequence) - expectedAcc2 := NewModuleAccount( - NewBaseAccount(acc2.Address, acc2.Coins, nil, acc2.AccountNumber, acc2.Sequence), - acc2.ModuleName, acc2.ModulePermissions..., - ) - expectedAcc3 := NewContinuousVestingAccountRaw( - NewBaseVestingAccount( - NewBaseAccount(acc3.Address, acc3.Coins, nil, acc3.AccountNumber, acc3.Sequence), - acc3.OriginalVesting, acc3.DelegatedFree, acc3.DelegatedVesting, acc3.EndTime, - ), - acc3.StartTime, - ) - expectedAcc4 := NewDelayedVestingAccountRaw( - NewBaseVestingAccount( - NewBaseAccount(acc4.Address, acc4.Coins, nil, acc4.AccountNumber, acc4.Sequence), - acc4.OriginalVesting, acc4.DelegatedFree, acc4.DelegatedVesting, acc4.EndTime, - ), - ) - - require.Equal( - t, genesisState, GenesisState{ - Params: params, - Accounts: GenesisAccounts{expectedAcc1, expectedAcc2, expectedAcc3, expectedAcc4}, - }, - ) -} - -func TestMigrateInvalid(t *testing.T) { - testCases := []struct { - name string - acc v036genaccounts.GenesisAccount - }{ - { - "vesting account with no base coins", - v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch17n9sztlhx32tfy0tg0zc2ttmkeeth50yhp9p4w"), - Coins: sdk.Coins{}, - OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - StartTime: time.Now().Unix(), - EndTime: time.Now().Add(48 * time.Hour).Unix(), - Sequence: 5, - AccountNumber: 3, - }, - }, - { - "vesting account with base coins <= original vesting", - v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch17n9sztlhx32tfy0tg0zc2ttmkeeth50yhp9p4w"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 10000205)), - OriginalVesting: sdk.NewCoins(sdk.NewInt64Coin("stake", 50000205)), - StartTime: time.Now().Unix(), - EndTime: time.Now().Add(48 * time.Hour).Unix(), - Sequence: 5, - AccountNumber: 3, - }, - }, - { - "module account with invalid name", - v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3xxqtmq"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), - Sequence: 4, - AccountNumber: 2, - ModuleName: " ", - ModulePermissions: []string{"burner", "staking"}, - }, - }, - { - "module account with invalid permissions", - v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3xxqtmq"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), - Sequence: 4, - AccountNumber: 2, - ModuleName: "bonded_tokens_pool", - ModulePermissions: []string{""}, - }, - }, - { - "module account with invalid address", - v036genaccounts.GenesisAccount{ - Address: accAddressFromBech32(t, "fetch17n9sztlhx32tfy0tg0zc2ttmkeeth50yhp9p4w"), - Coins: sdk.NewCoins(sdk.NewInt64Coin("stake", 400000000)), - Sequence: 4, - AccountNumber: 2, - ModuleName: "bonded_tokens_pool", - ModulePermissions: []string{"burner", "staking"}, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - require.Panics(t, func() { - Migrate( - v036auth.GenesisState{ - Params: v034auth.Params{ - MaxMemoCharacters: 10, - TxSigLimit: 10, - TxSizeCostPerByte: 10, - SigVerifyCostED25519: 10, - SigVerifyCostSecp256k1: 10, - }, - }, - v036genaccounts.GenesisState{tc.acc}, - ) - }) - }) - } -} diff --git a/x/auth/legacy/v0_38/types.go b/x/auth/legacy/v0_38/types.go deleted file mode 100644 index 15896a39121f..000000000000 --- a/x/auth/legacy/v0_38/types.go +++ /dev/null @@ -1,532 +0,0 @@ -package v038 - -// DONTCOVER -// nolint - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "sort" - "strings" - - "github.com/tendermint/tendermint/crypto" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" -) - -const ( - ModuleName = "auth" -) - -type ( - // partial interface needed only for amino encoding and sanitization - Account interface { - GetAddress() sdk.AccAddress - GetAccountNumber() uint64 - GetCoins() sdk.Coins - SetCoins(sdk.Coins) error - } - - GenesisAccount interface { - Account - - Validate() error - } - - GenesisAccounts []GenesisAccount - - GenesisState struct { - Params v034auth.Params `json:"params" yaml:"params"` - Accounts GenesisAccounts `json:"accounts" yaml:"accounts"` - } - - BaseAccount struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - } - - baseAccountPretty struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey string `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - } - - BaseVestingAccount struct { - *BaseAccount - - OriginalVesting sdk.Coins `json:"original_vesting"` - DelegatedFree sdk.Coins `json:"delegated_free"` - DelegatedVesting sdk.Coins `json:"delegated_vesting"` - - EndTime int64 `json:"end_time"` - } - - vestingAccountPretty struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey string `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` - DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` - DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` - EndTime int64 `json:"end_time" yaml:"end_time"` - - // custom fields based on concrete vesting type which can be omitted - StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` - } - - ContinuousVestingAccount struct { - *BaseVestingAccount - - StartTime int64 `json:"start_time"` - } - - DelayedVestingAccount struct { - *BaseVestingAccount - } - - ModuleAccount struct { - *BaseAccount - - Name string `json:"name" yaml:"name"` - Permissions []string `json:"permissions" yaml:"permissions"` - } - - moduleAccountPretty struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey string `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - Name string `json:"name" yaml:"name"` - Permissions []string `json:"permissions" yaml:"permissions"` - } -) - -func NewGenesisState(params v034auth.Params, accounts GenesisAccounts) GenesisState { - return GenesisState{ - Params: params, - Accounts: accounts, - } -} - -func NewBaseAccountWithAddress(addr sdk.AccAddress) BaseAccount { - return BaseAccount{ - Address: addr, - } -} - -func NewBaseAccount( - address sdk.AccAddress, coins sdk.Coins, pk crypto.PubKey, accountNumber, sequence uint64, -) *BaseAccount { - - return &BaseAccount{ - Address: address, - Coins: coins, - PubKey: pk, - AccountNumber: accountNumber, - Sequence: sequence, - } -} - -func (acc BaseAccount) GetAddress() sdk.AccAddress { - return acc.Address -} - -func (acc *BaseAccount) GetAccountNumber() uint64 { - return acc.AccountNumber -} - -func (acc *BaseAccount) GetCoins() sdk.Coins { - return acc.Coins -} - -func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { - acc.Coins = coins - return nil -} - -func (acc BaseAccount) Validate() error { - if acc.PubKey != nil && acc.Address != nil && - !bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) { - return errors.New("pubkey and address pair is invalid") - } - - return nil -} - -func (acc BaseAccount) MarshalJSON() ([]byte, error) { - alias := baseAccountPretty{ - Address: acc.Address, - Coins: acc.Coins, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, - } - - if acc.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount. -func (acc *BaseAccount) UnmarshalJSON(bz []byte) error { - var alias baseAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - if alias.PubKey != "" { - pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - - acc.PubKey = pk - } - - acc.Address = alias.Address - acc.Coins = alias.Coins - acc.AccountNumber = alias.AccountNumber - acc.Sequence = alias.Sequence - - return nil -} - -func NewBaseVestingAccount( - baseAccount *BaseAccount, originalVesting, delegatedFree, delegatedVesting sdk.Coins, endTime int64, -) *BaseVestingAccount { - - return &BaseVestingAccount{ - BaseAccount: baseAccount, - OriginalVesting: originalVesting, - DelegatedFree: delegatedFree, - DelegatedVesting: delegatedVesting, - EndTime: endTime, - } -} - -func (bva BaseVestingAccount) Validate() error { - if (bva.Coins.IsZero() && !bva.OriginalVesting.IsZero()) || - bva.OriginalVesting.IsAnyGT(bva.Coins) { - return errors.New("vesting amount cannot be greater than total amount") - } - - return bva.BaseAccount.Validate() -} - -// MarshalJSON returns the JSON representation of a BaseVestingAccount. -func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: bva.Address, - Coins: bva.Coins, - AccountNumber: bva.AccountNumber, - Sequence: bva.Sequence, - OriginalVesting: bva.OriginalVesting, - DelegatedFree: bva.DelegatedFree, - DelegatedVesting: bva.DelegatedVesting, - EndTime: bva.EndTime, - } - - if bva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a BaseVestingAccount. -func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - bva.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) - bva.OriginalVesting = alias.OriginalVesting - bva.DelegatedFree = alias.DelegatedFree - bva.DelegatedVesting = alias.DelegatedVesting - bva.EndTime = alias.EndTime - - return nil -} - -func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *ContinuousVestingAccount { - return &ContinuousVestingAccount{ - BaseVestingAccount: bva, - StartTime: startTime, - } -} - -func (cva ContinuousVestingAccount) Validate() error { - if cva.StartTime >= cva.EndTime { - return errors.New("vesting start-time cannot be before end-time") - } - - return cva.BaseVestingAccount.Validate() -} - -// MarshalJSON returns the JSON representation of a ContinuousVestingAccount. -func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: cva.Address, - Coins: cva.Coins, - AccountNumber: cva.AccountNumber, - Sequence: cva.Sequence, - OriginalVesting: cva.OriginalVesting, - DelegatedFree: cva.DelegatedFree, - DelegatedVesting: cva.DelegatedVesting, - EndTime: cva.EndTime, - StartTime: cva.StartTime, - } - - if cva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a ContinuousVestingAccount. -func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - cva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), - OriginalVesting: alias.OriginalVesting, - DelegatedFree: alias.DelegatedFree, - DelegatedVesting: alias.DelegatedVesting, - EndTime: alias.EndTime, - } - cva.StartTime = alias.StartTime - - return nil -} - -func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount { - return &DelayedVestingAccount{ - BaseVestingAccount: bva, - } -} - -func (dva DelayedVestingAccount) Validate() error { - return dva.BaseVestingAccount.Validate() -} - -// MarshalJSON returns the JSON representation of a DelayedVestingAccount. -func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: dva.Address, - Coins: dva.Coins, - AccountNumber: dva.AccountNumber, - Sequence: dva.Sequence, - OriginalVesting: dva.OriginalVesting, - DelegatedFree: dva.DelegatedFree, - DelegatedVesting: dva.DelegatedVesting, - EndTime: dva.EndTime, - } - - if dva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, dva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. -func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - dva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), - OriginalVesting: alias.OriginalVesting, - DelegatedFree: alias.DelegatedFree, - DelegatedVesting: alias.DelegatedVesting, - EndTime: alias.EndTime, - } - - return nil -} - -func NewModuleAddress(name string) sdk.AccAddress { - return sdk.AccAddress(crypto.AddressHash([]byte(name))) -} - -func NewModuleAccount(baseAccount *BaseAccount, name string, permissions ...string) *ModuleAccount { - return &ModuleAccount{ - BaseAccount: baseAccount, - Name: name, - Permissions: permissions, - } -} - -func (ma ModuleAccount) Validate() error { - if err := validatePermissions(ma.Permissions...); err != nil { - return err - } - - if strings.TrimSpace(ma.Name) == "" { - return errors.New("module account name cannot be blank") - } - - if !ma.Address.Equals(sdk.AccAddress(crypto.AddressHash([]byte(ma.Name)))) { - return fmt.Errorf("address %s cannot be derived from the module name '%s'", ma.Address, ma.Name) - } - - return ma.BaseAccount.Validate() -} - -// MarshalJSON returns the JSON representation of a ModuleAccount. -func (ma ModuleAccount) MarshalJSON() ([]byte, error) { - return json.Marshal(moduleAccountPretty{ - Address: ma.Address, - Coins: ma.Coins, - PubKey: "", - AccountNumber: ma.AccountNumber, - Sequence: ma.Sequence, - Name: ma.Name, - Permissions: ma.Permissions, - }) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. -func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { - var alias moduleAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - ma.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) - ma.Name = alias.Name - ma.Permissions = alias.Permissions - - return nil -} - -func validatePermissions(permissions ...string) error { - for _, perm := range permissions { - if strings.TrimSpace(perm) == "" { - return fmt.Errorf("module permission is empty") - } - } - - return nil -} - -func sanitizeGenesisAccounts(genAccounts GenesisAccounts) GenesisAccounts { - sort.Slice(genAccounts, func(i, j int) bool { - return genAccounts[i].GetAccountNumber() < genAccounts[j].GetAccountNumber() - }) - - for _, acc := range genAccounts { - if err := acc.SetCoins(acc.GetCoins().Sort()); err != nil { - panic(err) - } - } - - return genAccounts -} - -func validateGenAccounts(genAccounts GenesisAccounts) error { - addrMap := make(map[string]bool, len(genAccounts)) - for _, acc := range genAccounts { - - // check for duplicated accounts - addrStr := acc.GetAddress().String() - if _, ok := addrMap[addrStr]; ok { - return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) - } - - addrMap[addrStr] = true - - // check account specific validation - if err := acc.Validate(); err != nil { - return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error()) - } - } - - return nil -} - -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*GenesisAccount)(nil), nil) - cdc.RegisterInterface((*Account)(nil), nil) - cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil) - cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil) - cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil) - cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil) - cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) -} diff --git a/x/auth/module.go b/x/auth/module.go index d3cf501e4f04..73aa9a1066a9 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -1,23 +1,28 @@ package auth import ( + "context" "encoding/json" "fmt" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" - sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -34,21 +39,21 @@ func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterCodec registers the auth module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - types.RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the auth module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the auth // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the auth module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { var data types.GenesisState - if err := types.ModuleCdc.UnmarshalJSON(bz, &data); err != nil { + if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } @@ -56,18 +61,28 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { } // RegisterRESTRoutes registers the REST routes for the auth module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr, types.StoreKey) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { + rest.RegisterRoutes(clientCtx, rtr, types.StoreKey) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the auth module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) } // GetTxCmd returns the root tx command for the auth module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(cdc) +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return nil } // GetQueryCmd returns the root query command for the auth module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(cdc) +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers interfaces and implementations of the auth module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } //____________________________________________________________________________ @@ -76,14 +91,16 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - accountKeeper AccountKeeper + accountKeeper keeper.AccountKeeper + randGenAccountsFn types.RandomGenesisAccountsFn } // NewAppModule creates a new AppModule object -func NewAppModule(accountKeeper AccountKeeper) AppModule { +func NewAppModule(cdc codec.Marshaler, accountKeeper keeper.AccountKeeper, randGenAccountsFn types.RandomGenesisAccountsFn) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - accountKeeper: accountKeeper, + AppModuleBasic: AppModuleBasic{}, + accountKeeper: accountKeeper, + randGenAccountsFn: randGenAccountsFn, } } @@ -96,35 +113,38 @@ func (AppModule) Name() string { func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route returns the message routing key for the auth module. -func (AppModule) Route() string { return "" } - -// NewHandler returns an sdk.Handler for the auth module. -func (AppModule) NewHandler() sdk.Handler { return nil } +func (AppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns the auth module's querier route name. func (AppModule) QuerierRoute() string { return types.QuerierRoute } -// NewQuerierHandler returns the auth module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.accountKeeper) +// LegacyQuerierHandler returns the auth module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.accountKeeper, legacyQuerierCdc) +} + +// 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.accountKeeper) } // InitGenesis performs genesis initialization for the auth module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) +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.accountKeeper, genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the auth // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { gs := ExportGenesis(ctx, am.accountKeeper) - return types.ModuleCdc.MustMarshalJSON(gs) + return cdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the auth module. @@ -141,26 +161,26 @@ func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Validato // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the auth module -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) +func (am AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState, am.randGenAccountsFn) } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return nil } // RandomizedParams creates randomized auth param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for auth module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.accountKeeper) } // WeightedOperations doesn't return any auth module operation. -func (AppModule) WeightedOperations(_ module.SimulationState) []sim.WeightedOperation { +func (AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { return nil } diff --git a/x/auth/module_test.go b/x/auth/module_test.go new file mode 100644 index 000000000000..0bf268ddba5c --- /dev/null +++ b/x/auth/module_test.go @@ -0,0 +1,27 @@ +package auth_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.InitChain( + abcitypes.RequestInitChain{ + AppStateBytes: []byte("{}"), + ChainId: "test-chain-id", + }, + ) + + acc := app.AccountKeeper.GetAccount(ctx, types.NewModuleAddress(types.FeeCollectorName)) + require.NotNil(t, acc) +} diff --git a/x/auth/signing/handler_map.go b/x/auth/signing/handler_map.go new file mode 100644 index 000000000000..936de47da5db --- /dev/null +++ b/x/auth/signing/handler_map.go @@ -0,0 +1,60 @@ +package signing + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/tx/signing" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SignModeHandlerMap is SignModeHandler that aggregates multiple SignModeHandler's into +// a single handler +type SignModeHandlerMap struct { + defaultMode signing.SignMode + modes []signing.SignMode + signModeHandlers map[signing.SignMode]SignModeHandler +} + +var _ SignModeHandler = SignModeHandlerMap{} + +// NewSignModeHandlerMap returns a new SignModeHandlerMap with the provided defaultMode and handlers +func NewSignModeHandlerMap(defaultMode signing.SignMode, handlers []SignModeHandler) SignModeHandlerMap { + handlerMap := make(map[signing.SignMode]SignModeHandler) + var modes []signing.SignMode + + for _, h := range handlers { + for _, m := range h.Modes() { + if _, have := handlerMap[m]; have { + panic(fmt.Errorf("duplicate sign mode handler for mode %s", m)) + } + handlerMap[m] = h + modes = append(modes, m) + } + } + + return SignModeHandlerMap{ + defaultMode: defaultMode, + modes: modes, + signModeHandlers: handlerMap, + } +} + +// DefaultMode implements SignModeHandler.DefaultMode +func (h SignModeHandlerMap) DefaultMode() signing.SignMode { + return h.defaultMode +} + +// Modes implements SignModeHandler.Modes +func (h SignModeHandlerMap) Modes() []signing.SignMode { + return h.modes +} + +// DefaultMode implements SignModeHandler.GetSignBytes +func (h SignModeHandlerMap) GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) { + handler, found := h.signModeHandlers[mode] + if !found { + return nil, fmt.Errorf("can't verify sign mode %s", mode.String()) + } + return handler.GetSignBytes(mode, data, tx) +} diff --git a/x/auth/signing/handler_map_test.go b/x/auth/signing/handler_map_test.go new file mode 100644 index 000000000000..041b8182e95a --- /dev/null +++ b/x/auth/signing/handler_map_test.go @@ -0,0 +1,90 @@ +package signing_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func MakeTestHandlerMap() signing.SignModeHandler { + return signing.NewSignModeHandlerMap( + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + []signing.SignModeHandler{ + legacytx.NewStdTxSignModeHandler(), + }, + ) +} + +func TestHandlerMap_GetSignBytes(t *testing.T) { + priv1 := secp256k1.GenPrivKey() + addr1 := sdk.AccAddress(priv1.PubKey().Address()) + priv2 := secp256k1.GenPrivKey() + addr2 := sdk.AccAddress(priv2.PubKey().Address()) + + coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} + + fee := legacytx.StdFee{ + Amount: coins, + Gas: 10000, + } + memo := "foo" + msgs := []sdk.Msg{ + &banktypes.MsgSend{ + FromAddress: addr1.String(), + ToAddress: addr2.String(), + Amount: coins, + }, + } + + tx := legacytx.StdTx{ + Msgs: msgs, + Fee: fee, + Signatures: nil, + Memo: memo, + } + + var ( + chainId = "test-chain" + accNum uint64 = 7 + seqNum uint64 = 7 + ) + + handler := MakeTestHandlerMap() + aminoJSONHandler := legacytx.NewStdTxSignModeHandler() + + signingData := signing.SignerData{ + ChainID: chainId, + AccountNumber: accNum, + Sequence: seqNum, + } + signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.NoError(t, err) + + expectedSignBz, err := aminoJSONHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.NoError(t, err) + + require.Equal(t, expectedSignBz, signBz) + + // expect error with wrong sign mode + _, err = aminoJSONHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx) + require.Error(t, err) +} + +func TestHandlerMap_DefaultMode(t *testing.T) { + handler := MakeTestHandlerMap() + require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode()) +} + +func TestHandlerMap_Modes(t *testing.T) { + handler := MakeTestHandlerMap() + modes := handler.Modes() + require.Contains(t, modes, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON) + require.Len(t, modes, 1) +} diff --git a/x/auth/signing/sig_verifiable_tx.go b/x/auth/signing/sig_verifiable_tx.go new file mode 100644 index 000000000000..8381ad491aa3 --- /dev/null +++ b/x/auth/signing/sig_verifiable_tx.go @@ -0,0 +1,26 @@ +package signing + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// SigVerifiableTx defines a transaction interface for all signature verification +// handlers. +type SigVerifiableTx interface { + types.Tx + GetSigners() []types.AccAddress + GetPubKeys() []cryptotypes.PubKey // If signer already has pubkey in context, this list will have nil in its place + GetSignaturesV2() ([]signing.SignatureV2, error) +} + +// Tx defines a transaction interface that supports all standard message, signature +// fee, memo, and auxiliary interfaces. +type Tx interface { + SigVerifiableTx + + types.TxWithMemo + types.FeeTx + types.TxWithTimeoutHeight +} diff --git a/x/auth/signing/sign_mode_handler.go b/x/auth/signing/sign_mode_handler.go new file mode 100644 index 000000000000..e70246ee27f8 --- /dev/null +++ b/x/auth/signing/sign_mode_handler.go @@ -0,0 +1,37 @@ +package signing + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// SignModeHandler defines a interface to be implemented by types which will handle +// SignMode's by generating sign bytes from a Tx and SignerData +type SignModeHandler interface { + // DefaultMode is the default mode that is to be used with this handler if no + // other mode is specified. This can be useful for testing and CLI usage + DefaultMode() signing.SignMode + + // Modes is the list of modes supporting by this handler + Modes() []signing.SignMode + + // GetSignBytes returns the sign bytes for the provided SignMode, SignerData and Tx, + // or an error + GetSignBytes(mode signing.SignMode, data SignerData, tx sdk.Tx) ([]byte, error) +} + +// SignerData is the specific information needed to sign a transaction that generally +// isn't included in the transaction body itself +type SignerData struct { + // ChainID is the chain that this transaction is targeted + ChainID string + + // AccountNumber is the account number of the signer + AccountNumber uint64 + + // Sequence is the account sequence number of the signer that is used + // for replay protection. This field is only useful for Legacy Amino signing, + // since in SIGN_MODE_DIRECT the account sequence is already in the signer + // info. + Sequence uint64 +} diff --git a/x/auth/signing/verify.go b/x/auth/signing/verify.go new file mode 100644 index 000000000000..5a5395de69f3 --- /dev/null +++ b/x/auth/signing/verify.go @@ -0,0 +1,41 @@ +package signing + +import ( + "fmt" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// VerifySignature verifies a transaction signature contained in SignatureData abstracting over different signing modes +// and single vs multi-signatures. +func VerifySignature(pubKey cryptotypes.PubKey, signerData SignerData, sigData signing.SignatureData, handler SignModeHandler, tx sdk.Tx) error { + switch data := sigData.(type) { + case *signing.SingleSignatureData: + signBytes, err := handler.GetSignBytes(data.SignMode, signerData, tx) + if err != nil { + return err + } + if !pubKey.VerifySignature(signBytes, data.Signature) { + return fmt.Errorf("unable to verify single signer signature") + } + return nil + + case *signing.MultiSignatureData: + multiPK, ok := pubKey.(multisig.PubKey) + if !ok { + return fmt.Errorf("expected %T, got %T", (multisig.PubKey)(nil), pubKey) + } + err := multiPK.VerifyMultisignature(func(mode signing.SignMode) ([]byte, error) { + return handler.GetSignBytes(mode, signerData, tx) + }, data) + if err != nil { + return err + } + return nil + default: + return fmt.Errorf("unexpected SignatureData %T", sigData) + } +} diff --git a/x/auth/signing/verify_test.go b/x/auth/signing/verify_test.go new file mode 100644 index 000000000000..7e842d13537c --- /dev/null +++ b/x/auth/signing/verify_test.go @@ -0,0 +1,105 @@ +package signing_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "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" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func TestVerifySignature(t *testing.T) { + priv, pubKey, addr := testdata.KeyTestPubAddr() + priv1, pubKey1, addr1 := testdata.KeyTestPubAddr() + + const ( + memo = "testmemo" + chainId = "test-chain" + ) + + app, ctx := createTestApp(false) + ctx = ctx.WithBlockHeight(1) + + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + types.RegisterLegacyAminoCodec(cdc) + cdc.RegisterConcrete(testdata.TestMsg{}, "cosmos-sdk/Test", nil) + + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + _ = 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)) + acc, err := ante.GetSignerAcc(ctx, app.AccountKeeper, addr) + require.NoError(t, app.BankKeeper.SetBalances(ctx, addr, balances)) + + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + fee := legacytx.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) + signerData := signing.SignerData{ + ChainID: chainId, + AccountNumber: acc.GetAccountNumber(), + Sequence: acc.GetSequence(), + } + signBytes := legacytx.StdSignBytes(signerData.ChainID, signerData.AccountNumber, signerData.Sequence, 10, fee, msgs, memo) + signature, err := priv.Sign(signBytes) + require.NoError(t, err) + + stdSig := legacytx.StdSignature{PubKey: pubKey, Signature: signature} + sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig) + require.NoError(t, err) + + handler := MakeTestHandlerMap() + stdTx := legacytx.NewStdTx(msgs, fee, []legacytx.StdSignature{stdSig}, memo) + stdTx.TimeoutHeight = 10 + err = signing.VerifySignature(pubKey, signerData, sigV2.Data, handler, stdTx) + require.NoError(t, err) + + pkSet := []cryptotypes.PubKey{pubKey, pubKey1} + multisigKey := kmultisig.NewLegacyAminoPubKey(2, pkSet) + multisignature := multisig.NewMultisig(2) + msgs = []sdk.Msg{testdata.NewTestMsg(addr, addr1)} + multiSignBytes := legacytx.StdSignBytes(signerData.ChainID, signerData.AccountNumber, signerData.Sequence, 10, fee, msgs, memo) + + sig1, err := priv.Sign(multiSignBytes) + require.NoError(t, err) + stdSig1 := legacytx.StdSignature{PubKey: pubKey, Signature: sig1} + sig1V2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig1) + require.NoError(t, err) + + sig2, err := priv1.Sign(multiSignBytes) + require.NoError(t, err) + stdSig2 := legacytx.StdSignature{PubKey: pubKey, Signature: sig2} + sig2V2, err := legacytx.StdSignatureToSignatureV2(cdc, stdSig2) + require.NoError(t, err) + + err = multisig.AddSignatureFromPubKey(multisignature, sig1V2.Data, pkSet[0], pkSet) + require.NoError(t, err) + err = multisig.AddSignatureFromPubKey(multisignature, sig2V2.Data, pkSet[1], pkSet) + require.NoError(t, err) + + stdTx = legacytx.NewStdTx(msgs, fee, []legacytx.StdSignature{stdSig1, stdSig2}, memo) + stdTx.TimeoutHeight = 10 + + err = signing.VerifySignature(multisigKey, signerData, multisignature, handler, stdTx) + require.NoError(t, 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, types.DefaultParams()) + + return app, ctx +} diff --git a/x/auth/simulation/decoder.go b/x/auth/simulation/decoder.go index 725d82fc0a64..2fdf752037b3 100644 --- a/x/auth/simulation/decoder.go +++ b/x/auth/simulation/decoder.go @@ -4,27 +4,45 @@ import ( "bytes" "fmt" - tmkv "github.com/tendermint/tendermint/libs/kv" + gogotypes "github.com/gogo/protobuf/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding auth type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.AddressStoreKeyPrefix): - var accA, accB exported.Account - cdc.MustUnmarshalBinaryBare(kvA.Value, &accA) - cdc.MustUnmarshalBinaryBare(kvB.Value, &accB) - return fmt.Sprintf("%v\n%v", accA, accB) - case bytes.Equal(kvA.Key, types.GlobalAccountNumberKey): - var globalAccNumberA, globalAccNumberB uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &globalAccNumberA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &globalAccNumberB) - return fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumberA, globalAccNumberB) - default: - panic(fmt.Sprintf("invalid account key %X", kvA.Key)) +type AuthUnmarshaler interface { + UnmarshalAccount([]byte) (types.AccountI, error) + GetCodec() codec.BinaryMarshaler +} + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding auth type. +func NewDecodeStore(ak AuthUnmarshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.AddressStoreKeyPrefix): + accA, err := ak.UnmarshalAccount(kvA.Value) + if err != nil { + panic(err) + } + + accB, err := ak.UnmarshalAccount(kvB.Value) + if err != nil { + panic(err) + } + + return fmt.Sprintf("%v\n%v", accA, accB) + + case bytes.Equal(kvA.Key, types.GlobalAccountNumberKey): + var globalAccNumberA, globalAccNumberB gogotypes.UInt64Value + ak.GetCodec().MustUnmarshalBinaryBare(kvA.Value, &globalAccNumberA) + ak.GetCodec().MustUnmarshalBinaryBare(kvB.Value, &globalAccNumberB) + + return fmt.Sprintf("GlobalAccNumberA: %d\nGlobalAccNumberB: %d", globalAccNumberA, globalAccNumberB) + + default: + panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key)) + } } } diff --git a/x/auth/simulation/decoder_test.go b/x/auth/simulation/decoder_test.go index 2ef143f15ef9..c7ececcf95cc 100644 --- a/x/auth/simulation/decoder_test.go +++ b/x/auth/simulation/decoder_test.go @@ -1,16 +1,17 @@ -package simulation +package simulation_test import ( "fmt" "testing" + gogotypes "github.com/gogo/protobuf/types" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" + "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/auth/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -19,23 +20,32 @@ var ( delAddr1 = sdk.AccAddress(delPk1.Address()) ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) - return -} - func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() + app := simapp.Setup(false) + cdc, _ := simapp.MakeCodecs() acc := types.NewBaseAccountWithAddress(delAddr1) - globalAccNumber := uint64(10) + dec := simulation.NewDecodeStore(app.AccountKeeper) + + accBz, err := app.AccountKeeper.MarshalAccount(acc) + require.NoError(t, err) + + globalAccNumber := gogotypes.UInt64Value{Value: 10} - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.AddressStoreKey(delAddr1), Value: cdc.MustMarshalBinaryBare(acc)}, - tmkv.Pair{Key: types.GlobalAccountNumberKey, Value: cdc.MustMarshalBinaryLengthPrefixed(globalAccNumber)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: types.AddressStoreKey(delAddr1), + Value: accBz, + }, + { + Key: types.GlobalAccountNumberKey, + Value: cdc.MustMarshalBinaryBare(&globalAccNumber), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, } tests := []struct { name string @@ -51,9 +61,9 @@ func TestDecodeStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/auth/simulation/genesis.go b/x/auth/simulation/genesis.go index 808d81ac296f..2da36b54fa82 100644 --- a/x/auth/simulation/genesis.go +++ b/x/auth/simulation/genesis.go @@ -1,18 +1,15 @@ package simulation -// DONTCOVER - import ( + "encoding/json" "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - "github.com/cosmos/cosmos-sdk/x/simulation" ) // Simulation parameter constants @@ -24,6 +21,44 @@ const ( SigVerifyCostSECP256K1 = "sig_verify_cost_secp256k1" ) +// RandomGenesisAccounts defines the default RandomGenesisAccountsFn used on the SDK. +// It creates a slice of BaseAccount, ContinuousVestingAccount and DelayedVestingAccount. +func RandomGenesisAccounts(simState *module.SimulationState) types.GenesisAccounts { + genesisAccs := make(types.GenesisAccounts, len(simState.Accounts)) + for i, acc := range simState.Accounts { + bacc := types.NewBaseAccountWithAddress(acc.Address) + + // Only consider making a vesting account once the initial bonded validator + // set is exhausted due to needing to track DelegatedVesting. + if !(int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50) { + genesisAccs[i] = bacc + continue + } + + initialVesting := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, simState.Rand.Int63n(simState.InitialStake))) + var endTime int64 + + startTime := simState.GenTimestamp.Unix() + + // Allow for some vesting accounts to vest very quickly while others very slowly. + if simState.Rand.Intn(100) < 50 { + endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*24*30)))) + } else { + endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*12)))) + } + + bva := vestingtypes.NewBaseVestingAccount(bacc, initialVesting, endTime) + + if simState.Rand.Intn(100) < 50 { + genesisAccs[i] = vestingtypes.NewContinuousVestingAccountRaw(bva, startTime) + } else { + genesisAccs[i] = vestingtypes.NewDelayedVestingAccountRaw(bva) + } + } + + return genesisAccs +} + // GenMaxMemoChars randomized MaxMemoChars func GenMaxMemoChars(r *rand.Rand) uint64 { return uint64(simulation.RandIntBetween(r, 100, 200)) @@ -53,7 +88,7 @@ func GenSigVerifyCostSECP256K1(r *rand.Rand) uint64 { } // RandomizedGenState generates a random GenesisState for auth -func RandomizedGenState(simState *module.SimulationState) { +func RandomizedGenState(simState *module.SimulationState, randGenAccountsFn types.RandomGenesisAccountsFn) { var maxMemoChars uint64 simState.AppParams.GetOrGenerate( simState.Cdc, MaxMemoChars, &maxMemoChars, simState.Rand, @@ -86,47 +121,14 @@ func RandomizedGenState(simState *module.SimulationState) { params := types.NewParams(maxMemoChars, txSigLimit, txSizeCostPerByte, sigVerifyCostED25519, sigVerifyCostSECP256K1) - genesisAccs := RandomGenesisAccounts(simState) + genesisAccs := randGenAccountsFn(simState) authGenesis := types.NewGenesisState(params, genesisAccs) - fmt.Printf("Selected randomly generated auth parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, authGenesis.Params)) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis) -} - -// RandomGenesisAccounts returns randomly generated genesis accounts -func RandomGenesisAccounts(simState *module.SimulationState) (genesisAccs exported.GenesisAccounts) { - for i, acc := range simState.Accounts { - coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))} - bacc := types.NewBaseAccountWithAddress(acc.Address) - if err := bacc.SetCoins(coins); err != nil { - panic(err) - } - - var gacc exported.GenesisAccount = &bacc - - // Only consider making a vesting account once the initial bonded validator - // set is exhausted due to needing to track DelegatedVesting. - if int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50 { - var endTime int64 - - startTime := simState.GenTimestamp.Unix() - - // Allow for some vesting accounts to vest very quickly while others very slowly. - if simState.Rand.Intn(100) < 50 { - endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*24*30)))) - } else { - endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*12)))) - } - - if simState.Rand.Intn(100) < 50 { - gacc = vestingtypes.NewContinuousVestingAccount(&bacc, startTime, endTime) - } else { - gacc = vestingtypes.NewDelayedVestingAccount(&bacc, endTime) - } - } - genesisAccs = append(genesisAccs, gacc) + bz, err := json.MarshalIndent(&authGenesis.Params, "", " ") + if err != nil { + panic(err) } - - return genesisAccs + fmt.Printf("Selected randomly generated auth parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis) } diff --git a/x/auth/simulation/genesis_test.go b/x/auth/simulation/genesis_test.go new file mode 100644 index 000000000000..830a264ff4c6 --- /dev/null +++ b/x/auth/simulation/genesis_test.go @@ -0,0 +1,55 @@ +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/auth/simulation" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +func TestRandomizedGenState(t *testing.T) { + registry := codectypes.NewInterfaceRegistry() + types.RegisterInterfaces(registry) + cdc := codec.NewProtoCodec(registry) + + 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, simulation.RandomGenesisAccounts) + + var authGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &authGenesis) + + require.Equal(t, uint64(0x8c), authGenesis.Params.GetMaxMemoCharacters()) + require.Equal(t, uint64(0x2b6), authGenesis.Params.GetSigVerifyCostED25519()) + require.Equal(t, uint64(0x1ff), authGenesis.Params.GetSigVerifyCostSecp256k1()) + require.Equal(t, uint64(9), authGenesis.Params.GetTxSigLimit()) + require.Equal(t, uint64(5), authGenesis.Params.GetTxSizeCostPerByte()) + + genAccounts, err := types.UnpackAccounts(authGenesis.Accounts) + require.NoError(t, err) + require.Len(t, genAccounts, 3) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", genAccounts[2].GetAddress().String()) + require.Equal(t, uint64(0), genAccounts[2].GetAccountNumber()) + require.Equal(t, uint64(0), genAccounts[2].GetSequence()) +} diff --git a/x/auth/simulation/params.go b/x/auth/simulation/params.go index afca0348d3a5..5ce9ac95fcc5 100644 --- a/x/auth/simulation/params.go +++ b/x/auth/simulation/params.go @@ -6,6 +6,7 @@ import ( "fmt" "math/rand" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -18,8 +19,8 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keyMaxMemoCharacters, func(r *rand.Rand) string { return fmt.Sprintf("\"%d\"", GenMaxMemoChars(r)) diff --git a/x/auth/simulation/params_test.go b/x/auth/simulation/params_test.go new file mode 100644 index 000000000000..b8fb384a80f6 --- /dev/null +++ b/x/auth/simulation/params_test.go @@ -0,0 +1,37 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/auth/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"auth/MaxMemoCharacters", "MaxMemoCharacters", "\"181\"", "auth"}, + {"auth/TxSigLimit", "TxSigLimit", "\"7\"", "auth"}, + {"auth/TxSizeCostPerByte", "TxSizeCostPerByte", "\"12\"", "auth"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 3) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} 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/02_state.md b/x/auth/spec/02_state.md index 0d1c29d90d77..b9008603d158 100644 --- a/x/auth/spec/02_state.md +++ b/x/auth/spec/02_state.md @@ -15,30 +15,39 @@ Accounts are exposed externally as an interface, and stored internally as either a base account or vesting account. Module clients wishing to add more account types may do so. -- `0x01 | Address -> amino(account)` +- `0x01 | Address -> ProtocolBuffer(account)` ### Account Interface The account interface exposes methods to read and write standard account information. -Note that all of these methods operate on an account struct confirming to the interface -- in order to write the account to the store, the account keeper will need to be used. +Note that all of these methods operate on an account struct confirming to the +interface - in order to write the account to the store, the account keeper will +need to be used. ```go -type Account interface { - GetAddress() AccAddress - SetAddress(AccAddress) +// AccountI is an interface used to store coins at a given address within state. +// It presumes a notion of sequence numbers for replay protection, +// a notion of account numbers for replay protection for previously pruned accounts, +// and a pubkey for authentication purposes. +// +// Many complex conditions can be used in the concrete struct which implements AccountI. +type AccountI interface { + proto.Message - GetPubKey() PubKey - SetPubKey(PubKey) + GetAddress() sdk.AccAddress + SetAddress(sdk.AccAddress) error // errors if already set. - GetAccountNumber() uint64 - SetAccountNumber(uint64) + GetPubKey() crypto.PubKey // can return nil. + SetPubKey(crypto.PubKey) error - GetSequence() uint64 - SetSequence(uint64) + GetAccountNumber() uint64 + SetAccountNumber(uint64) error - GetCoins() Coins - SetCoins(Coins) + GetSequence() uint64 + SetSequence(uint64) error + + // Ensure that account implements stringer + String() string } ``` @@ -47,16 +56,18 @@ type Account interface { A base account is the simplest and most common account type, which just stores all requisite fields directly in a struct. -```go -type BaseAccount struct { - Address AccAddress - Coins Coins - PubKey PubKey - AccountNumber uint64 - Sequence uint64 +```protobuf +// BaseAccount defines a base account type. It contains all the necessary fields +// for basic account functionality. Any custom account type should extend this +// type for additional functionality (e.g. vesting). +message BaseAccount { + string address = 1; + google.protobuf.Any pub_key = 2; + uint64 account_number = 3; + uint64 sequence = 4; } ``` ### Vesting Account -See [Vesting](vesting.md). +See [Vesting](05_vesting.md). diff --git a/x/auth/spec/03_antehandlers.md b/x/auth/spec/03_antehandlers.md new file mode 100644 index 000000000000..709cae3789f4 --- /dev/null +++ b/x/auth/spec/03_antehandlers.md @@ -0,0 +1,44 @@ + + +# AnthHandlers + +## Handlers + +The auth module presently has no transaction handlers of its own, but does expose +the special `AnteHandler`, used for performing basic validity checks on a transaction, +such that it could be thrown out of the mempool. Note that the ante handler is called on +`CheckTx`, but _also_ on `DeliverTx`, as Tendermint proposers presently have the ability +to include in their proposed block transactions which fail `CheckTx`. + +### Ante Handler + +```go +anteHandler(ak AccountKeeper, fck FeeCollectionKeeper, tx sdk.Tx) + if !tx.(StdTx) + fail with "not a StdTx" + + if isCheckTx and tx.Fee < config.SubjectiveMinimumFee + fail with "insufficient fee for mempool inclusion" + + if tx.ValidateBasic() != nil + fail with "tx failed ValidateBasic" + + if tx.Fee > 0 + account = GetAccount(tx.GetSigners()[0]) + coins := acount.GetCoins() + if coins < tx.Fee + fail with "insufficient fee to pay for transaction" + account.SetCoins(coins - tx.Fee) + fck.AddCollectedFees(tx.Fee) + + for index, signature in tx.GetSignatures() + account = GetAccount(tx.GetSigners()[index]) + bytesToSign := StdSignBytes(chainID, acc.GetAccountNumber(), + acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo) + if !signature.Verify(bytesToSign) + fail with "invalid signature" + + return +``` diff --git a/x/auth/spec/03_messages.md b/x/auth/spec/03_messages.md deleted file mode 100644 index 37834d8d1627..000000000000 --- a/x/auth/spec/03_messages.md +++ /dev/null @@ -1,46 +0,0 @@ - - -# Messages - -TODO make this file conform to typical messages spec - -## Handlers - -The auth module presently has no transaction handlers of its own, but does expose -the special `AnteHandler`, used for performing basic validity checks on a transaction, -such that it could be thrown out of the mempool. Note that the ante handler is called on -`CheckTx`, but *also* on `DeliverTx`, as Tendermint proposers presently have the ability -to include in their proposed block transactions which fail `CheckTx`. - -### Ante Handler - -```go -anteHandler(ak AccountKeeper, fck FeeCollectionKeeper, tx sdk.Tx) - if !tx.(StdTx) - fail with "not a StdTx" - - if isCheckTx and tx.Fee < config.SubjectiveMinimumFee - fail with "insufficient fee for mempool inclusion" - - if tx.ValidateBasic() != nil - fail with "tx failed ValidateBasic" - - if tx.Fee > 0 - account = GetAccount(tx.GetSigners()[0]) - coins := acount.GetCoins() - if coins < tx.Fee - fail with "insufficient fee to pay for transaction" - account.SetCoins(coins - tx.Fee) - fck.AddCollectedFees(tx.Fee) - - for index, signature in tx.GetSignatures() - account = GetAccount(tx.GetSigners()[index]) - bytesToSign := StdSignBytes(chainID, acc.GetAccountNumber(), - acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo) - if !signature.Verify(bytesToSign) - fail with "invalid signature" - - return -``` diff --git a/x/auth/spec/03_types.md b/x/auth/spec/03_types.md deleted file mode 100644 index e71be190733f..000000000000 --- a/x/auth/spec/03_types.md +++ /dev/null @@ -1,69 +0,0 @@ - - -# Types - -Besides accounts (specified in [State](02_state.md)), the types exposed by the auth module -are `StdFee`, the combination of an amount and gas limit, `StdSignature`, the combination -of an optional public key and a cryptographic signature as a byte array, `StdTx`, -a struct which implements the `sdk.Tx` interface using `StdFee` and `StdSignature`, and -`StdSignDoc`, a replay-prevention structure for `StdTx` which transaction senders must sign over. - -## StdFee - -A `StdFee` is simply the combination of a fee amount, in any number of denominations, -and a gas limit (where dividing the amount by the gas limit gives a "gas price"). - -```go -type StdFee struct { - Amount Coins - Gas uint64 -} -``` - -## StdSignature - -A `StdSignature` is the combination of an optional public key and a cryptographic signature -as a byte array. The SDK is agnostic to particular key or signature formats and supports any -supported by the `PubKey` interface. - -```go -type StdSignature struct { - PubKey PubKey - Signature []byte -} -``` - -## StdTx - -A `StdTx` is a struct which implements the `sdk.Tx` interface, and is likely to be generic -enough to serve the purposes of many Cosmos SDK blockchains. - -```go -type StdTx struct { - Msgs []sdk.Msg - Fee StdFee - Signatures []StdSignature - Memo string -} -``` - -## StdSignDoc - -A `StdSignDoc` is a replay-prevention structure to be signed over, which ensures that -any submitted transaction (which is simply a signature over a particular bytestring) -will only be executable once on a particular blockchain. - -`json.RawMessage` is preferred over using the SDK types for future compatibility. - -```go -type StdSignDoc struct { - AccountNumber uint64 - ChainID string - Fee json.RawMessage - Memo string - Msgs []json.RawMessage - Sequence uint64 -} -``` diff --git a/x/auth/spec/04_keepers.md b/x/auth/spec/04_keepers.md index e154e5383042..f57988b4fa7c 100644 --- a/x/auth/spec/04_keepers.md +++ b/x/auth/spec/04_keepers.md @@ -12,32 +12,33 @@ Presently only one fully-permissioned account keeper is exposed, which has the a all fields of all accounts, and to iterate over all stored accounts. ```go -type AccountKeeper interface { - // Return a new account with the next account number and the specified address. Does not save the new account to the store. - NewAccountWithAddress(AccAddress) Account +// AccountKeeperI is the interface contract that x/auth's keeper implements. +type AccountKeeperI interface { + // Return a new account with the next account number and the specified address. Does not save the new account to the store. + NewAccountWithAddress(sdk.Context, sdk.AccAddress) types.AccountI - // Return a new account with the next account number. Does not save the new account to the store. - NewAccount(Account) Account + // Return a new account with the next account number. Does not save the new account to the store. + NewAccount(sdk.Context, types.AccountI) types.AccountI - // Retrieve an account from the store - GetAccount(AccAddress) Account + // Retrieve an account from the store. + GetAccount(sdk.Context, sdk.AccAddress) types.AccountI - // Set an account in the store - SetAccount(Account) + // Set an account in the store. + SetAccount(sdk.Context, types.AccountI) - // Remove an account from the store - RemoveAccount(Account) + // Remove an account from the store. + RemoveAccount(sdk.Context, types.AccountI) - // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. - IterateAccounts(func(Account) (bool)) + // Iterate over all accounts, calling the provided function. Stop iteraiton when it returns false. + IterateAccounts(sdk.Context, func(types.AccountI) bool) - // Fetch the public key of an account at a specified address - GetPubKey(AccAddress) PubKey + // Fetch the public key of an account at a specified address + GetPubKey(sdk.Context, sdk.AccAddress) (crypto.PubKey, error) - // Fetch the sequence of an account at a specified address - GetSequence(AccAddress) uint64 + // Fetch the sequence of an account at a specified address. + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) - // Fetch the next account number, and increment the internal counter - GetNextAccountNumber() uint64 + // Fetch the next account number, and increment the internal counter. + GetNextAccountNumber(sdk.Context) uint64 } ``` diff --git a/x/auth/spec/05_vesting.md b/x/auth/spec/05_vesting.md index 7cddc26e7c67..a253f7de604b 100644 --- a/x/auth/spec/05_vesting.md +++ b/x/auth/spec/05_vesting.md @@ -6,10 +6,12 @@ order: 6 - [Vesting](#vesting) - [Intro and Requirements](#intro-and-requirements) + - [Note](#note) - [Vesting Account Types](#vesting-account-types) - [Vesting Account Specification](#vesting-account-specification) - [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts) - [Continuously Vesting Accounts](#continuously-vesting-accounts) + - [Periodic Vesting Accounts](#periodic-vesting-accounts) - [Delayed/Discrete Vesting Accounts](#delayeddiscrete-vesting-accounts) - [Transferring/Sending](#transferringsending) - [Keepers/Handlers](#keepershandlers) @@ -22,6 +24,7 @@ order: 6 - [Examples](#examples) - [Simple](#simple) - [Slashing](#slashing) + - [Periodic Vesting](#periodic-vesting) - [Glossary](#glossary) ## Intro and Requirements @@ -67,76 +70,59 @@ having coins fail to vest). // VestingAccount defines an interface that any vesting account type must // implement. type VestingAccount interface { - Account + Account - GetVestedCoins(Time) Coins - GetVestingCoins(Time) Coins + GetVestedCoins(Time) Coins + GetVestingCoins(Time) Coins - // Delegation and undelegation accounting that returns the resulting base - // coins amount. - TrackDelegation(Time, Coins) - TrackUndelegation(Coins) + // TrackDelegation performs internal vesting accounting necessary when + // delegating from a vesting account. It accepts the current block time, the + // delegation amount and balance of all coins whose denomination exists in + // the account's original vesting balance. + TrackDelegation(Time, Coins, Coins) - GetStartTime() int64 - GetEndTime() int64 -} - -// BaseVestingAccount implements the VestingAccount interface. It contains all -// the necessary fields needed for any vesting account implementation. -type BaseVestingAccount struct { - BaseAccount + // TrackUndelegation performs internal vesting accounting necessary when a + // vesting account performs an undelegation. + TrackUndelegation(Coins) - 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 + GetStartTime() int64 + GetEndTime() int64 } +``` +### BaseVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L10-L33 -// ContinuousVestingAccount implements the VestingAccount interface. It -// continuously vests by unlocking coins linearly with respect to time. -type ContinuousVestingAccount struct { - BaseVestingAccount +### ContinuousVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L35-L43 - StartTime int64 // when the coins start to vest -} +### DelayedVestingAccount ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L45-L53 -// 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 -} - -// 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 usage, the existing `Account` interface is updated to contain -the following: +flexibility in account balance usage, the existing `x/bank` `ViewKeeper` interface +is updated to contain the following: ```go -type Account interface { - // ... +type ViewKeeper interface { + // ... + + // Calculates the total locked account balance. + LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Calculates the amount of coins that can be sent to other accounts given - // the current time. - SpendableCoins(Time) Coins + // Calculates the total spendable balance that can be sent to other accounts. + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins } ``` @@ -268,10 +254,31 @@ In other words, a vesting account may transfer the minimum of the base account balance and the base account balance plus the number of currently delegated vesting coins less the number of coins vested so far. +However, given that account balances are tracked via the `x/bank` module and that +we want to avoid loading the entire account balance, we can instead determine +the locked balance, which can be defined as `max(V - DV, 0)`, and infer the +spendable balance from that. + ```go -func (va VestingAccount) SpendableCoins(t Time) Coins { - bc := va.GetCoins() - return min((bc + va.DelegatedVesting) - va.GetVestingCoins(t), bc) +func (va VestingAccount) LockedCoins(t Time) Coins { + return max(va.GetVestingCoins(t) - va.DelegatedVesting, 0) +} +``` + +The `x/bank` `ViewKeeper` can then provide APIs to determine locked and spendable +coins for any account: + +```go +func (k Keeper) LockedCoins(ctx Context, addr AccAddress) Coins { + acc := k.GetAccount(ctx, addr) + if acc != nil { + if acc.IsVesting() { + return acc.LockedCoins(ctx.BlockTime()) + } + } + + // non-vesting accounts do not have any locked coins + return NewCoins() } ``` @@ -281,21 +288,18 @@ The corresponding `x/bank` keeper should appropriately handle sending coins based on if the account is a vesting account or not. ```go -func SendCoins(t Time, from Account, to Account, amount Coins) { - bc := from.GetCoins() - - if isVesting(from) { - sc := from.SpendableCoins(t) - assert(amount <= sc) - } +func (k Keeper) SendCoins(ctx Context, from Account, to Account, amount Coins) { + bc := k.GetBalances(ctx, from) + v := k.LockedCoins(ctx, from) - newCoins := bc - amount + spendable := bc - v + newCoins := spendable - amount assert(newCoins >= 0) - from.SetCoins(bc - amount) - to.SetCoins(amount) + from.SetBalance(newCoins) + to.AddBalance(amount) - // save accounts... + // save balances... } ``` @@ -310,7 +314,8 @@ For a vesting account attempting to delegate `D` coins, the following is perform 5. Set `DF += Y` ```go -func (va VestingAccount) TrackDelegation(t Time, amount Coins) { +func (va VestingAccount) TrackDelegation(t Time, balance Coins, amount Coins) { + assert(balance <= amount) x := min(max(va.GetVestingCoins(t) - va.DelegatedVesting, 0), amount) y := amount - x @@ -326,13 +331,10 @@ fields, so upstream callers MUST modify the `Coins` field by subtracting `amount ```go func DelegateCoins(t Time, from Account, amount Coins) { - bc := from.GetCoins() - assert(amount <= bc) - if isVesting(from) { from.TrackDelegation(t, amount) } else { - from.SetCoins(sc - amount) + from.SetBalance(sc - amount) } // save account... @@ -383,7 +385,7 @@ func UndelegateCoins(to Account, amount Coins) { // save account ... } } else { - AddCoins(to, amount) + AddBalance(to, amount) // save account... } } diff --git a/x/auth/spec/07_params.md b/x/auth/spec/07_params.md index 6ef63ed1215b..1bf24e7f6d6e 100644 --- a/x/auth/spec/07_params.md +++ b/x/auth/spec/07_params.md @@ -7,9 +7,9 @@ order: 7 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/spec/README.md b/x/auth/spec/README.md index ea49a9736efe..f666e0923b2e 100644 --- a/x/auth/spec/README.md +++ b/x/auth/spec/README.md @@ -21,24 +21,19 @@ This module will be used in the Cosmos Hub. ## Contents 1. **[Concepts](01_concepts.md)** - - [Gas & Fees](01_concepts.md#gas-&-fees) + - [Gas & Fees](01_concepts.md#gas-&-fees) 2. **[State](02_state.md)** - - [Accounts](02_state.md#accounts) -3. **[Messages](03_messages.md)** - - [Handlers](03_messages.md#handlers) -4. **[Types](03_types.md)** - - [StdFee](03_types.md#stdfee) - - [StdSignature](03_types.md#stdsignature) - - [StdTx](03_types.md#stdtx) - - [StdSignDoc](03_types.md#stdsigndoc) -5. **[Keepers](04_keepers.md)** - - [Account Keeper](04_keepers.md#account-keeper) -6. **[Vesting](05_vesting.md)** - - [Intro and Requirements](05_vesting.md#intro-and-requirements) - - [Vesting Account Types](05_vesting.md#vesting-account-types) - - [Vesting Account Specification](05_vesting.md#vesting-account-specification) - - [Keepers & Handlers](05_vesting.md#keepers-&-handlers) - - [Genesis Initialization](05_vesting.md#genesis-initialization) - - [Examples](05_vesting.md#examples) - - [Glossary](05_vesting.md#glossary) -7. **[Parameters](07_params.md)** + - [Accounts](02_state.md#accounts) +3. **[AnteHandlers](03_antehandlers.md)** + - [Handlers](03_antehandlers.md#handlers) +4. **[Keepers](04_keepers.md)** + - [Account Keeper](04_keepers.md#account-keeper) +5. **[Vesting](05_vesting.md)** + - [Intro and Requirements](05_vesting.md#intro-and-requirements) + - [Vesting Account Types](05_vesting.md#vesting-account-types) + - [Vesting Account Specification](05_vesting.md#vesting-account-specification) + - [Keepers & Handlers](05_vesting.md#keepers-&-handlers) + - [Genesis Initialization](05_vesting.md#genesis-initialization) + - [Examples](05_vesting.md#examples) + - [Glossary](05_vesting.md#glossary) +6. **[Parameters](07_params.md)** diff --git a/x/auth/testutil/suite.go b/x/auth/testutil/suite.go new file mode 100644 index 000000000000..f8002e68e4e6 --- /dev/null +++ b/x/auth/testutil/suite.go @@ -0,0 +1,310 @@ +package testutil + +import ( + "bytes" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// TxConfigTestSuite provides a test suite that can be used to test that a TxConfig implementation is correct +//nolint:golint // type name will be used as tx.TxConfigTestSuite by other packages, and that stutters; consider calling this GeneratorTestSuite +type TxConfigTestSuite struct { + suite.Suite + TxConfig client.TxConfig +} + +// NewTxConfigTestSuite returns a new TxConfigTestSuite with the provided TxConfig implementation +func NewTxConfigTestSuite(txConfig client.TxConfig) *TxConfigTestSuite { + return &TxConfigTestSuite{TxConfig: txConfig} +} + +func (s *TxConfigTestSuite) TestTxBuilderGetTx() { + txBuilder := s.TxConfig.NewTxBuilder() + tx := txBuilder.GetTx() + s.Require().NotNil(tx) + s.Require().Equal(len(tx.GetMsgs()), 0) +} + +func (s *TxConfigTestSuite) TestTxBuilderSetFeeAmount() { + txBuilder := s.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{ + sdk.NewInt64Coin("atom", 20000000), + } + txBuilder.SetFeeAmount(feeAmount) + feeTx := txBuilder.GetTx() + s.Require().Equal(feeAmount, feeTx.GetFee()) +} + +func (s *TxConfigTestSuite) TestTxBuilderSetGasLimit() { + const newGas uint64 = 300000 + txBuilder := s.TxConfig.NewTxBuilder() + txBuilder.SetGasLimit(newGas) + feeTx := txBuilder.GetTx() + s.Require().Equal(newGas, feeTx.GetGas()) +} + +func (s *TxConfigTestSuite) TestTxBuilderSetMemo() { + const newMemo string = "newfoomemo" + txBuilder := s.TxConfig.NewTxBuilder() + txBuilder.SetMemo(newMemo) + txWithMemo := txBuilder.GetTx() + s.Require().Equal(txWithMemo.GetMemo(), newMemo) +} + +func (s *TxConfigTestSuite) TestTxBuilderSetMsgs() { + _, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + msg1 := testdata.NewTestMsg(addr1) + msg2 := testdata.NewTestMsg(addr2) + msgs := []sdk.Msg{msg1, msg2} + + txBuilder := s.TxConfig.NewTxBuilder() + + err := txBuilder.SetMsgs(msgs...) + s.Require().NoError(err) + tx := txBuilder.GetTx() + s.Require().Equal(msgs, tx.GetMsgs()) + s.Require().Equal([]sdk.AccAddress{addr1, addr2}, tx.GetSigners()) + s.Require().Equal(addr1, tx.FeePayer()) + s.Require().Error(tx.ValidateBasic()) // should fail because of no signatures +} + +func (s *TxConfigTestSuite) TestTxBuilderSetSignatures() { + privKey, pubkey, addr := testdata.KeyTestPubAddr() + privKey2, pubkey2, _ := testdata.KeyTestPubAddr() + multisigPk := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pubkey, pubkey2}) + + txBuilder := s.TxConfig.NewTxBuilder() + + // set test msg + msg := testdata.NewTestMsg(addr) + msigAddr := sdk.AccAddress(multisigPk.Address()) + msg2 := testdata.NewTestMsg(msigAddr) + err := txBuilder.SetMsgs(msg, msg2) + s.Require().NoError(err) + + // check that validation fails + s.Require().Error(txBuilder.GetTx().ValidateBasic()) + + signModeHandler := s.TxConfig.SignModeHandler() + s.Require().Contains(signModeHandler.Modes(), signModeHandler.DefaultMode()) + + // set SignatureV2 without actual signature bytes + seq1 := uint64(2) // Arbitrary account sequence + sigData1 := &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()} + sig1 := signingtypes.SignatureV2{PubKey: pubkey, Data: sigData1, Sequence: seq1} + + mseq := uint64(4) // Arbitrary account sequence + msigData := multisig.NewMultisig(2) + multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()}, 0) + multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()}, 1) + msig := signingtypes.SignatureV2{PubKey: multisigPk, Data: msigData, Sequence: mseq} + + // fail validation without required signers + err = txBuilder.SetSignatures(sig1) + s.Require().NoError(err) + sigTx := txBuilder.GetTx() + s.Require().Error(sigTx.ValidateBasic()) + + err = txBuilder.SetSignatures(sig1, msig) + s.Require().NoError(err) + sigTx = txBuilder.GetTx() + sigsV2, err := sigTx.GetSignaturesV2() + s.Require().NoError(err) + s.Require().Len(sigsV2, 2) + s.Require().True(sigEquals(sig1, sigsV2[0])) + s.Require().True(sigEquals(msig, sigsV2[1])) + s.Require().Equal([]sdk.AccAddress{addr, msigAddr}, sigTx.GetSigners()) + s.Require().NoError(sigTx.ValidateBasic()) + + // sign transaction + signerData := signing.SignerData{ + ChainID: "test", + AccountNumber: 1, + Sequence: seq1, + } + signBytes, err := signModeHandler.GetSignBytes(signModeHandler.DefaultMode(), signerData, sigTx) + s.Require().NoError(err) + sigBz, err := privKey.Sign(signBytes) + s.Require().NoError(err) + + signerData = signing.SignerData{ + ChainID: "test", + AccountNumber: 3, + Sequence: mseq, + } + mSignBytes, err := signModeHandler.GetSignBytes(signModeHandler.DefaultMode(), signerData, sigTx) + s.Require().NoError(err) + mSigBz1, err := privKey.Sign(mSignBytes) + s.Require().NoError(err) + mSigBz2, err := privKey2.Sign(mSignBytes) + s.Require().NoError(err) + msigData = multisig.NewMultisig(2) + multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{ + SignMode: signModeHandler.DefaultMode(), Signature: mSigBz1}, 0) + multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{ + SignMode: signModeHandler.DefaultMode(), Signature: mSigBz2}, 0) + + // set signature + sigData1.Signature = sigBz + sig1 = signingtypes.SignatureV2{PubKey: pubkey, Data: sigData1, Sequence: seq1} + msig = signingtypes.SignatureV2{PubKey: multisigPk, Data: msigData, Sequence: mseq} + err = txBuilder.SetSignatures(sig1, msig) + s.Require().NoError(err) + sigTx = txBuilder.GetTx() + sigsV2, err = sigTx.GetSignaturesV2() + s.Require().NoError(err) + s.Require().Len(sigsV2, 2) + s.Require().True(sigEquals(sig1, sigsV2[0])) + s.Require().True(sigEquals(msig, sigsV2[1])) + s.Require().Equal([]sdk.AccAddress{addr, msigAddr}, sigTx.GetSigners()) + s.Require().NoError(sigTx.ValidateBasic()) +} + +func sigEquals(sig1, sig2 signingtypes.SignatureV2) bool { + if !sig1.PubKey.Equals(sig2.PubKey) { + return false + } + + if sig1.Data == nil && sig2.Data == nil { + return true + } + + return sigDataEquals(sig1.Data, sig2.Data) +} + +func sigDataEquals(data1, data2 signingtypes.SignatureData) bool { + switch data1 := data1.(type) { + case *signingtypes.SingleSignatureData: + data2, ok := data2.(*signingtypes.SingleSignatureData) + if !ok { + return false + } + + if data1.SignMode != data2.SignMode { + return false + } + + return bytes.Equal(data1.Signature, data2.Signature) + case *signingtypes.MultiSignatureData: + data2, ok := data2.(*signingtypes.MultiSignatureData) + if !ok { + return false + } + + if data1.BitArray.ExtraBitsStored != data2.BitArray.ExtraBitsStored { + return false + } + + if !bytes.Equal(data1.BitArray.Elems, data2.BitArray.Elems) { + return false + } + + if len(data1.Signatures) != len(data2.Signatures) { + return false + } + + for i, s := range data1.Signatures { + if !sigDataEquals(s, data2.Signatures[i]) { + return false + } + } + + return true + default: + return false + } +} + +func (s *TxConfigTestSuite) TestTxEncodeDecode() { + log := s.T().Log + _, pubkey, addr := testdata.KeyTestPubAddr() + feeAmount := sdk.Coins{sdk.NewInt64Coin("atom", 150)} + gasLimit := uint64(50000) + memo := "foomemo" + msg := testdata.NewTestMsg(addr) + dummySig := []byte("dummySig") + sig := signingtypes.SignatureV2{ + PubKey: pubkey, + Data: &signingtypes.SingleSignatureData{ + SignMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + Signature: dummySig, + }, + } + + txBuilder := s.TxConfig.NewTxBuilder() + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo(memo) + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + err = txBuilder.SetSignatures(sig) + s.Require().NoError(err) + tx := txBuilder.GetTx() + + log("encode transaction") + txBytes, err := s.TxConfig.TxEncoder()(tx) + s.Require().NoError(err) + s.Require().NotNil(txBytes) + log("decode transaction", s.TxConfig) + tx2, err := s.TxConfig.TxDecoder()(txBytes) + + s.Require().NoError(err) + tx3, ok := tx2.(signing.Tx) + s.Require().True(ok) + s.Require().Equal([]sdk.Msg{msg}, tx3.GetMsgs()) + s.Require().Equal(feeAmount, tx3.GetFee()) + s.Require().Equal(gasLimit, tx3.GetGas()) + s.Require().Equal(memo, tx3.GetMemo()) + tx3Sigs, err := tx3.GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal([]signingtypes.SignatureV2{sig}, tx3Sigs) + s.Require().Equal([]cryptotypes.PubKey{pubkey}, tx3.GetPubKeys()) + + log("JSON encode transaction") + jsonTxBytes, err := s.TxConfig.TxJSONEncoder()(tx) + s.Require().NoError(err) + s.Require().NotNil(jsonTxBytes) + + log("JSON decode transaction") + tx2, err = s.TxConfig.TxJSONDecoder()(jsonTxBytes) + s.Require().NoError(err) + tx3, ok = tx2.(signing.Tx) + s.Require().True(ok) + s.Require().Equal([]sdk.Msg{msg}, tx3.GetMsgs()) + s.Require().Equal(feeAmount, tx3.GetFee()) + s.Require().Equal(gasLimit, tx3.GetGas()) + s.Require().Equal(memo, tx3.GetMemo()) + tx3Sigs, err = tx3.GetSignaturesV2() + s.Require().NoError(err) + s.Require().Equal([]signingtypes.SignatureV2{sig}, tx3Sigs) + s.Require().Equal([]cryptotypes.PubKey{pubkey}, tx3.GetPubKeys()) +} + +func (s *TxConfigTestSuite) TestWrapTxBuilder() { + _, _, addr := testdata.KeyTestPubAddr() + feeAmount := sdk.Coins{sdk.NewInt64Coin("atom", 150)} + gasLimit := uint64(50000) + memo := "foomemo" + msg := testdata.NewTestMsg(addr) + + txBuilder := s.TxConfig.NewTxBuilder() + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo(memo) + err := txBuilder.SetMsgs(msg) + s.Require().NoError(err) + + newTxBldr, err := s.TxConfig.WrapTxBuilder(txBuilder.GetTx()) + s.Require().NoError(err) + s.Require().Equal(txBuilder, newTxBldr) +} diff --git a/x/auth/tx/builder.go b/x/auth/tx/builder.go new file mode 100644 index 000000000000..06e347dfc7fb --- /dev/null +++ b/x/auth/tx/builder.go @@ -0,0 +1,356 @@ +package tx + +import ( + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// wrapper is a wrapper around the tx.Tx proto.Message which retain the raw +// body and auth_info bytes. +type wrapper struct { + tx *tx.Tx + + // bodyBz represents the protobuf encoding of TxBody. This should be encoding + // from the client using TxRaw if the tx was decoded from the wire + bodyBz []byte + + // authInfoBz represents the protobuf encoding of TxBody. This should be encoding + // from the client using TxRaw if the tx was decoded from the wire + authInfoBz []byte + + txBodyHasUnknownNonCriticals bool +} + +var ( + _ authsigning.Tx = &wrapper{} + _ client.TxBuilder = &wrapper{} + _ ante.HasExtensionOptionsTx = &wrapper{} + _ ExtensionOptionsTxBuilder = &wrapper{} + _ ProtoTxProvider = &wrapper{} +) + +// ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions. +type ExtensionOptionsTxBuilder interface { + client.TxBuilder + + SetExtensionOptions(...*codectypes.Any) + SetNonCriticalExtensionOptions(...*codectypes.Any) +} + +func newBuilder() *wrapper { + return &wrapper{ + tx: &tx.Tx{ + Body: &tx.TxBody{}, + AuthInfo: &tx.AuthInfo{ + Fee: &tx.Fee{}, + }, + }, + } +} + +func (w *wrapper) GetMsgs() []sdk.Msg { + return w.tx.GetMsgs() +} + +func (w *wrapper) ValidateBasic() error { + return w.tx.ValidateBasic() +} + +func (w *wrapper) getBodyBytes() []byte { + if len(w.bodyBz) == 0 { + // if bodyBz is empty, then marshal the body. bodyBz will generally + // be set to nil whenever SetBody is called so the result of calling + // this method should always return the correct bytes. Note that after + // decoding bodyBz is derived from TxRaw so that it matches what was + // transmitted over the wire + var err error + w.bodyBz, err = proto.Marshal(w.tx.Body) + if err != nil { + panic(err) + } + } + return w.bodyBz +} + +func (w *wrapper) getAuthInfoBytes() []byte { + if len(w.authInfoBz) == 0 { + // if authInfoBz is empty, then marshal the body. authInfoBz will generally + // be set to nil whenever SetAuthInfo is called so the result of calling + // this method should always return the correct bytes. Note that after + // decoding authInfoBz is derived from TxRaw so that it matches what was + // transmitted over the wire + var err error + w.authInfoBz, err = proto.Marshal(w.tx.AuthInfo) + if err != nil { + panic(err) + } + } + return w.authInfoBz +} + +func (w *wrapper) GetSigners() []sdk.AccAddress { + return w.tx.GetSigners() +} + +func (w *wrapper) GetPubKeys() []cryptotypes.PubKey { + signerInfos := w.tx.AuthInfo.SignerInfos + pks := make([]cryptotypes.PubKey, len(signerInfos)) + + for i, si := range signerInfos { + // NOTE: it is okay to leave this nil if there is no PubKey in the SignerInfo. + // PubKey's can be left unset in SignerInfo. + if si.PublicKey == nil { + continue + } + + pk, ok := si.PublicKey.GetCachedValue().(cryptotypes.PubKey) + if ok { + pks[i] = pk + } + } + + return pks +} + +func (w *wrapper) GetGas() uint64 { + return w.tx.AuthInfo.Fee.GasLimit +} + +func (w *wrapper) GetFee() sdk.Coins { + return w.tx.AuthInfo.Fee.Amount +} + +func (w *wrapper) FeePayer() sdk.AccAddress { + feePayer := w.tx.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 w.GetSigners()[0] +} + +func (w *wrapper) FeeGranter() sdk.AccAddress { + feePayer := w.tx.AuthInfo.Fee.Granter + if feePayer != "" { + granterAddr, err := sdk.AccAddressFromBech32(feePayer) + if err != nil { + panic(err) + } + return granterAddr + } + return nil +} + +func (w *wrapper) GetMemo() string { + return w.tx.Body.Memo +} + +// GetTimeoutHeight returns the transaction's timeout height (if set). +func (w *wrapper) GetTimeoutHeight() uint64 { + return w.tx.Body.TimeoutHeight +} + +func (w *wrapper) GetSignaturesV2() ([]signing.SignatureV2, error) { + signerInfos := w.tx.AuthInfo.SignerInfos + sigs := w.tx.Signatures + pubKeys := w.GetPubKeys() + n := len(signerInfos) + res := make([]signing.SignatureV2, n) + + for i, si := range signerInfos { + // handle nil signatures (in case of simulation) + if si.ModeInfo == nil { + res[i] = signing.SignatureV2{ + PubKey: pubKeys[i], + } + } else { + var err error + sigData, err := ModeInfoAndSigToSignatureData(si.ModeInfo, sigs[i]) + if err != nil { + return nil, err + } + res[i] = signing.SignatureV2{ + PubKey: pubKeys[i], + Data: sigData, + Sequence: si.GetSequence(), + } + + } + } + + return res, nil +} + +func (w *wrapper) SetMsgs(msgs ...sdk.Msg) error { + anys := make([]*codectypes.Any, len(msgs)) + + for i, msg := range msgs { + var err error + switch msg := msg.(type) { + case sdk.ServiceMsg: + anys[i], err = codectypes.NewAnyWithCustomTypeURL(msg.Request, msg.MethodName) + default: + anys[i], err = codectypes.NewAnyWithValue(msg) + } + if err != nil { + return err + } + } + + w.tx.Body.Messages = anys + + // set bodyBz to nil because the cached bodyBz no longer matches tx.Body + w.bodyBz = nil + + return nil +} + +// SetTimeoutHeight sets the transaction's height timeout. +func (w *wrapper) SetTimeoutHeight(height uint64) { + w.tx.Body.TimeoutHeight = height + + // set bodyBz to nil because the cached bodyBz no longer matches tx.Body + w.bodyBz = nil +} + +func (w *wrapper) SetMemo(memo string) { + w.tx.Body.Memo = memo + + // set bodyBz to nil because the cached bodyBz no longer matches tx.Body + w.bodyBz = nil +} + +func (w *wrapper) SetGasLimit(limit uint64) { + if w.tx.AuthInfo.Fee == nil { + w.tx.AuthInfo.Fee = &tx.Fee{} + } + + w.tx.AuthInfo.Fee.GasLimit = limit + + // set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo + w.authInfoBz = nil +} + +func (w *wrapper) SetFeeAmount(coins sdk.Coins) { + if w.tx.AuthInfo.Fee == nil { + w.tx.AuthInfo.Fee = &tx.Fee{} + } + + w.tx.AuthInfo.Fee.Amount = coins + + // set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo + w.authInfoBz = nil +} + +func (w *wrapper) SetFeePayer(feePayer sdk.AccAddress) { + if w.tx.AuthInfo.Fee == nil { + w.tx.AuthInfo.Fee = &tx.Fee{} + } + + w.tx.AuthInfo.Fee.Payer = feePayer.String() + + // set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo + w.authInfoBz = nil +} + +func (w *wrapper) SetFeeGranter(feeGranter sdk.AccAddress) { + if w.tx.AuthInfo.Fee == nil { + w.tx.AuthInfo.Fee = &tx.Fee{} + } + + w.tx.AuthInfo.Fee.Granter = feeGranter.String() + + // set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo + w.authInfoBz = nil +} + +func (w *wrapper) SetSignatures(signatures ...signing.SignatureV2) error { + n := len(signatures) + signerInfos := make([]*tx.SignerInfo, n) + rawSigs := make([][]byte, n) + + for i, sig := range signatures { + var modeInfo *tx.ModeInfo + modeInfo, rawSigs[i] = SignatureDataToModeInfoAndSig(sig.Data) + any, err := codectypes.NewAnyWithValue(sig.PubKey) + if err != nil { + return err + } + signerInfos[i] = &tx.SignerInfo{ + PublicKey: any, + ModeInfo: modeInfo, + Sequence: sig.Sequence, + } + } + + w.setSignerInfos(signerInfos) + w.setSignatures(rawSigs) + + return nil +} + +func (w *wrapper) setSignerInfos(infos []*tx.SignerInfo) { + w.tx.AuthInfo.SignerInfos = infos + // set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo + w.authInfoBz = nil +} + +func (w *wrapper) setSignatures(sigs [][]byte) { + w.tx.Signatures = sigs +} + +func (w *wrapper) GetTx() authsigning.Tx { + return w +} + +func (w *wrapper) GetProtoTx() *tx.Tx { + return w.tx +} + +// Deprecated: AsAny extracts proto Tx and wraps it into Any. +// NOTE: You should probably use `GetProtoTx` if you want to serialize the transaction. +func (w *wrapper) AsAny() *codectypes.Any { + return codectypes.UnsafePackAny(w.tx) +} + +// WrapTx creates a TxBuilder wrapper around a tx.Tx proto message. +func WrapTx(protoTx *tx.Tx) client.TxBuilder { + return &wrapper{ + tx: protoTx, + } +} + +func (w *wrapper) GetExtensionOptions() []*codectypes.Any { + return w.tx.Body.ExtensionOptions +} + +func (w *wrapper) GetNonCriticalExtensionOptions() []*codectypes.Any { + return w.tx.Body.NonCriticalExtensionOptions +} + +func (w *wrapper) SetExtensionOptions(extOpts ...*codectypes.Any) { + w.tx.Body.ExtensionOptions = extOpts + w.bodyBz = nil +} + +func (w *wrapper) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) { + w.tx.Body.NonCriticalExtensionOptions = extOpts + w.bodyBz = nil +} + +// ProtoTxProvider is a type which can provide a proto transaction. +type ProtoTxProvider interface { + GetProtoTx() *tx.Tx +} diff --git a/x/auth/tx/builder_test.go b/x/auth/tx/builder_test.go new file mode 100644 index 000000000000..ddec560851be --- /dev/null +++ b/x/auth/tx/builder_test.go @@ -0,0 +1,312 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +func TestTxBuilder(t *testing.T) { + _, pubkey, addr := testdata.KeyTestPubAddr() + + marshaler := codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + txBuilder := newBuilder() + + memo := "sometestmemo" + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + accSeq := uint64(2) // Arbitrary account sequence + any, err := codectypes.NewAnyWithValue(pubkey) + require.NoError(t, err) + + var signerInfo []*txtypes.SignerInfo + signerInfo = append(signerInfo, &txtypes.SignerInfo{ + PublicKey: any, + ModeInfo: &txtypes.ModeInfo{ + Sum: &txtypes.ModeInfo_Single_{ + Single: &txtypes.ModeInfo_Single{ + Mode: signing.SignMode_SIGN_MODE_DIRECT, + }, + }, + }, + Sequence: accSeq, + }) + + var sig signing.SignatureV2 + sig = signing.SignatureV2{ + PubKey: pubkey, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: legacy.Cdc.MustMarshalBinaryBare(pubkey), + }, + Sequence: accSeq, + } + + fee := txtypes.Fee{Amount: sdk.NewCoins(sdk.NewInt64Coin("atom", 150)), GasLimit: 20000} + + t.Log("verify that authInfo bytes encoded with DefaultTxEncoder and decoded with DefaultTxDecoder can be retrieved from getAuthInfoBytes") + authInfo := &txtypes.AuthInfo{ + Fee: &fee, + SignerInfos: signerInfo, + } + + authInfoBytes := marshaler.MustMarshalBinaryBare(authInfo) + + require.NotEmpty(t, authInfoBytes) + + t.Log("verify that body bytes encoded with DefaultTxEncoder and decoded with DefaultTxDecoder can be retrieved from getBodyBytes") + anys := make([]*codectypes.Any, len(msgs)) + + for i, msg := range msgs { + var err error + anys[i], err = codectypes.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + } + + txBody := &txtypes.TxBody{ + Memo: memo, + Messages: anys, + } + bodyBytes := marshaler.MustMarshalBinaryBare(txBody) + require.NotEmpty(t, bodyBytes) + require.Empty(t, txBuilder.getBodyBytes()) + + t.Log("verify that calling the SetMsgs, SetMemo results in the correct getBodyBytes") + require.NotEqual(t, bodyBytes, txBuilder.getBodyBytes()) + err = txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + require.NotEqual(t, bodyBytes, txBuilder.getBodyBytes()) + txBuilder.SetMemo(memo) + require.Equal(t, bodyBytes, txBuilder.getBodyBytes()) + require.Equal(t, len(msgs), len(txBuilder.GetMsgs())) + require.Equal(t, 0, len(txBuilder.GetPubKeys())) + + t.Log("verify that updated AuthInfo results in the correct getAuthInfoBytes and GetPubKeys") + require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes()) + txBuilder.SetFeeAmount(fee.Amount) + require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes()) + txBuilder.SetGasLimit(fee.GasLimit) + require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes()) + err = txBuilder.SetSignatures(sig) + require.NoError(t, err) + + // once fee, gas and signerInfos are all set, AuthInfo bytes should match + require.Equal(t, authInfoBytes, txBuilder.getAuthInfoBytes()) + + require.Equal(t, len(msgs), len(txBuilder.GetMsgs())) + require.Equal(t, 1, len(txBuilder.GetPubKeys())) + require.Equal(t, legacy.Cdc.MustMarshalBinaryBare(pubkey), legacy.Cdc.MustMarshalBinaryBare(txBuilder.GetPubKeys()[0])) + + any, err = codectypes.NewAnyWithValue(testdata.NewTestMsg()) + require.NoError(t, err) + txBuilder.SetExtensionOptions(any) + require.Equal(t, []*codectypes.Any{any}, txBuilder.GetExtensionOptions()) + txBuilder.SetNonCriticalExtensionOptions(any) + require.Equal(t, []*codectypes.Any{any}, txBuilder.GetNonCriticalExtensionOptions()) + + txBuilder = &wrapper{} + require.NotPanics(t, func() { + _ = txBuilder.GetMsgs() + }) +} + +func TestBuilderValidateBasic(t *testing.T) { + // keys and addresses + _, pubKey1, addr1 := testdata.KeyTestPubAddr() + _, pubKey2, addr2 := testdata.KeyTestPubAddr() + + // msg and signatures + msg1 := testdata.NewTestMsg(addr1, addr2) + feeAmount := testdata.NewTestFeeAmount() + msgs := []sdk.Msg{msg1} + + // require to fail validation upon invalid fee + badFeeAmount := testdata.NewTestFeeAmount() + badFeeAmount[0].Amount = sdk.NewInt(-5) + txBuilder := newBuilder() + + var sig1, sig2 signing.SignatureV2 + sig1 = signing.SignatureV2{ + PubKey: pubKey1, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: legacy.Cdc.MustMarshalBinaryBare(pubKey1), + }, + Sequence: 0, // Arbitrary account sequence + } + + sig2 = signing.SignatureV2{ + PubKey: pubKey2, + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: legacy.Cdc.MustMarshalBinaryBare(pubKey2), + }, + Sequence: 0, // Arbitrary account sequence + } + + err := txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + txBuilder.SetGasLimit(200000) + err = txBuilder.SetSignatures(sig1, sig2) + require.NoError(t, err) + txBuilder.SetFeeAmount(badFeeAmount) + err = txBuilder.ValidateBasic() + require.Error(t, err) + _, code, _ := sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrInsufficientFee.ABCICode(), code) + + // require to fail validation when no signatures exist + err = txBuilder.SetSignatures() + require.NoError(t, err) + txBuilder.SetFeeAmount(feeAmount) + err = txBuilder.ValidateBasic() + require.Error(t, err) + _, code, _ = sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrNoSignatures.ABCICode(), code) + + // require to fail with nil values for tx, authinfo + err = txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + err = txBuilder.ValidateBasic() + require.Error(t, err) + + // require to fail validation when signatures do not match expected signers + err = txBuilder.SetSignatures(sig1) + require.NoError(t, err) + + err = txBuilder.ValidateBasic() + require.Error(t, err) + _, code, _ = sdkerrors.ABCIInfo(err, false) + require.Equal(t, sdkerrors.ErrUnauthorized.ABCICode(), code) + + require.Error(t, err) + txBuilder.SetFeeAmount(feeAmount) + err = txBuilder.SetSignatures(sig1, sig2) + require.NoError(t, err) + err = txBuilder.ValidateBasic() + require.NoError(t, err) + + // gas limit too high + txBuilder.SetGasLimit(txtypes.MaxGasWanted + 1) + err = txBuilder.ValidateBasic() + require.Error(t, err) + txBuilder.SetGasLimit(txtypes.MaxGasWanted - 1) + err = txBuilder.ValidateBasic() + require.NoError(t, err) + + // bad builder structs + + // missing body + body := txBuilder.tx.Body + txBuilder.tx.Body = nil + err = txBuilder.ValidateBasic() + require.Error(t, err) + txBuilder.tx.Body = body + err = txBuilder.ValidateBasic() + require.NoError(t, err) + + // missing fee + f := txBuilder.tx.AuthInfo.Fee + txBuilder.tx.AuthInfo.Fee = nil + err = txBuilder.ValidateBasic() + require.Error(t, err) + txBuilder.tx.AuthInfo.Fee = f + err = txBuilder.ValidateBasic() + require.NoError(t, err) + + // missing AuthInfo + authInfo := txBuilder.tx.AuthInfo + txBuilder.tx.AuthInfo = nil + err = txBuilder.ValidateBasic() + require.Error(t, err) + txBuilder.tx.AuthInfo = authInfo + err = txBuilder.ValidateBasic() + require.NoError(t, err) + + // missing tx + txBuilder.tx = nil + err = txBuilder.ValidateBasic() + require.Error(t, err) +} + +func TestBuilderFeePayer(t *testing.T) { + // keys and addresses + _, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + _, _, addr3 := testdata.KeyTestPubAddr() + + // msg and signatures + msg1 := testdata.NewTestMsg(addr1, addr2) + feeAmount := testdata.NewTestFeeAmount() + msgs := []sdk.Msg{msg1} + + cases := map[string]struct { + txFeePayer sdk.AccAddress + expectedSigners []sdk.AccAddress + expectedPayer sdk.AccAddress + }{ + "no fee payer specified": { + expectedSigners: []sdk.AccAddress{addr1, addr2}, + expectedPayer: addr1, + }, + "secondary signer set as fee payer": { + txFeePayer: addr2, + expectedSigners: []sdk.AccAddress{addr1, addr2}, + expectedPayer: addr2, + }, + "outside signer set as fee payer": { + txFeePayer: addr3, + expectedSigners: []sdk.AccAddress{addr1, addr2, addr3}, + expectedPayer: addr3, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + // setup basic tx + txBuilder := newBuilder() + err := txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + txBuilder.SetGasLimit(200000) + txBuilder.SetFeeAmount(feeAmount) + + // set fee payer + txBuilder.SetFeePayer(tc.txFeePayer) + // and check it updates fields properly + require.Equal(t, tc.expectedSigners, txBuilder.GetSigners()) + require.Equal(t, tc.expectedPayer, txBuilder.FeePayer()) + }) + } +} + +func TestBuilderFeeGranter(t *testing.T) { + // keys and addresses + _, _, addr1 := testdata.KeyTestPubAddr() + + // msg and signatures + msg1 := testdata.NewTestMsg(addr1, addr2) + feeAmount := testdata.NewTestFeeAmount() + msgs := []sdk.Msg{msg1} + + txBuilder := newBuilder() + err := txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + txBuilder.SetGasLimit(200000) + txBuilder.SetFeeAmount(feeAmount) + + require.Empty(t, txBuilder.GetTx().FeeGranter()) + + // set fee granter + txBuilder.SetFeeGranter(addr1) + require.Equal(t, addr1, txBuilder.GetTx().FeeGranter()) +} diff --git a/x/auth/tx/config.go b/x/auth/tx/config.go new file mode 100644 index 000000000000..8402423dbf77 --- /dev/null +++ b/x/auth/tx/config.go @@ -0,0 +1,69 @@ +package tx + +import ( + "fmt" + + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +type config struct { + handler signing.SignModeHandler + decoder sdk.TxDecoder + encoder sdk.TxEncoder + jsonDecoder sdk.TxDecoder + jsonEncoder sdk.TxEncoder + protoCodec codec.ProtoCodecMarshaler +} + +// NewTxConfig returns a new protobuf TxConfig using the provided ProtoCodec and sign modes. The +// first enabled sign mode will become the default sign mode. +func NewTxConfig(protoCodec codec.ProtoCodecMarshaler, enabledSignModes []signingtypes.SignMode) client.TxConfig { + return &config{ + handler: makeSignModeHandler(enabledSignModes), + decoder: DefaultTxDecoder(protoCodec), + encoder: DefaultTxEncoder(), + jsonDecoder: DefaultJSONTxDecoder(protoCodec), + jsonEncoder: DefaultJSONTxEncoder(protoCodec), + protoCodec: protoCodec, + } +} + +func (g config) NewTxBuilder() client.TxBuilder { + return newBuilder() +} + +// WrapTxBuilder returns a builder from provided transaction +func (g config) WrapTxBuilder(newTx sdk.Tx) (client.TxBuilder, error) { + newBuilder, ok := newTx.(*wrapper) + if !ok { + return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, newTx) + } + + return newBuilder, nil +} + +func (g config) SignModeHandler() signing.SignModeHandler { + return g.handler +} + +func (g config) TxEncoder() sdk.TxEncoder { + return g.encoder +} + +func (g config) TxDecoder() sdk.TxDecoder { + return g.decoder +} + +func (g config) TxJSONEncoder() sdk.TxEncoder { + return g.jsonEncoder +} + +func (g config) TxJSONDecoder() sdk.TxDecoder { + return g.jsonDecoder +} diff --git a/x/auth/tx/config_test.go b/x/auth/tx/config_test.go new file mode 100644 index 000000000000..b20cf1ce4e85 --- /dev/null +++ b/x/auth/tx/config_test.go @@ -0,0 +1,22 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/testutil" +) + +func TestGenerator(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + std.RegisterInterfaces(interfaceRegistry) + interfaceRegistry.RegisterImplementations((*sdk.Msg)(nil), &testdata.TestMsg{}) + protoCodec := codec.NewProtoCodec(interfaceRegistry) + suite.Run(t, testutil.NewTxConfigTestSuite(NewTxConfig(protoCodec, DefaultSignModes))) +} diff --git a/x/auth/tx/decoder.go b/x/auth/tx/decoder.go new file mode 100644 index 000000000000..5f48ddd3aae6 --- /dev/null +++ b/x/auth/tx/decoder.go @@ -0,0 +1,81 @@ +package tx + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/unknownproto" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx" +) + +// DefaultTxDecoder returns a default protobuf TxDecoder using the provided Marshaler. +func DefaultTxDecoder(cdc codec.ProtoCodecMarshaler) sdk.TxDecoder { + return func(txBytes []byte) (sdk.Tx, error) { + var raw tx.TxRaw + + // reject all unknown proto fields in the root TxRaw + err := unknownproto.RejectUnknownFieldsStrict(txBytes, &raw, cdc.InterfaceRegistry()) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + err = cdc.UnmarshalBinaryBare(txBytes, &raw) + if err != nil { + return nil, err + } + + var body tx.TxBody + + // allow non-critical unknown fields in TxBody + txBodyHasUnknownNonCriticals, err := unknownproto.RejectUnknownFields(raw.BodyBytes, &body, true, cdc.InterfaceRegistry()) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + err = cdc.UnmarshalBinaryBare(raw.BodyBytes, &body) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + var authInfo tx.AuthInfo + + // reject all unknown proto fields in AuthInfo + err = unknownproto.RejectUnknownFieldsStrict(raw.AuthInfoBytes, &authInfo, cdc.InterfaceRegistry()) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + err = cdc.UnmarshalBinaryBare(raw.AuthInfoBytes, &authInfo) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + theTx := &tx.Tx{ + Body: &body, + AuthInfo: &authInfo, + Signatures: raw.Signatures, + } + + return &wrapper{ + tx: theTx, + bodyBz: raw.BodyBytes, + authInfoBz: raw.AuthInfoBytes, + txBodyHasUnknownNonCriticals: txBodyHasUnknownNonCriticals, + }, nil + } +} + +// DefaultJSONTxDecoder returns a default protobuf JSON TxDecoder using the provided Marshaler. +func DefaultJSONTxDecoder(cdc codec.ProtoCodecMarshaler) sdk.TxDecoder { + return func(txBytes []byte) (sdk.Tx, error) { + var theTx tx.Tx + err := cdc.UnmarshalJSON(txBytes, &theTx) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + return &wrapper{ + tx: &theTx, + }, nil + } +} diff --git a/x/auth/tx/direct.go b/x/auth/tx/direct.go new file mode 100644 index 000000000000..4acc52a08dd6 --- /dev/null +++ b/x/auth/tx/direct.go @@ -0,0 +1,55 @@ +package tx + +import ( + "fmt" + + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + + sdk "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// signModeDirectHandler defines the SIGN_MODE_DIRECT SignModeHandler +type signModeDirectHandler struct{} + +var _ signing.SignModeHandler = signModeDirectHandler{} + +// DefaultMode implements SignModeHandler.DefaultMode +func (signModeDirectHandler) DefaultMode() signingtypes.SignMode { + return signingtypes.SignMode_SIGN_MODE_DIRECT +} + +// Modes implements SignModeHandler.Modes +func (signModeDirectHandler) Modes() []signingtypes.SignMode { + return []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_DIRECT} +} + +// GetSignBytes implements SignModeHandler.GetSignBytes +func (signModeDirectHandler) GetSignBytes(mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) { + if mode != signingtypes.SignMode_SIGN_MODE_DIRECT { + return nil, fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_DIRECT, mode) + } + + protoTx, ok := tx.(*wrapper) + if !ok { + return nil, fmt.Errorf("can only handle a protobuf Tx, got %T", tx) + } + + bodyBz := protoTx.getBodyBytes() + authInfoBz := protoTx.getAuthInfoBytes() + + return DirectSignBytes(bodyBz, authInfoBz, data.ChainID, data.AccountNumber) +} + +// DirectSignBytes returns the SIGN_MODE_DIRECT sign bytes for the provided TxBody bytes, AuthInfo bytes, chain ID, +// account number and sequence. +func DirectSignBytes(bodyBytes, authInfoBytes []byte, chainID string, accnum uint64) ([]byte, error) { + signDoc := types.SignDoc{ + BodyBytes: bodyBytes, + AuthInfoBytes: authInfoBytes, + ChainId: chainID, + AccountNumber: accnum, + } + return signDoc.Marshal() +} diff --git a/x/auth/tx/direct_test.go b/x/auth/tx/direct_test.go new file mode 100644 index 000000000000..731a5968da65 --- /dev/null +++ b/x/auth/tx/direct_test.go @@ -0,0 +1,165 @@ +package tx + +import ( + "fmt" + "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/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +func TestDirectModeHandler(t *testing.T) { + privKey, pubkey, addr := testdata.KeyTestPubAddr() + interfaceRegistry := codectypes.NewInterfaceRegistry() + interfaceRegistry.RegisterImplementations((*sdk.Msg)(nil), &testdata.TestMsg{}) + marshaler := codec.NewProtoCodec(interfaceRegistry) + + txConfig := NewTxConfig(marshaler, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_DIRECT}) + txBuilder := txConfig.NewTxBuilder() + + memo := "sometestmemo" + msgs := []sdk.Msg{testdata.NewTestMsg(addr)} + accSeq := uint64(2) // Arbitrary account sequence + any, err := codectypes.NewAnyWithValue(pubkey) + require.NoError(t, err) + + var signerInfo []*txtypes.SignerInfo + signerInfo = append(signerInfo, &txtypes.SignerInfo{ + PublicKey: any, + ModeInfo: &txtypes.ModeInfo{ + Sum: &txtypes.ModeInfo_Single_{ + Single: &txtypes.ModeInfo_Single{ + Mode: signingtypes.SignMode_SIGN_MODE_DIRECT, + }, + }, + }, + Sequence: accSeq, + }) + + sigData := &signingtypes.SingleSignatureData{ + SignMode: signingtypes.SignMode_SIGN_MODE_DIRECT, + } + sig := signingtypes.SignatureV2{ + PubKey: pubkey, + Data: sigData, + Sequence: accSeq, + } + + fee := txtypes.Fee{Amount: sdk.NewCoins(sdk.NewInt64Coin("atom", 150)), GasLimit: 20000} + + err = txBuilder.SetMsgs(msgs...) + require.NoError(t, err) + txBuilder.SetMemo(memo) + txBuilder.SetFeeAmount(fee.Amount) + txBuilder.SetGasLimit(fee.GasLimit) + + err = txBuilder.SetSignatures(sig) + require.NoError(t, err) + + t.Log("verify modes and default-mode") + modeHandler := txConfig.SignModeHandler() + require.Equal(t, modeHandler.DefaultMode(), signingtypes.SignMode_SIGN_MODE_DIRECT) + require.Len(t, modeHandler.Modes(), 1) + + signingData := signing.SignerData{ + ChainID: "test-chain", + AccountNumber: 1, + } + + signBytes, err := modeHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, txBuilder.GetTx()) + + require.NoError(t, err) + require.NotNil(t, signBytes) + + authInfo := &txtypes.AuthInfo{ + Fee: &fee, + SignerInfos: signerInfo, + } + + authInfoBytes := marshaler.MustMarshalBinaryBare(authInfo) + + anys := make([]*codectypes.Any, len(msgs)) + + for i, msg := range msgs { + var err error + anys[i], err = codectypes.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + } + + txBody := &txtypes.TxBody{ + Memo: memo, + Messages: anys, + } + bodyBytes := marshaler.MustMarshalBinaryBare(txBody) + + t.Log("verify GetSignBytes with generating sign bytes by marshaling SignDoc") + signDoc := txtypes.SignDoc{ + AccountNumber: 1, + AuthInfoBytes: authInfoBytes, + BodyBytes: bodyBytes, + ChainId: "test-chain", + } + + expectedSignBytes, err := signDoc.Marshal() + require.NoError(t, err) + require.Equal(t, expectedSignBytes, signBytes) + + t.Log("verify that setting signature doesn't change sign bytes") + sigData.Signature, err = privKey.Sign(signBytes) + require.NoError(t, err) + err = txBuilder.SetSignatures(sig) + require.NoError(t, err) + signBytes, err = modeHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, txBuilder.GetTx()) + require.NoError(t, err) + require.Equal(t, expectedSignBytes, signBytes) + + t.Log("verify GetSignBytes with false txBody data") + signDoc.BodyBytes = []byte("dfafdasfds") + expectedSignBytes, err = signDoc.Marshal() + require.NoError(t, err) + require.NotEqual(t, expectedSignBytes, signBytes) +} + +func TestDirectModeHandler_nonDIRECT_MODE(t *testing.T) { + invalidModes := []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_TEXTUAL, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + signingtypes.SignMode_SIGN_MODE_UNSPECIFIED, + } + for _, invalidMode := range invalidModes { + t.Run(invalidMode.String(), func(t *testing.T) { + var dh signModeDirectHandler + var signingData signing.SignerData + _, err := dh.GetSignBytes(invalidMode, signingData, nil) + require.Error(t, err) + wantErr := fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_DIRECT, invalidMode) + require.Equal(t, err, wantErr) + }) + } +} + +type nonProtoTx int + +func (npt *nonProtoTx) GetMsgs() []sdk.Msg { return nil } +func (npt *nonProtoTx) ValidateBasic() error { return nil } + +var _ sdk.Tx = (*nonProtoTx)(nil) + +func TestDirectModeHandler_nonProtoTx(t *testing.T) { + var dh signModeDirectHandler + var signingData signing.SignerData + tx := new(nonProtoTx) + _, err := dh.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx) + require.Error(t, err) + wantErr := fmt.Errorf("can only handle a protobuf Tx, got %T", tx) + require.Equal(t, err, wantErr) +} diff --git a/x/auth/tx/encode_decode_test.go b/x/auth/tx/encode_decode_test.go new file mode 100644 index 000000000000..55fff38c366f --- /dev/null +++ b/x/auth/tx/encode_decode_test.go @@ -0,0 +1,161 @@ +package tx + +import ( + "fmt" + "testing" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/cosmos/cosmos-sdk/types/tx" + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func TestDefaultTxDecoderError(t *testing.T) { + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + encoder := DefaultTxEncoder() + decoder := DefaultTxDecoder(cdc) + + builder := newBuilder() + err := builder.SetMsgs(testdata.NewTestMsg()) + require.NoError(t, err) + + txBz, err := encoder(builder.GetTx()) + require.NoError(t, err) + + _, err = decoder(txBz) + require.EqualError(t, err, "unable to resolve type URL /testdata.TestMsg: tx parse error") + + testdata.RegisterInterfaces(registry) + _, err = decoder(txBz) + require.NoError(t, err) +} + +func TestUnknownFields(t *testing.T) { + registry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(registry) + decoder := DefaultTxDecoder(cdc) + + tests := []struct { + name string + body *testdata.TestUpdatedTxBody + authInfo *testdata.TestUpdatedAuthInfo + shouldErr bool + shouldAminoErr string + }{ + { + name: "no new fields should pass", + body: &testdata.TestUpdatedTxBody{ + Memo: "foo", + }, + authInfo: &testdata.TestUpdatedAuthInfo{}, + shouldErr: false, + }, + { + name: "non-critical fields in TxBody should not error on decode, but should error with amino", + body: &testdata.TestUpdatedTxBody{ + Memo: "foo", + SomeNewFieldNonCriticalField: "blah", + }, + authInfo: &testdata.TestUpdatedAuthInfo{}, + shouldErr: false, + shouldAminoErr: fmt.Sprintf("%s: %s", aminoNonCriticalFieldsError, sdkerrors.ErrInvalidRequest.Error()), + }, + { + name: "critical fields in TxBody should error on decode", + body: &testdata.TestUpdatedTxBody{ + Memo: "foo", + SomeNewField: 10, + }, + authInfo: &testdata.TestUpdatedAuthInfo{}, + shouldErr: true, + }, + { + name: "critical fields in AuthInfo should error on decode", + body: &testdata.TestUpdatedTxBody{ + Memo: "foo", + }, + authInfo: &testdata.TestUpdatedAuthInfo{ + NewField_3: []byte("xyz"), + }, + shouldErr: true, + }, + { + name: "non-critical fields in AuthInfo should error on decode", + body: &testdata.TestUpdatedTxBody{ + Memo: "foo", + }, + authInfo: &testdata.TestUpdatedAuthInfo{ + NewField_1024: []byte("xyz"), + }, + shouldErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + bodyBz, err := tt.body.Marshal() + require.NoError(t, err) + + authInfoBz, err := tt.authInfo.Marshal() + require.NoError(t, err) + + txRaw := &tx.TxRaw{ + BodyBytes: bodyBz, + AuthInfoBytes: authInfoBz, + } + txBz, err := txRaw.Marshal() + require.NoError(t, err) + + _, err = decoder(txBz) + if tt.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + if tt.shouldAminoErr != "" { + handler := signModeLegacyAminoJSONHandler{} + decoder := DefaultTxDecoder(codec.NewProtoCodec(codectypes.NewInterfaceRegistry())) + theTx, err := decoder(txBz) + require.NoError(t, err) + _, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signing.SignerData{}, theTx) + require.EqualError(t, err, tt.shouldAminoErr) + } + }) + } + + t.Log("test TxRaw no new fields, should succeed") + txRaw := &testdata.TestUpdatedTxRaw{} + txBz, err := txRaw.Marshal() + require.NoError(t, err) + _, err = decoder(txBz) + require.NoError(t, err) + + t.Log("new field in TxRaw should fail") + txRaw = &testdata.TestUpdatedTxRaw{ + NewField_5: []byte("abc"), + } + txBz, err = txRaw.Marshal() + require.NoError(t, err) + _, err = decoder(txBz) + require.Error(t, err) + + // + t.Log("new \"non-critical\" field in TxRaw should fail") + txRaw = &testdata.TestUpdatedTxRaw{ + NewField_1024: []byte("abc"), + } + txBz, err = txRaw.Marshal() + require.NoError(t, err) + _, err = decoder(txBz) + require.Error(t, err) +} diff --git a/x/auth/tx/encoder.go b/x/auth/tx/encoder.go new file mode 100644 index 000000000000..35cecac556eb --- /dev/null +++ b/x/auth/tx/encoder.go @@ -0,0 +1,47 @@ +package tx + +import ( + "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" +) + +// DefaultTxEncoder returns a default protobuf TxEncoder using the provided Marshaler +func DefaultTxEncoder() sdk.TxEncoder { + return func(tx sdk.Tx) ([]byte, error) { + txWrapper, ok := tx.(*wrapper) + if !ok { + return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, tx) + } + + raw := &txtypes.TxRaw{ + BodyBytes: txWrapper.getBodyBytes(), + AuthInfoBytes: txWrapper.getAuthInfoBytes(), + Signatures: txWrapper.tx.Signatures, + } + + return proto.Marshal(raw) + } +} + +// DefaultJSONTxEncoder returns a default protobuf JSON TxEncoder using the provided Marshaler. +func DefaultJSONTxEncoder(cdc codec.ProtoCodecMarshaler) sdk.TxEncoder { + return func(tx sdk.Tx) ([]byte, error) { + txWrapper, ok := tx.(*wrapper) + if ok { + return cdc.MarshalJSON(txWrapper.tx) + } + + protoTx, ok := tx.(*txtypes.Tx) + if ok { + return cdc.MarshalJSON(protoTx) + } + + return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, tx) + + } +} diff --git a/x/auth/tx/legacy_amino_json.go b/x/auth/tx/legacy_amino_json.go new file mode 100644 index 000000000000..090e3d864eab --- /dev/null +++ b/x/auth/tx/legacy_amino_json.go @@ -0,0 +1,54 @@ +package tx + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + signingtypes "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" +) + +const aminoNonCriticalFieldsError = "protobuf transaction contains unknown non-critical fields. This is a transaction malleability issue and SIGN_MODE_LEGACY_AMINO_JSON cannot be used." + +var _ signing.SignModeHandler = signModeLegacyAminoJSONHandler{} + +// signModeLegacyAminoJSONHandler defines the SIGN_MODE_LEGACY_AMINO_JSON +// SignModeHandler. +type signModeLegacyAminoJSONHandler struct{} + +func (s signModeLegacyAminoJSONHandler) DefaultMode() signingtypes.SignMode { + return signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON +} + +func (s signModeLegacyAminoJSONHandler) Modes() []signingtypes.SignMode { + return []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON} +} + +func (s signModeLegacyAminoJSONHandler) GetSignBytes(mode signingtypes.SignMode, data signing.SignerData, tx sdk.Tx) ([]byte, error) { + if mode != signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON { + return nil, fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode) + } + + protoTx, ok := tx.(*wrapper) + if !ok { + return nil, fmt.Errorf("can only handle a protobuf Tx, got %T", tx) + } + + if protoTx.txBodyHasUnknownNonCriticals { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, aminoNonCriticalFieldsError) + } + + body := protoTx.tx.Body + + if len(body.ExtensionOptions) != 0 || len(body.NonCriticalExtensionOptions) != 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "SIGN_MODE_LEGACY_AMINO_JSON does not support protobuf extension options.") + } + + return legacytx.StdSignBytes( + data.ChainID, data.AccountNumber, data.Sequence, protoTx.GetTimeoutHeight(), + legacytx.StdFee{Amount: protoTx.GetFee(), Gas: protoTx.GetGas()}, + tx.GetMsgs(), protoTx.GetMemo(), + ), nil +} diff --git a/x/auth/tx/legacy_amino_json_test.go b/x/auth/tx/legacy_amino_json_test.go new file mode 100644 index 000000000000..3db335286901 --- /dev/null +++ b/x/auth/tx/legacy_amino_json_test.go @@ -0,0 +1,93 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + signingtypes "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" +) + +var ( + _, _, addr1 = testdata.KeyTestPubAddr() + _, _, addr2 = testdata.KeyTestPubAddr() + + coins = sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} + gas = uint64(10000) + msg = testdata.NewTestMsg(addr1, addr2) + memo = "foo" + timeout = uint64(10) +) + +func buildTx(t *testing.T, bldr *wrapper) { + bldr.SetFeeAmount(coins) + bldr.SetGasLimit(gas) + bldr.SetMemo(memo) + bldr.SetTimeoutHeight(timeout) + require.NoError(t, bldr.SetMsgs(msg)) +} + +func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) { + bldr := newBuilder() + buildTx(t, bldr) + tx := bldr.GetTx() + + var ( + chainId = "test-chain" + accNum uint64 = 7 + seqNum uint64 = 7 + ) + + handler := signModeLegacyAminoJSONHandler{} + signingData := signing.SignerData{ + ChainID: chainId, + AccountNumber: accNum, + Sequence: seqNum, + } + signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.NoError(t, err) + + expectedSignBz := legacytx.StdSignBytes(chainId, accNum, seqNum, timeout, legacytx.StdFee{ + Amount: coins, + Gas: gas, + }, []sdk.Msg{msg}, memo) + + require.Equal(t, expectedSignBz, signBz) + + // expect error with wrong sign mode + _, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx) + require.Error(t, err) + + // expect error with extension options + bldr = newBuilder() + buildTx(t, bldr) + any, err := cdctypes.NewAnyWithValue(testdata.NewTestMsg()) + require.NoError(t, err) + bldr.tx.Body.ExtensionOptions = []*cdctypes.Any{any} + tx = bldr.GetTx() + signBz, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.Error(t, err) + + // expect error with non-critical extension options + bldr = newBuilder() + buildTx(t, bldr) + bldr.tx.Body.NonCriticalExtensionOptions = []*cdctypes.Any{any} + tx = bldr.GetTx() + signBz, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx) + require.Error(t, err) +} + +func TestLegacyAminoJSONHandler_DefaultMode(t *testing.T) { + handler := signModeLegacyAminoJSONHandler{} + require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode()) +} + +func TestLegacyAminoJSONHandler_Modes(t *testing.T) { + handler := signModeLegacyAminoJSONHandler{} + require.Equal(t, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes()) +} diff --git a/x/auth/tx/mode_handler.go b/x/auth/tx/mode_handler.go new file mode 100644 index 000000000000..f49ee16198d7 --- /dev/null +++ b/x/auth/tx/mode_handler.go @@ -0,0 +1,40 @@ +package tx + +import ( + "fmt" + + signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/signing" +) + +// DefaultSignModes are the default sign modes enabled for protobuf transactions. +var DefaultSignModes = []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, +} + +// makeSignModeHandler returns the default protobuf SignModeHandler supporting +// SIGN_MODE_DIRECT and SIGN_MODE_LEGACY_AMINO_JSON. +func makeSignModeHandler(modes []signingtypes.SignMode) signing.SignModeHandler { + if len(modes) < 1 { + panic(fmt.Errorf("no sign modes enabled")) + } + + handlers := make([]signing.SignModeHandler, len(modes)) + + for i, mode := range modes { + switch mode { + case signingtypes.SignMode_SIGN_MODE_DIRECT: + handlers[i] = signModeDirectHandler{} + case signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: + handlers[i] = signModeLegacyAminoJSONHandler{} + default: + panic(fmt.Errorf("unsupported sign mode %+v", mode)) + } + } + + return signing.NewSignModeHandlerMap( + modes[0], + handlers, + ) +} diff --git a/x/auth/tx/service.go b/x/auth/tx/service.go new file mode 100644 index 000000000000..0314495f8697 --- /dev/null +++ b/x/auth/tx/service.go @@ -0,0 +1,175 @@ +package tx + +import ( + "context" + "fmt" + "strings" + + gogogrpc "github.com/gogo/protobuf/grpc" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + pagination "github.com/cosmos/cosmos-sdk/types/query" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" +) + +// baseAppSimulateFn is the signature of the Baseapp#Simulate function. +type baseAppSimulateFn func(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) + +// txServer is the server for the protobuf Tx service. +type txServer struct { + clientCtx client.Context + simulate baseAppSimulateFn + interfaceRegistry codectypes.InterfaceRegistry +} + +// NewTxServer creates a new Tx service server. +func NewTxServer(clientCtx client.Context, simulate baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) txtypes.ServiceServer { + return txServer{ + clientCtx: clientCtx, + simulate: simulate, + interfaceRegistry: interfaceRegistry, + } +} + +var _ txtypes.ServiceServer = txServer{} + +const ( + eventFormat = "{eventType}.{eventAttribute}={value}" +) + +// TxsByEvents implements the ServiceServer.TxsByEvents RPC method. +func (s txServer) GetTxsEvent(ctx context.Context, req *txtypes.GetTxsEventRequest) (*txtypes.GetTxsEventResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "request cannot be nil") + } + + page, limit, err := pagination.ParsePagination(req.Pagination) + if err != nil { + return nil, err + } + orderBy := parseOrderBy(req.OrderBy) + + if len(req.Events) == 0 { + return nil, status.Error(codes.InvalidArgument, "must declare at least one event to search") + } + + for _, event := range req.Events { + if strings.Count(event, "=") != 1 { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid event; event %s should be of the format: %s", event, eventFormat)) + } + } + + result, err := queryTxsByEvents(ctx, s.clientCtx, req.Events, page, limit, orderBy) + if err != nil { + return nil, err + } + + // Create a proto codec, we need it to unmarshal the tx bytes. + txsList := make([]*txtypes.Tx, len(result.Txs)) + + for i, tx := range result.Txs { + protoTx, ok := tx.Tx.GetCachedValue().(*txtypes.Tx) + if !ok { + return nil, status.Errorf(codes.Internal, "expected %T, got %T", txtypes.Tx{}, tx.Tx.GetCachedValue()) + } + + txsList[i] = protoTx + } + + return &txtypes.GetTxsEventResponse{ + Txs: txsList, + TxResponses: result.Txs, + Pagination: &pagination.PageResponse{ + Total: result.TotalCount, + }, + }, nil +} + +// Simulate implements the ServiceServer.Simulate RPC method. +func (s txServer) Simulate(ctx context.Context, req *txtypes.SimulateRequest) (*txtypes.SimulateResponse, error) { + if req == nil || req.Tx == nil { + return nil, status.Error(codes.InvalidArgument, "invalid empty tx") + } + + err := req.Tx.UnpackInterfaces(s.interfaceRegistry) + if err != nil { + return nil, err + } + txBytes, err := req.Tx.Marshal() + if err != nil { + return nil, err + } + + gasInfo, result, err := s.simulate(txBytes) + if err != nil { + return nil, err + } + + return &txtypes.SimulateResponse{ + GasInfo: &gasInfo, + Result: result, + }, nil +} + +// GetTx implements the ServiceServer.GetTx RPC method. +func (s txServer) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtypes.GetTxResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "request cannot be nil") + } + + // TODO We should also check the proof flag in gRPC header. + // https://github.com/cosmos/cosmos-sdk/issues/7036. + result, err := queryTx(ctx, s.clientCtx, req.Hash) + if err != nil { + return nil, err + } + + protoTx, ok := result.Tx.GetCachedValue().(*txtypes.Tx) + if !ok { + return nil, status.Errorf(codes.Internal, "expected %T, got %T", txtypes.Tx{}, result.Tx.GetCachedValue()) + } + + return &txtypes.GetTxResponse{ + Tx: protoTx, + TxResponse: result, + }, nil +} + +func (s txServer) BroadcastTx(ctx context.Context, req *txtypes.BroadcastTxRequest) (*txtypes.BroadcastTxResponse, error) { + return client.TxServiceBroadcast(ctx, s.clientCtx, req) +} + +// RegisterTxService registers the tx service on the gRPC router. +func RegisterTxService( + qrt gogogrpc.Server, + clientCtx client.Context, + simulateFn baseAppSimulateFn, + interfaceRegistry codectypes.InterfaceRegistry, +) { + txtypes.RegisterServiceServer( + qrt, + NewTxServer(clientCtx, simulateFn, interfaceRegistry), + ) +} + +// RegisterGRPCGatewayRoutes mounts the tx service's GRPC-gateway routes on the +// given Mux. +func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) { + txtypes.RegisterServiceHandlerClient(context.Background(), mux, txtypes.NewServiceClient(clientConn)) +} + +func parseOrderBy(orderBy txtypes.OrderBy) string { + switch orderBy { + case txtypes.OrderBy_ORDER_BY_ASC: + return "asc" + case txtypes.OrderBy_ORDER_BY_DESC: + return "desc" + default: + return "" // Defaults to Tendermint's default, which is `asc` now. + } +} diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go new file mode 100644 index 000000000000..4eb25467a8f4 --- /dev/null +++ b/x/auth/tx/service_test.go @@ -0,0 +1,504 @@ +package tx_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + + queryClient tx.ServiceClient + txRes sdk.TxResponse +} + +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) + s.Require().NotNil(s.network) + + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + + s.queryClient = tx.NewServiceClient(val.ClientCtx) + + // Create a new MsgSend tx from val to itself. + out, err := bankcli.MsgSendExec( + val.ClientCtx, + val.Address, + val.Address, + sdk.NewCoins( + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + ), + 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), + fmt.Sprintf("--%s=foobar", flags.FlagMemo), + ) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &s.txRes)) + s.Require().Equal(uint32(0), s.txRes.Code) + + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s IntegrationTestSuite) TestSimulateTx_GRPC() { + txBuilder := s.mkTxBuilder() + // Convert the txBuilder to a tx.Tx. + protoTx, err := txBuilderToProtoTx(txBuilder) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.SimulateRequest{}, true, "invalid empty tx"}, + {"valid request", &tx.SimulateRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + res, err := s.queryClient.Simulate(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + // Check the result and gas used are correct. + s.Require().Equal(len(res.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } +} + +func (s IntegrationTestSuite) TestSimulateTx_GRPCGateway() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + // Convert the txBuilder to a tx.Tx. + protoTx, err := txBuilderToProtoTx(txBuilder) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.SimulateRequest{}, true, "invalid empty tx"}, + {"valid request", &tx.SimulateRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + req, err := val.ClientCtx.JSONMarshaler.MarshalJSON(tc.req) + s.Require().NoError(err) + res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", val.APIAddress), "application/json", req) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tx.SimulateResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + // Check the result and gas used are correct. + s.Require().Equal(len(result.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { + testCases := []struct { + name string + req *tx.GetTxsEventRequest + expErr bool + expErrMsg string + }{ + { + "nil request", + nil, + true, "request cannot be nil", + }, + { + "empty request", + &tx.GetTxsEventRequest{}, + true, "must declare at least one event to search", + }, + { + "request with dummy event", + &tx.GetTxsEventRequest{Events: []string{"foobar"}}, + true, "event foobar should be of the format: {eventType}.{eventAttribute}={value}", + }, + { + "request with order-by", + &tx.GetTxsEventRequest{ + Events: []string{"message.action='send'"}, + OrderBy: tx.OrderBy_ORDER_BY_ASC, + }, + false, "", + }, + { + "without pagination", + &tx.GetTxsEventRequest{ + Events: []string{"message.action='send'"}, + }, + false, "", + }, + { + "with pagination", + &tx.GetTxsEventRequest{ + Events: []string{"message.action='send'"}, + Pagination: &query.PageRequest{ + CountTotal: false, + Offset: 0, + Limit: 1, + }, + }, + false, "", + }, + { + "with multi events", + &tx.GetTxsEventRequest{ + Events: []string{"message.action='send'", "message.module='bank'"}, + }, + false, "", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + // Query the tx via gRPC. + grpcRes, err := s.queryClient.GetTxsEvent(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().GreaterOrEqual(len(grpcRes.Txs), 1) + s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) + + // Make sure fields are populated. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 + // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 + s.Require().NotEmpty(grpcRes.TxResponses[0].Timestamp) + s.Require().NotEmpty(grpcRes.TxResponses[0].RawLog) + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTxEvents_GRPCGateway() { + val := s.network.Validators[0] + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), + true, + "must declare at least one event to search", + }, + { + "without pagination", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action='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), + false, + "", + }, + { + "valid request: order by asc", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_ASC", val.APIAddress, "message.action='send'", "message.module='bank'"), + false, + "", + }, + { + "valid request: order by desc", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_DESC", val.APIAddress, "message.action='send'", "message.module='bank'"), + false, + "", + }, + { + "invalid request: invalid order by", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=invalid_order", val.APIAddress, "message.action='send'", "message.module='bank'"), + true, + "is not a valid tx.OrderBy", + }, + { + "expect pass with multiple-events", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, "message.action='send'", "message.module='bank'"), + false, + "", + }, + { + "expect pass with escape event", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D'send'"), + false, + "", + }, + } + for _, tc := range testCases { + 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 tx.GetTxsEventResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().GreaterOrEqual(len(result.Txs), 1) + s.Require().Equal("foobar", result.Txs[0].Body.Memo) + s.Require().NotZero(result.TxResponses[0].Height) + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTx_GRPC() { + testCases := []struct { + name string + req *tx.GetTxRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.GetTxRequest{}, true, "transaction hash cannot be empty"}, + {"request with dummy hash", &tx.GetTxRequest{Hash: "deadbeef"}, true, "tx (DEADBEEF) not found"}, + {"good request", &tx.GetTxRequest{Hash: s.txRes.TxHash}, false, ""}, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + // Query the tx via gRPC. + grpcRes, err := s.queryClient.GetTx(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().Equal("foobar", grpcRes.Tx.Body.Memo) + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTx_GRPCGateway() { + val := s.network.Validators[0] + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", val.APIAddress), + true, "transaction hash cannot be empty", + }, + { + "dummy hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "deadbeef"), + true, "tx (DEADBEEF) not found", + }, + { + "good hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, s.txRes.TxHash), + false, "", + }, + } + for _, tc := range testCases { + 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 tx.GetTxResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal("foobar", result.Tx.Body.Memo) + s.Require().NotZero(result.TxResponse.Height) + + // Make sure fields are populated. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 + // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 + s.Require().NotEmpty(result.TxResponse.Timestamp) + s.Require().NotEmpty(result.TxResponse.RawLog) + } + }) + } +} + +func (s IntegrationTestSuite) TestBroadcastTx_GRPC() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.BroadcastTxRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, + {"no mode", &tx.BroadcastTxRequest{ + TxBytes: txBytes, + }, true, "supported types: sync, async, block"}, + {"valid request", &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, false, ""}, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + grpcRes, err := s.queryClient.BroadcastTx(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().Equal(uint32(0), grpcRes.TxResponse.Code) + } + }) + } +} + +func (s IntegrationTestSuite) TestBroadcastTx_GRPCGateway() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.BroadcastTxRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, + {"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async, block"}, + {"valid request", &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, false, ""}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + req, err := val.ClientCtx.JSONMarshaler.MarshalJSON(tc.req) + s.Require().NoError(err) + res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), "application/json", req) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tx.BroadcastTxResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal(uint32(0), result.TxResponse.Code) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder { + val := s.network.Validators[0] + s.Require().NoError(s.network.WaitForNextBlock()) + + // prepare txBuilder with msg + txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + s.Require().NoError( + txBuilder.SetMsgs(&banktypes.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, + }), + ) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo("foobar") + + // setup txFactory + txFactory := clienttx.Factory{}. + WithChainID(val.ClientCtx.ChainID). + WithKeybase(val.ClientCtx.Keyring). + WithTxConfig(val.ClientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true) + s.Require().NoError(err) + + return txBuilder +} + +// txBuilderToProtoTx converts a txBuilder into a proto tx.Tx. +func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint + protoProvider, ok := txBuilder.(authtx.ProtoTxProvider) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected proto tx builder, got %T", txBuilder) + } + + return protoProvider.GetProtoTx(), nil +} diff --git a/x/auth/tx/sigs.go b/x/auth/tx/sigs.go new file mode 100644 index 000000000000..e2d5d63a6015 --- /dev/null +++ b/x/auth/tx/sigs.go @@ -0,0 +1,151 @@ +package tx + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" +) + +// SignatureDataToModeInfoAndSig converts a SignatureData to a ModeInfo and raw bytes signature +func SignatureDataToModeInfoAndSig(data signing.SignatureData) (*tx.ModeInfo, []byte) { + if data == nil { + return nil, nil + } + + switch data := data.(type) { + case *signing.SingleSignatureData: + return &tx.ModeInfo{ + Sum: &tx.ModeInfo_Single_{ + Single: &tx.ModeInfo_Single{Mode: data.SignMode}, + }, + }, data.Signature + case *signing.MultiSignatureData: + n := len(data.Signatures) + modeInfos := make([]*tx.ModeInfo, n) + sigs := make([][]byte, n) + + for i, d := range data.Signatures { + modeInfos[i], sigs[i] = SignatureDataToModeInfoAndSig(d) + } + + multisig := cryptotypes.MultiSignature{ + Signatures: sigs, + } + sig, err := multisig.Marshal() + if err != nil { + panic(err) + } + + return &tx.ModeInfo{ + Sum: &tx.ModeInfo_Multi_{ + Multi: &tx.ModeInfo_Multi{ + Bitarray: data.BitArray, + ModeInfos: modeInfos, + }, + }, + }, sig + default: + panic(fmt.Sprintf("unexpected signature data type %T", data)) + } +} + +// ModeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData or returns +// an error +func ModeInfoAndSigToSignatureData(modeInfo *tx.ModeInfo, sig []byte) (signing.SignatureData, error) { + switch modeInfo := modeInfo.Sum.(type) { + case *tx.ModeInfo_Single_: + return &signing.SingleSignatureData{ + SignMode: modeInfo.Single.Mode, + Signature: sig, + }, nil + + case *tx.ModeInfo_Multi_: + multi := modeInfo.Multi + + sigs, err := decodeMultisignatures(sig) + if err != nil { + return nil, err + } + + sigv2s := make([]signing.SignatureData, len(sigs)) + for i, mi := range multi.ModeInfos { + sigv2s[i], err = ModeInfoAndSigToSignatureData(mi, sigs[i]) + if err != nil { + return nil, err + } + } + + return &signing.MultiSignatureData{ + BitArray: multi.Bitarray, + Signatures: sigv2s, + }, nil + + default: + panic(fmt.Errorf("unexpected ModeInfo data type %T", modeInfo)) + } +} + +// decodeMultisignatures safely decodes the the raw bytes as a MultiSignature protobuf message +func decodeMultisignatures(bz []byte) ([][]byte, error) { + multisig := cryptotypes.MultiSignature{} + err := multisig.Unmarshal(bz) + if err != nil { + return nil, err + } + // NOTE: it is import to reject multi-signatures that contain unrecognized fields because this is an exploitable + // malleability in the protobuf message. Basically an attacker could bloat a MultiSignature message with unknown + // fields, thus bloating the transaction and causing it to fail. + if len(multisig.XXX_unrecognized) > 0 { + return nil, fmt.Errorf("rejecting unrecognized fields found in MultiSignature") + } + return multisig.Signatures, nil +} + +func (g config) MarshalSignatureJSON(sigs []signing.SignatureV2) ([]byte, error) { + descs := make([]*signing.SignatureDescriptor, len(sigs)) + + for i, sig := range sigs { + descData := signing.SignatureDataToProto(sig.Data) + any, err := codectypes.NewAnyWithValue(sig.PubKey) + if err != nil { + return nil, err + } + + descs[i] = &signing.SignatureDescriptor{ + PublicKey: any, + Data: descData, + Sequence: sig.Sequence, + } + } + + toJSON := &signing.SignatureDescriptors{Signatures: descs} + + return codec.ProtoMarshalJSON(toJSON, nil) +} + +func (g config) UnmarshalSignatureJSON(bz []byte) ([]signing.SignatureV2, error) { + var sigDescs signing.SignatureDescriptors + err := g.protoCodec.UnmarshalJSON(bz, &sigDescs) + if err != nil { + return nil, err + } + + sigs := make([]signing.SignatureV2, len(sigDescs.Signatures)) + for i, desc := range sigDescs.Signatures { + pubKey, _ := desc.PublicKey.GetCachedValue().(cryptotypes.PubKey) + + data := signing.SignatureDataFromProto(desc.Data) + + sigs[i] = signing.SignatureV2{ + PubKey: pubKey, + Data: data, + Sequence: desc.Sequence, + } + } + + return sigs, nil +} diff --git a/x/auth/tx/sigs_test.go b/x/auth/tx/sigs_test.go new file mode 100644 index 000000000000..ab868db1cdef --- /dev/null +++ b/x/auth/tx/sigs_test.go @@ -0,0 +1,40 @@ +package tx + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" +) + +func TestDecodeMultisignatures(t *testing.T) { + testSigs := [][]byte{ + []byte("dummy1"), + []byte("dummy2"), + []byte("dummy3"), + } + + badMultisig := testdata.BadMultiSignature{ + Signatures: testSigs, + MaliciousField: []byte("bad stuff..."), + } + bz, err := badMultisig.Marshal() + require.NoError(t, err) + + _, err = decodeMultisignatures(bz) + require.Error(t, err) + + goodMultisig := types.MultiSignature{ + Signatures: testSigs, + } + bz, err = goodMultisig.Marshal() + require.NoError(t, err) + + decodedSigs, err := decodeMultisignatures(bz) + require.NoError(t, err) + + require.Equal(t, testSigs, decodedSigs) +} diff --git a/x/auth/tx/xauthclient.go b/x/auth/tx/xauthclient.go new file mode 100644 index 000000000000..057afe529d8f --- /dev/null +++ b/x/auth/tx/xauthclient.go @@ -0,0 +1,157 @@ +// Package tx 's xauthclient.go file is copy-pasted from +// https://github.com/cosmos/cosmos-sdk/blob/v0.41.3/x/auth/client/query.go +// It is duplicated as to not introduce any breaking change in 0.41.4, see PR: +// https://github.com/cosmos/cosmos-sdk/pull/8732#discussion_r584746947 +package tx + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "strings" + "time" + + ctypes "github.com/tendermint/tendermint/rpc/core/types" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryTxsByEvents performs a search for transactions for a given set of events +// via the Tendermint RPC. An event takes the form of: +// "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is +// concatenated with an 'AND' operand. It returns a slice of Info object +// containing txs and metadata. An error is returned if the query fails. +// If an empty string is provided it will order txs by asc +func queryTxsByEvents(goCtx context.Context, clientCtx client.Context, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) { + if len(events) == 0 { + return nil, errors.New("must declare at least one event to search") + } + + if page <= 0 { + return nil, errors.New("page must greater than 0") + } + + if limit <= 0 { + return nil, errors.New("limit must greater than 0") + } + + // XXX: implement ANY + query := strings.Join(events, " AND ") + + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + // TODO: this may not always need to be proven + // https://github.com/cosmos/cosmos-sdk/issues/6807 + resTxs, err := node.TxSearch(goCtx, query, true, &page, &limit, orderBy) + if err != nil { + return nil, err + } + + resBlocks, err := getBlocksForTxResults(goCtx, clientCtx, resTxs.Txs) + if err != nil { + return nil, err + } + + txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks) + if err != nil { + return nil, err + } + + result := sdk.NewSearchTxsResult(uint64(resTxs.TotalCount), uint64(len(txs)), uint64(page), uint64(limit), txs) + + return result, nil +} + +// QueryTx queries for a single transaction by a hash string in hex format. An +// error is returned if the transaction does not exist or cannot be queried. +func queryTx(goCtx context.Context, clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) { + hash, err := hex.DecodeString(hashHexStr) + if err != nil { + return nil, err + } + + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + //TODO: this may not always need to be proven + // https://github.com/cosmos/cosmos-sdk/issues/6807 + resTx, err := node.Tx(goCtx, hash, true) + if err != nil { + return nil, err + } + + resBlocks, err := getBlocksForTxResults(goCtx, clientCtx, []*ctypes.ResultTx{resTx}) + if err != nil { + return nil, err + } + + out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height]) + if err != nil { + return out, err + } + + return out, nil +} + +// formatTxResults parses the indexed txs into a slice of TxResponse objects. +func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]*sdk.TxResponse, error) { + var err error + out := make([]*sdk.TxResponse, len(resTxs)) + for i := range resTxs { + out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height]) + if err != nil { + return nil, err + } + } + + return out, nil +} + +func getBlocksForTxResults(goCtx context.Context, clientCtx client.Context, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + + resBlocks := make(map[int64]*ctypes.ResultBlock) + + for _, resTx := range resTxs { + if _, ok := resBlocks[resTx.Height]; !ok { + resBlock, err := node.Block(goCtx, &resTx.Height) + if err != nil { + return nil, err + } + + resBlocks[resTx.Height] = resBlock + } + } + + return resBlocks, nil +} + +func mkTxResult(txConfig client.TxConfig, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (*sdk.TxResponse, error) { + txb, err := txConfig.TxDecoder()(resTx.Tx) + if err != nil { + return nil, err + } + p, ok := txb.(intoAny) + if !ok { + return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb) + } + any := p.AsAny() + return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil +} + +// Deprecated: this interface is used only internally for scenario we are +// deprecating (StdTxConfig support) +type intoAny interface { + AsAny() *codectypes.Any +} diff --git a/x/auth/types/account.go b/x/auth/types/account.go index a8d919a385ee..eb9939ffce10 100644 --- a/x/auth/types/account.go +++ b/x/auth/types/account.go @@ -4,138 +4,135 @@ import ( "bytes" "encoding/json" "errors" - "time" + "fmt" + "strings" + "github.com/gogo/protobuf/proto" "github.com/tendermint/tendermint/crypto" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" ) -//----------------------------------------------------------------------------- -// BaseAccount - -var _ exported.Account = (*BaseAccount)(nil) -var _ exported.GenesisAccount = (*BaseAccount)(nil) - -// BaseAccount - a base account structure. -// This can be extended by embedding within in your AppAccount. -// However one doesn't have to use BaseAccount as long as your struct -// implements Account. -type BaseAccount struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` -} +var ( + _ AccountI = (*BaseAccount)(nil) + _ GenesisAccount = (*BaseAccount)(nil) + _ codectypes.UnpackInterfacesMessage = (*BaseAccount)(nil) + _ GenesisAccount = (*ModuleAccount)(nil) + _ ModuleAccountI = (*ModuleAccount)(nil) +) // NewBaseAccount creates a new BaseAccount object -func NewBaseAccount(address sdk.AccAddress, coins sdk.Coins, - pubKey crypto.PubKey, accountNumber uint64, sequence uint64) *BaseAccount { - - return &BaseAccount{ - Address: address, - Coins: coins, - PubKey: pubKey, +//nolint:interfacer +func NewBaseAccount(address sdk.AccAddress, pubKey cryptotypes.PubKey, accountNumber, sequence uint64) *BaseAccount { + acc := &BaseAccount{ + Address: address.String(), AccountNumber: accountNumber, Sequence: sequence, } + + err := acc.SetPubKey(pubKey) + if err != nil { + panic(err) + } + + return acc } // ProtoBaseAccount - a prototype function for BaseAccount -func ProtoBaseAccount() exported.Account { +func ProtoBaseAccount() AccountI { return &BaseAccount{} } // NewBaseAccountWithAddress - returns a new base account with a given address -func NewBaseAccountWithAddress(addr sdk.AccAddress) BaseAccount { - return BaseAccount{ - Address: addr, +func NewBaseAccountWithAddress(addr sdk.AccAddress) *BaseAccount { + return &BaseAccount{ + Address: addr.String(), } } -// GetAddress - Implements sdk.Account. +// GetAddress - Implements sdk.AccountI. func (acc BaseAccount) GetAddress() sdk.AccAddress { - return acc.Address + addr, _ := sdk.AccAddressFromBech32(acc.Address) + return addr } -// SetAddress - Implements sdk.Account. +// SetAddress - Implements sdk.AccountI. func (acc *BaseAccount) SetAddress(addr sdk.AccAddress) error { if len(acc.Address) != 0 { return errors.New("cannot override BaseAccount address") } - acc.Address = addr - return nil -} - -// GetPubKey - Implements sdk.Account. -func (acc BaseAccount) GetPubKey() crypto.PubKey { - return acc.PubKey -} -// SetPubKey - Implements sdk.Account. -func (acc *BaseAccount) SetPubKey(pubKey crypto.PubKey) error { - acc.PubKey = pubKey + acc.Address = addr.String() return nil } -// GetCoins - Implements sdk.Account. -func (acc *BaseAccount) GetCoins() sdk.Coins { - return acc.Coins +// GetPubKey - Implements sdk.AccountI. +func (acc BaseAccount) GetPubKey() (pk cryptotypes.PubKey) { + if acc.PubKey == nil { + return nil + } + content, ok := acc.PubKey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil + } + return content } -// SetCoins - Implements sdk.Account. -func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { - acc.Coins = coins - return nil +// SetPubKey - Implements sdk.AccountI. +func (acc *BaseAccount) SetPubKey(pubKey cryptotypes.PubKey) error { + if pubKey == nil { + acc.PubKey = nil + return nil + } + any, err := codectypes.NewAnyWithValue(pubKey) + if err == nil { + acc.PubKey = any + } + return err } -// GetAccountNumber - Implements Account -func (acc *BaseAccount) GetAccountNumber() uint64 { +// GetAccountNumber - Implements AccountI +func (acc BaseAccount) GetAccountNumber() uint64 { return acc.AccountNumber } -// SetAccountNumber - Implements Account +// SetAccountNumber - Implements AccountI func (acc *BaseAccount) SetAccountNumber(accNumber uint64) error { acc.AccountNumber = accNumber return nil } -// GetSequence - Implements sdk.Account. -func (acc *BaseAccount) GetSequence() uint64 { +// GetSequence - Implements sdk.AccountI. +func (acc BaseAccount) GetSequence() uint64 { return acc.Sequence } -// SetSequence - Implements sdk.Account. +// SetSequence - Implements sdk.AccountI. func (acc *BaseAccount) SetSequence(seq uint64) error { acc.Sequence = seq return nil } -// SpendableCoins returns the total set of spendable coins. For a base account, -// this is simply the base coins. -func (acc *BaseAccount) SpendableCoins(_ time.Time) sdk.Coins { - return acc.GetCoins() -} - // Validate checks for errors on the account fields func (acc BaseAccount) Validate() error { - if acc.PubKey != nil && acc.Address != nil && - !bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) { - return errors.New("pubkey and address pair is invalid") + if acc.Address == "" || acc.PubKey == nil { + return nil } - return nil -} + accAddr, err := sdk.AccAddressFromBech32(acc.Address) + if err != nil { + return err + } -type baseAccountPretty struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey string `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` + if !bytes.Equal(acc.GetPubKey().Address().Bytes(), accAddr.Bytes()) { + return errors.New("account address and pubkey address do not match") + } + + return nil } func (acc BaseAccount) String() string { @@ -145,71 +142,220 @@ func (acc BaseAccount) String() string { // MarshalYAML returns the YAML representation of an account. func (acc BaseAccount) MarshalYAML() (interface{}, error) { - alias := baseAccountPretty{ - Address: acc.Address, - Coins: acc.Coins, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, + bz, err := codec.MarshalYAML(codec.NewProtoCodec(codectypes.NewInterfaceRegistry()), &acc) + if err != nil { + return nil, err + } + return string(bz), err +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (acc BaseAccount) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + if acc.PubKey == nil { + return nil + } + var pubKey cryptotypes.PubKey + return unpacker.UnpackAny(acc.PubKey, &pubKey) +} + +// NewModuleAddress creates an AccAddress from the hash of the module's name +func NewModuleAddress(name string) sdk.AccAddress { + return sdk.AccAddress(crypto.AddressHash([]byte(name))) +} + +// NewEmptyModuleAccount creates a empty ModuleAccount from a string +func NewEmptyModuleAccount(name string, permissions ...string) *ModuleAccount { + moduleAddress := NewModuleAddress(name) + baseAcc := NewBaseAccountWithAddress(moduleAddress) + + if err := validatePermissions(permissions...); err != nil { + panic(err) + } + + return &ModuleAccount{ + BaseAccount: baseAcc, + Name: name, + Permissions: permissions, + } +} + +// NewModuleAccount creates a new ModuleAccount instance +func NewModuleAccount(ba *BaseAccount, name string, permissions ...string) *ModuleAccount { + if err := validatePermissions(permissions...); err != nil { + panic(err) } - if acc.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) - if err != nil { - return nil, err + return &ModuleAccount{ + BaseAccount: ba, + Name: name, + Permissions: permissions, + } +} + +// HasPermission returns whether or not the module account has permission. +func (ma ModuleAccount) HasPermission(permission string) bool { + for _, perm := range ma.Permissions { + if perm == permission { + return true } + } + return false +} + +// GetName returns the the name of the holder's module +func (ma ModuleAccount) GetName() string { + return ma.Name +} - alias.PubKey = pks +// GetPermissions returns permissions granted to the module account +func (ma ModuleAccount) GetPermissions() []string { + return ma.Permissions +} + +// SetPubKey - Implements AccountI +func (ma ModuleAccount) SetPubKey(pubKey cryptotypes.PubKey) error { + return fmt.Errorf("not supported for module accounts") +} + +// SetSequence - Implements AccountI +func (ma ModuleAccount) SetSequence(seq uint64) error { + return fmt.Errorf("not supported for module accounts") +} + +// Validate checks for errors on the account fields +func (ma ModuleAccount) Validate() error { + if strings.TrimSpace(ma.Name) == "" { + return errors.New("module account name cannot be blank") } - bz, err := yaml.Marshal(alias) + if ma.Address != sdk.AccAddress(crypto.AddressHash([]byte(ma.Name))).String() { + return fmt.Errorf("address %s cannot be derived from the module name '%s'", ma.Address, ma.Name) + } + + return ma.BaseAccount.Validate() +} + +type moduleAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` +} + +func (ma ModuleAccount) String() string { + out, _ := ma.MarshalYAML() + return out.(string) +} + +// MarshalYAML returns the YAML representation of a ModuleAccount. +func (ma ModuleAccount) MarshalYAML() (interface{}, error) { + accAddr, err := sdk.AccAddressFromBech32(ma.Address) if err != nil { return nil, err } - return string(bz), err -} + bs, err := yaml.Marshal(moduleAccountPretty{ + Address: accAddr, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) -// MarshalJSON returns the JSON representation of a BaseAccount. -func (acc BaseAccount) MarshalJSON() ([]byte, error) { - alias := baseAccountPretty{ - Address: acc.Address, - Coins: acc.Coins, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, + if err != nil { + return nil, err } - if acc.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) - if err != nil { - return nil, err - } + return string(bs), nil +} - alias.PubKey = pks +// MarshalJSON returns the JSON representation of a ModuleAccount. +func (ma ModuleAccount) MarshalJSON() ([]byte, error) { + accAddr, err := sdk.AccAddressFromBech32(ma.Address) + if err != nil { + return nil, err } - return json.Marshal(alias) + return json.Marshal(moduleAccountPretty{ + Address: accAddr, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) } -// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount. -func (acc *BaseAccount) UnmarshalJSON(bz []byte) error { - var alias baseAccountPretty +// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. +func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { + var alias moduleAccountPretty if err := json.Unmarshal(bz, &alias); err != nil { return err } - if alias.PubKey != "" { - pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } + ma.BaseAccount = NewBaseAccount(alias.Address, nil, alias.AccountNumber, alias.Sequence) + ma.Name = alias.Name + ma.Permissions = alias.Permissions + + return nil +} + +// AccountI is an interface used to store coins at a given address within state. +// It presumes a notion of sequence numbers for replay protection, +// a notion of account numbers for replay protection for previously pruned accounts, +// and a pubkey for authentication purposes. +// +// Many complex conditions can be used in the concrete struct which implements AccountI. +type AccountI interface { + proto.Message + + GetAddress() sdk.AccAddress + SetAddress(sdk.AccAddress) error // errors if already set. - acc.PubKey = pk + GetPubKey() cryptotypes.PubKey // can return nil. + SetPubKey(cryptotypes.PubKey) error + + GetAccountNumber() uint64 + SetAccountNumber(uint64) error + + GetSequence() uint64 + SetSequence(uint64) error + + // Ensure that account implements stringer + String() string +} + +// ModuleAccountI defines an account interface for modules that hold tokens in +// an escrow. +type ModuleAccountI interface { + AccountI + + GetName() string + GetPermissions() []string + HasPermission(string) bool +} + +// GenesisAccounts defines a slice of GenesisAccount objects +type GenesisAccounts []GenesisAccount + +// Contains returns true if the given address exists in a slice of GenesisAccount +// objects. +func (ga GenesisAccounts) Contains(addr sdk.Address) bool { + for _, acc := range ga { + if acc.GetAddress().Equals(addr) { + return true + } } - acc.Address = alias.Address - acc.Coins = alias.Coins - acc.AccountNumber = alias.AccountNumber - acc.Sequence = alias.Sequence + return false +} - return nil +// GenesisAccount defines a genesis account that embeds an AccountI with validation capabilities. +type GenesisAccount interface { + AccountI + + Validate() error } diff --git a/x/auth/types/account_retriever.go b/x/auth/types/account_retriever.go index ab87e3259b95..f84c744188ff 100644 --- a/x/auth/types/account_retriever.go +++ b/x/auth/types/account_retriever.go @@ -1,74 +1,81 @@ package types import ( + "context" "fmt" + "strconv" + grpc "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" ) -// NodeQuerier is an interface that is satisfied by types that provide the QueryWithData method -type NodeQuerier interface { - // QueryWithData performs a query to a Tendermint node with the provided path - // and a data payload. It returns the result and height of the query upon success - // or an error if the query fails. - QueryWithData(path string, data []byte) ([]byte, int64, error) -} +var ( + _ client.Account = AccountI(nil) + _ client.AccountRetriever = AccountRetriever{} +) // AccountRetriever defines the properties of a type that can be used to // retrieve accounts. -type AccountRetriever struct { - querier NodeQuerier -} - -// NewAccountRetriever initialises a new AccountRetriever instance. -func NewAccountRetriever(querier NodeQuerier) AccountRetriever { - return AccountRetriever{querier: querier} -} +type AccountRetriever struct{} // GetAccount queries for an account given an address and a block height. An // error is returned if the query or decoding fails. -func (ar AccountRetriever) GetAccount(addr sdk.AccAddress) (exported.Account, error) { - account, _, err := ar.GetAccountWithHeight(addr) +func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddress) (client.Account, error) { + account, _, err := ar.GetAccountWithHeight(clientCtx, addr) return account, err } // GetAccountWithHeight queries for an account given an address. Returns the // height of the query with the account. An error is returned if the query // or decoding fails. -func (ar AccountRetriever) GetAccountWithHeight(addr sdk.AccAddress) (exported.Account, int64, error) { - bs, err := ModuleCdc.MarshalJSON(NewQueryAccountParams(addr)) +//nolint:interfacer +func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (client.Account, int64, error) { + var header metadata.MD + + queryClient := NewQueryClient(clientCtx) + res, err := queryClient.Account(context.Background(), &QueryAccountRequest{Address: addr.String()}, grpc.Header(&header)) if err != nil { return nil, 0, err } - res, height, err := ar.querier.QueryWithData(fmt.Sprintf("custom/%s/%s", QuerierRoute, QueryAccount), bs) + blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) + if l := len(blockHeight); l != 1 { + return nil, 0, fmt.Errorf("unexpected '%s' header length; got %d, expected: %d", grpctypes.GRPCBlockHeightHeader, l, 1) + } + + nBlockHeight, err := strconv.Atoi(blockHeight[0]) if err != nil { - return nil, height, err + return nil, 0, fmt.Errorf("failed to parse block height: %w", err) } - var account exported.Account - if err := ModuleCdc.UnmarshalJSON(res, &account); err != nil { - return nil, height, err + var acc AccountI + if err := clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { + return nil, 0, err } - return account, height, nil + return acc, int64(nBlockHeight), nil } // EnsureExists returns an error if no account exists for the given address else nil. -func (ar AccountRetriever) EnsureExists(addr sdk.AccAddress) error { - if _, err := ar.GetAccount(addr); err != nil { +func (ar AccountRetriever) EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error { + if _, err := ar.GetAccount(clientCtx, addr); err != nil { return err } + return nil } // GetAccountNumberSequence returns sequence and account number for the given address. // It returns an error if the account couldn't be retrieved from the state. -func (ar AccountRetriever) GetAccountNumberSequence(addr sdk.AccAddress) (uint64, uint64, error) { - acc, err := ar.GetAccount(addr) +func (ar AccountRetriever) GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) { + acc, err := ar.GetAccount(clientCtx, addr) if err != nil { return 0, 0, err } + return acc.GetAccountNumber(), acc.GetSequence(), nil } diff --git a/x/auth/types/account_retriever_test.go b/x/auth/types/account_retriever_test.go index 76126db115d3..1053ffdcbdf6 100644 --- a/x/auth/types/account_retriever_test.go +++ b/x/auth/types/account_retriever_test.go @@ -1,40 +1,43 @@ -package types +package types_test import ( - "errors" "testing" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/tests/mocks" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) -var errFoo = errors.New("dummy") - func TestAccountRetriever(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() + cfg := network.DefaultConfig() + cfg.NumValidators = 1 + + network := network.New(t, cfg) + defer network.Cleanup() + + _, err := network.WaitForHeight(3) + require.NoError(t, err) + + val := network.Validators[0] + clientCtx := val.ClientCtx + ar := types.AccountRetriever{} - mockNodeQuerier := mocks.NewMockNodeQuerier(mockCtrl) - accRetr := NewAccountRetriever(mockNodeQuerier) - addr := []byte("test") - bs, err := ModuleCdc.MarshalJSON(NewQueryAccountParams(addr)) + clientCtx = clientCtx.WithHeight(2) + + acc, err := ar.GetAccount(clientCtx, val.Address) + require.NoError(t, err) + require.NotNil(t, acc) + + acc, height, err := ar.GetAccountWithHeight(clientCtx, val.Address) require.NoError(t, err) + require.NotNil(t, acc) + require.Equal(t, height, int64(2)) - mockNodeQuerier.EXPECT().QueryWithData(gomock.Eq("custom/acc/account"), - gomock.Eq(bs)).Return(nil, int64(0), errFoo).Times(1) - _, err = accRetr.GetAccount(addr) - require.Error(t, err) - - mockNodeQuerier.EXPECT().QueryWithData(gomock.Eq("custom/acc/account"), - gomock.Eq(bs)).Return(nil, int64(0), errFoo).Times(1) - n, s, err := accRetr.GetAccountNumberSequence(addr) - require.Error(t, err) - require.Equal(t, uint64(0), n) - require.Equal(t, uint64(0), s) - - mockNodeQuerier.EXPECT().QueryWithData(gomock.Eq("custom/acc/account"), - gomock.Eq(bs)).Return(nil, int64(0), errFoo).Times(1) - require.Error(t, accRetr.EnsureExists(addr)) + require.NoError(t, ar.EnsureExists(clientCtx, val.Address)) + + accNum, accSeq, err := ar.GetAccountNumberSequence(clientCtx, val.Address) + require.NoError(t, err) + require.Equal(t, accNum, uint64(0)) + require.Equal(t, accSeq, uint64(1)) } diff --git a/x/auth/types/account_test.go b/x/auth/types/account_test.go index 7be6a25e5623..033b1c3ef966 100644 --- a/x/auth/types/account_test.go +++ b/x/auth/types/account_test.go @@ -1,22 +1,24 @@ -package types +package types_test import ( "encoding/json" "errors" + "fmt" "testing" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" + yaml "gopkg.in/yaml.v2" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestBaseAddressPubKey(t *testing.T) { - _, pub1, addr1 := KeyTestPubAddr() - _, pub2, addr2 := KeyTestPubAddr() - acc := NewBaseAccountWithAddress(addr1) + _, pub1, addr1 := testdata.KeyTestPubAddr() + _, pub2, addr2 := testdata.KeyTestPubAddr() + acc := types.NewBaseAccountWithAddress(addr1) // check the address (set) and pubkey (not set) require.EqualValues(t, addr1, acc.GetAddress()) @@ -40,27 +42,15 @@ func TestBaseAddressPubKey(t *testing.T) { //------------------------------------ // can set address on empty account - acc2 := BaseAccount{} + acc2 := types.BaseAccount{} err = acc2.SetAddress(addr2) require.Nil(t, err) require.EqualValues(t, addr2, acc2.GetAddress()) } -func TestBaseAccountCoins(t *testing.T) { - _, _, addr := KeyTestPubAddr() - acc := NewBaseAccountWithAddress(addr) - - someCoins := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 246)} - - err := acc.SetCoins(someCoins) - require.Nil(t, err) - require.Equal(t, someCoins, acc.GetCoins()) -} - -func TestBaseAccountSequence(t *testing.T) { - _, _, addr := KeyTestPubAddr() - acc := NewBaseAccountWithAddress(addr) - +func TestBaseSequence(t *testing.T) { + _, _, addr := testdata.KeyTestPubAddr() + acc := types.NewBaseAccountWithAddress(addr) seq := uint64(7) err := acc.SetSequence(seq) @@ -69,10 +59,8 @@ func TestBaseAccountSequence(t *testing.T) { } func TestBaseAccountMarshal(t *testing.T) { - _, pub, addr := KeyTestPubAddr() - acc := NewBaseAccountWithAddress(addr) - - someCoins := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 246)} + _, pub, addr := testdata.KeyTestPubAddr() + acc := types.NewBaseAccountWithAddress(addr) seq := uint64(7) // set everything on the account @@ -80,45 +68,105 @@ func TestBaseAccountMarshal(t *testing.T) { require.Nil(t, err) err = acc.SetSequence(seq) require.Nil(t, err) - err = acc.SetCoins(someCoins) - require.Nil(t, err) - - // need a codec for marshaling - cdc := codec.New() - codec.RegisterCrypto(cdc) - b, err := cdc.MarshalBinaryLengthPrefixed(acc) + bz, err := app.AccountKeeper.MarshalAccount(acc) require.Nil(t, err) - acc2 := BaseAccount{} - err = cdc.UnmarshalBinaryLengthPrefixed(b, &acc2) + acc2, err := app.AccountKeeper.UnmarshalAccount(bz) require.Nil(t, err) require.Equal(t, acc, acc2) // error on bad bytes - acc2 = BaseAccount{} - err = cdc.UnmarshalBinaryLengthPrefixed(b[:len(b)/2], &acc2) + _, err = app.AccountKeeper.UnmarshalAccount(bz[:len(bz)/2]) require.NotNil(t, err) } func TestGenesisAccountValidate(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) - baseAcc := NewBaseAccount(addr, nil, pubkey, 0, 0) + baseAcc := types.NewBaseAccount(addr, pubkey, 0, 0) + tests := []struct { name string - acc exported.GenesisAccount - expErr error + acc types.GenesisAccount + expErr bool }{ { "valid base account", baseAcc, - nil, + false, }, { "invalid base valid account", - NewBaseAccount(addr, sdk.NewCoins(), secp256k1.GenPrivKey().PubKey(), 0, 0), - errors.New("pubkey and address pair is invalid"), + types.NewBaseAccount(addr, secp256k1.GenPrivKey().PubKey(), 0, 0), + true, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expErr, tt.acc.Validate() != nil) + }) + } +} + +func TestModuleAccountMarshalYAML(t *testing.T) { + name := "test" + moduleAcc := types.NewEmptyModuleAccount(name, types.Minter, types.Burner, types.Staking) + bs, err := yaml.Marshal(moduleAcc) + require.NoError(t, err) + + want := "|\n address: cosmos1n7rdpqvgf37ktx30a2sv2kkszk3m7ncmg5drhe\n public_key: \"\"\n account_number: 0\n sequence: 0\n name: test\n permissions:\n - minter\n - burner\n - staking\n" + require.Equal(t, want, string(bs)) +} + +func TestHasPermissions(t *testing.T) { + name := "test" + macc := types.NewEmptyModuleAccount(name, types.Staking, types.Minter, types.Burner) + cases := []struct { + permission string + expectHas bool + }{ + {types.Staking, true}, + {types.Minter, true}, + {types.Burner, true}, + {"other", false}, + } + + for i, tc := range cases { + hasPerm := macc.HasPermission(tc.permission) + if tc.expectHas { + require.True(t, hasPerm, "test case #%d", i) + } else { + require.False(t, hasPerm, "test case #%d", i) + } + } +} + +func TestValidate(t *testing.T) { + addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + baseAcc := types.NewBaseAccount(addr, nil, 0, 0) + tests := []struct { + name string + acc types.GenesisAccount + expErr error + }{ + { + "valid module account", + types.NewEmptyModuleAccount("test"), + nil, + }, + { + "invalid name and address pair", + types.NewModuleAccount(baseAcc, "test"), + fmt.Errorf("address %s cannot be derived from the module name 'test'", addr), + }, + { + "empty module account name", + types.NewModuleAccount(baseAcc, " "), + errors.New("module account name cannot be blank"), }, } for _, tt := range tests { @@ -130,27 +178,32 @@ func TestGenesisAccountValidate(t *testing.T) { } } -func TestBaseAccountJSON(t *testing.T) { +func TestModuleAccountJSON(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) - coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := NewBaseAccount(addr, coins, pubkey, 10, 50) + baseAcc := types.NewBaseAccount(addr, nil, 10, 50) + acc := types.NewModuleAccount(baseAcc, "test", "burner") - bz, err := json.Marshal(baseAcc) + bz, err := json.Marshal(acc) require.NoError(t, err) - bz1, err := baseAcc.MarshalJSON() + bz1, err := acc.MarshalJSON() require.NoError(t, err) require.Equal(t, string(bz1), string(bz)) - var a BaseAccount + var a types.ModuleAccount require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, baseAcc.String(), a.String()) + require.Equal(t, acc.String(), a.String()) +} - bz, err = ModuleCdc.MarshalJSON(baseAcc) - require.NoError(t, err) +func TestGenesisAccountsContains(t *testing.T) { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + acc := types.NewBaseAccount(addr, secp256k1.GenPrivKey().PubKey(), 0, 0) + + genAccounts := types.GenesisAccounts{} + require.False(t, genAccounts.Contains(acc.GetAddress())) - var b BaseAccount - require.NoError(t, ModuleCdc.UnmarshalJSON(bz, &b)) - require.Equal(t, baseAcc.String(), b.String()) + genAccounts = append(genAccounts, acc) + require.True(t, genAccounts.Contains(acc.GetAddress())) } diff --git a/x/auth/types/auth.pb.go b/x/auth/types/auth.pb.go new file mode 100644 index 000000000000..fa52ac5a8b90 --- /dev/null +++ b/x/auth/types/auth.pb.go @@ -0,0 +1,1048 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/auth/v1beta1/auth.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/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// BaseAccount defines a base account type. It contains all the necessary fields +// for basic account functionality. Any custom account type should extend this +// type for additional functionality (e.g. vesting). +type BaseAccount struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + PubKey *types.Any `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"public_key,omitempty" yaml:"public_key"` + AccountNumber uint64 `protobuf:"varint,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty" yaml:"account_number"` + Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *BaseAccount) Reset() { *m = BaseAccount{} } +func (*BaseAccount) ProtoMessage() {} +func (*BaseAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_7e1f7e915d020d2d, []int{0} +} +func (m *BaseAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BaseAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BaseAccount.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 *BaseAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_BaseAccount.Merge(m, src) +} +func (m *BaseAccount) XXX_Size() int { + return m.Size() +} +func (m *BaseAccount) XXX_DiscardUnknown() { + xxx_messageInfo_BaseAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_BaseAccount proto.InternalMessageInfo + +// ModuleAccount defines an account for modules that holds coins on a pool. +type ModuleAccount struct { + *BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty" yaml:"base_account"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Permissions []string `protobuf:"bytes,3,rep,name=permissions,proto3" json:"permissions,omitempty"` +} + +func (m *ModuleAccount) Reset() { *m = ModuleAccount{} } +func (*ModuleAccount) ProtoMessage() {} +func (*ModuleAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_7e1f7e915d020d2d, []int{1} +} +func (m *ModuleAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ModuleAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ModuleAccount.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 *ModuleAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_ModuleAccount.Merge(m, src) +} +func (m *ModuleAccount) XXX_Size() int { + return m.Size() +} +func (m *ModuleAccount) XXX_DiscardUnknown() { + xxx_messageInfo_ModuleAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_ModuleAccount proto.InternalMessageInfo + +// Params defines the parameters for the auth module. +type Params struct { + MaxMemoCharacters uint64 `protobuf:"varint,1,opt,name=max_memo_characters,json=maxMemoCharacters,proto3" json:"max_memo_characters,omitempty" yaml:"max_memo_characters"` + TxSigLimit uint64 `protobuf:"varint,2,opt,name=tx_sig_limit,json=txSigLimit,proto3" json:"tx_sig_limit,omitempty" yaml:"tx_sig_limit"` + TxSizeCostPerByte uint64 `protobuf:"varint,3,opt,name=tx_size_cost_per_byte,json=txSizeCostPerByte,proto3" json:"tx_size_cost_per_byte,omitempty" yaml:"tx_size_cost_per_byte"` + SigVerifyCostED25519 uint64 `protobuf:"varint,4,opt,name=sig_verify_cost_ed25519,json=sigVerifyCostEd25519,proto3" json:"sig_verify_cost_ed25519,omitempty" yaml:"sig_verify_cost_ed25519"` + SigVerifyCostSecp256k1 uint64 `protobuf:"varint,5,opt,name=sig_verify_cost_secp256k1,json=sigVerifyCostSecp256k1,proto3" json:"sig_verify_cost_secp256k1,omitempty" yaml:"sig_verify_cost_secp256k1"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_7e1f7e915d020d2d, []int{2} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetMaxMemoCharacters() uint64 { + if m != nil { + return m.MaxMemoCharacters + } + return 0 +} + +func (m *Params) GetTxSigLimit() uint64 { + if m != nil { + return m.TxSigLimit + } + return 0 +} + +func (m *Params) GetTxSizeCostPerByte() uint64 { + if m != nil { + return m.TxSizeCostPerByte + } + return 0 +} + +func (m *Params) GetSigVerifyCostED25519() uint64 { + if m != nil { + return m.SigVerifyCostED25519 + } + return 0 +} + +func (m *Params) GetSigVerifyCostSecp256k1() uint64 { + if m != nil { + return m.SigVerifyCostSecp256k1 + } + return 0 +} + +func init() { + proto.RegisterType((*BaseAccount)(nil), "cosmos.auth.v1beta1.BaseAccount") + proto.RegisterType((*ModuleAccount)(nil), "cosmos.auth.v1beta1.ModuleAccount") + proto.RegisterType((*Params)(nil), "cosmos.auth.v1beta1.Params") +} + +func init() { proto.RegisterFile("cosmos/auth/v1beta1/auth.proto", fileDescriptor_7e1f7e915d020d2d) } + +var fileDescriptor_7e1f7e915d020d2d = []byte{ + // 674 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0x4d, 0x4f, 0xdb, 0x4a, + 0x14, 0x8d, 0x5f, 0xf2, 0xf8, 0x98, 0x00, 0x12, 0x26, 0x80, 0x93, 0xf7, 0x64, 0x5b, 0x5e, 0xe5, + 0x49, 0x2f, 0x8e, 0x92, 0x8a, 0x4a, 0x64, 0x51, 0x15, 0xd3, 0x2e, 0x50, 0x0b, 0x42, 0x46, 0xea, + 0xa2, 0xaa, 0xe4, 0x8e, 0x9d, 0xc1, 0x58, 0x64, 0x32, 0xc6, 0x33, 0x46, 0x31, 0xbf, 0xa0, 0xcb, + 0x2e, 0xbb, 0xe4, 0x47, 0xf0, 0x0f, 0xba, 0xe9, 0x12, 0xb1, 0xea, 0xca, 0xad, 0xc2, 0xa6, 0xea, + 0x32, 0xfb, 0x4a, 0x95, 0x67, 0x9c, 0x90, 0xa0, 0x74, 0x95, 0xb9, 0xe7, 0x9c, 0x7b, 0xee, 0x9d, + 0x7b, 0xe3, 0x01, 0xaa, 0x47, 0x28, 0x26, 0xb4, 0x09, 0x63, 0x76, 0xd6, 0xbc, 0x6c, 0xb9, 0x88, + 0xc1, 0x16, 0x0f, 0xcc, 0x30, 0x22, 0x8c, 0xc8, 0x1b, 0x82, 0x37, 0x39, 0x94, 0xf3, 0xb5, 0xaa, + 0x00, 0x1d, 0x2e, 0x69, 0xe6, 0x0a, 0x1e, 0xd4, 0x2a, 0x3e, 0xf1, 0x89, 0xc0, 0xb3, 0x53, 0x8e, + 0x56, 0x7d, 0x42, 0xfc, 0x1e, 0x6a, 0xf2, 0xc8, 0x8d, 0x4f, 0x9b, 0xb0, 0x9f, 0x08, 0xca, 0xf8, + 0x25, 0x81, 0xb2, 0x05, 0x29, 0xda, 0xf3, 0x3c, 0x12, 0xf7, 0x99, 0xac, 0x80, 0x45, 0xd8, 0xed, + 0x46, 0x88, 0x52, 0x45, 0xd2, 0xa5, 0xfa, 0xb2, 0x3d, 0x0e, 0xe5, 0x77, 0x60, 0x31, 0x8c, 0x5d, + 0xe7, 0x1c, 0x25, 0xca, 0x5f, 0xba, 0x54, 0x2f, 0xb7, 0x2b, 0xa6, 0xb0, 0x35, 0xc7, 0xb6, 0xe6, + 0x5e, 0x3f, 0xb1, 0x1a, 0x3f, 0x53, 0xad, 0x12, 0xc6, 0x6e, 0x2f, 0xf0, 0x32, 0xed, 0xff, 0x04, + 0x07, 0x0c, 0xe1, 0x90, 0x25, 0xa3, 0x54, 0x5b, 0x4f, 0x20, 0xee, 0x75, 0x8c, 0x07, 0xd6, 0xb0, + 0x17, 0xc2, 0xd8, 0x7d, 0x85, 0x12, 0xf9, 0x39, 0x58, 0x83, 0xa2, 0x05, 0xa7, 0x1f, 0x63, 0x17, + 0x45, 0x4a, 0x51, 0x97, 0xea, 0x25, 0xab, 0x3a, 0x4a, 0xb5, 0x4d, 0x91, 0x36, 0xcb, 0x1b, 0xf6, + 0x6a, 0x0e, 0x1c, 0xf1, 0x58, 0xae, 0x81, 0x25, 0x8a, 0x2e, 0x62, 0xd4, 0xf7, 0x90, 0x52, 0xca, + 0x72, 0xed, 0x49, 0xdc, 0x51, 0x3e, 0x5c, 0x6b, 0x85, 0x4f, 0xd7, 0x5a, 0xe1, 0xc7, 0xb5, 0x56, + 0xb8, 0xbb, 0x69, 0x2c, 0xe5, 0xd7, 0x3d, 0x30, 0x3e, 0x4b, 0x60, 0xf5, 0x90, 0x74, 0xe3, 0xde, + 0x64, 0x02, 0xef, 0xc1, 0x8a, 0x0b, 0x29, 0x72, 0x72, 0x77, 0x3e, 0x86, 0x72, 0x5b, 0x37, 0xe7, + 0x6c, 0xc2, 0x9c, 0x9a, 0x9c, 0xf5, 0xcf, 0x6d, 0xaa, 0x49, 0xa3, 0x54, 0xdb, 0x10, 0xdd, 0x4e, + 0x7b, 0x18, 0x76, 0xd9, 0x9d, 0x9a, 0xb1, 0x0c, 0x4a, 0x7d, 0x88, 0x11, 0x1f, 0xe3, 0xb2, 0xcd, + 0xcf, 0xb2, 0x0e, 0xca, 0x21, 0x8a, 0x70, 0x40, 0x69, 0x40, 0xfa, 0x54, 0x29, 0xea, 0xc5, 0xfa, + 0xb2, 0x3d, 0x0d, 0x75, 0x6a, 0xe3, 0x3b, 0xdc, 0xdd, 0x34, 0xd6, 0x66, 0x5a, 0x3e, 0x30, 0xbe, + 0x15, 0xc1, 0xc2, 0x31, 0x8c, 0x20, 0xa6, 0xf2, 0x11, 0xd8, 0xc0, 0x70, 0xe0, 0x60, 0x84, 0x89, + 0xe3, 0x9d, 0xc1, 0x08, 0x7a, 0x0c, 0x45, 0x62, 0x99, 0x25, 0x4b, 0x1d, 0xa5, 0x5a, 0x4d, 0xf4, + 0x37, 0x47, 0x64, 0xd8, 0xeb, 0x18, 0x0e, 0x0e, 0x11, 0x26, 0xfb, 0x13, 0x4c, 0xde, 0x05, 0x2b, + 0x6c, 0xe0, 0xd0, 0xc0, 0x77, 0x7a, 0x01, 0x0e, 0x18, 0x6f, 0xba, 0x64, 0x6d, 0x3f, 0x5c, 0x74, + 0x9a, 0x35, 0x6c, 0xc0, 0x06, 0x27, 0x81, 0xff, 0x3a, 0x0b, 0x64, 0x1b, 0x6c, 0x72, 0xf2, 0x0a, + 0x39, 0x1e, 0xa1, 0xcc, 0x09, 0x51, 0xe4, 0xb8, 0x09, 0x43, 0xf9, 0x6a, 0xf5, 0x51, 0xaa, 0xfd, + 0x3b, 0xe5, 0xf1, 0x58, 0x66, 0xd8, 0xeb, 0x99, 0xd9, 0x15, 0xda, 0x27, 0x94, 0x1d, 0xa3, 0xc8, + 0x4a, 0x18, 0x92, 0x2f, 0xc0, 0x76, 0x56, 0xed, 0x12, 0x45, 0xc1, 0x69, 0x22, 0xf4, 0xa8, 0xdb, + 0xde, 0xd9, 0x69, 0xed, 0x8a, 0xa5, 0x5b, 0x9d, 0x61, 0xaa, 0x55, 0x4e, 0x02, 0xff, 0x0d, 0x57, + 0x64, 0xa9, 0x2f, 0x5f, 0x70, 0x7e, 0x94, 0x6a, 0xaa, 0xa8, 0xf6, 0x07, 0x03, 0xc3, 0xae, 0xd0, + 0x99, 0x3c, 0x01, 0xcb, 0x09, 0xa8, 0x3e, 0xce, 0xa0, 0xc8, 0x0b, 0xdb, 0x3b, 0x4f, 0xcf, 0x5b, + 0xca, 0xdf, 0xbc, 0xe8, 0xb3, 0x61, 0xaa, 0x6d, 0xcd, 0x14, 0x3d, 0x19, 0x2b, 0x46, 0xa9, 0xa6, + 0xcf, 0x2f, 0x3b, 0x31, 0x31, 0xec, 0x2d, 0x3a, 0x37, 0xb7, 0xb3, 0x94, 0xff, 0x67, 0x25, 0x6b, + 0xff, 0xcb, 0x50, 0x95, 0x6e, 0x87, 0xaa, 0xf4, 0x7d, 0xa8, 0x4a, 0x1f, 0xef, 0xd5, 0xc2, 0xed, + 0xbd, 0x5a, 0xf8, 0x7a, 0xaf, 0x16, 0xde, 0xfe, 0xe7, 0x07, 0xec, 0x2c, 0x76, 0x4d, 0x8f, 0xe0, + 0xfc, 0x2d, 0xc8, 0x7f, 0x1a, 0xb4, 0x7b, 0xde, 0x1c, 0x88, 0xa7, 0x85, 0x25, 0x21, 0xa2, 0xee, + 0x02, 0xff, 0x52, 0x9f, 0xfc, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x49, 0x90, 0x16, 0xd9, 0x76, 0x04, + 0x00, 0x00, +} + +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.MaxMemoCharacters != that1.MaxMemoCharacters { + return false + } + if this.TxSigLimit != that1.TxSigLimit { + return false + } + if this.TxSizeCostPerByte != that1.TxSizeCostPerByte { + return false + } + if this.SigVerifyCostED25519 != that1.SigVerifyCostED25519 { + return false + } + if this.SigVerifyCostSecp256k1 != that1.SigVerifyCostSecp256k1 { + return false + } + return true +} +func (m *BaseAccount) 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 *BaseAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BaseAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x20 + } + if m.AccountNumber != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.AccountNumber)) + i-- + dAtA[i] = 0x18 + } + if m.PubKey != nil { + { + size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuth(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ModuleAccount) 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 *ModuleAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ModuleAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Permissions) > 0 { + for iNdEx := len(m.Permissions) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Permissions[iNdEx]) + copy(dAtA[i:], m.Permissions[iNdEx]) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Permissions[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintAuth(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.BaseAccount != nil { + { + size, err := m.BaseAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAuth(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SigVerifyCostSecp256k1 != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.SigVerifyCostSecp256k1)) + i-- + dAtA[i] = 0x28 + } + if m.SigVerifyCostED25519 != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.SigVerifyCostED25519)) + i-- + dAtA[i] = 0x20 + } + if m.TxSizeCostPerByte != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.TxSizeCostPerByte)) + i-- + dAtA[i] = 0x18 + } + if m.TxSigLimit != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.TxSigLimit)) + i-- + dAtA[i] = 0x10 + } + if m.MaxMemoCharacters != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.MaxMemoCharacters)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintAuth(dAtA []byte, offset int, v uint64) int { + offset -= sovAuth(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *BaseAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + if m.PubKey != nil { + l = m.PubKey.Size() + n += 1 + l + sovAuth(uint64(l)) + } + if m.AccountNumber != 0 { + n += 1 + sovAuth(uint64(m.AccountNumber)) + } + if m.Sequence != 0 { + n += 1 + sovAuth(uint64(m.Sequence)) + } + return n +} + +func (m *ModuleAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseAccount != nil { + l = m.BaseAccount.Size() + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + if len(m.Permissions) > 0 { + for _, s := range m.Permissions { + l = len(s) + n += 1 + l + sovAuth(uint64(l)) + } + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MaxMemoCharacters != 0 { + n += 1 + sovAuth(uint64(m.MaxMemoCharacters)) + } + if m.TxSigLimit != 0 { + n += 1 + sovAuth(uint64(m.TxSigLimit)) + } + if m.TxSizeCostPerByte != 0 { + n += 1 + sovAuth(uint64(m.TxSizeCostPerByte)) + } + if m.SigVerifyCostED25519 != 0 { + n += 1 + sovAuth(uint64(m.SigVerifyCostED25519)) + } + if m.SigVerifyCostSecp256k1 != 0 { + n += 1 + sovAuth(uint64(m.SigVerifyCostSecp256k1)) + } + return n +} + +func sovAuth(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuth(x uint64) (n int) { + return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *BaseAccount) 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 ErrIntOverflowAuth + } + 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: BaseAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BaseAccount: 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 ErrIntOverflowAuth + } + 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 ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + 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 PubKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PubKey == nil { + m.PubKey = &types.Any{} + } + if err := m.PubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountNumber", wireType) + } + m.AccountNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AccountNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ModuleAccount) 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 ErrIntOverflowAuth + } + 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: ModuleAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ModuleAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseAccount == nil { + m.BaseAccount = &BaseAccount{} + } + if err := m.BaseAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + 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 ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + 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 ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Permissions = append(m.Permissions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowAuth + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxMemoCharacters", wireType) + } + m.MaxMemoCharacters = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxMemoCharacters |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxSigLimit", wireType) + } + m.TxSigLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxSigLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxSizeCostPerByte", wireType) + } + m.TxSizeCostPerByte = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxSizeCostPerByte |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SigVerifyCostED25519", wireType) + } + m.SigVerifyCostED25519 = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SigVerifyCostED25519 |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SigVerifyCostSecp256k1", wireType) + } + m.SigVerifyCostSecp256k1 = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SigVerifyCostSecp256k1 |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuth(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, ErrIntOverflowAuth + } + 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, ErrIntOverflowAuth + } + 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, ErrIntOverflowAuth + } + 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, ErrInvalidLengthAuth + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuth + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuth + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuth = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/auth/types/codec.go b/x/auth/types/codec.go index c6be8883f7ad..629e2919d24d 100644 --- a/x/auth/types/codec.go +++ b/x/auth/types/codec.go @@ -2,27 +2,47 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) -// ModuleCdc auth module wide codec -var ModuleCdc = codec.New() +// RegisterLegacyAminoCodec registers the account interfaces and concrete types on the +// provided LegacyAmino codec. These types are used for Amino JSON serialization +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*ModuleAccountI)(nil), nil) + cdc.RegisterInterface((*GenesisAccount)(nil), nil) + cdc.RegisterInterface((*AccountI)(nil), nil) + cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/BaseAccount", nil) + cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) -// RegisterCodec registers concrete types on the codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*exported.GenesisAccount)(nil), nil) - cdc.RegisterInterface((*exported.Account)(nil), nil) - cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil) - cdc.RegisterConcrete(StdTx{}, "cosmos-sdk/StdTx", nil) + legacytx.RegisterLegacyAminoCodec(cdc) } -// RegisterAccountTypeCodec registers an external account type defined in -// another module for the internal ModuleCdc. -func RegisterAccountTypeCodec(o interface{}, name string) { - ModuleCdc.RegisterConcrete(o, name, nil) +// RegisterInterfaces associates protoName with AccountI interface +// and creates a registry of it's concrete implementations +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterInterface( + "cosmos.auth.v1beta1.AccountI", + (*AccountI)(nil), + &BaseAccount{}, + &ModuleAccount{}, + ) + + registry.RegisterInterface( + "cosmos.auth.v1beta1.GenesisAccount", + (*GenesisAccount)(nil), + &BaseAccount{}, + &ModuleAccount{}, + ) } +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + func init() { - RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) } diff --git a/x/auth/types/common_test.go b/x/auth/types/common_test.go new file mode 100644 index 000000000000..5c6ff0fb6488 --- /dev/null +++ b/x/auth/types/common_test.go @@ -0,0 +1,10 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/simapp" +) + +var ( + app = simapp.Setup(false) + appCodec, legacyAmino = simapp.MakeCodecs() +) diff --git a/x/auth/types/expected_keepers.go b/x/auth/types/expected_keepers.go index 499bc288c849..0ab3f9c4308e 100644 --- a/x/auth/types/expected_keepers.go +++ b/x/auth/types/expected_keepers.go @@ -2,12 +2,9 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" ) -// SupplyKeeper defines the expected supply Keeper (noalias) -type SupplyKeeper interface { +// BankKeeper defines the contract needed for supply related APIs (noalias) +type BankKeeper interface { SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - GetModuleAccount(ctx sdk.Context, moduleName string) exported.ModuleAccountI - GetModuleAddress(moduleName string) sdk.AccAddress } diff --git a/x/auth/types/genesis.go b/x/auth/types/genesis.go index 78c4f5a5bd29..2b33aa3a5a82 100644 --- a/x/auth/types/genesis.go +++ b/x/auth/types/genesis.go @@ -5,33 +5,52 @@ import ( "fmt" "sort" + proto "github.com/gogo/protobuf/proto" + "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" ) -// GenesisState - all auth state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - Accounts exported.GenesisAccounts `json:"accounts" yaml:"accounts"` -} +var _ types.UnpackInterfacesMessage = GenesisState{} + +// RandomGenesisAccountsFn defines the function required to generate custom account types +type RandomGenesisAccountsFn func(simState *module.SimulationState) GenesisAccounts // NewGenesisState - Create a new genesis state -func NewGenesisState(params Params, accounts exported.GenesisAccounts) GenesisState { - return GenesisState{ +func NewGenesisState(params Params, accounts GenesisAccounts) *GenesisState { + genAccounts, err := PackAccounts(accounts) + if err != nil { + panic(err) + } + return &GenesisState{ Params: params, - Accounts: accounts, + Accounts: genAccounts, + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (g GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, any := range g.Accounts { + var account GenesisAccount + err := unpacker.UnpackAny(any, &account) + if err != nil { + return err + } } + return nil } // DefaultGenesisState - Return a default genesis state -func DefaultGenesisState() GenesisState { - return NewGenesisState(DefaultParams(), exported.GenesisAccounts{}) +func DefaultGenesisState() *GenesisState { + return NewGenesisState(DefaultParams(), GenesisAccounts{}) } // GetGenesisStateFromAppState returns x/auth GenesisState given raw application // genesis state. -func GetGenesisStateFromAppState(cdc *codec.Codec, appState map[string]json.RawMessage) GenesisState { +func GetGenesisStateFromAppState(cdc codec.Marshaler, appState map[string]json.RawMessage) GenesisState { var genesisState GenesisState + if appState[ModuleName] != nil { cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) } @@ -46,29 +65,28 @@ func ValidateGenesis(data GenesisState) error { return err } - return ValidateGenAccounts(data.Accounts) + genAccs, err := UnpackAccounts(data.Accounts) + if err != nil { + return err + } + + return ValidateGenAccounts(genAccs) } // SanitizeGenesisAccounts sorts accounts and coin sets. -func SanitizeGenesisAccounts(genAccs exported.GenesisAccounts) exported.GenesisAccounts { +func SanitizeGenesisAccounts(genAccs GenesisAccounts) GenesisAccounts { sort.Slice(genAccs, func(i, j int) bool { return genAccs[i].GetAccountNumber() < genAccs[j].GetAccountNumber() }) - for _, acc := range genAccs { - if err := acc.SetCoins(acc.GetCoins().Sort()); err != nil { - panic(err) - } - } - return genAccs } // ValidateGenAccounts validates an array of GenesisAccounts and checks for duplicates -func ValidateGenAccounts(accounts exported.GenesisAccounts) error { +func ValidateGenAccounts(accounts GenesisAccounts) error { addrMap := make(map[string]bool, len(accounts)) - for _, acc := range accounts { + for _, acc := range accounts { // check for duplicated accounts addrStr := acc.GetAddress().String() if _, ok := addrMap[addrStr]; ok { @@ -92,12 +110,47 @@ type GenesisAccountIterator struct{} // appGenesis and invokes a callback on each genesis account. If any call // returns true, iteration stops. func (GenesisAccountIterator) IterateGenesisAccounts( - cdc *codec.Codec, appGenesis map[string]json.RawMessage, cb func(exported.Account) (stop bool), + cdc codec.Marshaler, appGenesis map[string]json.RawMessage, cb func(AccountI) (stop bool), ) { - for _, genAcc := range GetGenesisStateFromAppState(cdc, appGenesis).Accounts { - if cb(genAcc) { + acc, ok := genAcc.GetCachedValue().(AccountI) + if !ok { + panic("expected account") + } + if cb(acc) { break } } } + +// PackAccounts converts GenesisAccounts to Any slice +func PackAccounts(accounts GenesisAccounts) ([]*types.Any, error) { + accountsAny := make([]*types.Any, len(accounts)) + for i, acc := range accounts { + msg, ok := acc.(proto.Message) + if !ok { + return nil, fmt.Errorf("cannot proto marshal %T", acc) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + return nil, err + } + accountsAny[i] = any + } + + return accountsAny, nil +} + +// UnpackAccounts converts Any slice to GenesisAccounts +func UnpackAccounts(accountsAny []*types.Any) (GenesisAccounts, error) { + accounts := make(GenesisAccounts, len(accountsAny)) + for i, any := range accountsAny { + acc, ok := any.GetCachedValue().(GenesisAccount) + if !ok { + return nil, fmt.Errorf("expected genesis account") + } + accounts[i] = acc + } + + return accounts, nil +} diff --git a/x/auth/types/genesis.pb.go b/x/auth/types/genesis.pb.go new file mode 100644 index 000000000000..4e7396ab7cb3 --- /dev/null +++ b/x/auth/types/genesis.pb.go @@ -0,0 +1,389 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/auth/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" + 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 defines the auth module's genesis state. +type GenesisState struct { + // params defines all the paramaters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // accounts are the accounts present at genesis. + Accounts []*types.Any `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty"` +} + +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_d897ccbce9822332, []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) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetAccounts() []*types.Any { + if m != nil { + return m.Accounts + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.auth.v1beta1.GenesisState") +} + +func init() { proto.RegisterFile("cosmos/auth/v1beta1/genesis.proto", fileDescriptor_d897ccbce9822332) } + +var fileDescriptor_d897ccbce9822332 = []byte{ + // 252 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2c, 0x2d, 0xc9, 0xd0, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, + 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, + 0x28, 0xd1, 0x03, 0x29, 0xd1, 0x83, 0x2a, 0x91, 0x92, 0x4c, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, + 0x07, 0x2b, 0x49, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0x84, 0xa8, 0x97, 0x12, 0x49, 0xcf, 0x4f, + 0xcf, 0x07, 0x33, 0xf5, 0x41, 0x2c, 0xa8, 0xa8, 0x1c, 0x36, 0x8b, 0xc0, 0x46, 0x82, 0xe5, 0x95, + 0xaa, 0xb9, 0x78, 0xdc, 0x21, 0xd6, 0x06, 0x97, 0x24, 0x96, 0xa4, 0x0a, 0x59, 0x72, 0xb1, 0x15, + 0x24, 0x16, 0x25, 0xe6, 0x16, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0x49, 0xeb, 0x61, 0x71, + 0x86, 0x5e, 0x00, 0x58, 0x89, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x50, 0x0d, 0x42, 0x06, + 0x5c, 0x1c, 0x89, 0xc9, 0xc9, 0xf9, 0xa5, 0x79, 0x25, 0xc5, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, + 0x46, 0x22, 0x7a, 0x10, 0xe7, 0xea, 0xc1, 0x9c, 0xab, 0xe7, 0x98, 0x57, 0x19, 0x04, 0x57, 0xe5, + 0xe4, 0x7c, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, + 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x9a, 0xe9, 0x99, 0x25, + 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x50, 0x1f, 0x40, 0x28, 0xdd, 0xe2, 0x94, 0x6c, + 0xfd, 0x0a, 0x88, 0x77, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x86, 0x1b, 0x03, 0x02, + 0x00, 0x00, 0xff, 0xff, 0xac, 0xe2, 0xe7, 0xb6, 0x53, 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.Accounts) > 0 { + for iNdEx := len(m.Accounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.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 + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.Accounts) > 0 { + for _, e := range m.Accounts { + 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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accounts", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.Accounts = append(m.Accounts, &types.Any{}) + if err := m.Accounts[len(m.Accounts)-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/auth/types/genesis_test.go b/x/auth/types/genesis_test.go index f28b67b21a2d..1030885e799e 100644 --- a/x/auth/types/genesis_test.go +++ b/x/auth/types/genesis_test.go @@ -1,44 +1,35 @@ -package types +package types_test import ( "encoding/json" "testing" + proto "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestSanitize(t *testing.T) { addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) - authAcc1 := NewBaseAccountWithAddress(addr1) - authAcc1.SetCoins(sdk.Coins{ - sdk.NewInt64Coin("bcoin", 150), - sdk.NewInt64Coin("acoin", 150), - }) - authAcc1.SetAccountNumber(1) + authAcc1 := types.NewBaseAccountWithAddress(addr1) + err := authAcc1.SetAccountNumber(1) + require.NoError(t, err) addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) - authAcc2 := NewBaseAccountWithAddress(addr2) - authAcc2.SetCoins(sdk.Coins{ - sdk.NewInt64Coin("acoin", 150), - sdk.NewInt64Coin("bcoin", 150), - }) + authAcc2 := types.NewBaseAccountWithAddress(addr2) - genAccs := exported.GenesisAccounts{&authAcc1, &authAcc2} + genAccs := types.GenesisAccounts{authAcc1, authAcc2} require.True(t, genAccs[0].GetAccountNumber() > genAccs[1].GetAccountNumber()) - require.Equal(t, genAccs[0].GetCoins()[0].Denom, "bcoin") - require.Equal(t, genAccs[0].GetCoins()[1].Denom, "acoin") require.Equal(t, genAccs[1].GetAddress(), addr2) - genAccs = SanitizeGenesisAccounts(genAccs) + genAccs = types.SanitizeGenesisAccounts(genAccs) require.False(t, genAccs[0].GetAccountNumber() > genAccs[1].GetAccountNumber()) require.Equal(t, genAccs[1].GetAddress(), addr1) - require.Equal(t, genAccs[1].GetCoins()[0].Denom, "acoin") - require.Equal(t, genAccs[1].GetCoins()[1].Denom, "bcoin") } var ( @@ -50,37 +41,35 @@ var ( // require duplicate accounts fails validation func TestValidateGenesisDuplicateAccounts(t *testing.T) { - acc1 := NewBaseAccountWithAddress(sdk.AccAddress(addr1)) - acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + acc1 := types.NewBaseAccountWithAddress(sdk.AccAddress(addr1)) - genAccs := make(exported.GenesisAccounts, 2) - genAccs[0] = &acc1 - genAccs[1] = &acc1 + genAccs := make(types.GenesisAccounts, 2) + genAccs[0] = acc1 + genAccs[1] = acc1 - require.Error(t, ValidateGenAccounts(genAccs)) + require.Error(t, types.ValidateGenAccounts(genAccs)) } func TestGenesisAccountIterator(t *testing.T) { - acc1 := NewBaseAccountWithAddress(sdk.AccAddress(addr1)) - acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) - - acc2 := NewBaseAccountWithAddress(sdk.AccAddress(addr2)) - acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + acc1 := types.NewBaseAccountWithAddress(sdk.AccAddress(addr1)) + acc2 := types.NewBaseAccountWithAddress(sdk.AccAddress(addr2)) - genAccounts := exported.GenesisAccounts{&acc1, &acc2} + genAccounts := types.GenesisAccounts{acc1, acc2} - authGenState := DefaultGenesisState() - authGenState.Accounts = genAccounts + authGenState := types.DefaultGenesisState() + accounts, err := types.PackAccounts(genAccounts) + require.NoError(t, err) + authGenState.Accounts = accounts appGenesis := make(map[string]json.RawMessage) - authGenStateBz, err := ModuleCdc.MarshalJSON(authGenState) + authGenStateBz, err := appCodec.MarshalJSON(authGenState) require.NoError(t, err) - appGenesis[ModuleName] = authGenStateBz + appGenesis[types.ModuleName] = authGenStateBz var addresses []sdk.AccAddress - GenesisAccountIterator{}.IterateGenesisAccounts( - ModuleCdc, appGenesis, func(acc exported.Account) (stop bool) { + types.GenesisAccountIterator{}.IterateGenesisAccounts( + appCodec, appGenesis, func(acc types.AccountI) (stop bool) { addresses = append(addresses, acc.GetAddress()) return false }, @@ -90,3 +79,56 @@ func TestGenesisAccountIterator(t *testing.T) { require.Equal(t, addresses[0], acc1.GetAddress()) require.Equal(t, addresses[1], acc2.GetAddress()) } + +func TestPackAccountsAny(t *testing.T) { + var ( + accounts []*codectypes.Any + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "expected genesis account", + func() { + accounts = []*codectypes.Any{{}} + }, + false, + }, + { + "success", + func() { + genAccounts := types.GenesisAccounts{&types.BaseAccount{}} + accounts = make([]*codectypes.Any, len(genAccounts)) + + for i, a := range genAccounts { + msg, ok := a.(proto.Message) + require.Equal(t, ok, true) + any, err := codectypes.NewAnyWithValue(msg) + require.NoError(t, err) + accounts[i] = any + } + }, + true, + }, + } + + for _, tc := range testCases { + t.Run(tc.msg, func(t *testing.T) { + tc.malleate() + + res, err := types.UnpackAccounts(accounts) + + if tc.expPass { + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, len(res), len(accounts)) + } else { + require.Error(t, err) + require.Nil(t, res) + } + }) + } +} diff --git a/x/auth/types/keys.go b/x/auth/types/keys.go index 149fe2ba51f3..4cb2538f252c 100644 --- a/x/auth/types/keys.go +++ b/x/auth/types/keys.go @@ -14,8 +14,8 @@ const ( // FeeCollectorName the root string for the fee collector account address FeeCollectorName = "fee_collector" - // QuerierRoute is the querier route for acc - QuerierRoute = StoreKey + // QuerierRoute is the querier route for auth + QuerierRoute = ModuleName ) var ( diff --git a/x/auth/types/params.go b/x/auth/types/params.go index 2f994ef7eecf..710ee963b4a3 100644 --- a/x/auth/types/params.go +++ b/x/auth/types/params.go @@ -1,16 +1,12 @@ package types import ( - "bytes" "fmt" - "strings" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/params/subspace" -) + yaml "gopkg.in/yaml.v2" -// DefaultParamspace defines the default auth module parameter subspace -const DefaultParamspace = ModuleName + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) // Default parameter values const ( @@ -30,21 +26,12 @@ var ( KeySigVerifyCostSecp256k1 = []byte("SigVerifyCostSecp256k1") ) -var _ subspace.ParamSet = &Params{} - -// Params defines the parameters for the auth module. -type Params struct { - MaxMemoCharacters uint64 `json:"max_memo_characters" yaml:"max_memo_characters"` - TxSigLimit uint64 `json:"tx_sig_limit" yaml:"tx_sig_limit"` - TxSizeCostPerByte uint64 `json:"tx_size_cost_per_byte" yaml:"tx_size_cost_per_byte"` - SigVerifyCostED25519 uint64 `json:"sig_verify_cost_ed25519" yaml:"sig_verify_cost_ed25519"` - SigVerifyCostSecp256k1 uint64 `json:"sig_verify_cost_secp256k1" yaml:"sig_verify_cost_secp256k1"` -} +var _ paramtypes.ParamSet = &Params{} // NewParams creates a new Params object -func NewParams(maxMemoCharacters, txSigLimit, txSizeCostPerByte, - sigVerifyCostED25519, sigVerifyCostSecp256k1 uint64) Params { - +func NewParams( + maxMemoCharacters, txSigLimit, txSizeCostPerByte, sigVerifyCostED25519, sigVerifyCostSecp256k1 uint64, +) Params { return Params{ MaxMemoCharacters: maxMemoCharacters, TxSigLimit: txSigLimit, @@ -55,30 +42,23 @@ func NewParams(maxMemoCharacters, txSigLimit, txSizeCostPerByte, } // ParamKeyTable for auth module -func ParamKeyTable() subspace.KeyTable { - return subspace.NewKeyTable().RegisterParamSet(&Params{}) +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) } // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs // pairs of auth module's parameters. // nolint -func (p *Params) ParamSetPairs() subspace.ParamSetPairs { - return subspace.ParamSetPairs{ - params.NewParamSetPair(KeyMaxMemoCharacters, &p.MaxMemoCharacters, validateMaxMemoCharacters), - params.NewParamSetPair(KeyTxSigLimit, &p.TxSigLimit, validateTxSigLimit), - params.NewParamSetPair(KeyTxSizeCostPerByte, &p.TxSizeCostPerByte, validateTxSizeCostPerByte), - params.NewParamSetPair(KeySigVerifyCostED25519, &p.SigVerifyCostED25519, validateSigVerifyCostED25519), - params.NewParamSetPair(KeySigVerifyCostSecp256k1, &p.SigVerifyCostSecp256k1, validateSigVerifyCostSecp256k1), +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyMaxMemoCharacters, &p.MaxMemoCharacters, validateMaxMemoCharacters), + paramtypes.NewParamSetPair(KeyTxSigLimit, &p.TxSigLimit, validateTxSigLimit), + paramtypes.NewParamSetPair(KeyTxSizeCostPerByte, &p.TxSizeCostPerByte, validateTxSizeCostPerByte), + paramtypes.NewParamSetPair(KeySigVerifyCostED25519, &p.SigVerifyCostED25519, validateSigVerifyCostED25519), + paramtypes.NewParamSetPair(KeySigVerifyCostSecp256k1, &p.SigVerifyCostSecp256k1, validateSigVerifyCostSecp256k1), } } -// Equal returns a boolean determining if two Params types are identical. -func (p Params) Equal(p2 Params) bool { - bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p) - bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p2) - return bytes.Equal(bz1, bz2) -} - // DefaultParams returns a default set of parameters. func DefaultParams() Params { return Params{ @@ -92,14 +72,8 @@ func DefaultParams() Params { // String implements the stringer interface. func (p Params) String() string { - var sb strings.Builder - sb.WriteString("Params: \n") - sb.WriteString(fmt.Sprintf("MaxMemoCharacters: %d\n", p.MaxMemoCharacters)) - sb.WriteString(fmt.Sprintf("TxSigLimit: %d\n", p.TxSigLimit)) - sb.WriteString(fmt.Sprintf("TxSizeCostPerByte: %d\n", p.TxSizeCostPerByte)) - sb.WriteString(fmt.Sprintf("SigVerifyCostED25519: %d\n", p.SigVerifyCostED25519)) - sb.WriteString(fmt.Sprintf("SigVerifyCostSecp256k1: %d\n", p.SigVerifyCostSecp256k1)) - return sb.String() + out, _ := yaml.Marshal(p) + return string(out) } func validateTxSigLimit(i interface{}) error { @@ -178,7 +152,7 @@ func (p Params) Validate() error { if err := validateSigVerifyCostSecp256k1(p.SigVerifyCostSecp256k1); err != nil { return err } - if err := validateSigVerifyCostSecp256k1(p.MaxMemoCharacters); err != nil { + if err := validateMaxMemoCharacters(p.MaxMemoCharacters); err != nil { return err } if err := validateTxSizeCostPerByte(p.TxSizeCostPerByte); err != nil { diff --git a/x/auth/types/params_test.go b/x/auth/types/params_test.go index 9929b8e06a1c..fcec36cb833c 100644 --- a/x/auth/types/params_test.go +++ b/x/auth/types/params_test.go @@ -1,16 +1,50 @@ -package types +package types_test import ( + "fmt" "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/auth/types" ) func TestParamsEqual(t *testing.T) { - p1 := DefaultParams() - p2 := DefaultParams() + p1 := types.DefaultParams() + p2 := types.DefaultParams() require.Equal(t, p1, p2) p1.TxSigLimit += 10 require.NotEqual(t, p1, p2) } + +func TestParams_Validate(t *testing.T) { + tests := []struct { + name string + params types.Params + wantErr error + }{ + {"default params", types.DefaultParams(), nil}, + {"invalid tx signature limit", types.NewParams(types.DefaultMaxMemoCharacters, 0, types.DefaultTxSizeCostPerByte, + types.DefaultSigVerifyCostED25519, types.DefaultSigVerifyCostSecp256k1), fmt.Errorf("invalid tx signature limit: 0")}, + {"invalid ED25519 signature verification cost", types.NewParams(types.DefaultMaxMemoCharacters, types.DefaultTxSigLimit, types.DefaultTxSizeCostPerByte, + 0, types.DefaultSigVerifyCostSecp256k1), fmt.Errorf("invalid ED25519 signature verification cost: 0")}, + {"invalid SECK256k1 signature verification cost", types.NewParams(types.DefaultMaxMemoCharacters, types.DefaultTxSigLimit, types.DefaultTxSizeCostPerByte, + types.DefaultSigVerifyCostED25519, 0), fmt.Errorf("invalid SECK256k1 signature verification cost: 0")}, + {"invalid max memo characters", types.NewParams(0, types.DefaultTxSigLimit, types.DefaultTxSizeCostPerByte, + types.DefaultSigVerifyCostED25519, types.DefaultSigVerifyCostSecp256k1), fmt.Errorf("invalid max memo characters: 0")}, + {"invalid tx size cost per byte", types.NewParams(types.DefaultMaxMemoCharacters, types.DefaultTxSigLimit, 0, + types.DefaultSigVerifyCostED25519, types.DefaultSigVerifyCostSecp256k1), fmt.Errorf("invalid tx size cost per byte: 0")}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got := tt.params.Validate() + if tt.wantErr == nil { + require.NoError(t, got) + return + } + require.Equal(t, tt.wantErr, got) + }) + } +} diff --git a/x/supply/internal/types/permissions.go b/x/auth/types/permissions.go similarity index 100% rename from x/supply/internal/types/permissions.go rename to x/auth/types/permissions.go diff --git a/x/supply/internal/types/permissions_test.go b/x/auth/types/permissions_test.go similarity index 100% rename from x/supply/internal/types/permissions_test.go rename to x/auth/types/permissions_test.go diff --git a/x/auth/types/querier.go b/x/auth/types/querier.go index 84a80b1411a4..b643df4a2b96 100644 --- a/x/auth/types/querier.go +++ b/x/auth/types/querier.go @@ -1,20 +1,7 @@ package types -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - // query endpoints supported by the auth Querier const ( QueryAccount = "account" + QueryParams = "params" ) - -// QueryAccountParams defines the params for querying accounts. -type QueryAccountParams struct { - Address sdk.AccAddress -} - -// NewQueryAccountParams creates a new instance of QueryAccountParams. -func NewQueryAccountParams(addr sdk.AccAddress) QueryAccountParams { - return QueryAccountParams{Address: addr} -} diff --git a/x/auth/types/query.go b/x/auth/types/query.go new file mode 100644 index 000000000000..ce0fa7fe2c44 --- /dev/null +++ b/x/auth/types/query.go @@ -0,0 +1,10 @@ +package types + +import codectypes "github.com/cosmos/cosmos-sdk/codec/types" + +func (m *QueryAccountResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var account AccountI + return unpacker.UnpackAny(m.Account, &account) +} + +var _ codectypes.UnpackInterfacesMessage = &QueryAccountResponse{} diff --git a/x/auth/types/query.pb.go b/x/auth/types/query.pb.go new file mode 100644 index 000000000000..fcb0efa96614 --- /dev/null +++ b/x/auth/types/query.pb.go @@ -0,0 +1,930 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/auth/v1beta1/query.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" + _ "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 + +// QueryAccountRequest is the request type for the Query/Account RPC method. +type QueryAccountRequest struct { + // address defines the address to query for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryAccountRequest) Reset() { *m = QueryAccountRequest{} } +func (m *QueryAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAccountRequest) ProtoMessage() {} +func (*QueryAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{0} +} +func (m *QueryAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAccountRequest.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 *QueryAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAccountRequest.Merge(m, src) +} +func (m *QueryAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAccountRequest proto.InternalMessageInfo + +// QueryAccountResponse is the response type for the Query/Account RPC method. +type QueryAccountResponse struct { + // account defines the account of the corresponding address. + Account *types.Any `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` +} + +func (m *QueryAccountResponse) Reset() { *m = QueryAccountResponse{} } +func (m *QueryAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAccountResponse) ProtoMessage() {} +func (*QueryAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{1} +} +func (m *QueryAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAccountResponse.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 *QueryAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAccountResponse.Merge(m, src) +} +func (m *QueryAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAccountResponse proto.InternalMessageInfo + +func (m *QueryAccountResponse) GetAccount() *types.Any { + if m != nil { + return m.Account + } + return nil +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{2} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params defines the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{3} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryAccountRequest)(nil), "cosmos.auth.v1beta1.QueryAccountRequest") + proto.RegisterType((*QueryAccountResponse)(nil), "cosmos.auth.v1beta1.QueryAccountResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.auth.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.auth.v1beta1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("cosmos/auth/v1beta1/query.proto", fileDescriptor_c451370b3929a27c) } + +var fileDescriptor_c451370b3929a27c = []byte{ + // 424 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x31, 0x6f, 0xda, 0x40, + 0x14, 0xc7, 0x6d, 0xd4, 0x02, 0xbd, 0x76, 0x3a, 0x3c, 0x50, 0xd3, 0xda, 0x95, 0x3b, 0x00, 0x03, + 0x77, 0x82, 0x4e, 0x54, 0x5d, 0xa0, 0x53, 0x37, 0x6a, 0x75, 0xea, 0x52, 0x9d, 0xcd, 0xd5, 0xa0, + 0x16, 0x9f, 0xf1, 0x9d, 0xab, 0xa2, 0xaa, 0x52, 0x94, 0x29, 0x5b, 0x22, 0x65, 0xcd, 0xc0, 0x87, + 0xc8, 0x87, 0x40, 0x99, 0x90, 0xb2, 0x64, 0x8a, 0x22, 0xc8, 0x90, 0x8f, 0x11, 0x71, 0x77, 0x1e, + 0x90, 0x1c, 0x25, 0x13, 0xbc, 0xf7, 0xfe, 0xff, 0xff, 0xfb, 0xf9, 0x1d, 0x70, 0x43, 0xc6, 0x67, + 0x8c, 0x63, 0x92, 0x89, 0x09, 0xfe, 0xd3, 0x0d, 0xa8, 0x20, 0x5d, 0x3c, 0xcf, 0x68, 0xba, 0x40, + 0x49, 0xca, 0x04, 0x83, 0x35, 0x25, 0x40, 0x3b, 0x01, 0xd2, 0x02, 0xdb, 0x8a, 0x58, 0xc4, 0xe4, + 0x1c, 0xef, 0xfe, 0x29, 0xa9, 0xfd, 0x3a, 0x62, 0x2c, 0xfa, 0x4d, 0xb1, 0xac, 0x82, 0xec, 0x27, + 0x26, 0xb1, 0x4e, 0xb1, 0xdf, 0xe8, 0x11, 0x49, 0xa6, 0x98, 0xc4, 0x31, 0x13, 0x44, 0x4c, 0x59, + 0xcc, 0xf5, 0xd4, 0x29, 0x82, 0x90, 0x0b, 0x75, 0xb0, 0x9a, 0xff, 0x50, 0x1b, 0x35, 0x90, 0x2c, + 0xbc, 0x3e, 0xa8, 0x7d, 0xdd, 0xd1, 0x0e, 0xc2, 0x90, 0x65, 0xb1, 0xf0, 0xe9, 0x3c, 0xa3, 0x5c, + 0xc0, 0x3a, 0xa8, 0x90, 0xf1, 0x38, 0xa5, 0x9c, 0xd7, 0xcd, 0x77, 0x66, 0xeb, 0x85, 0x9f, 0x97, + 0x1f, 0xab, 0x47, 0x4b, 0xd7, 0xb8, 0x5b, 0xba, 0x86, 0xf7, 0x0d, 0x58, 0xfb, 0x56, 0x9e, 0xb0, + 0x98, 0x53, 0xf8, 0x09, 0x54, 0x88, 0x6a, 0x49, 0xef, 0xcb, 0x9e, 0x85, 0x14, 0x3d, 0xca, 0x3f, + 0x0c, 0x0d, 0xe2, 0xc5, 0xf0, 0xd5, 0xc5, 0x79, 0xa7, 0xaa, 0xbd, 0x5f, 0xfc, 0xdc, 0xe2, 0x59, + 0x00, 0xca, 0xd4, 0x11, 0x49, 0xc9, 0x8c, 0x6b, 0x1e, 0x6f, 0xa4, 0x31, 0xf3, 0xae, 0x5e, 0xd5, + 0x07, 0xe5, 0x44, 0x76, 0xf4, 0xa6, 0x06, 0x2a, 0xb8, 0x36, 0x52, 0xa6, 0xe1, 0xb3, 0xd5, 0xb5, + 0x6b, 0xf8, 0xda, 0xd0, 0x3b, 0x2b, 0x81, 0xe7, 0x32, 0x12, 0x1e, 0x9b, 0xa0, 0xa2, 0x39, 0x60, + 0xab, 0x30, 0xa0, 0xe0, 0x42, 0x76, 0xfb, 0x09, 0x4a, 0x45, 0xe9, 0xe1, 0xc3, 0xcb, 0xdb, 0xd3, + 0x52, 0x1b, 0x36, 0x71, 0xe1, 0x3b, 0x29, 0x35, 0xc7, 0xff, 0xf4, 0x89, 0xff, 0xc3, 0x03, 0x13, + 0x94, 0x15, 0x34, 0x6c, 0x3e, 0xbc, 0x66, 0xef, 0x42, 0x76, 0xeb, 0x71, 0xa1, 0xc6, 0x79, 0x2f, + 0x71, 0xde, 0xc2, 0x46, 0x21, 0x8e, 0x3a, 0xcf, 0xf0, 0xf3, 0x6a, 0xe3, 0x98, 0xeb, 0x8d, 0x63, + 0xde, 0x6c, 0x1c, 0xf3, 0x64, 0xeb, 0x18, 0xeb, 0xad, 0x63, 0x5c, 0x6d, 0x1d, 0xe3, 0x7b, 0x3b, + 0x9a, 0x8a, 0x49, 0x16, 0xa0, 0x90, 0xcd, 0xf2, 0x00, 0xf5, 0xd3, 0xe1, 0xe3, 0x5f, 0xf8, 0xaf, + 0x4a, 0x13, 0x8b, 0x84, 0xf2, 0xa0, 0x2c, 0x1f, 0xfc, 0xc3, 0x7d, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xef, 0x40, 0xd8, 0x2e, 0x25, 0x03, 0x00, 0x00, +} + +// 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 { + // Account returns account details based on address. + Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) + // Params queries all parameters. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) { + out := new(QueryAccountResponse) + err := c.cc.Invoke(ctx, "/cosmos.auth.v1beta1.Query/Account", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.auth.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Account returns account details based on address. + Account(context.Context, *QueryAccountRequest) (*QueryAccountResponse, error) + // Params queries all parameters. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Account(ctx context.Context, req *QueryAccountRequest) (*QueryAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Account not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Account_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Account(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.auth.v1beta1.Query/Account", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Account(ctx, req.(*QueryAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.auth.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.auth.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Account", + Handler: _Query_Account_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/auth/v1beta1/query.proto", +} + +func (m *QueryAccountRequest) 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 *QueryAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAccountResponse) 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 *QueryAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Account != nil { + { + size, err := m.Account.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 *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *QueryAccountRequest) 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)) + } + return n +} + +func (m *QueryAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Account != nil { + l = m.Account.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.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 *QueryAccountRequest) 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: QueryAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAccountRequest: 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 + 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 *QueryAccountResponse) 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: QueryAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Account", 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.Account == nil { + m.Account = &types.Any{} + } + if err := m.Account.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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/auth/types/query.pb.gw.go b/x/auth/types/query.pb.gw.go new file mode 100644 index 000000000000..0525b9c15364 --- /dev/null +++ b/x/auth/types/query.pb.gw.go @@ -0,0 +1,246 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/auth/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_Account_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.Account(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Account_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.Account(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(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_Account_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_Account_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_Account_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_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_Account_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_Account_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_Account_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Account_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "auth", "v1beta1", "accounts", "address"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Account_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/auth/types/stdsignmsg.go b/x/auth/types/stdsignmsg.go deleted file mode 100644 index e018dac40048..000000000000 --- a/x/auth/types/stdsignmsg.go +++ /dev/null @@ -1,22 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// StdSignMsg is a convenience structure for passing along -// a Msg with the other requirements for a StdSignDoc before -// it is signed. For use in the CLI. -type StdSignMsg struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - Fee StdFee `json:"fee" yaml:"fee"` - Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` - Memo string `json:"memo" yaml:"memo"` -} - -// get message bytes -func (msg StdSignMsg) Bytes() []byte { - return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.Fee, msg.Msgs, msg.Memo) -} diff --git a/x/auth/types/stdtx.go b/x/auth/types/stdtx.go deleted file mode 100644 index a37408f9e9e6..000000000000 --- a/x/auth/types/stdtx.go +++ /dev/null @@ -1,305 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/multisig" - yaml "gopkg.in/yaml.v2" - - "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/auth/exported" -) - -var ( - _ sdk.Tx = (*StdTx)(nil) - - maxGasWanted = uint64((1 << 63) - 1) -) - -// StdTx is a standard way to wrap a Msg with Fee and Signatures. -// NOTE: the first signature is the fee payer (Signatures must not be nil). -type StdTx struct { - Msgs []sdk.Msg `json:"msg" yaml:"msg"` - Fee StdFee `json:"fee" yaml:"fee"` - Signatures []StdSignature `json:"signatures" yaml:"signatures"` - Memo string `json:"memo" yaml:"memo"` -} - -func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdTx { - return StdTx{ - Msgs: msgs, - Fee: fee, - Signatures: sigs, - Memo: memo, - } -} - -// GetMsgs returns the all the transaction's messages. -func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } - -// ValidateBasic does a simple and lightweight validation check that doesn't -// require access to any other information. -func (tx StdTx) ValidateBasic() error { - stdSigs := tx.GetSignatures() - - if tx.Fee.Gas > maxGasWanted { - return sdkerrors.Wrapf( - sdkerrors.ErrInvalidRequest, - "invalid gas supplied; %d > %d", tx.Fee.Gas, maxGasWanted, - ) - } - if tx.Fee.Amount.IsAnyNegative() { - return sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFee, - "invalid fee provided: %s", tx.Fee.Amount, - ) - } - if len(stdSigs) == 0 { - return sdkerrors.ErrNoSignatures - } - if len(stdSigs) != len(tx.GetSigners()) { - return sdkerrors.Wrapf( - sdkerrors.ErrUnauthorized, - "wrong number of signers; expected %d, got %d", tx.GetSigners(), len(stdSigs), - ) - } - - return nil -} - -// CountSubKeys counts the total number of keys for a multi-sig public key. -func CountSubKeys(pub crypto.PubKey) int { - v, ok := pub.(multisig.PubKeyMultisigThreshold) - if !ok { - return 1 - } - - numKeys := 0 - for _, subkey := range v.PubKeys { - numKeys += CountSubKeys(subkey) - } - - return numKeys -} - -// GetSigners returns the addresses that must sign the transaction. -// Addresses are returned in a deterministic order. -// They are accumulated from the GetSigners method for each Msg -// in the order they appear in tx.GetMsgs(). -// Duplicate addresses will be omitted. -func (tx StdTx) GetSigners() []sdk.AccAddress { - seen := map[string]bool{} - var signers []sdk.AccAddress - for _, msg := range tx.GetMsgs() { - for _, addr := range msg.GetSigners() { - if !seen[addr.String()] { - signers = append(signers, addr) - seen[addr.String()] = true - } - } - } - return signers -} - -// GetMemo returns the memo -func (tx StdTx) GetMemo() string { return tx.Memo } - -// GetSignatures returns the signature of signers who signed the Msg. -// CONTRACT: Length returned is same as length of -// pubkeys returned from MsgKeySigners, and the order -// matches. -// CONTRACT: If the signature is missing (ie the Msg is -// invalid), then the corresponding signature is -// .Empty(). -func (tx StdTx) GetSignatures() [][]byte { - sigs := make([][]byte, len(tx.Signatures)) - for i, stdSig := range tx.Signatures { - sigs[i] = stdSig.Signature - } - return sigs -} - -// GetPubkeys returns the pubkeys of signers if the pubkey is included in the signature -// If pubkey is not included in the signature, then nil is in the slice instead -func (tx StdTx) GetPubKeys() []crypto.PubKey { - pks := make([]crypto.PubKey, len(tx.Signatures)) - for i, stdSig := range tx.Signatures { - pks[i] = stdSig.PubKey - } - return pks -} - -// GetSignBytes returns the signBytes of the tx for a given signer -func (tx StdTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { - genesis := ctx.BlockHeight() == 0 - chainID := ctx.ChainID() - var accNum uint64 - if !genesis { - accNum = acc.GetAccountNumber() - } - - return StdSignBytes( - chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo, - ) -} - -// GetGas returns the Gas in StdFee -func (tx StdTx) GetGas() uint64 { return tx.Fee.Gas } - -// GetFee returns the FeeAmount in StdFee -func (tx StdTx) GetFee() sdk.Coins { return tx.Fee.Amount } - -// FeePayer returns the address that is responsible for paying fee -// StdTx returns the first signer as the fee payer -// If no signers for tx, return empty address -func (tx StdTx) FeePayer() sdk.AccAddress { - if tx.GetSigners() != nil { - return tx.GetSigners()[0] - } - return sdk.AccAddress{} -} - -//__________________________________________________________ - -// StdFee includes the amount of coins paid in fees and the maximum -// gas to be used by the transaction. The ratio yields an effective "gasprice", -// which must be above some miminum to be accepted into the mempool. -type StdFee struct { - Amount sdk.Coins `json:"amount" yaml:"amount"` - Gas uint64 `json:"gas" yaml:"gas"` -} - -// NewStdFee returns a new instance of StdFee -func NewStdFee(gas uint64, amount sdk.Coins) StdFee { - return StdFee{ - Amount: amount, - Gas: gas, - } -} - -// Bytes for signing later -func (fee StdFee) Bytes() []byte { - // normalize. XXX - // this is a sign of something ugly - // (in the lcd_test, client side its null, - // server side its []) - if len(fee.Amount) == 0 { - fee.Amount = sdk.NewCoins() - } - bz, err := ModuleCdc.MarshalJSON(fee) // TODO - if err != nil { - panic(err) - } - return bz -} - -// GasPrices returns the gas prices for a StdFee. -// -// NOTE: The gas prices returned are not the true gas prices that were -// originally part of the submitted transaction because the fee is computed -// as fee = ceil(gasWanted * gasPrices). -func (fee StdFee) GasPrices() sdk.DecCoins { - return sdk.NewDecCoinsFromCoins(fee.Amount...).QuoDec(sdk.NewDec(int64(fee.Gas))) -} - -//__________________________________________________________ - -// StdSignDoc is replay-prevention structure. -// It includes the result of msg.GetSignBytes(), -// as well as the ChainID (prevent cross chain replay) -// and the Sequence numbers for each signature (prevent -// inchain replay and enforce tx ordering per account). -type StdSignDoc struct { - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - ChainID string `json:"chain_id" yaml:"chain_id"` - Fee json.RawMessage `json:"fee" yaml:"fee"` - Memo string `json:"memo" yaml:"memo"` - Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` - Sequence uint64 `json:"sequence" yaml:"sequence"` -} - -// StdSignBytes returns the bytes to sign for a transaction. -func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string) []byte { - msgsBytes := make([]json.RawMessage, 0, len(msgs)) - for _, msg := range msgs { - msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) - } - bz, err := ModuleCdc.MarshalJSON(StdSignDoc{ - AccountNumber: accnum, - ChainID: chainID, - Fee: json.RawMessage(fee.Bytes()), - Memo: memo, - Msgs: msgsBytes, - Sequence: sequence, - }) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(bz) -} - -// StdSignature represents a sig -type StdSignature struct { - crypto.PubKey `json:"pub_key" yaml:"pub_key"` // optional - Signature []byte `json:"signature" yaml:"signature"` -} - -// DefaultTxDecoder logic for standard transaction decoding -func DefaultTxDecoder(cdc *codec.Codec) sdk.TxDecoder { - return func(txBytes []byte) (sdk.Tx, error) { - var tx = StdTx{} - - if len(txBytes) == 0 { - return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty") - } - - // StdTx.Msg is an interface. The concrete types - // are registered by MakeTxCodec - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) - } - - return tx, nil - } -} - -// DefaultTxEncoder logic for standard transaction encoding -func DefaultTxEncoder(cdc *codec.Codec) sdk.TxEncoder { - return func(tx sdk.Tx) ([]byte, error) { - return cdc.MarshalBinaryLengthPrefixed(tx) - } -} - -// MarshalYAML returns the YAML representation of the signature. -func (ss StdSignature) MarshalYAML() (interface{}, error) { - var ( - bz []byte - pubkey string - err error - ) - - if ss.PubKey != nil { - pubkey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, ss.PubKey) - if err != nil { - return nil, err - } - } - - bz, err = yaml.Marshal(struct { - PubKey string - Signature string - }{ - PubKey: pubkey, - Signature: fmt.Sprintf("%s", ss.Signature), - }) - if err != nil { - return nil, err - } - - return string(bz), err -} diff --git a/x/auth/types/stdtx_test.go b/x/auth/types/stdtx_test.go deleted file mode 100644 index a8ce9d721076..000000000000 --- a/x/auth/types/stdtx_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package types - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - yaml "gopkg.in/yaml.v2" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -var ( - priv = ed25519.GenPrivKey() - addr = sdk.AccAddress(priv.PubKey().Address()) -) - -func TestStdTx(t *testing.T) { - msgs := []sdk.Msg{sdk.NewTestMsg(addr)} - fee := NewTestStdFee() - sigs := []StdSignature{} - - tx := NewStdTx(msgs, fee, sigs, "") - require.Equal(t, msgs, tx.GetMsgs()) - require.Equal(t, sigs, tx.Signatures) - - feePayer := tx.GetSigners()[0] - require.Equal(t, addr, feePayer) -} - -func TestStdSignBytes(t *testing.T) { - type args struct { - chainID string - accnum uint64 - sequence uint64 - fee StdFee - msgs []sdk.Msg - memo string - } - defaultFee := NewTestStdFee() - tests := []struct { - args args - want string - }{ - { - args{"1234", 3, 6, defaultFee, []sdk.Msg{sdk.NewTestMsg(addr)}, "memo"}, - fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"100000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}", addr), - }, - } - for i, tc := range tests { - got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.fee, tc.args.msgs, tc.args.memo)) - require.Equal(t, tc.want, got, "Got unexpected result on test case i: %d", i) - } -} - -func TestTxValidateBasic(t *testing.T) { - ctx := sdk.NewContext(nil, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) - - // keys and addresses - priv1, _, addr1 := KeyTestPubAddr() - priv2, _, addr2 := KeyTestPubAddr() - - // msg and signatures - msg1 := NewTestMsg(addr1, addr2) - fee := NewTestStdFee() - - msgs := []sdk.Msg{msg1} - - // require to fail validation upon invalid fee - badFee := NewTestStdFee() - badFee.Amount[0].Amount = sdk.NewInt(-5) - tx := NewTestTx(ctx, nil, nil, nil, nil, badFee) - - err := tx.ValidateBasic() - require.Error(t, err) - _, code, _ := sdkerrors.ABCIInfo(err, false) - require.Equal(t, sdkerrors.ErrInsufficientFee.ABCICode(), code) - - // require to fail validation when no signatures exist - privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} - tx = NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - err = tx.ValidateBasic() - require.Error(t, err) - _, code, _ = sdkerrors.ABCIInfo(err, false) - require.Equal(t, sdkerrors.ErrNoSignatures.ABCICode(), code) - - // require to fail validation when signatures do not match expected signers - privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0, 1}, []uint64{0, 0} - tx = NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - err = tx.ValidateBasic() - require.Error(t, err) - _, code, _ = sdkerrors.ABCIInfo(err, false) - require.Equal(t, sdkerrors.ErrUnauthorized.ABCICode(), code) - - // require to fail with invalid gas supplied - badFee = NewTestStdFee() - badFee.Gas = 9223372036854775808 - tx = NewTestTx(ctx, nil, nil, nil, nil, badFee) - - err = tx.ValidateBasic() - require.Error(t, err) - _, code, _ = sdkerrors.ABCIInfo(err, false) - require.Equal(t, sdkerrors.ErrInvalidRequest.ABCICode(), code) - - // require to pass when above criteria are matched - privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} - tx = NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - err = tx.ValidateBasic() - require.NoError(t, err) -} - -func TestDefaultTxEncoder(t *testing.T) { - cdc := codec.New() - sdk.RegisterCodec(cdc) - RegisterCodec(cdc) - cdc.RegisterConcrete(sdk.TestMsg{}, "cosmos-sdk/Test", nil) - encoder := DefaultTxEncoder(cdc) - - msgs := []sdk.Msg{sdk.NewTestMsg(addr)} - fee := NewTestStdFee() - sigs := []StdSignature{} - - tx := NewStdTx(msgs, fee, sigs, "") - - cdcBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) - - require.NoError(t, err) - encoderBytes, err := encoder(tx) - - require.NoError(t, err) - require.Equal(t, cdcBytes, encoderBytes) -} - -func TestStdSignatureMarshalYAML(t *testing.T) { - _, pubKey, _ := KeyTestPubAddr() - - testCases := []struct { - sig StdSignature - output string - }{ - { - StdSignature{}, - "|\n pubkey: \"\"\n signature: \"\"\n", - }, - { - StdSignature{PubKey: pubKey, Signature: []byte("dummySig")}, - fmt.Sprintf("|\n pubkey: %s\n signature: dummySig\n", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)), - }, - { - StdSignature{PubKey: pubKey, Signature: nil}, - fmt.Sprintf("|\n pubkey: %s\n signature: \"\"\n", sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey)), - }, - } - - for i, tc := range testCases { - bz, err := yaml.Marshal(tc.sig) - require.NoError(t, err) - require.Equal(t, tc.output, string(bz), "test case #%d", i) - } -} diff --git a/x/auth/types/test_common.go b/x/auth/types/test_common.go deleted file mode 100644 index 9e2ba20e98d2..000000000000 --- a/x/auth/types/test_common.go +++ /dev/null @@ -1,82 +0,0 @@ -// nolint noalias -package types - -import ( - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func NewTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { - return sdk.NewTestMsg(addrs...) -} - -func NewTestStdFee() StdFee { - return NewStdFee(100000, - sdk.NewCoins(sdk.NewInt64Coin("atom", 150)), - ) -} - -// coins to more than cover the fee -func NewTestCoins() sdk.Coins { - return sdk.Coins{ - sdk.NewInt64Coin("atom", 10000000), - } -} - -func KeyTestPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { - key := secp256k1.GenPrivKey() - pub := key.PubKey() - addr := sdk.AccAddress(pub.Address()) - return key, pub, addr -} - -func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee) sdk.Tx { - sigs := make([]StdSignature, len(privs)) - for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") - - sig, err := priv.Sign(signBytes) - if err != nil { - panic(err) - } - - sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} - } - - tx := NewStdTx(msgs, fee, sigs, "") - return tx -} - -func NewTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, memo string) sdk.Tx { - sigs := make([]StdSignature, len(privs)) - for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo) - - sig, err := priv.Sign(signBytes) - if err != nil { - panic(err) - } - - sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} - } - - tx := NewStdTx(msgs, fee, sigs, memo) - return tx -} - -func NewTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, signBytes []byte, memo string) sdk.Tx { - sigs := make([]StdSignature, len(privs)) - for i, priv := range privs { - sig, err := priv.Sign(signBytes) - if err != nil { - panic(err) - } - - sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} - } - - tx := NewStdTx(msgs, fee, sigs, memo) - return tx -} diff --git a/x/auth/types/txbuilder.go b/x/auth/types/txbuilder.go deleted file mode 100644 index 533596ccb649..000000000000 --- a/x/auth/types/txbuilder.go +++ /dev/null @@ -1,293 +0,0 @@ -package types - -import ( - "errors" - "fmt" - "io" - "os" - "strings" - - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keys" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// TxBuilder implements a transaction context created in SDK modules. -type TxBuilder struct { - txEncoder sdk.TxEncoder - keybase keys.Keybase - accountNumber uint64 - sequence uint64 - gas uint64 - gasAdjustment float64 - simulateAndExecute bool - chainID string - memo string - fees sdk.Coins - gasPrices sdk.DecCoins -} - -// NewTxBuilder returns a new initialized TxBuilder. -func NewTxBuilder( - txEncoder sdk.TxEncoder, accNumber, seq, gas uint64, gasAdj float64, - simulateAndExecute bool, chainID, memo string, fees sdk.Coins, gasPrices sdk.DecCoins, -) TxBuilder { - - return TxBuilder{ - txEncoder: txEncoder, - keybase: nil, - accountNumber: accNumber, - sequence: seq, - gas: gas, - gasAdjustment: gasAdj, - simulateAndExecute: simulateAndExecute, - chainID: chainID, - memo: memo, - fees: fees, - gasPrices: gasPrices, - } -} - -// NewTxBuilderFromCLI returns a new initialized TxBuilder with parameters from -// the command line using Viper. -func NewTxBuilderFromCLI(input io.Reader) TxBuilder { - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), input) - if err != nil { - panic(err) - } - txbldr := TxBuilder{ - keybase: kb, - accountNumber: uint64(viper.GetInt64(flags.FlagAccountNumber)), - sequence: uint64(viper.GetInt64(flags.FlagSequence)), - gas: flags.GasFlagVar.Gas, - gasAdjustment: viper.GetFloat64(flags.FlagGasAdjustment), - simulateAndExecute: flags.GasFlagVar.Simulate, - chainID: viper.GetString(flags.FlagChainID), - memo: viper.GetString(flags.FlagMemo), - } - - txbldr = txbldr.WithFees(viper.GetString(flags.FlagFees)) - txbldr = txbldr.WithGasPrices(viper.GetString(flags.FlagGasPrices)) - - return txbldr -} - -// TxEncoder returns the transaction encoder -func (bldr TxBuilder) TxEncoder() sdk.TxEncoder { return bldr.txEncoder } - -// AccountNumber returns the account number -func (bldr TxBuilder) AccountNumber() uint64 { return bldr.accountNumber } - -// Sequence returns the transaction sequence -func (bldr TxBuilder) Sequence() uint64 { return bldr.sequence } - -// Gas returns the gas for the transaction -func (bldr TxBuilder) Gas() uint64 { return bldr.gas } - -// GasAdjustment returns the gas adjustment -func (bldr TxBuilder) GasAdjustment() float64 { return bldr.gasAdjustment } - -// Keybase returns the keybase -func (bldr TxBuilder) Keybase() keys.Keybase { return bldr.keybase } - -// SimulateAndExecute returns the option to simulate and then execute the transaction -// using the gas from the simulation results -func (bldr TxBuilder) SimulateAndExecute() bool { return bldr.simulateAndExecute } - -// ChainID returns the chain id -func (bldr TxBuilder) ChainID() string { return bldr.chainID } - -// Memo returns the memo message -func (bldr TxBuilder) Memo() string { return bldr.memo } - -// Fees returns the fees for the transaction -func (bldr TxBuilder) Fees() sdk.Coins { return bldr.fees } - -// GasPrices returns the gas prices set for the transaction, if any. -func (bldr TxBuilder) GasPrices() sdk.DecCoins { return bldr.gasPrices } - -// WithTxEncoder returns a copy of the context with an updated codec. -func (bldr TxBuilder) WithTxEncoder(txEncoder sdk.TxEncoder) TxBuilder { - bldr.txEncoder = txEncoder - return bldr -} - -// WithChainID returns a copy of the context with an updated chainID. -func (bldr TxBuilder) WithChainID(chainID string) TxBuilder { - bldr.chainID = chainID - return bldr -} - -// WithGas returns a copy of the context with an updated gas. -func (bldr TxBuilder) WithGas(gas uint64) TxBuilder { - bldr.gas = gas - return bldr -} - -// WithFees returns a copy of the context with an updated fee. -func (bldr TxBuilder) WithFees(fees string) TxBuilder { - parsedFees, err := sdk.ParseCoins(fees) - if err != nil { - panic(err) - } - - bldr.fees = parsedFees - return bldr -} - -// WithGasPrices returns a copy of the context with updated gas prices. -func (bldr TxBuilder) WithGasPrices(gasPrices string) TxBuilder { - parsedGasPrices, err := sdk.ParseDecCoins(gasPrices) - if err != nil { - panic(err) - } - - bldr.gasPrices = parsedGasPrices - return bldr -} - -// WithKeybase returns a copy of the context with updated keybase. -func (bldr TxBuilder) WithKeybase(keybase keys.Keybase) TxBuilder { - bldr.keybase = keybase - return bldr -} - -// WithSequence returns a copy of the context with an updated sequence number. -func (bldr TxBuilder) WithSequence(sequence uint64) TxBuilder { - bldr.sequence = sequence - return bldr -} - -// WithMemo returns a copy of the context with an updated memo. -func (bldr TxBuilder) WithMemo(memo string) TxBuilder { - bldr.memo = strings.TrimSpace(memo) - return bldr -} - -// WithAccountNumber returns a copy of the context with an account number. -func (bldr TxBuilder) WithAccountNumber(accnum uint64) TxBuilder { - bldr.accountNumber = accnum - return bldr -} - -// BuildSignMsg builds a single message to be signed from a TxBuilder given a -// set of messages. It returns an error if a fee is supplied but cannot be -// parsed. -func (bldr TxBuilder) BuildSignMsg(msgs []sdk.Msg) (StdSignMsg, error) { - if bldr.chainID == "" { - return StdSignMsg{}, fmt.Errorf("chain ID required but not specified") - } - - fees := bldr.fees - if !bldr.gasPrices.IsZero() { - if !fees.IsZero() { - return StdSignMsg{}, errors.New("cannot provide both fees and gas prices") - } - - glDec := sdk.NewDec(int64(bldr.gas)) - - // Derive the fees based on the provided gas prices, where - // fee = ceil(gasPrice * gasLimit). - fees = make(sdk.Coins, len(bldr.gasPrices)) - for i, gp := range bldr.gasPrices { - fee := gp.Amount.Mul(glDec) - fees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) - } - } - - return StdSignMsg{ - ChainID: bldr.chainID, - AccountNumber: bldr.accountNumber, - Sequence: bldr.sequence, - Memo: bldr.memo, - Msgs: msgs, - Fee: NewStdFee(bldr.gas, fees), - }, nil -} - -// Sign signs a transaction given a name, passphrase, and a single message to -// signed. An error is returned if signing fails. -func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, error) { - sig, err := MakeSignature(bldr.keybase, name, passphrase, msg) - if err != nil { - return nil, err - } - - return bldr.txEncoder(NewStdTx(msg.Msgs, msg.Fee, []StdSignature{sig}, msg.Memo)) -} - -// BuildAndSign builds a single message to be signed, and signs a transaction -// with the built message given a name, passphrase, and a set of messages. -func (bldr TxBuilder) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]byte, error) { - msg, err := bldr.BuildSignMsg(msgs) - if err != nil { - return nil, err - } - - return bldr.Sign(name, passphrase, msg) -} - -// BuildTxForSim creates a StdSignMsg and encodes a transaction with the -// StdSignMsg with a single empty StdSignature for tx simulation. -func (bldr TxBuilder) BuildTxForSim(msgs []sdk.Msg) ([]byte, error) { - signMsg, err := bldr.BuildSignMsg(msgs) - if err != nil { - return nil, err - } - - // the ante handler will populate with a sentinel pubkey - sigs := []StdSignature{{}} - return bldr.txEncoder(NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo)) -} - -// SignStdTx appends a signature to a StdTx and returns a copy of it. If append -// is false, it replaces the signatures already attached with the new signature. -func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig bool) (signedStdTx StdTx, err error) { - if bldr.chainID == "" { - return StdTx{}, fmt.Errorf("chain ID required but not specified") - } - - stdSignature, err := MakeSignature(bldr.keybase, name, passphrase, StdSignMsg{ - ChainID: bldr.chainID, - AccountNumber: bldr.accountNumber, - Sequence: bldr.sequence, - Fee: stdTx.Fee, - Msgs: stdTx.GetMsgs(), - Memo: stdTx.GetMemo(), - }) - if err != nil { - return - } - - sigs := stdTx.Signatures - if len(sigs) == 0 || !appendSig { - sigs = []StdSignature{stdSignature} - } else { - sigs = append(sigs, stdSignature) - } - signedStdTx = NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo()) - return -} - -// MakeSignature builds a StdSignature given keybase, key name, passphrase, and a StdSignMsg. -func MakeSignature(keybase keys.Keybase, name, passphrase string, - msg StdSignMsg) (sig StdSignature, err error) { - - if keybase == nil { - keybase, err = keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), os.Stdin) - if err != nil { - return - } - } - - sigBytes, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes()) - if err != nil { - return - } - return StdSignature{ - PubKey: pubkey, - Signature: sigBytes, - }, nil -} diff --git a/x/auth/types/txbuilder_test.go b/x/auth/types/txbuilder_test.go deleted file mode 100644 index c0a0e337a9ab..000000000000 --- a/x/auth/types/txbuilder_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package types - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestTxBuilderBuild(t *testing.T) { - type fields struct { - TxEncoder sdk.TxEncoder - AccountNumber uint64 - Sequence uint64 - Gas uint64 - GasAdjustment float64 - SimulateGas bool - ChainID string - Memo string - Fees sdk.Coins - GasPrices sdk.DecCoins - } - defaultMsg := []sdk.Msg{sdk.NewTestMsg(addr)} - tests := []struct { - name string - fields fields - msgs []sdk.Msg - want StdSignMsg - wantErr bool - }{ - { - "builder with fees", - fields{ - TxEncoder: DefaultTxEncoder(codec.New()), - AccountNumber: 1, - Sequence: 1, - Gas: 200000, - GasAdjustment: 1.1, - SimulateGas: false, - ChainID: "test-chain", - Memo: "hello from Voyager 1!", - Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), - }, - defaultMsg, - StdSignMsg{ - ChainID: "test-chain", - AccountNumber: 1, - Sequence: 1, - Memo: "hello from Voyager 1!", - Msgs: defaultMsg, - Fee: NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), - }, - false, - }, - { - "builder with gas prices", - fields{ - TxEncoder: DefaultTxEncoder(codec.New()), - AccountNumber: 1, - Sequence: 1, - Gas: 200000, - GasAdjustment: 1.1, - SimulateGas: false, - ChainID: "test-chain", - Memo: "hello from Voyager 2!", - GasPrices: sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, - }, - defaultMsg, - StdSignMsg{ - ChainID: "test-chain", - AccountNumber: 1, - Sequence: 1, - Memo: "hello from Voyager 2!", - Msgs: defaultMsg, - Fee: NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), - }, - false, - }, - { - "no chain-id supplied", - fields{ - TxEncoder: DefaultTxEncoder(codec.New()), - AccountNumber: 1, - Sequence: 1, - Gas: 200000, - GasAdjustment: 1.1, - SimulateGas: false, - ChainID: "", - Memo: "hello from Voyager 1!", - Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), - }, - defaultMsg, - StdSignMsg{ - ChainID: "test-chain", - AccountNumber: 1, - Sequence: 1, - Memo: "hello from Voyager 1!", - Msgs: defaultMsg, - Fee: NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), - }, - true, - }, - { - "builder w/ fees and gas prices", - fields{ - TxEncoder: DefaultTxEncoder(codec.New()), - AccountNumber: 1, - Sequence: 1, - Gas: 200000, - GasAdjustment: 1.1, - SimulateGas: false, - ChainID: "test-chain", - Memo: "hello from Voyager 1!", - Fees: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), - GasPrices: sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, - }, - defaultMsg, - StdSignMsg{ - ChainID: "test-chain", - AccountNumber: 1, - Sequence: 1, - Memo: "hello from Voyager 1!", - Msgs: defaultMsg, - Fee: NewStdFee(200000, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))}), - }, - true, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - bldr := NewTxBuilder( - tt.fields.TxEncoder, tt.fields.AccountNumber, tt.fields.Sequence, - tt.fields.Gas, tt.fields.GasAdjustment, tt.fields.SimulateGas, - tt.fields.ChainID, tt.fields.Memo, tt.fields.Fees, tt.fields.GasPrices, - ) - got, err := bldr.BuildSignMsg(tt.msgs) - require.Equal(t, tt.wantErr, (err != nil)) - if err == nil { - require.True(t, reflect.DeepEqual(tt.want, got)) - } - }) - } -} diff --git a/x/auth/vesting/alias.go b/x/auth/vesting/alias.go deleted file mode 100644 index 387ca51e51f4..000000000000 --- a/x/auth/vesting/alias.go +++ /dev/null @@ -1,33 +0,0 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/auth/vesting/types/ -package vesting - -import ( - "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" -) - -var ( - // functions aliases - RegisterCodec = types.RegisterCodec - NewBaseVestingAccount = types.NewBaseVestingAccount - NewContinuousVestingAccountRaw = types.NewContinuousVestingAccountRaw - NewContinuousVestingAccount = types.NewContinuousVestingAccount - NewPeriodicVestingAccountRaw = types.NewPeriodicVestingAccountRaw - NewPeriodicVestingAccount = types.NewPeriodicVestingAccount - NewDelayedVestingAccountRaw = types.NewDelayedVestingAccountRaw - NewDelayedVestingAccount = types.NewDelayedVestingAccount - - // variable aliases - VestingCdc = types.VestingCdc -) - -type ( - BaseVestingAccount = types.BaseVestingAccount - ContinuousVestingAccount = types.ContinuousVestingAccount - PeriodicVestingAccount = types.PeriodicVestingAccount - DelayedVestingAccount = types.DelayedVestingAccount - Period = types.Period - Periods = types.Periods -) diff --git a/x/auth/vesting/client/cli/cli_test.go b/x/auth/vesting/client/cli/cli_test.go new file mode 100644 index 000000000000..ebd417a62b20 --- /dev/null +++ b/x/auth/vesting/client/cli/cli_test.go @@ -0,0 +1,139 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + 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/auth/vesting/client/cli" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestNewMsgCreateVestingAccountCmd() { + val := s.network.Validators[0] + + testCases := map[string]struct { + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + "create a continuous vesting account": { + args: []string{ + sdk.AccAddress("addr2_______________").String(), + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String(), + "4070908800", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + 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()), + }, + expectErr: false, + respType: &sdk.TxResponse{}, + expectedCode: 0, + }, + "create a delayed vesting account": { + args: []string{ + sdk.AccAddress("addr3_______________").String(), + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String(), + "4070908800", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + fmt.Sprintf("--%s=true", cli.FlagDelayed), + 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()), + }, + expectErr: false, + respType: &sdk.TxResponse{}, + expectedCode: 0, + }, + "invalid address": { + args: []string{ + sdk.AccAddress("addr4").String(), + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String(), + "4070908800", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + }, + expectErr: true, + respType: &sdk.TxResponse{}, + expectedCode: 0, + }, + "invalid coins": { + args: []string{ + sdk.AccAddress("addr4_______________").String(), + "fooo", + "4070908800", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + }, + expectErr: true, + respType: &sdk.TxResponse{}, + expectedCode: 0, + }, + "invalid end time": { + args: []string{ + sdk.AccAddress("addr4_______________").String(), + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String(), + "-4070908800", + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), + }, + expectErr: true, + respType: &sdk.TxResponse{}, + expectedCode: 0, + }, + } + + for name, tc := range testCases { + tc := tc + + s.Run(name, func() { + clientCtx := val.ClientCtx + + bw, err := clitestutil.ExecTestCLICmd(clientCtx, cli.NewMsgCreateVestingAccountCmd(), tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(bw.Bytes(), tc.respType), bw.String()) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/auth/vesting/client/cli/tx.go b/x/auth/vesting/client/cli/tx.go new file mode 100644 index 000000000000..9cacdfdaab25 --- /dev/null +++ b/x/auth/vesting/client/cli/tx.go @@ -0,0 +1,84 @@ +package cli + +import ( + "strconv" + + "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/x/auth/vesting/types" +) + +// Transaction command flags +const ( + FlagDelayed = "delayed" +) + +// GetTxCmd returns vesting module's transaction commands. +func GetTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Vesting transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewMsgCreateVestingAccountCmd(), + ) + + return txCmd +} + +// NewMsgCreateVestingAccountCmd returns a CLI command handler for creating a +// MsgCreateVestingAccount transaction. +func NewMsgCreateVestingAccountCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-vesting-account [to_address] [amount] [end_time]", + Short: "Create a new vesting account funded with an allocation of tokens.", + Long: `Create a new vesting account funded with an allocation of tokens. The +account can either be a delayed or continuous vesting account, which is determined +by the '--delayed' flag. All vesting accouts created will have their start time +set by the committed block's time. The end_time must be provided as a UNIX epoch +timestamp.`, + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + toAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinsNormalized(args[1]) + if err != nil { + return err + } + + endTime, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return err + } + + delayed, _ := cmd.Flags().GetBool(FlagDelayed) + + msg := types.NewMsgCreateVestingAccount(clientCtx.GetFromAddress(), toAddr, amount, endTime, delayed) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().Bool(FlagDelayed, false, "Create a delayed vesting account if true") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/auth/vesting/exported/exported.go b/x/auth/vesting/exported/exported.go index 16b07d3614f4..f47d24ae9eb1 100644 --- a/x/auth/vesting/exported/exported.go +++ b/x/auth/vesting/exported/exported.go @@ -3,17 +3,30 @@ package exported import ( "time" + "github.com/cosmos/cosmos-sdk/x/auth/types" + sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" ) // VestingAccount defines an account type that vests coins via a vesting schedule. type VestingAccount interface { - authexported.Account + types.AccountI + + // LockedCoins returns the set of coins that are not spendable (i.e. locked). + // + // To get spendable coins of a vesting account, first the total balance must + // be retrieved and the locked tokens can be subtracted from the total balance. + // Note, the spendable balance can be negative. + LockedCoins(blockTime time.Time) sdk.Coins + + // TrackDelegation performs internal vesting accounting necessary when + // delegating from a vesting account. It accepts the current block time, the + // delegation amount and balance of all coins whose denomination exists in + // the account's original vesting balance. + TrackDelegation(blockTime time.Time, balance, amount sdk.Coins) - // Delegation and undelegation accounting that returns the resulting base - // coins amount. - TrackDelegation(blockTime time.Time, amount sdk.Coins) + // TrackUndelegation performs internal vesting accounting necessary when a + // vesting account performs an undelegation. TrackUndelegation(amount sdk.Coins) GetVestedCoins(blockTime time.Time) sdk.Coins diff --git a/x/auth/vesting/handler.go b/x/auth/vesting/handler.go new file mode 100644 index 000000000000..1d32e9969bb5 --- /dev/null +++ b/x/auth/vesting/handler.go @@ -0,0 +1,26 @@ +package vesting + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +// NewHandler returns a handler for x/auth message types. +func NewHandler(ak keeper.AccountKeeper, bk types.BankKeeper) sdk.Handler { + msgServer := NewMsgServerImpl(ak, bk) + + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case *types.MsgCreateVestingAccount: + res, err := msgServer.CreateVestingAccount(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/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go new file mode 100644 index 000000000000..490fc59971f1 --- /dev/null +++ b/x/auth/vesting/handler_test.go @@ -0,0 +1,96 @@ +package vesting_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/auth/vesting" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +type HandlerTestSuite struct { + suite.Suite + + handler sdk.Handler + app *simapp.SimApp +} + +func (suite *HandlerTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + + suite.handler = vesting.NewHandler(app.AccountKeeper, app.BankKeeper) + suite.app = app +} + +func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() { + ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) + + balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addr3 := sdk.AccAddress([]byte("addr3_______________")) + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + suite.app.AccountKeeper.SetAccount(ctx, acc1) + suite.Require().NoError(suite.app.BankKeeper.SetBalances(ctx, addr1, balances)) + + testCases := []struct { + name string + msg *types.MsgCreateVestingAccount + expectErr bool + }{ + { + name: "create delayed vesting account", + msg: types.NewMsgCreateVestingAccount(addr1, addr2, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, true), + expectErr: false, + }, + { + name: "create continuous vesting account", + msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), + expectErr: false, + }, + { + name: "continuous vesting account already exists", + msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), + expectErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + res, err := suite.handler(ctx, tc.msg) + if tc.expectErr { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().NotNil(res) + + toAddr, err := sdk.AccAddressFromBech32(tc.msg.ToAddress) + suite.Require().NoError(err) + accI := suite.app.AccountKeeper.GetAccount(ctx, toAddr) + suite.Require().NotNil(accI) + + if tc.msg.Delayed { + acc, ok := accI.(*types.DelayedVestingAccount) + suite.Require().True(ok) + suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) + } else { + acc, ok := accI.(*types.ContinuousVestingAccount) + suite.Require().True(ok) + suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) + } + } + }) + } +} + +func TestHandlerTestSuite(t *testing.T) { + suite.Run(t, new(HandlerTestSuite)) +} diff --git a/x/auth/vesting/module.go b/x/auth/vesting/module.go new file mode 100644 index 000000000000..3cc579a40e37 --- /dev/null +++ b/x/auth/vesting/module.go @@ -0,0 +1,129 @@ +package vesting + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the sub-vesting +// module. The module itself contain no special logic or state other than message +// handling. +type AppModuleBasic struct{} + +// Name returns the module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterCodec registers the module's types with the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterInterfaces registers the module's interfaces and implementations with +// the given interface registry. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns the module's default genesis state as raw bytes. +func (AppModuleBasic) DefaultGenesis(_ codec.JSONMarshaler) json.RawMessage { + return []byte("{}") +} + +// ValidateGenesis performs genesis state validation. Currently, this is a no-op. +func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, _ client.TxEncodingConfig, bz json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes registers module's REST handlers. Currently, this is a no-op. +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the module's gRPC Gateway routes. Currently, this +// is a no-op. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) {} + +// GetTxCmd returns the root tx command for the auth module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns the module's root query command. Currently, this is a no-op. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil +} + +// AppModule extends the AppModuleBasic implementation by implementing the +// AppModule interface. +type AppModule struct { + AppModuleBasic + + accountKeeper keeper.AccountKeeper + bankKeeper types.BankKeeper +} + +func NewAppModule(ak keeper.AccountKeeper, bk types.BankKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + accountKeeper: ak, + bankKeeper: bk, + } +} + +// RegisterInvariants performs a no-op; there are no invariants to enforce. +func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// Route returns the module's message router and handler. +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.accountKeeper, am.bankKeeper)) +} + +// QuerierRoute returns an empty string as the module contains no query +// functionality. +func (AppModule) QuerierRoute() string { return "" } + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), NewMsgServerImpl(am.accountKeeper, am.bankKeeper)) +} + +// LegacyQuerierHandler performs a no-op. +func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier { + return nil +} + +// InitGenesis performs a no-op. +func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONMarshaler, _ json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// BeginBlock performs a no-op. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock performs a no-op. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis is always empty, as InitGenesis does nothing either. +func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return am.DefaultGenesis(cdc) +} diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go new file mode 100644 index 000000000000..dadd65dbcf1c --- /dev/null +++ b/x/auth/vesting/msg_server.go @@ -0,0 +1,100 @@ +package vesting + +import ( + "context" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" +) + +type msgServer struct { + keeper.AccountKeeper + types.BankKeeper +} + +// NewMsgServerImpl returns an implementation of the vesting MsgServer interface, +// wrapping the corresponding AccountKeeper and BankKeeper. +func NewMsgServerImpl(k keeper.AccountKeeper, bk types.BankKeeper) types.MsgServer { + return &msgServer{AccountKeeper: k, BankKeeper: bk} +} + +var _ types.MsgServer = msgServer{} + +func (s msgServer) CreateVestingAccount(goCtx context.Context, msg *types.MsgCreateVestingAccount) (*types.MsgCreateVestingAccountResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + ak := s.AccountKeeper + bk := s.BankKeeper + + if err := bk.SendEnabledCoins(ctx, msg.Amount...); err != nil { + return nil, err + } + + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return nil, err + } + to, err := sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + return nil, err + } + + if bk.BlockedAddr(to) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress) + } + + if acc := ak.GetAccount(ctx, to); acc != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress) + } + + baseAccount := ak.NewAccountWithAddress(ctx, to) + if _, ok := baseAccount.(*authtypes.BaseAccount); !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid account type; expected: BaseAccount, got: %T", baseAccount) + } + + baseVestingAccount := types.NewBaseVestingAccount(baseAccount.(*authtypes.BaseAccount), msg.Amount.Sort(), msg.EndTime) + + var acc authtypes.AccountI + + if msg.Delayed { + acc = types.NewDelayedVestingAccountRaw(baseVestingAccount) + } else { + acc = types.NewContinuousVestingAccountRaw(baseVestingAccount, ctx.BlockTime().Unix()) + } + + ak.SetAccount(ctx, acc) + + defer func() { + telemetry.IncrCounter(1, "new", "account") + + for _, a := range msg.Amount { + if a.Amount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "create_vesting_account"}, + float32(a.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", a.Denom)}, + ) + } + } + }() + + err = bk.SendCoins(ctx, from, to, msg.Amount) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + ) + + return &types.MsgCreateVestingAccountResponse{}, nil +} diff --git a/x/auth/vesting/types/codec.go b/x/auth/vesting/types/codec.go index 556e71117849..eeaf80a95ad6 100644 --- a/x/auth/vesting/types/codec.go +++ b/x/auth/vesting/types/codec.go @@ -2,11 +2,16 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" ) -// RegisterCodec registers concrete types on the codec -func RegisterCodec(cdc *codec.Codec) { +// RegisterLegacyAminoCodec registers the vesting interfaces and concrete types on the +// provided LegacyAmino codec. These types are used for Amino JSON serialization +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterInterface((*exported.VestingAccount)(nil), nil) cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil) cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil) @@ -14,11 +19,44 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil) } -// VestingCdc module wide codec -var VestingCdc *codec.Codec +// RegisterInterface associates protoName with AccountI and VestingAccount +// Interfaces and creates a registry of it's concrete implementations +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterInterface( + "cosmos.vesting.v1beta1.VestingAccount", + (*exported.VestingAccount)(nil), + &ContinuousVestingAccount{}, + &DelayedVestingAccount{}, + &PeriodicVestingAccount{}, + ) + + registry.RegisterImplementations( + (*authtypes.AccountI)(nil), + &BaseVestingAccount{}, + &DelayedVestingAccount{}, + &ContinuousVestingAccount{}, + &PeriodicVestingAccount{}, + ) + + registry.RegisterImplementations( + (*authtypes.GenesisAccount)(nil), + &BaseVestingAccount{}, + &DelayedVestingAccount{}, + &ContinuousVestingAccount{}, + &PeriodicVestingAccount{}, + ) + + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgCreateVestingAccount{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var amino = codec.NewLegacyAmino() func init() { - VestingCdc = codec.New() - RegisterCodec(VestingCdc) - VestingCdc.Seal() + RegisterLegacyAminoCodec(amino) + amino.Seal() } diff --git a/x/auth/vesting/types/common_test.go b/x/auth/vesting/types/common_test.go new file mode 100644 index 000000000000..4f361059ad3c --- /dev/null +++ b/x/auth/vesting/types/common_test.go @@ -0,0 +1,10 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/simapp" +) + +var ( + app = simapp.Setup(false) + appCodec, _ = simapp.MakeCodecs() +) diff --git a/x/auth/vesting/types/constants.go b/x/auth/vesting/types/constants.go new file mode 100644 index 000000000000..1d0b0ebbb326 --- /dev/null +++ b/x/auth/vesting/types/constants.go @@ -0,0 +1,12 @@ +package types + +const ( + // ModuleName defines the module's name. + ModuleName = "vesting" + + // AttributeValueCategory is an alias for the message event value. + AttributeValueCategory = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName +) diff --git a/x/auth/vesting/types/expected_keepers.go b/x/auth/vesting/types/expected_keepers.go new file mode 100644 index 000000000000..8212f6e3757f --- /dev/null +++ b/x/auth/vesting/types/expected_keepers.go @@ -0,0 +1,13 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// BankKeeper defines the expected interface contract the vesting module requires +// for creating vesting accounts with funds. +type BankKeeper interface { + SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + BlockedAddr(addr sdk.AccAddress) bool +} diff --git a/x/auth/vesting/types/genesis_test.go b/x/auth/vesting/types/genesis_test.go index aee8d1fa091a..98aabb15cfde 100644 --- a/x/auth/vesting/types/genesis_test.go +++ b/x/auth/vesting/types/genesis_test.go @@ -4,10 +4,9 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -21,21 +20,21 @@ var ( // require invalid vesting account fails validation func TestValidateGenesisInvalidAccounts(t *testing.T) { acc1 := authtypes.NewBaseAccountWithAddress(sdk.AccAddress(addr1)) - acc1.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) - baseVestingAcc, err := NewBaseVestingAccount(&acc1, acc1.Coins, 1548775410) - require.NoError(t, err) + acc1Balance := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + baseVestingAcc := NewBaseVestingAccount(acc1, acc1Balance, 1548775410) + // invalid delegated vesting - baseVestingAcc.DelegatedVesting = acc1.Coins.Add(acc1.Coins...) + baseVestingAcc.DelegatedVesting = acc1Balance.Add(acc1Balance...) acc2 := authtypes.NewBaseAccountWithAddress(sdk.AccAddress(addr2)) - acc2.Coins = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) + // acc2Balance := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 150)) - genAccs := make([]exported.GenesisAccount, 2) + genAccs := make([]authtypes.GenesisAccount, 2) genAccs[0] = baseVestingAcc - genAccs[1] = &acc2 + genAccs[1] = acc2 require.Error(t, authtypes.ValidateGenAccounts(genAccs)) - baseVestingAcc.DelegatedVesting = acc1.Coins + baseVestingAcc.DelegatedVesting = acc1Balance genAccs[0] = baseVestingAcc require.NoError(t, authtypes.ValidateGenAccounts(genAccs)) // invalid start time diff --git a/x/auth/vesting/types/msgs.go b/x/auth/vesting/types/msgs.go new file mode 100644 index 000000000000..3308e0304881 --- /dev/null +++ b/x/auth/vesting/types/msgs.go @@ -0,0 +1,77 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// TypeMsgCreateVestingAccount defines the type value for a MsgCreateVestingAccount. +const TypeMsgCreateVestingAccount = "msg_create_vesting_account" + +var _ sdk.Msg = &MsgCreateVestingAccount{} + +// NewMsgCreateVestingAccount returns a reference to a new MsgCreateVestingAccount. +//nolint:interfacer +func NewMsgCreateVestingAccount(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, endTime int64, delayed bool) *MsgCreateVestingAccount { + return &MsgCreateVestingAccount{ + FromAddress: fromAddr.String(), + ToAddress: toAddr.String(), + Amount: amount, + EndTime: endTime, + Delayed: delayed, + } +} + +// Route returns the message route for a MsgCreateVestingAccount. +func (msg MsgCreateVestingAccount) Route() string { return RouterKey } + +// Type returns the message type for a MsgCreateVestingAccount. +func (msg MsgCreateVestingAccount) Type() string { return TypeMsgCreateVestingAccount } + +// ValidateBasic Implements Msg. +func (msg MsgCreateVestingAccount) ValidateBasic() error { + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return err + } + to, err := sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + return err + } + if err := sdk.VerifyAddressFormat(from); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid sender address: %s", err) + } + + if err := sdk.VerifyAddressFormat(to); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid recipient address: %s", err) + } + + if !msg.Amount.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) + } + + if !msg.Amount.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) + } + + if msg.EndTime <= 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid end time") + } + + return nil +} + +// GetSignBytes returns the bytes all expected signers must sign over for a +// MsgCreateVestingAccount. +func (msg MsgCreateVestingAccount) GetSignBytes() []byte { + return sdk.MustSortJSON(amino.MustMarshalJSON(&msg)) +} + +// GetSigners returns the expected signers for a MsgCreateVestingAccount. +func (msg MsgCreateVestingAccount) GetSigners() []sdk.AccAddress { + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{from} +} diff --git a/x/auth/vesting/types/period.go b/x/auth/vesting/types/period.go index 1c4a5e2f4735..a3b32fcaf7b3 100644 --- a/x/auth/vesting/types/period.go +++ b/x/auth/vesting/types/period.go @@ -4,22 +4,16 @@ import ( "fmt" "strings" - sdk "github.com/cosmos/cosmos-sdk/types" + yaml "gopkg.in/yaml.v2" ) -// Period defines a length of time and amount of coins that will vest -type Period struct { - Length int64 `json:"length" yaml:"length"` // length of the period, in seconds - Amount sdk.Coins `json:"amount" yaml:"amount"` // amount of coins vesting during this period -} - // Periods stores all vesting periods passed as part of a PeriodicVestingAccount type Periods []Period // String Period implements stringer interface func (p Period) String() string { - return fmt.Sprintf(`Length: %d - Amount: %s`, p.Length, p.Amount) + out, _ := yaml.Marshal(p) + return string(out) } // String Periods implements stringer interface @@ -28,6 +22,7 @@ func (vp Periods) String() string { for _, period := range vp { periodsListString = append(periodsListString, period.String()) } + return strings.TrimSpace(fmt.Sprintf(`Vesting Periods: %s`, strings.Join(periodsListString, ", "))) } diff --git a/x/auth/vesting/types/test_common.go b/x/auth/vesting/types/test_common.go index ce73d6defdd3..0e22ea2352dd 100644 --- a/x/auth/vesting/types/test_common.go +++ b/x/auth/vesting/types/test_common.go @@ -1,16 +1,16 @@ -// nolint noalias package types import ( - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" ) // NewTestMsg generates a test message -func NewTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { - return sdk.NewTestMsg(addrs...) +func NewTestMsg(addrs ...sdk.AccAddress) *testdata.TestMsg { + return testdata.NewTestMsg(addrs...) } // NewTestCoins coins to more than cover the fee @@ -21,7 +21,7 @@ func NewTestCoins() sdk.Coins { } // KeyTestPubAddr generates a test key pair -func KeyTestPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { +func KeyTestPubAddr() (cryptotypes.PrivKey, cryptotypes.PubKey, sdk.AccAddress) { key := secp256k1.GenPrivKey() pub := key.PubKey() addr := sdk.AccAddress(pub.Address()) diff --git a/x/auth/vesting/types/tx.pb.go b/x/auth/vesting/types/tx.pb.go new file mode 100644 index 000000000000..d07a154dd326 --- /dev/null +++ b/x/auth/vesting/types/tx.pb.go @@ -0,0 +1,775 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/vesting/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgCreateVestingAccount defines a message that enables creating a vesting +// account. +type MsgCreateVestingAccount struct { + FromAddress string `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty" yaml:"from_address"` + ToAddress string `protobuf:"bytes,2,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty" yaml:"to_address"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` + EndTime int64 `protobuf:"varint,4,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty" yaml:"end_time"` + Delayed bool `protobuf:"varint,5,opt,name=delayed,proto3" json:"delayed,omitempty"` +} + +func (m *MsgCreateVestingAccount) Reset() { *m = MsgCreateVestingAccount{} } +func (m *MsgCreateVestingAccount) String() string { return proto.CompactTextString(m) } +func (*MsgCreateVestingAccount) ProtoMessage() {} +func (*MsgCreateVestingAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_5338ca97811f9792, []int{0} +} +func (m *MsgCreateVestingAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateVestingAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateVestingAccount.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 *MsgCreateVestingAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateVestingAccount.Merge(m, src) +} +func (m *MsgCreateVestingAccount) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateVestingAccount) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateVestingAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateVestingAccount proto.InternalMessageInfo + +func (m *MsgCreateVestingAccount) GetFromAddress() string { + if m != nil { + return m.FromAddress + } + return "" +} + +func (m *MsgCreateVestingAccount) GetToAddress() string { + if m != nil { + return m.ToAddress + } + return "" +} + +func (m *MsgCreateVestingAccount) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +func (m *MsgCreateVestingAccount) GetEndTime() int64 { + if m != nil { + return m.EndTime + } + return 0 +} + +func (m *MsgCreateVestingAccount) GetDelayed() bool { + if m != nil { + return m.Delayed + } + return false +} + +// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type. +type MsgCreateVestingAccountResponse struct { +} + +func (m *MsgCreateVestingAccountResponse) Reset() { *m = MsgCreateVestingAccountResponse{} } +func (m *MsgCreateVestingAccountResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateVestingAccountResponse) ProtoMessage() {} +func (*MsgCreateVestingAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5338ca97811f9792, []int{1} +} +func (m *MsgCreateVestingAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateVestingAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateVestingAccountResponse.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 *MsgCreateVestingAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateVestingAccountResponse.Merge(m, src) +} +func (m *MsgCreateVestingAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateVestingAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateVestingAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateVestingAccountResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateVestingAccount)(nil), "cosmos.vesting.v1beta1.MsgCreateVestingAccount") + proto.RegisterType((*MsgCreateVestingAccountResponse)(nil), "cosmos.vesting.v1beta1.MsgCreateVestingAccountResponse") +} + +func init() { proto.RegisterFile("cosmos/vesting/v1beta1/tx.proto", fileDescriptor_5338ca97811f9792) } + +var fileDescriptor_5338ca97811f9792 = []byte{ + // 410 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0xbd, 0xae, 0xd3, 0x30, + 0x14, 0x8e, 0x6f, 0x2e, 0xf7, 0xc7, 0x17, 0x09, 0x91, 0x16, 0x1a, 0x3a, 0xc4, 0x21, 0x53, 0x16, + 0x6c, 0x5a, 0x90, 0x90, 0xba, 0x35, 0x1d, 0x51, 0x97, 0x08, 0x31, 0xb0, 0x54, 0x4e, 0x62, 0xd2, + 0x88, 0x26, 0xae, 0x62, 0xb7, 0x6a, 0x37, 0x46, 0x46, 0x1e, 0x81, 0x99, 0xa7, 0x60, 0xec, 0xd8, + 0x91, 0x29, 0xa0, 0x76, 0x61, 0xee, 0x13, 0xa0, 0xc4, 0x49, 0x61, 0x68, 0x91, 0x98, 0xec, 0xa3, + 0xef, 0xc7, 0xe7, 0x7c, 0x3e, 0x10, 0x85, 0x5c, 0xa4, 0x5c, 0x90, 0x25, 0x13, 0x32, 0xc9, 0x62, + 0xb2, 0xec, 0x05, 0x4c, 0xd2, 0x1e, 0x91, 0x2b, 0x3c, 0xcf, 0xb9, 0xe4, 0xc6, 0x63, 0x45, 0xc0, + 0x35, 0x01, 0xd7, 0x84, 0x6e, 0x3b, 0xe6, 0x31, 0xaf, 0x28, 0xa4, 0xbc, 0x29, 0x76, 0xd7, 0xaa, + 0xed, 0x02, 0x2a, 0xd8, 0xd1, 0x2b, 0xe4, 0x49, 0xa6, 0x70, 0xe7, 0xdb, 0x05, 0xec, 0x8c, 0x45, + 0x3c, 0xca, 0x19, 0x95, 0xec, 0xad, 0xb2, 0x1c, 0x86, 0x21, 0x5f, 0x64, 0xd2, 0x18, 0xc0, 0xfb, + 0xef, 0x73, 0x9e, 0x4e, 0x68, 0x14, 0xe5, 0x4c, 0x08, 0x13, 0xd8, 0xc0, 0xbd, 0xf5, 0x3a, 0x87, + 0x02, 0xb5, 0xd6, 0x34, 0x9d, 0x0d, 0x9c, 0xbf, 0x51, 0xc7, 0xbf, 0x2b, 0xcb, 0xa1, 0xaa, 0x8c, + 0x97, 0x10, 0x4a, 0x7e, 0x54, 0x5e, 0x54, 0xca, 0x47, 0x87, 0x02, 0x3d, 0x54, 0xca, 0x3f, 0x98, + 0xe3, 0xdf, 0x4a, 0xde, 0xa8, 0x42, 0x78, 0x45, 0xd3, 0xf2, 0x6d, 0x53, 0xb7, 0x75, 0xf7, 0xae, + 0xff, 0x04, 0xd7, 0xc3, 0x96, 0xed, 0x37, 0x93, 0xe2, 0x11, 0x4f, 0x32, 0xef, 0xf9, 0xa6, 0x40, + 0xda, 0xd7, 0x1f, 0xc8, 0x8d, 0x13, 0x39, 0x5d, 0x04, 0x38, 0xe4, 0x29, 0xa9, 0x67, 0x55, 0xc7, + 0x33, 0x11, 0x7d, 0x20, 0x72, 0x3d, 0x67, 0xa2, 0x12, 0x08, 0xbf, 0xb6, 0x36, 0x30, 0xbc, 0x61, + 0x59, 0x34, 0x91, 0x49, 0xca, 0xcc, 0x4b, 0x1b, 0xb8, 0xba, 0xd7, 0x3a, 0x14, 0xe8, 0x81, 0x6a, + 0xac, 0x41, 0x1c, 0xff, 0x9a, 0x65, 0xd1, 0x9b, 0x24, 0x65, 0x86, 0x09, 0xaf, 0x23, 0x36, 0xa3, + 0x6b, 0x16, 0x99, 0xf7, 0x6c, 0xe0, 0xde, 0xf8, 0x4d, 0x39, 0xb8, 0xfc, 0xf5, 0x05, 0x01, 0xe7, + 0x29, 0x44, 0x67, 0x12, 0xf4, 0x99, 0x98, 0xf3, 0x4c, 0xb0, 0xfe, 0x27, 0x00, 0xf5, 0xb1, 0x88, + 0x8d, 0x8f, 0x00, 0xb6, 0x4f, 0x46, 0x4d, 0xf0, 0xe9, 0x5f, 0xc5, 0x67, 0x9c, 0xbb, 0xaf, 0xfe, + 0x53, 0xd0, 0xb4, 0xe2, 0xbd, 0xde, 0xec, 0x2c, 0xb0, 0xdd, 0x59, 0xe0, 0xe7, 0xce, 0x02, 0x9f, + 0xf7, 0x96, 0xb6, 0xdd, 0x5b, 0xda, 0xf7, 0xbd, 0xa5, 0xbd, 0xeb, 0xfd, 0x33, 0xc9, 0x15, 0xa1, + 0x0b, 0x39, 0x3d, 0xae, 0x65, 0x15, 0x6c, 0x70, 0x55, 0x2d, 0xd1, 0x8b, 0xdf, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0x28, 0xaf, 0xe5, 0xb5, 0x02, 0x00, 0x00, +} + +func (this *MsgCreateVestingAccount) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgCreateVestingAccount) + if !ok { + that2, ok := that.(MsgCreateVestingAccount) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.FromAddress != that1.FromAddress { + return false + } + if this.ToAddress != that1.ToAddress { + return false + } + if len(this.Amount) != len(that1.Amount) { + return false + } + for i := range this.Amount { + if !this.Amount[i].Equal(&that1.Amount[i]) { + return false + } + } + if this.EndTime != that1.EndTime { + return false + } + if this.Delayed != that1.Delayed { + return false + } + return true +} + +// 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 { + // CreateVestingAccount defines a method that enables creating a vesting + // account. + CreateVestingAccount(ctx context.Context, in *MsgCreateVestingAccount, opts ...grpc.CallOption) (*MsgCreateVestingAccountResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateVestingAccount(ctx context.Context, in *MsgCreateVestingAccount, opts ...grpc.CallOption) (*MsgCreateVestingAccountResponse, error) { + out := new(MsgCreateVestingAccountResponse) + err := c.cc.Invoke(ctx, "/cosmos.vesting.v1beta1.Msg/CreateVestingAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateVestingAccount defines a method that enables creating a vesting + // account. + CreateVestingAccount(context.Context, *MsgCreateVestingAccount) (*MsgCreateVestingAccountResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateVestingAccount(ctx context.Context, req *MsgCreateVestingAccount) (*MsgCreateVestingAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateVestingAccount not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateVestingAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateVestingAccount) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateVestingAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.vesting.v1beta1.Msg/CreateVestingAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateVestingAccount(ctx, req.(*MsgCreateVestingAccount)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.vesting.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateVestingAccount", + Handler: _Msg_CreateVestingAccount_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/vesting/v1beta1/tx.proto", +} + +func (m *MsgCreateVestingAccount) 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 *MsgCreateVestingAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateVestingAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Delayed { + i-- + if m.Delayed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.EndTime != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.EndTime)) + i-- + dAtA[i] = 0x20 + } + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[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.ToAddress) > 0 { + i -= len(m.ToAddress) + copy(dAtA[i:], m.ToAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.FromAddress) > 0 { + i -= len(m.FromAddress) + copy(dAtA[i:], m.FromAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateVestingAccountResponse) 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 *MsgCreateVestingAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateVestingAccountResponse) 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 *MsgCreateVestingAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FromAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ToAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if m.EndTime != 0 { + n += 1 + sovTx(uint64(m.EndTime)) + } + if m.Delayed { + n += 2 + } + return n +} + +func (m *MsgCreateVestingAccountResponse) 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 *MsgCreateVestingAccount) 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: MsgCreateVestingAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateVestingAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromAddress", 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.FromAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddress", 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.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EndTime", wireType) + } + m.EndTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EndTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Delayed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Delayed = bool(v != 0) + 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 *MsgCreateVestingAccountResponse) 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: MsgCreateVestingAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateVestingAccountResponse: 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/auth/vesting/types/vesting.pb.go b/x/auth/vesting/types/vesting.pb.go new file mode 100644 index 000000000000..7672ce66f8e0 --- /dev/null +++ b/x/auth/vesting/types/vesting.pb.go @@ -0,0 +1,1391 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/vesting/v1beta1/vesting.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "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 + +// BaseVestingAccount implements the VestingAccount interface. It contains all +// the necessary fields needed for any vesting account implementation. +type BaseVestingAccount struct { + *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty"` + OriginalVesting github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=original_vesting,json=originalVesting,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"original_vesting" yaml:"original_vesting"` + DelegatedFree github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=delegated_free,json=delegatedFree,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=delegated_vesting,json=delegatedVesting,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `protobuf:"varint,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty" yaml:"end_time"` +} + +func (m *BaseVestingAccount) Reset() { *m = BaseVestingAccount{} } +func (*BaseVestingAccount) ProtoMessage() {} +func (*BaseVestingAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_89e80273ca606d6e, []int{0} +} +func (m *BaseVestingAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BaseVestingAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BaseVestingAccount.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 *BaseVestingAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_BaseVestingAccount.Merge(m, src) +} +func (m *BaseVestingAccount) XXX_Size() int { + return m.Size() +} +func (m *BaseVestingAccount) XXX_DiscardUnknown() { + xxx_messageInfo_BaseVestingAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_BaseVestingAccount proto.InternalMessageInfo + +// ContinuousVestingAccount implements the VestingAccount interface. It +// continuously vests by unlocking coins linearly with respect to time. +type ContinuousVestingAccount struct { + *BaseVestingAccount `protobuf:"bytes,1,opt,name=base_vesting_account,json=baseVestingAccount,proto3,embedded=base_vesting_account" json:"base_vesting_account,omitempty"` + StartTime int64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty" yaml:"start_time"` +} + +func (m *ContinuousVestingAccount) Reset() { *m = ContinuousVestingAccount{} } +func (*ContinuousVestingAccount) ProtoMessage() {} +func (*ContinuousVestingAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_89e80273ca606d6e, []int{1} +} +func (m *ContinuousVestingAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContinuousVestingAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContinuousVestingAccount.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 *ContinuousVestingAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContinuousVestingAccount.Merge(m, src) +} +func (m *ContinuousVestingAccount) XXX_Size() int { + return m.Size() +} +func (m *ContinuousVestingAccount) XXX_DiscardUnknown() { + xxx_messageInfo_ContinuousVestingAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_ContinuousVestingAccount proto.InternalMessageInfo + +// 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 `protobuf:"bytes,1,opt,name=base_vesting_account,json=baseVestingAccount,proto3,embedded=base_vesting_account" json:"base_vesting_account,omitempty"` +} + +func (m *DelayedVestingAccount) Reset() { *m = DelayedVestingAccount{} } +func (*DelayedVestingAccount) ProtoMessage() {} +func (*DelayedVestingAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_89e80273ca606d6e, []int{2} +} +func (m *DelayedVestingAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelayedVestingAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelayedVestingAccount.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 *DelayedVestingAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelayedVestingAccount.Merge(m, src) +} +func (m *DelayedVestingAccount) XXX_Size() int { + return m.Size() +} +func (m *DelayedVestingAccount) XXX_DiscardUnknown() { + xxx_messageInfo_DelayedVestingAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_DelayedVestingAccount proto.InternalMessageInfo + +// Period defines a length of time and amount of coins that will vest. +type Period struct { + Length int64 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *Period) Reset() { *m = Period{} } +func (*Period) ProtoMessage() {} +func (*Period) Descriptor() ([]byte, []int) { + return fileDescriptor_89e80273ca606d6e, []int{3} +} +func (m *Period) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Period) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Period.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 *Period) XXX_Merge(src proto.Message) { + xxx_messageInfo_Period.Merge(m, src) +} +func (m *Period) XXX_Size() int { + return m.Size() +} +func (m *Period) XXX_DiscardUnknown() { + xxx_messageInfo_Period.DiscardUnknown(m) +} + +var xxx_messageInfo_Period proto.InternalMessageInfo + +func (m *Period) GetLength() int64 { + if m != nil { + return m.Length + } + return 0 +} + +func (m *Period) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// PeriodicVestingAccount implements the VestingAccount interface. It +// periodically vests by unlocking coins during each specified period. +type PeriodicVestingAccount struct { + *BaseVestingAccount `protobuf:"bytes,1,opt,name=base_vesting_account,json=baseVestingAccount,proto3,embedded=base_vesting_account" json:"base_vesting_account,omitempty"` + StartTime int64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty" yaml:"start_time"` + VestingPeriods []Period `protobuf:"bytes,3,rep,name=vesting_periods,json=vestingPeriods,proto3" json:"vesting_periods" yaml:"vesting_periods"` +} + +func (m *PeriodicVestingAccount) Reset() { *m = PeriodicVestingAccount{} } +func (*PeriodicVestingAccount) ProtoMessage() {} +func (*PeriodicVestingAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_89e80273ca606d6e, []int{4} +} +func (m *PeriodicVestingAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PeriodicVestingAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PeriodicVestingAccount.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 *PeriodicVestingAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_PeriodicVestingAccount.Merge(m, src) +} +func (m *PeriodicVestingAccount) XXX_Size() int { + return m.Size() +} +func (m *PeriodicVestingAccount) XXX_DiscardUnknown() { + xxx_messageInfo_PeriodicVestingAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_PeriodicVestingAccount proto.InternalMessageInfo + +func init() { + proto.RegisterType((*BaseVestingAccount)(nil), "cosmos.vesting.v1beta1.BaseVestingAccount") + proto.RegisterType((*ContinuousVestingAccount)(nil), "cosmos.vesting.v1beta1.ContinuousVestingAccount") + proto.RegisterType((*DelayedVestingAccount)(nil), "cosmos.vesting.v1beta1.DelayedVestingAccount") + proto.RegisterType((*Period)(nil), "cosmos.vesting.v1beta1.Period") + proto.RegisterType((*PeriodicVestingAccount)(nil), "cosmos.vesting.v1beta1.PeriodicVestingAccount") +} + +func init() { + proto.RegisterFile("cosmos/vesting/v1beta1/vesting.proto", fileDescriptor_89e80273ca606d6e) +} + +var fileDescriptor_89e80273ca606d6e = []byte{ + // 593 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x7d, 0x49, 0x08, 0xe5, 0x02, 0x4d, 0x6b, 0x9a, 0x60, 0x3a, 0xd8, 0x91, 0xc5, 0x10, + 0x21, 0xe1, 0x90, 0xc2, 0x94, 0x0d, 0x17, 0x21, 0x55, 0x65, 0x40, 0x16, 0x62, 0x60, 0x89, 0xfc, + 0xe3, 0x70, 0x4e, 0xc4, 0xbe, 0xc8, 0x77, 0xa9, 0xc8, 0x1f, 0x80, 0x84, 0xd4, 0x05, 0x24, 0x06, + 0xc6, 0x2e, 0x2c, 0xfc, 0x11, 0xcc, 0x1d, 0x23, 0x26, 0xa6, 0x80, 0x92, 0xff, 0x20, 0x7f, 0x01, + 0xf2, 0xdd, 0xd9, 0x01, 0x17, 0x88, 0xca, 0x80, 0xd4, 0x29, 0x79, 0xf7, 0xde, 0xfb, 0xde, 0xe7, + 0xbd, 0x7b, 0x77, 0x86, 0xb7, 0x7c, 0x42, 0x23, 0x42, 0x3b, 0x47, 0x88, 0x32, 0x1c, 0x87, 0x9d, + 0xa3, 0xae, 0x87, 0x98, 0xdb, 0xcd, 0x6c, 0x6b, 0x94, 0x10, 0x46, 0xd4, 0xa6, 0x88, 0xb2, 0xb2, + 0x55, 0x19, 0xb5, 0xbb, 0x13, 0x92, 0x90, 0xf0, 0x90, 0x4e, 0xfa, 0x4f, 0x44, 0xef, 0xea, 0x52, + 0xd3, 0x73, 0x29, 0xca, 0x05, 0x7d, 0x82, 0xe3, 0x82, 0xdf, 0x1d, 0xb3, 0x41, 0xee, 0x4f, 0x0d, + 0xe1, 0x37, 0xbf, 0x54, 0xa0, 0x6a, 0xbb, 0x14, 0x3d, 0x13, 0xbb, 0x3d, 0xf0, 0x7d, 0x32, 0x8e, + 0x99, 0x7a, 0x00, 0xaf, 0xa6, 0x8a, 0x7d, 0x57, 0xd8, 0x1a, 0x68, 0x81, 0x76, 0x6d, 0xaf, 0x65, + 0x49, 0x36, 0x2e, 0x20, 0xd5, 0xac, 0x34, 0x5d, 0xe6, 0xd9, 0x95, 0xe9, 0xcc, 0x00, 0x4e, 0xcd, + 0x5b, 0x2d, 0xa9, 0xef, 0x00, 0xdc, 0x22, 0x09, 0x0e, 0x71, 0xec, 0x0e, 0xfb, 0xb2, 0x28, 0xad, + 0xd4, 0x2a, 0xb7, 0x6b, 0x7b, 0x37, 0x33, 0xbd, 0x34, 0x3e, 0xd7, 0xdb, 0x27, 0x38, 0xb6, 0x0f, + 0x4f, 0x67, 0x86, 0xb2, 0x9c, 0x19, 0x37, 0x26, 0x6e, 0x34, 0xec, 0x99, 0x45, 0x01, 0xf3, 0xd3, + 0x37, 0xa3, 0x1d, 0x62, 0x36, 0x18, 0x7b, 0x96, 0x4f, 0xa2, 0x8e, 0xac, 0x52, 0xfc, 0xdc, 0xa1, + 0xc1, 0xcb, 0x0e, 0x9b, 0x8c, 0x10, 0xe5, 0x5a, 0xd4, 0xa9, 0x67, 0xe9, 0xb2, 0x4a, 0xf5, 0x18, + 0xc0, 0xcd, 0x00, 0x0d, 0x51, 0xe8, 0x32, 0x14, 0xf4, 0x5f, 0x24, 0x08, 0x69, 0xe5, 0x75, 0x44, + 0x07, 0x92, 0xa8, 0x21, 0x88, 0x7e, 0x4d, 0x3f, 0x1f, 0xcf, 0xb5, 0x3c, 0xf9, 0x51, 0x82, 0x90, + 0xfa, 0x1e, 0xc0, 0xed, 0x95, 0x5c, 0xd6, 0xa2, 0xca, 0x3a, 0xa0, 0xc7, 0x12, 0x48, 0x2b, 0x02, + 0xfd, 0x53, 0x8f, 0xb6, 0xf2, 0xfc, 0xac, 0x49, 0x16, 0xdc, 0x40, 0x71, 0xd0, 0x67, 0x38, 0x42, + 0xda, 0xa5, 0x16, 0x68, 0x97, 0xed, 0xeb, 0xcb, 0x99, 0x51, 0x17, 0xbb, 0x65, 0x1e, 0xd3, 0xb9, + 0x8c, 0xe2, 0xe0, 0x29, 0x8e, 0x50, 0x6f, 0xe3, 0xcd, 0x89, 0xa1, 0x7c, 0x38, 0x31, 0x14, 0xf3, + 0x33, 0x80, 0xda, 0x3e, 0x89, 0x19, 0x8e, 0xc7, 0x64, 0x4c, 0x0b, 0xa3, 0xe5, 0xc1, 0x1d, 0x3e, + 0x5a, 0x92, 0xb2, 0x30, 0x62, 0xb7, 0xad, 0xdf, 0x8f, 0xbf, 0x75, 0x76, 0x48, 0xe5, 0xb0, 0xa9, + 0xde, 0xd9, 0xf1, 0xbd, 0x0f, 0x21, 0x65, 0x6e, 0xc2, 0x04, 0x7c, 0x89, 0xc3, 0x37, 0x96, 0x33, + 0x63, 0x5b, 0xc0, 0xaf, 0x7c, 0xa6, 0x73, 0x85, 0x1b, 0x85, 0x02, 0x5e, 0x03, 0xd8, 0x78, 0x88, + 0x86, 0xee, 0x24, 0xef, 0xc6, 0x7f, 0xa4, 0xff, 0x89, 0xe3, 0x18, 0xc0, 0xea, 0x13, 0x94, 0x60, + 0x12, 0xa8, 0x4d, 0x58, 0x1d, 0xa2, 0x38, 0x64, 0x03, 0xbe, 0x55, 0xd9, 0x91, 0x96, 0xea, 0xc3, + 0xaa, 0x1b, 0x71, 0x84, 0xb5, 0x77, 0xea, 0x6e, 0x3a, 0x30, 0xe7, 0x1a, 0x0a, 0x29, 0xdd, 0xab, + 0x70, 0x9a, 0x8f, 0x25, 0xd8, 0x14, 0x34, 0xd8, 0xbf, 0x28, 0x87, 0xaa, 0x86, 0xb0, 0x9e, 0x41, + 0x8d, 0x38, 0x3b, 0x95, 0x57, 0x5d, 0xff, 0x13, 0x94, 0x28, 0xd1, 0xd6, 0xe5, 0xf5, 0x6a, 0x0a, + 0xf9, 0x82, 0x88, 0xe9, 0x6c, 0xca, 0x15, 0x11, 0x4e, 0x57, 0xa7, 0x66, 0x1f, 0x9e, 0xce, 0x75, + 0x30, 0x9d, 0xeb, 0xe0, 0xfb, 0x5c, 0x07, 0x6f, 0x17, 0xba, 0x32, 0x5d, 0xe8, 0xca, 0xd7, 0x85, + 0xae, 0x3c, 0xef, 0xfe, 0xb5, 0xf3, 0xaf, 0xe4, 0x2b, 0x2d, 0x3f, 0x0f, 0xfc, 0x20, 0xbc, 0x2a, + 0x7f, 0xa7, 0xef, 0xfd, 0x08, 0x00, 0x00, 0xff, 0xff, 0x97, 0xc5, 0xe2, 0xb4, 0x3d, 0x06, 0x00, + 0x00, +} + +func (m *BaseVestingAccount) 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 *BaseVestingAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BaseVestingAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EndTime != 0 { + i = encodeVarintVesting(dAtA, i, uint64(m.EndTime)) + i-- + dAtA[i] = 0x28 + } + if len(m.DelegatedVesting) > 0 { + for iNdEx := len(m.DelegatedVesting) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatedVesting[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.DelegatedFree) > 0 { + for iNdEx := len(m.DelegatedFree) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatedFree[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.OriginalVesting) > 0 { + for iNdEx := len(m.OriginalVesting) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.OriginalVesting[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.BaseAccount != nil { + { + size, err := m.BaseAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ContinuousVestingAccount) 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 *ContinuousVestingAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContinuousVestingAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.StartTime != 0 { + i = encodeVarintVesting(dAtA, i, uint64(m.StartTime)) + i-- + dAtA[i] = 0x10 + } + if m.BaseVestingAccount != nil { + { + size, err := m.BaseVestingAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DelayedVestingAccount) 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 *DelayedVestingAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelayedVestingAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BaseVestingAccount != nil { + { + size, err := m.BaseVestingAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Period) 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 *Period) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Period) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Length != 0 { + i = encodeVarintVesting(dAtA, i, uint64(m.Length)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *PeriodicVestingAccount) 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 *PeriodicVestingAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PeriodicVestingAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VestingPeriods) > 0 { + for iNdEx := len(m.VestingPeriods) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.VestingPeriods[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.StartTime != 0 { + i = encodeVarintVesting(dAtA, i, uint64(m.StartTime)) + i-- + dAtA[i] = 0x10 + } + if m.BaseVestingAccount != nil { + { + size, err := m.BaseVestingAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintVesting(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintVesting(dAtA []byte, offset int, v uint64) int { + offset -= sovVesting(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *BaseVestingAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseAccount != nil { + l = m.BaseAccount.Size() + n += 1 + l + sovVesting(uint64(l)) + } + if len(m.OriginalVesting) > 0 { + for _, e := range m.OriginalVesting { + l = e.Size() + n += 1 + l + sovVesting(uint64(l)) + } + } + if len(m.DelegatedFree) > 0 { + for _, e := range m.DelegatedFree { + l = e.Size() + n += 1 + l + sovVesting(uint64(l)) + } + } + if len(m.DelegatedVesting) > 0 { + for _, e := range m.DelegatedVesting { + l = e.Size() + n += 1 + l + sovVesting(uint64(l)) + } + } + if m.EndTime != 0 { + n += 1 + sovVesting(uint64(m.EndTime)) + } + return n +} + +func (m *ContinuousVestingAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseVestingAccount != nil { + l = m.BaseVestingAccount.Size() + n += 1 + l + sovVesting(uint64(l)) + } + if m.StartTime != 0 { + n += 1 + sovVesting(uint64(m.StartTime)) + } + return n +} + +func (m *DelayedVestingAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseVestingAccount != nil { + l = m.BaseVestingAccount.Size() + n += 1 + l + sovVesting(uint64(l)) + } + return n +} + +func (m *Period) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Length != 0 { + n += 1 + sovVesting(uint64(m.Length)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovVesting(uint64(l)) + } + } + return n +} + +func (m *PeriodicVestingAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseVestingAccount != nil { + l = m.BaseVestingAccount.Size() + n += 1 + l + sovVesting(uint64(l)) + } + if m.StartTime != 0 { + n += 1 + sovVesting(uint64(m.StartTime)) + } + if len(m.VestingPeriods) > 0 { + for _, e := range m.VestingPeriods { + l = e.Size() + n += 1 + l + sovVesting(uint64(l)) + } + } + return n +} + +func sovVesting(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozVesting(x uint64) (n int) { + return sovVesting(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *BaseVestingAccount) 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 ErrIntOverflowVesting + } + 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: BaseVestingAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BaseVestingAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseAccount == nil { + m.BaseAccount = &types.BaseAccount{} + } + if err := m.BaseAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OriginalVesting", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OriginalVesting = append(m.OriginalVesting, types1.Coin{}) + if err := m.OriginalVesting[len(m.OriginalVesting)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatedFree", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatedFree = append(m.DelegatedFree, types1.Coin{}) + if err := m.DelegatedFree[len(m.DelegatedFree)-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 DelegatedVesting", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatedVesting = append(m.DelegatedVesting, types1.Coin{}) + if err := m.DelegatedVesting[len(m.DelegatedVesting)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EndTime", wireType) + } + m.EndTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EndTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipVesting(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVesting + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContinuousVestingAccount) 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 ErrIntOverflowVesting + } + 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: ContinuousVestingAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContinuousVestingAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseVestingAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseVestingAccount == nil { + m.BaseVestingAccount = &BaseVestingAccount{} + } + if err := m.BaseVestingAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + m.StartTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipVesting(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVesting + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DelayedVestingAccount) 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 ErrIntOverflowVesting + } + 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: DelayedVestingAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelayedVestingAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseVestingAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseVestingAccount == nil { + m.BaseVestingAccount = &BaseVestingAccount{} + } + if err := m.BaseVestingAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipVesting(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVesting + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Period) 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 ErrIntOverflowVesting + } + 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: Period: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Period: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Length", wireType) + } + m.Length = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Length |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types1.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipVesting(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVesting + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PeriodicVestingAccount) 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 ErrIntOverflowVesting + } + 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: PeriodicVestingAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PeriodicVestingAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseVestingAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseVestingAccount == nil { + m.BaseVestingAccount = &BaseVestingAccount{} + } + if err := m.BaseVestingAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + m.StartTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VestingPeriods", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVesting + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthVesting + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthVesting + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VestingPeriods = append(m.VestingPeriods, Period{}) + if err := m.VestingPeriods[len(m.VestingPeriods)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipVesting(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVesting + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipVesting(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, ErrIntOverflowVesting + } + 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, ErrIntOverflowVesting + } + 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, ErrIntOverflowVesting + } + 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, ErrInvalidLengthVesting + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupVesting + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthVesting + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthVesting = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowVesting = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupVesting = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/auth/vesting/types/vesting_account.go b/x/auth/vesting/types/vesting_account.go index 256c9d598fec..e011bd6efbbf 100644 --- a/x/auth/vesting/types/vesting_account.go +++ b/x/auth/vesting/types/vesting_account.go @@ -1,96 +1,72 @@ package types import ( - "encoding/json" "errors" "time" + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" - - "github.com/tendermint/tendermint/crypto" - "gopkg.in/yaml.v2" ) // Compile-time type assertions var ( - _ authexported.Account = (*BaseVestingAccount)(nil) + _ authtypes.AccountI = (*BaseVestingAccount)(nil) _ vestexported.VestingAccount = (*ContinuousVestingAccount)(nil) _ vestexported.VestingAccount = (*PeriodicVestingAccount)(nil) _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) ) -// Register the vesting account types on the auth module codec -func init() { - authtypes.RegisterAccountTypeCodec(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount") - authtypes.RegisterAccountTypeCodec(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount") - authtypes.RegisterAccountTypeCodec(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount") - authtypes.RegisterAccountTypeCodec(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount") -} - -// BaseVestingAccount implements the VestingAccount interface. It contains all -// the necessary fields needed for any vesting account implementation. -type BaseVestingAccount struct { - *authtypes.BaseAccount - - OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // coins in account upon initialization - DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // coins that are vested and delegated - DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // coins that vesting and delegated - EndTime int64 `json:"end_time" yaml:"end_time"` // when the coins become unlocked -} +//----------------------------------------------------------------------------- +// Base Vesting Account -// NewBaseVestingAccount creates a new BaseVestingAccount object -func NewBaseVestingAccount(baseAccount *authtypes.BaseAccount, originalVesting sdk.Coins, endTime int64) (*BaseVestingAccount, error) { - if (baseAccount.Coins.IsZero() && !originalVesting.IsZero()) || originalVesting.IsAnyGT(baseAccount.Coins) { - return &BaseVestingAccount{}, errors.New("vesting amount cannot be greater than total amount") - } +// NewBaseVestingAccount creates a new BaseVestingAccount object. It is the +// callers responsibility to ensure the base account has sufficient funds with +// regards to the original vesting amount. +func NewBaseVestingAccount(baseAccount *authtypes.BaseAccount, originalVesting sdk.Coins, endTime int64) *BaseVestingAccount { return &BaseVestingAccount{ BaseAccount: baseAccount, OriginalVesting: originalVesting, DelegatedFree: sdk.NewCoins(), DelegatedVesting: sdk.NewCoins(), EndTime: endTime, - }, nil + } } -// SpendableCoinsVestingAccount returns all the spendable coins for a vesting account given a -// set of vesting coins. +// LockedCoinsFromVesting returns all the coins that are not spendable (i.e. locked) +// for a vesting account given the current vesting coins. If no coins are locked, +// an empty slice of Coins is returned. // -// CONTRACT: The account's coins, delegated vesting coins, vestingCoins must be -// sorted. -func (bva BaseVestingAccount) SpendableCoinsVestingAccount(vestingCoins sdk.Coins) sdk.Coins { - var spendableCoins sdk.Coins - bc := bva.GetCoins() - - for _, coin := range bc { - baseAmt := coin.Amount - vestingAmt := vestingCoins.AmountOf(coin.Denom) - delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom) +// CONTRACT: Delegated vesting coins and vestingCoins must be sorted. +func (bva BaseVestingAccount) LockedCoinsFromVesting(vestingCoins sdk.Coins) sdk.Coins { + lockedCoins := sdk.NewCoins() + + for _, vestingCoin := range vestingCoins { + vestingAmt := vestingCoin.Amount + delVestingAmt := bva.DelegatedVesting.AmountOf(vestingCoin.Denom) - // compute min((BC + DV) - V, BC) per the specification - min := sdk.MinInt(baseAmt.Add(delVestingAmt).Sub(vestingAmt), baseAmt) - spendableCoin := sdk.NewCoin(coin.Denom, min) + max := sdk.MaxInt(vestingAmt.Sub(delVestingAmt), sdk.ZeroInt()) + lockedCoin := sdk.NewCoin(vestingCoin.Denom, max) - if !spendableCoin.IsZero() { - spendableCoins = spendableCoins.Add(spendableCoin) + if !lockedCoin.IsZero() { + lockedCoins = lockedCoins.Add(lockedCoin) } } - return spendableCoins + return lockedCoins } // TrackDelegation tracks a delegation amount for any given vesting account type -// given the amount of coins currently vesting. +// given the amount of coins currently vesting and the current account balance +// of the delegation denominations. // // CONTRACT: The account's coins, delegation coins, vesting coins, and delegated // vesting coins must be sorted. -func (bva *BaseVestingAccount) TrackDelegation(vestingCoins, amount sdk.Coins) { - bc := bva.GetCoins() - +func (bva *BaseVestingAccount) TrackDelegation(balance, vestingCoins, amount sdk.Coins) { for _, coin := range amount { - baseAmt := bc.AmountOf(coin.Denom) + baseAmt := balance.AmountOf(coin.Denom) vestingAmt := vestingCoins.AmountOf(coin.Denom) delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom) @@ -185,9 +161,8 @@ func (bva BaseVestingAccount) Validate() error { return bva.BaseAccount.Validate() } -type vestingAccountPretty struct { +type vestingAccountYAML struct { Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` PubKey string `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` @@ -208,39 +183,13 @@ func (bva BaseVestingAccount) String() string { // MarshalYAML returns the YAML representation of a BaseVestingAccount. func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ - Address: bva.Address, - Coins: bva.Coins, - AccountNumber: bva.AccountNumber, - Sequence: bva.Sequence, - OriginalVesting: bva.OriginalVesting, - DelegatedFree: bva.DelegatedFree, - DelegatedVesting: bva.DelegatedVesting, - EndTime: bva.EndTime, - } - - if bva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - bz, err := yaml.Marshal(alias) + accAddr, err := sdk.AccAddressFromBech32(bva.Address) if err != nil { return nil, err } - return string(bz), err -} - -// MarshalJSON returns the JSON representation of a BaseVestingAccount. -func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: bva.Address, - Coins: bva.Coins, + alias := vestingAccountYAML{ + Address: accAddr, AccountNumber: bva.AccountNumber, Sequence: bva.Sequence, OriginalVesting: bva.OriginalVesting, @@ -249,8 +198,9 @@ func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { EndTime: bva.EndTime, } - if bva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) + pk := bva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -258,50 +208,19 @@ func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { alias.PubKey = pks } - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a BaseVestingAccount. -func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } + bz, err := yaml.Marshal(alias) + if err != nil { + return nil, err } - bva.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) - bva.OriginalVesting = alias.OriginalVesting - bva.DelegatedFree = alias.DelegatedFree - bva.DelegatedVesting = alias.DelegatedVesting - bva.EndTime = alias.EndTime - - return nil + return string(bz), err } //----------------------------------------------------------------------------- // Continuous Vesting Account var _ vestexported.VestingAccount = (*ContinuousVestingAccount)(nil) -var _ authexported.GenesisAccount = (*ContinuousVestingAccount)(nil) - -// ContinuousVestingAccount implements the VestingAccount interface. It -// continuously vests by unlocking coins linearly with respect to time. -type ContinuousVestingAccount struct { - *BaseVestingAccount - - StartTime int64 `json:"start_time" yaml:"start_time"` // when the coins start to vest -} +var _ authtypes.GenesisAccount = (*ContinuousVestingAccount)(nil) // NewContinuousVestingAccountRaw creates a new ContinuousVestingAccount object from BaseVestingAccount func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *ContinuousVestingAccount { @@ -312,10 +231,10 @@ func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *C } // NewContinuousVestingAccount returns a new ContinuousVestingAccount -func NewContinuousVestingAccount(baseAcc *authtypes.BaseAccount, startTime, endTime int64) *ContinuousVestingAccount { +func NewContinuousVestingAccount(baseAcc *authtypes.BaseAccount, originalVesting sdk.Coins, startTime, endTime int64) *ContinuousVestingAccount { baseVestingAcc := &BaseVestingAccount{ BaseAccount: baseAcc, - OriginalVesting: baseAcc.Coins, + OriginalVesting: originalVesting, EndTime: endTime, } @@ -358,17 +277,16 @@ func (cva ContinuousVestingAccount) GetVestingCoins(blockTime time.Time) sdk.Coi return cva.OriginalVesting.Sub(cva.GetVestedCoins(blockTime)) } -// SpendableCoins returns the total number of spendable coins per denom for a -// continuous vesting account. -func (cva ContinuousVestingAccount) SpendableCoins(blockTime time.Time) sdk.Coins { - return cva.BaseVestingAccount.SpendableCoinsVestingAccount(cva.GetVestingCoins(blockTime)) +// LockedCoins returns the set of coins that are not spendable (i.e. locked). +func (cva ContinuousVestingAccount) LockedCoins(blockTime time.Time) sdk.Coins { + return cva.BaseVestingAccount.LockedCoinsFromVesting(cva.GetVestingCoins(blockTime)) } // TrackDelegation tracks a desired delegation amount by setting the appropriate // values for the amount of delegated vesting, delegated free, and reducing the // overall amount of base coins. -func (cva *ContinuousVestingAccount) TrackDelegation(blockTime time.Time, amount sdk.Coins) { - cva.BaseVestingAccount.TrackDelegation(cva.GetVestingCoins(blockTime), amount) +func (cva *ContinuousVestingAccount) TrackDelegation(blockTime time.Time, balance, amount sdk.Coins) { + cva.BaseVestingAccount.TrackDelegation(balance, cva.GetVestingCoins(blockTime), amount) } // GetStartTime returns the time when vesting starts for a continuous vesting @@ -393,40 +311,13 @@ func (cva ContinuousVestingAccount) String() string { // MarshalYAML returns the YAML representation of a ContinuousVestingAccount. func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ - Address: cva.Address, - Coins: cva.Coins, - AccountNumber: cva.AccountNumber, - Sequence: cva.Sequence, - OriginalVesting: cva.OriginalVesting, - DelegatedFree: cva.DelegatedFree, - DelegatedVesting: cva.DelegatedVesting, - EndTime: cva.EndTime, - StartTime: cva.StartTime, - } - - if cva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - bz, err := yaml.Marshal(alias) + accAddr, err := sdk.AccAddressFromBech32(cva.Address) if err != nil { return nil, err } - return string(bz), err -} - -// MarshalJSON returns the JSON representation of a ContinuousVestingAccount. -func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: cva.Address, - Coins: cva.Coins, + alias := vestingAccountYAML{ + Address: accAddr, AccountNumber: cva.AccountNumber, Sequence: cva.Sequence, OriginalVesting: cva.OriginalVesting, @@ -436,8 +327,9 @@ func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { StartTime: cva.StartTime, } - if cva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) + pk := cva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -445,53 +337,19 @@ func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { alias.PubKey = pks } - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a ContinuousVestingAccount. -func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - cva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), - OriginalVesting: alias.OriginalVesting, - DelegatedFree: alias.DelegatedFree, - DelegatedVesting: alias.DelegatedVesting, - EndTime: alias.EndTime, + bz, err := yaml.Marshal(alias) + if err != nil { + return nil, err } - cva.StartTime = alias.StartTime - return nil + return string(bz), err } //----------------------------------------------------------------------------- // Periodic Vesting Account var _ vestexported.VestingAccount = (*PeriodicVestingAccount)(nil) -var _ authexported.GenesisAccount = (*PeriodicVestingAccount)(nil) - -// PeriodicVestingAccount implements the VestingAccount interface. It -// periodically vests by unlocking coins during each specified period -type PeriodicVestingAccount struct { - *BaseVestingAccount - StartTime int64 `json:"start_time" yaml:"start_time"` // when the coins start to vest - VestingPeriods Periods `json:"vesting_periods" yaml:"vesting_periods"` // the vesting schedule -} +var _ authtypes.GenesisAccount = (*PeriodicVestingAccount)(nil) // NewPeriodicVestingAccountRaw creates a new PeriodicVestingAccount object from BaseVestingAccount func NewPeriodicVestingAccountRaw(bva *BaseVestingAccount, startTime int64, periods Periods) *PeriodicVestingAccount { @@ -503,14 +361,14 @@ func NewPeriodicVestingAccountRaw(bva *BaseVestingAccount, startTime int64, peri } // NewPeriodicVestingAccount returns a new PeriodicVestingAccount -func NewPeriodicVestingAccount(baseAcc *authtypes.BaseAccount, startTime int64, periods Periods) *PeriodicVestingAccount { +func NewPeriodicVestingAccount(baseAcc *authtypes.BaseAccount, originalVesting sdk.Coins, startTime int64, periods Periods) *PeriodicVestingAccount { endTime := startTime for _, p := range periods { endTime += p.Length } baseVestingAcc := &BaseVestingAccount{ BaseAccount: baseAcc, - OriginalVesting: baseAcc.Coins, + OriginalVesting: originalVesting, EndTime: endTime, } @@ -537,16 +395,20 @@ func (pva PeriodicVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins // track the start time of the next period currentPeriodStartTime := pva.StartTime + // for each period, if the period is over, add those coins as vested and check the next period. for _, period := range pva.VestingPeriods { x := blockTime.Unix() - currentPeriodStartTime if x < period.Length { break } + vestedCoins = vestedCoins.Add(period.Amount...) - // Update the start time of the next period + + // update the start time of the next period currentPeriodStartTime += period.Length } + return vestedCoins } @@ -556,17 +418,16 @@ func (pva PeriodicVestingAccount) GetVestingCoins(blockTime time.Time) sdk.Coins return pva.OriginalVesting.Sub(pva.GetVestedCoins(blockTime)) } -// SpendableCoins returns the total number of spendable coins per denom for a -// periodic vesting account. -func (pva PeriodicVestingAccount) SpendableCoins(blockTime time.Time) sdk.Coins { - return pva.BaseVestingAccount.SpendableCoinsVestingAccount(pva.GetVestingCoins(blockTime)) +// LockedCoins returns the set of coins that are not spendable (i.e. locked). +func (pva PeriodicVestingAccount) LockedCoins(blockTime time.Time) sdk.Coins { + return pva.BaseVestingAccount.LockedCoinsFromVesting(pva.GetVestingCoins(blockTime)) } // TrackDelegation tracks a desired delegation amount by setting the appropriate // values for the amount of delegated vesting, delegated free, and reducing the // overall amount of base coins. -func (pva *PeriodicVestingAccount) TrackDelegation(blockTime time.Time, amount sdk.Coins) { - pva.BaseVestingAccount.TrackDelegation(pva.GetVestingCoins(blockTime), amount) +func (pva *PeriodicVestingAccount) TrackDelegation(blockTime time.Time, balance, amount sdk.Coins) { + pva.BaseVestingAccount.TrackDelegation(balance, pva.GetVestingCoins(blockTime), amount) } // GetStartTime returns the time when vesting starts for a periodic vesting @@ -608,41 +469,13 @@ func (pva PeriodicVestingAccount) String() string { // MarshalYAML returns the YAML representation of a PeriodicVestingAccount. func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ - Address: pva.Address, - Coins: pva.Coins, - AccountNumber: pva.AccountNumber, - Sequence: pva.Sequence, - OriginalVesting: pva.OriginalVesting, - DelegatedFree: pva.DelegatedFree, - DelegatedVesting: pva.DelegatedVesting, - EndTime: pva.EndTime, - StartTime: pva.StartTime, - VestingPeriods: pva.VestingPeriods, - } - - if pva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - bz, err := yaml.Marshal(alias) + accAddr, err := sdk.AccAddressFromBech32(pva.Address) if err != nil { return nil, err } - return string(bz), err -} - -// MarshalJSON returns the JSON representation of a PeriodicVestingAccount. -func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: pva.Address, - Coins: pva.Coins, + alias := vestingAccountYAML{ + Address: accAddr, AccountNumber: pva.AccountNumber, Sequence: pva.Sequence, OriginalVesting: pva.OriginalVesting, @@ -653,8 +486,9 @@ func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { VestingPeriods: pva.VestingPeriods, } - if pva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pva.PubKey) + pk := pva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -662,53 +496,19 @@ func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { alias.PubKey = pks } - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. -func (pva *PeriodicVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - pva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), - OriginalVesting: alias.OriginalVesting, - DelegatedFree: alias.DelegatedFree, - DelegatedVesting: alias.DelegatedVesting, - EndTime: alias.EndTime, + bz, err := yaml.Marshal(alias) + if err != nil { + return nil, err } - pva.StartTime = alias.StartTime - pva.VestingPeriods = alias.VestingPeriods - return nil + return string(bz), err } //----------------------------------------------------------------------------- // Delayed Vesting Account var _ vestexported.VestingAccount = (*DelayedVestingAccount)(nil) -var _ authexported.GenesisAccount = (*DelayedVestingAccount)(nil) - -// 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 -} +var _ authtypes.GenesisAccount = (*DelayedVestingAccount)(nil) // NewDelayedVestingAccountRaw creates a new DelayedVestingAccount object from BaseVestingAccount func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount { @@ -718,10 +518,10 @@ func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount } // NewDelayedVestingAccount returns a DelayedVestingAccount -func NewDelayedVestingAccount(baseAcc *authtypes.BaseAccount, endTime int64) *DelayedVestingAccount { +func NewDelayedVestingAccount(baseAcc *authtypes.BaseAccount, originalVesting sdk.Coins, endTime int64) *DelayedVestingAccount { baseVestingAcc := &BaseVestingAccount{ BaseAccount: baseAcc, - OriginalVesting: baseAcc.Coins, + OriginalVesting: originalVesting, EndTime: endTime, } @@ -744,17 +544,16 @@ func (dva DelayedVestingAccount) GetVestingCoins(blockTime time.Time) sdk.Coins return dva.OriginalVesting.Sub(dva.GetVestedCoins(blockTime)) } -// SpendableCoins returns the total number of spendable coins for a delayed -// vesting account. -func (dva DelayedVestingAccount) SpendableCoins(blockTime time.Time) sdk.Coins { - return dva.BaseVestingAccount.SpendableCoinsVestingAccount(dva.GetVestingCoins(blockTime)) +// LockedCoins returns the set of coins that are not spendable (i.e. locked). +func (dva DelayedVestingAccount) LockedCoins(blockTime time.Time) sdk.Coins { + return dva.BaseVestingAccount.LockedCoinsFromVesting(dva.GetVestingCoins(blockTime)) } // TrackDelegation tracks a desired delegation amount by setting the appropriate // values for the amount of delegated vesting, delegated free, and reducing the // overall amount of base coins. -func (dva *DelayedVestingAccount) TrackDelegation(blockTime time.Time, amount sdk.Coins) { - dva.BaseVestingAccount.TrackDelegation(dva.GetVestingCoins(blockTime), amount) +func (dva *DelayedVestingAccount) TrackDelegation(blockTime time.Time, balance, amount sdk.Coins) { + dva.BaseVestingAccount.TrackDelegation(balance, dva.GetVestingCoins(blockTime), amount) } // GetStartTime returns zero since a delayed vesting account has no start time. @@ -767,57 +566,7 @@ func (dva DelayedVestingAccount) Validate() error { return dva.BaseVestingAccount.Validate() } -// MarshalJSON returns the JSON representation of a DelayedVestingAccount. -func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ - Address: dva.Address, - Coins: dva.Coins, - AccountNumber: dva.AccountNumber, - Sequence: dva.Sequence, - OriginalVesting: dva.OriginalVesting, - DelegatedFree: dva.DelegatedFree, - DelegatedVesting: dva.DelegatedVesting, - EndTime: dva.EndTime, - } - - if dva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, dva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. -func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - dva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), - OriginalVesting: alias.OriginalVesting, - DelegatedFree: alias.DelegatedFree, - DelegatedVesting: alias.DelegatedVesting, - EndTime: alias.EndTime, - } - - return nil +func (dva DelayedVestingAccount) String() string { + out, _ := dva.MarshalYAML() + return out.(string) } diff --git a/x/auth/vesting/types/vesting_account_test.go b/x/auth/vesting/types/vesting_account_test.go index 651416338c06..01a5e2ad9804 100644 --- a/x/auth/vesting/types/vesting_account_test.go +++ b/x/auth/vesting/types/vesting_account_test.go @@ -1,18 +1,17 @@ -package types +package types_test import ( - "encoding/json" - "errors" "testing" "time" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/secp256k1" tmtime "github.com/tendermint/tendermint/types/time" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" ) var ( @@ -24,11 +23,10 @@ func TestGetVestedCoinsContVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) + cva := types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) // require no coins vested in the very beginning of the vesting schedule vestedCoins := cva.GetVestedCoins(now) @@ -51,11 +49,10 @@ func TestGetVestingCoinsContVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) + cva := types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) // require all coins vesting in the beginning of the vesting schedule vestingCoins := cva.GetVestingCoins(now) @@ -74,80 +71,64 @@ func TestSpendableCoinsContVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - // require that there exist no spendable coins in the beginning of the - // vesting schedule - spendableCoins := cva.SpendableCoins(now) - require.Nil(t, spendableCoins) + cva := types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) - // require that all original coins are spendable at the end of the vesting + // require that all original coins are locked at the end of the vesting // schedule - spendableCoins = cva.SpendableCoins(endTime) - require.Equal(t, origCoins, spendableCoins) + lockedCoins := cva.LockedCoins(now) + require.Equal(t, origCoins, lockedCoins) - // require that all vested coins (50%) are spendable - spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins) + // require that there exist no locked coins in the beginning of the + lockedCoins = cva.LockedCoins(endTime) + require.Equal(t, sdk.NewCoins(), lockedCoins) - // receive some coins - recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} - cva.SetCoins(cva.GetCoins().Add(recvAmt...)) + // require that all vested coins (50%) are spendable + lockedCoins = cva.LockedCoins(now.Add(12 * time.Hour)) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, lockedCoins) // require that all vested coins (50%) are spendable plus any received - spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins) - - // spend all spendable coins - cva.SetCoins(cva.GetCoins().Sub(spendableCoins)) - - // require that no more coins are spendable - spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour)) - require.Nil(t, spendableCoins) + lockedCoins = cva.LockedCoins(now.Add(12 * time.Hour)) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, lockedCoins) } func TestTrackDelegationContVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to delegate all vesting coins - cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now, origCoins) + cva := types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + cva.TrackDelegation(now, origCoins, origCoins) require.Equal(t, origCoins, cva.DelegatedVesting) require.Nil(t, cva.DelegatedFree) // require the ability to delegate all vested coins - bacc.SetCoins(origCoins) - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(endTime, origCoins) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + cva.TrackDelegation(endTime, origCoins, origCoins) require.Nil(t, cva.DelegatedVesting) require.Equal(t, origCoins, cva.DelegatedFree) // require the ability to delegate all vesting coins (50%) and all vested coins (50%) - bacc.SetCoins(origCoins) - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + cva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting) require.Nil(t, cva.DelegatedFree) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + cva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedFree) // require no modifications when delegation amount is zero or not enough funds - bacc.SetCoins(origCoins) - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) require.Panics(t, func() { - cva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) + cva.TrackDelegation(endTime, origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) }) require.Nil(t, cva.DelegatedVesting) require.Nil(t, cva.DelegatedFree) @@ -157,30 +138,27 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to undelegate all vesting coins - cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now, origCoins) + cva := types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + cva.TrackDelegation(now, origCoins, origCoins) cva.TrackUndelegation(origCoins) require.Nil(t, cva.DelegatedFree) require.Nil(t, cva.DelegatedVesting) // require the ability to undelegate all vested coins - bacc.SetCoins(origCoins) - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) - cva.TrackDelegation(endTime, origCoins) + cva.TrackDelegation(endTime, origCoins, origCoins) cva.TrackUndelegation(origCoins) require.Nil(t, cva.DelegatedFree) require.Nil(t, cva.DelegatedVesting) // require no modifications when the undelegation amount is zero - bacc.SetCoins(origCoins) - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) require.Panics(t, func() { cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)}) @@ -189,9 +167,9 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) { require.Nil(t, cva.DelegatedVesting) // vest 50% and delegate to two validators - cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + cva = types.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + cva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + cva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) // undelegate from one validator that got slashed 50% cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}) @@ -208,13 +186,12 @@ func TestGetVestedCoinsDelVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require no coins are vested until schedule maturation - dva := NewDelayedVestingAccount(&bacc, endTime.Unix()) + dva := types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) vestedCoins := dva.GetVestedCoins(now) require.Nil(t, vestedCoins) @@ -227,13 +204,12 @@ func TestGetVestingCoinsDelVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require all coins vesting at the beginning of the schedule - dva := NewDelayedVestingAccount(&bacc, endTime.Unix()) + dva := types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) vestingCoins := dva.GetVestingCoins(now) require.Equal(t, origCoins, vestingCoins) @@ -246,80 +222,71 @@ func TestSpendableCoinsDelVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - // require that no coins are spendable in the beginning of the vesting + // require that all coins are locked in the beginning of the vesting // schedule - dva := NewDelayedVestingAccount(&bacc, endTime.Unix()) - spendableCoins := dva.SpendableCoins(now) - require.Nil(t, spendableCoins) + dva := types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + lockedCoins := dva.LockedCoins(now) + require.True(t, lockedCoins.IsEqual(origCoins)) // require that all coins are spendable after the maturation of the vesting // schedule - spendableCoins = dva.SpendableCoins(endTime) - require.Equal(t, origCoins, spendableCoins) + lockedCoins = dva.LockedCoins(endTime) + require.Equal(t, sdk.NewCoins(), lockedCoins) // require that all coins are still vesting after some time - spendableCoins = dva.SpendableCoins(now.Add(12 * time.Hour)) - require.Nil(t, spendableCoins) + lockedCoins = dva.LockedCoins(now.Add(12 * time.Hour)) + require.True(t, lockedCoins.IsEqual(origCoins)) // receive some coins - recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} - dva.SetCoins(dva.GetCoins().Add(recvAmt...)) - // require that only received coins are spendable since the account is still // vesting - spendableCoins = dva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, recvAmt, spendableCoins) - - // spend all spendable coins - dva.SetCoins(dva.GetCoins().Sub(spendableCoins)) - - // require that no more coins are spendable - spendableCoins = dva.SpendableCoins(now.Add(12 * time.Hour)) - require.Nil(t, spendableCoins) + lockedCoins = dva.LockedCoins(now.Add(12 * time.Hour)) + require.True(t, lockedCoins.IsEqual(origCoins)) + + // delegate some locked coins + // require that locked is reduced + delegatedAmount := sdk.NewCoins(sdk.NewInt64Coin(stakeDenom, 50)) + dva.TrackDelegation(now.Add(12*time.Hour), origCoins, delegatedAmount) + lockedCoins = dva.LockedCoins(now.Add(12 * time.Hour)) + require.True(t, lockedCoins.IsEqual(origCoins.Sub(delegatedAmount))) } func TestTrackDelegationDelVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to delegate all vesting coins - bacc.SetCoins(origCoins) - dva := NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(now, origCoins) + dva := types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(now, origCoins, origCoins) require.Equal(t, origCoins, dva.DelegatedVesting) require.Nil(t, dva.DelegatedFree) // require the ability to delegate all vested coins - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(endTime, origCoins) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(endTime, origCoins, origCoins) require.Nil(t, dva.DelegatedVesting) require.Equal(t, origCoins, dva.DelegatedFree) // require the ability to delegate all coins half way through the vesting // schedule - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(now.Add(12*time.Hour), origCoins) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(now.Add(12*time.Hour), origCoins, origCoins) require.Equal(t, origCoins, dva.DelegatedVesting) require.Nil(t, dva.DelegatedFree) // require no modifications when delegation amount is zero or not enough funds - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) require.Panics(t, func() { - dva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) + dva.TrackDelegation(endTime, origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) }) require.Nil(t, dva.DelegatedVesting) require.Nil(t, dva.DelegatedFree) @@ -329,30 +296,26 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to undelegate all vesting coins - bacc.SetCoins(origCoins) - dva := NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(now, origCoins) + dva := types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(now, origCoins, origCoins) dva.TrackUndelegation(origCoins) require.Nil(t, dva.DelegatedFree) require.Nil(t, dva.DelegatedVesting) // require the ability to undelegate all vested coins - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(endTime, origCoins) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(endTime, origCoins, origCoins) dva.TrackUndelegation(origCoins) require.Nil(t, dva.DelegatedFree) require.Nil(t, dva.DelegatedVesting) // require no modifications when the undelegation amount is zero - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) require.Panics(t, func() { dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)}) @@ -361,10 +324,9 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { require.Nil(t, dva.DelegatedVesting) // vest 50% and delegate to two validators - bacc.SetCoins(origCoins) - dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) - dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + dva = types.NewDelayedVestingAccount(bacc, origCoins, endTime.Unix()) + dva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + dva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) // undelegate from one validator that got slashed 50% dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}) @@ -381,17 +343,16 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { func TestGetVestedCoinsPeriodicVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - periods := Periods{ - Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + periods := types.Periods{ + types.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, } - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - pva := NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) // require no coins vested at the beginning of the vesting schedule vestedCoins := pva.GetVestedCoins(now) @@ -427,18 +388,17 @@ func TestGetVestedCoinsPeriodicVestingAcc(t *testing.T) { func TestGetVestingCoinsPeriodicVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - periods := Periods{ - Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + periods := types.Periods{ + types.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, } - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{ sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - pva := NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) // require all coins vesting at the beginning of the vesting schedule vestingCoins := pva.GetVestingCoins(now) @@ -468,108 +428,91 @@ func TestGetVestingCoinsPeriodicVestingAcc(t *testing.T) { func TestSpendableCoinsPeriodicVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - periods := Periods{ - Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + periods := types.Periods{ + types.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, } - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{ sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) - pva := NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) // require that there exist no spendable coins at the beginning of the // vesting schedule - spendableCoins := pva.SpendableCoins(now) - require.Nil(t, spendableCoins) + lockedCoins := pva.LockedCoins(now) + require.Equal(t, origCoins, lockedCoins) // require that all original coins are spendable at the end of the vesting // schedule - spendableCoins = pva.SpendableCoins(endTime) - require.Equal(t, origCoins, spendableCoins) + lockedCoins = pva.LockedCoins(endTime) + require.Equal(t, sdk.NewCoins(), lockedCoins) - // require that all vested coins (50%) are spendable - spendableCoins = pva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins) + // require that all still vesting coins (50%) are locked + lockedCoins = pva.LockedCoins(now.Add(12 * time.Hour)) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, lockedCoins) // receive some coins - recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} - pva.SetCoins(pva.GetCoins().Add(recvAmt...)) - - // require that all vested coins (50%) are spendable plus any received - spendableCoins = pva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins) - - // spend all spendable coins - pva.SetCoins(pva.GetCoins().Sub(spendableCoins)) - - // require that no more coins are spendable - spendableCoins = pva.SpendableCoins(now.Add(12 * time.Hour)) - require.Nil(t, spendableCoins) + // require that all still vesting coins (50% of original) are locked plus any received + lockedCoins = pva.LockedCoins(now.Add(12 * time.Hour)) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, lockedCoins) } func TestTrackDelegationPeriodicVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - periods := Periods{ - Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + periods := types.Periods{ + types.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, } - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to delegate all vesting coins - pva := NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now, origCoins) + pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now, origCoins, origCoins) require.Equal(t, origCoins, pva.DelegatedVesting) require.Nil(t, pva.DelegatedFree) // require the ability to delegate all vested coins - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(endTime, origCoins) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(endTime, origCoins, origCoins) require.Nil(t, pva.DelegatedVesting) require.Equal(t, origCoins, pva.DelegatedFree) // delegate half of vesting coins - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now, periods[0].Amount) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now, origCoins, periods[0].Amount) // require that all delegated coins are delegated vesting require.Equal(t, pva.DelegatedVesting, periods[0].Amount) require.Nil(t, pva.DelegatedFree) // delegate 75% of coins, split between vested and vesting - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now.Add(12*time.Hour), periods[0].Amount.Add(periods[1].Amount...)) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now.Add(12*time.Hour), origCoins, periods[0].Amount.Add(periods[1].Amount...)) // require that the maximum possible amount of vesting coins are chosen for delegation. require.Equal(t, pva.DelegatedFree, periods[1].Amount) require.Equal(t, pva.DelegatedVesting, periods[0].Amount) // require the ability to delegate all vesting coins (50%) and all vested coins (50%) - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, pva.DelegatedVesting) require.Nil(t, pva.DelegatedFree) - pva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + pva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, pva.DelegatedVesting) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, pva.DelegatedFree) // require no modifications when delegation amount is zero or not enough funds - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) require.Panics(t, func() { - pva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) + pva.TrackDelegation(endTime, origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) }) require.Nil(t, pva.DelegatedVesting) require.Nil(t, pva.DelegatedFree) @@ -578,44 +521,40 @@ func TestTrackDelegationPeriodicVestingAcc(t *testing.T) { func TestTrackUndelegationPeriodicVestingAcc(t *testing.T) { now := tmtime.Now() endTime := now.Add(24 * time.Hour) - periods := Periods{ - Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, - Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + periods := types.Periods{ + types.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, + types.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}}, } - _, _, addr := KeyTestPubAddr() + _, _, addr := testdata.KeyTestPubAddr() origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := authtypes.NewBaseAccountWithAddress(addr) - bacc.SetCoins(origCoins) // require the ability to undelegate all vesting coins at the beginning of vesting - pva := NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now, origCoins) + pva := types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now, origCoins, origCoins) pva.TrackUndelegation(origCoins) require.Nil(t, pva.DelegatedFree) require.Nil(t, pva.DelegatedVesting) // require the ability to undelegate all vested coins at the end of vesting - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) - pva.TrackDelegation(endTime, origCoins) + pva.TrackDelegation(endTime, origCoins, origCoins) pva.TrackUndelegation(origCoins) require.Nil(t, pva.DelegatedFree) require.Nil(t, pva.DelegatedVesting) // require the ability to undelegate half of coins - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(endTime, periods[0].Amount) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(endTime, origCoins, periods[0].Amount) pva.TrackUndelegation(periods[0].Amount) require.Nil(t, pva.DelegatedFree) require.Nil(t, pva.DelegatedVesting) // require no modifications when the undelegation amount is zero - bacc.SetCoins(origCoins) - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) require.Panics(t, func() { pva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)}) @@ -624,9 +563,9 @@ func TestTrackUndelegationPeriodicVestingAcc(t *testing.T) { require.Nil(t, pva.DelegatedVesting) // vest 50% and delegate to two validators - pva = NewPeriodicVestingAccount(&bacc, now.Unix(), periods) - pva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) - pva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + pva = types.NewPeriodicVestingAccount(bacc, origCoins, now.Unix(), periods) + pva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + pva.TrackDelegation(now.Add(12*time.Hour), origCoins, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) // undelegate from one validator that got slashed 50% pva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}) @@ -639,180 +578,132 @@ func TestTrackUndelegationPeriodicVestingAcc(t *testing.T) { require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, pva.DelegatedVesting) } -func TestNewBaseVestingAccount(t *testing.T) { - pubkey := secp256k1.GenPrivKey().PubKey() - addr := sdk.AccAddress(pubkey.Address()) - _, err := NewBaseVestingAccount( - authtypes.NewBaseAccount(addr, sdk.NewCoins(), pubkey, 0, 0), - sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}, 100, - ) - require.Equal(t, errors.New("vesting amount cannot be greater than total amount"), err) - - _, err = NewBaseVestingAccount( - authtypes.NewBaseAccount(addr, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)), pubkey, 0, 0), - sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}, 100, - ) - require.Equal(t, errors.New("vesting amount cannot be greater than total amount"), err) - - _, err = NewBaseVestingAccount( - authtypes.NewBaseAccount(addr, sdk.NewCoins(sdk.NewInt64Coin("uatom", 50), sdk.NewInt64Coin("eth", 50)), pubkey, 0, 0), - sdk.NewCoins(sdk.NewInt64Coin("uatom", 100), sdk.NewInt64Coin("eth", 20)), 100, - ) - require.Equal(t, errors.New("vesting amount cannot be greater than total amount"), err) - - _, err = NewBaseVestingAccount( - authtypes.NewBaseAccount(addr, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}, pubkey, 0, 0), - sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}, 100, - ) - require.NoError(t, err) - -} - func TestGenesisAccountValidate(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) - baseAcc := authtypes.NewBaseAccount(addr, nil, pubkey, 0, 0) - baseAccWithCoins := authtypes.NewBaseAccount(addr, nil, pubkey, 0, 0) - baseAccWithCoins.SetCoins(sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}) - baseVestingWithCoins, _ := NewBaseVestingAccount(baseAccWithCoins, baseAccWithCoins.Coins, 100) + baseAcc := authtypes.NewBaseAccount(addr, pubkey, 0, 0) + initialVesting := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)) + baseVestingWithCoins := types.NewBaseVestingAccount(baseAcc, initialVesting, 100) tests := []struct { name string - acc authexported.GenesisAccount - expErr error + acc authtypes.GenesisAccount + expErr bool }{ { "valid base account", baseAcc, - nil, + false, }, { "invalid base valid account", - authtypes.NewBaseAccount(addr, sdk.NewCoins(), secp256k1.GenPrivKey().PubKey(), 0, 0), - errors.New("pubkey and address pair is invalid"), + authtypes.NewBaseAccount(addr, secp256k1.GenPrivKey().PubKey(), 0, 0), + true, }, { "valid base vesting account", baseVestingWithCoins, - nil, + false, }, { "valid continuous vesting account", - NewContinuousVestingAccount(baseAcc, 100, 200), - nil, + types.NewContinuousVestingAccount(baseAcc, initialVesting, 100, 200), + false, }, { "invalid vesting times", - NewContinuousVestingAccount(baseAcc, 1654668078, 1554668078), - errors.New("vesting start-time cannot be before end-time"), + types.NewContinuousVestingAccount(baseAcc, initialVesting, 1654668078, 1554668078), + true, }, { "valid periodic vesting account", - NewPeriodicVestingAccount(baseAccWithCoins, 0, Periods{Period{Length: int64(100), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}}}), - nil, + types.NewPeriodicVestingAccount(baseAcc, initialVesting, 0, types.Periods{types.Period{Length: int64(100), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}}}), + false, }, { "invalid vesting period lengths", - NewPeriodicVestingAccountRaw( + types.NewPeriodicVestingAccountRaw( baseVestingWithCoins, - 0, Periods{Period{Length: int64(50), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}}}), - errors.New("vesting end time does not match length of all vesting periods"), + 0, types.Periods{types.Period{Length: int64(50), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}}}), + true, }, { "invalid vesting period amounts", - NewPeriodicVestingAccountRaw( + types.NewPeriodicVestingAccountRaw( baseVestingWithCoins, - 0, Periods{Period{Length: int64(100), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}}}), - errors.New("original vesting coins does not match the sum of all coins in vesting periods"), + 0, types.Periods{types.Period{Length: int64(100), Amount: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}}}), + true, }, } + for _, tt := range tests { tt := tt + t.Run(tt.name, func(t *testing.T) { - err := tt.acc.Validate() - require.Equal(t, tt.expErr, err) + require.Equal(t, tt.expErr, tt.acc.Validate() != nil) }) } } -func TestBaseVestingAccountJSON(t *testing.T) { - pubkey := secp256k1.GenPrivKey().PubKey() - addr := sdk.AccAddress(pubkey.Address()) - coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) - - acc, err := NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) - require.NoError(t, err) - - bz, err := json.Marshal(acc) - require.NoError(t, err) - - bz1, err := acc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) - - var a BaseVestingAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, acc.String(), a.String()) -} - -func TestContinuousVestingAccountJSON(t *testing.T) { +func TestContinuousVestingAccountMarshal(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + baseAcc := authtypes.NewBaseAccount(addr, pubkey, 10, 50) - baseVesting, err := NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) - acc := NewContinuousVestingAccountRaw(baseVesting, baseVesting.EndTime) - require.NoError(t, err) + baseVesting := types.NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) + acc := types.NewContinuousVestingAccountRaw(baseVesting, baseVesting.EndTime) - bz, err := json.Marshal(acc) - require.NoError(t, err) + bz, err := app.AccountKeeper.MarshalAccount(acc) + require.Nil(t, err) - bz1, err := acc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) + acc2, err := app.AccountKeeper.UnmarshalAccount(bz) + require.Nil(t, err) + require.IsType(t, &types.ContinuousVestingAccount{}, acc2) + require.Equal(t, acc.String(), acc2.String()) - var a ContinuousVestingAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, acc.String(), a.String()) + // error on bad bytes + _, err = app.AccountKeeper.UnmarshalAccount(bz[:len(bz)/2]) + require.NotNil(t, err) } -func TestPeriodicVestingAccountJSON(t *testing.T) { +func TestPeriodicVestingAccountMarshal(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + baseAcc := authtypes.NewBaseAccount(addr, pubkey, 10, 50) - acc := NewPeriodicVestingAccount(baseAcc, time.Now().Unix(), Periods{Period{3600, coins}}) + acc := types.NewPeriodicVestingAccount(baseAcc, coins, time.Now().Unix(), types.Periods{types.Period{3600, coins}}) - bz, err := json.Marshal(acc) - require.NoError(t, err) + bz, err := app.AccountKeeper.MarshalAccount(acc) + require.Nil(t, err) - bz1, err := acc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) + acc2, err := app.AccountKeeper.UnmarshalAccount(bz) + require.Nil(t, err) + require.IsType(t, &types.PeriodicVestingAccount{}, acc2) + require.Equal(t, acc.String(), acc2.String()) - var a PeriodicVestingAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, acc.String(), a.String()) + // error on bad bytes + _, err = app.AccountKeeper.UnmarshalAccount(bz[:len(bz)/2]) + require.NotNil(t, err) } -func TestDelayedVestingAccountJSON(t *testing.T) { +func TestDelayedVestingAccountMarshal(t *testing.T) { pubkey := secp256k1.GenPrivKey().PubKey() addr := sdk.AccAddress(pubkey.Address()) coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := authtypes.NewBaseAccount(addr, coins, pubkey, 10, 50) + baseAcc := authtypes.NewBaseAccount(addr, pubkey, 10, 50) - acc := NewDelayedVestingAccount(baseAcc, time.Now().Unix()) + acc := types.NewDelayedVestingAccount(baseAcc, coins, time.Now().Unix()) - bz, err := json.Marshal(acc) - require.NoError(t, err) + bz, err := app.AccountKeeper.MarshalAccount(acc) + require.Nil(t, err) - bz1, err := acc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) + acc2, err := app.AccountKeeper.UnmarshalAccount(bz) + require.Nil(t, err) + require.IsType(t, &types.DelayedVestingAccount{}, acc2) + require.Equal(t, acc.String(), acc2.String()) - var a DelayedVestingAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, acc.String(), a.String()) + // error on bad bytes + _, err = app.AccountKeeper.UnmarshalAccount(bz[:len(bz)/2]) + require.NotNil(t, err) } diff --git a/x/bank/alias.go b/x/bank/alias.go deleted file mode 100644 index 1ac7cecebd90..000000000000 --- a/x/bank/alias.go +++ /dev/null @@ -1,63 +0,0 @@ -package bank - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" -) - -const ( - QueryBalance = keeper.QueryBalance - ModuleName = types.ModuleName - QuerierRoute = types.QuerierRoute - RouterKey = types.RouterKey - DefaultParamspace = types.DefaultParamspace - DefaultSendEnabled = types.DefaultSendEnabled - - EventTypeTransfer = types.EventTypeTransfer - AttributeKeyRecipient = types.AttributeKeyRecipient - AttributeKeySender = types.AttributeKeySender - AttributeValueCategory = types.AttributeValueCategory -) - -var ( - RegisterInvariants = keeper.RegisterInvariants - NonnegativeBalanceInvariant = keeper.NonnegativeBalanceInvariant - NewBaseKeeper = keeper.NewBaseKeeper - NewBaseSendKeeper = keeper.NewBaseSendKeeper - NewBaseViewKeeper = keeper.NewBaseViewKeeper - NewQuerier = keeper.NewQuerier - RegisterCodec = types.RegisterCodec - ErrNoInputs = types.ErrNoInputs - ErrNoOutputs = types.ErrNoOutputs - ErrInputOutputMismatch = types.ErrInputOutputMismatch - ErrSendDisabled = types.ErrSendDisabled - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - NewMsgSend = types.NewMsgSend - NewMsgMultiSend = types.NewMsgMultiSend - NewInput = types.NewInput - NewOutput = types.NewOutput - ValidateInputsOutputs = types.ValidateInputsOutputs - ParamKeyTable = types.ParamKeyTable - NewQueryBalanceParams = types.NewQueryBalanceParams - ModuleCdc = types.ModuleCdc - ParamStoreKeySendEnabled = types.ParamStoreKeySendEnabled -) - -type ( - Keeper = keeper.Keeper - BaseKeeper = keeper.BaseKeeper - SendKeeper = keeper.SendKeeper - BaseSendKeeper = keeper.BaseSendKeeper - ViewKeeper = keeper.ViewKeeper - BaseViewKeeper = keeper.BaseViewKeeper - GenesisState = types.GenesisState - MsgSend = types.MsgSend - MsgMultiSend = types.MsgMultiSend - Input = types.Input - Output = types.Output - QueryBalanceParams = types.QueryBalanceParams -) diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 7a9894aa8c3b..8048ce9f1d20 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -4,19 +4,14 @@ import ( "testing" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/supply" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) type ( @@ -26,12 +21,13 @@ type ( } appTestCase struct { + desc string expSimPass bool expPass bool msgs []sdk.Msg accNums []uint64 accSeqs []uint64 - privKeys []crypto.PrivKey + privKeys []cryptotypes.PrivKey expectedBalances []expectedBalance } ) @@ -49,20 +45,19 @@ var ( halfCoins = sdk.Coins{sdk.NewInt64Coin("foocoin", 5)} sendMsg1 = types.NewMsgSend(addr1, addr2, coins) - sendMsg2 = types.NewMsgSend(addr1, moduleAccAddr, coins) - multiSendMsg1 = types.MsgMultiSend{ + multiSendMsg1 = &types.MsgMultiSend{ Inputs: []types.Input{types.NewInput(addr1, coins)}, Outputs: []types.Output{types.NewOutput(addr2, coins)}, } - multiSendMsg2 = types.MsgMultiSend{ + multiSendMsg2 = &types.MsgMultiSend{ Inputs: []types.Input{types.NewInput(addr1, coins)}, Outputs: []types.Output{ types.NewOutput(addr2, halfCoins), types.NewOutput(addr3, halfCoins), }, } - multiSendMsg3 = types.MsgMultiSend{ + multiSendMsg3 = &types.MsgMultiSend{ Inputs: []types.Input{ types.NewInput(addr1, coins), types.NewInput(addr4, coins), @@ -72,7 +67,7 @@ var ( types.NewOutput(addr3, coins), }, } - multiSendMsg4 = types.MsgMultiSend{ + multiSendMsg4 = &types.MsgMultiSend{ Inputs: []types.Input{ types.NewInput(addr2, coins), }, @@ -80,7 +75,7 @@ var ( types.NewOutput(addr1, coins), }, } - multiSendMsg5 = types.MsgMultiSend{ + multiSendMsg5 = &types.MsgMultiSend{ Inputs: []types.Input{ types.NewInput(addr1, coins), }, @@ -91,151 +86,102 @@ var ( ) func TestSendNotEnoughBalance(t *testing.T) { - acc := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}, + acc := &authtypes.BaseAccount{ + Address: addr1.String(), } - genAccs := []authexported.GenesisAccount{acc} + genAccs := []authtypes.GenesisAccount{acc} 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) - ctxCheck := app.BaseApp.NewContext(true, abci.Header{}) + app.Commit() - res1 := app.AccountKeeper.GetAccount(ctxCheck, addr1) + res1 := app.AccountKeeper.GetAccount(ctx, addr1) require.NotNil(t, res1) - require.Equal(t, acc, res1.(*auth.BaseAccount)) + require.Equal(t, acc, res1.(*authtypes.BaseAccount)) origAccNum := res1.GetAccountNumber() origSeq := res1.GetSequence() sendMsg := types.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 100)}) - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, []sdk.Msg{sendMsg}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1) + 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) + require.Error(t, err) simapp.CheckBalance(t, app, addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}) - res2 := app.AccountKeeper.GetAccount(app.NewContext(true, abci.Header{}), addr1) + res2 := app.AccountKeeper.GetAccount(app.NewContext(true, tmproto.Header{}), addr1) require.NotNil(t, res2) require.Equal(t, res2.GetAccountNumber(), origAccNum) require.Equal(t, res2.GetSequence(), origSeq+1) } -// A module account cannot be the recipient of bank sends unless it has been marked as such -func TestSendToModuleAcc(t *testing.T) { - tests := []struct { - name string - fromBalance sdk.Coins - msg types.MsgSend - expSimPass bool - expPass bool - expFromBalance sdk.Coins - expToBalance sdk.Coins - }{ - { - name: "Normal module account cannot be the recipient of bank sends", - fromBalance: coins, - msg: types.NewMsgSend(addr1, moduleAccAddr, coins), - expSimPass: false, - expPass: false, - expFromBalance: coins, - expToBalance: sdk.NewCoins(), - }, - { - name: "Allowed module account can be the recipient of bank sends", - fromBalance: coins, - msg: types.NewMsgSend(addr1, supply.NewModuleAddress(distribution.ModuleName), coins), - expPass: true, - expSimPass: true, - expFromBalance: sdk.NewCoins(), - expToBalance: coins, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - acc := &auth.BaseAccount{ - Address: test.msg.FromAddress, - Coins: test.fromBalance, - } - - genAccs := []authexported.GenesisAccount{acc} - app := simapp.SetupWithGenesisAccounts(genAccs) - - ctxCheck := app.BaseApp.NewContext(true, abci.Header{}) - - res1 := app.AccountKeeper.GetAccount(ctxCheck, test.msg.FromAddress) - require.NotNil(t, res1) - require.Equal(t, acc, res1.(*auth.BaseAccount)) - - origAccNum := res1.GetAccountNumber() - origSeq := res1.GetSequence() - - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, []sdk.Msg{test.msg}, []uint64{origAccNum}, []uint64{origSeq}, test.expSimPass, test.expPass, priv1) - - simapp.CheckBalance(t, app, test.msg.FromAddress, test.expFromBalance) - simapp.CheckBalance(t, app, test.msg.ToAddress, test.expToBalance) - - res2 := app.AccountKeeper.GetAccount(app.NewContext(true, abci.Header{}), addr1) - require.NotNil(t, res2) - - require.Equal(t, res2.GetAccountNumber(), origAccNum) - require.Equal(t, res2.GetSequence(), origSeq+1) - }) - } -} - func TestMsgMultiSendWithAccounts(t *testing.T) { - acc := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}, + acc := &authtypes.BaseAccount{ + Address: addr1.String(), } - genAccs := []authexported.GenesisAccount{acc} + genAccs := []authtypes.GenesisAccount{acc} 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) - ctxCheck := app.BaseApp.NewContext(true, abci.Header{}) + app.Commit() - res1 := app.AccountKeeper.GetAccount(ctxCheck, addr1) + res1 := app.AccountKeeper.GetAccount(ctx, addr1) require.NotNil(t, res1) - require.Equal(t, acc, res1.(*auth.BaseAccount)) + require.Equal(t, acc, res1.(*authtypes.BaseAccount)) testCases := []appTestCase{ { + desc: "make a valid tx", msgs: []sdk.Msg{multiSendMsg1}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, expPass: true, - privKeys: []crypto.PrivKey{priv1}, + privKeys: []cryptotypes.PrivKey{priv1}, expectedBalances: []expectedBalance{ {addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 57)}}, {addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}}, }, }, { + desc: "wrong accNum should pass Simulate, but not Deliver", msgs: []sdk.Msg{multiSendMsg1, multiSendMsg2}, - accNums: []uint64{0}, - accSeqs: []uint64{0}, + accNums: []uint64{1}, // wrong account number + accSeqs: []uint64{1}, expSimPass: true, // doesn't check signature expPass: false, - privKeys: []crypto.PrivKey{priv1}, + privKeys: []cryptotypes.PrivKey{priv1}, }, { + desc: "wrong accSeq should not pass Simulate", msgs: []sdk.Msg{multiSendMsg5}, accNums: []uint64{0}, - accSeqs: []uint64{0}, + accSeqs: []uint64{0}, // wrong account sequence expSimPass: false, expPass: false, - privKeys: []crypto.PrivKey{priv1}, + privKeys: []cryptotypes.PrivKey{priv1}, }, } for _, tc := range testCases { - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + if tc.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + } for _, eb := range tc.expectedBalances { simapp.CheckBalance(t, app, eb.addr, eb.coins) @@ -244,18 +190,24 @@ func TestMsgMultiSendWithAccounts(t *testing.T) { } func TestMsgMultiSendMultipleOut(t *testing.T) { - - acc1 := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}, + acc1 := &authtypes.BaseAccount{ + Address: addr1.String(), } - acc2 := &auth.BaseAccount{ - Address: addr2, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}, + acc2 := &authtypes.BaseAccount{ + Address: addr2.String(), } - genAccs := []authexported.GenesisAccount{acc1, acc2} + genAccs := []authtypes.GenesisAccount{acc1, acc2} 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) + + err = app.BankKeeper.SetBalances(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) + require.NoError(t, err) + + app.Commit() testCases := []appTestCase{ { @@ -264,7 +216,7 @@ func TestMsgMultiSendMultipleOut(t *testing.T) { accSeqs: []uint64{0}, expSimPass: true, expPass: true, - privKeys: []crypto.PrivKey{priv1}, + privKeys: []cryptotypes.PrivKey{priv1}, expectedBalances: []expectedBalance{ {addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}}, {addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 47)}}, @@ -274,8 +226,10 @@ func TestMsgMultiSendMultipleOut(t *testing.T) { } for _, tc := range testCases { - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + require.NoError(t, err) for _, eb := range tc.expectedBalances { simapp.CheckBalance(t, app, eb.addr, eb.coins) @@ -284,22 +238,30 @@ func TestMsgMultiSendMultipleOut(t *testing.T) { } func TestMsgMultiSendMultipleInOut(t *testing.T) { - - acc1 := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}, + acc1 := &authtypes.BaseAccount{ + Address: addr1.String(), } - acc2 := &auth.BaseAccount{ - Address: addr2, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}, + acc2 := &authtypes.BaseAccount{ + Address: addr2.String(), } - acc4 := &auth.BaseAccount{ - Address: addr4, - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}, + acc4 := &authtypes.BaseAccount{ + Address: addr4.String(), } - genAccs := []authexported.GenesisAccount{acc1, acc2, acc4} + genAccs := []authtypes.GenesisAccount{acc1, acc2, acc4} 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) + + err = app.BankKeeper.SetBalances(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) + require.NoError(t, err) + + err = app.BankKeeper.SetBalances(ctx, addr4, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) + require.NoError(t, err) + + app.Commit() testCases := []appTestCase{ { @@ -308,7 +270,7 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) { accSeqs: []uint64{0, 0}, expSimPass: true, expPass: true, - privKeys: []crypto.PrivKey{priv1, priv4}, + privKeys: []cryptotypes.PrivKey{priv1, priv4}, expectedBalances: []expectedBalance{ {addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}}, {addr4, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}}, @@ -319,8 +281,10 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) { } for _, tc := range testCases { - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + require.NoError(t, err) for _, eb := range tc.expectedBalances { simapp.CheckBalance(t, app, eb.addr, eb.coins) @@ -329,15 +293,19 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) { } func TestMsgMultiSendDependent(t *testing.T) { - acc1 := auth.NewBaseAccountWithAddress(addr1) - acc2 := auth.NewBaseAccountWithAddress(addr2) - err := acc1.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))) - require.NoError(t, err) - err = acc2.SetAccountNumber(1) + acc1 := authtypes.NewBaseAccountWithAddress(addr1) + acc2 := authtypes.NewBaseAccountWithAddress(addr2) + err := acc2.SetAccountNumber(1) require.NoError(t, err) - genAccs := []authexported.GenesisAccount{&acc1, &acc2} + genAccs := []authtypes.GenesisAccount{acc1, acc2} 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) + + app.Commit() testCases := []appTestCase{ { @@ -346,7 +314,7 @@ func TestMsgMultiSendDependent(t *testing.T) { accSeqs: []uint64{0}, expSimPass: true, expPass: true, - privKeys: []crypto.PrivKey{priv1}, + privKeys: []cryptotypes.PrivKey{priv1}, expectedBalances: []expectedBalance{ {addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}}, {addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}}, @@ -358,7 +326,7 @@ func TestMsgMultiSendDependent(t *testing.T) { accSeqs: []uint64{0}, expSimPass: true, expPass: true, - privKeys: []crypto.PrivKey{priv2}, + privKeys: []cryptotypes.PrivKey{priv2}, expectedBalances: []expectedBalance{ {addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}}, }, @@ -366,8 +334,10 @@ func TestMsgMultiSendDependent(t *testing.T) { } for _, tc := range testCases { - header := abci.Header{Height: app.LastBlockHeight() + 1} - simapp.SignCheckDeliver(t, app.Codec(), app.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + require.NoError(t, err) for _, eb := range tc.expectedBalances { simapp.CheckBalance(t, app, eb.addr, eb.coins) diff --git a/x/bank/atlas/atlas-v0.39.1.md b/x/bank/atlas/atlas-v0.39.1.md new file mode 100644 index 000000000000..affca5b9e2bf --- /dev/null +++ b/x/bank/atlas/atlas-v0.39.1.md @@ -0,0 +1,208 @@ +# x/bank + +The `x/bank` 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/bank" + ) + ``` + +2. Add `AppModuleBasic` to your `ModuleBasics`. + + ```go + var ( + ModuleBasics = module.NewBasicManager( + // ... + bank.AppModuleBasic{}, + } + ) + ``` + +3. Create the module's parameter subspace in your application constructor. + + ```go + func NewApp(...) *App { + // ... + app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace) + } + ``` + +4. Create the keeper. Note, the `x/bank` module depends on the `x/auth` module + and a list of blacklisted account addresses which funds are not allowed to be + sent to. Your application will need to define this method based your needs. + + ```go + func NewApp(...) *App { + // ... + app.BankKeeper = bank.NewBaseKeeper( + app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(), + ) + } + ``` + +5. Add the `x/bank` module to the app's `ModuleManager`. + + ```go + func NewApp(...) *App { + // ... + app.mm = module.NewManager( + // ... + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + // ... + ) + } + ``` + +6. Set the `x/bank` module genesis order. + + ```go + func NewApp(...) *App { + // ... + app.mm.SetOrderInitGenesis(..., bank.ModuleName, ...) + } + ``` + +7. Add the `x/bank` module to the simulation manager (if you have one set). + + ```go + func NewApp(...) *App { + // ... + app.sm = module.NewSimulationManager( + // ... + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + // ... + ) + } + +## Genesis + +The `x/bank` module defines its genesis state as follows: + +```go +type GenesisState struct { + SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` +} +``` + +The `SendEnabled` parameter determines if transfers are enabled or disabled +entirely on the chain. This can be used to start a network without enabling +transfers while ensuring critical network functionality is operating as expected. + +## Messages + +### `MsgSend` + +The `x/bank` module allows for transfer of funds from a source account to a +destination account. + +```go +type MsgSend struct { + FromAddress sdk.AccAddress `json:"from_address" yaml:"from_address"` + ToAddress sdk.AccAddress `json:"to_address" yaml:"to_address"` + Amount sdk.Coins `json:"amount" yaml:"amount"` +} +``` + +### `MsgMultiSend` + +The `x/bank` module also allows for multiple inputs and outputs. The sum of all +inputs must be equivalent to the sum of all outputs. + +```go +type Input struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` +} + +type Output struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` +} + +type MsgMultiSend struct { + Inputs []Input `json:"inputs" yaml:"inputs"` + Outputs []Output `json:"outputs" yaml:"outputs"` +} +``` + +## Client + +### CLI + +The `x/bank` supports the following transactional commands. + +1. Send tokens via a `MsgSend` message. + + ```shell + $ app tx send [from_key_or_address] [to_address] [amount] [...flags] + ``` + +Note, the `x/bank` module does not natively support constructing a `MsgMultiSend` +message. This type of message must be constructed manually, but it may be signed +and broadcasted via the CLI. + +### REST + +The `x/bank` supports various query API endpoints and a `MsgSend` construction +endpoint. + +1. Construct an unsigned `MsgSend` transaction. + + | Method | Path | + | :----- | :----------------------- | + | `POST` | `/bank/accounts/{address}/transfers` | + + Sample payload: + + ```json + { + "base_req": { + "chain_id": "chain-foo", + "from": "cosmos1u3fneykx9carelvurc6av22vpjvptytj9wklk0", + "memo": "memo", + "fees": [ + { + "denom": "stake", + "amount": "25000" + } + ] + }, + "amount": [ + { + "denom": "stake", + "amount": "400000000" + } + ] + } + ``` + +2. Query for an account's balance. + + | Method | Path | + | :----- | :----------------------- | + | `GET` | `/bank/balances/{address}` | + + Sample response: + + ```json + { + "height": "0", + "result": [ + { + "denom": "node0token", + "amount": "1000000000" + }, + { + "denom": "stake", + "amount": "400000000" + } + ] + } + ``` diff --git a/x/bank/atlas/atlas.toml b/x/bank/atlas/atlas.toml new file mode 100644 index 000000000000..9639ffd40000 --- /dev/null +++ b/x/bank/atlas/atlas.toml @@ -0,0 +1,34 @@ +[module] +description = "The bank 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." +homepage = "https://github.com/cosmos/cosmos-sdk" +keywords = [ + "tokens", + "bank", + "transfer", + "asset", + "fungible", + "coins", +] + +name = "x/bank" + +[bug_tracker] +url = "https://github.com/cosmos/cosmos-sdk/issues" + +[[authors]] +name = "alexanderbez" + +[[authors]] +name = "fedekunze" + +[[authors]] +name = "cwgoes" + +[[authors]] +name = "alessio" + +[version] +documentation = "https://raw.githubusercontent.com/cosmos/cosmos-sdk/master/x/bank/atlas/atlas-v0.39.1.md" +repo = "https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.2" +sdk_compat = "v0.39.x" +version = "v0.39.2" diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index 9161a88c31ba..529911943498 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -3,74 +3,101 @@ package bank_test import ( "testing" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/simapp" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/auth/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -var moduleAccAddr = supply.NewModuleAddress(staking.BondedPoolName) +var moduleAccAddr = authtypes.NewModuleAddress(stakingtypes.BondedPoolName) func BenchmarkOneBankSendTxPerBlock(b *testing.B) { // Add an account at genesis - acc := auth.BaseAccount{ - Address: addr1, - // Some value conceivably higher than the benchmarks would ever go - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 100000000000)}, + acc := authtypes.BaseAccount{ + Address: addr1.String(), } - // Construct genesis state - genAccs := []authexported.GenesisAccount{&acc} + // construct genesis state + genAccs := []types.GenesisAccount{&acc} benchmarkApp := simapp.SetupWithGenesisAccounts(genAccs) + 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) + + benchmarkApp.Commit() + txGen := simappparams.MakeTestEncodingConfig().TxConfig // Precompute all txs - txs := simapp.GenSequenceOfTxs([]sdk.Msg{sendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) + txs, err := simapp.GenSequenceOfTxs(txGen, []sdk.Msg{sendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) + require.NoError(b, err) b.ResetTimer() + + height := int64(3) + // Run this with a profiler, so its easy to distinguish what time comes from // Committing, and what time comes from Check/Deliver Tx. for i := 0; i < b.N; i++ { - benchmarkApp.BeginBlock(abci.RequestBeginBlock{}) - _, _, err := benchmarkApp.Check(txs[i]) + benchmarkApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: height}}) + _, _, err := benchmarkApp.Check(txGen.TxEncoder(), txs[i]) if err != nil { panic("something is broken in checking transaction") } - benchmarkApp.Deliver(txs[i]) - benchmarkApp.EndBlock(abci.RequestEndBlock{}) + _, _, err = benchmarkApp.Deliver(txGen.TxEncoder(), txs[i]) + require.NoError(b, err) + benchmarkApp.EndBlock(abci.RequestEndBlock{Height: height}) benchmarkApp.Commit() + height++ } } func BenchmarkOneBankMultiSendTxPerBlock(b *testing.B) { + b.ReportAllocs() // Add an account at genesis - acc := auth.BaseAccount{ - Address: addr1, - // Some value conceivably higher than the benchmarks would ever go - Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 100000000000)}, + acc := authtypes.BaseAccount{ + Address: addr1.String(), } // Construct genesis state - genAccs := []authexported.GenesisAccount{&acc} + genAccs := []authtypes.GenesisAccount{&acc} benchmarkApp := simapp.SetupWithGenesisAccounts(genAccs) + 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) + + benchmarkApp.Commit() + txGen := simappparams.MakeTestEncodingConfig().TxConfig // Precompute all txs - txs := simapp.GenSequenceOfTxs([]sdk.Msg{multiSendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) + txs, err := simapp.GenSequenceOfTxs(txGen, []sdk.Msg{multiSendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) + require.NoError(b, err) b.ResetTimer() + + height := int64(3) + // Run this with a profiler, so its easy to distinguish what time comes from // Committing, and what time comes from Check/Deliver Tx. for i := 0; i < b.N; i++ { - benchmarkApp.BeginBlock(abci.RequestBeginBlock{}) - _, _, err := benchmarkApp.Check(txs[i]) + benchmarkApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: height}}) + _, _, err := benchmarkApp.Check(txGen.TxEncoder(), txs[i]) if err != nil { panic("something is broken in checking transaction") } - benchmarkApp.Deliver(txs[i]) - benchmarkApp.EndBlock(abci.RequestEndBlock{}) + _, _, err = benchmarkApp.Deliver(txGen.TxEncoder(), txs[i]) + require.NoError(b, err) + benchmarkApp.EndBlock(abci.RequestEndBlock{Height: height}) benchmarkApp.Commit() + height++ } } diff --git a/x/bank/client/cli/cli_test.go b/x/bank/client/cli/cli_test.go new file mode 100644 index 000000000000..48d4a536a074 --- /dev/null +++ b/x/bank/client/cli/cli_test.go @@ -0,0 +1,526 @@ +package cli_test + +import ( + "context" + "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" + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGetBalancesCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expected proto.Message + }{ + {"no address provided", []string{}, true, nil, nil}, + { + "total account balance", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &types.QueryAllBalancesResponse{}, + &types.QueryAllBalancesResponse{ + Balances: sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ), + Pagination: &query.PageResponse{}, + }, + }, + { + "total account balance of a specific denom", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=%s", cli.FlagDenom, s.cfg.BondDenom), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &sdk.Coin{}, + NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + }, + { + "total account balance of a bogus denom", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=foobar", cli.FlagDenom), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + &sdk.Coin{}, + NewCoin("foobar", sdk.ZeroInt()), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetBalancesCmd() + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryTotalSupply() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expected proto.Message + }{ + { + name: "total supply", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + respType: &types.QueryTotalSupplyResponse{}, + expected: &types.QueryTotalSupplyResponse{ + Supply: sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + )}, + }, + { + name: "total supply of a specific denomination", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=%s", cli.FlagDenom, s.cfg.BondDenom), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + respType: &sdk.Coin{}, + expected: &sdk.Coin{s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))}, + }, + { + name: "total supply of a bogus denom", + args: []string{ + fmt.Sprintf("--%s=1", flags.FlagHeight), + fmt.Sprintf("--%s=foobar", cli.FlagDenom), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + respType: &sdk.Coin{}, + expected: &sdk.Coin{"foobar", sdk.ZeroInt()}, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryTotalSupply() + 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) 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] + + clientCtx := val.ClientCtx + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + from := val.Address + to := val.Address + amount := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + ) + args := []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()), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + } + + bz, err := banktestutil.MsgSendExec(clientCtx, from, to, amount, args...) + 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()) +} + +func (s *IntegrationTestSuite) TestNewSendTxCmd() { + 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 + }{ + { + "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, + }, + { + "not enough fees", + 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(1))).String()), + }, + false, + &sdk.TxResponse{}, + sdkerrors.ErrInsufficientFee.ABCICode(), + }, + { + "not enough gas", + 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()), + "--gas=10", + }, + false, + &sdk.TxResponse{}, + sdkerrors.ErrOutOfGas.ABCICode(), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + clientCtx := val.ClientCtx + + bz, err := banktestutil.MsgSendExec(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) + } + }) + } +} + +// 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)) +} + +func NewCoin(denom string, amount sdk.Int) *sdk.Coin { + coin := sdk.NewCoin(denom, amount) + return &coin +} diff --git a/x/bank/client/cli/query.go b/x/bank/client/cli/query.go new file mode 100644 index 000000000000..fbb9ba43825d --- /dev/null +++ b/x/bank/client/cli/query.go @@ -0,0 +1,209 @@ +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/bank/types" +) + +const ( + FlagDenom = "denom" +) + +// GetQueryCmd returns the parent command for all x/bank CLi query commands. The +// provided clientCtx should have, at a minimum, a verifier, Tendermint RPC client, +// and marshaler set. +func GetQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the bank module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + GetBalancesCmd(), + GetCmdQueryTotalSupply(), + GetCmdDenomsMetadata(), + ) + + return cmd +} + +func GetBalancesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "balances [address]", + Short: "Query for account balances by address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query the total balance of an account or of a specific denomination. + +Example: + $ %s query %s balances [address] + $ %s query %s balances [address] --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []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) + + addr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + if denom == "" { + params := types.NewQueryAllBalancesRequest(addr, pageReq) + + res, err := queryClient.AllBalances(cmd.Context(), params) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + } + + params := types.NewQueryBalanceRequest(addr, denom) + res, err := queryClient.Balance(cmd.Context(), params) + if err != nil { + return err + } + + return clientCtx.PrintProto(res.Balance) + }, + } + + cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for") + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "all balances") + + 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", + Short: "Query the total supply of coins of the chain", + Long: strings.TrimSpace( + fmt.Sprintf(`Query total supply of coins that are held by accounts in the chain. + +Example: + $ %s query %s total + +To query for the total supply of a specific coin denomination use: + $ %s query %s total --denom=[denom] +`, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, + ), + ), + RunE: func(cmd *cobra.Command, args []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.TotalSupply(cmd.Context(), &types.QueryTotalSupplyRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + } + + res, err := queryClient.SupplyOf(cmd.Context(), &types.QuerySupplyOfRequest{Denom: denom}) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Amount) + }, + } + + cmd.Flags().String(FlagDenom, "", "The specific balance denomination to query for") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 58bce19eba6f..d88a95fc6c09 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -1,22 +1,17 @@ package cli import ( - "bufio" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(cdc *codec.Codec) *cobra.Command { +// NewTxCmd returns a root CLI command handler for all x/bank transaction commands. +func NewTxCmd() *cobra.Command { txCmd := &cobra.Command{ Use: types.ModuleName, Short: "Bank transaction subcommands", @@ -24,41 +19,45 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } - txCmd.AddCommand( - SendTxCmd(cdc), - ) + + txCmd.AddCommand(NewSendTxCmd()) + return txCmd } -// SendTxCmd will create a send tx and sign it with the given key. -func SendTxCmd(cdc *codec.Codec) *cobra.Command { +// NewSendTxCmd returns a CLI command handler for creating a MsgSend transaction. +func NewSendTxCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "send [from_key_or_address] [to_address] [amount]", - Short: "Create and sign a send tx", - Args: cobra.ExactArgs(3), + 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 { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInputAndFrom(inBuf, args[0]).WithCodec(cdc) - - to, err := sdk.AccAddressFromBech32(args[1]) + 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 } - // parse coins trying to be sent - coins, err := sdk.ParseCoins(args[2]) + coins, err := sdk.ParseCoinsNormalized(args[2]) if err != nil { return err } - // build and sign the transaction, then broadcast to Tendermint - msg := types.NewMsgSend(cliCtx.GetFromAddress(), to, coins) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - cmd = flags.PostCommands(cmd)[0] + flags.AddTxFlagsToCmd(cmd) return cmd } diff --git a/x/bank/client/rest/grpc_query_test.go b/x/bank/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..83ee93c263ae --- /dev/null +++ b/x/bank/client/rest/grpc_query_test.go @@ -0,0 +1,262 @@ +// +build norace + +package rest_test + +import ( + "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (s *IntegrationTestSuite) TestTotalSupplyGRPCHandler() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + headers map[string]string + respType proto.Message + expected proto.Message + }{ + { + "test GRPC total supply", + fmt.Sprintf("%s/cosmos/bank/v1beta1/supply", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + &types.QueryTotalSupplyResponse{}, + &types.QueryTotalSupplyResponse{ + Supply: sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + ), + }, + }, + { + "GRPC total supply of a specific denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/%s", baseURL, s.cfg.BondDenom), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + &types.QuerySupplyOfResponse{}, + &types.QuerySupplyOfResponse{ + Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + }, + }, + { + "Query for `height` > 1", + fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/%s", baseURL, s.cfg.BondDenom), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + &types.QuerySupplyOfResponse{}, + &types.QuerySupplyOfResponse{ + Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(4))), + }, + }, + { + "Query params shouldn't be considered as height", + fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/%s?height=2", baseURL, s.cfg.BondDenom), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + &types.QuerySupplyOfResponse{}, + &types.QuerySupplyOfResponse{ + Amount: sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + }, + }, + { + "GRPC total supply of a bogus denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/foobar", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + &types.QuerySupplyOfResponse{}, + &types.QuerySupplyOfResponse{ + Amount: sdk.NewCoin("foobar", sdk.ZeroInt()), + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) + } +} + +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 + + testCases := []struct { + name string + url string + respType proto.Message + expected proto.Message + }{ + { + "gRPC total account balance", + fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", baseURL, val.Address.String()), + &types.QueryAllBalancesResponse{}, + &types.QueryAllBalancesResponse{ + Balances: sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ), + Pagination: &query.PageResponse{ + Total: 2, + }, + }, + }, + { + "gPRC account balance of a denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s/%s", baseURL, val.Address.String(), s.cfg.BondDenom), + &types.QueryBalanceResponse{}, + &types.QueryBalanceResponse{ + Balance: &sdk.Coin{ + Denom: s.cfg.BondDenom, + Amount: s.cfg.StakingTokens.Sub(s.cfg.BondedTokens), + }, + }, + }, + { + "gPRC account balance of a bogus denom", + fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s/foobar", baseURL, val.Address.String()), + &types.QueryBalanceResponse{}, + &types.QueryBalanceResponse{ + Balance: &sdk.Coin{ + Denom: "foobar", + Amount: sdk.NewInt(0), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) + } +} diff --git a/x/bank/client/rest/query.go b/x/bank/client/rest/query.go index 97bb16df3274..7c88c790f83c 100644 --- a/x/bank/client/rest/query.go +++ b/x/bank/client/rest/query.go @@ -1,55 +1,118 @@ package rest import ( + "fmt" "net/http" "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// query accountREST Handler -func QueryBalancesRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +// QueryBalancesRequestHandlerFn returns a REST handler that queries for all +// account balances or a specific balance by denomination. +func QueryBalancesRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") + vars := mux.Vars(r) bech32addr := vars["address"] addr, err := sdk.AccAddressFromBech32(bech32addr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, err) { + return + } + + ctx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) + if !ok { + return + } + + var ( + params interface{} + route string + ) + + denom := r.FormValue("denom") + if denom == "" { + params = types.NewQueryAllBalancesRequest(addr, nil) + route = fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllBalances) + } else { + params = types.NewQueryBalanceRequest(addr, denom) + route = fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryBalance) + } + + bz, err := ctx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { + return + } + + res, height, err := ctx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { + return + } + + ctx = ctx.WithHeight(height) + rest.PostProcessResponse(w, ctx, res) + } +} + +// HTTP request handler to query the total supply of coins +func totalSupplyHandlerFn(clientCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQueryBalanceParams(addr) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + params := types.NewQueryTotalSupplyParams(page, limit) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData("custom/bank/balances", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply), bz) + + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) + } +} + +// HTTP request handler to query the supply of a single denom +func supplyOfHandlerFn(clientCtx client.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + denom := mux.Vars(r)["denom"] + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) + if !ok { + return + } + + params := types.NewQuerySupplyOfParams(denom) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + + if rest.CheckBadRequestError(w, err) { + return + } - // the query will return empty if there is no data for this account - if len(res) == 0 { - rest.PostProcessResponse(w, cliCtx, sdk.Coins{}) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySupplyOf), bz) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/bank/client/rest/query_test.go b/x/bank/client/rest/query_test.go new file mode 100644 index 000000000000..1c949e80c516 --- /dev/null +++ b/x/bank/client/rest/query_test.go @@ -0,0 +1,192 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "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 { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestQueryBalancesRequestHandlerFn() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + expHeight int64 + respType fmt.Stringer + expected fmt.Stringer + }{ + { + "total account balance", + fmt.Sprintf("%s/bank/balances/%s", baseURL, val.Address), + -1, + &sdk.Coins{}, + sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ), + }, + { + "total account balance with height", + fmt.Sprintf("%s/bank/balances/%s?height=1", baseURL, val.Address), + 1, + &sdk.Coins{}, + sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + ), + }, + { + "total account balance of a specific denom", + fmt.Sprintf("%s/bank/balances/%s?denom=%s", baseURL, val.Address, s.cfg.BondDenom), + -1, + &sdk.Coin{}, + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Sub(s.cfg.BondedTokens)), + }, + { + "total account balance of a bogus denom", + fmt.Sprintf("%s/bank/balances/%s?denom=foobar", baseURL, val.Address), + -1, + &sdk.Coin{}, + sdk.NewCoin("foobar", sdk.ZeroInt()), + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + respJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var resp = rest.ResponseWithHeight{} + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &resp) + s.Require().NoError(err) + + // Check height. + if tc.expHeight >= 0 { + s.Require().Equal(resp.Height, tc.expHeight) + } else { + // To avoid flakiness, just test that height is positive. + s.Require().Greater(resp.Height, int64(0)) + } + + // Check result. + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(resp.Result, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) + } +} + +func (s *IntegrationTestSuite) TestTotalSupplyHandlerFn() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType fmt.Stringer + expected fmt.Stringer + }{ + { + "total supply", + fmt.Sprintf("%s/bank/total?height=1", baseURL), + &sdk.Coins{}, + sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), s.cfg.AccountTokens), + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + ), + }, + { + "total supply of a specific denom", + fmt.Sprintf("%s/bank/total/%s?height=1", baseURL, s.cfg.BondDenom), + &sdk.Coin{}, + sdk.NewCoin(s.cfg.BondDenom, s.cfg.StakingTokens.Add(sdk.NewInt(2))), + }, + { + "total supply of a bogus denom", + fmt.Sprintf("%s/bank/total/foobar?height=1", baseURL), + &sdk.Coin{}, + sdk.NewCoin("foobar", sdk.ZeroInt()), + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + bz, err := rest.ParseResponseWithHeight(val.ClientCtx.LegacyAmino, resp) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(bz, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/bank/client/rest/rest.go b/x/bank/client/rest/rest.go new file mode 100644 index 000000000000..db4909c53258 --- /dev/null +++ b/x/bank/client/rest/rest.go @@ -0,0 +1,19 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/rest" + + "github.com/cosmos/cosmos-sdk/client" +) + +// RegisterHandlers registers all x/bank transaction and query HTTP REST handlers +// on the provided mux router. +func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + r.HandleFunc("/bank/accounts/{address}/transfers", NewSendRequestHandlerFn(clientCtx)).Methods("POST") + r.HandleFunc("/bank/balances/{address}", QueryBalancesRequestHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/bank/total", totalSupplyHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/bank/total/{denom}", supplyOfHandlerFn(clientCtx)).Methods("GET") +} diff --git a/x/bank/client/rest/tx.go b/x/bank/client/rest/tx.go index 10ac15abc790..e630710dff22 100644 --- a/x/bank/client/rest/tx.go +++ b/x/bank/client/rest/tx.go @@ -5,40 +5,33 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "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/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cliCtx)).Methods("POST") - r.HandleFunc("/bank/balances/{address}", QueryBalancesRequestHandlerFn(cliCtx)).Methods("GET") -} - // SendReq defines the properties of a send request's body. type SendReq struct { BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` Amount sdk.Coins `json:"amount" yaml:"amount"` } -// SendRequestHandlerFn - http request handler to send coins to a address. -func SendRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +// NewSendRequestHandlerFn returns an HTTP REST handler for creating a MsgSend +// transaction. +func NewSendRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32Addr := vars["address"] toAddr, err := sdk.AccAddressFromBech32(bech32Addr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } var req SendReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -48,12 +41,11 @@ func SendRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } msg := types.NewMsgSend(fromAddr, toAddr, req.Amount) - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/bank/client/rest/tx_test.go b/x/bank/client/rest/tx_test.go new file mode 100644 index 000000000000..d770ebec3da7 --- /dev/null +++ b/x/bank/client/rest/tx_test.go @@ -0,0 +1,111 @@ +// +build norace + +package rest_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/rest" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankrest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (s *IntegrationTestSuite) TestCoinSend() { + encodingConfig := simapp.MakeTestEncodingConfig() + authclient.Codec = encodingConfig.Marshaler + + val := s.network.Validators[0] + + account, err := getAccountInfo(val) + s.Require().NoError(err) + + sendReq := generateSendReq( + account, + types.Coins{types.NewCoin(s.cfg.BondDenom, types.TokensFromConsensusPower(1))}, + ) + + stdTx, err := submitSendReq(val, sendReq) + s.Require().NoError(err) + + s.Require().Nil(stdTx.Signatures) + s.Require().Equal([]types.Msg{ + &banktypes.MsgSend{ + FromAddress: account.GetAddress().String(), + ToAddress: account.GetAddress().String(), + Amount: sendReq.Amount, + }, + }, stdTx.GetMsgs()) +} + +func submitSendReq(val *network.Validator, req bankrest.SendReq) (legacytx.StdTx, error) { + url := fmt.Sprintf("%s/bank/accounts/%s/transfers", val.APIAddress, val.Address) + + // NOTE: this uses amino explicitly, don't migrate it! + bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(req) + if err != nil { + return legacytx.StdTx{}, errors.Wrap(err, "error encoding SendReq to json") + } + + res, err := rest.PostRequest(url, "application/json", bz) + if err != nil { + return legacytx.StdTx{}, err + } + + var tx legacytx.StdTx + // NOTE: this uses amino explicitly, don't migrate it! + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &tx) + if err != nil { + return legacytx.StdTx{}, errors.Wrap(err, "error unmarshaling to StdTx SendReq response") + } + + return tx, nil +} + +func generateSendReq(from authtypes.AccountI, amount types.Coins) bankrest.SendReq { + baseReq := rest.NewBaseReq( + from.GetAddress().String(), + "someMemo", + "some-id", + "10000", + fmt.Sprintf("%f", 1.0), + from.GetAccountNumber(), + from.GetSequence(), + types.NewCoins(), + nil, + false, + ) + + return bankrest.SendReq{ + BaseReq: baseReq, + Amount: amount, + } +} + +func getAccountInfo(val *network.Validator) (authtypes.AccountI, error) { + url := fmt.Sprintf("%s/auth/accounts/%s", val.APIAddress, val.Address) + + resp, err := rest.GetRequest(url) + if err != nil { + return nil, err + } + + bz, err := rest.ParseResponseWithHeight(val.ClientCtx.LegacyAmino, resp) + if err != nil { + return nil, err + } + + var acc authtypes.AccountI + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(bz, &acc) + if err != nil { + return nil, err + } + + return acc, nil +} diff --git a/x/bank/client/testutil/cli_helpers.go b/x/bank/client/testutil/cli_helpers.go new file mode 100644 index 000000000000..820c668c4558 --- /dev/null +++ b/x/bank/client/testutil/cli_helpers.go @@ -0,0 +1,118 @@ +package testutil + +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" + sdk "github.com/cosmos/cosmos-sdk/types" + bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func MsgSendExec(clientCtx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{from.String(), to.String(), amount.String()} + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, bankcli.NewSendTxCmd(), args) +} + +func QueryBalancesExec(clientCtx client.Context, address fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{address.String(), fmt.Sprintf("--%s=json", cli.OutputFlag)} + args = append(args, 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() + if err != nil { + return 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{} + +// 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 + } + + 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...) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// ServiceMsgSendExec is a temporary method to test Msg services in CLI using +// x/bank's Msg/Send service. After https://github.com/cosmos/cosmos-sdk/issues/7541 +// is merged, this method should be removed, and we should prefer MsgSendExec +// instead. +func ServiceMsgSendExec(clientCtx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { + args := []string{from.String(), to.String(), amount.String()} + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, newSendTxMsgServiceCmd(), args) +} diff --git a/x/bank/exported/exported.go b/x/bank/exported/exported.go new file mode 100644 index 000000000000..ae13d99d5a87 --- /dev/null +++ b/x/bank/exported/exported.go @@ -0,0 +1,29 @@ +package exported + +import ( + "github.com/gogo/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisBalance defines a genesis balance interface that allows for account +// address and balance retrieval. +type GenesisBalance interface { + GetAddress() sdk.AccAddress + GetCoins() sdk.Coins +} + +// SupplyI defines an inflationary supply interface for modules that handle +// token supply. +type SupplyI interface { + proto.Message + + GetTotal() sdk.Coins + SetTotal(total sdk.Coins) + + Inflate(amount sdk.Coins) + Deflate(amount sdk.Coins) + + String() string + ValidateBasic() error +} diff --git a/x/bank/genesis.go b/x/bank/genesis.go deleted file mode 100644 index 641875e6dc88..000000000000 --- a/x/bank/genesis.go +++ /dev/null @@ -1,15 +0,0 @@ -package bank - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// InitGenesis sets distribution information for genesis. -func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { - keeper.SetSendEnabled(ctx, data.SendEnabled) -} - -// ExportGenesis returns a GenesisState for a given context and keeper. -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { - return NewGenesisState(keeper.GetSendEnabled(ctx)) -} diff --git a/x/bank/handler.go b/x/bank/handler.go index 3c83ec4c2af4..0fb0f53a4cd8 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -3,77 +3,28 @@ package bank import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) // NewHandler returns a handler for "bank" type messages. func NewHandler(k keeper.Keeper) sdk.Handler { + msgServer := keeper.NewMsgServerImpl(k) + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case types.MsgSend: - return handleMsgSend(ctx, k, msg) + case *types.MsgSend: + res, err := msgServer.Send(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgMultiSend: - return handleMsgMultiSend(ctx, k, msg) + case *types.MsgMultiSend: + res, err := msgServer.MultiSend(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized bank message type: %T", msg) } } } - -// Handle MsgSend. -func handleMsgSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgSend) (*sdk.Result, error) { - if !k.GetSendEnabled(ctx) { - return nil, types.ErrSendDisabled - } - - if k.BlacklistedAddr(msg.ToAddress) { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive transactions", msg.ToAddress) - } - - err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -// Handle MsgMultiSend. -func handleMsgMultiSend(ctx sdk.Context, k keeper.Keeper, msg types.MsgMultiSend) (*sdk.Result, error) { - // NOTE: totalIn == totalOut should already have been checked - if !k.GetSendEnabled(ctx) { - return nil, types.ErrSendDisabled - } - - for _, out := range msg.Outputs { - if k.BlacklistedAddr(out.Address) { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive transactions", out.Address) - } - } - - err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} diff --git a/x/bank/handler_test.go b/x/bank/handler_test.go index f9d313385f0a..5fcdf02f1a93 100644 --- a/x/bank/handler_test.go +++ b/x/bank/handler_test.go @@ -1,23 +1,88 @@ -package bank +package bank_test import ( "strings" "testing" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestInvalidMsg(t *testing.T) { - h := NewHandler(nil) + h := bank.NewHandler(nil) - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) require.Error(t, err) require.Nil(t, res) _, _, log := sdkerrors.ABCIInfo(err, false) require.True(t, strings.Contains(log, "unrecognized bank message type")) } + +// A module account cannot be the recipient of bank sends unless it has been marked as such +func TestSendToModuleAccount(t *testing.T) { + priv1 := secp256k1.GenPrivKey() + addr1 := sdk.AccAddress(priv1.PubKey().Address()) + moduleAccAddr := authtypes.NewModuleAddress(stakingtypes.BondedPoolName) + coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} + + tests := []struct { + name string + expectedError error + msg *types.MsgSend + }{ + { + name: "not allowed module account", + msg: types.NewMsgSend(addr1, moduleAccAddr, coins), + expectedError: sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", moduleAccAddr), + }, + { + name: "allowed module account", + msg: types.NewMsgSend(addr1, authtypes.NewModuleAddress(stakingtypes.ModuleName), coins), + expectedError: nil, + }, + } + + acc1 := &authtypes.BaseAccount{ + Address: addr1.String(), + } + accs := authtypes.GenesisAccounts{acc1} + balances := []types.Balance{ + { + Address: addr1.String(), + Coins: coins, + }, + } + + app := simapp.SetupWithGenesisAccounts(accs, balances...) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.BankKeeper = bankkeeper.NewBaseKeeper( + app.AppCodec(), app.GetKey(types.StoreKey), app.AccountKeeper, app.GetSubspace(types.ModuleName), map[string]bool{ + moduleAccAddr.String(): true, + }, + ) + handler := bank.NewHandler(app.BankKeeper) + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := handler(ctx, tc.msg) + if tc.expectedError != nil { + require.EqualError(t, err, tc.expectedError.Error()) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/bank/internal/keeper/integration_test.go b/x/bank/internal/keeper/integration_test.go deleted file mode 100644 index f008a5558839..000000000000 --- a/x/bank/internal/keeper/integration_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package keeper_test - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" -) - -func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { - app := simapp.Setup(isCheckTx) - ctx := app.BaseApp.NewContext(isCheckTx, abci.Header{}) - - app.AccountKeeper.SetParams(ctx, auth.DefaultParams()) - app.BankKeeper.SetSendEnabled(ctx, true) - - return app, ctx -} diff --git a/x/bank/internal/keeper/invariants.go b/x/bank/internal/keeper/invariants.go deleted file mode 100644 index 0be1e8f58f23..000000000000 --- a/x/bank/internal/keeper/invariants.go +++ /dev/null @@ -1,37 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" -) - -// RegisterInvariants registers the bank module invariants -func RegisterInvariants(ir sdk.InvariantRegistry, ak types.AccountKeeper) { - ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding", - NonnegativeBalanceInvariant(ak)) -} - -// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances -func NonnegativeBalanceInvariant(ak types.AccountKeeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var msg string - var count int - - accts := ak.GetAllAccounts(ctx) - for _, acc := range accts { - coins := acc.GetCoins() - if coins.IsAnyNegative() { - count++ - msg += fmt.Sprintf("\t%s has a negative denomination of %s\n", - acc.GetAddress().String(), - coins.String()) - } - } - broken := count != 0 - - return sdk.FormatInvariant(types.ModuleName, "nonnegative-outstanding", - fmt.Sprintf("amount of negative accounts found %d\n%s", count, msg)), broken - } -} diff --git a/x/bank/internal/keeper/keeper.go b/x/bank/internal/keeper/keeper.go deleted file mode 100644 index 8e5f5faa5c8d..000000000000 --- a/x/bank/internal/keeper/keeper.go +++ /dev/null @@ -1,394 +0,0 @@ -package keeper - -import ( - "fmt" - "time" - - "github.com/tendermint/tendermint/libs/log" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -var _ Keeper = (*BaseKeeper)(nil) - -// Keeper defines a module interface that facilitates the transfer of coins -// between accounts. -type Keeper interface { - SendKeeper - - DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error - UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error -} - -// BaseKeeper manages transfers between accounts. It implements the Keeper interface. -type BaseKeeper struct { - BaseSendKeeper - - ak types.AccountKeeper - paramSpace params.Subspace -} - -// NewBaseKeeper returns a new BaseKeeper -func NewBaseKeeper( - ak types.AccountKeeper, paramSpace params.Subspace, blacklistedAddrs map[string]bool, -) BaseKeeper { - - ps := paramSpace.WithKeyTable(types.ParamKeyTable()) - return BaseKeeper{ - BaseSendKeeper: NewBaseSendKeeper(ak, ps, blacklistedAddrs), - ak: ak, - paramSpace: ps, - } -} - -// DelegateCoins performs delegation by deducting amt coins from an account with -// address addr. For vesting accounts, delegations amounts are tracked for both -// vesting and vested coins. -// The coins are then transferred from the delegator address to a ModuleAccount address. -// If any of the delegation amounts are negative, an error is returned. -func (keeper BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error { - delegatorAcc := keeper.ak.GetAccount(ctx, delegatorAddr) - if delegatorAcc == nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", delegatorAddr) - } - - moduleAcc := keeper.ak.GetAccount(ctx, moduleAccAddr) - if moduleAcc == nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr) - } - - if !amt.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) - } - - oldCoins := delegatorAcc.GetCoins() - - _, hasNeg := oldCoins.SafeSub(amt) - if hasNeg { - return sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, "insufficient account funds; %s < %s", oldCoins, amt, - ) - } - - if err := trackDelegation(delegatorAcc, ctx.BlockHeader().Time, amt); err != nil { - return sdkerrors.Wrap(err, "failed to track delegation") - } - - keeper.ak.SetAccount(ctx, delegatorAcc) - - _, err := keeper.AddCoins(ctx, moduleAccAddr, amt) - if err != nil { - return err - } - - return nil -} - -// UndelegateCoins performs undelegation by crediting amt coins to an account with -// address addr. For vesting accounts, undelegation amounts are tracked for both -// vesting and vested coins. -// The coins are then transferred from a ModuleAccount address to the delegator address. -// If any of the undelegation amounts are negative, an error is returned. -func (keeper BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error { - delegatorAcc := keeper.ak.GetAccount(ctx, delegatorAddr) - if delegatorAcc == nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", delegatorAddr) - } - - moduleAcc := keeper.ak.GetAccount(ctx, moduleAccAddr) - if moduleAcc == nil { - return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr) - } - - if !amt.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) - } - - oldCoins := moduleAcc.GetCoins() - - newCoins, hasNeg := oldCoins.SafeSub(amt) - if hasNeg { - return sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, "insufficient account funds; %s < %s", oldCoins, amt, - ) - } - - err := keeper.SetCoins(ctx, moduleAccAddr, newCoins) - if err != nil { - return err - } - - if err := trackUndelegation(delegatorAcc, amt); err != nil { - return sdkerrors.Wrap(err, "failed to track undelegation") - } - - keeper.ak.SetAccount(ctx, delegatorAcc) - return nil -} - -// SendKeeper defines a module interface that facilitates the transfer of coins -// between accounts without the possibility of creating coins. -type SendKeeper interface { - ViewKeeper - - 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) (sdk.Coins, error) - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) - SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error - - GetSendEnabled(ctx sdk.Context) bool - SetSendEnabled(ctx sdk.Context, enabled bool) - - BlacklistedAddr(addr sdk.AccAddress) bool -} - -var _ SendKeeper = (*BaseSendKeeper)(nil) - -// BaseSendKeeper only allows transfers between accounts without the possibility of -// creating coins. It implements the SendKeeper interface. -type BaseSendKeeper struct { - BaseViewKeeper - - ak types.AccountKeeper - paramSpace params.Subspace - - // list of addresses that are restricted from receiving transactions - blacklistedAddrs map[string]bool -} - -// NewBaseSendKeeper returns a new BaseSendKeeper. -func NewBaseSendKeeper( - ak types.AccountKeeper, paramSpace params.Subspace, blacklistedAddrs map[string]bool, -) BaseSendKeeper { - - return BaseSendKeeper{ - BaseViewKeeper: NewBaseViewKeeper(ak), - ak: ak, - paramSpace: paramSpace, - blacklistedAddrs: blacklistedAddrs, - } -} - -// InputOutputCoins handles a list of inputs and outputs -func (keeper BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error { - // Safety check ensuring that when sending coins the keeper must maintain the - // Check supply invariant and validity of Coins. - if err := types.ValidateInputsOutputs(inputs, outputs); err != nil { - return err - } - - for _, in := range inputs { - _, err := keeper.SubtractCoins(ctx, in.Address, in.Coins) - if err != nil { - return err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(types.AttributeKeySender, in.Address.String()), - ), - ) - } - - for _, out := range outputs { - _, err := keeper.AddCoins(ctx, out.Address, out.Coins) - if err != nil { - return err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeTransfer, - sdk.NewAttribute(types.AttributeKeyRecipient, out.Address.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()), - ), - ) - } - - return nil -} - -// SendCoins moves coins from one account to another -func (keeper BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { - ctx.EventManager().EmitEvents(sdk.Events{ - // This event should have all info (to, from, amount) without looking at other events - sdk.NewEvent( - types.EventTypeTransfer, - sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()), - sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), - ), - }) - - _, err := keeper.SubtractCoins(ctx, fromAddr, amt) - if err != nil { - return err - } - - _, err = keeper.AddCoins(ctx, toAddr, amt) - if err != nil { - return err - } - - return nil -} - -// SubtractCoins subtracts amt from the coins at the addr. -// -// CONTRACT: If the account is a vesting account, the amount has to be spendable. -func (keeper BaseSendKeeper) SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) { - if !amt.IsValid() { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) - } - - oldCoins, spendableCoins := sdk.NewCoins(), sdk.NewCoins() - - acc := keeper.ak.GetAccount(ctx, addr) - if acc != nil { - oldCoins = acc.GetCoins() - spendableCoins = acc.SpendableCoins(ctx.BlockHeader().Time) - } - - // For non-vesting accounts, spendable coins will simply be the original coins. - // So the check here is sufficient instead of subtracting from oldCoins. - _, hasNeg := spendableCoins.SafeSub(amt) - if hasNeg { - return amt, sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, "insufficient account funds; %s < %s", spendableCoins, amt, - ) - } - - newCoins := oldCoins.Sub(amt) // should not panic as spendable coins was already checked - err := keeper.SetCoins(ctx, addr, newCoins) - - return newCoins, err -} - -// AddCoins adds amt to the coins at the addr. -func (keeper BaseSendKeeper) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) { - if !amt.IsValid() { - return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) - } - - oldCoins := keeper.GetCoins(ctx, addr) - newCoins := oldCoins.Add(amt...) - - if newCoins.IsAnyNegative() { - return amt, sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, "insufficient account funds; %s < %s", oldCoins, amt, - ) - } - - err := keeper.SetCoins(ctx, addr, newCoins) - return newCoins, err -} - -// SetCoins sets the coins at the addr. -func (keeper BaseSendKeeper) SetCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { - if !amt.IsValid() { - sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) - } - - acc := keeper.ak.GetAccount(ctx, addr) - if acc == nil { - acc = keeper.ak.NewAccountWithAddress(ctx, addr) - } - - err := acc.SetCoins(amt) - if err != nil { - panic(err) - } - - keeper.ak.SetAccount(ctx, acc) - return nil -} - -// GetSendEnabled returns the current SendEnabled -func (keeper BaseSendKeeper) GetSendEnabled(ctx sdk.Context) bool { - var enabled bool - keeper.paramSpace.Get(ctx, types.ParamStoreKeySendEnabled, &enabled) - return enabled -} - -// SetSendEnabled sets the send enabled -func (keeper BaseSendKeeper) SetSendEnabled(ctx sdk.Context, enabled bool) { - keeper.paramSpace.Set(ctx, types.ParamStoreKeySendEnabled, &enabled) -} - -// BlacklistedAddr checks if a given address is blacklisted (i.e restricted from -// receiving funds) -func (keeper BaseSendKeeper) BlacklistedAddr(addr sdk.AccAddress) bool { - return keeper.blacklistedAddrs[addr.String()] -} - -var _ ViewKeeper = (*BaseViewKeeper)(nil) - -// ViewKeeper defines a module interface that facilitates read only access to -// account balances. -type ViewKeeper interface { - GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool -} - -// BaseViewKeeper implements a read only keeper implementation of ViewKeeper. -type BaseViewKeeper struct { - ak types.AccountKeeper -} - -// NewBaseViewKeeper returns a new BaseViewKeeper. -func NewBaseViewKeeper(ak types.AccountKeeper) BaseViewKeeper { - return BaseViewKeeper{ak: ak} -} - -// Logger returns a module-specific logger. -func (keeper BaseViewKeeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) -} - -// GetCoins returns the coins at the addr. -func (keeper BaseViewKeeper) GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { - acc := keeper.ak.GetAccount(ctx, addr) - if acc == nil { - return sdk.NewCoins() - } - return acc.GetCoins() -} - -// HasCoins returns whether or not an account has at least amt coins. -func (keeper BaseViewKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool { - return keeper.GetCoins(ctx, addr).IsAllGTE(amt) -} - -// CONTRACT: assumes that amt is valid. -func trackDelegation(acc authexported.Account, blockTime time.Time, amt sdk.Coins) error { - vacc, ok := acc.(vestexported.VestingAccount) - if ok { - // TODO: return error on account.TrackDelegation - vacc.TrackDelegation(blockTime, amt) - } - - return acc.SetCoins(acc.GetCoins().Sub(amt)) -} - -// CONTRACT: assumes that amt is valid. -func trackUndelegation(acc authexported.Account, amt sdk.Coins) error { - vacc, ok := acc.(vestexported.VestingAccount) - if ok { - // TODO: return error on account.TrackUndelegation - vacc.TrackUndelegation(amt) - } - - return acc.SetCoins(acc.GetCoins().Add(amt...)) -} diff --git a/x/bank/internal/keeper/keeper_test.go b/x/bank/internal/keeper/keeper_test.go deleted file mode 100644 index 1f8b38ab5723..000000000000 --- a/x/bank/internal/keeper/keeper_test.go +++ /dev/null @@ -1,544 +0,0 @@ -package keeper_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - tmkv "github.com/tendermint/tendermint/libs/kv" - tmtime "github.com/tendermint/tendermint/types/time" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - keep "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" - "github.com/cosmos/cosmos-sdk/x/supply" -) - -func TestKeeper(t *testing.T) { - app, ctx := createTestApp(false) - - addr := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - addr3 := sdk.AccAddress([]byte("addr3")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - - // Test GetCoins/SetCoins - app.AccountKeeper.SetAccount(ctx, acc) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins())) - - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - - // Test HasCoins - require.True(t, app.BankKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, app.BankKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - require.False(t, app.BankKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15)))) - require.False(t, app.BankKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 5)))) - - // Test AddCoins - app.BankKeeper.AddCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 25)))) - - app.BankKeeper.AddCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 15))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 15), sdk.NewInt64Coin("foocoin", 25)))) - - // Test SubtractCoins - app.BankKeeper.SubtractCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10))) - app.BankKeeper.SubtractCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 5))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 15)))) - - app.BankKeeper.SubtractCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 11))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 15)))) - - app.BankKeeper.SubtractCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15)))) - require.False(t, app.BankKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 1)))) - - // Test SendCoins - app.BankKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - - app.BankKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - - app.BankKeeper.AddCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 30))) - app.BankKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 5))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 20), sdk.NewInt64Coin("foocoin", 5)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 10)))) - - // Test InputOutputCoins - input1 := types.NewInput(addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 2))) - output1 := types.NewOutput(addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 2))) - app.BankKeeper.InputOutputCoins(ctx, []types.Input{input1}, []types.Output{output1}) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 20), sdk.NewInt64Coin("foocoin", 7)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 8)))) - - inputs := []types.Input{ - types.NewInput(addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 3))), - types.NewInput(addr2, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 3), sdk.NewInt64Coin("foocoin", 2))), - } - - outputs := []types.Output{ - types.NewOutput(addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 1))), - types.NewOutput(addr3, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 2), sdk.NewInt64Coin("foocoin", 5))), - } - app.BankKeeper.InputOutputCoins(ctx, inputs, outputs) - require.True(t, app.BankKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 21), sdk.NewInt64Coin("foocoin", 4)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 7), sdk.NewInt64Coin("foocoin", 6)))) - require.True(t, app.BankKeeper.GetCoins(ctx, addr3).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 2), sdk.NewInt64Coin("foocoin", 5)))) - - // Test retrieving black listed accounts - for acc := range simapp.GetMaccPerms() { - addr := supply.NewModuleAddress(acc) - require.Equal(t, app.BlacklistedAccAddrs()[addr.String()], app.BankKeeper.BlacklistedAddr(addr)) - } -} - -func TestSendKeeper(t *testing.T) { - app, ctx := createTestApp(false) - - blacklistedAddrs := make(map[string]bool) - - paramSpace := app.ParamsKeeper.Subspace("newspace") - sendKeeper := keep.NewBaseSendKeeper(app.AccountKeeper, paramSpace, blacklistedAddrs) - app.BankKeeper.SetSendEnabled(ctx, true) - - addr := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - - // Test GetCoins/SetCoins - app.AccountKeeper.SetAccount(ctx, acc) - require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins())) - - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10))) - require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - - // Test HasCoins - require.True(t, sendKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, sendKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - require.False(t, sendKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15)))) - require.False(t, sendKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 5)))) - - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15))) - - // Test SendCoins - sendKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5))) - require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - - sendKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) - require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - - app.BankKeeper.AddCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 30))) - sendKeeper.SendCoins(ctx, addr, addr2, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 5))) - require.True(t, sendKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 20), sdk.NewInt64Coin("foocoin", 5)))) - require.True(t, sendKeeper.GetCoins(ctx, addr2).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("barcoin", 10), sdk.NewInt64Coin("foocoin", 10)))) - - // validate coins with invalid denoms or negative values cannot be sent - // NOTE: We must use the Coin literal as the constructor does not allow - // negative values. - err := sendKeeper.SendCoins(ctx, addr, addr2, sdk.Coins{sdk.Coin{Denom: "FOOCOIN", Amount: sdk.NewInt(-5)}}) - require.Error(t, err) -} - -func TestMsgSendEvents(t *testing.T) { - app, ctx := createTestApp(false) - - app.BankKeeper.SetSendEnabled(ctx, true) - - addr := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - - app.AccountKeeper.SetAccount(ctx, acc) - newCoins := sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50)) - err := app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins) - require.Error(t, err) - events := ctx.EventManager().Events() - require.Equal(t, 2, len(events)) - event1 := sdk.Event{ - Type: types.EventTypeTransfer, - Attributes: []tmkv.Pair{}, - } - event1.Attributes = append( - event1.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr2.String())}, - tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, - tmkv.Pair{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())}) - event2 := sdk.Event{ - Type: sdk.EventTypeMessage, - Attributes: []tmkv.Pair{}, - } - event2.Attributes = append( - event2.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}) - require.Equal(t, event1, events[0]) - require.Equal(t, event2, events[1]) - - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) - newCoins = sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50)) - err = app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins) - require.NoError(t, err) - events = ctx.EventManager().Events() - require.Equal(t, 4, len(events)) - require.Equal(t, event1, events[2]) - require.Equal(t, event2, events[3]) -} - -func TestMsgMultiSendEvents(t *testing.T) { - app, ctx := createTestApp(false) - - app.BankKeeper.SetSendEnabled(ctx, true) - - addr := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - addr3 := sdk.AccAddress([]byte("addr3")) - addr4 := sdk.AccAddress([]byte("addr4")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - - app.AccountKeeper.SetAccount(ctx, acc) - app.AccountKeeper.SetAccount(ctx, acc2) - newCoins := sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50)) - newCoins2 := sdk.NewCoins(sdk.NewInt64Coin("barcoin", 100)) - inputs := []types.Input{ - {Address: addr, Coins: newCoins}, - {Address: addr2, Coins: newCoins2}, - } - outputs := []types.Output{ - {Address: addr3, Coins: newCoins}, - {Address: addr4, Coins: newCoins2}, - } - err := app.BankKeeper.InputOutputCoins(ctx, inputs, outputs) - require.Error(t, err) - events := ctx.EventManager().Events() - require.Equal(t, 0, len(events)) - - // Set addr's coins but not addr2's coins - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) - - err = app.BankKeeper.InputOutputCoins(ctx, inputs, outputs) - require.Error(t, err) - events = ctx.EventManager().Events() - require.Equal(t, 1, len(events)) - event1 := sdk.Event{ - Type: sdk.EventTypeMessage, - Attributes: []tmkv.Pair{}, - } - event1.Attributes = append( - event1.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}) - require.Equal(t, event1, events[0]) - - // Set addr's coins and addr2's coins - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50))) - newCoins = sdk.NewCoins(sdk.NewInt64Coin("foocoin", 50)) - app.BankKeeper.SetCoins(ctx, addr2, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 100))) - newCoins2 = sdk.NewCoins(sdk.NewInt64Coin("barcoin", 100)) - - err = app.BankKeeper.InputOutputCoins(ctx, inputs, outputs) - require.NoError(t, err) - events = ctx.EventManager().Events() - require.Equal(t, 5, len(events)) - event2 := sdk.Event{ - Type: sdk.EventTypeMessage, - Attributes: []tmkv.Pair{}, - } - event2.Attributes = append( - event2.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr2.String())}) - event3 := sdk.Event{ - Type: types.EventTypeTransfer, - Attributes: []tmkv.Pair{}, - } - event3.Attributes = append( - event3.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr3.String())}) - event3.Attributes = append( - event3.Attributes, - tmkv.Pair{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())}) - event4 := sdk.Event{ - Type: types.EventTypeTransfer, - Attributes: []tmkv.Pair{}, - } - event4.Attributes = append( - event4.Attributes, - tmkv.Pair{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr4.String())}) - event4.Attributes = append( - event4.Attributes, - tmkv.Pair{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())}) - require.Equal(t, event1, events[1]) - require.Equal(t, event2, events[2]) - require.Equal(t, event3, events[3]) - require.Equal(t, event4, events[4]) -} - -func TestViewKeeper(t *testing.T) { - app, ctx := createTestApp(false) - - //paramSpace := app.ParamsKeeper.Subspace(types.DefaultParamspace) - viewKeeper := keep.NewBaseViewKeeper(app.AccountKeeper) - - addr := sdk.AccAddress([]byte("addr1")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - - // Test GetCoins/SetCoins - app.AccountKeeper.SetAccount(ctx, acc) - require.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins())) - - app.BankKeeper.SetCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10))) - require.True(t, viewKeeper.GetCoins(ctx, addr).IsEqual(sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - - // Test HasCoins - require.True(t, viewKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 10)))) - require.True(t, viewKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 5)))) - require.False(t, viewKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 15)))) - require.False(t, viewKeeper.HasCoins(ctx, addr, sdk.NewCoins(sdk.NewInt64Coin("barcoin", 5)))) -} - -func TestVestingAccountSend(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - endTime := now.Add(24 * time.Hour) - - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - vacc := vesting.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix()) - app.AccountKeeper.SetAccount(ctx, vacc) - - // require that no coins be sendable at the beginning of the vesting schedule - err := app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) - require.Error(t, err) - - // receive some coins - vacc.SetCoins(origCoins.Add(sendCoins...)) - app.AccountKeeper.SetAccount(ctx, vacc) - - // require that all vested coins are spendable plus any received - ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) - err = app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) - vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) - require.NoError(t, err) - require.Equal(t, origCoins, vacc.GetCoins()) -} - -func TestPeriodicVestingAccountSend(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - periods := vesting.Periods{ - vesting.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 50)}}, - vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, - vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, - } - vacc := vesting.NewPeriodicVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), periods) - app.AccountKeeper.SetAccount(ctx, vacc) - - // require that no coins be sendable at the beginning of the vesting schedule - err := app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) - require.Error(t, err) - - // receive some coins - vacc.SetCoins(origCoins.Add(sendCoins...)) - app.AccountKeeper.SetAccount(ctx, vacc) - - // require that all vested coins are spendable plus any received - ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) - err = app.BankKeeper.SendCoins(ctx, addr1, addr2, sendCoins) - vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.PeriodicVestingAccount) - require.NoError(t, err) - require.Equal(t, origCoins, vacc.GetCoins()) -} - -func TestVestingAccountReceive(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - endTime := now.Add(24 * time.Hour) - - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - vacc := vesting.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix()) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - app.AccountKeeper.SetAccount(ctx, vacc) - app.AccountKeeper.SetAccount(ctx, acc) - app.BankKeeper.SetCoins(ctx, addr2, origCoins) - - // send some coins to the vesting account - app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins) - - // require the coins are spendable - vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) - require.Equal(t, origCoins.Add(sendCoins...), vacc.GetCoins()) - require.Equal(t, vacc.SpendableCoins(now), sendCoins) - - // require coins are spendable plus any that have vested - require.Equal(t, vacc.SpendableCoins(now.Add(12*time.Hour)), origCoins) -} - -func TestPeriodicVestingAccountReceive(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - periods := vesting.Periods{ - vesting.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 50)}}, - vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, - vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, - } - vacc := vesting.NewPeriodicVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), periods) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) - app.AccountKeeper.SetAccount(ctx, vacc) - app.AccountKeeper.SetAccount(ctx, acc) - app.BankKeeper.SetCoins(ctx, addr2, origCoins) - - // send some coins to the vesting account - app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins) - - // require the coins are spendable - vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.PeriodicVestingAccount) - require.Equal(t, origCoins.Add(sendCoins...), vacc.GetCoins()) - require.Equal(t, vacc.SpendableCoins(now), sendCoins) - - // require coins are spendable plus any that have vested - require.Equal(t, vacc.SpendableCoins(now.Add(12*time.Hour)), origCoins) -} - -func TestDelegateCoins(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - endTime := now.Add(24 * time.Hour) - ak := app.AccountKeeper - - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - delCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - addrModule := sdk.AccAddress([]byte("moduleAcc")) - - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - macc := ak.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing - vacc := vesting.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix()) - acc := ak.NewAccountWithAddress(ctx, addr2) - ak.SetAccount(ctx, vacc) - ak.SetAccount(ctx, acc) - ak.SetAccount(ctx, macc) - app.BankKeeper.SetCoins(ctx, addr2, origCoins) - - ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) - - // require the ability for a non-vesting account to delegate - err := app.BankKeeper.DelegateCoins(ctx, addr2, addrModule, delCoins) - acc = ak.GetAccount(ctx, addr2) - macc = ak.GetAccount(ctx, addrModule) - require.NoError(t, err) - require.Equal(t, origCoins.Sub(delCoins), acc.GetCoins()) - require.Equal(t, delCoins, macc.GetCoins()) - - // require the ability for a vesting account to delegate - err = app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins) - vacc = ak.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) - require.NoError(t, err) - require.Equal(t, delCoins, vacc.GetCoins()) -} - -func TestUndelegateCoins(t *testing.T) { - app, ctx := createTestApp(false) - now := tmtime.Now() - ctx = ctx.WithBlockHeader(abci.Header{Time: now}) - endTime := now.Add(24 * time.Hour) - ak := app.AccountKeeper - - origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - delCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) - - addr1 := sdk.AccAddress([]byte("addr1")) - addr2 := sdk.AccAddress([]byte("addr2")) - addrModule := sdk.AccAddress([]byte("moduleAcc")) - - bacc := auth.NewBaseAccountWithAddress(addr1) - bacc.SetCoins(origCoins) - macc := ak.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing - vacc := vesting.NewContinuousVestingAccount(&bacc, ctx.BlockHeader().Time.Unix(), endTime.Unix()) - acc := ak.NewAccountWithAddress(ctx, addr2) - ak.SetAccount(ctx, vacc) - ak.SetAccount(ctx, acc) - ak.SetAccount(ctx, macc) - app.BankKeeper.SetCoins(ctx, addr2, origCoins) - - ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) - - // require the ability for a non-vesting account to delegate - err := app.BankKeeper.DelegateCoins(ctx, addr2, addrModule, delCoins) - require.NoError(t, err) - - acc = ak.GetAccount(ctx, addr2) - macc = ak.GetAccount(ctx, addrModule) - require.Equal(t, origCoins.Sub(delCoins), acc.GetCoins()) - require.Equal(t, delCoins, macc.GetCoins()) - - // require the ability for a non-vesting account to undelegate - err = app.BankKeeper.UndelegateCoins(ctx, addrModule, addr2, delCoins) - require.NoError(t, err) - - acc = ak.GetAccount(ctx, addr2) - macc = ak.GetAccount(ctx, addrModule) - require.Equal(t, origCoins, acc.GetCoins()) - require.True(t, macc.GetCoins().Empty()) - - // require the ability for a vesting account to delegate - err = app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins) - require.NoError(t, err) - - vacc = ak.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) - macc = ak.GetAccount(ctx, addrModule) - require.Equal(t, origCoins.Sub(delCoins), vacc.GetCoins()) - require.Equal(t, delCoins, macc.GetCoins()) - - // require the ability for a vesting account to undelegate - err = app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins) - require.NoError(t, err) - - vacc = ak.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) - macc = ak.GetAccount(ctx, addrModule) - require.Equal(t, origCoins, vacc.GetCoins()) - require.True(t, macc.GetCoins().Empty()) -} diff --git a/x/bank/internal/keeper/querier.go b/x/bank/internal/keeper/querier.go deleted file mode 100644 index 165d01879621..000000000000 --- a/x/bank/internal/keeper/querier.go +++ /dev/null @@ -1,50 +0,0 @@ -package keeper - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "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/bank/internal/types" -) - -const ( - // query balance path - QueryBalance = "balances" -) - -// NewQuerier returns a new sdk.Keeper instance. -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case QueryBalance: - return queryBalance(ctx, req, k) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) - } - } -} - -// queryBalance fetch an account's balance for the supplied height. -// Height and account address are passed as first and second path components respectively. -func queryBalance(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryBalanceParams - - if err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms); err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - coins := k.GetCoins(ctx, params.Address) - if coins == nil { - coins = sdk.NewCoins() - } - - bz, err := codec.MarshalJSONIndent(types.ModuleCdc, coins) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} diff --git a/x/bank/internal/keeper/querier_test.go b/x/bank/internal/keeper/querier_test.go deleted file mode 100644 index 11f7504043a4..000000000000 --- a/x/bank/internal/keeper/querier_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package keeper_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - keep "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" -) - -func TestBalances(t *testing.T) { - app, ctx := createTestApp(false) - req := abci.RequestQuery{ - Path: fmt.Sprintf("custom/bank/%s", keep.QueryBalance), - Data: []byte{}, - } - - querier := keep.NewQuerier(app.BankKeeper) - - res, err := querier(ctx, []string{"balances"}, req) - require.NotNil(t, err) - require.Nil(t, res) - - _, _, addr := authtypes.KeyTestPubAddr() - req.Data = app.Codec().MustMarshalJSON(types.NewQueryBalanceParams(addr)) - res, err = querier(ctx, []string{"balances"}, req) - require.Nil(t, err) // the account does not exist, no error returned anyway - require.NotNil(t, res) - - var coins sdk.Coins - require.NoError(t, app.Codec().UnmarshalJSON(res, &coins)) - require.True(t, coins.IsZero()) - - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - acc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin("foo", 10))) - app.AccountKeeper.SetAccount(ctx, acc) - res, err = querier(ctx, []string{"balances"}, req) - require.Nil(t, err) - require.NotNil(t, res) - require.NoError(t, app.Codec().UnmarshalJSON(res, &coins)) - require.True(t, coins.AmountOf("foo").Equal(sdk.NewInt(10))) -} - -func TestQuerierRouteNotFound(t *testing.T) { - app, ctx := createTestApp(false) - req := abci.RequestQuery{ - Path: "custom/bank/notfound", - Data: []byte{}, - } - - querier := keep.NewQuerier(app.BankKeeper) - _, err := querier(ctx, []string{"notfound"}, req) - require.Error(t, err) -} diff --git a/x/bank/internal/types/codec.go b/x/bank/internal/types/codec.go deleted file mode 100644 index 6d4d49adc118..000000000000 --- a/x/bank/internal/types/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/MsgSend", nil) - cdc.RegisterConcrete(MsgMultiSend{}, "cosmos-sdk/MsgMultiSend", nil) -} - -// module codec -var ModuleCdc *codec.Codec - -func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - ModuleCdc.Seal() -} diff --git a/x/bank/internal/types/errors.go b/x/bank/internal/types/errors.go deleted file mode 100644 index 157408c941b7..000000000000 --- a/x/bank/internal/types/errors.go +++ /dev/null @@ -1,13 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// x/bank module sentinel errors -var ( - ErrNoInputs = sdkerrors.Register(ModuleName, 1, "no inputs to send transaction") - ErrNoOutputs = sdkerrors.Register(ModuleName, 2, "no outputs to send transaction") - ErrInputOutputMismatch = sdkerrors.Register(ModuleName, 3, "sum inputs != sum outputs") - ErrSendDisabled = sdkerrors.Register(ModuleName, 4, "send transactions are disabled") -) diff --git a/x/bank/internal/types/expected_keepers.go b/x/bank/internal/types/expected_keepers.go deleted file mode 100644 index 23e06d454939..000000000000 --- a/x/bank/internal/types/expected_keepers.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" -) - -// AccountKeeper defines the account contract that must be fulfilled when -// creating a x/bank keeper. -type AccountKeeper interface { - NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) exported.Account - - GetAccount(ctx sdk.Context, addr sdk.AccAddress) exported.Account - GetAllAccounts(ctx sdk.Context) []exported.Account - SetAccount(ctx sdk.Context, acc exported.Account) - - IterateAccounts(ctx sdk.Context, process func(exported.Account) bool) -} diff --git a/x/bank/internal/types/genesis.go b/x/bank/internal/types/genesis.go deleted file mode 100644 index 159b04f1f9a4..000000000000 --- a/x/bank/internal/types/genesis.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -// GenesisState is the bank state that must be provided at genesis. -type GenesisState struct { - SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` -} - -// NewGenesisState creates a new genesis state. -func NewGenesisState(sendEnabled bool) GenesisState { - return GenesisState{SendEnabled: sendEnabled} -} - -// DefaultGenesisState returns a default genesis state -func DefaultGenesisState() GenesisState { return NewGenesisState(true) } - -// ValidateGenesis performs basic validation of bank genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { return nil } diff --git a/x/bank/internal/types/key.go b/x/bank/internal/types/key.go deleted file mode 100644 index 7e38aeb52487..000000000000 --- a/x/bank/internal/types/key.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -const ( - // module name - ModuleName = "bank" - QuerierRoute = ModuleName -) diff --git a/x/bank/internal/types/msgs.go b/x/bank/internal/types/msgs.go deleted file mode 100644 index 50f8e3f06ff1..000000000000 --- a/x/bank/internal/types/msgs.go +++ /dev/null @@ -1,188 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// RouterKey is they name of the bank module -const RouterKey = ModuleName - -// MsgSend - high level transaction of the coin module -type MsgSend struct { - FromAddress sdk.AccAddress `json:"from_address" yaml:"from_address"` - ToAddress sdk.AccAddress `json:"to_address" yaml:"to_address"` - Amount sdk.Coins `json:"amount" yaml:"amount"` -} - -var _ sdk.Msg = MsgSend{} - -// NewMsgSend - construct arbitrary multi-in, multi-out send msg. -func NewMsgSend(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) MsgSend { - return MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount} -} - -// Route Implements Msg. -func (msg MsgSend) Route() string { return RouterKey } - -// Type Implements Msg. -func (msg MsgSend) Type() string { return "send" } - -// ValidateBasic Implements Msg. -func (msg MsgSend) ValidateBasic() error { - if msg.FromAddress.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing sender address") - } - if msg.ToAddress.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing recipient address") - } - if !msg.Amount.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) - } - if !msg.Amount.IsAllPositive() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) - } - return nil -} - -// GetSignBytes Implements Msg. -func (msg MsgSend) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -// GetSigners Implements Msg. -func (msg MsgSend) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.FromAddress} -} - -// MsgMultiSend - high level transaction of the coin module -type MsgMultiSend struct { - Inputs []Input `json:"inputs" yaml:"inputs"` - Outputs []Output `json:"outputs" yaml:"outputs"` -} - -var _ sdk.Msg = MsgMultiSend{} - -// NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. -func NewMsgMultiSend(in []Input, out []Output) MsgMultiSend { - return MsgMultiSend{Inputs: in, Outputs: out} -} - -// Route Implements Msg -func (msg MsgMultiSend) Route() string { return RouterKey } - -// Type Implements Msg -func (msg MsgMultiSend) Type() string { return "multisend" } - -// ValidateBasic Implements Msg. -func (msg MsgMultiSend) ValidateBasic() error { - // this just makes sure all the inputs and outputs are properly formatted, - // not that they actually have the money inside - if len(msg.Inputs) == 0 { - return ErrNoInputs - } - if len(msg.Outputs) == 0 { - return ErrNoOutputs - } - - return ValidateInputsOutputs(msg.Inputs, msg.Outputs) -} - -// GetSignBytes Implements Msg. -func (msg MsgMultiSend) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -// GetSigners Implements Msg. -func (msg MsgMultiSend) GetSigners() []sdk.AccAddress { - addrs := make([]sdk.AccAddress, len(msg.Inputs)) - for i, in := range msg.Inputs { - addrs[i] = in.Address - } - return addrs -} - -// Input models transaction input -type Input struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` -} - -// ValidateBasic - validate transaction input -func (in Input) ValidateBasic() error { - if len(in.Address) == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "input address missing") - } - if !in.Coins.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, in.Coins.String()) - } - if !in.Coins.IsAllPositive() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, in.Coins.String()) - } - return nil -} - -// NewInput - create a transaction input, used with MsgMultiSend -func NewInput(addr sdk.AccAddress, coins sdk.Coins) Input { - return Input{ - Address: addr, - Coins: coins, - } -} - -// Output models transaction outputs -type Output struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` -} - -// ValidateBasic - validate transaction output -func (out Output) ValidateBasic() error { - if len(out.Address) == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "output address missing") - } - if !out.Coins.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, out.Coins.String()) - } - if !out.Coins.IsAllPositive() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, out.Coins.String()) - } - return nil -} - -// NewOutput - create a transaction output, used with MsgMultiSend -func NewOutput(addr sdk.AccAddress, coins sdk.Coins) Output { - return Output{ - Address: addr, - Coins: coins, - } -} - -// ValidateInputsOutputs validates that each respective input and output is -// valid and that the sum of inputs is equal to the sum of outputs. -func ValidateInputsOutputs(inputs []Input, outputs []Output) error { - var totalIn, totalOut sdk.Coins - - for _, in := range inputs { - if err := in.ValidateBasic(); err != nil { - return err - } - - totalIn = totalIn.Add(in.Coins...) - } - - for _, out := range outputs { - if err := out.ValidateBasic(); err != nil { - return err - } - - totalOut = totalOut.Add(out.Coins...) - } - - // make sure inputs and outputs match - if !totalIn.IsEqual(totalOut) { - return ErrInputOutputMismatch - } - - return nil -} diff --git a/x/bank/internal/types/msgs_test.go b/x/bank/internal/types/msgs_test.go deleted file mode 100644 index e683b91d7e59..000000000000 --- a/x/bank/internal/types/msgs_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package types - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestMsgSendRoute(t *testing.T) { - addr1 := sdk.AccAddress([]byte("from")) - addr2 := sdk.AccAddress([]byte("to")) - coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) - var msg = NewMsgSend(addr1, addr2, coins) - - require.Equal(t, msg.Route(), RouterKey) - require.Equal(t, msg.Type(), "send") -} - -func TestMsgSendValidation(t *testing.T) { - addr1 := sdk.AccAddress([]byte("from")) - addr2 := sdk.AccAddress([]byte("to")) - atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) - atom0 := sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) - atom123eth123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 123)) - atom123eth0 := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 0)} - - var emptyAddr sdk.AccAddress - - cases := []struct { - valid bool - tx MsgSend - }{ - {true, NewMsgSend(addr1, addr2, atom123)}, // valid send - {true, NewMsgSend(addr1, addr2, atom123eth123)}, // valid send with multiple coins - {false, NewMsgSend(addr1, addr2, atom0)}, // non positive coin - {false, NewMsgSend(addr1, addr2, atom123eth0)}, // non positive coin in multicoins - {false, NewMsgSend(emptyAddr, addr2, atom123)}, // empty from addr - {false, NewMsgSend(addr1, emptyAddr, atom123)}, // empty to addr - } - - for _, tc := range cases { - err := tc.tx.ValidateBasic() - if tc.valid { - require.Nil(t, err) - } else { - require.NotNil(t, err) - } - } -} - -func TestMsgSendGetSignBytes(t *testing.T) { - addr1 := sdk.AccAddress([]byte("input")) - addr2 := sdk.AccAddress([]byte("output")) - coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) - var msg = NewMsgSend(addr1, addr2, coins) - res := msg.GetSignBytes() - - expected := `{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"10","denom":"atom"}],"from_address":"fetch1d9h8qat5fcpcsd","to_address":"fetch1da6hgur4wstrw7fy"}}` - require.Equal(t, expected, string(res)) -} - -func TestMsgSendGetSigners(t *testing.T) { - var msg = NewMsgSend(sdk.AccAddress([]byte("input1")), sdk.AccAddress{}, sdk.NewCoins()) - res := msg.GetSigners() - // TODO: fix this ! - require.Equal(t, fmt.Sprintf("%v", res), "[696E70757431]") -} - -func TestMsgMultiSendRoute(t *testing.T) { - // Construct a MsgSend - addr1 := sdk.AccAddress([]byte("input")) - addr2 := sdk.AccAddress([]byte("output")) - coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) - var msg = MsgMultiSend{ - Inputs: []Input{NewInput(addr1, coins)}, - Outputs: []Output{NewOutput(addr2, coins)}, - } - - // TODO some failures for bad result - require.Equal(t, msg.Route(), RouterKey) - require.Equal(t, msg.Type(), "multisend") -} - -func TestInputValidation(t *testing.T) { - addr1 := sdk.AccAddress([]byte{1, 2}) - addr2 := sdk.AccAddress([]byte{7, 8}) - someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) - multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) - - var emptyAddr sdk.AccAddress - emptyCoins := sdk.NewCoins() - emptyCoins2 := sdk.NewCoins(sdk.NewInt64Coin("eth", 0)) - someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} - unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} - - cases := []struct { - valid bool - txIn Input - }{ - // auth works with different apps - {true, NewInput(addr1, someCoins)}, - {true, NewInput(addr2, someCoins)}, - {true, NewInput(addr2, multiCoins)}, - - {false, NewInput(emptyAddr, someCoins)}, // empty address - {false, NewInput(addr1, emptyCoins)}, // invalid coins - {false, NewInput(addr1, emptyCoins2)}, // invalid coins - {false, NewInput(addr1, someEmptyCoins)}, // invalid coins - {false, NewInput(addr1, unsortedCoins)}, // unsorted coins - } - - for i, tc := range cases { - err := tc.txIn.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -func TestOutputValidation(t *testing.T) { - addr1 := sdk.AccAddress([]byte{1, 2}) - addr2 := sdk.AccAddress([]byte{7, 8}) - someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) - multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) - - var emptyAddr sdk.AccAddress - emptyCoins := sdk.NewCoins() - emptyCoins2 := sdk.NewCoins(sdk.NewInt64Coin("eth", 0)) - someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} - unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} - - cases := []struct { - valid bool - txOut Output - }{ - // auth works with different apps - {true, NewOutput(addr1, someCoins)}, - {true, NewOutput(addr2, someCoins)}, - {true, NewOutput(addr2, multiCoins)}, - - {false, NewOutput(emptyAddr, someCoins)}, // empty address - {false, NewOutput(addr1, emptyCoins)}, // invalid coins - {false, NewOutput(addr1, emptyCoins2)}, // invalid coins - {false, NewOutput(addr1, someEmptyCoins)}, // invalid coins - {false, NewOutput(addr1, unsortedCoins)}, // unsorted coins - } - - for i, tc := range cases { - err := tc.txOut.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -func TestMsgMultiSendValidation(t *testing.T) { - addr1 := sdk.AccAddress([]byte{1, 2}) - addr2 := sdk.AccAddress([]byte{7, 8}) - atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) - atom124 := sdk.NewCoins(sdk.NewInt64Coin("atom", 124)) - eth123 := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - atom123eth123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 123)) - - input1 := NewInput(addr1, atom123) - input2 := NewInput(addr1, eth123) - output1 := NewOutput(addr2, atom123) - output2 := NewOutput(addr2, atom124) - outputMulti := NewOutput(addr2, atom123eth123) - - var emptyAddr sdk.AccAddress - - cases := []struct { - valid bool - tx MsgMultiSend - }{ - {false, MsgMultiSend{}}, // no input or output - {false, MsgMultiSend{Inputs: []Input{input1}}}, // just input - {false, MsgMultiSend{Outputs: []Output{output1}}}, // just output - {false, MsgMultiSend{ - Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input - Outputs: []Output{output1}}}, - {false, MsgMultiSend{ - Inputs: []Input{input1}, - Outputs: []Output{{emptyAddr, atom123}}}, // invalid output - }, - {false, MsgMultiSend{ - Inputs: []Input{input1}, - Outputs: []Output{output2}}, // amounts dont match - }, - {true, MsgMultiSend{ - Inputs: []Input{input1}, - Outputs: []Output{output1}}, - }, - {true, MsgMultiSend{ - Inputs: []Input{input1, input2}, - Outputs: []Output{outputMulti}}, - }, - } - - for i, tc := range cases { - err := tc.tx.ValidateBasic() - if tc.valid { - require.Nil(t, err, "%d: %+v", i, err) - } else { - require.NotNil(t, err, "%d", i) - } - } -} - -func TestMsgMultiSendGetSignBytes(t *testing.T) { - addr1 := sdk.AccAddress([]byte("input")) - addr2 := sdk.AccAddress([]byte("output")) - coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) - var msg = MsgMultiSend{ - Inputs: []Input{NewInput(addr1, coins)}, - Outputs: []Output{NewOutput(addr2, coins)}, - } - res := msg.GetSignBytes() - - expected := `{"type":"cosmos-sdk/MsgMultiSend","value":{"inputs":[{"address":"fetch1d9h8qat5fcpcsd","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"fetch1da6hgur4wstrw7fy","coins":[{"amount":"10","denom":"atom"}]}]}}` - require.Equal(t, expected, string(res)) -} - -func TestMsgMultiSendGetSigners(t *testing.T) { - var msg = MsgMultiSend{ - Inputs: []Input{ - NewInput(sdk.AccAddress([]byte("input1")), nil), - NewInput(sdk.AccAddress([]byte("input2")), nil), - NewInput(sdk.AccAddress([]byte("input3")), nil), - }, - } - res := msg.GetSigners() - // TODO: fix this ! - require.Equal(t, fmt.Sprintf("%v", res), "[696E70757431 696E70757432 696E70757433]") -} - -/* -// what to do w/ this test? -func TestMsgSendSigners(t *testing.T) { - signers := []sdk.AccAddress{ - {1, 2, 3}, - {4, 5, 6}, - {7, 8, 9}, - } - - someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) - inputs := make([]Input, len(signers)) - for i, signer := range signers { - inputs[i] = NewInput(signer, someCoins) - } - tx := NewMsgSend(inputs, nil) - - require.Equal(t, signers, tx.Signers()) -} -*/ diff --git a/x/bank/internal/types/params.go b/x/bank/internal/types/params.go deleted file mode 100644 index cdd6aff84f81..000000000000 --- a/x/bank/internal/types/params.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/x/params" -) - -const ( - // DefaultParamspace for params keeper - DefaultParamspace = ModuleName - // DefaultSendEnabled enabled - DefaultSendEnabled = true -) - -// ParamStoreKeySendEnabled is store's key for SendEnabled -var ParamStoreKeySendEnabled = []byte("sendenabled") - -// ParamKeyTable type declaration for parameters -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable( - params.NewParamSetPair(ParamStoreKeySendEnabled, false, validateSendEnabled), - ) -} - -func validateSendEnabled(i interface{}) error { - _, ok := i.(bool) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - return nil -} diff --git a/x/bank/internal/types/querier.go b/x/bank/internal/types/querier.go deleted file mode 100644 index ef0a8576b879..000000000000 --- a/x/bank/internal/types/querier.go +++ /dev/null @@ -1,15 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// QueryBalanceParams defines the params for querying an account balance. -type QueryBalanceParams struct { - Address sdk.AccAddress -} - -// NewQueryBalanceParams creates a new instance of QueryBalanceParams. -func NewQueryBalanceParams(addr sdk.AccAddress) QueryBalanceParams { - return QueryBalanceParams{Address: addr} -} diff --git a/x/bank/keeper/genesis.go b/x/bank/keeper/genesis.go new file mode 100644 index 000000000000..d30415c6a285 --- /dev/null +++ b/x/bank/keeper/genesis.go @@ -0,0 +1,49 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// InitGenesis initializes the bank module's state from a given genesis state. +func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { + k.SetParams(ctx, genState.Params) + + var totalSupply sdk.Coins + + genState.Balances = types.SanitizeGenesisBalances(genState.Balances) + for _, balance := range genState.Balances { + addr, err := sdk.AccAddressFromBech32(balance.Address) + if err != nil { + panic(err) + } + + if err := k.SetBalances(ctx, addr, balance.Coins); err != nil { + panic(fmt.Errorf("error on setting balances %w", err)) + } + + totalSupply = totalSupply.Add(balance.Coins...) + } + + if genState.Supply.Empty() { + genState.Supply = totalSupply + } + + k.SetSupply(ctx, types.NewSupply(genState.Supply)) + + for _, meta := range genState.DenomMetadata { + k.SetDenomMetaData(ctx, meta) + } +} + +// ExportGenesis returns the bank module's genesis state. +func (k BaseKeeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return types.NewGenesisState( + k.GetParams(ctx), + k.GetAccountsBalances(ctx), + k.GetSupply(ctx).GetTotal(), + k.GetAllDenomMetaData(ctx), + ) +} diff --git a/x/bank/keeper/genesis_test.go b/x/bank/keeper/genesis_test.go new file mode 100644 index 000000000000..56c95c04bee9 --- /dev/null +++ b/x/bank/keeper/genesis_test.go @@ -0,0 +1,55 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (suite *IntegrationTestSuite) TestExportGenesis() { + app, ctx := suite.app, suite.ctx + + expectedMetadata := suite.getTestMetadata() + expectedBalances := suite.getTestBalances() + 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) + } + + totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))) + app.BankKeeper.SetSupply(ctx, totalSupply) + app.BankKeeper.SetParams(ctx, types.DefaultParams()) + + exportGenesis := app.BankKeeper.ExportGenesis(ctx) + + suite.Require().Len(exportGenesis.Params.SendEnabled, 0) + suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled) + suite.Require().Equal(totalSupply.GetTotal(), exportGenesis.Supply) + suite.Require().Equal(expectedBalances, exportGenesis.Balances) + suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata) +} + +func (suite *IntegrationTestSuite) getTestBalances() []types.Balance { + addr2, _ := sdk.AccAddressFromBech32("cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0") + addr1, _ := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + 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)}}, + } + +} + +func (suite *IntegrationTestSuite) TestInitGenesis() { + 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) + suite.Require().Equal(m, m2) +} diff --git a/x/bank/keeper/grpc_query.go b/x/bank/keeper/grpc_query.go new file mode 100644 index 000000000000..a0621967cf88 --- /dev/null +++ b/x/bank/keeper/grpc_query.go @@ -0,0 +1,165 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "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/bank/types" +) + +var _ types.QueryServer = BaseKeeper{} + +// Balance implements the Query/Balance gRPC method +func (k BaseKeeper) Balance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.Address == "" { + return nil, status.Error(codes.InvalidArgument, "address cannot be empty") + } + + if req.Denom == "" { + return nil, status.Error(codes.InvalidArgument, "invalid denom") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + address, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error()) + } + + balance := k.GetBalance(sdkCtx, address, req.Denom) + + return &types.QueryBalanceResponse{Balance: &balance}, nil +} + +// AllBalances implements the Query/AllBalances gRPC method +func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalancesRequest) (*types.QueryAllBalancesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.Address == "" { + return nil, status.Error(codes.InvalidArgument, "address cannot be empty") + } + + addr, err := sdk.AccAddressFromBech32(req.Address) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid address: %s", err.Error()) + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + + balances := sdk.NewCoins() + store := sdkCtx.KVStore(k.storeKey) + balancesStore := prefix.NewStore(store, types.BalancesPrefix) + accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + + pageRes, err := query.Paginate(accountStore, req.Pagination, func(_, value []byte) error { + var result sdk.Coin + err := k.cdc.UnmarshalBinaryBare(value, &result) + if err != nil { + return err + } + balances = append(balances, result) + return nil + }) + + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "paginate: %v", err) + } + + return &types.QueryAllBalancesResponse{Balances: balances, Pagination: pageRes}, nil +} + +// TotalSupply implements the Query/TotalSupply gRPC method +func (k BaseKeeper) TotalSupply(ctx context.Context, _ *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + totalSupply := k.GetSupply(sdkCtx).GetTotal() + + return &types.QueryTotalSupplyResponse{Supply: totalSupply}, nil +} + +// SupplyOf implements the Query/SupplyOf gRPC method +func (k BaseKeeper) SupplyOf(c context.Context, req *types.QuerySupplyOfRequest) (*types.QuerySupplyOfResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.Denom == "" { + return nil, status.Error(codes.InvalidArgument, "invalid denom") + } + + ctx := sdk.UnwrapSDKContext(c) + supply := k.GetSupply(ctx).GetTotal().AmountOf(req.Denom) + + return &types.QuerySupplyOfResponse{Amount: sdk.NewCoin(req.Denom, supply)}, nil +} + +// Params implements the gRPC service handler for querying x/bank parameters. +func (k BaseKeeper) Params(ctx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + params := k.GetParams(sdkCtx) + + 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 := k.GetDenomMetaData(ctx, req.Denom) + if metadata.Base == "" && metadata.Display == "" && metadata.Description == "" && len(metadata.DenomUnits) == 0 { + 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 new file mode 100644 index 000000000000..e178f1301751 --- /dev/null +++ b/x/bank/keeper/grpc_query_test.go @@ -0,0 +1,301 @@ +// +build norace + +package keeper_test + +import ( + gocontext "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (suite *IntegrationTestSuite) TestQueryBalance() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + _, _, addr := testdata.KeyTestPubAddr() + + _, err := queryClient.Balance(gocontext.Background(), &types.QueryBalanceRequest{}) + suite.Require().Error(err) + + _, err = queryClient.Balance(gocontext.Background(), &types.QueryBalanceRequest{Address: addr.String()}) + suite.Require().Error(err) + + req := types.NewQueryBalanceRequest(addr, fooDenom) + res, err := queryClient.Balance(gocontext.Background(), req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.True(res.Balance.IsZero()) + + origCoins := sdk.NewCoins(newFooCoin(50), newBarCoin(30)) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + + app.AccountKeeper.SetAccount(ctx, acc) + suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + + res, err = queryClient.Balance(gocontext.Background(), req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.True(res.Balance.IsEqual(newFooCoin(50))) +} + +func (suite *IntegrationTestSuite) TestQueryAllBalances() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + _, _, addr := testdata.KeyTestPubAddr() + _, err := queryClient.AllBalances(gocontext.Background(), &types.QueryAllBalancesRequest{}) + suite.Require().Error(err) + + pageReq := &query.PageRequest{ + Key: nil, + Limit: 1, + CountTotal: false, + } + req := types.NewQueryAllBalancesRequest(addr, pageReq) + res, err := queryClient.AllBalances(gocontext.Background(), req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.True(res.Balances.IsZero()) + + fooCoins := newFooCoin(50) + barCoins := newBarCoin(30) + + origCoins := sdk.NewCoins(fooCoins, barCoins) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + + app.AccountKeeper.SetAccount(ctx, acc) + suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + + res, err = queryClient.AllBalances(gocontext.Background(), req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Equal(res.Balances.Len(), 1) + suite.NotNil(res.Pagination.NextKey) + + suite.T().Log("query second page with nextkey") + pageReq = &query.PageRequest{ + Key: res.Pagination.NextKey, + Limit: 1, + CountTotal: true, + } + req = types.NewQueryAllBalancesRequest(addr, pageReq) + res, err = queryClient.AllBalances(gocontext.Background(), req) + suite.Equal(res.Balances.Len(), 1) + suite.Nil(res.Pagination.NextKey) +} + +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) + + res, err := queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{}) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + suite.Require().Equal(expectedTotalSupply.Total, res.Supply) +} + +func (suite *IntegrationTestSuite) TestQueryTotalSupplyOf() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + test1Supply := sdk.NewInt64Coin("test1", 4000000) + test2Supply := sdk.NewInt64Coin("test2", 700000000) + expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply)) + app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + + _, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{}) + suite.Require().Error(err) + + res, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{Denom: test1Supply.Denom}) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + suite.Require().Equal(test1Supply, res.Amount) +} + +func (suite *IntegrationTestSuite) TestQueryParams() { + res, err := suite.queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) + suite.Require().NoError(err) + 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/invariants.go b/x/bank/keeper/invariants.go new file mode 100644 index 000000000000..f06cde011196 --- /dev/null +++ b/x/bank/keeper/invariants.go @@ -0,0 +1,68 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// RegisterInvariants registers the bank module invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { + ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding", NonnegativeBalanceInvariant(k)) + ir.RegisterRoute(types.ModuleName, "total-supply", TotalSupply(k)) +} + +// AllInvariants runs all invariants of the X/bank module. +func AllInvariants(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + return TotalSupply(k)(ctx) + } +} + +// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances +func NonnegativeBalanceInvariant(k ViewKeeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var ( + msg string + count int + ) + + k.IterateAllBalances(ctx, func(addr sdk.AccAddress, balance sdk.Coin) bool { + if balance.IsNegative() { + count++ + msg += fmt.Sprintf("\t%s has a negative balance of %s\n", addr, balance) + } + + return false + }) + + broken := count != 0 + + return sdk.FormatInvariant( + types.ModuleName, "nonnegative-outstanding", + fmt.Sprintf("amount of negative balances found %d\n%s", count, msg), + ), broken + } +} + +// TotalSupply checks that the total supply reflects all the coins held in accounts +func TotalSupply(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + expectedTotal := sdk.Coins{} + supply := k.GetSupply(ctx) + + k.IterateAllBalances(ctx, func(_ sdk.AccAddress, balance sdk.Coin) bool { + expectedTotal = expectedTotal.Add(balance) + return false + }) + + broken := !expectedTotal.IsEqual(supply.GetTotal()) + + return sdk.FormatInvariant(types.ModuleName, "total supply", + fmt.Sprintf( + "\tsum of accounts coins: %v\n"+ + "\tsupply.Total: %v\n", + expectedTotal, supply.GetTotal())), broken + } +} diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go new file mode 100644 index 000000000000..10ab72790c5c --- /dev/null +++ b/x/bank/keeper/keeper.go @@ -0,0 +1,425 @@ +package keeper + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" + "github.com/cosmos/cosmos-sdk/x/bank/exported" + "github.com/cosmos/cosmos-sdk/x/bank/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var _ Keeper = (*BaseKeeper)(nil) + +// Keeper defines a module interface that facilitates the transfer of coins +// between accounts. +type Keeper interface { + SendKeeper + + InitGenesis(sdk.Context, *types.GenesisState) + 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 + SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) + IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) + + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + + DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error + UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error + MarshalSupply(supplyI exported.SupplyI) ([]byte, error) + UnmarshalSupply(bz []byte) (exported.SupplyI, error) + + types.QueryServer +} + +// BaseKeeper manages transfers between accounts. It implements the Keeper interface. +type BaseKeeper struct { + BaseSendKeeper + + ak types.AccountKeeper + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + paramSpace paramtypes.Subspace +} + +func NewBaseKeeper( + cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper, paramSpace paramtypes.Subspace, + blockedAddrs map[string]bool, +) BaseKeeper { + + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + return BaseKeeper{ + BaseSendKeeper: NewBaseSendKeeper(cdc, storeKey, ak, paramSpace, blockedAddrs), + ak: ak, + cdc: cdc, + storeKey: storeKey, + paramSpace: paramSpace, + } +} + +// DelegateCoins performs delegation by deducting amt coins from an account with +// address addr. For vesting accounts, delegations amounts are tracked for both +// vesting and vested coins. The coins are then transferred from the delegator +// address to a ModuleAccount address. If any of the delegation amounts are negative, +// an error is returned. +func (k BaseKeeper) DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error { + moduleAcc := k.ak.GetAccount(ctx, moduleAccAddr) + if moduleAcc == nil { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr) + } + + if !amt.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) + } + + balances := sdk.NewCoins() + + for _, coin := range amt { + balance := k.GetBalance(ctx, delegatorAddr, coin.Denom) + if balance.IsLT(coin) { + return sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFunds, "failed to delegate; %s is smaller than %s", balance, amt, + ) + } + + balances = balances.Add(balance) + err := k.SetBalance(ctx, delegatorAddr, balance.Sub(coin)) + if err != nil { + return err + } + } + + if err := k.trackDelegation(ctx, delegatorAddr, ctx.BlockHeader().Time, balances, amt); err != nil { + return sdkerrors.Wrap(err, "failed to track delegation") + } + + err := k.AddCoins(ctx, moduleAccAddr, amt) + if err != nil { + return err + } + + return nil +} + +// UndelegateCoins performs undelegation by crediting amt coins to an account with +// address addr. For vesting accounts, undelegation amounts are tracked for both +// vesting and vested coins. The coins are then transferred from a ModuleAccount +// address to the delegator address. If any of the undelegation amounts are +// negative, an error is returned. +func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error { + moduleAcc := k.ak.GetAccount(ctx, moduleAccAddr) + if moduleAcc == nil { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleAccAddr) + } + + if !amt.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) + } + + err := k.SubtractCoins(ctx, moduleAccAddr, amt) + if err != nil { + return err + } + + if err := k.trackUndelegation(ctx, delegatorAddr, amt); err != nil { + return sdkerrors.Wrap(err, "failed to track undelegation") + } + + err = k.AddCoins(ctx, delegatorAddr, amt) + if err != nil { + return err + } + + return nil +} + +// GetSupply retrieves the Supply from store +func (k BaseKeeper) GetSupply(ctx sdk.Context) exported.SupplyI { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.SupplyKey) + if bz == nil { + panic("stored supply should not have been nil") + } + + supply, err := k.UnmarshalSupply(bz) + if err != nil { + panic(err) + } + + return supply +} + +// 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 { + panic(err) + } + + store.Set(types.SupplyKey, bz) +} + +// GetDenomMetaData retrieves the denomination metadata +func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata { + store := ctx.KVStore(k.storeKey) + store = prefix.NewStore(store, types.DenomMetadataKey(denom)) + + bz := store.Get([]byte(denom)) + if bz == nil { + return types.Metadata{} + } + + var metadata types.Metadata + k.cdc.MustUnmarshalBinaryBare(bz, &metadata) + + return metadata +} + +// GetAllDenomMetaData retrieves all denominations metadata +func (k BaseKeeper) GetAllDenomMetaData(ctx sdk.Context) []types.Metadata { + denomMetaData := make([]types.Metadata, 0) + k.IterateAllDenomMetaData(ctx, func(metadata types.Metadata) bool { + denomMetaData = append(denomMetaData, metadata) + return false + }) + + return denomMetaData +} + +// IterateAllDenomMetaData iterates over all the denominations metadata and +// provides the metadata to a callback. If true is returned from the +// callback, iteration is halted. +func (k BaseKeeper) IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) { + store := ctx.KVStore(k.storeKey) + denomMetaDataStore := prefix.NewStore(store, types.DenomMetadataPrefix) + + iterator := denomMetaDataStore.Iterator(nil, nil) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var metadata types.Metadata + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &metadata) + + if cb(metadata) { + break + } + } +} + +// SetDenomMetaData sets the denominations metadata +func (k BaseKeeper) SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) { + store := ctx.KVStore(k.storeKey) + denomMetaDataStore := prefix.NewStore(store, types.DenomMetadataKey(denomMetaData.Base)) + + m := k.cdc.MustMarshalBinaryBare(&denomMetaData) + denomMetaDataStore.Set([]byte(denomMetaData.Base), m) +} + +// SendCoinsFromModuleToAccount transfers coins from a ModuleAccount to an AccAddress. +// It will panic if the module account does not exist. An error is returned if +// the recipient address is black-listed or if sending the tokens fails. +func (k BaseKeeper) SendCoinsFromModuleToAccount( + ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, +) error { + + senderAddr := k.ak.GetModuleAddress(senderModule) + if senderAddr == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) + } + + if k.BlockedAddr(recipientAddr) { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", recipientAddr) + } + + return k.SendCoins(ctx, senderAddr, recipientAddr, amt) +} + +// SendCoinsFromModuleToModule transfers coins from a ModuleAccount to another. +// It will panic if either module account does not exist. +func (k BaseKeeper) SendCoinsFromModuleToModule( + ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins, +) error { + + senderAddr := k.ak.GetModuleAddress(senderModule) + if senderAddr == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) + } + + recipientAcc := k.ak.GetModuleAccount(ctx, recipientModule) + if recipientAcc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) + } + + return k.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) +} + +// SendCoinsFromAccountToModule transfers coins from an AccAddress to a ModuleAccount. +// It will panic if the module account does not exist. +func (k BaseKeeper) SendCoinsFromAccountToModule( + ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins, +) error { + + recipientAcc := k.ak.GetModuleAccount(ctx, recipientModule) + if recipientAcc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) + } + + return k.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) +} + +// DelegateCoinsFromAccountToModule delegates coins and transfers them from a +// delegator account to a module account. It will panic if the module account +// does not exist or is unauthorized. +func (k BaseKeeper) DelegateCoinsFromAccountToModule( + ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins, +) error { + + recipientAcc := k.ak.GetModuleAccount(ctx, recipientModule) + if recipientAcc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) + } + + if !recipientAcc.HasPermission(authtypes.Staking) { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to receive delegated coins", recipientModule)) + } + + return k.DelegateCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) +} + +// UndelegateCoinsFromModuleToAccount undelegates the unbonding coins and transfers +// them from a module account to the delegator account. It will panic if the +// module account does not exist or is unauthorized. +func (k BaseKeeper) UndelegateCoinsFromModuleToAccount( + ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, +) error { + + acc := k.ak.GetModuleAccount(ctx, senderModule) + if acc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) + } + + if !acc.HasPermission(authtypes.Staking) { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to undelegate coins", senderModule)) + } + + return k.UndelegateCoins(ctx, acc.GetAddress(), recipientAddr, amt) +} + +// MintCoins creates new coins from thin air and adds it to the module account. +// It will panic if the module account does not exist or is unauthorized. +func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { + acc := k.ak.GetModuleAccount(ctx, moduleName) + if acc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleName)) + } + + if !acc.HasPermission(authtypes.Minter) { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to mint tokens", moduleName)) + } + + err := k.AddCoins(ctx, acc.GetAddress(), amt) + if err != nil { + return err + } + + // update total supply + supply := k.GetSupply(ctx) + supply.Inflate(amt) + + k.SetSupply(ctx, supply) + + logger := k.Logger(ctx) + logger.Info("minted coins from module account", "amount", amt.String(), "from", moduleName) + + return nil +} + +// BurnCoins burns coins deletes coins from the balance of the module account. +// It will panic if the module account does not exist or is unauthorized. +func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { + acc := k.ak.GetModuleAccount(ctx, moduleName) + if acc == nil { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleName)) + } + + if !acc.HasPermission(authtypes.Burner) { + panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to burn tokens", moduleName)) + } + + err := k.SubtractCoins(ctx, acc.GetAddress(), amt) + if err != nil { + return err + } + + // update total supply + supply := k.GetSupply(ctx) + supply.Deflate(amt) + k.SetSupply(ctx, supply) + + logger := k.Logger(ctx) + logger.Info("burned tokens from module account", "amount", amt.String(), "from", moduleName) + + return nil +} + +func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, blockTime time.Time, balance, amt sdk.Coins) error { + acc := k.ak.GetAccount(ctx, addr) + if acc == nil { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) + } + + vacc, ok := acc.(vestexported.VestingAccount) + if ok { + // TODO: return error on account.TrackDelegation + vacc.TrackDelegation(blockTime, balance, amt) + } + + return nil +} + +func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { + acc := k.ak.GetAccount(ctx, addr) + if acc == nil { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) + } + + vacc, ok := acc.(vestexported.VestingAccount) + if ok { + // TODO: return error on account.TrackUndelegation + vacc.TrackUndelegation(amt) + } + + return nil +} + +// MarshalSupply protobuf serializes a Supply interface +func (k BaseKeeper) MarshalSupply(supplyI exported.SupplyI) ([]byte, error) { + return k.cdc.MarshalInterface(supplyI) +} + +// UnmarshalSupply returns a Supply interface from raw encoded supply +// bytes of a Proto-based Supply type +func (k BaseKeeper) UnmarshalSupply(bz []byte) (exported.SupplyI, error) { + var evi exported.SupplyI + return evi, k.cdc.UnmarshalInterface(bz, &evi) +} diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go new file mode 100644 index 000000000000..fee8f2a77c85 --- /dev/null +++ b/x/bank/keeper/keeper_test.go @@ -0,0 +1,1078 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +const ( + fooDenom = "foo" + barDenom = "bar" + initialPower = int64(100) + holder = "holder" + multiPerm = "multiple permissions account" + randomPerm = "random permission" +) + +var ( + holderAcc = authtypes.NewEmptyModuleAccount(holder) + burnerAcc = authtypes.NewEmptyModuleAccount(authtypes.Burner, authtypes.Burner) + minterAcc = authtypes.NewEmptyModuleAccount(authtypes.Minter, authtypes.Minter) + multiPermAcc = authtypes.NewEmptyModuleAccount(multiPerm, authtypes.Burner, authtypes.Minter, authtypes.Staking) + randomPermAcc = authtypes.NewEmptyModuleAccount(randomPerm, "random") + + initTokens = sdk.TokensFromConsensusPower(initialPower) + initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) +) + +func newFooCoin(amt int64) sdk.Coin { + return sdk.NewInt64Coin(fooDenom, amt) +} + +func newBarCoin(amt int64) sdk.Coin { + return sdk.NewInt64Coin(barDenom, amt) +} + +// nolint: interfacer +func getCoinsByName(ctx sdk.Context, bk keeper.Keeper, ak types.AccountKeeper, moduleName string) sdk.Coins { + moduleAddress := ak.GetModuleAddress(moduleName) + macc := ak.GetAccount(ctx, moduleAddress) + if macc == nil { + return sdk.Coins(nil) + } + + return bk.GetAllBalances(ctx, macc.GetAddress()) +} + +type IntegrationTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient +} + +func (suite *IntegrationTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + app.BankKeeper.SetParams(ctx, types.DefaultParams()) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.BankKeeper) + queryClient := types.NewQueryClient(queryHelper) + + suite.app = app + suite.ctx = ctx + suite.queryClient = queryClient +} + +func (suite *IntegrationTestSuite) TestSupply() { + app, ctx := suite.app, suite.ctx + + initialPower := int64(100) + initTokens := sdk.TokensFromConsensusPower(initialPower) + + totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) + app.BankKeeper.SetSupply(ctx, types.NewSupply(totalSupply)) + + total := app.BankKeeper.GetSupply(ctx).GetTotal() + suite.Require().Equal(totalSupply, total) +} + +func (suite *IntegrationTestSuite) TestSendCoinsFromModuleToAccount_Blacklist() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) + appCodec := app.AppCodec() + + // add module accounts to supply keeper + 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"} + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + + authKeeper := authkeeper.NewAccountKeeper( + appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + keeper := keeper.NewBaseKeeper( + appCodec, app.GetKey(types.StoreKey), authKeeper, + app.GetSubspace(types.ModuleName), map[string]bool{addr1.String(): true}, + ) + + baseAcc := authKeeper.NewAccountWithAddress(ctx, authtypes.NewModuleAddress("baseAcc")) + suite.Require().NoError(keeper.SetBalances(ctx, holderAcc.GetAddress(), initCoins)) + + keeper.SetSupply(ctx, types.NewSupply(initCoins)) + authKeeper.SetModuleAccount(ctx, holderAcc) + authKeeper.SetAccount(ctx, baseAcc) + + suite.Require().Error(keeper.SendCoinsFromModuleToAccount(ctx, holderAcc.GetName(), addr1, initCoins)) +} + +func (suite *IntegrationTestSuite) TestSupply_SendCoins() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) + appCodec := app.AppCodec() + + // add module accounts to supply keeper + 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"} + + authKeeper := authkeeper.NewAccountKeeper( + appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + keeper := keeper.NewBaseKeeper( + appCodec, app.GetKey(types.StoreKey), authKeeper, + app.GetSubspace(types.ModuleName), make(map[string]bool), + ) + + baseAcc := authKeeper.NewAccountWithAddress(ctx, authtypes.NewModuleAddress("baseAcc")) + suite.Require().NoError(keeper.SetBalances(ctx, holderAcc.GetAddress(), initCoins)) + + keeper.SetSupply(ctx, types.NewSupply(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 + }) + + suite.Require().Panics(func() { + keeper.SendCoinsFromModuleToModule(ctx, authtypes.Burner, "", initCoins) // nolint:errcheck + }) + + suite.Require().Panics(func() { + keeper.SendCoinsFromModuleToAccount(ctx, "", baseAcc.GetAddress(), initCoins) // nolint:errcheck + }) + + suite.Require().Error( + keeper.SendCoinsFromModuleToAccount(ctx, holderAcc.GetName(), baseAcc.GetAddress(), initCoins.Add(initCoins...)), + ) + + suite.Require().NoError( + keeper.SendCoinsFromModuleToModule(ctx, holderAcc.GetName(), authtypes.Burner, initCoins), + ) + suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, holderAcc.GetName())) + suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) + + suite.Require().NoError( + keeper.SendCoinsFromModuleToAccount(ctx, authtypes.Burner, baseAcc.GetAddress(), initCoins), + ) + suite.Require().Equal(sdk.Coins(nil), getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) + suite.Require().Equal(initCoins, keeper.GetAllBalances(ctx, baseAcc.GetAddress())) + + suite.Require().NoError(keeper.SendCoinsFromAccountToModule(ctx, baseAcc.GetAddress(), authtypes.Burner, initCoins)) + suite.Require().Equal(sdk.Coins(nil), keeper.GetAllBalances(ctx, baseAcc.GetAddress())) + suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Burner)) +} + +func (suite *IntegrationTestSuite) TestSupply_MintCoins() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) + appCodec := app.AppCodec() + + // add module accounts to supply keeper + 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"} + + authKeeper := authkeeper.NewAccountKeeper( + appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + keeper := keeper.NewBaseKeeper( + appCodec, app.GetKey(types.StoreKey), authKeeper, + app.GetSubspace(types.ModuleName), make(map[string]bool), + ) + + authKeeper.SetModuleAccount(ctx, burnerAcc) + authKeeper.SetModuleAccount(ctx, minterAcc) + authKeeper.SetModuleAccount(ctx, multiPermAcc) + authKeeper.SetModuleAccount(ctx, randomPermAcc) + + initialSupply := keeper.GetSupply(ctx) + + suite.Require().Panics(func() { keeper.MintCoins(ctx, "", initCoins) }, "no module account") // nolint:errcheck + suite.Require().Panics(func() { keeper.MintCoins(ctx, authtypes.Burner, initCoins) }, "invalid permission") // nolint:errcheck + + err := keeper.MintCoins(ctx, authtypes.Minter, sdk.Coins{sdk.Coin{Denom: "denom", Amount: sdk.NewInt(-10)}}) + suite.Require().Error(err, "insufficient coins") + + suite.Require().Panics(func() { keeper.MintCoins(ctx, randomPerm, initCoins) }) // nolint:errcheck + + err = keeper.MintCoins(ctx, authtypes.Minter, initCoins) + suite.Require().NoError(err) + + suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, authtypes.Minter)) + suite.Require().Equal(initialSupply.GetTotal().Add(initCoins...), keeper.GetSupply(ctx).GetTotal()) + + // test same functionality on module account with multiple permissions + initialSupply = keeper.GetSupply(ctx) + + err = keeper.MintCoins(ctx, multiPermAcc.GetName(), initCoins) + suite.Require().NoError(err) + + suite.Require().Equal(initCoins, getCoinsByName(ctx, keeper, authKeeper, multiPermAcc.GetName())) + suite.Require().Equal(initialSupply.GetTotal().Add(initCoins...), keeper.GetSupply(ctx).GetTotal()) + suite.Require().Panics(func() { keeper.MintCoins(ctx, authtypes.Burner, initCoins) }) // nolint:errcheck +} + +func (suite *IntegrationTestSuite) TestSupply_BurnCoins() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1}) + appCodec, _ := simapp.MakeCodecs() + + // add module accounts to supply keeper + 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"} + + authKeeper := authkeeper.NewAccountKeeper( + appCodec, app.GetKey(types.StoreKey), app.GetSubspace(types.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + keeper := keeper.NewBaseKeeper( + appCodec, app.GetKey(types.StoreKey), authKeeper, + app.GetSubspace(types.ModuleName), make(map[string]bool), + ) + + suite.Require().NoError(keeper.SetBalances(ctx, burnerAcc.GetAddress(), initCoins)) + keeper.SetSupply(ctx, types.NewSupply(initCoins)) + 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().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()) + + // test same functionality on module account with multiple permissions + initialSupply = keeper.GetSupply(ctx) + initialSupply.Inflate(initCoins) + keeper.SetSupply(ctx, initialSupply) + + suite.Require().NoError(keeper.SetBalances(ctx, 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()) +} + +func (suite *IntegrationTestSuite) TestSendCoinsNewAccount() { + app, ctx := suite.app, suite.ctx + balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) + + 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)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + suite.Require().Equal(balances, acc1Balances) + + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + suite.Require().Nil(app.AccountKeeper.GetAccount(ctx, addr2)) + app.BankKeeper.GetAllBalances(ctx, addr2) + suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addr2)) + + sendAmt := sdk.NewCoins(newFooCoin(50), newBarCoin(25)) + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt)) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + suite.Require().Equal(sendAmt, acc2Balances) + suite.Require().NotNil(app.AccountKeeper.GetAccount(ctx, addr2)) +} + +func (suite *IntegrationTestSuite) TestInputOutputNewAccount() { + app, ctx := suite.app, suite.ctx + + balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) + 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)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + suite.Require().Equal(balances, acc1Balances) + + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + suite.Require().Nil(app.AccountKeeper.GetAccount(ctx, addr2)) + suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addr2)) + + inputs := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + } + outputs := []types.Output{ + {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + } + + suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + expected := sdk.NewCoins(newFooCoin(30), newBarCoin(10)) + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + suite.Require().Equal(expected, acc2Balances) + suite.Require().NotNil(app.AccountKeeper.GetAccount(ctx, addr2)) +} + +func (suite *IntegrationTestSuite) TestInputOutputCoins() { + app, ctx := suite.app, suite.ctx + balances := sdk.NewCoins(newFooCoin(90), newBarCoin(30)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + + addr2 := sdk.AccAddress([]byte("addr2_______________")) + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + + addr3 := sdk.AccAddress([]byte("addr3_______________")) + acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) + app.AccountKeeper.SetAccount(ctx, acc3) + + inputs := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + } + outputs := []types.Output{ + {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + {Address: addr3.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, + } + + 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)) + + insufficientInputs := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, + } + insufficientOutputs := []types.Output{ + {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, + {Address: addr3.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, + } + suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, insufficientInputs, insufficientOutputs)) + suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFooCoin(30), newBarCoin(10)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + suite.Require().Equal(expected, acc2Balances) + + acc3Balances := app.BankKeeper.GetAllBalances(ctx, addr3) + suite.Require().Equal(expected, acc3Balances) +} + +func (suite *IntegrationTestSuite) TestSendCoins() { + app, ctx := suite.app, suite.ctx + balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + + addr2 := sdk.AccAddress([]byte("addr2_______________")) + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + suite.Require().NoError(app.BankKeeper.SetBalances(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(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFooCoin(50), newBarCoin(25)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFooCoin(150), newBarCoin(75)) + suite.Require().Equal(expected, acc2Balances) +} + +func (suite *IntegrationTestSuite) TestValidateBalance() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + suite.Require().Error(app.BankKeeper.ValidateBalance(ctx, addr1)) + + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc) + + balances := sdk.NewCoins(newFooCoin(100)) + suite.Require().NoError(app.BankKeeper.SetBalances(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().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 + params := types.DefaultParams() + suite.Require().Equal(enabled, params.DefaultSendEnabled) + + app.BankKeeper.SetParams(ctx, params) + + bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()) + fooCoin := sdk.NewCoin("foocoin", sdk.OneInt()) + barCoin := sdk.NewCoin("barcoin", sdk.OneInt()) + + // assert with default (all denom) send enabled both Bar and Bond Denom are enabled + suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, barCoin)) + suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, bondCoin)) + + // Both coins should be send enabled. + err := app.BankKeeper.SendEnabledCoins(ctx, fooCoin, bondCoin) + suite.Require().NoError(err) + + // Set default send_enabled to !enabled, add a foodenom that overrides default as enabled + params.DefaultSendEnabled = !enabled + params = params.SetSendEnabledParam(fooCoin.Denom, enabled) + app.BankKeeper.SetParams(ctx, params) + + // Expect our specific override to be enabled, others to be !enabled. + suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, fooCoin)) + suite.Require().Equal(!enabled, app.BankKeeper.SendEnabledCoin(ctx, barCoin)) + suite.Require().Equal(!enabled, app.BankKeeper.SendEnabledCoin(ctx, bondCoin)) + + // Foo coin should be send enabled. + err = app.BankKeeper.SendEnabledCoins(ctx, fooCoin) + suite.Require().NoError(err) + + // Expect an error when one coin is not send enabled. + err = app.BankKeeper.SendEnabledCoins(ctx, fooCoin, bondCoin) + suite.Require().Error(err) + + // Expect an error when all coins are not send enabled. + err = app.BankKeeper.SendEnabledCoins(ctx, bondCoin, barCoin) + suite.Require().Error(err) +} + +func (suite *IntegrationTestSuite) TestHasBalance() { + app, ctx := suite.app, suite.ctx + addr := sdk.AccAddress([]byte("addr1_______________")) + + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + app.AccountKeeper.SetAccount(ctx, acc) + + balances := sdk.NewCoins(newFooCoin(100)) + suite.Require().False(app.BankKeeper.HasBalance(ctx, addr, newFooCoin(99))) + + app.BankKeeper.SetBalances(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))) +} + +func (suite *IntegrationTestSuite) TestMsgSendEvents() { + app, ctx := suite.app, suite.ctx + addr := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + + app.AccountKeeper.SetAccount(ctx, acc) + newCoins := sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) + + suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) + + events := ctx.EventManager().ABCIEvents() + suite.Require().Equal(2, len(events)) + + event1 := sdk.Event{ + Type: types.EventTypeTransfer, + Attributes: []abci.EventAttribute{}, + } + event1.Attributes = append( + event1.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr2.String())}, + ) + event1.Attributes = append( + event1.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, + ) + event1.Attributes = append( + event1.Attributes, + abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())}, + ) + event2 := sdk.Event{ + Type: sdk.EventTypeMessage, + Attributes: []abci.EventAttribute{}, + } + event2.Attributes = append( + event2.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, + ) + + 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))) + newCoins = sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) + + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) + + 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]) +} + +func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { + app, ctx := suite.app, suite.ctx + + app.BankKeeper.SetParams(ctx, types.DefaultParams()) + + addr := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addr3 := sdk.AccAddress([]byte("addr3_______________")) + addr4 := sdk.AccAddress([]byte("addr4_______________")) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + + app.AccountKeeper.SetAccount(ctx, acc) + app.AccountKeeper.SetAccount(ctx, acc2) + + newCoins := sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) + newCoins2 := sdk.NewCoins(sdk.NewInt64Coin(barDenom, 100)) + inputs := []types.Input{ + {Address: addr.String(), Coins: newCoins}, + {Address: addr2.String(), Coins: newCoins2}, + } + outputs := []types.Output{ + {Address: addr3.String(), Coins: newCoins}, + {Address: addr4.String(), Coins: newCoins2}, + } + + suite.Require().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + events := ctx.EventManager().ABCIEvents() + 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().Error(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + events = ctx.EventManager().ABCIEvents() + suite.Require().Equal(1, len(events)) + + event1 := sdk.Event{ + Type: sdk.EventTypeMessage, + Attributes: []abci.EventAttribute{}, + } + event1.Attributes = append( + event1.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, + ) + suite.Require().Equal(abci.Event(event1), events[0]) + + // Set addr's coins and addr2's coins + app.BankKeeper.SetBalances(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))) + 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)) + + event2 := sdk.Event{ + Type: sdk.EventTypeMessage, + Attributes: []abci.EventAttribute{}, + } + event2.Attributes = append( + event2.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr2.String())}, + ) + event3 := sdk.Event{ + Type: types.EventTypeTransfer, + Attributes: []abci.EventAttribute{}, + } + event3.Attributes = append( + event3.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr3.String())}, + ) + event3.Attributes = append( + event3.Attributes, + abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())}) + event4 := sdk.Event{ + Type: types.EventTypeTransfer, + Attributes: []abci.EventAttribute{}, + } + event4.Attributes = append( + event4.Attributes, + abci.EventAttribute{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr4.String())}, + ) + event4.Attributes = append( + event4.Attributes, + abci.EventAttribute{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins2.String())}, + ) + + suite.Require().Equal(abci.Event(event1), events[1]) + suite.Require().Equal(abci.Event(event2), events[2]) + suite.Require().Equal(abci.Event(event3), events[3]) + suite.Require().Equal(abci.Event(event4), events[4]) +} + +func (suite *IntegrationTestSuite) TestSpendableCoins() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + delCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addrModule := sdk.AccAddress([]byte("moduleAcc___________")) + + macc := app.AccountKeeper.NewAccountWithAddress(ctx, addrModule) + bacc := authtypes.NewBaseAccountWithAddress(addr1) + vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), endTime.Unix()) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + + 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().Equal(origCoins, app.BankKeeper.SpendableCoins(ctx, addr2)) + + ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) + suite.Require().NoError(app.BankKeeper.DelegateCoins(ctx, addr2, addrModule, delCoins)) + suite.Require().Equal(origCoins.Sub(delCoins), app.BankKeeper.SpendableCoins(ctx, addr1)) +} + +func (suite *IntegrationTestSuite) TestVestingAccountSend() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + bacc := authtypes.NewBaseAccountWithAddress(addr1) + vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, now.Unix(), endTime.Unix()) + + app.AccountKeeper.SetAccount(ctx, vacc) + suite.Require().NoError(app.BankKeeper.SetBalances(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...))) + + // 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)) + suite.Require().Equal(origCoins, app.BankKeeper.GetAllBalances(ctx, addr1)) +} + +func (suite *IntegrationTestSuite) TestPeriodicVestingAccountSend() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + periods := vesting.Periods{ + vesting.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 50)}}, + vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, + vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, + } + + bacc := authtypes.NewBaseAccountWithAddress(addr1) + vacc := vesting.NewPeriodicVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), periods) + + app.AccountKeeper.SetAccount(ctx, vacc) + suite.Require().NoError(app.BankKeeper.SetBalances(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...))) + + // 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)) + suite.Require().Equal(origCoins, app.BankKeeper.GetAllBalances(ctx, addr1)) +} + +func (suite *IntegrationTestSuite) TestVestingAccountReceive() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + bacc := authtypes.NewBaseAccountWithAddress(addr1) + vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), endTime.Unix()) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + + 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)) + + // send some coins to the vesting account + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins)) + + // require the coins are spendable + vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.ContinuousVestingAccount) + balances := app.BankKeeper.GetAllBalances(ctx, addr1) + suite.Require().Equal(origCoins.Add(sendCoins...), balances) + suite.Require().Equal(balances.Sub(vacc.LockedCoins(now)), sendCoins) + + // require coins are spendable plus any that have vested + suite.Require().Equal(balances.Sub(vacc.LockedCoins(now.Add(12*time.Hour))), origCoins) +} + +func (suite *IntegrationTestSuite) TestPeriodicVestingAccountReceive() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + sendCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + + bacc := authtypes.NewBaseAccountWithAddress(addr1) + periods := vesting.Periods{ + vesting.Period{Length: int64(12 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 50)}}, + vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, + vesting.Period{Length: int64(6 * 60 * 60), Amount: sdk.Coins{sdk.NewInt64Coin("stake", 25)}}, + } + + vacc := vesting.NewPeriodicVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), periods) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + + 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)) + + // send some coins to the vesting account + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr2, addr1, sendCoins)) + + // require the coins are spendable + vacc = app.AccountKeeper.GetAccount(ctx, addr1).(*vesting.PeriodicVestingAccount) + balances := app.BankKeeper.GetAllBalances(ctx, addr1) + suite.Require().Equal(origCoins.Add(sendCoins...), balances) + suite.Require().Equal(balances.Sub(vacc.LockedCoins(now)), sendCoins) + + // require coins are spendable plus any that have vested + suite.Require().Equal(balances.Sub(vacc.LockedCoins(now.Add(12*time.Hour))), origCoins) +} + +func (suite *IntegrationTestSuite) TestDelegateCoins() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + delCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addrModule := sdk.AccAddress([]byte("moduleAcc___________")) + + macc := app.AccountKeeper.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + bacc := authtypes.NewBaseAccountWithAddress(addr1) + vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), endTime.Unix()) + + 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)) + + ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) + + // require the ability for a non-vesting account to delegate + suite.Require().NoError(app.BankKeeper.DelegateCoins(ctx, addr2, addrModule, delCoins)) + suite.Require().Equal(origCoins.Sub(delCoins), app.BankKeeper.GetAllBalances(ctx, addr2)) + suite.Require().Equal(delCoins, app.BankKeeper.GetAllBalances(ctx, addrModule)) + + // require the ability for a vesting account to delegate + suite.Require().NoError(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins)) + suite.Require().Equal(delCoins, app.BankKeeper.GetAllBalances(ctx, addr1)) +} + +func (suite *IntegrationTestSuite) TestDelegateCoins_Invalid() { + app, ctx := suite.app, suite.ctx + + origCoins := sdk.NewCoins(newFooCoin(100)) + delCoins := sdk.NewCoins(newFooCoin(50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addrModule := sdk.AccAddress([]byte("moduleAcc___________")) + macc := app.AccountKeeper.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + + suite.Require().Error(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins)) + invalidCoins := sdk.Coins{sdk.Coin{Denom: "fooDenom", Amount: sdk.NewInt(-50)}} + 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...))) +} + +func (suite *IntegrationTestSuite) TestUndelegateCoins() { + app, ctx := suite.app, suite.ctx + now := tmtime.Now() + ctx = ctx.WithBlockHeader(tmproto.Header{Time: now}) + endTime := now.Add(24 * time.Hour) + + origCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + delCoins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addr2 := sdk.AccAddress([]byte("addr2_______________")) + addrModule := sdk.AccAddress([]byte("moduleAcc___________")) + + bacc := authtypes.NewBaseAccountWithAddress(addr1) + macc := app.AccountKeeper.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing + + vacc := vesting.NewContinuousVestingAccount(bacc, origCoins, ctx.BlockHeader().Time.Unix(), endTime.Unix()) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + + 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)) + + ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) + + // require the ability for a non-vesting account to delegate + err := app.BankKeeper.DelegateCoins(ctx, addr2, addrModule, delCoins) + suite.Require().NoError(err) + + suite.Require().Equal(origCoins.Sub(delCoins), app.BankKeeper.GetAllBalances(ctx, addr2)) + suite.Require().Equal(delCoins, app.BankKeeper.GetAllBalances(ctx, addrModule)) + + // require the ability for a non-vesting account to undelegate + suite.Require().NoError(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr2, delCoins)) + + suite.Require().Equal(origCoins, app.BankKeeper.GetAllBalances(ctx, addr2)) + suite.Require().True(app.BankKeeper.GetAllBalances(ctx, addrModule).Empty()) + + // require the ability for a vesting account to delegate + suite.Require().NoError(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins)) + + suite.Require().Equal(origCoins.Sub(delCoins), app.BankKeeper.GetAllBalances(ctx, addr1)) + suite.Require().Equal(delCoins, app.BankKeeper.GetAllBalances(ctx, addrModule)) + + // require the ability for a vesting account to undelegate + suite.Require().NoError(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins)) + + suite.Require().Equal(origCoins, app.BankKeeper.GetAllBalances(ctx, addr1)) + suite.Require().True(app.BankKeeper.GetAllBalances(ctx, addrModule).Empty()) +} + +func (suite *IntegrationTestSuite) TestUndelegateCoins_Invalid() { + app, ctx := suite.app, suite.ctx + + origCoins := sdk.NewCoins(newFooCoin(100)) + delCoins := sdk.NewCoins(newFooCoin(50)) + + addr1 := sdk.AccAddress([]byte("addr1_______________")) + addrModule := sdk.AccAddress([]byte("moduleAcc___________")) + macc := app.AccountKeeper.NewAccountWithAddress(ctx, addrModule) // we don't need to define an actual module account bc we just need the address for testing + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + + 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().Error(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins)) + app.AccountKeeper.SetAccount(ctx, acc) + + suite.Require().Error(app.BankKeeper.UndelegateCoins(ctx, addrModule, addr1, delCoins)) +} + +func (suite *IntegrationTestSuite) TestSetDenomMetaData() { + app, ctx := suite.app, suite.ctx + + metadata := suite.getTestMetadata() + + for i := range []int{1, 2} { + app.BankKeeper.SetDenomMetaData(ctx, metadata[i]) + } + + actualMetadata := app.BankKeeper.GetDenomMetaData(ctx, metadata[1].Base) + suite.Require().Equal(metadata[1].GetBase(), actualMetadata.GetBase()) + suite.Require().Equal(metadata[1].GetDisplay(), actualMetadata.GetDisplay()) + suite.Require().Equal(metadata[1].GetDescription(), actualMetadata.GetDescription()) + suite.Require().Equal(metadata[1].GetDenomUnits()[1].GetDenom(), actualMetadata.GetDenomUnits()[1].GetDenom()) + suite.Require().Equal(metadata[1].GetDenomUnits()[1].GetExponent(), actualMetadata.GetDenomUnits()[1].GetExponent()) + suite.Require().Equal(metadata[1].GetDenomUnits()[1].GetAliases(), actualMetadata.GetDenomUnits()[1].GetAliases()) +} + +func (suite *IntegrationTestSuite) TestIterateAllDenomMetaData() { + app, ctx := suite.app, suite.ctx + + expectedMetadata := suite.getTestMetadata() + // set metadata + for i := range []int{1, 2} { + app.BankKeeper.SetDenomMetaData(ctx, expectedMetadata[i]) + } + // retrieve metadata + actualMetadata := make([]types.Metadata, 0) + app.BankKeeper.IterateAllDenomMetaData(ctx, func(metadata types.Metadata) bool { + actualMetadata = append(actualMetadata, metadata) + return false + }) + // execute checks + for i := range []int{1, 2} { + suite.Require().Equal(expectedMetadata[i].GetBase(), actualMetadata[i].GetBase()) + suite.Require().Equal(expectedMetadata[i].GetDisplay(), actualMetadata[i].GetDisplay()) + suite.Require().Equal(expectedMetadata[i].GetDescription(), actualMetadata[i].GetDescription()) + suite.Require().Equal(expectedMetadata[i].GetDenomUnits()[1].GetDenom(), actualMetadata[i].GetDenomUnits()[1].GetDenom()) + suite.Require().Equal(expectedMetadata[i].GetDenomUnits()[1].GetExponent(), actualMetadata[i].GetDenomUnits()[1].GetExponent()) + suite.Require().Equal(expectedMetadata[i].GetDenomUnits()[1].GetAliases(), actualMetadata[i].GetDenomUnits()[1].GetAliases()) + } +} + +func (suite *IntegrationTestSuite) getTestMetadata() []types.Metadata { + return []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", + }, + { + Description: "The native staking token of the Token Hub.", + DenomUnits: []*types.DenomUnit{ + {"1token", uint32(5), []string{"decitoken"}}, + {"2token", uint32(4), []string{"centitoken"}}, + {"3token", uint32(7), []string{"dekatoken"}}, + }, + Base: "utoken", + Display: "token", + }, + } +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/bank/keeper/msg_server.go b/x/bank/keeper/msg_server.go new file mode 100644 index 000000000000..b318db2cc461 --- /dev/null +++ b/x/bank/keeper/msg_server.go @@ -0,0 +1,106 @@ +package keeper + +import ( + "context" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the bank MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (k msgServer) Send(goCtx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := k.SendEnabledCoins(ctx, msg.Amount...); err != nil { + return nil, err + } + + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return nil, err + } + to, err := sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + return nil, err + } + + if k.BlockedAddr(to) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress) + } + + err = k.SendCoins(ctx, from, to, msg.Amount) + if err != nil { + return nil, err + } + + defer func() { + for _, a := range msg.Amount { + if a.Amount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "send"}, + float32(a.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", a.Denom)}, + ) + } + } + }() + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + ) + + return &types.MsgSendResponse{}, nil +} + +func (k msgServer) MultiSend(goCtx context.Context, msg *types.MsgMultiSend) (*types.MsgMultiSendResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // NOTE: totalIn == totalOut should already have been checked + for _, in := range msg.Inputs { + if err := k.SendEnabledCoins(ctx, in.Coins...); err != nil { + return nil, err + } + } + + for _, out := range msg.Outputs { + accAddr, err := sdk.AccAddressFromBech32(out.Address) + if err != nil { + panic(err) + } + if k.BlockedAddr(accAddr) { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive transactions", out.Address) + } + } + + err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + ) + + return &types.MsgMultiSendResponse{}, nil +} diff --git a/x/bank/keeper/querier.go b/x/bank/keeper/querier.go new file mode 100644 index 000000000000..b4a6148c7f55 --- /dev/null +++ b/x/bank/keeper/querier.go @@ -0,0 +1,121 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "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/bank/types" +) + +// NewQuerier returns a new sdk.Keeper instance. +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + case types.QueryBalance: + return queryBalance(ctx, req, k, legacyQuerierCdc) + + case types.QueryAllBalances: + return queryAllBalance(ctx, req, k, legacyQuerierCdc) + + case types.QueryTotalSupply: + return queryTotalSupply(ctx, req, k, legacyQuerierCdc) + + case types.QuerySupplyOf: + return querySupplyOf(ctx, req, k, legacyQuerierCdc) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + } +} + +func queryBalance(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryBalanceRequest + + if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + address, err := sdk.AccAddressFromBech32(params.Address) + if err != nil { + return nil, err + } + + balance := k.GetBalance(ctx, address, params.Denom) + + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, balance) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryAllBalance(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryAllBalancesRequest + + if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + address, err := sdk.AccAddressFromBech32(params.Address) + if err != nil { + return nil, err + } + + balances := k.GetAllBalances(ctx, address) + + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, balances) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryTotalSupply(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryTotalSupplyParams + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + totalSupply := k.GetSupply(ctx).GetTotal() + + start, end := client.Paginate(len(totalSupply), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + totalSupply = sdk.Coins{} + } else { + totalSupply = totalSupply[start:end] + } + + res, err := legacyQuerierCdc.MarshalJSON(totalSupply) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QuerySupplyOfParams + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + amount := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom) + supply := sdk.NewCoin(params.Denom, amount) + + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, supply) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} diff --git a/x/bank/keeper/querier_test.go b/x/bank/keeper/querier_test.go new file mode 100644 index 000000000000..84ec3d095799 --- /dev/null +++ b/x/bank/keeper/querier_test.go @@ -0,0 +1,155 @@ +package keeper_test + +import ( + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func (suite *IntegrationTestSuite) TestQuerier_QueryBalance() { + app, ctx := suite.app, suite.ctx + legacyAmino := app.LegacyAmino() + _, _, addr := testdata.KeyTestPubAddr() + req := abci.RequestQuery{ + Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryBalance), + Data: []byte{}, + } + + querier := keeper.NewQuerier(app.BankKeeper, legacyAmino) + + res, err := querier(ctx, []string{types.QueryBalance}, req) + suite.Require().NotNil(err) + suite.Require().Nil(res) + + req.Data = legacyAmino.MustMarshalJSON(types.NewQueryBalanceRequest(addr, fooDenom)) + res, err = querier(ctx, []string{types.QueryBalance}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + var balance sdk.Coin + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &balance)) + suite.True(balance.IsZero()) + + origCoins := sdk.NewCoins(newFooCoin(50), newBarCoin(30)) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + + app.AccountKeeper.SetAccount(ctx, acc) + suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + + res, err = querier(ctx, []string{types.QueryBalance}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &balance)) + suite.True(balance.IsEqual(newFooCoin(50))) +} + +func (suite *IntegrationTestSuite) TestQuerier_QueryAllBalances() { + app, ctx := suite.app, suite.ctx + legacyAmino := app.LegacyAmino() + _, _, addr := testdata.KeyTestPubAddr() + req := abci.RequestQuery{ + Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryAllBalances), + Data: []byte{}, + } + + querier := keeper.NewQuerier(app.BankKeeper, legacyAmino) + + res, err := querier(ctx, []string{types.QueryAllBalances}, req) + suite.Require().NotNil(err) + suite.Require().Nil(res) + + req.Data = legacyAmino.MustMarshalJSON(types.NewQueryAllBalancesRequest(addr, nil)) + res, err = querier(ctx, []string{types.QueryAllBalances}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + var balances sdk.Coins + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &balances)) + suite.True(balances.IsZero()) + + origCoins := sdk.NewCoins(newFooCoin(50), newBarCoin(30)) + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + + app.AccountKeeper.SetAccount(ctx, acc) + suite.Require().NoError(app.BankKeeper.SetBalances(ctx, acc.GetAddress(), origCoins)) + + res, err = querier(ctx, []string{types.QueryAllBalances}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &balances)) + suite.True(balances.IsEqual(origCoins)) +} + +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) + + req := abci.RequestQuery{ + Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryTotalSupply), + Data: []byte{}, + } + + querier := keeper.NewQuerier(app.BankKeeper, legacyAmino) + + res, err := querier(ctx, []string{types.QueryTotalSupply}, req) + suite.Require().NotNil(err) + suite.Require().Nil(res) + + req.Data = legacyAmino.MustMarshalJSON(types.NewQueryTotalSupplyParams(1, 100)) + res, err = querier(ctx, []string{types.QueryTotalSupply}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + var resp sdk.Coins + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &resp)) + suite.Require().Equal(expectedTotalSupply.Total, resp) +} + +func (suite *IntegrationTestSuite) TestQuerier_QueryTotalSupplyOf() { + app, ctx := suite.app, suite.ctx + legacyAmino := app.LegacyAmino() + test1Supply := sdk.NewInt64Coin("test1", 4000000) + test2Supply := sdk.NewInt64Coin("test2", 700000000) + expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply)) + app.BankKeeper.SetSupply(ctx, expectedTotalSupply) + + req := abci.RequestQuery{ + Path: fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QuerySupplyOf), + Data: []byte{}, + } + + querier := keeper.NewQuerier(app.BankKeeper, legacyAmino) + + res, err := querier(ctx, []string{types.QuerySupplyOf}, req) + suite.Require().NotNil(err) + suite.Require().Nil(res) + + req.Data = legacyAmino.MustMarshalJSON(types.NewQuerySupplyOfParams(test1Supply.Denom)) + res, err = querier(ctx, []string{types.QuerySupplyOf}, req) + suite.Require().NoError(err) + suite.Require().NotNil(res) + + var resp sdk.Coin + suite.Require().NoError(legacyAmino.UnmarshalJSON(res, &resp)) + suite.Require().Equal(test1Supply, resp) +} + +func (suite *IntegrationTestSuite) TestQuerierRouteNotFound() { + app, ctx := suite.app, suite.ctx + legacyAmino := app.LegacyAmino() + req := abci.RequestQuery{ + Path: fmt.Sprintf("custom/%s/invalid", types.ModuleName), + Data: []byte{}, + } + + querier := keeper.NewQuerier(app.BankKeeper, legacyAmino) + _, err := querier(ctx, []string{"invalid"}, req) + suite.Error(err) +} diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go new file mode 100644 index 000000000000..c1000b7d3025 --- /dev/null +++ b/x/bank/keeper/send.go @@ -0,0 +1,298 @@ +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" + "github.com/cosmos/cosmos-sdk/x/bank/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// SendKeeper defines a module interface that facilitates the transfer of coins +// between accounts without the possibility of creating coins. +type SendKeeper interface { + ViewKeeper + + 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) + + SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool + SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + + BlockedAddr(addr sdk.AccAddress) bool +} + +var _ SendKeeper = (*BaseSendKeeper)(nil) + +// BaseSendKeeper only allows transfers between accounts without the possibility of +// creating coins. It implements the SendKeeper interface. +type BaseSendKeeper struct { + BaseViewKeeper + + cdc codec.BinaryMarshaler + ak types.AccountKeeper + storeKey sdk.StoreKey + paramSpace paramtypes.Subspace + + // list of addresses that are restricted from receiving transactions + blockedAddrs map[string]bool +} + +func NewBaseSendKeeper( + cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper, paramSpace paramtypes.Subspace, blockedAddrs map[string]bool, +) BaseSendKeeper { + + return BaseSendKeeper{ + BaseViewKeeper: NewBaseViewKeeper(cdc, storeKey, ak), + cdc: cdc, + ak: ak, + storeKey: storeKey, + paramSpace: paramSpace, + blockedAddrs: blockedAddrs, + } +} + +// GetParams returns the total set of bank parameters. +func (k BaseSendKeeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of bank parameters. +func (k BaseSendKeeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} + +// InputOutputCoins performs multi-send functionality. It accepts a series of +// inputs that correspond to a series of outputs. It returns an error if the +// inputs and outputs don't lineup or if any single transfer of tokens fails. +func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error { + // Safety check ensuring that when sending coins the keeper must maintain the + // Check supply invariant and validity of Coins. + if err := types.ValidateInputsOutputs(inputs, outputs); err != nil { + return err + } + + for _, in := range inputs { + inAddress, err := sdk.AccAddressFromBech32(in.Address) + if err != nil { + return err + } + + err = k.SubtractCoins(ctx, inAddress, in.Coins) + if err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(types.AttributeKeySender, in.Address), + ), + ) + } + + for _, out := range outputs { + outAddress, err := sdk.AccAddressFromBech32(out.Address) + if err != nil { + return err + } + err = k.AddCoins(ctx, outAddress, out.Coins) + if err != nil { + return err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeyRecipient, out.Address), + sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()), + ), + ) + + // Create account if recipient does not exist. + // + // NOTE: This should ultimately be removed in favor a more flexible approach + // such as delegated fee messages. + acc := k.ak.GetAccount(ctx, outAddress) + if acc == nil { + defer telemetry.IncrCounter(1, "new", "account") + k.ak.SetAccount(ctx, k.ak.NewAccountWithAddress(ctx, outAddress)) + } + } + + return nil +} + +// SendCoins transfers amt coins from a sending account to a receiving account. +// An error is returned upon failure. +func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()), + sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), + ), + }) + + err := k.SubtractCoins(ctx, fromAddr, amt) + if err != nil { + return err + } + + err = k.AddCoins(ctx, toAddr, amt) + if err != nil { + return err + } + + // Create account if recipient does not exist. + // + // NOTE: This should ultimately be removed in favor a more flexible approach + // such as delegated fee messages. + acc := k.ak.GetAccount(ctx, toAddr) + if acc == nil { + defer telemetry.IncrCounter(1, "new", "account") + k.ak.SetAccount(ctx, k.ak.NewAccountWithAddress(ctx, toAddr)) + } + + return nil +} + +// 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 { + if !amt.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) + } + + lockedCoins := k.LockedCoins(ctx, addr) + + for _, coin := range amt { + balance := k.GetBalance(ctx, addr, coin.Denom) + locked := sdk.NewCoin(coin.Denom, lockedCoins.AmountOf(coin.Denom)) + spendable := balance.Sub(locked) + + _, hasNeg := sdk.Coins{spendable}.SafeSub(sdk.Coins{coin}) + if hasNeg { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "%s is smaller than %s", spendable, coin) + } + + newBalance := balance.Sub(coin) + + err := k.SetBalance(ctx, addr, newBalance) + if err != nil { + return err + } + } + + return nil +} + +// AddCoins adds amt to the account balance given by the provided address. An +// error is returned if the initial amount is invalid or if any resulting new +// balance is negative. +func (k BaseSendKeeper) AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { + if !amt.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, amt.String()) + } + + for _, coin := range amt { + balance := k.GetBalance(ctx, addr, coin.Denom) + newBalance := balance.Add(coin) + + err := k.SetBalance(ctx, addr, newBalance) + if err != nil { + return err + } + } + + return nil +} + +// ClearBalances removes all balances for a given account by address. +func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) { + keys := [][]byte{} + k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool { + keys = append(keys, []byte(balance.Denom)) + return false + }) + + store := ctx.KVStore(k.storeKey) + balancesStore := prefix.NewStore(store, types.BalancesPrefix) + accountStore := prefix.NewStore(balancesStore, addr.Bytes()) + + for _, key := range keys { + accountStore.Delete(key) + } +} + +// SetBalances sets the balance (multiple coins) for an account by address. It will +// clear out all balances prior to setting the new coins as to set existing balances +// to zero if they don't exist in amt. An error is returned upon failure. +func (k BaseSendKeeper) SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error { + k.ClearBalances(ctx, addr) + + for _, balance := range balances { + err := k.SetBalance(ctx, addr, balance) + if err != nil { + return err + } + } + + 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 { + 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()) + + bz := k.cdc.MustMarshalBinaryBare(&balance) + accountStore.Set([]byte(balance.Denom), bz) + + return nil +} + +// SendEnabledCoins checks the coins provide and returns an ErrSendDisabled if +// any of the coins are not configured for sending. Returns nil if sending is enabled +// for all provided coin +func (k BaseSendKeeper) SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error { + for _, coin := range coins { + if !k.SendEnabledCoin(ctx, coin) { + return sdkerrors.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", coin.Denom) + } + } + return nil +} + +// SendEnabledCoin returns the current SendEnabled status of the provided coin's denom +func (k BaseSendKeeper) SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool { + return k.GetParams(ctx).SendEnabledDenom(coin.Denom) +} + +// BlockedAddr checks if a given address is restricted from +// receiving funds. +func (k BaseSendKeeper) BlockedAddr(addr sdk.AccAddress) bool { + return k.blockedAddrs[addr.String()] +} diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go new file mode 100644 index 000000000000..d4bcabad2b62 --- /dev/null +++ b/x/bank/keeper/view.go @@ -0,0 +1,216 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +var _ ViewKeeper = (*BaseViewKeeper)(nil) + +// ViewKeeper defines a module interface that facilitates read only access to +// account balances. +type ViewKeeper interface { + ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error + HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool + + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + GetAccountsBalances(ctx sdk.Context) []types.Balance + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin + LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + + IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) + IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) +} + +// BaseViewKeeper implements a read only keeper implementation of ViewKeeper. +type BaseViewKeeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + ak types.AccountKeeper +} + +// NewBaseViewKeeper returns a new BaseViewKeeper. +func NewBaseViewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper) BaseViewKeeper { + return BaseViewKeeper{ + cdc: cdc, + storeKey: storeKey, + ak: ak, + } +} + +// Logger returns a module-specific logger. +func (k BaseViewKeeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// HasBalance returns whether or not an account has at least amt balance. +func (k BaseViewKeeper) HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool { + return k.GetBalance(ctx, addr, amt.Denom).IsGTE(amt) +} + +// GetAllBalances returns all the account balances for the given account address. +func (k BaseViewKeeper) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { + balances := sdk.NewCoins() + k.IterateAccountBalances(ctx, addr, func(balance sdk.Coin) bool { + balances = balances.Add(balance) + return false + }) + + return balances.Sort() +} + +// GetAccountsBalances returns all the accounts balances from the store. +func (k BaseViewKeeper) GetAccountsBalances(ctx sdk.Context) []types.Balance { + balances := make([]types.Balance, 0) + mapAddressToBalancesIdx := make(map[string]int) + + k.IterateAllBalances(ctx, func(addr sdk.AccAddress, balance sdk.Coin) bool { + idx, ok := mapAddressToBalancesIdx[addr.String()] + if ok { + // address is already on the set of accounts balances + balances[idx].Coins = balances[idx].Coins.Add(balance) + balances[idx].Coins.Sort() + return false + } + + accountBalance := types.Balance{ + Address: addr.String(), + Coins: sdk.NewCoins(balance), + } + balances = append(balances, accountBalance) + mapAddressToBalancesIdx[addr.String()] = len(balances) - 1 + return false + }) + + return balances +} + +// 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()) + + bz := accountStore.Get([]byte(denom)) + if bz == nil { + return sdk.NewCoin(denom, sdk.ZeroInt()) + } + + var balance sdk.Coin + k.cdc.MustUnmarshalBinaryBare(bz, &balance) + + return balance +} + +// IterateAccountBalances iterates over the balances of a single account and +// 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()) + + iterator := accountStore.Iterator(nil, nil) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + var balance sdk.Coin + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance) + + if cb(balance) { + break + } + } +} + +// IterateAllBalances iterates over all the balances of all accounts and +// denominations that are provided to a callback. If true is returned from the +// callback, iteration is halted. +func (k BaseViewKeeper) IterateAllBalances(ctx sdk.Context, cb func(sdk.AccAddress, sdk.Coin) bool) { + store := ctx.KVStore(k.storeKey) + balancesStore := prefix.NewStore(store, types.BalancesPrefix) + + iterator := balancesStore.Iterator(nil, nil) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + address := types.AddressFromBalancesStore(iterator.Key()) + + var balance sdk.Coin + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance) + + if cb(address, balance) { + break + } + } +} + +// LockedCoins returns all the coins that are not spendable (i.e. locked) for an +// account by address. For standard accounts, the result will always be no coins. +// For vesting accounts, LockedCoins is delegated to the concrete vesting account +// type. +func (k BaseViewKeeper) LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { + acc := k.ak.GetAccount(ctx, addr) + if acc != nil { + vacc, ok := acc.(vestexported.VestingAccount) + if ok { + return vacc.LockedCoins(ctx.BlockTime()) + } + } + + return sdk.NewCoins() +} + +// SpendableCoins returns the total balances of spendable coins for an account +// by address. If the account has no spendable coins, an empty Coins slice is +// returned. +func (k BaseViewKeeper) SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins { + balances := k.GetAllBalances(ctx, addr) + locked := k.LockedCoins(ctx, addr) + + spendable, hasNeg := balances.SafeSub(locked) + if hasNeg { + return sdk.NewCoins() + } + + return spendable +} + +// ValidateBalance validates all balances for a given account address returning +// an error if any balance is invalid. It will check for vesting account types +// and validate the balances against the original vesting balances. +// +// CONTRACT: ValidateBalance should only be called upon genesis state. In the +// case of vesting accounts, balances may change in a valid manner that would +// otherwise yield an error from this call. +func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error { + acc := k.ak.GetAccount(ctx, addr) + if acc == nil { + return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr) + } + + balances := k.GetAllBalances(ctx, addr) + if !balances.IsValid() { + return fmt.Errorf("account balance of %s is invalid", balances) + } + + vacc, ok := acc.(vestexported.VestingAccount) + if ok { + ogv := vacc.GetOriginalVesting() + if ogv.IsAnyGT(balances) { + return fmt.Errorf("vesting amount %s cannot be greater than total amount %s", ogv, balances) + } + } + + return nil +} diff --git a/x/bank/legacy/v036/types.go b/x/bank/legacy/v036/types.go new file mode 100644 index 000000000000..1de8ffb9b71f --- /dev/null +++ b/x/bank/legacy/v036/types.go @@ -0,0 +1,21 @@ +// DONTCOVER +// nolint +package v036 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ModuleName = "supply" + +type ( + GenesisState struct { + Supply sdk.Coins `json:"supply" yaml:"supply"` + } +) + +func EmptyGenesisState() GenesisState { + return GenesisState{ + Supply: sdk.NewCoins(), // leave this empty as it's filled on initialization + } +} diff --git a/x/bank/legacy/v038/types.go b/x/bank/legacy/v038/types.go new file mode 100644 index 000000000000..e67640bf2b12 --- /dev/null +++ b/x/bank/legacy/v038/types.go @@ -0,0 +1,14 @@ +package v038 + +// DONTCOVER +// nolint + +const ( + ModuleName = "bank" +) + +type ( + GenesisState struct { + SendEnabled bool `json:"send_enabled" yaml:"send_enabled"` + } +) diff --git a/x/bank/legacy/v040/migrate.go b/x/bank/legacy/v040/migrate.go new file mode 100644 index 000000000000..ab2e596f57c8 --- /dev/null +++ b/x/bank/legacy/v040/migrate.go @@ -0,0 +1,38 @@ +package v040 + +import ( + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v036supply "github.com/cosmos/cosmos-sdk/x/bank/legacy/v036" + v038bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v038" + v040bank "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// Migrate accepts exported v0.39 x/auth and v0.38 x/bank genesis state and +// migrates it to v0.40 x/bank genesis state. The migration includes: +// +// - Moving balances from x/auth to x/bank genesis state. +// - Moving supply from x/supply to x/bank genesis state. +// - Re-encode in v0.40 GenesisState. +func Migrate( + bankGenState v038bank.GenesisState, + authGenState v039auth.GenesisState, + supplyGenState v036supply.GenesisState, +) *v040bank.GenesisState { + balances := make([]v040bank.Balance, len(authGenState.Accounts)) + for i, acc := range authGenState.Accounts { + balances[i] = v040bank.Balance{ + Address: acc.GetAddress().String(), + Coins: acc.GetCoins(), + } + } + + return &v040bank.GenesisState{ + Params: v040bank.Params{ + SendEnabled: []*v040bank.SendEnabled{}, + DefaultSendEnabled: bankGenState.SendEnabled, + }, + Balances: balances, + Supply: supplyGenState.Supply, + DenomMetadata: []v040bank.Metadata{}, + } +} diff --git a/x/bank/legacy/v040/migrate_test.go b/x/bank/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..9b458e120992 --- /dev/null +++ b/x/bank/legacy/v040/migrate_test.go @@ -0,0 +1,56 @@ +package v040_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v036supply "github.com/cosmos/cosmos-sdk/x/bank/legacy/v036" + v038bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v038" + v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + coins := sdk.NewCoins(sdk.NewInt64Coin("stake", 50)) + addr1, _ := sdk.AccAddressFromBech32("cosmos1xxkueklal9vejv9unqu80w9vptyepfa95pd53u") + acc1 := v038auth.NewBaseAccount(addr1, coins, nil, 1, 0) + + addr2, _ := sdk.AccAddressFromBech32("cosmos15v50ymp6n5dn73erkqtmq0u8adpl8d3ujv2e74") + vaac := v038auth.NewContinuousVestingAccountRaw( + v038auth.NewBaseVestingAccount( + v038auth.NewBaseAccount(addr2, coins, nil, 1, 0), coins, nil, nil, 3160620846, + ), + 1580309972, + ) + + supply := sdk.NewCoins(sdk.NewInt64Coin("stake", 1000)) + + bankGenState := v038bank.GenesisState{ + SendEnabled: true, + } + authGenState := v039auth.GenesisState{ + Accounts: v038auth.GenesisAccounts{acc1, vaac}, + } + supplyGenState := v036supply.GenesisState{ + Supply: supply, + } + + migrated := v040bank.Migrate(bankGenState, authGenState, supplyGenState) + expected := `{"params":{"send_enabled":[],"default_send_enabled":true},"balances":[{"address":"cosmos1xxkueklal9vejv9unqu80w9vptyepfa95pd53u","coins":[{"denom":"stake","amount":"50"}]},{"address":"cosmos15v50ymp6n5dn73erkqtmq0u8adpl8d3ujv2e74","coins":[{"denom":"stake","amount":"50"}]}],"supply":[{"denom":"stake","amount":"1000"}],"denom_metadata":[]}` + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + require.Equal(t, expected, string(bz)) +} diff --git a/x/bank/legacy/v040/types.go b/x/bank/legacy/v040/types.go new file mode 100644 index 000000000000..04ac05f9fcd3 --- /dev/null +++ b/x/bank/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "bank" +) diff --git a/x/bank/module.go b/x/bank/module.go index 114962382758..f271fa21975c 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -1,25 +1,29 @@ package bank import ( + "context" "encoding/json" "fmt" "math/rand" + "time" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/bank/client/cli" "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - "github.com/cosmos/cosmos-sdk/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/keeper" "github.com/cosmos/cosmos-sdk/x/bank/simulation" - sim "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) var ( @@ -29,42 +33,58 @@ var ( ) // AppModuleBasic defines the basic application module used by the bank module. -type AppModuleBasic struct{} +type AppModuleBasic struct { + cdc codec.Marshaler +} // Name returns the bank module's name. -func (AppModuleBasic) Name() string { return ModuleName } +func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterCodec registers the bank module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) } +// RegisterLegacyAminoCodec registers the bank module's types on the LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} // DefaultGenesis returns default genesis state as raw bytes for the bank // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the bank module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, _ client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return data.Validate() } // RegisterRESTRoutes registers the REST routes for the bank module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { + rest.RegisterHandlers(clientCtx, rtr) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the bank module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) } // GetTxCmd returns the root tx command for the bank module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(cdc) +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() } // GetQueryCmd returns no root query command for the bank module. -func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers interfaces and implementations of the bank module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} //____________________________________________________________________________ @@ -72,55 +92,63 @@ func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper accountKeeper types.AccountKeeper } +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper) AppModule { +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, accountKeeper types.AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, accountKeeper: accountKeeper, } } // Name returns the bank module's name. -func (AppModule) Name() string { return ModuleName } +func (AppModule) Name() string { return types.ModuleName } // RegisterInvariants registers the bank module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - keeper.RegisterInvariants(ir, am.accountKeeper) + keeper.RegisterInvariants(ir, am.keeper) } // Route returns the message routing key for the bank module. -func (AppModule) Route() string { return RouterKey } - -// NewHandler returns an sdk.Handler for the bank module. -func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) +} // QuerierRoute returns the bank module's querier route name. -func (AppModule) QuerierRoute() string { return RouterKey } +func (AppModule) QuerierRoute() string { return types.RouterKey } -// NewQuerierHandler returns the bank module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return keeper.NewQuerier(am.keeper) +// LegacyQuerierHandler returns the bank module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } // InitGenesis performs genesis initialization for the bank module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, genesisState) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + start := time.Now() + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + telemetry.MeasureSince(start, "InitGenesis", "crisis", "unmarshal") + + am.keeper.InitGenesis(ctx, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the bank // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) } // BeginBlock performs a no-op. @@ -142,20 +170,22 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { return nil } // RandomizedParams creates randomized bank param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } -// RegisterStoreDecoder performs a no-op. -func (AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} +// RegisterStoreDecoder registers a decoder for supply module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.keeper) +} // WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { return simulation.WeightedOperations( simState.AppParams, simState.Cdc, am.accountKeeper, am.keeper, ) diff --git a/x/bank/simulation/decoder.go b/x/bank/simulation/decoder.go new file mode 100644 index 000000000000..be885eac6ec9 --- /dev/null +++ b/x/bank/simulation/decoder.go @@ -0,0 +1,39 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/bank/exported" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// SupplyUnmarshaler defines the expected encoding store functions. +type SupplyUnmarshaler interface { + UnmarshalSupply([]byte) (exported.SupplyI, error) +} + +// NewDecodeStore returns a function closure that unmarshals the KVPair's values +// to the corresponding types. +func NewDecodeStore(cdc SupplyUnmarshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.SupplyKey): + supplyA, err := cdc.UnmarshalSupply(kvA.Value) + if err != nil { + panic(err) + } + + supplyB, err := cdc.UnmarshalSupply(kvB.Value) + if err != nil { + panic(err) + } + + return fmt.Sprintf("%v\n%v", supplyA, supplyB) + + default: + panic(fmt.Sprintf("unexpected %s key %X (%s)", types.ModuleName, kvA.Key, kvA.Key)) + } + } +} diff --git a/x/bank/simulation/decoder_test.go b/x/bank/simulation/decoder_test.go new file mode 100644 index 000000000000..82ab32c1497b --- /dev/null +++ b/x/bank/simulation/decoder_test.go @@ -0,0 +1,51 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "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/bank/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + dec := simulation.NewDecodeStore(app.BankKeeper) + + totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000))) + + supplyBz, err := app.BankKeeper.MarshalSupply(totalSupply) + require.NoError(t, err) + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: types.SupplyKey, Value: supplyBz}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, + } + + tests := []struct { + name string + expectedLog string + }{ + {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)}, + {"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/bank/simulation/genesis.go b/x/bank/simulation/genesis.go index 2dae431c9864..9031d0336451 100644 --- a/x/bank/simulation/genesis.go +++ b/x/bank/simulation/genesis.go @@ -3,34 +3,84 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// Simulation parameter constants -const ( - SendEnabled = "send_enabled" -) +// RandomGenesisDefaultSendParam computes randomized allow all send transfers param for the bank module +func RandomGenesisDefaultSendParam(r *rand.Rand) bool { + // 90% chance of transfers being enable or P(a) = 0.9 for success + return r.Int63n(101) <= 90 +} + +// RandomGenesisSendParams randomized Parameters for the bank module +func RandomGenesisSendParams(r *rand.Rand) types.SendEnabledParams { + params := types.DefaultParams() + // 90% chance of transfers being DefaultSendEnabled=true or P(a) = 0.9 for success + // 50% of the time add an additional denom specific record (P(b) = 0.475 = 0.5 * 0.95) + if r.Int63n(101) <= 50 { + // set send enabled 95% of the time + bondEnabled := r.Int63n(101) <= 95 + params = params.SetSendEnabledParam( + sdk.DefaultBondDenom, + bondEnabled) + } -// GenSendEnabled randomized SendEnabled -func GenSendEnabled(r *rand.Rand) bool { - return r.Int63n(101) <= 95 // 95% chance of transfers being enabled + // overall probability of enabled for bond denom is 94.75% (P(a)+P(b) - P(a)*P(b)) + return params.SendEnabled +} + +// RandomGenesisBalances returns a slice of account balances. Each account has +// a balance of simState.InitialStake for sdk.DefaultBondDenom. +func RandomGenesisBalances(simState *module.SimulationState) []types.Balance { + genesisBalances := []types.Balance{} + + for _, acc := range simState.Accounts { + genesisBalances = append(genesisBalances, types.Balance{ + Address: acc.Address.String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))), + }) + } + + return genesisBalances } // RandomizedGenState generates a random GenesisState for bank func RandomizedGenState(simState *module.SimulationState) { - var sendEnabled bool + var sendEnabledParams types.SendEnabledParams simState.AppParams.GetOrGenerate( - simState.Cdc, SendEnabled, &sendEnabled, simState.Rand, - func(r *rand.Rand) { sendEnabled = GenSendEnabled(r) }, + simState.Cdc, string(types.KeySendEnabled), &sendEnabledParams, simState.Rand, + func(r *rand.Rand) { sendEnabledParams = RandomGenesisSendParams(r) }, ) - bankGenesis := types.NewGenesisState(sendEnabled) + var defaultSendEnabledParam bool + simState.AppParams.GetOrGenerate( + simState.Cdc, string(types.KeyDefaultSendEnabled), &defaultSendEnabledParam, simState.Rand, + func(r *rand.Rand) { defaultSendEnabledParam = RandomGenesisDefaultSendParam(r) }, + ) + + numAccs := int64(len(simState.Accounts)) + totalSupply := sdk.NewInt(simState.InitialStake * (numAccs + simState.NumBonded)) + supply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply)) + + bankGenesis := types.GenesisState{ + Params: types.Params{ + SendEnabled: sendEnabledParams, + DefaultSendEnabled: defaultSendEnabledParam, + }, + Balances: RandomGenesisBalances(simState), + Supply: supply, + } - fmt.Printf("Selected randomly generated bank parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, bankGenesis)) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(bankGenesis) + paramsBytes, err := json.MarshalIndent(&bankGenesis.Params, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated bank parameters:\n%s\n", paramsBytes) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&bankGenesis) } diff --git a/x/bank/simulation/genesis_test.go b/x/bank/simulation/genesis_test.go new file mode 100644 index 000000000000..fc31ca38e9ef --- /dev/null +++ b/x/bank/simulation/genesis_test.go @@ -0,0 +1,75 @@ +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/bank/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 bankGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &bankGenesis) + + require.Equal(t, true, bankGenesis.Params.GetDefaultSendEnabled()) + require.Len(t, bankGenesis.Params.GetSendEnabled(), 1) + require.Len(t, bankGenesis.Balances, 3) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", bankGenesis.Balances[2].GetAddress().String()) + require.Equal(t, "1000stake", bankGenesis.Balances[2].GetCoins().String()) + require.Equal(t, "6000stake", bankGenesis.Supply.String()) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 123e0b8eb45a..9fcc80f0fe45 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -3,15 +3,15 @@ package simulation import ( "math/rand" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/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/x/bank/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -22,8 +22,9 @@ const ( ) // WeightedOperations returns all the operations from the module with their respective weights -func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, - bk keeper.Keeper) simulation.WeightedOperations { +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, bk keeper.Keeper, +) simulation.WeightedOperations { var weightMsgSend, weightMsgMultiSend int appParams.GetOrGenerate(cdc, OpWeightMsgSend, &weightMsgSend, nil, @@ -52,59 +53,63 @@ func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak typ // SimulateMsgSend tests and runs a single msg send where both // accounts already exist. -// nolint: funlen -func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation { +func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, toSimAcc, coins, skip := randomSendFields(r, ctx, accs, bk, ak) - if !bk.GetSendEnabled(ctx) { - return simulation.NoOpMsg(types.ModuleName), nil, nil - } - - simAccount, toSimAcc, coins, skip, err := randomSendFields(r, ctx, accs, ak) - if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + // Check send_enabled status of each coin denom + if err := bk.SendEnabledCoins(ctx, coins...); err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSend, err.Error()), nil, nil } if skip { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSend, "skip all transfers"), nil, nil } msg := types.NewMsgSend(simAccount.Address, toSimAcc.Address, coins) - err = sendMsgSend(r, app, ak, msg, ctx, chainID, []crypto.PrivKey{simAccount.PrivKey}) + err := sendMsgSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{simAccount.PrivKey}) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // sendMsgSend sends a transaction with a MsgSend from a provided random account. +// nolint: interfacer func sendMsgSend( - r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, - msg types.MsgSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, + r *rand.Rand, app *baseapp.BaseApp, bk keeper.Keeper, ak types.AccountKeeper, + msg *types.MsgSend, ctx sdk.Context, chainID string, privkeys []cryptotypes.PrivKey, ) error { - account := ak.GetAccount(ctx, msg.FromAddress) - coins := account.SpendableCoins(ctx.BlockTime()) - var ( fees sdk.Coins err error ) - coins, hasNeg := coins.SafeSub(msg.Amount) + + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return err + } + + account := ak.GetAccount(ctx, from) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + coins, hasNeg := spendable.SafeSub(msg.Amount) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { return err } } - - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -113,8 +118,11 @@ func sendMsgSend( []uint64{account.GetSequence()}, privkeys..., ) + if err != nil { + return err + } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return err } @@ -124,23 +132,18 @@ func sendMsgSend( // SimulateMsgMultiSend tests and runs a single msg multisend, with randomized, capped number of inputs/outputs. // all accounts in msg fields exist in state -// nolint: funlen -func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation { +func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - - if !bk.GetSendEnabled(ctx) { - return simulation.NoOpMsg(types.ModuleName), nil, nil - } + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { // random number of inputs/outputs between [1, 3] inputs := make([]types.Input, r.Intn(3)+1) outputs := make([]types.Output, r.Intn(3)+1) // collect signer privKeys - privs := make([]crypto.PrivKey, len(inputs)) + privs := make([]cryptotypes.PrivKey, len(inputs)) // use map to check if address already exists as input usedAddrs := make(map[string]bool) @@ -148,18 +151,15 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.O var totalSentCoins sdk.Coins for i := range inputs { // generate random input fields, ignore to address - simAccount, _, coins, skip, err := randomSendFields(r, ctx, accs, ak) + simAccount, _, coins, skip := randomSendFields(r, ctx, accs, bk, ak) // make sure account is fresh and not used in previous input for usedAddrs[simAccount.Address.String()] { - simAccount, _, coins, skip, err = randomSendFields(r, ctx, accs, ak) + simAccount, _, coins, skip = randomSendFields(r, ctx, accs, bk, ak) } - if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err - } if skip { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, "skip all transfers"), nil, nil } // set input address in used address map @@ -173,8 +173,13 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.O totalSentCoins = totalSentCoins.Add(coins...) } + // Check send_enabled status of each sent coin denom + if err := bk.SendEnabledCoins(ctx, totalSentCoins...); err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, err.Error()), nil, nil + } + for o := range outputs { - outAddr, _ := simulation.RandomAcc(r, accs) + outAddr, _ := simtypes.RandomAcc(r, accs) var outCoins sdk.Coins // split total sent coins into random subsets for output @@ -183,7 +188,7 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.O } else { // take random subset of remaining coins for output // and update remaining coins - outCoins = simulation.RandSubsetCoins(r, totalSentCoins) + outCoins = simtypes.RandSubsetCoins(r, totalSentCoins) totalSentCoins = totalSentCoins.Sub(outCoins) } @@ -202,53 +207,66 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.O } } - msg := types.MsgMultiSend{ + msg := &types.MsgMultiSend{ Inputs: inputs, Outputs: outputs, } - err := sendMsgMultiSend(r, app, ak, msg, ctx, chainID, privs) + err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, privs) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // sendMsgMultiSend sends a transaction with a MsgMultiSend from a provided random // account. +// nolint: interfacer func sendMsgMultiSend( - r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, - msg types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, + r *rand.Rand, app *baseapp.BaseApp, bk keeper.Keeper, ak types.AccountKeeper, + msg *types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []cryptotypes.PrivKey, ) error { accountNumbers := make([]uint64, len(msg.Inputs)) sequenceNumbers := make([]uint64, len(msg.Inputs)) for i := 0; i < len(msg.Inputs); i++ { - acc := ak.GetAccount(ctx, msg.Inputs[i].Address) + addr, err := sdk.AccAddressFromBech32(msg.Inputs[i].Address) + if err != nil { + panic(err) + } + acc := ak.GetAccount(ctx, addr) accountNumbers[i] = acc.GetAccountNumber() sequenceNumbers[i] = acc.GetSequence() } - // feePayer is the first signer, i.e. first input address - feePayer := ak.GetAccount(ctx, msg.Inputs[0].Address) - coins := feePayer.SpendableCoins(ctx.BlockTime()) - var ( fees sdk.Coins err error ) - coins, hasNeg := coins.SafeSub(msg.Inputs[0].Coins) + + addr, err := sdk.AccAddressFromBech32(msg.Inputs[0].Address) + if err != nil { + panic(err) + } + + // feePayer is the first signer, i.e. first input address + feePayer := ak.GetAccount(ctx, addr) + spendable := bk.SpendableCoins(ctx, feePayer.GetAddress()) + + coins, hasNeg := spendable.SafeSub(msg.Inputs[0].Coins) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { return err } } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -257,8 +275,11 @@ func sendMsgMultiSend( sequenceNumbers, privkeys..., ) + if err != nil { + return err + } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { return err } @@ -268,29 +289,30 @@ func sendMsgMultiSend( // randomSendFields returns the sender and recipient simulation accounts as well // as the transferred amount. +// nolint: interfacer func randomSendFields( - r *rand.Rand, ctx sdk.Context, accs []simulation.Account, ak types.AccountKeeper, -) (simulation.Account, simulation.Account, sdk.Coins, bool, error) { + r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, bk keeper.Keeper, ak types.AccountKeeper, +) (simtypes.Account, simtypes.Account, sdk.Coins, bool) { - simAccount, _ := simulation.RandomAcc(r, accs) - toSimAcc, _ := simulation.RandomAcc(r, accs) + simAccount, _ := simtypes.RandomAcc(r, accs) + toSimAcc, _ := simtypes.RandomAcc(r, accs) // disallow sending money to yourself for simAccount.PubKey.Equals(toSimAcc.PubKey) { - toSimAcc, _ = simulation.RandomAcc(r, accs) + toSimAcc, _ = simtypes.RandomAcc(r, accs) } acc := ak.GetAccount(ctx, simAccount.Address) if acc == nil { - return simAccount, toSimAcc, nil, true, nil // skip error + return simAccount, toSimAcc, nil, true } - coins := acc.SpendableCoins(ctx.BlockHeader().Time) + spendable := bk.SpendableCoins(ctx, acc.GetAddress()) - sendCoins := simulation.RandSubsetCoins(r, coins) + sendCoins := simtypes.RandSubsetCoins(r, spendable) if sendCoins.Empty() { - return simAccount, toSimAcc, nil, true, nil // skip error + return simAccount, toSimAcc, nil, true } - return simAccount, toSimAcc, sendCoins, false, nil + return simAccount, toSimAcc, sendCoins, false } diff --git a/x/bank/simulation/operations_test.go b/x/bank/simulation/operations_test.go new file mode 100644 index 000000000000..144dc5dd3429 --- /dev/null +++ b/x/bank/simulation/operations_test.go @@ -0,0 +1,143 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "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/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/bank/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +type SimTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp +} + +func (suite *SimTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) +} + +// TestWeightedOperations tests the weights of the operations. +func (suite *SimTestSuite) TestWeightedOperations() { + cdc := suite.app.AppCodec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations(appParams, cdc, suite.app.AccountKeeper, suite.app.BankKeeper) + + // 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.DefaultWeightMsgSend, types.ModuleName, types.TypeMsgSend}, + {simappparams.DefaultWeightMsgMultiSend, types.ModuleName, types.TypeMsgMultiSend}, + } + + 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") + } +} + +// TestSimulateMsgSend tests the normal scenario of a valid message of type TypeMsgSend. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgSend() { + // 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}}) + + // execute operation + op := simulation.SimulateMsgSend(suite.app.AccountKeeper, suite.app.BankKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgSend + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("65337742stake", msg.Amount.String()) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.FromAddress) + suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.ToAddress) + suite.Require().Equal(types.TypeMsgSend, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgSend tests the normal scenario of a valid message of type TypeMsgMultiSend. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgMultiSend() { + // 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}}) + + // execute operation + op := simulation.SimulateMsgMultiSend(suite.app.AccountKeeper, suite.app.BankKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgMultiSend + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Len(msg.Inputs, 3) + suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Inputs[1].Address) + suite.Require().Equal("185121068stake", msg.Inputs[1].Coins.String()) + suite.Require().Len(msg.Outputs, 2) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Outputs[1].Address) + suite.Require().Equal("260469617stake", msg.Outputs[1].Coins.String()) + suite.Require().Equal(types.TypeMsgMultiSend, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +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 { + 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) + } + + return accounts +} + +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +} diff --git a/x/bank/simulation/params.go b/x/bank/simulation/params.go index f5aa25bf6a0c..c3da182205b7 100644 --- a/x/bank/simulation/params.go +++ b/x/bank/simulation/params.go @@ -3,22 +3,32 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/x/bank/internal/types" "github.com/cosmos/cosmos-sdk/x/simulation" -) -const keySendEnabled = "sendenabled" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, keySendEnabled, +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, string(types.KeySendEnabled), + func(r *rand.Rand) string { + paramsBytes, err := json.Marshal(RandomGenesisSendParams(r)) + if err != nil { + panic(err) + } + return fmt.Sprintf("%s", paramsBytes) + }, + ), + simulation.NewSimParamChange(types.ModuleName, string(types.KeyDefaultSendEnabled), func(r *rand.Rand) string { - return fmt.Sprintf("%v", GenSendEnabled(r)) + return fmt.Sprintf("%v", RandomGenesisDefaultSendParam(r)) }, ), } diff --git a/x/bank/simulation/params_test.go b/x/bank/simulation/params_test.go new file mode 100644 index 000000000000..3045bb507530 --- /dev/null +++ b/x/bank/simulation/params_test.go @@ -0,0 +1,36 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/bank/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"bank/SendEnabled", "SendEnabled", "[]", "bank"}, + {"bank/DefaultSendEnabled", "DefaultSendEnabled", "true", "bank"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 2) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/bank/spec/01_state.md b/x/bank/spec/01_state.md index ba0e25b899b0..f744e2e779a1 100644 --- a/x/bank/spec/01_state.md +++ b/x/bank/spec/01_state.md @@ -4,6 +4,8 @@ order: 1 # State -Presently, the bank module has no inherent state — it simply reads and writes accounts using the `AccountKeeper` from the `auth` module. +The `x/bank` module keeps state of two primary objects, account balances and the +total supply of all balances. -This implementation choice is intended to minimize necessary state reads/writes, since we expect most transactions to involve coin amounts (for fees), so storing coin data in the account saves reading it separately. +- Balances: `[]byte("balances") | []byte(address) / []byte(balance.Denom) -> ProtocolBuffer(balance)` +- Supply: `0x0 -> ProtocolBuffer(Supply)` diff --git a/x/bank/spec/02_keepers.md b/x/bank/spec/02_keepers.md index d20b42fe3dea..0032961b1f3b 100644 --- a/x/bank/spec/02_keepers.md +++ b/x/bank/spec/02_keepers.md @@ -14,10 +14,11 @@ Note that you should always review the `bank` module code to ensure that permiss An input of a multiparty transfer -```go -type Input struct { - Address AccAddress - Coins Coins +```protobuf +// Input models transaction input. +message Input { + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2; } ``` @@ -25,10 +26,11 @@ type Input struct { An output of a multiparty transfer. -```go -type Output struct { - Address AccAddress - Coins Coins +```protobuf +// Output models transaction outputs. +message Output { + string address = 1; + repeated cosmos.base.v1beta1.Coin coins = 2; } ``` @@ -37,99 +39,85 @@ type Output struct { The base keeper provides full-permission access: the ability to arbitrary modify any account's balance and mint or burn coins. ```go -type BaseKeeper interface { - SetCoins(addr AccAddress, amt Coins) - SubtractCoins(addr AccAddress, amt Coins) - AddCoins(addr AccAddress, amt Coins) - InputOutputCoins(inputs []Input, outputs []Output) +// Keeper defines a module interface that facilitates the transfer of coins +// between accounts. +type Keeper interface { + SendKeeper + + InitGenesis(sdk.Context, *types.GenesisState) + 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 + SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) + IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) + + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + + DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error + UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error + MarshalSupply(supplyI exported.SupplyI) ([]byte, error) + UnmarshalSupply(bz []byte) (exported.SupplyI, error) + + types.QueryServer } ``` -`setCoins` fetches an account by address, sets the coins on the account, and saves the account. - -``` -setCoins(addr AccAddress, amt Coins) - account = accountKeeper.getAccount(addr) - if account == nil - fail with "no account found" - account.Coins = amt - accountKeeper.setAccount(account) -``` - -`subtractCoins` fetches the coins of an account, subtracts the provided amount, and saves the account. This decreases the total supply. +## SendKeeper -``` -subtractCoins(addr AccAddress, amt Coins) - oldCoins = getCoins(addr) - newCoins = oldCoins - amt - if newCoins < 0 - fail with "cannot end up with negative coins" - setCoins(addr, newCoins) -``` +The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins). -`addCoins` fetches the coins of an account, adds the provided amount, and saves the account. This increases the total supply. +```go +// SendKeeper defines a module interface that facilitates the transfer of coins +// between accounts without the possibility of creating coins. +type SendKeeper interface { + ViewKeeper -``` -addCoins(addr AccAddress, amt Coins) - oldCoins = getCoins(addr) - newCoins = oldCoins + amt - setCoins(addr, newCoins) -``` + 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 -`inputOutputCoins` transfers coins from any number of input accounts to any number of output accounts. + SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error + AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error -``` -inputOutputCoins(inputs []Input, outputs []Output) - for input in inputs - subtractCoins(input.Address, input.Coins) - for output in outputs - addCoins(output.Address, output.Coins) -``` + SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error + SetBalances(ctx sdk.Context, addr sdk.AccAddress, balances sdk.Coins) error -## SendKeeper + GetParams(ctx sdk.Context) types.Params + SetParams(ctx sdk.Context, params types.Params) -The send keeper provides access to account balances and the ability to transfer coins between accounts, but not to alter the total supply (mint or burn coins). + SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool + SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error -```go -type SendKeeper interface { - SendCoins(from AccAddress, to AccAddress, amt Coins) + BlockedAddr(addr sdk.AccAddress) bool } ``` -`sendCoins` transfers coins from one account to another. - -``` -sendCoins(from AccAddress, to AccAddress, amt Coins) - subtractCoins(from, amt) - addCoins(to, amt) -``` - ## ViewKeeper The view keeper provides read-only access to account balances but no balance alteration functionality. All balance lookups are `O(1)`. ```go +// ViewKeeper defines a module interface that facilitates read only access to +// account balances. type ViewKeeper interface { - GetCoins(addr AccAddress) Coins - HasCoins(addr AccAddress, amt Coins) bool -} -``` - -`getCoins` returns the coins associated with an account. - -``` -getCoins(addr AccAddress) - account = accountKeeper.getAccount(addr) - if account == nil - return Coins{} - return account.Coins -``` + ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error + HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool -`hasCoins` returns whether or not an account has at least the provided amount of coins. + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + GetAccountsBalances(ctx sdk.Context) []types.Balance + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin + LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins -``` -hasCoins(addr AccAddress, amt Coins) - account = accountKeeper.getAccount(addr) - coins = getCoins(addr) - return coins >= amt + IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) + IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) +} ``` diff --git a/x/bank/spec/03_messages.md b/x/bank/spec/03_messages.md index f9cde522837a..07c42f5f78e5 100644 --- a/x/bank/spec/03_messages.md +++ b/x/bank/spec/03_messages.md @@ -6,16 +6,11 @@ order: 3 ## MsgSend -```go -type MsgSend struct { - Inputs []Input - Outputs []Output -} -``` ++++ 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/spec/04_events.md b/x/bank/spec/04_events.md index 1f97e7ab9b74..55c93b805ed1 100644 --- a/x/bank/spec/04_events.md +++ b/x/bank/spec/04_events.md @@ -11,7 +11,7 @@ The bank module emits the following events: ### MsgSend | Type | Attribute Key | Attribute Value | -|----------|---------------|--------------------| +| -------- | ------------- | ------------------ | | transfer | recipient | {recipientAddress} | | transfer | amount | {amount} | | message | module | bank | @@ -21,7 +21,7 @@ The bank module emits the following events: ### MsgMultiSend | Type | Attribute Key | Attribute Value | -|----------|---------------|--------------------| +| -------- | ------------- | ------------------ | | transfer | recipient | {recipientAddress} | | transfer | amount | {amount} | | message | module | bank | diff --git a/x/bank/spec/05_params.md b/x/bank/spec/05_params.md index fe59bed244ec..8d254243c4bc 100644 --- a/x/bank/spec/05_params.md +++ b/x/bank/spec/05_params.md @@ -6,7 +6,19 @@ order: 5 The bank module contains the following parameters: -| Key | Type | Example | -|-------------|------|---------| -| sendenabled | bool | true | +| Key | Type | Example | +| ------------------ | ------------- | ---------------------------------- | +| SendEnabled | []SendEnabled | [{denom: "stake", enabled: true }] | +| DefaultSendEnabled | bool | true | +## SendEnabled + +The send enabled parameter is an array of SendEnabled entries mapping coin +denominations to their send_enabled status. Entries in this list take +precedence over the `DefaultSendEnabled` setting. + +## DefaultSendEnabled + +The default send enabled value controls send transfer capability for all +coin denominations unless specifically included in the array of `SendEnabled` +parameters. diff --git a/x/bank/spec/README.md b/x/bank/spec/README.md index 28902554f49f..9a1a0afb6edc 100644 --- a/x/bank/spec/README.md +++ b/x/bank/spec/README.md @@ -5,7 +5,7 @@ parent: title: "bank" --> -# `bank` +# `x/bank` ## Abstract @@ -17,18 +17,86 @@ with particular kinds of accounts (notably delegating/undelegating for vesting accounts). It exposes several interfaces with varying capabilities for secure interaction with other modules which must alter user balances. +In addition, the bank module tracks and provides query support for the total +supply of all assets used in the application. + This module will be used in the Cosmos Hub. +## Supply + +The `supply` functionality: + +- passively tracks the total supply of coins within a chain, +- provides a pattern for modules to hold/interact with `Coins`, and +- introduces the invariant check to verify a chain's total supply. + +### Total Supply + +The total `Supply` of the network is equal to the sum of all coins from the +account. The total supply is updated every time a `Coin` is minted (eg: as part +of the inflation mechanism) or burned (eg: due to slashing or if a governance +proposal is vetoed). + +## Module Accounts + +The supply functionality introduces a new type of `auth.Account` which can be used by +modules to allocate tokens and in special cases mint or burn tokens. At a base +level these module accounts are capable of sending/receiving tokens to and from +`auth.Account`s and other module accounts. This design replaces previous +alternative designs where, to hold tokens, modules would burn the incoming +tokens from the sender account, and then track those tokens internally. Later, +in order to send tokens, the module would need to effectively mint tokens +within a destination account. The new design removes duplicate logic between +modules to perform this accounting. + +The `ModuleAccount` interface is defined as follows: + +```go +type ModuleAccount interface { + auth.Account // same methods as the Account interface + + GetName() string // name of the module; used to obtain the address + GetPermissions() []string // permissions of module account + HasPermission(string) bool +} +``` + +> **WARNING!** +> Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed). + +The supply `Keeper` also introduces new wrapper functions for the auth `Keeper` +and the bank `Keeper` that are related to `ModuleAccount`s in order to be able +to: + +- Get and set `ModuleAccount`s by providing the `Name`. +- Send coins from and to other `ModuleAccount`s or standard `Account`s + (`BaseAccount` or `VestingAccount`) by passing only the `Name`. +- `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions). + +### Permissions + +Each `ModuleAccount` has a different set of permissions that provide different +object capabilities to perform certain actions. Permissions need to be +registered upon the creation of the supply `Keeper` so that every time a +`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the +permissions to that specific account and perform or not the action. + +The available permissions are: + +- `Minter`: allows for a module to mint a specific amount of coins. +- `Burner`: allows for a module to burn a specific amount of coins. +- `Staking`: allows for a module to delegate and undelegate a specific amount of coins. + ## Contents 1. **[State](01_state.md)** 2. **[Keepers](02_keepers.md)** - - [Common Types](02_keepers.md#common-types) - - [BaseKeeper](02_keepers.md#basekeeper) - - [SendKeeper](02_keepers.md#sendkeeper) - - [ViewKeeper](02_keepers.md#viewkeeper) + - [Common Types](02_keepers.md#common-types) + - [BaseKeeper](02_keepers.md#basekeeper) + - [SendKeeper](02_keepers.md#sendkeeper) + - [ViewKeeper](02_keepers.md#viewkeeper) 3. **[Messages](03_messages.md)** - - [MsgSend](03_messages.md#msgsend) + - [MsgSend](03_messages.md#msgsend) 4. **[Events](04_events.md)** - - [Handlers](04_events.md#handlers) + - [Handlers](04_events.md#handlers) 5. **[Parameters](05_params.md)** diff --git a/x/bank/types/balance.go b/x/bank/types/balance.go new file mode 100644 index 000000000000..7c017ee90051 --- /dev/null +++ b/x/bank/types/balance.go @@ -0,0 +1,123 @@ +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 + } + 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 { + // Given that this function sorts balances, using the standard library's + // Quicksort based algorithms, we have algorithmic complexities of: + // * Best case: O(nlogn) + // * Worst case: O(n^2) + // The comparator used MUST be cheap to use lest we incur expenses like we had + // before whereby sdk.AccAddressFromBech32, which is a very expensive operation + // compared n * n elements yet discarded computations each time, as per: + // https://github.com/cosmos/cosmos-sdk/issues/7766#issuecomment-786671734 + // with this change the first step is to extract out and singly produce the values + // that'll be used for comparisons and keep them cheap and fast. + + // 1. Retrieve the byte equivalents for each Balance's address and maintain a mapping of + // its Balance, as the mapper will be used in sorting. + type addrToBalance struct { + // We use a pointer here to avoid averse effects of value copying + // wasting RAM all around. + balance *Balance + accAddr []byte + } + adL := make([]*addrToBalance, 0, len(balances)) + for _, balance := range balances { + balance := balance + addr, _ := sdk.AccAddressFromBech32(balance.Address) + adL = append(adL, &addrToBalance{ + balance: &balance, + accAddr: []byte(addr), + }) + } + + // 2. Sort with the cheap mapping, using the mapper's + // already accAddr bytes values which is a cheaper operation. + sort.Slice(adL, func(i, j int) bool { + ai, aj := adL[i], adL[j] + return bytes.Compare(ai.accAddr, aj.accAddr) < 0 + }) + + // 3. To complete the sorting, we have to now just insert + // back the balances in the order returned by the sort. + for i, ad := range adL { + balances[i] = *ad.balance + } + 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..9736c847baa0 --- /dev/null +++ b/x/bank/types/balance_test.go @@ -0,0 +1,181 @@ +package types_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + 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", + }, + false, + }, + { + "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()) + } + }) + } +} + +func TestSanitizeBalances(t *testing.T) { + // 1. Generate balances + tokens := sdk.TokensFromConsensusPower(81) + coin := sdk.NewCoin("benchcoin", tokens) + coins := sdk.Coins{coin} + addrs, _ := makeRandomAddressesAndPublicKeys(20) + + var balances []bank.Balance + for _, addr := range addrs { + balances = append(balances, bank.Balance{ + Address: addr.String(), + Coins: coins, + }) + } + // 2. Sort the values. + sorted := bank.SanitizeGenesisBalances(balances) + + // 3. Compare and ensure that all the values are sorted in ascending order. + // Invariant after sorting: + // a[i] <= a[i+1...n] + for i := 0; i < len(sorted); i++ { + ai := sorted[i] + // Ensure that every single value that comes after i is less than it. + for j := i + 1; j < len(sorted); j++ { + aj := sorted[j] + + if got := bytes.Compare(ai.GetAddress(), aj.GetAddress()); got > 0 { + t.Errorf("Balance(%d) > Balance(%d)", i, j) + } + } + } +} + +func makeRandomAddressesAndPublicKeys(n int) (accL []sdk.AccAddress, pkL []*ed25519.PubKey) { + for i := 0; i < n; i++ { + pk := ed25519.GenPrivKey().PubKey().(*ed25519.PubKey) + pkL = append(pkL, pk) + accL = append(accL, sdk.AccAddress(pk.Address())) + } + return accL, pkL +} + +var sink, revert interface{} + +func BenchmarkSanitizeBalances500(b *testing.B) { + benchmarkSanitizeBalances(b, 500) +} + +func BenchmarkSanitizeBalances1000(b *testing.B) { + benchmarkSanitizeBalances(b, 1000) +} + +func benchmarkSanitizeBalances(b *testing.B, nAddresses int) { + b.ReportAllocs() + tokens := sdk.TokensFromConsensusPower(81) + coin := sdk.NewCoin("benchcoin", tokens) + coins := sdk.Coins{coin} + addrs, _ := makeRandomAddressesAndPublicKeys(nAddresses) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + var balances []bank.Balance + for _, addr := range addrs { + balances = append(balances, bank.Balance{ + Address: addr.String(), + Coins: coins, + }) + } + sink = bank.SanitizeGenesisBalances(balances) + } + if sink == nil { + b.Fatal("Benchmark did not run") + } + sink = revert +} diff --git a/x/bank/types/bank.pb.go b/x/bank/types/bank.pb.go new file mode 100644 index 000000000000..ef1c52c1ccb5 --- /dev/null +++ b/x/bank/types/bank.pb.go @@ -0,0 +1,1886 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/bank/v1beta1/bank.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Params defines the parameters for the bank module. +type Params struct { + SendEnabled []*SendEnabled `protobuf:"bytes,1,rep,name=send_enabled,json=sendEnabled,proto3" json:"send_enabled,omitempty" yaml:"send_enabled,omitempty"` + DefaultSendEnabled bool `protobuf:"varint,2,opt,name=default_send_enabled,json=defaultSendEnabled,proto3" json:"default_send_enabled,omitempty" yaml:"default_send_enabled,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetSendEnabled() []*SendEnabled { + if m != nil { + return m.SendEnabled + } + return nil +} + +func (m *Params) GetDefaultSendEnabled() bool { + if m != nil { + return m.DefaultSendEnabled + } + return false +} + +// SendEnabled maps coin denom to a send_enabled status (whether a denom is +// sendable). +type SendEnabled struct { + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` +} + +func (m *SendEnabled) Reset() { *m = SendEnabled{} } +func (*SendEnabled) ProtoMessage() {} +func (*SendEnabled) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{1} +} +func (m *SendEnabled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SendEnabled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SendEnabled.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 *SendEnabled) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendEnabled.Merge(m, src) +} +func (m *SendEnabled) XXX_Size() int { + return m.Size() +} +func (m *SendEnabled) XXX_DiscardUnknown() { + xxx_messageInfo_SendEnabled.DiscardUnknown(m) +} + +var xxx_messageInfo_SendEnabled proto.InternalMessageInfo + +func (m *SendEnabled) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *SendEnabled) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +// Input models transaction input. +type Input struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` +} + +func (m *Input) Reset() { *m = Input{} } +func (m *Input) String() string { return proto.CompactTextString(m) } +func (*Input) ProtoMessage() {} +func (*Input) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{2} +} +func (m *Input) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Input) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Input.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 *Input) XXX_Merge(src proto.Message) { + xxx_messageInfo_Input.Merge(m, src) +} +func (m *Input) XXX_Size() int { + return m.Size() +} +func (m *Input) XXX_DiscardUnknown() { + xxx_messageInfo_Input.DiscardUnknown(m) +} + +var xxx_messageInfo_Input proto.InternalMessageInfo + +// Output models transaction outputs. +type Output struct { + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` +} + +func (m *Output) Reset() { *m = Output{} } +func (m *Output) String() string { return proto.CompactTextString(m) } +func (*Output) ProtoMessage() {} +func (*Output) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{3} +} +func (m *Output) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Output) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Output.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 *Output) XXX_Merge(src proto.Message) { + xxx_messageInfo_Output.Merge(m, src) +} +func (m *Output) XXX_Size() int { + return m.Size() +} +func (m *Output) XXX_DiscardUnknown() { + xxx_messageInfo_Output.DiscardUnknown(m) +} + +var xxx_messageInfo_Output proto.InternalMessageInfo + +// Supply represents a struct that passively keeps track of the total supply +// amounts in the network. +type Supply struct { + Total github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=total,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"total"` +} + +func (m *Supply) Reset() { *m = Supply{} } +func (*Supply) ProtoMessage() {} +func (*Supply) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{4} +} +func (m *Supply) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Supply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Supply.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 *Supply) XXX_Merge(src proto.Message) { + xxx_messageInfo_Supply.Merge(m, src) +} +func (m *Supply) XXX_Size() int { + return m.Size() +} +func (m *Supply) XXX_DiscardUnknown() { + xxx_messageInfo_Supply.DiscardUnknown(m) +} + +var xxx_messageInfo_Supply proto.InternalMessageInfo + +// DenomUnit represents a struct that describes a given +// denomination unit of the basic token. +type DenomUnit struct { + // denom represents the string name of the given denom unit (e.g uatom). + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // 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). + Exponent uint32 `protobuf:"varint,2,opt,name=exponent,proto3" json:"exponent,omitempty"` + // aliases is a list of string aliases for the given denom + Aliases []string `protobuf:"bytes,3,rep,name=aliases,proto3" json:"aliases,omitempty"` +} + +func (m *DenomUnit) Reset() { *m = DenomUnit{} } +func (m *DenomUnit) String() string { return proto.CompactTextString(m) } +func (*DenomUnit) ProtoMessage() {} +func (*DenomUnit) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{5} +} +func (m *DenomUnit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DenomUnit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DenomUnit.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 *DenomUnit) XXX_Merge(src proto.Message) { + xxx_messageInfo_DenomUnit.Merge(m, src) +} +func (m *DenomUnit) XXX_Size() int { + return m.Size() +} +func (m *DenomUnit) XXX_DiscardUnknown() { + xxx_messageInfo_DenomUnit.DiscardUnknown(m) +} + +var xxx_messageInfo_DenomUnit proto.InternalMessageInfo + +func (m *DenomUnit) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *DenomUnit) GetExponent() uint32 { + if m != nil { + return m.Exponent + } + return 0 +} + +func (m *DenomUnit) GetAliases() []string { + if m != nil { + return m.Aliases + } + return nil +} + +// Metadata represents a struct that describes +// a basic token. +type Metadata struct { + Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` + // denom_units represents the list of DenomUnit's for a given coin + DenomUnits []*DenomUnit `protobuf:"bytes,2,rep,name=denom_units,json=denomUnits,proto3" json:"denom_units,omitempty"` + // base represents the base denom (should be the DenomUnit with exponent = 0). + Base string `protobuf:"bytes,3,opt,name=base,proto3" json:"base,omitempty"` + // display indicates the suggested denom that should be + // displayed in clients. + Display string `protobuf:"bytes,4,opt,name=display,proto3" json:"display,omitempty"` +} + +func (m *Metadata) Reset() { *m = Metadata{} } +func (m *Metadata) String() string { return proto.CompactTextString(m) } +func (*Metadata) ProtoMessage() {} +func (*Metadata) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{6} +} +func (m *Metadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metadata.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 *Metadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metadata.Merge(m, src) +} +func (m *Metadata) XXX_Size() int { + return m.Size() +} +func (m *Metadata) XXX_DiscardUnknown() { + xxx_messageInfo_Metadata.DiscardUnknown(m) +} + +var xxx_messageInfo_Metadata proto.InternalMessageInfo + +func (m *Metadata) GetDescription() string { + if m != nil { + return m.Description + } + return "" +} + +func (m *Metadata) GetDenomUnits() []*DenomUnit { + if m != nil { + return m.DenomUnits + } + return nil +} + +func (m *Metadata) GetBase() string { + if m != nil { + return m.Base + } + return "" +} + +func (m *Metadata) GetDisplay() string { + if m != nil { + return m.Display + } + return "" +} + +func init() { + proto.RegisterType((*Params)(nil), "cosmos.bank.v1beta1.Params") + proto.RegisterType((*SendEnabled)(nil), "cosmos.bank.v1beta1.SendEnabled") + proto.RegisterType((*Input)(nil), "cosmos.bank.v1beta1.Input") + proto.RegisterType((*Output)(nil), "cosmos.bank.v1beta1.Output") + proto.RegisterType((*Supply)(nil), "cosmos.bank.v1beta1.Supply") + proto.RegisterType((*DenomUnit)(nil), "cosmos.bank.v1beta1.DenomUnit") + proto.RegisterType((*Metadata)(nil), "cosmos.bank.v1beta1.Metadata") +} + +func init() { proto.RegisterFile("cosmos/bank/v1beta1/bank.proto", fileDescriptor_dd052eee12edf988) } + +var fileDescriptor_dd052eee12edf988 = []byte{ + // 566 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbf, 0x6f, 0x13, 0x31, + 0x14, 0x3e, 0x37, 0x4d, 0x48, 0x1d, 0x58, 0x4c, 0x86, 0x6b, 0x24, 0xee, 0x8e, 0x93, 0x90, 0x52, + 0x44, 0x2f, 0x14, 0xc4, 0x92, 0x05, 0x29, 0xa5, 0x42, 0x1d, 0x10, 0xe8, 0x2a, 0x84, 0x04, 0x43, + 0xe4, 0xc4, 0x6e, 0xb0, 0x7a, 0x67, 0x9f, 0x62, 0x1f, 0x6a, 0xfe, 0x03, 0x26, 0x60, 0x44, 0x62, + 0xe9, 0xcc, 0x88, 0xf8, 0x23, 0x3a, 0x56, 0xb0, 0x30, 0x05, 0x94, 0x2c, 0xcc, 0xfd, 0x0b, 0x90, + 0xed, 0xcb, 0x8f, 0x4a, 0x01, 0x31, 0x30, 0x30, 0x9d, 0x9f, 0xdf, 0xf7, 0xbe, 0xf7, 0xe9, 0x7b, + 0xcf, 0x07, 0xbd, 0xbe, 0x90, 0xa9, 0x90, 0xad, 0x1e, 0xe6, 0x47, 0xad, 0x57, 0x3b, 0x3d, 0xaa, + 0xf0, 0x8e, 0x09, 0xa2, 0x6c, 0x28, 0x94, 0x40, 0x57, 0x6d, 0x3e, 0x32, 0x57, 0x45, 0xbe, 0x51, + 0x1f, 0x88, 0x81, 0x30, 0xf9, 0x96, 0x3e, 0x59, 0x68, 0x63, 0xd3, 0x42, 0xbb, 0x36, 0x51, 0xd4, + 0xd9, 0xd4, 0xa2, 0x8b, 0xa4, 0xf3, 0x2e, 0x7d, 0xc1, 0xb8, 0xcd, 0x87, 0x5f, 0x01, 0xac, 0x3c, + 0xc1, 0x43, 0x9c, 0x4a, 0x74, 0x08, 0x2f, 0x4b, 0xca, 0x49, 0x97, 0x72, 0xdc, 0x4b, 0x28, 0x71, + 0x41, 0x50, 0x6a, 0xd6, 0xee, 0x04, 0xd1, 0x0a, 0x1d, 0xd1, 0x01, 0xe5, 0x64, 0xcf, 0xe2, 0x3a, + 0xd7, 0xcf, 0xc7, 0xfe, 0xb5, 0x11, 0x4e, 0x93, 0x76, 0xb8, 0x5c, 0x7f, 0x4b, 0xa4, 0x4c, 0xd1, + 0x34, 0x53, 0xa3, 0x30, 0xae, 0xc9, 0x05, 0x1e, 0xbd, 0x80, 0x75, 0x42, 0x0f, 0x71, 0x9e, 0xa8, + 0xee, 0x85, 0x7e, 0x6b, 0x01, 0x68, 0x56, 0x3b, 0x5b, 0xe7, 0x63, 0xff, 0x86, 0x65, 0x5b, 0x85, + 0x5a, 0x66, 0x45, 0x05, 0x60, 0x49, 0x4c, 0x7b, 0xfd, 0xfd, 0x89, 0xef, 0x84, 0x0f, 0x61, 0x6d, + 0xe9, 0x12, 0xd5, 0x61, 0x99, 0x50, 0x2e, 0x52, 0x17, 0x04, 0xa0, 0xb9, 0x11, 0xdb, 0x00, 0xb9, + 0xf0, 0xd2, 0x85, 0xd6, 0xf1, 0x2c, 0x6c, 0x57, 0x35, 0xc9, 0xcf, 0x13, 0x1f, 0x84, 0x6f, 0x00, + 0x2c, 0xef, 0xf3, 0x2c, 0x57, 0x1a, 0x8d, 0x09, 0x19, 0x52, 0x29, 0x0b, 0x96, 0x59, 0x88, 0x30, + 0x2c, 0x6b, 0x43, 0xa5, 0xbb, 0x66, 0x0c, 0xdb, 0x5c, 0x18, 0x26, 0xe9, 0xdc, 0xb0, 0x5d, 0xc1, + 0x78, 0xe7, 0xf6, 0xe9, 0xd8, 0x77, 0x3e, 0x7e, 0xf7, 0x9b, 0x03, 0xa6, 0x5e, 0xe6, 0xbd, 0xa8, + 0x2f, 0xd2, 0x62, 0x5a, 0xc5, 0x67, 0x5b, 0x92, 0xa3, 0x96, 0x1a, 0x65, 0x54, 0x9a, 0x02, 0x19, + 0x5b, 0xe6, 0x76, 0xf5, 0xb5, 0x15, 0xe4, 0x84, 0x6f, 0x01, 0xac, 0x3c, 0xce, 0xd5, 0x7f, 0xa4, + 0xe8, 0x13, 0x80, 0x95, 0x83, 0x3c, 0xcb, 0x92, 0x91, 0xee, 0xab, 0x84, 0xc2, 0x49, 0xb1, 0x3a, + 0xff, 0xb6, 0xaf, 0x61, 0x6e, 0xef, 0xe9, 0xbe, 0xb3, 0xf1, 0x7c, 0xf9, 0xbc, 0x7d, 0xef, 0xe6, + 0x1f, 0x19, 0x8e, 0xed, 0xf3, 0xa2, 0xc7, 0x99, 0x18, 0x2a, 0x4a, 0x22, 0x2b, 0x74, 0x3f, 0x7c, + 0x06, 0x37, 0x1e, 0xe8, 0x25, 0x78, 0xca, 0x99, 0xfa, 0xcd, 0x7a, 0x34, 0x60, 0x55, 0x97, 0x71, + 0xca, 0x95, 0xd9, 0x8f, 0x2b, 0xf1, 0x3c, 0x36, 0xd6, 0x27, 0x0c, 0x4b, 0x2a, 0xdd, 0x52, 0x50, + 0x32, 0xd6, 0xdb, 0x30, 0xfc, 0x00, 0x60, 0xf5, 0x11, 0x55, 0x98, 0x60, 0x85, 0x51, 0x00, 0x6b, + 0x84, 0xca, 0xfe, 0x90, 0x65, 0x8a, 0x09, 0x5e, 0xd0, 0x2f, 0x5f, 0xa1, 0xfb, 0x1a, 0xc1, 0x45, + 0xda, 0xcd, 0x39, 0x53, 0xb3, 0x79, 0x79, 0x2b, 0x9f, 0xdc, 0x5c, 0x6f, 0x0c, 0xc9, 0xec, 0x28, + 0x11, 0x82, 0xeb, 0xda, 0x5d, 0xb7, 0x64, 0xb8, 0xcd, 0x59, 0xab, 0x23, 0x4c, 0x66, 0x09, 0x1e, + 0xb9, 0xeb, 0x76, 0x31, 0x8a, 0xb0, 0xb3, 0x7b, 0x3a, 0xf1, 0xc0, 0xd9, 0xc4, 0x03, 0x3f, 0x26, + 0x1e, 0x78, 0x37, 0xf5, 0x9c, 0xb3, 0xa9, 0xe7, 0x7c, 0x9b, 0x7a, 0xce, 0xf3, 0xad, 0xbf, 0xb1, + 0xd1, 0xcc, 0xa3, 0x57, 0x31, 0x7f, 0x8e, 0xbb, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x72, + 0xf6, 0xc6, 0xc1, 0x04, 0x00, 0x00, +} + +func (this *SendEnabled) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SendEnabled) + if !ok { + that2, ok := that.(SendEnabled) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Denom != that1.Denom { + return false + } + if this.Enabled != that1.Enabled { + return false + } + return true +} +func (this *Supply) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Supply) + if !ok { + that2, ok := that.(Supply) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Total) != len(that1.Total) { + return false + } + for i := range this.Total { + if !this.Total[i].Equal(&that1.Total[i]) { + return false + } + } + return true +} +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DefaultSendEnabled { + i-- + if m.DefaultSendEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.SendEnabled) > 0 { + for iNdEx := len(m.SendEnabled) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SendEnabled[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBank(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *SendEnabled) 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 *SendEnabled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SendEnabled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintBank(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Input) 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 *Input) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Input) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBank(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintBank(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Output) 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 *Output) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Output) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBank(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintBank(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Supply) 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 *Supply) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Supply) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Total) > 0 { + for iNdEx := len(m.Total) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Total[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBank(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *DenomUnit) 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 *DenomUnit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DenomUnit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Aliases) > 0 { + for iNdEx := len(m.Aliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Aliases[iNdEx]) + copy(dAtA[i:], m.Aliases[iNdEx]) + i = encodeVarintBank(dAtA, i, uint64(len(m.Aliases[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.Exponent != 0 { + i = encodeVarintBank(dAtA, i, uint64(m.Exponent)) + i-- + dAtA[i] = 0x10 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintBank(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Metadata) 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 *Metadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Display) > 0 { + i -= len(m.Display) + copy(dAtA[i:], m.Display) + i = encodeVarintBank(dAtA, i, uint64(len(m.Display))) + i-- + dAtA[i] = 0x22 + } + if len(m.Base) > 0 { + i -= len(m.Base) + copy(dAtA[i:], m.Base) + i = encodeVarintBank(dAtA, i, uint64(len(m.Base))) + i-- + dAtA[i] = 0x1a + } + if len(m.DenomUnits) > 0 { + for iNdEx := len(m.DenomUnits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomUnits[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBank(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintBank(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintBank(dAtA []byte, offset int, v uint64) int { + offset -= sovBank(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SendEnabled) > 0 { + for _, e := range m.SendEnabled { + l = e.Size() + n += 1 + l + sovBank(uint64(l)) + } + } + if m.DefaultSendEnabled { + n += 2 + } + return n +} + +func (m *SendEnabled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + if m.Enabled { + n += 2 + } + return n +} + +func (m *Input) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + if len(m.Coins) > 0 { + for _, e := range m.Coins { + l = e.Size() + n += 1 + l + sovBank(uint64(l)) + } + } + return n +} + +func (m *Output) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + if len(m.Coins) > 0 { + for _, e := range m.Coins { + l = e.Size() + n += 1 + l + sovBank(uint64(l)) + } + } + return n +} + +func (m *Supply) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Total) > 0 { + for _, e := range m.Total { + l = e.Size() + n += 1 + l + sovBank(uint64(l)) + } + } + return n +} + +func (m *DenomUnit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + if m.Exponent != 0 { + n += 1 + sovBank(uint64(m.Exponent)) + } + if len(m.Aliases) > 0 { + for _, s := range m.Aliases { + l = len(s) + n += 1 + l + sovBank(uint64(l)) + } + } + return n +} + +func (m *Metadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Description) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + if len(m.DenomUnits) > 0 { + for _, e := range m.DenomUnits { + l = e.Size() + n += 1 + l + sovBank(uint64(l)) + } + } + l = len(m.Base) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + l = len(m.Display) + if l > 0 { + n += 1 + l + sovBank(uint64(l)) + } + return n +} + +func sovBank(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozBank(x uint64) (n int) { + return sovBank(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) 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 ErrIntOverflowBank + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SendEnabled", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SendEnabled = append(m.SendEnabled, &SendEnabled{}) + if err := m.SendEnabled[len(m.SendEnabled)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultSendEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DefaultSendEnabled = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SendEnabled) 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 ErrIntOverflowBank + } + 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: SendEnabled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SendEnabled: 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 ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Enabled = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Input) 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 ErrIntOverflowBank + } + 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: Input: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Input: 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 ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Output) 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 ErrIntOverflowBank + } + 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: Output: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Output: 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 ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Supply) 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 ErrIntOverflowBank + } + 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: Supply: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Supply: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Total = append(m.Total, types.Coin{}) + if err := m.Total[len(m.Total)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DenomUnit) 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 ErrIntOverflowBank + } + 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: DenomUnit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenomUnit: 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 ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exponent", wireType) + } + m.Exponent = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Exponent |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Aliases", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Aliases = append(m.Aliases, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Metadata) 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 ErrIntOverflowBank + } + 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: Metadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomUnits", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DenomUnits = append(m.DenomUnits, &DenomUnit{}) + if err := m.DenomUnits[len(m.DenomUnits)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Base", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Base = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Display", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Display = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipBank(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, ErrIntOverflowBank + } + 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, ErrIntOverflowBank + } + 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, ErrIntOverflowBank + } + 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, ErrInvalidLengthBank + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupBank + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthBank + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthBank = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowBank = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupBank = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/bank/types/codec.go b/x/bank/types/codec.go new file mode 100644 index 000000000000..ef1c897d9759 --- /dev/null +++ b/x/bank/types/codec.go @@ -0,0 +1,52 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/bank/exported" +) + +// RegisterLegacyAminoCodec registers the necessary x/bank interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*exported.SupplyI)(nil), nil) + cdc.RegisterConcrete(&Supply{}, "cosmos-sdk/Supply", nil) + cdc.RegisterConcrete(&MsgSend{}, "cosmos-sdk/MsgSend", nil) + cdc.RegisterConcrete(&MsgMultiSend{}, "cosmos-sdk/MsgMultiSend", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSend{}, + &MsgMultiSend{}, + ) + + registry.RegisterInterface( + "cosmos.bank.v1beta1.SupplyI", + (*exported.SupplyI)(nil), + &Supply{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/bank module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/staking and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/bank/types/errors.go b/x/bank/types/errors.go new file mode 100644 index 000000000000..ab88b40e9b1d --- /dev/null +++ b/x/bank/types/errors.go @@ -0,0 +1,14 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// 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") + ErrDenomMetadataNotFound = sdkerrors.Register(ModuleName, 6, "client denom metadata not found") +) diff --git a/x/bank/internal/types/events.go b/x/bank/types/events.go similarity index 100% rename from x/bank/internal/types/events.go rename to x/bank/types/events.go diff --git a/x/bank/types/expected_keepers.go b/x/bank/types/expected_keepers.go new file mode 100644 index 000000000000..7307864b86e6 --- /dev/null +++ b/x/bank/types/expected_keepers.go @@ -0,0 +1,27 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// AccountKeeper defines the account contract that must be fulfilled when +// creating a x/bank keeper. +type AccountKeeper interface { + NewAccount(sdk.Context, types.AccountI) types.AccountI + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + GetAllAccounts(ctx sdk.Context) []types.AccountI + SetAccount(ctx sdk.Context, acc types.AccountI) + + IterateAccounts(ctx sdk.Context, process func(types.AccountI) bool) + + ValidatePermissions(macc types.ModuleAccountI) error + + GetModuleAddress(moduleName string) sdk.AccAddress + GetModuleAddressAndPermissions(moduleName string) (addr sdk.AccAddress, permissions []string) + GetModuleAccountAndPermissions(ctx sdk.Context, moduleName string) (types.ModuleAccountI, []string) + GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI + SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) +} diff --git a/x/bank/types/genesis.go b/x/bank/types/genesis.go new file mode 100644 index 000000000000..4adc758f3bd9 --- /dev/null +++ b/x/bank/types/genesis.go @@ -0,0 +1,74 @@ +package types + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// 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 + } + + seenBalances := make(map[string]bool) + seenMetadatas := make(map[string]bool) + + for _, balance := range gs.Balances { + if seenBalances[balance.Address] { + return fmt.Errorf("duplicate balance for address %s", balance.Address) + } + + if err := balance.Validate(); err != nil { + return err + } + + seenBalances[balance.Address] = true + } + + for _, metadata := range gs.DenomMetadata { + if seenMetadatas[metadata.Base] { + return fmt.Errorf("duplicate client metadata for denom %s", metadata.Base) + } + + if err := metadata.Validate(); err != nil { + return err + } + + seenMetadatas[metadata.Base] = true + } + + // NOTE: this errors if supply for any given coin is zero + return NewSupply(gs.Supply).ValidateBasic() +} + +// NewGenesisState creates a new genesis state. +func NewGenesisState(params Params, balances []Balance, supply sdk.Coins, denomMetaData []Metadata) *GenesisState { + return &GenesisState{ + Params: params, + Balances: balances, + Supply: supply, + DenomMetadata: denomMetaData, + } +} + +// DefaultGenesisState returns a default bank module genesis state. +func DefaultGenesisState() *GenesisState { + return NewGenesisState(DefaultParams(), []Balance{}, DefaultSupply().GetTotal(), []Metadata{}) +} + +// GetGenesisStateFromAppState returns x/bank GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc codec.JSONMarshaler, appState map[string]json.RawMessage) *GenesisState { + var genesisState GenesisState + + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return &genesisState +} diff --git a/x/bank/types/genesis.pb.go b/x/bank/types/genesis.pb.go new file mode 100644 index 000000000000..f1d124bdd4e6 --- /dev/null +++ b/x/bank/types/genesis.pb.go @@ -0,0 +1,746 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/bank/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + 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 defines the bank module's genesis state. +type GenesisState struct { + // params defines all the paramaters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // balances is an array containing the balances of all the accounts. + Balances []Balance `protobuf:"bytes,2,rep,name=balances,proto3" json:"balances"` + // supply represents the total supply. + Supply github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=supply,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"supply"` + // denom_metadata defines the metadata of the differents coins. + DenomMetadata []Metadata `protobuf:"bytes,4,rep,name=denom_metadata,json=denomMetadata,proto3" json:"denom_metadata" yaml:"denom_metadata"` +} + +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_8f007de11b420c6e, []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) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetBalances() []Balance { + if m != nil { + return m.Balances + } + return nil +} + +func (m *GenesisState) GetSupply() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Supply + } + return nil +} + +func (m *GenesisState) GetDenomMetadata() []Metadata { + if m != nil { + return m.DenomMetadata + } + return nil +} + +// Balance defines an account address and balance pair used in the bank module's +// genesis state. +type Balance struct { + // address is the address of the balance holder. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // coins defines the different coins this balance holds. + Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"` +} + +func (m *Balance) Reset() { *m = Balance{} } +func (m *Balance) String() string { return proto.CompactTextString(m) } +func (*Balance) ProtoMessage() {} +func (*Balance) Descriptor() ([]byte, []int) { + return fileDescriptor_8f007de11b420c6e, []int{1} +} +func (m *Balance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Balance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Balance.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 *Balance) XXX_Merge(src proto.Message) { + xxx_messageInfo_Balance.Merge(m, src) +} +func (m *Balance) XXX_Size() int { + return m.Size() +} +func (m *Balance) XXX_DiscardUnknown() { + xxx_messageInfo_Balance.DiscardUnknown(m) +} + +var xxx_messageInfo_Balance proto.InternalMessageInfo + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.bank.v1beta1.GenesisState") + proto.RegisterType((*Balance)(nil), "cosmos.bank.v1beta1.Balance") +} + +func init() { proto.RegisterFile("cosmos/bank/v1beta1/genesis.proto", fileDescriptor_8f007de11b420c6e) } + +var fileDescriptor_8f007de11b420c6e = []byte{ + // 383 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0x3f, 0x4f, 0xc2, 0x40, + 0x18, 0x87, 0x5b, 0x40, 0xc0, 0x43, 0x1d, 0xaa, 0x26, 0x15, 0xa5, 0xc5, 0x4e, 0x38, 0xd8, 0x0a, + 0x4e, 0x32, 0x38, 0x94, 0xc1, 0xc9, 0xc4, 0xd4, 0xcd, 0xc5, 0x5c, 0xdb, 0x4b, 0x6d, 0xa0, 0xbd, + 0x86, 0x3b, 0x8c, 0x7c, 0x03, 0x47, 0x3e, 0x02, 0xb3, 0x9f, 0x84, 0x91, 0xc4, 0xc5, 0x09, 0x0d, + 0x2c, 0xce, 0x7e, 0x02, 0xd3, 0xbb, 0xa3, 0x6a, 0x24, 0x4e, 0x4e, 0xfd, 0xf3, 0xfe, 0x9e, 0xe7, + 0xbd, 0xf7, 0xee, 0xc0, 0xa1, 0x87, 0x49, 0x84, 0x89, 0xe5, 0xc2, 0xb8, 0x6b, 0xdd, 0x37, 0x5d, + 0x44, 0x61, 0xd3, 0x0a, 0x50, 0x8c, 0x48, 0x48, 0xcc, 0xa4, 0x8f, 0x29, 0x56, 0xb6, 0x79, 0xc4, + 0x4c, 0x23, 0xa6, 0x88, 0x54, 0x77, 0x02, 0x1c, 0x60, 0x56, 0xb7, 0xd2, 0x37, 0x1e, 0xad, 0x6a, + 0x99, 0x8d, 0xa0, 0xcc, 0xe6, 0xe1, 0x30, 0xfe, 0x55, 0xff, 0xd6, 0x8d, 0x79, 0x59, 0xdd, 0x78, + 0xce, 0x81, 0x8d, 0x0b, 0xde, 0xfc, 0x9a, 0x42, 0x8a, 0x94, 0x33, 0x50, 0x4c, 0x60, 0x1f, 0x46, + 0x44, 0x95, 0xeb, 0x72, 0xa3, 0xd2, 0xda, 0x37, 0x57, 0x2c, 0xc6, 0xbc, 0x62, 0x11, 0xbb, 0x30, + 0x99, 0xe9, 0x92, 0x23, 0x00, 0xe5, 0x1c, 0x94, 0x5d, 0xd8, 0x83, 0xb1, 0x87, 0x88, 0x9a, 0xab, + 0xe7, 0x1b, 0x95, 0xd6, 0xc1, 0x4a, 0xd8, 0xe6, 0x21, 0x41, 0x67, 0x8c, 0xe2, 0x81, 0x22, 0x19, + 0x24, 0x49, 0x6f, 0xa8, 0xe6, 0x19, 0xbd, 0xf7, 0x45, 0x13, 0x94, 0xd1, 0x1d, 0x1c, 0xc6, 0xf6, + 0x49, 0x8a, 0x3e, 0xbd, 0xea, 0x8d, 0x20, 0xa4, 0x77, 0x03, 0xd7, 0xf4, 0x70, 0x64, 0x89, 0x49, + 0xf9, 0xe3, 0x98, 0xf8, 0x5d, 0x8b, 0x0e, 0x13, 0x44, 0x18, 0x40, 0x1c, 0xa1, 0x56, 0x3c, 0xb0, + 0xe5, 0xa3, 0x18, 0x47, 0xb7, 0x11, 0xa2, 0xd0, 0x87, 0x14, 0xaa, 0x05, 0xd6, 0xac, 0xb6, 0x72, + 0xa9, 0x97, 0x22, 0x64, 0xd7, 0xd2, 0x86, 0x1f, 0x33, 0x7d, 0x77, 0x08, 0xa3, 0x5e, 0xdb, 0xf8, + 0xa9, 0x30, 0x9c, 0x4d, 0xf6, 0x63, 0x99, 0x36, 0x46, 0x32, 0x28, 0x89, 0x29, 0x15, 0x15, 0x94, + 0xa0, 0xef, 0xf7, 0x11, 0xe1, 0x3b, 0xba, 0xee, 0x2c, 0x3f, 0x15, 0x08, 0xd6, 0xd2, 0x93, 0x5a, + 0x6e, 0xd6, 0xbf, 0x8e, 0xcb, 0xcd, 0xed, 0xf2, 0xe3, 0x58, 0x97, 0xde, 0xc7, 0xba, 0x64, 0x77, + 0x26, 0x73, 0x4d, 0x9e, 0xce, 0x35, 0xf9, 0x6d, 0xae, 0xc9, 0xa3, 0x85, 0x26, 0x4d, 0x17, 0x9a, + 0xf4, 0xb2, 0xd0, 0xa4, 0x9b, 0xa3, 0x3f, 0xa5, 0x0f, 0xfc, 0xea, 0x30, 0xb7, 0x5b, 0x64, 0x97, + 0xe6, 0xf4, 0x33, 0x00, 0x00, 0xff, 0xff, 0x15, 0x28, 0xf0, 0x44, 0xc4, 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.DenomMetadata) > 0 { + for iNdEx := len(m.DenomMetadata) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomMetadata[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Supply) > 0 { + for iNdEx := len(m.Supply) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Supply[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Balances) > 0 { + for iNdEx := len(m.Balances) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Balances[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.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 *Balance) 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 *Balance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Balance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Coins) > 0 { + for iNdEx := len(m.Coins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Coins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + 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 + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.Balances) > 0 { + for _, e := range m.Balances { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Supply) > 0 { + for _, e := range m.Supply { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.DenomMetadata) > 0 { + for _, e := range m.DenomMetadata { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *Balance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.Coins) > 0 { + for _, e := range m.Coins { + 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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + 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 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.Balances = append(m.Balances, Balance{}) + if err := m.Balances[len(m.Balances)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Supply", 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.Supply = append(m.Supply, types.Coin{}) + if err := m.Supply[len(m.Supply)-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 DenomMetadata", 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.DenomMetadata = append(m.DenomMetadata, Metadata{}) + if err := m.DenomMetadata[len(m.DenomMetadata)-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 *Balance) 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: Balance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Balance: 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 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coins", 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.Coins = append(m.Coins, types.Coin{}) + if err := m.Coins[len(m.Coins)-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/bank/types/genesis_test.go b/x/bank/types/genesis_test.go new file mode 100644 index 000000000000..6e8009d83fc5 --- /dev/null +++ b/x/bank/types/genesis_test.go @@ -0,0 +1,152 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func TestGenesisStateValidate(t *testing.T) { + + testCases := []struct { + name string + genesisState types.GenesisState + expErr bool + }{ + { + "valid genesisState", + types.GenesisState{ + Params: types.DefaultParams(), + Balances: []types.Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + }, + Supply: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + DenomMetadata: []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, + }, + {"empty genesisState", types.GenesisState{}, false}, + { + "invalid params ", + types.GenesisState{ + Params: types.Params{ + SendEnabled: []*types.SendEnabled{ + {"", true}, + }, + }, + }, + true, + }, + { + "dup balances", + types.GenesisState{ + Balances: []types.Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{sdk.NewInt64Coin("uatom", 1)}, + }, + }, + }, + true, + }, + { + "0 balance", + types.GenesisState{ + Balances: []types.Balance{ + { + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + }, + }, + }, + false, + }, + { + "dup Metadata", + types.GenesisState{ + DenomMetadata: []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", + }, + { + 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", + }, + }, + }, + true, + }, + { + "invalid Metadata", + types.GenesisState{ + DenomMetadata: []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: "", + Display: "", + }, + }, + }, + true, + }, + { + "invalid supply", + types.GenesisState{ + Supply: sdk.Coins{sdk.Coin{Denom: "", Amount: sdk.OneInt()}}, + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + err := tc.genesisState.Validate() + + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/bank/types/key.go b/x/bank/types/key.go new file mode 100644 index 000000000000..ec8619d00185 --- /dev/null +++ b/x/bank/types/key.go @@ -0,0 +1,46 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +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[:sdk.AddrLen] + if len(addr) != sdk.AddrLen { + panic(fmt.Sprintf("unexpected account address key length; got: %d, expected: %d", len(addr), sdk.AddrLen)) + } + + return sdk.AccAddress(addr) +} diff --git a/x/bank/types/key_test.go b/x/bank/types/key_test.go new file mode 100644 index 000000000000..f3b5717fbecb --- /dev/null +++ b/x/bank/types/key_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +func cloneAppend(bz []byte, tail []byte) (res []byte) { + res = make([]byte, len(bz)+len(tail)) + copy(res, bz) + copy(res[len(bz):], tail) + return +} + +func TestAddressFromBalancesStore(t *testing.T) { + addr, err := sdk.AccAddressFromBech32("cosmos1n88uc38xhjgxzw9nwre4ep2c8ga4fjxcar6mn7") + require.NoError(t, err) + + key := cloneAppend(addr.Bytes(), []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..64241e6098e0 --- /dev/null +++ b/x/bank/types/metadata_test.go @@ -0,0 +1,208 @@ +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, + }, + {"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 new file mode 100644 index 000000000000..22fdc30b3886 --- /dev/null +++ b/x/bank/types/msgs.go @@ -0,0 +1,190 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// bank message types +const ( + TypeMsgSend = "send" + TypeMsgMultiSend = "multisend" +) + +var _ sdk.Msg = &MsgSend{} + +// NewMsgSend - construct a msg to send coins from one account to another. +//nolint:interfacer +func NewMsgSend(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) *MsgSend { + return &MsgSend{FromAddress: fromAddr.String(), ToAddress: toAddr.String(), Amount: amount} +} + +// Route Implements Msg. +func (msg MsgSend) Route() string { return RouterKey } + +// Type Implements Msg. +func (msg MsgSend) Type() string { return TypeMsgSend } + +// ValidateBasic Implements Msg. +func (msg MsgSend) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err) + } + + _, err = sdk.AccAddressFromBech32(msg.ToAddress) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid recipient address (%s)", err) + } + + if !msg.Amount.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) + } + + if !msg.Amount.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) + } + + return nil +} + +// GetSignBytes Implements Msg. +func (msg MsgSend) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// GetSigners Implements Msg. +func (msg MsgSend) GetSigners() []sdk.AccAddress { + from, err := sdk.AccAddressFromBech32(msg.FromAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{from} +} + +var _ sdk.Msg = &MsgMultiSend{} + +// NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. +func NewMsgMultiSend(in []Input, out []Output) *MsgMultiSend { + return &MsgMultiSend{Inputs: in, Outputs: out} +} + +// Route Implements Msg +func (msg MsgMultiSend) Route() string { return RouterKey } + +// Type Implements Msg +func (msg MsgMultiSend) Type() string { return TypeMsgMultiSend } + +// ValidateBasic Implements Msg. +func (msg MsgMultiSend) ValidateBasic() error { + // this just makes sure all the inputs and outputs are properly formatted, + // not that they actually have the money inside + if len(msg.Inputs) == 0 { + return ErrNoInputs + } + + if len(msg.Outputs) == 0 { + return ErrNoOutputs + } + + return ValidateInputsOutputs(msg.Inputs, msg.Outputs) +} + +// GetSignBytes Implements Msg. +func (msg MsgMultiSend) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// GetSigners Implements Msg. +func (msg MsgMultiSend) GetSigners() []sdk.AccAddress { + addrs := make([]sdk.AccAddress, len(msg.Inputs)) + for i, in := range msg.Inputs { + addr, _ := sdk.AccAddressFromBech32(in.Address) + addrs[i] = addr + } + + return addrs +} + +// ValidateBasic - validate transaction input +func (in Input) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(in.Address) + if err != nil { + return err + } + + if !in.Coins.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, in.Coins.String()) + } + + if !in.Coins.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, in.Coins.String()) + } + + return nil +} + +// NewInput - create a transaction input, used with MsgMultiSend +//nolint:interfacer +func NewInput(addr sdk.AccAddress, coins sdk.Coins) Input { + return Input{ + Address: addr.String(), + Coins: coins, + } +} + +// ValidateBasic - validate transaction output +func (out Output) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(out.Address) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid output address (%s)", err) + } + + if !out.Coins.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, out.Coins.String()) + } + + if !out.Coins.IsAllPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, out.Coins.String()) + } + + return nil +} + +// NewOutput - create a transaction output, used with MsgMultiSend +//nolint:interfacer +func NewOutput(addr sdk.AccAddress, coins sdk.Coins) Output { + return Output{ + Address: addr.String(), + Coins: coins, + } +} + +// ValidateInputsOutputs validates that each respective input and output is +// valid and that the sum of inputs is equal to the sum of outputs. +func ValidateInputsOutputs(inputs []Input, outputs []Output) error { + var totalIn, totalOut sdk.Coins + + for _, in := range inputs { + if err := in.ValidateBasic(); err != nil { + return err + } + + totalIn = totalIn.Add(in.Coins...) + } + + for _, out := range outputs { + if err := out.ValidateBasic(); err != nil { + return err + } + + totalOut = totalOut.Add(out.Coins...) + } + + // make sure inputs and outputs match + if !totalIn.IsEqual(totalOut) { + return ErrInputOutputMismatch + } + + return nil +} diff --git a/x/bank/types/msgs_test.go b/x/bank/types/msgs_test.go new file mode 100644 index 000000000000..96132150ad4f --- /dev/null +++ b/x/bank/types/msgs_test.go @@ -0,0 +1,272 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestMsgSendRoute(t *testing.T) { + addr1 := sdk.AccAddress([]byte("from")) + addr2 := sdk.AccAddress([]byte("to")) + coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) + var msg = NewMsgSend(addr1, addr2, coins) + + require.Equal(t, msg.Route(), RouterKey) + require.Equal(t, msg.Type(), "send") +} + +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")) + + atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) + atom0 := sdk.NewCoins(sdk.NewInt64Coin("atom", 0)) + atom123eth123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 123)) + atom123eth0 := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 0)} + + cases := []struct { + expectedErr string // empty means no error expected + msg *MsgSend + }{ + {"", NewMsgSend(addr1, addr2, atom123)}, // valid send + {"", NewMsgSend(addr1, addr2, atom123eth123)}, // valid send with multiple coins + {": 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 { + err := tc.msg.ValidateBasic() + if tc.expectedErr == "" { + require.Nil(t, err) + } else { + require.EqualError(t, err, tc.expectedErr) + } + } +} + +func TestMsgSendGetSignBytes(t *testing.T) { + addr1 := sdk.AccAddress([]byte("input")) + addr2 := sdk.AccAddress([]byte("output")) + coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) + var msg = NewMsgSend(addr1, addr2, coins) + res := msg.GetSignBytes() + + expected := `{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"10","denom":"atom"}],"from_address":"cosmos1d9h8qat57ljhcm","to_address":"cosmos1da6hgur4wsmpnjyg"}}` + require.Equal(t, expected, string(res)) +} + +func TestMsgSendGetSigners(t *testing.T) { + var msg = NewMsgSend(sdk.AccAddress([]byte("input111111111111111")), sdk.AccAddress{}, sdk.NewCoins()) + res := msg.GetSigners() + // TODO: fix this ! + require.Equal(t, fmt.Sprintf("%v", res), "[696E707574313131313131313131313131313131]") +} + +func TestMsgMultiSendRoute(t *testing.T) { + // Construct a MsgSend + addr1 := sdk.AccAddress([]byte("input")) + addr2 := sdk.AccAddress([]byte("output")) + coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) + var msg = MsgMultiSend{ + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, + } + + // TODO some failures for bad result + require.Equal(t, msg.Route(), RouterKey) + require.Equal(t, msg.Type(), "multisend") +} + +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")) + + someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) + multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) + + emptyCoins := sdk.NewCoins() + emptyCoins2 := sdk.NewCoins(sdk.NewInt64Coin("eth", 0)) + someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} + unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} + + cases := []struct { + expectedErr string // empty means no error expected + txIn Input + }{ + // auth works with different apps + {"", NewInput(addr1, someCoins)}, + {"", NewInput(addr2, someCoins)}, + {"", NewInput(addr2, multiCoins)}, + + {"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 + {"1eth,1atom: invalid coins", NewInput(addr1, unsortedCoins)}, // unsorted coins + } + + for i, tc := range cases { + err := tc.txIn.ValidateBasic() + if tc.expectedErr == "" { + require.Nil(t, err, "%d: %+v", i, err) + } else { + require.EqualError(t, err, tc.expectedErr, "%d", i) + } + } +} + +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")) + + someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) + multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20)) + + emptyCoins := sdk.NewCoins() + emptyCoins2 := sdk.NewCoins(sdk.NewInt64Coin("eth", 0)) + someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} + unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} + + cases := []struct { + expectedErr string // empty means no error expected + txOut Output + }{ + // auth works with different apps + {"", NewOutput(addr1, someCoins)}, + {"", NewOutput(addr2, someCoins)}, + {"", NewOutput(addr2, multiCoins)}, + + {"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 + {"1eth,1atom: invalid coins", NewOutput(addr1, unsortedCoins)}, // unsorted coins + } + + for i, tc := range cases { + err := tc.txOut.ValidateBasic() + if tc.expectedErr == "" { + require.Nil(t, err, "%d: %+v", i, err) + } else { + require.EqualError(t, err, tc.expectedErr, "%d", i) + } + } +} + +func TestMsgMultiSendValidation(t *testing.T) { + addr1 := sdk.AccAddress([]byte("_______alice________")) + addr2 := sdk.AccAddress([]byte("________bob_________")) + atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) + atom124 := sdk.NewCoins(sdk.NewInt64Coin("atom", 124)) + eth123 := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) + atom123eth123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 123)) + + input1 := NewInput(addr1, atom123) + input2 := NewInput(addr1, eth123) + output1 := NewOutput(addr2, atom123) + output2 := NewOutput(addr2, atom124) + outputMulti := NewOutput(addr2, atom123eth123) + + var emptyAddr sdk.AccAddress + + cases := []struct { + valid bool + tx MsgMultiSend + }{ + {false, MsgMultiSend{}}, // no input or output + {false, MsgMultiSend{Inputs: []Input{input1}}}, // just input + {false, MsgMultiSend{Outputs: []Output{output1}}}, // just output + {false, MsgMultiSend{ + Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input + Outputs: []Output{output1}}}, + {false, MsgMultiSend{ + Inputs: []Input{input1}, + Outputs: []Output{{emptyAddr.String(), atom123}}}, // invalid output + }, + {false, MsgMultiSend{ + Inputs: []Input{input1}, + Outputs: []Output{output2}}, // amounts dont match + }, + {true, MsgMultiSend{ + Inputs: []Input{input1}, + Outputs: []Output{output1}}, + }, + {true, MsgMultiSend{ + Inputs: []Input{input1, input2}, + Outputs: []Output{outputMulti}}, + }, + } + + for i, tc := range cases { + err := tc.tx.ValidateBasic() + if tc.valid { + require.Nil(t, err, "%d: %+v", i, err) + } else { + require.NotNil(t, err, "%d", i) + } + } +} + +func TestMsgMultiSendGetSignBytes(t *testing.T) { + addr1 := sdk.AccAddress([]byte("input")) + addr2 := sdk.AccAddress([]byte("output")) + coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) + var msg = MsgMultiSend{ + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, + } + res := msg.GetSignBytes() + + expected := `{"type":"cosmos-sdk/MsgMultiSend","value":{"inputs":[{"address":"cosmos1d9h8qat57ljhcm","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmos1da6hgur4wsmpnjyg","coins":[{"amount":"10","denom":"atom"}]}]}}` + require.Equal(t, expected, string(res)) +} + +func TestMsgMultiSendGetSigners(t *testing.T) { + var msg = MsgMultiSend{ + Inputs: []Input{ + NewInput(sdk.AccAddress([]byte("input111111111111111")), nil), + NewInput(sdk.AccAddress([]byte("input222222222222222")), nil), + NewInput(sdk.AccAddress([]byte("input333333333333333")), nil), + }, + } + + res := msg.GetSigners() + // TODO: fix this ! + 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}, + {4, 5, 6}, + {7, 8, 9}, + } + + someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123)) + inputs := make([]Input, len(signers)) + for i, signer := range signers { + inputs[i] = NewInput(signer, someCoins) + } + tx := NewMsgSend(inputs, nil) + + require.Equal(t, signers, tx.Signers()) +} +*/ diff --git a/x/bank/types/params.go b/x/bank/types/params.go new file mode 100644 index 000000000000..ed94ead34765 --- /dev/null +++ b/x/bank/types/params.go @@ -0,0 +1,142 @@ +package types + +import ( + "fmt" + + yaml "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +const ( + // DefaultSendEnabled enabled + DefaultSendEnabled = true +) + +var ( + // KeySendEnabled is store's key for SendEnabled Params + KeySendEnabled = []byte("SendEnabled") + // KeyDefaultSendEnabled is store's key for the DefaultSendEnabled option + KeyDefaultSendEnabled = []byte("DefaultSendEnabled") +) + +// ParamKeyTable for bank module. +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// NewParams creates a new parameter configuration for the bank module +func NewParams(defaultSendEnabled bool, sendEnabledParams SendEnabledParams) Params { + return Params{ + SendEnabled: sendEnabledParams, + DefaultSendEnabled: defaultSendEnabled, + } +} + +// DefaultParams is the default parameter configuration for the bank module +func DefaultParams() Params { + return Params{ + SendEnabled: SendEnabledParams{}, + // The default send enabled value allows send transfers for all coin denoms + DefaultSendEnabled: true, + } +} + +// Validate all bank module parameters +func (p Params) Validate() error { + if err := validateSendEnabledParams(p.SendEnabled); err != nil { + return err + } + return validateIsBool(p.DefaultSendEnabled) +} + +// String implements the Stringer interface. +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} + +// SendEnabledDenom returns true if the given denom is enabled for sending +func (p Params) SendEnabledDenom(denom string) bool { + for _, pse := range p.SendEnabled { + if pse.Denom == denom { + return pse.Enabled + } + } + return p.DefaultSendEnabled +} + +// SetSendEnabledParam returns an updated set of Parameters with the given denom +// send enabled flag set. +func (p Params) SetSendEnabledParam(denom string, sendEnabled bool) Params { + var sendParams SendEnabledParams + for _, p := range p.SendEnabled { + if p.Denom != denom { + sendParams = append(sendParams, NewSendEnabled(p.Denom, p.Enabled)) + } + } + sendParams = append(sendParams, NewSendEnabled(denom, sendEnabled)) + return NewParams(p.DefaultSendEnabled, sendParams) +} + +// ParamSetPairs implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeySendEnabled, &p.SendEnabled, validateSendEnabledParams), + paramtypes.NewParamSetPair(KeyDefaultSendEnabled, &p.DefaultSendEnabled, validateIsBool), + } +} + +// SendEnabledParams is a collection of parameters indicating if a coin denom is enabled for sending +type SendEnabledParams []*SendEnabled + +func validateSendEnabledParams(i interface{}) error { + params, ok := i.([]*SendEnabled) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + // ensure each denom is only registered one time. + registered := make(map[string]bool) + for _, p := range params { + if _, exists := registered[p.Denom]; exists { + return fmt.Errorf("duplicate send enabled parameter found: '%s'", p.Denom) + } + if err := validateSendEnabled(*p); err != nil { + return err + } + registered[p.Denom] = true + } + return nil +} + +// NewSendEnabled creates a new SendEnabled object +// The denom may be left empty to control the global default setting of send_enabled +func NewSendEnabled(denom string, sendEnabled bool) *SendEnabled { + return &SendEnabled{ + Denom: denom, + Enabled: sendEnabled, + } +} + +// String implements stringer insterface +func (se SendEnabled) String() string { + out, _ := yaml.Marshal(se) + return string(out) +} + +func validateSendEnabled(i interface{}) error { + param, ok := i.(SendEnabled) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + return sdk.ValidateDenom(param.Denom) +} + +func validateIsBool(i interface{}) error { + _, ok := i.(bool) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + return nil +} diff --git a/x/bank/types/params_test.go b/x/bank/types/params_test.go new file mode 100644 index 000000000000..f6f95d73737e --- /dev/null +++ b/x/bank/types/params_test.go @@ -0,0 +1,125 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func Test_validateSendEnabledParam(t *testing.T) { + type args struct { + i interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"invalid type", args{sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt())}, true}, + + {"invalid empty denom send enabled", args{*NewSendEnabled("", true)}, true}, + {"invalid empty denom send disabled", args{*NewSendEnabled("", false)}, true}, + + {"valid denom send enabled", args{*NewSendEnabled(sdk.DefaultBondDenom, true)}, false}, + {"valid denom send disabled", args{*NewSendEnabled(sdk.DefaultBondDenom, false)}, false}, + + {"invalid denom send enabled", args{*NewSendEnabled("0FOO", true)}, true}, + {"invalid denom send disabled", args{*NewSendEnabled("0FOO", false)}, true}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantErr, validateSendEnabled(tt.args.i) != nil) + }) + } +} + +func Test_sendParamEqual(t *testing.T) { + paramsA := NewSendEnabled(sdk.DefaultBondDenom, true) + paramsB := NewSendEnabled(sdk.DefaultBondDenom, true) + paramsC := NewSendEnabled("foodenom", false) + + ok := paramsA.Equal(paramsB) + require.True(t, ok) + + ok = paramsA.Equal(paramsC) + require.False(t, ok) +} + +func Test_sendParamString(t *testing.T) { + paramString := "denom: foo\nenabled: false\n" + param := NewSendEnabled("foo", false) + + require.Equal(t, paramString, param.String()) +} + +func Test_validateParams(t *testing.T) { + params := DefaultParams() + + // default params have no error + require.NoError(t, params.Validate()) + + // default case is all denoms are enabled for sending + require.True(t, params.SendEnabledDenom(sdk.DefaultBondDenom)) + require.True(t, params.SendEnabledDenom("foodenom")) + + params.DefaultSendEnabled = false + params = params.SetSendEnabledParam("foodenom", true) + + require.NoError(t, validateSendEnabledParams(params.SendEnabled)) + require.True(t, params.SendEnabledDenom("foodenom")) + require.False(t, params.SendEnabledDenom(sdk.DefaultBondDenom)) + + params.DefaultSendEnabled = true + params = params.SetSendEnabledParam("foodenom", false) + + require.NoError(t, validateSendEnabledParams(params.SendEnabled)) + require.False(t, params.SendEnabledDenom("foodenom")) + require.True(t, params.SendEnabledDenom(sdk.DefaultBondDenom)) + + params = params.SetSendEnabledParam("foodenom", true) + require.True(t, params.SendEnabledDenom("foodenom")) + + params = params.SetSendEnabledParam("foodenom", false) + require.False(t, params.SendEnabledDenom("foodenom")) + + require.True(t, params.SendEnabledDenom("foodenom2")) + params = params.SetSendEnabledParam("foodenom2", false) + require.True(t, params.SendEnabledDenom("")) + require.True(t, params.SendEnabledDenom(sdk.DefaultBondDenom)) + require.False(t, params.SendEnabledDenom("foodenom2")) + + paramYaml := `send_enabled: +- denom: foodenom + enabled: false +- denom: foodenom2 + enabled: false +default_send_enabled: true +` + require.Equal(t, paramYaml, params.String()) + + // Ensure proper format of yaml output when false + params.DefaultSendEnabled = false + paramYaml = `send_enabled: +- denom: foodenom + enabled: false +- denom: foodenom2 + enabled: false +` + require.Equal(t, paramYaml, params.String()) + + params = NewParams(true, SendEnabledParams{ + NewSendEnabled("foodenom", false), + NewSendEnabled("foodenom", true), // this is not allowed + }) + + // fails due to duplicate entries. + require.Error(t, params.Validate()) + + // fails due to invalid type + require.Error(t, validateSendEnabledParams(NewSendEnabled("foodenom", true))) + + require.Error(t, validateSendEnabledParams(SendEnabledParams{NewSendEnabled("INVALIDDENOM", true)})) +} diff --git a/x/bank/types/querier.go b/x/bank/types/querier.go new file mode 100644 index 000000000000..1ef0093386e4 --- /dev/null +++ b/x/bank/types/querier.go @@ -0,0 +1,51 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" +) + +// Querier path constants +const ( + QueryBalance = "balance" + QueryAllBalances = "all_balances" + QueryTotalSupply = "total_supply" + QuerySupplyOf = "supply_of" +) + +// NewQueryBalanceRequest creates a new instance of QueryBalanceRequest. +//nolint:interfacer +func NewQueryBalanceRequest(addr sdk.AccAddress, denom string) *QueryBalanceRequest { + return &QueryBalanceRequest{Address: addr.String(), Denom: denom} +} + +// NewQueryAllBalancesRequest creates a new instance of QueryAllBalancesRequest. +//nolint:interfacer +func NewQueryAllBalancesRequest(addr sdk.AccAddress, req *query.PageRequest) *QueryAllBalancesRequest { + return &QueryAllBalancesRequest{Address: addr.String(), Pagination: req} +} + +// QueryTotalSupplyParams defines the params for the following queries: +// +// - 'custom/bank/totalSupply' +type QueryTotalSupplyParams struct { + Page, Limit int +} + +// NewQueryTotalSupplyParams creates a new instance to query the total supply +func NewQueryTotalSupplyParams(page, limit int) QueryTotalSupplyParams { + return QueryTotalSupplyParams{page, limit} +} + +// QuerySupplyOfParams defines the params for the following queries: +// +// - 'custom/bank/totalSupplyOf' +type QuerySupplyOfParams struct { + Denom string +} + +// NewQuerySupplyOfParams creates a new instance to query the total supply +// of a given denomination +func NewQuerySupplyOfParams(denom string) QuerySupplyOfParams { + return QuerySupplyOfParams{denom} +} diff --git a/x/bank/types/query.pb.go b/x/bank/types/query.pb.go new file mode 100644 index 000000000000..c4bedb0098c5 --- /dev/null +++ b/x/bank/types/query.pb.go @@ -0,0 +1,3078 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/bank/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/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" + _ "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 + +// QueryBalanceRequest is the request type for the Query/Balance RPC method. +type QueryBalanceRequest struct { + // address is the address to query balances for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // denom is the coin denom to query balances for. + Denom string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty"` +} + +func (m *QueryBalanceRequest) Reset() { *m = QueryBalanceRequest{} } +func (m *QueryBalanceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBalanceRequest) ProtoMessage() {} +func (*QueryBalanceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{0} +} +func (m *QueryBalanceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBalanceRequest.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 *QueryBalanceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBalanceRequest.Merge(m, src) +} +func (m *QueryBalanceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBalanceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBalanceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBalanceRequest proto.InternalMessageInfo + +// QueryBalanceResponse is the response type for the Query/Balance RPC method. +type QueryBalanceResponse struct { + // balance is the balance of the coin. + Balance *types.Coin `protobuf:"bytes,1,opt,name=balance,proto3" json:"balance,omitempty"` +} + +func (m *QueryBalanceResponse) Reset() { *m = QueryBalanceResponse{} } +func (m *QueryBalanceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBalanceResponse) ProtoMessage() {} +func (*QueryBalanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{1} +} +func (m *QueryBalanceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBalanceResponse.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 *QueryBalanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBalanceResponse.Merge(m, src) +} +func (m *QueryBalanceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBalanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBalanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBalanceResponse proto.InternalMessageInfo + +func (m *QueryBalanceResponse) GetBalance() *types.Coin { + if m != nil { + return m.Balance + } + return nil +} + +// QueryBalanceRequest is the request type for the Query/AllBalances RPC method. +type QueryAllBalancesRequest struct { + // address is the address to query balances for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllBalancesRequest) Reset() { *m = QueryAllBalancesRequest{} } +func (m *QueryAllBalancesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllBalancesRequest) ProtoMessage() {} +func (*QueryAllBalancesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{2} +} +func (m *QueryAllBalancesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllBalancesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllBalancesRequest.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 *QueryAllBalancesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllBalancesRequest.Merge(m, src) +} +func (m *QueryAllBalancesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllBalancesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllBalancesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllBalancesRequest proto.InternalMessageInfo + +// QueryAllBalancesResponse is the response type for the Query/AllBalances RPC +// method. +type QueryAllBalancesResponse struct { + // balances is the balances of all the coins. + Balances github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=balances,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"balances"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllBalancesResponse) Reset() { *m = QueryAllBalancesResponse{} } +func (m *QueryAllBalancesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllBalancesResponse) ProtoMessage() {} +func (*QueryAllBalancesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{3} +} +func (m *QueryAllBalancesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllBalancesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllBalancesResponse.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 *QueryAllBalancesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllBalancesResponse.Merge(m, src) +} +func (m *QueryAllBalancesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllBalancesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllBalancesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllBalancesResponse proto.InternalMessageInfo + +func (m *QueryAllBalancesResponse) GetBalances() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Balances + } + return nil +} + +func (m *QueryAllBalancesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryTotalSupplyRequest is the request type for the Query/TotalSupply RPC +// method. +type QueryTotalSupplyRequest struct { +} + +func (m *QueryTotalSupplyRequest) Reset() { *m = QueryTotalSupplyRequest{} } +func (m *QueryTotalSupplyRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTotalSupplyRequest) ProtoMessage() {} +func (*QueryTotalSupplyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{4} +} +func (m *QueryTotalSupplyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalSupplyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalSupplyRequest.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 *QueryTotalSupplyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalSupplyRequest.Merge(m, src) +} +func (m *QueryTotalSupplyRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalSupplyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalSupplyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalSupplyRequest proto.InternalMessageInfo + +// QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC +// method +type QueryTotalSupplyResponse struct { + // supply is the supply of the coins + Supply github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=supply,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"supply"` +} + +func (m *QueryTotalSupplyResponse) Reset() { *m = QueryTotalSupplyResponse{} } +func (m *QueryTotalSupplyResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTotalSupplyResponse) ProtoMessage() {} +func (*QueryTotalSupplyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{5} +} +func (m *QueryTotalSupplyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTotalSupplyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTotalSupplyResponse.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 *QueryTotalSupplyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTotalSupplyResponse.Merge(m, src) +} +func (m *QueryTotalSupplyResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTotalSupplyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTotalSupplyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTotalSupplyResponse proto.InternalMessageInfo + +func (m *QueryTotalSupplyResponse) GetSupply() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Supply + } + return nil +} + +// QuerySupplyOfRequest is the request type for the Query/SupplyOf RPC method. +type QuerySupplyOfRequest struct { + // denom is the coin denom to query balances for. + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` +} + +func (m *QuerySupplyOfRequest) Reset() { *m = QuerySupplyOfRequest{} } +func (m *QuerySupplyOfRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySupplyOfRequest) ProtoMessage() {} +func (*QuerySupplyOfRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{6} +} +func (m *QuerySupplyOfRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySupplyOfRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySupplyOfRequest.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 *QuerySupplyOfRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySupplyOfRequest.Merge(m, src) +} +func (m *QuerySupplyOfRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySupplyOfRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySupplyOfRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySupplyOfRequest proto.InternalMessageInfo + +func (m *QuerySupplyOfRequest) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +// QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. +type QuerySupplyOfResponse struct { + // amount is the supply of the coin. + Amount types.Coin `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount"` +} + +func (m *QuerySupplyOfResponse) Reset() { *m = QuerySupplyOfResponse{} } +func (m *QuerySupplyOfResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySupplyOfResponse) ProtoMessage() {} +func (*QuerySupplyOfResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{7} +} +func (m *QuerySupplyOfResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySupplyOfResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySupplyOfResponse.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 *QuerySupplyOfResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySupplyOfResponse.Merge(m, src) +} +func (m *QuerySupplyOfResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySupplyOfResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySupplyOfResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySupplyOfResponse proto.InternalMessageInfo + +func (m *QuerySupplyOfResponse) GetAmount() types.Coin { + if m != nil { + return m.Amount + } + return types.Coin{} +} + +// QueryParamsRequest defines the request type for querying x/bank parameters. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{8} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse defines the response type for querying x/bank parameters. +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9c6fc1939682df13, []int{9} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.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") + proto.RegisterType((*QueryAllBalancesRequest)(nil), "cosmos.bank.v1beta1.QueryAllBalancesRequest") + proto.RegisterType((*QueryAllBalancesResponse)(nil), "cosmos.bank.v1beta1.QueryAllBalancesResponse") + proto.RegisterType((*QueryTotalSupplyRequest)(nil), "cosmos.bank.v1beta1.QueryTotalSupplyRequest") + proto.RegisterType((*QueryTotalSupplyResponse)(nil), "cosmos.bank.v1beta1.QueryTotalSupplyResponse") + proto.RegisterType((*QuerySupplyOfRequest)(nil), "cosmos.bank.v1beta1.QuerySupplyOfRequest") + 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{ + // 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. +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 { + // Balance queries the balance of a single coin for a single account. + Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error) + // AllBalances queries the balance of all coins for a single account. + AllBalances(ctx context.Context, in *QueryAllBalancesRequest, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) + // TotalSupply queries the total supply of all coins. + TotalSupply(ctx context.Context, in *QueryTotalSupplyRequest, opts ...grpc.CallOption) (*QueryTotalSupplyResponse, error) + // SupplyOf queries the supply of a single coin. + 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 { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error) { + out := new(QueryBalanceResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/Balance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) AllBalances(ctx context.Context, in *QueryAllBalancesRequest, opts ...grpc.CallOption) (*QueryAllBalancesResponse, error) { + out := new(QueryAllBalancesResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/AllBalances", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TotalSupply(ctx context.Context, in *QueryTotalSupplyRequest, opts ...grpc.CallOption) (*QueryTotalSupplyResponse, error) { + out := new(QueryTotalSupplyResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/TotalSupply", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) SupplyOf(ctx context.Context, in *QuerySupplyOfRequest, opts ...grpc.CallOption) (*QuerySupplyOfResponse, error) { + out := new(QuerySupplyOfResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/SupplyOf", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + 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. + Balance(context.Context, *QueryBalanceRequest) (*QueryBalanceResponse, error) + // AllBalances queries the balance of all coins for a single account. + AllBalances(context.Context, *QueryAllBalancesRequest) (*QueryAllBalancesResponse, error) + // TotalSupply queries the total supply of all coins. + TotalSupply(context.Context, *QueryTotalSupplyRequest) (*QueryTotalSupplyResponse, error) + // SupplyOf queries the supply of a single coin. + 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. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Balance(ctx context.Context, req *QueryBalanceRequest) (*QueryBalanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented") +} +func (*UnimplementedQueryServer) AllBalances(ctx context.Context, req *QueryAllBalancesRequest) (*QueryAllBalancesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllBalances not implemented") +} +func (*UnimplementedQueryServer) TotalSupply(ctx context.Context, req *QueryTotalSupplyRequest) (*QueryTotalSupplyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TotalSupply not implemented") +} +func (*UnimplementedQueryServer) SupplyOf(ctx context.Context, req *QuerySupplyOfRequest) (*QuerySupplyOfResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SupplyOf not implemented") +} +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) +} + +func _Query_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBalanceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Balance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/Balance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Balance(ctx, req.(*QueryBalanceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_AllBalances_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllBalancesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllBalances(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/AllBalances", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllBalances(ctx, req.(*QueryAllBalancesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TotalSupply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTotalSupplyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TotalSupply(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/TotalSupply", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TotalSupply(ctx, req.(*QueryTotalSupplyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_SupplyOf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySupplyOfRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).SupplyOf(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/SupplyOf", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).SupplyOf(ctx, req.(*QuerySupplyOfRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + 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), + Methods: []grpc.MethodDesc{ + { + MethodName: "Balance", + Handler: _Query_Balance_Handler, + }, + { + MethodName: "AllBalances", + Handler: _Query_AllBalances_Handler, + }, + { + MethodName: "TotalSupply", + Handler: _Query_TotalSupply_Handler, + }, + { + MethodName: "SupplyOf", + Handler: _Query_SupplyOf_Handler, + }, + { + 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", +} + +func (m *QueryBalanceRequest) 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 *QueryBalanceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBalanceRequest) 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] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBalanceResponse) 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 *QueryBalanceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Balance != nil { + { + size, err := m.Balance.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 *QueryAllBalancesRequest) 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 *QueryAllBalancesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllBalancesRequest) 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.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAllBalancesResponse) 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 *QueryAllBalancesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllBalancesResponse) 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.Balances) > 0 { + for iNdEx := len(m.Balances) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Balances[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 *QueryTotalSupplyRequest) 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 *QueryTotalSupplyRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalSupplyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryTotalSupplyResponse) 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 *QueryTotalSupplyResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTotalSupplyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Supply) > 0 { + for iNdEx := len(m.Supply) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Supply[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 *QuerySupplyOfRequest) 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 *QuerySupplyOfRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySupplyOfRequest) 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 *QuerySupplyOfResponse) 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 *QuerySupplyOfResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySupplyOfResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.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 *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *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 dAtA[:n], nil +} + +func (m *QueryDenomsMetadataRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomsMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *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 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.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) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryTotalSupplyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Supply) > 0 { + for _, e := range m.Supply { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QuerySupplyOfRequest) 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 *QuerySupplyOfResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Amount.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + 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 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryBalanceRequest) 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: QueryBalanceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBalanceRequest: 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 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) || (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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTotalSupplyResponse) 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: QueryTotalSupplyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Supply", 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.Supply = append(m.Supply, types.Coin{}) + if err := m.Supply[len(m.Supply)-1].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 *QuerySupplyOfRequest) 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: QuerySupplyOfRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 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) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QuerySupplyOfResponse) 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: QuerySupplyOfResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Amount", 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 err := m.Amount.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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 *QueryDenomsMetadataRequest) 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: QueryDenomsMetadataRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 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 *QueryDenomsMetadataResponse) 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: QueryDenomsMetadataResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Metadatas", 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.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 + 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 *QueryDenomMetadataRequest) 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: QueryDenomMetadataRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryDenomMetadataResponse) 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: QueryDenomMetadataResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Metadata", 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 err := m.Metadata.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/bank/types/query.pb.gw.go b/x/bank/types/query.pb.gw.go new file mode 100644 index 000000000000..06c7e2d04aa5 --- /dev/null +++ b/x/bank/types/query.pb.gw.go @@ -0,0 +1,722 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/bank/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_Balance_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBalanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", 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.Balance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Balance_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBalanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", 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.Balance(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_AllBalances_0 = &utilities.DoubleArray{Encoding: map[string]int{"address": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_AllBalances_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllBalancesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", 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_AllBalances_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.AllBalances(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AllBalances_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllBalancesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", 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_AllBalances_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.AllBalances(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_TotalSupply_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalSupplyRequest + var metadata runtime.ServerMetadata + + msg, err := client.TotalSupply(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TotalSupply_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTotalSupplyRequest + var metadata runtime.ServerMetadata + + msg, err := server.TotalSupply(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_SupplyOf_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySupplyOfRequest + 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.SupplyOf(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_SupplyOf_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySupplyOfRequest + 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.SupplyOf(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +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. +// 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_Balance_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_Balance_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_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllBalances_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_AllBalances_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_AllBalances_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalSupply_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_TotalSupply_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_TotalSupply_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SupplyOf_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_SupplyOf_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_SupplyOf_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + 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 +} + +// 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_Balance_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_Balance_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_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllBalances_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_AllBalances_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_AllBalances_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TotalSupply_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_TotalSupply_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_TotalSupply_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SupplyOf_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_SupplyOf_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_SupplyOf_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + 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 +} + +var ( + pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "bank", "v1beta1", "balances", "address", "denom"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_AllBalances_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "bank", "v1beta1", "balances", "address"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_TotalSupply_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "bank", "v1beta1", "supply"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_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 ( + forward_Query_Balance_0 = runtime.ForwardResponseMessage + + forward_Query_AllBalances_0 = runtime.ForwardResponseMessage + + forward_Query_TotalSupply_0 = runtime.ForwardResponseMessage + + 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 new file mode 100644 index 000000000000..c5c14b15bc2c --- /dev/null +++ b/x/bank/types/supply.go @@ -0,0 +1,58 @@ +package types + +import ( + "fmt" + + yaml "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank/exported" +) + +// Implements Delegation interface +var _ exported.SupplyI = (*Supply)(nil) + +// NewSupply creates a new Supply instance +func NewSupply(total sdk.Coins) *Supply { + return &Supply{total} +} + +// DefaultSupply creates an empty Supply +func DefaultSupply() *Supply { + return NewSupply(sdk.NewCoins()) +} + +// SetTotal sets the total supply. +func (supply *Supply) SetTotal(total sdk.Coins) { + supply.Total = total +} + +// GetTotal returns the supply total. +func (supply Supply) GetTotal() sdk.Coins { + return supply.Total +} + +// Inflate adds coins to the total supply +func (supply *Supply) Inflate(amount sdk.Coins) { + supply.Total = supply.Total.Add(amount...) +} + +// Deflate subtracts coins from the total supply. +func (supply *Supply) Deflate(amount sdk.Coins) { + supply.Total = supply.Total.Sub(amount) +} + +// String returns a human readable string representation of a supplier. +func (supply Supply) String() string { + bz, _ := yaml.Marshal(supply) + return string(bz) +} + +// ValidateBasic validates the Supply coins and returns error if invalid +func (supply Supply) ValidateBasic() error { + if err := supply.Total.Validate(); err != nil { + return fmt.Errorf("invalid total supply: %w", err) + } + + return nil +} diff --git a/x/supply/internal/types/supply_test.go b/x/bank/types/supply_test.go similarity index 94% rename from x/supply/internal/types/supply_test.go rename to x/bank/types/supply_test.go index e99e1e5aea49..6fff2182c824 100644 --- a/x/supply/internal/types/supply_test.go +++ b/x/bank/types/supply_test.go @@ -4,17 +4,16 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/require" yaml "gopkg.in/yaml.v2" sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/stretchr/testify/require" ) func TestSupplyMarshalYAML(t *testing.T) { supply := DefaultSupply() coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt())) - supply = supply.Inflate(coins) + supply.Inflate(coins) bz, err := yaml.Marshal(supply) require.NoError(t, err) diff --git a/x/bank/types/tx.pb.go b/x/bank/types/tx.pb.go new file mode 100644 index 000000000000..6dd52c4b64d8 --- /dev/null +++ b/x/bank/types/tx.pb.go @@ -0,0 +1,1037 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/bank/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgSend represents a message to send coins from one account to another. +type MsgSend struct { + FromAddress string `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty" yaml:"from_address"` + ToAddress string `protobuf:"bytes,2,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty" yaml:"to_address"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgSend) Reset() { *m = MsgSend{} } +func (m *MsgSend) String() string { return proto.CompactTextString(m) } +func (*MsgSend) ProtoMessage() {} +func (*MsgSend) Descriptor() ([]byte, []int) { + return fileDescriptor_1d8cb1613481f5b7, []int{0} +} +func (m *MsgSend) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSend.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 *MsgSend) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSend.Merge(m, src) +} +func (m *MsgSend) XXX_Size() int { + return m.Size() +} +func (m *MsgSend) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSend.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSend proto.InternalMessageInfo + +// MsgSendResponse defines the Msg/Send response type. +type MsgSendResponse struct { +} + +func (m *MsgSendResponse) Reset() { *m = MsgSendResponse{} } +func (m *MsgSendResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSendResponse) ProtoMessage() {} +func (*MsgSendResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1d8cb1613481f5b7, []int{1} +} +func (m *MsgSendResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendResponse.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 *MsgSendResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendResponse.Merge(m, src) +} +func (m *MsgSendResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSendResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendResponse proto.InternalMessageInfo + +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. +type MsgMultiSend struct { + Inputs []Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs"` + Outputs []Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs"` +} + +func (m *MsgMultiSend) Reset() { *m = MsgMultiSend{} } +func (m *MsgMultiSend) String() string { return proto.CompactTextString(m) } +func (*MsgMultiSend) ProtoMessage() {} +func (*MsgMultiSend) Descriptor() ([]byte, []int) { + return fileDescriptor_1d8cb1613481f5b7, []int{2} +} +func (m *MsgMultiSend) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMultiSend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMultiSend.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 *MsgMultiSend) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMultiSend.Merge(m, src) +} +func (m *MsgMultiSend) XXX_Size() int { + return m.Size() +} +func (m *MsgMultiSend) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMultiSend.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMultiSend proto.InternalMessageInfo + +func (m *MsgMultiSend) GetInputs() []Input { + if m != nil { + return m.Inputs + } + return nil +} + +func (m *MsgMultiSend) GetOutputs() []Output { + if m != nil { + return m.Outputs + } + return nil +} + +// MsgMultiSendResponse defines the Msg/MultiSend response type. +type MsgMultiSendResponse struct { +} + +func (m *MsgMultiSendResponse) Reset() { *m = MsgMultiSendResponse{} } +func (m *MsgMultiSendResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMultiSendResponse) ProtoMessage() {} +func (*MsgMultiSendResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1d8cb1613481f5b7, []int{3} +} +func (m *MsgMultiSendResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMultiSendResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMultiSendResponse.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 *MsgMultiSendResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMultiSendResponse.Merge(m, src) +} +func (m *MsgMultiSendResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMultiSendResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMultiSendResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMultiSendResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSend)(nil), "cosmos.bank.v1beta1.MsgSend") + proto.RegisterType((*MsgSendResponse)(nil), "cosmos.bank.v1beta1.MsgSendResponse") + proto.RegisterType((*MsgMultiSend)(nil), "cosmos.bank.v1beta1.MsgMultiSend") + proto.RegisterType((*MsgMultiSendResponse)(nil), "cosmos.bank.v1beta1.MsgMultiSendResponse") +} + +func init() { proto.RegisterFile("cosmos/bank/v1beta1/tx.proto", fileDescriptor_1d8cb1613481f5b7) } + +var fileDescriptor_1d8cb1613481f5b7 = []byte{ + // 436 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x3f, 0xef, 0xd2, 0x40, + 0x1c, 0xc6, 0x7b, 0x3f, 0x08, 0x3f, 0x39, 0x48, 0x0c, 0x05, 0x15, 0x2b, 0x69, 0xb1, 0x71, 0x80, + 0xc1, 0xab, 0xa0, 0x83, 0xa9, 0x93, 0x65, 0xd2, 0xa4, 0x31, 0xa9, 0x93, 0x2e, 0xa6, 0x7f, 0xce, + 0xda, 0x40, 0x7b, 0x0d, 0x77, 0x35, 0xf0, 0x0e, 0x4c, 0x5c, 0x7c, 0x09, 0xcc, 0xc6, 0x17, 0xc2, + 0xc8, 0xe8, 0x84, 0x06, 0x16, 0xe3, 0xc8, 0x2b, 0x30, 0xbd, 0xfe, 0x81, 0x44, 0xc4, 0xa9, 0xbd, + 0x3c, 0xdf, 0xcf, 0xd3, 0xe7, 0xe9, 0xf7, 0x60, 0xcf, 0x25, 0x34, 0x24, 0x54, 0x73, 0xec, 0x68, + 0xaa, 0x7d, 0x1c, 0x39, 0x98, 0xd9, 0x23, 0x8d, 0x2d, 0x50, 0x3c, 0x27, 0x8c, 0x88, 0xed, 0x4c, + 0x45, 0xa9, 0x8a, 0x72, 0x55, 0xea, 0xf8, 0xc4, 0x27, 0x5c, 0xd7, 0xd2, 0xb7, 0x6c, 0x54, 0x92, + 0x4b, 0x23, 0x8a, 0x4b, 0x23, 0x97, 0x04, 0xd1, 0x5f, 0xfa, 0xc9, 0x87, 0xb8, 0x2f, 0xd7, 0xd5, + 0xdf, 0x00, 0x5e, 0x9b, 0xd4, 0x7f, 0x8d, 0x23, 0x4f, 0xd4, 0x61, 0xf3, 0xfd, 0x9c, 0x84, 0xef, + 0x6c, 0xcf, 0x9b, 0x63, 0x4a, 0xbb, 0xa0, 0x0f, 0x06, 0x75, 0xe3, 0xce, 0x61, 0xab, 0xb4, 0x97, + 0x76, 0x38, 0xd3, 0xd5, 0x53, 0x55, 0xb5, 0x1a, 0xe9, 0xf1, 0x79, 0x76, 0x12, 0x9f, 0x40, 0xc8, + 0x48, 0x49, 0x5e, 0x71, 0xf2, 0xd6, 0x61, 0xab, 0xb4, 0x32, 0xf2, 0xa8, 0xa9, 0x56, 0x9d, 0x91, + 0x82, 0x72, 0x61, 0xcd, 0x0e, 0x49, 0x12, 0xb1, 0x6e, 0xa5, 0x5f, 0x19, 0x34, 0xc6, 0x77, 0x51, + 0xd9, 0x9c, 0xe2, 0xa2, 0x39, 0x9a, 0x90, 0x20, 0x32, 0x1e, 0xad, 0xb7, 0x8a, 0xf0, 0xf5, 0x87, + 0x32, 0xf0, 0x03, 0xf6, 0x21, 0x71, 0x90, 0x4b, 0x42, 0x2d, 0xef, 0x96, 0x3d, 0x1e, 0x52, 0x6f, + 0xaa, 0xb1, 0x65, 0x8c, 0x29, 0x07, 0xa8, 0x95, 0x5b, 0xeb, 0x37, 0x3e, 0xad, 0x14, 0xe1, 0xd7, + 0x4a, 0x11, 0xd4, 0x16, 0xbc, 0x99, 0x77, 0xb5, 0x30, 0x8d, 0x49, 0x44, 0xb1, 0xfa, 0x19, 0xc0, + 0xa6, 0x49, 0x7d, 0x33, 0x99, 0xb1, 0x80, 0xff, 0x84, 0xa7, 0xb0, 0x16, 0x44, 0x71, 0xc2, 0xd2, + 0xfa, 0x69, 0x24, 0x09, 0x9d, 0x59, 0x06, 0x7a, 0x91, 0x8e, 0x18, 0xd5, 0x34, 0x93, 0x95, 0xcf, + 0x8b, 0xcf, 0xe0, 0x35, 0x49, 0x18, 0x47, 0xaf, 0x38, 0x7a, 0xef, 0x2c, 0xfa, 0x8a, 0xcf, 0xe4, + 0x6c, 0x41, 0xe8, 0x55, 0x1e, 0xf0, 0x36, 0xec, 0x9c, 0x86, 0x29, 0x52, 0x8e, 0xbf, 0x01, 0x58, + 0x31, 0xa9, 0x2f, 0xbe, 0x84, 0x55, 0x1e, 0xb2, 0x77, 0xd6, 0x39, 0xef, 0x26, 0x3d, 0xb8, 0xa4, + 0x16, 0x9e, 0xe2, 0x1b, 0x58, 0x3f, 0xb6, 0xbe, 0xff, 0x2f, 0xa4, 0x1c, 0x91, 0x86, 0xff, 0x1d, + 0x29, 0xac, 0x8d, 0xc9, 0x7a, 0x27, 0x83, 0xcd, 0x4e, 0x06, 0x3f, 0x77, 0x32, 0xf8, 0xb2, 0x97, + 0x85, 0xcd, 0x5e, 0x16, 0xbe, 0xef, 0x65, 0xe1, 0xed, 0xf0, 0xe2, 0xf6, 0x16, 0xd9, 0x35, 0xe5, + 0x4b, 0x74, 0x6a, 0xfc, 0x82, 0x3e, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x78, 0xdc, 0x7c, 0x0b, + 0x2b, 0x03, 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 { + // Send defines a method for sending coins from one account to another account. + Send(ctx context.Context, in *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) + // MultiSend defines a method for sending coins from some accounts to other accounts. + MultiSend(ctx context.Context, in *MsgMultiSend, opts ...grpc.CallOption) (*MsgMultiSendResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) Send(ctx context.Context, in *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) { + out := new(MsgSendResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Msg/Send", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) MultiSend(ctx context.Context, in *MsgMultiSend, opts ...grpc.CallOption) (*MsgMultiSendResponse, error) { + out := new(MsgMultiSendResponse) + err := c.cc.Invoke(ctx, "/cosmos.bank.v1beta1.Msg/MultiSend", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // Send defines a method for sending coins from one account to another account. + Send(context.Context, *MsgSend) (*MsgSendResponse, error) + // MultiSend defines a method for sending coins from some accounts to other accounts. + MultiSend(context.Context, *MsgMultiSend) (*MsgMultiSendResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) Send(ctx context.Context, req *MsgSend) (*MsgSendResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Send not implemented") +} +func (*UnimplementedMsgServer) MultiSend(ctx context.Context, req *MsgMultiSend) (*MsgMultiSendResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MultiSend not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSend) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Send(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Msg/Send", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Send(ctx, req.(*MsgSend)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_MultiSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMultiSend) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MultiSend(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.bank.v1beta1.Msg/MultiSend", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MultiSend(ctx, req.(*MsgMultiSend)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.bank.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Send", + Handler: _Msg_Send_Handler, + }, + { + MethodName: "MultiSend", + Handler: _Msg_MultiSend_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/bank/v1beta1/tx.proto", +} + +func (m *MsgSend) 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 *MsgSend) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSend) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[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.ToAddress) > 0 { + i -= len(m.ToAddress) + copy(dAtA[i:], m.ToAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.FromAddress) > 0 { + i -= len(m.FromAddress) + copy(dAtA[i:], m.FromAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSendResponse) 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 *MsgSendResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgMultiSend) 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 *MsgMultiSend) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMultiSend) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Outputs) > 0 { + for iNdEx := len(m.Outputs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Outputs[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.Inputs) > 0 { + for iNdEx := len(m.Inputs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Inputs[iNdEx].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 *MsgMultiSendResponse) 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 *MsgMultiSendResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMultiSendResponse) 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 *MsgSend) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FromAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ToAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgSendResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgMultiSend) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Inputs) > 0 { + for _, e := range m.Inputs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + if len(m.Outputs) > 0 { + for _, e := range m.Outputs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgMultiSendResponse) 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 *MsgSend) 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: MsgSend: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSend: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromAddress", 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.FromAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddress", 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.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-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 *MsgSendResponse) 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: MsgSendResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendResponse: 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 *MsgMultiSend) 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: MsgMultiSend: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMultiSend: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inputs", 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.Inputs = append(m.Inputs, Input{}) + if err := m.Inputs[len(m.Inputs)-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 Outputs", 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.Outputs = append(m.Outputs, Output{}) + if err := m.Outputs[len(m.Outputs)-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 *MsgMultiSendResponse) 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: MsgMultiSendResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMultiSendResponse: 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/capability/genesis.go b/x/capability/genesis.go new file mode 100644 index 000000000000..ba8e09dcd375 --- /dev/null +++ b/x/capability/genesis.go @@ -0,0 +1,45 @@ +package capability + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// InitGenesis initializes the capability module's state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { + if err := k.InitializeIndex(ctx, genState.Index); err != nil { + panic(err) + } + + // set owners for each index and initialize capability + for _, genOwner := range genState.Owners { + k.SetOwners(ctx, genOwner.Index, genOwner.IndexOwners) + k.InitializeCapability(ctx, genOwner.Index, genOwner.IndexOwners) + } +} + +// ExportGenesis returns the capability module's exported genesis. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + index := k.GetLatestIndex(ctx) + owners := []types.GenesisOwners{} + + for i := uint64(1); i < index; i++ { + capabilityOwners, ok := k.GetOwners(ctx, i) + if !ok || len(capabilityOwners.Owners) == 0 { + continue + } + + genOwner := types.GenesisOwners{ + Index: i, + IndexOwners: capabilityOwners, + } + owners = append(owners, genOwner) + } + + return &types.GenesisState{ + Index: index, + Owners: owners, + } +} diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go new file mode 100644 index 000000000000..61de0338991c --- /dev/null +++ b/x/capability/keeper/keeper.go @@ -0,0 +1,468 @@ +package keeper + +import ( + "fmt" + "strings" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +type ( + // Keeper defines the capability module's keeper. It is responsible for provisioning, + // tracking, and authenticating capabilities at runtime. During application + // initialization, the keeper can be hooked up to modules through unique function + // references so that it can identify the calling module when later invoked. + // + // When the initial state is loaded from disk, the keeper allows the ability to + // create new capability keys for all previously allocated capability identifiers + // (allocated during execution of past transactions and assigned to particular modes), + // and keep them in a memory-only store while the chain is running. + // + // The keeper allows the ability to create scoped sub-keepers which are tied to + // a single specific module. + Keeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + memKey sdk.StoreKey + capMap map[uint64]*types.Capability + scopedModules map[string]struct{} + sealed bool + } + + // ScopedKeeper defines a scoped sub-keeper which is tied to a single specific + // module provisioned by the capability keeper. Scoped keepers must be created + // at application initialization and passed to modules, which can then use them + // to claim capabilities they receive and retrieve capabilities which they own + // by name, in addition to creating new capabilities & authenticating capabilities + // passed by other modules. + ScopedKeeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + memKey sdk.StoreKey + capMap map[uint64]*types.Capability + module string + } +) + +// NewKeeper constructs a new CapabilityKeeper instance and initializes maps +// for capability map and scopedModules map. +func NewKeeper(cdc codec.BinaryMarshaler, storeKey, memKey sdk.StoreKey) *Keeper { + return &Keeper{ + cdc: cdc, + storeKey: storeKey, + memKey: memKey, + capMap: make(map[uint64]*types.Capability), + scopedModules: make(map[string]struct{}), + sealed: false, + } +} + +// ScopeToModule attempts to create and return a ScopedKeeper for a given module +// by name. It will panic if the keeper is already sealed or if the module name +// already has a ScopedKeeper. +func (k *Keeper) ScopeToModule(moduleName string) ScopedKeeper { + if k.sealed { + panic("cannot scope to module via a sealed capability keeper") + } + if strings.TrimSpace(moduleName) == "" { + panic("cannot scope to an empty module name") + } + + if _, ok := k.scopedModules[moduleName]; ok { + panic(fmt.Sprintf("cannot create multiple scoped keepers for the same module name: %s", moduleName)) + } + + k.scopedModules[moduleName] = struct{}{} + + return ScopedKeeper{ + cdc: k.cdc, + storeKey: k.storeKey, + memKey: k.memKey, + capMap: k.capMap, + module: moduleName, + } +} + +// InitializeAndSeal loads all capabilities from the persistent KVStore into the +// in-memory store and seals the keeper to prevent further modules from creating +// a scoped keeper. InitializeAndSeal must be called once after the application +// state is loaded. +func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { + if k.sealed { + panic("cannot initialize and seal an already sealed capability keeper") + } + + memStore := ctx.KVStore(k.memKey) + memStoreType := memStore.GetStoreType() + + if memStoreType != sdk.StoreTypeMemory { + panic(fmt.Sprintf("invalid memory store type; got %s, expected: %s", memStoreType, sdk.StoreTypeMemory)) + } + + prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) + iterator := sdk.KVStorePrefixIterator(prefixStore, nil) + + // initialize the in-memory store for all persisted capabilities + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + index := types.IndexFromKey(iterator.Key()) + + var capOwners types.CapabilityOwners + + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &capOwners) + k.InitializeCapability(ctx, index, capOwners) + } + + k.sealed = true +} + +// InitializeIndex sets the index to one (or greater) in InitChain according +// to the GenesisState. It must only be called once. +// It will panic if the provided index is 0, or if the index is already set. +func (k Keeper) InitializeIndex(ctx sdk.Context, index uint64) error { + if index == 0 { + panic("SetIndex requires index > 0") + } + latest := k.GetLatestIndex(ctx) + if latest > 0 { + panic("SetIndex requires index to not be set") + } + + // set the global index to the passed index + store := ctx.KVStore(k.storeKey) + store.Set(types.KeyIndex, types.IndexToKey(index)) + return nil +} + +// GetLatestIndex returns the latest index of the CapabilityKeeper +func (k Keeper) GetLatestIndex(ctx sdk.Context) uint64 { + store := ctx.KVStore(k.storeKey) + return types.IndexFromKey(store.Get(types.KeyIndex)) +} + +// SetOwners set the capability owners to the store +func (k Keeper) SetOwners(ctx sdk.Context, index uint64, owners types.CapabilityOwners) { + prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(index) + + // set owners in persistent store + prefixStore.Set(indexKey, k.cdc.MustMarshalBinaryBare(&owners)) +} + +// GetOwners returns the capability owners with a given index. +func (k Keeper) GetOwners(ctx sdk.Context, index uint64) (types.CapabilityOwners, bool) { + prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(index) + + // get owners for index from persistent store + ownerBytes := prefixStore.Get(indexKey) + if ownerBytes == nil { + return types.CapabilityOwners{}, false + } + var owners types.CapabilityOwners + k.cdc.MustUnmarshalBinaryBare(ownerBytes, &owners) + return owners, true +} + +// InitializeCapability takes in an index and an owners array. It creates the capability in memory +// and sets the fwd and reverse keys for each owner in the memstore. +// It is used during initialization from genesis. +func (k Keeper) InitializeCapability(ctx sdk.Context, index uint64, owners types.CapabilityOwners) { + + memStore := ctx.KVStore(k.memKey) + + cap := types.NewCapability(index) + for _, owner := range owners.Owners { + // Set the forward mapping between the module and capability tuple and the + // capability name in the memKVStore + memStore.Set(types.FwdCapabilityKey(owner.Module, cap), []byte(owner.Name)) + + // Set the reverse mapping between the module and capability name and the + // index in the in-memory store. Since marshalling and unmarshalling into a store + // will change memory address of capability, we simply store index as value here + // and retrieve the in-memory pointer to the capability from our map + memStore.Set(types.RevCapabilityKey(owner.Module, owner.Name), sdk.Uint64ToBigEndian(index)) + + // Set the mapping from index from index to in-memory capability in the go map + k.capMap[index] = cap + } + +} + +// NewCapability attempts to create a new capability with a given name. If the +// capability already exists in the in-memory store, an error will be returned. +// Otherwise, a new capability is created with the current global unique index. +// The newly created capability has the scoped module name and capability name +// tuple set as the initial owner. Finally, the global index is incremented along +// with forward and reverse indexes set in the in-memory store. +// +// Note, namespacing is completely local, which is safe since records are prefixed +// with the module name and no two ScopedKeeper can have the same module name. +func (sk ScopedKeeper) NewCapability(ctx sdk.Context, name string) (*types.Capability, error) { + if strings.TrimSpace(name) == "" { + return nil, sdkerrors.Wrap(types.ErrInvalidCapabilityName, "capability name cannot be empty") + } + store := ctx.KVStore(sk.storeKey) + + if _, ok := sk.GetCapability(ctx, name); ok { + return nil, sdkerrors.Wrapf(types.ErrCapabilityTaken, fmt.Sprintf("module: %s, name: %s", sk.module, name)) + } + + // create new capability with the current global index + index := types.IndexFromKey(store.Get(types.KeyIndex)) + cap := types.NewCapability(index) + + // update capability owner set + if err := sk.addOwner(ctx, cap, name); err != nil { + return nil, err + } + + // increment global index + store.Set(types.KeyIndex, types.IndexToKey(index+1)) + + memStore := ctx.KVStore(sk.memKey) + + // Set the forward mapping between the module and capability tuple and the + // capability name in the memKVStore + memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) + + // Set the reverse mapping between the module and capability name and the + // index in the in-memory store. Since marshalling and unmarshalling into a store + // will change memory address of capability, we simply store index as value here + // and retrieve the in-memory pointer to the capability from our map + memStore.Set(types.RevCapabilityKey(sk.module, name), sdk.Uint64ToBigEndian(index)) + + // Set the mapping from index from index to in-memory capability in the go map + sk.capMap[index] = cap + + logger(ctx).Info("created new capability", "module", sk.module, "name", name) + + return cap, nil +} + +// AuthenticateCapability attempts to authenticate a given capability and name +// from a caller. It allows for a caller to check that a capability does in fact +// correspond to a particular name. The scoped keeper will lookup the capability +// from the internal in-memory store and check against the provided name. It returns +// true upon success and false upon failure. +// +// Note, the capability's forward mapping is indexed by a string which should +// contain its unique memory reference. +func (sk ScopedKeeper) AuthenticateCapability(ctx sdk.Context, cap *types.Capability, name string) bool { + if strings.TrimSpace(name) == "" || cap == nil { + return false + } + return sk.GetCapabilityName(ctx, cap) == name +} + +// ClaimCapability attempts to claim a given Capability. The provided name and +// the scoped module's name tuple are treated as the owner. It will attempt +// to add the owner to the persistent set of capability owners for the capability +// index. If the owner already exists, it will return an error. Otherwise, it will +// also set a forward and reverse index for the capability and capability name. +func (sk ScopedKeeper) ClaimCapability(ctx sdk.Context, cap *types.Capability, name string) error { + if cap == nil { + return sdkerrors.Wrap(types.ErrNilCapability, "cannot claim nil capability") + } + if strings.TrimSpace(name) == "" { + return sdkerrors.Wrap(types.ErrInvalidCapabilityName, "capability name cannot be empty") + } + // update capability owner set + if err := sk.addOwner(ctx, cap, name); err != nil { + return err + } + + memStore := ctx.KVStore(sk.memKey) + + // Set the forward mapping between the module and capability tuple and the + // capability name in the memKVStore + memStore.Set(types.FwdCapabilityKey(sk.module, cap), []byte(name)) + + // Set the reverse mapping between the module and capability name and the + // index in the in-memory store. Since marshalling and unmarshalling into a store + // will change memory address of capability, we simply store index as value here + // and retrieve the in-memory pointer to the capability from our map + memStore.Set(types.RevCapabilityKey(sk.module, name), sdk.Uint64ToBigEndian(cap.GetIndex())) + + logger(ctx).Info("claimed capability", "module", sk.module, "name", name, "capability", cap.GetIndex()) + + return nil +} + +// ReleaseCapability allows a scoped module to release a capability which it had +// previously claimed or created. After releasing the capability, if no more +// owners exist, the capability will be globally removed. +func (sk ScopedKeeper) ReleaseCapability(ctx sdk.Context, cap *types.Capability) error { + if cap == nil { + return sdkerrors.Wrap(types.ErrNilCapability, "cannot release nil capability") + } + name := sk.GetCapabilityName(ctx, cap) + if len(name) == 0 { + return sdkerrors.Wrap(types.ErrCapabilityNotOwned, sk.module) + } + + memStore := ctx.KVStore(sk.memKey) + + // Delete the forward mapping between the module and capability tuple and the + // capability name in the memKVStore + memStore.Delete(types.FwdCapabilityKey(sk.module, cap)) + + // Delete the reverse mapping between the module and capability name and the + // index in the in-memory store. + memStore.Delete(types.RevCapabilityKey(sk.module, name)) + + // remove owner + capOwners := sk.getOwners(ctx, cap) + capOwners.Remove(types.NewOwner(sk.module, name)) + + prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(cap.GetIndex()) + + if len(capOwners.Owners) == 0 { + // remove capability owner set + prefixStore.Delete(indexKey) + // since no one owns capability, we can delete capability from map + delete(sk.capMap, cap.GetIndex()) + } else { + // update capability owner set + prefixStore.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) + } + + return nil +} + +// GetCapability allows a module to fetch a capability which it previously claimed +// by name. The module is not allowed to retrieve capabilities which it does not +// own. +func (sk ScopedKeeper) GetCapability(ctx sdk.Context, name string) (*types.Capability, bool) { + if strings.TrimSpace(name) == "" { + return nil, false + } + memStore := ctx.KVStore(sk.memKey) + + key := types.RevCapabilityKey(sk.module, name) + indexBytes := memStore.Get(key) + index := sdk.BigEndianToUint64(indexBytes) + + if len(indexBytes) == 0 { + // If a tx failed and NewCapability got reverted, it is possible + // to still have the capability in the go map since changes to + // go map do not automatically get reverted on tx failure, + // so we delete here to remove unnecessary values in map + // TODO: Delete index correctly from capMap by storing some reverse lookup + // in-memory map. Issue: https://github.com/cosmos/cosmos-sdk/issues/7805 + return nil, false + } + + cap := sk.capMap[index] + if cap == nil { + panic("capability found in memstore is missing from map") + } + + return cap, true +} + +// GetCapabilityName allows a module to retrieve the name under which it stored a given +// capability given the capability +func (sk ScopedKeeper) GetCapabilityName(ctx sdk.Context, cap *types.Capability) string { + if cap == nil { + return "" + } + memStore := ctx.KVStore(sk.memKey) + + return string(memStore.Get(types.FwdCapabilityKey(sk.module, cap))) +} + +// GetOwners all the Owners that own the capability associated with the name this ScopedKeeper uses +// to refer to the capability +func (sk ScopedKeeper) GetOwners(ctx sdk.Context, name string) (*types.CapabilityOwners, bool) { + if strings.TrimSpace(name) == "" { + return nil, false + } + cap, ok := sk.GetCapability(ctx, name) + if !ok { + return nil, false + } + + prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(cap.GetIndex()) + + var capOwners types.CapabilityOwners + + bz := prefixStore.Get(indexKey) + if len(bz) == 0 { + return nil, false + } + + sk.cdc.MustUnmarshalBinaryBare(bz, &capOwners) + + return &capOwners, true +} + +// LookupModules returns all the module owners for a given capability +// as a string array and the capability itself. +// The method returns an error if either the capability or the owners cannot be +// retreived from the memstore. +func (sk ScopedKeeper) LookupModules(ctx sdk.Context, name string) ([]string, *types.Capability, error) { + if strings.TrimSpace(name) == "" { + return nil, nil, sdkerrors.Wrap(types.ErrInvalidCapabilityName, "cannot lookup modules with empty capability name") + } + cap, ok := sk.GetCapability(ctx, name) + if !ok { + return nil, nil, sdkerrors.Wrap(types.ErrCapabilityNotFound, name) + } + + capOwners, ok := sk.GetOwners(ctx, name) + if !ok { + return nil, nil, sdkerrors.Wrap(types.ErrCapabilityOwnersNotFound, name) + } + + mods := make([]string, len(capOwners.Owners)) + for i, co := range capOwners.Owners { + mods[i] = co.Module + } + + return mods, cap, nil +} + +func (sk ScopedKeeper) addOwner(ctx sdk.Context, cap *types.Capability, name string) error { + prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(cap.GetIndex()) + + capOwners := sk.getOwners(ctx, cap) + + if err := capOwners.Set(types.NewOwner(sk.module, name)); err != nil { + return err + } + + // update capability owner set + prefixStore.Set(indexKey, sk.cdc.MustMarshalBinaryBare(capOwners)) + + return nil +} + +func (sk ScopedKeeper) getOwners(ctx sdk.Context, cap *types.Capability) *types.CapabilityOwners { + prefixStore := prefix.NewStore(ctx.KVStore(sk.storeKey), types.KeyPrefixIndexCapability) + indexKey := types.IndexToKey(cap.GetIndex()) + + bz := prefixStore.Get(indexKey) + + if len(bz) == 0 { + return types.NewCapabilityOwners() + } + + var capOwners types.CapabilityOwners + sk.cdc.MustUnmarshalBinaryBare(bz, &capOwners) + return &capOwners +} + +func logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go new file mode 100644 index 000000000000..e62176a72463 --- /dev/null +++ b/x/capability/keeper/keeper_test.go @@ -0,0 +1,320 @@ +package keeper_test + +import ( + "fmt" + "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" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp + keeper *keeper.Keeper +} + +func (suite *KeeperTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + cdc := app.AppCodec() + + // create new keeper so we can define custom scoping before init and seal + keeper := keeper.NewKeeper(cdc, app.GetKey(types.StoreKey), app.GetMemKey(types.MemStoreKey)) + + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.keeper = keeper +} + +func (suite *KeeperTestSuite) TestInitializeAndSeal() { + sk := suite.keeper.ScopeToModule(banktypes.ModuleName) + suite.Require().Panics(func() { + suite.keeper.ScopeToModule(" ") + }) + + caps := make([]*types.Capability, 5) + // Get Latest Index before creating new ones to sychronize indices correctly + prevIndex := suite.keeper.GetLatestIndex(suite.ctx) + + for i := range caps { + cap, err := sk.NewCapability(suite.ctx, fmt.Sprintf("transfer-%d", i)) + suite.Require().NoError(err) + suite.Require().NotNil(cap) + suite.Require().Equal(uint64(i)+prevIndex, cap.GetIndex()) + + caps[i] = cap + } + + suite.Require().NotPanics(func() { + suite.keeper.InitializeAndSeal(suite.ctx) + }) + + for i, cap := range caps { + got, ok := sk.GetCapability(suite.ctx, fmt.Sprintf("transfer-%d", i)) + suite.Require().True(ok) + suite.Require().Equal(cap, got) + suite.Require().Equal(uint64(i)+prevIndex, got.GetIndex()) + } + + suite.Require().Panics(func() { + suite.keeper.InitializeAndSeal(suite.ctx) + }) + + suite.Require().Panics(func() { + _ = suite.keeper.ScopeToModule(stakingtypes.ModuleName) + }) +} + +func (suite *KeeperTestSuite) TestNewCapability() { + sk := suite.keeper.ScopeToModule(banktypes.ModuleName) + + got, ok := sk.GetCapability(suite.ctx, "transfer") + suite.Require().False(ok) + suite.Require().Nil(got) + + cap, err := sk.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + got, ok = sk.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + suite.Require().True(cap == got, "expected memory addresses to be equal") + + got, ok = sk.GetCapability(suite.ctx, "invalid") + suite.Require().False(ok) + suite.Require().Nil(got) + + got, ok = sk.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + suite.Require().True(cap == got, "expected memory addresses to be equal") + + cap2, err := sk.NewCapability(suite.ctx, "transfer") + suite.Require().Error(err) + suite.Require().Nil(cap2) + + got, ok = sk.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + suite.Require().True(cap == got, "expected memory addresses to be equal") + + cap, err = sk.NewCapability(suite.ctx, " ") + suite.Require().Error(err) + suite.Require().Nil(cap) +} + +func (suite *KeeperTestSuite) TestOriginalCapabilityKeeper() { + got, ok := suite.app.ScopedIBCKeeper.GetCapability(suite.ctx, "invalid") + suite.Require().False(ok) + suite.Require().Nil(got) + + port, ok := suite.app.ScopedIBCKeeper.GetCapability(suite.ctx, "ports/transfer") + suite.Require().True(ok) + suite.Require().NotNil(port) +} + +func (suite *KeeperTestSuite) TestAuthenticateCapability() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + sk2 := suite.keeper.ScopeToModule(stakingtypes.ModuleName) + + cap1, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap1) + + forgedCap := types.NewCapability(cap1.Index) // index should be the same index as the first capability + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, forgedCap, "transfer")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, forgedCap, "transfer")) + + cap2, err := sk2.NewCapability(suite.ctx, "bond") + suite.Require().NoError(err) + suite.Require().NotNil(cap2) + + got, ok := sk1.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + + suite.Require().True(sk1.AuthenticateCapability(suite.ctx, cap1, "transfer")) + suite.Require().True(sk1.AuthenticateCapability(suite.ctx, got, "transfer")) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, cap1, "invalid")) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, cap2, "transfer")) + + suite.Require().True(sk2.AuthenticateCapability(suite.ctx, cap2, "bond")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap2, "invalid")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap1, "bond")) + + sk2.ReleaseCapability(suite.ctx, cap2) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, cap2, "bond")) + + badCap := types.NewCapability(100) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, badCap, "transfer")) + suite.Require().False(sk2.AuthenticateCapability(suite.ctx, badCap, "bond")) + + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, cap1, " ")) + suite.Require().False(sk1.AuthenticateCapability(suite.ctx, nil, "transfer")) +} + +func (suite *KeeperTestSuite) TestClaimCapability() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + sk2 := suite.keeper.ScopeToModule(stakingtypes.ModuleName) + sk3 := suite.keeper.ScopeToModule("foo") + + cap, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + suite.Require().Error(sk1.ClaimCapability(suite.ctx, cap, "transfer")) + suite.Require().NoError(sk2.ClaimCapability(suite.ctx, cap, "transfer")) + + got, ok := sk1.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + + got, ok = sk2.GetCapability(suite.ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap, got) + + suite.Require().Error(sk3.ClaimCapability(suite.ctx, cap, " ")) + suite.Require().Error(sk3.ClaimCapability(suite.ctx, nil, "transfer")) +} + +func (suite *KeeperTestSuite) TestGetOwners() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + sk2 := suite.keeper.ScopeToModule(stakingtypes.ModuleName) + sk3 := suite.keeper.ScopeToModule("foo") + + sks := []keeper.ScopedKeeper{sk1, sk2, sk3} + + cap, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + suite.Require().NoError(sk2.ClaimCapability(suite.ctx, cap, "transfer")) + suite.Require().NoError(sk3.ClaimCapability(suite.ctx, cap, "transfer")) + + expectedOrder := []string{banktypes.ModuleName, "foo", stakingtypes.ModuleName} + // Ensure all scoped keepers can get owners + for _, sk := range sks { + owners, ok := sk.GetOwners(suite.ctx, "transfer") + mods, gotCap, err := sk.LookupModules(suite.ctx, "transfer") + + suite.Require().True(ok, "could not retrieve owners") + suite.Require().NotNil(owners, "owners is nil") + + suite.Require().NoError(err, "could not retrieve modules") + suite.Require().NotNil(gotCap, "capability is nil") + suite.Require().NotNil(mods, "modules is nil") + suite.Require().Equal(cap, gotCap, "caps not equal") + + suite.Require().Equal(len(expectedOrder), len(owners.Owners), "length of owners is unexpected") + for i, o := range owners.Owners { + // Require owner is in expected position + suite.Require().Equal(expectedOrder[i], o.Module, "module is unexpected") + suite.Require().Equal(expectedOrder[i], mods[i], "module in lookup is unexpected") + } + } + + // foo module releases capability + err = sk3.ReleaseCapability(suite.ctx, cap) + suite.Require().Nil(err, "could not release capability") + + // new expected order and scoped capabilities + expectedOrder = []string{banktypes.ModuleName, stakingtypes.ModuleName} + sks = []keeper.ScopedKeeper{sk1, sk2} + + // Ensure all scoped keepers can get owners + for _, sk := range sks { + owners, ok := sk.GetOwners(suite.ctx, "transfer") + mods, cap, err := sk.LookupModules(suite.ctx, "transfer") + + suite.Require().True(ok, "could not retrieve owners") + suite.Require().NotNil(owners, "owners is nil") + + suite.Require().NoError(err, "could not retrieve modules") + suite.Require().NotNil(cap, "capability is nil") + suite.Require().NotNil(mods, "modules is nil") + + suite.Require().Equal(len(expectedOrder), len(owners.Owners), "length of owners is unexpected") + for i, o := range owners.Owners { + // Require owner is in expected position + suite.Require().Equal(expectedOrder[i], o.Module, "module is unexpected") + suite.Require().Equal(expectedOrder[i], mods[i], "module in lookup is unexpected") + } + } + + _, ok := sk1.GetOwners(suite.ctx, " ") + suite.Require().False(ok, "got owners from empty capability name") +} + +func (suite *KeeperTestSuite) TestReleaseCapability() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + sk2 := suite.keeper.ScopeToModule(stakingtypes.ModuleName) + + cap1, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap1) + + suite.Require().NoError(sk2.ClaimCapability(suite.ctx, cap1, "transfer")) + + cap2, err := sk2.NewCapability(suite.ctx, "bond") + suite.Require().NoError(err) + suite.Require().NotNil(cap2) + + suite.Require().Error(sk1.ReleaseCapability(suite.ctx, cap2)) + + suite.Require().NoError(sk2.ReleaseCapability(suite.ctx, cap1)) + got, ok := sk2.GetCapability(suite.ctx, "transfer") + suite.Require().False(ok) + suite.Require().Nil(got) + + suite.Require().NoError(sk1.ReleaseCapability(suite.ctx, cap1)) + got, ok = sk1.GetCapability(suite.ctx, "transfer") + suite.Require().False(ok) + suite.Require().Nil(got) + + suite.Require().Error(sk1.ReleaseCapability(suite.ctx, nil)) +} + +func (suite KeeperTestSuite) TestRevertCapability() { + sk := suite.keeper.ScopeToModule(banktypes.ModuleName) + + ms := suite.ctx.MultiStore() + + msCache := ms.CacheMultiStore() + cacheCtx := suite.ctx.WithMultiStore(msCache) + + capName := "revert" + // Create capability on cached context + cap, err := sk.NewCapability(cacheCtx, capName) + suite.Require().NoError(err, "could not create capability") + + // Check that capability written in cached context + gotCache, ok := sk.GetCapability(cacheCtx, capName) + suite.Require().True(ok, "could not retrieve capability from cached context") + suite.Require().Equal(cap, gotCache, "did not get correct capability from cached context") + + // Check that capability is NOT written to original context + got, ok := sk.GetCapability(suite.ctx, capName) + suite.Require().False(ok, "retrieved capability from original context before write") + suite.Require().Nil(got, "capability not nil in original store") + + // Write to underlying memKVStore + msCache.Write() + + got, ok = sk.GetCapability(suite.ctx, capName) + suite.Require().True(ok, "could not retrieve capability from context") + suite.Require().Equal(cap, got, "did not get correct capability from context") +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/capability/module.go b/x/capability/module.go new file mode 100644 index 000000000000..7957f57747d6 --- /dev/null +++ b/x/capability/module.go @@ -0,0 +1,171 @@ +package capability + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "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" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/simulation" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface for the capability module. +type AppModuleBasic struct { + cdc codec.Marshaler +} + +func NewAppModuleBasic(cdc codec.Marshaler) AppModuleBasic { + return AppModuleBasic{cdc: cdc} +} + +// Name returns the capability module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec does nothing. Capability does not support amino. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers the module's interface types +func (a AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {} + +// DefaultGenesis returns the capability module's default genesis state. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis performs genesis state validation for the capability module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// RegisterRESTRoutes registers the capability module's REST service handlers. +func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the capability module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) { +} + +// GetTxCmd returns the capability module's root tx command. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { return nil } + +// GetQueryCmd returns the capability module's root query command. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface for the capability module. +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper) AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(cdc), + keeper: keeper, + } +} + +// Name returns the capability module's name. +func (am AppModule) Name() string { + return am.AppModuleBasic.Name() +} + +// Route returns the capability module's message routing key. +func (AppModule) Route() sdk.Route { return sdk.Route{} } + +// QuerierRoute returns the capability module's query routing key. +func (AppModule) QuerierRoute() string { return "" } + +// LegacyQuerierHandler returns the capability module's Querier. +func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { return nil } + +// RegisterServices registers a GRPC query service to respond to the +// module-specific GRPC queries. +func (am AppModule) RegisterServices(module.Configurator) {} + +// RegisterInvariants registers the capability module's invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs the capability module's genesis initialization It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, gs json.RawMessage) []abci.ValidatorUpdate { + var genState types.GenesisState + // Initialize global index to index in genesis state + cdc.MustUnmarshalJSON(gs, &genState) + + InitGenesis(ctx, am.keeper, genState) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the capability module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + genState := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(genState) +} + +// BeginBlock executes all ABCI BeginBlock logic respective to the capability module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock executes all ABCI EndBlock logic respective to the capability module. It +// returns no validator updates. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// GenerateGenesisState creates a randomized GenState of the capability module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents performs a no-op +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized capability param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for capability 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 { + return nil +} diff --git a/x/capability/simulation/decoder.go b/x/capability/simulation/decoder.go new file mode 100644 index 000000000000..9cd0dcc6da4e --- /dev/null +++ b/x/capability/simulation/decoder.go @@ -0,0 +1,33 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding capability 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, types.KeyIndex): + idxA := sdk.BigEndianToUint64(kvA.Value) + idxB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("Index A: %d\nIndex B: %d\n", idxA, idxB) + + case bytes.HasPrefix(kvA.Key, types.KeyPrefixIndexCapability): + var capOwnersA, capOwnersB types.CapabilityOwners + cdc.MustUnmarshalBinaryBare(kvA.Value, &capOwnersA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &capOwnersB) + return fmt.Sprintf("CapabilityOwners A: %v\nCapabilityOwners B: %v\n", capOwnersA, capOwnersB) + + default: + panic(fmt.Sprintf("invalid %s key prefix %X (%s)", types.ModuleName, kvA.Key, string(kvA.Key))) + } + } +} diff --git a/x/capability/simulation/decoder_test.go b/x/capability/simulation/decoder_test.go new file mode 100644 index 000000000000..911580505944 --- /dev/null +++ b/x/capability/simulation/decoder_test.go @@ -0,0 +1,60 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "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/capability/simulation" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +func TestDecodeStore(t *testing.T) { + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) + + capOwners := types.CapabilityOwners{ + Owners: []types.Owner{{Module: "transfer", Name: "ports/transfer"}}, + } + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: types.KeyIndex, + Value: sdk.Uint64ToBigEndian(10), + }, + { + Key: types.KeyPrefixIndexCapability, + Value: cdc.MustMarshalBinaryBare(&capOwners), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"Index", "Index A: 10\nIndex B: 10\n"}, + {"CapabilityOwners", fmt.Sprintf("CapabilityOwners A: %v\nCapabilityOwners B: %v\n", capOwners, capOwners)}, + {"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/capability/simulation/genesis.go b/x/capability/simulation/genesis.go new file mode 100644 index 000000000000..ab1d11fb3d46 --- /dev/null +++ b/x/capability/simulation/genesis.go @@ -0,0 +1,39 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// Simulation parameter constants +const index = "index" + +// GenIndex returns a random global index between 1-1000 +func GenIndex(r *rand.Rand) uint64 { + return uint64(r.Int63n(1000)) + 1 +} + +// RandomizedGenState generates a random GenesisState for capability +func RandomizedGenState(simState *module.SimulationState) { + var idx uint64 + + simState.AppParams.GetOrGenerate( + simState.Cdc, index, &idx, simState.Rand, + func(r *rand.Rand) { idx = GenIndex(r) }, + ) + + capabilityGenesis := types.GenesisState{Index: idx} + + bz, err := json.MarshalIndent(&capabilityGenesis, "", " ") + 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(&capabilityGenesis) +} diff --git a/x/capability/simulation/genesis_test.go b/x/capability/simulation/genesis_test.go new file mode 100644 index 000000000000..16d54c177a50 --- /dev/null +++ b/x/capability/simulation/genesis_test.go @@ -0,0 +1,71 @@ +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/capability/simulation" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 capGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &capGenesis) + + require.Equal(t, uint64(149), capGenesis.Index) + require.Len(t, capGenesis.Owners, 0) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/capability/spec/01_concepts.md b/x/capability/spec/01_concepts.md new file mode 100644 index 000000000000..7751df4c0817 --- /dev/null +++ b/x/capability/spec/01_concepts.md @@ -0,0 +1,34 @@ + + +# Concepts + +## Capabilities + +Capabilities are multi-owner. A scoped keeper can create a capability via `NewCapability` +which creates a new unique, unforgeable object-capability reference. The newly +created capability is automatically persisted; the calling module need not call +`ClaimCapability`. Calling `NewCapability` will create the capability with the +calling module and name as a tuple to be treated the capabilities first owner. + +Capabilities can be claimed by other modules which add them as owners. `ClaimCapability` +allows a module to claim a capability key which it has received from another +module so that future `GetCapability` calls will succeed. `ClaimCapability` MUST +be called if a module which receives a capability wishes to access it by name in +the future. Again, capabilities are multi-owner, so if multiple modules have a +single Capability reference, they will all own it. If a module receives a capability +from another module but does not call `ClaimCapability`, it may use it in the executing +transaction but will not be able to access it afterwards. + +`AuthenticateCapability` can be called by any module to check that a capability +does in fact correspond to a particular name (the name can be un-trusted user input) +with which the calling module previously associated it. + +`GetCapability` allows a module to fetch a capability which it has previously +claimed by name. The module is not allowed to retrieve capabilities which it does +not own. + +## Stores + +- MemStore diff --git a/x/capability/spec/02_state.md b/x/capability/spec/02_state.md new file mode 100644 index 000000000000..b93de4bf4ab7 --- /dev/null +++ b/x/capability/spec/02_state.md @@ -0,0 +1,11 @@ + + +# State + +## Index + +## CapabilityOwners + +## Capability diff --git a/x/capability/spec/README.md b/x/capability/spec/README.md new file mode 100644 index 000000000000..ec612ba97697 --- /dev/null +++ b/x/capability/spec/README.md @@ -0,0 +1,76 @@ + + +# `capability` + +## Overview + +`x/capability` is an implementation of a Cosmos SDK module, per [ADR 003](./../../../docs/architecture/adr-003-dynamic-capability-store.md), +that allows for provisioning, tracking, and authenticating multi-owner capabilities +at runtime. + +The keeper maintains two states: persistent and ephemeral in-memory. The persistent +store maintains a globally unique auto-incrementing index and a mapping from +capability index to a set of capability owners that are defined as a module and +capability name tuple. The in-memory ephemeral state keeps track of the actual +capabilities, represented as addresses in local memory, with both forward and reverse indexes. +The forward index maps module name and capability tuples to the capability name. The +reverse index maps between the module and capability name and the capability itself. + +The keeper allows the creation of "scoped" sub-keepers which are tied to a particular +module by name. Scoped keepers must be created at application initialization and +passed to modules, which can then use them to claim capabilities they receive and +retrieve capabilities which they own by name, in addition to creating new capabilities +& authenticating capabilities passed by other modules. A scoped keeper cannot escape its scope, +so a module cannot interfere with or inspect capabilities owned by other modules. + +The keeper provides no other core functionality that can be found in other modules +like queriers, REST and CLI handlers, and genesis state. + +## Initialization + +During application initialization, the keeper must be instantiated with a persistent +store key and an in-memory store key. + +```go +type App struct { + // ... + + capabilityKeeper *capability.Keeper +} + +func NewApp(...) *App { + // ... + + app.capabilityKeeper = capability.NewKeeper(codec, persistentStoreKey, memStoreKey) +} +``` + +After the keeper is created, it can be used to create scoped sub-keepers which +are passed to other modules that can create, authenticate, and claim capabilities. +After all the necessary scoped keepers are created and the state is loaded, the +main capability keeper must be initialized and sealed to populate the in-memory +state and to prevent further scoped keepers from being created. + +```go +func NewApp(...) *App { + // ... + + // Initialize and seal the capability keeper so all persistent capabilities + // are loaded in-memory and prevent any further modules from creating scoped + // sub-keepers. + ctx := app.BaseApp.NewContext(true, tmproto.Header{}) + app.capabilityKeeper.InitializeAndSeal(ctx) + + return app +} +``` + +## Contents + +1. **[Concepts](01_concepts.md)** +1. **[State](02_state.md)** diff --git a/x/capability/types/capability.pb.go b/x/capability/types/capability.pb.go new file mode 100644 index 000000000000..785742bd338a --- /dev/null +++ b/x/capability/types/capability.pb.go @@ -0,0 +1,702 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/capability/v1beta1/capability.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 + +// Capability defines an implementation of an object capability. The index +// provided to a Capability must be globally unique. +type Capability struct { + Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty" yaml:"index"` +} + +func (m *Capability) Reset() { *m = Capability{} } +func (*Capability) ProtoMessage() {} +func (*Capability) Descriptor() ([]byte, []int) { + return fileDescriptor_6308261edd8470a9, []int{0} +} +func (m *Capability) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Capability) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Capability.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 *Capability) XXX_Merge(src proto.Message) { + xxx_messageInfo_Capability.Merge(m, src) +} +func (m *Capability) XXX_Size() int { + return m.Size() +} +func (m *Capability) XXX_DiscardUnknown() { + xxx_messageInfo_Capability.DiscardUnknown(m) +} + +var xxx_messageInfo_Capability proto.InternalMessageInfo + +func (m *Capability) GetIndex() uint64 { + if m != nil { + return m.Index + } + return 0 +} + +// Owner defines a single capability owner. An owner is defined by the name of +// capability and the module name. +type Owner struct { + Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty" yaml:"module"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" yaml:"name"` +} + +func (m *Owner) Reset() { *m = Owner{} } +func (*Owner) ProtoMessage() {} +func (*Owner) Descriptor() ([]byte, []int) { + return fileDescriptor_6308261edd8470a9, []int{1} +} +func (m *Owner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Owner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Owner.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 *Owner) XXX_Merge(src proto.Message) { + xxx_messageInfo_Owner.Merge(m, src) +} +func (m *Owner) XXX_Size() int { + return m.Size() +} +func (m *Owner) XXX_DiscardUnknown() { + xxx_messageInfo_Owner.DiscardUnknown(m) +} + +var xxx_messageInfo_Owner proto.InternalMessageInfo + +// CapabilityOwners defines a set of owners of a single Capability. The set of +// owners must be unique. +type CapabilityOwners struct { + Owners []Owner `protobuf:"bytes,1,rep,name=owners,proto3" json:"owners"` +} + +func (m *CapabilityOwners) Reset() { *m = CapabilityOwners{} } +func (m *CapabilityOwners) String() string { return proto.CompactTextString(m) } +func (*CapabilityOwners) ProtoMessage() {} +func (*CapabilityOwners) Descriptor() ([]byte, []int) { + return fileDescriptor_6308261edd8470a9, []int{2} +} +func (m *CapabilityOwners) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CapabilityOwners) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CapabilityOwners.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 *CapabilityOwners) XXX_Merge(src proto.Message) { + xxx_messageInfo_CapabilityOwners.Merge(m, src) +} +func (m *CapabilityOwners) XXX_Size() int { + return m.Size() +} +func (m *CapabilityOwners) XXX_DiscardUnknown() { + xxx_messageInfo_CapabilityOwners.DiscardUnknown(m) +} + +var xxx_messageInfo_CapabilityOwners proto.InternalMessageInfo + +func (m *CapabilityOwners) GetOwners() []Owner { + if m != nil { + return m.Owners + } + return nil +} + +func init() { + proto.RegisterType((*Capability)(nil), "cosmos.capability.v1beta1.Capability") + proto.RegisterType((*Owner)(nil), "cosmos.capability.v1beta1.Owner") + proto.RegisterType((*CapabilityOwners)(nil), "cosmos.capability.v1beta1.CapabilityOwners") +} + +func init() { + proto.RegisterFile("cosmos/capability/v1beta1/capability.proto", fileDescriptor_6308261edd8470a9) +} + +var fileDescriptor_6308261edd8470a9 = []byte{ + // 299 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4a, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4e, 0x2c, 0x48, 0x4c, 0xca, 0xcc, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, + 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0x44, 0x12, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x84, + 0xa8, 0xd5, 0x43, 0x92, 0x80, 0xaa, 0x95, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd2, 0x07, + 0xb1, 0x20, 0x1a, 0x94, 0xac, 0xb8, 0xb8, 0x9c, 0xe1, 0x6a, 0x85, 0xd4, 0xb8, 0x58, 0x33, 0xf3, + 0x52, 0x52, 0x2b, 0x24, 0x18, 0x15, 0x18, 0x35, 0x58, 0x9c, 0x04, 0x3e, 0xdd, 0x93, 0xe7, 0xa9, + 0x4c, 0xcc, 0xcd, 0xb1, 0x52, 0x02, 0x0b, 0x2b, 0x05, 0x41, 0xa4, 0xad, 0x58, 0x66, 0x2c, 0x90, + 0x67, 0x50, 0x4a, 0xe4, 0x62, 0xf5, 0x2f, 0xcf, 0x4b, 0x2d, 0x12, 0xd2, 0xe4, 0x62, 0xcb, 0xcd, + 0x4f, 0x29, 0xcd, 0x49, 0x05, 0xeb, 0xe3, 0x74, 0x12, 0xfc, 0x74, 0x4f, 0x9e, 0x17, 0xa2, 0x0f, + 0x22, 0xae, 0x14, 0x04, 0x55, 0x20, 0xa4, 0xcc, 0xc5, 0x92, 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0x04, + 0x56, 0xc8, 0xff, 0xe9, 0x9e, 0x3c, 0x37, 0x44, 0x21, 0x48, 0x54, 0x29, 0x08, 0x2c, 0x69, 0xc5, + 0xd1, 0xb1, 0x40, 0x9e, 0x01, 0x6c, 0x45, 0x10, 0x97, 0x00, 0xc2, 0x79, 0x60, 0xcb, 0x8a, 0x85, + 0xec, 0xb8, 0xd8, 0xf2, 0xc1, 0x2c, 0x09, 0x46, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x05, 0x3d, 0x9c, + 0x9e, 0xd6, 0x03, 0x6b, 0x71, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xcb, 0xc9, 0xf3, + 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, + 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xf4, 0xd3, 0x33, 0x4b, 0x32, 0x4a, + 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x61, 0x81, 0x0e, 0xa6, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x2b, + 0x90, 0x63, 0xa0, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0x88, 0xc6, 0x80, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x1d, 0xc9, 0xe6, 0xa8, 0xa3, 0x01, 0x00, 0x00, +} + +func (m *Capability) 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 *Capability) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Capability) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Index != 0 { + i = encodeVarintCapability(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Owner) 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 *Owner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Owner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintCapability(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Module) > 0 { + i -= len(m.Module) + copy(dAtA[i:], m.Module) + i = encodeVarintCapability(dAtA, i, uint64(len(m.Module))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CapabilityOwners) 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 *CapabilityOwners) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CapabilityOwners) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Owners) > 0 { + for iNdEx := len(m.Owners) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Owners[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCapability(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintCapability(dAtA []byte, offset int, v uint64) int { + offset -= sovCapability(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Capability) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovCapability(uint64(m.Index)) + } + return n +} + +func (m *Owner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Module) + if l > 0 { + n += 1 + l + sovCapability(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovCapability(uint64(l)) + } + return n +} + +func (m *CapabilityOwners) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Owners) > 0 { + for _, e := range m.Owners { + l = e.Size() + n += 1 + l + sovCapability(uint64(l)) + } + } + return n +} + +func sovCapability(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCapability(x uint64) (n int) { + return sovCapability(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Capability) 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 ErrIntOverflowCapability + } + 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: Capability: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Capability: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCapability + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCapability(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCapability + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Owner) 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 ErrIntOverflowCapability + } + 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: Owner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Owner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Module", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCapability + } + 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 ErrInvalidLengthCapability + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCapability + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Module = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCapability + } + 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 ErrInvalidLengthCapability + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCapability + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCapability(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCapability + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CapabilityOwners) 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 ErrIntOverflowCapability + } + 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: CapabilityOwners: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CapabilityOwners: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owners", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCapability + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCapability + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCapability + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owners = append(m.Owners, Owner{}) + if err := m.Owners[len(m.Owners)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCapability(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCapability + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCapability(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, ErrIntOverflowCapability + } + 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, ErrIntOverflowCapability + } + 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, ErrIntOverflowCapability + } + 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, ErrInvalidLengthCapability + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCapability + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCapability + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCapability = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCapability = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCapability = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/capability/types/errors.go b/x/capability/types/errors.go new file mode 100644 index 000000000000..7c582ccbb0a5 --- /dev/null +++ b/x/capability/types/errors.go @@ -0,0 +1,18 @@ +package types + +// DONTCOVER + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/capability module sentinel errors +var ( + ErrInvalidCapabilityName = sdkerrors.Register(ModuleName, 2, "capability name not valid") + ErrNilCapability = sdkerrors.Register(ModuleName, 3, "provided capability is nil") + ErrCapabilityTaken = sdkerrors.Register(ModuleName, 4, "capability name already taken") + ErrOwnerClaimed = sdkerrors.Register(ModuleName, 5, "given owner already claimed capability") + ErrCapabilityNotOwned = sdkerrors.Register(ModuleName, 6, "capability not owned by module") + ErrCapabilityNotFound = sdkerrors.Register(ModuleName, 7, "capability not found") + ErrCapabilityOwnersNotFound = sdkerrors.Register(ModuleName, 8, "owners not found for capability") +) diff --git a/x/capability/types/genesis.go b/x/capability/types/genesis.go new file mode 100644 index 000000000000..afab1d0c7d31 --- /dev/null +++ b/x/capability/types/genesis.go @@ -0,0 +1,49 @@ +package types + +import ( + "fmt" + "strings" +) + +// DefaultIndex is the default capability global index +const DefaultIndex uint64 = 1 + +// DefaultGenesis returns the default Capability genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Index: DefaultIndex, + Owners: []GenesisOwners{}, + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + // NOTE: index must be greater than 0 + if gs.Index == 0 { + return fmt.Errorf("capability index must be non-zero") + } + + for _, genOwner := range gs.Owners { + if len(genOwner.IndexOwners.Owners) == 0 { + return fmt.Errorf("empty owners in genesis") + } + + // all exported existing indices must be between [1, gs.Index) + if genOwner.Index == 0 || genOwner.Index >= gs.Index { + return fmt.Errorf("owners exist for index %d outside of valid range: %d-%d", genOwner.Index, 1, gs.Index-1) + } + + for _, owner := range genOwner.IndexOwners.Owners { + if strings.TrimSpace(owner.Module) == "" { + return fmt.Errorf("owner's module cannot be blank: %s", owner) + } + + if strings.TrimSpace(owner.Name) == "" { + return fmt.Errorf("owner's name cannot be blank: %s", owner) + } + } + } + + return nil +} diff --git a/x/capability/types/genesis.pb.go b/x/capability/types/genesis.pb.go new file mode 100644 index 000000000000..e72c18eda53b --- /dev/null +++ b/x/capability/types/genesis.pb.go @@ -0,0 +1,585 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/capability/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 + +// GenesisOwners defines the capability owners with their corresponding index. +type GenesisOwners struct { + // index is the index of the capability owner. + Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + // index_owners are the owners at the given index. + IndexOwners CapabilityOwners `protobuf:"bytes,2,opt,name=index_owners,json=indexOwners,proto3" json:"index_owners" yaml:"index_owners"` +} + +func (m *GenesisOwners) Reset() { *m = GenesisOwners{} } +func (m *GenesisOwners) String() string { return proto.CompactTextString(m) } +func (*GenesisOwners) ProtoMessage() {} +func (*GenesisOwners) Descriptor() ([]byte, []int) { + return fileDescriptor_94922dd16a11c23e, []int{0} +} +func (m *GenesisOwners) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisOwners) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisOwners.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 *GenesisOwners) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisOwners.Merge(m, src) +} +func (m *GenesisOwners) XXX_Size() int { + return m.Size() +} +func (m *GenesisOwners) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisOwners.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisOwners proto.InternalMessageInfo + +func (m *GenesisOwners) GetIndex() uint64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *GenesisOwners) GetIndexOwners() CapabilityOwners { + if m != nil { + return m.IndexOwners + } + return CapabilityOwners{} +} + +// GenesisState defines the capability module's genesis state. +type GenesisState struct { + // index is the capability global index. + Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + // owners represents a map from index to owners of the capability index + // index key is string to allow amino marshalling. + Owners []GenesisOwners `protobuf:"bytes,2,rep,name=owners,proto3" json:"owners"` +} + +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_94922dd16a11c23e, []int{1} +} +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) GetIndex() uint64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *GenesisState) GetOwners() []GenesisOwners { + if m != nil { + return m.Owners + } + return nil +} + +func init() { + proto.RegisterType((*GenesisOwners)(nil), "cosmos.capability.v1beta1.GenesisOwners") + proto.RegisterType((*GenesisState)(nil), "cosmos.capability.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/capability/v1beta1/genesis.proto", fileDescriptor_94922dd16a11c23e) +} + +var fileDescriptor_94922dd16a11c23e = []byte{ + // 281 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4e, 0x2c, 0x48, 0x4c, 0xca, 0xcc, 0xc9, 0x2c, 0xa9, 0xd4, 0x2f, 0x33, + 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x84, 0x28, 0xd4, 0x43, 0x28, 0xd4, 0x83, 0x2a, 0x94, 0x12, 0x49, + 0xcf, 0x4f, 0xcf, 0x07, 0xab, 0xd2, 0x07, 0xb1, 0x20, 0x1a, 0xa4, 0xb4, 0x70, 0x9b, 0x8c, 0x64, + 0x06, 0x58, 0xad, 0xd2, 0x24, 0x46, 0x2e, 0x5e, 0x77, 0x88, 0x75, 0xfe, 0xe5, 0x79, 0xa9, 0x45, + 0xc5, 0x42, 0x22, 0x5c, 0xac, 0x99, 0x79, 0x29, 0xa9, 0x15, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, + 0x41, 0x10, 0x8e, 0x50, 0x36, 0x17, 0x0f, 0x98, 0x11, 0x9f, 0x0f, 0x56, 0x25, 0xc1, 0xa4, 0xc0, + 0xa8, 0xc1, 0x6d, 0xa4, 0xad, 0x87, 0xd3, 0x6d, 0x7a, 0xce, 0x70, 0x21, 0x88, 0xc1, 0x4e, 0xd2, + 0x27, 0xee, 0xc9, 0x33, 0x7c, 0xba, 0x27, 0x2f, 0x5c, 0x99, 0x98, 0x9b, 0x63, 0xa5, 0x84, 0x6c, + 0x9c, 0x52, 0x10, 0x37, 0x98, 0x0b, 0x51, 0xa9, 0x94, 0xc3, 0xc5, 0x03, 0x75, 0x53, 0x70, 0x49, + 0x62, 0x49, 0x2a, 0x0e, 0x27, 0xb9, 0x71, 0xb1, 0xc1, 0x1d, 0xc3, 0xac, 0xc1, 0x6d, 0xa4, 0x81, + 0xc7, 0x31, 0x28, 0x5e, 0x74, 0x62, 0x01, 0xb9, 0x24, 0x08, 0xaa, 0xdb, 0xc9, 0xf3, 0xc4, 0x23, + 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, + 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xf4, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, + 0x92, 0xf3, 0x73, 0xf5, 0x61, 0x61, 0x0a, 0xa6, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x2b, 0x90, 0x03, + 0xb8, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0xa8, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xfd, 0xf4, 0xf2, 0x5d, 0xdc, 0x01, 0x00, 0x00, +} + +func (m *GenesisOwners) 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 *GenesisOwners) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisOwners) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.IndexOwners.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.Index != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +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.Owners) > 0 { + for iNdEx := len(m.Owners) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Owners[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Index != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + 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 *GenesisOwners) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovGenesis(uint64(m.Index)) + } + l = m.IndexOwners.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovGenesis(uint64(m.Index)) + } + if len(m.Owners) > 0 { + for _, e := range m.Owners { + 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 *GenesisOwners) 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: GenesisOwners: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisOwners: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexOwners", 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 := m.IndexOwners.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 *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 != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owners", 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.Owners = append(m.Owners, GenesisOwners{}) + if err := m.Owners[len(m.Owners)-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/capability/types/genesis_test.go b/x/capability/types/genesis_test.go new file mode 100644 index 000000000000..d8a02e019254 --- /dev/null +++ b/x/capability/types/genesis_test.go @@ -0,0 +1,131 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidateGenesis(t *testing.T) { + testCases := []struct { + name string + malleate func(*GenesisState) + expPass bool + }{ + { + name: "default", + malleate: func(_ *GenesisState) {}, + expPass: true, + }, + { + name: "valid genesis state", + malleate: func(genState *GenesisState) { + genState.Index = 10 + genOwner := GenesisOwners{ + Index: 1, + IndexOwners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + }, + expPass: true, + }, + { + name: "initial index is 0", + malleate: func(genState *GenesisState) { + genState.Index = 0 + genOwner := GenesisOwners{ + Index: 0, + IndexOwners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + + { + name: "blank owner module", + malleate: func(genState *GenesisState) { + genState.Index = 1 + genOwner := GenesisOwners{ + Index: 1, + IndexOwners: CapabilityOwners{[]Owner{{Module: "", Name: "port/transfer"}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + { + name: "blank owner name", + malleate: func(genState *GenesisState) { + genState.Index = 1 + genOwner := GenesisOwners{ + Index: 1, + IndexOwners: CapabilityOwners{[]Owner{{Module: "ibc", Name: ""}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + { + name: "index above range", + malleate: func(genState *GenesisState) { + genState.Index = 10 + genOwner := GenesisOwners{ + Index: 12, + IndexOwners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + { + name: "index below range", + malleate: func(genState *GenesisState) { + genState.Index = 10 + genOwner := GenesisOwners{ + Index: 0, + IndexOwners: CapabilityOwners{[]Owner{{Module: "ibc", Name: "port/transfer"}}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + { + name: "owners are empty", + malleate: func(genState *GenesisState) { + genState.Index = 10 + genOwner := GenesisOwners{ + Index: 0, + IndexOwners: CapabilityOwners{[]Owner{}}, + } + + genState.Owners = append(genState.Owners, genOwner) + + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + genState := DefaultGenesis() + tc.malleate(genState) + err := genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go new file mode 100644 index 000000000000..5f171e28a7f4 --- /dev/null +++ b/x/capability/types/keys.go @@ -0,0 +1,51 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName defines the module name + ModuleName = "capability" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // MemStoreKey defines the in-memory store key + MemStoreKey = "mem_capability" +) + +var ( + // KeyIndex defines the key that stores the current globally unique capability + // index. + KeyIndex = []byte("index") + + // KeyPrefixIndexCapability defines a key prefix that stores index to capability + // name mappings. + KeyPrefixIndexCapability = []byte("capability_index") +) + +// RevCapabilityKey returns a reverse lookup key for a given module and capability +// name. +func RevCapabilityKey(module, name string) []byte { + return []byte(fmt.Sprintf("%s/rev/%s", module, name)) +} + +// FwdCapabilityKey returns a forward lookup key for a given module and capability +// reference. +func FwdCapabilityKey(module string, cap *Capability) []byte { + return []byte(fmt.Sprintf("%s/fwd/%p", module, cap)) +} + +// IndexToKey returns bytes to be used as a key for a given capability index. +func IndexToKey(index uint64) []byte { + return sdk.Uint64ToBigEndian(index) +} + +// IndexFromKey returns an index from a call to IndexToKey for a given capability +// index. +func IndexFromKey(key []byte) uint64 { + return sdk.BigEndianToUint64(key) +} diff --git a/x/capability/types/keys_test.go b/x/capability/types/keys_test.go new file mode 100644 index 000000000000..e767c3d34222 --- /dev/null +++ b/x/capability/types/keys_test.go @@ -0,0 +1,29 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +func TestRevCapabilityKey(t *testing.T) { + expected := []byte("bank/rev/send") + require.Equal(t, expected, types.RevCapabilityKey("bank", "send")) +} + +func TestFwdCapabilityKey(t *testing.T) { + cap := types.NewCapability(23) + expected := []byte(fmt.Sprintf("bank/fwd/%p", cap)) + require.Equal(t, expected, types.FwdCapabilityKey("bank", cap)) +} + +func TestIndexToKey(t *testing.T) { + require.Equal(t, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x5a}, types.IndexToKey(3162)) +} + +func TestIndexFromKey(t *testing.T) { + require.Equal(t, uint64(3162), types.IndexFromKey([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x5a})) +} diff --git a/x/capability/types/types.go b/x/capability/types/types.go new file mode 100644 index 000000000000..2dcceb8c8818 --- /dev/null +++ b/x/capability/types/types.go @@ -0,0 +1,87 @@ +package types + +import ( + "fmt" + "sort" + + yaml "gopkg.in/yaml.v2" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewCapability returns a reference to a new Capability to be used as an +// actual capability. +func NewCapability(index uint64) *Capability { + return &Capability{Index: index} +} + +// String returns the string representation of a Capability. The string contains +// the Capability's memory reference as the string is to be used in a composite +// key and to authenticate capabilities. +func (ck *Capability) String() string { + return fmt.Sprintf("Capability{%p, %d}", ck, ck.Index) +} + +func NewOwner(module, name string) Owner { + return Owner{Module: module, Name: name} +} + +// Key returns a composite key for an Owner. +func (o Owner) Key() string { + return fmt.Sprintf("%s/%s", o.Module, o.Name) +} + +func (o Owner) String() string { + bz, _ := yaml.Marshal(o) + return string(bz) +} + +func NewCapabilityOwners() *CapabilityOwners { + return &CapabilityOwners{Owners: make([]Owner, 0)} +} + +// Set attempts to add a given owner to the CapabilityOwners. If the owner +// already exists, an error will be returned. Set runs in O(log n) average time +// and O(n) in the worst case. +func (co *CapabilityOwners) Set(owner Owner) error { + i, ok := co.Get(owner) + if ok { + // owner already exists at co.Owners[i] + return sdkerrors.Wrapf(ErrOwnerClaimed, owner.String()) + } + + // owner does not exist in the set of owners, so we insert at position i + co.Owners = append(co.Owners, Owner{}) // expand by 1 in amortized O(1) / O(n) worst case + copy(co.Owners[i+1:], co.Owners[i:]) + co.Owners[i] = owner + + return nil +} + +// Remove removes a provided owner from the CapabilityOwners if it exists. If the +// owner does not exist, Remove is considered a no-op. +func (co *CapabilityOwners) Remove(owner Owner) { + if len(co.Owners) == 0 { + return + } + + i, ok := co.Get(owner) + if ok { + // owner exists at co.Owners[i] + co.Owners = append(co.Owners[:i], co.Owners[i+1:]...) + } +} + +// Get returns (i, true) of the provided owner in the CapabilityOwners if the +// owner exists, where i indicates the owner's index in the set. Otherwise +// (i, false) where i indicates where in the set the owner should be added. +func (co *CapabilityOwners) Get(owner Owner) (int, bool) { + // find smallest index s.t. co.Owners[i] >= owner in O(log n) time + i := sort.Search(len(co.Owners), func(i int) bool { return co.Owners[i].Key() >= owner.Key() }) + if i < len(co.Owners) && co.Owners[i].Key() == owner.Key() { + // owner exists at co.Owners[i] + return i, true + } + + return i, false +} diff --git a/x/capability/types/types_test.go b/x/capability/types/types_test.go new file mode 100644 index 000000000000..8cec569d2faf --- /dev/null +++ b/x/capability/types/types_test.go @@ -0,0 +1,69 @@ +package types_test + +import ( + "fmt" + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +func TestCapabilityKey(t *testing.T) { + idx := uint64(3162) + cap := types.NewCapability(idx) + require.Equal(t, idx, cap.GetIndex()) + require.Equal(t, fmt.Sprintf("Capability{%p, %d}", cap, idx), cap.String()) +} + +func TestOwner(t *testing.T) { + o := types.NewOwner("bank", "send") + require.Equal(t, "bank/send", o.Key()) + require.Equal(t, "module: bank\nname: send\n", o.String()) +} + +func TestCapabilityOwners_Set(t *testing.T) { + co := types.NewCapabilityOwners() + + owners := make([]types.Owner, 1024) + for i := range owners { + var owner types.Owner + + if i%2 == 0 { + owner = types.NewOwner("bank", fmt.Sprintf("send-%d", i)) + } else { + owner = types.NewOwner("slashing", fmt.Sprintf("slash-%d", i)) + } + + owners[i] = owner + require.NoError(t, co.Set(owner)) + } + + sort.Slice(owners, func(i, j int) bool { return owners[i].Key() < owners[j].Key() }) + require.Equal(t, owners, co.Owners) + + for _, owner := range owners { + require.Error(t, co.Set(owner)) + } +} + +func TestCapabilityOwners_Remove(t *testing.T) { + co := types.NewCapabilityOwners() + + co.Remove(types.NewOwner("bank", "send-0")) + require.Len(t, co.Owners, 0) + + for i := 0; i < 5; i++ { + require.NoError(t, co.Set(types.NewOwner("bank", fmt.Sprintf("send-%d", i)))) + } + + require.Len(t, co.Owners, 5) + + for i := 0; i < 5; i++ { + co.Remove(types.NewOwner("bank", fmt.Sprintf("send-%d", i))) + require.Len(t, co.Owners, 5-(i+1)) + } + + require.Len(t, co.Owners, 0) +} diff --git a/x/crisis/abci.go b/x/crisis/abci.go index 3e5485979980..fa1b932b8f63 100644 --- a/x/crisis/abci.go +++ b/x/crisis/abci.go @@ -1,11 +1,18 @@ package crisis import ( + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + "github.com/cosmos/cosmos-sdk/x/crisis/types" ) // check all registered invariants -func EndBlocker(ctx sdk.Context, k Keeper) { +func EndBlocker(ctx sdk.Context, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + if k.InvCheckPeriod() == 0 || ctx.BlockHeight()%int64(k.InvCheckPeriod()) != 0 { // skip running the invariant check return diff --git a/x/crisis/alias.go b/x/crisis/alias.go deleted file mode 100644 index 1b05371f376b..000000000000 --- a/x/crisis/alias.go +++ /dev/null @@ -1,37 +0,0 @@ -package crisis - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/crisis/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" -) - -const ( - ModuleName = types.ModuleName - DefaultParamspace = types.DefaultParamspace - EventTypeInvariant = types.EventTypeInvariant - AttributeValueCrisis = types.AttributeValueCrisis - AttributeKeyRoute = types.AttributeKeyRoute -) - -var ( - RegisterCodec = types.RegisterCodec - ErrNoSender = types.ErrNoSender - ErrUnknownInvariant = types.ErrUnknownInvariant - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - NewMsgVerifyInvariant = types.NewMsgVerifyInvariant - ParamKeyTable = types.ParamKeyTable - NewInvarRoute = types.NewInvarRoute - NewKeeper = keeper.NewKeeper - ModuleCdc = types.ModuleCdc - ParamStoreKeyConstantFee = types.ParamStoreKeyConstantFee -) - -type ( - GenesisState = types.GenesisState - MsgVerifyInvariant = types.MsgVerifyInvariant - InvarRoute = types.InvarRoute - Keeper = keeper.Keeper -) diff --git a/x/crisis/client/cli/cli_test.go b/x/crisis/client/cli/cli_test.go new file mode 100644 index 000000000000..92b439614943 --- /dev/null +++ b/x/crisis/client/cli/cli_test.go @@ -0,0 +1,112 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client/flags" + 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/crisis/client/cli" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestNewMsgVerifyInvariantTxCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "missing module", + []string{ + "", "total-supply", + 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, nil, 0, + }, + { + "missing invariant route", + []string{ + "bank", "", + 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, nil, 0, + }, + { + "valid transaction", + []string{ + "bank", "total-supply", + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewMsgVerifyInvariantTxCmd() + 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) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/crisis/client/cli/tx.go b/x/crisis/client/cli/tx.go index e2df844db639..c18231724ee3 100644 --- a/x/crisis/client/cli/tx.go +++ b/x/crisis/client/cli/tx.go @@ -1,54 +1,63 @@ -// nolint package cli import ( - "bufio" + "errors" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/x/crisis/types" ) -// command to replace a delegator's withdrawal address -func GetCmdInvariantBroken(cdc *codec.Codec) *cobra.Command { +// NewTxCmd returns a root CLI command handler for all x/crisis transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Crisis transactions subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand(NewMsgVerifyInvariantTxCmd()) + + return txCmd +} + +// NewMsgVerifyInvariantTxCmd returns a CLI command handler for creating a +// MsgVerifyInvariant transaction. +func NewMsgVerifyInvariantTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "invariant-broken [module-name] [invariant-route]", - Short: "submit proof that an invariant broken to halt the chain", + Short: "Submit proof that an invariant broken to halt the chain", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + moduleName, route := args[0], args[1] + if moduleName == "" { + return errors.New("invalid module name") + } + if route == "" { + return errors.New("invalid invariant route") + } - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + senderAddr := clientCtx.GetFromAddress() - senderAddr := cliCtx.GetFromAddress() - moduleName, route := args[0], args[1] msg := types.NewMsgVerifyInvariant(senderAddr, moduleName, route) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - return cmd -} -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(cdc *codec.Codec) *cobra.Command { - txCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Crisis transactions subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } + flags.AddTxFlagsToCmd(cmd) - txCmd.AddCommand(flags.PostCommands( - GetCmdInvariantBroken(cdc), - )...) - return txCmd + return cmd } diff --git a/x/crisis/genesis.go b/x/crisis/genesis.go deleted file mode 100644 index dede2ab89154..000000000000 --- a/x/crisis/genesis.go +++ /dev/null @@ -1,18 +0,0 @@ -package crisis - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" -) - -// new crisis genesis -func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, data types.GenesisState) { - keeper.SetConstantFee(ctx, data.ConstantFee) -} - -// ExportGenesis returns a GenesisState for a given context and keeper. -func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) types.GenesisState { - constantFee := keeper.GetConstantFee(ctx) - return types.NewGenesisState(constantFee) -} diff --git a/x/crisis/handler.go b/x/crisis/handler.go index c7e58b6861be..0e6cf985f877 100644 --- a/x/crisis/handler.go +++ b/x/crisis/handler.go @@ -3,85 +3,23 @@ package crisis import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" + "github.com/cosmos/cosmos-sdk/x/crisis/types" ) // RouterKey const RouterKey = types.ModuleName -func NewHandler(k keeper.Keeper) sdk.Handler { +func NewHandler(k types.MsgServer) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case types.MsgVerifyInvariant: - return handleMsgVerifyInvariant(ctx, msg, k) + case *types.MsgVerifyInvariant: + res, err := k.VerifyInvariant(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized crisis message type: %T", msg) } } } - -func handleMsgVerifyInvariant(ctx sdk.Context, msg types.MsgVerifyInvariant, k keeper.Keeper) (*sdk.Result, error) { - constantFee := sdk.NewCoins(k.GetConstantFee(ctx)) - - if err := k.SendCoinsFromAccountToFeeCollector(ctx, msg.Sender, constantFee); err != nil { - return nil, err - } - - // use a cached context to avoid gas costs during invariants - cacheCtx, _ := ctx.CacheContext() - - found := false - msgFullRoute := msg.FullInvariantRoute() - - var res string - var stop bool - for _, invarRoute := range k.Routes() { - if invarRoute.FullRoute() == msgFullRoute { - res, stop = invarRoute.Invar(cacheCtx) - found = true - break - } - } - - if !found { - return nil, types.ErrUnknownInvariant - } - - if stop { - // NOTE currently, because the chain halts here, this transaction will never be included - // in the blockchain thus the constant fee will have never been deducted. Thus no - // refund is required. - - // TODO uncomment the following code block with implementation of the circuit breaker - //// refund constant fee - //err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender) - //if err != nil { - //// if there are insufficient coins to refund, log the error, - //// but still halt the chain. - //logger := ctx.Logger().With("module", "x/crisis") - //logger.Error(fmt.Sprintf( - //"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err)) - //} - - // TODO replace with circuit breaker - panic(res) - } - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeInvariant, - sdk.NewAttribute(types.AttributeKeyRoute, msg.InvariantRoute), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCrisis), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), - ), - }) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} diff --git a/x/crisis/handler_test.go b/x/crisis/handler_test.go index 912bd863f7c5..dcde377f6d42 100644 --- a/x/crisis/handler_test.go +++ b/x/crisis/handler_test.go @@ -5,40 +5,42 @@ 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" "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" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( testModuleName = "dummy" - dummyRouteWhichPasses = crisis.NewInvarRoute(testModuleName, "which-passes", func(_ sdk.Context) (string, bool) { return "", false }) - dummyRouteWhichFails = crisis.NewInvarRoute(testModuleName, "which-fails", func(_ sdk.Context) (string, bool) { return "whoops", true }) + dummyRouteWhichPasses = types.NewInvarRoute(testModuleName, "which-passes", func(_ sdk.Context) (string, bool) { return "", false }) + dummyRouteWhichFails = types.NewInvarRoute(testModuleName, "which-fails", func(_ sdk.Context) (string, bool) { return "whoops", true }) ) func createTestApp() (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { db := dbm.NewMemDB() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 1) - ctx := app.NewContext(true, abci.Header{}) + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 1, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + ctx := app.NewContext(true, tmproto.Header{}) constantFee := sdk.NewInt64Coin(sdk.DefaultBondDenom, 10) app.CrisisKeeper.SetConstantFee(ctx, constantFee) - app.StakingKeeper.SetParams(ctx, staking.DefaultParams()) + app.StakingKeeper.SetParams(ctx, stakingtypes.DefaultParams()) app.CrisisKeeper.RegisterRoute(testModuleName, dummyRouteWhichPasses.Route, dummyRouteWhichPasses.Invar) app.CrisisKeeper.RegisterRoute(testModuleName, dummyRouteWhichFails.Route, dummyRouteWhichFails.Invar) - feePool := distr.InitialFeePool() + feePool := distrtypes.InitialFeePool() feePool.CommunityPool = sdk.NewDecCoinsFromCoins(sdk.NewCoins(constantFee)...) app.DistrKeeper.SetFeePool(ctx, feePool) - app.SupplyKeeper.SetSupply(ctx, supply.NewSupply(sdk.Coins{})) + app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(sdk.Coins{})) addrs := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(10000)) @@ -56,10 +58,10 @@ func TestHandleMsgVerifyInvariant(t *testing.T) { msg sdk.Msg expectedResult string }{ - {"bad invariant route", crisis.NewMsgVerifyInvariant(sender, testModuleName, "route-that-doesnt-exist"), "fail"}, - {"invariant broken", crisis.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route), "panic"}, - {"invariant passing", crisis.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route), "pass"}, - {"invalid msg", sdk.NewTestMsg(), "fail"}, + {"bad invariant route", types.NewMsgVerifyInvariant(sender, testModuleName, "route-that-doesnt-exist"), "fail"}, + {"invariant broken", types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route), "panic"}, + {"invariant passing", types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route), "pass"}, + {"invalid msg", testdata.NewTestMsg(), "fail"}, } for _, tc := range cases { @@ -80,7 +82,7 @@ func TestHandleMsgVerifyInvariant(t *testing.T) { case "panic": require.Panics(t, func() { - h(ctx, tc.msg) + h(ctx, tc.msg) // nolint:errcheck }) } }) @@ -90,12 +92,12 @@ func TestHandleMsgVerifyInvariant(t *testing.T) { func TestHandleMsgVerifyInvariantWithNotEnoughSenderCoins(t *testing.T) { app, ctx, addrs := createTestApp() sender := addrs[0] - coin := app.AccountKeeper.GetAccount(ctx, sender).GetCoins()[0] + coin := app.BankKeeper.GetAllBalances(ctx, sender)[0] excessCoins := sdk.NewCoin(coin.Denom, coin.Amount.AddRaw(1)) app.CrisisKeeper.SetConstantFee(ctx, excessCoins) h := crisis.NewHandler(app.CrisisKeeper) - msg := crisis.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route) + msg := types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route) res, err := h(ctx, msg) require.Error(t, err) @@ -112,7 +114,7 @@ func TestHandleMsgVerifyInvariantWithInvariantBrokenAndNotEnoughPoolCoins(t *tes app.DistrKeeper.SetFeePool(ctx, feePool) h := crisis.NewHandler(app.CrisisKeeper) - msg := crisis.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route) + msg := types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route) var res *sdk.Result require.Panics(t, func() { diff --git a/x/crisis/internal/keeper/integration_test.go b/x/crisis/internal/keeper/integration_test.go deleted file mode 100644 index a98a0b44533c..000000000000 --- a/x/crisis/internal/keeper/integration_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package keeper_test - -import ( - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/simapp" -) - -func createTestApp() *simapp.SimApp { - db := dbm.NewMemDB() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 5) - // init chain must be called to stop deliverState from being nil - genesisState := simapp.NewDefaultGenesisState() - stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) - if err != nil { - panic(err) - } - - // Initialize the chain - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - AppStateBytes: stateBytes, - }, - ) - app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: app.LastBlockHeight() + 1}}) - - return app -} diff --git a/x/crisis/internal/keeper/keeper.go b/x/crisis/internal/keeper/keeper.go deleted file mode 100644 index f4c8163bb740..000000000000 --- a/x/crisis/internal/keeper/keeper.go +++ /dev/null @@ -1,95 +0,0 @@ -package keeper - -import ( - "fmt" - "time" - - "github.com/tendermint/tendermint/libs/log" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper - crisis keeper -type Keeper struct { - routes []types.InvarRoute - paramSpace params.Subspace - invCheckPeriod uint - - supplyKeeper types.SupplyKeeper - - feeCollectorName string // name of the FeeCollector ModuleAccount -} - -// NewKeeper creates a new Keeper object -func NewKeeper( - paramSpace params.Subspace, invCheckPeriod uint, supplyKeeper types.SupplyKeeper, - feeCollectorName string, -) Keeper { - - return Keeper{ - routes: make([]types.InvarRoute, 0), - paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()), - invCheckPeriod: invCheckPeriod, - supplyKeeper: supplyKeeper, - feeCollectorName: feeCollectorName, - } -} - -// 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)) -} - -// RegisterRoute register the routes for each of the invariants -func (k *Keeper) RegisterRoute(moduleName, route string, invar sdk.Invariant) { - invarRoute := types.NewInvarRoute(moduleName, route, invar) - k.routes = append(k.routes, invarRoute) -} - -// Routes - return the keeper's invariant routes -func (k Keeper) Routes() []types.InvarRoute { - return k.routes -} - -// Invariants returns all the registered Crisis keeper invariants. -func (k Keeper) Invariants() []sdk.Invariant { - invars := make([]sdk.Invariant, len(k.routes)) - for i, route := range k.routes { - invars[i] = route.Invar - } - return invars -} - -// AssertInvariants asserts all registered invariants. If any invariant fails, -// the method panics. -func (k Keeper) AssertInvariants(ctx sdk.Context) { - logger := k.Logger(ctx) - - start := time.Now() - invarRoutes := k.Routes() - - for _, ir := range invarRoutes { - if res, stop := ir.Invar(ctx); stop { - // TODO: Include app name as part of context to allow for this to be - // variable. - panic(fmt.Errorf("invariant broken: %s\n"+ - "\tCRITICAL please submit the following transaction:\n"+ - "\t\t tx crisis invariant-broken %s %s", res, ir.ModuleName, ir.Route)) - } - } - - end := time.Now() - diff := end.Sub(start) - - logger.Info("asserted all invariants", "duration", diff, "height", ctx.BlockHeight()) -} - -// InvCheckPeriod returns the invariant checks period. -func (k Keeper) InvCheckPeriod() uint { return k.invCheckPeriod } - -// SendCoinsFromAccountToFeeCollector transfers amt to the fee collector account. -func (k Keeper) SendCoinsFromAccountToFeeCollector(ctx sdk.Context, senderAddr sdk.AccAddress, amt sdk.Coins) error { - return k.supplyKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, k.feeCollectorName, amt) -} diff --git a/x/crisis/internal/keeper/keeper_test.go b/x/crisis/internal/keeper/keeper_test.go deleted file mode 100644 index 80f292008bfb..000000000000 --- a/x/crisis/internal/keeper/keeper_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package keeper_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestLogger(t *testing.T) { - app := createTestApp() - - ctx := app.NewContext(true, abci.Header{}) - require.Equal(t, ctx.Logger(), app.CrisisKeeper.Logger(ctx)) -} - -func TestInvariants(t *testing.T) { - app := createTestApp() - require.Equal(t, app.CrisisKeeper.InvCheckPeriod(), uint(5)) - - // SimApp has 11 registered invariants - orgInvRoutes := app.CrisisKeeper.Routes() - app.CrisisKeeper.RegisterRoute("testModule", "testRoute", func(sdk.Context) (string, bool) { return "", false }) - require.Equal(t, len(app.CrisisKeeper.Routes()), len(orgInvRoutes)+1) -} - -func TestAssertInvariants(t *testing.T) { - app := createTestApp() - ctx := app.NewContext(true, abci.Header{}) - - app.CrisisKeeper.RegisterRoute("testModule", "testRoute1", func(sdk.Context) (string, bool) { return "", false }) - require.NotPanics(t, func() { app.CrisisKeeper.AssertInvariants(ctx) }) - - app.CrisisKeeper.RegisterRoute("testModule", "testRoute2", func(sdk.Context) (string, bool) { return "", true }) - require.Panics(t, func() { app.CrisisKeeper.AssertInvariants(ctx) }) -} diff --git a/x/crisis/internal/keeper/params.go b/x/crisis/internal/keeper/params.go deleted file mode 100644 index cdf7169af07a..000000000000 --- a/x/crisis/internal/keeper/params.go +++ /dev/null @@ -1,17 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" -) - -// GetConstantFee get's the constant fee from the paramSpace -func (k Keeper) GetConstantFee(ctx sdk.Context) (constantFee sdk.Coin) { - k.paramSpace.Get(ctx, types.ParamStoreKeyConstantFee, &constantFee) - return -} - -// GetConstantFee set's the constant fee in the paramSpace -func (k Keeper) SetConstantFee(ctx sdk.Context, constantFee sdk.Coin) { - k.paramSpace.Set(ctx, types.ParamStoreKeyConstantFee, constantFee) -} diff --git a/x/crisis/internal/types/codec.go b/x/crisis/internal/types/codec.go deleted file mode 100644 index 92aeb130bce8..000000000000 --- a/x/crisis/internal/types/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgVerifyInvariant{}, "cosmos-sdk/MsgVerifyInvariant", nil) -} - -// generic sealed codec to be used throughout module -var ModuleCdc *codec.Codec - -func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() -} diff --git a/x/crisis/internal/types/errors.go b/x/crisis/internal/types/errors.go deleted file mode 100644 index ac63f3ff43de..000000000000 --- a/x/crisis/internal/types/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// x/crisis module sentinel errors -var ( - ErrNoSender = sdkerrors.Register(ModuleName, 1, "sender address is empty") - ErrUnknownInvariant = sdkerrors.Register(ModuleName, 2, "unknown invariant") -) diff --git a/x/crisis/internal/types/genesis.go b/x/crisis/internal/types/genesis.go deleted file mode 100644 index b38fd67166fc..000000000000 --- a/x/crisis/internal/types/genesis.go +++ /dev/null @@ -1,34 +0,0 @@ -package types - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState - crisis genesis state -type GenesisState struct { - ConstantFee sdk.Coin `json:"constant_fee" yaml:"constant_fee"` -} - -// NewGenesisState creates a new GenesisState object -func NewGenesisState(constantFee sdk.Coin) GenesisState { - return GenesisState{ - ConstantFee: constantFee, - } -} - -// DefaultGenesisState creates a default GenesisState object -func DefaultGenesisState() GenesisState { - return GenesisState{ - ConstantFee: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), - } -} - -// ValidateGenesis - validate crisis genesis data -func ValidateGenesis(data GenesisState) error { - if !data.ConstantFee.IsPositive() { - return fmt.Errorf("constant fee must be positive: %s", data.ConstantFee) - } - return nil -} diff --git a/x/crisis/internal/types/msgs.go b/x/crisis/internal/types/msgs.go deleted file mode 100644 index f496df636b60..000000000000 --- a/x/crisis/internal/types/msgs.go +++ /dev/null @@ -1,52 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// MsgVerifyInvariant - message struct to verify a particular invariance -type MsgVerifyInvariant struct { - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - InvariantModuleName string `json:"invariant_module_name" yaml:"invariant_module_name"` - InvariantRoute string `json:"invariant_route" yaml:"invariant_route"` -} - -// ensure Msg interface compliance at compile time -var _ sdk.Msg = &MsgVerifyInvariant{} - -// NewMsgVerifyInvariant creates a new MsgVerifyInvariant object -func NewMsgVerifyInvariant(sender sdk.AccAddress, invariantModuleName, - invariantRoute string) MsgVerifyInvariant { - - return MsgVerifyInvariant{ - Sender: sender, - InvariantModuleName: invariantModuleName, - InvariantRoute: invariantRoute, - } -} - -//nolint -func (msg MsgVerifyInvariant) Route() string { return ModuleName } -func (msg MsgVerifyInvariant) Type() string { return "verify_invariant" } - -// get the bytes for the message signer to sign on -func (msg MsgVerifyInvariant) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} } - -// GetSignBytes gets the sign bytes for the msg MsgVerifyInvariant -func (msg MsgVerifyInvariant) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) -} - -// quick validity check -func (msg MsgVerifyInvariant) ValidateBasic() error { - if msg.Sender.Empty() { - return ErrNoSender - } - return nil -} - -// FullInvariantRoute - get the messages full invariant route -func (msg MsgVerifyInvariant) FullInvariantRoute() string { - return msg.InvariantModuleName + "/" + msg.InvariantRoute -} diff --git a/x/crisis/internal/types/params.go b/x/crisis/internal/types/params.go deleted file mode 100644 index 5749c9192e34..000000000000 --- a/x/crisis/internal/types/params.go +++ /dev/null @@ -1,38 +0,0 @@ -package types - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Default parameter namespace -const ( - DefaultParamspace = ModuleName -) - -var ( - // key for constant fee parameter - ParamStoreKeyConstantFee = []byte("ConstantFee") -) - -// type declaration for parameters -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable( - params.NewParamSetPair(ParamStoreKeyConstantFee, sdk.Coin{}, validateConstantFee), - ) -} - -func validateConstantFee(i interface{}) error { - v, ok := i.(sdk.Coin) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if !v.IsValid() { - return fmt.Errorf("invalid constant fee: %s", v) - } - - return nil -} diff --git a/x/crisis/keeper/genesis.go b/x/crisis/keeper/genesis.go new file mode 100644 index 000000000000..8420201d4e96 --- /dev/null +++ b/x/crisis/keeper/genesis.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/types" +) + +// new crisis genesis +func (k Keeper) InitGenesis(ctx sdk.Context, data *types.GenesisState) { + k.SetConstantFee(ctx, data.ConstantFee) +} + +// ExportGenesis returns a GenesisState for a given context and keeper. +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + constantFee := k.GetConstantFee(ctx) + return types.NewGenesisState(constantFee) +} diff --git a/x/crisis/keeper/keeper.go b/x/crisis/keeper/keeper.go new file mode 100644 index 000000000000..b9c563d5293d --- /dev/null +++ b/x/crisis/keeper/keeper.go @@ -0,0 +1,99 @@ +package keeper + +import ( + "fmt" + "time" + + "github.com/tendermint/tendermint/libs/log" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Keeper - crisis keeper +type Keeper struct { + routes []types.InvarRoute + paramSpace paramtypes.Subspace + invCheckPeriod uint + + supplyKeeper types.SupplyKeeper + + feeCollectorName string // name of the FeeCollector ModuleAccount +} + +// NewKeeper creates a new Keeper object +func NewKeeper( + paramSpace paramtypes.Subspace, invCheckPeriod uint, supplyKeeper types.SupplyKeeper, + feeCollectorName string, +) Keeper { + + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + routes: make([]types.InvarRoute, 0), + paramSpace: paramSpace, + invCheckPeriod: invCheckPeriod, + supplyKeeper: supplyKeeper, + feeCollectorName: feeCollectorName, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// RegisterRoute register the routes for each of the invariants +func (k *Keeper) RegisterRoute(moduleName, route string, invar sdk.Invariant) { + invarRoute := types.NewInvarRoute(moduleName, route, invar) + k.routes = append(k.routes, invarRoute) +} + +// Routes - return the keeper's invariant routes +func (k Keeper) Routes() []types.InvarRoute { + return k.routes +} + +// Invariants returns a copy of all registered Crisis keeper invariants. +func (k Keeper) Invariants() []sdk.Invariant { + invars := make([]sdk.Invariant, len(k.routes)) + for i, route := range k.routes { + invars[i] = route.Invar + } + return invars +} + +// AssertInvariants asserts all registered invariants. If any invariant fails, +// the method panics. +func (k Keeper) AssertInvariants(ctx sdk.Context) { + logger := k.Logger(ctx) + + start := time.Now() + invarRoutes := k.Routes() + n := len(invarRoutes) + for i, ir := range invarRoutes { + logger.Info("asserting crisis invariants", "inv", fmt.Sprint(i, "/", n), "name", ir.FullRoute()) + if res, stop := ir.Invar(ctx); stop { + // TODO: Include app name as part of context to allow for this to be + // variable. + panic(fmt.Errorf("invariant broken: %s\n"+ + "\tCRITICAL please submit the following transaction:\n"+ + "\t\t tx crisis invariant-broken %s %s", res, ir.ModuleName, ir.Route)) + } + } + + diff := time.Since(start) + logger.Info("asserted all invariants", "duration", diff, "height", ctx.BlockHeight()) +} + +// InvCheckPeriod returns the invariant checks period. +func (k Keeper) InvCheckPeriod() uint { return k.invCheckPeriod } + +// SendCoinsFromAccountToFeeCollector transfers amt to the fee collector account. +func (k Keeper) SendCoinsFromAccountToFeeCollector(ctx sdk.Context, senderAddr sdk.AccAddress, amt sdk.Coins) error { + return k.supplyKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, k.feeCollectorName, amt) +} diff --git a/x/crisis/keeper/keeper_test.go b/x/crisis/keeper/keeper_test.go new file mode 100644 index 000000000000..563c0e4b1096 --- /dev/null +++ b/x/crisis/keeper/keeper_test.go @@ -0,0 +1,46 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestLogger(t *testing.T) { + app := simapp.Setup(false) + + ctx := app.NewContext(true, tmproto.Header{}) + require.Equal(t, ctx.Logger(), app.CrisisKeeper.Logger(ctx)) +} + +func TestInvariants(t *testing.T) { + app := simapp.Setup(false) + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) + + require.Equal(t, app.CrisisKeeper.InvCheckPeriod(), uint(5)) + + // SimApp has 11 registered invariants + orgInvRoutes := app.CrisisKeeper.Routes() + app.CrisisKeeper.RegisterRoute("testModule", "testRoute", func(sdk.Context) (string, bool) { return "", false }) + require.Equal(t, len(app.CrisisKeeper.Routes()), len(orgInvRoutes)+1) +} + +func TestAssertInvariants(t *testing.T) { + app := simapp.Setup(false) + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) + + ctx := app.NewContext(true, tmproto.Header{}) + + app.CrisisKeeper.RegisterRoute("testModule", "testRoute1", func(sdk.Context) (string, bool) { return "", false }) + require.NotPanics(t, func() { app.CrisisKeeper.AssertInvariants(ctx) }) + + app.CrisisKeeper.RegisterRoute("testModule", "testRoute2", func(sdk.Context) (string, bool) { return "", true }) + require.Panics(t, func() { app.CrisisKeeper.AssertInvariants(ctx) }) +} diff --git a/x/crisis/keeper/msg_server.go b/x/crisis/keeper/msg_server.go new file mode 100644 index 000000000000..304f8b17243d --- /dev/null +++ b/x/crisis/keeper/msg_server.go @@ -0,0 +1,78 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/types" +) + +var _ types.MsgServer = Keeper{} + +func (k Keeper) VerifyInvariant(goCtx context.Context, msg *types.MsgVerifyInvariant) (*types.MsgVerifyInvariantResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + constantFee := sdk.NewCoins(k.GetConstantFee(ctx)) + + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, err + } + if err := k.SendCoinsFromAccountToFeeCollector(ctx, sender, constantFee); err != nil { + return nil, err + } + + // use a cached context to avoid gas costs during invariants + cacheCtx, _ := ctx.CacheContext() + + found := false + msgFullRoute := msg.FullInvariantRoute() + + var res string + var stop bool + for _, invarRoute := range k.Routes() { + if invarRoute.FullRoute() == msgFullRoute { + res, stop = invarRoute.Invar(cacheCtx) + found = true + + break + } + } + + if !found { + return nil, types.ErrUnknownInvariant + } + + if stop { + // NOTE currently, because the chain halts here, this transaction will never be included + // in the blockchain thus the constant fee will have never been deducted. Thus no + // refund is required. + + // TODO uncomment the following code block with implementation of the circuit breaker + //// refund constant fee + //err := k.distrKeeper.DistributeFromFeePool(ctx, constantFee, msg.Sender) + //if err != nil { + //// if there are insufficient coins to refund, log the error, + //// but still halt the chain. + //logger := ctx.Logger().With("module", "x/crisis") + //logger.Error(fmt.Sprintf( + //"WARNING: insufficient funds to allocate to sender from fee pool, err: %s", err)) + //} + + // TODO replace with circuit breaker + panic(res) + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeInvariant, + sdk.NewAttribute(types.AttributeKeyRoute, msg.InvariantRoute), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCrisis), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), + ), + }) + + return &types.MsgVerifyInvariantResponse{}, nil +} diff --git a/x/crisis/keeper/params.go b/x/crisis/keeper/params.go new file mode 100644 index 000000000000..d44efa4ebf11 --- /dev/null +++ b/x/crisis/keeper/params.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/crisis/types" +) + +// GetConstantFee get's the constant fee from the paramSpace +func (k Keeper) GetConstantFee(ctx sdk.Context) (constantFee sdk.Coin) { + k.paramSpace.Get(ctx, types.ParamStoreKeyConstantFee, &constantFee) + return +} + +// GetConstantFee set's the constant fee in the paramSpace +func (k Keeper) SetConstantFee(ctx sdk.Context, constantFee sdk.Coin) { + k.paramSpace.Set(ctx, types.ParamStoreKeyConstantFee, constantFee) +} diff --git a/x/crisis/legacy/v039/types.go b/x/crisis/legacy/v039/types.go new file mode 100644 index 000000000000..44903e349118 --- /dev/null +++ b/x/crisis/legacy/v039/types.go @@ -0,0 +1,13 @@ +package v039 + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + ModuleName = "crisis" +) + +type ( + GenesisState struct { + ConstantFee sdk.Coin `json:"constant_fee" yaml:"constant_fee"` + } +) diff --git a/x/crisis/legacy/v040/migrate.go b/x/crisis/legacy/v040/migrate.go new file mode 100644 index 000000000000..eb97ba9ee645 --- /dev/null +++ b/x/crisis/legacy/v040/migrate.go @@ -0,0 +1,16 @@ +package v040 + +import ( + v039crisis "github.com/cosmos/cosmos-sdk/x/crisis/legacy/v039" + v040crisis "github.com/cosmos/cosmos-sdk/x/crisis/types" +) + +// Migrate accepts exported v0.39 x/crisis genesis state and +// migrates it to v0.40 x/crisis genesis state. The migration includes: +// +// - Re-encode in v0.40 GenesisState. +func Migrate(crisisGenState v039crisis.GenesisState) *v040crisis.GenesisState { + return &v040crisis.GenesisState{ + ConstantFee: crisisGenState.ConstantFee, + } +} diff --git a/x/crisis/legacy/v040/types.go b/x/crisis/legacy/v040/types.go new file mode 100644 index 000000000000..68c25e93eec1 --- /dev/null +++ b/x/crisis/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "crisis" +) diff --git a/x/crisis/module.go b/x/crisis/module.go index 1885f69ade9f..5d34c1ce28f9 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -3,19 +3,22 @@ package crisis import ( "encoding/json" "fmt" + "time" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/crisis/client/cli" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/crisis/internal/types" + "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + "github.com/cosmos/cosmos-sdk/x/crisis/types" ) var ( @@ -23,45 +26,59 @@ var ( _ module.AppModuleBasic = AppModuleBasic{} ) +// Module init related flags +const ( + FlagSkipGenesisInvariants = "x-crisis-skip-assert-invariants" +) + // AppModuleBasic defines the basic application module used by the crisis module. type AppModuleBasic struct{} // Name returns the crisis module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the crisis module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the crisis module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the crisis // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return types.ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the crisis module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { var data types.GenesisState - if err := types.ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return types.ValidateGenesis(data) + return types.ValidateGenesis(&data) } // RegisterRESTRoutes registers no REST routes for the crisis module. -func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the capability module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) {} // GetTxCmd returns the root tx command for the crisis module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(cdc) +func (b AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() } // GetQueryCmd returns no root query command for the crisis module. -func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } + +// RegisterInterfaces registers interfaces and implementations of the crisis +// module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} //____________________________________________________________________________ @@ -73,56 +90,72 @@ type AppModule struct { // manager is created, the invariants can be properly registered and // executed. keeper *keeper.Keeper + + skipGenesisInvariants bool } -// NewAppModule creates a new AppModule object -func NewAppModule(keeper *keeper.Keeper) AppModule { +// NewAppModule creates a new AppModule object. If initChainAssertInvariants is set, +// we will call keeper.AssertInvariants during InitGenesis (it may take a significant time) +// - which doesn't impact the chain security unless 66+% of validators have a wrongly +// modified genesis file. +func NewAppModule(keeper *keeper.Keeper, skipGenesisInvariants bool) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, + + skipGenesisInvariants: skipGenesisInvariants, } } +// AddModuleInitFlags implements servertypes.ModuleInitFlags interface. +func AddModuleInitFlags(startCmd *cobra.Command) { + startCmd.Flags().Bool(FlagSkipGenesisInvariants, false, "Skip x/crisis invariants check on startup") +} + // Name returns the crisis module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants performs a no-op. func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route returns the message routing key for the crisis module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the crisis module. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(*am.keeper) +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(RouterKey, NewHandler(*am.keeper)) } // QuerierRoute returns no querier route. func (AppModule) QuerierRoute() string { return "" } -// NewQuerierHandler returns no sdk.Querier. -func (AppModule) NewQuerierHandler() sdk.Querier { return nil } +// LegacyQuerierHandler returns no sdk.Querier. +func (AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { return nil } + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) +} // InitGenesis performs genesis initialization for the crisis module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, *am.keeper, genesisState) - - am.keeper.AssertInvariants(ctx) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + start := time.Now() + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + telemetry.MeasureSince(start, "InitGenesis", "crisis", "unmarshal") + + am.keeper.InitGenesis(ctx, &genesisState) + if !am.skipGenesisInvariants { + am.keeper.AssertInvariants(ctx) + } return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the crisis // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - gs := ExportGenesis(ctx, *am.keeper) - return types.ModuleCdc.MustMarshalJSON(gs) +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) } // BeginBlock performs a no-op. 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..5dce6274091d 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/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/spec/03_events.md b/x/crisis/spec/03_events.md index 5aef2078ebf9..90070e48286d 100644 --- a/x/crisis/spec/03_events.md +++ b/x/crisis/spec/03_events.md @@ -6,9 +6,9 @@ order: 3 The crisis module emits the following events: -## Handlers +## MsgServer -### MsgVerifyInvariance +### MsgVerifyInvariant | Type | Attribute Key | Attribute Value | |-----------|---------------|------------------| diff --git a/x/crisis/types/codec.go b/x/crisis/types/codec.go new file mode 100644 index 000000000000..ac02c54630cb --- /dev/null +++ b/x/crisis/types/codec.go @@ -0,0 +1,41 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers the necessary x/crisis interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgVerifyInvariant{}, "cosmos-sdk/MsgVerifyInvariant", nil) +} + +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgVerifyInvariant{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/crisis module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/crisis and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/crisis/types/errors.go b/x/crisis/types/errors.go new file mode 100644 index 000000000000..cf8c4901b315 --- /dev/null +++ b/x/crisis/types/errors.go @@ -0,0 +1,11 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/crisis module sentinel errors +var ( + ErrNoSender = sdkerrors.Register(ModuleName, 2, "sender address is empty") + ErrUnknownInvariant = sdkerrors.Register(ModuleName, 3, "unknown invariant") +) diff --git a/x/crisis/internal/types/events.go b/x/crisis/types/events.go similarity index 100% rename from x/crisis/internal/types/events.go rename to x/crisis/types/events.go diff --git a/x/crisis/internal/types/expected_keepers.go b/x/crisis/types/expected_keepers.go similarity index 100% rename from x/crisis/internal/types/expected_keepers.go rename to x/crisis/types/expected_keepers.go diff --git a/x/crisis/types/genesis.go b/x/crisis/types/genesis.go new file mode 100644 index 000000000000..f53099130277 --- /dev/null +++ b/x/crisis/types/genesis.go @@ -0,0 +1,29 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewGenesisState creates a new GenesisState object +func NewGenesisState(constantFee sdk.Coin) *GenesisState { + return &GenesisState{ + ConstantFee: constantFee, + } +} + +// DefaultGenesisState creates a default GenesisState object +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + ConstantFee: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)), + } +} + +// ValidateGenesis - validate crisis genesis data +func ValidateGenesis(data *GenesisState) error { + if !data.ConstantFee.IsPositive() { + return fmt.Errorf("constant fee must be positive: %s", data.ConstantFee) + } + return nil +} diff --git a/x/crisis/types/genesis.pb.go b/x/crisis/types/genesis.pb.go new file mode 100644 index 000000000000..06a870ff7193 --- /dev/null +++ b/x/crisis/types/genesis.pb.go @@ -0,0 +1,328 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crisis/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "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 defines the crisis module's genesis state. +type GenesisState struct { + // constant_fee is the fee used to verify the invariant in the crisis + // module. + ConstantFee types.Coin `protobuf:"bytes,3,opt,name=constant_fee,json=constantFee,proto3" json:"constant_fee" yaml:"constant_fee"` +} + +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_7a9c2781aa8a27ae, []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) GetConstantFee() types.Coin { + if m != nil { + return m.ConstantFee + } + return types.Coin{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.crisis.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/crisis/v1beta1/genesis.proto", fileDescriptor_7a9c2781aa8a27ae) +} + +var fileDescriptor_7a9c2781aa8a27ae = []byte{ + // 238 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4e, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xca, 0x2c, 0xce, 0x2c, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, + 0x34, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x12, 0x85, 0x28, 0xd2, 0x83, 0x28, 0xd2, 0x83, 0x2a, 0x92, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, + 0xab, 0xd0, 0x07, 0xb1, 0x20, 0x8a, 0xa5, 0xe4, 0xa0, 0x26, 0x26, 0x25, 0x16, 0xa7, 0xc2, 0xcd, + 0x4b, 0xce, 0xcf, 0xcc, 0x83, 0xc8, 0x2b, 0x65, 0x72, 0xf1, 0xb8, 0x43, 0x4c, 0x0f, 0x2e, 0x49, + 0x2c, 0x49, 0x15, 0x8a, 0xe4, 0xe2, 0x49, 0xce, 0xcf, 0x2b, 0x2e, 0x49, 0xcc, 0x2b, 0x89, 0x4f, + 0x4b, 0x4d, 0x95, 0x60, 0x56, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd4, 0x83, 0xda, 0x09, 0x32, 0x06, + 0x66, 0xa3, 0x9e, 0x73, 0x7e, 0x66, 0x9e, 0x93, 0xf4, 0x89, 0x7b, 0xf2, 0x0c, 0x9f, 0xee, 0xc9, + 0x0b, 0x57, 0x26, 0xe6, 0xe6, 0x58, 0x29, 0x21, 0x6b, 0x56, 0x0a, 0xe2, 0x86, 0x71, 0xdd, 0x52, + 0x53, 0x9d, 0x5c, 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, 0x3b, 0x3d, + 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x16, 0x02, 0x60, 0x4a, 0xb7, 0x38, + 0x25, 0x5b, 0xbf, 0x02, 0x16, 0x1c, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x87, 0x1b, + 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x43, 0x06, 0xff, 0x2c, 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 + { + size, err := m.ConstantFee.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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 + l = m.ConstantFee.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 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConstantFee", 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 := m.ConstantFee.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/crisis/internal/types/keys.go b/x/crisis/types/keys.go similarity index 100% rename from x/crisis/internal/types/keys.go rename to x/crisis/types/keys.go diff --git a/x/crisis/types/msgs.go b/x/crisis/types/msgs.go new file mode 100644 index 000000000000..6161a1d4b73e --- /dev/null +++ b/x/crisis/types/msgs.go @@ -0,0 +1,46 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure Msg interface compliance at compile time +var _ sdk.Msg = &MsgVerifyInvariant{} + +// NewMsgVerifyInvariant creates a new MsgVerifyInvariant object +//nolint:interfacer +func NewMsgVerifyInvariant(sender sdk.AccAddress, invModeName, invRoute string) *MsgVerifyInvariant { + return &MsgVerifyInvariant{ + Sender: sender.String(), + InvariantModuleName: invModeName, + InvariantRoute: invRoute, + } +} + +func (msg MsgVerifyInvariant) Route() string { return ModuleName } +func (msg MsgVerifyInvariant) Type() string { return "verify_invariant" } + +// get the bytes for the message signer to sign on +func (msg MsgVerifyInvariant) GetSigners() []sdk.AccAddress { + sender, _ := sdk.AccAddressFromBech32(msg.Sender) + return []sdk.AccAddress{sender} +} + +// GetSignBytes gets the sign bytes for the msg MsgVerifyInvariant +func (msg MsgVerifyInvariant) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgVerifyInvariant) ValidateBasic() error { + if msg.Sender == "" { + return ErrNoSender + } + return nil +} + +// FullInvariantRoute - get the messages full invariant route +func (msg MsgVerifyInvariant) FullInvariantRoute() string { + return msg.InvariantModuleName + "/" + msg.InvariantRoute +} diff --git a/x/crisis/types/params.go b/x/crisis/types/params.go new file mode 100644 index 000000000000..880f350f117c --- /dev/null +++ b/x/crisis/types/params.go @@ -0,0 +1,33 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var ( + // key for constant fee parameter + ParamStoreKeyConstantFee = []byte("ConstantFee") +) + +// type declaration for parameters +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable( + paramtypes.NewParamSetPair(ParamStoreKeyConstantFee, sdk.Coin{}, validateConstantFee), + ) +} + +func validateConstantFee(i interface{}) error { + v, ok := i.(sdk.Coin) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if !v.IsValid() { + return fmt.Errorf("invalid constant fee: %s", v) + } + + return nil +} diff --git a/x/crisis/internal/types/route.go b/x/crisis/types/route.go similarity index 100% rename from x/crisis/internal/types/route.go rename to x/crisis/types/route.go diff --git a/x/crisis/types/tx.pb.go b/x/crisis/types/tx.pb.go new file mode 100644 index 000000000000..f84c4adbf106 --- /dev/null +++ b/x/crisis/types/tx.pb.go @@ -0,0 +1,614 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/crisis/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgVerifyInvariant represents a message to verify a particular invariance. +type MsgVerifyInvariant struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + InvariantModuleName string `protobuf:"bytes,2,opt,name=invariant_module_name,json=invariantModuleName,proto3" json:"invariant_module_name,omitempty" yaml:"invariant_module_name"` + InvariantRoute string `protobuf:"bytes,3,opt,name=invariant_route,json=invariantRoute,proto3" json:"invariant_route,omitempty" yaml:"invariant_route"` +} + +func (m *MsgVerifyInvariant) Reset() { *m = MsgVerifyInvariant{} } +func (m *MsgVerifyInvariant) String() string { return proto.CompactTextString(m) } +func (*MsgVerifyInvariant) ProtoMessage() {} +func (*MsgVerifyInvariant) Descriptor() ([]byte, []int) { + return fileDescriptor_61276163172fe867, []int{0} +} +func (m *MsgVerifyInvariant) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVerifyInvariant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVerifyInvariant.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 *MsgVerifyInvariant) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVerifyInvariant.Merge(m, src) +} +func (m *MsgVerifyInvariant) XXX_Size() int { + return m.Size() +} +func (m *MsgVerifyInvariant) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVerifyInvariant.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVerifyInvariant proto.InternalMessageInfo + +// MsgVerifyInvariantResponse defines the Msg/VerifyInvariant response type. +type MsgVerifyInvariantResponse struct { +} + +func (m *MsgVerifyInvariantResponse) Reset() { *m = MsgVerifyInvariantResponse{} } +func (m *MsgVerifyInvariantResponse) String() string { return proto.CompactTextString(m) } +func (*MsgVerifyInvariantResponse) ProtoMessage() {} +func (*MsgVerifyInvariantResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_61276163172fe867, []int{1} +} +func (m *MsgVerifyInvariantResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVerifyInvariantResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVerifyInvariantResponse.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 *MsgVerifyInvariantResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVerifyInvariantResponse.Merge(m, src) +} +func (m *MsgVerifyInvariantResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVerifyInvariantResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVerifyInvariantResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVerifyInvariantResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgVerifyInvariant)(nil), "cosmos.crisis.v1beta1.MsgVerifyInvariant") + proto.RegisterType((*MsgVerifyInvariantResponse)(nil), "cosmos.crisis.v1beta1.MsgVerifyInvariantResponse") +} + +func init() { proto.RegisterFile("cosmos/crisis/v1beta1/tx.proto", fileDescriptor_61276163172fe867) } + +var fileDescriptor_61276163172fe867 = []byte{ + // 318 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x2e, 0xca, 0x2c, 0xce, 0x2c, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, + 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x85, 0xc8, 0xeb, 0x41, + 0xe4, 0xf5, 0xa0, 0xf2, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x15, 0xfa, 0x20, 0x16, 0x44, + 0xb1, 0xd2, 0x45, 0x46, 0x2e, 0x21, 0xdf, 0xe2, 0xf4, 0xb0, 0xd4, 0xa2, 0xcc, 0xb4, 0x4a, 0xcf, + 0xbc, 0xb2, 0xc4, 0xa2, 0xcc, 0xc4, 0xbc, 0x12, 0x21, 0x31, 0x2e, 0xb6, 0xe2, 0xd4, 0xbc, 0x94, + 0xd4, 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x28, 0x4f, 0x28, 0x84, 0x4b, 0x34, 0x13, + 0xa6, 0x28, 0x3e, 0x37, 0x3f, 0xa5, 0x34, 0x27, 0x35, 0x3e, 0x2f, 0x31, 0x37, 0x55, 0x82, 0x09, + 0xa4, 0xcc, 0x49, 0xe1, 0xd3, 0x3d, 0x79, 0x99, 0xca, 0xc4, 0xdc, 0x1c, 0x2b, 0x25, 0xac, 0xca, + 0x94, 0x82, 0x84, 0xe1, 0xe2, 0xbe, 0x60, 0x61, 0xbf, 0xc4, 0xdc, 0x54, 0x21, 0x67, 0x2e, 0x7e, + 0x84, 0xf2, 0xa2, 0xfc, 0xd2, 0x92, 0x54, 0x09, 0x66, 0xb0, 0x79, 0x52, 0x9f, 0xee, 0xc9, 0x8b, + 0xa1, 0x9b, 0x07, 0x56, 0xa0, 0x14, 0xc4, 0x07, 0x17, 0x09, 0x02, 0x09, 0x58, 0x71, 0x74, 0x2c, + 0x90, 0x67, 0x78, 0xb1, 0x40, 0x9e, 0x41, 0x49, 0x86, 0x4b, 0x0a, 0xd3, 0x4b, 0x41, 0xa9, 0xc5, + 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x46, 0x65, 0x5c, 0xcc, 0xbe, 0xc5, 0xe9, 0x42, 0xf9, 0x5c, 0xfc, + 0xe8, 0x9e, 0xd6, 0xd4, 0xc3, 0x1a, 0x72, 0x7a, 0x98, 0x86, 0x49, 0x19, 0x12, 0xad, 0x14, 0x66, + 0xaf, 0x93, 0xeb, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, + 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xa7, 0x67, + 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xc3, 0xe2, 0x16, 0x4c, 0xe9, 0x16, 0xa7, + 0x64, 0xeb, 0x57, 0xc0, 0x22, 0xba, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0x6f, 0xc6, + 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9c, 0x60, 0x68, 0x5a, 0x06, 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 { + // VerifyInvariant defines a method to verify a particular invariance. + VerifyInvariant(ctx context.Context, in *MsgVerifyInvariant, opts ...grpc.CallOption) (*MsgVerifyInvariantResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) VerifyInvariant(ctx context.Context, in *MsgVerifyInvariant, opts ...grpc.CallOption) (*MsgVerifyInvariantResponse, error) { + out := new(MsgVerifyInvariantResponse) + err := c.cc.Invoke(ctx, "/cosmos.crisis.v1beta1.Msg/VerifyInvariant", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // VerifyInvariant defines a method to verify a particular invariance. + VerifyInvariant(context.Context, *MsgVerifyInvariant) (*MsgVerifyInvariantResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) VerifyInvariant(ctx context.Context, req *MsgVerifyInvariant) (*MsgVerifyInvariantResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VerifyInvariant not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_VerifyInvariant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVerifyInvariant) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VerifyInvariant(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.crisis.v1beta1.Msg/VerifyInvariant", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VerifyInvariant(ctx, req.(*MsgVerifyInvariant)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.crisis.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "VerifyInvariant", + Handler: _Msg_VerifyInvariant_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/crisis/v1beta1/tx.proto", +} + +func (m *MsgVerifyInvariant) 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 *MsgVerifyInvariant) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyInvariant) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.InvariantRoute) > 0 { + i -= len(m.InvariantRoute) + copy(dAtA[i:], m.InvariantRoute) + i = encodeVarintTx(dAtA, i, uint64(len(m.InvariantRoute))) + i-- + dAtA[i] = 0x1a + } + if len(m.InvariantModuleName) > 0 { + i -= len(m.InvariantModuleName) + copy(dAtA[i:], m.InvariantModuleName) + i = encodeVarintTx(dAtA, i, uint64(len(m.InvariantModuleName))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgVerifyInvariantResponse) 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 *MsgVerifyInvariantResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVerifyInvariantResponse) 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 *MsgVerifyInvariant) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.InvariantModuleName) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.InvariantRoute) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVerifyInvariantResponse) 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 *MsgVerifyInvariant) 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: MsgVerifyInvariant: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVerifyInvariant: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", 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.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InvariantModuleName", 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.InvariantModuleName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InvariantRoute", 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.InvariantRoute = 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 *MsgVerifyInvariantResponse) 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: MsgVerifyInvariantResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVerifyInvariantResponse: 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/distribution/abci.go b/x/distribution/abci.go index 44a2286cad22..7c612c1665f2 100644 --- a/x/distribution/abci.go +++ b/x/distribution/abci.go @@ -1,15 +1,21 @@ package distribution import ( + "time" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + "github.com/cosmos/cosmos-sdk/x/distribution/types" ) // BeginBlocker sets the proposer for determining distribution during endblock // and distribute rewards for the previous block func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + // determine the total power signing the block var previousTotalPower, sumPreviousPrecommitPower int64 for _, voteInfo := range req.LastCommitInfo.GetVotes() { diff --git a/x/distribution/alias.go b/x/distribution/alias.go deleted file mode 100644 index 47143d995cbb..000000000000 --- a/x/distribution/alias.go +++ /dev/null @@ -1,158 +0,0 @@ -package distribution - -import ( - "github.com/cosmos/cosmos-sdk/x/distribution/client" - "github.com/cosmos/cosmos-sdk/x/distribution/keeper" - "github.com/cosmos/cosmos-sdk/x/distribution/types" -) - -// nolint - -const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - ProposalTypeCommunityPoolSpend = types.ProposalTypeCommunityPoolSpend - QueryParams = types.QueryParams - QueryValidatorOutstandingRewards = types.QueryValidatorOutstandingRewards - QueryValidatorCommission = types.QueryValidatorCommission - QueryValidatorSlashes = types.QueryValidatorSlashes - QueryDelegationRewards = types.QueryDelegationRewards - QueryDelegatorTotalRewards = types.QueryDelegatorTotalRewards - QueryDelegatorValidators = types.QueryDelegatorValidators - QueryWithdrawAddr = types.QueryWithdrawAddr - QueryCommunityPool = types.QueryCommunityPool - DefaultParamspace = types.DefaultParamspace - TypeMsgFundCommunityPool = types.TypeMsgFundCommunityPool -) - -var ( - // functions aliases - RegisterInvariants = keeper.RegisterInvariants - AllInvariants = keeper.AllInvariants - NonNegativeOutstandingInvariant = keeper.NonNegativeOutstandingInvariant - CanWithdrawInvariant = keeper.CanWithdrawInvariant - ReferenceCountInvariant = keeper.ReferenceCountInvariant - ModuleAccountInvariant = keeper.ModuleAccountInvariant - NewKeeper = keeper.NewKeeper - GetValidatorOutstandingRewardsAddress = types.GetValidatorOutstandingRewardsAddress - GetDelegatorWithdrawInfoAddress = types.GetDelegatorWithdrawInfoAddress - GetDelegatorStartingInfoAddresses = types.GetDelegatorStartingInfoAddresses - GetValidatorHistoricalRewardsAddressPeriod = types.GetValidatorHistoricalRewardsAddressPeriod - GetValidatorCurrentRewardsAddress = types.GetValidatorCurrentRewardsAddress - GetValidatorAccumulatedCommissionAddress = types.GetValidatorAccumulatedCommissionAddress - GetValidatorSlashEventAddressHeight = types.GetValidatorSlashEventAddressHeight - GetValidatorOutstandingRewardsKey = types.GetValidatorOutstandingRewardsKey - GetDelegatorWithdrawAddrKey = types.GetDelegatorWithdrawAddrKey - GetDelegatorStartingInfoKey = types.GetDelegatorStartingInfoKey - GetValidatorHistoricalRewardsPrefix = types.GetValidatorHistoricalRewardsPrefix - GetValidatorHistoricalRewardsKey = types.GetValidatorHistoricalRewardsKey - GetValidatorCurrentRewardsKey = types.GetValidatorCurrentRewardsKey - GetValidatorAccumulatedCommissionKey = types.GetValidatorAccumulatedCommissionKey - GetValidatorSlashEventPrefix = types.GetValidatorSlashEventPrefix - GetValidatorSlashEventKeyPrefix = types.GetValidatorSlashEventKeyPrefix - GetValidatorSlashEventKey = types.GetValidatorSlashEventKey - HandleCommunityPoolSpendProposal = keeper.HandleCommunityPoolSpendProposal - NewQuerier = keeper.NewQuerier - MakeTestCodec = keeper.MakeTestCodec - CreateTestInputDefault = keeper.CreateTestInputDefault - CreateTestInputAdvanced = keeper.CreateTestInputAdvanced - ParamKeyTable = types.ParamKeyTable - DefaultParams = types.DefaultParams - RegisterCodec = types.RegisterCodec - NewDelegatorStartingInfo = types.NewDelegatorStartingInfo - ErrEmptyDelegatorAddr = types.ErrEmptyDelegatorAddr - ErrEmptyWithdrawAddr = types.ErrEmptyWithdrawAddr - ErrEmptyValidatorAddr = types.ErrEmptyValidatorAddr - ErrEmptyDelegationDistInfo = types.ErrEmptyDelegationDistInfo - ErrNoValidatorDistInfo = types.ErrNoValidatorDistInfo - ErrNoValidatorExists = types.ErrNoValidatorExists - ErrNoDelegationExists = types.ErrNoDelegationExists - ErrNoValidatorCommission = types.ErrNoValidatorCommission - ErrSetWithdrawAddrDisabled = types.ErrSetWithdrawAddrDisabled - ErrBadDistribution = types.ErrBadDistribution - ErrInvalidProposalAmount = types.ErrInvalidProposalAmount - ErrEmptyProposalRecipient = types.ErrEmptyProposalRecipient - InitialFeePool = types.InitialFeePool - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - NewMsgSetWithdrawAddress = types.NewMsgSetWithdrawAddress - NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward - NewMsgWithdrawValidatorCommission = types.NewMsgWithdrawValidatorCommission - MsgFundCommunityPool = types.NewMsgFundCommunityPool - NewCommunityPoolSpendProposal = types.NewCommunityPoolSpendProposal - NewQueryValidatorOutstandingRewardsParams = types.NewQueryValidatorOutstandingRewardsParams - NewQueryValidatorCommissionParams = types.NewQueryValidatorCommissionParams - NewQueryValidatorSlashesParams = types.NewQueryValidatorSlashesParams - NewQueryDelegationRewardsParams = types.NewQueryDelegationRewardsParams - NewQueryDelegatorParams = types.NewQueryDelegatorParams - NewQueryDelegatorWithdrawAddrParams = types.NewQueryDelegatorWithdrawAddrParams - NewQueryDelegatorTotalRewardsResponse = types.NewQueryDelegatorTotalRewardsResponse - NewDelegationDelegatorReward = types.NewDelegationDelegatorReward - NewValidatorHistoricalRewards = types.NewValidatorHistoricalRewards - NewValidatorCurrentRewards = types.NewValidatorCurrentRewards - InitialValidatorAccumulatedCommission = types.InitialValidatorAccumulatedCommission - NewValidatorSlashEvent = types.NewValidatorSlashEvent - - // variable aliases - FeePoolKey = types.FeePoolKey - ProposerKey = types.ProposerKey - ValidatorOutstandingRewardsPrefix = types.ValidatorOutstandingRewardsPrefix - DelegatorWithdrawAddrPrefix = types.DelegatorWithdrawAddrPrefix - DelegatorStartingInfoPrefix = types.DelegatorStartingInfoPrefix - ValidatorHistoricalRewardsPrefix = types.ValidatorHistoricalRewardsPrefix - ValidatorCurrentRewardsPrefix = types.ValidatorCurrentRewardsPrefix - ValidatorAccumulatedCommissionPrefix = types.ValidatorAccumulatedCommissionPrefix - ValidatorSlashEventPrefix = types.ValidatorSlashEventPrefix - ParamStoreKeyCommunityTax = types.ParamStoreKeyCommunityTax - ParamStoreKeyBaseProposerReward = types.ParamStoreKeyBaseProposerReward - ParamStoreKeyBonusProposerReward = types.ParamStoreKeyBonusProposerReward - ParamStoreKeyWithdrawAddrEnabled = types.ParamStoreKeyWithdrawAddrEnabled - ModuleCdc = types.ModuleCdc - EventTypeSetWithdrawAddress = types.EventTypeSetWithdrawAddress - EventTypeRewards = types.EventTypeRewards - EventTypeCommission = types.EventTypeCommission - EventTypeWithdrawRewards = types.EventTypeWithdrawRewards - EventTypeWithdrawCommission = types.EventTypeWithdrawCommission - EventTypeProposerReward = types.EventTypeProposerReward - AttributeKeyWithdrawAddress = types.AttributeKeyWithdrawAddress - AttributeKeyValidator = types.AttributeKeyValidator - AttributeValueCategory = types.AttributeValueCategory - ProposalHandler = client.ProposalHandler -) - -type ( - Hooks = keeper.Hooks - Keeper = keeper.Keeper - DelegatorStartingInfo = types.DelegatorStartingInfo - FeePool = types.FeePool - DelegatorWithdrawInfo = types.DelegatorWithdrawInfo - ValidatorOutstandingRewardsRecord = types.ValidatorOutstandingRewardsRecord - ValidatorAccumulatedCommissionRecord = types.ValidatorAccumulatedCommissionRecord - ValidatorHistoricalRewardsRecord = types.ValidatorHistoricalRewardsRecord - ValidatorCurrentRewardsRecord = types.ValidatorCurrentRewardsRecord - DelegatorStartingInfoRecord = types.DelegatorStartingInfoRecord - ValidatorSlashEventRecord = types.ValidatorSlashEventRecord - Params = types.Params - GenesisState = types.GenesisState - MsgSetWithdrawAddress = types.MsgSetWithdrawAddress - MsgWithdrawDelegatorReward = types.MsgWithdrawDelegatorReward - MsgWithdrawValidatorCommission = types.MsgWithdrawValidatorCommission - CommunityPoolSpendProposal = types.CommunityPoolSpendProposal - QueryValidatorOutstandingRewardsParams = types.QueryValidatorOutstandingRewardsParams - QueryValidatorCommissionParams = types.QueryValidatorCommissionParams - QueryValidatorSlashesParams = types.QueryValidatorSlashesParams - QueryDelegationRewardsParams = types.QueryDelegationRewardsParams - QueryDelegatorParams = types.QueryDelegatorParams - QueryDelegatorWithdrawAddrParams = types.QueryDelegatorWithdrawAddrParams - QueryDelegatorTotalRewardsResponse = types.QueryDelegatorTotalRewardsResponse - DelegationDelegatorReward = types.DelegationDelegatorReward - ValidatorHistoricalRewards = types.ValidatorHistoricalRewards - ValidatorCurrentRewards = types.ValidatorCurrentRewards - ValidatorAccumulatedCommission = types.ValidatorAccumulatedCommission - ValidatorSlashEvent = types.ValidatorSlashEvent - ValidatorSlashEvents = types.ValidatorSlashEvents - ValidatorOutstandingRewards = types.ValidatorOutstandingRewards -) diff --git a/x/distribution/client/cli/cli_test.go b/x/distribution/client/cli/cli_test.go new file mode 100644 index 000000000000..8a827fe69610 --- /dev/null +++ b/x/distribution/client/cli/cli_test.go @@ -0,0 +1,757 @@ +// +build norace + +package cli_test + +import ( + "context" + "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/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + testnet "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" + distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/client/testutil" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg testnet.Config + network *testnet.Network +} + +// SetupTest creates a new network for _each_ integration test. We create a new +// network for each test because there are some state modifications that are +// needed to be made in order to make useful queries. However, we don't want +// these state changes to be present in other tests. +func (s *IntegrationTestSuite) SetupTest() { + s.T().Log("setting up integration test suite") + + cfg := testnet.DefaultConfig() + genesisState := cfg.GenesisState + cfg.NumValidators = 1 + + var mintData minttypes.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[minttypes.ModuleName], &mintData)) + + inflation := sdk.MustNewDecFromStr("1.0") + mintData.Minter.Inflation = inflation + + mintDataBz, err := cfg.Codec.MarshalJSON(&mintData) + s.Require().NoError(err) + genesisState[minttypes.ModuleName] = mintDataBz + cfg.GenesisState = genesisState + + s.cfg = cfg + s.network = testnet.New(s.T(), cfg) + + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +// TearDownTest cleans up the curret test network after _each_ test. +func (s *IntegrationTestSuite) TearDownTest() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGetCmdQueryParams() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"community_tax":"0.020000000000000000","base_proposer_reward":"0.010000000000000000","bonus_proposer_reward":"0.040000000000000000","withdraw_addr_enabled":true}`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `base_proposer_reward: "0.010000000000000000" +bonus_proposer_reward: "0.040000000000000000" +community_tax: "0.020000000000000000" +withdraw_addr_enabled: true`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorOutstandingRewards() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", + }, + true, + "", + }, + { + "json output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + `{"rewards":[{"denom":"stake","amount":"5.880000000000000000"}]}`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `rewards: +- amount: "5.880000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorOutstandingRewards() + 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().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorCommission() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", + }, + true, + "", + }, + { + "json output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + `{"commission":[{"denom":"stake","amount":"2.940000000000000000"}]}`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), + }, + false, + `commission: +- amount: "2.940000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorCommission() + 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().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorSlashes() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + "foo", "1", "3", + }, + true, + "", + }, + { + "invalid start height", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "-1", "3", + }, + true, + "", + }, + { + "invalid end height", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "-3", + }, + true, + "", + }, + { + "json output", + []string{ + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "3", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + "{\"slashes\":[],\"pagination\":{\"next_key\":null,\"total\":\"0\"}}", + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=3", flags.FlagHeight), + sdk.ValAddress(val.Address).String(), "1", "3", + }, + false, + "pagination:\n next_key: null\n total: \"0\"\nslashes: []", + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorSlashes() + 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().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryDelegatorRewards() { + val := s.network.Validators[0] + addr := val.Address + valAddr := sdk.ValAddress(addr) + + _, err := s.network.WaitForHeightWithTimeout(11, time.Minute) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "invalid delegator address", + []string{ + fmt.Sprintf("--%s=5", flags.FlagHeight), + "foo", valAddr.String(), + }, + true, + "", + }, + { + "invalid validator address", + []string{ + fmt.Sprintf("--%s=5", flags.FlagHeight), + addr.String(), "foo", + }, + true, + "", + }, + { + "json output", + []string{ + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + fmt.Sprintf(`{"rewards":[{"validator_address":"%s","reward":[{"denom":"stake","amount":"9.800000000000000000"}]}],"total":[{"denom":"stake","amount":"9.800000000000000000"}]}`, valAddr.String()), + }, + { + "json output (specific validator)", + []string{ + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), valAddr.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + `{"rewards":[{"denom":"stake","amount":"9.800000000000000000"}]}`, + }, + { + "text output", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), + }, + false, + fmt.Sprintf(`rewards: +- reward: + - amount: "9.800000000000000000" + denom: stake + validator_address: %s +total: +- amount: "9.800000000000000000" + denom: stake`, valAddr.String()), + }, + { + "text output (specific validator)", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=10", flags.FlagHeight), + addr.String(), valAddr.String(), + }, + false, + `rewards: +- amount: "9.800000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegatorRewards() + 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().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryCommunityPool() { + val := s.network.Validators[0] + + _, err := s.network.WaitForHeight(4) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=3", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"pool":[{"denom":"stake","amount":"0.120000000000000000"}]}`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag), fmt.Sprintf("--%s=3", flags.FlagHeight)}, + `pool: +- amount: "0.120000000000000000" + denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryCommunityPool() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestNewWithdrawRewardsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + valAddr fmt.Stringer + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid validator address", + val.Address, + []string{ + 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, nil, 0, + }, + { + "valid transaction", + sdk.ValAddress(val.Address), + []string{ + 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, &sdk.TxResponse{}, 0, + }, + { + "valid transaction (with commission)", + sdk.ValAddress(val.Address), + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=true", cli.FlagCommission), + 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, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + clientCtx := val.ClientCtx + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + bz, err := distrtestutil.MsgWithdrawDelegatorRewardExec(clientCtx, tc.valAddr, tc.args...) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(bz, tc.respType), string(bz)) + + txResp := tc.respType.(*sdk.TxResponse) + s.Require().Equal(tc.expectedCode, txResp.Code) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewWithdrawAllRewardsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "valid transaction (offline)", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagOffline), + 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, nil, 0, + }, + { + "valid transaction", + []string{ + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewWithdrawAllRewardsCmd() + 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) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewSetWithdrawAddrCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid withdraw address", + []string{ + "foo", + 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, nil, 0, + }, + { + "valid transaction", + []string{ + val.Address.String(), + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewSetWithdrawAddrCmd() + 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) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewFundCommunityPoolCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid funding amount", + []string{ + "-43foocoin", + 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, nil, 0, + }, + { + "valid transaction", + []string{ + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431))).String(), + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewFundCommunityPoolCmd() + 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) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdSubmitProposal() { + val := s.network.Validators[0] + invalidProp := `{ + "title": "", + "description": "Pay me some Atoms!", + "recipient": "foo", + "amount": "-343foocoin", + "deposit": -324foocoin +}` + invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) + validProp := fmt.Sprintf(`{ + "title": "Community Pool Spend", + "description": "Pay me some Atoms!", + "recipient": "%s", + "amount": "%s", + "deposit": "%s" +}`, val.Address.String(), sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)), sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431))) + validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid proposal", + []string{ + invalidPropFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction", + []string{ + validPropFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // sync mode as there are no funds yet + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdSubmitProposal() + clientCtx := val.ClientCtx + flags.AddTxFlagsToCmd(cmd) + + 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/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 258f389a6597..3b6032ce7b99 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strconv" "strings" @@ -8,17 +9,14 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { distQueryCmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the distribution module", @@ -27,94 +25,95 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - distQueryCmd.AddCommand(flags.GetCommands( - GetCmdQueryParams(queryRoute, cdc), - GetCmdQueryValidatorOutstandingRewards(queryRoute, cdc), - GetCmdQueryValidatorCommission(queryRoute, cdc), - GetCmdQueryValidatorSlashes(queryRoute, cdc), - GetCmdQueryDelegatorRewards(queryRoute, cdc), - GetCmdQueryCommunityPool(queryRoute, cdc), - )...) + distQueryCmd.AddCommand( + GetCmdQueryParams(), + GetCmdQueryValidatorOutstandingRewards(), + GetCmdQueryValidatorCommission(), + GetCmdQueryValidatorSlashes(), + GetCmdQueryDelegatorRewards(), + GetCmdQueryCommunityPool(), + ) return distQueryCmd } // GetCmdQueryParams implements the query params command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ Use: "params", Args: cobra.NoArgs, Short: "Query distribution params", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParams) - res, _, err := cliCtx.QueryWithData(route, nil) + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - var params types.Params - if err := cdc.UnmarshalJSON(res, ¶ms); err != nil { - return fmt.Errorf("failed to unmarshal params: %w", err) + res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + if err != nil { + return err } - return cliCtx.PrintOutput(params) + return clientCtx.PrintProto(&res.Params) }, } + + flags.AddQueryFlagsToCmd(cmd) + return cmd } -// GetCmdQueryValidatorOutstandingRewards implements the query validator outstanding rewards command. -func GetCmdQueryValidatorOutstandingRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryValidatorOutstandingRewards implements the query validator +// outstanding rewards command. +func GetCmdQueryValidatorOutstandingRewards() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "validator-outstanding-rewards [validator]", Args: cobra.ExactArgs(1), Short: "Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations", Long: strings.TrimSpace( - fmt.Sprintf(`Query distribution outstanding (un-withdrawn) rewards -for a validator and all their delegations. + fmt.Sprintf(`Query distribution outstanding (un-withdrawn) rewards for a validator and all their delegations. Example: -$ %s query distribution validator-outstanding-rewards fetchvaloper1lwjmdnks33xwnmfayc64ycprww49n33mck8cw6 +$ %s query distribution validator-outstanding-rewards %s1lwjmdnks33xwnmfayc64ycprww49n33mtm92ne `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddr, err := sdk.ValAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - params := types.NewQueryValidatorOutstandingRewardsParams(valAddr) - bz, err := cdc.MarshalJSON(params) + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - resp, _, err := cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorOutstandingRewards), - bz, + res, err := queryClient.ValidatorOutstandingRewards( + context.Background(), + &types.QueryValidatorOutstandingRewardsRequest{ValidatorAddress: validatorAddr.String()}, ) if err != nil { return err } - var outstandingRewards types.ValidatorOutstandingRewards - if err := cdc.UnmarshalJSON(resp, &outstandingRewards); err != nil { - return err - } - - return cliCtx.PrintOutput(outstandingRewards) + return clientCtx.PrintProto(&res.Rewards) }, } + + flags.AddQueryFlagsToCmd(cmd) + return cmd } // GetCmdQueryValidatorCommission implements the query validator commission command. -func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidatorCommission() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "commission [validator]", Args: cobra.ExactArgs(1), Short: "Query distribution validator commission", @@ -122,34 +121,44 @@ func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra. fmt.Sprintf(`Query validator commission rewards from delegators to that validator. Example: -$ %s query distribution commission fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query distribution commission %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) validatorAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - res, err := common.QueryValidatorCommission(cliCtx, queryRoute, validatorAddr) + res, err := queryClient.ValidatorCommission( + context.Background(), + &types.QueryValidatorCommissionRequest{ValidatorAddress: validatorAddr.String()}, + ) if err != nil { return err } - var valCom types.ValidatorAccumulatedCommission - cdc.MustUnmarshalJSON(res, &valCom) - return cliCtx.PrintOutput(valCom) + return clientCtx.PrintProto(&res.Commission) }, } + + flags.AddQueryFlagsToCmd(cmd) + return cmd } // GetCmdQueryValidatorSlashes implements the query validator slashes command. -func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidatorSlashes() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "slashes [validator] [start-height] [end-height]", Args: cobra.ExactArgs(3), Short: "Query distribution validator slashes", @@ -157,13 +166,17 @@ func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Com fmt.Sprintf(`Query all slashes of a validator for a given block range. Example: -$ %s query distribution slashes fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 0 100 +$ %s query distribution slashes %svaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 0 100 `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) validatorAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { @@ -180,89 +193,101 @@ $ %s query distribution slashes fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4 return fmt.Errorf("end-height %s not a valid uint, please input a valid end-height", args[2]) } - params := types.NewQueryValidatorSlashesParams(validatorAddr, startHeight, endHeight) - bz, err := cdc.MarshalJSON(params) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_slashes", queryRoute), bz) + res, err := queryClient.ValidatorSlashes( + context.Background(), + &types.QueryValidatorSlashesRequest{ + ValidatorAddress: validatorAddr.String(), + StartingHeight: startHeight, + EndingHeight: endHeight, + Pagination: pageReq, + }, + ) if err != nil { return err } - var slashes types.ValidatorSlashEvents - cdc.MustUnmarshalJSON(res, &slashes) - return cliCtx.PrintOutput(slashes) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "validator slashes") + return cmd } // GetCmdQueryDelegatorRewards implements the query delegator rewards command. -func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "rewards [delegator-addr] []", +func GetCmdQueryDelegatorRewards() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ + Use: "rewards [delegator-addr] [validator-addr]", Args: cobra.RangeArgs(1, 2), Short: "Query all distribution delegator rewards or rewards from a particular validator", Long: strings.TrimSpace( fmt.Sprintf(`Query all rewards earned by a delegator, optionally restrict to rewards from a single validator. Example: -$ %s query distribution rewards fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k -$ %s query distribution rewards fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +$ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, version.ClientName, + version.AppName, bech32PrefixAccAddr, version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } // query for rewards from a particular delegation if len(args) == 2 { - resp, _, err := common.QueryDelegationRewards(cliCtx, queryRoute, args[0], args[1]) + validatorAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { return err } - var result sdk.DecCoins - if err = cdc.UnmarshalJSON(resp, &result); err != nil { - return fmt.Errorf("failed to unmarshal response: %w", err) + res, err := queryClient.DelegationRewards( + context.Background(), + &types.QueryDelegationRewardsRequest{DelegatorAddress: delegatorAddr.String(), ValidatorAddress: validatorAddr.String()}, + ) + if err != nil { + return err } - return cliCtx.PrintOutput(result) + return clientCtx.PrintProto(res) } - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - return err - } - - params := types.NewQueryDelegatorParams(delegatorAddr) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return fmt.Errorf("failed to marshal params: %w", err) - } - - // query for delegator total rewards - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorTotalRewards) - res, _, err := cliCtx.QueryWithData(route, bz) + res, err := queryClient.DelegationTotalRewards( + context.Background(), + &types.QueryDelegationTotalRewardsRequest{DelegatorAddress: delegatorAddr.String()}, + ) if err != nil { return err } - var result types.QueryDelegatorTotalRewardsResponse - if err = cdc.UnmarshalJSON(res, &result); err != nil { - return fmt.Errorf("failed to unmarshal response: %w", err) - } - - return cliCtx.PrintOutput(result) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + return cmd } -// GetCmdQueryCommunityPool returns the command for fetching community pool info -func GetCmdQueryCommunityPool(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryCommunityPool returns the command for fetching community pool info. +func GetCmdQueryCommunityPool() *cobra.Command { + cmd := &cobra.Command{ Use: "community-pool", Args: cobra.NoArgs, Short: "Query the amount of coins in the community pool", @@ -272,20 +297,25 @@ func GetCmdQueryCommunityPool(queryRoute string, cdc *codec.Codec) *cobra.Comman Example: $ %s query distribution community-pool `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", queryRoute), nil) + res, err := queryClient.CommunityPool(context.Background(), &types.QueryCommunityPoolRequest{}) if err != nil { return err } - var result sdk.DecCoins - cdc.MustUnmarshalJSON(res, &result) - return cliCtx.PrintOutput(result) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + return cmd } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 96547a8a6b0e..d62bcd21fa07 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -1,41 +1,34 @@ -// nolint package cli import ( - "bufio" + "context" "fmt" "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" + "github.com/spf13/pflag" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/gov" - - "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) +// Transaction flags for the x/distribution module var ( - flagOnlyFromValidator = "only-from-validator" - flagIsValidator = "is-validator" - flagCommission = "commission" - flagMaxMessagesPerTx = "max-msgs" + FlagCommission = "commission" + FlagMaxMessagesPerTx = "max-msgs" ) const ( MaxMessagesPerTxDefault = 5 ) -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { +// NewTxCmd returns a root CLI command handler for all x/distribution transaction commands. +func NewTxCmd() *cobra.Command { distTxCmd := &cobra.Command{ Use: types.ModuleName, Short: "Distribution transactions subcommands", @@ -44,28 +37,25 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - distTxCmd.AddCommand(flags.PostCommands( - GetCmdWithdrawRewards(cdc), - GetCmdSetWithdrawAddr(cdc), - GetCmdWithdrawAllRewards(cdc, storeKey), - GetCmdFundCommunityPool(cdc), - )...) + distTxCmd.AddCommand( + NewWithdrawRewardsCmd(), + NewWithdrawAllRewardsCmd(), + NewSetWithdrawAddrCmd(), + NewFundCommunityPoolCmd(), + ) return distTxCmd } -type generateOrBroadcastFunc func(context.CLIContext, auth.TxBuilder, []sdk.Msg) error +type newGenerateOrBroadcastFunc func(client.Context, *pflag.FlagSet, ...sdk.Msg) error -func splitAndApply( - generateOrBroadcast generateOrBroadcastFunc, - cliCtx context.CLIContext, - txBldr auth.TxBuilder, - msgs []sdk.Msg, - chunkSize int, +func newSplitAndApply( + genOrBroadcastFn newGenerateOrBroadcastFunc, clientCtx client.Context, + fs *pflag.FlagSet, msgs []sdk.Msg, chunkSize int, ) error { if chunkSize == 0 { - return generateOrBroadcast(cliCtx, txBldr, msgs) + return genOrBroadcastFn(clientCtx, fs, msgs...) } // split messages into slices of length chunkSize @@ -78,7 +68,7 @@ func splitAndApply( } msgChunk := msgs[i:sliceEnd] - if err := generateOrBroadcast(cliCtx, txBldr, msgChunk); err != nil { + if err := genOrBroadcastFn(clientCtx, fs, msgChunk...); err != nil { return err } } @@ -86,8 +76,9 @@ func splitAndApply( return nil } -// command to withdraw rewards -func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { +func NewWithdrawRewardsCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + cmd := &cobra.Command{ Use: "withdraw-rewards [validator-addr]", Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator", @@ -96,38 +87,47 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { and optionally withdraw validator commission if the delegation address given is a validator operator. Example: -$ %s tx distribution withdraw-rewards fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 --from mykey -$ %s tx distribution withdraw-rewards fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 --from mykey --commission +$ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey +$ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission `, - version.ClientName, version.ClientName, + version.AppName, bech32PrefixValAddr, version.AppName, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - delAddr := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)} - if viper.GetBool(flagCommission) { + + if commission, _ := cmd.Flags().GetBool(FlagCommission); commission { msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr)) } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs) + for _, msg := range msgs { + if err := msg.ValidateBasic(); err != nil { + return err + } + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) }, } - cmd.Flags().Bool(flagCommission, false, "also withdraw validator's commission") + + cmd.Flags().Bool(FlagCommission, false, "Withdraw the validator's commission in addition to the rewards") + flags.AddTxFlagsToCmd(cmd) + return cmd } -// command to withdraw all rewards -func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command { +func NewWithdrawAllRewardsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-all-rewards", Short: "withdraw all delegations rewards for a delegator", @@ -137,164 +137,203 @@ func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Comman Example: $ %s tx distribution withdraw-all-rewards --from mykey `, - version.ClientName, + version.AppName, ), ), Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - delAddr := cliCtx.GetFromAddress() + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() // The transaction cannot be generated offline since it requires a query // to get all the validators. - if cliCtx.GenerateOnly { - return fmt.Errorf("command disabled with the provided flag: %s", flags.FlagGenerateOnly) + if clientCtx.Offline { + return fmt.Errorf("cannot generate tx in offline mode") } - msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr) + queryClient := types.NewQueryClient(clientCtx) + delValsRes, err := queryClient.DelegatorValidators(context.Background(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()}) if err != nil { return err } - chunkSize := viper.GetInt(flagMaxMessagesPerTx) - return splitAndApply(utils.GenerateOrBroadcastMsgs, cliCtx, txBldr, msgs, chunkSize) + validators := delValsRes.Validators + // build multi-message transaction + msgs := make([]sdk.Msg, 0, len(validators)) + for _, valAddr := range validators { + val, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return err + } + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, val) + if err := msg.ValidateBasic(); err != nil { + return err + } + msgs = append(msgs, msg) + } + + chunkSize, _ := cmd.Flags().GetInt(FlagMaxMessagesPerTx) + return newSplitAndApply(tx.GenerateOrBroadcastTxCLI, clientCtx, cmd.Flags(), msgs, chunkSize) }, } - cmd.Flags().Int(flagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") + cmd.Flags().Int(FlagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)") + flags.AddTxFlagsToCmd(cmd) + return cmd } -// command to replace a delegator's withdrawal address -func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func NewSetWithdrawAddrCmd() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ Use: "set-withdraw-addr [withdraw-addr]", Short: "change the default withdraw address for rewards associated with an address", Long: strings.TrimSpace( fmt.Sprintf(`Set the withdraw address for rewards associated with a delegator address. Example: -$ %s tx distribution set-withdraw-addr fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k --from mykey +$ %s tx distribution set-withdraw-addr %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - delAddr := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } -// GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal -func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command { +func NewFundCommunityPoolCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "community-pool-spend [proposal-file]", + Use: "fund-community-pool [amount]", Args: cobra.ExactArgs(1), - Short: "Submit a community pool spend proposal", + Short: "Funds the community pool with the specified amount", Long: strings.TrimSpace( - fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit. -The proposal details must be supplied via a JSON file. + fmt.Sprintf(`Funds the community pool with the specified amount Example: -$ %s tx gov submit-proposal community-pool-spend --from= - -Where proposal.json contains: - -{ - "title": "Community Pool Spend", - "description": "Pay me some Atoms!", - "recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", - "amount": [ - { - "denom": "stake", - "amount": "10000" - } - ], - "deposit": [ - { - "denom": "stake", - "amount": "10000" - } - ] -} +$ %s tx distribution fund-community-pool 100uatom --from mykey `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - proposal, err := ParseCommunityPoolSpendProposalJSON(cdc, args[0]) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + depositorAddr := clientCtx.GetFromAddress() + amount, err := sdk.ParseCoinsNormalized(args[0]) if err != nil { return err } - from := cliCtx.GetFromAddress() - content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, proposal.Recipient, proposal.Amount) - - msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from) + msg := types.NewMsgFundCommunityPool(amount, depositorAddr) if err := msg.ValidateBasic(); err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + flags.AddTxFlagsToCmd(cmd) + return cmd } -// GetCmdFundCommunityPool returns a command implementation that supports directly -// funding the community pool. -func GetCmdFundCommunityPool(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "fund-community-pool [amount]", +// GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal +func GetCmdSubmitProposal() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ + Use: "community-pool-spend [proposal-file]", Args: cobra.ExactArgs(1), - Short: "Funds the community pool with the specified amount", + Short: "Submit a community pool spend proposal", Long: strings.TrimSpace( - fmt.Sprintf(`Funds the community pool with the specified amount + fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. Example: -$ %s tx distribution fund-community-pool 100uatom --from mykey +$ %s tx gov submit-proposal community-pool-spend --from= + +Where proposal.json contains: + +{ + "title": "Community Pool Spend", + "description": "Pay me some Atoms!", + "recipient": "%s1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", + "amount": "1000stake", + "deposit": "1000stake" +} `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + proposal, err := ParseCommunityPoolSpendProposalWithDeposit(clientCtx.JSONMarshaler, args[0]) + if err != nil { + return err + } - depositorAddr := cliCtx.GetFromAddress() - amount, err := sdk.ParseCoins(args[0]) + amount, err := sdk.ParseCoinsNormalized(proposal.Amount) + if err != nil { + return err + } + + deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + recpAddr, err := sdk.AccAddressFromBech32(proposal.Recipient) + if err != nil { + return err + } + content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, recpAddr, amount) + + msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from) if err != nil { return err } - msg := types.NewMsgFundCommunityPool(amount, depositorAddr) if err := msg.ValidateBasic(); err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + return cmd } diff --git a/x/distribution/client/cli/tx_test.go b/x/distribution/client/cli/tx_test.go index af09d5445a89..36a946e7cca7 100644 --- a/x/distribution/client/cli/tx_test.go +++ b/x/distribution/client/cli/tx_test.go @@ -3,65 +3,51 @@ package cli import ( "testing" + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" ) -func createFakeTxBuilder() auth.TxBuilder { - cdc := codec.New() - return auth.NewTxBuilder( - utils.GetTxEncoder(cdc), - 123, - 9876, - 0, - 1.2, - false, - "test_chain", - "hello", - sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), - sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, - ) -} - func Test_splitAndCall_NoMessages(t *testing.T) { - ctx := context.CLIContext{} - txBldr := createFakeTxBuilder() + clientCtx := client.Context{} - err := splitAndApply(nil, ctx, txBldr, nil, 10) + err := newSplitAndApply(nil, clientCtx, nil, nil, 10) assert.NoError(t, err, "") } func Test_splitAndCall_Splitting(t *testing.T) { - ctx := context.CLIContext{} - txBldr := createFakeTxBuilder() + clientCtx := client.Context{} addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) // Add five messages msgs := []sdk.Msg{ - sdk.NewTestMsg(addr), - sdk.NewTestMsg(addr), - sdk.NewTestMsg(addr), - sdk.NewTestMsg(addr), - sdk.NewTestMsg(addr), + testdata.NewTestMsg(addr), + testdata.NewTestMsg(addr), + testdata.NewTestMsg(addr), + testdata.NewTestMsg(addr), + testdata.NewTestMsg(addr), } // Keep track of number of calls const chunkSize = 2 callCount := 0 - err := splitAndApply( - func(ctx context.CLIContext, txBldr auth.TxBuilder, msgs []sdk.Msg) error { + err := newSplitAndApply( + func(clientCtx client.Context, fs *pflag.FlagSet, msgs ...sdk.Msg) error { callCount++ - assert.NotNil(t, ctx) - assert.NotNil(t, txBldr) + assert.NotNil(t, clientCtx) assert.NotNil(t, msgs) if callCount < 3 { @@ -72,8 +58,31 @@ func Test_splitAndCall_Splitting(t *testing.T) { return nil }, - ctx, txBldr, msgs, chunkSize) + clientCtx, nil, msgs, chunkSize) assert.NoError(t, err, "") assert.Equal(t, 3, callCount) } + +func TestParseProposal(t *testing.T) { + encodingConfig := params.MakeTestEncodingConfig() + + okJSON := testutil.WriteToNewTempFile(t, ` +{ + "title": "Community Pool Spend", + "description": "Pay me some Atoms!", + "recipient": "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", + "amount": "1000stake", + "deposit": "1000stake" +} +`) + + proposal, err := ParseCommunityPoolSpendProposalWithDeposit(encodingConfig.Marshaler, okJSON.Name()) + require.NoError(t, err) + + require.Equal(t, "Community Pool Spend", proposal.Title) + require.Equal(t, "Pay me some Atoms!", proposal.Description) + require.Equal(t, "cosmos1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq", proposal.Recipient) + require.Equal(t, "1000stake", proposal.Deposit) + require.Equal(t, "1000stake", proposal.Amount) +} diff --git a/x/distribution/client/cli/utils.go b/x/distribution/client/cli/utils.go index 286016250228..fe531e49b9cb 100644 --- a/x/distribution/client/cli/utils.go +++ b/x/distribution/client/cli/utils.go @@ -4,30 +4,19 @@ import ( "io/ioutil" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" ) -type ( - // CommunityPoolSpendProposalJSON defines a CommunityPoolSpendProposal with a deposit - CommunityPoolSpendProposalJSON struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` - Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` - Amount sdk.Coins `json:"amount" yaml:"amount"` - Deposit sdk.Coins `json:"deposit" yaml:"deposit"` - } -) - -// ParseCommunityPoolSpendProposalJSON reads and parses a CommunityPoolSpendProposalJSON from a file. -func ParseCommunityPoolSpendProposalJSON(cdc *codec.Codec, proposalFile string) (CommunityPoolSpendProposalJSON, error) { - proposal := CommunityPoolSpendProposalJSON{} +// ParseCommunityPoolSpendProposalWithDeposit reads and parses a CommunityPoolSpendProposalWithDeposit from a file. +func ParseCommunityPoolSpendProposalWithDeposit(cdc codec.JSONMarshaler, proposalFile string) (types.CommunityPoolSpendProposalWithDeposit, error) { + proposal := types.CommunityPoolSpendProposalWithDeposit{} contents, err := ioutil.ReadFile(proposalFile) if err != nil { return proposal, err } - if err := cdc.UnmarshalJSON(contents, &proposal); err != nil { + if err = cdc.UnmarshalJSON(contents, &proposal); err != nil { return proposal, err } diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go index 06860012c8cf..a2d5fceda0bb 100644 --- a/x/distribution/client/common/common.go +++ b/x/distribution/client/common/common.go @@ -3,14 +3,14 @@ package common import ( "fmt" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) // QueryDelegationRewards queries a delegation rewards between a delegator and a // validator. -func QueryDelegationRewards(cliCtx context.CLIContext, queryRoute, delAddr, valAddr string) ([]byte, int64, error) { +func QueryDelegationRewards(clientCtx client.Context, delAddr, valAddr string) ([]byte, int64, error) { delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) if err != nil { return nil, 0, err @@ -22,46 +22,46 @@ func QueryDelegationRewards(cliCtx context.CLIContext, queryRoute, delAddr, valA } params := types.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) if err != nil { return nil, 0, fmt.Errorf("failed to marshal params: %w", err) } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegationRewards) - return cliCtx.QueryWithData(route, bz) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegationRewards) + return clientCtx.QueryWithData(route, bz) } // QueryDelegatorValidators returns delegator's list of validators // it submitted delegations to. -func QueryDelegatorValidators(cliCtx context.CLIContext, queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { - res, _, err := cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorValidators), - cliCtx.Codec.MustMarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)), +func QueryDelegatorValidators(clientCtx client.Context, delegatorAddr sdk.AccAddress) ([]byte, error) { + res, _, err := clientCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidators), + clientCtx.LegacyAmino.MustMarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)), ) return res, err } // QueryValidatorCommission returns a validator's commission. -func QueryValidatorCommission(cliCtx context.CLIContext, queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { - res, _, err := cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorCommission), - cliCtx.Codec.MustMarshalJSON(types.NewQueryValidatorCommissionParams(validatorAddr)), +func QueryValidatorCommission(clientCtx client.Context, validatorAddr sdk.ValAddress) ([]byte, error) { + res, _, err := clientCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorCommission), + clientCtx.LegacyAmino.MustMarshalJSON(types.NewQueryValidatorCommissionParams(validatorAddr)), ) return res, err } // WithdrawAllDelegatorRewards builds a multi-message slice to be used // to withdraw all delegations rewards for the given delegator. -func WithdrawAllDelegatorRewards(cliCtx context.CLIContext, queryRoute string, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { +func WithdrawAllDelegatorRewards(clientCtx client.Context, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { // retrieve the comprehensive list of all validators which the // delegator had submitted delegations to - bz, err := QueryDelegatorValidators(cliCtx, queryRoute, delegatorAddr) + bz, err := QueryDelegatorValidators(clientCtx, delegatorAddr) if err != nil { return nil, err } var validators []sdk.ValAddress - if err := cliCtx.Codec.UnmarshalJSON(bz, &validators); err != nil { + if err := clientCtx.LegacyAmino.UnmarshalJSON(bz, &validators); err != nil { return nil, err } diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go index ec8c43175ecb..6b3ecdd5cdfb 100644 --- a/x/distribution/client/common/common_test.go +++ b/x/distribution/client/common/common_test.go @@ -5,17 +5,18 @@ import ( "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/distribution/types" ) func TestQueryDelegationRewardsAddrValidation(t *testing.T) { - cdc := codec.New() - ctx := context.NewCLIContext().WithCodec(cdc) + clientCtx := client.Context{}.WithLegacyAmino(types.ModuleCdc.LegacyAmino) + type args struct { delAddr string valAddr string } + tests := []struct { name string args args @@ -24,13 +25,14 @@ func TestQueryDelegationRewardsAddrValidation(t *testing.T) { }{ {"invalid delegator address", args{"invalid", ""}, nil, true}, {"empty delegator address", args{"", ""}, nil, true}, - {"invalid validator address", args{"fetch1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqr9h3fr2", "invalid"}, nil, true}, - {"empty validator address", args{"fetch1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqr9h3fr2", ""}, nil, true}, + {"invalid validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", "invalid"}, nil, true}, + {"empty validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", ""}, nil, true}, } + for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - _, _, err := QueryDelegationRewards(ctx, "", tt.args.delAddr, tt.args.valAddr) + _, _, err := QueryDelegationRewards(clientCtx, tt.args.delAddr, tt.args.valAddr) require.True(t, err != nil, tt.wantErr) }) } diff --git a/x/distribution/client/proposal_handler.go b/x/distribution/client/proposal_handler.go index c7fcc0bdce91..517f508bff68 100644 --- a/x/distribution/client/proposal_handler.go +++ b/x/distribution/client/proposal_handler.go @@ -6,7 +6,7 @@ import ( govclient "github.com/cosmos/cosmos-sdk/x/gov/client" ) -// param change proposal handler +// ProposalHandler is the community spend proposal handler. var ( ProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitProposal, rest.ProposalRESTHandler) ) diff --git a/x/distribution/client/rest/grpc_query_test.go b/x/distribution/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..62539ce55152 --- /dev/null +++ b/x/distribution/client/rest/grpc_query_test.go @@ -0,0 +1,464 @@ +package rest_test + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TestQueryParamsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/params", baseURL), + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + Params: types.DefaultParams(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + s.Run(tc.name, func() { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected, tc.respType) + }) + } +} + +func (s *IntegrationTestSuite) TestQueryOutstandingRewardsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + rewards, err := sdk.ParseDecCoins("3.92stake") + s.Require().NoError(err) + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params with wrong validator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards", baseURL, "wrongAddress"), + map[string]string{}, + true, + &types.QueryValidatorOutstandingRewardsResponse{}, + &types.QueryValidatorOutstandingRewardsResponse{}, + }, + { + "gRPC request params valid address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/outstanding_rewards", baseURL, val.ValAddress.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + false, + &types.QueryValidatorOutstandingRewardsResponse{}, + &types.QueryValidatorOutstandingRewardsResponse{ + Rewards: types.ValidatorOutstandingRewards{ + Rewards: rewards, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryValidatorCommissionGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + commission, err := sdk.ParseDecCoins("1.96stake") + s.Require().NoError(err) + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params with wrong validator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/commission", baseURL, "wrongAddress"), + map[string]string{}, + true, + &types.QueryValidatorCommissionResponse{}, + &types.QueryValidatorCommissionResponse{}, + }, + { + "gRPC request params valid address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/commission", baseURL, val.ValAddress.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + false, + &types.QueryValidatorCommissionResponse{}, + &types.QueryValidatorCommissionResponse{ + Commission: types.ValidatorAccumulatedCommission{ + Commission: commission, + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQuerySlashesGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "invalid validator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes", baseURL, ""), + true, + &types.QueryValidatorSlashesResponse{}, + nil, + }, + { + "invalid start height", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.ValAddress.String(), "-1", "3"), + true, + &types.QueryValidatorSlashesResponse{}, + nil, + }, + { + "invalid start height", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.ValAddress.String(), "1", "-3"), + true, + &types.QueryValidatorSlashesResponse{}, + nil, + }, + { + "valid request get slashes", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/validators/%s/slashes?starting_height=%s&ending_height=%s", baseURL, val.ValAddress.String(), "1", "3"), + false, + &types.QueryValidatorSlashesResponse{}, + &types.QueryValidatorSlashesResponse{ + Pagination: &query.PageResponse{}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegatorRewardsGRPC() { + val := s.network.Validators[0] + baseUrl := val.APIAddress + + rewards, err := sdk.ParseDecCoins("1.96stake") + s.Require().NoError(err) + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "wrong delegator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards", baseUrl, "wrongDelegatorAddress"), + map[string]string{}, + true, + &types.QueryDelegationTotalRewardsResponse{}, + nil, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards", baseUrl, val.Address.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + false, + &types.QueryDelegationTotalRewardsResponse{}, + &types.QueryDelegationTotalRewardsResponse{ + Rewards: []types.DelegationDelegatorReward{ + types.NewDelegationDelegatorReward(val.ValAddress, rewards), + }, + Total: rewards, + }, + }, + { + "wrong validator address(specific validator rewards)", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards/%s", baseUrl, val.Address.String(), "wrongValAddress"), + map[string]string{}, + true, + &types.QueryDelegationTotalRewardsResponse{}, + nil, + }, + { + "valid request(specific validator rewards)", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/rewards/%s", baseUrl, val.Address.String(), val.ValAddress.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + false, + &types.QueryDelegationRewardsResponse{}, + &types.QueryDelegationRewardsResponse{ + Rewards: rewards, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegatorValidatorsGRPC() { + val := s.network.Validators[0] + baseUrl := val.APIAddress + + testCases := []struct { + name string + url string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "empty delegator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseUrl, ""), + true, + &types.QueryDelegatorValidatorsResponse{}, + nil, + }, + { + "wrong delegator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseUrl, "wrongDelegatorAddress"), + true, + &types.QueryDelegatorValidatorsResponse{}, + nil, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/validators", baseUrl, val.Address.String()), + false, + &types.QueryDelegatorValidatorsResponse{}, + &types.QueryDelegatorValidatorsResponse{ + Validators: []string{val.ValAddress.String()}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryWithdrawAddressGRPC() { + val := s.network.Validators[0] + baseUrl := val.APIAddress + + testCases := []struct { + name string + url string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "empty delegator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseUrl, ""), + true, + &types.QueryDelegatorWithdrawAddressResponse{}, + nil, + }, + { + "wrong delegator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseUrl, "wrongDelegatorAddress"), + true, + &types.QueryDelegatorWithdrawAddressResponse{}, + nil, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", baseUrl, val.Address.String()), + false, + &types.QueryDelegatorWithdrawAddressResponse{}, + &types.QueryDelegatorWithdrawAddressResponse{ + WithdrawAddress: val.Address.String(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryValidatorCommunityPoolGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + communityPool, err := sdk.ParseDecCoins("0.08stake") + s.Require().NoError(err) + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params with wrong validator address", + fmt.Sprintf("%s/cosmos/distribution/v1beta1/community_pool", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "2", + }, + false, + &types.QueryCommunityPoolResponse{}, + &types.QueryCommunityPoolResponse{ + Pool: communityPool, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + + s.Run(tc.name, func() { + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go index 4c362409dd13..0fb923afc94d 100644 --- a/x/distribution/client/rest/query.go +++ b/x/distribution/client/rest/query.go @@ -9,66 +9,66 @@ import ( "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { // Get the total rewards balance from all delegations r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - delegatorRewardsHandlerFn(cliCtx, queryRoute), + delegatorRewardsHandlerFn(clientCtx), ).Methods("GET") // Query a delegation reward r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - delegationRewardsHandlerFn(cliCtx, queryRoute), + delegationRewardsHandlerFn(clientCtx), ).Methods("GET") // Get the rewards withdrawal address r.HandleFunc( "/distribution/delegators/{delegatorAddr}/withdraw_address", - delegatorWithdrawalAddrHandlerFn(cliCtx, queryRoute), + delegatorWithdrawalAddrHandlerFn(clientCtx), ).Methods("GET") // Validator distribution information r.HandleFunc( "/distribution/validators/{validatorAddr}", - validatorInfoHandlerFn(cliCtx, queryRoute), + validatorInfoHandlerFn(clientCtx), ).Methods("GET") // Commission and self-delegation rewards of a single a validator r.HandleFunc( "/distribution/validators/{validatorAddr}/rewards", - validatorRewardsHandlerFn(cliCtx, queryRoute), + validatorRewardsHandlerFn(clientCtx), ).Methods("GET") // Outstanding rewards of a single validator r.HandleFunc( "/distribution/validators/{validatorAddr}/outstanding_rewards", - outstandingRewardsHandlerFn(cliCtx, queryRoute), + outstandingRewardsHandlerFn(clientCtx), ).Methods("GET") // Get the current distribution parameter values r.HandleFunc( "/distribution/parameters", - paramsHandlerFn(cliCtx, queryRoute), + paramsHandlerFn(clientCtx), ).Methods("GET") // Get the amount held in the community pool r.HandleFunc( "/distribution/community_pool", - communityPoolHandler(cliCtx, queryRoute), + communityPoolHandler(clientCtx), ).Methods("GET") } // HTTP request handler to query the total rewards balance from all delegations -func delegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } @@ -79,28 +79,27 @@ func delegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt } params := types.NewQueryDelegatorParams(delegatorAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal params: %s", err)) return } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorTotalRewards) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorTotalRewards) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query a delegation rewards -func delegationRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegationRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } @@ -109,38 +108,37 @@ func delegationRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) ht valAddr := mux.Vars(r)["validatorAddr"] // query for rewards from a particular delegation - res, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr, valAddr) + res, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr, valAddr) if !ok { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query a delegation rewards -func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func delegatorWithdrawalAddrHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { delegatorAddr, ok := checkDelegatorAddressVar(w, r) if !ok { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - bz := cliCtx.Codec.MustMarshalJSON(types.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + bz := clientCtx.LegacyAmino.MustMarshalJSON(types.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", types.QuerierRoute), bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } @@ -163,57 +161,53 @@ func NewValidatorDistInfo(operatorAddr sdk.AccAddress, rewards sdk.DecCoins, } // HTTP request handler to query validator's distribution information -func validatorInfoHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func validatorInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { valAddr, ok := checkValidatorAddressVar(w, r) if !ok { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } // query commission - bz, err := common.QueryValidatorCommission(cliCtx, queryRoute, valAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + bz, err := common.QueryValidatorCommission(clientCtx, valAddr) + if rest.CheckInternalServerError(w, err) { return } var commission types.ValidatorAccumulatedCommission - if err := cliCtx.Codec.UnmarshalJSON(bz, &commission); err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, clientCtx.LegacyAmino.UnmarshalJSON(bz, &commission)) { return } // self bond rewards delAddr := sdk.AccAddress(valAddr) - bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr.String(), valAddr.String()) + bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr.String(), valAddr.String()) if !ok { return } var rewards sdk.DecCoins - if err := cliCtx.Codec.UnmarshalJSON(bz, &rewards); err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, clientCtx.LegacyAmino.UnmarshalJSON(bz, &rewards)) { return } - bz, err = cliCtx.Codec.MarshalJSON(NewValidatorDistInfo(delAddr, rewards, commission)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + bz, err = clientCtx.LegacyAmino.MarshalJSON(NewValidatorDistInfo(delAddr, rewards, commission)) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, bz) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, bz) } } // HTTP request handler to query validator's commission and self-delegation rewards -func validatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func validatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { valAddr := mux.Vars(r)["validatorAddr"] validatorAddr, ok := checkValidatorAddressVar(w, r) @@ -221,98 +215,93 @@ func validatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) htt return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } delAddr := sdk.AccAddress(validatorAddr).String() - bz, height, ok := checkResponseQueryDelegationRewards(w, cliCtx, queryRoute, delAddr, valAddr) + bz, height, ok := checkResponseQueryDelegationRewards(w, clientCtx, delAddr, valAddr) if !ok { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, bz) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, bz) } } // HTTP request handler to query the distribution params values -func paramsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func paramsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParams) - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func communityPoolHandler(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func communityPoolHandler(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", queryRoute), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/community_pool", types.QuerierRoute), nil) + if rest.CheckInternalServerError(w, err) { return } var result sdk.DecCoins - if err := cliCtx.Codec.UnmarshalJSON(res, &result); err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, clientCtx.LegacyAmino.UnmarshalJSON(res, &result)) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, result) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, result) } } // HTTP request handler to query the outstanding rewards -func outstandingRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func outstandingRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { validatorAddr, ok := checkValidatorAddressVar(w, r) if !ok { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - bin := cliCtx.Codec.MustMarshalJSON(types.NewQueryValidatorOutstandingRewardsParams(validatorAddr)) - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_outstanding_rewards", queryRoute), bin) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + bin := clientCtx.LegacyAmino.MustMarshalJSON(types.NewQueryValidatorOutstandingRewardsParams(validatorAddr)) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/validator_outstanding_rewards", types.QuerierRoute), bin) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } func checkResponseQueryDelegationRewards( - w http.ResponseWriter, cliCtx context.CLIContext, queryRoute, delAddr, valAddr string, + w http.ResponseWriter, clientCtx client.Context, delAddr, valAddr string, ) (res []byte, height int64, ok bool) { - res, height, err := common.QueryDelegationRewards(cliCtx, queryRoute, delAddr, valAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := common.QueryDelegationRewards(clientCtx, delAddr, valAddr) + if rest.CheckInternalServerError(w, err) { return nil, 0, false } diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go index 3b09478ece66..3fdcef89e1cc 100644 --- a/x/distribution/client/rest/rest.go +++ b/x/distribution/client/rest/rest.go @@ -5,33 +5,35 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/gov" govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) -// RegisterRoutes register distribution REST routes. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { - registerQueryRoutes(cliCtx, r, queryRoute) - registerTxRoutes(cliCtx, r, queryRoute) +func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { + r := clientrest.WithHTTPDeprecationHeaders(rtr) + + registerQueryRoutes(clientCtx, r) + registerTxHandlers(clientCtx, r) } +// TODO add proto compatible Handler after x/gov migration // ProposalRESTHandler returns a ProposalRESTHandler that exposes the community pool spend REST handler with a given sub-route. -func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler { +func ProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler { return govrest.ProposalRESTHandler{ SubRoute: "community_pool_spend", - Handler: postProposalHandlerFn(cliCtx), + Handler: postProposalHandlerFn(clientCtx), } } -func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func postProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req CommunityPoolSpendProposalReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -42,12 +44,14 @@ func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { content := types.NewCommunityPoolSpendProposal(req.Title, req.Description, req.Recipient, req.Amount) - msg := gov.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go index ace2c11a7aba..675be4af1abc 100644 --- a/x/distribution/client/rest/tx.go +++ b/x/distribution/client/rest/tx.go @@ -5,69 +5,66 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "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/rest" "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" +) - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" +type ( + withdrawRewardsReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + } + + setWithdrawalAddrReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` + } + + fundCommunityPoolReq struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + } ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) { +func registerTxHandlers(clientCtx client.Context, r *mux.Router) { // Withdraw all delegator rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards", - withdrawDelegatorRewardsHandlerFn(cliCtx, queryRoute), + newWithdrawDelegatorRewardsHandlerFn(clientCtx), ).Methods("POST") // Withdraw delegation rewards r.HandleFunc( "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", - withdrawDelegationRewardsHandlerFn(cliCtx), + newWithdrawDelegationRewardsHandlerFn(clientCtx), ).Methods("POST") // Replace the rewards withdrawal address r.HandleFunc( "/distribution/delegators/{delegatorAddr}/withdraw_address", - setDelegatorWithdrawalAddrHandlerFn(cliCtx), + newSetDelegatorWithdrawalAddrHandlerFn(clientCtx), ).Methods("POST") // Withdraw validator rewards and commission r.HandleFunc( "/distribution/validators/{validatorAddr}/rewards", - withdrawValidatorRewardsHandlerFn(cliCtx), + newWithdrawValidatorRewardsHandlerFn(clientCtx), ).Methods("POST") // Fund the community pool r.HandleFunc( "/distribution/community_pool", - fundCommunityPoolHandlerFn(cliCtx), + newFundCommunityPoolHandlerFn(clientCtx), ).Methods("POST") - } -type ( - withdrawRewardsReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - } - - setWithdrawalAddrReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` - } - - fundCommunityPoolReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - Amount sdk.Coins `json:"amount" yaml:"amount"` - } -) - -// Withdraw delegator rewards -func withdrawDelegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc { +func newWithdrawDelegatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -82,22 +79,19 @@ func withdrawDelegatorRewardsHandlerFn(cliCtx context.CLIContext, queryRoute str return } - msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, queryRoute, delAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + msgs, err := common.WithdrawAllDelegatorRewards(clientCtx, delAddr) + if rest.CheckInternalServerError(w, err) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, msgs) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msgs...) } } -// Withdraw delegation rewards -func withdrawDelegationRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newWithdrawDelegationRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -118,21 +112,18 @@ func withdrawDelegationRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerF } msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -// Replace the rewards withdrawal address -func setDelegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newSetDelegatorWithdrawalAddrHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req setWithdrawalAddrReq - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -148,21 +139,18 @@ func setDelegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext) http.Handler } msg := types.NewMsgSetWithdrawAddress(delAddr, req.WithdrawAddress) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -// Withdraw validator rewards and commission -func withdrawValidatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newWithdrawValidatorRewardsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req withdrawRewardsReq - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -179,19 +167,18 @@ func withdrawValidatorRewardsHandlerFn(cliCtx context.CLIContext) http.HandlerFu // prepare multi-message transaction msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, msgs) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msgs...) } } -func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newFundCommunityPoolHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req fundCommunityPoolReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -201,18 +188,16 @@ func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } msg := types.NewMsgFundCommunityPool(req.Amount, fromAddr) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } @@ -220,8 +205,7 @@ func fundCommunityPoolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return nil, false } @@ -230,8 +214,7 @@ func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAd func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return nil, false } diff --git a/x/distribution/client/testutil/helpers.go b/x/distribution/client/testutil/helpers.go new file mode 100644 index 000000000000..19612c23fec0 --- /dev/null +++ b/x/distribution/client/testutil/helpers.go @@ -0,0 +1,32 @@ +package testutil + +import ( + "bytes" + "context" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + distrcli "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" +) + +func MsgWithdrawDelegatorRewardExec(clientCtx client.Context, valAddr fmt.Stringer, extraArgs ...string) ([]byte, error) { + buf := new(bytes.Buffer) + clientCtx = clientCtx.WithOutput(buf) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + args := []string{valAddr.String()} + args = append(args, extraArgs...) + + cmd := distrcli.NewWithdrawRewardsCmd() + cmd.SetErr(buf) + cmd.SetOut(buf) + cmd.SetArgs(args) + + if err := cmd.ExecuteContext(ctx); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/x/distribution/genesis.go b/x/distribution/genesis.go deleted file mode 100644 index 6a7f9c1fdbaf..000000000000 --- a/x/distribution/genesis.go +++ /dev/null @@ -1,143 +0,0 @@ -package distribution - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/distribution/types" -) - -// InitGenesis sets distribution information for genesis -func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper, data types.GenesisState) { - var moduleHoldings sdk.DecCoins - - keeper.SetFeePool(ctx, data.FeePool) - keeper.SetParams(ctx, data.Params) - - for _, dwi := range data.DelegatorWithdrawInfos { - keeper.SetDelegatorWithdrawAddr(ctx, dwi.DelegatorAddress, dwi.WithdrawAddress) - } - keeper.SetPreviousProposerConsAddr(ctx, data.PreviousProposer) - for _, rew := range data.OutstandingRewards { - keeper.SetValidatorOutstandingRewards(ctx, rew.ValidatorAddress, rew.OutstandingRewards) - moduleHoldings = moduleHoldings.Add(rew.OutstandingRewards...) - } - for _, acc := range data.ValidatorAccumulatedCommissions { - keeper.SetValidatorAccumulatedCommission(ctx, acc.ValidatorAddress, acc.Accumulated) - } - for _, his := range data.ValidatorHistoricalRewards { - keeper.SetValidatorHistoricalRewards(ctx, his.ValidatorAddress, his.Period, his.Rewards) - } - for _, cur := range data.ValidatorCurrentRewards { - keeper.SetValidatorCurrentRewards(ctx, cur.ValidatorAddress, cur.Rewards) - } - for _, del := range data.DelegatorStartingInfos { - keeper.SetDelegatorStartingInfo(ctx, del.ValidatorAddress, del.DelegatorAddress, del.StartingInfo) - } - for _, evt := range data.ValidatorSlashEvents { - keeper.SetValidatorSlashEvent(ctx, evt.ValidatorAddress, evt.Height, evt.Period, evt.Event) - } - - moduleHoldings = moduleHoldings.Add(data.FeePool.CommunityPool...) - moduleHoldingsInt, _ := moduleHoldings.TruncateDecimal() - - // check if the module account exists - moduleAcc := keeper.GetDistributionAccount(ctx) - if moduleAcc == nil { - panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) - } - - if moduleAcc.GetCoins().IsZero() { - if err := moduleAcc.SetCoins(moduleHoldingsInt); err != nil { - panic(err) - } - supplyKeeper.SetModuleAccount(ctx, moduleAcc) - } -} - -// ExportGenesis returns a GenesisState for a given context and keeper. -func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { - feePool := keeper.GetFeePool(ctx) - params := keeper.GetParams(ctx) - - dwi := make([]types.DelegatorWithdrawInfo, 0) - keeper.IterateDelegatorWithdrawAddrs(ctx, func(del sdk.AccAddress, addr sdk.AccAddress) (stop bool) { - dwi = append(dwi, types.DelegatorWithdrawInfo{ - DelegatorAddress: del, - WithdrawAddress: addr, - }) - return false - }) - - pp := keeper.GetPreviousProposerConsAddr(ctx) - outstanding := make([]types.ValidatorOutstandingRewardsRecord, 0) - keeper.IterateValidatorOutstandingRewards(ctx, - func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - outstanding = append(outstanding, types.ValidatorOutstandingRewardsRecord{ - ValidatorAddress: addr, - OutstandingRewards: rewards, - }) - return false - }, - ) - - acc := make([]types.ValidatorAccumulatedCommissionRecord, 0) - keeper.IterateValidatorAccumulatedCommissions(ctx, - func(addr sdk.ValAddress, commission types.ValidatorAccumulatedCommission) (stop bool) { - acc = append(acc, types.ValidatorAccumulatedCommissionRecord{ - ValidatorAddress: addr, - Accumulated: commission, - }) - return false - }, - ) - - his := make([]types.ValidatorHistoricalRewardsRecord, 0) - keeper.IterateValidatorHistoricalRewards(ctx, - func(val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) (stop bool) { - his = append(his, types.ValidatorHistoricalRewardsRecord{ - ValidatorAddress: val, - Period: period, - Rewards: rewards, - }) - return false - }, - ) - - cur := make([]types.ValidatorCurrentRewardsRecord, 0) - keeper.IterateValidatorCurrentRewards(ctx, - func(val sdk.ValAddress, rewards types.ValidatorCurrentRewards) (stop bool) { - cur = append(cur, types.ValidatorCurrentRewardsRecord{ - ValidatorAddress: val, - Rewards: rewards, - }) - return false - }, - ) - dels := make([]types.DelegatorStartingInfoRecord, 0) - keeper.IterateDelegatorStartingInfos(ctx, - func(val sdk.ValAddress, del sdk.AccAddress, info types.DelegatorStartingInfo) (stop bool) { - dels = append(dels, types.DelegatorStartingInfoRecord{ - ValidatorAddress: val, - DelegatorAddress: del, - StartingInfo: info, - }) - return false - }, - ) - - slashes := make([]types.ValidatorSlashEventRecord, 0) - keeper.IterateValidatorSlashEvents(ctx, - func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool) { - slashes = append(slashes, types.ValidatorSlashEventRecord{ - ValidatorAddress: val, - Height: height, - Period: event.ValidatorPeriod, - Event: event, - }) - return false - }, - ) - - return types.NewGenesisState(params, feePool, dwi, pp, outstanding, acc, his, cur, dels, slashes) -} diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 642b9e0d89a1..279a6bf726de 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -9,21 +9,27 @@ import ( ) func NewHandler(k keeper.Keeper) sdk.Handler { + msgServer := keeper.NewMsgServerImpl(k) + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case types.MsgSetWithdrawAddress: - return handleMsgModifyWithdrawAddress(ctx, msg, k) + case *types.MsgSetWithdrawAddress: + res, err := msgServer.SetWithdrawAddress(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgWithdrawDelegatorReward: - return handleMsgWithdrawDelegatorReward(ctx, msg, k) + case *types.MsgWithdrawDelegatorReward: + res, err := msgServer.WithdrawDelegatorReward(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgWithdrawValidatorCommission: - return handleMsgWithdrawValidatorCommission(ctx, msg, k) + case *types.MsgWithdrawValidatorCommission: + res, err := msgServer.WithdrawValidatorCommission(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgFundCommunityPool: - return handleMsgFundCommunityPool(ctx, msg, k) + case *types.MsgFundCommunityPool: + res, err := msgServer.FundCommunityPool(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized distribution message type: %T", msg) @@ -31,79 +37,10 @@ func NewHandler(k keeper.Keeper) sdk.Handler { } } -// These functions assume everything has been authenticated (ValidateBasic passed, and signatures checked) - -func handleMsgModifyWithdrawAddress(ctx sdk.Context, msg types.MsgSetWithdrawAddress, k keeper.Keeper) (*sdk.Result, error) { - err := k.SetWithdrawAddr(ctx, msg.DelegatorAddress, msg.WithdrawAddress) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgWithdrawDelegatorReward(ctx sdk.Context, msg types.MsgWithdrawDelegatorReward, k keeper.Keeper) (*sdk.Result, error) { - _, err := k.WithdrawDelegationRewards(ctx, msg.DelegatorAddress, msg.ValidatorAddress) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgWithdrawValidatorCommission(ctx sdk.Context, msg types.MsgWithdrawValidatorCommission, k keeper.Keeper) (*sdk.Result, error) { - _, err := k.WithdrawValidatorCommission(ctx, msg.ValidatorAddress) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgFundCommunityPool(ctx sdk.Context, msg types.MsgFundCommunityPool, k keeper.Keeper) (*sdk.Result, error) { - if err := k.FundCommunityPool(ctx, msg.Amount, msg.Depositor); err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func NewCommunityPoolSpendProposalHandler(k Keeper) govtypes.Handler { +func NewCommunityPoolSpendProposalHandler(k keeper.Keeper) govtypes.Handler { return func(ctx sdk.Context, content govtypes.Content) error { switch c := content.(type) { - case types.CommunityPoolSpendProposal: + case *types.CommunityPoolSpendProposal: return keeper.HandleCommunityPoolSpendProposal(ctx, k, c) default: diff --git a/x/distribution/keeper/alias_functions.go b/x/distribution/keeper/alias_functions.go index a02d2f2a0b4d..5d7f1b97f23c 100644 --- a/x/distribution/keeper/alias_functions.go +++ b/x/distribution/keeper/alias_functions.go @@ -2,13 +2,13 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" ) // get outstanding rewards func (k Keeper) GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.ValAddress) sdk.DecCoins { - return k.GetValidatorOutstandingRewards(ctx, val) + return k.GetValidatorOutstandingRewards(ctx, val).Rewards } // get the community coins @@ -17,6 +17,6 @@ func (k Keeper) GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins { } // GetDistributionAccount returns the distribution ModuleAccount -func (k Keeper) GetDistributionAccount(ctx sdk.Context) exported.ModuleAccountI { - return k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName) +func (k Keeper) GetDistributionAccount(ctx sdk.Context) authtypes.ModuleAccountI { + return k.authKeeper.GetModuleAccount(ctx, types.ModuleName) } diff --git a/x/distribution/keeper/allocation.go b/x/distribution/keeper/allocation.go index b6a34a3d643b..9436bada9d69 100644 --- a/x/distribution/keeper/allocation.go +++ b/x/distribution/keeper/allocation.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AllocateTokens handles distribution of the collected fees @@ -21,12 +21,12 @@ func (k Keeper) AllocateTokens( // fetch and clear the collected fees for distribution, since this is // called in BeginBlock, collected fees will be from the previous block // (and distributed to the previous proposer) - feeCollector := k.supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName) - feesCollectedInt := feeCollector.GetCoins() + feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName) + feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) // transfer collected fees to the distribution module account - err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt) + err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt) if err != nil { panic(err) } @@ -100,7 +100,7 @@ func (k Keeper) AllocateTokens( } // AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commission -func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.ValidatorI, tokens sdk.DecCoins) { +func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) { // split tokens between validator and delegators according to commission commission := tokens.MulDec(val.GetCommission()) shared := tokens.Sub(commission) @@ -114,7 +114,7 @@ func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.Validato ), ) currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) - currentCommission = currentCommission.Add(commission...) + currentCommission.Commission = currentCommission.Commission.Add(commission...) k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission) // update current rewards @@ -131,6 +131,6 @@ func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.Validato ), ) outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) - outstanding = outstanding.Add(tokens...) + outstanding.Rewards = outstanding.Rewards.Add(tokens...) k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding) } diff --git a/x/distribution/keeper/allocation_test.go b/x/distribution/keeper/allocation_test.go index 83821ea3820d..9d8b0012a51c 100644 --- a/x/distribution/keeper/allocation_test.go +++ b/x/distribution/keeper/allocation_test.go @@ -1,69 +1,63 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + 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/staking" + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestAllocateTokensToValidatorWithCommission(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator( - valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt(), - ) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + addrs := simapp.AddTestAddrs(app, ctx, 3, sdk.NewInt(1234)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - val := sk.Validator(ctx, valOpAddr1) + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(sdk.ValAddress(addrs[0]), valConsPk1, sdk.NewInt(100), true) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) // allocate tokens tokens := sdk.DecCoins{ {Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(10)}, } - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // check commission expected := sdk.DecCoins{ {Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(5)}, } - require.Equal(t, expected, k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())) + require.Equal(t, expected, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()).Commission) // check current rewards - require.Equal(t, expected, k.GetValidatorCurrentRewards(ctx, val.GetOperator()).Rewards) + require.Equal(t, expected, app.DistrKeeper.GetValidatorCurrentRewards(ctx, val.GetOperator()).Rewards) } func TestAllocateTokensToManyValidators(t *testing.T) { - ctx, ak, k, sk, supplyKeeper := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) + addrs := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1234)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) // create second validator with 0% commission - commission = staking.NewCommissionRates(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)) - msg = staking.NewMsgCreateValidator(valOpAddr2, valConsPk2, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) - - res, err = sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[1], valConsPk2, sdk.NewInt(100), true) abciValA := abci.Validator{ Address: valConsPk1.Address(), @@ -75,22 +69,22 @@ func TestAllocateTokensToManyValidators(t *testing.T) { } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr1).IsZero()) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr2).IsZero()) - require.True(t, k.GetFeePool(ctx).CommunityPool.IsZero()) - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1).IsZero()) - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr2).IsZero()) - require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr1).Rewards.IsZero()) - require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr2).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) - feeCollector := supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName) + feeCollector := app.AccountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) - err = feeCollector.SetCoins(fees) + err := app.BankKeeper.SetBalances(ctx, feeCollector.GetAddress(), fees) require.NoError(t, err) - ak.SetAccount(ctx, feeCollector) + app.AccountKeeper.SetAccount(ctx, feeCollector) votes := []abci.VoteInfo{ { @@ -102,51 +96,42 @@ func TestAllocateTokensToManyValidators(t *testing.T) { SignedLastBlock: true, }, } - k.AllocateTokens(ctx, 200, 200, valConsAddr2, votes) + app.DistrKeeper.AllocateTokens(ctx, 200, 200, valConsAddr2, votes) // 98 outstanding rewards (100 less 2 to community pool) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(465, 1)}}, k.GetValidatorOutstandingRewards(ctx, valOpAddr1)) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(515, 1)}}, k.GetValidatorOutstandingRewards(ctx, valOpAddr2)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(465, 1)}}, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(515, 1)}}, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards) // 2 community pool coins - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(2)}}, k.GetFeePool(ctx).CommunityPool) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(2)}}, app.DistrKeeper.GetFeePool(ctx).CommunityPool) // 50% commission for first proposer, (0.5 * 93%) * 100 / 2 = 23.25 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2325, 2)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2325, 2)}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) // zero commission for second proposer - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr2).IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) // just staking.proportional for first proposer less commission = (0.5 * 93%) * 100 / 2 = 23.25 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2325, 2)}}, k.GetValidatorCurrentRewards(ctx, valOpAddr1).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2325, 2)}}, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards) // proposer reward + staking.proportional for second proposer = (5 % + 0.5 * (93%)) * 100 = 51.5 - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(515, 1)}}, k.GetValidatorCurrentRewards(ctx, valOpAddr2).Rewards) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(515, 1)}}, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards) } func TestAllocateTokensTruncation(t *testing.T) { - communityTax := sdk.NewDec(0) - ctx, ak, _, k, sk, _, supplyKeeper := CreateTestInputAdvanced(t, false, 1000000, communityTax) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs := simapp.AddTestAddrs(app, ctx, 3, sdk.NewInt(1234)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator with 10% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(110)), staking.Description{}, commission, sdk.OneInt()) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(110), true) // create second validator with 10% commission - commission = staking.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) - msg = staking.NewMsgCreateValidator(valOpAddr2, valConsPk2, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) - res, err = sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[1], valConsPk2, sdk.NewInt(100), true) // create third validator with 10% commission - commission = staking.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) - msg = staking.NewMsgCreateValidator(valOpAddr3, valConsPk3, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) - res, err = sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[2], valConsPk3, sdk.NewInt(100), true) abciValA := abci.Validator{ Address: valConsPk1.Address(), @@ -162,25 +147,25 @@ func TestAllocateTokensTruncation(t *testing.T) { } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr1).IsZero()) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr2).IsZero()) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr3).IsZero()) - require.True(t, k.GetFeePool(ctx).CommunityPool.IsZero()) - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1).IsZero()) - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr2).IsZero()) - require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr1).Rewards.IsZero()) - require.True(t, k.GetValidatorCurrentRewards(ctx, valOpAddr2).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) + require.True(t, app.DistrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840))) - feeCollector := supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName) + feeCollector := app.AccountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) - err = feeCollector.SetCoins(fees) + err := app.BankKeeper.SetBalances(ctx, feeCollector.GetAddress(), fees) require.NoError(t, err) - ak.SetAccount(ctx, feeCollector) + app.AccountKeeper.SetAccount(ctx, feeCollector) votes := []abci.VoteInfo{ { @@ -196,9 +181,9 @@ func TestAllocateTokensTruncation(t *testing.T) { SignedLastBlock: true, }, } - k.AllocateTokens(ctx, 31, 31, valConsAddr2, votes) + app.DistrKeeper.AllocateTokens(ctx, 31, 31, sdk.ConsAddress(valConsPk2.Address()), votes) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr1).IsValid()) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr2).IsValid()) - require.True(t, k.GetValidatorOutstandingRewards(ctx, valOpAddr3).IsValid()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsValid()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsValid()) + require.True(t, app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[2]).Rewards.IsValid()) } diff --git a/x/distribution/keeper/common_test.go b/x/distribution/keeper/common_test.go new file mode 100644 index 000000000000..807fd45c2520 --- /dev/null +++ b/x/distribution/keeper/common_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + "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/distribution/types" +) + +var ( + PKS = simapp.CreateTestPubKeys(5) + + valConsPk1 = PKS[0] + valConsPk2 = PKS[1] + valConsPk3 = PKS[2] + + valConsAddr1 = sdk.ConsAddress(valConsPk1.Address()) + valConsAddr2 = sdk.ConsAddress(valConsPk2.Address()) + + distrAcc = authtypes.NewEmptyModuleAccount(types.ModuleName) +) diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 7d79a35b40b9..61631da5dc22 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // initialize starting info for a new delegation @@ -28,7 +28,7 @@ func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sd } // calculate the rewards accrued by a delegation between two periods -func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val exported.ValidatorI, +func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val stakingtypes.ValidatorI, startingPeriod, endingPeriod uint64, stake sdk.Dec) (rewards sdk.DecCoins) { // sanity check if startingPeriod > endingPeriod { @@ -53,7 +53,7 @@ func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val exported. } // calculate the total rewards accrued by a delegation -func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val exported.ValidatorI, del exported.DelegationI, endingPeriod uint64) (rewards sdk.DecCoins) { +func (k Keeper) CalculateDelegationRewards(ctx sdk.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI, endingPeriod uint64) (rewards sdk.DecCoins) { // fetch starting info for delegation startingInfo := k.GetDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) @@ -100,7 +100,7 @@ func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val exported.Validat currentStake := val.TokensFromShares(del.GetShares()) if stake.GT(currentStake) { - // Account for rounding inconsistencies between: + // AccountI for rounding inconsistencies between: // // currentStake: calculated as in staking with a single computation // stake: calculated as an accumulation of stake @@ -136,25 +136,29 @@ func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val exported.Validat return rewards } -func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val exported.ValidatorI, del exported.DelegationI) (sdk.Coins, error) { +func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val stakingtypes.ValidatorI, del stakingtypes.DelegationI) (sdk.Coins, error) { // check existence of delegator starting info if !k.HasDelegatorStartingInfo(ctx, del.GetValidatorAddr(), del.GetDelegatorAddr()) { return nil, types.ErrEmptyDelegationDistInfo } // end current period and calculate rewards - endingPeriod := k.incrementValidatorPeriod(ctx, val) - rewardsRaw := k.calculateDelegationRewards(ctx, val, del, endingPeriod) - outstanding := k.GetValidatorOutstandingRewards(ctx, del.GetValidatorAddr()) + endingPeriod := k.IncrementValidatorPeriod(ctx, val) + rewardsRaw := k.CalculateDelegationRewards(ctx, val, del, endingPeriod) + outstanding := k.GetValidatorOutstandingRewardsCoins(ctx, del.GetValidatorAddr()) // defensive edge case may happen on the very final digits // of the decCoins due to operation order of the distribution mechanism. rewards := rewardsRaw.Intersect(outstanding) if !rewards.IsEqual(rewardsRaw) { logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("missing rewards rounding error, delegator %v"+ - "withdrawing rewards from validator %v, should have received %v, got %v", - val.GetOperator(), del.GetDelegatorAddr(), rewardsRaw, rewards)) + logger.Info( + "rounding error withdrawing rewards from validator", + "delegator", del.GetDelegatorAddr().String(), + "validator", val.GetOperator().String(), + "got", rewards.String(), + "expected", rewardsRaw.String(), + ) } // truncate coins, return remainder to community pool @@ -163,7 +167,7 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val exported.Validato // add coins to user account if !coins.IsZero() { withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, del.GetDelegatorAddr()) - err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins) if err != nil { return nil, err } @@ -171,7 +175,7 @@ func (k Keeper) withdrawDelegationRewards(ctx sdk.Context, val exported.Validato // update the outstanding rewards and the community pool only if the // transaction was successful - k.SetValidatorOutstandingRewards(ctx, del.GetValidatorAddr(), outstanding.Sub(rewards)) + k.SetValidatorOutstandingRewards(ctx, del.GetValidatorAddr(), types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(rewards)}) feePool := k.GetFeePool(ctx) feePool.CommunityPool = feePool.CommunityPool.Add(remainder...) k.SetFeePool(ctx, feePool) diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 6d23ad958991..ac8ac4a33402 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -1,49 +1,50 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + 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/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestCalculateRewardsBasic(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator( - valOpAddr1, valConsPk1, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt(), - ) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) - // end block to bond validator - staking.EndBlocker(ctx, sk) + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) - // next block + // end block to bond validator and start new block + staking.EndBlocker(ctx, app.StakingKeeper) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + tstaking.Ctx = ctx // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // historical count should be 2 (once for validator init, once for delegation init) - require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(2), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // historical count should be 2 still - require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(2), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // calculate delegation rewards - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be zero require.True(t, rewards.IsZero()) @@ -51,51 +52,49 @@ func TestCalculateRewardsBasic(t *testing.T) { // allocate some rewards initial := int64(10) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestCalculateRewardsAfterSlash(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) valPower := int64(100) - valTokens := sdk.TokensFromConsensusPower(valPower) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt()) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be zero require.True(t, rewards.IsZero()) @@ -104,10 +103,10 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) // retrieve validator - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) @@ -115,52 +114,50 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { // allocate some rewards initial := sdk.TokensFromConsensusPower(10) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.ToDec()}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoRaw(2).ToDec()}}, rewards) // commission should be the other half require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoRaw(2).ToDec()}}, - k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestCalculateRewardsAfterManySlashes(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - power := int64(100) - valTokens := sdk.TokensFromConsensusPower(power) - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt()) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be zero require.True(t, rewards.IsZero()) @@ -169,10 +166,10 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) // fetch the validator again - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) @@ -180,208 +177,196 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { // allocate some rewards initial := sdk.TokensFromConsensusPower(10) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.ToDec()}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator by 50% again - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power/2, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) // fetch the validator again - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.ToDec()}}, rewards) // commission should be the other half require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.ToDec()}}, - k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestCalculateRewardsMultiDelegator(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(100000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del1 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del1 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // allocate some rewards initial := int64(20) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // second delegation - msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) - - res, err = sh(ctx, msg2) - require.NoError(t, err) - require.NotNil(t, res) - - del2 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1) + tstaking.Ctx = ctx + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) + del2 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) // fetch updated validator - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := k.calculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be 3/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial * 3 / 4)}}, rewards) // calculate delegation rewards for del2 - rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be 1/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial * 1 / 4)}}, rewards) // commission should be equal to initial (50% twice) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestWithdrawDelegationRewardsBasic(t *testing.T) { balancePower := int64(1000) balanceTokens := sdk.TokensFromConsensusPower(balancePower) - ctx, ak, k, sk, _ := CreateTestInputDefault(t, false, balancePower) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set module account coins - distrAcc := k.GetDistributionAccount(ctx) - distrAcc.SetCoins(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens))) - k.supplyKeeper.SetModuleAccount(ctx, distrAcc) + distrAcc := app.DistrKeeper.GetDistributionAccount(ctx) + require.NoError(t, app.BankKeeper.SetBalances(ctx, distrAcc.GetAddress(), sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, balanceTokens)))) + app.AccountKeeper.SetModuleAccount(ctx, distrAcc) // create validator with 50% commission power := int64(100) - valTokens := sdk.TokensFromConsensusPower(power) - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator( - valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), - staking.Description{}, commission, sdk.OneInt(), - ) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + valTokens := tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, power, true) // assert correct initial balance expTokens := balanceTokens.Sub(valTokens) require.Equal(t, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, expTokens)}, - ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins(), + app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), ) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) // allocate some rewards initial := sdk.TokensFromConsensusPower(10) tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // historical count should be 2 (initial + latest for delegation) - require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(2), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // withdraw rewards - _, err = k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + _, err := app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) require.Nil(t, err) // historical count should still be 2 (added one record, cleared one) - require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(2), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // assert correct balance exp := balanceTokens.Sub(valTokens).Add(initial.QuoRaw(2)) require.Equal(t, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins(), + app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), ) // withdraw commission - _, err = k.WithdrawValidatorCommission(ctx, valOpAddr1) + _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) require.Nil(t, err) // assert correct balance exp = balanceTokens.Sub(valTokens).Add(initial) require.Equal(t, sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, exp)}, - ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins(), + app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])), ) } func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - power := int64(100) - valTokens := sdk.TokensFromConsensusPower(power) - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt()) + addr := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + // create validator with 50% commission + valPower := int64(100) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be zero require.True(t, rewards.IsZero()) @@ -392,258 +377,252 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { // allocate some rewards initial := sdk.TokensFromConsensusPower(10).ToDec() tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) // slash the validator by 50% again - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power/2, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower/2, sdk.NewDecWithPrec(5, 1)) // fetch the validator again - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // increase block height ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards - rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - power := int64(100) - valTokens := sdk.TokensFromConsensusPower(power) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt()) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + // create validator with 50% commission + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + valPower := int64(100) + tstaking.CreateValidatorWithValPower(valAddrs[0], valConsPk1, valPower, true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del1 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del1 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // allocate some rewards initial := sdk.TokensFromConsensusPower(30).ToDec() tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}} - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // second delegation - delTokens := sdk.TokensFromConsensusPower(100) - msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, - sdk.NewCoin(sdk.DefaultBondDenom, delTokens)) + tstaking.DelegateWithPower(sdk.AccAddress(valAddrs[1]), valAddrs[0], 100) - res, err = sh(ctx, msg2) - require.NoError(t, err) - require.NotNil(t, res) - - del2 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1) + del2 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // slash the validator again ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // fetch updated validator - val = sk.Validator(ctx, valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := k.calculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) // calculate delegation rewards for del2 - rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be initial / 3 require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards) // commission should be equal to initial (twice 50% commission, unaffected by slashing) - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) } func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { - ctx, _, k, sk, _ := CreateTestInputDefault(t, false, 1000) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) initial := int64(20) // set module account coins - distrAcc := k.GetDistributionAccount(ctx) - distrAcc.SetCoins(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000)))) - k.supplyKeeper.SetModuleAccount(ctx, distrAcc) + 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) + app.AccountKeeper.SetModuleAccount(ctx, distrAcc) tokens := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDec(initial))} // create validator with 50% commission - commission := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt()) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) // end block to bond validator - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // fetch validator and delegation - val := sk.Validator(ctx, valOpAddr1) - del1 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + del1 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) // allocate some rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // historical count should be 2 (validator init, delegation init) - require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(2), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // second delegation - msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) - res, err = sh(ctx, msg2) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(sdk.AccAddress(valAddrs[1]), valAddrs[0], sdk.NewInt(100)) // historical count should be 3 (second delegation init) - require.Equal(t, uint64(3), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(3), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // fetch updated validator - val = sk.Validator(ctx, valOpAddr1) - del2 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1) + val = app.StakingKeeper.Validator(ctx, valAddrs[0]) + del2 := app.StakingKeeper.Delegation(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // first delegator withdraws - k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) // second delegator withdraws - k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1) + _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[1]), valAddrs[0]) + require.NoError(t, err) // historical count should be 3 (validator init + two delegations) - require.Equal(t, uint64(3), k.GetValidatorHistoricalReferenceCount(ctx)) + require.Equal(t, uint64(3), app.DistrKeeper.GetValidatorHistoricalReferenceCount(ctx)) // validator withdraws commission - k.WithdrawValidatorCommission(ctx, valOpAddr1) + _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) // end period - endingPeriod := k.incrementValidatorPeriod(ctx, val) + endingPeriod := app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards := k.calculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards := app.DistrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be zero require.True(t, rewards.IsZero()) // calculate delegation rewards for del2 - rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be zero require.True(t, rewards.IsZero()) // commission should be zero - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1).IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // first delegator withdraws again - k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) + _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddrs[0]), valAddrs[0]) + require.NoError(t, err) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards = k.calculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be zero require.True(t, rewards.IsZero()) // calculate delegation rewards for del2 - rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be 1/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 4)}}, rewards) // commission should be half initial - require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) // next block ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) // allocate some more rewards - k.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) // withdraw commission - k.WithdrawValidatorCommission(ctx, valOpAddr1) + _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) // end period - endingPeriod = k.incrementValidatorPeriod(ctx, val) + endingPeriod = app.DistrKeeper.IncrementValidatorPeriod(ctx, val) // calculate delegation rewards for del1 - rewards = k.calculateDelegationRewards(ctx, val, del1, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be 1/4 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 4)}}, rewards) // calculate delegation rewards for del2 - rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) + rewards = app.DistrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be 1/2 initial require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, rewards) // commission should be zero - require.True(t, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1).IsZero()) + require.True(t, app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) } diff --git a/x/distribution/keeper/fee_pool.go b/x/distribution/keeper/fee_pool.go index 02107bb815aa..a6047501355c 100644 --- a/x/distribution/keeper/fee_pool.go +++ b/x/distribution/keeper/fee_pool.go @@ -20,7 +20,7 @@ func (k Keeper) DistributeFromFeePool(ctx sdk.Context, amount sdk.Coins, receive feePool.CommunityPool = newPool - err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiveAddr, amount) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiveAddr, amount) if err != nil { return err } diff --git a/x/distribution/keeper/genesis.go b/x/distribution/keeper/genesis.go new file mode 100644 index 000000000000..4aa25671a32e --- /dev/null +++ b/x/distribution/keeper/genesis.go @@ -0,0 +1,195 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// InitGenesis sets distribution information for genesis +func (k Keeper) InitGenesis(ctx sdk.Context, data types.GenesisState) { + var moduleHoldings sdk.DecCoins + + k.SetFeePool(ctx, data.FeePool) + k.SetParams(ctx, data.Params) + + for _, dwi := range data.DelegatorWithdrawInfos { + delegatorAddress, err := sdk.AccAddressFromBech32(dwi.DelegatorAddress) + if err != nil { + panic(err) + } + withdrawAddress, err := sdk.AccAddressFromBech32(dwi.WithdrawAddress) + if err != nil { + panic(err) + } + + k.SetDelegatorWithdrawAddr(ctx, delegatorAddress, withdrawAddress) + } + + var previousProposer sdk.ConsAddress + if data.PreviousProposer != "" { + var err error + previousProposer, err = sdk.ConsAddressFromBech32(data.PreviousProposer) + if err != nil { + panic(err) + } + } + + k.SetPreviousProposerConsAddr(ctx, previousProposer) + + for _, rew := range data.OutstandingRewards { + valAddr, err := sdk.ValAddressFromBech32(rew.ValidatorAddress) + if err != nil { + panic(err) + } + k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: rew.OutstandingRewards}) + moduleHoldings = moduleHoldings.Add(rew.OutstandingRewards...) + } + for _, acc := range data.ValidatorAccumulatedCommissions { + valAddr, err := sdk.ValAddressFromBech32(acc.ValidatorAddress) + if err != nil { + panic(err) + } + k.SetValidatorAccumulatedCommission(ctx, valAddr, acc.Accumulated) + } + for _, his := range data.ValidatorHistoricalRewards { + valAddr, err := sdk.ValAddressFromBech32(his.ValidatorAddress) + if err != nil { + panic(err) + } + k.SetValidatorHistoricalRewards(ctx, valAddr, his.Period, his.Rewards) + } + for _, cur := range data.ValidatorCurrentRewards { + valAddr, err := sdk.ValAddressFromBech32(cur.ValidatorAddress) + if err != nil { + panic(err) + } + k.SetValidatorCurrentRewards(ctx, valAddr, cur.Rewards) + } + for _, del := range data.DelegatorStartingInfos { + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delegatorAddress, err := sdk.AccAddressFromBech32(del.DelegatorAddress) + if err != nil { + panic(err) + } + k.SetDelegatorStartingInfo(ctx, valAddr, delegatorAddress, del.StartingInfo) + } + for _, evt := range data.ValidatorSlashEvents { + valAddr, err := sdk.ValAddressFromBech32(evt.ValidatorAddress) + if err != nil { + panic(err) + } + k.SetValidatorSlashEvent(ctx, valAddr, evt.Height, evt.Period, evt.ValidatorSlashEvent) + } + + moduleHoldings = moduleHoldings.Add(data.FeePool.CommunityPool...) + moduleHoldingsInt, _ := moduleHoldings.TruncateDecimal() + + // check if the module account exists + moduleAcc := k.GetDistributionAccount(ctx) + if moduleAcc == nil { + panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) + } + + 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) + } +} + +// ExportGenesis returns a GenesisState for a given context and keeper. +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + feePool := k.GetFeePool(ctx) + params := k.GetParams(ctx) + + dwi := make([]types.DelegatorWithdrawInfo, 0) + k.IterateDelegatorWithdrawAddrs(ctx, func(del sdk.AccAddress, addr sdk.AccAddress) (stop bool) { + dwi = append(dwi, types.DelegatorWithdrawInfo{ + DelegatorAddress: del.String(), + WithdrawAddress: addr.String(), + }) + return false + }) + + pp := k.GetPreviousProposerConsAddr(ctx) + outstanding := make([]types.ValidatorOutstandingRewardsRecord, 0) + + k.IterateValidatorOutstandingRewards(ctx, + func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { + outstanding = append(outstanding, types.ValidatorOutstandingRewardsRecord{ + ValidatorAddress: addr.String(), + OutstandingRewards: rewards.Rewards, + }) + return false + }, + ) + + acc := make([]types.ValidatorAccumulatedCommissionRecord, 0) + k.IterateValidatorAccumulatedCommissions(ctx, + func(addr sdk.ValAddress, commission types.ValidatorAccumulatedCommission) (stop bool) { + acc = append(acc, types.ValidatorAccumulatedCommissionRecord{ + ValidatorAddress: addr.String(), + Accumulated: commission, + }) + return false + }, + ) + + his := make([]types.ValidatorHistoricalRewardsRecord, 0) + k.IterateValidatorHistoricalRewards(ctx, + func(val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) (stop bool) { + his = append(his, types.ValidatorHistoricalRewardsRecord{ + ValidatorAddress: val.String(), + Period: period, + Rewards: rewards, + }) + return false + }, + ) + + cur := make([]types.ValidatorCurrentRewardsRecord, 0) + k.IterateValidatorCurrentRewards(ctx, + func(val sdk.ValAddress, rewards types.ValidatorCurrentRewards) (stop bool) { + cur = append(cur, types.ValidatorCurrentRewardsRecord{ + ValidatorAddress: val.String(), + Rewards: rewards, + }) + return false + }, + ) + + dels := make([]types.DelegatorStartingInfoRecord, 0) + k.IterateDelegatorStartingInfos(ctx, + func(val sdk.ValAddress, del sdk.AccAddress, info types.DelegatorStartingInfo) (stop bool) { + dels = append(dels, types.DelegatorStartingInfoRecord{ + ValidatorAddress: val.String(), + DelegatorAddress: del.String(), + StartingInfo: info, + }) + return false + }, + ) + + slashes := make([]types.ValidatorSlashEventRecord, 0) + k.IterateValidatorSlashEvents(ctx, + func(val sdk.ValAddress, height uint64, event types.ValidatorSlashEvent) (stop bool) { + slashes = append(slashes, types.ValidatorSlashEventRecord{ + ValidatorAddress: val.String(), + Height: height, + Period: event.ValidatorPeriod, + ValidatorSlashEvent: event, + }) + return false + }, + ) + + return types.NewGenesisState(params, feePool, dwi, pp, outstanding, acc, his, cur, dels, slashes) +} diff --git a/x/distribution/keeper/grpc_query.go b/x/distribution/keeper/grpc_query.go new file mode 100644 index 000000000000..7991634fc401 --- /dev/null +++ b/x/distribution/keeper/grpc_query.go @@ -0,0 +1,250 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var _ types.QueryServer = Keeper{} + +// Params queries params of distribution module +func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + var params types.Params + k.paramSpace.GetParamSet(ctx, ¶ms) + + return &types.QueryParamsResponse{Params: params}, nil +} + +// ValidatorOutstandingRewards queries rewards of a validator address +func (k Keeper) ValidatorOutstandingRewards(c context.Context, req *types.QueryValidatorOutstandingRewardsRequest) (*types.QueryValidatorOutstandingRewardsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ValidatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty validator address") + } + + ctx := sdk.UnwrapSDKContext(c) + + valAdr, err := sdk.ValAddressFromBech32(req.ValidatorAddress) + if err != nil { + return nil, err + } + rewards := k.GetValidatorOutstandingRewards(ctx, valAdr) + + return &types.QueryValidatorOutstandingRewardsResponse{Rewards: rewards}, nil +} + +// ValidatorCommission queries accumulated commission for a validator +func (k Keeper) ValidatorCommission(c context.Context, req *types.QueryValidatorCommissionRequest) (*types.QueryValidatorCommissionResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ValidatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty validator address") + } + + ctx := sdk.UnwrapSDKContext(c) + + valAdr, err := sdk.ValAddressFromBech32(req.ValidatorAddress) + if err != nil { + return nil, err + } + commission := k.GetValidatorAccumulatedCommission(ctx, valAdr) + + return &types.QueryValidatorCommissionResponse{Commission: commission}, nil +} + +// ValidatorSlashes queries slash events of a validator +func (k Keeper) ValidatorSlashes(c context.Context, req *types.QueryValidatorSlashesRequest) (*types.QueryValidatorSlashesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ValidatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty validator address") + } + + if req.EndingHeight < req.StartingHeight { + return nil, status.Errorf(codes.InvalidArgument, "starting height greater than ending height (%d > %d)", req.StartingHeight, req.EndingHeight) + } + + ctx := sdk.UnwrapSDKContext(c) + events := make([]types.ValidatorSlashEvent, 0) + store := ctx.KVStore(k.storeKey) + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddress) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid validator address") + } + slashesStore := prefix.NewStore(store, types.GetValidatorSlashEventPrefix(valAddr)) + + pageRes, err := query.FilteredPaginate(slashesStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + var result types.ValidatorSlashEvent + err := k.cdc.UnmarshalBinaryBare(value, &result) + + if err != nil { + return false, err + } + + if result.ValidatorPeriod < req.StartingHeight || result.ValidatorPeriod > req.EndingHeight { + return false, nil + } + + if accumulate { + events = append(events, result) + } + return true, nil + }) + + if err != nil { + return nil, err + } + + return &types.QueryValidatorSlashesResponse{Slashes: events, Pagination: pageRes}, nil +} + +// DelegationRewards the total rewards accrued by a delegation +func (k Keeper) DelegationRewards(c context.Context, req *types.QueryDelegationRewardsRequest) (*types.QueryDelegationRewardsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.DelegatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty delegator address") + } + + if req.ValidatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty validator address") + } + + ctx := sdk.UnwrapSDKContext(c) + + valAdr, err := sdk.ValAddressFromBech32(req.ValidatorAddress) + if err != nil { + return nil, err + } + + val := k.stakingKeeper.Validator(ctx, valAdr) + if val == nil { + return nil, sdkerrors.Wrap(types.ErrNoValidatorExists, req.ValidatorAddress) + } + + delAdr, err := sdk.AccAddressFromBech32(req.DelegatorAddress) + if err != nil { + return nil, err + } + del := k.stakingKeeper.Delegation(ctx, delAdr, valAdr) + if del == nil { + return nil, types.ErrNoDelegationExists + } + + endingPeriod := k.IncrementValidatorPeriod(ctx, val) + rewards := k.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + return &types.QueryDelegationRewardsResponse{Rewards: rewards}, nil +} + +// DelegationTotalRewards the total rewards accrued by a each validator +func (k Keeper) DelegationTotalRewards(c context.Context, req *types.QueryDelegationTotalRewardsRequest) (*types.QueryDelegationTotalRewardsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.DelegatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty delegator address") + } + + ctx := sdk.UnwrapSDKContext(c) + + total := sdk.DecCoins{} + var delRewards []types.DelegationDelegatorReward + + delAdr, err := sdk.AccAddressFromBech32(req.DelegatorAddress) + if err != nil { + return nil, err + } + + k.stakingKeeper.IterateDelegations( + ctx, delAdr, + func(_ int64, del stakingtypes.DelegationI) (stop bool) { + valAddr := del.GetValidatorAddr() + val := k.stakingKeeper.Validator(ctx, valAddr) + endingPeriod := k.IncrementValidatorPeriod(ctx, val) + delReward := k.CalculateDelegationRewards(ctx, val, del, endingPeriod) + + delRewards = append(delRewards, types.NewDelegationDelegatorReward(valAddr, delReward)) + total = total.Add(delReward...) + return false + }, + ) + + return &types.QueryDelegationTotalRewardsResponse{Rewards: delRewards, Total: total}, nil +} + +// DelegatorValidators queries the validators list of a delegator +func (k Keeper) DelegatorValidators(c context.Context, req *types.QueryDelegatorValidatorsRequest) (*types.QueryDelegatorValidatorsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.DelegatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty delegator address") + } + + ctx := sdk.UnwrapSDKContext(c) + delAdr, err := sdk.AccAddressFromBech32(req.DelegatorAddress) + if err != nil { + return nil, err + } + var validators []string + + k.stakingKeeper.IterateDelegations( + ctx, delAdr, + func(_ int64, del stakingtypes.DelegationI) (stop bool) { + validators = append(validators, del.GetValidatorAddr().String()) + return false + }, + ) + + return &types.QueryDelegatorValidatorsResponse{Validators: validators}, nil +} + +// DelegatorWithdrawAddress queries Query/delegatorWithdrawAddress +func (k Keeper) DelegatorWithdrawAddress(c context.Context, req *types.QueryDelegatorWithdrawAddressRequest) (*types.QueryDelegatorWithdrawAddressResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.DelegatorAddress == "" { + return nil, status.Error(codes.InvalidArgument, "empty delegator address") + } + delAdr, err := sdk.AccAddressFromBech32(req.DelegatorAddress) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, delAdr) + + return &types.QueryDelegatorWithdrawAddressResponse{WithdrawAddress: withdrawAddr.String()}, nil +} + +// CommunityPool queries the community pool coins +func (k Keeper) CommunityPool(c context.Context, req *types.QueryCommunityPoolRequest) (*types.QueryCommunityPoolResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + pool := k.GetFeePoolCommunityCoins(ctx) + + return &types.QueryCommunityPoolResponse{Pool: pool}, nil +} diff --git a/x/distribution/keeper/grpc_query_test.go b/x/distribution/keeper/grpc_query_test.go new file mode 100644 index 000000000000..412144094626 --- /dev/null +++ b/x/distribution/keeper/grpc_query_test.go @@ -0,0 +1,657 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient + addrs []sdk.AccAddress + valAddrs []sdk.ValAddress +} + +func (suite *KeeperTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.DistrKeeper) + queryClient := types.NewQueryClient(queryHelper) + + suite.app = app + suite.ctx = ctx + suite.queryClient = queryClient + + suite.addrs = simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) + suite.valAddrs = simapp.ConvertAddrsToValAddrs(suite.addrs) +} + +func (suite *KeeperTestSuite) TestGRPCParams() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + var ( + params types.Params + req *types.QueryParamsRequest + expParams types.Params + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty params request", + func() { + req = &types.QueryParamsRequest{} + expParams = types.DefaultParams() + }, + true, + }, + { + "valid request", + func() { + params = types.Params{ + CommunityTax: sdk.NewDecWithPrec(3, 1), + BaseProposerReward: sdk.NewDecWithPrec(2, 1), + BonusProposerReward: sdk.NewDecWithPrec(1, 1), + WithdrawAddrEnabled: true, + } + + app.DistrKeeper.SetParams(ctx, params) + req = &types.QueryParamsRequest{} + expParams = params + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + paramsRes, err := queryClient.Params(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(paramsRes) + suite.Require().Equal(paramsRes.Params, expParams) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCValidatorOutstandingRewards() { + app, ctx, queryClient, valAddrs := suite.app, suite.ctx, suite.queryClient, suite.valAddrs + + valCommission := sdk.DecCoins{ + sdk.NewDecCoinFromDec("mytoken", sdk.NewDec(5000)), + sdk.NewDecCoinFromDec("stake", sdk.NewDec(300)), + } + + // set outstanding rewards + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) + rewards := app.DistrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]) + + var req *types.QueryValidatorOutstandingRewardsRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorOutstandingRewardsRequest{} + }, + false, + }, { + "valid request", + func() { + req = &types.QueryValidatorOutstandingRewardsRequest{ValidatorAddress: valAddrs[0].String()} + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + validatorOutstandingRewards, err := queryClient.ValidatorOutstandingRewards(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rewards, validatorOutstandingRewards.Rewards) + suite.Require().Equal(valCommission, validatorOutstandingRewards.Rewards.Rewards) + } else { + suite.Require().Error(err) + suite.Require().Nil(validatorOutstandingRewards) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCValidatorCommission() { + app, ctx, queryClient, valAddrs := suite.app, suite.ctx, suite.queryClient, suite.valAddrs + + commission := sdk.DecCoins{{Denom: "token1", Amount: sdk.NewDec(4)}, {Denom: "token2", Amount: sdk.NewDec(2)}} + app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, valAddrs[0], types.ValidatorAccumulatedCommission{Commission: commission}) + + var req *types.QueryValidatorCommissionRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorCommissionRequest{} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryValidatorCommissionRequest{ValidatorAddress: valAddrs[0].String()} + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + commissionRes, err := queryClient.ValidatorCommission(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(commissionRes) + suite.Require().Equal(commissionRes.Commission.Commission, commission) + } else { + suite.Require().Error(err) + suite.Require().Nil(commissionRes) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCValidatorSlashes() { + app, ctx, queryClient, valAddrs := suite.app, suite.ctx, suite.queryClient, suite.valAddrs + + slashes := []types.ValidatorSlashEvent{ + types.NewValidatorSlashEvent(3, sdk.NewDecWithPrec(5, 1)), + types.NewValidatorSlashEvent(5, sdk.NewDecWithPrec(5, 1)), + types.NewValidatorSlashEvent(7, sdk.NewDecWithPrec(5, 1)), + types.NewValidatorSlashEvent(9, sdk.NewDecWithPrec(5, 1)), + } + + for i, slash := range slashes { + app.DistrKeeper.SetValidatorSlashEvent(ctx, valAddrs[0], uint64(i+2), 0, slash) + } + + var ( + req *types.QueryValidatorSlashesRequest + expRes *types.QueryValidatorSlashesResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorSlashesRequest{} + expRes = &types.QueryValidatorSlashesResponse{} + }, + false, + }, + { + "Ending height lesser than start height request", + func() { + req = &types.QueryValidatorSlashesRequest{ + ValidatorAddress: valAddrs[1].String(), + StartingHeight: 10, + EndingHeight: 1, + } + expRes = &types.QueryValidatorSlashesResponse{Pagination: &query.PageResponse{}} + }, + false, + }, + { + "no slash event validator request", + func() { + req = &types.QueryValidatorSlashesRequest{ + ValidatorAddress: valAddrs[1].String(), + StartingHeight: 1, + EndingHeight: 10, + } + expRes = &types.QueryValidatorSlashesResponse{Pagination: &query.PageResponse{}} + }, + true, + }, + { + "request slashes with offset 2 and limit 2", + func() { + pageReq := &query.PageRequest{ + Offset: 2, + Limit: 2, + } + + req = &types.QueryValidatorSlashesRequest{ + ValidatorAddress: valAddrs[0].String(), + StartingHeight: 1, + EndingHeight: 10, + Pagination: pageReq, + } + + expRes = &types.QueryValidatorSlashesResponse{ + Slashes: slashes[2:], + } + }, + true, + }, + { + "request slashes with page limit 3 and count total", + func() { + pageReq := &query.PageRequest{ + Limit: 3, + CountTotal: true, + } + + req = &types.QueryValidatorSlashesRequest{ + ValidatorAddress: valAddrs[0].String(), + StartingHeight: 1, + EndingHeight: 10, + Pagination: pageReq, + } + + expRes = &types.QueryValidatorSlashesResponse{ + Slashes: slashes[:3], + } + }, + true, + }, + { + "request slashes with page limit 4 and count total", + func() { + pageReq := &query.PageRequest{ + Limit: 4, + CountTotal: true, + } + + req = &types.QueryValidatorSlashesRequest{ + ValidatorAddress: valAddrs[0].String(), + StartingHeight: 1, + EndingHeight: 10, + Pagination: pageReq, + } + + expRes = &types.QueryValidatorSlashesResponse{ + Slashes: slashes[:4], + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + slashesRes, err := queryClient.ValidatorSlashes(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes.GetSlashes(), slashesRes.GetSlashes()) + } else { + suite.Require().Error(err) + suite.Require().Nil(slashesRes) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCDelegationRewards() { + app, ctx, addrs, valAddrs := suite.app, suite.ctx, suite.addrs, suite.valAddrs + + tstaking := teststaking.NewHelper(suite.T(), ctx, app.StakingKeeper) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valAddrs[0], valConsPk1, sdk.NewInt(100), true) + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.DistrKeeper) + queryClient := types.NewQueryClient(queryHelper) + + val := app.StakingKeeper.Validator(ctx, valAddrs[0]) + + initial := int64(10) + tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}} + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) + + // test command delegation rewards grpc + var ( + req *types.QueryDelegationRewardsRequest + expRes *types.QueryDelegationRewardsResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegationRewardsRequest{} + }, + false, + }, + { + "empty delegator request", + func() { + req = &types.QueryDelegationRewardsRequest{ + DelegatorAddress: "", + ValidatorAddress: valAddrs[0].String(), + } + }, + false, + }, + { + "empty validator request", + func() { + req = &types.QueryDelegationRewardsRequest{ + DelegatorAddress: addrs[1].String(), + ValidatorAddress: "", + } + }, + false, + }, + { + "request with wrong delegator and validator", + func() { + req = &types.QueryDelegationRewardsRequest{ + DelegatorAddress: addrs[1].String(), + ValidatorAddress: valAddrs[1].String(), + } + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegationRewardsRequest{ + DelegatorAddress: addrs[0].String(), + ValidatorAddress: valAddrs[0].String(), + } + + expRes = &types.QueryDelegationRewardsResponse{ + Rewards: sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + rewards, err := queryClient.DelegationRewards(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes, rewards) + } else { + suite.Require().Error(err) + suite.Require().Nil(rewards) + } + }) + } + + // test command delegator total rewards grpc + var ( + totalRewardsReq *types.QueryDelegationTotalRewardsRequest + expTotalRewardsRes *types.QueryDelegationTotalRewardsResponse + ) + + testCases = []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + totalRewardsReq = &types.QueryDelegationTotalRewardsRequest{} + }, + false, + }, + { + "valid total delegation rewards", + func() { + totalRewardsReq = &types.QueryDelegationTotalRewardsRequest{ + DelegatorAddress: addrs[0].String(), + } + + expectedDelReward := types.NewDelegationDelegatorReward(valAddrs[0], + sdk.DecCoins{sdk.NewInt64DecCoin("stake", 5)}) + + expTotalRewardsRes = &types.QueryDelegationTotalRewardsResponse{ + Rewards: []types.DelegationDelegatorReward{expectedDelReward}, + Total: expectedDelReward.Reward, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + totalRewardsRes, err := queryClient.DelegationTotalRewards(gocontext.Background(), totalRewardsReq) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(totalRewardsRes, expTotalRewardsRes) + } else { + + suite.Require().Error(err) + suite.Require().Nil(totalRewardsRes) + } + }) + } + + // test command validator delegators grpc + var ( + delegatorValidatorsReq *types.QueryDelegatorValidatorsRequest + expDelegatorValidatorsRes *types.QueryDelegatorValidatorsResponse + ) + + testCases = []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + delegatorValidatorsReq = &types.QueryDelegatorValidatorsRequest{} + }, + false, + }, + { + "request no delegations address", + func() { + delegatorValidatorsReq = &types.QueryDelegatorValidatorsRequest{ + DelegatorAddress: addrs[1].String(), + } + + expDelegatorValidatorsRes = &types.QueryDelegatorValidatorsResponse{} + }, + true, + }, + { + "valid request", + func() { + delegatorValidatorsReq = &types.QueryDelegatorValidatorsRequest{ + DelegatorAddress: addrs[0].String(), + } + expDelegatorValidatorsRes = &types.QueryDelegatorValidatorsResponse{ + Validators: []string{valAddrs[0].String()}, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + validators, err := queryClient.DelegatorValidators(gocontext.Background(), delegatorValidatorsReq) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expDelegatorValidatorsRes, validators) + } else { + suite.Require().Error(err) + suite.Require().Nil(validators) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCDelegatorWithdrawAddress() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + err := app.DistrKeeper.SetWithdrawAddr(ctx, addrs[0], addrs[1]) + suite.Require().Nil(err) + + var req *types.QueryDelegatorWithdrawAddressRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorWithdrawAddressRequest{} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryDelegatorWithdrawAddressRequest{DelegatorAddress: addrs[0].String()} + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + withdrawAddress, err := queryClient.DelegatorWithdrawAddress(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(withdrawAddress.WithdrawAddress, addrs[1].String()) + } else { + suite.Require().Error(err) + suite.Require().Nil(withdrawAddress) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCCommunityPool() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + var ( + req *types.QueryCommunityPoolRequest + expPool *types.QueryCommunityPoolResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "valid request empty community pool", + func() { + req = &types.QueryCommunityPoolRequest{} + expPool = &types.QueryCommunityPoolResponse{} + }, + true, + }, + { + "valid request", + func() { + amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) + suite.Require().NoError(app.BankKeeper.SetBalances(ctx, addrs[0], amount)) + + err := app.DistrKeeper.FundCommunityPool(ctx, amount, addrs[0]) + suite.Require().Nil(err) + req = &types.QueryCommunityPoolRequest{} + + expPool = &types.QueryCommunityPoolResponse{Pool: sdk.NewDecCoinsFromCoins(amount...)} + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + pool, err := queryClient.CommunityPool(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expPool, pool) + } else { + suite.Require().Error(err) + suite.Require().Nil(pool) + } + }) + } +} + +func TestDistributionTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index e7f843dd2bd7..ba951ed1ac9f 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -24,12 +24,11 @@ func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { // cleanup for after validator is removed func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr sdk.ValAddress) { - // fetch outstanding - outstanding := h.k.GetValidatorOutstandingRewards(ctx, valAddr) + outstanding := h.k.GetValidatorOutstandingRewardsCoins(ctx, valAddr) // force-withdraw commission - commission := h.k.GetValidatorAccumulatedCommission(ctx, valAddr) + commission := h.k.GetValidatorAccumulatedCommission(ctx, valAddr).Commission if !commission.IsZero() { // subtract from outstanding outstanding = outstanding.Sub(commission) @@ -44,11 +43,10 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr // add to validator account if !coins.IsZero() { - accAddr := sdk.AccAddress(valAddr) withdrawAddr := h.k.GetDelegatorWithdrawAddr(ctx, accAddr) - err := h.k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins) - if err != nil { + + if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, coins); err != nil { panic(err) } } @@ -78,13 +76,14 @@ func (h Hooks) AfterValidatorRemoved(ctx sdk.Context, _ sdk.ConsAddress, valAddr // increment period func (h Hooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { val := h.k.stakingKeeper.Validator(ctx, valAddr) - h.k.incrementValidatorPeriod(ctx, val) + h.k.IncrementValidatorPeriod(ctx, val) } // withdraw delegation rewards (which also increments period) func (h Hooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { val := h.k.stakingKeeper.Validator(ctx, valAddr) del := h.k.stakingKeeper.Delegation(ctx, delAddr, valAddr) + if _, err := h.k.withdrawDelegationRewards(ctx, val, del); err != nil { panic(err) } @@ -100,7 +99,6 @@ func (h Hooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, f h.k.updateValidatorSlashFraction(ctx, valAddr, fraction) } -// nolint - unused hooks func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) AfterValidatorBonded(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} diff --git a/x/distribution/keeper/invariants.go b/x/distribution/keeper/invariants.go index cff02da07691..0a23d36be8f7 100644 --- a/x/distribution/keeper/invariants.go +++ b/x/distribution/keeper/invariants.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // register all distribution invariants @@ -47,7 +47,7 @@ func NonNegativeOutstandingInvariant(k Keeper) sdk.Invariant { var outstanding sdk.DecCoins k.IterateValidatorOutstandingRewards(ctx, func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - outstanding = rewards + outstanding = rewards.GetRewards() if outstanding.IsAnyNegative() { count++ msg += fmt.Sprintf("\t%v has negative outstanding coins: %v\n", addr, outstanding) @@ -77,7 +77,7 @@ func CanWithdrawInvariant(k Keeper) sdk.Invariant { } // iterate over all validators - k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { + k.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { _, _ = k.WithdrawValidatorCommission(ctx, val.GetOperator()) delegationAddrs, ok := valDelegationAddrs[val.GetOperator().String()] @@ -89,7 +89,7 @@ func CanWithdrawInvariant(k Keeper) sdk.Invariant { } } - remaining = k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) + remaining = k.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) if len(remaining) > 0 && remaining[0].Amount.IsNegative() { return true } @@ -108,7 +108,7 @@ func ReferenceCountInvariant(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { valCount := uint64(0) - k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { + k.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { valCount++ return false }) @@ -140,7 +140,7 @@ func ModuleAccountInvariant(k Keeper) sdk.Invariant { var expectedCoins sdk.DecCoins k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - expectedCoins = expectedCoins.Add(rewards...) + expectedCoins = expectedCoins.Add(rewards.Rewards...) return false }) @@ -148,11 +148,15 @@ func ModuleAccountInvariant(k Keeper) sdk.Invariant { expectedInt, _ := expectedCoins.Add(communityPool...).TruncateDecimal() macc := k.GetDistributionAccount(ctx) + balances := k.bankKeeper.GetAllBalances(ctx, macc.GetAddress()) - broken := !macc.GetCoins().IsEqual(expectedInt) - return sdk.FormatInvariant(types.ModuleName, "ModuleAccount coins", + broken := !balances.IsEqual(expectedInt) + return sdk.FormatInvariant( + types.ModuleName, "ModuleAccount coins", fmt.Sprintf("\texpected ModuleAccount coins: %s\n"+ "\tdistribution ModuleAccount coins: %s\n", - expectedInt, macc.GetCoins())), broken + expectedInt, balances, + ), + ), broken } } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index 18cbce2b6848..0d5c32f78eb0 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -3,37 +3,38 @@ 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/distribution/types" - "github.com/cosmos/cosmos-sdk/x/params" - - "github.com/tendermint/tendermint/libs/log" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) // Keeper of the distribution store type Keeper struct { storeKey sdk.StoreKey - cdc *codec.Codec - paramSpace params.Subspace + cdc codec.BinaryMarshaler + paramSpace paramtypes.Subspace + authKeeper types.AccountKeeper + bankKeeper types.BankKeeper stakingKeeper types.StakingKeeper - supplyKeeper types.SupplyKeeper - blacklistedAddrs map[string]bool + blockedAddrs map[string]bool feeCollectorName string // name of the FeeCollector ModuleAccount } // NewKeeper creates a new distribution Keeper instance func NewKeeper( - cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, - sk types.StakingKeeper, supplyKeeper types.SupplyKeeper, feeCollectorName string, - blacklistedAddrs map[string]bool, + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace paramtypes.Subspace, + ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, + feeCollectorName string, blockedAddrs map[string]bool, ) Keeper { // ensure distribution module account is set - if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil { + if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } @@ -46,22 +47,23 @@ func NewKeeper( storeKey: key, cdc: cdc, paramSpace: paramSpace, + authKeeper: ak, + bankKeeper: bk, stakingKeeper: sk, - supplyKeeper: supplyKeeper, feeCollectorName: feeCollectorName, - blacklistedAddrs: blacklistedAddrs, + blockedAddrs: blockedAddrs, } } // 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)) + return ctx.Logger().With("module", "x/"+types.ModuleName) } // SetWithdrawAddr sets a new address that will receive the rewards upon withdrawal func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error { - if k.blacklistedAddrs[withdrawAddr.String()] { - return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is blacklisted from receiving external funds", withdrawAddr) + if k.blockedAddrs[withdrawAddr.String()] { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", withdrawAddr) } if !k.GetWithdrawAddrEnabled(ctx) { @@ -114,21 +116,21 @@ func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddres func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddress) (sdk.Coins, error) { // fetch validator accumulated commission accumCommission := k.GetValidatorAccumulatedCommission(ctx, valAddr) - if accumCommission.IsZero() { + if accumCommission.Commission.IsZero() { return nil, types.ErrNoValidatorCommission } - commission, remainder := accumCommission.TruncateDecimal() - k.SetValidatorAccumulatedCommission(ctx, valAddr, remainder) // leave remainder to withdraw later + commission, remainder := accumCommission.Commission.TruncateDecimal() + k.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: remainder}) // leave remainder to withdraw later // update outstanding - outstanding := k.GetValidatorOutstandingRewards(ctx, valAddr) - k.SetValidatorOutstandingRewards(ctx, valAddr, outstanding.Sub(sdk.NewDecCoinsFromCoins(commission...))) + outstanding := k.GetValidatorOutstandingRewards(ctx, valAddr).Rewards + k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(sdk.NewDecCoinsFromCoins(commission...))}) if !commission.IsZero() { accAddr := sdk.AccAddress(valAddr) withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr) - err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission) if err != nil { return nil, err } @@ -148,7 +150,7 @@ func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddr func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) { k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - totalRewards = totalRewards.Add(rewards...) + totalRewards = totalRewards.Add(rewards.Rewards...) return false }, ) @@ -161,7 +163,7 @@ func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) { // added to the pool. An error is returned if the amount cannot be sent to the // module account. func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error { - if err := k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil { + if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil { return err } diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index 5b2d989f7f82..a8205dfdc1db 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -1,75 +1,85 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + 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/distribution/types" ) func TestSetWithdrawAddr(t *testing.T) { - ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 1000) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) - params := keeper.GetParams(ctx) + params := app.DistrKeeper.GetParams(ctx) params.WithdrawAddrEnabled = false - keeper.SetParams(ctx, params) + app.DistrKeeper.SetParams(ctx, params) - err := keeper.SetWithdrawAddr(ctx, delAddr1, delAddr2) + err := app.DistrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) require.NotNil(t, err) params.WithdrawAddrEnabled = true - keeper.SetParams(ctx, params) + app.DistrKeeper.SetParams(ctx, params) - err = keeper.SetWithdrawAddr(ctx, delAddr1, delAddr2) + err = app.DistrKeeper.SetWithdrawAddr(ctx, addr[0], addr[1]) require.Nil(t, err) - keeper.blacklistedAddrs[distrAcc.GetAddress().String()] = true - require.Error(t, keeper.SetWithdrawAddr(ctx, delAddr1, distrAcc.GetAddress())) + require.Error(t, app.DistrKeeper.SetWithdrawAddr(ctx, addr[0], distrAcc.GetAddress())) } func TestWithdrawValidatorCommission(t *testing.T) { - ctx, ak, keeper, _, _ := CreateTestInputDefault(t, false, 1000) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) valCommission := sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", sdk.NewDec(5).Quo(sdk.NewDec(4))), sdk.NewDecCoinFromDec("stake", sdk.NewDec(3).Quo(sdk.NewDec(2))), } + addr := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + // set module account coins - distrAcc := keeper.GetDistributionAccount(ctx) - distrAcc.SetCoins(sdk.NewCoins( + 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)), )) - keeper.supplyKeeper.SetModuleAccount(ctx, distrAcc) + require.NoError(t, err) + app.AccountKeeper.SetModuleAccount(ctx, distrAcc) // check initial balance - balance := ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins() + balance := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) expTokens := sdk.TokensFromConsensusPower(1000) expCoins := sdk.NewCoins(sdk.NewCoin("stake", expTokens)) require.Equal(t, expCoins, balance) // set outstanding rewards - keeper.SetValidatorOutstandingRewards(ctx, valOpAddr3, valCommission) + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) // set commission - keeper.SetValidatorAccumulatedCommission(ctx, valOpAddr3, valCommission) + app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, valAddrs[0], types.ValidatorAccumulatedCommission{Commission: valCommission}) // withdraw commission - keeper.WithdrawValidatorCommission(ctx, valOpAddr3) + _, err = app.DistrKeeper.WithdrawValidatorCommission(ctx, valAddrs[0]) + require.NoError(t, err) // check balance increase - balance = ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins() + balance = app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(valAddrs[0])) require.Equal(t, sdk.NewCoins( sdk.NewCoin("mytoken", sdk.NewInt(1)), sdk.NewCoin("stake", expTokens.AddRaw(1)), ), balance) // check remainder - remainder := keeper.GetValidatorAccumulatedCommission(ctx, valOpAddr3) + remainder := app.DistrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission require.Equal(t, sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", sdk.NewDec(1).Quo(sdk.NewDec(4))), sdk.NewDecCoinFromDec("stake", sdk.NewDec(1).Quo(sdk.NewDec(2))), @@ -79,35 +89,41 @@ func TestWithdrawValidatorCommission(t *testing.T) { } func TestGetTotalRewards(t *testing.T) { - ctx, _, keeper, _, _ := CreateTestInputDefault(t, false, 1000) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) valCommission := sdk.DecCoins{ sdk.NewDecCoinFromDec("mytoken", sdk.NewDec(5).Quo(sdk.NewDec(4))), sdk.NewDecCoinFromDec("stake", sdk.NewDec(3).Quo(sdk.NewDec(2))), } - keeper.SetValidatorOutstandingRewards(ctx, valOpAddr1, valCommission) - keeper.SetValidatorOutstandingRewards(ctx, valOpAddr2, valCommission) + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[0], types.ValidatorOutstandingRewards{Rewards: valCommission}) + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, valAddrs[1], types.ValidatorOutstandingRewards{Rewards: valCommission}) expectedRewards := valCommission.MulDec(sdk.NewDec(2)) - totalRewards := keeper.GetTotalRewards(ctx) + totalRewards := app.DistrKeeper.GetTotalRewards(ctx) require.Equal(t, expectedRewards, totalRewards) } func TestFundCommunityPool(t *testing.T) { - // nolint dogsled - ctx, _, bk, keeper, _, _, _ := CreateTestInputAdvanced(t, false, 1000, sdk.NewDecWithPrec(2, 2)) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simapp.AddTestAddrs(app, ctx, 2, sdk.NewInt(1000000000)) amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100)) - _ = bk.SetCoins(ctx, delAddr1, amount) + require.NoError(t, app.BankKeeper.SetBalances(ctx, addr[0], amount)) - initPool := keeper.GetFeePool(ctx) + initPool := app.DistrKeeper.GetFeePool(ctx) assert.Empty(t, initPool.CommunityPool) - err := keeper.FundCommunityPool(ctx, amount, delAddr1) + err := app.DistrKeeper.FundCommunityPool(ctx, amount, addr[0]) assert.Nil(t, err) - assert.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), keeper.GetFeePool(ctx).CommunityPool) - assert.Empty(t, bk.GetCoins(ctx, delAddr1)) + assert.Equal(t, initPool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...), app.DistrKeeper.GetFeePool(ctx).CommunityPool) + assert.Empty(t, app.BankKeeper.GetAllBalances(ctx, addr[0])) } diff --git a/x/distribution/keeper/msg_server.go b/x/distribution/keeper/msg_server.go new file mode 100644 index 000000000000..bff869ee55d7 --- /dev/null +++ b/x/distribution/keeper/msg_server.go @@ -0,0 +1,145 @@ +package keeper + +import ( + "context" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the distribution MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (k msgServer) SetWithdrawAddress(goCtx context.Context, msg *types.MsgSetWithdrawAddress) (*types.MsgSetWithdrawAddressResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + withdrawAddress, err := sdk.AccAddressFromBech32(msg.WithdrawAddress) + if err != nil { + return nil, err + } + err = k.SetWithdrawAddr(ctx, delegatorAddress, withdrawAddress) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + ) + + return &types.MsgSetWithdrawAddressResponse{}, nil +} + +func (k msgServer) WithdrawDelegatorReward(goCtx context.Context, msg *types.MsgWithdrawDelegatorReward) (*types.MsgWithdrawDelegatorRewardResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return nil, err + } + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + amount, err := k.WithdrawDelegationRewards(ctx, delegatorAddress, valAddr) + if err != nil { + return nil, err + } + + defer func() { + for _, a := range amount { + if a.Amount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "withdraw_reward"}, + float32(a.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", a.Denom)}, + ) + } + } + }() + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + ) + return &types.MsgWithdrawDelegatorRewardResponse{}, nil +} + +func (k msgServer) WithdrawValidatorCommission(goCtx context.Context, msg *types.MsgWithdrawValidatorCommission) (*types.MsgWithdrawValidatorCommissionResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return nil, err + } + amount, err := k.Keeper.WithdrawValidatorCommission(ctx, valAddr) + if err != nil { + return nil, err + } + + defer func() { + for _, a := range amount { + if a.Amount.IsInt64() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "withdraw_commission"}, + float32(a.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", a.Denom)}, + ) + } + } + }() + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress), + ), + ) + + return &types.MsgWithdrawValidatorCommissionResponse{}, nil +} + +func (k msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCommunityPool) (*types.MsgFundCommunityPoolResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + depositer, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + return nil, err + } + if err := k.Keeper.FundCommunityPool(ctx, msg.Amount, depositer); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor), + ), + ) + + return &types.MsgFundCommunityPoolResponse{}, nil +} diff --git a/x/distribution/keeper/params.go b/x/distribution/keeper/params.go index 63d6ecedc8ba..23b4bbeb8118 100644 --- a/x/distribution/keeper/params.go +++ b/x/distribution/keeper/params.go @@ -6,8 +6,8 @@ import ( ) // GetParams returns the total set of distribution parameters. -func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - k.paramSpace.GetParamSet(ctx, ¶ms) +func (k Keeper) GetParams(clientCtx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(clientCtx, ¶ms) return params } diff --git a/x/distribution/keeper/proposal_handler.go b/x/distribution/keeper/proposal_handler.go index f60a3f88154d..d96bfc64908c 100644 --- a/x/distribution/keeper/proposal_handler.go +++ b/x/distribution/keeper/proposal_handler.go @@ -1,25 +1,29 @@ package keeper import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) // HandleCommunityPoolSpendProposal is a handler for executing a passed community spend proposal -func HandleCommunityPoolSpendProposal(ctx sdk.Context, k Keeper, p types.CommunityPoolSpendProposal) error { - if k.blacklistedAddrs[p.Recipient.String()] { - return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is blacklisted from receiving external funds", p.Recipient) +func HandleCommunityPoolSpendProposal(ctx sdk.Context, k Keeper, p *types.CommunityPoolSpendProposal) error { + if k.blockedAddrs[p.Recipient] { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", p.Recipient) + } + + recipient, addrErr := sdk.AccAddressFromBech32(p.Recipient) + if addrErr != nil { + return addrErr } - err := k.DistributeFromFeePool(ctx, p.Amount, p.Recipient) + err := k.DistributeFromFeePool(ctx, p.Amount, recipient) if err != nil { return err } logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("transferred %s from the community pool to recipient %s", p.Amount, p.Recipient)) + logger.Info("transferred from the community pool to recipient", "amount", p.Amount.String(), "recipient", p.Recipient) + return nil } diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 4757ea5ffe3e..71370e24ba51 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -9,38 +9,38 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func NewQuerier(k Keeper) sdk.Querier { +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { switch path[0] { case types.QueryParams: - return queryParams(ctx, path[1:], req, k) + return queryParams(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryValidatorOutstandingRewards: - return queryValidatorOutstandingRewards(ctx, path[1:], req, k) + return queryValidatorOutstandingRewards(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryValidatorCommission: - return queryValidatorCommission(ctx, path[1:], req, k) + return queryValidatorCommission(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryValidatorSlashes: - return queryValidatorSlashes(ctx, path[1:], req, k) + return queryValidatorSlashes(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryDelegationRewards: - return queryDelegationRewards(ctx, path[1:], req, k) + return queryDelegationRewards(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryDelegatorTotalRewards: - return queryDelegatorTotalRewards(ctx, path[1:], req, k) + return queryDelegatorTotalRewards(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryDelegatorValidators: - return queryDelegatorValidators(ctx, path[1:], req, k) + return queryDelegatorValidators(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryWithdrawAddr: - return queryDelegatorWithdrawAddress(ctx, path[1:], req, k) + return queryDelegatorWithdrawAddress(ctx, path[1:], req, k, legacyQuerierCdc) case types.QueryCommunityPool: - return queryCommunityPool(ctx, path[1:], req, k) + return queryCommunityPool(ctx, path[1:], req, k, legacyQuerierCdc) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) @@ -48,10 +48,10 @@ func NewQuerier(k Keeper) sdk.Querier { } } -func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryParams(ctx sdk.Context, _ []string, _ abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { params := k.GetParams(ctx) - res, err := codec.MarshalJSONIndent(k.cdc, params) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -59,19 +59,19 @@ func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper return res, nil } -func queryValidatorOutstandingRewards(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidatorOutstandingRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorOutstandingRewardsParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } rewards := k.GetValidatorOutstandingRewards(ctx, params.ValidatorAddress) - if rewards == nil { - rewards = sdk.DecCoins{} + if rewards.GetRewards() == nil { + rewards.Rewards = sdk.DecCoins{} } - bz, err := codec.MarshalJSONIndent(k.cdc, rewards) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, rewards) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -79,19 +79,19 @@ func queryValidatorOutstandingRewards(ctx sdk.Context, path []string, req abci.R return bz, nil } -func queryValidatorCommission(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidatorCommission(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorCommissionParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } commission := k.GetValidatorAccumulatedCommission(ctx, params.ValidatorAddress) - if commission == nil { - commission = sdk.DecCoins{} + if commission.Commission == nil { + commission.Commission = sdk.DecCoins{} } - bz, err := codec.MarshalJSONIndent(k.cdc, commission) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, commission) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -99,9 +99,9 @@ func queryValidatorCommission(ctx sdk.Context, path []string, req abci.RequestQu return bz, nil } -func queryValidatorSlashes(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidatorSlashes(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorSlashesParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -114,7 +114,7 @@ func queryValidatorSlashes(ctx sdk.Context, path []string, req abci.RequestQuery }, ) - bz, err := codec.MarshalJSONIndent(k.cdc, events) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, events) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -122,14 +122,14 @@ func queryValidatorSlashes(ctx sdk.Context, path []string, req abci.RequestQuery return bz, nil } -func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegationRewardsParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() val := k.stakingKeeper.Validator(ctx, params.ValidatorAddress) @@ -142,13 +142,13 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return nil, types.ErrNoDelegationExists } - endingPeriod := k.incrementValidatorPeriod(ctx, val) - rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + endingPeriod := k.IncrementValidatorPeriod(ctx, val) + rewards := k.CalculateDelegationRewards(ctx, val, del, endingPeriod) if rewards == nil { rewards = sdk.DecCoins{} } - bz, err := codec.MarshalJSONIndent(k.cdc, rewards) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, rewards) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -156,26 +156,27 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return bz, nil } -func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() total := sdk.DecCoins{} + var delRewards []types.DelegationDelegatorReward k.stakingKeeper.IterateDelegations( ctx, params.DelegatorAddress, - func(_ int64, del exported.DelegationI) (stop bool) { + func(_ int64, del stakingtypes.DelegationI) (stop bool) { valAddr := del.GetValidatorAddr() val := k.stakingKeeper.Validator(ctx, valAddr) - endingPeriod := k.incrementValidatorPeriod(ctx, val) - delReward := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + endingPeriod := k.IncrementValidatorPeriod(ctx, val) + delReward := k.CalculateDelegationRewards(ctx, val, del, endingPeriod) delRewards = append(delRewards, types.NewDelegationDelegatorReward(valAddr, delReward)) total = total.Add(delReward...) @@ -193,27 +194,27 @@ func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQue return bz, nil } -func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() var validators []sdk.ValAddress k.stakingKeeper.IterateDelegations( ctx, params.DelegatorAddress, - func(_ int64, del exported.DelegationI) (stop bool) { + func(_ int64, del stakingtypes.DelegationI) (stop bool) { validators = append(validators, del.GetValidatorAddr()) return false }, ) - bz, err := codec.MarshalJSONIndent(k.cdc, validators) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, validators) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -221,18 +222,18 @@ func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery return bz, nil } -func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorWithdrawAddrParams - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, params.DelegatorAddress) - bz, err := codec.MarshalJSONIndent(k.cdc, withdrawAddr) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, withdrawAddr) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -240,13 +241,13 @@ func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.Request return bz, nil } -func queryCommunityPool(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryCommunityPool(ctx sdk.Context, _ []string, _ abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { pool := k.GetFeePoolCommunityCoins(ctx) if pool == nil { pool = sdk.DecCoins{} } - bz, err := k.cdc.MarshalJSON(pool) + bz, err := legacyQuerierCdc.MarshalJSON(pool) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } diff --git a/x/distribution/keeper/querier_test.go b/x/distribution/keeper/querier_test.go index b5efd15a3a62..9a3da854ab5d 100644 --- a/x/distribution/keeper/querier_test.go +++ b/x/distribution/keeper/querier_test.go @@ -1,23 +1,27 @@ -package keeper +package keeper_test import ( "strings" "testing" "github.com/stretchr/testify/require" - 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" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const custom = "custom" -func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) types.Params { +func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier) types.Params { var params types.Params bz, err := querier(ctx, []string{types.QueryParams}, abci.RequestQuery{}) @@ -27,7 +31,7 @@ func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier s return params } -func getQueriedValidatorOutstandingRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validatorAddr sdk.ValAddress) (outstandingRewards sdk.DecCoins) { +func getQueriedValidatorOutstandingRewards(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, validatorAddr sdk.ValAddress) sdk.DecCoins { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryValidatorOutstandingRewards}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryValidatorOutstandingRewardsParams(validatorAddr)), @@ -35,12 +39,13 @@ func getQueriedValidatorOutstandingRewards(t *testing.T, ctx sdk.Context, cdc *c bz, err := querier(ctx, []string{types.QueryValidatorOutstandingRewards}, query) require.Nil(t, err) + outstandingRewards := types.ValidatorOutstandingRewards{} require.Nil(t, cdc.UnmarshalJSON(bz, &outstandingRewards)) - return + return outstandingRewards.GetRewards() } -func getQueriedValidatorCommission(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validatorAddr sdk.ValAddress) (validatorCommission sdk.DecCoins) { +func getQueriedValidatorCommission(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, validatorAddr sdk.ValAddress) sdk.DecCoins { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryValidatorCommission}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryValidatorCommissionParams(validatorAddr)), @@ -48,12 +53,13 @@ func getQueriedValidatorCommission(t *testing.T, ctx sdk.Context, cdc *codec.Cod bz, err := querier(ctx, []string{types.QueryValidatorCommission}, query) require.Nil(t, err) + validatorCommission := types.ValidatorAccumulatedCommission{} require.Nil(t, cdc.UnmarshalJSON(bz, &validatorCommission)) - return + return validatorCommission.GetCommission() } -func getQueriedValidatorSlashes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, validatorAddr sdk.ValAddress, startHeight uint64, endHeight uint64) (slashes []types.ValidatorSlashEvent) { +func getQueriedValidatorSlashes(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, validatorAddr sdk.ValAddress, startHeight uint64, endHeight uint64) (slashes []types.ValidatorSlashEvent) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryValidatorSlashes}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryValidatorSlashesParams(validatorAddr, startHeight, endHeight)), @@ -66,7 +72,7 @@ func getQueriedValidatorSlashes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, return } -func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) (rewards sdk.DecCoins) { +func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) (rewards sdk.DecCoins) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDelegationRewards}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), @@ -79,7 +85,7 @@ func getQueriedDelegationRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec return } -func getQueriedDelegatorTotalRewards(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, delegatorAddr sdk.AccAddress) (response types.QueryDelegatorTotalRewardsResponse) { +func getQueriedDelegatorTotalRewards(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, delegatorAddr sdk.AccAddress) (response types.QueryDelegatorTotalRewardsResponse) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDelegatorTotalRewards}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)), @@ -92,7 +98,7 @@ func getQueriedDelegatorTotalRewards(t *testing.T, ctx sdk.Context, cdc *codec.C return } -func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (ptr []byte) { +func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier) (ptr []byte) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryCommunityPool}, ""), Data: []byte{}, @@ -106,11 +112,18 @@ func getQueriedCommunityPool(t *testing.T, ctx sdk.Context, cdc *codec.Codec, qu } func TestQueries(t *testing.T) { - cdc := codec.New() - types.RegisterCodec(cdc) - supply.RegisterCodec(cdc) - ctx, _, keeper, sk, _ := CreateTestInputDefault(t, false, 100) - querier := NewQuerier(keeper) + cdc := codec.NewLegacyAmino() + types.RegisterLegacyAminoCodec(cdc) + banktypes.RegisterLegacyAminoCodec(cdc) + + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addr := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(1000000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addr) + valOpAddr1 := valAddrs[0] + + querier := keeper.NewQuerier(app.DistrKeeper, cdc) // test param queries params := types.Params{ @@ -120,7 +133,7 @@ func TestQueries(t *testing.T) { WithdrawAddrEnabled: true, } - keeper.SetParams(ctx, params) + app.DistrKeeper.SetParams(ctx, params) paramsRes := getQueriedParams(t, ctx, cdc, querier) require.Equal(t, params.CommunityTax, paramsRes.CommunityTax) @@ -130,13 +143,13 @@ func TestQueries(t *testing.T) { // test outstanding rewards query outstandingRewards := sdk.DecCoins{{Denom: "mytoken", Amount: sdk.NewDec(3)}, {Denom: "myothertoken", Amount: sdk.NewDecWithPrec(3, 7)}} - keeper.SetValidatorOutstandingRewards(ctx, valOpAddr1, outstandingRewards) + app.DistrKeeper.SetValidatorOutstandingRewards(ctx, valOpAddr1, types.ValidatorOutstandingRewards{Rewards: outstandingRewards}) retOutstandingRewards := getQueriedValidatorOutstandingRewards(t, ctx, cdc, querier, valOpAddr1) require.Equal(t, outstandingRewards, retOutstandingRewards) // test validator commission query commission := sdk.DecCoins{{Denom: "token1", Amount: sdk.NewDec(4)}, {Denom: "token2", Amount: sdk.NewDec(2)}} - keeper.SetValidatorAccumulatedCommission(ctx, valOpAddr1, commission) + app.DistrKeeper.SetValidatorAccumulatedCommission(ctx, valOpAddr1, types.ValidatorAccumulatedCommission{Commission: commission}) retCommission := getQueriedValidatorCommission(t, ctx, cdc, querier, valOpAddr1) require.Equal(t, commission, retCommission) @@ -147,8 +160,8 @@ func TestQueries(t *testing.T) { // test validator slashes query with height range slashOne := types.NewValidatorSlashEvent(3, sdk.NewDecWithPrec(5, 1)) slashTwo := types.NewValidatorSlashEvent(7, sdk.NewDecWithPrec(6, 1)) - keeper.SetValidatorSlashEvent(ctx, valOpAddr1, 3, 0, slashOne) - keeper.SetValidatorSlashEvent(ctx, valOpAddr1, 7, 0, slashTwo) + app.DistrKeeper.SetValidatorSlashEvent(ctx, valOpAddr1, 3, 0, slashOne) + app.DistrKeeper.SetValidatorSlashEvent(ctx, valOpAddr1, 7, 0, slashTwo) slashes := getQueriedValidatorSlashes(t, ctx, cdc, querier, valOpAddr1, 0, 2) require.Equal(t, 0, len(slashes)) slashes = getQueriedValidatorSlashes(t, ctx, cdc, querier, valOpAddr1, 0, 5) @@ -157,25 +170,19 @@ func TestQueries(t *testing.T) { require.Equal(t, []types.ValidatorSlashEvent{slashOne, slashTwo}, slashes) // test delegation rewards query - sh := staking.NewHandler(sk) - comm := staking.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) - msg := staking.NewMsgCreateValidator( - valOpAddr1, valConsPk1, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, comm, sdk.OneInt(), - ) - - res, err := sh(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + tstaking.CreateValidator(valOpAddr1, valConsPk1, sdk.NewInt(100), true) - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) - val := sk.Validator(ctx, valOpAddr1) + val := app.StakingKeeper.Validator(ctx, valOpAddr1) rewards := getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1) require.True(t, rewards.IsZero()) initial := int64(10) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial)}} - keeper.AllocateTokensToValidator(ctx, val, tokens) + app.DistrKeeper.AllocateTokensToValidator(ctx, val, tokens) rewards = getQueriedDelegationRewards(t, ctx, cdc, querier, sdk.AccAddress(valOpAddr1), valOpAddr1) require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(initial / 2)}}, rewards) diff --git a/x/distribution/keeper/store.go b/x/distribution/keeper/store.go index e1a1c50d4343..302e2ee81e92 100644 --- a/x/distribution/keeper/store.go +++ b/x/distribution/keeper/store.go @@ -1,6 +1,8 @@ package keeper import ( + gogotypes "github.com/gogo/protobuf/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -48,47 +50,50 @@ func (k Keeper) GetFeePool(ctx sdk.Context) (feePool types.FeePool) { if b == nil { panic("Stored fee pool should not have been nil") } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &feePool) + k.cdc.MustUnmarshalBinaryBare(b, &feePool) return } // set the global fee pool distribution info func (k Keeper) SetFeePool(ctx sdk.Context, feePool types.FeePool) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(feePool) + b := k.cdc.MustMarshalBinaryBare(&feePool) store.Set(types.FeePoolKey, b) } -// get the proposer public key for this block -func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) (consAddr sdk.ConsAddress) { +// GetPreviousProposerConsAddr returns the proposer consensus address for the +// current block. +func (k Keeper) GetPreviousProposerConsAddr(ctx sdk.Context) sdk.ConsAddress { store := ctx.KVStore(k.storeKey) - b := store.Get(types.ProposerKey) - if b == nil { - panic("Previous proposer not set") + bz := store.Get(types.ProposerKey) + if bz == nil { + panic("previous proposer not set") } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &consAddr) - return + + addrValue := gogotypes.BytesValue{} + k.cdc.MustUnmarshalBinaryBare(bz, &addrValue) + return addrValue.GetValue() } // set the proposer public key for this block func (k Keeper) SetPreviousProposerConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(consAddr) - store.Set(types.ProposerKey, b) + bz := k.cdc.MustMarshalBinaryBare(&gogotypes.BytesValue{Value: consAddr}) + store.Set(types.ProposerKey, bz) } // get the starting info associated with a delegator func (k Keeper) GetDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress) (period types.DelegatorStartingInfo) { store := ctx.KVStore(k.storeKey) b := store.Get(types.GetDelegatorStartingInfoKey(val, del)) - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &period) + k.cdc.MustUnmarshalBinaryBare(b, &period) return } // set the starting info associated with a delegator func (k Keeper) SetDelegatorStartingInfo(ctx sdk.Context, val sdk.ValAddress, del sdk.AccAddress, period types.DelegatorStartingInfo) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(period) + b := k.cdc.MustMarshalBinaryBare(&period) store.Set(types.GetDelegatorStartingInfoKey(val, del), b) } @@ -111,7 +116,7 @@ func (k Keeper) IterateDelegatorStartingInfos(ctx sdk.Context, handler func(val defer iter.Close() for ; iter.Valid(); iter.Next() { var info types.DelegatorStartingInfo - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &info) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &info) val, del := types.GetDelegatorStartingInfoAddresses(iter.Key()) if handler(val, del, info) { break @@ -123,14 +128,14 @@ func (k Keeper) IterateDelegatorStartingInfos(ctx sdk.Context, handler func(val func (k Keeper) GetValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAddress, period uint64) (rewards types.ValidatorHistoricalRewards) { store := ctx.KVStore(k.storeKey) b := store.Get(types.GetValidatorHistoricalRewardsKey(val, period)) - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &rewards) + k.cdc.MustUnmarshalBinaryBare(b, &rewards) return } // set historical rewards for a particular period func (k Keeper) SetValidatorHistoricalRewards(ctx sdk.Context, val sdk.ValAddress, period uint64, rewards types.ValidatorHistoricalRewards) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(rewards) + b := k.cdc.MustMarshalBinaryBare(&rewards) store.Set(types.GetValidatorHistoricalRewardsKey(val, period), b) } @@ -141,7 +146,7 @@ func (k Keeper) IterateValidatorHistoricalRewards(ctx sdk.Context, handler func( defer iter.Close() for ; iter.Valid(); iter.Next() { var rewards types.ValidatorHistoricalRewards - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &rewards) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &rewards) addr, period := types.GetValidatorHistoricalRewardsAddressPeriod(iter.Key()) if handler(addr, period, rewards) { break @@ -182,7 +187,7 @@ func (k Keeper) GetValidatorHistoricalReferenceCount(ctx sdk.Context) (count uin defer iter.Close() for ; iter.Valid(); iter.Next() { var rewards types.ValidatorHistoricalRewards - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &rewards) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &rewards) count += uint64(rewards.ReferenceCount) } return @@ -192,14 +197,14 @@ func (k Keeper) GetValidatorHistoricalReferenceCount(ctx sdk.Context) (count uin func (k Keeper) GetValidatorCurrentRewards(ctx sdk.Context, val sdk.ValAddress) (rewards types.ValidatorCurrentRewards) { store := ctx.KVStore(k.storeKey) b := store.Get(types.GetValidatorCurrentRewardsKey(val)) - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &rewards) + k.cdc.MustUnmarshalBinaryBare(b, &rewards) return } // set current rewards for a validator func (k Keeper) SetValidatorCurrentRewards(ctx sdk.Context, val sdk.ValAddress, rewards types.ValidatorCurrentRewards) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(rewards) + b := k.cdc.MustMarshalBinaryBare(&rewards) store.Set(types.GetValidatorCurrentRewardsKey(val), b) } @@ -216,7 +221,7 @@ func (k Keeper) IterateValidatorCurrentRewards(ctx sdk.Context, handler func(val defer iter.Close() for ; iter.Valid(); iter.Next() { var rewards types.ValidatorCurrentRewards - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &rewards) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &rewards) addr := types.GetValidatorCurrentRewardsAddress(iter.Key()) if handler(addr, rewards) { break @@ -231,7 +236,7 @@ func (k Keeper) GetValidatorAccumulatedCommission(ctx sdk.Context, val sdk.ValAd if b == nil { return types.ValidatorAccumulatedCommission{} } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &commission) + k.cdc.MustUnmarshalBinaryBare(b, &commission) return } @@ -240,10 +245,10 @@ func (k Keeper) SetValidatorAccumulatedCommission(ctx sdk.Context, val sdk.ValAd var bz []byte store := ctx.KVStore(k.storeKey) - if commission.IsZero() { - bz = k.cdc.MustMarshalBinaryLengthPrefixed(types.InitialValidatorAccumulatedCommission()) + if commission.Commission.IsZero() { + bz = k.cdc.MustMarshalBinaryBare(&types.ValidatorAccumulatedCommission{}) } else { - bz = k.cdc.MustMarshalBinaryLengthPrefixed(commission) + bz = k.cdc.MustMarshalBinaryBare(&commission) } store.Set(types.GetValidatorAccumulatedCommissionKey(val), bz) @@ -262,7 +267,7 @@ func (k Keeper) IterateValidatorAccumulatedCommissions(ctx sdk.Context, handler defer iter.Close() for ; iter.Valid(); iter.Next() { var commission types.ValidatorAccumulatedCommission - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &commission) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &commission) addr := types.GetValidatorAccumulatedCommissionAddress(iter.Key()) if handler(addr, commission) { break @@ -273,15 +278,15 @@ func (k Keeper) IterateValidatorAccumulatedCommissions(ctx sdk.Context, handler // get validator outstanding rewards func (k Keeper) GetValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress) (rewards types.ValidatorOutstandingRewards) { store := ctx.KVStore(k.storeKey) - b := store.Get(types.GetValidatorOutstandingRewardsKey(val)) - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &rewards) + bz := store.Get(types.GetValidatorOutstandingRewardsKey(val)) + k.cdc.MustUnmarshalBinaryBare(bz, &rewards) return } // set validator outstanding rewards func (k Keeper) SetValidatorOutstandingRewards(ctx sdk.Context, val sdk.ValAddress, rewards types.ValidatorOutstandingRewards) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(rewards) + b := k.cdc.MustMarshalBinaryBare(&rewards) store.Set(types.GetValidatorOutstandingRewardsKey(val), b) } @@ -297,8 +302,8 @@ func (k Keeper) IterateValidatorOutstandingRewards(ctx sdk.Context, handler func iter := sdk.KVStorePrefixIterator(store, types.ValidatorOutstandingRewardsPrefix) defer iter.Close() for ; iter.Valid(); iter.Next() { - var rewards types.ValidatorOutstandingRewards - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &rewards) + rewards := types.ValidatorOutstandingRewards{} + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &rewards) addr := types.GetValidatorOutstandingRewardsAddress(iter.Key()) if handler(addr, rewards) { break @@ -313,14 +318,14 @@ func (k Keeper) GetValidatorSlashEvent(ctx sdk.Context, val sdk.ValAddress, heig if b == nil { return types.ValidatorSlashEvent{}, false } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &event) + k.cdc.MustUnmarshalBinaryBare(b, &event) return event, true } // set slash event for height func (k Keeper) SetValidatorSlashEvent(ctx sdk.Context, val sdk.ValAddress, height, period uint64, event types.ValidatorSlashEvent) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(event) + b := k.cdc.MustMarshalBinaryBare(&event) store.Set(types.GetValidatorSlashEventKey(val, height, period), b) } @@ -335,7 +340,7 @@ func (k Keeper) IterateValidatorSlashEventsBetween(ctx sdk.Context, val sdk.ValA defer iter.Close() for ; iter.Valid(); iter.Next() { var event types.ValidatorSlashEvent - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &event) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &event) _, height := types.GetValidatorSlashEventAddressHeight(iter.Key()) if handler(height, event) { break @@ -350,7 +355,7 @@ func (k Keeper) IterateValidatorSlashEvents(ctx sdk.Context, handler func(val sd defer iter.Close() for ; iter.Valid(); iter.Next() { var event types.ValidatorSlashEvent - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &event) + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &event) val, height := types.GetValidatorSlashEventAddressHeight(iter.Key()) if handler(val, height, event) { break diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go deleted file mode 100644 index 78425e47c11d..000000000000 --- a/x/distribution/keeper/test_common.go +++ /dev/null @@ -1,169 +0,0 @@ -package keeper - -import ( - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" - - "github.com/cosmos/cosmos-sdk/x/distribution/types" -) - -//nolint:deadcode,unused -var ( - delPk1 = ed25519.GenPrivKey().PubKey() - delPk2 = ed25519.GenPrivKey().PubKey() - delPk3 = ed25519.GenPrivKey().PubKey() - delAddr1 = sdk.AccAddress(delPk1.Address()) - delAddr2 = sdk.AccAddress(delPk2.Address()) - delAddr3 = sdk.AccAddress(delPk3.Address()) - - valOpPk1 = ed25519.GenPrivKey().PubKey() - valOpPk2 = ed25519.GenPrivKey().PubKey() - valOpPk3 = ed25519.GenPrivKey().PubKey() - valOpAddr1 = sdk.ValAddress(valOpPk1.Address()) - valOpAddr2 = sdk.ValAddress(valOpPk2.Address()) - valOpAddr3 = sdk.ValAddress(valOpPk3.Address()) - valAccAddr1 = sdk.AccAddress(valOpPk1.Address()) // generate acc addresses for these validator keys too - valAccAddr2 = sdk.AccAddress(valOpPk2.Address()) - valAccAddr3 = sdk.AccAddress(valOpPk3.Address()) - - valConsPk1 = ed25519.GenPrivKey().PubKey() - valConsPk2 = ed25519.GenPrivKey().PubKey() - valConsPk3 = ed25519.GenPrivKey().PubKey() - valConsAddr1 = sdk.ConsAddress(valConsPk1.Address()) - valConsAddr2 = sdk.ConsAddress(valConsPk2.Address()) - - // TODO move to common testing package for all modules - // test addresses - TestAddrs = []sdk.AccAddress{ - delAddr1, delAddr2, delAddr3, - valAccAddr1, valAccAddr2, valAccAddr3, - } - - distrAcc = supply.NewEmptyModuleAccount(types.ModuleName) -) - -// create a codec used only for testing -func MakeTestCodec() *codec.Codec { - var cdc = codec.New() - bank.RegisterCodec(cdc) - staking.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - supply.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - types.RegisterCodec(cdc) // distr - return cdc -} - -// test input with default values -func CreateTestInputDefault(t *testing.T, isCheckTx bool, initPower int64) ( - sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, types.SupplyKeeper) { - - communityTax := sdk.NewDecWithPrec(2, 2) - - ctx, ak, _, dk, sk, _, supplyKeeper := CreateTestInputAdvanced(t, isCheckTx, initPower, communityTax) - return ctx, ak, dk, sk, supplyKeeper -} - -// hogpodge of all sorts of input required for testing -func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64, - communityTax sdk.Dec) (sdk.Context, auth.AccountKeeper, bank.Keeper, - Keeper, staking.Keeper, params.Keeper, types.SupplyKeeper) { - - initTokens := sdk.TokensFromConsensusPower(initPower) - - keyDistr := sdk.NewKVStoreKey(types.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - - ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - - err := ms.LoadLatestVersion() - require.Nil(t, err) - - feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - blacklistedAddrs[distrAcc.GetAddress().String()] = true - - cdc := MakeTestCodec() - pk := params.NewKeeper(cdc, keyParams, tkeyParams) - - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger()) - accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), blacklistedAddrs) - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - types.ModuleName: nil, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) - - sk := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace)) - sk.SetParams(ctx, staking.DefaultParams()) - - keeper := NewKeeper(cdc, keyDistr, pk.Subspace(types.DefaultParamspace), sk, supplyKeeper, auth.FeeCollectorName, blacklistedAddrs) - - initCoins := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens)) - totalSupply := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens.MulRaw(int64(len(TestAddrs))))) - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - // fill all the addresses with some coins, set the loose pool tokens simultaneously - for _, addr := range TestAddrs { - _, err := bankKeeper.AddCoins(ctx, addr, initCoins) - require.Nil(t, err) - } - - // set module accounts - keeper.supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) - keeper.supplyKeeper.SetModuleAccount(ctx, bondPool) - keeper.supplyKeeper.SetModuleAccount(ctx, distrAcc) - - // set the distribution hooks on staking - sk.SetHooks(keeper.Hooks()) - - // set genesis items required for distribution - keeper.SetFeePool(ctx, types.InitialFeePool()) - - params := types.DefaultParams() - params.CommunityTax = communityTax - params.BaseProposerReward = sdk.NewDecWithPrec(1, 2) - params.BonusProposerReward = sdk.NewDecWithPrec(4, 2) - keeper.SetParams(ctx, params) - - return ctx, accountKeeper, bankKeeper, keeper, sk, pk, supplyKeeper -} diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index ccedb7ede163..3d1953c3b5fa 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -6,11 +6,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // initialize rewards for a new validator -func (k Keeper) initializeValidator(ctx sdk.Context, val exported.ValidatorI) { +func (k Keeper) initializeValidator(ctx sdk.Context, val stakingtypes.ValidatorI) { // set initial historical rewards (period 0) with reference count of 1 k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), 0, types.NewValidatorHistoricalRewards(sdk.DecCoins{}, 1)) @@ -21,11 +21,11 @@ func (k Keeper) initializeValidator(ctx sdk.Context, val exported.ValidatorI) { k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), types.InitialValidatorAccumulatedCommission()) // set outstanding rewards - k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), sdk.DecCoins{}) + k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), types.ValidatorOutstandingRewards{Rewards: sdk.DecCoins{}}) } // increment validator period, returning the period just ended -func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val exported.ValidatorI) uint64 { +func (k Keeper) IncrementValidatorPeriod(ctx sdk.Context, val stakingtypes.ValidatorI) uint64 { // fetch current rewards rewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator()) @@ -38,7 +38,7 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val exported.Validator feePool := k.GetFeePool(ctx) outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) feePool.CommunityPool = feePool.CommunityPool.Add(rewards.Rewards...) - outstanding = outstanding.Sub(rewards.Rewards) + outstanding.Rewards = outstanding.GetRewards().Sub(rewards.Rewards) k.SetFeePool(ctx, feePool) k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding) @@ -95,7 +95,7 @@ func (k Keeper) updateValidatorSlashFraction(ctx sdk.Context, valAddr sdk.ValAdd val := k.stakingKeeper.Validator(ctx, valAddr) // increment current period - newPeriod := k.incrementValidatorPeriod(ctx, val) + newPeriod := k.IncrementValidatorPeriod(ctx, val) // increment reference count on period we need to track k.incrementReferenceCount(ctx, valAddr, newPeriod) diff --git a/x/distribution/legacy/v034/types.go b/x/distribution/legacy/v034/types.go new file mode 100644 index 000000000000..3ef672f4af3b --- /dev/null +++ b/x/distribution/legacy/v034/types.go @@ -0,0 +1,98 @@ +// DONTCOVER +// nolint +package v034 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ---------------------------------------------------------------------------- +// Types and Constants +// ---------------------------------------------------------------------------- + +const ( + ModuleName = "distr" +) + +type ( + ValidatorAccumulatedCommission = sdk.DecCoins + + DelegatorStartingInfo struct { + PreviousPeriod uint64 `json:"previous_period"` + Stake sdk.Dec `json:"stake"` + Height uint64 `json:"height"` + } + + DelegatorWithdrawInfo struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + WithdrawAddress sdk.AccAddress `json:"withdraw_address"` + } + + ValidatorOutstandingRewardsRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + OutstandingRewards sdk.DecCoins `json:"outstanding_rewards"` + } + + ValidatorAccumulatedCommissionRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Accumulated ValidatorAccumulatedCommission `json:"accumulated"` + } + + ValidatorHistoricalRewardsRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Period uint64 `json:"period"` + Rewards ValidatorHistoricalRewards `json:"rewards"` + } + + ValidatorHistoricalRewards struct { + CumulativeRewardRatio sdk.DecCoins `json:"cumulative_reward_ratio"` + ReferenceCount uint16 `json:"reference_count"` + } + + ValidatorCurrentRewards struct { + Rewards sdk.DecCoins `json:"rewards"` + Period uint64 `json:"period"` + } + + ValidatorCurrentRewardsRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Rewards ValidatorCurrentRewards `json:"rewards"` + } + + DelegatorStartingInfoRecord struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + StartingInfo DelegatorStartingInfo `json:"starting_info"` + } + + ValidatorSlashEventRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Height uint64 `json:"height"` + Event ValidatorSlashEvent `json:"validator_slash_event"` + } + + FeePool struct { + CommunityPool sdk.DecCoins `json:"community_pool"` + } + + ValidatorSlashEvent struct { + ValidatorPeriod uint64 `json:"validator_period"` + Fraction sdk.Dec `json:"fraction"` + } + + GenesisState struct { + FeePool FeePool `json:"fee_pool"` + CommunityTax sdk.Dec `json:"community_tax"` + BaseProposerReward sdk.Dec `json:"base_proposer_reward"` + BonusProposerReward sdk.Dec `json:"bonus_proposer_reward"` + WithdrawAddrEnabled bool `json:"withdraw_addr_enabled"` + DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` + PreviousProposer sdk.ConsAddress `json:"previous_proposer"` + OutstandingRewards []ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` + ValidatorAccumulatedCommissions []ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` + ValidatorHistoricalRewards []ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` + ValidatorCurrentRewards []ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` + DelegatorStartingInfos []DelegatorStartingInfoRecord `json:"delegator_starting_infos"` + ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events"` + } +) diff --git a/x/distribution/legacy/v036/migrate.go b/x/distribution/legacy/v036/migrate.go new file mode 100644 index 000000000000..7cbe3b99cbb9 --- /dev/null +++ b/x/distribution/legacy/v036/migrate.go @@ -0,0 +1,30 @@ +package v036 + +import ( + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. All entries are identical except for validator slashing events +// which now include the period. +func Migrate(oldGenState v034distr.GenesisState) GenesisState { + // migrate slash events which now have the period included + slashEvents := make([]ValidatorSlashEventRecord, len(oldGenState.ValidatorSlashEvents)) + for i, se := range oldGenState.ValidatorSlashEvents { + slashEvents[i] = ValidatorSlashEventRecord{ + ValidatorAddress: se.ValidatorAddress, + Height: se.Height, + Period: se.Event.ValidatorPeriod, + Event: se.Event, + } + } + + return NewGenesisState( + oldGenState.FeePool, oldGenState.CommunityTax, oldGenState.BaseProposerReward, + oldGenState.BonusProposerReward, oldGenState.WithdrawAddrEnabled, + oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer, + oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions, + oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards, + oldGenState.DelegatorStartingInfos, slashEvents, + ) +} diff --git a/x/distribution/legacy/v036/migrate_test.go b/x/distribution/legacy/v036/migrate_test.go new file mode 100644 index 000000000000..d0cd69e5e50d --- /dev/null +++ b/x/distribution/legacy/v036/migrate_test.go @@ -0,0 +1,64 @@ +package v036 + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/types" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + + "github.com/stretchr/testify/require" +) + +var ( + priv = secp256k1.GenPrivKey() + addr = types.AccAddress(priv.PubKey().Address()) + valAddr, _ = types.ValAddressFromBech32(addr.String()) + + event = v034distr.ValidatorSlashEvent{ + ValidatorPeriod: 1, + Fraction: types.Dec{}, + } +) + +func TestMigrate(t *testing.T) { + var genesisState GenesisState + require.NotPanics(t, func() { + genesisState = Migrate(v034distr.GenesisState{ + ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{ + { + ValidatorAddress: valAddr, + Height: 1, + Event: event, + }, + }, + }) + }) + + require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{ + ValidatorAddress: valAddr, + Height: 1, + Period: event.ValidatorPeriod, + Event: event, + }) +} + +func TestMigrateEmptyRecord(t *testing.T) { + var genesisState GenesisState + + require.NotPanics(t, func() { + genesisState = Migrate(v034distr.GenesisState{ + ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{{}}, + }) + }) + + require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{ + ValidatorAddress: valAddr, + Height: 0, + Period: 0, + Event: v034distr.ValidatorSlashEvent{ + ValidatorPeriod: 0, + Fraction: types.Dec{}, + }, + }) +} diff --git a/x/distribution/legacy/v036/types.go b/x/distribution/legacy/v036/types.go new file mode 100644 index 000000000000..1c43eb5acd2a --- /dev/null +++ b/x/distribution/legacy/v036/types.go @@ -0,0 +1,134 @@ +// DONTCOVER +// nolint +package v036 + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" +) + +// ---------------------------------------------------------------------------- +// Types and Constants +// ---------------------------------------------------------------------------- + +const ( + ModuleName = "distribution" + + // RouterKey is the message route for distribution + RouterKey = ModuleName + + // ProposalTypeCommunityPoolSpend defines the type for a CommunityPoolSpendProposal + ProposalTypeCommunityPoolSpend = "CommunityPoolSpend" +) + +type ( + ValidatorAccumulatedCommission = sdk.DecCoins + + ValidatorSlashEventRecord struct { + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Height uint64 `json:"height"` + Period uint64 `json:"period"` + Event v034distr.ValidatorSlashEvent `json:"validator_slash_event"` + } + + GenesisState struct { + FeePool v034distr.FeePool `json:"fee_pool"` + CommunityTax sdk.Dec `json:"community_tax"` + BaseProposerReward sdk.Dec `json:"base_proposer_reward"` + BonusProposerReward sdk.Dec `json:"bonus_proposer_reward"` + WithdrawAddrEnabled bool `json:"withdraw_addr_enabled"` + DelegatorWithdrawInfos []v034distr.DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` + PreviousProposer sdk.ConsAddress `json:"previous_proposer"` + OutstandingRewards []v034distr.ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` + ValidatorAccumulatedCommissions []v034distr.ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` + ValidatorHistoricalRewards []v034distr.ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` + ValidatorCurrentRewards []v034distr.ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` + DelegatorStartingInfos []v034distr.DelegatorStartingInfoRecord `json:"delegator_starting_infos"` + ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events"` + } + + // CommunityPoolSpendProposal spends from the community pool + CommunityPoolSpendProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + } +) + +func NewGenesisState( + feePool v034distr.FeePool, communityTax, baseProposerReward, bonusProposerReward sdk.Dec, + withdrawAddrEnabled bool, dwis []v034distr.DelegatorWithdrawInfo, pp sdk.ConsAddress, + r []v034distr.ValidatorOutstandingRewardsRecord, acc []v034distr.ValidatorAccumulatedCommissionRecord, + historical []v034distr.ValidatorHistoricalRewardsRecord, cur []v034distr.ValidatorCurrentRewardsRecord, + dels []v034distr.DelegatorStartingInfoRecord, slashes []ValidatorSlashEventRecord, +) GenesisState { + + return GenesisState{ + FeePool: feePool, + CommunityTax: communityTax, + BaseProposerReward: baseProposerReward, + BonusProposerReward: bonusProposerReward, + WithdrawAddrEnabled: withdrawAddrEnabled, + DelegatorWithdrawInfos: dwis, + PreviousProposer: pp, + OutstandingRewards: r, + ValidatorAccumulatedCommissions: acc, + ValidatorHistoricalRewards: historical, + ValidatorCurrentRewards: cur, + DelegatorStartingInfos: dels, + ValidatorSlashEvents: slashes, + } +} + +var _ v036gov.Content = CommunityPoolSpendProposal{} + +// GetTitle returns the title of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) GetTitle() string { return csp.Title } + +// GetDescription returns the description of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) GetDescription() string { return csp.Description } + +// GetDescription returns the routing key of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) ProposalType() string { return ProposalTypeCommunityPoolSpend } + +// ValidateBasic runs basic stateless validity checks +func (csp CommunityPoolSpendProposal) ValidateBasic() error { + err := v036gov.ValidateAbstract(csp) + if err != nil { + return err + } + if !csp.Amount.IsValid() { + return types.ErrInvalidProposalAmount + } + if csp.Recipient.Empty() { + return types.ErrEmptyProposalRecipient + } + + return nil +} + +// String implements the Stringer interface. +func (csp CommunityPoolSpendProposal) String() string { + var b strings.Builder + b.WriteString(fmt.Sprintf(`Community Pool Spend Proposal: + Title: %s + Description: %s + Recipient: %s + Amount: %s +`, csp.Title, csp.Description, csp.Recipient, csp.Amount)) + return b.String() +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil) +} diff --git a/x/distribution/legacy/v038/migrate.go b/x/distribution/legacy/v038/migrate.go new file mode 100644 index 000000000000..cd00668b1efd --- /dev/null +++ b/x/distribution/legacy/v038/migrate.go @@ -0,0 +1,27 @@ +package v038 + +// DONTCOVER +// nolint + +import ( + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" +) + +// Migrate accepts exported genesis state from v0.36 or v0.37 and migrates it to +// v0.38 genesis state. All entries are identical except for parameters. +func Migrate(oldGenState v036distr.GenesisState) GenesisState { + params := Params{ + CommunityTax: oldGenState.CommunityTax, + BaseProposerReward: oldGenState.BaseProposerReward, + BonusProposerReward: oldGenState.BonusProposerReward, + WithdrawAddrEnabled: oldGenState.WithdrawAddrEnabled, + } + + return NewGenesisState( + params, oldGenState.FeePool, + oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer, + oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions, + oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards, + oldGenState.DelegatorStartingInfos, oldGenState.ValidatorSlashEvents, + ) +} diff --git a/x/distribution/legacy/v038/types.go b/x/distribution/legacy/v038/types.go new file mode 100644 index 000000000000..af7146418dbc --- /dev/null +++ b/x/distribution/legacy/v038/types.go @@ -0,0 +1,57 @@ +package v038 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" +) + +// DONTCOVER +// nolint + +const ( + ModuleName = "distribution" +) + +type ( + GenesisState struct { + Params Params `json:"params" yaml:"params"` + FeePool v034distr.FeePool `json:"fee_pool"` + DelegatorWithdrawInfos []v034distr.DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` + PreviousProposer sdk.ConsAddress `json:"previous_proposer" yaml:"previous_proposer"` + OutstandingRewards []v034distr.ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` + ValidatorAccumulatedCommissions []v034distr.ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` + ValidatorHistoricalRewards []v034distr.ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` + ValidatorCurrentRewards []v034distr.ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` + DelegatorStartingInfos []v034distr.DelegatorStartingInfoRecord `json:"delegator_starting_infos"` + ValidatorSlashEvents []v036distr.ValidatorSlashEventRecord `json:"validator_slash_events" yaml:"validator_slash_events"` + } + + Params struct { + CommunityTax sdk.Dec `json:"community_tax" yaml:"community_tax"` + BaseProposerReward sdk.Dec `json:"base_proposer_reward" yaml:"base_proposer_reward"` + BonusProposerReward sdk.Dec `json:"bonus_proposer_reward" yaml:"bonus_proposer_reward"` + WithdrawAddrEnabled bool `json:"withdraw_addr_enabled" yaml:"withdraw_addr_enabled"` + } +) + +func NewGenesisState( + params Params, feePool v034distr.FeePool, dwis []v034distr.DelegatorWithdrawInfo, pp sdk.ConsAddress, + r []v034distr.ValidatorOutstandingRewardsRecord, acc []v034distr.ValidatorAccumulatedCommissionRecord, + historical []v034distr.ValidatorHistoricalRewardsRecord, cur []v034distr.ValidatorCurrentRewardsRecord, + dels []v034distr.DelegatorStartingInfoRecord, slashes []v036distr.ValidatorSlashEventRecord, +) GenesisState { + + return GenesisState{ + FeePool: feePool, + Params: params, + DelegatorWithdrawInfos: dwis, + PreviousProposer: pp, + OutstandingRewards: r, + ValidatorAccumulatedCommissions: acc, + ValidatorHistoricalRewards: historical, + ValidatorCurrentRewards: cur, + DelegatorStartingInfos: dels, + ValidatorSlashEvents: slashes, + } +} diff --git a/x/distribution/legacy/v040/migrate.go b/x/distribution/legacy/v040/migrate.go new file mode 100644 index 000000000000..b0ba30bba861 --- /dev/null +++ b/x/distribution/legacy/v040/migrate.go @@ -0,0 +1,108 @@ +package v040 + +import ( + v038distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" + v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// Migrate accepts exported x/distribution genesis state from v0.38 and migrates it +// to v0.40 x/distribution genesis state. The migration includes: +// +// - Convert addresses from bytes to bech32 strings. +// - Re-encode in v0.40 GenesisState. +func Migrate(oldDistributionState v038distribution.GenesisState) *v040distribution.GenesisState { + newDelegatorWithdrawInfos := make([]v040distribution.DelegatorWithdrawInfo, len(oldDistributionState.DelegatorWithdrawInfos)) + for i, oldDelegatorWithdrawInfo := range oldDistributionState.DelegatorWithdrawInfos { + newDelegatorWithdrawInfos[i] = v040distribution.DelegatorWithdrawInfo{ + DelegatorAddress: oldDelegatorWithdrawInfo.DelegatorAddress.String(), + WithdrawAddress: oldDelegatorWithdrawInfo.WithdrawAddress.String(), + } + } + + newValidatorOutstandingRewards := make([]v040distribution.ValidatorOutstandingRewardsRecord, len(oldDistributionState.OutstandingRewards)) + for i, oldValidatorOutstandingReward := range oldDistributionState.OutstandingRewards { + newValidatorOutstandingRewards[i] = v040distribution.ValidatorOutstandingRewardsRecord{ + ValidatorAddress: oldValidatorOutstandingReward.ValidatorAddress.String(), + OutstandingRewards: oldValidatorOutstandingReward.OutstandingRewards, + } + } + + newValidatorAccumulatedCommissions := make([]v040distribution.ValidatorAccumulatedCommissionRecord, len(oldDistributionState.ValidatorAccumulatedCommissions)) + for i, oldValidatorAccumulatedCommission := range oldDistributionState.ValidatorAccumulatedCommissions { + newValidatorAccumulatedCommissions[i] = v040distribution.ValidatorAccumulatedCommissionRecord{ + ValidatorAddress: oldValidatorAccumulatedCommission.ValidatorAddress.String(), + Accumulated: v040distribution.ValidatorAccumulatedCommission{ + Commission: oldValidatorAccumulatedCommission.Accumulated, + }, + } + } + + newValidatorHistoricalRewards := make([]v040distribution.ValidatorHistoricalRewardsRecord, len(oldDistributionState.ValidatorHistoricalRewards)) + for i, oldValidatorHistoricalReward := range oldDistributionState.ValidatorHistoricalRewards { + newValidatorHistoricalRewards[i] = v040distribution.ValidatorHistoricalRewardsRecord{ + ValidatorAddress: oldValidatorHistoricalReward.ValidatorAddress.String(), + Period: oldValidatorHistoricalReward.Period, + Rewards: v040distribution.ValidatorHistoricalRewards{ + CumulativeRewardRatio: oldValidatorHistoricalReward.Rewards.CumulativeRewardRatio, + ReferenceCount: uint32(oldValidatorHistoricalReward.Rewards.ReferenceCount), + }, + } + } + + newValidatorCurrentRewards := make([]v040distribution.ValidatorCurrentRewardsRecord, len(oldDistributionState.ValidatorCurrentRewards)) + for i, oldValidatorCurrentReward := range oldDistributionState.ValidatorCurrentRewards { + newValidatorCurrentRewards[i] = v040distribution.ValidatorCurrentRewardsRecord{ + ValidatorAddress: oldValidatorCurrentReward.ValidatorAddress.String(), + Rewards: v040distribution.ValidatorCurrentRewards{ + Rewards: oldValidatorCurrentReward.Rewards.Rewards, + Period: oldValidatorCurrentReward.Rewards.Period, + }, + } + } + + newDelegatorStartingInfos := make([]v040distribution.DelegatorStartingInfoRecord, len(oldDistributionState.DelegatorStartingInfos)) + for i, oldDelegatorStartingInfo := range oldDistributionState.DelegatorStartingInfos { + newDelegatorStartingInfos[i] = v040distribution.DelegatorStartingInfoRecord{ + DelegatorAddress: oldDelegatorStartingInfo.DelegatorAddress.String(), + ValidatorAddress: oldDelegatorStartingInfo.ValidatorAddress.String(), + StartingInfo: v040distribution.DelegatorStartingInfo{ + PreviousPeriod: oldDelegatorStartingInfo.StartingInfo.PreviousPeriod, + Stake: oldDelegatorStartingInfo.StartingInfo.Stake, + Height: oldDelegatorStartingInfo.StartingInfo.Height, + }, + } + } + + newValidatorSlashEvents := make([]v040distribution.ValidatorSlashEventRecord, len(oldDistributionState.ValidatorSlashEvents)) + for i, oldValidatorSlashEvent := range oldDistributionState.ValidatorSlashEvents { + newValidatorSlashEvents[i] = v040distribution.ValidatorSlashEventRecord{ + ValidatorAddress: oldValidatorSlashEvent.ValidatorAddress.String(), + Height: oldValidatorSlashEvent.Height, + Period: oldValidatorSlashEvent.Period, + ValidatorSlashEvent: v040distribution.ValidatorSlashEvent{ + ValidatorPeriod: oldValidatorSlashEvent.Event.ValidatorPeriod, + Fraction: oldValidatorSlashEvent.Event.Fraction, + }, + } + } + + return &v040distribution.GenesisState{ + Params: v040distribution.Params{ + CommunityTax: oldDistributionState.Params.CommunityTax, + BaseProposerReward: oldDistributionState.Params.BaseProposerReward, + BonusProposerReward: oldDistributionState.Params.BonusProposerReward, + WithdrawAddrEnabled: oldDistributionState.Params.WithdrawAddrEnabled, + }, + FeePool: v040distribution.FeePool{ + CommunityPool: oldDistributionState.FeePool.CommunityPool, + }, + DelegatorWithdrawInfos: newDelegatorWithdrawInfos, + PreviousProposer: oldDistributionState.PreviousProposer.String(), + OutstandingRewards: newValidatorOutstandingRewards, + ValidatorAccumulatedCommissions: newValidatorAccumulatedCommissions, + ValidatorHistoricalRewards: newValidatorHistoricalRewards, + ValidatorCurrentRewards: newValidatorCurrentRewards, + DelegatorStartingInfos: newDelegatorStartingInfos, + ValidatorSlashEvents: newValidatorSlashEvents, + } +} diff --git a/x/distribution/legacy/v040/types.go b/x/distribution/legacy/v040/types.go new file mode 100644 index 000000000000..aa502303803e --- /dev/null +++ b/x/distribution/legacy/v040/types.go @@ -0,0 +1,6 @@ +package v040 + +// Default parameter values +const ( + ModuleName = "distribution" +) diff --git a/x/distribution/legacy/v0_34/types.go b/x/distribution/legacy/v0_34/types.go deleted file mode 100644 index 84e1b4f0adac..000000000000 --- a/x/distribution/legacy/v0_34/types.go +++ /dev/null @@ -1,98 +0,0 @@ -// DONTCOVER -// nolint -package v0_34 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ---------------------------------------------------------------------------- -// Types and Constants -// ---------------------------------------------------------------------------- - -const ( - ModuleName = "distr" -) - -type ( - ValidatorAccumulatedCommission = sdk.DecCoins - - DelegatorStartingInfo struct { - PreviousPeriod uint64 `json:"previous_period"` - Stake sdk.Dec `json:"stake"` - Height uint64 `json:"height"` - } - - DelegatorWithdrawInfo struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address"` - } - - ValidatorOutstandingRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - OutstandingRewards sdk.DecCoins `json:"outstanding_rewards"` - } - - ValidatorAccumulatedCommissionRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Accumulated ValidatorAccumulatedCommission `json:"accumulated"` - } - - ValidatorHistoricalRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Period uint64 `json:"period"` - Rewards ValidatorHistoricalRewards `json:"rewards"` - } - - ValidatorHistoricalRewards struct { - CumulativeRewardRatio sdk.DecCoins `json:"cumulative_reward_ratio"` - ReferenceCount uint16 `json:"reference_count"` - } - - ValidatorCurrentRewards struct { - Rewards sdk.DecCoins `json:"rewards"` - Period uint64 `json:"period"` - } - - ValidatorCurrentRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Rewards ValidatorCurrentRewards `json:"rewards"` - } - - DelegatorStartingInfoRecord struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address"` - StartingInfo DelegatorStartingInfo `json:"starting_info"` - } - - ValidatorSlashEventRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Height uint64 `json:"height"` - Event ValidatorSlashEvent `json:"validator_slash_event"` - } - - FeePool struct { - CommunityPool sdk.DecCoins `json:"community_pool"` - } - - ValidatorSlashEvent struct { - ValidatorPeriod uint64 `json:"validator_period"` - Fraction sdk.Dec `json:"fraction"` - } - - GenesisState struct { - FeePool FeePool `json:"fee_pool"` - CommunityTax sdk.Dec `json:"community_tax"` - BaseProposerReward sdk.Dec `json:"base_proposer_reward"` - BonusProposerReward sdk.Dec `json:"bonus_proposer_reward"` - WithdrawAddrEnabled bool `json:"withdraw_addr_enabled"` - DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` - PreviousProposer sdk.ConsAddress `json:"previous_proposer"` - OutstandingRewards []ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` - ValidatorAccumulatedCommissions []ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` - ValidatorHistoricalRewards []ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` - ValidatorCurrentRewards []ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` - DelegatorStartingInfos []DelegatorStartingInfoRecord `json:"delegator_starting_infos"` - ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events"` - } -) diff --git a/x/distribution/legacy/v0_36/migrate.go b/x/distribution/legacy/v0_36/migrate.go deleted file mode 100644 index e2f3c8119de1..000000000000 --- a/x/distribution/legacy/v0_36/migrate.go +++ /dev/null @@ -1,30 +0,0 @@ -package v0_36 - -import ( - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 -// genesis state. All entries are identical except for validator slashing events -// which now include the period. -func Migrate(oldGenState v034distr.GenesisState) GenesisState { - // migrate slash events which now have the period included - slashEvents := make([]ValidatorSlashEventRecord, len(oldGenState.ValidatorSlashEvents)) - for i, se := range oldGenState.ValidatorSlashEvents { - slashEvents[i] = ValidatorSlashEventRecord{ - ValidatorAddress: se.ValidatorAddress, - Height: se.Height, - Period: se.Event.ValidatorPeriod, - Event: se.Event, - } - } - - return NewGenesisState( - oldGenState.FeePool, oldGenState.CommunityTax, oldGenState.BaseProposerReward, - oldGenState.BonusProposerReward, oldGenState.WithdrawAddrEnabled, - oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer, - oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions, - oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards, - oldGenState.DelegatorStartingInfos, slashEvents, - ) -} diff --git a/x/distribution/legacy/v0_36/migrate_test.go b/x/distribution/legacy/v0_36/migrate_test.go deleted file mode 100644 index 1aadbe29a4e7..000000000000 --- a/x/distribution/legacy/v0_36/migrate_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package v0_36 - -import ( - "testing" - - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/types" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" - - "github.com/stretchr/testify/require" -) - -var ( - priv = secp256k1.GenPrivKey() - addr = types.AccAddress(priv.PubKey().Address()) - valAddr, _ = types.ValAddressFromBech32(addr.String()) - - event = v034distr.ValidatorSlashEvent{ - ValidatorPeriod: 1, - Fraction: types.Dec{}, - } -) - -func TestMigrate(t *testing.T) { - var genesisState GenesisState - require.NotPanics(t, func() { - genesisState = Migrate(v034distr.GenesisState{ - ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{ - { - ValidatorAddress: valAddr, - Height: 1, - Event: event, - }, - }, - }) - }) - - require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{ - ValidatorAddress: valAddr, - Height: 1, - Period: event.ValidatorPeriod, - Event: event, - }) -} - -func TestMigrateEmptyRecord(t *testing.T) { - var genesisState GenesisState - - require.NotPanics(t, func() { - genesisState = Migrate(v034distr.GenesisState{ - ValidatorSlashEvents: []v034distr.ValidatorSlashEventRecord{{}}, - }) - }) - - require.Equal(t, genesisState.ValidatorSlashEvents[0], ValidatorSlashEventRecord{ - ValidatorAddress: valAddr, - Height: 0, - Period: 0, - Event: v034distr.ValidatorSlashEvent{ - ValidatorPeriod: 0, - Fraction: types.Dec{}, - }, - }) -} diff --git a/x/distribution/legacy/v0_36/types.go b/x/distribution/legacy/v0_36/types.go deleted file mode 100644 index d1f02b629694..000000000000 --- a/x/distribution/legacy/v0_36/types.go +++ /dev/null @@ -1,68 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" -) - -// ---------------------------------------------------------------------------- -// Types and Constants -// ---------------------------------------------------------------------------- - -const ( - ModuleName = "distribution" -) - -type ( - ValidatorAccumulatedCommission = sdk.DecCoins - - ValidatorSlashEventRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Height uint64 `json:"height"` - Period uint64 `json:"period"` - Event v034distr.ValidatorSlashEvent `json:"validator_slash_event"` - } - - GenesisState struct { - FeePool v034distr.FeePool `json:"fee_pool"` - CommunityTax sdk.Dec `json:"community_tax"` - BaseProposerReward sdk.Dec `json:"base_proposer_reward"` - BonusProposerReward sdk.Dec `json:"bonus_proposer_reward"` - WithdrawAddrEnabled bool `json:"withdraw_addr_enabled"` - DelegatorWithdrawInfos []v034distr.DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` - PreviousProposer sdk.ConsAddress `json:"previous_proposer"` - OutstandingRewards []v034distr.ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` - ValidatorAccumulatedCommissions []v034distr.ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` - ValidatorHistoricalRewards []v034distr.ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` - ValidatorCurrentRewards []v034distr.ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` - DelegatorStartingInfos []v034distr.DelegatorStartingInfoRecord `json:"delegator_starting_infos"` - ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events"` - } -) - -func NewGenesisState( - feePool v034distr.FeePool, communityTax, baseProposerReward, bonusProposerReward sdk.Dec, - withdrawAddrEnabled bool, dwis []v034distr.DelegatorWithdrawInfo, pp sdk.ConsAddress, - r []v034distr.ValidatorOutstandingRewardsRecord, acc []v034distr.ValidatorAccumulatedCommissionRecord, - historical []v034distr.ValidatorHistoricalRewardsRecord, cur []v034distr.ValidatorCurrentRewardsRecord, - dels []v034distr.DelegatorStartingInfoRecord, slashes []ValidatorSlashEventRecord, -) GenesisState { - - return GenesisState{ - FeePool: feePool, - CommunityTax: communityTax, - BaseProposerReward: baseProposerReward, - BonusProposerReward: bonusProposerReward, - WithdrawAddrEnabled: withdrawAddrEnabled, - DelegatorWithdrawInfos: dwis, - PreviousProposer: pp, - OutstandingRewards: r, - ValidatorAccumulatedCommissions: acc, - ValidatorHistoricalRewards: historical, - ValidatorCurrentRewards: cur, - DelegatorStartingInfos: dels, - ValidatorSlashEvents: slashes, - } -} diff --git a/x/distribution/legacy/v0_38/migrate.go b/x/distribution/legacy/v0_38/migrate.go deleted file mode 100644 index fb1b1d2f8f14..000000000000 --- a/x/distribution/legacy/v0_38/migrate.go +++ /dev/null @@ -1,27 +0,0 @@ -package v0_38 - -// DONTCOVER -// nolint - -import ( - v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_36" -) - -// Migrate accepts exported genesis state from v0.36 or v0.37 and migrates it to -// v0.38 genesis state. All entries are identical except for parameters. -func Migrate(oldGenState v036distr.GenesisState) GenesisState { - params := Params{ - CommunityTax: oldGenState.CommunityTax, - BaseProposerReward: oldGenState.BaseProposerReward, - BonusProposerReward: oldGenState.BonusProposerReward, - WithdrawAddrEnabled: oldGenState.WithdrawAddrEnabled, - } - - return NewGenesisState( - params, oldGenState.FeePool, - oldGenState.DelegatorWithdrawInfos, oldGenState.PreviousProposer, - oldGenState.OutstandingRewards, oldGenState.ValidatorAccumulatedCommissions, - oldGenState.ValidatorHistoricalRewards, oldGenState.ValidatorCurrentRewards, - oldGenState.DelegatorStartingInfos, oldGenState.ValidatorSlashEvents, - ) -} diff --git a/x/distribution/legacy/v0_38/types.go b/x/distribution/legacy/v0_38/types.go deleted file mode 100644 index 0b904372e63d..000000000000 --- a/x/distribution/legacy/v0_38/types.go +++ /dev/null @@ -1,57 +0,0 @@ -package v0_38 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" - v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_36" -) - -// DONTCOVER -// nolint - -const ( - ModuleName = "distribution" -) - -type ( - GenesisState struct { - Params Params `json:"params" yaml:"params"` - FeePool v034distr.FeePool `json:"fee_pool"` - DelegatorWithdrawInfos []v034distr.DelegatorWithdrawInfo `json:"delegator_withdraw_infos"` - PreviousProposer sdk.ConsAddress `json:"previous_proposer" yaml:"previous_proposer"` - OutstandingRewards []v034distr.ValidatorOutstandingRewardsRecord `json:"outstanding_rewards"` - ValidatorAccumulatedCommissions []v034distr.ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions"` - ValidatorHistoricalRewards []v034distr.ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards"` - ValidatorCurrentRewards []v034distr.ValidatorCurrentRewardsRecord `json:"validator_current_rewards"` - DelegatorStartingInfos []v034distr.DelegatorStartingInfoRecord `json:"delegator_starting_infos"` - ValidatorSlashEvents []v036distr.ValidatorSlashEventRecord `json:"validator_slash_events" yaml:"validator_slash_events"` - } - - Params struct { - CommunityTax sdk.Dec `json:"community_tax" yaml:"community_tax"` - BaseProposerReward sdk.Dec `json:"base_proposer_reward" yaml:"base_proposer_reward"` - BonusProposerReward sdk.Dec `json:"bonus_proposer_reward" yaml:"bonus_proposer_reward"` - WithdrawAddrEnabled bool `json:"withdraw_addr_enabled" yaml:"withdraw_addr_enabled"` - } -) - -func NewGenesisState( - params Params, feePool v034distr.FeePool, dwis []v034distr.DelegatorWithdrawInfo, pp sdk.ConsAddress, - r []v034distr.ValidatorOutstandingRewardsRecord, acc []v034distr.ValidatorAccumulatedCommissionRecord, - historical []v034distr.ValidatorHistoricalRewardsRecord, cur []v034distr.ValidatorCurrentRewardsRecord, - dels []v034distr.DelegatorStartingInfoRecord, slashes []v036distr.ValidatorSlashEventRecord, -) GenesisState { - - return GenesisState{ - FeePool: feePool, - Params: params, - DelegatorWithdrawInfos: dwis, - PreviousProposer: pp, - OutstandingRewards: r, - ValidatorAccumulatedCommissions: acc, - ValidatorHistoricalRewards: historical, - ValidatorCurrentRewards: cur, - DelegatorStartingInfos: dels, - ValidatorSlashEvents: slashes, - } -} diff --git a/x/distribution/module.go b/x/distribution/module.go index 0b143d6478b8..034be6d9651d 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -1,24 +1,28 @@ package distribution import ( + "context" "encoding/json" "fmt" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + 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" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" + "github.com/cosmos/cosmos-sdk/x/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/distribution/types" - sim "github.com/cosmos/cosmos-sdk/x/simulation" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) @@ -29,47 +33,59 @@ var ( ) // AppModuleBasic defines the basic application module used by the distribution module. -type AppModuleBasic struct{} +type AppModuleBasic struct { + cdc codec.Marshaler +} // Name returns the distribution module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the distribution module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the distribution module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the distribution // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the distribution module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +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 fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return types.ValidateGenesis(&data) } // RegisterRESTRoutes registers the REST routes for the distribution module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr, StoreKey) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx sdkclient.Context, rtr *mux.Router) { + rest.RegisterHandlers(clientCtx, rtr) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the distribution 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 distribution module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(StoreKey, cdc) +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() } // GetQueryCmd returns the root query command for the distribution module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(StoreKey, cdc) +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces implements InterfaceModule +func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } //____________________________________________________________________________ @@ -78,68 +94,71 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper stakingKeeper stakingkeeper.Keeper - supplyKeeper types.SupplyKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper, - supplyKeeper types.SupplyKeeper, stakingKeeper stakingkeeper.Keeper) AppModule { +func NewAppModule( + cdc codec.Marshaler, keeper keeper.Keeper, accountKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, stakingKeeper stakingkeeper.Keeper, +) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, accountKeeper: accountKeeper, - supplyKeeper: supplyKeeper, + bankKeeper: bankKeeper, stakingKeeper: stakingKeeper, } } // Name returns the distribution module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers the distribution module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - RegisterInvariants(ir, am.keeper) + keeper.RegisterInvariants(ir, am.keeper) } // Route returns the message routing key for the distribution module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the distribution module. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(am.keeper) +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) } // QuerierRoute returns the distribution module's querier route name. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute +} + +// LegacyQuerierHandler returns the distribution module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } -// NewQuerierHandler returns the distribution module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) } // InitGenesis performs genesis initialization for the distribution module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + am.keeper.InitGenesis(ctx, genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the distribution // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the distribution module. @@ -164,22 +183,23 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { // ProposalContents returns all the distribution content functions used to // simulate governance proposals. -func (am AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return simulation.ProposalContents(am.keeper) } // RandomizedParams creates randomized distribution param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for distribution module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +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) []sim.WeightedOperation { - return simulation.WeightedOperations(simState.AppParams, simState.Cdc, - am.accountKeeper, am.keeper, am.stakingKeeper) +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, am.keeper, am.stakingKeeper, + ) } diff --git a/x/distribution/module_test.go b/x/distribution/module_test.go new file mode 100644 index 000000000000..113622d208e9 --- /dev/null +++ b/x/distribution/module_test.go @@ -0,0 +1,28 @@ +package distribution_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.InitChain( + abcitypes.RequestInitChain{ + AppStateBytes: []byte("{}"), + ChainId: "test-chain-id", + }, + ) + + acc := app.AccountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName)) + require.NotNil(t, acc) +} diff --git a/x/distribution/proposal_handler_test.go b/x/distribution/proposal_handler_test.go index 1aeee1f2880a..575dbb199cbe 100644 --- a/x/distribution/proposal_handler_test.go +++ b/x/distribution/proposal_handler_test.go @@ -1,13 +1,15 @@ -package distribution +package distribution_test import ( "testing" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "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/x/distribution" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -18,50 +20,54 @@ var ( amount = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))) ) -func testProposal(recipient sdk.AccAddress, amount sdk.Coins) types.CommunityPoolSpendProposal { - return types.NewCommunityPoolSpendProposal( - "Test", - "description", - recipient, - amount, - ) +func testProposal(recipient sdk.AccAddress, amount sdk.Coins) *types.CommunityPoolSpendProposal { + return types.NewCommunityPoolSpendProposal("Test", "description", recipient, amount) } func TestProposalHandlerPassed(t *testing.T) { - ctx, accountKeeper, keeper, _, supplyKeeper := CreateTestInputDefault(t, false, 10) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + recipient := delAddr1 // add coins to the module account - macc := keeper.GetDistributionAccount(ctx) - err := macc.SetCoins(macc.GetCoins().Add(amount...)) + 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) - supplyKeeper.SetModuleAccount(ctx, macc) + app.AccountKeeper.SetModuleAccount(ctx, macc) - account := accountKeeper.NewAccountWithAddress(ctx, recipient) - require.True(t, account.GetCoins().IsZero()) - accountKeeper.SetAccount(ctx, account) + account := app.AccountKeeper.NewAccountWithAddress(ctx, recipient) + app.AccountKeeper.SetAccount(ctx, account) + require.True(t, app.BankKeeper.GetAllBalances(ctx, account.GetAddress()).IsZero()) - feePool := keeper.GetFeePool(ctx) + feePool := app.DistrKeeper.GetFeePool(ctx) feePool.CommunityPool = sdk.NewDecCoinsFromCoins(amount...) - keeper.SetFeePool(ctx, feePool) + app.DistrKeeper.SetFeePool(ctx, feePool) tp := testProposal(recipient, amount) - hdlr := NewCommunityPoolSpendProposalHandler(keeper) + hdlr := distribution.NewCommunityPoolSpendProposalHandler(app.DistrKeeper) require.NoError(t, hdlr(ctx, tp)) - require.Equal(t, accountKeeper.GetAccount(ctx, recipient).GetCoins(), amount) + + balances = app.BankKeeper.GetAllBalances(ctx, recipient) + require.Equal(t, balances, amount) } func TestProposalHandlerFailed(t *testing.T) { - ctx, accountKeeper, keeper, _, _ := CreateTestInputDefault(t, false, 10) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + recipient := delAddr1 - account := accountKeeper.NewAccountWithAddress(ctx, recipient) - require.True(t, account.GetCoins().IsZero()) - accountKeeper.SetAccount(ctx, account) + account := app.AccountKeeper.NewAccountWithAddress(ctx, recipient) + app.AccountKeeper.SetAccount(ctx, account) + require.True(t, app.BankKeeper.GetAllBalances(ctx, account.GetAddress()).IsZero()) tp := testProposal(recipient, amount) - hdlr := NewCommunityPoolSpendProposalHandler(keeper) + hdlr := distribution.NewCommunityPoolSpendProposalHandler(app.DistrKeeper) require.Error(t, hdlr(ctx, tp)) - require.True(t, accountKeeper.GetAccount(ctx, recipient).GetCoins().IsZero()) + + balances := app.BankKeeper.GetAllBalances(ctx, recipient) + require.True(t, balances.IsZero()) } diff --git a/x/distribution/simulation/decoder.go b/x/distribution/simulation/decoder.go index 79298073a94d..a2be7dc188bf 100644 --- a/x/distribution/simulation/decoder.go +++ b/x/distribution/simulation/decoder.go @@ -4,65 +4,67 @@ import ( "bytes" "fmt" - tmkv "github.com/tendermint/tendermint/libs/kv" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding distribution type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.FeePoolKey): - var feePoolA, feePoolB types.FeePool - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &feePoolA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &feePoolB) - return fmt.Sprintf("%v\n%v", feePoolA, feePoolB) +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding distribution 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.FeePoolKey): + var feePoolA, feePoolB types.FeePool + cdc.MustUnmarshalBinaryBare(kvA.Value, &feePoolA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &feePoolB) + return fmt.Sprintf("%v\n%v", feePoolA, feePoolB) - case bytes.Equal(kvA.Key[:1], types.ProposerKey): - return fmt.Sprintf("%v\n%v", sdk.ConsAddress(kvA.Value), sdk.ConsAddress(kvB.Value)) + case bytes.Equal(kvA.Key[:1], types.ProposerKey): + return fmt.Sprintf("%v\n%v", sdk.ConsAddress(kvA.Value), sdk.ConsAddress(kvB.Value)) - case bytes.Equal(kvA.Key[:1], types.ValidatorOutstandingRewardsPrefix): - var rewardsA, rewardsB types.ValidatorOutstandingRewards - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + case bytes.Equal(kvA.Key[:1], types.ValidatorOutstandingRewardsPrefix): + var rewardsA, rewardsB types.ValidatorOutstandingRewards + cdc.MustUnmarshalBinaryBare(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - case bytes.Equal(kvA.Key[:1], types.DelegatorWithdrawAddrPrefix): - return fmt.Sprintf("%v\n%v", sdk.AccAddress(kvA.Value), sdk.AccAddress(kvB.Value)) + case bytes.Equal(kvA.Key[:1], types.DelegatorWithdrawAddrPrefix): + return fmt.Sprintf("%v\n%v", sdk.AccAddress(kvA.Value), sdk.AccAddress(kvB.Value)) - case bytes.Equal(kvA.Key[:1], types.DelegatorStartingInfoPrefix): - var infoA, infoB types.DelegatorStartingInfo - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) - return fmt.Sprintf("%v\n%v", infoA, infoB) + case bytes.Equal(kvA.Key[:1], types.DelegatorStartingInfoPrefix): + var infoA, infoB types.DelegatorStartingInfo + cdc.MustUnmarshalBinaryBare(kvA.Value, &infoA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &infoB) + return fmt.Sprintf("%v\n%v", infoA, infoB) - case bytes.Equal(kvA.Key[:1], types.ValidatorHistoricalRewardsPrefix): - var rewardsA, rewardsB types.ValidatorHistoricalRewards - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + case bytes.Equal(kvA.Key[:1], types.ValidatorHistoricalRewardsPrefix): + var rewardsA, rewardsB types.ValidatorHistoricalRewards + cdc.MustUnmarshalBinaryBare(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - case bytes.Equal(kvA.Key[:1], types.ValidatorCurrentRewardsPrefix): - var rewardsA, rewardsB types.ValidatorCurrentRewards - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &rewardsA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &rewardsB) - return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) + case bytes.Equal(kvA.Key[:1], types.ValidatorCurrentRewardsPrefix): + var rewardsA, rewardsB types.ValidatorCurrentRewards + cdc.MustUnmarshalBinaryBare(kvA.Value, &rewardsA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &rewardsB) + return fmt.Sprintf("%v\n%v", rewardsA, rewardsB) - case bytes.Equal(kvA.Key[:1], types.ValidatorAccumulatedCommissionPrefix): - var commissionA, commissionB types.ValidatorAccumulatedCommission - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &commissionA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &commissionB) - return fmt.Sprintf("%v\n%v", commissionA, commissionB) + case bytes.Equal(kvA.Key[:1], types.ValidatorAccumulatedCommissionPrefix): + var commissionA, commissionB types.ValidatorAccumulatedCommission + cdc.MustUnmarshalBinaryBare(kvA.Value, &commissionA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &commissionB) + return fmt.Sprintf("%v\n%v", commissionA, commissionB) - case bytes.Equal(kvA.Key[:1], types.ValidatorSlashEventPrefix): - var eventA, eventB types.ValidatorSlashEvent - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &eventA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &eventB) - return fmt.Sprintf("%v\n%v", eventA, eventB) + case bytes.Equal(kvA.Key[:1], types.ValidatorSlashEventPrefix): + var eventA, eventB types.ValidatorSlashEvent + cdc.MustUnmarshalBinaryBare(kvA.Value, &eventA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &eventB) + return fmt.Sprintf("%v\n%v", eventA, eventB) - default: - panic(fmt.Sprintf("invalid distribution key prefix %X", kvA.Key[:1])) + default: + panic(fmt.Sprintf("invalid distribution key prefix %X", kvA.Key[:1])) + } } } diff --git a/x/distribution/simulation/decoder_test.go b/x/distribution/simulation/decoder_test.go index d8c6d719ce9e..7a705f97da10 100644 --- a/x/distribution/simulation/decoder_test.go +++ b/x/distribution/simulation/decoder_test.go @@ -1,4 +1,4 @@ -package simulation +package simulation_test import ( "fmt" @@ -6,11 +6,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" + "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/distribution/simulation" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -21,38 +21,33 @@ var ( consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) - return -} - func TestDecodeDistributionStore(t *testing.T) { - cdc := makeTestCodec() + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} feePool := types.InitialFeePool() feePool.CommunityPool = decCoins info := types.NewDelegatorStartingInfo(2, sdk.OneDec(), 200) - outstanding := types.ValidatorOutstandingRewards{decCoins[0]} - commission := types.ValidatorAccumulatedCommission{decCoins[0]} + outstanding := types.ValidatorOutstandingRewards{Rewards: decCoins} + commission := types.ValidatorAccumulatedCommission{Commission: decCoins} historicalRewards := types.NewValidatorHistoricalRewards(decCoins, 100) currentRewards := types.NewValidatorCurrentRewards(decCoins, 5) slashEvent := types.NewValidatorSlashEvent(10, sdk.OneDec()) - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.FeePoolKey, Value: cdc.MustMarshalBinaryLengthPrefixed(feePool)}, - tmkv.Pair{Key: types.ProposerKey, Value: consAddr1.Bytes()}, - tmkv.Pair{Key: types.GetValidatorOutstandingRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(outstanding)}, - tmkv.Pair{Key: types.GetDelegatorWithdrawAddrKey(delAddr1), Value: delAddr1.Bytes()}, - tmkv.Pair{Key: types.GetDelegatorStartingInfoKey(valAddr1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, - tmkv.Pair{Key: types.GetValidatorHistoricalRewardsKey(valAddr1, 100), Value: cdc.MustMarshalBinaryLengthPrefixed(historicalRewards)}, - tmkv.Pair{Key: types.GetValidatorCurrentRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(currentRewards)}, - tmkv.Pair{Key: types.GetValidatorAccumulatedCommissionKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(commission)}, - tmkv.Pair{Key: types.GetValidatorSlashEventKeyPrefix(valAddr1, 13), Value: cdc.MustMarshalBinaryLengthPrefixed(slashEvent)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: types.FeePoolKey, Value: cdc.MustMarshalBinaryBare(&feePool)}, + {Key: types.ProposerKey, Value: consAddr1.Bytes()}, + {Key: types.GetValidatorOutstandingRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryBare(&outstanding)}, + {Key: types.GetDelegatorWithdrawAddrKey(delAddr1), Value: delAddr1.Bytes()}, + {Key: types.GetDelegatorStartingInfoKey(valAddr1, delAddr1), Value: cdc.MustMarshalBinaryBare(&info)}, + {Key: types.GetValidatorHistoricalRewardsKey(valAddr1, 100), Value: cdc.MustMarshalBinaryBare(&historicalRewards)}, + {Key: types.GetValidatorCurrentRewardsKey(valAddr1), Value: cdc.MustMarshalBinaryBare(¤tRewards)}, + {Key: types.GetValidatorAccumulatedCommissionKey(valAddr1), Value: cdc.MustMarshalBinaryBare(&commission)}, + {Key: types.GetValidatorSlashEventKeyPrefix(valAddr1, 13), Value: cdc.MustMarshalBinaryBare(&slashEvent)}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, } tests := []struct { @@ -75,9 +70,9 @@ func TestDecodeDistributionStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/distribution/simulation/genesis.go b/x/distribution/simulation/genesis.go index 9bc9df198c8b..3f64d5c01e54 100644 --- a/x/distribution/simulation/genesis.go +++ b/x/distribution/simulation/genesis.go @@ -3,11 +3,10 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -77,6 +76,10 @@ func RandomizedGenState(simState *module.SimulationState) { }, } - fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, distrGenesis)) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(distrGenesis) + bz, err := json.MarshalIndent(&distrGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&distrGenesis) } diff --git a/x/distribution/simulation/genesis_test.go b/x/distribution/simulation/genesis_test.go new file mode 100644 index 000000000000..e923fbd4c833 --- /dev/null +++ b/x/distribution/simulation/genesis_test.go @@ -0,0 +1,81 @@ +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" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 distrGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &distrGenesis) + + dec1, _ := sdk.NewDecFromStr("0.170000000000000000") + dec2, _ := sdk.NewDecFromStr("0.010000000000000000") + dec3, _ := sdk.NewDecFromStr("0.210000000000000000") + + require.Equal(t, dec1, distrGenesis.Params.BaseProposerReward) + require.Equal(t, dec2, distrGenesis.Params.BonusProposerReward) + require.Equal(t, dec3, distrGenesis.Params.CommunityTax) + require.Equal(t, true, distrGenesis.Params.WithdrawAddrEnabled) + require.Len(t, distrGenesis.DelegatorStartingInfos, 0) + require.Len(t, distrGenesis.DelegatorWithdrawInfos, 0) + require.Len(t, distrGenesis.ValidatorSlashEvents, 0) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/distribution/simulation/operations.go b/x/distribution/simulation/operations.go index d2857e6c5aff..09090d1f64d6 100644 --- a/x/distribution/simulation/operations.go +++ b/x/distribution/simulation/operations.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/helpers" 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/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/simulation" @@ -25,8 +26,8 @@ const ( // WeightedOperations returns all the operations from the module with their respective weights func WeightedOperations( - appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, - k keeper.Keeper, sk stakingkeeper.Keeper, + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, + bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper, ) simulation.WeightedOperations { var weightMsgSetWithdrawAddress int @@ -60,45 +61,48 @@ func WeightedOperations( return simulation.WeightedOperations{ simulation.NewWeightedOperation( weightMsgSetWithdrawAddress, - SimulateMsgSetWithdrawAddress(ak, k), + SimulateMsgSetWithdrawAddress(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgWithdrawDelegationReward, - SimulateMsgWithdrawDelegatorReward(ak, k, sk), + SimulateMsgWithdrawDelegatorReward(ak, bk, k, sk), ), simulation.NewWeightedOperation( weightMsgWithdrawValidatorCommission, - SimulateMsgWithdrawValidatorCommission(ak, k, sk), + SimulateMsgWithdrawValidatorCommission(ak, bk, k, sk), ), simulation.NewWeightedOperation( weightMsgFundCommunityPool, - SimulateMsgFundCommunityPool(ak, k, sk), + SimulateMsgFundCommunityPool(ak, bk, k, sk), ), } } // SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values. -// nolint: funlen -func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { if !k.GetWithdrawAddrEnabled(ctx) { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSetWithdrawAddress, "withdrawal is not enabled"), nil, nil } - simAccount, _ := simulation.RandomAcc(r, accs) - simToAccount, _ := simulation.RandomAcc(r, accs) + simAccount, _ := simtypes.RandomAcc(r, accs) + simToAccount, _ := simtypes.RandomAcc(r, accs) + account := ak.GetAccount(ctx, simAccount.Address) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSetWithdrawAddress, "unable to generate fees"), nil, err } msg := types.NewMsgSetWithdrawAddress(simAccount.Address, simToAccount.Address) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -107,44 +111,50 @@ func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, k keeper.Keeper) simu []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values. -// nolint: funlen -func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { +func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - simAccount, _ := simulation.RandomAcc(r, accs) + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) delegations := sk.GetAllDelegatorDelegations(ctx, simAccount.Address) if len(delegations) == 0 { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawDelegatorReward, "number of delegators equal 0"), nil, nil } delegation := delegations[r.Intn(len(delegations))] validator := sk.Validator(ctx, delegation.GetValidatorAddr()) if validator == nil { - return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", delegation.GetValidatorAddr()) + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawDelegatorReward, "validator is nil"), nil, fmt.Errorf("validator %s not found", delegation.GetValidatorAddr()) } account := ak.GetAccount(ctx, simAccount.Address) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawDelegatorReward, "unable to generate fees"), nil, err } msg := types.NewMsgWithdrawDelegatorReward(simAccount.Address, validator.GetOperator()) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -153,47 +163,53 @@ func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, k keeper.Keeper, []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values. -// nolint: funlen -func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { +func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { validator, ok := stakingkeeper.RandomValidator(r, sk, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawValidatorCommission, "random validator is not ok"), nil, nil } commission := k.GetValidatorAccumulatedCommission(ctx, validator.GetOperator()) - if commission.IsZero() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + if commission.Commission.IsZero() { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawValidatorCommission, "validator commission is zero"), nil, nil } - simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(validator.GetOperator())) + simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(validator.GetOperator())) if !found { - return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", validator.GetOperator()) + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawValidatorCommission, "could not find account"), nil, fmt.Errorf("validator %s not found", validator.GetOperator()) } account := ak.GetAccount(ctx, simAccount.Address) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgWithdrawValidatorCommission, "unable to generate fees"), nil, err } msg := types.NewMsgWithdrawValidatorCommission(validator.GetOperator()) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -202,31 +218,34 @@ func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, k keeper.Kee []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgFundCommunityPool simulates MsgFundCommunityPool execution where // a random account sends a random amount of its funds to the community pool. -func SimulateMsgFundCommunityPool(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { +func SimulateMsgFundCommunityPool(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - funder, _ := simulation.RandomAcc(r, accs) + funder, _ := simtypes.RandomAcc(r, accs) account := ak.GetAccount(ctx, funder.Address) - coins := account.SpendableCoins(ctx.BlockTime()) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) - fundAmount := simulation.RandSubsetCoins(r, coins) + fundAmount := simtypes.RandSubsetCoins(r, spendable) if fundAmount.Empty() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgFundCommunityPool, "fund amount is empty"), nil, nil } var ( @@ -234,16 +253,18 @@ func SimulateMsgFundCommunityPool(ak types.AccountKeeper, k keeper.Keeper, sk st err error ) - coins, hasNeg := coins.SafeSub(fundAmount) + coins, hasNeg := spendable.SafeSub(fundAmount) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgFundCommunityPool, "unable to generate fees"), nil, err } } msg := types.NewMsgFundCommunityPool(fundAmount, funder.Address) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -252,12 +273,15 @@ func SimulateMsgFundCommunityPool(ak types.AccountKeeper, k keeper.Keeper, sk st []uint64{account.GetSequence()}, funder.PrivKey, ) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err + } - _, _, err = app.Deliver(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go new file mode 100644 index 000000000000..4a3dff30878e --- /dev/null +++ b/x/distribution/simulation/operations_test.go @@ -0,0 +1,275 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "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/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/distribution/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TestWeightedOperations tests the weights of the operations. +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.DistrKeeper, suite.app.StakingKeeper, + ) + + // 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.DefaultWeightMsgSetWithdrawAddress, types.ModuleName, types.TypeMsgSetWithdrawAddress}, + {simappparams.DefaultWeightMsgWithdrawDelegationReward, types.ModuleName, types.TypeMsgWithdrawDelegatorReward}, + {simappparams.DefaultWeightMsgWithdrawValidatorCommission, types.ModuleName, types.TypeMsgWithdrawValidatorCommission}, + {simappparams.DefaultWeightMsgFundCommunityPool, types.ModuleName, types.TypeMsgFundCommunityPool}, + } + + 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") + } +} + +// TestSimulateMsgSetWithdrawAddress tests the normal scenario of a valid message of type TypeMsgSetWithdrawAddress. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgSetWithdrawAddress() { + + // 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}}) + + // execute operation + op := simulation.SimulateMsgSetWithdrawAddress(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgSetWithdrawAddress + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress) + suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.WithdrawAddress) + suite.Require().Equal(types.TypeMsgSetWithdrawAddress, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgWithdrawDelegatorReward tests the normal scenario of a valid message +// of type TypeMsgWithdrawDelegatorReward. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() { + // setup 3 accounts + s := rand.NewSource(4) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // setup accounts[0] as validator + validator0 := suite.getTestingValidator0(accounts) + + // setup delegation + delTokens := sdk.TokensFromConsensusPower(2) + validator0, issuedShares := validator0.AddTokensFromDel(delTokens) + delegator := accounts[1] + delegation := stakingtypes.NewDelegation(delegator.Address, validator0.GetOperator(), issuedShares) + suite.app.StakingKeeper.SetDelegation(suite.ctx, delegation) + suite.app.DistrKeeper.SetDelegatorStartingInfo(suite.ctx, validator0.GetOperator(), delegator.Address, distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) + + suite.setupValidatorRewards(validator0.GetOperator()) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgWithdrawDelegatorReward(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgWithdrawDelegatorReward + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmosvaloper1l4s054098kk9hmr5753c6k3m2kw65h686d3mhr", msg.ValidatorAddress) + suite.Require().Equal("cosmos1d6u7zhjwmsucs678d7qn95uqajd4ucl9jcjt26", msg.DelegatorAddress) + suite.Require().Equal(types.TypeMsgWithdrawDelegatorReward, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgWithdrawValidatorCommission tests the normal scenario of a valid message +// of type TypeMsgWithdrawValidatorCommission. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgWithdrawValidatorCommission() { + suite.testSimulateMsgWithdrawValidatorCommission("atoken") + suite.testSimulateMsgWithdrawValidatorCommission("tokenxxx") +} + +// all the checks in this function should not fail if we change the tokenName +func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName string) { + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := suite.getTestingAccounts(r, 3) + + // setup accounts[0] as validator + validator0 := suite.getTestingValidator0(accounts) + + // set module account coins + distrAcc := suite.app.DistrKeeper.GetDistributionAccount(suite.ctx) + err := suite.app.BankKeeper.SetBalances(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 + valCommission := sdk.NewDecCoins( + sdk.NewDecCoinFromDec(tokenName, sdk.NewDec(5).Quo(sdk.NewDec(2))), + sdk.NewDecCoinFromDec("stake", sdk.NewDec(1).Quo(sdk.NewDec(1))), + ) + + suite.app.DistrKeeper.SetValidatorOutstandingRewards(suite.ctx, validator0.GetOperator(), types.ValidatorOutstandingRewards{Rewards: valCommission}) + + // setup validator accumulated commission + suite.app.DistrKeeper.SetValidatorAccumulatedCommission(suite.ctx, validator0.GetOperator(), types.ValidatorAccumulatedCommission{Commission: valCommission}) + + // begin a new block + suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgWithdrawValidatorCommission(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgWithdrawValidatorCommission + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) + suite.Require().Equal(types.TypeMsgWithdrawValidatorCommission, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +// TestSimulateMsgFundCommunityPool tests the normal scenario of a valid message of type TypeMsgFundCommunityPool. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func (suite *SimTestSuite) TestSimulateMsgFundCommunityPool() { + // 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}}) + + // execute operation + op := simulation.SimulateMsgFundCommunityPool(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) + operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") + suite.Require().NoError(err) + + var msg types.MsgFundCommunityPool + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("4896096stake", msg.Amount.String()) + suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor) + suite.Require().Equal(types.TypeMsgFundCommunityPool, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) +} + +type SimTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp +} + +func (suite *SimTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) +} + +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 { + 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) + } + + return accounts +} + +func (suite *SimTestSuite) getTestingValidator0(accounts []simtypes.Account) stakingtypes.Validator { + commission0 := stakingtypes.NewCommission(sdk.ZeroDec(), sdk.OneDec(), sdk.OneDec()) + return suite.getTestingValidator(accounts, commission0, 0) +} + +func (suite *SimTestSuite) getTestingValidator(accounts []simtypes.Account, commission stakingtypes.Commission, n int) stakingtypes.Validator { + require := suite.Require() + account := accounts[n] + valPubKey := account.PubKey + valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) + validator, err := stakingtypes.NewValidator(valAddr, valPubKey, stakingtypes. + Description{}) + require.NoError(err) + validator, err = validator.SetInitialCommission(commission) + require.NoError(err) + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.NewInt(1000000) + + suite.app.StakingKeeper.SetValidator(suite.ctx, validator) + + return validator +} + +func (suite *SimTestSuite) setupValidatorRewards(valAddress sdk.ValAddress) { + decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} + historicalRewards := distrtypes.NewValidatorHistoricalRewards(decCoins, 2) + suite.app.DistrKeeper.SetValidatorHistoricalRewards(suite.ctx, valAddress, 2, historicalRewards) + // setup current revards + currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3) + suite.app.DistrKeeper.SetValidatorCurrentRewards(suite.ctx, valAddress, currentRewards) + +} + +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +} diff --git a/x/distribution/simulation/params.go b/x/distribution/simulation/params.go index 60c591a6dc3b..98fcec342fd9 100644 --- a/x/distribution/simulation/params.go +++ b/x/distribution/simulation/params.go @@ -6,8 +6,10 @@ import ( "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/simulation" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/distribution/types" ) const ( @@ -18,8 +20,8 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keyCommunityTax, func(r *rand.Rand) string { return fmt.Sprintf("\"%s\"", GenCommunityTax(r)) diff --git a/x/distribution/simulation/params_test.go b/x/distribution/simulation/params_test.go new file mode 100644 index 000000000000..d28ee9076a2b --- /dev/null +++ b/x/distribution/simulation/params_test.go @@ -0,0 +1,37 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/distribution/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"distribution/communitytax", "communitytax", "\"0.120000000000000000\"", "distribution"}, + {"distribution/baseproposerreward", "baseproposerreward", "\"0.280000000000000000\"", "distribution"}, + {"distribution/bonusproposerreward", "bonusproposerreward", "\"0.180000000000000000\"", "distribution"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 3) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/distribution/simulation/proposals.go b/x/distribution/simulation/proposals.go index cd6fcb1b7506..719229cb96f3 100644 --- a/x/distribution/simulation/proposals.go +++ b/x/distribution/simulation/proposals.go @@ -5,9 +5,9 @@ import ( 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/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -15,21 +15,20 @@ import ( const OpWeightSubmitCommunitySpendProposal = "op_weight_submit_community_spend_proposal" // ProposalContents defines the module weighted proposals' contents -func ProposalContents(k keeper.Keeper) []simulation.WeightedProposalContent { - return []simulation.WeightedProposalContent{ - { - AppParamsKey: OpWeightSubmitCommunitySpendProposal, - DefaultWeight: simappparams.DefaultWeightCommunitySpendProposal, - ContentSimulatorFn: SimulateCommunityPoolSpendProposalContent(k), - }, +func ProposalContents(k keeper.Keeper) []simtypes.WeightedProposalContent { + return []simtypes.WeightedProposalContent{ + simulation.NewWeightedProposalContent( + OpWeightSubmitCommunitySpendProposal, + simappparams.DefaultWeightCommunitySpendProposal, + SimulateCommunityPoolSpendProposalContent(k), + ), } } // SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content -// nolint: funlen -func SimulateCommunityPoolSpendProposalContent(k keeper.Keeper) simulation.ContentSimulatorFn { - return func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) govtypes.Content { - simAccount, _ := simulation.RandomAcc(r, accs) +func SimulateCommunityPoolSpendProposalContent(k keeper.Keeper) simtypes.ContentSimulatorFn { + return func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { + simAccount, _ := simtypes.RandomAcc(r, accs) balance := k.GetFeePool(ctx).CommunityPool if balance.Empty() { @@ -37,14 +36,14 @@ func SimulateCommunityPoolSpendProposalContent(k keeper.Keeper) simulation.Conte } denomIndex := r.Intn(len(balance)) - amount, err := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt()) + amount, err := simtypes.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt()) if err != nil { return nil } return types.NewCommunityPoolSpendProposal( - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 100), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 100), simAccount.Address, sdk.NewCoins(sdk.NewCoin(balance[denomIndex].Denom, amount)), ) diff --git a/x/distribution/simulation/proposals_test.go b/x/distribution/simulation/proposals_test.go new file mode 100644 index 000000000000..6d5895ea8dfd --- /dev/null +++ b/x/distribution/simulation/proposals_test.go @@ -0,0 +1,49 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "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/distribution/simulation" +) + +func TestProposalContents(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + // initialize parameters + s := rand.NewSource(1) + r := rand.New(s) + + accounts := simtypes.RandomAccounts(r, 3) + + // execute ProposalContents function + weightedProposalContent := simulation.ProposalContents(app.DistrKeeper) + require.Len(t, weightedProposalContent, 1) + + w0 := weightedProposalContent[0] + + // tests w0 interface: + require.Equal(t, simulation.OpWeightSubmitCommunitySpendProposal, w0.AppParamsKey()) + require.Equal(t, simappparams.DefaultWeightTextProposal, w0.DefaultWeight()) + + amount := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1)), sdk.NewCoin("atoken", sdk.NewInt(2))) + + feePool := app.DistrKeeper.GetFeePool(ctx) + feePool.CommunityPool = sdk.NewDecCoinsFromCoins(amount...) + app.DistrKeeper.SetFeePool(ctx, feePool) + + content := w0.ContentSimulatorFn()(r, ctx, accounts) + + require.Equal(t, "sTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeHVIkPZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhu", content.GetDescription()) + require.Equal(t, "xKGLwQvuyN", content.GetTitle()) + require.Equal(t, "distribution", content.ProposalRoute()) + require.Equal(t, "CommunityPoolSpend", content.ProposalType()) +} 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..49c94cb8a0a5 100644 --- a/x/distribution/spec/04_messages.md +++ b/x/distribution/spec/04_messages.md @@ -4,108 +4,82 @@ 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`. + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37 ```go -type MsgWithdrawDelegationRewardsAll struct { - DelegatorAddr sdk.AccAddress -} -func WithdrawDelegationRewardsAll(delegatorAddr, withdrawAddr sdk.AccAddress) - height = GetHeight() - withdraw = GetDelegatorRewardsAll(delegatorAddr, height) - SendCoins(distributionModuleAcc, withdrawAddr, withdraw.TruncateDecimal()) +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" + } -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) - - feePool, diWithdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, - validator.Tokens, validator.DelegatorShares, validator.Commission) - withdraw += diWithdraw - - SetFeePool(feePool) - return withdraw + if !k.GetWithdrawAddrEnabled(ctx) { + fail with `ErrSetWithdrawAddrDisabled` + } + + k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr) ``` -## MsgWithdrawDelegationReward +## MsgWithdrawDelegatorReward -under special circumstances a delegator may wish to withdraw rewards from only +Under special circumstances a delegator may wish to withdraw rewards from only a single validator. ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50 + ```go -type MsgWithdrawDelegationReward struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress +// withdraw rewards from a delegation +func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) { + val := k.stakingKeeper.Validator(ctx, valAddr) + if val == nil { + return nil, types.ErrNoValidatorDistInfo + } + + del := k.stakingKeeper.Delegation(ctx, delAddr, valAddr) + if del == nil { + return nil, types.ErrEmptyDelegationDistInfo + } + + // withdraw rewards + rewards, err := k.withdrawDelegationRewards(ctx, val, del) + if err != nil { + return nil, err + } + + // reinitialize the delegation + k.initializeDelegation(ctx, valAddr, delAddr) + return rewards, nil } - -func WithdrawDelegationReward(delegatorAddr, validatorAddr, withdrawAddr sdk.AccAddress) - height = GetHeight() - - // get all distribution scenarios - pool = staking.GetPool() - feePool = GetFeePool() - delInfo = GetDelegationDistInfo(delegatorAddr, - validatorAddr) - valInfo = GetValidatorDistInfo(validatorAddr) - validator = GetValidator(validatorAddr) - - feePool, withdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, - validator.Tokens, validator.DelegatorShares, validator.Commission) - - SetFeePool(feePool) - SendCoins(distributionModuleAcc, withdrawAddr, withdraw.TruncateDecimal()) ``` +## Withdraw Validator Rewards All -## MsgWithdrawValidatorRewardsAll - -When a validator wishes to withdraw their rewards it must send -`MsgWithdrawValidatorRewardsAll`. Note that parts of this transaction logic are also +When a validator wishes to withdraw their rewards it must send an +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) - - height = GetHeight() - feePool = GetFeePool() - pool = GetPool() - ValInfo = GetValidatorDistInfo(delegation.ValidatorAddr) - validator = GetValidator(delegation.ValidatorAddr) - // withdraw self-delegation - withdraw = GetDelegatorRewardsAll(validator.OperatorAddr, height) - - // withdrawal validator commission rewards - feePool, commission = valInfo.WithdrawCommission(feePool, valInfo, height, pool.BondedTokens, - validator.Tokens, validator.Commission) - withdraw += commission - SetFeePool(feePool) - - SendCoins(distributionModuleAcc, withdrawAddr, withdraw.TruncateDecimal()) +for _, valAddr := range validators { + val, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return err + } + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, val) + if err := msg.ValidateBasic(); err != nil { + return err + } + msgs = append(msgs, msg) +} ``` ## Common calculations diff --git a/x/distribution/spec/07_params.md b/x/distribution/spec/07_params.md index 5a933e923a6b..432a4e5a9695 100644 --- a/x/distribution/spec/07_params.md +++ b/x/distribution/spec/07_params.md @@ -6,9 +6,12 @@ order: 7 The distribution module contains the following parameters: -| Key | Type | Example | -|---------------------|--------------|------------------------| -| communitytax | string (dec) | "0.020000000000000000" | -| baseproposerreward | string (dec) | "0.010000000000000000" | -| bonusproposerreward | string (dec) | "0.040000000000000000" | -| withdrawaddrenabled | bool | true | +| Key | Type | Example | +| ------------------- | ------------ | -------------------------- | +| communitytax | string (dec) | "0.020000000000000000" [0] | +| baseproposerreward | string (dec) | "0.010000000000000000" [1] | +| bonusproposerreward | string (dec) | "0.040000000000000000" [1] | +| withdrawaddrenabled | bool | true | + +* [0] The value of `communitytax` must be positive and cannot exceed 1.00. +* [1] `baseproposerreward` and `bonusproposerreward` must be positive and their sum cannot exceed 1.00. diff --git a/x/distribution/spec/README.md b/x/distribution/spec/README.md index 6eac14be46ba..fe5f5069d63f 100644 --- a/x/distribution/spec/README.md +++ b/x/distribution/spec/README.md @@ -66,7 +66,7 @@ you are incentivized to not withdraw until after this event, increasing the worth of your existing _accum_. See [#2764](https://github.com/cosmos/cosmos-sdk/issues/2764) for further details. -## Affect on Staking +## Effect on Staking Charging commission on Atom provisions while also allowing for Atom-provisions to be auto-bonded (distributed directly to the validators bonded stake) is @@ -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/codec.go b/x/distribution/types/codec.go index a2d4bf11eaae..73aca45060e6 100644 --- a/x/distribution/types/codec.go +++ b/x/distribution/types/codec.go @@ -2,22 +2,53 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil) - cdc.RegisterConcrete(MsgWithdrawValidatorCommission{}, "cosmos-sdk/MsgWithdrawValidatorCommission", nil) - cdc.RegisterConcrete(MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil) - cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil) +// RegisterLegacyAminoCodec registers the necessary x/distribution interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil) + cdc.RegisterConcrete(&MsgWithdrawValidatorCommission{}, "cosmos-sdk/MsgWithdrawValidatorCommission", nil) + cdc.RegisterConcrete(&MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil) + cdc.RegisterConcrete(&MsgFundCommunityPool{}, "cosmos-sdk/MsgFundCommunityPool", nil) + cdc.RegisterConcrete(&CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil) } -// generic sealed codec to be used throughout module -var ModuleCdc *codec.Codec +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgWithdrawDelegatorReward{}, + &MsgWithdrawValidatorCommission{}, + &MsgSetWithdrawAddress{}, + &MsgFundCommunityPool{}, + ) + registry.RegisterImplementations( + (*govtypes.Content)(nil), + &CommunityPoolSpendProposal{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/distribution module codec. Note, the codec + // should ONLY be used in certain instances of tests and for JSON encoding as Amino + // is still used for that purpose. + // + // The actual codec used for serialization should be provided to x/distribution and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() } diff --git a/x/distribution/types/common_test.go b/x/distribution/types/common_test.go index c9dbfde6f0b1..4e6ef6b01231 100644 --- a/x/distribution/types/common_test.go +++ b/x/distribution/types/common_test.go @@ -1,9 +1,8 @@ package types import ( - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -25,5 +24,5 @@ var ( valAddr3 = sdk.ValAddress(valPk3.Address()) emptyValAddr sdk.ValAddress - emptyPubkey crypto.PubKey + emptyPubkey cryptotypes.PubKey ) diff --git a/x/distribution/types/delegator.go b/x/distribution/types/delegator.go index 8c9e0b9933ce..850878660780 100644 --- a/x/distribution/types/delegator.go +++ b/x/distribution/types/delegator.go @@ -4,19 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// starting info for a delegator reward period -// 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 that 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 -type DelegatorStartingInfo struct { - PreviousPeriod uint64 `json:"previous_period" yaml:"previous_period"` // period at which the delegation should withdraw starting from - Stake sdk.Dec `json:"stake" yaml:"stake"` // amount of staking token delegated - Height uint64 `json:"creation_height" yaml:"creation_height"` // height at which delegation was created -} - // create a new DelegatorStartingInfo func NewDelegatorStartingInfo(previousPeriod uint64, stake sdk.Dec, height uint64) DelegatorStartingInfo { return DelegatorStartingInfo{ diff --git a/x/distribution/types/distribution.pb.go b/x/distribution/types/distribution.pb.go new file mode 100644 index 000000000000..2657160a6ea0 --- /dev/null +++ b/x/distribution/types/distribution.pb.go @@ -0,0 +1,3339 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/distribution/v1beta1/distribution.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + 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 + +// Params defines the set of params for the distribution module. +type Params struct { + CommunityTax github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=community_tax,json=communityTax,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"community_tax" yaml:"community_tax"` + BaseProposerReward github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=base_proposer_reward,json=baseProposerReward,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"base_proposer_reward" yaml:"base_proposer_reward"` + BonusProposerReward github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=bonus_proposer_reward,json=bonusProposerReward,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"bonus_proposer_reward" yaml:"bonus_proposer_reward"` + WithdrawAddrEnabled bool `protobuf:"varint,4,opt,name=withdraw_addr_enabled,json=withdrawAddrEnabled,proto3" json:"withdraw_addr_enabled,omitempty" yaml:"withdraw_addr_enabled"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetWithdrawAddrEnabled() bool { + if m != nil { + return m.WithdrawAddrEnabled + } + return false +} + +// 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 +type ValidatorHistoricalRewards struct { + CumulativeRewardRatio github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=cumulative_reward_ratio,json=cumulativeRewardRatio,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"cumulative_reward_ratio" yaml:"cumulative_reward_ratio"` + ReferenceCount uint32 `protobuf:"varint,2,opt,name=reference_count,json=referenceCount,proto3" json:"reference_count,omitempty" yaml:"reference_count"` +} + +func (m *ValidatorHistoricalRewards) Reset() { *m = ValidatorHistoricalRewards{} } +func (m *ValidatorHistoricalRewards) String() string { return proto.CompactTextString(m) } +func (*ValidatorHistoricalRewards) ProtoMessage() {} +func (*ValidatorHistoricalRewards) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{1} +} +func (m *ValidatorHistoricalRewards) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorHistoricalRewards) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorHistoricalRewards.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 *ValidatorHistoricalRewards) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorHistoricalRewards.Merge(m, src) +} +func (m *ValidatorHistoricalRewards) XXX_Size() int { + return m.Size() +} +func (m *ValidatorHistoricalRewards) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorHistoricalRewards.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorHistoricalRewards proto.InternalMessageInfo + +func (m *ValidatorHistoricalRewards) GetCumulativeRewardRatio() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.CumulativeRewardRatio + } + return nil +} + +func (m *ValidatorHistoricalRewards) GetReferenceCount() uint32 { + if m != nil { + return m.ReferenceCount + } + return 0 +} + +// 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. +type ValidatorCurrentRewards struct { + Rewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=rewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"rewards"` + Period uint64 `protobuf:"varint,2,opt,name=period,proto3" json:"period,omitempty"` +} + +func (m *ValidatorCurrentRewards) Reset() { *m = ValidatorCurrentRewards{} } +func (m *ValidatorCurrentRewards) String() string { return proto.CompactTextString(m) } +func (*ValidatorCurrentRewards) ProtoMessage() {} +func (*ValidatorCurrentRewards) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{2} +} +func (m *ValidatorCurrentRewards) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorCurrentRewards) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorCurrentRewards.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 *ValidatorCurrentRewards) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorCurrentRewards.Merge(m, src) +} +func (m *ValidatorCurrentRewards) XXX_Size() int { + return m.Size() +} +func (m *ValidatorCurrentRewards) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorCurrentRewards.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorCurrentRewards proto.InternalMessageInfo + +func (m *ValidatorCurrentRewards) GetRewards() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Rewards + } + return nil +} + +func (m *ValidatorCurrentRewards) GetPeriod() uint64 { + if m != nil { + return m.Period + } + return 0 +} + +// ValidatorAccumulatedCommission represents accumulated commission +// for a validator kept as a running counter, can be withdrawn at any time. +type ValidatorAccumulatedCommission struct { + Commission github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=commission,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"commission"` +} + +func (m *ValidatorAccumulatedCommission) Reset() { *m = ValidatorAccumulatedCommission{} } +func (m *ValidatorAccumulatedCommission) String() string { return proto.CompactTextString(m) } +func (*ValidatorAccumulatedCommission) ProtoMessage() {} +func (*ValidatorAccumulatedCommission) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{3} +} +func (m *ValidatorAccumulatedCommission) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorAccumulatedCommission) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorAccumulatedCommission.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 *ValidatorAccumulatedCommission) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorAccumulatedCommission.Merge(m, src) +} +func (m *ValidatorAccumulatedCommission) XXX_Size() int { + return m.Size() +} +func (m *ValidatorAccumulatedCommission) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorAccumulatedCommission.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorAccumulatedCommission proto.InternalMessageInfo + +func (m *ValidatorAccumulatedCommission) GetCommission() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Commission + } + return nil +} + +// ValidatorOutstandingRewards represents outstanding (un-withdrawn) rewards +// for a validator inexpensive to track, allows simple sanity checks. +type ValidatorOutstandingRewards struct { + Rewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=rewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"rewards" yaml:"rewards"` +} + +func (m *ValidatorOutstandingRewards) Reset() { *m = ValidatorOutstandingRewards{} } +func (m *ValidatorOutstandingRewards) String() string { return proto.CompactTextString(m) } +func (*ValidatorOutstandingRewards) ProtoMessage() {} +func (*ValidatorOutstandingRewards) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{4} +} +func (m *ValidatorOutstandingRewards) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorOutstandingRewards) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorOutstandingRewards.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 *ValidatorOutstandingRewards) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorOutstandingRewards.Merge(m, src) +} +func (m *ValidatorOutstandingRewards) XXX_Size() int { + return m.Size() +} +func (m *ValidatorOutstandingRewards) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorOutstandingRewards.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorOutstandingRewards proto.InternalMessageInfo + +func (m *ValidatorOutstandingRewards) GetRewards() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Rewards + } + return nil +} + +// 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. +type ValidatorSlashEvent struct { + ValidatorPeriod uint64 `protobuf:"varint,1,opt,name=validator_period,json=validatorPeriod,proto3" json:"validator_period,omitempty" yaml:"validator_period"` + Fraction github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=fraction,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"fraction"` +} + +func (m *ValidatorSlashEvent) Reset() { *m = ValidatorSlashEvent{} } +func (m *ValidatorSlashEvent) String() string { return proto.CompactTextString(m) } +func (*ValidatorSlashEvent) ProtoMessage() {} +func (*ValidatorSlashEvent) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{5} +} +func (m *ValidatorSlashEvent) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorSlashEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorSlashEvent.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 *ValidatorSlashEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorSlashEvent.Merge(m, src) +} +func (m *ValidatorSlashEvent) XXX_Size() int { + return m.Size() +} +func (m *ValidatorSlashEvent) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorSlashEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorSlashEvent proto.InternalMessageInfo + +func (m *ValidatorSlashEvent) GetValidatorPeriod() uint64 { + if m != nil { + return m.ValidatorPeriod + } + return 0 +} + +// ValidatorSlashEvents is a collection of ValidatorSlashEvent messages. +type ValidatorSlashEvents struct { + ValidatorSlashEvents []ValidatorSlashEvent `protobuf:"bytes,1,rep,name=validator_slash_events,json=validatorSlashEvents,proto3" json:"validator_slash_events" yaml:"validator_slash_events"` +} + +func (m *ValidatorSlashEvents) Reset() { *m = ValidatorSlashEvents{} } +func (*ValidatorSlashEvents) ProtoMessage() {} +func (*ValidatorSlashEvents) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{6} +} +func (m *ValidatorSlashEvents) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorSlashEvents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorSlashEvents.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 *ValidatorSlashEvents) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorSlashEvents.Merge(m, src) +} +func (m *ValidatorSlashEvents) XXX_Size() int { + return m.Size() +} +func (m *ValidatorSlashEvents) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorSlashEvents.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorSlashEvents proto.InternalMessageInfo + +func (m *ValidatorSlashEvents) GetValidatorSlashEvents() []ValidatorSlashEvent { + if m != nil { + return m.ValidatorSlashEvents + } + return nil +} + +// FeePool is the global fee pool for distribution. +type FeePool struct { + CommunityPool github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=community_pool,json=communityPool,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"community_pool" yaml:"community_pool"` +} + +func (m *FeePool) Reset() { *m = FeePool{} } +func (m *FeePool) String() string { return proto.CompactTextString(m) } +func (*FeePool) ProtoMessage() {} +func (*FeePool) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{7} +} +func (m *FeePool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeePool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeePool.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 *FeePool) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeePool.Merge(m, src) +} +func (m *FeePool) XXX_Size() int { + return m.Size() +} +func (m *FeePool) XXX_DiscardUnknown() { + xxx_messageInfo_FeePool.DiscardUnknown(m) +} + +var xxx_messageInfo_FeePool proto.InternalMessageInfo + +func (m *FeePool) GetCommunityPool() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.CommunityPool + } + return nil +} + +// CommunityPoolSpendProposal details a proposal for use of community funds, +// together with how many coins are proposed to be spent, and to which +// recipient account. +type CommunityPoolSpendProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Recipient string `protobuf:"bytes,3,opt,name=recipient,proto3" json:"recipient,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *CommunityPoolSpendProposal) Reset() { *m = CommunityPoolSpendProposal{} } +func (*CommunityPoolSpendProposal) ProtoMessage() {} +func (*CommunityPoolSpendProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{8} +} +func (m *CommunityPoolSpendProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolSpendProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolSpendProposal.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 *CommunityPoolSpendProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolSpendProposal.Merge(m, src) +} +func (m *CommunityPoolSpendProposal) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolSpendProposal) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolSpendProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolSpendProposal proto.InternalMessageInfo + +// 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. +type DelegatorStartingInfo struct { + PreviousPeriod uint64 `protobuf:"varint,1,opt,name=previous_period,json=previousPeriod,proto3" json:"previous_period,omitempty" yaml:"previous_period"` + Stake github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=stake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"stake" yaml:"stake"` + Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"creation_height" yaml:"creation_height"` +} + +func (m *DelegatorStartingInfo) Reset() { *m = DelegatorStartingInfo{} } +func (m *DelegatorStartingInfo) String() string { return proto.CompactTextString(m) } +func (*DelegatorStartingInfo) ProtoMessage() {} +func (*DelegatorStartingInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{9} +} +func (m *DelegatorStartingInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelegatorStartingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelegatorStartingInfo.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 *DelegatorStartingInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelegatorStartingInfo.Merge(m, src) +} +func (m *DelegatorStartingInfo) XXX_Size() int { + return m.Size() +} +func (m *DelegatorStartingInfo) XXX_DiscardUnknown() { + xxx_messageInfo_DelegatorStartingInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_DelegatorStartingInfo proto.InternalMessageInfo + +func (m *DelegatorStartingInfo) GetPreviousPeriod() uint64 { + if m != nil { + return m.PreviousPeriod + } + return 0 +} + +func (m *DelegatorStartingInfo) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// DelegationDelegatorReward represents the properties +// of a delegator's delegation reward. +type DelegationDelegatorReward struct { + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + Reward github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,2,rep,name=reward,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"reward"` +} + +func (m *DelegationDelegatorReward) Reset() { *m = DelegationDelegatorReward{} } +func (m *DelegationDelegatorReward) String() string { return proto.CompactTextString(m) } +func (*DelegationDelegatorReward) ProtoMessage() {} +func (*DelegationDelegatorReward) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{10} +} +func (m *DelegationDelegatorReward) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelegationDelegatorReward) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelegationDelegatorReward.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 *DelegationDelegatorReward) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelegationDelegatorReward.Merge(m, src) +} +func (m *DelegationDelegatorReward) XXX_Size() int { + return m.Size() +} +func (m *DelegationDelegatorReward) XXX_DiscardUnknown() { + xxx_messageInfo_DelegationDelegatorReward.DiscardUnknown(m) +} + +var xxx_messageInfo_DelegationDelegatorReward proto.InternalMessageInfo + +// CommunityPoolSpendProposalWithDeposit defines a CommunityPoolSpendProposal +// with a deposit +type CommunityPoolSpendProposalWithDeposit struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty" yaml:"title"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty" yaml:"description"` + Recipient string `protobuf:"bytes,3,opt,name=recipient,proto3" json:"recipient,omitempty" yaml:"recipient"` + Amount string `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount,omitempty" yaml:"amount"` + Deposit string `protobuf:"bytes,5,opt,name=deposit,proto3" json:"deposit,omitempty" yaml:"deposit"` +} + +func (m *CommunityPoolSpendProposalWithDeposit) Reset() { *m = CommunityPoolSpendProposalWithDeposit{} } +func (m *CommunityPoolSpendProposalWithDeposit) String() string { return proto.CompactTextString(m) } +func (*CommunityPoolSpendProposalWithDeposit) ProtoMessage() {} +func (*CommunityPoolSpendProposalWithDeposit) Descriptor() ([]byte, []int) { + return fileDescriptor_cd78a31ea281a992, []int{11} +} +func (m *CommunityPoolSpendProposalWithDeposit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommunityPoolSpendProposalWithDeposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommunityPoolSpendProposalWithDeposit.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 *CommunityPoolSpendProposalWithDeposit) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunityPoolSpendProposalWithDeposit.Merge(m, src) +} +func (m *CommunityPoolSpendProposalWithDeposit) XXX_Size() int { + return m.Size() +} +func (m *CommunityPoolSpendProposalWithDeposit) XXX_DiscardUnknown() { + xxx_messageInfo_CommunityPoolSpendProposalWithDeposit.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunityPoolSpendProposalWithDeposit proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Params)(nil), "cosmos.distribution.v1beta1.Params") + proto.RegisterType((*ValidatorHistoricalRewards)(nil), "cosmos.distribution.v1beta1.ValidatorHistoricalRewards") + proto.RegisterType((*ValidatorCurrentRewards)(nil), "cosmos.distribution.v1beta1.ValidatorCurrentRewards") + proto.RegisterType((*ValidatorAccumulatedCommission)(nil), "cosmos.distribution.v1beta1.ValidatorAccumulatedCommission") + proto.RegisterType((*ValidatorOutstandingRewards)(nil), "cosmos.distribution.v1beta1.ValidatorOutstandingRewards") + proto.RegisterType((*ValidatorSlashEvent)(nil), "cosmos.distribution.v1beta1.ValidatorSlashEvent") + proto.RegisterType((*ValidatorSlashEvents)(nil), "cosmos.distribution.v1beta1.ValidatorSlashEvents") + proto.RegisterType((*FeePool)(nil), "cosmos.distribution.v1beta1.FeePool") + proto.RegisterType((*CommunityPoolSpendProposal)(nil), "cosmos.distribution.v1beta1.CommunityPoolSpendProposal") + proto.RegisterType((*DelegatorStartingInfo)(nil), "cosmos.distribution.v1beta1.DelegatorStartingInfo") + proto.RegisterType((*DelegationDelegatorReward)(nil), "cosmos.distribution.v1beta1.DelegationDelegatorReward") + proto.RegisterType((*CommunityPoolSpendProposalWithDeposit)(nil), "cosmos.distribution.v1beta1.CommunityPoolSpendProposalWithDeposit") +} + +func init() { + proto.RegisterFile("cosmos/distribution/v1beta1/distribution.proto", fileDescriptor_cd78a31ea281a992) +} + +var fileDescriptor_cd78a31ea281a992 = []byte{ + // 1102 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xf6, 0x24, 0x8e, 0x93, 0x4e, 0xf3, 0xab, 0x13, 0x27, 0x71, 0x93, 0xe0, 0x8d, 0x46, 0x6a, + 0x15, 0x04, 0x75, 0x9a, 0xf6, 0x82, 0x72, 0x40, 0x8a, 0x9d, 0x44, 0x14, 0x01, 0x8d, 0xb6, 0x01, + 0x24, 0x2e, 0xd6, 0x78, 0x77, 0x62, 0x8f, 0x62, 0xef, 0x2c, 0x33, 0x63, 0x27, 0x39, 0x20, 0x24, + 0x4e, 0x5c, 0x10, 0x20, 0x2e, 0x1c, 0x00, 0xe5, 0xc8, 0xaf, 0x3f, 0xa4, 0xc7, 0xde, 0x40, 0x20, + 0x2d, 0x28, 0x11, 0x12, 0xe2, 0xe8, 0x1b, 0x37, 0xb4, 0x3b, 0xb3, 0xbb, 0xb6, 0x6b, 0xaa, 0xb8, + 0x52, 0x4f, 0xf6, 0x7e, 0xf3, 0xe6, 0xbd, 0xef, 0xbd, 0xf7, 0xed, 0x7b, 0x0b, 0x4b, 0x0e, 0x97, + 0x2d, 0x2e, 0x37, 0x5d, 0x26, 0x95, 0x60, 0xb5, 0xb6, 0x62, 0xdc, 0xdb, 0xec, 0x6c, 0xd5, 0xa8, + 0x22, 0x5b, 0x7d, 0x60, 0xc9, 0x17, 0x5c, 0x71, 0xb4, 0xaa, 0xed, 0x4b, 0x7d, 0x47, 0xc6, 0x7e, + 0x25, 0x5f, 0xe7, 0x75, 0x1e, 0xd9, 0x6d, 0x86, 0xff, 0xf4, 0x95, 0x95, 0xa2, 0x09, 0x51, 0x23, + 0x92, 0x26, 0xae, 0x1d, 0xce, 0x8c, 0x4b, 0xfc, 0xcb, 0x38, 0xcc, 0x1d, 0x10, 0x41, 0x5a, 0x12, + 0x1d, 0xc3, 0x19, 0x87, 0xb7, 0x5a, 0x6d, 0x8f, 0xa9, 0xb3, 0xaa, 0x22, 0xa7, 0x05, 0xb0, 0x0e, + 0x36, 0xae, 0x95, 0xf7, 0x1f, 0x07, 0x56, 0xe6, 0xb7, 0xc0, 0xba, 0x5d, 0x67, 0xaa, 0xd1, 0xae, + 0x95, 0x1c, 0xde, 0xda, 0x34, 0x4e, 0xf5, 0xcf, 0x1d, 0xe9, 0x1e, 0x6f, 0xaa, 0x33, 0x9f, 0xca, + 0xd2, 0x2e, 0x75, 0xba, 0x81, 0x95, 0x3f, 0x23, 0xad, 0xe6, 0x36, 0xee, 0x73, 0x86, 0xed, 0xe9, + 0xe4, 0xf9, 0x90, 0x9c, 0xa2, 0x8f, 0x61, 0x3e, 0xa4, 0x54, 0xf5, 0x05, 0xf7, 0xb9, 0xa4, 0xa2, + 0x2a, 0xe8, 0x09, 0x11, 0x6e, 0x61, 0x2c, 0x8a, 0xf9, 0xf6, 0xc8, 0x31, 0x57, 0x75, 0xcc, 0x61, + 0x3e, 0xb1, 0x8d, 0x42, 0xf8, 0xc0, 0xa0, 0x76, 0x04, 0xa2, 0x4f, 0x00, 0x5c, 0xac, 0x71, 0xaf, + 0x2d, 0x9f, 0xa2, 0x30, 0x1e, 0x51, 0x78, 0x67, 0x64, 0x0a, 0x6b, 0x86, 0xc2, 0x30, 0xa7, 0xd8, + 0x5e, 0x88, 0xf0, 0x01, 0x12, 0x87, 0x70, 0xf1, 0x84, 0xa9, 0x86, 0x2b, 0xc8, 0x49, 0x95, 0xb8, + 0xae, 0xa8, 0x52, 0x8f, 0xd4, 0x9a, 0xd4, 0x2d, 0x64, 0xd7, 0xc1, 0xc6, 0x54, 0x79, 0x3d, 0xf5, + 0x3a, 0xd4, 0x0c, 0xdb, 0x0b, 0x31, 0xbe, 0xe3, 0xba, 0x62, 0x4f, 0xa3, 0xdb, 0xd9, 0xaf, 0xcf, + 0xad, 0x0c, 0xfe, 0x7c, 0x0c, 0xae, 0xbc, 0x47, 0x9a, 0xcc, 0x25, 0x8a, 0x8b, 0x37, 0x98, 0x54, + 0x5c, 0x30, 0x87, 0x34, 0x75, 0x64, 0x89, 0x7e, 0x02, 0x70, 0xd9, 0x69, 0xb7, 0xda, 0x4d, 0xa2, + 0x58, 0x87, 0x1a, 0x9a, 0x55, 0x41, 0x14, 0xe3, 0x05, 0xb0, 0x3e, 0xbe, 0x71, 0xfd, 0xde, 0x9a, + 0x91, 0x67, 0x29, 0xac, 0x5e, 0x2c, 0xb3, 0x30, 0xd7, 0x0a, 0x67, 0x5e, 0xf9, 0xdd, 0xb0, 0x3e, + 0xdd, 0xc0, 0x2a, 0x9a, 0x66, 0x0f, 0x77, 0x85, 0x7f, 0xfc, 0xc3, 0x7a, 0xe5, 0x6a, 0x15, 0x0c, + 0xbd, 0x4a, 0x7b, 0x31, 0x75, 0xa4, 0x99, 0xda, 0xa1, 0x1b, 0x54, 0x81, 0x73, 0x82, 0x1e, 0x51, + 0x41, 0x3d, 0x87, 0x56, 0x1d, 0xde, 0xf6, 0x54, 0xa4, 0x94, 0x99, 0xf2, 0x4a, 0x37, 0xb0, 0x96, + 0x34, 0x85, 0x01, 0x03, 0x6c, 0xcf, 0x26, 0x48, 0x25, 0x02, 0xbe, 0x03, 0x70, 0x39, 0xa9, 0x48, + 0xa5, 0x2d, 0x04, 0xf5, 0x54, 0x5c, 0x8e, 0x63, 0x38, 0xa9, 0x79, 0xcb, 0x2b, 0x65, 0x7f, 0x3f, + 0xcc, 0x7e, 0xd4, 0xdc, 0xe2, 0x08, 0x68, 0x09, 0xe6, 0x7c, 0x2a, 0x18, 0xd7, 0x72, 0xcf, 0xda, + 0xe6, 0x09, 0x7f, 0x05, 0x60, 0x31, 0x21, 0xb8, 0xe3, 0x98, 0x52, 0x50, 0xb7, 0xc2, 0x5b, 0x2d, + 0x26, 0x25, 0xe3, 0x1e, 0xfa, 0x10, 0x42, 0x27, 0x79, 0x7a, 0x71, 0x54, 0x7b, 0x82, 0xe0, 0x6f, + 0x00, 0x5c, 0x4d, 0x58, 0x3d, 0x6c, 0x2b, 0xa9, 0x88, 0xe7, 0x32, 0xaf, 0x1e, 0x97, 0xee, 0xa3, + 0xd1, 0x4a, 0xb7, 0x67, 0x84, 0x33, 0x1b, 0x77, 0x2d, 0xba, 0x8a, 0x9f, 0xb7, 0x98, 0xf8, 0x07, + 0x00, 0x17, 0x12, 0x7a, 0x8f, 0x9a, 0x44, 0x36, 0xf6, 0x3a, 0xd4, 0x53, 0x68, 0x1f, 0xce, 0x77, + 0x62, 0xb8, 0x6a, 0xca, 0x1d, 0x4e, 0xb4, 0x6c, 0x79, 0xb5, 0x1b, 0x58, 0xcb, 0x3a, 0xfa, 0xa0, + 0x05, 0xb6, 0xe7, 0x12, 0xe8, 0x20, 0x42, 0xd0, 0x9b, 0x70, 0xea, 0x48, 0x10, 0x27, 0x9c, 0xb5, + 0x66, 0x3a, 0x95, 0x46, 0x1b, 0x0d, 0x76, 0x72, 0x1f, 0xff, 0x0c, 0x60, 0x7e, 0x08, 0x57, 0x89, + 0x3e, 0x03, 0x70, 0x29, 0xe5, 0x22, 0xc3, 0x93, 0x2a, 0x8d, 0x8e, 0x4c, 0x4d, 0xef, 0x96, 0x9e, + 0x31, 0xfb, 0x4b, 0x43, 0x7c, 0x96, 0x6f, 0x99, 0x3a, 0xbf, 0x34, 0x98, 0x69, 0xaf, 0x77, 0x6c, + 0xe7, 0x3b, 0x43, 0xf8, 0x98, 0x11, 0xf2, 0x2d, 0x80, 0x93, 0xfb, 0x94, 0x1e, 0x70, 0xde, 0x44, + 0x5f, 0x02, 0x38, 0x9b, 0x4e, 0x74, 0x9f, 0xf3, 0xe6, 0x95, 0xba, 0xfd, 0x96, 0x61, 0xb1, 0x38, + 0xb8, 0x13, 0x42, 0x0f, 0x23, 0x37, 0x3d, 0x5d, 0x50, 0x21, 0x27, 0xfc, 0x17, 0x80, 0x2b, 0x95, + 0x5e, 0xe4, 0x91, 0x4f, 0x3d, 0x57, 0xcf, 0x58, 0xd2, 0x44, 0x79, 0x38, 0xa1, 0x98, 0x6a, 0x52, + 0xbd, 0xc8, 0x6c, 0xfd, 0x80, 0xd6, 0xe1, 0x75, 0x97, 0x4a, 0x47, 0x30, 0x3f, 0x6d, 0xa9, 0xdd, + 0x0b, 0xa1, 0x35, 0x78, 0x4d, 0x50, 0x87, 0xf9, 0x8c, 0x7a, 0x4a, 0x6f, 0x03, 0x3b, 0x05, 0x90, + 0x03, 0x73, 0xa4, 0x15, 0x4d, 0xa0, 0x6c, 0x94, 0xff, 0xcd, 0xa1, 0xf9, 0x47, 0xc9, 0xdf, 0x35, + 0xaf, 0xde, 0xc6, 0x15, 0x72, 0xd4, 0x09, 0x1a, 0xd7, 0xdb, 0xd3, 0x9f, 0x9e, 0x5b, 0x99, 0xb0, + 0x07, 0x7f, 0x87, 0x7d, 0xf8, 0x17, 0xc0, 0xc5, 0x5d, 0xda, 0xa4, 0xf5, 0xa8, 0x4d, 0x8a, 0x08, + 0xc5, 0xbc, 0xfa, 0x03, 0xef, 0x28, 0x9a, 0x8b, 0xbe, 0xa0, 0x1d, 0xc6, 0xc3, 0x95, 0xd3, 0xab, + 0xf1, 0x9e, 0xb9, 0x38, 0x60, 0x80, 0xed, 0xd9, 0x18, 0x31, 0x0a, 0x3f, 0x84, 0x13, 0x52, 0x91, + 0x63, 0x6a, 0xe4, 0xfd, 0xfa, 0xc8, 0x9b, 0x6f, 0x5a, 0x07, 0x8a, 0x9c, 0x60, 0x5b, 0x3b, 0x43, + 0x7b, 0x30, 0xd7, 0xa0, 0xac, 0xde, 0xd0, 0x25, 0xcc, 0x96, 0xef, 0xfc, 0x13, 0x58, 0x73, 0x8e, + 0xa0, 0xe1, 0x3c, 0xf7, 0xaa, 0xfa, 0x28, 0x25, 0x39, 0x70, 0x80, 0x6d, 0x73, 0x19, 0xff, 0x0e, + 0xe0, 0x4d, 0x93, 0x3b, 0xe3, 0x5e, 0x52, 0x05, 0xb3, 0x40, 0x1f, 0xc0, 0x1b, 0xa9, 0xb0, 0xc3, + 0xd5, 0x48, 0xa5, 0x34, 0xdf, 0x2d, 0x6b, 0xdd, 0xc0, 0x2a, 0x0c, 0x6a, 0xdf, 0x98, 0x60, 0x3b, + 0x9d, 0x0d, 0x3b, 0x1a, 0x42, 0x0c, 0xe6, 0x92, 0x6f, 0x90, 0x17, 0x34, 0x55, 0x4d, 0x80, 0xed, + 0x29, 0xd3, 0x5d, 0x80, 0xcf, 0xc7, 0xe0, 0xad, 0xff, 0x57, 0xf0, 0xfb, 0x4c, 0x35, 0x76, 0xa9, + 0xcf, 0x25, 0x53, 0xe8, 0x76, 0x9f, 0x98, 0xcb, 0xf3, 0x69, 0xd9, 0x23, 0x18, 0xc7, 0xf2, 0x7e, + 0x6d, 0x88, 0xbc, 0xcb, 0x4b, 0xdd, 0xc0, 0x42, 0xda, 0xba, 0xe7, 0x10, 0xf7, 0xcb, 0xfe, 0xde, + 0x53, 0xb2, 0x2f, 0xe7, 0xbb, 0x81, 0x35, 0x1f, 0xcf, 0x69, 0x73, 0x84, 0x7b, 0x5f, 0x86, 0x97, + 0x7b, 0x5e, 0x86, 0xf0, 0xc2, 0x8d, 0x6e, 0x60, 0xcd, 0xe8, 0x0b, 0x1a, 0xc7, 0xb1, 0xa4, 0xd1, + 0xab, 0x70, 0xd2, 0xd5, 0xb9, 0x14, 0x26, 0x22, 0x5b, 0x94, 0x2e, 0x01, 0x73, 0x80, 0xed, 0xd8, + 0x24, 0x2d, 0x51, 0xf9, 0xe1, 0xf7, 0x17, 0x45, 0xf0, 0xf8, 0xa2, 0x08, 0x9e, 0x5c, 0x14, 0xc1, + 0x9f, 0x17, 0x45, 0xf0, 0xc5, 0x65, 0x31, 0xf3, 0xe4, 0xb2, 0x98, 0xf9, 0xf5, 0xb2, 0x98, 0xf9, + 0x60, 0xeb, 0x99, 0xf5, 0x3f, 0xed, 0xff, 0xb4, 0x8e, 0xda, 0x51, 0xcb, 0x45, 0x5f, 0xbe, 0xf7, + 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x45, 0x1d, 0xb8, 0x42, 0x7e, 0x0b, 0x00, 0x00, +} + +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.CommunityTax.Equal(that1.CommunityTax) { + return false + } + if !this.BaseProposerReward.Equal(that1.BaseProposerReward) { + return false + } + if !this.BonusProposerReward.Equal(that1.BonusProposerReward) { + return false + } + if this.WithdrawAddrEnabled != that1.WithdrawAddrEnabled { + return false + } + return true +} +func (this *ValidatorHistoricalRewards) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorHistoricalRewards) + if !ok { + that2, ok := that.(ValidatorHistoricalRewards) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.CumulativeRewardRatio) != len(that1.CumulativeRewardRatio) { + return false + } + for i := range this.CumulativeRewardRatio { + if !this.CumulativeRewardRatio[i].Equal(&that1.CumulativeRewardRatio[i]) { + return false + } + } + if this.ReferenceCount != that1.ReferenceCount { + return false + } + return true +} +func (this *ValidatorCurrentRewards) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorCurrentRewards) + if !ok { + that2, ok := that.(ValidatorCurrentRewards) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Rewards) != len(that1.Rewards) { + return false + } + for i := range this.Rewards { + if !this.Rewards[i].Equal(&that1.Rewards[i]) { + return false + } + } + if this.Period != that1.Period { + return false + } + return true +} +func (this *ValidatorAccumulatedCommission) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorAccumulatedCommission) + if !ok { + that2, ok := that.(ValidatorAccumulatedCommission) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Commission) != len(that1.Commission) { + return false + } + for i := range this.Commission { + if !this.Commission[i].Equal(&that1.Commission[i]) { + return false + } + } + return true +} +func (this *ValidatorOutstandingRewards) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorOutstandingRewards) + if !ok { + that2, ok := that.(ValidatorOutstandingRewards) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Rewards) != len(that1.Rewards) { + return false + } + for i := range this.Rewards { + if !this.Rewards[i].Equal(&that1.Rewards[i]) { + return false + } + } + return true +} +func (this *ValidatorSlashEvent) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorSlashEvent) + if !ok { + that2, ok := that.(ValidatorSlashEvent) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ValidatorPeriod != that1.ValidatorPeriod { + return false + } + if !this.Fraction.Equal(that1.Fraction) { + return false + } + return true +} +func (this *ValidatorSlashEvents) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorSlashEvents) + if !ok { + that2, ok := that.(ValidatorSlashEvents) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.ValidatorSlashEvents) != len(that1.ValidatorSlashEvents) { + return false + } + for i := range this.ValidatorSlashEvents { + if !this.ValidatorSlashEvents[i].Equal(&that1.ValidatorSlashEvents[i]) { + return false + } + } + return true +} +func (this *FeePool) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeePool) + if !ok { + that2, ok := that.(FeePool) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.CommunityPool) != len(that1.CommunityPool) { + return false + } + for i := range this.CommunityPool { + if !this.CommunityPool[i].Equal(&that1.CommunityPool[i]) { + return false + } + } + return true +} +func (this *DelegatorStartingInfo) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*DelegatorStartingInfo) + if !ok { + that2, ok := that.(DelegatorStartingInfo) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.PreviousPeriod != that1.PreviousPeriod { + return false + } + if !this.Stake.Equal(that1.Stake) { + return false + } + if this.Height != that1.Height { + return false + } + return true +} +func (this *DelegationDelegatorReward) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*DelegationDelegatorReward) + if !ok { + that2, ok := that.(DelegationDelegatorReward) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ValidatorAddress != that1.ValidatorAddress { + return false + } + if len(this.Reward) != len(that1.Reward) { + return false + } + for i := range this.Reward { + if !this.Reward[i].Equal(&that1.Reward[i]) { + return false + } + } + return true +} +func (this *CommunityPoolSpendProposalWithDeposit) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CommunityPoolSpendProposalWithDeposit) + if !ok { + that2, ok := that.(CommunityPoolSpendProposalWithDeposit) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + if this.Recipient != that1.Recipient { + return false + } + if this.Amount != that1.Amount { + return false + } + if this.Deposit != that1.Deposit { + return false + } + return true +} +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.WithdrawAddrEnabled { + i-- + if m.WithdrawAddrEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + { + size := m.BonusProposerReward.Size() + i -= size + if _, err := m.BonusProposerReward.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.BaseProposerReward.Size() + i -= size + if _, err := m.BaseProposerReward.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.CommunityTax.Size() + i -= size + if _, err := m.CommunityTax.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ValidatorHistoricalRewards) 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 *ValidatorHistoricalRewards) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorHistoricalRewards) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReferenceCount != 0 { + i = encodeVarintDistribution(dAtA, i, uint64(m.ReferenceCount)) + i-- + dAtA[i] = 0x10 + } + if len(m.CumulativeRewardRatio) > 0 { + for iNdEx := len(m.CumulativeRewardRatio) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CumulativeRewardRatio[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ValidatorCurrentRewards) 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 *ValidatorCurrentRewards) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorCurrentRewards) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Period != 0 { + i = encodeVarintDistribution(dAtA, i, uint64(m.Period)) + i-- + dAtA[i] = 0x10 + } + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ValidatorAccumulatedCommission) 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 *ValidatorAccumulatedCommission) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorAccumulatedCommission) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Commission) > 0 { + for iNdEx := len(m.Commission) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Commission[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ValidatorOutstandingRewards) 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 *ValidatorOutstandingRewards) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorOutstandingRewards) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ValidatorSlashEvent) 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 *ValidatorSlashEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorSlashEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Fraction.Size() + i -= size + if _, err := m.Fraction.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.ValidatorPeriod != 0 { + i = encodeVarintDistribution(dAtA, i, uint64(m.ValidatorPeriod)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ValidatorSlashEvents) 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 *ValidatorSlashEvents) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorSlashEvents) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorSlashEvents) > 0 { + for iNdEx := len(m.ValidatorSlashEvents) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorSlashEvents[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *FeePool) 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 *FeePool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeePool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CommunityPool) > 0 { + for iNdEx := len(m.CommunityPool) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CommunityPool[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *CommunityPoolSpendProposal) 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 *CommunityPoolSpendProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolSpendProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DelegatorStartingInfo) 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 *DelegatorStartingInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelegatorStartingInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintDistribution(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x18 + } + { + size := m.Stake.Size() + i -= size + if _, err := m.Stake.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.PreviousPeriod != 0 { + i = encodeVarintDistribution(dAtA, i, uint64(m.PreviousPeriod)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DelegationDelegatorReward) 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 *DelegationDelegatorReward) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelegationDelegatorReward) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Reward) > 0 { + for iNdEx := len(m.Reward) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reward[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDistribution(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommunityPoolSpendProposalWithDeposit) 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 *CommunityPoolSpendProposalWithDeposit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommunityPoolSpendProposalWithDeposit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Deposit) > 0 { + i -= len(m.Deposit) + copy(dAtA[i:], m.Deposit) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Deposit))) + i-- + dAtA[i] = 0x2a + } + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Amount))) + i-- + dAtA[i] = 0x22 + } + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintDistribution(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintDistribution(dAtA []byte, offset int, v uint64) int { + offset -= sovDistribution(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.CommunityTax.Size() + n += 1 + l + sovDistribution(uint64(l)) + l = m.BaseProposerReward.Size() + n += 1 + l + sovDistribution(uint64(l)) + l = m.BonusProposerReward.Size() + n += 1 + l + sovDistribution(uint64(l)) + if m.WithdrawAddrEnabled { + n += 2 + } + return n +} + +func (m *ValidatorHistoricalRewards) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CumulativeRewardRatio) > 0 { + for _, e := range m.CumulativeRewardRatio { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + if m.ReferenceCount != 0 { + n += 1 + sovDistribution(uint64(m.ReferenceCount)) + } + return n +} + +func (m *ValidatorCurrentRewards) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + if m.Period != 0 { + n += 1 + sovDistribution(uint64(m.Period)) + } + return n +} + +func (m *ValidatorAccumulatedCommission) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Commission) > 0 { + for _, e := range m.Commission { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *ValidatorOutstandingRewards) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *ValidatorSlashEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ValidatorPeriod != 0 { + n += 1 + sovDistribution(uint64(m.ValidatorPeriod)) + } + l = m.Fraction.Size() + n += 1 + l + sovDistribution(uint64(l)) + return n +} + +func (m *ValidatorSlashEvents) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ValidatorSlashEvents) > 0 { + for _, e := range m.ValidatorSlashEvents { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *FeePool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CommunityPool) > 0 { + for _, e := range m.CommunityPool { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *CommunityPoolSpendProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *DelegatorStartingInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PreviousPeriod != 0 { + n += 1 + sovDistribution(uint64(m.PreviousPeriod)) + } + l = m.Stake.Size() + n += 1 + l + sovDistribution(uint64(l)) + if m.Height != 0 { + n += 1 + sovDistribution(uint64(m.Height)) + } + return n +} + +func (m *DelegationDelegatorReward) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + if len(m.Reward) > 0 { + for _, e := range m.Reward { + l = e.Size() + n += 1 + l + sovDistribution(uint64(l)) + } + } + return n +} + +func (m *CommunityPoolSpendProposalWithDeposit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + l = len(m.Deposit) + if l > 0 { + n += 1 + l + sovDistribution(uint64(l)) + } + return n +} + +func sovDistribution(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDistribution(x uint64) (n int) { + return sovDistribution(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) 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 ErrIntOverflowDistribution + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommunityTax", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CommunityTax.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseProposerReward", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BaseProposerReward.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BonusProposerReward", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BonusProposerReward.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawAddrEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.WithdrawAddrEnabled = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorHistoricalRewards) 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 ErrIntOverflowDistribution + } + 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: ValidatorHistoricalRewards: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorHistoricalRewards: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CumulativeRewardRatio", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CumulativeRewardRatio = append(m.CumulativeRewardRatio, types.DecCoin{}) + if err := m.CumulativeRewardRatio[len(m.CumulativeRewardRatio)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReferenceCount", wireType) + } + m.ReferenceCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReferenceCount |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorCurrentRewards) 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 ErrIntOverflowDistribution + } + 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: ValidatorCurrentRewards: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorCurrentRewards: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rewards = append(m.Rewards, types.DecCoin{}) + if err := m.Rewards[len(m.Rewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Period", wireType) + } + m.Period = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Period |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorAccumulatedCommission) 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 ErrIntOverflowDistribution + } + 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: ValidatorAccumulatedCommission: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorAccumulatedCommission: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Commission = append(m.Commission, types.DecCoin{}) + if err := m.Commission[len(m.Commission)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorOutstandingRewards) 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 ErrIntOverflowDistribution + } + 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: ValidatorOutstandingRewards: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorOutstandingRewards: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rewards = append(m.Rewards, types.DecCoin{}) + if err := m.Rewards[len(m.Rewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorSlashEvent) 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 ErrIntOverflowDistribution + } + 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: ValidatorSlashEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorSlashEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorPeriod", wireType) + } + m.ValidatorPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValidatorPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fraction", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Fraction.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatorSlashEvents) 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 ErrIntOverflowDistribution + } + 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: ValidatorSlashEvents: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorSlashEvents: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSlashEvents", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorSlashEvents = append(m.ValidatorSlashEvents, ValidatorSlashEvent{}) + if err := m.ValidatorSlashEvents[len(m.ValidatorSlashEvents)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeePool) 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 ErrIntOverflowDistribution + } + 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: FeePool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeePool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommunityPool", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CommunityPool = append(m.CommunityPool, types.DecCoin{}) + if err := m.CommunityPool[len(m.CommunityPool)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommunityPoolSpendProposal) 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 ErrIntOverflowDistribution + } + 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: CommunityPoolSpendProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolSpendProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Recipient = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DelegatorStartingInfo) 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 ErrIntOverflowDistribution + } + 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: DelegatorStartingInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelegatorStartingInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PreviousPeriod", wireType) + } + m.PreviousPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PreviousPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stake", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Stake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DelegationDelegatorReward) 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 ErrIntOverflowDistribution + } + 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: DelegationDelegatorReward: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelegationDelegatorReward: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reward", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDistribution + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reward = append(m.Reward, types.DecCoin{}) + if err := m.Reward[len(m.Reward)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommunityPoolSpendProposalWithDeposit) 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 ErrIntOverflowDistribution + } + 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: CommunityPoolSpendProposalWithDeposit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommunityPoolSpendProposalWithDeposit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Recipient = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDistribution + } + 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 ErrInvalidLengthDistribution + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDistribution + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Deposit = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDistribution(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDistribution + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDistribution(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, ErrIntOverflowDistribution + } + 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, ErrIntOverflowDistribution + } + 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, ErrIntOverflowDistribution + } + 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, ErrInvalidLengthDistribution + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDistribution + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthDistribution + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthDistribution = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDistribution = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDistribution = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/distribution/types/errors.go b/x/distribution/types/errors.go index c06b731b6ee4..147cfd320341 100644 --- a/x/distribution/types/errors.go +++ b/x/distribution/types/errors.go @@ -6,16 +6,16 @@ import ( // x/distribution module sentinel errors var ( - ErrEmptyDelegatorAddr = sdkerrors.Register(ModuleName, 1, "delegator address is empty") - ErrEmptyWithdrawAddr = sdkerrors.Register(ModuleName, 2, "withdraw address is empty") - ErrEmptyValidatorAddr = sdkerrors.Register(ModuleName, 3, "validator address is empty") - ErrEmptyDelegationDistInfo = sdkerrors.Register(ModuleName, 4, "no delegation distribution info") - ErrNoValidatorDistInfo = sdkerrors.Register(ModuleName, 5, "no validator distribution info") - ErrNoValidatorCommission = sdkerrors.Register(ModuleName, 6, "no validator commission to withdraw") - ErrSetWithdrawAddrDisabled = sdkerrors.Register(ModuleName, 7, "set withdraw address disabled") - ErrBadDistribution = sdkerrors.Register(ModuleName, 8, "community pool does not have sufficient coins to distribute") - ErrInvalidProposalAmount = sdkerrors.Register(ModuleName, 9, "invalid community pool spend proposal amount") - ErrEmptyProposalRecipient = sdkerrors.Register(ModuleName, 10, "invalid community pool spend proposal recipient") - ErrNoValidatorExists = sdkerrors.Register(ModuleName, 11, "validator does not exist") - ErrNoDelegationExists = sdkerrors.Register(ModuleName, 12, "delegation does not exist") + ErrEmptyDelegatorAddr = sdkerrors.Register(ModuleName, 2, "delegator address is empty") + ErrEmptyWithdrawAddr = sdkerrors.Register(ModuleName, 3, "withdraw address is empty") + ErrEmptyValidatorAddr = sdkerrors.Register(ModuleName, 4, "validator address is empty") + ErrEmptyDelegationDistInfo = sdkerrors.Register(ModuleName, 5, "no delegation distribution info") + ErrNoValidatorDistInfo = sdkerrors.Register(ModuleName, 6, "no validator distribution info") + ErrNoValidatorCommission = sdkerrors.Register(ModuleName, 7, "no validator commission to withdraw") + ErrSetWithdrawAddrDisabled = sdkerrors.Register(ModuleName, 8, "set withdraw address disabled") + ErrBadDistribution = sdkerrors.Register(ModuleName, 9, "community pool does not have sufficient coins to distribute") + ErrInvalidProposalAmount = sdkerrors.Register(ModuleName, 10, "invalid community pool spend proposal amount") + ErrEmptyProposalRecipient = sdkerrors.Register(ModuleName, 11, "invalid community pool spend proposal recipient") + ErrNoValidatorExists = sdkerrors.Register(ModuleName, 12, "validator does not exist") + ErrNoDelegationExists = sdkerrors.Register(ModuleName, 13, "delegation does not exist") ) diff --git a/x/distribution/types/expected_keepers.go b/x/distribution/types/expected_keepers.go index faa2f37235da..29346d39081e 100644 --- a/x/distribution/types/expected_keepers.go +++ b/x/distribution/types/expected_keepers.go @@ -2,33 +2,50 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + "github.com/cosmos/cosmos-sdk/x/auth/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AccountKeeper defines the expected account keeper used for simulations (noalias) type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, name string) types.ModuleAccountI + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + SetModuleAccount(sdk.Context, types.ModuleAccountI) +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +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 + + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error } // StakingKeeper expected staking keeper (noalias) type StakingKeeper interface { // iterate through validators by operator address, execute func for each validator IterateValidators(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator stakingtypes.ValidatorI) (stop bool)) // iterate through bonded validators by operator address, execute func for each validator IterateBondedValidatorsByPower(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator stakingtypes.ValidatorI) (stop bool)) // iterate through the consensus validator set of the last block by operator address, execute func for each validator IterateLastValidators(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator stakingtypes.ValidatorI) (stop bool)) - Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address - ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address + Validator(sdk.Context, sdk.ValAddress) stakingtypes.ValidatorI // get a particular validator by operator address + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingtypes.ValidatorI // get a particular validator by consensus address // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) @@ -37,18 +54,18 @@ type StakingKeeper interface { // Delegation allows for getting a particular delegation for a given validator // and delegator outside the scope of the staking module. - Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI + Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingtypes.DelegationI // MaxValidators returns the maximum amount of bonded validators - MaxValidators(sdk.Context) uint16 + MaxValidators(sdk.Context) uint32 IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, - fn func(index int64, delegation stakingexported.DelegationI) (stop bool)) + fn func(index int64, delegation stakingtypes.DelegationI) (stop bool)) GetLastTotalPower(ctx sdk.Context) sdk.Int GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) int64 - GetAllSDKDelegations(ctx sdk.Context) []staking.Delegation + GetAllSDKDelegations(ctx sdk.Context) []stakingtypes.Delegation } // StakingHooks event hooks for staking validator object (noalias) @@ -61,16 +78,3 @@ type StakingHooks interface { AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) } - -// SupplyKeeper defines the expected supply Keeper (noalias) -type SupplyKeeper interface { - GetModuleAddress(name string) sdk.AccAddress - GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI - - // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 - SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) - - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error -} diff --git a/x/distribution/types/fee_pool.go b/x/distribution/types/fee_pool.go index b542ced7be24..645afd401a33 100644 --- a/x/distribution/types/fee_pool.go +++ b/x/distribution/types/fee_pool.go @@ -6,11 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// global fee pool for distribution -type FeePool struct { - CommunityPool sdk.DecCoins `json:"community_pool" yaml:"community_pool"` // pool for community funds yet to be spent -} - // zero fee pool func InitialFeePool() FeePool { return FeePool{ diff --git a/x/distribution/types/genesis.go b/x/distribution/types/genesis.go index 8ef054c72479..981d914fade9 100644 --- a/x/distribution/types/genesis.go +++ b/x/distribution/types/genesis.go @@ -1,81 +1,24 @@ package types import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) -// the address for where distributions rewards are withdrawn to by default -// this struct is only used at genesis to feed in default withdraw addresses -type DelegatorWithdrawInfo struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` -} - -// used for import/export via genesis json -type ValidatorOutstandingRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - OutstandingRewards sdk.DecCoins `json:"outstanding_rewards" yaml:"outstanding_rewards"` -} - -// used for import / export via genesis json -type ValidatorAccumulatedCommissionRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Accumulated ValidatorAccumulatedCommission `json:"accumulated" yaml:"accumulated"` -} - -// used for import / export via genesis json -type ValidatorHistoricalRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Period uint64 `json:"period" yaml:"period"` - Rewards ValidatorHistoricalRewards `json:"rewards" yaml:"rewards"` -} - -// used for import / export via genesis json -type ValidatorCurrentRewardsRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Rewards ValidatorCurrentRewards `json:"rewards" yaml:"rewards"` -} - -// used for import / export via genesis json -type DelegatorStartingInfoRecord struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - StartingInfo DelegatorStartingInfo `json:"starting_info" yaml:"starting_info"` -} - -// used for import / export via genesis json -type ValidatorSlashEventRecord struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Height uint64 `json:"height" yaml:"height"` - Period uint64 `json:"period" yaml:"period"` - Event ValidatorSlashEvent `json:"validator_slash_event" yaml:"validator_slash_event"` -} - -// GenesisState - all distribution state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - FeePool FeePool `json:"fee_pool" yaml:"fee_pool"` - DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos" yaml:"delegator_withdraw_infos"` - PreviousProposer sdk.ConsAddress `json:"previous_proposer" yaml:"previous_proposer"` - OutstandingRewards []ValidatorOutstandingRewardsRecord `json:"outstanding_rewards" yaml:"outstanding_rewards"` - ValidatorAccumulatedCommissions []ValidatorAccumulatedCommissionRecord `json:"validator_accumulated_commissions" yaml:"validator_accumulated_commissions"` - ValidatorHistoricalRewards []ValidatorHistoricalRewardsRecord `json:"validator_historical_rewards" yaml:"validator_historical_rewards"` - ValidatorCurrentRewards []ValidatorCurrentRewardsRecord `json:"validator_current_rewards" yaml:"validator_current_rewards"` - DelegatorStartingInfos []DelegatorStartingInfoRecord `json:"delegator_starting_infos" yaml:"delegator_starting_infos"` - ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events" yaml:"validator_slash_events"` -} - +//nolint:interfacer func NewGenesisState( params Params, fp FeePool, dwis []DelegatorWithdrawInfo, pp sdk.ConsAddress, r []ValidatorOutstandingRewardsRecord, acc []ValidatorAccumulatedCommissionRecord, historical []ValidatorHistoricalRewardsRecord, cur []ValidatorCurrentRewardsRecord, dels []DelegatorStartingInfoRecord, slashes []ValidatorSlashEventRecord, -) GenesisState { +) *GenesisState { - return GenesisState{ + return &GenesisState{ Params: params, FeePool: fp, DelegatorWithdrawInfos: dwis, - PreviousProposer: pp, + PreviousProposer: pp.String(), OutstandingRewards: r, ValidatorAccumulatedCommissions: acc, ValidatorHistoricalRewards: historical, @@ -86,12 +29,12 @@ func NewGenesisState( } // get raw genesis raw message for testing -func DefaultGenesisState() GenesisState { - return GenesisState{ +func DefaultGenesisState() *GenesisState { + return &GenesisState{ FeePool: InitialFeePool(), Params: DefaultParams(), DelegatorWithdrawInfos: []DelegatorWithdrawInfo{}, - PreviousProposer: nil, + PreviousProposer: "", OutstandingRewards: []ValidatorOutstandingRewardsRecord{}, ValidatorAccumulatedCommissions: []ValidatorAccumulatedCommissionRecord{}, ValidatorHistoricalRewards: []ValidatorHistoricalRewardsRecord{}, @@ -102,9 +45,21 @@ func DefaultGenesisState() GenesisState { } // ValidateGenesis validates the genesis state of distribution genesis input -func ValidateGenesis(gs GenesisState) error { +func ValidateGenesis(gs *GenesisState) error { if err := gs.Params.ValidateBasic(); err != nil { return err } return gs.FeePool.ValidateGenesis() } + +// GetGenesisStateFromAppState returns x/distribution GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc codec.JSONMarshaler, appState map[string]json.RawMessage) *GenesisState { + var genesisState GenesisState + + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return &genesisState +} diff --git a/x/distribution/types/genesis.pb.go b/x/distribution/types/genesis.pb.go new file mode 100644 index 000000000000..5c94d4359ffa --- /dev/null +++ b/x/distribution/types/genesis.pb.go @@ -0,0 +1,2481 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/distribution/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + 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 + +// 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. +type DelegatorWithdrawInfo struct { + // delegator_address is the address of the delegator. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + // withdraw_address is the address to withdraw the delegation rewards to. + WithdrawAddress string `protobuf:"bytes,2,opt,name=withdraw_address,json=withdrawAddress,proto3" json:"withdraw_address,omitempty" yaml:"withdraw_address"` +} + +func (m *DelegatorWithdrawInfo) Reset() { *m = DelegatorWithdrawInfo{} } +func (m *DelegatorWithdrawInfo) String() string { return proto.CompactTextString(m) } +func (*DelegatorWithdrawInfo) ProtoMessage() {} +func (*DelegatorWithdrawInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{0} +} +func (m *DelegatorWithdrawInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelegatorWithdrawInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelegatorWithdrawInfo.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 *DelegatorWithdrawInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelegatorWithdrawInfo.Merge(m, src) +} +func (m *DelegatorWithdrawInfo) XXX_Size() int { + return m.Size() +} +func (m *DelegatorWithdrawInfo) XXX_DiscardUnknown() { + xxx_messageInfo_DelegatorWithdrawInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_DelegatorWithdrawInfo proto.InternalMessageInfo + +// ValidatorOutstandingRewardsRecord is used for import/export via genesis json. +type ValidatorOutstandingRewardsRecord struct { + // validator_address is the address of the validator. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // outstanding_rewards represents the oustanding rewards of a validator. + OutstandingRewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,2,rep,name=outstanding_rewards,json=outstandingRewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"outstanding_rewards" yaml:"outstanding_rewards"` +} + +func (m *ValidatorOutstandingRewardsRecord) Reset() { *m = ValidatorOutstandingRewardsRecord{} } +func (m *ValidatorOutstandingRewardsRecord) String() string { return proto.CompactTextString(m) } +func (*ValidatorOutstandingRewardsRecord) ProtoMessage() {} +func (*ValidatorOutstandingRewardsRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{1} +} +func (m *ValidatorOutstandingRewardsRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorOutstandingRewardsRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorOutstandingRewardsRecord.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 *ValidatorOutstandingRewardsRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorOutstandingRewardsRecord.Merge(m, src) +} +func (m *ValidatorOutstandingRewardsRecord) XXX_Size() int { + return m.Size() +} +func (m *ValidatorOutstandingRewardsRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorOutstandingRewardsRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorOutstandingRewardsRecord proto.InternalMessageInfo + +// ValidatorAccumulatedCommissionRecord is used for import / export via genesis +// json. +type ValidatorAccumulatedCommissionRecord struct { + // validator_address is the address of the validator. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // accumulated is the accumulated commission of a validator. + Accumulated ValidatorAccumulatedCommission `protobuf:"bytes,2,opt,name=accumulated,proto3" json:"accumulated" yaml:"accumulated"` +} + +func (m *ValidatorAccumulatedCommissionRecord) Reset() { *m = ValidatorAccumulatedCommissionRecord{} } +func (m *ValidatorAccumulatedCommissionRecord) String() string { return proto.CompactTextString(m) } +func (*ValidatorAccumulatedCommissionRecord) ProtoMessage() {} +func (*ValidatorAccumulatedCommissionRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{2} +} +func (m *ValidatorAccumulatedCommissionRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorAccumulatedCommissionRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorAccumulatedCommissionRecord.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 *ValidatorAccumulatedCommissionRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorAccumulatedCommissionRecord.Merge(m, src) +} +func (m *ValidatorAccumulatedCommissionRecord) XXX_Size() int { + return m.Size() +} +func (m *ValidatorAccumulatedCommissionRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorAccumulatedCommissionRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorAccumulatedCommissionRecord proto.InternalMessageInfo + +// ValidatorHistoricalRewardsRecord is used for import / export via genesis +// json. +type ValidatorHistoricalRewardsRecord struct { + // validator_address is the address of the validator. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // period defines the period the historical rewards apply to. + Period uint64 `protobuf:"varint,2,opt,name=period,proto3" json:"period,omitempty"` + // rewards defines the historical rewards of a validator. + Rewards ValidatorHistoricalRewards `protobuf:"bytes,3,opt,name=rewards,proto3" json:"rewards" yaml:"rewards"` +} + +func (m *ValidatorHistoricalRewardsRecord) Reset() { *m = ValidatorHistoricalRewardsRecord{} } +func (m *ValidatorHistoricalRewardsRecord) String() string { return proto.CompactTextString(m) } +func (*ValidatorHistoricalRewardsRecord) ProtoMessage() {} +func (*ValidatorHistoricalRewardsRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{3} +} +func (m *ValidatorHistoricalRewardsRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorHistoricalRewardsRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorHistoricalRewardsRecord.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 *ValidatorHistoricalRewardsRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorHistoricalRewardsRecord.Merge(m, src) +} +func (m *ValidatorHistoricalRewardsRecord) XXX_Size() int { + return m.Size() +} +func (m *ValidatorHistoricalRewardsRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorHistoricalRewardsRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorHistoricalRewardsRecord proto.InternalMessageInfo + +// ValidatorCurrentRewardsRecord is used for import / export via genesis json. +type ValidatorCurrentRewardsRecord struct { + // validator_address is the address of the validator. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // rewards defines the current rewards of a validator. + Rewards ValidatorCurrentRewards `protobuf:"bytes,2,opt,name=rewards,proto3" json:"rewards" yaml:"rewards"` +} + +func (m *ValidatorCurrentRewardsRecord) Reset() { *m = ValidatorCurrentRewardsRecord{} } +func (m *ValidatorCurrentRewardsRecord) String() string { return proto.CompactTextString(m) } +func (*ValidatorCurrentRewardsRecord) ProtoMessage() {} +func (*ValidatorCurrentRewardsRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{4} +} +func (m *ValidatorCurrentRewardsRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorCurrentRewardsRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorCurrentRewardsRecord.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 *ValidatorCurrentRewardsRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorCurrentRewardsRecord.Merge(m, src) +} +func (m *ValidatorCurrentRewardsRecord) XXX_Size() int { + return m.Size() +} +func (m *ValidatorCurrentRewardsRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorCurrentRewardsRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorCurrentRewardsRecord proto.InternalMessageInfo + +// DelegatorStartingInfoRecord used for import / export via genesis json. +type DelegatorStartingInfoRecord struct { + // delegator_address is the 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 address of the validator. + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // starting_info defines the starting info of a delegator. + StartingInfo DelegatorStartingInfo `protobuf:"bytes,3,opt,name=starting_info,json=startingInfo,proto3" json:"starting_info" yaml:"starting_info"` +} + +func (m *DelegatorStartingInfoRecord) Reset() { *m = DelegatorStartingInfoRecord{} } +func (m *DelegatorStartingInfoRecord) String() string { return proto.CompactTextString(m) } +func (*DelegatorStartingInfoRecord) ProtoMessage() {} +func (*DelegatorStartingInfoRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{5} +} +func (m *DelegatorStartingInfoRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelegatorStartingInfoRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelegatorStartingInfoRecord.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 *DelegatorStartingInfoRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelegatorStartingInfoRecord.Merge(m, src) +} +func (m *DelegatorStartingInfoRecord) XXX_Size() int { + return m.Size() +} +func (m *DelegatorStartingInfoRecord) XXX_DiscardUnknown() { + xxx_messageInfo_DelegatorStartingInfoRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_DelegatorStartingInfoRecord proto.InternalMessageInfo + +// ValidatorSlashEventRecord is used for import / export via genesis json. +type ValidatorSlashEventRecord struct { + // validator_address is the address of the validator. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + // height defines the block height at which the slash event occured. + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + // period is the period of the slash event. + Period uint64 `protobuf:"varint,3,opt,name=period,proto3" json:"period,omitempty"` + // validator_slash_event describes the slash event. + ValidatorSlashEvent ValidatorSlashEvent `protobuf:"bytes,4,opt,name=validator_slash_event,json=validatorSlashEvent,proto3" json:"validator_slash_event" yaml:"event"` +} + +func (m *ValidatorSlashEventRecord) Reset() { *m = ValidatorSlashEventRecord{} } +func (m *ValidatorSlashEventRecord) String() string { return proto.CompactTextString(m) } +func (*ValidatorSlashEventRecord) ProtoMessage() {} +func (*ValidatorSlashEventRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_76eed0f9489db580, []int{6} +} +func (m *ValidatorSlashEventRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorSlashEventRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorSlashEventRecord.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 *ValidatorSlashEventRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorSlashEventRecord.Merge(m, src) +} +func (m *ValidatorSlashEventRecord) XXX_Size() int { + return m.Size() +} +func (m *ValidatorSlashEventRecord) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorSlashEventRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorSlashEventRecord proto.InternalMessageInfo + +// GenesisState defines the distribution module's genesis state. +type GenesisState struct { + // params defines all the paramaters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params" yaml:"params"` + // fee_pool defines the fee pool at genesis. + FeePool FeePool `protobuf:"bytes,2,opt,name=fee_pool,json=feePool,proto3" json:"fee_pool" yaml:"fee_pool"` + // fee_pool defines the delegator withdraw infos at genesis. + DelegatorWithdrawInfos []DelegatorWithdrawInfo `protobuf:"bytes,3,rep,name=delegator_withdraw_infos,json=delegatorWithdrawInfos,proto3" json:"delegator_withdraw_infos" yaml:"delegator_withdraw_infos"` + // fee_pool defines the previous proposer at genesis. + PreviousProposer string `protobuf:"bytes,4,opt,name=previous_proposer,json=previousProposer,proto3" json:"previous_proposer,omitempty" yaml:"previous_proposer"` + // fee_pool defines the outstanding rewards of all validators at genesis. + OutstandingRewards []ValidatorOutstandingRewardsRecord `protobuf:"bytes,5,rep,name=outstanding_rewards,json=outstandingRewards,proto3" json:"outstanding_rewards" yaml:"outstanding_rewards"` + // fee_pool defines the accumulated commisions of all validators at genesis. + ValidatorAccumulatedCommissions []ValidatorAccumulatedCommissionRecord `protobuf:"bytes,6,rep,name=validator_accumulated_commissions,json=validatorAccumulatedCommissions,proto3" json:"validator_accumulated_commissions" yaml:"validator_accumulated_commissions"` + // fee_pool defines the historical rewards of all validators at genesis. + ValidatorHistoricalRewards []ValidatorHistoricalRewardsRecord `protobuf:"bytes,7,rep,name=validator_historical_rewards,json=validatorHistoricalRewards,proto3" json:"validator_historical_rewards" yaml:"validator_historical_rewards"` + // fee_pool defines the current rewards of all validators at genesis. + ValidatorCurrentRewards []ValidatorCurrentRewardsRecord `protobuf:"bytes,8,rep,name=validator_current_rewards,json=validatorCurrentRewards,proto3" json:"validator_current_rewards" yaml:"validator_current_rewards"` + // fee_pool defines the delegator starting infos at genesis. + DelegatorStartingInfos []DelegatorStartingInfoRecord `protobuf:"bytes,9,rep,name=delegator_starting_infos,json=delegatorStartingInfos,proto3" json:"delegator_starting_infos" yaml:"delegator_starting_infos"` + // fee_pool defines the validator slash events at genesis. + ValidatorSlashEvents []ValidatorSlashEventRecord `protobuf:"bytes,10,rep,name=validator_slash_events,json=validatorSlashEvents,proto3" json:"validator_slash_events" yaml:"validator_slash_events"` +} + +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_76eed0f9489db580, []int{7} +} +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 init() { + proto.RegisterType((*DelegatorWithdrawInfo)(nil), "cosmos.distribution.v1beta1.DelegatorWithdrawInfo") + proto.RegisterType((*ValidatorOutstandingRewardsRecord)(nil), "cosmos.distribution.v1beta1.ValidatorOutstandingRewardsRecord") + proto.RegisterType((*ValidatorAccumulatedCommissionRecord)(nil), "cosmos.distribution.v1beta1.ValidatorAccumulatedCommissionRecord") + proto.RegisterType((*ValidatorHistoricalRewardsRecord)(nil), "cosmos.distribution.v1beta1.ValidatorHistoricalRewardsRecord") + proto.RegisterType((*ValidatorCurrentRewardsRecord)(nil), "cosmos.distribution.v1beta1.ValidatorCurrentRewardsRecord") + proto.RegisterType((*DelegatorStartingInfoRecord)(nil), "cosmos.distribution.v1beta1.DelegatorStartingInfoRecord") + proto.RegisterType((*ValidatorSlashEventRecord)(nil), "cosmos.distribution.v1beta1.ValidatorSlashEventRecord") + proto.RegisterType((*GenesisState)(nil), "cosmos.distribution.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/distribution/v1beta1/genesis.proto", fileDescriptor_76eed0f9489db580) +} + +var fileDescriptor_76eed0f9489db580 = []byte{ + // 1024 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6f, 0x1b, 0x45, + 0x1c, 0xf5, 0x3a, 0x25, 0x49, 0x27, 0x29, 0x0d, 0xdb, 0x7c, 0xb8, 0x4e, 0xea, 0x4d, 0xa7, 0x45, + 0x04, 0x55, 0xac, 0x9b, 0x80, 0x00, 0x05, 0x81, 0x94, 0x4d, 0x29, 0xf4, 0xd4, 0x30, 0x91, 0x00, + 0x71, 0xb1, 0xd6, 0xbb, 0x63, 0x7b, 0x84, 0xbd, 0x63, 0xcd, 0x8c, 0x1d, 0xc2, 0x3f, 0x00, 0x47, + 0x24, 0xc4, 0xa9, 0x1c, 0x72, 0x44, 0x88, 0x63, 0xef, 0x5c, 0x7b, 0xec, 0x91, 0x03, 0x0a, 0x28, + 0xb9, 0x70, 0xce, 0x81, 0x03, 0x27, 0xb4, 0x33, 0xb3, 0x5f, 0xf6, 0xda, 0x38, 0xa1, 0x39, 0x25, + 0x1e, 0xff, 0xf6, 0xbd, 0xf7, 0x7b, 0xf3, 0xfb, 0x58, 0x83, 0xd7, 0x3d, 0xca, 0x3b, 0x94, 0x57, + 0x7d, 0xc2, 0x05, 0x23, 0xf5, 0x9e, 0x20, 0x34, 0xa8, 0xf6, 0x37, 0xeb, 0x58, 0xb8, 0x9b, 0xd5, + 0x26, 0x0e, 0x30, 0x27, 0xdc, 0xee, 0x32, 0x2a, 0xa8, 0xb9, 0xaa, 0x42, 0xed, 0x74, 0xa8, 0xad, + 0x43, 0xcb, 0x8b, 0x4d, 0xda, 0xa4, 0x32, 0xae, 0x1a, 0xfe, 0xa7, 0x1e, 0x29, 0x57, 0x34, 0x7a, + 0xdd, 0xe5, 0x38, 0x46, 0xf5, 0x28, 0x09, 0xf4, 0xf7, 0xf6, 0x38, 0xf6, 0x0c, 0x8f, 0x8c, 0x87, + 0x4f, 0x0d, 0xb0, 0xf4, 0x00, 0xb7, 0x71, 0xd3, 0x15, 0x94, 0x7d, 0x46, 0x44, 0xcb, 0x67, 0xee, + 0xc1, 0xa3, 0xa0, 0x41, 0xcd, 0x47, 0xe0, 0x15, 0x3f, 0xfa, 0xa2, 0xe6, 0xfa, 0x3e, 0xc3, 0x9c, + 0x97, 0x8c, 0x75, 0x63, 0xe3, 0xaa, 0xb3, 0x76, 0x76, 0x6c, 0x95, 0x0e, 0xdd, 0x4e, 0x7b, 0x1b, + 0x0e, 0x85, 0x40, 0xb4, 0x10, 0x9f, 0xed, 0xa8, 0x23, 0xf3, 0x21, 0x58, 0x38, 0xd0, 0xd0, 0x31, + 0x52, 0x51, 0x22, 0xad, 0x9e, 0x1d, 0x5b, 0x2b, 0x0a, 0x69, 0x30, 0x02, 0xa2, 0xeb, 0xd1, 0x91, + 0xc6, 0xd9, 0x9e, 0xfd, 0xf6, 0xc8, 0x2a, 0xfc, 0x75, 0x64, 0x15, 0xe0, 0x93, 0x22, 0xb8, 0xfd, + 0xa9, 0xdb, 0x26, 0x7e, 0x48, 0xf3, 0xb8, 0x27, 0xb8, 0x70, 0x03, 0x9f, 0x04, 0x4d, 0x84, 0x0f, + 0x5c, 0xe6, 0x73, 0x84, 0x3d, 0xca, 0xfc, 0x30, 0x85, 0x7e, 0x14, 0x34, 0x3a, 0x85, 0xa1, 0x10, + 0x88, 0x16, 0xe2, 0xb3, 0x28, 0x85, 0x23, 0x03, 0xdc, 0xa0, 0x09, 0x4f, 0x8d, 0x29, 0xa2, 0x52, + 0x71, 0x7d, 0x6a, 0x63, 0x6e, 0x6b, 0x4d, 0xdb, 0x6e, 0x87, 0xd7, 0x12, 0xdd, 0xa0, 0xfd, 0x00, + 0x7b, 0xbb, 0x94, 0x04, 0xce, 0x27, 0xcf, 0x8e, 0xad, 0xc2, 0xd9, 0xb1, 0x55, 0x56, 0x7c, 0x39, + 0x30, 0xf0, 0xe7, 0x3f, 0xac, 0x7b, 0x4d, 0x22, 0x5a, 0xbd, 0xba, 0xed, 0xd1, 0x4e, 0x55, 0x5f, + 0xa2, 0xfa, 0xf3, 0x06, 0xf7, 0xbf, 0xac, 0x8a, 0xc3, 0x2e, 0xe6, 0x11, 0x22, 0x47, 0x26, 0x1d, + 0xca, 0x39, 0xe5, 0xce, 0xdf, 0x06, 0xb8, 0x1b, 0xbb, 0xb3, 0xe3, 0x79, 0xbd, 0x4e, 0xaf, 0xed, + 0x0a, 0xec, 0xef, 0xd2, 0x4e, 0x87, 0x70, 0x4e, 0x68, 0xf0, 0xe2, 0x0d, 0x3a, 0x04, 0x73, 0x6e, + 0xc2, 0x24, 0xaf, 0x77, 0x6e, 0xeb, 0x3d, 0x7b, 0x4c, 0x85, 0xdb, 0xe3, 0x25, 0x3a, 0x65, 0x6d, + 0x9b, 0xa9, 0x54, 0xa4, 0xd0, 0x21, 0x4a, 0x73, 0xa5, 0x12, 0xff, 0xc7, 0x00, 0xeb, 0x31, 0xea, + 0xc7, 0x84, 0x0b, 0xca, 0x88, 0xe7, 0xb6, 0x2f, 0xad, 0x2a, 0x96, 0xc1, 0x74, 0x17, 0x33, 0x42, + 0x55, 0xbe, 0x57, 0x90, 0xfe, 0x64, 0x12, 0x30, 0x13, 0x15, 0xc8, 0x94, 0x34, 0xe2, 0x9d, 0xc9, + 0x8c, 0x18, 0x92, 0xec, 0x2c, 0x6b, 0x13, 0x5e, 0x56, 0xaa, 0xa2, 0x7a, 0x41, 0x11, 0x7e, 0x2a, + 0xf9, 0xdf, 0x0d, 0x70, 0x2b, 0x46, 0xda, 0xed, 0x31, 0x86, 0x03, 0x71, 0x69, 0x99, 0x37, 0x92, + 0x0c, 0xd5, 0x55, 0xbf, 0x35, 0x59, 0x86, 0x59, 0x5d, 0xe7, 0x49, 0xef, 0x69, 0x11, 0xac, 0xc6, + 0x93, 0x6a, 0x5f, 0xb8, 0x4c, 0x90, 0xa0, 0x19, 0x4e, 0xaa, 0x24, 0xb9, 0x17, 0x35, 0xaf, 0x72, + 0x7d, 0x2a, 0x5e, 0xc8, 0xa7, 0x1e, 0xb8, 0xc6, 0xb5, 0xd6, 0x1a, 0x09, 0x1a, 0x54, 0xd7, 0xc3, + 0xd6, 0x58, 0xb7, 0x72, 0xd3, 0x74, 0xd6, 0xb4, 0x57, 0x8b, 0x8a, 0x3e, 0x03, 0x0b, 0xd1, 0x3c, + 0x4f, 0xc5, 0xa6, 0x6c, 0xfb, 0xb1, 0x08, 0x6e, 0xc6, 0xee, 0xef, 0xb7, 0x5d, 0xde, 0xfa, 0xb0, + 0x2f, 0x2f, 0xe0, 0x12, 0x7a, 0xa1, 0x85, 0x49, 0xb3, 0x25, 0xa2, 0x5e, 0x50, 0x9f, 0x52, 0x3d, + 0x32, 0x95, 0xe9, 0x91, 0xaf, 0xc1, 0x52, 0x82, 0xcb, 0x43, 0x61, 0x35, 0x1c, 0x2a, 0x2b, 0x5d, + 0x91, 0x0e, 0xdd, 0x9f, 0xac, 0x9e, 0x92, 0x8c, 0x9c, 0x45, 0xed, 0xcf, 0xbc, 0x12, 0x2d, 0xc1, + 0x20, 0xba, 0xd1, 0x1f, 0x0e, 0x4d, 0xd9, 0xf3, 0xcd, 0x1c, 0x98, 0xff, 0x48, 0x2d, 0xe5, 0x7d, + 0xe1, 0x0a, 0x6c, 0x22, 0x30, 0xdd, 0x75, 0x99, 0xdb, 0x51, 0x36, 0xcc, 0x6d, 0xdd, 0x19, 0xab, + 0x63, 0x4f, 0x86, 0x3a, 0x4b, 0x9a, 0xfa, 0x9a, 0xa2, 0x56, 0x00, 0x10, 0x69, 0x24, 0xf3, 0x73, + 0x30, 0xdb, 0xc0, 0xb8, 0xd6, 0xa5, 0xb4, 0xad, 0xbb, 0xe5, 0xee, 0x58, 0xd4, 0x87, 0x18, 0xef, + 0x51, 0xda, 0x76, 0x56, 0x34, 0xec, 0x75, 0x05, 0x1b, 0x61, 0x40, 0x34, 0xd3, 0x50, 0x11, 0xe6, + 0x0f, 0x06, 0x28, 0x25, 0x25, 0x1d, 0xaf, 0xd0, 0xb0, 0x24, 0xc2, 0xd1, 0x33, 0x35, 0x79, 0xa9, + 0xa5, 0x77, 0xbf, 0xf3, 0x9a, 0x26, 0xb6, 0x06, 0x9b, 0x26, 0xcb, 0x00, 0xd1, 0xb2, 0x9f, 0xf7, + 0xbc, 0xec, 0xa0, 0x2e, 0xc3, 0x7d, 0x42, 0x7b, 0xbc, 0xd6, 0x65, 0xb4, 0x4b, 0x39, 0x66, 0xf2, + 0x62, 0x33, 0x75, 0x35, 0x14, 0x02, 0xd1, 0x42, 0x74, 0xb6, 0xa7, 0x8f, 0xcc, 0xef, 0x47, 0x6c, + 0xde, 0x97, 0x64, 0x76, 0x1f, 0x4c, 0x56, 0x26, 0xa3, 0x5e, 0x11, 0x1c, 0xf8, 0xdf, 0xbb, 0x39, + 0x6f, 0xd9, 0x9a, 0xbf, 0x1a, 0xe0, 0x76, 0xaa, 0x2d, 0x92, 0x6d, 0x54, 0xf3, 0xe2, 0x0d, 0xc6, + 0x4b, 0xd3, 0x52, 0xe3, 0xce, 0xff, 0xd8, 0x82, 0x5a, 0xe6, 0x7d, 0x2d, 0x73, 0x63, 0xa8, 0x21, + 0xf3, 0x99, 0x21, 0xb2, 0xfa, 0x63, 0x71, 0xb9, 0xf9, 0x8b, 0x01, 0xd6, 0x12, 0x9c, 0x56, 0xbc, + 0x79, 0x62, 0x83, 0x67, 0xa4, 0xf8, 0xf7, 0x2f, 0xb8, 0xb9, 0xb4, 0xf0, 0x7b, 0x5a, 0xf8, 0x9d, + 0x41, 0xe1, 0xc3, 0x84, 0x10, 0x95, 0xfb, 0x23, 0xe1, 0xc2, 0x17, 0xb0, 0x9b, 0xc9, 0xd3, 0x9e, + 0x5a, 0x23, 0xb1, 0xd6, 0x59, 0xa9, 0x75, 0xfb, 0x22, 0x3b, 0x48, 0x0b, 0xdd, 0xd0, 0x42, 0xd7, + 0x07, 0x85, 0x0e, 0x50, 0x41, 0xb4, 0xd2, 0xcf, 0x07, 0x32, 0x9f, 0x64, 0x9a, 0x31, 0x33, 0x9f, + 0x79, 0xe9, 0xaa, 0x54, 0xf8, 0xee, 0xf9, 0xe7, 0xbe, 0xd6, 0x37, 0xb2, 0x25, 0xb3, 0x3c, 0xe9, + 0x96, 0x4c, 0xa3, 0xf0, 0xb0, 0x8f, 0x96, 0x73, 0x07, 0x2e, 0x2f, 0x01, 0xa9, 0xed, 0xed, 0xf3, + 0x4e, 0x5c, 0xad, 0xec, 0x55, 0xad, 0xec, 0xd6, 0xa0, 0x73, 0x69, 0x0e, 0x88, 0x16, 0x73, 0x06, + 0x71, 0x6a, 0xbf, 0x3b, 0x8f, 0x7f, 0x3a, 0xa9, 0x18, 0xcf, 0x4e, 0x2a, 0xc6, 0xf3, 0x93, 0x8a, + 0xf1, 0xe7, 0x49, 0xc5, 0xf8, 0xee, 0xb4, 0x52, 0x78, 0x7e, 0x5a, 0x29, 0xfc, 0x76, 0x5a, 0x29, + 0x7c, 0xb1, 0x39, 0xf6, 0xed, 0xf8, 0xab, 0xec, 0xef, 0x1d, 0xf9, 0xb2, 0x5c, 0x9f, 0x96, 0xbf, + 0x70, 0xde, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0xa8, 0xee, 0xfe, 0x4a, 0x91, 0x0d, 0x00, 0x00, +} + +func (m *DelegatorWithdrawInfo) 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 *DelegatorWithdrawInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelegatorWithdrawInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WithdrawAddress) > 0 { + i -= len(m.WithdrawAddress) + copy(dAtA[i:], m.WithdrawAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.WithdrawAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorOutstandingRewardsRecord) 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 *ValidatorOutstandingRewardsRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorOutstandingRewardsRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.OutstandingRewards) > 0 { + for iNdEx := len(m.OutstandingRewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.OutstandingRewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorAccumulatedCommissionRecord) 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 *ValidatorAccumulatedCommissionRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorAccumulatedCommissionRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Accumulated.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorHistoricalRewardsRecord) 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 *ValidatorHistoricalRewardsRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorHistoricalRewardsRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Rewards.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Period != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Period)) + i-- + dAtA[i] = 0x10 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorCurrentRewardsRecord) 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 *ValidatorCurrentRewardsRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorCurrentRewardsRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Rewards.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DelegatorStartingInfoRecord) 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 *DelegatorStartingInfoRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelegatorStartingInfoRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.StartingInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorSlashEventRecord) 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 *ValidatorSlashEventRecord) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorSlashEventRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ValidatorSlashEvent.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.Period != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Period)) + i-- + dAtA[i] = 0x18 + } + if m.Height != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +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.ValidatorSlashEvents) > 0 { + for iNdEx := len(m.ValidatorSlashEvents) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorSlashEvents[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + } + if len(m.DelegatorStartingInfos) > 0 { + for iNdEx := len(m.DelegatorStartingInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatorStartingInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if len(m.ValidatorCurrentRewards) > 0 { + for iNdEx := len(m.ValidatorCurrentRewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorCurrentRewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.ValidatorHistoricalRewards) > 0 { + for iNdEx := len(m.ValidatorHistoricalRewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorHistoricalRewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.ValidatorAccumulatedCommissions) > 0 { + for iNdEx := len(m.ValidatorAccumulatedCommissions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ValidatorAccumulatedCommissions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.OutstandingRewards) > 0 { + for iNdEx := len(m.OutstandingRewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.OutstandingRewards[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.PreviousProposer) > 0 { + i -= len(m.PreviousProposer) + copy(dAtA[i:], m.PreviousProposer) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.PreviousProposer))) + i-- + dAtA[i] = 0x22 + } + if len(m.DelegatorWithdrawInfos) > 0 { + for iNdEx := len(m.DelegatorWithdrawInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegatorWithdrawInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.FeePool.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Params.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 *DelegatorWithdrawInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.WithdrawAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func (m *ValidatorOutstandingRewardsRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.OutstandingRewards) > 0 { + for _, e := range m.OutstandingRewards { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *ValidatorAccumulatedCommissionRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Accumulated.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *ValidatorHistoricalRewardsRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Period != 0 { + n += 1 + sovGenesis(uint64(m.Period)) + } + l = m.Rewards.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *ValidatorCurrentRewardsRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Rewards.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *DelegatorStartingInfoRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.StartingInfo.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *ValidatorSlashEventRecord) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovGenesis(uint64(m.Height)) + } + if m.Period != 0 { + n += 1 + sovGenesis(uint64(m.Period)) + } + l = m.ValidatorSlashEvent.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.FeePool.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.DelegatorWithdrawInfos) > 0 { + for _, e := range m.DelegatorWithdrawInfos { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = len(m.PreviousProposer) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.OutstandingRewards) > 0 { + for _, e := range m.OutstandingRewards { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ValidatorAccumulatedCommissions) > 0 { + for _, e := range m.ValidatorAccumulatedCommissions { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ValidatorHistoricalRewards) > 0 { + for _, e := range m.ValidatorHistoricalRewards { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ValidatorCurrentRewards) > 0 { + for _, e := range m.ValidatorCurrentRewards { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.DelegatorStartingInfos) > 0 { + for _, e := range m.DelegatorStartingInfos { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ValidatorSlashEvents) > 0 { + for _, e := range m.ValidatorSlashEvents { + 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 *DelegatorWithdrawInfo) 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: DelegatorWithdrawInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelegatorWithdrawInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawAddress", 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.WithdrawAddress = string(dAtA[iNdEx:postIndex]) + 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 *ValidatorOutstandingRewardsRecord) 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: ValidatorOutstandingRewardsRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorOutstandingRewardsRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutstandingRewards", 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.OutstandingRewards = append(m.OutstandingRewards, types.DecCoin{}) + if err := m.OutstandingRewards[len(m.OutstandingRewards)-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 *ValidatorAccumulatedCommissionRecord) 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: ValidatorAccumulatedCommissionRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorAccumulatedCommissionRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accumulated", 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 := m.Accumulated.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 *ValidatorHistoricalRewardsRecord) 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: ValidatorHistoricalRewardsRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorHistoricalRewardsRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Period", wireType) + } + m.Period = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Period |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", 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 := m.Rewards.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 *ValidatorCurrentRewardsRecord) 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: ValidatorCurrentRewardsRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorCurrentRewardsRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", 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 := m.Rewards.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 *DelegatorStartingInfoRecord) 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: DelegatorStartingInfoRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelegatorStartingInfoRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartingInfo", 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 := m.StartingInfo.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 *ValidatorSlashEventRecord) 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: ValidatorSlashEventRecord: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorSlashEventRecord: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Period", wireType) + } + m.Period = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Period |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSlashEvent", 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 := m.ValidatorSlashEvent.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 *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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeePool", 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 := m.FeePool.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorWithdrawInfos", 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.DelegatorWithdrawInfos = append(m.DelegatorWithdrawInfos, DelegatorWithdrawInfo{}) + if err := m.DelegatorWithdrawInfos[len(m.DelegatorWithdrawInfos)-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 PreviousProposer", 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.PreviousProposer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OutstandingRewards", 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.OutstandingRewards = append(m.OutstandingRewards, ValidatorOutstandingRewardsRecord{}) + if err := m.OutstandingRewards[len(m.OutstandingRewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAccumulatedCommissions", 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.ValidatorAccumulatedCommissions = append(m.ValidatorAccumulatedCommissions, ValidatorAccumulatedCommissionRecord{}) + if err := m.ValidatorAccumulatedCommissions[len(m.ValidatorAccumulatedCommissions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorHistoricalRewards", 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.ValidatorHistoricalRewards = append(m.ValidatorHistoricalRewards, ValidatorHistoricalRewardsRecord{}) + if err := m.ValidatorHistoricalRewards[len(m.ValidatorHistoricalRewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorCurrentRewards", 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.ValidatorCurrentRewards = append(m.ValidatorCurrentRewards, ValidatorCurrentRewardsRecord{}) + if err := m.ValidatorCurrentRewards[len(m.ValidatorCurrentRewards)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorStartingInfos", 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.DelegatorStartingInfos = append(m.DelegatorStartingInfos, DelegatorStartingInfoRecord{}) + if err := m.DelegatorStartingInfos[len(m.DelegatorStartingInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSlashEvents", 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.ValidatorSlashEvents = append(m.ValidatorSlashEvents, ValidatorSlashEventRecord{}) + if err := m.ValidatorSlashEvents[len(m.ValidatorSlashEvents)-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/distribution/types/msg.go b/x/distribution/types/msg.go index 2068caef149e..8dc72081e9ac 100644 --- a/x/distribution/types/msg.go +++ b/x/distribution/types/msg.go @@ -6,134 +6,128 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) +// distribution message types +const ( + TypeMsgSetWithdrawAddress = "set_withdraw_address" + TypeMsgWithdrawDelegatorReward = "withdraw_delegator_reward" + TypeMsgWithdrawValidatorCommission = "withdraw_validator_commission" + TypeMsgFundCommunityPool = "fund_community_pool" +) + // Verify interface at compile time var _, _, _ sdk.Msg = &MsgSetWithdrawAddress{}, &MsgWithdrawDelegatorReward{}, &MsgWithdrawValidatorCommission{} -// msg struct for changing the withdraw address for a delegator (or validator self-delegation) -type MsgSetWithdrawAddress struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - WithdrawAddress sdk.AccAddress `json:"withdraw_address" yaml:"withdraw_address"` -} - -func NewMsgSetWithdrawAddress(delAddr, withdrawAddr sdk.AccAddress) MsgSetWithdrawAddress { - return MsgSetWithdrawAddress{ - DelegatorAddress: delAddr, - WithdrawAddress: withdrawAddr, +func NewMsgSetWithdrawAddress(delAddr, withdrawAddr sdk.AccAddress) *MsgSetWithdrawAddress { + return &MsgSetWithdrawAddress{ + DelegatorAddress: delAddr.String(), + WithdrawAddress: withdrawAddr.String(), } } func (msg MsgSetWithdrawAddress) Route() string { return ModuleName } -func (msg MsgSetWithdrawAddress) Type() string { return "set_withdraw_address" } +func (msg MsgSetWithdrawAddress) Type() string { return TypeMsgSetWithdrawAddress } // Return address that must sign over msg.GetSignBytes() func (msg MsgSetWithdrawAddress) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.DelegatorAddress)} + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{delAddr} } // get the bytes for the message signer to sign on func (msg MsgSetWithdrawAddress) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // quick validity check func (msg MsgSetWithdrawAddress) ValidateBasic() error { - if msg.DelegatorAddress.Empty() { + if msg.DelegatorAddress == "" { return ErrEmptyDelegatorAddr } - if msg.WithdrawAddress.Empty() { + if msg.WithdrawAddress == "" { return ErrEmptyWithdrawAddr } return nil } -// msg struct for delegation withdraw from a single validator -type MsgWithdrawDelegatorReward struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` -} - -func NewMsgWithdrawDelegatorReward(delAddr sdk.AccAddress, valAddr sdk.ValAddress) MsgWithdrawDelegatorReward { - return MsgWithdrawDelegatorReward{ - DelegatorAddress: delAddr, - ValidatorAddress: valAddr, +func NewMsgWithdrawDelegatorReward(delAddr sdk.AccAddress, valAddr sdk.ValAddress) *MsgWithdrawDelegatorReward { + return &MsgWithdrawDelegatorReward{ + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), } } func (msg MsgWithdrawDelegatorReward) Route() string { return ModuleName } -func (msg MsgWithdrawDelegatorReward) Type() string { return "withdraw_delegator_reward" } +func (msg MsgWithdrawDelegatorReward) Type() string { return TypeMsgWithdrawDelegatorReward } // Return address that must sign over msg.GetSignBytes() func (msg MsgWithdrawDelegatorReward) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.DelegatorAddress)} + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{delAddr} } // get the bytes for the message signer to sign on func (msg MsgWithdrawDelegatorReward) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // quick validity check func (msg MsgWithdrawDelegatorReward) ValidateBasic() error { - if msg.DelegatorAddress.Empty() { + if msg.DelegatorAddress == "" { return ErrEmptyDelegatorAddr } - if msg.ValidatorAddress.Empty() { + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } return nil } -// msg struct for validator withdraw -type MsgWithdrawValidatorCommission struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` -} - -func NewMsgWithdrawValidatorCommission(valAddr sdk.ValAddress) MsgWithdrawValidatorCommission { - return MsgWithdrawValidatorCommission{ - ValidatorAddress: valAddr, +func NewMsgWithdrawValidatorCommission(valAddr sdk.ValAddress) *MsgWithdrawValidatorCommission { + return &MsgWithdrawValidatorCommission{ + ValidatorAddress: valAddr.String(), } } func (msg MsgWithdrawValidatorCommission) Route() string { return ModuleName } -func (msg MsgWithdrawValidatorCommission) Type() string { return "withdraw_validator_commission" } +func (msg MsgWithdrawValidatorCommission) Type() string { return TypeMsgWithdrawValidatorCommission } // Return address that must sign over msg.GetSignBytes() func (msg MsgWithdrawValidatorCommission) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddress.Bytes())} + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} } // get the bytes for the message signer to sign on func (msg MsgWithdrawValidatorCommission) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // quick validity check func (msg MsgWithdrawValidatorCommission) ValidateBasic() error { - if msg.ValidatorAddress.Empty() { + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } return nil } -const TypeMsgFundCommunityPool = "fund_community_pool" - -// MsgFundCommunityPool defines a Msg type that allows an account to directly -// fund the community pool. -type MsgFundCommunityPool struct { - Amount sdk.Coins `json:"amount" yaml:"amount"` - Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` -} - // NewMsgFundCommunityPool returns a new MsgFundCommunityPool with a sender and // a funding amount. -func NewMsgFundCommunityPool(amount sdk.Coins, depositor sdk.AccAddress) MsgFundCommunityPool { - return MsgFundCommunityPool{ +func NewMsgFundCommunityPool(amount sdk.Coins, depositor sdk.AccAddress) *MsgFundCommunityPool { + return &MsgFundCommunityPool{ Amount: amount, - Depositor: depositor, + Depositor: depositor.String(), } } @@ -146,13 +140,17 @@ func (msg MsgFundCommunityPool) Type() string { return TypeMsgFundCommunityPool // GetSigners returns the signer addresses that are expected to sign the result // of GetSignBytes. func (msg MsgFundCommunityPool) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Depositor} + depoAddr, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + panic(err) + } + return []sdk.AccAddress{depoAddr} } // GetSignBytes returns the raw bytes for a MsgFundCommunityPool message that // the expected signer needs to sign. func (msg MsgFundCommunityPool) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } @@ -161,8 +159,8 @@ func (msg MsgFundCommunityPool) ValidateBasic() error { if !msg.Amount.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) } - if msg.Depositor.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Depositor.String()) + if msg.Depositor == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Depositor) } return nil diff --git a/x/distribution/types/params.go b/x/distribution/types/params.go index f4530dd81c3b..ecd04ece67c3 100644 --- a/x/distribution/types/params.go +++ b/x/distribution/types/params.go @@ -3,15 +3,10 @@ package types import ( "fmt" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -const ( - // default paramspace for params keeper - DefaultParamspace = ModuleName + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) // Parameter keys @@ -22,17 +17,9 @@ var ( ParamStoreKeyWithdrawAddrEnabled = []byte("withdrawaddrenabled") ) -// Params defines the set of distribution parameters. -type Params struct { - CommunityTax sdk.Dec `json:"community_tax" yaml:"community_tax"` - BaseProposerReward sdk.Dec `json:"base_proposer_reward" yaml:"base_proposer_reward"` - BonusProposerReward sdk.Dec `json:"bonus_proposer_reward" yaml:"bonus_proposer_reward"` - WithdrawAddrEnabled bool `json:"withdraw_addr_enabled" yaml:"withdraw_addr_enabled"` -} - // ParamKeyTable returns the parameter key table. -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable().RegisterParamSet(&Params{}) +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) } // DefaultParams returns default distribution parameters @@ -51,12 +38,12 @@ func (p Params) String() string { } // ParamSetPairs returns the parameter set pairs. -func (p *Params) ParamSetPairs() params.ParamSetPairs { - return params.ParamSetPairs{ - params.NewParamSetPair(ParamStoreKeyCommunityTax, &p.CommunityTax, validateCommunityTax), - params.NewParamSetPair(ParamStoreKeyBaseProposerReward, &p.BaseProposerReward, validateBaseProposerReward), - params.NewParamSetPair(ParamStoreKeyBonusProposerReward, &p.BonusProposerReward, validateBonusProposerReward), - params.NewParamSetPair(ParamStoreKeyWithdrawAddrEnabled, &p.WithdrawAddrEnabled, validateWithdrawAddrEnabled), +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(ParamStoreKeyCommunityTax, &p.CommunityTax, validateCommunityTax), + paramtypes.NewParamSetPair(ParamStoreKeyBaseProposerReward, &p.BaseProposerReward, validateBaseProposerReward), + paramtypes.NewParamSetPair(ParamStoreKeyBonusProposerReward, &p.BonusProposerReward, validateBonusProposerReward), + paramtypes.NewParamSetPair(ParamStoreKeyWithdrawAddrEnabled, &p.WithdrawAddrEnabled, validateWithdrawAddrEnabled), } } diff --git a/x/distribution/types/params_test.go b/x/distribution/types/params_test.go index 157041a65d3c..cf250160477a 100644 --- a/x/distribution/types/params_test.go +++ b/x/distribution/types/params_test.go @@ -12,8 +12,7 @@ func Test_validateAuxFuncs(t *testing.T) { type args struct { i interface{} } - - testCases := []struct { + tests := []struct { name string args args wantErr bool @@ -24,14 +23,12 @@ func Test_validateAuxFuncs(t *testing.T) { {"one dec", args{sdk.NewDec(1)}, false}, {"two dec", args{sdk.NewDec(2)}, true}, } - - for _, tc := range testCases { - stc := tc - - t.Run(stc.name, func(t *testing.T) { - require.Equal(t, stc.wantErr, validateCommunityTax(stc.args.i) != nil) - require.Equal(t, stc.wantErr, validateBaseProposerReward(stc.args.i) != nil) - require.Equal(t, stc.wantErr, validateBonusProposerReward(stc.args.i) != nil) + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.wantErr, validateCommunityTax(tt.args.i) != nil) + require.Equal(t, tt.wantErr, validateBaseProposerReward(tt.args.i) != nil) + require.Equal(t, tt.wantErr, validateBonusProposerReward(tt.args.i) != nil) }) } } diff --git a/x/distribution/types/proposal.go b/x/distribution/types/proposal.go index 931e111ba719..1a0d0a886ed7 100644 --- a/x/distribution/types/proposal.go +++ b/x/distribution/types/proposal.go @@ -14,40 +14,33 @@ const ( ) // Assert CommunityPoolSpendProposal implements govtypes.Content at compile-time -var _ govtypes.Content = CommunityPoolSpendProposal{} +var _ govtypes.Content = &CommunityPoolSpendProposal{} func init() { govtypes.RegisterProposalType(ProposalTypeCommunityPoolSpend) - govtypes.RegisterProposalTypeCodec(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal") -} - -// CommunityPoolSpendProposal spends from the community pool -type CommunityPoolSpendProposal struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` - Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` - Amount sdk.Coins `json:"amount" yaml:"amount"` + govtypes.RegisterProposalTypeCodec(&CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal") } // NewCommunityPoolSpendProposal creates a new community pool spned proposal. -func NewCommunityPoolSpendProposal(title, description string, recipient sdk.AccAddress, amount sdk.Coins) CommunityPoolSpendProposal { - return CommunityPoolSpendProposal{title, description, recipient, amount} +//nolint:interfacer +func NewCommunityPoolSpendProposal(title, description string, recipient sdk.AccAddress, amount sdk.Coins) *CommunityPoolSpendProposal { + return &CommunityPoolSpendProposal{title, description, recipient.String(), amount} } // GetTitle returns the title of a community pool spend proposal. -func (csp CommunityPoolSpendProposal) GetTitle() string { return csp.Title } +func (csp *CommunityPoolSpendProposal) GetTitle() string { return csp.Title } // GetDescription returns the description of a community pool spend proposal. -func (csp CommunityPoolSpendProposal) GetDescription() string { return csp.Description } +func (csp *CommunityPoolSpendProposal) GetDescription() string { return csp.Description } // GetDescription returns the routing key of a community pool spend proposal. -func (csp CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey } +func (csp *CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey } // ProposalType returns the type of a community pool spend proposal. -func (csp CommunityPoolSpendProposal) ProposalType() string { return ProposalTypeCommunityPoolSpend } +func (csp *CommunityPoolSpendProposal) ProposalType() string { return ProposalTypeCommunityPoolSpend } // ValidateBasic runs basic stateless validity checks -func (csp CommunityPoolSpendProposal) ValidateBasic() error { +func (csp *CommunityPoolSpendProposal) ValidateBasic() error { err := govtypes.ValidateAbstract(csp) if err != nil { return err @@ -55,7 +48,7 @@ func (csp CommunityPoolSpendProposal) ValidateBasic() error { if !csp.Amount.IsValid() { return ErrInvalidProposalAmount } - if csp.Recipient.Empty() { + if csp.Recipient == "" { return ErrEmptyProposalRecipient } diff --git a/x/distribution/types/querier.go b/x/distribution/types/querier.go index cbf7d8d0a426..fb9fd4677dca 100644 --- a/x/distribution/types/querier.go +++ b/x/distribution/types/querier.go @@ -1,6 +1,8 @@ package types -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // querier keys const ( diff --git a/x/distribution/types/query.go b/x/distribution/types/query.go index d286631409fa..caaf2b7860be 100644 --- a/x/distribution/types/query.go +++ b/x/distribution/types/query.go @@ -32,15 +32,9 @@ func (res QueryDelegatorTotalRewardsResponse) String() string { return strings.TrimSpace(out) } -// DelegationDelegatorReward defines the properties -// of a delegator's delegation reward. -type DelegationDelegatorReward struct { - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Reward sdk.DecCoins `json:"reward" yaml:"reward"` -} - // NewDelegationDelegatorReward constructs a DelegationDelegatorReward. +//nolint:interfacer func NewDelegationDelegatorReward(valAddr sdk.ValAddress, reward sdk.DecCoins) DelegationDelegatorReward { - return DelegationDelegatorReward{ValidatorAddress: valAddr, Reward: reward} + return DelegationDelegatorReward{ValidatorAddress: valAddr.String(), Reward: reward} } diff --git a/x/distribution/types/query.pb.go b/x/distribution/types/query.pb.go new file mode 100644 index 000000000000..47acd5e73c52 --- /dev/null +++ b/x/distribution/types/query.pb.go @@ -0,0 +1,3900 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/distribution/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/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" + _ "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 + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params defines the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryValidatorOutstandingRewardsRequest is the request type for the +// Query/ValidatorOutstandingRewards RPC method. +type QueryValidatorOutstandingRewardsRequest struct { + // validator_address defines the validator address to query for. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` +} + +func (m *QueryValidatorOutstandingRewardsRequest) Reset() { + *m = QueryValidatorOutstandingRewardsRequest{} +} +func (m *QueryValidatorOutstandingRewardsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorOutstandingRewardsRequest) ProtoMessage() {} +func (*QueryValidatorOutstandingRewardsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{2} +} +func (m *QueryValidatorOutstandingRewardsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorOutstandingRewardsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorOutstandingRewardsRequest.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 *QueryValidatorOutstandingRewardsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorOutstandingRewardsRequest.Merge(m, src) +} +func (m *QueryValidatorOutstandingRewardsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorOutstandingRewardsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorOutstandingRewardsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorOutstandingRewardsRequest proto.InternalMessageInfo + +func (m *QueryValidatorOutstandingRewardsRequest) GetValidatorAddress() string { + if m != nil { + return m.ValidatorAddress + } + return "" +} + +// QueryValidatorOutstandingRewardsResponse is the response type for the +// Query/ValidatorOutstandingRewards RPC method. +type QueryValidatorOutstandingRewardsResponse struct { + Rewards ValidatorOutstandingRewards `protobuf:"bytes,1,opt,name=rewards,proto3" json:"rewards"` +} + +func (m *QueryValidatorOutstandingRewardsResponse) Reset() { + *m = QueryValidatorOutstandingRewardsResponse{} +} +func (m *QueryValidatorOutstandingRewardsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorOutstandingRewardsResponse) ProtoMessage() {} +func (*QueryValidatorOutstandingRewardsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{3} +} +func (m *QueryValidatorOutstandingRewardsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorOutstandingRewardsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorOutstandingRewardsResponse.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 *QueryValidatorOutstandingRewardsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorOutstandingRewardsResponse.Merge(m, src) +} +func (m *QueryValidatorOutstandingRewardsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorOutstandingRewardsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorOutstandingRewardsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorOutstandingRewardsResponse proto.InternalMessageInfo + +func (m *QueryValidatorOutstandingRewardsResponse) GetRewards() ValidatorOutstandingRewards { + if m != nil { + return m.Rewards + } + return ValidatorOutstandingRewards{} +} + +// QueryValidatorCommissionRequest is the request type for the +// Query/ValidatorCommission RPC method +type QueryValidatorCommissionRequest struct { + // validator_address defines the validator address to query for. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` +} + +func (m *QueryValidatorCommissionRequest) Reset() { *m = QueryValidatorCommissionRequest{} } +func (m *QueryValidatorCommissionRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorCommissionRequest) ProtoMessage() {} +func (*QueryValidatorCommissionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{4} +} +func (m *QueryValidatorCommissionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorCommissionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorCommissionRequest.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 *QueryValidatorCommissionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorCommissionRequest.Merge(m, src) +} +func (m *QueryValidatorCommissionRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorCommissionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorCommissionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorCommissionRequest proto.InternalMessageInfo + +func (m *QueryValidatorCommissionRequest) GetValidatorAddress() string { + if m != nil { + return m.ValidatorAddress + } + return "" +} + +// QueryValidatorCommissionResponse is the response type for the +// Query/ValidatorCommission RPC method +type QueryValidatorCommissionResponse struct { + // commission defines the commision the validator received. + Commission ValidatorAccumulatedCommission `protobuf:"bytes,1,opt,name=commission,proto3" json:"commission"` +} + +func (m *QueryValidatorCommissionResponse) Reset() { *m = QueryValidatorCommissionResponse{} } +func (m *QueryValidatorCommissionResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorCommissionResponse) ProtoMessage() {} +func (*QueryValidatorCommissionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{5} +} +func (m *QueryValidatorCommissionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorCommissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorCommissionResponse.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 *QueryValidatorCommissionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorCommissionResponse.Merge(m, src) +} +func (m *QueryValidatorCommissionResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorCommissionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorCommissionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorCommissionResponse proto.InternalMessageInfo + +func (m *QueryValidatorCommissionResponse) GetCommission() ValidatorAccumulatedCommission { + if m != nil { + return m.Commission + } + return ValidatorAccumulatedCommission{} +} + +// QueryValidatorSlashesRequest is the request type for the +// Query/ValidatorSlashes RPC method +type QueryValidatorSlashesRequest struct { + // validator_address defines the validator address to query for. + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` + // starting_height defines the optional starting height to query the slashes. + StartingHeight uint64 `protobuf:"varint,2,opt,name=starting_height,json=startingHeight,proto3" json:"starting_height,omitempty"` + // starting_height defines the optional ending height to query the slashes. + EndingHeight uint64 `protobuf:"varint,3,opt,name=ending_height,json=endingHeight,proto3" json:"ending_height,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorSlashesRequest) Reset() { *m = QueryValidatorSlashesRequest{} } +func (m *QueryValidatorSlashesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSlashesRequest) ProtoMessage() {} +func (*QueryValidatorSlashesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{6} +} +func (m *QueryValidatorSlashesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSlashesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSlashesRequest.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 *QueryValidatorSlashesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSlashesRequest.Merge(m, src) +} +func (m *QueryValidatorSlashesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSlashesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSlashesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSlashesRequest proto.InternalMessageInfo + +// QueryValidatorSlashesResponse is the response type for the +// Query/ValidatorSlashes RPC method. +type QueryValidatorSlashesResponse struct { + // slashes defines the slashes the validator received. + Slashes []ValidatorSlashEvent `protobuf:"bytes,1,rep,name=slashes,proto3" json:"slashes"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorSlashesResponse) Reset() { *m = QueryValidatorSlashesResponse{} } +func (m *QueryValidatorSlashesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSlashesResponse) ProtoMessage() {} +func (*QueryValidatorSlashesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{7} +} +func (m *QueryValidatorSlashesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSlashesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSlashesResponse.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 *QueryValidatorSlashesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSlashesResponse.Merge(m, src) +} +func (m *QueryValidatorSlashesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSlashesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSlashesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSlashesResponse proto.InternalMessageInfo + +func (m *QueryValidatorSlashesResponse) GetSlashes() []ValidatorSlashEvent { + if m != nil { + return m.Slashes + } + return nil +} + +func (m *QueryValidatorSlashesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDelegationRewardsRequest is the request type for the +// Query/DelegationRewards RPC method. +type QueryDelegationRewardsRequest struct { + // delegator_address defines the delegator address to query for. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty"` + // validator_address defines the validator address to query for. + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` +} + +func (m *QueryDelegationRewardsRequest) Reset() { *m = QueryDelegationRewardsRequest{} } +func (m *QueryDelegationRewardsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationRewardsRequest) ProtoMessage() {} +func (*QueryDelegationRewardsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{8} +} +func (m *QueryDelegationRewardsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationRewardsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationRewardsRequest.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 *QueryDelegationRewardsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationRewardsRequest.Merge(m, src) +} +func (m *QueryDelegationRewardsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationRewardsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationRewardsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationRewardsRequest proto.InternalMessageInfo + +// QueryDelegationRewardsResponse is the response type for the +// Query/DelegationRewards RPC method. +type QueryDelegationRewardsResponse struct { + // rewards defines the rewards accrued by a delegation. + Rewards github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=rewards,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"rewards"` +} + +func (m *QueryDelegationRewardsResponse) Reset() { *m = QueryDelegationRewardsResponse{} } +func (m *QueryDelegationRewardsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationRewardsResponse) ProtoMessage() {} +func (*QueryDelegationRewardsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{9} +} +func (m *QueryDelegationRewardsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationRewardsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationRewardsResponse.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 *QueryDelegationRewardsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationRewardsResponse.Merge(m, src) +} +func (m *QueryDelegationRewardsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationRewardsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationRewardsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationRewardsResponse proto.InternalMessageInfo + +func (m *QueryDelegationRewardsResponse) GetRewards() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Rewards + } + return nil +} + +// QueryDelegationTotalRewardsRequest is the request type for the +// Query/DelegationTotalRewards RPC method. +type QueryDelegationTotalRewardsRequest struct { + // delegator_address defines the delegator address to query for. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty"` +} + +func (m *QueryDelegationTotalRewardsRequest) Reset() { *m = QueryDelegationTotalRewardsRequest{} } +func (m *QueryDelegationTotalRewardsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationTotalRewardsRequest) ProtoMessage() {} +func (*QueryDelegationTotalRewardsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{10} +} +func (m *QueryDelegationTotalRewardsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationTotalRewardsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationTotalRewardsRequest.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 *QueryDelegationTotalRewardsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationTotalRewardsRequest.Merge(m, src) +} +func (m *QueryDelegationTotalRewardsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationTotalRewardsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationTotalRewardsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationTotalRewardsRequest proto.InternalMessageInfo + +// QueryDelegationTotalRewardsResponse is the response type for the +// Query/DelegationTotalRewards RPC method. +type QueryDelegationTotalRewardsResponse struct { + // rewards defines all the rewards accrued by a delegator. + Rewards []DelegationDelegatorReward `protobuf:"bytes,1,rep,name=rewards,proto3" json:"rewards"` + // total defines the sum of all the rewards. + Total github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,2,rep,name=total,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"total"` +} + +func (m *QueryDelegationTotalRewardsResponse) Reset() { *m = QueryDelegationTotalRewardsResponse{} } +func (m *QueryDelegationTotalRewardsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationTotalRewardsResponse) ProtoMessage() {} +func (*QueryDelegationTotalRewardsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{11} +} +func (m *QueryDelegationTotalRewardsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationTotalRewardsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationTotalRewardsResponse.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 *QueryDelegationTotalRewardsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationTotalRewardsResponse.Merge(m, src) +} +func (m *QueryDelegationTotalRewardsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationTotalRewardsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationTotalRewardsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationTotalRewardsResponse proto.InternalMessageInfo + +func (m *QueryDelegationTotalRewardsResponse) GetRewards() []DelegationDelegatorReward { + if m != nil { + return m.Rewards + } + return nil +} + +func (m *QueryDelegationTotalRewardsResponse) GetTotal() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Total + } + return nil +} + +// QueryDelegatorValidatorsRequest is the request type for the +// Query/DelegatorValidators RPC method. +type QueryDelegatorValidatorsRequest struct { + // delegator_address defines the delegator address to query for. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty"` +} + +func (m *QueryDelegatorValidatorsRequest) Reset() { *m = QueryDelegatorValidatorsRequest{} } +func (m *QueryDelegatorValidatorsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorsRequest) ProtoMessage() {} +func (*QueryDelegatorValidatorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{12} +} +func (m *QueryDelegatorValidatorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorsRequest.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 *QueryDelegatorValidatorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorsRequest.Merge(m, src) +} +func (m *QueryDelegatorValidatorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorsRequest proto.InternalMessageInfo + +// QueryDelegatorValidatorsResponse is the response type for the +// Query/DelegatorValidators RPC method. +type QueryDelegatorValidatorsResponse struct { + // validators defines the validators a delegator is delegating for. + Validators []string `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators,omitempty"` +} + +func (m *QueryDelegatorValidatorsResponse) Reset() { *m = QueryDelegatorValidatorsResponse{} } +func (m *QueryDelegatorValidatorsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorsResponse) ProtoMessage() {} +func (*QueryDelegatorValidatorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{13} +} +func (m *QueryDelegatorValidatorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorsResponse.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 *QueryDelegatorValidatorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorsResponse.Merge(m, src) +} +func (m *QueryDelegatorValidatorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorsResponse proto.InternalMessageInfo + +// QueryDelegatorWithdrawAddressRequest is the request type for the +// Query/DelegatorWithdrawAddress RPC method. +type QueryDelegatorWithdrawAddressRequest struct { + // delegator_address defines the delegator address to query for. + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty"` +} + +func (m *QueryDelegatorWithdrawAddressRequest) Reset() { *m = QueryDelegatorWithdrawAddressRequest{} } +func (m *QueryDelegatorWithdrawAddressRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorWithdrawAddressRequest) ProtoMessage() {} +func (*QueryDelegatorWithdrawAddressRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{14} +} +func (m *QueryDelegatorWithdrawAddressRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorWithdrawAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorWithdrawAddressRequest.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 *QueryDelegatorWithdrawAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorWithdrawAddressRequest.Merge(m, src) +} +func (m *QueryDelegatorWithdrawAddressRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorWithdrawAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorWithdrawAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorWithdrawAddressRequest proto.InternalMessageInfo + +// QueryDelegatorWithdrawAddressResponse is the response type for the +// Query/DelegatorWithdrawAddress RPC method. +type QueryDelegatorWithdrawAddressResponse struct { + // withdraw_address defines the delegator address to query for. + WithdrawAddress string `protobuf:"bytes,1,opt,name=withdraw_address,json=withdrawAddress,proto3" json:"withdraw_address,omitempty"` +} + +func (m *QueryDelegatorWithdrawAddressResponse) Reset() { *m = QueryDelegatorWithdrawAddressResponse{} } +func (m *QueryDelegatorWithdrawAddressResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorWithdrawAddressResponse) ProtoMessage() {} +func (*QueryDelegatorWithdrawAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{15} +} +func (m *QueryDelegatorWithdrawAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorWithdrawAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorWithdrawAddressResponse.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 *QueryDelegatorWithdrawAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorWithdrawAddressResponse.Merge(m, src) +} +func (m *QueryDelegatorWithdrawAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorWithdrawAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorWithdrawAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorWithdrawAddressResponse proto.InternalMessageInfo + +// QueryCommunityPoolRequest is the request type for the Query/CommunityPool RPC +// method. +type QueryCommunityPoolRequest struct { +} + +func (m *QueryCommunityPoolRequest) Reset() { *m = QueryCommunityPoolRequest{} } +func (m *QueryCommunityPoolRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCommunityPoolRequest) ProtoMessage() {} +func (*QueryCommunityPoolRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{16} +} +func (m *QueryCommunityPoolRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCommunityPoolRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCommunityPoolRequest.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 *QueryCommunityPoolRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCommunityPoolRequest.Merge(m, src) +} +func (m *QueryCommunityPoolRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCommunityPoolRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCommunityPoolRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCommunityPoolRequest proto.InternalMessageInfo + +// QueryCommunityPoolResponse is the response type for the Query/CommunityPool +// RPC method. +type QueryCommunityPoolResponse struct { + // pool defines community pool's coins. + Pool github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=pool,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"pool"` +} + +func (m *QueryCommunityPoolResponse) Reset() { *m = QueryCommunityPoolResponse{} } +func (m *QueryCommunityPoolResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCommunityPoolResponse) ProtoMessage() {} +func (*QueryCommunityPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5efd02cbc06efdc9, []int{17} +} +func (m *QueryCommunityPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCommunityPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCommunityPoolResponse.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 *QueryCommunityPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCommunityPoolResponse.Merge(m, src) +} +func (m *QueryCommunityPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCommunityPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCommunityPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCommunityPoolResponse proto.InternalMessageInfo + +func (m *QueryCommunityPoolResponse) GetPool() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.Pool + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.distribution.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.distribution.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryValidatorOutstandingRewardsRequest)(nil), "cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsRequest") + proto.RegisterType((*QueryValidatorOutstandingRewardsResponse)(nil), "cosmos.distribution.v1beta1.QueryValidatorOutstandingRewardsResponse") + proto.RegisterType((*QueryValidatorCommissionRequest)(nil), "cosmos.distribution.v1beta1.QueryValidatorCommissionRequest") + proto.RegisterType((*QueryValidatorCommissionResponse)(nil), "cosmos.distribution.v1beta1.QueryValidatorCommissionResponse") + proto.RegisterType((*QueryValidatorSlashesRequest)(nil), "cosmos.distribution.v1beta1.QueryValidatorSlashesRequest") + proto.RegisterType((*QueryValidatorSlashesResponse)(nil), "cosmos.distribution.v1beta1.QueryValidatorSlashesResponse") + proto.RegisterType((*QueryDelegationRewardsRequest)(nil), "cosmos.distribution.v1beta1.QueryDelegationRewardsRequest") + proto.RegisterType((*QueryDelegationRewardsResponse)(nil), "cosmos.distribution.v1beta1.QueryDelegationRewardsResponse") + proto.RegisterType((*QueryDelegationTotalRewardsRequest)(nil), "cosmos.distribution.v1beta1.QueryDelegationTotalRewardsRequest") + proto.RegisterType((*QueryDelegationTotalRewardsResponse)(nil), "cosmos.distribution.v1beta1.QueryDelegationTotalRewardsResponse") + proto.RegisterType((*QueryDelegatorValidatorsRequest)(nil), "cosmos.distribution.v1beta1.QueryDelegatorValidatorsRequest") + proto.RegisterType((*QueryDelegatorValidatorsResponse)(nil), "cosmos.distribution.v1beta1.QueryDelegatorValidatorsResponse") + proto.RegisterType((*QueryDelegatorWithdrawAddressRequest)(nil), "cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressRequest") + proto.RegisterType((*QueryDelegatorWithdrawAddressResponse)(nil), "cosmos.distribution.v1beta1.QueryDelegatorWithdrawAddressResponse") + proto.RegisterType((*QueryCommunityPoolRequest)(nil), "cosmos.distribution.v1beta1.QueryCommunityPoolRequest") + proto.RegisterType((*QueryCommunityPoolResponse)(nil), "cosmos.distribution.v1beta1.QueryCommunityPoolResponse") +} + +func init() { + proto.RegisterFile("cosmos/distribution/v1beta1/query.proto", fileDescriptor_5efd02cbc06efdc9) +} + +var fileDescriptor_5efd02cbc06efdc9 = []byte{ + // 1101 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x98, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xc7, 0x3d, 0x6e, 0xda, 0xd2, 0x57, 0x4a, 0xd2, 0x69, 0x85, 0xcc, 0x26, 0xd8, 0xd1, 0x86, + 0x92, 0x40, 0x54, 0x6f, 0x93, 0x48, 0x05, 0x5a, 0x10, 0xe4, 0x57, 0xa9, 0xd4, 0x2a, 0x4d, 0x4d, + 0x95, 0x84, 0x5f, 0x8a, 0x26, 0xde, 0xd1, 0x7a, 0x55, 0x7b, 0xc7, 0xdd, 0x19, 0x27, 0x44, 0x55, + 0x2f, 0x04, 0x24, 0x2e, 0x48, 0x48, 0x5c, 0x7a, 0xcc, 0x99, 0x3b, 0x17, 0xfe, 0x82, 0x1e, 0x2b, + 0x21, 0xa1, 0x9e, 0x00, 0x25, 0x08, 0x55, 0x42, 0x9c, 0xb9, 0x22, 0xcf, 0xcc, 0xda, 0xbb, 0xf6, + 0x7a, 0xfd, 0x4b, 0x3d, 0xc5, 0x7a, 0x3b, 0xef, 0x3b, 0xef, 0xf3, 0x66, 0xde, 0xbc, 0xa7, 0xc0, + 0x74, 0x91, 0xf1, 0x0a, 0xe3, 0x96, 0xed, 0x72, 0xe1, 0xbb, 0x3b, 0x35, 0xe1, 0x32, 0xcf, 0xda, + 0x9d, 0xdb, 0xa1, 0x82, 0xcc, 0x59, 0x0f, 0x6a, 0xd4, 0xdf, 0xcf, 0x57, 0x7d, 0x26, 0x18, 0x1e, + 0x57, 0x0b, 0xf3, 0xe1, 0x85, 0x79, 0xbd, 0xd0, 0x78, 0x5b, 0xab, 0xec, 0x10, 0x4e, 0x95, 0x57, + 0x43, 0xa3, 0x4a, 0x1c, 0xd7, 0x23, 0x72, 0xb5, 0x14, 0x32, 0x2e, 0x3a, 0xcc, 0x61, 0xf2, 0xa7, + 0x55, 0xff, 0xa5, 0xad, 0x13, 0x0e, 0x63, 0x4e, 0x99, 0x5a, 0xa4, 0xea, 0x5a, 0xc4, 0xf3, 0x98, + 0x90, 0x2e, 0x5c, 0x7f, 0xcd, 0x86, 0xf5, 0x03, 0xe5, 0x22, 0x73, 0x03, 0xcd, 0x7c, 0x12, 0x45, + 0x24, 0x62, 0xb9, 0xde, 0xbc, 0x08, 0xf8, 0x6e, 0x3d, 0xca, 0x75, 0xe2, 0x93, 0x0a, 0x2f, 0xd0, + 0x07, 0x35, 0xca, 0x85, 0xb9, 0x05, 0x17, 0x22, 0x56, 0x5e, 0x65, 0x1e, 0xa7, 0x78, 0x11, 0x4e, + 0x55, 0xa5, 0x25, 0x83, 0x26, 0xd1, 0xcc, 0xd9, 0xf9, 0xa9, 0x7c, 0x42, 0x2a, 0xf2, 0xca, 0x79, + 0x69, 0xe4, 0xc9, 0xef, 0xb9, 0x54, 0x41, 0x3b, 0x9a, 0x1b, 0x30, 0x2d, 0x95, 0x37, 0x48, 0xd9, + 0xb5, 0x89, 0x60, 0xfe, 0x9d, 0x9a, 0xe0, 0x82, 0x78, 0xb6, 0xeb, 0x39, 0x05, 0xba, 0x47, 0x7c, + 0x3b, 0x08, 0x02, 0xcf, 0xc2, 0xf9, 0xdd, 0x60, 0xd5, 0x36, 0xb1, 0x6d, 0x9f, 0x72, 0xb5, 0xf1, + 0x99, 0xc2, 0x58, 0xe3, 0xc3, 0xa2, 0xb2, 0x9b, 0xdf, 0x20, 0x98, 0xe9, 0x2e, 0xac, 0x39, 0xb6, + 0xe0, 0xb4, 0xaf, 0x4c, 0x1a, 0xe4, 0xdd, 0x44, 0x90, 0x04, 0x49, 0x4d, 0x17, 0xc8, 0x99, 0x6b, + 0x90, 0x8b, 0x46, 0xb1, 0xcc, 0x2a, 0x15, 0x97, 0x73, 0x97, 0x79, 0x03, 0x61, 0x7d, 0x8b, 0x60, + 0xb2, 0xb3, 0xa0, 0xc6, 0x21, 0x00, 0xc5, 0x86, 0x55, 0x13, 0x5d, 0xef, 0x8d, 0x68, 0xb1, 0x58, + 0xac, 0x55, 0x6a, 0x65, 0x22, 0xa8, 0xdd, 0x14, 0xd6, 0x50, 0x21, 0x51, 0xf3, 0x1f, 0x04, 0x13, + 0xd1, 0x38, 0x3e, 0x29, 0x13, 0x5e, 0xa2, 0x03, 0x1d, 0x16, 0x9e, 0x86, 0x51, 0x2e, 0x88, 0x2f, + 0x5c, 0xcf, 0xd9, 0x2e, 0x51, 0xd7, 0x29, 0x89, 0x4c, 0x7a, 0x12, 0xcd, 0x8c, 0x14, 0x5e, 0x09, + 0xcc, 0x37, 0xa5, 0x15, 0x4f, 0xc1, 0x39, 0x2a, 0xd3, 0x1d, 0x2c, 0x3b, 0x21, 0x97, 0xbd, 0xac, + 0x8c, 0x7a, 0xd1, 0x0d, 0x80, 0x66, 0x69, 0x65, 0x46, 0x24, 0xfe, 0x9b, 0x01, 0x7e, 0xbd, 0x4e, + 0xf2, 0xaa, 0x7a, 0x9b, 0xf7, 0xd2, 0xa1, 0x3a, 0xec, 0x42, 0xc8, 0xf3, 0xda, 0x4b, 0xdf, 0x1d, + 0xe6, 0x52, 0x8f, 0x0f, 0x73, 0xc8, 0xfc, 0x05, 0xc1, 0xeb, 0x1d, 0x68, 0x75, 0xca, 0xd7, 0xe1, + 0x34, 0x57, 0xa6, 0x0c, 0x9a, 0x3c, 0x31, 0x73, 0x76, 0xfe, 0x4a, 0x6f, 0xf9, 0x96, 0x3a, 0xab, + 0xbb, 0xd4, 0x13, 0xc1, 0xcd, 0xd1, 0x32, 0xf8, 0xe3, 0x08, 0x45, 0x5a, 0x52, 0x4c, 0x77, 0xa5, + 0x50, 0xe1, 0x84, 0x31, 0xcc, 0x83, 0x20, 0xf8, 0x15, 0x5a, 0xa6, 0x8e, 0xb4, 0xb5, 0x17, 0x96, + 0xad, 0xbe, 0xb5, 0x9f, 0x55, 0xe3, 0x43, 0x70, 0x56, 0xb1, 0x07, 0x9b, 0x8e, 0x3f, 0x58, 0x95, + 0xc2, 0xe7, 0x87, 0xb9, 0x94, 0xf9, 0x3d, 0x82, 0x6c, 0xa7, 0x28, 0x74, 0x0e, 0xef, 0x87, 0xab, + 0xb0, 0x9e, 0xc3, 0x89, 0x08, 0x6e, 0x00, 0xba, 0x42, 0x8b, 0xcb, 0xcc, 0xf5, 0x96, 0x16, 0xea, + 0xf9, 0xfa, 0xe9, 0x8f, 0xdc, 0xac, 0xe3, 0x8a, 0x52, 0x6d, 0x27, 0x5f, 0x64, 0x15, 0x4b, 0x3f, + 0x76, 0xea, 0xcf, 0x65, 0x6e, 0xdf, 0xb7, 0xc4, 0x7e, 0x95, 0xf2, 0xc0, 0x87, 0x37, 0x0b, 0xf3, + 0x73, 0x30, 0x5b, 0xc2, 0xb9, 0xc7, 0x04, 0x29, 0x0f, 0x91, 0x99, 0x10, 0xec, 0xdf, 0x08, 0xa6, + 0x12, 0xd5, 0x35, 0xf1, 0x46, 0x2b, 0xf1, 0xd5, 0xc4, 0x5b, 0xd3, 0x54, 0x5b, 0x09, 0xf6, 0x56, + 0x8a, 0x2d, 0xaf, 0x0e, 0x76, 0xe0, 0xa4, 0xa8, 0xef, 0x97, 0x49, 0xbf, 0xa8, 0x3c, 0x2a, 0x7d, + 0x73, 0x4b, 0x3f, 0x6f, 0x8d, 0x78, 0x1a, 0x17, 0x7b, 0xd8, 0x14, 0xde, 0xd6, 0xef, 0x5c, 0xac, + 0xb2, 0x4e, 0x5f, 0x16, 0xa0, 0x71, 0xe3, 0x54, 0x06, 0xcf, 0x14, 0x42, 0x96, 0x90, 0xda, 0x97, + 0xf0, 0x46, 0x54, 0x6d, 0xd3, 0x15, 0x25, 0xdb, 0x27, 0x7b, 0x7a, 0xe3, 0x21, 0x83, 0xfd, 0x02, + 0x2e, 0x75, 0x91, 0xd7, 0x11, 0xbf, 0x05, 0x63, 0x7b, 0xfa, 0x53, 0x8b, 0xfc, 0xe8, 0x5e, 0xd4, + 0x25, 0xa4, 0x3e, 0x0e, 0xaf, 0x49, 0xf5, 0xfa, 0x83, 0x5c, 0xf3, 0x5c, 0xb1, 0xbf, 0xce, 0x58, + 0x39, 0xe8, 0xcc, 0x07, 0x08, 0x8c, 0xb8, 0xaf, 0x7a, 0x43, 0x0a, 0x23, 0x55, 0xc6, 0xca, 0x2f, + 0xae, 0xa0, 0xa4, 0xfc, 0xfc, 0xb3, 0x51, 0x38, 0x29, 0xa3, 0xc0, 0x8f, 0x11, 0x9c, 0x52, 0x8d, + 0x1e, 0x5b, 0x89, 0x97, 0xb9, 0x7d, 0xca, 0x30, 0xae, 0xf4, 0xee, 0xa0, 0xf0, 0xcc, 0xd9, 0xaf, + 0x7f, 0xfd, 0xeb, 0xc7, 0xf4, 0x25, 0x3c, 0x65, 0x25, 0x8d, 0x39, 0x6a, 0xd4, 0xc0, 0x07, 0x69, + 0x18, 0x4f, 0x68, 0xdd, 0x78, 0xa5, 0xfb, 0xf6, 0xdd, 0xa7, 0x14, 0x63, 0x75, 0x48, 0x15, 0x4d, + 0xb6, 0x29, 0xc9, 0xee, 0xe2, 0x3b, 0x89, 0x64, 0xcd, 0xcb, 0x6e, 0x3d, 0x6c, 0x7b, 0x95, 0x1f, + 0x59, 0xac, 0xa9, 0xbf, 0x1d, 0xbc, 0x0d, 0x47, 0x08, 0x2e, 0xc4, 0x0c, 0x0f, 0xf8, 0xfd, 0x3e, + 0xe2, 0x6e, 0x1b, 0x62, 0x8c, 0x0f, 0x06, 0xf4, 0xd6, 0xb4, 0x6b, 0x92, 0xf6, 0x26, 0xbe, 0x31, + 0x0c, 0x6d, 0x73, 0x3c, 0xc1, 0xbf, 0x21, 0x18, 0x6b, 0xed, 0xd5, 0xf8, 0xbd, 0x3e, 0x62, 0x8c, + 0x4e, 0x33, 0xc6, 0xb5, 0x41, 0x5c, 0x35, 0xdb, 0x2d, 0xc9, 0xb6, 0x8a, 0x97, 0x87, 0x61, 0x0b, + 0xa6, 0x82, 0x7f, 0x11, 0x9c, 0x6f, 0xeb, 0xa0, 0xb8, 0x87, 0xf0, 0x3a, 0x35, 0x7f, 0xe3, 0xfa, + 0x40, 0xbe, 0x9a, 0x6d, 0x5b, 0xb2, 0x7d, 0x8a, 0x37, 0x13, 0xd9, 0x1a, 0x2f, 0x27, 0xb7, 0x1e, + 0xb6, 0x3d, 0xaf, 0x8f, 0x2c, 0x7d, 0x33, 0xe3, 0xb8, 0xf1, 0x73, 0x04, 0xaf, 0xc6, 0x37, 0x51, + 0xfc, 0x61, 0x3f, 0x81, 0xc7, 0x34, 0x77, 0xe3, 0xa3, 0xc1, 0x05, 0xfa, 0x3a, 0xda, 0xde, 0xf0, + 0x65, 0x61, 0xc6, 0x74, 0xbb, 0x5e, 0x0a, 0xb3, 0x73, 0xfb, 0xed, 0xa5, 0x30, 0x13, 0x5a, 0x6c, + 0x8f, 0x85, 0xd9, 0x85, 0xb0, 0x79, 0xb7, 0xf1, 0x7f, 0x08, 0x32, 0x9d, 0xba, 0x24, 0x5e, 0xec, + 0x23, 0xd6, 0xf8, 0x06, 0x6e, 0x2c, 0x0d, 0x23, 0xa1, 0x99, 0xef, 0x49, 0xe6, 0x35, 0x7c, 0x7b, + 0x18, 0xe6, 0xd6, 0x36, 0x8f, 0x7f, 0x46, 0x70, 0x2e, 0xd2, 0xa3, 0xf1, 0xd5, 0xee, 0xb1, 0xc6, + 0xb5, 0x7c, 0xe3, 0x9d, 0xbe, 0xfd, 0x34, 0xd8, 0x82, 0x04, 0xbb, 0x8c, 0x67, 0x13, 0xc1, 0x8a, + 0x81, 0xef, 0x76, 0xbd, 0xb5, 0x2f, 0xdd, 0x7a, 0x72, 0x94, 0x45, 0x4f, 0x8f, 0xb2, 0xe8, 0xcf, + 0xa3, 0x2c, 0xfa, 0xe1, 0x38, 0x9b, 0x7a, 0x7a, 0x9c, 0x4d, 0x3d, 0x3b, 0xce, 0xa6, 0x3e, 0x9b, + 0x4b, 0x9c, 0x13, 0xbe, 0x8a, 0xaa, 0xcb, 0xb1, 0x61, 0xe7, 0x94, 0xfc, 0x27, 0xc3, 0xc2, 0xff, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x68, 0x0b, 0x91, 0xae, 0x5c, 0x11, 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 { + // Params queries params of the distribution module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // ValidatorOutstandingRewards queries rewards of a validator address. + ValidatorOutstandingRewards(ctx context.Context, in *QueryValidatorOutstandingRewardsRequest, opts ...grpc.CallOption) (*QueryValidatorOutstandingRewardsResponse, error) + // ValidatorCommission queries accumulated commission for a validator. + ValidatorCommission(ctx context.Context, in *QueryValidatorCommissionRequest, opts ...grpc.CallOption) (*QueryValidatorCommissionResponse, error) + // ValidatorSlashes queries slash events of a validator. + ValidatorSlashes(ctx context.Context, in *QueryValidatorSlashesRequest, opts ...grpc.CallOption) (*QueryValidatorSlashesResponse, error) + // DelegationRewards queries the total rewards accrued by a delegation. + DelegationRewards(ctx context.Context, in *QueryDelegationRewardsRequest, opts ...grpc.CallOption) (*QueryDelegationRewardsResponse, error) + // DelegationTotalRewards queries the total rewards accrued by a each + // validator. + DelegationTotalRewards(ctx context.Context, in *QueryDelegationTotalRewardsRequest, opts ...grpc.CallOption) (*QueryDelegationTotalRewardsResponse, error) + // DelegatorValidators queries the validators of a delegator. + DelegatorValidators(ctx context.Context, in *QueryDelegatorValidatorsRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorsResponse, error) + // DelegatorWithdrawAddress queries withdraw address of a delegator. + DelegatorWithdrawAddress(ctx context.Context, in *QueryDelegatorWithdrawAddressRequest, opts ...grpc.CallOption) (*QueryDelegatorWithdrawAddressResponse, error) + // CommunityPool queries the community pool coins. + CommunityPool(ctx context.Context, in *QueryCommunityPoolRequest, opts ...grpc.CallOption) (*QueryCommunityPoolResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorOutstandingRewards(ctx context.Context, in *QueryValidatorOutstandingRewardsRequest, opts ...grpc.CallOption) (*QueryValidatorOutstandingRewardsResponse, error) { + out := new(QueryValidatorOutstandingRewardsResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorCommission(ctx context.Context, in *QueryValidatorCommissionRequest, opts ...grpc.CallOption) (*QueryValidatorCommissionResponse, error) { + out := new(QueryValidatorCommissionResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/ValidatorCommission", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorSlashes(ctx context.Context, in *QueryValidatorSlashesRequest, opts ...grpc.CallOption) (*QueryValidatorSlashesResponse, error) { + out := new(QueryValidatorSlashesResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/ValidatorSlashes", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegationRewards(ctx context.Context, in *QueryDelegationRewardsRequest, opts ...grpc.CallOption) (*QueryDelegationRewardsResponse, error) { + out := new(QueryDelegationRewardsResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/DelegationRewards", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegationTotalRewards(ctx context.Context, in *QueryDelegationTotalRewardsRequest, opts ...grpc.CallOption) (*QueryDelegationTotalRewardsResponse, error) { + out := new(QueryDelegationTotalRewardsResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/DelegationTotalRewards", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorValidators(ctx context.Context, in *QueryDelegatorValidatorsRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorsResponse, error) { + out := new(QueryDelegatorValidatorsResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/DelegatorValidators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorWithdrawAddress(ctx context.Context, in *QueryDelegatorWithdrawAddressRequest, opts ...grpc.CallOption) (*QueryDelegatorWithdrawAddressResponse, error) { + out := new(QueryDelegatorWithdrawAddressResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/DelegatorWithdrawAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) CommunityPool(ctx context.Context, in *QueryCommunityPoolRequest, opts ...grpc.CallOption) (*QueryCommunityPoolResponse, error) { + out := new(QueryCommunityPoolResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Query/CommunityPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries params of the distribution module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // ValidatorOutstandingRewards queries rewards of a validator address. + ValidatorOutstandingRewards(context.Context, *QueryValidatorOutstandingRewardsRequest) (*QueryValidatorOutstandingRewardsResponse, error) + // ValidatorCommission queries accumulated commission for a validator. + ValidatorCommission(context.Context, *QueryValidatorCommissionRequest) (*QueryValidatorCommissionResponse, error) + // ValidatorSlashes queries slash events of a validator. + ValidatorSlashes(context.Context, *QueryValidatorSlashesRequest) (*QueryValidatorSlashesResponse, error) + // DelegationRewards queries the total rewards accrued by a delegation. + DelegationRewards(context.Context, *QueryDelegationRewardsRequest) (*QueryDelegationRewardsResponse, error) + // DelegationTotalRewards queries the total rewards accrued by a each + // validator. + DelegationTotalRewards(context.Context, *QueryDelegationTotalRewardsRequest) (*QueryDelegationTotalRewardsResponse, error) + // DelegatorValidators queries the validators of a delegator. + DelegatorValidators(context.Context, *QueryDelegatorValidatorsRequest) (*QueryDelegatorValidatorsResponse, error) + // DelegatorWithdrawAddress queries withdraw address of a delegator. + DelegatorWithdrawAddress(context.Context, *QueryDelegatorWithdrawAddressRequest) (*QueryDelegatorWithdrawAddressResponse, error) + // CommunityPool queries the community pool coins. + CommunityPool(context.Context, *QueryCommunityPoolRequest) (*QueryCommunityPoolResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) ValidatorOutstandingRewards(ctx context.Context, req *QueryValidatorOutstandingRewardsRequest) (*QueryValidatorOutstandingRewardsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorOutstandingRewards not implemented") +} +func (*UnimplementedQueryServer) ValidatorCommission(ctx context.Context, req *QueryValidatorCommissionRequest) (*QueryValidatorCommissionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorCommission not implemented") +} +func (*UnimplementedQueryServer) ValidatorSlashes(ctx context.Context, req *QueryValidatorSlashesRequest) (*QueryValidatorSlashesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorSlashes not implemented") +} +func (*UnimplementedQueryServer) DelegationRewards(ctx context.Context, req *QueryDelegationRewardsRequest) (*QueryDelegationRewardsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegationRewards not implemented") +} +func (*UnimplementedQueryServer) DelegationTotalRewards(ctx context.Context, req *QueryDelegationTotalRewardsRequest) (*QueryDelegationTotalRewardsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegationTotalRewards not implemented") +} +func (*UnimplementedQueryServer) DelegatorValidators(ctx context.Context, req *QueryDelegatorValidatorsRequest) (*QueryDelegatorValidatorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorValidators not implemented") +} +func (*UnimplementedQueryServer) DelegatorWithdrawAddress(ctx context.Context, req *QueryDelegatorWithdrawAddressRequest) (*QueryDelegatorWithdrawAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorWithdrawAddress not implemented") +} +func (*UnimplementedQueryServer) CommunityPool(ctx context.Context, req *QueryCommunityPoolRequest) (*QueryCommunityPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CommunityPool not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorOutstandingRewards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorOutstandingRewardsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorOutstandingRewards(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorOutstandingRewards(ctx, req.(*QueryValidatorOutstandingRewardsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorCommission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorCommissionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorCommission(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/ValidatorCommission", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorCommission(ctx, req.(*QueryValidatorCommissionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorSlashes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorSlashesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorSlashes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/ValidatorSlashes", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorSlashes(ctx, req.(*QueryValidatorSlashesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegationRewards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegationRewardsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegationRewards(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/DelegationRewards", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegationRewards(ctx, req.(*QueryDelegationRewardsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegationTotalRewards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegationTotalRewardsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegationTotalRewards(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/DelegationTotalRewards", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegationTotalRewards(ctx, req.(*QueryDelegationTotalRewardsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorValidators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorValidatorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorValidators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/DelegatorValidators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorValidators(ctx, req.(*QueryDelegatorValidatorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorWithdrawAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorWithdrawAddressRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorWithdrawAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/DelegatorWithdrawAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorWithdrawAddress(ctx, req.(*QueryDelegatorWithdrawAddressRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_CommunityPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCommunityPoolRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CommunityPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Query/CommunityPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CommunityPool(ctx, req.(*QueryCommunityPoolRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.distribution.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "ValidatorOutstandingRewards", + Handler: _Query_ValidatorOutstandingRewards_Handler, + }, + { + MethodName: "ValidatorCommission", + Handler: _Query_ValidatorCommission_Handler, + }, + { + MethodName: "ValidatorSlashes", + Handler: _Query_ValidatorSlashes_Handler, + }, + { + MethodName: "DelegationRewards", + Handler: _Query_DelegationRewards_Handler, + }, + { + MethodName: "DelegationTotalRewards", + Handler: _Query_DelegationTotalRewards_Handler, + }, + { + MethodName: "DelegatorValidators", + Handler: _Query_DelegatorValidators_Handler, + }, + { + MethodName: "DelegatorWithdrawAddress", + Handler: _Query_DelegatorWithdrawAddress_Handler, + }, + { + MethodName: "CommunityPool", + Handler: _Query_CommunityPool_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/distribution/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *QueryValidatorOutstandingRewardsRequest) 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 *QueryValidatorOutstandingRewardsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorOutstandingRewardsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorOutstandingRewardsResponse) 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 *QueryValidatorOutstandingRewardsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorOutstandingRewardsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Rewards.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 *QueryValidatorCommissionRequest) 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 *QueryValidatorCommissionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorCommissionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorCommissionResponse) 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 *QueryValidatorCommissionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorCommissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Commission.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 *QueryValidatorSlashesRequest) 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 *QueryValidatorSlashesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSlashesRequest) 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] = 0x22 + } + if m.EndingHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.EndingHeight)) + i-- + dAtA[i] = 0x18 + } + if m.StartingHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.StartingHeight)) + i-- + dAtA[i] = 0x10 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorSlashesResponse) 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 *QueryValidatorSlashesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSlashesResponse) 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.Slashes) > 0 { + for iNdEx := len(m.Slashes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Slashes[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 *QueryDelegationRewardsRequest) 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 *QueryDelegationRewardsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationRewardsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegationRewardsResponse) 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 *QueryDelegationRewardsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationRewardsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[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 *QueryDelegationTotalRewardsRequest) 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 *QueryDelegationTotalRewardsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationTotalRewardsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegationTotalRewardsResponse) 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 *QueryDelegationTotalRewardsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationTotalRewardsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Total) > 0 { + for iNdEx := len(m.Total) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Total[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Rewards) > 0 { + for iNdEx := len(m.Rewards) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rewards[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 *QueryDelegatorValidatorsRequest) 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 *QueryDelegatorValidatorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorValidatorsResponse) 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 *QueryDelegatorValidatorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Validators[iNdEx]) + copy(dAtA[i:], m.Validators[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Validators[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorWithdrawAddressRequest) 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 *QueryDelegatorWithdrawAddressRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorWithdrawAddressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorWithdrawAddressResponse) 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 *QueryDelegatorWithdrawAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorWithdrawAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WithdrawAddress) > 0 { + i -= len(m.WithdrawAddress) + copy(dAtA[i:], m.WithdrawAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.WithdrawAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCommunityPoolRequest) 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 *QueryCommunityPoolRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCommunityPoolRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryCommunityPoolResponse) 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 *QueryCommunityPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCommunityPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pool) > 0 { + for iNdEx := len(m.Pool) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pool[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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryValidatorOutstandingRewardsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorOutstandingRewardsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Rewards.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryValidatorCommissionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorCommissionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Commission.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryValidatorSlashesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.StartingHeight != 0 { + n += 1 + sovQuery(uint64(m.StartingHeight)) + } + if m.EndingHeight != 0 { + n += 1 + sovQuery(uint64(m.EndingHeight)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorSlashesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Slashes) > 0 { + for _, e := range m.Slashes { + 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 *QueryDelegationRewardsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegationRewardsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryDelegationTotalRewardsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegationTotalRewardsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Rewards) > 0 { + for _, e := range m.Rewards { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if len(m.Total) > 0 { + for _, e := range m.Total { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryDelegatorValidatorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegatorValidatorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Validators) > 0 { + for _, s := range m.Validators { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *QueryDelegatorWithdrawAddressRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegatorWithdrawAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.WithdrawAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCommunityPoolRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryCommunityPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pool) > 0 { + for _, e := range m.Pool { + l = e.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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 *QueryValidatorOutstandingRewardsRequest) 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: QueryValidatorOutstandingRewardsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorOutstandingRewardsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = 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 *QueryValidatorOutstandingRewardsResponse) 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: QueryValidatorOutstandingRewardsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorOutstandingRewardsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", 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 err := m.Rewards.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 *QueryValidatorCommissionRequest) 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: QueryValidatorCommissionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorCommissionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = 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 *QueryValidatorCommissionResponse) 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: QueryValidatorCommissionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorCommissionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", 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 err := m.Commission.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 *QueryValidatorSlashesRequest) 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: QueryValidatorSlashesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSlashesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartingHeight", wireType) + } + m.StartingHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartingHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EndingHeight", wireType) + } + m.EndingHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EndingHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + 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 *QueryValidatorSlashesResponse) 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: QueryValidatorSlashesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSlashesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Slashes", 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.Slashes = append(m.Slashes, ValidatorSlashEvent{}) + if err := m.Slashes[len(m.Slashes)-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 *QueryDelegationRewardsRequest) 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: QueryDelegationRewardsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationRewardsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = 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 *QueryDelegationRewardsResponse) 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: QueryDelegationRewardsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationRewardsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", 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.Rewards = append(m.Rewards, types.DecCoin{}) + if err := m.Rewards[len(m.Rewards)-1].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 *QueryDelegationTotalRewardsRequest) 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: QueryDelegationTotalRewardsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationTotalRewardsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = 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 *QueryDelegationTotalRewardsResponse) 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: QueryDelegationTotalRewardsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationTotalRewardsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rewards", 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.Rewards = append(m.Rewards, DelegationDelegatorReward{}) + if err := m.Rewards[len(m.Rewards)-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 Total", 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.Total = append(m.Total, types.DecCoin{}) + if err := m.Total[len(m.Total)-1].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 *QueryDelegatorValidatorsRequest) 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: QueryDelegatorValidatorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = 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 *QueryDelegatorValidatorsResponse) 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: QueryDelegatorValidatorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", 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.Validators = append(m.Validators, 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 *QueryDelegatorWithdrawAddressRequest) 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: QueryDelegatorWithdrawAddressRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorWithdrawAddressRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = 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 *QueryDelegatorWithdrawAddressResponse) 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: QueryDelegatorWithdrawAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorWithdrawAddressResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawAddress", 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.WithdrawAddress = 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 *QueryCommunityPoolRequest) 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: QueryCommunityPoolRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCommunityPoolRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCommunityPoolResponse) 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: QueryCommunityPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCommunityPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pool", 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.Pool = append(m.Pool, types.DecCoin{}) + if err := m.Pool[len(m.Pool)-1].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/distribution/types/query.pb.gw.go b/x/distribution/types/query.pb.gw.go new file mode 100644 index 000000000000..7bbe7b7a29fb --- /dev/null +++ b/x/distribution/types/query.pb.gw.go @@ -0,0 +1,936 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/distribution/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_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ValidatorOutstandingRewards_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorOutstandingRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := client.ValidatorOutstandingRewards(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorOutstandingRewards_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorOutstandingRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := server.ValidatorOutstandingRewards(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ValidatorCommission_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorCommissionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := client.ValidatorCommission(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorCommission_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorCommissionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := server.ValidatorCommission(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ValidatorSlashes_0 = &utilities.DoubleArray{Encoding: map[string]int{"validator_address": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ValidatorSlashes_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSlashesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", 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_ValidatorSlashes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ValidatorSlashes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorSlashes_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSlashesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", 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_ValidatorSlashes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ValidatorSlashes(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DelegationRewards_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := client.DelegationRewards(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegationRewards_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + val, ok = pathParams["validator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_address") + } + + protoReq.ValidatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_address", err) + } + + msg, err := server.DelegationRewards(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DelegationTotalRewards_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationTotalRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := client.DelegationTotalRewards(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegationTotalRewards_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationTotalRewardsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := server.DelegationTotalRewards(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DelegatorValidators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := client.DelegatorValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorValidators_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := server.DelegatorValidators(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DelegatorWithdrawAddress_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorWithdrawAddressRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := client.DelegatorWithdrawAddress(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorWithdrawAddress_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorWithdrawAddressRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_address") + } + + protoReq.DelegatorAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_address", err) + } + + msg, err := server.DelegatorWithdrawAddress(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_CommunityPool_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCommunityPoolRequest + var metadata runtime.ServerMetadata + + msg, err := client.CommunityPool(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CommunityPool_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCommunityPoolRequest + var metadata runtime.ServerMetadata + + msg, err := server.CommunityPool(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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorOutstandingRewards_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_ValidatorOutstandingRewards_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_ValidatorOutstandingRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorCommission_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_ValidatorCommission_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_ValidatorCommission_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorSlashes_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_ValidatorSlashes_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_ValidatorSlashes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegationRewards_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_DelegationRewards_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_DelegationRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegationTotalRewards_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_DelegationTotalRewards_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_DelegationTotalRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidators_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_DelegatorValidators_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_DelegatorValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorWithdrawAddress_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_DelegatorWithdrawAddress_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_DelegatorWithdrawAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_CommunityPool_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_CommunityPool_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_CommunityPool_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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorOutstandingRewards_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_ValidatorOutstandingRewards_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_ValidatorOutstandingRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorCommission_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_ValidatorCommission_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_ValidatorCommission_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorSlashes_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_ValidatorSlashes_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_ValidatorSlashes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegationRewards_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_DelegationRewards_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_DelegationRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegationTotalRewards_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_DelegationTotalRewards_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_DelegationTotalRewards_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidators_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_DelegatorValidators_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_DelegatorValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorWithdrawAddress_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_DelegatorWithdrawAddress_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_DelegatorWithdrawAddress_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_CommunityPool_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_CommunityPool_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_CommunityPool_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ValidatorOutstandingRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "outstanding_rewards"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ValidatorCommission_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "commission"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ValidatorSlashes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "validators", "validator_address", "slashes"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegationRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards", "validator_address"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegationTotalRewards_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "rewards"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorWithdrawAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "distribution", "v1beta1", "delegators", "delegator_address", "withdraw_address"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_CommunityPool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "distribution", "v1beta1", "community_pool"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorOutstandingRewards_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorCommission_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorSlashes_0 = runtime.ForwardResponseMessage + + forward_Query_DelegationRewards_0 = runtime.ForwardResponseMessage + + forward_Query_DelegationTotalRewards_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorValidators_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorWithdrawAddress_0 = runtime.ForwardResponseMessage + + forward_Query_CommunityPool_0 = runtime.ForwardResponseMessage +) diff --git a/x/distribution/types/tx.pb.go b/x/distribution/types/tx.pb.go new file mode 100644 index 000000000000..55cfce4e78f4 --- /dev/null +++ b/x/distribution/types/tx.pb.go @@ -0,0 +1,1753 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/distribution/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgSetWithdrawAddress sets the withdraw address for +// a delegator (or validator self-delegation). +type MsgSetWithdrawAddress struct { + DelegatorAddress string `protobuf:"bytes,1,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + WithdrawAddress string `protobuf:"bytes,2,opt,name=withdraw_address,json=withdrawAddress,proto3" json:"withdraw_address,omitempty" yaml:"withdraw_address"` +} + +func (m *MsgSetWithdrawAddress) Reset() { *m = MsgSetWithdrawAddress{} } +func (m *MsgSetWithdrawAddress) String() string { return proto.CompactTextString(m) } +func (*MsgSetWithdrawAddress) ProtoMessage() {} +func (*MsgSetWithdrawAddress) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{0} +} +func (m *MsgSetWithdrawAddress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetWithdrawAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetWithdrawAddress.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 *MsgSetWithdrawAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetWithdrawAddress.Merge(m, src) +} +func (m *MsgSetWithdrawAddress) XXX_Size() int { + return m.Size() +} +func (m *MsgSetWithdrawAddress) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetWithdrawAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetWithdrawAddress proto.InternalMessageInfo + +// MsgSetWithdrawAddressResponse defines the Msg/SetWithdrawAddress response type. +type MsgSetWithdrawAddressResponse struct { +} + +func (m *MsgSetWithdrawAddressResponse) Reset() { *m = MsgSetWithdrawAddressResponse{} } +func (m *MsgSetWithdrawAddressResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSetWithdrawAddressResponse) ProtoMessage() {} +func (*MsgSetWithdrawAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{1} +} +func (m *MsgSetWithdrawAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSetWithdrawAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSetWithdrawAddressResponse.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 *MsgSetWithdrawAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSetWithdrawAddressResponse.Merge(m, src) +} +func (m *MsgSetWithdrawAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSetWithdrawAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSetWithdrawAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSetWithdrawAddressResponse proto.InternalMessageInfo + +// MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +// from a single validator. +type MsgWithdrawDelegatorReward 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"` +} + +func (m *MsgWithdrawDelegatorReward) Reset() { *m = MsgWithdrawDelegatorReward{} } +func (m *MsgWithdrawDelegatorReward) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawDelegatorReward) ProtoMessage() {} +func (*MsgWithdrawDelegatorReward) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{2} +} +func (m *MsgWithdrawDelegatorReward) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawDelegatorReward) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawDelegatorReward.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 *MsgWithdrawDelegatorReward) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawDelegatorReward.Merge(m, src) +} +func (m *MsgWithdrawDelegatorReward) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawDelegatorReward) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawDelegatorReward.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawDelegatorReward proto.InternalMessageInfo + +// MsgWithdrawDelegatorRewardResponse defines the Msg/WithdrawDelegatorReward response type. +type MsgWithdrawDelegatorRewardResponse struct { +} + +func (m *MsgWithdrawDelegatorRewardResponse) Reset() { *m = MsgWithdrawDelegatorRewardResponse{} } +func (m *MsgWithdrawDelegatorRewardResponse) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawDelegatorRewardResponse) ProtoMessage() {} +func (*MsgWithdrawDelegatorRewardResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{3} +} +func (m *MsgWithdrawDelegatorRewardResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawDelegatorRewardResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawDelegatorRewardResponse.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 *MsgWithdrawDelegatorRewardResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawDelegatorRewardResponse.Merge(m, src) +} +func (m *MsgWithdrawDelegatorRewardResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawDelegatorRewardResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawDelegatorRewardResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawDelegatorRewardResponse proto.InternalMessageInfo + +// MsgWithdrawValidatorCommission withdraws the full commission to the validator +// address. +type MsgWithdrawValidatorCommission struct { + ValidatorAddress string `protobuf:"bytes,1,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` +} + +func (m *MsgWithdrawValidatorCommission) Reset() { *m = MsgWithdrawValidatorCommission{} } +func (m *MsgWithdrawValidatorCommission) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawValidatorCommission) ProtoMessage() {} +func (*MsgWithdrawValidatorCommission) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{4} +} +func (m *MsgWithdrawValidatorCommission) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawValidatorCommission) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawValidatorCommission.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 *MsgWithdrawValidatorCommission) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawValidatorCommission.Merge(m, src) +} +func (m *MsgWithdrawValidatorCommission) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawValidatorCommission) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawValidatorCommission.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawValidatorCommission proto.InternalMessageInfo + +// MsgWithdrawValidatorCommissionResponse defines the Msg/WithdrawValidatorCommission response type. +type MsgWithdrawValidatorCommissionResponse struct { +} + +func (m *MsgWithdrawValidatorCommissionResponse) Reset() { + *m = MsgWithdrawValidatorCommissionResponse{} +} +func (m *MsgWithdrawValidatorCommissionResponse) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawValidatorCommissionResponse) ProtoMessage() {} +func (*MsgWithdrawValidatorCommissionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{5} +} +func (m *MsgWithdrawValidatorCommissionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawValidatorCommissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawValidatorCommissionResponse.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 *MsgWithdrawValidatorCommissionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawValidatorCommissionResponse.Merge(m, src) +} +func (m *MsgWithdrawValidatorCommissionResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawValidatorCommissionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawValidatorCommissionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawValidatorCommissionResponse proto.InternalMessageInfo + +// MsgFundCommunityPool allows an account to directly +// fund the community pool. +type MsgFundCommunityPool struct { + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` +} + +func (m *MsgFundCommunityPool) Reset() { *m = MsgFundCommunityPool{} } +func (m *MsgFundCommunityPool) String() string { return proto.CompactTextString(m) } +func (*MsgFundCommunityPool) ProtoMessage() {} +func (*MsgFundCommunityPool) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{6} +} +func (m *MsgFundCommunityPool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFundCommunityPool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFundCommunityPool.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 *MsgFundCommunityPool) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFundCommunityPool.Merge(m, src) +} +func (m *MsgFundCommunityPool) XXX_Size() int { + return m.Size() +} +func (m *MsgFundCommunityPool) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFundCommunityPool.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFundCommunityPool proto.InternalMessageInfo + +// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type. +type MsgFundCommunityPoolResponse struct { +} + +func (m *MsgFundCommunityPoolResponse) Reset() { *m = MsgFundCommunityPoolResponse{} } +func (m *MsgFundCommunityPoolResponse) String() string { return proto.CompactTextString(m) } +func (*MsgFundCommunityPoolResponse) ProtoMessage() {} +func (*MsgFundCommunityPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ed4f433d965e58ca, []int{7} +} +func (m *MsgFundCommunityPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgFundCommunityPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgFundCommunityPoolResponse.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 *MsgFundCommunityPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgFundCommunityPoolResponse.Merge(m, src) +} +func (m *MsgFundCommunityPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgFundCommunityPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgFundCommunityPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgFundCommunityPoolResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSetWithdrawAddress)(nil), "cosmos.distribution.v1beta1.MsgSetWithdrawAddress") + proto.RegisterType((*MsgSetWithdrawAddressResponse)(nil), "cosmos.distribution.v1beta1.MsgSetWithdrawAddressResponse") + proto.RegisterType((*MsgWithdrawDelegatorReward)(nil), "cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward") + proto.RegisterType((*MsgWithdrawDelegatorRewardResponse)(nil), "cosmos.distribution.v1beta1.MsgWithdrawDelegatorRewardResponse") + proto.RegisterType((*MsgWithdrawValidatorCommission)(nil), "cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission") + proto.RegisterType((*MsgWithdrawValidatorCommissionResponse)(nil), "cosmos.distribution.v1beta1.MsgWithdrawValidatorCommissionResponse") + proto.RegisterType((*MsgFundCommunityPool)(nil), "cosmos.distribution.v1beta1.MsgFundCommunityPool") + proto.RegisterType((*MsgFundCommunityPoolResponse)(nil), "cosmos.distribution.v1beta1.MsgFundCommunityPoolResponse") +} + +func init() { + proto.RegisterFile("cosmos/distribution/v1beta1/tx.proto", fileDescriptor_ed4f433d965e58ca) +} + +var fileDescriptor_ed4f433d965e58ca = []byte{ + // 558 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x3f, 0x6f, 0xd3, 0x40, + 0x18, 0xc6, 0x7d, 0x2d, 0xaa, 0xe8, 0x31, 0x90, 0x58, 0x45, 0x0d, 0x4e, 0x38, 0x57, 0x56, 0x85, + 0xb2, 0x60, 0x93, 0x30, 0x20, 0xc2, 0x80, 0x48, 0x50, 0xa5, 0x0e, 0x11, 0xc8, 0x48, 0x20, 0xb1, + 0x20, 0x3b, 0x77, 0x72, 0x4f, 0xc4, 0xbe, 0xc8, 0x77, 0x6e, 0x9a, 0x11, 0x89, 0x81, 0x11, 0x89, + 0x0f, 0x40, 0x25, 0x16, 0xc4, 0xcc, 0xc8, 0x07, 0xc8, 0xd8, 0x91, 0x29, 0xa0, 0x64, 0x61, 0xee, + 0x27, 0x40, 0xf1, 0x3f, 0x92, 0xda, 0x49, 0x29, 0xe9, 0x94, 0xe8, 0xbd, 0xe7, 0xf9, 0xf9, 0x79, + 0x95, 0xe7, 0x62, 0xb8, 0xdb, 0x61, 0xdc, 0x65, 0xdc, 0xc0, 0x94, 0x0b, 0x9f, 0xda, 0x81, 0xa0, + 0xcc, 0x33, 0x0e, 0x6b, 0x36, 0x11, 0x56, 0xcd, 0x10, 0x47, 0x7a, 0xcf, 0x67, 0x82, 0xc9, 0xe5, + 0x48, 0xa5, 0xcf, 0xaa, 0xf4, 0x58, 0xa5, 0x6c, 0x39, 0xcc, 0x61, 0xa1, 0xce, 0x98, 0x7e, 0x8b, + 0x2c, 0x0a, 0x8a, 0xc1, 0xb6, 0xc5, 0x49, 0x0a, 0xec, 0x30, 0xea, 0x45, 0xe7, 0xda, 0x37, 0x00, + 0x6f, 0xb4, 0xb9, 0xf3, 0x9c, 0x88, 0x97, 0x54, 0x1c, 0x60, 0xdf, 0xea, 0x3f, 0xc6, 0xd8, 0x27, + 0x9c, 0xcb, 0xfb, 0xb0, 0x88, 0x49, 0x97, 0x38, 0x96, 0x60, 0xfe, 0x6b, 0x2b, 0x1a, 0x96, 0xc0, + 0x0e, 0xa8, 0x6e, 0x36, 0x2b, 0xa7, 0x23, 0xb5, 0x34, 0xb0, 0xdc, 0x6e, 0x43, 0xcb, 0x48, 0x34, + 0xb3, 0x90, 0xce, 0x12, 0xd4, 0x1e, 0x2c, 0xf4, 0x63, 0x7a, 0x4a, 0x5a, 0x0b, 0x49, 0xe5, 0xd3, + 0x91, 0xba, 0x1d, 0x91, 0xce, 0x2a, 0x34, 0xf3, 0x7a, 0x7f, 0x3e, 0x52, 0xe3, 0xea, 0xfb, 0x63, + 0x55, 0xfa, 0x7d, 0xac, 0x4a, 0x9a, 0x0a, 0x6f, 0xe5, 0xa6, 0x36, 0x09, 0xef, 0x31, 0x8f, 0x13, + 0xed, 0x3b, 0x80, 0x4a, 0x9b, 0x3b, 0xc9, 0xf1, 0x93, 0x24, 0x92, 0x49, 0xfa, 0x96, 0x8f, 0x2f, + 0x73, 0xb9, 0x7d, 0x58, 0x3c, 0xb4, 0xba, 0x14, 0xcf, 0xa1, 0xd6, 0xce, 0xa2, 0x32, 0x12, 0xcd, + 0x2c, 0xa4, 0xb3, 0xec, 0x7e, 0xbb, 0x50, 0x5b, 0x9c, 0x3e, 0x5d, 0x32, 0x80, 0x68, 0x46, 0xf5, + 0x22, 0xc1, 0xb5, 0x98, 0xeb, 0x52, 0xce, 0x29, 0xf3, 0xf2, 0xc3, 0x81, 0x15, 0xc3, 0x55, 0xe1, + 0xed, 0xe5, 0x8f, 0x4d, 0x03, 0x7e, 0x06, 0x70, 0xab, 0xcd, 0x9d, 0xbd, 0xc0, 0xc3, 0xd3, 0xd3, + 0xc0, 0xa3, 0x62, 0xf0, 0x8c, 0xb1, 0xae, 0xdc, 0x81, 0x1b, 0x96, 0xcb, 0x02, 0x4f, 0x94, 0xc0, + 0xce, 0x7a, 0xf5, 0x5a, 0xfd, 0xa6, 0x1e, 0x57, 0x7b, 0xda, 0xd3, 0xa4, 0xd2, 0x7a, 0x8b, 0x51, + 0xaf, 0x79, 0x77, 0x38, 0x52, 0xa5, 0xaf, 0x3f, 0xd5, 0xaa, 0x43, 0xc5, 0x41, 0x60, 0xeb, 0x1d, + 0xe6, 0x1a, 0x71, 0xa9, 0xa3, 0x8f, 0x3b, 0x1c, 0xbf, 0x31, 0xc4, 0xa0, 0x47, 0x78, 0x68, 0xe0, + 0x66, 0x8c, 0x96, 0x2b, 0x70, 0x13, 0x93, 0x1e, 0xe3, 0x54, 0x30, 0x3f, 0xfa, 0x45, 0xcc, 0xbf, + 0x83, 0x99, 0x7d, 0x10, 0xac, 0xe4, 0x85, 0x4c, 0xb6, 0xa8, 0x0f, 0xaf, 0xc0, 0xf5, 0x36, 0x77, + 0xe4, 0x77, 0x00, 0xca, 0x39, 0x17, 0xa5, 0xae, 0x2f, 0xb9, 0x96, 0x7a, 0x6e, 0x4d, 0x95, 0xc6, + 0xc5, 0x3d, 0x49, 0x1c, 0xf9, 0x23, 0x80, 0xdb, 0x8b, 0x7a, 0x7d, 0xff, 0x3c, 0xee, 0x02, 0xa3, + 0xf2, 0xe8, 0x3f, 0x8d, 0x69, 0xaa, 0x4f, 0x00, 0x96, 0x97, 0x35, 0xf1, 0xe1, 0xbf, 0x3e, 0x20, + 0xc7, 0xac, 0xb4, 0x56, 0x30, 0xa7, 0x09, 0xdf, 0x02, 0x58, 0xcc, 0x36, 0xb1, 0x76, 0x1e, 0x3a, + 0x63, 0x51, 0x1e, 0x5c, 0xd8, 0x92, 0x64, 0x68, 0x3e, 0xfd, 0x32, 0x46, 0x60, 0x38, 0x46, 0xe0, + 0x64, 0x8c, 0xc0, 0xaf, 0x31, 0x02, 0x1f, 0x26, 0x48, 0x3a, 0x99, 0x20, 0xe9, 0xc7, 0x04, 0x49, + 0xaf, 0x6a, 0x4b, 0x2b, 0x7e, 0x34, 0xff, 0x76, 0x08, 0x1b, 0x6f, 0x6f, 0x84, 0x7f, 0xe3, 0xf7, + 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd2, 0x18, 0x71, 0xb0, 0x41, 0x06, 0x00, 0x00, +} + +func (this *MsgSetWithdrawAddressResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgSetWithdrawAddressResponse) + if !ok { + that2, ok := that.(MsgSetWithdrawAddressResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} +func (this *MsgWithdrawDelegatorRewardResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgWithdrawDelegatorRewardResponse) + if !ok { + that2, ok := that.(MsgWithdrawDelegatorRewardResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} +func (this *MsgWithdrawValidatorCommissionResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgWithdrawValidatorCommissionResponse) + if !ok { + that2, ok := that.(MsgWithdrawValidatorCommissionResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} +func (this *MsgFundCommunityPoolResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgFundCommunityPoolResponse) + if !ok { + that2, ok := that.(MsgFundCommunityPoolResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} + +// 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 { + // SetWithdrawAddress defines a method to change the withdraw address + // for a delegator (or validator self-delegation). + SetWithdrawAddress(ctx context.Context, in *MsgSetWithdrawAddress, opts ...grpc.CallOption) (*MsgSetWithdrawAddressResponse, error) + // WithdrawDelegatorReward defines a method to withdraw rewards of delegator + // from a single validator. + WithdrawDelegatorReward(ctx context.Context, in *MsgWithdrawDelegatorReward, opts ...grpc.CallOption) (*MsgWithdrawDelegatorRewardResponse, error) + // WithdrawValidatorCommission defines a method to withdraw the + // full commission to the validator address. + WithdrawValidatorCommission(ctx context.Context, in *MsgWithdrawValidatorCommission, opts ...grpc.CallOption) (*MsgWithdrawValidatorCommissionResponse, error) + // FundCommunityPool defines a method to allow an account to directly + // fund the community pool. + FundCommunityPool(ctx context.Context, in *MsgFundCommunityPool, opts ...grpc.CallOption) (*MsgFundCommunityPoolResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SetWithdrawAddress(ctx context.Context, in *MsgSetWithdrawAddress, opts ...grpc.CallOption) (*MsgSetWithdrawAddressResponse, error) { + out := new(MsgSetWithdrawAddressResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Msg/SetWithdrawAddress", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) WithdrawDelegatorReward(ctx context.Context, in *MsgWithdrawDelegatorReward, opts ...grpc.CallOption) (*MsgWithdrawDelegatorRewardResponse, error) { + out := new(MsgWithdrawDelegatorRewardResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Msg/WithdrawDelegatorReward", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) WithdrawValidatorCommission(ctx context.Context, in *MsgWithdrawValidatorCommission, opts ...grpc.CallOption) (*MsgWithdrawValidatorCommissionResponse, error) { + out := new(MsgWithdrawValidatorCommissionResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Msg/WithdrawValidatorCommission", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) FundCommunityPool(ctx context.Context, in *MsgFundCommunityPool, opts ...grpc.CallOption) (*MsgFundCommunityPoolResponse, error) { + out := new(MsgFundCommunityPoolResponse) + err := c.cc.Invoke(ctx, "/cosmos.distribution.v1beta1.Msg/FundCommunityPool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // SetWithdrawAddress defines a method to change the withdraw address + // for a delegator (or validator self-delegation). + SetWithdrawAddress(context.Context, *MsgSetWithdrawAddress) (*MsgSetWithdrawAddressResponse, error) + // WithdrawDelegatorReward defines a method to withdraw rewards of delegator + // from a single validator. + WithdrawDelegatorReward(context.Context, *MsgWithdrawDelegatorReward) (*MsgWithdrawDelegatorRewardResponse, error) + // WithdrawValidatorCommission defines a method to withdraw the + // full commission to the validator address. + WithdrawValidatorCommission(context.Context, *MsgWithdrawValidatorCommission) (*MsgWithdrawValidatorCommissionResponse, error) + // FundCommunityPool defines a method to allow an account to directly + // fund the community pool. + FundCommunityPool(context.Context, *MsgFundCommunityPool) (*MsgFundCommunityPoolResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SetWithdrawAddress(ctx context.Context, req *MsgSetWithdrawAddress) (*MsgSetWithdrawAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetWithdrawAddress not implemented") +} +func (*UnimplementedMsgServer) WithdrawDelegatorReward(ctx context.Context, req *MsgWithdrawDelegatorReward) (*MsgWithdrawDelegatorRewardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawDelegatorReward not implemented") +} +func (*UnimplementedMsgServer) WithdrawValidatorCommission(ctx context.Context, req *MsgWithdrawValidatorCommission) (*MsgWithdrawValidatorCommissionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WithdrawValidatorCommission not implemented") +} +func (*UnimplementedMsgServer) FundCommunityPool(ctx context.Context, req *MsgFundCommunityPool) (*MsgFundCommunityPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FundCommunityPool not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SetWithdrawAddress_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSetWithdrawAddress) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SetWithdrawAddress(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Msg/SetWithdrawAddress", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SetWithdrawAddress(ctx, req.(*MsgSetWithdrawAddress)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_WithdrawDelegatorReward_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWithdrawDelegatorReward) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).WithdrawDelegatorReward(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Msg/WithdrawDelegatorReward", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).WithdrawDelegatorReward(ctx, req.(*MsgWithdrawDelegatorReward)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_WithdrawValidatorCommission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWithdrawValidatorCommission) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).WithdrawValidatorCommission(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Msg/WithdrawValidatorCommission", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).WithdrawValidatorCommission(ctx, req.(*MsgWithdrawValidatorCommission)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_FundCommunityPool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgFundCommunityPool) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).FundCommunityPool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.distribution.v1beta1.Msg/FundCommunityPool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).FundCommunityPool(ctx, req.(*MsgFundCommunityPool)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.distribution.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SetWithdrawAddress", + Handler: _Msg_SetWithdrawAddress_Handler, + }, + { + MethodName: "WithdrawDelegatorReward", + Handler: _Msg_WithdrawDelegatorReward_Handler, + }, + { + MethodName: "WithdrawValidatorCommission", + Handler: _Msg_WithdrawValidatorCommission_Handler, + }, + { + MethodName: "FundCommunityPool", + Handler: _Msg_FundCommunityPool_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/distribution/v1beta1/tx.proto", +} + +func (m *MsgSetWithdrawAddress) 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 *MsgSetWithdrawAddress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetWithdrawAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.WithdrawAddress) > 0 { + i -= len(m.WithdrawAddress) + copy(dAtA[i:], m.WithdrawAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.WithdrawAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSetWithdrawAddressResponse) 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 *MsgSetWithdrawAddressResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSetWithdrawAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawDelegatorReward) 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 *MsgWithdrawDelegatorReward) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawDelegatorReward) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawDelegatorRewardResponse) 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 *MsgWithdrawDelegatorRewardResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawDelegatorRewardResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawValidatorCommission) 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 *MsgWithdrawValidatorCommission) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawValidatorCommission) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawValidatorCommissionResponse) 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 *MsgWithdrawValidatorCommissionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawValidatorCommissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgFundCommunityPool) 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 *MsgFundCommunityPool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFundCommunityPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintTx(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x12 + } + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].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 *MsgFundCommunityPoolResponse) 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 *MsgFundCommunityPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgFundCommunityPoolResponse) 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 *MsgSetWithdrawAddress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.WithdrawAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSetWithdrawAddressResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgWithdrawDelegatorReward) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgWithdrawDelegatorRewardResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgWithdrawValidatorCommission) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgWithdrawValidatorCommissionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgFundCommunityPool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgFundCommunityPoolResponse) 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 *MsgSetWithdrawAddress) 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: MsgSetWithdrawAddress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetWithdrawAddress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WithdrawAddress", 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.WithdrawAddress = 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 *MsgSetWithdrawAddressResponse) 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: MsgSetWithdrawAddressResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSetWithdrawAddressResponse: 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 *MsgWithdrawDelegatorReward) 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: MsgWithdrawDelegatorReward: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawDelegatorReward: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = 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 *MsgWithdrawDelegatorRewardResponse) 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: MsgWithdrawDelegatorRewardResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawDelegatorRewardResponse: 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 *MsgWithdrawValidatorCommission) 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: MsgWithdrawValidatorCommission: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawValidatorCommission: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = 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 *MsgWithdrawValidatorCommissionResponse) 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: MsgWithdrawValidatorCommissionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawValidatorCommissionResponse: 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 *MsgFundCommunityPool) 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: MsgFundCommunityPool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFundCommunityPool: 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) + } + 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.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-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 Depositor", 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.Depositor = 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 *MsgFundCommunityPoolResponse) 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: MsgFundCommunityPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgFundCommunityPoolResponse: 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/distribution/types/validator.go b/x/distribution/types/validator.go index 9ff1f4c9aa1f..aa64a3703068 100644 --- a/x/distribution/types/validator.go +++ b/x/distribution/types/validator.go @@ -7,38 +7,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// 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 -type ValidatorHistoricalRewards struct { - CumulativeRewardRatio sdk.DecCoins `json:"cumulative_reward_ratio" yaml:"cumulative_reward_ratio"` - ReferenceCount uint16 `json:"reference_count" yaml:"reference_count"` -} - // create a new ValidatorHistoricalRewards -func NewValidatorHistoricalRewards(cumulativeRewardRatio sdk.DecCoins, referenceCount uint16) ValidatorHistoricalRewards { +func NewValidatorHistoricalRewards(cumulativeRewardRatio sdk.DecCoins, referenceCount uint32) ValidatorHistoricalRewards { return ValidatorHistoricalRewards{ CumulativeRewardRatio: cumulativeRewardRatio, ReferenceCount: referenceCount, } } -// 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 -type ValidatorCurrentRewards struct { - Rewards sdk.DecCoins `json:"rewards" yaml:"rewards"` // current rewards - Period uint64 `json:"period" yaml:"period"` // current period -} - // create a new ValidatorCurrentRewards func NewValidatorCurrentRewards(rewards sdk.DecCoins, period uint64) ValidatorCurrentRewards { return ValidatorCurrentRewards{ @@ -47,24 +23,11 @@ func NewValidatorCurrentRewards(rewards sdk.DecCoins, period uint64) ValidatorCu } } -// accumulated commission for a validator -// kept as a running counter, can be withdrawn at any time -type ValidatorAccumulatedCommission = sdk.DecCoins - // return the initial accumulated commission (zero) func InitialValidatorAccumulatedCommission() ValidatorAccumulatedCommission { return ValidatorAccumulatedCommission{} } -// validator slash event -// height is implicit within the store key -// needed to calculate appropriate amounts of staking token -// for delegations which withdraw after a slash has occurred -type ValidatorSlashEvent struct { - ValidatorPeriod uint64 `json:"validator_period" yaml:"validator_period"` // period when the slash occurred - Fraction sdk.Dec `json:"fraction" yaml:"fraction"` // slash fraction -} - // create a new ValidatorSlashEvent func NewValidatorSlashEvent(validatorPeriod uint64, fraction sdk.Dec) ValidatorSlashEvent { return ValidatorSlashEvent{ @@ -73,17 +36,9 @@ func NewValidatorSlashEvent(validatorPeriod uint64, fraction sdk.Dec) ValidatorS } } -func (vs ValidatorSlashEvent) String() string { - return fmt.Sprintf(`Period: %d -Fraction: %s`, vs.ValidatorPeriod, vs.Fraction) -} - -// ValidatorSlashEvents is a collection of ValidatorSlashEvent -type ValidatorSlashEvents []ValidatorSlashEvent - func (vs ValidatorSlashEvents) String() string { out := "Validator Slash Events:\n" - for i, sl := range vs { + for i, sl := range vs.ValidatorSlashEvents { out += fmt.Sprintf(` Slash %d: Period: %d Fraction: %s @@ -91,7 +46,3 @@ func (vs ValidatorSlashEvents) String() string { } return strings.TrimSpace(out) } - -// outstanding (un-withdrawn) rewards for a validator -// inexpensive to track, allows simple sanity checks -type ValidatorOutstandingRewards = sdk.DecCoins diff --git a/x/evidence/abci.go b/x/evidence/abci.go index e12ed7065f9b..63866fd2bc68 100644 --- a/x/evidence/abci.go +++ b/x/evidence/abci.go @@ -2,21 +2,28 @@ package evidence import ( "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" + "time" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) // BeginBlocker iterates through and handles any newly discovered evidence of // misbehavior submitted by Tendermint. Currently, only equivocation is handled. -func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) { +func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + for _, tmEvidence := range req.ByzantineValidators { switch tmEvidence.Type { - case tmtypes.ABCIEvidenceTypeDuplicateVote: - evidence := ConvertDuplicateVoteEvidence(tmEvidence) - k.HandleDoubleSign(ctx, evidence.(Equivocation)) + // It's still ongoing discussion how should we treat and slash attacks with + // premeditation. So for now we agree to treat them in the same way. + case abci.EvidenceType_DUPLICATE_VOTE, abci.EvidenceType_LIGHT_CLIENT_ATTACK: + evidence := types.FromABCIEvidence(tmEvidence) + k.HandleEquivocationEvidence(ctx, evidence.(*types.Equivocation)) default: k.Logger(ctx).Error(fmt.Sprintf("ignored unknown evidence type: %s", tmEvidence.Type)) diff --git a/x/evidence/alias.go b/x/evidence/alias.go deleted file mode 100644 index 29b8447a365e..000000000000 --- a/x/evidence/alias.go +++ /dev/null @@ -1,53 +0,0 @@ -package evidence - -import ( - "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -// nolint - -const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - DefaultParamspace = types.DefaultParamspace - QueryEvidence = types.QueryEvidence - QueryAllEvidence = types.QueryAllEvidence - QueryParameters = types.QueryParameters - TypeMsgSubmitEvidence = types.TypeMsgSubmitEvidence - EventTypeSubmitEvidence = types.EventTypeSubmitEvidence - AttributeValueCategory = types.AttributeValueCategory - AttributeKeyEvidenceHash = types.AttributeKeyEvidenceHash - DefaultMaxEvidenceAge = types.DefaultMaxEvidenceAge -) - -var ( - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - - NewMsgSubmitEvidence = types.NewMsgSubmitEvidence - NewRouter = types.NewRouter - NewQueryEvidenceParams = types.NewQueryEvidenceParams - NewQueryAllEvidenceParams = types.NewQueryAllEvidenceParams - RegisterCodec = types.RegisterCodec - RegisterEvidenceTypeCodec = types.RegisterEvidenceTypeCodec - ModuleCdc = types.ModuleCdc - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ConvertDuplicateVoteEvidence = types.ConvertDuplicateVoteEvidence - KeyMaxEvidenceAge = types.KeyMaxEvidenceAge - DoubleSignJailEndTime = types.DoubleSignJailEndTime - ParamKeyTable = types.ParamKeyTable -) - -type ( - Keeper = keeper.Keeper - - GenesisState = types.GenesisState - MsgSubmitEvidence = types.MsgSubmitEvidence - Handler = types.Handler - Router = types.Router - Equivocation = types.Equivocation -) diff --git a/x/evidence/client/cli/cli_test.go b/x/evidence/client/cli/cli_test.go new file mode 100644 index 000000000000..84b3697b43ac --- /dev/null +++ b/x/evidence/client/cli/cli_test.go @@ -0,0 +1,80 @@ +package cli_test + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/suite" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + testnet "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg testnet.Config + network *testnet.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testnet.DefaultConfig() + cfg.NumValidators = 1 + + s.cfg = cfg + s.network = testnet.New(s.T(), cfg) + + _, 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 TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (s *IntegrationTestSuite) TestGetQueryCmd() { + val := s.network.Validators[0] + + testCases := map[string]struct { + args []string + expectedOutput string + expectErr bool + }{ + "non-existent evidence": { + []string{"DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660"}, + "evidence DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660 not found", + true, + }, + "all evidence (default pagination)": { + []string{}, + "evidence: []\npagination:\n next_key: null\n total: \"0\"", + false, + }, + } + + for name, tc := range testCases { + tc := tc + + s.Run(name, func() { + cmd := cli.GetQueryCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + + s.Require().Contains(strings.TrimSpace(out.String()), tc.expectedOutput) + }) + } +} diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index da09ae7b4b42..9c987fb15e36 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -1,143 +1,98 @@ package cli import ( + "context" "encoding/hex" "fmt" "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) // GetQueryCmd returns the CLI command with all evidence module query commands // mounted. -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Query for evidence by hash or for all (paginated) submitted evidence", Long: strings.TrimSpace( fmt.Sprintf(`Query for specific submitted evidence by hash or query for all (paginated) evidence: - + Example: $ %s query %s DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660 $ %s query %s --page=2 --limit=50 `, - version.ClientName, types.ModuleName, version.ClientName, types.ModuleName, + version.AppName, types.ModuleName, version.AppName, types.ModuleName, ), ), Args: cobra.MaximumNArgs(1), DisableFlagParsing: true, SuggestionsMinimumDistance: 2, - RunE: QueryEvidenceCmd(cdc), + RunE: QueryEvidenceCmd(), } - cmd.Flags().Int(flags.FlagPage, 1, "pagination page of evidence to to query for") - cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of evidence to query for") - - cmd.AddCommand(flags.GetCommands(QueryParamsCmd(cdc))...) + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "evidence") - return flags.GetCommands(cmd)[0] -} - -// QueryParamsCmd returns the command handler for evidence parameter querying. -func QueryParamsCmd(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "params", - Short: "Query the current evidence parameters", - Args: cobra.NoArgs, - Long: strings.TrimSpace(`Query the current evidence parameters: - -$ query evidence params -`), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters) - res, _, err := cliCtx.QueryWithData(route, nil) - if err != nil { - return err - } - - var params types.Params - if err := cdc.UnmarshalJSON(res, ¶ms); err != nil { - return fmt.Errorf("failed to unmarshal params: %w", err) - } - - return cliCtx.PrintOutput(params) - }, - } + return cmd } // QueryEvidenceCmd returns the command handler for evidence querying. Evidence // can be queried for by hash or paginated evidence can be returned. -func QueryEvidenceCmd(cdc *codec.Codec) func(*cobra.Command, []string) error { +func QueryEvidenceCmd() func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - if err := client.ValidateCmd(cmd, args); err != nil { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { return err } + if len(args) > 0 { + return queryEvidence(clientCtx, args[0]) + } - cliCtx := context.NewCLIContext().WithCodec(cdc) - - if hash := args[0]; hash != "" { - return queryEvidence(cdc, cliCtx, hash) + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err } - return queryAllEvidence(cdc, cliCtx) + return queryAllEvidence(clientCtx, pageReq) } } -func queryEvidence(cdc *codec.Codec, cliCtx context.CLIContext, hash string) error { - if _, err := hex.DecodeString(hash); err != nil { +func queryEvidence(clientCtx client.Context, hash string) error { + decodedHash, err := hex.DecodeString(hash) + if err != nil { return fmt.Errorf("invalid evidence hash: %w", err) } - params := types.NewQueryEvidenceParams(hash) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return fmt.Errorf("failed to marshal query params: %w", err) - } + queryClient := types.NewQueryClient(clientCtx) - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryEvidence) - res, _, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err - } + params := &types.QueryEvidenceRequest{EvidenceHash: decodedHash} + res, err := queryClient.Evidence(context.Background(), params) - var evidence exported.Evidence - err = cdc.UnmarshalJSON(res, &evidence) if err != nil { - return fmt.Errorf("failed to unmarshal evidence: %w", err) + return err } - return cliCtx.PrintOutput(evidence) + return clientCtx.PrintProto(res.Evidence) } -func queryAllEvidence(cdc *codec.Codec, cliCtx context.CLIContext) error { - params := types.NewQueryAllEvidenceParams(viper.GetInt(flags.FlagPage), viper.GetInt(flags.FlagLimit)) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return fmt.Errorf("failed to marshal query params: %w", err) - } +func queryAllEvidence(clientCtx client.Context, pageReq *query.PageRequest) error { + queryClient := types.NewQueryClient(clientCtx) - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllEvidence) - res, _, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err + params := &types.QueryAllEvidenceRequest{ + Pagination: pageReq, } - var evidence []exported.Evidence - err = cdc.UnmarshalJSON(res, &evidence) + res, err := queryClient.AllEvidence(context.Background(), params) if err != nil { - return fmt.Errorf("failed to unmarshal evidence: %w", err) + return err } - return cliCtx.PrintOutput(evidence) + return clientCtx.PrintProto(res) } diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go index b97b56c0a23d..f13e3e8e2f65 100644 --- a/x/evidence/client/cli/tx.go +++ b/x/evidence/client/cli/tx.go @@ -2,9 +2,7 @@ package cli import ( "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/spf13/cobra" ) @@ -14,7 +12,7 @@ import ( // modules, under a sub-command. This allows external modules to implement custom // Evidence types and Handlers while having the ability to create and sign txs // containing them all from a single root command. -func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *cobra.Command { +func GetTxCmd(childCmds []*cobra.Command) *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: "Evidence transaction subcommands", @@ -23,9 +21,9 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co RunE: client.ValidateCmd, } - submitEvidenceCmd := SubmitEvidenceCmd(cdc) + submitEvidenceCmd := SubmitEvidenceCmd() for _, childCmd := range childCmds { - submitEvidenceCmd.AddCommand(flags.PostCommands(childCmd)[0]) + submitEvidenceCmd.AddCommand(childCmd) } // TODO: Add tx commands. @@ -36,7 +34,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co // SubmitEvidenceCmd returns the top-level evidence submission command handler. // All concrete evidence submission child command handlers should be registered // under this command. -func SubmitEvidenceCmd(cdc *codec.Codec) *cobra.Command { +func SubmitEvidenceCmd() *cobra.Command { cmd := &cobra.Command{ Use: "submit", Short: "Submit arbitrary evidence of misbehavior", diff --git a/x/evidence/client/evidence_handler.go b/x/evidence/client/evidence_handler.go index 0b56b4c4cbf1..7cada571ccc3 100644 --- a/x/evidence/client/evidence_handler.go +++ b/x/evidence/client/evidence_handler.go @@ -3,17 +3,16 @@ package client import ( "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" ) type ( // RESTHandlerFn defines a REST service handler for evidence submission - RESTHandlerFn func(context.CLIContext) rest.EvidenceRESTHandler + RESTHandlerFn func(client.Context) rest.EvidenceRESTHandler // CLIHandlerFn defines a CLI command handler for evidence submission - CLIHandlerFn func(*codec.Codec) *cobra.Command + CLIHandlerFn func() *cobra.Command // EvidenceHandler defines a type that exposes REST and CLI client handlers for // evidence submission. diff --git a/x/evidence/client/rest/query.go b/x/evidence/client/rest/query.go index 45b66660ac73..21ae8b7bb306 100644 --- a/x/evidence/client/rest/query.go +++ b/x/evidence/client/rest/query.go @@ -1,35 +1,31 @@ package rest import ( + "encoding/hex" "fmt" "net/http" "strings" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/gorilla/mux" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { r.HandleFunc( fmt.Sprintf("/evidence/{%s}", RestParamEvidenceHash), - queryEvidenceHandler(cliCtx), + queryEvidenceHandler(clientCtx), ).Methods(MethodGet) r.HandleFunc( "/evidence", - queryAllEvidenceHandler(cliCtx), - ).Methods(MethodGet) - - r.HandleFunc( - "/evidence/params", - queryParamsHandler(cliCtx), + queryAllEvidenceHandler(clientCtx), ).Methods(MethodGet) } -func queryEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { +func queryEvidenceHandler(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) evidenceHash := vars[RestParamEvidenceHash] @@ -39,77 +35,61 @@ func queryEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQueryEvidenceParams(evidenceHash) - bz, err := cliCtx.Codec.MarshalJSON(params) + decodedHash, err := hex.DecodeString(evidenceHash) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, "invalid evidence hash") + return + } + + params := types.NewQueryEvidenceRequest(decodedHash) + bz, err := clientCtx.JSONMarshaler.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryEvidence) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryAllEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { +func queryAllEvidenceHandler(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryAllEvidenceParams(page, limit) - bz, err := cliCtx.Codec.MarshalJSON(params) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllEvidence) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) - } -} - -func queryParamsHandler(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) - if !ok { - return - } - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters) - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/evidence/client/rest/rest.go b/x/evidence/client/rest/rest.go index bb66afbbc073..f2714cc7ba4e 100644 --- a/x/evidence/client/rest/rest.go +++ b/x/evidence/client/rest/rest.go @@ -3,7 +3,8 @@ package rest import ( "net/http" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/gorilla/mux" ) @@ -24,7 +25,9 @@ type EvidenceRESTHandler struct { // RegisterRoutes registers all Evidence submission handlers for the evidence module's // REST service handler. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, handlers []EvidenceRESTHandler) { - registerQueryRoutes(cliCtx, r) - registerTxRoutes(cliCtx, r, handlers) +func RegisterRoutes(clientCtx client.Context, rtr *mux.Router, handlers []EvidenceRESTHandler) { + r := rest.WithHTTPDeprecationHeaders(rtr) + + registerQueryRoutes(clientCtx, r) + registerTxRoutes(clientCtx, r, handlers) } diff --git a/x/evidence/client/rest/tx.go b/x/evidence/client/rest/tx.go index b053e4fca6b8..0c3c0966a3d0 100644 --- a/x/evidence/client/rest/tx.go +++ b/x/evidence/client/rest/tx.go @@ -1,11 +1,11 @@ package rest import ( - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/gorilla/mux" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, handlers []EvidenceRESTHandler) { +func registerTxRoutes(clientCtx client.Context, r *mux.Router, handlers []EvidenceRESTHandler) { // TODO: Register tx handlers. } diff --git a/x/evidence/doc.go b/x/evidence/doc.go index 23fd4c9aed01..d68e0e64967c 100644 --- a/x/evidence/doc.go +++ b/x/evidence/doc.go @@ -20,13 +20,11 @@ A full setup of the evidence module may look something as follows: evidence.AppModuleBasic{}, ) - // First, create the keeper's subspace for parameters and the keeper itself. - evidenceParamspace := app.ParamsKeeper.Subspace(evidence.DefaultParamspace) + // First, create the keeper evidenceKeeper := evidence.NewKeeper( - app.cdc, keys[evidence.StoreKey], evidenceParamspace, evidence.DefaultCodespace, + appCodec, keys[evidence.StoreKey], &app.StakingKeeper, app.SlashingKeeper, ) - // Second, create the evidence Handler and register all desired routes. evidenceRouter := evidence.NewRouter(). AddRoute(evidenceRoute, evidenceHandler). @@ -34,9 +32,11 @@ A full setup of the evidence module may look something as follows: evidenceKeeper.SetRouter(evidenceRouter) + app.EvidenceKeeper = *evidenceKeeper + app.mm = module.NewManager( // ... - evidence.NewAppModule(evidenceKeeper), + evidence.NewAppModule(app.EvidenceKeeper), ) // Remaining application bootstrapping... diff --git a/x/evidence/exported/evidence.go b/x/evidence/exported/evidence.go index 55b9ef1671cc..8cf00fa6a93c 100644 --- a/x/evidence/exported/evidence.go +++ b/x/evidence/exported/evidence.go @@ -1,25 +1,34 @@ package exported import ( - sdk "github.com/cosmos/cosmos-sdk/types" - + "github.com/gogo/protobuf/proto" tmbytes "github.com/tendermint/tendermint/libs/bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // Evidence defines the contract which concrete evidence types of misbehavior // must implement. type Evidence interface { + proto.Message + Route() string Type() string String() string Hash() tmbytes.HexBytes ValidateBasic() error - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() sdk.ConsAddress - // Height at which the infraction occurred GetHeight() int64 +} + +// ValidatorEvidence extends Evidence interface to define contract +// for evidence against malicious validators +type ValidatorEvidence interface { + Evidence + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() sdk.ConsAddress // The total power of the malicious validator at time of infraction GetValidatorPower() int64 @@ -27,3 +36,13 @@ type Evidence interface { // The total validator set power at time of infraction GetTotalPower() int64 } + +// MsgSubmitEvidenceI defines the specific interface a concrete message must +// implement in order to process submitted evidence. The concrete MsgSubmitEvidence +// must be defined at the application-level. +type MsgSubmitEvidenceI interface { + sdk.Msg + + GetEvidence() Evidence + GetSubmitter() sdk.AccAddress +} diff --git a/x/evidence/genesis.go b/x/evidence/genesis.go index 2ec9e954202e..2be3fee88276 100644 --- a/x/evidence/genesis.go +++ b/x/evidence/genesis.go @@ -3,31 +3,51 @@ package evidence import ( "fmt" + "github.com/gogo/protobuf/proto" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/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" ) // InitGenesis initializes the evidence module's state from a provided genesis // state. -func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) { +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs *types.GenesisState) { if err := gs.Validate(); err != nil { - panic(fmt.Sprintf("failed to validate %s genesis state: %s", ModuleName, err)) + panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err)) } for _, e := range gs.Evidence { - if _, ok := k.GetEvidence(ctx, e.Hash()); ok { - panic(fmt.Sprintf("evidence with hash %s already exists", e.Hash())) + evi, ok := e.GetCachedValue().(exported.Evidence) + if !ok { + panic("expected evidence") + } + if _, ok := k.GetEvidence(ctx, evi.Hash()); ok { + panic(fmt.Sprintf("evidence with hash %s already exists", evi.Hash())) } - k.SetEvidence(ctx, e) + k.SetEvidence(ctx, evi) } - - k.SetParams(ctx, gs.Params) } // ExportGenesis returns the evidence module's exported genesis. -func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { - return GenesisState{ - Params: k.GetParams(ctx), - Evidence: k.GetAllEvidence(ctx), +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + e := k.GetAllEvidence(ctx) + evidence := make([]*codectypes.Any, len(e)) + for i, evi := range e { + msg, ok := evi.(proto.Message) + if !ok { + panic(fmt.Errorf("cannot proto marshal %T", evi)) + } + any, err := codectypes.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + evidence[i] = any + } + return &types.GenesisState{ + Evidence: evidence, } } diff --git a/x/evidence/genesis_test.go b/x/evidence/genesis_test.go index 10a8a2c75730..3ae8eee03cd8 100644 --- a/x/evidence/genesis_test.go +++ b/x/evidence/genesis_test.go @@ -1,108 +1,156 @@ package evidence_test import ( + "fmt" "testing" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/types/time" + "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/x/evidence" "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - - "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) type GenesisTestSuite struct { suite.Suite ctx sdk.Context - keeper evidence.Keeper + keeper keeper.Keeper } func (suite *GenesisTestSuite) SetupTest() { checkTx := false app := simapp.Setup(checkTx) - // get the app's codec and register custom testing types - cdc := app.Codec() - cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.keeper = app.EvidenceKeeper +} - // recreate keeper in order to use custom testing types - evidenceKeeper := evidence.NewKeeper( - cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), app.StakingKeeper, app.SlashingKeeper, +func (suite *GenesisTestSuite) TestInitGenesis() { + var ( + genesisState *types.GenesisState + testEvidence []exported.Evidence + pk = ed25519.GenPrivKey() ) - router := evidence.NewRouter() - router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) - evidenceKeeper.SetRouter(router) - suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) - suite.keeper = *evidenceKeeper -} + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func() + }{ + { + "valid", + func() { + testEvidence = make([]exported.Evidence, 100) + for i := 0; i < 100; i++ { + testEvidence[i] = &types.Equivocation{ + Height: int64(i + 1), + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + } + } + genesisState = types.NewGenesisState(testEvidence) + }, + true, + func() { + for _, e := range testEvidence { + _, ok := suite.keeper.GetEvidence(suite.ctx, e.Hash()) + suite.True(ok) + } + }, + }, + { + "invalid", + func() { + testEvidence = make([]exported.Evidence, 100) + for i := 0; i < 100; i++ { + testEvidence[i] = &types.Equivocation{ + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + } + } + genesisState = types.NewGenesisState(testEvidence) + }, + false, + func() { + suite.Empty(suite.keeper.GetAllEvidence(suite.ctx)) + }, + }, + } -func (suite *GenesisTestSuite) TestInitGenesis_Valid() { - pk := ed25519.GenPrivKey() + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() - testEvidence := make([]exported.Evidence, 100) - for i := 0; i < 100; i++ { - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - sig, err := pk.Sign(sv.SignBytes("test-chain")) - suite.NoError(err) - sv.Signature = sig - - testEvidence[i] = types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - } + tc.malleate() - suite.NotPanics(func() { - evidence.InitGenesis(suite.ctx, suite.keeper, evidence.NewGenesisState(types.DefaultParams(), testEvidence)) - }) + if tc.expPass { + suite.NotPanics(func() { + evidence.InitGenesis(suite.ctx, suite.keeper, genesisState) + }) + } else { + suite.Panics(func() { + evidence.InitGenesis(suite.ctx, suite.keeper, genesisState) + }) + } - for _, e := range testEvidence { - _, ok := suite.keeper.GetEvidence(suite.ctx, e.Hash()) - suite.True(ok) + tc.posttests() + }) } } -func (suite *GenesisTestSuite) TestInitGenesis_Invalid() { +func (suite *GenesisTestSuite) TestExportGenesis() { pk := ed25519.GenPrivKey() - testEvidence := make([]exported.Evidence, 100) - for i := 0; i < 100; i++ { - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - sig, err := pk.Sign(sv.SignBytes("test-chain")) - suite.NoError(err) - sv.Signature = sig - - testEvidence[i] = types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: types.TestVote{Height: 10, Round: 1}, - } + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func() + }{ + { + "success", + func() { + suite.keeper.SetEvidence(suite.ctx, &types.Equivocation{ + Height: 1, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + }) + }, + true, + func() {}, + }, } - suite.Panics(func() { - evidence.InitGenesis(suite.ctx, suite.keeper, evidence.NewGenesisState(types.DefaultParams(), testEvidence)) - }) + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() - suite.Empty(suite.keeper.GetAllEvidence(suite.ctx)) + if tc.expPass { + suite.NotPanics(func() { + evidence.ExportGenesis(suite.ctx, suite.keeper) + }) + } else { + suite.Panics(func() { + evidence.ExportGenesis(suite.ctx, suite.keeper) + }) + } + + tc.posttests() + }) + } } func TestGenesisTestSuite(t *testing.T) { diff --git a/x/evidence/handler.go b/x/evidence/handler.go index d5af1206bc01..93da4d7bf50d 100644 --- a/x/evidence/handler.go +++ b/x/evidence/handler.go @@ -3,37 +3,24 @@ package evidence import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) -func NewHandler(k Keeper) sdk.Handler { +// NewHandler returns a handler for evidence messages. +func NewHandler(k keeper.Keeper) sdk.Handler { + msgServer := keeper.NewMsgServerImpl(k) + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case MsgSubmitEvidence: - return handleMsgSubmitEvidence(ctx, k, msg) + case *types.MsgSubmitEvidence: + res, err := msgServer.SubmitEvidence(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } } } - -func handleMsgSubmitEvidence(ctx sdk.Context, k Keeper, msg MsgSubmitEvidence) (*sdk.Result, error) { - if err := k.SubmitEvidence(ctx, msg.Evidence); err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Submitter.String()), - ), - ) - - return &sdk.Result{ - Data: msg.Evidence.Hash(), - Events: ctx.EventManager().Events(), - }, nil -} diff --git a/x/evidence/handler_test.go b/x/evidence/handler_test.go index e5798bd01482..b3f95c300a4f 100644 --- a/x/evidence/handler_test.go +++ b/x/evidence/handler_test.go @@ -1,102 +1,125 @@ package evidence_test import ( + "fmt" "testing" + "time" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "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/x/evidence" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - - "github.com/stretchr/testify/suite" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) type HandlerTestSuite struct { suite.Suite - ctx sdk.Context handler sdk.Handler - keeper evidence.Keeper + app *simapp.SimApp +} + +func testMsgSubmitEvidence(r *require.Assertions, e exported.Evidence, s sdk.AccAddress) exported.MsgSubmitEvidenceI { + msg, err := types.NewMsgSubmitEvidence(s, e) + r.NoError(err) + return msg +} + +func testEquivocationHandler(k interface{}) types.Handler { + return func(ctx sdk.Context, e exported.Evidence) error { + if err := e.ValidateBasic(); err != nil { + return err + } + + ee, ok := e.(*types.Equivocation) + if !ok { + return fmt.Errorf("unexpected evidence type: %T", e) + } + if ee.Height%2 == 0 { + return fmt.Errorf("unexpected even evidence height: %d", ee.Height) + } + + return nil + } } func (suite *HandlerTestSuite) SetupTest() { checkTx := false app := simapp.Setup(checkTx) - // get the app's codec and register custom testing types - cdc := app.Codec() - cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) - // recreate keeper in order to use custom testing types - evidenceKeeper := evidence.NewKeeper( - cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), app.StakingKeeper, app.SlashingKeeper, + evidenceKeeper := keeper.NewKeeper( + app.AppCodec(), app.GetKey(types.StoreKey), app.StakingKeeper, app.SlashingKeeper, ) - router := evidence.NewRouter() - router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) + router := types.NewRouter() + router = router.AddRoute(types.RouteEquivocation, testEquivocationHandler(*evidenceKeeper)) evidenceKeeper.SetRouter(router) - suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) + app.EvidenceKeeper = *evidenceKeeper + suite.handler = evidence.NewHandler(*evidenceKeeper) - suite.keeper = *evidenceKeeper + suite.app = app } -func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Valid() { +func (suite *HandlerTestSuite) TestMsgSubmitEvidence() { pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, + s := sdk.AccAddress("test________________") + + testCases := []struct { + msg sdk.Msg + expectErr bool + }{ + { + testMsgSubmitEvidence( + suite.Require(), + &types.Equivocation{ + Height: 11, + Time: time.Now().UTC(), + Power: 100, + ConsensusAddress: pk.PubKey().Address().String(), + }, + s, + ), + false, + }, + { + testMsgSubmitEvidence( + suite.Require(), + &types.Equivocation{ + Height: 10, + Time: time.Now().UTC(), + Power: 100, + ConsensusAddress: pk.PubKey().Address().String(), + }, + s, + ), + true, + }, } - sig, err := pk.Sign(sv.SignBytes(suite.ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - s := sdk.AccAddress("test") - e := types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } + for i, tc := range testCases { + ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) - ctx := suite.ctx.WithIsCheckTx(false) - msg := evidence.NewMsgSubmitEvidence(e, s) - res, err := suite.handler(ctx, msg) - suite.NoError(err) - suite.NotNil(res) - suite.Equal(e.Hash().Bytes(), res.Data) -} + res, err := suite.handler(ctx, tc.msg) + if tc.expectErr { + suite.Require().Error(err, "expected error; tc #%d", i) + } else { + suite.Require().NoError(err, "unexpected error; tc #%d", i) + suite.Require().NotNil(res, "expected non-nil result; tc #%d", i) -func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Invalid() { - pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, - } + msg := tc.msg.(exported.MsgSubmitEvidenceI) - sig, err := pk.Sign(sv.SignBytes(suite.ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - s := sdk.AccAddress("test") - e := types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: types.TestVote{Height: 10, Round: 1}, + var resultData types.MsgSubmitEvidenceResponse + suite.app.AppCodec().UnmarshalBinaryBare(res.Data, &resultData) + suite.Require().Equal(msg.GetEvidence().Hash().Bytes(), resultData.Hash, "invalid hash; tc #%d", i) + } } - - ctx := suite.ctx.WithIsCheckTx(false) - msg := evidence.NewMsgSubmitEvidence(e, s) - res, err := suite.handler(ctx, msg) - suite.Error(err) - suite.Nil(res) } func TestHandlerTestSuite(t *testing.T) { diff --git a/x/evidence/internal/keeper/infraction.go b/x/evidence/internal/keeper/infraction.go deleted file mode 100644 index fab16c833e74..000000000000 --- a/x/evidence/internal/keeper/infraction.go +++ /dev/null @@ -1,109 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -// HandleDoubleSign implements an equivocation evidence handler. Assuming the -// evidence is valid, the validator committing the misbehavior will be slashed, -// jailed and tombstoned. Once tombstoned, the validator will not be able to -// recover. Note, the evidence contains the block time and height at the time of -// the equivocation. -// -// The evidence is considered invalid if: -// - the evidence is too old -// - the validator is unbonded or does not exist -// - the signing info does not exist (will panic) -// - is already tombstoned -// -// TODO: Some of the invalid constraints listed above may need to be reconsidered -// in the case of a lunatic attack. -func (k Keeper) HandleDoubleSign(ctx sdk.Context, evidence types.Equivocation) { - logger := k.Logger(ctx) - consAddr := evidence.GetConsensusAddress() - infractionHeight := evidence.GetHeight() - - // calculate the age of the evidence - blockTime := ctx.BlockHeader().Time - age := blockTime.Sub(evidence.GetTime()) - - if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { - // Ignore evidence that cannot be handled. - // - // NOTE: We used to panic with: - // `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`, - // but this couples the expectations of the app to both Tendermint and - // the simulator. Both are expected to provide the full range of - // allowable but none of the disallowed evidence types. Instead of - // getting this coordination right, it is easier to relax the - // constraints and ignore evidence that cannot be handled. - return - } - - // reject evidence if the double-sign is too old - if age > k.MaxEvidenceAge(ctx) { - logger.Info( - fmt.Sprintf( - "ignored double sign from %s at height %d, age of %d past max age of %d", - consAddr, infractionHeight, age, k.MaxEvidenceAge(ctx), - ), - ) - return - } - - validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) - if validator == nil || validator.IsUnbonded() { - // Defensive: Simulation doesn't take unbonding periods into account, and - // Tendermint might break this assumption at some point. - return - } - - if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { - panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr)) - } - - // ignore if the validator is already tombstoned - if k.slashingKeeper.IsTombstoned(ctx, consAddr) { - logger.Info( - fmt.Sprintf( - "ignored double sign from %s at height %d, validator already tombstoned", - consAddr, infractionHeight, - ), - ) - return - } - - logger.Info(fmt.Sprintf("confirmed double sign from %s at height %d, age of %d", consAddr, infractionHeight, age)) - - // We need to retrieve the stake distribution which signed the block, so we - // subtract ValidatorUpdateDelay from the evidence height. - // Note, that this *can* result in a negative "distributionHeight", up to - // -ValidatorUpdateDelay, i.e. at the end of the - // pre-genesis block (none) = at the beginning of the genesis block. - // That's fine since this is just used to filter unbonding delegations & redelegations. - distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay - - // Slash validator. The `power` is the int64 power of the validator as provided - // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via - // ABCI, and now received as evidence. The fraction is passed in to separately - // to slash unbonding and rebonding delegations. - k.slashingKeeper.Slash( - ctx, - consAddr, - k.slashingKeeper.SlashFractionDoubleSign(ctx), - evidence.GetValidatorPower(), distributionHeight, - ) - - // Jail the validator if not already jailed. This will begin unbonding the - // validator if not already unbonding (tombstoned). - if !validator.IsJailed() { - k.slashingKeeper.Jail(ctx, consAddr) - } - - k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) - k.slashingKeeper.Tombstone(ctx, consAddr) -} diff --git a/x/evidence/internal/keeper/infraction_test.go b/x/evidence/internal/keeper/infraction_test.go deleted file mode 100644 index 6d6c0dc26aa5..000000000000 --- a/x/evidence/internal/keeper/infraction_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package keeper_test - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/cosmos/cosmos-sdk/x/staking" - - "github.com/tendermint/tendermint/crypto" -) - -func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator { - commission := staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - return staking.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), - staking.Description{}, commission, sdk.OneInt(), - ) -} - -func (suite *KeeperTestSuite) TestHandleDoubleSign() { - ctx := suite.ctx.WithIsCheckTx(false).WithBlockHeight(1) - suite.populateValidators(ctx) - - power := int64(100) - stakingParams := suite.app.StakingKeeper.GetParams(ctx) - amt := sdk.TokensFromConsensusPower(power) - operatorAddr, val := valAddresses[0], pubkeys[0] - - // create validator - res, err := staking.NewHandler(suite.app.StakingKeeper)(ctx, newTestMsgCreateValidator(operatorAddr, val, amt)) - suite.NoError(err) - suite.NotNil(res) - - // execute end-blocker and verify validator attributes - staking.EndBlocker(ctx, suite.app.StakingKeeper) - suite.Equal( - suite.app.BankKeeper.GetCoins(ctx, sdk.AccAddress(operatorAddr)), - sdk.NewCoins(sdk.NewCoin(stakingParams.BondDenom, initAmt.Sub(amt))), - ) - suite.Equal(amt, suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetBondedTokens()) - - // handle a signature to set signing info - suite.app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), sdk.TokensToConsensusPower(amt), true) - - // double sign less than max age - oldTokens := suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens() - evidence := types.Equivocation{ - Height: 0, - Time: time.Unix(0, 0), - Power: power, - ConsensusAddress: sdk.ConsAddress(val.Address()), - } - suite.keeper.HandleDoubleSign(ctx, evidence) - - // should be jailed and tombstoned - suite.True(suite.app.StakingKeeper.Validator(ctx, operatorAddr).IsJailed()) - suite.True(suite.app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(val.Address()))) - - // tokens should be decreased - newTokens := suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens() - suite.True(newTokens.LT(oldTokens)) - - // submit duplicate evidence - suite.keeper.HandleDoubleSign(ctx, evidence) - - // tokens should be the same (capped slash) - suite.True(suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens().Equal(newTokens)) - - // jump to past the unbonding period - ctx = ctx.WithBlockTime(time.Unix(1, 0).Add(stakingParams.UnbondingTime)) - - // require we cannot unjail - suite.Error(suite.app.SlashingKeeper.Unjail(ctx, operatorAddr)) - - // require we be able to unbond now - ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - del, _ := suite.app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr) - validator, _ := suite.app.StakingKeeper.GetValidator(ctx, operatorAddr) - totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt() - msgUnbond := staking.NewMsgUndelegate(sdk.AccAddress(operatorAddr), operatorAddr, sdk.NewCoin(stakingParams.BondDenom, totalBond)) - res, err = staking.NewHandler(suite.app.StakingKeeper)(ctx, msgUnbond) - suite.NoError(err) - suite.NotNil(res) -} - -func (suite *KeeperTestSuite) TestHandleDoubleSign_TooOld() { - ctx := suite.ctx.WithIsCheckTx(false).WithBlockHeight(1).WithBlockTime(time.Now()) - suite.populateValidators(ctx) - - power := int64(100) - stakingParams := suite.app.StakingKeeper.GetParams(ctx) - amt := sdk.TokensFromConsensusPower(power) - operatorAddr, val := valAddresses[0], pubkeys[0] - - // create validator - res, err := staking.NewHandler(suite.app.StakingKeeper)(ctx, newTestMsgCreateValidator(operatorAddr, val, amt)) - suite.NoError(err) - suite.NotNil(res) - - // execute end-blocker and verify validator attributes - staking.EndBlocker(ctx, suite.app.StakingKeeper) - suite.Equal( - suite.app.BankKeeper.GetCoins(ctx, sdk.AccAddress(operatorAddr)), - sdk.NewCoins(sdk.NewCoin(stakingParams.BondDenom, initAmt.Sub(amt))), - ) - suite.Equal(amt, suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetBondedTokens()) - - evidence := types.Equivocation{ - Height: 0, - Time: ctx.BlockTime(), - Power: power, - ConsensusAddress: sdk.ConsAddress(val.Address()), - } - ctx = ctx.WithBlockTime(ctx.BlockTime().Add(suite.app.EvidenceKeeper.MaxEvidenceAge(ctx) + 1)) - suite.keeper.HandleDoubleSign(ctx, evidence) - - suite.False(suite.app.StakingKeeper.Validator(ctx, operatorAddr).IsJailed()) - suite.False(suite.app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(val.Address()))) -} diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go deleted file mode 100644 index 29a3df0bf374..000000000000 --- a/x/evidence/internal/keeper/keeper.go +++ /dev/null @@ -1,156 +0,0 @@ -package keeper - -import ( - "fmt" - - tmbytes "github.com/tendermint/tendermint/libs/bytes" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper defines the evidence module's keeper. The keeper is responsible for -// managing persistence, state transitions and query handling for the evidence -// module. -type Keeper struct { - cdc *codec.Codec - storeKey sdk.StoreKey - paramSpace params.Subspace - router types.Router - stakingKeeper types.StakingKeeper - slashingKeeper types.SlashingKeeper -} - -func NewKeeper( - cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, - stakingKeeper types.StakingKeeper, slashingKeeper types.SlashingKeeper, -) *Keeper { - - // set KeyTable if it has not already been set - if !paramSpace.HasKeyTable() { - paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) - } - - return &Keeper{ - cdc: cdc, - storeKey: storeKey, - paramSpace: paramSpace, - stakingKeeper: stakingKeeper, - slashingKeeper: slashingKeeper, - } -} - -// 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)) -} - -// SetRouter sets the Evidence Handler router for the x/evidence module. Note, -// we allow the ability to set the router after the Keeper is constructed as a -// given Handler may need access the Keeper before being constructed. The router -// may only be set once and will be sealed if it's not already sealed. -func (k *Keeper) SetRouter(rtr types.Router) { - // It is vital to seal the Evidence Handler router as to not allow further - // handlers to be registered after the keeper is created since this - // could create invalid or non-deterministic behavior. - if !rtr.Sealed() { - rtr.Seal() - } - if k.router != nil { - panic(fmt.Sprintf("attempting to reset router on x/%s", types.ModuleName)) - } - - k.router = rtr -} - -// GetEvidenceHandler returns a registered Handler for a given Evidence type. If -// no handler exists, an error is returned. -func (k Keeper) GetEvidenceHandler(evidenceRoute string) (types.Handler, error) { - if !k.router.HasRoute(evidenceRoute) { - return nil, sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidenceRoute) - } - - return k.router.GetRoute(evidenceRoute), nil -} - -// SubmitEvidence attempts to match evidence against the keepers router and execute -// the corresponding registered Evidence Handler. An error is returned if no -// registered Handler exists or if the Handler fails. Otherwise, the evidence is -// persisted. -func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence exported.Evidence) error { - if _, ok := k.GetEvidence(ctx, evidence.Hash()); ok { - return sdkerrors.Wrap(types.ErrEvidenceExists, evidence.Hash().String()) - } - if !k.router.HasRoute(evidence.Route()) { - return sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route()) - } - - handler := k.router.GetRoute(evidence.Route()) - if err := handler(ctx, evidence); err != nil { - return sdkerrors.Wrap(types.ErrInvalidEvidence, err.Error()) - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeSubmitEvidence, - sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), - ), - ) - - k.SetEvidence(ctx, evidence) - return nil -} - -// SetEvidence sets Evidence by hash in the module's KVStore. -func (k Keeper) SetEvidence(ctx sdk.Context, evidence exported.Evidence) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) - store.Set(evidence.Hash(), bz) -} - -// GetEvidence retrieves Evidence by hash if it exists. If no Evidence exists for -// the given hash, (nil, false) is returned. -func (k Keeper) GetEvidence(ctx sdk.Context, hash tmbytes.HexBytes) (evidence exported.Evidence, found bool) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) - - bz := store.Get(hash) - if len(bz) == 0 { - return nil, false - } - - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &evidence) - return evidence, true -} - -// IterateEvidence provides an interator over all stored Evidence objects. For -// each Evidence object, cb will be called. If the cb returns true, the iterator -// will close and stop. -func (k Keeper) IterateEvidence(ctx sdk.Context, cb func(exported.Evidence) bool) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) - iterator := sdk.KVStorePrefixIterator(store, nil) - - defer iterator.Close() - for ; iterator.Valid(); iterator.Next() { - var evidence exported.Evidence - k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &evidence) - - if cb(evidence) { - break - } - } -} - -// GetAllEvidence returns all stored Evidence objects. -func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []exported.Evidence) { - k.IterateEvidence(ctx, func(e exported.Evidence) bool { - evidence = append(evidence, e) - return false - }) - return evidence -} diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go deleted file mode 100644 index 45347ab96be0..000000000000 --- a/x/evidence/internal/keeper/keeper_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package keeper_test - -import ( - "encoding/hex" - "testing" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/cosmos/cosmos-sdk/x/supply" - - "github.com/stretchr/testify/suite" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" -) - -var ( - pubkeys = []crypto.PubKey{ - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"), - } - - valAddresses = []sdk.ValAddress{ - sdk.ValAddress(pubkeys[0].Address()), - sdk.ValAddress(pubkeys[1].Address()), - sdk.ValAddress(pubkeys[2].Address()), - } - - initAmt = sdk.TokensFromConsensusPower(200) - initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) -) - -func newPubKey(pk string) (res crypto.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - - var pubkey ed25519.PubKeyEd25519 - copy(pubkey[:], pkBytes) - - return pubkey -} - -type KeeperTestSuite struct { - suite.Suite - - ctx sdk.Context - querier sdk.Querier - keeper keeper.Keeper - app *simapp.SimApp -} - -func (suite *KeeperTestSuite) SetupTest() { - checkTx := false - app := simapp.Setup(checkTx) - - // get the app's codec and register custom testing types - cdc := app.Codec() - cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) - - // recreate keeper in order to use custom testing types - evidenceKeeper := evidence.NewKeeper( - cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), app.StakingKeeper, app.SlashingKeeper, - ) - router := evidence.NewRouter() - router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) - evidenceKeeper.SetRouter(router) - - suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) - suite.querier = keeper.NewQuerier(*evidenceKeeper) - suite.keeper = *evidenceKeeper - suite.app = app -} - -func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []exported.Evidence { - evidence := make([]exported.Evidence, numEvidence) - - for i := 0; i < numEvidence; i++ { - pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - - sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - evidence[i] = types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - - suite.Nil(suite.keeper.SubmitEvidence(ctx, evidence[i])) - } - - return evidence -} - -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.SupplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - for _, addr := range valAddresses { - _, err := suite.app.BankKeeper.AddCoins(ctx, sdk.AccAddress(addr), initCoins) - suite.NoError(err) - } -} - -func (suite *KeeperTestSuite) TestSubmitValidEvidence() { - ctx := suite.ctx.WithIsCheckTx(false) - pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, - } - - sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - e := types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - - suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) - - res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) - suite.True(ok) - suite.Equal(e, res) -} - -func (suite *KeeperTestSuite) TestSubmitValidEvidence_Duplicate() { - ctx := suite.ctx.WithIsCheckTx(false) - pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, - } - - sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - e := types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - - suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) - suite.Error(suite.keeper.SubmitEvidence(ctx, e)) - - res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) - suite.True(ok) - suite.Equal(e, res) -} - -func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { - ctx := suite.ctx.WithIsCheckTx(false) - pk := ed25519.GenPrivKey() - e := types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 10, - Round: 0, - }, - VoteB: types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, - }, - } - - suite.Error(suite.keeper.SubmitEvidence(ctx, e)) - - res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) - suite.False(ok) - suite.Nil(res) -} - -func (suite *KeeperTestSuite) TestIterateEvidence() { - ctx := suite.ctx.WithIsCheckTx(false) - numEvidence := 100 - suite.populateEvidence(ctx, numEvidence) - - evidence := suite.keeper.GetAllEvidence(ctx) - suite.Len(evidence, numEvidence) -} - -func (suite *KeeperTestSuite) TestGetEvidenceHandler() { - handler, err := suite.keeper.GetEvidenceHandler(types.TestEquivocationEvidence{}.Route()) - suite.NoError(err) - suite.NotNil(handler) - - handler, err = suite.keeper.GetEvidenceHandler("invalidHandler") - suite.Error(err) - suite.Nil(handler) -} - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(KeeperTestSuite)) -} diff --git a/x/evidence/internal/keeper/params.go b/x/evidence/internal/keeper/params.go deleted file mode 100644 index 8db4867781e7..000000000000 --- a/x/evidence/internal/keeper/params.go +++ /dev/null @@ -1,25 +0,0 @@ -package keeper - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -// MaxEvidenceAge returns the maximum age for submitted evidence. -func (k Keeper) MaxEvidenceAge(ctx sdk.Context) (res time.Duration) { - k.paramSpace.Get(ctx, types.KeyMaxEvidenceAge, &res) - return -} - -// GetParams returns the total set of evidence parameters. -func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - k.paramSpace.GetParamSet(ctx, ¶ms) - return params -} - -// SetParams sets the evidence parameters to the param space. -func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - k.paramSpace.SetParamSet(ctx, ¶ms) -} diff --git a/x/evidence/internal/keeper/params_test.go b/x/evidence/internal/keeper/params_test.go deleted file mode 100644 index 58d25230eec7..000000000000 --- a/x/evidence/internal/keeper/params_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package keeper_test - -import ( - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -func (suite *KeeperTestSuite) TestParams() { - ctx := suite.ctx.WithIsCheckTx(false) - suite.Equal(types.DefaultParams(), suite.keeper.GetParams(ctx)) - suite.Equal(types.DefaultMaxEvidenceAge, suite.keeper.MaxEvidenceAge(ctx)) -} diff --git a/x/evidence/internal/keeper/querier.go b/x/evidence/internal/keeper/querier.go deleted file mode 100644 index cbb616677e8f..000000000000 --- a/x/evidence/internal/keeper/querier.go +++ /dev/null @@ -1,101 +0,0 @@ -package keeper - -import ( - "encoding/hex" - - "github.com/cosmos/cosmos-sdk/client" - "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/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - - abci "github.com/tendermint/tendermint/abci/types" -) - -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - var ( - res []byte - err error - ) - - switch path[0] { - case types.QueryParameters: - res, err = queryParams(ctx, k) - - case types.QueryEvidence: - res, err = queryEvidence(ctx, req, k) - - case types.QueryAllEvidence: - res, err = queryAllEvidence(ctx, req, k) - - default: - err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - - return res, err - } -} - -func queryParams(ctx sdk.Context, k Keeper) ([]byte, error) { - params := k.GetParams(ctx) - - res, err := codec.MarshalJSONIndent(k.cdc, params) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func queryEvidence(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryEvidenceParams - - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - hash, err := hex.DecodeString(params.EvidenceHash) - if err != nil { - return nil, sdkerrors.Wrap(err, "failed to decode evidence hash string query") - } - - evidence, ok := k.GetEvidence(ctx, hash) - if !ok { - return nil, sdkerrors.Wrap(types.ErrNoEvidenceExists, params.EvidenceHash) - } - - res, err := codec.MarshalJSONIndent(k.cdc, evidence) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func queryAllEvidence(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryAllEvidenceParams - - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - evidence := k.GetAllEvidence(ctx) - - start, end := client.Paginate(len(evidence), params.Page, params.Limit, 100) - if start < 0 || end < 0 { - evidence = []exported.Evidence{} - } else { - evidence = evidence[start:end] - } - - res, err := codec.MarshalJSONIndent(k.cdc, evidence) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} diff --git a/x/evidence/internal/keeper/querier_test.go b/x/evidence/internal/keeper/querier_test.go deleted file mode 100644 index 68af96640ab4..000000000000 --- a/x/evidence/internal/keeper/querier_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package keeper_test - -import ( - "strings" - - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - - abci "github.com/tendermint/tendermint/abci/types" -) - -const ( - custom = "custom" -) - -func (suite *KeeperTestSuite) TestQueryEvidence_Existing() { - ctx := suite.ctx.WithIsCheckTx(false) - numEvidence := 100 - - evidence := suite.populateEvidence(ctx, numEvidence) - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), - Data: types.TestingCdc.MustMarshalJSON(types.NewQueryEvidenceParams(evidence[0].Hash().String())), - } - - bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) - suite.Nil(err) - suite.NotNil(bz) - - var e exported.Evidence - suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) - suite.Equal(evidence[0], e) -} - -func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() { - ctx := suite.ctx.WithIsCheckTx(false) - numEvidence := 100 - - suite.populateEvidence(ctx, numEvidence) - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), - Data: types.TestingCdc.MustMarshalJSON(types.NewQueryEvidenceParams("0000000000000000000000000000000000000000000000000000000000000000")), - } - - bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) - suite.NotNil(err) - suite.Nil(bz) -} - -func (suite *KeeperTestSuite) TestQueryAllEvidence() { - ctx := suite.ctx.WithIsCheckTx(false) - numEvidence := 100 - - suite.populateEvidence(ctx, numEvidence) - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), - Data: types.TestingCdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(1, numEvidence)), - } - - bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) - suite.Nil(err) - suite.NotNil(bz) - - var e []exported.Evidence - suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) - suite.Len(e, numEvidence) -} - -func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() { - ctx := suite.ctx.WithIsCheckTx(false) - numEvidence := 100 - - suite.populateEvidence(ctx, numEvidence) - query := abci.RequestQuery{ - Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), - Data: types.TestingCdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(0, numEvidence)), - } - - bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) - suite.Nil(err) - suite.NotNil(bz) - - var e []exported.Evidence - suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) - suite.Len(e, 0) -} - -func (suite *KeeperTestSuite) TestQueryParams() { - ctx := suite.ctx.WithIsCheckTx(false) - - bz, err := suite.querier(ctx, []string{types.QueryParameters}, abci.RequestQuery{}) - suite.Nil(err) - suite.NotNil(bz) - suite.Equal("{\n \"max_evidence_age\": \"120000000000\"\n}", string(bz)) -} diff --git a/x/evidence/internal/types/codec.go b/x/evidence/internal/types/codec.go deleted file mode 100644 index 7c57bc4a71f8..000000000000 --- a/x/evidence/internal/types/codec.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" -) - -// ModuleCdc defines the evidence module's codec. The codec is not sealed as to -// allow other modules to register their concrete Evidence types. -var ModuleCdc = codec.New() - -// RegisterCodec registers all the necessary types and interfaces for the -// evidence module. -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*exported.Evidence)(nil), nil) - cdc.RegisterConcrete(MsgSubmitEvidence{}, "cosmos-sdk/MsgSubmitEvidence", nil) - cdc.RegisterConcrete(Equivocation{}, "cosmos-sdk/Equivocation", nil) -} - -// RegisterEvidenceTypeCodec registers an external concrete Evidence type defined -// in another module for the internal ModuleCdc. This allows the MsgSubmitEvidence -// to be correctly Amino encoded and decoded. -func RegisterEvidenceTypeCodec(o interface{}, name string) { - ModuleCdc.RegisterConcrete(o, name, nil) -} - -func init() { - RegisterCodec(ModuleCdc) -} diff --git a/x/evidence/internal/types/codec_test.go b/x/evidence/internal/types/codec_test.go deleted file mode 100644 index e175a7292e94..000000000000 --- a/x/evidence/internal/types/codec_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - tmbytes "github.com/tendermint/tendermint/libs/bytes" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -var _ exported.Evidence = (*testEvidence)(nil) - -type testEvidence struct{} - -func (te testEvidence) Route() string { return "" } -func (te testEvidence) Type() string { return "" } -func (te testEvidence) String() string { return "" } -func (te testEvidence) ValidateBasic() error { return nil } -func (te testEvidence) GetConsensusAddress() sdk.ConsAddress { return nil } -func (te testEvidence) Hash() tmbytes.HexBytes { return nil } -func (te testEvidence) GetHeight() int64 { return 0 } -func (te testEvidence) GetValidatorPower() int64 { return 0 } -func (te testEvidence) GetTotalPower() int64 { return 0 } - -func TestCodec(t *testing.T) { - cdc := codec.New() - types.RegisterCodec(cdc) - types.RegisterEvidenceTypeCodec(testEvidence{}, "cosmos-sdk/testEvidence") - - var e exported.Evidence = testEvidence{} - bz, err := cdc.MarshalBinaryBare(e) - require.NoError(t, err) - - var te testEvidence - require.NoError(t, cdc.UnmarshalBinaryBare(bz, &te)) - - require.Panics(t, func() { types.RegisterEvidenceTypeCodec(testEvidence{}, "cosmos-sdk/testEvidence") }) -} diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go deleted file mode 100644 index 83ffe06002ae..000000000000 --- a/x/evidence/internal/types/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -// DONTCOVER -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// x/evidence module sentinel errors -var ( - ErrNoEvidenceHandlerExists = sdkerrors.Register(ModuleName, 1, "unregistered handler for evidence type") - ErrInvalidEvidence = sdkerrors.Register(ModuleName, 2, "invalid evidence") - ErrNoEvidenceExists = sdkerrors.Register(ModuleName, 3, "evidence does not exist") - ErrEvidenceExists = sdkerrors.Register(ModuleName, 4, "evidence already exists") -) diff --git a/x/evidence/internal/types/evidence.go b/x/evidence/internal/types/evidence.go deleted file mode 100644 index 4306acbe0de7..000000000000 --- a/x/evidence/internal/types/evidence.go +++ /dev/null @@ -1,101 +0,0 @@ -package types - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/tmhash" - tmbytes "github.com/tendermint/tendermint/libs/bytes" - "gopkg.in/yaml.v2" -) - -// Evidence type constants -const ( - RouteEquivocation = "equivocation" - TypeEquivocation = "equivocation" -) - -var _ exported.Evidence = (*Equivocation)(nil) - -// Equivocation implements the Evidence interface and defines evidence of double -// signing misbehavior. -type Equivocation struct { - Height int64 `json:"height" yaml:"height"` - Time time.Time `json:"time" yaml:"time"` - Power int64 `json:"power" yaml:"power"` - ConsensusAddress sdk.ConsAddress `json:"consensus_address" yaml:"consensus_address"` -} - -// Route returns the Evidence Handler route for an Equivocation type. -func (e Equivocation) Route() string { return RouteEquivocation } - -// Type returns the Evidence Handler type for an Equivocation type. -func (e Equivocation) Type() string { return TypeEquivocation } - -func (e Equivocation) String() string { - bz, _ := yaml.Marshal(e) - return string(bz) -} - -// Hash returns the hash of an Equivocation object. -func (e Equivocation) Hash() tmbytes.HexBytes { - return tmhash.Sum(ModuleCdc.MustMarshalBinaryBare(e)) -} - -// ValidateBasic performs basic stateless validation checks on an Equivocation object. -func (e Equivocation) ValidateBasic() error { - if e.Time.IsZero() { - return fmt.Errorf("invalid equivocation time: %s", e.Time) - } - if e.Height < 1 { - return fmt.Errorf("invalid equivocation height: %d", e.Height) - } - if e.Power < 1 { - return fmt.Errorf("invalid equivocation validator power: %d", e.Power) - } - if e.ConsensusAddress.Empty() { - return fmt.Errorf("invalid equivocation validator consensus address: %s", e.ConsensusAddress) - } - - return nil -} - -// GetConsensusAddress returns the validator's consensus address at time of the -// Equivocation infraction. -func (e Equivocation) GetConsensusAddress() sdk.ConsAddress { - return e.ConsensusAddress -} - -// GetHeight returns the height at time of the Equivocation infraction. -func (e Equivocation) GetHeight() int64 { - return e.Height -} - -// GetTime returns the time at time of the Equivocation infraction. -func (e Equivocation) GetTime() time.Time { - return e.Time -} - -// GetValidatorPower returns the validator's power at time of the Equivocation -// infraction. -func (e Equivocation) GetValidatorPower() int64 { - return e.Power -} - -// GetTotalPower is a no-op for the Equivocation type. -func (e Equivocation) GetTotalPower() int64 { return 0 } - -// ConvertDuplicateVoteEvidence converts a Tendermint concrete Evidence type to -// SDK Evidence using Equivocation as the concrete type. -func ConvertDuplicateVoteEvidence(dupVote abci.Evidence) exported.Evidence { - return Equivocation{ - Height: dupVote.Height, - Power: dupVote.Validator.Power, - ConsensusAddress: sdk.ConsAddress(dupVote.Validator.Address), - Time: dupVote.Time, - } -} diff --git a/x/evidence/internal/types/evidence_test.go b/x/evidence/internal/types/evidence_test.go deleted file mode 100644 index b1e51c6cc357..000000000000 --- a/x/evidence/internal/types/evidence_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package types_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -func TestEquivocation_Valid(t *testing.T) { - n, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - e := types.Equivocation{ - Height: 100, - Time: n, - Power: 1000000, - ConsensusAddress: sdk.ConsAddress("foo"), - } - - require.Equal(t, e.GetTotalPower(), int64(0)) - require.Equal(t, e.GetValidatorPower(), e.Power) - require.Equal(t, e.GetTime(), e.Time) - require.Equal(t, e.GetConsensusAddress(), e.ConsensusAddress) - require.Equal(t, e.GetHeight(), e.Height) - require.Equal(t, e.Type(), types.TypeEquivocation) - require.Equal(t, e.Route(), types.RouteEquivocation) - require.Equal(t, e.Hash().String(), "808DA679674C9C0599965D02EBC5D4DCFD5E700D03035BBCD2DECCBBF44386F7") - require.Equal(t, e.String(), "height: 100\ntime: 2006-01-02T15:04:05Z\npower: 1000000\nconsensus_address: fetchvalcons1vehk757r7a9\n") - require.NoError(t, e.ValidateBasic()) -} - -func TestEquivocationValidateBasic(t *testing.T) { - var zeroTime time.Time - - n, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - testCases := []struct { - name string - e types.Equivocation - expectErr bool - }{ - {"valid", types.Equivocation{100, n, 1000000, sdk.ConsAddress("foo")}, false}, - {"invalid time", types.Equivocation{100, zeroTime, 1000000, sdk.ConsAddress("foo")}, true}, - {"invalid height", types.Equivocation{0, n, 1000000, sdk.ConsAddress("foo")}, true}, - {"invalid power", types.Equivocation{100, n, 0, sdk.ConsAddress("foo")}, true}, - {"invalid address", types.Equivocation{100, n, 1000000, nil}, true}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.expectErr, tc.e.ValidateBasic() != nil) - }) - } -} diff --git a/x/evidence/internal/types/expected_keepers.go b/x/evidence/internal/types/expected_keepers.go deleted file mode 100644 index fc4b12247639..000000000000 --- a/x/evidence/internal/types/expected_keepers.go +++ /dev/null @@ -1,31 +0,0 @@ -package types - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" - - "github.com/tendermint/tendermint/crypto" -) - -type ( - // StakingKeeper defines the staking module interface contract needed by the - // evidence module. - StakingKeeper interface { - ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI - } - - // SlashingKeeper defines the slashing module interface contract needed by the - // evidence module. - SlashingKeeper interface { - GetPubkey(sdk.Context, crypto.Address) (crypto.PubKey, error) - IsTombstoned(sdk.Context, sdk.ConsAddress) bool - HasValidatorSigningInfo(sdk.Context, sdk.ConsAddress) bool - Tombstone(sdk.Context, sdk.ConsAddress) - Slash(sdk.Context, sdk.ConsAddress, sdk.Dec, int64, int64) - SlashFractionDoubleSign(sdk.Context) sdk.Dec - Jail(sdk.Context, sdk.ConsAddress) - JailUntil(sdk.Context, sdk.ConsAddress, time.Time) - } -) diff --git a/x/evidence/internal/types/genesis.go b/x/evidence/internal/types/genesis.go deleted file mode 100644 index 83c5a0f10a58..000000000000 --- a/x/evidence/internal/types/genesis.go +++ /dev/null @@ -1,48 +0,0 @@ -package types - -import ( - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/x/evidence/exported" -) - -// DONTCOVER - -// GenesisState defines the evidence module's genesis state. -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - Evidence []exported.Evidence `json:"evidence" yaml:"evidence"` -} - -func NewGenesisState(p Params, e []exported.Evidence) GenesisState { - return GenesisState{ - Params: p, - Evidence: e, - } -} - -// DefaultGenesisState returns the evidence module's default genesis state. -func DefaultGenesisState() GenesisState { - return GenesisState{ - Params: DefaultParams(), - Evidence: []exported.Evidence{}, - } -} - -// Validate performs basic gensis state validation returning an error upon any -// failure. -func (gs GenesisState) Validate() error { - for _, e := range gs.Evidence { - if err := e.ValidateBasic(); err != nil { - return err - } - } - - maxEvidence := gs.Params.MaxEvidenceAge - if maxEvidence < 1*time.Minute { - return fmt.Errorf("max evidence age must be at least 1 minute, is %s", maxEvidence.String()) - } - - return nil -} diff --git a/x/evidence/internal/types/genesis_test.go b/x/evidence/internal/types/genesis_test.go deleted file mode 100644 index f7ef8027b25c..000000000000 --- a/x/evidence/internal/types/genesis_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -func TestDefaultGenesisState(t *testing.T) { - gs := types.DefaultGenesisState() - require.NotNil(t, gs.Evidence) - require.Len(t, gs.Evidence, 0) -} - -func TestGenesisStateValidate_Valid(t *testing.T) { - pk := ed25519.GenPrivKey() - - evidence := make([]exported.Evidence, 100) - for i := 0; i < 100; i++ { - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - sig, err := pk.Sign(sv.SignBytes("test-chain")) - require.NoError(t, err) - sv.Signature = sig - - evidence[i] = types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - } - - gs := types.NewGenesisState(types.DefaultParams(), evidence) - require.NoError(t, gs.Validate()) -} - -func TestGenesisStateValidate_Invalid(t *testing.T) { - pk := ed25519.GenPrivKey() - - evidence := make([]exported.Evidence, 100) - for i := 0; i < 100; i++ { - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - sig, err := pk.Sign(sv.SignBytes("test-chain")) - require.NoError(t, err) - sv.Signature = sig - - evidence[i] = types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: types.TestVote{Height: 10, Round: 1}, - } - } - - gs := types.NewGenesisState(types.DefaultParams(), evidence) - require.Error(t, gs.Validate()) -} diff --git a/x/evidence/internal/types/msgs.go b/x/evidence/internal/types/msgs.go deleted file mode 100644 index 3fbb50844f57..000000000000 --- a/x/evidence/internal/types/msgs.go +++ /dev/null @@ -1,59 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" -) - -// Message types for the evidence module -const ( - TypeMsgSubmitEvidence = "submit_evidence" -) - -var ( - _ sdk.Msg = MsgSubmitEvidence{} -) - -// MsgSubmitEvidence defines an sdk.Msg type that supports submitting arbitrary -// Evidence. -type MsgSubmitEvidence struct { - Evidence exported.Evidence `json:"evidence" yaml:"evidence"` - Submitter sdk.AccAddress `json:"submitter" yaml:"submitter"` -} - -func NewMsgSubmitEvidence(e exported.Evidence, s sdk.AccAddress) MsgSubmitEvidence { - return MsgSubmitEvidence{Evidence: e, Submitter: s} -} - -// Route returns the MsgSubmitEvidence's route. -func (m MsgSubmitEvidence) Route() string { return RouterKey } - -// Type returns the MsgSubmitEvidence's type. -func (m MsgSubmitEvidence) Type() string { return TypeMsgSubmitEvidence } - -// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitEvidence. -func (m MsgSubmitEvidence) ValidateBasic() error { - if m.Evidence == nil { - return sdkerrors.Wrap(ErrInvalidEvidence, "missing evidence") - } - if err := m.Evidence.ValidateBasic(); err != nil { - return err - } - if m.Submitter.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Submitter.String()) - } - - return nil -} - -// GetSignBytes returns the raw bytes a signer is expected to sign when submitting -// a MsgSubmitEvidence message. -func (m MsgSubmitEvidence) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(m)) -} - -// GetSigners returns the single expected signer for a MsgSubmitEvidence. -func (m MsgSubmitEvidence) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{m.Submitter} -} diff --git a/x/evidence/internal/types/msgs_test.go b/x/evidence/internal/types/msgs_test.go deleted file mode 100644 index 5a50ba6e7b2a..000000000000 --- a/x/evidence/internal/types/msgs_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package types_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" -) - -func TestMsgSubmitEvidence(t *testing.T) { - pk := ed25519.GenPrivKey() - sv := types.TestVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: 11, - Round: 0, - } - sig, err := pk.Sign(sv.SignBytes("test-chain")) - require.NoError(t, err) - sv.Signature = sig - - submitter := sdk.AccAddress("test") - testCases := []struct { - evidence exported.Evidence - submitter sdk.AccAddress - expectErr bool - }{ - {nil, submitter, true}, - { - types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - }, - submitter, - false, - }, - { - types.TestEquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: types.TestVote{Height: 10, Round: 1}, - }, - submitter, - true, - }, - } - - for i, tc := range testCases { - msg := types.NewMsgSubmitEvidence(tc.evidence, tc.submitter) - require.Equal(t, msg.Route(), types.RouterKey, "unexpected result for tc #%d", i) - require.Equal(t, msg.Type(), types.TypeMsgSubmitEvidence, "unexpected result for tc #%d", i) - require.Equal(t, tc.expectErr, msg.ValidateBasic() != nil, "unexpected result for tc #%d", i) - - if !tc.expectErr { - require.Equal(t, msg.GetSigners(), []sdk.AccAddress{tc.submitter}, "unexpected result for tc #%d", i) - } - } -} diff --git a/x/evidence/internal/types/params.go b/x/evidence/internal/types/params.go deleted file mode 100644 index 5c0dbe638fa3..000000000000 --- a/x/evidence/internal/types/params.go +++ /dev/null @@ -1,69 +0,0 @@ -package types - -import ( - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/x/params" - - "gopkg.in/yaml.v2" -) - -// DONTCOVER - -// Default parameter values -const ( - DefaultParamspace = ModuleName - DefaultMaxEvidenceAge = 60 * 2 * time.Second -) - -// Parameter store keys -var ( - KeyMaxEvidenceAge = []byte("MaxEvidenceAge") - - // The Double Sign Jail period ends at Max Time supported by Amino - // (Dec 31, 9999 - 23:59:59 GMT). - DoubleSignJailEndTime = time.Unix(253402300799, 0) -) - -// Params defines the total set of parameters for the evidence module -type Params struct { - MaxEvidenceAge time.Duration `json:"max_evidence_age" yaml:"max_evidence_age"` -} - -// ParamKeyTable returns the parameter key table. -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable().RegisterParamSet(&Params{}) -} - -func (p Params) String() string { - out, _ := yaml.Marshal(p) - return string(out) -} - -// ParamSetPairs returns the parameter set pairs. -func (p *Params) ParamSetPairs() params.ParamSetPairs { - return params.ParamSetPairs{ - params.NewParamSetPair(KeyMaxEvidenceAge, &p.MaxEvidenceAge, validateMaxEvidenceAge), - } -} - -// DefaultParams returns the default parameters for the evidence module. -func DefaultParams() Params { - return Params{ - MaxEvidenceAge: DefaultMaxEvidenceAge, - } -} - -func validateMaxEvidenceAge(i interface{}) error { - v, ok := i.(time.Duration) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v <= 0 { - return fmt.Errorf("max evidence age must be positive: %s", v) - } - - return nil -} diff --git a/x/evidence/internal/types/querier.go b/x/evidence/internal/types/querier.go deleted file mode 100644 index 130d0699de45..000000000000 --- a/x/evidence/internal/types/querier.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -// Querier routes for the evidence module -const ( - QueryParameters = "parameters" - QueryEvidence = "evidence" - QueryAllEvidence = "all_evidence" -) - -// QueryEvidenceParams defines the parameters necessary for querying Evidence. -type QueryEvidenceParams struct { - EvidenceHash string `json:"evidence_hash" yaml:"evidence_hash"` -} - -func NewQueryEvidenceParams(hash string) QueryEvidenceParams { - return QueryEvidenceParams{EvidenceHash: hash} -} - -// QueryAllEvidenceParams defines the parameters necessary for querying for all Evidence. -type QueryAllEvidenceParams struct { - Page int `json:"page" yaml:"page"` - Limit int `json:"limit" yaml:"limit"` -} - -func NewQueryAllEvidenceParams(page, limit int) QueryAllEvidenceParams { - return QueryAllEvidenceParams{Page: page, Limit: limit} -} diff --git a/x/evidence/internal/types/router_test.go b/x/evidence/internal/types/router_test.go deleted file mode 100644 index b8cb42d2d488..000000000000 --- a/x/evidence/internal/types/router_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package types_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" -) - -func testHandler(sdk.Context, exported.Evidence) error { return nil } - -func TestRouterSeal(t *testing.T) { - r := types.NewRouter() - r.Seal() - require.Panics(t, func() { r.AddRoute("test", nil) }) - require.Panics(t, func() { r.Seal() }) -} - -func TestRouter(t *testing.T) { - r := types.NewRouter() - r.AddRoute("test", testHandler) - require.True(t, r.HasRoute("test")) - require.Panics(t, func() { r.AddRoute("test", testHandler) }) - require.Panics(t, func() { r.AddRoute(" ", testHandler) }) -} diff --git a/x/evidence/internal/types/test_util.go b/x/evidence/internal/types/test_util.go deleted file mode 100644 index 24500cba993c..000000000000 --- a/x/evidence/internal/types/test_util.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Common testing types and utility functions and methods to be used in unit and -integration testing of the evidence module. -*/ -// DONTCOVER -package types - -import ( - "bytes" - "errors" - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - - "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/tmhash" - tmbytes "github.com/tendermint/tendermint/libs/bytes" -) - -var ( - _ exported.Evidence = (*TestEquivocationEvidence)(nil) - - TestingCdc = codec.New() -) - -const ( - TestEvidenceRouteEquivocation = "TestEquivocationEvidence" - TestEvidenceTypeEquivocation = "equivocation" -) - -type ( - TestVote struct { - Height int64 - Round int64 - Timestamp time.Time - ValidatorAddress tmbytes.HexBytes - Signature []byte - } - - TestCanonicalVote struct { - Height int64 - Round int64 - Timestamp time.Time - ChainID string - } - - TestEquivocationEvidence struct { - Power int64 - TotalPower int64 - PubKey crypto.PubKey - VoteA TestVote - VoteB TestVote - } -) - -func init() { - RegisterCodec(TestingCdc) - codec.RegisterCrypto(TestingCdc) - TestingCdc.RegisterConcrete(TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) -} - -func (e TestEquivocationEvidence) Route() string { return TestEvidenceRouteEquivocation } -func (e TestEquivocationEvidence) Type() string { return TestEvidenceTypeEquivocation } -func (e TestEquivocationEvidence) GetHeight() int64 { return e.VoteA.Height } -func (e TestEquivocationEvidence) GetValidatorPower() int64 { return e.Power } -func (e TestEquivocationEvidence) GetTotalPower() int64 { return e.TotalPower } - -func (e TestEquivocationEvidence) String() string { - bz, _ := yaml.Marshal(e) - return string(bz) -} - -func (e TestEquivocationEvidence) GetConsensusAddress() sdk.ConsAddress { - return sdk.ConsAddress(e.PubKey.Address()) -} - -func (e TestEquivocationEvidence) ValidateBasic() error { - if e.VoteA.Height != e.VoteB.Height || - e.VoteA.Round != e.VoteB.Round { - return fmt.Errorf("H/R/S does not match (got %v and %v)", e.VoteA, e.VoteB) - } - - if !bytes.Equal(e.VoteA.ValidatorAddress, e.VoteB.ValidatorAddress) { - return fmt.Errorf( - "validator addresses do not match (got %X and %X)", - e.VoteA.ValidatorAddress, - e.VoteB.ValidatorAddress, - ) - } - - return nil -} - -func (e TestEquivocationEvidence) Hash() tmbytes.HexBytes { - return tmhash.Sum(TestingCdc.MustMarshalBinaryBare(e)) -} - -func (v TestVote) SignBytes(chainID string) []byte { - scv := TestCanonicalVote{ - Height: v.Height, - Round: v.Round, - Timestamp: v.Timestamp, - ChainID: chainID, - } - bz, _ := TestingCdc.MarshalBinaryLengthPrefixed(scv) - return bz -} - -func TestEquivocationHandler(k interface{}) Handler { - return func(ctx sdk.Context, e exported.Evidence) error { - if err := e.ValidateBasic(); err != nil { - return err - } - - ee, ok := e.(TestEquivocationEvidence) - if !ok { - return fmt.Errorf("unexpected evidence type: %T", e) - } - if !ee.PubKey.VerifyBytes(ee.VoteA.SignBytes(ctx.ChainID()), ee.VoteA.Signature) { - return errors.New("failed to verify vote A signature") - } - if !ee.PubKey.VerifyBytes(ee.VoteB.SignBytes(ctx.ChainID()), ee.VoteB.Signature) { - return errors.New("failed to verify vote B signature") - } - - // TODO: Slashing! - - return nil - } -} diff --git a/x/evidence/keeper/grpc_query.go b/x/evidence/keeper/grpc_query.go new file mode 100644 index 000000000000..f084c16717a0 --- /dev/null +++ b/x/evidence/keeper/grpc_query.go @@ -0,0 +1,88 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/types/query" + + proto "github.com/gogo/protobuf/proto" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +var _ types.QueryServer = Keeper{} + +// Evidence implements the Query/Evidence gRPC method +func (k Keeper) Evidence(c context.Context, req *types.QueryEvidenceRequest) (*types.QueryEvidenceResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.EvidenceHash == nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid hash") + } + + ctx := sdk.UnwrapSDKContext(c) + + evidence, _ := k.GetEvidence(ctx, req.EvidenceHash) + if evidence == nil { + return nil, status.Errorf(codes.NotFound, "evidence %s not found", req.EvidenceHash) + } + + msg, ok := evidence.(proto.Message) + if !ok { + return nil, status.Errorf(codes.Internal, "can't protomarshal %T", msg) + } + + evidenceAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &types.QueryEvidenceResponse{Evidence: evidenceAny}, nil +} + +// AllEvidence implements the Query/AllEvidence gRPC method +func (k Keeper) AllEvidence(c context.Context, req *types.QueryAllEvidenceRequest) (*types.QueryAllEvidenceResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(c) + + k.GetAllEvidence(ctx) + + var evidence []*codectypes.Any + store := ctx.KVStore(k.storeKey) + evidenceStore := prefix.NewStore(store, types.KeyPrefixEvidence) + + pageRes, err := query.Paginate(evidenceStore, req.Pagination, func(key []byte, value []byte) error { + result, err := k.UnmarshalEvidence(value) + if err != nil { + return err + } + + msg, ok := result.(proto.Message) + if !ok { + return status.Errorf(codes.Internal, "can't protomarshal %T", msg) + } + + evidenceAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return err + } + evidence = append(evidence, evidenceAny) + return nil + }) + + if err != nil { + return &types.QueryAllEvidenceResponse{}, err + } + + return &types.QueryAllEvidenceResponse{Evidence: evidence, Pagination: pageRes}, nil +} diff --git a/x/evidence/keeper/grpc_query_test.go b/x/evidence/keeper/grpc_query_test.go new file mode 100644 index 000000000000..df760c8edfe0 --- /dev/null +++ b/x/evidence/keeper/grpc_query_test.go @@ -0,0 +1,143 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" +) + +func (suite *KeeperTestSuite) TestQueryEvidence() { + var ( + req *types.QueryEvidenceRequest + evidence []exported.Evidence + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func(res *types.QueryEvidenceResponse) + }{ + { + "empty request", + func() { + req = &types.QueryEvidenceRequest{} + }, + false, + func(res *types.QueryEvidenceResponse) {}, + }, + { + "invalid request with empty evidence hash", + func() { + req = &types.QueryEvidenceRequest{EvidenceHash: tmbytes.HexBytes{}} + }, + false, + func(res *types.QueryEvidenceResponse) {}, + }, + { + "success", + func() { + numEvidence := 100 + evidence = suite.populateEvidence(suite.ctx, numEvidence) + req = types.NewQueryEvidenceRequest(evidence[0].Hash()) + }, + true, + func(res *types.QueryEvidenceResponse) { + var evi exported.Evidence + err := suite.app.InterfaceRegistry().UnpackAny(res.Evidence, &evi) + suite.Require().NoError(err) + suite.Require().NotNil(evi) + suite.Require().Equal(evi, evidence[0]) + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.ctx) + + res, err := suite.queryClient.Evidence(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + + tc.posttests(res) + }) + } +} + +func (suite *KeeperTestSuite) TestQueryAllEvidence() { + var ( + req *types.QueryAllEvidenceRequest + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func(res *types.QueryAllEvidenceResponse) + }{ + { + "success without evidence", + func() { + req = &types.QueryAllEvidenceRequest{} + }, + true, + func(res *types.QueryAllEvidenceResponse) { + suite.Require().Empty(res.Evidence) + }, + }, + { + "success", + func() { + numEvidence := 100 + _ = suite.populateEvidence(suite.ctx, numEvidence) + pageReq := &query.PageRequest{ + Key: nil, + Limit: 50, + CountTotal: false, + } + req = types.NewQueryAllEvidenceRequest(pageReq) + }, + true, + func(res *types.QueryAllEvidenceResponse) { + suite.Equal(len(res.Evidence), 50) + suite.NotNil(res.Pagination.NextKey) + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.ctx) + + res, err := suite.queryClient.AllEvidence(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + + tc.posttests(res) + }) + } +} diff --git a/x/evidence/keeper/infraction.go b/x/evidence/keeper/infraction.go new file mode 100644 index 000000000000..427c8de429ed --- /dev/null +++ b/x/evidence/keeper/infraction.go @@ -0,0 +1,122 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// HandleEquivocationEvidence implements an equivocation evidence handler. Assuming the +// evidence is valid, the validator committing the misbehavior will be slashed, +// jailed and tombstoned. Once tombstoned, the validator will not be able to +// recover. Note, the evidence contains the block time and height at the time of +// the equivocation. +// +// The evidence is considered invalid if: +// - the evidence is too old +// - the validator is unbonded or does not exist +// - the signing info does not exist (will panic) +// - is already tombstoned +// +// TODO: Some of the invalid constraints listed above may need to be reconsidered +// in the case of a lunatic attack. +func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) { + logger := k.Logger(ctx) + consAddr := evidence.GetConsensusAddress() + + if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { + // Ignore evidence that cannot be handled. + // + // NOTE: We used to panic with: + // `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`, + // but this couples the expectations of the app to both Tendermint and + // the simulator. Both are expected to provide the full range of + // allowable but none of the disallowed evidence types. Instead of + // getting this coordination right, it is easier to relax the + // constraints and ignore evidence that cannot be handled. + return + } + + // calculate the age of the evidence + infractionHeight := evidence.GetHeight() + infractionTime := evidence.GetTime() + ageDuration := ctx.BlockHeader().Time.Sub(infractionTime) + ageBlocks := ctx.BlockHeader().Height - infractionHeight + + // Reject evidence if the double-sign is too old. Evidence is considered stale + // if the difference in time and number of blocks is greater than the allowed + // parameters defined. + cp := ctx.ConsensusParams() + if cp != nil && cp.Evidence != nil { + if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks { + logger.Info( + "ignored equivocation; evidence too old", + "validator", consAddr, + "infraction_height", infractionHeight, + "max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks, + "infraction_time", infractionTime, + "max_age_duration", cp.Evidence.MaxAgeDuration, + ) + return + } + } + + validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) + if validator == nil || validator.IsUnbonded() { + // Defensive: Simulation doesn't take unbonding periods into account, and + // Tendermint might break this assumption at some point. + return + } + + if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { + panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr)) + } + + // ignore if the validator is already tombstoned + if k.slashingKeeper.IsTombstoned(ctx, consAddr) { + logger.Info( + "ignored equivocation; validator already tombstoned", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + return + } + + logger.Info( + "confirmed equivocation", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + + // We need to retrieve the stake distribution which signed the block, so we + // subtract ValidatorUpdateDelay from the evidence height. + // Note, that this *can* result in a negative "distributionHeight", up to + // -ValidatorUpdateDelay, i.e. at the end of the + // pre-genesis block (none) = at the beginning of the genesis block. + // That's fine since this is just used to filter unbonding delegations & redelegations. + distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay + + // Slash validator. The `power` is the int64 power of the validator as provided + // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via + // ABCI, and now received as evidence. The fraction is passed in to separately + // to slash unbonding and rebonding delegations. + k.slashingKeeper.Slash( + ctx, + consAddr, + k.slashingKeeper.SlashFractionDoubleSign(ctx), + evidence.GetValidatorPower(), distributionHeight, + ) + + // Jail the validator if not already jailed. This will begin unbonding the + // validator if not already unbonding (tombstoned). + if !validator.IsJailed() { + k.slashingKeeper.Jail(ctx, consAddr) + } + + k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) + k.slashingKeeper.Tombstone(ctx, consAddr) +} diff --git a/x/evidence/keeper/infraction_test.go b/x/evidence/keeper/infraction_test.go new file mode 100644 index 000000000000..0f1adee14f46 --- /dev/null +++ b/x/evidence/keeper/infraction_test.go @@ -0,0 +1,109 @@ +package keeper_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" +) + +func (suite *KeeperTestSuite) TestHandleDoubleSign() { + ctx := suite.ctx.WithIsCheckTx(false).WithBlockHeight(1) + suite.populateValidators(ctx) + + power := int64(100) + stakingParams := suite.app.StakingKeeper.GetParams(ctx) + operatorAddr, val := valAddresses[0], pubkeys[0] + tstaking := teststaking.NewHelper(suite.T(), ctx, suite.app.StakingKeeper) + + selfDelegation := tstaking.CreateValidatorWithValPower(operatorAddr, val, power, true) + + // execute end-blocker and verify validator attributes + staking.EndBlocker(ctx, suite.app.StakingKeeper) + suite.Equal( + suite.app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(operatorAddr)).String(), + sdk.NewCoins(sdk.NewCoin(stakingParams.BondDenom, initAmt.Sub(selfDelegation))).String(), + ) + suite.Equal(selfDelegation, suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetBondedTokens()) + + // handle a signature to set signing info + suite.app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), selfDelegation.Int64(), true) + + // double sign less than max age + oldTokens := suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens() + evidence := &types.Equivocation{ + Height: 0, + Time: time.Unix(0, 0), + Power: power, + ConsensusAddress: sdk.ConsAddress(val.Address()).String(), + } + suite.app.EvidenceKeeper.HandleEquivocationEvidence(ctx, evidence) + + // should be jailed and tombstoned + suite.True(suite.app.StakingKeeper.Validator(ctx, operatorAddr).IsJailed()) + suite.True(suite.app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(val.Address()))) + + // tokens should be decreased + newTokens := suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens() + suite.True(newTokens.LT(oldTokens)) + + // submit duplicate evidence + suite.app.EvidenceKeeper.HandleEquivocationEvidence(ctx, evidence) + + // tokens should be the same (capped slash) + suite.True(suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetTokens().Equal(newTokens)) + + // jump to past the unbonding period + ctx = ctx.WithBlockTime(time.Unix(1, 0).Add(stakingParams.UnbondingTime)) + + // require we cannot unjail + suite.Error(suite.app.SlashingKeeper.Unjail(ctx, operatorAddr)) + + // require we be able to unbond now + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + del, _ := suite.app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr) + validator, _ := suite.app.StakingKeeper.GetValidator(ctx, operatorAddr) + totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt() + tstaking.Ctx = ctx + tstaking.Denom = stakingParams.BondDenom + tstaking.Undelegate(sdk.AccAddress(operatorAddr), operatorAddr, totalBond, true) +} + +func (suite *KeeperTestSuite) TestHandleDoubleSign_TooOld() { + ctx := suite.ctx.WithIsCheckTx(false).WithBlockHeight(1).WithBlockTime(time.Now()) + suite.populateValidators(ctx) + + power := int64(100) + stakingParams := suite.app.StakingKeeper.GetParams(ctx) + operatorAddr, val := valAddresses[0], pubkeys[0] + tstaking := teststaking.NewHelper(suite.T(), ctx, suite.app.StakingKeeper) + + amt := tstaking.CreateValidatorWithValPower(operatorAddr, val, power, true) + + // execute end-blocker and verify validator attributes + staking.EndBlocker(ctx, suite.app.StakingKeeper) + suite.Equal( + suite.app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(operatorAddr)), + sdk.NewCoins(sdk.NewCoin(stakingParams.BondDenom, initAmt.Sub(amt))), + ) + suite.Equal(amt, suite.app.StakingKeeper.Validator(ctx, operatorAddr).GetBondedTokens()) + + evidence := &types.Equivocation{ + Height: 0, + Time: ctx.BlockTime(), + Power: power, + ConsensusAddress: sdk.ConsAddress(val.Address()).String(), + } + + cp := suite.app.BaseApp.GetConsensusParams(ctx) + + ctx = ctx.WithConsensusParams(cp) + ctx = ctx.WithBlockTime(ctx.BlockTime().Add(cp.Evidence.MaxAgeDuration + 1)) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + cp.Evidence.MaxAgeNumBlocks + 1) + suite.app.EvidenceKeeper.HandleEquivocationEvidence(ctx, evidence) + + suite.False(suite.app.StakingKeeper.Validator(ctx, operatorAddr).IsJailed()) + suite.False(suite.app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(val.Address()))) +} diff --git a/x/evidence/keeper/init_test.go b/x/evidence/keeper/init_test.go new file mode 100644 index 000000000000..d5854aab467e --- /dev/null +++ b/x/evidence/keeper/init_test.go @@ -0,0 +1,11 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/evidence/keeper/keeper.go b/x/evidence/keeper/keeper.go new file mode 100644 index 000000000000..aa660bcff13b --- /dev/null +++ b/x/evidence/keeper/keeper.go @@ -0,0 +1,180 @@ +package keeper + +import ( + "fmt" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// Keeper defines the evidence module's keeper. The keeper is responsible for +// managing persistence, state transitions and query handling for the evidence +// module. +type Keeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + router types.Router + stakingKeeper types.StakingKeeper + slashingKeeper types.SlashingKeeper +} + +func NewKeeper( + cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, stakingKeeper types.StakingKeeper, + slashingKeeper types.SlashingKeeper, +) *Keeper { + + return &Keeper{ + cdc: cdc, + storeKey: storeKey, + stakingKeeper: stakingKeeper, + slashingKeeper: slashingKeeper, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// SetRouter sets the Evidence Handler router for the x/evidence module. Note, +// we allow the ability to set the router after the Keeper is constructed as a +// given Handler may need access the Keeper before being constructed. The router +// may only be set once and will be sealed if it's not already sealed. +func (k *Keeper) SetRouter(rtr types.Router) { + // It is vital to seal the Evidence Handler router as to not allow further + // handlers to be registered after the keeper is created since this + // could create invalid or non-deterministic behavior. + if !rtr.Sealed() { + rtr.Seal() + } + if k.router != nil { + panic(fmt.Sprintf("attempting to reset router on x/%s", types.ModuleName)) + } + + k.router = rtr +} + +// GetEvidenceHandler returns a registered Handler for a given Evidence type. If +// no handler exists, an error is returned. +func (k Keeper) GetEvidenceHandler(evidenceRoute string) (types.Handler, error) { + if !k.router.HasRoute(evidenceRoute) { + return nil, sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidenceRoute) + } + + return k.router.GetRoute(evidenceRoute), nil +} + +// SubmitEvidence attempts to match evidence against the keepers router and execute +// the corresponding registered Evidence Handler. An error is returned if no +// registered Handler exists or if the Handler fails. Otherwise, the evidence is +// persisted. +func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence exported.Evidence) error { + if _, ok := k.GetEvidence(ctx, evidence.Hash()); ok { + return sdkerrors.Wrap(types.ErrEvidenceExists, evidence.Hash().String()) + } + if !k.router.HasRoute(evidence.Route()) { + return sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route()) + } + + handler := k.router.GetRoute(evidence.Route()) + if err := handler(ctx, evidence); err != nil { + return sdkerrors.Wrap(types.ErrInvalidEvidence, err.Error()) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSubmitEvidence, + sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), + ), + ) + + k.SetEvidence(ctx, evidence) + return nil +} + +// SetEvidence sets Evidence by hash in the module's KVStore. +func (k Keeper) SetEvidence(ctx sdk.Context, evidence exported.Evidence) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) + store.Set(evidence.Hash(), k.MustMarshalEvidence(evidence)) +} + +// GetEvidence retrieves Evidence by hash if it exists. If no Evidence exists for +// the given hash, (nil, false) is returned. +func (k Keeper) GetEvidence(ctx sdk.Context, hash tmbytes.HexBytes) (exported.Evidence, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) + + bz := store.Get(hash) + if len(bz) == 0 { + return nil, false + } + + return k.MustUnmarshalEvidence(bz), true +} + +// IterateEvidence provides an interator over all stored Evidence objects. For +// each Evidence object, cb will be called. If the cb returns true, the iterator +// will close and stop. +func (k Keeper) IterateEvidence(ctx sdk.Context, cb func(exported.Evidence) bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) + iterator := sdk.KVStorePrefixIterator(store, nil) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + evidence := k.MustUnmarshalEvidence(iterator.Value()) + + if cb(evidence) { + break + } + } +} + +// GetAllEvidence returns all stored Evidence objects. +func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []exported.Evidence) { + k.IterateEvidence(ctx, func(e exported.Evidence) bool { + evidence = append(evidence, e) + return false + }) + + return evidence +} + +// MustUnmarshalEvidence attempts to decode and return an Evidence object from +// raw encoded bytes. It panics on error. +func (k Keeper) MustUnmarshalEvidence(bz []byte) exported.Evidence { + evidence, err := k.UnmarshalEvidence(bz) + if err != nil { + panic(fmt.Errorf("failed to decode evidence: %w", err)) + } + + return evidence +} + +// MustMarshalEvidence attempts to encode an Evidence object and returns the +// raw encoded bytes. It panics on error. +func (k Keeper) MustMarshalEvidence(evidence exported.Evidence) []byte { + bz, err := k.MarshalEvidence(evidence) + if err != nil { + panic(fmt.Errorf("failed to encode evidence: %w", err)) + } + + return bz +} + +// MarshalEvidence protobuf serializes an Evidence interface +func (k Keeper) MarshalEvidence(evidenceI exported.Evidence) ([]byte, error) { + return k.cdc.MarshalInterface(evidenceI) +} + +// UnmarshalEvidence returns an Evidence interface from raw encoded evidence +// bytes of a Proto-based Evidence type +func (k Keeper) UnmarshalEvidence(bz []byte) (exported.Evidence, error) { + var evi exported.Evidence + return evi, k.cdc.UnmarshalInterface(bz, &evi) +} diff --git a/x/evidence/keeper/keeper_test.go b/x/evidence/keeper/keeper_test.go new file mode 100644 index 000000000000..9165730466b7 --- /dev/null +++ b/x/evidence/keeper/keeper_test.go @@ -0,0 +1,212 @@ +package keeper_test + +import ( + "encoding/hex" + "fmt" + "time" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "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" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +var ( + pubkeys = []cryptotypes.PubKey{ + newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"), + newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"), + newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"), + } + + valAddresses = []sdk.ValAddress{ + sdk.ValAddress(pubkeys[0].Address()), + sdk.ValAddress(pubkeys[1].Address()), + sdk.ValAddress(pubkeys[2].Address()), + } + + initAmt = sdk.TokensFromConsensusPower(200) + initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) +) + +func newPubKey(pk string) (res cryptotypes.PubKey) { + pkBytes, err := hex.DecodeString(pk) + if err != nil { + panic(err) + } + + pubkey := &ed25519.PubKey{Key: pkBytes} + + return pubkey +} + +func testEquivocationHandler(_ interface{}) types.Handler { + return func(ctx sdk.Context, e exported.Evidence) error { + if err := e.ValidateBasic(); err != nil { + return err + } + + ee, ok := e.(*types.Equivocation) + if !ok { + return fmt.Errorf("unexpected evidence type: %T", e) + } + if ee.Height%2 == 0 { + return fmt.Errorf("unexpected even evidence height: %d", ee.Height) + } + + return nil + } +} + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + querier sdk.Querier + app *simapp.SimApp + + queryClient types.QueryClient + stakingHdl sdk.Handler +} + +func (suite *KeeperTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + + // recreate keeper in order to use custom testing types + evidenceKeeper := keeper.NewKeeper( + app.AppCodec(), app.GetKey(types.StoreKey), app.StakingKeeper, app.SlashingKeeper, + ) + router := types.NewRouter() + router = router.AddRoute(types.RouteEquivocation, testEquivocationHandler(*evidenceKeeper)) + evidenceKeeper.SetRouter(router) + + app.EvidenceKeeper = *evidenceKeeper + + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.querier = keeper.NewQuerier(*evidenceKeeper, app.LegacyAmino()) + suite.app = app + + for i, addr := range valAddresses { + addr := sdk.AccAddress(addr) + app.AccountKeeper.SetAccount(suite.ctx, authtypes.NewBaseAccount(addr, pubkeys[i], uint64(i), 0)) + } + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.EvidenceKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) + suite.stakingHdl = staking.NewHandler(app.StakingKeeper) +} + +func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []exported.Evidence { + evidence := make([]exported.Evidence, numEvidence) + + for i := 0; i < numEvidence; i++ { + pk := ed25519.GenPrivKey() + + evidence[i] = &types.Equivocation{ + Height: 11, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: sdk.ConsAddress(pk.PubKey().Address().Bytes()).String(), + } + + suite.Nil(suite.app.EvidenceKeeper.SubmitEvidence(ctx, evidence[i])) + } + + return evidence +} + +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)) + + for _, addr := range valAddresses { + err := suite.app.BankKeeper.AddCoins(ctx, sdk.AccAddress(addr), initCoins) + suite.NoError(err) + } +} + +func (suite *KeeperTestSuite) TestSubmitValidEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + + e := &types.Equivocation{ + Height: 1, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: sdk.ConsAddress(pk.PubKey().Address().Bytes()).String(), + } + + suite.Nil(suite.app.EvidenceKeeper.SubmitEvidence(ctx, e)) + + res, ok := suite.app.EvidenceKeeper.GetEvidence(ctx, e.Hash()) + suite.True(ok) + suite.Equal(e, res) +} + +func (suite *KeeperTestSuite) TestSubmitValidEvidence_Duplicate() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + + e := &types.Equivocation{ + Height: 1, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: sdk.ConsAddress(pk.PubKey().Address().Bytes()).String(), + } + + suite.Nil(suite.app.EvidenceKeeper.SubmitEvidence(ctx, e)) + suite.Error(suite.app.EvidenceKeeper.SubmitEvidence(ctx, e)) + + res, ok := suite.app.EvidenceKeeper.GetEvidence(ctx, e.Hash()) + suite.True(ok) + suite.Equal(e, res) +} + +func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + e := &types.Equivocation{ + Height: 0, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: sdk.ConsAddress(pk.PubKey().Address().Bytes()).String(), + } + + suite.Error(suite.app.EvidenceKeeper.SubmitEvidence(ctx, e)) + + res, ok := suite.app.EvidenceKeeper.GetEvidence(ctx, e.Hash()) + suite.False(ok) + suite.Nil(res) +} + +func (suite *KeeperTestSuite) TestIterateEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + suite.populateEvidence(ctx, numEvidence) + + evidence := suite.app.EvidenceKeeper.GetAllEvidence(ctx) + suite.Len(evidence, numEvidence) +} + +func (suite *KeeperTestSuite) TestGetEvidenceHandler() { + handler, err := suite.app.EvidenceKeeper.GetEvidenceHandler((&types.Equivocation{}).Route()) + suite.NoError(err) + suite.NotNil(handler) + + handler, err = suite.app.EvidenceKeeper.GetEvidenceHandler("invalidHandler") + suite.Error(err) + suite.Nil(handler) +} diff --git a/x/evidence/keeper/msg_server.go b/x/evidence/keeper/msg_server.go new file mode 100644 index 000000000000..df3cabd4fe67 --- /dev/null +++ b/x/evidence/keeper/msg_server.go @@ -0,0 +1,42 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the bank MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// SubmitEvidence implements the MsgServer.SubmitEvidence method. +func (ms msgServer) SubmitEvidence(goCtx context.Context, msg *types.MsgSubmitEvidence) (*types.MsgSubmitEvidenceResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + evidence := msg.GetEvidence() + if err := ms.Keeper.SubmitEvidence(ctx, evidence); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.GetSubmitter().String()), + ), + ) + + return &types.MsgSubmitEvidenceResponse{ + Hash: evidence.Hash(), + }, nil +} diff --git a/x/evidence/keeper/querier.go b/x/evidence/keeper/querier.go new file mode 100644 index 000000000000..c1ae20d4d9c1 --- /dev/null +++ b/x/evidence/keeper/querier.go @@ -0,0 +1,80 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/client" + "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/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + var ( + res []byte + err error + ) + + switch path[0] { + case types.QueryEvidence: + res, err = queryEvidence(ctx, req, k, legacyQuerierCdc) + + case types.QueryAllEvidence: + res, err = queryAllEvidence(ctx, req, k, legacyQuerierCdc) + + default: + err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + + return res, err + } +} + +func queryEvidence(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryEvidenceRequest + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + evidence, ok := k.GetEvidence(ctx, params.EvidenceHash) + if !ok { + return nil, sdkerrors.Wrap(types.ErrNoEvidenceExists, params.EvidenceHash.String()) + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, evidence) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func queryAllEvidence(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryAllEvidenceParams + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + evidence := k.GetAllEvidence(ctx) + + start, end := client.Paginate(len(evidence), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + evidence = []exported.Evidence{} + } else { + evidence = evidence[start:end] + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, evidence) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} diff --git a/x/evidence/keeper/querier_test.go b/x/evidence/keeper/querier_test.go new file mode 100644 index 000000000000..58a1ff81bb3e --- /dev/null +++ b/x/evidence/keeper/querier_test.go @@ -0,0 +1,92 @@ +package keeper_test + +import ( + "strings" + + "github.com/cosmos/cosmos-sdk/simapp" + + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +const ( + custom = "custom" +) + +func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_Existing() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + _, cdc := simapp.MakeCodecs() + + 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())), + } + + bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e exported.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Equal(evidence[0], e) +} + +func (suite *KeeperTestSuite) TestQuerier_QueryEvidence_NonExisting() { + ctx := suite.ctx.WithIsCheckTx(false) + cdc, _ := simapp.MakeCodecs() + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryEvidenceRequest([]byte("0000000000000000000000000000000000000000000000000000000000000000"))), + } + + bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) + suite.NotNil(err) + suite.Nil(bz) +} + +func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + _, cdc := simapp.MakeCodecs() + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(1, numEvidence)), + } + + bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e []exported.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Len(e, numEvidence) +} + +func (suite *KeeperTestSuite) TestQuerier_QueryAllEvidence_InvalidPagination() { + ctx := suite.ctx.WithIsCheckTx(false) + _, cdc := simapp.MakeCodecs() + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(0, numEvidence)), + } + + bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e []exported.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Len(e, 0) +} diff --git a/x/evidence/legacy/v038/types.go b/x/evidence/legacy/v038/types.go new file mode 100644 index 000000000000..994f718ca5fd --- /dev/null +++ b/x/evidence/legacy/v038/types.go @@ -0,0 +1,113 @@ +package v038 + +import ( + "fmt" + "time" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Default parameter values +const ( + ModuleName = "evidence" + DefaultParamspace = ModuleName + DefaultMaxEvidenceAge = 60 * 2 * time.Second +) + +// Evidence type constants +const ( + RouteEquivocation = "equivocation" + TypeEquivocation = "equivocation" +) + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/evidence module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/evidence and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +// Evidence defines the contract which concrete evidence types of misbehavior +// must implement. +type Evidence interface { + Route() string + Type() string + String() string + Hash() tmbytes.HexBytes + ValidateBasic() error + + // Height at which the infraction occurred + GetHeight() int64 +} + +// Params defines the total set of parameters for the evidence module +type Params struct { + MaxEvidenceAge time.Duration `json:"max_evidence_age" yaml:"max_evidence_age"` +} + +// GenesisState defines the evidence module's genesis state. +type GenesisState struct { + Params Params `json:"params" yaml:"params"` + Evidence []Evidence `json:"evidence" yaml:"evidence"` +} + +// Assert interface implementation. +var _ Evidence = Equivocation{} + +// Equivocation implements the Evidence interface and defines evidence of double +// signing misbehavior. +type Equivocation struct { + Height int64 `json:"height" yaml:"height"` + Time time.Time `json:"time" yaml:"time"` + Power int64 `json:"power" yaml:"power"` + ConsensusAddress sdk.ConsAddress `json:"consensus_address" yaml:"consensus_address"` +} + +// Route returns the Evidence Handler route for an Equivocation type. +func (e Equivocation) Route() string { return RouteEquivocation } + +// Type returns the Evidence Handler type for an Equivocation type. +func (e Equivocation) Type() string { return TypeEquivocation } + +func (e Equivocation) String() string { + bz, _ := yaml.Marshal(e) + return string(bz) +} + +// Hash returns the hash of an Equivocation object. +func (e Equivocation) Hash() tmbytes.HexBytes { + return tmhash.Sum(ModuleCdc.LegacyAmino.MustMarshalBinaryBare(e)) +} + +// ValidateBasic performs basic stateless validation checks on an Equivocation object. +func (e Equivocation) ValidateBasic() error { + if e.Time.Unix() <= 0 { + return fmt.Errorf("invalid equivocation time: %s", e.Time) + } + if e.Height < 1 { + return fmt.Errorf("invalid equivocation height: %d", e.Height) + } + if e.Power < 1 { + return fmt.Errorf("invalid equivocation validator power: %d", e.Power) + } + if e.ConsensusAddress.Empty() { + return fmt.Errorf("invalid equivocation validator consensus address: %s", e.ConsensusAddress) + } + + return nil +} + +// GetHeight returns the height at time of the Equivocation infraction. +func (e Equivocation) GetHeight() int64 { + return e.Height +} diff --git a/x/evidence/legacy/v040/migrate.go b/x/evidence/legacy/v040/migrate.go new file mode 100644 index 000000000000..f848130f345c --- /dev/null +++ b/x/evidence/legacy/v040/migrate.go @@ -0,0 +1,48 @@ +package v040 + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + v038evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v038" + v040evidence "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func migrateEvidence(oldEvidence v038evidence.Evidence) *codectypes.Any { + switch oldEvidence := oldEvidence.(type) { + case v038evidence.Equivocation: + { + newEquivocation := &v040evidence.Equivocation{ + Height: oldEvidence.Height, + Time: oldEvidence.Time, + Power: oldEvidence.Power, + ConsensusAddress: oldEvidence.ConsensusAddress.String(), + } + any, err := codectypes.NewAnyWithValue(newEquivocation) + if err != nil { + panic(err) + } + + return any + } + default: + panic(fmt.Errorf("'%T' is not a valid evidence type", oldEvidence)) + } +} + +// Migrate accepts exported v0.38 x/evidence genesis state and migrates it to +// v0.40 x/evidence genesis state. The migration includes: +// +// - Removing the `Params` field. +// - Converting Equivocations into Anys. +// - Re-encode in v0.40 GenesisState. +func Migrate(evidenceState v038evidence.GenesisState) *v040evidence.GenesisState { + var newEvidences = make([]*codectypes.Any, len(evidenceState.Evidence)) + for i, oldEvidence := range evidenceState.Evidence { + newEvidences[i] = migrateEvidence(oldEvidence) + } + + return &v040evidence.GenesisState{ + Evidence: newEvidences, + } +} diff --git a/x/evidence/legacy/v040/migrate_test.go b/x/evidence/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..99260f026851 --- /dev/null +++ b/x/evidence/legacy/v040/migrate_test.go @@ -0,0 +1,40 @@ +package v040_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + v038evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v038" + v040evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v040" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + addr1, _ := sdk.AccAddressFromBech32("cosmos1xxkueklal9vejv9unqu80w9vptyepfa95pd53u") + + evidenceGenState := v038evidence.GenesisState{ + Params: v038evidence.Params{MaxEvidenceAge: v038evidence.DefaultMaxEvidenceAge}, + Evidence: []v038evidence.Evidence{v038evidence.Equivocation{ + Height: 20, + Power: 100, + ConsensusAddress: addr1.Bytes(), + }}, + } + + migrated := v040evidence.Migrate(evidenceGenState) + expected := `{"evidence":[{"@type":"/cosmos.evidence.v1beta1.Equivocation","height":"20","time":"0001-01-01T00:00:00Z","power":"100","consensus_address":"cosmosvalcons1xxkueklal9vejv9unqu80w9vptyepfa99x2a3w"}]}` + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + require.Equal(t, expected, string(bz)) +} diff --git a/x/evidence/legacy/v040/types.go b/x/evidence/legacy/v040/types.go new file mode 100644 index 000000000000..41556b96b81c --- /dev/null +++ b/x/evidence/legacy/v040/types.go @@ -0,0 +1,6 @@ +package v040 + +// Default parameter values +const ( + ModuleName = "evidence" +) diff --git a/x/evidence/module.go b/x/evidence/module.go index 30b427a95c1d..4367fe8d58ce 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -1,28 +1,36 @@ package evidence import ( + "context" "encoding/json" "fmt" + "math/rand" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/evidence/client" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + eviclient "github.com/cosmos/cosmos-sdk/x/evidence/client" "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" - - "github.com/gorilla/mux" - "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/simulation" + "github.com/cosmos/cosmos-sdk/x/evidence/types" ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - - // TODO: Enable simulation once concrete types are defined. - // _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} ) // ---------------------------------------------------------------------------- @@ -31,10 +39,11 @@ var ( // AppModuleBasic implements the AppModuleBasic interface for the evidence module. type AppModuleBasic struct { - evidenceHandlers []client.EvidenceHandler // client evidence submission handlers + evidenceHandlers []eviclient.EvidenceHandler // eviclient evidence submission handlers } -func NewAppModuleBasic(evidenceHandlers ...client.EvidenceHandler) AppModuleBasic { +// NewAppModuleBasic crates a AppModuleBasic without the codec. +func NewAppModuleBasic(evidenceHandlers ...eviclient.EvidenceHandler) AppModuleBasic { return AppModuleBasic{ evidenceHandlers: evidenceHandlers, } @@ -42,54 +51,63 @@ func NewAppModuleBasic(evidenceHandlers ...client.EvidenceHandler) AppModuleBasi // Name returns the evidence module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the evidence module's types to the provided codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the evidence module's types to the LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns the evidence module's default genesis state. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the evidence module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var gs GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &gs); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var gs types.GenesisState + if err := cdc.UnmarshalJSON(bz, &gs); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } return gs.Validate() } // RegisterRESTRoutes registers the evidence module's REST service handlers. -func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { +func (a AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { evidenceRESTHandlers := make([]rest.EvidenceRESTHandler, len(a.evidenceHandlers)) for i, evidenceHandler := range a.evidenceHandlers { - evidenceRESTHandlers[i] = evidenceHandler.RESTHandler(ctx) + evidenceRESTHandlers[i] = evidenceHandler.RESTHandler(clientCtx) } - rest.RegisterRoutes(ctx, rtr, evidenceRESTHandlers) + rest.RegisterRoutes(clientCtx, rtr, evidenceRESTHandlers) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the evidence module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) } // GetTxCmd returns the evidence module's root tx command. -func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { +func (a AppModuleBasic) GetTxCmd() *cobra.Command { evidenceCLIHandlers := make([]*cobra.Command, len(a.evidenceHandlers)) for i, evidenceHandler := range a.evidenceHandlers { - evidenceCLIHandlers[i] = evidenceHandler.CLIHandler(cdc) + evidenceCLIHandlers[i] = evidenceHandler.CLIHandler() } - return cli.GetTxCmd(StoreKey, cdc, evidenceCLIHandlers) + return cli.GetTxCmd(evidenceCLIHandlers) } -// GetTxCmd returns the evidence module's root query command. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(StoreKey, cdc) +// GetQueryCmd returns the evidence module's root query command. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } // ---------------------------------------------------------------------------- @@ -100,12 +118,12 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper } -func NewAppModule(keeper Keeper) AppModule { +func NewAppModule(keeper keeper.Keeper) AppModule { return AppModule{ - AppModuleBasic: NewAppModuleBasic(), + AppModuleBasic: AppModuleBasic{}, keeper: keeper, } } @@ -116,23 +134,24 @@ func (am AppModule) Name() string { } // Route returns the evidence module's message routing key. -func (AppModule) Route() string { - return RouterKey +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) } // QuerierRoute returns the evidence module's query routing key. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute } -// NewHandler returns the evidence module's message Handler. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(am.keeper) +// LegacyQuerierHandler returns the evidence module's Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } -// NewQuerierHandler returns the evidence module's Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) } // RegisterInvariants registers the evidence module's invariants. @@ -140,20 +159,20 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // InitGenesis performs the evidence module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, bz json.RawMessage) []abci.ValidatorUpdate { - var gs GenesisState - err := ModuleCdc.UnmarshalJSON(bz, &gs) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, bz json.RawMessage) []abci.ValidatorUpdate { + var gs types.GenesisState + err := cdc.UnmarshalJSON(bz, &gs) if err != nil { - panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", ModuleName, err)) + panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", types.ModuleName, err)) } - InitGenesis(ctx, am.keeper, gs) + InitGenesis(ctx, am.keeper, &gs) return []abci.ValidatorUpdate{} } // ExportGenesis returns the evidence module's exported genesis state as raw JSON bytes. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - return ModuleCdc.MustMarshalJSON(ExportGenesis(ctx, am.keeper)) +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(ExportGenesis(ctx, am.keeper)) } // BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. @@ -166,3 +185,33 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the evidence module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents returns all the evidence content functions used to +// simulate governance proposals. +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized evidence param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for evidence module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.keeper) +} + +// WeightedOperations returns the all the gov module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/evidence/simulation/decoder.go b/x/evidence/simulation/decoder.go new file mode 100644 index 000000000000..d8c185fd0488 --- /dev/null +++ b/x/evidence/simulation/decoder.go @@ -0,0 +1,37 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +type EvidenceUnmarshaler interface { + UnmarshalEvidence([]byte) (exported.Evidence, error) +} + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding evidence type. +func NewDecodeStore(cdc EvidenceUnmarshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.KeyPrefixEvidence): + evidenceA, err := cdc.UnmarshalEvidence(kvA.Value) + if err != nil { + panic(fmt.Sprintf("cannot unmarshal evidence: %s", err.Error())) + } + + evidenceB, err := cdc.UnmarshalEvidence(kvB.Value) + if err != nil { + panic(fmt.Sprintf("cannot unmarshal evidence: %s", err.Error())) + } + + return fmt.Sprintf("%v\n%v", evidenceA, evidenceB) + default: + panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1])) + } + } +} diff --git a/x/evidence/simulation/decoder_test.go b/x/evidence/simulation/decoder_test.go new file mode 100644 index 000000000000..a63beae50120 --- /dev/null +++ b/x/evidence/simulation/decoder_test.go @@ -0,0 +1,65 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "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/evidence/simulation" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + dec := simulation.NewDecodeStore(app.EvidenceKeeper) + + delPk1 := ed25519.GenPrivKey().PubKey() + + ev := &types.Equivocation{ + Height: 10, + Time: time.Now().UTC(), + Power: 1000, + ConsensusAddress: sdk.ConsAddress(delPk1.Address()).String(), + } + + evBz, err := app.EvidenceKeeper.MarshalEvidence(ev) + require.NoError(t, err) + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: types.KeyPrefixEvidence, + Value: evBz, + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"Evidence", fmt.Sprintf("%v\n%v", ev, ev)}, + {"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/evidence/simulation/genesis.go b/x/evidence/simulation/genesis.go new file mode 100644 index 000000000000..640670ebe9a9 --- /dev/null +++ b/x/evidence/simulation/genesis.go @@ -0,0 +1,41 @@ +package simulation + +// DONTCOVER + +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/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// Simulation parameter constants +const evidence = "evidence" + +// GenEvidences returns an empty slice of evidences. +func GenEvidences(_ *rand.Rand, _ []simtypes.Account) []exported.Evidence { + return []exported.Evidence{} +} + +// RandomizedGenState generates a random GenesisState for evidence +func RandomizedGenState(simState *module.SimulationState) { + var ev []exported.Evidence + + simState.AppParams.GetOrGenerate( + simState.Cdc, evidence, &ev, simState.Rand, + func(r *rand.Rand) { ev = GenEvidences(r, simState.Accounts) }, + ) + + evidenceGenesis := types.NewGenesisState(ev) + + bz, err := json.MarshalIndent(&evidenceGenesis, "", " ") + 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(evidenceGenesis) +} diff --git a/x/evidence/simulation/genesis_test.go b/x/evidence/simulation/genesis_test.go new file mode 100644 index 000000000000..8cfa086adc97 --- /dev/null +++ b/x/evidence/simulation/genesis_test.go @@ -0,0 +1,43 @@ +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/evidence/simulation" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 evidenceGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &evidenceGenesis) + + require.Len(t, evidenceGenesis.Evidence, 0) +} diff --git a/x/evidence/spec/01_concepts.md b/x/evidence/spec/01_concepts.md index 3b50ccecfb76..78a16523daaf 100644 --- a/x/evidence/spec/01_concepts.md +++ b/x/evidence/spec/01_concepts.md @@ -9,27 +9,38 @@ order: 1 Any concrete type of evidence submitted to the `x/evidence` module must fulfill the `Evidence` contract outlined below. Not all concrete types of evidence will fulfill this contract in the same way and some data may be entirely irrelevant to certain -types of evidence. +types of evidence. An additional `ValidatorEvidence`, which extends `Evidence`, +has also been created to define a contract for evidence against malicious validators. ```go +// Evidence defines the contract which concrete evidence types of misbehavior +// must implement. type Evidence interface { - Route() string - Type() string - String() string - Hash() HexBytes - ValidateBasic() error + proto.Message - // The consensus address of the malicious validator at time of infraction - GetConsensusAddress() ConsAddress + Route() string + Type() string + String() string + Hash() tmbytes.HexBytes + ValidateBasic() error - // Height at which the infraction occurred - GetHeight() int64 + // Height at which the infraction occurred + GetHeight() int64 +} + +// ValidatorEvidence extends Evidence interface to define contract +// for evidence against malicious validators +type ValidatorEvidence interface { + Evidence + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() sdk.ConsAddress - // The total power of the malicious validator at time of infraction - GetValidatorPower() int64 + // The total power of the malicious validator at time of infraction + GetValidatorPower() int64 - // The total validator set power at time of infraction - GetTotalPower() int64 + // The total validator set power at time of infraction + GetTotalPower() int64 } ``` @@ -58,5 +69,9 @@ keepers provided to the `Handler`. In addition, the `Handler` may also perform capabilities such as slashing and jailing a validator. ```go -type Handler func(Context, Evidence) error +// Handler defines an agnostic Evidence handler. The handler is responsible +// for executing all corresponding business logic necessary for verifying the +// evidence as valid. In addition, the Handler may execute any necessary +// slashing and potential jailing. +type Handler func(sdk.Context, Evidence) error ``` diff --git a/x/evidence/spec/02_state.md b/x/evidence/spec/02_state.md index f65876837ae2..00d8d05bedff 100644 --- a/x/evidence/spec/02_state.md +++ b/x/evidence/spec/02_state.md @@ -7,10 +7,13 @@ order: 2 Currently the `x/evidence` module only stores valid submitted `Evidence` in state. The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`. -```go -type GenesisState struct { - Evidence []Evidence `json:"evidence" yaml:"evidence"` +```protobuf +// GenesisState defines the evidence module's genesis state. +message GenesisState { + // evidence defines all the evidence at genesis. + repeated google.protobuf.Any evidence = 1; } + ``` All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`). diff --git a/x/evidence/spec/03_messages.md b/x/evidence/spec/03_messages.md index a39052dab986..cd902ec99c60 100644 --- a/x/evidence/spec/03_messages.md +++ b/x/evidence/spec/03_messages.md @@ -8,10 +8,12 @@ order: 3 Evidence is submitted through a `MsgSubmitEvidence` message: -```go -type MsgSubmitEvidence struct { - Evidence Evidence - Submitter AccAddress +```protobuf +// MsgSubmitEvidence represents a message that supports submitting arbitrary +// Evidence of misbehavior such as equivocation or counterfactual signing. +message MsgSubmitEvidence { + string submitter = 1; + google.protobuf.Any evidence = 2; } ``` @@ -25,17 +27,24 @@ as follows: ```go func SubmitEvidence(ctx Context, evidence Evidence) error { if _, ok := GetEvidence(ctx, evidence.Hash()); ok { - return ErrEvidenceExists(codespace, evidence.Hash().String()) + return sdkerrors.Wrap(types.ErrEvidenceExists, evidence.Hash().String()) } if !router.HasRoute(evidence.Route()) { - return ErrNoEvidenceHandlerExists(codespace, evidence.Route()) + return sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route()) } handler := router.GetRoute(evidence.Route()) if err := handler(ctx, evidence); err != nil { - return ErrInvalidEvidence(codespace, err.Error()) + return sdkerrors.Wrap(types.ErrInvalidEvidence, err.Error()) } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSubmitEvidence, + sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), + ), + ) + SetEvidence(ctx, evidence) return nil } @@ -43,4 +52,4 @@ func SubmitEvidence(ctx Context, evidence Evidence) error { First, there must not already exist valid submitted `Evidence` of the exact same type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally, -if there is no error in handling the `Evidence`, it is persisted to state. +if there is no error in handling the `Evidence`, an event is emitted and it is persisted to state. diff --git a/x/evidence/spec/05_params.md b/x/evidence/spec/05_params.md index 15a444ecde9e..4c48b540afdd 100644 --- a/x/evidence/spec/05_params.md +++ b/x/evidence/spec/05_params.md @@ -4,8 +4,4 @@ order: 5 # Parameters -The evidence module contains the following parameters: - -| Key | Type | Example | -| -------------- | ---------------- | -------------- | -| MaxEvidenceAge | string (time ns) | "120000000000" | +The evidence module does not contain any parameters. diff --git a/x/evidence/spec/06_begin_block.md b/x/evidence/spec/06_begin_block.md index e90543862e71..317b5523ee93 100644 --- a/x/evidence/spec/06_begin_block.md +++ b/x/evidence/spec/06_begin_block.md @@ -14,8 +14,22 @@ the validator an be accordingly punished. ### Equivocation -Currently, the evidence module only handles evidence of type `Equivocation` which is derived from -Tendermint's `ABCIEvidenceTypeDuplicateVote` during `BeginBlock`. +Currently, the SDK handles two types of evidence inside ABCI's `BeginBlock`: + +- `DuplicateVoteEvidence`, +- `LightClientAttackEvidence`. + +These two evidence types are handled the same way by the evidence module. First, the SDK converts the Tendermint concrete evidence type to a SDK `Evidence` interface using `Equivocation` as the concrete type. + +```proto +// Equivocation implements the Evidence interface. +message Equivocation { + int64 height = 1; + google.protobuf.Timestamp time = 2; + int64 power = 3; + string consensus_address = 4; +} +``` For some `Equivocation` submitted in `block` to be valid, it must satisfy: @@ -36,62 +50,103 @@ validator to ever re-enter the validator set. The `Equivocation` evidence is handled as follows: ```go -func (k Keeper) HandleDoubleSign(ctx Context, evidence Equivocation) { - consAddr := evidence.GetConsensusAddress() - infractionHeight := evidence.GetHeight() - - // calculate the age of the evidence - blockTime := ctx.BlockHeader().Time - age := blockTime.Sub(evidence.GetTime()) - - // reject evidence we cannot handle - if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { - return - } - - // reject evidence if it is too old - if age > k.MaxEvidenceAge(ctx) { - return - } - - // reject evidence if the validator is already unbonded - validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) - if validator == nil || validator.IsUnbonded() { - return - } - - // verify the validator has signing info in order to be slashed and tombstoned - if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { - panic(...) - } - - // reject evidence if the validator is already tombstoned - if k.slashingKeeper.IsTombstoned(ctx, consAddr) { - return - } - - // We need to retrieve the stake distribution which signed the block, so we - // subtract ValidatorUpdateDelay from the evidence height. - // Note, that this *can* result in a negative "distributionHeight", up to - // -ValidatorUpdateDelay, i.e. at the end of the - // pre-genesis block (none) = at the beginning of the genesis block. - // That's fine since this is just used to filter unbonding delegations & redelegations. - distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay - - // Slash validator. The `power` is the int64 power of the validator as provided - // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via - // ABCI, and now received as evidence. The fraction is passed in to separately - // to slash unbonding and rebonding delegations. - k.slashingKeeper.Slash(ctx, consAddr, evidence.GetValidatorPower(), distributionHeight) - - // Jail the validator if not already jailed. This will begin unbonding the - // validator if not already unbonding (tombstoned). - if !validator.IsJailed() { - k.slashingKeeper.Jail(ctx, consAddr) - } - - k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) - k.slashingKeeper.Tombstone(ctx, consAddr) +func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) { + logger := k.Logger(ctx) + consAddr := evidence.GetConsensusAddress() + + if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil { + // Ignore evidence that cannot be handled. + // + // NOTE: We used to panic with: + // `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`, + // but this couples the expectations of the app to both Tendermint and + // the simulator. Both are expected to provide the full range of + // allowable but none of the disallowed evidence types. Instead of + // getting this coordination right, it is easier to relax the + // constraints and ignore evidence that cannot be handled. + return + } + + // calculate the age of the evidence + infractionHeight := evidence.GetHeight() + infractionTime := evidence.GetTime() + ageDuration := ctx.BlockHeader().Time.Sub(infractionTime) + ageBlocks := ctx.BlockHeader().Height - infractionHeight + + // Reject evidence if the double-sign is too old. Evidence is considered stale + // if the difference in time and number of blocks is greater than the allowed + // parameters defined. + cp := ctx.ConsensusParams() + if cp != nil && cp.Evidence != nil { + if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks { + logger.Info( + "ignored equivocation; evidence too old", + "validator", consAddr, + "infraction_height", infractionHeight, + "max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks, + "infraction_time", infractionTime, + "max_age_duration", cp.Evidence.MaxAgeDuration, + ) + return + } + } + + validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr) + if validator == nil || validator.IsUnbonded() { + // Defensive: Simulation doesn't take unbonding periods into account, and + // Tendermint might break this assumption at some point. + return + } + + if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok { + panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr)) + } + + // ignore if the validator is already tombstoned + if k.slashingKeeper.IsTombstoned(ctx, consAddr) { + logger.Info( + "ignored equivocation; validator already tombstoned", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + return + } + + logger.Info( + "confirmed equivocation", + "validator", consAddr, + "infraction_height", infractionHeight, + "infraction_time", infractionTime, + ) + + // We need to retrieve the stake distribution which signed the block, so we + // subtract ValidatorUpdateDelay from the evidence height. + // Note, that this *can* result in a negative "distributionHeight", up to + // -ValidatorUpdateDelay, i.e. at the end of the + // pre-genesis block (none) = at the beginning of the genesis block. + // That's fine since this is just used to filter unbonding delegations & redelegations. + distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay + + // Slash validator. The `power` is the int64 power of the validator as provided + // to/by Tendermint. This value is validator.Tokens as sent to Tendermint via + // ABCI, and now received as evidence. The fraction is passed in to separately + // to slash unbonding and rebonding delegations. + k.slashingKeeper.Slash( + ctx, + consAddr, + k.slashingKeeper.SlashFractionDoubleSign(ctx), + evidence.GetValidatorPower(), distributionHeight, + ) + + // Jail the validator if not already jailed. This will begin unbonding the + // validator if not already unbonding (tombstoned). + if !validator.IsJailed() { + k.slashingKeeper.Jail(ctx, consAddr) + } + + k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime) + k.slashingKeeper.Tombstone(ctx, consAddr) } ``` diff --git a/x/evidence/spec/README.md b/x/evidence/spec/README.md index ff0754133ce6..dd72780164e7 100644 --- a/x/evidence/spec/README.md +++ b/x/evidence/spec/README.md @@ -5,11 +5,12 @@ parent: title: "evidence" --> -# `evidence` +# `x/evidence` ## Table of Contents + 1. **[Concepts](01_concepts.md)** 2. **[State](02_state.md)** 3. **[Messages](03_messages.md)** diff --git a/x/evidence/types/codec.go b/x/evidence/types/codec.go new file mode 100644 index 000000000000..9de2743e38ef --- /dev/null +++ b/x/evidence/types/codec.go @@ -0,0 +1,47 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" +) + +// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the +// evidence module. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*exported.Evidence)(nil), nil) + cdc.RegisterConcrete(&MsgSubmitEvidence{}, "cosmos-sdk/MsgSubmitEvidence", nil) + cdc.RegisterConcrete(&Equivocation{}, "cosmos-sdk/Equivocation", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitEvidence{}) + registry.RegisterInterface( + "cosmos.evidence.v1beta1.Evidence", + (*exported.Evidence)(nil), + &Equivocation{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/evidence module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/evidence and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/evidence/types/errors.go b/x/evidence/types/errors.go new file mode 100644 index 000000000000..f44802709149 --- /dev/null +++ b/x/evidence/types/errors.go @@ -0,0 +1,14 @@ +// DONTCOVER +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/evidence module sentinel errors +var ( + ErrNoEvidenceHandlerExists = sdkerrors.Register(ModuleName, 2, "unregistered handler for evidence type") + ErrInvalidEvidence = sdkerrors.Register(ModuleName, 3, "invalid evidence") + ErrNoEvidenceExists = sdkerrors.Register(ModuleName, 4, "evidence does not exist") + ErrEvidenceExists = sdkerrors.Register(ModuleName, 5, "evidence already exists") +) diff --git a/x/evidence/internal/types/events.go b/x/evidence/types/events.go similarity index 100% rename from x/evidence/internal/types/events.go rename to x/evidence/types/events.go diff --git a/x/evidence/types/evidence.go b/x/evidence/types/evidence.go new file mode 100644 index 000000000000..fca6126ec36f --- /dev/null +++ b/x/evidence/types/evidence.go @@ -0,0 +1,103 @@ +package types + +import ( + "fmt" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/tmhash" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" +) + +// Evidence type constants +const ( + RouteEquivocation = "equivocation" + TypeEquivocation = "equivocation" +) + +var _ exported.Evidence = &Equivocation{} + +// Route returns the Evidence Handler route for an Equivocation type. +func (e *Equivocation) Route() string { return RouteEquivocation } + +// Type returns the Evidence Handler type for an Equivocation type. +func (e *Equivocation) Type() string { return TypeEquivocation } + +func (e *Equivocation) String() string { + bz, _ := yaml.Marshal(e) + return string(bz) +} + +// Hash returns the hash of an Equivocation object. +func (e *Equivocation) Hash() tmbytes.HexBytes { + bz, err := e.Marshal() + if err != nil { + panic(err) + } + return tmhash.Sum(bz) +} + +// ValidateBasic performs basic stateless validation checks on an Equivocation object. +func (e *Equivocation) ValidateBasic() error { + if e.Time.Unix() <= 0 { + return fmt.Errorf("invalid equivocation time: %s", e.Time) + } + if e.Height < 1 { + return fmt.Errorf("invalid equivocation height: %d", e.Height) + } + if e.Power < 1 { + return fmt.Errorf("invalid equivocation validator power: %d", e.Power) + } + if e.ConsensusAddress == "" { + return fmt.Errorf("invalid equivocation validator consensus address: %s", e.ConsensusAddress) + } + + return nil +} + +// GetConsensusAddress returns the validator's consensus address at time of the +// Equivocation infraction. +func (e Equivocation) GetConsensusAddress() sdk.ConsAddress { + addr, _ := sdk.ConsAddressFromBech32(e.ConsensusAddress) + return addr +} + +// GetHeight returns the height at time of the Equivocation infraction. +func (e Equivocation) GetHeight() int64 { + return e.Height +} + +// GetTime returns the time at time of the Equivocation infraction. +func (e Equivocation) GetTime() time.Time { + return e.Time +} + +// GetValidatorPower returns the validator's power at time of the Equivocation +// infraction. +func (e Equivocation) GetValidatorPower() int64 { + return e.Power +} + +// GetTotalPower is a no-op for the Equivocation type. +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 { + bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix() + consAddr, err := sdk.Bech32ifyAddressBytes(bech32PrefixConsAddr, e.Validator.Address) + if err != nil { + panic(err) + } + + return &Equivocation{ + Height: e.Height, + Power: e.Validator.Power, + ConsensusAddress: consAddr, + Time: e.Time, + } +} diff --git a/x/evidence/types/evidence.pb.go b/x/evidence/types/evidence.pb.go new file mode 100644 index 000000000000..116d6362d5c7 --- /dev/null +++ b/x/evidence/types/evidence.pb.go @@ -0,0 +1,426 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/evidence/v1beta1/evidence.proto + +package types + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// Equivocation implements the Evidence interface and defines evidence of double +// signing misbehavior. +type Equivocation struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Time time.Time `protobuf:"bytes,2,opt,name=time,proto3,stdtime" json:"time"` + Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` + ConsensusAddress string `protobuf:"bytes,4,opt,name=consensus_address,json=consensusAddress,proto3" json:"consensus_address,omitempty" yaml:"consensus_address"` +} + +func (m *Equivocation) Reset() { *m = Equivocation{} } +func (*Equivocation) ProtoMessage() {} +func (*Equivocation) Descriptor() ([]byte, []int) { + return fileDescriptor_dd143e71a177f0dd, []int{0} +} +func (m *Equivocation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Equivocation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Equivocation.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 *Equivocation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Equivocation.Merge(m, src) +} +func (m *Equivocation) XXX_Size() int { + return m.Size() +} +func (m *Equivocation) XXX_DiscardUnknown() { + xxx_messageInfo_Equivocation.DiscardUnknown(m) +} + +var xxx_messageInfo_Equivocation proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Equivocation)(nil), "cosmos.evidence.v1beta1.Equivocation") +} + +func init() { + proto.RegisterFile("cosmos/evidence/v1beta1/evidence.proto", fileDescriptor_dd143e71a177f0dd) +} + +var fileDescriptor_dd143e71a177f0dd = []byte{ + // 324 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x3f, 0x4f, 0x02, 0x31, + 0x18, 0xc6, 0x5b, 0x41, 0xa2, 0x27, 0x83, 0x5e, 0x88, 0x5e, 0x88, 0x69, 0x09, 0x83, 0x61, 0xe1, + 0x1a, 0x74, 0x31, 0x6c, 0x92, 0x38, 0x18, 0x37, 0xe2, 0xe4, 0x62, 0xee, 0x4f, 0x2d, 0x8d, 0xdc, + 0xbd, 0x27, 0xed, 0xa1, 0x7c, 0x03, 0x47, 0x46, 0x47, 0x46, 0x3f, 0x0a, 0x9b, 0x8c, 0x4e, 0x68, + 0x8e, 0xc5, 0xd9, 0x4f, 0x60, 0xb8, 0x02, 0x0e, 0x4e, 0xed, 0xf3, 0xe4, 0xf7, 0xfe, 0x92, 0xf7, + 0xb5, 0x4e, 0x02, 0x50, 0x11, 0x28, 0xc6, 0x87, 0x32, 0xe4, 0x71, 0xc0, 0xd9, 0xb0, 0xe5, 0x73, + 0xed, 0xb5, 0x36, 0x85, 0x9b, 0x0c, 0x40, 0x83, 0x7d, 0x64, 0x38, 0x77, 0x53, 0xaf, 0xb8, 0x6a, + 0x45, 0x80, 0x80, 0x9c, 0x61, 0xcb, 0x9f, 0xc1, 0xab, 0x54, 0x00, 0x88, 0x3e, 0x67, 0x79, 0xf2, + 0xd3, 0x7b, 0xa6, 0x65, 0xc4, 0x95, 0xf6, 0xa2, 0xc4, 0x00, 0xf5, 0x77, 0x6c, 0x95, 0x2f, 0x1f, + 0x53, 0x39, 0x84, 0xc0, 0xd3, 0x12, 0x62, 0xfb, 0xd0, 0x2a, 0xf5, 0xb8, 0x14, 0x3d, 0xed, 0xe0, + 0x1a, 0x6e, 0x14, 0xba, 0xab, 0x64, 0x9f, 0x5b, 0xc5, 0xe5, 0xac, 0xb3, 0x55, 0xc3, 0x8d, 0xbd, + 0xd3, 0xaa, 0x6b, 0xc4, 0xee, 0x5a, 0xec, 0xde, 0xac, 0xc5, 0x9d, 0x9d, 0xe9, 0x9c, 0xa2, 0xf1, + 0x27, 0xc5, 0xdd, 0x7c, 0xc2, 0xae, 0x58, 0xdb, 0x09, 0x3c, 0xf1, 0x81, 0x53, 0xc8, 0x85, 0x26, + 0xd8, 0x57, 0xd6, 0x41, 0x00, 0xb1, 0xe2, 0xb1, 0x4a, 0xd5, 0x9d, 0x17, 0x86, 0x03, 0xae, 0x94, + 0x53, 0xac, 0xe1, 0xc6, 0x6e, 0xe7, 0xf8, 0x67, 0x4e, 0x9d, 0x91, 0x17, 0xf5, 0xdb, 0xf5, 0x7f, + 0x48, 0xbd, 0xbb, 0xbf, 0xe9, 0x2e, 0x4c, 0xd5, 0x2e, 0xbf, 0x4c, 0x28, 0x7a, 0x9d, 0x50, 0xf4, + 0x3d, 0xa1, 0xa8, 0x73, 0xfd, 0x96, 0x11, 0x3c, 0xcd, 0x08, 0x9e, 0x65, 0x04, 0x7f, 0x65, 0x04, + 0x8f, 0x17, 0x04, 0xcd, 0x16, 0x04, 0x7d, 0x2c, 0x08, 0xba, 0x6d, 0x0a, 0xa9, 0x7b, 0xa9, 0xef, + 0x06, 0x10, 0xb1, 0xd5, 0xc9, 0xcd, 0xd3, 0x54, 0xe1, 0x03, 0x7b, 0xfe, 0xbb, 0xbf, 0x1e, 0x25, + 0x5c, 0xf9, 0xa5, 0x7c, 0xbf, 0xb3, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x61, 0x33, 0x3a, 0x69, + 0x9f, 0x01, 0x00, 0x00, +} + +func (m *Equivocation) 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 *Equivocation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Equivocation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsensusAddress) > 0 { + i -= len(m.ConsensusAddress) + copy(dAtA[i:], m.ConsensusAddress) + i = encodeVarintEvidence(dAtA, i, uint64(len(m.ConsensusAddress))) + i-- + dAtA[i] = 0x22 + } + if m.Power != 0 { + i = encodeVarintEvidence(dAtA, i, uint64(m.Power)) + i-- + dAtA[i] = 0x18 + } + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintEvidence(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x12 + if m.Height != 0 { + i = encodeVarintEvidence(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintEvidence(dAtA []byte, offset int, v uint64) int { + offset -= sovEvidence(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Equivocation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovEvidence(uint64(m.Height)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Time) + n += 1 + l + sovEvidence(uint64(l)) + if m.Power != 0 { + n += 1 + sovEvidence(uint64(m.Power)) + } + l = len(m.ConsensusAddress) + if l > 0 { + n += 1 + l + sovEvidence(uint64(l)) + } + return n +} + +func sovEvidence(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvidence(x uint64) (n int) { + return sovEvidence(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Equivocation) 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 ErrIntOverflowEvidence + } + 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: Equivocation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Equivocation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + 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 ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvidence + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvidence + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Time, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) + } + m.Power = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Power |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvidence + } + 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 ErrInvalidLengthEvidence + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvidence + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsensusAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvidence(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvidence + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvidence(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, ErrIntOverflowEvidence + } + 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, ErrIntOverflowEvidence + } + 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, ErrIntOverflowEvidence + } + 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, ErrInvalidLengthEvidence + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvidence + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvidence + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvidence = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvidence = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvidence = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evidence/types/evidence_test.go b/x/evidence/types/evidence_test.go new file mode 100644 index 000000000000..c78f0b059cc9 --- /dev/null +++ b/x/evidence/types/evidence_test.go @@ -0,0 +1,79 @@ +package types_test + +import ( + "testing" + "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" +) + +func TestEquivocation_Valid(t *testing.T) { + n, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + addr := sdk.ConsAddress("foo_________________") + + e := types.Equivocation{ + Height: 100, + Time: n, + Power: 1000000, + ConsensusAddress: addr.String(), + } + + require.Equal(t, e.GetTotalPower(), int64(0)) + require.Equal(t, e.GetValidatorPower(), e.Power) + require.Equal(t, e.GetTime(), e.Time) + require.Equal(t, e.GetConsensusAddress().String(), e.ConsensusAddress) + require.Equal(t, e.GetHeight(), e.Height) + require.Equal(t, e.Type(), types.TypeEquivocation) + require.Equal(t, e.Route(), types.RouteEquivocation) + require.Equal(t, e.Hash().String(), "1E10F9267BEA3A9A4AB5302C2C510CC1AFD7C54E232DA5B2E3360DFAFACF7A76") + require.Equal(t, e.String(), "height: 100\ntime: 2006-01-02T15:04:05Z\npower: 1000000\nconsensus_address: cosmosvalcons1vehk7h6lta047h6lta047h6lta047h6l8m4r53\n") + require.NoError(t, e.ValidateBasic()) +} + +func TestEquivocationValidateBasic(t *testing.T) { + var zeroTime time.Time + addr := sdk.ConsAddress("foo_________________") + + n, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + testCases := []struct { + name string + e types.Equivocation + expectErr bool + }{ + {"valid", types.Equivocation{100, n, 1000000, addr.String()}, false}, + {"invalid time", types.Equivocation{100, zeroTime, 1000000, addr.String()}, true}, + {"invalid height", types.Equivocation{0, n, 1000000, addr.String()}, true}, + {"invalid power", types.Equivocation{100, n, 0, addr.String()}, true}, + {"invalid address", types.Equivocation{100, n, 1000000, ""}, true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expectErr, tc.e.ValidateBasic() != nil) + }) + } +} + +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/expected_keepers.go b/x/evidence/types/expected_keepers.go new file mode 100644 index 000000000000..0f1d87774c6c --- /dev/null +++ b/x/evidence/types/expected_keepers.go @@ -0,0 +1,30 @@ +package types + +import ( + "time" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type ( + // StakingKeeper defines the staking module interface contract needed by the + // evidence module. + StakingKeeper interface { + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingtypes.ValidatorI + } + + // SlashingKeeper defines the slashing module interface contract needed by the + // evidence module. + SlashingKeeper interface { + GetPubkey(sdk.Context, cryptotypes.Address) (cryptotypes.PubKey, error) + IsTombstoned(sdk.Context, sdk.ConsAddress) bool + HasValidatorSigningInfo(sdk.Context, sdk.ConsAddress) bool + Tombstone(sdk.Context, sdk.ConsAddress) + Slash(sdk.Context, sdk.ConsAddress, sdk.Dec, int64, int64) + SlashFractionDoubleSign(sdk.Context) sdk.Dec + Jail(sdk.Context, sdk.ConsAddress) + JailUntil(sdk.Context, sdk.ConsAddress, time.Time) + } +) diff --git a/x/evidence/types/genesis.go b/x/evidence/types/genesis.go new file mode 100644 index 000000000000..b274ea20714f --- /dev/null +++ b/x/evidence/types/genesis.go @@ -0,0 +1,66 @@ +package types + +import ( + "fmt" + + proto "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" +) + +var _ types.UnpackInterfacesMessage = GenesisState{} + +// NewGenesisState creates a new genesis state for the evidence module. +func NewGenesisState(e []exported.Evidence) *GenesisState { + evidence := make([]*types.Any, len(e)) + for i, evi := range e { + msg, ok := evi.(proto.Message) + if !ok { + panic(fmt.Errorf("cannot proto marshal %T", evi)) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + evidence[i] = any + } + return &GenesisState{ + Evidence: evidence, + } +} + +// DefaultGenesisState returns the evidence module's default genesis state. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Evidence: []*types.Any{}, + } +} + +// Validate performs basic gensis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + for _, e := range gs.Evidence { + evi, ok := e.GetCachedValue().(exported.Evidence) + if !ok { + return fmt.Errorf("expected evidence") + } + if err := evi.ValidateBasic(); err != nil { + return err + } + } + + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (gs GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, any := range gs.Evidence { + var evi exported.Evidence + err := unpacker.UnpackAny(any, &evi) + if err != nil { + return err + } + } + return nil +} diff --git a/x/evidence/types/genesis.pb.go b/x/evidence/types/genesis.pb.go new file mode 100644 index 000000000000..672a0ce8cf2e --- /dev/null +++ b/x/evidence/types/genesis.pb.go @@ -0,0 +1,333 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/evidence/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + 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 defines the evidence module's genesis state. +type GenesisState struct { + // evidence defines all the evidence at genesis. + Evidence []*types.Any `protobuf:"bytes,1,rep,name=evidence,proto3" json:"evidence,omitempty"` +} + +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_c610c52c26e0e202, []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) GetEvidence() []*types.Any { + if m != nil { + return m.Evidence + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.evidence.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/evidence/v1beta1/genesis.proto", fileDescriptor_c610c52c26e0e202) +} + +var fileDescriptor_c610c52c26e0e202 = []byte{ + // 195 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, 0x2d, 0xcb, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 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, 0x92, 0x4c, 0xcf, 0xcf, + 0x4f, 0xcf, 0x49, 0xd5, 0x07, 0x2b, 0x4b, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0x84, 0xe8, 0x51, + 0x72, 0xe0, 0xe2, 0x71, 0x87, 0x18, 0x12, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0x64, 0xc0, 0xc5, 0x01, + 0xd3, 0x2e, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, 0x24, 0xa2, 0x07, 0xd1, 0xad, 0x07, 0xd3, 0xad, + 0xe7, 0x98, 0x57, 0x19, 0x04, 0x57, 0xe5, 0xe4, 0x7e, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, + 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, + 0x72, 0x0c, 0x51, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x50, + 0x1f, 0x40, 0x28, 0xdd, 0xe2, 0x94, 0x6c, 0xfd, 0x0a, 0x84, 0x77, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, + 0x93, 0xd8, 0xc0, 0x16, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9c, 0x4b, 0xa9, 0xfe, 0xee, + 0x00, 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.Evidence) > 0 { + for iNdEx := len(m.Evidence) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Evidence[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.Evidence) > 0 { + for _, e := range m.Evidence { + 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 Evidence", 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.Evidence = append(m.Evidence, &types.Any{}) + if err := m.Evidence[len(m.Evidence)-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/evidence/types/genesis_test.go b/x/evidence/types/genesis_test.go new file mode 100644 index 000000000000..339696c82e1f --- /dev/null +++ b/x/evidence/types/genesis_test.go @@ -0,0 +1,191 @@ +package types_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func TestDefaultGenesisState(t *testing.T) { + gs := types.DefaultGenesisState() + require.NotNil(t, gs.Evidence) + require.Len(t, gs.Evidence, 0) +} + +func TestNewGenesisState(t *testing.T) { + var ( + evidence []exported.Evidence + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "can proto marshal", + func() { + evidence = []exported.Evidence{&TestEvidence{}} + }, + true, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.msg), func(t *testing.T) { + tc.malleate() + + if tc.expPass { + require.NotPanics(t, func() { + types.NewGenesisState(evidence) + }) + } else { + require.Panics(t, func() { + types.NewGenesisState(evidence) + }) + } + }) + } +} + +func TestGenesisStateValidate(t *testing.T) { + var ( + genesisState *types.GenesisState + testEvidence []exported.Evidence + pk = ed25519.GenPrivKey() + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "valid", + func() { + testEvidence = make([]exported.Evidence, 100) + for i := 0; i < 100; i++ { + testEvidence[i] = &types.Equivocation{ + Height: int64(i + 1), + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + } + } + genesisState = types.NewGenesisState(testEvidence) + }, + true, + }, + { + "invalid", + func() { + testEvidence = make([]exported.Evidence, 100) + for i := 0; i < 100; i++ { + testEvidence[i] = &types.Equivocation{ + Height: int64(i), + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + } + } + genesisState = types.NewGenesisState(testEvidence) + }, + false, + }, + { + "expected evidence", + func() { + genesisState = &types.GenesisState{ + Evidence: []*codectypes.Any{{}}, + } + }, + false, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.msg), func(t *testing.T) { + tc.malleate() + + if tc.expPass { + require.NoError(t, genesisState.Validate()) + } else { + require.Error(t, genesisState.Validate()) + } + }) + } +} + +func TestUnpackInterfaces(t *testing.T) { + var gs = types.GenesisState{ + Evidence: []*codectypes.Any{{}}, + } + + testCases := []struct { + msg string + unpacker codectypes.AnyUnpacker + expPass bool + }{ + { + "success", + codectypes.NewInterfaceRegistry(), + true, + }, + { + "error", + codec.NewLegacyAmino(), + false, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.msg), func(t *testing.T) { + + if tc.expPass { + require.NoError(t, gs.UnpackInterfaces(tc.unpacker)) + } else { + require.Error(t, gs.UnpackInterfaces(tc.unpacker)) + } + }) + } +} + +type TestEvidence struct{} + +var _ exported.Evidence = &TestEvidence{} + +func (*TestEvidence) Route() string { + return "test-route" +} + +func (*TestEvidence) Type() string { + return "test-type" +} + +func (*TestEvidence) String() string { + return "test-string" +} + +func (*TestEvidence) ProtoMessage() {} +func (*TestEvidence) Reset() {} + +func (*TestEvidence) Hash() tmbytes.HexBytes { + return tmbytes.HexBytes([]byte("test-hash")) +} + +func (*TestEvidence) ValidateBasic() error { + return nil +} + +func (*TestEvidence) GetHeight() int64 { + return 0 +} diff --git a/x/evidence/internal/types/keys.go b/x/evidence/types/keys.go similarity index 100% rename from x/evidence/internal/types/keys.go rename to x/evidence/types/keys.go diff --git a/x/evidence/types/msgs.go b/x/evidence/types/msgs.go new file mode 100644 index 000000000000..cd2e2ba0321d --- /dev/null +++ b/x/evidence/types/msgs.go @@ -0,0 +1,97 @@ +package types + +import ( + "fmt" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" +) + +// Message types for the evidence module +const ( + TypeMsgSubmitEvidence = "submit_evidence" +) + +var ( + _ sdk.Msg = &MsgSubmitEvidence{} + _ types.UnpackInterfacesMessage = MsgSubmitEvidence{} + _ exported.MsgSubmitEvidenceI = &MsgSubmitEvidence{} +) + +// NewMsgSubmitEvidence returns a new MsgSubmitEvidence with a signer/submitter. +//nolint:interfacer +func NewMsgSubmitEvidence(s sdk.AccAddress, evi exported.Evidence) (*MsgSubmitEvidence, error) { + msg, ok := evi.(proto.Message) + if !ok { + return nil, fmt.Errorf("cannot proto marshal %T", evi) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + return nil, err + } + return &MsgSubmitEvidence{Submitter: s.String(), Evidence: any}, nil +} + +// Route returns the MsgSubmitEvidence's route. +func (m MsgSubmitEvidence) Route() string { return RouterKey } + +// Type returns the MsgSubmitEvidence's type. +func (m MsgSubmitEvidence) Type() string { return TypeMsgSubmitEvidence } + +// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitEvidence. +func (m MsgSubmitEvidence) ValidateBasic() error { + if m.Submitter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Submitter) + } + + evi := m.GetEvidence() + if evi == nil { + return sdkerrors.Wrap(ErrInvalidEvidence, "missing evidence") + } + if err := evi.ValidateBasic(); err != nil { + return err + } + + return nil +} + +// GetSignBytes returns the raw bytes a signer is expected to sign when submitting +// a MsgSubmitEvidence message. +func (m MsgSubmitEvidence) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m)) +} + +// GetSigners returns the single expected signer for a MsgSubmitEvidence. +func (m MsgSubmitEvidence) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(m.Submitter) + if err != nil { + return nil + } + + return []sdk.AccAddress{accAddr} +} + +func (m MsgSubmitEvidence) GetEvidence() exported.Evidence { + evi, ok := m.Evidence.GetCachedValue().(exported.Evidence) + if !ok { + return nil + } + return evi +} + +func (m MsgSubmitEvidence) GetSubmitter() sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(m.Submitter) + if err != nil { + return nil + } + return accAddr +} + +func (m MsgSubmitEvidence) UnpackInterfaces(ctx types.AnyUnpacker) error { + var evi exported.Evidence + return ctx.UnpackAny(m.Evidence, &evi) +} diff --git a/x/evidence/types/msgs_test.go b/x/evidence/types/msgs_test.go new file mode 100644 index 000000000000..4248b0dfba94 --- /dev/null +++ b/x/evidence/types/msgs_test.go @@ -0,0 +1,61 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func testMsgSubmitEvidence(t *testing.T, e exported.Evidence, s sdk.AccAddress) exported.MsgSubmitEvidenceI { + msg, err := types.NewMsgSubmitEvidence(s, e) + require.NoError(t, err) + return msg +} + +func TestMsgSubmitEvidence(t *testing.T) { + pk := ed25519.GenPrivKey() + submitter := sdk.AccAddress("test________________") + + testCases := []struct { + msg sdk.Msg + submitter sdk.AccAddress + expectErr bool + }{ + { + testMsgSubmitEvidence(t, &types.Equivocation{ + Height: 0, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + }, submitter), + submitter, + true, + }, + { + testMsgSubmitEvidence(t, &types.Equivocation{ + Height: 10, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: pk.PubKey().Address().String(), + }, submitter), + submitter, + false, + }, + } + + for i, tc := range testCases { + require.Equal(t, tc.msg.Route(), types.RouterKey, "unexpected result for tc #%d", i) + require.Equal(t, tc.msg.Type(), types.TypeMsgSubmitEvidence, "unexpected result for tc #%d", i) + require.Equal(t, tc.expectErr, tc.msg.ValidateBasic() != nil, "unexpected result for tc #%d", i) + + if !tc.expectErr { + require.Equal(t, tc.msg.GetSigners(), []sdk.AccAddress{tc.submitter}, "unexpected result for tc #%d", i) + } + } +} diff --git a/x/evidence/types/params.go b/x/evidence/types/params.go new file mode 100644 index 000000000000..07c23c9854e8 --- /dev/null +++ b/x/evidence/types/params.go @@ -0,0 +1,11 @@ +package types + +import ( + "time" +) + +// DONTCOVER + +// DoubleSignJailEndTime period ends at Max Time supported by Amino +// (Dec 31, 9999 - 23:59:59 GMT). +var DoubleSignJailEndTime = time.Unix(253402300799, 0) diff --git a/x/evidence/types/querier.go b/x/evidence/types/querier.go new file mode 100644 index 000000000000..72f8a604d3e0 --- /dev/null +++ b/x/evidence/types/querier.go @@ -0,0 +1,33 @@ +package types + +import ( + tmbytes "github.com/tendermint/tendermint/libs/bytes" + + query "github.com/cosmos/cosmos-sdk/types/query" +) + +// Querier routes for the evidence module +const ( + QueryEvidence = "evidence" + QueryAllEvidence = "all_evidence" +) + +// NewQueryEvidenceRequest creates a new instance of QueryEvidenceRequest. +func NewQueryEvidenceRequest(hash tmbytes.HexBytes) *QueryEvidenceRequest { + return &QueryEvidenceRequest{EvidenceHash: hash} +} + +// NewQueryAllEvidenceRequest creates a new instance of QueryAllEvidenceRequest. +func NewQueryAllEvidenceRequest(pageReq *query.PageRequest) *QueryAllEvidenceRequest { + return &QueryAllEvidenceRequest{Pagination: pageReq} +} + +// QueryAllEvidenceParams defines the parameters necessary for querying for all Evidence. +type QueryAllEvidenceParams struct { + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` +} + +func NewQueryAllEvidenceParams(page, limit int) QueryAllEvidenceParams { + return QueryAllEvidenceParams{Page: page, Limit: limit} +} diff --git a/x/evidence/types/query.pb.go b/x/evidence/types/query.pb.go new file mode 100644 index 000000000000..e084ea29c899 --- /dev/null +++ b/x/evidence/types/query.pb.go @@ -0,0 +1,1078 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/evidence/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + github_com_tendermint_tendermint_libs_bytes "github.com/tendermint/tendermint/libs/bytes" + _ "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 + +// QueryEvidenceRequest is the request type for the Query/Evidence RPC method. +type QueryEvidenceRequest struct { + // evidence_hash defines the hash of the requested evidence. + EvidenceHash github_com_tendermint_tendermint_libs_bytes.HexBytes `protobuf:"bytes,1,opt,name=evidence_hash,json=evidenceHash,proto3,casttype=github.com/tendermint/tendermint/libs/bytes.HexBytes" json:"evidence_hash,omitempty"` +} + +func (m *QueryEvidenceRequest) Reset() { *m = QueryEvidenceRequest{} } +func (m *QueryEvidenceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryEvidenceRequest) ProtoMessage() {} +func (*QueryEvidenceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_07043de1a84d215a, []int{0} +} +func (m *QueryEvidenceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEvidenceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEvidenceRequest.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 *QueryEvidenceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEvidenceRequest.Merge(m, src) +} +func (m *QueryEvidenceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryEvidenceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEvidenceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEvidenceRequest proto.InternalMessageInfo + +func (m *QueryEvidenceRequest) GetEvidenceHash() github_com_tendermint_tendermint_libs_bytes.HexBytes { + if m != nil { + return m.EvidenceHash + } + return nil +} + +// QueryEvidenceResponse is the response type for the Query/Evidence RPC method. +type QueryEvidenceResponse struct { + // evidence returns the requested evidence. + Evidence *types.Any `protobuf:"bytes,1,opt,name=evidence,proto3" json:"evidence,omitempty"` +} + +func (m *QueryEvidenceResponse) Reset() { *m = QueryEvidenceResponse{} } +func (m *QueryEvidenceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryEvidenceResponse) ProtoMessage() {} +func (*QueryEvidenceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_07043de1a84d215a, []int{1} +} +func (m *QueryEvidenceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEvidenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEvidenceResponse.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 *QueryEvidenceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEvidenceResponse.Merge(m, src) +} +func (m *QueryEvidenceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryEvidenceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEvidenceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEvidenceResponse proto.InternalMessageInfo + +func (m *QueryEvidenceResponse) GetEvidence() *types.Any { + if m != nil { + return m.Evidence + } + return nil +} + +// QueryEvidenceRequest is the request type for the Query/AllEvidence RPC +// method. +type QueryAllEvidenceRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllEvidenceRequest) Reset() { *m = QueryAllEvidenceRequest{} } +func (m *QueryAllEvidenceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllEvidenceRequest) ProtoMessage() {} +func (*QueryAllEvidenceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_07043de1a84d215a, []int{2} +} +func (m *QueryAllEvidenceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllEvidenceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllEvidenceRequest.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 *QueryAllEvidenceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllEvidenceRequest.Merge(m, src) +} +func (m *QueryAllEvidenceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllEvidenceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllEvidenceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllEvidenceRequest proto.InternalMessageInfo + +func (m *QueryAllEvidenceRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryAllEvidenceResponse is the response type for the Query/AllEvidence RPC +// method. +type QueryAllEvidenceResponse struct { + // evidence returns all evidences. + Evidence []*types.Any `protobuf:"bytes,1,rep,name=evidence,proto3" json:"evidence,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllEvidenceResponse) Reset() { *m = QueryAllEvidenceResponse{} } +func (m *QueryAllEvidenceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllEvidenceResponse) ProtoMessage() {} +func (*QueryAllEvidenceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_07043de1a84d215a, []int{3} +} +func (m *QueryAllEvidenceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllEvidenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllEvidenceResponse.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 *QueryAllEvidenceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllEvidenceResponse.Merge(m, src) +} +func (m *QueryAllEvidenceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllEvidenceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllEvidenceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllEvidenceResponse proto.InternalMessageInfo + +func (m *QueryAllEvidenceResponse) GetEvidence() []*types.Any { + if m != nil { + return m.Evidence + } + return nil +} + +func (m *QueryAllEvidenceResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterType((*QueryEvidenceRequest)(nil), "cosmos.evidence.v1beta1.QueryEvidenceRequest") + proto.RegisterType((*QueryEvidenceResponse)(nil), "cosmos.evidence.v1beta1.QueryEvidenceResponse") + proto.RegisterType((*QueryAllEvidenceRequest)(nil), "cosmos.evidence.v1beta1.QueryAllEvidenceRequest") + proto.RegisterType((*QueryAllEvidenceResponse)(nil), "cosmos.evidence.v1beta1.QueryAllEvidenceResponse") +} + +func init() { + proto.RegisterFile("cosmos/evidence/v1beta1/query.proto", fileDescriptor_07043de1a84d215a) +} + +var fileDescriptor_07043de1a84d215a = []byte{ + // 468 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcf, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0xeb, 0x22, 0xd0, 0xe4, 0x8d, 0x8b, 0x55, 0xb4, 0x11, 0xa1, 0x00, 0x99, 0xc4, 0x2f, + 0xa9, 0xf6, 0xb2, 0x71, 0x80, 0xe3, 0x2a, 0xc1, 0xc6, 0x0d, 0x72, 0x44, 0x42, 0xc8, 0x69, 0x4d, + 0x12, 0x91, 0xda, 0x59, 0xed, 0x4c, 0x8d, 0x10, 0x17, 0xfe, 0x02, 0x24, 0xc4, 0x91, 0x1b, 0x7f, + 0x0c, 0x27, 0x54, 0x89, 0x0b, 0x27, 0x84, 0x5a, 0xfe, 0x0a, 0x4e, 0x28, 0xb6, 0xd3, 0xa6, 0xbf, + 0x28, 0x9c, 0xf2, 0x62, 0xbf, 0xf7, 0xfd, 0x7e, 0xfc, 0xde, 0x83, 0xfb, 0x5d, 0x21, 0xfb, 0x42, + 0x12, 0x76, 0x9e, 0xf4, 0x18, 0xef, 0x32, 0x72, 0xee, 0x87, 0x4c, 0x51, 0x9f, 0x9c, 0xe5, 0x6c, + 0x50, 0xe0, 0x6c, 0x20, 0x94, 0x40, 0xbb, 0x26, 0x09, 0x57, 0x49, 0xd8, 0x26, 0x39, 0xf7, 0x6c, + 0x75, 0x48, 0x25, 0x33, 0x15, 0xd3, 0xfa, 0x8c, 0x46, 0x09, 0xa7, 0x2a, 0x11, 0xdc, 0x88, 0x38, + 0xad, 0x48, 0x44, 0x42, 0x87, 0xa4, 0x8c, 0xec, 0xe9, 0xd5, 0x48, 0x88, 0x28, 0x65, 0x44, 0xff, + 0x85, 0xf9, 0x2b, 0x42, 0xb9, 0x75, 0x75, 0xae, 0xd9, 0x2b, 0x9a, 0x25, 0x84, 0x72, 0x2e, 0x94, + 0x56, 0x93, 0xe6, 0xd6, 0xcb, 0x61, 0xeb, 0x59, 0x69, 0xf8, 0xc8, 0x32, 0x05, 0xec, 0x2c, 0x67, + 0x52, 0xa1, 0x17, 0xf0, 0x72, 0x85, 0xf9, 0x32, 0xa6, 0x32, 0xde, 0x03, 0x37, 0xc0, 0x9d, 0x9d, + 0xce, 0x83, 0xdf, 0x3f, 0xae, 0xdf, 0x8f, 0x12, 0x15, 0xe7, 0x21, 0xee, 0x8a, 0x3e, 0x51, 0x8c, + 0xf7, 0xd8, 0xa0, 0x9f, 0x70, 0x55, 0x0f, 0xd3, 0x24, 0x94, 0x24, 0x2c, 0x14, 0x93, 0xf8, 0x94, + 0x0d, 0x3b, 0x65, 0x10, 0xec, 0x54, 0x72, 0xa7, 0x54, 0xc6, 0xde, 0x13, 0x78, 0x65, 0xc1, 0x56, + 0x66, 0x82, 0x4b, 0x86, 0x0e, 0xe0, 0x56, 0x95, 0xa8, 0x2d, 0xb7, 0x0f, 0x5b, 0xd8, 0x3c, 0x00, + 0x57, 0x6f, 0xc3, 0xc7, 0xbc, 0x08, 0xa6, 0x59, 0x1e, 0x85, 0xbb, 0x5a, 0xea, 0x38, 0x4d, 0x17, + 0x1f, 0xf1, 0x18, 0xc2, 0x59, 0xff, 0xac, 0xdc, 0x2d, 0x6c, 0xa7, 0x50, 0x36, 0x1b, 0x9b, 0xf1, + 0xd8, 0x66, 0xe3, 0xa7, 0x34, 0xaa, 0x6a, 0x83, 0x5a, 0xa5, 0xf7, 0x11, 0xc0, 0xbd, 0x65, 0x8f, + 0x95, 0xc4, 0x17, 0x36, 0x13, 0xa3, 0x93, 0x39, 0xac, 0xa6, 0xc6, 0xba, 0xbd, 0x11, 0xcb, 0xd8, + 0xd5, 0xb9, 0x0e, 0xbf, 0x36, 0xe1, 0x45, 0xcd, 0x85, 0x3e, 0x03, 0xb8, 0x55, 0x91, 0xa1, 0x36, + 0x5e, 0xb3, 0x68, 0x78, 0xd5, 0xa8, 0x1d, 0xfc, 0xaf, 0xe9, 0x86, 0xc0, 0x7b, 0xf8, 0xee, 0xdb, + 0xaf, 0x0f, 0xcd, 0x23, 0xe4, 0x93, 0x75, 0x4b, 0x3f, 0x3d, 0x78, 0x33, 0xb7, 0x43, 0x6f, 0xd1, + 0x27, 0x00, 0xb7, 0x6b, 0x3d, 0x44, 0x07, 0x7f, 0xb7, 0x5e, 0x1e, 0xa9, 0xe3, 0xff, 0x47, 0x85, + 0xe5, 0xbd, 0xab, 0x79, 0xf7, 0xd1, 0xcd, 0x8d, 0xbc, 0x9d, 0x93, 0x2f, 0x63, 0x17, 0x8c, 0xc6, + 0x2e, 0xf8, 0x39, 0x76, 0xc1, 0xfb, 0x89, 0xdb, 0x18, 0x4d, 0xdc, 0xc6, 0xf7, 0x89, 0xdb, 0x78, + 0xde, 0xae, 0x2d, 0xbd, 0x95, 0x31, 0x9f, 0xb6, 0xec, 0xbd, 0x26, 0xc3, 0x99, 0xa6, 0x2a, 0x32, + 0x26, 0xc3, 0x4b, 0x7a, 0xf4, 0x47, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x50, 0xfb, 0x8a, 0x39, + 0x18, 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 { + // Evidence queries evidence based on evidence hash. + Evidence(ctx context.Context, in *QueryEvidenceRequest, opts ...grpc.CallOption) (*QueryEvidenceResponse, error) + // AllEvidence queries all evidence. + AllEvidence(ctx context.Context, in *QueryAllEvidenceRequest, opts ...grpc.CallOption) (*QueryAllEvidenceResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Evidence(ctx context.Context, in *QueryEvidenceRequest, opts ...grpc.CallOption) (*QueryEvidenceResponse, error) { + out := new(QueryEvidenceResponse) + err := c.cc.Invoke(ctx, "/cosmos.evidence.v1beta1.Query/Evidence", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) AllEvidence(ctx context.Context, in *QueryAllEvidenceRequest, opts ...grpc.CallOption) (*QueryAllEvidenceResponse, error) { + out := new(QueryAllEvidenceResponse) + err := c.cc.Invoke(ctx, "/cosmos.evidence.v1beta1.Query/AllEvidence", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Evidence queries evidence based on evidence hash. + Evidence(context.Context, *QueryEvidenceRequest) (*QueryEvidenceResponse, error) + // AllEvidence queries all evidence. + AllEvidence(context.Context, *QueryAllEvidenceRequest) (*QueryAllEvidenceResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Evidence(ctx context.Context, req *QueryEvidenceRequest) (*QueryEvidenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Evidence not implemented") +} +func (*UnimplementedQueryServer) AllEvidence(ctx context.Context, req *QueryAllEvidenceRequest) (*QueryAllEvidenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllEvidence not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Evidence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryEvidenceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Evidence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.evidence.v1beta1.Query/Evidence", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Evidence(ctx, req.(*QueryEvidenceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_AllEvidence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllEvidenceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllEvidence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.evidence.v1beta1.Query/AllEvidence", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllEvidence(ctx, req.(*QueryAllEvidenceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.evidence.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Evidence", + Handler: _Query_Evidence_Handler, + }, + { + MethodName: "AllEvidence", + Handler: _Query_AllEvidence_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/evidence/v1beta1/query.proto", +} + +func (m *QueryEvidenceRequest) 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 *QueryEvidenceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEvidenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EvidenceHash) > 0 { + i -= len(m.EvidenceHash) + copy(dAtA[i:], m.EvidenceHash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.EvidenceHash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryEvidenceResponse) 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 *QueryEvidenceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEvidenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Evidence != nil { + { + size, err := m.Evidence.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 *QueryAllEvidenceRequest) 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 *QueryAllEvidenceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllEvidenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAllEvidenceResponse) 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 *QueryAllEvidenceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllEvidenceResponse) 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.Evidence) > 0 { + for iNdEx := len(m.Evidence) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Evidence[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 *QueryEvidenceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.EvidenceHash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryEvidenceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Evidence != nil { + l = m.Evidence.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllEvidenceRequest) 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 *QueryAllEvidenceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Evidence) > 0 { + for _, e := range m.Evidence { + 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 *QueryEvidenceRequest) 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: QueryEvidenceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEvidenceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EvidenceHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EvidenceHash = append(m.EvidenceHash[:0], dAtA[iNdEx:postIndex]...) + if m.EvidenceHash == nil { + m.EvidenceHash = []byte{} + } + 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 *QueryEvidenceResponse) 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: QueryEvidenceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEvidenceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", 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.Evidence == nil { + m.Evidence = &types.Any{} + } + if err := m.Evidence.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 *QueryAllEvidenceRequest) 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: QueryAllEvidenceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllEvidenceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAllEvidenceResponse) 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: QueryAllEvidenceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllEvidenceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", 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.Evidence = append(m.Evidence, &types.Any{}) + if err := m.Evidence[len(m.Evidence)-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/evidence/types/query.pb.gw.go b/x/evidence/types/query.pb.gw.go new file mode 100644 index 000000000000..e15d163524b4 --- /dev/null +++ b/x/evidence/types/query.pb.gw.go @@ -0,0 +1,264 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/evidence/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_Evidence_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEvidenceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["evidence_hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "evidence_hash") + } + + protoReq.EvidenceHash, err = runtime.Bytes(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "evidence_hash", err) + } + + msg, err := client.Evidence(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Evidence_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEvidenceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["evidence_hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "evidence_hash") + } + + protoReq.EvidenceHash, err = runtime.Bytes(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "evidence_hash", err) + } + + msg, err := server.Evidence(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_AllEvidence_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_AllEvidence_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllEvidenceRequest + 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_AllEvidence_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.AllEvidence(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AllEvidence_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllEvidenceRequest + 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_AllEvidence_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.AllEvidence(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_Evidence_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_Evidence_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_Evidence_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllEvidence_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_AllEvidence_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_AllEvidence_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_Evidence_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_Evidence_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_Evidence_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AllEvidence_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_AllEvidence_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_AllEvidence_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Evidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1, 1, 0, 4, 1, 5, 3}, []string{"cosmos", "evidence", "v1beta1", "evidence_hash"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_AllEvidence_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "evidence", "v1beta1"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Evidence_0 = runtime.ForwardResponseMessage + + forward_Query_AllEvidence_0 = runtime.ForwardResponseMessage +) diff --git a/x/evidence/internal/types/router.go b/x/evidence/types/router.go similarity index 100% rename from x/evidence/internal/types/router.go rename to x/evidence/types/router.go diff --git a/x/evidence/types/router_test.go b/x/evidence/types/router_test.go new file mode 100644 index 000000000000..01e942e9ea52 --- /dev/null +++ b/x/evidence/types/router_test.go @@ -0,0 +1,28 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func testHandler(sdk.Context, exported.Evidence) error { return nil } + +func TestRouterSeal(t *testing.T) { + r := types.NewRouter() + r.Seal() + require.Panics(t, func() { r.AddRoute("test", nil) }) + require.Panics(t, func() { r.Seal() }) +} + +func TestRouter(t *testing.T) { + r := types.NewRouter() + r.AddRoute("test", testHandler) + require.True(t, r.HasRoute("test")) + require.Panics(t, func() { r.AddRoute("test", testHandler) }) + require.Panics(t, func() { r.AddRoute(" ", testHandler) }) +} diff --git a/x/evidence/types/tx.pb.go b/x/evidence/types/tx.pb.go new file mode 100644 index 000000000000..0cce8d2cbb4a --- /dev/null +++ b/x/evidence/types/tx.pb.go @@ -0,0 +1,664 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/evidence/v1beta1/tx.proto + +package types + +import ( + bytes "bytes" + 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 + +// MsgSubmitEvidence represents a message that supports submitting arbitrary +// Evidence of misbehavior such as equivocation or counterfactual signing. +type MsgSubmitEvidence struct { + Submitter string `protobuf:"bytes,1,opt,name=submitter,proto3" json:"submitter,omitempty"` + Evidence *types.Any `protobuf:"bytes,2,opt,name=evidence,proto3" json:"evidence,omitempty"` +} + +func (m *MsgSubmitEvidence) Reset() { *m = MsgSubmitEvidence{} } +func (m *MsgSubmitEvidence) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitEvidence) ProtoMessage() {} +func (*MsgSubmitEvidence) Descriptor() ([]byte, []int) { + return fileDescriptor_3e3242cb23c956e0, []int{0} +} +func (m *MsgSubmitEvidence) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitEvidence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitEvidence.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 *MsgSubmitEvidence) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitEvidence.Merge(m, src) +} +func (m *MsgSubmitEvidence) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitEvidence) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitEvidence.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitEvidence proto.InternalMessageInfo + +// MsgSubmitEvidenceResponse defines the Msg/SubmitEvidence response type. +type MsgSubmitEvidenceResponse struct { + // hash defines the hash of the evidence. + Hash []byte `protobuf:"bytes,4,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *MsgSubmitEvidenceResponse) Reset() { *m = MsgSubmitEvidenceResponse{} } +func (m *MsgSubmitEvidenceResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitEvidenceResponse) ProtoMessage() {} +func (*MsgSubmitEvidenceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3e3242cb23c956e0, []int{1} +} +func (m *MsgSubmitEvidenceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitEvidenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitEvidenceResponse.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 *MsgSubmitEvidenceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitEvidenceResponse.Merge(m, src) +} +func (m *MsgSubmitEvidenceResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitEvidenceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitEvidenceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitEvidenceResponse proto.InternalMessageInfo + +func (m *MsgSubmitEvidenceResponse) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func init() { + proto.RegisterType((*MsgSubmitEvidence)(nil), "cosmos.evidence.v1beta1.MsgSubmitEvidence") + proto.RegisterType((*MsgSubmitEvidenceResponse)(nil), "cosmos.evidence.v1beta1.MsgSubmitEvidenceResponse") +} + +func init() { proto.RegisterFile("cosmos/evidence/v1beta1/tx.proto", fileDescriptor_3e3242cb23c956e0) } + +var fileDescriptor_3e3242cb23c956e0 = []byte{ + // 316 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, 0x2d, 0xcb, 0x4c, 0x49, 0xcd, 0x4b, 0x4e, 0xd5, 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, + 0x52, 0x35, 0x97, 0xa0, 0x6f, 0x71, 0x7a, 0x70, 0x69, 0x52, 0x6e, 0x66, 0x89, 0x2b, 0xd4, 0x22, + 0x21, 0x19, 0x2e, 0xce, 0x62, 0xb0, 0x48, 0x49, 0x6a, 0x91, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, + 0x10, 0x42, 0x40, 0xc8, 0x8e, 0x8b, 0x03, 0xe6, 0x24, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, + 0x11, 0x3d, 0x88, 0xdd, 0x7a, 0x30, 0xbb, 0xf5, 0x1c, 0xf3, 0x2a, 0x9d, 0x78, 0x4e, 0x6d, 0xd1, + 0xe5, 0x80, 0x99, 0x19, 0x04, 0xd7, 0x63, 0xc5, 0xd1, 0xb1, 0x40, 0x9e, 0xe1, 0xc5, 0x02, 0x79, + 0x06, 0x25, 0x7d, 0x2e, 0x49, 0x0c, 0xcb, 0x83, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, + 0x84, 0xb8, 0x58, 0x32, 0x12, 0x8b, 0x33, 0x24, 0x58, 0x14, 0x18, 0x35, 0x78, 0x82, 0xc0, 0x6c, + 0xa3, 0x72, 0x2e, 0x66, 0xdf, 0xe2, 0x74, 0xa1, 0x02, 0x2e, 0x3e, 0x34, 0x17, 0x6b, 0xe9, 0xe1, + 0x08, 0x2c, 0x3d, 0x0c, 0x0b, 0xa4, 0x8c, 0x88, 0x57, 0x0b, 0x73, 0x8c, 0x93, 0xf7, 0x8a, 0x47, + 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, + 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9b, 0x9e, 0x59, + 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0x0b, 0x0d, 0x5d, 0x28, 0xa5, 0x5b, 0x9c, 0x92, 0xad, + 0x5f, 0x81, 0x88, 0xe3, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0x70, 0x30, 0x19, 0x03, 0x02, + 0x00, 0x00, 0xff, 0xff, 0xa7, 0x78, 0xdf, 0xa8, 0x03, 0x02, 0x00, 0x00, +} + +func (this *MsgSubmitEvidenceResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgSubmitEvidenceResponse) + if !ok { + that2, ok := that.(MsgSubmitEvidenceResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.Hash, that1.Hash) { + return false + } + return true +} + +// 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 { + // SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or + // counterfactual signing. + SubmitEvidence(ctx context.Context, in *MsgSubmitEvidence, opts ...grpc.CallOption) (*MsgSubmitEvidenceResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SubmitEvidence(ctx context.Context, in *MsgSubmitEvidence, opts ...grpc.CallOption) (*MsgSubmitEvidenceResponse, error) { + out := new(MsgSubmitEvidenceResponse) + err := c.cc.Invoke(ctx, "/cosmos.evidence.v1beta1.Msg/SubmitEvidence", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // SubmitEvidence submits an arbitrary Evidence of misbehavior such as equivocation or + // counterfactual signing. + SubmitEvidence(context.Context, *MsgSubmitEvidence) (*MsgSubmitEvidenceResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SubmitEvidence(ctx context.Context, req *MsgSubmitEvidence) (*MsgSubmitEvidenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitEvidence not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SubmitEvidence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSubmitEvidence) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SubmitEvidence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.evidence.v1beta1.Msg/SubmitEvidence", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SubmitEvidence(ctx, req.(*MsgSubmitEvidence)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.evidence.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SubmitEvidence", + Handler: _Msg_SubmitEvidence_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/evidence/v1beta1/tx.proto", +} + +func (m *MsgSubmitEvidence) 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 *MsgSubmitEvidence) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitEvidence) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Evidence != nil { + { + size, err := m.Evidence.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Submitter) > 0 { + i -= len(m.Submitter) + copy(dAtA[i:], m.Submitter) + i = encodeVarintTx(dAtA, i, uint64(len(m.Submitter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSubmitEvidenceResponse) 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 *MsgSubmitEvidenceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitEvidenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintTx(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x22 + } + 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 *MsgSubmitEvidence) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Submitter) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Evidence != nil { + l = m.Evidence.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitEvidenceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovTx(uint64(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 *MsgSubmitEvidence) 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: MsgSubmitEvidence: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitEvidence: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Submitter", 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.Submitter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Evidence", 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.Evidence == nil { + m.Evidence = &types.Any{} + } + if err := m.Evidence.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 *MsgSubmitEvidenceResponse) 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: MsgSubmitEvidenceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitEvidenceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + 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 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/genaccounts/doc.go b/x/genaccounts/doc.go index ac2f1a8c1ad2..2d255ad4651c 100644 --- a/x/genaccounts/doc.go +++ b/x/genaccounts/doc.go @@ -6,7 +6,7 @@ The ADR can be found here: https://github.com/cosmos/cosmos-sdk/blob/master/docs Genesis accounts that existed in the genesis application state under `app_state.accounts` now exists under the x/auth module's genesis state under the `app_state.auth.accounts` key. -Migration can be performed via x/auth/legacy/v0_38/migrate.go. In addition, because genesis +Migration can be performed via x/auth/legacy/v038/migrate.go. In addition, because genesis accounts are now generalized via an interface, it is now up to the application to define the concrete types and the respective client logic to add them to a genesis state/file. For an example implementation of the `add-genesis-account` command please diff --git a/x/genaccounts/legacy/v034/types.go b/x/genaccounts/legacy/v034/types.go new file mode 100644 index 000000000000..080b534d8ec1 --- /dev/null +++ b/x/genaccounts/legacy/v034/types.go @@ -0,0 +1,28 @@ +// DONTCOVER +// nolint +package v034 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "accounts" +) + +type ( + GenesisAccount struct { + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Sequence uint64 `json:"sequence_number"` + AccountNumber uint64 `json:"account_number"` + + OriginalVesting sdk.Coins `json:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting"` + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + } + + GenesisState []GenesisAccount +) diff --git a/x/genaccounts/legacy/v036/migrate.go b/x/genaccounts/legacy/v036/migrate.go new file mode 100644 index 000000000000..4da938aeaf79 --- /dev/null +++ b/x/genaccounts/legacy/v036/migrate.go @@ -0,0 +1,179 @@ +// DONTCOVER +// nolint +package v036 + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v034" + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + + "github.com/tendermint/tendermint/crypto" +) + +const ( + notBondedPoolName = "not_bonded_tokens_pool" + bondedPoolName = "bonded_tokens_pool" + feeCollectorName = "fee_collector" + mintModuleName = "mint" + + basic = "basic" + minter = "minter" + burner = "burner" + staking = "staking" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. It deletes the governance base accounts and creates the new module accounts. +// The remaining accounts are updated to the new GenesisAccount type from 0.36 +func Migrate( + oldGenState v034accounts.GenesisState, fees sdk.Coins, communityPool sdk.DecCoins, + deposits []v034gov.DepositWithMetadata, vals v034staking.Validators, ubds []v034staking.UnbondingDelegation, + valOutRewards []v034distr.ValidatorOutstandingRewardsRecord, bondDenom, distrModuleName, govModuleName string, +) GenesisState { + + depositedCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) + burnedDepositCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) + + bondedAmt := sdk.ZeroInt() + notBondedAmt := sdk.ZeroInt() + + // remove the two previous governance base accounts for deposits and burned + // coins from rejected proposals add six new module accounts: + // distribution, gov, mint, fee collector, bonded and not bonded pool + var ( + newGenState GenesisState + govCoins sdk.Coins + extraAccounts = 6 + ) + + for _, acc := range oldGenState { + switch { + case acc.Address.Equals(depositedCoinsAccAddr): + // remove gov deposits base account + govCoins = acc.Coins + extraAccounts -= 1 + + case acc.Address.Equals(burnedDepositCoinsAccAddr): + // remove gov burned deposits base account + extraAccounts -= 1 + + default: + newGenState = append( + newGenState, + NewGenesisAccount( + acc.Address, acc.Coins, acc.Sequence, + acc.OriginalVesting, acc.DelegatedFree, acc.DelegatedVesting, + acc.StartTime, acc.EndTime, "", []string{}, + ), + ) + } + } + + var expDeposits sdk.Coins + for _, deposit := range deposits { + expDeposits = expDeposits.Add(deposit.Deposit.Amount...) + } + + if !expDeposits.IsEqual(govCoins) { + panic( + fmt.Sprintf( + "pre migration deposit base account coins ≠ stored deposits coins (%s ≠ %s)", + expDeposits.String(), govCoins.String(), + ), + ) + } + + // get staking module accounts coins + for _, validator := range vals { + switch validator.Status { + case v034staking.Bonded: + bondedAmt = bondedAmt.Add(validator.Tokens) + + case v034staking.Unbonding, v034staking.Unbonded: + notBondedAmt = notBondedAmt.Add(validator.Tokens) + + default: + panic("invalid validator status") + } + } + + for _, ubd := range ubds { + for _, entry := range ubd.Entries { + notBondedAmt = notBondedAmt.Add(entry.Balance) + } + } + + bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, bondedAmt)) + notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, notBondedAmt)) + + // get distr module account coins + var distrDecCoins sdk.DecCoins + for _, reward := range valOutRewards { + distrDecCoins = distrDecCoins.Add(reward.OutstandingRewards...) + } + + distrCoins, _ := distrDecCoins.Add(communityPool...).TruncateDecimal() + + // get module account addresses + feeCollectorAddr := sdk.AccAddress(crypto.AddressHash([]byte(feeCollectorName))) + govAddr := sdk.AccAddress(crypto.AddressHash([]byte(govModuleName))) + bondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(bondedPoolName))) + notBondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(notBondedPoolName))) + distrAddr := sdk.AccAddress(crypto.AddressHash([]byte(distrModuleName))) + mintAddr := sdk.AccAddress(crypto.AddressHash([]byte(mintModuleName))) + + // create module genesis accounts + feeCollectorModuleAcc := NewGenesisAccount( + feeCollectorAddr, fees, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, feeCollectorName, []string{basic}, + ) + govModuleAcc := NewGenesisAccount( + govAddr, govCoins, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, govModuleName, []string{burner}, + ) + distrModuleAcc := NewGenesisAccount( + distrAddr, distrCoins, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, distrModuleName, []string{basic}, + ) + bondedModuleAcc := NewGenesisAccount( + bondedAddr, bondedCoins, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, bondedPoolName, []string{burner, staking}, + ) + notBondedModuleAcc := NewGenesisAccount( + notBondedAddr, notBondedCoins, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, notBondedPoolName, []string{burner, staking}, + ) + mintModuleAcc := NewGenesisAccount( + mintAddr, sdk.Coins{}, 0, + sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, + 0, 0, mintModuleName, []string{minter}, + ) + + newGenState = append( + newGenState, + []GenesisAccount{ + feeCollectorModuleAcc, govModuleAcc, distrModuleAcc, + bondedModuleAcc, notBondedModuleAcc, mintModuleAcc, + }..., + ) + + // verify the total number of accounts is correct + if len(newGenState) != len(oldGenState)+extraAccounts { + panic( + fmt.Sprintf( + "invalid total number of genesis accounts; got: %d, expected: %d", + len(newGenState), len(oldGenState)+extraAccounts), + ) + } + + return newGenState +} diff --git a/x/genaccounts/legacy/v036/migrate_test.go b/x/genaccounts/legacy/v036/migrate_test.go new file mode 100644 index 000000000000..09f6bb3e6901 --- /dev/null +++ b/x/genaccounts/legacy/v036/migrate_test.go @@ -0,0 +1,126 @@ +package v036 + +import ( + "testing" + + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/types" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v034" + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + + "github.com/stretchr/testify/require" +) + +var ( + priv = secp256k1.GenPrivKey() + addr = types.AccAddress(priv.PubKey().Address()) + depositedCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) + burnedDepositCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) + + coins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 10)} + halfCoins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 5)} + + accountDeposited = v034accounts.GenesisAccount{ + Address: depositedCoinsAccAddr, + Coins: coins, + Sequence: 1, + AccountNumber: 1, + + OriginalVesting: coins, + DelegatedFree: coins, + DelegatedVesting: coins, + StartTime: 0, + EndTime: 0, + } + + accountBurned = v034accounts.GenesisAccount{ + Address: burnedDepositCoinsAccAddr, + Coins: coins, + Sequence: 2, + AccountNumber: 2, + + OriginalVesting: coins, + DelegatedFree: coins, + DelegatedVesting: coins, + StartTime: 0, + EndTime: 0, + } + + deposit = v034gov.DepositWithMetadata{ + ProposalID: 1, + Deposit: v034gov.Deposit{ + ProposalID: 1, + Depositor: addr, + Amount: coins, + }, + } +) + +func TestMigrateEmptyRecord(t *testing.T) { + + type args struct { + accounts v034accounts.GenesisState + deposits []v034gov.DepositWithMetadata + } + tests := []struct { + name string + args args + }{ + {"No Accounts", args{v034accounts.GenesisState{}, []v034gov.DepositWithMetadata{}}}, + {"Deposited account", args{v034accounts.GenesisState{accountDeposited}, []v034gov.DepositWithMetadata{deposit}}}, + {"Burned account", args{v034accounts.GenesisState{accountBurned}, []v034gov.DepositWithMetadata{}}}, + {"Burned and deposited accounts", args{v034accounts.GenesisState{accountDeposited, accountBurned}, []v034gov.DepositWithMetadata{deposit}}}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + require.NotPanics(t, func() { + Migrate( + tt.args.accounts, + types.Coins{}, + types.DecCoins{}, + tt.args.deposits, + v034staking.Validators{}, + []v034staking.UnbondingDelegation{}, + []v034distr.ValidatorOutstandingRewardsRecord{}, + types.DefaultBondDenom, + v034distr.ModuleName, + v034gov.ModuleName, + ) + }) + }) + } +} + +func TestMigrateWrongDeposit(t *testing.T) { + require.Panics(t, func() { + Migrate( + v034accounts.GenesisState{ + accountDeposited, + accountBurned, + }, + types.Coins{}, + types.DecCoins{}, + []v034gov.DepositWithMetadata{ + { + ProposalID: 1, + Deposit: v034gov.Deposit{ + ProposalID: 1, + Depositor: addr, + Amount: halfCoins, + }, + }, + }, + v034staking.Validators{}, + []v034staking.UnbondingDelegation{}, + []v034distr.ValidatorOutstandingRewardsRecord{}, + types.DefaultBondDenom, + v034distr.ModuleName, + v034gov.ModuleName, + ) + }) +} diff --git a/x/genaccounts/legacy/v036/types.go b/x/genaccounts/legacy/v036/types.go new file mode 100644 index 000000000000..8d143e69d2dc --- /dev/null +++ b/x/genaccounts/legacy/v036/types.go @@ -0,0 +1,53 @@ +// DONTCOVER +// nolint +package v036 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "accounts" +) + +type ( + GenesisAccount struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + Sequence uint64 `json:"sequence_number" yaml:"sequence_number"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + StartTime int64 `json:"start_time" yaml:"start_time"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + ModuleName string `json:"module_name" yaml:"module_name"` + ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` + } + + GenesisState []GenesisAccount +) + +// NewGenesisAccount creates a new GenesisAccount object +func NewGenesisAccount( + address sdk.AccAddress, coins sdk.Coins, sequence uint64, + vestingAmount, delFree, delVesting sdk.Coins, vestingStartTime, vestingEndTime int64, + module string, permissions []string, +) GenesisAccount { + + return GenesisAccount{ + Address: address, + Coins: coins, + Sequence: sequence, + AccountNumber: 0, // ignored set by the account keeper during InitGenesis + OriginalVesting: vestingAmount, + DelegatedFree: delFree, + DelegatedVesting: delVesting, + StartTime: vestingStartTime, + EndTime: vestingEndTime, + ModuleName: module, + ModulePermissions: permissions, + } +} diff --git a/x/genaccounts/legacy/v0_34/types.go b/x/genaccounts/legacy/v0_34/types.go deleted file mode 100644 index ee1bd419abeb..000000000000 --- a/x/genaccounts/legacy/v0_34/types.go +++ /dev/null @@ -1,28 +0,0 @@ -// DONTCOVER -// nolint -package v0_34 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - ModuleName = "accounts" -) - -type ( - GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - Sequence uint64 `json:"sequence_number"` - AccountNumber uint64 `json:"account_number"` - - OriginalVesting sdk.Coins `json:"original_vesting"` - DelegatedFree sdk.Coins `json:"delegated_free"` - DelegatedVesting sdk.Coins `json:"delegated_vesting"` - StartTime int64 `json:"start_time"` - EndTime int64 `json:"end_time"` - } - - GenesisState []GenesisAccount -) diff --git a/x/genaccounts/legacy/v0_36/migrate.go b/x/genaccounts/legacy/v0_36/migrate.go deleted file mode 100644 index 7fea5fed3f28..000000000000 --- a/x/genaccounts/legacy/v0_36/migrate.go +++ /dev/null @@ -1,179 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" - v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_34" - v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_34" - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" - - "github.com/tendermint/tendermint/crypto" -) - -const ( - notBondedPoolName = "not_bonded_tokens_pool" - bondedPoolName = "bonded_tokens_pool" - feeCollectorName = "fee_collector" - mintModuleName = "mint" - - basic = "basic" - minter = "minter" - burner = "burner" - staking = "staking" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 -// genesis state. It deletes the governance base accounts and creates the new module accounts. -// The remaining accounts are updated to the new GenesisAccount type from 0.36 -func Migrate( - oldGenState v034accounts.GenesisState, fees sdk.Coins, communityPool sdk.DecCoins, - deposits []v034gov.DepositWithMetadata, vals v034staking.Validators, ubds []v034staking.UnbondingDelegation, - valOutRewards []v034distr.ValidatorOutstandingRewardsRecord, bondDenom, distrModuleName, govModuleName string, -) GenesisState { - - depositedCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) - burnedDepositCoinsAccAddr := sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) - - bondedAmt := sdk.ZeroInt() - notBondedAmt := sdk.ZeroInt() - - // remove the two previous governance base accounts for deposits and burned - // coins from rejected proposals add six new module accounts: - // distribution, gov, mint, fee collector, bonded and not bonded pool - var ( - newGenState GenesisState - govCoins sdk.Coins - extraAccounts = 6 - ) - - for _, acc := range oldGenState { - switch { - case acc.Address.Equals(depositedCoinsAccAddr): - // remove gov deposits base account - govCoins = acc.Coins - extraAccounts -= 1 - - case acc.Address.Equals(burnedDepositCoinsAccAddr): - // remove gov burned deposits base account - extraAccounts -= 1 - - default: - newGenState = append( - newGenState, - NewGenesisAccount( - acc.Address, acc.Coins, acc.Sequence, - acc.OriginalVesting, acc.DelegatedFree, acc.DelegatedVesting, - acc.StartTime, acc.EndTime, "", []string{}, - ), - ) - } - } - - var expDeposits sdk.Coins - for _, deposit := range deposits { - expDeposits = expDeposits.Add(deposit.Deposit.Amount...) - } - - if !expDeposits.IsEqual(govCoins) { - panic( - fmt.Sprintf( - "pre migration deposit base account coins ≠ stored deposits coins (%s ≠ %s)", - expDeposits.String(), govCoins.String(), - ), - ) - } - - // get staking module accounts coins - for _, validator := range vals { - switch validator.Status { - case sdk.Bonded: - bondedAmt = bondedAmt.Add(validator.Tokens) - - case sdk.Unbonding, sdk.Unbonded: - notBondedAmt = notBondedAmt.Add(validator.Tokens) - - default: - panic("invalid validator status") - } - } - - for _, ubd := range ubds { - for _, entry := range ubd.Entries { - notBondedAmt = notBondedAmt.Add(entry.Balance) - } - } - - bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, bondedAmt)) - notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, notBondedAmt)) - - // get distr module account coins - var distrDecCoins sdk.DecCoins - for _, reward := range valOutRewards { - distrDecCoins = distrDecCoins.Add(reward.OutstandingRewards...) - } - - distrCoins, _ := distrDecCoins.Add(communityPool...).TruncateDecimal() - - // get module account addresses - feeCollectorAddr := sdk.AccAddress(crypto.AddressHash([]byte(feeCollectorName))) - govAddr := sdk.AccAddress(crypto.AddressHash([]byte(govModuleName))) - bondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(bondedPoolName))) - notBondedAddr := sdk.AccAddress(crypto.AddressHash([]byte(notBondedPoolName))) - distrAddr := sdk.AccAddress(crypto.AddressHash([]byte(distrModuleName))) - mintAddr := sdk.AccAddress(crypto.AddressHash([]byte(mintModuleName))) - - // create module genesis accounts - feeCollectorModuleAcc := NewGenesisAccount( - feeCollectorAddr, fees, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, feeCollectorName, []string{basic}, - ) - govModuleAcc := NewGenesisAccount( - govAddr, govCoins, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, govModuleName, []string{burner}, - ) - distrModuleAcc := NewGenesisAccount( - distrAddr, distrCoins, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, distrModuleName, []string{basic}, - ) - bondedModuleAcc := NewGenesisAccount( - bondedAddr, bondedCoins, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, bondedPoolName, []string{burner, staking}, - ) - notBondedModuleAcc := NewGenesisAccount( - notBondedAddr, notBondedCoins, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, notBondedPoolName, []string{burner, staking}, - ) - mintModuleAcc := NewGenesisAccount( - mintAddr, sdk.Coins{}, 0, - sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, - 0, 0, mintModuleName, []string{minter}, - ) - - newGenState = append( - newGenState, - []GenesisAccount{ - feeCollectorModuleAcc, govModuleAcc, distrModuleAcc, - bondedModuleAcc, notBondedModuleAcc, mintModuleAcc, - }..., - ) - - // verify the total number of accounts is correct - if len(newGenState) != len(oldGenState)+extraAccounts { - panic( - fmt.Sprintf( - "invalid total number of genesis accounts; got: %d, expected: %d", - len(newGenState), len(oldGenState)+extraAccounts), - ) - } - - return newGenState -} diff --git a/x/genaccounts/legacy/v0_36/migrate_test.go b/x/genaccounts/legacy/v0_36/migrate_test.go deleted file mode 100644 index 250dd85474f1..000000000000 --- a/x/genaccounts/legacy/v0_36/migrate_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package v0_36 - -import ( - "testing" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - "github.com/cosmos/cosmos-sdk/types" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" - v034accounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_34" - v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_34" - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" - - "github.com/stretchr/testify/require" -) - -var ( - priv = secp256k1.GenPrivKey() - addr = types.AccAddress(priv.PubKey().Address()) - depositedCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govDepositedCoins"))) - burnedDepositCoinsAccAddr = types.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) - - coins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 10)} - halfCoins = types.Coins{types.NewInt64Coin(types.DefaultBondDenom, 5)} - - accountDeposited = v034accounts.GenesisAccount{ - Address: depositedCoinsAccAddr, - Coins: coins, - Sequence: 1, - AccountNumber: 1, - - OriginalVesting: coins, - DelegatedFree: coins, - DelegatedVesting: coins, - StartTime: 0, - EndTime: 0, - } - - accountBurned = v034accounts.GenesisAccount{ - Address: burnedDepositCoinsAccAddr, - Coins: coins, - Sequence: 2, - AccountNumber: 2, - - OriginalVesting: coins, - DelegatedFree: coins, - DelegatedVesting: coins, - StartTime: 0, - EndTime: 0, - } - - deposit = v034gov.DepositWithMetadata{ - ProposalID: 1, - Deposit: v034gov.Deposit{ - ProposalID: 1, - Depositor: addr, - Amount: coins, - }, - } -) - -func TestMigrateEmptyRecord(t *testing.T) { - - type args struct { - accounts v034accounts.GenesisState - deposits []v034gov.DepositWithMetadata - } - tests := []struct { - name string - args args - }{ - {"No Accounts", args{v034accounts.GenesisState{}, []v034gov.DepositWithMetadata{}}}, - {"Deposited account", args{v034accounts.GenesisState{accountDeposited}, []v034gov.DepositWithMetadata{deposit}}}, - {"Burned account", args{v034accounts.GenesisState{accountBurned}, []v034gov.DepositWithMetadata{}}}, - {"Burned and deposited accounts", args{v034accounts.GenesisState{accountDeposited, accountBurned}, []v034gov.DepositWithMetadata{deposit}}}, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - require.NotPanics(t, func() { - Migrate( - tt.args.accounts, - types.Coins{}, - types.DecCoins{}, - tt.args.deposits, - v034staking.Validators{}, - []v034staking.UnbondingDelegation{}, - []v034distr.ValidatorOutstandingRewardsRecord{}, - types.DefaultBondDenom, - v034distr.ModuleName, - v034gov.ModuleName, - ) - }) - }) - } -} - -func TestMigrateWrongDeposit(t *testing.T) { - require.Panics(t, func() { - Migrate( - v034accounts.GenesisState{ - accountDeposited, - accountBurned, - }, - types.Coins{}, - types.DecCoins{}, - []v034gov.DepositWithMetadata{ - { - ProposalID: 1, - Deposit: v034gov.Deposit{ - ProposalID: 1, - Depositor: addr, - Amount: halfCoins, - }, - }, - }, - v034staking.Validators{}, - []v034staking.UnbondingDelegation{}, - []v034distr.ValidatorOutstandingRewardsRecord{}, - types.DefaultBondDenom, - v034distr.ModuleName, - v034gov.ModuleName, - ) - }) -} diff --git a/x/genaccounts/legacy/v0_36/types.go b/x/genaccounts/legacy/v0_36/types.go deleted file mode 100644 index 54596f0a6350..000000000000 --- a/x/genaccounts/legacy/v0_36/types.go +++ /dev/null @@ -1,53 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - ModuleName = "accounts" -) - -type ( - GenesisAccount struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - Sequence uint64 `json:"sequence_number" yaml:"sequence_number"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - - OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` - DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` - DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` - StartTime int64 `json:"start_time" yaml:"start_time"` - EndTime int64 `json:"end_time" yaml:"end_time"` - - ModuleName string `json:"module_name" yaml:"module_name"` - ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` - } - - GenesisState []GenesisAccount -) - -// NewGenesisAccount creates a new GenesisAccount object -func NewGenesisAccount( - address sdk.AccAddress, coins sdk.Coins, sequence uint64, - vestingAmount, delFree, delVesting sdk.Coins, vestingStartTime, vestingEndTime int64, - module string, permissions []string, -) GenesisAccount { - - return GenesisAccount{ - Address: address, - Coins: coins, - Sequence: sequence, - AccountNumber: 0, // ignored set by the account keeper during InitGenesis - OriginalVesting: vestingAmount, - DelegatedFree: delFree, - DelegatedVesting: delVesting, - StartTime: vestingStartTime, - EndTime: vestingEndTime, - ModuleName: module, - ModulePermissions: permissions, - } -} diff --git a/x/genutil/alias.go b/x/genutil/alias.go deleted file mode 100644 index 92cd6f78b06f..000000000000 --- a/x/genutil/alias.go +++ /dev/null @@ -1,36 +0,0 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/genutil/types -package genutil - -import ( - "github.com/cosmos/cosmos-sdk/x/genutil/types" -) - -const ( - ModuleName = types.ModuleName -) - -var ( - // functions aliases - NewGenesisState = types.NewGenesisState - NewGenesisStateFromStdTx = types.NewGenesisStateFromStdTx - NewInitConfig = types.NewInitConfig - GetGenesisStateFromAppState = types.GetGenesisStateFromAppState - SetGenesisStateInAppState = types.SetGenesisStateInAppState - GenesisStateFromGenDoc = types.GenesisStateFromGenDoc - GenesisStateFromGenFile = types.GenesisStateFromGenFile - ValidateGenesis = types.ValidateGenesis - - // variable aliases - ModuleCdc = types.ModuleCdc -) - -type ( - GenesisState = types.GenesisState - AppMap = types.AppMap - MigrationCallback = types.MigrationCallback - MigrationMap = types.MigrationMap - InitConfig = types.InitConfig -) diff --git a/x/genutil/client/cli/collect.go b/x/genutil/client/cli/collect.go index bc38ab74d287..cc862583680c 100644 --- a/x/genutil/client/cli/collect.go +++ b/x/genutil/client/cli/collect.go @@ -6,12 +6,10 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -20,16 +18,19 @@ import ( const flagGenTxDir = "gentx-dir" // CollectGenTxsCmd - return the cobra command to collect genesis transactions -func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec, - genAccIterator types.GenesisAccountsIterator, defaultNodeHome string) *cobra.Command { - +func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "collect-gentxs", Short: "Collect genesis txs and output a genesis.json file", - RunE: func(_ *cobra.Command, _ []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) - name := viper.GetString(flags.FlagName) + RunE: func(cmd *cobra.Command, _ []string) error { + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + clientCtx := client.GetClientContextFromCmd(cmd) + cdc := clientCtx.JSONMarshaler + + config.SetRoot(clientCtx.HomeDir) + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) if err != nil { return errors.Wrap(err, "failed to initialize node validator files") @@ -40,31 +41,30 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec, return errors.Wrap(err, "failed to read genesis doc from file") } - genTxsDir := viper.GetString(flagGenTxDir) + genTxDir, _ := cmd.Flags().GetString(flagGenTxDir) + genTxsDir := genTxDir if genTxsDir == "" { genTxsDir = filepath.Join(config.RootDir, "config", "gentx") } toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage("")) - initCfg := genutil.NewInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey) + initCfg := types.NewInitConfig(genDoc.ChainID, genTxsDir, nodeID, valPubKey) - appMessage, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator) + appMessage, err := genutil.GenAppStateFromConfig(cdc, + clientCtx.TxConfig, + config, initCfg, *genDoc, genBalIterator) if err != nil { return errors.Wrap(err, "failed to get genesis app state from config") } toPrint.AppMessage = appMessage - // print out some key information - return displayInfo(cdc, toPrint) + return displayInfo(toPrint) }, } - cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") - cmd.Flags().String(flagGenTxDir, "", - "override default \"gentx\" directory from which collect and execute "+ - "genesis transactions; default [--home]/config/gentx/") + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") + return cmd } - -// DONTCOVER diff --git a/x/genutil/client/cli/gentx.go b/x/genutil/client/cli/gentx.go index 4624d1540a4e..42d957346fba 100644 --- a/x/genutil/client/cli/gentx.go +++ b/x/genutil/client/cli/gentx.go @@ -12,66 +12,72 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - flag "github.com/spf13/pflag" - "github.com/spf13/viper" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" tmos "github.com/tendermint/tendermint/libs/os" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/version" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" ) -// StakingMsgBuildingHelpers helpers for message building gen-tx command -type StakingMsgBuildingHelpers interface { - CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) - PrepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey) - BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) (auth.TxBuilder, sdk.Msg, error) -} - // GenTxCmd builds the application's gentx command. -// nolint: errcheck -func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, smbh StakingMsgBuildingHelpers, - genAccIterator types.GenesisAccountsIterator, defaultNodeHome, defaultCLIHome string) *cobra.Command { - +func GenTxCmd(mbm module.BasicManager, txEncCfg client.TxEncodingConfig, genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command { ipDefault, _ := server.ExternalIP() - fsCreateValidator, flagNodeID, flagPubKey, flagAmount, defaultsDesc := smbh.CreateValidatorMsgHelpers(ipDefault) + fsCreateValidator, defaultsDesc := cli.CreateValidatorMsgFlagSet(ipDefault) cmd := &cobra.Command{ - Use: "gentx", + Use: "gentx [key_name] [amount]", Short: "Generate a genesis tx carrying a self delegation", - Args: cobra.NoArgs, - Long: fmt.Sprintf(`This command is an alias of the 'tx create-validator' command'. - - It creates a genesis transaction to create a validator. - The following default parameters are included: - %s`, defaultsDesc), - + Args: cobra.ExactArgs(2), + Long: fmt.Sprintf(`Generate a genesis transaction that creates a validator with a self-delegation, +that is signed by the key in the Keyring referenced by a given name. A node ID and Bech32 consensus +pubkey may optionally be provided. If they are omitted, they will be retrieved from the priv_validator.json +file. The following default parameters are included: + %s + +Example: +$ %s gentx my-key-name 1000000stake --home=/path/to/home/dir --keyring-backend=os --chain-id=test-chain-1 \ + --moniker="myvalidator" \ + --commission-max-change-rate=0.01 \ + --commission-max-rate=1.0 \ + --commission-rate=0.07 \ + --details="..." \ + --security-contact="..." \ + --website="..." +`, defaultsDesc, version.AppName, + ), RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := server.GetServerContextFromCmd(cmd) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + cdc := clientCtx.JSONMarshaler + + config := serverCtx.Config + config.SetRoot(clientCtx.HomeDir) - config := ctx.Config - config.SetRoot(viper.GetString(flags.FlagHome)) - nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(ctx.Config) + nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(serverCtx.Config) if err != nil { return errors.Wrap(err, "failed to initialize node validator files") } - // Read --nodeID, if empty take it from priv_validator.json - if nodeIDString := viper.GetString(flagNodeID); nodeIDString != "" { + // read --nodeID, if empty take it from priv_validator.json + if nodeIDString, _ := cmd.Flags().GetString(cli.FlagNodeID); nodeIDString != "" { nodeID = nodeIDString } - // Read --pubkey, if empty take it from priv_validator.json - if valPubKeyString := viper.GetString(flagPubKey); valPubKeyString != "" { + + // read --pubkey, if empty take it from priv_validator.json + if valPubKeyString, _ := cmd.Flags().GetString(cli.FlagPubKey); valPubKeyString != "" { valPubKey, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, valPubKeyString) if err != nil { return errors.Wrap(err, "failed to get consensus node public key") @@ -84,86 +90,101 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm } var genesisState map[string]json.RawMessage - if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { + if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { return errors.Wrap(err, "failed to unmarshal genesis state") } - if err = mbm.ValidateGenesis(genesisState); err != nil { + if err = mbm.ValidateGenesis(cdc, txEncCfg, genesisState); err != nil { return errors.Wrap(err, "failed to validate genesis state") } inBuf := bufio.NewReader(cmd.InOrStdin()) - kb, err := keys.NewKeyring(sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), viper.GetString(flagClientHome), inBuf) + + name := args[0] + key, err := clientCtx.Keyring.Key(name) if err != nil { - return errors.Wrap(err, "failed to initialize keybase") + return errors.Wrapf(err, "failed to fetch '%s' from the keyring", name) } - name := viper.GetString(flags.FlagName) - key, err := kb.Get(name) - if err != nil { - return errors.Wrap(err, "failed to read from keybase") + moniker := config.Moniker + if m, _ := cmd.Flags().GetString(cli.FlagMoniker); m != "" { + moniker = m } - // Set flags for creating gentx - viper.Set(flags.FlagHome, viper.GetString(flagClientHome)) - smbh.PrepareFlagsForTxCreateValidator(config, nodeID, genDoc.ChainID, valPubKey) + // set flags for creating a gentx + createValCfg, err := cli.PrepareConfigForTxCreateValidator(cmd.Flags(), moniker, nodeID, genDoc.ChainID, valPubKey) + if err != nil { + return errors.Wrap(err, "error creating configuration to create validator msg") + } - // Fetch the amount of coins staked - amount := viper.GetString(flagAmount) - coins, err := sdk.ParseCoins(amount) + amount := args[1] + coins, err := sdk.ParseCoinsNormalized(amount) if err != nil { return errors.Wrap(err, "failed to parse coins") } - err = genutil.ValidateAccountInGenesis(genesisState, genAccIterator, key.GetAddress(), coins, cdc) + err = genutil.ValidateAccountInGenesis(genesisState, genBalIterator, key.GetAddress(), coins, cdc) if err != nil { return errors.Wrap(err, "failed to validate account in genesis") } - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + if err != nil { + return errors.Wrap(err, "error creating tx builder") + } - // Set the generate-only flag here after the CLI context has - // been created. This allows the from name/key to be correctly populated. + clientCtx = clientCtx.WithInput(inBuf).WithFromAddress(key.GetAddress()) + + // The following line comes from a discrepancy between the `gentx` + // and `create-validator` commands: + // - `gentx` expects amount as an arg, + // - `create-validator` expects amount as a required flag. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8251 + // Since gentx doesn't set the amount flag (which `create-validator` + // reads from), we copy the amount arg into the valCfg directly. // - // TODO: Consider removing the manual setting of generate-only in - // favor of a 'gentx' flag in the create-validator command. - viper.Set(flags.FlagGenerateOnly, true) + // Ideally, the `create-validator` command should take a validator + // config file instead of so many flags. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8177 + createValCfg.Amount = amount // create a 'create-validator' message - txBldr, msg, err := smbh.BuildCreateValidatorMsg(cliCtx, txBldr) + txBldr, msg, err := cli.BuildCreateValidatorMsg(clientCtx, createValCfg, txFactory, true) if err != nil { return errors.Wrap(err, "failed to build create-validator message") } - if key.GetType() == keys.TypeOffline || key.GetType() == keys.TypeMulti { - fmt.Println("Offline key passed in. Use `tx sign` command to sign:") - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}) + if key.GetType() == keyring.TypeOffline || key.GetType() == keyring.TypeMulti { + cmd.PrintErrln("Offline key passed in. Use `tx sign` command to sign.") + return authclient.PrintUnsignedStdTx(txBldr, clientCtx, []sdk.Msg{msg}) } // write the unsigned transaction to the buffer w := bytes.NewBuffer([]byte{}) - cliCtx = cliCtx.WithOutput(w) + clientCtx = clientCtx.WithOutput(w) - if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}); err != nil { + if err = authclient.PrintUnsignedStdTx(txBldr, clientCtx, []sdk.Msg{msg}); err != nil { return errors.Wrap(err, "failed to print unsigned std tx") } // read the transaction - stdTx, err := readUnsignedGenTxFile(cdc, w) + stdTx, err := readUnsignedGenTxFile(clientCtx, w) if err != nil { return errors.Wrap(err, "failed to read unsigned gen tx file") } // sign the transaction and write it to the output file - signedTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, false, true) + txBuilder, err := clientCtx.TxConfig.WrapTxBuilder(stdTx) + if err != nil { + return fmt.Errorf("error creating tx builder: %w", err) + } + + err = authclient.SignTx(txFactory, clientCtx, name, txBuilder, true, true) if err != nil { return errors.Wrap(err, "failed to sign std tx") } - // Fetch output file name - outputDocument := viper.GetString(flags.FlagOutputDocument) + outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument) if outputDocument == "" { outputDocument, err = makeOutputFilepath(config.RootDir, nodeID) if err != nil { @@ -171,26 +192,21 @@ func GenTxCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, sm } } - if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { + if err := writeSignedGenTx(clientCtx, outputDocument, stdTx); err != nil { return errors.Wrap(err, "failed to write signed gen tx") } - fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument) + cmd.PrintErrf("Genesis transaction written to %q\n", outputDocument) return nil - }, } - cmd.Flags().String(flags.FlagHome, defaultNodeHome, "node's home directory") - cmd.Flags().String(flagClientHome, defaultCLIHome, "client's home directory") - cmd.Flags().String(flags.FlagName, "", "name of private key with which to sign the gentx") - cmd.Flags().String(flags.FlagOutputDocument, "", - "write the genesis transaction JSON document to the given file instead of the default location") + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().String(flags.FlagOutputDocument, "", "Write the genesis transaction JSON document to the given file instead of the default location") + cmd.Flags().String(flags.FlagChainID, "", "The network chain ID") cmd.Flags().AddFlagSet(fsCreateValidator) - cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") - viper.BindPFlag(flags.FlagKeyringBackend, cmd.Flags().Lookup(flags.FlagKeyringBackend)) + flags.AddTxFlagsToCmd(cmd) - cmd.MarkFlagRequired(flags.FlagName) return cmd } @@ -199,31 +215,37 @@ func makeOutputFilepath(rootDir, nodeID string) (string, error) { if err := tmos.EnsureDir(writePath, 0700); err != nil { return "", err } + return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil } -func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) { - var stdTx auth.StdTx - bytes, err := ioutil.ReadAll(r) +func readUnsignedGenTxFile(clientCtx client.Context, r io.Reader) (sdk.Tx, error) { + bz, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + aTx, err := clientCtx.TxConfig.TxJSONDecoder()(bz) if err != nil { - return stdTx, err + return nil, err } - err = cdc.UnmarshalJSON(bytes, &stdTx) - return stdTx, err + + return aTx, err } -func writeSignedGenTx(cdc *codec.Codec, outputDocument string, tx auth.StdTx) error { +func writeSignedGenTx(clientCtx client.Context, outputDocument string, tx sdk.Tx) error { outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) if err != nil { return err } defer outputFile.Close() - json, err := cdc.MarshalJSON(tx) + + json, err := clientCtx.TxConfig.TxJSONEncoder()(tx) if err != nil { return err } + _, err = fmt.Fprintf(outputFile, "%s\n", json) + return err } - -// DONTCOVER diff --git a/x/genutil/client/cli/gentx_test.go b/x/genutil/client/cli/gentx_test.go new file mode 100644 index 000000000000..d15ee8896265 --- /dev/null +++ b/x/genutil/client/cli/gentx_test.go @@ -0,0 +1,97 @@ +package cli_test + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestGenTxCmd() { + val := s.network.Validators[0] + dir := s.T().TempDir() + + cmd := cli.GenTxCmd( + simapp.ModuleBasics, + val.ClientCtx.TxConfig, banktypes.GenesisBalancesIterator{}, val.ClientCtx.HomeDir) + + _, out := testutil.ApplyMockIO(cmd) + clientCtx := val.ClientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + + amount := sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(12)) + genTxFile := filepath.Join(dir, "myTx") + cmd.SetArgs([]string{ + fmt.Sprintf("--%s=%s", flags.FlagChainID, s.network.Config.ChainID), + fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, genTxFile), + val.Moniker, + amount.String(), + }) + + err := cmd.ExecuteContext(ctx) + s.Require().NoError(err) + + // Validate generated transaction. + open, err := os.Open(genTxFile) + s.Require().NoError(err) + + all, err := ioutil.ReadAll(open) + s.Require().NoError(err) + + tx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(all) + s.Require().NoError(err) + + msgs := tx.GetMsgs() + s.Require().Len(msgs, 1) + + s.Require().Equal(types.TypeMsgCreateValidator, msgs[0].Type()) + s.Require().Equal([]sdk.AccAddress{val.Address}, msgs[0].GetSigners()) + s.Require().Equal(amount, msgs[0].(*types.MsgCreateValidator).Value) + err = tx.ValidateBasic() + s.Require().NoError(err) +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/genutil/client/cli/init.go b/x/genutil/client/cli/init.go index b87691d1d0f2..074bb7da4e74 100644 --- a/x/genutil/client/cli/init.go +++ b/x/genutil/client/cli/init.go @@ -1,22 +1,24 @@ package cli import ( + "bufio" "encoding/json" "fmt" "os" "path/filepath" + "github.com/cosmos/go-bip39" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" tmos "github.com/tendermint/tendermint/libs/os" tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -24,8 +26,11 @@ import ( ) const ( - flagOverwrite = "overwrite" - flagClientHome = "home-client" + // FlagOverwrite defines a flag to overwrite an existing genesis JSON file. + FlagOverwrite = "overwrite" + + // FlagSeed defines a flag to initialize the private validator key from a specific seed. + FlagRecover = "recover" ) type printInfo struct { @@ -36,9 +41,7 @@ type printInfo struct { AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` } -func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, - appMessage json.RawMessage) printInfo { - +func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { return printInfo{ Moniker: moniker, ChainID: chainID, @@ -48,35 +51,55 @@ func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, } } -func displayInfo(cdc *codec.Codec, info printInfo) error { - out, err := codec.MarshalJSONIndent(cdc, info) +func displayInfo(info printInfo) error { + out, err := json.MarshalIndent(info, "", " ") if err != nil { return err } _, err = fmt.Fprintf(os.Stderr, "%s\n", string(sdk.MustSortJSON(out))) + return err } // InitCmd returns a command that initializes all files needed for Tendermint // and the respective application. -func InitCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, - defaultNodeHome string) *cobra.Command { // nolint: golint +func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "init [moniker]", Short: "Initialize private validator, p2p, genesis, and application configuration files", Long: `Initialize validators's and node's configuration files.`, Args: cobra.ExactArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + cdc := clientCtx.JSONMarshaler + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config - chainID := viper.GetString(flags.FlagChainID) + config.SetRoot(clientCtx.HomeDir) + + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) if chainID == "" { chainID = fmt.Sprintf("test-chain-%v", tmrand.Str(6)) } - nodeID, _, err := genutil.InitializeNodeValidatorFiles(config) + // Get bip39 mnemonic + var mnemonic string + recover, _ := cmd.Flags().GetBool(FlagRecover) + if recover { + inBuf := bufio.NewReader(cmd.InOrStdin()) + mnemonic, err := input.GetString("Enter your bip39 mnemonic", inBuf) + if err != nil { + return err + } + + if !bip39.IsMnemonicValid(mnemonic) { + return errors.New("invalid mnemonic") + } + } + + nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) if err != nil { return err } @@ -84,10 +107,12 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, config.Moniker = args[0] genFile := config.GenesisFile() - if !viper.GetBool(flagOverwrite) && tmos.FileExists(genFile) { + overwrite, _ := cmd.Flags().GetBool(FlagOverwrite) + + if !overwrite && tmos.FileExists(genFile) { return fmt.Errorf("genesis.json file already exists: %v", genFile) } - appState, err := codec.MarshalJSONIndent(cdc, mbm.DefaultGenesis()) + appState, err := json.MarshalIndent(mbm.DefaultGenesis(cdc), "", " ") if err != nil { return errors.Wrap(err, "Failed to marshall default genesis state") } @@ -114,12 +139,13 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager, toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - return displayInfo(cdc, toPrint) + return displayInfo(toPrint) }, } cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") - cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().Bool(FlagRecover, false, "provide seed phrase to recover existing key instead of creating") cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") return cmd diff --git a/x/genutil/client/cli/init_test.go b/x/genutil/client/cli/init_test.go index 6ab1ef208abe..80820052dfa1 100644 --- a/x/genutil/client/cli/init_test.go +++ b/x/genutil/client/cli/init_test.go @@ -1,7 +1,9 @@ -package cli +package cli_test import ( "bytes" + "context" + "fmt" "io" "os" "testing" @@ -9,70 +11,143 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - abciServer "github.com/tendermint/tendermint/abci/server" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + abci_server "github.com/tendermint/tendermint/abci/server" "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/mock" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/genutil" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" ) var testMbm = module.NewBasicManager(genutil.AppModuleBasic{}) func TestInitCmd(t *testing.T) { - defer server.SetupViper(t)() - defer setupClientHome(t)() - home, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) + tests := []struct { + name string + flags func(dir string) []string + shouldErr bool + err error + }{ + { + name: "happy path", + flags: func(dir string) []string { + return []string{ + "appnode-test", + } + }, + shouldErr: false, + + err: nil, + }, + } - ctx := server.NewContext(cfg, logger) - cdc := makeCodec() - cmd := InitCmd(ctx, cdc, testMbm, home) + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithJSONMarshaler(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := genutilcli.InitCmd(testMbm, home) + cmd.SetArgs( + tt.flags(home), + ) + + if tt.shouldErr { + err := cmd.ExecuteContext(ctx) + require.EqualError(t, err, tt.err.Error()) + } else { + require.NoError(t, cmd.ExecuteContext(ctx)) + } + }) + } - require.NoError(t, cmd.RunE(nil, []string{"appnode-test"})) } -func setupClientHome(t *testing.T) func() { - clientDir, cleanup := tests.NewTestCaseDir(t) - viper.Set(flagClientHome, clientDir) - return cleanup +func TestInitRecover(t *testing.T) { + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) + + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithJSONMarshaler(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd := genutilcli.InitCmd(testMbm, home) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + cmd.SetArgs([]string{ + "appnode-test", + fmt.Sprintf("--%s=true", genutilcli.FlagRecover), + }) + + // use valid mnemonic and complete recovery key generation successfully + mockIn.Reset("decide praise business actor peasant farm drastic weather extend front hurt later song give verb rhythm worry fun pond reform school tumble august one\n") + require.NoError(t, cmd.ExecuteContext(ctx)) } func TestEmptyState(t *testing.T) { - defer server.SetupViper(t)() - defer setupClientHome(t)() + home := t.TempDir() + logger := log.NewNopLogger() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) + require.NoError(t, err) - home, cleanup := tests.NewTestCaseDir(t) - defer cleanup() + serverCtx := server.NewContext(viper.New(), cfg, logger) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + clientCtx := client.Context{}. + WithJSONMarshaler(marshaler). + WithLegacyAmino(makeCodec()). + WithHomeDir(home) - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) - ctx := server.NewContext(cfg, logger) - cdc := makeCodec() + cmd := genutilcli.InitCmd(testMbm, home) + cmd.SetArgs([]string{"appnode-test", fmt.Sprintf("--%s=%s", cli.HomeFlag, home)}) - cmd := InitCmd(ctx, cdc, testMbm, home) - require.NoError(t, cmd.RunE(nil, []string{"appnode-test"})) + require.NoError(t, cmd.ExecuteContext(ctx)) old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w - cmd = server.ExportCmd(ctx, cdc, nil) - err = cmd.RunE(nil, nil) - require.NoError(t, err) + cmd = server.ExportCmd(nil, home) + cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", cli.HomeFlag, home)}) + require.NoError(t, cmd.ExecuteContext(ctx)) outC := make(chan string) go func() { @@ -93,52 +168,48 @@ func TestEmptyState(t *testing.T) { } func TestStartStandAlone(t *testing.T) { - home, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - viper.Set(cli.HomeFlag, home) - defer setupClientHome(t)() - + home := t.TempDir() logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - ctx := server.NewContext(cfg, logger) - cdc := makeCodec() - initCmd := InitCmd(ctx, cdc, testMbm, home) - require.NoError(t, initCmd.RunE(nil, []string{"appnode-test"})) + interfaceRegistry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + err := genutiltest.ExecInitCmd(testMbm, home, marshaler) + require.NoError(t, err) app, err := mock.NewApp(home, logger) - require.Nil(t, err) + require.NoError(t, err) + svrAddr, _, err := server.FreeTCPAddr() - require.Nil(t, err) - svr, err := abciServer.NewServer(svrAddr, "socket", app) - require.Nil(t, err, "error creating listener") + require.NoError(t, err) + + svr, err := abci_server.NewServer(svrAddr, "socket", app) + require.NoError(t, err, "error creating listener") + svr.SetLogger(logger.With("module", "abci-server")) - svr.Start() + err = svr.Start() + require.NoError(t, err) timer := time.NewTimer(time.Duration(2) * time.Second) for range timer.C { - svr.Stop() + err = svr.Stop() + require.NoError(t, err) break } } func TestInitNodeValidatorFiles(t *testing.T) { - home, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - viper.Set(cli.HomeFlag, home) - viper.Set(flags.FlagName, "moniker") - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) + home := t.TempDir() + cfg, err := genutiltest.CreateDefaultTendermintConfig(home) nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(cfg) + require.Nil(t, err) require.NotEqual(t, "", nodeID) require.NotEqual(t, 0, len(valPubKey.Bytes())) } // custom tx codec -func makeCodec() *codec.Codec { - var cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) +func makeCodec() *codec.LegacyAmino { + var cdc = codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) return cdc } diff --git a/x/genutil/client/cli/migrate.go b/x/genutil/client/cli/migrate.go index 3ae77c506d61..483f4aaf107d 100644 --- a/x/genutil/client/cli/migrate.go +++ b/x/genutil/client/cli/migrate.go @@ -1,38 +1,40 @@ package cli import ( + "encoding/json" "fmt" "sort" "time" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/tendermint/tendermint/types" + tmjson "github.com/tendermint/tendermint/libs/json" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" + "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" - extypes "github.com/cosmos/cosmos-sdk/x/genutil" - v036 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_36" - v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_38" + v036 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v036" + v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v038" + v039 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v039" + v040 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v040" + "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -const ( - flagGenesisTime = "genesis-time" - flagChainID = "chain-id" -) +const flagGenesisTime = "genesis-time" // Allow applications to extend and modify the migration process. // // Ref: https://github.com/cosmos/cosmos-sdk/issues/5041 -var migrationMap = extypes.MigrationMap{ +var migrationMap = types.MigrationMap{ "v0.36": v036.Migrate, "v0.38": v038.Migrate, // NOTE: v0.37 and v0.38 are genesis compatible + "v0.39": v039.Migrate, + "v0.40": v040.Migrate, } // GetMigrationCallback returns a MigrationCallback for a given version. -func GetMigrationCallback(version string) extypes.MigrationCallback { +func GetMigrationCallback(version string) types.MigrationCallback { return migrationMap[version] } @@ -41,18 +43,19 @@ func GetMigrationVersions() []string { versions := make([]string, len(migrationMap)) var i int + for version := range migrationMap { versions[i] = version i++ } sort.Strings(versions) + return versions } // MigrateGenesisCmd returns a command to execute genesis state migration. -// nolint: funlen -func MigrateGenesisCmd(_ *server.Context, cdc *codec.Codec) *cobra.Command { +func MigrateGenesisCmd() *cobra.Command { cmd := &cobra.Command{ Use: "migrate [target-version] [genesis-file]", Short: "Migrate genesis to a specified target version", @@ -60,21 +63,31 @@ func MigrateGenesisCmd(_ *server.Context, cdc *codec.Codec) *cobra.Command { Example: $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2019-04-22T17:00:00Z -`, version.ServerName), +`, version.AppName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + var err error target := args[0] importGenesis := args[1] - genDoc, err := types.GenesisDocFromFile(importGenesis) + genDoc, err := validateGenDoc(importGenesis) if err != nil { - return errors.Wrapf(err, "failed to read genesis document from file %s", importGenesis) + return err + } + + // Since some default values are valid values, we just print to + // make sure the user didn't forget to update these values. + if genDoc.ConsensusParams.Evidence.MaxBytes == 0 { + fmt.Printf("Warning: consensus_params.evidence.max_bytes is set to 0. If this is"+ + " deliberate, feel free to ignore this warning. If not, please have a look at the chain"+ + " upgrade guide at %s.\n", chainUpgradeGuide) } - var initialState extypes.AppMap - if err := cdc.UnmarshalJSON(genDoc.AppState, &initialState); err != nil { + var initialState types.AppMap + if err := json.Unmarshal(genDoc.AppState, &initialState); err != nil { return errors.Wrap(err, "failed to JSON unmarshal initial genesis state") } @@ -84,14 +97,14 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 } // TODO: handler error from migrationFunc call - newGenState := migrationFunc(initialState) + newGenState := migrationFunc(initialState, clientCtx) - genDoc.AppState, err = cdc.MarshalJSON(newGenState) + genDoc.AppState, err = json.Marshal(newGenState) if err != nil { return errors.Wrap(err, "failed to JSON marshal migrated genesis state") } - genesisTime := cmd.Flag(flagGenesisTime).Value.String() + genesisTime, _ := cmd.Flags().GetString(flagGenesisTime) if genesisTime != "" { var t time.Time @@ -103,12 +116,12 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 genDoc.GenesisTime = t } - chainID := cmd.Flag(flagChainID).Value.String() + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) if chainID != "" { genDoc.ChainID = chainID } - bz, err := cdc.MarshalJSONIndent(genDoc, "", " ") + bz, err := tmjson.Marshal(genDoc) if err != nil { return errors.Wrap(err, "failed to marshal genesis doc") } @@ -124,7 +137,7 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 } cmd.Flags().String(flagGenesisTime, "", "override genesis_time with this flag") - cmd.Flags().String(flagChainID, "", "override chain_id with this flag") + cmd.Flags().String(flags.FlagChainID, "", "override chain_id with this flag") return cmd } diff --git a/x/genutil/client/cli/migrate_test.go b/x/genutil/client/cli/migrate_test.go index 423629efcb13..d31f8962a958 100644 --- a/x/genutil/client/cli/migrate_test.go +++ b/x/genutil/client/cli/migrate_test.go @@ -1,64 +1,60 @@ -package cli +package cli_test import ( - "io/ioutil" - "path" "testing" - "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/stretchr/testify/require" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - "github.com/tendermint/tendermint/libs/cli" - "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" ) -func setupCmd(genesisTime string, chainID string) *cobra.Command { - c := &cobra.Command{ - Use: "c", - Args: cobra.ArbitraryArgs, - Run: func(_ *cobra.Command, args []string) {}, - } - - c.Flags().String(flagGenesisTime, genesisTime, "") - c.Flags().String(flagChainID, chainID, "") - - return c -} - func TestGetMigrationCallback(t *testing.T) { - for _, version := range GetMigrationVersions() { - require.NotNil(t, GetMigrationCallback(version)) + for _, version := range cli.GetMigrationVersions() { + require.NotNil(t, cli.GetMigrationCallback(version)) } } -func TestMigrateGenesis(t *testing.T) { - home, cleanup := tests.NewTestCaseDir(t) - viper.Set(cli.HomeFlag, home) - viper.Set(flags.FlagName, "moniker") - logger := log.NewNopLogger() - cfg, err := tcmd.ParseConfig() - require.Nil(t, err) - ctx := server.NewContext(cfg, logger) - cdc := makeCodec() - - genesisPath := path.Join(home, "genesis.json") - target := "v0.36" - - defer cleanup() - - // Reject if we dont' have the right parameters or genesis does not exists - require.Error(t, MigrateGenesisCmd(ctx, cdc).RunE(nil, []string{target, genesisPath})) +func (s *IntegrationTestSuite) TestMigrateGenesis() { + val0 := s.network.Validators[0] + + testCases := []struct { + name string + genesis string + target string + expErr bool + expErrMsg string + }{ + { + "migrate to 0.36", + `{"chain_id":"test","app_state":{}}`, + "v0.36", + false, "", + }, + { + "exported 0.37 genesis file", + v037Exported, + "v0.40", + true, "Make sure that you have correctly migrated all Tendermint consensus params", + }, + { + "valid 0.40 genesis file", + v040Valid, + "v0.40", + false, "", + }, + } - // Noop migration with minimal genesis - emptyGenesis := []byte(`{"chain_id":"test","app_state":{}}`) - err = ioutil.WriteFile(genesisPath, emptyGenesis, 0644) - require.Nil(t, err) - cmd := setupCmd("", "test2") - require.NoError(t, MigrateGenesisCmd(ctx, cdc).RunE(cmd, []string{target, genesisPath})) - // Every migration function shuold tests its own module separately + for _, tc := range testCases { + s.Run(tc.name, func() { + genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) + _, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cli.MigrateGenesisCmd(), []string{tc.target, genesisFile.Name()}) + if tc.expErr { + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + } + }) + } } diff --git a/x/genutil/client/cli/validate_genesis.go b/x/genutil/client/cli/validate_genesis.go index 6be33bdabf09..0514cc66fd2f 100644 --- a/x/genutil/client/cli/validate_genesis.go +++ b/x/genutil/client/cli/validate_genesis.go @@ -3,52 +3,69 @@ package cli import ( "encoding/json" "fmt" - "os" "github.com/spf13/cobra" tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/types/module" ) -// Validate genesis command takes -func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec, mbm module.BasicManager) *cobra.Command { +const chainUpgradeGuide = "https://docs.cosmos.network/master/migrations/chain-upgrade-guide-040.html" + +// ValidateGenesisCmd takes a genesis file, and makes sure that it is valid. +func ValidateGenesisCmd(mbm module.BasicManager) *cobra.Command { return &cobra.Command{ Use: "validate-genesis [file]", Args: cobra.RangeArgs(0, 1), Short: "validates the genesis file at the default location or at the location passed as an arg", RunE: func(cmd *cobra.Command, args []string) (err error) { + serverCtx := server.GetServerContextFromCmd(cmd) + clientCtx := client.GetClientContextFromCmd(cmd) + + cdc := clientCtx.JSONMarshaler // Load default if passed no args, otherwise load passed file var genesis string if len(args) == 0 { - genesis = ctx.Config.GenesisFile() + genesis = serverCtx.Config.GenesisFile() } else { genesis = args[0] } - fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis) - - var genDoc *tmtypes.GenesisDoc - if genDoc, err = tmtypes.GenesisDocFromFile(genesis); err != nil { - return fmt.Errorf("error loading genesis doc from %s: %s", genesis, err.Error()) + genDoc, err := validateGenDoc(genesis) + if err != nil { + return err } var genState map[string]json.RawMessage - if err = cdc.UnmarshalJSON(genDoc.AppState, &genState); err != nil { + if err = json.Unmarshal(genDoc.AppState, &genState); err != nil { return fmt.Errorf("error unmarshalling genesis doc %s: %s", genesis, err.Error()) } - if err = mbm.ValidateGenesis(genState); err != nil { + if err = mbm.ValidateGenesis(cdc, clientCtx.TxConfig, genState); err != nil { return fmt.Errorf("error validating genesis file %s: %s", genesis, err.Error()) } - // TODO test to make sure initchain doesn't panic - fmt.Printf("File at %s is a valid genesis file\n", genesis) return nil }, } } + +// validateGenDoc reads a genesis file and validates that it is a correct +// Tendermint GenesisDoc. This function does not do any cosmos-related +// validation. +func validateGenDoc(importGenesisFile string) (*tmtypes.GenesisDoc, error) { + genDoc, err := tmtypes.GenesisDocFromFile(importGenesisFile) + if err != nil { + return nil, fmt.Errorf("%s. Make sure that"+ + " you have correctly migrated all Tendermint consensus params, please see the"+ + " chain migration guide at %s for more info", + err.Error(), chainUpgradeGuide, + ) + } + + return genDoc, nil +} diff --git a/x/genutil/client/cli/validate_genesis_test.go b/x/genutil/client/cli/validate_genesis_test.go new file mode 100644 index 000000000000..f62aa1b7a48e --- /dev/null +++ b/x/genutil/client/cli/validate_genesis_test.go @@ -0,0 +1,82 @@ +package cli_test + +import ( + "github.com/cosmos/cosmos-sdk/testutil" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" +) + +// An example exported genesis file from a 0.37 chain. Note that evidence +// parameters only contains `max_age`. +var v037Exported = `{ + "app_hash": "", + "app_state": {}, + "chain_id": "test", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { "max_age": "100000" }, + "validator": { "pub_key_types": ["ed25519"] } + }, + "genesis_time": "2020-09-29T20:16:29.172362037Z", + "validators": [] +}` + +// An example exported genesis file that's 0.40 compatible. +var v040Valid = `{ + "app_hash": "", + "app_state": {}, + "chain_id": "test", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000", + "max_bytes": "0" + }, + "validator": { "pub_key_types": ["ed25519"] } + }, + "genesis_time": "2020-09-29T20:16:29.172362037Z", + "validators": [] +}` + +func (s *IntegrationTestSuite) TestValidateGenesis() { + val0 := s.network.Validators[0] + + testCases := []struct { + name string + genesis string + expErr bool + }{ + { + "exported 0.37 genesis file", + v037Exported, + true, + }, + { + "valid 0.40 genesis file", + v040Valid, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + genesisFile := testutil.WriteToNewTempFile(s.T(), tc.genesis) + _, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cli.ValidateGenesisCmd(nil), []string{genesisFile.Name()}) + if tc.expErr { + s.Require().Contains(err.Error(), "Make sure that you have correctly migrated all Tendermint consensus params") + + } else { + s.Require().NoError(err) + } + }) + } +} diff --git a/x/genutil/client/rest/query.go b/x/genutil/client/rest/query.go index 0159223e8e7c..dddd2b75a558 100644 --- a/x/genutil/client/rest/query.go +++ b/x/genutil/client/rest/query.go @@ -1,10 +1,11 @@ package rest import ( + "context" "fmt" "net/http" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -12,8 +13,8 @@ import ( // QueryGenesisTxs writes the genesis transactions to the response if no error // occurs. -func QueryGenesisTxs(cliCtx context.CLIContext, w http.ResponseWriter) { - resultGenesis, err := cliCtx.Client.Genesis() +func QueryGenesisTxs(clientCtx client.Context, w http.ResponseWriter) { + resultGenesis, err := clientCtx.Client.Genesis(context.Background()) if err != nil { rest.WriteErrorResponse( w, http.StatusInternalServerError, @@ -22,7 +23,7 @@ func QueryGenesisTxs(cliCtx context.CLIContext, w http.ResponseWriter) { return } - appState, err := types.GenesisStateFromGenDoc(cliCtx.Codec, *resultGenesis.Genesis) + appState, err := types.GenesisStateFromGenDoc(*resultGenesis.Genesis) if err != nil { rest.WriteErrorResponse( w, http.StatusInternalServerError, @@ -31,10 +32,10 @@ func QueryGenesisTxs(cliCtx context.CLIContext, w http.ResponseWriter) { return } - genState := types.GetGenesisStateFromAppState(cliCtx.Codec, appState) + genState := types.GetGenesisStateFromAppState(clientCtx.JSONMarshaler, appState) genTxs := make([]sdk.Tx, len(genState.GenTxs)) for i, tx := range genState.GenTxs { - err := cliCtx.Codec.UnmarshalJSON(tx, &genTxs[i]) + err := clientCtx.LegacyAmino.UnmarshalJSON(tx, &genTxs[i]) if err != nil { rest.WriteErrorResponse( w, http.StatusInternalServerError, @@ -44,5 +45,5 @@ func QueryGenesisTxs(cliCtx context.CLIContext, w http.ResponseWriter) { } } - rest.PostProcessResponseBare(w, cliCtx, genTxs) + rest.PostProcessResponseBare(w, clientCtx, genTxs) } diff --git a/x/genutil/client/testutil/helpers.go b/x/genutil/client/testutil/helpers.go new file mode 100644 index 000000000000..1fc4affb7d42 --- /dev/null +++ b/x/genutil/client/testutil/helpers.go @@ -0,0 +1,53 @@ +package testutil + +import ( + "context" + "fmt" + + "github.com/spf13/viper" + tmcfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/types/module" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" +) + +func ExecInitCmd(testMbm module.BasicManager, home string, cdc codec.JSONMarshaler) error { + logger := log.NewNopLogger() + cfg, err := CreateDefaultTendermintConfig(home) + if err != nil { + return err + } + + cmd := genutilcli.InitCmd(testMbm, home) + serverCtx := server.NewContext(viper.New(), cfg, logger) + clientCtx := client.Context{}.WithJSONMarshaler(cdc).WithHomeDir(home) + + _, out := testutil.ApplyMockIO(cmd) + clientCtx = clientCtx.WithOutput(out) + + ctx := context.Background() + ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) + ctx = context.WithValue(ctx, server.ServerContextKey, serverCtx) + + cmd.SetArgs([]string{"appnode-test", fmt.Sprintf("--%s=%s", cli.HomeFlag, home)}) + + return cmd.ExecuteContext(ctx) +} + +func CreateDefaultTendermintConfig(rootDir string) (*tmcfg.Config, error) { + conf := tmcfg.DefaultConfig() + conf.SetRoot(rootDir) + tmcfg.EnsureRoot(rootDir) + + if err := conf.ValidateBasic(); err != nil { + return nil, fmt.Errorf("error in config file: %v", err) + } + + return conf, nil +} diff --git a/x/genutil/collect.go b/x/genutil/collect.go index 7145efb048a6..ea548a5d3de1 100644 --- a/x/genutil/collect.go +++ b/x/genutil/collect.go @@ -9,29 +9,30 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "sort" "strings" cfg "github.com/tendermint/tendermint/config" tmtypes "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // GenAppStateFromConfig gets the genesis app state from the config -func GenAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, - initCfg InitConfig, genDoc tmtypes.GenesisDoc, - genAccIterator types.GenesisAccountsIterator, +func GenAppStateFromConfig(cdc codec.JSONMarshaler, txEncodingConfig client.TxEncodingConfig, + config *cfg.Config, initCfg types.InitConfig, genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator, ) (appState json.RawMessage, err error) { // process genesis transactions, else create default genesis.json - appGenTxs, persistentPeers, err := CollectStdTxs( - cdc, config.Moniker, initCfg.GenTxsDir, genDoc, genAccIterator) + appGenTxs, persistentPeers, err := CollectTxs( + cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator, + ) if err != nil { return appState, err } @@ -45,30 +46,38 @@ func GenAppStateFromConfig(cdc *codec.Codec, config *cfg.Config, } // create the app state - appGenesisState, err := GenesisStateFromGenDoc(cdc, genDoc) + appGenesisState, err := types.GenesisStateFromGenDoc(genDoc) if err != nil { return appState, err } - appGenesisState, err = SetGenTxsInAppGenesisState(cdc, appGenesisState, appGenTxs) + appGenesisState, err = SetGenTxsInAppGenesisState(cdc, txEncodingConfig.TxJSONEncoder(), appGenesisState, appGenTxs) if err != nil { return appState, err } - appState, err = codec.MarshalJSONIndent(cdc, appGenesisState) + + appState, err = json.MarshalIndent(appGenesisState, "", " ") if err != nil { return appState, err } genDoc.AppState = appState err = ExportGenesisFile(&genDoc, config.GenesisFile()) + return appState, err } -// CollectStdTxs processes and validates application's genesis StdTxs and returns +// CollectTxs processes and validates application's genesis Txs and returns // the list of appGenTxs, and persistent peers required to generate genesis.json. -func CollectStdTxs(cdc *codec.Codec, moniker, genTxsDir string, - genDoc tmtypes.GenesisDoc, genAccIterator types.GenesisAccountsIterator, -) (appGenTxs []authtypes.StdTx, persistentPeers string, err error) { +func CollectTxs(cdc codec.JSONMarshaler, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string, + genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator, +) (appGenTxs []sdk.Tx, persistentPeers string, err error) { + // prepare a map of all balances in genesis state to then validate + // against the validators addresses + var appState map[string]json.RawMessage + if err := json.Unmarshal(genDoc.AppState, &appState); err != nil { + return appGenTxs, persistentPeers, err + } var fos []os.FileInfo fos, err = ioutil.ReadDir(genTxsDir) @@ -76,17 +85,12 @@ func CollectStdTxs(cdc *codec.Codec, moniker, genTxsDir string, return appGenTxs, persistentPeers, err } - // prepare a map of all accounts in genesis state to then validate - // against the validators addresses - var appState map[string]json.RawMessage - if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil { - return appGenTxs, persistentPeers, err - } + balancesMap := make(map[string]bankexported.GenesisBalance) - addrMap := make(map[string]authexported.Account) - genAccIterator.IterateGenesisAccounts(cdc, appState, - func(acc authexported.Account) (stop bool) { - addrMap[acc.GetAddress().String()] = acc + genBalIterator.IterateGenesisBalances( + cdc, appState, + func(balance bankexported.GenesisBalance) (stop bool) { + balancesMap[balance.GetAddress().String()] = balance return false }, ) @@ -95,60 +99,78 @@ func CollectStdTxs(cdc *codec.Codec, moniker, genTxsDir string, var addressesIPs []string for _, fo := range fos { - filename := filepath.Join(genTxsDir, fo.Name()) - if !fo.IsDir() && (filepath.Ext(filename) != ".json") { + if fo.IsDir() { + continue + } + if !strings.HasSuffix(fo.Name(), ".json") { continue } - // get the genStdTx - var jsonRawTx []byte - if jsonRawTx, err = ioutil.ReadFile(filename); err != nil { + // get the genTx + jsonRawTx, err := ioutil.ReadFile(filepath.Join(genTxsDir, fo.Name())) + if err != nil { return appGenTxs, persistentPeers, err } - var genStdTx authtypes.StdTx - if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil { + + var genTx sdk.Tx + if genTx, err = txJSONDecoder(jsonRawTx); err != nil { return appGenTxs, persistentPeers, err } - appGenTxs = append(appGenTxs, genStdTx) + + appGenTxs = append(appGenTxs, genTx) // the memo flag is used to store // the ip and node-id, for example this may be: // "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656" - nodeAddrIP := genStdTx.GetMemo() + + memoTx, ok := genTx.(sdk.TxWithMemo) + if !ok { + return appGenTxs, persistentPeers, fmt.Errorf("expected TxWithMemo, got %T", genTx) + } + nodeAddrIP := memoTx.GetMemo() if len(nodeAddrIP) == 0 { - return appGenTxs, persistentPeers, fmt.Errorf( - "couldn't find node's address and IP in %s", fo.Name()) + return appGenTxs, persistentPeers, fmt.Errorf("failed to find node's address and IP in %s", fo.Name()) } // genesis transactions must be single-message - msgs := genStdTx.GetMsgs() + msgs := genTx.GetMsgs() if len(msgs) != 1 { - return appGenTxs, persistentPeers, errors.New( - "each genesis transaction must provide a single genesis message") + return appGenTxs, persistentPeers, errors.New("each genesis transaction must provide a single genesis message") } // TODO abstract out staking message validation back to staking - msg := msgs[0].(stakingtypes.MsgCreateValidator) + msg := msgs[0].(*stakingtypes.MsgCreateValidator) + // validate delegator and validator addresses and funds against the accounts in the state - delAddr := msg.DelegatorAddress.String() - valAddr := sdk.AccAddress(msg.ValidatorAddress).String() + delAddr := msg.DelegatorAddress + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return appGenTxs, persistentPeers, err + } - delAcc, delOk := addrMap[delAddr] + delBal, delOk := balancesMap[delAddr] if !delOk { - return appGenTxs, persistentPeers, fmt.Errorf( - "account %v not in genesis.json: %+v", delAddr, addrMap) + _, file, no, ok := runtime.Caller(1) + if ok { + fmt.Printf("CollectTxs-1, called from %s#%d\n", file, no) + } + + return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", delAddr, balancesMap) } - _, valOk := addrMap[valAddr] + _, valOk := balancesMap[sdk.AccAddress(valAddr).String()] if !valOk { - return appGenTxs, persistentPeers, fmt.Errorf( - "account %v not in genesis.json: %+v", valAddr, addrMap) + _, file, no, ok := runtime.Caller(1) + if ok { + fmt.Printf("CollectTxs-2, called from %s#%d - %s\n", file, no, sdk.AccAddress(msg.ValidatorAddress).String()) + } + return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", valAddr, balancesMap) } - if delAcc.GetCoins().AmountOf(msg.Value.Denom).LT(msg.Value.Amount) { + if delBal.GetCoins().AmountOf(msg.Value.Denom).LT(msg.Value.Amount) { return appGenTxs, persistentPeers, fmt.Errorf( "insufficient fund for delegation %v: %v < %v", - delAcc.GetAddress(), delAcc.GetCoins().AmountOf(msg.Value.Denom), msg.Value.Amount, + delBal.GetAddress().String(), delBal.GetCoins().AmountOf(msg.Value.Denom), msg.Value.Amount, ) } diff --git a/x/genutil/collect_test.go b/x/genutil/collect_test.go new file mode 100644 index 000000000000..66244cf74448 --- /dev/null +++ b/x/genutil/collect_test.go @@ -0,0 +1,68 @@ +package genutil_test + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/gogo/protobuf/proto" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/types" + bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" + "github.com/cosmos/cosmos-sdk/x/genutil" + gtypes "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +type doNothingUnmarshalJSON struct { + codec.JSONMarshaler +} + +func (dnj *doNothingUnmarshalJSON) UnmarshalJSON(_ []byte, _ proto.Message) error { + return nil +} + +type doNothingIterator struct { + gtypes.GenesisBalancesIterator +} + +func (dni *doNothingIterator) IterateGenesisBalances(_ codec.JSONMarshaler, _ map[string]json.RawMessage, _ func(bankexported.GenesisBalance) bool) { +} + +// Ensures that CollectTx correctly traverses directories and won't error out on encountering +// a directory during traversal of the first level. See issue https://github.com/cosmos/cosmos-sdk/issues/6788. +func TestCollectTxsHandlesDirectories(t *testing.T) { + testDir, err := ioutil.TempDir(os.TempDir(), "testCollectTxs") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(testDir) + + // 1. We'll insert a directory as the first element before JSON file. + subDirPath := filepath.Join(testDir, "_adir") + if err := os.MkdirAll(subDirPath, 0755); err != nil { + t.Fatal(err) + } + + txDecoder := types.TxDecoder(func(txBytes []byte) (types.Tx, error) { + return nil, nil + }) + + // 2. Ensure that we don't encounter any error traversing the directory. + srvCtx := server.NewDefaultContext() + _ = srvCtx + cdc := codec.NewProtoCodec(cdctypes.NewInterfaceRegistry()) + gdoc := tmtypes.GenesisDoc{AppState: []byte("{}")} + balItr := new(doNothingIterator) + + dnc := &doNothingUnmarshalJSON{cdc} + if _, _, err := genutil.CollectTxs(dnc, txDecoder, "foo", testDir, gdoc, balItr); err != nil { + t.Fatal(err) + } +} diff --git a/x/genutil/genesis.go b/x/genutil/genesis.go index c8564bdc4e2d..c6d17edb8da9 100644 --- a/x/genutil/genesis.go +++ b/x/genutil/genesis.go @@ -3,21 +3,19 @@ package genutil import ( abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/genutil/types" ) // InitGenesis - initialize accounts and deliver genesis transactions func InitGenesis( - ctx sdk.Context, cdc *codec.Codec, stakingKeeper types.StakingKeeper, - deliverTx deliverTxfn, genesisState GenesisState, -) []abci.ValidatorUpdate { - - var validators []abci.ValidatorUpdate + ctx sdk.Context, stakingKeeper types.StakingKeeper, + deliverTx deliverTxfn, genesisState types.GenesisState, + txEncodingConfig client.TxEncodingConfig, +) (validators []abci.ValidatorUpdate, err error) { if len(genesisState.GenTxs) > 0 { - validators = DeliverGenTxs(ctx, cdc, genesisState.GenTxs, stakingKeeper, deliverTx) + validators, err = DeliverGenTxs(ctx, genesisState.GenTxs, stakingKeeper, deliverTx, txEncodingConfig) } - - return validators + return } diff --git a/x/genutil/gentx.go b/x/genutil/gentx.go index 1ac4e64ed4c6..b5415ffb31f2 100644 --- a/x/genutil/gentx.go +++ b/x/genutil/gentx.go @@ -1,87 +1,86 @@ package genutil -// DONTCOVER - import ( "encoding/json" "fmt" abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" "github.com/cosmos/cosmos-sdk/x/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // SetGenTxsInAppGenesisState - sets the genesis transactions in the app genesis state -func SetGenTxsInAppGenesisState(cdc *codec.Codec, appGenesisState map[string]json.RawMessage, - genTxs []authtypes.StdTx) (map[string]json.RawMessage, error) { +func SetGenTxsInAppGenesisState( + cdc codec.JSONMarshaler, txJSONEncoder sdk.TxEncoder, appGenesisState map[string]json.RawMessage, genTxs []sdk.Tx, +) (map[string]json.RawMessage, error) { - genesisState := GetGenesisStateFromAppState(cdc, appGenesisState) - // convert all the GenTxs to JSON + genesisState := types.GetGenesisStateFromAppState(cdc, appGenesisState) genTxsBz := make([]json.RawMessage, 0, len(genTxs)) + for _, genTx := range genTxs { - txBz, err := cdc.MarshalJSON(genTx) + txBz, err := txJSONEncoder(genTx) if err != nil { return appGenesisState, err } + genTxsBz = append(genTxsBz, txBz) } genesisState.GenTxs = genTxsBz - return SetGenesisStateInAppState(cdc, appGenesisState, genesisState), nil + return types.SetGenesisStateInAppState(cdc, appGenesisState, genesisState), nil } -// ValidateAccountInGenesis checks that the provided key has sufficient -// coins in the genesis accounts -func ValidateAccountInGenesis(appGenesisState map[string]json.RawMessage, - genAccIterator types.GenesisAccountsIterator, - key sdk.Address, coins sdk.Coins, cdc *codec.Codec) error { - - accountIsInGenesis := false +// ValidateAccountInGenesis checks that the provided account has a sufficient +// balance in the set of genesis accounts. +func ValidateAccountInGenesis( + appGenesisState map[string]json.RawMessage, genBalIterator types.GenesisBalancesIterator, + addr sdk.Address, coins sdk.Coins, cdc codec.JSONMarshaler, +) error { - // TODO: refactor out bond denom to common state area - stakingDataBz := appGenesisState[stakingtypes.ModuleName] var stakingData stakingtypes.GenesisState - cdc.MustUnmarshalJSON(stakingDataBz, &stakingData) + cdc.MustUnmarshalJSON(appGenesisState[stakingtypes.ModuleName], &stakingData) bondDenom := stakingData.Params.BondDenom - genUtilDataBz := appGenesisState[stakingtypes.ModuleName] - var genesisState GenesisState - cdc.MustUnmarshalJSON(genUtilDataBz, &genesisState) - var err error - genAccIterator.IterateGenesisAccounts(cdc, appGenesisState, - func(acc authexported.Account) (stop bool) { - accAddress := acc.GetAddress() - accCoins := acc.GetCoins() - // Ensure that account is in genesis - if accAddress.Equals(key) { + accountIsInGenesis := false + + genBalIterator.IterateGenesisBalances(cdc, appGenesisState, + func(bal bankexported.GenesisBalance) (stop bool) { + accAddress := bal.GetAddress() + accCoins := bal.GetCoins() - // Ensure account contains enough funds of default bond denom + // ensure that account is in genesis + if accAddress.Equals(addr) { + // ensure account contains enough funds of default bond denom if coins.AmountOf(bondDenom).GT(accCoins.AmountOf(bondDenom)) { err = fmt.Errorf( - "account %v is in genesis, but it only has %v%v available to stake, not %v%v", - key.String(), accCoins.AmountOf(bondDenom), bondDenom, coins.AmountOf(bondDenom), bondDenom, + "account %s has a balance in genesis, but it only has %v%s available to stake, not %v%s", + addr, accCoins.AmountOf(bondDenom), bondDenom, coins.AmountOf(bondDenom), bondDenom, ) + return true } + accountIsInGenesis = true return true } + return false }, ) + if err != nil { return err } if !accountIsInGenesis { - return fmt.Errorf("account %s in not in the app_state.accounts array of genesis.json", key) + return fmt.Errorf("account %s does not have a balance in the genesis state", addr) } return nil @@ -89,18 +88,31 @@ func ValidateAccountInGenesis(appGenesisState map[string]json.RawMessage, type deliverTxfn func(abci.RequestDeliverTx) abci.ResponseDeliverTx -// DeliverGenTxs - deliver a genesis transaction -func DeliverGenTxs(ctx sdk.Context, cdc *codec.Codec, genTxs []json.RawMessage, - stakingKeeper types.StakingKeeper, deliverTx deliverTxfn) []abci.ValidatorUpdate { +// DeliverGenTxs iterates over all genesis txs, decodes each into a Tx and +// invokes the provided deliverTxfn with the decoded Tx. It returns the result +// of the staking module's ApplyAndReturnValidatorSetUpdates. +func DeliverGenTxs( + ctx sdk.Context, genTxs []json.RawMessage, + stakingKeeper types.StakingKeeper, deliverTx deliverTxfn, + txEncodingConfig client.TxEncodingConfig, +) ([]abci.ValidatorUpdate, error) { for _, genTx := range genTxs { - var tx authtypes.StdTx - cdc.MustUnmarshalJSON(genTx, &tx) - bz := cdc.MustMarshalBinaryLengthPrefixed(tx) + tx, err := txEncodingConfig.TxJSONDecoder()(genTx) + if err != nil { + panic(err) + } + + bz, err := txEncodingConfig.TxEncoder()(tx) + if err != nil { + panic(err) + } + res := deliverTx(abci.RequestDeliverTx{Tx: bz}) if !res.IsOK() { panic(res.Log) } } + return stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } diff --git a/x/genutil/gentx_test.go b/x/genutil/gentx_test.go index ea03ce733ce9..b4b843bc986e 100644 --- a/x/genutil/gentx_test.go +++ b/x/genutil/gentx_test.go @@ -1,14 +1,286 @@ -package genutil +package genutil_test -import "testing" +import ( + "encoding/json" + "fmt" + "testing" -func TestGenTx(t *testing.T) { + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - // TODO test that key overwrite flags work / no overwrites if set off - // TODO test validator created has provided pubkey - // TODO test the account created has the correct pubkey + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) - // TODO test must provide at least genesis transaction - // TODO test with both one and two genesis transactions: - // TODO correct: genesis account created, canididates created, pool token variance +var ( + priv1 = secp256k1.GenPrivKey() + priv2 = secp256k1.GenPrivKey() + pk1 = priv1.PubKey() + pk2 = priv2.PubKey() + addr1 = sdk.AccAddress(pk1.Address()) + addr2 = sdk.AccAddress(pk2.Address()) + desc = stakingtypes.NewDescription("testname", "", "", "", "") + comm = stakingtypes.CommissionRates{} +) + +// GenTxTestSuite is a test suite to be used with gentx tests. +type GenTxTestSuite struct { + suite.Suite + + ctx sdk.Context + app *simapp.SimApp + encodingConfig simappparams.EncodingConfig + + msg1, msg2 *stakingtypes.MsgCreateValidator +} + +func (suite *GenTxTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{}) + suite.app = app + suite.encodingConfig = simapp.MakeTestEncodingConfig() + + var err error + amount := sdk.NewInt64Coin(sdk.DefaultBondDenom, 50) + one := sdk.OneInt() + suite.msg1, err = stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(pk1.Address()), pk1, amount, desc, comm, one) + suite.NoError(err) + suite.msg2, err = stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(pk2.Address()), pk1, amount, desc, comm, one) + suite.NoError(err) +} + +func (suite *GenTxTestSuite) setAccountBalance(addr sdk.AccAddress, amount int64) json.RawMessage { + 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)}, + ) + suite.Require().NoError(err) + + bankGenesisState := suite.app.BankKeeper.ExportGenesis(suite.ctx) + bankGenesis, err := suite.encodingConfig.Amino.MarshalJSON(bankGenesisState) // TODO switch this to use Marshaler + suite.Require().NoError(err) + + return bankGenesis +} + +func (suite *GenTxTestSuite) TestSetGenTxsInAppGenesisState() { + var ( + txBuilder = suite.encodingConfig.TxConfig.NewTxBuilder() + genTxs []sdk.Tx + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "one genesis transaction", + func() { + err := txBuilder.SetMsgs(suite.msg1) + suite.Require().NoError(err) + tx := txBuilder.GetTx() + genTxs = []sdk.Tx{tx} + }, + true, + }, + { + "two genesis transactions", + func() { + err := txBuilder.SetMsgs(suite.msg1, suite.msg2) + suite.Require().NoError(err) + tx := txBuilder.GetTx() + genTxs = []sdk.Tx{tx} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + cdc := suite.encodingConfig.Marshaler + txJSONEncoder := suite.encodingConfig.TxConfig.TxJSONEncoder() + + tc.malleate() + appGenesisState, err := genutil.SetGenTxsInAppGenesisState(cdc, txJSONEncoder, make(map[string]json.RawMessage), genTxs) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(appGenesisState[types.ModuleName]) + + var genesisState types.GenesisState + err := cdc.UnmarshalJSON(appGenesisState[types.ModuleName], &genesisState) + suite.Require().NoError(err) + suite.Require().NotNil(genesisState.GenTxs) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *GenTxTestSuite) TestValidateAccountInGenesis() { + var ( + appGenesisState = make(map[string]json.RawMessage) + coins sdk.Coins + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "no accounts", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)} + }, + false, + }, + { + "account without balance in the genesis state", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)} + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance(addr2, 50) + }, + false, + }, + { + "account without enough funds of default bond denom", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)} + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance(addr1, 25) + }, + false, + }, + { + "account with enough funds of default bond denom", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)} + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance(addr1, 25) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + cdc := suite.encodingConfig.Marshaler + + suite.app.StakingKeeper.SetParams(suite.ctx, stakingtypes.DefaultParams()) + stakingGenesisState := staking.ExportGenesis(suite.ctx, suite.app.StakingKeeper) + suite.Require().Equal(stakingGenesisState.Params, stakingtypes.DefaultParams()) + stakingGenesis, err := cdc.MarshalJSON(stakingGenesisState) // TODO switch this to use Marshaler + suite.Require().NoError(err) + appGenesisState[stakingtypes.ModuleName] = stakingGenesis + + tc.malleate() + err = genutil.ValidateAccountInGenesis( + appGenesisState, banktypes.GenesisBalancesIterator{}, + addr1, coins, cdc, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + }) + } +} + +func (suite *GenTxTestSuite) TestDeliverGenTxs() { + var ( + genTxs []json.RawMessage + txBuilder = suite.encodingConfig.TxConfig.NewTxBuilder() + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "no signature supplied", + func() { + err := txBuilder.SetMsgs(suite.msg1) + suite.Require().NoError(err) + + genTxs = make([]json.RawMessage, 1) + tx, err := suite.encodingConfig.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + suite.Require().NoError(err) + genTxs[0] = tx + }, + false, + }, + { + "success", + func() { + _ = suite.setAccountBalance(addr1, 50) + _ = suite.setAccountBalance(addr2, 0) + + msg := banktypes.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 1)}) + tx, err := helpers.GenTx( + suite.encodingConfig.TxConfig, + []sdk.Msg{msg}, + sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)}, + helpers.DefaultGenTxGas, + suite.ctx.ChainID(), + []uint64{0}, + []uint64{0}, + priv1, + ) + suite.Require().NoError(err) + + genTxs = make([]json.RawMessage, 1) + genTx, err := suite.encodingConfig.TxConfig.TxJSONEncoder()(tx) + suite.Require().NoError(err) + genTxs[0] = genTx + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() + + if tc.expPass { + suite.Require().NotPanics(func() { + genutil.DeliverGenTxs( + suite.ctx, genTxs, suite.app.StakingKeeper, suite.app.BaseApp.DeliverTx, + suite.encodingConfig.TxConfig, + ) + }) + } else { + suite.Require().Panics(func() { + genutil.DeliverGenTxs( + suite.ctx, genTxs, suite.app.StakingKeeper, suite.app.BaseApp.DeliverTx, + suite.encodingConfig.TxConfig, + ) + }) + } + }) + } +} + +func TestGenTxTestSuite(t *testing.T) { + suite.Run(t, new(GenTxTestSuite)) } diff --git a/x/genutil/legacy/v036/migrate.go b/x/genutil/legacy/v036/migrate.go new file mode 100644 index 000000000000..204eebc233e3 --- /dev/null +++ b/x/genutil/legacy/v036/migrate.go @@ -0,0 +1,101 @@ +package v036 + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v034" + v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036" + v036bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v036" + v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v034genAccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v034" + v036genAccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" +) + +// Migrate migrates exported state from v0.34 to a v0.36 genesis state. +func Migrate(appState types.AppMap, _ client.Context) types.AppMap { + v034Codec := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(v034Codec) + v034gov.RegisterLegacyAminoCodec(v034Codec) + + v036Codec := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(v036Codec) + v036gov.RegisterLegacyAminoCodec(v036Codec) + v036distr.RegisterLegacyAminoCodec(v036Codec) + v036params.RegisterLegacyAminoCodec(v036Codec) + + // migrate genesis accounts state + if appState[v034genAccounts.ModuleName] != nil { + var genAccs v034genAccounts.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034genAccounts.ModuleName], &genAccs) + + var authGenState v034auth.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034auth.ModuleName], &authGenState) + + var govGenState v034gov.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034gov.ModuleName], &govGenState) + + var distrGenState v034distr.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034distr.ModuleName], &distrGenState) + + var stakingGenState v034staking.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034staking.ModuleName], &stakingGenState) + + delete(appState, v034genAccounts.ModuleName) // delete old key in case the name changed + appState[v036genAccounts.ModuleName] = v036Codec.MustMarshalJSON( + v036genAccounts.Migrate( + genAccs, authGenState.CollectedFees, distrGenState.FeePool.CommunityPool, govGenState.Deposits, + stakingGenState.Validators, stakingGenState.UnbondingDelegations, distrGenState.OutstandingRewards, + stakingGenState.Params.BondDenom, v036distr.ModuleName, v036gov.ModuleName, + ), + ) + } + + // migrate auth state + if appState[v034auth.ModuleName] != nil { + var authGenState v034auth.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034auth.ModuleName], &authGenState) + + delete(appState, v034auth.ModuleName) // delete old key in case the name changed + appState[v036auth.ModuleName] = v036Codec.MustMarshalJSON(v036auth.Migrate(authGenState)) + } + + // migrate gov state + if appState[v034gov.ModuleName] != nil { + var govGenState v034gov.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034gov.ModuleName], &govGenState) + + delete(appState, v034gov.ModuleName) // delete old key in case the name changed + appState[v036gov.ModuleName] = v036Codec.MustMarshalJSON(v036gov.Migrate(govGenState)) + } + + // migrate distribution state + if appState[v034distr.ModuleName] != nil { + var slashingGenState v034distr.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034distr.ModuleName], &slashingGenState) + + delete(appState, v034distr.ModuleName) // delete old key in case the name changed + appState[v036distr.ModuleName] = v036Codec.MustMarshalJSON(v036distr.Migrate(slashingGenState)) + } + + // migrate staking state + if appState[v034staking.ModuleName] != nil { + var stakingGenState v034staking.GenesisState + v034Codec.MustUnmarshalJSON(appState[v034staking.ModuleName], &stakingGenState) + + delete(appState, v034staking.ModuleName) // delete old key in case the name changed + appState[v036staking.ModuleName] = v036Codec.MustMarshalJSON(v036staking.Migrate(stakingGenState)) + } + + // migrate supply state + appState[v036bank.ModuleName] = v036Codec.MustMarshalJSON(v036bank.EmptyGenesisState()) + + return appState +} diff --git a/x/genutil/legacy/v036/migrate_test.go b/x/genutil/legacy/v036/migrate_test.go new file mode 100644 index 000000000000..bdd6c4f0ff55 --- /dev/null +++ b/x/genutil/legacy/v036/migrate_test.go @@ -0,0 +1,107 @@ +package v036 + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +var basic034Gov = []byte(` + { + "starting_proposal_id": "2", + "deposits": [ + { + "proposal_id": "1", + "deposit": { + "depositor": "cosmos1grgelyng2v6v3t8z87wu3sxgt9m5s03xvslewd", + "proposal_id": "1", + "amount": [ + { + "denom": "uatom", + "amount": "512000000" + } + ] + } + } + ], + "votes" : [ + { + "proposal_id": "1", + "vote": { + "voter": "cosmos1lktjhnzkpkz3ehrg8psvmwhafg56kfss5597tg", + "proposal_id": "1", + "option": "Yes" + } + } + ], + "proposals": [ + { + "proposal_content": { + "type": "gov/TextProposal", + "value": { + "title": "test", + "description": "test" + } + }, + "proposal_id": "1", + "proposal_status": "Passed", + "final_tally_result": { + "yes": "1", + "abstain": "0", + "no": "0", + "no_with_veto": "0" + }, + "submit_time": "2019-05-03T21:08:25.443199036Z", + "deposit_end_time": "2019-05-17T21:08:25.443199036Z", + "total_deposit": [ + { + "denom": "uatom", + "amount": "512000000" + } + ], + "voting_start_time": "2019-05-04T16:02:33.24680295Z", + "voting_end_time": "2019-05-18T16:02:33.24680295Z" + } + ], + "deposit_params": { + "min_deposit": [ + { + "denom": "uatom", + "amount": "512000000" + } + ], + "max_deposit_period": "1209600000000000" + }, + "voting_params": { + "voting_period": "1209600000000000" + }, + "tally_params": { + "quorum": "0.400000000000000000", + "threshold": "0.500000000000000000", + "veto": "0.334000000000000000" + } + } +`) + +func TestDummyGenesis(t *testing.T) { + genesisDummy := types.AppMap{ + "foo": {}, + "bar": []byte(`{"custom": "module"}`), + } + migratedDummy := Migrate(genesisDummy, client.Context{}) + + // We should not touch custom modules in the map + require.Equal(t, genesisDummy["foo"], migratedDummy["foo"]) + require.Equal(t, genesisDummy["bar"], migratedDummy["bar"]) +} + +func TestGovGenesis(t *testing.T) { + genesis := types.AppMap{ + "gov": basic034Gov, + } + + require.NotPanics(t, func() { Migrate(genesis, client.Context{}) }) +} diff --git a/x/genutil/legacy/v038/migrate.go b/x/genutil/legacy/v038/migrate.go new file mode 100644 index 000000000000..017d0e68fa53 --- /dev/null +++ b/x/genutil/legacy/v038/migrate.go @@ -0,0 +1,70 @@ +package v038 + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" + v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" + v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" +) + +// Migrate migrates exported state from v0.36/v0.37 to a v0.38 genesis state. +func Migrate(appState types.AppMap, _ client.Context) types.AppMap { + v036Codec := codec.NewLegacyAmino() + cryptocodec.RegisterCrypto(v036Codec) + v036gov.RegisterLegacyAminoCodec(v036Codec) + v036distr.RegisterLegacyAminoCodec(v036Codec) + v036params.RegisterLegacyAminoCodec(v036Codec) + + v038Codec := codec.NewLegacyAmino() + v038auth.RegisterLegacyAminoCodec(v038Codec) + v036gov.RegisterLegacyAminoCodec(v038Codec) + v036distr.RegisterLegacyAminoCodec(v038Codec) + v036params.RegisterLegacyAminoCodec(v038Codec) + v038upgrade.RegisterLegacyAminoCodec(v038Codec) + + if appState[v036genaccounts.ModuleName] != nil { + // unmarshal relative source genesis application state + var authGenState v036auth.GenesisState + v036Codec.MustUnmarshalJSON(appState[v036auth.ModuleName], &authGenState) + + var genAccountsGenState v036genaccounts.GenesisState + v036Codec.MustUnmarshalJSON(appState[v036genaccounts.ModuleName], &genAccountsGenState) + + // delete deprecated genaccounts genesis state + delete(appState, v036genaccounts.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v038auth.ModuleName] = v038Codec.MustMarshalJSON(v038auth.Migrate(authGenState, genAccountsGenState)) + } + + // migrate staking state + if appState[v036staking.ModuleName] != nil { + var stakingGenState v036staking.GenesisState + v036Codec.MustUnmarshalJSON(appState[v036staking.ModuleName], &stakingGenState) + + delete(appState, v036staking.ModuleName) // delete old key in case the name changed + appState[v038staking.ModuleName] = v038Codec.MustMarshalJSON(v038staking.Migrate(stakingGenState)) + } + + // migrate distribution state + if appState[v036distr.ModuleName] != nil { + var distrGenState v036distr.GenesisState + v036Codec.MustUnmarshalJSON(appState[v036distr.ModuleName], &distrGenState) + + delete(appState, v036distr.ModuleName) // delete old key in case the name changed + appState[v038distr.ModuleName] = v038Codec.MustMarshalJSON(v038distr.Migrate(distrGenState)) + } + + return appState +} diff --git a/x/genutil/legacy/v038/migrate_test.go b/x/genutil/legacy/v038/migrate_test.go new file mode 100644 index 000000000000..bbc8b476ed85 --- /dev/null +++ b/x/genutil/legacy/v038/migrate_test.go @@ -0,0 +1,143 @@ +package v038_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/client" + v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v036" + v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" + v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v038" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" + + "github.com/stretchr/testify/require" +) + +var genAccountsState = []byte(`[ + { + "account_number": "0", + "address": "cosmos1q7380u26f7ntke3facjmynajs4umlr329vr4ja", + "coins": [ + { + "amount": "1000000000", + "denom": "node0token" + }, + { + "amount": "400000198", + "denom": "stake" + } + ], + "delegated_free": [], + "delegated_vesting": [], + "end_time": "0", + "module_name": "", + "module_permissions": [], + "original_vesting": [], + "sequence_number": "1", + "start_time": "0" + }, + { + "account_number": "0", + "address": "cosmos1tygms3xhhs3yv487phx3dw4a95jn7t7lpm470r", + "coins": [], + "delegated_free": [], + "delegated_vesting": [], + "end_time": "0", + "module_name": "not_bonded_tokens_pool", + "module_permissions": [ + "burner", + "staking" + ], + "original_vesting": [], + "sequence_number": "0", + "start_time": "0" + }, + { + "account_number": "0", + "address": "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", + "coins": [], + "delegated_free": [], + "delegated_vesting": [], + "end_time": "0", + "module_name": "mint", + "module_permissions": [ + "minter" + ], + "original_vesting": [], + "sequence_number": "0", + "start_time": "0" + } + ]`) + +var genAuthState = []byte(`{ + "params": { + "max_memo_characters": "256", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10" + } +}`) + +var genStakingState = []byte(`{ + "delegations": [ + { + "delegator_address": "cosmos1q7380u26f7ntke3facjmynajs4umlr329vr4ja", + "shares": "100000000.000000000000000000", + "validator_address": "cosmosvaloper1q7380u26f7ntke3facjmynajs4umlr32qchq7w" + } + ], + "exported": true, + "last_total_power": "400", + "last_validator_powers": [ + { + "Address": "cosmosvaloper1q7380u26f7ntke3facjmynajs4umlr32qchq7w", + "Power": "100" + } + ], + "params": { + "bond_denom": "stake", + "max_entries": 7, + "max_validators": 100, + "unbonding_time": "259200000000000" + }, + "redelegations": null, + "unbonding_delegations": null, + "validators": [ + { + "commission": { + "commission_rates": { + "max_change_rate": "0.000000000000000000", + "max_rate": "0.000000000000000000", + "rate": "0.000000000000000000" + }, + "update_time": "2019-09-24T23:11:22.9692177Z" + }, + "consensus_pubkey": "cosmosvalconspub1zcjduepqygqrt0saxf76lhsmp56rx52j0acdxyjvcdkq3tqvwrsmmm0ke28q36kh9h", + "delegator_shares": "100000000.000000000000000000", + "description": { + "details": "", + "identity": "", + "moniker": "node0", + "website": "" + }, + "jailed": false, + "min_self_delegation": "1", + "operator_address": "cosmosvaloper1q7380u26f7ntke3facjmynajs4umlr32qchq7w", + "status": 2, + "tokens": "100000000", + "unbonding_height": "0", + "unbonding_time": "1970-01-01T00:00:00Z" + } + ] +}`) + +func TestMigrate(t *testing.T) { + genesis := types.AppMap{ + v036auth.ModuleName: genAuthState, + v036genaccounts.ModuleName: genAccountsState, + v036staking.ModuleName: genStakingState, + } + + require.NotPanics(t, func() { v038.Migrate(genesis, client.Context{}) }) +} diff --git a/x/genutil/legacy/v039/migrate.go b/x/genutil/legacy/v039/migrate.go new file mode 100644 index 000000000000..99c7fce772f4 --- /dev/null +++ b/x/genutil/legacy/v039/migrate.go @@ -0,0 +1,44 @@ +package v039 + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" +) + +// Migrate migrates exported state from v0.38 to a v0.39 genesis state. +// +// NOTE: No actual migration occurs since the types do not change, but JSON +// serialization of accounts do change. +func Migrate(appState types.AppMap, _ client.Context) types.AppMap { + v038Codec := codec.NewLegacyAmino() + v038auth.RegisterLegacyAminoCodec(v038Codec) + v036gov.RegisterLegacyAminoCodec(v038Codec) + v036distr.RegisterLegacyAminoCodec(v038Codec) + v036params.RegisterLegacyAminoCodec(v038Codec) + v038upgrade.RegisterLegacyAminoCodec(v038Codec) + + v039Codec := codec.NewLegacyAmino() + v039auth.RegisterLegacyAminoCodec(v039Codec) + v036gov.RegisterLegacyAminoCodec(v039Codec) + v036distr.RegisterLegacyAminoCodec(v039Codec) + v036params.RegisterLegacyAminoCodec(v039Codec) + v038upgrade.RegisterLegacyAminoCodec(v039Codec) + + // migrate x/auth state (JSON serialization only) + if appState[v038auth.ModuleName] != nil { + var authGenState v038auth.GenesisState + v038Codec.MustUnmarshalJSON(appState[v038auth.ModuleName], &authGenState) + + delete(appState, v038auth.ModuleName) // delete old key in case the name changed + appState[v039auth.ModuleName] = v039Codec.MustMarshalJSON(v039auth.Migrate(authGenState)) + } + + return appState +} diff --git a/x/genutil/legacy/v039/migrate_test.go b/x/genutil/legacy/v039/migrate_test.go new file mode 100644 index 000000000000..65a7ec3df7e3 --- /dev/null +++ b/x/genutil/legacy/v039/migrate_test.go @@ -0,0 +1,121 @@ +package v039_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v039 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v039" + "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +var genAuthState = []byte(`{ + "params": { + "max_memo_characters": "10", + "tx_sig_limit": "10", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "10", + "sig_verify_cost_secp256k1": "10" + }, + "accounts": [ + { + "type": "cosmos-sdk/Account", + "value": { + "address": "cosmos19hz3ee9e3lj9mne4jggj3v8hxjrpre22jukj9y", + "coins": [ + { + "denom": "stake", + "amount": "400000" + } + ], + "public_key": "cosmospub1addwnpepqtezq4ajkevh724ls45zp72x70rj8mhszqf5pxcaahazm8trv490swlf404", + "account_number": 1, + "sequence": 1 + } + }, + { + "type": "cosmos-sdk/ModuleAccount", + "value": { + "address": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "coins": [ + { + "denom": "stake", + "amount": "400000000" + } + ], + "public_key": "", + "account_number": 2, + "sequence": 4, + "name": "bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + } + }, + { + "type": "cosmos-sdk/ContinuousVestingAccount", + "value": { + "address": "cosmos1vtzxzyjv506dvhl9pa527xsugf5gez4fnqxq0n", + "coins": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "public_key": "cosmospub1addwnpepqdxrk48q89xlmnzrr5nkssle05tkp73uknevzaavm53c02v26vlyzz6vcdh", + "account_number": 3, + "sequence": 5, + "original_vesting": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "delegated_free": [], + "delegated_vesting": [], + "end_time": 1596125048, + "start_time": 1595952248 + } + }, + { + "type": "cosmos-sdk/DelayedVestingAccount", + "value": { + "address": "cosmos1prxkcqclweqa0g28p7vmf6z78ghyeckm4qak30", + "coins": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "public_key": "cosmospub1addwnpepqwewcad349e2yw3weatf8lzfyv5cd6am9jkk4ajach3f568k6gg47nls3p8", + "account_number": 4, + "sequence": 15, + "original_vesting": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "delegated_free": [], + "delegated_vesting": [], + "end_time": 1596125048 + } + } + ] +}`) + +var expectedGenAuthState = []byte(`{"params":{"max_memo_characters":"10","tx_sig_limit":"10","tx_size_cost_per_byte":"10","sig_verify_cost_ed25519":"10","sig_verify_cost_secp256k1":"10"},"accounts":[{"type":"cosmos-sdk/Account","value":{"address":"cosmos19hz3ee9e3lj9mne4jggj3v8hxjrpre22jukj9y","coins":[{"denom":"stake","amount":"400000"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"AvIgV7K2WX8qv4VoIPlG88cj7vAQE0CbHe36LZ1jZUr4"},"account_number":"1","sequence":"1"}},{"type":"cosmos-sdk/ModuleAccount","value":{"address":"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh","coins":[{"denom":"stake","amount":"400000000"}],"public_key":"","account_number":"2","sequence":"4","name":"bonded_tokens_pool","permissions":["burner","staking"]}},{"type":"cosmos-sdk/ContinuousVestingAccount","value":{"address":"cosmos1vtzxzyjv506dvhl9pa527xsugf5gez4fnqxq0n","coins":[{"denom":"stake","amount":"10000205"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"A0w7VOA5Tf3MQx0naEP5fRdg+jy08sF3rN0jh6mK0z5B"},"account_number":"3","sequence":"5","original_vesting":[{"denom":"stake","amount":"10000205"}],"delegated_free":[],"delegated_vesting":[],"end_time":"1596125048","start_time":"1595952248"}},{"type":"cosmos-sdk/DelayedVestingAccount","value":{"address":"cosmos1prxkcqclweqa0g28p7vmf6z78ghyeckm4qak30","coins":[{"denom":"stake","amount":"10000205"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"A7LsdbGpcqI6Ls9Wk/xJIymG67ssrWr2XcXimmj20hFf"},"account_number":"4","sequence":"15","original_vesting":[{"denom":"stake","amount":"10000205"}],"delegated_free":[],"delegated_vesting":[],"end_time":"1596125048"}}]}`) + +func TestMigrate(t *testing.T) { + genesis := types.AppMap{ + v038auth.ModuleName: genAuthState, + } + + var migrated types.AppMap + require.NotPanics(t, func() { migrated = v039.Migrate(genesis, client.Context{}) }) + require.Equal(t, string(expectedGenAuthState), string(migrated[v039auth.ModuleName])) +} diff --git a/x/genutil/legacy/v039/types.go b/x/genutil/legacy/v039/types.go new file mode 100644 index 000000000000..12d082d1bc8a --- /dev/null +++ b/x/genutil/legacy/v039/types.go @@ -0,0 +1,12 @@ +package v039 + +import "encoding/json" + +const ( + ModuleName = "genutil" +) + +// GenesisState defines the raw genesis transaction in JSON +type GenesisState struct { + GenTxs []json.RawMessage `json:"gentxs" yaml:"gentxs"` +} diff --git a/x/genutil/legacy/v040/migrate.go b/x/genutil/legacy/v040/migrate.go new file mode 100644 index 000000000000..eb5d10690a26 --- /dev/null +++ b/x/genutil/legacy/v040/migrate.go @@ -0,0 +1,200 @@ +package v040 + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" + v036supply "github.com/cosmos/cosmos-sdk/x/bank/legacy/v036" + v038bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v038" + v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" + v039crisis "github.com/cosmos/cosmos-sdk/x/crisis/legacy/v039" + v040crisis "github.com/cosmos/cosmos-sdk/x/crisis/legacy/v040" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" + v040distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040" + v038evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v038" + v040evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v040" + v039genutil "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v039" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" + v039mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v039" + v040mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v040" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" + v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" + v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" + v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" +) + +func migrateGenutil(oldGenState v039genutil.GenesisState) *types.GenesisState { + return &types.GenesisState{ + GenTxs: oldGenState.GenTxs, + } +} + +// Migrate migrates exported state from v0.39 to a v0.40 genesis state. +func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { + v039Codec := codec.NewLegacyAmino() + v039auth.RegisterLegacyAminoCodec(v039Codec) + v036gov.RegisterLegacyAminoCodec(v039Codec) + v036distr.RegisterLegacyAminoCodec(v039Codec) + v036params.RegisterLegacyAminoCodec(v039Codec) + v038upgrade.RegisterLegacyAminoCodec(v039Codec) + + v040Codec := clientCtx.JSONMarshaler + + if appState[v038bank.ModuleName] != nil { + // unmarshal relative source genesis application state + var bankGenState v038bank.GenesisState + v039Codec.MustUnmarshalJSON(appState[v038bank.ModuleName], &bankGenState) + + // unmarshal x/auth genesis state to retrieve all account balances + var authGenState v039auth.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039auth.ModuleName], &authGenState) + + // unmarshal x/supply genesis state to retrieve total supply + var supplyGenState v036supply.GenesisState + v039Codec.MustUnmarshalJSON(appState[v036supply.ModuleName], &supplyGenState) + + // delete deprecated x/bank genesis state + delete(appState, v038bank.ModuleName) + + // delete deprecated x/supply genesis state + delete(appState, v036supply.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040bank.ModuleName] = v040Codec.MustMarshalJSON(v040bank.Migrate(bankGenState, authGenState, supplyGenState)) + } + + // remove balances from existing accounts + if appState[v039auth.ModuleName] != nil { + // unmarshal relative source genesis application state + var authGenState v039auth.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039auth.ModuleName], &authGenState) + + // delete deprecated x/auth genesis state + delete(appState, v039auth.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040auth.ModuleName] = v040Codec.MustMarshalJSON(v040auth.Migrate(authGenState)) + } + + // Migrate x/crisis. + if appState[v039crisis.ModuleName] != nil { + // unmarshal relative source genesis application state + var crisisGenState v039crisis.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039crisis.ModuleName], &crisisGenState) + + // delete deprecated x/crisis genesis state + delete(appState, v039crisis.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040crisis.ModuleName] = v040Codec.MustMarshalJSON(v040crisis.Migrate(crisisGenState)) + } + + // Migrate x/distribution. + if appState[v038distr.ModuleName] != nil { + // unmarshal relative source genesis application state + var distributionGenState v038distr.GenesisState + v039Codec.MustUnmarshalJSON(appState[v038distr.ModuleName], &distributionGenState) + + // delete deprecated x/distribution genesis state + delete(appState, v038distr.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040distr.ModuleName] = v040Codec.MustMarshalJSON(v040distr.Migrate(distributionGenState)) + } + + // Migrate x/evidence. + if appState[v038evidence.ModuleName] != nil { + // unmarshal relative source genesis application state + var evidenceGenState v038evidence.GenesisState + v039Codec.MustUnmarshalJSON(appState[v038bank.ModuleName], &evidenceGenState) + + // delete deprecated x/evidence genesis state + delete(appState, v038evidence.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040evidence.ModuleName] = v040Codec.MustMarshalJSON(v040evidence.Migrate(evidenceGenState)) + } + + // Migrate x/gov. + if appState[v036gov.ModuleName] != nil { + // unmarshal relative source genesis application state + var govGenState v036gov.GenesisState + v039Codec.MustUnmarshalJSON(appState[v036gov.ModuleName], &govGenState) + + // delete deprecated x/gov genesis state + delete(appState, v036gov.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040gov.ModuleName] = v040Codec.MustMarshalJSON(v040gov.Migrate(govGenState)) + } + + // Migrate x/mint. + if appState[v039mint.ModuleName] != nil { + // unmarshal relative source genesis application state + var mintGenState v039mint.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039mint.ModuleName], &mintGenState) + + // delete deprecated x/mint genesis state + delete(appState, v039mint.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040mint.ModuleName] = v040Codec.MustMarshalJSON(v040mint.Migrate(mintGenState)) + } + + // Migrate x/slashing. + if appState[v039slashing.ModuleName] != nil { + // unmarshal relative source genesis application state + var slashingGenState v039slashing.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039slashing.ModuleName], &slashingGenState) + + // delete deprecated x/slashing genesis state + delete(appState, v039slashing.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040slashing.ModuleName] = v040Codec.MustMarshalJSON(v040slashing.Migrate(slashingGenState)) + } + + // Migrate x/staking. + if appState[v038staking.ModuleName] != nil { + // unmarshal relative source genesis application state + var stakingGenState v038staking.GenesisState + v039Codec.MustUnmarshalJSON(appState[v038staking.ModuleName], &stakingGenState) + + // delete deprecated x/staking genesis state + delete(appState, v038staking.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[v040staking.ModuleName] = v040Codec.MustMarshalJSON(v040staking.Migrate(stakingGenState)) + } + + // Migrate x/genutil + if appState[v039genutil.ModuleName] != nil { + // unmarshal relative source genesis application state + var genutilGenState v039genutil.GenesisState + v039Codec.MustUnmarshalJSON(appState[v039genutil.ModuleName], &genutilGenState) + + // delete deprecated x/staking genesis state + delete(appState, v039genutil.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + appState[ModuleName] = v040Codec.MustMarshalJSON(migrateGenutil(genutilGenState)) + } + + return appState +} diff --git a/x/genutil/legacy/v040/types.go b/x/genutil/legacy/v040/types.go new file mode 100644 index 000000000000..f641dbff51e1 --- /dev/null +++ b/x/genutil/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "genutil" +) diff --git a/x/genutil/legacy/v0_36/migrate.go b/x/genutil/legacy/v0_36/migrate.go deleted file mode 100644 index 2ec6d01a2a0a..000000000000 --- a/x/genutil/legacy/v0_36/migrate.go +++ /dev/null @@ -1,96 +0,0 @@ -package v036 - -import ( - "github.com/cosmos/cosmos-sdk/codec" - v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" - v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" - v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_34" - v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_36" - v034genAccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_34" - v036genAccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - "github.com/cosmos/cosmos-sdk/x/genutil" - v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_34" - v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_36" - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" - v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" - v036supply "github.com/cosmos/cosmos-sdk/x/supply/legacy/v0_36" -) - -// Migrate migrates exported state from v0.34 to a v0.36 genesis state. -func Migrate(appState genutil.AppMap) genutil.AppMap { - v034Codec := codec.New() - codec.RegisterCrypto(v034Codec) - v034gov.RegisterCodec(v034Codec) - - v036Codec := codec.New() - codec.RegisterCrypto(v036Codec) - v036gov.RegisterCodec(v036Codec) - - // migrate genesis accounts state - if appState[v034genAccounts.ModuleName] != nil { - var genAccs v034genAccounts.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034genAccounts.ModuleName], &genAccs) - - var authGenState v034auth.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034auth.ModuleName], &authGenState) - - var govGenState v034gov.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034gov.ModuleName], &govGenState) - - var distrGenState v034distr.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034distr.ModuleName], &distrGenState) - - var stakingGenState v034staking.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034staking.ModuleName], &stakingGenState) - - delete(appState, v034genAccounts.ModuleName) // delete old key in case the name changed - appState[v036genAccounts.ModuleName] = v036Codec.MustMarshalJSON( - v036genAccounts.Migrate( - genAccs, authGenState.CollectedFees, distrGenState.FeePool.CommunityPool, govGenState.Deposits, - stakingGenState.Validators, stakingGenState.UnbondingDelegations, distrGenState.OutstandingRewards, - stakingGenState.Params.BondDenom, v036distr.ModuleName, v036gov.ModuleName, - ), - ) - } - - // migrate auth state - if appState[v034auth.ModuleName] != nil { - var authGenState v034auth.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034auth.ModuleName], &authGenState) - - delete(appState, v034auth.ModuleName) // delete old key in case the name changed - appState[v036auth.ModuleName] = v036Codec.MustMarshalJSON(v036auth.Migrate(authGenState)) - } - - // migrate gov state - if appState[v034gov.ModuleName] != nil { - var govGenState v034gov.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034gov.ModuleName], &govGenState) - - delete(appState, v034gov.ModuleName) // delete old key in case the name changed - appState[v036gov.ModuleName] = v036Codec.MustMarshalJSON(v036gov.Migrate(govGenState)) - } - - // migrate distribution state - if appState[v034distr.ModuleName] != nil { - var slashingGenState v034distr.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034distr.ModuleName], &slashingGenState) - - delete(appState, v034distr.ModuleName) // delete old key in case the name changed - appState[v036distr.ModuleName] = v036Codec.MustMarshalJSON(v036distr.Migrate(slashingGenState)) - } - - // migrate staking state - if appState[v034staking.ModuleName] != nil { - var stakingGenState v034staking.GenesisState - v034Codec.MustUnmarshalJSON(appState[v034staking.ModuleName], &stakingGenState) - - delete(appState, v034staking.ModuleName) // delete old key in case the name changed - appState[v036staking.ModuleName] = v036Codec.MustMarshalJSON(v036staking.Migrate(stakingGenState)) - } - - // migrate supply state - appState[v036supply.ModuleName] = v036Codec.MustMarshalJSON(v036supply.EmptyGenesisState()) - - return appState -} diff --git a/x/genutil/legacy/v0_36/migrate_test.go b/x/genutil/legacy/v0_36/migrate_test.go deleted file mode 100644 index 11e2f1676dd2..000000000000 --- a/x/genutil/legacy/v0_36/migrate_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package v036 - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/x/genutil" -) - -var basic034Gov = []byte(` - { - "starting_proposal_id": "2", - "deposits": [ - { - "proposal_id": "1", - "deposit": { - "depositor": "fetch1grgelyng2v6v3t8z87wu3sxgt9m5s03xldkav6", - "proposal_id": "1", - "amount": [ - { - "denom": "uatom", - "amount": "512000000" - } - ] - } - } - ], - "votes" : [ - { - "proposal_id": "1", - "vote": { - "voter": "fetch1lktjhnzkpkz3ehrg8psvmwhafg56kfss8fv6fl", - "proposal_id": "1", - "option": "Yes" - } - } - ], - "proposals": [ - { - "proposal_content": { - "type": "gov/TextProposal", - "value": { - "title": "test", - "description": "test" - } - }, - "proposal_id": "1", - "proposal_status": "Passed", - "final_tally_result": { - "yes": "1", - "abstain": "0", - "no": "0", - "no_with_veto": "0" - }, - "submit_time": "2019-05-03T21:08:25.443199036Z", - "deposit_end_time": "2019-05-17T21:08:25.443199036Z", - "total_deposit": [ - { - "denom": "uatom", - "amount": "512000000" - } - ], - "voting_start_time": "2019-05-04T16:02:33.24680295Z", - "voting_end_time": "2019-05-18T16:02:33.24680295Z" - } - ], - "deposit_params": { - "min_deposit": [ - { - "denom": "uatom", - "amount": "512000000" - } - ], - "max_deposit_period": "1209600000000000" - }, - "voting_params": { - "voting_period": "1209600000000000" - }, - "tally_params": { - "quorum": "0.400000000000000000", - "threshold": "0.500000000000000000", - "veto": "0.334000000000000000" - } - } -`) - -func TestDummyGenesis(t *testing.T) { - genesisDummy := genutil.AppMap{ - "foo": {}, - "bar": []byte(`{"custom": "module"}`), - } - migratedDummy := Migrate(genesisDummy) - - // We should not touch custom modules in the map - require.Equal(t, genesisDummy["foo"], migratedDummy["foo"]) - require.Equal(t, genesisDummy["bar"], migratedDummy["bar"]) -} - -func TestGovGenesis(t *testing.T) { - genesis := genutil.AppMap{ - "gov": basic034Gov, - } - - require.NotPanics(t, func() { Migrate(genesis) }) -} diff --git a/x/genutil/legacy/v0_38/migrate.go b/x/genutil/legacy/v0_38/migrate.go deleted file mode 100644 index 59ba4772705f..000000000000 --- a/x/genutil/legacy/v0_38/migrate.go +++ /dev/null @@ -1,61 +0,0 @@ -package v038 - -import ( - "github.com/cosmos/cosmos-sdk/codec" - v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" - v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38" - v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_36" - v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_38" - v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - "github.com/cosmos/cosmos-sdk/x/genutil" - v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" - v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_38" -) - -// Migrate migrates exported state from v0.36/v0.37 to a v0.38 genesis state. -func Migrate(appState genutil.AppMap) genutil.AppMap { - v036Codec := codec.New() - codec.RegisterCrypto(v036Codec) - - v038Codec := codec.New() - codec.RegisterCrypto(v038Codec) - v038auth.RegisterCodec(v038Codec) - - if appState[v036genaccounts.ModuleName] != nil { - // unmarshal relative source genesis application state - var authGenState v036auth.GenesisState - v036Codec.MustUnmarshalJSON(appState[v036auth.ModuleName], &authGenState) - - var genAccountsGenState v036genaccounts.GenesisState - v036Codec.MustUnmarshalJSON(appState[v036genaccounts.ModuleName], &genAccountsGenState) - - // delete deprecated genaccounts genesis state - delete(appState, v036genaccounts.ModuleName) - - // Migrate relative source genesis application state and marshal it into - // the respective key. - appState[v038auth.ModuleName] = v038Codec.MustMarshalJSON( - v038auth.Migrate(authGenState, genAccountsGenState), - ) - } - - // migrate staking state - if appState[v036staking.ModuleName] != nil { - var stakingGenState v036staking.GenesisState - v036Codec.MustUnmarshalJSON(appState[v036staking.ModuleName], &stakingGenState) - - delete(appState, v036staking.ModuleName) // delete old key in case the name changed - appState[v038staking.ModuleName] = v038Codec.MustMarshalJSON(v038staking.Migrate(stakingGenState)) - } - - // migrate distribution state - if appState[v036distr.ModuleName] != nil { - var distrGenState v036distr.GenesisState - v036Codec.MustUnmarshalJSON(appState[v036distr.ModuleName], &distrGenState) - - delete(appState, v036distr.ModuleName) // delete old key in case the name changed - appState[v038distr.ModuleName] = v038Codec.MustMarshalJSON(v038distr.Migrate(distrGenState)) - } - - return appState -} diff --git a/x/genutil/legacy/v0_38/migrate_test.go b/x/genutil/legacy/v0_38/migrate_test.go deleted file mode 100644 index a19d1316f934..000000000000 --- a/x/genutil/legacy/v0_38/migrate_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package v038_test - -import ( - "testing" - - v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" - v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - "github.com/cosmos/cosmos-sdk/x/genutil" - v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_38" - v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" - - "github.com/stretchr/testify/require" -) - -var genAccountsState = []byte(`[ - { - "account_number": "0", - "address": "fetch1q7380u26f7ntke3facjmynajs4umlr32k323s2", - "coins": [ - { - "amount": "1000000000", - "denom": "node0token" - }, - { - "amount": "400000198", - "denom": "stake" - } - ], - "delegated_free": [], - "delegated_vesting": [], - "end_time": "0", - "module_name": "", - "module_permissions": [], - "original_vesting": [], - "sequence_number": "1", - "start_time": "0" - }, - { - "account_number": "0", - "address": "fetch1tygms3xhhs3yv487phx3dw4a95jn7t7ljxu6d5", - "coins": [], - "delegated_free": [], - "delegated_vesting": [], - "end_time": "0", - "module_name": "not_bonded_tokens_pool", - "module_permissions": [ - "burner", - "staking" - ], - "original_vesting": [], - "sequence_number": "0", - "start_time": "0" - }, - { - "account_number": "0", - "address": "fetch1m3h30wlvsf8llruxtpukdvsy0km2kum8mvwu9h", - "coins": [], - "delegated_free": [], - "delegated_vesting": [], - "end_time": "0", - "module_name": "mint", - "module_permissions": [ - "minter" - ], - "original_vesting": [], - "sequence_number": "0", - "start_time": "0" - } - ]`) - -var genAuthState = []byte(`{ - "params": { - "max_memo_characters": "256", - "sig_verify_cost_ed25519": "590", - "sig_verify_cost_secp256k1": "1000", - "tx_sig_limit": "7", - "tx_size_cost_per_byte": "10" - } -}`) - -var genStakingState = []byte(`{ - "delegations": [ - { - "delegator_address": "fetch1q7380u26f7ntke3facjmynajs4umlr32k323s2", - "shares": "100000000.000000000000000000", - "validator_address": "fetchvaloper1q7380u26f7ntke3facjmynajs4umlr32n44jrd" - } - ], - "exported": true, - "last_total_power": "400", - "last_validator_powers": [ - { - "Address": "fetchvaloper1q7380u26f7ntke3facjmynajs4umlr32n44jrd", - "Power": "100" - } - ], - "params": { - "bond_denom": "stake", - "max_entries": 7, - "max_validators": 100, - "unbonding_time": "259200000000000" - }, - "redelegations": null, - "unbonding_delegations": null, - "validators": [ - { - "commission": { - "commission_rates": { - "max_change_rate": "0.000000000000000000", - "max_rate": "0.000000000000000000", - "rate": "0.000000000000000000" - }, - "update_time": "2019-09-24T23:11:22.9692177Z" - }, - "consensus_pubkey": "fetchvalconspub1zcjduepqygqrt0saxf76lhsmp56rx52j0acdxyjvcdkq3tqvwrsmmm0ke28ql3chal", - "delegator_shares": "100000000.000000000000000000", - "description": { - "details": "", - "identity": "", - "moniker": "node0", - "website": "" - }, - "jailed": false, - "min_self_delegation": "1", - "operator_address": "fetchvaloper1q7380u26f7ntke3facjmynajs4umlr32n44jrd", - "status": 2, - "tokens": "100000000", - "unbonding_height": "0", - "unbonding_time": "1970-01-01T00:00:00Z" - } - ] -}`) - -func TestMigrate(t *testing.T) { - genesis := genutil.AppMap{ - v036auth.ModuleName: genAuthState, - v036genaccounts.ModuleName: genAccountsState, - v036staking.ModuleName: genStakingState, - } - - require.NotPanics(t, func() { v038.Migrate(genesis) }) -} diff --git a/x/genutil/module.go b/x/genutil/module.go index a35aa1bffb37..bfaeb0c59168 100644 --- a/x/genutil/module.go +++ b/x/genutil/module.go @@ -5,12 +5,14 @@ import ( "fmt" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "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" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/genutil/types" @@ -26,36 +28,43 @@ type AppModuleBasic struct{} // Name returns the genutil module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the genutil module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} +// RegisterLegacyAminoCodec registers the genutil module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers the module's interface types +func (b AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {} // DefaultGenesis returns default genesis state as raw bytes for the genutil // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(types.DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the genutil module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +func (b AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, txEncodingConfig client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return types.ValidateGenesis(&data, txEncodingConfig.TxJSONDecoder()) } // RegisterRESTRoutes registers the REST routes for the genutil module. -func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the genutil module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) { +} // GetTxCmd returns no root tx command for the genutil module. -func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetTxCmd() *cobra.Command { return nil } // GetQueryCmd returns no root query command for the genutil module. -func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd() *cobra.Command { return nil } //____________________________________________________________________________ @@ -63,33 +72,41 @@ func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } type AppModule struct { AppModuleBasic - accountKeeper types.AccountKeeper - stakingKeeper types.StakingKeeper - deliverTx deliverTxfn + accountKeeper types.AccountKeeper + stakingKeeper types.StakingKeeper + deliverTx deliverTxfn + txEncodingConfig client.TxEncodingConfig } // NewAppModule creates a new AppModule object func NewAppModule(accountKeeper types.AccountKeeper, - stakingKeeper types.StakingKeeper, deliverTx deliverTxfn) module.AppModule { + stakingKeeper types.StakingKeeper, deliverTx deliverTxfn, + txEncodingConfig client.TxEncodingConfig, +) module.AppModule { return module.NewGenesisOnlyAppModule(AppModule{ - AppModuleBasic: AppModuleBasic{}, - accountKeeper: accountKeeper, - stakingKeeper: stakingKeeper, - deliverTx: deliverTx, + AppModuleBasic: AppModuleBasic{}, + accountKeeper: accountKeeper, + stakingKeeper: stakingKeeper, + deliverTx: deliverTx, + txEncodingConfig: txEncodingConfig, }) } // InitGenesis performs genesis initialization for the genutil module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - return InitGenesis(ctx, ModuleCdc, am.stakingKeeper, am.deliverTx, genesisState) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + validators, err := InitGenesis(ctx, am.stakingKeeper, am.deliverTx, genesisState, am.txEncodingConfig) + if err != nil { + panic(err) + } + return validators } // ExportGenesis returns the exported genesis state as raw bytes for the genutil // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - return am.DefaultGenesis() +func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return am.DefaultGenesis(cdc) } diff --git a/x/genutil/types/codec.go b/x/genutil/types/codec.go deleted file mode 100644 index 624be93d138f..000000000000 --- a/x/genutil/types/codec.go +++ /dev/null @@ -1,22 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// ModuleCdc defines a generic sealed codec to be used throughout this module -var ModuleCdc *codec.Codec - -// TODO: abstract genesis transactions registration back to staking -// required for genesis transactions -func init() { - ModuleCdc = codec.New() - stakingtypes.RegisterCodec(ModuleCdc) - authtypes.RegisterCodec(ModuleCdc) - sdk.RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() -} diff --git a/x/genutil/types/expected_keepers.go b/x/genutil/types/expected_keepers.go index b44236d7d8d1..341d2eb2c8ec 100644 --- a/x/genutil/types/expected_keepers.go +++ b/x/genutil/types/expected_keepers.go @@ -7,26 +7,36 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" ) // StakingKeeper defines the expected staking keeper (noalias) type StakingKeeper interface { - ApplyAndReturnValidatorSetUpdates(sdk.Context) (updates []abci.ValidatorUpdate) + ApplyAndReturnValidatorSetUpdates(sdk.Context) (updates []abci.ValidatorUpdate, err error) } // AccountKeeper defines the expected account keeper (noalias) type AccountKeeper interface { - NewAccount(sdk.Context, authexported.Account) authexported.Account - SetAccount(sdk.Context, authexported.Account) - IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool)) + NewAccount(sdk.Context, auth.AccountI) auth.AccountI + SetAccount(sdk.Context, auth.AccountI) + IterateAccounts(ctx sdk.Context, process func(auth.AccountI) (stop bool)) } // GenesisAccountsIterator defines the expected iterating genesis accounts object (noalias) type GenesisAccountsIterator interface { IterateGenesisAccounts( - cdc *codec.Codec, + cdc *codec.LegacyAmino, appGenesis map[string]json.RawMessage, - iterateFn func(authexported.Account) (stop bool), + cb func(auth.AccountI) (stop bool), + ) +} + +// GenesisAccountsIterator defines the expected iterating genesis accounts object (noalias) +type GenesisBalancesIterator interface { + IterateGenesisBalances( + cdc codec.JSONMarshaler, + appGenesis map[string]json.RawMessage, + cb func(bankexported.GenesisBalance) (stop bool), ) } diff --git a/x/genutil/types/genesis.pb.go b/x/genutil/types/genesis.pb.go new file mode 100644 index 000000000000..369cd0edfaf6 --- /dev/null +++ b/x/genutil/types/genesis.pb.go @@ -0,0 +1,329 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/genutil/v1beta1/genesis.proto + +package types + +import ( + encoding_json "encoding/json" + 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 defines the raw genesis transaction in JSON. +type GenesisState struct { + // gen_txs defines the genesis transactions. + GenTxs []encoding_json.RawMessage `protobuf:"bytes,1,rep,name=gen_txs,json=genTxs,proto3,casttype=encoding/json.RawMessage" json:"gentxs" yaml:"gentxs"` +} + +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_31771d25e8d8f90f, []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) GetGenTxs() []encoding_json.RawMessage { + if m != nil { + return m.GenTxs + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.genutil.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/genutil/v1beta1/genesis.proto", fileDescriptor_31771d25e8d8f90f) +} + +var fileDescriptor_31771d25e8d8f90f = []byte{ + // 234 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x49, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0x4f, 0xcd, 0x2b, 0x2d, 0xc9, 0xcc, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, + 0x49, 0x34, 0x04, 0xf1, 0x53, 0x8b, 0x33, 0x8b, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0xc4, + 0x20, 0xaa, 0xf4, 0xa0, 0xaa, 0xf4, 0xa0, 0xaa, 0xa4, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4a, + 0xf4, 0x41, 0x2c, 0x88, 0x6a, 0xa5, 0x04, 0x2e, 0x1e, 0x77, 0x88, 0xf6, 0xe0, 0x92, 0xc4, 0x92, + 0x54, 0xa1, 0x00, 0x2e, 0xf6, 0xf4, 0xd4, 0xbc, 0xf8, 0x92, 0x8a, 0x62, 0x09, 0x46, 0x05, 0x66, + 0x0d, 0x1e, 0x27, 0xf3, 0x57, 0xf7, 0xe4, 0xd9, 0xd2, 0x53, 0xf3, 0x4a, 0x2a, 0x8a, 0x3f, 0xdd, + 0x93, 0xe7, 0xad, 0x4c, 0xcc, 0xcd, 0xb1, 0x52, 0x82, 0xf0, 0x95, 0x7e, 0xdd, 0x93, 0x97, 0x48, + 0xcd, 0x4b, 0xce, 0x4f, 0xc9, 0xcc, 0x4b, 0xd7, 0xcf, 0x2a, 0xce, 0xcf, 0xd3, 0x0b, 0x4a, 0x2c, + 0xf7, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0x0d, 0x02, 0x69, 0x0a, 0xa9, 0x28, 0x76, 0x72, 0x3b, + 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, 0x9d, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, + 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xd7, 0x20, 0x94, 0x6e, 0x71, 0x4a, 0xb6, 0x7e, 0x05, + 0xdc, 0x9f, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x07, 0x1b, 0x03, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x6b, 0x84, 0x7a, 0x20, 0x06, 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.GenTxs) > 0 { + for iNdEx := len(m.GenTxs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.GenTxs[iNdEx]) + copy(dAtA[i:], m.GenTxs[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.GenTxs[iNdEx]))) + 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.GenTxs) > 0 { + for _, b := range m.GenTxs { + l = len(b) + 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 GenTxs", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GenTxs = append(m.GenTxs, make([]byte, postIndex-iNdEx)) + copy(m.GenTxs[len(m.GenTxs)-1], dAtA[iNdEx:postIndex]) + 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/genutil/types/genesis_state.go b/x/genutil/types/genesis_state.go index 6c5c555482ee..b3e28bb6de80 100644 --- a/x/genutil/types/genesis_state.go +++ b/x/genutil/types/genesis_state.go @@ -9,51 +9,55 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// GenesisState defines the raw genesis transaction in JSON -type GenesisState struct { - GenTxs []json.RawMessage `json:"gentxs" yaml:"gentxs"` -} - // NewGenesisState creates a new GenesisState object -func NewGenesisState(genTxs []json.RawMessage) GenesisState { - return GenesisState{ +func NewGenesisState(genTxs []json.RawMessage) *GenesisState { + // Ensure genTxs is never nil, https://github.com/cosmos/cosmos-sdk/issues/5086 + if len(genTxs) == 0 { + genTxs = make([]json.RawMessage, 0) + } + return &GenesisState{ GenTxs: genTxs, } } // DefaultGenesisState returns the genutil module's default genesis state. -func DefaultGenesisState() GenesisState { - return GenesisState{ +func DefaultGenesisState() *GenesisState { + return &GenesisState{ GenTxs: []json.RawMessage{}, } } -// NewGenesisStateFromStdTx creates a new GenesisState object +// NewGenesisStateFromTx creates a new GenesisState object // from auth transactions -func NewGenesisStateFromStdTx(genTxs []authtypes.StdTx) GenesisState { +func NewGenesisStateFromTx(txJSONEncoder sdk.TxEncoder, genTxs []sdk.Tx) *GenesisState { genTxsBz := make([]json.RawMessage, len(genTxs)) for i, genTx := range genTxs { - genTxsBz[i] = ModuleCdc.MustMarshalJSON(genTx) + var err error + genTxsBz[i], err = txJSONEncoder(genTx) + if err != nil { + panic(err) + } } return NewGenesisState(genTxsBz) } // GetGenesisStateFromAppState gets the genutil genesis state from the expected app state -func GetGenesisStateFromAppState(cdc *codec.Codec, appState map[string]json.RawMessage) GenesisState { +func GetGenesisStateFromAppState(cdc codec.JSONMarshaler, appState map[string]json.RawMessage) *GenesisState { var genesisState GenesisState if appState[ModuleName] != nil { cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) } - return genesisState + return &genesisState } // SetGenesisStateInAppState sets the genutil genesis state within the expected app state -func SetGenesisStateInAppState(cdc *codec.Codec, - appState map[string]json.RawMessage, genesisState GenesisState) map[string]json.RawMessage { +func SetGenesisStateInAppState( + cdc codec.JSONMarshaler, appState map[string]json.RawMessage, genesisState *GenesisState, +) map[string]json.RawMessage { genesisStateBz := cdc.MustMarshalJSON(genesisState) appState[ModuleName] = genesisStateBz @@ -64,10 +68,9 @@ func SetGenesisStateInAppState(cdc *codec.Codec, // for the application. // // NOTE: The pubkey input is this machines pubkey. -func GenesisStateFromGenDoc(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, -) (genesisState map[string]json.RawMessage, err error) { +func GenesisStateFromGenDoc(genDoc tmtypes.GenesisDoc) (genesisState map[string]json.RawMessage, err error) { - if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil { + if err = json.Unmarshal(genDoc.AppState, &genesisState); err != nil { return genesisState, err } return genesisState, nil @@ -77,38 +80,38 @@ func GenesisStateFromGenDoc(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, // for the application. // // NOTE: The pubkey input is this machines pubkey. -func GenesisStateFromGenFile(cdc *codec.Codec, genFile string, -) (genesisState map[string]json.RawMessage, genDoc *tmtypes.GenesisDoc, err error) { - +func GenesisStateFromGenFile(genFile string) (genesisState map[string]json.RawMessage, genDoc *tmtypes.GenesisDoc, err error) { if !tmos.FileExists(genFile) { return genesisState, genDoc, fmt.Errorf("%s does not exist, run `init` first", genFile) } + genDoc, err = tmtypes.GenesisDocFromFile(genFile) if err != nil { return genesisState, genDoc, err } - genesisState, err = GenesisStateFromGenDoc(cdc, *genDoc) + genesisState, err = GenesisStateFromGenDoc(*genDoc) return genesisState, genDoc, err } // ValidateGenesis validates GenTx transactions -func ValidateGenesis(genesisState GenesisState) error { +func ValidateGenesis(genesisState *GenesisState, txJSONDecoder sdk.TxDecoder) error { for i, genTx := range genesisState.GenTxs { - var tx authtypes.StdTx - if err := ModuleCdc.UnmarshalJSON(genTx, &tx); err != nil { + var tx sdk.Tx + tx, err := txJSONDecoder(genTx) + if err != nil { return err } msgs := tx.GetMsgs() if len(msgs) != 1 { return errors.New( - "must provide genesis StdTx with exactly 1 CreateValidator message") + "must provide genesis Tx with exactly 1 CreateValidator message") } // TODO: abstract back to staking - if _, ok := msgs[0].(stakingtypes.MsgCreateValidator); !ok { + if _, ok := msgs[0].(*stakingtypes.MsgCreateValidator); !ok { return fmt.Errorf( "genesis transaction %v does not contain a MsgCreateValidator", i) } diff --git a/x/genutil/types/genesis_state_test.go b/x/genutil/types/genesis_state_test.go index aed7cd7f311e..01e1db9fd330 100644 --- a/x/genutil/types/genesis_state_test.go +++ b/x/genutil/types/genesis_state_test.go @@ -1,13 +1,18 @@ -package types +package types_test import ( + "encoding/json" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "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/genutil/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -16,21 +21,38 @@ var ( pk2 = ed25519.GenPrivKey().PubKey() ) -func TestValidateGenesisMultipleMessages(t *testing.T) { +func TestNetGenesisState(t *testing.T) { + gen := types.NewGenesisState(nil) + assert.NotNil(t, gen.GenTxs) // https://github.com/cosmos/cosmos-sdk/issues/5086 + + gen = types.NewGenesisState( + []json.RawMessage{ + []byte(`{"foo":"bar"}`), + }, + ) + assert.Equal(t, string(gen.GenTxs[0]), `{"foo":"bar"}`) +} +func TestValidateGenesisMultipleMessages(t *testing.T) { desc := stakingtypes.NewDescription("testname", "", "", "", "") comm := stakingtypes.CommissionRates{} - msg1 := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk1.Address()), pk1, + msg1, err := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk1.Address()), pk1, sdk.NewInt64Coin(sdk.DefaultBondDenom, 50), desc, comm, sdk.OneInt()) + require.NoError(t, err) - msg2 := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk2.Address()), pk2, + msg2, err := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk2.Address()), pk2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 50), desc, comm, sdk.OneInt()) + require.NoError(t, err) + + txGen := simapp.MakeTestEncodingConfig().TxConfig + txBuilder := txGen.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(msg1, msg2)) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1, msg2}, authtypes.StdFee{}, nil, "") - genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) + tx := txBuilder.GetTx() + genesisState := types.NewGenesisStateFromTx(txGen.TxJSONEncoder(), []sdk.Tx{tx}) - err := ValidateGenesis(genesisState) + err = types.ValidateGenesis(genesisState, simapp.MakeTestEncodingConfig().TxConfig.TxJSONDecoder()) require.Error(t, err) } @@ -39,9 +61,35 @@ func TestValidateGenesisBadMessage(t *testing.T) { msg1 := stakingtypes.NewMsgEditValidator(sdk.ValAddress(pk1.Address()), desc, nil, nil) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1}, authtypes.StdFee{}, nil, "") - genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) + txGen := simapp.MakeTestEncodingConfig().TxConfig + txBuilder := txGen.NewTxBuilder() + err := txBuilder.SetMsgs(msg1) + require.NoError(t, err) + + tx := txBuilder.GetTx() + genesisState := types.NewGenesisStateFromTx(txGen.TxJSONEncoder(), []sdk.Tx{tx}) - err := ValidateGenesis(genesisState) + err = types.ValidateGenesis(genesisState, simapp.MakeTestEncodingConfig().TxConfig.TxJSONDecoder()) require.Error(t, err) } + +func TestGenesisStateFromGenFile(t *testing.T) { + cdc := codec.NewLegacyAmino() + + genFile := "../../../tests/fixtures/adr-024-coin-metadata_genesis.json" + genesisState, _, err := types.GenesisStateFromGenFile(genFile) + require.NoError(t, err) + + var bankGenesis banktypes.GenesisState + cdc.MustUnmarshalJSON(genesisState[banktypes.ModuleName], &bankGenesis) + + require.True(t, bankGenesis.Params.DefaultSendEnabled) + require.Equal(t, "1000nametoken,100000000stake", bankGenesis.Balances[0].GetCoins().String()) + require.Equal(t, "cosmos106vrzv5xkheqhjm023pxcxlqmcjvuhtfyachz4", bankGenesis.Balances[0].GetAddress().String()) + require.Equal(t, "The native staking token of the Cosmos Hub.", bankGenesis.DenomMetadata[0].GetDescription()) + require.Equal(t, "uatom", bankGenesis.DenomMetadata[0].GetBase()) + require.Equal(t, "matom", bankGenesis.DenomMetadata[0].GetDenomUnits()[1].GetDenom()) + require.Equal(t, []string{"milliatom"}, bankGenesis.DenomMetadata[0].GetDenomUnits()[1].GetAliases()) + require.Equal(t, uint32(3), bankGenesis.DenomMetadata[0].GetDenomUnits()[1].GetExponent()) + +} diff --git a/x/genutil/types/types.go b/x/genutil/types/types.go index 3104af9b1511..694eebdbe3bb 100644 --- a/x/genutil/types/types.go +++ b/x/genutil/types/types.go @@ -3,7 +3,8 @@ package types import ( "encoding/json" - "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) // DONTCOVER @@ -16,7 +17,7 @@ type ( // targeted one. // // TODO: MigrationCallback should also return an error upon failure. - MigrationCallback func(AppMap) AppMap + MigrationCallback func(AppMap, client.Context) AppMap // MigrationMap defines a mapping from a version to a MigrationCallback. MigrationMap map[string]MigrationCallback @@ -29,17 +30,15 @@ const ModuleName = "genutil" type InitConfig struct { ChainID string GenTxsDir string - Name string NodeID string - ValPubKey crypto.PubKey + ValPubKey cryptotypes.PubKey } // NewInitConfig creates a new InitConfig object -func NewInitConfig(chainID, genTxsDir, name, nodeID string, valPubKey crypto.PubKey) InitConfig { +func NewInitConfig(chainID, genTxsDir, nodeID string, valPubKey cryptotypes.PubKey) InitConfig { return InitConfig{ ChainID: chainID, GenTxsDir: genTxsDir, - Name: name, NodeID: nodeID, ValPubKey: valPubKey, } diff --git a/x/genutil/utils.go b/x/genutil/utils.go index ad669e74603e..4c46bdb6f121 100644 --- a/x/genutil/utils.go +++ b/x/genutil/utils.go @@ -2,15 +2,20 @@ package genutil import ( "encoding/json" + "fmt" "path/filepath" "time" + "github.com/cosmos/go-bip39" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" + tmed25519 "github.com/tendermint/tendermint/crypto/ed25519" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" tmtypes "github.com/tendermint/tendermint/types" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) // ExportGenesisFile creates and writes the genesis configuration to disk. An @@ -45,29 +50,50 @@ func ExportGenesisFileWithTime( } // InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles(config *cfg.Config, -) (nodeID string, valPubKey crypto.PubKey, err error) { +func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey cryptotypes.PubKey, err error) { + return InitializeNodeValidatorFilesFromMnemonic(config, "") +} + +// InitializeNodeValidatorFiles creates private validator and p2p configuration files using the given mnemonic. +// If no valid mnemonic is given, a random one will be used instead. +func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic string) (nodeID string, valPubKey cryptotypes.PubKey, err error) { + if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { + return "", nil, fmt.Errorf("invalid mnemonic") + } nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { - return nodeID, valPubKey, err + return "", nil, err } nodeID = string(nodeKey.ID()) pvKeyFile := config.PrivValidatorKeyFile() if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { - return nodeID, valPubKey, nil + return "", nil, err } pvStateFile := config.PrivValidatorStateFile() if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { - return nodeID, valPubKey, nil + return "", nil, err + } + + var filePV *privval.FilePV + if len(mnemonic) == 0 { + filePV = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile) + } else { + privKey := tmed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + filePV = privval.NewFilePV(privKey, pvKeyFile, pvStateFile) + } + + tmValPubKey, err := filePV.GetPubKey() + if err != nil { + return "", nil, err } - valPubKey, err = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey() + valPubKey, err = cryptocodec.FromTmPubKeyInterface(tmValPubKey) if err != nil { - return nodeID, valPubKey, err + return "", nil, err } return nodeID, valPubKey, nil diff --git a/x/genutil/utils_test.go b/x/genutil/utils_test.go index cb04c8f866b1..a845cc68cd96 100644 --- a/x/genutil/utils_test.go +++ b/x/genutil/utils_test.go @@ -2,20 +2,62 @@ package genutil import ( "encoding/json" + "os" "path/filepath" "testing" "time" - "github.com/cosmos/cosmos-sdk/tests" - "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/config" ) func TestExportGenesisFileWithTime(t *testing.T) { t.Parallel() - dir, cleanup := tests.NewTestCaseDir(t) - defer cleanup() - fname := filepath.Join(dir, "genesis.json") - require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now())) + fname := filepath.Join(t.TempDir(), "genesis.json") + + require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(`{"account_owner": "Bob"}`), time.Now())) +} + +func TestInitializeNodeValidatorFilesFromMnemonic(t *testing.T) { + t.Parallel() + + cfg := config.TestConfig() + cfg.RootDir = t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(cfg.RootDir, "config"), 0755)) + + tests := []struct { + name string + mnemonic string + expError bool + }{ + { + name: "invalid mnemonic returns error", + mnemonic: "side video kiss hotel essence", + expError: true, + }, + { + name: "empty mnemonic does not return error", + mnemonic: "", + expError: false, + }, + { + name: "valid mnemonic does not return error", + mnemonic: "side video kiss hotel essence door angle student degree during vague adjust submit trick globe muscle frozen vacuum artwork million shield bind useful wave", + expError: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + _, _, err := InitializeNodeValidatorFilesFromMnemonic(cfg, tt.mnemonic) + + if tt.expError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } } diff --git a/x/gov/abci.go b/x/gov/abci.go index fa990acbf0e9..f9815e0fb812 100644 --- a/x/gov/abci.go +++ b/x/gov/abci.go @@ -2,49 +2,54 @@ package gov import ( "fmt" + "time" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" ) // EndBlocker called every block, process inflation, update validator set. -func EndBlocker(ctx sdk.Context, keeper Keeper) { +func EndBlocker(ctx sdk.Context, keeper keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + logger := keeper.Logger(ctx) // delete inactive proposal from store and its deposits - keeper.IterateInactiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal Proposal) bool { - keeper.DeleteProposal(ctx, proposal.ProposalID) - keeper.DeleteDeposits(ctx, proposal.ProposalID) + keeper.IterateInactiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal types.Proposal) bool { + keeper.DeleteProposal(ctx, proposal.ProposalId) + keeper.DeleteDeposits(ctx, proposal.ProposalId) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeInactiveProposal, - sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalID)), + sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalId)), sdk.NewAttribute(types.AttributeKeyProposalResult, types.AttributeValueProposalDropped), ), ) logger.Info( - fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted", - proposal.ProposalID, - proposal.GetTitle(), - keeper.GetDepositParams(ctx).MinDeposit, - proposal.TotalDeposit, - ), + "proposal did not meet minimum deposit; deleted", + "proposal", proposal.ProposalId, + "title", proposal.GetTitle(), + "min_deposit", keeper.GetDepositParams(ctx).MinDeposit.String(), + "total_deposit", proposal.TotalDeposit.String(), ) + return false }) // fetch active proposals whose voting periods have ended (are passed the block time) - keeper.IterateActiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal Proposal) bool { + keeper.IterateActiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal types.Proposal) bool { var tagValue, logMsg string passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) if burnDeposits { - keeper.DeleteDeposits(ctx, proposal.ProposalID) + keeper.DeleteDeposits(ctx, proposal.ProposalId) } else { - keeper.RefundDeposits(ctx, proposal.ProposalID) + keeper.RefundDeposits(ctx, proposal.ProposalId) } if passes { @@ -54,9 +59,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) { // The proposal handler may execute state mutating logic depending // on the proposal content. If the handler fails, no state mutation // is written and the error message is logged. - err := handler(cacheCtx, proposal.Content) + err := handler(cacheCtx, proposal.GetContent()) if err == nil { - proposal.Status = StatusPassed + proposal.Status = types.StatusPassed tagValue = types.AttributeValueProposalPassed logMsg = "passed" @@ -69,12 +74,12 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) { // write state to the underlying multi-store writeCache() } else { - proposal.Status = StatusFailed + proposal.Status = types.StatusFailed tagValue = types.AttributeValueProposalFailed logMsg = fmt.Sprintf("passed, but failed on execution: %s", err) } } else { - proposal.Status = StatusRejected + proposal.Status = types.StatusRejected tagValue = types.AttributeValueProposalRejected logMsg = "rejected" } @@ -82,19 +87,19 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) { proposal.FinalTallyResult = tallyResults keeper.SetProposal(ctx, proposal) - keeper.RemoveFromActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime) + keeper.RemoveFromActiveProposalQueue(ctx, proposal.ProposalId, proposal.VotingEndTime) logger.Info( - fmt.Sprintf( - "proposal %d (%s) tallied; result: %s", - proposal.ProposalID, proposal.GetTitle(), logMsg, - ), + "proposal tallied", + "proposal", proposal.ProposalId, + "title", proposal.GetTitle(), + "result", logMsg, ) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeActiveProposal, - sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalID)), + sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalId)), sdk.NewAttribute(types.AttributeKeyProposalResult, tagValue), ), ) diff --git a/x/gov/abci_test.go b/x/gov/abci_test.go index db5509f45e18..bfda1b3590b6 100644 --- a/x/gov/abci_test.go +++ b/x/gov/abci_test.go @@ -1,42 +1,47 @@ -package gov +package gov_test import ( "testing" "time" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - keep "github.com/cosmos/cosmos-sdk/x/gov/keeper" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/staking" ) func TestTickExpiredDepositPeriod(t *testing.T) { - input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 10, valTokens) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(input.keeper) + govHandler := gov.NewHandler(app.GovKeeper) - inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal( - ContentFromProposalType("test", "test", ProposalTypeText), + newProposalMsg, err := types.NewMsgSubmitProposal( + types.ContentFromProposalType("test", "test", types.ProposalTypeText), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}, - input.addrs[0], + addrs[0], ) + require.NoError(t, err) res, err := govHandler(ctx, newProposalMsg) require.NoError(t, err) require.NotNil(t, res) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() @@ -44,49 +49,51 @@ func TestTickExpiredDepositPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(input.keeper.GetDepositParams(ctx).MaxDepositPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.True(t, inactiveQueue.Valid()) inactiveQueue.Close() - EndBlocker(ctx, input.keeper) + gov.EndBlocker(ctx, app.GovKeeper) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() } func TestTickMultipleExpiredDepositPeriod(t *testing.T) { - input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 10, valTokens) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(input.keeper) + govHandler := gov.NewHandler(app.GovKeeper) - inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal( - ContentFromProposalType("test", "test", ProposalTypeText), + newProposalMsg, err := types.NewMsgSubmitProposal( + types.ContentFromProposalType("test", "test", types.ProposalTypeText), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}, - input.addrs[0], + addrs[0], ) + require.NoError(t, err) res, err := govHandler(ctx, newProposalMsg) require.NoError(t, err) require.NotNil(t, res) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() @@ -94,29 +101,32 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(2) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg2 := NewMsgSubmitProposal( - ContentFromProposalType("test2", "test2", ProposalTypeText), + newProposalMsg2, err := types.NewMsgSubmitProposal( + types.ContentFromProposalType("test2", "test2", types.ProposalTypeText), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}, - input.addrs[0], + addrs[0], ) + require.NoError(t, err) res, err = govHandler(ctx, newProposalMsg2) require.NoError(t, err) require.NotNil(t, res) newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(input.keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) + newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.True(t, inactiveQueue.Valid()) inactiveQueue.Close() - EndBlocker(ctx, input.keeper) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + + gov.EndBlocker(ctx, app.GovKeeper) + + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() @@ -124,44 +134,52 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(5) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.True(t, inactiveQueue.Valid()) inactiveQueue.Close() - EndBlocker(ctx, input.keeper) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + + gov.EndBlocker(ctx, app.GovKeeper) + + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() } func TestTickPassedDepositPeriod(t *testing.T) { - input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 10, valTokens) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(input.keeper) + govHandler := gov.NewHandler(app.GovKeeper) - inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - activeQueue := input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + activeQueue := app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, activeQueue.Valid()) activeQueue.Close() - newProposalMsg := NewMsgSubmitProposal( - ContentFromProposalType("test2", "test2", ProposalTypeText), + newProposalMsg, err := types.NewMsgSubmitProposal( + types.ContentFromProposalType("test2", "test2", types.ProposalTypeText), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}, - input.addrs[0], + addrs[0], ) + require.NoError(t, err) res, err := govHandler(ctx, newProposalMsg) require.NoError(t, err) require.NotNil(t, res) - proposalID := GetProposalIDFromBytes(res.Data) + var proposalData types.MsgSubmitProposalResponse + err = proto.Unmarshal(res.Data, &proposalData) + require.NoError(t, err) + + proposalID := proposalData.ProposalId - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() @@ -169,168 +187,172 @@ func TestTickPassedDepositPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newDepositMsg := NewMsgDeposit(input.addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}) + newDepositMsg := types.NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}) res, err = govHandler(ctx, newDepositMsg) require.NoError(t, err) require.NotNil(t, res) - activeQueue = input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + activeQueue = app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, activeQueue.Valid()) activeQueue.Close() } func TestTickPassedVotingPeriod(t *testing.T) { - input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler) - SortAddresses(input.addrs) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 10, valTokens) + + SortAddresses(addrs) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(input.keeper) + govHandler := gov.NewHandler(app.GovKeeper) - inactiveQueue := input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - activeQueue := input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + activeQueue := app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, activeQueue.Valid()) activeQueue.Close() proposalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5))} - newProposalMsg := NewMsgSubmitProposal(keep.TestProposal, proposalCoins, input.addrs[0]) + newProposalMsg, err := types.NewMsgSubmitProposal(TestProposal, proposalCoins, addrs[0]) + require.NoError(t, err) res, err := govHandler(ctx, newProposalMsg) require.NoError(t, err) require.NotNil(t, res) - proposalID := GetProposalIDFromBytes(res.Data) + var proposalData types.MsgSubmitProposalResponse + err = proto.Unmarshal(res.Data, &proposalData) + require.NoError(t, err) + + proposalID := proposalData.ProposalId newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - newDepositMsg := NewMsgDeposit(input.addrs[1], proposalID, proposalCoins) + newDepositMsg := types.NewMsgDeposit(addrs[1], proposalID, proposalCoins) res, err = govHandler(ctx, newDepositMsg) require.NoError(t, err) require.NotNil(t, res) newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(input.keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(input.keeper.GetVotingParams(ctx).VotingPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod).Add(app.GovKeeper.GetVotingParams(ctx).VotingPeriod) ctx = ctx.WithBlockHeader(newHeader) - inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - activeQueue = input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + activeQueue = app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.True(t, activeQueue.Valid()) - activeProposalID := GetProposalIDFromBytes(activeQueue.Value()) - proposal, ok := input.keeper.GetProposal(ctx, activeProposalID) + activeProposalID := types.GetProposalIDFromBytes(activeQueue.Value()) + proposal, ok := app.GovKeeper.GetProposal(ctx, activeProposalID) require.True(t, ok) - require.Equal(t, StatusVotingPeriod, proposal.Status) + require.Equal(t, types.StatusVotingPeriod, proposal.Status) activeQueue.Close() - EndBlocker(ctx, input.keeper) + gov.EndBlocker(ctx, app.GovKeeper) - activeQueue = input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) + activeQueue = app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, activeQueue.Valid()) activeQueue.Close() } func TestProposalPassedEndblocker(t *testing.T) { - input := getMockApp(t, 1, GenesisState{}, nil, ProposalHandler) - SortAddresses(input.addrs) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 10, valTokens) - handler := NewHandler(input.keeper) - stakingHandler := staking.NewHandler(input.sk) + SortAddresses(addrs) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) + handler := gov.NewHandler(app.GovKeeper) + stakingHandler := staking.NewHandler(app.StakingKeeper) - valAddr := sdk.ValAddress(input.addrs[0]) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + + valAddr := sdk.ValAddress(addrs[0]) createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10}) - staking.EndBlocker(ctx, input.sk) + staking.EndBlocker(ctx, app.StakingKeeper) - macc := input.keeper.GetGovernanceAccount(ctx) + macc := app.GovKeeper.GetGovernanceAccount(ctx) require.NotNil(t, macc) - initialModuleAccCoins := macc.GetCoins() + initialModuleAccCoins := app.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) - proposal, err := input.keeper.SubmitProposal(ctx, keep.TestProposal) + proposal, err := app.GovKeeper.SubmitProposal(ctx, TestProposal) require.NoError(t, err) proposalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10))} - newDepositMsg := NewMsgDeposit(input.addrs[0], proposal.ProposalID, proposalCoins) + newDepositMsg := types.NewMsgDeposit(addrs[0], proposal.ProposalId, proposalCoins) - res, err := handler(ctx, newDepositMsg) - require.NoError(t, err) - require.NotNil(t, res) + handleAndCheck(t, handler, ctx, newDepositMsg) - macc = input.keeper.GetGovernanceAccount(ctx) + macc = app.GovKeeper.GetGovernanceAccount(ctx) require.NotNil(t, macc) - moduleAccCoins := macc.GetCoins() + moduleAccCoins := app.BankKeeper.GetAllBalances(ctx, macc.GetAddress()) deposits := initialModuleAccCoins.Add(proposal.TotalDeposit...).Add(proposalCoins...) require.True(t, moduleAccCoins.IsEqual(deposits)) - err = input.keeper.AddVote(ctx, proposal.ProposalID, input.addrs[0], OptionYes) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) require.NoError(t, err) newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(input.keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(input.keeper.GetVotingParams(ctx).VotingPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod).Add(app.GovKeeper.GetVotingParams(ctx).VotingPeriod) ctx = ctx.WithBlockHeader(newHeader) - EndBlocker(ctx, input.keeper) + gov.EndBlocker(ctx, app.GovKeeper) - macc = input.keeper.GetGovernanceAccount(ctx) + macc = app.GovKeeper.GetGovernanceAccount(ctx) require.NotNil(t, macc) - require.True(t, macc.GetCoins().IsEqual(initialModuleAccCoins)) + require.True(t, app.BankKeeper.GetAllBalances(ctx, macc.GetAddress()).IsEqual(initialModuleAccCoins)) } func TestEndBlockerProposalHandlerFailed(t *testing.T) { - // hijack the router to one that will fail in a proposal's handler - input := getMockApp(t, 1, GenesisState{}, nil, badProposalHandler) - SortAddresses(input.addrs) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 1, valTokens) - handler := NewHandler(input.keeper) - stakingHandler := staking.NewHandler(input.sk) + SortAddresses(addrs) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) + stakingHandler := staking.NewHandler(app.StakingKeeper) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - valAddr := sdk.ValAddress(input.addrs[0]) + valAddr := sdk.ValAddress(addrs[0]) createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10}) - staking.EndBlocker(ctx, input.sk) + staking.EndBlocker(ctx, app.StakingKeeper) // Create a proposal where the handler will pass for the test proposal // because the value of contextKeyBadProposal is true. ctx = ctx.WithValue(contextKeyBadProposal, true) - proposal, err := input.keeper.SubmitProposal(ctx, keep.TestProposal) + proposal, err := app.GovKeeper.SubmitProposal(ctx, TestProposal) require.NoError(t, err) proposalCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10))) - newDepositMsg := NewMsgDeposit(input.addrs[0], proposal.ProposalID, proposalCoins) + newDepositMsg := types.NewMsgDeposit(addrs[0], proposal.ProposalId, proposalCoins) - res, err := handler(ctx, newDepositMsg) - require.NoError(t, err) - require.NotNil(t, res) + handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg) - err = input.keeper.AddVote(ctx, proposal.ProposalID, input.addrs[0], OptionYes) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionYes) require.NoError(t, err) newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(input.keeper.GetDepositParams(ctx).MaxDepositPeriod).Add(input.keeper.GetVotingParams(ctx).VotingPeriod) + newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod).Add(app.GovKeeper.GetVotingParams(ctx).VotingPeriod) ctx = ctx.WithBlockHeader(newHeader) // Set the contextKeyBadProposal value to false so that the handler will fail @@ -338,5 +360,5 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { ctx = ctx.WithValue(contextKeyBadProposal, false) // validate that the proposal fails/has been rejected - EndBlocker(ctx, input.keeper) + gov.EndBlocker(ctx, app.GovKeeper) } diff --git a/x/gov/alias.go b/x/gov/alias.go deleted file mode 100644 index df5f2cb9ce77..000000000000 --- a/x/gov/alias.go +++ /dev/null @@ -1,155 +0,0 @@ -package gov - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/gov/keeper" - "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -const ( - MaxDescriptionLength = types.MaxDescriptionLength - MaxTitleLength = types.MaxTitleLength - DefaultPeriod = types.DefaultPeriod - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - DefaultParamspace = types.DefaultParamspace - TypeMsgDeposit = types.TypeMsgDeposit - TypeMsgVote = types.TypeMsgVote - TypeMsgSubmitProposal = types.TypeMsgSubmitProposal - StatusNil = types.StatusNil - StatusDepositPeriod = types.StatusDepositPeriod - StatusVotingPeriod = types.StatusVotingPeriod - StatusPassed = types.StatusPassed - StatusRejected = types.StatusRejected - StatusFailed = types.StatusFailed - ProposalTypeText = types.ProposalTypeText - QueryParams = types.QueryParams - QueryProposals = types.QueryProposals - QueryProposal = types.QueryProposal - QueryDeposits = types.QueryDeposits - QueryDeposit = types.QueryDeposit - QueryVotes = types.QueryVotes - QueryVote = types.QueryVote - QueryTally = types.QueryTally - ParamDeposit = types.ParamDeposit - ParamVoting = types.ParamVoting - ParamTallying = types.ParamTallying - OptionEmpty = types.OptionEmpty - OptionYes = types.OptionYes - OptionAbstain = types.OptionAbstain - OptionNo = types.OptionNo - OptionNoWithVeto = types.OptionNoWithVeto -) - -var ( - // functions aliases - RegisterInvariants = keeper.RegisterInvariants - AllInvariants = keeper.AllInvariants - ModuleAccountInvariant = keeper.ModuleAccountInvariant - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - RegisterCodec = types.RegisterCodec - RegisterProposalTypeCodec = types.RegisterProposalTypeCodec - ValidateAbstract = types.ValidateAbstract - NewDeposit = types.NewDeposit - ErrUnknownProposal = types.ErrUnknownProposal - ErrInactiveProposal = types.ErrInactiveProposal - ErrAlreadyActiveProposal = types.ErrAlreadyActiveProposal - ErrInvalidProposalContent = types.ErrInvalidProposalContent - ErrInvalidProposalType = types.ErrInvalidProposalType - ErrInvalidVote = types.ErrInvalidVote - ErrInvalidGenesis = types.ErrInvalidGenesis - ErrNoProposalHandlerExists = types.ErrNoProposalHandlerExists - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - GetProposalIDBytes = types.GetProposalIDBytes - GetProposalIDFromBytes = types.GetProposalIDFromBytes - ProposalKey = types.ProposalKey - ActiveProposalByTimeKey = types.ActiveProposalByTimeKey - ActiveProposalQueueKey = types.ActiveProposalQueueKey - InactiveProposalByTimeKey = types.InactiveProposalByTimeKey - InactiveProposalQueueKey = types.InactiveProposalQueueKey - DepositsKey = types.DepositsKey - DepositKey = types.DepositKey - VotesKey = types.VotesKey - VoteKey = types.VoteKey - SplitProposalKey = types.SplitProposalKey - SplitActiveProposalQueueKey = types.SplitActiveProposalQueueKey - SplitInactiveProposalQueueKey = types.SplitInactiveProposalQueueKey - SplitKeyDeposit = types.SplitKeyDeposit - SplitKeyVote = types.SplitKeyVote - NewMsgSubmitProposal = types.NewMsgSubmitProposal - NewMsgDeposit = types.NewMsgDeposit - NewMsgVote = types.NewMsgVote - ParamKeyTable = types.ParamKeyTable - NewDepositParams = types.NewDepositParams - NewTallyParams = types.NewTallyParams - NewVotingParams = types.NewVotingParams - NewParams = types.NewParams - NewProposal = types.NewProposal - NewRouter = types.NewRouter - ProposalStatusFromString = types.ProposalStatusFromString - ValidProposalStatus = types.ValidProposalStatus - NewTextProposal = types.NewTextProposal - RegisterProposalType = types.RegisterProposalType - ContentFromProposalType = types.ContentFromProposalType - IsValidProposalType = types.IsValidProposalType - ProposalHandler = types.ProposalHandler - NewQueryProposalParams = types.NewQueryProposalParams - NewQueryDepositParams = types.NewQueryDepositParams - NewQueryVoteParams = types.NewQueryVoteParams - NewQueryProposalsParams = types.NewQueryProposalsParams - NewValidatorGovInfo = types.NewValidatorGovInfo - NewTallyResult = types.NewTallyResult - NewTallyResultFromMap = types.NewTallyResultFromMap - EmptyTallyResult = types.EmptyTallyResult - NewVote = types.NewVote - VoteOptionFromString = types.VoteOptionFromString - ValidVoteOption = types.ValidVoteOption - - // variable aliases - ModuleCdc = types.ModuleCdc - ProposalsKeyPrefix = types.ProposalsKeyPrefix - ActiveProposalQueuePrefix = types.ActiveProposalQueuePrefix - InactiveProposalQueuePrefix = types.InactiveProposalQueuePrefix - ProposalIDKey = types.ProposalIDKey - DepositsKeyPrefix = types.DepositsKeyPrefix - VotesKeyPrefix = types.VotesKeyPrefix - ParamStoreKeyDepositParams = types.ParamStoreKeyDepositParams - ParamStoreKeyVotingParams = types.ParamStoreKeyVotingParams - ParamStoreKeyTallyParams = types.ParamStoreKeyTallyParams -) - -type ( - Keeper = keeper.Keeper - Content = types.Content - Handler = types.Handler - Deposit = types.Deposit - Deposits = types.Deposits - GenesisState = types.GenesisState - MsgSubmitProposal = types.MsgSubmitProposal - MsgDeposit = types.MsgDeposit - MsgVote = types.MsgVote - DepositParams = types.DepositParams - TallyParams = types.TallyParams - VotingParams = types.VotingParams - Params = types.Params - Proposal = types.Proposal - Proposals = types.Proposals - ProposalQueue = types.ProposalQueue - ProposalStatus = types.ProposalStatus - TextProposal = types.TextProposal - QueryProposalParams = types.QueryProposalParams - QueryDepositParams = types.QueryDepositParams - QueryVoteParams = types.QueryVoteParams - QueryProposalsParams = types.QueryProposalsParams - ValidatorGovInfo = types.ValidatorGovInfo - TallyResult = types.TallyResult - Vote = types.Vote - Votes = types.Votes - VoteOption = types.VoteOption -) diff --git a/x/gov/client/cli/cli_test.go b/x/gov/client/cli/cli_test.go new file mode 100644 index 000000000000..f0ee9b40e5ac --- /dev/null +++ b/x/gov/client/cli/cli_test.go @@ -0,0 +1,802 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/testutil" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + 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/gov/client/cli" + govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + + val := s.network.Validators[0] + + // create a proposal with deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 1", "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 proposal + _, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "1", "yes") + s.Require().NoError(err) + + // create a proposal without deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 2", "Where is the title!?", types.ProposalTypeText) + 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() +} + +func (s *IntegrationTestSuite) TestCmdParams() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"voting_params":{"voting_period":"172800000000000"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}}`, + }, + { + "text output", + []string{}, + ` +deposit_params: + max_deposit_period: "172800000000000" + min_deposit: + - amount: "10000000" + denom: stake +tally_params: + quorum: "0.334000000000000000" + threshold: "0.500000000000000000" + veto_threshold: "0.334000000000000000" +voting_params: + voting_period: "172800000000000" + `, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestCmdParam() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "voting params", + []string{ + "voting", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + `{"voting_period":"172800000000000"}`, + }, + { + "tally params", + []string{ + "tallying", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + `{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"}`, + }, + { + "deposit params", + []string{ + "deposit", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + `{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParam() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestCmdProposer() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + { + "without proposal id", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + ``, + }, + { + "json output", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + fmt.Sprintf("{\"proposal_id\":\"%s\",\"proposer\":\"%s\"}", "1", val.Address.String()), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryProposer() + 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().Equal(strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdTally() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput types.TallyResult + }{ + { + "without proposal id", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + types.TallyResult{}, + }, + { + "json output", + []string{ + "2", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + types.NewTallyResult(sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)), + }, + { + "json output", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + types.NewTallyResult(s.cfg.BondedTokens, sdk.NewInt(0), sdk.NewInt(0), sdk.NewInt(0)), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryTally() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + var tally types.TallyResult + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &tally), out.String()) + s.Require().Equal(tally, tc.expectedOutput) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdSubmitProposal() { + val := s.network.Validators[0] + invalidProp := `{ + "title": "", + "description": "Where is the title!?", + "type": "Text", + "deposit": "-324foocoin" +}` + invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp) + validProp := fmt.Sprintf(`{ + "title": "Text Proposal", + "description": "Hello, World!", + "type": "Text", + "deposit": "%s" +}`, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431))) + validPropFile := testutil.WriteToNewTempFile(s.T(), validProp) + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid proposal (file)", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagProposal, invalidPropFile.Name()), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "invalid proposal", + []string{ + fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), + fmt.Sprintf("--%s=%s", cli.FlagProposalType, types.ProposalTypeText), + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)).String()), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + true, nil, 0, + }, + { + "valid transaction (file)", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagProposal, validPropFile.Name()), + 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, &sdk.TxResponse{}, 0, + }, + { + "valid transaction", + []string{ + fmt.Sprintf("--%s='Text Proposal'", cli.FlagTitle), + fmt.Sprintf("--%s='Where is the title!?'", cli.FlagDescription), + fmt.Sprintf("--%s=%s", cli.FlagProposalType, types.ProposalTypeText), + fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)).String()), + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewCmdSubmitProposal() + 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) TestCmdGetProposal() { + val := s.network.Validators[0] + + title := "Text Proposal 1" + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get non existing proposal", + []string{ + "10", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "get proposal with json response", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryProposal() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + var proposal types.Proposal + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &proposal), out.String()) + s.Require().Equal(title, proposal.GetTitle()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdGetProposals() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get proposals as json response", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + { + "get proposals with invalid status", + []string{ + "--status=unknown", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryProposals() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + var proposals types.QueryProposalsResponse + + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &proposals), out.String()) + s.Require().Len(proposals.Proposals, 2) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryDeposits() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get deposits of non existing proposal", + []string{ + "10", + }, + true, + }, + { + "get deposits(valid req)", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDeposits() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var deposits types.QueryDepositsResponse + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &deposits), out.String()) + s.Require().Len(deposits.Deposits, 1) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryDeposit() { + val := s.network.Validators[0] + depositAmount := sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens) + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get deposit with no depositer", + []string{ + "1", + }, + true, + }, + { + "get deposit with wrong deposit address", + []string{ + "1", + "wrong address", + }, + true, + }, + { + "get deposit (valid req)", + []string{ + "1", + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDeposit() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var deposit types.Deposit + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &deposit), out.String()) + s.Require().Equal(depositAmount.String(), deposit.Amount.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdDeposit() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + expectedCode uint32 + }{ + { + "without proposal id", + []string{ + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)).String(), //10stake + 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, + }, + { + "without deposit amount", + []string{ + "1", + 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, + }, + { + "deposit on non existing proposal", + []string{ + "10", + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)).String(), //10stake + 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, + }, + { + "deposit on non existing proposal", + []string{ + "1", + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)).String(), //10stake + 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 + var resp sdk.TxResponse + + s.Run(tc.name, func() { + cmd := cli.NewCmdDeposit() + 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(), &resp), out.String()) + s.Require().Equal(tc.expectedCode, resp.Code, out.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryVotes() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get votes with no proposal id", + []string{}, + true, + }, + { + "get votes of non existed proposal", + []string{ + "10", + }, + true, + }, + { + "vote for invalid proposal", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryVotes() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var votes types.QueryVotesResponse + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &votes), out.String()) + s.Require().Len(votes.Votes, 1) + } + }) + } +} + +func (s *IntegrationTestSuite) TestCmdQueryVote() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "get vote of non existing proposal", + []string{ + "10", + val.Address.String(), + }, + true, + }, + { + "get vote by wrong voter", + []string{ + "1", + "wrong address", + }, + true, + }, + { + "vote for valid proposal", + []string{ + "1", + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryVote() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + var vote types.Vote + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &vote), out.String()) + s.Require().Equal(types.OptionYes, vote.Option) + } + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdVote() { + 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, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.NewCmdVote() + 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/parse.go b/x/gov/client/cli/parse.go index dd9415f18d19..90066b645abe 100644 --- a/x/gov/client/cli/parse.go +++ b/x/gov/client/cli/parse.go @@ -5,25 +5,27 @@ import ( "fmt" "io/ioutil" - "github.com/spf13/viper" + "github.com/spf13/pflag" govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" ) -func parseSubmitProposalFlags() (*proposal, error) { +func parseSubmitProposalFlags(fs *pflag.FlagSet) (*proposal, error) { proposal := &proposal{} - proposalFile := viper.GetString(FlagProposal) + proposalFile, _ := fs.GetString(FlagProposal) if proposalFile == "" { - proposal.Title = viper.GetString(FlagTitle) - proposal.Description = viper.GetString(FlagDescription) - proposal.Type = govutils.NormalizeProposalType(viper.GetString(flagProposalType)) - proposal.Deposit = viper.GetString(FlagDeposit) + proposalType, _ := fs.GetString(FlagProposalType) + + proposal.Title, _ = fs.GetString(FlagTitle) + proposal.Description, _ = fs.GetString(FlagDescription) + proposal.Type = govutils.NormalizeProposalType(proposalType) + proposal.Deposit, _ = fs.GetString(FlagDeposit) return proposal, nil } for _, flag := range ProposalFlags { - if viper.GetString(flag) != "" { + if v, _ := fs.GetString(flag); v != "" { return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) } } diff --git a/x/gov/client/cli/parse_test.go b/x/gov/client/cli/parse_test.go index 2b5cfbffd98d..da6aeea71949 100644 --- a/x/gov/client/cli/parse_test.go +++ b/x/gov/client/cli/parse_test.go @@ -1,17 +1,15 @@ package cli import ( - "io/ioutil" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" ) func TestParseSubmitProposalFlags(t *testing.T) { - okJSON, err := ioutil.TempFile("", "proposal") - require.Nil(t, err, "unexpected error") - okJSON.WriteString(` + okJSON := testutil.WriteToNewTempFile(t, ` { "title": "Test Proposal", "description": "My awesome proposal", @@ -20,23 +18,22 @@ func TestParseSubmitProposalFlags(t *testing.T) { } `) - badJSON, err := ioutil.TempFile("", "proposal") - require.Nil(t, err, "unexpected error") - badJSON.WriteString("bad json") + badJSON := testutil.WriteToNewTempFile(t, "bad json") + fs := NewCmdSubmitProposal().Flags() // nonexistent json - viper.Set(FlagProposal, "fileDoesNotExist") - _, err = parseSubmitProposalFlags() + fs.Set(FlagProposal, "fileDoesNotExist") + _, err := parseSubmitProposalFlags(fs) require.Error(t, err) // invalid json - viper.Set(FlagProposal, badJSON.Name()) - _, err = parseSubmitProposalFlags() + fs.Set(FlagProposal, badJSON.Name()) + _, err = parseSubmitProposalFlags(fs) require.Error(t, err) // ok json - viper.Set(FlagProposal, okJSON.Name()) - proposal1, err := parseSubmitProposalFlags() + fs.Set(FlagProposal, okJSON.Name()) + proposal1, err := parseSubmitProposalFlags(fs) require.Nil(t, err, "unexpected error") require.Equal(t, "Test Proposal", proposal1.Title) require.Equal(t, "My awesome proposal", proposal1.Description) @@ -45,19 +42,20 @@ func TestParseSubmitProposalFlags(t *testing.T) { // flags that can't be used with --proposal for _, incompatibleFlag := range ProposalFlags { - viper.Set(incompatibleFlag, "some value") - _, err := parseSubmitProposalFlags() + fs.Set(incompatibleFlag, "some value") + _, err := parseSubmitProposalFlags(fs) require.Error(t, err) - viper.Set(incompatibleFlag, "") + fs.Set(incompatibleFlag, "") } // no --proposal, only flags - viper.Set(FlagProposal, "") - viper.Set(FlagTitle, proposal1.Title) - viper.Set(FlagDescription, proposal1.Description) - viper.Set(flagProposalType, proposal1.Type) - viper.Set(FlagDeposit, proposal1.Deposit) - proposal2, err := parseSubmitProposalFlags() + fs.Set(FlagProposal, "") + fs.Set(FlagTitle, proposal1.Title) + fs.Set(FlagDescription, proposal1.Description) + fs.Set(FlagProposalType, proposal1.Type) + fs.Set(FlagDeposit, proposal1.Deposit) + proposal2, err := parseSubmitProposalFlags(fs) + require.Nil(t, err, "unexpected error") require.Equal(t, proposal1.Title, proposal2.Title) require.Equal(t, proposal1.Description, proposal2.Description) diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index b49f7fe46b7f..8c67cd96e5e1 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -1,17 +1,15 @@ package cli import ( + "context" "fmt" "strconv" "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" @@ -19,7 +17,7 @@ import ( ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group gov queries under a subcommand govQueryCmd := &cobra.Command{ Use: types.ModuleName, @@ -29,24 +27,25 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - govQueryCmd.AddCommand(flags.GetCommands( - GetCmdQueryProposal(queryRoute, cdc), - GetCmdQueryProposals(queryRoute, cdc), - GetCmdQueryVote(queryRoute, cdc), - GetCmdQueryVotes(queryRoute, cdc), - GetCmdQueryParam(queryRoute, cdc), - GetCmdQueryParams(queryRoute, cdc), - GetCmdQueryProposer(queryRoute, cdc), - GetCmdQueryDeposit(queryRoute, cdc), - GetCmdQueryDeposits(queryRoute, cdc), - GetCmdQueryTally(queryRoute, cdc))...) + govQueryCmd.AddCommand( + GetCmdQueryProposal(), + GetCmdQueryProposals(), + GetCmdQueryVote(), + GetCmdQueryVotes(), + GetCmdQueryParam(), + GetCmdQueryParams(), + GetCmdQueryProposer(), + GetCmdQueryDeposit(), + GetCmdQueryDeposits(), + GetCmdQueryTally(), + ) return govQueryCmd } // GetCmdQueryProposal implements the query proposal command. -func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryProposal() *cobra.Command { + cmd := &cobra.Command{ Use: "proposal [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query details of a single proposal", @@ -57,11 +56,15 @@ proposal-id by running "%s query gov proposals". Example: $ %s query gov proposal 1 `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -70,20 +73,26 @@ $ %s query gov proposal 1 } // Query the proposal - res, err := gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + res, err := queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { return err } - var proposal types.Proposal - cdc.MustUnmarshalJSON(res, &proposal) - return cliCtx.PrintOutput(proposal) // nolint:errcheck + return clientCtx.PrintProto(&res.Proposal) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } -// GetCmdQueryProposals implements a query proposals command. -func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { +// GetCmdQueryProposals implements a query proposals command. Command to Get a +// Proposal Information. +func GetCmdQueryProposals() *cobra.Command { cmd := &cobra.Command{ Use: "proposals", Short: "Query proposals with optional filters", @@ -91,90 +100,88 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Query for a all paginated proposals that match optional filters: Example: -$ %s query gov proposals --depositor fetch1skjwj5whet0lpe65qaq4rpq03hjxlwd9q5cpap -$ %s query gov proposals --voter fetch1skjwj5whet0lpe65qaq4rpq03hjxlwd9q5cpap +$ %s query gov proposals --depositor cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk +$ %s query gov proposals --voter cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk $ %s query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Rejected) $ %s query gov proposals --page=2 --limit=100 `, - version.ClientName, version.ClientName, version.ClientName, version.ClientName, + version.AppName, version.AppName, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - bechDepositorAddr := viper.GetString(flagDepositor) - bechVoterAddr := viper.GetString(flagVoter) - strProposalStatus := viper.GetString(flagStatus) - page := viper.GetInt(flags.FlagPage) - limit := viper.GetInt(flags.FlagLimit) - - var depositorAddr sdk.AccAddress - var voterAddr sdk.AccAddress - var proposalStatus types.ProposalStatus + bechDepositorAddr, _ := cmd.Flags().GetString(flagDepositor) + bechVoterAddr, _ := cmd.Flags().GetString(flagVoter) + strProposalStatus, _ := cmd.Flags().GetString(flagStatus) - params := types.NewQueryProposalsParams(page, limit, proposalStatus, voterAddr, depositorAddr) + var proposalStatus types.ProposalStatus if len(bechDepositorAddr) != 0 { - depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) + _, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { return err } - params.Depositor = depositorAddr } if len(bechVoterAddr) != 0 { - voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) + _, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { return err } - params.Voter = voterAddr } if len(strProposalStatus) != 0 { - proposalStatus, err := types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus)) + proposalStatus1, err := types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus)) + proposalStatus = proposalStatus1 if err != nil { return err } - params.ProposalStatus = proposalStatus } - bz, err := cdc.MarshalJSON(params) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposals", queryRoute), bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var matchingProposals types.Proposals - err = cdc.UnmarshalJSON(res, &matchingProposals) + res, err := queryClient.Proposals( + context.Background(), + &types.QueryProposalsRequest{ + ProposalStatus: proposalStatus, + Voter: bechVoterAddr, + Depositor: bechDepositorAddr, + Pagination: pageReq, + }, + ) if err != nil { return err } - if len(matchingProposals) == 0 { - return fmt.Errorf("no matching proposals found") + if len(res.GetProposals()) == 0 { + return fmt.Errorf("no proposals found") } - return cliCtx.PrintOutput(matchingProposals) // nolint:errcheck + return clientCtx.PrintProto(res) }, } - cmd.Flags().Int(flags.FlagPage, 1, "pagination page of proposals to to query for") - cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of proposals to query for") cmd.Flags().String(flagDepositor, "", "(optional) filter by proposals deposited on by depositor") cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") + flags.AddPaginationFlagsToCmd(cmd, "proposals") + flags.AddQueryFlagsToCmd(cmd) return cmd } -// Command to Get a Proposal Information -// GetCmdQueryVote implements the query proposal vote command. -func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryVote implements the query proposal vote command. Command to Get a +// Proposal Information. +func GetCmdQueryVote() *cobra.Command { + cmd := &cobra.Command{ Use: "vote [proposal-id] [voter-addr]", Args: cobra.ExactArgs(2), Short: "Query details of a single vote", @@ -184,11 +191,15 @@ func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { Example: $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -197,7 +208,10 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk } // check to see if the proposal is in the store - _, err = gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + _, err = queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } @@ -207,42 +221,39 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk return err } - params := types.NewQueryVoteParams(proposalID, voterAddr) - bz, err := cdc.MarshalJSON(params) + res, err := queryClient.Vote( + context.Background(), + &types.QueryVoteRequest{ProposalId: proposalID, Voter: args[1]}, + ) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/vote", queryRoute), bz) - if err != nil { - return err - } - - var vote types.Vote - - // XXX: Allow the decoding to potentially fail as the vote may have been - // pruned from state. If so, decoding will fail and so we need to check the - // Empty() case. Consider updating Vote JSON decoding to not fail when empty. - _ = cdc.UnmarshalJSON(res, &vote) - + vote := res.GetVote() if vote.Empty() { - res, err = gcutils.QueryVoteByTxQuery(cliCtx, params) + params := types.NewQueryVoteParams(proposalID, voterAddr) + resByTxQuery, err := gcutils.QueryVoteByTxQuery(clientCtx, params) + if err != nil { return err } - if err := cdc.UnmarshalJSON(res, &vote); err != nil { + if err := clientCtx.JSONMarshaler.UnmarshalJSON(resByTxQuery, &vote); err != nil { return err } } - return cliCtx.PrintOutput(vote) + return clientCtx.PrintProto(&res.Vote) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryVotes implements the command to query for proposal votes. -func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryVotes() *cobra.Command { cmd := &cobra.Command{ Use: "votes [proposal-id]", Args: cobra.ExactArgs(1), @@ -254,11 +265,15 @@ Example: $ %[1]s query gov votes 1 $ %[1]s query gov votes 1 --page=2 --limit=100 `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -266,49 +281,63 @@ $ %[1]s query gov votes 1 --page=2 --limit=100 return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) } - page := viper.GetInt(flags.FlagPage) - limit := viper.GetInt(flags.FlagLimit) - - params := types.NewQueryProposalVotesParams(proposalID, page, limit) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - // check to see if the proposal is in the store - res, err := gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + proposalRes, err := queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } - var proposal types.Proposal - cdc.MustUnmarshalJSON(res, &proposal) - - propStatus := proposal.Status + propStatus := proposalRes.GetProposal().Status if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) { - res, err = gcutils.QueryVotesByTxQuery(cliCtx, params) - } else { - res, _, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz) + page, _ := cmd.Flags().GetInt(flags.FlagPage) + limit, _ := cmd.Flags().GetInt(flags.FlagLimit) + + params := types.NewQueryProposalVotesParams(proposalID, page, limit) + resByTxQuery, err := gcutils.QueryVotesByTxQuery(clientCtx, params) + if err != nil { + return err + } + + var votes types.Votes + // TODO migrate to use JSONMarshaler (implement MarshalJSONArray + // or wrap lists of proto.Message in some other message) + clientCtx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &votes) + return clientCtx.PrintObjectLegacy(votes) + } + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var votes types.Votes - cdc.MustUnmarshalJSON(res, &votes) - return cliCtx.PrintOutput(votes) + res, err := queryClient.Votes( + context.Background(), + &types.QueryVotesRequest{ProposalId: proposalID, Pagination: pageReq}, + ) + + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, } - cmd.Flags().Int(flags.FlagPage, 1, "pagination page of votes to to query for") - cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of votes to query for") + + flags.AddPaginationFlagsToCmd(cmd, "votes") + flags.AddQueryFlagsToCmd(cmd) + return cmd } -// Command to Get a specific Deposit Information -// GetCmdQueryDeposit implements the query proposal deposit command. -func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryDeposit implements the query proposal deposit command. Command to +// get a specific Deposit Information +func GetCmdQueryDeposit() *cobra.Command { + cmd := &cobra.Command{ Use: "deposit [proposal-id] [depositer-addr]", Args: cobra.ExactArgs(2), Short: "Query details of a deposit", @@ -316,13 +345,17 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Query details for a single proposal deposit on a proposal by its identifier. Example: -$ %s query gov deposit 1 fetch1skjwj5whet0lpe65qaq4rpq03hjxlwd9q5cpap +$ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -331,7 +364,10 @@ $ %s query gov deposit 1 fetch1skjwj5whet0lpe65qaq4rpq03hjxlwd9q5cpap } // check to see if the proposal is in the store - _, err = gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + _, err = queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } @@ -341,36 +377,36 @@ $ %s query gov deposit 1 fetch1skjwj5whet0lpe65qaq4rpq03hjxlwd9q5cpap return err } - params := types.NewQueryDepositParams(proposalID, depositorAddr) - bz, err := cdc.MarshalJSON(params) + res, err := queryClient.Deposit( + context.Background(), + &types.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, + ) if err != nil { return err } - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposit", queryRoute), bz) - if err != nil { - return err - } - - var deposit types.Deposit - cdc.MustUnmarshalJSON(res, &deposit) - + deposit := res.GetDeposit() if deposit.Empty() { - res, err = gcutils.QueryDepositByTxQuery(cliCtx, params) + params := types.NewQueryDepositParams(proposalID, depositorAddr) + resByTxQuery, err := gcutils.QueryDepositByTxQuery(clientCtx, params) if err != nil { return err } - cdc.MustUnmarshalJSON(res, &deposit) + clientCtx.JSONMarshaler.MustUnmarshalJSON(resByTxQuery, &deposit) } - return cliCtx.PrintOutput(deposit) + return clientCtx.PrintProto(&deposit) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryDeposits implements the command to query for proposal deposits. -func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryDeposits() *cobra.Command { + cmd := &cobra.Command{ Use: "deposits [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query deposits on a proposal", @@ -381,11 +417,15 @@ You can find the proposal-id by running "%s query gov proposals". Example: $ %s query gov deposits 1 `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -393,42 +433,58 @@ $ %s query gov deposits 1 return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) } - params := types.NewQueryProposalParams(proposalID) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - // check to see if the proposal is in the store - res, err := gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + proposalRes, err := queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { - return fmt.Errorf("failed to fetch proposal with id %d: %s", proposalID, err) + return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } - var proposal types.Proposal - cdc.MustUnmarshalJSON(res, &proposal) - - propStatus := proposal.Status + propStatus := proposalRes.GetProposal().Status if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) { - res, err = gcutils.QueryDepositsByTxQuery(cliCtx, params) - } else { - res, _, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz) + params := types.NewQueryProposalParams(proposalID) + resByTxQuery, err := gcutils.QueryDepositsByTxQuery(clientCtx, params) + if err != nil { + return err + } + + var dep types.Deposits + // TODO migrate to use JSONMarshaler (implement MarshalJSONArray + // or wrap lists of proto.Message in some other message) + clientCtx.LegacyAmino.MustUnmarshalJSON(resByTxQuery, &dep) + + return clientCtx.PrintObjectLegacy(dep) + } + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err } + res, err := queryClient.Deposits( + context.Background(), + &types.QueryDepositsRequest{ProposalId: proposalID, Pagination: pageReq}, + ) + if err != nil { return err } - var dep types.Deposits - cdc.MustUnmarshalJSON(res, &dep) - return cliCtx.PrintOutput(dep) + return clientCtx.PrintProto(res) }, } + + flags.AddPaginationFlagsToCmd(cmd, "deposits") + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryTally implements the command to query for proposal tally result. -func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryTally() *cobra.Command { + cmd := &cobra.Command{ Use: "tally [proposal-id]", Args: cobra.ExactArgs(1), Short: "Get the tally of a proposal vote", @@ -439,11 +495,15 @@ the proposal-id by running "%s query gov proposals". Example: $ %s query gov tally 1 `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -452,34 +512,35 @@ $ %s query gov tally 1 } // check to see if the proposal is in the store - _, err = gcutils.QueryProposalByID(proposalID, cliCtx, queryRoute) + _, err = queryClient.Proposal( + context.Background(), + &types.QueryProposalRequest{ProposalId: proposalID}, + ) if err != nil { return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err) } - // Construct query - params := types.NewQueryProposalParams(proposalID) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - // Query store - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/tally", queryRoute), bz) + res, err := queryClient.TallyResult( + context.Background(), + &types.QueryTallyResultRequest{ProposalId: proposalID}, + ) if err != nil { return err } - var tally types.TallyResult - cdc.MustUnmarshalJSON(res, &tally) - return cliCtx.PrintOutput(tally) + return clientCtx.PrintProto(&res.Tally) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } -// GetCmdQueryProposal implements the query proposal command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryParams implements the query params command. +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ Use: "params", Short: "Query the parameters of the governance process", Long: strings.TrimSpace( @@ -488,40 +549,60 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { Example: $ %s query gov params `, - version.ClientName, + version.AppName, ), ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - tp, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/tallying", queryRoute), nil) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } - dp, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/deposit", queryRoute), nil) + queryClient := types.NewQueryClient(clientCtx) + + // Query store for all 3 params + votingRes, err := queryClient.Params( + context.Background(), + &types.QueryParamsRequest{ParamsType: "voting"}, + ) if err != nil { return err } - vp, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/voting", queryRoute), nil) + + tallyRes, err := queryClient.Params( + context.Background(), + &types.QueryParamsRequest{ParamsType: "tallying"}, + ) if err != nil { return err } - var tallyParams types.TallyParams - cdc.MustUnmarshalJSON(tp, &tallyParams) - var depositParams types.DepositParams - cdc.MustUnmarshalJSON(dp, &depositParams) - var votingParams types.VotingParams - cdc.MustUnmarshalJSON(vp, &votingParams) + depositRes, err := queryClient.Params( + context.Background(), + &types.QueryParamsRequest{ParamsType: "deposit"}, + ) + if err != nil { + return err + } + + params := types.NewParams( + votingRes.GetVotingParams(), + tallyRes.GetTallyParams(), + depositRes.GetDepositParams(), + ) - return cliCtx.PrintOutput(types.NewParams(votingParams, tallyParams, depositParams)) + return clientCtx.PrintObjectLegacy(params) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } -// GetCmdQueryProposal implements the query proposal command. -func GetCmdQueryParam(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetCmdQueryParam implements the query param command. +func GetCmdQueryParam() *cobra.Command { + cmd := &cobra.Command{ Use: "param [param-type]", Args: cobra.ExactArgs(1), Short: "Query the parameters (voting|tallying|deposit) of the governance process", @@ -533,43 +614,49 @@ $ %s query gov param voting $ %s query gov param tallying $ %s query gov param deposit `, - version.ClientName, version.ClientName, version.ClientName, + version.AppName, version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) // Query store - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, args[0]), nil) + res, err := queryClient.Params( + context.Background(), + &types.QueryParamsRequest{ParamsType: args[0]}, + ) if err != nil { return err } + var out fmt.Stringer switch args[0] { case "voting": - var param types.VotingParams - cdc.MustUnmarshalJSON(res, ¶m) - out = param + out = res.GetVotingParams() case "tallying": - var param types.TallyParams - cdc.MustUnmarshalJSON(res, ¶m) - out = param + out = res.GetTallyParams() case "deposit": - var param types.DepositParams - cdc.MustUnmarshalJSON(res, ¶m) - out = param + out = res.GetDepositParams() default: return fmt.Errorf("argument must be one of (voting|tallying|deposit), was %s", args[0]) } - return cliCtx.PrintOutput(out) + return clientCtx.PrintObjectLegacy(out) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryProposer implements the query proposer command. -func GetCmdQueryProposer(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryProposer() *cobra.Command { + cmd := &cobra.Command{ Use: "proposer [proposal-id]", Args: cobra.ExactArgs(1), Short: "Query the proposer of a governance proposal", @@ -579,11 +666,14 @@ func GetCmdQueryProposer(queryRoute string, cdc *codec.Codec) *cobra.Command { Example: $ %s query gov proposer 1 `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } // validate that the proposalID is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -591,14 +681,16 @@ $ %s query gov proposer 1 return fmt.Errorf("proposal-id %s is not a valid uint", args[0]) } - prop, err := gcutils.QueryProposerByTxQuery(cliCtx, proposalID) + prop, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID) if err != nil { return err } - return cliCtx.PrintOutput(prop) + return clientCtx.PrintObjectLegacy(prop) }, } -} -// DONTCOVER + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 3946f9780c55..16ca7f7c68bc 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -1,7 +1,6 @@ package cli import ( - "bufio" "fmt" "strconv" "strings" @@ -9,13 +8,10 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" govutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -24,7 +20,7 @@ import ( const ( FlagTitle = "title" FlagDescription = "description" - flagProposalType = "type" + FlagProposalType = "type" FlagDeposit = "deposit" flagVoter = "voter" flagDepositor = "depositor" @@ -45,16 +41,16 @@ type proposal struct { var ProposalFlags = []string{ FlagTitle, FlagDescription, - flagProposalType, + FlagProposalType, FlagDeposit, } -// GetTxCmd returns the transaction commands for this module +// NewTxCmd returns the transaction commands for this module // governance ModuleClient is slightly different from other ModuleClients in that // it contains a slice of "proposal" child commands. These commands are respective // to proposal type handlers that are implemented in other modules but are mounted // under the governance CLI (eg. parameter change proposals). -func GetTxCmd(storeKey string, cdc *codec.Codec, pcmds []*cobra.Command) *cobra.Command { +func NewTxCmd(propCmds []*cobra.Command) *cobra.Command { govTxCmd := &cobra.Command{ Use: types.ModuleName, Short: "Governance transactions subcommands", @@ -63,22 +59,23 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, pcmds []*cobra.Command) *cobra. RunE: client.ValidateCmd, } - cmdSubmitProp := GetCmdSubmitProposal(cdc) - for _, pcmd := range pcmds { - cmdSubmitProp.AddCommand(flags.PostCommands(pcmd)[0]) + cmdSubmitProp := NewCmdSubmitProposal() + for _, propCmd := range propCmds { + flags.AddTxFlagsToCmd(propCmd) + cmdSubmitProp.AddCommand(propCmd) } - govTxCmd.AddCommand(flags.PostCommands( - GetCmdDeposit(cdc), - GetCmdVote(cdc), + govTxCmd.AddCommand( + NewCmdDeposit(), + NewCmdVote(), cmdSubmitProp, - )...) + ) return govTxCmd } -// GetCmdSubmitProposal implements submitting a proposal transaction command. -func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command { +// NewCmdSubmitProposal implements submitting a proposal transaction command. +func NewCmdSubmitProposal() *cobra.Command { cmd := &cobra.Command{ Use: "submit-proposal", Short: "Submit a proposal along with an initial deposit", @@ -102,47 +99,53 @@ Which is equivalent to: $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="10test" --from mykey `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - proposal, err := parseSubmitProposalFlags() + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } - amount, err := sdk.ParseCoins(proposal.Deposit) + proposal, err := parseSubmitProposalFlags(cmd.Flags()) + if err != nil { + return fmt.Errorf("failed to parse proposal: %w", err) + } + + amount, err := sdk.ParseCoinsNormalized(proposal.Deposit) if err != nil { return err } content := types.ContentFromProposalType(proposal.Title, proposal.Description, proposal.Type) - msg := types.NewMsgSubmitProposal(content, amount, cliCtx.GetFromAddress()) - if err := msg.ValidateBasic(); err != nil { - return err + msg, err := types.NewMsgSubmitProposal(content, amount, clientCtx.GetFromAddress()) + if err != nil { + return fmt.Errorf("invalid message: %w", err) } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err = msg.ValidateBasic(); err != nil { + return fmt.Errorf("message validation failed: %w", err) + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - cmd.Flags().String(FlagTitle, "", "title of proposal") - cmd.Flags().String(FlagDescription, "", "description of proposal") - cmd.Flags().String(flagProposalType, "", "proposalType of proposal, types: text/parameter_change/software_upgrade") - cmd.Flags().String(FlagDeposit, "", "deposit of proposal") - cmd.Flags().String(FlagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)") + cmd.Flags().String(FlagTitle, "", "The proposal title") + cmd.Flags().String(FlagDescription, "", "The proposal description") + cmd.Flags().String(FlagProposalType, "", "The proposal Type") + cmd.Flags().String(FlagDeposit, "", "The proposal deposit") + cmd.Flags().String(FlagProposal, "", "Proposal file path (if this path is given, other proposal flags are ignored)") + flags.AddTxFlagsToCmd(cmd) return cmd } -// GetCmdDeposit implements depositing tokens for an active proposal. -func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// NewCmdDeposit implements depositing tokens for an active proposal. +func NewCmdDeposit() *cobra.Command { + cmd := &cobra.Command{ Use: "deposit [proposal-id] [deposit]", Args: cobra.ExactArgs(2), Short: "Deposit tokens for an active proposal", @@ -153,13 +156,14 @@ find the proposal-id by running "%s query gov proposals". Example: $ %s tx gov deposit 1 10stake --from mykey `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -168,10 +172,10 @@ $ %s tx gov deposit 1 10stake --from mykey } // Get depositor address - from := cliCtx.GetFromAddress() + from := clientCtx.GetFromAddress() // Get amount of coins - amount, err := sdk.ParseCoins(args[1]) + amount, err := sdk.ParseCoinsNormalized(args[1]) if err != nil { return err } @@ -182,14 +186,18 @@ $ %s tx gov deposit 1 10stake --from mykey return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } -// GetCmdVote implements creating a new vote command. -func GetCmdVote(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// NewCmdVote implements creating a new vote command. +func NewCmdVote() *cobra.Command { + cmd := &cobra.Command{ Use: "vote [proposal-id] [option]", Args: cobra.ExactArgs(2), Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain", @@ -201,16 +209,16 @@ find the proposal-id by running "%s query gov proposals". Example: $ %s tx gov vote 1 yes --from mykey `, - version.ClientName, version.ClientName, + version.AppName, version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } // Get voting address - from := cliCtx.GetFromAddress() + from := clientCtx.GetFromAddress() // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -231,9 +239,11 @@ $ %s tx gov vote 1 yes --from mykey return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } -} -// DONTCOVER + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/gov/client/proposal_handler.go b/x/gov/client/proposal_handler.go index cdfc0115e094..61ee3d8c3be5 100644 --- a/x/gov/client/proposal_handler.go +++ b/x/gov/client/proposal_handler.go @@ -3,16 +3,15 @@ package client import ( "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/x/gov/client/rest" ) // function to create the rest handler -type RESTHandlerFn func(context.CLIContext) rest.ProposalRESTHandler +type RESTHandlerFn func(client.Context) rest.ProposalRESTHandler // function to create the cli handler -type CLIHandlerFn func(*codec.Codec) *cobra.Command +type CLIHandlerFn func() *cobra.Command // The combined type for a proposal handler for both cli and rest type ProposalHandler struct { diff --git a/x/gov/client/rest/grpc_query_test.go b/x/gov/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..027ce2962ed0 --- /dev/null +++ b/x/gov/client/rest/grpc_query_test.go @@ -0,0 +1,448 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + + val := s.network.Validators[0] + + // create a proposal with deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 1", "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 proposal + _, err = govtestutil.MsgVote(val.ClientCtx, val.Address.String(), "1", "yes") + s.Require().NoError(err) + + // create a proposal without deposit + _, err = govtestutil.MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 2", "Where is the title!?", types.ProposalTypeText) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) TestGetProposalGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "empty proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s", val.APIAddress, ""), + true, + }, + { + "get non existing proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s", val.APIAddress, "10"), + true, + }, + { + "get proposal with id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s", val.APIAddress, "1"), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var proposal types.QueryProposalResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &proposal) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotNil(proposal.Proposal) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetProposalsGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + headers map[string]string + wantNumProposals int + expErr bool + }{ + { + "get proposals with height 1", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals", val.APIAddress), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + 0, + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals", val.APIAddress), + map[string]string{}, + 2, + false, + }, + { + "valid request with filter by status", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals?proposal_status=1", val.APIAddress), + map[string]string{}, + 1, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + var proposals types.QueryProposalsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &proposals) + + if tc.expErr { + s.Require().Empty(proposals.Proposals) + } else { + s.Require().NoError(err) + s.Require().Len(proposals.Proposals, tc.wantNumProposals) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetProposalVoteGRPC() { + val := s.network.Validators[0] + + voterAddressBech32 := val.Address.String() + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "empty proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "", voterAddressBech32), + true, + }, + { + "get non existing proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "10", voterAddressBech32), + true, + }, + { + "get proposal with wrong voter address", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", "wrongVoterAddress"), + true, + }, + { + "get proposal with id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes/%s", val.APIAddress, "1", voterAddressBech32), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var vote types.QueryVoteResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &vote) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotEmpty(vote.Vote) + s.Require().Equal(types.OptionYes, vote.Vote.Option) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetProposalVotesGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "votes with empty proposal id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes", val.APIAddress, ""), + true, + }, + { + "get votes with valid id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/votes", val.APIAddress, "1"), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var votes types.QueryVotesResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &votes) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Len(votes.Votes, 1) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetProposalDepositGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "get deposit with empty proposal id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits/%s", val.APIAddress, "", val.Address.String()), + true, + }, + { + "get deposit of non existing proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits/%s", val.APIAddress, "10", val.Address.String()), + true, + }, + { + "get deposit with wrong depositer address", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits/%s", val.APIAddress, "1", "wrongDepositerAddress"), + true, + }, + { + "get deposit valid request", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits/%s", val.APIAddress, "1", val.Address.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var deposit types.QueryDepositResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &deposit) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotEmpty(deposit.Deposit) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetProposalDepositsGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "get deposits with empty proposal id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits", val.APIAddress, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/deposits", val.APIAddress, "1"), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var deposits types.QueryDepositsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &deposits) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Len(deposits.Deposits, 1) + s.Require().NotEmpty(deposits.Deposits[0]) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetTallyGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + }{ + { + "get tally with no proposal id", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/tally", val.APIAddress, ""), + true, + }, + { + "get tally with non existing proposal", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/tally", val.APIAddress, "10"), + true, + }, + { + "get tally valid request", + fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%s/tally", val.APIAddress, "1"), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var tally types.QueryTallyResultResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &tally) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotEmpty(tally.Tally) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetParamsGRPC() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + respType proto.Message + expectResp proto.Message + }{ + { + "request params with empty params type", + fmt.Sprintf("%s/cosmos/gov/v1beta1/params/%s", val.APIAddress, ""), + true, nil, nil, + }, + { + "get deposit params", + fmt.Sprintf("%s/cosmos/gov/v1beta1/params/%s", val.APIAddress, types.ParamDeposit), + false, + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + DepositParams: types.DefaultDepositParams(), + }, + }, + { + "get vote params", + fmt.Sprintf("%s/cosmos/gov/v1beta1/params/%s", val.APIAddress, types.ParamVoting), + false, + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + VotingParams: types.DefaultVotingParams(), + }, + }, + { + "get tally params", + fmt.Sprintf("%s/cosmos/gov/v1beta1/params/%s", val.APIAddress, types.ParamTallying), + false, + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + TallyParams: types.DefaultTallyParams(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectResp.String(), tc.respType.String()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/gov/client/rest/query.go b/x/gov/client/rest/query.go index 3eb5800d7a99..ffdbd8bc0290 100644 --- a/x/gov/client/rest/query.go +++ b/x/gov/client/rest/query.go @@ -7,47 +7,46 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc(fmt.Sprintf("/gov/parameters/{%s}", RestParamsType), queryParamsHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/proposer", RestProposalID), queryProposerHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cliCtx)).Methods("GET") +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { + r.HandleFunc(fmt.Sprintf("/gov/parameters/{%s}", RestParamsType), queryParamsHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/proposer", RestProposalID), queryProposerHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(clientCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(clientCtx)).Methods("GET") } -func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryParamsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) paramType := vars[RestParamsType] - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", types.QueryParams, paramType), nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", types.QueryParams, paramType), nil) + if rest.CheckNotFoundError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -63,31 +62,29 @@ func queryProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryProposalParams(proposalID) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData("custom/gov/proposal", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData("custom/gov/proposal", bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryDepositsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -97,28 +94,25 @@ func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryProposalParams(proposalID) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, _, err := clientCtx.QueryWithData("custom/gov/proposal", bz) + if rest.CheckInternalServerError(w, err) { return } var proposal types.Proposal - if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, clientCtx.LegacyAmino.UnmarshalJSON(res, &proposal)) { return } @@ -126,21 +120,20 @@ func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // as they're no longer in state. propStatus := proposal.Status if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) { - res, err = gcutils.QueryDepositsByTxQuery(cliCtx, params) + res, err = gcutils.QueryDepositsByTxQuery(clientCtx, params) } else { - res, _, err = cliCtx.QueryWithData("custom/gov/deposits", bz) + res, _, err = clientCtx.QueryWithData("custom/gov/deposits", bz) } - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, res) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryProposerHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -150,22 +143,21 @@ func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, err := gcutils.QueryProposerByTxQuery(cliCtx, proposalID) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, res) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryDepositHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -189,33 +181,29 @@ func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryDepositParams(proposalID, depositorAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, _, err := cliCtx.QueryWithData("custom/gov/deposit", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, _, err := clientCtx.QueryWithData("custom/gov/deposit", bz) + if rest.CheckInternalServerError(w, err) { return } var deposit types.Deposit - if err := cliCtx.Codec.UnmarshalJSON(res, &deposit); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, clientCtx.LegacyAmino.UnmarshalJSON(res, &deposit)) { return } @@ -223,31 +211,29 @@ func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // which case the deposit would be removed from state and should be queried // for directly via a txs query. if deposit.Empty() { - bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(types.NewQueryProposalParams(proposalID)) + if rest.CheckBadRequestError(w, err) { return } - res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz) + res, _, err = clientCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { err := fmt.Errorf("proposalID %d does not exist", proposalID) rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - res, err = gcutils.QueryDepositByTxQuery(cliCtx, params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, err = gcutils.QueryDepositByTxQuery(clientCtx, params) + if rest.CheckInternalServerError(w, err) { return } } - rest.PostProcessResponse(w, cliCtx, res) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryVoteHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -276,28 +262,25 @@ func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryVoteParams(proposalID, voterAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, _, err := cliCtx.QueryWithData("custom/gov/vote", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, _, err := clientCtx.QueryWithData("custom/gov/vote", bz) + if rest.CheckInternalServerError(w, err) { return } var vote types.Vote - if err := cliCtx.Codec.UnmarshalJSON(res, &vote); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, clientCtx.LegacyAmino.UnmarshalJSON(res, &vote)) { return } @@ -305,36 +288,33 @@ func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // which case the vote would be removed from state and should be queried for // directly via a txs query. if vote.Empty() { - bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(types.NewQueryProposalParams(proposalID)) + if rest.CheckBadRequestError(w, err) { return } - res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz) + res, _, err = clientCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { err := fmt.Errorf("proposalID %d does not exist", proposalID) rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - res, err = gcutils.QueryVoteByTxQuery(cliCtx, params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, err = gcutils.QueryVoteByTxQuery(clientCtx, params) + if rest.CheckInternalServerError(w, err) { return } } - rest.PostProcessResponse(w, cliCtx, res) + rest.PostProcessResponse(w, clientCtx, res) } } // todo: Split this functionality into helper functions to remove the above -func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryVotesOnProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + _, page, limit, err := rest.ParseHTTPArgs(r) + if rest.CheckBadRequestError(w, err) { return } @@ -352,59 +332,59 @@ func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQueryProposalVotesParams(proposalID, page, limit) - - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(types.NewQueryProposalParams(proposalID)) + if rest.CheckBadRequestError(w, err) { return } - res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, _, err := clientCtx.QueryWithData("custom/gov/proposal", bz) + if rest.CheckInternalServerError(w, err) { return } var proposal types.Proposal - if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, clientCtx.LegacyAmino.UnmarshalJSON(res, &proposal)) { return } // For inactive proposals we must query the txs directly to get the votes // as they're no longer in state. + params := types.NewQueryProposalVotesParams(proposalID, page, limit) + propStatus := proposal.Status if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) { - res, err = gcutils.QueryVotesByTxQuery(cliCtx, params) + res, err = gcutils.QueryVotesByTxQuery(clientCtx, params) } else { - res, _, err = cliCtx.QueryWithData("custom/gov/votes", bz) + bz, err = clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { + return + } + + res, _, err = clientCtx.QueryWithData("custom/gov/votes", bz) } - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, res) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query list of governance proposals -func queryProposalsWithParameterFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryProposalsWithParameterFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } @@ -417,49 +397,44 @@ func queryProposalsWithParameterFn(cliCtx context.CLIContext) http.HandlerFunc { if v := r.URL.Query().Get(RestVoter); len(v) != 0 { voterAddr, err = sdk.AccAddressFromBech32(v) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } } if v := r.URL.Query().Get(RestDepositor); len(v) != 0 { depositorAddr, err = sdk.AccAddressFromBech32(v) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } } if v := r.URL.Query().Get(RestProposalStatus); len(v) != 0 { proposalStatus, err = types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(v)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } } params := types.NewQueryProposalsParams(page, limit, proposalStatus, voterAddr, depositorAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryProposals) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // todo: Split this functionality into helper functions to remove the above -func queryTallyOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryTallyOnProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -475,26 +450,24 @@ func queryTallyOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryProposalParams(proposalID) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData("custom/gov/tally", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData("custom/gov/tally", bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index eca37e364416..f11798e96760 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -5,7 +5,8 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" ) @@ -28,10 +29,10 @@ type ProposalRESTHandler struct { Handler func(http.ResponseWriter, *http.Request) } -// RegisterRoutes - Central function to define routes that get registered by the main application -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, phs []ProposalRESTHandler) { - registerQueryRoutes(cliCtx, r) - registerTxRoutes(cliCtx, r, phs) +func RegisterHandlers(clientCtx client.Context, rtr *mux.Router, phs []ProposalRESTHandler) { + r := clientrest.WithHTTPDeprecationHeaders(rtr) + registerQueryRoutes(clientCtx, r) + registerTxHandlers(clientCtx, r, phs) } // PostProposalReq defines the properties of a proposal request's body. diff --git a/x/gov/client/rest/rest_test.go b/x/gov/client/rest/rest_test.go new file mode 100644 index 000000000000..df7f2e4cc6ef --- /dev/null +++ b/x/gov/client/rest/rest_test.go @@ -0,0 +1,166 @@ +// +build norace + +package rest_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +func (s *IntegrationTestSuite) TestLegacyGetAllProposals() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + numProposals int + expErr bool + expErrMsg string + }{ + { + "get all existing proposals", + fmt.Sprintf("%s/gov/proposals", val.APIAddress), + 2, false, "", + }, + { + "get proposals in deposit period", + fmt.Sprintf("%s/gov/proposals?status=deposit_period", val.APIAddress), + 1, false, "", + }, + { + "get proposals in voting period", + fmt.Sprintf("%s/gov/proposals?status=voting_period", val.APIAddress), + 1, false, "", + }, + { + "wrong status parameter", + fmt.Sprintf("%s/gov/proposals?status=invalidstatus", val.APIAddress), + 0, true, "'invalidstatus' is not a valid proposal status", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + respJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + if tc.expErr { + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &errResp)) + s.Require().Equal(errResp.Error, tc.expErrMsg) + } else { + var resp = rest.ResponseWithHeight{} + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &resp) + s.Require().NoError(err) + + // Check results. + var proposals types.Proposals + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(resp.Result, &proposals)) + s.Require().Equal(tc.numProposals, len(proposals)) + } + }) + } +} + +func (s *IntegrationTestSuite) TestLegacyGetVote() { + val := s.network.Validators[0] + voterAddressBech32 := val.Address.String() + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "get non existing proposal", + fmt.Sprintf("%s/gov/proposals/%s/votes/%s", val.APIAddress, "10", voterAddressBech32), + true, "proposalID 10 does not exist", + }, + { + "get proposal with wrong voter address", + fmt.Sprintf("%s/gov/proposals/%s/votes/%s", val.APIAddress, "1", "wrongVoterAddress"), + true, "decoding bech32 failed: string not all lowercase or all uppercase", + }, + { + "get proposal with id", + fmt.Sprintf("%s/gov/proposals/%s/votes/%s", val.APIAddress, "1", voterAddressBech32), + false, "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + respJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + if tc.expErr { + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &errResp)) + + s.Require().Equal(errResp.Error, tc.expErrMsg) + } else { + var resp = rest.ResponseWithHeight{} + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &resp) + s.Require().NoError(err) + + // Check result is not empty. + var vote types.Vote + 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) + } + }) + } +} + +func (s *IntegrationTestSuite) TestLegacyGetVotes() { + val := s.network.Validators[0] + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "votes with empty proposal id", + fmt.Sprintf("%s/gov/proposals/%s/votes", val.APIAddress, ""), + true, "'votes' is not a valid uint64", + }, + { + "get votes with valid id", + fmt.Sprintf("%s/gov/proposals/%s/votes", val.APIAddress, "1"), + false, "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + respJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + if tc.expErr { + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &errResp)) + + s.Require().Equal(errResp.Error, tc.expErrMsg) + } else { + var resp = rest.ResponseWithHeight{} + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &resp) + s.Require().NoError(err) + + // Check result is not empty. + var votes []types.Vote + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(resp.Result, &votes)) + s.Require().Greater(len(votes), 0) + } + }) + } +} diff --git a/x/gov/client/rest/tx.go b/x/gov/client/rest/tx.go index c705bddf4e2d..284c67148170 100644 --- a/x/gov/client/rest/tx.go +++ b/x/gov/client/rest/tx.go @@ -6,29 +6,28 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, phs []ProposalRESTHandler) { +func registerTxHandlers(clientCtx client.Context, r *mux.Router, phs []ProposalRESTHandler) { propSubRtr := r.PathPrefix("/gov/proposals").Subrouter() for _, ph := range phs { propSubRtr.HandleFunc(fmt.Sprintf("/%s", ph.SubRoute), ph.Handler).Methods("POST") } - r.HandleFunc("/gov/proposals", postProposalHandlerFn(cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cliCtx)).Methods("POST") + r.HandleFunc("/gov/proposals", newPostProposalHandlerFn(clientCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), newDepositHandlerFn(clientCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), newVoteHandlerFn(clientCtx)).Methods("POST") } -func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newPostProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req PostProposalReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -40,17 +39,19 @@ func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { proposalType := gcutils.NormalizeProposalType(req.ProposalType) content := types.ContentFromProposalType(req.Title, req.Description, proposalType) - msg := types.NewMsgSubmitProposal(content, req.InitialDeposit, req.Proposer) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + msg, err := types.NewMsgSubmitProposal(content, req.InitialDeposit, req.Proposer) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -func depositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newDepositHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -66,7 +67,7 @@ func depositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } var req DepositReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -77,16 +78,15 @@ func depositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // create the message msg := types.NewMsgDeposit(req.Depositor, proposalID, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -func voteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newVoteHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] @@ -102,7 +102,7 @@ func voteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } var req VoteReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -112,18 +112,16 @@ func voteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } voteOption, err := types.VoteOptionFromString(gcutils.NormalizeVoteOption(req.Option)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } // create the message msg := types.NewMsgVote(req.Voter, proposalID, voteOption) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/gov/client/testutil/helpers.go b/x/gov/client/testutil/helpers.go new file mode 100644 index 000000000000..535cb8d09ca6 --- /dev/null +++ b/x/gov/client/testutil/helpers.go @@ -0,0 +1,45 @@ +package testutil + +import ( + "fmt" + + "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" + govcli "github.com/cosmos/cosmos-sdk/x/gov/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()), +} + +// MsgSubmitProposal creates a tx for submit proposal +func MsgSubmitProposal(clientCtx client.Context, from, title, description, proposalType string, extraArgs ...string) (testutil.BufferWriter, error) { + args := append([]string{ + fmt.Sprintf("--%s=%s", govcli.FlagTitle, title), + fmt.Sprintf("--%s=%s", govcli.FlagDescription, description), + fmt.Sprintf("--%s=%s", govcli.FlagProposalType, proposalType), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + }, commonArgs...) + + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdSubmitProposal(), args) +} + +// MsgVote votes for a proposal +func MsgVote(clientCtx client.Context, from, id, vote string, extraArgs ...string) (testutil.BufferWriter, error) { + args := append([]string{ + id, + vote, + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + }, commonArgs...) + + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdVote(), args) +} diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index d4be3c010f93..e543f526b3f0 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -4,9 +4,8 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -37,7 +36,7 @@ func (p Proposer) String() string { // // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. -func QueryDepositsByTxQuery(cliCtx context.CLIContext, params types.QueryProposalParams) ([]byte, error) { +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))), @@ -45,7 +44,7 @@ func QueryDepositsByTxQuery(cliCtx context.CLIContext, params types.QueryProposa // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit) + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") if err != nil { return nil, err } @@ -53,30 +52,31 @@ func QueryDepositsByTxQuery(cliCtx context.CLIContext, params types.QueryProposa var deposits []types.Deposit for _, info := range searchResult.Txs { - for _, msg := range info.Tx.GetMsgs() { + for _, msg := range info.GetTx().GetMsgs() { if msg.Type() == types.TypeMsgDeposit { - depMsg := msg.(types.MsgDeposit) + depMsg := msg.(*types.MsgDeposit) deposits = append(deposits, types.Deposit{ Depositor: depMsg.Depositor, - ProposalID: params.ProposalID, + ProposalId: params.ProposalID, Amount: depMsg.Amount, }) } } } - if cliCtx.Indent { - return cliCtx.Codec.MarshalJSONIndent(deposits, "", " ") + bz, err := clientCtx.LegacyAmino.MarshalJSON(deposits) + if err != nil { + return nil, err } - return cliCtx.Codec.MarshalJSON(deposits) + return bz, nil } // QueryVotesByTxQuery will query for votes via a direct txs tags query. It // will fetch and build votes directly from the returned txs and return a JSON // marshalled result or any error that occurred. -func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVotesParams) ([]byte, error) { +func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVotesParams) ([]byte, error) { var ( events = []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), @@ -88,19 +88,19 @@ func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVo ) // query interrupted either if we collected enough votes or tx indexer run out of relevant txs for len(votes) < totalLimit { - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, nextTxPage, defaultLimit) + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, nextTxPage, defaultLimit, "") if err != nil { return nil, err } nextTxPage++ for _, info := range searchResult.Txs { - for _, msg := range info.Tx.GetMsgs() { + for _, msg := range info.GetTx().GetMsgs() { if msg.Type() == types.TypeMsgVote { - voteMsg := msg.(types.MsgVote) + voteMsg := msg.(*types.MsgVote) votes = append(votes, types.Vote{ Voter: voteMsg.Voter, - ProposalID: params.ProposalID, + ProposalId: params.ProposalID, Option: voteMsg.Option, }) } @@ -116,14 +116,17 @@ func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVo } else { votes = votes[start:end] } - if cliCtx.Indent { - return cliCtx.Codec.MarshalJSONIndent(votes, "", " ") + + bz, err := clientCtx.LegacyAmino.MarshalJSON(votes) + if err != nil { + return nil, err } - return cliCtx.Codec.MarshalJSON(votes) + + return bz, nil } // QueryVoteByTxQuery will query for a single vote via a direct txs tags query. -func QueryVoteByTxQuery(cliCtx context.CLIContext, params types.QueryVoteParams) ([]byte, error) { +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))), @@ -132,27 +135,28 @@ func QueryVoteByTxQuery(cliCtx context.CLIContext, params types.QueryVoteParams) // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit) + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") if err != nil { return nil, err } for _, info := range searchResult.Txs { - for _, msg := range info.Tx.GetMsgs() { + for _, msg := range info.GetTx().GetMsgs() { // there should only be a single vote under the given conditions if msg.Type() == types.TypeMsgVote { - voteMsg := msg.(types.MsgVote) + voteMsg := msg.(*types.MsgVote) vote := types.Vote{ Voter: voteMsg.Voter, - ProposalID: params.ProposalID, + ProposalId: params.ProposalID, Option: voteMsg.Option, } - if cliCtx.Indent { - return cliCtx.Codec.MarshalJSONIndent(vote, "", " ") + bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) + if err != nil { + return nil, err } - return cliCtx.Codec.MarshalJSON(vote) + return bz, nil } } } @@ -162,7 +166,7 @@ func QueryVoteByTxQuery(cliCtx context.CLIContext, params types.QueryVoteParams) // QueryDepositByTxQuery will query for a single deposit via a direct txs tags // query. -func QueryDepositByTxQuery(cliCtx context.CLIContext, params types.QueryDepositParams) ([]byte, error) { +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))), @@ -171,28 +175,29 @@ func QueryDepositByTxQuery(cliCtx context.CLIContext, params types.QueryDepositP // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit) + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") if err != nil { return nil, err } for _, info := range searchResult.Txs { - for _, msg := range info.Tx.GetMsgs() { + for _, msg := range info.GetTx().GetMsgs() { // there should only be a single deposit under the given conditions if msg.Type() == types.TypeMsgDeposit { - depMsg := msg.(types.MsgDeposit) + depMsg := msg.(*types.MsgDeposit) deposit := types.Deposit{ Depositor: depMsg.Depositor, - ProposalID: params.ProposalID, + ProposalId: params.ProposalID, Amount: depMsg.Amount, } - if cliCtx.Indent { - return cliCtx.Codec.MarshalJSONIndent(deposit, "", " ") + bz, err := clientCtx.JSONMarshaler.MarshalJSON(&deposit) + if err != nil { + return nil, err } - return cliCtx.Codec.MarshalJSON(deposit) + return bz, nil } } } @@ -202,7 +207,7 @@ func QueryDepositByTxQuery(cliCtx context.CLIContext, params types.QueryDepositP // QueryProposerByTxQuery will query for a proposer of a governance proposal by // ID. -func QueryProposerByTxQuery(cliCtx context.CLIContext, proposalID uint64) (Proposer, error) { +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))), @@ -210,17 +215,17 @@ func QueryProposerByTxQuery(cliCtx context.CLIContext, proposalID uint64) (Propo // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. - searchResult, err := utils.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit) + searchResult, err := authclient.QueryTxsByEvents(clientCtx, events, defaultPage, defaultLimit, "") if err != nil { return Proposer{}, err } for _, info := range searchResult.Txs { - for _, msg := range info.Tx.GetMsgs() { + 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) - return NewProposer(proposalID, subMsg.Proposer.String()), nil + subMsg := msg.(*types.MsgSubmitProposal) + return NewProposer(proposalID, subMsg.Proposer), nil } } } @@ -229,14 +234,14 @@ func QueryProposerByTxQuery(cliCtx context.CLIContext, proposalID uint64) (Propo } // QueryProposalByID takes a proposalID and returns a proposal -func QueryProposalByID(proposalID uint64, cliCtx context.CLIContext, queryRoute string) ([]byte, error) { +func QueryProposalByID(proposalID uint64, clientCtx client.Context, queryRoute string) ([]byte, error) { params := types.NewQueryProposalParams(proposalID) - bz, err := cliCtx.Codec.MarshalJSON(params) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) if err != nil { return nil, err } - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz) + res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz) if err != nil { return nil, err } diff --git a/x/gov/client/utils/query_test.go b/x/gov/client/utils/query_test.go index eb4d71e0b061..81b15a457fb1 100644 --- a/x/gov/client/utils/query_test.go +++ b/x/gov/client/utils/query_test.go @@ -1,6 +1,7 @@ -package utils +package utils_test import ( + "context" "testing" "github.com/stretchr/testify/require" @@ -9,10 +10,11 @@ import ( tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "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" ) @@ -21,8 +23,16 @@ type TxSearchMock struct { txs []tmtypes.Tx } -func (mock TxSearchMock) TxSearch(query string, prove bool, page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error) { - start, end := client.Paginate(len(mock.txs), page, perPage, 100) +func (mock TxSearchMock) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error) { + if page == nil { + *page = 0 + } + + if perPage == nil { + *perPage = 0 + } + + 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 @@ -35,16 +45,16 @@ func (mock TxSearchMock) TxSearch(query string, prove bool, page, perPage int, o return rst, nil } -func (mock TxSearchMock) Block(height *int64) (*ctypes.ResultBlock, error) { +func (mock TxSearchMock) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) { // any non nil Block needs to be returned. used to get time value return &ctypes.ResultBlock{Block: &tmtypes.Block{}}, nil } -func newTestCodec() *codec.Codec { - cdc := codec.New() - sdk.RegisterCodec(cdc) - types.RegisterCodec(cdc) - authtypes.RegisterCodec(cdc) +func newTestCodec() *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + types.RegisterLegacyAminoCodec(cdc) + authtypes.RegisterLegacyAminoCodec(cdc) return cdc } @@ -52,7 +62,7 @@ func TestGetPaginatedVotes(t *testing.T) { type testCase struct { description string page, limit int - txs []authtypes.StdTx + msgs [][]sdk.Msg votes []types.Vote } acc1 := make(sdk.AccAddress, 20) @@ -72,22 +82,21 @@ func TestGetPaginatedVotes(t *testing.T) { description: "1MsgPerTxAll", page: 1, limit: 2, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs[:1]}, - {Msgs: acc2Msgs[:1]}, + msgs: [][]sdk.Msg{ + acc1Msgs[:1], + acc2Msgs[:1], }, votes: []types.Vote{ types.NewVote(0, acc1, types.OptionYes), types.NewVote(0, acc2, types.OptionYes)}, }, - { description: "2MsgPerTx1Chunk", page: 1, limit: 2, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs}, - {Msgs: acc2Msgs}, + msgs: [][]sdk.Msg{ + acc1Msgs, + acc2Msgs, }, votes: []types.Vote{ types.NewVote(0, acc1, types.OptionYes), @@ -97,9 +106,9 @@ func TestGetPaginatedVotes(t *testing.T) { description: "2MsgPerTx2Chunk", page: 2, limit: 2, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs}, - {Msgs: acc2Msgs}, + msgs: [][]sdk.Msg{ + acc1Msgs, + acc2Msgs, }, votes: []types.Vote{ types.NewVote(0, acc2, types.OptionYes), @@ -109,46 +118,57 @@ func TestGetPaginatedVotes(t *testing.T) { description: "IncompleteSearchTx", page: 1, limit: 2, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs[:1]}, + msgs: [][]sdk.Msg{ + acc1Msgs[:1], }, votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)}, }, { description: "InvalidPage", page: -1, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs[:1]}, + msgs: [][]sdk.Msg{ + acc1Msgs[:1], }, }, { description: "OutOfBounds", page: 2, limit: 10, - txs: []authtypes.StdTx{ - {Msgs: acc1Msgs[:1]}, + msgs: [][]sdk.Msg{ + acc1Msgs[:1], }, }, } { tc := tc + t.Run(tc.description, func(t *testing.T) { var ( - marshalled = make([]tmtypes.Tx, len(tc.txs)) + marshalled = make([]tmtypes.Tx, len(tc.msgs)) cdc = newTestCodec() ) - for i := range tc.txs { - tx, err := cdc.MarshalBinaryLengthPrefixed(&tc.txs[i]) + + encodingConfig := simapp.MakeTestEncodingConfig() + cli := TxSearchMock{txs: marshalled} + clientCtx := client.Context{}. + WithLegacyAmino(cdc). + WithClient(cli). + WithTxConfig(encodingConfig.TxConfig) + + for i := range tc.msgs { + txBuilder := clientCtx.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs(tc.msgs[i]...) + require.NoError(t, err) + + tx, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) require.NoError(t, err) - marshalled[i] = tmtypes.Tx(tx) + marshalled[i] = tx } - client := TxSearchMock{txs: marshalled} - ctx := context.CLIContext{}.WithCodec(cdc).WithTrustNode(true).WithClient(client) params := types.NewQueryProposalVotesParams(0, tc.page, tc.limit) - votesData, err := QueryVotesByTxQuery(ctx, params) + votesData, err := utils.QueryVotesByTxQuery(clientCtx, params) require.NoError(t, err) votes := []types.Vote{} - require.NoError(t, ctx.Codec.UnmarshalJSON(votesData, &votes)) + require.NoError(t, clientCtx.LegacyAmino.UnmarshalJSON(votesData, &votes)) require.Equal(t, len(tc.votes), len(votes)) for i := range votes { require.Equal(t, tc.votes[i], votes[i]) diff --git a/x/gov/client/utils/utils.go b/x/gov/client/utils/utils.go index b5640a39bcd7..86e4dc5f3e96 100644 --- a/x/gov/client/utils/utils.go +++ b/x/gov/client/utils/utils.go @@ -18,7 +18,7 @@ func NormalizeVoteOption(option string) string { return types.OptionNoWithVeto.String() default: - return "" + return option } } @@ -37,13 +37,14 @@ func NormalizeProposalType(proposalType string) string { func NormalizeProposalStatus(status string) string { switch status { case "DepositPeriod", "deposit_period": - return "DepositPeriod" + return types.StatusDepositPeriod.String() case "VotingPeriod", "voting_period": - return "VotingPeriod" + return types.StatusVotingPeriod.String() case "Passed", "passed": - return "Passed" + return types.StatusPassed.String() case "Rejected", "rejected": - return "Rejected" + return types.StatusRejected.String() + default: + return status } - return "" } diff --git a/x/gov/client/utils/utils_test.go b/x/gov/client/utils/utils_test.go new file mode 100644 index 000000000000..11098c380e50 --- /dev/null +++ b/x/gov/client/utils/utils_test.go @@ -0,0 +1,35 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/gov/client/utils" +) + +func TestNormalizeProposalStatus(t *testing.T) { + type args struct { + status string + } + tests := []struct { + name string + args args + want string + }{ + {"invalid", args{"unknown"}, "unknown"}, + {"deposit_period", args{"deposit_period"}, "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, + {"DepositPeriod", args{"DepositPeriod"}, "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, + {"voting_period", args{"deposit_period"}, "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, + {"VotingPeriod", args{"DepositPeriod"}, "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, + {"passed", args{"passed"}, "PROPOSAL_STATUS_PASSED"}, + {"Passed", args{"Passed"}, "PROPOSAL_STATUS_PASSED"}, + {"Rejected", args{"Rejected"}, "PROPOSAL_STATUS_REJECTED"}, + {"rejected", args{"rejected"}, "PROPOSAL_STATUS_REJECTED"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, utils.NormalizeProposalStatus(tt.args.status)) + }) + } +} diff --git a/x/gov/common_test.go b/x/gov/common_test.go new file mode 100644 index 000000000000..5ef50ea5db03 --- /dev/null +++ b/x/gov/common_test.go @@ -0,0 +1,99 @@ +package gov_test + +import ( + "bytes" + "log" + "sort" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + valTokens = sdk.TokensFromConsensusPower(42) + TestProposal = types.NewTextProposal("Test", "description") + TestDescription = stakingtypes.NewDescription("T", "E", "S", "T", "Z") + TestCommissionRates = stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) +) + +// SortAddresses - Sorts Addresses +func SortAddresses(addrs []sdk.AccAddress) { + byteAddrs := make([][]byte, len(addrs)) + + for i, addr := range addrs { + byteAddrs[i] = addr.Bytes() + } + + SortByteArrays(byteAddrs) + + for i, byteAddr := range byteAddrs { + addrs[i] = byteAddr + } +} + +// implement `Interface` in sort package. +type sortByteArrays [][]byte + +func (b sortByteArrays) Len() int { + return len(b) +} + +func (b sortByteArrays) Less(i, j int) bool { + // bytes package already implements Comparable for []byte. + switch bytes.Compare(b[i], b[j]) { + case -1: + return true + case 0, 1: + return false + default: + log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + return false + } +} + +func (b sortByteArrays) Swap(i, j int) { + b[j], b[i] = b[i], b[j] +} + +// SortByteArrays - sorts the provided byte array +func SortByteArrays(src [][]byte) [][]byte { + sorted := sortByteArrays(src) + sort.Sort(sorted) + return sorted +} + +const contextKeyBadProposal = "contextKeyBadProposal" + +var ( + pubkeys = []cryptotypes.PubKey{ + ed25519.GenPrivKey().PubKey(), + ed25519.GenPrivKey().PubKey(), + ed25519.GenPrivKey().PubKey(), + } +) + +func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { + require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") + + for i := 0; i < len(addrs); i++ { + valTokens := sdk.TokensFromConsensusPower(powerAmt[i]) + valCreateMsg, err := stakingtypes.NewMsgCreateValidator( + addrs[i], pubkeys[i], sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + TestDescription, TestCommissionRates, sdk.OneInt(), + ) + require.NoError(t, err) + handleAndCheck(t, stakingHandler, ctx, valCreateMsg) + } +} + +func handleAndCheck(t *testing.T, h sdk.Handler, ctx sdk.Context, msg sdk.Msg) { + res, err := h(ctx, msg) + require.NoError(t, err) + require.NotNil(t, res) +} diff --git a/x/gov/genesis.go b/x/gov/genesis.go index b487c23ef85d..553edbaa0f77 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -4,13 +4,13 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" ) // InitGenesis - store genesis parameters -func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, data GenesisState) { - - k.SetProposalID(ctx, data.StartingProposalID) +func InitGenesis(ctx sdk.Context, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, data *types.GenesisState) { + k.SetProposalID(ctx, data.StartingProposalId) k.SetDepositParams(ctx, data.DepositParams) k.SetVotingParams(ctx, data.VotingParams) k.SetTallyParams(ctx, data.TallyParams) @@ -33,43 +33,44 @@ func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, dat for _, proposal := range data.Proposals { switch proposal.Status { - case StatusDepositPeriod: - k.InsertInactiveProposalQueue(ctx, proposal.ProposalID, proposal.DepositEndTime) - case StatusVotingPeriod: - k.InsertActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime) + case types.StatusDepositPeriod: + k.InsertInactiveProposalQueue(ctx, proposal.ProposalId, proposal.DepositEndTime) + case types.StatusVotingPeriod: + k.InsertActiveProposalQueue(ctx, proposal.ProposalId, proposal.VotingEndTime) } k.SetProposal(ctx, proposal) } // add coins if not provided on genesis - if moduleAcc.GetCoins().IsZero() { - if err := moduleAcc.SetCoins(totalDeposits); err != nil { + if bk.GetAllBalances(ctx, moduleAcc.GetAddress()).IsZero() { + if err := bk.SetBalances(ctx, moduleAcc.GetAddress(), totalDeposits); err != nil { panic(err) } - supplyKeeper.SetModuleAccount(ctx, moduleAcc) + + ak.SetModuleAccount(ctx, moduleAcc) } } // ExportGenesis - output genesis parameters -func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { startingProposalID, _ := k.GetProposalID(ctx) depositParams := k.GetDepositParams(ctx) votingParams := k.GetVotingParams(ctx) tallyParams := k.GetTallyParams(ctx) proposals := k.GetProposals(ctx) - var proposalsDeposits Deposits - var proposalsVotes Votes + var proposalsDeposits types.Deposits + var proposalsVotes types.Votes for _, proposal := range proposals { - deposits := k.GetDeposits(ctx, proposal.ProposalID) + deposits := k.GetDeposits(ctx, proposal.ProposalId) proposalsDeposits = append(proposalsDeposits, deposits...) - votes := k.GetVotes(ctx, proposal.ProposalID) + votes := k.GetVotes(ctx, proposal.ProposalId) proposalsVotes = append(proposalsVotes, votes...) } - return GenesisState{ - StartingProposalID: startingProposalID, + return &types.GenesisState{ + StartingProposalId: startingProposalID, Deposits: proposalsDeposits, Votes: proposalsVotes, Proposals: proposals, diff --git a/x/gov/genesis_test.go b/x/gov/genesis_test.go index 02b6eed9b42c..2fb3aea9467b 100644 --- a/x/gov/genesis_test.go +++ b/x/gov/genesis_test.go @@ -1,112 +1,149 @@ -package gov +package gov_test import ( + "encoding/json" "testing" "github.com/stretchr/testify/require" - - keep "github.com/cosmos/cosmos-sdk/x/gov/keeper" - 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/simapp" + "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" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/types" ) func TestImportExportQueues(t *testing.T) { - // Generate mock app and keepers - input := getMockApp(t, 2, GenesisState{}, nil, ProposalHandler) - SortAddresses(input.addrs) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 2, valTokens) + + SortAddresses(addrs) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) + ctx = app.BaseApp.NewContext(false, tmproto.Header{}) // Create two proposals, put the second into the voting period - proposal := keep.TestProposal - proposal1, err := input.keeper.SubmitProposal(ctx, proposal) + proposal := TestProposal + proposal1, err := app.GovKeeper.SubmitProposal(ctx, proposal) require.NoError(t, err) - proposalID1 := proposal1.ProposalID + proposalID1 := proposal1.ProposalId - proposal2, err := input.keeper.SubmitProposal(ctx, proposal) + proposal2, err := app.GovKeeper.SubmitProposal(ctx, proposal) require.NoError(t, err) - proposalID2 := proposal2.ProposalID + proposalID2 := proposal2.ProposalId - votingStarted, err := input.keeper.AddDeposit(ctx, proposalID2, input.addrs[0], input.keeper.GetDepositParams(ctx).MinDeposit) + votingStarted, err := app.GovKeeper.AddDeposit(ctx, proposalID2, addrs[0], app.GovKeeper.GetDepositParams(ctx).MinDeposit) require.NoError(t, err) require.True(t, votingStarted) - proposal1, ok := input.keeper.GetProposal(ctx, proposalID1) + proposal1, ok := app.GovKeeper.GetProposal(ctx, proposalID1) require.True(t, ok) - proposal2, ok = input.keeper.GetProposal(ctx, proposalID2) + proposal2, ok = app.GovKeeper.GetProposal(ctx, proposalID2) require.True(t, ok) - require.True(t, proposal1.Status == StatusDepositPeriod) - require.True(t, proposal2.Status == StatusVotingPeriod) + require.True(t, proposal1.Status == types.StatusDepositPeriod) + require.True(t, proposal2.Status == types.StatusVotingPeriod) + + authGenState := auth.ExportGenesis(ctx, app.AccountKeeper) + bankGenState := app.BankKeeper.ExportGenesis(ctx) + + // export the state and import it into a new app + govGenState := gov.ExportGenesis(ctx, app.GovKeeper) + genesisState := simapp.NewDefaultGenesisState(app.AppCodec()) + + genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenState) + genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenState) + genesisState[types.ModuleName] = app.AppCodec().MustMarshalJSON(govGenState) - genAccs := input.mApp.AccountKeeper.GetAllAccounts(ctx) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + if err != nil { + panic(err) + } - // Export the state and import it into a new Mock App - genState := ExportGenesis(ctx, input.keeper) - input2 := getMockApp(t, 2, genState, genAccs, ProposalHandler) + db := dbm.NewMemDB() + app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 0, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) - header = abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input2.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + app2.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simapp.DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) - ctx2 := input2.mApp.BaseApp.NewContext(false, abci.Header{}) + app2.Commit() + app2.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app2.LastBlockHeight() + 1}}) + + header = tmproto.Header{Height: app2.LastBlockHeight() + 1} + app2.BeginBlock(abci.RequestBeginBlock{Header: header}) + + ctx2 := app2.BaseApp.NewContext(false, tmproto.Header{}) // Jump the time forward past the DepositPeriod and VotingPeriod - ctx2 = ctx2.WithBlockTime(ctx2.BlockHeader().Time.Add(input2.keeper.GetDepositParams(ctx2).MaxDepositPeriod).Add(input2.keeper.GetVotingParams(ctx2).VotingPeriod)) + ctx2 = ctx2.WithBlockTime(ctx2.BlockHeader().Time.Add(app2.GovKeeper.GetDepositParams(ctx2).MaxDepositPeriod).Add(app2.GovKeeper.GetVotingParams(ctx2).VotingPeriod)) // Make sure that they are still in the DepositPeriod and VotingPeriod respectively - proposal1, ok = input2.keeper.GetProposal(ctx2, proposalID1) + proposal1, ok = app2.GovKeeper.GetProposal(ctx2, proposalID1) require.True(t, ok) - proposal2, ok = input2.keeper.GetProposal(ctx2, proposalID2) + proposal2, ok = app2.GovKeeper.GetProposal(ctx2, proposalID2) require.True(t, ok) - require.True(t, proposal1.Status == StatusDepositPeriod) - require.True(t, proposal2.Status == StatusVotingPeriod) + require.True(t, proposal1.Status == types.StatusDepositPeriod) + require.True(t, proposal2.Status == types.StatusVotingPeriod) - require.Equal(t, input2.keeper.GetDepositParams(ctx2).MinDeposit, input2.keeper.GetGovernanceAccount(ctx2).GetCoins()) + macc := app2.GovKeeper.GetGovernanceAccount(ctx2) + require.Equal(t, app2.GovKeeper.GetDepositParams(ctx2).MinDeposit, app2.BankKeeper.GetAllBalances(ctx2, macc.GetAddress())) // Run the endblocker. Check to make sure that proposal1 is removed from state, and proposal2 is finished VotingPeriod. - EndBlocker(ctx2, input2.keeper) + gov.EndBlocker(ctx2, app2.GovKeeper) - proposal1, ok = input2.keeper.GetProposal(ctx2, proposalID1) + proposal1, ok = app2.GovKeeper.GetProposal(ctx2, proposalID1) require.False(t, ok) - proposal2, ok = input2.keeper.GetProposal(ctx2, proposalID2) + + proposal2, ok = app2.GovKeeper.GetProposal(ctx2, proposalID2) require.True(t, ok) - require.True(t, proposal2.Status == StatusRejected) + require.True(t, proposal2.Status == types.StatusRejected) } func TestEqualProposals(t *testing.T) { - // Generate mock app and keepers - input := getMockApp(t, 2, GenesisState{}, nil, ProposalHandler) - SortAddresses(input.addrs) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrs(app, ctx, 2, valTokens) - header := abci.Header{Height: input.mApp.LastBlockHeight() + 1} - input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + SortAddresses(addrs) - ctx := input.mApp.BaseApp.NewContext(false, abci.Header{}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) // Submit two proposals - proposal := keep.TestProposal - proposal1, err := input.keeper.SubmitProposal(ctx, proposal) + proposal := TestProposal + proposal1, err := app.GovKeeper.SubmitProposal(ctx, proposal) require.NoError(t, err) - proposal2, err := input.keeper.SubmitProposal(ctx, proposal) + + proposal2, err := app.GovKeeper.SubmitProposal(ctx, proposal) require.NoError(t, err) // They are similar but their IDs should be different require.NotEqual(t, proposal1, proposal2) - require.False(t, keep.ProposalEqual(proposal1, proposal2)) + require.NotEqual(t, proposal1, proposal2) // Now create two genesis blocks - state1 := GenesisState{Proposals: []Proposal{proposal1}} - state2 := GenesisState{Proposals: []Proposal{proposal2}} + state1 := types.GenesisState{Proposals: []types.Proposal{proposal1}} + state2 := types.GenesisState{Proposals: []types.Proposal{proposal2}} require.NotEqual(t, state1, state2) require.False(t, state1.Equal(state2)) // Now make proposals identical by setting both IDs to 55 - proposal1.ProposalID = 55 - proposal2.ProposalID = 55 + proposal1.ProposalId = 55 + proposal2.ProposalId = 55 require.Equal(t, proposal1, proposal1) - require.True(t, keep.ProposalEqual(proposal1, proposal2)) + require.Equal(t, proposal1, proposal2) // Reassign proposals into state state1.Proposals[0] = proposal1 diff --git a/x/gov/handler.go b/x/gov/handler.go index 4e4aee2c5334..25d97bfabb47 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -1,106 +1,34 @@ package gov import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" ) // NewHandler creates an sdk.Handler for all the gov type messages -func NewHandler(keeper Keeper) sdk.Handler { +func NewHandler(k keeper.Keeper) sdk.Handler { + msgServer := keeper.NewMsgServerImpl(k) + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case MsgDeposit: - return handleMsgDeposit(ctx, keeper, msg) + case *types.MsgDeposit: + res, err := msgServer.Deposit(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case MsgSubmitProposal: - return handleMsgSubmitProposal(ctx, keeper, msg) + case *types.MsgSubmitProposal: + res, err := msgServer.SubmitProposal(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case MsgVote: - return handleMsgVote(ctx, keeper, msg) + case *types.MsgVote: + res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } } } - -func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitProposal) (*sdk.Result, error) { - proposal, err := keeper.SubmitProposal(ctx, msg.Content) - if err != nil { - return nil, err - } - - votingStarted, err := keeper.AddDeposit(ctx, proposal.ProposalID, msg.Proposer, msg.InitialDeposit) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Proposer.String()), - ), - ) - - submitEvent := sdk.NewEvent(types.EventTypeSubmitProposal, sdk.NewAttribute(types.AttributeKeyProposalType, msg.Content.ProposalType())) - if votingStarted { - submitEvent = submitEvent.AppendAttributes( - sdk.NewAttribute(types.AttributeKeyVotingPeriodStart, fmt.Sprintf("%d", proposal.ProposalID)), - ) - } - ctx.EventManager().EmitEvent(submitEvent) - - return &sdk.Result{ - Data: GetProposalIDBytes(proposal.ProposalID), - Events: ctx.EventManager().Events(), - }, nil -} - -func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) (*sdk.Result, error) { - votingStarted, err := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositor, msg.Amount) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()), - ), - ) - - if votingStarted { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeProposalDeposit, - sdk.NewAttribute(types.AttributeKeyVotingPeriodStart, fmt.Sprintf("%d", msg.ProposalID)), - ), - ) - } - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) (*sdk.Result, error) { - err := keeper.AddVote(ctx, msg.ProposalID, msg.Voter, msg.Option) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.Voter.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} diff --git a/x/gov/handler_test.go b/x/gov/handler_test.go index 134b25ff0fc5..a62134e4259e 100644 --- a/x/gov/handler_test.go +++ b/x/gov/handler_test.go @@ -1,21 +1,25 @@ -package gov +package gov_test import ( "strings" "testing" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" ) func TestInvalidMsg(t *testing.T) { - k := Keeper{} - h := NewHandler(k) + k := keeper.Keeper{} + h := gov.NewHandler(k) - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) require.Error(t, err) require.Nil(t, res) require.True(t, strings.Contains(err.Error(), "unrecognized gov message type")) diff --git a/x/gov/keeper/common_test.go b/x/gov/keeper/common_test.go new file mode 100644 index 000000000000..7ba9c5827f87 --- /dev/null +++ b/x/gov/keeper/common_test.go @@ -0,0 +1,58 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + TestProposal = types.NewTextProposal("Test", "description") +) + +func createValidators(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers []int64) ([]sdk.AccAddress, []sdk.ValAddress) { + addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, sdk.NewInt(30000000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + pks := simapp.CreateTestPubKeys(5) + + appCodec, _ := simapp.MakeCodecs() + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, + app.GetKey(stakingtypes.StoreKey), + app.AccountKeeper, + app.BankKeeper, + app.GetSubspace(stakingtypes.ModuleName), + ) + + val1, err := stakingtypes.NewValidator(valAddrs[0], pks[0], stakingtypes.Description{}) + require.NoError(t, err) + val2, err := stakingtypes.NewValidator(valAddrs[1], pks[1], stakingtypes.Description{}) + require.NoError(t, err) + val3, err := stakingtypes.NewValidator(valAddrs[2], pks[2], stakingtypes.Description{}) + require.NoError(t, err) + + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidator(ctx, val2) + app.StakingKeeper.SetValidator(ctx, val3) + app.StakingKeeper.SetValidatorByConsAddr(ctx, val1) + app.StakingKeeper.SetValidatorByConsAddr(ctx, val2) + app.StakingKeeper.SetValidatorByConsAddr(ctx, val3) + app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val1) + app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val2) + app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val3) + + _, _ = app.StakingKeeper.Delegate(ctx, addrs[0], sdk.TokensFromConsensusPower(powers[0]), stakingtypes.Unbonded, val1, true) + _, _ = app.StakingKeeper.Delegate(ctx, addrs[1], sdk.TokensFromConsensusPower(powers[1]), stakingtypes.Unbonded, val2, true) + _, _ = app.StakingKeeper.Delegate(ctx, addrs[2], sdk.TokensFromConsensusPower(powers[2]), stakingtypes.Unbonded, val3, true) + + _ = staking.EndBlocker(ctx, app.StakingKeeper) + + return addrs, valAddrs +} diff --git a/x/gov/keeper/deposit.go b/x/gov/keeper/deposit.go index 423679998b44..507ac6793703 100644 --- a/x/gov/keeper/deposit.go +++ b/x/gov/keeper/deposit.go @@ -16,15 +16,21 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAdd return deposit, false } - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &deposit) + keeper.cdc.MustUnmarshalBinaryBare(bz, &deposit) + return deposit, true } // SetDeposit sets a Deposit to the gov store func (keeper Keeper) SetDeposit(ctx sdk.Context, deposit types.Deposit) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit) - store.Set(types.DepositKey(deposit.ProposalID, deposit.Depositor), bz) + bz := keeper.cdc.MustMarshalBinaryBare(&deposit) + depositor, err := sdk.AccAddressFromBech32(deposit.Depositor) + if err != nil { + panic(err) + } + + store.Set(types.DepositKey(deposit.ProposalId, depositor), bz) } // GetAllDeposits returns all the deposits from the store @@ -33,6 +39,7 @@ func (keeper Keeper) GetAllDeposits(ctx sdk.Context) (deposits types.Deposits) { deposits = append(deposits, deposit) return false }) + return } @@ -42,6 +49,7 @@ func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) (deposits t deposits = append(deposits, deposit) return false }) + return } @@ -50,12 +58,16 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool { - err := keeper.supplyKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount) + err := keeper.bankKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount) if err != nil { panic(err) } - store.Delete(types.DepositKey(proposalID, deposit.Depositor)) + depositor, err := sdk.AccAddressFromBech32(deposit.Depositor) + if err != nil { + panic(err) + } + store.Delete(types.DepositKey(proposalID, depositor)) return false }) } @@ -66,9 +78,11 @@ func (keeper Keeper) IterateAllDeposits(ctx sdk.Context, cb func(deposit types.D iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix) defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { var deposit types.Deposit - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit) + + keeper.cdc.MustUnmarshalBinaryBare(iterator.Value(), &deposit) if cb(deposit) { break @@ -82,9 +96,11 @@ func (keeper Keeper) IterateDeposits(ctx sdk.Context, proposalID uint64, cb func iterator := sdk.KVStorePrefixIterator(store, types.DepositsKey(proposalID)) defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { var deposit types.Deposit - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit) + + keeper.cdc.MustUnmarshalBinaryBare(iterator.Value(), &deposit) if cb(deposit) { break @@ -107,7 +123,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd } // update the governance module's account coins pool - err := keeper.supplyKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount) + err := keeper.bankKeeper.SendCoinsFromAccountToModule(ctx, depositorAddr, types.ModuleName, depositAmount) if err != nil { return false, err } @@ -118,13 +134,16 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd // Check if deposit has provided sufficient total funds to transition the proposal into the voting period activatedVotingPeriod := false + if proposal.Status == types.StatusDepositPeriod && proposal.TotalDeposit.IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) { - keeper.activateVotingPeriod(ctx, proposal) + keeper.ActivateVotingPeriod(ctx, proposal) + activatedVotingPeriod = true } // Add or update deposit object deposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr) + if found { deposit.Amount = deposit.Amount.Add(depositAmount...) } else { @@ -140,6 +159,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd ) keeper.SetDeposit(ctx, deposit) + return activatedVotingPeriod, nil } @@ -148,12 +168,17 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool { - err := keeper.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, deposit.Depositor, deposit.Amount) + depositor, err := sdk.AccAddressFromBech32(deposit.Depositor) + if err != nil { + panic(err) + } + + err = keeper.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, depositor, deposit.Amount) if err != nil { panic(err) } - store.Delete(types.DepositKey(proposalID, deposit.Depositor)) + store.Delete(types.DepositKey(proposalID, depositor)) return false }) } diff --git a/x/gov/keeper/deposit_test.go b/x/gov/keeper/deposit_test.go index edf452464094..dfa857dc57fe 100644 --- a/x/gov/keeper/deposit_test.go +++ b/x/gov/keeper/deposit_test.go @@ -1,98 +1,103 @@ -package keeper +package keeper_test import ( "testing" "time" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" ) func TestDeposits(t *testing.T) { - ctx, ak, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + TestAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000000)) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId fourStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4))) fiveStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5))) - addr0Initial := ak.GetAccount(ctx, TestAddrs[0]).GetCoins() - addr1Initial := ak.GetAccount(ctx, TestAddrs[1]).GetCoins() + addr0Initial := app.BankKeeper.GetAllBalances(ctx, TestAddrs[0]) + addr1Initial := app.BankKeeper.GetAllBalances(ctx, TestAddrs[1]) require.True(t, proposal.TotalDeposit.IsEqual(sdk.NewCoins())) // Check no deposits at beginning - deposit, found := keeper.GetDeposit(ctx, proposalID, TestAddrs[1]) + deposit, found := app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[1]) require.False(t, found) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) require.True(t, proposal.VotingStartTime.Equal(time.Time{})) // Check first deposit - votingStarted, err := keeper.AddDeposit(ctx, proposalID, TestAddrs[0], fourStake) + votingStarted, err := app.GovKeeper.AddDeposit(ctx, proposalID, TestAddrs[0], fourStake) require.NoError(t, err) require.False(t, votingStarted) - deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[0]) + deposit, found = app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[0]) require.True(t, found) require.Equal(t, fourStake, deposit.Amount) - require.Equal(t, TestAddrs[0], deposit.Depositor) - proposal, ok = keeper.GetProposal(ctx, proposalID) + require.Equal(t, TestAddrs[0].String(), deposit.Depositor) + proposal, ok = app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) require.Equal(t, fourStake, proposal.TotalDeposit) - require.Equal(t, addr0Initial.Sub(fourStake), ak.GetAccount(ctx, TestAddrs[0]).GetCoins()) + require.Equal(t, addr0Initial.Sub(fourStake), app.BankKeeper.GetAllBalances(ctx, TestAddrs[0])) // Check a second deposit from same address - votingStarted, err = keeper.AddDeposit(ctx, proposalID, TestAddrs[0], fiveStake) + votingStarted, err = app.GovKeeper.AddDeposit(ctx, proposalID, TestAddrs[0], fiveStake) require.NoError(t, err) require.False(t, votingStarted) - deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[0]) + deposit, found = app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[0]) require.True(t, found) require.Equal(t, fourStake.Add(fiveStake...), deposit.Amount) - require.Equal(t, TestAddrs[0], deposit.Depositor) - proposal, ok = keeper.GetProposal(ctx, proposalID) + require.Equal(t, TestAddrs[0].String(), deposit.Depositor) + proposal, ok = app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) require.Equal(t, fourStake.Add(fiveStake...), proposal.TotalDeposit) - require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), ak.GetAccount(ctx, TestAddrs[0]).GetCoins()) + require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), app.BankKeeper.GetAllBalances(ctx, TestAddrs[0])) // Check third deposit from a new address - votingStarted, err = keeper.AddDeposit(ctx, proposalID, TestAddrs[1], fourStake) + votingStarted, err = app.GovKeeper.AddDeposit(ctx, proposalID, TestAddrs[1], fourStake) require.NoError(t, err) require.True(t, votingStarted) - deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1]) + deposit, found = app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[1]) require.True(t, found) - require.Equal(t, TestAddrs[1], deposit.Depositor) + require.Equal(t, TestAddrs[1].String(), deposit.Depositor) require.Equal(t, fourStake, deposit.Amount) - proposal, ok = keeper.GetProposal(ctx, proposalID) + proposal, ok = app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) require.Equal(t, fourStake.Add(fiveStake...).Add(fourStake...), proposal.TotalDeposit) - require.Equal(t, addr1Initial.Sub(fourStake), ak.GetAccount(ctx, TestAddrs[1]).GetCoins()) + require.Equal(t, addr1Initial.Sub(fourStake), app.BankKeeper.GetAllBalances(ctx, TestAddrs[1])) // Check that proposal moved to voting period - proposal, ok = keeper.GetProposal(ctx, proposalID) + proposal, ok = app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time)) // Test deposit iterator // NOTE order of deposits is determined by the addresses - deposits := keeper.GetAllDeposits(ctx) + deposits := app.GovKeeper.GetAllDeposits(ctx) require.Len(t, deposits, 2) - require.Equal(t, deposits, keeper.GetDeposits(ctx, proposalID)) - require.Equal(t, TestAddrs[0], deposits[0].Depositor) + require.Equal(t, deposits, app.GovKeeper.GetDeposits(ctx, proposalID)) + require.Equal(t, TestAddrs[0].String(), deposits[0].Depositor) require.Equal(t, fourStake.Add(fiveStake...), deposits[0].Amount) - require.Equal(t, TestAddrs[1], deposits[1].Depositor) + require.Equal(t, TestAddrs[1].String(), deposits[1].Depositor) require.Equal(t, fourStake, deposits[1].Amount) // Test Refund Deposits - deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1]) + deposit, found = app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[1]) require.True(t, found) require.Equal(t, fourStake, deposit.Amount) - keeper.RefundDeposits(ctx, proposalID) - deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1]) + app.GovKeeper.RefundDeposits(ctx, proposalID) + deposit, found = app.GovKeeper.GetDeposit(ctx, proposalID, TestAddrs[1]) require.False(t, found) - require.Equal(t, addr0Initial, ak.GetAccount(ctx, TestAddrs[0]).GetCoins()) - require.Equal(t, addr1Initial, ak.GetAccount(ctx, TestAddrs[1]).GetCoins()) + require.Equal(t, addr0Initial, app.BankKeeper.GetAllBalances(ctx, TestAddrs[0])) + require.Equal(t, addr1Initial, app.BankKeeper.GetAllBalances(ctx, TestAddrs[1])) } diff --git a/x/gov/keeper/grpc_query.go b/x/gov/keeper/grpc_query.go new file mode 100644 index 000000000000..93a0e05937ed --- /dev/null +++ b/x/gov/keeper/grpc_query.go @@ -0,0 +1,278 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "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/gov/types" +) + +var _ types.QueryServer = Keeper{} + +// Proposal returns proposal details based on ProposalID +func (q Keeper) Proposal(c context.Context, req *types.QueryProposalRequest) (*types.QueryProposalResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + ctx := sdk.UnwrapSDKContext(c) + + proposal, found := q.GetProposal(ctx, req.ProposalId) + if !found { + return nil, status.Errorf(codes.NotFound, "proposal %d doesn't exist", req.ProposalId) + } + + return &types.QueryProposalResponse{Proposal: proposal}, nil +} + +// Proposals implements the Query/Proposals gRPC method +func (q Keeper) Proposals(c context.Context, req *types.QueryProposalsRequest) (*types.QueryProposalsResponse, error) { + var filteredProposals types.Proposals + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(q.storeKey) + proposalStore := prefix.NewStore(store, types.ProposalsKeyPrefix) + + pageRes, err := query.FilteredPaginate(proposalStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + var p types.Proposal + if err := q.cdc.UnmarshalBinaryBare(value, &p); err != nil { + return false, status.Error(codes.Internal, err.Error()) + } + + matchVoter, matchDepositor, matchStatus := true, true, true + + // match status (if supplied/valid) + if types.ValidProposalStatus(req.ProposalStatus) { + matchStatus = p.Status == req.ProposalStatus + } + + // match voter address (if supplied) + if len(req.Voter) > 0 { + voter, err := sdk.AccAddressFromBech32(req.Voter) + if err != nil { + return false, err + } + + _, matchVoter = q.GetVote(ctx, p.ProposalId, voter) + } + + // match depositor (if supplied) + if len(req.Depositor) > 0 { + depositor, err := sdk.AccAddressFromBech32(req.Depositor) + if err != nil { + return false, err + } + _, matchDepositor = q.GetDeposit(ctx, p.ProposalId, depositor) + } + + if matchVoter && matchDepositor && matchStatus { + if accumulate { + filteredProposals = append(filteredProposals, p) + } + + return true, nil + } + + return false, nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryProposalsResponse{Proposals: filteredProposals, Pagination: pageRes}, nil +} + +// Vote returns Voted information based on proposalID, voterAddr +func (q Keeper) Vote(c context.Context, req *types.QueryVoteRequest) (*types.QueryVoteResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + if req.Voter == "" { + return nil, status.Error(codes.InvalidArgument, "empty voter address") + } + + ctx := sdk.UnwrapSDKContext(c) + + voter, err := sdk.AccAddressFromBech32(req.Voter) + if err != nil { + return nil, err + } + vote, found := q.GetVote(ctx, req.ProposalId, voter) + if !found { + return nil, status.Errorf(codes.InvalidArgument, + "voter: %v not found for proposal: %v", req.Voter, req.ProposalId) + } + + return &types.QueryVoteResponse{Vote: vote}, nil +} + +// Votes returns single proposal's votes +func (q Keeper) Votes(c context.Context, req *types.QueryVotesRequest) (*types.QueryVotesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + var votes types.Votes + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(q.storeKey) + votesStore := prefix.NewStore(store, types.VotesKey(req.ProposalId)) + + pageRes, err := query.Paginate(votesStore, req.Pagination, func(key []byte, value []byte) error { + var vote types.Vote + if err := q.cdc.UnmarshalBinaryBare(value, &vote); err != nil { + return err + } + + votes = append(votes, vote) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryVotesResponse{Votes: votes, Pagination: pageRes}, nil +} + +// Params queries all params +func (q Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(c) + + switch req.ParamsType { + case types.ParamDeposit: + depositParmas := q.GetDepositParams(ctx) + return &types.QueryParamsResponse{DepositParams: depositParmas}, nil + + case types.ParamVoting: + votingParmas := q.GetVotingParams(ctx) + return &types.QueryParamsResponse{VotingParams: votingParmas}, nil + + case types.ParamTallying: + tallyParams := q.GetTallyParams(ctx) + return &types.QueryParamsResponse{TallyParams: tallyParams}, nil + + default: + return nil, status.Errorf(codes.InvalidArgument, + "%s is not a valid parameter type", req.ParamsType) + } +} + +// Deposit queries single deposit information based proposalID, depositAddr +func (q Keeper) Deposit(c context.Context, req *types.QueryDepositRequest) (*types.QueryDepositResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + if req.Depositor == "" { + return nil, status.Error(codes.InvalidArgument, "empty depositor address") + } + + ctx := sdk.UnwrapSDKContext(c) + + depositor, err := sdk.AccAddressFromBech32(req.Depositor) + if err != nil { + return nil, err + } + deposit, found := q.GetDeposit(ctx, req.ProposalId, depositor) + if !found { + return nil, status.Errorf(codes.InvalidArgument, + "depositer: %v not found for proposal: %v", req.Depositor, req.ProposalId) + } + + return &types.QueryDepositResponse{Deposit: deposit}, nil +} + +// Deposits returns single proposal's all deposits +func (q Keeper) Deposits(c context.Context, req *types.QueryDepositsRequest) (*types.QueryDepositsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + var deposits types.Deposits + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(q.storeKey) + depositStore := prefix.NewStore(store, types.DepositsKey(req.ProposalId)) + + pageRes, err := query.Paginate(depositStore, req.Pagination, func(key []byte, value []byte) error { + var deposit types.Deposit + if err := q.cdc.UnmarshalBinaryBare(value, &deposit); err != nil { + return err + } + + deposits = append(deposits, deposit) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDepositsResponse{Deposits: deposits, Pagination: pageRes}, nil +} + +// TallyResult queries the tally of a proposal vote +func (q Keeper) TallyResult(c context.Context, req *types.QueryTallyResultRequest) (*types.QueryTallyResultResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + if req.ProposalId == 0 { + return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") + } + + ctx := sdk.UnwrapSDKContext(c) + + proposal, ok := q.GetProposal(ctx, req.ProposalId) + if !ok { + return nil, status.Errorf(codes.NotFound, "proposal %d doesn't exist", req.ProposalId) + } + + var tallyResult types.TallyResult + + switch { + case proposal.Status == types.StatusDepositPeriod: + tallyResult = types.EmptyTallyResult() + + case proposal.Status == types.StatusPassed || proposal.Status == types.StatusRejected: + tallyResult = proposal.FinalTallyResult + + default: + // proposal is in voting period + _, _, tallyResult = q.Tally(ctx, proposal) + } + + return &types.QueryTallyResultResponse{Tally: tallyResult}, nil +} diff --git a/x/gov/keeper/grpc_query_test.go b/x/gov/keeper/grpc_query_test.go new file mode 100644 index 000000000000..dc330d734829 --- /dev/null +++ b/x/gov/keeper/grpc_query_test.go @@ -0,0 +1,818 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +func (suite *KeeperTestSuite) TestGRPCQueryProposal() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + var ( + req *types.QueryProposalRequest + expProposal types.Proposal + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryProposalRequest{} + }, + false, + }, + { + "non existing proposal request", + func() { + req = &types.QueryProposalRequest{ProposalId: 3} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryProposalRequest{ProposalId: 0} + }, + false, + }, + { + "valid request", + func() { + req = &types.QueryProposalRequest{ProposalId: 1} + testProposal := types.NewTextProposal("Proposal", "testing proposal") + submittedProposal, err := app.GovKeeper.SubmitProposal(ctx, testProposal) + suite.Require().NoError(err) + suite.Require().NotEmpty(submittedProposal) + + expProposal = submittedProposal + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + proposalRes, err := queryClient.Proposal(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expProposal.String(), proposalRes.Proposal.String()) + } else { + suite.Require().Error(err) + suite.Require().Nil(proposalRes) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryProposals() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + testProposals := []types.Proposal{} + + var ( + req *types.QueryProposalsRequest + expRes *types.QueryProposalsResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty state request", + func() { + req = &types.QueryProposalsRequest{} + }, + true, + }, + { + "request proposals with limit 3", + func() { + // create 5 test proposals + for i := 0; i < 5; i++ { + num := strconv.Itoa(i + 1) + testProposal := types.NewTextProposal("Proposal"+num, "testing proposal "+num) + proposal, err := app.GovKeeper.SubmitProposal(ctx, testProposal) + suite.Require().NotEmpty(proposal) + suite.Require().NoError(err) + testProposals = append(testProposals, proposal) + } + + req = &types.QueryProposalsRequest{ + Pagination: &query.PageRequest{Limit: 3}, + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals[:3], + } + }, + true, + }, + { + "request 2nd page with limit 4", + func() { + req = &types.QueryProposalsRequest{ + Pagination: &query.PageRequest{Offset: 3, Limit: 3}, + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals[3:], + } + }, + true, + }, + { + "request with limit 2 and count true", + func() { + req = &types.QueryProposalsRequest{ + Pagination: &query.PageRequest{Limit: 2, CountTotal: true}, + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals[:2], + } + }, + true, + }, + { + "request with filter of status deposit period", + func() { + req = &types.QueryProposalsRequest{ + ProposalStatus: types.StatusDepositPeriod, + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals, + } + }, + true, + }, + { + "request with filter of deposit address", + func() { + depositCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(20))) + deposit := types.NewDeposit(testProposals[0].ProposalId, addrs[0], depositCoins) + app.GovKeeper.SetDeposit(ctx, deposit) + + req = &types.QueryProposalsRequest{ + Depositor: addrs[0].String(), + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals[:1], + } + }, + true, + }, + { + "request with filter of deposit address", + 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)) + + req = &types.QueryProposalsRequest{ + Voter: addrs[0].String(), + } + + expRes = &types.QueryProposalsResponse{ + Proposals: testProposals[1:2], + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + proposals, err := queryClient.Proposals(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + + suite.Require().Len(proposals.GetProposals(), len(expRes.GetProposals())) + for i := 0; i < len(proposals.GetProposals()); i++ { + suite.Require().Equal(proposals.GetProposals()[i].String(), expRes.GetProposals()[i].String()) + } + + } else { + suite.Require().Error(err) + suite.Require().Nil(proposals) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryVote() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + var ( + req *types.QueryVoteRequest + expRes *types.QueryVoteResponse + proposal types.Proposal + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryVoteRequest{} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryVoteRequest{ + ProposalId: 0, + Voter: addrs[0].String(), + } + }, + false, + }, + { + "empty voter request", + func() { + req = &types.QueryVoteRequest{ + ProposalId: 1, + Voter: "", + } + }, + false, + }, + { + "non existed proposal", + func() { + req = &types.QueryVoteRequest{ + ProposalId: 3, + Voter: addrs[0].String(), + } + }, + false, + }, + { + "no votes present", + func() { + var err error + proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal) + suite.Require().NoError(err) + + req = &types.QueryVoteRequest{ + ProposalId: proposal.ProposalId, + Voter: addrs[0].String(), + } + + expRes = &types.QueryVoteResponse{} + }, + false, + }, + { + "valid request", + func() { + proposal.Status = types.StatusVotingPeriod + app.GovKeeper.SetProposal(ctx, proposal) + suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.OptionAbstain)) + + req = &types.QueryVoteRequest{ + ProposalId: proposal.ProposalId, + Voter: addrs[0].String(), + } + + expRes = &types.QueryVoteResponse{Vote: types.NewVote(proposal.ProposalId, addrs[0], types.OptionAbstain)} + }, + true, + }, + { + "wrong voter id request", + func() { + req = &types.QueryVoteRequest{ + ProposalId: proposal.ProposalId, + Voter: addrs[1].String(), + } + + expRes = &types.QueryVoteResponse{} + }, + false, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + vote, err := queryClient.Vote(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes, vote) + } else { + suite.Require().Error(err) + suite.Require().Nil(vote) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryVotes() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + addrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(30000000)) + + var ( + req *types.QueryVotesRequest + expRes *types.QueryVotesResponse + proposal types.Proposal + votes types.Votes + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryVotesRequest{} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryVotesRequest{ + ProposalId: 0, + } + }, + false, + }, + { + "non existed proposals", + func() { + req = &types.QueryVotesRequest{ + ProposalId: 2, + } + }, + true, + }, + { + "create a proposal and get votes", + func() { + var err error + proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal) + suite.Require().NoError(err) + + req = &types.QueryVotesRequest{ + ProposalId: proposal.ProposalId, + } + }, + true, + }, + { + "request after adding 2 votes", + func() { + proposal.Status = types.StatusVotingPeriod + app.GovKeeper.SetProposal(ctx, proposal) + + votes = []types.Vote{ + {proposal.ProposalId, addrs[0].String(), types.OptionAbstain}, + {proposal.ProposalId, addrs[1].String(), 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)) + + req = &types.QueryVotesRequest{ + ProposalId: proposal.ProposalId, + } + + expRes = &types.QueryVotesResponse{ + Votes: votes, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + votes, err := queryClient.Votes(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes.GetVotes(), votes.GetVotes()) + } else { + suite.Require().Error(err) + suite.Require().Nil(votes) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryParams() { + queryClient := suite.queryClient + + var ( + req *types.QueryParamsRequest + expRes *types.QueryParamsResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryParamsRequest{} + }, + false, + }, + { + "deposit params request", + func() { + req = &types.QueryParamsRequest{ParamsType: types.ParamDeposit} + expRes = &types.QueryParamsResponse{ + DepositParams: types.DefaultDepositParams(), + TallyParams: types.NewTallyParams(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)), + } + }, + true, + }, + { + "voting params request", + func() { + req = &types.QueryParamsRequest{ParamsType: types.ParamVoting} + expRes = &types.QueryParamsResponse{ + VotingParams: types.DefaultVotingParams(), + TallyParams: types.NewTallyParams(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)), + } + }, + true, + }, + { + "tally params request", + func() { + req = &types.QueryParamsRequest{ParamsType: types.ParamTallying} + expRes = &types.QueryParamsResponse{ + TallyParams: types.DefaultTallyParams(), + } + }, + true, + }, + { + "invalid request", + func() { + req = &types.QueryParamsRequest{ParamsType: "wrongPath"} + expRes = &types.QueryParamsResponse{} + }, + false, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + params, err := queryClient.Params(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes.GetDepositParams(), params.GetDepositParams()) + suite.Require().Equal(expRes.GetVotingParams(), params.GetVotingParams()) + suite.Require().Equal(expRes.GetTallyParams(), params.GetTallyParams()) + } else { + suite.Require().Error(err) + suite.Require().Nil(params) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDeposit() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + var ( + req *types.QueryDepositRequest + expRes *types.QueryDepositResponse + proposal types.Proposal + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDepositRequest{} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryDepositRequest{ + ProposalId: 0, + Depositor: addrs[0].String(), + } + }, + false, + }, + { + "empty deposit address request", + func() { + req = &types.QueryDepositRequest{ + ProposalId: 1, + Depositor: "", + } + }, + false, + }, + { + "non existed proposal", + func() { + req = &types.QueryDepositRequest{ + ProposalId: 2, + Depositor: addrs[0].String(), + } + }, + false, + }, + { + "no deposits proposal", + func() { + var err error + proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal) + suite.Require().NoError(err) + suite.Require().NotNil(proposal) + + req = &types.QueryDepositRequest{ + ProposalId: proposal.ProposalId, + Depositor: addrs[0].String(), + } + }, + false, + }, + { + "valid request", + func() { + depositCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(20))) + deposit := types.NewDeposit(proposal.ProposalId, addrs[0], depositCoins) + app.GovKeeper.SetDeposit(ctx, deposit) + + req = &types.QueryDepositRequest{ + ProposalId: proposal.ProposalId, + Depositor: addrs[0].String(), + } + + expRes = &types.QueryDepositResponse{Deposit: deposit} + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + deposit, err := queryClient.Deposit(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(deposit.GetDeposit(), expRes.GetDeposit()) + } else { + suite.Require().Error(err) + suite.Require().Nil(expRes) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDeposits() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + + var ( + req *types.QueryDepositsRequest + expRes *types.QueryDepositsResponse + proposal types.Proposal + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDepositsRequest{} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryDepositsRequest{ + ProposalId: 0, + } + }, + false, + }, + { + "non existed proposal", + func() { + req = &types.QueryDepositsRequest{ + ProposalId: 2, + } + }, + true, + }, + { + "create a proposal and get deposits", + func() { + var err error + proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal) + suite.Require().NoError(err) + + req = &types.QueryDepositsRequest{ + ProposalId: proposal.ProposalId, + } + }, + true, + }, + { + "get deposits with default limit", + func() { + depositAmount1 := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(20))) + deposit1 := types.NewDeposit(proposal.ProposalId, addrs[0], depositAmount1) + app.GovKeeper.SetDeposit(ctx, deposit1) + + depositAmount2 := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(30))) + deposit2 := types.NewDeposit(proposal.ProposalId, addrs[1], depositAmount2) + app.GovKeeper.SetDeposit(ctx, deposit2) + + deposits := types.Deposits{deposit1, deposit2} + + req = &types.QueryDepositsRequest{ + ProposalId: proposal.ProposalId, + } + + expRes = &types.QueryDepositsResponse{ + Deposits: deposits, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + deposits, err := queryClient.Deposits(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes.GetDeposits(), deposits.GetDeposits()) + } else { + suite.Require().Error(err) + suite.Require().Nil(deposits) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryTally() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + addrs, _ := createValidators(suite.T(), ctx, app, []int64{5, 5, 5}) + + var ( + req *types.QueryTallyResultRequest + expRes *types.QueryTallyResultResponse + proposal types.Proposal + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryTallyResultRequest{} + }, + false, + }, + { + "zero proposal id request", + func() { + req = &types.QueryTallyResultRequest{ProposalId: 0} + }, + false, + }, + { + "query non existed proposal", + func() { + req = &types.QueryTallyResultRequest{ProposalId: 1} + }, + false, + }, + { + "create a proposal and get tally", + func() { + var err error + proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal) + suite.Require().NoError(err) + suite.Require().NotNil(proposal) + + req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId} + + expRes = &types.QueryTallyResultResponse{ + Tally: types.EmptyTallyResult(), + } + }, + true, + }, + { + "request tally after few votes", + func() { + 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)) + + req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId} + + expRes = &types.QueryTallyResultResponse{ + Tally: types.TallyResult{ + Yes: sdk.NewInt(3 * 5 * 1000000), + }, + } + }, + true, + }, + { + "request final tally after status changed", + func() { + proposal.Status = types.StatusPassed + app.GovKeeper.SetProposal(ctx, proposal) + proposal, _ = app.GovKeeper.GetProposal(ctx, proposal.ProposalId) + + req = &types.QueryTallyResultRequest{ProposalId: proposal.ProposalId} + + expRes = &types.QueryTallyResultResponse{ + Tally: proposal.FinalTallyResult, + } + }, + true, + }, + } + + for _, testCase := range testCases { + suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() { + testCase.malleate() + + tally, err := queryClient.TallyResult(gocontext.Background(), req) + + if testCase.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expRes.String(), tally.String()) + } else { + suite.Require().Error(err) + suite.Require().Nil(tally) + } + }) + } +} diff --git a/x/gov/keeper/invariants.go b/x/gov/keeper/invariants.go index 1ebe62c15d0a..d0ec4dbfacc8 100644 --- a/x/gov/keeper/invariants.go +++ b/x/gov/keeper/invariants.go @@ -10,20 +10,20 @@ import ( ) // RegisterInvariants registers all governance invariants -func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) { - ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(keeper)) +func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper, bk types.BankKeeper) { + ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(keeper, bk)) } // AllInvariants runs all invariants of the governance module -func AllInvariants(keeper Keeper) sdk.Invariant { +func AllInvariants(keeper Keeper, bk types.BankKeeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - return ModuleAccountInvariant(keeper)(ctx) + return ModuleAccountInvariant(keeper, bk)(ctx) } } // ModuleAccountInvariant checks that the module account coins reflects the sum of // deposit amounts held on store -func ModuleAccountInvariant(keeper Keeper) sdk.Invariant { +func ModuleAccountInvariant(keeper Keeper, bk types.BankKeeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { var expectedDeposits sdk.Coins @@ -33,10 +33,11 @@ func ModuleAccountInvariant(keeper Keeper) sdk.Invariant { }) macc := keeper.GetGovernanceAccount(ctx) - broken := !macc.GetCoins().IsEqual(expectedDeposits) + balances := bk.GetAllBalances(ctx, macc.GetAddress()) + broken := !balances.IsEqual(expectedDeposits) return sdk.FormatInvariant(types.ModuleName, "deposits", fmt.Sprintf("\tgov ModuleAccount coins: %s\n\tsum of deposit amounts: %s\n", - macc.GetCoins(), expectedDeposits)), broken + balances, expectedDeposits)), broken } } diff --git a/x/gov/keeper/keeper.go b/x/gov/keeper/keeper.go index 7a51e6e2bf29..60db99d32c11 100644 --- a/x/gov/keeper/keeper.go +++ b/x/gov/keeper/keeper.go @@ -4,12 +4,12 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" - - "github.com/tendermint/tendermint/libs/log" ) // Keeper defines the governance module Keeper @@ -17,8 +17,8 @@ type Keeper struct { // The reference to the Paramstore to get and set gov specific params paramSpace types.ParamSubspace - // The SupplyKeeper to reduce the supply of the network - supplyKeeper types.SupplyKeeper + authKeeper types.AccountKeeper + bankKeeper types.BankKeeper // The reference to the DelegationSet and ValidatorSet to get information about validators and delegators sk types.StakingKeeper @@ -27,7 +27,7 @@ type Keeper struct { storeKey sdk.StoreKey // The codec codec for binary encoding/decoding. - cdc *codec.Codec + cdc codec.BinaryMarshaler // Proposal router router types.Router @@ -41,12 +41,12 @@ type Keeper struct { // // CONTRACT: the parameter Subspace must have the param key table already initialized func NewKeeper( - cdc *codec.Codec, key sdk.StoreKey, paramSpace types.ParamSubspace, - supplyKeeper types.SupplyKeeper, sk types.StakingKeeper, rtr types.Router, + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace types.ParamSubspace, + authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper, rtr types.Router, ) Keeper { // ensure governance module account is set - if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil { + if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } @@ -56,18 +56,19 @@ func NewKeeper( rtr.Seal() return Keeper{ - storeKey: key, - paramSpace: paramSpace, - supplyKeeper: supplyKeeper, - sk: sk, - cdc: cdc, - router: rtr, + storeKey: key, + paramSpace: paramSpace, + authKeeper: authKeeper, + bankKeeper: bankKeeper, + sk: sk, + cdc: cdc, + router: rtr, } } // Logger returns a module-specific logger. func (keeper Keeper) Logger(ctx sdk.Context) log.Logger { - return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) + return ctx.Logger().With("module", "x/"+types.ModuleName) } // Router returns the gov Keeper's Router @@ -76,8 +77,8 @@ func (keeper Keeper) Router() types.Router { } // GetGovernanceAccount returns the governance ModuleAccount -func (keeper Keeper) GetGovernanceAccount(ctx sdk.Context) exported.ModuleAccountI { - return keeper.supplyKeeper.GetModuleAccount(ctx, types.ModuleName) +func (keeper Keeper) GetGovernanceAccount(ctx sdk.Context) authtypes.ModuleAccountI { + return keeper.authKeeper.GetModuleAccount(ctx, types.ModuleName) } // ProposalQueues diff --git a/x/gov/keeper/keeper_test.go b/x/gov/keeper/keeper_test.go index 5549049e3a35..105a9d002848 100644 --- a/x/gov/keeper/keeper_test.go +++ b/x/gov/keeper/keeper_test.go @@ -1,51 +1,92 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov/types" ) +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient + addrs []sdk.AccAddress +} + +func (suite *KeeperTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.GovKeeper) + queryClient := types.NewQueryClient(queryHelper) + + suite.app = app + suite.ctx = ctx + suite.queryClient = queryClient + suite.addrs = simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(30000000)) +} + func TestIncrementProposalNumber(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) tp := TestProposal - keeper.SubmitProposal(ctx, tp) - keeper.SubmitProposal(ctx, tp) - keeper.SubmitProposal(ctx, tp) - keeper.SubmitProposal(ctx, tp) - keeper.SubmitProposal(ctx, tp) - proposal6, err := keeper.SubmitProposal(ctx, tp) + _, err := app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + _, err = app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + _, err = app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + _, err = app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + _, err = app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + proposal6, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - require.Equal(t, uint64(6), proposal6.ProposalID) + require.Equal(t, uint64(6), proposal6.ProposalId) } func TestProposalQueues(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) // create test proposals tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, proposal.DepositEndTime) + inactiveIterator := app.GovKeeper.InactiveProposalQueueIterator(ctx, proposal.DepositEndTime) require.True(t, inactiveIterator.Valid()) proposalID := types.GetProposalIDFromBytes(inactiveIterator.Value()) - require.Equal(t, proposalID, proposal.ProposalID) + require.Equal(t, proposalID, proposal.ProposalId) inactiveIterator.Close() - keeper.activateVotingPeriod(ctx, proposal) + app.GovKeeper.ActivateVotingPeriod(ctx, proposal) - proposal, ok := keeper.GetProposal(ctx, proposal.ProposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposal.ProposalId) require.True(t, ok) - activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime) + activeIterator := app.GovKeeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime) require.True(t, activeIterator.Valid()) - keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) - require.Equal(t, proposalID, proposal.ProposalID) + + proposalID, _ = types.SplitActiveProposalQueueKey(activeIterator.Key()) + require.Equal(t, proposalID, proposal.ProposalId) + activeIterator.Close() } + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/gov/keeper/msg_server.go b/x/gov/keeper/msg_server.go new file mode 100644 index 000000000000..39ddf3ab4539 --- /dev/null +++ b/x/gov/keeper/msg_server.go @@ -0,0 +1,129 @@ +package keeper + +import ( + "context" + "fmt" + "strconv" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the gov MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (k msgServer) SubmitProposal(goCtx context.Context, msg *types.MsgSubmitProposal) (*types.MsgSubmitProposalResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + proposal, err := k.Keeper.SubmitProposal(ctx, msg.GetContent()) + if err != nil { + return nil, err + } + + defer telemetry.IncrCounter(1, types.ModuleName, "proposal") + + votingStarted, err := k.Keeper.AddDeposit(ctx, proposal.ProposalId, msg.GetProposer(), msg.GetInitialDeposit()) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.GetProposer().String()), + ), + ) + + submitEvent := sdk.NewEvent(types.EventTypeSubmitProposal, sdk.NewAttribute(types.AttributeKeyProposalType, msg.GetContent().ProposalType())) + if votingStarted { + submitEvent = submitEvent.AppendAttributes( + sdk.NewAttribute(types.AttributeKeyVotingPeriodStart, fmt.Sprintf("%d", proposal.ProposalId)), + ) + } + + ctx.EventManager().EmitEvent(submitEvent) + return &types.MsgSubmitProposalResponse{ + ProposalId: proposal.ProposalId, + }, nil +} + +func (k msgServer) Vote(goCtx context.Context, msg *types.MsgVote) (*types.MsgVoteResponse, 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.Option) + 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.MsgVoteResponse{}, nil +} + +func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types.MsgDepositResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + accAddr, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + return nil, err + } + votingStarted, err := k.Keeper.AddDeposit(ctx, msg.ProposalId, accAddr, msg.Amount) + if err != nil { + return nil, err + } + + defer telemetry.IncrCounterWithLabels( + []string{types.ModuleName, "deposit"}, + 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.Depositor), + ), + ) + + if votingStarted { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeProposalDeposit, + sdk.NewAttribute(types.AttributeKeyVotingPeriodStart, fmt.Sprintf("%d", msg.ProposalId)), + ), + ) + } + + return &types.MsgDepositResponse{}, nil +} diff --git a/x/gov/keeper/proposal.go b/x/gov/keeper/proposal.go index c5d569314fb2..14a324d78078 100644 --- a/x/gov/keeper/proposal.go +++ b/x/gov/keeper/proposal.go @@ -15,9 +15,9 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content types.Content) (typ return types.Proposal{}, sdkerrors.Wrap(types.ErrNoProposalHandlerExists, content.ProposalRoute()) } - // Execute the proposal content in a cache-wrapped context to validate the - // actual parameter changes before the proposal proceeds through the - // governance process. State is not persisted. + // Execute the proposal content in a new context branch (with branched store) + // to validate the actual parameter changes before the proposal proceeds + // through the governance process. State is not persisted. cacheCtx, _ := ctx.CacheContext() handler := keeper.router.GetRoute(content.ProposalRoute()) if err := handler(cacheCtx, content); err != nil { @@ -32,7 +32,10 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content types.Content) (typ submitTime := ctx.BlockHeader().Time depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod - proposal := types.NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod)) + proposal, err := types.NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod)) + if err != nil { + return types.Proposal{}, err + } keeper.SetProposal(ctx, proposal) keeper.InsertInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime) @@ -49,21 +52,27 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content types.Content) (typ } // GetProposal get proposal from store by ProposalID -func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal types.Proposal, ok bool) { +func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (types.Proposal, bool) { store := ctx.KVStore(keeper.storeKey) + bz := store.Get(types.ProposalKey(proposalID)) if bz == nil { - return + return types.Proposal{}, false } - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal) + + var proposal types.Proposal + keeper.MustUnmarshalProposal(bz, &proposal) + return proposal, true } // SetProposal set a proposal to store func (keeper Keeper) SetProposal(ctx sdk.Context, proposal types.Proposal) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal) - store.Set(types.ProposalKey(proposal.ProposalID), bz) + + bz := keeper.MustMarshalProposal(proposal) + + store.Set(types.ProposalKey(proposal.ProposalId), bz) } // DeleteProposal deletes a proposal from store @@ -81,12 +90,16 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { // IterateProposals iterates over the all the proposals and performs a callback function func (keeper Keeper) IterateProposals(ctx sdk.Context, cb func(proposal types.Proposal) (stop bool)) { store := ctx.KVStore(keeper.storeKey) - iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix) + iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix) defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { var proposal types.Proposal - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &proposal) + err := keeper.UnmarshalProposal(iterator.Value(), &proposal) + if err != nil { + panic(err) + } if cb(proposal) { break @@ -112,7 +125,7 @@ func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals types.Proposals) { // // NOTE: If no filters are provided, all proposals will be returned in paginated // form. -func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, params types.QueryProposalsParams) []types.Proposal { +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, params types.QueryProposalsParams) types.Proposals { proposals := keeper.GetProposals(ctx) filteredProposals := make([]types.Proposal, 0, len(proposals)) @@ -126,12 +139,12 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, params types.QueryPro // match voter address (if supplied) if len(params.Voter) > 0 { - _, matchVoter = keeper.GetVote(ctx, p.ProposalID, params.Voter) + _, matchVoter = keeper.GetVote(ctx, p.ProposalId, params.Voter) } // match depositor (if supplied) if len(params.Depositor) > 0 { - _, matchDepositor = keeper.GetDeposit(ctx, p.ProposalID, params.Depositor) + _, matchDepositor = keeper.GetDeposit(ctx, p.ProposalId, params.Depositor) } if matchVoter && matchDepositor && matchStatus { @@ -167,13 +180,44 @@ func (keeper Keeper) SetProposalID(ctx sdk.Context, proposalID uint64) { store.Set(types.ProposalIDKey, types.GetProposalIDBytes(proposalID)) } -func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal types.Proposal) { +func (keeper Keeper) ActivateVotingPeriod(ctx sdk.Context, proposal types.Proposal) { proposal.VotingStartTime = ctx.BlockHeader().Time votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod proposal.VotingEndTime = proposal.VotingStartTime.Add(votingPeriod) proposal.Status = types.StatusVotingPeriod keeper.SetProposal(ctx, proposal) - keeper.RemoveFromInactiveProposalQueue(ctx, proposal.ProposalID, proposal.DepositEndTime) - keeper.InsertActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime) + keeper.RemoveFromInactiveProposalQueue(ctx, proposal.ProposalId, proposal.DepositEndTime) + keeper.InsertActiveProposalQueue(ctx, proposal.ProposalId, proposal.VotingEndTime) +} + +func (keeper Keeper) MarshalProposal(proposal types.Proposal) ([]byte, error) { + bz, err := keeper.cdc.MarshalBinaryBare(&proposal) + if err != nil { + return nil, err + } + return bz, nil +} + +func (keeper Keeper) UnmarshalProposal(bz []byte, proposal *types.Proposal) error { + err := keeper.cdc.UnmarshalBinaryBare(bz, proposal) + if err != nil { + return err + } + return nil +} + +func (keeper Keeper) MustMarshalProposal(proposal types.Proposal) []byte { + bz, err := keeper.MarshalProposal(proposal) + if err != nil { + panic(err) + } + return bz +} + +func (keeper Keeper) MustUnmarshalProposal(bz []byte, proposal *types.Proposal) { + err := keeper.UnmarshalProposal(bz, proposal) + if err != nil { + panic(err) + } } diff --git a/x/gov/keeper/proposal_test.go b/x/gov/keeper/proposal_test.go index b8f37cbd3649..1a833737d940 100644 --- a/x/gov/keeper/proposal_test.go +++ b/x/gov/keeper/proposal_test.go @@ -1,148 +1,112 @@ -package keeper +package keeper_test import ( "errors" + "fmt" "strings" "testing" "time" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov/types" ) func TestGetSetProposal(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID - keeper.SetProposal(ctx, proposal) + proposalID := proposal.ProposalId + app.GovKeeper.SetProposal(ctx, proposal) - gotProposal, ok := keeper.GetProposal(ctx, proposalID) + gotProposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - require.True(t, ProposalEqual(proposal, gotProposal)) + require.True(t, proposal.Equal(gotProposal)) } func TestActivateVotingPeriod(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) require.True(t, proposal.VotingStartTime.Equal(time.Time{})) - keeper.activateVotingPeriod(ctx, proposal) + app.GovKeeper.ActivateVotingPeriod(ctx, proposal) require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time)) - proposal, ok := keeper.GetProposal(ctx, proposal.ProposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposal.ProposalId) require.True(t, ok) - activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime) + activeIterator := app.GovKeeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime) require.True(t, activeIterator.Valid()) proposalID := types.GetProposalIDFromBytes(activeIterator.Value()) - require.Equal(t, proposalID, proposal.ProposalID) + require.Equal(t, proposalID, proposal.ProposalId) activeIterator.Close() } -type validProposal struct{} - -func (validProposal) GetTitle() string { return "title" } -func (validProposal) GetDescription() string { return "description" } -func (validProposal) ProposalRoute() string { return types.RouterKey } -func (validProposal) ProposalType() string { return types.ProposalTypeText } -func (validProposal) String() string { return "" } -func (validProposal) ValidateBasic() error { return nil } - -type invalidProposalTitle1 struct{ validProposal } - -func (invalidProposalTitle1) GetTitle() string { return "" } - -type invalidProposalTitle2 struct{ validProposal } - -func (invalidProposalTitle2) GetTitle() string { return strings.Repeat("1234567890", 100) } - -type invalidProposalDesc1 struct{ validProposal } - -func (invalidProposalDesc1) GetDescription() string { return "" } - -type invalidProposalDesc2 struct{ validProposal } - -func (invalidProposalDesc2) GetDescription() string { return strings.Repeat("1234567890", 1000) } - -type invalidProposalRoute struct{ validProposal } +type invalidProposalRoute struct{ types.TextProposal } func (invalidProposalRoute) ProposalRoute() string { return "nonexistingroute" } -type invalidProposalValidation struct{ validProposal } - -func (invalidProposalValidation) ValidateBasic() error { - return errors.New("invalid proposal") -} - -func registerTestCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(validProposal{}, "test/validproposal", nil) - cdc.RegisterConcrete(invalidProposalTitle1{}, "test/invalidproposalt1", nil) - cdc.RegisterConcrete(invalidProposalTitle2{}, "test/invalidproposalt2", nil) - cdc.RegisterConcrete(invalidProposalDesc1{}, "test/invalidproposald1", nil) - cdc.RegisterConcrete(invalidProposalDesc2{}, "test/invalidproposald2", nil) - cdc.RegisterConcrete(invalidProposalRoute{}, "test/invalidproposalr", nil) - cdc.RegisterConcrete(invalidProposalValidation{}, "test/invalidproposalv", nil) -} - func TestSubmitProposal(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) - - registerTestCodec(keeper.cdc) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) testCases := []struct { content types.Content expectedErr error }{ - {validProposal{}, nil}, + {&types.TextProposal{Title: "title", Description: "description"}, nil}, // Keeper does not check the validity of title and description, no error - {invalidProposalTitle1{}, nil}, - {invalidProposalTitle2{}, nil}, - {invalidProposalDesc1{}, nil}, - {invalidProposalDesc2{}, nil}, + {&types.TextProposal{Title: "", Description: "description"}, nil}, + {&types.TextProposal{Title: strings.Repeat("1234567890", 100), Description: "description"}, nil}, + {&types.TextProposal{Title: "title", Description: ""}, nil}, + {&types.TextProposal{Title: "title", Description: strings.Repeat("1234567890", 1000)}, nil}, // error only when invalid route - {invalidProposalRoute{}, types.ErrNoProposalHandlerExists}, - // Keeper does not call ValidateBasic, msg.ValidateBasic does - {invalidProposalValidation{}, nil}, + {&invalidProposalRoute{}, types.ErrNoProposalHandlerExists}, } for i, tc := range testCases { - _, err := keeper.SubmitProposal(ctx, tc.content) + _, err := app.GovKeeper.SubmitProposal(ctx, tc.content) require.True(t, errors.Is(tc.expectedErr, err), "tc #%d; got: %v, expected: %v", i, err, tc.expectedErr) } } func TestGetProposalsFiltered(t *testing.T) { proposalID := uint64(1) - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + status := []types.ProposalStatus{types.StatusDepositPeriod, types.StatusVotingPeriod} - addr1 := sdk.AccAddress("foo") + addr1 := sdk.AccAddress("foo_________________") for _, s := range status { for i := 0; i < 50; i++ { - p := types.NewProposal(TestProposal, proposalID, time.Now(), time.Now()) + p, err := types.NewProposal(TestProposal, proposalID, time.Now(), time.Now()) + require.NoError(t, err) + p.Status = s if i%2 == 0 { d := types.NewDeposit(proposalID, addr1, nil) v := types.NewVote(proposalID, addr1, types.OptionYes) - keeper.SetDeposit(ctx, d) - keeper.SetVote(ctx, v) + app.GovKeeper.SetDeposit(ctx, d) + app.GovKeeper.SetVote(ctx, v) } - keeper.SetProposal(ctx, p) + app.GovKeeper.SetProposal(ctx, p) proposalID++ } } @@ -165,14 +129,16 @@ func TestGetProposalsFiltered(t *testing.T) { {types.NewQueryProposalsParams(1, 50, types.StatusVotingPeriod, nil, nil), 50}, } - for _, tc := range testCases { - proposals := keeper.GetProposalsFiltered(ctx, tc.params) - require.Len(t, proposals, tc.expectedNumResults) - - for _, p := range proposals { - if len(tc.params.ProposalStatus.String()) != 0 { - require.Equal(t, tc.params.ProposalStatus, p.Status) + for i, tc := range testCases { + t.Run(fmt.Sprintf("Test Case %d", i), func(t *testing.T) { + proposals := app.GovKeeper.GetProposalsFiltered(ctx, tc.params) + require.Len(t, proposals, tc.expectedNumResults) + + for _, p := range proposals { + if types.ValidProposalStatus(tc.params.ProposalStatus) { + require.Equal(t, tc.params.ProposalStatus, p.Status) + } } - } + }) } } diff --git a/x/gov/keeper/querier.go b/x/gov/keeper/querier.go index 9545e5e29b83..6eb343d070a7 100644 --- a/x/gov/keeper/querier.go +++ b/x/gov/keeper/querier.go @@ -11,32 +11,32 @@ import ( ) // NewQuerier creates a new gov Querier instance -func NewQuerier(keeper Keeper) sdk.Querier { +func NewQuerier(keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { switch path[0] { case types.QueryParams: - return queryParams(ctx, path[1:], req, keeper) + return queryParams(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryProposals: - return queryProposals(ctx, path[1:], req, keeper) + return queryProposals(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryProposal: - return queryProposal(ctx, path[1:], req, keeper) + return queryProposal(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryDeposits: - return queryDeposits(ctx, path[1:], req, keeper) + return queryDeposits(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryDeposit: - return queryDeposit(ctx, path[1:], req, keeper) + return queryDeposit(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryVotes: - return queryVotes(ctx, path[1:], req, keeper) + return queryVotes(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryVote: - return queryVote(ctx, path[1:], req, keeper) + return queryVote(ctx, path[1:], req, keeper, legacyQuerierCdc) case types.QueryTally: - return queryTally(ctx, path[1:], req, keeper) + return queryTally(ctx, path[1:], req, keeper, legacyQuerierCdc) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) @@ -44,24 +44,24 @@ func NewQuerier(keeper Keeper) sdk.Querier { } } -func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { switch path[0] { case types.ParamDeposit: - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx)) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, keeper.GetDepositParams(ctx)) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil case types.ParamVoting: - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx)) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, keeper.GetVotingParams(ctx)) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil case types.ParamTallying: - bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx)) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, keeper.GetTallyParams(ctx)) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -73,9 +73,9 @@ func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper K } // nolint: unparam -func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryProposalParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -85,7 +85,7 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper return nil, sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", params.ProposalID) } - bz, err := codec.MarshalJSONIndent(keeper.cdc, proposal) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, proposal) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -94,15 +94,15 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper } // nolint: unparam -func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDepositParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositor) - bz, err := codec.MarshalJSONIndent(keeper.cdc, deposit) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, deposit) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -111,15 +111,15 @@ func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper } // nolint: unparam -func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryVoteParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) - bz, err := codec.MarshalJSONIndent(keeper.cdc, vote) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, vote) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -128,9 +128,9 @@ func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Kee } // nolint: unparam -func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryProposalParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -140,7 +140,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper deposits = types.Deposits{} } - bz, err := codec.MarshalJSONIndent(keeper.cdc, deposits) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, deposits) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -149,9 +149,9 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper } // nolint: unparam -func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryProposalParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -177,7 +177,7 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke _, _, tallyResult = keeper.Tally(ctx, proposal) } - bz, err := codec.MarshalJSONIndent(keeper.cdc, tallyResult) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, tallyResult) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -186,9 +186,9 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } // nolint: unparam -func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryProposalVotesParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -205,7 +205,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } } - bz, err := codec.MarshalJSONIndent(keeper.cdc, votes) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, votes) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -213,9 +213,9 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke return bz, nil } -func queryProposals(ctx sdk.Context, _ []string, req abci.RequestQuery, keeper Keeper) ([]byte, error) { +func queryProposals(ctx sdk.Context, _ []string, req abci.RequestQuery, keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryProposalsParams - err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -225,7 +225,7 @@ func queryProposals(ctx sdk.Context, _ []string, req abci.RequestQuery, keeper K proposals = types.Proposals{} } - bz, err := codec.MarshalJSONIndent(keeper.cdc, proposals) + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, proposals) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } diff --git a/x/gov/keeper/querier_test.go b/x/gov/keeper/querier_test.go index f79057f03e96..d405b8b49427 100644 --- a/x/gov/keeper/querier_test.go +++ b/x/gov/keeper/querier_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "math/rand" @@ -8,15 +8,18 @@ import ( "github.com/stretchr/testify/require" 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" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" ) const custom = "custom" -func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (types.DepositParams, types.VotingParams, types.TallyParams) { +func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier) (types.DepositParams, types.VotingParams, types.TallyParams) { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamDeposit}, "/"), Data: []byte{}, @@ -57,7 +60,7 @@ func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier s } func getQueriedProposals( - t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, + t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, depositor, voter sdk.AccAddress, status types.ProposalStatus, page, limit int, ) []types.Proposal { @@ -76,7 +79,7 @@ func getQueriedProposals( return proposals } -func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) types.Deposit { +func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) types.Deposit { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposit}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryDepositParams(proposalID, depositor)), @@ -92,7 +95,7 @@ func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier return deposit } -func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []types.Deposit { +func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64) []types.Deposit { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposits}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)), @@ -108,7 +111,7 @@ func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier return deposits } -func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) types.Vote { +func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) types.Vote { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"), Data: cdc.MustMarshalJSON(types.NewQueryVoteParams(proposalID, voter)), @@ -124,7 +127,7 @@ func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk return vote } -func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, +func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64, page, limit int) []types.Vote { query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"), @@ -142,53 +145,68 @@ func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sd } func TestQueries(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 1000) - querier := NewQuerier(keeper) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + legacyQuerierCdc := app.LegacyAmino() + querier := keeper.NewQuerier(app.GovKeeper, legacyQuerierCdc) + + TestAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(20000001)) oneCoins := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1)) consCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10))) tp := TestProposal - depositParams, _, _ := getQueriedParams(t, ctx, keeper.cdc, querier) + depositParams, _, _ := getQueriedParams(t, ctx, legacyQuerierCdc, querier) // TestAddrs[0] proposes (and deposits) proposals #1 and #2 - proposal1, err := keeper.SubmitProposal(ctx, tp) + proposal1, err := app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + deposit1 := types.NewDeposit(proposal1.ProposalId, TestAddrs[0], oneCoins) + depositer1, err := sdk.AccAddressFromBech32(deposit1.Depositor) require.NoError(t, err) - deposit1 := types.NewDeposit(proposal1.ProposalID, TestAddrs[0], oneCoins) - _, err = keeper.AddDeposit(ctx, deposit1.ProposalID, deposit1.Depositor, deposit1.Amount) + _, err = app.GovKeeper.AddDeposit(ctx, deposit1.ProposalId, depositer1, deposit1.Amount) require.NoError(t, err) proposal1.TotalDeposit = proposal1.TotalDeposit.Add(deposit1.Amount...) - proposal2, err := keeper.SubmitProposal(ctx, tp) + proposal2, err := app.GovKeeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + deposit2 := types.NewDeposit(proposal2.ProposalId, TestAddrs[0], consCoins) + depositer2, err := sdk.AccAddressFromBech32(deposit2.Depositor) require.NoError(t, err) - deposit2 := types.NewDeposit(proposal2.ProposalID, TestAddrs[0], consCoins) - _, err = keeper.AddDeposit(ctx, deposit2.ProposalID, deposit2.Depositor, deposit2.Amount) + _, err = app.GovKeeper.AddDeposit(ctx, deposit2.ProposalId, depositer2, deposit2.Amount) require.NoError(t, err) proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit2.Amount...) // TestAddrs[1] proposes (and deposits) on proposal #3 - proposal3, err := keeper.SubmitProposal(ctx, tp) + proposal3, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - deposit3 := types.NewDeposit(proposal3.ProposalID, TestAddrs[1], oneCoins) - _, err = keeper.AddDeposit(ctx, deposit3.ProposalID, deposit3.Depositor, deposit3.Amount) + deposit3 := types.NewDeposit(proposal3.ProposalId, TestAddrs[1], oneCoins) + depositer3, err := sdk.AccAddressFromBech32(deposit3.Depositor) + require.NoError(t, err) + + _, err = app.GovKeeper.AddDeposit(ctx, deposit3.ProposalId, depositer3, deposit3.Amount) require.NoError(t, err) proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit3.Amount...) // TestAddrs[1] deposits on proposals #2 & #3 - deposit4 := types.NewDeposit(proposal2.ProposalID, TestAddrs[1], depositParams.MinDeposit) - _, err = keeper.AddDeposit(ctx, deposit4.ProposalID, deposit4.Depositor, deposit4.Amount) + deposit4 := types.NewDeposit(proposal2.ProposalId, TestAddrs[1], depositParams.MinDeposit) + depositer4, err := sdk.AccAddressFromBech32(deposit4.Depositor) + require.NoError(t, err) + _, err = app.GovKeeper.AddDeposit(ctx, deposit4.ProposalId, depositer4, deposit4.Amount) require.NoError(t, err) proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit4.Amount...) proposal2.Status = types.StatusVotingPeriod proposal2.VotingEndTime = proposal2.VotingEndTime.Add(types.DefaultPeriod) - deposit5 := types.NewDeposit(proposal3.ProposalID, TestAddrs[1], depositParams.MinDeposit) - _, err = keeper.AddDeposit(ctx, deposit5.ProposalID, deposit5.Depositor, deposit5.Amount) + deposit5 := types.NewDeposit(proposal3.ProposalId, TestAddrs[1], depositParams.MinDeposit) + depositer5, err := sdk.AccAddressFromBech32(deposit5.Depositor) + require.NoError(t, err) + _, err = app.GovKeeper.AddDeposit(ctx, deposit5.ProposalId, depositer5, deposit5.Amount) require.NoError(t, err) proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit5.Amount...) @@ -198,119 +216,133 @@ func TestQueries(t *testing.T) { deposit5.Amount = deposit5.Amount.Add(deposit3.Amount...) // check deposits on proposal1 match individual deposits - deposits := getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal1.ProposalID) + + deposits := getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal1.ProposalId) require.Len(t, deposits, 1) require.Equal(t, deposit1, deposits[0]) - deposit := getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal1.ProposalID, TestAddrs[0]) + deposit := getQueriedDeposit(t, ctx, legacyQuerierCdc, querier, proposal1.ProposalId, TestAddrs[0]) require.Equal(t, deposit1, deposit) // check deposits on proposal2 match individual deposits - deposits = getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal2.ProposalID) + deposits = getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId) require.Len(t, deposits, 2) // NOTE order of deposits is determined by the addresses require.Equal(t, deposit2, deposits[0]) require.Equal(t, deposit4, deposits[1]) // check deposits on proposal3 match individual deposits - deposits = getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal3.ProposalID) + deposits = getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId) require.Len(t, deposits, 1) require.Equal(t, deposit5, deposits[0]) - deposit = getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal3.ProposalID, TestAddrs[1]) + deposit = getQueriedDeposit(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId, TestAddrs[1]) require.Equal(t, deposit5, deposit) // Only proposal #1 should be in types.Deposit Period - proposals := getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusDepositPeriod, 1, 0) + proposals := getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusDepositPeriod, 1, 0) require.Len(t, proposals, 1) require.Equal(t, proposal1, proposals[0]) // Only proposals #2 and #3 should be in Voting Period - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusVotingPeriod, 1, 0) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusVotingPeriod, 1, 0) require.Len(t, proposals, 2) require.Equal(t, proposal2, proposals[0]) 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) - keeper.SetVote(ctx, vote1) - keeper.SetVote(ctx, vote2) + vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.OptionYes) + vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], 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) - keeper.SetVote(ctx, vote3) + vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.OptionYes) + app.GovKeeper.SetVote(ctx, vote3) // Test query voted by TestAddrs[0] - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, TestAddrs[0], types.StatusNil, 1, 0) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, TestAddrs[0], types.StatusNil, 1, 0) require.Equal(t, proposal2, proposals[0]) require.Equal(t, proposal3, proposals[1]) // Test query votes on types.Proposal 2 - votes := getQueriedVotes(t, ctx, keeper.cdc, querier, proposal2.ProposalID, 1, 0) + votes := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId, 1, 0) require.Len(t, votes, 1) require.Equal(t, vote1, votes[0]) - vote := getQueriedVote(t, ctx, keeper.cdc, querier, proposal2.ProposalID, TestAddrs[0]) + vote := getQueriedVote(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId, TestAddrs[0]) require.Equal(t, vote1, vote) // Test query votes on types.Proposal 3 - votes = getQueriedVotes(t, ctx, keeper.cdc, querier, proposal3.ProposalID, 1, 0) + votes = getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId, 1, 0) require.Len(t, votes, 2) require.Equal(t, vote2, votes[0]) require.Equal(t, vote3, votes[1]) // Test query all proposals - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusNil, 1, 0) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusNil, 1, 0) require.Equal(t, proposal1, proposals[0]) require.Equal(t, proposal2, proposals[1]) require.Equal(t, proposal3, proposals[2]) // Test query voted by TestAddrs[1] - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, TestAddrs[1], types.StatusNil, 1, 0) - require.Equal(t, proposal3.ProposalID, proposals[0].ProposalID) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, TestAddrs[1], types.StatusNil, 1, 0) + require.Equal(t, proposal3.ProposalId, proposals[0].ProposalId) // Test query deposited by TestAddrs[0] - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[0], nil, types.StatusNil, 1, 0) - require.Equal(t, proposal1.ProposalID, proposals[0].ProposalID) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[0], nil, types.StatusNil, 1, 0) + require.Equal(t, proposal1.ProposalId, proposals[0].ProposalId) // Test query deposited by addr2 - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[1], nil, types.StatusNil, 1, 0) - require.Equal(t, proposal2.ProposalID, proposals[0].ProposalID) - require.Equal(t, proposal3.ProposalID, proposals[1].ProposalID) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[1], nil, types.StatusNil, 1, 0) + require.Equal(t, proposal2.ProposalId, proposals[0].ProposalId) + require.Equal(t, proposal3.ProposalId, proposals[1].ProposalId) // Test query voted AND deposited by addr1 - proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[0], TestAddrs[0], types.StatusNil, 1, 0) - require.Equal(t, proposal2.ProposalID, proposals[0].ProposalID) + proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[0], TestAddrs[0], types.StatusNil, 1, 0) + require.Equal(t, proposal2.ProposalId, proposals[0].ProposalId) } func TestPaginatedVotesQuery(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 1000) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + legacyQuerierCdc := app.LegacyAmino() proposal := types.Proposal{ - ProposalID: 100, + ProposalId: 100, Status: types.StatusVotingPeriod, } - keeper.SetProposal(ctx, proposal) + + app.GovKeeper.SetProposal(ctx, proposal) votes := make([]types.Vote, 20) - rand := rand.New(rand.NewSource(time.Now().UnixNano())) - addr := make(sdk.AccAddress, 20) + random := rand.New(rand.NewSource(time.Now().UnixNano())) + addrMap := make(map[string]struct{}) + genAddr := func() string { + addr := make(sdk.AccAddress, 20) + for { + random.Read(addr) + addrStr := addr.String() + if _, ok := addrMap[addrStr]; !ok { + addrMap[addrStr] = struct{}{} + return addrStr + } + } + } for i := range votes { - rand.Read(addr) vote := types.Vote{ - ProposalID: proposal.ProposalID, - Voter: addr, + ProposalId: proposal.ProposalId, + Voter: genAddr(), Option: types.OptionYes, } votes[i] = vote - keeper.SetVote(ctx, vote) + app.GovKeeper.SetVote(ctx, vote) } - querier := NewQuerier(keeper) + querier := keeper.NewQuerier(app.GovKeeper, legacyQuerierCdc) // keeper preserves consistent order for each query, but this is not the insertion order - all := getQueriedVotes(t, ctx, keeper.cdc, querier, proposal.ProposalID, 1, 0) + all := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal.ProposalId, 1, 0) require.Equal(t, len(all), len(votes)) type testCase struct { @@ -344,7 +376,7 @@ func TestPaginatedVotesQuery(t *testing.T) { } { tc := tc t.Run(tc.description, func(t *testing.T) { - votes := getQueriedVotes(t, ctx, keeper.cdc, querier, proposal.ProposalID, tc.page, tc.limit) + votes := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal.ProposalId, tc.page, tc.limit) require.Equal(t, len(tc.votes), len(votes)) for i := range votes { require.Equal(t, tc.votes[i], votes[i]) diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index f6b0008d4052..cddfd59027dd 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // TODO: Break into several smaller functions for clarity @@ -21,7 +21,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo currValidators := make(map[string]types.ValidatorGovInfo) // fetch all the bonded validators, insert them into currValidators - keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator exported.ValidatorI) (stop bool) { + keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { currValidators[validator.GetOperator().String()] = types.NewValidatorGovInfo( validator.GetOperator(), validator.GetBondedTokens(), @@ -33,16 +33,22 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo return false }) - keeper.IterateVotes(ctx, proposal.ProposalID, func(vote types.Vote) bool { + keeper.IterateVotes(ctx, proposal.ProposalId, func(vote types.Vote) bool { // if validator, just record it in the map - valAddrStr := sdk.ValAddress(vote.Voter).String() + voter, err := sdk.AccAddressFromBech32(vote.Voter) + + if err != nil { + panic(err) + } + + valAddrStr := sdk.ValAddress(voter.Bytes()).String() if val, ok := currValidators[valAddrStr]; ok { val.Vote = vote.Option currValidators[valAddrStr] = val } // iterate over all delegations from voter, deduct from any delegated-to validators - keeper.sk.IterateDelegations(ctx, vote.Voter, func(index int64, delegation exported.DelegationI) (stop bool) { + keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) { valAddrStr := delegation.GetValidatorAddr().String() if val, ok := currValidators[valAddrStr]; ok { @@ -51,8 +57,8 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares()) currValidators[valAddrStr] = val - delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) - votingPower := delegatorShare.MulInt(val.BondedTokens) + // delegation shares * bonded / total shares + votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares) results[vote.Option] = results[vote.Option].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) @@ -61,7 +67,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo return false }) - keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) + keeper.deleteVote(ctx, vote.ProposalId, voter) return false }) @@ -72,8 +78,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo } sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions) - fractionAfterDeductions := sharesAfterDeductions.Quo(val.DelegatorShares) - votingPower := fractionAfterDeductions.MulInt(val.BondedTokens) + votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares) results[val.Vote] = results[val.Vote].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) @@ -100,7 +105,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo } // If more than 1/3 of voters veto, proposal fails - if results[types.OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) { + if results[types.OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.VetoThreshold) { return false, true, tallyResults } diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index ceaed4eb4437..77f468cd2c0b 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -1,29 +1,34 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + 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/gov/types" "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestTallyNoOneVotes(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 5, 5}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + createValidators(t, ctx, app, []int64{5, 5, 5}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.True(t, burnDeposits) @@ -31,44 +36,50 @@ func TestTallyNoOneVotes(t *testing.T) { } func TestTallyNoQuorum(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{2, 5, 0}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + createValidators(t, ctx, app, []int64{2, 5, 0}) + + addrs := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000000)) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - err = keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes) + err = app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes) require.Nil(t, err) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, _ := keeper.Tally(ctx, proposal) + passes, burnDeposits, _ := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.True(t, burnDeposits) } func TestTallyOnlyValidatorsAllYes(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 5, 5}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs, _ := createValidators(t, ctx, app, []int64{5, 5, 5}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) @@ -76,44 +87,48 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { } func TestTallyOnlyValidators51No(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 0}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 0}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionNo)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, _ := keeper.Tally(ctx, proposal) + passes, burnDeposits, _ := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) } func TestTallyOnlyValidators51Yes(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 0}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 0}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.OptionYes)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) @@ -121,48 +136,51 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { } func TestTallyOnlyValidatorsVetoed(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{6, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNoWithVeto)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.True(t, burnDeposits) require.False(t, tallyResults.Equals(types.EmptyTallyResult())) - } func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{6, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionAbstain)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) @@ -170,23 +188,25 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { } func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{6, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionAbstain)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) @@ -194,22 +214,25 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { } func TestTallyOnlyValidatorsNonVoter(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 7}) + valAccAddr1, valAccAddr2 := valAccAddrs[0], valAccAddrs[1] tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) @@ -217,33 +240,35 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { } func TestTallyDelgatorOverride(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs, valAddrs := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := sdk.TokensFromConsensusPower(30) - val1, found := sk.GetValidator(ctx, valOpAddr1) + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) require.True(t, found) - _, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val1, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[4], delTokens, stakingtypes.Unbonded, val1, true) require.NoError(t, err) - _ = staking.EndBlocker(ctx, sk) + _ = staking.EndBlocker(ctx, app.StakingKeeper) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionNo)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) @@ -251,32 +276,34 @@ func TestTallyDelgatorOverride(t *testing.T) { } func TestTallyDelgatorInherit(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := sdk.TokensFromConsensusPower(30) - val3, found := sk.GetValidator(ctx, valOpAddr3) + val3, found := app.StakingKeeper.GetValidator(ctx, vals[2]) require.True(t, found) - _, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val3, true) require.NoError(t, err) - _ = staking.EndBlocker(ctx, sk) + _ = staking.EndBlocker(ctx, app.StakingKeeper) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) @@ -284,37 +311,39 @@ func TestTallyDelgatorInherit(t *testing.T) { } func TestTallyDelgatorMultipleOverride(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{5, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := sdk.TokensFromConsensusPower(10) - val1, found := sk.GetValidator(ctx, valOpAddr1) + val1, found := app.StakingKeeper.GetValidator(ctx, vals[0]) require.True(t, found) - val2, found := sk.GetValidator(ctx, valOpAddr2) + val2, found := app.StakingKeeper.GetValidator(ctx, vals[1]) require.True(t, found) - _, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val1, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val1, true) require.NoError(t, err) - _, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true) + _, err = app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val2, true) require.NoError(t, err) - _ = staking.EndBlocker(ctx, sk) + _ = staking.EndBlocker(ctx, app.StakingKeeper) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionNo)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) @@ -322,36 +351,40 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { } func TestTallyDelgatorMultipleInherit(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{25, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + createValidators(t, ctx, app, []int64{25, 6, 7}) + + addrs, vals := createValidators(t, ctx, app, []int64{5, 6, 7}) delTokens := sdk.TokensFromConsensusPower(10) - val2, found := sk.GetValidator(ctx, valOpAddr2) + val2, found := app.StakingKeeper.GetValidator(ctx, vals[1]) require.True(t, found) - val3, found := sk.GetValidator(ctx, valOpAddr3) + val3, found := app.StakingKeeper.GetValidator(ctx, vals[2]) require.True(t, found) - _, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val2, true) require.NoError(t, err) - _, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true) + _, err = app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val3, true) require.NoError(t, err) - _ = staking.EndBlocker(ctx, sk) + _ = staking.EndBlocker(ctx, app.StakingKeeper) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.False(t, passes) require.False(t, burnDeposits) @@ -359,38 +392,42 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { } func TestTallyJailedValidator(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{25, 6, 7}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs, valAddrs := createValidators(t, ctx, app, []int64{25, 6, 7}) delTokens := sdk.TokensFromConsensusPower(10) - val2, found := sk.GetValidator(ctx, valOpAddr2) + val2, found := app.StakingKeeper.GetValidator(ctx, valAddrs[1]) require.True(t, found) - val3, found := sk.GetValidator(ctx, valOpAddr3) + val3, found := app.StakingKeeper.GetValidator(ctx, valAddrs[2]) require.True(t, found) - _, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val2, true) require.NoError(t, err) - _, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true) + _, err = app.StakingKeeper.Delegate(ctx, addrs[3], delTokens, stakingtypes.Unbonded, val3, true) require.NoError(t, err) - _ = staking.EndBlocker(ctx, sk) + _ = staking.EndBlocker(ctx, app.StakingKeeper) - sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address())) + consAddr, err := val2.GetConsAddr() + require.NoError(t, err) + app.StakingKeeper.Jail(ctx, sdk.ConsAddress(consAddr.Bytes())) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) @@ -398,30 +435,32 @@ func TestTallyJailedValidator(t *testing.T) { } func TestTallyValidatorMultipleDelegations(t *testing.T) { - ctx, _, keeper, sk, _ := createTestInput(t, false, 100) - createValidators(ctx, sk, []int64{10, 10, 10}) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs, valAddrs := createValidators(t, ctx, app, []int64{10, 10, 10}) delTokens := sdk.TokensFromConsensusPower(10) - val2, found := sk.GetValidator(ctx, valOpAddr2) + val2, found := app.StakingKeeper.GetValidator(ctx, valAddrs[1]) require.True(t, found) - _, err := sk.Delegate(ctx, valAccAddr1, delTokens, sdk.Unbonded, val2, true) + _, err := app.StakingKeeper.Delegate(ctx, addrs[0], delTokens, stakingtypes.Unbonded, val2, true) require.NoError(t, err) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) - require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) + 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)) - proposal, ok := keeper.GetProposal(ctx, proposalID) + proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + passes, burnDeposits, tallyResults := app.GovKeeper.Tally(ctx, proposal) require.True(t, passes) require.False(t, burnDeposits) diff --git a/x/gov/keeper/test_common.go b/x/gov/keeper/test_common.go deleted file mode 100644 index 777d71a8c887..000000000000 --- a/x/gov/keeper/test_common.go +++ /dev/null @@ -1,205 +0,0 @@ -// nolint -package keeper // noalias - -// DONTCOVER - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" -) - -// dummy addresses used for testing -var ( - delPk1 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51") - delPk2 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50") - delPk3 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52") - delAddr1 = sdk.AccAddress(delPk1.Address()) - delAddr2 = sdk.AccAddress(delPk2.Address()) - delAddr3 = sdk.AccAddress(delPk3.Address()) - - valOpPk1 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB53") - valOpPk2 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB54") - valOpPk3 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB55") - valOpAddr1 = sdk.ValAddress(valOpPk1.Address()) - valOpAddr2 = sdk.ValAddress(valOpPk2.Address()) - valOpAddr3 = sdk.ValAddress(valOpPk3.Address()) - valAccAddr1 = sdk.AccAddress(valOpPk1.Address()) - valAccAddr2 = sdk.AccAddress(valOpPk2.Address()) - valAccAddr3 = sdk.AccAddress(valOpPk3.Address()) - - TestAddrs = []sdk.AccAddress{ - delAddr1, delAddr2, delAddr3, - valAccAddr1, valAccAddr2, valAccAddr3, - } - - emptyDelAddr sdk.AccAddress - emptyValAddr sdk.ValAddress - emptyPubkey crypto.PubKey -) - -// TODO: remove dependency with staking -var ( - TestProposal = types.NewTextProposal("Test", "description") - TestDescription = staking.NewDescription("T", "E", "S", "T", "Z") - TestCommissionRates = staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) -) - -// TODO move to common testing framework -func newPubKey(pk string) (res crypto.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - var pkEd ed25519.PubKeyEd25519 - copy(pkEd[:], pkBytes[:]) - return pkEd -} - -func makeTestCodec() *codec.Codec { - var cdc = codec.New() - auth.RegisterCodec(cdc) - types.RegisterCodec(cdc) - supply.RegisterCodec(cdc) - staking.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -func createTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, types.SupplyKeeper) { - - initTokens := sdk.TokensFromConsensusPower(initPower) - - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyGov := sdk.NewKVStoreKey(types.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyGov, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - require.Nil(t, ms.LoadLatestVersion()) - - ctx := sdk.NewContext(ms, abci.Header{ChainID: "gov-chain"}, isCheckTx, log.NewNopLogger()) - ctx = ctx.WithConsensusParams( - &abci.ConsensusParams{ - Validator: &abci.ValidatorParams{ - PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, - }, - }, - ) - cdc := makeTestCodec() - - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - types.ModuleName: nil, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - - // create module accounts - feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner) - notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true - blacklistedAddrs[govAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - pk := params.NewKeeper(cdc, keyParams, tkeyParams) - accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), blacklistedAddrs) - supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) - - sk := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace)) - sk.SetParams(ctx, staking.DefaultParams()) - - rtr := types.NewRouter(). - AddRoute(types.RouterKey, types.ProposalHandler) - - keeper := NewKeeper( - cdc, keyGov, pk.Subspace(types.DefaultParamspace).WithKeyTable(types.ParamKeyTable()), supplyKeeper, sk, rtr, - ) - - keeper.SetProposalID(ctx, types.DefaultStartingProposalID) - keeper.SetDepositParams(ctx, types.DefaultDepositParams()) - keeper.SetVotingParams(ctx, types.DefaultVotingParams()) - keeper.SetTallyParams(ctx, types.DefaultTallyParams()) - - initCoins := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens)) - totalSupply := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens.MulRaw(int64(len(TestAddrs))))) - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - for _, addr := range TestAddrs { - _, err := bankKeeper.AddCoins(ctx, addr, initCoins) - require.Nil(t, err) - } - - keeper.supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) - keeper.supplyKeeper.SetModuleAccount(ctx, govAcc) - keeper.supplyKeeper.SetModuleAccount(ctx, bondPool) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) - - return ctx, accountKeeper, keeper, sk, supplyKeeper -} - -// ProposalEqual checks if two proposals are equal (note: slow, for tests only) -func ProposalEqual(proposalA types.Proposal, proposalB types.Proposal) bool { - return bytes.Equal(types.ModuleCdc.MustMarshalBinaryBare(proposalA), - types.ModuleCdc.MustMarshalBinaryBare(proposalB)) -} - -func createValidators(ctx sdk.Context, sk staking.Keeper, powers []int64) { - val1 := staking.NewValidator(valOpAddr1, valOpPk1, staking.Description{}) - val2 := staking.NewValidator(valOpAddr2, valOpPk2, staking.Description{}) - val3 := staking.NewValidator(valOpAddr3, valOpPk3, staking.Description{}) - - sk.SetValidator(ctx, val1) - sk.SetValidator(ctx, val2) - sk.SetValidator(ctx, val3) - sk.SetValidatorByConsAddr(ctx, val1) - sk.SetValidatorByConsAddr(ctx, val2) - sk.SetValidatorByConsAddr(ctx, val3) - sk.SetNewValidatorByPowerIndex(ctx, val1) - sk.SetNewValidatorByPowerIndex(ctx, val2) - sk.SetNewValidatorByPowerIndex(ctx, val3) - - _, _ = sk.Delegate(ctx, valAccAddr1, sdk.TokensFromConsensusPower(powers[0]), sdk.Unbonded, val1, true) - _, _ = sk.Delegate(ctx, valAccAddr2, sdk.TokensFromConsensusPower(powers[1]), sdk.Unbonded, val2, true) - _, _ = sk.Delegate(ctx, valAccAddr3, sdk.TokensFromConsensusPower(powers[2]), sdk.Unbonded, val3, true) - - _ = staking.EndBlocker(ctx, sk) -} diff --git a/x/gov/keeper/vote.go b/x/gov/keeper/vote.go index 309446714287..a35569bc3465 100644 --- a/x/gov/keeper/vote.go +++ b/x/gov/keeper/vote.go @@ -62,15 +62,19 @@ func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.A return vote, false } - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote) + keeper.cdc.MustUnmarshalBinaryBare(bz, &vote) return vote, true } // SetVote sets a Vote to the gov store func (keeper Keeper) SetVote(ctx sdk.Context, vote types.Vote) { store := ctx.KVStore(keeper.storeKey) - bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote) - store.Set(types.VoteKey(vote.ProposalID, vote.Voter), bz) + bz := keeper.cdc.MustMarshalBinaryBare(&vote) + addr, err := sdk.AccAddressFromBech32(vote.Voter) + if err != nil { + panic(err) + } + store.Set(types.VoteKey(vote.ProposalId, addr), bz) } // IterateAllVotes iterates over the all the stored votes and performs a callback function @@ -81,7 +85,7 @@ func (keeper Keeper) IterateAllVotes(ctx sdk.Context, cb func(vote types.Vote) ( defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var vote types.Vote - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryBare(iterator.Value(), &vote) if cb(vote) { break @@ -97,7 +101,7 @@ func (keeper Keeper) IterateVotes(ctx sdk.Context, proposalID uint64, cb func(vo defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var vote types.Vote - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote) + keeper.cdc.MustUnmarshalBinaryBare(iterator.Value(), &vote) if cb(vote) { break diff --git a/x/gov/keeper/vote_test.go b/x/gov/keeper/vote_test.go index 36c02d091374..5f5bf0cc73a4 100644 --- a/x/gov/keeper/vote_test.go +++ b/x/gov/keeper/vote_test.go @@ -1,64 +1,70 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + 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/gov/types" ) func TestVotes(t *testing.T) { - ctx, _, keeper, _, _ := createTestInput(t, false, 100) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, sdk.NewInt(30000000)) tp := TestProposal - proposal, err := keeper.SubmitProposal(ctx, tp) + proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) require.NoError(t, err) - proposalID := proposal.ProposalID + proposalID := proposal.ProposalId var invalidOption types.VoteOption = 0x10 - require.Error(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes), "proposal not on voting period") - require.Error(t, keeper.AddVote(ctx, 10, TestAddrs[0], types.OptionYes), "invalid proposal ID") + 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") proposal.Status = types.StatusVotingPeriod - keeper.SetProposal(ctx, proposal) + app.GovKeeper.SetProposal(ctx, proposal) - require.Error(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], invalidOption), "invalid option") + require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], invalidOption), "invalid option") // Test first vote - require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionAbstain)) - vote, found := keeper.GetVote(ctx, proposalID, TestAddrs[0]) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionAbstain)) + vote, found := app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) require.True(t, found) - require.Equal(t, TestAddrs[0], vote.Voter) - require.Equal(t, proposalID, vote.ProposalID) + require.Equal(t, addrs[0].String(), vote.Voter) + require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, types.OptionAbstain, vote.Option) // Test change of vote - require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes)) - vote, found = keeper.GetVote(ctx, proposalID, TestAddrs[0]) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.OptionYes)) + vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) require.True(t, found) - require.Equal(t, TestAddrs[0], vote.Voter) - require.Equal(t, proposalID, vote.ProposalID) + require.Equal(t, addrs[0].String(), vote.Voter) + require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, types.OptionYes, vote.Option) // Test second vote - require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[1], types.OptionNoWithVeto)) - vote, found = keeper.GetVote(ctx, proposalID, TestAddrs[1]) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.OptionNoWithVeto)) + vote, found = app.GovKeeper.GetVote(ctx, proposalID, addrs[1]) require.True(t, found) - require.Equal(t, TestAddrs[1], vote.Voter) - require.Equal(t, proposalID, vote.ProposalID) + require.Equal(t, addrs[1].String(), vote.Voter) + require.Equal(t, proposalID, vote.ProposalId) require.Equal(t, types.OptionNoWithVeto, vote.Option) // Test vote iterator // NOTE order of deposits is determined by the addresses - votes := keeper.GetAllVotes(ctx) + votes := app.GovKeeper.GetAllVotes(ctx) require.Len(t, votes, 2) - require.Equal(t, votes, keeper.GetVotes(ctx, proposalID)) - require.Equal(t, TestAddrs[0], votes[0].Voter) - require.Equal(t, proposalID, votes[0].ProposalID) + 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.Equal(t, TestAddrs[1], votes[1].Voter) - require.Equal(t, proposalID, votes[1].ProposalID) + require.Equal(t, addrs[1].String(), votes[1].Voter) + require.Equal(t, proposalID, votes[1].ProposalId) require.Equal(t, types.OptionNoWithVeto, votes[1].Option) } diff --git a/x/gov/legacy/v034/types.go b/x/gov/legacy/v034/types.go new file mode 100644 index 000000000000..dd98cca21be3 --- /dev/null +++ b/x/gov/legacy/v034/types.go @@ -0,0 +1,332 @@ +// DONTCOVER +// nolint +package v034 + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ ProposalContent = TextProposal{} +) + +const ( + ModuleName = "gov" + + StatusNil ProposalStatus = 0x00 + StatusDepositPeriod ProposalStatus = 0x01 + StatusVotingPeriod ProposalStatus = 0x02 + StatusPassed ProposalStatus = 0x03 + StatusRejected ProposalStatus = 0x04 + StatusFailed ProposalStatus = 0x05 + + OptionEmpty VoteOption = 0x00 + OptionYes VoteOption = 0x01 + OptionAbstain VoteOption = 0x02 + OptionNo VoteOption = 0x03 + OptionNoWithVeto VoteOption = 0x04 + + ProposalTypeNil ProposalKind = 0x00 + ProposalTypeText ProposalKind = 0x01 + ProposalTypeParameterChange ProposalKind = 0x02 +) + +type ( + ProposalQueue []uint64 + + ProposalKind byte + + VoteOption byte + ProposalStatus byte + + ProposalContent interface { + GetTitle() string + GetDescription() string + ProposalType() ProposalKind + } + + Proposals []Proposal + + TextProposal struct { + Title string `json:"title"` + Description string `json:"description"` + } + + Proposal struct { + ProposalContent `json:"proposal_content"` + + ProposalID uint64 `json:"proposal_id"` + + Status ProposalStatus `json:"proposal_status"` + FinalTallyResult TallyResult `json:"final_tally_result"` + + SubmitTime time.Time `json:"submit_time"` + DepositEndTime time.Time `json:"deposit_end_time"` + TotalDeposit sdk.Coins `json:"total_deposit"` + + VotingStartTime time.Time `json:"voting_start_time"` + VotingEndTime time.Time `json:"voting_end_time"` + } + + TallyParams struct { + Quorum sdk.Dec `json:"quorum,omitempty"` + Threshold sdk.Dec `json:"threshold,omitempty"` + Veto sdk.Dec `json:"veto,omitempty"` + } + + VotingParams struct { + VotingPeriod time.Duration `json:"voting_period,omitempty"` + } + + TallyResult struct { + Yes sdk.Int `json:"yes"` + Abstain sdk.Int `json:"abstain"` + No sdk.Int `json:"no"` + NoWithVeto sdk.Int `json:"no_with_veto"` + } + + Deposits []Deposit + + Vote struct { + ProposalID uint64 `json:"proposal_id"` + Voter sdk.AccAddress `json:"voter"` + Option VoteOption `json:"option"` + } + + Votes []Vote + + DepositParams struct { + MinDeposit sdk.Coins `json:"min_deposit,omitempty"` + MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty"` + } + + Deposit struct { + ProposalID uint64 `json:"proposal_id"` + Depositor sdk.AccAddress `json:"depositor"` + Amount sdk.Coins `json:"amount"` + } + + DepositWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Deposit Deposit `json:"deposit"` + } + + VoteWithMetadata struct { + ProposalID uint64 `json:"proposal_id"` + Vote Vote `json:"vote"` + } + + GenesisState struct { + StartingProposalID uint64 `json:"starting_proposal_id"` + Deposits []DepositWithMetadata `json:"deposits"` + Votes []VoteWithMetadata `json:"votes"` + Proposals []Proposal `json:"proposals"` + DepositParams DepositParams `json:"deposit_params"` + VotingParams VotingParams `json:"voting_params"` + TallyParams TallyParams `json:"tally_params"` + } +) + +func (tp TextProposal) GetTitle() string { return tp.Title } +func (tp TextProposal) GetDescription() string { return tp.Description } +func (tp TextProposal) ProposalType() ProposalKind { return ProposalTypeText } + +// ProposalStatusToString turns a string into a ProposalStatus +func ProposalStatusFromString(str string) (ProposalStatus, error) { + switch str { + case "DepositPeriod": + return StatusDepositPeriod, nil + + case "VotingPeriod": + return StatusVotingPeriod, nil + + case "Passed": + return StatusPassed, nil + + case "Rejected": + return StatusRejected, nil + + case "Failed": + return StatusFailed, nil + + case "": + return StatusNil, nil + + default: + return ProposalStatus(0xff), fmt.Errorf("'%s' is not a valid proposal status", str) + } +} + +func (status ProposalStatus) Marshal() ([]byte, error) { + return []byte{byte(status)}, nil +} + +func (status *ProposalStatus) Unmarshal(data []byte) error { + *status = ProposalStatus(data[0]) + return nil +} + +func (status ProposalStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(status.String()) +} + +func (status *ProposalStatus) UnmarshalJSON(data []byte) error { + var s string + err := json.Unmarshal(data, &s) + if err != nil { + return err + } + + bz2, err := ProposalStatusFromString(s) + if err != nil { + return err + } + + *status = bz2 + return nil +} + +func (status ProposalStatus) String() string { + switch status { + case StatusDepositPeriod: + return "DepositPeriod" + + case StatusVotingPeriod: + return "VotingPeriod" + + case StatusPassed: + return "Passed" + + case StatusRejected: + return "Rejected" + + case StatusFailed: + return "Failed" + + default: + return "" + } +} + +func VoteOptionFromString(str string) (VoteOption, error) { + switch str { + case "Yes": + return OptionYes, nil + + case "Abstain": + return OptionAbstain, nil + + case "No": + return OptionNo, nil + + case "NoWithVeto": + return OptionNoWithVeto, nil + + default: + return VoteOption(0xff), fmt.Errorf("'%s' is not a valid vote option", str) + } +} + +func (vo VoteOption) Marshal() ([]byte, error) { + return []byte{byte(vo)}, nil +} + +func (vo *VoteOption) Unmarshal(data []byte) error { + *vo = VoteOption(data[0]) + return nil +} + +func (vo VoteOption) MarshalJSON() ([]byte, error) { + return json.Marshal(vo.String()) +} + +func (vo *VoteOption) UnmarshalJSON(data []byte) error { + var s string + err := json.Unmarshal(data, &s) + if err != nil { + return err + } + + bz2, err := VoteOptionFromString(s) + if err != nil { + return err + } + + *vo = bz2 + return nil +} + +func (vo VoteOption) String() string { + switch vo { + case OptionYes: + return "Yes" + case OptionAbstain: + return "Abstain" + case OptionNo: + return "No" + case OptionNoWithVeto: + return "NoWithVeto" + default: + return "" + } +} + +func ProposalTypeFromString(str string) (ProposalKind, error) { + switch str { + case "Text": + return ProposalTypeText, nil + case "ParameterChange": + return ProposalTypeParameterChange, nil + default: + return ProposalKind(0xff), fmt.Errorf("'%s' is not a valid proposal type", str) + } +} + +func (pt ProposalKind) Marshal() ([]byte, error) { + return []byte{byte(pt)}, nil +} + +func (pt *ProposalKind) Unmarshal(data []byte) error { + *pt = ProposalKind(data[0]) + return nil +} + +func (pt ProposalKind) MarshalJSON() ([]byte, error) { + return json.Marshal(pt.String()) +} + +func (pt *ProposalKind) UnmarshalJSON(data []byte) error { + var s string + err := json.Unmarshal(data, &s) + if err != nil { + return err + } + + bz2, err := ProposalTypeFromString(s) + if err != nil { + return err + } + *pt = bz2 + return nil +} + +func (pt ProposalKind) String() string { + switch pt { + case ProposalTypeText: + return "Text" + case ProposalTypeParameterChange: + return "ParameterChange" + default: + return "" + } +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*ProposalContent)(nil), nil) + cdc.RegisterConcrete(TextProposal{}, "gov/TextProposal", nil) +} diff --git a/x/gov/legacy/v036/migrate.go b/x/gov/legacy/v036/migrate.go new file mode 100644 index 000000000000..d20df1d9c755 --- /dev/null +++ b/x/gov/legacy/v036/migrate.go @@ -0,0 +1,49 @@ +package v036 + +import ( + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. This migration flattens the deposits and votes and updates the +// proposal content to the new +func Migrate(oldGenState v034gov.GenesisState) GenesisState { + deposits := make(v034gov.Deposits, len(oldGenState.Deposits)) + for i, deposit := range oldGenState.Deposits { + deposits[i] = deposit.Deposit + } + + votes := make(v034gov.Votes, len(oldGenState.Votes)) + for i, vote := range oldGenState.Votes { + votes[i] = vote.Vote + } + + proposals := make([]Proposal, len(oldGenState.Proposals)) + for i, proposal := range oldGenState.Proposals { + proposals[i] = Proposal{ + Content: migrateContent(proposal.ProposalContent), + ProposalID: proposal.ProposalID, + Status: proposal.Status, + FinalTallyResult: proposal.FinalTallyResult, + SubmitTime: proposal.SubmitTime, + DepositEndTime: proposal.DepositEndTime, + TotalDeposit: proposal.TotalDeposit, + VotingStartTime: proposal.VotingStartTime, + VotingEndTime: proposal.VotingEndTime, + } + } + + return NewGenesisState( + oldGenState.StartingProposalID, deposits, votes, proposals, + oldGenState.DepositParams, oldGenState.VotingParams, oldGenState.TallyParams, + ) +} + +func migrateContent(proposalContent v034gov.ProposalContent) (content Content) { + switch proposalContent.ProposalType() { + case v034gov.ProposalTypeText: + return NewTextProposal(proposalContent.GetTitle(), proposalContent.GetDescription()) + default: + return nil + } +} diff --git a/x/gov/legacy/v036/types.go b/x/gov/legacy/v036/types.go new file mode 100644 index 000000000000..d0bc4620bc73 --- /dev/null +++ b/x/gov/legacy/v036/types.go @@ -0,0 +1,133 @@ +// DONTCOVER +// nolint +package v036 + +import ( + "fmt" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" +) + +const ( + ModuleName = "gov" + RouterKey = ModuleName + + ProposalTypeText string = "Text" + + MaxDescriptionLength int = 5000 + MaxTitleLength int = 140 +) + +var ( + _ Content = TextProposal{} +) + +type ( + Proposals []Proposal + ProposalQueue []uint64 + + TextProposal struct { + Title string `json:"title"` + Description string `json:"description"` + } + + Content interface { + GetTitle() string + GetDescription() string + ProposalRoute() string + ProposalType() string + ValidateBasic() error + String() string + } + + Proposal struct { + Content `json:"content"` + + ProposalID uint64 `json:"id"` + Status v034gov.ProposalStatus `json:"proposal_status"` + FinalTallyResult v034gov.TallyResult `json:"final_tally_result"` + + SubmitTime time.Time `json:"submit_time"` + DepositEndTime time.Time `json:"deposit_end_time"` + TotalDeposit sdk.Coins `json:"total_deposit"` + + VotingStartTime time.Time `json:"voting_start_time"` + VotingEndTime time.Time `json:"voting_end_time"` + } + + GenesisState struct { + StartingProposalID uint64 `json:"starting_proposal_id"` + Deposits v034gov.Deposits `json:"deposits"` + Votes v034gov.Votes `json:"votes"` + Proposals []Proposal `json:"proposals"` + DepositParams v034gov.DepositParams `json:"deposit_params"` + VotingParams v034gov.VotingParams `json:"voting_params"` + TallyParams v034gov.TallyParams `json:"tally_params"` + } +) + +func NewGenesisState( + startingProposalID uint64, deposits v034gov.Deposits, votes v034gov.Votes, proposals []Proposal, + depositParams v034gov.DepositParams, votingParams v034gov.VotingParams, tallyParams v034gov.TallyParams, +) GenesisState { + + return GenesisState{ + StartingProposalID: startingProposalID, + Deposits: deposits, + Votes: votes, + Proposals: proposals, + DepositParams: depositParams, + VotingParams: votingParams, + TallyParams: tallyParams, + } +} + +func NewTextProposal(title, description string) Content { + return TextProposal{title, description} +} + +func (tp TextProposal) GetTitle() string { return tp.Title } +func (tp TextProposal) GetDescription() string { return tp.Description } +func (tp TextProposal) ProposalRoute() string { return RouterKey } +func (tp TextProposal) ProposalType() string { return ProposalTypeText } +func (tp TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } + +func (tp TextProposal) String() string { + return fmt.Sprintf(`Text Proposal: + Title: %s + Description: %s +`, tp.Title, tp.Description) +} + +func ErrInvalidProposalContent(msg string) error { + return fmt.Errorf("invalid proposal content: %s", msg) +} + +func ValidateAbstract(c Content) error { + title := c.GetTitle() + if len(strings.TrimSpace(title)) == 0 { + return ErrInvalidProposalContent("proposal title cannot be blank") + } + if len(title) > MaxTitleLength { + return ErrInvalidProposalContent(fmt.Sprintf("proposal title is longer than max length of %d", MaxTitleLength)) + } + + description := c.GetDescription() + if len(description) == 0 { + return ErrInvalidProposalContent("proposal description cannot be blank") + } + if len(description) > MaxDescriptionLength { + return ErrInvalidProposalContent(fmt.Sprintf("proposal description is longer than max length of %d", MaxDescriptionLength)) + } + + return nil +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterInterface((*Content)(nil), nil) + cdc.RegisterConcrete(TextProposal{}, "cosmos-sdk/TextProposal", nil) +} diff --git a/x/gov/legacy/v040/migrate.go b/x/gov/legacy/v040/migrate.go new file mode 100644 index 000000000000..2accb74284cd --- /dev/null +++ b/x/gov/legacy/v040/migrate.go @@ -0,0 +1,209 @@ +package v040 + +import ( + "fmt" + + proto "github.com/gogo/protobuf/proto" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v040distr "github.com/cosmos/cosmos-sdk/x/distribution/types" + v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/types" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v040params "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" + v040upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func migrateVoteOption(oldVoteOption v034gov.VoteOption) v040gov.VoteOption { + switch oldVoteOption { + case v034gov.OptionEmpty: + return v040gov.OptionEmpty + + case v034gov.OptionYes: + return v040gov.OptionYes + + case v034gov.OptionAbstain: + return v040gov.OptionAbstain + + case v034gov.OptionNo: + return v040gov.OptionNo + + case v034gov.OptionNoWithVeto: + return v040gov.OptionNoWithVeto + + default: + panic(fmt.Errorf("'%s' is not a valid vote option", oldVoteOption)) + } +} + +func migrateProposalStatus(oldProposalStatus v034gov.ProposalStatus) v040gov.ProposalStatus { + switch oldProposalStatus { + + case v034gov.StatusNil: + return v040gov.StatusNil + + case v034gov.StatusDepositPeriod: + return v040gov.StatusDepositPeriod + + case v034gov.StatusVotingPeriod: + return v040gov.StatusVotingPeriod + + case v034gov.StatusPassed: + return v040gov.StatusPassed + + case v034gov.StatusRejected: + return v040gov.StatusRejected + + case v034gov.StatusFailed: + return v040gov.StatusFailed + + default: + panic(fmt.Errorf("'%s' is not a valid proposal status", oldProposalStatus)) + } +} + +func migrateContent(oldContent v036gov.Content) *codectypes.Any { + var protoProposal proto.Message + + switch oldContent := oldContent.(type) { + case v036gov.TextProposal: + { + protoProposal = &v040gov.TextProposal{ + Title: oldContent.Title, + Description: oldContent.Description, + } + // Convert the content into Any. + contentAny, err := codectypes.NewAnyWithValue(protoProposal) + if err != nil { + panic(err) + } + + return contentAny + } + case v036distr.CommunityPoolSpendProposal: + { + protoProposal = &v040distr.CommunityPoolSpendProposal{ + Title: oldContent.Title, + Description: oldContent.Description, + Recipient: oldContent.Recipient.String(), + Amount: oldContent.Amount, + } + } + case v038upgrade.CancelSoftwareUpgradeProposal: + { + protoProposal = &v040upgrade.CancelSoftwareUpgradeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + } + } + case v038upgrade.SoftwareUpgradeProposal: + { + protoProposal = &v040upgrade.SoftwareUpgradeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + Plan: v040upgrade.Plan{ + Name: oldContent.Plan.Name, + Time: oldContent.Plan.Time, + Height: oldContent.Plan.Height, + Info: oldContent.Plan.Info, + }, + } + } + case v036params.ParameterChangeProposal: + { + newChanges := make([]v040params.ParamChange, len(oldContent.Changes)) + for i, oldChange := range oldContent.Changes { + newChanges[i] = v040params.ParamChange{ + Subspace: oldChange.Subspace, + Key: oldChange.Key, + Value: oldChange.Value, + } + } + + protoProposal = &v040params.ParameterChangeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + Changes: newChanges, + } + } + default: + panic(fmt.Errorf("%T is not a valid proposal content type", oldContent)) + } + + // Convert the content into Any. + contentAny, err := codectypes.NewAnyWithValue(protoProposal) + if err != nil { + panic(err) + } + + return contentAny +} + +// Migrate accepts exported v0.36 x/gov genesis state and migrates it to +// v0.40 x/gov genesis state. The migration includes: +// +// - Convert vote option & proposal status from byte to enum. +// - Migrate proposal content to Any. +// - Convert addresses from bytes to bech32 strings. +// - Re-encode in v0.40 GenesisState. +func Migrate(oldGovState v036gov.GenesisState) *v040gov.GenesisState { + newDeposits := make([]v040gov.Deposit, len(oldGovState.Deposits)) + for i, oldDeposit := range oldGovState.Deposits { + newDeposits[i] = v040gov.Deposit{ + ProposalId: oldDeposit.ProposalID, + Depositor: oldDeposit.Depositor.String(), + Amount: oldDeposit.Amount, + } + } + + newVotes := make([]v040gov.Vote, len(oldGovState.Votes)) + for i, oldVote := range oldGovState.Votes { + newVotes[i] = v040gov.Vote{ + ProposalId: oldVote.ProposalID, + Voter: oldVote.Voter.String(), + Option: migrateVoteOption(oldVote.Option), + } + } + + newProposals := make([]v040gov.Proposal, len(oldGovState.Proposals)) + for i, oldProposal := range oldGovState.Proposals { + newProposals[i] = v040gov.Proposal{ + ProposalId: oldProposal.ProposalID, + Content: migrateContent(oldProposal.Content), + Status: migrateProposalStatus(oldProposal.Status), + FinalTallyResult: v040gov.TallyResult{ + Yes: oldProposal.FinalTallyResult.Yes, + Abstain: oldProposal.FinalTallyResult.Abstain, + No: oldProposal.FinalTallyResult.No, + NoWithVeto: oldProposal.FinalTallyResult.NoWithVeto, + }, + SubmitTime: oldProposal.SubmitTime, + DepositEndTime: oldProposal.DepositEndTime, + TotalDeposit: oldProposal.TotalDeposit, + VotingStartTime: oldProposal.VotingStartTime, + VotingEndTime: oldProposal.VotingEndTime, + } + } + + return &v040gov.GenesisState{ + StartingProposalId: oldGovState.StartingProposalID, + Deposits: newDeposits, + Votes: newVotes, + Proposals: newProposals, + DepositParams: v040gov.DepositParams{ + MinDeposit: oldGovState.DepositParams.MinDeposit, + MaxDepositPeriod: oldGovState.DepositParams.MaxDepositPeriod, + }, + VotingParams: v040gov.VotingParams{ + VotingPeriod: oldGovState.VotingParams.VotingPeriod, + }, + TallyParams: v040gov.TallyParams{ + Quorum: oldGovState.TallyParams.Quorum, + Threshold: oldGovState.TallyParams.Threshold, + VetoThreshold: oldGovState.TallyParams.Veto, + }, + } +} diff --git a/x/gov/legacy/v040/migrate_test.go b/x/gov/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..049f5b885387 --- /dev/null +++ b/x/gov/legacy/v040/migrate_test.go @@ -0,0 +1,239 @@ +package v040_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + recipient, err := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + require.NoError(t, err) + govGenState := v036gov.GenesisState{ + Proposals: []v036gov.Proposal{ + { + Content: v036gov.TextProposal{ + Title: "foo_text", + Description: "bar_text", + }, + }, + { + Content: v036distr.CommunityPoolSpendProposal{ + Title: "foo_community", + Description: "bar_community", + Recipient: recipient, + Amount: sdk.NewCoins(sdk.NewCoin("footoken", sdk.NewInt(2))), + }, + }, + { + Content: v038upgrade.CancelSoftwareUpgradeProposal{ + Title: "foo_cancel_upgrade", + Description: "bar_cancel_upgrade", + }, + }, + { + Content: v038upgrade.SoftwareUpgradeProposal{ + Title: "foo_software_upgrade", + Description: "bar_software_upgrade", + Plan: v038upgrade.Plan{ + Name: "foo_upgrade_name", + Height: 123, + Info: "foo_upgrade_info", + }, + }, + }, + { + Content: v036params.ParameterChangeProposal{ + Title: "foo_param_change", + Description: "bar_param_change", + Changes: []v036params.ParamChange{ + { + Subspace: "foo_param_change_subspace", + Key: "foo_param_change_key", + Subkey: "foo_param_change_subkey", + Value: "foo_param_change_value", + }, + }, + }, + }, + }, + } + + migrated := v040gov.Migrate(govGenState) + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + require.NoError(t, err) + indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") + require.NoError(t, err) + + // Make sure about: + // - TextProposal has correct JSON. + // - CommunityPoolSpendProposal has correct JSON. + // - CancelSoftwareUpgradeProposal has correct JSON. + // - SoftwareUpgradeProposal has correct JSON. + // - ParameterChangeProposal has correct JSON. + expected := `{ + "deposit_params": { + "max_deposit_period": "0s", + "min_deposit": [] + }, + "deposits": [], + "proposals": [ + { + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "description": "bar_text", + "title": "foo_text" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.distribution.v1beta1.CommunityPoolSpendProposal", + "amount": [ + { + "amount": "2", + "denom": "footoken" + } + ], + "description": "bar_community", + "recipient": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "title": "foo_community" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal", + "description": "bar_cancel_upgrade", + "title": "foo_cancel_upgrade" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", + "description": "bar_software_upgrade", + "plan": { + "height": "123", + "info": "foo_upgrade_info", + "name": "foo_upgrade_name", + "time": "0001-01-01T00:00:00Z", + "upgraded_client_state": null + }, + "title": "foo_software_upgrade" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.params.v1beta1.ParameterChangeProposal", + "changes": [ + { + "key": "foo_param_change_key", + "subspace": "foo_param_change_subspace", + "value": "foo_param_change_value" + } + ], + "description": "bar_param_change", + "title": "foo_param_change" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + } + ], + "starting_proposal_id": "0", + "tally_params": { + "quorum": "0", + "threshold": "0", + "veto_threshold": "0" + }, + "votes": [], + "voting_params": { + "voting_period": "0s" + } +}` + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/gov/legacy/v040/types.go b/x/gov/legacy/v040/types.go new file mode 100644 index 000000000000..27f668b2e1aa --- /dev/null +++ b/x/gov/legacy/v040/types.go @@ -0,0 +1,6 @@ +package v040 + +// Default parameter values +const ( + ModuleName = "gov" +) diff --git a/x/gov/legacy/v0_34/types.go b/x/gov/legacy/v0_34/types.go deleted file mode 100644 index 23e5eebc5e47..000000000000 --- a/x/gov/legacy/v0_34/types.go +++ /dev/null @@ -1,332 +0,0 @@ -// DONTCOVER -// nolint -package v0_34 - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var ( - _ ProposalContent = TextProposal{} -) - -const ( - ModuleName = "gov" - - StatusNil ProposalStatus = 0x00 - StatusDepositPeriod ProposalStatus = 0x01 - StatusVotingPeriod ProposalStatus = 0x02 - StatusPassed ProposalStatus = 0x03 - StatusRejected ProposalStatus = 0x04 - StatusFailed ProposalStatus = 0x05 - - OptionEmpty VoteOption = 0x00 - OptionYes VoteOption = 0x01 - OptionAbstain VoteOption = 0x02 - OptionNo VoteOption = 0x03 - OptionNoWithVeto VoteOption = 0x04 - - ProposalTypeNil ProposalKind = 0x00 - ProposalTypeText ProposalKind = 0x01 - ProposalTypeParameterChange ProposalKind = 0x02 -) - -type ( - ProposalQueue []uint64 - - ProposalKind byte - - VoteOption byte - ProposalStatus byte - - ProposalContent interface { - GetTitle() string - GetDescription() string - ProposalType() ProposalKind - } - - Proposals []Proposal - - TextProposal struct { - Title string `json:"title"` - Description string `json:"description"` - } - - Proposal struct { - ProposalContent `json:"proposal_content"` - - ProposalID uint64 `json:"proposal_id"` - - Status ProposalStatus `json:"proposal_status"` - FinalTallyResult TallyResult `json:"final_tally_result"` - - SubmitTime time.Time `json:"submit_time"` - DepositEndTime time.Time `json:"deposit_end_time"` - TotalDeposit sdk.Coins `json:"total_deposit"` - - VotingStartTime time.Time `json:"voting_start_time"` - VotingEndTime time.Time `json:"voting_end_time"` - } - - TallyParams struct { - Quorum sdk.Dec `json:"quorum,omitempty"` - Threshold sdk.Dec `json:"threshold,omitempty"` - Veto sdk.Dec `json:"veto,omitempty"` - } - - VotingParams struct { - VotingPeriod time.Duration `json:"voting_period,omitempty"` - } - - TallyResult struct { - Yes sdk.Int `json:"yes"` - Abstain sdk.Int `json:"abstain"` - No sdk.Int `json:"no"` - NoWithVeto sdk.Int `json:"no_with_veto"` - } - - Deposits []Deposit - - Vote struct { - ProposalID uint64 `json:"proposal_id"` - Voter sdk.AccAddress `json:"voter"` - Option VoteOption `json:"option"` - } - - Votes []Vote - - DepositParams struct { - MinDeposit sdk.Coins `json:"min_deposit,omitempty"` - MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty"` - } - - Deposit struct { - ProposalID uint64 `json:"proposal_id"` - Depositor sdk.AccAddress `json:"depositor"` - Amount sdk.Coins `json:"amount"` - } - - DepositWithMetadata struct { - ProposalID uint64 `json:"proposal_id"` - Deposit Deposit `json:"deposit"` - } - - VoteWithMetadata struct { - ProposalID uint64 `json:"proposal_id"` - Vote Vote `json:"vote"` - } - - GenesisState struct { - StartingProposalID uint64 `json:"starting_proposal_id"` - Deposits []DepositWithMetadata `json:"deposits"` - Votes []VoteWithMetadata `json:"votes"` - Proposals []Proposal `json:"proposals"` - DepositParams DepositParams `json:"deposit_params"` - VotingParams VotingParams `json:"voting_params"` - TallyParams TallyParams `json:"tally_params"` - } -) - -func (tp TextProposal) GetTitle() string { return tp.Title } -func (tp TextProposal) GetDescription() string { return tp.Description } -func (tp TextProposal) ProposalType() ProposalKind { return ProposalTypeText } - -// ProposalStatusToString turns a string into a ProposalStatus -func ProposalStatusFromString(str string) (ProposalStatus, error) { - switch str { - case "DepositPeriod": - return StatusDepositPeriod, nil - - case "VotingPeriod": - return StatusVotingPeriod, nil - - case "Passed": - return StatusPassed, nil - - case "Rejected": - return StatusRejected, nil - - case "Failed": - return StatusFailed, nil - - case "": - return StatusNil, nil - - default: - return ProposalStatus(0xff), fmt.Errorf("'%s' is not a valid proposal status", str) - } -} - -func (status ProposalStatus) Marshal() ([]byte, error) { - return []byte{byte(status)}, nil -} - -func (status *ProposalStatus) Unmarshal(data []byte) error { - *status = ProposalStatus(data[0]) - return nil -} - -func (status ProposalStatus) MarshalJSON() ([]byte, error) { - return json.Marshal(status.String()) -} - -func (status *ProposalStatus) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - bz2, err := ProposalStatusFromString(s) - if err != nil { - return err - } - - *status = bz2 - return nil -} - -func (status ProposalStatus) String() string { - switch status { - case StatusDepositPeriod: - return "DepositPeriod" - - case StatusVotingPeriod: - return "VotingPeriod" - - case StatusPassed: - return "Passed" - - case StatusRejected: - return "Rejected" - - case StatusFailed: - return "Failed" - - default: - return "" - } -} - -func VoteOptionFromString(str string) (VoteOption, error) { - switch str { - case "Yes": - return OptionYes, nil - - case "Abstain": - return OptionAbstain, nil - - case "No": - return OptionNo, nil - - case "NoWithVeto": - return OptionNoWithVeto, nil - - default: - return VoteOption(0xff), fmt.Errorf("'%s' is not a valid vote option", str) - } -} - -func (vo VoteOption) Marshal() ([]byte, error) { - return []byte{byte(vo)}, nil -} - -func (vo *VoteOption) Unmarshal(data []byte) error { - *vo = VoteOption(data[0]) - return nil -} - -func (vo VoteOption) MarshalJSON() ([]byte, error) { - return json.Marshal(vo.String()) -} - -func (vo *VoteOption) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - bz2, err := VoteOptionFromString(s) - if err != nil { - return err - } - - *vo = bz2 - return nil -} - -func (vo VoteOption) String() string { - switch vo { - case OptionYes: - return "Yes" - case OptionAbstain: - return "Abstain" - case OptionNo: - return "No" - case OptionNoWithVeto: - return "NoWithVeto" - default: - return "" - } -} - -func ProposalTypeFromString(str string) (ProposalKind, error) { - switch str { - case "Text": - return ProposalTypeText, nil - case "ParameterChange": - return ProposalTypeParameterChange, nil - default: - return ProposalKind(0xff), fmt.Errorf("'%s' is not a valid proposal type", str) - } -} - -func (pt ProposalKind) Marshal() ([]byte, error) { - return []byte{byte(pt)}, nil -} - -func (pt *ProposalKind) Unmarshal(data []byte) error { - *pt = ProposalKind(data[0]) - return nil -} - -func (pt ProposalKind) MarshalJSON() ([]byte, error) { - return json.Marshal(pt.String()) -} - -func (pt *ProposalKind) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - bz2, err := ProposalTypeFromString(s) - if err != nil { - return err - } - *pt = bz2 - return nil -} - -func (pt ProposalKind) String() string { - switch pt { - case ProposalTypeText: - return "Text" - case ProposalTypeParameterChange: - return "ParameterChange" - default: - return "" - } -} - -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*ProposalContent)(nil), nil) - cdc.RegisterConcrete(TextProposal{}, "gov/TextProposal", nil) -} diff --git a/x/gov/legacy/v0_36/migrate.go b/x/gov/legacy/v0_36/migrate.go deleted file mode 100644 index 6b55a53ea462..000000000000 --- a/x/gov/legacy/v0_36/migrate.go +++ /dev/null @@ -1,49 +0,0 @@ -package v0_36 - -import ( - v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_34" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 -// genesis state. This migration flattens the deposits and votes and updates the -// proposal content to the new -func Migrate(oldGenState v034gov.GenesisState) GenesisState { - deposits := make(v034gov.Deposits, len(oldGenState.Deposits)) - for i, deposit := range oldGenState.Deposits { - deposits[i] = deposit.Deposit - } - - votes := make(v034gov.Votes, len(oldGenState.Votes)) - for i, vote := range oldGenState.Votes { - votes[i] = vote.Vote - } - - proposals := make([]Proposal, len(oldGenState.Proposals)) - for i, proposal := range oldGenState.Proposals { - proposals[i] = Proposal{ - Content: migrateContent(proposal.ProposalContent), - ProposalID: proposal.ProposalID, - Status: proposal.Status, - FinalTallyResult: proposal.FinalTallyResult, - SubmitTime: proposal.SubmitTime, - DepositEndTime: proposal.DepositEndTime, - TotalDeposit: proposal.TotalDeposit, - VotingStartTime: proposal.VotingStartTime, - VotingEndTime: proposal.VotingEndTime, - } - } - - return NewGenesisState( - oldGenState.StartingProposalID, deposits, votes, proposals, - oldGenState.DepositParams, oldGenState.VotingParams, oldGenState.TallyParams, - ) -} - -func migrateContent(proposalContent v034gov.ProposalContent) (content Content) { - switch proposalContent.ProposalType() { - case v034gov.ProposalTypeText: - return NewTextProposal(proposalContent.GetTitle(), proposalContent.GetDescription()) - default: - return nil - } -} diff --git a/x/gov/legacy/v0_36/types.go b/x/gov/legacy/v0_36/types.go deleted file mode 100644 index faaaa4e32171..000000000000 --- a/x/gov/legacy/v0_36/types.go +++ /dev/null @@ -1,133 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - "fmt" - "strings" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v0_34" -) - -const ( - ModuleName = "gov" - RouterKey = ModuleName - - ProposalTypeText string = "Text" - - MaxDescriptionLength int = 5000 - MaxTitleLength int = 140 -) - -var ( - _ Content = TextProposal{} -) - -type ( - Proposals []Proposal - ProposalQueue []uint64 - - TextProposal struct { - Title string `json:"title"` - Description string `json:"description"` - } - - Content interface { - GetTitle() string - GetDescription() string - ProposalRoute() string - ProposalType() string - ValidateBasic() error - String() string - } - - Proposal struct { - Content `json:"content"` - - ProposalID uint64 `json:"id"` - Status v034gov.ProposalStatus `json:"proposal_status"` - FinalTallyResult v034gov.TallyResult `json:"final_tally_result"` - - SubmitTime time.Time `json:"submit_time"` - DepositEndTime time.Time `json:"deposit_end_time"` - TotalDeposit sdk.Coins `json:"total_deposit"` - - VotingStartTime time.Time `json:"voting_start_time"` - VotingEndTime time.Time `json:"voting_end_time"` - } - - GenesisState struct { - StartingProposalID uint64 `json:"starting_proposal_id"` - Deposits v034gov.Deposits `json:"deposits"` - Votes v034gov.Votes `json:"votes"` - Proposals []Proposal `json:"proposals"` - DepositParams v034gov.DepositParams `json:"deposit_params"` - VotingParams v034gov.VotingParams `json:"voting_params"` - TallyParams v034gov.TallyParams `json:"tally_params"` - } -) - -func NewGenesisState( - startingProposalID uint64, deposits v034gov.Deposits, votes v034gov.Votes, proposals []Proposal, - depositParams v034gov.DepositParams, votingParams v034gov.VotingParams, tallyParams v034gov.TallyParams, -) GenesisState { - - return GenesisState{ - StartingProposalID: startingProposalID, - Deposits: deposits, - Votes: votes, - Proposals: proposals, - DepositParams: depositParams, - VotingParams: votingParams, - TallyParams: tallyParams, - } -} - -func NewTextProposal(title, description string) Content { - return TextProposal{title, description} -} - -func (tp TextProposal) GetTitle() string { return tp.Title } -func (tp TextProposal) GetDescription() string { return tp.Description } -func (tp TextProposal) ProposalRoute() string { return RouterKey } -func (tp TextProposal) ProposalType() string { return ProposalTypeText } -func (tp TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } - -func (tp TextProposal) String() string { - return fmt.Sprintf(`Text Proposal: - Title: %s - Description: %s -`, tp.Title, tp.Description) -} - -func ErrInvalidProposalContent(msg string) error { - return fmt.Errorf("invalid proposal content: %s", msg) -} - -func ValidateAbstract(c Content) error { - title := c.GetTitle() - if len(strings.TrimSpace(title)) == 0 { - return ErrInvalidProposalContent("proposal title cannot be blank") - } - if len(title) > MaxTitleLength { - return ErrInvalidProposalContent(fmt.Sprintf("proposal title is longer than max length of %d", MaxTitleLength)) - } - - description := c.GetDescription() - if len(description) == 0 { - return ErrInvalidProposalContent("proposal description cannot be blank") - } - if len(description) > MaxDescriptionLength { - return ErrInvalidProposalContent(fmt.Sprintf("proposal description is longer than max length of %d", MaxDescriptionLength)) - } - - return nil -} - -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*Content)(nil), nil) - cdc.RegisterConcrete(TextProposal{}, "cosmos-sdk/TextProposal", nil) -} diff --git a/x/gov/module.go b/x/gov/module.go index 7ad5ecbc0ace..ad2191660c08 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -3,25 +3,30 @@ package gov // DONTCOVER import ( + "context" "encoding/json" "fmt" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/gov/client" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" "github.com/cosmos/cosmos-sdk/x/gov/client/cli" "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" - sim "github.com/cosmos/cosmos-sdk/x/simulation" ) var ( @@ -32,11 +37,12 @@ var ( // AppModuleBasic defines the basic application module used by the gov module. type AppModuleBasic struct { - proposalHandlers []client.ProposalHandler // proposal handlers which live in governance cli and rest + cdc codec.Marshaler + proposalHandlers []govclient.ProposalHandler // proposal handlers which live in governance cli and rest } // NewAppModuleBasic creates a new AppModuleBasic object -func NewAppModuleBasic(proposalHandlers ...client.ProposalHandler) AppModuleBasic { +func NewAppModuleBasic(proposalHandlers ...govclient.ProposalHandler) AppModuleBasic { return AppModuleBasic{ proposalHandlers: proposalHandlers, } @@ -47,51 +53,60 @@ func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterCodec registers the gov module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the gov module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the gov // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the gov module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return types.ValidateGenesis(&data) } // RegisterRESTRoutes registers the REST routes for the gov module. -func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { +func (a AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { proposalRESTHandlers := make([]rest.ProposalRESTHandler, 0, len(a.proposalHandlers)) for _, proposalHandler := range a.proposalHandlers { - proposalRESTHandlers = append(proposalRESTHandlers, proposalHandler.RESTHandler(ctx)) + proposalRESTHandlers = append(proposalRESTHandlers, proposalHandler.RESTHandler(clientCtx)) } - rest.RegisterRoutes(ctx, rtr, proposalRESTHandlers) + rest.RegisterHandlers(clientCtx, rtr, proposalRESTHandlers) } -// GetTxCmd returns the root tx command for the gov module. -func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the gov module. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} +// GetTxCmd returns the root tx command for the gov module. +func (a AppModuleBasic) GetTxCmd() *cobra.Command { proposalCLIHandlers := make([]*cobra.Command, 0, len(a.proposalHandlers)) for _, proposalHandler := range a.proposalHandlers { - proposalCLIHandlers = append(proposalCLIHandlers, proposalHandler.CLIHandler(cdc)) + proposalCLIHandlers = append(proposalCLIHandlers, proposalHandler.CLIHandler()) } - return cli.GetTxCmd(StoreKey, cdc, proposalCLIHandlers) + return cli.NewTxCmd(proposalCLIHandlers) } // GetQueryCmd returns the root query command for the gov module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(StoreKey, cdc) +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces implements InterfaceModule.RegisterInterfaces +func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } //____________________________________________________________________________ @@ -100,65 +115,66 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper accountKeeper types.AccountKeeper - supplyKeeper types.SupplyKeeper + bankKeeper types.BankKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper) AppModule { +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, - accountKeeper: accountKeeper, - supplyKeeper: supplyKeeper, + accountKeeper: ak, + bankKeeper: bk, } } // Name returns the gov module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers module invariants func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - RegisterInvariants(ir, am.keeper) + keeper.RegisterInvariants(ir, am.keeper, am.bankKeeper) } // Route returns the message routing key for the gov module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the gov module. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(am.keeper) +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) } // QuerierRoute returns the gov module's querier route name. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute +} + +// LegacyQuerierHandler returns no sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } -// NewQuerierHandler returns no sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) } // InitGenesis performs genesis initialization for the gov module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState) +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.accountKeeper, am.bankKeeper, am.keeper, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the gov // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) + return cdc.MustMarshalJSON(gs) } // BeginBlock performs a no-op. @@ -182,23 +198,24 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { // ProposalContents returns all the gov content functions used to // simulate governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return simulation.ProposalContents() } // RandomizedParams creates randomized gov param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for gov module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +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) []sim.WeightedOperation { +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { return simulation.WeightedOperations( simState.AppParams, simState.Cdc, - am.accountKeeper, am.keeper, simState.Contents) + am.accountKeeper, am.bankKeeper, am.keeper, simState.Contents, + ) } diff --git a/x/gov/module_test.go b/x/gov/module_test.go new file mode 100644 index 000000000000..4e54c0b79af3 --- /dev/null +++ b/x/gov/module_test.go @@ -0,0 +1,28 @@ +package gov_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.InitChain( + abcitypes.RequestInitChain{ + AppStateBytes: []byte("{}"), + ChainId: "test-chain-id", + }, + ) + + acc := app.AccountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName)) + require.NotNil(t, acc) +} diff --git a/x/gov/simulation/decoder.go b/x/gov/simulation/decoder.go index 6e4c3169ac1f..75cb4a5fccc8 100644 --- a/x/gov/simulation/decoder.go +++ b/x/gov/simulation/decoder.go @@ -5,41 +5,50 @@ import ( "encoding/binary" "fmt" - tmkv "github.com/tendermint/tendermint/libs/kv" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/gov/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding gov type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.ProposalsKeyPrefix): - var proposalA, proposalB types.Proposal - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &proposalA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &proposalB) - return fmt.Sprintf("%v\n%v", proposalA, proposalB) - - case bytes.Equal(kvA.Key[:1], types.ActiveProposalQueuePrefix), - bytes.Equal(kvA.Key[:1], types.InactiveProposalQueuePrefix), - bytes.Equal(kvA.Key[:1], types.ProposalIDKey): - proposalIDA := binary.LittleEndian.Uint64(kvA.Value) - proposalIDB := binary.LittleEndian.Uint64(kvB.Value) - return fmt.Sprintf("proposalIDA: %d\nProposalIDB: %d", proposalIDA, proposalIDB) - - case bytes.Equal(kvA.Key[:1], types.DepositsKeyPrefix): - var depositA, depositB types.Deposit - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &depositA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &depositB) - return fmt.Sprintf("%v\n%v", depositA, depositB) - - case bytes.Equal(kvA.Key[:1], types.VotesKeyPrefix): - var voteA, voteB types.Vote - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &voteA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &voteB) - return fmt.Sprintf("%v\n%v", voteA, voteB) - - default: - panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1])) +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding gov 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.ProposalsKeyPrefix): + var proposalA types.Proposal + err := cdc.UnmarshalBinaryBare(kvA.Value, &proposalA) + if err != nil { + panic(err) + } + var proposalB types.Proposal + err = cdc.UnmarshalBinaryBare(kvA.Value, &proposalB) + if err != nil { + panic(err) + } + return fmt.Sprintf("%v\n%v", proposalA, proposalB) + + case bytes.Equal(kvA.Key[:1], types.ActiveProposalQueuePrefix), + bytes.Equal(kvA.Key[:1], types.InactiveProposalQueuePrefix), + bytes.Equal(kvA.Key[:1], types.ProposalIDKey): + proposalIDA := binary.LittleEndian.Uint64(kvA.Value) + proposalIDB := binary.LittleEndian.Uint64(kvB.Value) + return fmt.Sprintf("proposalIDA: %d\nProposalIDB: %d", proposalIDA, proposalIDB) + + case bytes.Equal(kvA.Key[:1], types.DepositsKeyPrefix): + var depositA, depositB types.Deposit + cdc.MustUnmarshalBinaryBare(kvA.Value, &depositA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &depositB) + return fmt.Sprintf("%v\n%v", depositA, depositB) + + case bytes.Equal(kvA.Key[:1], types.VotesKeyPrefix): + var voteA, voteB types.Vote + cdc.MustUnmarshalBinaryBare(kvA.Value, &voteA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &voteB) + return fmt.Sprintf("%v\n%v", voteA, voteB) + + default: + panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1])) + } } } diff --git a/x/gov/simulation/decoder_test.go b/x/gov/simulation/decoder_test.go index 025dbeedc71c..7a5b0fc1bc9d 100644 --- a/x/gov/simulation/decoder_test.go +++ b/x/gov/simulation/decoder_test.go @@ -1,4 +1,4 @@ -package simulation +package simulation_test import ( "encoding/binary" @@ -8,11 +8,11 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" + "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/gov/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -21,32 +21,32 @@ var ( delAddr1 = sdk.AccAddress(delPk1.Address()) ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) - return -} - func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) endTime := time.Now().UTC() content := types.ContentFromProposalType("test", "test", types.ProposalTypeText) - proposal := types.NewProposal(content, 1, endTime, endTime.Add(24*time.Hour)) + proposal, err := types.NewProposal(content, 1, 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) - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.ProposalKey(1), Value: cdc.MustMarshalBinaryLengthPrefixed(proposal)}, - tmkv.Pair{Key: types.InactiveProposalQueueKey(1, endTime), Value: proposalIDBz}, - tmkv.Pair{Key: types.DepositKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)}, - tmkv.Pair{Key: types.VoteKey(1, delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(vote)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + proposalBz, err := cdc.MarshalBinaryBare(&proposal) + 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 { @@ -65,9 +65,9 @@ func TestDecodeStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/gov/simulation/genesis.go b/x/gov/simulation/genesis.go index f2541643bd36..3f9ed2d086a7 100644 --- a/x/gov/simulation/genesis.go +++ b/x/gov/simulation/genesis.go @@ -3,16 +3,15 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" "time" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/simulation" ) // Simulation parameter constants @@ -102,6 +101,10 @@ func RandomizedGenState(simState *module.SimulationState) { types.NewTallyParams(quorum, threshold, veto), ) - fmt.Printf("Selected randomly generated governance parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, govGenesis)) + bz, err := json.MarshalIndent(&govGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated governance parameters:\n%s\n", bz) simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(govGenesis) } diff --git a/x/gov/simulation/genesis_test.go b/x/gov/simulation/genesis_test.go new file mode 100644 index 000000000000..db8b98bff103 --- /dev/null +++ b/x/gov/simulation/genesis_test.go @@ -0,0 +1,84 @@ +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" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/gov/simulation" + "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 govGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &govGenesis) + + dec1, _ := sdk.NewDecFromStr("0.361000000000000000") + dec2, _ := sdk.NewDecFromStr("0.512000000000000000") + dec3, _ := sdk.NewDecFromStr("0.267000000000000000") + + require.Equal(t, "905stake", govGenesis.DepositParams.MinDeposit.String()) + require.Equal(t, "77h26m10s", govGenesis.DepositParams.MaxDepositPeriod.String()) + require.Equal(t, float64(148296), govGenesis.VotingParams.VotingPeriod.Seconds()) + require.Equal(t, dec1, govGenesis.TallyParams.Quorum) + require.Equal(t, dec2, govGenesis.TallyParams.Threshold) + require.Equal(t, dec3, govGenesis.TallyParams.VetoThreshold) + require.Equal(t, uint64(0x28), govGenesis.StartingProposalId) + require.Equal(t, types.Deposits{}, govGenesis.Deposits) + require.Equal(t, types.Votes{}, govGenesis.Votes) + require.Equal(t, types.Proposals{}, govGenesis.Proposals) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/gov/simulation/operations.go b/x/gov/simulation/operations.go index b53a97350146..2ea89ca97bb1 100644 --- a/x/gov/simulation/operations.go +++ b/x/gov/simulation/operations.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/helpers" 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/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/simulation" @@ -24,8 +25,10 @@ const ( ) // WeightedOperations returns all the operations from the module with their respective weights -func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, - k keeper.Keeper, wContents []simulation.WeightedProposalContent) simulation.WeightedOperations { +func WeightedOperations( + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, + bk types.BankKeeper, k keeper.Keeper, wContents []simtypes.WeightedProposalContent, +) simulation.WeightedOperations { var ( weightMsgDeposit int @@ -50,14 +53,14 @@ func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak typ for _, wContent := range wContents { wContent := wContent // pin variable var weight int - appParams.GetOrGenerate(cdc, wContent.AppParamsKey, &weight, nil, - func(_ *rand.Rand) { weight = wContent.DefaultWeight }) + appParams.GetOrGenerate(cdc, wContent.AppParamsKey(), &weight, nil, + func(_ *rand.Rand) { weight = wContent.DefaultWeight() }) wProposalOps = append( wProposalOps, simulation.NewWeightedOperation( weight, - SimulateSubmitProposal(ak, k, wContent.ContentSimulatorFn), + SimulateMsgSubmitProposal(ak, bk, k, wContent.ContentSimulatorFn()), ), ) } @@ -65,11 +68,11 @@ func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak typ wGovOps := simulation.WeightedOperations{ simulation.NewWeightedOperation( weightMsgDeposit, - SimulateMsgDeposit(ak, k), + SimulateMsgDeposit(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgVote, - SimulateMsgVote(ak, k), + SimulateMsgVote(ak, bk, k), ), } @@ -79,10 +82,9 @@ func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak typ // SimulateSubmitProposal simulates creating a msg Submit Proposal // voting on the proposal, and subsequently slashing the proposal. It is implemented using // future operations. -// nolint: funlen -func SimulateSubmitProposal( - ak types.AccountKeeper, k keeper.Keeper, contentSim simulation.ContentSimulatorFn, -) simulation.Operation { +func SimulateMsgSubmitProposal( + ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, contentSim simtypes.ContentSimulatorFn, +) simtypes.Operation { // The states are: // column 1: All validators vote // column 2: 90% vote @@ -106,38 +108,43 @@ func SimulateSubmitProposal( return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { // 1) submit proposal now content := contentSim(r, ctx, accs) if content == nil { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSubmitProposal, "content is nil"), nil, nil } - simAccount, _ := simulation.RandomAcc(r, accs) - deposit, skip, err := randomDeposit(r, ctx, ak, k, simAccount.Address) + simAccount, _ := simtypes.RandomAcc(r, accs) + deposit, skip, err := randomDeposit(r, ctx, ak, bk, k, simAccount.Address) switch { case skip: - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSubmitProposal, "skip deposit"), nil, nil case err != nil: - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSubmitProposal, "unable to generate deposit"), nil, err } - msg := types.NewMsgSubmitProposal(content, deposit, simAccount.Address) + msg, err := types.NewMsgSubmitProposal(content, deposit, simAccount.Address) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate a submit proposal msg"), nil, err + } account := ak.GetAccount(ctx, simAccount.Address) - coins := account.SpendableCoins(ctx.BlockTime()) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) var fees sdk.Coins - coins, hasNeg := coins.SafeSub(deposit) + coins, hasNeg := spendable.SafeSub(deposit) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err } } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -146,18 +153,21 @@ func SimulateSubmitProposal( []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - opMsg := simulation.NewOperationMsg(msg, true, "") + opMsg := simtypes.NewOperationMsg(msg, true, "") // get the submitted proposal ID proposalID, err := k.GetProposalID(ctx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate proposalID"), nil, err } // 2) Schedule operations for votes @@ -172,12 +182,12 @@ func SimulateSubmitProposal( whoVotes = whoVotes[:numVotes] votingPeriod := k.GetVotingParams(ctx).VotingPeriod - fops := make([]simulation.FutureOperation, numVotes+1) + fops := make([]simtypes.FutureOperation, numVotes+1) for i := 0; i < numVotes; i++ { whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) - fops[i] = simulation.FutureOperation{ + fops[i] = simtypes.FutureOperation{ BlockTime: whenVote, - Op: operationSimulateMsgVote(ak, k, accs[whoVotes[i]], int64(proposalID)), + Op: operationSimulateMsgVote(ak, bk, k, accs[whoVotes[i]], int64(proposalID)), } } @@ -186,41 +196,42 @@ func SimulateSubmitProposal( } // SimulateMsgDeposit generates a MsgDeposit with random values. -// nolint: funlen -func SimulateMsgDeposit(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgDeposit(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - simAccount, _ := simulation.RandomAcc(r, accs) + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) proposalID, ok := randomProposalID(r, k, ctx, types.StatusDepositPeriod) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDeposit, "unable to generate proposalID"), nil, nil } - deposit, skip, err := randomDeposit(r, ctx, ak, k, simAccount.Address) + deposit, skip, err := randomDeposit(r, ctx, ak, bk, k, simAccount.Address) switch { case skip: - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDeposit, "skip deposit"), nil, nil case err != nil: - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDeposit, "unable to generate deposit"), nil, err } msg := types.NewMsgDeposit(simAccount.Address, proposalID, deposit) account := ak.GetAccount(ctx, simAccount.Address) - coins := account.SpendableCoins(ctx.BlockTime()) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) var fees sdk.Coins - coins, hasNeg := coins.SafeSub(deposit) + coins, hasNeg := spendable.SafeSub(deposit) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err } } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -229,30 +240,31 @@ func SimulateMsgDeposit(ak types.AccountKeeper, k keeper.Keeper) simulation.Oper []uint64{account.GetSequence()}, simAccount.PrivKey, ) - - _, _, err = app.Deliver(tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + 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 simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgVote generates a MsgVote with random values. -// nolint: funlen -func SimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { - return operationSimulateMsgVote(ak, k, simulation.Account{}, -1) +func SimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { + return operationSimulateMsgVote(ak, bk, k, simtypes.Account{}, -1) } -func operationSimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper, - simAccount simulation.Account, proposalIDInt int64) simulation.Operation { +func operationSimulateMsgVote(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 []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - if simAccount.Equals(simulation.Account{}) { - simAccount, _ = simulation.RandomAcc(r, accs) + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + if simAccount.Equals(simtypes.Account{}) { + simAccount, _ = simtypes.RandomAcc(r, accs) } var proposalID uint64 @@ -262,23 +274,26 @@ func operationSimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper, var ok bool proposalID, ok = randomProposalID(r, k, ctx, types.StatusVotingPeriod) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgVote, "unable to generate proposalID"), nil, nil } default: proposalID = uint64(proposalIDInt) } option := randomVotingOption(r) - msg := types.NewMsgVote(simAccount.Address, proposalID, option) account := ak.GetAccount(ctx, simAccount.Address) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -287,13 +302,16 @@ func operationSimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper, []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } @@ -302,11 +320,12 @@ func operationSimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper, // This is to simulate multiple users depositing to get the // proposal above the minimum deposit amount func randomDeposit(r *rand.Rand, ctx sdk.Context, - ak types.AccountKeeper, k keeper.Keeper, addr sdk.AccAddress, + ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, addr sdk.AccAddress, ) (deposit sdk.Coins, skip bool, err error) { account := ak.GetAccount(ctx, addr) - coins := account.SpendableCoins(ctx.BlockHeader().Time) - if coins.Empty() { + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + if spendable.Empty() { return nil, true, nil // skip } @@ -314,7 +333,7 @@ func randomDeposit(r *rand.Rand, ctx sdk.Context, denomIndex := r.Intn(len(minDeposit)) denom := minDeposit[denomIndex].Denom - depositCoins := coins.AmountOf(denom) + depositCoins := spendable.AmountOf(denom) if depositCoins.IsZero() { return nil, true, nil } @@ -324,7 +343,7 @@ func randomDeposit(r *rand.Rand, ctx sdk.Context, maxAmt = minDeposit[denomIndex].Amount } - amount, err := simulation.RandPositiveInt(r, maxAmt) + amount, err := simtypes.RandPositiveInt(r, maxAmt) if err != nil { return nil, false, err } @@ -343,7 +362,7 @@ func randomProposalID(r *rand.Rand, k keeper.Keeper, switch { case proposalID > initialProposalID: // select a random ID between [initialProposalID, proposalID] - proposalID = uint64(simulation.RandIntBetween(r, int(initialProposalID), int(proposalID))) + proposalID = uint64(simtypes.RandIntBetween(r, int(initialProposalID), int(proposalID))) default: // This is called on the first call to this funcion diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go new file mode 100644 index 000000000000..9c6e8306f821 --- /dev/null +++ b/x/gov/simulation/operations_test.go @@ -0,0 +1,237 @@ +package simulation_test + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "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/gov/simulation" + "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type MockWeightedProposalContent struct { + n int +} + +func (m MockWeightedProposalContent) AppParamsKey() string { + return fmt.Sprintf("AppParamsKey-%d", m.n) +} + +func (m MockWeightedProposalContent) DefaultWeight() int { + return m.n +} + +func (m MockWeightedProposalContent) ContentSimulatorFn() simtypes.ContentSimulatorFn { + return func(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) simtypes.Content { + return types.NewTextProposal( + fmt.Sprintf("title-%d: %s", m.n, simtypes.RandStringOfLength(r, 100)), + fmt.Sprintf("description-%d: %s", m.n, simtypes.RandStringOfLength(r, 4000)), + ) + } +} + +// make sure the MockWeightedProposalContent satisfied the WeightedProposalContent interface +var _ simtypes.WeightedProposalContent = MockWeightedProposalContent{} + +func mockWeightedProposalContent(n int) []simtypes.WeightedProposalContent { + wpc := make([]simtypes.WeightedProposalContent, n) + for i := 0; i < n; i++ { + wpc[i] = MockWeightedProposalContent{i} + + } + return wpc + +} + +// TestWeightedOperations tests the weights of the operations. +func TestWeightedOperations(t *testing.T) { + app, ctx := createTestApp(false) + ctx.WithChainID("test-chain") + + cdc := app.AppCodec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations(appParams, cdc, app.AccountKeeper, + app.BankKeeper, app.GovKeeper, mockWeightedProposalContent(3), + ) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accs := getTestingAccounts(t, r, app, ctx, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{ + {0, types.ModuleName, "submit_proposal"}, + {1, types.ModuleName, "submit_proposal"}, + {2, types.ModuleName, "submit_proposal"}, + {simappparams.DefaultWeightMsgDeposit, types.ModuleName, types.TypeMsgDeposit}, + {simappparams.DefaultWeightMsgVote, types.ModuleName, types.TypeMsgVote}, + } + + 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(t, expected[i].weight, w.Weight(), "weight should be the same") + require.Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + require.Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +// TestSimulateMsgSubmitProposal tests the normal scenario of a valid message of type TypeMsgSubmitProposal. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgSubmitProposal(t *testing.T) { + app, ctx := createTestApp(false) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := getTestingAccounts(t, r, app, ctx, 3) + + // begin a new block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgSubmitProposal(app.AccountKeeper, app.BankKeeper, app.GovKeeper, MockWeightedProposalContent{3}.ContentSimulatorFn()) + operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgSubmitProposal + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Proposer) + require.Equal(t, "2686011stake", msg.InitialDeposit.String()) + require.Equal(t, "title-3: ZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHeAerqyNEUzXPFGkqEGqiQWIXnku", msg.GetContent().GetTitle()) + require.Equal(t, "description-3: NJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmTPXtpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeH", msg.GetContent().GetDescription()) + require.Equal(t, "gov", msg.Route()) + require.Equal(t, types.TypeMsgSubmitProposal, msg.Type()) +} + +// TestSimulateMsgDeposit tests the normal scenario of a valid message of type TypeMsgDeposit. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgDeposit(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.SetProposal(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.SimulateMsgDeposit(app.AccountKeeper, app.BankKeeper, app.GovKeeper) + operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgDeposit + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, uint64(1), msg.ProposalId) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor) + require.Equal(t, "560969stake", msg.Amount.String()) + require.Equal(t, "gov", msg.Route()) + require.Equal(t, types.TypeMsgDeposit, msg.Type()) +} + +// TestSimulateMsgVote tests the normal scenario of a valid message of type TypeMsgVote. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgVote(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.SimulateMsgVote(app.AccountKeeper, app.BankKeeper, app.GovKeeper) + operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgVote + 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.Equal(t, types.OptionYes, msg.Option) + require.Equal(t, "gov", msg.Route()) + require.Equal(t, types.TypeMsgVote, msg.Type()) + +} + +// returns context and an app with updated mint keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.MintKeeper.SetParams(ctx, minttypes.DefaultParams()) + app.MintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) + + return app, ctx +} + +func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk.Context, 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 { + acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) + app.AccountKeeper.SetAccount(ctx, acc) + err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) + require.NoError(t, err) + } + + return accounts +} diff --git a/x/gov/simulation/params.go b/x/gov/simulation/params.go index 420b5a8eec78..c0f0ac05aecf 100644 --- a/x/gov/simulation/params.go +++ b/x/gov/simulation/params.go @@ -8,6 +8,7 @@ import ( "math/rand" sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -23,8 +24,8 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keyVotingParams, func(r *rand.Rand) string { return fmt.Sprintf(`{"voting_period": "%d"}`, GenVotingParamsVotingPeriod(r)) @@ -47,7 +48,7 @@ func ParamChanges(r *rand.Rand) []simulation.ParamChange { } pc := make(map[string]string) - numChanges := simulation.RandIntBetween(r, 1, len(changes)) + numChanges := simtypes.RandIntBetween(r, 1, len(changes)) for i := 0; i < numChanges; i++ { c := changes[r.Intn(len(changes))] diff --git a/x/gov/simulation/params_test.go b/x/gov/simulation/params_test.go new file mode 100644 index 000000000000..de528d14d9d5 --- /dev/null +++ b/x/gov/simulation/params_test.go @@ -0,0 +1,37 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/gov/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"gov/votingparams", "votingparams", "{\"voting_period\": \"82639000000000\"}", "gov"}, + {"gov/depositparams", "depositparams", "{\"max_deposit_period\": \"47332000000000\"}", "gov"}, + {"gov/tallyparams", "tallyparams", "{\"threshold\":\"0.509000000000000000\"}", "gov"}, + } + + paramChanges := simulation.ParamChanges(r) + require.Len(t, paramChanges, 3) + + for i, p := range paramChanges { + + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/gov/simulation/proposals.go b/x/gov/simulation/proposals.go index b3d73d15c349..322774c984eb 100644 --- a/x/gov/simulation/proposals.go +++ b/x/gov/simulation/proposals.go @@ -5,6 +5,7 @@ import ( 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/gov/types" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -13,20 +14,20 @@ import ( const OpWeightSubmitTextProposal = "op_weight_submit_text_proposal" // ProposalContents defines the module weighted proposals' contents -func ProposalContents() []simulation.WeightedProposalContent { - return []simulation.WeightedProposalContent{ - { - AppParamsKey: OpWeightSubmitTextProposal, - DefaultWeight: simappparams.DefaultWeightTextProposal, - ContentSimulatorFn: SimulateTextProposalContent, - }, +func ProposalContents() []simtypes.WeightedProposalContent { + return []simtypes.WeightedProposalContent{ + simulation.NewWeightedProposalContent( + OpWeightMsgDeposit, + simappparams.DefaultWeightTextProposal, + SimulateTextProposalContent, + ), } } // SimulateTextProposalContent returns a random text proposal content. -func SimulateTextProposalContent(r *rand.Rand, _ sdk.Context, _ []simulation.Account) types.Content { +func SimulateTextProposalContent(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) simtypes.Content { return types.NewTextProposal( - simulation.RandStringOfLength(r, 140), - simulation.RandStringOfLength(r, 5000), + simtypes.RandStringOfLength(r, 140), + simtypes.RandStringOfLength(r, 5000), ) } diff --git a/x/gov/simulation/proposals_test.go b/x/gov/simulation/proposals_test.go new file mode 100644 index 000000000000..dcd6f3c3f08b --- /dev/null +++ b/x/gov/simulation/proposals_test.go @@ -0,0 +1,40 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + 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/gov/simulation" +) + +func TestProposalContents(t *testing.T) { + // initialize parameters + s := rand.NewSource(1) + r := rand.New(s) + + ctx := sdk.NewContext(nil, tmproto.Header{}, true, nil) + accounts := simtypes.RandomAccounts(r, 3) + + // execute ProposalContents function + weightedProposalContent := simulation.ProposalContents() + require.Len(t, weightedProposalContent, 1) + + w0 := weightedProposalContent[0] + + // tests w0 interface: + require.Equal(t, simulation.OpWeightMsgDeposit, w0.AppParamsKey()) + require.Equal(t, simappparams.DefaultWeightTextProposal, w0.DefaultWeight()) + + content := w0.ContentSimulatorFn()(r, ctx, accounts) + + require.Equal(t, "NxImpptHBIFDQfnxaTiOBJUgNzvqHbVcVJYlIFWFlzFqqRTTyFzDUMntPzyRamUFqeJAEaSHIuUHZoTWDjWXsYxYvwXwXZEsjRQKgKMselyUqWXMbHzRNDHnMzhWSirUgVggjiBxtWDfhzPDgrorEoNmDEiDdBldYegphCBTYWrmFFXNjxhtygsGBFHTejaKjMsqNdikEzDalEyWRHfJhKqifCKsedVuuJbQMbmRVuIPDluAWGpngjgBjOxuRFwSadayHNIhVVmNWBbfaTOldclxTTLUMvaBnLfwjHTtsKetEIvgrxLijhKJNablmvqpWIWsmhWQAYNLycREypoASHnyKWrxpoNLBJuyCGysZJgXbQAAmSIbGxMFXuwMVGZgBiZWfPWorAfjBeekCFvljHAtVZaTOsRxbPIioNxLTnWUTzGTvaNhplQQPmMADRRDuUIsiBpnGqPheKmLnopieVseFdTSAvOCacxaqFWFuXzsrVZzlGfeRpClwKuGEBujaPrzSLjVIOMvLlWxuznEOXlxbZroBRVEvEfBBAHOECribZNrYiFnzQqQmBnLksmFNAadusWAGltuqYNntgOlgOGwSdDjWdLboWyAWIcCfmpGJTfbljKPriLehwObuszICkaXNUkmeddeeRulbZBXJVLgteiKIfofGdNBregwUPlINQECatDSNXSIuefyMxxoKfcmjHEwbVtFiXtEnLJkLHUghmzFiymrgBChucZgOQUpGGVQEpRtIQjIBxYhtZPgUORdxXNWUMErWrUeriqYJPcgIDgLMWAyuuQnsHncCtjvHmvFbzYErxeunQllYDUVlXaRBveRUKeXwEGJFTSAqZtaBSDGDtzlADCnGjuTmYMJlapRsWfugmjwKEuoXJVpZvlcHeFvVvRRktRVGwzLfKezPEMABZtbLExQIjynSoahmkmoTHefdzFoBHMcQHFkKVHhpNtudPqJrYuQswzFuFHbSmpNltFnYJpvMrAYHFrNouZaanEUGHvbHIUUFTCtZrcpRHwgjblxlDNJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmTPXtpHRGdIbuucfTjOygZsTxPjf", content.GetDescription()) + require.Equal(t, "XhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeHVIkPZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHeAerqyNEUzXPFGkqEGqiQWIXnku", content.GetTitle()) + require.Equal(t, "gov", content.ProposalRoute()) + require.Equal(t, "Text", content.ProposalType()) +} diff --git a/x/gov/spec/01_concepts.md b/x/gov/spec/01_concepts.md index deed64b36c95..0acbfa3b709b 100644 --- a/x/gov/spec/01_concepts.md +++ b/x/gov/spec/01_concepts.md @@ -4,41 +4,42 @@ order: 1 # Concepts -*Disclaimer: This is work in progress. Mechanisms are susceptible to change.* +_Disclaimer: This is work in progress. Mechanisms are susceptible to change._ The governance process is divided in a few steps that are outlined below: -* **Proposal submission:** Proposal is submitted to the blockchain with a +- **Proposal submission:** Proposal is submitted to the blockchain with a deposit. -* **Vote:** Once deposit reaches a certain value (`MinDeposit`), proposal is - confirmed and vote opens. Bonded Atom holders can then send `TxGovVote` +- **Vote:** Once deposit reaches a certain value (`MinDeposit`), proposal is + confirmed and vote opens. Bonded Atom holders can then send `TxGovVote` transactions to vote on the proposal. -* If the proposal involves a software upgrade: - * **Signal:** Validators start signaling that they are ready to switch to the +- If the proposal involves a software upgrade: + - **Signal:** Validators start signaling that they are ready to switch to the new version. - * **Switch:** Once more than 75% of validators have signaled that they are + - **Switch:** Once more than 75% of validators have signaled that they are ready to switch, their software automatically flips to the new version. ## Proposal submission ### Right to submit a proposal -Any Atom holder, whether bonded or unbonded, can submit proposals by sending a -`TxGovProposal` transaction. Once a proposal is submitted, it is identified by +Any Atom holder, whether bonded or unbonded, can submit proposals by sending a +`TxGovProposal` transaction. Once a proposal is submitted, it is identified by its unique `proposalID`. ### Proposal types -In the initial version of the governance module, there are two types of +In the initial version of the governance module, there are two types of proposal: -* `PlainTextProposal` 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 + +- `PlainTextProposal` 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`. -* `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 +- `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 `SoftwareUpgradeProposals`. Other modules may expand upon the governance module by implementing their own @@ -59,85 +60,84 @@ Once the proposal's deposit reaches `MinDeposit`, it enters voting period. If pr When a the a proposal finalized, the coins from the deposit are either refunded or burned, according to the final tally of the proposal: -* If the proposal is approved or if it's rejected but _not_ vetoed, deposits will automatically be refunded to their respective depositor (transferred from the governance `ModuleAccount`). -* When the proposal is vetoed with a supermajority, deposits be burned from the governance `ModuleAccount`. +- If the proposal is approved or if it's rejected but _not_ vetoed, deposits will automatically be refunded to their respective depositor (transferred from the governance `ModuleAccount`). +- When the proposal is vetoed with a supermajority, deposits be burned from the governance `ModuleAccount`. ## Vote ### Participants -*Participants* are users that have the right to vote on proposals. On the -Cosmos Hub, participants are bonded Atom holders. Unbonded Atom holders and -other users do not get the right to participate in governance. However, they +_Participants_ are users that have the right to vote on proposals. On the +Cosmos Hub, participants are bonded Atom holders. Unbonded Atom holders and +other users do not get the right to participate in governance. However, they can submit and deposit on proposals. -Note that some *participants* can be forbidden to vote on a proposal under a +Note that some _participants_ can be forbidden to vote on a proposal under a certain validator if: -* *participant* bonded or unbonded Atoms to said validator after proposal + +- _participant_ bonded or unbonded Atoms to said validator after proposal entered voting period. -* *participant* became validator after proposal entered voting period. +- _participant_ became validator after proposal entered voting period. -This does not prevent *participant* to vote with Atoms bonded to other -validators. For example, if a *participant* bonded some Atoms to validator A -before a proposal entered voting period and other Atoms to validator B after -proposal entered voting period, only the vote under validator B will be +This does not prevent _participant_ to vote with Atoms bonded to other +validators. For example, if a _participant_ bonded some Atoms to validator A +before a proposal entered voting period and other Atoms to validator B after +proposal entered voting period, only the vote under validator B will be forbidden. ### Voting period Once a proposal reaches `MinDeposit`, it immediately enters `Voting period`. We define `Voting period` as the interval between the moment the vote opens and -the moment the vote closes. `Voting period` should always be shorter than -`Unbonding period` to prevent double voting. The initial value of +the moment the vote closes. `Voting period` should always be shorter than +`Unbonding period` to prevent double voting. The initial value of `Voting period` is 2 weeks. ### Option set -The option set of a proposal refers to the set of choices a participant can +The option set of a proposal refers to the set of choices a participant can choose from when casting its vote. -The initial option set includes the following options: +The initial option set includes the following options: + - `Yes` - `No` -- `NoWithVeto` -- `Abstain` +- `NoWithVeto` +- `Abstain` -`NoWithVeto` counts as `No` but also adds a `Veto` vote. `Abstain` option +`NoWithVeto` counts as `No` but also adds a `Veto` vote. `Abstain` option allows voters to signal that they do not intend to vote in favor or against the -proposal but accept the result of the vote. +proposal but accept the result of the vote. -*Note: from the UI, for urgent proposals we should maybe add a ‘Not Urgent’ -option that casts a `NoWithVeto` vote.* +_Note: from the UI, for urgent proposals we should maybe add a ‘Not Urgent’ +option that casts a `NoWithVeto` vote._ -### Quorum +### Quorum -Quorum is defined as the minimum percentage of voting power that needs to be -casted on a proposal for the result to be valid. +Quorum is defined as the minimum percentage of voting power that needs to be +casted on a proposal for the result to be valid. ### Threshold -Threshold is defined as the minimum proportion of `Yes` votes (excluding +Threshold is defined as the minimum proportion of `Yes` votes (excluding `Abstain` votes) for the proposal to be accepted. Initially, the threshold is set at 50% with a possibility to veto if more than -1/3rd of votes (excluding `Abstain` votes) are `NoWithVeto` votes. This means -that proposals are accepted if the proportion of `Yes` votes (excluding -`Abstain` votes) at the end of the voting period is superior to 50% and if the -proportion of `NoWithVeto` votes is inferior to 1/3 (excluding `Abstain` +1/3rd of votes (excluding `Abstain` votes) are `NoWithVeto` votes. This means +that proposals are accepted if the proportion of `Yes` votes (excluding +`Abstain` votes) at the end of the voting period is superior to 50% and if the +proportion of `NoWithVeto` votes is inferior to 1/3 (excluding `Abstain` votes). -Proposals can be accepted before the end of the voting period if they meet a special condition. Namely, if the ratio of `Yes` votes to `InitTotalVotingPower`exceeds 2:3, the proposal will be immediately accepted, even if the `Voting period` is not finished. `InitTotalVotingPower` is the total voting power of all bonded Atom holders at the moment when the vote opens. -This condition exists so that the network can react quickly in case of urgency. - ### Inheritance If a delegator does not vote, it will inherit its validator vote. -* If the delegator votes before its validator, it will not inherit from the +- If the delegator votes before its validator, it will not inherit from the validator's vote. -* If the delegator votes after its validator, it will override its validator - vote with its own. If the proposal is urgent, it is possible - that the vote will close before delegators have a chance to react and +- If the delegator votes after its validator, it will override its validator + vote with its own. If the proposal is urgent, it is possible + that the vote will close before delegators have a chance to react and override their validator's vote. This is not a problem, as proposals require more than 2/3rd of the total voting power to pass before the end of the voting period. If more than 2/3rd of validators collude, they can censor the votes of delegators anyway. ### Validator’s punishment for non-voting @@ -150,29 +150,29 @@ Later, we may add permissioned keys that could only sign txs from certain module ## Software Upgrade -If proposals are of type `SoftwareUpgradeProposal`, then nodes need to upgrade -their software to the new version that was voted. This process is divided in +If proposals are of type `SoftwareUpgradeProposal`, then nodes need to upgrade +their software to the new version that was voted. This process is divided in two steps. ### Signal -After a `SoftwareUpgradeProposal` is accepted, validators are expected to -download and install the new version of the software while continuing to run -the previous version. Once a validator has downloaded and installed the -upgrade, it will start signaling to the network that it is ready to switch by -including the proposal's `proposalID` in its *precommits*.(*Note: Confirmation -that we want it in the precommit?*) +After a `SoftwareUpgradeProposal` is accepted, validators are expected to +download and install the new version of the software while continuing to run +the previous version. Once a validator has downloaded and installed the +upgrade, it will start signaling to the network that it is ready to switch by +including the proposal's `proposalID` in its _precommits_.(_Note: Confirmation +that we want it in the precommit?_) -Note: There is only one signal slot per *precommit*. If several -`SoftwareUpgradeProposals` are accepted in a short timeframe, a pipeline will -form and they will be implemented one after the other in the order that they +Note: There is only one signal slot per _precommit_. If several +`SoftwareUpgradeProposals` are accepted in a short timeframe, a pipeline will +form and they will be implemented one after the other in the order that they were accepted. ### Switch -Once a block contains more than 2/3rd *precommits* where a common -`SoftwareUpgradeProposal` is signaled, all the nodes (including validator +Once a block contains more than 2/3rd _precommits_ where a common +`SoftwareUpgradeProposal` is signaled, all the nodes (including validator nodes, non-validating full nodes and light-nodes) are expected to switch to the -new version of the software. +new version of the software. -*Note: Not clear how the flip is handled programmatically* +_Note: Not clear how the flip is handled programmatically_ diff --git a/x/gov/spec/02_state.md b/x/gov/spec/02_state.md index e180c2839461..ba3b12b3f3a7 100644 --- a/x/gov/spec/02_state.md +++ b/x/gov/spec/02_state.md @@ -96,7 +96,7 @@ the governance process. type Proposal struct { Content // Proposal content interface - ProposalID uint64 + ProposalID uint64 Status ProposalStatus // Status of the Proposal {Pending, Active, Passed, Rejected} FinalTallyResult TallyResult // Result of Tallies @@ -145,26 +145,26 @@ We also mention a method to update the tally for a given proposal: ## Stores -*Stores are KVStores in the multi-store. The key to find the store is the first -parameter in the list*` +_Stores are KVStores in the multi-store. The key to find the store is the first +parameter in the list_` We will use one KVStore `Governance` to store two mappings: -* A mapping from `proposalID|'proposal'` to `Proposal`. -* A mapping from `proposalID|'addresses'|address` to `Vote`. This mapping allows -us to query all addresses that voted on the proposal along with their vote by -doing a range query on `proposalID:addresses`. - +- A mapping from `proposalID|'proposal'` to `Proposal`. +- A mapping from `proposalID|'addresses'|address` to `Vote`. This mapping allows + us to query all addresses that voted on the proposal along with their vote by + doing a range query on `proposalID:addresses`. For pseudocode purposes, here are the two function we will use to read or write in stores: -* `load(StoreKey, Key)`: Retrieve item stored at key `Key` in store found at key `StoreKey` in the multistore -* `store(StoreKey, Key, value)`: Write value `Value` at key `Key` in store found at key `StoreKey` in the multistore +- `load(StoreKey, Key)`: Retrieve item stored at key `Key` in store found at key `StoreKey` in the multistore +- `store(StoreKey, Key, value)`: Write value `Value` at key `Key` in store found at key `StoreKey` in the multistore ## Proposal Processing Queue **Store:** -* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the + +- `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the `ProposalIDs` of proposals that reached `MinDeposit`. During each `EndBlock`, all the proposals that have reached the end of their voting period are processed. To process a finished proposal, the application tallies the votes, computes the diff --git a/x/gov/spec/03_messages.md b/x/gov/spec/03_messages.md index f0e8dc9161c5..cac55b5ed945 100644 --- a/x/gov/spec/03_messages.md +++ b/x/gov/spec/03_messages.md @@ -21,13 +21,14 @@ The `Content` of a `TxGovSubmitProposal` message must have an appropriate router set in the governance module. **State modifications:** -* Generate new `proposalID` -* Create new `Proposal` -* Initialise `Proposals` attributes -* Decrease balance of sender by `InitialDeposit` -* If `MinDeposit` is reached: - * Push `proposalID` in `ProposalProcessingQueue` -* Transfer `InitialDeposit` from the `Proposer` to the governance `ModuleAccount` + +- Generate new `proposalID` +- Create new `Proposal` +- Initialise `Proposals` attributes +- Decrease balance of sender by `InitialDeposit` +- If `MinDeposit` is reached: + - Push `proposalID` in `ProposalProcessingQueue` +- Transfer `InitialDeposit` from the `Proposer` to the governance `ModuleAccount` A `TxGovSubmitProposal` transaction can be handled according to the following pseudocode. @@ -88,12 +89,13 @@ type TxGovDeposit struct { ``` **State modifications:** -* Decrease balance of sender by `deposit` -* Add `deposit` of sender in `proposal.Deposits` -* Increase `proposal.TotalDeposit` by sender's `deposit` -* If `MinDeposit` is reached: - * Push `proposalID` in `ProposalProcessingQueueEnd` -* Transfer `Deposit` from the `proposer` to the governance `ModuleAccount` + +- Decrease balance of sender by `deposit` +- Add `deposit` of sender in `proposal.Deposits` +- Increase `proposal.TotalDeposit` by sender's `deposit` +- If `MinDeposit` is reached: + - 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. These checks are outlined in the following pseudocode. @@ -158,10 +160,10 @@ vote on the proposal. ``` **State modifications:** -* Record `Vote` of sender -*Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker* +- Record `Vote` of sender +_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 handled: diff --git a/x/gov/spec/04_events.md b/x/gov/spec/04_events.md index 918b3754ed19..dc3b1bf3a231 100644 --- a/x/gov/spec/04_events.md +++ b/x/gov/spec/04_events.md @@ -9,7 +9,7 @@ The governance module emits the following events: ## EndBlocker | Type | Attribute Key | Attribute Value | -|-------------------|-----------------|------------------| +| ----------------- | --------------- | ---------------- | | inactive_proposal | proposal_id | {proposalID} | | inactive_proposal | proposal_result | {proposalResult} | | active_proposal | proposal_id | {proposalID} | @@ -20,7 +20,7 @@ The governance module emits the following events: ### MsgSubmitProposal | Type | Attribute Key | Attribute Value | -|---------------------|---------------------|-----------------| +| ------------------- | ------------------- | --------------- | | submit_proposal | proposal_id | {proposalID} | | submit_proposal [0] | voting_period_start | {proposalID} | | proposal_deposit | amount | {depositAmount} | @@ -29,12 +29,12 @@ The governance module emits the following events: | message | action | submit_proposal | | message | sender | {senderAddress} | -* [0] Event only emitted if the voting period starts during the submission. +- [0] Event only emitted if the voting period starts during the submission. ### MsgVote | Type | Attribute Key | Attribute Value | -|---------------|---------------|-----------------| +| ------------- | ------------- | --------------- | | proposal_vote | option | {voteOption} | | proposal_vote | proposal_id | {proposalID} | | message | module | governance | @@ -44,7 +44,7 @@ The governance module emits the following events: ### MsgDeposit | Type | Attribute Key | Attribute Value | -|----------------------|---------------------|-----------------| +| -------------------- | ------------------- | --------------- | | proposal_deposit | amount | {depositAmount} | | proposal_deposit | proposal_id | {proposalID} | | proposal_deposit [0] | voting_period_start | {proposalID} | @@ -52,4 +52,4 @@ The governance module emits the following events: | message | action | deposit | | message | sender | {senderAddress} | -* [0] Event only emitted if the voting period starts during the submission. +- [0] Event only emitted if the voting period starts during the submission. diff --git a/x/gov/test_common.go b/x/gov/test_common.go deleted file mode 100644 index 7ecd4bd50f68..000000000000 --- a/x/gov/test_common.go +++ /dev/null @@ -1,239 +0,0 @@ -// nolint -// DONTCOVER -package gov - -import ( - "bytes" - "errors" - "log" - "sort" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/bank" - keep "github.com/cosmos/cosmos-sdk/x/gov/keeper" - "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/mock" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -var ( - valTokens = sdk.TokensFromConsensusPower(42) - initTokens = sdk.TokensFromConsensusPower(100000) - valCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens)) - initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) -) - -type testInput struct { - mApp *mock.App - keeper keep.Keeper - router types.Router - sk staking.Keeper - addrs []sdk.AccAddress - pubKeys []crypto.PubKey - privKeys []crypto.PrivKey -} - -func getMockApp( - t *testing.T, numGenAccs int, genState types.GenesisState, genAccs []authexported.Account, - handler func(ctx sdk.Context, c types.Content) error, -) testInput { - - mApp := mock.NewApp() - - staking.RegisterCodec(mApp.Cdc) - types.RegisterCodec(mApp.Cdc) - supply.RegisterCodec(mApp.Cdc) - - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keyGov := sdk.NewKVStoreKey(types.StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - - govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner) - notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[govAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - pk := mApp.ParamsKeeper - - rtr := types.NewRouter(). - AddRoute(types.RouterKey, handler) - - bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) - - maccPerms := map[string][]string{ - types.ModuleName: {supply.Burner}, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, maccPerms) - sk := staking.NewKeeper( - mApp.Cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), - ) - - keeper := keep.NewKeeper( - mApp.Cdc, keyGov, pk.Subspace(DefaultParamspace).WithKeyTable(ParamKeyTable()), supplyKeeper, sk, rtr, - ) - - mApp.Router().AddRoute(types.RouterKey, NewHandler(keeper)) - mApp.QueryRouter().AddRoute(types.QuerierRoute, keep.NewQuerier(keeper)) - - mApp.SetEndBlocker(getEndBlocker(keeper)) - mApp.SetInitChainer(getInitChainer(mApp, keeper, sk, supplyKeeper, genAccs, genState, - []supplyexported.ModuleAccountI{govAcc, notBondedPool, bondPool})) - - require.NoError(t, mApp.CompleteSetup(keyStaking, keyGov, keySupply)) - - var ( - addrs []sdk.AccAddress - pubKeys []crypto.PubKey - privKeys []crypto.PrivKey - ) - - if genAccs == nil || len(genAccs) == 0 { - genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs, valCoins) - } - - mock.SetGenesis(mApp, genAccs) - - return testInput{mApp, keeper, rtr, sk, addrs, pubKeys, privKeys} -} - -// gov and staking endblocker -func getEndBlocker(keeper Keeper) sdk.EndBlocker { - return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - EndBlocker(ctx, keeper) - return abci.ResponseEndBlock{} - } -} - -// gov and staking initchainer -func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper, supplyKeeper supply.Keeper, accs []authexported.Account, genState GenesisState, - blacklistedAddrs []supplyexported.ModuleAccountI) sdk.InitChainer { - return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - mapp.InitChainer(ctx, req) - - stakingGenesis := staking.DefaultGenesisState() - - totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(mapp.GenesisAccounts))))) - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - // set module accounts - for _, macc := range blacklistedAddrs { - supplyKeeper.SetModuleAccount(ctx, macc) - } - - validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis) - if genState.IsEmpty() { - InitGenesis(ctx, keeper, supplyKeeper, types.DefaultGenesisState()) - } else { - InitGenesis(ctx, keeper, supplyKeeper, genState) - } - return abci.ResponseInitChain{ - Validators: validators, - } - } -} - -// SortAddresses - Sorts Addresses -func SortAddresses(addrs []sdk.AccAddress) { - var byteAddrs [][]byte - for _, addr := range addrs { - byteAddrs = append(byteAddrs, addr.Bytes()) - } - SortByteArrays(byteAddrs) - for i, byteAddr := range byteAddrs { - addrs[i] = byteAddr - } -} - -// implement `Interface` in sort package. -type sortByteArrays [][]byte - -func (b sortByteArrays) Len() int { - return len(b) -} - -func (b sortByteArrays) Less(i, j int) bool { - // bytes package already implements Comparable for []byte. - switch bytes.Compare(b[i], b[j]) { - case -1: - return true - case 0, 1: - return false - default: - log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") - return false - } -} - -func (b sortByteArrays) Swap(i, j int) { - b[j], b[i] = b[i], b[j] -} - -// SortByteArrays - sorts the provided byte array -func SortByteArrays(src [][]byte) [][]byte { - sorted := sortByteArrays(src) - sort.Sort(sorted) - return sorted -} - -const contextKeyBadProposal = "contextKeyBadProposal" - -// badProposalHandler implements a governance proposal handler that is identical -// to the actual handler except this fails if the context doesn't contain a value -// for the key contextKeyBadProposal or if the value is false. -func badProposalHandler(ctx sdk.Context, c types.Content) error { - switch c.ProposalType() { - case types.ProposalTypeText: - v := ctx.Value(contextKeyBadProposal) - - if v == nil || !v.(bool) { - return errors.New("proposal failed") - } - - return nil - - default: - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized gov proposal type: %s", c.ProposalType()) - } -} - -var ( - pubkeys = []crypto.PubKey{ - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - ed25519.GenPrivKey().PubKey(), - } -) - -func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { - require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") - - for i := 0; i < len(addrs); i++ { - - valTokens := sdk.TokensFromConsensusPower(powerAmt[i]) - valCreateMsg := staking.NewMsgCreateValidator( - addrs[i], pubkeys[i], sdk.NewCoin(sdk.DefaultBondDenom, valTokens), - keep.TestDescription, keep.TestCommissionRates, sdk.OneInt(), - ) - - res, err := stakingHandler(ctx, valCreateMsg) - require.NoError(t, err) - require.NotNil(t, res) - } -} diff --git a/x/gov/types/codec.go b/x/gov/types/codec.go index fc3de0e3f30c..46deb5c59ecb 100644 --- a/x/gov/types/codec.go +++ b/x/gov/types/codec.go @@ -2,31 +2,60 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" ) -// module codec -var ModuleCdc = codec.New() - -// RegisterCodec registers all the necessary types and interfaces for -// governance. -func RegisterCodec(cdc *codec.Codec) { +// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the +// governance module. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterInterface((*Content)(nil), nil) + cdc.RegisterConcrete(&MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) + cdc.RegisterConcrete(&MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) + cdc.RegisterConcrete(&MsgVote{}, "cosmos-sdk/MsgVote", nil) + cdc.RegisterConcrete(&TextProposal{}, "cosmos-sdk/TextProposal", nil) +} - cdc.RegisterConcrete(MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) - cdc.RegisterConcrete(MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) - cdc.RegisterConcrete(MsgVote{}, "cosmos-sdk/MsgVote", nil) +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgSubmitProposal{}, + &MsgVote{}, + &MsgDeposit{}, + ) + registry.RegisterInterface( + "cosmos.gov.v1beta1.Content", + (*Content)(nil), + &TextProposal{}, + ) - cdc.RegisterConcrete(TextProposal{}, "cosmos-sdk/TextProposal", nil) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } // RegisterProposalTypeCodec registers an external proposal content type defined // in another module for the internal ModuleCdc. This allows the MsgSubmitProposal // to be correctly Amino encoded and decoded. +// +// NOTE: This should only be used for applications that are still using a concrete +// Amino codec for serialization. func RegisterProposalTypeCodec(o interface{}, name string) { - ModuleCdc.RegisterConcrete(o, name, nil) + amino.RegisterConcrete(o, name, nil) } -// TODO determine a good place to seal this codec +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/gov module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/gov and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + func init() { - RegisterCodec(ModuleCdc) + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) } diff --git a/x/gov/types/content.go b/x/gov/types/content.go index 21d70d5ab1e8..44cbe6af7547 100644 --- a/x/gov/types/content.go +++ b/x/gov/types/content.go @@ -17,6 +17,8 @@ const ( // information such as the title and description along with the type and routing // information for the appropriate handler to process the proposal. Content can // have additional fields, which will handled by a proposal's Handler. +// TODO Try to unify this interface with types/module/simulation +// https://github.com/cosmos/cosmos-sdk/issues/5853 type Content interface { GetTitle() string GetDescription() string diff --git a/x/gov/types/deposit.go b/x/gov/types/deposit.go index 4d9ff9304179..e386088737f7 100644 --- a/x/gov/types/deposit.go +++ b/x/gov/types/deposit.go @@ -3,46 +3,52 @@ package types import ( "fmt" + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" ) -// Deposit defines an amount deposited by an account address to an active proposal -type Deposit struct { - ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // proposalID of the proposal - Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor - Amount sdk.Coins `json:"amount" yaml:"amount"` // Deposit amount -} - // NewDeposit creates a new Deposit instance +//nolint:interfacer func NewDeposit(proposalID uint64, depositor sdk.AccAddress, amount sdk.Coins) Deposit { - return Deposit{proposalID, depositor, amount} + return Deposit{proposalID, depositor.String(), amount} } func (d Deposit) String() string { - return fmt.Sprintf("deposit by %s on Proposal %d is for the amount %s", - d.Depositor, d.ProposalID, d.Amount) + out, _ := yaml.Marshal(d) + return string(out) } // Deposits is a collection of Deposit objects type Deposits []Deposit +// Equal returns true if two slices (order-dependant) of deposits are equal. +func (d Deposits) Equal(other Deposits) bool { + if len(d) != len(other) { + return false + } + + for i, deposit := range d { + if deposit.String() != other[i].String() { + return false + } + } + + return true +} + func (d Deposits) String() string { if len(d) == 0 { return "[]" } - out := fmt.Sprintf("Deposits for Proposal %d:", d[0].ProposalID) + out := fmt.Sprintf("Deposits for Proposal %d:", d[0].ProposalId) for _, dep := range d { out += fmt.Sprintf("\n %s: %s", dep.Depositor, dep.Amount) } return out } -// Equals returns whether two deposits are equal. -func (d Deposit) Equals(comp Deposit) bool { - return d.Depositor.Equals(comp.Depositor) && d.ProposalID == comp.ProposalID && d.Amount.IsEqual(comp.Amount) -} - // Empty returns whether a deposit is empty. func (d Deposit) Empty() bool { - return d.Equals(Deposit{}) + return d.String() == Deposit{}.String() } diff --git a/x/gov/types/errors.go b/x/gov/types/errors.go index 2327a6f05b5e..96973f1751a2 100644 --- a/x/gov/types/errors.go +++ b/x/gov/types/errors.go @@ -6,12 +6,12 @@ import ( // x/gov module sentinel errors var ( - ErrUnknownProposal = sdkerrors.Register(ModuleName, 1, "unknown proposal") - ErrInactiveProposal = sdkerrors.Register(ModuleName, 2, "inactive proposal") - ErrAlreadyActiveProposal = sdkerrors.Register(ModuleName, 3, "proposal already active") - ErrInvalidProposalContent = sdkerrors.Register(ModuleName, 4, "invalid proposal content") - ErrInvalidProposalType = sdkerrors.Register(ModuleName, 5, "invalid proposal type") - ErrInvalidVote = sdkerrors.Register(ModuleName, 6, "invalid vote option") - ErrInvalidGenesis = sdkerrors.Register(ModuleName, 7, "invalid genesis state") - ErrNoProposalHandlerExists = sdkerrors.Register(ModuleName, 8, "no handler exists for proposal type") + ErrUnknownProposal = sdkerrors.Register(ModuleName, 2, "unknown proposal") + ErrInactiveProposal = sdkerrors.Register(ModuleName, 3, "inactive proposal") + ErrAlreadyActiveProposal = sdkerrors.Register(ModuleName, 4, "proposal already active") + ErrInvalidProposalContent = sdkerrors.Register(ModuleName, 5, "invalid proposal content") + ErrInvalidProposalType = sdkerrors.Register(ModuleName, 6, "invalid proposal type") + ErrInvalidVote = sdkerrors.Register(ModuleName, 7, "invalid vote option") + ErrInvalidGenesis = sdkerrors.Register(ModuleName, 8, "invalid genesis state") + ErrNoProposalHandlerExists = sdkerrors.Register(ModuleName, 9, "no handler exists for proposal type") ) diff --git a/x/gov/types/expected_keepers.go b/x/gov/types/expected_keepers.go index 31e5d1e05cff..f862a9bce563 100644 --- a/x/gov/types/expected_keepers.go +++ b/x/gov/types/expected_keepers.go @@ -2,9 +2,8 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + "github.com/cosmos/cosmos-sdk/x/auth/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // ParamSubspace defines the expected Subspace interface for parameters (noalias) @@ -13,34 +12,40 @@ type ParamSubspace interface { Set(ctx sdk.Context, key []byte, param interface{}) } -// SupplyKeeper defines the expected supply keeper for module accounts (noalias) -type SupplyKeeper interface { - GetModuleAddress(name string) sdk.AccAddress - GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI - - // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 - SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) - - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error -} - // StakingKeeper expected staking keeper (Validator and Delegator sets) (noalias) type StakingKeeper interface { // iterate through bonded validators by operator address, execute func for each validator IterateBondedValidatorsByPower( - sdk.Context, func(index int64, validator stakingexported.ValidatorI) (stop bool), + sdk.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool), ) TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set IterateDelegations( ctx sdk.Context, delegator sdk.AccAddress, - fn func(index int64, delegation stakingexported.DelegationI) (stop bool), + fn func(index int64, delegation stakingtypes.DelegationI) (stop bool), ) } // AccountKeeper defines the expected account keeper (noalias) type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI + + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, name string) types.ModuleAccountI + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + SetModuleAccount(sdk.Context, types.ModuleAccountI) +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +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 + + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error } diff --git a/x/gov/types/genesis.go b/x/gov/types/genesis.go index 69f49fa68bb2..7f2d138ae300 100644 --- a/x/gov/types/genesis.go +++ b/x/gov/types/genesis.go @@ -1,27 +1,16 @@ package types import ( - "bytes" "fmt" + "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" ) -// GenesisState - all staking state that must be provided at genesis -type GenesisState struct { - StartingProposalID uint64 `json:"starting_proposal_id" yaml:"starting_proposal_id"` - Deposits Deposits `json:"deposits" yaml:"deposits"` - Votes Votes `json:"votes" yaml:"votes"` - Proposals Proposals `json:"proposals" yaml:"proposals"` - DepositParams DepositParams `json:"deposit_params" yaml:"deposit_params"` - VotingParams VotingParams `json:"voting_params" yaml:"voting_params"` - TallyParams TallyParams `json:"tally_params" yaml:"tally_params"` -} - // NewGenesisState creates a new genesis state for the governance module -func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState { - return GenesisState{ - StartingProposalID: startingProposalID, +func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) *GenesisState { + return &GenesisState{ + StartingProposalId: startingProposalID, DepositParams: dp, VotingParams: vp, TallyParams: tp, @@ -29,7 +18,7 @@ func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParam } // DefaultGenesisState defines the default governance genesis state -func DefaultGenesisState() GenesisState { +func DefaultGenesisState() *GenesisState { return NewGenesisState( DefaultStartingProposalID, DefaultDepositParams(), @@ -38,27 +27,30 @@ func DefaultGenesisState() GenesisState { ) } -// Equal checks whether two gov GenesisState structs are equivalent -func (data GenesisState) Equal(data2 GenesisState) bool { - b1 := ModuleCdc.MustMarshalBinaryBare(data) - b2 := ModuleCdc.MustMarshalBinaryBare(data2) - return bytes.Equal(b1, b2) +func (data GenesisState) Equal(other GenesisState) bool { + return data.StartingProposalId == other.StartingProposalId && + data.Deposits.Equal(other.Deposits) && + data.Votes.Equal(other.Votes) && + data.Proposals.Equal(other.Proposals) && + data.DepositParams.Equal(other.DepositParams) && + data.TallyParams.Equal(other.TallyParams) && + data.VotingParams.Equal(other.VotingParams) } -// IsEmpty returns true if a GenesisState is empty -func (data GenesisState) IsEmpty() bool { +// Empty returns true if a GenesisState is empty +func (data GenesisState) Empty() bool { return data.Equal(GenesisState{}) } // ValidateGenesis checks if parameters are within valid ranges -func ValidateGenesis(data GenesisState) error { +func ValidateGenesis(data *GenesisState) error { threshold := data.TallyParams.Threshold if threshold.IsNegative() || threshold.GT(sdk.OneDec()) { return fmt.Errorf("governance vote threshold should be positive and less or equal to one, is %s", threshold.String()) } - veto := data.TallyParams.Veto + veto := data.TallyParams.VetoThreshold if veto.IsNegative() || veto.GT(sdk.OneDec()) { return fmt.Errorf("governance vote veto threshold should be positive and less or equal to one, is %s", veto.String()) @@ -71,3 +63,16 @@ func ValidateGenesis(data GenesisState) error { return nil } + +var _ types.UnpackInterfacesMessage = GenesisState{} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (data GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, p := range data.Proposals { + err := p.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} diff --git a/x/gov/types/genesis.pb.go b/x/gov/types/genesis.pb.go new file mode 100644 index 000000000000..4df93da3d07b --- /dev/null +++ b/x/gov/types/genesis.pb.go @@ -0,0 +1,669 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/gov/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 defines the gov module's genesis state. +type GenesisState struct { + // starting_proposal_id is the ID of the starting proposal. + StartingProposalId uint64 `protobuf:"varint,1,opt,name=starting_proposal_id,json=startingProposalId,proto3" json:"starting_proposal_id,omitempty" yaml:"starting_proposal_id"` + // deposits defines all the deposits present at genesis. + Deposits Deposits `protobuf:"bytes,2,rep,name=deposits,proto3,castrepeated=Deposits" json:"deposits"` + // votes defines all the votes present at genesis. + Votes Votes `protobuf:"bytes,3,rep,name=votes,proto3,castrepeated=Votes" json:"votes"` + // proposals defines all the proposals present at genesis. + Proposals Proposals `protobuf:"bytes,4,rep,name=proposals,proto3,castrepeated=Proposals" json:"proposals"` + // params defines all the paramaters of related to deposit. + DepositParams DepositParams `protobuf:"bytes,5,opt,name=deposit_params,json=depositParams,proto3" json:"deposit_params" yaml:"deposit_params"` + // params defines all the paramaters of related to voting. + VotingParams VotingParams `protobuf:"bytes,6,opt,name=voting_params,json=votingParams,proto3" json:"voting_params" yaml:"voting_params"` + // params defines all the paramaters of related to tally. + TallyParams TallyParams `protobuf:"bytes,7,opt,name=tally_params,json=tallyParams,proto3" json:"tally_params" yaml:"tally_params"` +} + +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_43cd825e0fa7a627, []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) GetStartingProposalId() uint64 { + if m != nil { + return m.StartingProposalId + } + return 0 +} + +func (m *GenesisState) GetDeposits() Deposits { + if m != nil { + return m.Deposits + } + return nil +} + +func (m *GenesisState) GetVotes() Votes { + if m != nil { + return m.Votes + } + return nil +} + +func (m *GenesisState) GetProposals() Proposals { + if m != nil { + return m.Proposals + } + return nil +} + +func (m *GenesisState) GetDepositParams() DepositParams { + if m != nil { + return m.DepositParams + } + return DepositParams{} +} + +func (m *GenesisState) GetVotingParams() VotingParams { + if m != nil { + return m.VotingParams + } + return VotingParams{} +} + +func (m *GenesisState) GetTallyParams() TallyParams { + if m != nil { + return m.TallyParams + } + return TallyParams{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.gov.v1beta1.GenesisState") +} + +func init() { proto.RegisterFile("cosmos/gov/v1beta1/genesis.proto", fileDescriptor_43cd825e0fa7a627) } + +var fileDescriptor_43cd825e0fa7a627 = []byte{ + // 430 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x31, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x63, 0x9a, 0x94, 0xf6, 0x92, 0x20, 0x38, 0x82, 0x64, 0x35, 0xc1, 0x36, 0x9e, 0xb2, + 0x60, 0xab, 0x65, 0x43, 0x62, 0xb1, 0x90, 0x50, 0x07, 0xa4, 0x62, 0x10, 0x03, 0x4b, 0x74, 0x89, + 0x4f, 0x87, 0x85, 0xdd, 0x67, 0xf9, 0x1d, 0x16, 0xf9, 0x16, 0x7c, 0x0e, 0x3e, 0x49, 0xc7, 0x8e, + 0x4c, 0x01, 0x25, 0x13, 0x6b, 0x3f, 0x01, 0xf2, 0xdd, 0x19, 0x1c, 0x61, 0x3a, 0x25, 0x7e, 0xf7, + 0xbf, 0xdf, 0xef, 0xdd, 0xd3, 0x23, 0xde, 0x0a, 0x30, 0x07, 0x0c, 0x05, 0x54, 0x61, 0x75, 0xba, + 0xe4, 0x92, 0x9d, 0x86, 0x82, 0x5f, 0x72, 0x4c, 0x31, 0x28, 0x4a, 0x90, 0x40, 0xa9, 0x4e, 0x04, + 0x02, 0xaa, 0xc0, 0x24, 0x4e, 0x26, 0x02, 0x04, 0xa8, 0xe3, 0xb0, 0xfe, 0xa7, 0x93, 0x27, 0xb3, + 0x2e, 0x16, 0x54, 0xfa, 0xd4, 0xff, 0xd5, 0x27, 0xa3, 0x57, 0x9a, 0xfc, 0x56, 0x32, 0xc9, 0xe9, + 0x1b, 0x32, 0x41, 0xc9, 0x4a, 0x99, 0x5e, 0x8a, 0x45, 0x51, 0x42, 0x01, 0xc8, 0xb2, 0x45, 0x9a, + 0xd8, 0x96, 0x67, 0xcd, 0xfb, 0x91, 0x7b, 0xb3, 0x71, 0xa7, 0x6b, 0x96, 0x67, 0xcf, 0xfd, 0xae, + 0x94, 0x1f, 0xd3, 0xa6, 0x7c, 0x61, 0xaa, 0xe7, 0x09, 0x3d, 0x27, 0x47, 0x09, 0x2f, 0x00, 0x53, + 0x89, 0xf6, 0x1d, 0xef, 0x60, 0x3e, 0x3c, 0x9b, 0x06, 0xff, 0xb6, 0x1f, 0xbc, 0xd4, 0x99, 0xe8, + 0xfe, 0xd5, 0xc6, 0xed, 0x7d, 0xfb, 0xe1, 0x1e, 0x99, 0x02, 0xc6, 0x7f, 0xae, 0xd3, 0x17, 0x64, + 0x50, 0x81, 0xe4, 0x68, 0x1f, 0x28, 0x8e, 0xdd, 0xc5, 0x79, 0x0f, 0x92, 0x47, 0x63, 0x03, 0x19, + 0xd4, 0x5f, 0x18, 0xeb, 0x5b, 0xf4, 0x35, 0x39, 0x6e, 0xba, 0x45, 0xbb, 0xaf, 0x10, 0xb3, 0x2e, + 0x44, 0xd3, 0x7c, 0xf4, 0xc0, 0x60, 0x8e, 0x9b, 0x0a, 0xc6, 0x7f, 0x09, 0x54, 0x90, 0x7b, 0xa6, + 0xb3, 0x45, 0xc1, 0x4a, 0x96, 0xa3, 0x3d, 0xf0, 0xac, 0xf9, 0xf0, 0xec, 0xc9, 0x2d, 0xcf, 0xbb, + 0x50, 0xc1, 0xe8, 0x71, 0x0d, 0xbe, 0xd9, 0xb8, 0x8f, 0xf4, 0x30, 0xf7, 0x31, 0x7e, 0x3c, 0x4e, + 0xda, 0x69, 0xba, 0x22, 0xe3, 0x0a, 0xf4, 0xb0, 0xb5, 0xe7, 0x50, 0x79, 0xbc, 0xff, 0x3c, 0xbf, + 0x1e, 0xbf, 0xd6, 0xcc, 0x8c, 0x66, 0xa2, 0x35, 0x7b, 0x10, 0x3f, 0x1e, 0x55, 0xad, 0x2c, 0x5d, + 0x90, 0x91, 0x64, 0x59, 0xb6, 0x6e, 0x1c, 0x77, 0x95, 0xc3, 0xed, 0x72, 0xbc, 0xab, 0x73, 0x46, + 0x31, 0x35, 0x8a, 0x87, 0x5a, 0xd1, 0x46, 0xf8, 0xf1, 0x50, 0xb6, 0x92, 0xd1, 0xd5, 0xd6, 0xb1, + 0xae, 0xb7, 0x8e, 0xf5, 0x73, 0xeb, 0x58, 0x5f, 0x77, 0x4e, 0xef, 0x7a, 0xe7, 0xf4, 0xbe, 0xef, + 0x9c, 0xde, 0x87, 0xb9, 0x48, 0xe5, 0xc7, 0xcf, 0xcb, 0x60, 0x05, 0x79, 0x68, 0xd6, 0x55, 0xff, + 0x3c, 0xc5, 0xe4, 0x53, 0xf8, 0x45, 0xed, 0xae, 0x5c, 0x17, 0x1c, 0x97, 0x87, 0x6a, 0x6d, 0x9f, + 0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x63, 0x67, 0xf6, 0x22, 0x03, 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 + { + size, err := m.TallyParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + { + size, err := m.VotingParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + { + size, err := m.DepositParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.Proposals) > 0 { + for iNdEx := len(m.Proposals) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Proposals[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Votes) > 0 { + for iNdEx := len(m.Votes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Votes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Deposits) > 0 { + for iNdEx := len(m.Deposits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Deposits[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.StartingProposalId != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.StartingProposalId)) + i-- + dAtA[i] = 0x8 + } + 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 m.StartingProposalId != 0 { + n += 1 + sovGenesis(uint64(m.StartingProposalId)) + } + if len(m.Deposits) > 0 { + for _, e := range m.Deposits { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Votes) > 0 { + for _, e := range m.Votes { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Proposals) > 0 { + for _, e := range m.Proposals { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.DepositParams.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.VotingParams.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.TallyParams.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 != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartingProposalId", wireType) + } + m.StartingProposalId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartingProposalId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposits", 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.Deposits = append(m.Deposits, Deposit{}) + if err := m.Deposits[len(m.Deposits)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Votes", 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.Votes = append(m.Votes, Vote{}) + if err := m.Votes[len(m.Votes)-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 Proposals", 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.Proposals = append(m.Proposals, Proposal{}) + if err := m.Proposals[len(m.Proposals)-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 DepositParams", 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 := m.DepositParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingParams", 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 := m.VotingParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TallyParams", 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 := m.TallyParams.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/gov/types/genesis_test.go b/x/gov/types/genesis_test.go index 6fc21138339c..3a3e6e4282e3 100644 --- a/x/gov/types/genesis_test.go +++ b/x/gov/types/genesis_test.go @@ -12,11 +12,11 @@ func TestEqualProposalID(t *testing.T) { require.Equal(t, state1, state2) // Proposals - state1.StartingProposalID = 1 + state1.StartingProposalId = 1 require.NotEqual(t, state1, state2) require.False(t, state1.Equal(state2)) - state2.StartingProposalID = 1 + state2.StartingProposalId = 1 require.Equal(t, state1, state2) require.True(t, state1.Equal(state2)) } diff --git a/x/gov/types/gov.pb.go b/x/gov/types/gov.pb.go new file mode 100644 index 000000000000..6f2b7dc697a2 --- /dev/null +++ b/x/gov/types/gov.pb.go @@ -0,0 +1,2569 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/gov/v1beta1/gov.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/regen-network/cosmos-proto" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// VoteOption enumerates the valid vote options for a given governance proposal. +type VoteOption int32 + +const ( + // VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + OptionEmpty VoteOption = 0 + // VOTE_OPTION_YES defines a yes vote option. + OptionYes VoteOption = 1 + // VOTE_OPTION_ABSTAIN defines an abstain vote option. + OptionAbstain VoteOption = 2 + // VOTE_OPTION_NO defines a no vote option. + OptionNo VoteOption = 3 + // VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + OptionNoWithVeto VoteOption = 4 +) + +var VoteOption_name = map[int32]string{ + 0: "VOTE_OPTION_UNSPECIFIED", + 1: "VOTE_OPTION_YES", + 2: "VOTE_OPTION_ABSTAIN", + 3: "VOTE_OPTION_NO", + 4: "VOTE_OPTION_NO_WITH_VETO", +} + +var VoteOption_value = map[string]int32{ + "VOTE_OPTION_UNSPECIFIED": 0, + "VOTE_OPTION_YES": 1, + "VOTE_OPTION_ABSTAIN": 2, + "VOTE_OPTION_NO": 3, + "VOTE_OPTION_NO_WITH_VETO": 4, +} + +func (x VoteOption) String() string { + return proto.EnumName(VoteOption_name, int32(x)) +} + +func (VoteOption) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{0} +} + +// ProposalStatus enumerates the valid statuses of a proposal. +type ProposalStatus int32 + +const ( + // PROPOSAL_STATUS_UNSPECIFIED defines the default propopsal status. + StatusNil ProposalStatus = 0 + // PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + // period. + StatusDepositPeriod ProposalStatus = 1 + // PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + // period. + StatusVotingPeriod ProposalStatus = 2 + // PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + // passed. + StatusPassed ProposalStatus = 3 + // PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + // been rejected. + StatusRejected ProposalStatus = 4 + // PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + // failed. + StatusFailed ProposalStatus = 5 +) + +var ProposalStatus_name = map[int32]string{ + 0: "PROPOSAL_STATUS_UNSPECIFIED", + 1: "PROPOSAL_STATUS_DEPOSIT_PERIOD", + 2: "PROPOSAL_STATUS_VOTING_PERIOD", + 3: "PROPOSAL_STATUS_PASSED", + 4: "PROPOSAL_STATUS_REJECTED", + 5: "PROPOSAL_STATUS_FAILED", +} + +var ProposalStatus_value = map[string]int32{ + "PROPOSAL_STATUS_UNSPECIFIED": 0, + "PROPOSAL_STATUS_DEPOSIT_PERIOD": 1, + "PROPOSAL_STATUS_VOTING_PERIOD": 2, + "PROPOSAL_STATUS_PASSED": 3, + "PROPOSAL_STATUS_REJECTED": 4, + "PROPOSAL_STATUS_FAILED": 5, +} + +func (x ProposalStatus) String() string { + return proto.EnumName(ProposalStatus_name, int32(x)) +} + +func (ProposalStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{1} +} + +// TextProposal defines a standard text proposal whose changes need to be +// manually updated in case of approval. +type TextProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` +} + +func (m *TextProposal) Reset() { *m = TextProposal{} } +func (*TextProposal) ProtoMessage() {} +func (*TextProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{0} +} +func (m *TextProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TextProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TextProposal.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 *TextProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_TextProposal.Merge(m, src) +} +func (m *TextProposal) XXX_Size() int { + return m.Size() +} +func (m *TextProposal) XXX_DiscardUnknown() { + xxx_messageInfo_TextProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_TextProposal proto.InternalMessageInfo + +// Deposit defines an amount deposited by an account address to an active +// proposal. +type Deposit struct { + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty" yaml:"proposal_id"` + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *Deposit) Reset() { *m = Deposit{} } +func (*Deposit) ProtoMessage() {} +func (*Deposit) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{1} +} +func (m *Deposit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Deposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Deposit.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 *Deposit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Deposit.Merge(m, src) +} +func (m *Deposit) XXX_Size() int { + return m.Size() +} +func (m *Deposit) XXX_DiscardUnknown() { + xxx_messageInfo_Deposit.DiscardUnknown(m) +} + +var xxx_messageInfo_Deposit proto.InternalMessageInfo + +// Proposal defines the core field members of a governance proposal. +type Proposal struct { + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"id" yaml:"id"` + Content *types1.Any `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + Status ProposalStatus `protobuf:"varint,3,opt,name=status,proto3,enum=cosmos.gov.v1beta1.ProposalStatus" json:"status,omitempty" yaml:"proposal_status"` + FinalTallyResult TallyResult `protobuf:"bytes,4,opt,name=final_tally_result,json=finalTallyResult,proto3" json:"final_tally_result" yaml:"final_tally_result"` + SubmitTime time.Time `protobuf:"bytes,5,opt,name=submit_time,json=submitTime,proto3,stdtime" json:"submit_time" yaml:"submit_time"` + DepositEndTime time.Time `protobuf:"bytes,6,opt,name=deposit_end_time,json=depositEndTime,proto3,stdtime" json:"deposit_end_time" yaml:"deposit_end_time"` + TotalDeposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,7,rep,name=total_deposit,json=totalDeposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"total_deposit" yaml:"total_deposit"` + VotingStartTime time.Time `protobuf:"bytes,8,opt,name=voting_start_time,json=votingStartTime,proto3,stdtime" json:"voting_start_time" yaml:"voting_start_time"` + VotingEndTime time.Time `protobuf:"bytes,9,opt,name=voting_end_time,json=votingEndTime,proto3,stdtime" json:"voting_end_time" yaml:"voting_end_time"` +} + +func (m *Proposal) Reset() { *m = Proposal{} } +func (*Proposal) ProtoMessage() {} +func (*Proposal) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{2} +} +func (m *Proposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Proposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Proposal.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 *Proposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_Proposal.Merge(m, src) +} +func (m *Proposal) XXX_Size() int { + return m.Size() +} +func (m *Proposal) XXX_DiscardUnknown() { + xxx_messageInfo_Proposal.DiscardUnknown(m) +} + +var xxx_messageInfo_Proposal proto.InternalMessageInfo + +// TallyResult defines a standard tally for a governance proposal. +type TallyResult struct { + Yes github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=yes,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"yes"` + Abstain github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=abstain,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"abstain"` + No github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=no,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"no"` + NoWithVeto github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=no_with_veto,json=noWithVeto,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"no_with_veto" yaml:"no_with_veto"` +} + +func (m *TallyResult) Reset() { *m = TallyResult{} } +func (*TallyResult) ProtoMessage() {} +func (*TallyResult) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{3} +} +func (m *TallyResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TallyResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TallyResult.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 *TallyResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_TallyResult.Merge(m, src) +} +func (m *TallyResult) XXX_Size() int { + return m.Size() +} +func (m *TallyResult) XXX_DiscardUnknown() { + xxx_messageInfo_TallyResult.DiscardUnknown(m) +} + +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"` +} + +func (m *Vote) Reset() { *m = Vote{} } +func (*Vote) ProtoMessage() {} +func (*Vote) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{4} +} +func (m *Vote) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Vote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Vote.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 *Vote) XXX_Merge(src proto.Message) { + xxx_messageInfo_Vote.Merge(m, src) +} +func (m *Vote) XXX_Size() int { + return m.Size() +} +func (m *Vote) XXX_DiscardUnknown() { + xxx_messageInfo_Vote.DiscardUnknown(m) +} + +var xxx_messageInfo_Vote proto.InternalMessageInfo + +// DepositParams defines the params for deposits on governance proposals. +type DepositParams struct { + // Minimum deposit for a proposal to enter voting period. + MinDeposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=min_deposit,json=minDeposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"min_deposit,omitempty" yaml:"min_deposit"` + // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + // months. + MaxDepositPeriod time.Duration `protobuf:"bytes,2,opt,name=max_deposit_period,json=maxDepositPeriod,proto3,stdduration" json:"max_deposit_period,omitempty" yaml:"max_deposit_period"` +} + +func (m *DepositParams) Reset() { *m = DepositParams{} } +func (*DepositParams) ProtoMessage() {} +func (*DepositParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{5} +} +func (m *DepositParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DepositParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DepositParams.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 *DepositParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_DepositParams.Merge(m, src) +} +func (m *DepositParams) XXX_Size() int { + return m.Size() +} +func (m *DepositParams) XXX_DiscardUnknown() { + xxx_messageInfo_DepositParams.DiscardUnknown(m) +} + +var xxx_messageInfo_DepositParams proto.InternalMessageInfo + +// VotingParams defines the params for voting on governance proposals. +type VotingParams struct { + // Length of the voting period. + VotingPeriod time.Duration `protobuf:"bytes,1,opt,name=voting_period,json=votingPeriod,proto3,stdduration" json:"voting_period,omitempty" yaml:"voting_period"` +} + +func (m *VotingParams) Reset() { *m = VotingParams{} } +func (*VotingParams) ProtoMessage() {} +func (*VotingParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{6} +} +func (m *VotingParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VotingParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VotingParams.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 *VotingParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_VotingParams.Merge(m, src) +} +func (m *VotingParams) XXX_Size() int { + return m.Size() +} +func (m *VotingParams) XXX_DiscardUnknown() { + xxx_messageInfo_VotingParams.DiscardUnknown(m) +} + +var xxx_messageInfo_VotingParams proto.InternalMessageInfo + +// TallyParams defines the params for tallying votes on governance proposals. +type TallyParams struct { + // Minimum percentage of total stake needed to vote for a result to be + // considered valid. + Quorum github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=quorum,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"quorum,omitempty"` + // Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + Threshold github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=threshold,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"threshold,omitempty"` + // Minimum value of Veto votes to Total votes ratio for proposal to be + // vetoed. Default value: 1/3. + VetoThreshold github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=veto_threshold,json=vetoThreshold,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"veto_threshold,omitempty" yaml:"veto_threshold"` +} + +func (m *TallyParams) Reset() { *m = TallyParams{} } +func (*TallyParams) ProtoMessage() {} +func (*TallyParams) Descriptor() ([]byte, []int) { + return fileDescriptor_6e82113c1a9a4b7c, []int{7} +} +func (m *TallyParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TallyParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TallyParams.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 *TallyParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_TallyParams.Merge(m, src) +} +func (m *TallyParams) XXX_Size() int { + return m.Size() +} +func (m *TallyParams) XXX_DiscardUnknown() { + xxx_messageInfo_TallyParams.DiscardUnknown(m) +} + +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((*TextProposal)(nil), "cosmos.gov.v1beta1.TextProposal") + proto.RegisterType((*Deposit)(nil), "cosmos.gov.v1beta1.Deposit") + proto.RegisterType((*Proposal)(nil), "cosmos.gov.v1beta1.Proposal") + proto.RegisterType((*TallyResult)(nil), "cosmos.gov.v1beta1.TallyResult") + proto.RegisterType((*Vote)(nil), "cosmos.gov.v1beta1.Vote") + proto.RegisterType((*DepositParams)(nil), "cosmos.gov.v1beta1.DepositParams") + proto.RegisterType((*VotingParams)(nil), "cosmos.gov.v1beta1.VotingParams") + proto.RegisterType((*TallyParams)(nil), "cosmos.gov.v1beta1.TallyParams") +} + +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, +} + +func (this *TextProposal) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*TextProposal) + if !ok { + that2, ok := that.(TextProposal) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + return true +} +func (this *Proposal) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Proposal) + if !ok { + that2, ok := that.(Proposal) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ProposalId != that1.ProposalId { + return false + } + if !this.Content.Equal(that1.Content) { + return false + } + if this.Status != that1.Status { + return false + } + if !this.FinalTallyResult.Equal(&that1.FinalTallyResult) { + return false + } + if !this.SubmitTime.Equal(that1.SubmitTime) { + return false + } + if !this.DepositEndTime.Equal(that1.DepositEndTime) { + return false + } + if len(this.TotalDeposit) != len(that1.TotalDeposit) { + return false + } + for i := range this.TotalDeposit { + if !this.TotalDeposit[i].Equal(&that1.TotalDeposit[i]) { + return false + } + } + if !this.VotingStartTime.Equal(that1.VotingStartTime) { + return false + } + if !this.VotingEndTime.Equal(that1.VotingEndTime) { + return false + } + return true +} +func (this *TallyResult) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*TallyResult) + if !ok { + that2, ok := that.(TallyResult) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Yes.Equal(that1.Yes) { + return false + } + if !this.Abstain.Equal(that1.Abstain) { + return false + } + if !this.No.Equal(that1.No) { + return false + } + if !this.NoWithVeto.Equal(that1.NoWithVeto) { + return false + } + return true +} +func (m *TextProposal) 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 *TextProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TextProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintGov(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintGov(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Deposit) 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 *Deposit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Deposit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintGov(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Proposal) 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 *Proposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.VotingEndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingEndTime):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintGov(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x4a + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.VotingStartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingStartTime):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintGov(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x42 + if len(m.TotalDeposit) > 0 { + for iNdEx := len(m.TotalDeposit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.TotalDeposit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.DepositEndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.DepositEndTime):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintGov(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x32 + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SubmitTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintGov(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x2a + { + size, err := m.FinalTallyResult.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.Status != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x18 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TallyResult) 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 *TallyResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TallyResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.NoWithVeto.Size() + i -= size + if _, err := m.NoWithVeto.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.No.Size() + i -= size + if _, err := m.No.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.Abstain.Size() + i -= size + if _, err := m.Abstain.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.Yes.Size() + i -= size + if _, err := m.Yes.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Vote) 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 *Vote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Option != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.Option)) + i-- + dAtA[i] = 0x18 + } + if len(m.Voter) > 0 { + i -= len(m.Voter) + copy(dAtA[i:], m.Voter) + i = encodeVarintGov(dAtA, i, uint64(len(m.Voter))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintGov(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DepositParams) 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 *DepositParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DepositParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n7, err7 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MaxDepositPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxDepositPeriod):]) + if err7 != nil { + return 0, err7 + } + i -= n7 + i = encodeVarintGov(dAtA, i, uint64(n7)) + i-- + dAtA[i] = 0x12 + if len(m.MinDeposit) > 0 { + for iNdEx := len(m.MinDeposit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MinDeposit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *VotingParams) 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 *VotingParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VotingParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n8, err8 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.VotingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.VotingPeriod):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintGov(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *TallyParams) 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 *TallyParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TallyParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.VetoThreshold.Size() + i -= size + if _, err := m.VetoThreshold.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.Threshold.Size() + i -= size + if _, err := m.Threshold.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.Quorum.Size() + i -= size + if _, err := m.Quorum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGov(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGov(dAtA []byte, offset int, v uint64) int { + offset -= sovGov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TextProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + return n +} + +func (m *Deposit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovGov(uint64(m.ProposalId)) + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + return n +} + +func (m *Proposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovGov(uint64(m.ProposalId)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovGov(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovGov(uint64(m.Status)) + } + l = m.FinalTallyResult.Size() + n += 1 + l + sovGov(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime) + n += 1 + l + sovGov(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.DepositEndTime) + n += 1 + l + sovGov(uint64(l)) + if len(m.TotalDeposit) > 0 { + for _, e := range m.TotalDeposit { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingStartTime) + n += 1 + l + sovGov(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingEndTime) + n += 1 + l + sovGov(uint64(l)) + return n +} + +func (m *TallyResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Yes.Size() + n += 1 + l + sovGov(uint64(l)) + l = m.Abstain.Size() + n += 1 + l + sovGov(uint64(l)) + l = m.No.Size() + n += 1 + l + sovGov(uint64(l)) + l = m.NoWithVeto.Size() + n += 1 + l + sovGov(uint64(l)) + return n +} + +func (m *Vote) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovGov(uint64(m.ProposalId)) + } + l = len(m.Voter) + if l > 0 { + n += 1 + l + sovGov(uint64(l)) + } + if m.Option != 0 { + n += 1 + sovGov(uint64(m.Option)) + } + return n +} + +func (m *DepositParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.MinDeposit) > 0 { + for _, e := range m.MinDeposit { + l = e.Size() + n += 1 + l + sovGov(uint64(l)) + } + } + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxDepositPeriod) + n += 1 + l + sovGov(uint64(l)) + return n +} + +func (m *VotingParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.VotingPeriod) + n += 1 + l + sovGov(uint64(l)) + return n +} + +func (m *TallyParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Quorum.Size() + n += 1 + l + sovGov(uint64(l)) + l = m.Threshold.Size() + n += 1 + l + sovGov(uint64(l)) + l = m.VetoThreshold.Size() + n += 1 + l + sovGov(uint64(l)) + return n +} + +func sovGov(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGov(x uint64) (n int) { + return sovGov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *TextProposal) 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: TextProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TextProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", 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 + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", 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 + } + m.Description = string(dAtA[iNdEx:postIndex]) + 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 *Deposit) 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: Deposit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Deposit: 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 ErrIntOverflowGov + } + 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 Depositor", 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 + } + m.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-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) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGov + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Proposal) 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: Proposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Proposal: 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 ErrIntOverflowGov + } + 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 Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if m.Content == nil { + m.Content = &types1.Any{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= ProposalStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalTallyResult", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := m.FinalTallyResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubmitTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.SubmitTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DepositEndTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.DepositEndTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalDeposit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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.TotalDeposit = append(m.TotalDeposit, types.Coin{}) + if err := m.TotalDeposit[len(m.TotalDeposit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingStartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.VotingStartTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingEndTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.VotingEndTime, 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 *TallyResult) 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: TallyResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TallyResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Yes", 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.Yes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Abstain", 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.Abstain.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field No", 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.No.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NoWithVeto", 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.NoWithVeto.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 *Vote) 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: Vote: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Vote: 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 ErrIntOverflowGov + } + 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 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 + } + m.Voter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + 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 + } + } + 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 *DepositParams) 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: DepositParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DepositParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinDeposit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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.MinDeposit = append(m.MinDeposit, types.Coin{}) + if err := m.MinDeposit[len(m.MinDeposit)-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 MaxDepositPeriod", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.MaxDepositPeriod, 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 *VotingParams) 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: VotingParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VotingParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPeriod", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + 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 + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.VotingPeriod, 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 *TallyParams) 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: TallyParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TallyParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Quorum", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Quorum.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Threshold", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Threshold.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VetoThreshold", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGov + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGov + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGov + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.VetoThreshold.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 skipGov(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, ErrIntOverflowGov + } + 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, ErrIntOverflowGov + } + 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, ErrIntOverflowGov + } + 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, ErrInvalidLengthGov + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGov + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGov + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGov = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGov = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGov = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/gov/types/keys.go b/x/gov/types/keys.go index 98f6f0d88c14..7681116fc46a 100644 --- a/x/gov/types/keys.go +++ b/x/gov/types/keys.go @@ -20,9 +20,6 @@ const ( // QuerierRoute is the querier route for gov QuerierRoute = ModuleName - - // DefaultParamspace default name for parameter store - DefaultParamspace = ModuleName ) // Keys for governance store diff --git a/x/gov/types/keys_test.go b/x/gov/types/keys_test.go index 80d77c66043a..80dfa7d207a3 100644 --- a/x/gov/types/keys_test.go +++ b/x/gov/types/keys_test.go @@ -5,8 +5,8 @@ import ( "time" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/gov/types/msgs.go b/x/gov/types/msgs.go index 882e0c560c31..e97c25ce8f01 100644 --- a/x/gov/types/msgs.go +++ b/x/gov/types/msgs.go @@ -3,6 +3,11 @@ package types import ( "fmt" + yaml "gopkg.in/yaml.v2" + + "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" ) @@ -14,77 +19,121 @@ const ( TypeMsgSubmitProposal = "submit_proposal" ) -var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{} +var ( + _, _, _ sdk.Msg = &MsgSubmitProposal{}, &MsgDeposit{}, &MsgVote{} + _ types.UnpackInterfacesMessage = &MsgSubmitProposal{} +) + +// NewMsgSubmitProposal creates a new MsgSubmitProposal. +//nolint:interfacer +func NewMsgSubmitProposal(content Content, initialDeposit sdk.Coins, proposer sdk.AccAddress) (*MsgSubmitProposal, error) { + m := &MsgSubmitProposal{ + InitialDeposit: initialDeposit, + Proposer: proposer.String(), + } + err := m.SetContent(content) + if err != nil { + return nil, err + } + return m, nil +} + +func (m *MsgSubmitProposal) GetInitialDeposit() sdk.Coins { return m.InitialDeposit } -// MsgSubmitProposal defines a message to create a governance proposal with a -// given content and initial deposit -type MsgSubmitProposal struct { - Content Content `json:"content" yaml:"content"` - InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive - Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` // Address of the proposer +func (m *MsgSubmitProposal) GetProposer() sdk.AccAddress { + proposer, _ := sdk.AccAddressFromBech32(m.Proposer) + return proposer } -// NewMsgSubmitProposal creates a new MsgSubmitProposal instance -func NewMsgSubmitProposal(content Content, initialDeposit sdk.Coins, proposer sdk.AccAddress) MsgSubmitProposal { - return MsgSubmitProposal{content, initialDeposit, proposer} +func (m *MsgSubmitProposal) GetContent() Content { + content, ok := m.Content.GetCachedValue().(Content) + if !ok { + return nil + } + return content +} + +func (m *MsgSubmitProposal) SetInitialDeposit(coins sdk.Coins) { + m.InitialDeposit = coins +} + +func (m *MsgSubmitProposal) SetProposer(address fmt.Stringer) { + m.Proposer = address.String() +} + +func (m *MsgSubmitProposal) SetContent(content Content) error { + msg, ok := content.(proto.Message) + if !ok { + return fmt.Errorf("can't proto marshal %T", msg) + } + any, err := types.NewAnyWithValue(msg) + if err != nil { + return err + } + m.Content = any + return nil } // Route implements Msg -func (msg MsgSubmitProposal) Route() string { return RouterKey } +func (m MsgSubmitProposal) Route() string { return RouterKey } // Type implements Msg -func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } +func (m MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } // ValidateBasic implements Msg -func (msg MsgSubmitProposal) ValidateBasic() error { - if msg.Content == nil { - return sdkerrors.Wrap(ErrInvalidProposalContent, "missing content") +func (m MsgSubmitProposal) ValidateBasic() error { + if m.Proposer == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Proposer) + } + if !m.InitialDeposit.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String()) } - if msg.Proposer.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Proposer.String()) + if m.InitialDeposit.IsAnyNegative() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.InitialDeposit.String()) } - if !msg.InitialDeposit.IsValid() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.InitialDeposit.String()) + + content := m.GetContent() + if content == nil { + return sdkerrors.Wrap(ErrInvalidProposalContent, "missing content") } - if msg.InitialDeposit.IsAnyNegative() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.InitialDeposit.String()) + if !IsValidProposalType(content.ProposalType()) { + return sdkerrors.Wrap(ErrInvalidProposalType, content.ProposalType()) } - if !IsValidProposalType(msg.Content.ProposalType()) { - return sdkerrors.Wrap(ErrInvalidProposalType, msg.Content.ProposalType()) + if err := content.ValidateBasic(); err != nil { + return err } - return msg.Content.ValidateBasic() -} - -// String implements the Stringer interface -func (msg MsgSubmitProposal) String() string { - return fmt.Sprintf(`Submit Proposal Message: - Content: %s - Initial Deposit: %s -`, msg.Content.String(), msg.InitialDeposit) + return nil } // GetSignBytes implements Msg -func (msg MsgSubmitProposal) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) +func (m MsgSubmitProposal) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&m) return sdk.MustSortJSON(bz) } // GetSigners implements Msg -func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Proposer} +func (m MsgSubmitProposal) GetSigners() []sdk.AccAddress { + proposer, _ := sdk.AccAddressFromBech32(m.Proposer) + return []sdk.AccAddress{proposer} } -// MsgDeposit defines a message to submit a deposit to an existing proposal -type MsgDeposit struct { - ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal - Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor - Amount sdk.Coins `json:"amount" yaml:"amount"` // Coins to add to the proposal's deposit +// String implements the Stringer interface +func (m MsgSubmitProposal) String() string { + out, _ := yaml.Marshal(m) + return string(out) +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (m MsgSubmitProposal) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var content Content + return unpacker.UnpackAny(m.Content, &content) } // NewMsgDeposit creates a new MsgDeposit instance -func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins) MsgDeposit { - return MsgDeposit{proposalID, depositor, amount} +//nolint:interfacer +func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins) *MsgDeposit { + return &MsgDeposit{proposalID, depositor.String(), amount} } // Route implements Msg @@ -95,8 +144,8 @@ func (msg MsgDeposit) Type() string { return TypeMsgDeposit } // ValidateBasic implements Msg func (msg MsgDeposit) ValidateBasic() error { - if msg.Depositor.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Depositor.String()) + if msg.Depositor == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Depositor) } if !msg.Amount.IsValid() { return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) @@ -110,34 +159,26 @@ func (msg MsgDeposit) ValidateBasic() error { // String implements the Stringer interface func (msg MsgDeposit) String() string { - return fmt.Sprintf(`Deposit Message: - Depositer: %s - Proposal ID: %d - Amount: %s -`, msg.Depositor, msg.ProposalID, msg.Amount) + out, _ := yaml.Marshal(msg) + return string(out) } // GetSignBytes implements Msg func (msg MsgDeposit) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // GetSigners implements Msg func (msg MsgDeposit) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Depositor} -} - -// MsgVote defines a message to cast a vote -type MsgVote struct { - ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal - Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter - Option VoteOption `json:"option" yaml:"option"` // option from OptionSet chosen by the voter + depositor, _ := sdk.AccAddressFromBech32(msg.Depositor) + return []sdk.AccAddress{depositor} } // NewMsgVote creates a message to cast a vote on an active proposal -func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) MsgVote { - return MsgVote{proposalID, voter, option} +//nolint:interfacer +func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) *MsgVote { + return &MsgVote{proposalID, voter.String(), option} } // Route implements Msg @@ -148,8 +189,8 @@ func (msg MsgVote) Type() string { return TypeMsgVote } // ValidateBasic implements Msg func (msg MsgVote) ValidateBasic() error { - if msg.Voter.Empty() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter.String()) + if msg.Voter == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, msg.Voter) } if !ValidVoteOption(msg.Option) { return sdkerrors.Wrap(ErrInvalidVote, msg.Option.String()) @@ -160,19 +201,18 @@ func (msg MsgVote) ValidateBasic() error { // String implements the Stringer interface func (msg MsgVote) String() string { - return fmt.Sprintf(`Vote Message: - Proposal ID: %d - Option: %s -`, msg.ProposalID, msg.Option) + out, _ := yaml.Marshal(msg) + return string(out) } // GetSignBytes implements Msg func (msg MsgVote) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // GetSigners implements Msg func (msg MsgVote) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Voter} + 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 8467e4861d94..9d7d6a36828a 100644 --- a/x/gov/types/msgs_test.go +++ b/x/gov/types/msgs_test.go @@ -43,12 +43,14 @@ func TestMsgSubmitProposal(t *testing.T) { } for i, tc := range tests { - msg := NewMsgSubmitProposal( + msg, err := NewMsgSubmitProposal( ContentFromProposalType(tc.title, tc.description, tc.proposalType), tc.initialDeposit, tc.proposerAddr, ) + require.NoError(t, err) + if tc.expectPass { require.NoError(t, msg.ValidateBasic(), "test: %v", i) } else { @@ -62,7 +64,7 @@ func TestMsgDepositGetSignBytes(t *testing.T) { msg := NewMsgDeposit(addr, 0, coinsPos) res := msg.GetSignBytes() - expected := `{"type":"cosmos-sdk/MsgDeposit","value":{"amount":[{"amount":"1000","denom":"stake"}],"depositor":"fetch1v9jxgu33pwr8tz","proposal_id":"0"}}` + expected := `{"type":"cosmos-sdk/MsgDeposit","value":{"amount":[{"amount":"1000","denom":"stake"}],"depositor":"cosmos1v9jxgu33kfsgr5","proposal_id":"0"}}` require.Equal(t, expected, string(res)) } @@ -115,3 +117,16 @@ func TestMsgVote(t *testing.T) { } } } + +// 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{}) + require.NoError(t, err) + var bz []byte + require.NotPanics(t, func() { + bz = msg.GetSignBytes() + }) + require.Equal(t, + `{"type":"cosmos-sdk/MsgSubmitProposal","value":{"content":{"type":"cosmos-sdk/TextProposal","value":{"description":"abcd","title":"test"}},"initial_deposit":[]}}`, + string(bz)) +} diff --git a/x/gov/types/params.go b/x/gov/types/params.go index ff671d493169..5421136f390f 100644 --- a/x/gov/types/params.go +++ b/x/gov/types/params.go @@ -4,8 +4,10 @@ import ( "fmt" "time" + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" - params "github.com/cosmos/cosmos-sdk/x/params/subspace" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) // Default period for deposits & voting @@ -18,7 +20,7 @@ var ( DefaultMinDepositTokens = sdk.TokensFromConsensusPower(10) DefaultQuorum = sdk.NewDecWithPrec(334, 3) DefaultThreshold = sdk.NewDecWithPrec(5, 1) - DefaultVeto = sdk.NewDecWithPrec(334, 3) + DefaultVetoThreshold = sdk.NewDecWithPrec(334, 3) ) // Parameter store key @@ -29,20 +31,14 @@ var ( ) // ParamKeyTable - Key declaration for parameters -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable( - params.NewParamSetPair(ParamStoreKeyDepositParams, DepositParams{}, validateDepositParams), - params.NewParamSetPair(ParamStoreKeyVotingParams, VotingParams{}, validateVotingParams), - params.NewParamSetPair(ParamStoreKeyTallyParams, TallyParams{}, validateTallyParams), +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable( + paramtypes.NewParamSetPair(ParamStoreKeyDepositParams, DepositParams{}, validateDepositParams), + paramtypes.NewParamSetPair(ParamStoreKeyVotingParams, VotingParams{}, validateVotingParams), + paramtypes.NewParamSetPair(ParamStoreKeyTallyParams, TallyParams{}, validateTallyParams), ) } -// DepositParams defines the params around deposits for governance -type DepositParams struct { - MinDeposit sdk.Coins `json:"min_deposit,omitempty" yaml:"min_deposit,omitempty"` // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty" yaml:"max_deposit_period,omitempty"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months -} - // NewDepositParams creates a new DepositParams object func NewDepositParams(minDeposit sdk.Coins, maxDepositPeriod time.Duration) DepositParams { return DepositParams{ @@ -61,9 +57,8 @@ func DefaultDepositParams() DepositParams { // String implements stringer insterface func (dp DepositParams) String() string { - return fmt.Sprintf(`Deposit Params: - Min Deposit: %s - Max Deposit Period: %s`, dp.MinDeposit, dp.MaxDepositPeriod) + out, _ := yaml.Marshal(dp) + return string(out) } // Equal checks equality of DepositParams @@ -87,34 +82,29 @@ func validateDepositParams(i interface{}) error { return nil } -// TallyParams defines the params around Tallying votes in governance -type TallyParams struct { - Quorum sdk.Dec `json:"quorum,omitempty" yaml:"quorum,omitempty"` // Minimum percentage of total stake needed to vote for a result to be considered valid - Threshold sdk.Dec `json:"threshold,omitempty" yaml:"threshold,omitempty"` // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5 - Veto sdk.Dec `json:"veto,omitempty" yaml:"veto,omitempty"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 -} - // NewTallyParams creates a new TallyParams object -func NewTallyParams(quorum, threshold, veto sdk.Dec) TallyParams { +func NewTallyParams(quorum, threshold, vetoThreshold sdk.Dec) TallyParams { return TallyParams{ - Quorum: quorum, - Threshold: threshold, - Veto: veto, + Quorum: quorum, + Threshold: threshold, + VetoThreshold: vetoThreshold, } } // DefaultTallyParams default parameters for tallying func DefaultTallyParams() TallyParams { - return NewTallyParams(DefaultQuorum, DefaultThreshold, DefaultVeto) + return NewTallyParams(DefaultQuorum, DefaultThreshold, DefaultVetoThreshold) +} + +// Equal checks equality of TallyParams +func (tp TallyParams) Equal(other TallyParams) bool { + return tp.Quorum.Equal(other.Quorum) && tp.Threshold.Equal(other.Threshold) && tp.VetoThreshold.Equal(other.VetoThreshold) } // String implements stringer insterface func (tp TallyParams) String() string { - return fmt.Sprintf(`Tally Params: - Quorum: %s - Threshold: %s - Veto: %s`, - tp.Quorum, tp.Threshold, tp.Veto) + out, _ := yaml.Marshal(tp) + return string(out) } func validateTallyParams(i interface{}) error { @@ -135,21 +125,16 @@ func validateTallyParams(i interface{}) error { if v.Threshold.GT(sdk.OneDec()) { return fmt.Errorf("vote threshold too large: %s", v) } - if !v.Veto.IsPositive() { + if !v.VetoThreshold.IsPositive() { return fmt.Errorf("veto threshold must be positive: %s", v.Threshold) } - if v.Veto.GT(sdk.OneDec()) { + if v.VetoThreshold.GT(sdk.OneDec()) { return fmt.Errorf("veto threshold too large: %s", v) } return nil } -// VotingParams defines the params around Voting in governance -type VotingParams struct { - VotingPeriod time.Duration `json:"voting_period,omitempty" yaml:"voting_period,omitempty"` // Length of the voting period. -} - // NewVotingParams creates a new VotingParams object func NewVotingParams(votingPeriod time.Duration) VotingParams { return VotingParams{ @@ -162,10 +147,15 @@ func DefaultVotingParams() VotingParams { return NewVotingParams(DefaultPeriod) } +// Equal checks equality of TallyParams +func (vp VotingParams) Equal(other VotingParams) bool { + return vp.VotingPeriod == other.VotingPeriod +} + // String implements stringer interface func (vp VotingParams) String() string { - return fmt.Sprintf(`Voting Params: - Voting Period: %s`, vp.VotingPeriod) + out, _ := yaml.Marshal(vp) + return string(out) } func validateVotingParams(i interface{}) error { @@ -185,7 +175,7 @@ func validateVotingParams(i interface{}) error { type Params struct { VotingParams VotingParams `json:"voting_params" yaml:"voting_params"` TallyParams TallyParams `json:"tally_params" yaml:"tally_params"` - DepositParams DepositParams `json:"deposit_params" yaml:"deposit_parmas"` + DepositParams DepositParams `json:"deposit_params" yaml:"deposit_params"` } func (gp Params) String() string { diff --git a/x/gov/types/proposal.go b/x/gov/types/proposal.go index 945cf786687b..0c856647f64b 100644 --- a/x/gov/types/proposal.go +++ b/x/gov/types/proposal.go @@ -1,11 +1,14 @@ package types import ( - "encoding/json" "fmt" "strings" "time" + "github.com/gogo/protobuf/proto" + yaml "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -13,110 +16,131 @@ import ( // DefaultStartingProposalID is 1 const DefaultStartingProposalID uint64 = 1 -// Proposal defines a struct used by the governance module to allow for voting -// on network changes. -type Proposal struct { - Content `json:"content" yaml:"content"` // Proposal content interface - - ProposalID uint64 `json:"id" yaml:"id"` // ID of the proposal - Status ProposalStatus `json:"proposal_status" yaml:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected} - FinalTallyResult TallyResult `json:"final_tally_result" yaml:"final_tally_result"` // Result of Tallys - - SubmitTime time.Time `json:"submit_time" yaml:"submit_time"` // Time of the block where TxGovSubmitProposal was included - DepositEndTime time.Time `json:"deposit_end_time" yaml:"deposit_end_time"` // Time that the Proposal would expire if deposit amount isn't met - TotalDeposit sdk.Coins `json:"total_deposit" yaml:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit - - VotingStartTime time.Time `json:"voting_start_time" yaml:"voting_start_time"` // Time of the block where MinDeposit was reached. -1 if MinDeposit is not reached - VotingEndTime time.Time `json:"voting_end_time" yaml:"voting_end_time"` // Time that the VotingPeriod for this proposal will end and votes will be tallied -} - // NewProposal creates a new Proposal instance -func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Time) Proposal { - return Proposal{ - Content: content, - ProposalID: id, +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) + } + + any, err := types.NewAnyWithValue(msg) + if err != nil { + return Proposal{}, err + } + + p.Content = any + + return p, nil } // String implements stringer interface func (p Proposal) String() string { - return fmt.Sprintf(`Proposal %d: - Title: %s - Type: %s - Status: %s - Submit Time: %s - Deposit End Time: %s - Total Deposit: %s - Voting Start Time: %s - Voting End Time: %s - Description: %s`, - p.ProposalID, p.GetTitle(), p.ProposalType(), - p.Status, p.SubmitTime, p.DepositEndTime, - p.TotalDeposit, p.VotingStartTime, p.VotingEndTime, p.GetDescription(), - ) + out, _ := yaml.Marshal(p) + return string(out) +} + +// GetContent returns the proposal Content +func (p Proposal) GetContent() Content { + content, ok := p.Content.GetCachedValue().(Content) + if !ok { + return nil + } + return content +} + +func (p Proposal) ProposalType() string { + content := p.GetContent() + if content == nil { + return "" + } + return content.ProposalType() +} + +func (p Proposal) ProposalRoute() string { + content := p.GetContent() + if content == nil { + return "" + } + return content.ProposalRoute() +} + +func (p Proposal) GetTitle() string { + content := p.GetContent() + if content == nil { + return "" + } + return content.GetTitle() +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (p Proposal) UnpackInterfaces(unpacker types.AnyUnpacker) error { + var content Content + return unpacker.UnpackAny(p.Content, &content) } // Proposals is an array of proposal type Proposals []Proposal +var _ types.UnpackInterfacesMessage = Proposals{} + +// Equal returns true if two slices (order-dependant) of proposals are equal. +func (p Proposals) Equal(other Proposals) bool { + if len(p) != len(other) { + return false + } + + for i, proposal := range p { + if !proposal.Equal(other[i]) { + return false + } + } + + return true +} + // String implements stringer interface func (p Proposals) String() string { out := "ID - (Status) [Type] Title\n" for _, prop := range p { out += fmt.Sprintf("%d - (%s) [%s] %s\n", - prop.ProposalID, prop.Status, + prop.ProposalId, prop.Status, prop.ProposalType(), prop.GetTitle()) } return strings.TrimSpace(out) } +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (p Proposals) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, x := range p { + err := x.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} + type ( // ProposalQueue defines a queue for proposal ids ProposalQueue []uint64 - - // ProposalStatus is a type alias that represents a proposal status as a byte - ProposalStatus byte -) - -// Valid Proposal statuses -const ( - StatusNil ProposalStatus = 0x00 - StatusDepositPeriod ProposalStatus = 0x01 - StatusVotingPeriod ProposalStatus = 0x02 - StatusPassed ProposalStatus = 0x03 - StatusRejected ProposalStatus = 0x04 - StatusFailed ProposalStatus = 0x05 ) // ProposalStatusFromString turns a string into a ProposalStatus func ProposalStatusFromString(str string) (ProposalStatus, error) { - switch str { - case "DepositPeriod": - return StatusDepositPeriod, nil - - case "VotingPeriod": - return StatusVotingPeriod, nil - - case "Passed": - return StatusPassed, nil - - case "Rejected": - return StatusRejected, nil - - case "Failed": - return StatusFailed, nil - - case "": - return StatusNil, nil - - default: - return ProposalStatus(0xff), fmt.Errorf("'%s' is not a valid proposal status", str) + num, ok := ProposalStatus_value[str] + if !ok { + return StatusNil, fmt.Errorf("'%s' is not a valid proposal status", str) } + return ProposalStatus(num), nil } // ValidProposalStatus returns true if the proposal status is valid and false @@ -143,51 +167,6 @@ func (status *ProposalStatus) Unmarshal(data []byte) error { return nil } -// MarshalJSON Marshals to JSON using string representation of the status -func (status ProposalStatus) MarshalJSON() ([]byte, error) { - return json.Marshal(status.String()) -} - -// UnmarshalJSON Unmarshals from JSON assuming Bech32 encoding -func (status *ProposalStatus) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - bz2, err := ProposalStatusFromString(s) - if err != nil { - return err - } - - *status = bz2 - return nil -} - -// String implements the Stringer interface. -func (status ProposalStatus) String() string { - switch status { - case StatusDepositPeriod: - return "DepositPeriod" - - case StatusVotingPeriod: - return "VotingPeriod" - - case StatusPassed: - return "Passed" - - case StatusRejected: - return "Rejected" - - case StatusFailed: - return "Failed" - - default: - return "" - } -} - // Format implements the fmt.Formatter interface. // nolint: errcheck func (status ProposalStatus) Format(s fmt.State, verb rune) { @@ -205,42 +184,33 @@ const ( ProposalTypeText string = "Text" ) -// TextProposal defines a standard text proposal whose changes need to be -// manually updated in case of approval -type TextProposal struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` -} +// Implements Content Interface +var _ Content = &TextProposal{} // NewTextProposal creates a text proposal Content func NewTextProposal(title, description string) Content { - return TextProposal{title, description} + return &TextProposal{title, description} } -// Implements Content Interface -var _ Content = TextProposal{} - // GetTitle returns the proposal title -func (tp TextProposal) GetTitle() string { return tp.Title } +func (tp *TextProposal) GetTitle() string { return tp.Title } // GetDescription returns the proposal description -func (tp TextProposal) GetDescription() string { return tp.Description } +func (tp *TextProposal) GetDescription() string { return tp.Description } // ProposalRoute returns the proposal router key -func (tp TextProposal) ProposalRoute() string { return RouterKey } +func (tp *TextProposal) ProposalRoute() string { return RouterKey } // ProposalType is "Text" -func (tp TextProposal) ProposalType() string { return ProposalTypeText } +func (tp *TextProposal) ProposalType() string { return ProposalTypeText } // ValidateBasic validates the content's title and description of the proposal -func (tp TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } +func (tp *TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } // String implements Stringer interface func (tp TextProposal) String() string { - return fmt.Sprintf(`Text Proposal: - Title: %s - Description: %s -`, tp.Title, tp.Description) + out, _ := yaml.Marshal(tp) + return string(out) } var validProposalTypes = map[string]struct{}{ diff --git a/x/gov/types/proposals_test.go b/x/gov/types/proposals_test.go index 70a4b1d8cec5..7f6a2b23bfae 100644 --- a/x/gov/types/proposals_test.go +++ b/x/gov/types/proposals_test.go @@ -8,13 +8,13 @@ import ( ) func TestProposalStatus_Format(t *testing.T) { - statusDepositPeriod, _ := ProposalStatusFromString("DepositPeriod") + statusDepositPeriod, _ := ProposalStatusFromString("PROPOSAL_STATUS_DEPOSIT_PERIOD") tests := []struct { pt ProposalStatus sprintFArgs string expectedStringOutput string }{ - {statusDepositPeriod, "%s", "DepositPeriod"}, + {statusDepositPeriod, "%s", "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, {statusDepositPeriod, "%v", "1"}, } for _, tt := range tests { diff --git a/x/gov/types/query.pb.go b/x/gov/types/query.pb.go new file mode 100644 index 000000000000..4ca1f33ae5f4 --- /dev/null +++ b/x/gov/types/query.pb.go @@ -0,0 +1,3859 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/gov/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 + +// QueryProposalRequest is the request type for the Query/Proposal RPC method. +type QueryProposalRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` +} + +func (m *QueryProposalRequest) Reset() { *m = QueryProposalRequest{} } +func (m *QueryProposalRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProposalRequest) ProtoMessage() {} +func (*QueryProposalRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{0} +} +func (m *QueryProposalRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposalRequest.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 *QueryProposalRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposalRequest.Merge(m, src) +} +func (m *QueryProposalRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProposalRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposalRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposalRequest proto.InternalMessageInfo + +func (m *QueryProposalRequest) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +// QueryProposalResponse is the response type for the Query/Proposal RPC method. +type QueryProposalResponse struct { + Proposal Proposal `protobuf:"bytes,1,opt,name=proposal,proto3" json:"proposal"` +} + +func (m *QueryProposalResponse) Reset() { *m = QueryProposalResponse{} } +func (m *QueryProposalResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProposalResponse) ProtoMessage() {} +func (*QueryProposalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{1} +} +func (m *QueryProposalResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposalResponse.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 *QueryProposalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposalResponse.Merge(m, src) +} +func (m *QueryProposalResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProposalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposalResponse proto.InternalMessageInfo + +func (m *QueryProposalResponse) GetProposal() Proposal { + if m != nil { + return m.Proposal + } + return Proposal{} +} + +// QueryProposalsRequest is the request type for the Query/Proposals RPC method. +type QueryProposalsRequest struct { + // proposal_status defines the status of the proposals. + ProposalStatus ProposalStatus `protobuf:"varint,1,opt,name=proposal_status,json=proposalStatus,proto3,enum=cosmos.gov.v1beta1.ProposalStatus" json:"proposal_status,omitempty"` + // voter defines the voter address for the proposals. + Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` + // depositor defines the deposit addresses from the proposals. + Depositor string `protobuf:"bytes,3,opt,name=depositor,proto3" json:"depositor,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryProposalsRequest) Reset() { *m = QueryProposalsRequest{} } +func (m *QueryProposalsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProposalsRequest) ProtoMessage() {} +func (*QueryProposalsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{2} +} +func (m *QueryProposalsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposalsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposalsRequest.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 *QueryProposalsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposalsRequest.Merge(m, src) +} +func (m *QueryProposalsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProposalsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposalsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposalsRequest proto.InternalMessageInfo + +// QueryProposalsResponse is the response type for the Query/Proposals RPC +// method. +type QueryProposalsResponse struct { + Proposals []Proposal `protobuf:"bytes,1,rep,name=proposals,proto3" json:"proposals"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryProposalsResponse) Reset() { *m = QueryProposalsResponse{} } +func (m *QueryProposalsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProposalsResponse) ProtoMessage() {} +func (*QueryProposalsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{3} +} +func (m *QueryProposalsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposalsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposalsResponse.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 *QueryProposalsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposalsResponse.Merge(m, src) +} +func (m *QueryProposalsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProposalsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposalsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposalsResponse proto.InternalMessageInfo + +func (m *QueryProposalsResponse) GetProposals() []Proposal { + if m != nil { + return m.Proposals + } + return nil +} + +func (m *QueryProposalsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryVoteRequest is the request type for the Query/Vote RPC method. +type QueryVoteRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` + // voter defines the oter address for the proposals. + Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty"` +} + +func (m *QueryVoteRequest) Reset() { *m = QueryVoteRequest{} } +func (m *QueryVoteRequest) String() string { return proto.CompactTextString(m) } +func (*QueryVoteRequest) ProtoMessage() {} +func (*QueryVoteRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{4} +} +func (m *QueryVoteRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVoteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVoteRequest.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 *QueryVoteRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVoteRequest.Merge(m, src) +} +func (m *QueryVoteRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryVoteRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVoteRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVoteRequest proto.InternalMessageInfo + +// QueryVoteResponse is the response type for the Query/Vote RPC method. +type QueryVoteResponse struct { + // vote defined the queried vote. + Vote Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote"` +} + +func (m *QueryVoteResponse) Reset() { *m = QueryVoteResponse{} } +func (m *QueryVoteResponse) String() string { return proto.CompactTextString(m) } +func (*QueryVoteResponse) ProtoMessage() {} +func (*QueryVoteResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{5} +} +func (m *QueryVoteResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVoteResponse.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 *QueryVoteResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVoteResponse.Merge(m, src) +} +func (m *QueryVoteResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryVoteResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVoteResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVoteResponse proto.InternalMessageInfo + +func (m *QueryVoteResponse) GetVote() Vote { + if m != nil { + return m.Vote + } + return Vote{} +} + +// QueryVotesRequest is the request type for the Query/Votes RPC method. +type QueryVotesRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryVotesRequest) Reset() { *m = QueryVotesRequest{} } +func (m *QueryVotesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryVotesRequest) ProtoMessage() {} +func (*QueryVotesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{6} +} +func (m *QueryVotesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVotesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVotesRequest.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 *QueryVotesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVotesRequest.Merge(m, src) +} +func (m *QueryVotesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryVotesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVotesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVotesRequest proto.InternalMessageInfo + +func (m *QueryVotesRequest) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +func (m *QueryVotesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryVotesResponse is the response type for the Query/Votes RPC method. +type QueryVotesResponse struct { + // votes defined the queried votes. + Votes []Vote `protobuf:"bytes,1,rep,name=votes,proto3" json:"votes"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryVotesResponse) Reset() { *m = QueryVotesResponse{} } +func (m *QueryVotesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryVotesResponse) ProtoMessage() {} +func (*QueryVotesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{7} +} +func (m *QueryVotesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryVotesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryVotesResponse.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 *QueryVotesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryVotesResponse.Merge(m, src) +} +func (m *QueryVotesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryVotesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryVotesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryVotesResponse proto.InternalMessageInfo + +func (m *QueryVotesResponse) GetVotes() []Vote { + if m != nil { + return m.Votes + } + return nil +} + +func (m *QueryVotesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { + // params_type defines which parameters to query for, can be one of "voting", + // "tallying" or "deposit". + ParamsType string `protobuf:"bytes,1,opt,name=params_type,json=paramsType,proto3" json:"params_type,omitempty"` +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{8} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +func (m *QueryParamsRequest) GetParamsType() string { + if m != nil { + return m.ParamsType + } + return "" +} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // voting_params defines the parameters related to voting. + VotingParams VotingParams `protobuf:"bytes,1,opt,name=voting_params,json=votingParams,proto3" json:"voting_params"` + // deposit_params defines the parameters related to deposit. + DepositParams DepositParams `protobuf:"bytes,2,opt,name=deposit_params,json=depositParams,proto3" json:"deposit_params"` + // tally_params defines the parameters related to tally. + TallyParams TallyParams `protobuf:"bytes,3,opt,name=tally_params,json=tallyParams,proto3" json:"tally_params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{9} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetVotingParams() VotingParams { + if m != nil { + return m.VotingParams + } + return VotingParams{} +} + +func (m *QueryParamsResponse) GetDepositParams() DepositParams { + if m != nil { + return m.DepositParams + } + return DepositParams{} +} + +func (m *QueryParamsResponse) GetTallyParams() TallyParams { + if m != nil { + return m.TallyParams + } + return TallyParams{} +} + +// QueryDepositRequest is the request type for the Query/Deposit RPC method. +type QueryDepositRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` + // depositor defines the deposit addresses from the proposals. + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` +} + +func (m *QueryDepositRequest) Reset() { *m = QueryDepositRequest{} } +func (m *QueryDepositRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDepositRequest) ProtoMessage() {} +func (*QueryDepositRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{10} +} +func (m *QueryDepositRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDepositRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDepositRequest.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 *QueryDepositRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDepositRequest.Merge(m, src) +} +func (m *QueryDepositRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDepositRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDepositRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDepositRequest proto.InternalMessageInfo + +// QueryDepositResponse is the response type for the Query/Deposit RPC method. +type QueryDepositResponse struct { + // deposit defines the requested deposit. + Deposit Deposit `protobuf:"bytes,1,opt,name=deposit,proto3" json:"deposit"` +} + +func (m *QueryDepositResponse) Reset() { *m = QueryDepositResponse{} } +func (m *QueryDepositResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDepositResponse) ProtoMessage() {} +func (*QueryDepositResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{11} +} +func (m *QueryDepositResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDepositResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDepositResponse.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 *QueryDepositResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDepositResponse.Merge(m, src) +} +func (m *QueryDepositResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDepositResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDepositResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDepositResponse proto.InternalMessageInfo + +func (m *QueryDepositResponse) GetDeposit() Deposit { + if m != nil { + return m.Deposit + } + return Deposit{} +} + +// QueryDepositsRequest is the request type for the Query/Deposits RPC method. +type QueryDepositsRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDepositsRequest) Reset() { *m = QueryDepositsRequest{} } +func (m *QueryDepositsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDepositsRequest) ProtoMessage() {} +func (*QueryDepositsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{12} +} +func (m *QueryDepositsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDepositsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDepositsRequest.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 *QueryDepositsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDepositsRequest.Merge(m, src) +} +func (m *QueryDepositsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDepositsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDepositsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDepositsRequest proto.InternalMessageInfo + +func (m *QueryDepositsRequest) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +func (m *QueryDepositsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDepositsResponse is the response type for the Query/Deposits RPC method. +type QueryDepositsResponse struct { + Deposits []Deposit `protobuf:"bytes,1,rep,name=deposits,proto3" json:"deposits"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDepositsResponse) Reset() { *m = QueryDepositsResponse{} } +func (m *QueryDepositsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDepositsResponse) ProtoMessage() {} +func (*QueryDepositsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{13} +} +func (m *QueryDepositsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDepositsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDepositsResponse.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 *QueryDepositsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDepositsResponse.Merge(m, src) +} +func (m *QueryDepositsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDepositsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDepositsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDepositsResponse proto.InternalMessageInfo + +func (m *QueryDepositsResponse) GetDeposits() []Deposit { + if m != nil { + return m.Deposits + } + return nil +} + +func (m *QueryDepositsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryTallyResultRequest is the request type for the Query/Tally RPC method. +type QueryTallyResultRequest struct { + // proposal_id defines the unique id of the proposal. + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id,omitempty"` +} + +func (m *QueryTallyResultRequest) Reset() { *m = QueryTallyResultRequest{} } +func (m *QueryTallyResultRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTallyResultRequest) ProtoMessage() {} +func (*QueryTallyResultRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{14} +} +func (m *QueryTallyResultRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTallyResultRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTallyResultRequest.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 *QueryTallyResultRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTallyResultRequest.Merge(m, src) +} +func (m *QueryTallyResultRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTallyResultRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTallyResultRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTallyResultRequest proto.InternalMessageInfo + +func (m *QueryTallyResultRequest) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +// QueryTallyResultResponse is the response type for the Query/Tally RPC method. +type QueryTallyResultResponse struct { + // tally defines the requested tally. + Tally TallyResult `protobuf:"bytes,1,opt,name=tally,proto3" json:"tally"` +} + +func (m *QueryTallyResultResponse) Reset() { *m = QueryTallyResultResponse{} } +func (m *QueryTallyResultResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTallyResultResponse) ProtoMessage() {} +func (*QueryTallyResultResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e35c0d133e91c0a2, []int{15} +} +func (m *QueryTallyResultResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTallyResultResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTallyResultResponse.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 *QueryTallyResultResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTallyResultResponse.Merge(m, src) +} +func (m *QueryTallyResultResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTallyResultResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTallyResultResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTallyResultResponse proto.InternalMessageInfo + +func (m *QueryTallyResultResponse) GetTally() TallyResult { + if m != nil { + return m.Tally + } + return TallyResult{} +} + +func init() { + proto.RegisterType((*QueryProposalRequest)(nil), "cosmos.gov.v1beta1.QueryProposalRequest") + proto.RegisterType((*QueryProposalResponse)(nil), "cosmos.gov.v1beta1.QueryProposalResponse") + proto.RegisterType((*QueryProposalsRequest)(nil), "cosmos.gov.v1beta1.QueryProposalsRequest") + proto.RegisterType((*QueryProposalsResponse)(nil), "cosmos.gov.v1beta1.QueryProposalsResponse") + proto.RegisterType((*QueryVoteRequest)(nil), "cosmos.gov.v1beta1.QueryVoteRequest") + proto.RegisterType((*QueryVoteResponse)(nil), "cosmos.gov.v1beta1.QueryVoteResponse") + proto.RegisterType((*QueryVotesRequest)(nil), "cosmos.gov.v1beta1.QueryVotesRequest") + proto.RegisterType((*QueryVotesResponse)(nil), "cosmos.gov.v1beta1.QueryVotesResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.gov.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.gov.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryDepositRequest)(nil), "cosmos.gov.v1beta1.QueryDepositRequest") + proto.RegisterType((*QueryDepositResponse)(nil), "cosmos.gov.v1beta1.QueryDepositResponse") + proto.RegisterType((*QueryDepositsRequest)(nil), "cosmos.gov.v1beta1.QueryDepositsRequest") + proto.RegisterType((*QueryDepositsResponse)(nil), "cosmos.gov.v1beta1.QueryDepositsResponse") + proto.RegisterType((*QueryTallyResultRequest)(nil), "cosmos.gov.v1beta1.QueryTallyResultRequest") + proto.RegisterType((*QueryTallyResultResponse)(nil), "cosmos.gov.v1beta1.QueryTallyResultResponse") +} + +func init() { proto.RegisterFile("cosmos/gov/v1beta1/query.proto", fileDescriptor_e35c0d133e91c0a2) } + +var fileDescriptor_e35c0d133e91c0a2 = []byte{ + // 963 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x41, 0x6f, 0x1b, 0x55, + 0x10, 0xf6, 0x4b, 0x9c, 0xd6, 0x9e, 0xb4, 0x01, 0x86, 0x00, 0xd6, 0x12, 0xec, 0xb0, 0xa2, 0xad, + 0x49, 0xa9, 0x97, 0x24, 0x05, 0xd4, 0x16, 0x50, 0x89, 0x50, 0x5b, 0x54, 0x09, 0x15, 0xa7, 0x02, + 0x89, 0x03, 0xd1, 0xa6, 0x5e, 0x2d, 0x2b, 0x1c, 0xbf, 0xad, 0xdf, 0xb3, 0x45, 0x14, 0x22, 0x24, + 0x4e, 0x20, 0x2e, 0xa0, 0x22, 0x6e, 0x88, 0x4a, 0x95, 0xf8, 0x2d, 0x3d, 0x56, 0x82, 0x03, 0x27, + 0x84, 0x12, 0x0e, 0x88, 0xdf, 0xc0, 0x01, 0xed, 0x7b, 0xf3, 0xd6, 0xbb, 0xce, 0x3a, 0xbb, 0x29, + 0x55, 0x4f, 0xb1, 0xe7, 0x7d, 0x33, 0xf3, 0x7d, 0x33, 0xf3, 0xe6, 0xc5, 0x50, 0xbf, 0xc5, 0xc5, + 0x16, 0x17, 0x8e, 0xcf, 0x87, 0xce, 0x70, 0x79, 0xd3, 0x93, 0xee, 0xb2, 0x73, 0x7b, 0xe0, 0xf5, + 0xb7, 0x5b, 0x61, 0x9f, 0x4b, 0x8e, 0xa8, 0xcf, 0x5b, 0x3e, 0x1f, 0xb6, 0xe8, 0xdc, 0x5a, 0x22, + 0x9f, 0x4d, 0x57, 0x78, 0x1a, 0x1c, 0xbb, 0x86, 0xae, 0x1f, 0xf4, 0x5c, 0x19, 0xf0, 0x9e, 0xf6, + 0xb7, 0xe6, 0x7d, 0xee, 0x73, 0xf5, 0xd1, 0x89, 0x3e, 0x91, 0x75, 0xc1, 0xe7, 0xdc, 0xef, 0x7a, + 0x8e, 0x1b, 0x06, 0x8e, 0xdb, 0xeb, 0x71, 0xa9, 0x5c, 0x84, 0x39, 0xcd, 0xe0, 0x14, 0xe5, 0x57, + 0xa7, 0xf6, 0x1b, 0x30, 0xff, 0x41, 0x94, 0xf3, 0x46, 0x9f, 0x87, 0x5c, 0xb8, 0xdd, 0xb6, 0x77, + 0x7b, 0xe0, 0x09, 0x89, 0x0d, 0x98, 0x0d, 0xc9, 0xb4, 0x11, 0x74, 0x6a, 0x6c, 0x91, 0x35, 0xcb, + 0x6d, 0x30, 0xa6, 0xf7, 0x3a, 0xf6, 0x47, 0xf0, 0xcc, 0x98, 0xa3, 0x08, 0x79, 0x4f, 0x78, 0xf8, + 0x36, 0x54, 0x0c, 0x4c, 0xb9, 0xcd, 0xae, 0x2c, 0xb4, 0x0e, 0xca, 0x6e, 0x19, 0xbf, 0xb5, 0xf2, + 0xfd, 0x3f, 0x1a, 0xa5, 0x76, 0xec, 0x63, 0xff, 0xc3, 0xc6, 0x22, 0x0b, 0xc3, 0xe9, 0x3a, 0x3c, + 0x11, 0x73, 0x12, 0xd2, 0x95, 0x03, 0xa1, 0x12, 0xcc, 0xad, 0xd8, 0x87, 0x25, 0x58, 0x57, 0xc8, + 0xf6, 0x5c, 0x98, 0xfa, 0x8e, 0xf3, 0x30, 0x33, 0xe4, 0xd2, 0xeb, 0xd7, 0xa6, 0x16, 0x59, 0xb3, + 0xda, 0xd6, 0x5f, 0x70, 0x01, 0xaa, 0x1d, 0x2f, 0xe4, 0x22, 0x90, 0xbc, 0x5f, 0x9b, 0x56, 0x27, + 0x23, 0x03, 0x5e, 0x01, 0x18, 0xb5, 0xa4, 0x56, 0x56, 0xe2, 0x4e, 0x9b, 0xdc, 0x51, 0xff, 0x5a, + 0xba, 0xd9, 0x31, 0x05, 0xd7, 0xf7, 0x88, 0x7c, 0x3b, 0xe1, 0x79, 0xb1, 0xf2, 0xf5, 0xdd, 0x46, + 0xe9, 0xef, 0xbb, 0x8d, 0x92, 0x7d, 0x8f, 0xc1, 0xb3, 0xe3, 0x62, 0xa9, 0x8e, 0x97, 0xa1, 0x6a, + 0x28, 0x47, 0x3a, 0xa7, 0x0b, 0x16, 0x72, 0xe4, 0x84, 0x57, 0x53, 0x74, 0xa7, 0x14, 0xdd, 0x33, + 0xb9, 0x74, 0x75, 0xfa, 0x24, 0x5f, 0x7b, 0x1d, 0x9e, 0x54, 0x24, 0x3f, 0xe4, 0xd2, 0x2b, 0x3a, + 0x20, 0xd9, 0x05, 0x4e, 0x48, 0xbf, 0x0a, 0x4f, 0x25, 0x82, 0x92, 0xe8, 0x15, 0x28, 0x47, 0x38, + 0x1a, 0x9c, 0x5a, 0x96, 0xde, 0x08, 0x4f, 0x5a, 0x15, 0xd6, 0xfe, 0x22, 0x11, 0x48, 0x14, 0xa6, + 0x77, 0x25, 0xa3, 0x38, 0x0f, 0xd1, 0x4b, 0xfb, 0x0e, 0x03, 0x4c, 0xa6, 0x27, 0x21, 0xe7, 0xb5, + 0x7a, 0xd3, 0xb9, 0x3c, 0x25, 0x1a, 0xfc, 0xe8, 0x3a, 0xf6, 0x1a, 0x91, 0xba, 0xe1, 0xf6, 0xdd, + 0xad, 0x54, 0x51, 0x94, 0x61, 0x43, 0x6e, 0x87, 0xba, 0xc8, 0xd5, 0xc8, 0x2d, 0x32, 0xdd, 0xdc, + 0x0e, 0x3d, 0xfb, 0x5f, 0x06, 0x4f, 0xa7, 0xfc, 0x48, 0xcd, 0x75, 0x38, 0x39, 0xe4, 0x32, 0xe8, + 0xf9, 0x1b, 0x1a, 0x4c, 0xfd, 0x59, 0x9c, 0xa0, 0x2a, 0xe8, 0xf9, 0x3a, 0x00, 0xa9, 0x3b, 0x31, + 0x4c, 0xd8, 0xf0, 0x7d, 0x98, 0xa3, 0x2b, 0x65, 0xa2, 0x69, 0xa1, 0x2f, 0x66, 0x45, 0x7b, 0x57, + 0x23, 0x53, 0xe1, 0x4e, 0x76, 0x92, 0x46, 0xbc, 0x06, 0x27, 0xa4, 0xdb, 0xed, 0x6e, 0x9b, 0x68, + 0xd3, 0x2a, 0x5a, 0x23, 0x2b, 0xda, 0xcd, 0x08, 0x97, 0x8a, 0x35, 0x2b, 0x47, 0x26, 0xfb, 0x13, + 0x52, 0x4f, 0x49, 0x0b, 0xcf, 0x52, 0x6a, 0x6b, 0x4c, 0x8d, 0x6d, 0x8d, 0xc4, 0xc8, 0xaf, 0xd3, + 0xb2, 0x8d, 0xe3, 0x53, 0x79, 0x2f, 0xc1, 0x71, 0x82, 0x53, 0x61, 0x9f, 0x3f, 0xa4, 0x14, 0x44, + 0xdc, 0x78, 0xd8, 0x5f, 0xa6, 0x83, 0x3e, 0xfe, 0x1b, 0xf0, 0xb3, 0x59, 0xd8, 0x23, 0x06, 0xa4, + 0xeb, 0x2d, 0xa8, 0x10, 0x4b, 0x73, 0x0f, 0x0a, 0x08, 0x8b, 0x5d, 0x1e, 0xdd, 0x6d, 0xb8, 0x08, + 0xcf, 0x29, 0x82, 0xaa, 0xfd, 0x6d, 0x4f, 0x0c, 0xba, 0xf2, 0x08, 0xef, 0x5c, 0xed, 0xa0, 0x6f, + 0xdc, 0xb7, 0x19, 0x35, 0x3e, 0xd4, 0xb5, 0xc9, 0x23, 0xa7, 0xfd, 0xcc, 0x5d, 0x57, 0x3e, 0x2b, + 0xbf, 0x55, 0x61, 0x46, 0x45, 0xc6, 0x1f, 0x18, 0x54, 0xcc, 0x16, 0xc7, 0x66, 0x56, 0x90, 0xac, + 0x27, 0xda, 0x7a, 0xb9, 0x00, 0x52, 0x13, 0xb5, 0x57, 0xbf, 0xfa, 0xf5, 0xaf, 0x3b, 0x53, 0xe7, + 0xf0, 0xac, 0x93, 0xf1, 0xcf, 0x40, 0xfc, 0x60, 0x38, 0x3b, 0x89, 0x52, 0xec, 0xe2, 0x37, 0x0c, + 0xaa, 0xf1, 0xb3, 0x84, 0xf9, 0xd9, 0xcc, 0xe4, 0x59, 0x4b, 0x45, 0xa0, 0xc4, 0xec, 0x94, 0x62, + 0xd6, 0xc0, 0x17, 0x0e, 0x65, 0x86, 0x3f, 0x32, 0x28, 0x47, 0xeb, 0x12, 0x5f, 0x9a, 0x18, 0x3b, + 0xf1, 0x38, 0x59, 0xa7, 0x72, 0x50, 0x94, 0xfc, 0x1d, 0x95, 0xfc, 0x12, 0x5e, 0x38, 0x42, 0x59, + 0x1c, 0xb5, 0xa9, 0x9d, 0x1d, 0xf5, 0x9c, 0xed, 0xe2, 0xf7, 0x0c, 0x66, 0xd4, 0xe6, 0xc7, 0xc3, + 0x73, 0xc6, 0xc5, 0x39, 0x9d, 0x07, 0x23, 0x6e, 0x17, 0x14, 0xb7, 0x55, 0x5c, 0x3e, 0x32, 0x37, + 0xfc, 0x96, 0xc1, 0x31, 0xda, 0x8d, 0x93, 0xb3, 0xa5, 0x5e, 0x06, 0xeb, 0x4c, 0x2e, 0x8e, 0x68, + 0xbd, 0xaa, 0x68, 0x2d, 0x61, 0x33, 0x93, 0x96, 0xc2, 0x3a, 0x3b, 0x89, 0x47, 0x66, 0x17, 0x7f, + 0x61, 0x70, 0x9c, 0x6e, 0x38, 0x4e, 0x4e, 0x93, 0x5e, 0xb9, 0x56, 0x33, 0x1f, 0x48, 0x84, 0xae, + 0x29, 0x42, 0x6b, 0x78, 0xf9, 0x28, 0x75, 0x32, 0x2b, 0xc6, 0xd9, 0x89, 0xd7, 0xf4, 0x2e, 0xfe, + 0xc4, 0xa0, 0x62, 0x56, 0x18, 0xe6, 0x12, 0x10, 0xf9, 0xd7, 0x70, 0x7c, 0x1f, 0xda, 0x6f, 0x2a, + 0xae, 0xaf, 0xe3, 0xf9, 0x87, 0xe1, 0x8a, 0xf7, 0x18, 0xcc, 0x26, 0xb6, 0x09, 0x9e, 0x9d, 0x98, + 0xf8, 0xe0, 0x9e, 0xb3, 0x5e, 0x29, 0x06, 0xfe, 0x3f, 0xc3, 0xa7, 0xd6, 0xda, 0xda, 0xda, 0xfd, + 0xbd, 0x3a, 0x7b, 0xb0, 0x57, 0x67, 0x7f, 0xee, 0xd5, 0xd9, 0x77, 0xfb, 0xf5, 0xd2, 0x83, 0xfd, + 0x7a, 0xe9, 0xf7, 0xfd, 0x7a, 0xe9, 0xe3, 0xa6, 0x1f, 0xc8, 0x4f, 0x07, 0x9b, 0xad, 0x5b, 0x7c, + 0xcb, 0x84, 0xd5, 0x7f, 0xce, 0x89, 0xce, 0x67, 0xce, 0xe7, 0x2a, 0x47, 0x34, 0x32, 0x62, 0xf3, + 0x98, 0xfa, 0x6d, 0xb2, 0xfa, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0x86, 0xe2, 0x1a, 0x4f, + 0x0d, 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 { + // Proposal queries proposal details based on ProposalID. + Proposal(ctx context.Context, in *QueryProposalRequest, opts ...grpc.CallOption) (*QueryProposalResponse, error) + // Proposals queries all proposals based on given status. + Proposals(ctx context.Context, in *QueryProposalsRequest, opts ...grpc.CallOption) (*QueryProposalsResponse, error) + // Vote queries voted information based on proposalID, voterAddr. + Vote(ctx context.Context, in *QueryVoteRequest, opts ...grpc.CallOption) (*QueryVoteResponse, error) + // Votes queries votes of a given proposal. + Votes(ctx context.Context, in *QueryVotesRequest, opts ...grpc.CallOption) (*QueryVotesResponse, error) + // Params queries all parameters of the gov module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // Deposit queries single deposit information based proposalID, depositAddr. + Deposit(ctx context.Context, in *QueryDepositRequest, opts ...grpc.CallOption) (*QueryDepositResponse, error) + // Deposits queries all deposits of a single proposal. + Deposits(ctx context.Context, in *QueryDepositsRequest, opts ...grpc.CallOption) (*QueryDepositsResponse, error) + // TallyResult queries the tally of a proposal vote. + TallyResult(ctx context.Context, in *QueryTallyResultRequest, opts ...grpc.CallOption) (*QueryTallyResultResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Proposal(ctx context.Context, in *QueryProposalRequest, opts ...grpc.CallOption) (*QueryProposalResponse, error) { + out := new(QueryProposalResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Proposal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Proposals(ctx context.Context, in *QueryProposalsRequest, opts ...grpc.CallOption) (*QueryProposalsResponse, error) { + out := new(QueryProposalsResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Proposals", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Vote(ctx context.Context, in *QueryVoteRequest, opts ...grpc.CallOption) (*QueryVoteResponse, error) { + out := new(QueryVoteResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Vote", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Votes(ctx context.Context, in *QueryVotesRequest, opts ...grpc.CallOption) (*QueryVotesResponse, error) { + out := new(QueryVotesResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Votes", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Deposit(ctx context.Context, in *QueryDepositRequest, opts ...grpc.CallOption) (*QueryDepositResponse, error) { + out := new(QueryDepositResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Deposit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Deposits(ctx context.Context, in *QueryDepositsRequest, opts ...grpc.CallOption) (*QueryDepositsResponse, error) { + out := new(QueryDepositsResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/Deposits", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TallyResult(ctx context.Context, in *QueryTallyResultRequest, opts ...grpc.CallOption) (*QueryTallyResultResponse, error) { + out := new(QueryTallyResultResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Query/TallyResult", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Proposal queries proposal details based on ProposalID. + Proposal(context.Context, *QueryProposalRequest) (*QueryProposalResponse, error) + // Proposals queries all proposals based on given status. + Proposals(context.Context, *QueryProposalsRequest) (*QueryProposalsResponse, error) + // Vote queries voted information based on proposalID, voterAddr. + Vote(context.Context, *QueryVoteRequest) (*QueryVoteResponse, error) + // Votes queries votes of a given proposal. + Votes(context.Context, *QueryVotesRequest) (*QueryVotesResponse, error) + // Params queries all parameters of the gov module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // Deposit queries single deposit information based proposalID, depositAddr. + Deposit(context.Context, *QueryDepositRequest) (*QueryDepositResponse, error) + // Deposits queries all deposits of a single proposal. + Deposits(context.Context, *QueryDepositsRequest) (*QueryDepositsResponse, error) + // TallyResult queries the tally of a proposal vote. + TallyResult(context.Context, *QueryTallyResultRequest) (*QueryTallyResultResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Proposal(ctx context.Context, req *QueryProposalRequest) (*QueryProposalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Proposal not implemented") +} +func (*UnimplementedQueryServer) Proposals(ctx context.Context, req *QueryProposalsRequest) (*QueryProposalsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Proposals not implemented") +} +func (*UnimplementedQueryServer) Vote(ctx context.Context, req *QueryVoteRequest) (*QueryVoteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented") +} +func (*UnimplementedQueryServer) Votes(ctx context.Context, req *QueryVotesRequest) (*QueryVotesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Votes not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) Deposit(ctx context.Context, req *QueryDepositRequest) (*QueryDepositResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") +} +func (*UnimplementedQueryServer) Deposits(ctx context.Context, req *QueryDepositsRequest) (*QueryDepositsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Deposits not implemented") +} +func (*UnimplementedQueryServer) TallyResult(ctx context.Context, req *QueryTallyResultRequest) (*QueryTallyResultResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TallyResult not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Proposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProposalRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Proposal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Proposal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Proposal(ctx, req.(*QueryProposalRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Proposals_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProposalsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Proposals(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Proposals", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Proposals(ctx, req.(*QueryProposalsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Vote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryVoteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Vote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Vote", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Vote(ctx, req.(*QueryVoteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Votes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryVotesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Votes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Votes", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Votes(ctx, req.(*QueryVotesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDepositRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Deposit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Deposit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Deposit(ctx, req.(*QueryDepositRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Deposits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDepositsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Deposits(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/Deposits", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Deposits(ctx, req.(*QueryDepositsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TallyResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTallyResultRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TallyResult(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Query/TallyResult", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TallyResult(ctx, req.(*QueryTallyResultRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.gov.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Proposal", + Handler: _Query_Proposal_Handler, + }, + { + MethodName: "Proposals", + Handler: _Query_Proposals_Handler, + }, + { + MethodName: "Vote", + Handler: _Query_Vote_Handler, + }, + { + MethodName: "Votes", + Handler: _Query_Votes_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "Deposit", + Handler: _Query_Deposit_Handler, + }, + { + MethodName: "Deposits", + Handler: _Query_Deposits_Handler, + }, + { + MethodName: "TallyResult", + Handler: _Query_TallyResult_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/gov/v1beta1/query.proto", +} + +func (m *QueryProposalRequest) 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 *QueryProposalRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposalRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryProposalResponse) 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 *QueryProposalResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposalResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Proposal.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 *QueryProposalsRequest) 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 *QueryProposalsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposalsRequest) 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] = 0x22 + } + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x1a + } + if len(m.Voter) > 0 { + i -= len(m.Voter) + copy(dAtA[i:], m.Voter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Voter))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalStatus != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalStatus)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryProposalsResponse) 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 *QueryProposalsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposalsResponse) 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.Proposals) > 0 { + for iNdEx := len(m.Proposals) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Proposals[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 *QueryVoteRequest) 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 *QueryVoteRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVoteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Voter) > 0 { + i -= len(m.Voter) + copy(dAtA[i:], m.Voter) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Voter))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryVoteResponse) 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 *QueryVoteResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Vote.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 *QueryVotesRequest) 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 *QueryVotesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVotesRequest) 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 m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryVotesResponse) 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 *QueryVotesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryVotesResponse) 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.Votes) > 0 { + for iNdEx := len(m.Votes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Votes[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 *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ParamsType) > 0 { + i -= len(m.ParamsType) + copy(dAtA[i:], m.ParamsType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ParamsType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.TallyParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.DepositParams.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.VotingParams.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 *QueryDepositRequest) 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 *QueryDepositRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDepositRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0x12 + } + if m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryDepositResponse) 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 *QueryDepositResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Deposit.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 *QueryDepositsRequest) 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 *QueryDepositsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDepositsRequest) 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 m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryDepositsResponse) 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 *QueryDepositsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDepositsResponse) 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.Deposits) > 0 { + for iNdEx := len(m.Deposits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Deposits[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 *QueryTallyResultRequest) 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 *QueryTallyResultRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTallyResultRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProposalId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryTallyResultResponse) 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 *QueryTallyResultResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTallyResultResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Tally.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 *QueryProposalRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + return n +} + +func (m *QueryProposalResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Proposal.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryProposalsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalStatus != 0 { + n += 1 + sovQuery(uint64(m.ProposalStatus)) + } + l = len(m.Voter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Depositor) + 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 *QueryProposalsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Proposals) > 0 { + for _, e := range m.Proposals { + 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 *QueryVoteRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + l = len(m.Voter) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryVoteResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Vote.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryVotesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryVotesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Votes) > 0 { + for _, e := range m.Votes { + 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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ParamsType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.VotingParams.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.DepositParams.Size() + n += 1 + l + sovQuery(uint64(l)) + l = m.TallyParams.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryDepositRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDepositResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Deposit.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryDepositsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDepositsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Deposits) > 0 { + for _, e := range m.Deposits { + 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 *QueryTallyResultRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovQuery(uint64(m.ProposalId)) + } + return n +} + +func (m *QueryTallyResultResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Tally.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 *QueryProposalRequest) 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: QueryProposalRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposalRequest: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryProposalResponse) 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: QueryProposalResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposalResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proposal", 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 err := m.Proposal.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 *QueryProposalsRequest) 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: QueryProposalsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposalsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposalStatus", wireType) + } + m.ProposalStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalStatus |= ProposalStatus(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 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.Voter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depositor", 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.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + 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 *QueryProposalsResponse) 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: QueryProposalsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposalsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proposals", 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.Proposals = append(m.Proposals, Proposal{}) + if err := m.Proposals[len(m.Proposals)-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 *QueryVoteRequest) 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: QueryVoteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVoteRequest: 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 ErrIntOverflowQuery + } + 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 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.Voter = 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 *QueryVoteResponse) 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: QueryVoteResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVoteResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Vote", 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 err := m.Vote.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 *QueryVotesRequest) 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: QueryVotesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVotesRequest: 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 ErrIntOverflowQuery + } + 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 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 *QueryVotesResponse) 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: QueryVotesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryVotesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Votes", 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.Votes = append(m.Votes, Vote{}) + if err := m.Votes[len(m.Votes)-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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ParamsType", 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.ParamsType = 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 *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 VotingParams", 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 err := m.VotingParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DepositParams", 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 err := m.DepositParams.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TallyParams", 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 err := m.TallyParams.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 *QueryDepositRequest) 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: QueryDepositRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDepositRequest: 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 ErrIntOverflowQuery + } + 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 Depositor", 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.Depositor = 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 *QueryDepositResponse) 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: QueryDepositResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDepositResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposit", 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 err := m.Deposit.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 *QueryDepositsRequest) 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: QueryDepositsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDepositsRequest: 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 ErrIntOverflowQuery + } + 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 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 *QueryDepositsResponse) 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: QueryDepositsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDepositsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposits", 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.Deposits = append(m.Deposits, Deposit{}) + if err := m.Deposits[len(m.Deposits)-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 *QueryTallyResultRequest) 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: QueryTallyResultRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTallyResultRequest: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryTallyResultResponse) 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: QueryTallyResultResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTallyResultResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tally", 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 err := m.Tally.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/gov/types/query.pb.gw.go b/x/gov/types/query.pb.gw.go new file mode 100644 index 000000000000..52814511b9cb --- /dev/null +++ b/x/gov/types/query.pb.gw.go @@ -0,0 +1,932 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/gov/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_Proposal_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposalRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + msg, err := client.Proposal(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Proposal_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposalRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + msg, err := server.Proposal(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Proposals_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Proposals_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposalsRequest + 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_Proposals_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Proposals(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Proposals_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposalsRequest + 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_Proposals_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Proposals(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Vote_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVoteRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + val, ok = pathParams["voter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "voter") + } + + protoReq.Voter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "voter", err) + } + + msg, err := client.Vote(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Vote_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVoteRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + val, ok = pathParams["voter"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "voter") + } + + protoReq.Voter, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "voter", err) + } + + msg, err := server.Vote(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Votes_0 = &utilities.DoubleArray{Encoding: map[string]int{"proposal_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Votes_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVotesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", 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_Votes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Votes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Votes_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryVotesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", 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_Votes_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Votes(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["params_type"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "params_type") + } + + protoReq.ParamsType, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "params_type", err) + } + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["params_type"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "params_type") + } + + protoReq.ParamsType, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "params_type", err) + } + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Deposit_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDepositRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + val, ok = pathParams["depositor"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "depositor") + } + + protoReq.Depositor, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "depositor", err) + } + + msg, err := client.Deposit(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Deposit_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDepositRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + val, ok = pathParams["depositor"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "depositor") + } + + protoReq.Depositor, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "depositor", err) + } + + msg, err := server.Deposit(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Deposits_0 = &utilities.DoubleArray{Encoding: map[string]int{"proposal_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Deposits_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDepositsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", 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_Deposits_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Deposits(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Deposits_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDepositsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", 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_Deposits_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Deposits(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_TallyResult_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTallyResultRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + msg, err := client.TallyResult(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TallyResult_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTallyResultRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["proposal_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "proposal_id") + } + + protoReq.ProposalId, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "proposal_id", err) + } + + msg, err := server.TallyResult(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_Proposal_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_Proposal_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_Proposal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Proposals_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_Proposals_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_Proposals_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Vote_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_Vote_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_Vote_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Votes_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_Votes_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_Votes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Deposit_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_Deposit_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_Deposit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Deposits_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_Deposits_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_Deposits_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TallyResult_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_TallyResult_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_TallyResult_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_Proposal_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_Proposal_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_Proposal_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Proposals_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_Proposals_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_Proposals_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Vote_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_Vote_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_Vote_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Votes_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_Votes_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_Votes_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Deposit_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_Deposit_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_Deposit_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Deposits_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_Deposits_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_Deposits_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TallyResult_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_TallyResult_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_TallyResult_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Proposal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Proposals_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "gov", "v1beta1", "proposals"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Vote_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes", "voter"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Votes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "votes"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "gov", "v1beta1", "params", "params_type"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Deposit_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits", "depositor"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Deposits_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "deposits"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_TallyResult_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "gov", "v1beta1", "proposals", "proposal_id", "tally"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Proposal_0 = runtime.ForwardResponseMessage + + forward_Query_Proposals_0 = runtime.ForwardResponseMessage + + forward_Query_Vote_0 = runtime.ForwardResponseMessage + + forward_Query_Votes_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_Deposit_0 = runtime.ForwardResponseMessage + + forward_Query_Deposits_0 = runtime.ForwardResponseMessage + + forward_Query_TallyResult_0 = runtime.ForwardResponseMessage +) diff --git a/x/gov/types/tally.go b/x/gov/types/tally.go index d437816e9a9e..a4e9ee908608 100644 --- a/x/gov/types/tally.go +++ b/x/gov/types/tally.go @@ -1,7 +1,7 @@ package types import ( - "fmt" + yaml "gopkg.in/yaml.v2" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -28,14 +28,6 @@ func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegator } } -// TallyResult defines a standard tally for a proposal -type TallyResult struct { - Yes sdk.Int `json:"yes" yaml:"yes"` - Abstain sdk.Int `json:"abstain" yaml:"abstain"` - No sdk.Int `json:"no" yaml:"no"` - NoWithVeto sdk.Int `json:"no_with_veto" yaml:"no_with_veto"` -} - // NewTallyResult creates a new TallyResult instance func NewTallyResult(yes, abstain, no, noWithVeto sdk.Int) TallyResult { return TallyResult{ @@ -71,9 +63,6 @@ func (tr TallyResult) Equals(comp TallyResult) bool { // String implements stringer interface func (tr TallyResult) String() string { - return fmt.Sprintf(`Tally Result: - Yes: %s - Abstain: %s - No: %s - NoWithVeto: %s`, tr.Yes, tr.Abstain, tr.No, tr.NoWithVeto) + out, _ := yaml.Marshal(tr) + return string(out) } diff --git a/x/gov/types/tx.pb.go b/x/gov/types/tx.pb.go new file mode 100644 index 000000000000..0261e1c13949 --- /dev/null +++ b/x/gov/types/tx.pb.go @@ -0,0 +1,1471 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/gov/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/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/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 + +// MsgSubmitProposal defines an sdk.Msg type that supports submitting arbitrary +// proposal Content. +type MsgSubmitProposal struct { + Content *types.Any `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` + InitialDeposit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=initial_deposit,json=initialDeposit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"initial_deposit" yaml:"initial_deposit"` + Proposer string `protobuf:"bytes,3,opt,name=proposer,proto3" json:"proposer,omitempty"` +} + +func (m *MsgSubmitProposal) Reset() { *m = MsgSubmitProposal{} } +func (*MsgSubmitProposal) ProtoMessage() {} +func (*MsgSubmitProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{0} +} +func (m *MsgSubmitProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitProposal.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 *MsgSubmitProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitProposal.Merge(m, src) +} +func (m *MsgSubmitProposal) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitProposal) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitProposal proto.InternalMessageInfo + +// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type. +type MsgSubmitProposalResponse struct { + ProposalId uint64 `protobuf:"varint,1,opt,name=proposal_id,json=proposalId,proto3" json:"proposal_id" yaml:"proposal_id"` +} + +func (m *MsgSubmitProposalResponse) Reset() { *m = MsgSubmitProposalResponse{} } +func (m *MsgSubmitProposalResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitProposalResponse) ProtoMessage() {} +func (*MsgSubmitProposalResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{1} +} +func (m *MsgSubmitProposalResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitProposalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitProposalResponse.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 *MsgSubmitProposalResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitProposalResponse.Merge(m, src) +} +func (m *MsgSubmitProposalResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitProposalResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitProposalResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitProposalResponse proto.InternalMessageInfo + +func (m *MsgSubmitProposalResponse) GetProposalId() uint64 { + if m != nil { + return m.ProposalId + } + return 0 +} + +// MsgVote defines a message to cast a vote. +type MsgVote 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"` + Option VoteOption `protobuf:"varint,3,opt,name=option,proto3,enum=cosmos.gov.v1beta1.VoteOption" json:"option,omitempty"` +} + +func (m *MsgVote) Reset() { *m = MsgVote{} } +func (*MsgVote) ProtoMessage() {} +func (*MsgVote) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{2} +} +func (m *MsgVote) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVote.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 *MsgVote) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVote.Merge(m, src) +} +func (m *MsgVote) XXX_Size() int { + return m.Size() +} +func (m *MsgVote) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVote.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVote proto.InternalMessageInfo + +// MsgVoteResponse defines the Msg/Vote response type. +type MsgVoteResponse struct { +} + +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} +} +func (m *MsgVoteResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteResponse.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 *MsgVoteResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteResponse.Merge(m, src) +} +func (m *MsgVoteResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteResponse 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"` + Depositor string `protobuf:"bytes,2,opt,name=depositor,proto3" json:"depositor,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgDeposit) Reset() { *m = MsgDeposit{} } +func (*MsgDeposit) ProtoMessage() {} +func (*MsgDeposit) Descriptor() ([]byte, []int) { + return fileDescriptor_3c053992595e3dce, []int{4} +} +func (m *MsgDeposit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDeposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDeposit.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 *MsgDeposit) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDeposit.Merge(m, src) +} +func (m *MsgDeposit) XXX_Size() int { + return m.Size() +} +func (m *MsgDeposit) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDeposit.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDeposit proto.InternalMessageInfo + +// MsgDepositResponse defines the Msg/Deposit response type. +type MsgDepositResponse struct { +} + +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} +} +func (m *MsgDepositResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDepositResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDepositResponse.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 *MsgDepositResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDepositResponse.Merge(m, src) +} +func (m *MsgDepositResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDepositResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDepositResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo + +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((*MsgVoteResponse)(nil), "cosmos.gov.v1beta1.MsgVoteResponse") + proto.RegisterType((*MsgDeposit)(nil), "cosmos.gov.v1beta1.MsgDeposit") + proto.RegisterType((*MsgDepositResponse)(nil), "cosmos.gov.v1beta1.MsgDepositResponse") +} + +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, +} + +// 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 { + // SubmitProposal defines a method to create new proposal given a content. + 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) + // Deposit defines a method to add deposit on a specific proposal. + Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SubmitProposal(ctx context.Context, in *MsgSubmitProposal, opts ...grpc.CallOption) (*MsgSubmitProposalResponse, error) { + out := new(MsgSubmitProposalResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/SubmitProposal", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Vote(ctx context.Context, in *MsgVote, opts ...grpc.CallOption) (*MsgVoteResponse, error) { + out := new(MsgVoteResponse) + err := c.cc.Invoke(ctx, "/cosmos.gov.v1beta1.Msg/Vote", 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...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // SubmitProposal defines a method to create new proposal given a content. + SubmitProposal(context.Context, *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) + // Vote defines a method to add a vote on a specific proposal. + Vote(context.Context, *MsgVote) (*MsgVoteResponse, error) + // Deposit defines a method to add deposit on a specific proposal. + Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SubmitProposal(ctx context.Context, req *MsgSubmitProposal) (*MsgSubmitProposalResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitProposal not implemented") +} +func (*UnimplementedMsgServer) Vote(ctx context.Context, req *MsgVote) (*MsgVoteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Vote not implemented") +} +func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SubmitProposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSubmitProposal) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SubmitProposal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Msg/SubmitProposal", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SubmitProposal(ctx, req.(*MsgSubmitProposal)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Vote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVote) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Vote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Msg/Vote", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Vote(ctx, req.(*MsgVote)) + } + 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 { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Deposit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.gov.v1beta1.Msg/Deposit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Deposit(ctx, req.(*MsgDeposit)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.gov.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SubmitProposal", + Handler: _Msg_SubmitProposal_Handler, + }, + { + MethodName: "Vote", + Handler: _Msg_Vote_Handler, + }, + { + MethodName: "Deposit", + Handler: _Msg_Deposit_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/gov/v1beta1/tx.proto", +} + +func (m *MsgSubmitProposal) 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 *MsgSubmitProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Proposer) > 0 { + i -= len(m.Proposer) + copy(dAtA[i:], m.Proposer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Proposer))) + i-- + dAtA[i] = 0x1a + } + if len(m.InitialDeposit) > 0 { + for iNdEx := len(m.InitialDeposit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.InitialDeposit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Content != nil { + { + size, err := m.Content.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 *MsgSubmitProposalResponse) 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 *MsgSubmitProposalResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitProposalResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProposalId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ProposalId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgVote) 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 *MsgVote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Option != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Option)) + i-- + dAtA[i] = 0x18 + } + 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) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteResponse) 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) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDeposit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDeposit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[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.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintTx(dAtA, i, uint64(len(m.Depositor))) + 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 *MsgDepositResponse) 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 *MsgDepositResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDepositResponse) 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 *MsgSubmitProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovTx(uint64(l)) + } + if len(m.InitialDeposit) > 0 { + for _, e := range m.InitialDeposit { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Proposer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitProposalResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovTx(uint64(m.ProposalId)) + } + return n +} + +func (m *MsgVote) 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 m.Option != 0 { + n += 1 + sovTx(uint64(m.Option)) + } + return n +} + +func (m *MsgVoteResponse) 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 + } + var l int + _ = l + if m.ProposalId != 0 { + n += 1 + sovTx(uint64(m.ProposalId)) + } + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgDepositResponse) 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 *MsgSubmitProposal) 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: MsgSubmitProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", 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.Content == nil { + m.Content = &types.Any{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialDeposit", 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.InitialDeposit = append(m.InitialDeposit, types1.Coin{}) + if err := m.InitialDeposit[len(m.InitialDeposit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proposer", 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.Proposer = 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 *MsgSubmitProposalResponse) 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: MsgSubmitProposalResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitProposalResponse: 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 + } + } + 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 *MsgVote) 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: MsgVote: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVote: 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 != 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 ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Option |= VoteOption(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVoteResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + 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: MsgVoteResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDeposit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + 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: MsgDeposit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDeposit: 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 Depositor", 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.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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.Amount = append(m.Amount, types1.Coin{}) + if err := m.Amount[len(m.Amount)-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 *MsgDepositResponse) 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: MsgDepositResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDepositResponse: 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/gov/types/vote.go b/x/gov/types/vote.go index ca49fdb6fa52..bc67a49a743e 100644 --- a/x/gov/types/vote.go +++ b/x/gov/types/vote.go @@ -1,85 +1,66 @@ package types import ( - "encoding/json" "fmt" + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" ) -// Vote -type Vote struct { - ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // proposalID of the proposal - Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter - Option VoteOption `json:"option" yaml:"option"` // option from OptionSet chosen by the voter -} - // NewVote creates a new Vote instance +//nolint:interfacer func NewVote(proposalID uint64, voter sdk.AccAddress, option VoteOption) Vote { - return Vote{proposalID, voter, option} + return Vote{proposalID, voter.String(), option} } func (v Vote) String() string { - return fmt.Sprintf("voter %s voted with option %s on proposal %d", v.Voter, v.Option, v.ProposalID) + out, _ := yaml.Marshal(v) + return string(out) } // Votes is a collection of Vote objects type Votes []Vote +// Equal returns true if two slices (order-dependant) of votes are equal. +func (v Votes) Equal(other Votes) bool { + if len(v) != len(other) { + return false + } + + for i, vote := range v { + if vote.String() != other[i].String() { + return false + } + } + + return true +} + func (v Votes) String() string { if len(v) == 0 { return "[]" } - out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalID) + out := fmt.Sprintf("Votes for Proposal %d:", v[0].ProposalId) for _, vot := range v { out += fmt.Sprintf("\n %s: %s", vot.Voter, vot.Option) } return out } -// Equals returns whether two votes are equal. -func (v Vote) Equals(comp Vote) bool { - return v.Voter.Equals(comp.Voter) && - v.ProposalID == comp.ProposalID && - v.Option == comp.Option -} - // Empty returns whether a vote is empty. func (v Vote) Empty() bool { - return v.Equals(Vote{}) + return v.String() == Vote{}.String() } -// VoteOption defines a vote option -type VoteOption byte - -// Vote options -const ( - OptionEmpty VoteOption = 0x00 - OptionYes VoteOption = 0x01 - OptionAbstain VoteOption = 0x02 - OptionNo VoteOption = 0x03 - OptionNoWithVeto VoteOption = 0x04 -) - // VoteOptionFromString returns a VoteOption from a string. It returns an error // if the string is invalid. func VoteOptionFromString(str string) (VoteOption, error) { - switch str { - case "Yes": - return OptionYes, nil - - case "Abstain": - return OptionAbstain, nil - - case "No": - return OptionNo, nil - - case "NoWithVeto": - return OptionNoWithVeto, nil - - default: - return VoteOption(0xff), fmt.Errorf("'%s' is not a valid vote option", str) + option, ok := VoteOption_value[str] + if !ok { + return OptionEmpty, fmt.Errorf("'%s' is not a valid vote option, available options: yes/no/no_with_veto/abstain", str) } + return VoteOption(option), nil } // ValidVoteOption returns true if the vote option is valid and false otherwise. @@ -104,44 +85,6 @@ func (vo *VoteOption) Unmarshal(data []byte) error { return nil } -// Marshals to JSON using string. -func (vo VoteOption) MarshalJSON() ([]byte, error) { - return json.Marshal(vo.String()) -} - -// UnmarshalJSON decodes from JSON assuming Bech32 encoding. -func (vo *VoteOption) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - bz2, err := VoteOptionFromString(s) - if err != nil { - return err - } - - *vo = bz2 - return nil -} - -// String implements the Stringer interface. -func (vo VoteOption) String() string { - switch vo { - case OptionYes: - return "Yes" - case OptionAbstain: - return "Abstain" - case OptionNo: - return "No" - case OptionNoWithVeto: - return "NoWithVeto" - default: - return "" - } -} - // Format implements the fmt.Formatter interface. func (vo VoteOption) Format(s fmt.State, verb rune) { switch verb { diff --git a/x/ibc/applications/transfer/client/cli/cli.go b/x/ibc/applications/transfer/client/cli/cli.go new file mode 100644 index 000000000000..d3ca8341e95c --- /dev/null +++ b/x/ibc/applications/transfer/client/cli/cli.go @@ -0,0 +1,42 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" +) + +// GetQueryCmd returns the query commands for IBC connections +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: "ibc-transfer", + Short: "IBC fungible token transfer query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + queryCmd.AddCommand( + GetCmdQueryDenomTrace(), + GetCmdQueryDenomTraces(), + GetCmdParams(), + ) + + return queryCmd +} + +// NewTxCmd returns the transaction commands for IBC fungible token transfer +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: "ibc-transfer", + Short: "IBC fungible token transfer transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewTransferTxCmd(), + ) + + return txCmd +} diff --git a/x/ibc/applications/transfer/client/cli/query.go b/x/ibc/applications/transfer/client/cli/query.go new file mode 100644 index 000000000000..7d281bb4155f --- /dev/null +++ b/x/ibc/applications/transfer/client/cli/query.go @@ -0,0 +1,109 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// GetCmdQueryDenomTrace defines the command to query a a denomination trace from a given hash. +func GetCmdQueryDenomTrace() *cobra.Command { + cmd := &cobra.Command{ + Use: "denom-trace [hash]", + Short: "Query the denom trace info from a given trace hash", + Long: "Query the denom trace info from a given trace hash", + Example: fmt.Sprintf("%s query ibc-transfer denom-trace [hash]", version.AppName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryDenomTraceRequest{ + Hash: args[0], + } + + res, err := queryClient.DenomTrace(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// GetCmdQueryDenomTraces defines the command to query all the denomination trace infos +// that this chain mantains. +func GetCmdQueryDenomTraces() *cobra.Command { + cmd := &cobra.Command{ + Use: "denom-traces", + Short: "Query the trace info for all token denominations", + Long: "Query the trace info for all token denominations", + Example: fmt.Sprintf("%s query ibc-transfer denom-traces", version.AppName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryDenomTracesRequest{ + Pagination: pageReq, + } + + res, err := queryClient.DenomTraces(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "denominations trace") + + return cmd +} + +// GetCmdParams returns the command handler for ibc-transfer parameter querying. +func GetCmdParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current ibc-transfer parameters", + Long: "Query the current ibc-transfer parameters", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s query ibc-transfer params", version.AppName), + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, _ := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + return clientCtx.PrintProto(res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/applications/transfer/client/cli/tx.go b/x/ibc/applications/transfer/client/cli/tx.go new file mode 100644 index 000000000000..c9c6fde51794 --- /dev/null +++ b/x/ibc/applications/transfer/client/cli/tx.go @@ -0,0 +1,113 @@ +package cli + +import ( + "fmt" + "strings" + + "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/version" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channelutils "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/utils" +) + +const ( + flagPacketTimeoutHeight = "packet-timeout-height" + flagPacketTimeoutTimestamp = "packet-timeout-timestamp" + flagAbsoluteTimeouts = "absolute-timeouts" +) + +// NewTransferTxCmd returns the command to create a NewMsgTransfer transaction +func NewTransferTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "transfer [src-port] [src-channel] [receiver] [amount]", + Short: "Transfer a fungible token through IBC", + Long: strings.TrimSpace(`Transfer a fungible token through IBC. Timeouts can be specified +as absolute or relative using the "absolute-timeouts" flag. Timeout height can be set by passing in the height string +in the form {revision}-{height} using the "packet-timeout-height" flag. Relative timeouts are added to +the block height and block timestamp queried from the latest consensus state corresponding +to the counterparty channel. Any timeout set to 0 is disabled.`), + Example: fmt.Sprintf("%s tx ibc-transfer transfer [src-port] [src-channel] [receiver] [amount]", version.AppName), + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + sender := clientCtx.GetFromAddress() + srcPort := args[0] + srcChannel := args[1] + receiver := args[2] + + coin, err := sdk.ParseCoinNormalized(args[3]) + if err != nil { + return err + } + + if !strings.HasPrefix(coin.Denom, "ibc/") { + denomTrace := types.ParseDenomTrace(coin.Denom) + coin.Denom = denomTrace.IBCDenom() + } + + timeoutHeightStr, err := cmd.Flags().GetString(flagPacketTimeoutHeight) + if err != nil { + return err + } + timeoutHeight, err := clienttypes.ParseHeight(timeoutHeightStr) + if err != nil { + return err + } + + timeoutTimestamp, err := cmd.Flags().GetUint64(flagPacketTimeoutTimestamp) + if err != nil { + return err + } + + absoluteTimeouts, err := cmd.Flags().GetBool(flagAbsoluteTimeouts) + if err != nil { + return err + } + + // if the timeouts are not absolute, retrieve latest block height and block timestamp + // for the consensus state connected to the destination port/channel + if !absoluteTimeouts { + consensusState, height, _, err := channelutils.QueryLatestConsensusState(clientCtx, srcPort, srcChannel) + if err != nil { + return err + } + + if !timeoutHeight.IsZero() { + absoluteHeight := height + absoluteHeight.RevisionNumber += timeoutHeight.RevisionNumber + absoluteHeight.RevisionHeight += timeoutHeight.RevisionHeight + timeoutHeight = absoluteHeight + } + + if timeoutTimestamp != 0 { + timeoutTimestamp = consensusState.GetTimestamp() + timeoutTimestamp + } + } + + msg := types.NewMsgTransfer( + srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(flagPacketTimeoutHeight, types.DefaultRelativePacketTimeoutHeight, "Packet timeout block height. The timeout is disabled when set to 0-0.") + cmd.Flags().Uint64(flagPacketTimeoutTimestamp, types.DefaultRelativePacketTimeoutTimestamp, "Packet timeout timestamp in nanoseconds. Default is 10 minutes. The timeout is disabled when set to 0.") + cmd.Flags().Bool(flagAbsoluteTimeouts, false, "Timeout flags are used as absolute timeouts.") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/applications/transfer/handler.go b/x/ibc/applications/transfer/handler.go new file mode 100644 index 000000000000..7c992c920e72 --- /dev/null +++ b/x/ibc/applications/transfer/handler.go @@ -0,0 +1,23 @@ +package transfer + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// NewHandler returns sdk.Handler for IBC token transfer module messages +func NewHandler(k types.MsgServer) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case *types.MsgTransfer: + res, err := k.Transfer(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized ICS-20 transfer message type: %T", msg) + } + } +} diff --git a/x/ibc/applications/transfer/handler_test.go b/x/ibc/applications/transfer/handler_test.go new file mode 100644 index 000000000000..92a04210115d --- /dev/null +++ b/x/ibc/applications/transfer/handler_test.go @@ -0,0 +1,123 @@ +package transfer_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + 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" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type TransferTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + chainC *ibctesting.TestChain +} + +func (suite *TransferTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +// constructs a send from chainA to chainB on the established channel/connection +// and sends the same coin back from chainB to chainA. +func (suite *TransferTestSuite) TestHandleMsgTransfer() { + // setup between chainA and chainB + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB := suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + // originalBalance := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom) + timeoutHeight := clienttypes.NewHeight(0, 110) + + coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + + // send from chainA to chainB + msg := types.NewMsgTransfer(channelA.PortID, channelA.ID, coinToSendToB, suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + + err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, clientB, msg) + suite.Require().NoError(err) // message committed + + // relay send + fungibleTokenPacket := types.NewFungibleTokenPacketData(coinToSendToB.Denom, coinToSendToB.Amount.Uint64(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) + packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + err = suite.coordinator.RelayPacket(suite.chainA, suite.chainB, clientA, clientB, packet, ack.GetBytes()) + suite.Require().NoError(err) // relay committed + + // check that voucher exists on chain B + voucherDenomTrace := types.ParseDenomTrace(types.GetPrefixedDenom(packet.GetDestPort(), packet.GetDestChannel(), sdk.DefaultBondDenom)) + balance := suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) + + coinSentFromAToB := types.GetTransferCoin(channelB.PortID, channelB.ID, sdk.DefaultBondDenom, 100) + suite.Require().Equal(coinSentFromAToB, balance) + + // setup between chainB to chainC + clientOnBForC, clientOnCForB, connOnBForC, connOnCForB := suite.coordinator.SetupClientConnections(suite.chainB, suite.chainC, exported.Tendermint) + channelOnBForC, channelOnCForB := suite.coordinator.CreateTransferChannels(suite.chainB, suite.chainC, connOnBForC, connOnCForB, channeltypes.UNORDERED) + + // send from chainB to chainC + msg = types.NewMsgTransfer(channelOnBForC.PortID, channelOnBForC.ID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0) + + err = suite.coordinator.SendMsg(suite.chainB, suite.chainC, clientOnCForB, msg) + suite.Require().NoError(err) // message committed + + // relay send + // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment + fullDenomPath := types.GetPrefixedDenom(channelOnCForB.PortID, channelOnCForB.ID, voucherDenomTrace.GetFullDenomPath()) + fungibleTokenPacket = types.NewFungibleTokenPacketData(voucherDenomTrace.GetFullDenomPath(), coinSentFromAToB.Amount.Uint64(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String()) + packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelOnBForC.PortID, channelOnBForC.ID, channelOnCForB.PortID, channelOnCForB.ID, timeoutHeight, 0) + err = suite.coordinator.RelayPacket(suite.chainB, suite.chainC, clientOnBForC, clientOnCForB, packet, ack.GetBytes()) + suite.Require().NoError(err) // relay committed + + coinSentFromBToC := sdk.NewInt64Coin(types.ParseDenomTrace(fullDenomPath).IBCDenom(), 100) + balance = suite.chainC.App.BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coinSentFromBToC.Denom) + + // check that the balance is updated on chainC + suite.Require().Equal(coinSentFromBToC, balance) + + // check that balance on chain B is empty + balance = suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromBToC.Denom) + suite.Require().Zero(balance.Amount.Int64()) + + // send from chainC back to chainB + msg = types.NewMsgTransfer(channelOnCForB.PortID, channelOnCForB.ID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0) + + err = suite.coordinator.SendMsg(suite.chainC, suite.chainB, clientOnBForC, msg) + suite.Require().NoError(err) // message committed + + // relay send + // NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment + fungibleTokenPacket = types.NewFungibleTokenPacketData(fullDenomPath, coinSentFromBToC.Amount.Uint64(), suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) + packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelOnCForB.PortID, channelOnCForB.ID, channelOnBForC.PortID, channelOnBForC.ID, timeoutHeight, 0) + err = suite.coordinator.RelayPacket(suite.chainC, suite.chainB, clientOnCForB, clientOnBForC, packet, ack.GetBytes()) + suite.Require().NoError(err) // relay committed + + balance = suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromAToB.Denom) + + // check that the balance on chainA returned back to the original state + suite.Require().Equal(coinSentFromAToB, balance) + + // check that module account escrow address is empty + escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) + balance = suite.chainB.App.BankKeeper.GetBalance(suite.chainB.GetContext(), escrowAddress, sdk.DefaultBondDenom) + suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), balance) + + // check that balance on chain B is empty + balance = suite.chainC.App.BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) + suite.Require().Zero(balance.Amount.Int64()) +} + +func TestTransferTestSuite(t *testing.T) { + suite.Run(t, new(TransferTestSuite)) +} diff --git a/x/ibc/applications/transfer/keeper/MBT_README.md b/x/ibc/applications/transfer/keeper/MBT_README.md new file mode 100644 index 000000000000..8a5930f6d390 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/MBT_README.md @@ -0,0 +1,51 @@ +## Token Transfer Model-based Testing Guide + +In the process of IBC Audit performed by Informal Systems, we have implemented +a preliminary set of model-based tests for the ICS-20 Token Transfer implementation. + +Model-based tests are based on the formal `TLA+` model of the Token transfer relay functions: see [relay.tla](relay_model/relay.tla). +The tests themselves are simple `TLA+` assertions, that describe the desired shape of execution that send or receive tokens; +see [relay_tests.tla](relay_model/relay_tests.tla) for some examples. +To be able to specify test assertions the TLA+ model contains the `history` variable, +which records the whole execution history. +So, by way of referring to `history` you simply specify declaratively what execution history you want to see. + +After you have specified your `TLA+` test, you can run it using [Apalache model checker](https://github.com/informalsystems/apalache). +E.g. for the test `TestUnescrowTokens` run + +```bash +apalache-mc check --inv=TestUnescrowTokensInv relay_tests.tla +``` + +In case there are no error in the TLA+ model or in the test assertions, this will produce a couple of so-called _counterexamples_. +This is a terminology from the model-checking community; for the testing purposes they can be considered simply as model executions. +See the files `counterexample.tla` for human-readable representation, and `counterexample.json` for machine-readable one. + +In order to execute the produced test, you need to translate it into another format. +For that translation you need the tool [Jsonatr (JSON Arrifact Translator)](https://github.com/informalsystems/jsonatr). +It performs the translation using this [transformation spec](relay_model/apalache-to-relay-test2.json); + +To transform a counterexample into a test, run + +```bash +jsonatr --use apalache-to-relay-test2.json --in counterexample.json --out model_based_tests/YourTestName.json +``` + +Now, if you run `go test` in this directory, the file you have produced above should be picked up by the [model-based test driver](mbt_relay_test.go), +and executed automatically. + + +The easiest way to run Apalache is by +[using a Docker image](https://github.com/informalsystems/apalache/blob/master/docs/manual.md#useDocker); +to run Jsonatr you need to locally clone the repository, and then, +after building it, add the `target/debug` directory into your `PATH`. + +To wrap Apalache docker image into an executable you might create the following executable bash script `apalache-mc`: + +```bash +#!/bin/bash +docker run --rm -v $(pwd):/var/apalache apalache/mc $@ +``` + + +In case of any questions please don't hesitate to contact Andrey Kuprianov (andrey@informal.systems). \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/encoding.go b/x/ibc/applications/transfer/keeper/encoding.go new file mode 100644 index 000000000000..ddb1bc4b0c52 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/encoding.go @@ -0,0 +1,35 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// UnmarshalDenomTrace attempts to decode and return an DenomTrace object from +// raw encoded bytes. +func (k Keeper) UnmarshalDenomTrace(bz []byte) (types.DenomTrace, error) { + var denomTrace types.DenomTrace + if err := k.cdc.UnmarshalBinaryBare(bz, &denomTrace); err != nil { + return types.DenomTrace{}, err + } + return denomTrace, nil +} + +// MustUnmarshalDenomTrace attempts to decode and return an DenomTrace object from +// raw encoded bytes. It panics on error. +func (k Keeper) MustUnmarshalDenomTrace(bz []byte) types.DenomTrace { + var denomTrace types.DenomTrace + k.cdc.MustUnmarshalBinaryBare(bz, &denomTrace) + return denomTrace +} + +// MarshalDenomTrace attempts to encode an DenomTrace object and returns the +// raw encoded bytes. +func (k Keeper) MarshalDenomTrace(denomTrace types.DenomTrace) ([]byte, error) { + return k.cdc.MarshalBinaryBare(&denomTrace) +} + +// MustMarshalDenomTrace attempts to encode an DenomTrace object and returns the +// raw encoded bytes. It panics on error. +func (k Keeper) MustMarshalDenomTrace(denomTrace types.DenomTrace) []byte { + return k.cdc.MustMarshalBinaryBare(&denomTrace) +} diff --git a/x/ibc/applications/transfer/keeper/genesis.go b/x/ibc/applications/transfer/keeper/genesis.go new file mode 100644 index 000000000000..58a0c0811573 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/genesis.go @@ -0,0 +1,45 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// InitGenesis initializes the ibc-transfer state and binds to PortID. +func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { + k.SetPort(ctx, state.PortId) + + for _, trace := range state.DenomTraces { + k.SetDenomTrace(ctx, trace) + } + + // Only try to bind to port if it is not already bound, since we may already own + // port capability from capability InitGenesis + if !k.IsBound(ctx, state.PortId) { + // transfer module binds to the transfer port on InitChain + // and claims the returned capability + err := k.BindPort(ctx, state.PortId) + if err != nil { + panic(fmt.Sprintf("could not claim port capability: %v", err)) + } + } + + k.SetParams(ctx, state.Params) + + // check if the module account exists + moduleAcc := k.GetTransferAccount(ctx) + if moduleAcc == nil { + panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) + } +} + +// ExportGenesis exports ibc-transfer module's portID and denom trace info into its genesis state. +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{ + PortId: k.GetPort(ctx), + DenomTraces: k.GetAllDenomTraces(ctx), + Params: k.GetParams(ctx), + } +} diff --git a/x/ibc/applications/transfer/keeper/genesis_test.go b/x/ibc/applications/transfer/keeper/genesis_test.go new file mode 100644 index 000000000000..a85434911fb8 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/genesis_test.go @@ -0,0 +1,39 @@ +package keeper_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +func (suite *KeeperTestSuite) TestGenesis() { + var ( + path string + traces types.Traces + ) + + for i := 0; i < 5; i++ { + prefix := fmt.Sprintf("transfer/channelToChain%d", i) + if i == 0 { + path = prefix + } else { + path = prefix + "/" + path + } + + denomTrace := types.DenomTrace{ + BaseDenom: "uatom", + Path: path, + } + traces = append(types.Traces{denomTrace}, traces...) + suite.chainA.App.TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), denomTrace) + } + + genesis := suite.chainA.App.TransferKeeper.ExportGenesis(suite.chainA.GetContext()) + + suite.Require().Equal(types.PortID, genesis.PortId) + suite.Require().Equal(traces.Sort(), genesis.DenomTraces) + + suite.Require().NotPanics(func() { + suite.chainA.App.TransferKeeper.InitGenesis(suite.chainA.GetContext(), *genesis) + }) +} diff --git a/x/ibc/applications/transfer/keeper/grpc_query.go b/x/ibc/applications/transfer/keeper/grpc_query.go new file mode 100644 index 000000000000..b6347895b42b --- /dev/null +++ b/x/ibc/applications/transfer/keeper/grpc_query.go @@ -0,0 +1,83 @@ +package keeper + +import ( + "context" + "fmt" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +var _ types.QueryServer = Keeper{} + +// DenomTrace implements the Query/DenomTrace gRPC method +func (q Keeper) DenomTrace(c context.Context, req *types.QueryDenomTraceRequest) (*types.QueryDenomTraceResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + hash, err := types.ParseHexHash(req.Hash) + if err != nil { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid denom trace hash %s, %s", req.Hash, err)) + } + + ctx := sdk.UnwrapSDKContext(c) + denomTrace, found := q.GetDenomTrace(ctx, hash) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrap(types.ErrTraceNotFound, req.Hash).Error(), + ) + } + + return &types.QueryDenomTraceResponse{ + DenomTrace: &denomTrace, + }, nil +} + +// DenomTraces implements the Query/DenomTraces gRPC method +func (q Keeper) DenomTraces(c context.Context, req *types.QueryDenomTracesRequest) (*types.QueryDenomTracesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + + traces := types.Traces{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), types.DenomTraceKey) + + pageRes, err := query.Paginate(store, req.Pagination, func(_, value []byte) error { + result, err := q.UnmarshalDenomTrace(value) + if err != nil { + return err + } + + traces = append(traces, result) + return nil + }) + + if err != nil { + return nil, err + } + + return &types.QueryDenomTracesResponse{ + DenomTraces: traces.Sort(), + Pagination: pageRes, + }, nil +} + +// Params implements the Query/Params gRPC method +func (q Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := q.GetParams(ctx) + + return &types.QueryParamsResponse{ + Params: ¶ms, + }, nil +} diff --git a/x/ibc/applications/transfer/keeper/grpc_query_test.go b/x/ibc/applications/transfer/keeper/grpc_query_test.go new file mode 100644 index 000000000000..0b16e0726b58 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/grpc_query_test.go @@ -0,0 +1,142 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +func (suite *KeeperTestSuite) TestQueryDenomTrace() { + var ( + req *types.QueryDenomTraceRequest + expTrace types.DenomTrace + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "invalid hex hash", + func() { + req = &types.QueryDenomTraceRequest{ + Hash: "!@#!@#!", + } + }, + false, + }, + { + "not found denom trace", + func() { + expTrace.Path = "transfer/channelToA/transfer/channelToB" + expTrace.BaseDenom = "uatom" + req = &types.QueryDenomTraceRequest{ + Hash: expTrace.Hash().String(), + } + }, + false, + }, + { + "success", + func() { + expTrace.Path = "transfer/channelToA/transfer/channelToB" + expTrace.BaseDenom = "uatom" + suite.chainA.App.TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), expTrace) + + req = &types.QueryDenomTraceRequest{ + Hash: expTrace.Hash().String(), + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.queryClient.DenomTrace(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(&expTrace, res.DenomTrace) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryDenomTraces() { + var ( + req *types.QueryDenomTracesRequest + expTraces = types.Traces(nil) + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty pagination", + func() { + req = &types.QueryDenomTracesRequest{} + }, + true, + }, + { + "success", + func() { + expTraces = append(expTraces, types.DenomTrace{Path: "", BaseDenom: "uatom"}) + expTraces = append(expTraces, types.DenomTrace{Path: "transfer/channelToB", BaseDenom: "uatom"}) + expTraces = append(expTraces, types.DenomTrace{Path: "transfer/channelToA/transfer/channelToB", BaseDenom: "uatom"}) + + for _, trace := range expTraces { + suite.chainA.App.TransferKeeper.SetDenomTrace(suite.chainA.GetContext(), trace) + } + + req = &types.QueryDenomTracesRequest{ + Pagination: &query.PageRequest{ + Limit: 5, + CountTotal: false, + }, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.queryClient.DenomTraces(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expTraces.Sort(), res.DenomTraces) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryParams() { + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + expParams := types.DefaultParams() + res, _ := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) + suite.Require().Equal(&expParams, res.Params) +} diff --git a/x/ibc/applications/transfer/keeper/keeper.go b/x/ibc/applications/transfer/keeper/keeper.go new file mode 100644 index 000000000000..a2eebb55e1ea --- /dev/null +++ b/x/ibc/applications/transfer/keeper/keeper.go @@ -0,0 +1,169 @@ +package keeper + +import ( + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Keeper defines the IBC fungible transfer keeper +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + paramSpace paramtypes.Subspace + + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + authKeeper types.AccountKeeper + bankKeeper types.BankKeeper + scopedKeeper capabilitykeeper.ScopedKeeper +} + +// NewKeeper creates a new IBC transfer Keeper instance +func NewKeeper( + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace paramtypes.Subspace, + channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, + authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, +) Keeper { + + // ensure ibc transfer module account is set + if addr := authKeeper.GetModuleAddress(types.ModuleName); addr == nil { + panic("the IBC transfer module account has not been set") + } + + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + cdc: cdc, + storeKey: key, + paramSpace: paramSpace, + channelKeeper: channelKeeper, + portKeeper: portKeeper, + authKeeper: authKeeper, + bankKeeper: bankKeeper, + scopedKeeper: scopedKeeper, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) +} + +// GetTransferAccount returns the ICS20 - transfers ModuleAccount +func (k Keeper) GetTransferAccount(ctx sdk.Context) authtypes.ModuleAccountI { + return k.authKeeper.GetModuleAccount(ctx, types.ModuleName) +} + +// ChanCloseInit defines a wrapper function for the channel Keeper's function +// in order to expose it to the ICS20 transfer handler. +func (k Keeper) ChanCloseInit(ctx sdk.Context, portID, channelID string) error { + capName := host.ChannelCapabilityPath(portID, channelID) + chanCap, ok := k.scopedKeeper.GetCapability(ctx, capName) + if !ok { + return sdkerrors.Wrapf(channeltypes.ErrChannelCapabilityNotFound, "could not retrieve channel capability at: %s", capName) + } + return k.channelKeeper.ChanCloseInit(ctx, portID, channelID, chanCap) +} + +// IsBound checks if the transfer module is already bound to the desired port +func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok +} + +// BindPort defines a wrapper function for the ort Keeper's function in +// order to expose it to module's InitGenesis function +func (k Keeper) BindPort(ctx sdk.Context, portID string) error { + cap := k.portKeeper.BindPort(ctx, portID) + return k.ClaimCapability(ctx, cap, host.PortPath(portID)) +} + +// GetPort returns the portID for the transfer module. Used in ExportGenesis +func (k Keeper) GetPort(ctx sdk.Context) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(types.PortKey)) +} + +// SetPort sets the portID for the transfer module. Used in InitGenesis +func (k Keeper) SetPort(ctx sdk.Context, portID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PortKey, []byte(portID)) +} + +// GetDenomTrace retreives the full identifiers trace and base denomination from the store. +func (k Keeper) GetDenomTrace(ctx sdk.Context, denomTraceHash tmbytes.HexBytes) (types.DenomTrace, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DenomTraceKey) + bz := store.Get(denomTraceHash) + if bz == nil { + return types.DenomTrace{}, false + } + + denomTrace := k.MustUnmarshalDenomTrace(bz) + return denomTrace, true +} + +// HasDenomTrace checks if a the key with the given denomination trace hash exists on the store. +func (k Keeper) HasDenomTrace(ctx sdk.Context, denomTraceHash tmbytes.HexBytes) bool { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DenomTraceKey) + return store.Has(denomTraceHash) +} + +// SetDenomTrace sets a new {trace hash -> denom trace} pair to the store. +func (k Keeper) SetDenomTrace(ctx sdk.Context, denomTrace types.DenomTrace) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.DenomTraceKey) + bz := k.MustMarshalDenomTrace(denomTrace) + store.Set(denomTrace.Hash(), bz) +} + +// GetAllDenomTraces returns the trace information for all the denominations. +func (k Keeper) GetAllDenomTraces(ctx sdk.Context) types.Traces { + traces := types.Traces{} + k.IterateDenomTraces(ctx, func(denomTrace types.DenomTrace) bool { + traces = append(traces, denomTrace) + return false + }) + + return traces.Sort() +} + +// IterateDenomTraces iterates over the denomination traces in the store +// and performs a callback function. +func (k Keeper) IterateDenomTraces(ctx sdk.Context, cb func(denomTrace types.DenomTrace) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DenomTraceKey) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + + denomTrace := k.MustUnmarshalDenomTrace(iterator.Value()) + if cb(denomTrace) { + break + } + } +} + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.scopedKeeper.AuthenticateCapability(ctx, cap, name) +} + +// ClaimCapability allows the transfer module that can claim a capability that IBC module +// passes to it +func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { + return k.scopedKeeper.ClaimCapability(ctx, cap, name) +} diff --git a/x/ibc/applications/transfer/keeper/keeper_test.go b/x/ibc/applications/transfer/keeper/keeper_test.go new file mode 100644 index 000000000000..cce9cbccae45 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/keeper_test.go @@ -0,0 +1,51 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + chainC *ibctesting.TestChain + + queryClient types.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(2)) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.chainA.GetContext(), suite.chainA.App.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.chainA.App.TransferKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +func (suite *KeeperTestSuite) TestGetTransferAccount() { + expectedMaccAddr := sdk.AccAddress(crypto.AddressHash([]byte(types.ModuleName))) + + macc := suite.chainA.App.TransferKeeper.GetTransferAccount(suite.chainA.GetContext()) + + suite.Require().NotNil(macc) + suite.Require().Equal(types.ModuleName, macc.GetName()) + suite.Require().Equal(expectedMaccAddr, macc.GetAddress()) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/ibc/applications/transfer/keeper/mbt_relay_test.go b/x/ibc/applications/transfer/keeper/mbt_relay_test.go new file mode 100644 index 000000000000..defcbbbc8db6 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/mbt_relay_test.go @@ -0,0 +1,394 @@ +package keeper_test + +/// This file is a test driver for model-based tests generated from the TLA+ model of token transfer +/// Written by Andrey Kuprianov within the scope of IBC Audit performed by Informal Systems. +/// In case of any questions please don't hesitate to contact andrey@informal.systems. + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "strconv" + "strings" + + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type TlaBalance struct { + Address []string `json:"address"` + Denom []string `json:"denom"` + Amount int64 `json:"amount"` +} + +type TlaFungibleTokenPacketData struct { + Sender string `json:"sender"` + Receiver string `json:"receiver"` + Amount int `json:"amount"` + Denom []string `json:"denom"` +} + +type TlaFungibleTokenPacket struct { + SourceChannel string `json:"sourceChannel"` + SourcePort string `json:"sourcePort"` + DestChannel string `json:"destChannel"` + DestPort string `json:"destPort"` + Data TlaFungibleTokenPacketData `json:"data"` +} + +type TlaOnRecvPacketTestCase = struct { + // The required subset of bank balances + BankBefore []TlaBalance `json:"bankBefore"` + // The packet to process + Packet TlaFungibleTokenPacket `json:"packet"` + // The handler to call + Handler string `json:"handler"` + // The expected changes in the bank + BankAfter []TlaBalance `json:"bankAfter"` + // Whether OnRecvPacket should fail or not + Error bool `json:"error"` +} + +type FungibleTokenPacket struct { + SourceChannel string + SourcePort string + DestChannel string + DestPort string + Data types.FungibleTokenPacketData +} + +type OnRecvPacketTestCase = struct { + description string + // The required subset of bank balances + bankBefore []Balance + // The packet to process + packet FungibleTokenPacket + // The handler to call + handler string + // The expected bank state after processing (wrt. bankBefore) + bankAfter []Balance + // Whether OnRecvPacket should pass or fail + pass bool +} + +type OwnedCoin struct { + Address string + Denom string +} + +type Balance struct { + Id string + Address string + Denom string + Amount sdk.Int +} + +func AddressFromString(address string) string { + return sdk.AccAddress(crypto.AddressHash([]byte(address))).String() +} + +func AddressFromTla(addr []string) string { + if len(addr) != 3 { + panic("failed to convert from TLA+ address: wrong number of address components") + } + s := "" + if len(addr[0]) == 0 && len(addr[1]) == 0 { + // simple address: id + s = addr[2] + } else if len(addr[2]) == 0 { + // escrow address: ics20-1\x00port/channel + s = fmt.Sprintf("%s\x00%s/%s", types.Version, addr[0], addr[1]) + } else { + panic("failed to convert from TLA+ address: neither simple nor escrow address") + } + return s +} + +func DenomFromTla(denom []string) string { + var i int + for i = 0; i+1 < len(denom) && len(denom[i]) == 0 && len(denom[i+1]) == 0; i += 2 { + // skip empty prefixes + } + return strings.Join(denom[i:], "/") +} + +func BalanceFromTla(balance TlaBalance) Balance { + return Balance{ + Id: AddressFromTla(balance.Address), + Address: AddressFromString(AddressFromTla(balance.Address)), + Denom: DenomFromTla(balance.Denom), + Amount: sdk.NewInt(balance.Amount), + } +} + +func BalancesFromTla(tla []TlaBalance) []Balance { + balances := make([]Balance, 0) + for _, b := range tla { + balances = append(balances, BalanceFromTla(b)) + } + return balances +} + +func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPacket { + return FungibleTokenPacket{ + SourceChannel: packet.SourceChannel, + SourcePort: packet.SourcePort, + DestChannel: packet.DestChannel, + DestPort: packet.DestPort, + Data: types.NewFungibleTokenPacketData( + DenomFromTla(packet.Data.Denom), + uint64(packet.Data.Amount), + AddressFromString(packet.Data.Sender), + AddressFromString(packet.Data.Receiver)), + } +} + +func OnRecvPacketTestCaseFromTla(tc TlaOnRecvPacketTestCase) OnRecvPacketTestCase { + return OnRecvPacketTestCase{ + description: "auto-generated", + bankBefore: BalancesFromTla(tc.BankBefore), + packet: FungibleTokenPacketFromTla(tc.Packet), + handler: tc.Handler, + bankAfter: BalancesFromTla(tc.BankAfter), // TODO different semantics + pass: !tc.Error, + } +} + +var addressMap = make(map[string]string) + +type Bank struct { + balances map[OwnedCoin]sdk.Int +} + +// Make an empty bank +func MakeBank() Bank { + return Bank{balances: make(map[OwnedCoin]sdk.Int)} +} + +// Subtract other bank from this bank +func (bank *Bank) Sub(other *Bank) Bank { + diff := MakeBank() + for coin, amount := range bank.balances { + otherAmount, exists := other.balances[coin] + if exists { + diff.balances[coin] = amount.Sub(otherAmount) + } else { + diff.balances[coin] = amount + } + } + for coin, amount := range other.balances { + if _, exists := bank.balances[coin]; !exists { + diff.balances[coin] = amount.Neg() + } + } + return diff +} + +// Set specific bank balance +func (bank *Bank) SetBalance(address string, denom string, amount sdk.Int) { + bank.balances[OwnedCoin{address, denom}] = amount +} + +// Set several balances at once +func (bank *Bank) SetBalances(balances []Balance) { + for _, balance := range balances { + bank.balances[OwnedCoin{balance.Address, balance.Denom}] = balance.Amount + addressMap[balance.Address] = balance.Id + } +} + +func NullCoin() OwnedCoin { + return OwnedCoin{ + Address: AddressFromString(""), + Denom: "", + } +} + +// Set several balances at once +func BankFromBalances(balances []Balance) Bank { + bank := MakeBank() + for _, balance := range balances { + coin := OwnedCoin{balance.Address, balance.Denom} + if coin != NullCoin() { // ignore null coin + bank.balances[coin] = balance.Amount + addressMap[balance.Address] = balance.Id + } + } + return bank +} + +// String representation of all bank balances +func (bank *Bank) String() string { + str := "" + for coin, amount := range bank.balances { + str += coin.Address + if addressMap[coin.Address] != "" { + str += "(" + addressMap[coin.Address] + ")" + } + str += " : " + coin.Denom + " = " + amount.String() + "\n" + } + return str +} + +// String representation of non-zero bank balances +func (bank *Bank) NonZeroString() string { + str := "" + for coin, amount := range bank.balances { + if !amount.IsZero() { + str += coin.Address + " : " + coin.Denom + " = " + amount.String() + "\n" + } + } + return str +} + +// Construct a bank out of the chain bank +func BankOfChain(chain *ibctesting.TestChain) Bank { + bank := MakeBank() + chain.App.BankKeeper.IterateAllBalances(chain.GetContext(), func(address sdk.AccAddress, coin sdk.Coin) (stop bool) { + fullDenom := coin.Denom + if strings.HasPrefix(coin.Denom, "ibc/") { + fullDenom, _ = chain.App.TransferKeeper.DenomPathFromHash(chain.GetContext(), coin.Denom) + } + bank.SetBalance(address.String(), fullDenom, coin.Amount) + return false + }) + 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) + bankChange := bankAfter.Sub(bankBefore) + diff := bankChange.Sub(expectedBankChange) + NonZeroString := diff.NonZeroString() + if len(NonZeroString) != 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "Unexpected changes in the bank: \n"+NonZeroString) + } + return nil +} + +func (suite *KeeperTestSuite) TestModelBasedRelay() { + dirname := "model_based_tests/" + files, err := ioutil.ReadDir(dirname) + if err != nil { + panic(fmt.Errorf("Failed to read model-based test files: %w", err)) + } + for _, file_info := range files { + var tlaTestCases = []TlaOnRecvPacketTestCase{} + if !strings.HasSuffix(file_info.Name(), ".json") { + continue + } + jsonBlob, err := ioutil.ReadFile(dirname + file_info.Name()) + if err != nil { + panic(fmt.Errorf("Failed to read JSON test fixture: %w", err)) + } + err = json.Unmarshal([]byte(jsonBlob), &tlaTestCases) + if err != nil { + panic(fmt.Errorf("Failed to parse JSON test fixture: %w", err)) + } + + suite.SetupTest() + _, _, connAB, connBA := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + _, _, connBC, connCB := suite.coordinator.SetupClientConnections(suite.chainB, suite.chainC, exported.Tendermint) + suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connAB, connBA, channeltypes.UNORDERED) + suite.coordinator.CreateTransferChannels(suite.chainB, suite.chainC, connBC, connCB, channeltypes.UNORDERED) + + for i, tlaTc := range tlaTestCases { + tc := OnRecvPacketTestCaseFromTla(tlaTc) + registerDenom := func() { + denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) + traceHash := denomTrace.Hash() + if !suite.chainB.App.TransferKeeper.HasDenomTrace(suite.chainB.GetContext(), traceHash) { + suite.chainB.App.TransferKeeper.SetDenomTrace(suite.chainB.GetContext(), denomTrace) + } + } + + description := file_info.Name() + " # " + strconv.Itoa(i+1) + suite.Run(fmt.Sprintf("Case %s", description), func() { + seq := uint64(1) + packet := channeltypes.NewPacket(tc.packet.Data.GetBytes(), seq, tc.packet.SourcePort, tc.packet.SourceChannel, tc.packet.DestPort, tc.packet.DestChannel, clienttypes.NewHeight(0, 100), 0) + bankBefore := BankFromBalances(tc.bankBefore) + realBankBefore := BankOfChain(suite.chainB) + // First validate the packet itself (mimics what happens when the packet is being sent and/or received) + err := packet.ValidateBasic() + if err != nil { + suite.Require().False(tc.pass, err.Error()) + return + } + switch tc.handler { + case "SendTransfer": + var sender sdk.AccAddress + sender, err = sdk.AccAddressFromBech32(tc.packet.Data.Sender) + if err != nil { + panic("MBT failed to convert sender address") + } + registerDenom() + denomTrace := types.ParseDenomTrace(tc.packet.Data.Denom) + denom := denomTrace.IBCDenom() + err = sdk.ValidateDenom(denom) + if err == nil { + err = suite.chainB.App.TransferKeeper.SendTransfer( + suite.chainB.GetContext(), + tc.packet.SourcePort, + tc.packet.SourceChannel, + sdk.NewCoin(denom, sdk.NewIntFromUint64(tc.packet.Data.Amount)), + sender, + tc.packet.Data.Receiver, + clienttypes.NewHeight(0, 110), + 0) + } + case "OnRecvPacket": + err = suite.chainB.App.TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, tc.packet.Data) + case "OnTimeoutPacket": + registerDenom() + err = suite.chainB.App.TransferKeeper.OnTimeoutPacket(suite.chainB.GetContext(), packet, tc.packet.Data) + case "OnRecvAcknowledgementResult": + err = suite.chainB.App.TransferKeeper.OnAcknowledgementPacket( + suite.chainB.GetContext(), packet, tc.packet.Data, + channeltypes.NewResultAcknowledgement(nil)) + case "OnRecvAcknowledgementError": + registerDenom() + err = suite.chainB.App.TransferKeeper.OnAcknowledgementPacket( + suite.chainB.GetContext(), packet, tc.packet.Data, + channeltypes.NewErrorAcknowledgement("MBT Error Acknowledgement")) + default: + err = fmt.Errorf("Unknown handler: %s", tc.handler) + } + if err != nil { + suite.Require().False(tc.pass, err.Error()) + return + } + bankAfter := BankFromBalances(tc.bankAfter) + expectedBankChange := bankAfter.Sub(&bankBefore) + if err := suite.CheckBankBalances(suite.chainB, &realBankBefore, &expectedBankChange); err != nil { + suite.Require().False(tc.pass, err.Error()) + return + } + suite.Require().True(tc.pass) + }) + } + } +} diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.json b/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.json new file mode 100644 index 000000000000..6ccdccc8aeb2 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.json @@ -0,0 +1,492 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a3", + "amount": 2, + "denom": [ + "", + "", + "", + "", + "btc" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "ethereum-hub", + "sourcePort": "channel-0", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "a3", + "amount": 1, + "denom": [ + "cosmos-hub", + "", + "", + "", + "btc" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "error": true + }, + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a2", + "receiver": "a2", + "amount": 4, + "denom": [ + "", + "", + "ethereum-hub", + "cosmos-hub", + "atom" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "ethereum-hub", + "cosmos-hub", + "atom" + ], + "amount": 4 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "", + "receiver": "a2", + "amount": 4, + "denom": [ + "", + "", + "ethereum-hub", + "cosmos-hub", + "atom" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "ethereum-hub", + "cosmos-hub", + "atom" + ], + "amount": 4 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "ethereum-hub", + "cosmos-hub", + "atom" + ], + "amount": 8 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "cosmos-hub", + "sourcePort": "bitcoin-hub", + "destChannel": "channel-0", + "destPort": "channel-1", + "data": { + "sender": "a1", + "receiver": "", + "amount": 1, + "denom": [ + "transfer", + "channel-0", + "transfer", + "channel-0", + "atom" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "ethereum-hub", + "cosmos-hub", + "atom" + ], + "amount": 8 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "ethereum-hub", + "cosmos-hub", + "atom" + ], + "amount": 8 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-1", + "btc" + ], + "amount": 2 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.tla b/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.tla new file mode 100644 index 000000000000..9691eec2f261 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/Test5Packets.tla @@ -0,0 +1,1056 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 3 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> "cosmos-hub"]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "channel-0"] + +(* Transition 0 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 2 +/\ error = TRUE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> "cosmos-hub"]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "channel-0"]] +/\ p = [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a2"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 5 to State5 *) + +State5 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 3 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> "cosmos-hub"]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "channel-0"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a2"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 5 to State6 *) + +State6 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 4 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> "cosmos-hub"]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "channel-0"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a2"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 4 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "channel-1", + sourceChannel |-> "cosmos-hub", + sourcePort |-> "bitcoin-hub"] + +(* Transition 0 to State7 *) + +State7 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 5 +/\ error = TRUE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> "cosmos-hub"]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "channel-0"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a2"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 4 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 4 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 4, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 5 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 8 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "channel-1", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "channel-1", + sourceChannel |-> "cosmos-hub", + sourcePort |-> "bitcoin-hub"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + count >= 5 + /\ BMC!Skolem((\E s1$2 \in DOMAIN history: + BMC!Skolem((\E s2$2 \in DOMAIN history: + ~(history[s1$2]["handler"] = history[s2$2]["handler"]))))) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:52:41 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.json new file mode 100644 index 000000000000..6a039f3eca45 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.json @@ -0,0 +1,612 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a2", + "amount": 3, + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ] + } + }, + "handler": "OnTimeoutPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a2", + "receiver": "a1", + "amount": 3, + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnRecvAcknowledgementError", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 3, + "denom": [ + "", + "", + "cosmos-hub", + "cosmos-hub", + "atom" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "atom" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "cosmos-hub", + "sourcePort": "bitcoin-hub", + "destChannel": "transfer", + "destPort": "cosmos-hub", + "data": { + "sender": "a1", + "receiver": "", + "amount": 2, + "denom": [ + "", + "channel-0", + "channel-1", + "channel-1", + "" + ] + } + }, + "handler": "OnRecvAcknowledgementResult", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "atom" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "atom" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a3", + "amount": 1, + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "atom" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 3 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "atom" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-1", + "cosmos-hub", + "cosmos-hub", + "btc" + ], + "amount": 3 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 2 + }, + { + "address": [ + "transfer", + "channel-1", + "" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "eth" + ], + "amount": 1 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.tla new file mode 100644 index 000000000000..89e6d87be527 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/Test5PacketsAllDifferentPass.tla @@ -0,0 +1,1188 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 6 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnTimeoutPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 10 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3 +/\ count = 2 +/\ error = FALSE +/\ handler = "OnRecvAcknowledgementError" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 5 to State5 *) + +State5 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3 +/\ count = 3 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-1", port |-> "channel-1"], + prefix1 |-> [channel |-> "channel-0", port |-> ""]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "transfer", + destPort |-> "cosmos-hub", + sourceChannel |-> "cosmos-hub", + sourcePort |-> "bitcoin-hub"] + +(* Transition 12 to State6 *) + +State6 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3 +/\ count = 4 +/\ error = FALSE +/\ handler = "OnRecvAcknowledgementResult" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 4 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementResult", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-1", port |-> "channel-1"], + prefix1 |-> [channel |-> "channel-0", port |-> ""]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "transfer", + destPort |-> "cosmos-hub", + sourceChannel |-> "cosmos-hub", + sourcePort |-> "bitcoin-hub"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 1 to State7 *) + +State7 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 1 +/\ count = 5 +/\ error = FALSE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 4 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementResult", + packet |-> + [data |-> + [amount |-> 2, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-1", port |-> "channel-1"], + prefix1 |-> [channel |-> "channel-0", port |-> ""]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "transfer", + destPort |-> "cosmos-hub", + sourceChannel |-> "cosmos-hub", + sourcePort |-> "bitcoin-hub"]] + @@ 5 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 3 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "cosmos-hub", port |-> "transfer"]], + receiver |-> "", + sender |-> ""], + destChannel |-> "bitcoin-hub", + destPort |-> "ethereum-hub", + sourceChannel |-> "transfer", + sourcePort |-> "channel-1"] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + (count >= 5 + /\ (\A s1$2 \in DOMAIN history: + \A s2$2 \in DOMAIN history: + s1$2 = s2$2 \/ ~(history[s1$2]["handler"] = history[s2$2]["handler"]))) + /\ (\A s$2 \in DOMAIN history: + s$2 <= 0 + \/ (history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 12:49:42 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.json new file mode 100644 index 000000000000..f1f553210b58 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "", + "sourcePort": "", + "destChannel": "", + "destPort": "", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 1, + "denom": [ + "cosmos-hub", + "transfer", + "channel-0", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnRecvAcknowledgementError", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.tla new file mode 100644 index 000000000000..583b3211dca8 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorFail.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* Transition 7 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = TRUE +/\ handler = "OnRecvAcknowledgementError" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> TRUE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvAcknowledgementError" + /\ history[s$2]["error"] = TRUE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:15:18 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.json new file mode 100644 index 000000000000..3fbfe7fdf09d --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.json @@ -0,0 +1,159 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "", + "receiver": "a1", + "amount": 1, + "denom": [ + "", + "", + "channel-0", + "ethereum-hub", + "btc" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "channel-0", + "ethereum-hub", + "btc" + ], + "amount": 1 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 1, + "denom": [ + "transfer", + "channel-1", + "channel-0", + "ethereum-hub", + "btc" + ] + } + }, + "handler": "OnRecvAcknowledgementError", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "channel-0", + "ethereum-hub", + "btc" + ], + "amount": 1 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "channel-0", + "ethereum-hub", + "btc" + ], + "amount": 2 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.tla new file mode 100644 index 000000000000..cd43eb2647e1 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementErrorPass.tla @@ -0,0 +1,310 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 2 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 11 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 2 +/\ count = 2 +/\ error = FALSE +/\ handler = "OnRecvAcknowledgementError" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementError", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "ethereum-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvAcknowledgementError" + /\ history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:14:33 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.json new file mode 100644 index 000000000000..9110a38ab653 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "", + "sourcePort": "", + "destChannel": "", + "destPort": "", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 1, + "denom": [ + "cosmos-hub", + "transfer", + "channel-0", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnRecvAcknowledgementResult", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.tla new file mode 100644 index 000000000000..b97ec73a3ddc --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultFail.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* Transition 13 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = TRUE +/\ handler = "OnRecvAcknowledgementResult" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> TRUE, + handler |-> "OnRecvAcknowledgementResult", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvAcknowledgementResult" + /\ history[s$2]["error"] = TRUE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:13:42 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.json new file mode 100644 index 000000000000..5215df7da332 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "ethereum-hub", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "ethereum-hub", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 1, + "denom": [ + "cosmos-hub", + "transfer", + "channel-0", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnRecvAcknowledgementResult", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.tla new file mode 100644 index 000000000000..f9d049c54636 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvAcknowledgementResultPass.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "ethereum-hub", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "ethereum-hub", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "transfer"] + +(* Transition 12 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvAcknowledgementResult" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "ethereum-hub", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvAcknowledgementResult", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "ethereum-hub", + sourceChannel |-> "ethereum-hub", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvAcknowledgementResult" + /\ history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:12:59 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.json new file mode 100644 index 000000000000..9a7e8c406e71 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "", + "receiver": "", + "amount": 1, + "denom": [ + "", + "", + "transfer", + "channel-0", + "" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.tla new file mode 100644 index 000000000000..980be28ae28d --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketFail.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 3 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = TRUE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> TRUE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvPacket" + /\ history[s$2]["error"] = TRUE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:02:31 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.json new file mode 100644 index 000000000000..35f94c572081 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.json @@ -0,0 +1,73 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "", + "receiver": "a2", + "amount": 1, + "denom": [ + "", + "", + "ethereum-hub", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-0", + "ethereum-hub", + "cosmos-hub", + "btc" + ], + "amount": 1 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.tla new file mode 100644 index 000000000000..342b097febb0 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnRecvPacketPass.tla @@ -0,0 +1,174 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 5 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> + [channel |-> "cosmos-hub", port |-> "ethereum-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnRecvPacket" + /\ history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:01:28 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.json new file mode 100644 index 000000000000..a78ed85ca585 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "", + "sourcePort": "", + "destChannel": "", + "destPort": "", + "data": { + "sender": "a1", + "receiver": "a2", + "amount": 1, + "denom": [ + "cosmos-hub", + "transfer", + "channel-0", + "cosmos-hub", + "btc" + ] + } + }, + "handler": "OnTimeoutPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.tla new file mode 100644 index 000000000000..1bc209d9d5ec --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutFail.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* Transition 6 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = TRUE +/\ handler = "OnTimeoutPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> TRUE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "channel-0"], + prefix1 |-> [channel |-> "transfer", port |-> "cosmos-hub"]], + receiver |-> "a2", + sender |-> "a1"], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnTimeoutPacket" + /\ history[s$2]["error"] = TRUE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:09:25 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.json new file mode 100644 index 000000000000..3136aace654b --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.json @@ -0,0 +1,159 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a1", + "amount": 1, + "denom": [ + "", + "", + "bitcoin-hub", + "transfer", + "btc" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "bitcoin-hub", + "transfer", + "btc" + ], + "amount": 1 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "", + "amount": 1, + "denom": [ + "transfer", + "channel-1", + "bitcoin-hub", + "transfer", + "btc" + ] + } + }, + "handler": "OnTimeoutPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "bitcoin-hub", + "transfer", + "btc" + ], + "amount": 1 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "transfer", + "channel-1", + "bitcoin-hub", + "transfer", + "btc" + ], + "amount": 2 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.tla new file mode 100644 index 000000000000..5dc5a994ae3e --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestOnTimeoutPass.tla @@ -0,0 +1,310 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 2 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 10 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 2 +/\ count = 2 +/\ error = FALSE +/\ handler = "OnTimeoutPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]] + >> + :> 1, + error |-> FALSE, + handler |-> "OnTimeoutPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-1", port |-> "transfer"]], + receiver |-> "", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "OnTimeoutPacket" + /\ history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:07:37 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.json new file mode 100644 index 000000000000..01d589d8677a --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.json @@ -0,0 +1,58 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "", + "receiver": "", + "amount": 1, + "denom": [ + "", + "", + "", + "", + "" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "error": true + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.tla new file mode 100644 index 000000000000..dc3a1c008bdf --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferFail.tla @@ -0,0 +1,159 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 0 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 1 +/\ error = TRUE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> TRUE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "SendTransfer" + /\ history[s$2]["error"] = TRUE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 11:00:34 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.json new file mode 100644 index 000000000000..452d2b3aa943 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.json @@ -0,0 +1,174 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a2", + "amount": 1, + "denom": [ + "", + "", + "cosmos-hub", + "cosmos-hub", + "eth" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-0", + "cosmos-hub", + "cosmos-hub", + "eth" + ], + "amount": 1 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a2", + "receiver": "a1", + "amount": 1, + "denom": [ + "transfer", + "channel-0", + "cosmos-hub", + "cosmos-hub", + "eth" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-0", + "cosmos-hub", + "cosmos-hub", + "eth" + ], + "amount": 1 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a2" + ], + "denom": [ + "transfer", + "channel-0", + "cosmos-hub", + "cosmos-hub", + "eth" + ], + "amount": 0 + }, + { + "address": [ + "transfer", + "channel-1", + "" + ], + "denom": [ + "transfer", + "channel-0", + "cosmos-hub", + "cosmos-hub", + "eth" + ], + "amount": 1 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.tla new file mode 100644 index 000000000000..23c45c6773ec --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestSendTransferPass.tla @@ -0,0 +1,323 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 2 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 1 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 0 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1 +/\ count = 2 +/\ error = FALSE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a2", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 0 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a2", port |-> ""], [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]] + >> + :> 1, + error |-> FALSE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "eth", + prefix0 |-> [channel |-> "cosmos-hub", port |-> "cosmos-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a2"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "", + sender |-> ""], + destChannel |-> "", + destPort |-> "", + sourceChannel |-> "", + sourcePort |-> ""] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + BMC!Skolem((\E s$2 \in DOMAIN history: + history[s$2]["handler"] = "SendTransfer" + /\ history[s$2]["error"] = FALSE + /\ history[s$2]["packet"]["data"]["amount"] > 0)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 10:58:54 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.json b/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.json new file mode 100644 index 000000000000..985522070477 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.json @@ -0,0 +1,305 @@ +[ + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "a3", + "amount": 5, + "denom": [ + "", + "", + "", + "", + "atom" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 5 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-1", + "sourcePort": "transfer", + "destChannel": "channel-0", + "destPort": "transfer", + "data": { + "sender": "a3", + "receiver": "a1", + "amount": 3, + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ] + } + }, + "handler": "SendTransfer", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 5 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 2 + }, + { + "address": [ + "transfer", + "channel-1", + "" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 3 + } + ], + "error": false + }, + { + "packet": { + "sourceChannel": "channel-0", + "sourcePort": "transfer", + "destChannel": "channel-1", + "destPort": "transfer", + "data": { + "sender": "a1", + "receiver": "a1", + "amount": 1, + "denom": [ + "transfer", + "channel-0", + "transfer", + "channel-0", + "atom" + ] + } + }, + "handler": "OnRecvPacket", + "bankBefore": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 2 + }, + { + "address": [ + "transfer", + "channel-1", + "" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 3 + } + ], + "bankAfter": [ + { + "address": [ + "", + "", + "" + ], + "denom": [ + "", + "", + "", + "", + "" + ], + "amount": 0 + }, + { + "address": [ + "", + "", + "a1" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 1 + }, + { + "address": [ + "", + "", + "a3" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 2 + }, + { + "address": [ + "transfer", + "channel-1", + "" + ], + "denom": [ + "", + "", + "transfer", + "channel-0", + "atom" + ], + "amount": 2 + } + ], + "error": false + } +] \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.tla b/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.tla new file mode 100644 index 000000000000..e99081c12378 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/model_based_tests/TestUnescrowTokens.tla @@ -0,0 +1,563 @@ +------------------------- MODULE counterexample ------------------------- + +EXTENDS relay_tests + +(* Initial state *) + +State1 == +TRUE +(* Transition 0 to State2 *) + +State2 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 +/\ count = 0 +/\ error = FALSE +/\ handler = "" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 3 to State3 *) + +State3 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5 +/\ count = 1 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"] + +(* Transition 1 to State4 *) + +State4 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3 +/\ count = 2 +/\ error = FALSE +/\ handler = "SendTransfer" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5, + error |-> FALSE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* Transition 4 to State5 *) + +State5 == +/\ bank = << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 1 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 +/\ count = 3 +/\ error = FALSE +/\ handler = "OnRecvPacket" +/\ history = 0 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 1 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 5, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a3", + sender |-> "a1"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] + @@ 2 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 5, + error |-> FALSE, + handler |-> "SendTransfer", + packet |-> + [data |-> + [amount |-> 3, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]], + receiver |-> "a1", + sender |-> "a3"], + destChannel |-> "channel-0", + destPort |-> "transfer", + sourceChannel |-> "channel-1", + sourcePort |-> "transfer"]] + @@ 3 + :> [bankAfter |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a1", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 1 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2, + bankBefore |-> + << + [channel |-> "", id |-> "", port |-> ""], [denom |-> "", + prefix0 |-> [channel |-> "", port |-> ""], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 0 + @@ << + [channel |-> "", id |-> "a3", port |-> ""], [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 2 + @@ << + [channel |-> "channel-1", id |-> "", port |-> "transfer"], [denom |-> + "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "", port |-> ""]] + >> + :> 3, + error |-> FALSE, + handler |-> "OnRecvPacket", + packet |-> + [data |-> + [amount |-> 1, + denomTrace |-> + [denom |-> "atom", + prefix0 |-> [channel |-> "channel-0", port |-> "transfer"], + prefix1 |-> [channel |-> "channel-0", port |-> "transfer"]], + receiver |-> "a1", + sender |-> "a1"], + destChannel |-> "channel-1", + destPort |-> "transfer", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"]] +/\ p = [data |-> + [amount |-> 0, + denomTrace |-> + [denom |-> "btc", + prefix0 |-> [channel |-> "transfer", port |-> "bitcoin-hub"], + prefix1 |-> [channel |-> "channel-0", port |-> "channel-1"]], + receiver |-> "a1", + sender |-> ""], + destChannel |-> "ethereum-hub", + destPort |-> "cosmos-hub", + sourceChannel |-> "channel-0", + sourcePort |-> "transfer"] + +(* The following formula holds true in the last state and violates the invariant *) + +InvariantViolation == + history[1]["handler"] = "OnRecvPacket" + /\ BMC!Skolem((\E s$2 \in DOMAIN history: + ((IF history[s$2]["packet"]["data"]["denomTrace"]["prefix0"] + = [port |-> "", channel |-> ""] + THEN [port |-> "", channel |-> ""] + ELSE IF history[s$2]["packet"]["data"]["denomTrace"]["prefix1"] + = [port |-> "", channel |-> ""] + THEN history[s$2]["packet"]["data"]["denomTrace"]["prefix0"] + ELSE history[s$2]["packet"]["data"]["denomTrace"]["prefix1"])[ + "port" + ] + = history[s$2]["packet"]["sourcePort"] + /\ (IF history[s$2]["packet"]["data"]["denomTrace"]["prefix0"] + = [port |-> "", channel |-> ""] + THEN [port |-> "", channel |-> ""] + ELSE IF history[s$2]["packet"]["data"]["denomTrace"]["prefix1"] + = [port |-> "", channel |-> ""] + THEN history[s$2]["packet"]["data"]["denomTrace"]["prefix0"] + ELSE history[s$2]["packet"]["data"]["denomTrace"]["prefix1"])[ + "channel" + ] + = history[s$2]["packet"]["sourceChannel"]) + /\ history[s$2]["handler"] = "OnRecvPacket" + /\ history[s$2]["error"] = FALSE)) + +================================================================================ +\* Created by Apalache on Thu Dec 10 13:38:11 CET 2020 +\* https://github.com/informalsystems/apalache diff --git a/x/ibc/applications/transfer/keeper/msg_server.go b/x/ibc/applications/transfer/keeper/msg_server.go new file mode 100644 index 000000000000..dd2999af341e --- /dev/null +++ b/x/ibc/applications/transfer/keeper/msg_server.go @@ -0,0 +1,43 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +var _ types.MsgServer = Keeper{} + +// See createOutgoingPacket in spec:https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay + +// Transfer defines a rpc handler method for MsgTransfer. +func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.MsgTransferResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return nil, err + } + if err := k.SendTransfer( + ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, + ); err != nil { + return nil, err + } + + k.Logger(ctx).Info("IBC fungible token transfer", "token", msg.Token.Denom, "amount", msg.Token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTransfer, + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), + sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + ), + }) + + return &types.MsgTransferResponse{}, nil +} diff --git a/x/ibc/applications/transfer/keeper/params.go b/x/ibc/applications/transfer/keeper/params.go new file mode 100644 index 000000000000..39a6c5d53de8 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/params.go @@ -0,0 +1,30 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// GetSendEnabled retrieves the send enabled boolean from the paramstore +func (k Keeper) GetSendEnabled(ctx sdk.Context) bool { + var res bool + k.paramSpace.Get(ctx, types.KeySendEnabled, &res) + return res +} + +// GetReceiveEnabled retrieves the receive enabled boolean from the paramstore +func (k Keeper) GetReceiveEnabled(ctx sdk.Context) bool { + var res bool + k.paramSpace.Get(ctx, types.KeyReceiveEnabled, &res) + return res +} + +// GetParams returns the total set of ibc-transfer parameters. +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + return types.NewParams(k.GetSendEnabled(ctx), k.GetReceiveEnabled(ctx)) +} + +// SetParams sets the total set of ibc-transfer parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/ibc/applications/transfer/keeper/params_test.go b/x/ibc/applications/transfer/keeper/params_test.go new file mode 100644 index 000000000000..96f17ff7f1cb --- /dev/null +++ b/x/ibc/applications/transfer/keeper/params_test.go @@ -0,0 +1,15 @@ +package keeper_test + +import "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + +func (suite *KeeperTestSuite) TestParams() { + expParams := types.DefaultParams() + + params := suite.chainA.App.TransferKeeper.GetParams(suite.chainA.GetContext()) + suite.Require().Equal(expParams, params) + + expParams.SendEnabled = false + suite.chainA.App.TransferKeeper.SetParams(suite.chainA.GetContext(), expParams) + params = suite.chainA.App.TransferKeeper.GetParams(suite.chainA.GetContext()) + suite.Require().Equal(expParams, params) +} diff --git a/x/ibc/applications/transfer/keeper/relay.go b/x/ibc/applications/transfer/keeper/relay.go new file mode 100644 index 000000000000..4889014a40af --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay.go @@ -0,0 +1,406 @@ +package keeper + +import ( + "fmt" + "strings" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// SendTransfer handles transfer sending logic. There are 2 possible cases: +// +// 1. Sender chain is acting as the source zone. The coins are transferred +// to an escrow address (i.e locked) on the sender chain and then transferred +// to the receiving chain through IBC TAO logic. It is expected that the +// receiving chain will mint vouchers to the receiving address. +// +// 2. Sender chain is acting as the sink zone. The coins (vouchers) are burned +// on the sender chain and then transferred to the receiving chain though IBC +// TAO logic. It is expected that the receiving chain, which had previously +// sent the original denomination, will unescrow the fungible token and send +// it to the receiving address. +// +// Another way of thinking of source and sink zones is through the token's +// timeline. Each send to any chain other than the one it was previously +// received from is a movement forwards in the token's timeline. This causes +// trace to be added to the token's history and the destination port and +// destination channel to be prefixed to the denomination. In these instances +// the sender chain is acting as the source zone. When the token is sent back +// to the chain it previously received from, the prefix is removed. This is +// a backwards movement in the token's timeline and the sender chain +// is acting as the sink zone. +// +// Example: +// These steps of transfer occur: A -> B -> C -> A -> C -> B -> A +// +// 1. A -> B : sender chain is source zone. Denom upon receiving: 'B/denom' +// 2. B -> C : sender chain is source zone. Denom upon receiving: 'C/B/denom' +// 3. C -> A : sender chain is source zone. Denom upon receiving: 'A/C/B/denom' +// 4. A -> C : sender chain is sink zone. Denom upon receiving: 'C/B/denom' +// 5. C -> B : sender chain is sink zone. Denom upon receiving: 'B/denom' +// 6. B -> A : sender chain is sink zone. Denom upon receiving: 'denom' +func (k Keeper) SendTransfer( + ctx sdk.Context, + sourcePort, + sourceChannel string, + token sdk.Coin, + sender sdk.AccAddress, + receiver string, + timeoutHeight clienttypes.Height, + timeoutTimestamp uint64, +) error { + + if !k.GetSendEnabled(ctx) { + return types.ErrSendDisabled + } + + sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) + if !found { + return sdkerrors.Wrapf(channeltypes.ErrChannelNotFound, "port ID (%s) channel ID (%s)", sourcePort, sourceChannel) + } + + destinationPort := sourceChannelEnd.GetCounterparty().GetPortID() + destinationChannel := sourceChannelEnd.GetCounterparty().GetChannelID() + + // get the next sequence + sequence, found := k.channelKeeper.GetNextSequenceSend(ctx, sourcePort, sourceChannel) + if !found { + return sdkerrors.Wrapf( + channeltypes.ErrSequenceSendNotFound, + "source port: %s, source channel: %s", sourcePort, sourceChannel, + ) + } + + // begin createOutgoingPacket logic + // See spec for this logic: https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#packet-relay + channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel)) + if !ok { + return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") + } + + // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic + fullDenomPath := token.Denom + + var err error + + // deconstruct the token denomination into the denomination trace info + // to determine if the sender is the source chain + if strings.HasPrefix(token.Denom, "ibc/") { + fullDenomPath, err = k.DenomPathFromHash(ctx, token.Denom) + if err != nil { + return err + } + } + + labels := []metrics.Label{ + telemetry.NewLabel("destination-port", destinationPort), + telemetry.NewLabel("destination-channel", destinationChannel), + } + + // NOTE: SendTransfer simply sends the denomination as it exists on its own + // chain inside the packet data. The receiving chain will perform denom + // prefixing as necessary. + + if types.SenderChainIsSource(sourcePort, sourceChannel, fullDenomPath) { + labels = append(labels, telemetry.NewLabel("source", "true")) + + // create the escrow address for the tokens + escrowAddress := types.GetEscrowAddress(sourcePort, sourceChannel) + + // escrow source tokens. It fails if balance insufficient. + if err := k.bankKeeper.SendCoins( + ctx, sender, escrowAddress, sdk.NewCoins(token), + ); err != nil { + return err + } + + } else { + labels = append(labels, telemetry.NewLabel("source", "false")) + + // transfer the coins to the module account and burn them + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, sender, types.ModuleName, sdk.NewCoins(token), + ); err != nil { + return err + } + + if err := k.bankKeeper.BurnCoins( + ctx, types.ModuleName, sdk.NewCoins(token), + ); err != nil { + // NOTE: should not happen as the module account was + // retrieved on the step above and it has enough balace + // to burn. + panic(fmt.Sprintf("cannot burn coins after a successful send to a module account: %v", err)) + } + } + + packetData := types.NewFungibleTokenPacketData( + fullDenomPath, token.Amount.Uint64(), sender.String(), receiver, + ) + + packet := channeltypes.NewPacket( + packetData.GetBytes(), + sequence, + sourcePort, + sourceChannel, + destinationPort, + destinationChannel, + timeoutHeight, + timeoutTimestamp, + ) + + if err := k.channelKeeper.SendPacket(ctx, channelCap, packet); err != nil { + return err + } + + defer func() { + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", "ibc", "transfer"}, + float32(token.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", fullDenomPath)}, + ) + + telemetry.IncrCounterWithLabels( + []string{"ibc", types.ModuleName, "send"}, + 1, + labels, + ) + }() + + return nil +} + +// OnRecvPacket processes a cross chain fungible token transfer. If the +// sender chain is the source of minted tokens then vouchers will be minted +// and sent to the receiving address. Otherwise if the sender chain is sending +// back tokens this chain originally transferred to it, the tokens are +// unescrowed and sent to the receiving address. +func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { + // validate packet data upon receiving + if err := data.ValidateBasic(); err != nil { + return err + } + + if !k.GetReceiveEnabled(ctx) { + return types.ErrReceiveDisabled + } + + // decode the receiver address + receiver, err := sdk.AccAddressFromBech32(data.Receiver) + if err != nil { + return err + } + + labels := []metrics.Label{ + telemetry.NewLabel("source-port", packet.GetSourcePort()), + telemetry.NewLabel("source-channel", packet.GetSourceChannel()), + } + + // This is the prefix that would have been prefixed to the denomination + // on sender chain IF and only if the token originally came from the + // receiving chain. + // + // NOTE: We use SourcePort and SourceChannel here, because the counterparty + // chain would have prefixed with DestPort and DestChannel when originally + // receiving this coin as seen in the "sender chain is the source" condition. + + if types.ReceiverChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { + // sender chain is not the source, unescrow tokens + + // remove prefix added by sender chain + voucherPrefix := types.GetDenomPrefix(packet.GetSourcePort(), packet.GetSourceChannel()) + unprefixedDenom := data.Denom[len(voucherPrefix):] + + // coin denomination used in sending from the escrow address + denom := unprefixedDenom + + // The denomination used to send the coins is either the native denom or the hash of the path + // if the denomination is not native. + denomTrace := types.ParseDenomTrace(unprefixedDenom) + if denomTrace.Path != "" { + denom = denomTrace.IBCDenom() + } + token := sdk.NewCoin(denom, sdk.NewIntFromUint64(data.Amount)) + + // unescrow tokens + escrowAddress := types.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) + if err := k.bankKeeper.SendCoins(ctx, escrowAddress, receiver, sdk.NewCoins(token)); err != nil { + // NOTE: this error is only expected to occur given an unexpected bug or a malicious + // counterparty module. The bug may occur in bank or any part of the code that allows + // the escrow address to be drained. A malicious counterparty module could drain the + // escrow address by allowing more tokens to be sent back then were escrowed. + return sdkerrors.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + } + + defer func() { + telemetry.SetGaugeWithLabels( + []string{"ibc", types.ModuleName, "packet", "receive"}, + float32(data.Amount), + []metrics.Label{telemetry.NewLabel("denom", unprefixedDenom)}, + ) + + telemetry.IncrCounterWithLabels( + []string{"ibc", types.ModuleName, "receive"}, + 1, + append( + labels, telemetry.NewLabel("source", "true"), + ), + ) + }() + + return nil + } + + // sender chain is the source, mint vouchers + + // since SendPacket did not prefix the denomination, we must prefix denomination here + sourcePrefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel()) + // NOTE: sourcePrefix contains the trailing "/" + prefixedDenom := sourcePrefix + data.Denom + + // construct the denomination trace from the full raw denomination + denomTrace := types.ParseDenomTrace(prefixedDenom) + + traceHash := denomTrace.Hash() + if !k.HasDenomTrace(ctx, traceHash) { + k.SetDenomTrace(ctx, denomTrace) + } + + voucherDenom := denomTrace.IBCDenom() + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeDenomTrace, + sdk.NewAttribute(types.AttributeKeyTraceHash, traceHash.String()), + sdk.NewAttribute(types.AttributeKeyDenom, voucherDenom), + ), + ) + + voucher := sdk.NewCoin(voucherDenom, sdk.NewIntFromUint64(data.Amount)) + + // mint new tokens if the source of the transfer is the same chain + if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(voucher), + ); err != nil { + return err + } + + // send to receiver + if err := k.bankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, receiver, sdk.NewCoins(voucher), + ); err != nil { + panic(fmt.Sprintf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) + } + + defer func() { + telemetry.SetGaugeWithLabels( + []string{"ibc", types.ModuleName, "packet", "receive"}, + float32(data.Amount), + []metrics.Label{telemetry.NewLabel("denom", data.Denom)}, + ) + + telemetry.IncrCounterWithLabels( + []string{"ibc", types.ModuleName, "receive"}, + 1, + append( + labels, telemetry.NewLabel("source", "false"), + ), + ) + }() + + return nil +} + +// OnAcknowledgementPacket responds to the the success or failure of a packet +// acknowledgement written on the receiving chain. If the acknowledgement +// was a success then nothing occurs. If the acknowledgement failed, then +// the sender is refunded their tokens using the refundPacketToken function. +func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData, ack channeltypes.Acknowledgement) error { + switch ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + return k.refundPacketToken(ctx, packet, data) + default: + // the acknowledgement succeeded on the receiving chain so nothing + // needs to be executed and no error needs to be returned + return nil + } +} + +// OnTimeoutPacket refunds the sender since the original packet sent was +// never received and has been timed out. +func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { + return k.refundPacketToken(ctx, packet, data) +} + +// refundPacketToken will unescrow and send back the tokens back to sender +// if the sending chain was the source chain. Otherwise, the sent tokens +// were burnt in the original send so new tokens are minted and sent to +// the sending address. +func (k Keeper) refundPacketToken(ctx sdk.Context, packet channeltypes.Packet, data types.FungibleTokenPacketData) error { + // NOTE: packet data type already checked in handler.go + + // parse the denomination from the full denom path + trace := types.ParseDenomTrace(data.Denom) + + token := sdk.NewCoin(trace.IBCDenom(), sdk.NewIntFromUint64(data.Amount)) + + // decode the sender address + sender, err := sdk.AccAddressFromBech32(data.Sender) + if err != nil { + return err + } + + if types.SenderChainIsSource(packet.GetSourcePort(), packet.GetSourceChannel(), data.Denom) { + // unescrow tokens back to sender + escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel()) + if err := k.bankKeeper.SendCoins(ctx, escrowAddress, sender, sdk.NewCoins(token)); err != nil { + // NOTE: this error is only expected to occur given an unexpected bug or a malicious + // counterparty module. The bug may occur in bank or any part of the code that allows + // the escrow address to be drained. A malicious counterparty module could drain the + // escrow address by allowing more tokens to be sent back then were escrowed. + return sdkerrors.Wrap(err, "unable to unescrow tokens, this may be caused by a malicious counterparty module or a bug: please open an issue on counterparty module") + } + + return nil + } + + // mint vouchers back to sender + if err := k.bankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(token), + ); err != nil { + return err + } + + if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, sdk.NewCoins(token)); err != nil { + panic(fmt.Sprintf("unable to send coins from module to account despite previously minting coins to module account: %v", err)) + } + + return nil +} + +// DenomPathFromHash returns the full denomination path prefix from an ibc denom with a hash +// component. +func (k Keeper) DenomPathFromHash(ctx sdk.Context, denom string) (string, error) { + // trim the denomination prefix, by default "ibc/" + hexHash := denom[len(types.DenomPrefix+"/"):] + + hash, err := types.ParseHexHash(hexHash) + if err != nil { + return "", sdkerrors.Wrap(types.ErrInvalidDenomForTransfer, err.Error()) + } + + denomTrace, found := k.GetDenomTrace(ctx, hash) + if !found { + return "", sdkerrors.Wrap(types.ErrTraceNotFound, hexHash) + } + + fullDenomPath := denomTrace.GetFullDenomPath() + return fullDenomPath, nil +} diff --git a/x/ibc/applications/transfer/keeper/relay_model/account.tla b/x/ibc/applications/transfer/keeper/relay_model/account.tla new file mode 100644 index 000000000000..84d743f6da6c --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/account.tla @@ -0,0 +1,36 @@ +-------------------------- MODULE account ---------------------------- + +(** + The accounts interface; please ignore the definition bodies. +*) + +EXTENDS identifiers + +CONSTANT + AccountIds + +\* a non-account +NullAccount == "NullAccount" + +\* All accounts +Accounts == { NullAccount } + +\* Make an escrow account for the given port and channel +MakeEscrowAccount(port, channel) == NullAccount + +\* Make an account from the accound id +MakeAccount(accountId) == NullAccount + +\* Type constraints for accounts +AccountTypeOK == + /\ NullAccount \in Accounts + /\ \A p \in Identifiers, c \in Identifiers: + MakeEscrowAccount(p, c) \in Accounts + /\ \A a \in Identifiers: + MakeAccount(a) \in Accounts + +============================================================================= +\* Modification History +\* Last modified Thu Nov 19 18:21:10 CET 2020 by c +\* Last modified Thu Nov 05 14:44:18 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/account_record.tla b/x/ibc/applications/transfer/keeper/relay_model/account_record.tla new file mode 100644 index 000000000000..c7eed27af1b5 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/account_record.tla @@ -0,0 +1,46 @@ +-------------------------- MODULE account_record ---------------------------- + +(** + The most basic implementation of accounts, which is a union of normal and escrow accounts + Represented via records. +*) + +EXTENDS identifiers + +CONSTANT + AccountIds + +NullAccount == [ + port |-> NullId, + channel |-> NullId, + id |-> NullId +] + +Accounts == [ + port: Identifiers, + channel: Identifiers, + id: AccountIds +] + +MakeEscrowAccount(port, channel) == [ + port |-> port, + channel |-> channel, + id |-> NullId +] + +MakeAccount(accountId) == [ + port |-> NullId, + channel |-> NullId, + id |-> accountId +] + + +ACCOUNT == INSTANCE account +AccountTypeOK == ACCOUNT!AccountTypeOK + + +============================================================================= +\* Modification History +\* Last modified Thu Nov 19 18:21:46 CET 2020 by c +\* Last modified Thu Nov 05 14:49:10 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test.json b/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test.json new file mode 100644 index 000000000000..c8d70a333294 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test.json @@ -0,0 +1,100 @@ +{ + "description": "Transforms an Apalache counterexample into the test for ICS20 Token Transfer OnRecvPacket", + "usage": "jsonatr --use apalache-to-recv-test.json --in counterexample.json --out recv-test.json", + "input": [ + { + "name": "history", + "description": "extract history from the last state of Apalache CE", + "kind": "INLINE", + "source": "$.declarations[-2].body.and..[?(@.eq == 'history')].arg.atat..arg.record" + }, + { + "name": "bankRecordToBalance", + "description": "", + "kind": "INLINE", + "source": { + "address": [ + "$.colonGreater.tuple[0]..[?(@.key.str == 'port')].value.str | unwrap", + "$.colonGreater.tuple[0]..[?(@.key.str == 'channel')].value.str | unwrap", + "$.colonGreater.tuple[0]..[?(@.key.str == 'id')].value.str | unwrap" + ], + "denom": [ + "$.colonGreater.tuple[1]..[?(@.key.str == 'port')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'channel')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'denom')].value.str | unwrap" + ], + "amount": "$.arg | unwrap" + } + }, + { + "name": "bankBefore", + "description": "extract bankBefore from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'bankBefore')].value.atat | unwrap | map(bankRecordToBalance)" + }, + { + "name": "bankAfter", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'bankAfter')].value.atat | unwrap | map(bankRecordToBalance)" + }, + { + "name": "packet", + "description": "extract packet from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'packet')].value.record" + }, + { + "name": "packetData", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'data')].value.record" + }, + { + "name": "packetDataDenom", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'data')].value.record.[?(@.key.str == 'denomTrace')].value.record" + }, + { + "name": "packetRecord", + "description": "decompose packet", + "kind": "INLINE", + "source": { + "sourceChannel" : "$.[?(@.key.str == 'sourceChannel')].value.str | unwrap", + "sourcePort" : "$.[?(@.key.str == 'sourcePort')].value.str | unwrap", + "destChannel" : "$.[?(@.key.str == 'destChannel')].value.str | unwrap", + "destPort" : "$.[?(@.key.str == 'destPort')].value.str | unwrap", + "data": { + "sender": "$packetData.[?(@.key.str == 'sender')].value.str | unwrap", + "receiver": "$packetData.[?(@.key.str == 'receiver')].value.str | unwrap", + "amount": "$packetData.[?(@.key.str == 'amount')].value | unwrap", + "denom": [ + "$packetDataDenom.[?(@.key.str == 'port')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'channel')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'denom')].value.str | unwrap" + ] + } + } + }, + { + "name": "handler", + "description": "extract handler from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'handler')].value.str" + }, + { + "name": "historyState", + "description": "decompose single history state", + "kind": "INLINE", + "source": { + "packet": "$packet | unwrap | packetRecord", + "handler": "$handler | unwrap", + "bankBefore": "$bankBefore", + "bankAfter": "$bankAfter", + "error": "$..[?(@.key.str == 'error')].value | unwrap" + } + } + ], + "output": "$history[1:] | map(historyState)" +} \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test2.json b/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test2.json new file mode 100644 index 000000000000..a2c821c4dbab --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/apalache-to-relay-test2.json @@ -0,0 +1,104 @@ +{ + "description": "Transforms an Apalache counterexample into the test for ICS20 Token Transfer OnRecvPacket", + "usage": "jsonatr --use apalache-to-recv-test.json --in counterexample.json --out recv-test.json", + "input": [ + { + "name": "history", + "description": "extract history from the last state of Apalache CE", + "kind": "INLINE", + "source": "$.declarations[-2].body.and..[?(@.eq == 'history')].arg.atat..arg.record" + }, + { + "name": "bankRecordToBalance", + "description": "", + "kind": "INLINE", + "source": { + "address": [ + "$.colonGreater.tuple[0]..[?(@.key.str == 'port')].value.str | unwrap", + "$.colonGreater.tuple[0]..[?(@.key.str == 'channel')].value.str | unwrap", + "$.colonGreater.tuple[0]..[?(@.key.str == 'id')].value.str | unwrap" + ], + "denom": [ + "$.colonGreater.tuple[1]..[?(@.key.str == 'prefix1')].value..[?(@.key.str == 'port')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'prefix1')].value..[?(@.key.str == 'channel')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'prefix0')].value..[?(@.key.str == 'port')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'prefix0')].value..[?(@.key.str == 'channel')].value.str | unwrap", + "$.colonGreater.tuple[1]..[?(@.key.str == 'denom')].value.str | unwrap" + ], + "amount": "$.arg | unwrap" + } + }, + { + "name": "bankBefore", + "description": "extract bankBefore from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'bankBefore')].value.atat | unwrap | map(bankRecordToBalance)" + }, + { + "name": "bankAfter", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'bankAfter')].value.atat | unwrap | map(bankRecordToBalance)" + }, + { + "name": "packet", + "description": "extract packet from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'packet')].value.record" + }, + { + "name": "packetData", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'data')].value.record" + }, + { + "name": "packetDataDenom", + "description": "extract bankAfter from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'data')].value.record.[?(@.key.str == 'denomTrace')].value.record" + }, + { + "name": "packetRecord", + "description": "decompose packet", + "kind": "INLINE", + "source": { + "sourceChannel" : "$.[?(@.key.str == 'sourceChannel')].value.str | unwrap", + "sourcePort" : "$.[?(@.key.str == 'sourcePort')].value.str | unwrap", + "destChannel" : "$.[?(@.key.str == 'destChannel')].value.str | unwrap", + "destPort" : "$.[?(@.key.str == 'destPort')].value.str | unwrap", + "data": { + "sender": "$packetData.[?(@.key.str == 'sender')].value.str | unwrap", + "receiver": "$packetData.[?(@.key.str == 'receiver')].value.str | unwrap", + "amount": "$packetData.[?(@.key.str == 'amount')].value | unwrap", + "denom": [ + "$packetDataDenom.[?(@.key.str == 'prefix1')].value..[?(@.key.str == 'port')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'prefix1')].value..[?(@.key.str == 'channel')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'prefix0')].value..[?(@.key.str == 'port')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'prefix0')].value..[?(@.key.str == 'channel')].value.str | unwrap", + "$packetDataDenom.[?(@.key.str == 'denom')].value.str | unwrap" + ] + } + } + }, + { + "name": "handler", + "description": "extract handler from the history state", + "kind": "INLINE", + "source": "$..[?(@.key.str == 'handler')].value.str" + }, + { + "name": "historyState", + "description": "decompose single history state", + "kind": "INLINE", + "source": { + "packet": "$packet | unwrap | packetRecord", + "handler": "$handler | unwrap", + "bankBefore": "$bankBefore", + "bankAfter": "$bankAfter", + "error": "$..[?(@.key.str == 'error')].value | unwrap" + } + } + ], + "output": "$history[1:] | map(historyState)" +} \ No newline at end of file diff --git a/x/ibc/applications/transfer/keeper/relay_model/denom.tla b/x/ibc/applications/transfer/keeper/relay_model/denom.tla new file mode 100644 index 000000000000..f729e7e14f41 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/denom.tla @@ -0,0 +1,50 @@ +-------------------------- MODULE denom ---------------------------- + +(** + The denomination traces interface; please ignore the definition bodies. +*) + +EXTENDS identifiers + +CONSTANT + Denoms + +\* A non-account +NullDenomTrace == "NullDenomTrace" + +\* All denomination traces +DenomTraces == {NullDenomTrace} + +\* Make a new denomination trace from the port/channel prefix and the basic denom +MakeDenomTrace(port, channel, denom) == NullDenomTrace + +\* Get the denomination trace port +GetPort(trace) == NullId + +\* Get the denomination trace port +GetChannel(trace) == NullId + +\* Get the denomination trace basic denomination +GetDenom(trace) == NullDenomTrace + +\* Is this denomination trace a native denomination, or is it a prefixed trace +\* Note that those cases are exclusive, but not exhaustive +IsNativeDenomTrace(trace) == GetPort(trace) = NullId /\ GetChannel(trace) = NullId +IsPrefixedDenomTrace(trace) == GetPort(trace) /= NullId /\ GetChannel(trace) /= NullId + +DenomTypeOK == + /\ NullDenomTrace \in DenomTraces + /\ \A p \in Identifiers, c \in Identifiers, d \in Denoms: + MakeDenomTrace(p, c, d) \in DenomTraces + /\ \A t \in DenomTraces: + /\ GetPort(t) \in Identifiers + /\ GetChannel(t) \in Identifiers + /\ GetDenom(t) \in DenomTraces + + + + +============================================================================= +\* Modification History +\* Last modified Thu Nov 05 15:49:23 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/denom_record.tla b/x/ibc/applications/transfer/keeper/relay_model/denom_record.tla new file mode 100644 index 000000000000..2eb0d06f1db8 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/denom_record.tla @@ -0,0 +1,53 @@ +-------------------------- MODULE denom_record ---------------------------- + +(** + The most basic implementation of denomination traces that allows only one-step sequences + Represented via records +*) + +EXTENDS identifiers + +CONSTANT + Denoms + +MaxDenomLength == 3 + +DenomTraces == [ + port: Identifiers, + channel: Identifiers, + denom: Denoms +] + +NullDenomTrace == [ + port |-> NullId, + channel |-> NullId, + denom |-> NullId +] + +GetPort(trace) == trace.port +GetChannel(trace) == trace.channel +GetDenom(trace) == trace.denom + +IsNativeDenomTrace(trace) == GetPort(trace) = NullId /\ GetChannel(trace) = NullId /\ GetDenom(trace) /= NullId +IsPrefixedDenomTrace(trace) == GetPort(trace) /= NullId /\ GetChannel(trace) /= NullId /\ GetDenom(trace) /= NullId + +ExtendDenomTrace(port, channel, trace) == + IF GetPort(trace) = NullId /\ GetChannel(trace) = NullId + THEN + [ + port |-> port, + channel |-> channel, + denom |-> trace.denom + ] + ELSE + NullDenomTrace + + +DENOM == INSTANCE denom +DenomTypeOK == DENOM!DenomTypeOK + + +============================================================================= +\* Modification History +\* Last modified Thu Nov 05 16:41:47 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/denom_record2.tla b/x/ibc/applications/transfer/keeper/relay_model/denom_record2.tla new file mode 100644 index 000000000000..a49d6c98de35 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/denom_record2.tla @@ -0,0 +1,114 @@ +-------------------------- MODULE denom_record2 ---------------------------- + +(** + The implementation of denomination traces that allows one- or two-step sequences + Represented via records +*) + +EXTENDS identifiers + +CONSTANT + Denoms + +MaxDenomLength == 5 + +DenomPrefixes == [ + port: Identifiers, + channel: Identifiers +] + +NullDenomPrefix == [ + port |-> NullId, + channel |-> NullId +] + +MakeDenomPrefix(port, channel) == [ + port |-> port, + channel |-> channel +] + +IsValidDenomPrefix(prefix) == + /\ prefix.port /= NullId + /\ prefix.channel /= NullId + +DenomTraces == [ + prefix1: DenomPrefixes, \* the most recent prefix + prefix0: DenomPrefixes, \* the deepest prefix + denom: Denoms +] + +NullDenomTrace == [ + prefix1 |-> NullDenomPrefix, + prefix0 |-> NullDenomPrefix, + denom |-> NullId +] + + +TraceLen(trace) == + IF trace.prefix0 = NullDenomPrefix + THEN 1 + ELSE IF trace.prefix1 = NullDenomPrefix + THEN 3 + ELSE 5 + +LatestPrefix(trace) == + IF trace.prefix0 = NullDenomPrefix + THEN NullDenomPrefix + ELSE IF trace.prefix1 = NullDenomPrefix + THEN trace.prefix0 + ELSE trace.prefix1 + + +ExtendDenomTrace(port, channel, trace) == + IF trace.prefix0 = NullDenomPrefix + THEN [ + prefix1 |-> NullDenomPrefix, + prefix0 |-> MakeDenomPrefix(port, channel), + denom |-> trace.denom + ] + ELSE IF trace.prefix1 = NullDenomPrefix + THEN [ + prefix1 |-> MakeDenomPrefix(port, channel), + prefix0 |-> trace.prefix0, + denom |-> trace.denom + ] + ELSE NullDenomTrace \* can extend only for two steps + +ReduceDenomTrace(trace) == + IF trace.prefix1 /= NullDenomPrefix + THEN [ + prefix1 |-> NullDenomPrefix, + prefix0 |-> trace.prefix0, + denom |-> trace.denom + ] + ELSE IF trace.prefix0 /= NullDenomPrefix + THEN [ + prefix1 |-> NullDenomPrefix, + prefix0 |-> NullDenomPrefix, + denom |-> trace.denom + ] + ELSE NullDenomTrace \* cannot reduce further + +GetPort(trace) == LatestPrefix(trace).port +GetChannel(trace) == LatestPrefix(trace).channel +GetDenom(trace) == trace.denom + +IsValidDenomTrace(trace) == + /\ GetDenom(trace) /= NullId + /\ IF IsValidDenomPrefix(trace.prefix1) + THEN IsValidDenomPrefix(trace.prefix0) + ELSE + /\ trace.prefix1 = NullDenomPrefix + /\ (IsValidDenomPrefix(trace.prefix0) \/ trace.prefix0 = NullDenomPrefix) + +IsNativeDenomTrace(trace) == LatestPrefix(trace) = NullDenomPrefix /\ GetDenom(trace) /= NullId +IsPrefixedDenomTrace(trace) == LatestPrefix(trace) /= NullDenomPrefix /\ GetDenom(trace) /= NullId + +DENOM == INSTANCE denom +DenomTypeOK == DENOM!DenomTypeOK + + +============================================================================= +\* Modification History +\* Last modified Fri Dec 04 10:38:10 CET 2020 by andrey +\* Created Fri Dec 04 10:22:10 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/denom_sequence.tla b/x/ibc/applications/transfer/keeper/relay_model/denom_sequence.tla new file mode 100644 index 000000000000..29b5f4edf288 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/denom_sequence.tla @@ -0,0 +1,47 @@ +-------------------------- MODULE denom_sequence ---------------------------- + +(** + The implementation of denomination traces via sequences +*) + +EXTENDS Integers, Sequences, identifiers + +CONSTANT + Denoms, + MaxDenomLength + + +a <: b == a +AsAddress(seq) == seq <: Seq(STRING) + +UNROLL_DEFAULT_GenSeq == { AsAddress(<< >>) } +UNROLL_TIMES_GenSeq == 5 + +\* This produces denomination sequences up to the given bound +RECURSIVE GenSeq(_) +GenSeq(n) == + IF n = 0 THEN { AsAddress(<< >>) } + ELSE LET Shorter == GenSeq(n-1) IN + { Append(s,x): x \in Identifiers, s \in Shorter } \union Shorter + +DenomTraces == GenSeq(MaxDenomLength) + +ExtendDenomTrace(port, channel, denom) == AsAddress(<>) \o denom + +GetPort(trace) == trace[1] +GetChannel(trace) == trace[2] +GetDenom(trace) == SubSeq(trace, 3, Len(trace)) + +NullDenomTrace == AsAddress(<< >>) + +IsNativeDenomTrace(trace) == GetPort(trace) = NullId /\ GetChannel(trace) = NullId /\ GetDenom(trace) /= NullDenomTrace +IsPrefixedDenomTrace(trace) == GetPort(trace) /= NullId /\ GetChannel(trace) /= NullId /\ GetDenom(trace) /= NullDenomTrace + +DENOM == INSTANCE denom +DenomTypeOK == DENOM!DenomTypeOK + + +============================================================================= +\* Modification History +\* Last modified Thu Nov 05 15:29:21 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/identifiers.tla b/x/ibc/applications/transfer/keeper/relay_model/identifiers.tla new file mode 100644 index 000000000000..089f276d8c08 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/identifiers.tla @@ -0,0 +1,10 @@ +-------------------------- MODULE identifiers ---------------------------- + +CONSTANT + Identifiers, + NullId + +============================================================================= +\* Modification History +\* Last modified Thu Nov 05 13:23:12 CET 2020 by andrey +\* Created Thu Nov 05 13:22:40 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/relay.tla b/x/ibc/applications/transfer/keeper/relay_model/relay.tla new file mode 100644 index 000000000000..029df3d7c7b0 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/relay.tla @@ -0,0 +1,278 @@ +-------------------------- MODULE relay ---------------------------- +(** + * A primitive model for account arithmetics and token movement + * of the Cosmos SDK ICS20 Token Transfer + * We completely abstract away many details, + * and want to focus on a minimal spec useful for testing + * + * We also try to make the model modular in that it uses + * denomination traces and accounts via abstract interfaces, + * outlined in denom.tla and account.tla + *) + +EXTENDS Integers, FiniteSets, Sequences, identifiers, denom_record2, account_record + +CONSTANT + MaxAmount + +VARIABLE + error, + bank, + p, \* we want to start with generating single packets, + handler, + history, + count + +Amounts == 0..MaxAmount + +GetSourceEscrowAccount(packet) == MakeEscrowAccount(packet.sourcePort, packet.sourceChannel) +GetDestEscrowAccount(packet) == MakeEscrowAccount(packet.destPort, packet.destChannel) + +FungibleTokenPacketData == [ + sender: AccountIds, + receiver: AccountIds, + denomTrace: DenomTraces, + amount: Amounts +] + +Packets == [ + \* We abstract those packet fields away + \* sequence: uint64 + \* timeoutHeight: Height + \* timeoutTimestamp: uint64 + sourcePort: Identifiers, + sourceChannel: Identifiers, + destPort: Identifiers, + destChannel: Identifiers, + data: FungibleTokenPacketData +] + + +IsSource(packet) == + /\ GetPort(packet.data.denomTrace) = packet.sourcePort + /\ GetChannel(packet.data.denomTrace) = packet.sourceChannel + +\* This function models the port and channel checks that happen when the packet is sent +IsValidSendChannel(packet) == + /\ packet.sourcePort = "transfer" + /\ (packet.sourceChannel = "channel-0" \/ packet.sourceChannel = "channel-1") + /\ packet.destPort = "transfer" + /\ packet.destChannel = "channel-0" + +\* This function models the port and channel checks that happen when relay gets the packet +IsValidRecvChannel(packet) == + /\ packet.sourcePort = "transfer" + /\ packet.sourceChannel = "channel-0" + /\ packet.destPort = "transfer" + /\ (packet.destChannel = "channel-0" \/ packet.destChannel = "channel-1") + + +WellFormedPacket(packet) == + /\ packet.sourcePort /= NullId + /\ packet.sourceChannel /= NullId + /\ packet.destPort /= NullId + /\ packet.destChannel /= NullId + +BankWithAccount(abank, account, denom) == + IF <> \in DOMAIN abank + THEN abank + ELSE [x \in DOMAIN bank \union { <> } + |-> IF x = <> + THEN 0 + ELSE bank[x] ] + +IsKnownDenomTrace(trace) == + \E account \in Accounts : + <> \in DOMAIN bank + + +SendTransferPre(packet, pbank) == + LET data == packet.data + trace == data.denomTrace + sender == data.sender + amount == data.amount + escrow == GetSourceEscrowAccount(packet) + IN + /\ WellFormedPacket(packet) + /\ IsValidSendChannel(packet) + /\ IsNativeDenomTrace(trace) \/ (IsValidDenomTrace(trace) /\ IsKnownDenomTrace(trace)) + /\ data.sender /= NullId + /\ <> \in DOMAIN pbank + /\ \/ amount = 0 \* SendTrasfer actually allows for 0 amount + \/ <> \in DOMAIN pbank /\ bank[MakeAccount(sender), trace] >= amount + +SendTransferNext(packet) == + LET data == packet.data IN + LET denom == GetDenom(data.denomTrace) IN + LET amount == data.amount IN + LET sender == data.sender IN + LET escrow == GetSourceEscrowAccount(packet) IN + LET bankwithescrow == BankWithAccount(bank, escrow, data.denomTrace) IN + IF SendTransferPre(packet,bankwithescrow) + THEN + /\ error' = FALSE + \*/\ IBCsend(chain, packet) + /\ IF ~IsSource(packet) + \* This is how the check is encoded in ICS20 and the implementation. + \* The meaning is "IF denom = AsAddress(NativeDenom)" because of the following argument: + \* observe that due to the disjunction in SendTransferPre(packet), we have + \* ~IsSource(packet) /\ SendTransferPre(packet) => denom = AsAddress(NativeDenom) + THEN + \* tokens are from this chain + \* transfer tokens from sender into escrow account + bank' = [bankwithescrow EXCEPT ![MakeAccount(sender), data.denomTrace] = @ - amount, + ![escrow, data.denomTrace] = @ + amount] + ELSE + \* tokens are from other chain. We forward them. + \* burn sender's money + bank' = [bankwithescrow EXCEPT ![MakeAccount(sender), data.denomTrace] = @ - amount] + ELSE + /\ error' = TRUE + /\ UNCHANGED bank + + +OnRecvPacketPre(packet) == + LET data == packet.data + trace == data.denomTrace + denom == GetDenom(trace) + amount == data.amount + IN + /\ WellFormedPacket(packet) + /\ IsValidRecvChannel(packet) + /\ IsValidDenomTrace(trace) + /\ amount > 0 + \* if there is no receiver account, it is created by the bank + /\ data.receiver /= NullId + /\ IsSource(packet) => + LET escrow == GetDestEscrowAccount(packet) IN + LET denomTrace == ReduceDenomTrace(trace) IN + /\ <> \in DOMAIN bank + /\ bank[escrow, denomTrace] >= amount + + +OnRecvPacketNext(packet) == + LET data == packet.data IN + LET trace == data.denomTrace IN + LET denom == GetDenom(trace) IN + LET amount == data.amount IN + LET receiver == data.receiver IN + /\ IF OnRecvPacketPre(packet) + THEN + \* This condition is necessary so that denomination traces do not exceed the maximum length + /\ (IsSource(packet) \/ TraceLen(trace) < MaxDenomLength) + /\ error' = FALSE + /\ IF IsSource(packet) + THEN + \* transfer from the escrow account to the receiver account + LET denomTrace == ReduceDenomTrace(trace) IN + LET escrow == GetDestEscrowAccount(packet) IN + LET bankwithreceiver == BankWithAccount(bank, MakeAccount(receiver), denomTrace) IN + bank' = [bankwithreceiver + EXCEPT ![MakeAccount(receiver), denomTrace] = @ + amount, + ![escrow, denomTrace] = @ - amount] + ELSE + \* create new tokens with new denomination and transfer it to the receiver account + LET denomTrace == ExtendDenomTrace(packet.destPort, packet.destChannel, trace) IN + LET bankwithreceiver == + BankWithAccount(bank, MakeAccount(receiver), denomTrace) IN + bank' = [bankwithreceiver + EXCEPT ![MakeAccount(receiver), denomTrace] = @ + amount] + ELSE + /\ error' = TRUE + /\ UNCHANGED bank + + +OnTimeoutPacketPre(packet) == + LET data == packet.data + trace == data.denomTrace + denom == GetDenom(trace) + amount == data.amount + IN + /\ WellFormedPacket(packet) + /\ IsValidSendChannel(packet) + /\ IsValidDenomTrace(trace) + /\ data.sender /= NullId + /\ ~IsSource(packet) => + LET escrow == GetSourceEscrowAccount(packet) + IN /\ <> \in DOMAIN bank + /\ bank[escrow, trace] >= amount + + +OnTimeoutPacketNext(packet) == + LET data == packet.data IN + LET trace == data.denomTrace IN + LET denom == GetDenom(data.denomTrace) IN + LET amount == data.amount IN + LET sender == data.sender IN + LET bankwithsender == BankWithAccount(bank, MakeAccount(sender), trace) IN + IF OnTimeoutPacketPre(packet) + THEN + /\ error' = FALSE + /\ IF ~IsSource(packet) + THEN + \* transfer from the escrow acount to the sender account + \* LET denomsuffix == SubSeq(denom, 3, Len(denom)) IN + LET escrow == GetSourceEscrowAccount(packet) IN + bank' = [bankwithsender + EXCEPT ![MakeAccount(sender), trace] = @ + amount, + ![escrow, trace] = @ - amount] + ELSE + \* mint back the money + bank' = [bankwithsender EXCEPT ![MakeAccount(sender), trace] = @ + amount] + + ELSE + /\ error' = TRUE + /\ UNCHANGED bank + + +OnAcknowledgementPacketResultNext(packet) == + IF WellFormedPacket(packet) + THEN + /\ error' = FALSE + /\ UNCHANGED bank + ELSE + /\ error' = TRUE + /\ UNCHANGED bank + + +OnAcknowledgementPacketErrorNext(packet) == + OnTimeoutPacketNext(packet) + +Init == + /\ p \in Packets + /\ bank = [ x \in {<>} |-> 0 ] + /\ count = 0 + /\ history = [ + n \in {0} |-> [ + error |-> FALSE, + packet |-> p, + handler |-> "", + bankBefore |-> bank, + bankAfter |-> bank + ] + ] + /\ error = FALSE + /\ handler = "" + +Next == + /\ p' \in Packets + /\ count'= count + 1 + /\ + \/ (SendTransferNext(p) /\ handler' = "SendTransfer") + \/ (OnRecvPacketNext(p) /\ handler' = "OnRecvPacket") + \/ (OnTimeoutPacketNext(p) /\ handler' = "OnTimeoutPacket") + \/ (OnAcknowledgementPacketResultNext(p) /\ handler' = "OnRecvAcknowledgementResult") + \/ (OnAcknowledgementPacketErrorNext(p) /\ handler' = "OnRecvAcknowledgementError") + /\ history' = [ n \in DOMAIN history \union {count'} |-> + IF n = count' THEN + [ packet |-> p, handler |-> handler', error |-> error', bankBefore |-> bank, bankAfter |-> bank' ] + ELSE history[n] + ] + +============================================================================= +\* Modification History +\* Last modified Wed Dec 2 10:15:45 CET 2020 by andrey +\* Last modified Fri Nov 20 12:37:38 CET 2020 by c +\* Last modified Thu Nov 05 20:56:37 CET 2020 by andrey +\* Last modified Fri Oct 30 21:52:38 CET 2020 by widder +\* Created Thu Oct 29 20:45:55 CET 2020 by andrey diff --git a/x/ibc/applications/transfer/keeper/relay_model/relay_tests.tla b/x/ibc/applications/transfer/keeper/relay_model/relay_tests.tla new file mode 100644 index 000000000000..7e7577526dc0 --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_model/relay_tests.tla @@ -0,0 +1,96 @@ +-------------------------- MODULE relay_tests ---------------------------- + +EXTENDS Integers, FiniteSets + +Identifiers == {"", "transfer", "channel-0", "channel-1", "cosmos-hub", "ethereum-hub", "bitcoin-hub"} +NullId == "" +MaxAmount == 5 +Denoms == {"", "atom", "eth", "btc" } +AccountIds == {"", "a1", "a2", "a3" } + +VARIABLES error, bank, p, count, history, handler + +INSTANCE relay + +\************************** Tests ****************************** + +\* Generic test for handler pass +TestHandlerPass(handlerName) == + \E s \in DOMAIN history : + /\ history[s].handler = handlerName + /\ history[s].error = FALSE + /\ history[s].packet.data.amount > 0 + +\* Generic test for handler fail +TestHandlerFail(handlerName) == + \E s \in DOMAIN history : + /\ history[s].handler = handlerName + /\ history[s].error = TRUE + /\ history[s].packet.data.amount > 0 + +TestSendTransferPass == TestHandlerPass("SendTransfer") +TestSendTransferPassInv == ~TestSendTransferPass + +TestSendTransferFail == TestHandlerFail("SendTransfer") +TestSendTransferFailInv == ~TestSendTransferFail + +TestOnRecvPacketPass == TestHandlerPass("OnRecvPacket") +TestOnRecvPacketPassInv == ~TestOnRecvPacketPass + +TestOnRecvPacketFail == TestHandlerFail("OnRecvPacket") +TestOnRecvPacketFailInv == ~TestOnRecvPacketFail + +TestOnTimeoutPass == TestHandlerPass("OnTimeoutPacket") +TestOnTimeoutPassInv == ~TestOnTimeoutPass + +TestOnTimeoutFail == TestHandlerFail("OnTimeoutPacket") +TestOnTimeoutFailInv == ~TestOnTimeoutFail + +TestOnRecvAcknowledgementResultPass == TestHandlerPass("OnRecvAcknowledgementResult") +TestOnRecvAcknowledgementResultPassInv == ~TestOnRecvAcknowledgementResultPass + +TestOnRecvAcknowledgementResultFail == TestHandlerFail("OnRecvAcknowledgementResult") +TestOnRecvAcknowledgementResultFailInv == ~TestOnRecvAcknowledgementResultFail + +TestOnRecvAcknowledgementErrorPass == TestHandlerPass("OnRecvAcknowledgementError") +TestOnRecvAcknowledgementErrorPassInv == ~TestOnRecvAcknowledgementErrorPass + +TestOnRecvAcknowledgementErrorFail == TestHandlerFail("OnRecvAcknowledgementError") +TestOnRecvAcknowledgementErrorFailInv == ~TestOnRecvAcknowledgementErrorFail + +Test5Packets == + count >= 5 + +Test5PacketsInv == ~Test5Packets + +Test5Packets2Different == + /\ count >= 5 + /\ \E s1, s2 \in DOMAIN history : + history[s1].handler /= history[s2].handler + +Test5Packets2DifferentInv == ~Test5Packets2Different + +Test5PacketsAllDifferent == + /\ count >= 5 + /\ \A s1, s2 \in DOMAIN history : + s1 /= s2 => history[s1].handler /= history[s2].handler + +Test5PacketsAllDifferentInv == ~Test5PacketsAllDifferent + +Test5PacketsAllDifferentPass == + /\ Test5PacketsAllDifferent + /\ \A s \in DOMAIN history : + s > 0 => + /\ history[s].error = FALSE + /\ history[s].packet.data.amount > 0 + +Test5PacketsAllDifferentPassInv == ~Test5PacketsAllDifferentPass + +TestUnescrowTokens == + \E s \in DOMAIN history : + /\ IsSource(history[s].packet) + /\ history[s].handler = "OnRecvPacket" + /\ history[s].error = FALSE +TestUnescrowTokensInv == ~TestUnescrowTokens + +============================================================================= diff --git a/x/ibc/applications/transfer/keeper/relay_test.go b/x/ibc/applications/transfer/keeper/relay_test.go new file mode 100644 index 000000000000..9f303175175f --- /dev/null +++ b/x/ibc/applications/transfer/keeper/relay_test.go @@ -0,0 +1,394 @@ +package keeper_test + +import ( + "fmt" + + 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" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +// test sending from chainA to chainB using both coin that orignate on +// chainA and coin that orignate on chainB +func (suite *KeeperTestSuite) TestSendTransfer() { + var ( + amount sdk.Coin + channelA, channelB ibctesting.TestChannel + err error + ) + + testCases := []struct { + msg string + malleate func() + sendFromSource bool + expPass bool + }{ + {"successful transfer from source chain", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + }, true, true}, + {"successful transfer with coin from counterparty chain", + func() { + // send coin from chainA back to chainB + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + amount = types.GetTransferCoin(channelA.PortID, channelA.ID, sdk.DefaultBondDenom, 100) + }, false, true}, + {"source channel not found", + func() { + // channel references wrong ID + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + channelA.ID = ibctesting.InvalidID + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + }, true, false}, + {"next seq send not found", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA = suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB = suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + // manually create channel so next seq send is never set + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, channeltypes.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, ibctesting.DefaultChannelVersion), + ) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + }, true, false}, + + // createOutgoingPacket tests + // - source chain + {"send coin failed", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + amount = sdk.NewCoin("randomdenom", sdk.NewInt(100)) + }, true, false}, + // - receiving chain + {"send from module account failed", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + amount = types.GetTransferCoin(channelA.PortID, channelA.ID, " randomdenom", 100) + }, false, false}, + {"channel capability not found", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + cap := suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // Release channel capability + suite.chainA.App.ScopedTransferKeeper.ReleaseCapability(suite.chainA.GetContext(), cap) + amount = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + }, true, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + if !tc.sendFromSource { + // send coin from chainB to chainA + coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + transferMsg := types.NewMsgTransfer(channelB.PortID, channelB.ID, coinFromBToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + err = suite.coordinator.SendMsg(suite.chainB, suite.chainA, channelA.ClientID, transferMsg) + suite.Require().NoError(err) // message committed + + // receive coin on chainA from chainB + fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.Uint64(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, clienttypes.NewHeight(0, 110), 0) + + // get proof of packet commitment from chainB + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetKey) + + recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainA.SenderAccount.GetAddress()) + err = suite.coordinator.SendMsg(suite.chainA, suite.chainB, channelB.ClientID, recvMsg) + suite.Require().NoError(err) // message committed + } + + err = suite.chainA.App.TransferKeeper.SendTransfer( + suite.chainA.GetContext(), channelA.PortID, channelA.ID, amount, + suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test receiving coin on chainB with coin that orignate on chainA and +// coin that orignated on chainB (source). The bulk of the testing occurs +// in the test case for loop since setup is intensive for all cases. The +// malleate function allows for testing invalid cases. +func (suite *KeeperTestSuite) TestOnRecvPacket() { + var ( + channelA, channelB ibctesting.TestChannel + trace types.DenomTrace + amount sdk.Int + receiver string + ) + + testCases := []struct { + msg string + malleate func() + recvIsSource bool // the receiving chain is the source of the coin originally + expPass bool + }{ + {"success receive on source chain", func() {}, true, true}, + {"success receive with coin from another chain as source", func() {}, false, true}, + {"empty coin", func() { + trace = types.DenomTrace{} + amount = sdk.ZeroInt() + }, true, false}, + {"invalid receiver address", func() { + receiver = "gaia1scqhwpgsmr6vmztaa7suurfl52my6nd2kmrudl" + }, true, false}, + + // onRecvPacket + // - coin from chain chainA + {"failure: mint zero coin", func() { + amount = sdk.ZeroInt() + }, false, false}, + + // - coin being sent back to original chain (chainB) + {"tries to unescrow more tokens than allowed", func() { + amount = sdk.NewInt(1000000) + }, true, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + receiver = suite.chainB.SenderAccount.GetAddress().String() // must be explicitly changed in malleate + + amount = sdk.NewInt(100) // must be explicitly changed in malleate + seq := uint64(1) + + if tc.recvIsSource { + // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB + coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + transferMsg := types.NewMsgTransfer(channelB.PortID, channelB.ID, coinFromBToA, suite.chainB.SenderAccount.GetAddress(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0) + err := suite.coordinator.SendMsg(suite.chainB, suite.chainA, channelA.ClientID, transferMsg) + suite.Require().NoError(err) // message committed + + // relay send packet + fungibleTokenPacket := types.NewFungibleTokenPacketData(coinFromBToA.Denom, coinFromBToA.Amount.Uint64(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String()) + packet := channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, clienttypes.NewHeight(0, 110), 0) + ack := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + err = suite.coordinator.RelayPacket(suite.chainB, suite.chainA, clientB, clientA, packet, ack.GetBytes()) + suite.Require().NoError(err) // relay committed + + seq++ + + // NOTE: trace must be explicitly changed in malleate to test invalid cases + trace = types.ParseDenomTrace(types.GetPrefixedDenom(channelA.PortID, channelA.ID, sdk.DefaultBondDenom)) + } else { + trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + } + + // send coin from chainA to chainB + transferMsg := types.NewMsgTransfer(channelA.PortID, channelA.ID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress(), receiver, clienttypes.NewHeight(0, 110), 0) + err := suite.coordinator.SendMsg(suite.chainA, suite.chainB, channelB.ClientID, transferMsg) + suite.Require().NoError(err) // message committed + + tc.malleate() + + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.Uint64(), suite.chainA.SenderAccount.GetAddress().String(), receiver) + packet := channeltypes.NewPacket(data.GetBytes(), seq, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + err = suite.chainB.App.TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestOnAcknowledgementPacket tests that successful acknowledgement is a no-op +// and failure acknowledment leads to refund when attempting to send from chainA +// to chainB. If sender is source than the denomination being refunded has no +// trace. +func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { + var ( + successAck = channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + failedAck = channeltypes.NewErrorAcknowledgement("failed packet transfer") + + channelA, channelB ibctesting.TestChannel + trace types.DenomTrace + amount sdk.Int + ) + + testCases := []struct { + msg string + ack channeltypes.Acknowledgement + malleate func() + success bool // success of ack + expPass bool + }{ + {"success ack causes no-op", successAck, func() { + trace = types.ParseDenomTrace(types.GetPrefixedDenom(channelB.PortID, channelB.ID, sdk.DefaultBondDenom)) + }, true, true}, + {"successful refund from source chain", failedAck, func() { + escrow := types.GetEscrowAddress(channelA.PortID, channelA.ID) + 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) + }, false, true}, + {"unsuccessful refund from source", failedAck, + func() { + trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + }, false, false}, + {"successful refund from with coin from external chain", failedAck, + func() { + escrow := types.GetEscrowAddress(channelA.PortID, channelA.ID) + 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) + }, false, true}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + _, _, _, _, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + amount = sdk.NewInt(100) // must be explicitly changed + + tc.malleate() + + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.Uint64(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String()) + packet := channeltypes.NewPacket(data.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + preCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + + err := suite.chainA.App.TransferKeeper.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, data, tc.ack) + if tc.expPass { + suite.Require().NoError(err) + postCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + deltaAmount := postCoin.Amount.Sub(preCoin.Amount) + + if tc.success { + suite.Require().Equal(int64(0), deltaAmount.Int64(), "successful ack changed balance") + } else { + suite.Require().Equal(amount, deltaAmount, "failed ack did not trigger refund") + } + + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestOnTimeoutPacket test private refundPacket function since it is a simple +// wrapper over it. The actual timeout does not matter since IBC core logic +// is not being tested. The test is timing out a send from chainA to chainB +// so the refunds are occurring on chainA. +func (suite *KeeperTestSuite) TestOnTimeoutPacket() { + var ( + channelA, channelB ibctesting.TestChannel + trace types.DenomTrace + amount sdk.Int + sender string + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"successful timeout from sender as source chain", + func() { + escrow := types.GetEscrowAddress(channelA.PortID, channelA.ID) + 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) + }, true}, + {"successful timeout from external chain", + func() { + escrow := types.GetEscrowAddress(channelA.PortID, channelA.ID) + 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) + }, true}, + {"no balance for coin denom", + func() { + trace = types.ParseDenomTrace("bitcoin") + }, false}, + {"unescrow failed", + func() { + trace = types.ParseDenomTrace(sdk.DefaultBondDenom) + }, false}, + {"mint failed", + func() { + trace = types.ParseDenomTrace(types.GetPrefixedDenom(channelA.PortID, channelA.ID, sdk.DefaultBondDenom)) + amount = sdk.OneInt() + sender = "invalid address" + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB = suite.coordinator.CreateTransferChannels(suite.chainA, suite.chainB, connA, connB, channeltypes.UNORDERED) + amount = sdk.NewInt(100) // must be explicitly changed + sender = suite.chainA.SenderAccount.GetAddress().String() + + tc.malleate() + + data := types.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.Uint64(), sender, suite.chainB.SenderAccount.GetAddress().String()) + packet := channeltypes.NewPacket(data.GetBytes(), 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + preCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + + err := suite.chainA.App.TransferKeeper.OnTimeoutPacket(suite.chainA.GetContext(), packet, data) + + postCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), trace.IBCDenom()) + deltaAmount := postCoin.Amount.Sub(preCoin.Amount) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(amount.Int64(), deltaAmount.Int64(), "successful timeout did not trigger refund") + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/applications/transfer/module.go b/x/ibc/applications/transfer/module.go new file mode 100644 index 000000000000..764f61e92bba --- /dev/null +++ b/x/ibc/applications/transfer/module.go @@ -0,0 +1,435 @@ +package transfer + +import ( + "context" + "encoding/json" + "fmt" + "math" + "math/rand" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "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/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +var ( + _ module.AppModule = AppModule{} + _ porttypes.IBCModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic is the IBC Transfer AppModuleBasic +type AppModuleBasic struct{} + +// Name implements AppModuleBasic interface +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec implements AppModuleBasic interface +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterInterfaces registers module concrete types into protobuf Any. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the ibc +// transfer module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the ibc transfer module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var gs types.GenesisState + if err := cdc.UnmarshalJSON(bz, &gs); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + + return gs.Validate() +} + +// RegisterRESTRoutes implements AppModuleBasic interface +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc-transfer module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetTxCmd implements AppModuleBasic interface +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd implements AppModuleBasic interface +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// AppModule represents the AppModule for this module +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper +} + +// NewAppModule creates a new 20-transfer module +func NewAppModule(k keeper.Keeper) AppModule { + return AppModule{ + keeper: k, + } +} + +// RegisterInvariants implements the AppModule interface +func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO +} + +// Route implements the AppModule interface +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) +} + +// QuerierRoute implements the AppModule interface +func (AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// LegacyQuerierHandler implements the AppModule interface +func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// InitGenesis performs genesis initialization for the ibc-transfer 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) + am.keeper.InitGenesis(ctx, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the ibc-transfer +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) +} + +// BeginBlock implements the AppModule interface +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock implements the AppModule interface +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the transfer module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized ibc-transfer param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for transfer module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.keeper) +} + +// WeightedOperations returns the all the transfer module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} + +//____________________________________________________________________________ + +// ValidateTransferChannelParams does validation of a newly created transfer channel. A transfer +// channel must be UNORDERED, use the correct port (by default 'transfer'), and use the current +// supported version. Only 2^32 channels are allowed to be created. +func ValidateTransferChannelParams( + ctx sdk.Context, + keeper keeper.Keeper, + order channeltypes.Order, + portID string, + channelID string, + version string, +) error { + // NOTE: for escrow address security only 2^32 channels are allowed to be created + // Issue: https://github.com/cosmos/cosmos-sdk/issues/7737 + channelSequence, err := channeltypes.ParseChannelSequence(channelID) + if err != nil { + return err + } + 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) + } + + // Require portID is the portID transfer module is bound to + boundPort := keeper.GetPort(ctx) + if boundPort != portID { + return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "invalid port: %s, expected %s", portID, boundPort) + } + + if version != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "got %s, expected %s", version, types.Version) + } + return nil +} + +// OnChanOpenInit implements the IBCModule interface +func (am AppModule) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) error { + if err := ValidateTransferChannelParams(ctx, am.keeper, order, portID, channelID, version); err != nil { + return err + } + + // Claim channel capability passed back by IBC module + if err := am.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + + return nil +} + +// OnChanOpenTry implements the IBCModule interface +func (am AppModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version, + counterpartyVersion string, +) error { + if err := ValidateTransferChannelParams(ctx, am.keeper, order, portID, channelID, version); err != nil { + return err + } + + if counterpartyVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: got: %s, expected %s", counterpartyVersion, types.Version) + } + + // Module may have already claimed capability in OnChanOpenInit in the case of crossing hellos + // (ie chainA and chainB both call ChanOpenInit before one of them calls ChanOpenTry) + // If module can already authenticate the capability then module already owns it so we don't need to claim + // Otherwise, module does not have channel capability and we must claim it from IBC + if !am.keeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + // Only claim channel capability passed back by IBC module if we do not already own it + if err := am.keeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + } + + return nil +} + +// OnChanOpenAck implements the IBCModule interface +func (am AppModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + if counterpartyVersion != types.Version { + return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version) + } + return nil +} + +// OnChanOpenConfirm implements the IBCModule interface +func (am AppModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return nil +} + +// OnChanCloseInit implements the IBCModule interface +func (am AppModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // Disallow user-initiated channel closing for transfer channels + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") +} + +// OnChanCloseConfirm implements the IBCModule interface +func (am AppModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return nil +} + +// OnRecvPacket implements the IBCModule interface +func (am AppModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) (*sdk.Result, []byte, error) { + var data types.FungibleTokenPacketData + if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { + return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + } + + acknowledgement := channeltypes.NewResultAcknowledgement([]byte{byte(1)}) + + err := am.keeper.OnRecvPacket(ctx, packet, data) + if err != nil { + acknowledgement = channeltypes.NewErrorAcknowledgement(err.Error()) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(types.AttributeKeyReceiver, data.Receiver), + sdk.NewAttribute(types.AttributeKeyDenom, data.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%d", data.Amount)), + sdk.NewAttribute(types.AttributeKeyAckSuccess, fmt.Sprintf("%t", err != nil)), + ), + ) + + // NOTE: acknowledgement will be written synchronously during IBC handler execution. + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, acknowledgement.GetBytes(), nil +} + +// OnAcknowledgementPacket implements the IBCModule interface +func (am AppModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, +) (*sdk.Result, error) { + var ack channeltypes.Acknowledgement + if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet acknowledgement: %v", err) + } + var data types.FungibleTokenPacketData + if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + } + + if err := am.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + 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)), + ), + ) + + switch resp := ack.Response.(type) { + case *channeltypes.Acknowledgement_Result: + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(types.AttributeKeyAckSuccess, string(resp.Result)), + ), + ) + case *channeltypes.Acknowledgement_Error: + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypePacket, + sdk.NewAttribute(types.AttributeKeyAckError, resp.Error), + ), + ) + } + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} + +// OnTimeoutPacket implements the IBCModule interface +func (am AppModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, +) (*sdk.Result, error) { + var data types.FungibleTokenPacketData + if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-20 transfer packet data: %s", err.Error()) + } + // refund tokens + if err := am.keeper.OnTimeoutPacket(ctx, packet, data); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeTimeout, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(types.AttributeKeyRefundReceiver, data.Sender), + sdk.NewAttribute(types.AttributeKeyRefundDenom, data.Denom), + sdk.NewAttribute(types.AttributeKeyRefundAmount, fmt.Sprintf("%d", data.Amount)), + ), + ) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} diff --git a/x/ibc/applications/transfer/module_test.go b/x/ibc/applications/transfer/module_test.go new file mode 100644 index 000000000000..d2acfb404311 --- /dev/null +++ b/x/ibc/applications/transfer/module_test.go @@ -0,0 +1,246 @@ +package transfer_test + +import ( + "math" + + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *TransferTestSuite) TestOnChanOpenInit() { + var ( + channel *channeltypes.Channel + testChannel ibctesting.TestChannel + connA *ibctesting.TestConnection + chanCap *capabilitytypes.Capability + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + + { + "success", func() {}, true, + }, + { + "max channels reached", func() { + testChannel.ID = channeltypes.FormatChannelIdentifier(math.MaxUint32 + 1) + }, false, + }, + { + "invalid order - ORDERED", func() { + channel.Ordering = channeltypes.ORDERED + }, false, + }, + { + "invalid port ID", func() { + testChannel = suite.chainA.NextTestChannel(connA, ibctesting.MockPort) + }, false, + }, + { + "invalid version", func() { + channel.Version = "version" + }, false, + }, + { + "capability already claimed", func() { + err := suite.chainA.App.ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(testChannel.PortID, testChannel.ID)) + suite.Require().NoError(err) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + _, _, connA, _ = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + testChannel = suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + counterparty := channeltypes.NewCounterparty(testChannel.PortID, testChannel.ID) + channel = &channeltypes.Channel{ + State: channeltypes.INIT, + Ordering: channeltypes.UNORDERED, + Counterparty: counterparty, + ConnectionHops: []string{connA.ID}, + Version: types.Version, + } + + module, _, err := suite.chainA.App.IBCKeeper.PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.TransferPort) + suite.Require().NoError(err) + + chanCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(ibctesting.TransferPort, testChannel.ID)) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.IBCKeeper.Router.GetRoute(module) + suite.Require().True(ok) + + tc.malleate() // explicitly change fields in channel and testChannel + + err = cbs.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), + testChannel.PortID, testChannel.ID, chanCap, channel.Counterparty, channel.GetVersion(), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + }) + } +} + +func (suite *TransferTestSuite) TestOnChanOpenTry() { + var ( + channel *channeltypes.Channel + testChannel ibctesting.TestChannel + connA *ibctesting.TestConnection + chanCap *capabilitytypes.Capability + counterpartyVersion string + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + + { + "success", func() {}, true, + }, + { + "max channels reached", func() { + testChannel.ID = channeltypes.FormatChannelIdentifier(math.MaxUint32 + 1) + }, false, + }, + { + "capability already claimed in INIT should pass", func() { + err := suite.chainA.App.ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(testChannel.PortID, testChannel.ID)) + suite.Require().NoError(err) + }, true, + }, + { + "invalid order - ORDERED", func() { + channel.Ordering = channeltypes.ORDERED + }, false, + }, + { + "invalid port ID", func() { + testChannel = suite.chainA.NextTestChannel(connA, ibctesting.MockPort) + }, false, + }, + { + "invalid version", func() { + channel.Version = "version" + }, false, + }, + { + "invalid counterparty version", func() { + counterpartyVersion = "version" + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + _, _, connA, _ = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + testChannel = suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + counterparty := channeltypes.NewCounterparty(testChannel.PortID, testChannel.ID) + channel = &channeltypes.Channel{ + State: channeltypes.TRYOPEN, + Ordering: channeltypes.UNORDERED, + Counterparty: counterparty, + ConnectionHops: []string{connA.ID}, + Version: types.Version, + } + counterpartyVersion = types.Version + + module, _, err := suite.chainA.App.IBCKeeper.PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.TransferPort) + suite.Require().NoError(err) + + chanCap, err = suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(ibctesting.TransferPort, testChannel.ID)) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.IBCKeeper.Router.GetRoute(module) + suite.Require().True(ok) + + tc.malleate() // explicitly change fields in channel and testChannel + + err = cbs.OnChanOpenTry(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(), + testChannel.PortID, testChannel.ID, chanCap, channel.Counterparty, channel.GetVersion(), counterpartyVersion, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + }) + } +} + +func (suite *TransferTestSuite) TestOnChanOpenAck() { + var ( + testChannel ibctesting.TestChannel + connA *ibctesting.TestConnection + counterpartyVersion string + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + + { + "success", func() {}, true, + }, + { + "invalid counterparty version", func() { + counterpartyVersion = "version" + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + _, _, connA, _ = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + testChannel = suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + counterpartyVersion = types.Version + + module, _, err := suite.chainA.App.IBCKeeper.PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.TransferPort) + suite.Require().NoError(err) + + cbs, ok := suite.chainA.App.IBCKeeper.Router.GetRoute(module) + suite.Require().True(ok) + + tc.malleate() // explicitly change fields in channel and testChannel + + err = cbs.OnChanOpenAck(suite.chainA.GetContext(), testChannel.PortID, testChannel.ID, counterpartyVersion) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + }) + } +} diff --git a/x/ibc/applications/transfer/simulation/decoder.go b/x/ibc/applications/transfer/simulation/decoder.go new file mode 100644 index 000000000000..df7834503883 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/decoder.go @@ -0,0 +1,33 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// TransferUnmarshaler defines the expected encoding store functions. +type TransferUnmarshaler interface { + MustUnmarshalDenomTrace([]byte) types.DenomTrace +} + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding DenomTrace type. +func NewDecodeStore(cdc TransferUnmarshaler) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.PortKey): + return fmt.Sprintf("Port A: %s\nPort B: %s", string(kvA.Value), string(kvB.Value)) + + case bytes.Equal(kvA.Key[:1], types.DenomTraceKey): + denomTraceA := cdc.MustUnmarshalDenomTrace(kvA.Value) + denomTraceB := cdc.MustUnmarshalDenomTrace(kvB.Value) + return fmt.Sprintf("DenomTrace A: %s\nDenomTrace B: %s", denomTraceA.IBCDenom(), denomTraceB.IBCDenom()) + + default: + panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1])) + } + } +} diff --git a/x/ibc/applications/transfer/simulation/decoder_test.go b/x/ibc/applications/transfer/simulation/decoder_test.go new file mode 100644 index 000000000000..729a067e0287 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/decoder_test.go @@ -0,0 +1,59 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + dec := simulation.NewDecodeStore(app.TransferKeeper) + + trace := types.DenomTrace{ + BaseDenom: "uatom", + Path: "transfer/channelToA", + } + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: types.PortKey, + Value: []byte(types.PortID), + }, + { + Key: types.DenomTraceKey, + Value: app.TransferKeeper.MustMarshalDenomTrace(trace), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"PortID", fmt.Sprintf("Port A: %s\nPort B: %s", types.PortID, types.PortID)}, + {"DenomTrace", fmt.Sprintf("DenomTrace A: %s\nDenomTrace B: %s", trace.IBCDenom(), trace.IBCDenom())}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + if i == len(tests)-1 { + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) + } else { + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) + } + }) + } +} diff --git a/x/ibc/applications/transfer/simulation/genesis.go b/x/ibc/applications/transfer/simulation/genesis.go new file mode 100644 index 000000000000..a51bce9f4717 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/genesis.go @@ -0,0 +1,54 @@ +package simulation + +import ( + "encoding/json" + "fmt" + "math/rand" + "strings" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// Simulation parameter constants +const port = "port_id" + +// RadomEnabled randomized send or receive enabled param with 75% prob of being true. +func RadomEnabled(r *rand.Rand) bool { + return r.Int63n(101) <= 75 +} + +// RandomizedGenState generates a random GenesisState for transfer. +func RandomizedGenState(simState *module.SimulationState) { + var portID string + simState.AppParams.GetOrGenerate( + simState.Cdc, port, &portID, simState.Rand, + func(r *rand.Rand) { portID = strings.ToLower(simtypes.RandStringOfLength(r, 20)) }, + ) + + var sendEnabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, string(types.KeySendEnabled), &sendEnabled, simState.Rand, + func(r *rand.Rand) { sendEnabled = RadomEnabled(r) }, + ) + + var receiveEnabled bool + simState.AppParams.GetOrGenerate( + simState.Cdc, string(types.KeyReceiveEnabled), &receiveEnabled, simState.Rand, + func(r *rand.Rand) { receiveEnabled = RadomEnabled(r) }, + ) + + transferGenesis := types.GenesisState{ + PortId: portID, + DenomTraces: types.Traces{}, + Params: types.NewParams(sendEnabled, receiveEnabled), + } + + bz, err := json.MarshalIndent(&transferGenesis, "", " ") + 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(&transferGenesis) +} diff --git a/x/ibc/applications/transfer/simulation/genesis_test.go b/x/ibc/applications/transfer/simulation/genesis_test.go new file mode 100644 index 000000000000..12791d7445f9 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/genesis_test.go @@ -0,0 +1,74 @@ +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/ibc/applications/transfer/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 ibcTransferGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &ibcTransferGenesis) + + require.Equal(t, "euzxpfgkqegqiqwixnku", ibcTransferGenesis.PortId) + require.True(t, ibcTransferGenesis.Params.SendEnabled) + require.True(t, ibcTransferGenesis.Params.ReceiveEnabled) + require.Len(t, ibcTransferGenesis.DenomTraces, 0) + +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/ibc/applications/transfer/simulation/params.go b/x/ibc/applications/transfer/simulation/params.go new file mode 100644 index 000000000000..67c61f514ed1 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/params.go @@ -0,0 +1,32 @@ +package simulation + +import ( + "fmt" + "math/rand" + + gogotypes "github.com/gogo/protobuf/types" + + "github.com/cosmos/cosmos-sdk/x/simulation" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// ParamChanges defines the parameters that can be modified by param change proposals +// on the simulation +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, string(types.KeySendEnabled), + func(r *rand.Rand) string { + sendEnabled := RadomEnabled(r) + return fmt.Sprintf("%s", types.ModuleCdc.MustMarshalJSON(&gogotypes.BoolValue{Value: sendEnabled})) + }, + ), + simulation.NewSimParamChange(types.ModuleName, string(types.KeyReceiveEnabled), + func(r *rand.Rand) string { + receiveEnabled := RadomEnabled(r) + return fmt.Sprintf("%s", types.ModuleCdc.MustMarshalJSON(&gogotypes.BoolValue{Value: receiveEnabled})) + }, + ), + } +} diff --git a/x/ibc/applications/transfer/simulation/params_test.go b/x/ibc/applications/transfer/simulation/params_test.go new file mode 100644 index 000000000000..a692d4328e92 --- /dev/null +++ b/x/ibc/applications/transfer/simulation/params_test.go @@ -0,0 +1,36 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"transfer/SendEnabled", "SendEnabled", "false", "transfer"}, + {"transfer/ReceiveEnabled", "ReceiveEnabled", "true", "transfer"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 2) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r), p.Key()) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/ibc/applications/transfer/spec/01_concepts.md b/x/ibc/applications/transfer/spec/01_concepts.md new file mode 100644 index 000000000000..96f05f12a7ca --- /dev/null +++ b/x/ibc/applications/transfer/spec/01_concepts.md @@ -0,0 +1,117 @@ + + +# Concepts + +## Acknowledgements + +ICS20 uses the recommended acknowledgement format as specified by [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope). + +A successful receive of a transfer packet will result in a Result Acknowledgement being written +with the value `[]byte(byte(1))` in the `Response` field. + +An unsuccessful receive of a transfer packet will result in an Error Acknowledgement being written +with the error message in the `Response` field. + +## Denomination Trace + +The denomination trace corresponds to the information that allows a token to be traced back to its +origin chain. It contains a sequence of port and channel identifiers ordered from the most recent to +the oldest in the timeline of transfers. + +This information is included on the token denomination field in the form of a hash to prevent an +unbounded denomination length. For example, the token `transfer/channelToA/uatom` will be displayed +as `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`. + +Each send to any chain other than the one it was previously received from is a movement forwards in +the token's timeline. This causes trace to be added to the token's history and the destination port +and destination channel to be prefixed to the denomination. In these instances the sender chain is +acting as the "source zone". When the token is sent back to the chain it previously received from, the +prefix is removed. This is a backwards movement in the token's timeline and the sender chain is +acting as the "sink zone". + +It is strongly recommended to read the full details of [ADR 001: Coin Source Tracing](./../../../../../docs/architecture/adr-001-coin-source-tracing.md) to understand the implications and context of the IBC token representations. + +### UX suggestions for clients + +For clients (wallets, exchanges, applications, block explorers, etc) that want to display the source of the token, it is recommended to use the following +alternatives for each of the cases below: + +#### Direct connection + +If the denomination trace contains a single identifier prefix pair (as in the example above), then +the easiest way to retrieve the chain and light client identifier is to map the trace information +directly. In summary, this requires querying the channel from the denomination trace identifiers, +and then the counterparty client state using the counterparty port and channel identifiers from the +retrieved channel. + +A general pseudo algorithm would look like the following: + +1. Query the full denomination trace. +2. Query the channel with the `portID/channelID` pair, which corresponds to the first destination of the + token. +3. Query the client state using the identifiers pair. Note that this query will return a `"Not + Found"` response if the current chain is not connected to this channel. +4. Retrieve the the client identifier or chain identifier from the client state (eg: on + Tendermint clients) and store it locally. + +Using the gRPC gataway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` stored on `chainB`: + +1. `GET /ibc_transfer/v1beta1/denom_traces/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` -> `{"path": "transfer/channelToA", "base_denom": "uatom"}` +2. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer/client_state"` -> `{"client_id": "clientA", "chain-id": "chainA", ...}` +3. `GET /ibc/channel/v1beta1/channels/channelToA/ports/transfer"` -> `{"channel_id": "channelToA", port_id": "transfer", counterparty: {"channel_id": "channelToB", port_id": "transfer"}, ...}` +4. `GET /ibc/channel/v1beta1/channels/channelToB/ports/transfer/client_state" -> {"client_id": "clientB", "chain-id": "chainB", ...}` + +Then, the token transfer chain path for the `uatom` denomination would be: `chainA` -> `chainB`. + +### Multiple hops + +The multiple channel hops case applies when the token has passed through multiple chains between the original source and final destination chains. + +The IBC protocol doesn't know the topology of the overall network (i.e connections between chains and identifier names between them). For this reason, in the the multiple hops case, a particular chain in the timeline of the individual transfers can't query the chain and client identifiers of the other chains. + +Take for example the following sequence of transfers `A -> B -> C` for an IBC token, with a final prefix path (trace info) of `transfer/channelChainC/transfer/channelChainB`. What the paragraph above means is that is that even in the case that chain `C` is directly connected to chain `A`, querying the port and channel identifiers that chain `B` uses to connect to chain `A` (eg: `transfer/channelChainA`) can be completely different from the one that chain `C` uses to connect to chain `A` (eg: `transfer/channelToChainA`). + +Thus the proposed solution for clients that the IBC team recommends are the following: + +- **Connect to all chains**: Connecting to all the chains in the timeline would allow clients to + perform the queries outlined in the [direct connection](#direct-connection) section to each + relevant chain. By repeatedly following the port and channel denomination trace transfer timeline, + clients should always be able to find all the relevant identifiers. This comes at the tradeoff + that the client must connect to nodes on each of the chains in order to perform the queries. +- **Relayer as a Service (RaaS)**: A longer term solution is to use/create a relayer service that + could map the denomination trace to the chain path timeline for each token (i.e `origin chain -> + chain #1 -> ... -> chain #(n-1) -> final chain`). These services could provide merkle proofs in + order to allow clients to optionally verify the path timeline correctness for themselves by + running light clients. If the proofs are not verified, they should be considered as trusted third + parties services. Additionally, client would be advised in the future to use RaaS that support the + largest number of connections between chains in the ecosystem. Unfortunately, none of the existing + public relayers (in [Golang](https://github.com/cosmos/relayer) and + [Rust](https://github.com/informalsystems/ibc-rs)), provide this service to clients. + +::: tip +The only viable alternative for clients (at the time of writing) to tokens with multiple connection hops, is to connect to all chains directly and perform relevant queries to each of them in the sequence. +::: + +## Locked Funds + +In some [exceptional cases](./../../../../../docs/architecture/adr-026-ibc-client-recovery-mechanisms.md#exceptional-cases), a client state associated with a given channel cannot be updated. This causes that funds from fungible tokens in that channel will be permanently locked and thus can no longer be transferred. + +To mitigate this, a client update governance proposal can be submitted to update the frozen client +with a new valid header. Once the proposal passes the client state will be unfrozen and the funds +from the associated channels will then be unlocked. This mechanism only applies to clients that +allow updates via governance, such as Tendermint clients. + +In addition to this, it's important to mention that a token must be sent back along the exact route +that it took originally un order to return it to its original form on the source chain (eg: the +Cosmos Hub for the `uatom`). Sending a token back to the same chain across a different channel will +**not** move the token back across its timeline. If a channel in the chain history closes before the +token can be sent back across that channel, then the token will not be returnable to its original +form. + + +## Security Considerations + +For safety, no other module must be capable of minting tokens with the `ibc/` prefix. The IBC +transfer module needs a subset of the denomination space that only it can create tokens in. diff --git a/x/ibc/applications/transfer/spec/02_state.md b/x/ibc/applications/transfer/spec/02_state.md new file mode 100644 index 000000000000..9cab8d677fd6 --- /dev/null +++ b/x/ibc/applications/transfer/spec/02_state.md @@ -0,0 +1,10 @@ + + +# State + +The transfer IBC application module keeps state of the port to which the module is binded and the denomination trace information as outlined in [ADR 01](./../../../../../docs/architecture/adr-001-coin-source-tracing.md). + +- `Port`: `0x01 -> ProtocolBuffer(string)` +- `DenomTrace`: `0x02 | []bytes(traceHash) -> ProtocolBuffer(DenomTrace)` diff --git a/x/ibc/applications/transfer/spec/03_state_transitions.md b/x/ibc/applications/transfer/spec/03_state_transitions.md new file mode 100644 index 000000000000..9090da543432 --- /dev/null +++ b/x/ibc/applications/transfer/spec/03_state_transitions.md @@ -0,0 +1,36 @@ + + +# State Transitions + +## Send Fungible Tokens + +A successful fungible token send has two state transitions depending if the +transfer is a movement forward or backwards in the token's timeline: + +1. Sender chain is the source chain, *i.e* a transfer to any chain other than the one it was previously received from is a movement forwards in the token's timeline. This results in the following state transitions: + +- The coins are transferred to an escrow address (i.e locked) on the sender chain +- The coins are transferred to the receiving chain through IBC TAO logic. + +2. Sender chain is the sink chain, *i.e* the token is sent back to the chain it previously received from. This is a backwards movement in the token's timeline. This results in the following state transitions: + +- The coins (vouchers) are burned on the sender chain +- The coins transferred to the receiving chain though IBC TAO logic. + +## Receive Fungible Tokens + +A successful fungible token receive has two state transitions depending if the +transfer is a movement forward or backwards in the token's timeline: + +1. Receiver chain is the source chain. This is a backwards movement in the token's timeline. This results in the following state transitions: + +- The leftmost port and channel identifier pair is removed from the token denomination prefix. +- The tokens are unescrowed and sent to the receiving address. + +2. Receiver chain is the sink chain. This is a movement forwards in the token's timeline. This results in the following state transitions: + +- Token vouchers are minted by prefixing the destination port and channel identifiers to the trace information. +- The receiving chain stores the new trace information in the store (if not set already). +- The vouchers are sent to the receiving address. diff --git a/x/ibc/applications/transfer/spec/04_messages.md b/x/ibc/applications/transfer/spec/04_messages.md new file mode 100644 index 000000000000..9da7673eb366 --- /dev/null +++ b/x/ibc/applications/transfer/spec/04_messages.md @@ -0,0 +1,40 @@ + + +# Messages + +## MsgTransfer + +A fungible token cross chain transfer is achieved by using the `MsgTransfer`: + +```go +type MsgTransfer struct { + SourcePort string + SourceChannel string + Token sdk.Coin + Sender string + Receiver string + TimeoutHeight ibcexported.Height + TimeoutTimestamp uint64 +} +``` + +This message is expected to fail if: + +- `SourcePort` is invalid (see 24-host naming requirements) +- `SourceChannel` is invalid (see 24-host naming requirements) +- `Token` is invalid (denom is invalid or amount is negative) +- `Token.Amount` is not positive +- `Sender` is empty +- `Receiver` is empty +- `TimeoutHeight` and `TimeoutTimestamp` are both zero +- `Token.Denom` is not a valid IBC denomination as per [ADR 001 - Coin Source Tracing](./../../../../../docs/architecture/adr-001-coin-source-tracing.md). + +This message will send a fungible token to the counterparty chain represented +by the counterparty Channel End connected to the Channel End with the identifiers +`SourcePort` and `SourceChannel`. + +The denomination provided for transfer should correspond to the same denomination +represented on this chain. The prefixes will be added as necessary upon by the +receiving chain. diff --git a/x/ibc/applications/transfer/spec/05_events.md b/x/ibc/applications/transfer/spec/05_events.md new file mode 100644 index 000000000000..51b49da4602d --- /dev/null +++ b/x/ibc/applications/transfer/spec/05_events.md @@ -0,0 +1,44 @@ + + +# Events + +## MsgTransfer + +| Type | Attribute Key | Attribute Value | +|--------------|---------------|-----------------| +| ibc_transfer | sender | {sender} | +| ibc_transfer | receiver | {receiver} | +| message | action | transfer | +| message | module | transfer | + +## OnRecvPacket callback + +| Type | Attribute Key | Attribute Value | +|-----------------------|---------------|-----------------| +| fungible_token_packet | module | transfer | +| fungible_token_packet | receiver | {receiver} | +| fungible_token_packet | denom | {denom} | +| fungible_token_packet | amount | {amount} | +| fungible_token_packet | success | {ackSuccess} | +| denomination_trace | trace_hash | {hex_hash} | + +## OnAcknowledgePacket callback + +| Type | Attribute Key | Attribute Value | +|-----------------------|-----------------|-------------------| +| fungible_token_packet | module | transfer | +| fungible_token_packet | receiver | {receiver} | +| fungible_token_packet | denom | {denom} | +| fungible_token_packet | amount | {amount} | +| fungible_token_packet | success | error | {ack.Response} | + +## OnTimeoutPacket callback + +| Type | Attribute Key | Attribute Value | +|-----------------------|-----------------|-----------------| +| fungible_token_packet | module | transfer | +| fungible_token_packet | refund_receiver | {receiver} | +| fungible_token_packet | denom | {denom} | +| fungible_token_packet | amount | {amount} | diff --git a/x/ibc/applications/transfer/spec/06_metrics.md b/x/ibc/applications/transfer/spec/06_metrics.md new file mode 100644 index 000000000000..21bb51c0a10e --- /dev/null +++ b/x/ibc/applications/transfer/spec/06_metrics.md @@ -0,0 +1,14 @@ + + +# Metrics + +The transfer IBC application module exposes the following set of [metrics](./../../../../../docs/core/telemetry.md). + +| Metric | Description | Unit | Type | +|:--------------------------------|:------------------------------------------------------------------------------------------|:----------------|:--------| +| `tx_msg_ibc_transfer` | The total amount of tokens transferred via IBC in a `MsgTransfer` (source or sink chain) | token | gauge | +| `ibc_transfer_packet_receive` | The total amount of tokens received in a `FungibleTokenPacketData` (source or sink chain) | token | gauge | +| `ibc_transfer_send` | Total number of IBC transfers sent from a chain (source or sink) | transfer | counter | +| `ibc_transfer_receive` | Total number of IBC transfers received to a chain (source or sink) | transfer | counter | diff --git a/x/ibc/applications/transfer/spec/07_params.md b/x/ibc/applications/transfer/spec/07_params.md new file mode 100644 index 000000000000..8d2b97c580d2 --- /dev/null +++ b/x/ibc/applications/transfer/spec/07_params.md @@ -0,0 +1,30 @@ + + +# Parameters + +The ibc-transfer module contains the following parameters: + +| Key | Type | Default Value | +|------------------|------|---------------| +| `SendEnabled` | bool | `true` | +| `ReceiveEnabled` | bool | `true` | + +## SendEnabled + +The transfers enabled parameter controls send cross-chain transfer capabilities for all fungible +tokens. + +To prevent a single token from being transferred from the chain, set the `SendEnabled` parameter to `true` and +then set the bank module's [`SendEnabled` parameter](./../../../../bank/spec/05_params.md#sendenabled) for +the denomination to `false`. + +## ReceiveEnabled + +The transfers enabled parameter controls receive cross-chain transfer capabilities for all fungible +tokens. + +To prevent a single token from being transferred to the chain, set the `ReceiveEnabled` parameter to `true` and +then set the bank module's [`SendEnabled` parameter](./../../../../bank/spec/05_params.md#sendenabled) for +the denomination to `false`. diff --git a/x/ibc/applications/transfer/spec/README.md b/x/ibc/applications/transfer/spec/README.md new file mode 100644 index 000000000000..5230fdde41b6 --- /dev/null +++ b/x/ibc/applications/transfer/spec/README.md @@ -0,0 +1,24 @@ + + +# `ibc-transfer` + +## Abstract + +This paper defines the implementation of the ICS20 protocol on the Cosmos SDK. + +For the general specification please refer to the [ICS20 Specification](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer). + +## Contents + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[State Transitions](03_state_transitions.md)** +4. **[Messages](04_messages.md)** +5. **[Events](05_events.md)** +6. **[Metrics](06_metrics.md)** +7. **[Parameters](07_params.md)** diff --git a/x/ibc/applications/transfer/types/codec.go b/x/ibc/applications/transfer/types/codec.go new file mode 100644 index 000000000000..24ad7e5a9023 --- /dev/null +++ b/x/ibc/applications/transfer/types/codec.go @@ -0,0 +1,41 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "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) { + registry.RegisterImplementations((*sdk.Msg)(nil), &MsgTransfer{}) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +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 + // 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/coin.go b/x/ibc/applications/transfer/types/coin.go new file mode 100644 index 000000000000..08ae9a8d3254 --- /dev/null +++ b/x/ibc/applications/transfer/types/coin.go @@ -0,0 +1,48 @@ +package types + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SenderChainIsSource returns false if the denomination originally came +// from the receiving chain and true otherwise. +func SenderChainIsSource(sourcePort, sourceChannel, denom string) bool { + // This is the prefix that would have been prefixed to the denomination + // on sender chain IF and only if the token originally came from the + // receiving chain. + + return !ReceiverChainIsSource(sourcePort, sourceChannel, denom) +} + +// ReceiverChainIsSource returns true if the denomination originally came +// from the receiving chain and false otherwise. +func ReceiverChainIsSource(sourcePort, sourceChannel, denom string) bool { + // The prefix passed in should contain the SourcePort and SourceChannel. + // If the receiver chain originally sent the token to the sender chain + // the denom will have the sender's SourcePort and SourceChannel as the + // prefix. + + voucherPrefix := GetDenomPrefix(sourcePort, sourceChannel) + return strings.HasPrefix(denom, voucherPrefix) + +} + +// GetDenomPrefix returns the receiving denomination prefix +func GetDenomPrefix(portID, channelID string) string { + return fmt.Sprintf("%s/%s/", portID, channelID) +} + +// GetPrefixedDenom returns the denomination with the portID and channelID prefixed +func GetPrefixedDenom(portID, channelID, baseDenom string) string { + return fmt.Sprintf("%s/%s/%s", portID, channelID, baseDenom) +} + +// GetTransferCoin creates a transfer coin with the port ID and channel ID +// prefixed to the base denom. +func GetTransferCoin(portID, channelID, baseDenom string, amount int64) sdk.Coin { + denomTrace := ParseDenomTrace(GetPrefixedDenom(portID, channelID, baseDenom)) + return sdk.NewInt64Coin(denomTrace.IBCDenom(), amount) +} diff --git a/x/ibc/applications/transfer/types/errors.go b/x/ibc/applications/transfer/types/errors.go new file mode 100644 index 000000000000..07cba1949157 --- /dev/null +++ b/x/ibc/applications/transfer/types/errors.go @@ -0,0 +1,17 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBC channel sentinel errors +var ( + ErrInvalidPacketTimeout = sdkerrors.Register(ModuleName, 2, "invalid packet timeout") + ErrInvalidDenomForTransfer = sdkerrors.Register(ModuleName, 3, "invalid denomination for cross-chain transfer") + ErrInvalidVersion = sdkerrors.Register(ModuleName, 4, "invalid ICS20 version") + ErrInvalidAmount = sdkerrors.Register(ModuleName, 5, "invalid token amount") + ErrTraceNotFound = sdkerrors.Register(ModuleName, 6, "denomination trace not found") + ErrSendDisabled = sdkerrors.Register(ModuleName, 7, "fungible token transfers from this chain are disabled") + ErrReceiveDisabled = sdkerrors.Register(ModuleName, 8, "fungible token transfers to this chain are disabled") + ErrMaxTransferChannels = sdkerrors.Register(ModuleName, 9, "max transfer channels") +) diff --git a/x/ibc/applications/transfer/types/events.go b/x/ibc/applications/transfer/types/events.go new file mode 100644 index 000000000000..a3ed5b413c73 --- /dev/null +++ b/x/ibc/applications/transfer/types/events.go @@ -0,0 +1,21 @@ +package types + +// IBC transfer events +const ( + EventTypeTimeout = "timeout" + EventTypePacket = "fungible_token_packet" + EventTypeTransfer = "ibc_transfer" + EventTypeChannelClose = "channel_closed" + EventTypeDenomTrace = "denomination_trace" + + AttributeKeyReceiver = "receiver" + AttributeKeyDenom = "denom" + AttributeKeyAmount = "amount" + AttributeKeyRefundReceiver = "refund_receiver" + AttributeKeyRefundDenom = "refund_denom" + AttributeKeyRefundAmount = "refund_amount" + AttributeKeyAckSuccess = "success" + AttributeKeyAck = "acknowledgement" + AttributeKeyAckError = "error" + AttributeKeyTraceHash = "trace_hash" +) diff --git a/x/ibc/applications/transfer/types/expected_keepers.go b/x/ibc/applications/transfer/types/expected_keepers.go new file mode 100644 index 000000000000..284463350efc --- /dev/null +++ b/x/ibc/applications/transfer/types/expected_keepers.go @@ -0,0 +1,48 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// AccountKeeper defines the contract required for account APIs. +type AccountKeeper interface { + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, name string) types.ModuleAccountI +} + +// BankKeeper defines the expected bank keeper +type BankKeeper interface { + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + +// ChannelKeeper defines the expected IBC channel keeper +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error +} + +// ClientKeeper defines the expected IBC client keeper +type ClientKeeper interface { + GetClientConsensusState(ctx sdk.Context, clientID string) (connection ibcexported.ConsensusState, found bool) +} + +// ConnectionKeeper defines the expected IBC connection keeper +type ConnectionKeeper interface { + GetConnection(ctx sdk.Context, connectionID string) (connection connectiontypes.ConnectionEnd, found bool) +} + +// PortKeeper defines the expected IBC port keeper +type PortKeeper interface { + BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability +} diff --git a/x/ibc/applications/transfer/types/genesis.go b/x/ibc/applications/transfer/types/genesis.go new file mode 100644 index 000000000000..682b04c4cf0a --- /dev/null +++ b/x/ibc/applications/transfer/types/genesis.go @@ -0,0 +1,35 @@ +package types + +import ( + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// NewGenesisState creates a new ibc-transfer GenesisState instance. +func NewGenesisState(portID string, denomTraces Traces, params Params) *GenesisState { + return &GenesisState{ + PortId: portID, + DenomTraces: denomTraces, + Params: params, + } +} + +// DefaultGenesisState returns a GenesisState with "transfer" as the default PortID. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + PortId: PortID, + DenomTraces: Traces{}, + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + if err := host.PortIdentifierValidator(gs.PortId); err != nil { + return err + } + if err := gs.DenomTraces.Validate(); err != nil { + return err + } + return gs.Params.Validate() +} diff --git a/x/ibc/applications/transfer/types/genesis.pb.go b/x/ibc/applications/transfer/types/genesis.pb.go new file mode 100644 index 000000000000..3ae0442f826b --- /dev/null +++ b/x/ibc/applications/transfer/types/genesis.pb.go @@ -0,0 +1,443 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v1/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 defines the ibc-transfer genesis state +type GenesisState struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + DenomTraces Traces `protobuf:"bytes,2,rep,name=denom_traces,json=denomTraces,proto3,castrepeated=Traces" json:"denom_traces" yaml:"denom_traces"` + Params Params `protobuf:"bytes,3,opt,name=params,proto3" json:"params"` +} + +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_a4f788affd5bea89, []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) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *GenesisState) GetDenomTraces() Traces { + if m != nil { + return m.DenomTraces + } + return nil +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.applications.transfer.v1.GenesisState") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v1/genesis.proto", fileDescriptor_a4f788affd5bea89) +} + +var fileDescriptor_a4f788affd5bea89 = []byte{ + // 317 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xca, 0x4c, 0x4a, 0xd6, + 0x4f, 0x2c, 0x28, 0xc8, 0xc9, 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0x2f, 0x29, 0x4a, + 0xcc, 0x2b, 0x4e, 0x4b, 0x2d, 0xd2, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xc9, 0x4c, 0x4a, 0xd6, 0x43, 0x56, 0xab, 0x07, + 0x53, 0xab, 0x57, 0x66, 0x28, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa8, 0x0f, 0x62, 0x41, + 0xf4, 0x48, 0x69, 0xe3, 0x35, 0x1f, 0xae, 0x1f, 0xac, 0x58, 0xe9, 0x33, 0x23, 0x17, 0x8f, 0x3b, + 0xc4, 0xca, 0xe0, 0x92, 0xc4, 0x92, 0x54, 0x21, 0x6d, 0x2e, 0xf6, 0x82, 0xfc, 0xa2, 0x92, 0xf8, + 0xcc, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xa1, 0x4f, 0xf7, 0xe4, 0xf9, 0x2a, 0x13, + 0x73, 0x73, 0xac, 0x94, 0xa0, 0x12, 0x4a, 0x41, 0x6c, 0x20, 0x96, 0x67, 0x8a, 0x50, 0x11, 0x17, + 0x4f, 0x4a, 0x6a, 0x5e, 0x7e, 0x6e, 0x7c, 0x49, 0x51, 0x62, 0x72, 0x6a, 0xb1, 0x04, 0x93, 0x02, + 0xb3, 0x06, 0xb7, 0x91, 0x86, 0x1e, 0x3e, 0x57, 0xeb, 0xb9, 0x80, 0x74, 0x84, 0x80, 0x34, 0x38, + 0xa9, 0x9e, 0xb8, 0x27, 0xcf, 0xf0, 0xe9, 0x9e, 0xbc, 0x30, 0xc4, 0x7c, 0x64, 0xb3, 0x94, 0x56, + 0xdd, 0x97, 0x67, 0x03, 0xab, 0x2a, 0x0e, 0xe2, 0x4e, 0x81, 0x6b, 0x29, 0x16, 0x72, 0xe2, 0x62, + 0x2b, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0x96, 0x60, 0x56, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xc1, 0x6f, + 0x5b, 0x00, 0x58, 0xad, 0x13, 0x0b, 0xc8, 0xa6, 0x20, 0xa8, 0x4e, 0xa7, 0x88, 0x13, 0x8f, 0xe4, + 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, + 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xb2, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, + 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0x86, 0x52, 0xba, 0xc5, 0x29, 0xd9, 0xfa, + 0x15, 0xfa, 0xb8, 0xc3, 0xb6, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0xac, 0xc6, 0x80, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xda, 0xbb, 0x81, 0x1e, 0xe5, 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 + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.DenomTraces) > 0 { + for iNdEx := len(m.DenomTraces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomTraces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.PortId))) + 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 + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.DenomTraces) > 0 { + for _, e := range m.DenomTraces { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.Params.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 PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomTraces", 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.DenomTraces = append(m.DenomTraces, DenomTrace{}) + if err := m.DenomTraces[len(m.DenomTraces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", 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 := m.Params.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/ibc/applications/transfer/types/genesis_test.go b/x/ibc/applications/transfer/types/genesis_test.go new file mode 100644 index 000000000000..a2aba58ca677 --- /dev/null +++ b/x/ibc/applications/transfer/types/genesis_test.go @@ -0,0 +1,47 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +func TestValidateGenesis(t *testing.T) { + testCases := []struct { + name string + genState *types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + "valid genesis", + &types.GenesisState{ + PortId: "portidone", + }, + true, + }, + { + "invalid client", + &types.GenesisState{ + PortId: "(INVALIDPORT)", + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/applications/transfer/types/keys.go b/x/ibc/applications/transfer/types/keys.go new file mode 100644 index 000000000000..c156af3fd88d --- /dev/null +++ b/x/ibc/applications/transfer/types/keys.go @@ -0,0 +1,55 @@ +package types + +import ( + "crypto/sha256" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName defines the IBC transfer name + ModuleName = "transfer" + + // Version defines the current version the IBC tranfer + // module supports + Version = "ics20-1" + + // PortID is the default port id that transfer module binds to + PortID = "transfer" + + // StoreKey is the store key string for IBC transfer + StoreKey = ModuleName + + // RouterKey is the message route for IBC transfer + RouterKey = ModuleName + + // QuerierRoute is the querier route for IBC transfer + QuerierRoute = ModuleName + + // DenomPrefix is the prefix used for internal SDK coin representation. + DenomPrefix = "ibc" +) + +var ( + // PortKey defines the key to store the port ID in store + PortKey = []byte{0x01} + // DenomTraceKey defines the key to store the denomination trace info in store + DenomTraceKey = []byte{0x02} +) + +// GetEscrowAddress returns the escrow address for the specified channel. +// The escrow address follows the format as outlined in ADR 028: +// https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-028-public-key-addresses.md +func GetEscrowAddress(portID, channelID string) sdk.AccAddress { + // a slash is used to create domain separation between port and channel identifiers to + // prevent address collisions between escrow addresses created for different channels + contents := fmt.Sprintf("%s/%s", portID, channelID) + + // ADR 028 AddressHash construction + preImage := []byte(Version) + preImage = append(preImage, 0) + preImage = append(preImage, contents...) + hash := sha256.Sum256(preImage) + return hash[:20] +} diff --git a/x/ibc/applications/transfer/types/keys_test.go b/x/ibc/applications/transfer/types/keys_test.go new file mode 100644 index 000000000000..9ab3314c2e3b --- /dev/null +++ b/x/ibc/applications/transfer/types/keys_test.go @@ -0,0 +1,24 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" +) + +// Test that there is domain separation between the port id and the channel id otherwise an +// escrow address may overlap with another channel end +func TestGetEscrowAddress(t *testing.T) { + var ( + port1 = "transfer" + channel1 = "channel" + port2 = "transfercha" + channel2 = "nnel" + ) + + escrow1 := types.GetEscrowAddress(port1, channel1) + escrow2 := types.GetEscrowAddress(port2, channel2) + require.NotEqual(t, escrow1, escrow2) +} diff --git a/x/ibc/applications/transfer/types/msgs.go b/x/ibc/applications/transfer/types/msgs.go new file mode 100644 index 000000000000..cf2293213a82 --- /dev/null +++ b/x/ibc/applications/transfer/types/msgs.go @@ -0,0 +1,85 @@ +package types + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// msg types +const ( + TypeMsgTransfer = "transfer" +) + +// NewMsgTransfer creates a new MsgTransfer instance +//nolint:interfacer +func NewMsgTransfer( + sourcePort, sourceChannel string, + token sdk.Coin, sender sdk.AccAddress, receiver string, + timeoutHeight clienttypes.Height, timeoutTimestamp uint64, +) *MsgTransfer { + return &MsgTransfer{ + SourcePort: sourcePort, + SourceChannel: sourceChannel, + Token: token, + Sender: sender.String(), + Receiver: receiver, + TimeoutHeight: timeoutHeight, + TimeoutTimestamp: timeoutTimestamp, + } +} + +// Route implements sdk.Msg +func (MsgTransfer) Route() string { + return RouterKey +} + +// Type implements sdk.Msg +func (MsgTransfer) Type() string { + return TypeMsgTransfer +} + +// ValidateBasic performs a basic check of the MsgTransfer fields. +// NOTE: timeout height or timestamp values can be 0 to disable the timeout. +// NOTE: The recipient addresses format is not validated as the format defined by +// the chain is not known to IBC. +func (msg MsgTransfer) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.SourcePort); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + if err := host.ChannelIdentifierValidator(msg.SourceChannel); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + if !msg.Token.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Token.String()) + } + if !msg.Token.IsPositive() { + return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, msg.Token.String()) + } + // NOTE: sender format must be validated as it is required by the GetSigners function. + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + if strings.TrimSpace(msg.Receiver) == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing recipient address") + } + return ValidateIBCDenom(msg.Token.Denom) +} + +// GetSignBytes implements sdk.Msg. +func (msg MsgTransfer) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners implements sdk.Msg +func (msg MsgTransfer) GetSigners() []sdk.AccAddress { + valAddr, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{valAddr} +} diff --git a/x/ibc/applications/transfer/types/msgs_test.go b/x/ibc/applications/transfer/types/msgs_test.go new file mode 100644 index 000000000000..1fc70c543bbe --- /dev/null +++ b/x/ibc/applications/transfer/types/msgs_test.go @@ -0,0 +1,103 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// define constants used for testing +const ( + validPort = "testportid" + invalidPort = "(invalidport1)" + invalidShortPort = "p" + invalidLongPort = "invalidlongportinvalidlongportinvalidlongportinvalidlongportinvalid" + + validChannel = "testchannel" + invalidChannel = "(invalidchannel1)" + invalidShortChannel = "invalid" + invalidLongChannel = "invalidlongchannelinvalidlongchannelinvalidlongchannelinvalidlongchannel" +) + +var ( + addr1 = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + addr2 = sdk.AccAddress("testaddr2").String() + emptyAddr sdk.AccAddress + + coin = sdk.NewCoin("atom", sdk.NewInt(100)) + ibcCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(100)) + invalidIBCCoin = sdk.NewCoin("notibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(100)) + invalidDenomCoin = sdk.Coin{Denom: "0atom", Amount: sdk.NewInt(100)} + zeroCoin = sdk.Coin{Denom: "atoms", Amount: sdk.NewInt(0)} + + timeoutHeight = clienttypes.NewHeight(0, 10) +) + +// TestMsgTransferRoute tests Route for MsgTransfer +func TestMsgTransferRoute(t *testing.T) { + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + + require.Equal(t, RouterKey, msg.Route()) +} + +// TestMsgTransferType tests Type for MsgTransfer +func TestMsgTransferType(t *testing.T) { + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + + 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 { + name string + msg *MsgTransfer + expPass bool + }{ + {"valid msg with base denom", NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), true}, + {"valid msg with trace hash", NewMsgTransfer(validPort, validChannel, ibcCoin, addr1, addr2, timeoutHeight, 0), true}, + {"invalid ibc denom", NewMsgTransfer(validPort, validChannel, invalidIBCCoin, addr1, addr2, timeoutHeight, 0), false}, + {"too short port id", NewMsgTransfer(invalidShortPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"too long port id", NewMsgTransfer(invalidLongPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"port id contains non-alpha", NewMsgTransfer(invalidPort, validChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"too short channel id", NewMsgTransfer(validPort, invalidShortChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"too long channel id", NewMsgTransfer(validPort, invalidLongChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"channel id contains non-alpha", NewMsgTransfer(validPort, invalidChannel, coin, addr1, addr2, timeoutHeight, 0), false}, + {"invalid denom", NewMsgTransfer(validPort, validChannel, invalidDenomCoin, addr1, addr2, timeoutHeight, 0), false}, + {"zero coin", NewMsgTransfer(validPort, validChannel, zeroCoin, addr1, addr2, timeoutHeight, 0), false}, + {"missing sender address", NewMsgTransfer(validPort, validChannel, coin, emptyAddr, addr2, timeoutHeight, 0), false}, + {"missing recipient address", NewMsgTransfer(validPort, validChannel, coin, addr1, "", timeoutHeight, 0), false}, + {"empty coin", NewMsgTransfer(validPort, validChannel, sdk.Coin{}, addr1, addr2, timeoutHeight, 0), false}, + } + + for i, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +// TestMsgTransferGetSigners tests GetSigners for MsgTransfer +func TestMsgTransferGetSigners(t *testing.T) { + msg := NewMsgTransfer(validPort, validChannel, coin, addr1, addr2, timeoutHeight, 0) + res := msg.GetSigners() + + require.Equal(t, []sdk.AccAddress{addr1}, res) +} diff --git a/x/ibc/applications/transfer/types/packet.go b/x/ibc/applications/transfer/types/packet.go new file mode 100644 index 000000000000..d726577f6f5c --- /dev/null +++ b/x/ibc/applications/transfer/types/packet.go @@ -0,0 +1,56 @@ +package types + +import ( + "strings" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + // DefaultRelativePacketTimeoutHeight is the default packet timeout height (in blocks) relative + // to the current block height of the counterparty chain provided by the client state. The + // timeout is disabled when set to 0. + DefaultRelativePacketTimeoutHeight = "0-1000" + + // DefaultRelativePacketTimeoutTimestamp is the default packet timeout timestamp (in nanoseconds) + // relative to the current block timestamp of the counterparty chain provided by the client + // state. The timeout is disabled when set to 0. The default is currently set to a 10 minute + // timeout. + DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds()) +) + +// NewFungibleTokenPacketData contructs a new FungibleTokenPacketData instance +func NewFungibleTokenPacketData( + denom string, amount uint64, + sender, receiver string, +) FungibleTokenPacketData { + return FungibleTokenPacketData{ + Denom: denom, + Amount: amount, + Sender: sender, + Receiver: receiver, + } +} + +// ValidateBasic is used for validating the token transfer. +// NOTE: The addresses formats are not validated as the sender and recipient can have different +// formats defined by their corresponding chains that are not known to IBC. +func (ftpd FungibleTokenPacketData) ValidateBasic() error { + if ftpd.Amount == 0 { + return sdkerrors.Wrap(ErrInvalidAmount, "amount cannot be 0") + } + if strings.TrimSpace(ftpd.Sender) == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be blank") + } + if strings.TrimSpace(ftpd.Receiver) == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "receiver address cannot be blank") + } + return ValidatePrefixedDenom(ftpd.Denom) +} + +// GetBytes is a helper for serialising +func (ftpd FungibleTokenPacketData) GetBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&ftpd)) +} diff --git a/x/ibc/applications/transfer/types/packet_test.go b/x/ibc/applications/transfer/types/packet_test.go new file mode 100644 index 000000000000..1edcb093d3c0 --- /dev/null +++ b/x/ibc/applications/transfer/types/packet_test.go @@ -0,0 +1,36 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + denom = "transfer/gaiachannel/atom" + amount = uint64(100) +) + +// TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData +func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { + testCases := []struct { + name string + packetData FungibleTokenPacketData + expPass bool + }{ + {"valid packet", NewFungibleTokenPacketData(denom, amount, addr1.String(), addr2), true}, + {"invalid denom", NewFungibleTokenPacketData("", amount, addr1.String(), addr2), false}, + {"invalid amount", NewFungibleTokenPacketData(denom, 0, addr1.String(), addr2), false}, + {"missing sender address", NewFungibleTokenPacketData(denom, amount, emptyAddr.String(), addr2), false}, + {"missing recipient address", NewFungibleTokenPacketData(denom, amount, addr1.String(), emptyAddr.String()), false}, + } + + for i, tc := range testCases { + err := tc.packetData.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %v", i, err) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/applications/transfer/types/params.go b/x/ibc/applications/transfer/types/params.go new file mode 100644 index 000000000000..4ecdfab77e7b --- /dev/null +++ b/x/ibc/applications/transfer/types/params.go @@ -0,0 +1,65 @@ +package types + +import ( + "fmt" + + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +const ( + // DefaultSendEnabled enabled + DefaultSendEnabled = true + // DefaultReceiveEnabled enabled + DefaultReceiveEnabled = true +) + +var ( + // KeySendEnabled is store's key for SendEnabled Params + KeySendEnabled = []byte("SendEnabled") + // KeyReceiveEnabled is store's key for ReceiveEnabled Params + KeyReceiveEnabled = []byte("ReceiveEnabled") +) + +// ParamKeyTable type declaration for parameters +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// NewParams creates a new parameter configuration for the ibc transfer module +func NewParams(enableSend, enableReceive bool) Params { + return Params{ + SendEnabled: enableSend, + ReceiveEnabled: enableReceive, + } +} + +// DefaultParams is the default parameter configuration for the ibc-transfer module +func DefaultParams() Params { + return NewParams(DefaultSendEnabled, DefaultReceiveEnabled) +} + +// Validate all ibc-transfer module parameters +func (p Params) Validate() error { + if err := validateEnabled(p.SendEnabled); err != nil { + return err + } + + return validateEnabled(p.ReceiveEnabled) +} + +// ParamSetPairs implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeySendEnabled, p.SendEnabled, validateEnabled), + paramtypes.NewParamSetPair(KeyReceiveEnabled, p.ReceiveEnabled, validateEnabled), + } +} + +func validateEnabled(i interface{}) error { + _, ok := i.(bool) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} diff --git a/x/ibc/applications/transfer/types/params_test.go b/x/ibc/applications/transfer/types/params_test.go new file mode 100644 index 000000000000..825efb825c1b --- /dev/null +++ b/x/ibc/applications/transfer/types/params_test.go @@ -0,0 +1,12 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidateParams(t *testing.T) { + require.NoError(t, DefaultParams().Validate()) + require.NoError(t, NewParams(true, false).Validate()) +} diff --git a/x/ibc/applications/transfer/types/query.pb.go b/x/ibc/applications/transfer/types/query.pb.go new file mode 100644 index 000000000000..1c1d69295192 --- /dev/null +++ b/x/ibc/applications/transfer/types/query.pb.go @@ -0,0 +1,1418 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v1/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 + +// QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC +// method +type QueryDenomTraceRequest struct { + // hash (in hex format) of the denomination trace information. + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *QueryDenomTraceRequest) Reset() { *m = QueryDenomTraceRequest{} } +func (m *QueryDenomTraceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDenomTraceRequest) ProtoMessage() {} +func (*QueryDenomTraceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{0} +} +func (m *QueryDenomTraceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomTraceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomTraceRequest.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 *QueryDenomTraceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomTraceRequest.Merge(m, src) +} +func (m *QueryDenomTraceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomTraceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomTraceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomTraceRequest proto.InternalMessageInfo + +func (m *QueryDenomTraceRequest) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +// QueryDenomTraceResponse is the response type for the Query/DenomTrace RPC +// method. +type QueryDenomTraceResponse struct { + // denom_trace returns the requested denomination trace information. + DenomTrace *DenomTrace `protobuf:"bytes,1,opt,name=denom_trace,json=denomTrace,proto3" json:"denom_trace,omitempty"` +} + +func (m *QueryDenomTraceResponse) Reset() { *m = QueryDenomTraceResponse{} } +func (m *QueryDenomTraceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDenomTraceResponse) ProtoMessage() {} +func (*QueryDenomTraceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{1} +} +func (m *QueryDenomTraceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomTraceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomTraceResponse.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 *QueryDenomTraceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomTraceResponse.Merge(m, src) +} +func (m *QueryDenomTraceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomTraceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomTraceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomTraceResponse proto.InternalMessageInfo + +func (m *QueryDenomTraceResponse) GetDenomTrace() *DenomTrace { + if m != nil { + return m.DenomTrace + } + return nil +} + +// QueryConnectionsRequest is the request type for the Query/DenomTraces RPC +// method +type QueryDenomTracesRequest struct { + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDenomTracesRequest) Reset() { *m = QueryDenomTracesRequest{} } +func (m *QueryDenomTracesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDenomTracesRequest) ProtoMessage() {} +func (*QueryDenomTracesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{2} +} +func (m *QueryDenomTracesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomTracesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomTracesRequest.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 *QueryDenomTracesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomTracesRequest.Merge(m, src) +} +func (m *QueryDenomTracesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomTracesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomTracesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomTracesRequest proto.InternalMessageInfo + +func (m *QueryDenomTracesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryConnectionsResponse is the response type for the Query/DenomTraces RPC +// method. +type QueryDenomTracesResponse struct { + // denom_traces returns all denominations trace information. + DenomTraces Traces `protobuf:"bytes,1,rep,name=denom_traces,json=denomTraces,proto3,castrepeated=Traces" json:"denom_traces"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDenomTracesResponse) Reset() { *m = QueryDenomTracesResponse{} } +func (m *QueryDenomTracesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDenomTracesResponse) ProtoMessage() {} +func (*QueryDenomTracesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{3} +} +func (m *QueryDenomTracesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDenomTracesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDenomTracesResponse.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 *QueryDenomTracesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDenomTracesResponse.Merge(m, src) +} +func (m *QueryDenomTracesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDenomTracesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDenomTracesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDenomTracesResponse proto.InternalMessageInfo + +func (m *QueryDenomTracesResponse) GetDenomTraces() Traces { + if m != nil { + return m.DenomTraces + } + return nil +} + +func (m *QueryDenomTracesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{4} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params defines the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a638e2800a01538c, []int{5} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + +func init() { + proto.RegisterType((*QueryDenomTraceRequest)(nil), "ibc.applications.transfer.v1.QueryDenomTraceRequest") + proto.RegisterType((*QueryDenomTraceResponse)(nil), "ibc.applications.transfer.v1.QueryDenomTraceResponse") + proto.RegisterType((*QueryDenomTracesRequest)(nil), "ibc.applications.transfer.v1.QueryDenomTracesRequest") + proto.RegisterType((*QueryDenomTracesResponse)(nil), "ibc.applications.transfer.v1.QueryDenomTracesResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "ibc.applications.transfer.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "ibc.applications.transfer.v1.QueryParamsResponse") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v1/query.proto", fileDescriptor_a638e2800a01538c) +} + +var fileDescriptor_a638e2800a01538c = []byte{ + // 528 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x3f, 0x6f, 0xd3, 0x40, + 0x14, 0xcf, 0x95, 0x12, 0x89, 0x17, 0xc4, 0x70, 0x54, 0x10, 0x59, 0x95, 0x5b, 0x59, 0x08, 0x02, + 0x85, 0x3b, 0x5c, 0xa0, 0x30, 0xa0, 0x0e, 0x15, 0x02, 0xb1, 0x95, 0xc0, 0x80, 0x60, 0x40, 0x67, + 0xe7, 0x70, 0x2c, 0x1a, 0x9f, 0xeb, 0xbb, 0x44, 0x54, 0x88, 0x85, 0x4f, 0x80, 0xc4, 0x8e, 0x98, + 0xd9, 0x19, 0xd8, 0x18, 0x3b, 0x56, 0x62, 0x61, 0x02, 0x94, 0xf0, 0x41, 0x90, 0xef, 0xce, 0x8d, + 0xa3, 0x20, 0x13, 0x4f, 0x39, 0x5d, 0xde, 0xef, 0xfd, 0xfe, 0xbc, 0xe7, 0x83, 0x4e, 0x1c, 0x84, + 0x94, 0xa5, 0xe9, 0x5e, 0x1c, 0x32, 0x15, 0x8b, 0x44, 0x52, 0x95, 0xb1, 0x44, 0xbe, 0xe4, 0x19, + 0x1d, 0xf9, 0x74, 0x7f, 0xc8, 0xb3, 0x03, 0x92, 0x66, 0x42, 0x09, 0xbc, 0x1a, 0x07, 0x21, 0x29, + 0x57, 0x92, 0xa2, 0x92, 0x8c, 0x7c, 0x67, 0x25, 0x12, 0x91, 0xd0, 0x85, 0x34, 0x3f, 0x19, 0x8c, + 0x73, 0x25, 0x14, 0x72, 0x20, 0x24, 0x0d, 0x98, 0xe4, 0xa6, 0x19, 0x1d, 0xf9, 0x01, 0x57, 0xcc, + 0xa7, 0x29, 0x8b, 0xe2, 0x44, 0x37, 0xb2, 0xb5, 0x1b, 0x95, 0x4a, 0x8e, 0xb9, 0x4c, 0xf1, 0x6a, + 0x24, 0x44, 0xb4, 0xc7, 0x29, 0x4b, 0x63, 0xca, 0x92, 0x44, 0x28, 0x2b, 0x49, 0xff, 0xeb, 0x5d, + 0x85, 0x73, 0x8f, 0x72, 0xb2, 0x7b, 0x3c, 0x11, 0x83, 0x27, 0x19, 0x0b, 0x79, 0x97, 0xef, 0x0f, + 0xb9, 0x54, 0x18, 0xc3, 0x72, 0x9f, 0xc9, 0x7e, 0x1b, 0xad, 0xa3, 0xce, 0xa9, 0xae, 0x3e, 0x7b, + 0x3d, 0x38, 0x3f, 0x57, 0x2d, 0x53, 0x91, 0x48, 0x8e, 0x1f, 0x42, 0xab, 0x97, 0xdf, 0xbe, 0x50, + 0xf9, 0xb5, 0x46, 0xb5, 0x36, 0x3b, 0xa4, 0x2a, 0x09, 0x52, 0x6a, 0x03, 0xbd, 0xe3, 0xb3, 0xc7, + 0xe6, 0x58, 0x64, 0x21, 0xea, 0x3e, 0xc0, 0x34, 0x0d, 0x4b, 0x72, 0x91, 0x98, 0xe8, 0x48, 0x1e, + 0x1d, 0x31, 0x73, 0xb0, 0xd1, 0x91, 0x5d, 0x16, 0x15, 0x86, 0xba, 0x25, 0xa4, 0xf7, 0x0d, 0x41, + 0x7b, 0x9e, 0xc3, 0x5a, 0x79, 0x0e, 0xa7, 0x4b, 0x56, 0x64, 0x1b, 0xad, 0x9f, 0xa8, 0xe3, 0x65, + 0xe7, 0xcc, 0xe1, 0xcf, 0xb5, 0xc6, 0xe7, 0x5f, 0x6b, 0x4d, 0xdb, 0xb7, 0x35, 0xf5, 0x26, 0xf1, + 0x83, 0x19, 0x07, 0x4b, 0xda, 0xc1, 0xa5, 0xff, 0x3a, 0x30, 0xca, 0x66, 0x2c, 0xac, 0x00, 0xd6, + 0x0e, 0x76, 0x59, 0xc6, 0x06, 0x45, 0x40, 0xde, 0x63, 0x38, 0x3b, 0x73, 0x6b, 0x2d, 0xdd, 0x85, + 0x66, 0xaa, 0x6f, 0x6c, 0x66, 0x17, 0xaa, 0xcd, 0x58, 0xb4, 0xc5, 0x6c, 0x7e, 0x5c, 0x86, 0x93, + 0xba, 0x2b, 0xfe, 0x8a, 0x00, 0xa6, 0x4e, 0xf1, 0xcd, 0xea, 0x36, 0xff, 0xde, 0x2c, 0xe7, 0x56, + 0x4d, 0x94, 0xf1, 0xe0, 0x6d, 0xbf, 0xfb, 0xfe, 0xe7, 0xc3, 0xd2, 0x1d, 0xbc, 0x45, 0xab, 0xd6, + 0xdf, 0x7c, 0x32, 0xe5, 0xf9, 0xd1, 0x37, 0xf9, 0xee, 0xbe, 0xc5, 0x5f, 0x10, 0xb4, 0x4a, 0xe3, + 0xc6, 0xf5, 0x64, 0x14, 0x09, 0x3b, 0x5b, 0x75, 0x61, 0x56, 0xfe, 0x6d, 0x2d, 0xdf, 0xc7, 0xb4, + 0xa6, 0x7c, 0xfc, 0x09, 0x41, 0xd3, 0x0c, 0x04, 0x5f, 0x5f, 0x80, 0x7b, 0x66, 0x1f, 0x1c, 0xbf, + 0x06, 0xc2, 0x0a, 0xf5, 0xb5, 0xd0, 0x0d, 0x7c, 0x79, 0x01, 0xa1, 0x66, 0x41, 0x76, 0x9e, 0x1e, + 0x8e, 0x5d, 0x74, 0x34, 0x76, 0xd1, 0xef, 0xb1, 0x8b, 0xde, 0x4f, 0xdc, 0xc6, 0xd1, 0xc4, 0x6d, + 0xfc, 0x98, 0xb8, 0x8d, 0x67, 0xdb, 0x51, 0xac, 0xfa, 0xc3, 0x80, 0x84, 0x62, 0x40, 0xed, 0x0b, + 0x67, 0x7e, 0xae, 0xc9, 0xde, 0x2b, 0xfa, 0xba, 0x82, 0x42, 0x1d, 0xa4, 0x5c, 0x06, 0x4d, 0xfd, + 0x4c, 0xdd, 0xf8, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x7f, 0xfe, 0xbd, 0x7d, 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 { + // DenomTrace queries a denomination trace information. + DenomTrace(ctx context.Context, in *QueryDenomTraceRequest, opts ...grpc.CallOption) (*QueryDenomTraceResponse, error) + // DenomTraces queries all denomination traces. + DenomTraces(ctx context.Context, in *QueryDenomTracesRequest, opts ...grpc.CallOption) (*QueryDenomTracesResponse, error) + // Params queries all parameters of the ibc-transfer module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) DenomTrace(ctx context.Context, in *QueryDenomTraceRequest, opts ...grpc.CallOption) (*QueryDenomTraceResponse, error) { + out := new(QueryDenomTraceResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.transfer.v1.Query/DenomTrace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DenomTraces(ctx context.Context, in *QueryDenomTracesRequest, opts ...grpc.CallOption) (*QueryDenomTracesResponse, error) { + out := new(QueryDenomTracesResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.transfer.v1.Query/DenomTraces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.transfer.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // DenomTrace queries a denomination trace information. + DenomTrace(context.Context, *QueryDenomTraceRequest) (*QueryDenomTraceResponse, error) + // DenomTraces queries all denomination traces. + DenomTraces(context.Context, *QueryDenomTracesRequest) (*QueryDenomTracesResponse, error) + // Params queries all parameters of the ibc-transfer module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) DenomTrace(ctx context.Context, req *QueryDenomTraceRequest) (*QueryDenomTraceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DenomTrace not implemented") +} +func (*UnimplementedQueryServer) DenomTraces(ctx context.Context, req *QueryDenomTracesRequest) (*QueryDenomTracesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DenomTraces not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_DenomTrace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDenomTraceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DenomTrace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.transfer.v1.Query/DenomTrace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DenomTrace(ctx, req.(*QueryDenomTraceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DenomTraces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDenomTracesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DenomTraces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.transfer.v1.Query/DenomTraces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DenomTraces(ctx, req.(*QueryDenomTracesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.transfer.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.applications.transfer.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DenomTrace", + Handler: _Query_DenomTrace_Handler, + }, + { + MethodName: "DenomTraces", + Handler: _Query_DenomTraces_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/applications/transfer/v1/query.proto", +} + +func (m *QueryDenomTraceRequest) 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 *QueryDenomTraceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomTraceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDenomTraceResponse) 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 *QueryDenomTraceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomTraceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DenomTrace != nil { + { + size, err := m.DenomTrace.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 *QueryDenomTracesRequest) 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 *QueryDenomTracesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomTracesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDenomTracesResponse) 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 *QueryDenomTracesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDenomTracesResponse) 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.DenomTraces) > 0 { + for iNdEx := len(m.DenomTraces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DenomTraces[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 *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.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 *QueryDenomTraceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDenomTraceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DenomTrace != nil { + l = m.DenomTrace.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDenomTracesRequest) 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 *QueryDenomTracesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.DenomTraces) > 0 { + for _, e := range m.DenomTraces { + 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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.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 *QueryDenomTraceRequest) 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: QueryDenomTraceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDenomTraceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", 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.Hash = 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 *QueryDenomTraceResponse) 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: QueryDenomTraceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDenomTraceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomTrace", 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.DenomTrace == nil { + m.DenomTrace = &DenomTrace{} + } + if err := m.DenomTrace.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 *QueryDenomTracesRequest) 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: QueryDenomTracesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDenomTracesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryDenomTracesResponse) 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: QueryDenomTracesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDenomTracesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DenomTraces", 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.DenomTraces = append(m.DenomTraces, DenomTrace{}) + if err := m.DenomTraces[len(m.DenomTraces)-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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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.Params == nil { + m.Params = &Params{} + } + if err := m.Params.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/ibc/applications/transfer/types/query.pb.gw.go b/x/ibc/applications/transfer/types/query.pb.gw.go new file mode 100644 index 000000000000..99a7f1906350 --- /dev/null +++ b/x/ibc/applications/transfer/types/query.pb.gw.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ibc/applications/transfer/v1/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_DenomTrace_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomTraceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := client.DenomTrace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DenomTrace_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomTraceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "hash") + } + + protoReq.Hash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "hash", err) + } + + msg, err := server.DenomTrace(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_DenomTraces_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_DenomTraces_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomTracesRequest + 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_DenomTraces_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DenomTraces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DenomTraces_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDenomTracesRequest + 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_DenomTraces_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DenomTraces(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(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_DenomTrace_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_DenomTrace_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_DenomTrace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DenomTraces_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_DenomTraces_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_DenomTraces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_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_DenomTrace_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_DenomTrace_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_DenomTrace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DenomTraces_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_DenomTraces_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_DenomTraces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_DenomTrace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DenomTraces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "denom_traces"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "applications", "transfer", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_DenomTrace_0 = runtime.ForwardResponseMessage + + forward_Query_DenomTraces_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/ibc/applications/transfer/types/trace.go b/x/ibc/applications/transfer/types/trace.go new file mode 100644 index 000000000000..f45113efa3ff --- /dev/null +++ b/x/ibc/applications/transfer/types/trace.go @@ -0,0 +1,203 @@ +package types + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "sort" + "strings" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// ParseDenomTrace parses a string with the ibc prefix (denom trace) and the base denomination +// into a DenomTrace type. +// +// Examples: +// +// - "portidone/channelidone/uatom" => DenomTrace{Path: "portidone/channelidone", BaseDenom: "uatom"} +// - "uatom" => DenomTrace{Path: "", BaseDenom: "uatom"} +func ParseDenomTrace(rawDenom string) DenomTrace { + denomSplit := strings.Split(rawDenom, "/") + + if denomSplit[0] == rawDenom { + return DenomTrace{ + Path: "", + BaseDenom: rawDenom, + } + } + + return DenomTrace{ + Path: strings.Join(denomSplit[:len(denomSplit)-1], "/"), + BaseDenom: denomSplit[len(denomSplit)-1], + } +} + +// Hash returns the hex bytes of the SHA256 hash of the DenomTrace fields using the following formula: +// +// hash = sha256(tracePath + "/" + baseDenom) +func (dt DenomTrace) Hash() tmbytes.HexBytes { + hash := sha256.Sum256([]byte(dt.GetFullDenomPath())) + return hash[:] +} + +// GetPrefix returns the receiving denomination prefix composed by the trace info and a separator. +func (dt DenomTrace) GetPrefix() string { + return dt.Path + "/" +} + +// IBCDenom a coin denomination for an ICS20 fungible token in the format +// 'ibc/{hash(tracePath + baseDenom)}'. If the trace is empty, it will return the base denomination. +func (dt DenomTrace) IBCDenom() string { + if dt.Path != "" { + return fmt.Sprintf("%s/%s", DenomPrefix, dt.Hash()) + } + return dt.BaseDenom +} + +// GetFullDenomPath returns the full denomination according to the ICS20 specification: +// tracePath + "/" + baseDenom +// If there exists no trace then the base denomination is returned. +func (dt DenomTrace) GetFullDenomPath() string { + if dt.Path == "" { + return dt.BaseDenom + } + return dt.GetPrefix() + dt.BaseDenom +} + +func validateTraceIdentifiers(identifiers []string) error { + if len(identifiers) == 0 || len(identifiers)%2 != 0 { + return fmt.Errorf("trace info must come in pairs of port and channel identifiers '{portID}/{channelID}', got the identifiers: %s", identifiers) + } + + // validate correctness of port and channel identifiers + for i := 0; i < len(identifiers); i += 2 { + if err := host.PortIdentifierValidator(identifiers[i]); err != nil { + return sdkerrors.Wrapf(err, "invalid port ID at position %d", i) + } + if err := host.ChannelIdentifierValidator(identifiers[i+1]); err != nil { + return sdkerrors.Wrapf(err, "invalid channel ID at position %d", i) + } + } + return nil +} + +// Validate performs a basic validation of the DenomTrace fields. +func (dt DenomTrace) Validate() error { + // empty trace is accepted when token lives on the original chain + switch { + case dt.Path == "" && dt.BaseDenom != "": + return nil + case strings.TrimSpace(dt.BaseDenom) == "": + return fmt.Errorf("base denomination cannot be blank") + } + + // NOTE: no base denomination validation + + identifiers := strings.Split(dt.Path, "/") + return validateTraceIdentifiers(identifiers) +} + +// Traces defines a wrapper type for a slice of DenomTrace. +type Traces []DenomTrace + +// Validate performs a basic validation of each denomination trace info. +func (t Traces) Validate() error { + seenTraces := make(map[string]bool) + for i, trace := range t { + hash := trace.Hash().String() + if seenTraces[hash] { + return fmt.Errorf("duplicated denomination trace with hash %s", trace.Hash()) + } + + if err := trace.Validate(); err != nil { + return sdkerrors.Wrapf(err, "failed denom trace %d validation", i) + } + seenTraces[hash] = true + } + return nil +} + +var _ sort.Interface = Traces{} + +// Len implements sort.Interface for Traces +func (t Traces) Len() int { return len(t) } + +// Less implements sort.Interface for Traces +func (t Traces) Less(i, j int) bool { return t[i].GetFullDenomPath() < t[j].GetFullDenomPath() } + +// Swap implements sort.Interface for Traces +func (t Traces) Swap(i, j int) { t[i], t[j] = t[j], t[i] } + +// Sort is a helper function to sort the set of denomination traces in-place +func (t Traces) Sort() Traces { + sort.Sort(t) + return t +} + +// ValidatePrefixedDenom checks that the denomination for an IBC fungible token packet denom is correctly prefixed. +// The function will return no error if the given string follows one of the two formats: +// +// - Prefixed denomination: '{portIDN}/{channelIDN}/.../{portID0}/{channelID0}/baseDenom' +// - Unprefixed denomination: 'baseDenom' +func ValidatePrefixedDenom(denom string) error { + denomSplit := strings.Split(denom, "/") + if denomSplit[0] == denom && strings.TrimSpace(denom) != "" { + // NOTE: no base denomination validation + return nil + } + + if strings.TrimSpace(denomSplit[len(denomSplit)-1]) == "" { + return sdkerrors.Wrap(ErrInvalidDenomForTransfer, "base denomination cannot be blank") + } + + identifiers := denomSplit[:len(denomSplit)-1] + return validateTraceIdentifiers(identifiers) +} + +// ValidateIBCDenom validates that the given denomination is either: +// +// - A valid base denomination (eg: 'uatom') +// - A valid fungible token representation (i.e 'ibc/{hash}') per ADR 001 https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-001-coin-source-tracing.md +func ValidateIBCDenom(denom string) error { + if err := sdk.ValidateDenom(denom); err != nil { + return err + } + + denomSplit := strings.SplitN(denom, "/", 2) + + switch { + case strings.TrimSpace(denom) == "", + len(denomSplit) == 1 && denomSplit[0] == DenomPrefix, + len(denomSplit) == 2 && (denomSplit[0] != DenomPrefix || strings.TrimSpace(denomSplit[1]) == ""): + return sdkerrors.Wrapf(ErrInvalidDenomForTransfer, "denomination should be prefixed with the format 'ibc/{hash(trace + \"/\" + %s)}'", denom) + + case denomSplit[0] == denom && strings.TrimSpace(denom) != "": + return nil + } + + if _, err := ParseHexHash(denomSplit[1]); err != nil { + return sdkerrors.Wrapf(err, "invalid denom trace hash %s", denomSplit[1]) + } + + return nil +} + +// ParseHexHash parses a hex hash in string format to bytes and validates its correctness. +func ParseHexHash(hexHash string) (tmbytes.HexBytes, error) { + hash, err := hex.DecodeString(hexHash) + if err != nil { + return nil, err + } + + if err := tmtypes.ValidateHash(hash); err != nil { + return nil, err + } + + return hash, nil +} diff --git a/x/ibc/applications/transfer/types/trace_test.go b/x/ibc/applications/transfer/types/trace_test.go new file mode 100644 index 000000000000..f0868d5680e5 --- /dev/null +++ b/x/ibc/applications/transfer/types/trace_test.go @@ -0,0 +1,150 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseDenomTrace(t *testing.T) { + testCases := []struct { + name string + denom string + expTrace DenomTrace + }{ + {"empty denom", "", DenomTrace{}}, + {"base denom", "uatom", DenomTrace{BaseDenom: "uatom"}}, + {"trace info", "transfer/channelToA/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}}, + {"incomplete path", "transfer/uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer"}}, + {"invalid path (1)", "transfer//uatom", DenomTrace{BaseDenom: "uatom", Path: "transfer/"}}, + {"invalid path (2)", "transfer/channelToA/uatom/", DenomTrace{BaseDenom: "", Path: "transfer/channelToA/uatom"}}, + } + + for _, tc := range testCases { + trace := ParseDenomTrace(tc.denom) + require.Equal(t, tc.expTrace, trace, tc.name) + } +} + +func TestDenomTrace_IBCDenom(t *testing.T) { + testCases := []struct { + name string + trace DenomTrace + expDenom string + }{ + {"base denom", DenomTrace{BaseDenom: "uatom"}, "uatom"}, + {"trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}, "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2"}, + } + + for _, tc := range testCases { + denom := tc.trace.IBCDenom() + require.Equal(t, tc.expDenom, denom, tc.name) + } +} + +func TestDenomTrace_Validate(t *testing.T) { + testCases := []struct { + name string + trace DenomTrace + expError bool + }{ + {"base denom only", DenomTrace{BaseDenom: "uatom"}, false}, + {"empty DenomTrace", DenomTrace{}, true}, + {"valid single trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA"}, false}, + {"valid multiple trace info", DenomTrace{BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, false}, + {"single trace identifier", DenomTrace{BaseDenom: "uatom", Path: "transfer"}, true}, + {"invalid port ID", DenomTrace{BaseDenom: "uatom", Path: "(transfer)/channelToA"}, true}, + {"invalid channel ID", DenomTrace{BaseDenom: "uatom", Path: "transfer/(channelToA)"}, true}, + {"empty base denom with trace", DenomTrace{BaseDenom: "", Path: "transfer/channelToA"}, true}, + } + + for _, tc := range testCases { + err := tc.trace.Validate() + if tc.expError { + require.Error(t, err, tc.name) + continue + } + require.NoError(t, err, tc.name) + } +} + +func TestTraces_Validate(t *testing.T) { + testCases := []struct { + name string + traces Traces + expError bool + }{ + {"empty Traces", Traces{}, false}, + {"valid multiple trace info", Traces{{BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}}, false}, + { + "valid multiple trace info", + Traces{ + {BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, + {BaseDenom: "uatom", Path: "transfer/channelToA/transfer/channelToB"}, + }, + true, + }, + {"empty base denom with trace", Traces{{BaseDenom: "", Path: "transfer/channelToA"}}, true}, + } + + for _, tc := range testCases { + err := tc.traces.Validate() + if tc.expError { + require.Error(t, err, tc.name) + continue + } + require.NoError(t, err, tc.name) + } +} + +func TestValidatePrefixedDenom(t *testing.T) { + testCases := []struct { + name string + denom string + expError bool + }{ + {"prefixed denom", "transfer/channelToA/uatom", false}, + {"base denom", "uatom", false}, + {"empty denom", "", true}, + {"empty prefix", "/uatom", true}, + {"empty identifiers", "//uatom", true}, + {"single trace identifier", "transfer/", true}, + {"invalid port ID", "(transfer)/channelToA/uatom", true}, + {"invalid channel ID", "transfer/(channelToA)/uatom", true}, + } + + for _, tc := range testCases { + err := ValidatePrefixedDenom(tc.denom) + if tc.expError { + require.Error(t, err, tc.name) + continue + } + require.NoError(t, err, tc.name) + } +} + +func TestValidateIBCDenom(t *testing.T) { + testCases := []struct { + name string + denom string + expError bool + }{ + {"denom with trace hash", "ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", false}, + {"base denom", "uatom", false}, + {"empty denom", "", true}, + {"invalid prefixed denom", "transfer/channelToA/uatom", true}, + {"denom 'ibc'", "ibc", true}, + {"denom 'ibc/'", "ibc/", true}, + {"invald prefix", "notibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true}, + {"invald hash", "ibc/!@#$!@#", true}, + } + + for _, tc := range testCases { + err := ValidateIBCDenom(tc.denom) + if tc.expError { + require.Error(t, err, tc.name) + continue + } + require.NoError(t, err, tc.name) + } +} diff --git a/x/ibc/applications/transfer/types/transfer.pb.go b/x/ibc/applications/transfer/types/transfer.pb.go new file mode 100644 index 000000000000..62734b85a49f --- /dev/null +++ b/x/ibc/applications/transfer/types/transfer.pb.go @@ -0,0 +1,909 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v1/transfer.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 + +// FungibleTokenPacketData defines a struct for the packet payload +// See FungibleTokenPacketData spec: +// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures +type FungibleTokenPacketData struct { + // the token denomination to be transferred + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + // the token amount to be transferred + Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` + // the sender address + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` + // the recipient address on the destination chain + Receiver string `protobuf:"bytes,4,opt,name=receiver,proto3" json:"receiver,omitempty"` +} + +func (m *FungibleTokenPacketData) Reset() { *m = FungibleTokenPacketData{} } +func (m *FungibleTokenPacketData) String() string { return proto.CompactTextString(m) } +func (*FungibleTokenPacketData) ProtoMessage() {} +func (*FungibleTokenPacketData) Descriptor() ([]byte, []int) { + return fileDescriptor_5041673e96e97901, []int{0} +} +func (m *FungibleTokenPacketData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FungibleTokenPacketData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FungibleTokenPacketData.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 *FungibleTokenPacketData) XXX_Merge(src proto.Message) { + xxx_messageInfo_FungibleTokenPacketData.Merge(m, src) +} +func (m *FungibleTokenPacketData) XXX_Size() int { + return m.Size() +} +func (m *FungibleTokenPacketData) XXX_DiscardUnknown() { + xxx_messageInfo_FungibleTokenPacketData.DiscardUnknown(m) +} + +var xxx_messageInfo_FungibleTokenPacketData proto.InternalMessageInfo + +func (m *FungibleTokenPacketData) GetDenom() string { + if m != nil { + return m.Denom + } + return "" +} + +func (m *FungibleTokenPacketData) GetAmount() uint64 { + if m != nil { + return m.Amount + } + return 0 +} + +func (m *FungibleTokenPacketData) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *FungibleTokenPacketData) GetReceiver() string { + if m != nil { + return m.Receiver + } + return "" +} + +// DenomTrace contains the base denomination for ICS20 fungible tokens and the +// source tracing information path. +type DenomTrace struct { + // path defines the chain of port/channel identifiers used for tracing the + // source of the fungible token. + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + // base denomination of the relayed fungible token. + BaseDenom string `protobuf:"bytes,2,opt,name=base_denom,json=baseDenom,proto3" json:"base_denom,omitempty"` +} + +func (m *DenomTrace) Reset() { *m = DenomTrace{} } +func (m *DenomTrace) String() string { return proto.CompactTextString(m) } +func (*DenomTrace) ProtoMessage() {} +func (*DenomTrace) Descriptor() ([]byte, []int) { + return fileDescriptor_5041673e96e97901, []int{1} +} +func (m *DenomTrace) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DenomTrace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DenomTrace.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 *DenomTrace) XXX_Merge(src proto.Message) { + xxx_messageInfo_DenomTrace.Merge(m, src) +} +func (m *DenomTrace) XXX_Size() int { + return m.Size() +} +func (m *DenomTrace) XXX_DiscardUnknown() { + xxx_messageInfo_DenomTrace.DiscardUnknown(m) +} + +var xxx_messageInfo_DenomTrace proto.InternalMessageInfo + +func (m *DenomTrace) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *DenomTrace) GetBaseDenom() string { + if m != nil { + return m.BaseDenom + } + return "" +} + +// Params defines the set of IBC transfer parameters. +// NOTE: To prevent a single token from being transferred, set the +// TransfersEnabled parameter to true and then set the bank module's SendEnabled +// parameter for the denomination to false. +type Params struct { + // send_enabled enables or disables all cross-chain token transfers from this + // chain. + SendEnabled bool `protobuf:"varint,1,opt,name=send_enabled,json=sendEnabled,proto3" json:"send_enabled,omitempty" yaml:"send_enabled"` + // receive_enabled enables or disables all cross-chain token transfers to this + // chain. + ReceiveEnabled bool `protobuf:"varint,2,opt,name=receive_enabled,json=receiveEnabled,proto3" json:"receive_enabled,omitempty" yaml:"receive_enabled"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_5041673e96e97901, []int{2} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetSendEnabled() bool { + if m != nil { + return m.SendEnabled + } + return false +} + +func (m *Params) GetReceiveEnabled() bool { + if m != nil { + return m.ReceiveEnabled + } + return false +} + +func init() { + proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v1.FungibleTokenPacketData") + proto.RegisterType((*DenomTrace)(nil), "ibc.applications.transfer.v1.DenomTrace") + proto.RegisterType((*Params)(nil), "ibc.applications.transfer.v1.Params") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v1/transfer.proto", fileDescriptor_5041673e96e97901) +} + +var fileDescriptor_5041673e96e97901 = []byte{ + // 362 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0x41, 0x6b, 0xe2, 0x40, + 0x14, 0xc7, 0x8d, 0xeb, 0x8a, 0xce, 0x2e, 0xbb, 0x30, 0x2b, 0x1a, 0x64, 0x1b, 0x25, 0x27, 0xa1, + 0x34, 0x41, 0x7a, 0xf3, 0xd0, 0x82, 0xb5, 0x3d, 0x4b, 0xf0, 0x50, 0x7a, 0x91, 0xc9, 0xe4, 0x35, + 0x06, 0x93, 0x99, 0x30, 0x33, 0x4a, 0xa5, 0x9f, 0xa0, 0xb7, 0x7e, 0xac, 0x1e, 0x3d, 0xf6, 0x24, + 0x45, 0xbf, 0x81, 0x9f, 0xa0, 0x64, 0x12, 0x82, 0x14, 0x7a, 0x9a, 0xf7, 0x7b, 0xef, 0xff, 0xff, + 0xcf, 0x83, 0x87, 0xce, 0x23, 0x9f, 0xba, 0x24, 0x4d, 0xe3, 0x88, 0x12, 0x15, 0x71, 0x26, 0x5d, + 0x25, 0x08, 0x93, 0x8f, 0x20, 0xdc, 0xf5, 0xb0, 0xac, 0x9d, 0x54, 0x70, 0xc5, 0xf1, 0xff, 0xc8, + 0xa7, 0xce, 0xa9, 0xd8, 0x29, 0x05, 0xeb, 0x61, 0xb7, 0x15, 0xf2, 0x90, 0x6b, 0xa1, 0x9b, 0x55, + 0xb9, 0xc7, 0x7e, 0x46, 0x9d, 0xbb, 0x15, 0x0b, 0x23, 0x3f, 0x86, 0x19, 0x5f, 0x02, 0x9b, 0x12, + 0xba, 0x04, 0x35, 0x21, 0x8a, 0xe0, 0x16, 0xfa, 0x19, 0x00, 0xe3, 0x89, 0x69, 0xf4, 0x8d, 0x41, + 0xd3, 0xcb, 0x01, 0xb7, 0x51, 0x9d, 0x24, 0x7c, 0xc5, 0x94, 0x59, 0xed, 0x1b, 0x83, 0x9a, 0x57, + 0x50, 0xd6, 0x97, 0xc0, 0x02, 0x10, 0xe6, 0x0f, 0x2d, 0x2f, 0x08, 0x77, 0x51, 0x43, 0x00, 0x85, + 0x68, 0x0d, 0xc2, 0xac, 0xe9, 0x49, 0xc9, 0xf6, 0x35, 0x42, 0x93, 0x2c, 0x74, 0x26, 0x08, 0x05, + 0x8c, 0x51, 0x2d, 0x25, 0x6a, 0x51, 0x7c, 0xa7, 0x6b, 0x7c, 0x86, 0x90, 0x4f, 0x24, 0xcc, 0xf3, + 0x45, 0xaa, 0x7a, 0xd2, 0xcc, 0x3a, 0xda, 0x67, 0xbf, 0x18, 0xa8, 0x3e, 0x25, 0x82, 0x24, 0x12, + 0x8f, 0xd0, 0xef, 0xec, 0xc7, 0x39, 0x30, 0xe2, 0xc7, 0x10, 0xe8, 0x94, 0xc6, 0xb8, 0x73, 0xdc, + 0xf5, 0xfe, 0x6d, 0x48, 0x12, 0x8f, 0xec, 0xd3, 0xa9, 0xed, 0xfd, 0xca, 0xf0, 0x36, 0x27, 0x7c, + 0x83, 0xfe, 0x16, 0x3b, 0x95, 0xf6, 0xaa, 0xb6, 0x77, 0x8f, 0xbb, 0x5e, 0x3b, 0xb7, 0x7f, 0x11, + 0xd8, 0xde, 0x9f, 0xa2, 0x53, 0x84, 0x8c, 0xef, 0xdf, 0xf6, 0x96, 0xb1, 0xdd, 0x5b, 0xc6, 0xc7, + 0xde, 0x32, 0x5e, 0x0f, 0x56, 0x65, 0x7b, 0xb0, 0x2a, 0xef, 0x07, 0xab, 0xf2, 0x70, 0x15, 0x46, + 0x6a, 0xb1, 0xf2, 0x1d, 0xca, 0x13, 0x97, 0x72, 0x99, 0x70, 0x59, 0x3c, 0x17, 0x32, 0x58, 0xba, + 0x4f, 0xee, 0xf7, 0x37, 0x56, 0x9b, 0x14, 0xa4, 0x5f, 0xd7, 0xa7, 0xba, 0xfc, 0x0c, 0x00, 0x00, + 0xff, 0xff, 0x46, 0x73, 0x85, 0x0b, 0x0d, 0x02, 0x00, 0x00, +} + +func (m *FungibleTokenPacketData) 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 *FungibleTokenPacketData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FungibleTokenPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x22 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x1a + } + if m.Amount != 0 { + i = encodeVarintTransfer(dAtA, i, uint64(m.Amount)) + i-- + dAtA[i] = 0x10 + } + if len(m.Denom) > 0 { + i -= len(m.Denom) + copy(dAtA[i:], m.Denom) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Denom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DenomTrace) 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 *DenomTrace) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DenomTrace) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BaseDenom) > 0 { + i -= len(m.BaseDenom) + copy(dAtA[i:], m.BaseDenom) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.BaseDenom))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReceiveEnabled { + i-- + if m.ReceiveEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.SendEnabled { + i-- + if m.SendEnabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintTransfer(dAtA []byte, offset int, v uint64) int { + offset -= sovTransfer(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *FungibleTokenPacketData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Denom) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + if m.Amount != 0 { + n += 1 + sovTransfer(uint64(m.Amount)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + return n +} + +func (m *DenomTrace) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + l = len(m.BaseDenom) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SendEnabled { + n += 2 + } + if m.ReceiveEnabled { + n += 2 + } + return n +} + +func sovTransfer(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTransfer(x uint64) (n int) { + return sovTransfer(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FungibleTokenPacketData) 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 ErrIntOverflowTransfer + } + 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: FungibleTokenPacketData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FungibleTokenPacketData: 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 ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Denom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + m.Amount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Amount |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTransfer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTransfer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DenomTrace) 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 ErrIntOverflowTransfer + } + 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: DenomTrace: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DenomTrace: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BaseDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTransfer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTransfer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowTransfer + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SendEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SendEnabled = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReceiveEnabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ReceiveEnabled = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTransfer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTransfer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTransfer(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, ErrIntOverflowTransfer + } + 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, ErrIntOverflowTransfer + } + 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, ErrIntOverflowTransfer + } + 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, ErrInvalidLengthTransfer + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTransfer + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTransfer + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTransfer = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTransfer = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTransfer = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/applications/transfer/types/tx.pb.go b/x/ibc/applications/transfer/types/tx.pb.go new file mode 100644 index 000000000000..e3a630b42738 --- /dev/null +++ b/x/ibc/applications/transfer/types/tx.pb.go @@ -0,0 +1,804 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/applications/transfer/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between +// ICS20 enabled chains. See ICS Spec here: +// https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer#data-structures +type MsgTransfer struct { + // the port on which the packet will be sent + SourcePort string `protobuf:"bytes,1,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"` + // the channel by which the packet will be sent + SourceChannel string `protobuf:"bytes,2,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` + // the tokens to be transferred + Token types.Coin `protobuf:"bytes,3,opt,name=token,proto3" json:"token"` + // the sender address + Sender string `protobuf:"bytes,4,opt,name=sender,proto3" json:"sender,omitempty"` + // the recipient address on the destination chain + Receiver string `protobuf:"bytes,5,opt,name=receiver,proto3" json:"receiver,omitempty"` + // Timeout height relative to the current block height. + // The timeout is disabled when set to 0. + TimeoutHeight types1.Height `protobuf:"bytes,6,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height" yaml:"timeout_height"` + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. + // The timeout is disabled when set to 0. + TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty" yaml:"timeout_timestamp"` +} + +func (m *MsgTransfer) Reset() { *m = MsgTransfer{} } +func (m *MsgTransfer) String() string { return proto.CompactTextString(m) } +func (*MsgTransfer) ProtoMessage() {} +func (*MsgTransfer) Descriptor() ([]byte, []int) { + return fileDescriptor_7401ed9bed2f8e09, []int{0} +} +func (m *MsgTransfer) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTransfer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTransfer.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 *MsgTransfer) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTransfer.Merge(m, src) +} +func (m *MsgTransfer) XXX_Size() int { + return m.Size() +} +func (m *MsgTransfer) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTransfer.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTransfer proto.InternalMessageInfo + +// MsgTransferResponse defines the Msg/Transfer response type. +type MsgTransferResponse struct { +} + +func (m *MsgTransferResponse) Reset() { *m = MsgTransferResponse{} } +func (m *MsgTransferResponse) String() string { return proto.CompactTextString(m) } +func (*MsgTransferResponse) ProtoMessage() {} +func (*MsgTransferResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_7401ed9bed2f8e09, []int{1} +} +func (m *MsgTransferResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTransferResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTransferResponse.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 *MsgTransferResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTransferResponse.Merge(m, src) +} +func (m *MsgTransferResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgTransferResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTransferResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTransferResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgTransfer)(nil), "ibc.applications.transfer.v1.MsgTransfer") + proto.RegisterType((*MsgTransferResponse)(nil), "ibc.applications.transfer.v1.MsgTransferResponse") +} + +func init() { + proto.RegisterFile("ibc/applications/transfer/v1/tx.proto", fileDescriptor_7401ed9bed2f8e09) +} + +var fileDescriptor_7401ed9bed2f8e09 = []byte{ + // 488 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0x13, 0xd6, 0x95, 0xe2, 0x6a, 0x13, 0x18, 0x36, 0x65, 0xd5, 0x48, 0xaa, 0x48, 0x48, + 0xe5, 0x80, 0xad, 0x0c, 0x21, 0xa4, 0x1d, 0x10, 0xca, 0x2e, 0x70, 0x98, 0x84, 0xa2, 0x1d, 0x10, + 0x97, 0x91, 0x78, 0x26, 0xb1, 0xd6, 0xd8, 0x91, 0xed, 0x46, 0xdb, 0x37, 0xe0, 0xc8, 0x47, 0xd8, + 0x99, 0x4f, 0xb2, 0xe3, 0x8e, 0x9c, 0x2a, 0xd4, 0x5e, 0x38, 0xf7, 0x13, 0xa0, 0xc4, 0x6e, 0x69, + 0x0f, 0x20, 0x4e, 0xf1, 0x7b, 0xff, 0xdf, 0xf3, 0x5f, 0xcf, 0xef, 0x05, 0x3c, 0x63, 0x19, 0xc1, + 0x69, 0x55, 0x8d, 0x19, 0x49, 0x35, 0x13, 0x5c, 0x61, 0x2d, 0x53, 0xae, 0xbe, 0x50, 0x89, 0xeb, + 0x08, 0xeb, 0x2b, 0x54, 0x49, 0xa1, 0x05, 0x3c, 0x64, 0x19, 0x41, 0xeb, 0x18, 0x5a, 0x62, 0xa8, + 0x8e, 0x06, 0x4f, 0x72, 0x91, 0x8b, 0x16, 0xc4, 0xcd, 0xc9, 0xd4, 0x0c, 0x7c, 0x22, 0x54, 0x29, + 0x14, 0xce, 0x52, 0x45, 0x71, 0x1d, 0x65, 0x54, 0xa7, 0x11, 0x26, 0x82, 0x71, 0xab, 0x07, 0x8d, + 0x35, 0x11, 0x92, 0x62, 0x32, 0x66, 0x94, 0xeb, 0xc6, 0xd0, 0x9c, 0x0c, 0x10, 0x7e, 0xdf, 0x02, + 0xfd, 0x53, 0x95, 0x9f, 0x59, 0x27, 0xf8, 0x1a, 0xf4, 0x95, 0x98, 0x48, 0x42, 0xcf, 0x2b, 0x21, + 0xb5, 0xe7, 0x0e, 0xdd, 0xd1, 0x83, 0x78, 0x7f, 0x31, 0x0d, 0xe0, 0x75, 0x5a, 0x8e, 0x8f, 0xc3, + 0x35, 0x31, 0x4c, 0x80, 0x89, 0x3e, 0x08, 0xa9, 0xe1, 0x5b, 0xb0, 0x6b, 0x35, 0x52, 0xa4, 0x9c, + 0xd3, 0xb1, 0x77, 0xaf, 0xad, 0x3d, 0x58, 0x4c, 0x83, 0xbd, 0x8d, 0x5a, 0xab, 0x87, 0xc9, 0x8e, + 0x49, 0x9c, 0x98, 0x18, 0xbe, 0x02, 0xdb, 0x5a, 0x5c, 0x52, 0xee, 0x6d, 0x0d, 0xdd, 0x51, 0xff, + 0xe8, 0x00, 0x99, 0xde, 0x50, 0xd3, 0x1b, 0xb2, 0xbd, 0xa1, 0x13, 0xc1, 0x78, 0xdc, 0xb9, 0x9d, + 0x06, 0x4e, 0x62, 0x68, 0xb8, 0x0f, 0xba, 0x8a, 0xf2, 0x0b, 0x2a, 0xbd, 0x4e, 0x63, 0x98, 0xd8, + 0x08, 0x0e, 0x40, 0x4f, 0x52, 0x42, 0x59, 0x4d, 0xa5, 0xb7, 0xdd, 0x2a, 0xab, 0x18, 0x7e, 0x06, + 0xbb, 0x9a, 0x95, 0x54, 0x4c, 0xf4, 0x79, 0x41, 0x59, 0x5e, 0x68, 0xaf, 0xdb, 0x7a, 0x0e, 0x50, + 0x33, 0x83, 0xe6, 0xbd, 0x90, 0x7d, 0xa5, 0x3a, 0x42, 0xef, 0x5a, 0x22, 0x7e, 0xda, 0x98, 0xfe, + 0x69, 0x66, 0xb3, 0x3e, 0x4c, 0x76, 0x6c, 0xc2, 0xd0, 0xf0, 0x3d, 0x78, 0xb4, 0x24, 0x9a, 0xaf, + 0xd2, 0x69, 0x59, 0x79, 0xf7, 0x87, 0xee, 0xa8, 0x13, 0x1f, 0x2e, 0xa6, 0x81, 0xb7, 0x79, 0xc9, + 0x0a, 0x09, 0x93, 0x87, 0x36, 0x77, 0xb6, 0x4c, 0x1d, 0xf7, 0xbe, 0xde, 0x04, 0xce, 0xaf, 0x9b, + 0xc0, 0x09, 0xf7, 0xc0, 0xe3, 0xb5, 0x59, 0x25, 0x54, 0x55, 0x82, 0x2b, 0x7a, 0x24, 0xc0, 0xd6, + 0xa9, 0xca, 0x61, 0x01, 0x7a, 0xab, 0x31, 0x3e, 0x47, 0xff, 0x5a, 0x26, 0xb4, 0x76, 0xcb, 0x20, + 0xfa, 0x6f, 0x74, 0x69, 0x18, 0x7f, 0xbc, 0x9d, 0xf9, 0xee, 0xdd, 0xcc, 0x77, 0x7f, 0xce, 0x7c, + 0xf7, 0xdb, 0xdc, 0x77, 0xee, 0xe6, 0xbe, 0xf3, 0x63, 0xee, 0x3b, 0x9f, 0xde, 0xe4, 0x4c, 0x17, + 0x93, 0x0c, 0x11, 0x51, 0x62, 0xbb, 0x9a, 0xe6, 0xf3, 0x42, 0x5d, 0x5c, 0xe2, 0x2b, 0xfc, 0xf7, + 0x3f, 0x41, 0x5f, 0x57, 0x54, 0x65, 0xdd, 0x76, 0x2b, 0x5f, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, + 0x26, 0x76, 0x5b, 0xfa, 0x33, 0x03, 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 { + // Transfer defines a rpc handler method for MsgTransfer. + Transfer(ctx context.Context, in *MsgTransfer, opts ...grpc.CallOption) (*MsgTransferResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) Transfer(ctx context.Context, in *MsgTransfer, opts ...grpc.CallOption) (*MsgTransferResponse, error) { + out := new(MsgTransferResponse) + err := c.cc.Invoke(ctx, "/ibc.applications.transfer.v1.Msg/Transfer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // Transfer defines a rpc handler method for MsgTransfer. + Transfer(context.Context, *MsgTransfer) (*MsgTransferResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) Transfer(ctx context.Context, req *MsgTransfer) (*MsgTransferResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Transfer not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_Transfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgTransfer) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Transfer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.applications.transfer.v1.Msg/Transfer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Transfer(ctx, req.(*MsgTransfer)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.applications.transfer.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Transfer", + Handler: _Msg_Transfer_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/applications/transfer/v1/tx.proto", +} + +func (m *MsgTransfer) 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 *MsgTransfer) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TimeoutTimestamp != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.TimeoutTimestamp)) + i-- + dAtA[i] = 0x38 + } + { + size, err := m.TimeoutHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if len(m.Receiver) > 0 { + i -= len(m.Receiver) + copy(dAtA[i:], m.Receiver) + i = encodeVarintTx(dAtA, i, uint64(len(m.Receiver))) + i-- + dAtA[i] = 0x2a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.Token.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.SourceChannel) > 0 { + i -= len(m.SourceChannel) + copy(dAtA[i:], m.SourceChannel) + i = encodeVarintTx(dAtA, i, uint64(len(m.SourceChannel))) + i-- + dAtA[i] = 0x12 + } + if len(m.SourcePort) > 0 { + i -= len(m.SourcePort) + copy(dAtA[i:], m.SourcePort) + i = encodeVarintTx(dAtA, i, uint64(len(m.SourcePort))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgTransferResponse) 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 *MsgTransferResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTransferResponse) 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 *MsgTransfer) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SourcePort) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.SourceChannel) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Token.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Receiver) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.TimeoutHeight.Size() + n += 1 + l + sovTx(uint64(l)) + if m.TimeoutTimestamp != 0 { + n += 1 + sovTx(uint64(m.TimeoutTimestamp)) + } + return n +} + +func (m *MsgTransferResponse) 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 *MsgTransfer) 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: MsgTransfer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTransfer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePort", 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.SourcePort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannel", 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.SourceChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", 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 := m.Token.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", 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.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Receiver", 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.Receiver = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutHeight", 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 := m.TimeoutHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutTimestamp", wireType) + } + m.TimeoutTimestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeoutTimestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *MsgTransferResponse) 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: MsgTransferResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTransferResponse: 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/ibc/core/02-client/abci.go b/x/ibc/core/02-client/abci.go new file mode 100644 index 000000000000..3c56d90ad37a --- /dev/null +++ b/x/ibc/core/02-client/abci.go @@ -0,0 +1,20 @@ +package client + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// BeginBlocker updates an existing localhost client with the latest block height. +func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { + _, found := k.GetClientState(ctx, exported.Localhost) + if !found { + return + } + + // update the localhost client with the latest block height + if err := k.UpdateClient(ctx, exported.Localhost, nil); err != nil { + panic(err) + } +} diff --git a/x/ibc/core/02-client/abci_test.go b/x/ibc/core/02-client/abci_test.go new file mode 100644 index 000000000000..3a296618b313 --- /dev/null +++ b/x/ibc/core/02-client/abci_test.go @@ -0,0 +1,60 @@ +package client_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + client "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type ClientTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *ClientTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + + // set localhost client + revision := types.ParseChainID(suite.chainA.GetContext().ChainID()) + localHostClient := localhosttypes.NewClientState( + suite.chainA.GetContext().ChainID(), types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), + ) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) +} + +func TestClientTestSuite(t *testing.T) { + suite.Run(t, new(ClientTestSuite)) +} + +func (suite *ClientTestSuite) TestBeginBlocker() { + prevHeight := types.GetSelfHeight(suite.chainA.GetContext()) + + localHostClient := suite.chainA.GetClientState(exported.Localhost) + suite.Require().Equal(prevHeight, localHostClient.GetLatestHeight()) + + for i := 0; i < 10; i++ { + // increment height + suite.coordinator.CommitBlock(suite.chainA, suite.chainB) + + suite.Require().NotPanics(func() { + client.BeginBlocker(suite.chainA.GetContext(), suite.chainA.App.IBCKeeper.ClientKeeper) + }, "BeginBlocker shouldn't panic") + + localHostClient = suite.chainA.GetClientState(exported.Localhost) + suite.Require().Equal(prevHeight.Increment(), localHostClient.GetLatestHeight()) + prevHeight = localHostClient.GetLatestHeight().(types.Height) + } +} diff --git a/x/ibc/core/02-client/client/cli/cli.go b/x/ibc/core/02-client/client/cli/cli.go new file mode 100644 index 000000000000..375d8f63693c --- /dev/null +++ b/x/ibc/core/02-client/client/cli/cli.go @@ -0,0 +1,31 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// GetQueryCmd returns the query commands for IBC clients +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC client query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + queryCmd.AddCommand( + GetCmdQueryClientStates(), + GetCmdQueryClientState(), + GetCmdQueryConsensusStates(), + GetCmdQueryConsensusState(), + GetCmdQueryHeader(), + GetCmdNodeConsensusState(), + GetCmdParams(), + ) + + return queryCmd +} diff --git a/x/ibc/core/02-client/client/cli/query.go b/x/ibc/core/02-client/client/cli/query.go new file mode 100644 index 000000000000..a1e9b45d22cf --- /dev/null +++ b/x/ibc/core/02-client/client/cli/query.go @@ -0,0 +1,263 @@ +package cli + +import ( + "context" + "errors" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + flagLatestHeight = "latest-height" +) + +// GetCmdQueryClientStates defines the command to query all the light clients +// that this chain mantains. +func GetCmdQueryClientStates() *cobra.Command { + cmd := &cobra.Command{ + Use: "states", + Short: "Query all available light clients", + Long: "Query all available light clients", + Example: fmt.Sprintf("%s query %s %s states", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryClientStatesRequest{ + Pagination: pageReq, + } + + res, err := queryClient.ClientStates(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "client states") + + return cmd +} + +// GetCmdQueryClientState defines the command to query the state of a client with +// a given id as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query +func GetCmdQueryClientState() *cobra.Command { + cmd := &cobra.Command{ + Use: "state [client-id]", + Short: "Query a client state", + Long: "Query stored client state", + Example: fmt.Sprintf("%s query %s %s state [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + clientID := args[0] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + clientStateRes, err := utils.QueryClientState(clientCtx, clientID, prove) + if err != nil { + return err + } + + return clientCtx.PrintProto(clientStateRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryConsensusStates defines the command to query all the consensus states from a given +// client state. +func GetCmdQueryConsensusStates() *cobra.Command { + cmd := &cobra.Command{ + Use: "consensus-states [client-id]", + Short: "Query all the consensus states of a client.", + Long: "Query all the consensus states from a given client state.", + Example: fmt.Sprintf("%s query %s %s consensus-states [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + clientID := args[0] + + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryConsensusStatesRequest{ + ClientId: clientID, + Pagination: pageReq, + } + + res, err := queryClient.ConsensusStates(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "consensus states") + + return cmd +} + +// GetCmdQueryConsensusState defines the command to query the consensus state of +// the chain as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query +func GetCmdQueryConsensusState() *cobra.Command { + cmd := &cobra.Command{ + Use: "consensus-state [client-id] [height]", + Short: "Query the consensus state of a client at a given height", + Long: `Query the consensus state for a particular light client at a given height. +If the '--latest' flag is included, the query returns the latest consensus state, overriding the height argument.`, + Example: fmt.Sprintf("%s query %s %s consensus-state [client-id] [height]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + clientID := args[0] + queryLatestHeight, _ := cmd.Flags().GetBool(flagLatestHeight) + var height types.Height + + if !queryLatestHeight { + if len(args) != 2 { + return errors.New("must include a second 'height' argument when '--latest-height' flag is not provided") + } + + height, err = types.ParseHeight(args[1]) + if err != nil { + return err + } + } + + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + csRes, err := utils.QueryConsensusState(clientCtx, clientID, height, prove, queryLatestHeight) + if err != nil { + return err + } + + return clientCtx.PrintProto(csRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + cmd.Flags().Bool(flagLatestHeight, false, "return latest stored consensus state") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryHeader defines the command to query the latest header on the chain +func GetCmdQueryHeader() *cobra.Command { + cmd := &cobra.Command{ + Use: "header", + Short: "Query the latest header of the running chain", + Long: "Query the latest Tendermint header of the running chain", + Example: fmt.Sprintf("%s query %s %s header", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + header, height, err := utils.QueryTendermintHeader(clientCtx) + if err != nil { + return err + } + + clientCtx = clientCtx.WithHeight(height) + return clientCtx.PrintProto(&header) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdNodeConsensusState defines the command to query the latest consensus state of a node +// The result is feed to client creation +func GetCmdNodeConsensusState() *cobra.Command { + cmd := &cobra.Command{ + Use: "node-state", + Short: "Query a node consensus state", + Long: "Query a node consensus state. This result is feed to the client creation transaction.", + Example: fmt.Sprintf("%s query %s %s node-state", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + state, height, err := utils.QueryNodeConsensusState(clientCtx) + if err != nil { + return err + } + + clientCtx = clientCtx.WithHeight(height) + return clientCtx.PrintProto(state) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdParams returns the command handler for ibc client parameter querying. +func GetCmdParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current ibc client parameters", + Long: "Query the current ibc client parameters", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s query %s %s params", version.AppName, host.ModuleName, types.SubModuleName), + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + res, _ := queryClient.ClientParams(context.Background(), &types.QueryClientParamsRequest{}) + return clientCtx.PrintProto(res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/core/02-client/client/utils/utils.go b/x/ibc/core/02-client/client/utils/utils.go new file mode 100644 index 000000000000..1a7bc003bc51 --- /dev/null +++ b/x/ibc/core/02-client/client/utils/utils.go @@ -0,0 +1,199 @@ +package utils + +import ( + "context" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/cosmos/cosmos-sdk/codec" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +// QueryClientState returns a client state. If prove is true, it performs an ABCI store query +// in order to retrieve the merkle proof. Otherwise, it uses the gRPC query client. +func QueryClientState( + clientCtx client.Context, clientID string, prove bool, +) (*types.QueryClientStateResponse, error) { + if prove { + return QueryClientStateABCI(clientCtx, clientID) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryClientStateRequest{ + ClientId: clientID, + } + + return queryClient.ClientState(context.Background(), req) +} + +// QueryClientStateABCI queries the store to get the light client state and a merkle proof. +func QueryClientStateABCI( + clientCtx client.Context, clientID string, +) (*types.QueryClientStateResponse, error) { + key := host.FullClientStateKey(clientID) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if client exists + if len(value) == 0 { + return nil, sdkerrors.Wrap(types.ErrClientNotFound, clientID) + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + clientState, err := types.UnmarshalClientState(cdc, value) + if err != nil { + return nil, err + } + + anyClientState, err := types.PackClientState(clientState) + if err != nil { + return nil, err + } + + clientStateRes := types.NewQueryClientStateResponse(anyClientState, proofBz, proofHeight) + return clientStateRes, nil +} + +// QueryConsensusState returns a consensus state. If prove is true, it performs an ABCI store +// query in order to retrieve the merkle proof. Otherwise, it uses the gRPC query client. +func QueryConsensusState( + clientCtx client.Context, clientID string, height exported.Height, prove, latestHeight bool, +) (*types.QueryConsensusStateResponse, error) { + if prove { + return QueryConsensusStateABCI(clientCtx, clientID, height) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryConsensusStateRequest{ + ClientId: clientID, + RevisionNumber: height.GetRevisionNumber(), + RevisionHeight: height.GetRevisionHeight(), + LatestHeight: latestHeight, + } + + return queryClient.ConsensusState(context.Background(), req) +} + +// QueryConsensusStateABCI queries the store to get the consensus state of a light client and a +// merkle proof of its existence or non-existence. +func QueryConsensusStateABCI( + clientCtx client.Context, clientID string, height exported.Height, +) (*types.QueryConsensusStateResponse, error) { + key := host.FullConsensusStateKey(clientID, height) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if consensus state exists + if len(value) == 0 { + return nil, sdkerrors.Wrap(types.ErrConsensusStateNotFound, clientID) + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + cs, err := types.UnmarshalConsensusState(cdc, value) + if err != nil { + return nil, err + } + + anyConsensusState, err := types.PackConsensusState(cs) + if err != nil { + return nil, err + } + + return types.NewQueryConsensusStateResponse(anyConsensusState, proofBz, proofHeight), nil +} + +// QueryTendermintHeader takes a client context and returns the appropriate +// tendermint header +func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, error) { + node, err := clientCtx.GetNode() + if err != nil { + return ibctmtypes.Header{}, 0, err + } + + info, err := node.ABCIInfo(context.Background()) + if err != nil { + return ibctmtypes.Header{}, 0, err + } + + height := info.Response.LastBlockHeight + + commit, err := node.Commit(context.Background(), &height) + if err != nil { + return ibctmtypes.Header{}, 0, err + } + + page := 0 + count := 10_000 + + validators, err := node.Validators(context.Background(), &height, &page, &count) + if err != nil { + return ibctmtypes.Header{}, 0, err + } + + protoCommit := commit.SignedHeader.ToProto() + protoValset, err := tmtypes.NewValidatorSet(validators.Validators).ToProto() + if err != nil { + return ibctmtypes.Header{}, 0, err + } + + header := ibctmtypes.Header{ + SignedHeader: protoCommit, + ValidatorSet: protoValset, + } + + return header, height, nil +} + +// QueryNodeConsensusState takes a client context and returns the appropriate +// tendermint consensus state +func QueryNodeConsensusState(clientCtx client.Context) (*ibctmtypes.ConsensusState, int64, error) { + node, err := clientCtx.GetNode() + if err != nil { + return &ibctmtypes.ConsensusState{}, 0, err + } + + info, err := node.ABCIInfo(context.Background()) + if err != nil { + return &ibctmtypes.ConsensusState{}, 0, err + } + + height := info.Response.LastBlockHeight + + commit, err := node.Commit(context.Background(), &height) + if err != nil { + return &ibctmtypes.ConsensusState{}, 0, err + } + + page := 1 + count := 10_000 + + nextHeight := height + 1 + nextVals, err := node.Validators(context.Background(), &nextHeight, &page, &count) + if err != nil { + return &ibctmtypes.ConsensusState{}, 0, err + } + + state := &ibctmtypes.ConsensusState{ + Timestamp: commit.Time, + Root: commitmenttypes.NewMerkleRoot(commit.AppHash), + NextValidatorsHash: tmtypes.NewValidatorSet(nextVals.Validators).Hash(), + } + + return state, height, nil +} diff --git a/x/ibc/core/02-client/doc.go b/x/ibc/core/02-client/doc.go new file mode 100644 index 000000000000..cfe3c76c6af7 --- /dev/null +++ b/x/ibc/core/02-client/doc.go @@ -0,0 +1,10 @@ +/* +Package client implements the ICS 02 - Client Semantics specification +https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics. This +concrete implementations defines types and method to store and update light +clients which tracks on other chain's state. + +The main type is `Client`, which provides `commitment.Root` to verify state proofs and `ConsensusState` to +verify header proofs. +*/ +package client diff --git a/x/ibc/core/02-client/genesis.go b/x/ibc/core/02-client/genesis.go new file mode 100644 index 000000000000..26635f0784c8 --- /dev/null +++ b/x/ibc/core/02-client/genesis.go @@ -0,0 +1,69 @@ +package client + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// InitGenesis initializes the ibc client submodule's state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) { + k.SetParams(ctx, gs.Params) + + // Set all client metadata first. This will allow client keeper to overwrite client and consensus state keys + // if clients accidentally write to ClientKeeper reserved keys. + if len(gs.ClientsMetadata) != 0 { + k.SetAllClientMetadata(ctx, gs.ClientsMetadata) + } + + for _, client := range gs.Clients { + cs, ok := client.ClientState.GetCachedValue().(exported.ClientState) + if !ok { + panic("invalid client state") + } + + if !gs.Params.IsAllowedClient(cs.ClientType()) { + panic(fmt.Sprintf("client state type %s is not registered on the allowlist", cs.ClientType())) + } + + k.SetClientState(ctx, client.ClientId, cs) + } + + for _, cs := range gs.ClientsConsensus { + for _, consState := range cs.ConsensusStates { + consensusState, ok := consState.ConsensusState.GetCachedValue().(exported.ConsensusState) + if !ok { + panic(fmt.Sprintf("invalid consensus state with client ID %s at height %s", cs.ClientId, consState.Height)) + } + + k.SetClientConsensusState(ctx, cs.ClientId, consState.Height, consensusState) + } + } + + k.SetNextClientSequence(ctx, gs.NextClientSequence) + + // NOTE: localhost creation is specifically disallowed for the time being. + // Issue: https://github.com/cosmos/cosmos-sdk/issues/7871 +} + +// ExportGenesis returns the ibc client submodule's exported genesis. +// NOTE: CreateLocalhost should always be false on export since a +// created localhost will be included in the exported clients. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { + genClients := k.GetAllGenesisClients(ctx) + clientsMetadata, err := k.GetAllClientMetadata(ctx, genClients) + if err != nil { + panic(err) + } + return types.GenesisState{ + Clients: genClients, + ClientsMetadata: clientsMetadata, + ClientsConsensus: k.GetAllConsensusStates(ctx), + Params: k.GetParams(ctx), + CreateLocalhost: false, + } +} diff --git a/x/ibc/core/02-client/keeper/client.go b/x/ibc/core/02-client/keeper/client.go new file mode 100644 index 000000000000..e822402bfb3e --- /dev/null +++ b/x/ibc/core/02-client/keeper/client.go @@ -0,0 +1,205 @@ +package keeper + +import ( + "encoding/hex" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CreateClient creates a new client state and populates it with a given consensus +// state as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#create +func (k Keeper) CreateClient( + ctx sdk.Context, clientState exported.ClientState, consensusState exported.ConsensusState, +) (string, error) { + params := k.GetParams(ctx) + if !params.IsAllowedClient(clientState.ClientType()) { + return "", sdkerrors.Wrapf( + types.ErrInvalidClientType, + "client state type %s is not registered in the allowlist", clientState.ClientType(), + ) + } + + clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType()) + + k.SetClientState(ctx, clientID, clientState) + k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) + + // verifies initial consensus state against client state and initializes client store with any client-specific metadata + // e.g. set ProcessedTime in Tendermint clients + if err := clientState.Initialize(ctx, k.cdc, k.ClientStore(ctx, clientID), consensusState); err != nil { + return "", err + } + + // check if consensus state is nil in case the created client is Localhost + if consensusState != nil { + k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) + } + + k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "create"}, + 1, + []metrics.Label{telemetry.NewLabel("client-type", clientState.ClientType())}, + ) + }() + + return clientID, nil +} + +// UpdateClient updates the consensus state and the state root from a provided header. +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { + clientState, found := k.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) + } + + // prevent update if the client is frozen before or at header height + if clientState.IsFrozen() && clientState.GetFrozenHeight().LTE(header.GetHeight()) { + return sdkerrors.Wrapf(types.ErrClientFrozen, "cannot update client with ID %s", clientID) + } + + clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, clientID), header) + if err != nil { + return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID) + } + + k.SetClientState(ctx, clientID, clientState) + + var consensusHeight exported.Height + + // we don't set consensus state for localhost client + if header != nil && clientID != exported.Localhost { + k.SetClientConsensusState(ctx, clientID, header.GetHeight(), consensusState) + consensusHeight = header.GetHeight() + } else { + consensusHeight = types.GetSelfHeight(ctx) + } + + k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel("client-type", clientState.ClientType()), + telemetry.NewLabel("client-id", clientID), + telemetry.NewLabel("update-type", "msg"), + }, + ) + }() + + // emit the full header in events + var headerStr string + if header != nil { + // Marshal the Header as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + headerStr = hex.EncodeToString(types.MustMarshalHeader(k.cdc, header)) + + } + + // emitting events in the keeper emits for both begin block and handler client updates + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUpdateClient, + sdk.NewAttribute(types.AttributeKeyClientID, clientID), + sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), + sdk.NewAttribute(types.AttributeKeyConsensusHeight, consensusHeight.String()), + sdk.NewAttribute(types.AttributeKeyHeader, headerStr), + ), + ) + + return nil +} + +// UpgradeClient upgrades the client to a new client state if this new client was committed to +// by the old client at the specified upgrade height +func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState, + proofUpgradeClient, proofUpgradeConsState []byte) error { + clientState, found := k.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) + } + + // prevent upgrade if current client is frozen + if clientState.IsFrozen() { + return sdkerrors.Wrapf(types.ErrClientFrozen, "cannot update client with ID %s", clientID) + } + + updatedClientState, updatedConsState, err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, clientID), + upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState) + if err != nil { + return sdkerrors.Wrapf(err, "cannot upgrade client with ID %s", clientID) + } + + k.SetClientState(ctx, clientID, updatedClientState) + k.SetClientConsensusState(ctx, clientID, updatedClientState.GetLatestHeight(), updatedConsState) + + k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", updatedClientState.GetLatestHeight().String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "upgrade"}, + 1, + []metrics.Label{ + telemetry.NewLabel("client-type", updatedClientState.ClientType()), + telemetry.NewLabel("client-id", clientID), + }, + ) + }() + + // emitting events in the keeper emits for client upgrades + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUpgradeClient, + sdk.NewAttribute(types.AttributeKeyClientID, clientID), + sdk.NewAttribute(types.AttributeKeyClientType, updatedClientState.ClientType()), + sdk.NewAttribute(types.AttributeKeyConsensusHeight, updatedClientState.GetLatestHeight().String()), + ), + ) + + return nil +} + +// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the +// client if so. +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, misbehaviour exported.Misbehaviour) error { + clientState, found := k.GetClientState(ctx, misbehaviour.GetClientID()) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID()) + } + + if clientState.IsFrozen() && clientState.GetFrozenHeight().LTE(misbehaviour.GetHeight()) { + return sdkerrors.Wrapf(types.ErrInvalidMisbehaviour, "client is already frozen at height ≤ misbehaviour height (%s ≤ %s)", clientState.GetFrozenHeight(), misbehaviour.GetHeight()) + } + + clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, misbehaviour.GetClientID()), misbehaviour) + if err != nil { + return err + } + + k.SetClientState(ctx, misbehaviour.GetClientID(), clientState) + k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", misbehaviour.GetClientID(), "height", misbehaviour.GetHeight().String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "misbehaviour"}, + 1, + []metrics.Label{ + telemetry.NewLabel("client-type", misbehaviour.ClientType()), + telemetry.NewLabel("client-id", misbehaviour.GetClientID()), + }, + ) + }() + + return nil +} diff --git a/x/ibc/core/02-client/keeper/client_test.go b/x/ibc/core/02-client/keeper/client_test.go new file mode 100644 index 000000000000..21d59b74025c --- /dev/null +++ b/x/ibc/core/02-client/keeper/client_test.go @@ -0,0 +1,640 @@ +package keeper_test + +import ( + "encoding/hex" + "fmt" + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + 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/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func (suite *KeeperTestSuite) TestCreateClient() { + cases := []struct { + msg string + clientState exported.ClientState + expPass bool + }{ + {"success", ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, + {"client type not supported", localhosttypes.NewClientState(testChainID, clienttypes.NewHeight(0, 1)), false}, + } + + for i, tc := range cases { + + clientID, err := suite.keeper.CreateClient(suite.ctx, tc.clientState, suite.consensusState) + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + suite.Require().NotNil(clientID, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + suite.Require().Equal("", clientID, "invalid test case %d passed: %s", i, tc.msg) + } + } +} + +func (suite *KeeperTestSuite) TestUpdateClientTendermint() { + // Must create header creation functions since suite.header gets recreated on each test case + createFutureUpdateFn := func(s *KeeperTestSuite) *ibctmtypes.Header { + heightPlus3 := clienttypes.NewHeight(suite.header.GetHeight().GetRevisionNumber(), suite.header.GetHeight().GetRevisionHeight()+3) + height := suite.header.GetHeight().(clienttypes.Height) + + return suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus3.RevisionHeight), height, suite.header.Header.Time.Add(time.Hour), + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + } + createPastUpdateFn := func(s *KeeperTestSuite) *ibctmtypes.Header { + heightMinus2 := clienttypes.NewHeight(suite.header.GetHeight().GetRevisionNumber(), suite.header.GetHeight().GetRevisionHeight()-2) + heightMinus4 := clienttypes.NewHeight(suite.header.GetHeight().GetRevisionNumber(), suite.header.GetHeight().GetRevisionHeight()-4) + + return suite.chainA.CreateTMClientHeader(testChainID, int64(heightMinus2.RevisionHeight), heightMinus4, suite.header.Header.Time, + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + } + var ( + updateHeader *ibctmtypes.Header + clientState *ibctmtypes.ClientState + clientID string + err error + ) + + cases := []struct { + name string + malleate func() error + expPass bool + }{ + {"valid update", func() error { + clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height + incrementedClientHeight := testClientHeight.Increment().(types.Height) + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, incrementedClientHeight, intermediateConsState) + + clientState.LatestHeight = incrementedClientHeight + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + updateHeader = createFutureUpdateFn(suite) + return err + }, true}, + {"valid past update", func() error { + clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + suite.Require().NoError(err) + + height1 := types.NewHeight(0, 1) + + // store previous consensus state + prevConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.past, + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, height1, prevConsState) + + height2 := types.NewHeight(0, 2) + + // store intermediate consensus state to check that trustedHeight does not need to be hightest consensus state before header height + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.past.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, height2, intermediateConsState) + + // updateHeader will fill in consensus state between prevConsState and suite.consState + // clientState should not be updated + updateHeader = createPastUpdateFn(suite) + return nil + }, true}, + {"client state not found", func() error { + updateHeader = createFutureUpdateFn(suite) + + return nil + }, false}, + {"consensus state not found", func() error { + clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + updateHeader = createFutureUpdateFn(suite) + + return nil + }, false}, + {"frozen client before update", func() error { + clientState = &ibctmtypes.ClientState{FrozenHeight: types.NewHeight(0, 1), LatestHeight: testClientHeight} + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + updateHeader = createFutureUpdateFn(suite) + + return nil + }, false}, + {"valid past update before client was frozen", func() error { + clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientState.FrozenHeight = types.NewHeight(0, testClientHeight.RevisionHeight-1) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + suite.Require().NoError(err) + + height1 := types.NewHeight(0, 1) + + // store previous consensus state + prevConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.past, + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, height1, prevConsState) + + // updateHeader will fill in consensus state between prevConsState and suite.consState + // clientState should not be updated + updateHeader = createPastUpdateFn(suite) + return nil + }, true}, + {"invalid header", func() error { + clientState = ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + _, err := suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + suite.Require().NoError(err) + updateHeader = createPastUpdateFn(suite) + + return nil + }, false}, + } + + for i, tc := range cases { + tc := tc + i := i + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + clientID = testClientID // must be explicitly changed + + err := tc.malleate() + suite.Require().NoError(err) + + suite.ctx = suite.ctx.WithBlockTime(updateHeader.Header.Time.Add(time.Minute)) + + err = suite.keeper.UpdateClient(suite.ctx, clientID, updateHeader) + + if tc.expPass { + suite.Require().NoError(err, err) + + expConsensusState := &ibctmtypes.ConsensusState{ + Timestamp: updateHeader.GetTime(), + Root: commitmenttypes.NewMerkleRoot(updateHeader.Header.GetAppHash()), + NextValidatorsHash: updateHeader.Header.NextValidatorsHash, + } + + newClientState, found := suite.keeper.GetClientState(suite.ctx, clientID) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + + consensusState, found := suite.keeper.GetClientConsensusState(suite.ctx, clientID, updateHeader.GetHeight()) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + + // Determine if clientState should be updated or not + if updateHeader.GetHeight().GT(clientState.GetLatestHeight()) { + // Header Height is greater than clientState latest Height, clientState should be updated with header.GetHeight() + suite.Require().Equal(updateHeader.GetHeight(), newClientState.GetLatestHeight(), "clientstate height did not update") + } else { + // Update will add past consensus state, clientState should not be updated at all + suite.Require().Equal(clientState.GetLatestHeight(), newClientState.GetLatestHeight(), "client state height updated for past header") + } + + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expConsensusState, consensusState, "consensus state should have been updated on case %s", tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + }) + } +} + +func (suite *KeeperTestSuite) TestUpdateClientLocalhost() { + revision := types.ParseChainID(suite.chainA.ChainID) + var localhostClient exported.ClientState = localhosttypes.NewClientState(suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight()))) + + ctx := suite.chainA.GetContext().WithBlockHeight(suite.chainA.GetContext().BlockHeight() + 1) + err := suite.chainA.App.IBCKeeper.ClientKeeper.UpdateClient(ctx, exported.Localhost, nil) + suite.Require().NoError(err) + + clientState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(ctx, exported.Localhost) + suite.Require().True(found) + suite.Require().Equal(localhostClient.GetLatestHeight().(types.Height).Increment(), clientState.GetLatestHeight()) +} + +func (suite *KeeperTestSuite) TestUpgradeClient() { + var ( + upgradedClient exported.ClientState + upgradedConsState exported.ConsensusState + lastHeight exported.Height + clientA string + proofUpgradedClient, proofUpgradedConsState []byte + ) + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + name: "successful upgrade", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: true, + }, + { + name: "client state not found", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + clientA = "wrongclientid" + }, + expPass: false, + }, + { + name: "client state frozen", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + // set frozen client in store + tmClient, ok := cs.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.FrozenHeight = types.NewHeight(0, 1) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClient) + }, + expPass: false, + }, + { + name: "tendermint client VerifyUpgrade fails", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // change upgradedClient client-specified parameters + upgradedClient = ibctmtypes.NewClientState("wrongchainID", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, true, true) + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + clientA, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + tc.setup() + + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + upgradedClient = upgradedClient.ZeroCustomFields() + + err := suite.chainA.App.IBCKeeper.ClientKeeper.UpgradeClient(suite.chainA.GetContext(), clientA, upgradedClient, upgradedConsState, proofUpgradedClient, proofUpgradedConsState) + + if tc.expPass { + suite.Require().NoError(err, "verify upgrade failed on valid case: %s", tc.name) + } else { + suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) + } + } + +} + +func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { + var ( + clientID string + err error + ) + + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + altVal := tmtypes.NewValidator(altPubKey, 4) + + // Set valSet here with suite.valSet so it doesn't get reset on each testcase + valSet := suite.valSet + valsHash := valSet.Hash() + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + bothValsHash := bothValSet.Hash() + // Create alternative validator set with only altVal + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + + // Create signer array and ensure it is in same order as bothValSet + _, suiteVal := suite.valSet.GetByIndex(0) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + + altSigners := []tmtypes.PrivValidator{altPrivVal} + + // Create valid Misbehaviour by making a duplicate header that signs over different block time + altTime := suite.ctx.BlockTime().Add(time.Minute) + + heightPlus3 := types.NewHeight(0, height+3) + heightPlus5 := types.NewHeight(0, height+5) + + testCases := []struct { + name string + misbehaviour *ibctmtypes.Misbehaviour + malleate func() error + expPass bool + }{ + { + "trusting period misbehavior should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + true, + }, + { + "misbehavior at later height should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: suite.valSetHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) + + clientState.LatestHeight = heightPlus3 + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + true, + }, + { + "misbehavior at later height with different trusted heights should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + // store trusted consensus state for Header2 + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: bothValsHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) + + clientState.LatestHeight = heightPlus3 + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + true, + }, + { + "trusted ConsensusState1 not found", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), heightPlus3, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + // intermediate consensus state at height + 3 is not created + return err + }, + false, + }, + { + "trusted ConsensusState2 not found", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + // intermediate consensus state at height + 3 is not created + return err + }, + false, + }, + { + "client state not found", + &ibctmtypes.Misbehaviour{}, + func() error { return nil }, + false, + }, + { + "client already frozen at earlier height", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + clientState.FrozenHeight = types.NewHeight(0, 1) + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + false, + }, + { + "misbehaviour check failed", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), + ClientId: clientID, + }, + func() error { + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + if err != nil { + return err + } + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + false, + }, + } + + for i, tc := range testCases { + tc := tc + i := i + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + clientID = testClientID // must be explicitly changed + + err := tc.malleate() + suite.Require().NoError(err) + + tc.misbehaviour.ClientId = clientID + + err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, tc.misbehaviour) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + + clientState, found := suite.keeper.GetClientState(suite.ctx, clientID) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + suite.Require().True(clientState.IsFrozen(), "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(tc.misbehaviour.GetHeight(), clientState.GetFrozenHeight(), + "valid test case %d failed: %s. Expected FrozenHeight %s got %s", tc.misbehaviour.GetHeight(), clientState.GetFrozenHeight()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + }) + } +} + +func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { + clientID, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientID) + suite.Require().NoError(err) + + msg, err := clienttypes.NewMsgUpdateClient( + clientID, header, + suite.chainA.SenderAccount.GetAddress(), + ) + + result, err := suite.chainA.SendMsgs(msg) + suite.Require().NoError(err) + // first event type is "message" + updateEvent := result.Events[1] + + suite.Require().Equal(clienttypes.EventTypeUpdateClient, updateEvent.Type) + + // use a boolean to ensure the update event contains the header + contains := false + for _, attr := range updateEvent.Attributes { + if string(attr.Key) == clienttypes.AttributeKeyHeader { + contains = true + + bz, err := hex.DecodeString(string(attr.Value)) + suite.Require().NoError(err) + + emittedHeader, err := types.UnmarshalHeader(suite.chainA.App.AppCodec(), bz) + suite.Require().NoError(err) + suite.Require().Equal(header, emittedHeader) + } + + } + suite.Require().True(contains) + +} diff --git a/x/ibc/core/02-client/keeper/encoding.go b/x/ibc/core/02-client/keeper/encoding.go new file mode 100644 index 000000000000..f2a07b864d27 --- /dev/null +++ b/x/ibc/core/02-client/keeper/encoding.go @@ -0,0 +1,42 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// UnmarshalClientState attempts to decode and return an ClientState object from +// raw encoded bytes. +func (k Keeper) UnmarshalClientState(bz []byte) (exported.ClientState, error) { + return types.UnmarshalClientState(k.cdc, bz) +} + +// MustUnmarshalClientState attempts to decode and return an ClientState object from +// raw encoded bytes. It panics on error. +func (k Keeper) MustUnmarshalClientState(bz []byte) exported.ClientState { + return types.MustUnmarshalClientState(k.cdc, bz) +} + +// UnmarshalConsensusState attempts to decode and return an ConsensusState object from +// raw encoded bytes. +func (k Keeper) UnmarshalConsensusState(bz []byte) (exported.ConsensusState, error) { + return types.UnmarshalConsensusState(k.cdc, bz) +} + +// MustUnmarshalConsensusState attempts to decode and return an ConsensusState object from +// raw encoded bytes. It panics on error. +func (k Keeper) MustUnmarshalConsensusState(bz []byte) exported.ConsensusState { + return types.MustUnmarshalConsensusState(k.cdc, bz) +} + +// MustMarshalClientState attempts to encode an ClientState object and returns the +// raw encoded bytes. It panics on error. +func (k Keeper) MustMarshalClientState(clientState exported.ClientState) []byte { + return types.MustMarshalClientState(k.cdc, clientState) +} + +// MustMarshalConsensusState attempts to encode an ConsensusState object and returns the +// raw encoded bytes. It panics on error. +func (k Keeper) MustMarshalConsensusState(consensusState exported.ConsensusState) []byte { + return types.MustMarshalConsensusState(k.cdc, consensusState) +} diff --git a/x/ibc/core/02-client/keeper/grpc_query.go b/x/ibc/core/02-client/keeper/grpc_query.go new file mode 100644 index 000000000000..6328e5de3b81 --- /dev/null +++ b/x/ibc/core/02-client/keeper/grpc_query.go @@ -0,0 +1,198 @@ +package keeper + +import ( + "context" + "fmt" + "sort" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ types.QueryServer = Keeper{} + +// ClientState implements the Query/ClientState gRPC method +func (q Keeper) ClientState(c context.Context, req *types.QueryClientStateRequest) (*types.QueryClientStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ClientIdentifierValidator(req.ClientId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + clientState, found := q.GetClientState(ctx, req.ClientId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrap(types.ErrClientNotFound, req.ClientId).Error(), + ) + } + + any, err := types.PackClientState(clientState) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + proofHeight := types.GetSelfHeight(ctx) + return &types.QueryClientStateResponse{ + ClientState: any, + ProofHeight: proofHeight, + }, nil +} + +// ClientStates implements the Query/ClientStates gRPC method +func (q Keeper) ClientStates(c context.Context, req *types.QueryClientStatesRequest) (*types.QueryClientStatesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + + clientStates := types.IdentifiedClientStates{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), host.KeyClientStorePrefix) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + keySplit := strings.Split(string(key), "/") + if keySplit[len(keySplit)-1] != "clientState" { + return nil + } + + clientState, err := q.UnmarshalClientState(value) + if err != nil { + return err + } + + clientID := keySplit[1] + if err := host.ClientIdentifierValidator(clientID); err != nil { + return err + } + + identifiedClient := types.NewIdentifiedClientState(clientID, clientState) + clientStates = append(clientStates, identifiedClient) + return nil + }) + + if err != nil { + return nil, err + } + + sort.Sort(clientStates) + + return &types.QueryClientStatesResponse{ + ClientStates: clientStates, + Pagination: pageRes, + }, nil +} + +// ConsensusState implements the Query/ConsensusState gRPC method +func (q Keeper) ConsensusState(c context.Context, req *types.QueryConsensusStateRequest) (*types.QueryConsensusStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ClientIdentifierValidator(req.ClientId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + + var ( + consensusState exported.ConsensusState + found bool + ) + + height := types.NewHeight(req.RevisionNumber, req.RevisionHeight) + if req.LatestHeight { + consensusState, found = q.GetLatestClientConsensusState(ctx, req.ClientId) + } else { + if req.RevisionHeight == 0 { + return nil, status.Error(codes.InvalidArgument, "consensus state height cannot be 0") + } + + consensusState, found = q.GetClientConsensusState(ctx, req.ClientId, height) + } + + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrConsensusStateNotFound, "client-id: %s, height: %s", req.ClientId, height).Error(), + ) + } + + any, err := types.PackConsensusState(consensusState) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + proofHeight := types.GetSelfHeight(ctx) + return &types.QueryConsensusStateResponse{ + ConsensusState: any, + ProofHeight: proofHeight, + }, nil +} + +// ConsensusStates implements the Query/ConsensusStates gRPC method +func (q Keeper) ConsensusStates(c context.Context, req *types.QueryConsensusStatesRequest) (*types.QueryConsensusStatesResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ClientIdentifierValidator(req.ClientId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + + consensusStates := []types.ConsensusStateWithHeight{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), host.FullClientKey(req.ClientId, []byte(fmt.Sprintf("%s/", host.KeyConsensusStatePrefix)))) + + 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), "/") { + return false, nil + } + + height, err := types.ParseHeight(string(key)) + if err != nil { + return false, err + } + + consensusState, err := q.UnmarshalConsensusState(value) + if err != nil { + return false, err + } + + consensusStates = append(consensusStates, types.NewConsensusStateWithHeight(height, consensusState)) + return true, nil + }) + + if err != nil { + return nil, err + } + + return &types.QueryConsensusStatesResponse{ + ConsensusStates: consensusStates, + Pagination: pageRes, + }, nil +} + +// ClientParams implements the Query/ClientParams gRPC method +func (q Keeper) ClientParams(c context.Context, _ *types.QueryClientParamsRequest) (*types.QueryClientParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := q.GetParams(ctx) + + return &types.QueryClientParamsResponse{ + Params: ¶ms, + }, 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 new file mode 100644 index 000000000000..5e361a76f083 --- /dev/null +++ b/x/ibc/core/02-client/keeper/grpc_query_test.go @@ -0,0 +1,381 @@ +package keeper_test + +import ( + "fmt" + "time" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/types/query" + "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/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *KeeperTestSuite) TestQueryClientState() { + var ( + req *types.QueryClientStateRequest + expClientState *codectypes.Any + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"invalid clientID", + func() { + req = &types.QueryClientStateRequest{} + }, + false, + }, + {"client not found", + func() { + req = &types.QueryClientStateRequest{ + ClientId: testClientID, + } + }, + false, + }, + { + "success", + func() { + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + + var err error + expClientState, err = types.PackClientState(clientState) + suite.Require().NoError(err) + + req = &types.QueryClientStateRequest{ + ClientId: testClientID, + } + }, + 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.ClientState(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expClientState, res.ClientState) + + // ensure UnpackInterfaces is defined + cachedValue := res.ClientState.GetCachedValue() + suite.Require().NotNil(cachedValue) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryClientStates() { + var ( + req *types.QueryClientStatesRequest + expClientStates = types.IdentifiedClientStates{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty pagination", + func() { + req = &types.QueryClientStatesRequest{} + }, + true, + }, + { + "success, no results", + func() { + req = &types.QueryClientStatesRequest{ + Pagination: &query.PageRequest{ + Limit: 3, + CountTotal: true, + }, + } + }, + true, + }, + { + "success", + func() { + clientA1, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientA2, _ := suite.coordinator.CreateClient(suite.chainA, suite.chainB, exported.Tendermint) + + clientStateA1 := suite.chainA.GetClientState(clientA1) + clientStateA2 := suite.chainA.GetClientState(clientA2) + + idcs := types.NewIdentifiedClientState(clientA1, clientStateA1) + idcs2 := types.NewIdentifiedClientState(clientA2, clientStateA2) + + // order is sorted by client id, localhost is last + expClientStates = types.IdentifiedClientStates{idcs, idcs2}.Sort() + req = &types.QueryClientStatesRequest{ + Pagination: &query.PageRequest{ + Limit: 7, + CountTotal: true, + }, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + expClientStates = nil + + tc.malleate() + + // always add localhost which is created by default in init genesis + localhostClientState := suite.chainA.GetClientState(exported.Localhost) + identifiedLocalhost := types.NewIdentifiedClientState(exported.Localhost, localhostClientState) + expClientStates = append(expClientStates, identifiedLocalhost) + + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ClientStates(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expClientStates.Sort(), res.ClientStates) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConsensusState() { + var ( + req *types.QueryConsensusStateRequest + expConsensusState *codectypes.Any + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "invalid clientID", + func() { + req = &types.QueryConsensusStateRequest{} + }, + false, + }, + { + "invalid height", + func() { + req = &types.QueryConsensusStateRequest{ + ClientId: testClientID, + RevisionNumber: 0, + RevisionHeight: 0, + LatestHeight: false, + } + }, + false, + }, + { + "consensus state not found", + func() { + req = &types.QueryConsensusStateRequest{ + ClientId: testClientID, + LatestHeight: true, + } + }, + false, + }, + { + "success latest height", + func() { + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + cs := ibctmtypes.NewConsensusState( + suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash1")), nil, + ) + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, cs) + + var err error + expConsensusState, err = types.PackConsensusState(cs) + suite.Require().NoError(err) + + req = &types.QueryConsensusStateRequest{ + ClientId: testClientID, + LatestHeight: true, + } + }, + true, + }, + { + "success with height", + func() { + cs := ibctmtypes.NewConsensusState( + suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash1")), nil, + ) + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, cs) + + var err error + expConsensusState, err = types.PackConsensusState(cs) + suite.Require().NoError(err) + + req = &types.QueryConsensusStateRequest{ + ClientId: testClientID, + RevisionNumber: 0, + RevisionHeight: height, + } + }, + 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.ConsensusState(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expConsensusState, res.ConsensusState) + + // ensure UnpackInterfaces is defined + cachedValue := res.ConsensusState.GetCachedValue() + suite.Require().NotNil(cachedValue) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConsensusStates() { + var ( + req *types.QueryConsensusStatesRequest + expConsensusStates = []types.ConsensusStateWithHeight{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "invalid client identifier", + func() { + req = &types.QueryConsensusStatesRequest{} + }, + false, + }, + { + "empty pagination", + func() { + req = &types.QueryConsensusStatesRequest{ + ClientId: testClientID, + } + }, + true, + }, + { + "success, no results", + func() { + req = &types.QueryConsensusStatesRequest{ + ClientId: testClientID, + Pagination: &query.PageRequest{ + Limit: 3, + CountTotal: true, + }, + } + }, + true, + }, + { + "success", + func() { + cs := ibctmtypes.NewConsensusState( + suite.consensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte("hash1")), nil, + ) + cs2 := ibctmtypes.NewConsensusState( + suite.consensusState.Timestamp.Add(time.Second), commitmenttypes.NewMerkleRoot([]byte("hash2")), nil, + ) + + clientState := ibctmtypes.NewClientState( + testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + ) + + // Use CreateClient to ensure that processedTime metadata gets stored. + clientId, err := suite.keeper.CreateClient(suite.ctx, clientState, cs) + suite.Require().NoError(err) + suite.keeper.SetClientConsensusState(suite.ctx, clientId, testClientHeight.Increment(), cs2) + + // order is swapped because the res is sorted by client id + expConsensusStates = []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight(testClientHeight, cs), + types.NewConsensusStateWithHeight(testClientHeight.Increment().(types.Height), cs2), + } + req = &types.QueryConsensusStatesRequest{ + ClientId: clientId, + Pagination: &query.PageRequest{ + Limit: 3, + 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.ConsensusStates(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(len(expConsensusStates), len(res.ConsensusStates)) + for i := range expConsensusStates { + suite.Require().NotNil(res.ConsensusStates[i]) + 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) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryParams() { + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + expParams := types.DefaultParams() + res, _ := suite.queryClient.ClientParams(ctx, &types.QueryClientParamsRequest{}) + suite.Require().Equal(&expParams, res.Params) +} diff --git a/x/ibc/core/02-client/keeper/keeper.go b/x/ibc/core/02-client/keeper/keeper.go new file mode 100644 index 000000000000..67c5c0658d40 --- /dev/null +++ b/x/ibc/core/02-client/keeper/keeper.go @@ -0,0 +1,367 @@ +package keeper + +import ( + "fmt" + "reflect" + "strings" + + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/light" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +// Keeper represents a type that grants read and write permissions to any client +// state information +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + paramSpace paramtypes.Subspace + stakingKeeper types.StakingKeeper +} + +// NewKeeper creates a new NewKeeper instance +func NewKeeper(cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace paramtypes.Subspace, sk types.StakingKeeper) Keeper { + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + storeKey: key, + cdc: cdc, + paramSpace: paramSpace, + stakingKeeper: sk, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) +} + +// GenerateClientIdentifier returns the next client identifier. +func (k Keeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string { + nextClientSeq := k.GetNextClientSequence(ctx) + clientID := types.FormatClientIdentifier(clientType, nextClientSeq) + + nextClientSeq++ + k.SetNextClientSequence(ctx, nextClientSeq) + return clientID +} + +// GetClientState gets a particular client from the store +func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) { + store := k.ClientStore(ctx, clientID) + bz := store.Get(host.ClientStateKey()) + if bz == nil { + return nil, false + } + + clientState := k.MustUnmarshalClientState(bz) + return clientState, true +} + +// SetClientState sets a particular Client to the store +func (k Keeper) SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) { + store := k.ClientStore(ctx, clientID) + store.Set(host.ClientStateKey(), k.MustMarshalClientState(clientState)) +} + +// GetClientConsensusState gets the stored consensus state from a client at a given height. +func (k Keeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) { + store := k.ClientStore(ctx, clientID) + bz := store.Get(host.ConsensusStateKey(height)) + if bz == nil { + return nil, false + } + + consensusState := k.MustUnmarshalConsensusState(bz) + return consensusState, true +} + +// SetClientConsensusState sets a ConsensusState to a particular client at the given +// height +func (k Keeper) SetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height, consensusState exported.ConsensusState) { + store := k.ClientStore(ctx, clientID) + store.Set(host.ConsensusStateKey(height), k.MustMarshalConsensusState(consensusState)) +} + +// GetNextClientSequence gets the next client sequence from the store. +func (k Keeper) GetNextClientSequence(ctx sdk.Context) uint64 { + store := ctx.KVStore(k.storeKey) + bz := store.Get([]byte(types.KeyNextClientSequence)) + if bz == nil { + panic("next client sequence is nil") + } + + return sdk.BigEndianToUint64(bz) +} + +// SetNextClientSequence sets the next client sequence to the store. +func (k Keeper) SetNextClientSequence(ctx sdk.Context, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set([]byte(types.KeyNextClientSequence), bz) +} + +// IterateConsensusStates provides an iterator over all stored consensus states. +// objects. For each State object, cb will be called. If the cb returns true, +// the iterator will close and stop. +func (k Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, cs types.ConsensusStateWithHeight) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + // consensus key is in the format "clients//consensusStates/" + if len(keySplit) != 4 || keySplit[2] != string(host.KeyConsensusStatePrefix) { + continue + } + clientID := keySplit[1] + height := types.MustParseHeight(keySplit[3]) + consensusState := k.MustUnmarshalConsensusState(iterator.Value()) + + consensusStateWithHeight := types.NewConsensusStateWithHeight(height, consensusState) + + if cb(clientID, consensusStateWithHeight) { + break + } + } +} + +// GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState +func (k Keeper) GetAllGenesisClients(ctx sdk.Context) types.IdentifiedClientStates { + var genClients types.IdentifiedClientStates + k.IterateClients(ctx, func(clientID string, cs exported.ClientState) bool { + genClients = append(genClients, types.NewIdentifiedClientState(clientID, cs)) + return false + }) + + return genClients.Sort() +} + +// GetAllClientMetadata will take a list of IdentifiedClientState and return a list +// of IdentifiedGenesisMetadata necessary for exporting and importing client metadata +// into the client store. +func (k Keeper) GetAllClientMetadata(ctx sdk.Context, genClients []types.IdentifiedClientState) ([]types.IdentifiedGenesisMetadata, error) { + genMetadata := make([]types.IdentifiedGenesisMetadata, 0) + for _, ic := range genClients { + cs, err := types.UnpackClientState(ic.ClientState) + if err != nil { + return nil, err + } + gms := cs.ExportMetadata(k.ClientStore(ctx, ic.ClientId)) + if len(gms) == 0 { + continue + } + clientMetadata := make([]types.GenesisMetadata, len(gms)) + for i, metadata := range gms { + cmd, ok := metadata.(types.GenesisMetadata) + if !ok { + return nil, sdkerrors.Wrapf(types.ErrInvalidClientMetadata, "expected metadata type: %T, got: %T", + types.GenesisMetadata{}, cmd) + } + clientMetadata[i] = cmd + } + genMetadata = append(genMetadata, types.NewIdentifiedGenesisMetadata( + ic.ClientId, + clientMetadata, + )) + } + return genMetadata, nil +} + +// SetAllClientMetadata takes a list of IdentifiedGenesisMetadata and stores all of the metadata in the client store at the appropriate paths. +func (k Keeper) SetAllClientMetadata(ctx sdk.Context, genMetadata []types.IdentifiedGenesisMetadata) { + for _, igm := range genMetadata { + // create client store + store := k.ClientStore(ctx, igm.ClientId) + // set all metadata kv pairs in client store + for _, md := range igm.ClientMetadata { + store.Set(md.GetKey(), md.GetValue()) + } + } +} + +// GetAllConsensusStates returns all stored client consensus states. +func (k Keeper) GetAllConsensusStates(ctx sdk.Context) types.ClientsConsensusStates { + clientConsStates := make(types.ClientsConsensusStates, 0) + mapClientIDToConsStateIdx := make(map[string]int) + + k.IterateConsensusStates(ctx, func(clientID string, cs types.ConsensusStateWithHeight) bool { + idx, ok := mapClientIDToConsStateIdx[clientID] + if ok { + clientConsStates[idx].ConsensusStates = append(clientConsStates[idx].ConsensusStates, cs) + return false + } + + clientConsState := types.ClientConsensusStates{ + ClientId: clientID, + ConsensusStates: []types.ConsensusStateWithHeight{cs}, + } + + clientConsStates = append(clientConsStates, clientConsState) + mapClientIDToConsStateIdx[clientID] = len(clientConsStates) - 1 + return false + }) + + return clientConsStates.Sort() +} + +// HasClientConsensusState returns if keeper has a ConsensusState for a particular +// client at the given height +func (k Keeper) HasClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) bool { + store := k.ClientStore(ctx, clientID) + return store.Has(host.ConsensusStateKey(height)) +} + +// GetLatestClientConsensusState gets the latest ConsensusState stored for a given client +func (k Keeper) GetLatestClientConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) { + clientState, ok := k.GetClientState(ctx, clientID) + if !ok { + return nil, false + } + return k.GetClientConsensusState(ctx, clientID, clientState.GetLatestHeight()) +} + +// GetSelfConsensusState introspects the (self) past historical info at a given height +// and returns the expected consensus state at that height. +// For now, can only retrieve self consensus states for the current revision +func (k Keeper) GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) { + selfHeight, ok := height.(types.Height) + if !ok { + return nil, false + } + // check that height revision matches chainID revision + revision := types.ParseChainID(ctx.ChainID()) + if revision != height.GetRevisionNumber() { + return nil, false + } + histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, int64(selfHeight.RevisionHeight)) + if !found { + return nil, false + } + + consensusState := &ibctmtypes.ConsensusState{ + Timestamp: histInfo.Header.Time, + Root: commitmenttypes.NewMerkleRoot(histInfo.Header.GetAppHash()), + NextValidatorsHash: histInfo.Header.NextValidatorsHash, + } + return consensusState, true +} + +// ValidateSelfClient validates the client parameters for a client of the running chain +// This function is only used to validate the client state the counterparty stores for this chain +// Client must be in same revision as the executing chain +func (k Keeper) ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error { + tmClient, ok := clientState.(*ibctmtypes.ClientState) + if !ok { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client must be a Tendermint client, expected: %T, got: %T", + &ibctmtypes.ClientState{}, tmClient) + } + + if clientState.IsFrozen() { + return types.ErrClientFrozen + } + + if ctx.ChainID() != tmClient.ChainId { + return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid chain-id. expected: %s, got: %s", + ctx.ChainID(), tmClient.ChainId) + } + + revision := types.ParseChainID(ctx.ChainID()) + + // client must be in the same revision as executing chain + if tmClient.LatestHeight.RevisionNumber != revision { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client is not in the same revision as the chain. expected revision: %d, got: %d", + tmClient.LatestHeight.RevisionNumber, revision) + } + + selfHeight := types.NewHeight(revision, uint64(ctx.BlockHeight())) + if tmClient.LatestHeight.GTE(selfHeight) { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client has LatestHeight %d greater than or equal to chain height %d", + tmClient.LatestHeight, selfHeight) + } + + expectedProofSpecs := commitmenttypes.GetSDKSpecs() + if !reflect.DeepEqual(expectedProofSpecs, tmClient.ProofSpecs) { + return sdkerrors.Wrapf(types.ErrInvalidClient, "client has invalid proof specs. expected: %v got: %v", + expectedProofSpecs, tmClient.ProofSpecs) + } + + if err := light.ValidateTrustLevel(tmClient.TrustLevel.ToTendermint()); err != nil { + return sdkerrors.Wrapf(types.ErrInvalidClient, "trust-level invalid: %v", err) + } + + expectedUbdPeriod := k.stakingKeeper.UnbondingTime(ctx) + if expectedUbdPeriod != tmClient.UnbondingPeriod { + return sdkerrors.Wrapf(types.ErrInvalidClient, "invalid unbonding period. expected: %s, got: %s", + expectedUbdPeriod, tmClient.UnbondingPeriod) + } + + if tmClient.UnbondingPeriod < tmClient.TrustingPeriod { + return sdkerrors.Wrapf(types.ErrInvalidClient, "unbonding period must be greater than trusting period. unbonding period (%d) < trusting period (%d)", + tmClient.UnbondingPeriod, tmClient.TrustingPeriod) + } + + if len(tmClient.UpgradePath) != 0 { + // For now, SDK IBC implementation assumes that upgrade path (if defined) is defined by SDK upgrade module + expectedUpgradePath := []string{upgradetypes.StoreKey, upgradetypes.KeyUpgradedIBCState} + if !reflect.DeepEqual(expectedUpgradePath, tmClient.UpgradePath) { + return sdkerrors.Wrapf(types.ErrInvalidClient, "upgrade path must be the upgrade path defined by upgrade module. expected %v, got %v", + expectedUpgradePath, tmClient.UpgradePath) + } + } + return nil +} + +// IterateClients provides an iterator over all stored light client State +// objects. For each State object, cb will be called. If the cb returns true, +// the iterator will close and stop. +func (k Keeper) IterateClients(ctx sdk.Context, cb func(clientID string, cs exported.ClientState) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, host.KeyClientStorePrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + if keySplit[len(keySplit)-1] != host.KeyClientState { + continue + } + clientState := k.MustUnmarshalClientState(iterator.Value()) + + // key is ibc/{clientid}/clientState + // Thus, keySplit[1] is clientID + if cb(keySplit[1], clientState) { + break + } + } +} + +// GetAllClients returns all stored light client State objects. +func (k Keeper) GetAllClients(ctx sdk.Context) (states []exported.ClientState) { + k.IterateClients(ctx, func(_ string, state exported.ClientState) bool { + states = append(states, state) + return false + }) + return states +} + +// ClientStore returns isolated prefix store for each client so they can read/write in separate +// namespace without being able to read/write other client's data +func (k Keeper) ClientStore(ctx sdk.Context, clientID string) sdk.KVStore { + clientPrefix := []byte(fmt.Sprintf("%s/%s/", host.KeyClientStorePrefix, clientID)) + return prefix.NewStore(ctx.KVStore(k.storeKey), clientPrefix) +} diff --git a/x/ibc/core/02-client/keeper/keeper_test.go b/x/ibc/core/02-client/keeper/keeper_test.go new file mode 100644 index 000000000000..c22e80cc9ec8 --- /dev/null +++ b/x/ibc/core/02-client/keeper/keeper_test.go @@ -0,0 +1,389 @@ +package keeper_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/suite" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + "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/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + testChainID = "gaiahub-0" + testChainIDRevision1 = "gaiahub-1" + + testClientID = "tendermint-0" + testClientID2 = "tendermint-1" + testClientID3 = "tendermint-2" + + height = 5 + + trustingPeriod time.Duration = time.Hour * 24 * 7 * 2 + ubdPeriod time.Duration = time.Hour * 24 * 7 * 3 + maxClockDrift time.Duration = time.Second * 10 +) + +var ( + testClientHeight = types.NewHeight(0, 5) + testClientHeightRevision1 = types.NewHeight(1, 5) + newClientHeight = types.NewHeight(1, 1) +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + cdc codec.Marshaler + ctx sdk.Context + keeper *keeper.Keeper + consensusState *ibctmtypes.ConsensusState + header *ibctmtypes.Header + valSet *tmtypes.ValidatorSet + valSetHash tmbytes.HexBytes + privVal tmtypes.PrivValidator + now time.Time + past time.Time + + queryClient types.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + + isCheckTx := false + suite.now = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) + suite.past = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + now2 := suite.now.Add(time.Hour) + app := simapp.Setup(isCheckTx) + + suite.cdc = app.AppCodec() + suite.ctx = app.BaseApp.NewContext(isCheckTx, tmproto.Header{Height: height, ChainID: testClientID, Time: now2}) + suite.keeper = &app.IBCKeeper.ClientKeeper + suite.privVal = ibctestingmock.NewPV() + + pubKey, err := suite.privVal.GetPubKey() + suite.Require().NoError(err) + + testClientHeightMinus1 := types.NewHeight(0, height-1) + + validator := tmtypes.NewValidator(pubKey, 1) + suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + suite.valSetHash = suite.valSet.Hash() + suite.header = suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeightMinus1, now2, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.consensusState = ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("hash")), suite.valSetHash) + + var validators stakingtypes.Validators + for i := 1; i < 11; i++ { + privVal := ibctestingmock.NewPV() + tmPk, err := privVal.GetPubKey() + suite.Require().NoError(err) + pk, err := cryptocodec.FromTmPubKeyInterface(tmPk) + suite.Require().NoError(err) + val, err := stakingtypes.NewValidator(sdk.ValAddress(pk.Address()), pk, stakingtypes.Description{}) + suite.Require().NoError(err) + + val.Status = stakingtypes.Bonded + val.Tokens = sdk.NewInt(rand.Int63()) + validators = append(validators, val) + + hi := stakingtypes.NewHistoricalInfo(suite.ctx.BlockHeader(), validators) + app.StakingKeeper.SetHistoricalInfo(suite.ctx, int64(i), &hi) + } + + // add localhost client + revision := types.ParseChainID(suite.chainA.ChainID) + localHostClient := localhosttypes.NewClientState( + suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight())), + ) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), exported.Localhost, localHostClient) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.IBCKeeper.ClientKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) TestSetClientState() { + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + + retrievedState, found := suite.keeper.GetClientState(suite.ctx, testClientID) + suite.Require().True(found, "GetClientState failed") + suite.Require().Equal(clientState, retrievedState, "Client states are not equal") +} + +func (suite *KeeperTestSuite) TestSetClientConsensusState() { + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) + + retrievedConsState, found := suite.keeper.GetClientConsensusState(suite.ctx, testClientID, testClientHeight) + suite.Require().True(found, "GetConsensusState failed") + + tmConsState, ok := retrievedConsState.(*ibctmtypes.ConsensusState) + suite.Require().True(ok) + suite.Require().Equal(suite.consensusState, tmConsState, "ConsensusState not stored correctly") +} + +func (suite *KeeperTestSuite) TestValidateSelfClient() { + testClientHeight := types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight()-1)) + + testCases := []struct { + name string + clientState exported.ClientState + expPass bool + }{ + { + "success", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + true, + }, + { + "success with nil UpgradePath", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), nil, false, false), + true, + }, + { + "invalid client type", + localhosttypes.NewClientState(suite.chainA.ChainID, testClientHeight), + false, + }, + { + "frozen client", + &ibctmtypes.ClientState{suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false}, + false, + }, + { + "incorrect chainID", + ibctmtypes.NewClientState("gaiatestnet", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid client height", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.NewHeight(0, uint64(suite.chainA.GetContext().BlockHeight())), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid client revision", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeightRevision1, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid proof specs", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, nil, ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid trust level", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.Fraction{0, 1}, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid unbonding period", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+10, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid trusting period", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ubdPeriod+10, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + false, + }, + { + "invalid upgrade path", + ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), []string{"bad", "upgrade", "path"}, false, false), + false, + }, + } + + for _, tc := range testCases { + err := suite.chainA.App.IBCKeeper.ClientKeeper.ValidateSelfClient(suite.chainA.GetContext(), tc.clientState) + if tc.expPass { + suite.Require().NoError(err, "expected valid client for case: %s", tc.name) + } else { + suite.Require().Error(err, "expected invalid client for case: %s", tc.name) + } + } +} + +func (suite KeeperTestSuite) TestGetAllGenesisClients() { + clientIDs := []string{ + testClientID2, testClientID3, testClientID, + } + expClients := []exported.ClientState{ + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, types.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + } + + expGenClients := make(types.IdentifiedClientStates, len(expClients)) + + for i := range expClients { + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientIDs[i], expClients[i]) + expGenClients[i] = types.NewIdentifiedClientState(clientIDs[i], expClients[i]) + } + + // add localhost client + localHostClient, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), exported.Localhost) + suite.Require().True(found) + expGenClients = append(expGenClients, types.NewIdentifiedClientState(exported.Localhost, localHostClient)) + + genClients := suite.chainA.App.IBCKeeper.ClientKeeper.GetAllGenesisClients(suite.chainA.GetContext()) + + suite.Require().Equal(expGenClients.Sort(), genClients) +} + +func (suite KeeperTestSuite) TestGetAllGenesisMetadata() { + expectedGenMetadata := []types.IdentifiedGenesisMetadata{ + types.NewIdentifiedGenesisMetadata( + "clientA", + []types.GenesisMetadata{ + types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 1)), []byte("foo")), + types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 2)), []byte("bar")), + types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(0, 3)), []byte("baz")), + }, + ), + types.NewIdentifiedGenesisMetadata( + "clientB", + []types.GenesisMetadata{ + types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(1, 100)), []byte("val1")), + types.NewGenesisMetadata(ibctmtypes.ProcessedTimeKey(types.NewHeight(2, 300)), []byte("val2")), + }, + ), + } + + genClients := []types.IdentifiedClientState{ + types.NewIdentifiedClientState("clientA", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientB", &ibctmtypes.ClientState{}), + types.NewIdentifiedClientState("clientC", &ibctmtypes.ClientState{}), types.NewIdentifiedClientState("clientD", &localhosttypes.ClientState{}), + } + + suite.chainA.App.IBCKeeper.ClientKeeper.SetAllClientMetadata(suite.chainA.GetContext(), expectedGenMetadata) + + actualGenMetadata, err := suite.chainA.App.IBCKeeper.ClientKeeper.GetAllClientMetadata(suite.chainA.GetContext(), genClients) + suite.Require().NoError(err, "get client metadata returned error unexpectedly") + suite.Require().Equal(expectedGenMetadata, actualGenMetadata, "retrieved metadata is unexpected") +} + +func (suite KeeperTestSuite) TestGetConsensusState() { + suite.ctx = suite.ctx.WithBlockHeight(10) + cases := []struct { + name string + height types.Height + expPass bool + }{ + {"zero height", types.ZeroHeight(), false}, + {"height > latest height", types.NewHeight(0, uint64(suite.ctx.BlockHeight())+1), false}, + {"latest height - 1", types.NewHeight(0, uint64(suite.ctx.BlockHeight())-1), true}, + {"latest height", types.GetSelfHeight(suite.ctx), true}, + } + + for i, tc := range cases { + tc := tc + cs, found := suite.keeper.GetSelfConsensusState(suite.ctx, tc.height) + if tc.expPass { + suite.Require().True(found, "Case %d should have passed: %s", i, tc.name) + suite.Require().NotNil(cs, "Case %d should have passed: %s", i, tc.name) + } else { + suite.Require().False(found, "Case %d should have failed: %s", i, tc.name) + suite.Require().Nil(cs, "Case %d should have failed: %s", i, tc.name) + } + } +} + +func (suite KeeperTestSuite) TestConsensusStateHelpers() { + // initial setup + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, testClientHeight, suite.consensusState) + + nextState := ibctmtypes.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot([]byte("next")), suite.valSetHash) + + testClientHeightPlus5 := types.NewHeight(0, height+5) + + header := suite.chainA.CreateTMClientHeader(testClientID, int64(testClientHeightPlus5.RevisionHeight), testClientHeight, suite.header.Header.Time.Add(time.Minute), + suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + + // mock update functionality + clientState.LatestHeight = header.GetHeight().(types.Height) + suite.keeper.SetClientConsensusState(suite.ctx, testClientID, header.GetHeight(), nextState) + suite.keeper.SetClientState(suite.ctx, testClientID, clientState) + + latest, ok := suite.keeper.GetLatestClientConsensusState(suite.ctx, testClientID) + suite.Require().True(ok) + suite.Require().Equal(nextState, latest, "Latest client not returned correctly") +} + +// 2 clients in total are created on chainA. The first client is updated so it contains an initial consensus state +// and a consensus state at the update height. +func (suite KeeperTestSuite) TestGetAllConsensusStates() { + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + clientState := suite.chainA.GetClientState(clientA) + expConsensusHeight0 := clientState.GetLatestHeight() + consensusState0, ok := suite.chainA.GetConsensusState(clientA, expConsensusHeight0) + suite.Require().True(ok) + + // update client to create a second consensus state + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + clientState = suite.chainA.GetClientState(clientA) + expConsensusHeight1 := clientState.GetLatestHeight() + suite.Require().True(expConsensusHeight1.GT(expConsensusHeight0)) + consensusState1, ok := suite.chainA.GetConsensusState(clientA, expConsensusHeight1) + suite.Require().True(ok) + + expConsensus := []exported.ConsensusState{ + consensusState0, + consensusState1, + } + + // create second client on chainA + clientA2, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState = suite.chainA.GetClientState(clientA2) + + expConsensusHeight2 := clientState.GetLatestHeight() + consensusState2, ok := suite.chainA.GetConsensusState(clientA2, expConsensusHeight2) + suite.Require().True(ok) + + expConsensus2 := []exported.ConsensusState{consensusState2} + + expConsensusStates := types.ClientsConsensusStates{ + types.NewClientConsensusStates(clientA, []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight(expConsensusHeight0.(types.Height), expConsensus[0]), + types.NewConsensusStateWithHeight(expConsensusHeight1.(types.Height), expConsensus[1]), + }), + types.NewClientConsensusStates(clientA2, []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight(expConsensusHeight2.(types.Height), expConsensus2[0]), + }), + }.Sort() + + consStates := suite.chainA.App.IBCKeeper.ClientKeeper.GetAllConsensusStates(suite.chainA.GetContext()) + suite.Require().Equal(expConsensusStates, consStates, "%s \n\n%s", expConsensusStates, consStates) +} diff --git a/x/ibc/core/02-client/keeper/params.go b/x/ibc/core/02-client/keeper/params.go new file mode 100644 index 000000000000..04f4a2563799 --- /dev/null +++ b/x/ibc/core/02-client/keeper/params.go @@ -0,0 +1,23 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// GetAllowedClients retrieves the receive enabled boolean from the paramstore +func (k Keeper) GetAllowedClients(ctx sdk.Context) []string { + var res []string + k.paramSpace.Get(ctx, types.KeyAllowedClients, &res) + return res +} + +// GetParams returns the total set of ibc-transfer parameters. +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + return types.NewParams(k.GetAllowedClients(ctx)...) +} + +// SetParams sets the total set of ibc-transfer parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} diff --git a/x/ibc/core/02-client/keeper/params_test.go b/x/ibc/core/02-client/keeper/params_test.go new file mode 100644 index 000000000000..9df08597100b --- /dev/null +++ b/x/ibc/core/02-client/keeper/params_test.go @@ -0,0 +1,17 @@ +package keeper_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +func (suite *KeeperTestSuite) TestParams() { + expParams := types.DefaultParams() + + params := suite.chainA.App.IBCKeeper.ClientKeeper.GetParams(suite.chainA.GetContext()) + suite.Require().Equal(expParams, params) + + expParams.AllowedClients = []string{} + suite.chainA.App.IBCKeeper.ClientKeeper.SetParams(suite.chainA.GetContext(), expParams) + params = suite.chainA.App.IBCKeeper.ClientKeeper.GetParams(suite.chainA.GetContext()) + suite.Require().Empty(expParams.AllowedClients) +} diff --git a/x/ibc/core/02-client/keeper/proposal.go b/x/ibc/core/02-client/keeper/proposal.go new file mode 100644 index 000000000000..6b17278e09dd --- /dev/null +++ b/x/ibc/core/02-client/keeper/proposal.go @@ -0,0 +1,63 @@ +package keeper + +import ( + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "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. +func (k Keeper) ClientUpdateProposal(ctx sdk.Context, p *types.ClientUpdateProposal) error { + if p.ClientId == exported.Localhost { + return sdkerrors.Wrap(types.ErrInvalidUpdateClientProposal, "cannot update localhost client with proposal") + } + + clientState, found := k.GetClientState(ctx, p.ClientId) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", p.ClientId) + } + + header, err := types.UnpackHeader(p.Header) + if err != nil { + return err + } + + clientState, consensusState, err := clientState.CheckProposedHeaderAndUpdateState(ctx, k.cdc, k.ClientStore(ctx, p.ClientId), header) + if err != nil { + return err + } + + k.SetClientState(ctx, p.ClientId, clientState) + k.SetClientConsensusState(ctx, p.ClientId, header.GetHeight(), consensusState) + + k.Logger(ctx).Info("client updated after governance proposal passed", "client-id", p.ClientId, "height", clientState.GetLatestHeight().String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel("client-type", clientState.ClientType()), + telemetry.NewLabel("client-id", p.ClientId), + telemetry.NewLabel("update-type", "proposal"), + }, + ) + }() + + // emitting events in the keeper for proposal updates to clients + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUpdateClientProposal, + sdk.NewAttribute(types.AttributeKeyClientID, p.ClientId), + sdk.NewAttribute(types.AttributeKeyClientType, clientState.ClientType()), + sdk.NewAttribute(types.AttributeKeyConsensusHeight, header.GetHeight().String()), + ), + ) + + return nil +} diff --git a/x/ibc/core/02-client/keeper/proposal_test.go b/x/ibc/core/02-client/keeper/proposal_test.go new file mode 100644 index 000000000000..ada205402b34 --- /dev/null +++ b/x/ibc/core/02-client/keeper/proposal_test.go @@ -0,0 +1,93 @@ +package keeper_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *KeeperTestSuite) TestClientUpdateProposal() { + var ( + content *types.ClientUpdateProposal + err error + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid update client proposal", func() { + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState := suite.chainA.GetClientState(clientA) + + tmClientState, ok := clientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.FrozenHeight = tmClientState.LatestHeight + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClientState) + + // use next header for chainB to update the client on chainA + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) + suite.Require().NoError(err) + + content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientA, header) + suite.Require().NoError(err) + }, true, + }, + { + "client type does not exist", func() { + content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, &ibctmtypes.Header{}) + suite.Require().NoError(err) + }, false, + }, + { + "cannot update localhost", func() { + content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, exported.Localhost, &ibctmtypes.Header{}) + suite.Require().NoError(err) + }, false, + }, + { + "client does not exist", func() { + content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, ibctesting.InvalidID, &ibctmtypes.Header{}) + suite.Require().NoError(err) + }, 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} + }, 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) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() + + err = suite.chainA.App.IBCKeeper.ClientKeeper.ClientUpdateProposal(suite.chainA.GetContext(), content) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} diff --git a/x/ibc/core/02-client/module.go b/x/ibc/core/02-client/module.go new file mode 100644 index 000000000000..9a051ba1abcf --- /dev/null +++ b/x/ibc/core/02-client/module.go @@ -0,0 +1,24 @@ +package client + +import ( + "github.com/gogo/protobuf/grpc" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// Name returns the IBC client name +func Name() string { + return types.SubModuleName +} + +// GetQueryCmd returns no root query command for the IBC client +func GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// 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.go b/x/ibc/core/02-client/proposal_handler.go new file mode 100644 index 000000000000..befa95df642f --- /dev/null +++ b/x/ibc/core/02-client/proposal_handler.go @@ -0,0 +1,22 @@ +package client + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// NewClientUpdateProposalHandler defines the client update proposal handler +func NewClientUpdateProposalHandler(k keeper.Keeper) govtypes.Handler { + return func(ctx sdk.Context, content govtypes.Content) error { + switch c := content.(type) { + case *types.ClientUpdateProposal: + return k.ClientUpdateProposal(ctx, c) + + default: + return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized ibc proposal content type: %T", c) + } + } +} diff --git a/x/ibc/core/02-client/proposal_handler_test.go b/x/ibc/core/02-client/proposal_handler_test.go new file mode 100644 index 000000000000..91c1451b7076 --- /dev/null +++ b/x/ibc/core/02-client/proposal_handler_test.go @@ -0,0 +1,76 @@ +package client_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + client "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *ClientTestSuite) TestNewClientUpdateProposalHandler() { + var ( + content govtypes.Content + err error + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid update client proposal", func() { + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState := suite.chainA.GetClientState(clientA) + + tmClientState, ok := clientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClientState.AllowUpdateAfterMisbehaviour = true + tmClientState.FrozenHeight = tmClientState.LatestHeight + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClientState) + + // use next header for chainB to update the client on chainA + header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, clientA) + suite.Require().NoError(err) + + content, err = clienttypes.NewClientUpdateProposal(ibctesting.Title, ibctesting.Description, clientA, header) + suite.Require().NoError(err) + }, true, + }, + { + "nil proposal", func() { + content = nil + }, false, + }, + { + "unsupported proposal type", func() { + content = distributiontypes.NewCommunityPoolSpendProposal(ibctesting.Title, ibctesting.Description, suite.chainA.SenderAccount.GetAddress(), sdk.NewCoins(sdk.NewCoin("communityfunds", sdk.NewInt(10)))) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() + + proposalHandler := client.NewClientUpdateProposalHandler(suite.chainA.App.IBCKeeper.ClientKeeper) + + err = proposalHandler(suite.chainA.GetContext(), content) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} diff --git a/x/ibc/core/02-client/simulation/decoder.go b/x/ibc/core/02-client/simulation/decoder.go new file mode 100644 index 000000000000..03a803b1b172 --- /dev/null +++ b/x/ibc/core/02-client/simulation/decoder.go @@ -0,0 +1,38 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ ClientUnmarshaler = (*keeper.Keeper)(nil) + +// ClientUnmarshaler defines an interface for unmarshaling ICS02 interfaces. +type ClientUnmarshaler interface { + MustUnmarshalClientState([]byte) exported.ClientState + MustUnmarshalConsensusState([]byte) exported.ConsensusState +} + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding client type. +func NewDecodeStore(cdc ClientUnmarshaler, kvA, kvB kv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.HasSuffix(kvA.Key, []byte(host.KeyClientState)): + clientStateA := cdc.MustUnmarshalClientState(kvA.Value) + clientStateB := cdc.MustUnmarshalClientState(kvB.Value) + return fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientStateA, clientStateB), true + + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.Contains(kvA.Key, []byte(host.KeyConsensusStatePrefix)): + consensusStateA := cdc.MustUnmarshalConsensusState(kvA.Value) + consensusStateB := cdc.MustUnmarshalConsensusState(kvB.Value) + return fmt.Sprintf("ConsensusState A: %v\nConsensusState B: %v", consensusStateA, consensusStateB), true + + default: + return "", false + } +} diff --git a/x/ibc/core/02-client/simulation/decoder_test.go b/x/ibc/core/02-client/simulation/decoder_test.go new file mode 100644 index 000000000000..095834ba0d61 --- /dev/null +++ b/x/ibc/core/02-client/simulation/decoder_test.go @@ -0,0 +1,70 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + clientID := "clientidone" + + height := types.NewHeight(0, 10) + + clientState := &ibctmtypes.ClientState{ + FrozenHeight: height, + } + + consState := &ibctmtypes.ConsensusState{ + Timestamp: time.Now().UTC(), + } + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: host.FullClientStateKey(clientID), + Value: app.IBCKeeper.ClientKeeper.MustMarshalClientState(clientState), + }, + { + Key: host.FullConsensusStateKey(clientID, height), + Value: app.IBCKeeper.ClientKeeper.MustMarshalConsensusState(consState), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientState", fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientState, clientState)}, + {"ConsensusState", fmt.Sprintf("ConsensusState A: %v\nConsensusState B: %v", consState, consState)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(app.IBCKeeper.ClientKeeper, kvPairs.Pairs[i], kvPairs.Pairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs.Pairs[i].Key)) + require.Empty(t, res, string(kvPairs.Pairs[i].Key)) + } else { + require.True(t, found, string(kvPairs.Pairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs.Pairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/core/02-client/simulation/genesis.go b/x/ibc/core/02-client/simulation/genesis.go new file mode 100644 index 000000000000..2f23197026c1 --- /dev/null +++ b/x/ibc/core/02-client/simulation/genesis.go @@ -0,0 +1,13 @@ +package simulation + +import ( + "math/rand" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// GenClientGenesis returns the default client genesis state. +func GenClientGenesis(_ *rand.Rand, _ []simtypes.Account) types.GenesisState { + return types.DefaultGenesisState() +} diff --git a/x/ibc/core/02-client/types/client.go b/x/ibc/core/02-client/types/client.go new file mode 100644 index 000000000000..6d51828af0f7 --- /dev/null +++ b/x/ibc/core/02-client/types/client.go @@ -0,0 +1,111 @@ +package types + +import ( + "fmt" + "math" + "sort" + "strings" + + proto "github.com/gogo/protobuf/proto" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + _ codectypes.UnpackInterfacesMessage = IdentifiedClientState{} + _ codectypes.UnpackInterfacesMessage = ConsensusStateWithHeight{} +) + +// NewIdentifiedClientState creates a new IdentifiedClientState instance +func NewIdentifiedClientState(clientID string, clientState exported.ClientState) IdentifiedClientState { + msg, ok := clientState.(proto.Message) + if !ok { + panic(fmt.Errorf("cannot proto marshal %T", clientState)) + } + + anyClientState, err := codectypes.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + + return IdentifiedClientState{ + ClientId: clientID, + ClientState: anyClientState, + } +} + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (ics IdentifiedClientState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(ics.ClientState, new(exported.ClientState)) +} + +var _ sort.Interface = IdentifiedClientStates{} + +// IdentifiedClientStates defines a slice of ClientConsensusStates that supports the sort interface +type IdentifiedClientStates []IdentifiedClientState + +// Len implements sort.Interface +func (ics IdentifiedClientStates) Len() int { return len(ics) } + +// Less implements sort.Interface +func (ics IdentifiedClientStates) Less(i, j int) bool { return ics[i].ClientId < ics[j].ClientId } + +// Swap implements sort.Interface +func (ics IdentifiedClientStates) Swap(i, j int) { ics[i], ics[j] = ics[j], ics[i] } + +// Sort is a helper function to sort the set of IdentifiedClientStates in place +func (ics IdentifiedClientStates) Sort() IdentifiedClientStates { + sort.Sort(ics) + return ics +} + +// NewConsensusStateWithHeight creates a new ConsensusStateWithHeight instance +func NewConsensusStateWithHeight(height Height, consensusState exported.ConsensusState) ConsensusStateWithHeight { + msg, ok := consensusState.(proto.Message) + if !ok { + panic(fmt.Errorf("cannot proto marshal %T", consensusState)) + } + + anyConsensusState, err := codectypes.NewAnyWithValue(msg) + if err != nil { + panic(err) + } + + return ConsensusStateWithHeight{ + Height: height, + ConsensusState: anyConsensusState, + } +} + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (cswh ConsensusStateWithHeight) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(cswh.ConsensusState, new(exported.ConsensusState)) +} + +// ValidateClientType validates the client type. It cannot be blank or empty. It must be a valid +// client identifier when used with '0' or the maximum uint64 as the sequence. +func ValidateClientType(clientType string) error { + if strings.TrimSpace(clientType) == "" { + return sdkerrors.Wrap(ErrInvalidClientType, "client type cannot be blank") + } + + smallestPossibleClientID := FormatClientIdentifier(clientType, 0) + largestPossibleClientID := FormatClientIdentifier(clientType, uint64(math.MaxUint64)) + + // IsValidClientID will check client type format and if the sequence is a uint64 + if !IsValidClientID(smallestPossibleClientID) { + return sdkerrors.Wrap(ErrInvalidClientType, "") + } + + if err := host.ClientIdentifierValidator(smallestPossibleClientID); err != nil { + return sdkerrors.Wrap(err, "client type results in smallest client identifier being invalid") + } + if err := host.ClientIdentifierValidator(largestPossibleClientID); err != nil { + return sdkerrors.Wrap(err, "client type results in largest client identifier being invalid") + } + + return nil +} diff --git a/x/ibc/core/02-client/types/client.pb.go b/x/ibc/core/02-client/types/client.pb.go new file mode 100644 index 000000000000..a42ddef4c593 --- /dev/null +++ b/x/ibc/core/02-client/types/client.pb.go @@ -0,0 +1,1549 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/client/v1/client.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" + 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 + +// IdentifiedClientState defines a client state with an additional client +// identifier field. +type IdentifiedClientState struct { + // client identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // client state + ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` +} + +func (m *IdentifiedClientState) Reset() { *m = IdentifiedClientState{} } +func (m *IdentifiedClientState) String() string { return proto.CompactTextString(m) } +func (*IdentifiedClientState) ProtoMessage() {} +func (*IdentifiedClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{0} +} +func (m *IdentifiedClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IdentifiedClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IdentifiedClientState.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 *IdentifiedClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_IdentifiedClientState.Merge(m, src) +} +func (m *IdentifiedClientState) XXX_Size() int { + return m.Size() +} +func (m *IdentifiedClientState) XXX_DiscardUnknown() { + xxx_messageInfo_IdentifiedClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_IdentifiedClientState proto.InternalMessageInfo + +func (m *IdentifiedClientState) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *IdentifiedClientState) GetClientState() *types.Any { + if m != nil { + return m.ClientState + } + return nil +} + +// ConsensusStateWithHeight defines a consensus state with an additional height field. +type ConsensusStateWithHeight struct { + // consensus state height + Height Height `protobuf:"bytes,1,opt,name=height,proto3" json:"height"` + // consensus state + ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml"consensus_state"` +} + +func (m *ConsensusStateWithHeight) Reset() { *m = ConsensusStateWithHeight{} } +func (m *ConsensusStateWithHeight) String() string { return proto.CompactTextString(m) } +func (*ConsensusStateWithHeight) ProtoMessage() {} +func (*ConsensusStateWithHeight) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{1} +} +func (m *ConsensusStateWithHeight) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusStateWithHeight) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusStateWithHeight.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 *ConsensusStateWithHeight) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusStateWithHeight.Merge(m, src) +} +func (m *ConsensusStateWithHeight) XXX_Size() int { + return m.Size() +} +func (m *ConsensusStateWithHeight) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusStateWithHeight.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusStateWithHeight proto.InternalMessageInfo + +func (m *ConsensusStateWithHeight) GetHeight() Height { + if m != nil { + return m.Height + } + return Height{} +} + +func (m *ConsensusStateWithHeight) GetConsensusState() *types.Any { + if m != nil { + return m.ConsensusState + } + return nil +} + +// ClientConsensusStates defines all the stored consensus states for a given +// client. +type ClientConsensusStates struct { + // client identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // consensus states and their heights associated with the client + ConsensusStates []ConsensusStateWithHeight `protobuf:"bytes,2,rep,name=consensus_states,json=consensusStates,proto3" json:"consensus_states" yaml:"consensus_states"` +} + +func (m *ClientConsensusStates) Reset() { *m = ClientConsensusStates{} } +func (m *ClientConsensusStates) String() string { return proto.CompactTextString(m) } +func (*ClientConsensusStates) ProtoMessage() {} +func (*ClientConsensusStates) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{2} +} +func (m *ClientConsensusStates) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientConsensusStates) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientConsensusStates.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 *ClientConsensusStates) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientConsensusStates.Merge(m, src) +} +func (m *ClientConsensusStates) XXX_Size() int { + return m.Size() +} +func (m *ClientConsensusStates) XXX_DiscardUnknown() { + xxx_messageInfo_ClientConsensusStates.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientConsensusStates proto.InternalMessageInfo + +func (m *ClientConsensusStates) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *ClientConsensusStates) GetConsensusStates() []ConsensusStateWithHeight { + if m != nil { + return m.ConsensusStates + } + 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. +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"` +} + +func (m *ClientUpdateProposal) Reset() { *m = ClientUpdateProposal{} } +func (m *ClientUpdateProposal) String() string { return proto.CompactTextString(m) } +func (*ClientUpdateProposal) ProtoMessage() {} +func (*ClientUpdateProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{3} +} +func (m *ClientUpdateProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientUpdateProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientUpdateProposal.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 *ClientUpdateProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientUpdateProposal.Merge(m, src) +} +func (m *ClientUpdateProposal) XXX_Size() int { + return m.Size() +} +func (m *ClientUpdateProposal) XXX_DiscardUnknown() { + xxx_messageInfo_ClientUpdateProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientUpdateProposal proto.InternalMessageInfo + +// Height is a monotonically increasing data type +// that can be compared against another Height for the purposes of updating and +// freezing clients +// +// Normally the RevisionHeight is incremented at each height while keeping RevisionNumber +// the same. However some consensus algorithms may choose to reset the +// height in certain conditions e.g. hard forks, state-machine breaking changes +// In these cases, the RevisionNumber is incremented so that height continues to +// be monitonically increasing even as the RevisionHeight gets reset +type Height struct { + // the revision that the client is currently on + RevisionNumber uint64 `protobuf:"varint,1,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty" yaml:"revision_number"` + // the height within the given revision + RevisionHeight uint64 `protobuf:"varint,2,opt,name=revision_height,json=revisionHeight,proto3" json:"revision_height,omitempty" yaml:"revision_height"` +} + +func (m *Height) Reset() { *m = Height{} } +func (*Height) ProtoMessage() {} +func (*Height) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{4} +} +func (m *Height) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Height) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Height.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 *Height) XXX_Merge(src proto.Message) { + xxx_messageInfo_Height.Merge(m, src) +} +func (m *Height) XXX_Size() int { + return m.Size() +} +func (m *Height) XXX_DiscardUnknown() { + xxx_messageInfo_Height.DiscardUnknown(m) +} + +var xxx_messageInfo_Height proto.InternalMessageInfo + +// Params defines the set of IBC light client parameters. +type Params struct { + // allowed_clients defines the list of allowed client state types. + AllowedClients []string `protobuf:"bytes,1,rep,name=allowed_clients,json=allowedClients,proto3" json:"allowed_clients,omitempty" yaml:"allowed_clients"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_b6bc4c8185546947, []int{5} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetAllowedClients() []string { + if m != nil { + return m.AllowedClients + } + return nil +} + +func init() { + proto.RegisterType((*IdentifiedClientState)(nil), "ibc.core.client.v1.IdentifiedClientState") + proto.RegisterType((*ConsensusStateWithHeight)(nil), "ibc.core.client.v1.ConsensusStateWithHeight") + proto.RegisterType((*ClientConsensusStates)(nil), "ibc.core.client.v1.ClientConsensusStates") + proto.RegisterType((*ClientUpdateProposal)(nil), "ibc.core.client.v1.ClientUpdateProposal") + proto.RegisterType((*Height)(nil), "ibc.core.client.v1.Height") + proto.RegisterType((*Params)(nil), "ibc.core.client.v1.Params") +} + +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, +} + +func (m *IdentifiedClientState) 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 *IdentifiedClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IdentifiedClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintClient(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintClient(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConsensusStateWithHeight) 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 *ConsensusStateWithHeight) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusStateWithHeight) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintClient(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintClient(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ClientConsensusStates) 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 *ClientConsensusStates) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientConsensusStates) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsensusStates) > 0 { + for iNdEx := len(m.ConsensusStates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ConsensusStates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintClient(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintClient(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ClientUpdateProposal) 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 *ClientUpdateProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientUpdateProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = 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)) + } + 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))) + i-- + dAtA[i] = 0x1a + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintClient(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintClient(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Height) 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 *Height) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Height) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RevisionHeight != 0 { + i = encodeVarintClient(dAtA, i, uint64(m.RevisionHeight)) + i-- + dAtA[i] = 0x10 + } + if m.RevisionNumber != 0 { + i = encodeVarintClient(dAtA, i, uint64(m.RevisionNumber)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowedClients) > 0 { + for iNdEx := len(m.AllowedClients) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedClients[iNdEx]) + copy(dAtA[i:], m.AllowedClients[iNdEx]) + i = encodeVarintClient(dAtA, i, uint64(len(m.AllowedClients[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintClient(dAtA []byte, offset int, v uint64) int { + offset -= sovClient(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *IdentifiedClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovClient(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovClient(uint64(l)) + } + return n +} + +func (m *ConsensusStateWithHeight) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Height.Size() + n += 1 + l + sovClient(uint64(l)) + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovClient(uint64(l)) + } + return n +} + +func (m *ClientConsensusStates) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovClient(uint64(l)) + } + if len(m.ConsensusStates) > 0 { + for _, e := range m.ConsensusStates { + l = e.Size() + n += 1 + l + sovClient(uint64(l)) + } + } + return n +} + +func (m *ClientUpdateProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovClient(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovClient(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovClient(uint64(l)) + } + if m.Header != nil { + l = m.Header.Size() + n += 1 + l + sovClient(uint64(l)) + } + return n +} + +func (m *Height) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.RevisionNumber != 0 { + n += 1 + sovClient(uint64(m.RevisionNumber)) + } + if m.RevisionHeight != 0 { + n += 1 + sovClient(uint64(m.RevisionHeight)) + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.AllowedClients) > 0 { + for _, s := range m.AllowedClients { + l = len(s) + n += 1 + l + sovClient(uint64(l)) + } + } + return n +} + +func sovClient(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozClient(x uint64) (n int) { + return sovClient(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *IdentifiedClientState) 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 ErrIntOverflowClient + } + 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: IdentifiedClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IdentifiedClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusStateWithHeight) 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 ErrIntOverflowClient + } + 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: ConsensusStateWithHeight: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusStateWithHeight: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Height.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientConsensusStates) 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 ErrIntOverflowClient + } + 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: ClientConsensusStates: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientConsensusStates: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusStates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsensusStates = append(m.ConsensusStates, ConsensusStateWithHeight{}) + if err := m.ConsensusStates[len(m.ConsensusStates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientUpdateProposal) 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 ErrIntOverflowClient + } + 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: ClientUpdateProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientUpdateProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", 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.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", 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.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthClient + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthClient + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header == nil { + m.Header = &types.Any{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Height) 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 ErrIntOverflowClient + } + 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: Height: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Height: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionNumber", wireType) + } + m.RevisionNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHeight", wireType) + } + m.RevisionHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowClient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowClient + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedClients", 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.AllowedClients = append(m.AllowedClients, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipClient(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthClient + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipClient(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, ErrIntOverflowClient + } + 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, ErrIntOverflowClient + } + 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, ErrIntOverflowClient + } + 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, ErrInvalidLengthClient + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupClient + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthClient + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthClient = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowClient = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupClient = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/core/02-client/types/client_test.go b/x/ibc/core/02-client/types/client_test.go new file mode 100644 index 000000000000..2dfd3967d288 --- /dev/null +++ b/x/ibc/core/02-client/types/client_test.go @@ -0,0 +1,87 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *TypesTestSuite) TestMarshalConsensusStateWithHeight() { + var ( + cswh types.ConsensusStateWithHeight + ) + + testCases := []struct { + name string + malleate func() + }{ + { + "solo machine client", func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 1) + cswh = types.NewConsensusStateWithHeight(types.NewHeight(0, soloMachine.Sequence), soloMachine.ConsensusState()) + }, + }, + { + "tendermint client", func() { + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState := suite.chainA.GetClientState(clientA) + consensusState, ok := suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) + suite.Require().True(ok) + + cswh = types.NewConsensusStateWithHeight(clientState.GetLatestHeight().(types.Height), consensusState) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.malleate() + + cdc := suite.chainA.App.AppCodec() + + // marshal message + bz, err := cdc.MarshalJSON(&cswh) + suite.Require().NoError(err) + + // unmarshal message + newCswh := &types.ConsensusStateWithHeight{} + err = cdc.UnmarshalJSON(bz, newCswh) + suite.Require().NoError(err) + }) + } +} + +func TestValidateClientType(t *testing.T) { + testCases := []struct { + name string + clientType string + expPass bool + }{ + {"valid", "tendermint", true}, + {"valid solomachine", "solomachine-v1", true}, + {"too large", "tenderminttenderminttenderminttenderminttendermintt", false}, + {"too short", "t", false}, + {"blank id", " ", false}, + {"empty id", "", false}, + {"ends with dash", "tendermint-", false}, + } + + for _, tc := range testCases { + + err := types.ValidateClientType(tc.clientType) + + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/core/02-client/types/codec.go b/x/ibc/core/02-client/types/codec.go new file mode 100644 index 000000000000..8d79dcdaa45c --- /dev/null +++ b/x/ibc/core/02-client/types/codec.go @@ -0,0 +1,183 @@ +package types + +import ( + proto "github.com/gogo/protobuf/proto" + + 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/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces registers the client interfaces to protobuf Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "ibc.core.client.v1.ClientState", + (*exported.ClientState)(nil), + ) + registry.RegisterInterface( + "ibc.core.client.v1.ConsensusState", + (*exported.ConsensusState)(nil), + ) + registry.RegisterInterface( + "ibc.core.client.v1.Header", + (*exported.Header)(nil), + ) + registry.RegisterInterface( + "ibc.core.client.v1.Height", + (*exported.Height)(nil), + &Height{}, + ) + registry.RegisterInterface( + "ibc.core.client.v1.Misbehaviour", + (*exported.Misbehaviour)(nil), + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgCreateClient{}, + &MsgUpdateClient{}, + &MsgUpgradeClient{}, + &MsgSubmitMisbehaviour{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +// PackClientState constructs a new Any packed with the given client state value. It returns +// an error if the client state can't be casted to a protobuf message or if the concrete +// implemention is not registered to the protobuf codec. +func PackClientState(clientState exported.ClientState) (*codectypes.Any, error) { + msg, ok := clientState.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", clientState) + } + + anyClientState, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + } + + return anyClientState, nil +} + +// UnpackClientState unpacks an Any into a ClientState. It returns an error if the +// client state can't be unpacked into a ClientState. +func UnpackClientState(any *codectypes.Any) (exported.ClientState, error) { + if any == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + clientState, ok := any.GetCachedValue().(exported.ClientState) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ClientState %T", any) + } + + return clientState, nil +} + +// PackConsensusState constructs a new Any packed with the given consensus state value. It returns +// an error if the consensus state can't be casted to a protobuf message or if the concrete +// implemention is not registered to the protobuf codec. +func PackConsensusState(consensusState exported.ConsensusState) (*codectypes.Any, error) { + msg, ok := consensusState.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", consensusState) + } + + anyConsensusState, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + } + + return anyConsensusState, nil +} + +// MustPackConsensusState calls PackConsensusState and panics on error. +func MustPackConsensusState(consensusState exported.ConsensusState) *codectypes.Any { + anyConsensusState, err := PackConsensusState(consensusState) + if err != nil { + panic(err) + } + + return anyConsensusState +} + +// UnpackConsensusState unpacks an Any into a ConsensusState. It returns an error if the +// consensus state can't be unpacked into a ConsensusState. +func UnpackConsensusState(any *codectypes.Any) (exported.ConsensusState, error) { + if any == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + consensusState, ok := any.GetCachedValue().(exported.ConsensusState) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into ConsensusState %T", any) + } + + return consensusState, nil +} + +// PackHeader constructs a new Any packed with the given header value. It returns +// an error if the header can't be casted to a protobuf message or if the concrete +// implemention is not registered to the protobuf codec. +func PackHeader(header exported.Header) (*codectypes.Any, error) { + msg, ok := header.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", header) + } + + anyHeader, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + } + + return anyHeader, nil +} + +// UnpackHeader unpacks an Any into a Header. It returns an error if the +// consensus state can't be unpacked into a Header. +func UnpackHeader(any *codectypes.Any) (exported.Header, error) { + if any == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + header, ok := any.GetCachedValue().(exported.Header) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Header %T", any) + } + + return header, nil +} + +// PackMisbehaviour constructs a new Any packed with the given misbehaviour value. It returns +// an error if the misbehaviour can't be casted to a protobuf message or if the concrete +// implemention is not registered to the protobuf codec. +func PackMisbehaviour(misbehaviour exported.Misbehaviour) (*codectypes.Any, error) { + msg, ok := misbehaviour.(proto.Message) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", misbehaviour) + } + + anyMisbhaviour, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, err.Error()) + } + + return anyMisbhaviour, nil +} + +// UnpackMisbehaviour unpacks an Any into a Misbehaviour. It returns an error if the +// Any can't be unpacked into a Misbehaviour. +func UnpackMisbehaviour(any *codectypes.Any) (exported.Misbehaviour, error) { + if any == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + misbehaviour, ok := any.GetCachedValue().(exported.Misbehaviour) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnpackAny, "cannot unpack Any into Misbehaviour %T", any) + } + + return misbehaviour, nil +} diff --git a/x/ibc/core/02-client/types/codec_test.go b/x/ibc/core/02-client/types/codec_test.go new file mode 100644 index 000000000000..75cfc97eb08f --- /dev/null +++ b/x/ibc/core/02-client/types/codec_test.go @@ -0,0 +1,210 @@ +package types_test + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "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/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type caseAny struct { + name string + any *codectypes.Any + expPass bool +} + +func (suite *TypesTestSuite) TestPackClientState() { + + testCases := []struct { + name string + clientState exported.ClientState + expPass bool + }{ + { + "solo machine client", + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ClientState(), + true, + }, + { + "tendermint client", + ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + true, + }, + { + "localhost client", + localhosttypes.NewClientState(chainID, clientHeight), + true, + }, + { + "nil", + nil, + false, + }, + } + + testCasesAny := []caseAny{} + + for _, tc := range testCases { + clientAny, err := types.PackClientState(tc.clientState) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + + testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + } + + for i, tc := range testCasesAny { + cs, err := types.UnpackClientState(tc.any) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(testCases[i].clientState, cs, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TypesTestSuite) TestPackConsensusState() { + testCases := []struct { + name string + consensusState exported.ConsensusState + expPass bool + }{ + { + "solo machine consensus", + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), + true, + }, + { + "tendermint consensus", + suite.chainA.LastHeader.ConsensusState(), + true, + }, + { + "nil", + nil, + false, + }, + } + + testCasesAny := []caseAny{} + + for _, tc := range testCases { + clientAny, err := types.PackConsensusState(tc.consensusState) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + } + + for i, tc := range testCasesAny { + cs, err := types.UnpackConsensusState(tc.any) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(testCases[i].consensusState, cs, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TypesTestSuite) TestPackHeader() { + testCases := []struct { + name string + header exported.Header + expPass bool + }{ + { + "solo machine header", + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateHeader(), + true, + }, + { + "tendermint header", + suite.chainA.LastHeader, + true, + }, + { + "nil", + nil, + false, + }, + } + + testCasesAny := []caseAny{} + + for _, tc := range testCases { + clientAny, err := types.PackHeader(tc.header) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + + testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + } + + for i, tc := range testCasesAny { + cs, err := types.UnpackHeader(tc.any) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(testCases[i].header, cs, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TypesTestSuite) TestPackMisbehaviour() { + testCases := []struct { + name string + misbehaviour exported.Misbehaviour + expPass bool + }{ + { + "solo machine misbehaviour", + ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour(), + true, + }, + { + "tendermint misbehaviour", + ibctmtypes.NewMisbehaviour("tendermint", suite.chainA.LastHeader, suite.chainA.LastHeader), + true, + }, + { + "nil", + nil, + false, + }, + } + + testCasesAny := []caseAny{} + + for _, tc := range testCases { + clientAny, err := types.PackMisbehaviour(tc.misbehaviour) + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + + testCasesAny = append(testCasesAny, caseAny{tc.name, clientAny, tc.expPass}) + } + + for i, tc := range testCasesAny { + cs, err := types.UnpackMisbehaviour(tc.any) + if tc.expPass { + suite.Require().NoError(err, tc.name) + suite.Require().Equal(testCases[i].misbehaviour, cs, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} diff --git a/x/ibc/core/02-client/types/encoding.go b/x/ibc/core/02-client/types/encoding.go new file mode 100644 index 000000000000..8621484563c4 --- /dev/null +++ b/x/ibc/core/02-client/types/encoding.go @@ -0,0 +1,113 @@ +package types + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// MustUnmarshalClientState attempts to decode and return an ClientState object from +// raw encoded bytes. It panics on error. +func MustUnmarshalClientState(cdc codec.BinaryMarshaler, bz []byte) exported.ClientState { + clientState, err := UnmarshalClientState(cdc, bz) + if err != nil { + panic(fmt.Errorf("failed to decode client state: %w", err)) + } + + return clientState +} + +// MustMarshalClientState attempts to encode an ClientState object and returns the +// raw encoded bytes. It panics on error. +func MustMarshalClientState(cdc codec.BinaryMarshaler, clientState exported.ClientState) []byte { + bz, err := MarshalClientState(cdc, clientState) + if err != nil { + panic(fmt.Errorf("failed to encode client state: %w", err)) + } + + return bz +} + +// MarshalClientState protobuf serializes an ClientState interface +func MarshalClientState(cdc codec.BinaryMarshaler, clientStateI exported.ClientState) ([]byte, error) { + return cdc.MarshalInterface(clientStateI) +} + +// UnmarshalClientState returns an ClientState interface from raw encoded clientState +// bytes of a Proto-based ClientState type. An error is returned upon decoding +// failure. +func UnmarshalClientState(cdc codec.BinaryMarshaler, bz []byte) (exported.ClientState, error) { + var clientState exported.ClientState + if err := cdc.UnmarshalInterface(bz, &clientState); err != nil { + return nil, err + } + + return clientState, nil +} + +// MustUnmarshalConsensusState attempts to decode and return an ConsensusState object from +// raw encoded bytes. It panics on error. +func MustUnmarshalConsensusState(cdc codec.BinaryMarshaler, bz []byte) exported.ConsensusState { + consensusState, err := UnmarshalConsensusState(cdc, bz) + if err != nil { + panic(fmt.Errorf("failed to decode consensus state: %w", err)) + } + + return consensusState +} + +// MustMarshalConsensusState attempts to encode a ConsensusState object and returns the +// raw encoded bytes. It panics on error. +func MustMarshalConsensusState(cdc codec.BinaryMarshaler, consensusState exported.ConsensusState) []byte { + bz, err := MarshalConsensusState(cdc, consensusState) + if err != nil { + panic(fmt.Errorf("failed to encode consensus state: %w", err)) + } + + return bz +} + +// MarshalConsensusState protobuf serializes a ConsensusState interface +func MarshalConsensusState(cdc codec.BinaryMarshaler, cs exported.ConsensusState) ([]byte, error) { + return cdc.MarshalInterface(cs) +} + +// UnmarshalConsensusState returns a ConsensusState interface from raw encoded consensus state +// bytes of a Proto-based ConsensusState type. An error is returned upon decoding +// failure. +func UnmarshalConsensusState(cdc codec.BinaryMarshaler, bz []byte) (exported.ConsensusState, error) { + var consensusState exported.ConsensusState + if err := cdc.UnmarshalInterface(bz, &consensusState); err != nil { + return nil, err + } + + return consensusState, nil +} + +// MarshalHeader protobuf serializes a Header interface +func MarshalHeader(cdc codec.BinaryMarshaler, h exported.Header) ([]byte, error) { + return cdc.MarshalInterface(h) +} + +// MustMarshalHeader attempts to encode a Header object and returns the +// raw encoded bytes. It panics on error. +func MustMarshalHeader(cdc codec.BinaryMarshaler, header exported.Header) []byte { + bz, err := MarshalHeader(cdc, header) + if err != nil { + panic(fmt.Errorf("failed to encode header: %w", err)) + } + + return bz +} + +// UnmarshalHeader returns a Header interface from raw proto encoded header bytes. +// An error is returned upon decoding failure. +func UnmarshalHeader(cdc codec.BinaryMarshaler, bz []byte) (exported.Header, error) { + var header exported.Header + if err := cdc.UnmarshalInterface(bz, &header); err != nil { + return nil, err + } + + return header, nil +} diff --git a/x/ibc/core/02-client/types/encoding_test.go b/x/ibc/core/02-client/types/encoding_test.go new file mode 100644 index 000000000000..89953bc9f1d4 --- /dev/null +++ b/x/ibc/core/02-client/types/encoding_test.go @@ -0,0 +1,30 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +func (suite *TypesTestSuite) TestMarshalHeader() { + + cdc := suite.chainA.App.AppCodec() + h := &ibctmtypes.Header{ + TrustedHeight: types.NewHeight(4, 100), + } + + // marshal header + bz, err := types.MarshalHeader(cdc, h) + suite.Require().NoError(err) + + // unmarshal header + newHeader, err := types.UnmarshalHeader(cdc, bz) + suite.Require().NoError(err) + + suite.Require().Equal(h, newHeader) + + // use invalid bytes + invalidHeader, err := types.UnmarshalHeader(cdc, []byte("invalid bytes")) + suite.Require().Error(err) + suite.Require().Nil(invalidHeader) + +} diff --git a/x/ibc/core/02-client/types/errors.go b/x/ibc/core/02-client/types/errors.go new file mode 100644 index 000000000000..09dc92959a2b --- /dev/null +++ b/x/ibc/core/02-client/types/errors.go @@ -0,0 +1,33 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBC client sentinel errors +var ( + ErrClientExists = sdkerrors.Register(SubModuleName, 2, "light client already exists") + ErrInvalidClient = sdkerrors.Register(SubModuleName, 3, "light client is invalid") + ErrClientNotFound = sdkerrors.Register(SubModuleName, 4, "light client not found") + ErrClientFrozen = sdkerrors.Register(SubModuleName, 5, "light client is frozen due to misbehaviour") + ErrInvalidClientMetadata = sdkerrors.Register(SubModuleName, 6, "invalid client metadata") + ErrConsensusStateNotFound = sdkerrors.Register(SubModuleName, 7, "consensus state not found") + ErrInvalidConsensus = sdkerrors.Register(SubModuleName, 8, "invalid consensus state") + ErrClientTypeNotFound = sdkerrors.Register(SubModuleName, 9, "client type not found") + ErrInvalidClientType = sdkerrors.Register(SubModuleName, 10, "invalid client type") + ErrRootNotFound = sdkerrors.Register(SubModuleName, 11, "commitment root not found") + ErrInvalidHeader = sdkerrors.Register(SubModuleName, 12, "invalid client header") + ErrInvalidMisbehaviour = sdkerrors.Register(SubModuleName, 13, "invalid light client misbehaviour") + ErrFailedClientStateVerification = sdkerrors.Register(SubModuleName, 14, "client state verification failed") + ErrFailedClientConsensusStateVerification = sdkerrors.Register(SubModuleName, 15, "client consensus state verification failed") + ErrFailedConnectionStateVerification = sdkerrors.Register(SubModuleName, 16, "connection state verification failed") + ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 17, "channel state verification failed") + ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 18, "packet commitment verification failed") + ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 19, "packet acknowledgement verification failed") + ErrFailedPacketReceiptVerification = sdkerrors.Register(SubModuleName, 20, "packet receipt verification failed") + ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 21, "next sequence receive verification failed") + ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 22, "self consensus state not found") + 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") +) diff --git a/x/ibc/core/02-client/types/events.go b/x/ibc/core/02-client/types/events.go new file mode 100644 index 000000000000..7a9bae220576 --- /dev/null +++ b/x/ibc/core/02-client/types/events.go @@ -0,0 +1,26 @@ +package types + +import ( + "fmt" + + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// IBC client events +const ( + AttributeKeyClientID = "client_id" + AttributeKeyClientType = "client_type" + AttributeKeyConsensusHeight = "consensus_height" + AttributeKeyHeader = "header" +) + +// IBC client events vars +var ( + EventTypeCreateClient = "create_client" + EventTypeUpdateClient = "update_client" + EventTypeUpgradeClient = "upgrade_client" + EventTypeSubmitMisbehaviour = "client_misbehaviour" + EventTypeUpdateClientProposal = "update_client_proposal" + + AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) +) diff --git a/x/ibc/core/02-client/types/expected_keepers.go b/x/ibc/core/02-client/types/expected_keepers.go new file mode 100644 index 000000000000..defc81506b1d --- /dev/null +++ b/x/ibc/core/02-client/types/expected_keepers.go @@ -0,0 +1,14 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// StakingKeeper expected staking keeper +type StakingKeeper interface { + GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) + UnbondingTime(ctx sdk.Context) time.Duration +} diff --git a/x/ibc/core/02-client/types/genesis.go b/x/ibc/core/02-client/types/genesis.go new file mode 100644 index 000000000000..3f197208e33a --- /dev/null +++ b/x/ibc/core/02-client/types/genesis.go @@ -0,0 +1,250 @@ +package types + +import ( + "fmt" + "sort" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + _ codectypes.UnpackInterfacesMessage = IdentifiedClientState{} + _ codectypes.UnpackInterfacesMessage = ClientsConsensusStates{} + _ codectypes.UnpackInterfacesMessage = ClientConsensusStates{} + _ codectypes.UnpackInterfacesMessage = GenesisState{} +) + +var ( + _ sort.Interface = ClientsConsensusStates{} + _ exported.GenesisMetadata = GenesisMetadata{} +) + +// ClientsConsensusStates defines a slice of ClientConsensusStates that supports the sort interface +type ClientsConsensusStates []ClientConsensusStates + +// Len implements sort.Interface +func (ccs ClientsConsensusStates) Len() int { return len(ccs) } + +// Less implements sort.Interface +func (ccs ClientsConsensusStates) Less(i, j int) bool { return ccs[i].ClientId < ccs[j].ClientId } + +// Swap implements sort.Interface +func (ccs ClientsConsensusStates) Swap(i, j int) { ccs[i], ccs[j] = ccs[j], ccs[i] } + +// Sort is a helper function to sort the set of ClientsConsensusStates in place +func (ccs ClientsConsensusStates) Sort() ClientsConsensusStates { + sort.Sort(ccs) + return ccs +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (ccs ClientsConsensusStates) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, clientConsensus := range ccs { + if err := clientConsensus.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + +// NewClientConsensusStates creates a new ClientConsensusStates instance. +func NewClientConsensusStates(clientID string, consensusStates []ConsensusStateWithHeight) ClientConsensusStates { + return ClientConsensusStates{ + ClientId: clientID, + ConsensusStates: consensusStates, + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (ccs ClientConsensusStates) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, consStateWithHeight := range ccs.ConsensusStates { + if err := consStateWithHeight.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + +// NewGenesisState creates a GenesisState instance. +func NewGenesisState( + clients []IdentifiedClientState, clientsConsensus ClientsConsensusStates, clientsMetadata []IdentifiedGenesisMetadata, + params Params, createLocalhost bool, nextClientSequence uint64, +) GenesisState { + return GenesisState{ + Clients: clients, + ClientsConsensus: clientsConsensus, + ClientsMetadata: clientsMetadata, + Params: params, + CreateLocalhost: createLocalhost, + NextClientSequence: nextClientSequence, + } +} + +// DefaultGenesisState returns the ibc client submodule's default genesis state. +func DefaultGenesisState() GenesisState { + return GenesisState{ + Clients: []IdentifiedClientState{}, + ClientsConsensus: ClientsConsensusStates{}, + Params: DefaultParams(), + CreateLocalhost: false, + NextClientSequence: 0, + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (gs GenesisState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, client := range gs.Clients { + if err := client.UnpackInterfaces(unpacker); err != nil { + return err + } + } + + return gs.ClientsConsensus.UnpackInterfaces(unpacker) +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + // keep track of the max sequence to ensure it is less than + // the next sequence used in creating client identifers. + var maxSequence uint64 = 0 + + if err := gs.Params.Validate(); err != nil { + return err + } + + validClients := make(map[string]string) + + for i, client := range gs.Clients { + if err := host.ClientIdentifierValidator(client.ClientId); err != nil { + return fmt.Errorf("invalid client consensus state identifier %s index %d: %w", client.ClientId, i, err) + } + + clientState, ok := client.ClientState.GetCachedValue().(exported.ClientState) + if !ok { + return fmt.Errorf("invalid client state with ID %s", client.ClientId) + } + + if !gs.Params.IsAllowedClient(clientState.ClientType()) { + return fmt.Errorf("client type %s not allowed by genesis params", clientState.ClientType()) + } + if err := clientState.Validate(); err != nil { + return fmt.Errorf("invalid client %v index %d: %w", client, i, err) + } + + clientType, sequence, err := ParseClientIdentifier(client.ClientId) + if err != nil { + return err + } + + if clientType != clientState.ClientType() { + return fmt.Errorf("client state type %s does not equal client type in client identifier %s", clientState.ClientType(), clientType) + } + + if err := ValidateClientType(clientType); err != nil { + return err + } + + if sequence > maxSequence { + maxSequence = sequence + } + + // add client id to validClients map + validClients[client.ClientId] = clientState.ClientType() + } + + for _, cc := range gs.ClientsConsensus { + // check that consensus state is for a client in the genesis clients list + clientType, ok := validClients[cc.ClientId] + if !ok { + return fmt.Errorf("consensus state in genesis has a client id %s that does not map to a genesis client", cc.ClientId) + } + + for i, consensusState := range cc.ConsensusStates { + if consensusState.Height.IsZero() { + return fmt.Errorf("consensus state height cannot be zero") + } + + cs, ok := consensusState.ConsensusState.GetCachedValue().(exported.ConsensusState) + if !ok { + return fmt.Errorf("invalid consensus state with client ID %s at height %s", cc.ClientId, consensusState.Height) + } + + if err := cs.ValidateBasic(); err != nil { + return fmt.Errorf("invalid client consensus state %v clientID %s index %d: %w", cs, cc.ClientId, i, err) + } + + // ensure consensus state type matches client state type + if clientType != cs.ClientType() { + return fmt.Errorf("consensus state client type %s does not equal client state client type %s", cs.ClientType(), clientType) + } + + } + } + + for _, clientMetadata := range gs.ClientsMetadata { + // check that metadata is for a client in the genesis clients list + _, ok := validClients[clientMetadata.ClientId] + if !ok { + return fmt.Errorf("metadata in genesis has a client id %s that does not map to a genesis client", clientMetadata.ClientId) + } + + for i, gm := range clientMetadata.ClientMetadata { + if err := gm.Validate(); err != nil { + return fmt.Errorf("invalid client metadata %v clientID %s index %d: %w", gm, clientMetadata.ClientId, i, err) + } + + } + + } + + if gs.CreateLocalhost && !gs.Params.IsAllowedClient(exported.Localhost) { + return fmt.Errorf("localhost client is not registered on the allowlist") + } + + if maxSequence != 0 && maxSequence >= gs.NextClientSequence { + return fmt.Errorf("next client identifier sequence %d must be greater than the maximum sequence used in the provided client identifiers %d", gs.NextClientSequence, maxSequence) + } + + return nil +} + +// NewGenesisMetadata is a constructor for GenesisMetadata +func NewGenesisMetadata(key, val []byte) GenesisMetadata { + return GenesisMetadata{ + Key: key, + Value: val, + } +} + +// GetKey returns the key of metadata. Implements exported.GenesisMetadata interface. +func (gm GenesisMetadata) GetKey() []byte { + return gm.Key +} + +// GetValue returns the value of metadata. Implements exported.GenesisMetadata interface. +func (gm GenesisMetadata) GetValue() []byte { + return gm.Value +} + +// Validate ensures key and value of metadata are not empty +func (gm GenesisMetadata) Validate() error { + if len(gm.Key) == 0 { + return fmt.Errorf("genesis metadata key cannot be empty") + } + if len(gm.Value) == 0 { + return fmt.Errorf("genesis metadata value cannot be empty") + } + return nil +} + +// NewIdentifiedGenesisMetadata takes in a client ID and list of genesis metadata for that client +// and constructs a new IdentifiedGenesisMetadata. +func NewIdentifiedGenesisMetadata(clientID string, gms []GenesisMetadata) IdentifiedGenesisMetadata { + return IdentifiedGenesisMetadata{ + ClientId: clientID, + ClientMetadata: gms, + } +} diff --git a/x/ibc/core/02-client/types/genesis.pb.go b/x/ibc/core/02-client/types/genesis.pb.go new file mode 100644 index 000000000000..a8253c343db0 --- /dev/null +++ b/x/ibc/core/02-client/types/genesis.pb.go @@ -0,0 +1,1057 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/client/v1/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 defines the ibc client submodule's genesis state. +type GenesisState struct { + // client states with their corresponding identifiers + Clients IdentifiedClientStates `protobuf:"bytes,1,rep,name=clients,proto3,castrepeated=IdentifiedClientStates" json:"clients"` + // consensus states from each client + ClientsConsensus ClientsConsensusStates `protobuf:"bytes,2,rep,name=clients_consensus,json=clientsConsensus,proto3,castrepeated=ClientsConsensusStates" json:"clients_consensus" yaml:"clients_consensus"` + // metadata from each client + ClientsMetadata []IdentifiedGenesisMetadata `protobuf:"bytes,3,rep,name=clients_metadata,json=clientsMetadata,proto3" json:"clients_metadata" yaml:"clients_metadata"` + Params Params `protobuf:"bytes,4,opt,name=params,proto3" json:"params"` + // create localhost on initialization + CreateLocalhost bool `protobuf:"varint,5,opt,name=create_localhost,json=createLocalhost,proto3" json:"create_localhost,omitempty" yaml:"create_localhost"` + // the sequence for the next generated client identifier + NextClientSequence uint64 `protobuf:"varint,6,opt,name=next_client_sequence,json=nextClientSequence,proto3" json:"next_client_sequence,omitempty" yaml:"next_client_sequence"` +} + +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_bcd0c0f1f2e6a91a, []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) GetClients() IdentifiedClientStates { + if m != nil { + return m.Clients + } + return nil +} + +func (m *GenesisState) GetClientsConsensus() ClientsConsensusStates { + if m != nil { + return m.ClientsConsensus + } + return nil +} + +func (m *GenesisState) GetClientsMetadata() []IdentifiedGenesisMetadata { + if m != nil { + return m.ClientsMetadata + } + return nil +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetCreateLocalhost() bool { + if m != nil { + return m.CreateLocalhost + } + return false +} + +func (m *GenesisState) GetNextClientSequence() uint64 { + if m != nil { + return m.NextClientSequence + } + return 0 +} + +// GenesisMetadata defines the genesis type for metadata that clients may return +// with ExportMetadata +type GenesisMetadata struct { + // store key of metadata without clientID-prefix + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // metadata value + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *GenesisMetadata) Reset() { *m = GenesisMetadata{} } +func (m *GenesisMetadata) String() string { return proto.CompactTextString(m) } +func (*GenesisMetadata) ProtoMessage() {} +func (*GenesisMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_bcd0c0f1f2e6a91a, []int{1} +} +func (m *GenesisMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisMetadata.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 *GenesisMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisMetadata.Merge(m, src) +} +func (m *GenesisMetadata) XXX_Size() int { + return m.Size() +} +func (m *GenesisMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisMetadata proto.InternalMessageInfo + +// IdentifiedGenesisMetadata has the client metadata with the corresponding client id. +type IdentifiedGenesisMetadata struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + ClientMetadata []GenesisMetadata `protobuf:"bytes,2,rep,name=client_metadata,json=clientMetadata,proto3" json:"client_metadata" yaml:"client_metadata"` +} + +func (m *IdentifiedGenesisMetadata) Reset() { *m = IdentifiedGenesisMetadata{} } +func (m *IdentifiedGenesisMetadata) String() string { return proto.CompactTextString(m) } +func (*IdentifiedGenesisMetadata) ProtoMessage() {} +func (*IdentifiedGenesisMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_bcd0c0f1f2e6a91a, []int{2} +} +func (m *IdentifiedGenesisMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IdentifiedGenesisMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IdentifiedGenesisMetadata.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 *IdentifiedGenesisMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_IdentifiedGenesisMetadata.Merge(m, src) +} +func (m *IdentifiedGenesisMetadata) XXX_Size() int { + return m.Size() +} +func (m *IdentifiedGenesisMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_IdentifiedGenesisMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_IdentifiedGenesisMetadata proto.InternalMessageInfo + +func (m *IdentifiedGenesisMetadata) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *IdentifiedGenesisMetadata) GetClientMetadata() []GenesisMetadata { + if m != nil { + return m.ClientMetadata + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.core.client.v1.GenesisState") + proto.RegisterType((*GenesisMetadata)(nil), "ibc.core.client.v1.GenesisMetadata") + proto.RegisterType((*IdentifiedGenesisMetadata)(nil), "ibc.core.client.v1.IdentifiedGenesisMetadata") +} + +func init() { proto.RegisterFile("ibc/core/client/v1/genesis.proto", fileDescriptor_bcd0c0f1f2e6a91a) } + +var fileDescriptor_bcd0c0f1f2e6a91a = []byte{ + // 535 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0x4d, 0x6e, 0xd3, 0x40, + 0x14, 0xce, 0x34, 0x69, 0x68, 0xa7, 0x15, 0x0d, 0xa3, 0xa8, 0x98, 0x54, 0xb2, 0x2d, 0xb3, 0x09, + 0x8b, 0xd8, 0x24, 0x2c, 0x40, 0xd9, 0x20, 0xb9, 0x12, 0xa8, 0x12, 0x48, 0xd4, 0xec, 0xd8, 0x58, + 0x93, 0xf1, 0x90, 0x5a, 0x75, 0x3c, 0x21, 0x33, 0x89, 0x9a, 0x1b, 0xb0, 0x44, 0x9c, 0x80, 0x35, + 0x67, 0xe0, 0x00, 0x5d, 0x76, 0xd9, 0x55, 0x40, 0xc9, 0x0d, 0x72, 0x02, 0xe4, 0x99, 0x71, 0x7f, + 0x5c, 0xb7, 0xab, 0xbc, 0x7c, 0xf3, 0x7d, 0xdf, 0x7b, 0xfa, 0x9e, 0x1f, 0xb4, 0xe3, 0x01, 0xf1, + 0x08, 0x9b, 0x50, 0x8f, 0x24, 0x31, 0x4d, 0x85, 0x37, 0xeb, 0x7a, 0x43, 0x9a, 0x52, 0x1e, 0x73, + 0x77, 0x3c, 0x61, 0x82, 0x21, 0x14, 0x0f, 0x88, 0x9b, 0x31, 0x5c, 0xc5, 0x70, 0x67, 0xdd, 0x96, + 0x55, 0xa2, 0xd2, 0xaf, 0x52, 0xd4, 0x6a, 0x0e, 0xd9, 0x90, 0xc9, 0xd2, 0xcb, 0x2a, 0x85, 0x3a, + 0x97, 0x35, 0xb8, 0xfb, 0x5e, 0x99, 0x7f, 0x16, 0x58, 0x50, 0x44, 0xe0, 0x23, 0x25, 0xe3, 0x06, + 0xb0, 0xab, 0xed, 0x9d, 0xde, 0x0b, 0xf7, 0x6e, 0x37, 0xf7, 0x28, 0xa2, 0xa9, 0x88, 0xbf, 0xc6, + 0x34, 0x3a, 0x94, 0x98, 0xd4, 0xfa, 0xe6, 0xf9, 0xc2, 0xaa, 0xfc, 0xfe, 0x6b, 0xed, 0x97, 0x3e, + 0xf3, 0x20, 0x77, 0x46, 0x3f, 0x01, 0x7c, 0xa2, 0xeb, 0x90, 0xb0, 0x94, 0xd3, 0x94, 0x4f, 0xb9, + 0xb1, 0x71, 0x7f, 0x3f, 0x65, 0x73, 0x98, 0x53, 0x95, 0x9f, 0xdf, 0xcf, 0xfa, 0xad, 0x17, 0x96, + 0x31, 0xc7, 0xa3, 0xa4, 0xef, 0xdc, 0x71, 0x74, 0xb2, 0x59, 0x94, 0x94, 0x17, 0xb4, 0x41, 0x83, + 0x14, 0x70, 0x34, 0x87, 0x39, 0x16, 0x8e, 0xa8, 0xc0, 0x11, 0x16, 0xd8, 0xa8, 0xca, 0x91, 0x3a, + 0x0f, 0x47, 0xa0, 0xf3, 0xfb, 0xa8, 0x45, 0xbe, 0xa5, 0xc7, 0x7a, 0x7a, 0x7b, 0xac, 0xdc, 0xd4, + 0x09, 0xf6, 0x34, 0x94, 0x2b, 0xd0, 0x1b, 0x58, 0x1f, 0xe3, 0x09, 0x1e, 0x71, 0xa3, 0x66, 0x83, + 0xf6, 0x4e, 0xaf, 0x55, 0xd6, 0xf0, 0x93, 0x64, 0xf8, 0xb5, 0xcc, 0x3d, 0xd0, 0x7c, 0xf4, 0x0e, + 0x36, 0xc8, 0x84, 0x62, 0x41, 0xc3, 0x84, 0x11, 0x9c, 0x9c, 0x30, 0x2e, 0x8c, 0x4d, 0x1b, 0xb4, + 0xb7, 0xfc, 0x83, 0x1b, 0x13, 0x14, 0x18, 0xd9, 0x04, 0x12, 0xfa, 0x90, 0x23, 0xe8, 0x18, 0x36, + 0x53, 0x7a, 0x26, 0x42, 0xd5, 0x2e, 0xe4, 0xf4, 0xdb, 0x94, 0xa6, 0x84, 0x1a, 0x75, 0x1b, 0xb4, + 0x6b, 0xbe, 0xb5, 0x5e, 0x58, 0x07, 0xca, 0xab, 0x8c, 0xe5, 0x04, 0x28, 0x83, 0xf5, 0xae, 0x73, + 0xf0, 0x2d, 0xdc, 0x2b, 0x24, 0x83, 0x1a, 0xb0, 0x7a, 0x4a, 0xe7, 0x06, 0xb0, 0x41, 0x7b, 0x37, + 0xc8, 0x4a, 0xd4, 0x84, 0x9b, 0x33, 0x9c, 0x4c, 0xa9, 0xb1, 0x21, 0x31, 0xf5, 0xa7, 0x5f, 0xfb, + 0xfe, 0xcb, 0xaa, 0x38, 0x7f, 0x00, 0x7c, 0x76, 0x6f, 0xca, 0xa8, 0x0b, 0xb7, 0xf5, 0x18, 0x71, + 0x24, 0x1d, 0xb7, 0xfd, 0xe6, 0x7a, 0x61, 0x35, 0x6e, 0x86, 0x1e, 0xc6, 0x91, 0x13, 0x6c, 0xa9, + 0xfa, 0x28, 0x42, 0x09, 0xd4, 0xc9, 0x5f, 0x2f, 0x58, 0x7d, 0x73, 0xcf, 0xcb, 0xf2, 0x2e, 0xae, + 0xd5, 0xd4, 0x6b, 0xdd, 0xbf, 0xd5, 0xe1, 0x7a, 0xab, 0x8f, 0x15, 0x72, 0xc5, 0x3f, 0x3e, 0x5f, + 0x9a, 0xe0, 0x62, 0x69, 0x82, 0x7f, 0x4b, 0x13, 0xfc, 0x58, 0x99, 0x95, 0x8b, 0x95, 0x59, 0xb9, + 0x5c, 0x99, 0x95, 0x2f, 0xaf, 0x87, 0xb1, 0x38, 0x99, 0x0e, 0x5c, 0xc2, 0x46, 0x1e, 0x61, 0x7c, + 0xc4, 0xb8, 0xfe, 0xe9, 0xf0, 0xe8, 0xd4, 0x3b, 0xf3, 0xae, 0x4e, 0xf9, 0x65, 0xaf, 0xa3, 0xaf, + 0x59, 0xcc, 0xc7, 0x94, 0x0f, 0xea, 0xf2, 0x68, 0x5f, 0xfd, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x61, + 0x6f, 0x94, 0xed, 0x23, 0x04, 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 m.NextClientSequence != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.NextClientSequence)) + i-- + dAtA[i] = 0x30 + } + if m.CreateLocalhost { + i-- + if m.CreateLocalhost { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ClientsMetadata) > 0 { + for iNdEx := len(m.ClientsMetadata) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ClientsMetadata[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.ClientsConsensus) > 0 { + for iNdEx := len(m.ClientsConsensus) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ClientsConsensus[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Clients) > 0 { + for iNdEx := len(m.Clients) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Clients[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 *GenesisMetadata) 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 *GenesisMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IdentifiedGenesisMetadata) 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 *IdentifiedGenesisMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IdentifiedGenesisMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientMetadata) > 0 { + for iNdEx := len(m.ClientMetadata) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ClientMetadata[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ClientId))) + 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.Clients) > 0 { + for _, e := range m.Clients { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ClientsConsensus) > 0 { + for _, e := range m.ClientsConsensus { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ClientsMetadata) > 0 { + for _, e := range m.ClientsMetadata { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if m.CreateLocalhost { + n += 2 + } + if m.NextClientSequence != 0 { + n += 1 + sovGenesis(uint64(m.NextClientSequence)) + } + return n +} + +func (m *GenesisMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + return n +} + +func (m *IdentifiedGenesisMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.ClientMetadata) > 0 { + for _, e := range m.ClientMetadata { + 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 Clients", 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.Clients = append(m.Clients, IdentifiedClientState{}) + if err := m.Clients[len(m.Clients)-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 ClientsConsensus", 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.ClientsConsensus = append(m.ClientsConsensus, ClientConsensusStates{}) + if err := m.ClientsConsensus[len(m.ClientsConsensus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientsMetadata", 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.ClientsMetadata = append(m.ClientsMetadata, IdentifiedGenesisMetadata{}) + if err := m.ClientsMetadata[len(m.ClientsMetadata)-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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateLocalhost", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CreateLocalhost = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextClientSequence", wireType) + } + m.NextClientSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextClientSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *GenesisMetadata) 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: GenesisMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisMetadata: 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 ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + 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 *IdentifiedGenesisMetadata) 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: IdentifiedGenesisMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IdentifiedGenesisMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientMetadata", 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.ClientMetadata = append(m.ClientMetadata, GenesisMetadata{}) + if err := m.ClientMetadata[len(m.ClientMetadata)-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/ibc/core/02-client/types/genesis_test.go b/x/ibc/core/02-client/types/genesis_test.go new file mode 100644 index 000000000000..d57b8d1ba532 --- /dev/null +++ b/x/ibc/core/02-client/types/genesis_test.go @@ -0,0 +1,549 @@ +package types_test + +import ( + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + client "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +const ( + chainID = "chainID" + tmClientID0 = "07-tendermint-0" + tmClientID1 = "07-tendermint-1" + invalidClientID = "myclient-0" + clientID = tmClientID0 + + height = 10 +) + +var clientHeight = types.NewHeight(0, 10) + +func (suite *TypesTestSuite) TestMarshalGenesisState() { + cdc := suite.chainA.App.AppCodec() + clientA, _, _, _, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + genesis := client.ExportGenesis(suite.chainA.GetContext(), suite.chainA.App.IBCKeeper.ClientKeeper) + + bz, err := cdc.MarshalJSON(&genesis) + suite.Require().NoError(err) + suite.Require().NotNil(bz) + + var gs types.GenesisState + err = cdc.UnmarshalJSON(bz, &gs) + suite.Require().NoError(err) +} + +func (suite *TypesTestSuite) TestValidateGenesis() { + privVal := ibctestingmock.NewPV() + pubKey, err := privVal.GetPubKey() + suite.Require().NoError(err) + + now := time.Now().UTC() + + val := tmtypes.NewValidator(pubKey, 10) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + + heightMinus1 := types.NewHeight(0, height-1) + header := suite.chainA.CreateTMClientHeader(chainID, int64(clientHeight.RevisionHeight), heightMinus1, now, valSet, valSet, []tmtypes.PrivValidator{privVal}) + + testCases := []struct { + name string + genState types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + name: "valid custom genesis", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + []types.IdentifiedGenesisMetadata{ + types.NewIdentifiedGenesisMetadata( + clientID, + []types.GenesisMetadata{ + types.NewGenesisMetadata([]byte("key1"), []byte("val1")), + types.NewGenesisMetadata([]byte("key2"), []byte("val2")), + }, + ), + }, + types.NewParams(exported.Tendermint, exported.Localhost), + false, + 2, + ), + expPass: true, + }, + { + name: "invalid clientid", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + invalidClientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + invalidClientID, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid client", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState(exported.Localhost, localhosttypes.NewClientState("chaindID", types.ZeroHeight())), + }, + nil, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "consensus state client id does not match client id in genesis clients", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID1, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + types.NewHeight(0, 1), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid consensus state height", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + types.ZeroHeight(), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid consensus state", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + types.NewHeight(0, 1), + ibctmtypes.NewConsensusState( + time.Time{}, commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint), + false, + 0, + ), + expPass: false, + }, + { + name: "client in genesis clients is disallowed by params", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Solomachine), + false, + 0, + ), + expPass: false, + }, + { + name: "metadata client-id does not match a genesis client", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + clientID, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + clientID, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + []types.IdentifiedGenesisMetadata{ + types.NewIdentifiedGenesisMetadata( + "wrongclientid", + []types.GenesisMetadata{ + types.NewGenesisMetadata([]byte("key1"), []byte("val1")), + types.NewGenesisMetadata([]byte("key2"), []byte("val2")), + }, + ), + }, + types.NewParams(exported.Tendermint, exported.Localhost), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid metadata", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + clientID, ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + clientID, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + []types.IdentifiedGenesisMetadata{ + types.NewIdentifiedGenesisMetadata( + clientID, + []types.GenesisMetadata{ + types.NewGenesisMetadata([]byte(""), []byte("val1")), + types.NewGenesisMetadata([]byte("key2"), []byte("val2")), + }, + ), + }, + types.NewParams(exported.Tendermint), + false, + 0, + ), + }, + { + name: "invalid params", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(" "), + false, + 0, + ), + expPass: false, + }, + { + name: "invalid param", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(" "), + true, + 0, + ), + expPass: false, + }, + { + name: "localhost client not registered on allowlist", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID1, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost+"-0", localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID1, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint), + true, + 2, + ), + expPass: false, + }, + { + name: "next sequence too small", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + tmClientID0, ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint, exported.Localhost), + false, + 0, + ), + expPass: false, + }, + { + name: "failed to parse client identifier in client state loop", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + "my-client", ibctmtypes.NewClientState(chainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + types.NewIdentifiedClientState( + exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + tmClientID0, + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint, exported.Localhost), + false, + 5, + ), + expPass: false, + }, + { + name: "consensus state different than client state type", + genState: types.NewGenesisState( + []types.IdentifiedClientState{ + types.NewIdentifiedClientState( + exported.Localhost+"-1", localhosttypes.NewClientState("chainID", clientHeight), + ), + }, + []types.ClientConsensusStates{ + types.NewClientConsensusStates( + exported.Localhost+"-1", + []types.ConsensusStateWithHeight{ + types.NewConsensusStateWithHeight( + header.GetHeight().(types.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + nil, + types.NewParams(exported.Tendermint, exported.Localhost), + false, + 5, + ), + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} diff --git a/x/ibc/core/02-client/types/height.go b/x/ibc/core/02-client/types/height.go new file mode 100644 index 000000000000..4216d54e6681 --- /dev/null +++ b/x/ibc/core/02-client/types/height.go @@ -0,0 +1,188 @@ +package types + +import ( + "fmt" + "math/big" + "regexp" + "strconv" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.Height = (*Height)(nil) + +// IsRevisionFormat checks if a chainID is in the format required for parsing revisions +// The chainID must be in the form: `{chainID}-{revision} +// 24-host may enforce stricter checks on chainID +var IsRevisionFormat = regexp.MustCompile(`^.*[^-]-{1}[1-9][0-9]*$`).MatchString + +// ZeroHeight is a helper function which returns an uninitialized height. +func ZeroHeight() Height { + return Height{} +} + +// NewHeight is a constructor for the IBC height type +func NewHeight(revisionNumber, revisionHeight uint64) Height { + return Height{ + RevisionNumber: revisionNumber, + RevisionHeight: revisionHeight, + } +} + +// GetRevisionNumber returns the revision-number of the height +func (h Height) GetRevisionNumber() uint64 { + return h.RevisionNumber +} + +// GetRevisionHeight returns the revision-height of the height +func (h Height) GetRevisionHeight() uint64 { + return h.RevisionHeight +} + +// Compare implements a method to compare two heights. When comparing two heights a, b +// we can call a.Compare(b) which will return +// -1 if a < b +// 0 if a = b +// 1 if a > b +// +// It first compares based on revision numbers, whichever has the higher revision number is the higher height +// If revision number is the same, then the revision height is compared +func (h Height) Compare(other exported.Height) int64 { + height, ok := other.(Height) + if !ok { + panic(fmt.Sprintf("cannot compare against invalid height type: %T. expected height type: %T", other, h)) + } + var a, b big.Int + if h.RevisionNumber != height.RevisionNumber { + a.SetUint64(h.RevisionNumber) + b.SetUint64(height.RevisionNumber) + } else { + a.SetUint64(h.RevisionHeight) + b.SetUint64(height.RevisionHeight) + } + return int64(a.Cmp(&b)) +} + +// LT Helper comparison function returns true if h < other +func (h Height) LT(other exported.Height) bool { + return h.Compare(other) == -1 +} + +// LTE Helper comparison function returns true if h <= other +func (h Height) LTE(other exported.Height) bool { + cmp := h.Compare(other) + return cmp <= 0 +} + +// GT Helper comparison function returns true if h > other +func (h Height) GT(other exported.Height) bool { + return h.Compare(other) == 1 +} + +// GTE Helper comparison function returns true if h >= other +func (h Height) GTE(other exported.Height) bool { + cmp := h.Compare(other) + return cmp >= 0 +} + +// EQ Helper comparison function returns true if h == other +func (h Height) EQ(other exported.Height) bool { + return h.Compare(other) == 0 +} + +// String returns a string representation of Height +func (h Height) String() string { + return fmt.Sprintf("%d-%d", h.RevisionNumber, h.RevisionHeight) +} + +// Decrement will return a new height with the RevisionHeight decremented +// If the RevisionHeight is already at lowest value (1), then false success flag is returend +func (h Height) Decrement() (decremented exported.Height, success bool) { + if h.RevisionHeight == 0 { + return Height{}, false + } + return NewHeight(h.RevisionNumber, h.RevisionHeight-1), true +} + +// Increment will return a height with the same revision number but an +// incremented revision height +func (h Height) Increment() exported.Height { + return NewHeight(h.RevisionNumber, h.RevisionHeight+1) +} + +// IsZero returns true if height revision and revision-height are both 0 +func (h Height) IsZero() bool { + return h.RevisionNumber == 0 && h.RevisionHeight == 0 +} + +// MustParseHeight will attempt to parse a string representation of a height and panic if +// parsing fails. +func MustParseHeight(heightStr string) Height { + height, err := ParseHeight(heightStr) + if err != nil { + panic(err) + } + + return height +} + +// ParseHeight is a utility function that takes a string representation of the height +// and returns a Height struct +func ParseHeight(heightStr string) (Height, error) { + splitStr := strings.Split(heightStr, "-") + if len(splitStr) != 2 { + return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "expected height string format: {revision}-{height}. Got: %s", heightStr) + } + revisionNumber, err := strconv.ParseUint(splitStr[0], 10, 64) + if err != nil { + return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid revision number. parse err: %s", err) + } + revisionHeight, err := strconv.ParseUint(splitStr[1], 10, 64) + if err != nil { + return Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid revision height. parse err: %s", err) + } + return NewHeight(revisionNumber, revisionHeight), nil +} + +// SetRevisionNumber takes a chainID in valid revision format and swaps the revision number +// in the chainID with the given revision number. +func SetRevisionNumber(chainID string, revision uint64) (string, error) { + if !IsRevisionFormat(chainID) { + return "", sdkerrors.Wrapf( + sdkerrors.ErrInvalidChainID, "chainID is not in revision format: %s", chainID, + ) + } + + splitStr := strings.Split(chainID, "-") + // swap out revision number with given revision + splitStr[len(splitStr)-1] = strconv.Itoa(int(revision)) + return strings.Join(splitStr, "-"), nil +} + +// ParseChainID is a utility function that returns an revision number from the given ChainID. +// ParseChainID attempts to parse a chain id in the format: `{chainID}-{revision}` +// and return the revisionnumber as a uint64. +// If the chainID is not in the expected format, a default revision value of 0 is returned. +func ParseChainID(chainID string) uint64 { + if !IsRevisionFormat(chainID) { + // chainID is not in revision format, return 0 as default + return 0 + } + splitStr := strings.Split(chainID, "-") + revision, err := strconv.ParseUint(splitStr[len(splitStr)-1], 10, 64) + // sanity check: error should always be nil since regex only allows numbers in last element + if err != nil { + panic(fmt.Sprintf("regex allowed non-number value as last split element for chainID: %s", chainID)) + } + return revision +} + +// GetSelfHeight is a utility function that returns self height given context +// Revision number is retrieved from ctx.ChainID() +func GetSelfHeight(ctx sdk.Context) Height { + revision := ParseChainID(ctx.ChainID()) + return NewHeight(revision, uint64(ctx.BlockHeight())) +} diff --git a/x/ibc/core/02-client/types/height_test.go b/x/ibc/core/02-client/types/height_test.go new file mode 100644 index 000000000000..a455b7f58d49 --- /dev/null +++ b/x/ibc/core/02-client/types/height_test.go @@ -0,0 +1,155 @@ +package types_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +func TestZeroHeight(t *testing.T) { + require.Equal(t, types.Height{}, types.ZeroHeight()) +} + +func TestCompareHeights(t *testing.T) { + testCases := []struct { + name string + height1 types.Height + height2 types.Height + compareSign int64 + }{ + {"revision number 1 is lesser", types.NewHeight(1, 3), types.NewHeight(3, 4), -1}, + {"revision number 1 is greater", types.NewHeight(7, 5), types.NewHeight(4, 5), 1}, + {"revision height 1 is lesser", types.NewHeight(3, 4), types.NewHeight(3, 9), -1}, + {"revision height 1 is greater", types.NewHeight(3, 8), types.NewHeight(3, 3), 1}, + {"revision number is MaxUint64", types.NewHeight(math.MaxUint64, 1), types.NewHeight(0, 1), 1}, + {"revision height is MaxUint64", types.NewHeight(1, math.MaxUint64), types.NewHeight(1, 0), 1}, + {"height is equal", types.NewHeight(4, 4), types.NewHeight(4, 4), 0}, + } + + for i, tc := range testCases { + compare := tc.height1.Compare(tc.height2) + + switch tc.compareSign { + case -1: + require.True(t, compare == -1, "case %d: %s should return negative value on comparison, got: %d", + i, tc.name, compare) + case 0: + require.True(t, compare == 0, "case %d: %s should return zero on comparison, got: %d", + i, tc.name, compare) + case 1: + require.True(t, compare == 1, "case %d: %s should return positive value on comparison, got: %d", + i, tc.name, compare) + } + } +} + +func TestDecrement(t *testing.T) { + validDecrement := types.NewHeight(3, 3) + expected := types.NewHeight(3, 2) + + actual, success := validDecrement.Decrement() + require.Equal(t, expected, actual, "decrementing %s did not return expected height: %s. got %s", + validDecrement, expected, actual) + require.True(t, success, "decrement failed unexpectedly") + + invalidDecrement := types.NewHeight(3, 0) + actual, success = invalidDecrement.Decrement() + + require.Equal(t, types.ZeroHeight(), actual, "invalid decrement returned non-zero height: %s", actual) + require.False(t, success, "invalid decrement passed") +} + +func TestString(t *testing.T) { + _, err := types.ParseHeight("height") + require.Error(t, err, "invalid height string passed") + + _, err = types.ParseHeight("revision-10") + require.Error(t, err, "invalid revision string passed") + + _, err = types.ParseHeight("3-height") + require.Error(t, err, "invalid revision-height string passed") + + height := types.NewHeight(3, 4) + recovered, err := types.ParseHeight(height.String()) + + require.NoError(t, err, "valid height string could not be parsed") + require.Equal(t, height, recovered, "recovered height not equal to original height") + + parse, err := types.ParseHeight("3-10") + require.NoError(t, err, "parse err") + require.Equal(t, types.NewHeight(3, 10), parse, "parse height returns wrong height") +} + +func (suite *TypesTestSuite) TestMustParseHeight() { + suite.Require().Panics(func() { + types.MustParseHeight("height") + }) + + suite.Require().NotPanics(func() { + types.MustParseHeight("111-1") + }) + + suite.Require().NotPanics(func() { + types.MustParseHeight("0-0") + }) +} + +func TestParseChainID(t *testing.T) { + cases := []struct { + chainID string + revision uint64 + formatted bool + }{ + {"gaiamainnet-3", 3, true}, + {"a-1", 1, true}, + {"gaia-mainnet-40", 40, true}, + {"gaiamainnet-3-39", 39, true}, + {"gaiamainnet--", 0, false}, + {"gaiamainnet-03", 0, false}, + {"gaiamainnet--4", 0, false}, + {"gaiamainnet-3.4", 0, false}, + {"gaiamainnet", 0, false}, + {"a--1", 0, false}, + {"-1", 0, false}, + {"--1", 0, false}, + } + + for i, tc := range cases { + require.Equal(t, tc.formatted, types.IsRevisionFormat(tc.chainID), "id %s does not match expected format", tc.chainID) + + revision := types.ParseChainID(tc.chainID) + require.Equal(t, tc.revision, revision, "case %d returns incorrect revision", i) + } + +} + +func TestSetRevisionNumber(t *testing.T) { + // Test SetRevisionNumber + chainID, err := types.SetRevisionNumber("gaiamainnet", 3) + require.Error(t, err, "invalid revision format passed SetRevisionNumber") + require.Equal(t, "", chainID, "invalid revision format returned non-empty string on SetRevisionNumber") + chainID = "gaiamainnet-3" + + chainID, err = types.SetRevisionNumber(chainID, 4) + require.NoError(t, err, "valid revision format failed SetRevisionNumber") + require.Equal(t, "gaiamainnet-4", chainID, "valid revision format returned incorrect string on SetRevisionNumber") +} + +func (suite *TypesTestSuite) TestSelfHeight() { + ctx := suite.chainA.GetContext() + + // Test default revision + ctx = ctx.WithChainID("gaiamainnet") + ctx = ctx.WithBlockHeight(10) + height := types.GetSelfHeight(ctx) + suite.Require().Equal(types.NewHeight(0, 10), height, "default self height failed") + + // Test successful revision format + ctx = ctx.WithChainID("gaiamainnet-3") + ctx = ctx.WithBlockHeight(18) + height = types.GetSelfHeight(ctx) + suite.Require().Equal(types.NewHeight(3, 18), height, "valid self height failed") +} diff --git a/x/ibc/core/02-client/types/keys.go b/x/ibc/core/02-client/types/keys.go new file mode 100644 index 000000000000..321f5e3ffa3c --- /dev/null +++ b/x/ibc/core/02-client/types/keys.go @@ -0,0 +1,65 @@ +package types + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + // SubModuleName defines the IBC client name + SubModuleName string = "client" + + // RouterKey is the message route for IBC client + RouterKey string = SubModuleName + + // QuerierRoute is the querier route for IBC client + QuerierRoute string = SubModuleName + + // KeyNextClientSequence is the key used to store the next client sequence in + // the keeper. + KeyNextClientSequence = "nextClientSequence" +) + +// FormatClientIdentifier returns the client identifier with the sequence appended. +// This is a SDK specific format not enforced by IBC protocol. +func FormatClientIdentifier(clientType string, sequence uint64) string { + return fmt.Sprintf("%s-%d", clientType, sequence) +} + +// IsClientIDFormat checks if a clientID is in the format required on the SDK for +// parsing client identifiers. The client identifier must be in the form: `{client-type}-{N} +var IsClientIDFormat = regexp.MustCompile(`^.*[^-]-[0-9]{1,20}$`).MatchString + +// IsValidClientID checks if the clientID is valid and can be parsed into the client +// identifier format. +func IsValidClientID(clientID string) bool { + _, _, err := ParseClientIdentifier(clientID) + return err == nil +} + +// ParseClientIdentifier parses the client type and sequence from the client identifier. +func ParseClientIdentifier(clientID string) (string, uint64, error) { + if !IsClientIDFormat(clientID) { + return "", 0, sdkerrors.Wrapf(host.ErrInvalidID, "invalid client identifier %s is not in format: `{client-type}-{N}`", clientID) + } + + splitStr := strings.Split(clientID, "-") + lastIndex := len(splitStr) - 1 + + clientType := strings.Join(splitStr[:lastIndex], "-") + if strings.TrimSpace(clientType) == "" { + return "", 0, sdkerrors.Wrap(host.ErrInvalidID, "client identifier must be in format: `{client-type}-{N}` and client type cannot be blank") + } + + sequence, err := strconv.ParseUint(splitStr[lastIndex], 10, 64) + if err != nil { + return "", 0, sdkerrors.Wrap(err, "failed to parse client identifier sequence") + } + + return clientType, sequence, nil +} diff --git a/x/ibc/core/02-client/types/keys_test.go b/x/ibc/core/02-client/types/keys_test.go new file mode 100644 index 000000000000..4938145236e7 --- /dev/null +++ b/x/ibc/core/02-client/types/keys_test.go @@ -0,0 +1,54 @@ +package types_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" +) + +// tests ParseClientIdentifier and IsValidClientID +func TestParseClientIdentifier(t *testing.T) { + testCases := []struct { + name string + clientID string + clientType string + expSeq uint64 + expPass bool + }{ + {"valid 0", "tendermint-0", "tendermint", 0, true}, + {"valid 1", "tendermint-1", "tendermint", 1, true}, + {"valid solemachine", "solomachine-v1-1", "solomachine-v1", 1, true}, + {"valid large sequence", types.FormatClientIdentifier("tendermint", math.MaxUint64), "tendermint", math.MaxUint64, true}, + {"valid short client type", "t-0", "t", 0, true}, + // one above uint64 max + {"invalid uint64", "tendermint-18446744073709551616", "tendermint", 0, false}, + // uint64 == 20 characters + {"invalid large sequence", "tendermint-2345682193567182931243", "tendermint", 0, false}, + {"missing dash", "tendermint0", "tendermint", 0, false}, + {"blank id", " ", " ", 0, false}, + {"empty id", "", "", 0, false}, + {"negative sequence", "tendermint--1", "tendermint", 0, false}, + {"invalid format", "tendermint-tm", "tendermint", 0, false}, + {"empty clientype", " -100", "tendermint", 0, false}, + } + + for _, tc := range testCases { + + clientType, seq, err := types.ParseClientIdentifier(tc.clientID) + valid := types.IsValidClientID(tc.clientID) + require.Equal(t, tc.expSeq, seq, tc.clientID) + + if tc.expPass { + require.NoError(t, err, tc.name) + require.True(t, valid) + require.Equal(t, tc.clientType, clientType) + } else { + require.Error(t, err, tc.name, tc.clientID) + require.False(t, valid) + require.Equal(t, "", clientType) + } + } +} diff --git a/x/ibc/core/02-client/types/msgs.go b/x/ibc/core/02-client/types/msgs.go new file mode 100644 index 000000000000..1e884123d74b --- /dev/null +++ b/x/ibc/core/02-client/types/msgs.go @@ -0,0 +1,343 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// message types for the IBC client +const ( + TypeMsgCreateClient string = "create_client" + TypeMsgUpdateClient string = "update_client" + TypeMsgUpgradeClient string = "upgrade_client" + TypeMsgSubmitMisbehaviour string = "submit_misbehaviour" +) + +var ( + _ sdk.Msg = &MsgCreateClient{} + _ sdk.Msg = &MsgUpdateClient{} + _ sdk.Msg = &MsgSubmitMisbehaviour{} + _ sdk.Msg = &MsgUpgradeClient{} + + _ codectypes.UnpackInterfacesMessage = MsgCreateClient{} + _ codectypes.UnpackInterfacesMessage = MsgUpdateClient{} + _ codectypes.UnpackInterfacesMessage = MsgSubmitMisbehaviour{} + _ codectypes.UnpackInterfacesMessage = MsgUpgradeClient{} +) + +// NewMsgCreateClient creates a new MsgCreateClient instance +//nolint:interfacer +func NewMsgCreateClient( + clientState exported.ClientState, consensusState exported.ConsensusState, signer sdk.AccAddress, +) (*MsgCreateClient, error) { + + anyClientState, err := PackClientState(clientState) + if err != nil { + return nil, err + } + + anyConsensusState, err := PackConsensusState(consensusState) + if err != nil { + return nil, err + } + + return &MsgCreateClient{ + ClientState: anyClientState, + ConsensusState: anyConsensusState, + Signer: signer.String(), + }, nil +} + +// Route implements sdk.Msg +func (msg MsgCreateClient) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgCreateClient) Type() string { + return TypeMsgCreateClient +} + +// ValidateBasic implements sdk.Msg +func (msg MsgCreateClient) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + clientState, err := UnpackClientState(msg.ClientState) + if err != nil { + return err + } + if err := clientState.Validate(); err != nil { + return err + } + if clientState.ClientType() == exported.Localhost { + return sdkerrors.Wrap(ErrInvalidClient, "localhost client can only be created on chain initialization") + } + consensusState, err := UnpackConsensusState(msg.ConsensusState) + if err != nil { + return err + } + if clientState.ClientType() != consensusState.ClientType() { + return sdkerrors.Wrap(ErrInvalidClientType, "client type for client state and consensus state do not match") + } + if err := ValidateClientType(clientState.ClientType()); err != nil { + return sdkerrors.Wrap(err, "client type does not meet naming constraints") + } + return consensusState.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgCreateClient) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgCreateClient) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgCreateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var clientState exported.ClientState + err := unpacker.UnpackAny(msg.ClientState, &clientState) + if err != nil { + return err + } + + var consensusState exported.ConsensusState + return unpacker.UnpackAny(msg.ConsensusState, &consensusState) +} + +// NewMsgUpdateClient creates a new MsgUpdateClient instance +//nolint:interfacer +func NewMsgUpdateClient(id string, header exported.Header, signer sdk.AccAddress) (*MsgUpdateClient, error) { + anyHeader, err := PackHeader(header) + if err != nil { + return nil, err + } + + return &MsgUpdateClient{ + ClientId: id, + Header: anyHeader, + Signer: signer.String(), + }, nil +} + +// Route implements sdk.Msg +func (msg MsgUpdateClient) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgUpdateClient) Type() string { + return TypeMsgUpdateClient +} + +// ValidateBasic implements sdk.Msg +func (msg MsgUpdateClient) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + header, err := UnpackHeader(msg.Header) + if err != nil { + return err + } + if err := header.ValidateBasic(); err != nil { + return err + } + if msg.ClientId == exported.Localhost { + return sdkerrors.Wrap(ErrInvalidClient, "localhost client is only updated on ABCI BeginBlock") + } + return host.ClientIdentifierValidator(msg.ClientId) +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgUpdateClient) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgUpdateClient) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var header exported.Header + return unpacker.UnpackAny(msg.Header, &header) +} + +// NewMsgUpgradeClient creates a new MsgUpgradeClient instance +// nolint: interfacer +func NewMsgUpgradeClient(clientID string, clientState exported.ClientState, consState exported.ConsensusState, + proofUpgradeClient, proofUpgradeConsState []byte, signer sdk.AccAddress) (*MsgUpgradeClient, error) { + anyClient, err := PackClientState(clientState) + if err != nil { + return nil, err + } + anyConsState, err := PackConsensusState(consState) + if err != nil { + return nil, err + } + + return &MsgUpgradeClient{ + ClientId: clientID, + ClientState: anyClient, + ConsensusState: anyConsState, + ProofUpgradeClient: proofUpgradeClient, + ProofUpgradeConsensusState: proofUpgradeConsState, + Signer: signer.String(), + }, nil +} + +// Route implements sdk.Msg +func (msg MsgUpgradeClient) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgUpgradeClient) Type() string { + return TypeMsgUpgradeClient +} + +// ValidateBasic implements sdk.Msg +func (msg MsgUpgradeClient) ValidateBasic() error { + // will not validate client state as committed client may not form a valid client state. + // client implementations are responsible for ensuring final upgraded client is valid. + clientState, err := UnpackClientState(msg.ClientState) + if err != nil { + return err + } + // will not validate consensus state here since the trusted kernel may not form a valid consenus state. + // client implementations are responsible for ensuring client can submit new headers against this consensus state. + consensusState, err := UnpackConsensusState(msg.ConsensusState) + if err != nil { + return err + } + + if clientState.ClientType() != consensusState.ClientType() { + return sdkerrors.Wrapf(ErrInvalidUpgradeClient, "consensus state's client-type does not match client. expected: %s, got: %s", + clientState.ClientType(), consensusState.ClientType()) + } + if len(msg.ProofUpgradeClient) == 0 { + return sdkerrors.Wrap(ErrInvalidUpgradeClient, "proof of upgrade client cannot be empty") + } + if len(msg.ProofUpgradeConsensusState) == 0 { + return sdkerrors.Wrap(ErrInvalidUpgradeClient, "proof of upgrade consensus state cannot be empty") + } + _, err = sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return host.ClientIdentifierValidator(msg.ClientId) +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgUpgradeClient) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgUpgradeClient) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgUpgradeClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var ( + clientState exported.ClientState + consState exported.ConsensusState + ) + if err := unpacker.UnpackAny(msg.ClientState, &clientState); err != nil { + return err + } + return unpacker.UnpackAny(msg.ConsensusState, &consState) +} + +// NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance. +//nolint:interfacer +func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviour, signer sdk.AccAddress) (*MsgSubmitMisbehaviour, error) { + anyMisbehaviour, err := PackMisbehaviour(misbehaviour) + if err != nil { + return nil, err + } + + return &MsgSubmitMisbehaviour{ + ClientId: clientID, + Misbehaviour: anyMisbehaviour, + Signer: signer.String(), + }, nil +} + +// Route returns the MsgSubmitClientMisbehaviour's route. +func (msg MsgSubmitMisbehaviour) Route() string { return host.RouterKey } + +// Type returns the MsgSubmitMisbehaviour's type. +func (msg MsgSubmitMisbehaviour) Type() string { + return TypeMsgSubmitMisbehaviour +} + +// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitMisbehaviour. +func (msg MsgSubmitMisbehaviour) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + misbehaviour, err := UnpackMisbehaviour(msg.Misbehaviour) + if err != nil { + return err + } + if err := misbehaviour.ValidateBasic(); err != nil { + return err + } + if misbehaviour.GetClientID() != msg.ClientId { + return sdkerrors.Wrapf( + ErrInvalidMisbehaviour, + "misbehaviour client-id doesn't match client-id from message (%s ≠ %s)", + misbehaviour.GetClientID(), msg.ClientId, + ) + } + + return host.ClientIdentifierValidator(msg.ClientId) +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgSubmitMisbehaviour) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners returns the single expected signer for a MsgSubmitMisbehaviour. +func (msg MsgSubmitMisbehaviour) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgSubmitMisbehaviour) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var misbehaviour exported.Misbehaviour + return unpacker.UnpackAny(msg.Misbehaviour, &misbehaviour) +} diff --git a/x/ibc/core/02-client/types/msgs_test.go b/x/ibc/core/02-client/types/msgs_test.go new file mode 100644 index 000000000000..e42725bae26f --- /dev/null +++ b/x/ibc/core/02-client/types/msgs_test.go @@ -0,0 +1,619 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/suite" + + "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/core/exported" + solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type TypesTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *TypesTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) +} + +func TestTypesTestSuite(t *testing.T) { + suite.Run(t, new(TypesTestSuite)) +} + +// tests that different clients within MsgCreateClient can be marshaled +// and unmarshaled. +func (suite *TypesTestSuite) TestMarshalMsgCreateClient() { + var ( + msg *types.MsgCreateClient + err error + ) + + testCases := []struct { + name string + malleate func() + }{ + { + "solo machine client", func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgCreateClient(soloMachine.ClientState(), soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + { + "tendermint client", func() { + tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.malleate() + + cdc := suite.chainA.App.AppCodec() + + // marshal message + bz, err := cdc.MarshalJSON(msg) + suite.Require().NoError(err) + + // unmarshal message + newMsg := &types.MsgCreateClient{} + err = cdc.UnmarshalJSON(bz, newMsg) + suite.Require().NoError(err) + + suite.Require().True(proto.Equal(msg, newMsg)) + }) + } +} + +func (suite *TypesTestSuite) TestMsgCreateClient_ValidateBasic() { + var ( + msg = &types.MsgCreateClient{} + err error + ) + + cases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid - tendermint client", + func() { + tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid tendermint client", + func() { + msg, err = types.NewMsgCreateClient(&ibctmtypes.ClientState{}, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "failed to unpack client", + func() { + msg.ClientState = nil + }, + false, + }, + { + "failed to unpack consensus state", + func() { + tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + msg, err = types.NewMsgCreateClient(tendermintClient, suite.chainA.CurrentTMClientHeader().ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + msg.ConsensusState = nil + }, + false, + }, + { + "invalid signer", + func() { + msg.Signer = "" + }, + false, + }, + { + "valid - solomachine client", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgCreateClient(soloMachine.ClientState(), soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid solomachine client", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgCreateClient(&solomachinetypes.ClientState{}, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "invalid solomachine consensus state", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgCreateClient(soloMachine.ClientState(), &solomachinetypes.ConsensusState{}, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "invalid - client state and consensus state client types do not match", + func() { + tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgCreateClient(tendermintClient, soloMachine.ConsensusState(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range cases { + tc.malleate() + err = msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +// tests that different header within MsgUpdateClient can be marshaled +// and unmarshaled. +func (suite *TypesTestSuite) TestMarshalMsgUpdateClient() { + var ( + msg *types.MsgUpdateClient + err error + ) + + testCases := []struct { + name string + malleate func() + }{ + { + "solo machine client", func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + { + "tendermint client", func() { + msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.malleate() + + cdc := suite.chainA.App.AppCodec() + + // marshal message + bz, err := cdc.MarshalJSON(msg) + suite.Require().NoError(err) + + // unmarshal message + newMsg := &types.MsgUpdateClient{} + err = cdc.UnmarshalJSON(bz, newMsg) + suite.Require().NoError(err) + + suite.Require().True(proto.Equal(msg, newMsg)) + }) + } +} + +func (suite *TypesTestSuite) TestMsgUpdateClient_ValidateBasic() { + var ( + msg = &types.MsgUpdateClient{} + err error + ) + + cases := []struct { + name string + malleate func() + expPass bool + }{ + { + "invalid client-id", + func() { + msg.ClientId = "" + }, + false, + }, + { + "valid - tendermint header", + func() { + msg, err = types.NewMsgUpdateClient("tendermint", suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid tendermint header", + func() { + msg, err = types.NewMsgUpdateClient("tendermint", &ibctmtypes.Header{}, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "failed to unpack header", + func() { + msg.Header = nil + }, + false, + }, + { + "invalid signer", + func() { + msg.Signer = "" + }, + false, + }, + { + "valid - solomachine header", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgUpdateClient(soloMachine.ClientID, soloMachine.CreateHeader(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid solomachine header", + func() { + msg, err = types.NewMsgUpdateClient("solomachine", &solomachinetypes.Header{}, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "unsupported - localhost", + func() { + msg, err = types.NewMsgUpdateClient(exported.Localhost, suite.chainA.CurrentTMClientHeader(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range cases { + tc.malleate() + err = msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TypesTestSuite) TestMarshalMsgUpgradeClient() { + var ( + msg *types.MsgUpgradeClient + err error + ) + + testCases := []struct { + name string + malleate func() + }{ + { + "client upgrades to new tendermint client", + func() { + tendermintClient := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + tendermintConsState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + msg, err = types.NewMsgUpgradeClient("clientid", tendermintClient, tendermintConsState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + { + "client upgrades to new solomachine client", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 1) + msg, err = types.NewMsgUpgradeClient("clientid", soloMachine.ClientState(), soloMachine.ConsensusState(), []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.malleate() + + cdc := suite.chainA.App.AppCodec() + + // marshal message + bz, err := cdc.MarshalJSON(msg) + suite.Require().NoError(err) + + // unmarshal message + newMsg := &types.MsgUpgradeClient{} + err = cdc.UnmarshalJSON(bz, newMsg) + suite.Require().NoError(err) + }) + } +} + +func (suite *TypesTestSuite) TestMsgUpgradeClient_ValidateBasic() { + cases := []struct { + name string + malleate func(*types.MsgUpgradeClient) + expPass bool + }{ + { + name: "success", + malleate: func(msg *types.MsgUpgradeClient) {}, + expPass: true, + }, + { + name: "client id empty", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ClientId = "" + }, + expPass: false, + }, + { + name: "invalid client id", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ClientId = "invalid~chain/id" + }, + expPass: false, + }, + { + name: "unpacking clientstate fails", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ClientState = nil + }, + expPass: false, + }, + { + name: "unpacking consensus state fails", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ConsensusState = nil + }, + expPass: false, + }, + { + name: "client and consensus type does not match", + malleate: func(msg *types.MsgUpgradeClient) { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + soloConsensus, err := types.PackConsensusState(soloMachine.ConsensusState()) + suite.Require().NoError(err) + msg.ConsensusState = soloConsensus + }, + expPass: false, + }, + { + name: "empty client proof", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ProofUpgradeClient = nil + }, + expPass: false, + }, + { + name: "empty consensus state proof", + malleate: func(msg *types.MsgUpgradeClient) { + msg.ProofUpgradeConsensusState = nil + }, + expPass: false, + }, + { + name: "empty signer", + malleate: func(msg *types.MsgUpgradeClient) { + msg.Signer = " " + }, + expPass: false, + }, + } + + for _, tc := range cases { + tc := tc + + clientState := ibctmtypes.NewClientState(suite.chainA.ChainID, ibctesting.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + consState := &ibctmtypes.ConsensusState{NextValidatorsHash: []byte("nextValsHash")} + msg, err := types.NewMsgUpgradeClient("testclientid", clientState, consState, []byte("proofUpgradeClient"), []byte("proofUpgradeConsState"), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + + tc.malleate(msg) + err = msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, "valid case %s failed", tc.name) + } else { + suite.Require().Error(err, "invalid case %s passed", tc.name) + } + } +} + +// tests that different misbehaviours within MsgSubmitMisbehaviour can be marshaled +// and unmarshaled. +func (suite *TypesTestSuite) TestMarshalMsgSubmitMisbehaviour() { + var ( + msg *types.MsgSubmitMisbehaviour + err error + ) + + testCases := []struct { + name string + malleate func() + }{ + { + "solo machine client", func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgSubmitMisbehaviour(soloMachine.ClientID, soloMachine.CreateMisbehaviour(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + }, + { + "tendermint client", func() { + height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) + heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + + misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + + }, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.malleate() + + cdc := suite.chainA.App.AppCodec() + + // marshal message + bz, err := cdc.MarshalJSON(msg) + suite.Require().NoError(err) + + // unmarshal message + newMsg := &types.MsgSubmitMisbehaviour{} + err = cdc.UnmarshalJSON(bz, newMsg) + suite.Require().NoError(err) + + suite.Require().True(proto.Equal(msg, newMsg)) + }) + } +} + +func (suite *TypesTestSuite) TestMsgSubmitMisbehaviour_ValidateBasic() { + var ( + msg = &types.MsgSubmitMisbehaviour{} + err error + ) + + cases := []struct { + name string + malleate func() + expPass bool + }{ + { + "invalid client-id", + func() { + msg.ClientId = "" + }, + false, + }, + { + "valid - tendermint misbehaviour", + func() { + height := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)) + heightMinus1 := types.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height)-1) + header1 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + header2 := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), heightMinus1, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + + misbehaviour := ibctmtypes.NewMisbehaviour("tendermint", header1, header2) + msg, err = types.NewMsgSubmitMisbehaviour("tendermint", misbehaviour, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid tendermint misbehaviour", + func() { + msg, err = types.NewMsgSubmitMisbehaviour("tendermint", &ibctmtypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "failed to unpack misbehaviourt", + func() { + msg.Misbehaviour = nil + }, + false, + }, + { + "invalid signer", + func() { + msg.Signer = "" + }, + false, + }, + { + "valid - solomachine misbehaviour", + func() { + soloMachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2) + msg, err = types.NewMsgSubmitMisbehaviour(soloMachine.ClientID, soloMachine.CreateMisbehaviour(), suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + true, + }, + { + "invalid solomachine misbehaviour", + func() { + msg, err = types.NewMsgSubmitMisbehaviour("solomachine", &solomachinetypes.Misbehaviour{}, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + { + "client-id mismatch", + func() { + soloMachineMisbehaviour := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).CreateMisbehaviour() + msg, err = types.NewMsgSubmitMisbehaviour("external", soloMachineMisbehaviour, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + false, + }, + } + + for _, tc := range cases { + tc.malleate() + err = msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} diff --git a/x/ibc/core/02-client/types/params.go b/x/ibc/core/02-client/types/params.go new file mode 100644 index 000000000000..6477e3f6f4f4 --- /dev/null +++ b/x/ibc/core/02-client/types/params.go @@ -0,0 +1,71 @@ +package types + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var ( + // DefaultAllowedClients are "06-solomachine" and "07-tendermint" + DefaultAllowedClients = []string{exported.Solomachine, exported.Tendermint} + + // KeyAllowedClients is store's key for AllowedClients Params + KeyAllowedClients = []byte("AllowedClients") +) + +// ParamKeyTable type declaration for parameters +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// NewParams creates a new parameter configuration for the ibc transfer module +func NewParams(allowedClients ...string) Params { + return Params{ + AllowedClients: allowedClients, + } +} + +// DefaultParams is the default parameter configuration for the ibc-transfer module +func DefaultParams() Params { + return NewParams(DefaultAllowedClients...) +} + +// Validate all ibc-transfer module parameters +func (p Params) Validate() error { + return validateClients(p.AllowedClients) +} + +// ParamSetPairs implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyAllowedClients, p.AllowedClients, validateClients), + } +} + +// IsAllowedClient checks if the given client type is registered on the allowlist. +func (p Params) IsAllowedClient(clientType string) bool { + for _, allowedClient := range p.AllowedClients { + if allowedClient == clientType { + return true + } + } + return false +} + +func validateClients(i interface{}) error { + clients, ok := i.([]string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for i, clientType := range clients { + if strings.TrimSpace(clientType) == "" { + return fmt.Errorf("client type %d cannot be blank", i) + } + } + + return nil +} diff --git a/x/ibc/core/02-client/types/params_test.go b/x/ibc/core/02-client/types/params_test.go new file mode 100644 index 000000000000..dac80a4b4217 --- /dev/null +++ b/x/ibc/core/02-client/types/params_test.go @@ -0,0 +1,30 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +func TestValidateParams(t *testing.T) { + testCases := []struct { + name string + params Params + expPass bool + }{ + {"default params", DefaultParams(), true}, + {"custom params", NewParams(exported.Tendermint), true}, + {"blank client", NewParams(" "), false}, + } + + for _, tc := range testCases { + err := tc.params.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/core/02-client/types/proposal.go b/x/ibc/core/02-client/types/proposal.go new file mode 100644 index 000000000000..334a9d4599ef --- /dev/null +++ b/x/ibc/core/02-client/types/proposal.go @@ -0,0 +1,70 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + 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 ( + // ProposalTypeClientUpdate defines the type for a ClientUpdateProposal + ProposalTypeClientUpdate = "ClientUpdate" +) + +var ( + _ govtypes.Content = &ClientUpdateProposal{} + _ codectypes.UnpackInterfacesMessage = 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 + } + + return &ClientUpdateProposal{ + Title: title, + Description: description, + ClientId: clientID, + Header: any, + }, nil +} + +// GetTitle returns the title of a client update proposal. +func (cup *ClientUpdateProposal) GetTitle() string { return cup.Title } + +// GetDescription returns the description of a client update proposal. +func (cup *ClientUpdateProposal) GetDescription() string { return cup.Description } + +// ProposalRoute returns the routing key of a client update proposal. +func (cup *ClientUpdateProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a client update proposal. +func (cup *ClientUpdateProposal) ProposalType() string { return ProposalTypeClientUpdate } + +// ValidateBasic runs basic stateless validity checks +func (cup *ClientUpdateProposal) ValidateBasic() error { + err := govtypes.ValidateAbstract(cup) + if err != nil { + return err + } + + if err := host.ClientIdentifierValidator(cup.ClientId); err != nil { + return err + } + + header, err := UnpackHeader(cup.Header) + if err != nil { + return err + } + + return header.ValidateBasic() +} + +// UnpackInterfaces implements the UnpackInterfacesMessage interface. +func (cup ClientUpdateProposal) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var header exported.Header + return unpacker.UnpackAny(cup.Header, &header) +} diff --git a/x/ibc/core/02-client/types/proposal_test.go b/x/ibc/core/02-client/types/proposal_test.go new file mode 100644 index 000000000000..5a47cf2f01cb --- /dev/null +++ b/x/ibc/core/02-client/types/proposal_test.go @@ -0,0 +1,107 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/codec" + 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" + 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) + + testCases := []struct { + name string + proposal govtypes.Content + expPass bool + }{ + { + "success", + &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, header}, + true, + }, + { + "fails validate abstract - empty title", + &types.ClientUpdateProposal{"", ibctesting.Description, clientID, header}, + false, + }, + { + "fails to unpack header", + &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, nil}, + false, + }, + { + "fails header validate basic", + &types.ClientUpdateProposal{ibctesting.Title, ibctesting.Description, clientID, invalidHeader}, + false, + }, + } + + for _, tc := range testCases { + + err := tc.proposal.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +// tests a client update proposal can be marshaled and unmarshaled, and the +// client state can be unpacked +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) + + // create codec + ir := codectypes.NewInterfaceRegistry() + types.RegisterInterfaces(ir) + govtypes.RegisterInterfaces(ir) + ibctmtypes.RegisterInterfaces(ir) + cdc := codec.NewProtoCodec(ir) + + // marshal message + bz, err := cdc.MarshalJSON(proposal) + suite.Require().NoError(err) + + // unmarshal proposal + 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 new file mode 100644 index 000000000000..c46bbfcfe778 --- /dev/null +++ b/x/ibc/core/02-client/types/query.go @@ -0,0 +1,65 @@ +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, +) *QueryClientStateResponse { + return &QueryClientStateResponse{ + ClientState: clientStateAny, + Proof: proof, + ProofHeight: height, + } +} + +// 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, +) *QueryConsensusStateResponse { + return &QueryConsensusStateResponse{ + ConsensusState: consensusStateAny, + Proof: proof, + 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 new file mode 100644 index 000000000000..651becb899ae --- /dev/null +++ b/x/ibc/core/02-client/types/query.pb.go @@ -0,0 +1,2683 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/client/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + _ "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 + +// QueryClientStateRequest is the request type for the Query/ClientState RPC +// method +type QueryClientStateRequest struct { + // client state unique identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` +} + +func (m *QueryClientStateRequest) Reset() { *m = QueryClientStateRequest{} } +func (m *QueryClientStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryClientStateRequest) ProtoMessage() {} +func (*QueryClientStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{0} +} +func (m *QueryClientStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientStateRequest.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 *QueryClientStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientStateRequest.Merge(m, src) +} +func (m *QueryClientStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryClientStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientStateRequest proto.InternalMessageInfo + +func (m *QueryClientStateRequest) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +// QueryClientStateResponse is the response type for the Query/ClientState RPC +// method. Besides the client state, it includes a proof and the height from +// which the proof was retrieved. +type QueryClientStateResponse struct { + // client state associated with the request identifier + ClientState *types.Any `protobuf:"bytes,1,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryClientStateResponse) Reset() { *m = QueryClientStateResponse{} } +func (m *QueryClientStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryClientStateResponse) ProtoMessage() {} +func (*QueryClientStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{1} +} +func (m *QueryClientStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientStateResponse.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 *QueryClientStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientStateResponse.Merge(m, src) +} +func (m *QueryClientStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryClientStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientStateResponse proto.InternalMessageInfo + +func (m *QueryClientStateResponse) GetClientState() *types.Any { + if m != nil { + return m.ClientState + } + return nil +} + +func (m *QueryClientStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryClientStateResponse) GetProofHeight() Height { + if m != nil { + return m.ProofHeight + } + return Height{} +} + +// QueryClientStatesRequest is the request type for the Query/ClientStates RPC +// method +type QueryClientStatesRequest struct { + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryClientStatesRequest) Reset() { *m = QueryClientStatesRequest{} } +func (m *QueryClientStatesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryClientStatesRequest) ProtoMessage() {} +func (*QueryClientStatesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{2} +} +func (m *QueryClientStatesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientStatesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientStatesRequest.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 *QueryClientStatesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientStatesRequest.Merge(m, src) +} +func (m *QueryClientStatesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryClientStatesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientStatesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientStatesRequest proto.InternalMessageInfo + +func (m *QueryClientStatesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryClientStatesResponse is the response type for the Query/ClientStates RPC +// method. +type QueryClientStatesResponse struct { + // list of stored ClientStates of the chain. + ClientStates IdentifiedClientStates `protobuf:"bytes,1,rep,name=client_states,json=clientStates,proto3,castrepeated=IdentifiedClientStates" json:"client_states"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryClientStatesResponse) Reset() { *m = QueryClientStatesResponse{} } +func (m *QueryClientStatesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryClientStatesResponse) ProtoMessage() {} +func (*QueryClientStatesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{3} +} +func (m *QueryClientStatesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientStatesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientStatesResponse.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 *QueryClientStatesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientStatesResponse.Merge(m, src) +} +func (m *QueryClientStatesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryClientStatesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientStatesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientStatesResponse proto.InternalMessageInfo + +func (m *QueryClientStatesResponse) GetClientStates() IdentifiedClientStates { + if m != nil { + return m.ClientStates + } + return nil +} + +func (m *QueryClientStatesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryConsensusStateRequest is the request type for the Query/ConsensusState +// RPC method. Besides the consensus state, it includes a proof and the height +// from which the proof was retrieved. +type QueryConsensusStateRequest struct { + // client identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // consensus state revision number + RevisionNumber uint64 `protobuf:"varint,2,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty"` + // consensus state revision height + RevisionHeight uint64 `protobuf:"varint,3,opt,name=revision_height,json=revisionHeight,proto3" json:"revision_height,omitempty"` + // latest_height overrrides the height field and queries the latest stored + // ConsensusState + LatestHeight bool `protobuf:"varint,4,opt,name=latest_height,json=latestHeight,proto3" json:"latest_height,omitempty"` +} + +func (m *QueryConsensusStateRequest) Reset() { *m = QueryConsensusStateRequest{} } +func (m *QueryConsensusStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsensusStateRequest) ProtoMessage() {} +func (*QueryConsensusStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{4} +} +func (m *QueryConsensusStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsensusStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsensusStateRequest.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 *QueryConsensusStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsensusStateRequest.Merge(m, src) +} +func (m *QueryConsensusStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsensusStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsensusStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsensusStateRequest proto.InternalMessageInfo + +func (m *QueryConsensusStateRequest) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *QueryConsensusStateRequest) GetRevisionNumber() uint64 { + if m != nil { + return m.RevisionNumber + } + return 0 +} + +func (m *QueryConsensusStateRequest) GetRevisionHeight() uint64 { + if m != nil { + return m.RevisionHeight + } + return 0 +} + +func (m *QueryConsensusStateRequest) GetLatestHeight() bool { + if m != nil { + return m.LatestHeight + } + return false +} + +// QueryConsensusStateResponse is the response type for the Query/ConsensusState +// RPC method +type QueryConsensusStateResponse struct { + // consensus state associated with the client identifier at the given height + ConsensusState *types.Any `protobuf:"bytes,1,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryConsensusStateResponse) Reset() { *m = QueryConsensusStateResponse{} } +func (m *QueryConsensusStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsensusStateResponse) ProtoMessage() {} +func (*QueryConsensusStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{5} +} +func (m *QueryConsensusStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsensusStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsensusStateResponse.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 *QueryConsensusStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsensusStateResponse.Merge(m, src) +} +func (m *QueryConsensusStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsensusStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsensusStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsensusStateResponse proto.InternalMessageInfo + +func (m *QueryConsensusStateResponse) GetConsensusState() *types.Any { + if m != nil { + return m.ConsensusState + } + return nil +} + +func (m *QueryConsensusStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryConsensusStateResponse) GetProofHeight() Height { + if m != nil { + return m.ProofHeight + } + return Height{} +} + +// QueryConsensusStatesRequest is the request type for the Query/ConsensusStates +// RPC method. +type QueryConsensusStatesRequest struct { + // client identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConsensusStatesRequest) Reset() { *m = QueryConsensusStatesRequest{} } +func (m *QueryConsensusStatesRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConsensusStatesRequest) ProtoMessage() {} +func (*QueryConsensusStatesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{6} +} +func (m *QueryConsensusStatesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsensusStatesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsensusStatesRequest.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 *QueryConsensusStatesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsensusStatesRequest.Merge(m, src) +} +func (m *QueryConsensusStatesRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConsensusStatesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsensusStatesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsensusStatesRequest proto.InternalMessageInfo + +func (m *QueryConsensusStatesRequest) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *QueryConsensusStatesRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryConsensusStatesResponse is the response type for the +// Query/ConsensusStates RPC method +type QueryConsensusStatesResponse struct { + // consensus states associated with the identifier + ConsensusStates []ConsensusStateWithHeight `protobuf:"bytes,1,rep,name=consensus_states,json=consensusStates,proto3" json:"consensus_states"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConsensusStatesResponse) Reset() { *m = QueryConsensusStatesResponse{} } +func (m *QueryConsensusStatesResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConsensusStatesResponse) ProtoMessage() {} +func (*QueryConsensusStatesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{7} +} +func (m *QueryConsensusStatesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConsensusStatesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConsensusStatesResponse.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 *QueryConsensusStatesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConsensusStatesResponse.Merge(m, src) +} +func (m *QueryConsensusStatesResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConsensusStatesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConsensusStatesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConsensusStatesResponse proto.InternalMessageInfo + +func (m *QueryConsensusStatesResponse) GetConsensusStates() []ConsensusStateWithHeight { + if m != nil { + return m.ConsensusStates + } + return nil +} + +func (m *QueryConsensusStatesResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryClientParamsRequest is the request type for the Query/ClientParams RPC method. +type QueryClientParamsRequest struct { +} + +func (m *QueryClientParamsRequest) Reset() { *m = QueryClientParamsRequest{} } +func (m *QueryClientParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryClientParamsRequest) ProtoMessage() {} +func (*QueryClientParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{8} +} +func (m *QueryClientParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientParamsRequest.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 *QueryClientParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientParamsRequest.Merge(m, src) +} +func (m *QueryClientParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryClientParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientParamsRequest proto.InternalMessageInfo + +// QueryClientParamsResponse is the response type for the Query/ClientParams RPC method. +type QueryClientParamsResponse struct { + // params defines the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *QueryClientParamsResponse) Reset() { *m = QueryClientParamsResponse{} } +func (m *QueryClientParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryClientParamsResponse) ProtoMessage() {} +func (*QueryClientParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dc42cdfd1d52d76e, []int{9} +} +func (m *QueryClientParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientParamsResponse.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 *QueryClientParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientParamsResponse.Merge(m, src) +} +func (m *QueryClientParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryClientParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientParamsResponse proto.InternalMessageInfo + +func (m *QueryClientParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + +func init() { + proto.RegisterType((*QueryClientStateRequest)(nil), "ibc.core.client.v1.QueryClientStateRequest") + proto.RegisterType((*QueryClientStateResponse)(nil), "ibc.core.client.v1.QueryClientStateResponse") + proto.RegisterType((*QueryClientStatesRequest)(nil), "ibc.core.client.v1.QueryClientStatesRequest") + proto.RegisterType((*QueryClientStatesResponse)(nil), "ibc.core.client.v1.QueryClientStatesResponse") + proto.RegisterType((*QueryConsensusStateRequest)(nil), "ibc.core.client.v1.QueryConsensusStateRequest") + proto.RegisterType((*QueryConsensusStateResponse)(nil), "ibc.core.client.v1.QueryConsensusStateResponse") + proto.RegisterType((*QueryConsensusStatesRequest)(nil), "ibc.core.client.v1.QueryConsensusStatesRequest") + proto.RegisterType((*QueryConsensusStatesResponse)(nil), "ibc.core.client.v1.QueryConsensusStatesResponse") + proto.RegisterType((*QueryClientParamsRequest)(nil), "ibc.core.client.v1.QueryClientParamsRequest") + proto.RegisterType((*QueryClientParamsResponse)(nil), "ibc.core.client.v1.QueryClientParamsResponse") +} + +func init() { proto.RegisterFile("ibc/core/client/v1/query.proto", fileDescriptor_dc42cdfd1d52d76e) } + +var fileDescriptor_dc42cdfd1d52d76e = []byte{ + // 824 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4d, 0x4f, 0xdb, 0x48, + 0x18, 0xce, 0xf0, 0x25, 0x98, 0x04, 0xb2, 0x1a, 0xa1, 0xdd, 0x60, 0x90, 0x89, 0xbc, 0x12, 0x64, + 0x77, 0x61, 0x86, 0x64, 0x3f, 0x90, 0x56, 0xe2, 0xb0, 0x20, 0xb1, 0xe5, 0xd2, 0x82, 0x7b, 0xa8, + 0x54, 0xa9, 0x42, 0xb6, 0x33, 0x38, 0x16, 0xc4, 0x13, 0x32, 0x4e, 0x54, 0x84, 0xb8, 0xf0, 0x07, + 0x5a, 0xa9, 0xc7, 0x5e, 0x7b, 0xea, 0xa1, 0xaa, 0xd4, 0x43, 0xff, 0x41, 0xc5, 0x11, 0xa9, 0x3d, + 0xf4, 0xd4, 0x56, 0xc0, 0xbf, 0xe8, 0xa5, 0xf2, 0xcc, 0x18, 0xec, 0xc4, 0x08, 0x0b, 0xb5, 0xa7, + 0xd8, 0xef, 0xd7, 0x3c, 0xcf, 0xf3, 0xbe, 0xef, 0x38, 0x50, 0xf7, 0x6c, 0x87, 0x38, 0xac, 0x4d, + 0x89, 0xb3, 0xe7, 0x51, 0x3f, 0x20, 0xdd, 0x2a, 0xd9, 0xef, 0xd0, 0xf6, 0x01, 0x6e, 0xb5, 0x59, + 0xc0, 0x10, 0xf2, 0x6c, 0x07, 0x87, 0x7e, 0x2c, 0xfd, 0xb8, 0x5b, 0xd5, 0x7e, 0x77, 0x18, 0x6f, + 0x32, 0x4e, 0x6c, 0x8b, 0x53, 0x19, 0x4c, 0xba, 0x55, 0x9b, 0x06, 0x56, 0x95, 0xb4, 0x2c, 0xd7, + 0xf3, 0xad, 0xc0, 0x63, 0xbe, 0xcc, 0xd7, 0x66, 0x53, 0xea, 0xab, 0x4a, 0x32, 0x60, 0xca, 0x65, + 0xcc, 0xdd, 0xa3, 0x44, 0xbc, 0xd9, 0x9d, 0x1d, 0x62, 0xf9, 0xea, 0x6c, 0x6d, 0x46, 0xb9, 0xac, + 0x96, 0x47, 0x2c, 0xdf, 0x67, 0x81, 0x28, 0xcc, 0x95, 0x77, 0xd2, 0x65, 0x2e, 0x13, 0x8f, 0x24, + 0x7c, 0x92, 0x56, 0xe3, 0x1f, 0xf8, 0xcb, 0x56, 0x88, 0x68, 0x4d, 0x9c, 0x71, 0x3f, 0xb0, 0x02, + 0x6a, 0xd2, 0xfd, 0x0e, 0xe5, 0x01, 0x9a, 0x86, 0x63, 0xf2, 0xe4, 0x6d, 0xaf, 0x5e, 0x02, 0x65, + 0x50, 0x19, 0x33, 0x47, 0xa5, 0x61, 0xa3, 0x6e, 0xbc, 0x02, 0xb0, 0xd4, 0x9f, 0xc8, 0x5b, 0xcc, + 0xe7, 0x14, 0x2d, 0xc3, 0x82, 0xca, 0xe4, 0xa1, 0x5d, 0x24, 0xe7, 0x6b, 0x93, 0x58, 0xe2, 0xc3, + 0x11, 0x74, 0xfc, 0x9f, 0x7f, 0x60, 0xe6, 0x9d, 0xab, 0x02, 0x68, 0x12, 0x0e, 0xb7, 0xda, 0x8c, + 0xed, 0x94, 0x06, 0xca, 0xa0, 0x52, 0x30, 0xe5, 0x0b, 0x5a, 0x83, 0x05, 0xf1, 0xb0, 0xdd, 0xa0, + 0x9e, 0xdb, 0x08, 0x4a, 0x83, 0xa2, 0x9c, 0x86, 0xfb, 0xa5, 0xc6, 0x77, 0x44, 0xc4, 0xea, 0xd0, + 0xc9, 0xa7, 0xd9, 0x9c, 0x99, 0x17, 0x59, 0xd2, 0x64, 0xd8, 0xfd, 0x78, 0x79, 0xc4, 0x74, 0x1d, + 0xc2, 0xab, 0x46, 0x28, 0xb4, 0x73, 0x58, 0x76, 0x0d, 0x87, 0x5d, 0xc3, 0xb2, 0xc5, 0xaa, 0x6b, + 0x78, 0xd3, 0x72, 0x23, 0x95, 0xcc, 0x58, 0xa6, 0xf1, 0x01, 0xc0, 0xa9, 0x94, 0x43, 0x94, 0x2a, + 0x3e, 0x1c, 0x8f, 0xab, 0xc2, 0x4b, 0xa0, 0x3c, 0x58, 0xc9, 0xd7, 0x7e, 0x4b, 0xe3, 0xb1, 0x51, + 0xa7, 0x7e, 0xe0, 0xed, 0x78, 0xb4, 0x1e, 0x2b, 0xb5, 0xaa, 0x87, 0xb4, 0x5e, 0x7e, 0x9e, 0xfd, + 0x39, 0xd5, 0xcd, 0xcd, 0x42, 0x4c, 0x4b, 0x8e, 0xfe, 0x4f, 0xb0, 0x1a, 0x10, 0xac, 0xe6, 0x6f, + 0x64, 0x25, 0xc1, 0x26, 0x68, 0xbd, 0x06, 0x50, 0x93, 0xb4, 0x42, 0x97, 0xcf, 0x3b, 0x3c, 0xf3, + 0x9c, 0xa0, 0x79, 0x58, 0x6c, 0xd3, 0xae, 0xc7, 0x3d, 0xe6, 0x6f, 0xfb, 0x9d, 0xa6, 0x4d, 0xdb, + 0x02, 0xc9, 0x90, 0x39, 0x11, 0x99, 0xef, 0x0a, 0x6b, 0x22, 0x30, 0xd6, 0xe7, 0x58, 0xa0, 0x6c, + 0x24, 0xfa, 0x15, 0x8e, 0xef, 0x85, 0xfc, 0x82, 0x28, 0x6c, 0xa8, 0x0c, 0x2a, 0xa3, 0x66, 0x41, + 0x1a, 0x55, 0xb7, 0xdf, 0x02, 0x38, 0x9d, 0x0a, 0x59, 0xf5, 0x62, 0x05, 0x16, 0x9d, 0xc8, 0x93, + 0x61, 0x48, 0x27, 0x9c, 0x44, 0x99, 0x1f, 0x39, 0xa7, 0xc7, 0xe9, 0xc8, 0x79, 0x26, 0xb5, 0xd7, + 0x53, 0x5a, 0x7e, 0x9b, 0x41, 0x7e, 0x07, 0xe0, 0x4c, 0x3a, 0x08, 0xa5, 0xdf, 0x23, 0xf8, 0x53, + 0x8f, 0x7e, 0xd1, 0x38, 0x2f, 0xa4, 0xd1, 0x4d, 0x96, 0x79, 0xe0, 0x05, 0x8d, 0x84, 0x00, 0xc5, + 0xa4, 0xbc, 0xdf, 0x71, 0x74, 0xb5, 0xc4, 0xd6, 0x6f, 0x5a, 0x6d, 0xab, 0x19, 0x29, 0x69, 0xdc, + 0x4b, 0x2c, 0x6b, 0xe4, 0x53, 0x04, 0x6b, 0x70, 0xa4, 0x25, 0x2c, 0x6a, 0x2e, 0x52, 0xbb, 0xa8, + 0x72, 0x54, 0x64, 0xed, 0xeb, 0x08, 0x1c, 0x16, 0x15, 0xd1, 0x0b, 0x00, 0xf3, 0xb1, 0xcd, 0x44, + 0x7f, 0xa4, 0x65, 0x5f, 0x73, 0xef, 0x6a, 0x0b, 0xd9, 0x82, 0x25, 0x50, 0xe3, 0xdf, 0xe3, 0xf7, + 0x17, 0xcf, 0x06, 0xfe, 0x42, 0x35, 0xd2, 0xff, 0xe5, 0x90, 0xdf, 0x98, 0xc4, 0xa5, 0x43, 0x0e, + 0x2f, 0xa7, 0xe7, 0x08, 0x3d, 0x07, 0xb0, 0x10, 0xbf, 0x40, 0x50, 0xa6, 0xa3, 0x23, 0x01, 0xb5, + 0xc5, 0x8c, 0xd1, 0x0a, 0x29, 0x16, 0x48, 0x2b, 0x68, 0x2e, 0x1b, 0x52, 0x74, 0x01, 0xe0, 0x44, + 0x72, 0x70, 0x10, 0xbe, 0xfe, 0xc4, 0xb4, 0xab, 0x49, 0x23, 0x99, 0xe3, 0x15, 0xc6, 0x7d, 0x81, + 0x71, 0x17, 0x79, 0xd7, 0x63, 0xec, 0x19, 0xfb, 0xb8, 0xa0, 0x24, 0xba, 0xaa, 0xc8, 0x61, 0xcf, + 0xa5, 0x77, 0x44, 0xe4, 0x9d, 0x10, 0x73, 0x48, 0xc3, 0x11, 0x7a, 0x03, 0x60, 0xb1, 0x67, 0xcd, + 0x50, 0x56, 0xdc, 0x97, 0xad, 0x58, 0xca, 0x9e, 0xa0, 0x98, 0xae, 0x08, 0xa6, 0xcb, 0xe8, 0xef, + 0x5b, 0x31, 0x45, 0x4f, 0x2e, 0x47, 0x47, 0x2e, 0xc1, 0x8d, 0xa3, 0x93, 0xd8, 0xbd, 0x1b, 0x47, + 0x27, 0xb9, 0x8d, 0x86, 0x21, 0xc0, 0xce, 0x20, 0x4d, 0x82, 0x4d, 0xe2, 0x94, 0xdb, 0xb7, 0xba, + 0x75, 0x72, 0xa6, 0x83, 0xd3, 0x33, 0x1d, 0x7c, 0x39, 0xd3, 0xc1, 0xd3, 0x73, 0x3d, 0x77, 0x7a, + 0xae, 0xe7, 0x3e, 0x9e, 0xeb, 0xb9, 0x87, 0xcb, 0xae, 0x17, 0x34, 0x3a, 0x36, 0x76, 0x58, 0x93, + 0xa8, 0xbf, 0x62, 0xf2, 0x67, 0x91, 0xd7, 0x77, 0xc9, 0xe3, 0x2b, 0x01, 0x96, 0x6a, 0x8b, 0xaa, + 0x76, 0x70, 0xd0, 0xa2, 0xdc, 0x1e, 0x11, 0x1f, 0x81, 0x3f, 0xbf, 0x05, 0x00, 0x00, 0xff, 0xff, + 0xa7, 0x0d, 0x7c, 0x62, 0xf5, 0x09, 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 { + // ClientState queries an IBC light client. + ClientState(ctx context.Context, in *QueryClientStateRequest, opts ...grpc.CallOption) (*QueryClientStateResponse, error) + // ClientStates queries all the IBC light clients of a chain. + ClientStates(ctx context.Context, in *QueryClientStatesRequest, opts ...grpc.CallOption) (*QueryClientStatesResponse, error) + // ConsensusState queries a consensus state associated with a client state at + // a given height. + ConsensusState(ctx context.Context, in *QueryConsensusStateRequest, opts ...grpc.CallOption) (*QueryConsensusStateResponse, error) + // ConsensusStates queries all the consensus state associated with a given + // client. + ConsensusStates(ctx context.Context, in *QueryConsensusStatesRequest, opts ...grpc.CallOption) (*QueryConsensusStatesResponse, error) + // ClientParams queries all parameters of the ibc client. + ClientParams(ctx context.Context, in *QueryClientParamsRequest, opts ...grpc.CallOption) (*QueryClientParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) ClientState(ctx context.Context, in *QueryClientStateRequest, opts ...grpc.CallOption) (*QueryClientStateResponse, error) { + out := new(QueryClientStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Query/ClientState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ClientStates(ctx context.Context, in *QueryClientStatesRequest, opts ...grpc.CallOption) (*QueryClientStatesResponse, error) { + out := new(QueryClientStatesResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Query/ClientStates", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConsensusState(ctx context.Context, in *QueryConsensusStateRequest, opts ...grpc.CallOption) (*QueryConsensusStateResponse, error) { + out := new(QueryConsensusStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Query/ConsensusState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConsensusStates(ctx context.Context, in *QueryConsensusStatesRequest, opts ...grpc.CallOption) (*QueryConsensusStatesResponse, error) { + out := new(QueryConsensusStatesResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Query/ConsensusStates", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ClientParams(ctx context.Context, in *QueryClientParamsRequest, opts ...grpc.CallOption) (*QueryClientParamsResponse, error) { + out := new(QueryClientParamsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Query/ClientParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // ClientState queries an IBC light client. + ClientState(context.Context, *QueryClientStateRequest) (*QueryClientStateResponse, error) + // ClientStates queries all the IBC light clients of a chain. + ClientStates(context.Context, *QueryClientStatesRequest) (*QueryClientStatesResponse, error) + // ConsensusState queries a consensus state associated with a client state at + // a given height. + ConsensusState(context.Context, *QueryConsensusStateRequest) (*QueryConsensusStateResponse, error) + // ConsensusStates queries all the consensus state associated with a given + // client. + ConsensusStates(context.Context, *QueryConsensusStatesRequest) (*QueryConsensusStatesResponse, error) + // ClientParams queries all parameters of the ibc client. + ClientParams(context.Context, *QueryClientParamsRequest) (*QueryClientParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) ClientState(ctx context.Context, req *QueryClientStateRequest) (*QueryClientStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClientState not implemented") +} +func (*UnimplementedQueryServer) ClientStates(ctx context.Context, req *QueryClientStatesRequest) (*QueryClientStatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClientStates not implemented") +} +func (*UnimplementedQueryServer) ConsensusState(ctx context.Context, req *QueryConsensusStateRequest) (*QueryConsensusStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConsensusState not implemented") +} +func (*UnimplementedQueryServer) ConsensusStates(ctx context.Context, req *QueryConsensusStatesRequest) (*QueryConsensusStatesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConsensusStates not implemented") +} +func (*UnimplementedQueryServer) ClientParams(ctx context.Context, req *QueryClientParamsRequest) (*QueryClientParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClientParams not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_ClientState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryClientStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ClientState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Query/ClientState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ClientState(ctx, req.(*QueryClientStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ClientStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryClientStatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ClientStates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Query/ClientStates", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ClientStates(ctx, req.(*QueryClientStatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConsensusState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsensusStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConsensusState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Query/ConsensusState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConsensusState(ctx, req.(*QueryConsensusStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConsensusStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConsensusStatesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConsensusStates(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Query/ConsensusStates", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConsensusStates(ctx, req.(*QueryConsensusStatesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ClientParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryClientParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ClientParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Query/ClientParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ClientParams(ctx, req.(*QueryClientParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.client.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ClientState", + Handler: _Query_ClientState_Handler, + }, + { + MethodName: "ClientStates", + Handler: _Query_ClientStates_Handler, + }, + { + MethodName: "ConsensusState", + Handler: _Query_ConsensusState_Handler, + }, + { + MethodName: "ConsensusStates", + Handler: _Query_ConsensusStates_Handler, + }, + { + MethodName: "ClientParams", + Handler: _Query_ClientParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/client/v1/query.proto", +} + +func (m *QueryClientStateRequest) 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 *QueryClientStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryClientStateResponse) 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 *QueryClientStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.ClientState != nil { + { + size, err := m.ClientState.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 *QueryClientStatesRequest) 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 *QueryClientStatesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientStatesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryClientStatesResponse) 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 *QueryClientStatesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientStatesResponse) 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.ClientStates) > 0 { + for iNdEx := len(m.ClientStates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ClientStates[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 *QueryConsensusStateRequest) 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 *QueryConsensusStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsensusStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.LatestHeight { + i-- + if m.LatestHeight { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.RevisionHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionHeight)) + i-- + dAtA[i] = 0x18 + } + if m.RevisionNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionNumber)) + i-- + dAtA[i] = 0x10 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsensusStateResponse) 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 *QueryConsensusStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsensusStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.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 *QueryConsensusStatesRequest) 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 *QueryConsensusStatesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsensusStatesRequest) 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.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConsensusStatesResponse) 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 *QueryConsensusStatesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConsensusStatesResponse) 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.ConsensusStates) > 0 { + for iNdEx := len(m.ConsensusStates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ConsensusStates[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 *QueryClientParamsRequest) 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 *QueryClientParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryClientParamsResponse) 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 *QueryClientParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.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 *QueryClientStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryClientStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryClientStatesRequest) 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 *QueryClientStatesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ClientStates) > 0 { + for _, e := range m.ClientStates { + 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 *QueryConsensusStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.RevisionNumber != 0 { + n += 1 + sovQuery(uint64(m.RevisionNumber)) + } + if m.RevisionHeight != 0 { + n += 1 + sovQuery(uint64(m.RevisionHeight)) + } + if m.LatestHeight { + n += 2 + } + return n +} + +func (m *QueryConsensusStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConsensusStatesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + 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 *QueryConsensusStatesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ConsensusStates) > 0 { + for _, e := range m.ConsensusStates { + 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 *QueryClientParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryClientParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.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 *QueryClientStateRequest) 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: QueryClientStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = 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 *QueryClientStateResponse) 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: QueryClientStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", 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.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryClientStatesRequest) 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: QueryClientStatesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientStatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryClientStatesResponse) 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: QueryClientStatesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientStatesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientStates", 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.ClientStates = append(m.ClientStates, IdentifiedClientState{}) + if err := m.ClientStates[len(m.ClientStates)-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 *QueryConsensusStateRequest) 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: QueryConsensusStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsensusStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionNumber", wireType) + } + m.RevisionNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHeight", wireType) + } + m.RevisionHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LatestHeight", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.LatestHeight = bool(v != 0) + 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 *QueryConsensusStateResponse) 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: QueryConsensusStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsensusStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", 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.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryConsensusStatesRequest) 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: QueryConsensusStatesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsensusStatesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = 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 *QueryConsensusStatesResponse) 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: QueryConsensusStatesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConsensusStatesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusStates", 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.ConsensusStates = append(m.ConsensusStates, ConsensusStateWithHeight{}) + if err := m.ConsensusStates[len(m.ConsensusStates)-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 *QueryClientParamsRequest) 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: QueryClientParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryClientParamsResponse) 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: QueryClientParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientParamsResponse: 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) + } + 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.Params == nil { + m.Params = &Params{} + } + if err := m.Params.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/ibc/core/02-client/types/query.pb.gw.go b/x/ibc/core/02-client/types/query.pb.gw.go new file mode 100644 index 000000000000..d4414d5ea33b --- /dev/null +++ b/x/ibc/core/02-client/types/query.pb.gw.go @@ -0,0 +1,602 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ibc/core/client/v1/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_ClientState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + msg, err := client.ClientState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ClientState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + msg, err := server.ClientState(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ClientStates_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_ClientStates_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientStatesRequest + 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_ClientStates_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ClientStates(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ClientStates_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientStatesRequest + 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_ClientStates_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ClientStates(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ConsensusState_0 = &utilities.DoubleArray{Encoding: map[string]int{"client_id": 0, "revision_number": 1, "revision_height": 2}, Base: []int{1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 1, 1, 2, 3, 4}} +) + +func request_Query_ConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", 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_ConsensusState_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ConsensusState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", 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_ConsensusState_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ConsensusState(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ConsensusStates_0 = &utilities.DoubleArray{Encoding: map[string]int{"client_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ConsensusStates_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsensusStatesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", 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_ConsensusStates_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ConsensusStates(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConsensusStates_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConsensusStatesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", 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_ConsensusStates_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ConsensusStates(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ClientParams_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.ClientParams(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ClientParams_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.ClientParams(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_ClientState_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_ClientState_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_ClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientStates_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_ClientStates_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_ClientStates_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsensusState_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_ConsensusState_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_ConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsensusStates_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_ConsensusStates_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_ConsensusStates_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientParams_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_ClientParams_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_ClientParams_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_ClientState_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_ClientState_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_ClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientStates_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_ClientStates_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_ClientStates_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsensusState_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_ConsensusState_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_ConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConsensusStates_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_ConsensusStates_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_ConsensusStates_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientParams_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_ClientParams_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_ClientParams_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_ClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "client_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ClientStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "client", "v1beta1", "client_states"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ConsensusStates_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "client", "v1beta1", "consensus_states", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ClientParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"ibc", "client", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_ClientState_0 = runtime.ForwardResponseMessage + + forward_Query_ClientStates_0 = runtime.ForwardResponseMessage + + forward_Query_ConsensusState_0 = runtime.ForwardResponseMessage + + forward_Query_ConsensusStates_0 = runtime.ForwardResponseMessage + + forward_Query_ClientParams_0 = runtime.ForwardResponseMessage +) diff --git a/x/ibc/core/02-client/types/tx.pb.go b/x/ibc/core/02-client/types/tx.pb.go new file mode 100644 index 000000000000..a314223fcff9 --- /dev/null +++ b/x/ibc/core/02-client/types/tx.pb.go @@ -0,0 +1,2071 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/client/v1/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" + 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 + +// MsgCreateClient defines a message to create an IBC client +type MsgCreateClient struct { + // light client state + ClientState *types.Any `protobuf:"bytes,1,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + // consensus state associated with the client that corresponds to a given + // height. + ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` + // signer address + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgCreateClient) Reset() { *m = MsgCreateClient{} } +func (m *MsgCreateClient) String() string { return proto.CompactTextString(m) } +func (*MsgCreateClient) ProtoMessage() {} +func (*MsgCreateClient) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{0} +} +func (m *MsgCreateClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateClient.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 *MsgCreateClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateClient.Merge(m, src) +} +func (m *MsgCreateClient) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateClient) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateClient.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateClient proto.InternalMessageInfo + +// MsgCreateClientResponse defines the Msg/CreateClient response type. +type MsgCreateClientResponse struct { +} + +func (m *MsgCreateClientResponse) Reset() { *m = MsgCreateClientResponse{} } +func (m *MsgCreateClientResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateClientResponse) ProtoMessage() {} +func (*MsgCreateClientResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{1} +} +func (m *MsgCreateClientResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateClientResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateClientResponse.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 *MsgCreateClientResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateClientResponse.Merge(m, src) +} +func (m *MsgCreateClientResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateClientResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateClientResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateClientResponse proto.InternalMessageInfo + +// MsgUpdateClient defines an sdk.Msg to update a IBC client state using +// the given header. +type MsgUpdateClient struct { + // client unique identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // header to update the light client + Header *types.Any `protobuf:"bytes,2,opt,name=header,proto3" json:"header,omitempty"` + // signer address + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgUpdateClient) Reset() { *m = MsgUpdateClient{} } +func (m *MsgUpdateClient) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateClient) ProtoMessage() {} +func (*MsgUpdateClient) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{2} +} +func (m *MsgUpdateClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateClient.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 *MsgUpdateClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateClient.Merge(m, src) +} +func (m *MsgUpdateClient) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateClient) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateClient.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateClient proto.InternalMessageInfo + +// MsgUpdateClientResponse defines the Msg/UpdateClient response type. +type MsgUpdateClientResponse struct { +} + +func (m *MsgUpdateClientResponse) Reset() { *m = MsgUpdateClientResponse{} } +func (m *MsgUpdateClientResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateClientResponse) ProtoMessage() {} +func (*MsgUpdateClientResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{3} +} +func (m *MsgUpdateClientResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateClientResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateClientResponse.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 *MsgUpdateClientResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateClientResponse.Merge(m, src) +} +func (m *MsgUpdateClientResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateClientResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateClientResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateClientResponse proto.InternalMessageInfo + +// MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client state +type MsgUpgradeClient struct { + // client unique identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // upgraded client state + ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + // upgraded consensus state, only contains enough information to serve as a basis of trust in update logic + ConsensusState *types.Any `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` + // proof that old chain committed to new client + ProofUpgradeClient []byte `protobuf:"bytes,4,opt,name=proof_upgrade_client,json=proofUpgradeClient,proto3" json:"proof_upgrade_client,omitempty" yaml:"proof_upgrade_client"` + // proof that old chain committed to new consensus state + ProofUpgradeConsensusState []byte `protobuf:"bytes,5,opt,name=proof_upgrade_consensus_state,json=proofUpgradeConsensusState,proto3" json:"proof_upgrade_consensus_state,omitempty" yaml:"proof_upgrade_consensus_state"` + // signer address + Signer string `protobuf:"bytes,6,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgUpgradeClient) Reset() { *m = MsgUpgradeClient{} } +func (m *MsgUpgradeClient) String() string { return proto.CompactTextString(m) } +func (*MsgUpgradeClient) ProtoMessage() {} +func (*MsgUpgradeClient) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{4} +} +func (m *MsgUpgradeClient) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpgradeClient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpgradeClient.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 *MsgUpgradeClient) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpgradeClient.Merge(m, src) +} +func (m *MsgUpgradeClient) XXX_Size() int { + return m.Size() +} +func (m *MsgUpgradeClient) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpgradeClient.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpgradeClient proto.InternalMessageInfo + +// MsgUpgradeClientResponse defines the Msg/UpgradeClient response type. +type MsgUpgradeClientResponse struct { +} + +func (m *MsgUpgradeClientResponse) Reset() { *m = MsgUpgradeClientResponse{} } +func (m *MsgUpgradeClientResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpgradeClientResponse) ProtoMessage() {} +func (*MsgUpgradeClientResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{5} +} +func (m *MsgUpgradeClientResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpgradeClientResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpgradeClientResponse.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 *MsgUpgradeClientResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpgradeClientResponse.Merge(m, src) +} +func (m *MsgUpgradeClientResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpgradeClientResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpgradeClientResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpgradeClientResponse proto.InternalMessageInfo + +// MsgSubmitMisbehaviour defines an sdk.Msg type that submits Evidence for +// light client misbehaviour. +type MsgSubmitMisbehaviour struct { + // client unique identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // misbehaviour used for freezing the light client + Misbehaviour *types.Any `protobuf:"bytes,2,opt,name=misbehaviour,proto3" json:"misbehaviour,omitempty"` + // signer address + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgSubmitMisbehaviour) Reset() { *m = MsgSubmitMisbehaviour{} } +func (m *MsgSubmitMisbehaviour) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitMisbehaviour) ProtoMessage() {} +func (*MsgSubmitMisbehaviour) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{6} +} +func (m *MsgSubmitMisbehaviour) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitMisbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitMisbehaviour.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 *MsgSubmitMisbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitMisbehaviour.Merge(m, src) +} +func (m *MsgSubmitMisbehaviour) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitMisbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitMisbehaviour.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitMisbehaviour proto.InternalMessageInfo + +// MsgSubmitMisbehaviourResponse defines the Msg/SubmitMisbehaviour response type. +type MsgSubmitMisbehaviourResponse struct { +} + +func (m *MsgSubmitMisbehaviourResponse) Reset() { *m = MsgSubmitMisbehaviourResponse{} } +func (m *MsgSubmitMisbehaviourResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSubmitMisbehaviourResponse) ProtoMessage() {} +func (*MsgSubmitMisbehaviourResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb5dc4651eb49a04, []int{7} +} +func (m *MsgSubmitMisbehaviourResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSubmitMisbehaviourResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSubmitMisbehaviourResponse.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 *MsgSubmitMisbehaviourResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSubmitMisbehaviourResponse.Merge(m, src) +} +func (m *MsgSubmitMisbehaviourResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSubmitMisbehaviourResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSubmitMisbehaviourResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSubmitMisbehaviourResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgCreateClient)(nil), "ibc.core.client.v1.MsgCreateClient") + proto.RegisterType((*MsgCreateClientResponse)(nil), "ibc.core.client.v1.MsgCreateClientResponse") + proto.RegisterType((*MsgUpdateClient)(nil), "ibc.core.client.v1.MsgUpdateClient") + proto.RegisterType((*MsgUpdateClientResponse)(nil), "ibc.core.client.v1.MsgUpdateClientResponse") + proto.RegisterType((*MsgUpgradeClient)(nil), "ibc.core.client.v1.MsgUpgradeClient") + proto.RegisterType((*MsgUpgradeClientResponse)(nil), "ibc.core.client.v1.MsgUpgradeClientResponse") + proto.RegisterType((*MsgSubmitMisbehaviour)(nil), "ibc.core.client.v1.MsgSubmitMisbehaviour") + proto.RegisterType((*MsgSubmitMisbehaviourResponse)(nil), "ibc.core.client.v1.MsgSubmitMisbehaviourResponse") +} + +func init() { proto.RegisterFile("ibc/core/client/v1/tx.proto", fileDescriptor_cb5dc4651eb49a04) } + +var fileDescriptor_cb5dc4651eb49a04 = []byte{ + // 601 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x3f, 0x6f, 0xd3, 0x40, + 0x1c, 0x8d, 0x1b, 0x88, 0x9a, 0x6b, 0xa0, 0x95, 0x09, 0x6d, 0xea, 0xaa, 0x76, 0x64, 0x3a, 0x04, + 0xd1, 0xfa, 0x48, 0x18, 0x40, 0xdd, 0x48, 0x27, 0x86, 0x48, 0xd4, 0x15, 0x03, 0x2c, 0xc1, 0x7f, + 0xae, 0x97, 0x53, 0x13, 0x5f, 0xe4, 0xb3, 0xa3, 0xe6, 0x1b, 0x30, 0x32, 0xf0, 0x01, 0x2a, 0x06, + 0x3e, 0x0b, 0x63, 0x07, 0x06, 0xa6, 0xa8, 0x4a, 0x16, 0xe6, 0x7c, 0x02, 0x14, 0x9f, 0x13, 0x62, + 0xd7, 0x8e, 0x2c, 0xa0, 0x53, 0x7c, 0xfe, 0xbd, 0x7b, 0xef, 0xf7, 0xf2, 0x7e, 0xe7, 0x03, 0x7b, + 0xc4, 0xb4, 0xa0, 0x45, 0x5d, 0x04, 0xad, 0x2e, 0x41, 0x8e, 0x07, 0x07, 0x75, 0xe8, 0x5d, 0x6a, + 0x7d, 0x97, 0x7a, 0x54, 0x14, 0x89, 0x69, 0x69, 0xb3, 0xa2, 0xc6, 0x8b, 0xda, 0xa0, 0x2e, 0x95, + 0x31, 0xc5, 0x34, 0x28, 0xc3, 0xd9, 0x13, 0x47, 0x4a, 0xbb, 0x98, 0x52, 0xdc, 0x45, 0x30, 0x58, + 0x99, 0xfe, 0x39, 0x34, 0x9c, 0x61, 0x58, 0x52, 0x12, 0x14, 0x42, 0xba, 0x00, 0xa0, 0xde, 0x08, + 0x60, 0xb3, 0xc5, 0xf0, 0x89, 0x8b, 0x0c, 0x0f, 0x9d, 0x04, 0x15, 0xf1, 0x2d, 0x28, 0x71, 0x4c, + 0x9b, 0x79, 0x86, 0x87, 0x2a, 0x42, 0x55, 0xa8, 0x6d, 0x34, 0xca, 0x1a, 0x97, 0xd1, 0xe6, 0x32, + 0xda, 0x6b, 0x67, 0xd8, 0xdc, 0x99, 0x8e, 0x94, 0x47, 0x43, 0xa3, 0xd7, 0x3d, 0x56, 0x97, 0xf7, + 0xa8, 0xfa, 0x06, 0x5f, 0x9e, 0xcd, 0x56, 0xe2, 0x7b, 0xb0, 0x69, 0x51, 0x87, 0x21, 0x87, 0xf9, + 0x2c, 0x24, 0x5d, 0x5b, 0x41, 0x2a, 0x4d, 0x47, 0xca, 0x76, 0x48, 0x1a, 0xdd, 0xa6, 0xea, 0x0f, + 0x17, 0x6f, 0x38, 0xf5, 0x36, 0x28, 0x30, 0x82, 0x1d, 0xe4, 0x56, 0xf2, 0x55, 0xa1, 0x56, 0xd4, + 0xc3, 0xd5, 0xf1, 0xfa, 0xa7, 0x2b, 0x25, 0xf7, 0xeb, 0x4a, 0xc9, 0xa9, 0xbb, 0x60, 0x27, 0xe6, + 0x50, 0x47, 0xac, 0x3f, 0x63, 0x51, 0xbf, 0x70, 0xf7, 0xef, 0xfa, 0xf6, 0x1f, 0xf7, 0x75, 0x50, + 0x0c, 0x9d, 0x10, 0x3b, 0xb0, 0x5e, 0x6c, 0x96, 0xa7, 0x23, 0x65, 0x2b, 0x62, 0x92, 0xd8, 0xaa, + 0xbe, 0xce, 0x9f, 0xdf, 0xd8, 0xe2, 0x21, 0x28, 0x74, 0x90, 0x61, 0x23, 0x77, 0x95, 0x2b, 0x3d, + 0xc4, 0x64, 0xee, 0x78, 0xb9, 0xab, 0x45, 0xc7, 0x3f, 0xf2, 0x60, 0x2b, 0xa8, 0x61, 0xd7, 0xb0, + 0xff, 0xa1, 0xe5, 0x78, 0xc6, 0x6b, 0x77, 0x91, 0x71, 0xfe, 0x3f, 0x65, 0x7c, 0x0a, 0xca, 0x7d, + 0x97, 0xd2, 0xf3, 0xb6, 0xcf, 0x6d, 0xb7, 0xb9, 0x6e, 0xe5, 0x5e, 0x55, 0xa8, 0x95, 0x9a, 0xca, + 0x74, 0xa4, 0xec, 0x71, 0xa6, 0x24, 0x94, 0xaa, 0x8b, 0xc1, 0xeb, 0xe8, 0x5f, 0x76, 0x01, 0xf6, + 0x63, 0xe0, 0x58, 0xef, 0xf7, 0x03, 0xee, 0xda, 0x74, 0xa4, 0x1c, 0x24, 0x72, 0xc7, 0x7b, 0x96, + 0x22, 0x22, 0x69, 0x33, 0x5a, 0x48, 0x49, 0x5c, 0x02, 0x95, 0x78, 0xaa, 0x8b, 0xc8, 0xbf, 0x09, + 0xe0, 0x71, 0x8b, 0xe1, 0x33, 0xdf, 0xec, 0x11, 0xaf, 0x45, 0x98, 0x89, 0x3a, 0xc6, 0x80, 0x50, + 0xdf, 0xfd, 0x9b, 0xdc, 0x5f, 0x81, 0x52, 0x6f, 0x89, 0x62, 0xe5, 0xc0, 0x46, 0x90, 0x19, 0xc6, + 0x56, 0x01, 0xfb, 0x89, 0x7d, 0xce, 0x9d, 0x34, 0xbe, 0xe6, 0x41, 0xbe, 0xc5, 0xb0, 0xf8, 0x11, + 0x94, 0x22, 0x1f, 0x9c, 0x27, 0xda, 0xed, 0x6f, 0x9d, 0x16, 0x3b, 0xb3, 0xd2, 0xb3, 0x0c, 0xa0, + 0xb9, 0xd2, 0x4c, 0x21, 0x72, 0xa8, 0xd3, 0x14, 0x96, 0x41, 0xa9, 0x0a, 0x49, 0x07, 0x51, 0xb4, + 0xc0, 0x83, 0xe8, 0x44, 0x1d, 0xa4, 0xee, 0x5e, 0x42, 0x49, 0x87, 0x59, 0x50, 0x0b, 0x11, 0x17, + 0x88, 0x09, 0xb1, 0x3f, 0x4d, 0xe1, 0xb8, 0x0d, 0x95, 0xea, 0x99, 0xa1, 0x73, 0xcd, 0xe6, 0xe9, + 0xf7, 0xb1, 0x2c, 0x5c, 0x8f, 0x65, 0xe1, 0x66, 0x2c, 0x0b, 0x9f, 0x27, 0x72, 0xee, 0x7a, 0x22, + 0xe7, 0x7e, 0x4e, 0xe4, 0xdc, 0x87, 0x97, 0x98, 0x78, 0x1d, 0xdf, 0xd4, 0x2c, 0xda, 0x83, 0x16, + 0x65, 0x3d, 0xca, 0xc2, 0x9f, 0x23, 0x66, 0x5f, 0xc0, 0x4b, 0xb8, 0xb8, 0x6b, 0x9e, 0x37, 0x8e, + 0xc2, 0xeb, 0xc6, 0x1b, 0xf6, 0x11, 0x33, 0x0b, 0xc1, 0x58, 0xbd, 0xf8, 0x1d, 0x00, 0x00, 0xff, + 0xff, 0xf4, 0xf1, 0xa7, 0x9a, 0xf0, 0x06, 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 { + // CreateClient defines a rpc handler method for MsgCreateClient. + CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) + // UpdateClient defines a rpc handler method for MsgUpdateClient. + UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) + // UpgradeClient defines a rpc handler method for MsgUpgradeClient. + UpgradeClient(ctx context.Context, in *MsgUpgradeClient, opts ...grpc.CallOption) (*MsgUpgradeClientResponse, error) + // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. + SubmitMisbehaviour(ctx context.Context, in *MsgSubmitMisbehaviour, opts ...grpc.CallOption) (*MsgSubmitMisbehaviourResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateClient(ctx context.Context, in *MsgCreateClient, opts ...grpc.CallOption) (*MsgCreateClientResponse, error) { + out := new(MsgCreateClientResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/CreateClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateClient(ctx context.Context, in *MsgUpdateClient, opts ...grpc.CallOption) (*MsgUpdateClientResponse, error) { + out := new(MsgUpdateClientResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/UpdateClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpgradeClient(ctx context.Context, in *MsgUpgradeClient, opts ...grpc.CallOption) (*MsgUpgradeClientResponse, error) { + out := new(MsgUpgradeClientResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/UpgradeClient", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) SubmitMisbehaviour(ctx context.Context, in *MsgSubmitMisbehaviour, opts ...grpc.CallOption) (*MsgSubmitMisbehaviourResponse, error) { + out := new(MsgSubmitMisbehaviourResponse) + err := c.cc.Invoke(ctx, "/ibc.core.client.v1.Msg/SubmitMisbehaviour", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateClient defines a rpc handler method for MsgCreateClient. + CreateClient(context.Context, *MsgCreateClient) (*MsgCreateClientResponse, error) + // UpdateClient defines a rpc handler method for MsgUpdateClient. + UpdateClient(context.Context, *MsgUpdateClient) (*MsgUpdateClientResponse, error) + // UpgradeClient defines a rpc handler method for MsgUpgradeClient. + UpgradeClient(context.Context, *MsgUpgradeClient) (*MsgUpgradeClientResponse, error) + // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. + SubmitMisbehaviour(context.Context, *MsgSubmitMisbehaviour) (*MsgSubmitMisbehaviourResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateClient(ctx context.Context, req *MsgCreateClient) (*MsgCreateClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented") +} +func (*UnimplementedMsgServer) UpdateClient(ctx context.Context, req *MsgUpdateClient) (*MsgUpdateClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateClient not implemented") +} +func (*UnimplementedMsgServer) UpgradeClient(ctx context.Context, req *MsgUpgradeClient) (*MsgUpgradeClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpgradeClient not implemented") +} +func (*UnimplementedMsgServer) SubmitMisbehaviour(ctx context.Context, req *MsgSubmitMisbehaviour) (*MsgSubmitMisbehaviourResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitMisbehaviour not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateClient) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Msg/CreateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateClient(ctx, req.(*MsgCreateClient)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateClient) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Msg/UpdateClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateClient(ctx, req.(*MsgUpdateClient)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpgradeClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpgradeClient) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpgradeClient(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Msg/UpgradeClient", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpgradeClient(ctx, req.(*MsgUpgradeClient)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_SubmitMisbehaviour_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSubmitMisbehaviour) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SubmitMisbehaviour(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.client.v1.Msg/SubmitMisbehaviour", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SubmitMisbehaviour(ctx, req.(*MsgSubmitMisbehaviour)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.client.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateClient", + Handler: _Msg_CreateClient_Handler, + }, + { + MethodName: "UpdateClient", + Handler: _Msg_UpdateClient_Handler, + }, + { + MethodName: "UpgradeClient", + Handler: _Msg_UpgradeClient_Handler, + }, + { + MethodName: "SubmitMisbehaviour", + Handler: _Msg_SubmitMisbehaviour_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/client/v1/tx.proto", +} + +func (m *MsgCreateClient) 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 *MsgCreateClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ClientState != nil { + { + size, err := m.ClientState.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 *MsgCreateClientResponse) 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 *MsgCreateClientResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateClient) 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 *MsgUpdateClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.Header != nil { + { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateClientResponse) 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 *MsgUpdateClientResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpgradeClient) 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 *MsgUpgradeClient) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpgradeClient) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x32 + } + if len(m.ProofUpgradeConsensusState) > 0 { + i -= len(m.ProofUpgradeConsensusState) + copy(dAtA[i:], m.ProofUpgradeConsensusState) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofUpgradeConsensusState))) + i-- + dAtA[i] = 0x2a + } + if len(m.ProofUpgradeClient) > 0 { + i -= len(m.ProofUpgradeClient) + copy(dAtA[i:], m.ProofUpgradeClient) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofUpgradeClient))) + i-- + dAtA[i] = 0x22 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpgradeClientResponse) 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 *MsgUpgradeClientResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpgradeClientResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgSubmitMisbehaviour) 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 *MsgSubmitMisbehaviour) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitMisbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if m.Misbehaviour != nil { + { + size, err := m.Misbehaviour.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSubmitMisbehaviourResponse) 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 *MsgSubmitMisbehaviourResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSubmitMisbehaviourResponse) 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 *MsgCreateClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Header != nil { + l = m.Header.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpdateClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpgradeClient) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofUpgradeClient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofUpgradeConsensusState) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUpgradeClientResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgSubmitMisbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Misbehaviour != nil { + l = m.Misbehaviour.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSubmitMisbehaviourResponse) 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 *MsgCreateClient) 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: MsgCreateClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", 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.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", 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.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgCreateClientResponse) 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: MsgCreateClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateClientResponse: 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 *MsgUpdateClient) 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: MsgUpdateClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", 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.Header == nil { + m.Header = &types.Any{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgUpdateClientResponse) 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: MsgUpdateClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateClientResponse: 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 *MsgUpgradeClient) 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: MsgUpgradeClient: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpgradeClient: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", 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.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", 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.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofUpgradeClient", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofUpgradeClient = append(m.ProofUpgradeClient[:0], dAtA[iNdEx:postIndex]...) + if m.ProofUpgradeClient == nil { + m.ProofUpgradeClient = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofUpgradeConsensusState", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofUpgradeConsensusState = append(m.ProofUpgradeConsensusState[:0], dAtA[iNdEx:postIndex]...) + if m.ProofUpgradeConsensusState == nil { + m.ProofUpgradeConsensusState = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgUpgradeClientResponse) 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: MsgUpgradeClientResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpgradeClientResponse: 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 *MsgSubmitMisbehaviour) 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: MsgSubmitMisbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitMisbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Misbehaviour", 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.Misbehaviour == nil { + m.Misbehaviour = &types.Any{} + } + if err := m.Misbehaviour.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgSubmitMisbehaviourResponse) 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: MsgSubmitMisbehaviourResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSubmitMisbehaviourResponse: 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/ibc/core/03-connection/client/cli/cli.go b/x/ibc/core/03-connection/client/cli/cli.go new file mode 100644 index 000000000000..01bb6f9b110f --- /dev/null +++ b/x/ibc/core/03-connection/client/cli/cli.go @@ -0,0 +1,46 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" +) + +// GetQueryCmd returns the query commands for IBC connections +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC connection query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + queryCmd.AddCommand( + GetCmdQueryConnections(), + GetCmdQueryConnection(), + GetCmdQueryClientConnections(), + ) + + return queryCmd +} + +// NewTxCmd returns a CLI command handler for all x/ibc connection transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC connection transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewConnectionOpenInitCmd(), + NewConnectionOpenTryCmd(), + NewConnectionOpenAckCmd(), + NewConnectionOpenConfirmCmd(), + ) + + return txCmd +} diff --git a/x/ibc/core/03-connection/client/cli/query.go b/x/ibc/core/03-connection/client/cli/query.go new file mode 100644 index 000000000000..071410398db5 --- /dev/null +++ b/x/ibc/core/03-connection/client/cli/query.go @@ -0,0 +1,119 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// GetCmdQueryConnections defines the command to query all the connection ends +// that this chain mantains. +func GetCmdQueryConnections() *cobra.Command { + cmd := &cobra.Command{ + Use: "connections", + Short: "Query all connections", + Long: "Query all connections ends from a chain", + Example: fmt.Sprintf("%s query %s %s connections", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryConnectionsRequest{ + Pagination: pageReq, + } + + res, err := queryClient.Connections(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "connection ends") + + return cmd +} + +// GetCmdQueryConnection defines the command to query a connection end +func GetCmdQueryConnection() *cobra.Command { + cmd := &cobra.Command{ + Use: "end [connection-id]", + Short: "Query stored connection end", + Long: "Query stored connection end", + Example: fmt.Sprintf("%s query %s %s end [connection-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + connectionID := args[0] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + connRes, err := utils.QueryConnection(clientCtx, connectionID, prove) + if err != nil { + return err + } + + clientCtx = clientCtx.WithHeight(int64(connRes.ProofHeight.RevisionHeight)) + return clientCtx.PrintProto(connRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryClientConnections defines the command to query a client connections +func GetCmdQueryClientConnections() *cobra.Command { + cmd := &cobra.Command{ + Use: "path [client-id]", + Short: "Query stored client connection paths", + Long: "Query stored client connection paths", + Example: fmt.Sprintf("%s query %s %s path [client-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + clientID := args[0] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + connPathsRes, err := utils.QueryClientConnections(clientCtx, clientID, prove) + if err != nil { + return err + } + + clientCtx = clientCtx.WithHeight(int64(connPathsRes.ProofHeight.RevisionHeight)) + return clientCtx.PrintProto(connPathsRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/core/03-connection/client/cli/tx.go b/x/ibc/core/03-connection/client/cli/tx.go new file mode 100644 index 000000000000..339e2e9770f4 --- /dev/null +++ b/x/ibc/core/03-connection/client/cli/tx.go @@ -0,0 +1,335 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "strings" + + "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/core/03-connection/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + flagVersionIdentifier = "version-identifier" + flagVersionFeatures = "version-features" + flagDelayPeriod = "delay-period" +) + +// NewConnectionOpenInitCmd defines the command to initialize a connection on +// chain A with a given counterparty chain B +func NewConnectionOpenInitCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-init [client-id] [counterparty-client-id] [path/to/counterparty_prefix.json]", + Short: "Initialize connection on chain A", + Long: `Initialize a connection on chain A with a given counterparty chain B. + - 'version-identifier' flag can be a single pre-selected version identifier to be used in the handshake. + - 'version-features' flag can be a list of features separated by commas to accompany the version identifier.`, + Example: fmt.Sprintf( + "%s tx %s %s open-init [client-id] [counterparty-client-id] [path/to/counterparty_prefix.json] --version-identifier=\"1.0\" --version-features=\"ORDER_UNORDERED\" --delay-period=500", + version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + clientID := args[0] + counterpartyClientID := args[1] + + counterpartyPrefix, err := utils.ParsePrefix(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + var version *types.Version + versionIdentifier, _ := cmd.Flags().GetString(flagVersionIdentifier) + + if versionIdentifier != "" { + var features []string + + versionFeatures, _ := cmd.Flags().GetString(flagVersionFeatures) + if versionFeatures != "" { + features = strings.Split(versionFeatures, ",") + } + + version = types.NewVersion(versionIdentifier, features) + } + + delayPeriod, err := cmd.Flags().GetUint64(flagDelayPeriod) + if err != nil { + return err + } + + msg := types.NewMsgConnectionOpenInit( + clientID, counterpartyClientID, + counterpartyPrefix, version, delayPeriod, clientCtx.GetFromAddress(), + ) + + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + // NOTE: we should use empty default values since the user may not want to select a version + // at this step in the handshake. + cmd.Flags().String(flagVersionIdentifier, "", "version identifier to be used in the connection handshake version negotiation") + cmd.Flags().String(flagVersionFeatures, "", "version features list separated by commas without spaces. The features must function with the version identifier.") + cmd.Flags().Uint64(flagDelayPeriod, 0, "delay period that must pass before packet verification can pass against a consensus state") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewConnectionOpenTryCmd defines the command to relay a try open a connection on +// chain B +func NewConnectionOpenTryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: strings.TrimSpace(`open-try [connection-id] [client-id] +[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] [path/to/client_state.json] +[path/to/counterparty_version1.json,path/to/counterparty_version2.json...] [consensus-height] [proof-height] [path/to/proof_init.json] [path/to/proof_client.json] [path/to/proof_consensus.json]`), + Short: "initiate connection handshake between two chains", + Long: "Initialize a connection on chain A with a given counterparty chain B. Provide counterparty versions separated by commas", + Example: fmt.Sprintf( + `%s tx %s %s open-try connection-id] [client-id] \ +[counterparty-connection-id] [counterparty-client-id] [path/to/counterparty_prefix.json] [path/to/client_state.json]\ +[counterparty-versions] [consensus-height] [proof-height] [path/to/proof_init.json] [path/to/proof_client.json] [path/to/proof_consensus.json]`, + version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(12), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + connectionID := args[0] + clientID := args[1] + counterpartyConnectionID := args[2] + counterpartyClientID := args[3] + + counterpartyPrefix, err := utils.ParsePrefix(clientCtx.LegacyAmino, args[4]) + if err != nil { + return err + } + + counterpartyClient, err := utils.ParseClientState(clientCtx.LegacyAmino, args[5]) + if err != nil { + return err + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + versionsStr := strings.Split(args[6], ",") + counterpartyVersions := make([]*types.Version, len(versionsStr)) + + for _, ver := range versionsStr { + + // attempt to unmarshal version + version := &types.Version{} + if err := cdc.UnmarshalJSON([]byte(ver), version); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(ver) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for version were provided") + } + + if err := cdc.UnmarshalJSON(contents, version); err != nil { + return errors.Wrap(err, "error unmarshalling version file") + } + } + } + + consensusHeight, err := clienttypes.ParseHeight(args[7]) + if err != nil { + return err + } + proofHeight, err := clienttypes.ParseHeight(args[8]) + if err != nil { + return err + } + + proofInit, err := utils.ParseProof(clientCtx.LegacyAmino, args[9]) + if err != nil { + return err + } + + proofClient, err := utils.ParseProof(clientCtx.LegacyAmino, args[10]) + if err != nil { + return err + } + + proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[11]) + if err != nil { + return err + } + + delayPeriod, err := cmd.Flags().GetUint64(flagDelayPeriod) + if err != nil { + return err + } + + msg := types.NewMsgConnectionOpenTry( + connectionID, clientID, counterpartyConnectionID, counterpartyClientID, + counterpartyClient, counterpartyPrefix, counterpartyVersions, delayPeriod, + proofInit, proofClient, proofConsensus, proofHeight, + consensusHeight, clientCtx.GetFromAddress(), + ) + + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().Uint64(flagDelayPeriod, 0, "delay period that must pass before packet verification can pass against a consensus state") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewConnectionOpenAckCmd defines the command to relay the acceptance of a +// connection open attempt from chain B to chain A +func NewConnectionOpenAckCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: `open-ack [connection-id] [counterparty-connection-id] [path/to/client_state.json] [consensus-height] [proof-height] + [path/to/proof_try.json] [path/to/proof_client.json] [path/to/proof_consensus.json] [version]`, + Short: "relay the acceptance of a connection open attempt", + Long: "Relay the acceptance of a connection open attempt from chain B to chain A", + Example: fmt.Sprintf( + `%s tx %s %s open-ack [connection-id] [counterparty-connection-id] [path/to/client_state.json] [consensus-height] [proof-height] + [path/to/proof_try.json] [path/to/proof_client.json] [path/to/proof_consensus.json] [version]`, + version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(9), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + connectionID := args[0] + counterpartyConnectionID := args[1] + + counterpartyClient, err := utils.ParseClientState(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + consensusHeight, err := clienttypes.ParseHeight(args[3]) + if err != nil { + return err + } + proofHeight, err := clienttypes.ParseHeight(args[4]) + if err != nil { + return err + } + + proofTry, err := utils.ParseProof(clientCtx.LegacyAmino, args[5]) + if err != nil { + return err + } + + proofClient, err := utils.ParseProof(clientCtx.LegacyAmino, args[6]) + if err != nil { + return err + } + + proofConsensus, err := utils.ParseProof(clientCtx.LegacyAmino, args[7]) + if err != nil { + return err + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + // attempt to unmarshal version + version := &types.Version{} + if err := cdc.UnmarshalJSON([]byte(args[8]), version); err != nil { + + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(args[8]) + if err != nil { + return errors.Wrap(err, "neither JSON input nor path to .json file for version were provided") + } + + if err := cdc.UnmarshalJSON(contents, version); err != nil { + return errors.Wrap(err, "error unmarshalling version file") + } + } + + msg := types.NewMsgConnectionOpenAck( + connectionID, counterpartyConnectionID, counterpartyClient, proofTry, proofClient, proofConsensus, proofHeight, + consensusHeight, version, clientCtx.GetFromAddress(), + ) + + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewConnectionOpenConfirmCmd defines the command to initialize a connection on +// chain A with a given counterparty chain B +func NewConnectionOpenConfirmCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-confirm [connection-id] [proof-height] [path/to/proof_ack.json]", + Short: "confirm to chain B that connection is open on chain A", + Long: "Confirm to chain B that connection is open on chain A", + Example: fmt.Sprintf( + "%s tx %s %s open-confirm [connection-id] [proof-height] [path/to/proof_ack.json]", + version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + connectionID := args[0] + proofHeight, err := clienttypes.ParseHeight(args[1]) + if err != nil { + return err + } + + proofAck, err := utils.ParseProof(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + msg := types.NewMsgConnectionOpenConfirm( + connectionID, proofAck, proofHeight, clientCtx.GetFromAddress(), + ) + + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/core/03-connection/client/utils/utils.go b/x/ibc/core/03-connection/client/utils/utils.go new file mode 100644 index 000000000000..e1eb1ce00c55 --- /dev/null +++ b/x/ibc/core/03-connection/client/utils/utils.go @@ -0,0 +1,219 @@ +package utils + +import ( + "context" + "fmt" + "io/ioutil" + + "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clientutils "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/utils" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// QueryConnection returns a connection end. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryConnection( + clientCtx client.Context, connectionID string, prove bool, +) (*types.QueryConnectionResponse, error) { + if prove { + return queryConnectionABCI(clientCtx, connectionID) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryConnectionRequest{ + ConnectionId: connectionID, + } + + return queryClient.Connection(context.Background(), req) +} + +func queryConnectionABCI(clientCtx client.Context, connectionID string) (*types.QueryConnectionResponse, error) { + key := host.ConnectionKey(connectionID) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if connection exists + if len(value) == 0 { + return nil, sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + var connection types.ConnectionEnd + if err := cdc.UnmarshalBinaryBare(value, &connection); err != nil { + return nil, err + } + + return types.NewQueryConnectionResponse(connection, proofBz, proofHeight), nil +} + +// QueryClientConnections queries the connection paths registered for a particular client. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryClientConnections( + clientCtx client.Context, clientID string, prove bool, +) (*types.QueryClientConnectionsResponse, error) { + if prove { + return queryClientConnectionsABCI(clientCtx, clientID) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryClientConnectionsRequest{ + ClientId: clientID, + } + + return queryClient.ClientConnections(context.Background(), req) +} + +func queryClientConnectionsABCI(clientCtx client.Context, clientID string) (*types.QueryClientConnectionsResponse, error) { + key := host.ClientConnectionsKey(clientID) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if connection paths exist + if len(value) == 0 { + return nil, sdkerrors.Wrap(types.ErrClientConnectionPathsNotFound, clientID) + } + + var paths []string + if err := clientCtx.LegacyAmino.UnmarshalBinaryBare(value, &paths); err != nil { + return nil, err + } + + return types.NewQueryClientConnectionsResponse(paths, proofBz, proofHeight), nil +} + +// QueryConnectionClientState returns the ClientState of a connection end. If +// prove is true, it performs an ABCI store query in order to retrieve the +// merkle proof. Otherwise, it uses the gRPC query client. +func QueryConnectionClientState( + clientCtx client.Context, connectionID string, prove bool, +) (*types.QueryConnectionClientStateResponse, error) { + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryConnectionClientStateRequest{ + ConnectionId: connectionID, + } + + res, err := queryClient.ConnectionClientState(context.Background(), req) + if err != nil { + return nil, err + } + + if prove { + clientStateRes, err := clientutils.QueryClientStateABCI(clientCtx, res.IdentifiedClientState.ClientId) + if err != nil { + return nil, err + } + + // use client state returned from ABCI query in case query height differs + identifiedClientState := clienttypes.IdentifiedClientState{ + ClientId: res.IdentifiedClientState.ClientId, + ClientState: clientStateRes.ClientState, + } + + res = types.NewQueryConnectionClientStateResponse(identifiedClientState, clientStateRes.Proof, clientStateRes.ProofHeight) + } + + return res, nil +} + +// QueryConnectionConsensusState returns the ConsensusState of a connection end. If +// prove is true, it performs an ABCI store query in order to retrieve the +// merkle proof. Otherwise, it uses the gRPC query client. +func QueryConnectionConsensusState( + clientCtx client.Context, connectionID string, height clienttypes.Height, prove bool, +) (*types.QueryConnectionConsensusStateResponse, error) { + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryConnectionConsensusStateRequest{ + ConnectionId: connectionID, + RevisionNumber: height.RevisionNumber, + RevisionHeight: height.RevisionHeight, + } + + res, err := queryClient.ConnectionConsensusState(context.Background(), req) + if err != nil { + return nil, err + } + + if prove { + consensusStateRes, err := clientutils.QueryConsensusStateABCI(clientCtx, res.ClientId, height) + if err != nil { + return nil, err + } + + res = types.NewQueryConnectionConsensusStateResponse(res.ClientId, consensusStateRes.ConsensusState, height, consensusStateRes.Proof, consensusStateRes.ProofHeight) + } + + return res, nil +} + +// ParseClientState unmarshals a cmd input argument from a JSON string to a client state +// If the input is not a JSON, it looks for a path to the JSON file +func ParseClientState(cdc *codec.LegacyAmino, arg string) (exported.ClientState, error) { + var clientState exported.ClientState + if err := cdc.UnmarshalJSON([]byte(arg), &clientState); err != nil { + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(arg) + if err != nil { + return nil, errors.New("either JSON input nor path to .json file were provided") + } + if err := cdc.UnmarshalJSON(contents, &clientState); err != nil { + return nil, errors.Wrap(err, "error unmarshalling client state") + } + } + return clientState, nil +} + +// ParsePrefix unmarshals an cmd input argument from a JSON string to a commitment +// Prefix. If the input is not a JSON, it looks for a path to the JSON file. +func ParsePrefix(cdc *codec.LegacyAmino, arg string) (commitmenttypes.MerklePrefix, error) { + var prefix commitmenttypes.MerklePrefix + if err := cdc.UnmarshalJSON([]byte(arg), &prefix); err != nil { + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(arg) + if err != nil { + return commitmenttypes.MerklePrefix{}, errors.New("neither JSON input nor path to .json file were provided") + } + if err := cdc.UnmarshalJSON(contents, &prefix); err != nil { + return commitmenttypes.MerklePrefix{}, errors.Wrap(err, "error unmarshalling commitment prefix") + } + } + return prefix, nil +} + +// ParseProof unmarshals a cmd input argument from a JSON string to a commitment +// Proof. If the input is not a JSON, it looks for a path to the JSON file. It +// then marshals the commitment proof into a proto encoded byte array. +func ParseProof(cdc *codec.LegacyAmino, arg string) ([]byte, error) { + var merkleProof commitmenttypes.MerkleProof + if err := cdc.UnmarshalJSON([]byte(arg), &merkleProof); err != nil { + // check for file path if JSON input is not provided + contents, err := ioutil.ReadFile(arg) + if err != nil { + return nil, errors.New("neither JSON input nor path to .json file were provided") + } + if err := cdc.UnmarshalJSON(contents, &merkleProof); err != nil { + return nil, fmt.Errorf("error unmarshalling commitment proof: %w", err) + } + } + + return cdc.MarshalBinaryBare(&merkleProof) +} diff --git a/x/ibc/core/03-connection/genesis.go b/x/ibc/core/03-connection/genesis.go new file mode 100644 index 000000000000..a1bb30f1fe56 --- /dev/null +++ b/x/ibc/core/03-connection/genesis.go @@ -0,0 +1,28 @@ +package connection + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" +) + +// InitGenesis initializes the ibc connection submodule's state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) { + for _, connection := range gs.Connections { + conn := types.NewConnectionEnd(connection.State, connection.ClientId, connection.Counterparty, connection.Versions, connection.DelayPeriod) + k.SetConnection(ctx, connection.Id, conn) + } + for _, connPaths := range gs.ClientConnectionPaths { + k.SetClientConnectionPaths(ctx, connPaths.ClientId, connPaths.Paths) + } + k.SetNextConnectionSequence(ctx, gs.NextConnectionSequence) +} + +// ExportGenesis returns the ibc connection submodule's exported genesis. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { + return types.GenesisState{ + Connections: k.GetAllConnections(ctx), + ClientConnectionPaths: k.GetAllClientConnectionPaths(ctx), + } +} diff --git a/x/ibc/core/03-connection/keeper/grpc_query.go b/x/ibc/core/03-connection/keeper/grpc_query.go new file mode 100644 index 000000000000..62b1c00a3498 --- /dev/null +++ b/x/ibc/core/03-connection/keeper/grpc_query.go @@ -0,0 +1,179 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +var _ types.QueryServer = Keeper{} + +// Connection implements the Query/Connection gRPC method +func (q Keeper) Connection(c context.Context, req *types.QueryConnectionRequest) (*types.QueryConnectionResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ConnectionIdentifierValidator(req.ConnectionId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + connection, found := q.GetConnection(ctx, req.ConnectionId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrap(types.ErrConnectionNotFound, req.ConnectionId).Error(), + ) + } + + return &types.QueryConnectionResponse{ + Connection: &connection, + ProofHeight: clienttypes.GetSelfHeight(ctx), + }, nil +} + +// Connections implements the Query/Connections gRPC method +func (q Keeper) Connections(c context.Context, req *types.QueryConnectionsRequest) (*types.QueryConnectionsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + + connections := []*types.IdentifiedConnection{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyConnectionPrefix)) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + var result types.ConnectionEnd + if err := q.cdc.UnmarshalBinaryBare(value, &result); err != nil { + return err + } + + connectionID, err := host.ParseConnectionPath(string(key)) + if err != nil { + return err + } + + identifiedConnection := types.NewIdentifiedConnection(connectionID, result) + connections = append(connections, &identifiedConnection) + return nil + }) + + if err != nil { + return nil, err + } + + return &types.QueryConnectionsResponse{ + Connections: connections, + Pagination: pageRes, + Height: clienttypes.GetSelfHeight(ctx), + }, nil +} + +// ClientConnections implements the Query/ClientConnections gRPC method +func (q Keeper) ClientConnections(c context.Context, req *types.QueryClientConnectionsRequest) (*types.QueryClientConnectionsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ClientIdentifierValidator(req.ClientId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + clientConnectionPaths, found := q.GetClientConnectionPaths(ctx, req.ClientId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrap(types.ErrClientConnectionPathsNotFound, req.ClientId).Error(), + ) + } + + return &types.QueryClientConnectionsResponse{ + ConnectionPaths: clientConnectionPaths, + ProofHeight: clienttypes.GetSelfHeight(ctx), + }, nil +} + +// ConnectionClientState implements the Query/ConnectionClientState gRPC method +func (q Keeper) ConnectionClientState(c context.Context, req *types.QueryConnectionClientStateRequest) (*types.QueryConnectionClientStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ConnectionIdentifierValidator(req.ConnectionId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + + connection, found := q.GetConnection(ctx, req.ConnectionId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), + ) + } + + clientState, found := q.clientKeeper.GetClientState(ctx, connection.ClientId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId).Error(), + ) + } + + identifiedClientState := clienttypes.NewIdentifiedClientState(connection.ClientId, clientState) + + height := clienttypes.GetSelfHeight(ctx) + return types.NewQueryConnectionClientStateResponse(identifiedClientState, nil, height), nil + +} + +// ConnectionConsensusState implements the Query/ConnectionConsensusState gRPC method +func (q Keeper) ConnectionConsensusState(c context.Context, req *types.QueryConnectionConsensusStateRequest) (*types.QueryConnectionConsensusStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ConnectionIdentifierValidator(req.ConnectionId); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + + connection, found := q.GetConnection(ctx, req.ConnectionId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrConnectionNotFound, "connection-id: %s", req.ConnectionId).Error(), + ) + } + + height := clienttypes.NewHeight(req.RevisionNumber, req.RevisionHeight) + consensusState, found := q.clientKeeper.GetClientConsensusState(ctx, connection.ClientId, height) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), + ) + } + + anyConsensusState, err := clienttypes.PackConsensusState(consensusState) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + proofHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryConnectionConsensusStateResponse(connection.ClientId, anyConsensusState, height, nil, proofHeight), nil +} diff --git a/x/ibc/core/03-connection/keeper/grpc_query_test.go b/x/ibc/core/03-connection/keeper/grpc_query_test.go new file mode 100644 index 000000000000..14fdb425d9eb --- /dev/null +++ b/x/ibc/core/03-connection/keeper/grpc_query_test.go @@ -0,0 +1,420 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *KeeperTestSuite) TestQueryConnection() { + var ( + req *types.QueryConnectionRequest + expConnection types.ConnectionEnd + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + {"invalid connectionID", + func() { + req = &types.QueryConnectionRequest{} + }, + false, + }, + {"connection not found", + func() { + req = &types.QueryConnectionRequest{ + ConnectionId: ibctesting.InvalidID, + } + }, + false, + }, + { + "success", + func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA := suite.chainA.GetFirstTestConnection(clientA, clientB) + connB := suite.chainB.GetFirstTestConnection(clientB, clientA) + + counterparty := types.NewCounterparty(clientB, connB.ID, suite.chainB.GetPrefix()) + expConnection = types.NewConnectionEnd(types.INIT, clientA, counterparty, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 500) + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, expConnection) + + req = &types.QueryConnectionRequest{ + ConnectionId: connA.ID, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.Connection(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(&expConnection, res.Connection) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConnections() { + var ( + req *types.QueryConnectionsRequest + expConnections = []*types.IdentifiedConnection{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "empty pagination", + func() { + req = &types.QueryConnectionsRequest{} + }, + true, + }, + { + "success", + func() { + clientA, clientB, connA0, connB0 := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + clientA1, clientB1, connA1, connB1 := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + connA2, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + counterparty1 := types.NewCounterparty(clientB, connB0.ID, suite.chainB.GetPrefix()) + counterparty2 := types.NewCounterparty(clientB1, connB1.ID, suite.chainB.GetPrefix()) + // counterparty connection id is blank after open init + counterparty3 := types.NewCounterparty(clientB, "", suite.chainB.GetPrefix()) + + conn1 := types.NewConnectionEnd(types.OPEN, clientA, counterparty1, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 0) + conn2 := types.NewConnectionEnd(types.OPEN, clientA1, counterparty2, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 0) + conn3 := types.NewConnectionEnd(types.INIT, clientA, counterparty3, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 0) + + iconn1 := types.NewIdentifiedConnection(connA0.ID, conn1) + iconn2 := types.NewIdentifiedConnection(connA1.ID, conn2) + iconn3 := types.NewIdentifiedConnection(connA2.ID, conn3) + + expConnections = []*types.IdentifiedConnection{&iconn1, &iconn2, &iconn3} + + req = &types.QueryConnectionsRequest{ + Pagination: &query.PageRequest{ + Limit: 3, + 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.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.Connections(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expConnections, res.Connections) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryClientConnections() { + var ( + req *types.QueryClientConnectionsRequest + expPaths []string + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + {"invalid connectionID", + func() { + req = &types.QueryClientConnectionsRequest{} + }, + false, + }, + {"connection not found", + func() { + req = &types.QueryClientConnectionsRequest{ + ClientId: ibctesting.InvalidID, + } + }, + false, + }, + { + "success", + func() { + clientA, clientB, connA0, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + connA1, _ := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + expPaths = []string{connA0.ID, connA1.ID} + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetClientConnectionPaths(suite.chainA.GetContext(), clientA, expPaths) + + req = &types.QueryClientConnectionsRequest{ + ClientId: clientA, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ClientConnections(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expPaths, res.ConnectionPaths) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConnectionClientState() { + var ( + req *types.QueryConnectionClientStateRequest + expIdentifiedClientState clienttypes.IdentifiedClientState + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid connection ID", + func() { + req = &types.QueryConnectionClientStateRequest{ + ConnectionId: "", + } + }, + false, + }, + { + "connection not found", + func() { + req = &types.QueryConnectionClientStateRequest{ + ConnectionId: "test-connection-id", + } + }, + false, + }, + { + "client state not found", + func() { + _, _, connA, _, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + // set connection to empty so clientID is empty + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, types.ConnectionEnd{}) + + req = &types.QueryConnectionClientStateRequest{ + ConnectionId: connA.ID, + } + }, false, + }, + { + "success", + func() { + clientA, _, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + expClientState := suite.chainA.GetClientState(clientA) + expIdentifiedClientState = clienttypes.NewIdentifiedClientState(clientA, expClientState) + + req = &types.QueryConnectionClientStateRequest{ + ConnectionId: connA.ID, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ConnectionClientState(ctx, req) + + if tc.expPass { + 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) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConnectionConsensusState() { + var ( + req *types.QueryConnectionConsensusStateRequest + expConsensusState exported.ConsensusState + expClientID string + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid connection ID", + func() { + req = &types.QueryConnectionConsensusStateRequest{ + ConnectionId: "", + RevisionNumber: 0, + RevisionHeight: 1, + } + }, + false, + }, + { + "connection not found", + func() { + req = &types.QueryConnectionConsensusStateRequest{ + ConnectionId: "test-connection-id", + RevisionNumber: 0, + RevisionHeight: 1, + } + }, + false, + }, + { + "consensus state not found", + func() { + _, _, connA, _, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + req = &types.QueryConnectionConsensusStateRequest{ + ConnectionId: connA.ID, + RevisionNumber: 0, + RevisionHeight: uint64(suite.chainA.GetContext().BlockHeight()), // use current height + } + }, false, + }, + { + "success", + func() { + clientA, _, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + clientState := suite.chainA.GetClientState(clientA) + expConsensusState, _ = suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) + suite.Require().NotNil(expConsensusState) + expClientID = clientA + + req = &types.QueryConnectionConsensusStateRequest{ + ConnectionId: connA.ID, + RevisionNumber: clientState.GetLatestHeight().GetRevisionNumber(), + RevisionHeight: clientState.GetLatestHeight().GetRevisionHeight(), + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ConnectionConsensusState(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState) + 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/keeper/handshake.go b/x/ibc/core/03-connection/keeper/handshake.go new file mode 100644 index 000000000000..b8f7466f159b --- /dev/null +++ b/x/ibc/core/03-connection/keeper/handshake.go @@ -0,0 +1,342 @@ +package keeper + +import ( + "bytes" + + "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// ConnOpenInit initialises a connection attempt on chain A. The generated connection identifier +// is returned. +// +// NOTE: Msg validation verifies the supplied identifiers and ensures that the counterparty +// connection identifier is empty. +func (k Keeper) ConnOpenInit( + ctx sdk.Context, + clientID string, + counterparty types.Counterparty, // counterpartyPrefix, counterpartyClientIdentifier + version *types.Version, + delayPeriod uint64, +) (string, error) { + versions := types.GetCompatibleVersions() + if version != nil { + if !types.IsSupportedVersion(version) { + return "", sdkerrors.Wrap(types.ErrInvalidVersion, "version is not supported") + } + + versions = []exported.Version{version} + } + + // connection defines chain A's ConnectionEnd + connectionID := k.GenerateConnectionIdentifier(ctx) + connection := types.NewConnectionEnd(types.INIT, clientID, counterparty, types.ExportedVersionsToProto(versions), delayPeriod) + k.SetConnection(ctx, connectionID, connection) + + if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { + return "", err + } + + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "NONE", "new-state", "INIT") + + defer func() { + telemetry.IncrCounter(1, "ibc", "connection", "open-init") + }() + + return connectionID, nil +} + +// ConnOpenTry relays notice of a connection attempt on chain A to chain B (this +// code is executed on chain B). +// +// NOTE: +// - Here chain A acts as the counterparty +// - Identifiers are checked on msg validation +func (k Keeper) ConnOpenTry( + ctx sdk.Context, + previousConnectionID string, // previousIdentifier + counterparty types.Counterparty, // counterpartyConnectionIdentifier, counterpartyPrefix and counterpartyClientIdentifier + delayPeriod uint64, + clientID string, // clientID of chainA + clientState exported.ClientState, // clientState that chainA has for chainB + counterpartyVersions []exported.Version, // supported versions of chain A + proofInit []byte, // proof that chainA stored connectionEnd in state (on ConnOpenInit) + proofClient []byte, // proof that chainA stored a light client of chainB + proofConsensus []byte, // proof that chainA stored chainB's consensus state at consensus height + proofHeight exported.Height, // height at which relayer constructs proof of A storing connectionEnd in state + consensusHeight exported.Height, // latest height of chain B which chain A has stored in its chain B client +) (string, error) { + var ( + connectionID string + previousConnection types.ConnectionEnd + found bool + ) + + // empty connection identifier indicates continuing a previous connection handshake + if previousConnectionID != "" { + // ensure that the previous connection exists + previousConnection, found = k.GetConnection(ctx, previousConnectionID) + if !found { + return "", sdkerrors.Wrapf(types.ErrConnectionNotFound, "previous connection does not exist for supplied previous connectionID %s", previousConnectionID) + } + + // ensure that the existing connection's + // counterparty is chainA and connection is on INIT stage. + // Check that existing connection versions for initialized connection is equal to compatible + // versions for this chain. + // ensure that existing connection's delay period is the same as desired delay period. + if !(previousConnection.Counterparty.ConnectionId == "" && + bytes.Equal(previousConnection.Counterparty.Prefix.Bytes(), counterparty.Prefix.Bytes()) && + previousConnection.ClientId == clientID && + previousConnection.Counterparty.ClientId == counterparty.ClientId && + previousConnection.DelayPeriod == delayPeriod) { + return "", sdkerrors.Wrap(types.ErrInvalidConnection, "connection fields mismatch previous connection fields") + } + + if !(previousConnection.State == types.INIT) { + return "", sdkerrors.Wrapf(types.ErrInvalidConnectionState, "previous connection state is in state %s, expected INIT", previousConnection.State) + } + + // continue with previous connection + connectionID = previousConnectionID + + } else { + // generate a new connection + connectionID = k.GenerateConnectionIdentifier(ctx) + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + if consensusHeight.GTE(selfHeight) { + return "", sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, + ) + } + + // validate client parameters of a chainB client stored on chainA + if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { + return "", err + } + + expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) + if !found { + return "", sdkerrors.Wrap(clienttypes.ErrSelfConsensusStateNotFound, consensusHeight.String()) + } + + // expectedConnection defines Chain A's ConnectionEnd + // NOTE: chain A's counterparty is chain B (i.e where this code is executed) + // NOTE: chainA and chainB must have the same delay period + prefix := k.GetCommitmentPrefix() + expectedCounterparty := types.NewCounterparty(clientID, "", commitmenttypes.NewMerklePrefix(prefix.Bytes())) + expectedConnection := types.NewConnectionEnd(types.INIT, counterparty.ClientId, expectedCounterparty, types.ExportedVersionsToProto(counterpartyVersions), delayPeriod) + + supportedVersions := types.GetCompatibleVersions() + if len(previousConnection.Versions) != 0 { + supportedVersions = previousConnection.GetVersions() + } + + // chain B picks a version from Chain A's available versions that is compatible + // with Chain B's supported IBC versions. PickVersion will select the intersection + // of the supported versions and the counterparty versions. + version, err := types.PickVersion(supportedVersions, counterpartyVersions) + if err != nil { + return "", err + } + + // connection defines chain B's ConnectionEnd + connection := types.NewConnectionEnd(types.TRYOPEN, clientID, counterparty, []*types.Version{version}, delayPeriod) + + // Check that ChainA committed expectedConnectionEnd to its state + if err := k.VerifyConnectionState( + ctx, connection, proofHeight, proofInit, counterparty.ConnectionId, + expectedConnection, + ); err != nil { + return "", err + } + + // Check that ChainA stored the clientState provided in the msg + if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { + return "", err + } + + // Check that ChainA stored the correct ConsensusState of chainB at the given consensusHeight + if err := k.VerifyClientConsensusState( + ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, + ); err != nil { + return "", err + } + + // store connection in chainB state + if err := k.addConnectionToClient(ctx, clientID, connectionID); err != nil { + return "", sdkerrors.Wrapf(err, "failed to add connection with ID %s to client with ID %s", connectionID, clientID) + } + + k.SetConnection(ctx, connectionID, connection) + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", previousConnection.State.String(), "new-state", "TRYOPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "connection", "open-try") + }() + + return connectionID, nil +} + +// ConnOpenAck relays acceptance of a connection open attempt from chain B back +// to chain A (this code is executed on chain A). +// +// NOTE: Identifiers are checked on msg validation. +func (k Keeper) ConnOpenAck( + ctx sdk.Context, + connectionID string, + clientState exported.ClientState, // client state for chainA on chainB + version *types.Version, // version that ChainB chose in ConnOpenTry + counterpartyConnectionID string, + proofTry []byte, // proof that connectionEnd was added to ChainB state in ConnOpenTry + proofClient []byte, // proof of client state on chainB for chainA + proofConsensus []byte, // proof that chainB has stored ConsensusState of chainA on its client + proofHeight exported.Height, // height that relayer constructed proofTry + consensusHeight exported.Height, // latest height of chainA that chainB has stored on its chainA client +) error { + // Check that chainB client hasn't stored invalid height + selfHeight := clienttypes.GetSelfHeight(ctx) + if consensusHeight.GTE(selfHeight) { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "consensus height is greater than or equal to the current block height (%s >= %s)", consensusHeight, selfHeight, + ) + } + + // Retrieve connection + connection, found := k.GetConnection(ctx, connectionID) + if !found { + return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + } + + // Verify the provided version against the previously set connection state + switch { + // connection on ChainA must be in INIT or TRYOPEN + case connection.State != types.INIT && connection.State != types.TRYOPEN: + return sdkerrors.Wrapf( + types.ErrInvalidConnectionState, + "connection state is not INIT or TRYOPEN (got %s)", connection.State.String(), + ) + + // if the connection is INIT then the provided version must be supproted + case connection.State == types.INIT && !types.IsSupportedVersion(version): + return sdkerrors.Wrapf( + types.ErrInvalidConnectionState, + "connection state is in INIT but the provided version is not supported %s", version, + ) + + // if the connection is in TRYOPEN then the version must be the only set version in the + // retreived connection state. + case connection.State == types.TRYOPEN && (len(connection.Versions) != 1 || !proto.Equal(connection.Versions[0], version)): + return sdkerrors.Wrapf( + types.ErrInvalidConnectionState, + "connection state is in TRYOPEN but the provided version (%s) is not set in the previous connection versions %s", version, connection.Versions, + ) + } + + // validate client parameters of a chainA client stored on chainB + if err := k.clientKeeper.ValidateSelfClient(ctx, clientState); err != nil { + return err + } + + // Retrieve chainA's consensus state at consensusheight + expectedConsensusState, found := k.clientKeeper.GetSelfConsensusState(ctx, consensusHeight) + if !found { + return clienttypes.ErrSelfConsensusStateNotFound + } + + prefix := k.GetCommitmentPrefix() + expectedCounterparty := types.NewCounterparty(connection.ClientId, connectionID, commitmenttypes.NewMerklePrefix(prefix.Bytes())) + expectedConnection := types.NewConnectionEnd(types.TRYOPEN, connection.Counterparty.ClientId, expectedCounterparty, []*types.Version{version}, connection.DelayPeriod) + + // Ensure that ChainB stored expected connectionEnd in its state during ConnOpenTry + if err := k.VerifyConnectionState( + ctx, connection, proofHeight, proofTry, counterpartyConnectionID, + expectedConnection, + ); err != nil { + return err + } + + // Check that ChainB stored the clientState provided in the msg + if err := k.VerifyClientState(ctx, connection, proofHeight, proofClient, clientState); err != nil { + return err + } + + // Ensure that ChainB has stored the correct ConsensusState for chainA at the consensusHeight + if err := k.VerifyClientConsensusState( + ctx, connection, proofHeight, consensusHeight, proofConsensus, expectedConsensusState, + ); err != nil { + return err + } + + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", connection.State.String(), "new-state", "OPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "connection", "open-ack") + }() + + // Update connection state to Open + connection.State = types.OPEN + connection.Versions = []*types.Version{version} + connection.Counterparty.ConnectionId = counterpartyConnectionID + k.SetConnection(ctx, connectionID, connection) + return nil +} + +// ConnOpenConfirm confirms opening of a connection on chain A to chain B, after +// which the connection is open on both chains (this code is executed on chain B). +// +// NOTE: Identifiers are checked on msg validation. +func (k Keeper) ConnOpenConfirm( + ctx sdk.Context, + connectionID string, + proofAck []byte, // proof that connection opened on ChainA during ConnOpenAck + proofHeight exported.Height, // height that relayer constructed proofAck +) error { + // Retrieve connection + connection, found := k.GetConnection(ctx, connectionID) + if !found { + return sdkerrors.Wrap(types.ErrConnectionNotFound, connectionID) + } + + // Check that connection state on ChainB is on state: TRYOPEN + if connection.State != types.TRYOPEN { + return sdkerrors.Wrapf( + types.ErrInvalidConnectionState, + "connection state is not TRYOPEN (got %s)", connection.State.String(), + ) + } + + prefix := k.GetCommitmentPrefix() + expectedCounterparty := types.NewCounterparty(connection.ClientId, connectionID, commitmenttypes.NewMerklePrefix(prefix.Bytes())) + expectedConnection := types.NewConnectionEnd(types.OPEN, connection.Counterparty.ClientId, expectedCounterparty, connection.Versions, connection.DelayPeriod) + + // Check that connection on ChainA is open + if err := k.VerifyConnectionState( + ctx, connection, proofHeight, proofAck, connection.Counterparty.ConnectionId, + expectedConnection, + ); err != nil { + return err + } + + // Update ChainB's connection to Open + connection.State = types.OPEN + k.SetConnection(ctx, connectionID, connection) + k.Logger(ctx).Info("connection state updated", "connection-id", connectionID, "previous-state", "TRYOPEN", "new-state", "OPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "connection", "open-confirm") + }() + + return nil +} diff --git a/x/ibc/core/03-connection/keeper/handshake_test.go b/x/ibc/core/03-connection/keeper/handshake_test.go new file mode 100644 index 000000000000..101c061a7526 --- /dev/null +++ b/x/ibc/core/03-connection/keeper/handshake_test.go @@ -0,0 +1,701 @@ +package keeper_test + +import ( + "time" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +// TestConnOpenInit - chainA initializes (INIT state) a connection with +// chainB which is yet UNINITIALIZED +func (suite *KeeperTestSuite) TestConnOpenInit() { + var ( + clientA string + clientB string + version *types.Version + delayPeriod uint64 + emptyConnBID bool + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"success", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + }, true}, + {"success with empty counterparty identifier", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + emptyConnBID = true + }, true}, + {"success with non empty version", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + version = types.ExportedVersionsToProto(types.GetCompatibleVersions())[0] + }, true}, + {"success with non zero delayPeriod", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + delayPeriod = uint64(time.Hour.Nanoseconds()) + }, true}, + + {"invalid version", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + version = &types.Version{} + }, false}, + {"couldn't add connection to client", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + // set clientA to invalid client identifier + clientA = "clientidentifier" + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + emptyConnBID = false // must be explicitly changed + version = nil // must be explicitly changed + + tc.malleate() + + connB := suite.chainB.GetFirstTestConnection(clientB, clientA) + if emptyConnBID { + connB.ID = "" + } + counterparty := types.NewCounterparty(clientB, connB.ID, suite.chainB.GetPrefix()) + + connectionID, err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenInit(suite.chainA.GetContext(), clientA, counterparty, version, delayPeriod) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(types.FormatConnectionIdentifier(0), connectionID) + } else { + suite.Require().Error(err) + suite.Require().Equal("", connectionID) + } + }) + } +} + +// TestConnOpenTry - chainB calls ConnOpenTry to verify the state of +// connection on chainA is INIT +func (suite *KeeperTestSuite) TestConnOpenTry() { + var ( + clientA string + clientB string + delayPeriod uint64 + previousConnectionID string + versions []exported.Version + consensusHeight exported.Height + counterpartyClient exported.ClientState + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"success", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + }, true}, + {"success with crossing hellos", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, connB, err := suite.coordinator.ConnOpenInitOnBothChains(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + previousConnectionID = connB.ID + }, true}, + {"success with delay period", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + delayPeriod = uint64(time.Hour.Nanoseconds()) + + // set delay period on counterparty to non-zero value + conn := suite.chainA.GetConnection(connA) + conn.DelayPeriod = delayPeriod + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, conn) + + // commit in order for proof to return correct value + suite.coordinator.CommitBlock(suite.chainA) + suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + }, true}, + {"invalid counterparty client", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + // Set an invalid client of chainA on chainB + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.ChainId = "wrongchainid" + + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClient) + }, false}, + {"consensus height >= latest height", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + consensusHeight = clienttypes.GetSelfHeight(suite.chainB.GetContext()) + }, false}, + {"self consensus state not found", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + consensusHeight = clienttypes.NewHeight(0, 1) + }, false}, + {"counterparty versions is empty", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + versions = nil + }, false}, + {"counterparty versions don't have a match", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + version := types.NewVersion("0.0", nil) + versions = []exported.Version{version} + }, false}, + {"connection state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + // chainA connection not created + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + }, false}, + {"client state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + // modify counterparty client without setting in store so it still passes validate but fails proof verification + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) + }, false}, + {"consensus state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + // give chainA wrong consensus state for chainB + consState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + suite.Require().True(ok) + + tmConsState.Timestamp = time.Now() + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientA, counterpartyClient.GetLatestHeight(), tmConsState) + + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + }, false}, + {"invalid previous connection is in TRYOPEN", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + // open init chainA + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // open try chainB + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + err = suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + previousConnectionID = connB.ID + }, false}, + {"invalid previous connection has invalid versions", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + // open init chainA + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // open try chainB + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + // modify connB to be in INIT with incorrect versions + connection, found := suite.chainB.App.IBCKeeper.ConnectionKeeper.GetConnection(suite.chainB.GetContext(), connB.ID) + suite.Require().True(found) + + connection.State = types.INIT + connection.Versions = []*types.Version{{}} + + suite.chainB.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainB.GetContext(), connB.ID, connection) + + err = suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + suite.Require().NoError(err) + + // retrieve client state of chainA to pass as counterpartyClient + counterpartyClient = suite.chainA.GetClientState(clientA) + + previousConnectionID = connB.ID + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + consensusHeight = clienttypes.ZeroHeight() // must be explicitly changed in malleate + versions = types.GetCompatibleVersions() // must be explicitly changed in malleate + previousConnectionID = "" + + tc.malleate() + + connA := suite.chainA.GetFirstTestConnection(clientA, clientB) + counterparty := types.NewCounterparty(clientA, connA.ID, suite.chainA.GetPrefix()) + + connectionKey := host.ConnectionKey(connA.ID) + proofInit, proofHeight := suite.chainA.QueryProof(connectionKey) + + if consensusHeight.IsZero() { + // retrieve consensus state height to provide proof for + consensusHeight = counterpartyClient.GetLatestHeight() + } + consensusKey := host.FullConsensusStateKey(clientA, consensusHeight) + proofConsensus, _ := suite.chainA.QueryProof(consensusKey) + + // retrieve proof of counterparty clientstate on chainA + clientKey := host.FullClientStateKey(clientA) + proofClient, _ := suite.chainA.QueryProof(clientKey) + + connectionID, err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenTry( + suite.chainB.GetContext(), previousConnectionID, counterparty, delayPeriod, clientB, counterpartyClient, + versions, proofInit, proofClient, proofConsensus, + proofHeight, consensusHeight, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(types.FormatConnectionIdentifier(0), connectionID) + } else { + suite.Require().Error(err) + suite.Require().Equal("", connectionID) + } + }) + } +} + +// TestConnOpenAck - Chain A (ID #1) calls TestConnOpenAck to acknowledge (ACK state) +// the initialization (TRYINIT) of the connection on Chain B (ID #2). +func (suite *KeeperTestSuite) TestConnOpenAck() { + var ( + clientA string + clientB string + consensusHeight exported.Height + version *types.Version + counterpartyClient exported.ClientState + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"success", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + }, true}, + {"success from tryopen", func() { + // chainA is in TRYOPEN, chainB is in TRYOPEN + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connB, connA, err := suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenTry(suite.chainA, suite.chainB, connA, connB) + suite.Require().NoError(err) + + // set chainB to TRYOPEN + connection := suite.chainB.GetConnection(connB) + connection.State = types.TRYOPEN + connection.Counterparty.ConnectionId = connA.ID + suite.chainB.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainB.GetContext(), connB.ID, connection) + // update clientB so state change is committed + suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + }, true}, + {"invalid counterparty client", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + // Set an invalid client of chainA on chainB + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.ChainId = "wrongchainid" + + suite.chainB.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainB.GetContext(), clientB, tmClient) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + }, false}, + {"consensus height >= latest height", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + consensusHeight = clienttypes.GetSelfHeight(suite.chainA.GetContext()) + }, false}, + {"connection not found", func() { + // connections are never created + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + }, false}, + {"invalid counterparty connection ID", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + // modify connB to set counterparty connection identifier to wrong identifier + connection, found := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), connA.ID) + suite.Require().True(found) + + connection.Counterparty.ConnectionId = "badconnectionid" + + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connection) + + err = suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + err = suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + suite.Require().NoError(err) + }, false}, + {"connection state is not INIT", func() { + // connection state is already OPEN on chainA + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenAck(suite.chainA, suite.chainB, connA, connB) + suite.Require().NoError(err) + }, false}, + {"connection is in INIT but the proposed version is invalid", func() { + // chainA is in INIT, chainB is in TRYOPEN + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + version = types.NewVersion("2.0", nil) + }, false}, + {"connection is in TRYOPEN but the set version in the connection is invalid", func() { + // chainA is in TRYOPEN, chainB is in TRYOPEN + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connB, connA, err := suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenTry(suite.chainA, suite.chainB, connA, connB) + suite.Require().NoError(err) + + // set chainB to TRYOPEN + connection := suite.chainB.GetConnection(connB) + connection.State = types.TRYOPEN + suite.chainB.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainB.GetContext(), connB.ID, connection) + + // update clientB so state change is committed + suite.coordinator.UpdateClient(suite.chainB, suite.chainA, clientB, exported.Tendermint) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + version = types.NewVersion("2.0", nil) + }, false}, + {"incompatible IBC versions", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + // set version to a non-compatible version + version = types.NewVersion("2.0", nil) + }, false}, + {"empty version", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + version = &types.Version{} + }, false}, + {"feature set verification failed - unsupported feature", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + version = types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_ORDERED", "ORDER_UNORDERED", "ORDER_DAG"}) + }, false}, + {"self consensus state not found", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + consensusHeight = clienttypes.NewHeight(0, 1) + }, false}, + {"connection state verification failed", func() { + // chainB connection is not in INIT + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, _, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + }, false}, + {"client state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + // modify counterparty client without setting in store so it still passes validate but fails proof verification + tmClient, ok := counterpartyClient.(*ibctmtypes.ClientState) + suite.Require().True(ok) + tmClient.LatestHeight = tmClient.LatestHeight.Increment().(clienttypes.Height) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + }, false}, + {"consensus state verification failed", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // retrieve client state of chainB to pass as counterpartyClient + counterpartyClient = suite.chainB.GetClientState(clientB) + + // give chainB wrong consensus state for chainA + consState, found := suite.chainB.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), clientB) + suite.Require().True(found) + + tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + suite.Require().True(ok) + + tmConsState.Timestamp = time.Now() + suite.chainB.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainB.GetContext(), clientB, counterpartyClient.GetLatestHeight(), tmConsState) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + version = types.ExportedVersionsToProto(types.GetCompatibleVersions())[0] // must be explicitly changed in malleate + consensusHeight = clienttypes.ZeroHeight() // must be explicitly changed in malleate + + tc.malleate() + + connA := suite.chainA.GetFirstTestConnection(clientA, clientB) + connB := suite.chainB.GetFirstTestConnection(clientB, clientA) + + connectionKey := host.ConnectionKey(connB.ID) + proofTry, proofHeight := suite.chainB.QueryProof(connectionKey) + + if consensusHeight.IsZero() { + // retrieve consensus state height to provide proof for + clientState := suite.chainB.GetClientState(clientB) + consensusHeight = clientState.GetLatestHeight() + } + consensusKey := host.FullConsensusStateKey(clientB, consensusHeight) + proofConsensus, _ := suite.chainB.QueryProof(consensusKey) + + // retrieve proof of counterparty clientstate on chainA + clientKey := host.FullClientStateKey(clientB) + proofClient, _ := suite.chainB.QueryProof(clientKey) + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.ConnOpenAck( + suite.chainA.GetContext(), connA.ID, counterpartyClient, version, connB.ID, + proofTry, proofClient, proofConsensus, proofHeight, consensusHeight, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestConnOpenConfirm - chainB calls ConnOpenConfirm to confirm that +// chainA state is now OPEN. +func (suite *KeeperTestSuite) TestConnOpenConfirm() { + var ( + clientA string + clientB string + ) + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"success", func() { + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenAck(suite.chainA, suite.chainB, connA, connB) + suite.Require().NoError(err) + }, true}, + {"connection not found", func() { + // connections are never created + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + }, false}, + {"chain B's connection state is not TRYOPEN", func() { + // connections are OPEN + clientA, clientB, _, _ = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + }, false}, + {"connection state verification failed", func() { + // chainA is in INIT + clientA, clientB = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.ConnOpenTry(suite.chainB, suite.chainA, connB, connA) + suite.Require().NoError(err) + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + tc.malleate() + + connA := suite.chainA.GetFirstTestConnection(clientA, clientB) + connB := suite.chainB.GetFirstTestConnection(clientB, clientA) + + connectionKey := host.ConnectionKey(connA.ID) + proofAck, proofHeight := suite.chainA.QueryProof(connectionKey) + + err := suite.chainB.App.IBCKeeper.ConnectionKeeper.ConnOpenConfirm( + suite.chainB.GetContext(), connB.ID, proofAck, proofHeight, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/core/03-connection/keeper/keeper.go b/x/ibc/core/03-connection/keeper/keeper.go new file mode 100644 index 000000000000..6637268687c0 --- /dev/null +++ b/x/ibc/core/03-connection/keeper/keeper.go @@ -0,0 +1,198 @@ +package keeper + +import ( + "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" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// Keeper defines the IBC connection keeper +type Keeper struct { + // implements gRPC QueryServer interface + types.QueryServer + + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + clientKeeper types.ClientKeeper +} + +// NewKeeper creates a new IBC connection Keeper instance +func NewKeeper(cdc codec.BinaryMarshaler, key sdk.StoreKey, ck types.ClientKeeper) Keeper { + return Keeper{ + storeKey: key, + cdc: cdc, + clientKeeper: ck, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) +} + +// GetCommitmentPrefix returns the IBC connection store prefix as a commitment +// Prefix +func (k Keeper) GetCommitmentPrefix() exported.Prefix { + return commitmenttypes.NewMerklePrefix([]byte(k.storeKey.Name())) +} + +// GenerateConnectionIdentifier returns the next connection identifier. +func (k Keeper) GenerateConnectionIdentifier(ctx sdk.Context) string { + nextConnSeq := k.GetNextConnectionSequence(ctx) + connectionID := types.FormatConnectionIdentifier(nextConnSeq) + + nextConnSeq++ + k.SetNextConnectionSequence(ctx, nextConnSeq) + return connectionID +} + +// GetConnection returns a connection with a particular identifier +func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.ConnectionEnd, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.ConnectionKey(connectionID)) + if bz == nil { + return types.ConnectionEnd{}, false + } + + var connection types.ConnectionEnd + k.cdc.MustUnmarshalBinaryBare(bz, &connection) + + return connection, true +} + +// SetConnection sets a connection to the store +func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(&connection) + store.Set(host.ConnectionKey(connectionID), bz) +} + +// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the +// given height. +func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.ConnectionEnd, height exported.Height) (uint64, error) { + consensusState, found := k.clientKeeper.GetClientConsensusState( + ctx, connection.GetClientID(), height, + ) + + if !found { + return 0, sdkerrors.Wrapf( + clienttypes.ErrConsensusStateNotFound, + "clientID (%s), height (%s)", connection.GetClientID(), height, + ) + } + + return consensusState.GetTimestamp(), nil +} + +// GetClientConnectionPaths returns all the connection paths stored under a +// particular client +func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.ClientConnectionsKey(clientID)) + if bz == nil { + return nil, false + } + + var clientPaths types.ClientPaths + k.cdc.MustUnmarshalBinaryBare(bz, &clientPaths) + return clientPaths.Paths, true +} + +// SetClientConnectionPaths sets the connections paths for client +func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths []string) { + store := ctx.KVStore(k.storeKey) + clientPaths := types.ClientPaths{Paths: paths} + bz := k.cdc.MustMarshalBinaryBare(&clientPaths) + store.Set(host.ClientConnectionsKey(clientID), bz) +} + +// GetNextConnectionSequence gets the next connection sequence from the store. +func (k Keeper) GetNextConnectionSequence(ctx sdk.Context) uint64 { + store := ctx.KVStore(k.storeKey) + bz := store.Get([]byte(types.KeyNextConnectionSequence)) + if bz == nil { + panic("next connection sequence is nil") + } + + return sdk.BigEndianToUint64(bz) +} + +// SetNextConnectionSequence sets the next connection sequence to the store. +func (k Keeper) SetNextConnectionSequence(ctx sdk.Context, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set([]byte(types.KeyNextConnectionSequence), bz) +} + +// GetAllClientConnectionPaths returns all stored clients connection id paths. It +// will ignore the clients that haven't initialized a connection handshake since +// no paths are stored. +func (k Keeper) GetAllClientConnectionPaths(ctx sdk.Context) []types.ConnectionPaths { + var allConnectionPaths []types.ConnectionPaths + k.clientKeeper.IterateClients(ctx, func(clientID string, cs exported.ClientState) bool { + paths, found := k.GetClientConnectionPaths(ctx, clientID) + if !found { + // continue when connection handshake is not initialized + return false + } + connPaths := types.NewConnectionPaths(clientID, paths) + allConnectionPaths = append(allConnectionPaths, connPaths) + return false + }) + + return allConnectionPaths +} + +// IterateConnections provides an iterator over all ConnectionEnd objects. +// For each ConnectionEnd, cb will be called. If the cb returns true, the +// iterator will close and stop. +func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.IdentifiedConnection) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyConnectionPrefix)) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var connection types.ConnectionEnd + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &connection) + + connectionID := host.MustParseConnectionPath(string(iterator.Key())) + identifiedConnection := types.NewIdentifiedConnection(connectionID, connection) + if cb(identifiedConnection) { + break + } + } +} + +// GetAllConnections returns all stored ConnectionEnd objects. +func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.IdentifiedConnection) { + k.IterateConnections(ctx, func(connection types.IdentifiedConnection) bool { + connections = append(connections, connection) + return false + }) + return connections +} + +// addConnectionToClient is used to add a connection identifier to the set of +// connections associated with a client. +func (k Keeper) addConnectionToClient(ctx sdk.Context, clientID, connectionID string) error { + _, found := k.clientKeeper.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + } + + conns, found := k.GetClientConnectionPaths(ctx, clientID) + if !found { + conns = []string{} + } + + conns = append(conns, connectionID) + k.SetClientConnectionPaths(ctx, clientID, conns) + return nil +} diff --git a/x/ibc/core/03-connection/keeper/keeper_test.go b/x/ibc/core/03-connection/keeper/keeper_test.go new file mode 100644 index 000000000000..f2a1124b551b --- /dev/null +++ b/x/ibc/core/03-connection/keeper/keeper_test.go @@ -0,0 +1,133 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) TestSetAndGetConnection() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + connA := suite.chainA.GetFirstTestConnection(clientA, clientB) + _, existed := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), connA.ID) + suite.Require().False(existed) + + suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + _, existed = suite.chainA.App.IBCKeeper.ConnectionKeeper.GetConnection(suite.chainA.GetContext(), connA.ID) + suite.Require().True(existed) +} + +func (suite *KeeperTestSuite) TestSetAndGetClientConnectionPaths() { + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + _, existed := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetClientConnectionPaths(suite.chainA.GetContext(), clientA) + suite.False(existed) + + connections := []string{"connectionA", "connectionB"} + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetClientConnectionPaths(suite.chainA.GetContext(), clientA, connections) + paths, existed := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetClientConnectionPaths(suite.chainA.GetContext(), clientA) + suite.True(existed) + suite.EqualValues(connections, paths) +} + +// create 2 connections: A0 - B0, A1 - B1 +func (suite KeeperTestSuite) TestGetAllConnections() { + clientA, clientB, connA0, connB0 := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + connA1, connB1 := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + + counterpartyB0 := types.NewCounterparty(clientB, connB0.ID, suite.chainB.GetPrefix()) // connection B0 + counterpartyB1 := types.NewCounterparty(clientB, connB1.ID, suite.chainB.GetPrefix()) // connection B1 + + conn1 := types.NewConnectionEnd(types.OPEN, clientA, counterpartyB0, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 0) // A0 - B0 + conn2 := types.NewConnectionEnd(types.OPEN, clientA, counterpartyB1, types.ExportedVersionsToProto(types.GetCompatibleVersions()), 0) // A1 - B1 + + iconn1 := types.NewIdentifiedConnection(connA0.ID, conn1) + iconn2 := types.NewIdentifiedConnection(connA1.ID, conn2) + + expConnections := []types.IdentifiedConnection{iconn1, iconn2} + + connections := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetAllConnections(suite.chainA.GetContext()) + suite.Require().Len(connections, len(expConnections)) + suite.Require().Equal(expConnections, connections) +} + +// the test creates 2 clients clientA0 and clientA1. clientA0 has a single +// connection and clientA1 has 2 connections. +func (suite KeeperTestSuite) TestGetAllClientConnectionPaths() { + clientA0, _, connA0, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + clientA1, clientB1, connA1, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + connA2, _ := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA1, clientB1) + + expPaths := []types.ConnectionPaths{ + types.NewConnectionPaths(clientA0, []string{connA0.ID}), + types.NewConnectionPaths(clientA1, []string{connA1.ID, connA2.ID}), + } + + connPaths := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetAllClientConnectionPaths(suite.chainA.GetContext()) + suite.Require().Len(connPaths, 2) + suite.Require().Equal(expPaths, connPaths) +} + +// TestGetTimestampAtHeight verifies if the clients on each chain return the +// correct timestamp for the other chain. +func (suite *KeeperTestSuite) TestGetTimestampAtHeight() { + var connection types.ConnectionEnd + + cases := []struct { + msg string + malleate func() + expPass bool + }{ + {"verification success", func() { + _, _, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + connection = suite.chainA.GetConnection(connA) + }, true}, + {"consensus state not found", func() { + // any non-nil value of connection is valid + suite.Require().NotNil(connection) + }, false}, + } + + for _, tc := range cases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + actualTimestamp, err := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetTimestampAtHeight( + suite.chainA.GetContext(), connection, suite.chainB.LastHeader.GetHeight(), + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().EqualValues(uint64(suite.chainB.LastHeader.GetTime().UnixNano()), actualTimestamp) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/core/03-connection/keeper/verify.go b/x/ibc/core/03-connection/keeper/verify.go new file mode 100644 index 000000000000..ddb1ea6b9612 --- /dev/null +++ b/x/ibc/core/03-connection/keeper/verify.go @@ -0,0 +1,225 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// VerifyClientState verifies a proof of a client state of the running machine +// stored on the target machine +func (k Keeper) VerifyClientState( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + clientState exported.ClientState, +) error { + clientID := connection.GetClientID() + targetClient, found := k.clientKeeper.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + } + + if err := targetClient.VerifyClientState( + k.clientKeeper.ClientStore(ctx, clientID), k.cdc, height, + connection.GetCounterparty().GetPrefix(), connection.GetCounterparty().GetClientID(), proof, clientState); err != nil { + return sdkerrors.Wrapf(err, "failed client state verification for target client: %s", connection.GetClientID()) + } + + return nil +} + +// VerifyClientConsensusState verifies a proof of the consensus state of the +// specified client stored on the target machine. +func (k Keeper) VerifyClientConsensusState( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + consensusHeight exported.Height, + proof []byte, + consensusState exported.ConsensusState, +) error { + clientID := connection.GetClientID() + clientState, found := k.clientKeeper.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, clientID) + } + + if err := clientState.VerifyClientConsensusState( + k.clientKeeper.ClientStore(ctx, clientID), k.cdc, height, + connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState, + ); err != nil { + return sdkerrors.Wrapf(err, "failed consensus state verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored on the target machine. +func (k Keeper) VerifyConnectionState( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + connectionID string, + connectionEnd exported.ConnectionI, // opposite connection +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyConnectionState( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, + ); err != nil { + return sdkerrors.Wrapf(err, "failed connection state verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the target machine. +func (k Keeper) VerifyChannelState( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + channel exported.ChannelI, +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyChannelState( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + connection.GetCounterparty().GetPrefix(), proof, + portID, channelID, channel, + ); err != nil { + return sdkerrors.Wrapf(err, "failed channel state verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. +func (k Keeper) VerifyPacketCommitment( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyPacketCommitment( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + uint64(ctx.BlockTime().UnixNano()), connection.GetDelayPeriod(), + connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, commitmentBytes, + ); err != nil { + return sdkerrors.Wrapf(err, "failed packet commitment verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. +func (k Keeper) VerifyPacketAcknowledgement( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyPacketAcknowledgement( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + uint64(ctx.BlockTime().UnixNano()), connection.GetDelayPeriod(), + connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, acknowledgement, + ); err != nil { + return sdkerrors.Wrapf(err, "failed packet acknowledgement verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyPacketReceiptAbsence verifies a proof of the absence of an +// incoming packet receipt at the specified port, specified channel, and +// specified sequence. +func (k Keeper) VerifyPacketReceiptAbsence( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyPacketReceiptAbsence( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + uint64(ctx.BlockTime().UnixNano()), connection.GetDelayPeriod(), + connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + sequence, + ); err != nil { + return sdkerrors.Wrapf(err, "failed packet receipt absence verification for client (%s)", connection.GetClientID()) + } + + return nil +} + +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. +func (k Keeper) VerifyNextSequenceRecv( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, +) error { + clientState, found := k.clientKeeper.GetClientState(ctx, connection.GetClientID()) + if !found { + return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) + } + + if err := clientState.VerifyNextSequenceRecv( + k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, + uint64(ctx.BlockTime().UnixNano()), connection.GetDelayPeriod(), + connection.GetCounterparty().GetPrefix(), proof, portID, channelID, + nextSequenceRecv, + ); err != nil { + return sdkerrors.Wrapf(err, "failed next sequence receive verification for client (%s)", connection.GetClientID()) + } + + return nil +} diff --git a/x/ibc/core/03-connection/keeper/verify_test.go b/x/ibc/core/03-connection/keeper/verify_test.go new file mode 100644 index 000000000000..2d94955d8ef4 --- /dev/null +++ b/x/ibc/core/03-connection/keeper/verify_test.go @@ -0,0 +1,514 @@ +package keeper_test + +import ( + "fmt" + "time" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +var defaultTimeoutHeight = clienttypes.NewHeight(0, 100000) + +// TestVerifyClientState verifies a client state of chainA +// stored on clientB (which is on chainB) +func (suite *KeeperTestSuite) TestVerifyClientState() { + cases := []struct { + msg string + changeClientID bool + heightDiff uint64 + malleateCounterparty bool + expPass bool + }{ + {"verification success", false, 0, false, true}, + {"client state not found", true, 0, false, false}, + {"consensus state for proof height not found", false, 5, false, false}, + {"verification failed", false, 0, true, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + _, clientB, connA, _ := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + counterpartyClient, clientProof := suite.chainB.QueryClientStateProof(clientB) + proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + + if tc.malleateCounterparty { + tmClient, _ := counterpartyClient.(*ibctmtypes.ClientState) + tmClient.ChainId = "wrongChainID" + } + + connection := suite.chainA.GetConnection(connA) + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyClientState( + suite.chainA.GetContext(), connection, + malleateHeight(proofHeight, tc.heightDiff), clientProof, counterpartyClient, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyClientConsensusState verifies that the consensus state of +// chainA stored on clientB (which is on chainB) matches the consensus +// state for chainA at that height. +func (suite *KeeperTestSuite) TestVerifyClientConsensusState() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + changeClientID bool + heightDiff uint64 + ) + cases := []struct { + msg string + malleate func() + expPass bool + }{ + {"verification success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + }, true}, + {"client state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + changeClientID = true + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + heightDiff = 5 + }, false}, + {"verification failed", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + clientB := connB.ClientID + clientState := suite.chainB.GetClientState(clientB) + + // give chainB wrong consensus state for chainA + consState, found := suite.chainB.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), clientB) + suite.Require().True(found) + + tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + suite.Require().True(ok) + + tmConsState.Timestamp = time.Now() + suite.chainB.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainB.GetContext(), clientB, clientState.GetLatestHeight(), tmConsState) + + suite.coordinator.CommitBlock(suite.chainB) + }, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed in malleate + changeClientID = false // must be explicitly changed in malleate + + tc.malleate() + + connection := suite.chainA.GetConnection(connA) + if changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + proof, consensusHeight := suite.chainB.QueryConsensusStateProof(connB.ClientID) + proofHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()-1)) + consensusState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetSelfConsensusState(suite.chainA.GetContext(), consensusHeight) + suite.Require().True(found) + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState( + suite.chainA.GetContext(), connection, + malleateHeight(proofHeight, heightDiff), consensusHeight, proof, consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyConnectionState verifies the connection state of the connection +// on chainB. The connections on chainA and chainB are fully opened. +func (suite *KeeperTestSuite) TestVerifyConnectionState() { + cases := []struct { + msg string + changeClientID bool + changeConnectionState bool + heightDiff uint64 + expPass bool + }{ + {"verification success", false, false, 0, true}, + {"client state not found - changed client ID", true, false, 0, false}, + {"consensus state not found - increased proof height", false, false, 5, false}, + {"verification failed - connection state is different than proof", false, true, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + connection := suite.chainA.GetConnection(connA) + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + expectedConnection := suite.chainB.GetConnection(connB) + + connectionKey := host.ConnectionKey(connB.ID) + proof, proofHeight := suite.chainB.QueryProof(connectionKey) + + if tc.changeConnectionState { + expectedConnection.State = types.TRYOPEN + } + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyConnectionState( + suite.chainA.GetContext(), connection, + malleateHeight(proofHeight, tc.heightDiff), proof, connB.ID, expectedConnection, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyChannelState verifies the channel state of the channel on +// chainB. The channels on chainA and chainB are fully opened. +func (suite *KeeperTestSuite) TestVerifyChannelState() { + cases := []struct { + msg string + changeClientID bool + changeChannelState bool + heightDiff uint64 + expPass bool + }{ + {"verification success", false, false, 0, true}, + {"client state not found- changed client ID", true, false, 0, false}, + {"consensus state not found - increased proof height", false, false, 5, false}, + {"verification failed - changed channel state", false, true, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + _, _, connA, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + connection := suite.chainA.GetConnection(connA) + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + channelKey := host.ChannelKey(channelB.PortID, channelB.ID) + proof, proofHeight := suite.chainB.QueryProof(channelKey) + + channel := suite.chainB.GetChannel(channelB) + if tc.changeChannelState { + channel.State = channeltypes.TRYOPEN + } + + err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyChannelState( + suite.chainA.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof, + channelB.PortID, channelB.ID, channel, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyPacketCommitmentState has chainB verify the packet commitment +// on channelA. The channels on chainA and chainB are fully opened and a +// packet is sent from chainA to chainB, but has not been received. +func (suite *KeeperTestSuite) TestVerifyPacketCommitment() { + cases := []struct { + msg string + changeClientID bool + changePacketCommitmentState bool + heightDiff uint64 + delayPeriod uint64 + expPass bool + }{ + {"verification success", false, false, 0, 0, true}, + {"verification success: delay period passed", false, false, 0, uint64(1 * time.Second.Nanoseconds()), true}, + {"delay period has not passed", false, false, 0, uint64(1 * time.Hour.Nanoseconds()), false}, + {"client state not found- changed client ID", true, false, 0, 0, false}, + {"consensus state not found - increased proof height", false, false, 5, 0, false}, + {"verification failed - changed packet commitment state", false, true, 0, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + _, clientB, _, connB, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + connection := suite.chainB.GetConnection(connB) + connection.DelayPeriod = tc.delayPeriod + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, defaultTimeoutHeight, 0) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + commitmentKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(commitmentKey) + + if tc.changePacketCommitmentState { + packet.Data = []byte(ibctesting.InvalidID) + } + + commitment := channeltypes.CommitPacket(suite.chainB.App.IBCKeeper.Codec(), packet) + err = suite.chainB.App.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment( + suite.chainB.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof, + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyPacketAcknowledgement has chainA verify the acknowledgement on +// channelB. The channels on chainA and chainB are fully opened and a packet +// is sent from chainA to chainB and received. +func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() { + cases := []struct { + msg string + changeClientID bool + changeAcknowledgement bool + heightDiff uint64 + delayPeriod uint64 + expPass bool + }{ + {"verification success", false, false, 0, 0, true}, + {"verification success: delay period passed", false, false, 0, uint64(1 * time.Second.Nanoseconds()), true}, + {"delay period has not passed", false, false, 0, uint64(1 * time.Hour.Nanoseconds()), false}, + {"client state not found- changed client ID", true, false, 0, 0, false}, + {"consensus state not found - increased proof height", false, false, 5, 0, false}, + {"verification failed - changed acknowledgement", false, true, 0, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + connection := suite.chainA.GetConnection(connA) + connection.DelayPeriod = tc.delayPeriod + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + // send and receive packet + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, defaultTimeoutHeight, 0) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // increment receiving chain's (chainB) time by 2 hour to always pass receive + suite.coordinator.IncrementTimeBy(time.Hour * 2) + suite.coordinator.CommitBlock(suite.chainB) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + packetAckKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetAckKey) + + ack := ibcmock.MockAcknowledgement + if tc.changeAcknowledgement { + ack = []byte(ibctesting.InvalidID) + } + + err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement( + suite.chainA.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ack, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyPacketReceiptAbsence has chainA verify the receipt +// absence on channelB. The channels on chainA and chainB are fully opened and +// a packet is sent from chainA to chainB and not received. +func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() { + cases := []struct { + msg string + changeClientID bool + recvAck bool + heightDiff uint64 + delayPeriod uint64 + expPass bool + }{ + {"verification success", false, false, 0, 0, true}, + {"verification success: delay period passed", false, false, 0, uint64(1 * time.Second.Nanoseconds()), true}, + {"delay period has not passed", false, false, 0, uint64(1 * time.Hour.Nanoseconds()), false}, + {"client state not found - changed client ID", true, false, 0, 0, false}, + {"consensus state not found - increased proof height", false, false, 5, 0, false}, + {"verification failed - acknowledgement was received", false, true, 0, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + connection := suite.chainA.GetConnection(connA) + connection.DelayPeriod = tc.delayPeriod + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + // send, only receive if specified + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, defaultTimeoutHeight, 0) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + if tc.recvAck { + // increment receiving chain's (chainB) time by 2 hour to always pass receive + suite.coordinator.IncrementTimeBy(time.Hour * 2) + suite.coordinator.CommitBlock(suite.chainB) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + } else { + // need to update height to prove absence + suite.coordinator.CommitBlock(suite.chainA, suite.chainB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + } + + packetReceiptKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetReceiptKey) + + err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketReceiptAbsence( + suite.chainA.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestVerifyNextSequenceRecv has chainA verify the next sequence receive on +// channelB. The channels on chainA and chainB are fully opened and a packet +// is sent from chainA to chainB and received. +func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() { + cases := []struct { + msg string + changeClientID bool + offsetSeq uint64 + heightDiff uint64 + delayPeriod uint64 + expPass bool + }{ + {"verification success", false, 0, 0, 0, true}, + {"verification success: delay period passed", false, 0, 0, uint64(1 * time.Second.Nanoseconds()), true}, + {"delay period has not passed", false, 0, 0, uint64(1 * time.Hour.Nanoseconds()), false}, + {"client state not found- changed client ID", true, 0, 0, 0, false}, + {"consensus state not found - increased proof height", false, 0, 5, 0, false}, + {"verification failed - wrong expected next seq recv", false, 1, 0, 0, false}, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + + clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + connection := suite.chainA.GetConnection(connA) + connection.DelayPeriod = tc.delayPeriod + if tc.changeClientID { + connection.ClientId = ibctesting.InvalidID + } + + // send and receive packet + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, defaultTimeoutHeight, 0) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // increment receiving chain's (chainB) time by 2 hour to always pass receive + suite.coordinator.IncrementTimeBy(time.Hour * 2) + suite.coordinator.CommitBlock(suite.chainB) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + nextSeqRecvKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + proof, proofHeight := suite.chainB.QueryProof(nextSeqRecvKey) + + err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv( + suite.chainA.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()+tc.offsetSeq, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func malleateHeight(height exported.Height, diff uint64) exported.Height { + return clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+diff) +} diff --git a/x/ibc/core/03-connection/module.go b/x/ibc/core/03-connection/module.go new file mode 100644 index 000000000000..6100caa4622e --- /dev/null +++ b/x/ibc/core/03-connection/module.go @@ -0,0 +1,29 @@ +package connection + +import ( + "github.com/gogo/protobuf/grpc" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" +) + +// Name returns the IBC connection ICS name. +func Name() string { + return types.SubModuleName +} + +// GetTxCmd returns the root tx command for the IBC connections. +func GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd returns the root query command for the IBC connections. +func GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterQueryService registers the gRPC query service for IBC connections. +func RegisterQueryService(server grpc.Server, queryServer types.QueryServer) { + types.RegisterQueryServer(server, queryServer) +} diff --git a/x/ibc/core/03-connection/simulation/decoder.go b/x/ibc/core/03-connection/simulation/decoder.go new file mode 100644 index 000000000000..ef988a103f5e --- /dev/null +++ b/x/ibc/core/03-connection/simulation/decoder.go @@ -0,0 +1,32 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding connection type. +func NewDecodeStore(cdc codec.BinaryMarshaler, kvA, kvB kv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, host.KeyClientStorePrefix) && bytes.HasSuffix(kvA.Key, []byte(host.KeyConnectionPrefix)): + var clientConnectionsA, clientConnectionsB types.ClientPaths + cdc.MustUnmarshalBinaryBare(kvA.Value, &clientConnectionsA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &clientConnectionsB) + return fmt.Sprintf("ClientPaths A: %v\nClientPaths B: %v", clientConnectionsA, clientConnectionsB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyConnectionPrefix)): + var connectionA, connectionB types.ConnectionEnd + cdc.MustUnmarshalBinaryBare(kvA.Value, &connectionA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &connectionB) + return fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connectionA, connectionB), true + + default: + return "", false + } +} diff --git a/x/ibc/core/03-connection/simulation/decoder_test.go b/x/ibc/core/03-connection/simulation/decoder_test.go new file mode 100644 index 000000000000..673bf640065b --- /dev/null +++ b/x/ibc/core/03-connection/simulation/decoder_test.go @@ -0,0 +1,69 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.AppCodec() + + connectionID := "connectionidone" + + connection := types.ConnectionEnd{ + ClientId: "clientidone", + Versions: types.ExportedVersionsToProto(types.GetCompatibleVersions()), + } + + paths := types.ClientPaths{ + Paths: []string{connectionID}, + } + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: host.ClientConnectionsKey(connection.ClientId), + Value: cdc.MustMarshalBinaryBare(&paths), + }, + { + Key: host.ConnectionKey(connectionID), + Value: cdc.MustMarshalBinaryBare(&connection), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientPaths", fmt.Sprintf("ClientPaths A: %v\nClientPaths B: %v", paths, paths)}, + {"ConnectionEnd", fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connection, connection)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(cdc, kvPairs.Pairs[i], kvPairs.Pairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs.Pairs[i].Key)) + require.Empty(t, res, string(kvPairs.Pairs[i].Key)) + } else { + require.True(t, found, string(kvPairs.Pairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs.Pairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/core/03-connection/simulation/genesis.go b/x/ibc/core/03-connection/simulation/genesis.go new file mode 100644 index 000000000000..43b0823776df --- /dev/null +++ b/x/ibc/core/03-connection/simulation/genesis.go @@ -0,0 +1,13 @@ +package simulation + +import ( + "math/rand" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" +) + +// GenConnectionGenesis returns the default connection genesis state. +func GenConnectionGenesis(_ *rand.Rand, _ []simtypes.Account) types.GenesisState { + return types.DefaultGenesisState() +} diff --git a/x/ibc/core/03-connection/types/codec.go b/x/ibc/core/03-connection/types/codec.go new file mode 100644 index 000000000000..6105fa9ee1db --- /dev/null +++ b/x/ibc/core/03-connection/types/codec.go @@ -0,0 +1,47 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces register the ibc interfaces submodule implementations to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "ibc.core.connection.v1.ConnectionI", + (*exported.ConnectionI)(nil), + &ConnectionEnd{}, + ) + registry.RegisterInterface( + "ibc.core.connection.v1.CounterpartyConnectionI", + (*exported.CounterpartyConnectionI)(nil), + &Counterparty{}, + ) + registry.RegisterInterface( + "ibc.core.connection.v1.Version", + (*exported.Version)(nil), + &Version{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgConnectionOpenInit{}, + &MsgConnectionOpenTry{}, + &MsgConnectionOpenAck{}, + &MsgConnectionOpenConfirm{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + // SubModuleCdc references the global x/ibc/core/03-connection 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/core/03-connection and + // defined at the application level. + SubModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) +) diff --git a/x/ibc/core/03-connection/types/connection.go b/x/ibc/core/03-connection/types/connection.go new file mode 100644 index 000000000000..197af83cad99 --- /dev/null +++ b/x/ibc/core/03-connection/types/connection.go @@ -0,0 +1,127 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.ConnectionI = (*ConnectionEnd)(nil) + +// NewConnectionEnd creates a new ConnectionEnd instance. +func NewConnectionEnd(state State, clientID string, counterparty Counterparty, versions []*Version, delayPeriod uint64) ConnectionEnd { + return ConnectionEnd{ + ClientId: clientID, + Versions: versions, + State: state, + Counterparty: counterparty, + DelayPeriod: delayPeriod, + } +} + +// GetState implements the Connection interface +func (c ConnectionEnd) GetState() int32 { + return int32(c.State) +} + +// GetClientID implements the Connection interface +func (c ConnectionEnd) GetClientID() string { + return c.ClientId +} + +// GetCounterparty implements the Connection interface +func (c ConnectionEnd) GetCounterparty() exported.CounterpartyConnectionI { + return c.Counterparty +} + +// GetVersions implements the Connection interface +func (c ConnectionEnd) GetVersions() []exported.Version { + return ProtoVersionsToExported(c.Versions) +} + +// GetDelayPeriod implements the Connection interface +func (c ConnectionEnd) GetDelayPeriod() uint64 { + return c.DelayPeriod +} + +// ValidateBasic implements the Connection interface. +// NOTE: the protocol supports that the connection and client IDs match the +// counterparty's. +func (c ConnectionEnd) ValidateBasic() error { + if err := host.ClientIdentifierValidator(c.ClientId); err != nil { + return sdkerrors.Wrap(err, "invalid client ID") + } + if len(c.Versions) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "empty connection versions") + } + for _, version := range c.Versions { + if err := ValidateVersion(version); err != nil { + return err + } + } + return c.Counterparty.ValidateBasic() +} + +var _ exported.CounterpartyConnectionI = (*Counterparty)(nil) + +// NewCounterparty creates a new Counterparty instance. +func NewCounterparty(clientID, connectionID string, prefix commitmenttypes.MerklePrefix) Counterparty { + return Counterparty{ + ClientId: clientID, + ConnectionId: connectionID, + Prefix: prefix, + } +} + +// GetClientID implements the CounterpartyConnectionI interface +func (c Counterparty) GetClientID() string { + return c.ClientId +} + +// GetConnectionID implements the CounterpartyConnectionI interface +func (c Counterparty) GetConnectionID() string { + return c.ConnectionId +} + +// GetPrefix implements the CounterpartyConnectionI interface +func (c Counterparty) GetPrefix() exported.Prefix { + return &c.Prefix +} + +// ValidateBasic performs a basic validation check of the identifiers and prefix +func (c Counterparty) ValidateBasic() error { + if c.ConnectionId != "" { + if err := host.ConnectionIdentifierValidator(c.ConnectionId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty connection ID") + } + } + if err := host.ClientIdentifierValidator(c.ClientId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty client ID") + } + if c.Prefix.Empty() { + return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty prefix cannot be empty") + } + return nil +} + +// NewIdentifiedConnection creates a new IdentifiedConnection instance +func NewIdentifiedConnection(connectionID string, conn ConnectionEnd) IdentifiedConnection { + return IdentifiedConnection{ + Id: connectionID, + ClientId: conn.ClientId, + Versions: conn.Versions, + State: conn.State, + Counterparty: conn.Counterparty, + DelayPeriod: conn.DelayPeriod, + } +} + +// ValidateBasic performs a basic validation of the connection identifier and connection fields. +func (ic IdentifiedConnection) ValidateBasic() error { + if err := host.ConnectionIdentifierValidator(ic.Id); err != nil { + return sdkerrors.Wrap(err, "invalid connection ID") + } + connection := NewConnectionEnd(ic.State, ic.ClientId, ic.Counterparty, ic.Versions, ic.DelayPeriod) + return connection.ValidateBasic() +} diff --git a/x/ibc/core/03-connection/types/connection.pb.go b/x/ibc/core/03-connection/types/connection.pb.go new file mode 100644 index 000000000000..14b85c62d432 --- /dev/null +++ b/x/ibc/core/03-connection/types/connection.pb.go @@ -0,0 +1,1800 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/connection/v1/connection.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + _ "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 + +// State defines if a connection is in one of the following states: +// INIT, TRYOPEN, OPEN or UNINITIALIZED. +type State int32 + +const ( + // Default State + UNINITIALIZED State = 0 + // A connection end has just started the opening handshake. + INIT State = 1 + // A connection end has acknowledged the handshake step on the counterparty + // chain. + TRYOPEN State = 2 + // A connection end has completed the handshake. + OPEN State = 3 +) + +var State_name = map[int32]string{ + 0: "STATE_UNINITIALIZED_UNSPECIFIED", + 1: "STATE_INIT", + 2: "STATE_TRYOPEN", + 3: "STATE_OPEN", +} + +var State_value = map[string]int32{ + "STATE_UNINITIALIZED_UNSPECIFIED": 0, + "STATE_INIT": 1, + "STATE_TRYOPEN": 2, + "STATE_OPEN": 3, +} + +func (x State) String() string { + return proto.EnumName(State_name, int32(x)) +} + +func (State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{0} +} + +// ConnectionEnd defines a stateful object on a chain connected to another +// separate one. +// NOTE: there must only be 2 defined ConnectionEnds to establish +// a connection between two chains. +type ConnectionEnd struct { + // client associated with this connection. + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection. + Versions []*Version `protobuf:"bytes,2,rep,name=versions,proto3" json:"versions,omitempty"` + // current state of the connection end. + State State `protobuf:"varint,3,opt,name=state,proto3,enum=ibc.core.connection.v1.State" json:"state,omitempty"` + // counterparty chain associated with this connection. + Counterparty Counterparty `protobuf:"bytes,4,opt,name=counterparty,proto3" json:"counterparty"` + // delay period that must pass before a consensus state can be used for packet-verification + // NOTE: delay period logic is only implemented by some clients. + DelayPeriod uint64 `protobuf:"varint,5,opt,name=delay_period,json=delayPeriod,proto3" json:"delay_period,omitempty" yaml:"delay_period"` +} + +func (m *ConnectionEnd) Reset() { *m = ConnectionEnd{} } +func (m *ConnectionEnd) String() string { return proto.CompactTextString(m) } +func (*ConnectionEnd) ProtoMessage() {} +func (*ConnectionEnd) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{0} +} +func (m *ConnectionEnd) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConnectionEnd) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConnectionEnd.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 *ConnectionEnd) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectionEnd.Merge(m, src) +} +func (m *ConnectionEnd) XXX_Size() int { + return m.Size() +} +func (m *ConnectionEnd) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectionEnd.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectionEnd proto.InternalMessageInfo + +// IdentifiedConnection defines a connection with additional connection +// identifier field. +type IdentifiedConnection struct { + // connection identifier. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"` + // client associated with this connection. + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // IBC version which can be utilised to determine encodings or protocols for + // channels or packets utilising this connection + Versions []*Version `protobuf:"bytes,3,rep,name=versions,proto3" json:"versions,omitempty"` + // current state of the connection end. + State State `protobuf:"varint,4,opt,name=state,proto3,enum=ibc.core.connection.v1.State" json:"state,omitempty"` + // counterparty chain associated with this connection. + Counterparty Counterparty `protobuf:"bytes,5,opt,name=counterparty,proto3" json:"counterparty"` + // delay period associated with this connection. + DelayPeriod uint64 `protobuf:"varint,6,opt,name=delay_period,json=delayPeriod,proto3" json:"delay_period,omitempty" yaml:"delay_period"` +} + +func (m *IdentifiedConnection) Reset() { *m = IdentifiedConnection{} } +func (m *IdentifiedConnection) String() string { return proto.CompactTextString(m) } +func (*IdentifiedConnection) ProtoMessage() {} +func (*IdentifiedConnection) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{1} +} +func (m *IdentifiedConnection) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IdentifiedConnection) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IdentifiedConnection.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 *IdentifiedConnection) XXX_Merge(src proto.Message) { + xxx_messageInfo_IdentifiedConnection.Merge(m, src) +} +func (m *IdentifiedConnection) XXX_Size() int { + return m.Size() +} +func (m *IdentifiedConnection) XXX_DiscardUnknown() { + xxx_messageInfo_IdentifiedConnection.DiscardUnknown(m) +} + +var xxx_messageInfo_IdentifiedConnection proto.InternalMessageInfo + +// Counterparty defines the counterparty chain associated with a connection end. +type Counterparty struct { + // identifies the client on the counterparty chain associated with a given + // connection. + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // identifies the connection end on the counterparty chain associated with a + // given connection. + ConnectionId string `protobuf:"bytes,2,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + // commitment merkle prefix of the counterparty chain. + Prefix types.MerklePrefix `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix"` +} + +func (m *Counterparty) Reset() { *m = Counterparty{} } +func (m *Counterparty) String() string { return proto.CompactTextString(m) } +func (*Counterparty) ProtoMessage() {} +func (*Counterparty) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{2} +} +func (m *Counterparty) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Counterparty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Counterparty.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 *Counterparty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Counterparty.Merge(m, src) +} +func (m *Counterparty) XXX_Size() int { + return m.Size() +} +func (m *Counterparty) XXX_DiscardUnknown() { + xxx_messageInfo_Counterparty.DiscardUnknown(m) +} + +var xxx_messageInfo_Counterparty proto.InternalMessageInfo + +// ClientPaths define all the connection paths for a client state. +type ClientPaths struct { + // list of connection paths + Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` +} + +func (m *ClientPaths) Reset() { *m = ClientPaths{} } +func (m *ClientPaths) String() string { return proto.CompactTextString(m) } +func (*ClientPaths) ProtoMessage() {} +func (*ClientPaths) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{3} +} +func (m *ClientPaths) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientPaths) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientPaths.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 *ClientPaths) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientPaths.Merge(m, src) +} +func (m *ClientPaths) XXX_Size() int { + return m.Size() +} +func (m *ClientPaths) XXX_DiscardUnknown() { + xxx_messageInfo_ClientPaths.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientPaths proto.InternalMessageInfo + +func (m *ClientPaths) GetPaths() []string { + if m != nil { + return m.Paths + } + return nil +} + +// ConnectionPaths define all the connection paths for a given client state. +type ConnectionPaths struct { + // client state unique identifier + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // list of connection paths + Paths []string `protobuf:"bytes,2,rep,name=paths,proto3" json:"paths,omitempty"` +} + +func (m *ConnectionPaths) Reset() { *m = ConnectionPaths{} } +func (m *ConnectionPaths) String() string { return proto.CompactTextString(m) } +func (*ConnectionPaths) ProtoMessage() {} +func (*ConnectionPaths) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{4} +} +func (m *ConnectionPaths) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConnectionPaths) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConnectionPaths.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 *ConnectionPaths) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectionPaths.Merge(m, src) +} +func (m *ConnectionPaths) XXX_Size() int { + return m.Size() +} +func (m *ConnectionPaths) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectionPaths.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectionPaths proto.InternalMessageInfo + +func (m *ConnectionPaths) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *ConnectionPaths) GetPaths() []string { + if m != nil { + return m.Paths + } + return nil +} + +// Version defines the versioning scheme used to negotiate the IBC verison in +// the connection handshake. +type Version struct { + // unique version identifier + Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` + // list of features compatible with the specified identifier + Features []string `protobuf:"bytes,2,rep,name=features,proto3" json:"features,omitempty"` +} + +func (m *Version) Reset() { *m = Version{} } +func (m *Version) String() string { return proto.CompactTextString(m) } +func (*Version) ProtoMessage() {} +func (*Version) Descriptor() ([]byte, []int) { + return fileDescriptor_90572467c054e43a, []int{5} +} +func (m *Version) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Version.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 *Version) XXX_Merge(src proto.Message) { + xxx_messageInfo_Version.Merge(m, src) +} +func (m *Version) XXX_Size() int { + return m.Size() +} +func (m *Version) XXX_DiscardUnknown() { + xxx_messageInfo_Version.DiscardUnknown(m) +} + +var xxx_messageInfo_Version proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("ibc.core.connection.v1.State", State_name, State_value) + proto.RegisterType((*ConnectionEnd)(nil), "ibc.core.connection.v1.ConnectionEnd") + proto.RegisterType((*IdentifiedConnection)(nil), "ibc.core.connection.v1.IdentifiedConnection") + proto.RegisterType((*Counterparty)(nil), "ibc.core.connection.v1.Counterparty") + proto.RegisterType((*ClientPaths)(nil), "ibc.core.connection.v1.ClientPaths") + proto.RegisterType((*ConnectionPaths)(nil), "ibc.core.connection.v1.ConnectionPaths") + proto.RegisterType((*Version)(nil), "ibc.core.connection.v1.Version") +} + +func init() { + proto.RegisterFile("ibc/core/connection/v1/connection.proto", fileDescriptor_90572467c054e43a) +} + +var fileDescriptor_90572467c054e43a = []byte{ + // 654 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6b, 0xdb, 0x4c, + 0x14, 0x94, 0x64, 0x39, 0xb1, 0xd7, 0xf1, 0xf7, 0xb9, 0x5b, 0xd3, 0x0a, 0x41, 0x24, 0xa1, 0x16, + 0x6a, 0x0a, 0xb1, 0xea, 0x04, 0x7a, 0x48, 0xe8, 0x21, 0x76, 0x5c, 0x10, 0x6d, 0x5d, 0xa3, 0x38, + 0x85, 0xe6, 0x62, 0x6c, 0x69, 0x93, 0x2c, 0xb1, 0xb5, 0x42, 0xda, 0x98, 0xf8, 0x1f, 0x84, 0x9c, + 0x7a, 0xed, 0x21, 0x50, 0xe8, 0x7f, 0x29, 0xa1, 0xa7, 0x1c, 0x7b, 0x32, 0x25, 0xb9, 0xf6, 0xe4, + 0x5f, 0x50, 0xa4, 0x95, 0x65, 0x25, 0x34, 0x87, 0xa4, 0x3d, 0xf9, 0xcd, 0xbe, 0x99, 0xf1, 0xbe, + 0xf1, 0xf3, 0x82, 0x67, 0xb8, 0x6f, 0x1b, 0x36, 0xf1, 0x91, 0x61, 0x13, 0xd7, 0x45, 0x36, 0xc5, + 0xc4, 0x35, 0x46, 0xb5, 0x14, 0xaa, 0x7a, 0x3e, 0xa1, 0x04, 0x3e, 0xc2, 0x7d, 0xbb, 0x1a, 0x12, + 0xab, 0xa9, 0xd6, 0xa8, 0x26, 0x97, 0xf7, 0xc9, 0x3e, 0x89, 0x28, 0x46, 0x58, 0x31, 0xb6, 0x9c, + 0xb6, 0x1d, 0x0e, 0x31, 0x1d, 0x22, 0x97, 0x32, 0xdb, 0x19, 0x62, 0x44, 0xfd, 0x9b, 0x00, 0x8a, + 0x8d, 0xc4, 0xb0, 0xe9, 0x3a, 0xb0, 0x06, 0xf2, 0xf6, 0x00, 0x23, 0x97, 0x76, 0xb1, 0x23, 0xf1, + 0x1a, 0x5f, 0xc9, 0xd7, 0xcb, 0xd3, 0x89, 0x5a, 0x1a, 0xf7, 0x86, 0x83, 0x75, 0x3d, 0x69, 0xe9, + 0x56, 0x8e, 0xd5, 0xa6, 0x03, 0x37, 0x40, 0x6e, 0x84, 0xfc, 0x00, 0x13, 0x37, 0x90, 0x04, 0x2d, + 0x53, 0x29, 0xac, 0xaa, 0xd5, 0x3f, 0x5f, 0xb7, 0xfa, 0x81, 0xf1, 0xac, 0x44, 0x00, 0xd7, 0x40, + 0x36, 0xa0, 0x3d, 0x8a, 0xa4, 0x8c, 0xc6, 0x57, 0xfe, 0x5b, 0x5d, 0xbe, 0x4d, 0xb9, 0x1d, 0x92, + 0x2c, 0xc6, 0x85, 0x2d, 0xb0, 0x64, 0x93, 0x23, 0x97, 0x22, 0xdf, 0xeb, 0xf9, 0x74, 0x2c, 0x89, + 0x1a, 0x5f, 0x29, 0xac, 0x3e, 0xbd, 0x4d, 0xdb, 0x48, 0x71, 0xeb, 0xe2, 0xf9, 0x44, 0xe5, 0xac, + 0x6b, 0x7a, 0xb8, 0x0e, 0x96, 0x1c, 0x34, 0xe8, 0x8d, 0xbb, 0x1e, 0xf2, 0x31, 0x71, 0xa4, 0xac, + 0xc6, 0x57, 0xc4, 0xfa, 0xe3, 0xe9, 0x44, 0x7d, 0xc8, 0xe6, 0x4e, 0x77, 0x75, 0xab, 0x10, 0xc1, + 0x76, 0x84, 0xd6, 0xc5, 0x93, 0x2f, 0x2a, 0xa7, 0xff, 0x12, 0x40, 0xd9, 0x74, 0x90, 0x4b, 0xf1, + 0x1e, 0x46, 0xce, 0x3c, 0x52, 0xb8, 0x0c, 0x84, 0x24, 0xc8, 0xe2, 0x74, 0xa2, 0xe6, 0x99, 0x61, + 0x98, 0xa0, 0x80, 0x6f, 0xc4, 0x2d, 0xdc, 0x39, 0xee, 0xcc, 0xbd, 0xe3, 0x16, 0xff, 0x22, 0xee, + 0xec, 0x3f, 0x8e, 0x7b, 0xe1, 0xce, 0x71, 0x7f, 0xe7, 0xc1, 0x52, 0xfa, 0x6b, 0xee, 0xb3, 0xb6, + 0xaf, 0x40, 0x71, 0x7e, 0xef, 0x79, 0xfc, 0xd2, 0x74, 0xa2, 0x96, 0x63, 0x59, 0xba, 0xad, 0x87, + 0x43, 0xcc, 0xb0, 0xe9, 0xc0, 0x3a, 0x58, 0xf0, 0x7c, 0xb4, 0x87, 0x8f, 0xa3, 0xcd, 0xbd, 0x11, + 0x47, 0xf2, 0x37, 0x1b, 0xd5, 0xaa, 0xef, 0x90, 0x7f, 0x38, 0x40, 0xed, 0x88, 0x1b, 0xc7, 0x11, + 0x2b, 0xe3, 0x61, 0x9e, 0x80, 0x42, 0x23, 0xba, 0x54, 0xbb, 0x47, 0x0f, 0x02, 0x58, 0x06, 0x59, + 0x2f, 0x2c, 0x24, 0x5e, 0xcb, 0x54, 0xf2, 0x16, 0x03, 0xfa, 0x2e, 0xf8, 0x7f, 0xbe, 0x55, 0x8c, + 0x78, 0x8f, 0x99, 0x13, 0x6f, 0x21, 0xed, 0xfd, 0x06, 0x2c, 0xc6, 0x9b, 0x02, 0x15, 0x00, 0xf0, + 0x6c, 0x8d, 0x7d, 0x66, 0x6a, 0xa5, 0x4e, 0xa0, 0x0c, 0x72, 0x7b, 0xa8, 0x47, 0x8f, 0x7c, 0x34, + 0xf3, 0x48, 0x30, 0x9b, 0xe6, 0xf9, 0x67, 0x1e, 0x64, 0xa3, 0xed, 0x81, 0x2f, 0x81, 0xba, 0xdd, + 0xd9, 0xec, 0x34, 0xbb, 0x3b, 0x2d, 0xb3, 0x65, 0x76, 0xcc, 0xcd, 0xb7, 0xe6, 0x6e, 0x73, 0xab, + 0xbb, 0xd3, 0xda, 0x6e, 0x37, 0x1b, 0xe6, 0x6b, 0xb3, 0xb9, 0x55, 0xe2, 0xe4, 0x07, 0xa7, 0x67, + 0x5a, 0xf1, 0x1a, 0x01, 0x4a, 0x00, 0x30, 0x5d, 0x78, 0x58, 0xe2, 0xe5, 0xdc, 0xe9, 0x99, 0x26, + 0x86, 0x35, 0x54, 0x40, 0x91, 0x75, 0x3a, 0xd6, 0xc7, 0xf7, 0xed, 0x66, 0xab, 0x24, 0xc8, 0x85, + 0xd3, 0x33, 0x6d, 0x31, 0x86, 0x73, 0x65, 0xd4, 0xcc, 0x30, 0x65, 0x58, 0xcb, 0xe2, 0xc9, 0x57, + 0x85, 0xab, 0xef, 0x9c, 0x5f, 0x2a, 0xfc, 0xc5, 0xa5, 0xc2, 0xff, 0xbc, 0x54, 0xf8, 0x4f, 0x57, + 0x0a, 0x77, 0x71, 0xa5, 0x70, 0x3f, 0xae, 0x14, 0x6e, 0x77, 0x63, 0x1f, 0xd3, 0x83, 0xa3, 0x7e, + 0xf8, 0xd3, 0x19, 0x36, 0x09, 0x86, 0x24, 0x88, 0x3f, 0x56, 0x02, 0xe7, 0xd0, 0x38, 0x36, 0x92, + 0x07, 0xf5, 0xc5, 0xda, 0x4a, 0xea, 0xa9, 0xa6, 0x63, 0x0f, 0x05, 0xfd, 0x85, 0xe8, 0x31, 0x5d, + 0xfb, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x48, 0x0f, 0xf2, 0xaa, 0xce, 0x05, 0x00, 0x00, +} + +func (m *ConnectionEnd) 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 *ConnectionEnd) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConnectionEnd) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DelayPeriod != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.DelayPeriod)) + i-- + dAtA[i] = 0x28 + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.State != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x18 + } + if len(m.Versions) > 0 { + for iNdEx := len(m.Versions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Versions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IdentifiedConnection) 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 *IdentifiedConnection) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IdentifiedConnection) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DelayPeriod != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.DelayPeriod)) + i-- + dAtA[i] = 0x30 + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.State != 0 { + i = encodeVarintConnection(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x20 + } + if len(m.Versions) > 0 { + for iNdEx := len(m.Versions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Versions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintConnection(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Counterparty) 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 *Counterparty) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Counterparty) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Prefix.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintConnection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ClientPaths) 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 *ClientPaths) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientPaths) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Paths) > 0 { + for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Paths[iNdEx]) + copy(dAtA[i:], m.Paths[iNdEx]) + i = encodeVarintConnection(dAtA, i, uint64(len(m.Paths[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ConnectionPaths) 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 *ConnectionPaths) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConnectionPaths) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Paths) > 0 { + for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Paths[iNdEx]) + copy(dAtA[i:], m.Paths[iNdEx]) + i = encodeVarintConnection(dAtA, i, uint64(len(m.Paths[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintConnection(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Version) 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 *Version) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Version) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Features) > 0 { + for iNdEx := len(m.Features) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Features[iNdEx]) + copy(dAtA[i:], m.Features[iNdEx]) + i = encodeVarintConnection(dAtA, i, uint64(len(m.Features[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Identifier) > 0 { + i -= len(m.Identifier) + copy(dAtA[i:], m.Identifier) + i = encodeVarintConnection(dAtA, i, uint64(len(m.Identifier))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintConnection(dAtA []byte, offset int, v uint64) int { + offset -= sovConnection(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ConnectionEnd) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + if len(m.Versions) > 0 { + for _, e := range m.Versions { + l = e.Size() + n += 1 + l + sovConnection(uint64(l)) + } + } + if m.State != 0 { + n += 1 + sovConnection(uint64(m.State)) + } + l = m.Counterparty.Size() + n += 1 + l + sovConnection(uint64(l)) + if m.DelayPeriod != 0 { + n += 1 + sovConnection(uint64(m.DelayPeriod)) + } + return n +} + +func (m *IdentifiedConnection) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + if len(m.Versions) > 0 { + for _, e := range m.Versions { + l = e.Size() + n += 1 + l + sovConnection(uint64(l)) + } + } + if m.State != 0 { + n += 1 + sovConnection(uint64(m.State)) + } + l = m.Counterparty.Size() + n += 1 + l + sovConnection(uint64(l)) + if m.DelayPeriod != 0 { + n += 1 + sovConnection(uint64(m.DelayPeriod)) + } + return n +} + +func (m *Counterparty) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + l = m.Prefix.Size() + n += 1 + l + sovConnection(uint64(l)) + return n +} + +func (m *ClientPaths) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Paths) > 0 { + for _, s := range m.Paths { + l = len(s) + n += 1 + l + sovConnection(uint64(l)) + } + } + return n +} + +func (m *ConnectionPaths) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + if len(m.Paths) > 0 { + for _, s := range m.Paths { + l = len(s) + n += 1 + l + sovConnection(uint64(l)) + } + } + return n +} + +func (m *Version) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Identifier) + if l > 0 { + n += 1 + l + sovConnection(uint64(l)) + } + if len(m.Features) > 0 { + for _, s := range m.Features { + l = len(s) + n += 1 + l + sovConnection(uint64(l)) + } + } + return n +} + +func sovConnection(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozConnection(x uint64) (n int) { + return sovConnection(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ConnectionEnd) 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 ErrIntOverflowConnection + } + 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: ConnectionEnd: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectionEnd: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Versions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Versions = append(m.Versions, &Version{}) + if err := m.Versions[len(m.Versions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + m.State = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.State |= State(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DelayPeriod", wireType) + } + m.DelayPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DelayPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IdentifiedConnection) 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 ErrIntOverflowConnection + } + 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: IdentifiedConnection: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IdentifiedConnection: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Versions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Versions = append(m.Versions, &Version{}) + if err := m.Versions[len(m.Versions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + m.State = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.State |= State(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DelayPeriod", wireType) + } + m.DelayPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DelayPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Counterparty) 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 ErrIntOverflowConnection + } + 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: Counterparty: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Counterparty: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Prefix", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthConnection + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Prefix.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientPaths) 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 ErrIntOverflowConnection + } + 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: ClientPaths: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientPaths: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectionPaths) 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 ErrIntOverflowConnection + } + 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: ConnectionPaths: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectionPaths: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Version) 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 ErrIntOverflowConnection + } + 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: Version: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Version: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Features", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowConnection + } + 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 ErrInvalidLengthConnection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthConnection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Features = append(m.Features, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipConnection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthConnection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipConnection(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, ErrIntOverflowConnection + } + 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, ErrIntOverflowConnection + } + 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, ErrIntOverflowConnection + } + 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, ErrInvalidLengthConnection + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupConnection + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthConnection + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthConnection = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowConnection = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupConnection = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/core/03-connection/types/connection_test.go b/x/ibc/core/03-connection/types/connection_test.go new file mode 100644 index 000000000000..e7e91538c4e3 --- /dev/null +++ b/x/ibc/core/03-connection/types/connection_test.go @@ -0,0 +1,121 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +var ( + chainID = "gaiamainnet" + connectionID = "connection-0" + clientID = "clientidone" + connectionID2 = "connectionidtwo" + clientID2 = "clientidtwo" + invalidConnectionID = "(invalidConnectionID)" + clientHeight = clienttypes.NewHeight(0, 6) +) + +func TestConnectionValidateBasic(t *testing.T) { + testCases := []struct { + name string + connection types.ConnectionEnd + expPass bool + }{ + { + "valid connection", + types.ConnectionEnd{clientID, []*types.Version{ibctesting.ConnectionVersion}, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}, + true, + }, + { + "invalid client id", + types.ConnectionEnd{"(clientID1)", []*types.Version{ibctesting.ConnectionVersion}, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}, + false, + }, + { + "empty versions", + types.ConnectionEnd{clientID, nil, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}, + false, + }, + { + "invalid version", + types.ConnectionEnd{clientID, []*types.Version{{}}, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}, + false, + }, + { + "invalid counterparty", + types.ConnectionEnd{clientID, []*types.Version{ibctesting.ConnectionVersion}, types.INIT, types.Counterparty{clientID2, connectionID2, emptyPrefix}, 500}, + false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.connection.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestCounterpartyValidateBasic(t *testing.T) { + testCases := []struct { + name string + counterparty types.Counterparty + expPass bool + }{ + {"valid counterparty", types.Counterparty{clientID, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, true}, + {"invalid client id", types.Counterparty{"(InvalidClient)", connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, false}, + {"invalid connection id", types.Counterparty{clientID, "(InvalidConnection)", commitmenttypes.NewMerklePrefix([]byte("prefix"))}, false}, + {"invalid prefix", types.Counterparty{clientID, connectionID2, emptyPrefix}, false}, + } + + for i, tc := range testCases { + tc := tc + + err := tc.counterparty.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestIdentifiedConnectionValidateBasic(t *testing.T) { + testCases := []struct { + name string + connection types.IdentifiedConnection + expPass bool + }{ + { + "valid connection", + types.NewIdentifiedConnection(clientID, types.ConnectionEnd{clientID, []*types.Version{ibctesting.ConnectionVersion}, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}), + true, + }, + { + "invalid connection id", + types.NewIdentifiedConnection("(connectionIDONE)", types.ConnectionEnd{clientID, []*types.Version{ibctesting.ConnectionVersion}, types.INIT, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, 500}), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.connection.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/core/03-connection/types/errors.go b/x/ibc/core/03-connection/types/errors.go new file mode 100644 index 000000000000..107a0e087c17 --- /dev/null +++ b/x/ibc/core/03-connection/types/errors.go @@ -0,0 +1,19 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBC connection sentinel errors +var ( + ErrConnectionExists = sdkerrors.Register(SubModuleName, 2, "connection already exists") + ErrConnectionNotFound = sdkerrors.Register(SubModuleName, 3, "connection not found") + ErrClientConnectionPathsNotFound = sdkerrors.Register(SubModuleName, 4, "light client connection paths not found") + ErrConnectionPath = sdkerrors.Register(SubModuleName, 5, "connection path is not associated to the given light client") + ErrInvalidConnectionState = sdkerrors.Register(SubModuleName, 6, "invalid connection state") + ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 7, "invalid counterparty connection") + ErrInvalidConnection = sdkerrors.Register(SubModuleName, 8, "invalid connection") + ErrInvalidVersion = sdkerrors.Register(SubModuleName, 9, "invalid connection version") + ErrVersionNegotiationFailed = sdkerrors.Register(SubModuleName, 10, "connection version negotiation failed") + ErrInvalidConnectionIdentifier = sdkerrors.Register(SubModuleName, 11, "invalid connection identifier") +) diff --git a/x/ibc/core/03-connection/types/events.go b/x/ibc/core/03-connection/types/events.go new file mode 100644 index 000000000000..3cb5997bd105 --- /dev/null +++ b/x/ibc/core/03-connection/types/events.go @@ -0,0 +1,25 @@ +package types + +import ( + "fmt" + + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// IBC connection events +const ( + AttributeKeyConnectionID = "connection_id" + AttributeKeyClientID = "client_id" + AttributeKeyCounterpartyClientID = "counterparty_client_id" + AttributeKeyCounterpartyConnectionID = "counterparty_connection_id" +) + +// IBC connection events vars +var ( + EventTypeConnectionOpenInit = MsgConnectionOpenInit{}.Type() + EventTypeConnectionOpenTry = MsgConnectionOpenTry{}.Type() + EventTypeConnectionOpenAck = MsgConnectionOpenAck{}.Type() + EventTypeConnectionOpenConfirm = MsgConnectionOpenConfirm{}.Type() + + AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) +) diff --git a/x/ibc/core/03-connection/types/expected_keepers.go b/x/ibc/core/03-connection/types/expected_keepers.go new file mode 100644 index 000000000000..9fc995867188 --- /dev/null +++ b/x/ibc/core/03-connection/types/expected_keepers.go @@ -0,0 +1,16 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// ClientKeeper expected account IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) + GetSelfConsensusState(ctx sdk.Context, height exported.Height) (exported.ConsensusState, bool) + ValidateSelfClient(ctx sdk.Context, clientState exported.ClientState) error + IterateClients(ctx sdk.Context, cb func(string, exported.ClientState) bool) + ClientStore(ctx sdk.Context, clientID string) sdk.KVStore +} diff --git a/x/ibc/core/03-connection/types/genesis.go b/x/ibc/core/03-connection/types/genesis.go new file mode 100644 index 000000000000..b10c300a84a5 --- /dev/null +++ b/x/ibc/core/03-connection/types/genesis.go @@ -0,0 +1,76 @@ +package types + +import ( + "fmt" + + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// NewConnectionPaths creates a ConnectionPaths instance. +func NewConnectionPaths(id string, paths []string) ConnectionPaths { + return ConnectionPaths{ + ClientId: id, + Paths: paths, + } +} + +// NewGenesisState creates a GenesisState instance. +func NewGenesisState( + connections []IdentifiedConnection, connPaths []ConnectionPaths, + nextConnectionSequence uint64, +) GenesisState { + return GenesisState{ + Connections: connections, + ClientConnectionPaths: connPaths, + NextConnectionSequence: nextConnectionSequence, + } +} + +// DefaultGenesisState returns the ibc connection submodule's default genesis state. +func DefaultGenesisState() GenesisState { + return GenesisState{ + Connections: []IdentifiedConnection{}, + ClientConnectionPaths: []ConnectionPaths{}, + NextConnectionSequence: 0, + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + // keep track of the max sequence to ensure it is less than + // the next sequence used in creating connection identifers. + var maxSequence uint64 = 0 + + for i, conn := range gs.Connections { + sequence, err := ParseConnectionSequence(conn.Id) + if err != nil { + return err + } + + if sequence > maxSequence { + maxSequence = sequence + } + + if err := conn.ValidateBasic(); err != nil { + return fmt.Errorf("invalid connection %v index %d: %w", conn, i, err) + } + } + + for i, conPaths := range gs.ClientConnectionPaths { + if err := host.ClientIdentifierValidator(conPaths.ClientId); err != nil { + return fmt.Errorf("invalid client connection path %d: %w", i, err) + } + for _, connectionID := range conPaths.Paths { + if err := host.ConnectionIdentifierValidator(connectionID); err != nil { + return fmt.Errorf("invalid client connection ID (%s) in connection paths %d: %w", connectionID, i, err) + } + } + } + + if maxSequence != 0 && maxSequence >= gs.NextConnectionSequence { + return fmt.Errorf("next connection sequence %d must be greater than maximum sequence used in connection identifier %d", gs.NextConnectionSequence, maxSequence) + } + + return nil +} diff --git a/x/ibc/core/03-connection/types/genesis.pb.go b/x/ibc/core/03-connection/types/genesis.pb.go new file mode 100644 index 000000000000..5f33f166ae07 --- /dev/null +++ b/x/ibc/core/03-connection/types/genesis.pb.go @@ -0,0 +1,438 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/connection/v1/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 defines the ibc connection submodule's genesis state. +type GenesisState struct { + Connections []IdentifiedConnection `protobuf:"bytes,1,rep,name=connections,proto3" json:"connections"` + ClientConnectionPaths []ConnectionPaths `protobuf:"bytes,2,rep,name=client_connection_paths,json=clientConnectionPaths,proto3" json:"client_connection_paths" yaml:"client_connection_paths"` + // the sequence for the next generated connection identifier + NextConnectionSequence uint64 `protobuf:"varint,3,opt,name=next_connection_sequence,json=nextConnectionSequence,proto3" json:"next_connection_sequence,omitempty" yaml:"next_connection_sequence"` +} + +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_1879d34bc6ac3cd7, []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) GetConnections() []IdentifiedConnection { + if m != nil { + return m.Connections + } + return nil +} + +func (m *GenesisState) GetClientConnectionPaths() []ConnectionPaths { + if m != nil { + return m.ClientConnectionPaths + } + return nil +} + +func (m *GenesisState) GetNextConnectionSequence() uint64 { + if m != nil { + return m.NextConnectionSequence + } + return 0 +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.core.connection.v1.GenesisState") +} + +func init() { + proto.RegisterFile("ibc/core/connection/v1/genesis.proto", fileDescriptor_1879d34bc6ac3cd7) +} + +var fileDescriptor_1879d34bc6ac3cd7 = []byte{ + // 326 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc9, 0x4c, 0x4a, 0xd6, + 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0xce, 0xcf, 0xcb, 0x4b, 0x4d, 0x2e, 0xc9, 0xcc, 0xcf, 0xd3, + 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, + 0x17, 0x12, 0xcb, 0x4c, 0x4a, 0xd6, 0x03, 0xa9, 0xd2, 0x43, 0xa8, 0xd2, 0x2b, 0x33, 0x94, 0x12, + 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x2b, 0xd1, 0x07, 0xb1, 0x20, 0xaa, 0xa5, 0xd4, 0x71, 0x98, 0x89, + 0xa4, 0x17, 0xac, 0x50, 0xe9, 0x2c, 0x13, 0x17, 0x8f, 0x3b, 0xc4, 0xa2, 0xe0, 0x92, 0xc4, 0x92, + 0x54, 0xa1, 0x10, 0x2e, 0x6e, 0x84, 0xa2, 0x62, 0x09, 0x46, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x1d, + 0x3d, 0xec, 0xb6, 0xeb, 0x79, 0xa6, 0xa4, 0xe6, 0x95, 0x64, 0xa6, 0x65, 0xa6, 0xa6, 0x38, 0xc3, + 0xc5, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x42, 0x36, 0x46, 0xa8, 0x9d, 0x91, 0x4b, 0x3c, + 0x39, 0x27, 0x33, 0x35, 0xaf, 0x24, 0x1e, 0x21, 0x1c, 0x5f, 0x90, 0x58, 0x92, 0x51, 0x2c, 0xc1, + 0x04, 0xb6, 0x42, 0x1d, 0x97, 0x15, 0x08, 0x83, 0x03, 0x40, 0xca, 0x9d, 0xd4, 0x40, 0xa6, 0x7f, + 0xba, 0x27, 0x2f, 0x57, 0x99, 0x98, 0x9b, 0x63, 0xa5, 0x84, 0xc3, 0x54, 0xa5, 0x20, 0x51, 0x88, + 0x0c, 0x9a, 0x76, 0xa1, 0x58, 0x2e, 0x89, 0xbc, 0xd4, 0x0a, 0x14, 0x0d, 0xc5, 0xa9, 0x85, 0xa5, + 0xa9, 0x79, 0xc9, 0xa9, 0x12, 0xcc, 0x0a, 0x8c, 0x1a, 0x2c, 0x4e, 0xca, 0x9f, 0xee, 0xc9, 0xcb, + 0x43, 0x0c, 0xc7, 0xa5, 0x52, 0x29, 0x48, 0x0c, 0x24, 0x85, 0x30, 0x3b, 0x18, 0x2a, 0xe1, 0x14, + 0x7a, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, + 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xd6, 0xe9, 0x99, 0x25, 0x19, + 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0x50, 0x4a, 0xb7, + 0x38, 0x25, 0x5b, 0xbf, 0x42, 0x1f, 0x1e, 0x63, 0x06, 0xc6, 0xba, 0x48, 0x91, 0x56, 0x52, 0x59, + 0x90, 0x5a, 0x9c, 0xc4, 0x06, 0x8e, 0x2d, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xad, + 0x14, 0x09, 0x2c, 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 m.NextConnectionSequence != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.NextConnectionSequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ClientConnectionPaths) > 0 { + for iNdEx := len(m.ClientConnectionPaths) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ClientConnectionPaths[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Connections) > 0 { + for iNdEx := len(m.Connections) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Connections[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.Connections) > 0 { + for _, e := range m.Connections { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.ClientConnectionPaths) > 0 { + for _, e := range m.ClientConnectionPaths { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.NextConnectionSequence != 0 { + n += 1 + sovGenesis(uint64(m.NextConnectionSequence)) + } + 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 Connections", 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.Connections = append(m.Connections, IdentifiedConnection{}) + if err := m.Connections[len(m.Connections)-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 ClientConnectionPaths", 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.ClientConnectionPaths = append(m.ClientConnectionPaths, ConnectionPaths{}) + if err := m.ClientConnectionPaths[len(m.ClientConnectionPaths)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextConnectionSequence", wireType) + } + m.NextConnectionSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextConnectionSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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/ibc/core/03-connection/types/genesis_test.go b/x/ibc/core/03-connection/types/genesis_test.go new file mode 100644 index 000000000000..846837f9af70 --- /dev/null +++ b/x/ibc/core/03-connection/types/genesis_test.go @@ -0,0 +1,114 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func TestValidateGenesis(t *testing.T) { + + testCases := []struct { + name string + genState types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + name: "valid genesis", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection(connectionID, types.NewConnectionEnd(types.INIT, clientID, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {clientID, []string{connectionID}}, + }, + 0, + ), + expPass: true, + }, + { + name: "invalid connection", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection(connectionID, types.NewConnectionEnd(types.INIT, "(CLIENTIDONE)", types.Counterparty{clientID, connectionID, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {clientID, []string{connectionID}}, + }, + 0, + ), + expPass: false, + }, + { + name: "invalid client id", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection(connectionID, types.NewConnectionEnd(types.INIT, clientID, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {"(CLIENTIDONE)", []string{connectionID}}, + }, + 0, + ), + expPass: false, + }, + { + name: "invalid path", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection(connectionID, types.NewConnectionEnd(types.INIT, clientID, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {clientID, []string{invalidConnectionID}}, + }, + 0, + ), + expPass: false, + }, + { + name: "invalid connection identifier", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection("conn-0", types.NewConnectionEnd(types.INIT, clientID, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {clientID, []string{connectionID}}, + }, + 0, + ), + expPass: false, + }, + { + name: "next connection sequence is not greater than maximum connection identifier sequence provided", + genState: types.NewGenesisState( + []types.IdentifiedConnection{ + types.NewIdentifiedConnection(types.FormatConnectionIdentifier(10), types.NewConnectionEnd(types.INIT, clientID, types.Counterparty{clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))}, []*types.Version{ibctesting.ConnectionVersion}, 500)), + }, + []types.ConnectionPaths{ + {clientID, []string{connectionID}}, + }, + 0, + ), + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/core/03-connection/types/keys.go b/x/ibc/core/03-connection/types/keys.go new file mode 100644 index 000000000000..65af565c2abb --- /dev/null +++ b/x/ibc/core/03-connection/types/keys.go @@ -0,0 +1,61 @@ +package types + +import ( + "fmt" + "regexp" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + // SubModuleName defines the IBC connection name + SubModuleName = "connection" + + // StoreKey is the store key string for IBC connections + StoreKey = SubModuleName + + // RouterKey is the message route for IBC connections + RouterKey = SubModuleName + + // QuerierRoute is the querier route for IBC connections + QuerierRoute = SubModuleName + + // KeyNextConnectionSequence is the key used to store the next connection sequence in + // the keeper. + KeyNextConnectionSequence = "nextConnectionSequence" + + // ConnectionPrefix is the prefix used when creating a connection identifier + ConnectionPrefix = "connection-" +) + +// FormatConnectionIdentifier returns the connection identifier with the sequence appended. +// This is a SDK specific format not enforced by IBC protocol. +func FormatConnectionIdentifier(sequence uint64) string { + return fmt.Sprintf("%s%d", ConnectionPrefix, sequence) +} + +// IsConnectionIDFormat checks if a connectionID is in the format required on the SDK for +// parsing connection identifiers. The connection identifier must be in the form: `connection-{N} +var IsConnectionIDFormat = regexp.MustCompile(`^connection-[0-9]{1,20}$`).MatchString + +// IsValidConnectionID checks if the connection identifier is valid and can be parsed to +// the connection identifier format. +func IsValidConnectionID(connectionID string) bool { + _, err := ParseConnectionSequence(connectionID) + return err == nil +} + +// ParseConnectionSequence parses the connection sequence from the connection identifier. +func ParseConnectionSequence(connectionID string) (uint64, error) { + if !IsConnectionIDFormat(connectionID) { + return 0, sdkerrors.Wrap(host.ErrInvalidID, "connection identifier is not in the format: `connection-{N}`") + } + + sequence, err := host.ParseIdentifier(connectionID, ConnectionPrefix) + if err != nil { + return 0, sdkerrors.Wrap(err, "invalid connection identifier") + } + + return sequence, nil +} diff --git a/x/ibc/core/03-connection/types/keys_test.go b/x/ibc/core/03-connection/types/keys_test.go new file mode 100644 index 000000000000..6adb8090f896 --- /dev/null +++ b/x/ibc/core/03-connection/types/keys_test.go @@ -0,0 +1,49 @@ +package types_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" +) + +// tests ParseConnectionSequence and IsValidConnectionID +func TestParseConnectionSequence(t *testing.T) { + testCases := []struct { + name string + connectionID string + expSeq uint64 + expPass bool + }{ + {"valid 0", "connection-0", 0, true}, + {"valid 1", "connection-1", 1, true}, + {"valid large sequence", types.FormatConnectionIdentifier(math.MaxUint64), math.MaxUint64, true}, + // one above uint64 max + {"invalid uint64", "connection-18446744073709551616", 0, false}, + // uint64 == 20 characters + {"invalid large sequence", "connection-2345682193567182931243", 0, false}, + {"capital prefix", "Connection-0", 0, false}, + {"double prefix", "connection-connection-0", 0, false}, + {"missing dash", "connection0", 0, false}, + {"blank id", " ", 0, false}, + {"empty id", "", 0, false}, + {"negative sequence", "connection--1", 0, false}, + } + + for _, tc := range testCases { + + seq, err := types.ParseConnectionSequence(tc.connectionID) + valid := types.IsValidConnectionID(tc.connectionID) + require.Equal(t, tc.expSeq, seq) + + if tc.expPass { + require.NoError(t, err, tc.name) + require.True(t, valid) + } else { + require.Error(t, err, tc.name) + require.False(t, valid) + } + } +} diff --git a/x/ibc/core/03-connection/types/msgs.go b/x/ibc/core/03-connection/types/msgs.go new file mode 100644 index 000000000000..3ba1aed8e7e0 --- /dev/null +++ b/x/ibc/core/03-connection/types/msgs.go @@ -0,0 +1,354 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +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. +//nolint:interfacer +func NewMsgConnectionOpenInit( + clientID, counterpartyClientID string, + counterpartyPrefix commitmenttypes.MerklePrefix, + version *Version, delayPeriod uint64, signer sdk.AccAddress, +) *MsgConnectionOpenInit { + // counterparty must have the same delay period + counterparty := NewCounterparty(counterpartyClientID, "", counterpartyPrefix) + return &MsgConnectionOpenInit{ + ClientId: clientID, + Counterparty: counterparty, + Version: version, + DelayPeriod: delayPeriod, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgConnectionOpenInit) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgConnectionOpenInit) Type() string { + return "connection_open_init" +} + +// ValidateBasic implements sdk.Msg. +func (msg MsgConnectionOpenInit) ValidateBasic() error { + if err := host.ClientIdentifierValidator(msg.ClientId); err != nil { + return sdkerrors.Wrap(err, "invalid client ID") + } + if msg.Counterparty.ConnectionId != "" { + return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty connection identifier must be empty") + } + + // NOTE: Version can be nil on MsgConnectionOpenInit + if msg.Version != nil { + if err := ValidateVersion(msg.Version); err != nil { + return sdkerrors.Wrap(err, "basic validation of the provided version failed") + } + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Counterparty.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgConnectionOpenInit) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgConnectionOpenInit) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// NewMsgConnectionOpenTry creates a new MsgConnectionOpenTry instance +//nolint:interfacer +func NewMsgConnectionOpenTry( + previousConnectionID, clientID, counterpartyConnectionID, + counterpartyClientID string, counterpartyClient exported.ClientState, + counterpartyPrefix commitmenttypes.MerklePrefix, + counterpartyVersions []*Version, delayPeriod uint64, + proofInit, proofClient, proofConsensus []byte, + proofHeight, consensusHeight clienttypes.Height, signer sdk.AccAddress, +) *MsgConnectionOpenTry { + counterparty := NewCounterparty(counterpartyClientID, counterpartyConnectionID, counterpartyPrefix) + csAny, _ := clienttypes.PackClientState(counterpartyClient) + return &MsgConnectionOpenTry{ + PreviousConnectionId: previousConnectionID, + ClientId: clientID, + ClientState: csAny, + Counterparty: counterparty, + CounterpartyVersions: counterpartyVersions, + DelayPeriod: delayPeriod, + ProofInit: proofInit, + ProofClient: proofClient, + ProofConsensus: proofConsensus, + ProofHeight: proofHeight, + ConsensusHeight: consensusHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgConnectionOpenTry) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgConnectionOpenTry) Type() string { + return "connection_open_try" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgConnectionOpenTry) ValidateBasic() error { + // an empty connection identifier indicates that a connection identifier should be generated + if msg.PreviousConnectionId != "" { + if !IsValidConnectionID(msg.PreviousConnectionId) { + return sdkerrors.Wrap(ErrInvalidConnectionIdentifier, "invalid previous connection ID") + } + } + if err := host.ClientIdentifierValidator(msg.ClientId); err != nil { + return sdkerrors.Wrap(err, "invalid client ID") + } + // counterparty validate basic allows empty counterparty connection identifiers + if err := host.ConnectionIdentifierValidator(msg.Counterparty.ConnectionId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty connection ID") + } + if msg.ClientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + } + clientState, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + } + if err := clientState.Validate(); err != nil { + return sdkerrors.Wrap(err, "counterparty client is invalid") + } + if len(msg.CounterpartyVersions) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidVersion, "empty counterparty versions") + } + for i, version := range msg.CounterpartyVersions { + if err := ValidateVersion(version); err != nil { + return sdkerrors.Wrapf(err, "basic validation failed on version with index %d", i) + } + } + if len(msg.ProofInit) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") + } + if len(msg.ProofClient) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + } + if len(msg.ProofConsensus) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + if msg.ConsensusHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "consensus height must be non-zero") + } + _, err = sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Counterparty.ValidateBasic() +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgConnectionOpenTry) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(msg.ClientState, new(exported.ClientState)) +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgConnectionOpenTry) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgConnectionOpenTry) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// NewMsgConnectionOpenAck creates a new MsgConnectionOpenAck instance +//nolint:interfacer +func NewMsgConnectionOpenAck( + connectionID, counterpartyConnectionID string, counterpartyClient exported.ClientState, + proofTry, proofClient, proofConsensus []byte, + proofHeight, consensusHeight clienttypes.Height, + version *Version, + signer sdk.AccAddress, +) *MsgConnectionOpenAck { + csAny, _ := clienttypes.PackClientState(counterpartyClient) + return &MsgConnectionOpenAck{ + ConnectionId: connectionID, + CounterpartyConnectionId: counterpartyConnectionID, + ClientState: csAny, + ProofTry: proofTry, + ProofClient: proofClient, + ProofConsensus: proofConsensus, + ProofHeight: proofHeight, + ConsensusHeight: consensusHeight, + Version: version, + Signer: signer.String(), + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgConnectionOpenAck) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(msg.ClientState, new(exported.ClientState)) +} + +// Route implements sdk.Msg +func (msg MsgConnectionOpenAck) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgConnectionOpenAck) Type() string { + return "connection_open_ack" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgConnectionOpenAck) ValidateBasic() error { + if !IsValidConnectionID(msg.ConnectionId) { + return ErrInvalidConnectionIdentifier + } + if err := host.ConnectionIdentifierValidator(msg.CounterpartyConnectionId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty connection ID") + } + if err := ValidateVersion(msg.Version); err != nil { + return err + } + if msg.ClientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "counterparty client is nil") + } + clientState, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "unpack err: %v", err) + } + if err := clientState.Validate(); err != nil { + return sdkerrors.Wrap(err, "counterparty client is invalid") + } + if len(msg.ProofTry) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") + } + if len(msg.ProofClient) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit empty proof client") + } + if len(msg.ProofConsensus) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of consensus state") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + if msg.ConsensusHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "consensus height must be non-zero") + } + _, err = sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgConnectionOpenAck) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgConnectionOpenAck) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} + +// NewMsgConnectionOpenConfirm creates a new MsgConnectionOpenConfirm instance +//nolint:interfacer +func NewMsgConnectionOpenConfirm( + connectionID string, proofAck []byte, proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgConnectionOpenConfirm { + return &MsgConnectionOpenConfirm{ + ConnectionId: connectionID, + ProofAck: proofAck, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgConnectionOpenConfirm) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgConnectionOpenConfirm) Type() string { + return "connection_open_confirm" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgConnectionOpenConfirm) ValidateBasic() error { + if !IsValidConnectionID(msg.ConnectionId) { + return ErrInvalidConnectionIdentifier + } + if len(msg.ProofAck) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgConnectionOpenConfirm) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgConnectionOpenConfirm) GetSigners() []sdk.AccAddress { + accAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{accAddr} +} diff --git a/x/ibc/core/03-connection/types/msgs_test.go b/x/ibc/core/03-connection/types/msgs_test.go new file mode 100644 index 000000000000..57c1925f66db --- /dev/null +++ b/x/ibc/core/03-connection/types/msgs_test.go @@ -0,0 +1,243 @@ +package types_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +var ( + emptyPrefix = commitmenttypes.MerklePrefix{} + emptyProof = []byte{} +) + +type MsgTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + proof []byte +} + +func (suite *MsgTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + + app := simapp.Setup(false) + db := dbm.NewMemDB() + store := rootmulti.NewStore(db) + storeKey := storetypes.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) + store.LoadVersion(0) + iavlStore := store.GetCommitStore(storeKey).(*iavl.Store) + + iavlStore.Set([]byte("KEY"), []byte("VALUE")) + _ = store.Commit() + + res := store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", storeKey.Name()), // required path to get key/value+proof + Data: []byte("KEY"), + Prove: true, + }) + + merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) + suite.Require().NoError(err) + proof, err := app.AppCodec().MarshalBinaryBare(&merkleProof) + suite.Require().NoError(err) + + suite.proof = proof + +} + +func TestMsgTestSuite(t *testing.T) { + suite.Run(t, new(MsgTestSuite)) +} + +func (suite *MsgTestSuite) TestNewMsgConnectionOpenInit() { + prefix := commitmenttypes.NewMerklePrefix([]byte("storePrefixKey")) + signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + // empty versions are considered valid, the default compatible versions + // will be used in protocol. + var version *types.Version + + var testCases = []struct { + name string + msg *types.MsgConnectionOpenInit + expPass bool + }{ + {"invalid client ID", types.NewMsgConnectionOpenInit("test/iris", "clienttotest", prefix, version, 500, signer), false}, + {"invalid counterparty client ID", types.NewMsgConnectionOpenInit("clienttotest", "(clienttotest)", prefix, version, 500, signer), false}, + {"invalid counterparty connection ID", &types.MsgConnectionOpenInit{connectionID, types.NewCounterparty("clienttotest", "connectiontotest", prefix), version, 500, signer.String()}, false}, + {"empty counterparty prefix", types.NewMsgConnectionOpenInit("clienttotest", "clienttotest", emptyPrefix, version, 500, signer), false}, + {"supplied version fails basic validation", types.NewMsgConnectionOpenInit("clienttotest", "clienttotest", prefix, &types.Version{}, 500, signer), false}, + {"empty singer", types.NewMsgConnectionOpenInit("clienttotest", "clienttotest", prefix, version, 500, nil), false}, + {"success", types.NewMsgConnectionOpenInit("clienttotest", "clienttotest", prefix, version, 500, signer), true}, + } + + for _, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() { + prefix := commitmenttypes.NewMerklePrefix([]byte("storePrefixKey")) + signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + + clientState := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + ) + + // Pack consensus state into any to test unpacking error + consState := ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), + ) + invalidAny := clienttypes.MustPackConsensusState(consState) + counterparty := types.NewCounterparty("connectiontotest", "clienttotest", prefix) + + // invalidClientState fails validateBasic + invalidClient := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + ) + + var testCases = []struct { + name string + msg *types.MsgConnectionOpenTry + expPass bool + }{ + {"invalid connection ID", types.NewMsgConnectionOpenTry("test/conn1", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid connection ID", types.NewMsgConnectionOpenTry("(invalidconnection)", "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid client ID", types.NewMsgConnectionOpenTry(connectionID, "test/iris", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"invalid counterparty connection ID", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "ibc/test", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + {"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}, + {"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}, + {"empty proofClient", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, signer), false}, + {"empty proofConsensus", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, signer), false}, + {"invalid proofHeight", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clienttypes.ZeroHeight(), clientHeight, signer), false}, + {"invalid consensusHeight", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clienttypes.ZeroHeight(), signer), false}, + {"empty singer", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, nil), false}, + {"success", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), true}, + {"invalid version", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{{}}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false}, + } + + for _, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() { + signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + clientState := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + ) + + // Pack consensus state into any to test unpacking error + consState := ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("root")), []byte("nextValsHash"), + ) + invalidAny := clienttypes.MustPackConsensusState(consState) + + // invalidClientState fails validateBasic + invalidClient := ibctmtypes.NewClientState( + chainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false, + ) + connectionID := "connection-0" + + var testCases = []struct { + name string + msg *types.MsgConnectionOpenAck + expPass bool + }{ + {"invalid connection ID", types.NewMsgConnectionOpenAck("test/conn1", connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false}, + {"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}, + {"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}, + {"invalid proofHeight", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clienttypes.ZeroHeight(), clientHeight, ibctesting.ConnectionVersion, signer), false}, + {"invalid consensusHeight", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clienttypes.ZeroHeight(), ibctesting.ConnectionVersion, signer), false}, + {"invalid version", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, &types.Version{}, signer), false}, + {"empty signer", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, nil), false}, + {"success", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), true}, + } + + for _, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *MsgTestSuite) TestNewMsgConnectionOpenConfirm() { + signer, _ := sdk.AccAddressFromBech32("cosmos1ckgw5d7jfj7wwxjzs9fdrdev9vc8dzcw3n2lht") + + testMsgs := []*types.MsgConnectionOpenConfirm{ + types.NewMsgConnectionOpenConfirm("test/conn1", suite.proof, clientHeight, signer), + types.NewMsgConnectionOpenConfirm(connectionID, emptyProof, clientHeight, signer), + types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clienttypes.ZeroHeight(), signer), + types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clientHeight, nil), + types.NewMsgConnectionOpenConfirm(connectionID, suite.proof, clientHeight, signer), + } + + var testCases = []struct { + msg *types.MsgConnectionOpenConfirm + expPass bool + errMsg string + }{ + {testMsgs[0], false, "invalid connection ID"}, + {testMsgs[1], false, "empty proofTry"}, + {testMsgs[2], false, "invalid proofHeight"}, + {testMsgs[3], false, "empty signer"}, + {testMsgs[4], true, "success"}, + } + + for i, tc := range testCases { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err, "Msg %d failed: %s", i, tc.errMsg) + } else { + suite.Require().Error(err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} diff --git a/x/ibc/core/03-connection/types/query.go b/x/ibc/core/03-connection/types/query.go new file mode 100644 index 000000000000..7661b38d9bba --- /dev/null +++ b/x/ibc/core/03-connection/types/query.go @@ -0,0 +1,70 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "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, +) *QueryConnectionResponse { + return &QueryConnectionResponse{ + Connection: &connection, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryClientConnectionsResponse creates a new ConnectionPaths instance +func NewQueryClientConnectionsResponse( + connectionPaths []string, proof []byte, height clienttypes.Height, +) *QueryClientConnectionsResponse { + return &QueryClientConnectionsResponse{ + ConnectionPaths: connectionPaths, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryClientConnectionsRequest creates a new QueryClientConnectionsRequest instance +func NewQueryClientConnectionsRequest(clientID string) *QueryClientConnectionsRequest { + return &QueryClientConnectionsRequest{ + ClientId: clientID, + } +} + +// NewQueryConnectionClientStateResponse creates a newQueryConnectionClientStateResponse instance +func NewQueryConnectionClientStateResponse(identifiedClientState clienttypes.IdentifiedClientState, proof []byte, height clienttypes.Height) *QueryConnectionClientStateResponse { + return &QueryConnectionClientStateResponse{ + IdentifiedClientState: &identifiedClientState, + Proof: proof, + ProofHeight: height, + } +} + +// 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{ + ConsensusState: anyConsensusState, + ClientId: clientID, + Proof: proof, + 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 new file mode 100644 index 000000000000..6796cdb1f8ad --- /dev/null +++ b/x/ibc/core/03-connection/types/query.pb.go @@ -0,0 +1,2892 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/connection/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "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 + +// QueryConnectionRequest is the request type for the Query/Connection RPC +// method +type QueryConnectionRequest struct { + // connection unique identifier + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` +} + +func (m *QueryConnectionRequest) Reset() { *m = QueryConnectionRequest{} } +func (m *QueryConnectionRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionRequest) ProtoMessage() {} +func (*QueryConnectionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{0} +} +func (m *QueryConnectionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionRequest.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 *QueryConnectionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionRequest.Merge(m, src) +} +func (m *QueryConnectionRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionRequest proto.InternalMessageInfo + +func (m *QueryConnectionRequest) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +// QueryConnectionResponse is the response type for the Query/Connection RPC +// method. Besides the connection end, it includes a proof and the height from +// which the proof was retrieved. +type QueryConnectionResponse struct { + // connection associated with the request identifier + Connection *ConnectionEnd `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryConnectionResponse) Reset() { *m = QueryConnectionResponse{} } +func (m *QueryConnectionResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionResponse) ProtoMessage() {} +func (*QueryConnectionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{1} +} +func (m *QueryConnectionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionResponse.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 *QueryConnectionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionResponse.Merge(m, src) +} +func (m *QueryConnectionResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionResponse proto.InternalMessageInfo + +func (m *QueryConnectionResponse) GetConnection() *ConnectionEnd { + if m != nil { + return m.Connection + } + return nil +} + +func (m *QueryConnectionResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryConnectionResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryConnectionsRequest is the request type for the Query/Connections RPC +// method +type QueryConnectionsRequest struct { + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConnectionsRequest) Reset() { *m = QueryConnectionsRequest{} } +func (m *QueryConnectionsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionsRequest) ProtoMessage() {} +func (*QueryConnectionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{2} +} +func (m *QueryConnectionsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionsRequest.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 *QueryConnectionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionsRequest.Merge(m, src) +} +func (m *QueryConnectionsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionsRequest proto.InternalMessageInfo + +func (m *QueryConnectionsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryConnectionsResponse is the response type for the Query/Connections RPC +// method. +type QueryConnectionsResponse struct { + // list of stored connections of the chain. + Connections []*IdentifiedConnection `protobuf:"bytes,1,rep,name=connections,proto3" json:"connections,omitempty"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` +} + +func (m *QueryConnectionsResponse) Reset() { *m = QueryConnectionsResponse{} } +func (m *QueryConnectionsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionsResponse) ProtoMessage() {} +func (*QueryConnectionsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{3} +} +func (m *QueryConnectionsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionsResponse.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 *QueryConnectionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionsResponse.Merge(m, src) +} +func (m *QueryConnectionsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionsResponse proto.InternalMessageInfo + +func (m *QueryConnectionsResponse) GetConnections() []*IdentifiedConnection { + if m != nil { + return m.Connections + } + return nil +} + +func (m *QueryConnectionsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryConnectionsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryClientConnectionsRequest is the request type for the +// Query/ClientConnections RPC method +type QueryClientConnectionsRequest struct { + // client identifier associated with a connection + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` +} + +func (m *QueryClientConnectionsRequest) Reset() { *m = QueryClientConnectionsRequest{} } +func (m *QueryClientConnectionsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryClientConnectionsRequest) ProtoMessage() {} +func (*QueryClientConnectionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{4} +} +func (m *QueryClientConnectionsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientConnectionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientConnectionsRequest.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 *QueryClientConnectionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientConnectionsRequest.Merge(m, src) +} +func (m *QueryClientConnectionsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryClientConnectionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientConnectionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientConnectionsRequest proto.InternalMessageInfo + +func (m *QueryClientConnectionsRequest) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +// QueryClientConnectionsResponse is the response type for the +// Query/ClientConnections RPC method +type QueryClientConnectionsResponse struct { + // slice of all the connection paths associated with a client. + ConnectionPaths []string `protobuf:"bytes,1,rep,name=connection_paths,json=connectionPaths,proto3" json:"connection_paths,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was generated + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryClientConnectionsResponse) Reset() { *m = QueryClientConnectionsResponse{} } +func (m *QueryClientConnectionsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryClientConnectionsResponse) ProtoMessage() {} +func (*QueryClientConnectionsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{5} +} +func (m *QueryClientConnectionsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryClientConnectionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryClientConnectionsResponse.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 *QueryClientConnectionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryClientConnectionsResponse.Merge(m, src) +} +func (m *QueryClientConnectionsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryClientConnectionsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryClientConnectionsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryClientConnectionsResponse proto.InternalMessageInfo + +func (m *QueryClientConnectionsResponse) GetConnectionPaths() []string { + if m != nil { + return m.ConnectionPaths + } + return nil +} + +func (m *QueryClientConnectionsResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryClientConnectionsResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryConnectionClientStateRequest is the request type for the +// Query/ConnectionClientState RPC method +type QueryConnectionClientStateRequest struct { + // connection identifier + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` +} + +func (m *QueryConnectionClientStateRequest) Reset() { *m = QueryConnectionClientStateRequest{} } +func (m *QueryConnectionClientStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionClientStateRequest) ProtoMessage() {} +func (*QueryConnectionClientStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{6} +} +func (m *QueryConnectionClientStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionClientStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionClientStateRequest.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 *QueryConnectionClientStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionClientStateRequest.Merge(m, src) +} +func (m *QueryConnectionClientStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionClientStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionClientStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionClientStateRequest proto.InternalMessageInfo + +func (m *QueryConnectionClientStateRequest) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +// QueryConnectionClientStateResponse is the response type for the +// Query/ConnectionClientState RPC method +type QueryConnectionClientStateResponse struct { + // client state associated with the channel + IdentifiedClientState *types.IdentifiedClientState `protobuf:"bytes,1,opt,name=identified_client_state,json=identifiedClientState,proto3" json:"identified_client_state,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryConnectionClientStateResponse) Reset() { *m = QueryConnectionClientStateResponse{} } +func (m *QueryConnectionClientStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionClientStateResponse) ProtoMessage() {} +func (*QueryConnectionClientStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{7} +} +func (m *QueryConnectionClientStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionClientStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionClientStateResponse.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 *QueryConnectionClientStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionClientStateResponse.Merge(m, src) +} +func (m *QueryConnectionClientStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionClientStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionClientStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionClientStateResponse proto.InternalMessageInfo + +func (m *QueryConnectionClientStateResponse) GetIdentifiedClientState() *types.IdentifiedClientState { + if m != nil { + return m.IdentifiedClientState + } + return nil +} + +func (m *QueryConnectionClientStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryConnectionClientStateResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryConnectionConsensusStateRequest is the request type for the +// Query/ConnectionConsensusState RPC method +type QueryConnectionConsensusStateRequest struct { + // connection identifier + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + RevisionNumber uint64 `protobuf:"varint,2,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty"` + RevisionHeight uint64 `protobuf:"varint,3,opt,name=revision_height,json=revisionHeight,proto3" json:"revision_height,omitempty"` +} + +func (m *QueryConnectionConsensusStateRequest) Reset() { *m = QueryConnectionConsensusStateRequest{} } +func (m *QueryConnectionConsensusStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionConsensusStateRequest) ProtoMessage() {} +func (*QueryConnectionConsensusStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{8} +} +func (m *QueryConnectionConsensusStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionConsensusStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionConsensusStateRequest.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 *QueryConnectionConsensusStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionConsensusStateRequest.Merge(m, src) +} +func (m *QueryConnectionConsensusStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionConsensusStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionConsensusStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionConsensusStateRequest proto.InternalMessageInfo + +func (m *QueryConnectionConsensusStateRequest) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +func (m *QueryConnectionConsensusStateRequest) GetRevisionNumber() uint64 { + if m != nil { + return m.RevisionNumber + } + return 0 +} + +func (m *QueryConnectionConsensusStateRequest) GetRevisionHeight() uint64 { + if m != nil { + return m.RevisionHeight + } + return 0 +} + +// QueryConnectionConsensusStateResponse is the response type for the +// Query/ConnectionConsensusState RPC method +type QueryConnectionConsensusStateResponse struct { + // consensus state associated with the channel + ConsensusState *types1.Any `protobuf:"bytes,1,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty"` + // client ID associated with the consensus state + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,3,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryConnectionConsensusStateResponse) Reset() { *m = QueryConnectionConsensusStateResponse{} } +func (m *QueryConnectionConsensusStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionConsensusStateResponse) ProtoMessage() {} +func (*QueryConnectionConsensusStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cd8d529f8c7cd06b, []int{9} +} +func (m *QueryConnectionConsensusStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionConsensusStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionConsensusStateResponse.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 *QueryConnectionConsensusStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionConsensusStateResponse.Merge(m, src) +} +func (m *QueryConnectionConsensusStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionConsensusStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionConsensusStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionConsensusStateResponse proto.InternalMessageInfo + +func (m *QueryConnectionConsensusStateResponse) GetConsensusState() *types1.Any { + if m != nil { + return m.ConsensusState + } + return nil +} + +func (m *QueryConnectionConsensusStateResponse) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *QueryConnectionConsensusStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryConnectionConsensusStateResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +func init() { + proto.RegisterType((*QueryConnectionRequest)(nil), "ibc.core.connection.v1.QueryConnectionRequest") + proto.RegisterType((*QueryConnectionResponse)(nil), "ibc.core.connection.v1.QueryConnectionResponse") + proto.RegisterType((*QueryConnectionsRequest)(nil), "ibc.core.connection.v1.QueryConnectionsRequest") + proto.RegisterType((*QueryConnectionsResponse)(nil), "ibc.core.connection.v1.QueryConnectionsResponse") + proto.RegisterType((*QueryClientConnectionsRequest)(nil), "ibc.core.connection.v1.QueryClientConnectionsRequest") + proto.RegisterType((*QueryClientConnectionsResponse)(nil), "ibc.core.connection.v1.QueryClientConnectionsResponse") + proto.RegisterType((*QueryConnectionClientStateRequest)(nil), "ibc.core.connection.v1.QueryConnectionClientStateRequest") + proto.RegisterType((*QueryConnectionClientStateResponse)(nil), "ibc.core.connection.v1.QueryConnectionClientStateResponse") + proto.RegisterType((*QueryConnectionConsensusStateRequest)(nil), "ibc.core.connection.v1.QueryConnectionConsensusStateRequest") + proto.RegisterType((*QueryConnectionConsensusStateResponse)(nil), "ibc.core.connection.v1.QueryConnectionConsensusStateResponse") +} + +func init() { + proto.RegisterFile("ibc/core/connection/v1/query.proto", fileDescriptor_cd8d529f8c7cd06b) +} + +var fileDescriptor_cd8d529f8c7cd06b = []byte{ + // 892 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x4f, 0x33, 0x45, + 0x18, 0xee, 0x14, 0xbe, 0x2f, 0x1f, 0x53, 0xfc, 0x3e, 0x9d, 0x14, 0xa8, 0xab, 0x16, 0x5c, 0x45, + 0x0a, 0x91, 0x19, 0x0a, 0xd1, 0x20, 0xd0, 0x44, 0x21, 0x88, 0x1c, 0x24, 0xb8, 0xc6, 0x8b, 0x17, + 0xb2, 0xbb, 0x1d, 0xb6, 0x1b, 0xe9, 0x4e, 0xe9, 0x6e, 0x1b, 0x1b, 0xac, 0x07, 0xe3, 0x0f, 0x30, + 0xf1, 0xee, 0xc1, 0x83, 0x89, 0x27, 0x8f, 0x1e, 0xfc, 0x01, 0x72, 0x24, 0xf1, 0xe2, 0x45, 0x62, + 0x8a, 0x57, 0x2f, 0xfe, 0x02, 0xb3, 0x33, 0x53, 0x76, 0xb6, 0xdd, 0x96, 0xd2, 0x7c, 0x9c, 0xba, + 0xfb, 0xce, 0xfb, 0xce, 0x3c, 0xcf, 0xf3, 0xbe, 0xf3, 0x6c, 0xa1, 0xee, 0x5a, 0x36, 0xb1, 0x59, + 0x9d, 0x12, 0x9b, 0x79, 0x1e, 0xb5, 0x03, 0x97, 0x79, 0xa4, 0x59, 0x24, 0xe7, 0x0d, 0x5a, 0x6f, + 0xe1, 0x5a, 0x9d, 0x05, 0x0c, 0xcd, 0xba, 0x96, 0x8d, 0xc3, 0x1c, 0x1c, 0xe5, 0xe0, 0x66, 0x51, + 0xcb, 0x3a, 0xcc, 0x61, 0x3c, 0x85, 0x84, 0x4f, 0x22, 0x5b, 0x5b, 0xb1, 0x99, 0x5f, 0x65, 0x3e, + 0xb1, 0x4c, 0x9f, 0x8a, 0x6d, 0x48, 0xb3, 0x68, 0xd1, 0xc0, 0x2c, 0x92, 0x9a, 0xe9, 0xb8, 0x9e, + 0xc9, 0xcb, 0x45, 0xee, 0x7c, 0x74, 0xfa, 0x99, 0x4b, 0xbd, 0x20, 0x3c, 0x59, 0x3c, 0xc9, 0x84, + 0xa5, 0x01, 0xf0, 0x14, 0x20, 0x22, 0xf1, 0x55, 0x87, 0x31, 0xe7, 0x8c, 0x12, 0xb3, 0xe6, 0x12, + 0xd3, 0xf3, 0x58, 0xc0, 0x8f, 0xf1, 0xe5, 0xea, 0xcb, 0x72, 0x95, 0xbf, 0x59, 0x8d, 0x53, 0x62, + 0x7a, 0x92, 0x9c, 0x5e, 0x82, 0xb3, 0x9f, 0x84, 0x20, 0xf7, 0x6e, 0x77, 0x34, 0xe8, 0x79, 0x83, + 0xfa, 0x01, 0x7a, 0x03, 0xbe, 0x10, 0x1d, 0x73, 0xe2, 0x96, 0x73, 0x60, 0x01, 0x14, 0xa6, 0x8c, + 0xe9, 0x28, 0x78, 0x58, 0xd6, 0x7f, 0x03, 0x70, 0xae, 0xaf, 0xde, 0xaf, 0x31, 0xcf, 0xa7, 0x68, + 0x1f, 0xc2, 0x28, 0x97, 0x57, 0x67, 0xd6, 0x17, 0x71, 0xb2, 0x98, 0x38, 0xaa, 0xdf, 0xf7, 0xca, + 0x86, 0x52, 0x88, 0xb2, 0xf0, 0x51, 0xad, 0xce, 0xd8, 0x69, 0x2e, 0xbd, 0x00, 0x0a, 0xd3, 0x86, + 0x78, 0x41, 0x7b, 0x70, 0x9a, 0x3f, 0x9c, 0x54, 0xa8, 0xeb, 0x54, 0x82, 0xdc, 0x04, 0xdf, 0x5e, + 0x53, 0xb6, 0x17, 0x3a, 0x36, 0x8b, 0xf8, 0x23, 0x9e, 0xb1, 0x3b, 0x79, 0x79, 0x3d, 0x9f, 0x32, + 0x32, 0xbc, 0x4a, 0x84, 0x74, 0xb3, 0x0f, 0xbc, 0xdf, 0x65, 0xff, 0x21, 0x84, 0x51, 0xbb, 0x24, + 0xf8, 0xb7, 0xb0, 0xe8, 0x2d, 0x0e, 0x7b, 0x8b, 0xc5, 0x88, 0xc8, 0xde, 0xe2, 0x63, 0xd3, 0xa1, + 0xb2, 0xd6, 0x50, 0x2a, 0xf5, 0x7f, 0x01, 0xcc, 0xf5, 0x9f, 0x21, 0x15, 0x3a, 0x82, 0x99, 0x88, + 0xa8, 0x9f, 0x03, 0x0b, 0x13, 0x85, 0xcc, 0xfa, 0xdb, 0x83, 0x24, 0x3a, 0x2c, 0x53, 0x2f, 0x70, + 0x4f, 0x5d, 0x5a, 0x56, 0xc4, 0x56, 0x37, 0x40, 0x07, 0x31, 0xd0, 0x69, 0x0e, 0x7a, 0xe9, 0x4e, + 0xd0, 0x02, 0x8c, 0x8a, 0x1a, 0x6d, 0xc2, 0xc7, 0xf7, 0xd4, 0x55, 0xe6, 0xeb, 0x3b, 0xf0, 0x35, + 0x41, 0x97, 0xa7, 0x25, 0x08, 0xfb, 0x0a, 0x9c, 0x12, 0x5b, 0x44, 0x23, 0xf5, 0x44, 0x04, 0x0e, + 0xcb, 0xfa, 0x4f, 0x00, 0xe6, 0x07, 0x95, 0x4b, 0xcd, 0x96, 0xe1, 0x8b, 0xca, 0x58, 0xd6, 0xcc, + 0xa0, 0x22, 0x84, 0x9b, 0x32, 0x9e, 0x45, 0xf1, 0xe3, 0x30, 0xfc, 0x90, 0x93, 0x63, 0xc1, 0xd7, + 0x7b, 0xba, 0x2a, 0x10, 0x7f, 0x1a, 0x98, 0x41, 0x77, 0x0e, 0x50, 0x29, 0xf1, 0x06, 0xed, 0xe6, + 0xfe, 0xbb, 0x9e, 0xcf, 0xb6, 0xcc, 0xea, 0xd9, 0x96, 0x1e, 0x5b, 0xd6, 0x7b, 0xee, 0x56, 0x07, + 0x40, 0x7d, 0xd8, 0x21, 0x52, 0x10, 0x13, 0xce, 0xb9, 0xb7, 0x93, 0x71, 0x22, 0xb5, 0xf5, 0xc3, + 0x14, 0x39, 0xb6, 0xcb, 0x49, 0xd4, 0x94, 0x61, 0x52, 0xf6, 0x9c, 0x71, 0x93, 0xc2, 0x0f, 0x29, + 0xe4, 0xaf, 0x00, 0xbe, 0xd9, 0x4b, 0x32, 0xa4, 0xe5, 0xf9, 0x0d, 0xff, 0x39, 0x8a, 0x89, 0x96, + 0xe0, 0xb3, 0x3a, 0x6d, 0xba, 0x7e, 0xb8, 0xea, 0x35, 0xaa, 0x16, 0xad, 0x73, 0x32, 0x93, 0xc6, + 0xd3, 0x6e, 0xf8, 0x88, 0x47, 0x63, 0x89, 0x0a, 0x31, 0x25, 0x51, 0x22, 0xbf, 0x06, 0x70, 0xf1, + 0x0e, 0xe4, 0xb2, 0x43, 0x25, 0x18, 0x8e, 0xa6, 0x58, 0x89, 0x75, 0x26, 0x8b, 0x85, 0x31, 0xe3, + 0xae, 0x31, 0xe3, 0x0f, 0xbc, 0x96, 0xf1, 0xd4, 0x8e, 0x6d, 0x13, 0xbf, 0x31, 0xe9, 0xf8, 0x8d, + 0x89, 0x5a, 0x33, 0x31, 0xac, 0x35, 0x93, 0x63, 0xb4, 0x66, 0xfd, 0xe7, 0x27, 0xf0, 0x11, 0x27, + 0x88, 0x7e, 0x01, 0x10, 0x46, 0x2c, 0x11, 0x1e, 0xe4, 0x50, 0xc9, 0x5f, 0x12, 0x8d, 0x8c, 0x9c, + 0x2f, 0x04, 0xd3, 0xdf, 0xff, 0xe6, 0x8f, 0x7f, 0xbe, 0x4f, 0x6f, 0xa1, 0x4d, 0x92, 0xfc, 0xfd, + 0x13, 0x9f, 0x53, 0xc5, 0xf9, 0xc8, 0x45, 0xac, 0xf9, 0x6d, 0xf4, 0x23, 0x80, 0x19, 0xc5, 0x3d, + 0xd0, 0xa8, 0x10, 0xba, 0x36, 0xa5, 0xad, 0x8d, 0x5e, 0x20, 0x41, 0xaf, 0x71, 0xd0, 0x2b, 0xa8, + 0x30, 0x2a, 0x68, 0xf4, 0x3b, 0x80, 0x2f, 0xf5, 0x19, 0x1d, 0x7a, 0x67, 0xf8, 0xc9, 0x03, 0x7c, + 0x55, 0x7b, 0xf7, 0xbe, 0x65, 0x12, 0xf6, 0x1e, 0x87, 0x5d, 0x42, 0xdb, 0xc3, 0x61, 0x8b, 0x01, + 0x8c, 0x4b, 0xde, 0x1d, 0xca, 0x36, 0xfa, 0x0b, 0xc0, 0x99, 0x44, 0x97, 0x42, 0xef, 0x8d, 0xa8, + 0x63, 0xbf, 0x7d, 0x6a, 0x5b, 0xe3, 0x94, 0x4a, 0x56, 0x1f, 0x73, 0x56, 0x07, 0x68, 0x7f, 0xdc, + 0x09, 0x22, 0xaa, 0x91, 0xa2, 0x1f, 0xd2, 0x30, 0x37, 0xe8, 0x9a, 0xa3, 0x9d, 0x51, 0x71, 0x26, + 0xf9, 0x9a, 0x56, 0x1a, 0xb3, 0x5a, 0x12, 0xfd, 0x16, 0x70, 0xa6, 0x5f, 0xa3, 0xaf, 0xc6, 0x67, + 0x1a, 0xf7, 0x26, 0xd2, 0xf5, 0x39, 0x72, 0xd1, 0xe3, 0x98, 0x6d, 0x22, 0xec, 0x44, 0x59, 0x10, + 0x81, 0xf6, 0xee, 0x67, 0x97, 0x9d, 0x3c, 0xb8, 0xea, 0xe4, 0xc1, 0xdf, 0x9d, 0x3c, 0xf8, 0xee, + 0x26, 0x9f, 0xba, 0xba, 0xc9, 0xa7, 0xfe, 0xbc, 0xc9, 0xa7, 0x3e, 0xdf, 0x76, 0xdc, 0xa0, 0xd2, + 0xb0, 0xb0, 0xcd, 0xaa, 0x44, 0xfe, 0x35, 0x16, 0x3f, 0xab, 0x7e, 0xf9, 0x0b, 0xf2, 0x65, 0x84, + 0x7a, 0x6d, 0x63, 0x55, 0x01, 0x1e, 0xb4, 0x6a, 0xd4, 0xb7, 0x1e, 0x73, 0x63, 0xdc, 0xf8, 0x3f, + 0x00, 0x00, 0xff, 0xff, 0x7c, 0x35, 0x91, 0xa4, 0xa7, 0x0b, 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 { + // Connection queries an IBC connection end. + Connection(ctx context.Context, in *QueryConnectionRequest, opts ...grpc.CallOption) (*QueryConnectionResponse, error) + // Connections queries all the IBC connections of a chain. + Connections(ctx context.Context, in *QueryConnectionsRequest, opts ...grpc.CallOption) (*QueryConnectionsResponse, error) + // ClientConnections queries the connection paths associated with a client + // state. + ClientConnections(ctx context.Context, in *QueryClientConnectionsRequest, opts ...grpc.CallOption) (*QueryClientConnectionsResponse, error) + // ConnectionClientState queries the client state associated with the + // connection. + ConnectionClientState(ctx context.Context, in *QueryConnectionClientStateRequest, opts ...grpc.CallOption) (*QueryConnectionClientStateResponse, error) + // ConnectionConsensusState queries the consensus state associated with the + // connection. + ConnectionConsensusState(ctx context.Context, in *QueryConnectionConsensusStateRequest, opts ...grpc.CallOption) (*QueryConnectionConsensusStateResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Connection(ctx context.Context, in *QueryConnectionRequest, opts ...grpc.CallOption) (*QueryConnectionResponse, error) { + out := new(QueryConnectionResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/Connection", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Connections(ctx context.Context, in *QueryConnectionsRequest, opts ...grpc.CallOption) (*QueryConnectionsResponse, error) { + out := new(QueryConnectionsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/Connections", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ClientConnections(ctx context.Context, in *QueryClientConnectionsRequest, opts ...grpc.CallOption) (*QueryClientConnectionsResponse, error) { + out := new(QueryClientConnectionsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/ClientConnections", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConnectionClientState(ctx context.Context, in *QueryConnectionClientStateRequest, opts ...grpc.CallOption) (*QueryConnectionClientStateResponse, error) { + out := new(QueryConnectionClientStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/ConnectionClientState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConnectionConsensusState(ctx context.Context, in *QueryConnectionConsensusStateRequest, opts ...grpc.CallOption) (*QueryConnectionConsensusStateResponse, error) { + out := new(QueryConnectionConsensusStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Query/ConnectionConsensusState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Connection queries an IBC connection end. + Connection(context.Context, *QueryConnectionRequest) (*QueryConnectionResponse, error) + // Connections queries all the IBC connections of a chain. + Connections(context.Context, *QueryConnectionsRequest) (*QueryConnectionsResponse, error) + // ClientConnections queries the connection paths associated with a client + // state. + ClientConnections(context.Context, *QueryClientConnectionsRequest) (*QueryClientConnectionsResponse, error) + // ConnectionClientState queries the client state associated with the + // connection. + ConnectionClientState(context.Context, *QueryConnectionClientStateRequest) (*QueryConnectionClientStateResponse, error) + // ConnectionConsensusState queries the consensus state associated with the + // connection. + ConnectionConsensusState(context.Context, *QueryConnectionConsensusStateRequest) (*QueryConnectionConsensusStateResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Connection(ctx context.Context, req *QueryConnectionRequest) (*QueryConnectionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Connection not implemented") +} +func (*UnimplementedQueryServer) Connections(ctx context.Context, req *QueryConnectionsRequest) (*QueryConnectionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Connections not implemented") +} +func (*UnimplementedQueryServer) ClientConnections(ctx context.Context, req *QueryClientConnectionsRequest) (*QueryClientConnectionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClientConnections not implemented") +} +func (*UnimplementedQueryServer) ConnectionClientState(ctx context.Context, req *QueryConnectionClientStateRequest) (*QueryConnectionClientStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionClientState not implemented") +} +func (*UnimplementedQueryServer) ConnectionConsensusState(ctx context.Context, req *QueryConnectionConsensusStateRequest) (*QueryConnectionConsensusStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionConsensusState not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Connection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Connection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/Connection", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Connection(ctx, req.(*QueryConnectionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Connections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Connections(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/Connections", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Connections(ctx, req.(*QueryConnectionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ClientConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryClientConnectionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ClientConnections(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/ClientConnections", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ClientConnections(ctx, req.(*QueryClientConnectionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConnectionClientState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionClientStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConnectionClientState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/ConnectionClientState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConnectionClientState(ctx, req.(*QueryConnectionClientStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConnectionConsensusState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionConsensusStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConnectionConsensusState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Query/ConnectionConsensusState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConnectionConsensusState(ctx, req.(*QueryConnectionConsensusStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.connection.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Connection", + Handler: _Query_Connection_Handler, + }, + { + MethodName: "Connections", + Handler: _Query_Connections_Handler, + }, + { + MethodName: "ClientConnections", + Handler: _Query_ClientConnections_Handler, + }, + { + MethodName: "ConnectionClientState", + Handler: _Query_ConnectionClientState_Handler, + }, + { + MethodName: "ConnectionConsensusState", + Handler: _Query_ConnectionConsensusState_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/connection/v1/query.proto", +} + +func (m *QueryConnectionRequest) 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 *QueryConnectionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionResponse) 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 *QueryConnectionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.Connection != nil { + { + size, err := m.Connection.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 *QueryConnectionsRequest) 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 *QueryConnectionsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionsResponse) 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 *QueryConnectionsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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.Connections) > 0 { + for iNdEx := len(m.Connections) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Connections[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 *QueryClientConnectionsRequest) 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 *QueryClientConnectionsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientConnectionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryClientConnectionsResponse) 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 *QueryClientConnectionsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryClientConnectionsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if len(m.ConnectionPaths) > 0 { + for iNdEx := len(m.ConnectionPaths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ConnectionPaths[iNdEx]) + copy(dAtA[i:], m.ConnectionPaths[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionPaths[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionClientStateRequest) 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 *QueryConnectionClientStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionClientStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionClientStateResponse) 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 *QueryConnectionClientStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionClientStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.IdentifiedClientState != nil { + { + size, err := m.IdentifiedClientState.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 *QueryConnectionConsensusStateRequest) 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 *QueryConnectionConsensusStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionConsensusStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RevisionHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionHeight)) + i-- + dAtA[i] = 0x18 + } + if m.RevisionNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionNumber)) + i-- + dAtA[i] = 0x10 + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionConsensusStateResponse) 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 *QueryConnectionConsensusStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionConsensusStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0x12 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.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 *QueryConnectionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConnectionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Connection != nil { + l = m.Connection.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConnectionsRequest) 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 *QueryConnectionsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Connections) > 0 { + for _, e := range m.Connections { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryClientConnectionsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryClientConnectionsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ConnectionPaths) > 0 { + for _, s := range m.ConnectionPaths { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConnectionClientStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryConnectionClientStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IdentifiedClientState != nil { + l = m.IdentifiedClientState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConnectionConsensusStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.RevisionNumber != 0 { + n += 1 + sovQuery(uint64(m.RevisionNumber)) + } + if m.RevisionHeight != 0 { + n += 1 + sovQuery(uint64(m.RevisionHeight)) + } + return n +} + +func (m *QueryConnectionConsensusStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.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 *QueryConnectionRequest) 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: QueryConnectionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", 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.ConnectionId = 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 *QueryConnectionResponse) 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: QueryConnectionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connection", 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.Connection == nil { + m.Connection = &ConnectionEnd{} + } + if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryConnectionsRequest) 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: QueryConnectionsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryConnectionsResponse) 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: QueryConnectionsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connections", 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.Connections = append(m.Connections, &IdentifiedConnection{}) + if err := m.Connections[len(m.Connections)-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 + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryClientConnectionsRequest) 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: QueryClientConnectionsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientConnectionsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = 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 *QueryClientConnectionsResponse) 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: QueryClientConnectionsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryClientConnectionsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionPaths", 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.ConnectionPaths = append(m.ConnectionPaths, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryConnectionClientStateRequest) 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: QueryConnectionClientStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionClientStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", 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.ConnectionId = 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 *QueryConnectionClientStateResponse) 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: QueryConnectionClientStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionClientStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IdentifiedClientState", 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.IdentifiedClientState == nil { + m.IdentifiedClientState = &types.IdentifiedClientState{} + } + if err := m.IdentifiedClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryConnectionConsensusStateRequest) 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: QueryConnectionConsensusStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionConsensusStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionNumber", wireType) + } + m.RevisionNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHeight", wireType) + } + m.RevisionHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryConnectionConsensusStateResponse) 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: QueryConnectionConsensusStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionConsensusStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", 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.ConsensusState == nil { + m.ConsensusState = &types1.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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/ibc/core/03-connection/types/query.pb.gw.go b/x/ibc/core/03-connection/types/query.pb.gw.go new file mode 100644 index 000000000000..3e6de4e06415 --- /dev/null +++ b/x/ibc/core/03-connection/types/query.pb.gw.go @@ -0,0 +1,602 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ibc/core/connection/v1/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_Connection_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := client.Connection(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Connection_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := server.Connection(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Connections_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Connections_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionsRequest + 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_Connections_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Connections(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Connections_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionsRequest + 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_Connections_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Connections(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ClientConnections_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientConnectionsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + msg, err := client.ClientConnections(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ClientConnections_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryClientConnectionsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["client_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "client_id") + } + + protoReq.ClientId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "client_id", err) + } + + msg, err := server.ClientConnections(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ConnectionClientState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := client.ConnectionClientState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConnectionClientState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + msg, err := server.ConnectionClientState(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ConnectionConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", err) + } + + msg, err := client.ConnectionConsensusState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConnectionConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection_id") + } + + protoReq.ConnectionId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", err) + } + + msg, err := server.ConnectionConsensusState(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_Connection_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_Connection_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_Connection_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Connections_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_Connections_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_Connections_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientConnections_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_ClientConnections_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_ClientConnections_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionClientState_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_ConnectionClientState_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_ConnectionClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionConsensusState_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_ConnectionConsensusState_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_ConnectionConsensusState_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_Connection_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_Connection_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_Connection_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Connections_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_Connections_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_Connections_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ClientConnections_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_ClientConnections_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_ClientConnections_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionClientState_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_ConnectionClientState_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_ConnectionClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionConsensusState_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_ConnectionConsensusState_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_ConnectionConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Connection_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Connections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "connection", "v1beta1", "connections"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ClientConnections_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"ibc", "core", "connection", "v1beta1", "client_connections", "client_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ConnectionClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ConnectionConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 2, 7, 1, 0, 4, 1, 5, 8, 2, 9, 1, 0, 4, 1, 5, 10}, []string{"ibc", "core", "connection", "v1beta1", "connections", "connection_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Connection_0 = runtime.ForwardResponseMessage + + forward_Query_Connections_0 = runtime.ForwardResponseMessage + + forward_Query_ClientConnections_0 = runtime.ForwardResponseMessage + + forward_Query_ConnectionClientState_0 = runtime.ForwardResponseMessage + + forward_Query_ConnectionConsensusState_0 = runtime.ForwardResponseMessage +) diff --git a/x/ibc/core/03-connection/types/tx.pb.go b/x/ibc/core/03-connection/types/tx.pb.go new file mode 100644 index 000000000000..00e4fd9d0bb7 --- /dev/null +++ b/x/ibc/core/03-connection/types/tx.pb.go @@ -0,0 +1,2778 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/connection/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgConnectionOpenInit defines the msg sent by an account on Chain A to +// initialize a connection with Chain B. +type MsgConnectionOpenInit struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + Counterparty Counterparty `protobuf:"bytes,2,opt,name=counterparty,proto3" json:"counterparty"` + Version *Version `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + DelayPeriod uint64 `protobuf:"varint,4,opt,name=delay_period,json=delayPeriod,proto3" json:"delay_period,omitempty" yaml:"delay_period"` + Signer string `protobuf:"bytes,5,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgConnectionOpenInit) Reset() { *m = MsgConnectionOpenInit{} } +func (m *MsgConnectionOpenInit) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenInit) ProtoMessage() {} +func (*MsgConnectionOpenInit) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{0} +} +func (m *MsgConnectionOpenInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenInit.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 *MsgConnectionOpenInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenInit.Merge(m, src) +} +func (m *MsgConnectionOpenInit) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenInit) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenInit.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenInit proto.InternalMessageInfo + +// MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response type. +type MsgConnectionOpenInitResponse struct { +} + +func (m *MsgConnectionOpenInitResponse) Reset() { *m = MsgConnectionOpenInitResponse{} } +func (m *MsgConnectionOpenInitResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenInitResponse) ProtoMessage() {} +func (*MsgConnectionOpenInitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{1} +} +func (m *MsgConnectionOpenInitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenInitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenInitResponse.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 *MsgConnectionOpenInitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenInitResponse.Merge(m, src) +} +func (m *MsgConnectionOpenInitResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenInitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenInitResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenInitResponse proto.InternalMessageInfo + +// MsgConnectionOpenTry defines a msg sent by a Relayer to try to open a +// connection on Chain B. +type MsgConnectionOpenTry struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + // in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier + // of the previous connection in state INIT + PreviousConnectionId string `protobuf:"bytes,2,opt,name=previous_connection_id,json=previousConnectionId,proto3" json:"previous_connection_id,omitempty" yaml:"previous_connection_id"` + ClientState *types.Any `protobuf:"bytes,3,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + Counterparty Counterparty `protobuf:"bytes,4,opt,name=counterparty,proto3" json:"counterparty"` + DelayPeriod uint64 `protobuf:"varint,5,opt,name=delay_period,json=delayPeriod,proto3" json:"delay_period,omitempty" yaml:"delay_period"` + CounterpartyVersions []*Version `protobuf:"bytes,6,rep,name=counterparty_versions,json=counterpartyVersions,proto3" json:"counterparty_versions,omitempty" yaml:"counterparty_versions"` + ProofHeight types1.Height `protobuf:"bytes,7,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + // proof of the initialization the connection on Chain A: `UNITIALIZED -> + // INIT` + ProofInit []byte `protobuf:"bytes,8,opt,name=proof_init,json=proofInit,proto3" json:"proof_init,omitempty" yaml:"proof_init"` + // proof of client state included in message + ProofClient []byte `protobuf:"bytes,9,opt,name=proof_client,json=proofClient,proto3" json:"proof_client,omitempty" yaml:"proof_client"` + // proof of client consensus state + ProofConsensus []byte `protobuf:"bytes,10,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` + ConsensusHeight types1.Height `protobuf:"bytes,11,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height" yaml:"consensus_height"` + Signer string `protobuf:"bytes,12,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgConnectionOpenTry) Reset() { *m = MsgConnectionOpenTry{} } +func (m *MsgConnectionOpenTry) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenTry) ProtoMessage() {} +func (*MsgConnectionOpenTry) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{2} +} +func (m *MsgConnectionOpenTry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenTry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenTry.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 *MsgConnectionOpenTry) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenTry.Merge(m, src) +} +func (m *MsgConnectionOpenTry) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenTry) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenTry.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenTry proto.InternalMessageInfo + +// MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type. +type MsgConnectionOpenTryResponse struct { +} + +func (m *MsgConnectionOpenTryResponse) Reset() { *m = MsgConnectionOpenTryResponse{} } +func (m *MsgConnectionOpenTryResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenTryResponse) ProtoMessage() {} +func (*MsgConnectionOpenTryResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{3} +} +func (m *MsgConnectionOpenTryResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenTryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenTryResponse.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 *MsgConnectionOpenTryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenTryResponse.Merge(m, src) +} +func (m *MsgConnectionOpenTryResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenTryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenTryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenTryResponse proto.InternalMessageInfo + +// MsgConnectionOpenAck defines a msg sent by a Relayer to Chain A to +// acknowledge the change of connection state to TRYOPEN on Chain B. +type MsgConnectionOpenAck struct { + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + CounterpartyConnectionId string `protobuf:"bytes,2,opt,name=counterparty_connection_id,json=counterpartyConnectionId,proto3" json:"counterparty_connection_id,omitempty" yaml:"counterparty_connection_id"` + Version *Version `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` + ClientState *types.Any `protobuf:"bytes,4,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` + ProofHeight types1.Height `protobuf:"bytes,5,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + // proof of the initialization the connection on Chain B: `UNITIALIZED -> + // TRYOPEN` + ProofTry []byte `protobuf:"bytes,6,opt,name=proof_try,json=proofTry,proto3" json:"proof_try,omitempty" yaml:"proof_try"` + // proof of client state included in message + ProofClient []byte `protobuf:"bytes,7,opt,name=proof_client,json=proofClient,proto3" json:"proof_client,omitempty" yaml:"proof_client"` + // proof of client consensus state + ProofConsensus []byte `protobuf:"bytes,8,opt,name=proof_consensus,json=proofConsensus,proto3" json:"proof_consensus,omitempty" yaml:"proof_consensus"` + ConsensusHeight types1.Height `protobuf:"bytes,9,opt,name=consensus_height,json=consensusHeight,proto3" json:"consensus_height" yaml:"consensus_height"` + Signer string `protobuf:"bytes,10,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgConnectionOpenAck) Reset() { *m = MsgConnectionOpenAck{} } +func (m *MsgConnectionOpenAck) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenAck) ProtoMessage() {} +func (*MsgConnectionOpenAck) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{4} +} +func (m *MsgConnectionOpenAck) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenAck.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 *MsgConnectionOpenAck) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenAck.Merge(m, src) +} +func (m *MsgConnectionOpenAck) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenAck) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenAck.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenAck proto.InternalMessageInfo + +// MsgConnectionOpenAckResponse defines the Msg/ConnectionOpenAck response type. +type MsgConnectionOpenAckResponse struct { +} + +func (m *MsgConnectionOpenAckResponse) Reset() { *m = MsgConnectionOpenAckResponse{} } +func (m *MsgConnectionOpenAckResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenAckResponse) ProtoMessage() {} +func (*MsgConnectionOpenAckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{5} +} +func (m *MsgConnectionOpenAckResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenAckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenAckResponse.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 *MsgConnectionOpenAckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenAckResponse.Merge(m, src) +} +func (m *MsgConnectionOpenAckResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenAckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenAckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenAckResponse proto.InternalMessageInfo + +// MsgConnectionOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of connection state to OPEN on Chain A. +type MsgConnectionOpenConfirm struct { + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" yaml:"connection_id"` + // proof for the change of the connection state on Chain A: `INIT -> OPEN` + ProofAck []byte `protobuf:"bytes,2,opt,name=proof_ack,json=proofAck,proto3" json:"proof_ack,omitempty" yaml:"proof_ack"` + ProofHeight types1.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgConnectionOpenConfirm) Reset() { *m = MsgConnectionOpenConfirm{} } +func (m *MsgConnectionOpenConfirm) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenConfirm) ProtoMessage() {} +func (*MsgConnectionOpenConfirm) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{6} +} +func (m *MsgConnectionOpenConfirm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenConfirm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenConfirm.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 *MsgConnectionOpenConfirm) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenConfirm.Merge(m, src) +} +func (m *MsgConnectionOpenConfirm) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenConfirm) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenConfirm.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenConfirm proto.InternalMessageInfo + +// MsgConnectionOpenConfirmResponse defines the Msg/ConnectionOpenConfirm response type. +type MsgConnectionOpenConfirmResponse struct { +} + +func (m *MsgConnectionOpenConfirmResponse) Reset() { *m = MsgConnectionOpenConfirmResponse{} } +func (m *MsgConnectionOpenConfirmResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConnectionOpenConfirmResponse) ProtoMessage() {} +func (*MsgConnectionOpenConfirmResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5d00fde5fc97399e, []int{7} +} +func (m *MsgConnectionOpenConfirmResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConnectionOpenConfirmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConnectionOpenConfirmResponse.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 *MsgConnectionOpenConfirmResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConnectionOpenConfirmResponse.Merge(m, src) +} +func (m *MsgConnectionOpenConfirmResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConnectionOpenConfirmResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConnectionOpenConfirmResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConnectionOpenConfirmResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgConnectionOpenInit)(nil), "ibc.core.connection.v1.MsgConnectionOpenInit") + proto.RegisterType((*MsgConnectionOpenInitResponse)(nil), "ibc.core.connection.v1.MsgConnectionOpenInitResponse") + proto.RegisterType((*MsgConnectionOpenTry)(nil), "ibc.core.connection.v1.MsgConnectionOpenTry") + proto.RegisterType((*MsgConnectionOpenTryResponse)(nil), "ibc.core.connection.v1.MsgConnectionOpenTryResponse") + proto.RegisterType((*MsgConnectionOpenAck)(nil), "ibc.core.connection.v1.MsgConnectionOpenAck") + proto.RegisterType((*MsgConnectionOpenAckResponse)(nil), "ibc.core.connection.v1.MsgConnectionOpenAckResponse") + proto.RegisterType((*MsgConnectionOpenConfirm)(nil), "ibc.core.connection.v1.MsgConnectionOpenConfirm") + proto.RegisterType((*MsgConnectionOpenConfirmResponse)(nil), "ibc.core.connection.v1.MsgConnectionOpenConfirmResponse") +} + +func init() { proto.RegisterFile("ibc/core/connection/v1/tx.proto", fileDescriptor_5d00fde5fc97399e) } + +var fileDescriptor_5d00fde5fc97399e = []byte{ + // 921 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x31, 0x93, 0xdb, 0x44, + 0x14, 0xb6, 0xce, 0xbe, 0x3b, 0x7b, 0x6d, 0x48, 0xb2, 0xf8, 0xee, 0x84, 0x48, 0x2c, 0x47, 0x03, + 0x83, 0x0b, 0x4e, 0x8a, 0x93, 0x30, 0x03, 0x66, 0x28, 0x6c, 0x37, 0x5c, 0x11, 0xc8, 0x88, 0x00, + 0x33, 0x69, 0x3c, 0xb6, 0xbc, 0xd6, 0x69, 0x6c, 0x6b, 0x35, 0x5a, 0xd9, 0x44, 0xb4, 0x34, 0x0c, + 0x15, 0x0d, 0x7d, 0xfe, 0x03, 0x7f, 0x22, 0xe5, 0x95, 0x54, 0x1a, 0xb8, 0x6b, 0xa8, 0xd5, 0xd1, + 0x31, 0xda, 0x95, 0xe4, 0xb5, 0x2d, 0x0f, 0x36, 0x3e, 0x2a, 0xe9, 0xed, 0xfb, 0xde, 0x7b, 0xbb, + 0xef, 0x7d, 0xdf, 0xce, 0x02, 0xd9, 0x1a, 0x18, 0x9a, 0x81, 0x5d, 0xa4, 0x19, 0xd8, 0xb6, 0x91, + 0xe1, 0x59, 0xd8, 0xd6, 0xe6, 0x4d, 0xcd, 0x7b, 0xa5, 0x3a, 0x2e, 0xf6, 0x30, 0x3c, 0xb5, 0x06, + 0x86, 0x1a, 0x01, 0xd4, 0x05, 0x40, 0x9d, 0x37, 0xa5, 0xaa, 0x89, 0x4d, 0x4c, 0x21, 0x5a, 0xf4, + 0xc7, 0xd0, 0xd2, 0xbb, 0x26, 0xc6, 0xe6, 0x04, 0x69, 0xd4, 0x1a, 0xcc, 0x46, 0x5a, 0xdf, 0xf6, + 0x63, 0x17, 0x57, 0x69, 0x62, 0x21, 0xdb, 0x8b, 0xaa, 0xb0, 0xbf, 0x18, 0xf0, 0xe1, 0x86, 0xad, + 0x70, 0x75, 0x29, 0x50, 0xf9, 0xed, 0x00, 0x9c, 0x3c, 0x23, 0x66, 0x37, 0x5d, 0xff, 0xca, 0x41, + 0xf6, 0x85, 0x6d, 0x79, 0xb0, 0x09, 0x4a, 0x2c, 0x65, 0xcf, 0x1a, 0x8a, 0x42, 0x5d, 0x68, 0x94, + 0x3a, 0xd5, 0x30, 0x90, 0xef, 0xfa, 0xfd, 0xe9, 0xa4, 0xa5, 0xa4, 0x2e, 0x45, 0x2f, 0xb2, 0xff, + 0x8b, 0x21, 0xfc, 0x12, 0x54, 0x0c, 0x3c, 0xb3, 0x3d, 0xe4, 0x3a, 0x7d, 0xd7, 0xf3, 0xc5, 0x83, + 0xba, 0xd0, 0x28, 0x3f, 0x7e, 0x5f, 0xcd, 0x3e, 0xb6, 0xda, 0xe5, 0xb0, 0x9d, 0xc2, 0x9b, 0x40, + 0xce, 0xe9, 0x4b, 0xf1, 0xf0, 0x53, 0x70, 0x3c, 0x47, 0x2e, 0xb1, 0xb0, 0x2d, 0xe6, 0x69, 0x2a, + 0x79, 0x53, 0xaa, 0x6f, 0x19, 0x4c, 0x4f, 0xf0, 0xb0, 0x05, 0x2a, 0x43, 0x34, 0xe9, 0xfb, 0x3d, + 0x07, 0xb9, 0x16, 0x1e, 0x8a, 0x85, 0xba, 0xd0, 0x28, 0x74, 0xce, 0xc2, 0x40, 0x7e, 0x87, 0x1d, + 0x80, 0xf7, 0x2a, 0x7a, 0x99, 0x9a, 0xcf, 0xa9, 0x05, 0x4f, 0xc1, 0x11, 0xb1, 0x4c, 0x1b, 0xb9, + 0xe2, 0x61, 0x74, 0x6c, 0x3d, 0xb6, 0x5a, 0xc5, 0x9f, 0x5e, 0xcb, 0xb9, 0xbf, 0x5e, 0xcb, 0x39, + 0x45, 0x06, 0x0f, 0x32, 0x9b, 0xa6, 0x23, 0xe2, 0x60, 0x9b, 0x20, 0xe5, 0xd7, 0x63, 0x50, 0x5d, + 0x43, 0xbc, 0x70, 0xfd, 0xff, 0xd2, 0xd5, 0xef, 0xc0, 0xa9, 0xe3, 0xa2, 0xb9, 0x85, 0x67, 0xa4, + 0xb7, 0x38, 0x75, 0x14, 0x7f, 0x40, 0xe3, 0x1f, 0x86, 0x81, 0xfc, 0x80, 0xc5, 0x67, 0xe3, 0x14, + 0xbd, 0x9a, 0x38, 0x16, 0x1b, 0xba, 0x18, 0xc2, 0xe7, 0xa0, 0x12, 0x17, 0x24, 0x5e, 0xdf, 0x43, + 0x71, 0x8f, 0xab, 0x2a, 0xe3, 0x9d, 0x9a, 0xf0, 0x4e, 0x6d, 0xdb, 0x3e, 0xdf, 0x39, 0x3e, 0x46, + 0xd1, 0xcb, 0xcc, 0xfc, 0x3a, 0xb2, 0xd6, 0x08, 0x50, 0xd8, 0x93, 0x00, 0xab, 0x53, 0x3c, 0xdc, + 0x61, 0x8a, 0x73, 0x70, 0xc2, 0xe7, 0xea, 0xc5, 0xcc, 0x20, 0xe2, 0x51, 0x3d, 0xbf, 0x05, 0x95, + 0x3a, 0xf5, 0x30, 0x90, 0xef, 0xc7, 0x27, 0xce, 0xca, 0xa3, 0xe8, 0x55, 0x7e, 0x3d, 0x0e, 0x23, + 0xf0, 0x25, 0xa8, 0x38, 0x2e, 0xc6, 0xa3, 0xde, 0x25, 0xb2, 0xcc, 0x4b, 0x4f, 0x3c, 0xa6, 0x3d, + 0x90, 0xb8, 0x72, 0x4c, 0xa8, 0xf3, 0xa6, 0xfa, 0x05, 0x45, 0x74, 0xde, 0x8b, 0x4e, 0xbe, 0x38, + 0x13, 0x1f, 0xad, 0xe8, 0x65, 0x6a, 0x32, 0x24, 0x7c, 0x0a, 0x00, 0xf3, 0x5a, 0xb6, 0xe5, 0x89, + 0xc5, 0xba, 0xd0, 0xa8, 0x74, 0x4e, 0xc2, 0x40, 0xbe, 0xc7, 0x47, 0x46, 0x3e, 0x45, 0x2f, 0x51, + 0x83, 0x2a, 0xb9, 0x95, 0xec, 0x88, 0x55, 0x16, 0x4b, 0x34, 0xee, 0x6c, 0xb5, 0x22, 0xf3, 0x26, + 0x15, 0xbb, 0xd4, 0x82, 0x5d, 0x70, 0x27, 0xf6, 0x46, 0xbc, 0xb6, 0xc9, 0x8c, 0x88, 0x80, 0x86, + 0x4b, 0x61, 0x20, 0x9f, 0x2e, 0x85, 0x27, 0x00, 0x45, 0x7f, 0x9b, 0x65, 0x48, 0x16, 0xe0, 0x08, + 0xdc, 0x4d, 0xbd, 0x49, 0x5b, 0xca, 0xff, 0xda, 0x16, 0x39, 0x6e, 0xcb, 0x59, 0x32, 0x84, 0xe5, + 0x0c, 0x8a, 0x7e, 0x27, 0x5d, 0x8a, 0xdb, 0xb3, 0x10, 0x6e, 0x65, 0x83, 0x70, 0x6b, 0xe0, 0x7e, + 0x96, 0x2c, 0x53, 0xdd, 0xfe, 0x79, 0x98, 0xa1, 0xdb, 0xb6, 0x31, 0x86, 0x9f, 0x83, 0xb7, 0x96, + 0xb5, 0xc7, 0xb4, 0x2b, 0x86, 0x81, 0x5c, 0x4d, 0xf7, 0xc7, 0x4b, 0xae, 0x62, 0xf0, 0x52, 0x33, + 0x80, 0xb4, 0x44, 0xa2, 0x2c, 0x1d, 0x7f, 0x10, 0x06, 0xf2, 0xc3, 0x0c, 0xc2, 0xad, 0x24, 0x16, + 0x79, 0xe7, 0x92, 0x9e, 0xf7, 0xb8, 0x2e, 0x57, 0xaf, 0x82, 0xc2, 0xde, 0x57, 0xc1, 0xaa, 0x0c, + 0x0e, 0x6f, 0x51, 0x06, 0x4d, 0xc0, 0xd8, 0xdd, 0xf3, 0x5c, 0x5f, 0x3c, 0xa2, 0x74, 0xe4, 0x2e, + 0xd1, 0xd4, 0xa5, 0xe8, 0x45, 0xfa, 0x1f, 0xdd, 0xbb, 0xab, 0x1a, 0x38, 0xde, 0x4f, 0x03, 0xc5, + 0x5b, 0xd1, 0x40, 0xe9, 0x7f, 0xd5, 0x00, 0xd8, 0x41, 0x03, 0x6d, 0x63, 0x9c, 0x6a, 0xe0, 0xe7, + 0x03, 0x20, 0xae, 0x01, 0xba, 0xd8, 0x1e, 0x59, 0xee, 0x74, 0x5f, 0x1d, 0xa4, 0x93, 0xeb, 0x1b, + 0x63, 0x4a, 0xfb, 0x8c, 0xc9, 0xf5, 0x8d, 0x71, 0x32, 0xb9, 0x48, 0x79, 0xab, 0x44, 0xca, 0xdf, + 0x22, 0x91, 0x16, 0xcd, 0x2a, 0x6c, 0x68, 0x96, 0x02, 0xea, 0x9b, 0x7a, 0x91, 0x34, 0xec, 0xf1, + 0xdf, 0x79, 0x90, 0x7f, 0x46, 0x4c, 0xf8, 0x03, 0x80, 0x19, 0xef, 0xa8, 0xf3, 0x4d, 0x22, 0xcc, + 0x7c, 0x41, 0x48, 0x1f, 0xef, 0x04, 0x4f, 0xf6, 0x00, 0xbf, 0x07, 0xf7, 0xd6, 0x1f, 0x1b, 0x1f, + 0x6d, 0x9d, 0xeb, 0x85, 0xeb, 0x4b, 0x4f, 0x77, 0x41, 0x6f, 0x2e, 0x1c, 0xcd, 0x6c, 0xfb, 0xc2, + 0x6d, 0x63, 0xbc, 0x43, 0x61, 0x8e, 0xa6, 0xf0, 0x47, 0x01, 0x9c, 0x64, 0x73, 0xf4, 0xd1, 0xd6, + 0xf9, 0xe2, 0x08, 0xe9, 0x93, 0x5d, 0x23, 0x92, 0x5d, 0x74, 0xbe, 0x79, 0x73, 0x5d, 0x13, 0xae, + 0xae, 0x6b, 0xc2, 0x1f, 0xd7, 0x35, 0xe1, 0x97, 0x9b, 0x5a, 0xee, 0xea, 0xa6, 0x96, 0xfb, 0xfd, + 0xa6, 0x96, 0x7b, 0xf9, 0x99, 0x69, 0x79, 0x97, 0xb3, 0x81, 0x6a, 0xe0, 0xa9, 0x66, 0x60, 0x32, + 0xc5, 0x24, 0xfe, 0x9c, 0x93, 0xe1, 0x58, 0x7b, 0xa5, 0xa5, 0x2f, 0xf4, 0x47, 0x4f, 0xce, 0xb9, + 0x47, 0xba, 0xe7, 0x3b, 0x88, 0x0c, 0x8e, 0xe8, 0x8d, 0xfb, 0xe4, 0x9f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xc4, 0x4d, 0xc5, 0x58, 0x53, 0x0c, 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 { + // ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. + ConnectionOpenInit(ctx context.Context, in *MsgConnectionOpenInit, opts ...grpc.CallOption) (*MsgConnectionOpenInitResponse, error) + // ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. + ConnectionOpenTry(ctx context.Context, in *MsgConnectionOpenTry, opts ...grpc.CallOption) (*MsgConnectionOpenTryResponse, error) + // ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. + ConnectionOpenAck(ctx context.Context, in *MsgConnectionOpenAck, opts ...grpc.CallOption) (*MsgConnectionOpenAckResponse, error) + // ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. + ConnectionOpenConfirm(ctx context.Context, in *MsgConnectionOpenConfirm, opts ...grpc.CallOption) (*MsgConnectionOpenConfirmResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) ConnectionOpenInit(ctx context.Context, in *MsgConnectionOpenInit, opts ...grpc.CallOption) (*MsgConnectionOpenInitResponse, error) { + out := new(MsgConnectionOpenInitResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Msg/ConnectionOpenInit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ConnectionOpenTry(ctx context.Context, in *MsgConnectionOpenTry, opts ...grpc.CallOption) (*MsgConnectionOpenTryResponse, error) { + out := new(MsgConnectionOpenTryResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Msg/ConnectionOpenTry", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ConnectionOpenAck(ctx context.Context, in *MsgConnectionOpenAck, opts ...grpc.CallOption) (*MsgConnectionOpenAckResponse, error) { + out := new(MsgConnectionOpenAckResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Msg/ConnectionOpenAck", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ConnectionOpenConfirm(ctx context.Context, in *MsgConnectionOpenConfirm, opts ...grpc.CallOption) (*MsgConnectionOpenConfirmResponse, error) { + out := new(MsgConnectionOpenConfirmResponse) + err := c.cc.Invoke(ctx, "/ibc.core.connection.v1.Msg/ConnectionOpenConfirm", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. + ConnectionOpenInit(context.Context, *MsgConnectionOpenInit) (*MsgConnectionOpenInitResponse, error) + // ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. + ConnectionOpenTry(context.Context, *MsgConnectionOpenTry) (*MsgConnectionOpenTryResponse, error) + // ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. + ConnectionOpenAck(context.Context, *MsgConnectionOpenAck) (*MsgConnectionOpenAckResponse, error) + // ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. + ConnectionOpenConfirm(context.Context, *MsgConnectionOpenConfirm) (*MsgConnectionOpenConfirmResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) ConnectionOpenInit(ctx context.Context, req *MsgConnectionOpenInit) (*MsgConnectionOpenInitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionOpenInit not implemented") +} +func (*UnimplementedMsgServer) ConnectionOpenTry(ctx context.Context, req *MsgConnectionOpenTry) (*MsgConnectionOpenTryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionOpenTry not implemented") +} +func (*UnimplementedMsgServer) ConnectionOpenAck(ctx context.Context, req *MsgConnectionOpenAck) (*MsgConnectionOpenAckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionOpenAck not implemented") +} +func (*UnimplementedMsgServer) ConnectionOpenConfirm(ctx context.Context, req *MsgConnectionOpenConfirm) (*MsgConnectionOpenConfirmResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionOpenConfirm not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_ConnectionOpenInit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConnectionOpenInit) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConnectionOpenInit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Msg/ConnectionOpenInit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConnectionOpenInit(ctx, req.(*MsgConnectionOpenInit)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ConnectionOpenTry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConnectionOpenTry) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConnectionOpenTry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Msg/ConnectionOpenTry", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConnectionOpenTry(ctx, req.(*MsgConnectionOpenTry)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ConnectionOpenAck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConnectionOpenAck) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConnectionOpenAck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Msg/ConnectionOpenAck", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConnectionOpenAck(ctx, req.(*MsgConnectionOpenAck)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ConnectionOpenConfirm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConnectionOpenConfirm) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConnectionOpenConfirm(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.connection.v1.Msg/ConnectionOpenConfirm", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConnectionOpenConfirm(ctx, req.(*MsgConnectionOpenConfirm)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.connection.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ConnectionOpenInit", + Handler: _Msg_ConnectionOpenInit_Handler, + }, + { + MethodName: "ConnectionOpenTry", + Handler: _Msg_ConnectionOpenTry_Handler, + }, + { + MethodName: "ConnectionOpenAck", + Handler: _Msg_ConnectionOpenAck_Handler, + }, + { + MethodName: "ConnectionOpenConfirm", + Handler: _Msg_ConnectionOpenConfirm_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/connection/v1/tx.proto", +} + +func (m *MsgConnectionOpenInit) 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 *MsgConnectionOpenInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x2a + } + if m.DelayPeriod != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.DelayPeriod)) + i-- + dAtA[i] = 0x20 + } + if m.Version != nil { + { + size, err := m.Version.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenInitResponse) 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 *MsgConnectionOpenInitResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenInitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenTry) 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 *MsgConnectionOpenTry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x62 + } + { + size, err := m.ConsensusHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + if len(m.ProofConsensus) > 0 { + i -= len(m.ProofConsensus) + copy(dAtA[i:], m.ProofConsensus) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofConsensus))) + i-- + dAtA[i] = 0x52 + } + if len(m.ProofClient) > 0 { + i -= len(m.ProofClient) + copy(dAtA[i:], m.ProofClient) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofClient))) + i-- + dAtA[i] = 0x4a + } + if len(m.ProofInit) > 0 { + i -= len(m.ProofInit) + copy(dAtA[i:], m.ProofInit) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofInit))) + i-- + dAtA[i] = 0x42 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if len(m.CounterpartyVersions) > 0 { + for iNdEx := len(m.CounterpartyVersions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CounterpartyVersions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if m.DelayPeriod != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.DelayPeriod)) + i-- + dAtA[i] = 0x28 + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.PreviousConnectionId) > 0 { + i -= len(m.PreviousConnectionId) + copy(dAtA[i:], m.PreviousConnectionId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PreviousConnectionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenTryResponse) 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 *MsgConnectionOpenTryResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenTryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenAck) 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 *MsgConnectionOpenAck) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x52 + } + { + size, err := m.ConsensusHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + if len(m.ProofConsensus) > 0 { + i -= len(m.ProofConsensus) + copy(dAtA[i:], m.ProofConsensus) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofConsensus))) + i-- + dAtA[i] = 0x42 + } + if len(m.ProofClient) > 0 { + i -= len(m.ProofClient) + copy(dAtA[i:], m.ProofClient) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofClient))) + i-- + dAtA[i] = 0x3a + } + if len(m.ProofTry) > 0 { + i -= len(m.ProofTry) + copy(dAtA[i:], m.ProofTry) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofTry))) + i-- + dAtA[i] = 0x32 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Version != nil { + { + size, err := m.Version.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.CounterpartyConnectionId) > 0 { + i -= len(m.CounterpartyConnectionId) + copy(dAtA[i:], m.CounterpartyConnectionId) + i = encodeVarintTx(dAtA, i, uint64(len(m.CounterpartyConnectionId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenAckResponse) 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 *MsgConnectionOpenAckResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenAckResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenConfirm) 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 *MsgConnectionOpenConfirm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenConfirm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ProofAck) > 0 { + i -= len(m.ProofAck) + copy(dAtA[i:], m.ProofAck) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofAck))) + i-- + dAtA[i] = 0x12 + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConnectionOpenConfirmResponse) 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 *MsgConnectionOpenConfirmResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConnectionOpenConfirmResponse) 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 *MsgConnectionOpenInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Counterparty.Size() + n += 1 + l + sovTx(uint64(l)) + if m.Version != nil { + l = m.Version.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.DelayPeriod != 0 { + n += 1 + sovTx(uint64(m.DelayPeriod)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConnectionOpenInitResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgConnectionOpenTry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.PreviousConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = m.Counterparty.Size() + n += 1 + l + sovTx(uint64(l)) + if m.DelayPeriod != 0 { + n += 1 + sovTx(uint64(m.DelayPeriod)) + } + if len(m.CounterpartyVersions) > 0 { + for _, e := range m.CounterpartyVersions { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ProofInit) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofClient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofConsensus) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ConsensusHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConnectionOpenTryResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgConnectionOpenAck) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.CounterpartyConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Version != nil { + l = m.Version.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ProofTry) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofClient) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofConsensus) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ConsensusHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConnectionOpenAckResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgConnectionOpenConfirm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofAck) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConnectionOpenConfirmResponse) 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 *MsgConnectionOpenInit) 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: MsgConnectionOpenInit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenInit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", 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 := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version == nil { + m.Version = &Version{} + } + if err := m.Version.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DelayPeriod", wireType) + } + m.DelayPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DelayPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgConnectionOpenInitResponse) 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: MsgConnectionOpenInitResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenInitResponse: 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 *MsgConnectionOpenTry) 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: MsgConnectionOpenTry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenTry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreviousConnectionId", 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.PreviousConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", 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.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", 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 := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DelayPeriod", wireType) + } + m.DelayPeriod = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DelayPeriod |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyVersions", 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.CounterpartyVersions = append(m.CounterpartyVersions, &Version{}) + if err := m.CounterpartyVersions[len(m.CounterpartyVersions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofInit", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofInit = append(m.ProofInit[:0], dAtA[iNdEx:postIndex]...) + if m.ProofInit == nil { + m.ProofInit = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofClient", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofClient = append(m.ProofClient[:0], dAtA[iNdEx:postIndex]...) + if m.ProofClient == nil { + m.ProofClient = []byte{} + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofConsensus", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofConsensus = append(m.ProofConsensus[:0], dAtA[iNdEx:postIndex]...) + if m.ProofConsensus == nil { + m.ProofConsensus = []byte{} + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusHeight", 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 := m.ConsensusHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgConnectionOpenTryResponse) 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: MsgConnectionOpenTryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenTryResponse: 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 *MsgConnectionOpenAck) 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: MsgConnectionOpenAck: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenAck: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyConnectionId", 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.CounterpartyConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", 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.Version == nil { + m.Version = &Version{} + } + if err := m.Version.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", 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.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofTry", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofTry = append(m.ProofTry[:0], dAtA[iNdEx:postIndex]...) + if m.ProofTry == nil { + m.ProofTry = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofClient", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofClient = append(m.ProofClient[:0], dAtA[iNdEx:postIndex]...) + if m.ProofClient == nil { + m.ProofClient = []byte{} + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofConsensus", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofConsensus = append(m.ProofConsensus[:0], dAtA[iNdEx:postIndex]...) + if m.ProofConsensus == nil { + m.ProofConsensus = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusHeight", 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 := m.ConsensusHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgConnectionOpenAckResponse) 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: MsgConnectionOpenAckResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenAckResponse: 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 *MsgConnectionOpenConfirm) 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: MsgConnectionOpenConfirm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenConfirm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", 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.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofAck", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofAck = append(m.ProofAck[:0], dAtA[iNdEx:postIndex]...) + if m.ProofAck == nil { + m.ProofAck = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgConnectionOpenConfirmResponse) 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: MsgConnectionOpenConfirmResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConnectionOpenConfirmResponse: 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/ibc/core/03-connection/types/version.go b/x/ibc/core/03-connection/types/version.go new file mode 100644 index 000000000000..10c5b33d285c --- /dev/null +++ b/x/ibc/core/03-connection/types/version.go @@ -0,0 +1,220 @@ +package types + +import ( + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + // DefaultIBCVersion represents the latest supported version of IBC used + // in connection version negotiation. The current version supports only + // ORDERED and UNORDERED channels and requires at least one channel type + // to be agreed upon. + DefaultIBCVersion = NewVersion(DefaultIBCVersionIdentifier, []string{"ORDER_ORDERED", "ORDER_UNORDERED"}) + + // DefaultIBCVersionIdentifier is the IBC v1.0.0 protocol version identifier + DefaultIBCVersionIdentifier = "1" + + // AllowNilFeatureSet is a helper map to indicate if a specified version + // identifier is allowed to have a nil feature set. Any versions supported, + // but not included in the map default to not supporting nil feature sets. + allowNilFeatureSet = map[string]bool{ + DefaultIBCVersionIdentifier: false, + } +) + +var _ exported.Version = &Version{} + +// NewVersion returns a new instance of Version. +func NewVersion(identifier string, features []string) *Version { + return &Version{ + Identifier: identifier, + Features: features, + } +} + +// GetIdentifier implements the VersionI interface +func (version Version) GetIdentifier() string { + return version.Identifier +} + +// GetFeatures implements the VersionI interface +func (version Version) GetFeatures() []string { + return version.Features +} + +// ValidateVersion does basic validation of the version identifier and +// features. It unmarshals the version string into a Version object. +func ValidateVersion(version *Version) error { + if version == nil { + return sdkerrors.Wrap(ErrInvalidVersion, "version cannot be nil") + } + if strings.TrimSpace(version.Identifier) == "" { + return sdkerrors.Wrap(ErrInvalidVersion, "version identifier cannot be blank") + } + for i, feature := range version.Features { + if strings.TrimSpace(feature) == "" { + return sdkerrors.Wrapf(ErrInvalidVersion, "feature cannot be blank, index %d", i) + } + } + + return nil +} + +// VerifyProposedVersion verifies that the entire feature set in the +// proposed version is supported by this chain. If the feature set is +// empty it verifies that this is allowed for the specified version +// identifier. +func (version Version) VerifyProposedVersion(proposedVersion exported.Version) error { + if proposedVersion.GetIdentifier() != version.GetIdentifier() { + return sdkerrors.Wrapf( + ErrVersionNegotiationFailed, + "proposed version identifier does not equal supported version identifier (%s != %s)", proposedVersion.GetIdentifier(), version.GetIdentifier(), + ) + } + + if len(proposedVersion.GetFeatures()) == 0 && !allowNilFeatureSet[proposedVersion.GetIdentifier()] { + return sdkerrors.Wrapf( + ErrVersionNegotiationFailed, + "nil feature sets are not supported for version identifier (%s)", proposedVersion.GetIdentifier(), + ) + } + + for _, proposedFeature := range proposedVersion.GetFeatures() { + if !contains(proposedFeature, version.GetFeatures()) { + return sdkerrors.Wrapf( + ErrVersionNegotiationFailed, + "proposed feature (%s) is not a supported feature set (%s)", proposedFeature, version.GetFeatures(), + ) + } + } + + return nil +} + +// VerifySupportedFeature takes in a version and feature string and returns +// true if the feature is supported by the version and false otherwise. +func VerifySupportedFeature(version exported.Version, feature string) bool { + for _, f := range version.GetFeatures() { + if f == feature { + return true + } + } + return false +} + +// GetCompatibleVersions returns a descending ordered set of compatible IBC +// versions for the caller chain's connection end. The latest supported +// version should be first element and the set should descend to the oldest +// supported version. +func GetCompatibleVersions() []exported.Version { + return []exported.Version{DefaultIBCVersion} +} + +// IsSupportedVersion returns true if the proposed version has a matching version +// identifier and its entire feature set is supported or the version identifier +// supports an empty feature set. +func IsSupportedVersion(proposedVersion *Version) bool { + supportedVersion, found := FindSupportedVersion(proposedVersion, GetCompatibleVersions()) + if !found { + return false + } + + if err := supportedVersion.VerifyProposedVersion(proposedVersion); err != nil { + return false + } + + return true +} + +// FindSupportedVersion returns the version with a matching version identifier +// if it exists. The returned boolean is true if the version is found and +// false otherwise. +func FindSupportedVersion(version exported.Version, supportedVersions []exported.Version) (exported.Version, bool) { + for _, supportedVersion := range supportedVersions { + if version.GetIdentifier() == supportedVersion.GetIdentifier() { + return supportedVersion, true + } + } + return nil, false +} + +// PickVersion iterates over the descending ordered set of compatible IBC +// versions and selects the first version with a version identifier that is +// supported by the counterparty. The returned version contains a feature +// set with the intersection of the features supported by the source and +// counterparty chains. If the feature set intersection is nil and this is +// not allowed for the chosen version identifier then the search for a +// compatible version continues. This function is called in the ConnOpenTry +// handshake procedure. +// +// CONTRACT: PickVersion must only provide a version that is in the +// intersection of the supported versions and the counterparty versions. +func PickVersion(supportedVersions, counterpartyVersions []exported.Version) (*Version, error) { + for _, supportedVersion := range supportedVersions { + // check if the source version is supported by the counterparty + if counterpartyVersion, found := FindSupportedVersion(supportedVersion, counterpartyVersions); found { + featureSet := GetFeatureSetIntersection(supportedVersion.GetFeatures(), counterpartyVersion.GetFeatures()) + if len(featureSet) == 0 && !allowNilFeatureSet[supportedVersion.GetIdentifier()] { + continue + } + + return NewVersion(supportedVersion.GetIdentifier(), featureSet), nil + } + } + + return nil, sdkerrors.Wrapf( + ErrVersionNegotiationFailed, + "failed to find a matching counterparty version (%v) from the supported version list (%v)", counterpartyVersions, supportedVersions, + ) +} + +// GetFeatureSetIntersection returns the intersections of source feature set +// and the counterparty feature set. This is done by iterating over all the +// features in the source version and seeing if they exist in the feature +// set for the counterparty version. +func GetFeatureSetIntersection(sourceFeatureSet, counterpartyFeatureSet []string) (featureSet []string) { + for _, feature := range sourceFeatureSet { + if contains(feature, counterpartyFeatureSet) { + featureSet = append(featureSet, feature) + } + } + + return featureSet +} + +// ExportedVersionsToProto casts a slice of the Version interface to a slice +// of the Version proto definition. +func ExportedVersionsToProto(exportedVersions []exported.Version) []*Version { + versions := make([]*Version, len(exportedVersions)) + for i := range exportedVersions { + versions[i] = exportedVersions[i].(*Version) + } + + return versions +} + +// ProtoVersionsToExported converts a slice of the Version proto definition to +// the Version interface. +func ProtoVersionsToExported(versions []*Version) []exported.Version { + exportedVersions := make([]exported.Version, len(versions)) + for i := range versions { + exportedVersions[i] = versions[i] + } + + return exportedVersions +} + +// contains returns true if the provided string element exists within the +// string set. +func contains(elem string, set []string) bool { + for _, element := range set { + if elem == element { + return true + } + } + + return false +} diff --git a/x/ibc/core/03-connection/types/version_test.go b/x/ibc/core/03-connection/types/version_test.go new file mode 100644 index 000000000000..8f882dd32711 --- /dev/null +++ b/x/ibc/core/03-connection/types/version_test.go @@ -0,0 +1,167 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func TestValidateVersion(t *testing.T) { + testCases := []struct { + name string + version *types.Version + expPass bool + }{ + {"valid version", types.DefaultIBCVersion, true}, + {"valid empty feature set", types.NewVersion(types.DefaultIBCVersionIdentifier, []string{}), true}, + {"empty version identifier", types.NewVersion(" ", []string{"ORDER_UNORDERED"}), false}, + {"empty feature", types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_UNORDERED", " "}), false}, + } + + for i, tc := range testCases { + err := types.ValidateVersion(tc.version) + + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestIsSupportedVersion(t *testing.T) { + testCases := []struct { + name string + version *types.Version + expPass bool + }{ + { + "version is supported", + types.ExportedVersionsToProto(types.GetCompatibleVersions())[0], + true, + }, + { + "version is not supported", + &types.Version{}, + false, + }, + { + "version feature is not supported", + types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_DAG"}), + false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.expPass, types.IsSupportedVersion(tc.version)) + } +} + +func TestFindSupportedVersion(t *testing.T) { + testCases := []struct { + name string + version *types.Version + supportedVersions []exported.Version + expVersion *types.Version + expFound bool + }{ + {"valid supported version", types.DefaultIBCVersion, types.GetCompatibleVersions(), types.DefaultIBCVersion, true}, + {"empty (invalid) version", &types.Version{}, types.GetCompatibleVersions(), &types.Version{}, false}, + {"empty supported versions", types.DefaultIBCVersion, []exported.Version{}, &types.Version{}, false}, + {"desired version is last", types.DefaultIBCVersion, []exported.Version{types.NewVersion("1.1", nil), types.NewVersion("2", []string{"ORDER_UNORDERED"}), types.NewVersion("3", nil), types.DefaultIBCVersion}, types.DefaultIBCVersion, true}, + {"desired version identifier with different feature set", types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_DAG"}), types.GetCompatibleVersions(), types.DefaultIBCVersion, true}, + {"version not supported", types.NewVersion("2", []string{"ORDER_DAG"}), types.GetCompatibleVersions(), &types.Version{}, false}, + } + + for i, tc := range testCases { + version, found := types.FindSupportedVersion(tc.version, tc.supportedVersions) + if tc.expFound { + require.Equal(t, tc.expVersion.GetIdentifier(), version.GetIdentifier(), "test case %d: %s", i, tc.name) + require.True(t, found, "test case %d: %s", i, tc.name) + } else { + require.False(t, found, "test case: %s", tc.name) + require.Nil(t, version, "test case: %s", tc.name) + } + } +} + +func TestPickVersion(t *testing.T) { + testCases := []struct { + name string + supportedVersions []exported.Version + counterpartyVersions []exported.Version + expVer *types.Version + expPass bool + }{ + {"valid default ibc version", types.GetCompatibleVersions(), types.GetCompatibleVersions(), types.DefaultIBCVersion, true}, + {"valid version in counterparty versions", types.GetCompatibleVersions(), []exported.Version{types.NewVersion("version1", nil), types.NewVersion("2.0.0", []string{"ORDER_UNORDERED-ZK"}), types.DefaultIBCVersion}, types.DefaultIBCVersion, true}, + {"valid identifier match but empty feature set not allowed", types.GetCompatibleVersions(), []exported.Version{types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"DAG", "ORDERED-ZK", "UNORDERED-zk]"})}, types.NewVersion(types.DefaultIBCVersionIdentifier, nil), false}, + {"empty counterparty versions", types.GetCompatibleVersions(), []exported.Version{}, &types.Version{}, false}, + {"non-matching counterparty versions", types.GetCompatibleVersions(), []exported.Version{types.NewVersion("2.0.0", nil)}, &types.Version{}, false}, + {"non-matching counterparty versions (uses ordered channels only) contained in supported versions (uses unordered channels only)", []exported.Version{types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_UNORDERED"})}, []exported.Version{types.NewVersion(types.DefaultIBCVersionIdentifier, []string{"ORDER_ORDERED"})}, &types.Version{}, false}, + } + + for i, tc := range testCases { + version, err := types.PickVersion(tc.supportedVersions, tc.counterpartyVersions) + + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + var emptyVersion *types.Version + require.Equal(t, emptyVersion, version, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestVerifyProposedVersion(t *testing.T) { + testCases := []struct { + name string + proposedVersion *types.Version + supportedVersion *types.Version + expPass bool + }{ + {"entire feature set supported", types.DefaultIBCVersion, types.NewVersion("1", []string{"ORDER_ORDERED", "ORDER_UNORDERED", "ORDER_DAG"}), true}, + {"empty feature sets not supported", types.NewVersion("1", []string{}), types.DefaultIBCVersion, false}, + {"one feature missing", types.DefaultIBCVersion, types.NewVersion("1", []string{"ORDER_UNORDERED", "ORDER_DAG"}), false}, + {"both features missing", types.DefaultIBCVersion, types.NewVersion("1", []string{"ORDER_DAG"}), false}, + {"identifiers do not match", types.NewVersion("2", []string{"ORDER_UNORDERED", "ORDER_ORDERED"}), types.DefaultIBCVersion, false}, + } + + for i, tc := range testCases { + err := tc.supportedVersion.VerifyProposedVersion(tc.proposedVersion) + + if tc.expPass { + require.NoError(t, err, "test case %d: %s", i, tc.name) + } else { + require.Error(t, err, "test case %d: %s", i, tc.name) + } + } + +} + +func TestVerifySupportedFeature(t *testing.T) { + nilFeatures := types.NewVersion(types.DefaultIBCVersionIdentifier, nil) + + testCases := []struct { + name string + version *types.Version + feature string + expPass bool + }{ + {"check ORDERED supported", ibctesting.ConnectionVersion, "ORDER_ORDERED", true}, + {"check UNORDERED supported", ibctesting.ConnectionVersion, "ORDER_UNORDERED", true}, + {"check DAG unsupported", ibctesting.ConnectionVersion, "ORDER_DAG", false}, + {"check empty feature set returns false", nilFeatures, "ORDER_ORDERED", false}, + } + + for i, tc := range testCases { + supported := types.VerifySupportedFeature(tc.version, tc.feature) + + require.Equal(t, tc.expPass, supported, "test case %d: %s", i, tc.name) + } +} diff --git a/x/ibc/core/04-channel/client/cli/cli.go b/x/ibc/core/04-channel/client/cli/cli.go new file mode 100644 index 000000000000..baf386fecaf3 --- /dev/null +++ b/x/ibc/core/04-channel/client/cli/cli.go @@ -0,0 +1,58 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// GetQueryCmd returns the query commands for IBC channels +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC channel query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + queryCmd.AddCommand( + GetCmdQueryChannels(), + GetCmdQueryChannel(), + GetCmdQueryConnectionChannels(), + GetCmdQueryChannelClientState(), + GetCmdQueryPacketCommitment(), + GetCmdQueryPacketCommitments(), + GetCmdQueryPacketReceipt(), + GetCmdQueryPacketAcknowledgement(), + GetCmdQueryUnreceivedPackets(), + GetCmdQueryUnreceivedAcks(), + GetCmdQueryNextSequenceReceive(), + // TODO: next sequence Send ? + ) + + return queryCmd +} + +// NewTxCmd returns a CLI command handler for all x/ibc channel transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.SubModuleName, + Short: "IBC channel transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewChannelOpenInitCmd(), + NewChannelOpenTryCmd(), + NewChannelOpenAckCmd(), + NewChannelOpenConfirmCmd(), + NewChannelCloseInitCmd(), + NewChannelCloseConfirmCmd(), + ) + + return txCmd +} diff --git a/x/ibc/core/04-channel/client/cli/query.go b/x/ibc/core/04-channel/client/cli/query.go new file mode 100644 index 000000000000..5d059c08cf94 --- /dev/null +++ b/x/ibc/core/04-channel/client/cli/query.go @@ -0,0 +1,458 @@ +package cli + +import ( + "context" + "fmt" + "strconv" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/utils" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + flagSequences = "sequences" +) + +// GetCmdQueryChannels defines the command to query all the channels ends +// that this chain mantains. +func GetCmdQueryChannels() *cobra.Command { + cmd := &cobra.Command{ + Use: "channels", + Short: "Query all channels", + Long: "Query all channels from a chain", + Example: fmt.Sprintf("%s query %s %s channels", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryChannelsRequest{ + Pagination: pageReq, + } + + res, err := queryClient.Channels(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "channels") + + return cmd +} + +// GetCmdQueryChannel defines the command to query a channel end +func GetCmdQueryChannel() *cobra.Command { + cmd := &cobra.Command{ + Use: "end [port-id] [channel-id]", + Short: "Query a channel end", + Long: "Query an IBC channel end from a port and channel identifiers", + Example: fmt.Sprintf( + "%s query %s %s end [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + channelRes, err := utils.QueryChannel(clientCtx, portID, channelID, prove) + if err != nil { + return err + } + + return clientCtx.PrintProto(channelRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryConnectionChannels defines the command to query all the channels associated with a +// connection +func GetCmdQueryConnectionChannels() *cobra.Command { + cmd := &cobra.Command{ + Use: "connections [connection-id]", + Short: "Query all channels associated with a connection", + Long: "Query all channels associated with a connection", + Example: fmt.Sprintf("%s query %s %s connections [connection-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryConnectionChannelsRequest{ + Connection: args[0], + Pagination: pageReq, + } + + res, err := queryClient.ConnectionChannels(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "channels associated with a connection") + + return cmd +} + +// GetCmdQueryChannelClientState defines the command to query a client state from a channel +func GetCmdQueryChannelClientState() *cobra.Command { + cmd := &cobra.Command{ + Use: "client-state [port-id] [channel-id]", + Short: "Query the client state associated with a channel", + Long: "Query the client state associated with a channel, by providing its port and channel identifiers.", + Example: fmt.Sprintf("%s query ibc channel client-state [port-id] [channel-id]", version.AppName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + + res, err := utils.QueryChannelClientState(clientCtx, portID, channelID, false) + if err != nil { + return err + } + + return clientCtx.PrintProto(res.IdentifiedClientState) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryPacketCommitments defines the command to query all packet commitments associated with +// a channel +func GetCmdQueryPacketCommitments() *cobra.Command { + cmd := &cobra.Command{ + Use: "packet-commitments [port-id] [channel-id]", + Short: "Query all packet commitments associated with a channel", + Long: "Query all packet commitments associated with a channel", + Example: fmt.Sprintf("%s query %s %s packet-commitments [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + req := &types.QueryPacketCommitmentsRequest{ + PortId: args[0], + ChannelId: args[1], + Pagination: pageReq, + } + + res, err := queryClient.PacketCommitments(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "packet commitments associated with a channel") + + return cmd +} + +// GetCmdQueryPacketCommitment defines the command to query a packet commitment +func GetCmdQueryPacketCommitment() *cobra.Command { + cmd := &cobra.Command{ + Use: "packet-commitment [port-id] [channel-id] [sequence]", + Short: "Query a packet commitment", + Long: "Query a packet commitment", + Example: fmt.Sprintf( + "%s query %s %s packet-commitment [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + res, err := utils.QueryPacketCommitment(clientCtx, portID, channelID, seq, prove) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryPacketReceipt defines the command to query a packet receipt +func GetCmdQueryPacketReceipt() *cobra.Command { + cmd := &cobra.Command{ + Use: "packet-receipt [port-id] [channel-id] [sequence]", + Short: "Query a packet receipt", + Long: "Query a packet receipt", + Example: fmt.Sprintf( + "%s query %s %s packet-receipt [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + res, err := utils.QueryPacketReceipt(clientCtx, portID, channelID, seq, prove) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryPacketAcknowledgement defines the command to query a packet acknowledgement +func GetCmdQueryPacketAcknowledgement() *cobra.Command { + cmd := &cobra.Command{ + Use: "packet-ack [port-id] [channel-id] [sequence]", + Short: "Query a packet acknowledgement", + Long: "Query a packet acknowledgement", + Example: fmt.Sprintf( + "%s query %s %s packet-ack [port-id] [channel-id] [sequence]", version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + seq, err := strconv.ParseUint(args[2], 10, 64) + if err != nil { + return err + } + + res, err := utils.QueryPacketAcknowledgement(clientCtx, portID, channelID, seq, prove) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryUnreceivedPackets defines the command to query all the unreceived +// packets on the receiving chain +func GetCmdQueryUnreceivedPackets() *cobra.Command { + cmd := &cobra.Command{ + Use: "unreceived-packets [port-id] [channel-id]", + Short: "Query all the unreceived packets associated with a channel", + Long: `Determine if a packet, given a list of packet commitment sequences, is unreceived. + +The return value represents: +- Unreceived packet commitments: no acknowledgement exists on receiving chain for the given packet commitment sequence on sending chain. +`, + Example: fmt.Sprintf("%s query %s %s unreceived-packets [port-id] [channel-id] --sequences=1,2,3", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + seqSlice, err := cmd.Flags().GetInt64Slice(flagSequences) + if err != nil { + return err + } + + seqs := make([]uint64, len(seqSlice)) + for i := range seqSlice { + seqs[i] = uint64(seqSlice[i]) + } + + req := &types.QueryUnreceivedPacketsRequest{ + PortId: args[0], + ChannelId: args[1], + PacketCommitmentSequences: seqs, + } + + res, err := queryClient.UnreceivedPackets(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().Int64Slice(flagSequences, []int64{}, "comma separated list of packet sequence numbers") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryUnreceivedAcks defines the command to query all the unreceived acks on the original sending chain +func GetCmdQueryUnreceivedAcks() *cobra.Command { + cmd := &cobra.Command{ + Use: "unreceived-acks [port-id] [channel-id]", + Short: "Query all the unreceived acks associated with a channel", + Long: `Given a list of acknowledgement sequences from counterparty, determine if an ack on the counterparty chain has been received on the executing chain. + +The return value represents: +- Unreceived packet acknowledgement: packet commitment exists on original sending (executing) chain and ack exists on receiving chain. +`, + Example: fmt.Sprintf("%s query %s %s unreceived-acks [port-id] [channel-id] --sequences=1,2,3", version.AppName, host.ModuleName, types.SubModuleName), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + seqSlice, err := cmd.Flags().GetInt64Slice(flagSequences) + if err != nil { + return err + } + + seqs := make([]uint64, len(seqSlice)) + for i := range seqSlice { + seqs[i] = uint64(seqSlice[i]) + } + + req := &types.QueryUnreceivedAcksRequest{ + PortId: args[0], + ChannelId: args[1], + PacketAckSequences: seqs, + } + + res, err := queryClient.UnreceivedAcks(context.Background(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + cmd.Flags().Int64Slice(flagSequences, []int64{}, "comma separated list of packet sequence numbers") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQueryNextSequenceReceive defines the command to query a next receive sequence for a given channel +func GetCmdQueryNextSequenceReceive() *cobra.Command { + cmd := &cobra.Command{ + Use: "next-sequence-receive [port-id] [channel-id]", + Short: "Query a next receive sequence", + Long: "Query the next receive sequence for a given channel", + Example: fmt.Sprintf( + "%s query %s %s next-sequence-receive [port-id] [channel-id]", version.AppName, host.ModuleName, types.SubModuleName, + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + prove, _ := cmd.Flags().GetBool(flags.FlagProve) + + sequenceRes, err := utils.QueryNextSequenceReceive(clientCtx, portID, channelID, prove) + if err != nil { + return err + } + + clientCtx = clientCtx.WithHeight(int64(sequenceRes.ProofHeight.RevisionHeight)) + return clientCtx.PrintProto(sequenceRes) + }, + } + + cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results") + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/ibc/core/04-channel/client/cli/tx.go b/x/ibc/core/04-channel/client/cli/tx.go new file mode 100644 index 000000000000..21aafba19460 --- /dev/null +++ b/x/ibc/core/04-channel/client/cli/tx.go @@ -0,0 +1,269 @@ +package cli + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + 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" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// IBC Channel flags +const ( + FlagOrdered = "ordered" + FlagIBCVersion = "ibc-version" +) + +// NewChannelOpenInitCmd returns the command to create a MsgChannelOpenInit transaction +func NewChannelOpenInitCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-init [port-id] [counterparty-port-id] [connection-hops]", + Short: "Creates and sends a ChannelOpenInit message", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + counterpartyPortID := args[1] + hops := strings.Split(args[2], "/") + order := channelOrder(cmd.Flags()) + version, _ := cmd.Flags().GetString(FlagIBCVersion) + + msg := types.NewMsgChannelOpenInit( + portID, version, order, hops, + counterpartyPortID, clientCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().Bool(FlagOrdered, true, "Pass flag for opening ordered channels") + cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewChannelOpenTryCmd returns the command to create a MsgChannelOpenTry transaction +func NewChannelOpenTryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-try [port-id] [channel-id] [counterparty-port-id] [counterparty-channel-id] [connection-hops] [/path/to/proof_init.json] [proof-height]", + Short: "Creates and sends a ChannelOpenTry message", + Args: cobra.ExactArgs(7), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + counterpartyPortID := args[2] + counterpartyChannelID := args[3] + hops := strings.Split(args[4], "/") + order := channelOrder(cmd.Flags()) + + // TODO: Differentiate between channel and counterparty versions. + version, _ := cmd.Flags().GetString(FlagIBCVersion) + + proofInit, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[5]) + if err != nil { + return err + } + + proofHeight, err := clienttypes.ParseHeight(args[6]) + if err != nil { + return err + } + + msg := types.NewMsgChannelOpenTry( + portID, channelID, version, order, hops, + counterpartyPortID, counterpartyChannelID, version, + proofInit, proofHeight, clientCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().Bool(FlagOrdered, true, "Pass flag for opening ordered channels") + cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewChannelOpenAckCmd returns the command to create a MsgChannelOpenAck transaction +func NewChannelOpenAckCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-ack [port-id] [channel-id] [counterparty-channel-id] [/path/to/proof_try.json] [proof-height]", + Short: "Creates and sends a ChannelOpenAck message", + Args: cobra.ExactArgs(5), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + counterpartyChannelID := args[2] + + // TODO: Differentiate between channel and counterparty versions. + version, _ := cmd.Flags().GetString(FlagIBCVersion) + + proofTry, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[3]) + if err != nil { + return err + } + + proofHeight, err := clienttypes.ParseHeight(args[4]) + if err != nil { + return err + } + + msg := types.NewMsgChannelOpenAck( + portID, channelID, counterpartyChannelID, version, proofTry, proofHeight, clientCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + cmd.Flags().String(FlagIBCVersion, ibctransfertypes.Version, "IBC application version") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewChannelOpenConfirmCmd returns the command to create a MsgChannelOpenConfirm transaction +func NewChannelOpenConfirmCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "open-confirm [port-id] [channel-id] [/path/to/proof_ack.json] [proof-height]", + Short: "Creates and sends a ChannelOpenConfirm message", + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + + proofAck, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + proofHeight, err := clienttypes.ParseHeight(args[3]) + if err != nil { + return err + } + + msg := types.NewMsgChannelOpenConfirm( + portID, channelID, proofAck, proofHeight, clientCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewChannelCloseInitCmd returns the command to create a MsgChannelCloseInit transaction +func NewChannelCloseInitCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "close-init [port-id] [channel-id]", + Short: "Creates and sends a ChannelCloseInit message", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + + msg := types.NewMsgChannelCloseInit(portID, channelID, clientCtx.GetFromAddress()) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +// NewChannelCloseConfirmCmd returns the command to create a MsgChannelCloseConfirm transaction +func NewChannelCloseConfirmCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "close-confirm [port-id] [channel-id] [/path/to/proof_init.json] [proof-height]", + Short: "Creates and sends a ChannelCloseConfirm message", + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + portID := args[0] + channelID := args[1] + + proofInit, err := connectionutils.ParseProof(clientCtx.LegacyAmino, args[2]) + if err != nil { + return err + } + + proofHeight, err := clienttypes.ParseHeight(args[3]) + if err != nil { + return err + } + + msg := types.NewMsgChannelCloseConfirm( + portID, channelID, proofInit, proofHeight, clientCtx.GetFromAddress(), + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} + +func channelOrder(fs *pflag.FlagSet) types.Order { + if ordered, _ := fs.GetBool(FlagOrdered); ordered { + return types.ORDERED + } + + return types.UNORDERED +} diff --git a/x/ibc/core/04-channel/client/utils/utils.go b/x/ibc/core/04-channel/client/utils/utils.go new file mode 100644 index 000000000000..167e05d048e9 --- /dev/null +++ b/x/ibc/core/04-channel/client/utils/utils.go @@ -0,0 +1,301 @@ +package utils + +import ( + "context" + "encoding/binary" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clientutils "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/client/utils" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/client" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// QueryChannel returns a channel end. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryChannel( + clientCtx client.Context, portID, channelID string, prove bool, +) (*types.QueryChannelResponse, error) { + if prove { + return queryChannelABCI(clientCtx, portID, channelID) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryChannelRequest{ + PortId: portID, + ChannelId: channelID, + } + + return queryClient.Channel(context.Background(), req) +} + +func queryChannelABCI(clientCtx client.Context, portID, channelID string) (*types.QueryChannelResponse, error) { + key := host.ChannelKey(portID, channelID) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if channel exists + if len(value) == 0 { + return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + var channel types.Channel + if err := cdc.UnmarshalBinaryBare(value, &channel); err != nil { + return nil, err + } + + return types.NewQueryChannelResponse(channel, proofBz, proofHeight), nil +} + +// QueryChannelClientState returns the ClientState of a channel end. If +// prove is true, it performs an ABCI store query in order to retrieve the +// merkle proof. Otherwise, it uses the gRPC query client. +func QueryChannelClientState( + clientCtx client.Context, portID, channelID string, prove bool, +) (*types.QueryChannelClientStateResponse, error) { + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryChannelClientStateRequest{ + PortId: portID, + ChannelId: channelID, + } + + res, err := queryClient.ChannelClientState(context.Background(), req) + if err != nil { + return nil, err + } + + if prove { + clientStateRes, err := clientutils.QueryClientStateABCI(clientCtx, res.IdentifiedClientState.ClientId) + if err != nil { + return nil, err + } + + // use client state returned from ABCI query in case query height differs + identifiedClientState := clienttypes.IdentifiedClientState{ + ClientId: res.IdentifiedClientState.ClientId, + ClientState: clientStateRes.ClientState, + } + res = types.NewQueryChannelClientStateResponse(identifiedClientState, clientStateRes.Proof, clientStateRes.ProofHeight) + } + + return res, nil +} + +// QueryChannelConsensusState returns the ConsensusState of a channel end. If +// prove is true, it performs an ABCI store query in order to retrieve the +// merkle proof. Otherwise, it uses the gRPC query client. +func QueryChannelConsensusState( + clientCtx client.Context, portID, channelID string, height clienttypes.Height, prove bool, +) (*types.QueryChannelConsensusStateResponse, error) { + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryChannelConsensusStateRequest{ + PortId: portID, + ChannelId: channelID, + RevisionNumber: height.RevisionNumber, + RevisionHeight: height.RevisionHeight, + } + + res, err := queryClient.ChannelConsensusState(context.Background(), req) + if err != nil { + return nil, err + } + + if prove { + consensusStateRes, err := clientutils.QueryConsensusStateABCI(clientCtx, res.ClientId, height) + if err != nil { + return nil, err + } + + res = types.NewQueryChannelConsensusStateResponse(res.ClientId, consensusStateRes.ConsensusState, height, consensusStateRes.Proof, consensusStateRes.ProofHeight) + } + + return res, nil +} + +// QueryLatestConsensusState uses the channel Querier to return the +// latest ConsensusState given the source port ID and source channel ID. +func QueryLatestConsensusState( + clientCtx client.Context, portID, channelID string, +) (exported.ConsensusState, clienttypes.Height, clienttypes.Height, error) { + clientRes, err := QueryChannelClientState(clientCtx, portID, channelID, false) + if err != nil { + return nil, clienttypes.Height{}, clienttypes.Height{}, err + } + + var clientState exported.ClientState + if err := clientCtx.InterfaceRegistry.UnpackAny(clientRes.IdentifiedClientState.ClientState, &clientState); err != nil { + return nil, clienttypes.Height{}, clienttypes.Height{}, err + } + + clientHeight, ok := clientState.GetLatestHeight().(clienttypes.Height) + if !ok { + return nil, clienttypes.Height{}, clienttypes.Height{}, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "invalid height type. expected type: %T, got: %T", + clienttypes.Height{}, clientHeight) + } + res, err := QueryChannelConsensusState(clientCtx, portID, channelID, clientHeight, false) + if err != nil { + return nil, clienttypes.Height{}, clienttypes.Height{}, err + } + + var consensusState exported.ConsensusState + if err := clientCtx.InterfaceRegistry.UnpackAny(res.ConsensusState, &consensusState); err != nil { + return nil, clienttypes.Height{}, clienttypes.Height{}, err + } + + return consensusState, clientHeight, res.ProofHeight, nil +} + +// QueryNextSequenceReceive returns the next sequence receive. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryNextSequenceReceive( + clientCtx client.Context, portID, channelID string, prove bool, +) (*types.QueryNextSequenceReceiveResponse, error) { + if prove { + return queryNextSequenceRecvABCI(clientCtx, portID, channelID) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryNextSequenceReceiveRequest{ + PortId: portID, + ChannelId: channelID, + } + + return queryClient.NextSequenceReceive(context.Background(), req) +} + +func queryNextSequenceRecvABCI(clientCtx client.Context, portID, channelID string) (*types.QueryNextSequenceReceiveResponse, error) { + key := host.NextSequenceRecvKey(portID, channelID) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if next sequence receive exists + if len(value) == 0 { + return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID) + } + + sequence := binary.BigEndian.Uint64(value) + + return types.NewQueryNextSequenceReceiveResponse(sequence, proofBz, proofHeight), nil +} + +// QueryPacketCommitment returns a packet commitment. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryPacketCommitment( + clientCtx client.Context, portID, channelID string, + sequence uint64, prove bool, +) (*types.QueryPacketCommitmentResponse, error) { + if prove { + return queryPacketCommitmentABCI(clientCtx, portID, channelID, sequence) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryPacketCommitmentRequest{ + PortId: portID, + ChannelId: channelID, + Sequence: sequence, + } + + return queryClient.PacketCommitment(context.Background(), req) +} + +func queryPacketCommitmentABCI( + clientCtx client.Context, portID, channelID string, sequence uint64, +) (*types.QueryPacketCommitmentResponse, error) { + key := host.PacketCommitmentKey(portID, channelID, sequence) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + // check if packet commitment exists + if len(value) == 0 { + return nil, sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) + } + + return types.NewQueryPacketCommitmentResponse(value, proofBz, proofHeight), nil +} + +// QueryPacketReceipt returns data about a packet receipt. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client. +func QueryPacketReceipt( + clientCtx client.Context, portID, channelID string, + sequence uint64, prove bool, +) (*types.QueryPacketReceiptResponse, error) { + if prove { + return queryPacketReceiptABCI(clientCtx, portID, channelID, sequence) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryPacketReceiptRequest{ + PortId: portID, + ChannelId: channelID, + Sequence: sequence, + } + + return queryClient.PacketReceipt(context.Background(), req) +} + +func queryPacketReceiptABCI( + clientCtx client.Context, portID, channelID string, sequence uint64, +) (*types.QueryPacketReceiptResponse, error) { + key := host.PacketReceiptKey(portID, channelID, sequence) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + return types.NewQueryPacketReceiptResponse(value != nil, proofBz, proofHeight), nil +} + +// QueryPacketAcknowledgement returns the data about a packet acknowledgement. +// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise, +// it uses the gRPC query client +func QueryPacketAcknowledgement(clientCtx client.Context, portID, channelID string, sequence uint64, prove bool) (*types.QueryPacketAcknowledgementResponse, error) { + if prove { + return queryPacketAcknowledgementABCI(clientCtx, portID, channelID, sequence) + } + + queryClient := types.NewQueryClient(clientCtx) + req := &types.QueryPacketAcknowledgementRequest{ + PortId: portID, + ChannelId: channelID, + Sequence: sequence, + } + + return queryClient.PacketAcknowledgement(context.Background(), req) +} + +func queryPacketAcknowledgementABCI(clientCtx client.Context, portID, channelID string, sequence uint64) (*types.QueryPacketAcknowledgementResponse, error) { + key := host.PacketAcknowledgementKey(portID, channelID, sequence) + + value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key) + if err != nil { + return nil, err + } + + if len(value) == 0 { + return nil, sdkerrors.Wrapf(types.ErrInvalidAcknowledgement, "portID (%s), channelID (%s), sequence (%d)", portID, channelID, sequence) + } + + return types.NewQueryPacketAcknowledgementResponse(value, proofBz, proofHeight), nil +} diff --git a/x/ibc/core/04-channel/genesis.go b/x/ibc/core/04-channel/genesis.go new file mode 100644 index 000000000000..07fad47d77b2 --- /dev/null +++ b/x/ibc/core/04-channel/genesis.go @@ -0,0 +1,48 @@ +package channel + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// InitGenesis initializes the ibc channel submodule's state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, gs types.GenesisState) { + for _, channel := range gs.Channels { + ch := types.NewChannel(channel.State, channel.Ordering, channel.Counterparty, channel.ConnectionHops, channel.Version) + k.SetChannel(ctx, channel.PortId, channel.ChannelId, ch) + } + for _, ack := range gs.Acknowledgements { + k.SetPacketAcknowledgement(ctx, ack.PortId, ack.ChannelId, ack.Sequence, ack.Data) + } + for _, commitment := range gs.Commitments { + k.SetPacketCommitment(ctx, commitment.PortId, commitment.ChannelId, commitment.Sequence, commitment.Data) + } + for _, receipt := range gs.Receipts { + k.SetPacketReceipt(ctx, receipt.PortId, receipt.ChannelId, receipt.Sequence) + } + for _, ss := range gs.SendSequences { + k.SetNextSequenceSend(ctx, ss.PortId, ss.ChannelId, ss.Sequence) + } + for _, rs := range gs.RecvSequences { + k.SetNextSequenceRecv(ctx, rs.PortId, rs.ChannelId, rs.Sequence) + } + for _, as := range gs.AckSequences { + k.SetNextSequenceAck(ctx, as.PortId, as.ChannelId, as.Sequence) + } + k.SetNextChannelSequence(ctx, gs.NextChannelSequence) +} + +// ExportGenesis returns the ibc channel submodule's exported genesis. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState { + return types.GenesisState{ + Channels: k.GetAllChannels(ctx), + Acknowledgements: k.GetAllPacketAcks(ctx), + Commitments: k.GetAllPacketCommitments(ctx), + Receipts: k.GetAllPacketReceipts(ctx), + SendSequences: k.GetAllPacketSendSeqs(ctx), + RecvSequences: k.GetAllPacketRecvSeqs(ctx), + AckSequences: k.GetAllPacketAckSeqs(ctx), + } +} diff --git a/x/ibc/core/04-channel/handler.go b/x/ibc/core/04-channel/handler.go new file mode 100644 index 000000000000..375c35263ec1 --- /dev/null +++ b/x/ibc/core/04-channel/handler.go @@ -0,0 +1,186 @@ +package channel + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// HandleMsgChannelOpenInit defines the sdk.Handler for MsgChannelOpenInit +func HandleMsgChannelOpenInit(ctx sdk.Context, k keeper.Keeper, portCap *capabilitytypes.Capability, msg *types.MsgChannelOpenInit) (*sdk.Result, string, *capabilitytypes.Capability, error) { + channelID, capKey, err := k.ChanOpenInit( + ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, + portCap, msg.Channel.Counterparty, msg.Channel.Version, + ) + if err != nil { + return nil, "", nil, sdkerrors.Wrap(err, "channel handshake open init failed") + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelOpenInit, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, channelID), + sdk.NewAttribute(types.AttributeCounterpartyPortID, msg.Channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, msg.Channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, msg.Channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, channelID, capKey, nil +} + +// HandleMsgChannelOpenTry defines the sdk.Handler for MsgChannelOpenTry +func HandleMsgChannelOpenTry(ctx sdk.Context, k keeper.Keeper, portCap *capabilitytypes.Capability, msg *types.MsgChannelOpenTry) (*sdk.Result, string, *capabilitytypes.Capability, error) { + channelID, capKey, err := k.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, msg.PreviousChannelId, + portCap, msg.Channel.Counterparty, msg.Channel.Version, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight, + ) + if err != nil { + return nil, "", nil, sdkerrors.Wrap(err, "channel handshake open try failed") + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelOpenTry, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, channelID), + sdk.NewAttribute(types.AttributeCounterpartyPortID, msg.Channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, msg.Channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, msg.Channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, channelID, capKey, nil +} + +// HandleMsgChannelOpenAck defines the sdk.Handler for MsgChannelOpenAck +func HandleMsgChannelOpenAck(ctx sdk.Context, k keeper.Keeper, channelCap *capabilitytypes.Capability, msg *types.MsgChannelOpenAck) (*sdk.Result, error) { + err := k.ChanOpenAck( + ctx, msg.PortId, msg.ChannelId, channelCap, msg.CounterpartyVersion, msg.CounterpartyChannelId, msg.ProofTry, msg.ProofHeight, + ) + if err != nil { + return nil, sdkerrors.Wrap(err, "channel handshake open ack failed") + } + + channel, _ := k.GetChannel(ctx, msg.PortId, msg.ChannelId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelOpenAck, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, msg.ChannelId), + sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} + +// HandleMsgChannelOpenConfirm defines the sdk.Handler for MsgChannelOpenConfirm +func HandleMsgChannelOpenConfirm(ctx sdk.Context, k keeper.Keeper, channelCap *capabilitytypes.Capability, msg *types.MsgChannelOpenConfirm) (*sdk.Result, error) { + err := k.ChanOpenConfirm(ctx, msg.PortId, msg.ChannelId, channelCap, msg.ProofAck, msg.ProofHeight) + if err != nil { + return nil, sdkerrors.Wrap(err, "channel handshake open confirm failed") + } + + channel, _ := k.GetChannel(ctx, msg.PortId, msg.ChannelId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelOpenConfirm, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, msg.ChannelId), + sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} + +// HandleMsgChannelCloseInit defines the sdk.Handler for MsgChannelCloseInit +func HandleMsgChannelCloseInit(ctx sdk.Context, k keeper.Keeper, channelCap *capabilitytypes.Capability, msg *types.MsgChannelCloseInit) (*sdk.Result, error) { + err := k.ChanCloseInit(ctx, msg.PortId, msg.ChannelId, channelCap) + if err != nil { + return nil, sdkerrors.Wrap(err, "channel handshake close init failed") + } + + channel, _ := k.GetChannel(ctx, msg.PortId, msg.ChannelId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelCloseInit, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, msg.ChannelId), + sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} + +// HandleMsgChannelCloseConfirm defines the sdk.Handler for MsgChannelCloseConfirm +func HandleMsgChannelCloseConfirm(ctx sdk.Context, k keeper.Keeper, channelCap *capabilitytypes.Capability, msg *types.MsgChannelCloseConfirm) (*sdk.Result, error) { + err := k.ChanCloseConfirm(ctx, msg.PortId, msg.ChannelId, channelCap, msg.ProofInit, msg.ProofHeight) + if err != nil { + return nil, sdkerrors.Wrap(err, "channel handshake close confirm failed") + } + + channel, _ := k.GetChannel(ctx, msg.PortId, msg.ChannelId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeChannelCloseConfirm, + sdk.NewAttribute(types.AttributeKeyPortID, msg.PortId), + sdk.NewAttribute(types.AttributeKeyChannelID, msg.ChannelId), + sdk.NewAttribute(types.AttributeCounterpartyPortID, channel.Counterparty.PortId), + sdk.NewAttribute(types.AttributeCounterpartyChannelID, channel.Counterparty.ChannelId), + sdk.NewAttribute(types.AttributeKeyConnectionID, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return &sdk.Result{ + Events: ctx.EventManager().Events().ToABCIEvents(), + }, nil +} diff --git a/x/ibc/core/04-channel/keeper/grpc_query.go b/x/ibc/core/04-channel/keeper/grpc_query.go new file mode 100644 index 000000000000..30df0a33ace5 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/grpc_query.go @@ -0,0 +1,486 @@ +package keeper + +import ( + "context" + "strconv" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/query" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +var _ types.QueryServer = (*Keeper)(nil) + +// Channel implements the Query/Channel gRPC method +func (q Keeper) Channel(c context.Context, req *types.QueryChannelRequest) (*types.QueryChannelResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + channel, found := q.GetChannel(ctx, req.PortId, req.ChannelId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + ) + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryChannelResponse(channel, nil, selfHeight), nil +} + +// Channels implements the Query/Channels gRPC method +func (q Keeper) Channels(c context.Context, req *types.QueryChannelsRequest) (*types.QueryChannelsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + + channels := []*types.IdentifiedChannel{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyChannelEndPrefix)) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + var result types.Channel + if err := q.cdc.UnmarshalBinaryBare(value, &result); err != nil { + return err + } + + portID, channelID, err := host.ParseChannelPath(string(key)) + if err != nil { + return err + } + + identifiedChannel := types.NewIdentifiedChannel(portID, channelID, result) + channels = append(channels, &identifiedChannel) + return nil + }) + + if err != nil { + return nil, err + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryChannelsResponse{ + Channels: channels, + Pagination: pageRes, + Height: selfHeight, + }, nil +} + +// ConnectionChannels implements the Query/ConnectionChannels gRPC method +func (q Keeper) ConnectionChannels(c context.Context, req *types.QueryConnectionChannelsRequest) (*types.QueryConnectionChannelsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := host.ConnectionIdentifierValidator(req.Connection); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + ctx := sdk.UnwrapSDKContext(c) + + channels := []*types.IdentifiedChannel{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyChannelEndPrefix)) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + var result types.Channel + if err := q.cdc.UnmarshalBinaryBare(value, &result); err != nil { + return err + } + + // ignore channel and continue to the next item if the connection is + // different than the requested one + if result.ConnectionHops[0] != req.Connection { + return nil + } + + portID, channelID, err := host.ParseChannelPath(string(key)) + if err != nil { + return err + } + + identifiedChannel := types.NewIdentifiedChannel(portID, channelID, result) + channels = append(channels, &identifiedChannel) + return nil + }) + + if err != nil { + return nil, err + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryConnectionChannelsResponse{ + Channels: channels, + Pagination: pageRes, + Height: selfHeight, + }, nil +} + +// ChannelClientState implements the Query/ChannelClientState gRPC method +func (q Keeper) ChannelClientState(c context.Context, req *types.QueryChannelClientStateRequest) (*types.QueryChannelClientStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + clientID, clientState, err := q.GetChannelClientState(ctx, req.PortId, req.ChannelId) + if err != nil { + return nil, status.Error(codes.NotFound, err.Error()) + } + + identifiedClientState := clienttypes.NewIdentifiedClientState(clientID, clientState) + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryChannelClientStateResponse(identifiedClientState, nil, selfHeight), nil +} + +// ChannelConsensusState implements the Query/ChannelConsensusState gRPC method +func (q Keeper) ChannelConsensusState(c context.Context, req *types.QueryChannelConsensusStateRequest) (*types.QueryChannelConsensusStateResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + channel, found := q.GetChannel(ctx, req.PortId, req.ChannelId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + ) + } + + connection, found := q.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]).Error(), + ) + } + + consHeight := clienttypes.NewHeight(req.RevisionNumber, req.RevisionHeight) + consensusState, found := q.clientKeeper.GetClientConsensusState(ctx, connection.ClientId, consHeight) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(), + ) + } + + anyConsensusState, err := clienttypes.PackConsensusState(consensusState) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryChannelConsensusStateResponse(connection.ClientId, anyConsensusState, consHeight, nil, selfHeight), nil +} + +// PacketCommitment implements the Query/PacketCommitment gRPC method +func (q Keeper) PacketCommitment(c context.Context, req *types.QueryPacketCommitmentRequest) (*types.QueryPacketCommitmentResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + if req.Sequence == 0 { + return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0") + } + + ctx := sdk.UnwrapSDKContext(c) + + commitmentBz := q.GetPacketCommitment(ctx, req.PortId, req.ChannelId, req.Sequence) + if len(commitmentBz) == 0 { + return nil, status.Error(codes.NotFound, "packet commitment hash not found") + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryPacketCommitmentResponse(commitmentBz, nil, selfHeight), nil +} + +// PacketCommitments implements the Query/PacketCommitments gRPC method +func (q Keeper) PacketCommitments(c context.Context, req *types.QueryPacketCommitmentsRequest) (*types.QueryPacketCommitmentsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + commitments := []*types.PacketState{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.PacketCommitmentPrefixPath(req.PortId, req.ChannelId))) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + keySplit := strings.Split(string(key), "/") + + sequence, err := strconv.ParseUint(keySplit[len(keySplit)-1], 10, 64) + if err != nil { + return err + } + + commitment := types.NewPacketState(req.PortId, req.ChannelId, sequence, value) + commitments = append(commitments, &commitment) + return nil + }) + + if err != nil { + return nil, err + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryPacketCommitmentsResponse{ + Commitments: commitments, + Pagination: pageRes, + Height: selfHeight, + }, nil +} + +// PacketReceipt implements the Query/PacketReceipt gRPC method +func (q Keeper) PacketReceipt(c context.Context, req *types.QueryPacketReceiptRequest) (*types.QueryPacketReceiptResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + if req.Sequence == 0 { + return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0") + } + + ctx := sdk.UnwrapSDKContext(c) + + _, recvd := q.GetPacketReceipt(ctx, req.PortId, req.ChannelId, req.Sequence) + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryPacketReceiptResponse(recvd, nil, selfHeight), nil +} + +// PacketAcknowledgement implements the Query/PacketAcknowledgement gRPC method +func (q Keeper) PacketAcknowledgement(c context.Context, req *types.QueryPacketAcknowledgementRequest) (*types.QueryPacketAcknowledgementResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + if req.Sequence == 0 { + return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0") + } + + ctx := sdk.UnwrapSDKContext(c) + + acknowledgementBz, found := q.GetPacketAcknowledgement(ctx, req.PortId, req.ChannelId, req.Sequence) + if !found || len(acknowledgementBz) == 0 { + return nil, status.Error(codes.NotFound, "packet acknowledgement hash not found") + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryPacketAcknowledgementResponse(acknowledgementBz, nil, selfHeight), nil +} + +// PacketAcknowledgements implements the Query/PacketAcknowledgements gRPC method +func (q Keeper) PacketAcknowledgements(c context.Context, req *types.QueryPacketAcknowledgementsRequest) (*types.QueryPacketAcknowledgementsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + acks := []*types.PacketState{} + store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.PacketAcknowledgementPrefixPath(req.PortId, req.ChannelId))) + + pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error { + keySplit := strings.Split(string(key), "/") + + sequence, err := strconv.ParseUint(keySplit[len(keySplit)-1], 10, 64) + if err != nil { + return err + } + + ack := types.NewPacketState(req.PortId, req.ChannelId, sequence, value) + acks = append(acks, &ack) + return nil + }) + + if err != nil { + return nil, err + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryPacketAcknowledgementsResponse{ + Acknowledgements: acks, + Pagination: pageRes, + Height: selfHeight, + }, nil +} + +// UnreceivedPackets implements the Query/UnreceivedPackets gRPC method. Given +// a list of counterparty packet commitments, the querier checks if the packet +// has already been received by checking if a receipt exists on this +// chain for the packet sequence. All packets that haven't been received yet +// are returned in the response +// Usage: To use this method correctly, first query all packet commitments on +// the sending chain using the Query/PacketCommitments gRPC method. +// Then input the returned sequences into the QueryUnreceivedPacketsRequest +// and send the request to this Query/UnreceivedPackets on the **receiving** +// chain. This gRPC method will then return the list of packet sequences that +// are yet to be received on the receiving chain. +// +// NOTE: The querier makes the assumption that the provided list of packet +// commitments is correct and will not function properly if the list +// is not up to date. Ideally the query height should equal the latest height +// on the counterparty's client which represents this chain. +func (q Keeper) UnreceivedPackets(c context.Context, req *types.QueryUnreceivedPacketsRequest) (*types.QueryUnreceivedPacketsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + var unreceivedSequences = []uint64{} + + for i, seq := range req.PacketCommitmentSequences { + if seq == 0 { + return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i) + } + + // if packet receipt exists on the receiving chain, then packet has already been received + if _, found := q.GetPacketReceipt(ctx, req.PortId, req.ChannelId, seq); !found { + unreceivedSequences = append(unreceivedSequences, seq) + } + + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryUnreceivedPacketsResponse{ + Sequences: unreceivedSequences, + Height: selfHeight, + }, nil +} + +// UnreceivedAcks implements the Query/UnreceivedAcks gRPC method. Given +// a list of counterparty packet acknowledgements, the querier checks if the packet +// has already been received by checking if the packet commitment still exists on this +// chain (original sender) for the packet sequence. +// All acknowledgmeents that haven't been received yet are returned in the response. +// Usage: To use this method correctly, first query all packet acknowledgements on +// the original receiving chain (ie the chain that wrote the acks) using the Query/PacketAcknowledgements gRPC method. +// Then input the returned sequences into the QueryUnreceivedAcksRequest +// and send the request to this Query/UnreceivedAcks on the **original sending** +// chain. This gRPC method will then return the list of packet sequences whose +// acknowledgements are already written on the receiving chain but haven't yet +// been received back to the sending chain. +// +// NOTE: The querier makes the assumption that the provided list of packet +// acknowledgements is correct and will not function properly if the list +// is not up to date. Ideally the query height should equal the latest height +// on the counterparty's client which represents this chain. +func (q Keeper) UnreceivedAcks(c context.Context, req *types.QueryUnreceivedAcksRequest) (*types.QueryUnreceivedAcksResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + + var unreceivedSequences = []uint64{} + + for i, seq := range req.PacketAckSequences { + if seq == 0 { + return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i) + } + + // if packet commitment still exists on the original sending chain, then packet ack has not been received + // since processing the ack will delete the packet commitment + if commitment := q.GetPacketCommitment(ctx, req.PortId, req.ChannelId, seq); len(commitment) != 0 { + unreceivedSequences = append(unreceivedSequences, seq) + } + + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return &types.QueryUnreceivedAcksResponse{ + Sequences: unreceivedSequences, + Height: selfHeight, + }, nil +} + +// NextSequenceReceive implements the Query/NextSequenceReceive gRPC method +func (q Keeper) NextSequenceReceive(c context.Context, req *types.QueryNextSequenceReceiveRequest) (*types.QueryNextSequenceReceiveResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + sequence, found := q.GetNextSequenceRecv(ctx, req.PortId, req.ChannelId) + if !found { + return nil, status.Error( + codes.NotFound, + sdkerrors.Wrapf(types.ErrSequenceReceiveNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(), + ) + } + + selfHeight := clienttypes.GetSelfHeight(ctx) + return types.NewQueryNextSequenceReceiveResponse(sequence, nil, selfHeight), nil +} + +func validategRPCRequest(portID, channelID string) error { + if err := host.PortIdentifierValidator(portID); err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + + if err := host.ChannelIdentifierValidator(channelID); err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + + return nil +} diff --git a/x/ibc/core/04-channel/keeper/grpc_query_test.go b/x/ibc/core/04-channel/keeper/grpc_query_test.go new file mode 100644 index 000000000000..689c241c7b8b --- /dev/null +++ b/x/ibc/core/04-channel/keeper/grpc_query_test.go @@ -0,0 +1,1376 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *KeeperTestSuite) TestQueryChannel() { + var ( + req *types.QueryChannelRequest + expChannel types.Channel + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryChannelRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryChannelRequest{ + PortId: "test-port-id", + ChannelId: "", + } + }, + false, + }, + {"channel not found", + func() { + req = &types.QueryChannelRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "success", + func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + // init channel + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + expChannel = suite.chainA.GetChannel(channelA) + + req = &types.QueryChannelRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.Channel(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(&expChannel, res.Channel) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryChannels() { + var ( + req *types.QueryChannelsRequest + expChannels = []*types.IdentifiedChannel{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "empty pagination", + func() { + req = &types.QueryChannelsRequest{} + }, + true, + }, + { + "success", + func() { + _, _, connA0, connB0, testchannel0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // channel0 on first connection on chainA + counterparty0 := types.Counterparty{ + PortId: connB0.Channels[0].PortID, + ChannelId: connB0.Channels[0].ID, + } + + // channel1 is second channel on first connection on chainA + testchannel1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA0, connB0, types.ORDERED) + counterparty1 := types.Counterparty{ + PortId: connB0.Channels[1].PortID, + ChannelId: connB0.Channels[1].ID, + } + + channel0 := types.NewChannel( + types.OPEN, types.UNORDERED, + counterparty0, []string{connA0.ID}, testchannel0.Version, + ) + channel1 := types.NewChannel( + types.OPEN, types.ORDERED, + counterparty1, []string{connA0.ID}, testchannel1.Version, + ) + + idCh0 := types.NewIdentifiedChannel(testchannel0.PortID, testchannel0.ID, channel0) + idCh1 := types.NewIdentifiedChannel(testchannel1.PortID, testchannel1.ID, channel1) + + expChannels = []*types.IdentifiedChannel{&idCh0, &idCh1} + + req = &types.QueryChannelsRequest{ + Pagination: &query.PageRequest{ + Key: nil, + Limit: 2, + 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.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.Channels(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expChannels, res.Channels) + suite.Require().Equal(len(expChannels), int(res.Pagination.Total)) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryConnectionChannels() { + var ( + req *types.QueryConnectionChannelsRequest + expChannels = []*types.IdentifiedChannel{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid connection ID", + func() { + req = &types.QueryConnectionChannelsRequest{ + Connection: "", + } + }, + false, + }, + { + "success", + func() { + _, _, connA0, connB0, testchannel0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // channel0 on first connection on chainA + counterparty0 := types.Counterparty{ + PortId: connB0.Channels[0].PortID, + ChannelId: connB0.Channels[0].ID, + } + + // channel1 is second channel on first connection on chainA + testchannel1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA0, connB0, types.ORDERED) + counterparty1 := types.Counterparty{ + PortId: connB0.Channels[1].PortID, + ChannelId: connB0.Channels[1].ID, + } + + channel0 := types.NewChannel( + types.OPEN, types.UNORDERED, + counterparty0, []string{connA0.ID}, testchannel0.Version, + ) + channel1 := types.NewChannel( + types.OPEN, types.ORDERED, + counterparty1, []string{connA0.ID}, testchannel1.Version, + ) + + idCh0 := types.NewIdentifiedChannel(testchannel0.PortID, testchannel0.ID, channel0) + idCh1 := types.NewIdentifiedChannel(testchannel1.PortID, testchannel1.ID, channel1) + + expChannels = []*types.IdentifiedChannel{&idCh0, &idCh1} + + req = &types.QueryConnectionChannelsRequest{ + Connection: connA0.ID, + Pagination: &query.PageRequest{ + Key: nil, + Limit: 2, + CountTotal: true, + }, + } + }, + true, + }, + { + "success, empty response", + func() { + suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expChannels = []*types.IdentifiedChannel{} + req = &types.QueryConnectionChannelsRequest{ + Connection: "externalConnID", + Pagination: &query.PageRequest{ + Key: nil, + Limit: 2, + CountTotal: false, + }, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ConnectionChannels(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expChannels, res.Channels) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryChannelClientState() { + var ( + req *types.QueryChannelClientStateRequest + expIdentifiedClientState clienttypes.IdentifiedClientState + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryChannelClientStateRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryChannelClientStateRequest{ + PortId: "test-port-id", + ChannelId: "", + } + }, + false, + }, + { + "channel not found", + func() { + req = &types.QueryChannelClientStateRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "connection not found", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + channel := suite.chainA.GetChannel(channelA) + // update channel to reference a connection that does not exist + channel.ConnectionHops[0] = "doesnotexist" + + // set connection hops to wrong connection ID + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + + req = &types.QueryChannelClientStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + } + }, false, + }, + { + "client state for channel's connection not found", + func() { + _, _, connA, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + // set connection to empty so clientID is empty + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connectiontypes.ConnectionEnd{}) + + req = &types.QueryChannelClientStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + } + }, false, + }, + { + "success", + func() { + clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + // init channel + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + expClientState := suite.chainA.GetClientState(clientA) + expIdentifiedClientState = clienttypes.NewIdentifiedClientState(clientA, expClientState) + + req = &types.QueryChannelClientStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ChannelClientState(ctx, req) + + if tc.expPass { + 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) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryChannelConsensusState() { + var ( + req *types.QueryChannelConsensusStateRequest + expConsensusState exported.ConsensusState + expClientID string + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryChannelConsensusStateRequest{ + PortId: "", + ChannelId: "test-channel-id", + RevisionNumber: 0, + RevisionHeight: 1, + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryChannelConsensusStateRequest{ + PortId: "test-port-id", + ChannelId: "", + RevisionNumber: 0, + RevisionHeight: 1, + } + }, + false, + }, + { + "channel not found", + func() { + req = &types.QueryChannelConsensusStateRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + RevisionNumber: 0, + RevisionHeight: 1, + } + }, + false, + }, + { + "connection not found", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + channel := suite.chainA.GetChannel(channelA) + // update channel to reference a connection that does not exist + channel.ConnectionHops[0] = "doesnotexist" + + // set connection hops to wrong connection ID + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + + req = &types.QueryChannelConsensusStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + RevisionNumber: 0, + RevisionHeight: 1, + } + }, false, + }, + { + "consensus state for channel's connection not found", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + req = &types.QueryChannelConsensusStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + RevisionNumber: 0, + RevisionHeight: uint64(suite.chainA.GetContext().BlockHeight()), // use current height + } + }, false, + }, + { + "success", + func() { + clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + // init channel + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + clientState := suite.chainA.GetClientState(clientA) + expConsensusState, _ = suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) + suite.Require().NotNil(expConsensusState) + expClientID = clientA + + req = &types.QueryChannelConsensusStateRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + RevisionNumber: clientState.GetLatestHeight().GetRevisionNumber(), + RevisionHeight: clientState.GetLatestHeight().GetRevisionHeight(), + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.ChannelConsensusState(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState) + 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) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryPacketCommitment() { + var ( + req *types.QueryPacketCommitmentRequest + expCommitment []byte + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryPacketCommitmentRequest{ + PortId: "", + ChannelId: "test-channel-id", + Sequence: 0, + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryPacketCommitmentRequest{ + PortId: "test-port-id", + ChannelId: "", + Sequence: 0, + } + }, + false, + }, + {"invalid sequence", + func() { + req = &types.QueryPacketCommitmentRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Sequence: 0, + } + }, + false, + }, + {"channel not found", + func() { + req = &types.QueryPacketCommitmentRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Sequence: 1, + } + }, + false, + }, + { + "success", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expCommitment = []byte("hash") + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, expCommitment) + + req = &types.QueryPacketCommitmentRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Sequence: 1, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.PacketCommitment(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expCommitment, res.Commitment) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryPacketCommitments() { + var ( + req *types.QueryPacketCommitmentsRequest + expCommitments = []*types.PacketState{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid ID", + func() { + req = &types.QueryPacketCommitmentsRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "success, empty res", + func() { + expCommitments = []*types.PacketState{} + + req = &types.QueryPacketCommitmentsRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Pagination: &query.PageRequest{ + Key: nil, + Limit: 2, + CountTotal: true, + }, + } + }, + true, + }, + { + "success", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + expCommitments = make([]*types.PacketState, 9) + + for i := uint64(0); i < 9; i++ { + commitment := types.NewPacketState(channelA.PortID, channelA.ID, i, []byte(fmt.Sprintf("hash_%d", i))) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), commitment.PortId, commitment.ChannelId, commitment.Sequence, commitment.Data) + expCommitments[i] = &commitment + } + + req = &types.QueryPacketCommitmentsRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Pagination: &query.PageRequest{ + Key: nil, + Limit: 11, + 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.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.PacketCommitments(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expCommitments, res.Commitments) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryPacketReceipt() { + var ( + req *types.QueryPacketReceiptRequest + expReceived bool + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryPacketReceiptRequest{ + PortId: "", + ChannelId: "test-channel-id", + Sequence: 1, + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryPacketReceiptRequest{ + PortId: "test-port-id", + ChannelId: "", + Sequence: 1, + } + }, + false, + }, + {"invalid sequence", + func() { + req = &types.QueryPacketReceiptRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Sequence: 0, + } + }, + false, + }, + { + "success: receipt not found", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1) + + req = &types.QueryPacketReceiptRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Sequence: 3, + } + expReceived = false + }, + true, + }, + { + "success: receipt found", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1) + + req = &types.QueryPacketReceiptRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Sequence: 1, + } + expReceived = true + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.PacketReceipt(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expReceived, res.Received) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryPacketAcknowledgement() { + var ( + req *types.QueryPacketAcknowledgementRequest + expAck []byte + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryPacketAcknowledgementRequest{ + PortId: "", + ChannelId: "test-channel-id", + Sequence: 0, + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryPacketAcknowledgementRequest{ + PortId: "test-port-id", + ChannelId: "", + Sequence: 0, + } + }, + false, + }, + {"invalid sequence", + func() { + req = &types.QueryPacketAcknowledgementRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Sequence: 0, + } + }, + false, + }, + {"channel not found", + func() { + req = &types.QueryPacketAcknowledgementRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Sequence: 1, + } + }, + false, + }, + { + "success", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expAck = []byte("hash") + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, expAck) + + req = &types.QueryPacketAcknowledgementRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Sequence: 1, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.PacketAcknowledgement(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expAck, res.Acknowledgement) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryPacketAcknowledgements() { + var ( + req *types.QueryPacketAcknowledgementsRequest + expAcknowledgements = []*types.PacketState{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid ID", + func() { + req = &types.QueryPacketAcknowledgementsRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "success, empty res", + func() { + expAcknowledgements = []*types.PacketState{} + + req = &types.QueryPacketAcknowledgementsRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + Pagination: &query.PageRequest{ + Key: nil, + Limit: 2, + CountTotal: true, + }, + } + }, + true, + }, + { + "success", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + expAcknowledgements = make([]*types.PacketState, 9) + + for i := uint64(0); i < 9; i++ { + ack := types.NewPacketState(channelA.PortID, channelA.ID, i, []byte(fmt.Sprintf("hash_%d", i))) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainA.GetContext(), ack.PortId, ack.ChannelId, ack.Sequence, ack.Data) + expAcknowledgements[i] = &ack + } + + req = &types.QueryPacketAcknowledgementsRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + Pagination: &query.PageRequest{ + Key: nil, + Limit: 11, + 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.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.PacketAcknowledgements(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expAcknowledgements, res.Acknowledgements) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryUnreceivedPackets() { + var ( + req *types.QueryUnreceivedPacketsRequest + expSeq = []uint64{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryUnreceivedPacketsRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryUnreceivedPacketsRequest{ + PortId: "test-port-id", + ChannelId: "", + } + }, + false, + }, + { + "invalid seq", + func() { + req = &types.QueryUnreceivedPacketsRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + PacketCommitmentSequences: []uint64{0}, + } + }, + false, + }, + { + "basic success unreceived packet commitments", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + // no ack exists + + expSeq = []uint64{1} + req = &types.QueryUnreceivedPacketsRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketCommitmentSequences: []uint64{1}, + } + }, + true, + }, + { + "basic success unreceived packet commitments, nothing to relay", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1) + + expSeq = []uint64{} + req = &types.QueryUnreceivedPacketsRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketCommitmentSequences: []uint64{1}, + } + }, + true, + }, + { + "success multiple unreceived packet commitments", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expSeq = []uint64{} // reset + packetCommitments := []uint64{} + + // set packet receipt for every other sequence + for seq := uint64(1); seq < 10; seq++ { + packetCommitments = append(packetCommitments, seq) + + if seq%2 == 0 { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq) + } else { + expSeq = append(expSeq, seq) + } + } + + req = &types.QueryUnreceivedPacketsRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketCommitmentSequences: packetCommitments, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.UnreceivedPackets(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expSeq, res.Sequences) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryUnreceivedAcks() { + var ( + req *types.QueryUnreceivedAcksRequest + expSeq = []uint64{} + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryUnreceivedAcksRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryUnreceivedAcksRequest{ + PortId: "test-port-id", + ChannelId: "", + } + }, + false, + }, + { + "invalid seq", + func() { + req = &types.QueryUnreceivedAcksRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + PacketAckSequences: []uint64{0}, + } + }, + false, + }, + { + "basic success unreceived packet acks", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 1, []byte("commitment")) + + expSeq = []uint64{1} + req = &types.QueryUnreceivedAcksRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketAckSequences: []uint64{1}, + } + }, + true, + }, + { + "basic success unreceived packet acknowledgements, nothing to relay", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + expSeq = []uint64{} + req = &types.QueryUnreceivedAcksRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketAckSequences: []uint64{1}, + } + }, + true, + }, + { + "success multiple unreceived packet acknowledgements", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expSeq = []uint64{} // reset + packetAcks := []uint64{} + + // set packet commitment for every other sequence + for seq := uint64(1); seq < 10; seq++ { + packetAcks = append(packetAcks, seq) + + if seq%2 == 0 { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, seq, []byte("commitement")) + expSeq = append(expSeq, seq) + } + } + + req = &types.QueryUnreceivedAcksRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + PacketAckSequences: packetAcks, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.UnreceivedAcks(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expSeq, res.Sequences) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestQueryNextSequenceReceive() { + var ( + req *types.QueryNextSequenceReceiveRequest + expSeq uint64 + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = nil + }, + false, + }, + { + "invalid port ID", + func() { + req = &types.QueryNextSequenceReceiveRequest{ + PortId: "", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "invalid channel ID", + func() { + req = &types.QueryNextSequenceReceiveRequest{ + PortId: "test-port-id", + ChannelId: "", + } + }, + false, + }, + {"channel not found", + func() { + req = &types.QueryNextSequenceReceiveRequest{ + PortId: "test-port-id", + ChannelId: "test-channel-id", + } + }, + false, + }, + { + "success", + func() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + expSeq = 1 + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(suite.chainA.GetContext(), channelA.PortID, channelA.ID, expSeq) + + req = &types.QueryNextSequenceReceiveRequest{ + PortId: channelA.PortID, + ChannelId: channelA.ID, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.chainA.GetContext()) + + res, err := suite.chainA.QueryServer.NextSequenceReceive(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expSeq, res.NextSequenceReceive) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/core/04-channel/keeper/handshake.go b/x/ibc/core/04-channel/keeper/handshake.go new file mode 100644 index 000000000000..b7cff480c965 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/handshake.go @@ -0,0 +1,496 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CounterpartyHops returns the connection hops of the counterparty channel. +// The counterparty hops are stored in the inverse order as the channel's. +// NOTE: Since connectionHops only supports single connection channels for now, +// this function requires that connection hops only contain a single connection id +func (k Keeper) CounterpartyHops(ctx sdk.Context, ch types.Channel) ([]string, bool) { + // Return empty array if connection hops is more than one + // ConnectionHops length should be verified earlier + if len(ch.ConnectionHops) != 1 { + return []string{}, false + } + counterpartyHops := make([]string, 1) + hop := ch.ConnectionHops[0] + conn, found := k.connectionKeeper.GetConnection(ctx, hop) + if !found { + return []string{}, false + } + + counterpartyHops[0] = conn.GetCounterparty().GetConnectionID() + return counterpartyHops, true +} + +// ChanOpenInit is called by a module to initiate a channel opening handshake with +// a module on another chain. The counterparty channel identifier is validated to be +// empty in msg validation. +func (k Keeper) ChanOpenInit( + ctx sdk.Context, + order types.Order, + connectionHops []string, + portID string, + portCap *capabilitytypes.Capability, + counterparty types.Counterparty, + version string, +) (string, *capabilitytypes.Capability, error) { + // connection hop length checked on msg.ValidateBasic() + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) + if !found { + return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) + } + + getVersions := connectionEnd.GetVersions() + if len(getVersions) != 1 { + return "", nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidVersion, + "single version must be negotiated on connection before opening channel, got: %v", + getVersions, + ) + } + + if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { + return "", nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidVersion, + "connection version %s does not support channel ordering: %s", + getVersions[0], order.String(), + ) + } + + if !k.portKeeper.Authenticate(ctx, portCap, portID) { + return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) + } + + channelID := k.GenerateChannelIdentifier(ctx) + channel := types.NewChannel(types.INIT, order, counterparty, connectionHops, version) + k.SetChannel(ctx, portID, channelID, channel) + + capKey, err := k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) + if err != nil { + return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) + } + + k.SetNextSequenceSend(ctx, portID, channelID, 1) + k.SetNextSequenceRecv(ctx, portID, channelID, 1) + k.SetNextSequenceAck(ctx, portID, channelID, 1) + + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "NONE", "new-state", "INIT") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "open-init") + }() + + return channelID, capKey, nil +} + +// ChanOpenTry is called by a module to accept the first step of a channel opening +// handshake initiated by a module on another chain. +func (k Keeper) ChanOpenTry( + ctx sdk.Context, + order types.Order, + connectionHops []string, + portID, + previousChannelID string, + portCap *capabilitytypes.Capability, + counterparty types.Counterparty, + version, + counterpartyVersion string, + proofInit []byte, + proofHeight exported.Height, +) (string, *capabilitytypes.Capability, error) { + var ( + previousChannel types.Channel + previousChannelFound bool + ) + + channelID := previousChannelID + + // empty channel identifier indicates continuing a previous channel handshake + if previousChannelID != "" { + // channel identifier and connection hop length checked on msg.ValidateBasic() + // ensure that the previous channel exists + previousChannel, previousChannelFound = k.GetChannel(ctx, portID, previousChannelID) + if !previousChannelFound { + return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannel, "previous channel does not exist for supplied previous channelID %s", previousChannelID) + } + // previous channel must use the same fields + if !(previousChannel.Ordering == order && + previousChannel.Counterparty.PortId == counterparty.PortId && + previousChannel.Counterparty.ChannelId == "" && + previousChannel.ConnectionHops[0] == connectionHops[0] && + previousChannel.Version == version) { + return "", nil, sdkerrors.Wrap(types.ErrInvalidChannel, "channel fields mismatch previous channel fields") + } + + if previousChannel.State != types.INIT { + return "", nil, sdkerrors.Wrapf(types.ErrInvalidChannelState, "previous channel state is in %s, expected INIT", previousChannel.State) + } + + } else { + // generate a new channel + channelID = k.GenerateChannelIdentifier(ctx) + } + + if !k.portKeeper.Authenticate(ctx, portCap, portID) { + return "", nil, sdkerrors.Wrapf(porttypes.ErrInvalidPort, "caller does not own port capability for port ID %s", portID) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, connectionHops[0]) + if !found { + return "", nil, sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, connectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return "", nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + getVersions := connectionEnd.GetVersions() + if len(getVersions) != 1 { + return "", nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidVersion, + "single version must be negotiated on connection before opening channel, got: %v", + getVersions, + ) + } + + if !connectiontypes.VerifySupportedFeature(getVersions[0], order.String()) { + return "", nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidVersion, + "connection version %s does not support channel ordering: %s", + getVersions[0], order.String(), + ) + } + + // NOTE: this step has been switched with the one below to reverse the connection + // hops + channel := types.NewChannel(types.TRYOPEN, order, counterparty, connectionHops, version) + + counterpartyHops, found := k.CounterpartyHops(ctx, channel) + if !found { + // should not reach here, connectionEnd was able to be retrieved above + panic("cannot find connection") + } + + // expectedCounterpaty is the counterparty of the counterparty's channel end + // (i.e self) + expectedCounterparty := types.NewCounterparty(portID, "") + expectedChannel := types.NewChannel( + types.INIT, channel.Ordering, expectedCounterparty, + counterpartyHops, counterpartyVersion, + ) + + if err := k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofInit, + counterparty.PortId, counterparty.ChannelId, expectedChannel, + ); err != nil { + return "", nil, err + } + + var ( + capKey *capabilitytypes.Capability + err error + ) + + if !previousChannelFound { + capKey, err = k.scopedKeeper.NewCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) + if err != nil { + return "", nil, sdkerrors.Wrapf(err, "could not create channel capability for port ID %s and channel ID %s", portID, channelID) + } + + k.SetNextSequenceSend(ctx, portID, channelID, 1) + k.SetNextSequenceRecv(ctx, portID, channelID, 1) + k.SetNextSequenceAck(ctx, portID, channelID, 1) + } else { + // capability initialized in ChanOpenInit + capKey, found = k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) + if !found { + return "", nil, sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, + "capability not found for existing channel, portID (%s) channelID (%s)", portID, channelID, + ) + } + } + + k.SetChannel(ctx, portID, channelID, channel) + + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", previousChannel.State.String(), "new-state", "TRYOPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "open-try") + }() + + return channelID, capKey, nil +} + +// ChanOpenAck is called by the handshake-originating module to acknowledge the +// acceptance of the initial request by the counterparty module on the other chain. +func (k Keeper) ChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterpartyVersion, + counterpartyChannelID string, + proofTry []byte, + proofHeight exported.Height, +) error { + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + + if !(channel.State == types.INIT || channel.State == types.TRYOPEN) { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state should be INIT or TRYOPEN (got %s)", channel.State.String(), + ) + } + + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + counterpartyHops, found := k.CounterpartyHops(ctx, channel) + if !found { + // should not reach here, connectionEnd was able to be retrieved above + panic("cannot find connection") + } + + // counterparty of the counterparty channel end (i.e self) + expectedCounterparty := types.NewCounterparty(portID, channelID) + expectedChannel := types.NewChannel( + types.TRYOPEN, channel.Ordering, expectedCounterparty, + counterpartyHops, counterpartyVersion, + ) + + if err := k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofTry, + channel.Counterparty.PortId, counterpartyChannelID, + expectedChannel, + ); err != nil { + return err + } + + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "OPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "open-ack") + }() + + channel.State = types.OPEN + channel.Version = counterpartyVersion + channel.Counterparty.ChannelId = counterpartyChannelID + k.SetChannel(ctx, portID, channelID, channel) + + return nil +} + +// ChanOpenConfirm is called by the counterparty module to close their end of the +// channel, since the other end has been closed. +func (k Keeper) ChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + proofAck []byte, + proofHeight exported.Height, +) error { + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + + if channel.State != types.TRYOPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not TRYOPEN (got %s)", channel.State.String(), + ) + } + + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + counterpartyHops, found := k.CounterpartyHops(ctx, channel) + if !found { + // Should not reach here, connectionEnd was able to be retrieved above + panic("cannot find connection") + } + + counterparty := types.NewCounterparty(portID, channelID) + expectedChannel := types.NewChannel( + types.OPEN, channel.Ordering, counterparty, + counterpartyHops, channel.Version, + ) + + if err := k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofAck, + channel.Counterparty.PortId, channel.Counterparty.ChannelId, + expectedChannel, + ); err != nil { + return err + } + + channel.State = types.OPEN + k.SetChannel(ctx, portID, channelID, channel) + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", "TRYOPEN", "new-state", "OPEN") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "open-confirm") + }() + return nil +} + +// Closing Handshake +// +// This section defines the set of functions required to close a channel handshake +// as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#closing-handshake +// +// ChanCloseInit is called by either module to close their end of the channel. Once +// closed, channels cannot be reopened. +func (k Keeper) ChanCloseInit( + ctx sdk.Context, + portID, + channelID string, + chanCap *capabilitytypes.Capability, +) error { + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", portID, channelID) + } + + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + + if channel.State == types.CLOSED { + return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "close-init") + }() + + channel.State = types.CLOSED + k.SetChannel(ctx, portID, channelID, channel) + + return nil +} + +// ChanCloseConfirm is called by the counterparty module to close their end of the +// channel, since the other end has been closed. +func (k Keeper) ChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + proofInit []byte, + proofHeight exported.Height, +) error { + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)) { + return sdkerrors.Wrap(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)") + } + + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", portID, channelID) + } + + if channel.State == types.CLOSED { + return sdkerrors.Wrap(types.ErrInvalidChannelState, "channel is already CLOSED") + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + counterpartyHops, found := k.CounterpartyHops(ctx, channel) + if !found { + // Should not reach here, connectionEnd was able to be retrieved above + panic("cannot find connection") + } + + counterparty := types.NewCounterparty(portID, channelID) + expectedChannel := types.NewChannel( + types.CLOSED, channel.Ordering, counterparty, + counterpartyHops, channel.Version, + ) + + if err := k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofInit, + channel.Counterparty.PortId, channel.Counterparty.ChannelId, + expectedChannel, + ); err != nil { + return err + } + + k.Logger(ctx).Info("channel state updated", "port-id", portID, "channel-id", channelID, "previous-state", channel.State.String(), "new-state", "CLOSED") + + defer func() { + telemetry.IncrCounter(1, "ibc", "channel", "close-confirm") + }() + + channel.State = types.CLOSED + k.SetChannel(ctx, portID, channelID, channel) + + return nil +} diff --git a/x/ibc/core/04-channel/keeper/handshake_test.go b/x/ibc/core/04-channel/keeper/handshake_test.go new file mode 100644 index 000000000000..120e1f8fe229 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/handshake_test.go @@ -0,0 +1,773 @@ +package keeper_test + +import ( + "fmt" + + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type testCase = struct { + msg string + malleate func() + expPass bool +} + +// TestChanOpenInit tests the OpenInit handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenInit directly. The channel is +// being created on chainA. The port capability must be created on chainA before ChanOpenInit +// can succeed. +func (suite *KeeperTestSuite) TestChanOpenInit() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + features []string + portCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + features = []string{"ORDER_ORDERED", "ORDER_UNORDERED"} + suite.chainA.CreatePortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + portCap = suite.chainA.GetPortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + }, true}, + {"channel already exists", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + }, false}, + {"connection doesn't exist", func() { + // any non-nil values of connA and connB are acceptable + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + }, false}, + {"capability is incorrect", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + features = []string{"ORDER_ORDERED", "ORDER_UNORDERED"} + portCap = capabilitytypes.NewCapability(3) + }, false}, + {"connection version not negotiated", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + // modify connA versions + conn := suite.chainA.GetConnection(connA) + + version := connectiontypes.NewVersion("2", []string{"ORDER_ORDERED", "ORDER_UNORDERED"}) + conn.Versions = append(conn.Versions, version) + + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection( + suite.chainA.GetContext(), + connA.ID, conn, + ) + features = []string{"ORDER_ORDERED", "ORDER_UNORDERED"} + suite.chainA.CreatePortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + portCap = suite.chainA.GetPortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + }, false}, + {"connection does not support ORDERED channels", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + // modify connA versions to only support UNORDERED channels + conn := suite.chainA.GetConnection(connA) + + version := connectiontypes.NewVersion("1", []string{"ORDER_UNORDERED"}) + conn.Versions = []*connectiontypes.Version{version} + + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection( + suite.chainA.GetContext(), + connA.ID, conn, + ) + // NOTE: Opening UNORDERED channels is still expected to pass but ORDERED channels should fail + features = []string{"ORDER_UNORDERED"} + suite.chainA.CreatePortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + portCap = suite.chainA.GetPortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + }, true}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + // run test for all types of ordering + for _, order := range []types.Order{types.UNORDERED, types.ORDERED} { + suite.SetupTest() // reset + tc.malleate() + + counterparty := types.NewCounterparty(connB.FirstOrNextTestChannel(ibctesting.MockPort).PortID, connB.FirstOrNextTestChannel(ibctesting.MockPort).ID) + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + + channelID, cap, err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenInit( + suite.chainA.GetContext(), order, []string{connA.ID}, + channelA.PortID, portCap, counterparty, channelA.Version, + ) + + // check if order is supported by channel to determine expected behaviour + orderSupported := false + for _, f := range features { + if f == order.String() { + orderSupported = true + } + } + + // Testcase must have expectedPass = true AND channel order supported before + // asserting the channel handshake initiation succeeded + if tc.expPass && orderSupported { + suite.Require().NoError(err) + suite.Require().NotNil(cap) + suite.Require().Equal(types.FormatChannelIdentifier(0), channelID) + + chanCap, ok := suite.chainA.App.ScopedIBCKeeper.GetCapability( + suite.chainA.GetContext(), + host.ChannelCapabilityPath(channelA.PortID, channelA.ID), + ) + suite.Require().True(ok, "could not retrieve channel capability after successful ChanOpenInit") + suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") + } else { + suite.Require().Error(err) + suite.Require().Nil(cap) + suite.Require().Equal("", channelID) + } + } + }) + } +} + +// TestChanOpenTry tests the OpenTry handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenTry directly. The channel +// is being created on chainB. The port capability must be created on chainB before +// ChanOpenTry can succeed. +func (suite *KeeperTestSuite) TestChanOpenTry() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + previousChannelID string + portCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + suite.chainB.CreatePortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + portCap = suite.chainB.GetPortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + }, true}, + {"success with crossing hello", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + _, channelB, err := suite.coordinator.ChanOpenInitOnBothChains(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + previousChannelID = channelB.ID + portCap = suite.chainB.GetPortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + }, true}, + {"previous channel with invalid state", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + // make previous channel have wrong ordering + suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, ibctesting.MockPort, ibctesting.MockPort, types.UNORDERED) + }, false}, + {"connection doesn't exist", func() { + // any non-nil values of connA and connB are acceptable + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + + // pass capability check + suite.chainB.CreatePortCapability(connB.FirstOrNextTestChannel(ibctesting.MockPort).PortID) + portCap = suite.chainB.GetPortCapability(connB.FirstOrNextTestChannel(ibctesting.MockPort).PortID) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + // pass capability check + suite.chainB.CreatePortCapability(connB.FirstOrNextTestChannel(ibctesting.MockPort).PortID) + portCap = suite.chainB.GetPortCapability(connB.FirstOrNextTestChannel(ibctesting.MockPort).PortID) + + var err error + connB, connA, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + suite.chainB.CreatePortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + portCap = suite.chainB.GetPortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + + heightDiff = 3 // consensus state doesn't exist at this height + }, false}, + {"channel verification failed", func() { + // not creating a channel on chainA will result in an invalid proof of existence + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + portCap = suite.chainB.GetPortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + }, false}, + {"port capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + portCap = capabilitytypes.NewCapability(3) + }, false}, + {"connection version not negotiated", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + // modify connB versions + conn := suite.chainB.GetConnection(connB) + + version := connectiontypes.NewVersion("2", []string{"ORDER_ORDERED", "ORDER_UNORDERED"}) + conn.Versions = append(conn.Versions, version) + + suite.chainB.App.IBCKeeper.ConnectionKeeper.SetConnection( + suite.chainB.GetContext(), + connB.ID, conn, + ) + suite.chainB.CreatePortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + portCap = suite.chainB.GetPortCapability(suite.chainB.NextTestChannel(connB, ibctesting.MockPort).PortID) + }, false}, + {"connection does not support ORDERED channels", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + // modify connA versions to only support UNORDERED channels + conn := suite.chainA.GetConnection(connA) + + version := connectiontypes.NewVersion("1", []string{"ORDER_UNORDERED"}) + conn.Versions = []*connectiontypes.Version{version} + + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection( + suite.chainA.GetContext(), + connA.ID, conn, + ) + suite.chainA.CreatePortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + portCap = suite.chainA.GetPortCapability(suite.chainA.NextTestChannel(connA, ibctesting.MockPort).PortID) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed in malleate + previousChannelID = "" + + tc.malleate() + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + channelB := connB.FirstOrNextTestChannel(ibctesting.MockPort) + counterparty := types.NewCounterparty(channelA.PortID, channelA.ID) + + channelKey := host.ChannelKey(counterparty.PortId, counterparty.ChannelId) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + channelID, cap, err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenTry( + suite.chainB.GetContext(), types.ORDERED, []string{connB.ID}, + channelB.PortID, previousChannelID, portCap, counterparty, channelB.Version, connA.FirstOrNextTestChannel(ibctesting.MockPort).Version, + proof, malleateHeight(proofHeight, heightDiff), + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(cap) + + chanCap, ok := suite.chainB.App.ScopedIBCKeeper.GetCapability( + suite.chainB.GetContext(), + host.ChannelCapabilityPath(channelB.PortID, channelID), + ) + suite.Require().True(ok, "could not retrieve channel capapbility after successful ChanOpenTry") + suite.Require().Equal(chanCap.String(), cap.String(), "channel capability is not correct") + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanOpenAck tests the OpenAck handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenAck directly. The handshake +// call is occurring on chainA. +func (suite *KeeperTestSuite) TestChanOpenAck() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + counterpartyChannelID string + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"success with empty stored counterparty channel ID", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + // set the channel's counterparty channel identifier to empty string + channel := suite.chainA.GetChannel(channelA) + channel.Counterparty.ChannelId = "" + + // use a different channel identifier + counterpartyChannelID = channelB.ID + + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not INIT or TRYOPEN", func() { + // create fully open channels on both chains + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainA.GetChannel(channelA) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // create channel in init + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + heightDiff = 3 // consensus state doesn't exist at this height + }, false}, + {"invalid counterparty channel identifier", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + counterpartyChannelID = "otheridentifier" + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel verification failed", func() { + // chainB is INIT, chainA in TRYOPEN + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelB, channelA, err := suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainA, suite.chainB, channelA, channelB, connA, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + + channelCap = capabilitytypes.NewCapability(6) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + counterpartyChannelID = "" // must be explicitly changed in malleate + heightDiff = 0 // must be explicitly changed + + tc.malleate() + + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + channelB := connB.FirstOrNextTestChannel(ibctesting.MockPort) + + if counterpartyChannelID == "" { + counterpartyChannelID = channelB.ID + } + + channelKey := host.ChannelKey(channelB.PortID, channelB.ID) + proof, proofHeight := suite.chainB.QueryProof(channelKey) + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanOpenAck( + suite.chainA.GetContext(), channelA.PortID, channelA.ID, channelCap, channelB.Version, counterpartyChannelID, + proof, malleateHeight(proofHeight, heightDiff), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanOpenConfirm tests the OpenAck handshake call for channels. It uses message passing +// to enter into the appropriate state and then calls ChanOpenConfirm directly. The handshake +// call is occurring on chainB. +func (suite *KeeperTestSuite) TestChanOpenConfirm() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + testCases := []testCase{ + {"success", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, true}, + {"channel doesn't exist", func() {}, false}, + {"channel state is not TRYOPEN", func() { + // create fully open channels on both cahins + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelB := connB.Channels[0] + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"connection not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainB.GetChannel(channelB) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainB.GetContext(), channelB.PortID, channelB.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + channelB := connB.FirstOrNextTestChannel(ibctesting.MockPort) + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + heightDiff = 3 + }, false}, + {"channel verification failed", func() { + // chainA is INIT, chainB in TRYOPEN + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenTry(suite.chainB, suite.chainA, channelB, channelA, connB, types.ORDERED) + suite.Require().NoError(err) + + err = suite.coordinator.ChanOpenAck(suite.chainA, suite.chainB, channelA, channelB) + suite.Require().NoError(err) + + channelCap = capabilitytypes.NewCapability(6) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must be explicitly changed + + tc.malleate() + + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + channelB := connB.FirstOrNextTestChannel(ibctesting.MockPort) + + channelKey := host.ChannelKey(channelA.PortID, channelA.ID) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanOpenConfirm( + suite.chainB.GetContext(), channelB.PortID, channelB.ID, + channelCap, proof, malleateHeight(proofHeight, heightDiff), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanCloseInit tests the initial closing of a handshake on chainA by calling +// ChanCloseInit. Both chains will use message passing to setup OPEN channels. +func (suite *KeeperTestSuite) TestChanCloseInit() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel doesn't exist", func() { + // any non-nil values work for connections + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + + // ensure channel capability check passes + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel state is CLOSED", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // close channel + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, false}, + {"connection not found", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA := connA.Channels[0] + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainA.GetChannel(channelA) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + var err error + connA, connB, err = suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + // create channel in init + channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + + // ensure channel capability check passes + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + channelA := connA.FirstOrNextTestChannel(ibctesting.MockPort) + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.ChanCloseInit( + suite.chainA.GetContext(), channelA.PortID, channelA.ID, channelCap, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestChanCloseConfirm tests the confirming closing channel ends by calling ChanCloseConfirm +// on chainB. Both chains will use message passing to setup OPEN channels. ChanCloseInit is +// bypassed on chainA by setting the channel state in the ChannelKeeper. +func (suite *KeeperTestSuite) TestChanCloseConfirm() { + var ( + connA *ibctesting.TestConnection + connB *ibctesting.TestConnection + channelA ibctesting.TestChannel + channelB ibctesting.TestChannel + channelCap *capabilitytypes.Capability + heightDiff uint64 + ) + + testCases := []testCase{ + {"success", func() { + _, _, connA, connB, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, true}, + {"channel doesn't exist", func() { + // any non-nil values work for connections + suite.Require().NotNil(connA) + suite.Require().NotNil(connB) + channelB = connB.FirstOrNextTestChannel(ibctesting.MockPort) + + // ensure channel capability check passes + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel state is CLOSED", func() { + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) + }, false}, + {"connection not found", func() { + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + // set the channel's connection hops to wrong connection ID + channel := suite.chainB.GetChannel(channelB) + channel.ConnectionHops[0] = "doesnotexist" + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainB.GetContext(), channelB.PortID, channelB.ID, channel) + }, false}, + {"connection is not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + var err error + connB, connA, err = suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + // create channel in init + channelB, _, err := suite.coordinator.ChanOpenInit(suite.chainB, suite.chainA, connB, connA, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.Require().NoError(err) + + // ensure channel capability check passes + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"consensus state not found", func() { + _, _, connA, connB, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + + heightDiff = 3 + }, false}, + {"channel verification failed", func() { + // channel not closed + _, _, connA, connB, _, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel capability not found", func() { + _, _, connA, connB, channelA, channelB = suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + + channelCap = capabilitytypes.NewCapability(3) + }, false}, + } + + for _, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + heightDiff = 0 // must explicitly be changed + + tc.malleate() + + channelA = connA.FirstOrNextTestChannel(ibctesting.MockPort) + channelB = connB.FirstOrNextTestChannel(ibctesting.MockPort) + + channelKey := host.ChannelKey(channelA.PortID, channelA.ID) + proof, proofHeight := suite.chainA.QueryProof(channelKey) + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.ChanCloseConfirm( + suite.chainB.GetContext(), channelB.PortID, channelB.ID, channelCap, + proof, malleateHeight(proofHeight, heightDiff), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func malleateHeight(height exported.Height, diff uint64) exported.Height { + return clienttypes.NewHeight(height.GetRevisionNumber(), height.GetRevisionHeight()+diff) +} diff --git a/x/ibc/core/04-channel/keeper/keeper.go b/x/ibc/core/04-channel/keeper/keeper.go new file mode 100644 index 000000000000..60452f315bdf --- /dev/null +++ b/x/ibc/core/04-channel/keeper/keeper.go @@ -0,0 +1,432 @@ +package keeper + +import ( + "strconv" + "strings" + + "github.com/tendermint/tendermint/libs/log" + db "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// Keeper defines the IBC channel keeper +type Keeper struct { + // implements gRPC QueryServer interface + types.QueryServer + + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + clientKeeper types.ClientKeeper + connectionKeeper types.ConnectionKeeper + portKeeper types.PortKeeper + scopedKeeper capabilitykeeper.ScopedKeeper +} + +// NewKeeper creates a new IBC channel Keeper instance +func NewKeeper( + cdc codec.BinaryMarshaler, key sdk.StoreKey, + clientKeeper types.ClientKeeper, connectionKeeper types.ConnectionKeeper, + portKeeper types.PortKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, +) Keeper { + return Keeper{ + storeKey: key, + cdc: cdc, + clientKeeper: clientKeeper, + connectionKeeper: connectionKeeper, + portKeeper: portKeeper, + scopedKeeper: scopedKeeper, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) +} + +// GenerateChannelIdentifier returns the next channel identifier. +func (k Keeper) GenerateChannelIdentifier(ctx sdk.Context) string { + nextChannelSeq := k.GetNextChannelSequence(ctx) + channelID := types.FormatChannelIdentifier(nextChannelSeq) + + nextChannelSeq++ + k.SetNextChannelSequence(ctx, nextChannelSeq) + return channelID +} + +// GetChannel returns a channel with a particular identifier binded to a specific port +func (k Keeper) GetChannel(ctx sdk.Context, portID, channelID string) (types.Channel, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.ChannelKey(portID, channelID)) + if bz == nil { + return types.Channel{}, false + } + + var channel types.Channel + k.cdc.MustUnmarshalBinaryBare(bz, &channel) + return channel, true +} + +// SetChannel sets a channel to the store +func (k Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel types.Channel) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(&channel) + store.Set(host.ChannelKey(portID, channelID), bz) +} + +// GetNextChannelSequence gets the next channel sequence from the store. +func (k Keeper) GetNextChannelSequence(ctx sdk.Context) uint64 { + store := ctx.KVStore(k.storeKey) + bz := store.Get([]byte(types.KeyNextChannelSequence)) + if bz == nil { + panic("next channel sequence is nil") + } + + return sdk.BigEndianToUint64(bz) +} + +// SetNextChannelSequence sets the next channel sequence to the store. +func (k Keeper) SetNextChannelSequence(ctx sdk.Context, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set([]byte(types.KeyNextChannelSequence), bz) +} + +// GetNextSequenceSend gets a channel's next send sequence from the store +func (k Keeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.NextSequenceSendKey(portID, channelID)) + if bz == nil { + return 0, false + } + + return sdk.BigEndianToUint64(bz), true +} + +// SetNextSequenceSend sets a channel's next send sequence to the store +func (k Keeper) SetNextSequenceSend(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set(host.NextSequenceSendKey(portID, channelID), bz) +} + +// GetNextSequenceRecv gets a channel's next receive sequence from the store +func (k Keeper) GetNextSequenceRecv(ctx sdk.Context, portID, channelID string) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.NextSequenceRecvKey(portID, channelID)) + if bz == nil { + return 0, false + } + + return sdk.BigEndianToUint64(bz), true +} + +// SetNextSequenceRecv sets a channel's next receive sequence to the store +func (k Keeper) SetNextSequenceRecv(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set(host.NextSequenceRecvKey(portID, channelID), bz) +} + +// GetNextSequenceAck gets a channel's next ack sequence from the store +func (k Keeper) GetNextSequenceAck(ctx sdk.Context, portID, channelID string) (uint64, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.NextSequenceAckKey(portID, channelID)) + if bz == nil { + return 0, false + } + + return sdk.BigEndianToUint64(bz), true +} + +// SetNextSequenceAck sets a channel's next ack sequence to the store +func (k Keeper) SetNextSequenceAck(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(sequence) + store.Set(host.NextSequenceAckKey(portID, channelID), bz) +} + +// GetPacketReceipt gets a packet receipt from the store +func (k Keeper) GetPacketReceipt(ctx sdk.Context, portID, channelID string, sequence uint64) (string, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.PacketReceiptKey(portID, channelID, sequence)) + if bz == nil { + return "", false + } + + return string(bz), true +} + +// SetPacketReceipt sets an empty packet receipt to the store +func (k Keeper) SetPacketReceipt(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + store.Set(host.PacketReceiptKey(portID, channelID, sequence), []byte{byte(1)}) +} + +// GetPacketCommitment gets the packet commitment hash from the store +func (k Keeper) GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.PacketCommitmentKey(portID, channelID, sequence)) + return bz +} + +// HasPacketCommitment returns true if the packet commitment exists +func (k Keeper) HasPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(host.PacketCommitmentKey(portID, channelID, sequence)) +} + +// SetPacketCommitment sets the packet commitment hash to the store +func (k Keeper) SetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64, commitmentHash []byte) { + store := ctx.KVStore(k.storeKey) + store.Set(host.PacketCommitmentKey(portID, channelID, sequence), commitmentHash) +} + +func (k Keeper) deletePacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(host.PacketCommitmentKey(portID, channelID, sequence)) +} + +// SetPacketAcknowledgement sets the packet ack hash to the store +func (k Keeper) SetPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64, ackHash []byte) { + store := ctx.KVStore(k.storeKey) + store.Set(host.PacketAcknowledgementKey(portID, channelID, sequence), ackHash) +} + +// GetPacketAcknowledgement gets the packet ack hash from the store +func (k Keeper) GetPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) ([]byte, bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(host.PacketAcknowledgementKey(portID, channelID, sequence)) + if bz == nil { + return nil, false + } + return bz, true +} + +// HasPacketAcknowledgement check if the packet ack hash is already on the store +func (k Keeper) HasPacketAcknowledgement(ctx sdk.Context, portID, channelID string, sequence uint64) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(host.PacketAcknowledgementKey(portID, channelID, sequence)) +} + +// IteratePacketSequence provides an iterator over all send, receive or ack sequences. +// For each sequence, cb will be called. If the cb returns true, the iterator +// will close and stop. +func (k Keeper) IteratePacketSequence(ctx sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64) bool) { + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + portID, channelID, err := host.ParseChannelPath(string(iterator.Key())) + if err != nil { + // return if the key is not a channel key + return + } + + sequence := sdk.BigEndianToUint64(iterator.Value()) + + if cb(portID, channelID, sequence) { + break + } + } +} + +// GetAllPacketSendSeqs returns all stored next send sequences. +func (k Keeper) GetAllPacketSendSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqSendPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextSendSeq uint64) bool { + ps := types.NewPacketSequence(portID, channelID, nextSendSeq) + seqs = append(seqs, ps) + return false + }) + return seqs +} + +// GetAllPacketRecvSeqs returns all stored next recv sequences. +func (k Keeper) GetAllPacketRecvSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqRecvPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextRecvSeq uint64) bool { + ps := types.NewPacketSequence(portID, channelID, nextRecvSeq) + seqs = append(seqs, ps) + return false + }) + return seqs +} + +// GetAllPacketAckSeqs returns all stored next acknowledgements sequences. +func (k Keeper) GetAllPacketAckSeqs(ctx sdk.Context) (seqs []types.PacketSequence) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyNextSeqAckPrefix)) + k.IteratePacketSequence(ctx, iterator, func(portID, channelID string, nextAckSeq uint64) bool { + ps := types.NewPacketSequence(portID, channelID, nextAckSeq) + seqs = append(seqs, ps) + return false + }) + return seqs +} + +// IteratePacketCommitment provides an iterator over all PacketCommitment objects. For each +// packet commitment, cb will be called. If the cb returns true, the iterator will close +// and stop. +func (k Keeper) IteratePacketCommitment(ctx sdk.Context, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyPacketCommitmentPrefix)) + k.iterateHashes(ctx, iterator, cb) +} + +// GetAllPacketCommitments returns all stored PacketCommitments objects. +func (k Keeper) GetAllPacketCommitments(ctx sdk.Context) (commitments []types.PacketState) { + k.IteratePacketCommitment(ctx, func(portID, channelID string, sequence uint64, hash []byte) bool { + pc := types.NewPacketState(portID, channelID, sequence, hash) + commitments = append(commitments, pc) + return false + }) + return commitments +} + +// IteratePacketCommitmentAtChannel provides an iterator over all PacketCommmitment objects +// at a specified channel. For each packet commitment, cb will be called. If the cb returns +// true, the iterator will close and stop. +func (k Keeper) IteratePacketCommitmentAtChannel(ctx sdk.Context, portID, channelID string, cb func(_, _ string, sequence uint64, hash []byte) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.PacketCommitmentPrefixPath(portID, channelID))) + k.iterateHashes(ctx, iterator, cb) +} + +// GetAllPacketCommitmentsAtChannel returns all stored PacketCommitments objects for a specified +// port ID and channel ID. +func (k Keeper) GetAllPacketCommitmentsAtChannel(ctx sdk.Context, portID, channelID string) (commitments []types.PacketState) { + k.IteratePacketCommitmentAtChannel(ctx, portID, channelID, func(_, _ string, sequence uint64, hash []byte) bool { + pc := types.NewPacketState(portID, channelID, sequence, hash) + commitments = append(commitments, pc) + return false + }) + return commitments +} + +// IteratePacketReceipt provides an iterator over all PacketReceipt objects. For each +// receipt, cb will be called. If the cb returns true, the iterator will close +// and stop. +func (k Keeper) IteratePacketReceipt(ctx sdk.Context, cb func(portID, channelID string, sequence uint64, receipt []byte) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyPacketReceiptPrefix)) + k.iterateHashes(ctx, iterator, cb) +} + +// GetAllPacketReceipts returns all stored PacketReceipt objects. +func (k Keeper) GetAllPacketReceipts(ctx sdk.Context) (receipts []types.PacketState) { + k.IteratePacketReceipt(ctx, func(portID, channelID string, sequence uint64, receipt []byte) bool { + packetReceipt := types.NewPacketState(portID, channelID, sequence, receipt) + receipts = append(receipts, packetReceipt) + return false + }) + return receipts +} + +// IteratePacketAcknowledgement provides an iterator over all PacketAcknowledgement objects. For each +// aknowledgement, cb will be called. If the cb returns true, the iterator will close +// and stop. +func (k Keeper) IteratePacketAcknowledgement(ctx sdk.Context, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyPacketAckPrefix)) + k.iterateHashes(ctx, iterator, cb) +} + +// GetAllPacketAcks returns all stored PacketAcknowledgements objects. +func (k Keeper) GetAllPacketAcks(ctx sdk.Context) (acks []types.PacketState) { + k.IteratePacketAcknowledgement(ctx, func(portID, channelID string, sequence uint64, ack []byte) bool { + packetAck := types.NewPacketState(portID, channelID, sequence, ack) + acks = append(acks, packetAck) + return false + }) + return acks +} + +// IterateChannels provides an iterator over all Channel objects. For each +// Channel, cb will be called. If the cb returns true, the iterator will close +// and stop. +func (k Keeper) IterateChannels(ctx sdk.Context, cb func(types.IdentifiedChannel) bool) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyChannelEndPrefix)) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var channel types.Channel + k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &channel) + + portID, channelID := host.MustParseChannelPath(string(iterator.Key())) + identifiedChannel := types.NewIdentifiedChannel(portID, channelID, channel) + if cb(identifiedChannel) { + break + } + } +} + +// GetAllChannels returns all stored Channel objects. +func (k Keeper) GetAllChannels(ctx sdk.Context) (channels []types.IdentifiedChannel) { + k.IterateChannels(ctx, func(channel types.IdentifiedChannel) bool { + channels = append(channels, channel) + return false + }) + return channels +} + +// GetChannelClientState returns the associated client state with its ID, from a port and channel identifier. +func (k Keeper) GetChannelClientState(ctx sdk.Context, portID, channelID string) (string, exported.ClientState, error) { + channel, found := k.GetChannel(ctx, portID, channelID) + if !found { + return "", nil, sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id: %s", portID, channelID) + } + + connection, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return "", nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]) + } + + clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientId) + if !found { + return "", nil, sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId) + } + + return connection.ClientId, clientState, nil +} + +// LookupModuleByChannel will return the IBCModule along with the capability associated with a given channel defined by its portID and channelID +func (k Keeper) LookupModuleByChannel(ctx sdk.Context, portID, channelID string) (string, *capabilitytypes.Capability, error) { + modules, cap, err := k.scopedKeeper.LookupModules(ctx, host.ChannelCapabilityPath(portID, channelID)) + if err != nil { + return "", nil, err + } + + return porttypes.GetModuleOwner(modules), cap, nil +} + +// common functionality for IteratePacketCommitment and IteratePacketAcknowledgement +func (k Keeper) iterateHashes(_ sdk.Context, iterator db.Iterator, cb func(portID, channelID string, sequence uint64, hash []byte) bool) { + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + portID := keySplit[2] + channelID := keySplit[4] + + sequence, err := strconv.ParseUint(keySplit[len(keySplit)-1], 10, 64) + if err != nil { + panic(err) + } + + if cb(portID, channelID, sequence, iterator.Value()) { + break + } + } +} diff --git a/x/ibc/core/04-channel/keeper/keeper_test.go b/x/ibc/core/04-channel/keeper/keeper_test.go new file mode 100644 index 000000000000..a9b7dd6cf1c6 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/keeper_test.go @@ -0,0 +1,329 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +// KeeperTestSuite is a testing suite to test keeper functions. +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// TestKeeperTestSuite runs all the tests within this package. +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) + suite.coordinator.CommitNBlocks(suite.chainA, 2) + suite.coordinator.CommitNBlocks(suite.chainB, 2) +} + +// TestSetChannel create clients and connections on both chains. It tests for the non-existence +// and existence of a channel in INIT on chainA. +func (suite *KeeperTestSuite) TestSetChannel() { + // create client and connections on both chains + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + + // check for channel to be created on chainA + channelA := suite.chainA.NextTestChannel(connA, ibctesting.MockPort) + _, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID) + suite.False(found) + + // init channel + channelA, channelB, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, ibctesting.MockPort, ibctesting.MockPort, types.ORDERED) + suite.NoError(err) + + storedChannel, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID) + // counterparty channel id is empty after open init + expectedCounterparty := types.NewCounterparty(channelB.PortID, "") + + suite.True(found) + suite.Equal(types.INIT, storedChannel.State) + suite.Equal(types.ORDERED, storedChannel.Ordering) + suite.Equal(expectedCounterparty, storedChannel.Counterparty) +} + +// TestGetAllChannels creates multiple channels on chain A through various connections +// and tests their retrieval. 2 channels are on connA0 and 1 channel is on connA1 +func (suite KeeperTestSuite) TestGetAllChannels() { + clientA, clientB, connA0, connB0, testchannel0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // channel0 on first connection on chainA + counterparty0 := types.Counterparty{ + PortId: connB0.Channels[0].PortID, + ChannelId: connB0.Channels[0].ID, + } + + // channel1 is second channel on first connection on chainA + testchannel1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA0, connB0, types.ORDERED) + counterparty1 := types.Counterparty{ + PortId: connB0.Channels[1].PortID, + ChannelId: connB0.Channels[1].ID, + } + + connA1, connB1 := suite.coordinator.CreateConnection(suite.chainA, suite.chainB, clientA, clientB) + + // channel2 is on a second connection on chainA + testchannel2, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA1, connB1, ibctesting.MockPort, ibctesting.MockPort, types.UNORDERED) + suite.Require().NoError(err) + + // counterparty channel id is empty after open init + counterparty2 := types.Counterparty{ + PortId: connB1.Channels[0].PortID, + ChannelId: "", + } + + channel0 := types.NewChannel( + types.OPEN, types.UNORDERED, + counterparty0, []string{connA0.ID}, testchannel0.Version, + ) + channel1 := types.NewChannel( + types.OPEN, types.ORDERED, + counterparty1, []string{connA0.ID}, testchannel1.Version, + ) + channel2 := types.NewChannel( + types.INIT, types.UNORDERED, + counterparty2, []string{connA1.ID}, testchannel2.Version, + ) + + expChannels := []types.IdentifiedChannel{ + types.NewIdentifiedChannel(testchannel0.PortID, testchannel0.ID, channel0), + types.NewIdentifiedChannel(testchannel1.PortID, testchannel1.ID, channel1), + types.NewIdentifiedChannel(testchannel2.PortID, testchannel2.ID, channel2), + } + + ctxA := suite.chainA.GetContext() + + channels := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllChannels(ctxA) + suite.Require().Len(channels, len(expChannels)) + suite.Require().Equal(expChannels, channels) +} + +// TestGetAllSequences sets all packet sequences for two different channels on chain A and +// tests their retrieval. +func (suite KeeperTestSuite) TestGetAllSequences() { + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) + + seq1 := types.NewPacketSequence(channelA0.PortID, channelA0.ID, 1) + seq2 := types.NewPacketSequence(channelA0.PortID, channelA0.ID, 2) + seq3 := types.NewPacketSequence(channelA1.PortID, channelA1.ID, 3) + + // seq1 should be overwritten by seq2 + expSeqs := []types.PacketSequence{seq2, seq3} + + ctxA := suite.chainA.GetContext() + + for _, seq := range []types.PacketSequence{seq1, seq2, seq3} { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctxA, seq.PortId, seq.ChannelId, seq.Sequence) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctxA, seq.PortId, seq.ChannelId, seq.Sequence) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctxA, seq.PortId, seq.ChannelId, seq.Sequence) + } + + sendSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketSendSeqs(ctxA) + recvSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketRecvSeqs(ctxA) + ackSeqs := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketAckSeqs(ctxA) + suite.Len(sendSeqs, 2) + suite.Len(recvSeqs, 2) + suite.Len(ackSeqs, 2) + + suite.Equal(expSeqs, sendSeqs) + suite.Equal(expSeqs, recvSeqs) + suite.Equal(expSeqs, ackSeqs) +} + +// TestGetAllPacketState creates a set of acks, packet commitments, and receipts on two different +// channels on chain A and tests their retrieval. +func (suite KeeperTestSuite) TestGetAllPacketState() { + _, _, connA, connB, channelA0, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + channelA1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) + + // channel 0 acks + ack1 := types.NewPacketState(channelA0.PortID, channelA0.ID, 1, []byte("ack")) + ack2 := types.NewPacketState(channelA0.PortID, channelA0.ID, 2, []byte("ack")) + + // duplicate ack + ack2dup := types.NewPacketState(channelA0.PortID, channelA0.ID, 2, []byte("ack")) + + // channel 1 acks + ack3 := types.NewPacketState(channelA1.PortID, channelA1.ID, 1, []byte("ack")) + + // create channel 0 receipts + receipt := string([]byte{byte(1)}) + rec1 := types.NewPacketState(channelA0.PortID, channelA0.ID, 1, []byte(receipt)) + rec2 := types.NewPacketState(channelA0.PortID, channelA0.ID, 2, []byte(receipt)) + + // channel 1 receipts + rec3 := types.NewPacketState(channelA1.PortID, channelA1.ID, 1, []byte(receipt)) + rec4 := types.NewPacketState(channelA1.PortID, channelA1.ID, 2, []byte(receipt)) + + // channel 0 packet commitments + comm1 := types.NewPacketState(channelA0.PortID, channelA0.ID, 1, []byte("hash")) + comm2 := types.NewPacketState(channelA0.PortID, channelA0.ID, 2, []byte("hash")) + + // channel 1 packet commitments + comm3 := types.NewPacketState(channelA1.PortID, channelA1.ID, 1, []byte("hash")) + comm4 := types.NewPacketState(channelA1.PortID, channelA1.ID, 2, []byte("hash")) + + expAcks := []types.PacketState{ack1, ack2, ack3} + expReceipts := []types.PacketState{rec1, rec2, rec3, rec4} + expCommitments := []types.PacketState{comm1, comm2, comm3, comm4} + + ctxA := suite.chainA.GetContext() + + // set acknowledgements + for _, ack := range []types.PacketState{ack1, ack2, ack2dup, ack3} { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, ack.PortId, ack.ChannelId, ack.Sequence, ack.Data) + } + + // set packet receipts + for _, rec := range expReceipts { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(ctxA, rec.PortId, rec.ChannelId, rec.Sequence) + } + + // set packet commitments + for _, comm := range expCommitments { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, comm.PortId, comm.ChannelId, comm.Sequence, comm.Data) + } + + acks := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketAcks(ctxA) + receipts := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketReceipts(ctxA) + commitments := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitments(ctxA) + + suite.Require().Len(acks, len(expAcks)) + suite.Require().Len(commitments, len(expCommitments)) + suite.Require().Len(receipts, len(expReceipts)) + + suite.Require().Equal(expAcks, acks) + suite.Require().Equal(expReceipts, receipts) + suite.Require().Equal(expCommitments, commitments) +} + +// TestSetSequence verifies that the keeper correctly sets the sequence counters. +func (suite *KeeperTestSuite) TestSetSequence() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + ctxA := suite.chainA.GetContext() + one := uint64(1) + + // initialized channel has next send seq of 1 + seq, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) + + // initialized channel has next seq recv of 1 + seq, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) + + // initialized channel has next seq ack of + seq, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(one, seq) + + nextSeqSend, nextSeqRecv, nextSeqAck := uint64(10), uint64(10), uint64(10) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(ctxA, channelA.PortID, channelA.ID, nextSeqSend) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceRecv(ctxA, channelA.PortID, channelA.ID, nextSeqRecv) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(ctxA, channelA.PortID, channelA.ID, nextSeqAck) + + storedNextSeqSend, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(nextSeqSend, storedNextSeqSend) + + storedNextSeqRecv, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceSend(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(nextSeqRecv, storedNextSeqRecv) + + storedNextSeqAck, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(ctxA, channelA.PortID, channelA.ID) + suite.True(found) + suite.Equal(nextSeqAck, storedNextSeqAck) +} + +// TestGetAllPacketCommitmentsAtChannel verifies that the keeper returns all stored packet +// commitments for a specific channel. The test will store consecutive commitments up to the +// value of "seq" and then add non-consecutive up to the value of "maxSeq". A final commitment +// with the value maxSeq + 1 is set on a different channel. +func (suite *KeeperTestSuite) TestGetAllPacketCommitmentsAtChannel() { + _, _, connA, connB, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + // create second channel + channelA1, _ := suite.coordinator.CreateMockChannels(suite.chainA, suite.chainB, connA, connB, types.UNORDERED) + + ctxA := suite.chainA.GetContext() + expectedSeqs := make(map[uint64]bool) + hash := []byte("commitment") + + seq := uint64(15) + maxSeq := uint64(25) + suite.Require().Greater(maxSeq, seq) + + // create consecutive commitments + for i := uint64(1); i < seq; i++ { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA.PortID, channelA.ID, i, hash) + expectedSeqs[i] = true + } + + // add non-consecutive commitments + for i := seq; i < maxSeq; i += 2 { + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA.PortID, channelA.ID, i, hash) + expectedSeqs[i] = true + } + + // add sequence on different channel/port + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(ctxA, channelA1.PortID, channelA1.ID, maxSeq+1, hash) + + commitments := suite.chainA.App.IBCKeeper.ChannelKeeper.GetAllPacketCommitmentsAtChannel(ctxA, channelA.PortID, channelA.ID) + + suite.Equal(len(expectedSeqs), len(commitments)) + // ensure above for loops occurred + suite.NotEqual(0, len(commitments)) + + // verify that all the packet commitments were stored + for _, packet := range commitments { + suite.True(expectedSeqs[packet.Sequence]) + suite.Equal(channelA.PortID, packet.PortId) + suite.Equal(channelA.ID, packet.ChannelId) + suite.Equal(hash, packet.Data) + + // prevent duplicates from passing checks + expectedSeqs[packet.Sequence] = false + } +} + +// TestSetPacketAcknowledgement verifies that packet acknowledgements are correctly +// set in the keeper. +func (suite *KeeperTestSuite) TestSetPacketAcknowledgement() { + _, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + ctxA := suite.chainA.GetContext() + seq := uint64(10) + + storedAckHash, found := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq) + suite.Require().False(found) + suite.Require().Nil(storedAckHash) + + ackHash := []byte("ackhash") + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq, ackHash) + + storedAckHash, found = suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq) + suite.Require().True(found) + suite.Require().Equal(ackHash, storedAckHash) + suite.Require().True(suite.chainA.App.IBCKeeper.ChannelKeeper.HasPacketAcknowledgement(ctxA, channelA.PortID, channelA.ID, seq)) +} diff --git a/x/ibc/core/04-channel/keeper/packet.go b/x/ibc/core/04-channel/keeper/packet.go new file mode 100644 index 000000000000..49b59733c5a3 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/packet.go @@ -0,0 +1,528 @@ +package keeper + +import ( + "bytes" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// SendPacket is called by a module in order to send an IBC packet on a channel +// end owned by the calling module to the corresponding module on the counterparty +// chain. +func (k Keeper) SendPacket( + ctx sdk.Context, + channelCap *capabilitytypes.Capability, + packet exported.PacketI, +) error { + if err := packet.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "packet failed basic validation") + } + + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetSourceChannel()) + } + + if channel.State == types.CLOSED { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel is CLOSED (got %s)", channel.State.String(), + ) + } + + if !k.scopedKeeper.AuthenticateCapability(ctx, channelCap, host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel())) { + return sdkerrors.Wrapf(types.ErrChannelCapabilityNotFound, "caller does not own capability for channel, port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + } + + if packet.GetDestPort() != channel.Counterparty.PortId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, + ) + } + + if packet.GetDestChannel() != channel.Counterparty.ChannelId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, + ) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + clientState, found := k.clientKeeper.GetClientState(ctx, connectionEnd.GetClientID()) + if !found { + return clienttypes.ErrConsensusStateNotFound + } + + // prevent accidental sends with clients that cannot be updated + if clientState.IsFrozen() { + return sdkerrors.Wrapf(clienttypes.ErrClientFrozen, "cannot send packet on a frozen client with ID %s", connectionEnd.GetClientID()) + } + + // check if packet timeouted on the receiving chain + latestHeight := clientState.GetLatestHeight() + timeoutHeight := packet.GetTimeoutHeight() + if !timeoutHeight.IsZero() && latestHeight.GTE(timeoutHeight) { + return sdkerrors.Wrapf( + types.ErrPacketTimeout, + "receiving chain block height >= packet timeout height (%s >= %s)", latestHeight, timeoutHeight, + ) + } + + latestTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, latestHeight) + if err != nil { + return err + } + + if packet.GetTimeoutTimestamp() != 0 && latestTimestamp >= packet.GetTimeoutTimestamp() { + return sdkerrors.Wrapf( + types.ErrPacketTimeout, + "receiving chain block timestamp >= packet timeout timestamp (%s >= %s)", time.Unix(0, int64(latestTimestamp)), time.Unix(0, int64(packet.GetTimeoutTimestamp())), + ) + } + + nextSequenceSend, found := k.GetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf( + types.ErrSequenceSendNotFound, + "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), + ) + } + + if packet.GetSequence() != nextSequenceSend { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet sequence ≠ next send sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceSend, + ) + } + + commitment := types.CommitPacket(k.cdc, packet) + + nextSequenceSend++ + k.SetNextSequenceSend(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceSend) + k.SetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment) + + // Emit Event with Packet data along with other packet information for relayer to pick up + // and relay to other chain + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeSendPacket, + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, timeoutHeight.String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + 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, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + k.Logger(ctx).Info("packet sent", "packet", fmt.Sprintf("%v", packet)) + return nil +} + +// RecvPacket is called by a module in order to receive & process an IBC packet +// sent on the corresponding channel end on the counterparty chain. +func (k Keeper) RecvPacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + proof []byte, + proofHeight exported.Height, +) error { + channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if !found { + return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) + } + + if channel.State != types.OPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) + } + + // Authenticate capability to ensure caller has authority to receive packet on this channel + capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { + return sdkerrors.Wrapf( + types.ErrInvalidChannelCapability, + "channel capability failed authentication for capability name %s", capName, + ) + } + + // packet must come from the channel's counterparty + if packet.GetSourcePort() != channel.Counterparty.PortId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet source port doesn't match the counterparty's port (%s ≠ %s)", packet.GetSourcePort(), channel.Counterparty.PortId, + ) + } + + if packet.GetSourceChannel() != channel.Counterparty.ChannelId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet source channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetSourceChannel(), channel.Counterparty.ChannelId, + ) + } + + // Connection must be OPEN to receive a packet. It is possible for connection to not yet be open if packet was + // sent optimistically before connection and channel handshake completed. However, to receive a packet, + // connection and channel must both be open + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + // check if packet timeouted by comparing it with the latest height of the chain + selfHeight := clienttypes.GetSelfHeight(ctx) + timeoutHeight := packet.GetTimeoutHeight() + if !timeoutHeight.IsZero() && selfHeight.GTE(timeoutHeight) { + return sdkerrors.Wrapf( + types.ErrPacketTimeout, + "block height >= packet timeout height (%s >= %s)", selfHeight, timeoutHeight, + ) + } + + // check if packet timeouted by comparing it with the latest timestamp of the chain + if packet.GetTimeoutTimestamp() != 0 && uint64(ctx.BlockTime().UnixNano()) >= packet.GetTimeoutTimestamp() { + return sdkerrors.Wrapf( + types.ErrPacketTimeout, + "block timestamp >= packet timeout timestamp (%s >= %s)", ctx.BlockTime(), time.Unix(0, int64(packet.GetTimeoutTimestamp())), + ) + } + + commitment := types.CommitPacket(k.cdc, packet) + + // verify that the counterparty did commit to sending this packet + if err := k.connectionKeeper.VerifyPacketCommitment( + ctx, connectionEnd, proofHeight, proof, + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), + commitment, + ); err != nil { + return sdkerrors.Wrap(err, "couldn't verify counterparty packet commitment") + } + + switch channel.Ordering { + case types.UNORDERED: + // check if the packet receipt has been received already for unordered channels + _, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + if found { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet sequence (%d) already has been received", packet.GetSequence(), + ) + } + + // All verification complete, update state + // For unordered channels we must set the receipt so it can be verified on the other side. + // This receipt does not contain any data, since the packet has not yet been processed, + // it's just a single store key set to an empty string to indicate that the packet has been received + k.SetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + case types.ORDERED: + // check if the packet is being received in order + nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if !found { + return sdkerrors.Wrapf( + types.ErrSequenceReceiveNotFound, + "destination port: %s, destination channel: %s", packet.GetDestPort(), packet.GetDestChannel(), + ) + } + + if packet.GetSequence() != nextSequenceRecv { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet sequence ≠ next receive sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceRecv, + ) + } + + // All verification complete, update state + // In ordered case, we must increment nextSequenceRecv + nextSequenceRecv++ + + // incrementing nextSequenceRecv and storing under this chain's channelEnd identifiers + // Since this is the receiving chain, our channelEnd is packet's destination port and channel + k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv) + + } + + // log that a packet has been received & executed + k.Logger(ctx).Info("packet received", "packet", fmt.Sprintf("%v", packet)) + + // emit an event that the relayer can query for + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeRecvPacket, + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + 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, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return nil +} + +// WriteAcknowledgement writes the packet execution acknowledgement to the state, +// which will be verified by the counterparty chain using AcknowledgePacket. +// +// CONTRACT: +// +// 1) For synchronous execution, this function is be called in the IBC handler . +// For async handling, it needs to be called directly by the module which originally +// processed the packet. +// +// 2) Assumes that packet receipt has been written (unordered), or nextSeqRecv was incremented (ordered) +// previously by RecvPacket. +func (k Keeper) WriteAcknowledgement( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + acknowledgement []byte, +) error { + channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel()) + if !found { + return sdkerrors.Wrap(types.ErrChannelNotFound, packet.GetDestChannel()) + } + + if channel.State != types.OPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) + } + + // Authenticate capability to ensure caller has authority to receive packet on this channel + capName := host.ChannelCapabilityPath(packet.GetDestPort(), packet.GetDestChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { + return sdkerrors.Wrapf( + types.ErrInvalidChannelCapability, + "channel capability failed authentication for capability name %s", capName, + ) + } + + // NOTE: IBC app modules might have written the acknowledgement synchronously on + // the OnRecvPacket callback so we need to check if the acknowledgement is already + // set on the store and return an error if so. + if k.HasPacketAcknowledgement(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) { + return types.ErrAcknowledgementExists + } + + if len(acknowledgement) == 0 { + return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty") + } + + // set the acknowledgement so that it can be verified on the other side + k.SetPacketAcknowledgement( + ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + types.CommitAcknowledgement(acknowledgement), + ) + + // log that a packet acknowledgement has been written + k.Logger(ctx).Info("acknowledged written", "packet", fmt.Sprintf("%v", packet)) + + // emit an event that the relayer can query for + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeWriteAck, + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + 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, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return nil +} + +// AcknowledgePacket is called by a module to process the acknowledgement of a +// packet previously sent by the calling module on a channel to a counterparty +// module on the counterparty chain. Its intended usage is within the ante +// handler. AcknowledgePacket will clean up the packet commitment, +// which is no longer necessary since the packet has been received and acted upon. +// It will also increment NextSequenceAck in case of ORDERED channels. +func (k Keeper) AcknowledgePacket( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + acknowledgement []byte, + proof []byte, + proofHeight exported.Height, +) error { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf( + types.ErrChannelNotFound, + "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), + ) + } + + if channel.State != types.OPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) + } + + // Authenticate capability to ensure caller has authority to receive packet on this channel + capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { + return sdkerrors.Wrapf( + types.ErrInvalidChannelCapability, + "channel capability failed authentication for capability name %s", capName, + ) + } + + // packet must have been sent to the channel's counterparty + if packet.GetDestPort() != channel.Counterparty.PortId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, + ) + } + + if packet.GetDestChannel() != channel.Counterparty.ChannelId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, + ) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + if connectionEnd.GetState() != int32(connectiontypes.OPEN) { + return sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnectionState, + "connection state is not OPEN (got %s)", connectiontypes.State(connectionEnd.GetState()).String(), + ) + } + + commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + packetCommitment := types.CommitPacket(k.cdc, packet) + + // verify we sent the packet and haven't cleared it out yet + if !bytes.Equal(commitment, packetCommitment) { + return sdkerrors.Wrapf(types.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment) + } + + if err := k.connectionKeeper.VerifyPacketAcknowledgement( + ctx, connectionEnd, proofHeight, proof, packet.GetDestPort(), packet.GetDestChannel(), + packet.GetSequence(), acknowledgement, + ); err != nil { + return sdkerrors.Wrap(err, "packet acknowledgement verification failed") + } + + // assert packets acknowledged in order + if channel.Ordering == types.ORDERED { + nextSequenceAck, found := k.GetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf( + types.ErrSequenceAckNotFound, + "source port: %s, source channel: %s", packet.GetSourcePort(), packet.GetSourceChannel(), + ) + } + + if packet.GetSequence() != nextSequenceAck { + return sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "packet sequence ≠ next ack sequence (%d ≠ %d)", packet.GetSequence(), nextSequenceAck, + ) + } + + // All verification complete, in the case of ORDERED channels we must increment nextSequenceAck + nextSequenceAck++ + + // incrementing NextSequenceAck and storing under this chain's channelEnd identifiers + // Since this is the original sending chain, our channelEnd is packet's source port and channel + k.SetNextSequenceAck(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), nextSequenceAck) + + } + + // Delete packet commitment, since the packet has been acknowledged, the commitement is no longer necessary + k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + // log that a packet has been acknowledged + k.Logger(ctx).Info("packet acknowledged", "packet", fmt.Sprintf("%v", packet)) + + // emit an event marking that we have processed the acknowledgement + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeAcknowledgePacket, + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + 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, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return nil +} diff --git a/x/ibc/core/04-channel/keeper/packet_test.go b/x/ibc/core/04-channel/keeper/packet_test.go new file mode 100644 index 000000000000..232e68758246 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/packet_test.go @@ -0,0 +1,665 @@ +package keeper_test + +import ( + "fmt" + + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +var ( + validPacketData = []byte("VALID PACKET DATA") + disabledTimeoutTimestamp = uint64(0) + disabledTimeoutHeight = clienttypes.ZeroHeight() + timeoutHeight = clienttypes.NewHeight(0, 100) + + // for when the testing package cannot be used + clientIDA = "clientA" + clientIDB = "clientB" + connIDA = "connA" + connIDB = "connB" + portID = "portid" + channelIDA = "channelidA" + channelIDB = "channelidB" +) + +// TestSendPacket tests SendPacket from chainA to chainB +func (suite *KeeperTestSuite) TestSendPacket() { + var ( + packet exported.PacketI + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success: UNORDERED channel", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"success: ORDERED channel", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"sending packet out of order on UNORDERED channel", func() { + // setup creates an unordered channel + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 5, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"sending packet out of order on ORDERED channel", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 5, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet basic validation failed, empty packet data", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket([]byte{}, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel closed", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, false}, + {"packet dest port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet dest channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not found", func() { + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, channelA.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"client state not found", func() { + _, _, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + // change connection client ID + connection := suite.chainA.GetConnection(connA) + connection.ClientId = ibctesting.InvalidID + suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connection) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"client state is frozen", func() { + _, _, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + + connection := suite.chainA.GetConnection(connA) + clientState := suite.chainA.GetClientState(connection.ClientId) + cs, ok := clientState.(*ibctmtypes.ClientState) + suite.Require().True(ok) + + // freeze client + cs.FrozenHeight = clienttypes.NewHeight(0, 1) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), connection.ClientId, cs) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + + {"timeout height passed", func() { + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use client state latest height for timeout + clientState := suite.chainA.GetClientState(clientA) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clientState.GetLatestHeight().(clienttypes.Height), disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"timeout timestamp passed", func() { + clientA, _, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use latest time on client state + clientState := suite.chainA.GetClientState(clientA) + connection := suite.chainA.GetConnection(connA) + timestamp, err := suite.chainA.App.IBCKeeper.ConnectionKeeper.GetTimestampAtHeight(suite.chainA.GetContext(), connection, clientState.GetLatestHeight()) + suite.Require().NoError(err) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, disabledTimeoutHeight, timestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next sequence send not found", func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA := suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB := suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // manually creating channel prevents next sequence from being set + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, channelA.Version), + ) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next sequence wrong", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 5) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = capabilitytypes.NewCapability(5) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.SetupTest() // reset + + tc.malleate() + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.SendPacket(suite.chainA.GetContext(), channelCap, packet) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} + +// TestRecvPacket test RecvPacket on chainB. Since packet commitment verification will always +// occur last (resource instensive), only tests expected to succeed and packet commitment +// verification tests need to simulate sending a packet from chainA to chainB. +func (suite *KeeperTestSuite) TestRecvPacket() { + var ( + packet exported.PacketI + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success: ORDERED channel", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, true}, + {"success UNORDERED channel", func() { + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, true}, + {"success with out of order packet: UNORDERED channel", func() { + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // send 2 packets + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // set sequence to 2 + packet = types.NewPacket(validPacketData, 2, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err = suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // attempts to receive packet 2 without receiving packet 1 + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, true}, + {"out of order packet failure with ORDERED channel", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // send 2 packets + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // set sequence to 2 + packet = types.NewPacket(validPacketData, 2, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err = suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + // attempts to receive packet 2 without receiving packet 1 + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel not open", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"capability cannot authenticate", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + channelCap = capabilitytypes.NewCapability(3) + }, false}, + {"packet source port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"packet source channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"connection not found", func() { + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connIDB}, channelB.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"connection not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + // connection on chainB is in INIT + connB, connA, err := suite.coordinator.ConnOpenInit(suite.chainB, suite.chainA, clientB, clientA) + suite.Require().NoError(err) + + channelA := suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB := suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connB.ID}, channelB.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"timeout height passed", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"timeout timestamp passed", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, disabledTimeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"next receive sequence is not found", func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA := suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB := suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + + // manually creating channel prevents next recv sequence from being set + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connB.ID}, channelB.Version), + ) + + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // manually set packet commitment + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, packet.GetSequence(), ibctesting.TestHash) + suite.chainB.CreateChannelCapability(channelB.PortID, channelB.ID) + + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"receipt already stored", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketReceipt(suite.chainB.GetContext(), channelB.PortID, channelB.ID, 1) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"validation failed", func() { + // packet commitment not set resulting in invalid proof + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.SetupTest() // reset + tc.malleate() + + // get proof of packet commitment from chainA + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.RecvPacket(suite.chainB.GetContext(), channelCap, packet, proof, proofHeight) + + if tc.expPass { + suite.Require().NoError(err) + + channelB, _ := suite.chainB.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel()) + nextSeqRecv, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetNextSequenceRecv(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel()) + suite.Require().True(found) + receipt, receiptStored := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketReceipt(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + if channelB.Ordering == types.ORDERED { + suite.Require().Equal(packet.GetSequence()+1, nextSeqRecv, "sequence not incremented in ordered channel") + suite.Require().False(receiptStored, "packet receipt stored on ORDERED channel") + } else { + suite.Require().Equal(uint64(1), nextSeqRecv, "sequence incremented for UNORDERED channel") + suite.Require().True(receiptStored, "packet receipt not stored after RecvPacket in UNORDERED channel") + suite.Require().Equal(string([]byte{byte(1)}), receipt, "packet receipt is not empty string") + } + } else { + suite.Require().Error(err) + } + }) + } + +} + +func (suite *KeeperTestSuite) TestWriteAcknowledgement() { + var ( + ack []byte + packet exported.PacketI + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + { + "success", + func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + ack = ibctesting.TestHash + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, + true, + }, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + ack = ibctesting.TestHash + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + {"channel not open", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + ack = ibctesting.TestHash + + err := suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.Require().NoError(err) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, false}, + { + "capability authentication failed", + func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + ack = ibctesting.TestHash + channelCap = capabilitytypes.NewCapability(3) + }, + false, + }, + { + "no-op, already acked", + func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + ack = ibctesting.TestHash + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ack) + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, + false, + }, + { + "empty acknowledgement", + func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + ack = nil + channelCap = suite.chainB.GetChannelCapability(channelB.PortID, channelB.ID) + }, + false, + }, + } + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.SetupTest() // reset + + tc.malleate() + + err := suite.chainB.App.IBCKeeper.ChannelKeeper.WriteAcknowledgement(suite.chainB.GetContext(), channelCap, packet, ack) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestAcknowledgePacket tests the call AcknowledgePacket on chainA. +func (suite *KeeperTestSuite) TestAcknowledgePacket() { + var ( + packet types.Packet + ack = ibcmock.MockAcknowledgement + + channelCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success on ordered channel", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet receipt and acknowledgement + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"success on unordered channel", func() { + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet receipt and acknowledgement + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"channel not open", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"capability authentication failed", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet receipt and acknowledgement + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + channelCap = capabilitytypes.NewCapability(3) + }, false}, + {"packet destination port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet destination channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not found", func() { + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainB.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainB.GetContext(), + channelB.PortID, channelB.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelA.PortID, channelA.ID), []string{connIDB}, channelB.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not OPEN", func() { + clientA, clientB := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + // connection on chainA is in INIT + connA, connB, err := suite.coordinator.ConnOpenInit(suite.chainA, suite.chainB, clientA, clientB) + suite.Require().NoError(err) + + channelA := suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB := suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, channelA.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet hasn't been sent", func() { + // packet commitment never written + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet ack verification failed", func() { + // ack never written + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create packet commitment + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next ack sequence not found", func() { + _, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, exported.Tendermint) + channelA := suite.chainA.NextTestChannel(connA, ibctesting.TransferPort) + channelB := suite.chainB.NextTestChannel(connB, ibctesting.TransferPort) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // manually creating channel prevents next sequence acknowledgement from being set + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connA.ID}, channelA.Version), + ) + // manually set packet commitment + suite.chainA.App.IBCKeeper.ChannelKeeper.SetPacketCommitment(suite.chainA.GetContext(), channelA.PortID, channelA.ID, packet.GetSequence(), ibctesting.TestHash) + + // manually set packet acknowledgement and capability + suite.chainB.App.IBCKeeper.ChannelKeeper.SetPacketAcknowledgement(suite.chainB.GetContext(), channelB.PortID, channelB.ID, packet.GetSequence(), ibctesting.TestHash) + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next ack sequence mismatch", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // create packet acknowledgement + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + // set next sequence ack wrong + suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceAck(suite.chainA.GetContext(), channelA.PortID, channelA.ID, 10) + channelCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.SetupTest() // reset + tc.malleate() + + packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetKey) + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.AcknowledgePacket(suite.chainA.GetContext(), channelCap, packet, ack, proof, proofHeight) + pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + channelA, _ := suite.chainA.App.IBCKeeper.ChannelKeeper.GetChannel(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel()) + sequenceAck, _ := suite.chainA.App.IBCKeeper.ChannelKeeper.GetNextSequenceAck(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel()) + + if tc.expPass { + suite.NoError(err) + suite.Nil(pc) + + if channelA.Ordering == types.ORDERED { + suite.Require().Equal(packet.GetSequence()+1, sequenceAck, "sequence not incremented in ordered channel") + } else { + suite.Require().Equal(uint64(1), sequenceAck, "sequence incremented for UNORDERED channel") + } + } else { + suite.Error(err) + } + }) + } +} diff --git a/x/ibc/core/04-channel/keeper/timeout.go b/x/ibc/core/04-channel/keeper/timeout.go new file mode 100644 index 000000000000..1f3dac918f67 --- /dev/null +++ b/x/ibc/core/04-channel/keeper/timeout.go @@ -0,0 +1,276 @@ +package keeper + +import ( + "bytes" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// TimeoutPacket is called by a module which originally attempted to send a +// packet to a counterparty module, where the timeout height has passed on the +// counterparty chain without the packet being committed, to prove that the +// packet can no longer be executed and to allow the calling module to safely +// perform appropriate state transitions. Its intended usage is within the +// ante handler. +func (k Keeper) TimeoutPacket( + ctx sdk.Context, + packet exported.PacketI, + proof []byte, + proofHeight exported.Height, + nextSequenceRecv uint64, +) error { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf( + types.ErrChannelNotFound, + "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel(), + ) + } + + if channel.State != types.OPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) + } + + // NOTE: TimeoutPacket is called by the AnteHandler which acts upon the packet.Route(), + // so the capability authentication can be omitted here + + if packet.GetDestPort() != channel.Counterparty.PortId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, + ) + } + + if packet.GetDestChannel() != channel.Counterparty.ChannelId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, + ) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap( + connectiontypes.ErrConnectionNotFound, + channel.ConnectionHops[0], + ) + } + + // check that timeout height or timeout timestamp has passed on the other end + proofTimestamp, err := k.connectionKeeper.GetTimestampAtHeight(ctx, connectionEnd, proofHeight) + if err != nil { + return err + } + + timeoutHeight := packet.GetTimeoutHeight() + if (timeoutHeight.IsZero() || proofHeight.LT(timeoutHeight)) && + (packet.GetTimeoutTimestamp() == 0 || proofTimestamp < packet.GetTimeoutTimestamp()) { + return sdkerrors.Wrap(types.ErrPacketTimeout, "packet timeout has not been reached for height or timestamp") + } + + commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + packetCommitment := types.CommitPacket(k.cdc, packet) + + // verify we sent the packet and haven't cleared it out yet + if !bytes.Equal(commitment, packetCommitment) { + return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) + } + + switch channel.Ordering { + case types.ORDERED: + // check that packet has not been received + if nextSequenceRecv > packet.GetSequence() { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet already received, next sequence receive > packet sequence (%d > %d)", nextSequenceRecv, packet.GetSequence(), + ) + } + + // check that the recv sequence is as claimed + err = k.connectionKeeper.VerifyNextSequenceRecv( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, + ) + case types.UNORDERED: + err = k.connectionKeeper.VerifyPacketReceiptAbsence( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + ) + default: + panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) + } + + if err != nil { + return err + } + + // NOTE: the remaining code is located in the TimeoutExecuted function + return nil +} + +// TimeoutExecuted deletes the commitment send from this chain after it verifies timeout. +// If the timed-out packet came from an ORDERED channel then this channel will be closed. +// +// CONTRACT: this function must be called in the IBC handler +func (k Keeper) TimeoutExecuted( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, +) error { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + } + + capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { + return sdkerrors.Wrapf( + types.ErrChannelCapabilityNotFound, + "caller does not own capability for channel with capability name %s", capName, + ) + } + + k.deletePacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + if channel.Ordering == types.ORDERED { + channel.State = types.CLOSED + k.SetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), channel) + } + + k.Logger(ctx).Info("packet timed-out", "packet", fmt.Sprintf("%v", packet)) + + // emit an event marking that we have processed the timeout + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTimeoutPacket, + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), + sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), + sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) + + return nil +} + +// TimeoutOnClose is called by a module in order to prove that the channel to +// which an unreceived packet was addressed has been closed, so the packet will +// never be received (even if the timeoutHeight has not yet been reached). +func (k Keeper) TimeoutOnClose( + ctx sdk.Context, + chanCap *capabilitytypes.Capability, + packet exported.PacketI, + proof, + proofClosed []byte, + proofHeight exported.Height, + nextSequenceRecv uint64, +) error { + channel, found := k.GetChannel(ctx, packet.GetSourcePort(), packet.GetSourceChannel()) + if !found { + return sdkerrors.Wrapf(types.ErrChannelNotFound, "port ID (%s) channel ID (%s)", packet.GetSourcePort(), packet.GetSourceChannel()) + } + + capName := host.ChannelCapabilityPath(packet.GetSourcePort(), packet.GetSourceChannel()) + if !k.scopedKeeper.AuthenticateCapability(ctx, chanCap, capName) { + return sdkerrors.Wrapf( + types.ErrInvalidChannelCapability, + "channel capability failed authentication with capability name %s", capName, + ) + } + + if packet.GetDestPort() != channel.Counterparty.PortId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination port doesn't match the counterparty's port (%s ≠ %s)", packet.GetDestPort(), channel.Counterparty.PortId, + ) + } + + if packet.GetDestChannel() != channel.Counterparty.ChannelId { + return sdkerrors.Wrapf( + types.ErrInvalidPacket, + "packet destination channel doesn't match the counterparty's channel (%s ≠ %s)", packet.GetDestChannel(), channel.Counterparty.ChannelId, + ) + } + + connectionEnd, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0]) + if !found { + return sdkerrors.Wrap(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0]) + } + + commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + packetCommitment := types.CommitPacket(k.cdc, packet) + + // verify we sent the packet and haven't cleared it out yet + if !bytes.Equal(commitment, packetCommitment) { + return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) + } + + counterpartyHops, found := k.CounterpartyHops(ctx, channel) + if !found { + // Should not reach here, connectionEnd was able to be retrieved above + panic("cannot find connection") + } + + counterparty := types.NewCounterparty(packet.GetSourcePort(), packet.GetSourceChannel()) + expectedChannel := types.NewChannel( + types.CLOSED, channel.Ordering, counterparty, counterpartyHops, channel.Version, + ) + + // check that the opposing channel end has closed + if err := k.connectionKeeper.VerifyChannelState( + ctx, connectionEnd, proofHeight, proofClosed, + channel.Counterparty.PortId, channel.Counterparty.ChannelId, + expectedChannel, + ); err != nil { + return err + } + + var err error + switch channel.Ordering { + case types.ORDERED: + // check that packet has not been received + if nextSequenceRecv > packet.GetSequence() { + return sdkerrors.Wrapf(types.ErrInvalidPacket, "packet already received, next sequence receive > packet sequence (%d > %d", nextSequenceRecv, packet.GetSequence()) + } + + // check that the recv sequence is as claimed + err = k.connectionKeeper.VerifyNextSequenceRecv( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv, + ) + case types.UNORDERED: + err = k.connectionKeeper.VerifyPacketReceiptAbsence( + ctx, connectionEnd, proofHeight, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + ) + default: + panic(sdkerrors.Wrapf(types.ErrInvalidChannelOrdering, channel.Ordering.String())) + } + + if err != nil { + return err + } + + // NOTE: the remaining code is located in the TimeoutExecuted function + return nil +} diff --git a/x/ibc/core/04-channel/keeper/timeout_test.go b/x/ibc/core/04-channel/keeper/timeout_test.go new file mode 100644 index 000000000000..640452e881be --- /dev/null +++ b/x/ibc/core/04-channel/keeper/timeout_test.go @@ -0,0 +1,351 @@ +package keeper_test + +import ( + "fmt" + + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +// TestTimeoutPacket test the TimeoutPacket call on chainA by ensuring the timeout has passed +// on chainB, but that no ack has been written yet. Test cases expected to reach proof +// verification must specify which proof to use using the ordered bool. +func (suite *KeeperTestSuite) TestTimeoutPacket() { + var ( + packet types.Packet + nextSeqRecv uint64 + ordered bool + ) + + testCases := []testCase{ + {"success: ORDERED", func() { + ordered = true + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, true}, + {"success: UNORDERED", func() { + ordered = false + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"channel not open", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA) + suite.Require().NoError(err) + }, false}, + {"packet destination port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"packet destination channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"connection not found", func() { + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, channelA.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"timeout", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, false}, + {"packet already received ", func() { + ordered = true + nextSeqRecv = 2 + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, false}, + {"packet hasn't been sent", func() { + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, false}, + {"next seq receive verification failed", func() { + // set ordered to false resulting in wrong proof provided + ordered = false + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, false}, + {"packet ack verification failed", func() { + // set ordered to true resulting in wrong proof provided + ordered = true + + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + var ( + proof []byte + proofHeight exported.Height + ) + + suite.SetupTest() // reset + nextSeqRecv = 1 // must be explicitly changed + tc.malleate() + + orderedPacketKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + unorderedPacketKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + if ordered { + proof, proofHeight = suite.chainB.QueryProof(orderedPacketKey) + } else { + proof, proofHeight = suite.chainB.QueryProof(unorderedPacketKey) + } + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutPacket(suite.chainA.GetContext(), packet, proof, proofHeight, nextSeqRecv) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// TestTimeoutExectued verifies that packet commitments are deleted on chainA after the +// channel capabilities are verified. +func (suite *KeeperTestSuite) TestTimeoutExecuted() { + var ( + packet types.Packet + chanCap *capabilitytypes.Capability + ) + + testCases := []testCase{ + {"success ORDERED", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"incorrect capability", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + + chanCap = capabilitytypes.NewCapability(100) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + suite.SetupTest() // reset + + tc.malleate() + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutExecuted(suite.chainA.GetContext(), chanCap, packet) + pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + + if tc.expPass { + suite.NoError(err) + suite.Nil(pc) + } else { + suite.Error(err) + } + }) + } +} + +// TestTimeoutOnClose tests the call TimeoutOnClose on chainA by closing the corresponding +// channel on chainB after the packet commitment has been created. +func (suite *KeeperTestSuite) TestTimeoutOnClose() { + var ( + packet types.Packet + chanCap *capabilitytypes.Capability + nextSeqRecv uint64 + ordered bool + ) + + testCases := []testCase{ + {"success: ORDERED", func() { + ordered = true + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"success: UNORDERED", func() { + ordered = false + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, true}, + {"channel not found", func() { + // use wrong channel naming + _, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + }, false}, + {"packet dest port ≠ channel counterparty port", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong port for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet dest channel ID ≠ channel counterparty channel ID", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + // use wrong channel for dest + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"connection not found", func() { + channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA} + channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB} + // pass channel check + suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel( + suite.chainA.GetContext(), + channelA.PortID, channelA.ID, + types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, channelA.Version), + ) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp) + + // create chancap + suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet hasn't been sent", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet already received", func() { + nextSeqRecv = 2 + ordered = true + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel verification failed", func() { + ordered = true + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"next seq receive verification failed", func() { + // set ordered to false providing the wrong proof for ORDERED case + ordered = false + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"packet ack verification failed", func() { + // set ordered to true providing the wrong proof for UNORDERED case + ordered = true + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.UNORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), disabledTimeoutTimestamp) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID) + }, false}, + {"channel capability not found", func() { + ordered = true + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, types.ORDERED) + packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB) + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + chanCap = capabilitytypes.NewCapability(100) + }, false}, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() { + var proof []byte + + suite.SetupTest() // reset + nextSeqRecv = 1 // must be explicitly changed + tc.malleate() + + channelKey := host.ChannelKey(packet.GetDestPort(), packet.GetDestChannel()) + unorderedPacketKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + orderedPacketKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + + proofClosed, proofHeight := suite.chainB.QueryProof(channelKey) + + if ordered { + proof, _ = suite.chainB.QueryProof(orderedPacketKey) + } else { + proof, _ = suite.chainB.QueryProof(unorderedPacketKey) + } + + err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutOnClose(suite.chainA.GetContext(), chanCap, packet, proof, proofClosed, proofHeight, nextSeqRecv) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} diff --git a/x/ibc/core/04-channel/module.go b/x/ibc/core/04-channel/module.go new file mode 100644 index 000000000000..569120ad921d --- /dev/null +++ b/x/ibc/core/04-channel/module.go @@ -0,0 +1,29 @@ +package channel + +import ( + "github.com/gogo/protobuf/grpc" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// Name returns the IBC channel ICS name. +func Name() string { + return types.SubModuleName +} + +// GetTxCmd returns the root tx command for IBC channels. +func GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd returns the root query command for IBC channels. +func GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterQueryService registers the gRPC query service for IBC channels. +func RegisterQueryService(server grpc.Server, queryServer types.QueryServer) { + types.RegisterQueryServer(server, queryServer) +} diff --git a/x/ibc/core/04-channel/simulation/decoder.go b/x/ibc/core/04-channel/simulation/decoder.go new file mode 100644 index 000000000000..809976cc0e23 --- /dev/null +++ b/x/ibc/core/04-channel/simulation/decoder.go @@ -0,0 +1,48 @@ +package simulation + +import ( + "bytes" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding channel type. +func NewDecodeStore(cdc codec.BinaryMarshaler, kvA, kvB kv.Pair) (string, bool) { + switch { + case bytes.HasPrefix(kvA.Key, []byte(host.KeyChannelEndPrefix)): + var channelA, channelB types.Channel + cdc.MustUnmarshalBinaryBare(kvA.Value, &channelA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &channelB) + return fmt.Sprintf("Channel A: %v\nChannel B: %v", channelA, channelB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqSendPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqSend A: %d\nNextSeqSend B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqRecvPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqRecv A: %d\nNextSeqRecv B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyNextSeqAckPrefix)): + seqA := sdk.BigEndianToUint64(kvA.Value) + seqB := sdk.BigEndianToUint64(kvB.Value) + return fmt.Sprintf("NextSeqAck A: %d\nNextSeqAck B: %d", seqA, seqB), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyPacketCommitmentPrefix)): + return fmt.Sprintf("CommitmentHash A: %X\nCommitmentHash B: %X", kvA.Value, kvB.Value), true + + case bytes.HasPrefix(kvA.Key, []byte(host.KeyPacketAckPrefix)): + return fmt.Sprintf("AckHash A: %X\nAckHash B: %X", kvA.Value, kvB.Value), true + + default: + return "", false + } +} diff --git a/x/ibc/core/04-channel/simulation/decoder_test.go b/x/ibc/core/04-channel/simulation/decoder_test.go new file mode 100644 index 000000000000..5f2ba2f5ecbf --- /dev/null +++ b/x/ibc/core/04-channel/simulation/decoder_test.go @@ -0,0 +1,89 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "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/ibc/core/04-channel/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + cdc := app.AppCodec() + + channelID := "channelidone" + portID := "portidone" + + channel := types.Channel{ + State: types.OPEN, + Version: "1.0", + } + + bz := []byte{0x1, 0x2, 0x3} + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: host.ChannelKey(portID, channelID), + Value: cdc.MustMarshalBinaryBare(&channel), + }, + { + Key: host.NextSequenceSendKey(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + { + Key: host.NextSequenceRecvKey(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + { + Key: host.NextSequenceAckKey(portID, channelID), + Value: sdk.Uint64ToBigEndian(1), + }, + { + Key: host.PacketCommitmentKey(portID, channelID, 1), + Value: bz, + }, + { + Key: host.PacketAcknowledgementKey(portID, channelID, 1), + Value: bz, + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"Channel", fmt.Sprintf("Channel A: %v\nChannel B: %v", channel, channel)}, + {"NextSeqSend", "NextSeqSend A: 1\nNextSeqSend B: 1"}, + {"NextSeqRecv", "NextSeqRecv A: 1\nNextSeqRecv B: 1"}, + {"NextSeqAck", "NextSeqAck A: 1\nNextSeqAck B: 1"}, + {"CommitmentHash", fmt.Sprintf("CommitmentHash A: %X\nCommitmentHash B: %X", bz, bz)}, + {"AckHash", fmt.Sprintf("AckHash A: %X\nAckHash B: %X", bz, bz)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + res, found := simulation.NewDecodeStore(cdc, kvPairs.Pairs[i], kvPairs.Pairs[i]) + if i == len(tests)-1 { + require.False(t, found, string(kvPairs.Pairs[i].Key)) + require.Empty(t, res, string(kvPairs.Pairs[i].Key)) + } else { + require.True(t, found, string(kvPairs.Pairs[i].Key)) + require.Equal(t, tt.expectedLog, res, string(kvPairs.Pairs[i].Key)) + } + }) + } +} diff --git a/x/ibc/core/04-channel/simulation/genesis.go b/x/ibc/core/04-channel/simulation/genesis.go new file mode 100644 index 000000000000..ed33902191b9 --- /dev/null +++ b/x/ibc/core/04-channel/simulation/genesis.go @@ -0,0 +1,13 @@ +package simulation + +import ( + "math/rand" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// GenChannelGenesis returns the default channel genesis state. +func GenChannelGenesis(_ *rand.Rand, _ []simtypes.Account) types.GenesisState { + return types.DefaultGenesisState() +} diff --git a/x/ibc/core/04-channel/types/channel.go b/x/ibc/core/04-channel/types/channel.go new file mode 100644 index 000000000000..8513a8123df8 --- /dev/null +++ b/x/ibc/core/04-channel/types/channel.go @@ -0,0 +1,172 @@ +package types + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + _ exported.ChannelI = (*Channel)(nil) + _ exported.CounterpartyChannelI = (*Counterparty)(nil) +) + +// NewChannel creates a new Channel instance +func NewChannel( + state State, ordering Order, counterparty Counterparty, + hops []string, version string, +) Channel { + return Channel{ + State: state, + Ordering: ordering, + Counterparty: counterparty, + ConnectionHops: hops, + Version: version, + } +} + +// GetState implements Channel interface. +func (ch Channel) GetState() int32 { + return int32(ch.State) +} + +// GetOrdering implements Channel interface. +func (ch Channel) GetOrdering() int32 { + return int32(ch.Ordering) +} + +// GetCounterparty implements Channel interface. +func (ch Channel) GetCounterparty() exported.CounterpartyChannelI { + return ch.Counterparty +} + +// GetConnectionHops implements Channel interface. +func (ch Channel) GetConnectionHops() []string { + return ch.ConnectionHops +} + +// GetVersion implements Channel interface. +func (ch Channel) GetVersion() string { + return ch.Version +} + +// ValidateBasic performs a basic validation of the channel fields +func (ch Channel) ValidateBasic() error { + if ch.State == UNINITIALIZED { + return ErrInvalidChannelState + } + if !(ch.Ordering == ORDERED || ch.Ordering == UNORDERED) { + return sdkerrors.Wrap(ErrInvalidChannelOrdering, ch.Ordering.String()) + } + if len(ch.ConnectionHops) != 1 { + return sdkerrors.Wrap( + ErrTooManyConnectionHops, + "current IBC version only supports one connection hop", + ) + } + if err := host.ConnectionIdentifierValidator(ch.ConnectionHops[0]); err != nil { + return sdkerrors.Wrap(err, "invalid connection hop ID") + } + return ch.Counterparty.ValidateBasic() +} + +// NewCounterparty returns a new Counterparty instance +func NewCounterparty(portID, channelID string) Counterparty { + return Counterparty{ + PortId: portID, + ChannelId: channelID, + } +} + +// GetPortID implements CounterpartyChannelI interface +func (c Counterparty) GetPortID() string { + return c.PortId +} + +// GetChannelID implements CounterpartyChannelI interface +func (c Counterparty) GetChannelID() string { + return c.ChannelId +} + +// ValidateBasic performs a basic validation check of the identifiers +func (c Counterparty) ValidateBasic() error { + if err := host.PortIdentifierValidator(c.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty port ID") + } + if c.ChannelId != "" { + if err := host.ChannelIdentifierValidator(c.ChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty channel ID") + } + } + return nil +} + +// NewIdentifiedChannel creates a new IdentifiedChannel instance +func NewIdentifiedChannel(portID, channelID string, ch Channel) IdentifiedChannel { + return IdentifiedChannel{ + State: ch.State, + Ordering: ch.Ordering, + Counterparty: ch.Counterparty, + ConnectionHops: ch.ConnectionHops, + Version: ch.Version, + PortId: portID, + ChannelId: channelID, + } +} + +// ValidateBasic performs a basic validation of the identifiers and channel fields. +func (ic IdentifiedChannel) ValidateBasic() error { + if err := host.ChannelIdentifierValidator(ic.ChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid channel ID") + } + if err := host.PortIdentifierValidator(ic.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + channel := NewChannel(ic.State, ic.Ordering, ic.Counterparty, ic.ConnectionHops, ic.Version) + return channel.ValidateBasic() +} + +// NewResultAcknowledgement returns a new instance of Acknowledgement using an Acknowledgement_Result +// type in the Response field. +func NewResultAcknowledgement(result []byte) Acknowledgement { + return Acknowledgement{ + Response: &Acknowledgement_Result{ + Result: result, + }, + } +} + +// NewErrorAcknowledgement returns a new instance of Acknowledgement using an Acknowledgement_Error +// type in the Response field. +func NewErrorAcknowledgement(err string) Acknowledgement { + return Acknowledgement{ + Response: &Acknowledgement_Error{ + Error: err, + }, + } +} + +// GetBytes is a helper for serialising acknowledgements +func (ack Acknowledgement) GetBytes() []byte { + return sdk.MustSortJSON(SubModuleCdc.MustMarshalJSON(&ack)) +} + +// ValidateBasic performs a basic validation of the acknowledgement +func (ack Acknowledgement) ValidateBasic() error { + switch resp := ack.Response.(type) { + case *Acknowledgement_Result: + if len(resp.Result) == 0 { + return sdkerrors.Wrap(ErrInvalidAcknowledgement, "acknowledgement result cannot be empty") + } + case *Acknowledgement_Error: + if strings.TrimSpace(resp.Error) == "" { + return sdkerrors.Wrap(ErrInvalidAcknowledgement, "acknowledgement error cannot be empty") + } + default: + return sdkerrors.Wrapf(ErrInvalidAcknowledgement, "unsupported acknowledgement response field type %T", resp) + } + return nil +} diff --git a/x/ibc/core/04-channel/types/channel.pb.go b/x/ibc/core/04-channel/types/channel.pb.go new file mode 100644 index 000000000000..1384a150d578 --- /dev/null +++ b/x/ibc/core/04-channel/types/channel.pb.go @@ -0,0 +1,2268 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/channel/v1/channel.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "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 + +// State defines if a channel is in one of the following states: +// CLOSED, INIT, TRYOPEN, OPEN or UNINITIALIZED. +type State int32 + +const ( + // Default State + UNINITIALIZED State = 0 + // A channel has just started the opening handshake. + INIT State = 1 + // A channel has acknowledged the handshake step on the counterparty chain. + TRYOPEN State = 2 + // A channel has completed the handshake. Open channels are + // ready to send and receive packets. + OPEN State = 3 + // A channel has been closed and can no longer be used to send or receive + // packets. + CLOSED State = 4 +) + +var State_name = map[int32]string{ + 0: "STATE_UNINITIALIZED_UNSPECIFIED", + 1: "STATE_INIT", + 2: "STATE_TRYOPEN", + 3: "STATE_OPEN", + 4: "STATE_CLOSED", +} + +var State_value = map[string]int32{ + "STATE_UNINITIALIZED_UNSPECIFIED": 0, + "STATE_INIT": 1, + "STATE_TRYOPEN": 2, + "STATE_OPEN": 3, + "STATE_CLOSED": 4, +} + +func (x State) String() string { + return proto.EnumName(State_name, int32(x)) +} + +func (State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{0} +} + +// Order defines if a channel is ORDERED or UNORDERED +type Order int32 + +const ( + // zero-value for channel ordering + NONE Order = 0 + // packets can be delivered in any order, which may differ from the order in + // which they were sent. + UNORDERED Order = 1 + // packets are delivered exactly in the order which they were sent + ORDERED Order = 2 +) + +var Order_name = map[int32]string{ + 0: "ORDER_NONE_UNSPECIFIED", + 1: "ORDER_UNORDERED", + 2: "ORDER_ORDERED", +} + +var Order_value = map[string]int32{ + "ORDER_NONE_UNSPECIFIED": 0, + "ORDER_UNORDERED": 1, + "ORDER_ORDERED": 2, +} + +func (x Order) String() string { + return proto.EnumName(Order_name, int32(x)) +} + +func (Order) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{1} +} + +// Channel defines pipeline for exactly-once packet delivery between specific +// modules on separate blockchains, which has at least one end capable of +// sending packets and one end capable of receiving packets. +type Channel struct { + // current state of the channel end + State State `protobuf:"varint,1,opt,name=state,proto3,enum=ibc.core.channel.v1.State" json:"state,omitempty"` + // whether the channel is ordered or unordered + Ordering Order `protobuf:"varint,2,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` + // counterparty channel end + Counterparty Counterparty `protobuf:"bytes,3,opt,name=counterparty,proto3" json:"counterparty"` + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + ConnectionHops []string `protobuf:"bytes,4,rep,name=connection_hops,json=connectionHops,proto3" json:"connection_hops,omitempty" yaml:"connection_hops"` + // opaque channel version, which is agreed upon during the handshake + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *Channel) Reset() { *m = Channel{} } +func (m *Channel) String() string { return proto.CompactTextString(m) } +func (*Channel) ProtoMessage() {} +func (*Channel) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{0} +} +func (m *Channel) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Channel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Channel.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 *Channel) XXX_Merge(src proto.Message) { + xxx_messageInfo_Channel.Merge(m, src) +} +func (m *Channel) XXX_Size() int { + return m.Size() +} +func (m *Channel) XXX_DiscardUnknown() { + xxx_messageInfo_Channel.DiscardUnknown(m) +} + +var xxx_messageInfo_Channel proto.InternalMessageInfo + +// IdentifiedChannel defines a channel with additional port and channel +// identifier fields. +type IdentifiedChannel struct { + // current state of the channel end + State State `protobuf:"varint,1,opt,name=state,proto3,enum=ibc.core.channel.v1.State" json:"state,omitempty"` + // whether the channel is ordered or unordered + Ordering Order `protobuf:"varint,2,opt,name=ordering,proto3,enum=ibc.core.channel.v1.Order" json:"ordering,omitempty"` + // counterparty channel end + Counterparty Counterparty `protobuf:"bytes,3,opt,name=counterparty,proto3" json:"counterparty"` + // list of connection identifiers, in order, along which packets sent on + // this channel will travel + ConnectionHops []string `protobuf:"bytes,4,rep,name=connection_hops,json=connectionHops,proto3" json:"connection_hops,omitempty" yaml:"connection_hops"` + // opaque channel version, which is agreed upon during the handshake + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` + // port identifier + PortId string `protobuf:"bytes,6,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel identifier + ChannelId string `protobuf:"bytes,7,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` +} + +func (m *IdentifiedChannel) Reset() { *m = IdentifiedChannel{} } +func (m *IdentifiedChannel) String() string { return proto.CompactTextString(m) } +func (*IdentifiedChannel) ProtoMessage() {} +func (*IdentifiedChannel) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{1} +} +func (m *IdentifiedChannel) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IdentifiedChannel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IdentifiedChannel.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 *IdentifiedChannel) XXX_Merge(src proto.Message) { + xxx_messageInfo_IdentifiedChannel.Merge(m, src) +} +func (m *IdentifiedChannel) XXX_Size() int { + return m.Size() +} +func (m *IdentifiedChannel) XXX_DiscardUnknown() { + xxx_messageInfo_IdentifiedChannel.DiscardUnknown(m) +} + +var xxx_messageInfo_IdentifiedChannel proto.InternalMessageInfo + +// Counterparty defines a channel end counterparty +type Counterparty struct { + // port on the counterparty chain which owns the other end of the channel. + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // channel end on the counterparty chain + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` +} + +func (m *Counterparty) Reset() { *m = Counterparty{} } +func (m *Counterparty) String() string { return proto.CompactTextString(m) } +func (*Counterparty) ProtoMessage() {} +func (*Counterparty) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{2} +} +func (m *Counterparty) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Counterparty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Counterparty.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 *Counterparty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Counterparty.Merge(m, src) +} +func (m *Counterparty) XXX_Size() int { + return m.Size() +} +func (m *Counterparty) XXX_DiscardUnknown() { + xxx_messageInfo_Counterparty.DiscardUnknown(m) +} + +var xxx_messageInfo_Counterparty proto.InternalMessageInfo + +// Packet defines a type that carries data across different chains through IBC +type Packet struct { + // number corresponds to the order of sends and receives, where a Packet + // with an earlier sequence number must be sent and received before a Packet + // with a later sequence number. + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // identifies the port on the sending chain. + SourcePort string `protobuf:"bytes,2,opt,name=source_port,json=sourcePort,proto3" json:"source_port,omitempty" yaml:"source_port"` + // identifies the channel end on the sending chain. + SourceChannel string `protobuf:"bytes,3,opt,name=source_channel,json=sourceChannel,proto3" json:"source_channel,omitempty" yaml:"source_channel"` + // identifies the port on the receiving chain. + DestinationPort string `protobuf:"bytes,4,opt,name=destination_port,json=destinationPort,proto3" json:"destination_port,omitempty" yaml:"destination_port"` + // identifies the channel end on the receiving chain. + DestinationChannel string `protobuf:"bytes,5,opt,name=destination_channel,json=destinationChannel,proto3" json:"destination_channel,omitempty" yaml:"destination_channel"` + // actual opaque bytes transferred directly to the application module + Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` + // block height after which the packet times out + TimeoutHeight types.Height `protobuf:"bytes,7,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height" yaml:"timeout_height"` + // block timestamp (in nanoseconds) after which the packet times out + TimeoutTimestamp uint64 `protobuf:"varint,8,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty" yaml:"timeout_timestamp"` +} + +func (m *Packet) Reset() { *m = Packet{} } +func (m *Packet) String() string { return proto.CompactTextString(m) } +func (*Packet) ProtoMessage() {} +func (*Packet) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{3} +} +func (m *Packet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Packet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Packet.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 *Packet) XXX_Merge(src proto.Message) { + xxx_messageInfo_Packet.Merge(m, src) +} +func (m *Packet) XXX_Size() int { + return m.Size() +} +func (m *Packet) XXX_DiscardUnknown() { + xxx_messageInfo_Packet.DiscardUnknown(m) +} + +var xxx_messageInfo_Packet proto.InternalMessageInfo + +// PacketState defines the generic type necessary to retrieve and store +// packet commitments, acknowledgements, and receipts. +// Caller is responsible for knowing the context necessary to interpret this +// state as a commitment, acknowledgement, or a receipt. +type PacketState struct { + // channel port identifier. + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // channel unique identifier. + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + // packet sequence. + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` + // embedded data that represents packet state. + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *PacketState) Reset() { *m = PacketState{} } +func (m *PacketState) String() string { return proto.CompactTextString(m) } +func (*PacketState) ProtoMessage() {} +func (*PacketState) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{4} +} +func (m *PacketState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketState.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 *PacketState) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketState.Merge(m, src) +} +func (m *PacketState) XXX_Size() int { + return m.Size() +} +func (m *PacketState) XXX_DiscardUnknown() { + xxx_messageInfo_PacketState.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketState proto.InternalMessageInfo + +// Acknowledgement is the recommended acknowledgement format to be used by +// app-specific protocols. +// NOTE: The field numbers 21 and 22 were explicitly chosen to avoid accidental +// conflicts with other protobuf message formats used for acknowledgements. +// The first byte of any message with this format will be the non-ASCII values +// `0xaa` (result) or `0xb2` (error). Implemented as defined by ICS: +// https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#acknowledgement-envelope +type Acknowledgement struct { + // response contains either a result or an error and must be non-empty + // + // Types that are valid to be assigned to Response: + // *Acknowledgement_Result + // *Acknowledgement_Error + Response isAcknowledgement_Response `protobuf_oneof:"response"` +} + +func (m *Acknowledgement) Reset() { *m = Acknowledgement{} } +func (m *Acknowledgement) String() string { return proto.CompactTextString(m) } +func (*Acknowledgement) ProtoMessage() {} +func (*Acknowledgement) Descriptor() ([]byte, []int) { + return fileDescriptor_c3a07336710636a0, []int{5} +} +func (m *Acknowledgement) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Acknowledgement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Acknowledgement.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 *Acknowledgement) XXX_Merge(src proto.Message) { + xxx_messageInfo_Acknowledgement.Merge(m, src) +} +func (m *Acknowledgement) XXX_Size() int { + return m.Size() +} +func (m *Acknowledgement) XXX_DiscardUnknown() { + xxx_messageInfo_Acknowledgement.DiscardUnknown(m) +} + +var xxx_messageInfo_Acknowledgement proto.InternalMessageInfo + +type isAcknowledgement_Response interface { + isAcknowledgement_Response() + MarshalTo([]byte) (int, error) + Size() int +} + +type Acknowledgement_Result struct { + Result []byte `protobuf:"bytes,21,opt,name=result,proto3,oneof" json:"result,omitempty"` +} +type Acknowledgement_Error struct { + Error string `protobuf:"bytes,22,opt,name=error,proto3,oneof" json:"error,omitempty"` +} + +func (*Acknowledgement_Result) isAcknowledgement_Response() {} +func (*Acknowledgement_Error) isAcknowledgement_Response() {} + +func (m *Acknowledgement) GetResponse() isAcknowledgement_Response { + if m != nil { + return m.Response + } + return nil +} + +func (m *Acknowledgement) GetResult() []byte { + if x, ok := m.GetResponse().(*Acknowledgement_Result); ok { + return x.Result + } + return nil +} + +func (m *Acknowledgement) GetError() string { + if x, ok := m.GetResponse().(*Acknowledgement_Error); ok { + return x.Error + } + return "" +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Acknowledgement) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Acknowledgement_Result)(nil), + (*Acknowledgement_Error)(nil), + } +} + +func init() { + proto.RegisterEnum("ibc.core.channel.v1.State", State_name, State_value) + proto.RegisterEnum("ibc.core.channel.v1.Order", Order_name, Order_value) + proto.RegisterType((*Channel)(nil), "ibc.core.channel.v1.Channel") + proto.RegisterType((*IdentifiedChannel)(nil), "ibc.core.channel.v1.IdentifiedChannel") + proto.RegisterType((*Counterparty)(nil), "ibc.core.channel.v1.Counterparty") + proto.RegisterType((*Packet)(nil), "ibc.core.channel.v1.Packet") + proto.RegisterType((*PacketState)(nil), "ibc.core.channel.v1.PacketState") + proto.RegisterType((*Acknowledgement)(nil), "ibc.core.channel.v1.Acknowledgement") +} + +func init() { proto.RegisterFile("ibc/core/channel/v1/channel.proto", fileDescriptor_c3a07336710636a0) } + +var fileDescriptor_c3a07336710636a0 = []byte{ + // 904 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0xcb, 0x6e, 0xdb, 0x46, + 0x14, 0x15, 0x25, 0xea, 0x75, 0x65, 0xc9, 0xf2, 0xa4, 0x56, 0x58, 0x36, 0x11, 0x15, 0xa2, 0x0b, + 0x23, 0x45, 0xa4, 0x38, 0x0d, 0xda, 0x22, 0xab, 0x5a, 0x8f, 0xc0, 0x44, 0x03, 0xc9, 0xa0, 0xe4, + 0x45, 0xb3, 0x51, 0x65, 0x72, 0x2a, 0x11, 0x96, 0x38, 0x2a, 0x39, 0xb2, 0xeb, 0x3f, 0x08, 0xb4, + 0xea, 0x0f, 0x08, 0x28, 0x50, 0xb4, 0xbf, 0xd0, 0x5f, 0xc8, 0x32, 0xcb, 0xae, 0x88, 0xc2, 0x5e, + 0x74, 0xaf, 0x1f, 0x68, 0xc1, 0x99, 0xa1, 0x1e, 0x4e, 0xe0, 0x65, 0x57, 0x59, 0x71, 0xee, 0x39, + 0xe7, 0x3e, 0x74, 0xef, 0xd5, 0x0c, 0x3c, 0x72, 0xce, 0xac, 0x9a, 0x45, 0x3c, 0x5c, 0xb3, 0x46, + 0x03, 0xd7, 0xc5, 0xe3, 0xda, 0xc5, 0x61, 0x74, 0xac, 0x4e, 0x3d, 0x42, 0x09, 0xba, 0xe7, 0x9c, + 0x59, 0xd5, 0x50, 0x52, 0x8d, 0xf0, 0x8b, 0x43, 0xf5, 0x93, 0x21, 0x19, 0x12, 0xc6, 0xd7, 0xc2, + 0x13, 0x97, 0xaa, 0xda, 0x3a, 0xda, 0xd8, 0xc1, 0x2e, 0x65, 0xc1, 0xd8, 0x89, 0x0b, 0xf4, 0xdf, + 0xe3, 0x90, 0x6e, 0xf0, 0x28, 0xe8, 0x29, 0x24, 0x7d, 0x3a, 0xa0, 0x58, 0x91, 0x2a, 0xd2, 0x41, + 0xe1, 0x99, 0x5a, 0xfd, 0x40, 0x9e, 0x6a, 0x37, 0x54, 0x98, 0x5c, 0x88, 0xbe, 0x82, 0x0c, 0xf1, + 0x6c, 0xec, 0x39, 0xee, 0x50, 0x89, 0xdf, 0xe1, 0xd4, 0x09, 0x45, 0xe6, 0x4a, 0x8b, 0xbe, 0x83, + 0x1d, 0x8b, 0xcc, 0x5c, 0x8a, 0xbd, 0xe9, 0xc0, 0xa3, 0x57, 0x4a, 0xa2, 0x22, 0x1d, 0xe4, 0x9e, + 0x3d, 0xfa, 0xa0, 0x6f, 0x63, 0x43, 0x58, 0x97, 0xdf, 0x06, 0x5a, 0xcc, 0xdc, 0x72, 0x46, 0x0d, + 0xd8, 0xb5, 0x88, 0xeb, 0x62, 0x8b, 0x3a, 0xc4, 0xed, 0x8f, 0xc8, 0xd4, 0x57, 0xe4, 0x4a, 0xe2, + 0x20, 0x5b, 0x57, 0x97, 0x81, 0x56, 0xba, 0x1a, 0x4c, 0xc6, 0x2f, 0xf4, 0x5b, 0x02, 0xdd, 0x2c, + 0xac, 0x91, 0x63, 0x32, 0xf5, 0x91, 0x02, 0xe9, 0x0b, 0xec, 0xf9, 0x0e, 0x71, 0x95, 0x64, 0x45, + 0x3a, 0xc8, 0x9a, 0x91, 0xf9, 0x42, 0x7e, 0xf3, 0xab, 0x16, 0xd3, 0xff, 0x89, 0xc3, 0x9e, 0x61, + 0x63, 0x97, 0x3a, 0x3f, 0x3a, 0xd8, 0xfe, 0xd8, 0xb1, 0x3b, 0x3a, 0x86, 0xee, 0x43, 0x7a, 0x4a, + 0x3c, 0xda, 0x77, 0x6c, 0x25, 0xc5, 0x98, 0x54, 0x68, 0x1a, 0x36, 0x7a, 0x08, 0x20, 0xca, 0x0c, + 0xb9, 0x34, 0xe3, 0xb2, 0x02, 0x31, 0x6c, 0xd1, 0xe9, 0x4b, 0xd8, 0xd9, 0xfc, 0x01, 0xe8, 0x8b, + 0x75, 0xb4, 0xb0, 0xcb, 0xd9, 0x3a, 0x5a, 0x06, 0x5a, 0x81, 0x17, 0x29, 0x08, 0x7d, 0x95, 0xe1, + 0xf9, 0x56, 0x86, 0x38, 0xd3, 0xef, 0x2f, 0x03, 0x6d, 0x4f, 0xfc, 0xa8, 0x15, 0xa7, 0xbf, 0x9f, + 0xf8, 0xdf, 0x04, 0xa4, 0x4e, 0x06, 0xd6, 0x39, 0xa6, 0x48, 0x85, 0x8c, 0x8f, 0x7f, 0x9a, 0x61, + 0xd7, 0xe2, 0xa3, 0x95, 0xcd, 0x95, 0x8d, 0xbe, 0x86, 0x9c, 0x4f, 0x66, 0x9e, 0x85, 0xfb, 0x61, + 0x4e, 0x91, 0xa3, 0xb4, 0x0c, 0x34, 0xc4, 0x73, 0x6c, 0x90, 0xba, 0x09, 0xdc, 0x3a, 0x21, 0x1e, + 0x45, 0xdf, 0x42, 0x41, 0x70, 0x22, 0x33, 0x1b, 0x62, 0xb6, 0xfe, 0xe9, 0x32, 0xd0, 0xf6, 0xb7, + 0x7c, 0x05, 0xaf, 0x9b, 0x79, 0x0e, 0x44, 0xeb, 0xf6, 0x12, 0x8a, 0x36, 0xf6, 0xa9, 0xe3, 0x0e, + 0xd8, 0x5c, 0x58, 0x7e, 0x99, 0xc5, 0xf8, 0x6c, 0x19, 0x68, 0xf7, 0x79, 0x8c, 0xdb, 0x0a, 0xdd, + 0xdc, 0xdd, 0x80, 0x58, 0x25, 0x1d, 0xb8, 0xb7, 0xa9, 0x8a, 0xca, 0x61, 0x63, 0xac, 0x97, 0x97, + 0x81, 0xa6, 0xbe, 0x1f, 0x6a, 0x55, 0x13, 0xda, 0x40, 0xa3, 0xc2, 0x10, 0xc8, 0xf6, 0x80, 0x0e, + 0xd8, 0xb8, 0x77, 0x4c, 0x76, 0x46, 0x3f, 0x40, 0x81, 0x3a, 0x13, 0x4c, 0x66, 0xb4, 0x3f, 0xc2, + 0xce, 0x70, 0x44, 0xd9, 0xc0, 0x73, 0x5b, 0xfb, 0xce, 0x6f, 0xa2, 0x8b, 0xc3, 0xea, 0x31, 0x53, + 0xd4, 0x1f, 0x86, 0xcb, 0xba, 0x6e, 0xc7, 0xb6, 0xbf, 0x6e, 0xe6, 0x05, 0xc0, 0xd5, 0xc8, 0x80, + 0xbd, 0x48, 0x11, 0x7e, 0x7d, 0x3a, 0x98, 0x4c, 0x95, 0x4c, 0x38, 0xae, 0xfa, 0x83, 0x65, 0xa0, + 0x29, 0xdb, 0x41, 0x56, 0x12, 0xdd, 0x2c, 0x0a, 0xac, 0x17, 0x41, 0x62, 0x03, 0xfe, 0x90, 0x20, + 0xc7, 0x37, 0x80, 0xfd, 0x67, 0xff, 0x87, 0xd5, 0xdb, 0xda, 0xb4, 0xc4, 0xad, 0x4d, 0x8b, 0xba, + 0x2a, 0xaf, 0xbb, 0x2a, 0x0a, 0xed, 0xc0, 0xee, 0x91, 0x75, 0xee, 0x92, 0xcb, 0x31, 0xb6, 0x87, + 0x78, 0x82, 0x5d, 0x8a, 0x14, 0x48, 0x79, 0xd8, 0x9f, 0x8d, 0xa9, 0xb2, 0x1f, 0xca, 0x8f, 0x63, + 0xa6, 0xb0, 0x51, 0x09, 0x92, 0xd8, 0xf3, 0x88, 0xa7, 0x94, 0xc2, 0x9a, 0x8e, 0x63, 0x26, 0x37, + 0xeb, 0x00, 0x19, 0x0f, 0xfb, 0x53, 0xe2, 0xfa, 0xf8, 0xf1, 0x9f, 0x12, 0x24, 0xbb, 0xe2, 0x82, + 0xd2, 0xba, 0xbd, 0xa3, 0x5e, 0xab, 0x7f, 0xda, 0x36, 0xda, 0x46, 0xcf, 0x38, 0x7a, 0x65, 0xbc, + 0x6e, 0x35, 0xfb, 0xa7, 0xed, 0xee, 0x49, 0xab, 0x61, 0xbc, 0x34, 0x5a, 0xcd, 0x62, 0x4c, 0xdd, + 0x9b, 0x2f, 0x2a, 0xf9, 0x2d, 0x01, 0x52, 0x00, 0xb8, 0x5f, 0x08, 0x16, 0x25, 0x35, 0x33, 0x5f, + 0x54, 0xe4, 0xf0, 0x8c, 0xca, 0x90, 0xe7, 0x4c, 0xcf, 0xfc, 0xbe, 0x73, 0xd2, 0x6a, 0x17, 0xe3, + 0x6a, 0x6e, 0xbe, 0xa8, 0xa4, 0x85, 0xb9, 0xf6, 0x64, 0x64, 0x82, 0x7b, 0x32, 0xe6, 0x01, 0xec, + 0x70, 0xa6, 0xf1, 0xaa, 0xd3, 0x6d, 0x35, 0x8b, 0xb2, 0x0a, 0xf3, 0x45, 0x25, 0xc5, 0x2d, 0x55, + 0x7e, 0xf3, 0x5b, 0x39, 0xf6, 0xf8, 0x12, 0x92, 0xec, 0xae, 0x44, 0x9f, 0x43, 0xa9, 0x63, 0x36, + 0x5b, 0x66, 0xbf, 0xdd, 0x69, 0xb7, 0x6e, 0xd5, 0xcb, 0x42, 0x86, 0x38, 0xd2, 0x61, 0x97, 0xab, + 0x4e, 0xdb, 0xec, 0xdb, 0x6a, 0x16, 0x25, 0x35, 0x3f, 0x5f, 0x54, 0xb2, 0x2b, 0x20, 0x2c, 0x98, + 0x6b, 0x22, 0x85, 0x28, 0x58, 0x98, 0x3c, 0x71, 0xdd, 0x7c, 0x7b, 0x5d, 0x96, 0xde, 0x5d, 0x97, + 0xa5, 0xbf, 0xaf, 0xcb, 0xd2, 0x2f, 0x37, 0xe5, 0xd8, 0xbb, 0x9b, 0x72, 0xec, 0xaf, 0x9b, 0x72, + 0xec, 0xf5, 0x37, 0x43, 0x87, 0x8e, 0x66, 0x67, 0x55, 0x8b, 0x4c, 0x6a, 0x16, 0xf1, 0x27, 0xc4, + 0x17, 0x9f, 0x27, 0xbe, 0x7d, 0x5e, 0xfb, 0xb9, 0xb6, 0x7a, 0x93, 0x9f, 0x3e, 0x7f, 0x12, 0x3d, + 0xf2, 0xf4, 0x6a, 0x8a, 0xfd, 0xb3, 0x14, 0x7b, 0x94, 0xbf, 0xfc, 0x2f, 0x00, 0x00, 0xff, 0xff, + 0xd7, 0x33, 0x69, 0x35, 0x05, 0x08, 0x00, 0x00, +} + +func (m *Channel) 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 *Channel) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Channel) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x2a + } + if len(m.ConnectionHops) > 0 { + for iNdEx := len(m.ConnectionHops) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ConnectionHops[iNdEx]) + copy(dAtA[i:], m.ConnectionHops[iNdEx]) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ConnectionHops[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintChannel(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Ordering != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.Ordering)) + i-- + dAtA[i] = 0x10 + } + if m.State != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *IdentifiedChannel) 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 *IdentifiedChannel) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IdentifiedChannel) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x3a + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0x32 + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x2a + } + if len(m.ConnectionHops) > 0 { + for iNdEx := len(m.ConnectionHops) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ConnectionHops[iNdEx]) + copy(dAtA[i:], m.ConnectionHops[iNdEx]) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ConnectionHops[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + { + size, err := m.Counterparty.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintChannel(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.Ordering != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.Ordering)) + i-- + dAtA[i] = 0x10 + } + if m.State != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Counterparty) 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 *Counterparty) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Counterparty) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Packet) 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 *Packet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Packet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TimeoutTimestamp != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.TimeoutTimestamp)) + i-- + dAtA[i] = 0x40 + } + { + size, err := m.TimeoutHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintChannel(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x32 + } + if len(m.DestinationChannel) > 0 { + i -= len(m.DestinationChannel) + copy(dAtA[i:], m.DestinationChannel) + i = encodeVarintChannel(dAtA, i, uint64(len(m.DestinationChannel))) + i-- + dAtA[i] = 0x2a + } + if len(m.DestinationPort) > 0 { + i -= len(m.DestinationPort) + copy(dAtA[i:], m.DestinationPort) + i = encodeVarintChannel(dAtA, i, uint64(len(m.DestinationPort))) + i-- + dAtA[i] = 0x22 + } + if len(m.SourceChannel) > 0 { + i -= len(m.SourceChannel) + copy(dAtA[i:], m.SourceChannel) + i = encodeVarintChannel(dAtA, i, uint64(len(m.SourceChannel))) + i-- + dAtA[i] = 0x1a + } + if len(m.SourcePort) > 0 { + i -= len(m.SourcePort) + copy(dAtA[i:], m.SourcePort) + i = encodeVarintChannel(dAtA, i, uint64(len(m.SourcePort))) + i-- + dAtA[i] = 0x12 + } + if m.Sequence != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *PacketState) 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 *PacketState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x22 + } + if m.Sequence != 0 { + i = encodeVarintChannel(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintChannel(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Acknowledgement) 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 *Acknowledgement) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Acknowledgement) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Response != nil { + { + size := m.Response.Size() + i -= size + if _, err := m.Response.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *Acknowledgement_Result) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Acknowledgement_Result) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Result != nil { + i -= len(m.Result) + copy(dAtA[i:], m.Result) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Result))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xaa + } + return len(dAtA) - i, nil +} +func (m *Acknowledgement_Error) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Acknowledgement_Error) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Error) + copy(dAtA[i:], m.Error) + i = encodeVarintChannel(dAtA, i, uint64(len(m.Error))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb2 + return len(dAtA) - i, nil +} +func encodeVarintChannel(dAtA []byte, offset int, v uint64) int { + offset -= sovChannel(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Channel) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.State != 0 { + n += 1 + sovChannel(uint64(m.State)) + } + if m.Ordering != 0 { + n += 1 + sovChannel(uint64(m.Ordering)) + } + l = m.Counterparty.Size() + n += 1 + l + sovChannel(uint64(l)) + if len(m.ConnectionHops) > 0 { + for _, s := range m.ConnectionHops { + l = len(s) + n += 1 + l + sovChannel(uint64(l)) + } + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + return n +} + +func (m *IdentifiedChannel) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.State != 0 { + n += 1 + sovChannel(uint64(m.State)) + } + if m.Ordering != 0 { + n += 1 + sovChannel(uint64(m.Ordering)) + } + l = m.Counterparty.Size() + n += 1 + l + sovChannel(uint64(l)) + if len(m.ConnectionHops) > 0 { + for _, s := range m.ConnectionHops { + l = len(s) + n += 1 + l + sovChannel(uint64(l)) + } + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + return n +} + +func (m *Counterparty) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + return n +} + +func (m *Packet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovChannel(uint64(m.Sequence)) + } + l = len(m.SourcePort) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.SourceChannel) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.DestinationPort) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.DestinationChannel) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = m.TimeoutHeight.Size() + n += 1 + l + sovChannel(uint64(l)) + if m.TimeoutTimestamp != 0 { + n += 1 + sovChannel(uint64(m.TimeoutTimestamp)) + } + return n +} + +func (m *PacketState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovChannel(uint64(m.Sequence)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovChannel(uint64(l)) + } + return n +} + +func (m *Acknowledgement) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Response != nil { + n += m.Response.Size() + } + return n +} + +func (m *Acknowledgement_Result) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Result != nil { + l = len(m.Result) + n += 2 + l + sovChannel(uint64(l)) + } + return n +} +func (m *Acknowledgement_Error) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Error) + n += 2 + l + sovChannel(uint64(l)) + return n +} + +func sovChannel(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozChannel(x uint64) (n int) { + return sovChannel(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Channel) 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 ErrIntOverflowChannel + } + 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: Channel: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Channel: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + m.State = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.State |= State(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ordering", wireType) + } + m.Ordering = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ordering |= Order(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionHops", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionHops = append(m.ConnectionHops, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IdentifiedChannel) 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 ErrIntOverflowChannel + } + 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: IdentifiedChannel: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IdentifiedChannel: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + m.State = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.State |= State(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ordering", wireType) + } + m.Ordering = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ordering |= Order(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Counterparty", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Counterparty.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionHops", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionHops = append(m.ConnectionHops, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Counterparty) 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 ErrIntOverflowChannel + } + 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: Counterparty: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Counterparty: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Packet) 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 ErrIntOverflowChannel + } + 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: Packet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Packet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourcePort", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourcePort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceChannel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SourceChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationPort", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DestinationPort = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationChannel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DestinationChannel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TimeoutHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutTimestamp", wireType) + } + m.TimeoutTimestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeoutTimestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketState) 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 ErrIntOverflowChannel + } + 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: PacketState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Acknowledgement) 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 ErrIntOverflowChannel + } + 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: Acknowledgement: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Acknowledgement: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 21: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthChannel + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := make([]byte, postIndex-iNdEx) + copy(v, dAtA[iNdEx:postIndex]) + m.Response = &Acknowledgement_Result{v} + iNdEx = postIndex + case 22: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowChannel + } + 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 ErrInvalidLengthChannel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthChannel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Response = &Acknowledgement_Error{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipChannel(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthChannel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipChannel(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, ErrIntOverflowChannel + } + 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, ErrIntOverflowChannel + } + 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, ErrIntOverflowChannel + } + 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, ErrInvalidLengthChannel + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupChannel + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthChannel + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthChannel = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowChannel = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupChannel = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/core/04-channel/types/channel_test.go b/x/ibc/core/04-channel/types/channel_test.go new file mode 100644 index 000000000000..30fee4443b2e --- /dev/null +++ b/x/ibc/core/04-channel/types/channel_test.go @@ -0,0 +1,119 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +func TestChannelValidateBasic(t *testing.T) { + counterparty := types.Counterparty{"portidone", "channelidone"} + testCases := []struct { + name string + channel types.Channel + expPass bool + }{ + {"valid channel", types.NewChannel(types.TRYOPEN, types.ORDERED, counterparty, connHops, version), true}, + {"invalid state", types.NewChannel(types.UNINITIALIZED, types.ORDERED, counterparty, connHops, version), false}, + {"invalid order", types.NewChannel(types.TRYOPEN, types.NONE, counterparty, connHops, version), false}, + {"more than 1 connection hop", types.NewChannel(types.TRYOPEN, types.ORDERED, counterparty, []string{"connection1", "connection2"}, version), false}, + {"invalid connection hop identifier", types.NewChannel(types.TRYOPEN, types.ORDERED, counterparty, []string{"(invalid)"}, version), false}, + {"invalid counterparty", types.NewChannel(types.TRYOPEN, types.ORDERED, types.NewCounterparty("(invalidport)", "channelidone"), connHops, version), false}, + } + + for i, tc := range testCases { + tc := tc + + err := tc.channel.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +func TestCounterpartyValidateBasic(t *testing.T) { + testCases := []struct { + name string + counterparty types.Counterparty + expPass bool + }{ + {"valid counterparty", types.Counterparty{"portidone", "channelidone"}, true}, + {"invalid port id", types.Counterparty{"(InvalidPort)", "channelidone"}, false}, + {"invalid channel id", types.Counterparty{"portidone", "(InvalidChannel)"}, false}, + } + + for i, tc := range testCases { + tc := tc + + err := tc.counterparty.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "valid test case %d failed: %s", i, tc.name) + } else { + require.Error(t, err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +// tests acknowledgement.ValidateBasic and acknowledgement.GetBytes +func (suite TypesTestSuite) TestAcknowledgement() { + testCases := []struct { + name string + ack types.Acknowledgement + expPass bool + }{ + { + "valid successful ack", + types.NewResultAcknowledgement([]byte("success")), + true, + }, + { + "valid failed ack", + types.NewErrorAcknowledgement("error"), + true, + }, + { + "empty successful ack", + types.NewResultAcknowledgement([]byte{}), + false, + }, + { + "empty faied ack", + types.NewErrorAcknowledgement(" "), + false, + }, + { + "nil response", + types.Acknowledgement{ + Response: nil, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + err := tc.ack.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + + // expect all acks to be able to be marshaled + suite.NotPanics(func() { + bz := tc.ack.GetBytes() + suite.Require().NotNil(bz) + }) + }) + } + +} diff --git a/x/ibc/core/04-channel/types/codec.go b/x/ibc/core/04-channel/types/codec.go new file mode 100644 index 000000000000..a74f0a7fc9c0 --- /dev/null +++ b/x/ibc/core/04-channel/types/codec.go @@ -0,0 +1,60 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces register the ibc channel submodule interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "ibc.core.channel.v1.ChannelI", + (*exported.ChannelI)(nil), + ) + registry.RegisterInterface( + "ibc.core.channel.v1.CounterpartyChannelI", + (*exported.CounterpartyChannelI)(nil), + ) + registry.RegisterInterface( + "ibc.core.channel.v1.PacketI", + (*exported.PacketI)(nil), + ) + registry.RegisterImplementations( + (*exported.ChannelI)(nil), + &Channel{}, + ) + registry.RegisterImplementations( + (*exported.CounterpartyChannelI)(nil), + &Counterparty{}, + ) + registry.RegisterImplementations( + (*exported.PacketI)(nil), + &Packet{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgChannelOpenInit{}, + &MsgChannelOpenTry{}, + &MsgChannelOpenAck{}, + &MsgChannelOpenConfirm{}, + &MsgChannelCloseInit{}, + &MsgChannelCloseConfirm{}, + &MsgRecvPacket{}, + &MsgAcknowledgement{}, + &MsgTimeout{}, + &MsgTimeoutOnClose{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +// SubModuleCdc references the global x/ibc/core/04-channel 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/core/04-channel and +// defined at the application level. +var SubModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) diff --git a/x/ibc/core/04-channel/types/errors.go b/x/ibc/core/04-channel/types/errors.go new file mode 100644 index 000000000000..82cf773057d7 --- /dev/null +++ b/x/ibc/core/04-channel/types/errors.go @@ -0,0 +1,28 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBC channel sentinel errors +var ( + ErrChannelExists = sdkerrors.Register(SubModuleName, 2, "channel already exists") + ErrChannelNotFound = sdkerrors.Register(SubModuleName, 3, "channel not found") + ErrInvalidChannel = sdkerrors.Register(SubModuleName, 4, "invalid channel") + ErrInvalidChannelState = sdkerrors.Register(SubModuleName, 5, "invalid channel state") + ErrInvalidChannelOrdering = sdkerrors.Register(SubModuleName, 6, "invalid channel ordering") + ErrInvalidCounterparty = sdkerrors.Register(SubModuleName, 7, "invalid counterparty channel") + ErrInvalidChannelCapability = sdkerrors.Register(SubModuleName, 8, "invalid channel capability") + ErrChannelCapabilityNotFound = sdkerrors.Register(SubModuleName, 9, "channel capability not found") + ErrSequenceSendNotFound = sdkerrors.Register(SubModuleName, 10, "sequence send not found") + ErrSequenceReceiveNotFound = sdkerrors.Register(SubModuleName, 11, "sequence receive not found") + ErrSequenceAckNotFound = sdkerrors.Register(SubModuleName, 12, "sequence acknowledgement not found") + ErrInvalidPacket = sdkerrors.Register(SubModuleName, 13, "invalid packet") + ErrPacketTimeout = sdkerrors.Register(SubModuleName, 14, "packet timeout") + ErrTooManyConnectionHops = sdkerrors.Register(SubModuleName, 15, "too many connection hops") + ErrInvalidAcknowledgement = sdkerrors.Register(SubModuleName, 16, "invalid acknowledgement") + ErrPacketCommitmentNotFound = sdkerrors.Register(SubModuleName, 17, "packet commitment not found") + ErrPacketReceived = sdkerrors.Register(SubModuleName, 18, "packet already received") + ErrAcknowledgementExists = sdkerrors.Register(SubModuleName, 19, "acknowledgement for packet already exists") + ErrInvalidChannelIdentifier = sdkerrors.Register(SubModuleName, 20, "invalid channel identifier") +) diff --git a/x/ibc/core/04-channel/types/events.go b/x/ibc/core/04-channel/types/events.go new file mode 100644 index 000000000000..b9ddb3052c30 --- /dev/null +++ b/x/ibc/core/04-channel/types/events.go @@ -0,0 +1,46 @@ +package types + +import ( + "fmt" + + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// IBC channel events +const ( + AttributeKeyConnectionID = "connection_id" + AttributeKeyPortID = "port_id" + AttributeKeyChannelID = "channel_id" + AttributeCounterpartyPortID = "counterparty_port_id" + AttributeCounterpartyChannelID = "counterparty_channel_id" + + EventTypeSendPacket = "send_packet" + EventTypeRecvPacket = "recv_packet" + EventTypeWriteAck = "write_acknowledgement" + EventTypeAcknowledgePacket = "acknowledge_packet" + EventTypeTimeoutPacket = "timeout_packet" + + AttributeKeyData = "packet_data" + AttributeKeyAck = "packet_ack" + AttributeKeyTimeoutHeight = "packet_timeout_height" + AttributeKeyTimeoutTimestamp = "packet_timeout_timestamp" + AttributeKeySequence = "packet_sequence" + AttributeKeySrcPort = "packet_src_port" + AttributeKeySrcChannel = "packet_src_channel" + AttributeKeyDstPort = "packet_dst_port" + AttributeKeyDstChannel = "packet_dst_channel" + AttributeKeyChannelOrdering = "packet_channel_ordering" + AttributeKeyConnection = "packet_connection" +) + +// IBC channel events vars +var ( + EventTypeChannelOpenInit = MsgChannelOpenInit{}.Type() + EventTypeChannelOpenTry = MsgChannelOpenTry{}.Type() + EventTypeChannelOpenAck = MsgChannelOpenAck{}.Type() + EventTypeChannelOpenConfirm = MsgChannelOpenConfirm{}.Type() + EventTypeChannelCloseInit = MsgChannelCloseInit{}.Type() + EventTypeChannelCloseConfirm = MsgChannelCloseConfirm{}.Type() + + AttributeValueCategory = fmt.Sprintf("%s_%s", host.ModuleName, SubModuleName) +) diff --git a/x/ibc/core/04-channel/types/expected_keepers.go b/x/ibc/core/04-channel/types/expected_keepers.go new file mode 100644 index 000000000000..d3b74b7e29ab --- /dev/null +++ b/x/ibc/core/04-channel/types/expected_keepers.go @@ -0,0 +1,76 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// ClientKeeper expected account IBC client keeper +type ClientKeeper interface { + GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) + GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) +} + +// ConnectionKeeper expected account IBC connection keeper +type ConnectionKeeper interface { + GetConnection(ctx sdk.Context, connectionID string) (connectiontypes.ConnectionEnd, bool) + GetTimestampAtHeight( + ctx sdk.Context, + connection connectiontypes.ConnectionEnd, + height exported.Height, + ) (uint64, error) + VerifyChannelState( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + channel exported.ChannelI, + ) error + VerifyPacketCommitment( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, + ) error + VerifyPacketAcknowledgement( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, + ) error + VerifyPacketReceiptAbsence( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + sequence uint64, + ) error + VerifyNextSequenceRecv( + ctx sdk.Context, + connection exported.ConnectionI, + height exported.Height, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, + ) error +} + +// PortKeeper expected account IBC port keeper +type PortKeeper interface { + Authenticate(ctx sdk.Context, key *capabilitytypes.Capability, portID string) bool +} diff --git a/x/ibc/core/04-channel/types/genesis.go b/x/ibc/core/04-channel/types/genesis.go new file mode 100644 index 000000000000..2c431e97b335 --- /dev/null +++ b/x/ibc/core/04-channel/types/genesis.go @@ -0,0 +1,156 @@ +package types + +import ( + "errors" + "fmt" + + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// NewPacketState creates a new PacketState instance. +func NewPacketState(portID, channelID string, seq uint64, data []byte) PacketState { + return PacketState{ + PortId: portID, + ChannelId: channelID, + Sequence: seq, + Data: data, + } +} + +// Validate performs basic validation of fields returning an error upon any +// failure. +func (pa PacketState) Validate() error { + if pa.Data == nil { + return errors.New("data bytes cannot be nil") + } + return validateGenFields(pa.PortId, pa.ChannelId, pa.Sequence) +} + +// NewPacketSequence creates a new PacketSequences instance. +func NewPacketSequence(portID, channelID string, seq uint64) PacketSequence { + return PacketSequence{ + PortId: portID, + ChannelId: channelID, + Sequence: seq, + } +} + +// Validate performs basic validation of fields returning an error upon any +// failure. +func (ps PacketSequence) Validate() error { + return validateGenFields(ps.PortId, ps.ChannelId, ps.Sequence) +} + +// NewGenesisState creates a GenesisState instance. +func NewGenesisState( + channels []IdentifiedChannel, acks, receipts, commitments []PacketState, + sendSeqs, recvSeqs, ackSeqs []PacketSequence, nextChannelSequence uint64, +) GenesisState { + return GenesisState{ + Channels: channels, + Acknowledgements: acks, + Commitments: commitments, + SendSequences: sendSeqs, + RecvSequences: recvSeqs, + AckSequences: ackSeqs, + NextChannelSequence: nextChannelSequence, + } +} + +// DefaultGenesisState returns the ibc channel submodule's default genesis state. +func DefaultGenesisState() GenesisState { + return GenesisState{ + Channels: []IdentifiedChannel{}, + Acknowledgements: []PacketState{}, + Receipts: []PacketState{}, + Commitments: []PacketState{}, + SendSequences: []PacketSequence{}, + RecvSequences: []PacketSequence{}, + AckSequences: []PacketSequence{}, + NextChannelSequence: 0, + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + // keep track of the max sequence to ensure it is less than + // the next sequence used in creating connection identifers. + var maxSequence uint64 = 0 + + for i, channel := range gs.Channels { + sequence, err := ParseChannelSequence(channel.ChannelId) + if err != nil { + return err + } + + if sequence > maxSequence { + maxSequence = sequence + } + + if err := channel.ValidateBasic(); err != nil { + return fmt.Errorf("invalid channel %v channel index %d: %w", channel, i, err) + } + } + + if maxSequence != 0 && maxSequence >= gs.NextChannelSequence { + return fmt.Errorf("next channel sequence %d must be greater than maximum sequence used in channel identifier %d", gs.NextChannelSequence, maxSequence) + } + + for i, ack := range gs.Acknowledgements { + if err := ack.Validate(); err != nil { + return fmt.Errorf("invalid acknowledgement %v ack index %d: %w", ack, i, err) + } + if len(ack.Data) == 0 { + return fmt.Errorf("invalid acknowledgement %v ack index %d: data bytes cannot be empty", ack, i) + } + } + + for i, receipt := range gs.Receipts { + if err := receipt.Validate(); err != nil { + return fmt.Errorf("invalid acknowledgement %v ack index %d: %w", receipt, i, err) + } + } + + for i, commitment := range gs.Commitments { + if err := commitment.Validate(); err != nil { + return fmt.Errorf("invalid commitment %v index %d: %w", commitment, i, err) + } + if len(commitment.Data) == 0 { + return fmt.Errorf("invalid acknowledgement %v ack index %d: data bytes cannot be empty", commitment, i) + } + } + + for i, ss := range gs.SendSequences { + if err := ss.Validate(); err != nil { + return fmt.Errorf("invalid send sequence %v index %d: %w", ss, i, err) + } + } + + for i, rs := range gs.RecvSequences { + if err := rs.Validate(); err != nil { + return fmt.Errorf("invalid receive sequence %v index %d: %w", rs, i, err) + } + } + + for i, as := range gs.AckSequences { + if err := as.Validate(); err != nil { + return fmt.Errorf("invalid acknowledgement sequence %v index %d: %w", as, i, err) + } + } + + return nil +} + +func validateGenFields(portID, channelID string, sequence uint64) error { + if err := host.PortIdentifierValidator(portID); err != nil { + return fmt.Errorf("invalid port Id: %w", err) + } + if err := host.ChannelIdentifierValidator(channelID); err != nil { + return fmt.Errorf("invalid channel Id: %w", err) + } + if sequence == 0 { + return errors.New("sequence cannot be 0") + } + return nil +} diff --git a/x/ibc/core/04-channel/types/genesis.pb.go b/x/ibc/core/04-channel/types/genesis.pb.go new file mode 100644 index 000000000000..c54c6f9deb03 --- /dev/null +++ b/x/ibc/core/04-channel/types/genesis.pb.go @@ -0,0 +1,1015 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/channel/v1/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 defines the ibc channel submodule's genesis state. +type GenesisState struct { + Channels []IdentifiedChannel `protobuf:"bytes,1,rep,name=channels,proto3,casttype=IdentifiedChannel" json:"channels"` + Acknowledgements []PacketState `protobuf:"bytes,2,rep,name=acknowledgements,proto3" json:"acknowledgements"` + Commitments []PacketState `protobuf:"bytes,3,rep,name=commitments,proto3" json:"commitments"` + Receipts []PacketState `protobuf:"bytes,4,rep,name=receipts,proto3" json:"receipts"` + SendSequences []PacketSequence `protobuf:"bytes,5,rep,name=send_sequences,json=sendSequences,proto3" json:"send_sequences" yaml:"send_sequences"` + RecvSequences []PacketSequence `protobuf:"bytes,6,rep,name=recv_sequences,json=recvSequences,proto3" json:"recv_sequences" yaml:"recv_sequences"` + AckSequences []PacketSequence `protobuf:"bytes,7,rep,name=ack_sequences,json=ackSequences,proto3" json:"ack_sequences" yaml:"ack_sequences"` + // the sequence for the next generated channel identifier + NextChannelSequence uint64 `protobuf:"varint,8,opt,name=next_channel_sequence,json=nextChannelSequence,proto3" json:"next_channel_sequence,omitempty" yaml:"next_channel_sequence"` +} + +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_cb06ec201f452595, []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) GetChannels() []IdentifiedChannel { + if m != nil { + return m.Channels + } + return nil +} + +func (m *GenesisState) GetAcknowledgements() []PacketState { + if m != nil { + return m.Acknowledgements + } + return nil +} + +func (m *GenesisState) GetCommitments() []PacketState { + if m != nil { + return m.Commitments + } + return nil +} + +func (m *GenesisState) GetReceipts() []PacketState { + if m != nil { + return m.Receipts + } + return nil +} + +func (m *GenesisState) GetSendSequences() []PacketSequence { + if m != nil { + return m.SendSequences + } + return nil +} + +func (m *GenesisState) GetRecvSequences() []PacketSequence { + if m != nil { + return m.RecvSequences + } + return nil +} + +func (m *GenesisState) GetAckSequences() []PacketSequence { + if m != nil { + return m.AckSequences + } + return nil +} + +func (m *GenesisState) GetNextChannelSequence() uint64 { + if m != nil { + return m.NextChannelSequence + } + return 0 +} + +// PacketSequence defines the genesis type necessary to retrieve and store +// next send and receive sequences. +type PacketSequence struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *PacketSequence) Reset() { *m = PacketSequence{} } +func (m *PacketSequence) String() string { return proto.CompactTextString(m) } +func (*PacketSequence) ProtoMessage() {} +func (*PacketSequence) Descriptor() ([]byte, []int) { + return fileDescriptor_cb06ec201f452595, []int{1} +} +func (m *PacketSequence) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketSequence) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketSequence.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 *PacketSequence) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketSequence.Merge(m, src) +} +func (m *PacketSequence) XXX_Size() int { + return m.Size() +} +func (m *PacketSequence) XXX_DiscardUnknown() { + xxx_messageInfo_PacketSequence.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketSequence proto.InternalMessageInfo + +func (m *PacketSequence) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *PacketSequence) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *PacketSequence) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.core.channel.v1.GenesisState") + proto.RegisterType((*PacketSequence)(nil), "ibc.core.channel.v1.PacketSequence") +} + +func init() { proto.RegisterFile("ibc/core/channel/v1/genesis.proto", fileDescriptor_cb06ec201f452595) } + +var fileDescriptor_cb06ec201f452595 = []byte{ + // 501 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0x87, 0xe3, 0x26, 0x4d, 0xd3, 0x6d, 0x13, 0xd1, 0x6d, 0x23, 0x99, 0xa8, 0xd8, 0xc6, 0x48, + 0x28, 0x12, 0xaa, 0x4d, 0xa1, 0x07, 0xc4, 0xd1, 0x1c, 0x20, 0x37, 0xb4, 0x70, 0x42, 0x42, 0x91, + 0xb3, 0x9e, 0xba, 0x2b, 0xc7, 0xde, 0xe0, 0xdd, 0x86, 0xf6, 0x29, 0xe0, 0xb1, 0x7a, 0xec, 0x91, + 0x93, 0x85, 0x92, 0x37, 0xc8, 0x91, 0x13, 0xf2, 0xdf, 0x24, 0x6a, 0x84, 0x68, 0x4f, 0xde, 0x9d, + 0xf9, 0xcd, 0xf7, 0xcd, 0xc1, 0x8b, 0x9e, 0xb2, 0x11, 0xb5, 0x29, 0x8f, 0xc1, 0xa6, 0x17, 0x6e, + 0x14, 0xc1, 0xd8, 0x9e, 0x9e, 0xda, 0x3e, 0x44, 0x20, 0x98, 0xb0, 0x26, 0x31, 0x97, 0x1c, 0x1f, + 0xb2, 0x11, 0xb5, 0xd2, 0x88, 0x55, 0x44, 0xac, 0xe9, 0x69, 0xef, 0xc8, 0xe7, 0x3e, 0xcf, 0xfa, + 0x76, 0x7a, 0xca, 0xa3, 0xbd, 0x8d, 0xb4, 0x72, 0x2a, 0x8b, 0x98, 0xf3, 0x6d, 0xb4, 0xff, 0x3e, + 0xe7, 0x7f, 0x92, 0xae, 0x04, 0xfc, 0x15, 0xb5, 0x8a, 0x84, 0x50, 0x15, 0xa3, 0xde, 0xdf, 0x7b, + 0xf5, 0xdc, 0xda, 0x60, 0xb4, 0x06, 0x1e, 0x44, 0x92, 0x9d, 0x33, 0xf0, 0xde, 0xe5, 0x45, 0xe7, + 0xf1, 0x4d, 0xa2, 0xd7, 0xfe, 0x24, 0xfa, 0xc1, 0x9d, 0x16, 0xa9, 0x90, 0x98, 0xa0, 0x47, 0x2e, + 0x0d, 0x22, 0xfe, 0x7d, 0x0c, 0x9e, 0x0f, 0x21, 0x44, 0x52, 0xa8, 0x5b, 0x99, 0xc6, 0xd8, 0xa8, + 0xf9, 0xe8, 0xd2, 0x00, 0x64, 0xb6, 0x9a, 0xd3, 0x48, 0x05, 0xe4, 0xce, 0x3c, 0xfe, 0x80, 0xf6, + 0x28, 0x0f, 0x43, 0x26, 0x73, 0x5c, 0xfd, 0x5e, 0xb8, 0xd5, 0x51, 0xec, 0xa0, 0x56, 0x0c, 0x14, + 0xd8, 0x44, 0x0a, 0xb5, 0x71, 0x2f, 0x4c, 0x35, 0x87, 0x19, 0xea, 0x08, 0x88, 0xbc, 0xa1, 0x80, + 0x6f, 0x97, 0x10, 0x51, 0x10, 0xea, 0x76, 0x46, 0x7a, 0xf6, 0x2f, 0x52, 0x91, 0x75, 0x9e, 0xa4, + 0xb0, 0x45, 0xa2, 0x77, 0xaf, 0xdd, 0x70, 0xfc, 0xd6, 0x5c, 0x07, 0x99, 0xa4, 0x9d, 0x16, 0xca, + 0x70, 0xa6, 0x8a, 0x81, 0x4e, 0x57, 0x54, 0xcd, 0x07, 0xab, 0xd6, 0x41, 0x26, 0x69, 0xa7, 0x85, + 0xa5, 0xea, 0x1c, 0xb5, 0x5d, 0x1a, 0xac, 0x98, 0x76, 0xfe, 0xdf, 0x74, 0x5c, 0x98, 0x8e, 0x72, + 0xd3, 0x1a, 0xc7, 0x24, 0xfb, 0x2e, 0x0d, 0x96, 0x9e, 0xcf, 0xa8, 0x1b, 0xc1, 0x95, 0x1c, 0x16, + 0xb4, 0x2a, 0xa8, 0xb6, 0x0c, 0xa5, 0xdf, 0x70, 0x8c, 0x45, 0xa2, 0x1f, 0xe7, 0x98, 0x8d, 0x31, + 0x93, 0x1c, 0xa6, 0xf5, 0xe2, 0xbf, 0x2b, 0xb1, 0xe6, 0x0f, 0x05, 0x75, 0xd6, 0x97, 0xc2, 0x2f, + 0xd0, 0xce, 0x84, 0xc7, 0x72, 0xc8, 0x3c, 0x55, 0x31, 0x94, 0xfe, 0xae, 0x83, 0x17, 0x89, 0xde, + 0xc9, 0xd1, 0x45, 0xc3, 0x24, 0xcd, 0xf4, 0x34, 0xf0, 0xf0, 0x19, 0x42, 0xa5, 0x89, 0x79, 0xea, + 0x56, 0x96, 0xef, 0x2e, 0x12, 0xfd, 0x20, 0xcf, 0x2f, 0x7b, 0x26, 0xd9, 0x2d, 0x2e, 0x03, 0x0f, + 0xf7, 0x50, 0xab, 0x5a, 0xbf, 0x9e, 0xae, 0x4f, 0xaa, 0xbb, 0x43, 0x6e, 0x66, 0x9a, 0x72, 0x3b, + 0xd3, 0x94, 0xdf, 0x33, 0x4d, 0xf9, 0x39, 0xd7, 0x6a, 0xb7, 0x73, 0xad, 0xf6, 0x6b, 0xae, 0xd5, + 0xbe, 0xbc, 0xf1, 0x99, 0xbc, 0xb8, 0x1c, 0x59, 0x94, 0x87, 0x36, 0xe5, 0x22, 0xe4, 0xa2, 0xf8, + 0x9c, 0x08, 0x2f, 0xb0, 0xaf, 0xec, 0xea, 0x4d, 0xbf, 0x3c, 0x3b, 0x29, 0x9f, 0xb5, 0xbc, 0x9e, + 0x80, 0x18, 0x35, 0xb3, 0x27, 0xfd, 0xfa, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x42, 0xc2, + 0x18, 0x45, 0x04, 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 m.NextChannelSequence != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.NextChannelSequence)) + i-- + dAtA[i] = 0x40 + } + if len(m.AckSequences) > 0 { + for iNdEx := len(m.AckSequences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AckSequences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.RecvSequences) > 0 { + for iNdEx := len(m.RecvSequences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RecvSequences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.SendSequences) > 0 { + for iNdEx := len(m.SendSequences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SendSequences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.Receipts) > 0 { + for iNdEx := len(m.Receipts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Receipts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Commitments) > 0 { + for iNdEx := len(m.Commitments) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Commitments[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Acknowledgements) > 0 { + for iNdEx := len(m.Acknowledgements) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Acknowledgements[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Channels) > 0 { + for iNdEx := len(m.Channels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Channels[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 *PacketSequence) 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 *PacketSequence) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketSequence) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.PortId))) + 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.Channels) > 0 { + for _, e := range m.Channels { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Acknowledgements) > 0 { + for _, e := range m.Acknowledgements { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Commitments) > 0 { + for _, e := range m.Commitments { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Receipts) > 0 { + for _, e := range m.Receipts { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.SendSequences) > 0 { + for _, e := range m.SendSequences { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.RecvSequences) > 0 { + for _, e := range m.RecvSequences { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.AckSequences) > 0 { + for _, e := range m.AckSequences { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.NextChannelSequence != 0 { + n += 1 + sovGenesis(uint64(m.NextChannelSequence)) + } + return n +} + +func (m *PacketSequence) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovGenesis(uint64(m.Sequence)) + } + 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 Channels", 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.Channels = append(m.Channels, IdentifiedChannel{}) + if err := m.Channels[len(m.Channels)-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 Acknowledgements", 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.Acknowledgements = append(m.Acknowledgements, PacketState{}) + if err := m.Acknowledgements[len(m.Acknowledgements)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commitments", 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.Commitments = append(m.Commitments, PacketState{}) + if err := m.Commitments[len(m.Commitments)-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 Receipts", 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.Receipts = append(m.Receipts, PacketState{}) + if err := m.Receipts[len(m.Receipts)-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 SendSequences", 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.SendSequences = append(m.SendSequences, PacketSequence{}) + if err := m.SendSequences[len(m.SendSequences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RecvSequences", 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.RecvSequences = append(m.RecvSequences, PacketSequence{}) + if err := m.RecvSequences[len(m.RecvSequences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AckSequences", 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.AckSequences = append(m.AckSequences, PacketSequence{}) + if err := m.AckSequences[len(m.AckSequences)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextChannelSequence", wireType) + } + m.NextChannelSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextChannelSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *PacketSequence) 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: PacketSequence: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketSequence: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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/ibc/core/04-channel/types/genesis_test.go b/x/ibc/core/04-channel/types/genesis_test.go new file mode 100644 index 000000000000..a0d21007a776 --- /dev/null +++ b/x/ibc/core/04-channel/types/genesis_test.go @@ -0,0 +1,225 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +const ( + testPort1 = "firstport" + testPort2 = "secondport" + testConnectionIDA = "connectionidatob" + + testChannel1 = "channel-0" + testChannel2 = "channel-1" + + testChannelOrder = types.ORDERED + testChannelVersion = "1.0" +) + +func TestValidateGenesis(t *testing.T) { + counterparty1 := types.NewCounterparty(testPort1, testChannel1) + counterparty2 := types.NewCounterparty(testPort2, testChannel2) + testCases := []struct { + name string + genState types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + name: "valid genesis", + genState: types.NewGenesisState( + []types.IdentifiedChannel{ + types.NewIdentifiedChannel( + testPort1, testChannel1, types.NewChannel( + types.INIT, testChannelOrder, counterparty2, []string{testConnectionIDA}, testChannelVersion, + ), + ), + types.NewIdentifiedChannel( + testPort2, testChannel2, types.NewChannel( + types.INIT, testChannelOrder, counterparty1, []string{testConnectionIDA}, testChannelVersion, + ), + ), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("ack")), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("")), + }, + []types.PacketState{ + types.NewPacketState(testPort1, testChannel1, 1, []byte("commit_hash")), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort1, testChannel1, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + 2, + ), + expPass: true, + }, + { + name: "invalid channel", + genState: types.GenesisState{ + Channels: []types.IdentifiedChannel{ + types.NewIdentifiedChannel( + testPort1, "(testChannel1)", types.NewChannel( + types.INIT, testChannelOrder, counterparty2, []string{testConnectionIDA}, testChannelVersion, + ), + ), + }, + }, + expPass: false, + }, + { + name: "invalid ack", + genState: types.GenesisState{ + Acknowledgements: []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, nil), + }, + }, + expPass: false, + }, + { + name: "invalid commitment", + genState: types.GenesisState{ + Commitments: []types.PacketState{ + types.NewPacketState(testPort1, testChannel1, 1, nil), + }, + }, + expPass: false, + }, + { + name: "invalid send seq", + genState: types.GenesisState{ + SendSequences: []types.PacketSequence{ + types.NewPacketSequence(testPort1, testChannel1, 0), + }, + }, + expPass: false, + }, + { + name: "invalid recv seq", + genState: types.GenesisState{ + RecvSequences: []types.PacketSequence{ + types.NewPacketSequence(testPort1, "(testChannel1)", 1), + }, + }, + expPass: false, + }, + { + name: "invalid recv seq 2", + genState: types.GenesisState{ + RecvSequences: []types.PacketSequence{ + types.NewPacketSequence("(testPort1)", testChannel1, 1), + }, + }, + expPass: false, + }, + { + name: "invalid ack seq", + genState: types.GenesisState{ + AckSequences: []types.PacketSequence{ + types.NewPacketSequence(testPort1, "(testChannel1)", 1), + }, + }, + expPass: false, + }, + { + name: "invalid channel identifier", + genState: types.NewGenesisState( + []types.IdentifiedChannel{ + types.NewIdentifiedChannel( + testPort1, "chan-0", types.NewChannel( + types.INIT, testChannelOrder, counterparty2, []string{testConnectionIDA}, testChannelVersion, + ), + ), + types.NewIdentifiedChannel( + testPort2, testChannel2, types.NewChannel( + types.INIT, testChannelOrder, counterparty1, []string{testConnectionIDA}, testChannelVersion, + ), + ), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("ack")), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("")), + }, + []types.PacketState{ + types.NewPacketState(testPort1, testChannel1, 1, []byte("commit_hash")), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort1, testChannel1, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + 0, + ), + expPass: false, + }, + { + name: "next channel sequence is less than maximum channel identifier sequence used", + genState: types.NewGenesisState( + []types.IdentifiedChannel{ + types.NewIdentifiedChannel( + testPort1, "channel-10", types.NewChannel( + types.INIT, testChannelOrder, counterparty2, []string{testConnectionIDA}, testChannelVersion, + ), + ), + types.NewIdentifiedChannel( + testPort2, testChannel2, types.NewChannel( + types.INIT, testChannelOrder, counterparty1, []string{testConnectionIDA}, testChannelVersion, + ), + ), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("ack")), + }, + []types.PacketState{ + types.NewPacketState(testPort2, testChannel2, 1, []byte("")), + }, + []types.PacketState{ + types.NewPacketState(testPort1, testChannel1, 1, []byte("commit_hash")), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort1, testChannel1, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + []types.PacketSequence{ + types.NewPacketSequence(testPort2, testChannel2, 1), + }, + 0, + ), + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/core/04-channel/types/keys.go b/x/ibc/core/04-channel/types/keys.go new file mode 100644 index 000000000000..d3a6cde24d56 --- /dev/null +++ b/x/ibc/core/04-channel/types/keys.go @@ -0,0 +1,61 @@ +package types + +import ( + "fmt" + "regexp" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + // SubModuleName defines the IBC channels name + SubModuleName = "channel" + + // StoreKey is the store key string for IBC channels + StoreKey = SubModuleName + + // RouterKey is the message route for IBC channels + RouterKey = SubModuleName + + // QuerierRoute is the querier route for IBC channels + QuerierRoute = SubModuleName + + // KeyNextChannelSequence is the key used to store the next channel sequence in + // the keeper. + KeyNextChannelSequence = "nextChannelSequence" + + // ChannelPrefix is the prefix used when creating a channel identifier + ChannelPrefix = "channel-" +) + +// FormatChannelIdentifier returns the channel identifier with the sequence appended. +// This is a SDK specific format not enforced by IBC protocol. +func FormatChannelIdentifier(sequence uint64) string { + return fmt.Sprintf("%s%d", ChannelPrefix, sequence) +} + +// IsChannelIDFormat checks if a channelID is in the format required on the SDK for +// parsing channel identifiers. The channel identifier must be in the form: `channel-{N} +var IsChannelIDFormat = regexp.MustCompile(`^channel-[0-9]{1,20}$`).MatchString + +// IsValidChannelID checks if a channelID is valid and can be parsed to the channel +// identifier format. +func IsValidChannelID(channelID string) bool { + _, err := ParseChannelSequence(channelID) + return err == nil +} + +// ParseChannelSequence parses the channel sequence from the channel identifier. +func ParseChannelSequence(channelID string) (uint64, error) { + if !IsChannelIDFormat(channelID) { + return 0, sdkerrors.Wrap(host.ErrInvalidID, "channel identifier is not in the format: `channel-{N}`") + } + + sequence, err := host.ParseIdentifier(channelID, ChannelPrefix) + if err != nil { + return 0, sdkerrors.Wrap(err, "invalid channel identifier") + } + + return sequence, nil +} diff --git a/x/ibc/core/04-channel/types/keys_test.go b/x/ibc/core/04-channel/types/keys_test.go new file mode 100644 index 000000000000..9bc6500b9aff --- /dev/null +++ b/x/ibc/core/04-channel/types/keys_test.go @@ -0,0 +1,47 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// tests ParseChannelSequence and IsValidChannelID +func TestParseChannelSequence(t *testing.T) { + testCases := []struct { + name string + channelID string + expSeq uint64 + expPass bool + }{ + {"valid 0", "channel-0", 0, true}, + {"valid 1", "channel-1", 1, true}, + {"valid large sequence", "channel-234568219356718293", 234568219356718293, true}, + // one above uint64 max + {"invalid uint64", "channel-18446744073709551616", 0, false}, + // uint64 == 20 characters + {"invalid large sequence", "channel-2345682193567182931243", 0, false}, + {"capital prefix", "Channel-0", 0, false}, + {"missing dash", "channel0", 0, false}, + {"blank id", " ", 0, false}, + {"empty id", "", 0, false}, + {"negative sequence", "channel--1", 0, false}, + } + + for _, tc := range testCases { + + seq, err := types.ParseChannelSequence(tc.channelID) + valid := types.IsValidChannelID(tc.channelID) + require.Equal(t, tc.expSeq, seq) + + if tc.expPass { + require.NoError(t, err, tc.name) + require.True(t, valid) + } else { + require.Error(t, err, tc.name) + require.False(t, valid) + } + } +} diff --git a/x/ibc/core/04-channel/types/msgs.go b/x/ibc/core/04-channel/types/msgs.go new file mode 100644 index 000000000000..da14a31030fa --- /dev/null +++ b/x/ibc/core/04-channel/types/msgs.go @@ -0,0 +1,652 @@ +package types + +import ( + "encoding/base64" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +var _ sdk.Msg = &MsgChannelOpenInit{} + +// NewMsgChannelOpenInit creates a new MsgChannelOpenInit. It sets the counterparty channel +// identifier to be empty. +// nolint:interfacer +func NewMsgChannelOpenInit( + portID, version string, channelOrder Order, connectionHops []string, + counterpartyPortID string, signer sdk.AccAddress, +) *MsgChannelOpenInit { + counterparty := NewCounterparty(counterpartyPortID, "") + channel := NewChannel(INIT, channelOrder, counterparty, connectionHops, version) + return &MsgChannelOpenInit{ + PortId: portID, + Channel: channel, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelOpenInit) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelOpenInit) Type() string { + return "channel_open_init" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelOpenInit) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if msg.Channel.State != INIT { + return sdkerrors.Wrapf(ErrInvalidChannelState, + "channel state must be INIT in MsgChannelOpenInit. expected: %s, got: %s", + INIT, msg.Channel.State, + ) + } + if msg.Channel.Counterparty.ChannelId != "" { + return sdkerrors.Wrap(ErrInvalidCounterparty, "counterparty channel identifier must be empty") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Channel.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelOpenInit) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelOpenInit) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgChannelOpenTry{} + +// NewMsgChannelOpenTry creates a new MsgChannelOpenTry instance +// nolint:interfacer +func NewMsgChannelOpenTry( + portID, previousChannelID, version string, channelOrder Order, connectionHops []string, + counterpartyPortID, counterpartyChannelID, counterpartyVersion string, + proofInit []byte, proofHeight clienttypes.Height, signer sdk.AccAddress, +) *MsgChannelOpenTry { + counterparty := NewCounterparty(counterpartyPortID, counterpartyChannelID) + channel := NewChannel(TRYOPEN, channelOrder, counterparty, connectionHops, version) + return &MsgChannelOpenTry{ + PortId: portID, + PreviousChannelId: previousChannelID, + Channel: channel, + CounterpartyVersion: counterpartyVersion, + ProofInit: proofInit, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelOpenTry) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelOpenTry) Type() string { + return "channel_open_try" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelOpenTry) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if msg.PreviousChannelId != "" { + if !IsValidChannelID(msg.PreviousChannelId) { + return sdkerrors.Wrap(ErrInvalidChannelIdentifier, "invalid previous channel ID") + } + } + if len(msg.ProofInit) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + if msg.Channel.State != TRYOPEN { + return sdkerrors.Wrapf(ErrInvalidChannelState, + "channel state must be TRYOPEN in MsgChannelOpenTry. expected: %s, got: %s", + TRYOPEN, msg.Channel.State, + ) + } + // counterparty validate basic allows empty counterparty channel identifiers + if err := host.ChannelIdentifierValidator(msg.Channel.Counterparty.ChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty channel ID") + } + + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Channel.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelOpenTry) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelOpenTry) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgChannelOpenAck{} + +// NewMsgChannelOpenAck creates a new MsgChannelOpenAck instance +// nolint:interfacer +func NewMsgChannelOpenAck( + portID, channelID, counterpartyChannelID string, cpv string, proofTry []byte, proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgChannelOpenAck { + return &MsgChannelOpenAck{ + PortId: portID, + ChannelId: channelID, + CounterpartyChannelId: counterpartyChannelID, + CounterpartyVersion: cpv, + ProofTry: proofTry, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelOpenAck) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelOpenAck) Type() string { + return "channel_open_ack" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelOpenAck) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if !IsValidChannelID(msg.ChannelId) { + return ErrInvalidChannelIdentifier + } + if err := host.ChannelIdentifierValidator(msg.CounterpartyChannelId); err != nil { + return sdkerrors.Wrap(err, "invalid counterparty channel ID") + } + if len(msg.ProofTry) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof try") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelOpenAck) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelOpenAck) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgChannelOpenConfirm{} + +// NewMsgChannelOpenConfirm creates a new MsgChannelOpenConfirm instance +// nolint:interfacer +func NewMsgChannelOpenConfirm( + portID, channelID string, proofAck []byte, proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgChannelOpenConfirm { + return &MsgChannelOpenConfirm{ + PortId: portID, + ChannelId: channelID, + ProofAck: proofAck, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelOpenConfirm) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelOpenConfirm) Type() string { + return "channel_open_confirm" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelOpenConfirm) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if !IsValidChannelID(msg.ChannelId) { + return ErrInvalidChannelIdentifier + } + if len(msg.ProofAck) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof ack") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelOpenConfirm) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelOpenConfirm) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgChannelCloseInit{} + +// NewMsgChannelCloseInit creates a new MsgChannelCloseInit instance +// nolint:interfacer +func NewMsgChannelCloseInit( + portID string, channelID string, signer sdk.AccAddress, +) *MsgChannelCloseInit { + return &MsgChannelCloseInit{ + PortId: portID, + ChannelId: channelID, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelCloseInit) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelCloseInit) Type() string { + return "channel_close_init" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelCloseInit) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if !IsValidChannelID(msg.ChannelId) { + return ErrInvalidChannelIdentifier + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelCloseInit) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelCloseInit) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgChannelCloseConfirm{} + +// NewMsgChannelCloseConfirm creates a new MsgChannelCloseConfirm instance +// nolint:interfacer +func NewMsgChannelCloseConfirm( + portID, channelID string, proofInit []byte, proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgChannelCloseConfirm { + return &MsgChannelCloseConfirm{ + PortId: portID, + ChannelId: channelID, + ProofInit: proofInit, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgChannelCloseConfirm) Route() string { + return host.RouterKey +} + +// Type implements sdk.Msg +func (msg MsgChannelCloseConfirm) Type() string { + return "channel_close_confirm" +} + +// ValidateBasic implements sdk.Msg +func (msg MsgChannelCloseConfirm) ValidateBasic() error { + if err := host.PortIdentifierValidator(msg.PortId); err != nil { + return sdkerrors.Wrap(err, "invalid port ID") + } + if !IsValidChannelID(msg.ChannelId) { + return ErrInvalidChannelIdentifier + } + if len(msg.ProofInit) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof init") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return nil +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgChannelCloseConfirm) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgChannelCloseConfirm) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +var _ sdk.Msg = &MsgRecvPacket{} + +// NewMsgRecvPacket constructs new MsgRecvPacket +// nolint:interfacer +func NewMsgRecvPacket( + packet Packet, proofCommitment []byte, proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgRecvPacket { + return &MsgRecvPacket{ + Packet: packet, + ProofCommitment: proofCommitment, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgRecvPacket) Route() string { + return host.RouterKey +} + +// ValidateBasic implements sdk.Msg +func (msg MsgRecvPacket) ValidateBasic() error { + if len(msg.ProofCommitment) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Packet.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgRecvPacket) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetDataSignBytes returns the base64-encoded bytes used for the +// data field when signing the packet. +func (msg MsgRecvPacket) GetDataSignBytes() []byte { + s := "\"" + base64.StdEncoding.EncodeToString(msg.Packet.Data) + "\"" + return []byte(s) +} + +// GetSigners implements sdk.Msg +func (msg MsgRecvPacket) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Type implements sdk.Msg +func (msg MsgRecvPacket) Type() string { + return "recv_packet" +} + +var _ sdk.Msg = &MsgTimeout{} + +// NewMsgTimeout constructs new MsgTimeout +// nolint:interfacer +func NewMsgTimeout( + packet Packet, nextSequenceRecv uint64, proofUnreceived []byte, + proofHeight clienttypes.Height, signer sdk.AccAddress, +) *MsgTimeout { + return &MsgTimeout{ + Packet: packet, + NextSequenceRecv: nextSequenceRecv, + ProofUnreceived: proofUnreceived, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgTimeout) Route() string { + return host.RouterKey +} + +// ValidateBasic implements sdk.Msg +func (msg MsgTimeout) ValidateBasic() error { + if len(msg.ProofUnreceived) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty unreceived proof") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + if msg.NextSequenceRecv == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "next sequence receive cannot be 0") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Packet.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgTimeout) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgTimeout) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Type implements sdk.Msg +func (msg MsgTimeout) Type() string { + return "timeout_packet" +} + +// NewMsgTimeoutOnClose constructs new MsgTimeoutOnClose +// nolint:interfacer +func NewMsgTimeoutOnClose( + packet Packet, nextSequenceRecv uint64, + proofUnreceived, proofClose []byte, + proofHeight clienttypes.Height, signer sdk.AccAddress, +) *MsgTimeoutOnClose { + return &MsgTimeoutOnClose{ + Packet: packet, + NextSequenceRecv: nextSequenceRecv, + ProofUnreceived: proofUnreceived, + ProofClose: proofClose, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgTimeoutOnClose) Route() string { + return host.RouterKey +} + +// ValidateBasic implements sdk.Msg +func (msg MsgTimeoutOnClose) ValidateBasic() error { + if msg.NextSequenceRecv == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "next sequence receive cannot be 0") + } + if len(msg.ProofUnreceived) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + } + if len(msg.ProofClose) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof of closed counterparty channel end") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Packet.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgTimeoutOnClose) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgTimeoutOnClose) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Type implements sdk.Msg +func (msg MsgTimeoutOnClose) Type() string { + return "timeout_on_close_packet" +} + +var _ sdk.Msg = &MsgAcknowledgement{} + +// NewMsgAcknowledgement constructs a new MsgAcknowledgement +// nolint:interfacer +func NewMsgAcknowledgement( + packet Packet, + ack, proofAcked []byte, + proofHeight clienttypes.Height, + signer sdk.AccAddress, +) *MsgAcknowledgement { + return &MsgAcknowledgement{ + Packet: packet, + Acknowledgement: ack, + ProofAcked: proofAcked, + ProofHeight: proofHeight, + Signer: signer.String(), + } +} + +// Route implements sdk.Msg +func (msg MsgAcknowledgement) Route() string { + return host.RouterKey +} + +// ValidateBasic implements sdk.Msg +func (msg MsgAcknowledgement) ValidateBasic() error { + if len(msg.ProofAcked) == 0 { + return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "cannot submit an empty proof") + } + if msg.ProofHeight.IsZero() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidHeight, "proof height must be non-zero") + } + if len(msg.Acknowledgement) == 0 { + return sdkerrors.Wrap(ErrInvalidAcknowledgement, "ack bytes cannot be empty") + } + _, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "string could not be parsed as address: %v", err) + } + return msg.Packet.ValidateBasic() +} + +// GetSignBytes implements sdk.Msg. The function will panic since it is used +// for amino transaction verification which IBC does not support. +func (msg MsgAcknowledgement) GetSignBytes() []byte { + panic("IBC messages do not support amino") +} + +// GetSigners implements sdk.Msg +func (msg MsgAcknowledgement) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +// Type implements sdk.Msg +func (msg MsgAcknowledgement) Type() string { + return "acknowledge_packet" +} diff --git a/x/ibc/core/04-channel/types/msgs_test.go b/x/ibc/core/04-channel/types/msgs_test.go new file mode 100644 index 000000000000..9c27fd69efa8 --- /dev/null +++ b/x/ibc/core/04-channel/types/msgs_test.go @@ -0,0 +1,446 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +const ( + // valid constatns used for testing + portid = "testportid" + chanid = "channel-0" + cpportid = "testcpport" + cpchanid = "testcpchannel" + + version = "1.0" + + // invalid constants used for testing + invalidPort = "(invalidport1)" + invalidShortPort = "p" + invalidLongPort = "invalidlongportinvalidlongportinvalidlongportidinvalidlongportidinvalid" + + invalidChannel = "(invalidchannel1)" + invalidShortChannel = "invalid" + invalidLongChannel = "invalidlongchannelinvalidlongchannelinvalidlongchannelinvalidlongchannel" + + invalidConnection = "(invalidconnection1)" + invalidShortConnection = "invalidcn" + invalidLongConnection = "invalidlongconnectioninvalidlongconnectioninvalidlongconnectioninvalid" +) + +// define variables used for testing +var ( + height = clienttypes.NewHeight(0, 1) + timeoutHeight = clienttypes.NewHeight(0, 100) + timeoutTimestamp = uint64(100) + disabledTimeout = clienttypes.ZeroHeight() + validPacketData = []byte("testdata") + unknownPacketData = []byte("unknown") + + packet = types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp) + invalidPacket = types.NewPacket(unknownPacketData, 0, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp) + + emptyProof = []byte{} + invalidProofs1 = exported.Proof(nil) + invalidProofs2 = emptyProof + + addr = sdk.AccAddress("testaddr111111111111") + emptyAddr sdk.AccAddress + + connHops = []string{"testconnection"} + invalidConnHops = []string{"testconnection", "testconnection"} + invalidShortConnHops = []string{invalidShortConnection} + invalidLongConnHops = []string{invalidLongConnection} +) + +type TypesTestSuite struct { + suite.Suite + + proof []byte +} + +func (suite *TypesTestSuite) SetupTest() { + app := simapp.Setup(false) + db := dbm.NewMemDB() + store := rootmulti.NewStore(db) + storeKey := storetypes.NewKVStoreKey("iavlStoreKey") + + store.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, nil) + store.LoadVersion(0) + iavlStore := store.GetCommitStore(storeKey).(*iavl.Store) + + iavlStore.Set([]byte("KEY"), []byte("VALUE")) + _ = store.Commit() + + res := store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", storeKey.Name()), // required path to get key/value+proof + Data: []byte("KEY"), + Prove: true, + }) + + merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) + suite.Require().NoError(err) + proof, err := app.AppCodec().MarshalBinaryBare(&merkleProof) + suite.Require().NoError(err) + + suite.proof = proof +} + +func TestTypesTestSuite(t *testing.T) { + suite.Run(t, new(TypesTestSuite)) +} + +func (suite *TypesTestSuite) TestMsgChannelOpenInitValidateBasic() { + counterparty := types.NewCounterparty(cpportid, cpchanid) + tryOpenChannel := types.NewChannel(types.TRYOPEN, types.ORDERED, counterparty, connHops, version) + + testCases := []struct { + name string + msg *types.MsgChannelOpenInit + expPass bool + }{ + {"", types.NewMsgChannelOpenInit(portid, version, types.ORDERED, connHops, cpportid, addr), true}, + {"too short port id", types.NewMsgChannelOpenInit(invalidShortPort, version, types.ORDERED, connHops, cpportid, addr), false}, + {"too long port id", types.NewMsgChannelOpenInit(invalidLongPort, version, types.ORDERED, connHops, cpportid, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenInit(invalidPort, version, types.ORDERED, connHops, cpportid, addr), false}, + {"invalid channel order", types.NewMsgChannelOpenInit(portid, version, types.Order(3), connHops, cpportid, addr), false}, + {"connection hops more than 1 ", types.NewMsgChannelOpenInit(portid, version, types.ORDERED, invalidConnHops, cpportid, addr), false}, + {"too short connection id", types.NewMsgChannelOpenInit(portid, version, types.UNORDERED, invalidShortConnHops, cpportid, addr), false}, + {"too long connection id", types.NewMsgChannelOpenInit(portid, version, types.UNORDERED, invalidLongConnHops, cpportid, addr), false}, + {"connection id contains non-alpha", types.NewMsgChannelOpenInit(portid, version, types.UNORDERED, []string{invalidConnection}, cpportid, addr), false}, + {"", types.NewMsgChannelOpenInit(portid, "", types.UNORDERED, connHops, cpportid, addr), true}, + {"invalid counterparty port id", types.NewMsgChannelOpenInit(portid, version, types.UNORDERED, connHops, invalidPort, addr), false}, + {"channel not in INIT state", &types.MsgChannelOpenInit{portid, tryOpenChannel, addr.String()}, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgChannelOpenTryValidateBasic() { + counterparty := types.NewCounterparty(cpportid, cpchanid) + initChannel := types.NewChannel(types.INIT, types.ORDERED, counterparty, connHops, version) + + testCases := []struct { + name string + msg *types.MsgChannelOpenTry + expPass bool + }{ + {"", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenTry(invalidShortPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenTry(invalidLongPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenTry(invalidPort, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenTry(portid, invalidShortChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenTry(portid, invalidLongChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenTry(portid, invalidChannel, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, "", suite.proof, height, addr), true}, + {"proof height is zero", types.NewMsgChannelOpenTry(portid, chanid, version, types.ORDERED, connHops, cpportid, cpchanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"invalid channel order", types.NewMsgChannelOpenTry(portid, chanid, version, types.Order(4), connHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection hops more than 1 ", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too short connection id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidShortConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"too long connection id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, invalidLongConnHops, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"connection id contains non-alpha", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, []string{invalidConnection}, cpportid, cpchanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenTry(portid, chanid, "", types.UNORDERED, connHops, cpportid, cpchanid, version, suite.proof, height, addr), true}, + {"invalid counterparty port id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, invalidPort, cpchanid, version, suite.proof, height, addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, cpportid, invalidChannel, version, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelOpenTry(portid, chanid, version, types.UNORDERED, connHops, cpportid, cpchanid, version, emptyProof, height, addr), false}, + {"channel not in TRYOPEN state", &types.MsgChannelOpenTry{portid, chanid, initChannel, version, suite.proof, height, addr.String()}, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgChannelOpenAckValidateBasic() { + testCases := []struct { + name string + msg *types.MsgChannelOpenAck + expPass bool + }{ + {"", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenAck(invalidShortPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenAck(invalidLongPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenAck(invalidPort, chanid, chanid, version, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenAck(portid, invalidShortChannel, chanid, version, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenAck(portid, invalidLongChannel, chanid, version, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenAck(portid, invalidChannel, chanid, version, suite.proof, height, addr), false}, + {"", types.NewMsgChannelOpenAck(portid, chanid, chanid, "", suite.proof, height, addr), true}, + {"empty proof", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelOpenAck(portid, chanid, chanid, version, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"invalid counterparty channel id", types.NewMsgChannelOpenAck(portid, chanid, invalidShortChannel, version, suite.proof, height, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgChannelOpenConfirmValidateBasic() { + testCases := []struct { + name string + msg *types.MsgChannelOpenConfirm + expPass bool + }{ + {"", types.NewMsgChannelOpenConfirm(portid, chanid, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelOpenConfirm(invalidShortPort, chanid, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelOpenConfirm(invalidLongPort, chanid, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelOpenConfirm(invalidPort, chanid, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelOpenConfirm(portid, invalidShortChannel, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelOpenConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelOpenConfirm(portid, invalidChannel, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelOpenConfirm(portid, chanid, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelOpenConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgChannelCloseInitValidateBasic() { + testCases := []struct { + name string + msg *types.MsgChannelCloseInit + expPass bool + }{ + {"", types.NewMsgChannelCloseInit(portid, chanid, addr), true}, + {"too short port id", types.NewMsgChannelCloseInit(invalidShortPort, chanid, addr), false}, + {"too long port id", types.NewMsgChannelCloseInit(invalidLongPort, chanid, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelCloseInit(invalidPort, chanid, addr), false}, + {"too short channel id", types.NewMsgChannelCloseInit(portid, invalidShortChannel, addr), false}, + {"too long channel id", types.NewMsgChannelCloseInit(portid, invalidLongChannel, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelCloseInit(portid, invalidChannel, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgChannelCloseConfirmValidateBasic() { + testCases := []struct { + name string + msg *types.MsgChannelCloseConfirm + expPass bool + }{ + {"", types.NewMsgChannelCloseConfirm(portid, chanid, suite.proof, height, addr), true}, + {"too short port id", types.NewMsgChannelCloseConfirm(invalidShortPort, chanid, suite.proof, height, addr), false}, + {"too long port id", types.NewMsgChannelCloseConfirm(invalidLongPort, chanid, suite.proof, height, addr), false}, + {"port id contains non-alpha", types.NewMsgChannelCloseConfirm(invalidPort, chanid, suite.proof, height, addr), false}, + {"too short channel id", types.NewMsgChannelCloseConfirm(portid, invalidShortChannel, suite.proof, height, addr), false}, + {"too long channel id", types.NewMsgChannelCloseConfirm(portid, invalidLongChannel, suite.proof, height, addr), false}, + {"channel id contains non-alpha", types.NewMsgChannelCloseConfirm(portid, invalidChannel, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgChannelCloseConfirm(portid, chanid, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgChannelCloseConfirm(portid, chanid, suite.proof, clienttypes.ZeroHeight(), addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgRecvPacketType() { + msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr) + + suite.Equal("recv_packet", msg.Type()) +} + +func (suite *TypesTestSuite) TestMsgRecvPacketValidateBasic() { + testCases := []struct { + name string + msg *types.MsgRecvPacket + expPass bool + }{ + {"success", types.NewMsgRecvPacket(packet, suite.proof, height, addr), true}, + {"proof height is zero", types.NewMsgRecvPacket(packet, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"proof contain empty proof", types.NewMsgRecvPacket(packet, emptyProof, height, addr), false}, + {"missing signer address", types.NewMsgRecvPacket(packet, suite.proof, height, emptyAddr), false}, + {"invalid packet", types.NewMsgRecvPacket(invalidPacket, suite.proof, height, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.NoError(err) + } else { + suite.Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgRecvPacketGetSigners() { + msg := types.NewMsgRecvPacket(packet, suite.proof, height, addr) + res := msg.GetSigners() + + expected := "[7465737461646472313131313131313131313131]" + suite.Equal(expected, fmt.Sprintf("%v", res)) +} + +func (suite *TypesTestSuite) TestMsgTimeoutValidateBasic() { + testCases := []struct { + name string + msg *types.MsgTimeout + expPass bool + }{ + {"success", types.NewMsgTimeout(packet, 1, suite.proof, height, addr), true}, + {"proof height must be > 0", types.NewMsgTimeout(packet, 1, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"seq 0", types.NewMsgTimeout(packet, 0, suite.proof, height, addr), false}, + {"missing signer address", types.NewMsgTimeout(packet, 1, suite.proof, height, emptyAddr), false}, + {"cannot submit an empty proof", types.NewMsgTimeout(packet, 1, emptyProof, height, addr), false}, + {"invalid packet", types.NewMsgTimeout(invalidPacket, 1, suite.proof, height, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgTimeoutOnCloseValidateBasic() { + testCases := []struct { + name string + msg sdk.Msg + expPass bool + }{ + {"success", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, height, addr), true}, + {"seq 0", types.NewMsgTimeoutOnClose(packet, 0, suite.proof, suite.proof, height, addr), false}, + {"empty proof", types.NewMsgTimeoutOnClose(packet, 1, emptyProof, suite.proof, height, addr), false}, + {"empty proof close", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, emptyProof, height, addr), false}, + {"proof height is zero", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"signer address is empty", types.NewMsgTimeoutOnClose(packet, 1, suite.proof, suite.proof, height, emptyAddr), false}, + {"invalid packet", types.NewMsgTimeoutOnClose(invalidPacket, 1, suite.proof, suite.proof, height, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *TypesTestSuite) TestMsgAcknowledgementValidateBasic() { + testCases := []struct { + name string + msg *types.MsgAcknowledgement + expPass bool + }{ + {"success", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, addr), true}, + {"proof height must be > 0", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, clienttypes.ZeroHeight(), addr), false}, + {"empty ack", types.NewMsgAcknowledgement(packet, nil, suite.proof, height, addr), false}, + {"missing signer address", types.NewMsgAcknowledgement(packet, packet.GetData(), suite.proof, height, emptyAddr), false}, + {"cannot submit an empty proof", types.NewMsgAcknowledgement(packet, packet.GetData(), emptyProof, height, addr), false}, + {"invalid packet", types.NewMsgAcknowledgement(invalidPacket, packet.GetData(), suite.proof, height, addr), false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/core/04-channel/types/packet.go b/x/ibc/core/04-channel/types/packet.go new file mode 100644 index 000000000000..b5c8d180438c --- /dev/null +++ b/x/ibc/core/04-channel/types/packet.go @@ -0,0 +1,112 @@ +package types + +import ( + "crypto/sha256" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CommitPacket returns the packet commitment bytes. The commitment consists of: +// sha256_hash(timeout_timestamp + timeout_height.RevisionNumber + timeout_height.RevisionHeight + sha256_hash(data)) +// from a given packet. This results in a fixed length preimage. +// NOTE: sdk.Uint64ToBigEndian sets the uint64 to a slice of length 8. +func CommitPacket(cdc codec.BinaryMarshaler, packet exported.PacketI) []byte { + timeoutHeight := packet.GetTimeoutHeight() + + buf := sdk.Uint64ToBigEndian(packet.GetTimeoutTimestamp()) + + revisionNumber := sdk.Uint64ToBigEndian(timeoutHeight.GetRevisionNumber()) + buf = append(buf, revisionNumber...) + + revisionHeight := sdk.Uint64ToBigEndian(timeoutHeight.GetRevisionHeight()) + buf = append(buf, revisionHeight...) + + dataHash := sha256.Sum256(packet.GetData()) + buf = append(buf, dataHash[:]...) + + hash := sha256.Sum256(buf) + return hash[:] +} + +// CommitAcknowledgement returns the hash of commitment bytes +func CommitAcknowledgement(data []byte) []byte { + hash := sha256.Sum256(data) + return hash[:] +} + +var _ exported.PacketI = (*Packet)(nil) + +// NewPacket creates a new Packet instance. It panics if the provided +// packet data interface is not registered. +func NewPacket( + data []byte, + sequence uint64, sourcePort, sourceChannel, + destinationPort, destinationChannel string, + timeoutHeight clienttypes.Height, timeoutTimestamp uint64, +) Packet { + return Packet{ + Data: data, + Sequence: sequence, + SourcePort: sourcePort, + SourceChannel: sourceChannel, + DestinationPort: destinationPort, + DestinationChannel: destinationChannel, + TimeoutHeight: timeoutHeight, + TimeoutTimestamp: timeoutTimestamp, + } +} + +// GetSequence implements PacketI interface +func (p Packet) GetSequence() uint64 { return p.Sequence } + +// GetSourcePort implements PacketI interface +func (p Packet) GetSourcePort() string { return p.SourcePort } + +// GetSourceChannel implements PacketI interface +func (p Packet) GetSourceChannel() string { return p.SourceChannel } + +// GetDestPort implements PacketI interface +func (p Packet) GetDestPort() string { return p.DestinationPort } + +// GetDestChannel implements PacketI interface +func (p Packet) GetDestChannel() string { return p.DestinationChannel } + +// GetData implements PacketI interface +func (p Packet) GetData() []byte { return p.Data } + +// GetTimeoutHeight implements PacketI interface +func (p Packet) GetTimeoutHeight() exported.Height { return p.TimeoutHeight } + +// GetTimeoutTimestamp implements PacketI interface +func (p Packet) GetTimeoutTimestamp() uint64 { return p.TimeoutTimestamp } + +// ValidateBasic implements PacketI interface +func (p Packet) ValidateBasic() error { + if err := host.PortIdentifierValidator(p.SourcePort); err != nil { + return sdkerrors.Wrap(err, "invalid source port ID") + } + if err := host.PortIdentifierValidator(p.DestinationPort); err != nil { + return sdkerrors.Wrap(err, "invalid destination port ID") + } + if err := host.ChannelIdentifierValidator(p.SourceChannel); err != nil { + return sdkerrors.Wrap(err, "invalid source channel ID") + } + if err := host.ChannelIdentifierValidator(p.DestinationChannel); err != nil { + return sdkerrors.Wrap(err, "invalid destination channel ID") + } + if p.Sequence == 0 { + return sdkerrors.Wrap(ErrInvalidPacket, "packet sequence cannot be 0") + } + if p.TimeoutHeight.IsZero() && p.TimeoutTimestamp == 0 { + return sdkerrors.Wrap(ErrInvalidPacket, "packet timeout height and packet timeout timestamp cannot both be 0") + } + if len(p.Data) == 0 { + return sdkerrors.Wrap(ErrInvalidPacket, "packet data bytes cannot be empty") + } + return nil +} diff --git a/x/ibc/core/04-channel/types/packet_test.go b/x/ibc/core/04-channel/types/packet_test.go new file mode 100644 index 000000000000..12ed828e665f --- /dev/null +++ b/x/ibc/core/04-channel/types/packet_test.go @@ -0,0 +1,53 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +func TestCommitPacket(t *testing.T) { + packet := types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp) + + registry := codectypes.NewInterfaceRegistry() + clienttypes.RegisterInterfaces(registry) + types.RegisterInterfaces(registry) + + cdc := codec.NewProtoCodec(registry) + + commitment := types.CommitPacket(cdc, &packet) + require.NotNil(t, commitment) +} + +func TestPacketValidateBasic(t *testing.T) { + testCases := []struct { + packet types.Packet + expPass bool + errMsg string + }{ + {types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp), true, ""}, + {types.NewPacket(validPacketData, 0, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp), false, "invalid sequence"}, + {types.NewPacket(validPacketData, 1, invalidPort, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp), false, "invalid source port"}, + {types.NewPacket(validPacketData, 1, portid, invalidChannel, cpportid, cpchanid, timeoutHeight, timeoutTimestamp), false, "invalid source channel"}, + {types.NewPacket(validPacketData, 1, portid, chanid, invalidPort, cpchanid, timeoutHeight, timeoutTimestamp), false, "invalid destination port"}, + {types.NewPacket(validPacketData, 1, portid, chanid, cpportid, invalidChannel, timeoutHeight, timeoutTimestamp), false, "invalid destination channel"}, + {types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, disabledTimeout, 0), false, "disabled both timeout height and timestamp"}, + {types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, disabledTimeout, timeoutTimestamp), true, "disabled timeout height, valid timeout timestamp"}, + {types.NewPacket(validPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, 0), true, "disabled timeout timestamp, valid timeout height"}, + {types.NewPacket(unknownPacketData, 1, portid, chanid, cpportid, cpchanid, timeoutHeight, timeoutTimestamp), true, ""}, + } + + for i, tc := range testCases { + err := tc.packet.ValidateBasic() + if tc.expPass { + require.NoError(t, err, "Msg %d failed: %s", i, tc.errMsg) + } else { + require.Error(t, err, "Invalid Msg %d passed: %s", i, tc.errMsg) + } + } +} diff --git a/x/ibc/core/04-channel/types/query.go b/x/ibc/core/04-channel/types/query.go new file mode 100644 index 000000000000..d1536dfc0565 --- /dev/null +++ b/x/ibc/core/04-channel/types/query.go @@ -0,0 +1,94 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "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{ + Channel: &channel, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryChannelClientStateResponse creates a newQueryChannelClientStateResponse instance +func NewQueryChannelClientStateResponse(identifiedClientState clienttypes.IdentifiedClientState, proof []byte, height clienttypes.Height) *QueryChannelClientStateResponse { + return &QueryChannelClientStateResponse{ + IdentifiedClientState: &identifiedClientState, + Proof: proof, + ProofHeight: height, + } +} + +// 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{ + ConsensusState: anyConsensusState, + ClientId: clientID, + Proof: proof, + ProofHeight: height, + } +} + +// 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, +) *QueryPacketCommitmentResponse { + return &QueryPacketCommitmentResponse{ + Commitment: commitment, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryPacketReceiptResponse creates a new QueryPacketReceiptResponse instance +func NewQueryPacketReceiptResponse( + recvd bool, proof []byte, height clienttypes.Height, +) *QueryPacketReceiptResponse { + return &QueryPacketReceiptResponse{ + Received: recvd, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryPacketAcknowledgementResponse creates a new QueryPacketAcknowledgementResponse instance +func NewQueryPacketAcknowledgementResponse( + acknowledgement []byte, proof []byte, height clienttypes.Height, +) *QueryPacketAcknowledgementResponse { + return &QueryPacketAcknowledgementResponse{ + Acknowledgement: acknowledgement, + Proof: proof, + ProofHeight: height, + } +} + +// NewQueryNextSequenceReceiveResponse creates a new QueryNextSequenceReceiveResponse instance +func NewQueryNextSequenceReceiveResponse( + sequence uint64, proof []byte, height clienttypes.Height, +) *QueryNextSequenceReceiveResponse { + return &QueryNextSequenceReceiveResponse{ + NextSequenceReceive: sequence, + Proof: proof, + ProofHeight: height, + } +} diff --git a/x/ibc/core/04-channel/types/query.pb.go b/x/ibc/core/04-channel/types/query.pb.go new file mode 100644 index 000000000000..c5cbd6e8f899 --- /dev/null +++ b/x/ibc/core/04-channel/types/query.pb.go @@ -0,0 +1,7991 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/channel/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + query "github.com/cosmos/cosmos-sdk/types/query" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "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 + +// QueryChannelRequest is the request type for the Query/Channel RPC method +type QueryChannelRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` +} + +func (m *QueryChannelRequest) Reset() { *m = QueryChannelRequest{} } +func (m *QueryChannelRequest) String() string { return proto.CompactTextString(m) } +func (*QueryChannelRequest) ProtoMessage() {} +func (*QueryChannelRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{0} +} +func (m *QueryChannelRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelRequest.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 *QueryChannelRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelRequest.Merge(m, src) +} +func (m *QueryChannelRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelRequest proto.InternalMessageInfo + +func (m *QueryChannelRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryChannelRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// QueryChannelResponse is the response type for the Query/Channel RPC method. +// Besides the Channel end, it includes a proof and the height from which the +// proof was retrieved. +type QueryChannelResponse struct { + // channel associated with the request identifiers + Channel *Channel `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryChannelResponse) Reset() { *m = QueryChannelResponse{} } +func (m *QueryChannelResponse) String() string { return proto.CompactTextString(m) } +func (*QueryChannelResponse) ProtoMessage() {} +func (*QueryChannelResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{1} +} +func (m *QueryChannelResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelResponse.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 *QueryChannelResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelResponse.Merge(m, src) +} +func (m *QueryChannelResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelResponse proto.InternalMessageInfo + +func (m *QueryChannelResponse) GetChannel() *Channel { + if m != nil { + return m.Channel + } + return nil +} + +func (m *QueryChannelResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryChannelResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryChannelsRequest is the request type for the Query/Channels RPC method +type QueryChannelsRequest struct { + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryChannelsRequest) Reset() { *m = QueryChannelsRequest{} } +func (m *QueryChannelsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryChannelsRequest) ProtoMessage() {} +func (*QueryChannelsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{2} +} +func (m *QueryChannelsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelsRequest.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 *QueryChannelsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelsRequest.Merge(m, src) +} +func (m *QueryChannelsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelsRequest proto.InternalMessageInfo + +func (m *QueryChannelsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryChannelsResponse is the response type for the Query/Channels RPC method. +type QueryChannelsResponse struct { + // list of stored channels of the chain. + Channels []*IdentifiedChannel `protobuf:"bytes,1,rep,name=channels,proto3" json:"channels,omitempty"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` +} + +func (m *QueryChannelsResponse) Reset() { *m = QueryChannelsResponse{} } +func (m *QueryChannelsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryChannelsResponse) ProtoMessage() {} +func (*QueryChannelsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{3} +} +func (m *QueryChannelsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelsResponse.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 *QueryChannelsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelsResponse.Merge(m, src) +} +func (m *QueryChannelsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelsResponse proto.InternalMessageInfo + +func (m *QueryChannelsResponse) GetChannels() []*IdentifiedChannel { + if m != nil { + return m.Channels + } + return nil +} + +func (m *QueryChannelsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryChannelsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryConnectionChannelsRequest is the request type for the +// Query/QueryConnectionChannels RPC method +type QueryConnectionChannelsRequest struct { + // connection unique identifier + Connection string `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryConnectionChannelsRequest) Reset() { *m = QueryConnectionChannelsRequest{} } +func (m *QueryConnectionChannelsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionChannelsRequest) ProtoMessage() {} +func (*QueryConnectionChannelsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{4} +} +func (m *QueryConnectionChannelsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionChannelsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionChannelsRequest.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 *QueryConnectionChannelsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionChannelsRequest.Merge(m, src) +} +func (m *QueryConnectionChannelsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionChannelsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionChannelsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionChannelsRequest proto.InternalMessageInfo + +func (m *QueryConnectionChannelsRequest) GetConnection() string { + if m != nil { + return m.Connection + } + return "" +} + +func (m *QueryConnectionChannelsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryConnectionChannelsResponse is the Response type for the +// Query/QueryConnectionChannels RPC method +type QueryConnectionChannelsResponse struct { + // list of channels associated with a connection. + Channels []*IdentifiedChannel `protobuf:"bytes,1,rep,name=channels,proto3" json:"channels,omitempty"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` +} + +func (m *QueryConnectionChannelsResponse) Reset() { *m = QueryConnectionChannelsResponse{} } +func (m *QueryConnectionChannelsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryConnectionChannelsResponse) ProtoMessage() {} +func (*QueryConnectionChannelsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{5} +} +func (m *QueryConnectionChannelsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryConnectionChannelsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryConnectionChannelsResponse.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 *QueryConnectionChannelsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryConnectionChannelsResponse.Merge(m, src) +} +func (m *QueryConnectionChannelsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryConnectionChannelsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryConnectionChannelsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryConnectionChannelsResponse proto.InternalMessageInfo + +func (m *QueryConnectionChannelsResponse) GetChannels() []*IdentifiedChannel { + if m != nil { + return m.Channels + } + return nil +} + +func (m *QueryConnectionChannelsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryConnectionChannelsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryChannelClientStateRequest is the request type for the Query/ClientState +// RPC method +type QueryChannelClientStateRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` +} + +func (m *QueryChannelClientStateRequest) Reset() { *m = QueryChannelClientStateRequest{} } +func (m *QueryChannelClientStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryChannelClientStateRequest) ProtoMessage() {} +func (*QueryChannelClientStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{6} +} +func (m *QueryChannelClientStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelClientStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelClientStateRequest.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 *QueryChannelClientStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelClientStateRequest.Merge(m, src) +} +func (m *QueryChannelClientStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelClientStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelClientStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelClientStateRequest proto.InternalMessageInfo + +func (m *QueryChannelClientStateRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryChannelClientStateRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +type QueryChannelClientStateResponse struct { + // client state associated with the channel + IdentifiedClientState *types.IdentifiedClientState `protobuf:"bytes,1,opt,name=identified_client_state,json=identifiedClientState,proto3" json:"identified_client_state,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryChannelClientStateResponse) Reset() { *m = QueryChannelClientStateResponse{} } +func (m *QueryChannelClientStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryChannelClientStateResponse) ProtoMessage() {} +func (*QueryChannelClientStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{7} +} +func (m *QueryChannelClientStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelClientStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelClientStateResponse.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 *QueryChannelClientStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelClientStateResponse.Merge(m, src) +} +func (m *QueryChannelClientStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelClientStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelClientStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelClientStateResponse proto.InternalMessageInfo + +func (m *QueryChannelClientStateResponse) GetIdentifiedClientState() *types.IdentifiedClientState { + if m != nil { + return m.IdentifiedClientState + } + return nil +} + +func (m *QueryChannelClientStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryChannelClientStateResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryChannelConsensusStateRequest is the request type for the +// Query/ConsensusState RPC method +type QueryChannelConsensusStateRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // revision number of the consensus state + RevisionNumber uint64 `protobuf:"varint,3,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty"` + // revision height of the consensus state + RevisionHeight uint64 `protobuf:"varint,4,opt,name=revision_height,json=revisionHeight,proto3" json:"revision_height,omitempty"` +} + +func (m *QueryChannelConsensusStateRequest) Reset() { *m = QueryChannelConsensusStateRequest{} } +func (m *QueryChannelConsensusStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryChannelConsensusStateRequest) ProtoMessage() {} +func (*QueryChannelConsensusStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{8} +} +func (m *QueryChannelConsensusStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelConsensusStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelConsensusStateRequest.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 *QueryChannelConsensusStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelConsensusStateRequest.Merge(m, src) +} +func (m *QueryChannelConsensusStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelConsensusStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelConsensusStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelConsensusStateRequest proto.InternalMessageInfo + +func (m *QueryChannelConsensusStateRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryChannelConsensusStateRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryChannelConsensusStateRequest) GetRevisionNumber() uint64 { + if m != nil { + return m.RevisionNumber + } + return 0 +} + +func (m *QueryChannelConsensusStateRequest) GetRevisionHeight() uint64 { + if m != nil { + return m.RevisionHeight + } + return 0 +} + +// QueryChannelClientStateResponse is the Response type for the +// Query/QueryChannelClientState RPC method +type QueryChannelConsensusStateResponse struct { + // consensus state associated with the channel + ConsensusState *types1.Any `protobuf:"bytes,1,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty"` + // client ID associated with the consensus state + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,3,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryChannelConsensusStateResponse) Reset() { *m = QueryChannelConsensusStateResponse{} } +func (m *QueryChannelConsensusStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryChannelConsensusStateResponse) ProtoMessage() {} +func (*QueryChannelConsensusStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{9} +} +func (m *QueryChannelConsensusStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryChannelConsensusStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryChannelConsensusStateResponse.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 *QueryChannelConsensusStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryChannelConsensusStateResponse.Merge(m, src) +} +func (m *QueryChannelConsensusStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryChannelConsensusStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryChannelConsensusStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryChannelConsensusStateResponse proto.InternalMessageInfo + +func (m *QueryChannelConsensusStateResponse) GetConsensusState() *types1.Any { + if m != nil { + return m.ConsensusState + } + return nil +} + +func (m *QueryChannelConsensusStateResponse) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *QueryChannelConsensusStateResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryChannelConsensusStateResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryPacketCommitmentRequest is the request type for the +// Query/PacketCommitment RPC method +type QueryPacketCommitmentRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // packet sequence + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *QueryPacketCommitmentRequest) Reset() { *m = QueryPacketCommitmentRequest{} } +func (m *QueryPacketCommitmentRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPacketCommitmentRequest) ProtoMessage() {} +func (*QueryPacketCommitmentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{10} +} +func (m *QueryPacketCommitmentRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketCommitmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketCommitmentRequest.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 *QueryPacketCommitmentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketCommitmentRequest.Merge(m, src) +} +func (m *QueryPacketCommitmentRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketCommitmentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketCommitmentRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketCommitmentRequest proto.InternalMessageInfo + +func (m *QueryPacketCommitmentRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryPacketCommitmentRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryPacketCommitmentRequest) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// QueryPacketCommitmentResponse defines the client query response for a packet +// which also includes a proof and the height from which the proof was +// retrieved +type QueryPacketCommitmentResponse struct { + // packet associated with the request fields + Commitment []byte `protobuf:"bytes,1,opt,name=commitment,proto3" json:"commitment,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryPacketCommitmentResponse) Reset() { *m = QueryPacketCommitmentResponse{} } +func (m *QueryPacketCommitmentResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPacketCommitmentResponse) ProtoMessage() {} +func (*QueryPacketCommitmentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{11} +} +func (m *QueryPacketCommitmentResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketCommitmentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketCommitmentResponse.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 *QueryPacketCommitmentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketCommitmentResponse.Merge(m, src) +} +func (m *QueryPacketCommitmentResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketCommitmentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketCommitmentResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketCommitmentResponse proto.InternalMessageInfo + +func (m *QueryPacketCommitmentResponse) GetCommitment() []byte { + if m != nil { + return m.Commitment + } + return nil +} + +func (m *QueryPacketCommitmentResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryPacketCommitmentResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryPacketCommitmentsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +type QueryPacketCommitmentsRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryPacketCommitmentsRequest) Reset() { *m = QueryPacketCommitmentsRequest{} } +func (m *QueryPacketCommitmentsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPacketCommitmentsRequest) ProtoMessage() {} +func (*QueryPacketCommitmentsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{12} +} +func (m *QueryPacketCommitmentsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketCommitmentsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketCommitmentsRequest.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 *QueryPacketCommitmentsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketCommitmentsRequest.Merge(m, src) +} +func (m *QueryPacketCommitmentsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketCommitmentsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketCommitmentsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketCommitmentsRequest proto.InternalMessageInfo + +func (m *QueryPacketCommitmentsRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryPacketCommitmentsRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryPacketCommitmentsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryPacketCommitmentsResponse is the request type for the +// Query/QueryPacketCommitments RPC method +type QueryPacketCommitmentsResponse struct { + Commitments []*PacketState `protobuf:"bytes,1,rep,name=commitments,proto3" json:"commitments,omitempty"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` +} + +func (m *QueryPacketCommitmentsResponse) Reset() { *m = QueryPacketCommitmentsResponse{} } +func (m *QueryPacketCommitmentsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPacketCommitmentsResponse) ProtoMessage() {} +func (*QueryPacketCommitmentsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{13} +} +func (m *QueryPacketCommitmentsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketCommitmentsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketCommitmentsResponse.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 *QueryPacketCommitmentsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketCommitmentsResponse.Merge(m, src) +} +func (m *QueryPacketCommitmentsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketCommitmentsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketCommitmentsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketCommitmentsResponse proto.InternalMessageInfo + +func (m *QueryPacketCommitmentsResponse) GetCommitments() []*PacketState { + if m != nil { + return m.Commitments + } + return nil +} + +func (m *QueryPacketCommitmentsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryPacketCommitmentsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryPacketReceiptRequest is the request type for the +// Query/PacketReceipt RPC method +type QueryPacketReceiptRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // packet sequence + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *QueryPacketReceiptRequest) Reset() { *m = QueryPacketReceiptRequest{} } +func (m *QueryPacketReceiptRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPacketReceiptRequest) ProtoMessage() {} +func (*QueryPacketReceiptRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{14} +} +func (m *QueryPacketReceiptRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketReceiptRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketReceiptRequest.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 *QueryPacketReceiptRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketReceiptRequest.Merge(m, src) +} +func (m *QueryPacketReceiptRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketReceiptRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketReceiptRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketReceiptRequest proto.InternalMessageInfo + +func (m *QueryPacketReceiptRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryPacketReceiptRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryPacketReceiptRequest) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// QueryPacketReceiptResponse defines the client query response for a packet receipt +// which also includes a proof, and the height from which the proof was +// retrieved +type QueryPacketReceiptResponse struct { + // success flag for if receipt exists + Received bool `protobuf:"varint,2,opt,name=received,proto3" json:"received,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,3,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryPacketReceiptResponse) Reset() { *m = QueryPacketReceiptResponse{} } +func (m *QueryPacketReceiptResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPacketReceiptResponse) ProtoMessage() {} +func (*QueryPacketReceiptResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{15} +} +func (m *QueryPacketReceiptResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketReceiptResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketReceiptResponse.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 *QueryPacketReceiptResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketReceiptResponse.Merge(m, src) +} +func (m *QueryPacketReceiptResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketReceiptResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketReceiptResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketReceiptResponse proto.InternalMessageInfo + +func (m *QueryPacketReceiptResponse) GetReceived() bool { + if m != nil { + return m.Received + } + return false +} + +func (m *QueryPacketReceiptResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryPacketReceiptResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryPacketAcknowledgementRequest is the request type for the +// Query/PacketAcknowledgement RPC method +type QueryPacketAcknowledgementRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // packet sequence + Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *QueryPacketAcknowledgementRequest) Reset() { *m = QueryPacketAcknowledgementRequest{} } +func (m *QueryPacketAcknowledgementRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPacketAcknowledgementRequest) ProtoMessage() {} +func (*QueryPacketAcknowledgementRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{16} +} +func (m *QueryPacketAcknowledgementRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketAcknowledgementRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketAcknowledgementRequest.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 *QueryPacketAcknowledgementRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketAcknowledgementRequest.Merge(m, src) +} +func (m *QueryPacketAcknowledgementRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketAcknowledgementRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketAcknowledgementRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketAcknowledgementRequest proto.InternalMessageInfo + +func (m *QueryPacketAcknowledgementRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryPacketAcknowledgementRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryPacketAcknowledgementRequest) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// QueryPacketAcknowledgementResponse defines the client query response for a +// packet which also includes a proof and the height from which the +// proof was retrieved +type QueryPacketAcknowledgementResponse struct { + // packet associated with the request fields + Acknowledgement []byte `protobuf:"bytes,1,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryPacketAcknowledgementResponse) Reset() { *m = QueryPacketAcknowledgementResponse{} } +func (m *QueryPacketAcknowledgementResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPacketAcknowledgementResponse) ProtoMessage() {} +func (*QueryPacketAcknowledgementResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{17} +} +func (m *QueryPacketAcknowledgementResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketAcknowledgementResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketAcknowledgementResponse.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 *QueryPacketAcknowledgementResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketAcknowledgementResponse.Merge(m, src) +} +func (m *QueryPacketAcknowledgementResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketAcknowledgementResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketAcknowledgementResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketAcknowledgementResponse proto.InternalMessageInfo + +func (m *QueryPacketAcknowledgementResponse) GetAcknowledgement() []byte { + if m != nil { + return m.Acknowledgement + } + return nil +} + +func (m *QueryPacketAcknowledgementResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryPacketAcknowledgementResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +// QueryPacketAcknowledgementsRequest is the request type for the +// Query/QueryPacketCommitments RPC method +type QueryPacketAcknowledgementsRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // pagination request + Pagination *query.PageRequest `protobuf:"bytes,3,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryPacketAcknowledgementsRequest) Reset() { *m = QueryPacketAcknowledgementsRequest{} } +func (m *QueryPacketAcknowledgementsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPacketAcknowledgementsRequest) ProtoMessage() {} +func (*QueryPacketAcknowledgementsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{18} +} +func (m *QueryPacketAcknowledgementsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketAcknowledgementsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketAcknowledgementsRequest.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 *QueryPacketAcknowledgementsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketAcknowledgementsRequest.Merge(m, src) +} +func (m *QueryPacketAcknowledgementsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketAcknowledgementsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketAcknowledgementsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketAcknowledgementsRequest proto.InternalMessageInfo + +func (m *QueryPacketAcknowledgementsRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryPacketAcknowledgementsRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryPacketAcknowledgementsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryPacketAcknowledgemetsResponse is the request type for the +// Query/QueryPacketAcknowledgements RPC method +type QueryPacketAcknowledgementsResponse struct { + Acknowledgements []*PacketState `protobuf:"bytes,1,rep,name=acknowledgements,proto3" json:"acknowledgements,omitempty"` + // pagination response + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,3,opt,name=height,proto3" json:"height"` +} + +func (m *QueryPacketAcknowledgementsResponse) Reset() { *m = QueryPacketAcknowledgementsResponse{} } +func (m *QueryPacketAcknowledgementsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPacketAcknowledgementsResponse) ProtoMessage() {} +func (*QueryPacketAcknowledgementsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{19} +} +func (m *QueryPacketAcknowledgementsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPacketAcknowledgementsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPacketAcknowledgementsResponse.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 *QueryPacketAcknowledgementsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPacketAcknowledgementsResponse.Merge(m, src) +} +func (m *QueryPacketAcknowledgementsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPacketAcknowledgementsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPacketAcknowledgementsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPacketAcknowledgementsResponse proto.InternalMessageInfo + +func (m *QueryPacketAcknowledgementsResponse) GetAcknowledgements() []*PacketState { + if m != nil { + return m.Acknowledgements + } + return nil +} + +func (m *QueryPacketAcknowledgementsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func (m *QueryPacketAcknowledgementsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryUnreceivedPacketsRequest is the request type for the +// Query/UnreceivedPackets RPC method +type QueryUnreceivedPacketsRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // list of packet sequences + PacketCommitmentSequences []uint64 `protobuf:"varint,3,rep,packed,name=packet_commitment_sequences,json=packetCommitmentSequences,proto3" json:"packet_commitment_sequences,omitempty"` +} + +func (m *QueryUnreceivedPacketsRequest) Reset() { *m = QueryUnreceivedPacketsRequest{} } +func (m *QueryUnreceivedPacketsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryUnreceivedPacketsRequest) ProtoMessage() {} +func (*QueryUnreceivedPacketsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{20} +} +func (m *QueryUnreceivedPacketsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnreceivedPacketsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnreceivedPacketsRequest.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 *QueryUnreceivedPacketsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnreceivedPacketsRequest.Merge(m, src) +} +func (m *QueryUnreceivedPacketsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryUnreceivedPacketsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnreceivedPacketsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnreceivedPacketsRequest proto.InternalMessageInfo + +func (m *QueryUnreceivedPacketsRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryUnreceivedPacketsRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryUnreceivedPacketsRequest) GetPacketCommitmentSequences() []uint64 { + if m != nil { + return m.PacketCommitmentSequences + } + return nil +} + +// QueryUnreceivedPacketsResponse is the response type for the +// Query/UnreceivedPacketCommitments RPC method +type QueryUnreceivedPacketsResponse struct { + // list of unreceived packet sequences + Sequences []uint64 `protobuf:"varint,1,rep,packed,name=sequences,proto3" json:"sequences,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` +} + +func (m *QueryUnreceivedPacketsResponse) Reset() { *m = QueryUnreceivedPacketsResponse{} } +func (m *QueryUnreceivedPacketsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryUnreceivedPacketsResponse) ProtoMessage() {} +func (*QueryUnreceivedPacketsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{21} +} +func (m *QueryUnreceivedPacketsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnreceivedPacketsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnreceivedPacketsResponse.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 *QueryUnreceivedPacketsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnreceivedPacketsResponse.Merge(m, src) +} +func (m *QueryUnreceivedPacketsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryUnreceivedPacketsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnreceivedPacketsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnreceivedPacketsResponse proto.InternalMessageInfo + +func (m *QueryUnreceivedPacketsResponse) GetSequences() []uint64 { + if m != nil { + return m.Sequences + } + return nil +} + +func (m *QueryUnreceivedPacketsResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryUnreceivedAcks is the request type for the +// Query/UnreceivedAcks RPC method +type QueryUnreceivedAcksRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` + // list of acknowledgement sequences + PacketAckSequences []uint64 `protobuf:"varint,3,rep,packed,name=packet_ack_sequences,json=packetAckSequences,proto3" json:"packet_ack_sequences,omitempty"` +} + +func (m *QueryUnreceivedAcksRequest) Reset() { *m = QueryUnreceivedAcksRequest{} } +func (m *QueryUnreceivedAcksRequest) String() string { return proto.CompactTextString(m) } +func (*QueryUnreceivedAcksRequest) ProtoMessage() {} +func (*QueryUnreceivedAcksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{22} +} +func (m *QueryUnreceivedAcksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnreceivedAcksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnreceivedAcksRequest.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 *QueryUnreceivedAcksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnreceivedAcksRequest.Merge(m, src) +} +func (m *QueryUnreceivedAcksRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryUnreceivedAcksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnreceivedAcksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnreceivedAcksRequest proto.InternalMessageInfo + +func (m *QueryUnreceivedAcksRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryUnreceivedAcksRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +func (m *QueryUnreceivedAcksRequest) GetPacketAckSequences() []uint64 { + if m != nil { + return m.PacketAckSequences + } + return nil +} + +// QueryUnreceivedAcksResponse is the response type for the +// Query/UnreceivedAcks RPC method +type QueryUnreceivedAcksResponse struct { + // list of unreceived acknowledgement sequences + Sequences []uint64 `protobuf:"varint,1,rep,packed,name=sequences,proto3" json:"sequences,omitempty"` + // query block height + Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` +} + +func (m *QueryUnreceivedAcksResponse) Reset() { *m = QueryUnreceivedAcksResponse{} } +func (m *QueryUnreceivedAcksResponse) String() string { return proto.CompactTextString(m) } +func (*QueryUnreceivedAcksResponse) ProtoMessage() {} +func (*QueryUnreceivedAcksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{23} +} +func (m *QueryUnreceivedAcksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnreceivedAcksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnreceivedAcksResponse.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 *QueryUnreceivedAcksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnreceivedAcksResponse.Merge(m, src) +} +func (m *QueryUnreceivedAcksResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryUnreceivedAcksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnreceivedAcksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnreceivedAcksResponse proto.InternalMessageInfo + +func (m *QueryUnreceivedAcksResponse) GetSequences() []uint64 { + if m != nil { + return m.Sequences + } + return nil +} + +func (m *QueryUnreceivedAcksResponse) GetHeight() types.Height { + if m != nil { + return m.Height + } + return types.Height{} +} + +// QueryNextSequenceReceiveRequest is the request type for the +// Query/QueryNextSequenceReceiveRequest RPC method +type QueryNextSequenceReceiveRequest struct { + // port unique identifier + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty"` + // channel unique identifier + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` +} + +func (m *QueryNextSequenceReceiveRequest) Reset() { *m = QueryNextSequenceReceiveRequest{} } +func (m *QueryNextSequenceReceiveRequest) String() string { return proto.CompactTextString(m) } +func (*QueryNextSequenceReceiveRequest) ProtoMessage() {} +func (*QueryNextSequenceReceiveRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{24} +} +func (m *QueryNextSequenceReceiveRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNextSequenceReceiveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNextSequenceReceiveRequest.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 *QueryNextSequenceReceiveRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNextSequenceReceiveRequest.Merge(m, src) +} +func (m *QueryNextSequenceReceiveRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryNextSequenceReceiveRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNextSequenceReceiveRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNextSequenceReceiveRequest proto.InternalMessageInfo + +func (m *QueryNextSequenceReceiveRequest) GetPortId() string { + if m != nil { + return m.PortId + } + return "" +} + +func (m *QueryNextSequenceReceiveRequest) GetChannelId() string { + if m != nil { + return m.ChannelId + } + return "" +} + +// QuerySequenceResponse is the request type for the +// Query/QueryNextSequenceReceiveResponse RPC method +type QueryNextSequenceReceiveResponse struct { + // next sequence receive number + NextSequenceReceive uint64 `protobuf:"varint,1,opt,name=next_sequence_receive,json=nextSequenceReceive,proto3" json:"next_sequence_receive,omitempty"` + // merkle proof of existence + Proof []byte `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` + // height at which the proof was retrieved + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height"` +} + +func (m *QueryNextSequenceReceiveResponse) Reset() { *m = QueryNextSequenceReceiveResponse{} } +func (m *QueryNextSequenceReceiveResponse) String() string { return proto.CompactTextString(m) } +func (*QueryNextSequenceReceiveResponse) ProtoMessage() {} +func (*QueryNextSequenceReceiveResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1034a1e9abc4cca1, []int{25} +} +func (m *QueryNextSequenceReceiveResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryNextSequenceReceiveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryNextSequenceReceiveResponse.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 *QueryNextSequenceReceiveResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryNextSequenceReceiveResponse.Merge(m, src) +} +func (m *QueryNextSequenceReceiveResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryNextSequenceReceiveResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryNextSequenceReceiveResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryNextSequenceReceiveResponse proto.InternalMessageInfo + +func (m *QueryNextSequenceReceiveResponse) GetNextSequenceReceive() uint64 { + if m != nil { + return m.NextSequenceReceive + } + return 0 +} + +func (m *QueryNextSequenceReceiveResponse) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + +func (m *QueryNextSequenceReceiveResponse) GetProofHeight() types.Height { + if m != nil { + return m.ProofHeight + } + return types.Height{} +} + +func init() { + proto.RegisterType((*QueryChannelRequest)(nil), "ibc.core.channel.v1.QueryChannelRequest") + proto.RegisterType((*QueryChannelResponse)(nil), "ibc.core.channel.v1.QueryChannelResponse") + proto.RegisterType((*QueryChannelsRequest)(nil), "ibc.core.channel.v1.QueryChannelsRequest") + proto.RegisterType((*QueryChannelsResponse)(nil), "ibc.core.channel.v1.QueryChannelsResponse") + proto.RegisterType((*QueryConnectionChannelsRequest)(nil), "ibc.core.channel.v1.QueryConnectionChannelsRequest") + proto.RegisterType((*QueryConnectionChannelsResponse)(nil), "ibc.core.channel.v1.QueryConnectionChannelsResponse") + proto.RegisterType((*QueryChannelClientStateRequest)(nil), "ibc.core.channel.v1.QueryChannelClientStateRequest") + proto.RegisterType((*QueryChannelClientStateResponse)(nil), "ibc.core.channel.v1.QueryChannelClientStateResponse") + proto.RegisterType((*QueryChannelConsensusStateRequest)(nil), "ibc.core.channel.v1.QueryChannelConsensusStateRequest") + proto.RegisterType((*QueryChannelConsensusStateResponse)(nil), "ibc.core.channel.v1.QueryChannelConsensusStateResponse") + proto.RegisterType((*QueryPacketCommitmentRequest)(nil), "ibc.core.channel.v1.QueryPacketCommitmentRequest") + proto.RegisterType((*QueryPacketCommitmentResponse)(nil), "ibc.core.channel.v1.QueryPacketCommitmentResponse") + proto.RegisterType((*QueryPacketCommitmentsRequest)(nil), "ibc.core.channel.v1.QueryPacketCommitmentsRequest") + proto.RegisterType((*QueryPacketCommitmentsResponse)(nil), "ibc.core.channel.v1.QueryPacketCommitmentsResponse") + proto.RegisterType((*QueryPacketReceiptRequest)(nil), "ibc.core.channel.v1.QueryPacketReceiptRequest") + proto.RegisterType((*QueryPacketReceiptResponse)(nil), "ibc.core.channel.v1.QueryPacketReceiptResponse") + proto.RegisterType((*QueryPacketAcknowledgementRequest)(nil), "ibc.core.channel.v1.QueryPacketAcknowledgementRequest") + proto.RegisterType((*QueryPacketAcknowledgementResponse)(nil), "ibc.core.channel.v1.QueryPacketAcknowledgementResponse") + proto.RegisterType((*QueryPacketAcknowledgementsRequest)(nil), "ibc.core.channel.v1.QueryPacketAcknowledgementsRequest") + proto.RegisterType((*QueryPacketAcknowledgementsResponse)(nil), "ibc.core.channel.v1.QueryPacketAcknowledgementsResponse") + proto.RegisterType((*QueryUnreceivedPacketsRequest)(nil), "ibc.core.channel.v1.QueryUnreceivedPacketsRequest") + proto.RegisterType((*QueryUnreceivedPacketsResponse)(nil), "ibc.core.channel.v1.QueryUnreceivedPacketsResponse") + proto.RegisterType((*QueryUnreceivedAcksRequest)(nil), "ibc.core.channel.v1.QueryUnreceivedAcksRequest") + proto.RegisterType((*QueryUnreceivedAcksResponse)(nil), "ibc.core.channel.v1.QueryUnreceivedAcksResponse") + proto.RegisterType((*QueryNextSequenceReceiveRequest)(nil), "ibc.core.channel.v1.QueryNextSequenceReceiveRequest") + proto.RegisterType((*QueryNextSequenceReceiveResponse)(nil), "ibc.core.channel.v1.QueryNextSequenceReceiveResponse") +} + +func init() { proto.RegisterFile("ibc/core/channel/v1/query.proto", fileDescriptor_1034a1e9abc4cca1) } + +var fileDescriptor_1034a1e9abc4cca1 = []byte{ + // 1482 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdd, 0x6f, 0x14, 0x55, + 0x14, 0xef, 0xdd, 0x16, 0x68, 0x0f, 0xc8, 0xc7, 0x6d, 0x0b, 0x65, 0x28, 0x4b, 0x19, 0x8d, 0x14, + 0x12, 0xe6, 0xd2, 0x82, 0x48, 0x62, 0xd0, 0x94, 0x26, 0x62, 0x13, 0x40, 0x1c, 0x40, 0x81, 0x28, + 0x9b, 0xd9, 0xd9, 0xcb, 0x76, 0xd2, 0x76, 0x66, 0xd8, 0x99, 0x2d, 0x6d, 0x9a, 0x7d, 0x50, 0x13, + 0xe2, 0x83, 0x24, 0x26, 0x3c, 0x68, 0x7c, 0xf1, 0xc5, 0xc4, 0xf0, 0xe0, 0x83, 0xff, 0x83, 0x0f, + 0xbc, 0x49, 0xa2, 0x26, 0x18, 0x13, 0x24, 0x60, 0x22, 0x0f, 0x3e, 0xeb, 0xab, 0x99, 0xfb, 0x31, + 0x1f, 0xbb, 0x33, 0xd3, 0x2e, 0xdb, 0x4d, 0x1a, 0x9f, 0xba, 0x73, 0xe7, 0x9c, 0x73, 0x7f, 0xbf, + 0xdf, 0xb9, 0xe7, 0xec, 0x3d, 0x5b, 0x38, 0x60, 0x95, 0x4d, 0x62, 0x3a, 0x35, 0x4a, 0xcc, 0x59, + 0xc3, 0xb6, 0xe9, 0x3c, 0x59, 0x9c, 0x20, 0xb7, 0xea, 0xb4, 0xb6, 0xac, 0xb9, 0x35, 0xc7, 0x77, + 0xf0, 0xa0, 0x55, 0x36, 0xb5, 0xc0, 0x40, 0x13, 0x06, 0xda, 0xe2, 0x84, 0x12, 0xf3, 0x9a, 0xb7, + 0xa8, 0xed, 0x07, 0x4e, 0xfc, 0x13, 0xf7, 0x52, 0x8e, 0x98, 0x8e, 0xb7, 0xe0, 0x78, 0xa4, 0x6c, + 0x78, 0x94, 0x87, 0x23, 0x8b, 0x13, 0x65, 0xea, 0x1b, 0x13, 0xc4, 0x35, 0xaa, 0x96, 0x6d, 0xf8, + 0x96, 0x63, 0x0b, 0xdb, 0x83, 0x69, 0x10, 0xe4, 0x66, 0xdc, 0x64, 0xb4, 0xea, 0x38, 0xd5, 0x79, + 0x4a, 0x0c, 0xd7, 0x22, 0x86, 0x6d, 0x3b, 0x3e, 0xf3, 0xf7, 0xc4, 0xdb, 0xbd, 0xe2, 0x2d, 0x7b, + 0x2a, 0xd7, 0x6f, 0x12, 0xc3, 0x16, 0xe8, 0x95, 0xa1, 0xaa, 0x53, 0x75, 0xd8, 0x47, 0x12, 0x7c, + 0xe2, 0xab, 0xea, 0x79, 0x18, 0x7c, 0x2f, 0xc0, 0x34, 0xcd, 0x37, 0xd1, 0xe9, 0xad, 0x3a, 0xf5, + 0x7c, 0xbc, 0x07, 0xb6, 0xb8, 0x4e, 0xcd, 0x2f, 0x59, 0x95, 0x11, 0x34, 0x86, 0xc6, 0x07, 0xf4, + 0xcd, 0xc1, 0xe3, 0x4c, 0x05, 0xef, 0x07, 0x10, 0x78, 0x82, 0x77, 0x05, 0xf6, 0x6e, 0x40, 0xac, + 0xcc, 0x54, 0xd4, 0xfb, 0x08, 0x86, 0x92, 0xf1, 0x3c, 0xd7, 0xb1, 0x3d, 0x8a, 0x4f, 0xc2, 0x16, + 0x61, 0xc5, 0x02, 0x6e, 0x9d, 0x1c, 0xd5, 0x52, 0xd4, 0xd4, 0xa4, 0x9b, 0x34, 0xc6, 0x43, 0xb0, + 0xc9, 0xad, 0x39, 0xce, 0x4d, 0xb6, 0xd5, 0x36, 0x9d, 0x3f, 0xe0, 0x69, 0xd8, 0xc6, 0x3e, 0x94, + 0x66, 0xa9, 0x55, 0x9d, 0xf5, 0x47, 0x7a, 0x59, 0x48, 0x25, 0x16, 0x92, 0x67, 0x60, 0x71, 0x42, + 0x7b, 0x87, 0x59, 0x9c, 0xe9, 0x7b, 0xf0, 0xf8, 0x40, 0x8f, 0xbe, 0x95, 0x79, 0xf1, 0x25, 0xf5, + 0x46, 0x12, 0xaa, 0x27, 0xb9, 0xbf, 0x0d, 0x10, 0x25, 0x46, 0xa0, 0x7d, 0x55, 0xe3, 0x59, 0xd4, + 0x82, 0x2c, 0x6a, 0xfc, 0x50, 0x88, 0x2c, 0x6a, 0x17, 0x8d, 0x2a, 0x15, 0xbe, 0x7a, 0xcc, 0x53, + 0x7d, 0x8c, 0x60, 0xb8, 0x69, 0x03, 0x21, 0xc6, 0x19, 0xe8, 0x17, 0xfc, 0xbc, 0x11, 0x34, 0xd6, + 0xcb, 0xe2, 0xa7, 0xa9, 0x31, 0x53, 0xa1, 0xb6, 0x6f, 0xdd, 0xb4, 0x68, 0x45, 0xea, 0x12, 0xfa, + 0xe1, 0xb3, 0x09, 0x94, 0x05, 0x86, 0xf2, 0xd0, 0xaa, 0x28, 0x39, 0x80, 0x38, 0x4c, 0x7c, 0x0a, + 0x36, 0xb7, 0xa9, 0xa2, 0xb0, 0x57, 0x3f, 0x43, 0x50, 0xe4, 0x04, 0x1d, 0xdb, 0xa6, 0x66, 0x10, + 0xad, 0x59, 0xcb, 0x22, 0x80, 0x19, 0xbe, 0x14, 0x47, 0x29, 0xb6, 0xd2, 0xa4, 0x75, 0xe1, 0x85, + 0xb5, 0x7e, 0x8e, 0xe0, 0x40, 0x26, 0x94, 0xff, 0x97, 0xea, 0x57, 0xa5, 0xe8, 0x1c, 0xd3, 0x34, + 0xb3, 0xbe, 0xe4, 0x1b, 0x3e, 0xed, 0xb4, 0x78, 0xff, 0x08, 0x45, 0x4c, 0x09, 0x2d, 0x44, 0x34, + 0x60, 0x8f, 0x15, 0xea, 0x53, 0xe2, 0x50, 0x4b, 0x5e, 0x60, 0x22, 0x2a, 0xe5, 0x70, 0x1a, 0x91, + 0x98, 0xa4, 0xb1, 0x98, 0xc3, 0x56, 0xda, 0x72, 0x37, 0x4b, 0xfe, 0x7b, 0x04, 0x07, 0x13, 0x0c, + 0x03, 0x4e, 0xb6, 0x57, 0xf7, 0xd6, 0x43, 0x3f, 0x7c, 0x08, 0x76, 0xd4, 0xe8, 0xa2, 0xe5, 0x59, + 0x8e, 0x5d, 0xb2, 0xeb, 0x0b, 0x65, 0x5a, 0x63, 0x28, 0xfb, 0xf4, 0xed, 0x72, 0xf9, 0x02, 0x5b, + 0x4d, 0x18, 0x0a, 0x3a, 0x7d, 0x49, 0x43, 0x81, 0xf7, 0x77, 0x04, 0x6a, 0x1e, 0x5e, 0x91, 0x94, + 0xd3, 0xb0, 0xc3, 0x94, 0x6f, 0x12, 0xc9, 0x18, 0xd2, 0xf8, 0xf7, 0x81, 0x26, 0xbf, 0x0f, 0xb4, + 0x29, 0x7b, 0x59, 0xdf, 0x6e, 0x26, 0xc2, 0xe0, 0x7d, 0x30, 0x20, 0x12, 0x19, 0xb2, 0xea, 0xe7, + 0x0b, 0x33, 0x95, 0x28, 0x1b, 0xbd, 0x79, 0xd9, 0xe8, 0x7b, 0x91, 0x6c, 0xd4, 0x60, 0x94, 0x91, + 0xbb, 0x68, 0x98, 0x73, 0xd4, 0x9f, 0x76, 0x16, 0x16, 0x2c, 0x7f, 0x81, 0xda, 0x7e, 0xa7, 0x79, + 0x50, 0xa0, 0xdf, 0x0b, 0x42, 0xd8, 0x26, 0x15, 0x09, 0x08, 0x9f, 0xd5, 0xaf, 0x11, 0xec, 0xcf, + 0xd8, 0x54, 0x88, 0xc9, 0x5a, 0x96, 0x5c, 0x65, 0x1b, 0x6f, 0xd3, 0x63, 0x2b, 0xdd, 0x3c, 0x9e, + 0xdf, 0x64, 0x81, 0xf3, 0x3a, 0x95, 0x24, 0xd9, 0x67, 0x7b, 0x5f, 0xb8, 0xcf, 0xfe, 0x25, 0x5b, + 0x7e, 0x0a, 0xc2, 0xb0, 0xcd, 0x6e, 0x8d, 0xd4, 0x92, 0x9d, 0x76, 0x2c, 0xb5, 0xd3, 0xf2, 0x20, + 0xfc, 0x2c, 0xc7, 0x9d, 0x36, 0x42, 0x9b, 0x75, 0x60, 0x6f, 0x8c, 0xa8, 0x4e, 0x4d, 0x6a, 0xb9, + 0x5d, 0x3d, 0x99, 0xf7, 0x10, 0x28, 0x69, 0x3b, 0x0a, 0x59, 0x15, 0xe8, 0xaf, 0x05, 0x4b, 0x8b, + 0x94, 0xc7, 0xed, 0xd7, 0xc3, 0xe7, 0x6e, 0xd6, 0xe8, 0x6d, 0xd1, 0x30, 0x39, 0xa8, 0x29, 0x73, + 0xce, 0x76, 0x6e, 0xcf, 0xd3, 0x4a, 0x95, 0x76, 0xbb, 0x50, 0xef, 0xcb, 0xd6, 0x97, 0xb1, 0xb3, + 0x90, 0x65, 0x1c, 0x76, 0x18, 0xc9, 0x57, 0xa2, 0x64, 0x9b, 0x97, 0xbb, 0x59, 0xb7, 0xdf, 0xe6, + 0x62, 0xdd, 0x30, 0xc5, 0xfb, 0x0f, 0x82, 0x97, 0x73, 0x61, 0x0a, 0x4d, 0xcf, 0xc1, 0xce, 0x26, + 0xf1, 0xd6, 0x5e, 0xc6, 0x2d, 0x9e, 0x1b, 0xa1, 0x96, 0xbf, 0x94, 0x7d, 0xf5, 0x8a, 0x2d, 0x6b, + 0x86, 0x63, 0xee, 0x38, 0x35, 0x6f, 0xc2, 0x3e, 0x97, 0x45, 0x2a, 0x45, 0xed, 0xab, 0x24, 0xcf, + 0xb0, 0x37, 0xd2, 0x3b, 0xd6, 0x3b, 0xde, 0xa7, 0xef, 0x75, 0x9b, 0x9a, 0xe5, 0x25, 0x69, 0xa0, + 0x2e, 0x89, 0x76, 0x9a, 0x02, 0x4c, 0x24, 0x63, 0x14, 0x06, 0xa2, 0x78, 0x88, 0xc5, 0x8b, 0x16, + 0x62, 0x9a, 0x14, 0xda, 0xd4, 0xe4, 0x8e, 0x6c, 0x37, 0xd1, 0xd6, 0x53, 0xe6, 0x5c, 0xc7, 0x82, + 0x1c, 0x83, 0x21, 0x21, 0x88, 0x61, 0xce, 0xb5, 0x28, 0x81, 0x5d, 0x79, 0xf2, 0x22, 0x09, 0xea, + 0xb0, 0x2f, 0x15, 0x47, 0x97, 0xf9, 0x5f, 0x13, 0x77, 0xdd, 0x0b, 0x74, 0x29, 0xcc, 0x87, 0xce, + 0x01, 0x74, 0x7a, 0x8f, 0xfe, 0x01, 0xc1, 0x58, 0x76, 0x6c, 0xc1, 0x6b, 0x12, 0x86, 0x6d, 0xba, + 0x14, 0x1d, 0x96, 0x92, 0x60, 0xcf, 0xb6, 0xea, 0xd3, 0x07, 0xed, 0x56, 0xdf, 0x2e, 0xb6, 0xb0, + 0xc9, 0x9f, 0x76, 0xc3, 0x26, 0x86, 0x19, 0x7f, 0x87, 0x60, 0x8b, 0xb8, 0x6e, 0xe2, 0xf1, 0xd4, + 0x7a, 0x4f, 0xf9, 0xc1, 0x40, 0x39, 0xbc, 0x06, 0x4b, 0xce, 0x5c, 0x3d, 0xfb, 0xc9, 0xcf, 0x7f, + 0xde, 0x2b, 0x4c, 0xe1, 0xb7, 0x48, 0xca, 0xaf, 0x1d, 0xfc, 0x87, 0x11, 0x39, 0x6f, 0x91, 0x95, + 0x48, 0xe7, 0x06, 0x09, 0xd4, 0xf7, 0xc8, 0x8a, 0xc8, 0x49, 0x03, 0xdf, 0x45, 0xd0, 0x2f, 0xa7, + 0x3c, 0xbc, 0x3a, 0x00, 0x79, 0xb6, 0x95, 0x23, 0x6b, 0x31, 0x15, 0x60, 0x8f, 0x30, 0xb0, 0xaf, + 0x60, 0x75, 0x75, 0xb0, 0xf8, 0x47, 0x04, 0xb8, 0x75, 0xfe, 0xc4, 0xc7, 0x73, 0xb6, 0xcb, 0x1a, + 0x9c, 0x95, 0x13, 0xed, 0x39, 0x09, 0xb4, 0xd3, 0x0c, 0xed, 0x69, 0xfc, 0x46, 0x0e, 0xda, 0xd0, + 0x3b, 0x50, 0x37, 0x7c, 0x68, 0x44, 0x34, 0x7e, 0x0d, 0x68, 0xb4, 0x4c, 0x80, 0xb9, 0x34, 0xb2, + 0x46, 0xd1, 0x5c, 0x1a, 0x99, 0x43, 0xa6, 0x7a, 0x99, 0xd1, 0xb8, 0x80, 0xcf, 0x75, 0x78, 0x42, + 0x48, 0x7c, 0x3e, 0xc5, 0x5f, 0x15, 0x60, 0x38, 0x75, 0x8e, 0xc2, 0x27, 0x57, 0x47, 0x99, 0x36, + 0x28, 0x2a, 0xaf, 0xb7, 0xed, 0x27, 0x08, 0xde, 0x45, 0x8c, 0xe1, 0x1d, 0x84, 0x3f, 0x45, 0x1d, + 0x73, 0x4c, 0x4e, 0x7e, 0x44, 0x8e, 0x90, 0x64, 0xa5, 0x69, 0x18, 0x6d, 0x10, 0xde, 0x20, 0x62, + 0x2f, 0xf8, 0x42, 0x03, 0x3f, 0x41, 0xb0, 0xb3, 0xf9, 0x46, 0x8f, 0x27, 0xb2, 0xd9, 0x65, 0x4c, + 0x6c, 0xca, 0x64, 0x3b, 0x2e, 0x42, 0x0b, 0xca, 0xa4, 0x28, 0xe1, 0x8f, 0x3a, 0x15, 0xa2, 0xe5, + 0x8b, 0xd8, 0x23, 0x2b, 0xb2, 0xbb, 0x36, 0xf0, 0x23, 0x04, 0xbb, 0x5a, 0x86, 0x16, 0xdc, 0x06, + 0xe0, 0xb0, 0x34, 0x8f, 0xb7, 0xe5, 0x23, 0x58, 0x5e, 0x67, 0x2c, 0x2f, 0x63, 0x7d, 0xfd, 0x59, + 0xe2, 0x5f, 0x10, 0xbc, 0x94, 0x18, 0x1a, 0xb0, 0xb6, 0x1a, 0xc4, 0xe4, 0x3c, 0xa3, 0x90, 0x35, + 0xdb, 0x0b, 0x3a, 0x65, 0x46, 0xe7, 0x43, 0x7c, 0x7d, 0x9d, 0xe8, 0xd4, 0x78, 0xfc, 0x44, 0xc6, + 0x9e, 0x23, 0x18, 0x4e, 0xbd, 0xa9, 0xe6, 0xd5, 0x6b, 0xde, 0x9c, 0x92, 0x57, 0xaf, 0xb9, 0x53, + 0x86, 0x7a, 0x83, 0xd1, 0xbd, 0x8a, 0xdf, 0x5f, 0x27, 0xba, 0x86, 0x39, 0x97, 0xa0, 0xfa, 0x37, + 0x82, 0xdd, 0xe9, 0x97, 0x72, 0xdc, 0x2e, 0xe6, 0xf0, 0x98, 0x9e, 0x6a, 0xdf, 0x51, 0xb0, 0x2d, + 0x31, 0xb6, 0xd7, 0xf0, 0x07, 0xeb, 0xc7, 0x36, 0xc9, 0xe9, 0xf3, 0x02, 0xec, 0x6a, 0xb9, 0xf1, + 0xe6, 0xd5, 0x62, 0xd6, 0xbd, 0x3d, 0xaf, 0x16, 0x33, 0xaf, 0xd4, 0xeb, 0xda, 0x7d, 0xd3, 0x9a, + 0x4e, 0xce, 0x44, 0xd0, 0x20, 0xf5, 0x10, 0x56, 0xc9, 0x15, 0xc4, 0xff, 0x45, 0xb0, 0x3d, 0x79, + 0xfb, 0xc5, 0x64, 0x2d, 0xbc, 0x62, 0xf7, 0x75, 0xe5, 0xd8, 0xda, 0x1d, 0x84, 0x0a, 0x1f, 0x73, + 0x15, 0x56, 0xf0, 0x72, 0x17, 0x35, 0x48, 0x0c, 0x01, 0x09, 0xf2, 0x41, 0x09, 0xe0, 0xdf, 0x10, + 0x0c, 0xa6, 0x5c, 0x92, 0x71, 0xce, 0xb5, 0x21, 0xfb, 0xbe, 0xae, 0xbc, 0xd6, 0xa6, 0x97, 0x10, + 0xe2, 0x0a, 0xd3, 0xe1, 0x5d, 0x7c, 0xbe, 0x53, 0x1d, 0x12, 0xf7, 0xf9, 0x33, 0xfa, 0x83, 0xa7, + 0x45, 0xf4, 0xf0, 0x69, 0x11, 0x3d, 0x79, 0x5a, 0x44, 0x5f, 0x3c, 0x2b, 0xf6, 0x3c, 0x7c, 0x56, + 0xec, 0x79, 0xf4, 0xac, 0xd8, 0x73, 0xfd, 0x54, 0xd5, 0xf2, 0x67, 0xeb, 0x65, 0xcd, 0x74, 0x16, + 0x88, 0xf8, 0xe7, 0x20, 0xff, 0x73, 0xd4, 0xab, 0xcc, 0x91, 0xa5, 0x08, 0xc6, 0xb1, 0x13, 0x47, + 0x25, 0x12, 0x7f, 0xd9, 0xa5, 0x5e, 0x79, 0x33, 0xfb, 0x1d, 0xf7, 0xf8, 0x7f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xce, 0x1d, 0x1b, 0xcd, 0xab, 0x1c, 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 { + // Channel queries an IBC Channel. + Channel(ctx context.Context, in *QueryChannelRequest, opts ...grpc.CallOption) (*QueryChannelResponse, error) + // Channels queries all the IBC channels of a chain. + Channels(ctx context.Context, in *QueryChannelsRequest, opts ...grpc.CallOption) (*QueryChannelsResponse, error) + // ConnectionChannels queries all the channels associated with a connection + // end. + ConnectionChannels(ctx context.Context, in *QueryConnectionChannelsRequest, opts ...grpc.CallOption) (*QueryConnectionChannelsResponse, error) + // ChannelClientState queries for the client state for the channel associated + // with the provided channel identifiers. + ChannelClientState(ctx context.Context, in *QueryChannelClientStateRequest, opts ...grpc.CallOption) (*QueryChannelClientStateResponse, error) + // ChannelConsensusState queries for the consensus state for the channel + // associated with the provided channel identifiers. + ChannelConsensusState(ctx context.Context, in *QueryChannelConsensusStateRequest, opts ...grpc.CallOption) (*QueryChannelConsensusStateResponse, error) + // PacketCommitment queries a stored packet commitment hash. + PacketCommitment(ctx context.Context, in *QueryPacketCommitmentRequest, opts ...grpc.CallOption) (*QueryPacketCommitmentResponse, error) + // PacketCommitments returns all the packet commitments hashes associated + // with a channel. + PacketCommitments(ctx context.Context, in *QueryPacketCommitmentsRequest, opts ...grpc.CallOption) (*QueryPacketCommitmentsResponse, error) + // PacketReceipt queries if a given packet sequence has been received on the queried chain + PacketReceipt(ctx context.Context, in *QueryPacketReceiptRequest, opts ...grpc.CallOption) (*QueryPacketReceiptResponse, error) + // PacketAcknowledgement queries a stored packet acknowledgement hash. + PacketAcknowledgement(ctx context.Context, in *QueryPacketAcknowledgementRequest, opts ...grpc.CallOption) (*QueryPacketAcknowledgementResponse, error) + // PacketAcknowledgements returns all the packet acknowledgements associated + // with a channel. + PacketAcknowledgements(ctx context.Context, in *QueryPacketAcknowledgementsRequest, opts ...grpc.CallOption) (*QueryPacketAcknowledgementsResponse, error) + // UnreceivedPackets returns all the unreceived IBC packets associated with a + // channel and sequences. + UnreceivedPackets(ctx context.Context, in *QueryUnreceivedPacketsRequest, opts ...grpc.CallOption) (*QueryUnreceivedPacketsResponse, error) + // UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a + // channel and sequences. + UnreceivedAcks(ctx context.Context, in *QueryUnreceivedAcksRequest, opts ...grpc.CallOption) (*QueryUnreceivedAcksResponse, error) + // NextSequenceReceive returns the next receive sequence for a given channel. + NextSequenceReceive(ctx context.Context, in *QueryNextSequenceReceiveRequest, opts ...grpc.CallOption) (*QueryNextSequenceReceiveResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Channel(ctx context.Context, in *QueryChannelRequest, opts ...grpc.CallOption) (*QueryChannelResponse, error) { + out := new(QueryChannelResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/Channel", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Channels(ctx context.Context, in *QueryChannelsRequest, opts ...grpc.CallOption) (*QueryChannelsResponse, error) { + out := new(QueryChannelsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/Channels", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ConnectionChannels(ctx context.Context, in *QueryConnectionChannelsRequest, opts ...grpc.CallOption) (*QueryConnectionChannelsResponse, error) { + out := new(QueryConnectionChannelsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/ConnectionChannels", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ChannelClientState(ctx context.Context, in *QueryChannelClientStateRequest, opts ...grpc.CallOption) (*QueryChannelClientStateResponse, error) { + out := new(QueryChannelClientStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/ChannelClientState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ChannelConsensusState(ctx context.Context, in *QueryChannelConsensusStateRequest, opts ...grpc.CallOption) (*QueryChannelConsensusStateResponse, error) { + out := new(QueryChannelConsensusStateResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/ChannelConsensusState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PacketCommitment(ctx context.Context, in *QueryPacketCommitmentRequest, opts ...grpc.CallOption) (*QueryPacketCommitmentResponse, error) { + out := new(QueryPacketCommitmentResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/PacketCommitment", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PacketCommitments(ctx context.Context, in *QueryPacketCommitmentsRequest, opts ...grpc.CallOption) (*QueryPacketCommitmentsResponse, error) { + out := new(QueryPacketCommitmentsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/PacketCommitments", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PacketReceipt(ctx context.Context, in *QueryPacketReceiptRequest, opts ...grpc.CallOption) (*QueryPacketReceiptResponse, error) { + out := new(QueryPacketReceiptResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/PacketReceipt", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PacketAcknowledgement(ctx context.Context, in *QueryPacketAcknowledgementRequest, opts ...grpc.CallOption) (*QueryPacketAcknowledgementResponse, error) { + out := new(QueryPacketAcknowledgementResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/PacketAcknowledgement", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) PacketAcknowledgements(ctx context.Context, in *QueryPacketAcknowledgementsRequest, opts ...grpc.CallOption) (*QueryPacketAcknowledgementsResponse, error) { + out := new(QueryPacketAcknowledgementsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/PacketAcknowledgements", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) UnreceivedPackets(ctx context.Context, in *QueryUnreceivedPacketsRequest, opts ...grpc.CallOption) (*QueryUnreceivedPacketsResponse, error) { + out := new(QueryUnreceivedPacketsResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/UnreceivedPackets", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) UnreceivedAcks(ctx context.Context, in *QueryUnreceivedAcksRequest, opts ...grpc.CallOption) (*QueryUnreceivedAcksResponse, error) { + out := new(QueryUnreceivedAcksResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/UnreceivedAcks", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) NextSequenceReceive(ctx context.Context, in *QueryNextSequenceReceiveRequest, opts ...grpc.CallOption) (*QueryNextSequenceReceiveResponse, error) { + out := new(QueryNextSequenceReceiveResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Query/NextSequenceReceive", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Channel queries an IBC Channel. + Channel(context.Context, *QueryChannelRequest) (*QueryChannelResponse, error) + // Channels queries all the IBC channels of a chain. + Channels(context.Context, *QueryChannelsRequest) (*QueryChannelsResponse, error) + // ConnectionChannels queries all the channels associated with a connection + // end. + ConnectionChannels(context.Context, *QueryConnectionChannelsRequest) (*QueryConnectionChannelsResponse, error) + // ChannelClientState queries for the client state for the channel associated + // with the provided channel identifiers. + ChannelClientState(context.Context, *QueryChannelClientStateRequest) (*QueryChannelClientStateResponse, error) + // ChannelConsensusState queries for the consensus state for the channel + // associated with the provided channel identifiers. + ChannelConsensusState(context.Context, *QueryChannelConsensusStateRequest) (*QueryChannelConsensusStateResponse, error) + // PacketCommitment queries a stored packet commitment hash. + PacketCommitment(context.Context, *QueryPacketCommitmentRequest) (*QueryPacketCommitmentResponse, error) + // PacketCommitments returns all the packet commitments hashes associated + // with a channel. + PacketCommitments(context.Context, *QueryPacketCommitmentsRequest) (*QueryPacketCommitmentsResponse, error) + // PacketReceipt queries if a given packet sequence has been received on the queried chain + PacketReceipt(context.Context, *QueryPacketReceiptRequest) (*QueryPacketReceiptResponse, error) + // PacketAcknowledgement queries a stored packet acknowledgement hash. + PacketAcknowledgement(context.Context, *QueryPacketAcknowledgementRequest) (*QueryPacketAcknowledgementResponse, error) + // PacketAcknowledgements returns all the packet acknowledgements associated + // with a channel. + PacketAcknowledgements(context.Context, *QueryPacketAcknowledgementsRequest) (*QueryPacketAcknowledgementsResponse, error) + // UnreceivedPackets returns all the unreceived IBC packets associated with a + // channel and sequences. + UnreceivedPackets(context.Context, *QueryUnreceivedPacketsRequest) (*QueryUnreceivedPacketsResponse, error) + // UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a + // channel and sequences. + UnreceivedAcks(context.Context, *QueryUnreceivedAcksRequest) (*QueryUnreceivedAcksResponse, error) + // NextSequenceReceive returns the next receive sequence for a given channel. + NextSequenceReceive(context.Context, *QueryNextSequenceReceiveRequest) (*QueryNextSequenceReceiveResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Channel(ctx context.Context, req *QueryChannelRequest) (*QueryChannelResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Channel not implemented") +} +func (*UnimplementedQueryServer) Channels(ctx context.Context, req *QueryChannelsRequest) (*QueryChannelsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Channels not implemented") +} +func (*UnimplementedQueryServer) ConnectionChannels(ctx context.Context, req *QueryConnectionChannelsRequest) (*QueryConnectionChannelsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConnectionChannels not implemented") +} +func (*UnimplementedQueryServer) ChannelClientState(ctx context.Context, req *QueryChannelClientStateRequest) (*QueryChannelClientStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelClientState not implemented") +} +func (*UnimplementedQueryServer) ChannelConsensusState(ctx context.Context, req *QueryChannelConsensusStateRequest) (*QueryChannelConsensusStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelConsensusState not implemented") +} +func (*UnimplementedQueryServer) PacketCommitment(ctx context.Context, req *QueryPacketCommitmentRequest) (*QueryPacketCommitmentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PacketCommitment not implemented") +} +func (*UnimplementedQueryServer) PacketCommitments(ctx context.Context, req *QueryPacketCommitmentsRequest) (*QueryPacketCommitmentsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PacketCommitments not implemented") +} +func (*UnimplementedQueryServer) PacketReceipt(ctx context.Context, req *QueryPacketReceiptRequest) (*QueryPacketReceiptResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PacketReceipt not implemented") +} +func (*UnimplementedQueryServer) PacketAcknowledgement(ctx context.Context, req *QueryPacketAcknowledgementRequest) (*QueryPacketAcknowledgementResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PacketAcknowledgement not implemented") +} +func (*UnimplementedQueryServer) PacketAcknowledgements(ctx context.Context, req *QueryPacketAcknowledgementsRequest) (*QueryPacketAcknowledgementsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PacketAcknowledgements not implemented") +} +func (*UnimplementedQueryServer) UnreceivedPackets(ctx context.Context, req *QueryUnreceivedPacketsRequest) (*QueryUnreceivedPacketsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnreceivedPackets not implemented") +} +func (*UnimplementedQueryServer) UnreceivedAcks(ctx context.Context, req *QueryUnreceivedAcksRequest) (*QueryUnreceivedAcksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnreceivedAcks not implemented") +} +func (*UnimplementedQueryServer) NextSequenceReceive(ctx context.Context, req *QueryNextSequenceReceiveRequest) (*QueryNextSequenceReceiveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NextSequenceReceive not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Channel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryChannelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Channel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/Channel", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Channel(ctx, req.(*QueryChannelRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Channels_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryChannelsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Channels(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/Channels", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Channels(ctx, req.(*QueryChannelsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ConnectionChannels_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryConnectionChannelsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ConnectionChannels(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/ConnectionChannels", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ConnectionChannels(ctx, req.(*QueryConnectionChannelsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ChannelClientState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryChannelClientStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ChannelClientState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/ChannelClientState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ChannelClientState(ctx, req.(*QueryChannelClientStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ChannelConsensusState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryChannelConsensusStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ChannelConsensusState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/ChannelConsensusState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ChannelConsensusState(ctx, req.(*QueryChannelConsensusStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PacketCommitment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPacketCommitmentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PacketCommitment(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/PacketCommitment", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PacketCommitment(ctx, req.(*QueryPacketCommitmentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PacketCommitments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPacketCommitmentsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PacketCommitments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/PacketCommitments", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PacketCommitments(ctx, req.(*QueryPacketCommitmentsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PacketReceipt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPacketReceiptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PacketReceipt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/PacketReceipt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PacketReceipt(ctx, req.(*QueryPacketReceiptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PacketAcknowledgement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPacketAcknowledgementRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PacketAcknowledgement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/PacketAcknowledgement", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PacketAcknowledgement(ctx, req.(*QueryPacketAcknowledgementRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_PacketAcknowledgements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPacketAcknowledgementsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).PacketAcknowledgements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/PacketAcknowledgements", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).PacketAcknowledgements(ctx, req.(*QueryPacketAcknowledgementsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_UnreceivedPackets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUnreceivedPacketsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UnreceivedPackets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/UnreceivedPackets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UnreceivedPackets(ctx, req.(*QueryUnreceivedPacketsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_UnreceivedAcks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUnreceivedAcksRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UnreceivedAcks(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/UnreceivedAcks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UnreceivedAcks(ctx, req.(*QueryUnreceivedAcksRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_NextSequenceReceive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNextSequenceReceiveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).NextSequenceReceive(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Query/NextSequenceReceive", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).NextSequenceReceive(ctx, req.(*QueryNextSequenceReceiveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.channel.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Channel", + Handler: _Query_Channel_Handler, + }, + { + MethodName: "Channels", + Handler: _Query_Channels_Handler, + }, + { + MethodName: "ConnectionChannels", + Handler: _Query_ConnectionChannels_Handler, + }, + { + MethodName: "ChannelClientState", + Handler: _Query_ChannelClientState_Handler, + }, + { + MethodName: "ChannelConsensusState", + Handler: _Query_ChannelConsensusState_Handler, + }, + { + MethodName: "PacketCommitment", + Handler: _Query_PacketCommitment_Handler, + }, + { + MethodName: "PacketCommitments", + Handler: _Query_PacketCommitments_Handler, + }, + { + MethodName: "PacketReceipt", + Handler: _Query_PacketReceipt_Handler, + }, + { + MethodName: "PacketAcknowledgement", + Handler: _Query_PacketAcknowledgement_Handler, + }, + { + MethodName: "PacketAcknowledgements", + Handler: _Query_PacketAcknowledgements_Handler, + }, + { + MethodName: "UnreceivedPackets", + Handler: _Query_UnreceivedPackets_Handler, + }, + { + MethodName: "UnreceivedAcks", + Handler: _Query_UnreceivedAcks_Handler, + }, + { + MethodName: "NextSequenceReceive", + Handler: _Query_NextSequenceReceive_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/channel/v1/query.proto", +} + +func (m *QueryChannelRequest) 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 *QueryChannelRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryChannelResponse) 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 *QueryChannelResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.Channel != nil { + { + size, err := m.Channel.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 *QueryChannelsRequest) 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 *QueryChannelsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryChannelsResponse) 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 *QueryChannelsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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.Channels) > 0 { + for iNdEx := len(m.Channels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Channels[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 *QueryConnectionChannelsRequest) 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 *QueryConnectionChannelsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionChannelsRequest) 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.Connection) > 0 { + i -= len(m.Connection) + copy(dAtA[i:], m.Connection) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Connection))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryConnectionChannelsResponse) 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 *QueryConnectionChannelsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryConnectionChannelsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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.Channels) > 0 { + for iNdEx := len(m.Channels) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Channels[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 *QueryChannelClientStateRequest) 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 *QueryChannelClientStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelClientStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryChannelClientStateResponse) 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 *QueryChannelClientStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelClientStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.IdentifiedClientState != nil { + { + size, err := m.IdentifiedClientState.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 *QueryChannelConsensusStateRequest) 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 *QueryChannelConsensusStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelConsensusStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RevisionHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionHeight)) + i-- + dAtA[i] = 0x20 + } + if m.RevisionNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.RevisionNumber)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryChannelConsensusStateResponse) 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 *QueryChannelConsensusStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryChannelConsensusStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x1a + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0x12 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.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 *QueryPacketCommitmentRequest) 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 *QueryPacketCommitmentRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketCommitmentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketCommitmentResponse) 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 *QueryPacketCommitmentResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketCommitmentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if len(m.Commitment) > 0 { + i -= len(m.Commitment) + copy(dAtA[i:], m.Commitment) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Commitment))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketCommitmentsRequest) 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 *QueryPacketCommitmentsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketCommitmentsRequest) 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.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketCommitmentsResponse) 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 *QueryPacketCommitmentsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketCommitmentsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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.Commitments) > 0 { + for iNdEx := len(m.Commitments) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Commitments[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 *QueryPacketReceiptRequest) 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 *QueryPacketReceiptRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketReceiptRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketReceiptResponse) 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 *QueryPacketReceiptResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketReceiptResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x1a + } + if m.Received { + i-- + if m.Received { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketAcknowledgementRequest) 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 *QueryPacketAcknowledgementRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketAcknowledgementRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x18 + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketAcknowledgementResponse) 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 *QueryPacketAcknowledgementResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketAcknowledgementResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if len(m.Acknowledgement) > 0 { + i -= len(m.Acknowledgement) + copy(dAtA[i:], m.Acknowledgement) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Acknowledgement))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketAcknowledgementsRequest) 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 *QueryPacketAcknowledgementsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketAcknowledgementsRequest) 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.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryPacketAcknowledgementsResponse) 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 *QueryPacketAcknowledgementsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPacketAcknowledgementsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + 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.Acknowledgements) > 0 { + for iNdEx := len(m.Acknowledgements) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Acknowledgements[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 *QueryUnreceivedPacketsRequest) 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 *QueryUnreceivedPacketsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnreceivedPacketsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PacketCommitmentSequences) > 0 { + dAtA23 := make([]byte, len(m.PacketCommitmentSequences)*10) + var j22 int + for _, num := range m.PacketCommitmentSequences { + for num >= 1<<7 { + dAtA23[j22] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j22++ + } + dAtA23[j22] = uint8(num) + j22++ + } + i -= j22 + copy(dAtA[i:], dAtA23[:j22]) + i = encodeVarintQuery(dAtA, i, uint64(j22)) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryUnreceivedPacketsResponse) 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 *QueryUnreceivedPacketsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnreceivedPacketsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Sequences) > 0 { + dAtA26 := make([]byte, len(m.Sequences)*10) + var j25 int + for _, num := range m.Sequences { + for num >= 1<<7 { + dAtA26[j25] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j25++ + } + dAtA26[j25] = uint8(num) + j25++ + } + i -= j25 + copy(dAtA[i:], dAtA26[:j25]) + i = encodeVarintQuery(dAtA, i, uint64(j25)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryUnreceivedAcksRequest) 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 *QueryUnreceivedAcksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnreceivedAcksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PacketAckSequences) > 0 { + dAtA28 := make([]byte, len(m.PacketAckSequences)*10) + var j27 int + for _, num := range m.PacketAckSequences { + for num >= 1<<7 { + dAtA28[j27] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j27++ + } + dAtA28[j27] = uint8(num) + j27++ + } + i -= j27 + copy(dAtA[i:], dAtA28[:j27]) + i = encodeVarintQuery(dAtA, i, uint64(j27)) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryUnreceivedAcksResponse) 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 *QueryUnreceivedAcksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnreceivedAcksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Sequences) > 0 { + dAtA31 := make([]byte, len(m.Sequences)*10) + var j30 int + for _, num := range m.Sequences { + for num >= 1<<7 { + dAtA31[j30] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j30++ + } + dAtA31[j30] = uint8(num) + j30++ + } + i -= j30 + copy(dAtA[i:], dAtA31[:j30]) + i = encodeVarintQuery(dAtA, i, uint64(j30)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryNextSequenceReceiveRequest) 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 *QueryNextSequenceReceiveRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNextSequenceReceiveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryNextSequenceReceiveResponse) 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 *QueryNextSequenceReceiveResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryNextSequenceReceiveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Proof) > 0 { + i -= len(m.Proof) + copy(dAtA[i:], m.Proof) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Proof))) + i-- + dAtA[i] = 0x12 + } + if m.NextSequenceReceive != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.NextSequenceReceive)) + i-- + dAtA[i] = 0x8 + } + 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 *QueryChannelRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryChannelResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Channel != nil { + l = m.Channel.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryChannelsRequest) 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 *QueryChannelsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Channels) > 0 { + for _, e := range m.Channels { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryConnectionChannelsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Connection) + 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 *QueryConnectionChannelsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Channels) > 0 { + for _, e := range m.Channels { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryChannelClientStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryChannelClientStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.IdentifiedClientState != nil { + l = m.IdentifiedClientState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryChannelConsensusStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.RevisionNumber != 0 { + n += 1 + sovQuery(uint64(m.RevisionNumber)) + } + if m.RevisionHeight != 0 { + n += 1 + sovQuery(uint64(m.RevisionHeight)) + } + return n +} + +func (m *QueryChannelConsensusStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPacketCommitmentRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovQuery(uint64(m.Sequence)) + } + return n +} + +func (m *QueryPacketCommitmentResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Commitment) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPacketCommitmentsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + 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 *QueryPacketCommitmentsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Commitments) > 0 { + for _, e := range m.Commitments { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPacketReceiptRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovQuery(uint64(m.Sequence)) + } + return n +} + +func (m *QueryPacketReceiptResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Received { + n += 2 + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPacketAcknowledgementRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovQuery(uint64(m.Sequence)) + } + return n +} + +func (m *QueryPacketAcknowledgementResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Acknowledgement) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryPacketAcknowledgementsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + 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 *QueryPacketAcknowledgementsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Acknowledgements) > 0 { + for _, e := range m.Acknowledgements { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryUnreceivedPacketsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.PacketCommitmentSequences) > 0 { + l = 0 + for _, e := range m.PacketCommitmentSequences { + l += sovQuery(uint64(e)) + } + n += 1 + sovQuery(uint64(l)) + l + } + return n +} + +func (m *QueryUnreceivedPacketsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Sequences) > 0 { + l = 0 + for _, e := range m.Sequences { + l += sovQuery(uint64(e)) + } + n += 1 + sovQuery(uint64(l)) + l + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryUnreceivedAcksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.PacketAckSequences) > 0 { + l = 0 + for _, e := range m.PacketAckSequences { + l += sovQuery(uint64(e)) + } + n += 1 + sovQuery(uint64(l)) + l + } + return n +} + +func (m *QueryUnreceivedAcksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Sequences) > 0 { + l = 0 + for _, e := range m.Sequences { + l += sovQuery(uint64(e)) + } + n += 1 + sovQuery(uint64(l)) + l + } + l = m.Height.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryNextSequenceReceiveRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryNextSequenceReceiveResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NextSequenceReceive != 0 { + n += 1 + sovQuery(uint64(m.NextSequenceReceive)) + } + l = len(m.Proof) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = m.ProofHeight.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 *QueryChannelRequest) 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: QueryChannelRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *QueryChannelResponse) 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: QueryChannelResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", 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.Channel == nil { + m.Channel = &Channel{} + } + if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryChannelsRequest) 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: QueryChannelsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryChannelsResponse) 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: QueryChannelsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channels", 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.Channels = append(m.Channels, &IdentifiedChannel{}) + if err := m.Channels[len(m.Channels)-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 + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryConnectionChannelsRequest) 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: QueryConnectionChannelsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionChannelsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connection", 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.Connection = 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 *QueryConnectionChannelsResponse) 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: QueryConnectionChannelsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryConnectionChannelsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channels", 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.Channels = append(m.Channels, &IdentifiedChannel{}) + if err := m.Channels[len(m.Channels)-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 + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryChannelClientStateRequest) 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: QueryChannelClientStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelClientStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *QueryChannelClientStateResponse) 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: QueryChannelClientStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelClientStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IdentifiedClientState", 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.IdentifiedClientState == nil { + m.IdentifiedClientState = &types.IdentifiedClientState{} + } + if err := m.IdentifiedClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryChannelConsensusStateRequest) 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: QueryChannelConsensusStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelConsensusStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionNumber", wireType) + } + m.RevisionNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHeight", wireType) + } + m.RevisionHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RevisionHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryChannelConsensusStateResponse) 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: QueryChannelConsensusStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryChannelConsensusStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", 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.ConsensusState == nil { + m.ConsensusState = &types1.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", 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.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryPacketCommitmentRequest) 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: QueryPacketCommitmentRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketCommitmentRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryPacketCommitmentResponse) 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: QueryPacketCommitmentResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketCommitmentResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) + if m.Commitment == nil { + m.Commitment = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryPacketCommitmentsRequest) 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: QueryPacketCommitmentsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketCommitmentsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *QueryPacketCommitmentsResponse) 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: QueryPacketCommitmentsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketCommitmentsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commitments", 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.Commitments = append(m.Commitments, &PacketState{}) + if err := m.Commitments[len(m.Commitments)-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 + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryPacketReceiptRequest) 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: QueryPacketReceiptRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketReceiptRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryPacketReceiptResponse) 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: QueryPacketReceiptResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketReceiptResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Received", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Received = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryPacketAcknowledgementRequest) 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: QueryPacketAcknowledgementRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketAcknowledgementRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryPacketAcknowledgementResponse) 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: QueryPacketAcknowledgementResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketAcknowledgementResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) + if m.Acknowledgement == nil { + m.Acknowledgement = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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 *QueryPacketAcknowledgementsRequest) 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: QueryPacketAcknowledgementsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketAcknowledgementsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *QueryPacketAcknowledgementsResponse) 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: QueryPacketAcknowledgementsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPacketAcknowledgementsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgements", 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.Acknowledgements = append(m.Acknowledgements, &PacketState{}) + if err := m.Acknowledgements[len(m.Acknowledgements)-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 + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryUnreceivedPacketsRequest) 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: QueryUnreceivedPacketsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnreceivedPacketsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PacketCommitmentSequences = append(m.PacketCommitmentSequences, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.PacketCommitmentSequences) == 0 { + m.PacketCommitmentSequences = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PacketCommitmentSequences = append(m.PacketCommitmentSequences, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field PacketCommitmentSequences", wireType) + } + 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 *QueryUnreceivedPacketsResponse) 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: QueryUnreceivedPacketsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnreceivedPacketsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sequences = append(m.Sequences, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Sequences) == 0 { + m.Sequences = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sequences = append(m.Sequences, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Sequences", wireType) + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryUnreceivedAcksRequest) 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: QueryUnreceivedAcksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnreceivedAcksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PacketAckSequences = append(m.PacketAckSequences, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.PacketAckSequences) == 0 { + m.PacketAckSequences = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PacketAckSequences = append(m.PacketAckSequences, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field PacketAckSequences", wireType) + } + 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 *QueryUnreceivedAcksResponse) 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: QueryUnreceivedAcksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnreceivedAcksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sequences = append(m.Sequences, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Sequences) == 0 { + m.Sequences = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sequences = append(m.Sequences, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Sequences", wireType) + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", 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 err := m.Height.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 *QueryNextSequenceReceiveRequest) 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: QueryNextSequenceReceiveRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNextSequenceReceiveRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = 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 *QueryNextSequenceReceiveResponse) 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: QueryNextSequenceReceiveResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryNextSequenceReceiveResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextSequenceReceive", wireType) + } + m.NextSequenceReceive = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextSequenceReceive |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof[:0], dAtA[iNdEx:postIndex]...) + if m.Proof == nil { + m.Proof = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 err := m.ProofHeight.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/ibc/core/04-channel/types/query.pb.gw.go b/x/ibc/core/04-channel/types/query.pb.gw.go new file mode 100644 index 000000000000..bb5a94aba749 --- /dev/null +++ b/x/ibc/core/04-channel/types/query.pb.gw.go @@ -0,0 +1,1792 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ibc/core/channel/v1/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_Channel_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := client.Channel(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Channel_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := server.Channel(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Channels_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Channels_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelsRequest + 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_Channels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Channels(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Channels_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelsRequest + 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_Channels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Channels(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ConnectionChannels_0 = &utilities.DoubleArray{Encoding: map[string]int{"connection": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ConnectionChannels_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionChannelsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection") + } + + protoReq.Connection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection", 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_ConnectionChannels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ConnectionChannels(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ConnectionChannels_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryConnectionChannelsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["connection"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "connection") + } + + protoReq.Connection, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "connection", 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_ConnectionChannels_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ConnectionChannels(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ChannelClientState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := client.ChannelClientState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ChannelClientState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelClientStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := server.ChannelClientState(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ChannelConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", err) + } + + msg, err := client.ChannelConsensusState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ChannelConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryChannelConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["revision_number"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_number") + } + + protoReq.RevisionNumber, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_number", err) + } + + val, ok = pathParams["revision_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "revision_height") + } + + protoReq.RevisionHeight, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision_height", err) + } + + msg, err := server.ChannelConsensusState(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_PacketCommitment_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketCommitmentRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := client.PacketCommitment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PacketCommitment_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketCommitmentRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := server.PacketCommitment(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_PacketCommitments_0 = &utilities.DoubleArray{Encoding: map[string]int{"channel_id": 0, "port_id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_PacketCommitments_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketCommitmentsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", 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_PacketCommitments_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.PacketCommitments(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PacketCommitments_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketCommitmentsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", 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_PacketCommitments_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.PacketCommitments(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_PacketReceipt_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketReceiptRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := client.PacketReceipt(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PacketReceipt_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketReceiptRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := server.PacketReceipt(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_PacketAcknowledgement_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketAcknowledgementRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := client.PacketAcknowledgement(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PacketAcknowledgement_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketAcknowledgementRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["sequence"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "sequence") + } + + protoReq.Sequence, err = runtime.Uint64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "sequence", err) + } + + msg, err := server.PacketAcknowledgement(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_PacketAcknowledgements_0 = &utilities.DoubleArray{Encoding: map[string]int{"channel_id": 0, "port_id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_Query_PacketAcknowledgements_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketAcknowledgementsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", 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_PacketAcknowledgements_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.PacketAcknowledgements(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_PacketAcknowledgements_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPacketAcknowledgementsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", 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_PacketAcknowledgements_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.PacketAcknowledgements(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_UnreceivedPackets_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnreceivedPacketsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["packet_commitment_sequences"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_commitment_sequences") + } + + protoReq.PacketCommitmentSequences, err = runtime.Uint64Slice(val, ",") + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_commitment_sequences", err) + } + + msg, err := client.UnreceivedPackets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_UnreceivedPackets_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnreceivedPacketsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["packet_commitment_sequences"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_commitment_sequences") + } + + protoReq.PacketCommitmentSequences, err = runtime.Uint64Slice(val, ",") + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_commitment_sequences", err) + } + + msg, err := server.UnreceivedPackets(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_UnreceivedAcks_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnreceivedAcksRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["packet_ack_sequences"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_ack_sequences") + } + + protoReq.PacketAckSequences, err = runtime.Uint64Slice(val, ",") + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_ack_sequences", err) + } + + msg, err := client.UnreceivedAcks(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_UnreceivedAcks_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnreceivedAcksRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + val, ok = pathParams["packet_ack_sequences"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "packet_ack_sequences") + } + + protoReq.PacketAckSequences, err = runtime.Uint64Slice(val, ",") + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "packet_ack_sequences", err) + } + + msg, err := server.UnreceivedAcks(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_NextSequenceReceive_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNextSequenceReceiveRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := client.NextSequenceReceive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_NextSequenceReceive_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryNextSequenceReceiveRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["channel_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "channel_id") + } + + protoReq.ChannelId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "channel_id", err) + } + + val, ok = pathParams["port_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "port_id") + } + + protoReq.PortId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "port_id", err) + } + + msg, err := server.NextSequenceReceive(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_Channel_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_Channel_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_Channel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Channels_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_Channels_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_Channels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionChannels_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_ConnectionChannels_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_ConnectionChannels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ChannelClientState_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_ChannelClientState_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_ChannelClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ChannelConsensusState_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_ChannelConsensusState_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_ChannelConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketCommitment_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_PacketCommitment_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_PacketCommitment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketCommitments_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_PacketCommitments_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_PacketCommitments_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketReceipt_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_PacketReceipt_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_PacketReceipt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketAcknowledgement_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_PacketAcknowledgement_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_PacketAcknowledgement_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketAcknowledgements_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_PacketAcknowledgements_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_PacketAcknowledgements_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnreceivedPackets_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_UnreceivedPackets_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_UnreceivedPackets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnreceivedAcks_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_UnreceivedAcks_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_UnreceivedAcks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NextSequenceReceive_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_NextSequenceReceive_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_NextSequenceReceive_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_Channel_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_Channel_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_Channel_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Channels_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_Channels_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_Channels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ConnectionChannels_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_ConnectionChannels_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_ConnectionChannels_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ChannelClientState_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_ChannelClientState_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_ChannelClientState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ChannelConsensusState_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_ChannelConsensusState_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_ChannelConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketCommitment_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_PacketCommitment_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_PacketCommitment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketCommitments_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_PacketCommitments_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_PacketCommitments_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketReceipt_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_PacketReceipt_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_PacketReceipt_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketAcknowledgement_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_PacketAcknowledgement_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_PacketAcknowledgement_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_PacketAcknowledgements_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_PacketAcknowledgements_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_PacketAcknowledgements_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnreceivedPackets_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_UnreceivedPackets_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_UnreceivedPackets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnreceivedAcks_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_UnreceivedAcks_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_UnreceivedAcks_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_NextSequenceReceive_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_NextSequenceReceive_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_NextSequenceReceive_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Channel_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Channels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"ibc", "core", "channel", "v1beta1", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ConnectionChannels_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"ibc", "core", "channel", "v1beta1", "connections", "connection", "channels"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ChannelClientState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "client_state"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ChannelConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 2, 9, 1, 0, 4, 1, 5, 10, 2, 11, 1, 0, 4, 1, 5, 12}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "consensus_state", "revision", "revision_number", "height", "revision_height"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_PacketCommitment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_PacketCommitments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_PacketReceipt_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_receipts", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_PacketAcknowledgement_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acks", "sequence"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_PacketAcknowledgements_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_acknowledgements"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_UnreceivedPackets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_commitment_sequences", "unreceived_packets"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_UnreceivedAcks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8, 1, 0, 4, 1, 5, 9, 2, 10}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "packet_commitments", "packet_ack_sequences", "unreceived_acks"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_NextSequenceReceive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 2, 8}, []string{"ibc", "core", "channel", "v1beta1", "channels", "channel_id", "ports", "port_id", "next_sequence"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Channel_0 = runtime.ForwardResponseMessage + + forward_Query_Channels_0 = runtime.ForwardResponseMessage + + forward_Query_ConnectionChannels_0 = runtime.ForwardResponseMessage + + forward_Query_ChannelClientState_0 = runtime.ForwardResponseMessage + + forward_Query_ChannelConsensusState_0 = runtime.ForwardResponseMessage + + forward_Query_PacketCommitment_0 = runtime.ForwardResponseMessage + + forward_Query_PacketCommitments_0 = runtime.ForwardResponseMessage + + forward_Query_PacketReceipt_0 = runtime.ForwardResponseMessage + + forward_Query_PacketAcknowledgement_0 = runtime.ForwardResponseMessage + + forward_Query_PacketAcknowledgements_0 = runtime.ForwardResponseMessage + + forward_Query_UnreceivedPackets_0 = runtime.ForwardResponseMessage + + forward_Query_UnreceivedAcks_0 = runtime.ForwardResponseMessage + + forward_Query_NextSequenceReceive_0 = runtime.ForwardResponseMessage +) diff --git a/x/ibc/core/04-channel/types/tx.pb.go b/x/ibc/core/04-channel/types/tx.pb.go new file mode 100644 index 000000000000..df6a9653f671 --- /dev/null +++ b/x/ibc/core/04-channel/types/tx.pb.go @@ -0,0 +1,5259 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/channel/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgChannelOpenInit defines an sdk.Msg to initialize a channel handshake. It +// is called by a relayer on Chain A. +type MsgChannelOpenInit struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + Channel Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel"` + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelOpenInit) Reset() { *m = MsgChannelOpenInit{} } +func (m *MsgChannelOpenInit) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenInit) ProtoMessage() {} +func (*MsgChannelOpenInit) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{0} +} +func (m *MsgChannelOpenInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenInit.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 *MsgChannelOpenInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenInit.Merge(m, src) +} +func (m *MsgChannelOpenInit) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenInit) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenInit.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenInit proto.InternalMessageInfo + +// MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type. +type MsgChannelOpenInitResponse struct { +} + +func (m *MsgChannelOpenInitResponse) Reset() { *m = MsgChannelOpenInitResponse{} } +func (m *MsgChannelOpenInitResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenInitResponse) ProtoMessage() {} +func (*MsgChannelOpenInitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{1} +} +func (m *MsgChannelOpenInitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenInitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenInitResponse.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 *MsgChannelOpenInitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenInitResponse.Merge(m, src) +} +func (m *MsgChannelOpenInitResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenInitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenInitResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenInitResponse proto.InternalMessageInfo + +// MsgChannelOpenInit defines a msg sent by a Relayer to try to open a channel +// on Chain B. +type MsgChannelOpenTry struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + // in the case of crossing hello's, when both chains call OpenInit, we need the channel identifier + // of the previous channel in state INIT + PreviousChannelId string `protobuf:"bytes,2,opt,name=previous_channel_id,json=previousChannelId,proto3" json:"previous_channel_id,omitempty" yaml:"previous_channel_id"` + Channel Channel `protobuf:"bytes,3,opt,name=channel,proto3" json:"channel"` + CounterpartyVersion string `protobuf:"bytes,4,opt,name=counterparty_version,json=counterpartyVersion,proto3" json:"counterparty_version,omitempty" yaml:"counterparty_version"` + ProofInit []byte `protobuf:"bytes,5,opt,name=proof_init,json=proofInit,proto3" json:"proof_init,omitempty" yaml:"proof_init"` + ProofHeight types.Height `protobuf:"bytes,6,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,7,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelOpenTry) Reset() { *m = MsgChannelOpenTry{} } +func (m *MsgChannelOpenTry) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenTry) ProtoMessage() {} +func (*MsgChannelOpenTry) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{2} +} +func (m *MsgChannelOpenTry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenTry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenTry.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 *MsgChannelOpenTry) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenTry.Merge(m, src) +} +func (m *MsgChannelOpenTry) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenTry) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenTry.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenTry proto.InternalMessageInfo + +// MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type. +type MsgChannelOpenTryResponse struct { +} + +func (m *MsgChannelOpenTryResponse) Reset() { *m = MsgChannelOpenTryResponse{} } +func (m *MsgChannelOpenTryResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenTryResponse) ProtoMessage() {} +func (*MsgChannelOpenTryResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{3} +} +func (m *MsgChannelOpenTryResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenTryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenTryResponse.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 *MsgChannelOpenTryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenTryResponse.Merge(m, src) +} +func (m *MsgChannelOpenTryResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenTryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenTryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenTryResponse proto.InternalMessageInfo + +// MsgChannelOpenAck defines a msg sent by a Relayer to Chain A to acknowledge +// the change of channel state to TRYOPEN on Chain B. +type MsgChannelOpenAck struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + CounterpartyChannelId string `protobuf:"bytes,3,opt,name=counterparty_channel_id,json=counterpartyChannelId,proto3" json:"counterparty_channel_id,omitempty" yaml:"counterparty_channel_id"` + CounterpartyVersion string `protobuf:"bytes,4,opt,name=counterparty_version,json=counterpartyVersion,proto3" json:"counterparty_version,omitempty" yaml:"counterparty_version"` + ProofTry []byte `protobuf:"bytes,5,opt,name=proof_try,json=proofTry,proto3" json:"proof_try,omitempty" yaml:"proof_try"` + ProofHeight types.Height `protobuf:"bytes,6,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,7,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelOpenAck) Reset() { *m = MsgChannelOpenAck{} } +func (m *MsgChannelOpenAck) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenAck) ProtoMessage() {} +func (*MsgChannelOpenAck) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{4} +} +func (m *MsgChannelOpenAck) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenAck.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 *MsgChannelOpenAck) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenAck.Merge(m, src) +} +func (m *MsgChannelOpenAck) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenAck) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenAck.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenAck proto.InternalMessageInfo + +// MsgChannelOpenAckResponse defines the Msg/ChannelOpenAck response type. +type MsgChannelOpenAckResponse struct { +} + +func (m *MsgChannelOpenAckResponse) Reset() { *m = MsgChannelOpenAckResponse{} } +func (m *MsgChannelOpenAckResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenAckResponse) ProtoMessage() {} +func (*MsgChannelOpenAckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{5} +} +func (m *MsgChannelOpenAckResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenAckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenAckResponse.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 *MsgChannelOpenAckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenAckResponse.Merge(m, src) +} +func (m *MsgChannelOpenAckResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenAckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenAckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenAckResponse proto.InternalMessageInfo + +// MsgChannelOpenConfirm defines a msg sent by a Relayer to Chain B to +// acknowledge the change of channel state to OPEN on Chain A. +type MsgChannelOpenConfirm struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + ProofAck []byte `protobuf:"bytes,3,opt,name=proof_ack,json=proofAck,proto3" json:"proof_ack,omitempty" yaml:"proof_ack"` + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,5,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelOpenConfirm) Reset() { *m = MsgChannelOpenConfirm{} } +func (m *MsgChannelOpenConfirm) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenConfirm) ProtoMessage() {} +func (*MsgChannelOpenConfirm) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{6} +} +func (m *MsgChannelOpenConfirm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenConfirm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenConfirm.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 *MsgChannelOpenConfirm) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenConfirm.Merge(m, src) +} +func (m *MsgChannelOpenConfirm) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenConfirm) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenConfirm.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenConfirm proto.InternalMessageInfo + +// MsgChannelOpenConfirmResponse defines the Msg/ChannelOpenConfirm response type. +type MsgChannelOpenConfirmResponse struct { +} + +func (m *MsgChannelOpenConfirmResponse) Reset() { *m = MsgChannelOpenConfirmResponse{} } +func (m *MsgChannelOpenConfirmResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelOpenConfirmResponse) ProtoMessage() {} +func (*MsgChannelOpenConfirmResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{7} +} +func (m *MsgChannelOpenConfirmResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelOpenConfirmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelOpenConfirmResponse.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 *MsgChannelOpenConfirmResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelOpenConfirmResponse.Merge(m, src) +} +func (m *MsgChannelOpenConfirmResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelOpenConfirmResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelOpenConfirmResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelOpenConfirmResponse proto.InternalMessageInfo + +// MsgChannelCloseInit defines a msg sent by a Relayer to Chain A +// to close a channel with Chain B. +type MsgChannelCloseInit struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + Signer string `protobuf:"bytes,3,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelCloseInit) Reset() { *m = MsgChannelCloseInit{} } +func (m *MsgChannelCloseInit) String() string { return proto.CompactTextString(m) } +func (*MsgChannelCloseInit) ProtoMessage() {} +func (*MsgChannelCloseInit) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{8} +} +func (m *MsgChannelCloseInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelCloseInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelCloseInit.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 *MsgChannelCloseInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelCloseInit.Merge(m, src) +} +func (m *MsgChannelCloseInit) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelCloseInit) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelCloseInit.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelCloseInit proto.InternalMessageInfo + +// MsgChannelCloseInitResponse defines the Msg/ChannelCloseInit response type. +type MsgChannelCloseInitResponse struct { +} + +func (m *MsgChannelCloseInitResponse) Reset() { *m = MsgChannelCloseInitResponse{} } +func (m *MsgChannelCloseInitResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelCloseInitResponse) ProtoMessage() {} +func (*MsgChannelCloseInitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{9} +} +func (m *MsgChannelCloseInitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelCloseInitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelCloseInitResponse.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 *MsgChannelCloseInitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelCloseInitResponse.Merge(m, src) +} +func (m *MsgChannelCloseInitResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelCloseInitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelCloseInitResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelCloseInitResponse proto.InternalMessageInfo + +// MsgChannelCloseConfirm defines a msg sent by a Relayer to Chain B +// to acknowledge the change of channel state to CLOSED on Chain A. +type MsgChannelCloseConfirm struct { + PortId string `protobuf:"bytes,1,opt,name=port_id,json=portId,proto3" json:"port_id,omitempty" yaml:"port_id"` + ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty" yaml:"channel_id"` + ProofInit []byte `protobuf:"bytes,3,opt,name=proof_init,json=proofInit,proto3" json:"proof_init,omitempty" yaml:"proof_init"` + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,5,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgChannelCloseConfirm) Reset() { *m = MsgChannelCloseConfirm{} } +func (m *MsgChannelCloseConfirm) String() string { return proto.CompactTextString(m) } +func (*MsgChannelCloseConfirm) ProtoMessage() {} +func (*MsgChannelCloseConfirm) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{10} +} +func (m *MsgChannelCloseConfirm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelCloseConfirm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelCloseConfirm.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 *MsgChannelCloseConfirm) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelCloseConfirm.Merge(m, src) +} +func (m *MsgChannelCloseConfirm) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelCloseConfirm) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelCloseConfirm.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelCloseConfirm proto.InternalMessageInfo + +// MsgChannelCloseConfirmResponse defines the Msg/ChannelCloseConfirm response type. +type MsgChannelCloseConfirmResponse struct { +} + +func (m *MsgChannelCloseConfirmResponse) Reset() { *m = MsgChannelCloseConfirmResponse{} } +func (m *MsgChannelCloseConfirmResponse) String() string { return proto.CompactTextString(m) } +func (*MsgChannelCloseConfirmResponse) ProtoMessage() {} +func (*MsgChannelCloseConfirmResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{11} +} +func (m *MsgChannelCloseConfirmResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgChannelCloseConfirmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgChannelCloseConfirmResponse.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 *MsgChannelCloseConfirmResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgChannelCloseConfirmResponse.Merge(m, src) +} +func (m *MsgChannelCloseConfirmResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgChannelCloseConfirmResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgChannelCloseConfirmResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgChannelCloseConfirmResponse proto.InternalMessageInfo + +// MsgRecvPacket receives incoming IBC packet +type MsgRecvPacket struct { + Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"` + ProofCommitment []byte `protobuf:"bytes,2,opt,name=proof_commitment,json=proofCommitment,proto3" json:"proof_commitment,omitempty" yaml:"proof_commitment"` + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,4,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgRecvPacket) Reset() { *m = MsgRecvPacket{} } +func (m *MsgRecvPacket) String() string { return proto.CompactTextString(m) } +func (*MsgRecvPacket) ProtoMessage() {} +func (*MsgRecvPacket) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{12} +} +func (m *MsgRecvPacket) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRecvPacket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRecvPacket.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 *MsgRecvPacket) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRecvPacket.Merge(m, src) +} +func (m *MsgRecvPacket) XXX_Size() int { + return m.Size() +} +func (m *MsgRecvPacket) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRecvPacket.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRecvPacket proto.InternalMessageInfo + +// MsgRecvPacketResponse defines the Msg/RecvPacket response type. +type MsgRecvPacketResponse struct { +} + +func (m *MsgRecvPacketResponse) Reset() { *m = MsgRecvPacketResponse{} } +func (m *MsgRecvPacketResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRecvPacketResponse) ProtoMessage() {} +func (*MsgRecvPacketResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{13} +} +func (m *MsgRecvPacketResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRecvPacketResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRecvPacketResponse.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 *MsgRecvPacketResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRecvPacketResponse.Merge(m, src) +} +func (m *MsgRecvPacketResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRecvPacketResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRecvPacketResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRecvPacketResponse proto.InternalMessageInfo + +// MsgTimeout receives timed-out packet +type MsgTimeout struct { + Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"` + ProofUnreceived []byte `protobuf:"bytes,2,opt,name=proof_unreceived,json=proofUnreceived,proto3" json:"proof_unreceived,omitempty" yaml:"proof_unreceived"` + ProofHeight types.Height `protobuf:"bytes,3,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + NextSequenceRecv uint64 `protobuf:"varint,4,opt,name=next_sequence_recv,json=nextSequenceRecv,proto3" json:"next_sequence_recv,omitempty" yaml:"next_sequence_recv"` + Signer string `protobuf:"bytes,5,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgTimeout) Reset() { *m = MsgTimeout{} } +func (m *MsgTimeout) String() string { return proto.CompactTextString(m) } +func (*MsgTimeout) ProtoMessage() {} +func (*MsgTimeout) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{14} +} +func (m *MsgTimeout) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTimeout) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTimeout.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 *MsgTimeout) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTimeout.Merge(m, src) +} +func (m *MsgTimeout) XXX_Size() int { + return m.Size() +} +func (m *MsgTimeout) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTimeout.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTimeout proto.InternalMessageInfo + +// MsgTimeoutResponse defines the Msg/Timeout response type. +type MsgTimeoutResponse struct { +} + +func (m *MsgTimeoutResponse) Reset() { *m = MsgTimeoutResponse{} } +func (m *MsgTimeoutResponse) String() string { return proto.CompactTextString(m) } +func (*MsgTimeoutResponse) ProtoMessage() {} +func (*MsgTimeoutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{15} +} +func (m *MsgTimeoutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTimeoutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTimeoutResponse.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 *MsgTimeoutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTimeoutResponse.Merge(m, src) +} +func (m *MsgTimeoutResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgTimeoutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTimeoutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTimeoutResponse proto.InternalMessageInfo + +// MsgTimeoutOnClose timed-out packet upon counterparty channel closure. +type MsgTimeoutOnClose struct { + Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"` + ProofUnreceived []byte `protobuf:"bytes,2,opt,name=proof_unreceived,json=proofUnreceived,proto3" json:"proof_unreceived,omitempty" yaml:"proof_unreceived"` + ProofClose []byte `protobuf:"bytes,3,opt,name=proof_close,json=proofClose,proto3" json:"proof_close,omitempty" yaml:"proof_close"` + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + NextSequenceRecv uint64 `protobuf:"varint,5,opt,name=next_sequence_recv,json=nextSequenceRecv,proto3" json:"next_sequence_recv,omitempty" yaml:"next_sequence_recv"` + Signer string `protobuf:"bytes,6,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgTimeoutOnClose) Reset() { *m = MsgTimeoutOnClose{} } +func (m *MsgTimeoutOnClose) String() string { return proto.CompactTextString(m) } +func (*MsgTimeoutOnClose) ProtoMessage() {} +func (*MsgTimeoutOnClose) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{16} +} +func (m *MsgTimeoutOnClose) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTimeoutOnClose) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTimeoutOnClose.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 *MsgTimeoutOnClose) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTimeoutOnClose.Merge(m, src) +} +func (m *MsgTimeoutOnClose) XXX_Size() int { + return m.Size() +} +func (m *MsgTimeoutOnClose) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTimeoutOnClose.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTimeoutOnClose proto.InternalMessageInfo + +// MsgTimeoutOnCloseResponse defines the Msg/TimeoutOnClose response type. +type MsgTimeoutOnCloseResponse struct { +} + +func (m *MsgTimeoutOnCloseResponse) Reset() { *m = MsgTimeoutOnCloseResponse{} } +func (m *MsgTimeoutOnCloseResponse) String() string { return proto.CompactTextString(m) } +func (*MsgTimeoutOnCloseResponse) ProtoMessage() {} +func (*MsgTimeoutOnCloseResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{17} +} +func (m *MsgTimeoutOnCloseResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgTimeoutOnCloseResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgTimeoutOnCloseResponse.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 *MsgTimeoutOnCloseResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgTimeoutOnCloseResponse.Merge(m, src) +} +func (m *MsgTimeoutOnCloseResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgTimeoutOnCloseResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgTimeoutOnCloseResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgTimeoutOnCloseResponse proto.InternalMessageInfo + +// MsgAcknowledgement receives incoming IBC acknowledgement +type MsgAcknowledgement struct { + Packet Packet `protobuf:"bytes,1,opt,name=packet,proto3" json:"packet"` + Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` + ProofAcked []byte `protobuf:"bytes,3,opt,name=proof_acked,json=proofAcked,proto3" json:"proof_acked,omitempty" yaml:"proof_acked"` + ProofHeight types.Height `protobuf:"bytes,4,opt,name=proof_height,json=proofHeight,proto3" json:"proof_height" yaml:"proof_height"` + Signer string `protobuf:"bytes,5,opt,name=signer,proto3" json:"signer,omitempty"` +} + +func (m *MsgAcknowledgement) Reset() { *m = MsgAcknowledgement{} } +func (m *MsgAcknowledgement) String() string { return proto.CompactTextString(m) } +func (*MsgAcknowledgement) ProtoMessage() {} +func (*MsgAcknowledgement) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{18} +} +func (m *MsgAcknowledgement) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAcknowledgement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAcknowledgement.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 *MsgAcknowledgement) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAcknowledgement.Merge(m, src) +} +func (m *MsgAcknowledgement) XXX_Size() int { + return m.Size() +} +func (m *MsgAcknowledgement) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAcknowledgement.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAcknowledgement proto.InternalMessageInfo + +// MsgAcknowledgementResponse defines the Msg/Acknowledgement response type. +type MsgAcknowledgementResponse struct { +} + +func (m *MsgAcknowledgementResponse) Reset() { *m = MsgAcknowledgementResponse{} } +func (m *MsgAcknowledgementResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAcknowledgementResponse) ProtoMessage() {} +func (*MsgAcknowledgementResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bc4637e0ac3fc7b7, []int{19} +} +func (m *MsgAcknowledgementResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAcknowledgementResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAcknowledgementResponse.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 *MsgAcknowledgementResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAcknowledgementResponse.Merge(m, src) +} +func (m *MsgAcknowledgementResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAcknowledgementResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAcknowledgementResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAcknowledgementResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgChannelOpenInit)(nil), "ibc.core.channel.v1.MsgChannelOpenInit") + proto.RegisterType((*MsgChannelOpenInitResponse)(nil), "ibc.core.channel.v1.MsgChannelOpenInitResponse") + proto.RegisterType((*MsgChannelOpenTry)(nil), "ibc.core.channel.v1.MsgChannelOpenTry") + proto.RegisterType((*MsgChannelOpenTryResponse)(nil), "ibc.core.channel.v1.MsgChannelOpenTryResponse") + proto.RegisterType((*MsgChannelOpenAck)(nil), "ibc.core.channel.v1.MsgChannelOpenAck") + proto.RegisterType((*MsgChannelOpenAckResponse)(nil), "ibc.core.channel.v1.MsgChannelOpenAckResponse") + proto.RegisterType((*MsgChannelOpenConfirm)(nil), "ibc.core.channel.v1.MsgChannelOpenConfirm") + proto.RegisterType((*MsgChannelOpenConfirmResponse)(nil), "ibc.core.channel.v1.MsgChannelOpenConfirmResponse") + proto.RegisterType((*MsgChannelCloseInit)(nil), "ibc.core.channel.v1.MsgChannelCloseInit") + proto.RegisterType((*MsgChannelCloseInitResponse)(nil), "ibc.core.channel.v1.MsgChannelCloseInitResponse") + proto.RegisterType((*MsgChannelCloseConfirm)(nil), "ibc.core.channel.v1.MsgChannelCloseConfirm") + proto.RegisterType((*MsgChannelCloseConfirmResponse)(nil), "ibc.core.channel.v1.MsgChannelCloseConfirmResponse") + proto.RegisterType((*MsgRecvPacket)(nil), "ibc.core.channel.v1.MsgRecvPacket") + proto.RegisterType((*MsgRecvPacketResponse)(nil), "ibc.core.channel.v1.MsgRecvPacketResponse") + proto.RegisterType((*MsgTimeout)(nil), "ibc.core.channel.v1.MsgTimeout") + proto.RegisterType((*MsgTimeoutResponse)(nil), "ibc.core.channel.v1.MsgTimeoutResponse") + proto.RegisterType((*MsgTimeoutOnClose)(nil), "ibc.core.channel.v1.MsgTimeoutOnClose") + proto.RegisterType((*MsgTimeoutOnCloseResponse)(nil), "ibc.core.channel.v1.MsgTimeoutOnCloseResponse") + proto.RegisterType((*MsgAcknowledgement)(nil), "ibc.core.channel.v1.MsgAcknowledgement") + proto.RegisterType((*MsgAcknowledgementResponse)(nil), "ibc.core.channel.v1.MsgAcknowledgementResponse") +} + +func init() { proto.RegisterFile("ibc/core/channel/v1/tx.proto", fileDescriptor_bc4637e0ac3fc7b7) } + +var fileDescriptor_bc4637e0ac3fc7b7 = []byte{ + // 1120 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0xd6, 0x8f, 0x2d, 0xdb, 0x63, 0x37, 0xb6, 0x29, 0xff, 0x28, 0x94, 0x2d, 0xba, 0x3c, 0x24, + 0x42, 0x8a, 0x48, 0xb1, 0x63, 0xa0, 0x6d, 0xd0, 0x8b, 0x64, 0xa0, 0x68, 0x50, 0xb8, 0x29, 0x18, + 0xb7, 0x07, 0xa3, 0x80, 0x20, 0xaf, 0x36, 0x14, 0x21, 0x89, 0xab, 0x92, 0x94, 0x62, 0xbd, 0x41, + 0x8f, 0x39, 0xf7, 0x94, 0x9e, 0x7b, 0x48, 0x1f, 0x23, 0xc7, 0x9c, 0xda, 0xa2, 0x07, 0xa2, 0xb0, + 0x2f, 0x3d, 0xf3, 0x09, 0x0a, 0xee, 0x2e, 0x29, 0x4a, 0x22, 0x2b, 0x2a, 0xa9, 0x9c, 0x9c, 0xb4, + 0x9c, 0xf9, 0x76, 0x76, 0xf6, 0xfb, 0x86, 0xb3, 0x4b, 0xc1, 0x9e, 0x76, 0x81, 0xca, 0x88, 0x18, + 0xb8, 0x8c, 0x9a, 0x75, 0x5d, 0xc7, 0xed, 0x72, 0xff, 0xb0, 0x6c, 0x5d, 0x96, 0xba, 0x06, 0xb1, + 0x88, 0x90, 0xd5, 0x2e, 0x50, 0xc9, 0xf5, 0x96, 0xb8, 0xb7, 0xd4, 0x3f, 0x14, 0xb7, 0x54, 0xa2, + 0x12, 0xea, 0x2f, 0xbb, 0x23, 0x06, 0x15, 0xa5, 0x61, 0xa0, 0xb6, 0x86, 0x75, 0xcb, 0x8d, 0xc3, + 0x46, 0x1c, 0xf0, 0x71, 0xd8, 0x4a, 0x5e, 0x58, 0x0a, 0x91, 0x7f, 0x49, 0x82, 0x70, 0x6a, 0xaa, + 0x27, 0xcc, 0xf8, 0xa4, 0x8b, 0xf5, 0xc7, 0xba, 0x66, 0x09, 0x9f, 0xc0, 0x52, 0x97, 0x18, 0x56, + 0x4d, 0x6b, 0xe4, 0x92, 0x07, 0xc9, 0xe2, 0x4a, 0x55, 0x70, 0x6c, 0xe9, 0xd6, 0xa0, 0xde, 0x69, + 0x3f, 0x92, 0xb9, 0x43, 0x56, 0x32, 0xee, 0xe8, 0x71, 0x43, 0xf8, 0x02, 0x96, 0x78, 0xd0, 0x5c, + 0xea, 0x20, 0x59, 0x5c, 0x3d, 0xda, 0x2b, 0x85, 0x6c, 0xa2, 0xc4, 0xd7, 0xa8, 0x2e, 0xbc, 0xb6, + 0xa5, 0x84, 0xe2, 0x4d, 0x11, 0x76, 0x20, 0x63, 0x6a, 0xaa, 0x8e, 0x8d, 0x5c, 0xda, 0x5d, 0x49, + 0xe1, 0x4f, 0x8f, 0x96, 0x7f, 0x7a, 0x29, 0x25, 0xfe, 0x79, 0x29, 0x25, 0xe4, 0x3d, 0x10, 0x27, + 0x53, 0x54, 0xb0, 0xd9, 0x25, 0xba, 0x89, 0xe5, 0xdf, 0xd3, 0xb0, 0x39, 0xea, 0x3e, 0x33, 0x06, + 0xb3, 0x6d, 0xe0, 0x1b, 0xc8, 0x76, 0x0d, 0xdc, 0xd7, 0x48, 0xcf, 0xac, 0xf1, 0xb4, 0xdc, 0x89, + 0x29, 0x3a, 0xb1, 0xe0, 0xd8, 0x92, 0xc8, 0x27, 0x4e, 0x82, 0x64, 0x65, 0xd3, 0xb3, 0xf2, 0x0c, + 0x46, 0x09, 0x49, 0xcf, 0x4e, 0x88, 0x02, 0x5b, 0x88, 0xf4, 0x74, 0x0b, 0x1b, 0xdd, 0xba, 0x61, + 0x0d, 0x6a, 0x7d, 0x6c, 0x98, 0x1a, 0xd1, 0x73, 0x0b, 0x34, 0x1d, 0xc9, 0xb1, 0xa5, 0x3c, 0x4b, + 0x27, 0x0c, 0x25, 0x2b, 0xd9, 0xa0, 0xf9, 0x7b, 0x66, 0x15, 0x8e, 0x01, 0xba, 0x06, 0x21, 0xcf, + 0x6a, 0x9a, 0xae, 0x59, 0xb9, 0xc5, 0x83, 0x64, 0x71, 0xad, 0xba, 0xed, 0xd8, 0xd2, 0xa6, 0xb7, + 0x31, 0xcf, 0x27, 0x2b, 0x2b, 0xf4, 0x81, 0x56, 0xc1, 0x39, 0xac, 0x31, 0x4f, 0x13, 0x6b, 0x6a, + 0xd3, 0xca, 0x65, 0xe8, 0x66, 0xc4, 0xc0, 0x66, 0x58, 0xb5, 0xf5, 0x0f, 0x4b, 0x5f, 0x51, 0x44, + 0x35, 0xef, 0x6e, 0xc5, 0xb1, 0xa5, 0x6c, 0x30, 0x2e, 0x9b, 0x2d, 0x2b, 0xab, 0xf4, 0x91, 0x21, + 0x03, 0xb2, 0x2f, 0x45, 0xc8, 0x9e, 0x87, 0xdb, 0x13, 0xba, 0xfa, 0xaa, 0xff, 0x31, 0xa1, 0x7a, + 0x05, 0xb5, 0x66, 0x53, 0xfd, 0x18, 0x60, 0x42, 0xec, 0x00, 0x27, 0x41, 0x8d, 0x57, 0x90, 0xaf, + 0xed, 0x39, 0xec, 0x8e, 0xf0, 0x1e, 0x08, 0x41, 0xeb, 0xb7, 0x2a, 0x3b, 0xb6, 0x54, 0x08, 0x11, + 0x28, 0x18, 0x6f, 0x3b, 0xe8, 0x19, 0xd6, 0xcd, 0x3c, 0x94, 0x3f, 0x04, 0x26, 0x68, 0xcd, 0x32, + 0x06, 0x5c, 0xf8, 0x2d, 0xc7, 0x96, 0x36, 0x82, 0x02, 0x59, 0xc6, 0x40, 0x56, 0x96, 0xe9, 0xd8, + 0x7d, 0x77, 0x3e, 0x30, 0xd9, 0x2b, 0xa8, 0xe5, 0xcb, 0xfe, 0x6b, 0x0a, 0xb6, 0x47, 0xbd, 0x27, + 0x44, 0x7f, 0xa6, 0x19, 0x9d, 0x9b, 0x90, 0xde, 0xa7, 0xb2, 0x8e, 0x5a, 0x54, 0xec, 0x10, 0x2a, + 0xeb, 0xa8, 0xe5, 0x51, 0xe9, 0x16, 0xe4, 0x38, 0x95, 0x0b, 0x73, 0xa1, 0x72, 0x31, 0x82, 0x4a, + 0x09, 0xf6, 0x43, 0xc9, 0xf2, 0xe9, 0xfc, 0x39, 0x09, 0xd9, 0x21, 0xe2, 0xa4, 0x4d, 0x4c, 0x3c, + 0x7b, 0xfb, 0x7f, 0x3b, 0x32, 0xa7, 0xb7, 0xfd, 0x7d, 0xc8, 0x87, 0xe4, 0xe6, 0xe7, 0xfe, 0x2a, + 0x05, 0x3b, 0x63, 0xfe, 0x1b, 0xac, 0x85, 0xd1, 0x86, 0x9a, 0x7e, 0xcb, 0x86, 0x7a, 0xb3, 0xe5, + 0x70, 0x00, 0x85, 0x70, 0xc2, 0x7c, 0x4e, 0x5f, 0xa4, 0xe0, 0xa3, 0x53, 0x53, 0x55, 0x30, 0xea, + 0x7f, 0x5b, 0x47, 0x2d, 0x6c, 0x09, 0x9f, 0x43, 0xa6, 0x4b, 0x47, 0x94, 0xc9, 0xd5, 0xa3, 0x7c, + 0xe8, 0x49, 0xc6, 0xc0, 0xfc, 0x20, 0xe3, 0x13, 0x84, 0x2f, 0x61, 0x83, 0xa5, 0x8b, 0x48, 0xa7, + 0xa3, 0x59, 0x1d, 0xac, 0x5b, 0x94, 0xde, 0xb5, 0x6a, 0xde, 0xb1, 0xa5, 0xdd, 0xe0, 0x86, 0x86, + 0x08, 0x59, 0x59, 0xa7, 0xa6, 0x13, 0xdf, 0x32, 0x41, 0x5a, 0x7a, 0x2e, 0xa4, 0x2d, 0x44, 0x90, + 0xb6, 0x4b, 0x1b, 0xce, 0x90, 0x11, 0x9f, 0xab, 0xbf, 0x52, 0x00, 0xa7, 0xa6, 0x7a, 0xa6, 0x75, + 0x30, 0xe9, 0xfd, 0x3f, 0x44, 0xf5, 0x74, 0x03, 0x23, 0xac, 0xf5, 0x71, 0x23, 0x8a, 0xa8, 0x21, + 0xc2, 0x23, 0xea, 0x3b, 0xdf, 0x32, 0x57, 0xa2, 0xbe, 0x06, 0x41, 0xc7, 0x97, 0x56, 0xcd, 0xc4, + 0x3f, 0xf6, 0xb0, 0x8e, 0x70, 0xcd, 0xc0, 0xa8, 0x4f, 0x49, 0x5b, 0xa8, 0xee, 0x3b, 0xb6, 0x74, + 0x9b, 0x45, 0x98, 0xc4, 0xc8, 0xca, 0x86, 0x6b, 0x7c, 0xca, 0x6d, 0x2e, 0x91, 0x31, 0x4a, 0x75, + 0x8b, 0xde, 0x4a, 0x39, 0xb7, 0xc3, 0x76, 0xc5, 0x0e, 0x7d, 0x6e, 0x7e, 0xa2, 0xd3, 0x1a, 0xfe, + 0x10, 0x98, 0xff, 0x14, 0x56, 0x79, 0x21, 0xbb, 0x19, 0xf1, 0x76, 0xb0, 0xe3, 0xd8, 0x92, 0x30, + 0x52, 0xe5, 0xae, 0x53, 0x56, 0x58, 0xe3, 0x60, 0xb9, 0xcf, 0xb3, 0x21, 0x84, 0x4b, 0xb6, 0xf8, + 0xae, 0x92, 0x65, 0xfe, 0xf3, 0xdc, 0x1e, 0xd5, 0xc6, 0x57, 0xee, 0xb7, 0x14, 0x15, 0xb4, 0x82, + 0x5a, 0x3a, 0x79, 0xde, 0xc6, 0x0d, 0x15, 0xd3, 0x57, 0xfb, 0x1d, 0xa4, 0x2b, 0xc2, 0x7a, 0x7d, + 0x34, 0x1a, 0x53, 0x4e, 0x19, 0x37, 0x0f, 0xc5, 0x71, 0x27, 0x36, 0xa2, 0xc4, 0xa1, 0x4e, 0x4f, + 0x9c, 0x8a, 0xfb, 0xf0, 0x9e, 0xbb, 0x35, 0xfb, 0xea, 0x19, 0x63, 0xcc, 0x23, 0xf4, 0xe8, 0xd5, + 0x32, 0xa4, 0x4f, 0x4d, 0x55, 0x68, 0xc1, 0xfa, 0xf8, 0xb7, 0xdb, 0xdd, 0x50, 0x12, 0x27, 0xbf, + 0xa0, 0xc4, 0x72, 0x4c, 0xa0, 0xb7, 0xa8, 0xd0, 0x84, 0x5b, 0x63, 0x9f, 0x59, 0x77, 0x62, 0x84, + 0x38, 0x33, 0x06, 0x62, 0x29, 0x1e, 0x2e, 0x62, 0x25, 0xf7, 0x26, 0x15, 0x67, 0xa5, 0x0a, 0x6a, + 0xc5, 0x5a, 0x29, 0x70, 0xa3, 0x14, 0x2c, 0x10, 0x42, 0x6e, 0x93, 0xf7, 0x62, 0x44, 0xe1, 0x58, + 0xf1, 0x28, 0x3e, 0xd6, 0x5f, 0x55, 0x87, 0x8d, 0x89, 0x4b, 0x57, 0x71, 0x4a, 0x1c, 0x1f, 0x29, + 0x3e, 0x88, 0x8b, 0xf4, 0xd7, 0x7b, 0x0e, 0xd9, 0xd0, 0x8b, 0x52, 0x9c, 0x40, 0xde, 0x3e, 0x1f, + 0xce, 0x00, 0xf6, 0x17, 0xfe, 0x01, 0x20, 0x70, 0x9b, 0x90, 0xa3, 0x42, 0x0c, 0x31, 0xe2, 0xbd, + 0xe9, 0x18, 0x3f, 0xfa, 0x53, 0x58, 0xf2, 0xce, 0x5f, 0x29, 0x6a, 0x1a, 0x07, 0x88, 0x77, 0xa7, + 0x00, 0x82, 0xb5, 0x37, 0x76, 0xc2, 0xdc, 0x99, 0x32, 0x95, 0xe3, 0xa2, 0x6b, 0x2f, 0xbc, 0x2b, + 0xba, 0x2f, 0xef, 0x78, 0x47, 0x8c, 0xcc, 0x72, 0x0c, 0x18, 0xfd, 0xf2, 0x46, 0x74, 0x8c, 0xaa, + 0xf2, 0xfa, 0xaa, 0x90, 0x7c, 0x73, 0x55, 0x48, 0xfe, 0x7d, 0x55, 0x48, 0xbe, 0xb8, 0x2e, 0x24, + 0xde, 0x5c, 0x17, 0x12, 0x7f, 0x5e, 0x17, 0x12, 0xe7, 0x9f, 0xa9, 0x9a, 0xd5, 0xec, 0x5d, 0x94, + 0x10, 0xe9, 0x94, 0x11, 0x31, 0x3b, 0xc4, 0xe4, 0x3f, 0xf7, 0xcd, 0x46, 0xab, 0x7c, 0x59, 0xf6, + 0xff, 0x45, 0x7a, 0x70, 0x7c, 0xdf, 0xfb, 0x23, 0xc9, 0x1a, 0x74, 0xb1, 0x79, 0x91, 0xa1, 0x7f, + 0x22, 0x3d, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0xe9, 0x16, 0xfa, 0xd3, 0x12, 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 { + // ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. + ChannelOpenInit(ctx context.Context, in *MsgChannelOpenInit, opts ...grpc.CallOption) (*MsgChannelOpenInitResponse, error) + // ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. + ChannelOpenTry(ctx context.Context, in *MsgChannelOpenTry, opts ...grpc.CallOption) (*MsgChannelOpenTryResponse, error) + // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. + ChannelOpenAck(ctx context.Context, in *MsgChannelOpenAck, opts ...grpc.CallOption) (*MsgChannelOpenAckResponse, error) + // ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. + ChannelOpenConfirm(ctx context.Context, in *MsgChannelOpenConfirm, opts ...grpc.CallOption) (*MsgChannelOpenConfirmResponse, error) + // ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. + ChannelCloseInit(ctx context.Context, in *MsgChannelCloseInit, opts ...grpc.CallOption) (*MsgChannelCloseInitResponse, error) + // ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. + ChannelCloseConfirm(ctx context.Context, in *MsgChannelCloseConfirm, opts ...grpc.CallOption) (*MsgChannelCloseConfirmResponse, error) + // RecvPacket defines a rpc handler method for MsgRecvPacket. + RecvPacket(ctx context.Context, in *MsgRecvPacket, opts ...grpc.CallOption) (*MsgRecvPacketResponse, error) + // Timeout defines a rpc handler method for MsgTimeout. + Timeout(ctx context.Context, in *MsgTimeout, opts ...grpc.CallOption) (*MsgTimeoutResponse, error) + // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. + TimeoutOnClose(ctx context.Context, in *MsgTimeoutOnClose, opts ...grpc.CallOption) (*MsgTimeoutOnCloseResponse, error) + // Acknowledgement defines a rpc handler method for MsgAcknowledgement. + Acknowledgement(ctx context.Context, in *MsgAcknowledgement, opts ...grpc.CallOption) (*MsgAcknowledgementResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) ChannelOpenInit(ctx context.Context, in *MsgChannelOpenInit, opts ...grpc.CallOption) (*MsgChannelOpenInitResponse, error) { + out := new(MsgChannelOpenInitResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelOpenInit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ChannelOpenTry(ctx context.Context, in *MsgChannelOpenTry, opts ...grpc.CallOption) (*MsgChannelOpenTryResponse, error) { + out := new(MsgChannelOpenTryResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelOpenTry", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ChannelOpenAck(ctx context.Context, in *MsgChannelOpenAck, opts ...grpc.CallOption) (*MsgChannelOpenAckResponse, error) { + out := new(MsgChannelOpenAckResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelOpenAck", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ChannelOpenConfirm(ctx context.Context, in *MsgChannelOpenConfirm, opts ...grpc.CallOption) (*MsgChannelOpenConfirmResponse, error) { + out := new(MsgChannelOpenConfirmResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelOpenConfirm", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ChannelCloseInit(ctx context.Context, in *MsgChannelCloseInit, opts ...grpc.CallOption) (*MsgChannelCloseInitResponse, error) { + out := new(MsgChannelCloseInitResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelCloseInit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ChannelCloseConfirm(ctx context.Context, in *MsgChannelCloseConfirm, opts ...grpc.CallOption) (*MsgChannelCloseConfirmResponse, error) { + out := new(MsgChannelCloseConfirmResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/ChannelCloseConfirm", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RecvPacket(ctx context.Context, in *MsgRecvPacket, opts ...grpc.CallOption) (*MsgRecvPacketResponse, error) { + out := new(MsgRecvPacketResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/RecvPacket", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Timeout(ctx context.Context, in *MsgTimeout, opts ...grpc.CallOption) (*MsgTimeoutResponse, error) { + out := new(MsgTimeoutResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/Timeout", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) TimeoutOnClose(ctx context.Context, in *MsgTimeoutOnClose, opts ...grpc.CallOption) (*MsgTimeoutOnCloseResponse, error) { + out := new(MsgTimeoutOnCloseResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/TimeoutOnClose", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Acknowledgement(ctx context.Context, in *MsgAcknowledgement, opts ...grpc.CallOption) (*MsgAcknowledgementResponse, error) { + out := new(MsgAcknowledgementResponse) + err := c.cc.Invoke(ctx, "/ibc.core.channel.v1.Msg/Acknowledgement", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. + ChannelOpenInit(context.Context, *MsgChannelOpenInit) (*MsgChannelOpenInitResponse, error) + // ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. + ChannelOpenTry(context.Context, *MsgChannelOpenTry) (*MsgChannelOpenTryResponse, error) + // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. + ChannelOpenAck(context.Context, *MsgChannelOpenAck) (*MsgChannelOpenAckResponse, error) + // ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. + ChannelOpenConfirm(context.Context, *MsgChannelOpenConfirm) (*MsgChannelOpenConfirmResponse, error) + // ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. + ChannelCloseInit(context.Context, *MsgChannelCloseInit) (*MsgChannelCloseInitResponse, error) + // ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. + ChannelCloseConfirm(context.Context, *MsgChannelCloseConfirm) (*MsgChannelCloseConfirmResponse, error) + // RecvPacket defines a rpc handler method for MsgRecvPacket. + RecvPacket(context.Context, *MsgRecvPacket) (*MsgRecvPacketResponse, error) + // Timeout defines a rpc handler method for MsgTimeout. + Timeout(context.Context, *MsgTimeout) (*MsgTimeoutResponse, error) + // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. + TimeoutOnClose(context.Context, *MsgTimeoutOnClose) (*MsgTimeoutOnCloseResponse, error) + // Acknowledgement defines a rpc handler method for MsgAcknowledgement. + Acknowledgement(context.Context, *MsgAcknowledgement) (*MsgAcknowledgementResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) ChannelOpenInit(ctx context.Context, req *MsgChannelOpenInit) (*MsgChannelOpenInitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelOpenInit not implemented") +} +func (*UnimplementedMsgServer) ChannelOpenTry(ctx context.Context, req *MsgChannelOpenTry) (*MsgChannelOpenTryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelOpenTry not implemented") +} +func (*UnimplementedMsgServer) ChannelOpenAck(ctx context.Context, req *MsgChannelOpenAck) (*MsgChannelOpenAckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelOpenAck not implemented") +} +func (*UnimplementedMsgServer) ChannelOpenConfirm(ctx context.Context, req *MsgChannelOpenConfirm) (*MsgChannelOpenConfirmResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelOpenConfirm not implemented") +} +func (*UnimplementedMsgServer) ChannelCloseInit(ctx context.Context, req *MsgChannelCloseInit) (*MsgChannelCloseInitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelCloseInit not implemented") +} +func (*UnimplementedMsgServer) ChannelCloseConfirm(ctx context.Context, req *MsgChannelCloseConfirm) (*MsgChannelCloseConfirmResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChannelCloseConfirm not implemented") +} +func (*UnimplementedMsgServer) RecvPacket(ctx context.Context, req *MsgRecvPacket) (*MsgRecvPacketResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RecvPacket not implemented") +} +func (*UnimplementedMsgServer) Timeout(ctx context.Context, req *MsgTimeout) (*MsgTimeoutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Timeout not implemented") +} +func (*UnimplementedMsgServer) TimeoutOnClose(ctx context.Context, req *MsgTimeoutOnClose) (*MsgTimeoutOnCloseResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TimeoutOnClose not implemented") +} +func (*UnimplementedMsgServer) Acknowledgement(ctx context.Context, req *MsgAcknowledgement) (*MsgAcknowledgementResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Acknowledgement not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_ChannelOpenInit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelOpenInit) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelOpenInit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelOpenInit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelOpenInit(ctx, req.(*MsgChannelOpenInit)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ChannelOpenTry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelOpenTry) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelOpenTry(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelOpenTry", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelOpenTry(ctx, req.(*MsgChannelOpenTry)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ChannelOpenAck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelOpenAck) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelOpenAck(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelOpenAck", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelOpenAck(ctx, req.(*MsgChannelOpenAck)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ChannelOpenConfirm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelOpenConfirm) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelOpenConfirm(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelOpenConfirm", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelOpenConfirm(ctx, req.(*MsgChannelOpenConfirm)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ChannelCloseInit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelCloseInit) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelCloseInit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelCloseInit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelCloseInit(ctx, req.(*MsgChannelCloseInit)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ChannelCloseConfirm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgChannelCloseConfirm) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ChannelCloseConfirm(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/ChannelCloseConfirm", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ChannelCloseConfirm(ctx, req.(*MsgChannelCloseConfirm)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RecvPacket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRecvPacket) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RecvPacket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/RecvPacket", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RecvPacket(ctx, req.(*MsgRecvPacket)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Timeout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgTimeout) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Timeout(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/Timeout", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Timeout(ctx, req.(*MsgTimeout)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_TimeoutOnClose_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgTimeoutOnClose) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).TimeoutOnClose(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/TimeoutOnClose", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).TimeoutOnClose(ctx, req.(*MsgTimeoutOnClose)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Acknowledgement_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAcknowledgement) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Acknowledgement(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ibc.core.channel.v1.Msg/Acknowledgement", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Acknowledgement(ctx, req.(*MsgAcknowledgement)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ibc.core.channel.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ChannelOpenInit", + Handler: _Msg_ChannelOpenInit_Handler, + }, + { + MethodName: "ChannelOpenTry", + Handler: _Msg_ChannelOpenTry_Handler, + }, + { + MethodName: "ChannelOpenAck", + Handler: _Msg_ChannelOpenAck_Handler, + }, + { + MethodName: "ChannelOpenConfirm", + Handler: _Msg_ChannelOpenConfirm_Handler, + }, + { + MethodName: "ChannelCloseInit", + Handler: _Msg_ChannelCloseInit_Handler, + }, + { + MethodName: "ChannelCloseConfirm", + Handler: _Msg_ChannelCloseConfirm_Handler, + }, + { + MethodName: "RecvPacket", + Handler: _Msg_RecvPacket_Handler, + }, + { + MethodName: "Timeout", + Handler: _Msg_Timeout_Handler, + }, + { + MethodName: "TimeoutOnClose", + Handler: _Msg_TimeoutOnClose_Handler, + }, + { + MethodName: "Acknowledgement", + Handler: _Msg_Acknowledgement_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ibc/core/channel/v1/tx.proto", +} + +func (m *MsgChannelOpenInit) 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 *MsgChannelOpenInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + { + size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenInitResponse) 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 *MsgChannelOpenInitResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenInitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenTry) 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 *MsgChannelOpenTry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenTry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x3a + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if len(m.ProofInit) > 0 { + i -= len(m.ProofInit) + copy(dAtA[i:], m.ProofInit) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofInit))) + i-- + dAtA[i] = 0x2a + } + if len(m.CounterpartyVersion) > 0 { + i -= len(m.CounterpartyVersion) + copy(dAtA[i:], m.CounterpartyVersion) + i = encodeVarintTx(dAtA, i, uint64(len(m.CounterpartyVersion))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.PreviousChannelId) > 0 { + i -= len(m.PreviousChannelId) + copy(dAtA[i:], m.PreviousChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PreviousChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenTryResponse) 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 *MsgChannelOpenTryResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenTryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenAck) 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 *MsgChannelOpenAck) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenAck) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x3a + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + if len(m.ProofTry) > 0 { + i -= len(m.ProofTry) + copy(dAtA[i:], m.ProofTry) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofTry))) + i-- + dAtA[i] = 0x2a + } + if len(m.CounterpartyVersion) > 0 { + i -= len(m.CounterpartyVersion) + copy(dAtA[i:], m.CounterpartyVersion) + i = encodeVarintTx(dAtA, i, uint64(len(m.CounterpartyVersion))) + i-- + dAtA[i] = 0x22 + } + if len(m.CounterpartyChannelId) > 0 { + i -= len(m.CounterpartyChannelId) + copy(dAtA[i:], m.CounterpartyChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.CounterpartyChannelId))) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenAckResponse) 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 *MsgChannelOpenAckResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenAckResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenConfirm) 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 *MsgChannelOpenConfirm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenConfirm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x2a + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ProofAck) > 0 { + i -= len(m.ProofAck) + copy(dAtA[i:], m.ProofAck) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofAck))) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelOpenConfirmResponse) 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 *MsgChannelOpenConfirmResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelOpenConfirmResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgChannelCloseInit) 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 *MsgChannelCloseInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelCloseInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelCloseInitResponse) 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 *MsgChannelCloseInitResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelCloseInitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgChannelCloseConfirm) 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 *MsgChannelCloseConfirm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelCloseConfirm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x2a + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ProofInit) > 0 { + i -= len(m.ProofInit) + copy(dAtA[i:], m.ProofInit) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofInit))) + i-- + dAtA[i] = 0x1a + } + if len(m.ChannelId) > 0 { + i -= len(m.ChannelId) + copy(dAtA[i:], m.ChannelId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChannelId))) + i-- + dAtA[i] = 0x12 + } + if len(m.PortId) > 0 { + i -= len(m.PortId) + copy(dAtA[i:], m.PortId) + i = encodeVarintTx(dAtA, i, uint64(len(m.PortId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgChannelCloseConfirmResponse) 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 *MsgChannelCloseConfirmResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgChannelCloseConfirmResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgRecvPacket) 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 *MsgRecvPacket) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRecvPacket) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ProofCommitment) > 0 { + i -= len(m.ProofCommitment) + copy(dAtA[i:], m.ProofCommitment) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofCommitment))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Packet.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 *MsgRecvPacketResponse) 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 *MsgRecvPacketResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRecvPacketResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgTimeout) 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 *MsgTimeout) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTimeout) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x2a + } + if m.NextSequenceRecv != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.NextSequenceRecv)) + i-- + dAtA[i] = 0x20 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ProofUnreceived) > 0 { + i -= len(m.ProofUnreceived) + copy(dAtA[i:], m.ProofUnreceived) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofUnreceived))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Packet.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 *MsgTimeoutResponse) 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 *MsgTimeoutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTimeoutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgTimeoutOnClose) 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 *MsgTimeoutOnClose) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTimeoutOnClose) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x32 + } + if m.NextSequenceRecv != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.NextSequenceRecv)) + i-- + dAtA[i] = 0x28 + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ProofClose) > 0 { + i -= len(m.ProofClose) + copy(dAtA[i:], m.ProofClose) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofClose))) + i-- + dAtA[i] = 0x1a + } + if len(m.ProofUnreceived) > 0 { + i -= len(m.ProofUnreceived) + copy(dAtA[i:], m.ProofUnreceived) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofUnreceived))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Packet.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 *MsgTimeoutOnCloseResponse) 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 *MsgTimeoutOnCloseResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgTimeoutOnCloseResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgAcknowledgement) 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 *MsgAcknowledgement) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAcknowledgement) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0x2a + } + { + size, err := m.ProofHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ProofAcked) > 0 { + i -= len(m.ProofAcked) + copy(dAtA[i:], m.ProofAcked) + i = encodeVarintTx(dAtA, i, uint64(len(m.ProofAcked))) + i-- + dAtA[i] = 0x1a + } + if len(m.Acknowledgement) > 0 { + i -= len(m.Acknowledgement) + copy(dAtA[i:], m.Acknowledgement) + i = encodeVarintTx(dAtA, i, uint64(len(m.Acknowledgement))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Packet.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 *MsgAcknowledgementResponse) 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 *MsgAcknowledgementResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgAcknowledgementResponse) 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 *MsgChannelOpenInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Channel.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelOpenInitResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgChannelOpenTry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.PreviousChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Channel.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.CounterpartyVersion) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofInit) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelOpenTryResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgChannelOpenAck) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.CounterpartyChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.CounterpartyVersion) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofTry) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelOpenAckResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgChannelOpenConfirm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofAck) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelOpenConfirmResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgChannelCloseInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelCloseInitResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgChannelCloseConfirm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PortId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChannelId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofInit) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgChannelCloseConfirmResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgRecvPacket) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Packet.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ProofCommitment) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRecvPacketResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgTimeout) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Packet.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ProofUnreceived) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + if m.NextSequenceRecv != 0 { + n += 1 + sovTx(uint64(m.NextSequenceRecv)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgTimeoutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgTimeoutOnClose) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Packet.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ProofUnreceived) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofClose) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + if m.NextSequenceRecv != 0 { + n += 1 + sovTx(uint64(m.NextSequenceRecv)) + } + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgTimeoutOnCloseResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgAcknowledgement) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Packet.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Acknowledgement) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ProofAcked) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.ProofHeight.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgAcknowledgementResponse) 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 *MsgChannelOpenInit) 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: MsgChannelOpenInit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenInit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", 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 := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelOpenInitResponse) 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: MsgChannelOpenInitResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenInitResponse: 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 *MsgChannelOpenTry) 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: MsgChannelOpenTry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenTry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreviousChannelId", 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.PreviousChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", 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 := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyVersion", 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.CounterpartyVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofInit", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofInit = append(m.ProofInit[:0], dAtA[iNdEx:postIndex]...) + if m.ProofInit == nil { + m.ProofInit = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelOpenTryResponse) 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: MsgChannelOpenTryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenTryResponse: 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 *MsgChannelOpenAck) 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: MsgChannelOpenAck: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenAck: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyChannelId", 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.CounterpartyChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CounterpartyVersion", 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.CounterpartyVersion = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofTry", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofTry = append(m.ProofTry[:0], dAtA[iNdEx:postIndex]...) + if m.ProofTry == nil { + m.ProofTry = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelOpenAckResponse) 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: MsgChannelOpenAckResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenAckResponse: 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 *MsgChannelOpenConfirm) 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: MsgChannelOpenConfirm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenConfirm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofAck", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofAck = append(m.ProofAck[:0], dAtA[iNdEx:postIndex]...) + if m.ProofAck == nil { + m.ProofAck = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelOpenConfirmResponse) 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: MsgChannelOpenConfirmResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelOpenConfirmResponse: 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 *MsgChannelCloseInit) 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: MsgChannelCloseInit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelCloseInit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelCloseInitResponse) 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: MsgChannelCloseInitResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelCloseInitResponse: 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 *MsgChannelCloseConfirm) 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: MsgChannelCloseConfirm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelCloseConfirm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PortId", 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.PortId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelId", 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.ChannelId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofInit", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofInit = append(m.ProofInit[:0], dAtA[iNdEx:postIndex]...) + if m.ProofInit == nil { + m.ProofInit = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgChannelCloseConfirmResponse) 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: MsgChannelCloseConfirmResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgChannelCloseConfirmResponse: 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 *MsgRecvPacket) 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: MsgRecvPacket: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRecvPacket: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Packet", 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 := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofCommitment", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofCommitment = append(m.ProofCommitment[:0], dAtA[iNdEx:postIndex]...) + if m.ProofCommitment == nil { + m.ProofCommitment = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgRecvPacketResponse) 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: MsgRecvPacketResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRecvPacketResponse: 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 *MsgTimeout) 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: MsgTimeout: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTimeout: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Packet", 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 := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofUnreceived", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofUnreceived = append(m.ProofUnreceived[:0], dAtA[iNdEx:postIndex]...) + if m.ProofUnreceived == nil { + m.ProofUnreceived = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextSequenceRecv", wireType) + } + m.NextSequenceRecv = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextSequenceRecv |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgTimeoutResponse) 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: MsgTimeoutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTimeoutResponse: 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 *MsgTimeoutOnClose) 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: MsgTimeoutOnClose: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTimeoutOnClose: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Packet", 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 := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofUnreceived", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofUnreceived = append(m.ProofUnreceived[:0], dAtA[iNdEx:postIndex]...) + if m.ProofUnreceived == nil { + m.ProofUnreceived = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofClose", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofClose = append(m.ProofClose[:0], dAtA[iNdEx:postIndex]...) + if m.ProofClose == nil { + m.ProofClose = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextSequenceRecv", wireType) + } + m.NextSequenceRecv = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextSequenceRecv |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgTimeoutOnCloseResponse) 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: MsgTimeoutOnCloseResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgTimeoutOnCloseResponse: 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 *MsgAcknowledgement) 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: MsgAcknowledgement: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAcknowledgement: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Packet", 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 := m.Packet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) + if m.Acknowledgement == nil { + m.Acknowledgement = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofAcked", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofAcked = append(m.ProofAcked[:0], dAtA[iNdEx:postIndex]...) + if m.ProofAcked == nil { + m.ProofAcked = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofHeight", 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 := m.ProofHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", 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.Signer = 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 *MsgAcknowledgementResponse) 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: MsgAcknowledgementResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAcknowledgementResponse: 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/ibc/core/05-port/keeper/keeper.go b/x/ibc/core/05-port/keeper/keeper.go new file mode 100644 index 000000000000..8a4b2300a4e0 --- /dev/null +++ b/x/ibc/core/05-port/keeper/keeper.go @@ -0,0 +1,80 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// Keeper defines the IBC connection keeper +type Keeper struct { + scopedKeeper capabilitykeeper.ScopedKeeper +} + +// NewKeeper creates a new IBC connection Keeper instance +func NewKeeper(sck capabilitykeeper.ScopedKeeper) Keeper { + return Keeper{ + scopedKeeper: sck, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"/"+types.SubModuleName) +} + +// isBounded checks a given port ID is already bounded. +func (k Keeper) isBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok +} + +// BindPort binds to a port and returns the associated capability. +// Ports must be bound statically when the chain starts in `app.go`. +// The capability must then be passed to a module which will need to pass +// it as an extra parameter when calling functions on the IBC module. +func (k *Keeper) BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability { + if err := host.PortIdentifierValidator(portID); err != nil { + panic(err.Error()) + } + + if k.isBound(ctx, portID) { + panic(fmt.Sprintf("port %s is already bound", portID)) + } + + key, err := k.scopedKeeper.NewCapability(ctx, host.PortPath(portID)) + if err != nil { + panic(err.Error()) + } + + k.Logger(ctx).Info("port binded", "port", portID) + return key +} + +// Authenticate authenticates a capability key against a port ID +// by checking if the memory address of the capability was previously +// generated and bound to the port (provided as a parameter) which the capability +// is being authenticated against. +func (k Keeper) Authenticate(ctx sdk.Context, key *capabilitytypes.Capability, portID string) bool { + if err := host.PortIdentifierValidator(portID); err != nil { + panic(err.Error()) + } + + return k.scopedKeeper.AuthenticateCapability(ctx, key, host.PortPath(portID)) +} + +// LookupModuleByPort will return the IBCModule along with the capability associated with a given portID +func (k Keeper) LookupModuleByPort(ctx sdk.Context, portID string) (string, *capabilitytypes.Capability, error) { + modules, cap, err := k.scopedKeeper.LookupModules(ctx, host.PortPath(portID)) + if err != nil { + return "", nil, err + } + + return types.GetModuleOwner(modules), cap, nil +} diff --git a/x/ibc/core/05-port/keeper/keeper_test.go b/x/ibc/core/05-port/keeper/keeper_test.go new file mode 100644 index 000000000000..29c0e1585741 --- /dev/null +++ b/x/ibc/core/05-port/keeper/keeper_test.go @@ -0,0 +1,70 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "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/ibc/core/05-port/keeper" +) + +var ( + validPort = "validportid" + invalidPort = "(invalidPortID)" +) + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + keeper *keeper.Keeper +} + +func (suite *KeeperTestSuite) SetupTest() { + isCheckTx := false + app := simapp.Setup(isCheckTx) + + suite.ctx = app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + suite.keeper = &app.IBCKeeper.PortKeeper +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) TestBind() { + // Test that invalid portID causes panic + require.Panics(suite.T(), func() { suite.keeper.BindPort(suite.ctx, invalidPort) }, "Did not panic on invalid portID") + + // Test that valid BindPort returns capability key + capKey := suite.keeper.BindPort(suite.ctx, validPort) + require.NotNil(suite.T(), capKey, "capabilityKey is nil on valid BindPort") + + // Test that rebinding the same portid causes panic + require.Panics(suite.T(), func() { suite.keeper.BindPort(suite.ctx, validPort) }, "did not panic on re-binding the same port") +} + +func (suite *KeeperTestSuite) TestAuthenticate() { + capKey := suite.keeper.BindPort(suite.ctx, validPort) + + // Require that passing in invalid portID causes panic + require.Panics(suite.T(), func() { suite.keeper.Authenticate(suite.ctx, capKey, invalidPort) }, "did not panic on invalid portID") + + // Valid authentication should return true + auth := suite.keeper.Authenticate(suite.ctx, capKey, validPort) + require.True(suite.T(), auth, "valid authentication failed") + + // Test that authenticating against incorrect portid fails + auth = suite.keeper.Authenticate(suite.ctx, capKey, "wrongportid") + require.False(suite.T(), auth, "invalid authentication failed") + + // Test that authenticating port against different valid + // capability key fails + capKey2 := suite.keeper.BindPort(suite.ctx, "otherportid") + auth = suite.keeper.Authenticate(suite.ctx, capKey2, validPort) + require.False(suite.T(), auth, "invalid authentication for different capKey failed") +} diff --git a/x/ibc/core/05-port/types/errors.go b/x/ibc/core/05-port/types/errors.go new file mode 100644 index 000000000000..23a2776f59d6 --- /dev/null +++ b/x/ibc/core/05-port/types/errors.go @@ -0,0 +1,13 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// IBC port sentinel errors +var ( + ErrPortExists = sdkerrors.Register(SubModuleName, 2, "port is already binded") + ErrPortNotFound = sdkerrors.Register(SubModuleName, 3, "port not found") + ErrInvalidPort = sdkerrors.Register(SubModuleName, 4, "invalid port") + ErrInvalidRoute = sdkerrors.Register(SubModuleName, 5, "route not found") +) diff --git a/x/ibc/core/05-port/types/keys.go b/x/ibc/core/05-port/types/keys.go new file mode 100644 index 000000000000..6e79bb535066 --- /dev/null +++ b/x/ibc/core/05-port/types/keys.go @@ -0,0 +1,15 @@ +package types + +const ( + // SubModuleName defines the IBC port name + SubModuleName = "port" + + // StoreKey is the store key string for IBC ports + StoreKey = SubModuleName + + // RouterKey is the message route for IBC ports + RouterKey = SubModuleName + + // QuerierRoute is the querier route for IBC ports + QuerierRoute = SubModuleName +) diff --git a/x/ibc/core/05-port/types/module.go b/x/ibc/core/05-port/types/module.go new file mode 100644 index 000000000000..4c686732018e --- /dev/null +++ b/x/ibc/core/05-port/types/module.go @@ -0,0 +1,78 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// IBCModule defines an interface that implements all the callbacks +// that modules must define as specified in ICS-26 +type IBCModule interface { + OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, + ) error + + OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + channelCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version, + counterpartyVersion string, + ) error + + OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, + ) error + + OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, + ) error + + OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, + ) error + + OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, + ) error + + // OnRecvPacket must return the acknowledgement bytes + // In the case of an asynchronous acknowledgement, nil should be returned. + OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + ) (*sdk.Result, []byte, error) + + OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + ) (*sdk.Result, error) + + OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + ) (*sdk.Result, error) +} diff --git a/x/ibc/core/05-port/types/router.go b/x/ibc/core/05-port/types/router.go new file mode 100644 index 000000000000..6bfba9076abd --- /dev/null +++ b/x/ibc/core/05-port/types/router.go @@ -0,0 +1,65 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// The router is a map from module name to the IBCModule +// which contains all the module-defined callbacks required by ICS-26 +type Router struct { + routes map[string]IBCModule + sealed bool +} + +func NewRouter() *Router { + return &Router{ + routes: make(map[string]IBCModule), + } +} + +// Seal prevents the Router from any subsequent route handlers to be registered. +// Seal will panic if called more than once. +func (rtr *Router) Seal() { + if rtr.sealed { + panic("router already sealed") + } + rtr.sealed = true +} + +// Sealed returns a boolean signifying if the Router is sealed or not. +func (rtr Router) Sealed() bool { + return rtr.sealed +} + +// AddRoute adds IBCModule for a given module name. It returns the Router +// so AddRoute calls can be linked. It will panic if the Router is sealed. +func (rtr *Router) AddRoute(module string, cbs IBCModule) *Router { + if rtr.sealed { + panic(fmt.Sprintf("router sealed; cannot register %s route callbacks", module)) + } + if !sdk.IsAlphaNumeric(module) { + panic("route expressions can only contain alphanumeric characters") + } + if rtr.HasRoute(module) { + panic(fmt.Sprintf("route %s has already been registered", module)) + } + + rtr.routes[module] = cbs + return rtr +} + +// HasRoute returns true if the Router has a module registered or false otherwise. +func (rtr *Router) HasRoute(module string) bool { + _, ok := rtr.routes[module] + return ok +} + +// GetRoute returns a IBCModule for a given module. +func (rtr *Router) GetRoute(module string) (IBCModule, bool) { + if !rtr.HasRoute(module) { + return nil, false + } + return rtr.routes[module], true +} diff --git a/x/ibc/core/05-port/types/utils.go b/x/ibc/core/05-port/types/utils.go new file mode 100644 index 000000000000..a12f2ef7f523 --- /dev/null +++ b/x/ibc/core/05-port/types/utils.go @@ -0,0 +1,17 @@ +package types + +import "fmt" + +// GetModuleOwner enforces that only IBC and the module bound to port can own the capability +// while future implementations may allow multiple modules to bind to a port, currently we +// only allow one module to be bound to a port at any given time +func GetModuleOwner(modules []string) string { + if len(modules) != 2 { + panic(fmt.Sprintf("capability should only be owned by port or channel owner and ibc module, multiple owners currently not supported, owners: %v", modules)) + } + + if modules[0] == "ibc" { + return modules[1] + } + return modules[0] +} diff --git a/x/ibc/core/23-commitment/types/bench_test.go b/x/ibc/core/23-commitment/types/bench_test.go new file mode 100644 index 000000000000..83794fc6f6e7 --- /dev/null +++ b/x/ibc/core/23-commitment/types/bench_test.go @@ -0,0 +1,15 @@ +package types + +import ( + "testing" +) + +func BenchmarkMerkleProofEmpty(b *testing.B) { + b.ReportAllocs() + var mk MerkleProof + for i := 0; i < b.N; i++ { + if !mk.Empty() { + b.Fatal("supposed to be empty") + } + } +} diff --git a/x/ibc/core/23-commitment/types/codec.go b/x/ibc/core/23-commitment/types/codec.go new file mode 100644 index 000000000000..1195c7c26db9 --- /dev/null +++ b/x/ibc/core/23-commitment/types/codec.go @@ -0,0 +1,43 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces registers the commitment interfaces to protobuf Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterInterface( + "ibc.core.commitment.v1.Root", + (*exported.Root)(nil), + ) + registry.RegisterInterface( + "ibc.core.commitment.v1.Prefix", + (*exported.Prefix)(nil), + ) + registry.RegisterInterface( + "ibc.core.commitment.v1.Path", + (*exported.Path)(nil), + ) + registry.RegisterInterface( + "ibc.core.commitment.v1.Proof", + (*exported.Proof)(nil), + ) + + registry.RegisterImplementations( + (*exported.Root)(nil), + &MerkleRoot{}, + ) + registry.RegisterImplementations( + (*exported.Prefix)(nil), + &MerklePrefix{}, + ) + registry.RegisterImplementations( + (*exported.Path)(nil), + &MerklePath{}, + ) + registry.RegisterImplementations( + (*exported.Proof)(nil), + &MerkleProof{}, + ) +} diff --git a/x/ibc/core/23-commitment/types/commitment.pb.go b/x/ibc/core/23-commitment/types/commitment.pb.go new file mode 100644 index 000000000000..06bc66522611 --- /dev/null +++ b/x/ibc/core/23-commitment/types/commitment.pb.go @@ -0,0 +1,863 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/commitment/v1/commitment.proto + +package types + +import ( + fmt "fmt" + _go "github.com/confio/ics23/go" + _ "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 + +// MerkleRoot defines a merkle root hash. +// In the Cosmos SDK, the AppHash of a block header becomes the root. +type MerkleRoot struct { + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (m *MerkleRoot) Reset() { *m = MerkleRoot{} } +func (m *MerkleRoot) String() string { return proto.CompactTextString(m) } +func (*MerkleRoot) ProtoMessage() {} +func (*MerkleRoot) Descriptor() ([]byte, []int) { + return fileDescriptor_7921d88972a41469, []int{0} +} +func (m *MerkleRoot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MerkleRoot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MerkleRoot.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 *MerkleRoot) XXX_Merge(src proto.Message) { + xxx_messageInfo_MerkleRoot.Merge(m, src) +} +func (m *MerkleRoot) XXX_Size() int { + return m.Size() +} +func (m *MerkleRoot) XXX_DiscardUnknown() { + xxx_messageInfo_MerkleRoot.DiscardUnknown(m) +} + +var xxx_messageInfo_MerkleRoot proto.InternalMessageInfo + +// MerklePrefix is merkle path prefixed to the key. +// The constructed key from the Path and the key will be append(Path.KeyPath, +// append(Path.KeyPrefix, key...)) +type MerklePrefix struct { + KeyPrefix []byte `protobuf:"bytes,1,opt,name=key_prefix,json=keyPrefix,proto3" json:"key_prefix,omitempty" yaml:"key_prefix"` +} + +func (m *MerklePrefix) Reset() { *m = MerklePrefix{} } +func (m *MerklePrefix) String() string { return proto.CompactTextString(m) } +func (*MerklePrefix) ProtoMessage() {} +func (*MerklePrefix) Descriptor() ([]byte, []int) { + return fileDescriptor_7921d88972a41469, []int{1} +} +func (m *MerklePrefix) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MerklePrefix) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MerklePrefix.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 *MerklePrefix) XXX_Merge(src proto.Message) { + xxx_messageInfo_MerklePrefix.Merge(m, src) +} +func (m *MerklePrefix) XXX_Size() int { + return m.Size() +} +func (m *MerklePrefix) XXX_DiscardUnknown() { + xxx_messageInfo_MerklePrefix.DiscardUnknown(m) +} + +var xxx_messageInfo_MerklePrefix proto.InternalMessageInfo + +func (m *MerklePrefix) GetKeyPrefix() []byte { + if m != nil { + return m.KeyPrefix + } + return nil +} + +// MerklePath is the path used to verify commitment proofs, which can be an +// arbitrary structured object (defined by a commitment type). +// MerklePath is represented from root-to-leaf +type MerklePath struct { + KeyPath []string `protobuf:"bytes,1,rep,name=key_path,json=keyPath,proto3" json:"key_path,omitempty" yaml:"key_path"` +} + +func (m *MerklePath) Reset() { *m = MerklePath{} } +func (*MerklePath) ProtoMessage() {} +func (*MerklePath) Descriptor() ([]byte, []int) { + return fileDescriptor_7921d88972a41469, []int{2} +} +func (m *MerklePath) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MerklePath) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MerklePath.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 *MerklePath) XXX_Merge(src proto.Message) { + xxx_messageInfo_MerklePath.Merge(m, src) +} +func (m *MerklePath) XXX_Size() int { + return m.Size() +} +func (m *MerklePath) XXX_DiscardUnknown() { + xxx_messageInfo_MerklePath.DiscardUnknown(m) +} + +var xxx_messageInfo_MerklePath proto.InternalMessageInfo + +func (m *MerklePath) GetKeyPath() []string { + if m != nil { + return m.KeyPath + } + return nil +} + +// MerkleProof is a wrapper type over a chain of CommitmentProofs. +// It demonstrates membership or non-membership for an element or set of +// elements, verifiable in conjunction with a known commitment root. Proofs +// should be succinct. +// MerkleProofs are ordered from leaf-to-root +type MerkleProof struct { + Proofs []*_go.CommitmentProof `protobuf:"bytes,1,rep,name=proofs,proto3" json:"proofs,omitempty"` +} + +func (m *MerkleProof) Reset() { *m = MerkleProof{} } +func (m *MerkleProof) String() string { return proto.CompactTextString(m) } +func (*MerkleProof) ProtoMessage() {} +func (*MerkleProof) Descriptor() ([]byte, []int) { + return fileDescriptor_7921d88972a41469, []int{3} +} +func (m *MerkleProof) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MerkleProof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MerkleProof.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 *MerkleProof) XXX_Merge(src proto.Message) { + xxx_messageInfo_MerkleProof.Merge(m, src) +} +func (m *MerkleProof) XXX_Size() int { + return m.Size() +} +func (m *MerkleProof) XXX_DiscardUnknown() { + xxx_messageInfo_MerkleProof.DiscardUnknown(m) +} + +var xxx_messageInfo_MerkleProof proto.InternalMessageInfo + +func (m *MerkleProof) GetProofs() []*_go.CommitmentProof { + if m != nil { + return m.Proofs + } + return nil +} + +func init() { + proto.RegisterType((*MerkleRoot)(nil), "ibc.core.commitment.v1.MerkleRoot") + proto.RegisterType((*MerklePrefix)(nil), "ibc.core.commitment.v1.MerklePrefix") + proto.RegisterType((*MerklePath)(nil), "ibc.core.commitment.v1.MerklePath") + proto.RegisterType((*MerkleProof)(nil), "ibc.core.commitment.v1.MerkleProof") +} + +func init() { + proto.RegisterFile("ibc/core/commitment/v1/commitment.proto", fileDescriptor_7921d88972a41469) +} + +var fileDescriptor_7921d88972a41469 = []byte{ + // 334 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xcf, 0x4e, 0xfa, 0x40, + 0x10, 0xc7, 0xdb, 0xfc, 0x08, 0x3f, 0x59, 0x48, 0x8c, 0x45, 0x89, 0xe1, 0x50, 0x4c, 0x0f, 0xca, + 0x85, 0xdd, 0x00, 0x9e, 0x30, 0x5e, 0xaa, 0x57, 0x13, 0xd2, 0xc4, 0x8b, 0x17, 0xd3, 0xae, 0x5b, + 0xba, 0x29, 0x65, 0x9a, 0xee, 0x4a, 0xe8, 0x1b, 0x78, 0xf4, 0xe8, 0xd1, 0xc7, 0xf1, 0xc8, 0xd1, + 0x13, 0x31, 0xf0, 0x06, 0x3c, 0x81, 0xe9, 0x2e, 0x60, 0x4f, 0x3b, 0xb3, 0xf3, 0x99, 0x7f, 0xdf, + 0x41, 0x57, 0x3c, 0xa0, 0x84, 0x42, 0xc6, 0x08, 0x85, 0x24, 0xe1, 0x32, 0x61, 0x33, 0x49, 0xe6, + 0xfd, 0x92, 0x87, 0xd3, 0x0c, 0x24, 0x58, 0x2d, 0x1e, 0x50, 0x5c, 0x80, 0xb8, 0x14, 0x9a, 0xf7, + 0xdb, 0xa7, 0x13, 0x98, 0x80, 0x42, 0x48, 0x61, 0x69, 0xba, 0xdd, 0xa4, 0x30, 0x0b, 0x39, 0x90, + 0x34, 0x03, 0x08, 0x85, 0xfe, 0x74, 0x2e, 0x11, 0x7a, 0x60, 0x59, 0x3c, 0x65, 0x1e, 0x80, 0xb4, + 0x2c, 0x54, 0x89, 0x7c, 0x11, 0x9d, 0x9b, 0x17, 0x66, 0xb7, 0xe1, 0x29, 0x7b, 0x54, 0x79, 0xfb, + 0xec, 0x18, 0xce, 0x3d, 0x6a, 0x68, 0x6e, 0x9c, 0xb1, 0x90, 0x2f, 0xac, 0x6b, 0x84, 0x62, 0x96, + 0x3f, 0xa7, 0xca, 0xd3, 0xbc, 0x7b, 0xb6, 0x5d, 0x75, 0x4e, 0x72, 0x3f, 0x99, 0x8e, 0x9c, 0xbf, + 0x98, 0xe3, 0xd5, 0x62, 0x96, 0xeb, 0x2c, 0xc7, 0xdd, 0x77, 0x1b, 0xfb, 0x32, 0xb2, 0x30, 0x3a, + 0x52, 0x9c, 0x2f, 0x8b, 0x8e, 0xff, 0xba, 0x35, 0xb7, 0xb9, 0x5d, 0x75, 0x8e, 0x4b, 0x15, 0x7c, + 0x19, 0x39, 0xde, 0xff, 0x22, 0xdf, 0x97, 0xd1, 0xa8, 0xf2, 0x51, 0x4c, 0x72, 0x8b, 0xea, 0xfb, + 0x49, 0x00, 0x42, 0x0b, 0xa3, 0xaa, 0x5e, 0x48, 0x95, 0xa8, 0x0f, 0x5a, 0x98, 0x53, 0x31, 0x18, + 0xe2, 0xbb, 0x83, 0x22, 0x8a, 0xf3, 0x76, 0x94, 0xfb, 0xf8, 0xb5, 0xb6, 0xcd, 0xe5, 0xda, 0x36, + 0x7f, 0xd6, 0xb6, 0xf9, 0xbe, 0xb1, 0x8d, 0xe5, 0xc6, 0x36, 0xbe, 0x37, 0xb6, 0xf1, 0x74, 0x33, + 0xe1, 0x32, 0x7a, 0x0d, 0x0a, 0x2d, 0x09, 0x05, 0x91, 0x80, 0xd8, 0x3d, 0x3d, 0xf1, 0x12, 0x93, + 0x05, 0x39, 0x5c, 0x65, 0x30, 0xec, 0x95, 0x0e, 0x23, 0xf3, 0x94, 0x89, 0xa0, 0xaa, 0xe4, 0x1c, + 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xe7, 0x68, 0xd0, 0xbc, 0x01, 0x00, 0x00, +} + +func (m *MerkleRoot) 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 *MerkleRoot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MerkleRoot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintCommitment(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MerklePrefix) 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 *MerklePrefix) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MerklePrefix) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.KeyPrefix) > 0 { + i -= len(m.KeyPrefix) + copy(dAtA[i:], m.KeyPrefix) + i = encodeVarintCommitment(dAtA, i, uint64(len(m.KeyPrefix))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MerklePath) 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 *MerklePath) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MerklePath) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.KeyPath) > 0 { + for iNdEx := len(m.KeyPath) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.KeyPath[iNdEx]) + copy(dAtA[i:], m.KeyPath[iNdEx]) + i = encodeVarintCommitment(dAtA, i, uint64(len(m.KeyPath[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *MerkleProof) 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 *MerkleProof) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MerkleProof) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Proofs) > 0 { + for iNdEx := len(m.Proofs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Proofs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommitment(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintCommitment(dAtA []byte, offset int, v uint64) int { + offset -= sovCommitment(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MerkleRoot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovCommitment(uint64(l)) + } + return n +} + +func (m *MerklePrefix) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.KeyPrefix) + if l > 0 { + n += 1 + l + sovCommitment(uint64(l)) + } + return n +} + +func (m *MerklePath) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.KeyPath) > 0 { + for _, s := range m.KeyPath { + l = len(s) + n += 1 + l + sovCommitment(uint64(l)) + } + } + return n +} + +func (m *MerkleProof) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Proofs) > 0 { + for _, e := range m.Proofs { + l = e.Size() + n += 1 + l + sovCommitment(uint64(l)) + } + } + return n +} + +func sovCommitment(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCommitment(x uint64) (n int) { + return sovCommitment(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MerkleRoot) 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 ErrIntOverflowCommitment + } + 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: MerkleRoot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MerkleRoot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCommitment + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCommitment + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitment(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitment + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MerklePrefix) 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 ErrIntOverflowCommitment + } + 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: MerklePrefix: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MerklePrefix: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KeyPrefix", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCommitment + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCommitment + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KeyPrefix = append(m.KeyPrefix[:0], dAtA[iNdEx:postIndex]...) + if m.KeyPrefix == nil { + m.KeyPrefix = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitment(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitment + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MerklePath) 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 ErrIntOverflowCommitment + } + 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: MerklePath: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MerklePath: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KeyPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitment + } + 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 ErrInvalidLengthCommitment + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommitment + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KeyPath = append(m.KeyPath, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitment(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitment + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MerkleProof) 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 ErrIntOverflowCommitment + } + 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: MerkleProof: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MerkleProof: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proofs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommitment + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommitment + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommitment + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proofs = append(m.Proofs, &_go.CommitmentProof{}) + if err := m.Proofs[len(m.Proofs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommitment(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCommitment + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCommitment(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, ErrIntOverflowCommitment + } + 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, ErrIntOverflowCommitment + } + 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, ErrIntOverflowCommitment + } + 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, ErrInvalidLengthCommitment + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCommitment + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCommitment + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCommitment = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCommitment = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCommitment = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/core/23-commitment/types/commitment_test.go b/x/ibc/core/23-commitment/types/commitment_test.go new file mode 100644 index 000000000000..932599e539cd --- /dev/null +++ b/x/ibc/core/23-commitment/types/commitment_test.go @@ -0,0 +1,37 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + + dbm "github.com/tendermint/tm-db" +) + +type MerkleTestSuite struct { + suite.Suite + + store *rootmulti.Store + storeKey *storetypes.KVStoreKey + iavlStore *iavl.Store +} + +func (suite *MerkleTestSuite) SetupTest() { + db := dbm.NewMemDB() + suite.store = rootmulti.NewStore(db) + + suite.storeKey = storetypes.NewKVStoreKey("iavlStoreKey") + + suite.store.MountStoreWithDB(suite.storeKey, storetypes.StoreTypeIAVL, nil) + suite.store.LoadVersion(0) + + suite.iavlStore = suite.store.GetCommitStore(suite.storeKey).(*iavl.Store) +} + +func TestMerkleTestSuite(t *testing.T) { + suite.Run(t, new(MerkleTestSuite)) +} diff --git a/x/ibc/core/23-commitment/types/errors.go b/x/ibc/core/23-commitment/types/errors.go new file mode 100644 index 000000000000..7191baef1cc9 --- /dev/null +++ b/x/ibc/core/23-commitment/types/errors.go @@ -0,0 +1,15 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// SubModuleName is the error codespace +const SubModuleName string = "commitment" + +// IBC connection sentinel errors +var ( + ErrInvalidProof = sdkerrors.Register(SubModuleName, 2, "invalid proof") + ErrInvalidPrefix = sdkerrors.Register(SubModuleName, 3, "invalid prefix") + ErrInvalidMerkleProof = sdkerrors.Register(SubModuleName, 4, "invalid merkle proof") +) diff --git a/x/ibc/core/23-commitment/types/merkle.go b/x/ibc/core/23-commitment/types/merkle.go new file mode 100644 index 000000000000..e90fccc34b27 --- /dev/null +++ b/x/ibc/core/23-commitment/types/merkle.go @@ -0,0 +1,312 @@ +package types + +import ( + "bytes" + "fmt" + "net/url" + + ics23 "github.com/confio/ics23/go" + "github.com/gogo/protobuf/proto" + tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// var representing the proofspecs for a SDK chain +var sdkSpecs = []*ics23.ProofSpec{ics23.IavlSpec, ics23.TendermintSpec} + +// ICS 023 Merkle Types Implementation +// +// This file defines Merkle commitment types that implements ICS 023. + +// Merkle proof implementation of the Proof interface +// Applied on SDK-based IBC implementation +var _ exported.Root = (*MerkleRoot)(nil) + +// GetSDKSpecs is a getter function for the proofspecs of an sdk chain +func GetSDKSpecs() []*ics23.ProofSpec { + return sdkSpecs +} + +// NewMerkleRoot constructs a new MerkleRoot +func NewMerkleRoot(hash []byte) MerkleRoot { + return MerkleRoot{ + Hash: hash, + } +} + +// GetHash implements RootI interface +func (mr MerkleRoot) GetHash() []byte { + return mr.Hash +} + +// Empty returns true if the root is empty +func (mr MerkleRoot) Empty() bool { + return len(mr.GetHash()) == 0 +} + +var _ exported.Prefix = (*MerklePrefix)(nil) + +// NewMerklePrefix constructs new MerklePrefix instance +func NewMerklePrefix(keyPrefix []byte) MerklePrefix { + return MerklePrefix{ + KeyPrefix: keyPrefix, + } +} + +// Bytes returns the key prefix bytes +func (mp MerklePrefix) Bytes() []byte { + return mp.KeyPrefix +} + +// Empty returns true if the prefix is empty +func (mp MerklePrefix) Empty() bool { + return len(mp.Bytes()) == 0 +} + +var _ exported.Path = (*MerklePath)(nil) + +// NewMerklePath creates a new MerklePath instance +// The keys must be passed in from root-to-leaf order +func NewMerklePath(keyPath ...string) MerklePath { + return MerklePath{ + KeyPath: keyPath, + } +} + +// String implements fmt.Stringer. +// This represents the path in the same way the tendermint KeyPath will +// represent a key path. The backslashes partition the key path into +// the respective stores they belong to. +func (mp MerklePath) String() string { + pathStr := "" + for _, k := range mp.KeyPath { + pathStr += "/" + url.PathEscape(k) + } + return pathStr +} + +// Pretty returns the unescaped path of the URL string. +// This function will unescape any backslash within a particular store key. +// This makes the keypath more human-readable while removing information +// about the exact partitions in the key path. +func (mp MerklePath) Pretty() string { + path, err := url.PathUnescape(mp.String()) + if err != nil { + panic(err) + } + return path +} + +// GetKey will return a byte representation of the key +// after URL escaping the key element +func (mp MerklePath) GetKey(i uint64) ([]byte, error) { + if i >= uint64(len(mp.KeyPath)) { + return nil, fmt.Errorf("index out of range. %d (index) >= %d (len)", i, len(mp.KeyPath)) + } + key, err := url.PathUnescape(mp.KeyPath[i]) + if err != nil { + return nil, err + } + return []byte(key), nil +} + +// Empty returns true if the path is empty +func (mp MerklePath) Empty() bool { + return len(mp.KeyPath) == 0 +} + +// ApplyPrefix constructs a new commitment path from the arguments. It prepends the prefix key +// with the given path. +func ApplyPrefix(prefix exported.Prefix, path MerklePath) (MerklePath, error) { + if prefix == nil || prefix.Empty() { + return MerklePath{}, sdkerrors.Wrap(ErrInvalidPrefix, "prefix can't be empty") + } + return NewMerklePath(append([]string{string(prefix.Bytes())}, path.KeyPath...)...), nil +} + +var _ exported.Proof = (*MerkleProof)(nil) + +// VerifyMembership verifies the membership pf a merkle proof against the given root, path, and value. +func (proof MerkleProof) VerifyMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, value []byte) error { + if err := proof.validateVerificationArgs(specs, root); err != nil { + return err + } + + // VerifyMembership specific argument validation + mpath, ok := path.(MerklePath) + if !ok { + return sdkerrors.Wrapf(ErrInvalidProof, "path %v is not of type MerklePath", path) + } + if len(mpath.KeyPath) != len(specs) { + return sdkerrors.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", + len(mpath.KeyPath), len(specs)) + } + if len(value) == 0 { + return sdkerrors.Wrap(ErrInvalidProof, "empty value in membership proof") + } + + // Since every proof in chain is a membership proof we can use verifyChainedMembershipProof from index 0 + // to validate entire proof + if err := verifyChainedMembershipProof(root.GetHash(), specs, proof.Proofs, mpath, value, 0); err != nil { + return err + } + return nil +} + +// VerifyNonMembership verifies the absence of a merkle proof against the given root and path. +// VerifyNonMembership verifies a chained proof where the absence of a given path is proven +// at the lowest subtree and then each subtree's inclusion is proved up to the final root. +func (proof MerkleProof) VerifyNonMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path) error { + if err := proof.validateVerificationArgs(specs, root); err != nil { + return err + } + + // VerifyNonMembership specific argument validation + mpath, ok := path.(MerklePath) + if !ok { + return sdkerrors.Wrapf(ErrInvalidProof, "path %v is not of type MerkleProof", path) + } + if len(mpath.KeyPath) != len(specs) { + return sdkerrors.Wrapf(ErrInvalidProof, "path length %d not same as proof %d", + len(mpath.KeyPath), len(specs)) + } + + switch proof.Proofs[0].Proof.(type) { + case *ics23.CommitmentProof_Nonexist: + // VerifyNonMembership will verify the absence of key in lowest subtree, and then chain inclusion proofs + // of all subroots up to final root + subroot, err := proof.Proofs[0].Calculate() + if err != nil { + return sdkerrors.Wrapf(ErrInvalidProof, "could not calculate root for proof index 0, merkle tree is likely empty. %v", err) + } + key, err := mpath.GetKey(uint64(len(mpath.KeyPath) - 1)) + if err != nil { + return sdkerrors.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key: %s", mpath.KeyPath[len(mpath.KeyPath)-1]) + } + if ok := ics23.VerifyNonMembership(specs[0], subroot, proof.Proofs[0], key); !ok { + return sdkerrors.Wrapf(ErrInvalidProof, "could not verify absence of key %s. Please ensure that the path is correct.", string(key)) + } + + // Verify chained membership proof starting from index 1 with value = subroot + if err := verifyChainedMembershipProof(root.GetHash(), specs, proof.Proofs, mpath, subroot, 1); err != nil { + return err + } + case *ics23.CommitmentProof_Exist: + return sdkerrors.Wrapf(ErrInvalidProof, + "got ExistenceProof in VerifyNonMembership. If this is unexpected, please ensure that proof was queried with the correct key.") + default: + return sdkerrors.Wrapf(ErrInvalidProof, + "expected proof type: %T, got: %T", &ics23.CommitmentProof_Exist{}, proof.Proofs[0].Proof) + } + return nil +} + +// BatchVerifyMembership verifies a group of key value pairs against the given root +// NOTE: Currently left unimplemented as it is unused +func (proof MerkleProof) BatchVerifyMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, items map[string][]byte) error { + return sdkerrors.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") +} + +// BatchVerifyNonMembership verifies absence of a group of keys against the given root +// NOTE: Currently left unimplemented as it is unused +func (proof MerkleProof) BatchVerifyNonMembership(specs []*ics23.ProofSpec, root exported.Root, path exported.Path, items [][]byte) error { + return sdkerrors.Wrap(ErrInvalidProof, "batch proofs are currently unsupported") +} + +// verifyChainedMembershipProof takes a list of proofs and specs and verifies each proof sequentially ensuring that the value is committed to +// by first proof and each subsequent subroot is committed to by the next subroot and checking that the final calculated root is equal to the given roothash. +// The proofs and specs are passed in from lowest subtree to the highest subtree, but the keys are passed in from highest subtree to lowest. +// The index specifies what index to start chaining the membership proofs, this is useful since the lowest proof may not be a membership proof, thus we +// will want to start the membership proof chaining from index 1 with value being the lowest subroot +func verifyChainedMembershipProof(root []byte, specs []*ics23.ProofSpec, proofs []*ics23.CommitmentProof, keys MerklePath, value []byte, index int) error { + var ( + subroot []byte + err error + ) + // Initialize subroot to value since the proofs list may be empty. + // This may happen if this call is verifying intermediate proofs after the lowest proof has been executed. + // In this case, there may be no intermediate proofs to verify and we just check that lowest proof root equals final root + subroot = value + for i := index; i < len(proofs); i++ { + switch proofs[i].Proof.(type) { + case *ics23.CommitmentProof_Exist: + subroot, err = proofs[i].Calculate() + if err != nil { + return sdkerrors.Wrapf(ErrInvalidProof, "could not calculate proof root at index %d, merkle tree may be empty. %v", i, err) + } + // Since keys are passed in from highest to lowest, we must grab their indices in reverse order + // from the proofs and specs which are lowest to highest + key, err := keys.GetKey(uint64(len(keys.KeyPath) - 1 - i)) + if err != nil { + return sdkerrors.Wrapf(ErrInvalidProof, "could not retrieve key bytes for key %s: %v", keys.KeyPath[len(keys.KeyPath)-1-i], err) + } + + // verify membership of the proof at this index with appropriate key and value + if ok := ics23.VerifyMembership(specs[i], subroot, proofs[i], key, value); !ok { + return sdkerrors.Wrapf(ErrInvalidProof, + "chained membership proof failed to verify membership of value: %X in subroot %X at index %d. Please ensure the path and value are both correct.", + value, subroot, i) + } + // Set value to subroot so that we verify next proof in chain commits to this subroot + value = subroot + case *ics23.CommitmentProof_Nonexist: + return sdkerrors.Wrapf(ErrInvalidProof, + "chained membership proof contains nonexistence proof at index %d. If this is unexpected, please ensure that proof was queried from the height that contained the value in store and was queried with the correct key.", + i) + default: + return sdkerrors.Wrapf(ErrInvalidProof, + "expected proof type: %T, got: %T", &ics23.CommitmentProof_Exist{}, proofs[i].Proof) + } + } + // Check that chained proof root equals passed-in root + if !bytes.Equal(root, subroot) { + return sdkerrors.Wrapf(ErrInvalidProof, + "proof did not commit to expected root: %X, got: %X. Please ensure proof was submitted with correct proofHeight and to the correct chain.", + root, subroot) + } + return nil +} + +// blankMerkleProof and blankProofOps will be used to compare against their zero values, +// and are declared as globals to avoid having to unnecessarily re-allocate on every comparison. +var blankMerkleProof = &MerkleProof{} +var blankProofOps = &tmcrypto.ProofOps{} + +// Empty returns true if the root is empty +func (proof *MerkleProof) Empty() bool { + return proof == nil || proto.Equal(proof, blankMerkleProof) || proto.Equal(proof, blankProofOps) +} + +// ValidateBasic checks if the proof is empty. +func (proof MerkleProof) ValidateBasic() error { + if proof.Empty() { + return ErrInvalidProof + } + return nil +} + +// validateVerificationArgs verifies the proof arguments are valid +func (proof MerkleProof) validateVerificationArgs(specs []*ics23.ProofSpec, root exported.Root) error { + if proof.Empty() { + return sdkerrors.Wrap(ErrInvalidMerkleProof, "proof cannot be empty") + } + + if root == nil || root.Empty() { + return sdkerrors.Wrap(ErrInvalidMerkleProof, "root cannot be empty") + } + + if len(specs) != len(proof.Proofs) { + return sdkerrors.Wrapf(ErrInvalidMerkleProof, + "length of specs: %d not equal to length of proof: %d", + len(specs), len(proof.Proofs)) + } + + for i, spec := range specs { + if spec == nil { + return sdkerrors.Wrapf(ErrInvalidProof, "spec at position %d is nil", i) + } + } + return nil +} diff --git a/x/ibc/core/23-commitment/types/merkle_test.go b/x/ibc/core/23-commitment/types/merkle_test.go new file mode 100644 index 000000000000..3c53847fadd4 --- /dev/null +++ b/x/ibc/core/23-commitment/types/merkle_test.go @@ -0,0 +1,172 @@ +package types_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" +) + +func (suite *MerkleTestSuite) TestVerifyMembership() { + suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := suite.store.Commit() + + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(suite.T(), res.ProofOps) + + proof, err := types.ConvertProofs(res.ProofOps) + require.NoError(suite.T(), err) + + suite.Require().NoError(proof.ValidateBasic()) + suite.Require().Error(types.MerkleProof{}.ValidateBasic()) + + cases := []struct { + name string + root []byte + pathArr []string + value []byte + malleate func() + shouldPass bool + }{ + {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), func() {}, true}, // valid proof + {"wrong value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("WRONGVALUE"), func() {}, false}, // invalid proof with wrong value + {"nil value", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte(nil), func() {}, false}, // invalid proof with nil value + {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "NOTMYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong key + {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong path + {"wrong path 2", cid.Hash, []string{suite.storeKey.Name()}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong path + {"wrong path 3", cid.Hash, []string{"MYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong path + {"wrong storekey", cid.Hash, []string{"otherStoreKey", "MYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong store prefix + {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with wrong root + {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), func() {}, false}, // invalid proof with nil root + {"proof is wrong length", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, []byte("MYVALUE"), func() { + proof = types.MerkleProof{ + Proofs: proof.Proofs[1:], + } + }, false}, // invalid proof with wrong length + + } + + for i, tc := range cases { + tc := tc + suite.Run(tc.name, func() { + tc.malleate() + + root := types.NewMerkleRoot(tc.root) + path := types.NewMerklePath(tc.pathArr...) + + err := proof.VerifyMembership(types.GetSDKSpecs(), &root, path, tc.value) + + if tc.shouldPass { + // nolint: scopelint + suite.Require().NoError(err, "test case %d should have passed", i) + } else { + // nolint: scopelint + suite.Require().Error(err, "test case %d should have failed", i) + } + }) + } + +} + +func (suite *MerkleTestSuite) TestVerifyNonMembership() { + suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := suite.store.Commit() + + // Get Proof + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof + Data: []byte("MYABSENTKEY"), + Prove: true, + }) + require.NotNil(suite.T(), res.ProofOps) + + proof, err := types.ConvertProofs(res.ProofOps) + require.NoError(suite.T(), err) + + suite.Require().NoError(proof.ValidateBasic()) + + cases := []struct { + name string + root []byte + pathArr []string + malleate func() + shouldPass bool + }{ + {"valid proof", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY"}, func() {}, true}, // valid proof + {"wrong key", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, func() {}, false}, // invalid proof with existent key + {"wrong path 1", cid.Hash, []string{suite.storeKey.Name(), "MYKEY", "MYABSENTKEY"}, func() {}, false}, // invalid proof with wrong path + {"wrong path 2", cid.Hash, []string{suite.storeKey.Name(), "MYABSENTKEY", "MYKEY"}, func() {}, false}, // invalid proof with wrong path + {"wrong path 3", cid.Hash, []string{suite.storeKey.Name()}, func() {}, false}, // invalid proof with wrong path + {"wrong path 4", cid.Hash, []string{"MYABSENTKEY"}, func() {}, false}, // invalid proof with wrong path + {"wrong storeKey", cid.Hash, []string{"otherStoreKey", "MYABSENTKEY"}, func() {}, false}, // invalid proof with wrong store prefix + {"wrong root", []byte("WRONGROOT"), []string{suite.storeKey.Name(), "MYABSENTKEY"}, func() {}, false}, // invalid proof with wrong root + {"nil root", []byte(nil), []string{suite.storeKey.Name(), "MYABSENTKEY"}, func() {}, false}, // invalid proof with nil root + {"proof is wrong length", cid.Hash, []string{suite.storeKey.Name(), "MYKEY"}, func() { + proof = types.MerkleProof{ + Proofs: proof.Proofs[1:], + } + }, false}, // invalid proof with wrong length + + } + + for i, tc := range cases { + tc := tc + + suite.Run(tc.name, func() { + tc.malleate() + + root := types.NewMerkleRoot(tc.root) + path := types.NewMerklePath(tc.pathArr...) + + err := proof.VerifyNonMembership(types.GetSDKSpecs(), &root, path) + + if tc.shouldPass { + // nolint: scopelint + suite.Require().NoError(err, "test case %d should have passed", i) + } else { + // nolint: scopelint + suite.Require().Error(err, "test case %d should have failed", i) + } + }) + } + +} + +func TestApplyPrefix(t *testing.T) { + prefix := types.NewMerklePrefix([]byte("storePrefixKey")) + + pathStr := "pathone/pathtwo/paththree/key" + path := types.MerklePath{ + KeyPath: []string{pathStr}, + } + + prefixedPath, err := types.ApplyPrefix(prefix, path) + require.NoError(t, err, "valid prefix returns error") + + require.Equal(t, "/storePrefixKey/"+pathStr, prefixedPath.Pretty(), "Prefixed path incorrect") + require.Equal(t, "/storePrefixKey/pathone%2Fpathtwo%2Fpaththree%2Fkey", prefixedPath.String(), "Prefixed escaped path incorrect") +} + +func TestString(t *testing.T) { + path := types.NewMerklePath("rootKey", "storeKey", "path/to/leaf") + + require.Equal(t, "/rootKey/storeKey/path%2Fto%2Fleaf", path.String(), "path String returns unxpected value") + require.Equal(t, "/rootKey/storeKey/path/to/leaf", path.Pretty(), "path's pretty string representation is incorrect") + + onePath := types.NewMerklePath("path/to/leaf") + + require.Equal(t, "/path%2Fto%2Fleaf", onePath.String(), "one element path does not have correct string representation") + require.Equal(t, "/path/to/leaf", onePath.Pretty(), "one element path has incorrect pretty string representation") + + zeroPath := types.NewMerklePath() + + require.Equal(t, "", zeroPath.String(), "zero element path does not have correct string representation") + require.Equal(t, "", zeroPath.Pretty(), "zero element path does not have correct pretty string representation") +} diff --git a/x/ibc/core/23-commitment/types/utils.go b/x/ibc/core/23-commitment/types/utils.go new file mode 100644 index 000000000000..e662f772655a --- /dev/null +++ b/x/ibc/core/23-commitment/types/utils.go @@ -0,0 +1,28 @@ +package types + +import ( + ics23 "github.com/confio/ics23/go" + crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ConvertProofs converts crypto.ProofOps into MerkleProof +func ConvertProofs(tmProof *crypto.ProofOps) (MerkleProof, error) { + if tmProof == nil { + return MerkleProof{}, sdkerrors.Wrapf(ErrInvalidMerkleProof, "tendermint proof is nil") + } + // Unmarshal all proof ops to CommitmentProof + proofs := make([]*ics23.CommitmentProof, len(tmProof.Ops)) + for i, op := range tmProof.Ops { + var p ics23.CommitmentProof + err := p.Unmarshal(op.Data) + if err != nil || p.Proof == nil { + return MerkleProof{}, sdkerrors.Wrapf(ErrInvalidMerkleProof, "could not unmarshal proof op into CommitmentProof at index %d: %v", i, err) + } + proofs[i] = &p + } + return MerkleProof{ + Proofs: proofs, + }, nil +} diff --git a/x/ibc/core/23-commitment/types/utils_test.go b/x/ibc/core/23-commitment/types/utils_test.go new file mode 100644 index 000000000000..f852fb6c2c4b --- /dev/null +++ b/x/ibc/core/23-commitment/types/utils_test.go @@ -0,0 +1,98 @@ +package types_test + +import ( + "fmt" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + crypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" +) + +func (suite *MerkleTestSuite) TestConvertProofs() { + suite.iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) + cid := suite.store.Commit() + + root := types.NewMerkleRoot(cid.Hash) + existsPath := types.NewMerklePath(suite.storeKey.Name(), "MYKEY") + nonexistPath := types.NewMerklePath(suite.storeKey.Name(), "NOTMYKEY") + value := []byte("MYVALUE") + + var proofOps *crypto.ProofOps + testcases := []struct { + name string + malleate func() + keyExists bool + expPass bool + }{ + { + "success for ExistenceProof", + func() { + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(suite.T(), res.ProofOps) + + proofOps = res.ProofOps + }, + true, true, + }, + { + "success for NonexistenceProof", + func() { + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof + Data: []byte("NOTMYKEY"), + Prove: true, + }) + require.NotNil(suite.T(), res.ProofOps) + + proofOps = res.ProofOps + }, + false, true, + }, + { + "nil proofOps", + func() { + proofOps = nil + }, + true, false, + }, + { + "proof op data is nil", + func() { + res := suite.store.Query(abci.RequestQuery{ + Path: fmt.Sprintf("/%s/key", suite.storeKey.Name()), // required path to get key/value+proof + Data: []byte("MYKEY"), + Prove: true, + }) + require.NotNil(suite.T(), res.ProofOps) + + proofOps = res.ProofOps + proofOps.Ops[0].Data = nil + }, + true, false, + }, + } + + for _, tc := range testcases { + tc.malleate() + + proof, err := types.ConvertProofs(proofOps) + if tc.expPass { + suite.Require().NoError(err, "ConvertProofs unexpectedly returned error for case: %s", tc.name) + if tc.keyExists { + err := proof.VerifyMembership(types.GetSDKSpecs(), &root, existsPath, value) + suite.Require().NoError(err, "converted proof failed to verify membership for case: %s", tc.name) + } else { + err := proof.VerifyNonMembership(types.GetSDKSpecs(), &root, nonexistPath) + suite.Require().NoError(err, "converted proof failed to verify membership for case: %s", tc.name) + } + } else { + suite.Require().Error(err, "ConvertProofs passed on invalid case for case: %s", tc.name) + } + } +} diff --git a/x/ibc/core/24-host/errors.go b/x/ibc/core/24-host/errors.go new file mode 100644 index 000000000000..fe8129bde864 --- /dev/null +++ b/x/ibc/core/24-host/errors.go @@ -0,0 +1,15 @@ +package host + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// SubModuleName defines the ICS 24 host +const SubModuleName = "host" + +// IBC client sentinel errors +var ( + ErrInvalidID = sdkerrors.Register(SubModuleName, 2, "invalid identifier") + ErrInvalidPath = sdkerrors.Register(SubModuleName, 3, "invalid path") + ErrInvalidPacket = sdkerrors.Register(SubModuleName, 4, "invalid packet") +) diff --git a/x/ibc/core/24-host/keys.go b/x/ibc/core/24-host/keys.go new file mode 100644 index 000000000000..21f4bc43090d --- /dev/null +++ b/x/ibc/core/24-host/keys.go @@ -0,0 +1,235 @@ +package host + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +const ( + // ModuleName is the name of the IBC module + ModuleName = "ibc" + + // StoreKey is the string store representation + StoreKey string = ModuleName + + // QuerierRoute is the querier route for the IBC module + QuerierRoute string = ModuleName + + // RouterKey is the msg router key for the IBC module + RouterKey string = ModuleName +) + +// KVStore key prefixes for IBC +var ( + KeyClientStorePrefix = []byte("clients") +) + +// KVStore key prefixes for IBC +const ( + KeyClientState = "clientState" + KeyConsensusStatePrefix = "consensusStates" + KeyConnectionPrefix = "connections" + KeyChannelEndPrefix = "channelEnds" + KeyChannelPrefix = "channels" + KeyPortPrefix = "ports" + KeySequencePrefix = "sequences" + KeyChannelCapabilityPrefix = "capabilities" + KeyNextSeqSendPrefix = "nextSequenceSend" + KeyNextSeqRecvPrefix = "nextSequenceRecv" + KeyNextSeqAckPrefix = "nextSequenceAck" + KeyPacketCommitmentPrefix = "commitments" + KeyPacketAckPrefix = "acks" + KeyPacketReceiptPrefix = "receipts" +) + +// FullClientPath returns the full path of a specific client path in the format: +// "clients/{clientID}/{path}" as a string. +func FullClientPath(clientID string, path string) string { + return fmt.Sprintf("%s/%s/%s", KeyClientStorePrefix, clientID, path) +} + +// FullClientKey returns the full path of specific client path in the format: +// "clients/{clientID}/{path}" as a byte array. +func FullClientKey(clientID string, path []byte) []byte { + return []byte(FullClientPath(clientID, string(path))) +} + +// ICS02 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#path-space + +// FullClientStatePath takes a client identifier and returns a Path under which to store a +// particular client state +func FullClientStatePath(clientID string) string { + return FullClientPath(clientID, KeyClientState) +} + +// FullClientStateKey takes a client identifier and returns a Key under which to store a +// particular client state. +func FullClientStateKey(clientID string) []byte { + return FullClientKey(clientID, []byte(KeyClientState)) +} + +// ClientStateKey returns a store key under which a particular client state is stored +// in a client prefixed store +func ClientStateKey() []byte { + return []byte(KeyClientState) +} + +// FullConsensusStatePath takes a client identifier and returns a Path under which to +// store the consensus state of a client. +func FullConsensusStatePath(clientID string, height exported.Height) string { + return FullClientPath(clientID, ConsensusStatePath(height)) +} + +// FullConsensusStateKey returns the store key for the consensus state of a particular +// client. +func FullConsensusStateKey(clientID string, height exported.Height) []byte { + return []byte(FullConsensusStatePath(clientID, height)) +} + +// ConsensusStatePath returns the suffix store key for the consensus state at a +// particular height stored in a client prefixed store. +func ConsensusStatePath(height exported.Height) string { + return fmt.Sprintf("%s/%s", KeyConsensusStatePrefix, height) +} + +// ConsensusStateKey returns the store key for a the consensus state of a particular +// client stored in a client prefixed store. +func ConsensusStateKey(height exported.Height) []byte { + return []byte(ConsensusStatePath(height)) +} + +// ICS03 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics#store-paths + +// ClientConnectionsPath defines a reverse mapping from clients to a set of connections +func ClientConnectionsPath(clientID string) string { + return FullClientPath(clientID, KeyConnectionPrefix) +} + +// ClientConnectionsKey returns the store key for the connections of a given client +func ClientConnectionsKey(clientID string) []byte { + return []byte(ClientConnectionsPath(clientID)) +} + +// ConnectionPath defines the path under which connection paths are stored +func ConnectionPath(connectionID string) string { + return fmt.Sprintf("%s/%s", KeyConnectionPrefix, connectionID) +} + +// ConnectionKey returns the store key for a particular connection +func ConnectionKey(connectionID string) []byte { + return []byte(ConnectionPath(connectionID)) +} + +// ICS04 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics#store-paths + +// ChannelPath defines the path under which channels are stored +func ChannelPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyChannelEndPrefix, channelPath(portID, channelID)) +} + +// ChannelKey returns the store key for a particular channel +func ChannelKey(portID, channelID string) []byte { + return []byte(ChannelPath(portID, channelID)) +} + +// ChannelCapabilityPath defines the path under which capability keys associated +// with a channel are stored +func ChannelCapabilityPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyChannelCapabilityPrefix, channelPath(portID, channelID)) +} + +// NextSequenceSendPath defines the next send sequence counter store path +func NextSequenceSendPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyNextSeqSendPrefix, channelPath(portID, channelID)) +} + +// NextSequenceSendKey returns the store key for the send sequence of a particular +// channel binded to a specific port. +func NextSequenceSendKey(portID, channelID string) []byte { + return []byte(NextSequenceSendPath(portID, channelID)) +} + +// NextSequenceRecvPath defines the next receive sequence counter store path. +func NextSequenceRecvPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyNextSeqRecvPrefix, channelPath(portID, channelID)) +} + +// NextSequenceRecvKey returns the store key for the receive sequence of a particular +// channel binded to a specific port +func NextSequenceRecvKey(portID, channelID string) []byte { + return []byte(NextSequenceRecvPath(portID, channelID)) +} + +// NextSequenceAckPath defines the next acknowledgement sequence counter store path +func NextSequenceAckPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s", KeyNextSeqAckPrefix, channelPath(portID, channelID)) +} + +// NextSequenceAckKey returns the store key for the acknowledgement sequence of +// a particular channel binded to a specific port. +func NextSequenceAckKey(portID, channelID string) []byte { + return []byte(NextSequenceAckPath(portID, channelID)) +} + +// PacketCommitmentPath defines the commitments to packet data fields store path +func PacketCommitmentPath(portID, channelID string, sequence uint64) string { + return fmt.Sprintf("%s/%d", PacketCommitmentPrefixPath(portID, channelID), sequence) +} + +// PacketCommitmentKey returns the store key of under which a packet commitment +// is stored +func PacketCommitmentKey(portID, channelID string, sequence uint64) []byte { + return []byte(PacketCommitmentPath(portID, channelID, sequence)) +} + +// PacketCommitmentPrefixPath defines the prefix for commitments to packet data fields store path. +func PacketCommitmentPrefixPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s/%s", KeyPacketCommitmentPrefix, channelPath(portID, channelID), KeySequencePrefix) +} + +// PacketAcknowledgementPath defines the packet acknowledgement store path +func PacketAcknowledgementPath(portID, channelID string, sequence uint64) string { + return fmt.Sprintf("%s/%d", PacketAcknowledgementPrefixPath(portID, channelID), sequence) +} + +// PacketAcknowledgementKey returns the store key of under which a packet +// acknowledgement is stored +func PacketAcknowledgementKey(portID, channelID string, sequence uint64) []byte { + return []byte(PacketAcknowledgementPath(portID, channelID, sequence)) +} + +// PacketAcknowledgementPrefixPath defines the prefix for commitments to packet data fields store path. +func PacketAcknowledgementPrefixPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s/%s", KeyPacketAckPrefix, channelPath(portID, channelID), KeySequencePrefix) +} + +// PacketReceiptPath defines the packet receipt store path +func PacketReceiptPath(portID, channelID string, sequence uint64) string { + return fmt.Sprintf("%s/%s/%s", KeyPacketReceiptPrefix, channelPath(portID, channelID), sequencePath(sequence)) +} + +// PacketReceiptKey returns the store key of under which a packet +// receipt is stored +func PacketReceiptKey(portID, channelID string, sequence uint64) []byte { + return []byte(PacketReceiptPath(portID, channelID, sequence)) +} + +func channelPath(portID, channelID string) string { + return fmt.Sprintf("%s/%s/%s/%s", KeyPortPrefix, portID, KeyChannelPrefix, channelID) +} + +func sequencePath(sequence uint64) string { + return fmt.Sprintf("%s/%d", KeySequencePrefix, sequence) +} + +// ICS05 +// The following paths are the keys to the store as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#store-paths + +// PortPath defines the path under which ports paths are stored on the capability module +func PortPath(portID string) string { + return fmt.Sprintf("%s/%s", KeyPortPrefix, portID) +} diff --git a/x/ibc/core/24-host/parse.go b/x/ibc/core/24-host/parse.go new file mode 100644 index 000000000000..8c3459500d92 --- /dev/null +++ b/x/ibc/core/24-host/parse.go @@ -0,0 +1,79 @@ +package host + +import ( + "strconv" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// ParseIdentifier parses the sequence from the identifier using the provided prefix. This function +// does not need to be used by counterparty chains. SDK generated connection and channel identifiers +// are required to use this format. +func ParseIdentifier(identifier, prefix string) (uint64, error) { + if !strings.HasPrefix(identifier, prefix) { + return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier doesn't contain prefix `%s`", prefix) + } + + splitStr := strings.Split(identifier, prefix) + if len(splitStr) != 2 { + return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier must be in format: `%s{N}`", prefix) + } + + // sanity check + if splitStr[0] != "" { + return 0, sdkerrors.Wrapf(ErrInvalidID, "identifier must begin with prefix %s", prefix) + } + + sequence, err := strconv.ParseUint(splitStr[1], 10, 64) + if err != nil { + return 0, sdkerrors.Wrap(err, "failed to parse identifier sequence") + } + return sequence, nil +} + +// ParseConnectionPath returns the connection ID from a full path. It returns +// an error if the provided path is invalid. +func ParseConnectionPath(path string) (string, error) { + split := strings.Split(path, "/") + if len(split) != 2 { + return "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse connection path %s", path) + } + + return split[1], nil +} + +// ParseChannelPath returns the port and channel ID from a full path. It returns +// an error if the provided path is invalid. +func ParseChannelPath(path string) (string, string, error) { + split := strings.Split(path, "/") + if len(split) < 5 { + return "", "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) + } + + if split[1] != KeyPortPrefix || split[3] != KeyChannelPrefix { + return "", "", sdkerrors.Wrapf(ErrInvalidPath, "cannot parse channel path %s", path) + } + + return split[2], split[4], nil +} + +// MustParseConnectionPath returns the connection ID from a full path. Panics +// if the provided path is invalid. +func MustParseConnectionPath(path string) string { + connectionID, err := ParseConnectionPath(path) + if err != nil { + panic(err) + } + return connectionID +} + +// MustParseChannelPath returns the port and channel ID from a full path. Panics +// if the provided path is invalid. +func MustParseChannelPath(path string) (string, string) { + portID, channelID, err := ParseChannelPath(path) + if err != nil { + panic(err) + } + return portID, channelID +} diff --git a/x/ibc/core/24-host/parse_test.go b/x/ibc/core/24-host/parse_test.go new file mode 100644 index 000000000000..9f74bf5f682d --- /dev/null +++ b/x/ibc/core/24-host/parse_test.go @@ -0,0 +1,48 @@ +package host_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +func TestParseIdentifier(t *testing.T) { + testCases := []struct { + name string + identifier string + prefix string + expSeq uint64 + expPass bool + }{ + {"valid 0", "connection-0", "connection-", 0, true}, + {"valid 1", "connection-1", "connection-", 1, true}, + {"valid large sequence", connectiontypes.FormatConnectionIdentifier(math.MaxUint64), "connection-", math.MaxUint64, true}, + // one above uint64 max + {"invalid uint64", "connection-18446744073709551616", "connection-", 0, false}, + // uint64 == 20 characters + {"invalid large sequence", "connection-2345682193567182931243", "connection-", 0, false}, + {"capital prefix", "Connection-0", "connection-", 0, false}, + {"double prefix", "connection-connection-0", "connection-", 0, false}, + {"doesn't have prefix", "connection-0", "prefix", 0, false}, + {"missing dash", "connection0", "connection-", 0, false}, + {"blank id", " ", "connection-", 0, false}, + {"empty id", "", "connection-", 0, false}, + {"negative sequence", "connection--1", "connection-", 0, false}, + } + + for _, tc := range testCases { + + seq, err := host.ParseIdentifier(tc.identifier, tc.prefix) + require.Equal(t, tc.expSeq, seq) + + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/ibc/core/24-host/validate.go b/x/ibc/core/24-host/validate.go new file mode 100644 index 000000000000..10458e8d3a71 --- /dev/null +++ b/x/ibc/core/24-host/validate.go @@ -0,0 +1,114 @@ +package host + +import ( + "regexp" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// DefaultMaxCharacterLength defines the default maximum character length used +// in validation of identifiers including the client, connection, port and +// channel identifiers. +// +// NOTE: this restriction is specific to this golang implementation of IBC. If +// your use case demands a higher limit, please open an issue and we will consider +// adjusting this restriction. +const DefaultMaxCharacterLength = 64 + +// IsValidID defines regular expression to check if the string consist of +// characters in one of the following categories only: +// - Alphanumeric +// - `.`, `_`, `+`, `-`, `#` +// - `[`, `]`, `<`, `>` +var IsValidID = regexp.MustCompile(`^[a-zA-Z0-9\.\_\+\-\#\[\]\<\>]+$`).MatchString + +// ICS 024 Identifier and Path Validation Implementation +// +// This file defines ValidateFn to validate identifier and path strings +// The spec for ICS 024 can be located here: +// https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements + +// ValidateFn function type to validate path and identifier bytestrings +type ValidateFn func(string) error + +func defaultIdentifierValidator(id string, min, max int) error { //nolint:unparam + if strings.TrimSpace(id) == "" { + return sdkerrors.Wrap(ErrInvalidID, "identifier cannot be blank") + } + // valid id MUST NOT contain "/" separator + if strings.Contains(id, "/") { + return sdkerrors.Wrapf(ErrInvalidID, "identifier %s cannot contain separator '/'", id) + } + // valid id must fit the length requirements + if len(id) < min || len(id) > max { + return sdkerrors.Wrapf(ErrInvalidID, "identifier %s has invalid length: %d, must be between %d-%d characters", id, len(id), min, max) + } + // valid id must contain only lower alphabetic characters + if !IsValidID(id) { + return sdkerrors.Wrapf( + ErrInvalidID, + "identifier %s must contain only alphanumeric or the following characters: '.', '_', '+', '-', '#', '[', ']', '<', '>'", + id, + ) + } + return nil +} + +// ClientIdentifierValidator is the default validator function for Client identifiers. +// A valid Identifier must be between 9-64 characters and only contain alphanumeric and some allowed +// special characters (see IsValidID). +func ClientIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 9, DefaultMaxCharacterLength) +} + +// ConnectionIdentifierValidator is the default validator function for Connection identifiers. +// A valid Identifier must be between 10-64 characters and only contain alphanumeric and some allowed +// special characters (see IsValidID). +func ConnectionIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 10, DefaultMaxCharacterLength) +} + +// ChannelIdentifierValidator is the default validator function for Channel identifiers. +// A valid Identifier must be between 8-64 characters and only contain alphanumeric and some allowed +// special characters (see IsValidID). +func ChannelIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 8, DefaultMaxCharacterLength) +} + +// PortIdentifierValidator is the default validator function for Port identifiers. +// A valid Identifier must be between 2-64 characters and only contain alphanumeric and some allowed +// special characters (see IsValidID). +func PortIdentifierValidator(id string) error { + return defaultIdentifierValidator(id, 2, DefaultMaxCharacterLength) +} + +// NewPathValidator takes in a Identifier Validator function and returns +// a Path Validator function which requires path to consist of `/`-separated valid identifiers, +// where a valid identifier is between 1-64 characters, contains only alphanumeric and some allowed +// special characters (see IsValidID), and satisfies the custom `idValidator` function. +func NewPathValidator(idValidator ValidateFn) ValidateFn { + return func(path string) error { + pathArr := strings.Split(path, "/") + if len(pathArr) > 0 && pathArr[0] == path { + return sdkerrors.Wrapf(ErrInvalidPath, "path %s doesn't contain any separator '/'", path) + } + + for _, p := range pathArr { + // a path beginning or ending in a separator returns empty string elements. + if p == "" { + return sdkerrors.Wrapf(ErrInvalidPath, "path %s cannot begin or end with '/'", path) + } + + if err := idValidator(p); err != nil { + return err + } + // Each path element must either be a valid identifier or constant number + if err := defaultIdentifierValidator(p, 1, DefaultMaxCharacterLength); err != nil { + return sdkerrors.Wrapf(err, "path %s contains an invalid identifier: '%s'", path, p) + } + } + + return nil + } +} diff --git a/x/ibc/core/24-host/validate_test.go b/x/ibc/core/24-host/validate_test.go new file mode 100644 index 000000000000..40987bd15714 --- /dev/null +++ b/x/ibc/core/24-host/validate_test.go @@ -0,0 +1,119 @@ +package host + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +type testCase struct { + msg string + id string + expPass bool +} + +func TestDefaultIdentifierValidator(t *testing.T) { + testCases := []testCase{ + {"valid lowercase", "lowercaseid", true}, + {"valid id special chars", "._+-#[]<>._+-#[]<>", true}, + {"valid id lower and special chars", "lower._+-#[]<>", true}, + {"numeric id", "1234567890", true}, + {"uppercase id", "NOTLOWERCASE", true}, + {"numeric id", "1234567890", true}, + {"blank id", " ", false}, + {"id length out of range", "1", false}, + {"id is too long", "this identifier is too long to be used as a valid identifier", false}, + {"path-like id", "lower/case/id", false}, + {"invalid id", "(clientid)", false}, + {"empty string", "", false}, + } + + for _, tc := range testCases { + + err := ClientIdentifierValidator(tc.id) + err1 := ConnectionIdentifierValidator(tc.id) + err2 := ChannelIdentifierValidator(tc.id) + err3 := PortIdentifierValidator(tc.id) + if tc.expPass { + require.NoError(t, err, tc.msg) + require.NoError(t, err1, tc.msg) + require.NoError(t, err2, tc.msg) + require.NoError(t, err3, tc.msg) + } else { + require.Error(t, err, tc.msg) + require.Error(t, err1, tc.msg) + require.Error(t, err2, tc.msg) + require.Error(t, err3, tc.msg) + } + } +} + +func TestPathValidator(t *testing.T) { + testCases := []testCase{ + {"valid lowercase", "p/lowercaseid", true}, + {"numeric path", "p/239123", true}, + {"valid id special chars", "p/._+-#[]<>._+-#[]<>", true}, + {"valid id lower and special chars", "lower/._+-#[]<>", true}, + {"id length out of range", "p/l", true}, + {"uppercase id", "p/NOTLOWERCASE", true}, + {"invalid path", "lowercaseid", false}, + {"blank id", "p/ ", false}, + {"id length out of range", "p/12345678901234567890123456789012345678901234567890123456789012345", false}, + {"invalid id", "p/(clientid)", false}, + {"empty string", "", false}, + {"separators only", "////", false}, + {"just separator", "/", false}, + {"begins with separator", "/id", false}, + {"blank before separator", " /id", false}, + {"ends with separator", "id/", false}, + {"blank after separator", "id/ ", false}, + {"blanks with separator", " / ", false}, + } + + for _, tc := range testCases { + f := NewPathValidator(func(path string) error { + return nil + }) + + err := f(tc.id) + + if tc.expPass { + seps := strings.Count(tc.id, "/") + require.Equal(t, 1, seps) + require.NoError(t, err, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +} + +func TestCustomPathValidator(t *testing.T) { + validateFn := NewPathValidator(func(path string) error { + if !strings.HasPrefix(path, "id_") { + return fmt.Errorf("identifier %s must start with 'id_", path) + } + return nil + }) + + testCases := []testCase{ + {"valid custom path", "id_client/id_one", true}, + {"invalid path", "client", false}, + {"invalid custom path", "id_one/client", false}, + {"invalid identifier", "id_client/id_1234567890123456789012345678901234567890123457890123456789012345", false}, + {"separators only", "////", false}, + {"just separator", "/", false}, + {"ends with separator", "id_client/id_one/", false}, + {"beings with separator", "/id_client/id_one", false}, + } + + for _, tc := range testCases { + err := validateFn(tc.id) + if tc.expPass { + require.NoError(t, err, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +} diff --git a/x/ibc/core/client/cli/cli.go b/x/ibc/core/client/cli/cli.go new file mode 100644 index 000000000000..51bb063a6a0b --- /dev/null +++ b/x/ibc/core/client/cli/cli.go @@ -0,0 +1,53 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + 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 +func GetTxCmd() *cobra.Command { + ibcTxCmd := &cobra.Command{ + Use: host.ModuleName, + Short: "IBC transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + ibcTxCmd.AddCommand( + solomachine.GetTxCmd(), + tendermint.GetTxCmd(), + connection.GetTxCmd(), + channel.GetTxCmd(), + ) + + return ibcTxCmd +} + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + // Group ibc queries under a subcommand + ibcQueryCmd := &cobra.Command{ + Use: host.ModuleName, + Short: "Querying commands for the IBC module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + ibcQueryCmd.AddCommand( + ibcclient.GetQueryCmd(), + connection.GetQueryCmd(), + channel.GetQueryCmd(), + ) + + return ibcQueryCmd +} diff --git a/x/ibc/core/client/query.go b/x/ibc/core/client/query.go new file mode 100644 index 000000000000..7055f1c740bf --- /dev/null +++ b/x/ibc/core/client/query.go @@ -0,0 +1,67 @@ +package client + +import ( + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +// QueryTendermintProof performs an ABCI query with the given key and returns +// the value of the query, the proto encoded merkle proof, and the height of +// the Tendermint block containing the state root. The desired tendermint height +// to perform the query should be set in the client context. The query will be +// performed at one below this height (at the IAVL version) in order to obtain +// the correct merkle proof. Proof queries at height less than or equal to 2 are +// not supported. Queries with a client context height of 0 will perform a query +// at the lastest state available. +// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567 +func QueryTendermintProof(clientCtx client.Context, key []byte) ([]byte, []byte, clienttypes.Height, error) { + height := clientCtx.Height + + // ABCI queries at heights 1, 2 or less than or equal to 0 are not supported. + // Base app does not support queries for height less than or equal to 1. + // Therefore, a query at height 2 would be equivalent to a query at height 3. + // A height of 0 will query with the lastest state. + if height != 0 && height <= 2 { + return nil, nil, clienttypes.Height{}, fmt.Errorf("proof queries at height <= 2 are not supported") + } + + // Use the IAVL height if a valid tendermint height is passed in. + // A height of 0 will query with the latest state. + if height != 0 { + height-- + } + + req := abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", host.StoreKey), + Height: height, + Data: key, + Prove: true, + } + + res, err := clientCtx.QueryABCI(req) + if err != nil { + return nil, nil, clienttypes.Height{}, err + } + + merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) + if err != nil { + return nil, nil, clienttypes.Height{}, err + } + + cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) + + proofBz, err := cdc.MarshalBinaryBare(&merkleProof) + if err != nil { + return nil, nil, clienttypes.Height{}, err + } + + revision := clienttypes.ParseChainID(clientCtx.ChainID) + return res.Value, proofBz, clienttypes.NewHeight(revision, uint64(res.Height)+1), nil +} diff --git a/x/ibc/core/exported/channel.go b/x/ibc/core/exported/channel.go new file mode 100644 index 000000000000..6a0d542c1eea --- /dev/null +++ b/x/ibc/core/exported/channel.go @@ -0,0 +1,32 @@ +package exported + +// ChannelI defines the standard interface for a channel end. +type ChannelI interface { + GetState() int32 + GetOrdering() int32 + GetCounterparty() CounterpartyChannelI + GetConnectionHops() []string + GetVersion() string + ValidateBasic() error +} + +// CounterpartyChannelI defines the standard interface for a channel end's +// counterparty. +type CounterpartyChannelI interface { + GetPortID() string + GetChannelID() string + ValidateBasic() error +} + +// PacketI defines the standard interface for IBC packets +type PacketI interface { + GetSequence() uint64 + GetTimeoutHeight() Height + GetTimeoutTimestamp() uint64 + GetSourcePort() string + GetSourceChannel() string + GetDestPort() string + GetDestChannel() string + GetData() []byte + ValidateBasic() error +} diff --git a/x/ibc/core/exported/client.go b/x/ibc/core/exported/client.go new file mode 100644 index 000000000000..656a233b3a9c --- /dev/null +++ b/x/ibc/core/exported/client.go @@ -0,0 +1,223 @@ +package exported + +import ( + ics23 "github.com/confio/ics23/go" + proto "github.com/gogo/protobuf/proto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // TypeClientMisbehaviour is the shared evidence misbehaviour type + TypeClientMisbehaviour string = "client_misbehaviour" + + // Solomachine is used to indicate that the light client is a solo machine. + Solomachine string = "06-solomachine" + + // Tendermint is used to indicate that the client uses the Tendermint Consensus Algorithm. + Tendermint string = "07-tendermint" + + // Localhost is the client type for a localhost client. It is also used as the clientID + // for the localhost client. + Localhost string = "09-localhost" +) + +// ClientState defines the required common functions for light clients. +type ClientState interface { + proto.Message + + ClientType() string + GetLatestHeight() Height + IsFrozen() bool + GetFrozenHeight() Height + Validate() error + GetProofSpecs() []*ics23.ProofSpec + + // Initialization function + // Clients must validate the initial consensus state, and may store any client-specific metadata + // necessary for correct light client operation + Initialize(sdk.Context, codec.BinaryMarshaler, sdk.KVStore, ConsensusState) error + + // Genesis function + ExportMetadata(sdk.KVStore) []GenesisMetadata + + // Update and Misbehaviour functions + + 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) + + // 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, + ) (ClientState, ConsensusState, error) + // 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 + + // State verification functions + + VerifyClientState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + prefix Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState ClientState, + ) error + VerifyClientConsensusState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + counterpartyClientIdentifier string, + consensusHeight Height, + prefix Prefix, + proof []byte, + consensusState ConsensusState, + ) error + VerifyConnectionState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + prefix Prefix, + proof []byte, + connectionID string, + connectionEnd ConnectionI, + ) error + VerifyChannelState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + prefix Prefix, + proof []byte, + portID, + channelID string, + channel ChannelI, + ) error + VerifyPacketCommitment( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, + ) error + VerifyPacketAcknowledgement( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, + ) error + VerifyPacketReceiptAbsence( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + ) error + VerifyNextSequenceRecv( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, + ) error +} + +// ConsensusState is the state of the consensus process +type ConsensusState interface { + proto.Message + + ClientType() string // Consensus kind + + // GetRoot returns the commitment root of the consensus state, + // which is used for key-value pair verification. + GetRoot() Root + + // GetTimestamp returns the timestamp (in nanoseconds) of the consensus state + GetTimestamp() uint64 + + ValidateBasic() error +} + +// Misbehaviour defines counterparty misbehaviour for a specific consensus type +type Misbehaviour interface { + proto.Message + + ClientType() string + GetClientID() string + ValidateBasic() error + + // Height at which the infraction occurred + GetHeight() Height +} + +// Header is the consensus state update information +type Header interface { + proto.Message + + ClientType() string + GetHeight() Height + ValidateBasic() error +} + +// Height is a wrapper interface over clienttypes.Height +// all clients must use the concrete implementation in types +type Height interface { + IsZero() bool + LT(Height) bool + LTE(Height) bool + EQ(Height) bool + GT(Height) bool + GTE(Height) bool + GetRevisionNumber() uint64 + GetRevisionHeight() uint64 + Increment() Height + Decrement() (Height, bool) + String() string +} + +// GenesisMetadata is a wrapper interface over clienttypes.GenesisMetadata +// all clients must use the concrete implementation in types +type GenesisMetadata interface { + // return store key that contains metadata without clientID-prefix + GetKey() []byte + // returns metadata value + GetValue() []byte +} diff --git a/x/ibc/core/exported/commitment.go b/x/ibc/core/exported/commitment.go new file mode 100644 index 000000000000..b4f2c0c18f59 --- /dev/null +++ b/x/ibc/core/exported/commitment.go @@ -0,0 +1,45 @@ +package exported + +import ics23 "github.com/confio/ics23/go" + +// ICS 023 Types Implementation +// +// This file includes types defined under +// https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments + +// spec:Path and spec:Value are defined as bytestring + +// Root implements spec:CommitmentRoot. +// A root is constructed from a set of key-value pairs, +// and the inclusion or non-inclusion of an arbitrary key-value pair +// can be proven with the proof. +type Root interface { + GetHash() []byte + Empty() bool +} + +// Prefix implements spec:CommitmentPrefix. +// Prefix represents the common "prefix" that a set of keys shares. +type Prefix interface { + Bytes() []byte + Empty() bool +} + +// Path implements spec:CommitmentPath. +// A path is the additional information provided to the verification function. +type Path interface { + String() string + Empty() bool +} + +// Proof implements spec:CommitmentProof. +// Proof can prove whether the key-value pair is a part of the Root or not. +// Each proof has designated key-value pair it is able to prove. +// Proofs includes key but value is provided dynamically at the verification time. +type Proof interface { + VerifyMembership([]*ics23.ProofSpec, Root, Path, []byte) error + VerifyNonMembership([]*ics23.ProofSpec, Root, Path) error + Empty() bool + + ValidateBasic() error +} diff --git a/x/ibc/core/exported/connection.go b/x/ibc/core/exported/connection.go new file mode 100644 index 000000000000..8f705daff1ad --- /dev/null +++ b/x/ibc/core/exported/connection.go @@ -0,0 +1,26 @@ +package exported + +// ConnectionI describes the required methods for a connection. +type ConnectionI interface { + GetClientID() string + GetState() int32 + GetCounterparty() CounterpartyConnectionI + GetVersions() []Version + GetDelayPeriod() uint64 + ValidateBasic() error +} + +// CounterpartyConnectionI describes the required methods for a counterparty connection. +type CounterpartyConnectionI interface { + GetClientID() string + GetConnectionID() string + GetPrefix() Prefix + ValidateBasic() error +} + +// Version defines an IBC version used in connection handshake negotiation. +type Version interface { + GetIdentifier() string + GetFeatures() []string + VerifyProposedVersion(Version) error +} diff --git a/x/ibc/core/genesis.go b/x/ibc/core/genesis.go new file mode 100644 index 000000000000..7d5d60b93453 --- /dev/null +++ b/x/ibc/core/genesis.go @@ -0,0 +1,27 @@ +package ibc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + client "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + connection "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection" + channel "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel" + "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" +) + +// InitGenesis initializes the ibc state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k keeper.Keeper, createLocalhost bool, gs *types.GenesisState) { + client.InitGenesis(ctx, k.ClientKeeper, gs.ClientGenesis) + connection.InitGenesis(ctx, k.ConnectionKeeper, gs.ConnectionGenesis) + channel.InitGenesis(ctx, k.ChannelKeeper, gs.ChannelGenesis) +} + +// ExportGenesis returns the ibc exported genesis. +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + return &types.GenesisState{ + ClientGenesis: client.ExportGenesis(ctx, k.ClientKeeper), + ConnectionGenesis: connection.ExportGenesis(ctx, k.ConnectionKeeper), + ChannelGenesis: channel.ExportGenesis(ctx, k.ChannelKeeper), + } +} diff --git a/x/ibc/core/genesis_test.go b/x/ibc/core/genesis_test.go new file mode 100644 index 000000000000..c29feef7f8fd --- /dev/null +++ b/x/ibc/core/genesis_test.go @@ -0,0 +1,370 @@ +package ibc_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + ibc "github.com/cosmos/cosmos-sdk/x/ibc/core" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +const ( + connectionID = "connection-0" + clientID = "07-tendermint-0" + connectionID2 = "connection-1" + clientID2 = "07-tendermin-1" + localhostID = exported.Localhost + "-1" + + port1 = "firstport" + port2 = "secondport" + + channel1 = "channel-0" + channel2 = "channel-1" +) + +var clientHeight = clienttypes.NewHeight(0, 10) + +type IBCTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *IBCTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) +} + +func TestIBCTestSuite(t *testing.T) { + suite.Run(t, new(IBCTestSuite)) +} + +func (suite *IBCTestSuite) TestValidateGenesis() { + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + + testCases := []struct { + name string + genState *types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + name: "valid genesis", + genState: &types.GenesisState{ + ClientGenesis: clienttypes.NewGenesisState( + []clienttypes.IdentifiedClientState{ + clienttypes.NewIdentifiedClientState( + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + clienttypes.NewIdentifiedClientState( + localhostID, localhosttypes.NewClientState("chaindID", clientHeight), + ), + }, + []clienttypes.ClientConsensusStates{ + clienttypes.NewClientConsensusStates( + clientID, + []clienttypes.ConsensusStateWithHeight{ + clienttypes.NewConsensusStateWithHeight( + header.GetHeight().(clienttypes.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + []clienttypes.IdentifiedGenesisMetadata{ + clienttypes.NewIdentifiedGenesisMetadata( + clientID, + []clienttypes.GenesisMetadata{ + clienttypes.NewGenesisMetadata([]byte("key1"), []byte("val1")), + clienttypes.NewGenesisMetadata([]byte("key2"), []byte("val2")), + }, + ), + }, + clienttypes.NewParams(exported.Tendermint, exported.Localhost), + true, + 2, + ), + ConnectionGenesis: connectiontypes.NewGenesisState( + []connectiontypes.IdentifiedConnection{ + connectiontypes.NewIdentifiedConnection(connectionID, connectiontypes.NewConnectionEnd(connectiontypes.INIT, clientID, connectiontypes.NewCounterparty(clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []*connectiontypes.Version{ibctesting.ConnectionVersion}, 0)), + }, + []connectiontypes.ConnectionPaths{ + connectiontypes.NewConnectionPaths(clientID, []string{connectionID}), + }, + 0, + ), + ChannelGenesis: channeltypes.NewGenesisState( + []channeltypes.IdentifiedChannel{ + channeltypes.NewIdentifiedChannel( + port1, channel1, channeltypes.NewChannel( + channeltypes.INIT, channeltypes.ORDERED, + channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, ibctesting.DefaultChannelVersion, + ), + ), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port2, channel2, 1, []byte("ack")), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port2, channel2, 1, []byte("")), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port1, channel1, 1, []byte("commit_hash")), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port1, channel1, 1), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port2, channel2, 1), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port2, channel2, 1), + }, + 0, + ), + }, + expPass: true, + }, + { + name: "invalid client genesis", + genState: &types.GenesisState{ + ClientGenesis: clienttypes.NewGenesisState( + []clienttypes.IdentifiedClientState{ + clienttypes.NewIdentifiedClientState( + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + clienttypes.NewIdentifiedClientState( + localhostID, localhosttypes.NewClientState("(chaindID)", clienttypes.ZeroHeight()), + ), + }, + nil, + []clienttypes.IdentifiedGenesisMetadata{ + clienttypes.NewIdentifiedGenesisMetadata( + clientID, + []clienttypes.GenesisMetadata{ + clienttypes.NewGenesisMetadata([]byte(""), []byte("val1")), + clienttypes.NewGenesisMetadata([]byte("key2"), []byte("")), + }, + ), + }, + clienttypes.NewParams(exported.Tendermint), + false, + 2, + ), + ConnectionGenesis: connectiontypes.DefaultGenesisState(), + }, + expPass: false, + }, + { + name: "invalid connection genesis", + genState: &types.GenesisState{ + ClientGenesis: clienttypes.DefaultGenesisState(), + ConnectionGenesis: connectiontypes.NewGenesisState( + []connectiontypes.IdentifiedConnection{ + connectiontypes.NewIdentifiedConnection(connectionID, connectiontypes.NewConnectionEnd(connectiontypes.INIT, "(CLIENTIDONE)", connectiontypes.NewCounterparty(clientID, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []*connectiontypes.Version{connectiontypes.NewVersion("1.1", nil)}, 0)), + }, + []connectiontypes.ConnectionPaths{ + connectiontypes.NewConnectionPaths(clientID, []string{connectionID}), + }, + 0, + ), + }, + expPass: false, + }, + { + name: "invalid channel genesis", + genState: &types.GenesisState{ + ClientGenesis: clienttypes.DefaultGenesisState(), + ConnectionGenesis: connectiontypes.DefaultGenesisState(), + ChannelGenesis: channeltypes.GenesisState{ + Acknowledgements: []channeltypes.PacketState{ + channeltypes.NewPacketState("(portID)", channel1, 1, []byte("ack")), + }, + }, + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *IBCTestSuite) TestInitGenesis() { + header := suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, suite.chainA.CurrentHeader.Height, clienttypes.NewHeight(0, uint64(suite.chainA.CurrentHeader.Height-1)), suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.Vals, suite.chainA.Signers) + + testCases := []struct { + name string + genState *types.GenesisState + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + }, + { + name: "valid genesis", + genState: &types.GenesisState{ + ClientGenesis: clienttypes.NewGenesisState( + []clienttypes.IdentifiedClientState{ + clienttypes.NewIdentifiedClientState( + clientID, ibctmtypes.NewClientState(suite.chainA.ChainID, ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod, ibctesting.MaxClockDrift, clientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), + ), + clienttypes.NewIdentifiedClientState( + exported.Localhost, localhosttypes.NewClientState("chaindID", clientHeight), + ), + }, + []clienttypes.ClientConsensusStates{ + clienttypes.NewClientConsensusStates( + clientID, + []clienttypes.ConsensusStateWithHeight{ + clienttypes.NewConsensusStateWithHeight( + header.GetHeight().(clienttypes.Height), + ibctmtypes.NewConsensusState( + header.GetTime(), commitmenttypes.NewMerkleRoot(header.Header.AppHash), header.Header.NextValidatorsHash, + ), + ), + }, + ), + }, + []clienttypes.IdentifiedGenesisMetadata{ + clienttypes.NewIdentifiedGenesisMetadata( + clientID, + []clienttypes.GenesisMetadata{ + clienttypes.NewGenesisMetadata([]byte("key1"), []byte("val1")), + clienttypes.NewGenesisMetadata([]byte("key2"), []byte("val2")), + }, + ), + }, + clienttypes.NewParams(exported.Tendermint, exported.Localhost), + true, + 0, + ), + ConnectionGenesis: connectiontypes.NewGenesisState( + []connectiontypes.IdentifiedConnection{ + connectiontypes.NewIdentifiedConnection(connectionID, connectiontypes.NewConnectionEnd(connectiontypes.INIT, clientID, connectiontypes.NewCounterparty(clientID2, connectionID2, commitmenttypes.NewMerklePrefix([]byte("prefix"))), []*connectiontypes.Version{ibctesting.ConnectionVersion}, 0)), + }, + []connectiontypes.ConnectionPaths{ + connectiontypes.NewConnectionPaths(clientID, []string{connectionID}), + }, + 0, + ), + ChannelGenesis: channeltypes.NewGenesisState( + []channeltypes.IdentifiedChannel{ + channeltypes.NewIdentifiedChannel( + port1, channel1, channeltypes.NewChannel( + channeltypes.INIT, channeltypes.ORDERED, + channeltypes.NewCounterparty(port2, channel2), []string{connectionID}, ibctesting.DefaultChannelVersion, + ), + ), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port2, channel2, 1, []byte("ack")), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port2, channel2, 1, []byte("")), + }, + []channeltypes.PacketState{ + channeltypes.NewPacketState(port1, channel1, 1, []byte("commit_hash")), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port1, channel1, 1), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port2, channel2, 1), + }, + []channeltypes.PacketSequence{ + channeltypes.NewPacketSequence(port2, channel2, 1), + }, + 0, + ), + }, + }, + } + + for _, tc := range testCases { + app := simapp.Setup(false) + + suite.NotPanics(func() { + ibc.InitGenesis(app.BaseApp.NewContext(false, tmproto.Header{Height: 1}), *app.IBCKeeper, true, tc.genState) + }) + } +} + +func (suite *IBCTestSuite) TestExportGenesis() { + testCases := []struct { + msg string + malleate func() + }{ + { + "success", + func() { + // creates clients + suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + // create extra clients + suite.coordinator.CreateClient(suite.chainA, suite.chainB, exported.Tendermint) + suite.coordinator.CreateClient(suite.chainA, suite.chainB, exported.Tendermint) + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() + + var gs *types.GenesisState + suite.NotPanics(func() { + gs = ibc.ExportGenesis(suite.chainA.GetContext(), *suite.chainA.App.IBCKeeper) + }) + + // init genesis based on export + suite.NotPanics(func() { + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.IBCKeeper, true, gs) + }) + + suite.NotPanics(func() { + cdc := codec.NewProtoCodec(suite.chainA.App.InterfaceRegistry()) + genState := cdc.MustMarshalJSON(gs) + cdc.MustUnmarshalJSON(genState, gs) + }) + + // init genesis based on marshal and unmarshal + suite.NotPanics(func() { + ibc.InitGenesis(suite.chainA.GetContext(), *suite.chainA.App.IBCKeeper, true, gs) + }) + }) + } +} diff --git a/x/ibc/core/handler.go b/x/ibc/core/handler.go new file mode 100644 index 000000000000..c8e4dfc89848 --- /dev/null +++ b/x/ibc/core/handler.go @@ -0,0 +1,98 @@ +package ibc + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" +) + +// NewHandler defines the IBC handler +func NewHandler(k keeper.Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + // IBC client msg interface types + case *clienttypes.MsgCreateClient: + res, err := k.CreateClient(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *clienttypes.MsgUpdateClient: + res, err := k.UpdateClient(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *clienttypes.MsgUpgradeClient: + res, err := k.UpgradeClient(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *clienttypes.MsgSubmitMisbehaviour: + res, err := k.SubmitMisbehaviour(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + // IBC connection msgs + case *connectiontypes.MsgConnectionOpenInit: + res, err := k.ConnectionOpenInit(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *connectiontypes.MsgConnectionOpenTry: + res, err := k.ConnectionOpenTry(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *connectiontypes.MsgConnectionOpenAck: + res, err := k.ConnectionOpenAck(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *connectiontypes.MsgConnectionOpenConfirm: + res, err := k.ConnectionOpenConfirm(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + // IBC channel msgs + case *channeltypes.MsgChannelOpenInit: + res, err := k.ChannelOpenInit(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgChannelOpenTry: + res, err := k.ChannelOpenTry(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgChannelOpenAck: + res, err := k.ChannelOpenAck(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgChannelOpenConfirm: + res, err := k.ChannelOpenConfirm(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgChannelCloseInit: + res, err := k.ChannelCloseInit(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgChannelCloseConfirm: + res, err := k.ChannelCloseConfirm(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + // IBC packet msgs get routed to the appropriate module callback + case *channeltypes.MsgRecvPacket: + res, err := k.RecvPacket(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgAcknowledgement: + res, err := k.Acknowledgement(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgTimeout: + res, err := k.Timeout(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + case *channeltypes.MsgTimeoutOnClose: + res, err := k.TimeoutOnClose(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized IBC message type: %T", msg) + } + } +} diff --git a/x/ibc/core/keeper/grpc_query.go b/x/ibc/core/keeper/grpc_query.go new file mode 100644 index 000000000000..f406d2e86f00 --- /dev/null +++ b/x/ibc/core/keeper/grpc_query.go @@ -0,0 +1,124 @@ +package keeper + +import ( + "context" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// ClientState implements the IBC QueryServer interface +func (q Keeper) ClientState(c context.Context, req *clienttypes.QueryClientStateRequest) (*clienttypes.QueryClientStateResponse, error) { + return q.ClientKeeper.ClientState(c, req) +} + +// ClientStates implements the IBC QueryServer interface +func (q Keeper) ClientStates(c context.Context, req *clienttypes.QueryClientStatesRequest) (*clienttypes.QueryClientStatesResponse, error) { + return q.ClientKeeper.ClientStates(c, req) +} + +// ConsensusState implements the IBC QueryServer interface +func (q Keeper) ConsensusState(c context.Context, req *clienttypes.QueryConsensusStateRequest) (*clienttypes.QueryConsensusStateResponse, error) { + return q.ClientKeeper.ConsensusState(c, req) +} + +// ConsensusStates implements the IBC QueryServer interface +func (q Keeper) ConsensusStates(c context.Context, req *clienttypes.QueryConsensusStatesRequest) (*clienttypes.QueryConsensusStatesResponse, error) { + return q.ClientKeeper.ConsensusStates(c, req) +} + +// ClientParams implements the IBC QueryServer interface +func (q Keeper) ClientParams(c context.Context, req *clienttypes.QueryClientParamsRequest) (*clienttypes.QueryClientParamsResponse, error) { + return q.ClientKeeper.ClientParams(c, req) +} + +// Connection implements the IBC QueryServer interface +func (q Keeper) Connection(c context.Context, req *connectiontypes.QueryConnectionRequest) (*connectiontypes.QueryConnectionResponse, error) { + return q.ConnectionKeeper.Connection(c, req) +} + +// Connections implements the IBC QueryServer interface +func (q Keeper) Connections(c context.Context, req *connectiontypes.QueryConnectionsRequest) (*connectiontypes.QueryConnectionsResponse, error) { + return q.ConnectionKeeper.Connections(c, req) +} + +// ClientConnections implements the IBC QueryServer interface +func (q Keeper) ClientConnections(c context.Context, req *connectiontypes.QueryClientConnectionsRequest) (*connectiontypes.QueryClientConnectionsResponse, error) { + return q.ConnectionKeeper.ClientConnections(c, req) +} + +// ConnectionClientState implements the IBC QueryServer interface +func (q Keeper) ConnectionClientState(c context.Context, req *connectiontypes.QueryConnectionClientStateRequest) (*connectiontypes.QueryConnectionClientStateResponse, error) { + return q.ConnectionKeeper.ConnectionClientState(c, req) +} + +// ConnectionConsensusState implements the IBC QueryServer interface +func (q Keeper) ConnectionConsensusState(c context.Context, req *connectiontypes.QueryConnectionConsensusStateRequest) (*connectiontypes.QueryConnectionConsensusStateResponse, error) { + return q.ConnectionKeeper.ConnectionConsensusState(c, req) +} + +// Channel implements the IBC QueryServer interface +func (q Keeper) Channel(c context.Context, req *channeltypes.QueryChannelRequest) (*channeltypes.QueryChannelResponse, error) { + return q.ChannelKeeper.Channel(c, req) +} + +// Channels implements the IBC QueryServer interface +func (q Keeper) Channels(c context.Context, req *channeltypes.QueryChannelsRequest) (*channeltypes.QueryChannelsResponse, error) { + return q.ChannelKeeper.Channels(c, req) +} + +// ConnectionChannels implements the IBC QueryServer interface +func (q Keeper) ConnectionChannels(c context.Context, req *channeltypes.QueryConnectionChannelsRequest) (*channeltypes.QueryConnectionChannelsResponse, error) { + return q.ChannelKeeper.ConnectionChannels(c, req) +} + +// ChannelClientState implements the IBC QueryServer interface +func (q Keeper) ChannelClientState(c context.Context, req *channeltypes.QueryChannelClientStateRequest) (*channeltypes.QueryChannelClientStateResponse, error) { + return q.ChannelKeeper.ChannelClientState(c, req) +} + +// ChannelConsensusState implements the IBC QueryServer interface +func (q Keeper) ChannelConsensusState(c context.Context, req *channeltypes.QueryChannelConsensusStateRequest) (*channeltypes.QueryChannelConsensusStateResponse, error) { + return q.ChannelKeeper.ChannelConsensusState(c, req) +} + +// PacketCommitment implements the IBC QueryServer interface +func (q Keeper) PacketCommitment(c context.Context, req *channeltypes.QueryPacketCommitmentRequest) (*channeltypes.QueryPacketCommitmentResponse, error) { + return q.ChannelKeeper.PacketCommitment(c, req) +} + +// PacketCommitments implements the IBC QueryServer interface +func (q Keeper) PacketCommitments(c context.Context, req *channeltypes.QueryPacketCommitmentsRequest) (*channeltypes.QueryPacketCommitmentsResponse, error) { + return q.ChannelKeeper.PacketCommitments(c, req) +} + +// PacketReceipt implements the IBC QueryServer interface +func (q Keeper) PacketReceipt(c context.Context, req *channeltypes.QueryPacketReceiptRequest) (*channeltypes.QueryPacketReceiptResponse, error) { + return q.ChannelKeeper.PacketReceipt(c, req) +} + +// PacketAcknowledgement implements the IBC QueryServer interface +func (q Keeper) PacketAcknowledgement(c context.Context, req *channeltypes.QueryPacketAcknowledgementRequest) (*channeltypes.QueryPacketAcknowledgementResponse, error) { + return q.ChannelKeeper.PacketAcknowledgement(c, req) +} + +// PacketAcknowledgements implements the IBC QueryServer interface +func (q Keeper) PacketAcknowledgements(c context.Context, req *channeltypes.QueryPacketAcknowledgementsRequest) (*channeltypes.QueryPacketAcknowledgementsResponse, error) { + return q.ChannelKeeper.PacketAcknowledgements(c, req) +} + +// UnreceivedPackets implements the IBC QueryServer interface +func (q Keeper) UnreceivedPackets(c context.Context, req *channeltypes.QueryUnreceivedPacketsRequest) (*channeltypes.QueryUnreceivedPacketsResponse, error) { + return q.ChannelKeeper.UnreceivedPackets(c, req) +} + +// UnreceivedAcks implements the IBC QueryServer interface +func (q Keeper) UnreceivedAcks(c context.Context, req *channeltypes.QueryUnreceivedAcksRequest) (*channeltypes.QueryUnreceivedAcksResponse, error) { + return q.ChannelKeeper.UnreceivedAcks(c, req) +} + +// NextSequenceReceive implements the IBC QueryServer interface +func (q Keeper) NextSequenceReceive(c context.Context, req *channeltypes.QueryNextSequenceReceiveRequest) (*channeltypes.QueryNextSequenceReceiveResponse, error) { + return q.ChannelKeeper.NextSequenceReceive(c, req) +} diff --git a/x/ibc/core/keeper/keeper.go b/x/ibc/core/keeper/keeper.go new file mode 100644 index 000000000000..5f9abc382ecb --- /dev/null +++ b/x/ibc/core/keeper/keeper.go @@ -0,0 +1,65 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + clientkeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/keeper" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectionkeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/keeper" + channelkeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/keeper" + portkeeper "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/keeper" + porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var _ types.QueryServer = (*Keeper)(nil) + +// Keeper defines each ICS keeper for IBC +type Keeper struct { + // implements gRPC QueryServer interface + types.QueryServer + + cdc codec.BinaryMarshaler + + ClientKeeper clientkeeper.Keeper + ConnectionKeeper connectionkeeper.Keeper + ChannelKeeper channelkeeper.Keeper + PortKeeper portkeeper.Keeper + Router *porttypes.Router +} + +// NewKeeper creates a new ibc Keeper +func NewKeeper( + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace paramtypes.Subspace, + stakingKeeper clienttypes.StakingKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, +) *Keeper { + clientKeeper := clientkeeper.NewKeeper(cdc, key, paramSpace, stakingKeeper) + connectionKeeper := connectionkeeper.NewKeeper(cdc, key, clientKeeper) + portKeeper := portkeeper.NewKeeper(scopedKeeper) + channelKeeper := channelkeeper.NewKeeper(cdc, key, clientKeeper, connectionKeeper, portKeeper, scopedKeeper) + + return &Keeper{ + cdc: cdc, + ClientKeeper: clientKeeper, + ConnectionKeeper: connectionKeeper, + ChannelKeeper: channelKeeper, + PortKeeper: portKeeper, + } +} + +// Codec returns the IBC module codec. +func (k Keeper) Codec() codec.BinaryMarshaler { + return k.cdc +} + +// SetRouter sets the Router in IBC Keeper and seals it. The method panics if +// there is an existing router that's already sealed. +func (k *Keeper) SetRouter(rtr *porttypes.Router) { + if k.Router != nil && k.Router.Sealed() { + panic("cannot reset a sealed router") + } + k.Router = rtr + k.Router.Seal() +} diff --git a/x/ibc/core/keeper/msg_server.go b/x/ibc/core/keeper/msg_server.go new file mode 100644 index 000000000000..dcddcaed16d4 --- /dev/null +++ b/x/ibc/core/keeper/msg_server.go @@ -0,0 +1,616 @@ +package keeper + +import ( + "context" + + "github.com/armon/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channel "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" +) + +var _ clienttypes.MsgServer = Keeper{} +var _ connectiontypes.MsgServer = Keeper{} +var _ channeltypes.MsgServer = Keeper{} + +// CreateClient defines a rpc handler method for MsgCreateClient. +func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateClient) (*clienttypes.MsgCreateClientResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + clientState, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, err + } + + consensusState, err := clienttypes.UnpackConsensusState(msg.ConsensusState) + if err != nil { + return nil, err + } + + clientID, err := k.ClientKeeper.CreateClient(ctx, clientState, consensusState) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + clienttypes.EventTypeCreateClient, + sdk.NewAttribute(clienttypes.AttributeKeyClientID, clientID), + sdk.NewAttribute(clienttypes.AttributeKeyClientType, clientState.ClientType()), + sdk.NewAttribute(clienttypes.AttributeKeyConsensusHeight, clientState.GetLatestHeight().String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, clienttypes.AttributeValueCategory), + ), + }) + + return &clienttypes.MsgCreateClientResponse{}, nil +} + +// UpdateClient defines a rpc handler method for MsgUpdateClient. +func (k Keeper) UpdateClient(goCtx context.Context, msg *clienttypes.MsgUpdateClient) (*clienttypes.MsgUpdateClientResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + header, err := clienttypes.UnpackHeader(msg.Header) + if err != nil { + return nil, err + } + + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, header); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, clienttypes.AttributeValueCategory), + ), + ) + + return &clienttypes.MsgUpdateClientResponse{}, nil +} + +// UpgradeClient defines a rpc handler method for MsgUpgradeClient. +func (k Keeper) UpgradeClient(goCtx context.Context, msg *clienttypes.MsgUpgradeClient) (*clienttypes.MsgUpgradeClientResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + upgradedClient, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, err + } + upgradedConsState, err := clienttypes.UnpackConsensusState(msg.ConsensusState) + if err != nil { + return nil, err + } + + if err = k.ClientKeeper.UpgradeClient(ctx, msg.ClientId, upgradedClient, upgradedConsState, + msg.ProofUpgradeClient, msg.ProofUpgradeConsensusState); err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, clienttypes.AttributeValueCategory), + ), + ) + + return &clienttypes.MsgUpgradeClientResponse{}, nil +} + +// SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. +func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSubmitMisbehaviour) (*clienttypes.MsgSubmitMisbehaviourResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + misbehaviour, err := clienttypes.UnpackMisbehaviour(msg.Misbehaviour) + if err != nil { + return nil, err + } + + if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, misbehaviour); err != nil { + return nil, sdkerrors.Wrap(err, "failed to process misbehaviour for IBC client") + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + clienttypes.EventTypeSubmitMisbehaviour, + sdk.NewAttribute(clienttypes.AttributeKeyClientID, msg.ClientId), + sdk.NewAttribute(clienttypes.AttributeKeyClientType, misbehaviour.ClientType()), + sdk.NewAttribute(clienttypes.AttributeKeyConsensusHeight, misbehaviour.GetHeight().String()), + ), + ) + + return &clienttypes.MsgSubmitMisbehaviourResponse{}, nil +} + +// ConnectionOpenInit defines a rpc handler method for MsgConnectionOpenInit. +func (k Keeper) ConnectionOpenInit(goCtx context.Context, msg *connectiontypes.MsgConnectionOpenInit) (*connectiontypes.MsgConnectionOpenInitResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + connectionID, err := k.ConnectionKeeper.ConnOpenInit(ctx, msg.ClientId, msg.Counterparty, msg.Version, msg.DelayPeriod) + if err != nil { + return nil, sdkerrors.Wrap(err, "connection handshake open init failed") + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + connectiontypes.EventTypeConnectionOpenInit, + sdk.NewAttribute(connectiontypes.AttributeKeyConnectionID, connectionID), + sdk.NewAttribute(connectiontypes.AttributeKeyClientID, msg.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyClientID, msg.Counterparty.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyConnectionID, msg.Counterparty.ConnectionId), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, connectiontypes.AttributeValueCategory), + ), + }) + + return &connectiontypes.MsgConnectionOpenInitResponse{}, nil +} + +// ConnectionOpenTry defines a rpc handler method for MsgConnectionOpenTry. +func (k Keeper) ConnectionOpenTry(goCtx context.Context, msg *connectiontypes.MsgConnectionOpenTry) (*connectiontypes.MsgConnectionOpenTryResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + targetClient, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, sdkerrors.Wrapf(err, "client in msg is not exported.ClientState. invalid client: %v.", targetClient) + } + + connectionID, err := k.ConnectionKeeper.ConnOpenTry( + ctx, msg.PreviousConnectionId, msg.Counterparty, msg.DelayPeriod, msg.ClientId, targetClient, + connectiontypes.ProtoVersionsToExported(msg.CounterpartyVersions), msg.ProofInit, msg.ProofClient, msg.ProofConsensus, + msg.ProofHeight, msg.ConsensusHeight, + ) + if err != nil { + return nil, sdkerrors.Wrap(err, "connection handshake open try failed") + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + connectiontypes.EventTypeConnectionOpenTry, + sdk.NewAttribute(connectiontypes.AttributeKeyConnectionID, connectionID), + sdk.NewAttribute(connectiontypes.AttributeKeyClientID, msg.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyClientID, msg.Counterparty.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyConnectionID, msg.Counterparty.ConnectionId), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, connectiontypes.AttributeValueCategory), + ), + }) + + return &connectiontypes.MsgConnectionOpenTryResponse{}, nil +} + +// ConnectionOpenAck defines a rpc handler method for MsgConnectionOpenAck. +func (k Keeper) ConnectionOpenAck(goCtx context.Context, msg *connectiontypes.MsgConnectionOpenAck) (*connectiontypes.MsgConnectionOpenAckResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + targetClient, err := clienttypes.UnpackClientState(msg.ClientState) + if err != nil { + return nil, sdkerrors.Wrapf(err, "client in msg is not exported.ClientState. invalid client: %v", targetClient) + } + + if err := k.ConnectionKeeper.ConnOpenAck( + ctx, msg.ConnectionId, targetClient, msg.Version, msg.CounterpartyConnectionId, + msg.ProofTry, msg.ProofClient, msg.ProofConsensus, + msg.ProofHeight, msg.ConsensusHeight, + ); err != nil { + return nil, sdkerrors.Wrap(err, "connection handshake open ack failed") + } + + connectionEnd, _ := k.ConnectionKeeper.GetConnection(ctx, msg.ConnectionId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + connectiontypes.EventTypeConnectionOpenAck, + sdk.NewAttribute(connectiontypes.AttributeKeyConnectionID, msg.ConnectionId), + sdk.NewAttribute(connectiontypes.AttributeKeyClientID, connectionEnd.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyClientID, connectionEnd.Counterparty.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyConnectionID, connectionEnd.Counterparty.ConnectionId), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, connectiontypes.AttributeValueCategory), + ), + }) + + return &connectiontypes.MsgConnectionOpenAckResponse{}, nil +} + +// ConnectionOpenConfirm defines a rpc handler method for MsgConnectionOpenConfirm. +func (k Keeper) ConnectionOpenConfirm(goCtx context.Context, msg *connectiontypes.MsgConnectionOpenConfirm) (*connectiontypes.MsgConnectionOpenConfirmResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := k.ConnectionKeeper.ConnOpenConfirm( + ctx, msg.ConnectionId, msg.ProofAck, msg.ProofHeight, + ); err != nil { + return nil, sdkerrors.Wrap(err, "connection handshake open confirm failed") + } + + connectionEnd, _ := k.ConnectionKeeper.GetConnection(ctx, msg.ConnectionId) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + connectiontypes.EventTypeConnectionOpenConfirm, + sdk.NewAttribute(connectiontypes.AttributeKeyConnectionID, msg.ConnectionId), + sdk.NewAttribute(connectiontypes.AttributeKeyClientID, connectionEnd.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyClientID, connectionEnd.Counterparty.ClientId), + sdk.NewAttribute(connectiontypes.AttributeKeyCounterpartyConnectionID, connectionEnd.Counterparty.ConnectionId), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, connectiontypes.AttributeValueCategory), + ), + }) + + return &connectiontypes.MsgConnectionOpenConfirmResponse{}, nil +} + +// ChannelOpenInit defines a rpc handler method for MsgChannelOpenInit. +func (k Keeper) ChannelOpenInit(goCtx context.Context, msg *channeltypes.MsgChannelOpenInit) (*channeltypes.MsgChannelOpenInitResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by port capability + module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + _, channelID, cap, err := channel.HandleMsgChannelOpenInit(ctx, k.ChannelKeeper, portCap, msg) + if err != nil { + return nil, err + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + if err = cbs.OnChanOpenInit(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.Channel.Version); err != nil { + return nil, sdkerrors.Wrap(err, "channel open init callback failed") + } + + return &channeltypes.MsgChannelOpenInitResponse{}, nil +} + +// ChannelOpenTry defines a rpc handler method for MsgChannelOpenTry. +func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChannelOpenTry) (*channeltypes.MsgChannelOpenTryResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // Lookup module by port capability + module, portCap, err := k.PortKeeper.LookupModuleByPort(ctx, msg.PortId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + _, channelID, cap, err := channel.HandleMsgChannelOpenTry(ctx, k.ChannelKeeper, portCap, msg) + if err != nil { + return nil, err + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + if err = cbs.OnChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.Channel.Version, msg.CounterpartyVersion); err != nil { + return nil, sdkerrors.Wrap(err, "channel open try callback failed") + } + + return &channeltypes.MsgChannelOpenTryResponse{}, nil +} + +// ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. +func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChannelOpenAck) (*channeltypes.MsgChannelOpenAckResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + _, 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 +} + +// ChannelOpenConfirm defines a rpc handler method for MsgChannelOpenConfirm. +func (k Keeper) ChannelOpenConfirm(goCtx context.Context, msg *channeltypes.MsgChannelOpenConfirm) (*channeltypes.MsgChannelOpenConfirmResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + _, 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 +} + +// ChannelCloseInit defines a rpc handler method for MsgChannelCloseInit. +func (k Keeper) ChannelCloseInit(goCtx context.Context, msg *channeltypes.MsgChannelCloseInit) (*channeltypes.MsgChannelCloseInitResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + if err = cbs.OnChanCloseInit(ctx, msg.PortId, msg.ChannelId); err != nil { + return nil, sdkerrors.Wrap(err, "channel close init callback failed") + } + + _, err = channel.HandleMsgChannelCloseInit(ctx, k.ChannelKeeper, cap, msg) + if err != nil { + return nil, err + } + + return &channeltypes.MsgChannelCloseInitResponse{}, nil +} + +// ChannelCloseConfirm defines a rpc handler method for MsgChannelCloseConfirm. +func (k Keeper) ChannelCloseConfirm(goCtx context.Context, msg *channeltypes.MsgChannelCloseConfirm) (*channeltypes.MsgChannelCloseConfirmResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.PortId, msg.ChannelId) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + if err = cbs.OnChanCloseConfirm(ctx, msg.PortId, msg.ChannelId); err != nil { + return nil, sdkerrors.Wrap(err, "channel close confirm callback failed") + } + + _, err = channel.HandleMsgChannelCloseConfirm(ctx, k.ChannelKeeper, cap, msg) + if err != nil { + return nil, err + } + + return &channeltypes.MsgChannelCloseConfirmResponse{}, nil +} + +// RecvPacket defines a rpc handler method for MsgRecvPacket. +func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacket) (*channeltypes.MsgRecvPacketResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.DestinationPort, msg.Packet.DestinationChannel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + // Perform TAO verification + if err := k.ChannelKeeper.RecvPacket(ctx, cap, msg.Packet, msg.ProofCommitment, msg.ProofHeight); err != nil { + return nil, sdkerrors.Wrap(err, "receive packet verification failed") + } + + // Perform application logic callback + _, ack, err := cbs.OnRecvPacket(ctx, msg.Packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "receive packet callback failed") + } + + // Set packet acknowledgement only if the acknowledgement is not nil. + // NOTE: IBC applications modules may call the WriteAcknowledgement asynchronously if the + // acknowledgement is nil. + if ack != nil { + if err := k.ChannelKeeper.WriteAcknowledgement(ctx, cap, msg.Packet, ack); err != nil { + return nil, err + } + } + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"tx", "msg", "ibc", msg.Type()}, + 1, + []metrics.Label{ + telemetry.NewLabel("source-port", msg.Packet.SourcePort), + telemetry.NewLabel("source-channel", msg.Packet.SourceChannel), + telemetry.NewLabel("destination-port", msg.Packet.DestinationPort), + telemetry.NewLabel("destination-channel", msg.Packet.DestinationChannel), + }, + ) + }() + + return &channeltypes.MsgRecvPacketResponse{}, nil +} + +// Timeout defines a rpc handler method for MsgTimeout. +func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*channeltypes.MsgTimeoutResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + // Perform TAO verification + if err := k.ChannelKeeper.TimeoutPacket(ctx, msg.Packet, msg.ProofUnreceived, msg.ProofHeight, msg.NextSequenceRecv); err != nil { + return nil, sdkerrors.Wrap(err, "timeout packet verification failed") + } + + // Perform application logic callback + _, err = cbs.OnTimeoutPacket(ctx, msg.Packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "timeout packet callback failed") + } + + // Delete packet commitment + if err = k.ChannelKeeper.TimeoutExecuted(ctx, cap, msg.Packet); err != nil { + return nil, err + } + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "timeout", "packet"}, + 1, + []metrics.Label{ + telemetry.NewLabel("source-port", msg.Packet.SourcePort), + telemetry.NewLabel("source-channel", msg.Packet.SourceChannel), + telemetry.NewLabel("destination-port", msg.Packet.DestinationPort), + telemetry.NewLabel("destination-channel", msg.Packet.DestinationChannel), + telemetry.NewLabel("timeout-type", "height"), + }, + ) + }() + + return &channeltypes.MsgTimeoutResponse{}, nil +} + +// TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. +func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeoutOnClose) (*channeltypes.MsgTimeoutOnCloseResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + // Perform TAO verification + if err := k.ChannelKeeper.TimeoutOnClose(ctx, cap, msg.Packet, msg.ProofUnreceived, msg.ProofClose, msg.ProofHeight, msg.NextSequenceRecv); err != nil { + return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed") + } + + // Perform application logic callback + // NOTE: MsgTimeout and MsgTimeoutOnClose use the same "OnTimeoutPacket" + // application logic callback. + _, err = cbs.OnTimeoutPacket(ctx, msg.Packet) + if err != nil { + return nil, sdkerrors.Wrap(err, "timeout packet callback failed") + } + + // Delete packet commitment + if err = k.ChannelKeeper.TimeoutExecuted(ctx, cap, msg.Packet); err != nil { + return nil, err + } + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "timeout", "packet"}, + 1, + []metrics.Label{ + telemetry.NewLabel("source-port", msg.Packet.SourcePort), + telemetry.NewLabel("source-channel", msg.Packet.SourceChannel), + telemetry.NewLabel("destination-port", msg.Packet.DestinationPort), + telemetry.NewLabel("destination-channel", msg.Packet.DestinationChannel), + telemetry.NewLabel("timeout-type", "channel-closed"), + }, + ) + }() + + return &channeltypes.MsgTimeoutOnCloseResponse{}, nil +} + +// Acknowledgement defines a rpc handler method for MsgAcknowledgement. +func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAcknowledgement) (*channeltypes.MsgAcknowledgementResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // Lookup module by channel capability + module, cap, err := k.ChannelKeeper.LookupModuleByChannel(ctx, msg.Packet.SourcePort, msg.Packet.SourceChannel) + if err != nil { + return nil, sdkerrors.Wrap(err, "could not retrieve module from port-id") + } + + // Retrieve callbacks from router + cbs, ok := k.Router.GetRoute(module) + if !ok { + return nil, sdkerrors.Wrapf(porttypes.ErrInvalidRoute, "route not found to module: %s", module) + } + + // Perform TAO verification + if err := k.ChannelKeeper.AcknowledgePacket(ctx, cap, msg.Packet, msg.Acknowledgement, msg.ProofAcked, msg.ProofHeight); err != nil { + return nil, sdkerrors.Wrap(err, "acknowledge packet verification failed") + } + + // Perform application logic callback + _, err = cbs.OnAcknowledgementPacket(ctx, msg.Packet, msg.Acknowledgement) + if err != nil { + return nil, sdkerrors.Wrap(err, "acknowledge packet callback failed") + } + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"tx", "msg", "ibc", msg.Type()}, + 1, + []metrics.Label{ + telemetry.NewLabel("source-port", msg.Packet.SourcePort), + telemetry.NewLabel("source-channel", msg.Packet.SourceChannel), + telemetry.NewLabel("destination-port", msg.Packet.DestinationPort), + telemetry.NewLabel("destination-channel", msg.Packet.DestinationChannel), + }, + ) + }() + + return &channeltypes.MsgAcknowledgementResponse{}, nil +} diff --git a/x/ibc/core/keeper/msg_server_test.go b/x/ibc/core/keeper/msg_server_test.go new file mode 100644 index 000000000000..1af4cdc18eb0 --- /dev/null +++ b/x/ibc/core/keeper/msg_server_test.go @@ -0,0 +1,714 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +const height = 10 + +var ( + timeoutHeight = clienttypes.NewHeight(0, 10000) + maxSequence = uint64(10) +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain +} + +// SetupTest creates a coordinator with 2 test chains. +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) + suite.coordinator.CommitNBlocks(suite.chainA, 2) + suite.coordinator.CommitNBlocks(suite.chainB, 2) +} + +func TestIBCTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// tests the IBC handler receiving a packet on ordered and unordered channels. +// It verifies that the storing of an acknowledgement on success occurs. It +// tests high level properties like ordering and basic sanity checks. More +// rigorous testing of 'RecvPacket' can be found in the +// 04-channel/keeper/packet_test.go. +func (suite *KeeperTestSuite) TestHandleRecvPacket() { + var ( + packet channeltypes.Packet + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + {"success: ORDERED", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + }, true}, + {"success: UNORDERED", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + }, true}, + {"success: UNORDERED out of order packet", func() { + // setup uses an UNORDERED channel + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + // attempts to receive packet with sequence 10 without receiving packet with sequence 1 + for i := uint64(1); i < 10; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + }, true}, + {"failure: ORDERED out of order packet", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + + // attempts to receive packet with sequence 10 without receiving packet with sequence 1 + for i := uint64(1); i < 10; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + }, false}, + {"channel does not exist", func() { + // any non-nil value of packet is valid + suite.Require().NotNil(packet) + }, false}, + {"packet not sent", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + }, false}, + {"ORDERED: packet already received (replay)", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + }, false}, + {"UNORDERED: packet already received (replay)", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() + + // get proof of packet commitment from chainA + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainA.QueryProof(packetKey) + + msg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress()) + + // ante-handle RecvPacket + _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.IBCKeeper, sdk.WrapSDKContext(suite.chainB.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) + + // replay should fail since state changes occur + _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.IBCKeeper, sdk.WrapSDKContext(suite.chainB.GetContext()), msg) + suite.Require().Error(err) + + // verify ack was written + ack, found := suite.chainB.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + suite.Require().NotNil(ack) + suite.Require().True(found) + } else { + suite.Require().Error(err) + } + }) + } +} + +// tests the IBC handler acknowledgement of a packet on ordered and unordered +// channels. It verifies that the deletion of packet commitments from state +// occurs. It test high level properties like ordering and basic sanity +// checks. More rigorous testing of 'AcknowledgePacket' +// can be found in the 04-channel/keeper/packet_test.go. +func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { + var ( + packet channeltypes.Packet + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + {"success: ORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + }, true}, + {"success: UNORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + }, true}, + {"success: UNORDERED acknowledge out of order packet", func() { + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + // attempts to acknowledge ack with sequence 10 without acknowledging ack with sequence 1 (removing packet commitment) + for i := uint64(1); i < 10; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + } + }, true}, + {"failure: ORDERED acknowledge out of order packet", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + + // attempts to acknowledge ack with sequence 10 without acknowledging ack with sequence 1 (removing packet commitment + for i := uint64(1); i < 10; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + } + }, false}, + {"channel does not exist", func() { + // any non-nil value of packet is valid + suite.Require().NotNil(packet) + }, false}, + {"packet not received", func() { + _, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + }, false}, + {"ORDERED: packet already acknowledged (replay)", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + err = suite.coordinator.AcknowledgePacket(suite.chainA, suite.chainB, clientB, packet, ibctesting.TestHash) + suite.Require().NoError(err) + }, false}, + {"UNORDERED: packet already acknowledged (replay)", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + err = suite.coordinator.AcknowledgePacket(suite.chainA, suite.chainB, clientB, packet, ibctesting.TestHash) + suite.Require().NoError(err) + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + ibctesting.TestHash = ibctesting.MockAcknowledgement + + tc.malleate() + + packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := suite.chainB.QueryProof(packetKey) + + msg := channeltypes.NewMsgAcknowledgement(packet, ibcmock.MockAcknowledgement, proof, proofHeight, suite.chainA.SenderAccount.GetAddress()) + + _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) + + // replay should an error + _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + suite.Require().Error(err) + + // verify packet commitment was deleted on source chain + has := suite.chainA.App.IBCKeeper.ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + suite.Require().False(has) + + } else { + suite.Require().Error(err) + } + }) + } +} + +// tests the IBC handler timing out a packet on ordered and unordered channels. +// It verifies that the deletion of a packet commitment occurs. It tests +// high level properties like ordering and basic sanity checks. More +// rigorous testing of 'TimeoutPacket' and 'TimeoutExecuted' can be found in +// the 04-channel/keeper/timeout_test.go. +func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { + var ( + packet channeltypes.Packet + packetKey []byte + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + {"success: ORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA client to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + }, true}, + {"success: UNORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), uint64(suite.chainB.GetContext().BlockTime().UnixNano())) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA client to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + }, true}, + {"success: UNORDERED timeout out of order packet", func() { + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + + // attempts to timeout the last packet sent without timing out the first packet + // packet sequences begin at 1 + for i := uint64(1); i < maxSequence; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), 0) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + }, true}, + {"success: ORDERED timeout out of order packet", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + + // attempts to timeout the last packet sent without timing out the first packet + // packet sequences begin at 1 + for i := uint64(1); i < maxSequence; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.GetSelfHeight(suite.chainB.GetContext()), 0) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + + }, true}, + {"channel does not exist", func() { + // any non-nil value of packet is valid + suite.Require().NotNil(packet) + + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + }, false}, + {"UNORDERED: packet not sent", func() { + _, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() + + proof, proofHeight := suite.chainB.QueryProof(packetKey) + + msg := channeltypes.NewMsgTimeout(packet, 1, proof, proofHeight, suite.chainA.SenderAccount.GetAddress()) + + _, err := keeper.Keeper.Timeout(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) + + // replay should return an error + _, err := keeper.Keeper.Timeout(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + suite.Require().Error(err) + + // verify packet commitment was deleted on source chain + has := suite.chainA.App.IBCKeeper.ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + suite.Require().False(has) + + } else { + suite.Require().Error(err) + } + }) + } +} + +// tests the IBC handler timing out a packet via channel closure on ordered +// and unordered channels. It verifies that the deletion of a packet +// commitment occurs. It tests high level properties like ordering and basic +// sanity checks. More rigorous testing of 'TimeoutOnClose' and +//'TimeoutExecuted' can be found in the 04-channel/keeper/timeout_test.go. +func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { + var ( + packet channeltypes.Packet + packetKey []byte + counterpartyChannel ibctesting.TestChannel + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + {"success: ORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA client to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + + // close counterparty channel + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel) + }, true}, + {"success: UNORDERED", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA client to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + // close counterparty channel + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel) + }, true}, + {"success: UNORDERED timeout out of order packet", func() { + // setup uses an UNORDERED channel + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // attempts to timeout the last packet sent without timing out the first packet + // packet sequences begin at 1 + for i := uint64(1); i < maxSequence; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + + // close counterparty channel + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel) + }, true}, + {"success: ORDERED timeout out of order packet", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // attempts to timeout the last packet sent without timing out the first packet + // packet sequences begin at 1 + for i := uint64(1); i < maxSequence; i++ { + packet = channeltypes.NewPacket(ibctesting.MockCommitment, i, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + } + + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + + // close counterparty channel + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel) + }, true}, + {"channel does not exist", func() { + // any non-nil value of packet is valid + suite.Require().NotNil(packet) + + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + }, false}, + {"UNORDERED: packet not sent", func() { + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + packetKey = host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // close counterparty channel + suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, counterpartyChannel) + }, false}, + {"ORDERED: channel not closed", func() { + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet = channeltypes.NewPacket(ibctesting.MockCommitment, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, 0) + counterpartyChannel = ibctesting.TestChannel{ + PortID: channelB.PortID, + ID: channelB.ID, + CounterpartyClientID: clientA, + } + + // create packet commitment + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA client to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + }, false}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + tc.malleate() + + proof, proofHeight := suite.chainB.QueryProof(packetKey) + + channelKey := host.ChannelKey(counterpartyChannel.PortID, counterpartyChannel.ID) + proofClosed, _ := suite.chainB.QueryProof(channelKey) + + msg := channeltypes.NewMsgTimeoutOnClose(packet, 1, proof, proofClosed, proofHeight, suite.chainA.SenderAccount.GetAddress()) + + _, err := keeper.Keeper.TimeoutOnClose(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) + + // replay should return an error + _, err := keeper.Keeper.TimeoutOnClose(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + suite.Require().Error(err) + + // verify packet commitment was deleted on source chain + has := suite.chainA.App.IBCKeeper.ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + suite.Require().False(has) + + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestUpgradeClient() { + var ( + clientA string + upgradedClient exported.ClientState + upgradedConsState exported.ConsensusState + lastHeight exported.Height + msg *clienttypes.MsgUpgradeClient + ) + + newClientHeight := clienttypes.NewHeight(1, 1) + + cases := []struct { + name string + setup func() + expPass bool + }{ + { + name: "successful upgrade", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + upgradedClient = upgradedClient.ZeroCustomFields() + + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradeClient, _ := suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ := suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + msg, err = clienttypes.NewMsgUpgradeClient(clientA, upgradedClient, upgradedConsState, + proofUpgradeClient, proofUpgradedConsState, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + expPass: true, + }, + { + name: "VerifyUpgrade fails", + setup: func() { + + upgradedClient = ibctmtypes.NewClientState("newChainId", ibctmtypes.DefaultTrustLevel, ibctesting.TrustingPeriod, ibctesting.UnbondingPeriod+ibctesting.TrustingPeriod, ibctesting.MaxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + upgradedClient = upgradedClient.ZeroCustomFields() + + upgradedConsState = &ibctmtypes.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // last Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + msg, err = clienttypes.NewMsgUpgradeClient(clientA, upgradedClient, upgradedConsState, nil, nil, suite.chainA.SenderAccount.GetAddress()) + suite.Require().NoError(err) + }, + expPass: false, + }, + } + + for _, tc := range cases { + tc := tc + clientA, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + tc.setup() + + _, err := keeper.Keeper.UpgradeClient(*suite.chainA.App.IBCKeeper, sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err, "upgrade handler failed on valid case: %s", tc.name) + newClient, ok := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(ok) + newChainSpecifiedClient := newClient.ZeroCustomFields() + suite.Require().Equal(upgradedClient, newChainSpecifiedClient) + } else { + suite.Require().Error(err, "upgrade handler passed on invalid case: %s", tc.name) + } + } +} diff --git a/x/ibc/core/module.go b/x/ibc/core/module.go new file mode 100644 index 000000000000..3371dc88a446 --- /dev/null +++ b/x/ibc/core/module.go @@ -0,0 +1,197 @@ +package ibc + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/client/cli" + "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" + "github.com/cosmos/cosmos-sdk/x/ibc/core/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// AppModuleBasic defines the basic application module used by the ibc module. +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// Name returns the ibc module's name. +func (AppModuleBasic) Name() string { + return host.ModuleName +} + +// RegisterLegacyAminoCodec does nothing. IBC does not support amino. +func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} + +// DefaultGenesis returns default genesis state as raw bytes for the ibc +// module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the ibc module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var gs types.GenesisState + if err := cdc.UnmarshalJSON(bz, &gs); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", host.ModuleName, err) + } + + return gs.Validate() +} + +// RegisterRESTRoutes does nothing. IBC does not support legacy REST routes. +func (AppModuleBasic) RegisterRESTRoutes(client.Context, *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + clienttypes.RegisterQueryHandlerClient(context.Background(), mux, clienttypes.NewQueryClient(clientCtx)) + connectiontypes.RegisterQueryHandlerClient(context.Background(), mux, connectiontypes.NewQueryClient(clientCtx)) + channeltypes.RegisterQueryHandlerClient(context.Background(), mux, channeltypes.NewQueryClient(clientCtx)) +} + +// GetTxCmd returns the root tx command for the ibc module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns no root query command for the ibc module. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers module concrete types into protobuf Any. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// AppModule implements an application module for the ibc module. +type AppModule struct { + AppModuleBasic + keeper *keeper.Keeper + + // create localhost by default + createLocalhost bool +} + +// NewAppModule creates a new AppModule object +func NewAppModule(k *keeper.Keeper) AppModule { + return AppModule{ + keeper: k, + } +} + +// Name returns the ibc module's name. +func (AppModule) Name() string { + return host.ModuleName +} + +// RegisterInvariants registers the ibc module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO: +} + +// Route returns the message routing key for the ibc module. +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(host.RouterKey, NewHandler(*am.keeper)) +} + +// QuerierRoute returns the ibc module's querier route name. +func (AppModule) QuerierRoute() string { + return host.QuerierRoute +} + +// LegacyQuerierHandler returns nil. IBC does not support the legacy querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + clienttypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) + connectiontypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) + channeltypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryService(cfg.QueryServer(), am.keeper) +} + +// InitGenesis performs genesis initialization for the ibc 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 + err := cdc.UnmarshalJSON(bz, &gs) + if err != nil { + panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", host.ModuleName, err)) + } + InitGenesis(ctx, *am.keeper, am.createLocalhost, &gs) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the ibc +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(ExportGenesis(ctx, *am.keeper)) +} + +// 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) +} + +// EndBlock returns the end blocker for the ibc module. It returns no validator +// updates. +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the ibc module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil since IBC doesn't register parameter changes. +func (AppModule) RandomizedParams(_ *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for ibc module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[host.StoreKey] = simulation.NewDecodeStore(*am.keeper) +} + +// WeightedOperations returns the all the ibc module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/ibc/core/simulation/decoder.go b/x/ibc/core/simulation/decoder.go new file mode 100644 index 000000000000..459eebb8f078 --- /dev/null +++ b/x/ibc/core/simulation/decoder.go @@ -0,0 +1,32 @@ +package simulation + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/kv" + clientsim "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/simulation" + connectionsim "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/simulation" + channelsim "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/simulation" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/keeper" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding ibc type. +func NewDecodeStore(k keeper.Keeper) func(kvA, kvB kv.Pair) string { + return func(kvA, kvB kv.Pair) string { + if res, found := clientsim.NewDecodeStore(k.ClientKeeper, kvA, kvB); found { + return res + } + + if res, found := connectionsim.NewDecodeStore(k.Codec(), kvA, kvB); found { + return res + } + + if res, found := channelsim.NewDecodeStore(k.Codec(), kvA, kvB); found { + return res + } + + panic(fmt.Sprintf("invalid %s key prefix: %s", host.ModuleName, string(kvA.Key))) + } +} diff --git a/x/ibc/core/simulation/decoder_test.go b/x/ibc/core/simulation/decoder_test.go new file mode 100644 index 000000000000..0951572743b1 --- /dev/null +++ b/x/ibc/core/simulation/decoder_test.go @@ -0,0 +1,80 @@ +package simulation_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/types/kv" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/simulation" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +func TestDecodeStore(t *testing.T) { + app := simapp.Setup(false) + dec := simulation.NewDecodeStore(*app.IBCKeeper) + + clientID := "clientidone" + connectionID := "connectionidone" + channelID := "channelidone" + portID := "portidone" + + clientState := &ibctmtypes.ClientState{ + FrozenHeight: clienttypes.NewHeight(0, 10), + } + connection := connectiontypes.ConnectionEnd{ + ClientId: "clientidone", + Versions: []*connectiontypes.Version{connectiontypes.NewVersion("1", nil)}, + } + channel := channeltypes.Channel{ + State: channeltypes.OPEN, + Version: "1.0", + } + + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + { + Key: host.FullClientStateKey(clientID), + Value: app.IBCKeeper.ClientKeeper.MustMarshalClientState(clientState), + }, + { + Key: host.ConnectionKey(connectionID), + Value: app.IBCKeeper.Codec().MustMarshalBinaryBare(&connection), + }, + { + Key: host.ChannelKey(portID, channelID), + Value: app.IBCKeeper.Codec().MustMarshalBinaryBare(&channel), + }, + { + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"ClientState", fmt.Sprintf("ClientState A: %v\nClientState B: %v", clientState, clientState)}, + {"ConnectionEnd", fmt.Sprintf("ConnectionEnd A: %v\nConnectionEnd B: %v", connection, connection)}, + {"Channel", fmt.Sprintf("Channel A: %v\nChannel B: %v", channel, channel)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + if i == len(tests)-1 { + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) + } else { + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) + } + }) + } +} diff --git a/x/ibc/core/simulation/genesis.go b/x/ibc/core/simulation/genesis.go new file mode 100644 index 000000000000..d71f4492500e --- /dev/null +++ b/x/ibc/core/simulation/genesis.go @@ -0,0 +1,63 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + clientsims "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/simulation" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectionsims "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/simulation" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channelsims "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/simulation" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" +) + +// Simulation parameter constants +const ( + clientGenesis = "client_genesis" + connectionGenesis = "connection_genesis" + channelGenesis = "channel_genesis" +) + +// RandomizedGenState generates a random GenesisState for evidence +func RandomizedGenState(simState *module.SimulationState) { + var ( + clientGenesisState clienttypes.GenesisState + connectionGenesisState connectiontypes.GenesisState + channelGenesisState channeltypes.GenesisState + ) + + simState.AppParams.GetOrGenerate( + simState.Cdc, clientGenesis, &clientGenesisState, simState.Rand, + func(r *rand.Rand) { clientGenesisState = clientsims.GenClientGenesis(r, simState.Accounts) }, + ) + + simState.AppParams.GetOrGenerate( + simState.Cdc, connectionGenesis, &connectionGenesisState, simState.Rand, + func(r *rand.Rand) { connectionGenesisState = connectionsims.GenConnectionGenesis(r, simState.Accounts) }, + ) + + simState.AppParams.GetOrGenerate( + simState.Cdc, channelGenesis, &channelGenesisState, simState.Rand, + func(r *rand.Rand) { channelGenesisState = channelsims.GenChannelGenesis(r, simState.Accounts) }, + ) + + ibcGenesis := types.GenesisState{ + ClientGenesis: clientGenesisState, + ConnectionGenesis: connectionGenesisState, + ChannelGenesis: channelGenesisState, + } + + bz, err := json.MarshalIndent(&ibcGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", host.ModuleName, bz) + simState.GenState[host.ModuleName] = simState.Cdc.MustMarshalJSON(&ibcGenesis) +} diff --git a/x/ibc/core/simulation/genesis_test.go b/x/ibc/core/simulation/genesis_test.go new file mode 100644 index 000000000000..54aff75ad9c8 --- /dev/null +++ b/x/ibc/core/simulation/genesis_test.go @@ -0,0 +1,49 @@ +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" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/simulation" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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), + } + + // Remark: the current RandomizedGenState function + // is actually not random as it does not utilize concretely the random value r. + // This tests will pass for any value of r. + simulation.RandomizedGenState(&simState) + + var ibcGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[host.ModuleName], &ibcGenesis) + + require.NotNil(t, ibcGenesis.ClientGenesis) + require.NotNil(t, ibcGenesis.ConnectionGenesis) + require.NotNil(t, ibcGenesis.ChannelGenesis) +} diff --git a/x/ibc/core/spec/01_concepts.md b/x/ibc/core/spec/01_concepts.md new file mode 100644 index 000000000000..045508999d39 --- /dev/null +++ b/x/ibc/core/spec/01_concepts.md @@ -0,0 +1,393 @@ + + +# Concepts + +> NOTE: if you are not familiar with the IBC terminology and concepts, please read +this [document](https://github.com/cosmos/ics/blob/master/ibc/1_IBC_TERMINOLOGY.md) as prerequisite reading. + +## Client Creation, Updates, and Upgrades + +IBC clients are on chain light clients. The light client is responsible for verifying +counterparty state. A light client can be created by any user submitting a valid initial +`ClientState` and `ConsensusState`. The client identifier is auto generated using the +client type and the global client counter appended in the format: `{client-type}-{N}`. +Clients are given a client identifier prefixed store to store their associated client +state and consensus states. Consensus states are stored using their associated height. + +Clients can be updated by any user submitting a valid `Header`. The client state callback +to `CheckHeaderAndUpdateState` is responsible for verifying the header against previously +stored state. The function should also return the updated client state and consensus state +if the header is considered a valid update. A light client, such as Tendermint, may have +client specific parameters like `TrustLevel` which must be considered valid in relation +to the `Header`. The update height is not necessarily the lastest height of the light +client. Updates may fill in missing consensus state heights. + +Clients may be upgraded. The upgrade should be verified using `VerifyUpgrade`. It is not +a requirement to allow for light client upgrades. For example, the solo machine client +will simply return an error on `VerifyUpgrade`. Clients which implement upgrades +are expected to account for, but not necessarily support, planned and unplanned upgrades. + +## Client Misbehaviour + +IBC clients must freeze when the counterparty chain becomes byzantine and +takes actions that could fool the light client into accepting invalid state +transitions. Thus, relayers are able to submit Misbehaviour proofs that prove +that a counterparty chain has signed two Headers for the same height. This +constitutes misbehaviour as the IBC client could have accepted either header +as valid. Upon verifying the misbehaviour the IBC client must freeze at that +height so that any proof verifications for the frozen height or later fail. + +Note, there is a difference between the chain-level Misbehaviour that IBC is +concerned with and the validator-level Evidence that Tendermint is concerned +with. Tendermint must be able to detect, submit, and punish any evidence of +individual validators breaking the Tendermint consensus protocol and attempting +to mount an attack. IBC clients must only act when an attack is successful +and the chain has successfully forked. In this case, valid Headers submitted +to the IBC client can no longer be trusted and the client must freeze. + +Governance may then choose to override a frozen client and provide the correct, +canonical Header so that the client can continue operating after the Misbehaviour +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'. + +The localhost client cannot be updated by a governance proposal. + +The solo machine client requires the boolean flag 'AllowUpdateAfterProposal' to be set +to true in order to be updated by a proposal. This is set upon client creation and cannot +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. + +## IBC Client Heights + +IBC Client Heights are represented by the struct: + +```go +type Height struct { + RevisionNumber uint64 + RevisionHeight uint64 +} +``` + +The `RevisionNumber` represents the revision of the chain that the height is representing. +An revision typically represents a continuous, monotonically increasing range of block-heights. +The `RevisionHeight` represents the height of the chain within the given revision. + +On any reset of the `RevisionHeight`, for example, when hard-forking a Tendermint chain, +the `RevisionNumber` will get incremented. This allows IBC clients to distinguish between a +block-height `n` of a previous revision of the chain (at revision `p`) and block-height `n` of the current +revision of the chain (at revision `e`). + +`Heights` that share the same revision number can be compared by simply comparing their respective `RevisionHeights`. +Heights that do not share the same revision number will only be compared using their respective `RevisionNumbers`. +Thus a height `h` with revision number `e+1` will always be greater than a height `g` with revision number `e`, +**REGARDLESS** of the difference in revision heights. + +Ex: + +```go +Height{RevisionNumber: 3, RevisionHeight: 0} > Height{RevisionNumber: 2, RevisionHeight: 100000000000} +``` + +When a Tendermint chain is running a particular revision, relayers can simply submit headers and proofs with the revision number +given by the chain's chainID, and the revision height given by the Tendermint block height. When a chain updates using a hard-fork +and resets its block-height, it is responsible for updating its chain-id to increment the revision number. +IBC Tendermint clients then verifies the revision number against their `ChainId` and treat the `RevisionHeight` as the Tendermint block-height. + +Tendermint chains wishing to use revisions to maintain persistent IBC connections even across height-resetting upgrades must format their chain-ids +in the following manner: `{chainID}-{revision_number}`. On any height-resetting upgrade, the chainID **MUST** be updated with a higher revision number +than the previous value. + +Ex: + +- Before upgrade ChainID: `gaiamainnet-3` +- After upgrade ChainID: `gaiamainnet-4` + +Clients that do not require revisions, such as the solo-machine client, simply hardcode `0` into the revision number whenever they +need to return an IBC height when implementing IBC interfaces and use the `RevisionHeight` exclusively. + +Other client-types may implement their own logic to verify the IBC Heights that relayers provide in their `Update`, `Misbehavior`, and +`Verify` functions respectively. + +The IBC interfaces expect an `ibcexported.Height` interface, however all clients should use the concrete implementation provided in +`02-client/types` and reproduced above. + +## Connection Handshake + +The connection handshake occurs in 4 steps as defined in [ICS 03](https://github.com/cosmos/ics/tree/master/spec/ics-003-connection-semantics). + +`ConnOpenInit` is the first attempt to initialize a connection on the executing chain. +The handshake is expected to succeed if the version selected is supported. The connection +identifier for the counterparty connection must be left empty indicating that the counterparty +must select its own identifier. The connection identifier is auto derived in the format: +`connection{N}` where N is the next sequence to be used. The counter begins at 0 and increments +by 1. The connection is set and stored in the INIT state upon success. + +`ConnOpenTry` is a response to a chain executing `ConnOpenInit`. The executing chain will validate +the chain level parameters the counterparty has stored such as its chainID. The executing chain +will also verify that if a previous connection exists for the specified connection identifier +that all the parameters match and its previous state was in INIT. This may occur when both +chains execute `ConnOpenInit` simultaneously. If the connection does not exist then a connection +identifier is generated in the same format done in `ConnOpenInit`. The executing chain will verify +that the counterparty created a connection in INIT state. The executing chain will also verify +The `ClientState` and `ConsensusState` the counterparty stores for the executing chain. The +executing chain will select a version from the intersection of its supported versions and the +versions set by the counterparty. The connection is set and stored in the TRYOPEN state upon +success. + +`ConnOpenAck` may be called on a chain when the counterparty connection has entered TRYOPEN. A +previous connection on the executing chain must exist in either INIT or TRYOPEN. The executing +chain will verify the version the counterparty selected. If the counterparty selected its own +connection identifier, it will be validated in the basic validation of a `MsgConnOpenAck`. +The counterparty connection state is verified along with the `ClientState` and `ConsensusState` +stored for the executing chain. The connection is set and stored in the OPEN state upon success. + +`ConnOpenConfirm` is a response to a chain executing `ConnOpenAck`. The executing chain's connection +must be in TRYOPEN. The counterparty connection state is verified to be in the OPEN state. The +connection is set and stored in the OPEN state upon success. + +## Connection Version Negotiation + +During the handshake procedure for connections a version is agreed +upon between the two parties. This occurs during the first 3 steps of the +handshake. + +During `ConnOpenInit`, party A is expected to set all the versions they wish +to support within their connection state. It is expected that this set of +versions is from most preferred to least preferred. This is not a strict +requirement for the SDK implementation of IBC because the party calling +`ConnOpenTry` will greedily select the latest version it supports that the +counterparty supports as well. A specific version can optionally be passed +as `Version` to ensure that the handshake will either complete with that +version or fail. + +During `ConnOpenTry`, party B will select a version from the counterparty's +supported versions. Priority will be placed on the latest supported version. +If a matching version cannot be found an error is returned. + +During `ConnOpenAck`, party A will verify that they can support the version +party B selected. If they do not support the selected version an error is +returned. After this step, the connection version is considered agreed upon. + + +A `Version` is defined as follows: + +```go +type Version struct { + // unique version identifier + Identifier string + // list of features compatible with the specified identifier + Features []string +} +``` + +A version must contain a non empty identifier. Empty feature sets are allowed, but each +feature must be a non empty string. + +::: warning +A set of versions should not contain two versions with the same +identifier, but differing feature sets. This will result in undefined behavior +with regards to version selection in `ConnOpenTry`. Each version in a set of +versions should have a unique version identifier. +::: + +## Channel Handshake + +The channel handshake occurs in 4 steps as defined in [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics). + +`ChanOpenInit` is the first attempt to initialize a channel on top of an existing connection. +The handshake is expected to succeed if the version selected for the existing connection is a +supported IBC version. The portID must correspond to a port already binded upon `InitChain`. +The channel identifier for the counterparty channel must be left empty indicating that the +counterparty must select its own identifier. The channel identifier is auto derived in the +format: `channel{N}` where N is the next sequence to be used. The channel is set and stored +in the INIT state upon success. The channel parameters `NextSequenceSend`, `NextSequenceRecv`, +and `NextSequenceAck` are all set to 1 and a channel capability is created for the given +portID and channelID path. + +`ChanOpenTry` is a response to a chain executing `ChanOpenInit`. If the executing chain is calling +`ChanOpenTry` after previously executing `ChanOpenInit` then the provided channel parameters must +match the previously selected parameters. If the previous channel does not exist then a channel +identifier is generated in the same format as done in `ChanOpenInit`. The connection the channel +is created on top of must be an OPEN state and its IBC version must support the desired channel +type being created (ORDERED, UNORDERED, etc). The executing chain will verify that the channel +state of the counterparty is in INIT. The executing chain will set and store the channel state +in TRYOPEN. The channel parameters `NextSequenceSend`, `NextSequenceRecv`, and `NextSequenceAck` +are all set to 1 and a channel capability is created for the given portID and channelID path only +if the channel did not previously exist. + +`ChanOpenAck` may be called on a chain when the counterparty channel has entered TRYOPEN. A +previous channel on the executing chain must exist be in either INIT or TRYOPEN state. If the +counterparty selected its own channel identifier, it will be validated in the basic validation +of `MsgChanOpenAck`. The executing chain verifies that the counterparty channel state is in +TRYOPEN. The channel is set and stored in the OPEN state upon success. + +`ChanOpenConfirm` is a response to a chain executing `ChanOpenAck`. The executing chain's +previous channel state must be in TRYOPEN. The executing chain verifies that the counterparty +channel state is OPEN. The channel is set and stored in the OPEN state upon success. + +## Channel Version Negotiation + +During the channel handshake procedure a version must be agreed upon between +the two parties. The selection process is largely left to the callers and +the verification of valid versioning must be handled by application developers +in the channel handshake callbacks. + +During `ChanOpenInit`, a version string is passed in and set in party A's +channel state. + +During `ChanOpenTry`, a version string for party A and for party B are passed +in. The party A version string must match the version string used in +`ChanOpenInit` otherwise channel state verification will fail. The party B +version string could be anything (even different than the proposed one by +party A). However, the proposed version by party B is expected to be fully +supported by party A. + +During the `ChanOpenAck` callback, the application module is expected to verify +the version proposed by party B using the `MsgChanOpenAck` `CounterpartyVersion` +field. The application module should throw an error if the version string is +not valid. + +In general empty version strings are to be considered valid options for an +application module. + +Application modules may implement their own versioning system, such as semantic +versioning, or they may lean upon the versioning system used for in connection +version negotiation. To use the connection version semantics the application +would simply pass the proto encoded version into each of the handshake calls +and decode the version string into a `Version` instance to do version verification +in the handshake callbacks. + +Implementations which do not feel they would benefit from versioning can do +basic string matching using a single compatible version. + +## Sending, Receiving, Acknowledging Packets + +Terminology: +**Packet Commitment** A hash of the packet stored on the sending chain. +**Packet Receipt** A single bit indicating that a packet has been received. +Used for timeouts. +**Acknowledgement** Data written to indicate the result of receiving a packet. +Typically conveying either success or failure of the receive. + +A packet may be associated with one of the following states: +- the packet does not exist (ie it has not been sent) +- the packet has been sent but not received (the packet commitment exists on the +sending chain, but no receipt exists on the receiving chain) +- the packet has been received but not acknowledged (packet commitment exists +on the sending chain, a receipt exists on the receiving chain, but no acknowledgement +exists on the receiving chain) +- the packet has been acknowledgement but the acknowledgement has not been relayed +(the packet commitment exists on the sending chain, the receipt and acknowledgement +exist on the receiving chain) +- the packet has completed its life cycle (the packet commitment does not exist on +the sending chain, but a receipt and acknowledgement exist on the receiving chain) + +Sending of a packet is initiated by a call to the `ChannelKeeper.SendPacket` +function by an application module. Packets being sent will be verified for +correctness (core logic only). If the packet is valid, a hash of the packet +will be stored as a packet commitment using the packet sequence in the key. +Packet commitments are stored on the sending chain. + +A message should be sent to the receving chain indicating that the packet +has been committed on the sending chain and should be received on the +receiving chain. The light client on the receiving chain, which verifies +the sending chain's state, should be updated to the lastest sending chain +state if possible. The verification will fail if the latest state of the +light client does not include the packet commitment. The receiving chain +is responsible for verifying that the counterparty set the hash of the +packet. If verification of the packet to be received is successful, the +receiving chain should store a receipt of the packet and call application +logic if necessary. An acknowledgement may be processed and stored at this time (synchronously) +or at another point in the future (asynchronously). + +Acknowledgements written on the receiving chain may be verified on the +sending chain. If the sending chain successfully verifies the acknowledgement +then it may delete the packet commitment stored at that sequence. There is +no requirement for acknowledgements to be written. Only the hash of the +acknowledgement is stored on the chain. Application logic may be executed +in conjunction with verifying an acknowledgement. For example, in fungible +cross-chain token transfer, a failed acknowledgement results in locked or +burned funds being refunded. + +Relayers are responsible for reconstructing packets between the sending, +receiving, and acknowledging of packets. + +IBC applications sending and receiving packets are expected to appropriately +handle data contained within a packet. For example, cross-chain token +transfers will unmarshal the data into proto definitions representing +a token transfer. + +Future optimizations may allow for storage cleanup. Stored packet +commitments could be removed from channels which do not write +packet acknowledgements and acknowledgements could be removed +when a packet has completed its life cycle. + +## Timing out Packets + +A packet may be timed out on the receiving chain if the packet timeout height or timestamp has +been surpassed on the receving chain or the channel has closed. A timed out +packet can only occur if the packet has never been received on the receiving +chain. ORDERED channels will verify that the packet sequence is greater than +the `NextSequenceRecv` on the receiving chain. UNORDERED channels will verify +that the packet receipt has not been written on the receiving chain. A timeout +on channel closure will additionally verify that the counterparty channel has +been closed. A successful timeout may execute application logic as appropriate. + +Both the packet's timeout timestamp and the timeout height must have been +surpassed on the receiving chain for a timeout to be valid. A timeout timestamp +or timeout height with a 0 value indicates the timeout field may be ignored. +Each packet is required to have at least one valid timeout field. + +## Closing Channels + +Closing a channel occurs in occurs in 2 handshake steps as defined in [ICS 04](https://github.com/cosmos/ics/tree/master/spec/ics-004-channel-and-packet-semantics). + +`ChanCloseInit` will close a channel on the executing chain if the channel exists, it is not +already closed and the connection it exists upon is OPEN. Channels can only be closed by a +calling module or in the case of a packet timeout on an ORDERED channel. + +`ChanCloseConfirm` is a response to a counterparty channel executing `ChanCloseInit`. The channel +on the executing chain will be closed if the channel exists, the channel is not already closed, +the connection the channel exists upon is OPEN and the executing chain successfully verifies +that the counterparty channel has been closed. + +## Port and Channel Capabilities + +## Hostname Validation + +Hostname validation is implemented as defined in [ICS 24](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements). + +The 24-host sub-module parses and validates identifiers. It also builds +the key paths used to store IBC related information. + +A valid identifier must conatin only alphanumeric characters or the +following list of allowed characters: +".", "\_", "+", "-", "#", "[", "]", "<", ">" + +- Client identifiers must contain between 9 and 64 characters. +- Connection identifiers must contain between 10 and 64 characters. +- Channel identifiers must contain between 10 and 64 characters. +- Port identifiers must contain between 2 and 64 characters. + +## Proofs + +Proofs for counterparty state validation are provided as bytes. These bytes +can be unmarshaled into proto definitions as necessary by light clients. +For example, the Tendermint light client will use the bytes as a merkle +proof where as the solo machine client will unmarshal the proof into +several layers proto definitions used for signature verficiation. diff --git a/x/ibc/core/spec/02_state.md b/x/ibc/core/spec/02_state.md new file mode 100644 index 000000000000..2c85a525a953 --- /dev/null +++ b/x/ibc/core/spec/02_state.md @@ -0,0 +1,28 @@ + + +# State + +The paths for the values stored in state is defined [here](https://github.com/cosmos/ics/blob/master/spec/ics-024-host-requirements/README.md#path-space). +Additionally, the SDK adds a prefix to the path to be able to aggregate the values for querying purposes. +The client type is not stored since it can be obtained through the client state. + +| Prefix | Path | Value type | +|--------|-----------------------------------------------------------------------------|----------------| +| "0/" | "clients/{identifier}/clientState" | ClientState | +| "0/" | "clients/{identifier}/consensusStates/{height}" | ConsensusState | +| "0/" | "clients/{identifier}/connections" | []string | +| "0/" | "nextClientSequence | uint64 | +| "0/" | "connections/{identifier}" | ConnectionEnd | +| "0/" | "nextConnectionSequence" | uint64 | +| "0/" | "ports/{identifier}" | CapabilityKey | +| "0/" | "channelEnds/ports/{identifier}/channels/{identifier}" | ChannelEnd | +| "0/" | "nextChannelSequence" | uint64 | +| "0/" | "capabilities/ports/{identifier}/channels/{identifier}" | CapabilityKey | +| "0/" | "nextSequenceSend/ports/{identifier}/channels/{identifier}" | uint64 | +| "0/" | "nextSequenceRecv/ports/{identifier}/channels/{identifier}" | uint64 | +| "0/" | "nextSequenceAck/ports/{identifier}/channels/{identifier}" | uint64 | +| "0/" | "commitments/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | +| "0/" | "receipts/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | +| "0/" | "acks/ports/{identifier}/channels/{identifier}/sequences/{sequence}" | bytes | diff --git a/x/ibc/core/spec/03_state_transitions.md b/x/ibc/core/spec/03_state_transitions.md new file mode 100644 index 000000000000..be3b508b795c --- /dev/null +++ b/x/ibc/core/spec/03_state_transitions.md @@ -0,0 +1,105 @@ + + +# State Transitions + +The described state transitions assume successful message exection. + +## Create Client + +`MsgCreateClient` will initialize and store a `ClientState` and `ConsensusState` in the sub-store +created using a generated client identifier. + +## Update Client + +`MsgUpdateClient` will update the `ClientState` and create a new `ConsensusState` for the +update height. + +## Misbehaviour + +`MsgSubmitMisbehaviour` will freeze a client. + +## Upgrade Client + +`MsgUpgradeClient` will upgrade the `ClientState` and `ConsensusState` to the update chain level +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`. + +## Connection Open Init + +`MsgConnectionOpenInit` will initialize a connection state in INIT. + +## Connection Open Try + +`MsgConnectionOpenTry` will initialize or update a connection state to be in TRYOPEN. + +## Connection Open Ack + +`MsgConnectionOpenAck` will update a connection state from INIT or TRYOPEN to be in OPEN. + +## Connection Open Confirm + +`MsgConnectionOpenAck` will update a connection state from TRYOPEN to OPEN. + +## Channel Open Init + +`MsgChannelOpenInit` will initialize a channel state in INIT. It will create a channel capability +and set all Send, Receive and Ack Sequences to 1 for the channel. + +## Channel Open Try + +`MsgChannelOpenTry` will initialize or update a channel state to be in TRYOPEN. If the channel +is being initialized, It will create a channel capability and set all Send, Receive and Ack +Sequences to 1 for the channel. + +## Channel Open Ack + +`MsgChannelOpenAck` will update the channel state to OPEN. It will set the version and channel +identifier for its counterparty. + +## Channel Open Confirm + +`MsgChannelOpenConfirm` will update the channel state to OPEN. + +## Channel Close Init + +`MsgChannelCloseInit` will update the channel state to CLOSED. + +## Channel Close Confirm + +`MsgChannelCloseConfirm` will update the channel state to CLOSED. + +## Send Packet + +A application calling `ChannelKeeper.SendPacket` will incremenet the next sequence send and set +a hash of the packet as the packet commitment. + +## Receive Packet + +`MsgRecvPacket` will increment the next sequence receive for ORDERED channels and set a packet +receipt for UNORDERED channels. + +## Write Acknowledgement + +`WriteAcknowledgement` may be executed synchronously during the execution of `MsgRecvPacket` or +asynchonously by an application module. It writes an acknowledgement to the store. + +## Acknowledge Packet + +`MsgAcknowledgePacket` deletes the packet commitment and for ORDERED channels increments next +sequences ack. + +## Timeout Packet + +`MsgTimeoutPacket` deletes the packet commitment and for ORDERED channels sets the channel state +to CLOSED. + +## Timeout Packet on Channel Closure + +`MsgTimeoutOnClose` deletes the packet commitment and for ORDERED channels sets the channel state +to CLOSED. diff --git a/x/ibc/core/spec/04_messages.md b/x/ibc/core/spec/04_messages.md new file mode 100644 index 000000000000..3728e6d6f323 --- /dev/null +++ b/x/ibc/core/spec/04_messages.md @@ -0,0 +1,497 @@ + + +# Messages + +In this section we describe the processing of the IBC messages and the corresponding updates to the state. + +## ICS 02 - Client + +### MsgCreateClient + +A light client is created using the `MsgCreateClient`. + +```go +type MsgCreateClient struct { + ClientState *types.Any // proto-packed client state + ConsensusState *types.Any // proto-packed consensus state + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ClientState` is empty or invalid +- `ConsensusState` is empty or invalid +- `Signer` is empty + +The message creates and stores a light client with an initial consensus state using a generated client +identifier. + +### MsgUpdateClient + +A light client is updated with a new header using the `MsgUpdateClient`. + +```go +type MsgUpdateClient struct { + ClientId string + Header *types.Any // proto-packed header + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ClientId` is invalid (not alphanumeric or not within 10-20 characters) +- `Header` is empty or invalid +- `Signer` is empty +- A `ClientState` hasn't been created for the given ID +- The client is frozen due to misbehaviour and cannot be updated +- The header fails to provide a valid update for the client + +The message validates the header and updates the client state and consensus state for the +header height. + +### MsgUpgradeClient +```go +type MsgUpgradeClient struct { + ClientId string + ClientState *types.Any // proto-packed client state + UpgradeHeight *Height + ProofUpgrade []byte + Signer string +} +``` + +This message is expected to fail if: + +- `ClientId` is invalid (not alphanumeric or not within 10-20 characters) +- `ClientState` is empty or invalid +- `UpgradeHeight` is empty or zero +- `ProofUpgrade` is empty +- `Signer` is empty +- A `ClientState` hasn't been created for the given ID +- The client is frozen due to misbehaviour and cannot be upgraded +- The upgrade proof fails + +The message upgrades the client state and consensus state upon successful validation of a +chain upgrade. + +### MsgSubmitMisbehaviour + +Submit a evidence of light client misbehaviour to freeze the client state and prevent additional packets from being relayed. + +```go +type MsgSubmitMisbehaviour struct { + ClientId string + Misbehaviour *types.Any // proto-packed misbehaviour + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ClientId` is invalid (not alphanumeric or not within 10-20 characters) +- `Misbehaviour` is empty or invalid +- `Signer` is empty +- A `ClientState` hasn't been created for the given ID +- `Misbehaviour` check failed + +The message verifies the misbehaviour and freezes the client. + +## ICS 03 - Connection + +### MsgConnectionOpenInit + +A connection is initialized on a light client using the `MsgConnectionOpenInit`. + +```go +type MsgConnectionOpenInit struct { + ClientId string + Counterparty Counterparty + Version string + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: +- `ClientId` is invalid (see naming requirements) +- `Counterparty` is empty +- 'Version' is not empty and invalid +- `Signer` is empty +- A Client hasn't been created for the given ID +- A Connection for the given ID already exists + +The message creates a connection for the given ID with an INIT state. + +### MsgConnectionOpenTry + +When a counterparty connection is initialized then a connection is initialized on a light client +using the `MsgConnectionOpenTry`. + +```go +type MsgConnectionOpenTry struct { + ClientId string + PreviousConnectionId string + ClientState *types.Any // proto-packed counterparty client + Counterparty Counterparty + CounterpartyVersions []string + ProofHeight Height + ProofInit []byte + ProofClient []byte + ProofConsensus []byte + ConsensusHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ClientId` is invalid (see naming requirements) +- `PreviousConnectionId` is not empty and invalid (see naming requirements) +- `ClientState` is not a valid client of the executing chain +- `Counterparty` is empty +- `CounterpartyVersions` is empty +- `ProofHeight` is zero +- `ProofInit` is empty +- `ProofClient` is empty +- `ProofConsensus` is empty +- `ConsensusHeight` is zero +- `Signer` is empty +- A Client hasn't been created for the given ID +- If a previous connection exists but does not match the supplied parameters. +- `ProofInit` does not prove that the counterparty connection is in state INIT +- `ProofClient` does not prove that the counterparty has stored the `ClientState` provided in message +- `ProofConsensus` does not prove that the counterparty has the correct consensus state for this chain + +The message creates a connection for a generated connection ID with an TRYOPEN State. If a previous +connection already exists, it updates the connection state from INIT to TRYOPEN. + +### MsgConnectionOpenAck + +When a counterparty connection is initialized then a connection is opened on a light client +using the `MsgConnectionOpenAck`. + +```go +type MsgConnectionOpenAck struct { + ConnectionId string + CounterpartyConnectionId string + Version string + ClientState *types.Any // proto-packed counterparty client + ProofHeight Height + ProofTry []byte + ProofClient []byte + ProofConsensus []byte + ConsensusHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ConnectionId` is invalid (see naming requirements) +- `CounterpartyConnectionId` is invalid (see naming requirements) +- `Version` is empty +- `ClientState` is not a valid client of the executing chain +- `ProofHeight` is zero +- `ProofTry` is empty +- `ProofClient` is empty +- `ProofConsensus` is empty +- `ConsensusHeight` is zero +- `Signer` is empty +- `ProofTry` does not prove that the counterparty connection is in state TRYOPEN +- `ProofClient` does not prove that the counterparty has stored the `ClientState` provided by message +- `ProofConsensus` does not prove that the counterparty has the correct consensus state for this chain + +The message sets the connection state for the given ID to OPEN. `CounterpartyConnectionId` +should be the `ConnectionId` used by the counterparty connection. + +### MsgConnectionOpenConfirm + +When a counterparty connection is opened then a connection is opened on a light client using +the `MsgConnectionOpenConfirm`. + +```go +type MsgConnectionOpenConfirm struct { + ConnectionId string + ProofAck []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `ConnectionId` is invalid (see naming requirements) +- `ProofAck` is empty +- `ProofHeight` is zero +- `Signer` is empty +- A Connection with the given ID does not exist +- `ProofAck` does not prove that the counterparty connection is in state OPEN + +The message sets the connection state for the given ID to OPEN. + +## ICS 04 - Channels + +### MsgChannelOpenInit + +A channel handshake is initiated by a chain A using the `MsgChannelOpenInit` +message. + +```go +type MsgChannelOpenInit struct { + PortId string + Channel Channel + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `Channel` is empty +- `Signer` is empty +- A Channel End exists for the given Channel ID and Port ID + +The message creates a channel on chain A with an INIT state for a generated Channel ID +and Port ID. + +### MsgChannelOpenTry + +A channel handshake initialization attempt is acknowledged by a chain B using +the `MsgChannelOpenTry` message. + +```go +type MsgChannelOpenTry struct { + PortId string + PreviousChannelId string + Channel Channel + CounterpartyVersion string + ProofInit []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `PreviousChannelId` is not empty and invalid (see naming requirements) +- `Channel` is empty +- `CounterpartyVersion` is empty +- `ProofInit` is empty +- `ProofHeight` is zero +- `Signer` is empty +- A previous channel exists and does not match the provided parameters. +- `ProofInit` does not prove that the counterparty's Channel state is in INIT + +The message creates a channel on chain B with an TRYOPEN state for using a generated Channel ID +and given Port ID if the previous channel does not already exist. Otherwise it udates the +previous channel state from INIT to TRYOPEN. + + +### MsgChannelOpenAck + +A channel handshake is opened by a chain A using the `MsgChannelOpenAck` message. + +```go +type MsgChannelOpenAck struct { + PortId string + ChannelId string + CounterpartyChannelId string + CounterpartyVersion string + ProofTry []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `ChannelId` is invalid (see naming requirements) +- `CounterpartyChannelId` is invalid (see naming requirements) +- `CounterpartyVersion` is empty +- `ProofTry` is empty +- `ProofHeight` is zero +- `Signer` is empty +- `ProofTry` does not prove that the counterparty's Channel state is in TRYOPEN + +The message sets a channel on chain A to state OPEN for the given Channel ID and Port ID. +`CounterpartyChannelId` should be the `ChannelId` used by the counterparty channel. + +### MsgChannelOpenConfirm + +A channel handshake is confirmed and opened by a chain B using the `MsgChannelOpenConfirm` +message. + +```go +type MsgChannelOpenConfirm struct { + PortId string + ChannelId string + ProofAck []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `ChannelId` is invalid (see naming requirements) +- `ProofAck` is empty +- `ProofHeight` is zero +- `Signer` is empty +- `ProofAck` does not prove that the counterparty's Channel state is in OPEN + +The message sets a channel on chain B to state OPEN for the given Channel ID and Port ID. + +### MsgChannelCloseInit + +A channel is closed on chain A using the `MsgChannelCloseInit`. + +```go +type MsgChannelCloseInit struct { + PortId string + ChannelId string + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `ChannelId` is invalid (see naming requirements) +- `Signer` is empty +- A Channel for the given Port ID and Channel ID does not exist or is already closed + +The message closes a channel on chain A for the given Port ID and Channel ID. + +### MsgChannelCloseConfirm + +A channel is closed on chain B using the `MsgChannelCloseConfirm`. + +```go +type MsgChannelCloseConfirm struct { + PortId string + ChannelId string + ProofInit []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `PortId` is invalid (see naming requirements) +- `ChannelId` is invalid (see naming requirements) +- `ProofInit` is empty +- `ProofHeight` is zero +- `Signer` is empty +- A Channel for the given Port ID and Channel ID does not exist or is already closed +- `ProofInit` does not prove that the counterparty set its channel to state CLOSED + +The message closes a channel on chain B for the given Port ID and Channel ID. + +### MsgRecvPacket + +A packet is received on chain B using the `MsgRecvPacket`. + +```go +type MsgRecvPacket struct { + Packet Packet + Proof []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `Proof` is empty +- `ProofHeight` is zero +- `Signer` is empty +- `Packet` fails basic validation +- `Proof` does not prove that the counterparty sent the `Packet`. + +The message receives a packet on chain B. + +### MsgTimeout + +A packet is timed out on chain A using the `MsgTimeout`. + +```go +type MsgTimeout struct { + Packet Packet + Proof []byte + ProofHeight Height + NextSequenceRecv uint64 + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `Proof` is empty +- `ProofHeight` is zero +- `NextSequenceRecv` is zero +- `Signer` is empty +- `Packet` fails basic validation +- `Proof` does not prove that the packet has not been received on the counterparty chain. + +The message times out a packet that was sent on chain A and never received on chain B. + +### MsgTimeoutOnClose + +A packet is timed out on chain A due to the closure of the channel end on chain B using +the `MsgTimeoutOnClose`. + +```go +type MsgTimeoutOnClose struct { + Packet Packet + Proof []byte + ProofClose []byte + ProofHeight Height + NextSequenceRecv uint64 + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `Proof` is empty +- `ProofClose` is empty +- `ProofHeight` is zero +- `NextSequenceRecv` is zero +- `Signer` is empty +- `Packet` fails basic validation +- `Proof` does not prove that the packet has not been received on the counterparty chain. +- `ProofClose` does not prove that the counterparty channel end has been closed. + +The message times out a packet that was sent on chain A and never received on chain B. + +### MsgAcknowledgement + +A packet is acknowledged on chain A using the `MsgAcknowledgement`. + +```go +type MsgAcknowledgement struct { + Packet Packet + Acknowledgement []byte + Proof []byte + ProofHeight Height + Signer sdk.AccAddress +} +``` + +This message is expected to fail if: + +- `Proof` is empty +- `ProofHeight` is zero +- `Signer` is empty +- `Packet` fails basic validation +- `Acknowledgement` is empty +- `Proof` does not prove that the counterparty received the `Packet`. + +The message acknowledges that the packet sent from chainA was received on chain B. diff --git a/x/ibc/core/spec/05_callbacks.md b/x/ibc/core/spec/05_callbacks.md new file mode 100644 index 000000000000..dd7473802590 --- /dev/null +++ b/x/ibc/core/spec/05_callbacks.md @@ -0,0 +1,80 @@ + + +# Callbacks + +Application modules implementing the IBC module must implement the following callbacks as found in [05-port](../05-port/types/module.go). +More information on how to implement these callbacks can be found in the [implementation guide](../../../../docs/ibc/custom.md). + +```go +// IBCModule defines an interface that implements all the callbacks +// that modules must define as specified in ICS-26 +type IBCModule interface { + OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portId string, + channelId string, + channelCap *capability.Capability, + counterparty channeltypes.Counterparty, + version string, + ) error + + OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portId, + channelId string, + channelCap *capability.Capability, + counterparty channeltypes.Counterparty, + version, + counterpartyVersion string, + ) error + + OnChanOpenAck( + ctx sdk.Context, + portId, + channelId string, + counterpartyVersion string, + ) error + + OnChanOpenConfirm( + ctx sdk.Context, + portId, + channelId string, + ) error + + OnChanCloseInit( + ctx sdk.Context, + portId, + channelId string, + ) error + + OnChanCloseConfirm( + ctx sdk.Context, + portId, + channelId string, + ) error + + // OnRecvPacket must return the acknowledgement bytes + // In the case of an asynchronous acknowledgement, nil should be returned. + OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + ) (*sdk.Result, []byte, error) + + OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + ) (*sdk.Result, error) + + OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + ) (*sdk.Result, error) +} +``` diff --git a/x/ibc/core/spec/06_events.md b/x/ibc/core/spec/06_events.md new file mode 100644 index 000000000000..8a416217e1fc --- /dev/null +++ b/x/ibc/core/spec/06_events.md @@ -0,0 +1,241 @@ + + +# Events + +The IBC module emits the following events. It can be expected that the type `message`, +with an attirbute key of `action` will represent the first event for each message +being processed as emitted by the SDK's baseapp. Each IBC TAO message will +also emit its module name in the format 'ibc_sub-modulename'. + +All the events for the Channel handshakes, `SendPacket`, `RecvPacket`, `AcknowledgePacket`, +`TimeoutPacket` and `TimeoutOnClose` will emit additional events not specified here due to +callbacks to IBC applications. + +## ICS 02 - Client + +### MsgCreateClient + +| Type | Attribute Key | Attribute Value | +|---------------|------------------|-------------------| +| create_client | client_id | {clientId} | +| create_client | client_type | {clientType} | +| create_client | consensus_height | {consensusHeight} | +| message | action | create_client | +| message | module | ibc_client | + +### MsgUpdateClient + +| Type | Attribute Key | Attribute Value | +|---------------|------------------|-------------------| +| update_client | client_id | {clientId} | +| update_client | client_type | {clientType} | +| update_client | consensus_height | {consensusHeight} | +| update_client | header | {header} | +| message | action | update_client | +| message | module | ibc_client | + +### MsgSubmitMisbehaviour + +| Type | Attribute Key | Attribute Value | +|---------------------|------------------|---------------------| +| client_misbehaviour | client_id | {clientId} | +| client_misbehaviour | client_type | {clientType} | +| client_misbehaviour | consensus_height | {consensusHeight} | +| message | action | client_misbehaviour | +| message | module | evidence | +| message | sender | {senderAddress} | +| submit_evidence | evidence_hash | {evidenceHash} | + +### UpdateClientProposal + +| Type | Attribute Key | Attribute Value | +|------------------------|------------------|-------------------| +| update_client_proposal | client_id | {clientId} | +| update_client_proposal | client_type | {clientType} | +| update_client_proposal | consensus_height | {consensusHeight} | + + + +## ICS 03 - Connection + +### MsgConnectionOpenInit + +| Type | Attribute Key | Attribute Value | +|----------------------|----------------------------|-----------------------------| +| connection_open_init | connection_id | {connectionId} | +| connection_open_init | client_id | {clientId} | +| connection_open_init | counterparty_client_id | {counterparty.clientId} | +| message | action | connection_open_init | +| message | module | ibc_connection | + +### MsgConnectionOpenTry + +| Type | Attribute Key | Attribute Value | +|---------------------|----------------------------|-----------------------------| +| connection_open_try | connection_id | {connectionId} | +| connection_open_try | client_id | {clientId} | +| connection_open_try | counterparty_client_id | {counterparty.clientId | +| connection_open_try | counterparty_connection_id | {counterparty.connectionId} | +| message | action | connection_open_try | +| message | module | ibc_connection | + +### MsgConnectionOpenAck + +| Type | Attribute Key | Attribute Value | +|----------------------|----------------------------|-----------------------------| +| connection_open_ack | connection_id | {connectionId} | +| connection_open_ack | client_id | {clientId} | +| connection_open_ack | counterparty_client_id | {counterparty.clientId} | +| connection_open_ack | counterparty_connection_id | {counterparty.connectionId} | +| message | module | ibc_connection | +| message | action | connection_open_ack | + +### MsgConnectionOpenConfirm + +| Type | Attribute Key | Attribute Value | +|-------------------------|----------------------------|-----------------------------| +| connection_open_confirm | connection_id | {connectionId} | +| connection_open_confirm | client_id | {clientId} | +| connection_open_confirm | counterparty_client_id | {counterparty.clientId} | +| connection_open_confirm | counterparty_connection_id | {counterparty.connectionId} | +| message | action | connection_open_confirm | +| message | module | ibc_connection | + +## ICS 04 - Channel + +### MsgChannelOpenInit + +| Type | Attribute Key | Attribute Value | +|-------------------|-------------------------|----------------------------------| +| channel_open_init | port_id | {portId} | +| channel_open_init | channel_id | {channelId} | +| channel_open_init | counterparty_port_id | {channel.counterparty.portId} | +| channel_open_init | connection_id | {channel.connectionHops} | +| message | action | channel_open_init | +| message | module | ibc_channel | + +### MsgChannelOpenTry + +| Type | Attribute Key | Attribute Value | +|------------------|-------------------------|----------------------------------| +| channel_open_try | port_id | {portId} | +| channel_open_try | channel_id | {channelId} | +| channel_open_try | counterparty_port_id | {channel.counterparty.portId} | +| channel_open_try | counterparty_channel_id | {channel.counterparty.channelId} | +| channel_open_try | connection_id | {channel.connectionHops} | +| message | action | channel_open_try | +| message | module | ibc_channel | + +### MsgChannelOpenAck + +| Type | Attribute Key | Attribute Value | +|------------------|-------------------------|----------------------------------| +| channel_open_ack | port_id | {portId} | +| channel_open_ack | channel_id | {channelId} | +| channel_open_ack | counterparty_port_id | {channel.counterparty.portId} | +| channel_open_ack | counterparty_channel_id | {channel.counterparty.channelId} | +| channel_open_ack | connection_id | {channel.connectionHops} | +| message | action | channel_open_ack | +| message | module | ibc_channel | + +### MsgChannelOpenConfirm + +| Type | Attribute Key | Attribute Value | +|----------------------|-------------------------|----------------------------------| +| channel_open_confirm | port_id | {portId} | +| channel_open_confirm | channel_id | {channelId} | +| channel_open_confirm | counterparty_port_id | {channel.counterparty.portId} | +| channel_open_confirm | counterparty_channel_id | {channel.counterparty.channelId} | +| channel_open_confirm | connection_id | {channel.connectionHops} | +| message | module | ibc_channel | +| message | action | channel_open_confirm | + +### MsgChannelCloseInit + +| Type | Attribute Key | Attribute Value | +|--------------------|-------------------------|----------------------------------| +| channel_close_init | port_id | {portId} | +| channel_close_init | channel_id | {channelId} | +| channel_close_init | counterparty_port_id | {channel.counterparty.portId} | +| channel_close_init | counterparty_channel_id | {channel.counterparty.channelId} | +| channel_close_init | connection_id | {channel.connectionHops} | +| message | action | channel_close_init | +| message | module | ibc_channel | + +### MsgChannelCloseConfirm + +| Type | Attribute Key | Attribute Value | +|-----------------------|-------------------------|----------------------------------| +| channel_close_confirm | port_id | {portId} | +| channel_close_confirm | channel_id | {channelId} | +| channel_close_confirm | counterparty_port_id | {channel.counterparty.portId} | +| channel_close_confirm | counterparty_channel_id | {channel.counterparty.channelId} | +| channel_close_confirm | connection_id | {channel.connectionHops} | +| message | action | channel_close_confirm | +| message | module | ibc_channel | + +### SendPacket (application module call) + +| Type | Attribute Key | Attribute Value | +|-------------|--------------------------|----------------------------------| +| send_packet | packet_data | {data} | +| send_packet | packet_timeout_height | {timeoutHeight} | +| send_packet | packet_timeout_timestamp | {timeoutTimestamp} | +| send_packet | packet_sequence | {sequence} | +| send_packet | packet_src_port | {sourcePort} | +| send_packet | packet_src_channel | {sourceChannel} | +| send_packet | packet_dst_port | {destinationPort} | +| send_packet | packet_dst_channel | {destinationChannel} | +| send_packet | packet_channel_ordering | {channel.Ordering} | +| message | action | application-module-defined-field | +| message | module | ibc-channel | + +### MsgRecvPacket + +| Type | Attribute Key | Attribute Value | +|-------------|--------------------------|----------------------| +| recv_packet | packet_data | {data} | +| recv_packet | packet_ack | {acknowledgement} | +| recv_packet | packet_timeout_height | {timeoutHeight} | +| recv_packet | packet_timeout_timestamp | {timeoutTimestamp} | +| recv_packet | packet_sequence | {sequence} | +| recv_packet | packet_src_port | {sourcePort} | +| recv_packet | packet_src_channel | {sourceChannel} | +| recv_packet | packet_dst_port | {destinationPort} | +| recv_packet | packet_dst_channel | {destinationChannel} | +| recv_packet | packet_channel_ordering | {channel.Ordering} | +| message | action | recv_packet | +| message | module | ibc-channel | + +### MsgAcknowledgePacket + +| Type | Attribute Key | Attribute Value | +|--------------------|--------------------------|----------------------| +| acknowledge_packet | packet_timeout_height | {timeoutHeight} | +| acknowledge_packet | packet_timeout_timestamp | {timeoutTimestamp} | +| acknowledge_packet | packet_sequence | {sequence} | +| acknowledge_packet | packet_src_port | {sourcePort} | +| acknowledge_packet | packet_src_channel | {sourceChannel} | +| acknowledge_packet | packet_dst_port | {destinationPort} | +| acknowledge_packet | packet_dst_channel | {destinationChannel} | +| acknowledge_packet | packet_channel_ordering | {channel.Ordering} | +| message | action | acknowledge_packet | +| message | module | ibc-channel | + +### MsgTimeoutPacket & MsgTimeoutOnClose + +| Type | Attribute Key | Attribute Value | +|----------------|--------------------------|----------------------| +| timeout_packet | packet_timeout_height | {timeoutHeight} | +| timeout_packet | packet_timeout_timestamp | {timeoutTimestamp} | +| timeout_packet | packet_sequence | {sequence} | +| timeout_packet | packet_src_port | {sourcePort} | +| timeout_packet | packet_src_channel | {sourceChannel} | +| timeout_packet | packet_dst_port | {destinationPort} | +| timeout_packet | packet_dst_channel | {destinationChannel} | +| timeout_packet | packet_channel_ordering | {channel.Ordering} | +| message | action | timeout_packet | +| message | module | ibc-channel | + diff --git a/x/ibc/core/spec/07_params.md b/x/ibc/core/spec/07_params.md new file mode 100644 index 000000000000..67e79ef81ddf --- /dev/null +++ b/x/ibc/core/spec/07_params.md @@ -0,0 +1,21 @@ + + +# Parameters + +## Clients + +The ibc clients contain the following parameters: + +| Key | Type | Default Value | +|------------------|------|---------------| +| `AllowedClients` | []string | `"06-solomachine","07-tendermint"` | + +### AllowedClients + +The allowed clients parameter defines an allowlist of client types supported by the chain. A client +that is not registered on this list will fail upon creation or on genesis validation. Note that, +since the client type is an arbitrary string, chains they must not register two light clients which +return the same value for the `ClientType()` function, otherwise the allowlist check can be +bypassed. diff --git a/x/ibc/core/spec/README.md b/x/ibc/core/spec/README.md new file mode 100644 index 000000000000..f6de9749b5eb --- /dev/null +++ b/x/ibc/core/spec/README.md @@ -0,0 +1,26 @@ + + +# `ibc core` + +## Abstract + +This paper defines the implementation of the IBC protocol on the Cosmos SDK, the +changes made to the specification and where to find each specific ICS spec within +the module. + +For the general specification please refer to the [Interchain Standards](https://github.com/cosmos/ics). + +## Contents + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[State Transitions](03_state_transitions.md)** +4. **[Messages](04_messages.md)** +5. **[Callbacks](05_callbacks.md)** +6. **[Events](06_events.md)** +7. **[Params](07_params.md)** diff --git a/x/ibc/core/types/codec.go b/x/ibc/core/types/codec.go new file mode 100644 index 000000000000..db110ac9d5fd --- /dev/null +++ b/x/ibc/core/types/codec.go @@ -0,0 +1,23 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" +) + +// RegisterInterfaces registers x/ibc interfaces into protobuf Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + clienttypes.RegisterInterfaces(registry) + connectiontypes.RegisterInterfaces(registry) + channeltypes.RegisterInterfaces(registry) + solomachinetypes.RegisterInterfaces(registry) + ibctmtypes.RegisterInterfaces(registry) + localhosttypes.RegisterInterfaces(registry) + commitmenttypes.RegisterInterfaces(registry) +} diff --git a/x/ibc/core/types/genesis.go b/x/ibc/core/types/genesis.go new file mode 100644 index 000000000000..f7d78e5c1140 --- /dev/null +++ b/x/ibc/core/types/genesis.go @@ -0,0 +1,38 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +var _ codectypes.UnpackInterfacesMessage = GenesisState{} + +// DefaultGenesisState returns the ibc module's default genesis state. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + ClientGenesis: clienttypes.DefaultGenesisState(), + ConnectionGenesis: connectiontypes.DefaultGenesisState(), + ChannelGenesis: channeltypes.DefaultGenesisState(), + } +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (gs GenesisState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return gs.ClientGenesis.UnpackInterfaces(unpacker) +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs *GenesisState) Validate() error { + if err := gs.ClientGenesis.Validate(); err != nil { + return err + } + + if err := gs.ConnectionGenesis.Validate(); err != nil { + return err + } + + return gs.ChannelGenesis.Validate() +} diff --git a/x/ibc/core/types/genesis.pb.go b/x/ibc/core/types/genesis.pb.go new file mode 100644 index 000000000000..a6f2b1754052 --- /dev/null +++ b/x/ibc/core/types/genesis.pb.go @@ -0,0 +1,440 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/core/types/v1/genesis.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + types2 "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + _ "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 defines the ibc module's genesis state. +type GenesisState struct { + // ICS002 - Clients genesis state + ClientGenesis types.GenesisState `protobuf:"bytes,1,opt,name=client_genesis,json=clientGenesis,proto3" json:"client_genesis" yaml:"client_genesis"` + // ICS003 - Connections genesis state + ConnectionGenesis types1.GenesisState `protobuf:"bytes,2,opt,name=connection_genesis,json=connectionGenesis,proto3" json:"connection_genesis" yaml:"connection_genesis"` + // ICS004 - Channel genesis state + ChannelGenesis types2.GenesisState `protobuf:"bytes,3,opt,name=channel_genesis,json=channelGenesis,proto3" json:"channel_genesis" yaml:"channel_genesis"` +} + +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_b9a49c5663e6fc59, []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) GetClientGenesis() types.GenesisState { + if m != nil { + return m.ClientGenesis + } + return types.GenesisState{} +} + +func (m *GenesisState) GetConnectionGenesis() types1.GenesisState { + if m != nil { + return m.ConnectionGenesis + } + return types1.GenesisState{} +} + +func (m *GenesisState) GetChannelGenesis() types2.GenesisState { + if m != nil { + return m.ChannelGenesis + } + return types2.GenesisState{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ibc.core.types.v1.GenesisState") +} + +func init() { proto.RegisterFile("ibc/core/types/v1/genesis.proto", fileDescriptor_b9a49c5663e6fc59) } + +var fileDescriptor_b9a49c5663e6fc59 = []byte{ + // 316 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0x3d, 0x4e, 0xc3, 0x30, + 0x18, 0x86, 0x93, 0x22, 0x31, 0x04, 0x28, 0x6a, 0x04, 0x08, 0x2a, 0xe1, 0x36, 0x51, 0x07, 0x96, + 0xda, 0x0a, 0x6c, 0x8c, 0x5d, 0xba, 0x87, 0x8d, 0x05, 0x25, 0xc6, 0xa4, 0x86, 0xc4, 0xae, 0x6a, + 0x13, 0xd1, 0x5b, 0x70, 0xac, 0x8e, 0x1d, 0x11, 0x43, 0x85, 0x92, 0x1b, 0x70, 0x02, 0xd4, 0xd8, + 0xe4, 0x47, 0x9e, 0x12, 0xbd, 0x7e, 0xbe, 0xf7, 0xf9, 0x94, 0xd8, 0x19, 0xd1, 0x18, 0x23, 0xcc, + 0x57, 0x04, 0xc9, 0xf5, 0x92, 0x08, 0x94, 0x07, 0x28, 0x21, 0x8c, 0x08, 0x2a, 0xe0, 0x72, 0xc5, + 0x25, 0x77, 0x07, 0x34, 0xc6, 0x70, 0x0f, 0xc0, 0x0a, 0x80, 0x79, 0x30, 0x3c, 0x4b, 0x78, 0xc2, + 0xab, 0x53, 0xb4, 0x7f, 0x53, 0xe0, 0x70, 0x5c, 0x37, 0xe1, 0x94, 0x12, 0x26, 0x8d, 0xaa, 0xe1, + 0xa4, 0x21, 0x38, 0x63, 0x04, 0x4b, 0xca, 0x99, 0x49, 0x79, 0x0d, 0xb5, 0x88, 0x18, 0x23, 0xa9, + 0x81, 0xf8, 0xdf, 0x3d, 0xe7, 0x78, 0xae, 0x92, 0x07, 0x19, 0x49, 0xe2, 0xbe, 0x38, 0x7d, 0x25, + 0x7d, 0xd2, 0xe0, 0xa5, 0x3d, 0xb6, 0x6f, 0x8e, 0x6e, 0xc7, 0xb0, 0xde, 0x5e, 0x9d, 0xc3, 0x3c, + 0x80, 0xed, 0xc9, 0xd9, 0xf5, 0x66, 0x37, 0xb2, 0x7e, 0x77, 0xa3, 0xf3, 0x75, 0x94, 0xa5, 0xf7, + 0x7e, 0xb7, 0xc5, 0x0f, 0x4f, 0x54, 0xa0, 0x47, 0xdc, 0xdc, 0x71, 0x9b, 0xd5, 0x6b, 0x57, 0xaf, + 0x72, 0x4d, 0x5a, 0xae, 0x9a, 0x31, 0x7c, 0x9e, 0xf6, 0x5d, 0x69, 0x9f, 0xd1, 0xe6, 0x87, 0x83, + 0x26, 0xfc, 0xf7, 0xbe, 0x3a, 0xa7, 0xfa, 0x63, 0xd4, 0xd2, 0x83, 0x4a, 0xea, 0xb5, 0xa4, 0x0a, + 0x30, 0x8c, 0x40, 0x1b, 0x2f, 0xb4, 0xb1, 0xdb, 0xe3, 0x87, 0x7d, 0x9d, 0xe8, 0xa1, 0xd9, 0x7c, + 0x53, 0x00, 0x7b, 0x5b, 0x00, 0xfb, 0xa7, 0x00, 0xf6, 0x67, 0x09, 0xac, 0x6d, 0x09, 0xac, 0xaf, + 0x12, 0x58, 0x8f, 0xd3, 0x84, 0xca, 0xc5, 0x7b, 0x0c, 0x31, 0xcf, 0x10, 0xe6, 0x22, 0xe3, 0x42, + 0x3f, 0xa6, 0xe2, 0xf9, 0x0d, 0x7d, 0xa0, 0xee, 0x55, 0x8a, 0x0f, 0xab, 0x9f, 0x75, 0xf7, 0x17, + 0x00, 0x00, 0xff, 0xff, 0x90, 0x81, 0x26, 0xd1, 0x63, 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 + { + size, err := m.ChannelGenesis.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.ConnectionGenesis.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.ClientGenesis.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 + l = m.ClientGenesis.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.ConnectionGenesis.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.ChannelGenesis.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 ClientGenesis", 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 := m.ClientGenesis.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionGenesis", 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 := m.ConnectionGenesis.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChannelGenesis", 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 := m.ChannelGenesis.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/ibc/core/types/query.go b/x/ibc/core/types/query.go new file mode 100644 index 000000000000..fba69b3a1979 --- /dev/null +++ b/x/ibc/core/types/query.go @@ -0,0 +1,26 @@ +package types + +import ( + "github.com/gogo/protobuf/grpc" + + client "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connection "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channel "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// QueryServer defines the IBC interfaces that the gRPC query server must implement +type QueryServer interface { + clienttypes.QueryServer + connectiontypes.QueryServer + channeltypes.QueryServer +} + +// RegisterQueryService registers each individual IBC submodule query service +func RegisterQueryService(server grpc.Server, queryService QueryServer) { + client.RegisterQueryService(server, queryService) + connection.RegisterQueryService(server, queryService) + channel.RegisterQueryService(server, queryService) +} diff --git a/x/ibc/light-clients/06-solomachine/client/cli/cli.go b/x/ibc/light-clients/06-solomachine/client/cli/cli.go new file mode 100644 index 000000000000..ab44926204f1 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/client/cli/cli.go @@ -0,0 +1,27 @@ +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 new file mode 100644 index 000000000000..dea2d2ae7388 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/client/cli/tx.go @@ -0,0 +1,169 @@ +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/doc.go b/x/ibc/light-clients/06-solomachine/doc.go new file mode 100644 index 000000000000..3673f3c3dc08 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/doc.go @@ -0,0 +1,7 @@ +/* +Package solomachine implements a concrete `ConsensusState`, `Header`, +`Misbehaviour` and `Equivocation` types for the Solo Machine light client. +This implementation is based off the ICS 06 specification: +https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client +*/ +package solomachine diff --git a/x/ibc/light-clients/06-solomachine/module.go b/x/ibc/light-clients/06-solomachine/module.go new file mode 100644 index 000000000000..bdc7d9c04838 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/module.go @@ -0,0 +1,18 @@ +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" +) + +// Name returns the solo machine client name. +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 new file mode 100644 index 000000000000..fd8a4f71b20e --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/01_concepts.md @@ -0,0 +1,163 @@ + + +# Concepts + +## Client State + +The `ClientState` for a solo machine light client stores the latest sequence, the frozen sequence, +the latest consensus state, and client flag indicating if the client should be allowed to be updated +after a governance proposal. + +If the client is not frozen then the frozen sequence is 0. + +## Consensus State + +The consensus states stores the public key, diversifier, and timestamp of the solo machine light client. + +The diversifier is used to prevent accidental misbehaviour if the same public key is used across +different chains with the same client identifier. It should be unique to the chain the light client +is used on. + +## Public Key + +The public key can be a single public key or a multi-signature public key. The public key type used +must fulfill the tendermint public key interface (this will become the SDK public key interface in the +near future). The public key must be registered on the application codec otherwise encoding/decoding +errors will arise. The public key stored in the consensus state is represented as a protobuf `Any`. +This allows for flexibility in what other public key types can be supported in the future. + +## Counterparty Verification + +The solo machine light client can verify counterparty client state, consensus state, connection state, +channel state, packet commitments, packet acknowledgements, packet receipt absence, +and the next sequence receive. At the end of each successful verification call the light +client sequence number will be incremented. + +Successful verification requires the current public key to sign over the proof. + +## Proofs + +A solo machine proof should verify that the solomachine public key signed +over some specified data. The format for generating marshaled proofs for +the SDK's implementation of solo machine is as follows: + +1. Construct the data using the associated protobuf definition and marshal it. + +For example: + +```go +data := &ClientStateData{ + Path: []byte(path.String()), + ClientState: any, +} + +dataBz, err := cdc.MarshalBinaryBare(data) +``` + +The helper functions `...DataBytes()` in [proofs.go](../types/proofs.go) handle this +functionality. + +2. Construct the `SignBytes` and marshal it. + +For example: + +```go +signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: CLIENT, + Data: dataBz, +} + +signBz, err := cdc.MarshalBinaryBare(signBytes) +``` + +The helper functions `...SignBytes()` in [proofs.go](../types/proofs.go) handle this functionality. +The `DataType` field is used to disambiguate what type of data was signed to prevent potential +proto encoding overlap. + +3. Sign the sign bytes. Embed the signatures into either `SingleSignatureData` or `MultiSignatureData`. +Convert the `SignatureData` to proto and marshal it. + +For example: + +```go +sig, err := key.Sign(signBz) +sigData := &signing.SingleSignatureData{ + Signature: sig, +} + +protoSigData := signing.SignatureDataToProto(sigData) +bz, err := cdc.MarshalBinaryBare(protoSigData) +``` + +4. Construct a `TimestampedSignatureData` and marshal it. The marshaled result can be passed in +as the proof parameter to the verification functions. + +For example: + +```go +timestampedSignatureData := &types.TimestampedSignatureData{ + SignatureData: sigData, + Timestamp: solomachine.Time, +} + +proof, err := cdc.MarshalBinaryBare(timestampedSignatureData) +``` + +## Updates By Header + +An update by a header will only succeed if: + +- the header provided is parseable to solo machine header +- the header sequence matches the current sequence +- the header timestamp is greater than or equal to the consensus state timestamp +- the currently registered public key generated the proof + +If the update is successful: + +- the public key is updated +- the diversifier is updated +- the timestamp is updated +- the sequence is incremented by 1 +- the new consensus state is set in the client state + +## Updates By Proposal + +An update by a governance proposal will only succeed if: + +- the header provided is parseable to solo machine header +- the `AllowUpdateAfterProposal` client parameter is set to `true` +- the new header public key does not equal the 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 client is unfrozen (if it was previously frozen) + +## Misbehaviour + +Misbehaviour handling will only succeed if: + +- the misbehaviour provided is parseable to solo machine misbehaviour +- the client is not already frozen +- the current public key signed over two unique data messages at the same sequence and diversifier. + +If the misbehaviour is successfully processed: + +- the client is frozen by setting the frozen sequence to the misbehaviour sequence + +NOTE: Misbehaviour processing is data processing order dependent. A misbehaving solo machine +could update to a new public key to prevent being frozen before misbehaviour is submitted. + +## Upgrades + +Upgrades to solo machine light clients are not supported since an entirely different type of +public key can be set using normal client updates. diff --git a/x/ibc/light-clients/06-solomachine/spec/02_state.md b/x/ibc/light-clients/06-solomachine/spec/02_state.md new file mode 100644 index 000000000000..a9ff4ea5b47c --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/02_state.md @@ -0,0 +1,12 @@ + + +# State + +The solo machine light client will only store consensus states for each update by a header +or a governance proposal. The latest client state is also maintained in the store. + +These values can be found under the light client paths defined in the IBC +[core store specs](../../../core/spec/02_state.md). + 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 new file mode 100644 index 000000000000..3ca4b7017acf --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/03_state_transitions.md @@ -0,0 +1,42 @@ + + +# State Transitions + +## Client State Verification Functions + +Successful state verification by a solo machine light client will result in: + +- the sequence being incremented by 1. + +## Update By Header + +A successful update of a solo machine light client by a header 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 incremented by 1 +- the consensus state being updated (consensus state stores the public key, diversifier, and timestamp) + +## Update By Governance Proposal + +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 frozen sequence being set to zero (client is unfrozen if it was previously frozen). + +## Upgrade + +Client udgrades are not supported for the solo machine light client. No state transition occurs. + +## Misbehaviour + +Successful misbehaviour processing of a solo machine light client will result in: + +- the frozen sequence being set to the sequence the misbehaviour occurred at diff --git a/x/ibc/light-clients/06-solomachine/spec/04_messages.md b/x/ibc/light-clients/06-solomachine/spec/04_messages.md new file mode 100644 index 000000000000..465ea6229a77 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/04_messages.md @@ -0,0 +1,8 @@ + + +# Messages + +The messages used to initialize a solo machine light client are defined in the +core sub-module [02-client](../../../core/spec/04_messages.md). diff --git a/x/ibc/light-clients/06-solomachine/spec/README.md b/x/ibc/light-clients/06-solomachine/spec/README.md new file mode 100644 index 000000000000..77db1bfeee14 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/spec/README.md @@ -0,0 +1,26 @@ + + +# `solomachine` + +## Abstract + +This paper defines the implementation of the ICS06 protocol on the Cosmos SDK. For the general +specification please refer to the [ICS06 Specification](https://github.com/cosmos/ics/tree/master/spec/ics-006-solo-machine-client). + +This implementation of a solo machine light client supports single and multi-signature public +keys. The client is capable of handling public key updates by header and governance proposals. +The light client is capable of processing client misbehaviour. Proofs of the counterparty state +are generated by the solo machine client by signing over the desired state with a certain sequence, +diversifier, and timestamp. + +## Contents + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[State Transitions](03_state_transitions.md)** +4. **[Messages](04_messages.md)** diff --git a/x/ibc/light-clients/06-solomachine/types/client_state.go b/x/ibc/light-clients/06-solomachine/types/client_state.go new file mode 100644 index 000000000000..24a6582f0f23 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/client_state.go @@ -0,0 +1,491 @@ +package types + +import ( + "reflect" + + ics23 "github.com/confio/ics23/go" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance. +func NewClientState(latestSequence uint64, consensusState *ConsensusState, allowUpdateAfterProposal bool) *ClientState { + return &ClientState{ + Sequence: latestSequence, + FrozenSequence: 0, + ConsensusState: consensusState, + AllowUpdateAfterProposal: allowUpdateAfterProposal, + } +} + +// ClientType is Solo Machine. +func (cs ClientState) ClientType() string { + return exported.Solomachine +} + +// GetLatestHeight returns the latest sequence number. +// Return exported.Height to satisfy ClientState interface +// Revision number is always 0 for a solo-machine. +func (cs ClientState) GetLatestHeight() exported.Height { + return clienttypes.NewHeight(0, cs.Sequence) +} + +// IsFrozen returns true if the client is frozen. +func (cs ClientState) IsFrozen() bool { + return cs.FrozenSequence != 0 +} + +// GetFrozenHeight returns the frozen sequence of the client. +// Return exported.Height to satisfy interface +// Revision number is always 0 for a solo-machine +func (cs ClientState) GetFrozenHeight() exported.Height { + return clienttypes.NewHeight(0, cs.FrozenSequence) +} + +// GetProofSpecs returns nil proof specs since client state verification uses signatures. +func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { + return nil +} + +// Validate performs basic validation of the client state fields. +func (cs ClientState) Validate() error { + if cs.Sequence == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "sequence cannot be 0") + } + if cs.ConsensusState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be nil") + } + return cs.ConsensusState.ValidateBasic() +} + +// ZeroCustomFields returns solomachine client state with client-specific fields FrozenSequence, +// and AllowUpdateAfterProposal zeroed out +func (cs ClientState) ZeroCustomFields() exported.ClientState { + return NewClientState( + cs.Sequence, cs.ConsensusState, false, + ) +} + +// Initialize will check that initial consensus state is equal to the latest consensus state of the initial client. +func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, consState exported.ConsensusState) error { + if !reflect.DeepEqual(cs.ConsensusState, consState) { + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "consensus state in initial client does not equal initial consensus state. expected: %s, got: %s", + cs.ConsensusState, consState) + } + return nil +} + +// ExportMetadata is a no-op since solomachine does not store any metadata in client store +func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { + return nil +} + +// VerifyUpgradeAndUpdateState returns an error since solomachine client does not support upgrades +func (cs ClientState) VerifyUpgradeAndUpdateState( + _ sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, + _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, +) (exported.ClientState, exported.ConsensusState, error) { + return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade solomachine client") +} + +// VerifyClientState verifies a proof of the client state of the running chain +// stored on the solo machine. +func (cs ClientState) VerifyClientState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState exported.ClientState, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + signBz, err := ClientStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, clientState) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyClientConsensusState verifies a proof of the consensus state of the +// running chain stored on the solo machine. +func (cs ClientState) VerifyClientConsensusState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + counterpartyClientIdentifier string, + consensusHeight exported.Height, + prefix exported.Prefix, + proof []byte, + consensusState exported.ConsensusState, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + signBz, err := ConsensusStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, consensusState) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored on the target machine. +func (cs ClientState) VerifyConnectionState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + proof []byte, + connectionID string, + connectionEnd exported.ConnectionI, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) + path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) + if err != nil { + return err + } + + signBz, err := ConnectionStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, connectionEnd) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the target machine. +func (cs ClientState) VerifyChannelState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + channel exported.ChannelI, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) + if err != nil { + return err + } + + signBz, err := ChannelStateSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, channel) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketCommitment( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + _ uint64, + _ uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + packetSequence uint64, + commitmentBytes []byte, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, packetSequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) + if err != nil { + return err + } + + signBz, err := PacketCommitmentSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, commitmentBytes) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketAcknowledgement( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + _ uint64, + _ uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + packetSequence uint64, + acknowledgement []byte, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, packetSequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) + if err != nil { + return err + } + + signBz, err := PacketAcknowledgementSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, acknowledgement) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyPacketReceiptAbsence verifies a proof of the absence of an +// incoming packet receipt at the specified port, specified channel, and +// specified sequence. +func (cs ClientState) VerifyPacketReceiptAbsence( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + _ uint64, + _ uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + packetSequence uint64, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, packetSequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) + if err != nil { + return err + } + + signBz, err := PacketReceiptAbsenceSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. +func (cs ClientState) VerifyNextSequenceRecv( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + _ uint64, + _ uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, +) error { + publicKey, sigData, timestamp, sequence, err := produceVerificationArgs(cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) + if err != nil { + return err + } + + signBz, err := NextSequenceRecvSignBytes(cdc, sequence, timestamp, cs.ConsensusState.Diversifier, path, nextSequenceRecv) + if err != nil { + return err + } + + if err := VerifySignature(publicKey, signBz, sigData); err != nil { + return err + } + + cs.Sequence++ + cs.ConsensusState.Timestamp = timestamp + setClientState(store, cdc, &cs) + return nil +} + +// produceVerificationArgs perfoms the basic checks on the arguments that are +// shared between the verification functions and returns the public key of the +// consensus state, the unmarshalled proof representing the signature and timestamp +// along with the solo-machine sequence encoded in the proofHeight. +func produceVerificationArgs( + cdc codec.BinaryMarshaler, + cs ClientState, + height exported.Height, + prefix exported.Prefix, + proof []byte, +) (cryptotypes.PubKey, signing.SignatureData, uint64, uint64, error) { + if revision := height.GetRevisionNumber(); revision != 0 { + return nil, nil, 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "revision must be 0 for solomachine, got revision-number: %d", revision) + } + // sequence is encoded in the revision height of height struct + sequence := height.GetRevisionHeight() + if cs.IsFrozen() { + return nil, nil, 0, 0, clienttypes.ErrClientFrozen + } + + if prefix == nil { + return nil, nil, 0, 0, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") + } + + _, ok := prefix.(commitmenttypes.MerklePrefix) + if !ok { + return nil, nil, 0, 0, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected MerklePrefix", prefix) + } + + if proof == nil { + return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "proof cannot be empty") + } + + timestampedSigData := &TimestampedSignatureData{} + if err := cdc.UnmarshalBinaryBare(proof, timestampedSigData); err != nil { + return nil, nil, 0, 0, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", timestampedSigData) + } + + timestamp := timestampedSigData.Timestamp + + if len(timestampedSigData.SignatureData) == 0 { + return nil, nil, 0, 0, sdkerrors.Wrap(ErrInvalidProof, "signature data cannot be empty") + } + + sigData, err := UnmarshalSignatureData(cdc, timestampedSigData.SignatureData) + if err != nil { + return nil, nil, 0, 0, err + } + + if cs.ConsensusState == nil { + return nil, nil, 0, 0, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") + } + + latestSequence := cs.GetLatestHeight().GetRevisionHeight() + if latestSequence != sequence { + return nil, nil, 0, 0, sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "client state sequence != proof sequence (%d != %d)", latestSequence, sequence, + ) + } + + if cs.ConsensusState.GetTimestamp() > timestamp { + return nil, nil, 0, 0, sdkerrors.Wrapf(ErrInvalidProof, "the consensus state timestamp is greater than the signature timestamp (%d >= %d)", cs.ConsensusState.GetTimestamp(), timestamp) + } + + publicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return nil, nil, 0, 0, err + } + + return publicKey, sigData, timestamp, sequence, nil +} + +// sets the client state to the store +func setClientState(store sdk.KVStore, cdc codec.BinaryMarshaler, clientState exported.ClientState) { + bz := clienttypes.MustMarshalClientState(cdc, clientState) + store.Set([]byte(host.KeyClientState), bz) +} diff --git a/x/ibc/light-clients/06-solomachine/types/client_state_test.go b/x/ibc/light-clients/06-solomachine/types/client_state_test.go new file mode 100644 index 000000000000..4f6c195c898e --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/client_state_test.go @@ -0,0 +1,912 @@ +package types_test + +import ( + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +const ( + counterpartyClientIdentifier = "chainA" + testConnectionID = "connectionid" + testChannelID = "testchannelid" + testPortID = "testportid" +) + +var ( + prefix = commitmenttypes.NewMerklePrefix([]byte("ibc")) + consensusHeight = clienttypes.ZeroHeight() +) + +func (suite *SoloMachineTestSuite) TestClientStateValidateBasic() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + clientState *types.ClientState + expPass bool + }{ + { + "valid client state", + solomachine.ClientState(), + true, + }, + { + "empty ClientState", + &types.ClientState{}, + false, + }, + { + "sequence is zero", + types.NewClientState(0, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, solomachine.Time}, false), + false, + }, + { + "timestamp is zero", + types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, solomachine.Diversifier, 0}, false), + false, + }, + { + "diversifier is blank", + types.NewClientState(1, &types.ConsensusState{solomachine.ConsensusState().PublicKey, " ", 1}, false), + false, + }, + { + "pubkey is empty", + types.NewClientState(1, &types.ConsensusState{nil, solomachine.Diversifier, solomachine.Time}, false), + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + err := tc.clientState.Validate() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestInitialize() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + malleatedConsensus := solomachine.ClientState().ConsensusState + malleatedConsensus.Timestamp = malleatedConsensus.Timestamp + 10 + + testCases := []struct { + name string + consState exported.ConsensusState + expPass bool + }{ + { + "valid consensus state", + solomachine.ConsensusState(), + true, + }, + { + "nil consensus state", + nil, + false, + }, + { + "invalid consensus state: Tendermint consensus state", + &ibctmtypes.ConsensusState{}, + false, + }, + { + "invalid consensus state: consensus state does not match consensus state in client", + malleatedConsensus, + false, + }, + } + + for _, tc := range testCases { + err := solomachine.ClientState().Initialize( + suite.chainA.GetContext(), suite.chainA.Codec, + suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), "solomachine"), + tc.consState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid testcase: %s failed", tc.name) + } else { + suite.Require().Error(err, "invalid testcase: %s passed", tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientState() { + // create client for tendermint so we can use client state for verification + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState := suite.chainA.GetClientState(clientA) + path := suite.solomachine.GetClientStatePath(counterpartyClientIdentifier) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + value, err := types.ClientStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, clientState) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + nil, + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "consensus state in client state is nil", + types.NewClientState(1, nil, false), + prefix, + proof, + false, + }, + { + "client state latest height is less than sequence", + types.NewClientState(solomachine.Sequence-1, + &types.ConsensusState{ + Timestamp: solomachine.Time, + PublicKey: solomachine.ConsensusState().PublicKey, + }, false), + prefix, + proof, + false, + }, + { + "consensus state timestamp is greater than signature", + types.NewClientState(solomachine.Sequence, + &types.ConsensusState{ + Timestamp: solomachine.Time + 1, + PublicKey: solomachine.ConsensusState().PublicKey, + }, false), + prefix, + proof, + false, + }, + + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + var expSeq uint64 + if tc.clientState.ConsensusState != nil { + expSeq = tc.clientState.Sequence + 1 + } + + err := tc.clientState.VerifyClientState( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, counterpartyClientIdentifier, tc.proof, clientState, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyClientConsensusState() { + // create client for tendermint so we can use consensus state for verification + clientA, _ := suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + clientState := suite.chainA.GetClientState(clientA) + consensusState, found := suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight()) + suite.Require().True(found) + + path := suite.solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + value, err := types.ConsensusStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, consensusState) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + nil, + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "consensus state in client state is nil", + types.NewClientState(1, nil, false), + prefix, + proof, + false, + }, + { + "client state latest height is less than sequence", + types.NewClientState(solomachine.Sequence-1, + &types.ConsensusState{ + Timestamp: solomachine.Time, + PublicKey: solomachine.ConsensusState().PublicKey, + }, false), + prefix, + proof, + false, + }, + { + "consensus state timestamp is greater than signature", + types.NewClientState(solomachine.Sequence, + &types.ConsensusState{ + Timestamp: solomachine.Time + 1, + PublicKey: solomachine.ConsensusState().PublicKey, + }, false), + prefix, + proof, + false, + }, + + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + var expSeq uint64 + if tc.clientState.ConsensusState != nil { + expSeq = tc.clientState.Sequence + 1 + } + + err := tc.clientState.VerifyClientConsensusState( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), counterpartyClientIdentifier, consensusHeight, tc.prefix, tc.proof, consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %s", suite.GetSequenceFromStore(), tc.name) + } else { + suite.Require().Error(err) + } + }) + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyConnectionState() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, prefix) + conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) + + path := suite.solomachine.GetConnectionStatePath(testConnectionID) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + value, err := types.ConnectionStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, conn) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + commitmenttypes.NewMerklePrefix([]byte{}), + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyConnectionState( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testConnectionID, conn, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyChannelState() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + + path := suite.solomachine.GetChannelStatePath(testPortID, testChannelID) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + value, err := types.ChannelStateSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ch) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + nil, + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyChannelState( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), tc.prefix, tc.proof, testPortID, testChannelID, ch, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyPacketCommitment() { + commitmentBytes := []byte("COMMITMENT BYTES") + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + path := solomachine.GetPacketCommitmentPath(testPortID, testChannelID) + + value, err := types.PacketCommitmentSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, commitmentBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + commitmenttypes.NewMerklePrefix([]byte{}), + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyPacketCommitment( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, commitmentBytes, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyPacketAcknowledgement() { + ack := []byte("ACK") + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + path := solomachine.GetPacketAcknowledgementPath(testPortID, testChannelID) + + value, err := types.PacketAcknowledgementSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, ack) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + commitmenttypes.NewMerklePrefix([]byte{}), + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyPacketAcknowledgement( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, ack, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyPacketReceiptAbsence() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + // absence uses receipt path as well + path := solomachine.GetPacketReceiptPath(testPortID, testChannelID) + + value, err := types.PacketReceiptAbsenceSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + commitmenttypes.NewMerklePrefix([]byte{}), + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyPacketReceiptAbsence( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, solomachine.Sequence, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} + +func (suite *SoloMachineTestSuite) TestVerifyNextSeqRecv() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + nextSeqRecv := solomachine.Sequence + 1 + path := solomachine.GetNextSequenceRecvPath(testPortID, testChannelID) + + value, err := types.NextSequenceRecvSignBytes(suite.chainA.Codec, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nextSeqRecv) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(value) + signatureDoc := &types.TimestampedSignatureData{ + SignatureData: sig, + Timestamp: solomachine.Time, + } + + proof, err := suite.chainA.Codec.MarshalBinaryBare(signatureDoc) + suite.Require().NoError(err) + + testCases := []struct { + name string + clientState *types.ClientState + prefix exported.Prefix + proof []byte + expPass bool + }{ + { + "successful verification", + solomachine.ClientState(), + prefix, + proof, + true, + }, + { + "ApplyPrefix failed", + solomachine.ClientState(), + commitmenttypes.NewMerklePrefix([]byte{}), + proof, + false, + }, + { + "client is frozen", + &types.ClientState{ + Sequence: 1, + FrozenSequence: 1, + ConsensusState: solomachine.ConsensusState(), + AllowUpdateAfterProposal: false, + }, + prefix, + proof, + false, + }, + { + "proof is nil", + solomachine.ClientState(), + prefix, + nil, + false, + }, + { + "proof verification failed", + solomachine.ClientState(), + prefix, + suite.GetInvalidProof(), + false, + }, + } + + for i, tc := range testCases { + tc := tc + + expSeq := tc.clientState.Sequence + 1 + + err := tc.clientState.VerifyNextSequenceRecv( + suite.store, suite.chainA.Codec, solomachine.GetHeight(), 0, 0, tc.prefix, tc.proof, testPortID, testChannelID, nextSeqRecv, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(expSeq, suite.GetSequenceFromStore(), "sequence not updated in the store (%d) on valid test case %d: %s", suite.GetSequenceFromStore(), i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/codec.go b/x/ibc/light-clients/06-solomachine/types/codec.go new file mode 100644 index 000000000000..313a910ca96b --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/codec.go @@ -0,0 +1,130 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces register the ibc channel submodule interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*exported.ClientState)(nil), + &ClientState{}, + ) + registry.RegisterImplementations( + (*exported.ConsensusState)(nil), + &ConsensusState{}, + ) + registry.RegisterImplementations( + (*exported.Header)(nil), + &Header{}, + ) + registry.RegisterImplementations( + (*exported.Misbehaviour)(nil), + &Misbehaviour{}, + ) +} + +func UnmarshalSignatureData(cdc codec.BinaryMarshaler, data []byte) (signing.SignatureData, error) { + protoSigData := &signing.SignatureDescriptor_Data{} + if err := cdc.UnmarshalBinaryBare(data, protoSigData); err != nil { + return nil, sdkerrors.Wrapf(err, "failed to unmarshal proof into type %T", protoSigData) + } + + sigData := signing.SignatureDataFromProto(protoSigData) + + return sigData, nil +} + +// UnmarshalDataByType attempts to unmarshal the data to the specified type. An error is +// return if it fails. +func UnmarshalDataByType(cdc codec.BinaryMarshaler, dataType DataType, data []byte) (Data, error) { + if len(data) == 0 { + return nil, sdkerrors.Wrap(ErrInvalidSignatureAndData, "data cannot be empty") + } + + switch dataType { + case UNSPECIFIED: + return nil, sdkerrors.Wrap(ErrInvalidDataType, "data type cannot be UNSPECIFIED") + + case CLIENT: + clientData := &ClientStateData{} + if err := cdc.UnmarshalBinaryBare(data, clientData); err != nil { + return nil, err + } + + // unpack any + if _, err := clienttypes.UnpackClientState(clientData.ClientState); err != nil { + return nil, err + } + return clientData, nil + + case CONSENSUS: + consensusData := &ConsensusStateData{} + if err := cdc.UnmarshalBinaryBare(data, consensusData); err != nil { + return nil, err + } + + // unpack any + if _, err := clienttypes.UnpackConsensusState(consensusData.ConsensusState); err != nil { + return nil, err + } + return consensusData, nil + + case CONNECTION: + connectionData := &ConnectionStateData{} + if err := cdc.UnmarshalBinaryBare(data, connectionData); err != nil { + return nil, err + } + + return connectionData, nil + + case CHANNEL: + channelData := &ChannelStateData{} + if err := cdc.UnmarshalBinaryBare(data, channelData); err != nil { + return nil, err + } + + return channelData, nil + + case PACKETCOMMITMENT: + commitmentData := &PacketCommitmentData{} + if err := cdc.UnmarshalBinaryBare(data, commitmentData); err != nil { + return nil, err + } + + return commitmentData, nil + + case PACKETACKNOWLEDGEMENT: + ackData := &PacketAcknowledgementData{} + if err := cdc.UnmarshalBinaryBare(data, ackData); err != nil { + return nil, err + } + + return ackData, nil + + case PACKETRECEIPTABSENCE: + receiptAbsenceData := &PacketReceiptAbsenceData{} + if err := cdc.UnmarshalBinaryBare(data, receiptAbsenceData); err != nil { + return nil, err + } + + return receiptAbsenceData, nil + + case NEXTSEQUENCERECV: + nextSeqRecvData := &NextSequenceRecvData{} + if err := cdc.UnmarshalBinaryBare(data, nextSeqRecvData); err != nil { + return nil, err + } + + return nextSeqRecvData, nil + + default: + return nil, sdkerrors.Wrapf(ErrInvalidDataType, "unsupported data type %T", dataType) + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/codec_test.go b/x/ibc/light-clients/06-solomachine/types/codec_test.go new file mode 100644 index 000000000000..70be186a10bb --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/codec_test.go @@ -0,0 +1,190 @@ +package types_test + +import ( + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite SoloMachineTestSuite) TestUnmarshalDataByType() { + var ( + data []byte + err error + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + cdc := suite.chainA.App.AppCodec() + cases := []struct { + name string + dataType types.DataType + malleate func() + expPass bool + }{ + { + "empty data", types.CLIENT, func() { + data = []byte{} + }, false, + }, + { + "unspecified", types.UNSPECIFIED, func() { + path := solomachine.GetClientStatePath(counterpartyClientIdentifier) + data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) + suite.Require().NoError(err) + }, false, + }, + { + "client", types.CLIENT, func() { + path := solomachine.GetClientStatePath(counterpartyClientIdentifier) + data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) + suite.Require().NoError(err) + }, true, + }, + { + "bad client (provides consensus state data)", types.CLIENT, func() { + path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) + data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) + suite.Require().NoError(err) + }, false, + }, + { + "consensus", types.CONSENSUS, func() { + path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, clienttypes.NewHeight(0, 5)) + data, err = types.ConsensusStateDataBytes(cdc, path, solomachine.ConsensusState()) + suite.Require().NoError(err) + + }, true, + }, + { + "bad consensus (provides client state data)", types.CONSENSUS, func() { + path := solomachine.GetClientStatePath(counterpartyClientIdentifier) + data, err = types.ClientStateDataBytes(cdc, path, solomachine.ClientState()) + suite.Require().NoError(err) + }, false, + }, + { + "connection", types.CONNECTION, func() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, prefix) + conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) + path := solomachine.GetConnectionStatePath("connectionID") + + data, err = types.ConnectionStateDataBytes(cdc, path, conn) + suite.Require().NoError(err) + + }, true, + }, + { + "bad connection (uses channel data)", types.CONNECTION, func() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + path := solomachine.GetChannelStatePath("portID", "channelID") + + data, err = types.ChannelStateDataBytes(cdc, path, ch) + suite.Require().NoError(err) + }, false, + }, + { + "channel", types.CHANNEL, func() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + path := solomachine.GetChannelStatePath("portID", "channelID") + + data, err = types.ChannelStateDataBytes(cdc, path, ch) + suite.Require().NoError(err) + }, true, + }, + { + "bad channel (uses connection data)", types.CHANNEL, func() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, prefix) + conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions()), 0) + path := solomachine.GetConnectionStatePath("connectionID") + + data, err = types.ConnectionStateDataBytes(cdc, path, conn) + suite.Require().NoError(err) + + }, false, + }, + { + "packet commitment", types.PACKETCOMMITMENT, func() { + commitment := []byte("packet commitment") + path := solomachine.GetPacketCommitmentPath("portID", "channelID") + + data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, true, + }, + { + "bad packet commitment (uses next seq recv)", types.PACKETCOMMITMENT, func() { + path := solomachine.GetNextSequenceRecvPath("portID", "channelID") + + data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, false, + }, + { + "packet acknowledgement", types.PACKETACKNOWLEDGEMENT, func() { + commitment := []byte("packet acknowledgement") + path := solomachine.GetPacketAcknowledgementPath("portID", "channelID") + + data, err = types.PacketAcknowledgementDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, true, + }, + { + "bad packet acknowledgement (uses next sequence recv)", types.PACKETACKNOWLEDGEMENT, func() { + path := solomachine.GetNextSequenceRecvPath("portID", "channelID") + + data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, false, + }, + { + "packet acknowledgement absence", types.PACKETRECEIPTABSENCE, func() { + path := solomachine.GetPacketReceiptPath("portID", "channelID") + + data, err = types.PacketReceiptAbsenceDataBytes(cdc, path) + suite.Require().NoError(err) + }, true, + }, + { + "next sequence recv", types.NEXTSEQUENCERECV, func() { + path := solomachine.GetNextSequenceRecvPath("portID", "channelID") + + data, err = types.NextSequenceRecvDataBytes(cdc, path, 10) + suite.Require().NoError(err) + }, true, + }, + { + "bad next sequence recv (uses packet commitment)", types.NEXTSEQUENCERECV, func() { + commitment := []byte("packet commitment") + path := solomachine.GetPacketCommitmentPath("portID", "channelID") + + data, err = types.PacketCommitmentDataBytes(cdc, path, commitment) + suite.Require().NoError(err) + }, false, + }, + } + + for _, tc := range cases { + tc := tc + + suite.Run(tc.name, func() { + tc.malleate() + + data, err := types.UnmarshalDataByType(cdc, tc.dataType, data) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(data) + } else { + suite.Require().Error(err) + suite.Require().Nil(data) + } + }) + } + } + +} diff --git a/x/ibc/light-clients/06-solomachine/types/consensus_state.go b/x/ibc/light-clients/06-solomachine/types/consensus_state.go new file mode 100644 index 000000000000..7d6d09cd046b --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/consensus_state.go @@ -0,0 +1,60 @@ +package types + +import ( + "strings" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.ConsensusState = &ConsensusState{} + +// ClientType returns Solo Machine type. +func (ConsensusState) ClientType() string { + return exported.Solomachine +} + +// GetTimestamp returns zero. +func (cs ConsensusState) GetTimestamp() uint64 { + return cs.Timestamp +} + +// GetRoot returns nil since solo machines do not have roots. +func (cs ConsensusState) GetRoot() exported.Root { + return nil +} + +// GetPubKey unmarshals the public key into a cryptotypes.PubKey type. +// An error is returned if the public key is nil or the cached value +// is not a PubKey. +func (cs ConsensusState) GetPubKey() (cryptotypes.PubKey, error) { + if cs.PublicKey == nil { + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey cannot be nil") + } + + publicKey, ok := cs.PublicKey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state PublicKey is not cryptotypes.PubKey") + } + + return publicKey, nil +} + +// ValidateBasic defines basic validation for the solo machine consensus state. +func (cs ConsensusState) ValidateBasic() error { + if cs.Timestamp == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "timestamp cannot be 0") + } + if cs.Diversifier != "" && strings.TrimSpace(cs.Diversifier) == "" { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "diversifier cannot contain only spaces") + } + + publicKey, err := cs.GetPubKey() + if err != nil || publicKey == nil || len(publicKey.Bytes()) == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "public key cannot be empty") + } + + return nil +} diff --git a/x/ibc/light-clients/06-solomachine/types/consensus_state_test.go b/x/ibc/light-clients/06-solomachine/types/consensus_state_test.go new file mode 100644 index 000000000000..e0c22f95957b --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/consensus_state_test.go @@ -0,0 +1,75 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestConsensusState() { + consensusState := suite.solomachine.ConsensusState() + + suite.Require().Equal(exported.Solomachine, consensusState.ClientType()) + suite.Require().Equal(suite.solomachine.Time, consensusState.GetTimestamp()) + suite.Require().Nil(consensusState.GetRoot()) +} + +func (suite *SoloMachineTestSuite) TestConsensusStateValidateBasic() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + consensusState *types.ConsensusState + expPass bool + }{ + { + "valid consensus state", + solomachine.ConsensusState(), + true, + }, + { + "timestamp is zero", + &types.ConsensusState{ + PublicKey: solomachine.ConsensusState().PublicKey, + Timestamp: 0, + Diversifier: solomachine.Diversifier, + }, + false, + }, + { + "diversifier is blank", + &types.ConsensusState{ + PublicKey: solomachine.ConsensusState().PublicKey, + Timestamp: solomachine.Time, + Diversifier: " ", + }, + false, + }, + { + "pubkey is nil", + &types.ConsensusState{ + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + PublicKey: nil, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + err := tc.consensusState.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/errors.go b/x/ibc/light-clients/06-solomachine/types/errors.go new file mode 100644 index 000000000000..3e27f60732ad --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/errors.go @@ -0,0 +1,18 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + SubModuleName = "solo machine" +) + +var ( + ErrInvalidHeader = sdkerrors.Register(SubModuleName, 2, "invalid header") + ErrInvalidSequence = sdkerrors.Register(SubModuleName, 3, "invalid sequence") + ErrInvalidSignatureAndData = sdkerrors.Register(SubModuleName, 4, "invalid signature and data") + ErrSignatureVerificationFailed = sdkerrors.Register(SubModuleName, 5, "signature verification failed") + ErrInvalidProof = sdkerrors.Register(SubModuleName, 6, "invalid solo machine proof") + ErrInvalidDataType = sdkerrors.Register(SubModuleName, 7, "invalid data type") +) diff --git a/x/ibc/light-clients/06-solomachine/types/header.go b/x/ibc/light-clients/06-solomachine/types/header.go new file mode 100644 index 000000000000..f9c5f176fdaa --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/header.go @@ -0,0 +1,67 @@ +package types + +import ( + "strings" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.Header = &Header{} + +// ClientType defines that the Header is a Solo Machine. +func (Header) ClientType() string { + return exported.Solomachine +} + +// GetHeight returns the current sequence number as the height. +// Return clientexported.Height to satisfy interface +// Revision number is always 0 for a solo-machine +func (h Header) GetHeight() exported.Height { + return clienttypes.NewHeight(0, h.Sequence) +} + +// GetPubKey unmarshals the new public key into a cryptotypes.PubKey type. +// An error is returned if the new public key is nil or the cached value +// is not a PubKey. +func (h Header) GetPubKey() (cryptotypes.PubKey, error) { + if h.NewPublicKey == nil { + return nil, sdkerrors.Wrap(ErrInvalidHeader, "header NewPublicKey cannot be nil") + } + + publicKey, ok := h.NewPublicKey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, sdkerrors.Wrap(ErrInvalidHeader, "header NewPublicKey is not cryptotypes.PubKey") + } + + return publicKey, nil +} + +// ValidateBasic ensures that the sequence, signature and public key have all +// been initialized. +func (h Header) ValidateBasic() error { + if h.Sequence == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "sequence number cannot be zero") + } + + if h.Timestamp == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "timestamp cannot be zero") + } + + if h.NewDiversifier != "" && strings.TrimSpace(h.NewDiversifier) == "" { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "diversifier cannot contain only spaces") + } + + if len(h.Signature) == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "signature cannot be empty") + } + + newPublicKey, err := h.GetPubKey() + if err != nil || newPublicKey == nil || len(newPublicKey.Bytes()) == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "new public key cannot be empty") + } + + return nil +} diff --git a/x/ibc/light-clients/06-solomachine/types/header_test.go b/x/ibc/light-clients/06-solomachine/types/header_test.go new file mode 100644 index 000000000000..a5ca45e8aa88 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/header_test.go @@ -0,0 +1,98 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestHeaderValidateBasic() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + header := solomachine.CreateHeader() + + cases := []struct { + name string + header *types.Header + expPass bool + }{ + { + "valid header", + header, + true, + }, + { + "sequence is zero", + &types.Header{ + Sequence: 0, + Timestamp: header.Timestamp, + Signature: header.Signature, + NewPublicKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + }, + false, + }, + { + "timestamp is zero", + &types.Header{ + Sequence: header.Sequence, + Timestamp: 0, + Signature: header.Signature, + NewPublicKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + }, + false, + }, + { + "signature is empty", + &types.Header{ + Sequence: header.Sequence, + Timestamp: header.Timestamp, + Signature: []byte{}, + NewPublicKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + }, + false, + }, + { + "diversifier contains only spaces", + &types.Header{ + Sequence: header.Sequence, + Timestamp: header.Timestamp, + Signature: header.Signature, + NewPublicKey: header.NewPublicKey, + NewDiversifier: " ", + }, + false, + }, + { + "public key is nil", + &types.Header{ + Sequence: header.Sequence, + Timestamp: header.Timestamp, + Signature: header.Signature, + NewPublicKey: nil, + NewDiversifier: header.NewDiversifier, + }, + false, + }, + } + + suite.Require().Equal(exported.Solomachine, header.ClientType()) + + for _, tc := range cases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.header.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go new file mode 100644 index 000000000000..65b8a4ee4753 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour.go @@ -0,0 +1,85 @@ +package types + +import ( + "bytes" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + _ exported.Misbehaviour = (*Misbehaviour)(nil) +) + +// ClientType is a Solo Machine light client. +func (misbehaviour Misbehaviour) ClientType() string { + return exported.Solomachine +} + +// GetClientID returns the ID of the client that committed a misbehaviour. +func (misbehaviour Misbehaviour) GetClientID() string { + return misbehaviour.ClientId +} + +// Type implements Evidence interface. +func (misbehaviour Misbehaviour) Type() string { + return exported.TypeClientMisbehaviour +} + +// GetHeight returns the sequence at which misbehaviour occurred. +// Return exported.Height to satisfy interface +// Revision number is always 0 for a solo-machine +func (misbehaviour Misbehaviour) GetHeight() exported.Height { + return clienttypes.NewHeight(0, misbehaviour.Sequence) +} + +// ValidateBasic implements Evidence interface. +func (misbehaviour Misbehaviour) ValidateBasic() error { + if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { + return sdkerrors.Wrap(err, "invalid client identifier for solo machine") + } + + if misbehaviour.Sequence == 0 { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "sequence cannot be 0") + } + + if err := misbehaviour.SignatureOne.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "signature one failed basic validation") + } + + if err := misbehaviour.SignatureTwo.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "signature two failed basic validation") + } + + // misbehaviour signatures cannot be identical + if bytes.Equal(misbehaviour.SignatureOne.Signature, misbehaviour.SignatureTwo.Signature) { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signatures cannot be equal") + } + + // message data signed cannot be identical + if bytes.Equal(misbehaviour.SignatureOne.Data, misbehaviour.SignatureTwo.Data) { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "misbehaviour signature data must be signed over different messages") + } + + return nil +} + +// ValidateBasic ensures that the signature and data fields are non-empty. +func (sd SignatureAndData) ValidateBasic() error { + if len(sd.Signature) == 0 { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "signature cannot be empty") + } + if len(sd.Data) == 0 { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data for signature cannot be empty") + } + if sd.DataType == UNSPECIFIED { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "data type cannot be UNSPECIFIED") + } + if sd.Timestamp == 0 { + return sdkerrors.Wrap(ErrInvalidSignatureAndData, "timestamp cannot be 0") + } + + return nil +} diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go new file mode 100644 index 000000000000..ce5d6351c48f --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle.go @@ -0,0 +1,92 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CheckMisbehaviourAndUpdateState determines whether or not the currently registered +// public key signed over two different messages with the same sequence. If this is true +// the client state is updated to a frozen status. +// NOTE: Misbehaviour is not tracked for previous public keys, a solo machine may update to +// a new public key before the misbehaviour is processed. Therefore, misbehaviour is data +// order processing dependent. +func (cs ClientState) CheckMisbehaviourAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryMarshaler, + clientStore sdk.KVStore, + misbehaviour exported.Misbehaviour, +) (exported.ClientState, error) { + + soloMisbehaviour, ok := misbehaviour.(*Misbehaviour) + if !ok { + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidClientType, + "misbehaviour type %T, expected %T", misbehaviour, &Misbehaviour{}, + ) + } + + if cs.IsFrozen() { + return nil, sdkerrors.Wrapf(clienttypes.ErrClientFrozen, "client is already frozen") + } + + // NOTE: a check that the misbehaviour message data are not equal is done by + // misbehaviour.ValidateBasic which is called by the 02-client keeper. + + // verify first signature + if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureOne); err != nil { + return nil, sdkerrors.Wrap(err, "failed to verify signature one") + } + + // verify second signature + if err := verifySignatureAndData(cdc, cs, soloMisbehaviour, soloMisbehaviour.SignatureTwo); err != nil { + return nil, sdkerrors.Wrap(err, "failed to verify signature two") + } + + cs.FrozenSequence = soloMisbehaviour.Sequence + return &cs, nil +} + +// verifySignatureAndData verifies that the currently registered public key has signed +// over the provided data and that the data is valid. The data is valid if it can be +// unmarshaled into the specified data type. +func verifySignatureAndData(cdc codec.BinaryMarshaler, clientState ClientState, misbehaviour *Misbehaviour, sigAndData *SignatureAndData) error { + + // do not check misbehaviour timestamp since we want to allow processing of past misbehaviour + + // ensure data can be unmarshaled to the specified data type + if _, err := UnmarshalDataByType(cdc, sigAndData.DataType, sigAndData.Data); err != nil { + return err + } + + data, err := MisbehaviourSignBytes( + cdc, + misbehaviour.Sequence, sigAndData.Timestamp, + clientState.ConsensusState.Diversifier, + sigAndData.DataType, + sigAndData.Data, + ) + if err != nil { + return err + } + + sigData, err := UnmarshalSignatureData(cdc, sigAndData.Signature) + if err != nil { + return err + } + + publicKey, err := clientState.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return err + } + + return nil + +} diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go new file mode 100644 index 000000000000..97ce22a3ed9c --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_handle_test.go @@ -0,0 +1,275 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestCheckMisbehaviourAndUpdateState() { + var ( + clientState exported.ClientState + misbehaviour exported.Misbehaviour + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "valid misbehaviour", + func() { + clientState = solomachine.ClientState() + misbehaviour = solomachine.CreateMisbehaviour() + }, + true, + }, + { + "old misbehaviour is successful (timestamp is less than current consensus state)", + func() { + clientState = solomachine.ClientState() + solomachine.Time = solomachine.Time - 5 + misbehaviour = solomachine.CreateMisbehaviour() + }, true, + }, + { + "client is frozen", + func() { + cs := solomachine.ClientState() + cs.FrozenSequence = 1 + clientState = cs + misbehaviour = solomachine.CreateMisbehaviour() + }, + false, + }, + { + "wrong client state type", + func() { + clientState = &ibctmtypes.ClientState{} + misbehaviour = solomachine.CreateMisbehaviour() + }, + false, + }, + { + "invalid misbehaviour type", + func() { + clientState = solomachine.ClientState() + misbehaviour = &ibctmtypes.Misbehaviour{} + }, + false, + }, + { + "invalid SignatureOne SignatureData", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureOne.Signature = suite.GetInvalidProof() + misbehaviour = m + }, false, + }, + { + "invalid SignatureTwo SignatureData", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureTwo.Signature = suite.GetInvalidProof() + misbehaviour = m + }, false, + }, + { + "invalid SignatureOne timestamp", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureOne.Timestamp = 1000000000000 + misbehaviour = m + }, false, + }, + { + "invalid SignatureTwo timestamp", + func() { + clientState = solomachine.ClientState() + m := solomachine.CreateMisbehaviour() + + m.SignatureTwo.Timestamp = 1000000000000 + misbehaviour = m + }, false, + }, + { + "invalid first signature data", + func() { + clientState = solomachine.ClientState() + + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + msg := []byte("DATA ONE") + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + misbehaviour = m + }, + false, + }, + { + "invalid second signature data", + func() { + clientState = solomachine.ClientState() + + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + msg := []byte("DATA TWO") + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + misbehaviour = m + }, + false, + }, + { + "wrong pubkey generates first signature", + func() { + clientState = solomachine.ClientState() + badMisbehaviour := solomachine.CreateMisbehaviour() + + // update public key to a new one + solomachine.CreateHeader() + m := solomachine.CreateMisbehaviour() + + // set SignatureOne to use the wrong signature + m.SignatureOne = badMisbehaviour.SignatureOne + misbehaviour = m + }, false, + }, + { + "wrong pubkey generates second signature", + func() { + clientState = solomachine.ClientState() + badMisbehaviour := solomachine.CreateMisbehaviour() + + // update public key to a new one + solomachine.CreateHeader() + m := solomachine.CreateMisbehaviour() + + // set SignatureTwo to use the wrong signature + m.SignatureTwo = badMisbehaviour.SignatureTwo + misbehaviour = m + }, false, + }, + + { + "signatures sign over different sequence", + func() { + clientState = solomachine.ClientState() + + // store in temp before assigning to interface type + m := solomachine.CreateMisbehaviour() + + // Signature One + msg := []byte("DATA ONE") + // sequence used is plus 1 + signBytes := &types.SignBytes{ + Sequence: solomachine.Sequence + 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + + data, err := suite.chainA.Codec.MarshalBinaryBare(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(data) + + m.SignatureOne.Signature = sig + m.SignatureOne.Data = msg + + // Signature Two + msg = []byte("DATA TWO") + // sequence used is minus 1 + + signBytes = &types.SignBytes{ + Sequence: solomachine.Sequence - 1, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: msg, + } + data, err = suite.chainA.Codec.MarshalBinaryBare(signBytes) + suite.Require().NoError(err) + + sig = solomachine.GenerateSignature(data) + + m.SignatureTwo.Signature = sig + m.SignatureTwo.Data = msg + + misbehaviour = m + + }, + false, + }, + { + "consensus state pubkey is nil", + func() { + cs := solomachine.ClientState() + cs.ConsensusState.PublicKey = nil + clientState = cs + misbehaviour = solomachine.CreateMisbehaviour() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // setup test + tc.setup() + + clientState, err := clientState.CheckMisbehaviourAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), suite.store, misbehaviour) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().True(clientState.IsFrozen(), "client not frozen") + } else { + suite.Require().Error(err) + suite.Require().Nil(clientState) + } + }) + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go b/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go new file mode 100644 index 000000000000..7c1f9168aaf6 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/misbehaviour_test.go @@ -0,0 +1,132 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestMisbehaviour() { + misbehaviour := suite.solomachine.CreateMisbehaviour() + + suite.Require().Equal(exported.Solomachine, misbehaviour.ClientType()) + suite.Require().Equal(suite.solomachine.ClientID, misbehaviour.GetClientID()) + suite.Require().Equal(uint64(0), misbehaviour.GetHeight().GetRevisionNumber()) + suite.Require().Equal(suite.solomachine.Sequence, misbehaviour.GetHeight().GetRevisionHeight()) +} + +func (suite *SoloMachineTestSuite) TestMisbehaviourValidateBasic() { + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleateMisbehaviour func(misbehaviour *types.Misbehaviour) + expPass bool + }{ + { + "valid misbehaviour", + func(*types.Misbehaviour) {}, + true, + }, + { + "invalid client ID", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.ClientId = "(badclientid)" + }, + false, + }, + { + "sequence is zero", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.Sequence = 0 + }, + false, + }, + { + "signature one sig is empty", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Signature = []byte{} + }, + false, + }, + { + "signature two sig is empty", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Signature = []byte{} + }, + false, + }, + { + "signature one data is empty", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Data = nil + }, + false, + }, + { + "signature two data is empty", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Data = []byte{} + }, + false, + }, + { + "signatures are identical", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Signature = misbehaviour.SignatureOne.Signature + }, + false, + }, + { + "data signed is identical", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Data = misbehaviour.SignatureOne.Data + }, + false, + }, + { + "data type for SignatureOne is unspecified", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.DataType = types.UNSPECIFIED + }, false, + }, + { + "data type for SignatureTwo is unspecified", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.DataType = types.UNSPECIFIED + }, false, + }, + { + "timestamp for SignatureOne is zero", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureOne.Timestamp = 0 + }, false, + }, + { + "timestamp for SignatureTwo is zero", + func(misbehaviour *types.Misbehaviour) { + misbehaviour.SignatureTwo.Timestamp = 0 + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + + misbehaviour := solomachine.CreateMisbehaviour() + tc.malleateMisbehaviour(misbehaviour) + + err := misbehaviour.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/proof.go b/x/ibc/light-clients/06-solomachine/types/proof.go new file mode 100644 index 000000000000..6c2e0b842886 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/proof.go @@ -0,0 +1,475 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// VerifySignature verifies if the the provided public key generated the signature +// over the given data. Single and Multi signature public keys are supported. +// The signature data type must correspond to the public key type. An error is +// returned if signature verification fails or an invalid SignatureData type is +// provided. +func VerifySignature(pubKey cryptotypes.PubKey, signBytes []byte, sigData signing.SignatureData) error { + switch pubKey := pubKey.(type) { + case multisig.PubKey: + data, ok := sigData.(*signing.MultiSignatureData) + if !ok { + return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.MultiSignatureData)(nil), data) + } + + // The function supplied fulfills the VerifyMultisignature interface. No special + // adjustments need to be made to the sign bytes based on the sign mode. + if err := pubKey.VerifyMultisignature(func(signing.SignMode) ([]byte, error) { + return signBytes, nil + }, data); err != nil { + return err + } + + default: + data, ok := sigData.(*signing.SingleSignatureData) + if !ok { + return sdkerrors.Wrapf(ErrSignatureVerificationFailed, "invalid signature data type, expected %T, got %T", (*signing.SingleSignatureData)(nil), data) + } + + if !pubKey.VerifySignature(signBytes, data.Signature) { + return ErrSignatureVerificationFailed + } + } + + return nil +} + +// MisbehaviourSignBytes returns the sign bytes for verification of misbehaviour. +func MisbehaviourSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + dataType DataType, + data []byte) ([]byte, error) { + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: dataType, + Data: data, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// HeaderSignBytes returns the sign bytes for verification of misbehaviour. +func HeaderSignBytes( + cdc codec.BinaryMarshaler, + header *Header, +) ([]byte, error) { + data := &HeaderData{ + NewPubKey: header.NewPublicKey, + NewDiversifier: header.NewDiversifier, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: header.Sequence, + Timestamp: header.Timestamp, + Diversifier: header.NewDiversifier, + DataType: HEADER, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// ClientStateSignBytes returns the sign bytes for verification of the +// client state. +func ClientStateSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + clientState exported.ClientState, +) ([]byte, error) { + dataBz, err := ClientStateDataBytes(cdc, path, clientState) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: CLIENT, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// ClientStateDataBytes returns the client state data bytes used in constructing +// SignBytes. +func ClientStateDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + clientState exported.ClientState, +) ([]byte, error) { + any, err := clienttypes.PackClientState(clientState) + if err != nil { + return nil, err + } + + data := &ClientStateData{ + Path: []byte(path.String()), + ClientState: any, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// ConsensusStateSignBytes returns the sign bytes for verification of the +// consensus state. +func ConsensusStateSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + consensusState exported.ConsensusState, +) ([]byte, error) { + dataBz, err := ConsensusStateDataBytes(cdc, path, consensusState) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: CONSENSUS, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// ConsensusStateDataBytes returns the consensus state data bytes used in constructing +// SignBytes. +func ConsensusStateDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + consensusState exported.ConsensusState, +) ([]byte, error) { + any, err := clienttypes.PackConsensusState(consensusState) + if err != nil { + return nil, err + } + + data := &ConsensusStateData{ + Path: []byte(path.String()), + ConsensusState: any, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// ConnectionStateSignBytes returns the sign bytes for verification of the +// connection state. +func ConnectionStateSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + connectionEnd exported.ConnectionI, +) ([]byte, error) { + dataBz, err := ConnectionStateDataBytes(cdc, path, connectionEnd) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: CONNECTION, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// ConnectionStateDataBytes returns the connection state data bytes used in constructing +// SignBytes. +func ConnectionStateDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + connectionEnd exported.ConnectionI, +) ([]byte, error) { + connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) + if !ok { + return nil, sdkerrors.Wrapf( + connectiontypes.ErrInvalidConnection, + "expected type %T, got %T", connectiontypes.ConnectionEnd{}, connectionEnd, + ) + } + + data := &ConnectionStateData{ + Path: []byte(path.String()), + Connection: &connection, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// ChannelStateSignBytes returns the sign bytes for verification of the +// channel state. +func ChannelStateSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + channelEnd exported.ChannelI, +) ([]byte, error) { + dataBz, err := ChannelStateDataBytes(cdc, path, channelEnd) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: CHANNEL, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// ChannelStateDataBytes returns the channel state data bytes used in constructing +// SignBytes. +func ChannelStateDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + channelEnd exported.ChannelI, +) ([]byte, error) { + channel, ok := channelEnd.(channeltypes.Channel) + if !ok { + return nil, sdkerrors.Wrapf( + channeltypes.ErrInvalidChannel, + "expected channel type %T, got %T", channeltypes.Channel{}, channelEnd) + } + + data := &ChannelStateData{ + Path: []byte(path.String()), + Channel: &channel, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// PacketCommitmentSignBytes returns the sign bytes for verification of the +// packet commitment. +func PacketCommitmentSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + commitmentBytes []byte, +) ([]byte, error) { + dataBz, err := PacketCommitmentDataBytes(cdc, path, commitmentBytes) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: PACKETCOMMITMENT, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// PacketCommitmentDataBytes returns the packet commitment data bytes used in constructing +// SignBytes. +func PacketCommitmentDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + commitmentBytes []byte, +) ([]byte, error) { + data := &PacketCommitmentData{ + Path: []byte(path.String()), + Commitment: commitmentBytes, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// PacketAcknowledgementSignBytes returns the sign bytes for verification of +// the acknowledgement. +func PacketAcknowledgementSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + acknowledgement []byte, +) ([]byte, error) { + dataBz, err := PacketAcknowledgementDataBytes(cdc, path, acknowledgement) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: PACKETACKNOWLEDGEMENT, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// PacketAcknowledgementDataBytes returns the packet acknowledgement data bytes used in constructing +// SignBytes. +func PacketAcknowledgementDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + acknowledgement []byte, +) ([]byte, error) { + data := &PacketAcknowledgementData{ + Path: []byte(path.String()), + Acknowledgement: acknowledgement, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// PacketReceiptAbsenceSignBytes returns the sign bytes for verification +// of the absence of an receipt. +func PacketReceiptAbsenceSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, +) ([]byte, error) { + dataBz, err := PacketReceiptAbsenceDataBytes(cdc, path) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: PACKETRECEIPTABSENCE, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// PacketReceiptAbsenceDataBytes returns the packet receipt absence data bytes +// used in constructing SignBytes. +func PacketReceiptAbsenceDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer +) ([]byte, error) { + data := &PacketReceiptAbsenceData{ + Path: []byte(path.String()), + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} + +// NextSequenceRecvSignBytes returns the sign bytes for verification of the next +// sequence to be received. +func NextSequenceRecvSignBytes( + cdc codec.BinaryMarshaler, + sequence, timestamp uint64, + diversifier string, + path commitmenttypes.MerklePath, + nextSequenceRecv uint64, +) ([]byte, error) { + dataBz, err := NextSequenceRecvDataBytes(cdc, path, nextSequenceRecv) + if err != nil { + return nil, err + } + + signBytes := &SignBytes{ + Sequence: sequence, + Timestamp: timestamp, + Diversifier: diversifier, + DataType: NEXTSEQUENCERECV, + Data: dataBz, + } + + return cdc.MarshalBinaryBare(signBytes) +} + +// NextSequenceRecvDataBytes returns the next sequence recv data bytes used in constructing +// SignBytes. +func NextSequenceRecvDataBytes( + cdc codec.BinaryMarshaler, + path commitmenttypes.MerklePath, // nolint: interfacer + nextSequenceRecv uint64, +) ([]byte, error) { + data := &NextSequenceRecvData{ + Path: []byte(path.String()), + NextSeqRecv: nextSequenceRecv, + } + + dataBz, err := cdc.MarshalBinaryBare(data) + if err != nil { + return nil, err + } + + return dataBz, nil +} diff --git a/x/ibc/light-clients/06-solomachine/types/proof_test.go b/x/ibc/light-clients/06-solomachine/types/proof_test.go new file mode 100644 index 000000000000..e2ba679a5b98 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/proof_test.go @@ -0,0 +1,102 @@ +package types_test + +import ( + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestVerifySignature() { + cdc := suite.chainA.App.AppCodec() + signBytes := []byte("sign bytes") + + singleSignature := suite.solomachine.GenerateSignature(signBytes) + singleSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, singleSignature) + suite.Require().NoError(err) + + multiSignature := suite.solomachineMulti.GenerateSignature(signBytes) + multiSigData, err := solomachinetypes.UnmarshalSignatureData(cdc, multiSignature) + suite.Require().NoError(err) + + testCases := []struct { + name string + publicKey cryptotypes.PubKey + sigData signing.SignatureData + expPass bool + }{ + { + "single signature with regular public key", + suite.solomachine.PublicKey, + singleSigData, + true, + }, + { + "multi signature with multisig public key", + suite.solomachineMulti.PublicKey, + multiSigData, + true, + }, + { + "single signature with multisig public key", + suite.solomachineMulti.PublicKey, + singleSigData, + false, + }, + { + "multi signature with regular public key", + suite.solomachine.PublicKey, + multiSigData, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := solomachinetypes.VerifySignature(tc.publicKey, signBytes, tc.sigData) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *SoloMachineTestSuite) TestClientStateSignBytes() { + cdc := suite.chainA.App.AppCodec() + + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + // success + path := solomachine.GetClientStatePath(counterpartyClientIdentifier) + bz, err := types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ClientState()) + suite.Require().NoError(err) + suite.Require().NotNil(bz) + + // nil client state + bz, err = types.ClientStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) + suite.Require().Error(err) + suite.Require().Nil(bz) + } +} + +func (suite *SoloMachineTestSuite) TestConsensusStateSignBytes() { + cdc := suite.chainA.App.AppCodec() + + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + // success + path := solomachine.GetConsensusStatePath(counterpartyClientIdentifier, consensusHeight) + bz, err := types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, solomachine.ConsensusState()) + suite.Require().NoError(err) + suite.Require().NotNil(bz) + + // nil consensus state + bz, err = types.ConsensusStateSignBytes(cdc, solomachine.Sequence, solomachine.Time, solomachine.Diversifier, path, nil) + suite.Require().Error(err) + suite.Require().Nil(bz) + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/proposal_handle.go b/x/ibc/light-clients/06-solomachine/types/proposal_handle.go new file mode 100644 index 000000000000..b55e612b3c96 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/proposal_handle.go @@ -0,0 +1,65 @@ +package types + +import ( + "reflect" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "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) { + + if !cs.AllowUpdateAfterProposal { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrUpdateClientFailed, + "solo machine client is not allowed to updated with a proposal", + ) + } + + smHeader, ok := header.(*Header) + if !ok { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, + ) + } + + consensusPublicKey, err := cs.ConsensusState.GetPubKey() + if err != nil { + return nil, nil, sdkerrors.Wrap(err, "failed to get consensus public key") + } + + headerPublicKey, err := smHeader.GetPubKey() + if err != nil { + return nil, nil, sdkerrors.Wrap(err, "failed to get header public key") + } + + if reflect.DeepEqual(consensusPublicKey, headerPublicKey) { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "new public key in header equals current public key", + ) + } + + clientState := &cs + + consensusState := &ConsensusState{ + PublicKey: smHeader.NewPublicKey, + Diversifier: smHeader.NewDiversifier, + Timestamp: smHeader.Timestamp, + } + + clientState.Sequence = smHeader.Sequence + clientState.ConsensusState = consensusState + clientState.FrozenSequence = 0 + + return clientState, consensusState, 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 new file mode 100644 index 000000000000..da5c815ecbc8 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/proposal_handle_test.go @@ -0,0 +1,91 @@ +package types_test + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestCheckProposedHeaderAndUpdateState() { + var header exported.Header + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "valid header", func() { + header = solomachine.CreateHeader() + }, true, + }, + { + "nil header", func() { + header = &ibctmtypes.Header{} + }, false, + }, + { + "header does not update public key", func() { + header = &types.Header{ + Sequence: 1, + NewPublicKey: solomachine.ConsensusState().PublicKey, + } + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + clientState := solomachine.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) + + clientState.AllowUpdateAfterProposal = true + cs, consState, err = clientState.CheckProposedHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) + + 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) + } else { + suite.Require().Error(err) + suite.Require().Nil(cs) + suite.Require().Nil(consState) + } + }) + } + } +} diff --git a/x/ibc/light-clients/06-solomachine/types/solomachine.go b/x/ibc/light-clients/06-solomachine/types/solomachine.go new file mode 100644 index 000000000000..d3936ef42747 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/solomachine.go @@ -0,0 +1,43 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// Interface implementation checks. +var _, _, _, _ codectypes.UnpackInterfacesMessage = &ClientState{}, &ConsensusState{}, &Header{}, &HeaderData{} + +// Data is an interface used for all the signature data bytes proto definitions. +type Data interface{} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (cs ClientState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return cs.ConsensusState.UnpackInterfaces(unpacker) +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (cs ConsensusState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(cs.PublicKey, new(cryptotypes.PubKey)) +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (h Header) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(h.NewPublicKey, new(cryptotypes.PubKey)) +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (hd HeaderData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(hd.NewPubKey, new(cryptotypes.PubKey)) +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (csd ClientStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(csd.ClientState, new(exported.ClientState)) +} + +// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method +func (csd ConsensusStateData) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(csd.ConsensusState, new(exported.ConsensusState)) +} diff --git a/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go new file mode 100644 index 000000000000..8b31fb0d4116 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/solomachine.pb.go @@ -0,0 +1,4118 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/solomachine/v1/solomachine.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + types2 "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + _ "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 + +// DataType defines the type of solo machine proof being created. This is done to preserve uniqueness of different +// data sign byte encodings. +type DataType int32 + +const ( + // Default State + UNSPECIFIED DataType = 0 + // Data type for client state verification + CLIENT DataType = 1 + // Data type for consensus state verification + CONSENSUS DataType = 2 + // Data type for connection state verification + CONNECTION DataType = 3 + // Data type for channel state verification + CHANNEL DataType = 4 + // Data type for packet commitment verification + PACKETCOMMITMENT DataType = 5 + // Data type for packet acknowledgement verification + PACKETACKNOWLEDGEMENT DataType = 6 + // Data type for packet receipt absence verification + PACKETRECEIPTABSENCE DataType = 7 + // Data type for next sequence recv verification + NEXTSEQUENCERECV DataType = 8 + // Data type for header verification + HEADER DataType = 9 +) + +var DataType_name = map[int32]string{ + 0: "DATA_TYPE_UNINITIALIZED_UNSPECIFIED", + 1: "DATA_TYPE_CLIENT_STATE", + 2: "DATA_TYPE_CONSENSUS_STATE", + 3: "DATA_TYPE_CONNECTION_STATE", + 4: "DATA_TYPE_CHANNEL_STATE", + 5: "DATA_TYPE_PACKET_COMMITMENT", + 6: "DATA_TYPE_PACKET_ACKNOWLEDGEMENT", + 7: "DATA_TYPE_PACKET_RECEIPT_ABSENCE", + 8: "DATA_TYPE_NEXT_SEQUENCE_RECV", + 9: "DATA_TYPE_HEADER", +} + +var DataType_value = map[string]int32{ + "DATA_TYPE_UNINITIALIZED_UNSPECIFIED": 0, + "DATA_TYPE_CLIENT_STATE": 1, + "DATA_TYPE_CONSENSUS_STATE": 2, + "DATA_TYPE_CONNECTION_STATE": 3, + "DATA_TYPE_CHANNEL_STATE": 4, + "DATA_TYPE_PACKET_COMMITMENT": 5, + "DATA_TYPE_PACKET_ACKNOWLEDGEMENT": 6, + "DATA_TYPE_PACKET_RECEIPT_ABSENCE": 7, + "DATA_TYPE_NEXT_SEQUENCE_RECV": 8, + "DATA_TYPE_HEADER": 9, +} + +func (x DataType) String() string { + return proto.EnumName(DataType_name, int32(x)) +} + +func (DataType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{0} +} + +// ClientState defines a solo machine client that tracks the current consensus +// state and if the client is frozen. +type ClientState struct { + // latest sequence of the client state + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + // frozen sequence of the solo machine + FrozenSequence uint64 `protobuf:"varint,2,opt,name=frozen_sequence,json=frozenSequence,proto3" json:"frozen_sequence,omitempty" yaml:"frozen_sequence"` + ConsensusState *ConsensusState `protobuf:"bytes,3,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` + // when set to true, will allow governance to update a solo machine client. + // The client will be unfrozen if it is frozen. + AllowUpdateAfterProposal bool `protobuf:"varint,4,opt,name=allow_update_after_proposal,json=allowUpdateAfterProposal,proto3" json:"allow_update_after_proposal,omitempty" yaml:"allow_update_after_proposal"` +} + +func (m *ClientState) Reset() { *m = ClientState{} } +func (m *ClientState) String() string { return proto.CompactTextString(m) } +func (*ClientState) ProtoMessage() {} +func (*ClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{0} +} +func (m *ClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientState.Merge(m, src) +} +func (m *ClientState) XXX_Size() int { + return m.Size() +} +func (m *ClientState) XXX_DiscardUnknown() { + xxx_messageInfo_ClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientState proto.InternalMessageInfo + +// ConsensusState defines a solo machine consensus state. The sequence of a consensus state +// is contained in the "height" key used in storing the consensus state. +type ConsensusState struct { + // public key of the solo machine + PublicKey *types.Any `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty" yaml:"public_key"` + // diversifier allows the same public key to be re-used across different solo machine clients + // (potentially on different chains) without being considered misbehaviour. + Diversifier string `protobuf:"bytes,2,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *ConsensusState) Reset() { *m = ConsensusState{} } +func (m *ConsensusState) String() string { return proto.CompactTextString(m) } +func (*ConsensusState) ProtoMessage() {} +func (*ConsensusState) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{1} +} +func (m *ConsensusState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusState.Merge(m, src) +} +func (m *ConsensusState) XXX_Size() int { + return m.Size() +} +func (m *ConsensusState) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusState.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusState proto.InternalMessageInfo + +// Header defines a solo machine consensus header +type Header struct { + // sequence to update solo machine public key at + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + NewPublicKey *types.Any `protobuf:"bytes,4,opt,name=new_public_key,json=newPublicKey,proto3" json:"new_public_key,omitempty" yaml:"new_public_key"` + NewDiversifier string `protobuf:"bytes,5,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{2} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +// Misbehaviour defines misbehaviour for a solo machine which consists +// of a sequence and two signatures over different messages at that sequence. +type Misbehaviour struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` + SignatureOne *SignatureAndData `protobuf:"bytes,3,opt,name=signature_one,json=signatureOne,proto3" json:"signature_one,omitempty" yaml:"signature_one"` + SignatureTwo *SignatureAndData `protobuf:"bytes,4,opt,name=signature_two,json=signatureTwo,proto3" json:"signature_two,omitempty" yaml:"signature_two"` +} + +func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } +func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } +func (*Misbehaviour) ProtoMessage() {} +func (*Misbehaviour) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{3} +} +func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Misbehaviour.Merge(m, src) +} +func (m *Misbehaviour) XXX_Size() int { + return m.Size() +} +func (m *Misbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_Misbehaviour.DiscardUnknown(m) +} + +var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo + +// SignatureAndData contains a signature and the data signed over to create that +// signature. +type SignatureAndData struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + DataType DataType `protobuf:"varint,2,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *SignatureAndData) Reset() { *m = SignatureAndData{} } +func (m *SignatureAndData) String() string { return proto.CompactTextString(m) } +func (*SignatureAndData) ProtoMessage() {} +func (*SignatureAndData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{4} +} +func (m *SignatureAndData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignatureAndData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignatureAndData.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 *SignatureAndData) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignatureAndData.Merge(m, src) +} +func (m *SignatureAndData) XXX_Size() int { + return m.Size() +} +func (m *SignatureAndData) XXX_DiscardUnknown() { + xxx_messageInfo_SignatureAndData.DiscardUnknown(m) +} + +var xxx_messageInfo_SignatureAndData proto.InternalMessageInfo + +// TimestampedSignatureData contains the signature data and the timestamp of the +// signature. +type TimestampedSignatureData struct { + SignatureData []byte `protobuf:"bytes,1,opt,name=signature_data,json=signatureData,proto3" json:"signature_data,omitempty" yaml:"signature_data"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *TimestampedSignatureData) Reset() { *m = TimestampedSignatureData{} } +func (m *TimestampedSignatureData) String() string { return proto.CompactTextString(m) } +func (*TimestampedSignatureData) ProtoMessage() {} +func (*TimestampedSignatureData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{5} +} +func (m *TimestampedSignatureData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TimestampedSignatureData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TimestampedSignatureData.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 *TimestampedSignatureData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TimestampedSignatureData.Merge(m, src) +} +func (m *TimestampedSignatureData) XXX_Size() int { + return m.Size() +} +func (m *TimestampedSignatureData) XXX_DiscardUnknown() { + xxx_messageInfo_TimestampedSignatureData.DiscardUnknown(m) +} + +var xxx_messageInfo_TimestampedSignatureData proto.InternalMessageInfo + +// SignBytes defines the signed bytes used for signature verification. +type SignBytes struct { + Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Diversifier string `protobuf:"bytes,3,opt,name=diversifier,proto3" json:"diversifier,omitempty"` + // type of the data used + DataType DataType `protobuf:"varint,4,opt,name=data_type,json=dataType,proto3,enum=ibc.lightclients.solomachine.v1.DataType" json:"data_type,omitempty" yaml:"data_type"` + // marshaled data + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *SignBytes) Reset() { *m = SignBytes{} } +func (m *SignBytes) String() string { return proto.CompactTextString(m) } +func (*SignBytes) ProtoMessage() {} +func (*SignBytes) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{6} +} +func (m *SignBytes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytes.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 *SignBytes) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytes.Merge(m, src) +} +func (m *SignBytes) XXX_Size() int { + return m.Size() +} +func (m *SignBytes) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytes.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytes proto.InternalMessageInfo + +// HeaderData returns the SignBytes data for update verification. +type HeaderData struct { + // header public key + NewPubKey *types.Any `protobuf:"bytes,1,opt,name=new_pub_key,json=newPubKey,proto3" json:"new_pub_key,omitempty" yaml:"new_pub_key"` + // header diversifier + NewDiversifier string `protobuf:"bytes,2,opt,name=new_diversifier,json=newDiversifier,proto3" json:"new_diversifier,omitempty" yaml:"new_diversifier"` +} + +func (m *HeaderData) Reset() { *m = HeaderData{} } +func (m *HeaderData) String() string { return proto.CompactTextString(m) } +func (*HeaderData) ProtoMessage() {} +func (*HeaderData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{7} +} +func (m *HeaderData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeaderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeaderData.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 *HeaderData) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeaderData.Merge(m, src) +} +func (m *HeaderData) XXX_Size() int { + return m.Size() +} +func (m *HeaderData) XXX_DiscardUnknown() { + xxx_messageInfo_HeaderData.DiscardUnknown(m) +} + +var xxx_messageInfo_HeaderData proto.InternalMessageInfo + +// ClientStateData returns the SignBytes data for client state verification. +type ClientStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty" yaml:"client_state"` +} + +func (m *ClientStateData) Reset() { *m = ClientStateData{} } +func (m *ClientStateData) String() string { return proto.CompactTextString(m) } +func (*ClientStateData) ProtoMessage() {} +func (*ClientStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{8} +} +func (m *ClientStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientStateData.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 *ClientStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientStateData.Merge(m, src) +} +func (m *ClientStateData) XXX_Size() int { + return m.Size() +} +func (m *ClientStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ClientStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientStateData proto.InternalMessageInfo + +// ConsensusStateData returns the SignBytes data for consensus state +// verification. +type ConsensusStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + ConsensusState *types.Any `protobuf:"bytes,2,opt,name=consensus_state,json=consensusState,proto3" json:"consensus_state,omitempty" yaml:"consensus_state"` +} + +func (m *ConsensusStateData) Reset() { *m = ConsensusStateData{} } +func (m *ConsensusStateData) String() string { return proto.CompactTextString(m) } +func (*ConsensusStateData) ProtoMessage() {} +func (*ConsensusStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{9} +} +func (m *ConsensusStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusStateData.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 *ConsensusStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusStateData.Merge(m, src) +} +func (m *ConsensusStateData) XXX_Size() int { + return m.Size() +} +func (m *ConsensusStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusStateData proto.InternalMessageInfo + +// ConnectionStateData returns the SignBytes data for connection state +// verification. +type ConnectionStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Connection *types1.ConnectionEnd `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` +} + +func (m *ConnectionStateData) Reset() { *m = ConnectionStateData{} } +func (m *ConnectionStateData) String() string { return proto.CompactTextString(m) } +func (*ConnectionStateData) ProtoMessage() {} +func (*ConnectionStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{10} +} +func (m *ConnectionStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConnectionStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConnectionStateData.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 *ConnectionStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConnectionStateData.Merge(m, src) +} +func (m *ConnectionStateData) XXX_Size() int { + return m.Size() +} +func (m *ConnectionStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ConnectionStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ConnectionStateData proto.InternalMessageInfo + +// ChannelStateData returns the SignBytes data for channel state +// verification. +type ChannelStateData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Channel *types2.Channel `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"` +} + +func (m *ChannelStateData) Reset() { *m = ChannelStateData{} } +func (m *ChannelStateData) String() string { return proto.CompactTextString(m) } +func (*ChannelStateData) ProtoMessage() {} +func (*ChannelStateData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{11} +} +func (m *ChannelStateData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ChannelStateData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ChannelStateData.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 *ChannelStateData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChannelStateData.Merge(m, src) +} +func (m *ChannelStateData) XXX_Size() int { + return m.Size() +} +func (m *ChannelStateData) XXX_DiscardUnknown() { + xxx_messageInfo_ChannelStateData.DiscardUnknown(m) +} + +var xxx_messageInfo_ChannelStateData proto.InternalMessageInfo + +// PacketCommitmentData returns the SignBytes data for packet commitment +// verification. +type PacketCommitmentData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Commitment []byte `protobuf:"bytes,2,opt,name=commitment,proto3" json:"commitment,omitempty"` +} + +func (m *PacketCommitmentData) Reset() { *m = PacketCommitmentData{} } +func (m *PacketCommitmentData) String() string { return proto.CompactTextString(m) } +func (*PacketCommitmentData) ProtoMessage() {} +func (*PacketCommitmentData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{12} +} +func (m *PacketCommitmentData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketCommitmentData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketCommitmentData.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 *PacketCommitmentData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketCommitmentData.Merge(m, src) +} +func (m *PacketCommitmentData) XXX_Size() int { + return m.Size() +} +func (m *PacketCommitmentData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketCommitmentData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketCommitmentData proto.InternalMessageInfo + +func (m *PacketCommitmentData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *PacketCommitmentData) GetCommitment() []byte { + if m != nil { + return m.Commitment + } + return nil +} + +// PacketAcknowledgementData returns the SignBytes data for acknowledgement +// verification. +type PacketAcknowledgementData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Acknowledgement []byte `protobuf:"bytes,2,opt,name=acknowledgement,proto3" json:"acknowledgement,omitempty"` +} + +func (m *PacketAcknowledgementData) Reset() { *m = PacketAcknowledgementData{} } +func (m *PacketAcknowledgementData) String() string { return proto.CompactTextString(m) } +func (*PacketAcknowledgementData) ProtoMessage() {} +func (*PacketAcknowledgementData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{13} +} +func (m *PacketAcknowledgementData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketAcknowledgementData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketAcknowledgementData.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 *PacketAcknowledgementData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketAcknowledgementData.Merge(m, src) +} +func (m *PacketAcknowledgementData) XXX_Size() int { + return m.Size() +} +func (m *PacketAcknowledgementData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketAcknowledgementData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketAcknowledgementData proto.InternalMessageInfo + +func (m *PacketAcknowledgementData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *PacketAcknowledgementData) GetAcknowledgement() []byte { + if m != nil { + return m.Acknowledgement + } + return nil +} + +// PacketReceiptAbsenceData returns the SignBytes data for +// packet receipt absence verification. +type PacketReceiptAbsenceData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` +} + +func (m *PacketReceiptAbsenceData) Reset() { *m = PacketReceiptAbsenceData{} } +func (m *PacketReceiptAbsenceData) String() string { return proto.CompactTextString(m) } +func (*PacketReceiptAbsenceData) ProtoMessage() {} +func (*PacketReceiptAbsenceData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{14} +} +func (m *PacketReceiptAbsenceData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PacketReceiptAbsenceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PacketReceiptAbsenceData.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 *PacketReceiptAbsenceData) XXX_Merge(src proto.Message) { + xxx_messageInfo_PacketReceiptAbsenceData.Merge(m, src) +} +func (m *PacketReceiptAbsenceData) XXX_Size() int { + return m.Size() +} +func (m *PacketReceiptAbsenceData) XXX_DiscardUnknown() { + xxx_messageInfo_PacketReceiptAbsenceData.DiscardUnknown(m) +} + +var xxx_messageInfo_PacketReceiptAbsenceData proto.InternalMessageInfo + +func (m *PacketReceiptAbsenceData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +// NextSequenceRecvData returns the SignBytes data for verification of the next +// sequence to be received. +type NextSequenceRecvData struct { + Path []byte `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + NextSeqRecv uint64 `protobuf:"varint,2,opt,name=next_seq_recv,json=nextSeqRecv,proto3" json:"next_seq_recv,omitempty" yaml:"next_seq_recv"` +} + +func (m *NextSequenceRecvData) Reset() { *m = NextSequenceRecvData{} } +func (m *NextSequenceRecvData) String() string { return proto.CompactTextString(m) } +func (*NextSequenceRecvData) ProtoMessage() {} +func (*NextSequenceRecvData) Descriptor() ([]byte, []int) { + return fileDescriptor_6cc2ee18f7f86d4e, []int{15} +} +func (m *NextSequenceRecvData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NextSequenceRecvData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NextSequenceRecvData.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 *NextSequenceRecvData) XXX_Merge(src proto.Message) { + xxx_messageInfo_NextSequenceRecvData.Merge(m, src) +} +func (m *NextSequenceRecvData) XXX_Size() int { + return m.Size() +} +func (m *NextSequenceRecvData) XXX_DiscardUnknown() { + xxx_messageInfo_NextSequenceRecvData.DiscardUnknown(m) +} + +var xxx_messageInfo_NextSequenceRecvData proto.InternalMessageInfo + +func (m *NextSequenceRecvData) GetPath() []byte { + if m != nil { + return m.Path + } + return nil +} + +func (m *NextSequenceRecvData) GetNextSeqRecv() uint64 { + if m != nil { + return m.NextSeqRecv + } + return 0 +} + +func init() { + proto.RegisterEnum("ibc.lightclients.solomachine.v1.DataType", DataType_name, DataType_value) + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.solomachine.v1.ClientState") + proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.solomachine.v1.ConsensusState") + proto.RegisterType((*Header)(nil), "ibc.lightclients.solomachine.v1.Header") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.solomachine.v1.Misbehaviour") + proto.RegisterType((*SignatureAndData)(nil), "ibc.lightclients.solomachine.v1.SignatureAndData") + proto.RegisterType((*TimestampedSignatureData)(nil), "ibc.lightclients.solomachine.v1.TimestampedSignatureData") + proto.RegisterType((*SignBytes)(nil), "ibc.lightclients.solomachine.v1.SignBytes") + proto.RegisterType((*HeaderData)(nil), "ibc.lightclients.solomachine.v1.HeaderData") + proto.RegisterType((*ClientStateData)(nil), "ibc.lightclients.solomachine.v1.ClientStateData") + proto.RegisterType((*ConsensusStateData)(nil), "ibc.lightclients.solomachine.v1.ConsensusStateData") + proto.RegisterType((*ConnectionStateData)(nil), "ibc.lightclients.solomachine.v1.ConnectionStateData") + proto.RegisterType((*ChannelStateData)(nil), "ibc.lightclients.solomachine.v1.ChannelStateData") + proto.RegisterType((*PacketCommitmentData)(nil), "ibc.lightclients.solomachine.v1.PacketCommitmentData") + proto.RegisterType((*PacketAcknowledgementData)(nil), "ibc.lightclients.solomachine.v1.PacketAcknowledgementData") + proto.RegisterType((*PacketReceiptAbsenceData)(nil), "ibc.lightclients.solomachine.v1.PacketReceiptAbsenceData") + proto.RegisterType((*NextSequenceRecvData)(nil), "ibc.lightclients.solomachine.v1.NextSequenceRecvData") +} + +func init() { + proto.RegisterFile("ibc/lightclients/solomachine/v1/solomachine.proto", fileDescriptor_6cc2ee18f7f86d4e) +} + +var fileDescriptor_6cc2ee18f7f86d4e = []byte{ + // 1359 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x8f, 0xdb, 0x44, + 0x10, 0x3f, 0xa7, 0xe9, 0xf5, 0x32, 0xb9, 0xe6, 0x82, 0x9b, 0xb6, 0x39, 0xb7, 0x4a, 0x8c, 0x11, + 0xe5, 0x40, 0x34, 0xe1, 0x8a, 0xa8, 0x50, 0x85, 0x00, 0xc7, 0x31, 0x34, 0xed, 0x9d, 0x2f, 0x38, + 0x3e, 0xa0, 0x15, 0x92, 0xe5, 0x38, 0x7b, 0x89, 0x75, 0x89, 0x1d, 0xe2, 0x4d, 0xd2, 0x20, 0x21, + 0x21, 0x9e, 0x4a, 0xc4, 0x03, 0x5f, 0x20, 0x12, 0x02, 0xf1, 0x55, 0x80, 0xc7, 0xf2, 0xc6, 0x53, + 0x40, 0xed, 0x37, 0xc8, 0x27, 0x40, 0xf6, 0x6e, 0x62, 0x3b, 0xd7, 0xcb, 0x89, 0x7f, 0x4f, 0xd9, + 0x9d, 0xf9, 0xcd, 0x6f, 0x66, 0x67, 0x26, 0xb3, 0x6b, 0xd8, 0xb5, 0xea, 0x66, 0xb1, 0x6d, 0x35, + 0x5b, 0xd8, 0x6c, 0x5b, 0xc8, 0xc6, 0x6e, 0xd1, 0x75, 0xda, 0x4e, 0xc7, 0x30, 0x5b, 0x96, 0x8d, + 0x8a, 0x83, 0xdd, 0xf0, 0xb6, 0xd0, 0xed, 0x39, 0xd8, 0x61, 0xf3, 0x56, 0xdd, 0x2c, 0x84, 0x4d, + 0x0a, 0x61, 0xcc, 0x60, 0x97, 0x7b, 0xc5, 0xe3, 0x34, 0x9d, 0x1e, 0x2a, 0x9a, 0x8e, 0x6d, 0x23, + 0x13, 0x5b, 0x8e, 0xed, 0x51, 0x05, 0x3b, 0xc2, 0xc4, 0xbd, 0x18, 0x00, 0x5b, 0x86, 0x6d, 0xa3, + 0xb6, 0x8f, 0x22, 0x4b, 0x0a, 0xc9, 0x34, 0x9d, 0xa6, 0xe3, 0x2f, 0x8b, 0xde, 0x8a, 0x4a, 0xb7, + 0x9b, 0x8e, 0xd3, 0x6c, 0xa3, 0xa2, 0xbf, 0xab, 0xf7, 0x8f, 0x8a, 0x86, 0x3d, 0x22, 0x2a, 0xe1, + 0xb7, 0x18, 0x24, 0x25, 0x3f, 0xae, 0x1a, 0x36, 0x30, 0x62, 0x39, 0xd8, 0x70, 0xd1, 0xe7, 0x7d, + 0x64, 0x9b, 0x28, 0xcb, 0xf0, 0xcc, 0x4e, 0x5c, 0x5d, 0xec, 0x59, 0x09, 0xb6, 0x8e, 0x7a, 0xce, + 0x17, 0xc8, 0xd6, 0x17, 0x90, 0x98, 0x07, 0x29, 0x71, 0xb3, 0x69, 0xfe, 0xca, 0xc8, 0xe8, 0xb4, + 0xef, 0x08, 0x4b, 0x00, 0x41, 0x4d, 0x11, 0x49, 0x6d, 0x4e, 0x82, 0x61, 0xcb, 0x74, 0x6c, 0x17, + 0xd9, 0x6e, 0xdf, 0xd5, 0x5d, 0xcf, 0x67, 0xf6, 0x1c, 0xcf, 0xec, 0x24, 0x6f, 0x15, 0x0b, 0x67, + 0x24, 0xaa, 0x20, 0xcd, 0xed, 0xfc, 0x50, 0xc3, 0x5e, 0x97, 0x18, 0x05, 0x35, 0x65, 0x46, 0xb0, + 0x2c, 0x82, 0x6b, 0x46, 0xbb, 0xed, 0x0c, 0xf5, 0x7e, 0xb7, 0x61, 0x60, 0xa4, 0x1b, 0x47, 0x18, + 0xf5, 0xf4, 0x6e, 0xcf, 0xe9, 0x3a, 0xae, 0xd1, 0xce, 0xc6, 0x79, 0x66, 0x67, 0xa3, 0x74, 0x63, + 0x36, 0xcd, 0x0b, 0x84, 0x70, 0x05, 0x58, 0x50, 0xb3, 0xbe, 0xf6, 0xd0, 0x57, 0x8a, 0x9e, 0xae, + 0x4a, 0x55, 0x77, 0xe2, 0x8f, 0xbf, 0xcf, 0xaf, 0x09, 0x3f, 0x30, 0x90, 0x8a, 0xc6, 0xca, 0xde, + 0x03, 0xe8, 0xf6, 0xeb, 0x6d, 0xcb, 0xd4, 0x8f, 0xd1, 0xc8, 0x4f, 0x6c, 0xf2, 0x56, 0xa6, 0x40, + 0xca, 0x52, 0x98, 0x97, 0xa5, 0x20, 0xda, 0xa3, 0xd2, 0xe5, 0xd9, 0x34, 0xff, 0x02, 0x09, 0x22, + 0xb0, 0x10, 0xd4, 0x04, 0xd9, 0xdc, 0x47, 0x23, 0x96, 0x87, 0x64, 0xc3, 0x1a, 0xa0, 0x9e, 0x6b, + 0x1d, 0x59, 0xa8, 0xe7, 0x97, 0x20, 0xa1, 0x86, 0x45, 0xec, 0x75, 0x48, 0x60, 0xab, 0x83, 0x5c, + 0x6c, 0x74, 0xba, 0x7e, 0x76, 0xe3, 0x6a, 0x20, 0xa0, 0x41, 0x7e, 0x1d, 0x83, 0xf5, 0xbb, 0xc8, + 0x68, 0xa0, 0xde, 0xca, 0x9a, 0x47, 0xa8, 0x62, 0x4b, 0x54, 0x9e, 0xd6, 0xb5, 0x9a, 0xb6, 0x81, + 0xfb, 0x3d, 0x52, 0xc6, 0x4d, 0x35, 0x10, 0xb0, 0x87, 0x90, 0xb2, 0xd1, 0x50, 0x0f, 0x1d, 0x3c, + 0xbe, 0xe2, 0xe0, 0xdb, 0xb3, 0x69, 0xfe, 0x32, 0x39, 0x78, 0xd4, 0x4a, 0x50, 0x37, 0x6d, 0x34, + 0xac, 0x2e, 0xce, 0x2f, 0xc1, 0x96, 0x07, 0x08, 0xe7, 0xe0, 0xbc, 0x97, 0x83, 0x70, 0x43, 0x2c, + 0x01, 0x04, 0xd5, 0x8b, 0xa4, 0x1c, 0x08, 0x68, 0x12, 0x7e, 0x89, 0xc1, 0xe6, 0xbe, 0xe5, 0xd6, + 0x51, 0xcb, 0x18, 0x58, 0x4e, 0xbf, 0xc7, 0xee, 0x42, 0x82, 0x34, 0x9f, 0x6e, 0x35, 0xfc, 0x5c, + 0x24, 0x4a, 0x99, 0xd9, 0x34, 0x9f, 0xa6, 0x6d, 0x36, 0x57, 0x09, 0xea, 0x06, 0x59, 0x57, 0x1a, + 0x91, 0xec, 0xc5, 0x96, 0xb2, 0xd7, 0x85, 0x8b, 0x8b, 0x74, 0xe8, 0x8e, 0x3d, 0x6f, 0xf5, 0xdd, + 0x33, 0x5b, 0xbd, 0x36, 0xb7, 0x12, 0xed, 0x46, 0xd9, 0xc0, 0x46, 0x29, 0x3b, 0x9b, 0xe6, 0x33, + 0x24, 0x8a, 0x08, 0xa3, 0xa0, 0x6e, 0x2e, 0xf6, 0x07, 0xf6, 0x92, 0x47, 0x3c, 0x74, 0x68, 0xca, + 0xff, 0x2b, 0x8f, 0x78, 0xe8, 0x84, 0x3d, 0x6a, 0x43, 0x87, 0x66, 0xf2, 0x67, 0x06, 0xd2, 0xcb, + 0x14, 0xd1, 0xf6, 0x60, 0x96, 0xdb, 0xe3, 0x33, 0x48, 0x34, 0x0c, 0x6c, 0xe8, 0x78, 0xd4, 0x25, + 0x99, 0x4b, 0xdd, 0x7a, 0xf5, 0xcc, 0x30, 0x3d, 0x5e, 0x6d, 0xd4, 0x45, 0xe1, 0xb2, 0x2c, 0x58, + 0x04, 0x75, 0xa3, 0x41, 0xf5, 0x2c, 0x0b, 0x71, 0x6f, 0x4d, 0xbb, 0xd2, 0x5f, 0x47, 0x9b, 0x39, + 0xfe, 0xfc, 0xff, 0xc5, 0x57, 0x0c, 0x64, 0xb5, 0xb9, 0x0c, 0x35, 0x16, 0x67, 0xf2, 0x0f, 0xf4, + 0x3e, 0xa4, 0x82, 0x5c, 0xf8, 0xf4, 0xfe, 0xa9, 0xc2, 0xbd, 0x1b, 0xd5, 0x0b, 0x6a, 0x50, 0x8e, + 0xf2, 0x89, 0x10, 0x62, 0xcf, 0x0f, 0xe1, 0x0f, 0x06, 0x12, 0x9e, 0xdf, 0xd2, 0x08, 0x23, 0xf7, + 0x5f, 0xfc, 0x3b, 0x97, 0x06, 0xc5, 0xb9, 0x93, 0x83, 0x22, 0x52, 0x82, 0xf8, 0xff, 0x55, 0x82, + 0xf3, 0x41, 0x09, 0xe8, 0x09, 0x7f, 0x62, 0x00, 0xc8, 0xf0, 0xf1, 0x93, 0xb2, 0x07, 0x49, 0xfa, + 0x97, 0x3f, 0x73, 0x3c, 0x5e, 0x99, 0x4d, 0xf3, 0x6c, 0x64, 0x4a, 0xd0, 0xf9, 0x48, 0x46, 0xc4, + 0x29, 0xf3, 0x21, 0xf6, 0x0f, 0xe7, 0xc3, 0x97, 0xb0, 0x15, 0xba, 0x1c, 0xfd, 0x58, 0x59, 0x88, + 0x77, 0x0d, 0xdc, 0xa2, 0xed, 0xec, 0xaf, 0xd9, 0x2a, 0x6c, 0xd2, 0xd1, 0x40, 0x2e, 0xb4, 0xd8, + 0x8a, 0x03, 0x5c, 0x9d, 0x4d, 0xf3, 0x97, 0x22, 0xe3, 0x84, 0x5e, 0x59, 0x49, 0x33, 0xf0, 0x44, + 0xdd, 0x7f, 0xc3, 0x00, 0x1b, 0xbd, 0x48, 0x4e, 0x0d, 0xe1, 0xc1, 0xc9, 0x6b, 0x75, 0x55, 0x14, + 0x7f, 0xe3, 0xee, 0xa4, 0xb1, 0x0c, 0xe0, 0x92, 0xb4, 0x78, 0x90, 0xac, 0x8e, 0x45, 0x06, 0x08, + 0xde, 0x2e, 0x34, 0x8c, 0x97, 0xfd, 0xb6, 0xf2, 0x1e, 0x2f, 0x85, 0xd0, 0xbb, 0x86, 0x5c, 0xea, + 0x74, 0x27, 0xdb, 0x0d, 0x35, 0x64, 0x48, 0xfd, 0x36, 0x20, 0x2d, 0x91, 0x27, 0xce, 0x6a, 0xa7, + 0xb7, 0xe1, 0x02, 0x7d, 0x0a, 0x51, 0x8f, 0xd7, 0x43, 0x1e, 0xe9, 0x1b, 0xc9, 0x73, 0x47, 0x96, + 0xea, 0x1c, 0x4c, 0xbd, 0xdc, 0x83, 0x4c, 0xd5, 0x30, 0x8f, 0x11, 0x96, 0x9c, 0x4e, 0xc7, 0xc2, + 0x1d, 0x64, 0xe3, 0x53, 0x3d, 0xe5, 0xbc, 0xe3, 0xcd, 0x51, 0xbe, 0xb3, 0x4d, 0x35, 0x24, 0x11, + 0x1e, 0xc0, 0x36, 0xe1, 0x12, 0xcd, 0x63, 0xdb, 0x19, 0xb6, 0x51, 0xa3, 0x89, 0x56, 0x12, 0xee, + 0xc0, 0x96, 0x11, 0x85, 0x52, 0xd6, 0x65, 0xb1, 0x50, 0x80, 0x2c, 0xa1, 0x56, 0x91, 0x89, 0xac, + 0x2e, 0x16, 0xeb, 0xae, 0x37, 0x07, 0x4e, 0x63, 0x16, 0x5a, 0x90, 0x51, 0xd0, 0x23, 0x3c, 0x7f, + 0x7c, 0xa9, 0xc8, 0x1c, 0x9c, 0x1a, 0xc5, 0x3b, 0x70, 0xd1, 0x46, 0x8f, 0xb0, 0xf7, 0x74, 0xd3, + 0x7b, 0xc8, 0x1c, 0xd0, 0xb7, 0x5d, 0xe8, 0x1a, 0x88, 0xa8, 0x05, 0x35, 0x69, 0x13, 0x6a, 0x8f, + 0xf5, 0xb5, 0x6f, 0xe3, 0xb0, 0x31, 0x1f, 0x0c, 0xec, 0xdb, 0xf0, 0x52, 0x59, 0xd4, 0x44, 0x5d, + 0x7b, 0x50, 0x95, 0xf5, 0x43, 0xa5, 0xa2, 0x54, 0xb4, 0x8a, 0xb8, 0x57, 0x79, 0x28, 0x97, 0xf5, + 0x43, 0xa5, 0x56, 0x95, 0xa5, 0xca, 0x07, 0x15, 0xb9, 0x9c, 0x5e, 0xe3, 0xb6, 0xc6, 0x13, 0x3e, + 0x19, 0x12, 0xb1, 0x37, 0xe0, 0x4a, 0x60, 0x29, 0xed, 0x55, 0x64, 0x45, 0xd3, 0x6b, 0x9a, 0xa8, + 0xc9, 0x69, 0x86, 0x83, 0xf1, 0x84, 0x5f, 0x27, 0x32, 0xf6, 0x75, 0xd8, 0x0e, 0xe1, 0x0e, 0x94, + 0x9a, 0xac, 0xd4, 0x0e, 0x6b, 0x14, 0x1a, 0xe3, 0x2e, 0x8e, 0x27, 0x7c, 0x62, 0x21, 0x66, 0x0b, + 0xc0, 0x45, 0xd0, 0x8a, 0x2c, 0x69, 0x95, 0x03, 0x85, 0xc2, 0xcf, 0x71, 0xa9, 0xf1, 0x84, 0x87, + 0x40, 0xce, 0xee, 0xc0, 0xd5, 0x10, 0xfe, 0xae, 0xa8, 0x28, 0xf2, 0x1e, 0x05, 0xc7, 0xb9, 0xe4, + 0x78, 0xc2, 0x5f, 0xa0, 0x42, 0xf6, 0x2d, 0xb8, 0x16, 0x20, 0xab, 0xa2, 0x74, 0x5f, 0xd6, 0x74, + 0xe9, 0x60, 0x7f, 0xbf, 0xa2, 0xed, 0xcb, 0x8a, 0x96, 0x3e, 0xcf, 0x65, 0xc6, 0x13, 0x3e, 0x4d, + 0x14, 0x81, 0x9c, 0x7d, 0x0f, 0xf8, 0x13, 0x66, 0xa2, 0x74, 0x5f, 0x39, 0xf8, 0x64, 0x4f, 0x2e, + 0x7f, 0x28, 0xfb, 0xb6, 0xeb, 0xdc, 0xf6, 0x78, 0xc2, 0x5f, 0x26, 0xda, 0x25, 0x25, 0xfb, 0xee, + 0x73, 0x08, 0x54, 0x59, 0x92, 0x2b, 0x55, 0x4d, 0x17, 0x4b, 0x35, 0x59, 0x91, 0xe4, 0xf4, 0x05, + 0x2e, 0x3b, 0x9e, 0xf0, 0x19, 0xa2, 0xa5, 0x4a, 0xaa, 0x63, 0x6f, 0xc3, 0xf5, 0xc0, 0x5e, 0x91, + 0x3f, 0xd5, 0xf4, 0x9a, 0xfc, 0xd1, 0xa1, 0xa7, 0xf2, 0x68, 0x3e, 0x4e, 0x6f, 0x90, 0xc0, 0x3d, + 0xcd, 0x5c, 0xe1, 0xc9, 0x59, 0x1e, 0xd2, 0x81, 0xdd, 0x5d, 0x59, 0x2c, 0xcb, 0x6a, 0x3a, 0x41, + 0x2a, 0x43, 0x76, 0x5c, 0xfc, 0xf1, 0x8f, 0xb9, 0xb5, 0x92, 0xfe, 0xeb, 0xd3, 0x1c, 0xf3, 0xe4, + 0x69, 0x8e, 0xf9, 0xf3, 0x69, 0x8e, 0xf9, 0xee, 0x59, 0x6e, 0xed, 0xc9, 0xb3, 0xdc, 0xda, 0xef, + 0xcf, 0x72, 0x6b, 0x0f, 0xe5, 0xa6, 0x85, 0x5b, 0xfd, 0x7a, 0xc1, 0x74, 0x3a, 0x45, 0xd3, 0x71, + 0x3b, 0x8e, 0x4b, 0x7f, 0x6e, 0xba, 0x8d, 0xe3, 0xe2, 0xa3, 0xe2, 0xe2, 0x03, 0xeb, 0xe6, 0xfc, + 0x0b, 0xeb, 0x8d, 0xdb, 0x37, 0xc3, 0x1f, 0x59, 0xde, 0x2d, 0xe3, 0xd6, 0xd7, 0xfd, 0x71, 0xf6, + 0xe6, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x80, 0xd3, 0x79, 0xb6, 0x91, 0x0d, 0x00, 0x00, +} + +func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AllowUpdateAfterProposal { + i-- + if m.AllowUpdateAfterProposal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.FrozenSequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.FrozenSequence)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x12 + } + if m.PublicKey != nil { + { + size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x2a + } + if m.NewPublicKey != nil { + { + size, err := m.NewPublicKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x1a + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SignatureTwo != nil { + { + size, err := m.SignatureTwo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.SignatureOne != nil { + { + size, err := m.SignatureOne.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignatureAndData) 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 *SignatureAndData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignatureAndData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if m.DataType != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + i-- + dAtA[i] = 0x10 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TimestampedSignatureData) 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 *TimestampedSignatureData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TimestampedSignatureData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if len(m.SignatureData) > 0 { + i -= len(m.SignatureData) + copy(dAtA[i:], m.SignatureData) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.SignatureData))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBytes) 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 *SignBytes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x2a + } + if m.DataType != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.DataType)) + i-- + dAtA[i] = 0x20 + } + if len(m.Diversifier) > 0 { + i -= len(m.Diversifier) + copy(dAtA[i:], m.Diversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Diversifier))) + i-- + dAtA[i] = 0x1a + } + if m.Timestamp != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x10 + } + if m.Sequence != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *HeaderData) 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 *HeaderData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeaderData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NewDiversifier) > 0 { + i -= len(m.NewDiversifier) + copy(dAtA[i:], m.NewDiversifier) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.NewDiversifier))) + i-- + dAtA[i] = 0x12 + } + if m.NewPubKey != nil { + { + size, err := m.NewPubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ClientStateData) 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 *ClientStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ClientState != nil { + { + size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConsensusStateData) 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 *ConsensusStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ConsensusState != nil { + { + size, err := m.ConsensusState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConnectionStateData) 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 *ConnectionStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConnectionStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Connection != nil { + { + size, err := m.Connection.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ChannelStateData) 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 *ChannelStateData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChannelStateData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Channel != nil { + { + size, err := m.Channel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSolomachine(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketCommitmentData) 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 *PacketCommitmentData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketCommitmentData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Commitment) > 0 { + i -= len(m.Commitment) + copy(dAtA[i:], m.Commitment) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Commitment))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketAcknowledgementData) 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 *PacketAcknowledgementData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketAcknowledgementData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Acknowledgement) > 0 { + i -= len(m.Acknowledgement) + copy(dAtA[i:], m.Acknowledgement) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Acknowledgement))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PacketReceiptAbsenceData) 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 *PacketReceiptAbsenceData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PacketReceiptAbsenceData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NextSequenceRecvData) 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 *NextSequenceRecvData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NextSequenceRecvData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NextSeqRecv != 0 { + i = encodeVarintSolomachine(dAtA, i, uint64(m.NextSeqRecv)) + i-- + dAtA[i] = 0x10 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintSolomachine(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSolomachine(dAtA []byte, offset int, v uint64) int { + offset -= sovSolomachine(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.FrozenSequence != 0 { + n += 1 + sovSolomachine(uint64(m.FrozenSequence)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.AllowUpdateAfterProposal { + n += 2 + } + return n +} + +func (m *ConsensusState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PublicKey != nil { + l = m.PublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.NewPublicKey != nil { + l = m.NewPublicKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *Misbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.SignatureOne != nil { + l = m.SignatureOne.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.SignatureTwo != nil { + l = m.SignatureTwo.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *SignatureAndData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.DataType != 0 { + n += 1 + sovSolomachine(uint64(m.DataType)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *TimestampedSignatureData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SignatureData) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + return n +} + +func (m *SignBytes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sequence != 0 { + n += 1 + sovSolomachine(uint64(m.Sequence)) + } + if m.Timestamp != 0 { + n += 1 + sovSolomachine(uint64(m.Timestamp)) + } + l = len(m.Diversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.DataType != 0 { + n += 1 + sovSolomachine(uint64(m.DataType)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *HeaderData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NewPubKey != nil { + l = m.NewPubKey.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.NewDiversifier) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ClientStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.ClientState != nil { + l = m.ClientState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ConsensusStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.ConsensusState != nil { + l = m.ConsensusState.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ConnectionStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Connection != nil { + l = m.Connection.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *ChannelStateData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.Channel != nil { + l = m.Channel.Size() + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketCommitmentData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Commitment) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketAcknowledgementData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + l = len(m.Acknowledgement) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *PacketReceiptAbsenceData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + return n +} + +func (m *NextSequenceRecvData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovSolomachine(uint64(l)) + } + if m.NextSeqRecv != 0 { + n += 1 + sovSolomachine(uint64(m.NextSeqRecv)) + } + return n +} + +func sovSolomachine(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSolomachine(x uint64) (n int) { + return sovSolomachine(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) 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 ErrIntOverflowSolomachine + } + 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: ClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FrozenSequence", wireType) + } + m.FrozenSequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FrozenSequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &ConsensusState{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterProposal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUpdateAfterProposal = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusState) 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 ErrIntOverflowSolomachine + } + 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: ConsensusState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PublicKey == nil { + m.PublicKey = &types.Any{} + } + if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Header) 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 ErrIntOverflowSolomachine + } + 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: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPublicKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPublicKey == nil { + m.NewPublicKey = &types.Any{} + } + if err := m.NewPublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Misbehaviour) 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 ErrIntOverflowSolomachine + } + 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: Misbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureOne", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureOne == nil { + m.SignatureOne = &SignatureAndData{} + } + if err := m.SignatureOne.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureTwo", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignatureTwo == nil { + m.SignatureTwo = &SignatureAndData{} + } + if err := m.SignatureTwo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignatureAndData) 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 ErrIntOverflowSolomachine + } + 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: SignatureAndData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignatureAndData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) + } + m.DataType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DataType |= DataType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TimestampedSignatureData) 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 ErrIntOverflowSolomachine + } + 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: TimestampedSignatureData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TimestampedSignatureData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignatureData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignatureData = append(m.SignatureData[:0], dAtA[iNdEx:postIndex]...) + if m.SignatureData == nil { + m.SignatureData = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBytes) 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 ErrIntOverflowSolomachine + } + 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: SignBytes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Diversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Diversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DataType", wireType) + } + m.DataType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DataType |= DataType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HeaderData) 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 ErrIntOverflowSolomachine + } + 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: HeaderData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HeaderData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewPubKey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NewPubKey == nil { + m.NewPubKey = &types.Any{} + } + if err := m.NewPubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewDiversifier", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + 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 ErrInvalidLengthSolomachine + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewDiversifier = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ClientStateData) 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 ErrIntOverflowSolomachine + } + 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: ClientStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ClientState == nil { + m.ClientState = &types.Any{} + } + if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusStateData) 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 ErrIntOverflowSolomachine + } + 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: ConsensusStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusState == nil { + m.ConsensusState = &types.Any{} + } + if err := m.ConsensusState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectionStateData) 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 ErrIntOverflowSolomachine + } + 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: ConnectionStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectionStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Connection == nil { + m.Connection = &types1.ConnectionEnd{} + } + if err := m.Connection.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ChannelStateData) 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 ErrIntOverflowSolomachine + } + 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: ChannelStateData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ChannelStateData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Channel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Channel == nil { + m.Channel = &types2.Channel{} + } + if err := m.Channel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketCommitmentData) 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 ErrIntOverflowSolomachine + } + 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: PacketCommitmentData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketCommitmentData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commitment", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Commitment = append(m.Commitment[:0], dAtA[iNdEx:postIndex]...) + if m.Commitment == nil { + m.Commitment = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketAcknowledgementData) 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 ErrIntOverflowSolomachine + } + 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: PacketAcknowledgementData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketAcknowledgementData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Acknowledgement", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Acknowledgement = append(m.Acknowledgement[:0], dAtA[iNdEx:postIndex]...) + if m.Acknowledgement == nil { + m.Acknowledgement = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PacketReceiptAbsenceData) 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 ErrIntOverflowSolomachine + } + 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: PacketReceiptAbsenceData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PacketReceiptAbsenceData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NextSequenceRecvData) 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 ErrIntOverflowSolomachine + } + 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: NextSequenceRecvData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NextSequenceRecvData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSolomachine + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSolomachine + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = append(m.Path[:0], dAtA[iNdEx:postIndex]...) + if m.Path == nil { + m.Path = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NextSeqRecv", wireType) + } + m.NextSeqRecv = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSolomachine + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NextSeqRecv |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSolomachine(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSolomachine + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSolomachine(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, ErrIntOverflowSolomachine + } + 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, ErrIntOverflowSolomachine + } + 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, ErrIntOverflowSolomachine + } + 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, ErrInvalidLengthSolomachine + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSolomachine + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSolomachine + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSolomachine = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSolomachine = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSolomachine = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/light-clients/06-solomachine/types/solomachine_test.go b/x/ibc/light-clients/06-solomachine/types/solomachine_test.go new file mode 100644 index 000000000000..50555e45145a --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/solomachine_test.go @@ -0,0 +1,113 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +type SoloMachineTestSuite struct { + suite.Suite + + solomachine *ibctesting.Solomachine // singlesig public key + solomachineMulti *ibctesting.Solomachine // multisig public key + coordinator *ibctesting.Coordinator + + // testing chain used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + store sdk.KVStore +} + +func (suite *SoloMachineTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + + suite.solomachine = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinesingle", "testing", 1) + suite.solomachineMulti = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachinemulti", "testing", 4) + + suite.store = suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), exported.Solomachine) +} + +func TestSoloMachineTestSuite(t *testing.T) { + suite.Run(t, new(SoloMachineTestSuite)) +} + +func (suite *SoloMachineTestSuite) GetSequenceFromStore() uint64 { + bz := suite.store.Get(host.ClientStateKey()) + suite.Require().NotNil(bz) + + var clientState exported.ClientState + err := suite.chainA.Codec.UnmarshalInterface(bz, &clientState) + suite.Require().NoError(err) + return clientState.GetLatestHeight().GetRevisionHeight() +} + +func (suite *SoloMachineTestSuite) GetInvalidProof() []byte { + invalidProof, err := suite.chainA.Codec.MarshalBinaryBare(&types.TimestampedSignatureData{Timestamp: suite.solomachine.Time}) + suite.Require().NoError(err) + + return invalidProof +} + +func TestUnpackInterfaces_Header(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) + any, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + + header := types.Header{ + NewPublicKey: any, + } + bz, err := header.Marshal() + require.NoError(t, err) + + var header2 types.Header + err = header2.Unmarshal(bz) + require.NoError(t, err) + + err = codectypes.UnpackInterfaces(header2, registry) + require.NoError(t, err) + + require.Equal(t, pk, header2.NewPublicKey.GetCachedValue()) +} + +func TestUnpackInterfaces_HeaderData(t *testing.T) { + registry := testdata.NewTestInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + + pk := secp256k1.GenPrivKey().PubKey().(cryptotypes.PubKey) + any, err := codectypes.NewAnyWithValue(pk) + require.NoError(t, err) + + hd := types.HeaderData{ + NewPubKey: any, + } + bz, err := hd.Marshal() + require.NoError(t, err) + + var hd2 types.HeaderData + err = hd2.Unmarshal(bz) + require.NoError(t, err) + + err = codectypes.UnpackInterfaces(hd2, registry) + require.NoError(t, err) + + require.Equal(t, pk, hd2.NewPubKey.GetCachedValue()) +} diff --git a/x/ibc/light-clients/06-solomachine/types/update.go b/x/ibc/light-clients/06-solomachine/types/update.go new file mode 100644 index 000000000000..4cf31fd98857 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/update.go @@ -0,0 +1,89 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CheckHeaderAndUpdateState checks if the provided header is valid and updates +// the consensus state if appropriate. It returns an error if: +// - the header provided is not parseable to a solo machine header +// - the header sequence does not match the current sequence +// - the header timestamp is less than the consensus state timestamp +// - the currently registered public key did not provide the update signature +func (cs ClientState) CheckHeaderAndUpdateState( + ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore, + header exported.Header, +) (exported.ClientState, exported.ConsensusState, error) { + smHeader, ok := header.(*Header) + if !ok { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "header type %T, expected %T", header, &Header{}, + ) + } + + if err := checkHeader(cdc, &cs, smHeader); err != nil { + return nil, nil, err + } + + clientState, consensusState := update(&cs, smHeader) + return clientState, consensusState, nil +} + +// checkHeader checks if the Solo Machine update signature is valid. +func checkHeader(cdc codec.BinaryMarshaler, clientState *ClientState, header *Header) error { + // assert update sequence is current sequence + if header.Sequence != clientState.Sequence { + return sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, + "header sequence does not match the client state sequence (%d != %d)", header.Sequence, clientState.Sequence, + ) + } + + // assert update timestamp is not less than current consensus state timestamp + if header.Timestamp < clientState.ConsensusState.Timestamp { + return sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, + "header timestamp is less than to the consensus state timestamp (%d < %d)", header.Timestamp, clientState.ConsensusState.Timestamp, + ) + } + + // assert currently registered public key signed over the new public key with correct sequence + data, err := HeaderSignBytes(cdc, header) + if err != nil { + return err + } + + sigData, err := UnmarshalSignatureData(cdc, header.Signature) + if err != nil { + return err + } + + publicKey, err := clientState.ConsensusState.GetPubKey() + if err != nil { + return err + } + + if err := VerifySignature(publicKey, data, sigData); err != nil { + return sdkerrors.Wrap(ErrInvalidHeader, err.Error()) + } + + return nil +} + +// update the consensus state to the new public key and an incremented sequence +func update(clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { + consensusState := &ConsensusState{ + PublicKey: header.NewPublicKey, + Diversifier: header.NewDiversifier, + Timestamp: header.Timestamp, + } + + // increment sequence number + clientState.Sequence++ + clientState.ConsensusState = consensusState + return clientState, consensusState +} diff --git a/x/ibc/light-clients/06-solomachine/types/update_test.go b/x/ibc/light-clients/06-solomachine/types/update_test.go new file mode 100644 index 000000000000..e49992cbb551 --- /dev/null +++ b/x/ibc/light-clients/06-solomachine/types/update_test.go @@ -0,0 +1,181 @@ +package types_test + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *SoloMachineTestSuite) TestCheckHeaderAndUpdateState() { + var ( + clientState exported.ClientState + header exported.Header + ) + + // test singlesig and multisig public keys + for _, solomachine := range []*ibctesting.Solomachine{suite.solomachine, suite.solomachineMulti} { + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + "successful update", + func() { + clientState = solomachine.ClientState() + header = solomachine.CreateHeader() + }, + true, + }, + { + "wrong client state type", + func() { + clientState = &ibctmtypes.ClientState{} + header = solomachine.CreateHeader() + }, + false, + }, + { + "invalid header type", + func() { + clientState = solomachine.ClientState() + header = &ibctmtypes.Header{} + }, + false, + }, + { + "wrong sequence in header", + func() { + clientState = solomachine.ClientState() + // store in temp before assigning to interface type + h := solomachine.CreateHeader() + h.Sequence++ + header = h + }, + false, + }, + { + "invalid header Signature", + func() { + clientState = solomachine.ClientState() + h := solomachine.CreateHeader() + h.Signature = suite.GetInvalidProof() + header = h + }, false, + }, + { + "invalid timestamp in header", + func() { + clientState = solomachine.ClientState() + h := solomachine.CreateHeader() + h.Timestamp-- + header = h + }, false, + }, + { + "signature uses wrong sequence", + func() { + clientState = solomachine.ClientState() + solomachine.Sequence++ + header = solomachine.CreateHeader() + }, + false, + }, + { + "signature uses new pubkey to sign", + func() { + // store in temp before assinging to interface type + cs := solomachine.ClientState() + h := solomachine.CreateHeader() + + publicKey, err := codectypes.NewAnyWithValue(solomachine.PublicKey) + suite.NoError(err) + + data := &types.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: h.NewDiversifier, + } + + dataBz, err := suite.chainA.Codec.MarshalBinaryBare(data) + suite.Require().NoError(err) + + // generate invalid signature + signBytes := &types.SignBytes{ + Sequence: cs.Sequence, + Timestamp: solomachine.Time, + Diversifier: solomachine.Diversifier, + DataType: types.CLIENT, + Data: dataBz, + } + + signBz, err := suite.chainA.Codec.MarshalBinaryBare(signBytes) + suite.Require().NoError(err) + + sig := solomachine.GenerateSignature(signBz) + suite.Require().NoError(err) + h.Signature = sig + + clientState = cs + header = h + + }, + false, + }, + { + "signature signs over old pubkey", + func() { + // store in temp before assinging to interface type + cs := solomachine.ClientState() + oldPubKey := solomachine.PublicKey + h := solomachine.CreateHeader() + + // generate invalid signature + data := append(sdk.Uint64ToBigEndian(cs.Sequence), oldPubKey.Bytes()...) + sig := solomachine.GenerateSignature(data) + h.Signature = sig + + clientState = cs + header = h + }, + false, + }, + { + "consensus state public key is nil", + func() { + cs := solomachine.ClientState() + cs.ConsensusState.PublicKey = nil + clientState = cs + header = solomachine.CreateHeader() + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + // setup test + tc.setup() + + clientState, consensusState, err := clientState.CheckHeaderAndUpdateState(suite.chainA.GetContext(), suite.chainA.Codec, suite.store, header) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(header.(*types.Header).NewPublicKey, clientState.(*types.ClientState).ConsensusState.PublicKey) + suite.Require().Equal(uint64(0), clientState.(*types.ClientState).FrozenSequence) + suite.Require().Equal(header.(*types.Header).Sequence+1, clientState.(*types.ClientState).Sequence) + suite.Require().Equal(consensusState, clientState.(*types.ClientState).ConsensusState) + } else { + suite.Require().Error(err) + suite.Require().Nil(clientState) + suite.Require().Nil(consensusState) + } + }) + } + } +} diff --git a/x/ibc/light-clients/07-tendermint/client/cli/cli.go b/x/ibc/light-clients/07-tendermint/client/cli/cli.go new file mode 100644 index 000000000000..a214869d089e --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/client/cli/cli.go @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000000..f8b925e4f0cc --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/client/cli/tx.go @@ -0,0 +1,281 @@ +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/doc.go b/x/ibc/light-clients/07-tendermint/doc.go new file mode 100644 index 000000000000..26aa430a82da --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/doc.go @@ -0,0 +1,5 @@ +/* +Package tendermint implements a concrete `ConsensusState`, `Header`, +`Misbehaviour` and `Equivocation` types for the Tendermint consensus light client. +*/ +package tendermint diff --git a/x/ibc/light-clients/07-tendermint/module.go b/x/ibc/light-clients/07-tendermint/module.go new file mode 100644 index 000000000000..38c7fa621b11 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/module.go @@ -0,0 +1,18 @@ +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" +) + +// Name returns the IBC client name +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/client_state.go b/x/ibc/light-clients/07-tendermint/types/client_state.go new file mode 100644 index 000000000000..c2bb5239f5f5 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/client_state.go @@ -0,0 +1,532 @@ +package types + +import ( + "strings" + "time" + + ics23 "github.com/confio/ics23/go" + "github.com/tendermint/tendermint/light" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance +func NewClientState( + chainID string, trustLevel Fraction, + trustingPeriod, ubdPeriod, maxClockDrift time.Duration, + latestHeight clienttypes.Height, specs []*ics23.ProofSpec, + upgradePath []string, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour bool, +) *ClientState { + return &ClientState{ + ChainId: chainID, + TrustLevel: trustLevel, + TrustingPeriod: trustingPeriod, + UnbondingPeriod: ubdPeriod, + MaxClockDrift: maxClockDrift, + LatestHeight: latestHeight, + FrozenHeight: clienttypes.ZeroHeight(), + ProofSpecs: specs, + UpgradePath: upgradePath, + AllowUpdateAfterExpiry: allowUpdateAfterExpiry, + AllowUpdateAfterMisbehaviour: allowUpdateAfterMisbehaviour, + } +} + +// GetChainID returns the chain-id +func (cs ClientState) GetChainID() string { + return cs.ChainId +} + +// ClientType is tendermint. +func (cs ClientState) ClientType() string { + return exported.Tendermint +} + +// GetLatestHeight returns latest block height. +func (cs ClientState) GetLatestHeight() exported.Height { + return cs.LatestHeight +} + +// IsFrozen returns true if the frozen height has been set. +func (cs ClientState) IsFrozen() bool { + return !cs.FrozenHeight.IsZero() +} + +// GetFrozenHeight returns the height at which client is frozen +// NOTE: FrozenHeight is zero if client is unfrozen +func (cs ClientState) GetFrozenHeight() exported.Height { + return cs.FrozenHeight +} + +// IsExpired returns whether or not the client has passed the trusting period since the last +// update (in which case no headers are considered valid). +func (cs ClientState) IsExpired(latestTimestamp, now time.Time) bool { + expirationTime := latestTimestamp.Add(cs.TrustingPeriod) + return !expirationTime.After(now) +} + +// Validate performs a basic validation of the client state fields. +func (cs ClientState) Validate() error { + if strings.TrimSpace(cs.ChainId) == "" { + return sdkerrors.Wrap(ErrInvalidChainID, "chain id cannot be empty string") + } + if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { + return err + } + if cs.TrustingPeriod == 0 { + return sdkerrors.Wrap(ErrInvalidTrustingPeriod, "trusting period cannot be zero") + } + if cs.UnbondingPeriod == 0 { + return sdkerrors.Wrap(ErrInvalidUnbondingPeriod, "unbonding period cannot be zero") + } + if cs.MaxClockDrift == 0 { + return sdkerrors.Wrap(ErrInvalidMaxClockDrift, "max clock drift cannot be zero") + } + if cs.LatestHeight.RevisionHeight == 0 { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "tendermint revision height cannot be zero") + } + if cs.TrustingPeriod >= cs.UnbondingPeriod { + return sdkerrors.Wrapf( + ErrInvalidTrustingPeriod, + "trusting period (%s) should be < unbonding period (%s)", cs.TrustingPeriod, cs.UnbondingPeriod, + ) + } + + if cs.ProofSpecs == nil { + return sdkerrors.Wrap(ErrInvalidProofSpecs, "proof specs cannot be nil for tm client") + } + for i, spec := range cs.ProofSpecs { + if spec == nil { + return sdkerrors.Wrapf(ErrInvalidProofSpecs, "proof spec cannot be nil at index: %d", i) + } + } + // UpgradePath may be empty, but if it isn't, each key must be non-empty + for i, k := range cs.UpgradePath { + if strings.TrimSpace(k) == "" { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "key in upgrade path at index %d cannot be empty", i) + } + } + + return nil +} + +// GetProofSpecs returns the format the client expects for proof verification +// as a string array specifying the proof type for each position in chained proof +func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { + return cs.ProofSpecs +} + +// ZeroCustomFields returns a ClientState that is a copy of the current ClientState +// with all client customizable fields zeroed out +func (cs ClientState) ZeroCustomFields() exported.ClientState { + // copy over all chain-specified fields + // and leave custom fields empty + return &ClientState{ + ChainId: cs.ChainId, + UnbondingPeriod: cs.UnbondingPeriod, + LatestHeight: cs.LatestHeight, + ProofSpecs: cs.ProofSpecs, + UpgradePath: cs.UpgradePath, + } +} + +// Initialize will check that initial consensus state is a Tendermint consensus state +// and will store ProcessedTime for initial consensus state as ctx.BlockTime() +func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryMarshaler, clientStore sdk.KVStore, consState exported.ConsensusState) error { + if _, ok := consState.(*ConsensusState); !ok { + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid initial consensus state. expected type: %T, got: %T", + &ConsensusState{}, consState) + } + // set processed time with initial consensus state height equal to initial client state's latest height + SetProcessedTime(clientStore, cs.GetLatestHeight(), uint64(ctx.BlockTime().UnixNano())) + return nil +} + +// VerifyClientState verifies a proof of the client state of the running chain +// stored on the target machine +func (cs ClientState) VerifyClientState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState exported.ClientState, +) error { + merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier)) + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + if clientState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "client state cannot be empty") + } + + _, ok := clientState.(*ClientState) + if !ok { + return sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "invalid client type %T, expected %T", clientState, &ClientState{}) + } + + bz, err := cdc.MarshalInterface(clientState) + if err != nil { + return err + } + + return merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz) +} + +// VerifyClientConsensusState verifies a proof of the consensus state of the +// Tendermint client stored on the target machine. +func (cs ClientState) VerifyClientConsensusState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + counterpartyClientIdentifier string, + consensusHeight exported.Height, + prefix exported.Prefix, + proof []byte, + consensusState exported.ConsensusState, +) error { + merkleProof, provingConsensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + clientPrefixedPath := commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight)) + path, err := commitmenttypes.ApplyPrefix(prefix, clientPrefixedPath) + if err != nil { + return err + } + + if consensusState == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") + } + + _, ok := consensusState.(*ConsensusState) + if !ok { + return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}) + } + + bz, err := cdc.MarshalInterface(consensusState) + if err != nil { + return err + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, provingConsensusState.GetRoot(), path, bz); err != nil { + return err + } + + return nil +} + +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored on the target machine. +func (cs ClientState) VerifyConnectionState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + proof []byte, + connectionID string, + connectionEnd exported.ConnectionI, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connectionID)) + path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) + if err != nil { + return err + } + + connection, ok := connectionEnd.(connectiontypes.ConnectionEnd) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid connection type %T", connectionEnd) + } + + bz, err := cdc.MarshalBinaryBare(&connection) + if err != nil { + return err + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { + return err + } + + return nil +} + +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the target machine. +func (cs ClientState) VerifyChannelState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + channel exported.ChannelI, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) + if err != nil { + return err + } + + channelEnd, ok := channel.(channeltypes.Channel) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "invalid channel type %T", channel) + } + + bz, err := cdc.MarshalBinaryBare(&channelEnd) + if err != nil { + return err + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { + return err + } + + return nil +} + +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketCommitment( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + // check delay period has passed + if err := verifyDelayPeriodPassed(store, height, currentTimestamp, delayPeriod); err != nil { + return err + } + + commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) + if err != nil { + return err + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, commitmentBytes); err != nil { + return err + } + + return nil +} + +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketAcknowledgement( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + // check delay period has passed + if err := verifyDelayPeriodPassed(store, height, currentTimestamp, delayPeriod); err != nil { + return err + } + + ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) + if err != nil { + return err + } + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, channeltypes.CommitAcknowledgement(acknowledgement)); err != nil { + return err + } + + return nil +} + +// VerifyPacketReceiptAbsence verifies a proof of the absence of an +// incoming packet receipt at the specified port, specified channel, and +// specified sequence. +func (cs ClientState) VerifyPacketReceiptAbsence( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + // check delay period has passed + if err := verifyDelayPeriodPassed(store, height, currentTimestamp, delayPeriod); err != nil { + return err + } + + receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) + if err != nil { + return err + } + + if err := merkleProof.VerifyNonMembership(cs.ProofSpecs, consensusState.GetRoot(), path); err != nil { + return err + } + + return nil +} + +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. +func (cs ClientState) VerifyNextSequenceRecv( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + height exported.Height, + currentTimestamp uint64, + delayPeriod uint64, + prefix exported.Prefix, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, +) error { + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) + if err != nil { + return err + } + + // check delay period has passed + if err := verifyDelayPeriodPassed(store, height, currentTimestamp, delayPeriod); err != nil { + return err + } + + nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) + if err != nil { + return err + } + + bz := sdk.Uint64ToBigEndian(nextSequenceRecv) + + if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), path, bz); err != nil { + return err + } + + return nil +} + +// verifyDelayPeriodPassed will ensure that at least delayPeriod amount of time has passed since consensus state was submitted +// before allowing verification to continue. +func verifyDelayPeriodPassed(store sdk.KVStore, proofHeight exported.Height, currentTimestamp, delayPeriod uint64) error { + // check that executing chain's timestamp has passed consensusState's processed time + delay period + processedTime, ok := GetProcessedTime(store, proofHeight) + if !ok { + return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight) + } + validTime := processedTime + delayPeriod + // NOTE: delay period is inclusive, so if currentTimestamp is validTime, then we return no error + if validTime > currentTimestamp { + return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", + validTime, currentTimestamp) + } + return nil +} + +// produceVerificationArgs perfoms the basic checks on the arguments that are +// shared between the verification functions and returns the unmarshalled +// merkle proof, the consensus state and an error if one occurred. +func produceVerificationArgs( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + cs ClientState, + height exported.Height, + prefix exported.Prefix, + proof []byte, +) (merkleProof commitmenttypes.MerkleProof, consensusState *ConsensusState, err error) { + if cs.GetLatestHeight().LT(height) { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf( + sdkerrors.ErrInvalidHeight, + "client state height < proof height (%d < %d)", cs.GetLatestHeight(), height, + ) + } + + if cs.IsFrozen() && !cs.FrozenHeight.GT(height) { + return commitmenttypes.MerkleProof{}, nil, clienttypes.ErrClientFrozen + } + + if prefix == nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") + } + + _, ok := prefix.(*commitmenttypes.MerklePrefix) + if !ok { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected *MerklePrefix", prefix) + } + + if proof == nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty") + } + + if err = cdc.UnmarshalBinaryBare(proof, &merkleProof); err != nil { + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") + } + + consensusState, err = GetConsensusState(store, cdc, height) + if err != nil { + return commitmenttypes.MerkleProof{}, nil, err + } + + return merkleProof, consensusState, nil +} diff --git a/x/ibc/light-clients/07-tendermint/types/client_state_test.go b/x/ibc/light-clients/07-tendermint/types/client_state_test.go new file mode 100644 index 000000000000..744b4729f6ef --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/client_state_test.go @@ -0,0 +1,779 @@ +package types_test + +import ( + "time" + + ics23 "github.com/confio/ics23/go" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +const ( + testClientID = "clientidone" + testConnectionID = "connectionid" + testPortID = "testportid" + testChannelID = "testchannelid" + testSequence = 1 +) + +var ( + invalidProof = []byte("invalid proof") +) + +func (suite *TendermintTestSuite) TestValidate() { + testCases := []struct { + name string + clientState *types.ClientState + expPass bool + }{ + { + name: "valid client", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: true, + }, + { + name: "valid client with nil upgrade path", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil, false, false), + expPass: true, + }, + { + name: "invalid chainID", + clientState: types.NewClientState(" ", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid trust level", + clientState: types.NewClientState(chainID, types.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid trusting period", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid unbonding period", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid max clock drift", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "invalid height", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "trusting period not less than unbonding period", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + expPass: false, + }, + { + name: "proof specs is nil", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath, false, false), + expPass: false, + }, + { + name: "proof specs contains nil", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath, false, false), + expPass: false, + }, + } + + for _, tc := range testCases { + err := tc.clientState.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *TendermintTestSuite) TestInitialize() { + + testCases := []struct { + name string + consensusState exported.ConsensusState + expPass bool + }{ + { + name: "valid consensus", + consensusState: &types.ConsensusState{}, + expPass: true, + }, + { + name: "invalid consensus: consensus state is solomachine consensus", + consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), + expPass: false, + }, + } + + clientA, err := suite.coordinator.CreateClient(suite.chainA, suite.chainB, exported.Tendermint) + suite.Require().NoError(err) + + clientState := suite.chainA.GetClientState(clientA) + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + for _, tc := range testCases { + err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) + if tc.expPass { + suite.Require().NoError(err, "valid case returned an error") + } else { + suite.Require().Error(err, "invalid case didn't return an error") + } + } +} + +func (suite *TendermintTestSuite) TestVerifyClientConsensusState() { + testCases := []struct { + name string + clientState *types.ClientState + consensusState *types.ConsensusState + prefix commitmenttypes.MerklePrefix + proof []byte + expPass bool + }{ + // FIXME: uncomment + // { + // name: "successful verification", + // clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), + // consensusState: types.ConsensusState{ + // Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), + // }, + // prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + // expPass: true, + // }, + { + name: "ApplyPrefix failed", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + consensusState: &types.ConsensusState{ + Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), + }, + prefix: commitmenttypes.MerklePrefix{}, + expPass: false, + }, + { + name: "latest client height < height", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + consensusState: &types.ConsensusState{ + Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), + }, + prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + expPass: false, + }, + { + name: "client is frozen", + clientState: &types.ClientState{LatestHeight: height, FrozenHeight: clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1)}, + consensusState: &types.ConsensusState{ + Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), + }, + prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + expPass: false, + }, + { + name: "proof verification failed", + clientState: types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + consensusState: &types.ConsensusState{ + Root: commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), + NextValidatorsHash: suite.valsHash, + }, + prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), + proof: []byte{}, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.clientState.VerifyClientConsensusState( + nil, suite.cdc, height, "chainA", tc.clientState.LatestHeight, tc.prefix, tc.proof, tc.consensusState, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + } +} + +// test verification of the connection on chainB being represented in the +// light client on chainA +func (suite *TendermintTestSuite) TestVerifyConnectionState() { + var ( + clientState *types.ClientState + proof []byte + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, _, _, connB, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + connection := suite.chainB.GetConnection(connB) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make connection proof + connectionKey := host.ConnectionKey(connB.ID) + proof, proofHeight = suite.chainB.QueryProof(connectionKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + err := clientState.VerifyConnectionState( + store, suite.chainA.Codec, proofHeight, &prefix, proof, connB.ID, connection, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test verification of the channel on chainB being represented in the light +// client on chainA +func (suite *TendermintTestSuite) TestVerifyChannelState() { + var ( + clientState *types.ClientState + proof []byte + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + channel := suite.chainB.GetChannel(channelB) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make channel proof + channelKey := host.ChannelKey(channelB.PortID, channelB.ID) + proof, proofHeight = suite.chainB.QueryProof(channelKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + err := clientState.VerifyChannelState( + store, suite.chainA.Codec, proofHeight, &prefix, proof, + channelB.PortID, channelB.ID, channel, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test verification of the packet commitment on chainB being represented +// in the light client on chainA. A send from chainB to chainA is simulated. +func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { + var ( + clientState *types.ClientState + proof []byte + delayPeriod uint64 + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + name: "delay period has passed", + malleate: func() { + delayPeriod = uint64(time.Second.Nanoseconds()) + }, + expPass: true, + }, + { + name: "delay period has not passed", + malleate: func() { + delayPeriod = uint64(time.Hour.Nanoseconds()) + }, + expPass: false, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, clienttypes.NewHeight(0, 100), 0) + err := suite.coordinator.SendPacket(suite.chainB, suite.chainA, packet, clientA) + suite.Require().NoError(err) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make packet commitment proof + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight = suite.chainB.QueryProof(packetKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + currentTime := uint64(suite.chainA.GetContext().BlockTime().UnixNano()) + commitment := channeltypes.CommitPacket(suite.chainA.App.IBCKeeper.Codec(), packet) + err = clientState.VerifyPacketCommitment( + store, suite.chainA.Codec, proofHeight, currentTime, delayPeriod, &prefix, proof, + packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), commitment, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test verification of the acknowledgement on chainB being represented +// in the light client on chainA. A send and ack from chainA to chainB +// is simulated. +func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { + var ( + clientState *types.ClientState + proof []byte + delayPeriod uint64 + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + name: "delay period has passed", + malleate: func() { + delayPeriod = uint64(time.Second.Nanoseconds()) + }, + expPass: true, + }, + { + name: "delay period has not passed", + malleate: func() { + delayPeriod = uint64(time.Hour.Nanoseconds()) + }, + expPass: false, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + // send packet + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // write receipt and ack + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make packet acknowledgement proof + acknowledgementKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight = suite.chainB.QueryProof(acknowledgementKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + currentTime := uint64(suite.chainA.GetContext().BlockTime().UnixNano()) + err = clientState.VerifyPacketAcknowledgement( + store, suite.chainA.Codec, proofHeight, currentTime, delayPeriod, &prefix, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ibcmock.MockAcknowledgement, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test verification of the absent acknowledgement on chainB being represented +// in the light client on chainA. A send from chainB to chainA is simulated, but +// no receive. +func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() { + var ( + clientState *types.ClientState + proof []byte + delayPeriod uint64 + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + name: "delay period has passed", + malleate: func() { + delayPeriod = uint64(time.Second.Nanoseconds()) + }, + expPass: true, + }, + { + name: "delay period has not passed", + malleate: func() { + delayPeriod = uint64(time.Hour.Nanoseconds()) + }, + expPass: false, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + // send packet, but no recv + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // need to update chainA's client representing chainB to prove missing ack + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make packet receipt absence proof + receiptKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight = suite.chainB.QueryProof(receiptKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + currentTime := uint64(suite.chainA.GetContext().BlockTime().UnixNano()) + err = clientState.VerifyPacketReceiptAbsence( + store, suite.chainA.Codec, proofHeight, currentTime, delayPeriod, &prefix, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +// test verification of the next receive sequence on chainB being represented +// in the light client on chainA. A send and receive from chainB to chainA is +// simulated. +func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { + var ( + clientState *types.ClientState + proof []byte + delayPeriod uint64 + proofHeight exported.Height + prefix commitmenttypes.MerklePrefix + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "successful verification", func() {}, true, + }, + { + name: "delay period has passed", + malleate: func() { + delayPeriod = uint64(time.Second.Nanoseconds()) + }, + expPass: true, + }, + { + name: "delay period has not passed", + malleate: func() { + delayPeriod = uint64(time.Hour.Nanoseconds()) + }, + expPass: false, + }, + { + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, + }, + { + "latest client height < height", func() { + proofHeight = clientState.LatestHeight.Increment() + }, false, + }, + { + "client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.ORDERED) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, clienttypes.NewHeight(0, 100), 0) + + // send packet + err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB) + suite.Require().NoError(err) + + // next seq recv incremented + err = suite.coordinator.RecvPacket(suite.chainA, suite.chainB, clientA, packet) + suite.Require().NoError(err) + + // need to update chainA's client representing chainB + suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + + var ok bool + clientStateI := suite.chainA.GetClientState(clientA) + clientState, ok = clientStateI.(*types.ClientState) + suite.Require().True(ok) + + prefix = suite.chainB.GetPrefix() + + // make next seq recv proof + nextSeqRecvKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) + proof, proofHeight = suite.chainB.QueryProof(nextSeqRecvKey) + + tc.malleate() // make changes as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + currentTime := uint64(suite.chainA.GetContext().BlockTime().UnixNano()) + err = clientState.VerifyNextSequenceRecv( + store, suite.chainA.Codec, proofHeight, currentTime, delayPeriod, &prefix, proof, + packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()+1, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/codec.go b/x/ibc/light-clients/07-tendermint/types/codec.go new file mode 100644 index 000000000000..5d876c8fe0e8 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/codec.go @@ -0,0 +1,27 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces registers the tendermint concrete client-related +// implementations and interfaces. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*exported.ClientState)(nil), + &ClientState{}, + ) + registry.RegisterImplementations( + (*exported.ConsensusState)(nil), + &ConsensusState{}, + ) + registry.RegisterImplementations( + (*exported.Header)(nil), + &Header{}, + ) + registry.RegisterImplementations( + (*exported.Misbehaviour)(nil), + &Misbehaviour{}, + ) +} diff --git a/x/ibc/light-clients/07-tendermint/types/consensus_state.go b/x/ibc/light-clients/07-tendermint/types/consensus_state.go new file mode 100644 index 000000000000..adb469a3d108 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/consensus_state.go @@ -0,0 +1,55 @@ +package types + +import ( + "time" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmtypes "github.com/tendermint/tendermint/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + 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/core/exported" +) + +// NewConsensusState creates a new ConsensusState instance. +func NewConsensusState( + timestamp time.Time, root commitmenttypes.MerkleRoot, nextValsHash tmbytes.HexBytes, +) *ConsensusState { + return &ConsensusState{ + Timestamp: timestamp, + Root: root, + NextValidatorsHash: nextValsHash, + } +} + +// ClientType returns Tendermint +func (ConsensusState) ClientType() string { + return exported.Tendermint +} + +// GetRoot returns the commitment Root for the specific +func (cs ConsensusState) GetRoot() exported.Root { + return cs.Root +} + +// GetTimestamp returns block time in nanoseconds of the header that created consensus state +func (cs ConsensusState) GetTimestamp() uint64 { + return uint64(cs.Timestamp.UnixNano()) +} + +// ValidateBasic defines a basic validation for the tendermint consensus state. +// NOTE: ProcessedTimestamp may be zero if this is an initial consensus state passed in by relayer +// as opposed to a consensus state constructed by the chain. +func (cs ConsensusState) ValidateBasic() error { + if cs.Root.Empty() { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "root cannot be empty") + } + if err := tmtypes.ValidateHash(cs.NextValidatorsHash); err != nil { + return sdkerrors.Wrap(err, "next validators hash is invalid") + } + 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/consensus_state_test.go b/x/ibc/light-clients/07-tendermint/types/consensus_state_test.go new file mode 100644 index 000000000000..313815d0c7b7 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/consensus_state_test.go @@ -0,0 +1,69 @@ +package types_test + +import ( + "time" + + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" +) + +func (suite *TendermintTestSuite) TestConsensusStateValidateBasic() { + testCases := []struct { + msg string + consensusState *types.ConsensusState + expectPass bool + }{ + {"success", + &types.ConsensusState{ + Timestamp: suite.now, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: suite.valsHash, + }, + true}, + {"root is nil", + &types.ConsensusState{ + Timestamp: suite.now, + Root: commitmenttypes.MerkleRoot{}, + NextValidatorsHash: suite.valsHash, + }, + false}, + {"root is empty", + &types.ConsensusState{ + Timestamp: suite.now, + Root: commitmenttypes.MerkleRoot{}, + NextValidatorsHash: suite.valsHash, + }, + false}, + {"nextvalshash is invalid", + &types.ConsensusState{ + Timestamp: suite.now, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: []byte("hi"), + }, + false}, + + {"timestamp is zero", + &types.ConsensusState{ + Timestamp: time.Time{}, + Root: commitmenttypes.NewMerkleRoot([]byte("app_hash")), + NextValidatorsHash: suite.valsHash, + }, + false}, + } + + for i, tc := range testCases { + tc := tc + + // check just to increase coverage + suite.Require().Equal(exported.Tendermint, tc.consensusState.ClientType()) + suite.Require().Equal(tc.consensusState.GetRoot(), tc.consensusState.Root) + + err := tc.consensusState.ValidateBasic() + if tc.expectPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg) + } + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/errors.go b/x/ibc/light-clients/07-tendermint/types/errors.go new file mode 100644 index 000000000000..276c225b7369 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/errors.go @@ -0,0 +1,25 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + SubModuleName = "tendermint-client" +) + +// IBC tendermint client sentinel errors +var ( + ErrInvalidChainID = sdkerrors.Register(SubModuleName, 2, "invalid chain-id") + ErrInvalidTrustingPeriod = sdkerrors.Register(SubModuleName, 3, "invalid trusting period") + ErrInvalidUnbondingPeriod = sdkerrors.Register(SubModuleName, 4, "invalid unbonding period") + ErrInvalidHeaderHeight = sdkerrors.Register(SubModuleName, 5, "invalid header height") + ErrInvalidHeader = sdkerrors.Register(SubModuleName, 6, "invalid header") + ErrInvalidMaxClockDrift = sdkerrors.Register(SubModuleName, 7, "invalid max clock drift") + ErrProcessedTimeNotFound = sdkerrors.Register(SubModuleName, 8, "processed time not found") + ErrDelayPeriodNotPassed = sdkerrors.Register(SubModuleName, 9, "packet-specified delay period has not been reached") + ErrTrustingPeriodExpired = sdkerrors.Register(SubModuleName, 10, "time since latest trusted state has passed the trusting period") + ErrUnbondingPeriodExpired = sdkerrors.Register(SubModuleName, 11, "time since latest trusted state has passed the unbonding period") + ErrInvalidProofSpecs = sdkerrors.Register(SubModuleName, 12, "invalid proof specs") + ErrInvalidValidatorSet = sdkerrors.Register(SubModuleName, 13, "invalid validator set") +) diff --git a/x/ibc/light-clients/07-tendermint/types/fraction.go b/x/ibc/light-clients/07-tendermint/types/fraction.go new file mode 100644 index 000000000000..e445f19ba6f8 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/fraction.go @@ -0,0 +1,25 @@ +package types + +import ( + tmmath "github.com/tendermint/tendermint/libs/math" + "github.com/tendermint/tendermint/light" +) + +// DefaultTrustLevel is the tendermint light client default trust level +var DefaultTrustLevel = NewFractionFromTm(light.DefaultTrustLevel) + +// NewFractionFromTm returns a new Fraction instance from a tmmath.Fraction +func NewFractionFromTm(f tmmath.Fraction) Fraction { + return Fraction{ + Numerator: f.Numerator, + Denominator: f.Denominator, + } +} + +// ToTendermint converts Fraction to tmmath.Fraction +func (f Fraction) ToTendermint() tmmath.Fraction { + return tmmath.Fraction{ + Numerator: f.Numerator, + Denominator: f.Denominator, + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/genesis.go b/x/ibc/light-clients/07-tendermint/types/genesis.go new file mode 100644 index 000000000000..7124643b55a0 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/genesis.go @@ -0,0 +1,21 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// ExportMetadata exports all the processed times in the client store so they can be included in clients genesis +// and imported by a ClientKeeper +func (cs ClientState) ExportMetadata(store sdk.KVStore) []exported.GenesisMetadata { + gm := make([]exported.GenesisMetadata, 0) + IterateProcessedTime(store, func(key, val []byte) bool { + gm = append(gm, clienttypes.NewGenesisMetadata(key, val)) + return false + }) + if len(gm) == 0 { + return nil + } + return gm +} diff --git a/x/ibc/light-clients/07-tendermint/types/genesis_test.go b/x/ibc/light-clients/07-tendermint/types/genesis_test.go new file mode 100644 index 000000000000..5732151e63ce --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/genesis_test.go @@ -0,0 +1,38 @@ +package types_test + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + 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" +) + +func (suite *TendermintTestSuite) TestExportMetadata() { + clientState := types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), "clientA", clientState) + + gm := clientState.ExportMetadata(suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), "clientA")) + suite.Require().Nil(gm, "client with no metadata returned non-nil exported metadata") + + clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), "clientA") + + // set some processed times + timestamp1 := uint64(time.Now().UnixNano()) + timestamp2 := uint64(time.Now().Add(time.Minute).UnixNano()) + timestampBz1 := sdk.Uint64ToBigEndian(timestamp1) + timestampBz2 := sdk.Uint64ToBigEndian(timestamp2) + types.SetProcessedTime(clientStore, clienttypes.NewHeight(0, 1), timestamp1) + types.SetProcessedTime(clientStore, clienttypes.NewHeight(0, 2), timestamp2) + + gm = clientState.ExportMetadata(suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), "clientA")) + suite.Require().NotNil(gm, "client with metadata returned nil exported metadata") + suite.Require().Len(gm, 2, "exported metadata has unexpected length") + + suite.Require().Equal(types.ProcessedTimeKey(clienttypes.NewHeight(0, 1)), gm[0].GetKey(), "metadata has unexpected key") + suite.Require().Equal(timestampBz1, gm[0].GetValue(), "metadata has unexpected value") + + suite.Require().Equal(types.ProcessedTimeKey(clienttypes.NewHeight(0, 2)), gm[1].GetKey(), "metadata has unexpected key") + suite.Require().Equal(timestampBz2, gm[1].GetValue(), "metadata has unexpected value") +} diff --git a/x/ibc/light-clients/07-tendermint/types/header.go b/x/ibc/light-clients/07-tendermint/types/header.go new file mode 100644 index 000000000000..0b9cfa1db1e2 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/header.go @@ -0,0 +1,83 @@ +package types + +import ( + "bytes" + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + 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/core/exported" +) + +var _ exported.Header = &Header{} + +// ConsensusState returns the updated consensus state associated with the header +func (h Header) ConsensusState() *ConsensusState { + return &ConsensusState{ + Timestamp: h.GetTime(), + Root: commitmenttypes.NewMerkleRoot(h.Header.GetAppHash()), + NextValidatorsHash: h.Header.NextValidatorsHash, + } +} + +// ClientType defines that the Header is a Tendermint consensus algorithm +func (h Header) ClientType() string { + return exported.Tendermint +} + +// GetHeight returns the current height. It returns 0 if the tendermint +// header is nil. +// NOTE: the header.Header is checked to be non nil in ValidateBasic. +func (h Header) GetHeight() exported.Height { + revision := clienttypes.ParseChainID(h.Header.ChainID) + return clienttypes.NewHeight(revision, uint64(h.Header.Height)) +} + +// GetTime returns the current block timestamp. It returns a zero time if +// the tendermint header is nil. +// NOTE: the header.Header is checked to be non nil in ValidateBasic. +func (h Header) GetTime() time.Time { + return h.Header.Time +} + +// ValidateBasic calls the SignedHeader ValidateBasic function and checks +// that validatorsets are not nil. +// NOTE: TrustedHeight and TrustedValidators may be empty when creating client +// with MsgCreateClient +func (h Header) ValidateBasic() error { + if h.SignedHeader == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "tendermint signed header cannot be nil") + } + if h.Header == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "tendermint header cannot be nil") + } + tmSignedHeader, err := tmtypes.SignedHeaderFromProto(h.SignedHeader) + if err != nil { + return sdkerrors.Wrap(err, "header is not a tendermint header") + } + if err := tmSignedHeader.ValidateBasic(h.Header.GetChainID()); err != nil { + return sdkerrors.Wrap(err, "header failed basic validation") + } + + // TrustedHeight is less than Header for updates + // and less than or equal to Header for misbehaviour + if h.TrustedHeight.GT(h.GetHeight()) { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "TrustedHeight %d must be less than or equal to header height %d", + h.TrustedHeight, h.GetHeight()) + } + + if h.ValidatorSet == nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set is nil") + } + tmValset, err := tmtypes.ValidatorSetFromProto(h.ValidatorSet) + if err != nil { + return sdkerrors.Wrap(err, "validator set is not tendermint validator set") + } + if !bytes.Equal(h.Header.ValidatorsHash, tmValset.Hash()) { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "validator set does not match hash") + } + return nil +} diff --git a/x/ibc/light-clients/07-tendermint/types/header_test.go b/x/ibc/light-clients/07-tendermint/types/header_test.go new file mode 100644 index 000000000000..97647f861400 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/header_test.go @@ -0,0 +1,82 @@ +package types_test + +import ( + "time" + + tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + 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" +) + +func (suite *TendermintTestSuite) TestGetHeight() { + header := suite.chainA.LastHeader + suite.Require().NotEqual(uint64(0), header.GetHeight()) +} + +func (suite *TendermintTestSuite) TestGetTime() { + header := suite.chainA.LastHeader + suite.Require().NotEqual(time.Time{}, header.GetTime()) +} + +func (suite *TendermintTestSuite) TestHeaderValidateBasic() { + var ( + header *types.Header + ) + testCases := []struct { + name string + malleate func() + expPass bool + }{ + {"valid header", func() {}, true}, + {"header is nil", func() { + header.Header = nil + }, false}, + {"signed header is nil", func() { + header.SignedHeader = nil + }, false}, + {"SignedHeaderFromProto failed", func() { + header.SignedHeader.Commit.Height = -1 + }, false}, + {"signed header failed tendermint ValidateBasic", func() { + header = suite.chainA.LastHeader + header.SignedHeader.Commit = nil + }, false}, + {"trusted height is greater than header height", func() { + header.TrustedHeight = header.GetHeight().(clienttypes.Height).Increment().(clienttypes.Height) + }, false}, + {"validator set nil", func() { + header.ValidatorSet = nil + }, false}, + {"ValidatorSetFromProto failed", func() { + header.ValidatorSet.Validators[0].PubKey = tmprotocrypto.PublicKey{} + }, false}, + {"header validator hash does not equal hash of validator set", func() { + // use chainB's randomly generated validator set + header.ValidatorSet = suite.chainB.LastHeader.ValidatorSet + }, false}, + } + + suite.Require().Equal(exported.Tendermint, suite.header.ClientType()) + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + header = suite.chainA.LastHeader // must be explicitly changed in malleate + + tc.malleate() + + err := header.ValidateBasic() + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour.go new file mode 100644 index 000000000000..340130d29fa0 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour.go @@ -0,0 +1,141 @@ +package types + +import ( + "bytes" + "time" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.Misbehaviour = &Misbehaviour{} + +// NewMisbehaviour creates a new Misbehaviour instance. +func NewMisbehaviour(clientID string, header1, header2 *Header) *Misbehaviour { + return &Misbehaviour{ + ClientId: clientID, + Header1: header1, + Header2: header2, + } +} + +// ClientType is Tendermint light client +func (misbehaviour Misbehaviour) ClientType() string { + return exported.Tendermint +} + +// GetClientID returns the ID of the client that committed a misbehaviour. +func (misbehaviour Misbehaviour) GetClientID() string { + return misbehaviour.ClientId +} + +// GetHeight returns the height at which misbehaviour occurred +// +// NOTE: assumes that misbehaviour headers have the same height +func (misbehaviour Misbehaviour) GetHeight() exported.Height { + return misbehaviour.Header1.GetHeight() +} + +// GetTime returns the timestamp at which misbehaviour occurred. It uses the +// maximum value from both headers to prevent producing an invalid header outside +// of the misbehaviour age range. +func (misbehaviour Misbehaviour) GetTime() time.Time { + t1, t2 := misbehaviour.Header1.GetTime(), misbehaviour.Header2.GetTime() + if t1.After(t2) { + return t1 + } + return t2 +} + +// ValidateBasic implements Misbehaviour interface +func (misbehaviour Misbehaviour) ValidateBasic() error { + if misbehaviour.Header1 == nil { + return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil") + } + if misbehaviour.Header2 == nil { + return sdkerrors.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil") + } + if misbehaviour.Header1.TrustedHeight.RevisionHeight == 0 { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height") + } + if misbehaviour.Header2.TrustedHeight.RevisionHeight == 0 { + return sdkerrors.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height") + } + if misbehaviour.Header1.TrustedValidators == nil { + return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty") + } + if misbehaviour.Header2.TrustedValidators == nil { + return sdkerrors.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty") + } + if misbehaviour.Header1.Header.ChainID != misbehaviour.Header2.Header.ChainID { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs") + } + + if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { + return sdkerrors.Wrap(err, "misbehaviour client ID is invalid") + } + + // ValidateBasic on both validators + if err := misbehaviour.Header1.ValidateBasic(); err != nil { + return sdkerrors.Wrap( + clienttypes.ErrInvalidMisbehaviour, + sdkerrors.Wrap(err, "header 1 failed validation").Error(), + ) + } + if err := misbehaviour.Header2.ValidateBasic(); err != nil { + return sdkerrors.Wrap( + clienttypes.ErrInvalidMisbehaviour, + sdkerrors.Wrap(err, "header 2 failed validation").Error(), + ) + } + // Ensure that Heights are the same + if misbehaviour.Header1.GetHeight() != misbehaviour.Header2.GetHeight() { + return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "headers in misbehaviour are on different heights (%d ≠ %d)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight()) + } + + blockID1, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID) + if err != nil { + return sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour") + } + blockID2, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID) + if err != nil { + return sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour") + } + + // Ensure that Commit Hashes are different + if bytes.Equal(blockID1.Hash, blockID2.Hash) { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers block hashes are equal") + } + if err := validCommit(misbehaviour.Header1.Header.ChainID, *blockID1, + misbehaviour.Header1.Commit, misbehaviour.Header1.ValidatorSet); err != nil { + return err + } + if err := validCommit(misbehaviour.Header2.Header.ChainID, *blockID2, + misbehaviour.Header2.Commit, misbehaviour.Header2.ValidatorSet); err != nil { + return err + } + return nil +} + +// validCommit checks if the given commit is a valid commit from the passed-in validatorset +func validCommit(chainID string, blockID tmtypes.BlockID, commit *tmproto.Commit, valSet *tmproto.ValidatorSet) (err error) { + tmCommit, err := tmtypes.CommitFromProto(commit) + if err != nil { + return sdkerrors.Wrap(err, "commit is not tendermint commit type") + } + tmValset, err := tmtypes.ValidatorSetFromProto(valSet) + if err != nil { + return sdkerrors.Wrap(err, "validator set is not tendermint validator set type") + } + + if err := tmValset.VerifyCommitLight(chainID, blockID, tmCommit.Height, tmCommit); err != nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header") + } + + return nil +} diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle.go new file mode 100644 index 000000000000..4c55552d3051 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle.go @@ -0,0 +1,119 @@ +package types + +import ( + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// CheckMisbehaviourAndUpdateState determines whether or not two conflicting +// headers at the same height would have convinced the light client. +// +// NOTE: consensusState1 is the trusted consensus state that corresponds to the TrustedHeight +// of misbehaviour.Header1 +// Similarly, consensusState2 is the trusted consensus state that corresponds +// to misbehaviour.Header2 +func (cs ClientState) CheckMisbehaviourAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryMarshaler, + clientStore sdk.KVStore, + misbehaviour exported.Misbehaviour, +) (exported.ClientState, error) { + tmMisbehaviour, ok := misbehaviour.(*Misbehaviour) + if !ok { + return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "expected type %T, got %T", misbehaviour, &Misbehaviour{}) + } + + // If client is already frozen at earlier height than misbehaviour, return with error + if cs.IsFrozen() && cs.FrozenHeight.LTE(misbehaviour.GetHeight()) { + return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, + "client is already frozen at earlier height %s than misbehaviour height %s", cs.FrozenHeight, misbehaviour.GetHeight()) + } + + // Retrieve trusted consensus states for each Header in misbehaviour + // and unmarshal from clientStore + + // Get consensus bytes from clientStore + tmConsensusState1, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header1.TrustedHeight) + if err != nil { + return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header1 at TrustedHeight: %s", tmMisbehaviour.Header1) + } + + // Get consensus bytes from clientStore + tmConsensusState2, err := GetConsensusState(clientStore, cdc, tmMisbehaviour.Header2.TrustedHeight) + if err != nil { + return nil, sdkerrors.Wrapf(err, "could not get trusted consensus state from clientStore for Header2 at TrustedHeight: %s", tmMisbehaviour.Header2) + } + + // Check the validity of the two conflicting headers against their respective + // trusted consensus states + // NOTE: header height and commitment root assertions are checked in + // misbehaviour.ValidateBasic by the client keeper and msg.ValidateBasic + // by the base application. + if err := checkMisbehaviourHeader( + &cs, tmConsensusState1, tmMisbehaviour.Header1, ctx.BlockTime(), + ); err != nil { + return nil, sdkerrors.Wrap(err, "verifying Header1 in Misbehaviour failed") + } + if err := checkMisbehaviourHeader( + &cs, tmConsensusState2, tmMisbehaviour.Header2, ctx.BlockTime(), + ); err != nil { + return nil, sdkerrors.Wrap(err, "verifying Header2 in Misbehaviour failed") + } + + cs.FrozenHeight = tmMisbehaviour.GetHeight().(clienttypes.Height) + return &cs, nil +} + +// checkMisbehaviourHeader checks that a Header in Misbehaviour is valid misbehaviour given +// a trusted ConsensusState +func checkMisbehaviourHeader( + clientState *ClientState, consState *ConsensusState, header *Header, currentTimestamp time.Time, +) error { + + tmTrustedValset, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return sdkerrors.Wrap(err, "trusted validator set is not tendermint validator set type") + } + + tmCommit, err := tmtypes.CommitFromProto(header.Commit) + if err != nil { + return sdkerrors.Wrap(err, "commit is not tendermint commit type") + } + + // check the trusted fields for the header against ConsensusState + if err := checkTrustedHeader(header, consState); err != nil { + return err + } + + // assert that the age of the trusted consensus state is not older than the trusting period + if currentTimestamp.Sub(consState.Timestamp) >= clientState.TrustingPeriod { + return sdkerrors.Wrapf( + ErrTrustingPeriodExpired, + "current timestamp minus the latest consensus state timestamp is greater than or equal to the trusting period (%d >= %d)", + currentTimestamp.Sub(consState.Timestamp), clientState.TrustingPeriod, + ) + } + + chainID := clientState.GetChainID() + // If chainID is in revision format, then set revision number of chainID with the revision number + // of the misbehaviour header + if clienttypes.IsRevisionFormat(chainID) { + chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) + } + + // - ValidatorSet must have TrustLevel similarity with trusted FromValidatorSet + // - ValidatorSets on both headers are valid given the last trusted ValidatorSet + if err := tmTrustedValset.VerifyCommitLightTrusting( + chainID, tmCommit, clientState.TrustLevel.ToTendermint(), + ); err != nil { + return sdkerrors.Wrapf(clienttypes.ErrInvalidMisbehaviour, "validator set in header has too much change from trusted validator set: %v", err) + } + return nil +} diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go new file mode 100644 index 000000000000..3ca2e4dc1199 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour_handle_test.go @@ -0,0 +1,372 @@ +package types_test + +import ( + "fmt" + "time" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmtypes "github.com/tendermint/tendermint/types" + + 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/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +func (suite *TendermintTestSuite) TestCheckMisbehaviourAndUpdateState() { + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + altVal := tmtypes.NewValidator(altPubKey, 4) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + bothValsHash := bothValSet.Hash() + // Create alternative validator set with only altVal + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + + _, suiteVal := suite.valSet.GetByIndex(0) + + // Create signer array and ensure it is in same order as bothValSet + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + + altSigners := []tmtypes.PrivValidator{altPrivVal} + + heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) + heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) + + testCases := []struct { + name string + clientState exported.ClientState + consensusState1 exported.ConsensusState + height1 clienttypes.Height + consensusState2 exported.ConsensusState + height2 clienttypes.Height + misbehaviour exported.Misbehaviour + timestamp time.Time + expPass bool + }{ + { + "valid misbehavior misbehaviour", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehavior at height greater than last consensusState", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour with different trusted heights", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour at a previous revision", + types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour at a future revision", + types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision0, 3, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "valid misbehaviour with trusted heights at a previous revision", + types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, heightMinus3, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "consensus state's valset hash different from misbehaviour should still pass", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + true, + }, + { + "invalid misbehavior misbehaviour from different chain", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "invalid misbehavior misbehaviour with trusted height different from trusted consensus state", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "invalid misbehavior misbehaviour with trusted validators different from trusted consensus state", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), suite.valsHash), + heightMinus3, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus3, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "already frozen client state", + &types.ClientState{FrozenHeight: clienttypes.NewHeight(0, 1)}, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "trusted consensus state does not exist", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + nil, // consensus state for trusted height - 1 does not exist in store + clienttypes.Height{}, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "invalid tendermint misbehaviour", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + nil, + suite.now, + false, + }, + { + "provided height > header height", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "trusting period expired", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(time.Time{}, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + heightMinus1, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now.Add(trustingPeriod), + false, + }, + { + "trusted validators is incorrect for given consensus state", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, suite.valSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "first valset has too much change", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), bothValSet, bothValSet, bothSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "second valset has too much change", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + { + "both valsets have too much change", + types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false), + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + types.NewConsensusState(suite.now, commitmenttypes.NewMerkleRoot(tmhash.Sum([]byte("app_hash"))), bothValsHash), + height, + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now, altValSet, bothValSet, altSigners), + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), height, suite.now.Add(time.Minute), altValSet, bothValSet, altSigners), + ClientId: chainID, + }, + suite.now, + false, + }, + } + + for i, tc := range testCases { + tc := tc + suite.Run(fmt.Sprintf("Case: %s", tc.name), func() { + // reset suite to create fresh application state + suite.SetupTest() + + // Set current timestamp in context + ctx := suite.chainA.GetContext().WithBlockTime(tc.timestamp) + + // Set trusted consensus states in client store + + if tc.consensusState1 != nil { + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height1, tc.consensusState1) + } + if tc.consensusState2 != nil { + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(ctx, clientID, tc.height2, tc.consensusState2) + } + + clientState, err := tc.clientState.CheckMisbehaviourAndUpdateState( + ctx, + suite.cdc, + suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(ctx, clientID), // pass in clientID prefixed clientStore + tc.misbehaviour, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + suite.Require().NotNil(clientState, "valid test case %d failed: %s", i, tc.name) + suite.Require().True(clientState.IsFrozen(), "valid test case %d failed: %s", i, tc.name) + suite.Require().Equal(tc.misbehaviour.GetHeight(), clientState.GetFrozenHeight(), + "valid test case %d failed: %s. Expected FrozenHeight %s got %s", tc.misbehaviour.GetHeight(), clientState.GetFrozenHeight()) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(clientState, "invalid test case %d passed: %s", i, tc.name) + } + }) + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go b/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go new file mode 100644 index 000000000000..dede4e6021a4 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/misbehaviour_test.go @@ -0,0 +1,244 @@ +package types_test + +import ( + "time" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + 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" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +func (suite *TendermintTestSuite) TestMisbehaviour() { + signers := []tmtypes.PrivValidator{suite.privVal} + heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) + + misbehaviour := &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + ClientId: clientID, + } + + suite.Require().Equal(exported.Tendermint, misbehaviour.ClientType()) + suite.Require().Equal(clientID, misbehaviour.GetClientID()) + suite.Require().Equal(height, misbehaviour.GetHeight()) +} + +func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() { + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + revisionHeight := int64(height.RevisionHeight) + + altVal := tmtypes.NewValidator(altPubKey, revisionHeight) + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + // Create alternative validator set with only altVal + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + + signers := []tmtypes.PrivValidator{suite.privVal} + + // Create signer array and ensure it is in same order as bothValSet + _, suiteVal := suite.valSet.GetByIndex(0) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + + altSigners := []tmtypes.PrivValidator{altPrivVal} + + heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) + + testCases := []struct { + name string + misbehaviour *types.Misbehaviour + malleateMisbehaviour func(misbehaviour *types.Misbehaviour) error + expPass bool + }{ + { + "valid misbehaviour", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + true, + }, + { + "misbehaviour Header1 is nil", + types.NewMisbehaviour(clientID, nil, suite.header), + func(m *types.Misbehaviour) error { return nil }, + false, + }, + { + "misbehaviour Header2 is nil", + types.NewMisbehaviour(clientID, suite.header, nil), + func(m *types.Misbehaviour) error { return nil }, + false, + }, + { + "valid misbehaviour with different trusted headers", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, bothValSet, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + true, + }, + { + "trusted height is 0 in Header1", + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + Header2: suite.header, + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "trusted height is 0 in Header2", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "trusted valset is nil in Header1", + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + Header2: suite.header, + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "trusted valset is nil in Header2", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, nil, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "invalid client ID ", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + ClientId: "GAIA", + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "chainIDs do not match", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "mismatched heights", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, 4), suite.now, suite.valSet, suite.valSet, signers), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "same block id", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.header, + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { return nil }, + false, + }, + { + "header 1 doesn't have 2/3 majority", + &types.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + Header2: suite.header, + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { + // voteSet contains only altVal which is less than 2/3 of total power (height/1height) + wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header1.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) + blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID) + if err != nil { + return err + } + + tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header1.Commit.Round, wrongVoteSet, altSigners, suite.now) + misbehaviour.Header1.Commit = tmCommit.ToProto() + return err + }, + false, + }, + { + "header 2 doesn't have 2/3 majority", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { + // voteSet contains only altVal which is less than 2/3 of total power (height/1height) + wrongVoteSet := tmtypes.NewVoteSet(chainID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), 1, tmproto.PrecommitType, altValSet) + blockID, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID) + if err != nil { + return err + } + + tmCommit, err := tmtypes.MakeCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header2.Commit.Round, wrongVoteSet, altSigners, suite.now) + misbehaviour.Header2.Commit = tmCommit.ToProto() + return err + }, + false, + }, + { + "validators sign off on wrong commit", + &types.Misbehaviour{ + Header1: suite.header, + Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, suite.valSet, bothSigners), + ClientId: clientID, + }, + func(misbehaviour *types.Misbehaviour) error { + tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset"))) + misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto() + return nil + }, + false, + }, + } + + for i, tc := range testCases { + tc := tc + + err := tc.malleateMisbehaviour(tc.misbehaviour) + suite.Require().NoError(err) + + if tc.expPass { + suite.Require().NoError(tc.misbehaviour.ValidateBasic(), "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(tc.misbehaviour.ValidateBasic(), "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/proposal_handle.go b/x/ibc/light-clients/07-tendermint/types/proposal_handle.go new file mode 100644 index 000000000000..4cd3eb376cbd --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/proposal_handle.go @@ -0,0 +1,127 @@ +package types + +import ( + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "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) + if !ok { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header, + ) + } + + // get consensus state corresponding to client state to check if the client is expired + consensusState, err := GetConsensusState(clientStore, cdc, cs.GetLatestHeight()) + if err != nil { + return nil, nil, sdkerrors.Wrapf( + err, "could not get consensus state from clientstore at height: %d", cs.GetLatestHeight(), + ) + } + + switch { + + case cs.IsFrozen(): + if !cs.AllowUpdateAfterMisbehaviour { + return nil, 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()) + } + + // 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") + } + +} + +// 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) { + + // the client is expired and either AllowUpdateAfterMisbehaviour or AllowUpdateAfterExpiry + // is set to true so light validation of the header is executed + if err := cs.checkProposedHeader(consensusState, header, currentTimestamp); err != nil { + return nil, nil, err + } + + newClientState, consensusState := update(ctx, clientStore, &cs, header) + return newClientState, consensusState, nil +} + +// 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) + } + + // 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(), + ) + } + + if err := tmSignedHeader.ValidateBasic(cs.GetChainID()); err != nil { + return sdkerrors.Wrap(err, "signed header failed basic validation") + } + + if cs.IsExpired(header.GetTime(), currentTimestamp) { + return sdkerrors.Wrap(clienttypes.ErrInvalidHeader, "header timestamp is already expired") + } + + return nil +} 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 new file mode 100644 index 000000000000..6863ad1d1c80 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/proposal_handle_test.go @@ -0,0 +1,356 @@ +package types_test + +import ( + 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" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +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) +} + +// 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() { + 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 + }{ + { + name: "not allowed to be updated, not frozen or expired", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: false, + ExpireClient: false, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "not allowed to be updated, client is frozen", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: true, + ExpireClient: false, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "not allowed to be updated, client is expired", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: false, + ExpireClient: true, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "not allowed to be updated, client is frozen and expired", + AllowUpdateAfterExpiry: false, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: true, + ExpireClient: true, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "allowed to be updated only after misbehaviour, not frozen or expired", + AllowUpdateAfterExpiry: false, + 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, + }, + { + name: "allowed to be updated only after misbehaviour, client is expired", + AllowUpdateAfterExpiry: false, + 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, + }, + { + name: "allowed to be updated only after expiry, not frozen or expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: false, + ExpireClient: false, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "allowed to be updated only after expiry, client is frozen", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: true, + ExpireClient: false, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "PASS: allowed to be updated only after expiry, client is expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: false, + ExpireClient: true, + expPassUnfreeze: true, + expPassUnexpire: true, + }, + { + name: "allowed to be updated only after expiry, client is frozen and expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: false, + FreezeClient: true, + ExpireClient: true, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "allowed to be updated after expiry and misbehaviour, not frozen or expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: false, + ExpireClient: false, + expPassUnfreeze: false, + expPassUnexpire: false, + }, + { + name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: true, + ExpireClient: false, + expPassUnfreeze: true, + expPassUnexpire: false, + }, + { + name: "PASS: allowed to be updated after expiry and misbehaviour, client is expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: false, + ExpireClient: true, + expPassUnfreeze: true, + expPassUnexpire: true, + }, + { + name: "PASS: allowed to be updated after expiry and misbehaviour, client is frozen and expired", + AllowUpdateAfterExpiry: true, + AllowUpdateAfterMisbehaviour: true, + FreezeClient: true, + ExpireClient: true, + expPassUnfreeze: true, + expPassUnexpire: true, + }, + } + + for _, tc := range testCases { + tc := tc + + // for each test case a header used for unexpiring clients and unfreezing + // a client are each tested to ensure that unexpiry headers cannot update + // a client when a unfreezing header is required. + suite.Run(tc.name, func() { + + // 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 + if tc.FreezeClient { + clientState.FrozenHeight = frozenHeight + } + if tc.ExpireClient { + suite.chainA.ExpireClient(clientState.TrustingPeriod) + suite.chainB.ExpireClient(clientState.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) + + 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) + + if tc.expPassUnfreeze { + 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) + } + + // 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 + + 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) + + if tc.expPassUnexpire { + 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) + } + }) + } +} + +// test softer validation on headers used for unexpiring clients +func (suite *TendermintTestSuite) TestCheckProposedHeader() { + var ( + header *types.Header + clientState *types.ClientState + clientA string + err error + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() {}, true, + }, + { + "invalid signed header", func() { + header.SignedHeader = nil + }, false, + }, + { + "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, + }, + { + "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, + }, + { + "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) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + 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 + + 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) + + 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 new file mode 100644 index 000000000000..8b2720c5a995 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/store.go @@ -0,0 +1,89 @@ +package types + +import ( + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// KeyProcessedTime is appended to consensus state key to store the processed time +var KeyProcessedTime = []byte("/processedTime") + +// 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) { + bz := store.Get(host.ConsensusStateKey(height)) + if bz == nil { + return nil, sdkerrors.Wrapf( + clienttypes.ErrConsensusStateNotFound, + "consensus state does not exist for height %s", height, + ) + } + + consensusStateI, err := clienttypes.UnmarshalConsensusState(cdc, bz) + if err != nil { + return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "unmarshal error: %v", err) + } + + consensusState, ok := consensusStateI.(*ConsensusState) + if !ok { + return nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidConsensus, + "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}, + ) + } + + return consensusState, nil +} + +// IterateProcessedTime iterates through the prefix store and applies the callback. +// If the cb returns true, then iterator will close and stop. +func IterateProcessedTime(store sdk.KVStore, cb func(key, val []byte) bool) { + iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyConsensusStatePrefix)) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + keySplit := strings.Split(string(iterator.Key()), "/") + // processed time key in prefix store has format: "consensusState//processedTime" + if len(keySplit) != 3 || keySplit[2] != "processedTime" { + // ignore all consensus state keys + continue + } + + if cb(iterator.Key(), iterator.Value()) { + break + } + } +} + +// ProcessedTime Store code + +// ProcessedTimeKey returns the key under which the processed time will be stored in the client store. +func ProcessedTimeKey(height exported.Height) []byte { + return append(host.ConsensusStateKey(height), KeyProcessedTime...) +} + +// SetProcessedTime stores the time at which a header was processed and the corresponding consensus state was created. +// This is useful when validating whether a packet has reached the specified delay period in the tendermint client's +// verification functions +func SetProcessedTime(clientStore sdk.KVStore, height exported.Height, timeNs uint64) { + key := ProcessedTimeKey(height) + val := sdk.Uint64ToBigEndian(timeNs) + clientStore.Set(key, val) +} + +// GetProcessedTime gets the time (in nanoseconds) at which this chain received and processed a tendermint header. +// This is used to validate that a received packet has passed the delay period. +func GetProcessedTime(clientStore sdk.KVStore, height exported.Height) (uint64, bool) { + key := ProcessedTimeKey(height) + bz := clientStore.Get(key) + if bz == nil { + return 0, false + } + return sdk.BigEndianToUint64(bz), true +} diff --git a/x/ibc/light-clients/07-tendermint/types/store_test.go b/x/ibc/light-clients/07-tendermint/types/store_test.go new file mode 100644 index 000000000000..b8badc09479a --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/store_test.go @@ -0,0 +1,113 @@ +package types_test + +import ( + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" +) + +func (suite *TendermintTestSuite) TestGetConsensusState() { + var ( + height exported.Height + clientA string + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() {}, true, + }, + { + "consensus state not found", func() { + // use height with no consensus state set + height = height.(clienttypes.Height).Increment() + }, false, + }, + { + "not a consensus state interface", func() { + // marshal an empty client state and set as consensus state + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + clientStateBz := suite.chainA.App.IBCKeeper.ClientKeeper.MustMarshalClientState(&types.ClientState{}) + store.Set(host.ConsensusStateKey(height), clientStateBz) + }, false, + }, + { + "invalid consensus state (solomachine)", func() { + // marshal and set solomachine consensus state + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + consensusStateBz := suite.chainA.App.IBCKeeper.ClientKeeper.MustMarshalConsensusState(&solomachinetypes.ConsensusState{}) + store.Set(host.ConsensusStateKey(height), consensusStateBz) + }, false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + clientA, _, _, _, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB, channeltypes.UNORDERED) + clientState := suite.chainA.GetClientState(clientA) + height = clientState.GetLatestHeight() + + tc.malleate() // change vars as necessary + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + consensusState, err := types.GetConsensusState(store, suite.chainA.Codec, height) + + if tc.expPass { + suite.Require().NoError(err) + expConsensusState, found := suite.chainA.GetConsensusState(clientA, height) + suite.Require().True(found) + suite.Require().Equal(expConsensusState, consensusState) + } else { + suite.Require().Error(err) + suite.Require().Nil(consensusState) + } + }) + } +} + +func (suite *TendermintTestSuite) TestGetProcessedTime() { + // Verify ProcessedTime on CreateClient + // coordinator increments time before creating client + expectedTime := suite.chainA.CurrentHeader.Time.Add(ibctesting.TimeIncrement) + + clientA, err := suite.coordinator.CreateClient(suite.chainA, suite.chainB, exported.Tendermint) + suite.Require().NoError(err) + + clientState := suite.chainA.GetClientState(clientA) + height := clientState.GetLatestHeight() + + store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + actualTime, ok := types.GetProcessedTime(store, height) + suite.Require().True(ok, "could not retrieve processed time for stored consensus state") + suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") + + // Verify ProcessedTime on UpdateClient + // coordinator increments time before updating client + expectedTime = suite.chainA.CurrentHeader.Time.Add(ibctesting.TimeIncrement) + + err = suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + clientState = suite.chainA.GetClientState(clientA) + height = clientState.GetLatestHeight() + + store = suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + actualTime, ok = types.GetProcessedTime(store, height) + suite.Require().True(ok, "could not retrieve processed time for stored consensus state") + suite.Require().Equal(uint64(expectedTime.UnixNano()), actualTime, "retrieved processed time is not expected value") + + // try to get processed time for height that doesn't exist in store + _, ok = types.GetProcessedTime(store, clienttypes.NewHeight(1, 1)) + suite.Require().False(ok, "retrieved processed time for a non-existent consensus state") +} diff --git a/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go b/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go new file mode 100644 index 000000000000..547a88318eb7 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/tendermint.pb.go @@ -0,0 +1,1914 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/tendermint/v1/tendermint.proto + +package types + +import ( + fmt "fmt" + _go "github.com/confio/ics23/go" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + types1 "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + github_com_tendermint_tendermint_libs_bytes "github.com/tendermint/tendermint/libs/bytes" + types2 "github.com/tendermint/tendermint/proto/tendermint/types" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// ClientState from Tendermint tracks the current validator set, latest height, +// and a possible frozen height. +type ClientState struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + TrustLevel Fraction `protobuf:"bytes,2,opt,name=trust_level,json=trustLevel,proto3" json:"trust_level" yaml:"trust_level"` + // duration of the period since the LastestTimestamp during which the + // submitted headers are valid for upgrade + TrustingPeriod time.Duration `protobuf:"bytes,3,opt,name=trusting_period,json=trustingPeriod,proto3,stdduration" json:"trusting_period" yaml:"trusting_period"` + // duration of the staking unbonding period + UnbondingPeriod time.Duration `protobuf:"bytes,4,opt,name=unbonding_period,json=unbondingPeriod,proto3,stdduration" json:"unbonding_period" yaml:"unbonding_period"` + // defines how much new (untrusted) header's Time can drift into the future. + MaxClockDrift time.Duration `protobuf:"bytes,5,opt,name=max_clock_drift,json=maxClockDrift,proto3,stdduration" json:"max_clock_drift" yaml:"max_clock_drift"` + // Block height when the client was frozen due to a misbehaviour + FrozenHeight types.Height `protobuf:"bytes,6,opt,name=frozen_height,json=frozenHeight,proto3" json:"frozen_height" yaml:"frozen_height"` + // Latest height the client was updated to + LatestHeight types.Height `protobuf:"bytes,7,opt,name=latest_height,json=latestHeight,proto3" json:"latest_height" yaml:"latest_height"` + // Proof specifications used in verifying counterparty state + ProofSpecs []*_go.ProofSpec `protobuf:"bytes,8,rep,name=proof_specs,json=proofSpecs,proto3" json:"proof_specs,omitempty" yaml:"proof_specs"` + // Path at which next upgraded client will be committed. + // Each element corresponds to the key for a single CommitmentProof in the chained proof. + // NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState` + // ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState` + // For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}` + UpgradePath []string `protobuf:"bytes,9,rep,name=upgrade_path,json=upgradePath,proto3" json:"upgrade_path,omitempty" yaml:"upgrade_path"` + // This flag, when set to true, will allow governance to recover a client + // which has expired + AllowUpdateAfterExpiry bool `protobuf:"varint,10,opt,name=allow_update_after_expiry,json=allowUpdateAfterExpiry,proto3" json:"allow_update_after_expiry,omitempty" yaml:"allow_update_after_expiry"` + // This flag, when set to true, will allow governance to unfreeze a client + // whose chain has experienced a misbehaviour event + AllowUpdateAfterMisbehaviour bool `protobuf:"varint,11,opt,name=allow_update_after_misbehaviour,json=allowUpdateAfterMisbehaviour,proto3" json:"allow_update_after_misbehaviour,omitempty" yaml:"allow_update_after_misbehaviour"` +} + +func (m *ClientState) Reset() { *m = ClientState{} } +func (m *ClientState) String() string { return proto.CompactTextString(m) } +func (*ClientState) ProtoMessage() {} +func (*ClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_c6d6cf2b288949be, []int{0} +} +func (m *ClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientState.Merge(m, src) +} +func (m *ClientState) XXX_Size() int { + return m.Size() +} +func (m *ClientState) XXX_DiscardUnknown() { + xxx_messageInfo_ClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientState proto.InternalMessageInfo + +// ConsensusState defines the consensus state from Tendermint. +type ConsensusState struct { + // timestamp that corresponds to the block height in which the ConsensusState + // was stored. + Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"timestamp"` + // commitment root (i.e app hash) + Root types1.MerkleRoot `protobuf:"bytes,2,opt,name=root,proto3" json:"root"` + NextValidatorsHash github_com_tendermint_tendermint_libs_bytes.HexBytes `protobuf:"bytes,3,opt,name=next_validators_hash,json=nextValidatorsHash,proto3,casttype=github.com/tendermint/tendermint/libs/bytes.HexBytes" json:"next_validators_hash,omitempty" yaml:"next_validators_hash"` +} + +func (m *ConsensusState) Reset() { *m = ConsensusState{} } +func (m *ConsensusState) String() string { return proto.CompactTextString(m) } +func (*ConsensusState) ProtoMessage() {} +func (*ConsensusState) Descriptor() ([]byte, []int) { + return fileDescriptor_c6d6cf2b288949be, []int{1} +} +func (m *ConsensusState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConsensusState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConsensusState.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 *ConsensusState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConsensusState.Merge(m, src) +} +func (m *ConsensusState) XXX_Size() int { + return m.Size() +} +func (m *ConsensusState) XXX_DiscardUnknown() { + xxx_messageInfo_ConsensusState.DiscardUnknown(m) +} + +var xxx_messageInfo_ConsensusState proto.InternalMessageInfo + +// Misbehaviour is a wrapper over two conflicting Headers +// that implements Misbehaviour interface expected by ICS-02 +type Misbehaviour struct { + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty" yaml:"client_id"` + Header1 *Header `protobuf:"bytes,2,opt,name=header_1,json=header1,proto3" json:"header_1,omitempty" yaml:"header_1"` + Header2 *Header `protobuf:"bytes,3,opt,name=header_2,json=header2,proto3" json:"header_2,omitempty" yaml:"header_2"` +} + +func (m *Misbehaviour) Reset() { *m = Misbehaviour{} } +func (m *Misbehaviour) String() string { return proto.CompactTextString(m) } +func (*Misbehaviour) ProtoMessage() {} +func (*Misbehaviour) Descriptor() ([]byte, []int) { + return fileDescriptor_c6d6cf2b288949be, []int{2} +} +func (m *Misbehaviour) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Misbehaviour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Misbehaviour.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 *Misbehaviour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Misbehaviour.Merge(m, src) +} +func (m *Misbehaviour) XXX_Size() int { + return m.Size() +} +func (m *Misbehaviour) XXX_DiscardUnknown() { + xxx_messageInfo_Misbehaviour.DiscardUnknown(m) +} + +var xxx_messageInfo_Misbehaviour proto.InternalMessageInfo + +// Header defines the Tendermint client consensus Header. +// It encapsulates all the information necessary to update from a trusted +// Tendermint ConsensusState. The inclusion of TrustedHeight and +// TrustedValidators allows this update to process correctly, so long as the +// ConsensusState for the TrustedHeight exists, this removes race conditions +// among relayers The SignedHeader and ValidatorSet are the new untrusted update +// fields for the client. The TrustedHeight is the height of a stored +// ConsensusState on the client that will be used to verify the new untrusted +// header. The Trusted ConsensusState must be within the unbonding period of +// current time in order to correctly verify, and the TrustedValidators must +// hash to TrustedConsensusState.NextValidatorsHash since that is the last +// trusted validator set at the TrustedHeight. +type Header struct { + *types2.SignedHeader `protobuf:"bytes,1,opt,name=signed_header,json=signedHeader,proto3,embedded=signed_header" json:"signed_header,omitempty" yaml:"signed_header"` + ValidatorSet *types2.ValidatorSet `protobuf:"bytes,2,opt,name=validator_set,json=validatorSet,proto3" json:"validator_set,omitempty" yaml:"validator_set"` + TrustedHeight types.Height `protobuf:"bytes,3,opt,name=trusted_height,json=trustedHeight,proto3" json:"trusted_height" yaml:"trusted_height"` + TrustedValidators *types2.ValidatorSet `protobuf:"bytes,4,opt,name=trusted_validators,json=trustedValidators,proto3" json:"trusted_validators,omitempty" yaml:"trusted_validators"` +} + +func (m *Header) Reset() { *m = Header{} } +func (m *Header) String() string { return proto.CompactTextString(m) } +func (*Header) ProtoMessage() {} +func (*Header) Descriptor() ([]byte, []int) { + return fileDescriptor_c6d6cf2b288949be, []int{3} +} +func (m *Header) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Header.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 *Header) XXX_Merge(src proto.Message) { + xxx_messageInfo_Header.Merge(m, src) +} +func (m *Header) XXX_Size() int { + return m.Size() +} +func (m *Header) XXX_DiscardUnknown() { + xxx_messageInfo_Header.DiscardUnknown(m) +} + +var xxx_messageInfo_Header proto.InternalMessageInfo + +func (m *Header) GetValidatorSet() *types2.ValidatorSet { + if m != nil { + return m.ValidatorSet + } + return nil +} + +func (m *Header) GetTrustedHeight() types.Height { + if m != nil { + return m.TrustedHeight + } + return types.Height{} +} + +func (m *Header) GetTrustedValidators() *types2.ValidatorSet { + if m != nil { + return m.TrustedValidators + } + return nil +} + +// Fraction defines the protobuf message type for tmmath.Fraction that only supports positive values. +type Fraction struct { + Numerator uint64 `protobuf:"varint,1,opt,name=numerator,proto3" json:"numerator,omitempty"` + Denominator uint64 `protobuf:"varint,2,opt,name=denominator,proto3" json:"denominator,omitempty"` +} + +func (m *Fraction) Reset() { *m = Fraction{} } +func (m *Fraction) String() string { return proto.CompactTextString(m) } +func (*Fraction) ProtoMessage() {} +func (*Fraction) Descriptor() ([]byte, []int) { + return fileDescriptor_c6d6cf2b288949be, []int{4} +} +func (m *Fraction) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Fraction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Fraction.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 *Fraction) XXX_Merge(src proto.Message) { + xxx_messageInfo_Fraction.Merge(m, src) +} +func (m *Fraction) XXX_Size() int { + return m.Size() +} +func (m *Fraction) XXX_DiscardUnknown() { + xxx_messageInfo_Fraction.DiscardUnknown(m) +} + +var xxx_messageInfo_Fraction proto.InternalMessageInfo + +func (m *Fraction) GetNumerator() uint64 { + if m != nil { + return m.Numerator + } + return 0 +} + +func (m *Fraction) GetDenominator() uint64 { + if m != nil { + return m.Denominator + } + return 0 +} + +func init() { + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.tendermint.v1.ClientState") + proto.RegisterType((*ConsensusState)(nil), "ibc.lightclients.tendermint.v1.ConsensusState") + proto.RegisterType((*Misbehaviour)(nil), "ibc.lightclients.tendermint.v1.Misbehaviour") + proto.RegisterType((*Header)(nil), "ibc.lightclients.tendermint.v1.Header") + proto.RegisterType((*Fraction)(nil), "ibc.lightclients.tendermint.v1.Fraction") +} + +func init() { + proto.RegisterFile("ibc/lightclients/tendermint/v1/tendermint.proto", fileDescriptor_c6d6cf2b288949be) +} + +var fileDescriptor_c6d6cf2b288949be = []byte{ + // 1081 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0xe3, 0xc4, + 0x17, 0x6f, 0xda, 0x7e, 0xb7, 0xc9, 0x24, 0xdd, 0xf6, 0xeb, 0x2d, 0xdd, 0xb4, 0x74, 0xe3, 0xc8, + 0xa0, 0x25, 0x42, 0xaa, 0x4d, 0xb2, 0x48, 0x48, 0x15, 0x17, 0xdc, 0x82, 0x5a, 0xc4, 0x4a, 0x95, + 0xcb, 0x0f, 0x09, 0x09, 0xcc, 0xc4, 0x9e, 0xc4, 0xa3, 0xda, 0x1e, 0xe3, 0x99, 0x84, 0x94, 0xbf, + 0x00, 0x0e, 0x48, 0x7b, 0x44, 0x9c, 0x38, 0xf0, 0xc7, 0xec, 0xb1, 0x47, 0x4e, 0x06, 0xb5, 0x17, + 0xce, 0x39, 0x72, 0x42, 0x9e, 0x19, 0xdb, 0xd3, 0x6c, 0x97, 0x6a, 0xb9, 0xb4, 0xf3, 0xde, 0xfb, + 0xbc, 0xcf, 0x27, 0xf3, 0xe6, 0xcd, 0x1b, 0x03, 0x0b, 0x0f, 0x3d, 0x2b, 0xc4, 0xe3, 0x80, 0x79, + 0x21, 0x46, 0x31, 0xa3, 0x16, 0x43, 0xb1, 0x8f, 0xd2, 0x08, 0xc7, 0xcc, 0x9a, 0xf6, 0x15, 0xcb, + 0x4c, 0x52, 0xc2, 0x88, 0xd6, 0xc1, 0x43, 0xcf, 0x54, 0x13, 0x4c, 0x05, 0x32, 0xed, 0xef, 0x76, + 0x95, 0x7c, 0x76, 0x91, 0x20, 0x6a, 0x4d, 0x61, 0x88, 0x7d, 0xc8, 0x48, 0x2a, 0x18, 0x76, 0xf7, + 0x5e, 0x40, 0xf0, 0xbf, 0x32, 0xfa, 0xc0, 0x23, 0xf1, 0x08, 0x13, 0x2b, 0x49, 0x09, 0x19, 0x15, + 0xce, 0xce, 0x98, 0x90, 0x71, 0x88, 0x2c, 0x6e, 0x0d, 0x27, 0x23, 0xcb, 0x9f, 0xa4, 0x90, 0x61, + 0x12, 0xcb, 0xb8, 0xbe, 0x18, 0x67, 0x38, 0x42, 0x94, 0xc1, 0x28, 0x29, 0x00, 0xf9, 0x36, 0x3d, + 0x92, 0x22, 0x4b, 0xfc, 0xea, 0x7c, 0x6b, 0x62, 0x25, 0x01, 0x6f, 0x55, 0x00, 0x12, 0x45, 0x98, + 0x45, 0x05, 0xa8, 0xb4, 0x24, 0x70, 0x6b, 0x4c, 0xc6, 0x84, 0x2f, 0xad, 0x7c, 0x25, 0xbc, 0xc6, + 0x5f, 0x6b, 0xa0, 0x79, 0xc8, 0xf9, 0xce, 0x18, 0x64, 0x48, 0xdb, 0x01, 0x75, 0x2f, 0x80, 0x38, + 0x76, 0xb1, 0xdf, 0xae, 0x75, 0x6b, 0xbd, 0x86, 0xb3, 0xc6, 0xed, 0x13, 0x5f, 0x43, 0xa0, 0xc9, + 0xd2, 0x09, 0x65, 0x6e, 0x88, 0xa6, 0x28, 0x6c, 0x2f, 0x77, 0x6b, 0xbd, 0xe6, 0xa0, 0x67, 0xfe, + 0x7b, 0x59, 0xcd, 0x8f, 0x52, 0xe8, 0xe5, 0x1b, 0xb6, 0x77, 0x9f, 0x67, 0xfa, 0xd2, 0x3c, 0xd3, + 0xb5, 0x0b, 0x18, 0x85, 0x07, 0x86, 0x42, 0x65, 0x38, 0x80, 0x5b, 0x9f, 0xe4, 0x86, 0x36, 0x02, + 0x1b, 0xdc, 0xc2, 0xf1, 0xd8, 0x4d, 0x50, 0x8a, 0x89, 0xdf, 0x5e, 0xe1, 0x52, 0x3b, 0xa6, 0x28, + 0x96, 0x59, 0x14, 0xcb, 0x3c, 0x92, 0xc5, 0xb4, 0x0d, 0xc9, 0xbd, 0xad, 0x70, 0x57, 0xf9, 0xc6, + 0xcf, 0x7f, 0xe8, 0x35, 0xe7, 0x7e, 0xe1, 0x3d, 0xe5, 0x4e, 0x0d, 0x83, 0xcd, 0x49, 0x3c, 0x24, + 0xb1, 0xaf, 0x08, 0xad, 0xde, 0x25, 0xf4, 0x86, 0x14, 0x7a, 0x28, 0x84, 0x16, 0x09, 0x84, 0xd2, + 0x46, 0xe9, 0x96, 0x52, 0x08, 0x6c, 0x44, 0x70, 0xe6, 0x7a, 0x21, 0xf1, 0xce, 0x5d, 0x3f, 0xc5, + 0x23, 0xd6, 0xfe, 0xdf, 0x2b, 0x6e, 0x69, 0x21, 0x5f, 0x08, 0xad, 0x47, 0x70, 0x76, 0x98, 0x3b, + 0x8f, 0x72, 0x9f, 0xf6, 0x15, 0x58, 0x1f, 0xa5, 0xe4, 0x7b, 0x14, 0xbb, 0x01, 0xca, 0x0f, 0xa4, + 0x7d, 0x8f, 0x8b, 0xec, 0xf2, 0x23, 0xca, 0x5b, 0xc4, 0x94, 0x9d, 0x33, 0xed, 0x9b, 0xc7, 0x1c, + 0x61, 0xef, 0x49, 0x95, 0x2d, 0xa1, 0x72, 0x23, 0xdd, 0x70, 0x5a, 0xc2, 0x16, 0xd8, 0x9c, 0x3e, + 0x84, 0x0c, 0x51, 0x56, 0xd0, 0xaf, 0xbd, 0x2a, 0xfd, 0x8d, 0x74, 0xc3, 0x69, 0x09, 0x5b, 0xd2, + 0x9f, 0x80, 0x26, 0xbf, 0x3a, 0x2e, 0x4d, 0x90, 0x47, 0xdb, 0xf5, 0xee, 0x4a, 0xaf, 0x39, 0xd8, + 0x34, 0xb1, 0x47, 0x07, 0x4f, 0xcc, 0xd3, 0x3c, 0x72, 0x96, 0x20, 0xcf, 0xde, 0xae, 0x5a, 0x48, + 0x81, 0x1b, 0x0e, 0x48, 0x0a, 0x08, 0xd5, 0x0e, 0x40, 0x6b, 0x92, 0x8c, 0x53, 0xe8, 0x23, 0x37, + 0x81, 0x2c, 0x68, 0x37, 0xba, 0x2b, 0xbd, 0x86, 0xfd, 0x70, 0x9e, 0xe9, 0x0f, 0xe4, 0xb9, 0x29, + 0x51, 0xc3, 0x69, 0x4a, 0xf3, 0x14, 0xb2, 0x40, 0x73, 0xc1, 0x0e, 0x0c, 0x43, 0xf2, 0x9d, 0x3b, + 0x49, 0x7c, 0xc8, 0x90, 0x0b, 0x47, 0x0c, 0xa5, 0x2e, 0x9a, 0x25, 0x38, 0xbd, 0x68, 0x83, 0x6e, + 0xad, 0x57, 0xb7, 0xdf, 0x9c, 0x67, 0x7a, 0x57, 0x10, 0xbd, 0x14, 0x6a, 0x38, 0xdb, 0x3c, 0xf6, + 0x19, 0x0f, 0x7d, 0x90, 0x47, 0x3e, 0xe4, 0x01, 0xed, 0x5b, 0xa0, 0xdf, 0x92, 0x15, 0x61, 0x3a, + 0x44, 0x01, 0x9c, 0x62, 0x32, 0x49, 0xdb, 0x4d, 0x2e, 0xf3, 0xf6, 0x3c, 0xd3, 0x1f, 0xbf, 0x54, + 0x46, 0x4d, 0x30, 0x9c, 0xbd, 0x45, 0xb1, 0xa7, 0x4a, 0xf8, 0x60, 0xf5, 0x87, 0x5f, 0xf5, 0x25, + 0xe3, 0xb7, 0x65, 0x70, 0xff, 0x90, 0xc4, 0x14, 0xc5, 0x74, 0x42, 0xc5, 0x6d, 0xb7, 0x41, 0xa3, + 0x1c, 0x38, 0xfc, 0xba, 0xe7, 0xc7, 0xb9, 0xd8, 0x92, 0x9f, 0x16, 0x08, 0xbb, 0x9e, 0x1f, 0xe7, + 0xb3, 0xbc, 0xf3, 0xaa, 0x34, 0xed, 0x7d, 0xb0, 0x9a, 0x12, 0xc2, 0xe4, 0x3c, 0x30, 0x94, 0x6e, + 0xa8, 0x26, 0xd0, 0xb4, 0x6f, 0x3e, 0x45, 0xe9, 0x79, 0x88, 0x1c, 0x42, 0x98, 0xbd, 0x9a, 0xd3, + 0x38, 0x3c, 0x4b, 0xfb, 0xb1, 0x06, 0xb6, 0x62, 0x34, 0x63, 0x6e, 0x39, 0x6c, 0xa9, 0x1b, 0x40, + 0x1a, 0xf0, 0x3b, 0xdf, 0xb2, 0xbf, 0x98, 0x67, 0xfa, 0xeb, 0xa2, 0x06, 0xb7, 0xa1, 0x8c, 0xbf, + 0x33, 0xfd, 0xdd, 0x31, 0x66, 0xc1, 0x64, 0x98, 0xcb, 0xa9, 0x4f, 0x80, 0xb2, 0x0c, 0xf1, 0x90, + 0x5a, 0xc3, 0x0b, 0x86, 0xa8, 0x79, 0x8c, 0x66, 0x76, 0xbe, 0x70, 0xb4, 0x9c, 0xee, 0xf3, 0x92, + 0xed, 0x18, 0xd2, 0x40, 0x96, 0xe9, 0xa7, 0x65, 0xd0, 0x52, 0xab, 0xa7, 0xf5, 0x41, 0x43, 0x34, + 0x76, 0x39, 0x13, 0xed, 0xad, 0x79, 0xa6, 0x6f, 0x8a, 0x9f, 0x55, 0x86, 0x0c, 0xa7, 0x2e, 0xd6, + 0x27, 0xbe, 0x06, 0x41, 0x3d, 0x40, 0xd0, 0x47, 0xa9, 0xdb, 0x97, 0x75, 0x79, 0x7c, 0xd7, 0x9c, + 0x3c, 0xe6, 0x78, 0xbb, 0x73, 0x95, 0xe9, 0x6b, 0x62, 0xdd, 0x9f, 0x67, 0xfa, 0x86, 0x10, 0x29, + 0xc8, 0x0c, 0x67, 0x4d, 0x2c, 0xfb, 0x8a, 0xc4, 0x40, 0xce, 0xc7, 0xff, 0x20, 0x31, 0x78, 0x41, + 0x62, 0x50, 0x4a, 0x0c, 0x64, 0x3d, 0x7e, 0x59, 0x01, 0xf7, 0x04, 0x5a, 0x83, 0x60, 0x9d, 0xe2, + 0x71, 0x8c, 0x7c, 0x57, 0x40, 0x64, 0xcb, 0x74, 0x54, 0x1d, 0xf1, 0x24, 0x9e, 0x71, 0x98, 0x14, + 0xdc, 0xbb, 0xcc, 0xf4, 0x5a, 0x35, 0x05, 0x6e, 0x50, 0x18, 0x4e, 0x8b, 0x2a, 0xd8, 0x7c, 0xc8, + 0x94, 0x67, 0xec, 0x52, 0x54, 0xb4, 0xd5, 0x2d, 0x12, 0xe5, 0xe1, 0x9d, 0x21, 0x66, 0xb7, 0x2b, + 0xfa, 0x1b, 0xe9, 0x86, 0xd3, 0x9a, 0x2a, 0x38, 0xed, 0x1b, 0x20, 0x9e, 0x01, 0xae, 0xcf, 0x87, + 0xd8, 0xca, 0x9d, 0x43, 0xec, 0x91, 0x1c, 0x62, 0xaf, 0x29, 0x8f, 0x4b, 0x99, 0x6f, 0x38, 0xeb, + 0xd2, 0x21, 0xc7, 0x58, 0x08, 0xb4, 0x02, 0x51, 0x35, 0xab, 0x7c, 0x58, 0xee, 0xda, 0xc5, 0xa3, + 0x79, 0xa6, 0xef, 0xdc, 0x54, 0xa9, 0x38, 0x0c, 0xe7, 0xff, 0xd2, 0x59, 0xb5, 0xad, 0xf1, 0x31, + 0xa8, 0x17, 0x0f, 0xac, 0xb6, 0x07, 0x1a, 0xf1, 0x24, 0x42, 0x69, 0x1e, 0xe1, 0x27, 0xb3, 0xea, + 0x54, 0x0e, 0xad, 0x0b, 0x9a, 0x3e, 0x8a, 0x49, 0x84, 0x63, 0x1e, 0x5f, 0xe6, 0x71, 0xd5, 0x65, + 0x7f, 0xfd, 0xfc, 0xaa, 0x53, 0xbb, 0xbc, 0xea, 0xd4, 0xfe, 0xbc, 0xea, 0xd4, 0x9e, 0x5d, 0x77, + 0x96, 0x2e, 0xaf, 0x3b, 0x4b, 0xbf, 0x5f, 0x77, 0x96, 0xbe, 0x3c, 0x52, 0xae, 0x98, 0x47, 0x68, + 0x44, 0xa8, 0xfc, 0xb7, 0x4f, 0xfd, 0x73, 0x6b, 0x56, 0x7d, 0x8a, 0xed, 0x17, 0xdf, 0x62, 0xef, + 0xbc, 0xb7, 0xbf, 0xf8, 0xb1, 0x34, 0xbc, 0xc7, 0x27, 0xca, 0x93, 0x7f, 0x02, 0x00, 0x00, 0xff, + 0xff, 0x8f, 0xde, 0xf9, 0xa9, 0xba, 0x09, 0x00, 0x00, +} + +func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AllowUpdateAfterMisbehaviour { + i-- + if m.AllowUpdateAfterMisbehaviour { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } + if m.AllowUpdateAfterExpiry { + i-- + if m.AllowUpdateAfterExpiry { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 + } + if len(m.UpgradePath) > 0 { + for iNdEx := len(m.UpgradePath) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.UpgradePath[iNdEx]) + copy(dAtA[i:], m.UpgradePath[iNdEx]) + i = encodeVarintTendermint(dAtA, i, uint64(len(m.UpgradePath[iNdEx]))) + i-- + dAtA[i] = 0x4a + } + } + if len(m.ProofSpecs) > 0 { + for iNdEx := len(m.ProofSpecs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ProofSpecs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + { + size, err := m.LatestHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + { + size, err := m.FrozenHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + n3, err3 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MaxClockDrift, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxClockDrift):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintTendermint(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x2a + n4, err4 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.UnbondingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingPeriod):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintTendermint(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x22 + n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.TrustingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.TrustingPeriod):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintTendermint(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0x1a + { + size, err := m.TrustLevel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTendermint(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConsensusState) 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 *ConsensusState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConsensusState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NextValidatorsHash) > 0 { + i -= len(m.NextValidatorsHash) + copy(dAtA[i:], m.NextValidatorsHash) + i = encodeVarintTendermint(dAtA, i, uint64(len(m.NextValidatorsHash))) + i-- + dAtA[i] = 0x1a + } + { + size, err := m.Root.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintTendermint(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Misbehaviour) 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 *Misbehaviour) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Misbehaviour) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Header2 != nil { + { + size, err := m.Header2.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Header1 != nil { + { + size, err := m.Header1.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintTendermint(dAtA, i, uint64(len(m.ClientId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Header) 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 *Header) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TrustedValidators != nil { + { + size, err := m.TrustedValidators.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + { + size, err := m.TrustedHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.ValidatorSet != nil { + { + size, err := m.ValidatorSet.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.SignedHeader != nil { + { + size, err := m.SignedHeader.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTendermint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Fraction) 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 *Fraction) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Fraction) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Denominator != 0 { + i = encodeVarintTendermint(dAtA, i, uint64(m.Denominator)) + i-- + dAtA[i] = 0x10 + } + if m.Numerator != 0 { + i = encodeVarintTendermint(dAtA, i, uint64(m.Numerator)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintTendermint(dAtA []byte, offset int, v uint64) int { + offset -= sovTendermint(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTendermint(uint64(l)) + } + l = m.TrustLevel.Size() + n += 1 + l + sovTendermint(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.TrustingPeriod) + n += 1 + l + sovTendermint(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingPeriod) + n += 1 + l + sovTendermint(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.MaxClockDrift) + n += 1 + l + sovTendermint(uint64(l)) + l = m.FrozenHeight.Size() + n += 1 + l + sovTendermint(uint64(l)) + l = m.LatestHeight.Size() + n += 1 + l + sovTendermint(uint64(l)) + if len(m.ProofSpecs) > 0 { + for _, e := range m.ProofSpecs { + l = e.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + } + if len(m.UpgradePath) > 0 { + for _, s := range m.UpgradePath { + l = len(s) + n += 1 + l + sovTendermint(uint64(l)) + } + } + if m.AllowUpdateAfterExpiry { + n += 2 + } + if m.AllowUpdateAfterMisbehaviour { + n += 2 + } + return n +} + +func (m *ConsensusState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovTendermint(uint64(l)) + l = m.Root.Size() + n += 1 + l + sovTendermint(uint64(l)) + l = len(m.NextValidatorsHash) + if l > 0 { + n += 1 + l + sovTendermint(uint64(l)) + } + return n +} + +func (m *Misbehaviour) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClientId) + if l > 0 { + n += 1 + l + sovTendermint(uint64(l)) + } + if m.Header1 != nil { + l = m.Header1.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + if m.Header2 != nil { + l = m.Header2.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + return n +} + +func (m *Header) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignedHeader != nil { + l = m.SignedHeader.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + if m.ValidatorSet != nil { + l = m.ValidatorSet.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + l = m.TrustedHeight.Size() + n += 1 + l + sovTendermint(uint64(l)) + if m.TrustedValidators != nil { + l = m.TrustedValidators.Size() + n += 1 + l + sovTendermint(uint64(l)) + } + return n +} + +func (m *Fraction) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Numerator != 0 { + n += 1 + sovTendermint(uint64(m.Numerator)) + } + if m.Denominator != 0 { + n += 1 + sovTendermint(uint64(m.Denominator)) + } + return n +} + +func sovTendermint(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTendermint(x uint64) (n int) { + return sovTendermint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) 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 ErrIntOverflowTendermint + } + 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: ClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + 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 ErrInvalidLengthTendermint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustLevel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TrustLevel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustingPeriod", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.TrustingPeriod, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingPeriod", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.UnbondingPeriod, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxClockDrift", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.MaxClockDrift, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FrozenHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FrozenHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LatestHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.LatestHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProofSpecs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProofSpecs = append(m.ProofSpecs, &_go.ProofSpec{}) + if err := m.ProofSpecs[len(m.ProofSpecs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + 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 ErrInvalidLengthTendermint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpgradePath = append(m.UpgradePath, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterExpiry", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUpdateAfterExpiry = bool(v != 0) + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowUpdateAfterMisbehaviour", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.AllowUpdateAfterMisbehaviour = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTendermint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTendermint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConsensusState) 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 ErrIntOverflowTendermint + } + 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: ConsensusState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConsensusState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Root.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextValidatorsHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextValidatorsHash = append(m.NextValidatorsHash[:0], dAtA[iNdEx:postIndex]...) + if m.NextValidatorsHash == nil { + m.NextValidatorsHash = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTendermint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTendermint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Misbehaviour) 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 ErrIntOverflowTendermint + } + 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: Misbehaviour: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Misbehaviour: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + 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 ErrInvalidLengthTendermint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClientId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header1", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header1 == nil { + m.Header1 = &Header{} + } + if err := m.Header1.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header2", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Header2 == nil { + m.Header2 = &Header{} + } + if err := m.Header2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTendermint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTendermint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Header) 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 ErrIntOverflowTendermint + } + 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: Header: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignedHeader", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SignedHeader == nil { + m.SignedHeader = &types2.SignedHeader{} + } + if err := m.SignedHeader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSet", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ValidatorSet == nil { + m.ValidatorSet = &types2.ValidatorSet{} + } + if err := m.ValidatorSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustedHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TrustedHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TrustedValidators", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTendermint + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTendermint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TrustedValidators == nil { + m.TrustedValidators = &types2.ValidatorSet{} + } + if err := m.TrustedValidators.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTendermint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTendermint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Fraction) 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 ErrIntOverflowTendermint + } + 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: Fraction: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Fraction: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Numerator", wireType) + } + m.Numerator = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Numerator |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Denominator", wireType) + } + m.Denominator = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTendermint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Denominator |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTendermint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTendermint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTendermint(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, ErrIntOverflowTendermint + } + 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, ErrIntOverflowTendermint + } + 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, ErrIntOverflowTendermint + } + 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, ErrInvalidLengthTendermint + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTendermint + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTendermint + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTendermint = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTendermint = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTendermint = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/light-clients/07-tendermint/types/tendermint_test.go b/x/ibc/light-clients/07-tendermint/types/tendermint_test.go new file mode 100644 index 000000000000..4f9b8142bfb9 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/tendermint_test.go @@ -0,0 +1,95 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +const ( + chainID = "gaia" + chainIDRevision0 = "gaia-revision-0" + chainIDRevision1 = "gaia-revision-1" + clientID = "gaiamainnet" + trustingPeriod time.Duration = time.Hour * 24 * 7 * 2 + ubdPeriod time.Duration = time.Hour * 24 * 7 * 3 + maxClockDrift time.Duration = time.Second * 10 +) + +var ( + height = clienttypes.NewHeight(0, 4) + newClientHeight = clienttypes.NewHeight(1, 1) + upgradePath = []string{"upgrade", "upgradedIBCState"} +) + +type TendermintTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + + // TODO: deprecate usage in favor of testing package + ctx sdk.Context + cdc codec.Marshaler + privVal tmtypes.PrivValidator + valSet *tmtypes.ValidatorSet + valsHash tmbytes.HexBytes + header *ibctmtypes.Header + now time.Time + headerTime time.Time + clientTime time.Time +} + +func (suite *TendermintTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + // commit some blocks so that QueryProof returns valid proof (cannot return valid query if height <= 1) + suite.coordinator.CommitNBlocks(suite.chainA, 2) + suite.coordinator.CommitNBlocks(suite.chainB, 2) + + // TODO: deprecate usage in favor of testing package + checkTx := false + app := simapp.Setup(checkTx) + + suite.cdc = app.AppCodec() + + // now is the time of the current chain, must be after the updating header + // mocks ctx.BlockTime() + suite.now = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) + suite.clientTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + // Header time is intended to be time for any new header used for updates + suite.headerTime = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) + + suite.privVal = ibctestingmock.NewPV() + + pubKey, err := suite.privVal.GetPubKey() + suite.Require().NoError(err) + + heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1) + + val := tmtypes.NewValidator(pubKey, 10) + suite.valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{val}) + suite.valsHash = suite.valSet.Hash() + suite.header = suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, []tmtypes.PrivValidator{suite.privVal}) + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, Time: suite.now}) +} + +func TestTendermintTestSuite(t *testing.T) { + suite.Run(t, new(TendermintTestSuite)) +} diff --git a/x/ibc/light-clients/07-tendermint/types/update.go b/x/ibc/light-clients/07-tendermint/types/update.go new file mode 100644 index 000000000000..e692e7466860 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/update.go @@ -0,0 +1,186 @@ +package types + +import ( + "bytes" + "time" + + "github.com/tendermint/tendermint/light" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + 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/core/exported" +) + +// CheckHeaderAndUpdateState checks if the provided header is valid, and if valid it will: +// create the consensus state for the header.Height +// and update the client state if the header height is greater than the latest client state height +// It returns an error if: +// - the client or header provided are not parseable to tendermint types +// - the header is invalid +// - header height is less than or equal to the trusted header height +// - header revision is not equal to trusted header revision +// - header valset commit verification fails +// - header timestamp is past the trusting period in relation to the consensus state +// - header timestamp is less than or equal to the consensus state timestamp +// +// UpdateClient may be used to either create a consensus state for: +// - a future height greater than the latest client state height +// - a past height that was skipped during bisection +// If we are updating to a past height, a consensus state is created for that height to be persisted in client store +// If we are updating to a future height, the consensus state is created and the client state is updated to reflect +// the new latest height +// UpdateClient must only be used to update within a single revision, thus header revision number and trusted height's revision +// number must be the same. To update to a new revision, use a separate upgrade path +// Tendermint client validity checking uses the bisection algorithm described +// in the [Tendermint spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md). +func (cs ClientState) CheckHeaderAndUpdateState( + ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore, + header exported.Header, +) (exported.ClientState, exported.ConsensusState, error) { + tmHeader, ok := header.(*Header) + if !ok { + return nil, nil, sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, "expected type %T, got %T", &Header{}, header, + ) + } + + // get consensus state from clientStore + tmConsState, err := GetConsensusState(clientStore, cdc, tmHeader.TrustedHeight) + if err != nil { + return nil, nil, sdkerrors.Wrapf( + err, "could not get consensus state from clientstore at TrustedHeight: %s", tmHeader.TrustedHeight, + ) + } + + if err := checkValidity(&cs, tmConsState, tmHeader, ctx.BlockTime()); err != nil { + return nil, nil, err + } + + newClientState, consensusState := update(ctx, clientStore, &cs, tmHeader) + return newClientState, consensusState, nil +} + +// checkTrustedHeader checks that consensus state matches trusted fields of Header +func checkTrustedHeader(header *Header, consState *ConsensusState) error { + tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") + } + + // assert that trustedVals is NextValidators of last trusted header + // to do this, we check that trustedVals.Hash() == consState.NextValidatorsHash + tvalHash := tmTrustedValidators.Hash() + if !bytes.Equal(consState.NextValidatorsHash, tvalHash) { + return sdkerrors.Wrapf( + ErrInvalidValidatorSet, + "trusted validators %s, does not hash to latest trusted validators. Expected: %X, got: %X", + header.TrustedValidators, consState.NextValidatorsHash, tvalHash, + ) + } + return nil +} + +// checkValidity checks if the Tendermint header is valid. +// CONTRACT: consState.Height == header.TrustedHeight +func checkValidity( + clientState *ClientState, consState *ConsensusState, + header *Header, currentTimestamp time.Time, +) error { + if err := checkTrustedHeader(header, consState); err != nil { + return err + } + + // UpdateClient only accepts updates with a header at the same revision + // as the trusted consensus state + if header.GetHeight().GetRevisionNumber() != header.TrustedHeight.RevisionNumber { + return sdkerrors.Wrapf( + ErrInvalidHeaderHeight, + "header height revision %d does not match trusted header revision %d", + header.GetHeight().GetRevisionNumber(), header.TrustedHeight.RevisionNumber, + ) + } + + tmTrustedValidators, err := tmtypes.ValidatorSetFromProto(header.TrustedValidators) + if err != nil { + return sdkerrors.Wrap(err, "trusted validator set in not tendermint validator set type") + } + + tmSignedHeader, err := tmtypes.SignedHeaderFromProto(header.SignedHeader) + if err != nil { + return sdkerrors.Wrap(err, "signed header in not tendermint signed header type") + } + + tmValidatorSet, err := tmtypes.ValidatorSetFromProto(header.ValidatorSet) + if err != nil { + return sdkerrors.Wrap(err, "validator set in not tendermint validator set type") + } + + // assert header height is newer than consensus state + if header.GetHeight().LTE(header.TrustedHeight) { + return sdkerrors.Wrapf( + clienttypes.ErrInvalidHeader, + "header height ≤ consensus state height (%s ≤ %s)", header.GetHeight(), header.TrustedHeight, + ) + } + + chainID := clientState.GetChainID() + // If chainID is in revision format, then set revision number of chainID with the revision number + // of the header we are verifying + // This is useful if the update is at a previous revision rather than an update to the latest revision + // of the client. + // The chainID must be set correctly for the previous revision before attempting verification. + // Updates for previous revisions are not supported if the chainID is not in revision format. + if clienttypes.IsRevisionFormat(chainID) { + chainID, _ = clienttypes.SetRevisionNumber(chainID, header.GetHeight().GetRevisionNumber()) + } + + // Construct a trusted header using the fields in consensus state + // Only Height, Time, and NextValidatorsHash are necessary for verification + trustedHeader := tmtypes.Header{ + ChainID: chainID, + Height: int64(header.TrustedHeight.RevisionHeight), + Time: consState.Timestamp, + NextValidatorsHash: consState.NextValidatorsHash, + } + signedHeader := tmtypes.SignedHeader{ + Header: &trustedHeader, + } + + // Verify next header with the passed-in trustedVals + // - asserts trusting period not passed + // - assert header timestamp is not past the trusting period + // - assert header timestamp is past latest stored consensus state timestamp + // - assert that a TrustLevel proportion of TrustedValidators signed new Commit + err = light.Verify( + &signedHeader, + tmTrustedValidators, tmSignedHeader, tmValidatorSet, + clientState.TrustingPeriod, currentTimestamp, clientState.MaxClockDrift, clientState.TrustLevel.ToTendermint(), + ) + if err != nil { + return sdkerrors.Wrap(err, "failed to verify header") + } + return nil +} + +// update the consensus state from a new header and set processed time metadata +func update(ctx sdk.Context, clientStore sdk.KVStore, clientState *ClientState, header *Header) (*ClientState, *ConsensusState) { + height := header.GetHeight().(clienttypes.Height) + if height.GT(clientState.LatestHeight) { + clientState.LatestHeight = height + } + consensusState := &ConsensusState{ + Timestamp: header.GetTime(), + Root: commitmenttypes.NewMerkleRoot(header.Header.GetAppHash()), + NextValidatorsHash: header.Header.NextValidatorsHash, + } + + // set context time as processed time as this is state internal to tendermint client logic. + // client state and consensus state will be set by client keeper + SetProcessedTime(clientStore, header.GetHeight(), uint64(ctx.BlockTime().UnixNano())) + + return clientState, consensusState +} diff --git a/x/ibc/light-clients/07-tendermint/types/update_test.go b/x/ibc/light-clients/07-tendermint/types/update_test.go new file mode 100644 index 000000000000..d9e550ed015a --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/update_test.go @@ -0,0 +1,281 @@ +package types_test + +import ( + "time" + + tmtypes "github.com/tendermint/tendermint/types" + + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + types "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + ibctestingmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +func (suite *TendermintTestSuite) TestCheckHeaderAndUpdateState() { + var ( + clientState *types.ClientState + consensusState *types.ConsensusState + consStateHeight clienttypes.Height + newHeader *types.Header + currentTime time.Time + ) + + // Setup different validators and signers for testing different types of updates + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + + revisionHeight := int64(height.RevisionHeight) + + // create modified heights to use for test-cases + heightPlus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+1) + heightMinus1 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-1) + heightMinus3 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight-3) + heightPlus5 := clienttypes.NewHeight(height.RevisionNumber, height.RevisionHeight+5) + + altVal := tmtypes.NewValidator(altPubKey, revisionHeight) + + // Create bothValSet with both suite validator and altVal. Would be valid update + bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + // Create alternative validator set with only altVal, invalid update (too much change in valSet) + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + + signers := []tmtypes.PrivValidator{suite.privVal} + + // Create signer array and ensure it is in same order as bothValSet + _, suiteVal := suite.valSet.GetByIndex(0) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + + altSigners := []tmtypes.PrivValidator{altPrivVal} + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + name: "successful update with next height and same validator set", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "successful update with future height and different validator set", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "successful update with next height and different validator set", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "successful update for a previous height", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + consStateHeight = heightMinus3 + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "successful update for a previous revision", + setup: func() { + clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision0, int64(height.RevisionHeight), heightMinus3, suite.headerTime, bothValSet, suite.valSet, bothSigners) + currentTime = suite.now + }, + expPass: true, + }, + { + name: "unsuccessful update with incorrect header chain-id", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader("ethermint", int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update to a future revision", + setup: func() { + clientState = types.NewClientState(chainIDRevision0, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 1, height, suite.headerTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update: header height revision and trusted height revision mismatch", + setup: func() { + clientState = types.NewClientState(chainIDRevision1, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainIDRevision1, 3, height, suite.headerTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update with next height: update header mismatches nextValSetHash", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, bothValSet, suite.valSet, bothSigners) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update with next height: update header mismatches different nextValSetHash", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), bothValSet.Hash()) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, bothValSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update with future height: too much change in validator set", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, altValSet, suite.valSet, altSigners) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful updates, passed in incorrect trusted validators for given consensus state", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus5.RevisionHeight), height, suite.headerTime, bothValSet, bothValSet, bothSigners) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update: trusting period has passed since last client timestamp", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + // make current time pass trusting period from last timestamp on clientstate + currentTime = suite.now.Add(trustingPeriod) + }, + expPass: false, + }, + { + name: "unsuccessful update: header timestamp is past current timestamp", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.now.Add(time.Minute), suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "unsuccessful update: header timestamp is not past last client timestamp", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.clientTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + { + name: "header basic validation failed", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightPlus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + // cause new header to fail validatebasic by changing commit height to mismatch header height + newHeader.SignedHeader.Commit.Height = revisionHeight - 1 + currentTime = suite.now + }, + expPass: false, + }, + { + name: "header height < consensus height", + setup: func() { + clientState = types.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(height.RevisionNumber, heightPlus5.RevisionHeight), commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + consensusState = types.NewConsensusState(suite.clientTime, commitmenttypes.NewMerkleRoot(suite.header.Header.GetAppHash()), suite.valsHash) + // Make new header at height less than latest client state + newHeader = suite.chainA.CreateTMClientHeader(chainID, int64(heightMinus1.RevisionHeight), height, suite.headerTime, suite.valSet, suite.valSet, signers) + currentTime = suite.now + }, + expPass: false, + }, + } + + for i, tc := range testCases { + tc := tc + + consStateHeight = height // must be explicitly changed + // setup test + tc.setup() + + // Set current timestamp in context + ctx := suite.chainA.GetContext().WithBlockTime(currentTime) + + // Set trusted consensus state in client store + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientConsensusState(ctx, clientID, consStateHeight, consensusState) + + height := newHeader.GetHeight() + expectedConsensus := &types.ConsensusState{ + Timestamp: newHeader.GetTime(), + Root: commitmenttypes.NewMerkleRoot(newHeader.Header.GetAppHash()), + NextValidatorsHash: newHeader.Header.NextValidatorsHash, + } + + newClientState, consensusState, err := clientState.CheckHeaderAndUpdateState( + ctx, + suite.cdc, + suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientID), // pass in clientID prefixed clientStore + newHeader, + ) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + + // Determine if clientState should be updated or not + // TODO: check the entire Height struct once GetLatestHeight returns clienttypes.Height + if height.GT(clientState.LatestHeight) { + // Header Height is greater than clientState latest Height, clientState should be updated with header.GetHeight() + suite.Require().Equal(height, newClientState.GetLatestHeight(), "clientstate height did not update") + } else { + // Update will add past consensus state, clientState should not be updated at all + suite.Require().Equal(clientState.LatestHeight, newClientState.GetLatestHeight(), "client state height updated for past header") + } + + suite.Require().Equal(expectedConsensus, consensusState, "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(newClientState, "invalid test case %d passed: %s", i, tc.name) + suite.Require().Nil(consensusState, "invalid test case %d passed: %s", i, tc.name) + } + } +} diff --git a/x/ibc/light-clients/07-tendermint/types/upgrade.go b/x/ibc/light-clients/07-tendermint/types/upgrade.go new file mode 100644 index 000000000000..397e9cfd8374 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/upgrade.go @@ -0,0 +1,156 @@ +package types + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + 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/core/exported" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +// VerifyUpgradeAndUpdateState checks if the upgraded client has been committed by the current client +// It will zero out all client-specific fields (e.g. TrustingPeriod and verify all data +// in client state that must be the same across all valid Tendermint clients for the new chain. +// VerifyUpgrade will return an error if: +// - the upgradedClient is not a Tendermint ClientState +// - the lastest height of the client state does not have the same revision number or has a greater +// height than the committed client. +// - the height of upgraded client is not greater than that of current client +// - the latest height of the new client does not match or is greater than the height in committed client +// - any Tendermint chain specified parameter in upgraded client such as ChainID, UnbondingPeriod, +// and ProofSpecs do not match parameters set by committed client +func (cs ClientState) VerifyUpgradeAndUpdateState( + ctx sdk.Context, cdc codec.BinaryMarshaler, clientStore sdk.KVStore, + upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState, + proofUpgradeClient, proofUpgradeConsState []byte, +) (exported.ClientState, exported.ConsensusState, error) { + if len(cs.UpgradePath) == 0 { + return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade client, no upgrade path set") + } + + // last height of current counterparty chain must be client's latest height + lastHeight := cs.GetLatestHeight() + + if !upgradedClient.GetLatestHeight().GT(lastHeight) { + return nil, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "upgraded client height %s must be at greater than current client height %s", + upgradedClient.GetLatestHeight(), lastHeight) + } + + // counterparty chain must commit the upgraded client with all client-customizable fields zeroed out + // at the upgrade path specified by current client + // counterparty must also commit to the upgraded consensus state at a sub-path under the upgrade path specified + tmUpgradeClient, ok := upgradedClient.(*ClientState) + if !ok { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClientType, "upgraded client must be Tendermint client. expected: %T got: %T", + &ClientState{}, upgradedClient) + } + tmUpgradeConsState, ok := upgradedConsState.(*ConsensusState) + if !ok { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "upgraded consensus state must be Tendermint consensus state. expected %T, got: %T", + &ConsensusState{}, upgradedConsState) + } + + // unmarshal proofs + var merkleProofClient, merkleProofConsState commitmenttypes.MerkleProof + if err := cdc.UnmarshalBinaryBare(proofUpgradeClient, &merkleProofClient); err != nil { + return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal client merkle proof: %v", err) + } + if err := cdc.UnmarshalBinaryBare(proofUpgradeConsState, &merkleProofConsState); err != nil { + return nil, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidProof, "could not unmarshal consensus state merkle proof: %v", err) + } + + // Must prove against latest consensus state to ensure we are verifying against latest upgrade plan + // This verifies that upgrade is intended for the provided revision, since committed client must exist + // at this consensus state + consState, err := GetConsensusState(clientStore, cdc, lastHeight) + if err != nil { + return nil, nil, sdkerrors.Wrap(err, "could not retrieve consensus state for lastHeight") + } + + if cs.IsExpired(consState.Timestamp, ctx.BlockTime()) { + return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidClient, "cannot upgrade an expired client") + } + + // Verify client proof + bz, err := cdc.MarshalInterface(upgradedClient) + if err != nil { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidClient, "could not marshal client state: %v", err) + } + // construct clientState Merkle path + upgradeClientPath := constructUpgradeClientMerklePath(cs.UpgradePath, lastHeight) + if err := merkleProofClient.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeClientPath, bz); err != nil { + return nil, nil, sdkerrors.Wrapf(err, "client state proof failed. Path: %s", upgradeClientPath.Pretty()) + } + + // Verify consensus state proof + bz, err = cdc.MarshalInterface(upgradedConsState) + if err != nil { + return nil, nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "could not marshal consensus state: %v", err) + } + // construct consensus state Merkle path + upgradeConsStatePath := constructUpgradeConsStateMerklePath(cs.UpgradePath, lastHeight) + if err := merkleProofConsState.VerifyMembership(cs.ProofSpecs, consState.GetRoot(), upgradeConsStatePath, bz); err != nil { + return nil, nil, sdkerrors.Wrapf(err, "consensus state proof failed. Path: %s", upgradeConsStatePath.Pretty()) + } + + // Construct new client state and consensus state + // Relayer chosen client parameters are ignored. + // All chain-chosen parameters come from committed client, all client-chosen parameters + // come from current client. + newClientState := NewClientState( + tmUpgradeClient.ChainId, cs.TrustLevel, cs.TrustingPeriod, tmUpgradeClient.UnbondingPeriod, + cs.MaxClockDrift, tmUpgradeClient.LatestHeight, tmUpgradeClient.ProofSpecs, tmUpgradeClient.UpgradePath, + cs.AllowUpdateAfterExpiry, cs.AllowUpdateAfterMisbehaviour, + ) + + if err := newClientState.Validate(); err != nil { + return nil, nil, sdkerrors.Wrap(err, "updated client state failed basic validation") + } + + // The new consensus state is merely used as a trusted kernel against which headers on the new + // chain can be verified. The root is empty as it cannot be known in advance, thus no proof verification will pass. + // The timestamp and the NextValidatorsHash of the consensus state is the blocktime and NextValidatorsHash + // of the last block committed by the old chain. This will allow the first block of the new chain to be verified against + // the last validators of the old chain so long as it is submitted within the TrustingPeriod of this client. + // NOTE: We do not set processed time for this consensus state since this consensus state should not be used for packet verification + // as the root is empty. The next consensus state submitted using update will be usable for packet-verification. + newConsState := NewConsensusState( + tmUpgradeConsState.Timestamp, commitmenttypes.MerkleRoot{}, tmUpgradeConsState.NextValidatorsHash, + ) + + return newClientState, newConsState, nil +} + +// construct MerklePath for the committed client from upgradePath +func constructUpgradeClientMerklePath(upgradePath []string, lastHeight exported.Height) commitmenttypes.MerklePath { + // copy all elements from upgradePath except final element + clientPath := make([]string, len(upgradePath)-1) + copy(clientPath, upgradePath) + + // append lastHeight and `upgradedClient` to last key of upgradePath and use as lastKey of clientPath + // this will create the IAVL key that is used to store client in upgrade store + lastKey := upgradePath[len(upgradePath)-1] + appendedKey := fmt.Sprintf("%s/%d/%s", lastKey, lastHeight.GetRevisionHeight(), upgradetypes.KeyUpgradedClient) + + clientPath = append(clientPath, appendedKey) + return commitmenttypes.NewMerklePath(clientPath...) +} + +// construct MerklePath for the committed consensus state from upgradePath +func constructUpgradeConsStateMerklePath(upgradePath []string, lastHeight exported.Height) commitmenttypes.MerklePath { + // copy all elements from upgradePath except final element + consPath := make([]string, len(upgradePath)-1) + copy(consPath, upgradePath) + + // append lastHeight and `upgradedClient` to last key of upgradePath and use as lastKey of clientPath + // this will create the IAVL key that is used to store client in upgrade store + lastKey := upgradePath[len(upgradePath)-1] + appendedKey := fmt.Sprintf("%s/%d/%s", lastKey, lastHeight.GetRevisionHeight(), upgradetypes.KeyUpgradedConsState) + + consPath = append(consPath, appendedKey) + return commitmenttypes.NewMerklePath(consPath...) +} diff --git a/x/ibc/light-clients/07-tendermint/types/upgrade_test.go b/x/ibc/light-clients/07-tendermint/types/upgrade_test.go new file mode 100644 index 000000000000..7be3a4943f33 --- /dev/null +++ b/x/ibc/light-clients/07-tendermint/types/upgrade_test.go @@ -0,0 +1,512 @@ +package types_test + +import ( + 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/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func (suite *TendermintTestSuite) TestVerifyUpgrade() { + var ( + upgradedClient exported.ClientState + upgradedConsState exported.ConsensusState + lastHeight clienttypes.Height + clientA string + proofUpgradedClient, proofUpgradedConsState []byte + ) + + testCases := []struct { + name string + setup func() + expPass bool + }{ + { + name: "successful upgrade", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: true, + }, + { + name: "successful upgrade to same revision", + setup: func() { + upgradedHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+2)) + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, upgradedHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: true, + }, + + { + name: "unsuccessful upgrade: upgrade height revision height is more than the current client revision height", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is 10 blocks from now + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+10)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: chain-specified parameters do not match committed client", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // change upgradedClient client-specified parameters + upgradedClient = types.NewClientState("wrongchainID", types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, true) + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: client-specified parameters do not match previous client", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // change upgradedClient client-specified parameters + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, true, false) + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: relayer-submitted consensus state does not match counterparty-committed consensus state", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // change submitted upgradedConsensusState + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("maliciousValidators"), + } + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: client proof unmarshal failed", + setup: func() { + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + proofUpgradedClient = []byte("proof") + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: consensus state proof unmarshal failed", + setup: func() { + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + proofUpgradedConsState = []byte("proof") + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: client proof verification failed", + setup: func() { + // create but do not store upgraded client + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: consensus state proof verification failed", + setup: func() { + // create but do not store upgraded client + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: upgrade path is empty", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + + // SetClientState with empty upgrade path + tmClient, _ := cs.(*types.ClientState) + tmClient.UpgradePath = []string{""} + suite.chainA.App.IBCKeeper.ClientKeeper.SetClientState(suite.chainA.GetContext(), clientA, tmClient) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: upgraded height is not greater than current height", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: consensus state for upgrade height cannot be found", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+100)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: client is expired", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + // expire chainB's client + suite.chainA.ExpireClient(ubdPeriod) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: updated unbonding period is equal to trusting period", + setup: func() { + + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + { + name: "unsuccessful upgrade: final client is not valid", + setup: func() { + + // new client has smaller unbonding period such that old trusting period is no longer valid + upgradedClient = types.NewClientState("newChainId", types.DefaultTrustLevel, trustingPeriod, trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) + upgradedConsState = &types.ConsensusState{ + NextValidatorsHash: []byte("nextValsHash"), + } + + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.App.UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClient) + suite.chainB.App.UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsState) + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, exported.Tendermint) + suite.Require().NoError(err) + + cs, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientA) + suite.Require().True(found) + + proofUpgradedClient, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + // reset suite + suite.SetupTest() + + clientA, _ = suite.coordinator.SetupClients(suite.chainA, suite.chainB, exported.Tendermint) + + tc.setup() + + cs := suite.chainA.GetClientState(clientA) + clientStore := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA) + + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + upgradedClient = upgradedClient.ZeroCustomFields() + + clientState, consensusState, err := cs.VerifyUpgradeAndUpdateState( + suite.chainA.GetContext(), + suite.cdc, + clientStore, + upgradedClient, + upgradedConsState, + proofUpgradedClient, + proofUpgradedConsState, + ) + + if tc.expPass { + suite.Require().NoError(err, "verify upgrade failed on valid case: %s", tc.name) + suite.Require().NotNil(clientState, "verify upgrade failed on valid case: %s", tc.name) + suite.Require().NotNil(consensusState, "verify upgrade failed on valid case: %s", tc.name) + } else { + suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) + suite.Require().Nil(clientState, "verify upgrade passed on invalid case: %s", tc.name) + + suite.Require().Nil(consensusState, "verify upgrade passed on invalid case: %s", tc.name) + + } + } +} diff --git a/x/ibc/light-clients/09-localhost/doc.go b/x/ibc/light-clients/09-localhost/doc.go new file mode 100644 index 000000000000..40a0f0608670 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/doc.go @@ -0,0 +1,5 @@ +/* +Package localhost implements a concrete `ConsensusState`, `Header`, +`Misbehaviour` and `Equivocation` types for the loop-back client. +*/ +package localhost diff --git a/x/ibc/light-clients/09-localhost/module.go b/x/ibc/light-clients/09-localhost/module.go new file mode 100644 index 000000000000..57b9c5bb2639 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/module.go @@ -0,0 +1,10 @@ +package localhost + +import ( + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" +) + +// Name returns the IBC client name +func Name() string { + return types.SubModuleName +} diff --git a/x/ibc/light-clients/09-localhost/types/client_state.go b/x/ibc/light-clients/09-localhost/types/client_state.go new file mode 100644 index 000000000000..e0ba7a2f0b20 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/client_state.go @@ -0,0 +1,345 @@ +package types + +import ( + "bytes" + "encoding/binary" + "reflect" + "strings" + + ics23 "github.com/confio/ics23/go" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ exported.ClientState = (*ClientState)(nil) + +// NewClientState creates a new ClientState instance +func NewClientState(chainID string, height clienttypes.Height) *ClientState { + return &ClientState{ + ChainId: chainID, + Height: height, + } +} + +// GetChainID returns an empty string +func (cs ClientState) GetChainID() string { + return cs.ChainId +} + +// ClientType is localhost. +func (cs ClientState) ClientType() string { + return exported.Localhost +} + +// GetLatestHeight returns the latest height stored. +func (cs ClientState) GetLatestHeight() exported.Height { + return cs.Height +} + +// IsFrozen returns false. +func (cs ClientState) IsFrozen() bool { + return false +} + +// GetFrozenHeight returns an uninitialized IBC Height. +func (cs ClientState) GetFrozenHeight() exported.Height { + return clienttypes.ZeroHeight() +} + +// Validate performs a basic validation of the client state fields. +func (cs ClientState) Validate() error { + if strings.TrimSpace(cs.ChainId) == "" { + return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "chain id cannot be blank") + } + if cs.Height.RevisionHeight == 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidHeight, "local revision height cannot be zero") + } + return nil +} + +// GetProofSpecs returns nil since localhost does not have to verify proofs +func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { + return nil +} + +// ZeroCustomFields returns the same client state since there are no custom fields in localhost +func (cs ClientState) ZeroCustomFields() exported.ClientState { + return &cs +} + +// Initialize ensures that initial consensus state for localhost is nil +func (cs ClientState) Initialize(_ sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, consState exported.ConsensusState) error { + if consState != nil { + return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "initial consensus state for localhost must be nil.") + } + return nil +} + +// ExportMetadata is a no-op for localhost client +func (cs ClientState) ExportMetadata(_ sdk.KVStore) []exported.GenesisMetadata { + return nil +} + +// CheckHeaderAndUpdateState updates the localhost client. It only needs access to the context +func (cs *ClientState) CheckHeaderAndUpdateState( + ctx sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, _ exported.Header, +) (exported.ClientState, exported.ConsensusState, error) { + // use the chain ID from context since the localhost client is from the running chain (i.e self). + cs.ChainId = ctx.ChainID() + revision := clienttypes.ParseChainID(cs.ChainId) + cs.Height = clienttypes.NewHeight(revision, uint64(ctx.BlockHeight())) + return cs, nil, nil +} + +// CheckMisbehaviourAndUpdateState implements ClientState +// Since localhost is the client of the running chain, misbehaviour cannot be submitted to it +// Thus, CheckMisbehaviourAndUpdateState returns an error for localhost +func (cs ClientState) CheckMisbehaviourAndUpdateState( + _ sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, _ exported.Misbehaviour, +) (exported.ClientState, error) { + return nil, sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "cannot submit misbehaviour to localhost client") +} + +// CheckProposedHeaderAndUpdateState 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") +} + +// VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded +func (cs ClientState) VerifyUpgradeAndUpdateState( + _ sdk.Context, _ codec.BinaryMarshaler, _ sdk.KVStore, + _ exported.ClientState, _ exported.ConsensusState, _, _ []byte, +) (exported.ClientState, exported.ConsensusState, error) { + return nil, nil, sdkerrors.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client") +} + +// VerifyClientState verifies that the localhost client state is stored locally +func (cs ClientState) VerifyClientState( + store sdk.KVStore, cdc codec.BinaryMarshaler, + _ exported.Height, _ exported.Prefix, _ string, _ []byte, clientState exported.ClientState, +) error { + path := host.KeyClientState + bz := store.Get([]byte(path)) + if bz == nil { + return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, + "not found for path: %s", path) + } + + selfClient := clienttypes.MustUnmarshalClientState(cdc, bz) + + if !reflect.DeepEqual(selfClient, clientState) { + return sdkerrors.Wrapf(clienttypes.ErrFailedClientStateVerification, + "stored clientState != provided clientState: \n%v\n≠\n%v", + selfClient, clientState, + ) + } + return nil +} + +// VerifyClientConsensusState returns nil since a local host client does not store consensus +// states. +func (cs ClientState) VerifyClientConsensusState( + sdk.KVStore, codec.BinaryMarshaler, + exported.Height, string, exported.Height, exported.Prefix, + []byte, exported.ConsensusState, +) error { + return nil +} + +// VerifyConnectionState verifies a proof of the connection state of the +// specified connection end stored locally. +func (cs ClientState) VerifyConnectionState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + _ exported.Height, + _ exported.Prefix, + _ []byte, + connectionID string, + connectionEnd exported.ConnectionI, +) error { + path := host.ConnectionKey(connectionID) + bz := store.Get(path) + if bz == nil { + return sdkerrors.Wrapf(clienttypes.ErrFailedConnectionStateVerification, "not found for path %s", path) + } + + var prevConnection connectiontypes.ConnectionEnd + err := cdc.UnmarshalBinaryBare(bz, &prevConnection) + if err != nil { + return err + } + + if !reflect.DeepEqual(&prevConnection, connectionEnd) { + return sdkerrors.Wrapf( + clienttypes.ErrFailedConnectionStateVerification, + "connection end ≠ previous stored connection: \n%v\n≠\n%v", connectionEnd, prevConnection, + ) + } + + return nil +} + +// VerifyChannelState verifies a proof of the channel state of the specified +// channel end, under the specified port, stored on the local machine. +func (cs ClientState) VerifyChannelState( + store sdk.KVStore, + cdc codec.BinaryMarshaler, + _ exported.Height, + prefix exported.Prefix, + _ []byte, + portID, + channelID string, + channel exported.ChannelI, +) error { + path := host.ChannelKey(portID, channelID) + bz := store.Get(path) + if bz == nil { + return sdkerrors.Wrapf(clienttypes.ErrFailedChannelStateVerification, "not found for path %s", path) + } + + var prevChannel channeltypes.Channel + err := cdc.UnmarshalBinaryBare(bz, &prevChannel) + if err != nil { + return err + } + + if !reflect.DeepEqual(&prevChannel, channel) { + return sdkerrors.Wrapf( + clienttypes.ErrFailedChannelStateVerification, + "channel end ≠ previous stored channel: \n%v\n≠\n%v", channel, prevChannel, + ) + } + + return nil +} + +// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at +// the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketCommitment( + store sdk.KVStore, + _ codec.BinaryMarshaler, + _ exported.Height, + _ uint64, + _ uint64, + _ exported.Prefix, + _ []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, +) error { + path := host.PacketCommitmentKey(portID, channelID, sequence) + + data := store.Get(path) + if len(data) == 0 { + return sdkerrors.Wrapf(clienttypes.ErrFailedPacketCommitmentVerification, "not found for path %s", path) + } + + if !bytes.Equal(data, commitmentBytes) { + return sdkerrors.Wrapf( + clienttypes.ErrFailedPacketCommitmentVerification, + "commitment ≠ previous commitment: \n%X\n≠\n%X", commitmentBytes, data, + ) + } + + return nil +} + +// VerifyPacketAcknowledgement verifies a proof of an incoming packet +// acknowledgement at the specified port, specified channel, and specified sequence. +func (cs ClientState) VerifyPacketAcknowledgement( + store sdk.KVStore, + _ codec.BinaryMarshaler, + _ exported.Height, + _ uint64, + _ uint64, + _ exported.Prefix, + _ []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, +) error { + path := host.PacketAcknowledgementKey(portID, channelID, sequence) + + data := store.Get(path) + if len(data) == 0 { + return sdkerrors.Wrapf(clienttypes.ErrFailedPacketAckVerification, "not found for path %s", path) + } + + if !bytes.Equal(data, acknowledgement) { + return sdkerrors.Wrapf( + clienttypes.ErrFailedPacketAckVerification, + "ak bytes ≠ previous ack: \n%X\n≠\n%X", acknowledgement, data, + ) + } + + return nil +} + +// VerifyPacketReceiptAbsence verifies a proof of the absence of an +// incoming packet receipt at the specified port, specified channel, and +// specified sequence. +func (cs ClientState) VerifyPacketReceiptAbsence( + store sdk.KVStore, + _ codec.BinaryMarshaler, + _ exported.Height, + _ uint64, + _ uint64, + _ exported.Prefix, + _ []byte, + portID, + channelID string, + sequence uint64, +) error { + path := host.PacketReceiptKey(portID, channelID, sequence) + + data := store.Get(path) + if data != nil { + return sdkerrors.Wrap(clienttypes.ErrFailedPacketReceiptVerification, "expected no packet receipt") + } + + return nil +} + +// VerifyNextSequenceRecv verifies a proof of the next sequence number to be +// received of the specified channel at the specified port. +func (cs ClientState) VerifyNextSequenceRecv( + store sdk.KVStore, + _ codec.BinaryMarshaler, + _ exported.Height, + _ uint64, + _ uint64, + _ exported.Prefix, + _ []byte, + portID, + channelID string, + nextSequenceRecv uint64, +) error { + path := host.NextSequenceRecvKey(portID, channelID) + + data := store.Get(path) + if len(data) == 0 { + return sdkerrors.Wrapf(clienttypes.ErrFailedNextSeqRecvVerification, "not found for path %s", path) + } + + prevSequenceRecv := binary.BigEndian.Uint64(data) + if prevSequenceRecv != nextSequenceRecv { + return sdkerrors.Wrapf( + clienttypes.ErrFailedNextSeqRecvVerification, + "next sequence receive ≠ previous stored sequence (%d ≠ %d)", nextSequenceRecv, prevSequenceRecv, + ) + } + + return nil +} 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 new file mode 100644 index 000000000000..13a1367d5c21 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/client_state_test.go @@ -0,0 +1,521 @@ +package types_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/09-localhost/types" +) + +const ( + testConnectionID = "connectionid" + testPortID = "testportid" + testChannelID = "testchannelid" + testSequence = 1 +) + +func (suite *LocalhostTestSuite) TestValidate() { + testCases := []struct { + name string + clientState *types.ClientState + expPass bool + }{ + { + name: "valid client", + clientState: types.NewClientState("chainID", clienttypes.NewHeight(3, 10)), + expPass: true, + }, + { + name: "invalid chain id", + clientState: types.NewClientState(" ", clienttypes.NewHeight(3, 10)), + expPass: false, + }, + { + name: "invalid height", + clientState: types.NewClientState("chainID", clienttypes.ZeroHeight()), + expPass: false, + }, + } + + for _, tc := range testCases { + err := tc.clientState.Validate() + if tc.expPass { + suite.Require().NoError(err, tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} + +func (suite *LocalhostTestSuite) TestInitialize() { + testCases := []struct { + name string + consState exported.ConsensusState + expPass bool + }{ + { + "valid initialization", + nil, + true, + }, + { + "invalid consenus state", + &ibctmtypes.ConsensusState{}, + false, + }, + } + + clientState := types.NewClientState("chainID", clienttypes.NewHeight(3, 10)) + + for _, tc := range testCases { + err := clientState.Initialize(suite.ctx, suite.cdc, suite.store, tc.consState) + + if tc.expPass { + suite.Require().NoError(err, "valid testcase: %s failed", tc.name) + } else { + suite.Require().Error(err, "invalid testcase: %s passed", tc.name) + } + } +} + +func (suite *LocalhostTestSuite) TestVerifyClientState() { + clientState := types.NewClientState("chainID", clientHeight) + invalidClient := types.NewClientState("chainID", clienttypes.NewHeight(0, 12)) + + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + counterparty *types.ClientState + expPass bool + }{ + { + name: "proof verification success", + clientState: clientState, + malleate: func() { + bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) + suite.store.Set(host.ClientStateKey(), bz) + }, + counterparty: clientState, + expPass: true, + }, + { + name: "proof verification failed: invalid client", + clientState: clientState, + malleate: func() { + bz := clienttypes.MustMarshalClientState(suite.cdc, clientState) + suite.store.Set(host.ClientStateKey(), bz) + }, + counterparty: invalidClient, + expPass: false, + }, + { + name: "proof verification failed: client not stored", + clientState: clientState, + malleate: func() {}, + counterparty: clientState, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyClientState( + suite.store, suite.cdc, clienttypes.NewHeight(0, 10), nil, "", []byte{}, tc.counterparty, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } + +} + +func (suite *LocalhostTestSuite) TestVerifyClientConsensusState() { + clientState := types.NewClientState("chainID", clientHeight) + err := clientState.VerifyClientConsensusState( + nil, nil, nil, "", nil, nil, nil, nil, + ) + suite.Require().NoError(err) +} + +func (suite *LocalhostTestSuite) TestCheckHeaderAndUpdateState() { + clientState := types.NewClientState("chainID", clientHeight) + cs, _, err := clientState.CheckHeaderAndUpdateState(suite.ctx, nil, nil, nil) + suite.Require().NoError(err) + suite.Require().Equal(uint64(0), cs.GetLatestHeight().GetRevisionNumber()) + suite.Require().Equal(suite.ctx.BlockHeight(), int64(cs.GetLatestHeight().GetRevisionHeight())) + suite.Require().Equal(suite.ctx.BlockHeader().ChainID, clientState.ChainId) +} + +func (suite *LocalhostTestSuite) TestMisbehaviourAndUpdateState() { + clientState := types.NewClientState("chainID", clientHeight) + cs, err := clientState.CheckMisbehaviourAndUpdateState(suite.ctx, nil, nil, nil) + suite.Require().Error(err) + suite.Require().Nil(cs) +} + +func (suite *LocalhostTestSuite) TestProposedHeaderAndUpdateState() { + clientState := types.NewClientState("chainID", clientHeight) + cs, consState, err := clientState.CheckProposedHeaderAndUpdateState(suite.ctx, nil, nil, nil) + suite.Require().Error(err) + suite.Require().Nil(cs) + suite.Require().Nil(consState) +} + +func (suite *LocalhostTestSuite) TestVerifyConnectionState() { + counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) + conn1 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("1", nil)}, 0) + conn2 := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []*connectiontypes.Version{connectiontypes.NewVersion("2", nil)}, 0) + + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + connection connectiontypes.ConnectionEnd + expPass bool + }{ + { + name: "proof verification success", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&conn1) + suite.Require().NoError(err) + suite.store.Set(host.ConnectionKey(testConnectionID), bz) + }, + connection: conn1, + expPass: true, + }, + { + name: "proof verification failed: connection not stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() {}, + connection: conn1, + expPass: false, + }, + { + name: "proof verification failed: unmarshal error", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set(host.ConnectionKey(testConnectionID), []byte("connection")) + }, + connection: conn1, + expPass: false, + }, + { + name: "proof verification failed: different connection stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&conn2) + suite.Require().NoError(err) + suite.store.Set(host.ConnectionKey(testConnectionID), bz) + }, + connection: conn1, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyConnectionState( + suite.store, suite.cdc, clientHeight, nil, []byte{}, testConnectionID, &tc.connection, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *LocalhostTestSuite) TestVerifyChannelState() { + counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) + ch1 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + ch2 := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "2.0.0") + + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + channel channeltypes.Channel + expPass bool + }{ + { + name: "proof verification success", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&ch1) + suite.Require().NoError(err) + suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) + }, + channel: ch1, + expPass: true, + }, + { + name: "proof verification failed: channel not stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() {}, + channel: ch1, + expPass: false, + }, + { + name: "proof verification failed: unmarshal failed", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set(host.ChannelKey(testPortID, testChannelID), []byte("channel")) + + }, + channel: ch1, + expPass: false, + }, + { + name: "proof verification failed: different channel stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + bz, err := suite.cdc.MarshalBinaryBare(&ch2) + suite.Require().NoError(err) + suite.store.Set(host.ChannelKey(testPortID, testChannelID), bz) + + }, + channel: ch1, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyChannelState( + suite.store, suite.cdc, clientHeight, nil, []byte{}, testPortID, testChannelID, &tc.channel, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() { + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + commitment []byte + expPass bool + }{ + { + name: "proof verification success", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("commitment"), + ) + }, + commitment: []byte("commitment"), + expPass: true, + }, + { + name: "proof verification failed: different commitment stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.PacketCommitmentKey(testPortID, testChannelID, testSequence), []byte("different"), + ) + }, + commitment: []byte("commitment"), + expPass: false, + }, + { + name: "proof verification failed: no commitment stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() {}, + commitment: []byte{}, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyPacketCommitment( + suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.commitment, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() { + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + ack []byte + expPass bool + }{ + { + name: "proof verification success", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("acknowledgement"), + ) + }, + ack: []byte("acknowledgement"), + expPass: true, + }, + { + name: "proof verification failed: different ack stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.PacketAcknowledgementKey(testPortID, testChannelID, testSequence), []byte("different"), + ) + }, + ack: []byte("acknowledgement"), + expPass: false, + }, + { + name: "proof verification failed: no commitment stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() {}, + ack: []byte{}, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyPacketAcknowledgement( + suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, testSequence, tc.ack, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *LocalhostTestSuite) TestVerifyPacketReceiptAbsence() { + clientState := types.NewClientState("chainID", clientHeight) + + err := clientState.VerifyPacketReceiptAbsence( + suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, + ) + + suite.Require().NoError(err, "receipt absence failed") + + suite.store.Set(host.PacketReceiptKey(testPortID, testChannelID, testSequence), []byte("receipt")) + + err = clientState.VerifyPacketReceiptAbsence( + suite.store, suite.cdc, clientHeight, 0, 0, nil, nil, testPortID, testChannelID, testSequence, + ) + suite.Require().Error(err, "receipt exists in store") +} + +func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() { + nextSeqRecv := uint64(5) + + testCases := []struct { + name string + clientState *types.ClientState + malleate func() + nextSeqRecv uint64 + expPass bool + }{ + { + name: "proof verification success", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.NextSequenceRecvKey(testPortID, testChannelID), + sdk.Uint64ToBigEndian(nextSeqRecv), + ) + }, + nextSeqRecv: nextSeqRecv, + expPass: true, + }, + { + name: "proof verification failed: different nextSeqRecv stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() { + suite.store.Set( + host.NextSequenceRecvKey(testPortID, testChannelID), + sdk.Uint64ToBigEndian(3), + ) + }, + nextSeqRecv: nextSeqRecv, + expPass: false, + }, + { + name: "proof verification failed: no nextSeqRecv stored", + clientState: types.NewClientState("chainID", clientHeight), + malleate: func() {}, + nextSeqRecv: nextSeqRecv, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + tc.malleate() + + err := tc.clientState.VerifyNextSequenceRecv( + suite.store, suite.cdc, clientHeight, 0, 0, nil, []byte{}, testPortID, testChannelID, nextSeqRecv, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/ibc/light-clients/09-localhost/types/codec.go b/x/ibc/light-clients/09-localhost/types/codec.go new file mode 100644 index 000000000000..b338dfb699ac --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/codec.go @@ -0,0 +1,15 @@ +package types + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +// RegisterInterfaces register the ibc interfaces submodule implementations to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*exported.ClientState)(nil), + &ClientState{}, + ) +} diff --git a/x/ibc/light-clients/09-localhost/types/errors.go b/x/ibc/light-clients/09-localhost/types/errors.go new file mode 100644 index 000000000000..57ad7c1f6a68 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/errors.go @@ -0,0 +1,10 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Localhost sentinel errors +var ( + ErrConsensusStatesNotStored = sdkerrors.Register(SubModuleName, 2, "localhost does not store consensus states") +) diff --git a/x/ibc/light-clients/09-localhost/types/keys.go b/x/ibc/light-clients/09-localhost/types/keys.go new file mode 100644 index 000000000000..2fe7c7e48f5b --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/keys.go @@ -0,0 +1,6 @@ +package types + +const ( + // SubModuleName for the localhost (loopback) client + SubModuleName = "localhost" +) diff --git a/x/ibc/light-clients/09-localhost/types/localhost.pb.go b/x/ibc/light-clients/09-localhost/types/localhost.pb.go new file mode 100644 index 000000000000..53f01758492b --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/localhost.pb.go @@ -0,0 +1,369 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ibc/lightclients/localhost/v1/localhost.proto + +package types + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + _ "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 + +// ClientState defines a loopback (localhost) client. It requires (read-only) +// access to keys outside the client prefix. +type ClientState struct { + // self chain ID + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty" yaml:"chain_id"` + // self latest block height + Height types.Height `protobuf:"bytes,2,opt,name=height,proto3" json:"height"` +} + +func (m *ClientState) Reset() { *m = ClientState{} } +func (m *ClientState) String() string { return proto.CompactTextString(m) } +func (*ClientState) ProtoMessage() {} +func (*ClientState) Descriptor() ([]byte, []int) { + return fileDescriptor_acd9f5b22d41bf6d, []int{0} +} +func (m *ClientState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClientState.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 *ClientState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientState.Merge(m, src) +} +func (m *ClientState) XXX_Size() int { + return m.Size() +} +func (m *ClientState) XXX_DiscardUnknown() { + xxx_messageInfo_ClientState.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientState proto.InternalMessageInfo + +func init() { + proto.RegisterType((*ClientState)(nil), "ibc.lightclients.localhost.v1.ClientState") +} + +func init() { + proto.RegisterFile("ibc/lightclients/localhost/v1/localhost.proto", fileDescriptor_acd9f5b22d41bf6d) +} + +var fileDescriptor_acd9f5b22d41bf6d = []byte{ + // 279 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xcd, 0x4c, 0x4a, 0xd6, + 0xcf, 0xc9, 0x4c, 0xcf, 0x28, 0x49, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x29, 0xd6, 0xcf, 0xc9, 0x4f, + 0x4e, 0xcc, 0xc9, 0xc8, 0x2f, 0x2e, 0xd1, 0x2f, 0x33, 0x44, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, + 0xf2, 0x85, 0x64, 0x33, 0x93, 0x92, 0xf5, 0x90, 0x95, 0xeb, 0x21, 0x54, 0x94, 0x19, 0x4a, 0x89, + 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x55, 0xea, 0x83, 0x58, 0x10, 0x4d, 0x52, 0xf2, 0x20, 0x3b, 0x92, + 0xf3, 0x8b, 0x52, 0xf5, 0x21, 0x9a, 0x40, 0x06, 0x43, 0x58, 0x10, 0x05, 0x4a, 0xb5, 0x5c, 0xdc, + 0xce, 0x60, 0x7e, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x1e, 0x17, 0x47, 0x72, 0x46, 0x62, 0x66, + 0x5e, 0x7c, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xa7, 0x93, 0xf0, 0xa7, 0x7b, 0xf2, 0xfc, + 0x95, 0x89, 0xb9, 0x39, 0x56, 0x4a, 0x30, 0x19, 0xa5, 0x20, 0x76, 0x30, 0xd3, 0x33, 0x45, 0xc8, + 0x82, 0x8b, 0x2d, 0x23, 0x15, 0xe4, 0x26, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x29, 0x3d, + 0x90, 0x2b, 0x41, 0x16, 0xea, 0x41, 0xad, 0x29, 0x33, 0xd4, 0xf3, 0x00, 0xab, 0x70, 0x62, 0x39, + 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xaa, 0xde, 0x8a, 0xa5, 0x63, 0x81, 0x3c, 0x83, 0x53, 0xec, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, + 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x39, 0xa7, 0x67, 0x96, 0x64, 0x94, 0x26, + 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0x43, 0x29, 0xdd, 0xe2, 0x94, + 0x6c, 0xfd, 0x0a, 0x7d, 0x78, 0xe0, 0xe9, 0xc2, 0x42, 0xcf, 0xc0, 0x52, 0x17, 0x11, 0x80, 0x25, + 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c, 0x60, 0x4f, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xcd, + 0x7d, 0x91, 0x77, 0x6b, 0x01, 0x00, 0x00, +} + +func (m *ClientState) 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 *ClientState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Height.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLocalhost(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintLocalhost(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintLocalhost(dAtA []byte, offset int, v uint64) int { + offset -= sovLocalhost(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ClientState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovLocalhost(uint64(l)) + } + l = m.Height.Size() + n += 1 + l + sovLocalhost(uint64(l)) + return n +} + +func sovLocalhost(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozLocalhost(x uint64) (n int) { + return sovLocalhost(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ClientState) 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 ErrIntOverflowLocalhost + } + 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: ClientState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ClientState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalhost + } + 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 ErrInvalidLengthLocalhost + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLocalhost + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalhost + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLocalhost + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLocalhost + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Height.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLocalhost(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLocalhost + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipLocalhost(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, ErrIntOverflowLocalhost + } + 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, ErrIntOverflowLocalhost + } + 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, ErrIntOverflowLocalhost + } + 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, ErrInvalidLengthLocalhost + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupLocalhost + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthLocalhost + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthLocalhost = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLocalhost = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupLocalhost = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ibc/light-clients/09-localhost/types/localhost_test.go b/x/ibc/light-clients/09-localhost/types/localhost_test.go new file mode 100644 index 000000000000..8ebaef843b08 --- /dev/null +++ b/x/ibc/light-clients/09-localhost/types/localhost_test.go @@ -0,0 +1,43 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +const ( + height = 4 +) + +var ( + clientHeight = clienttypes.NewHeight(0, 10) +) + +type LocalhostTestSuite struct { + suite.Suite + + cdc codec.Marshaler + ctx sdk.Context + store sdk.KVStore +} + +func (suite *LocalhostTestSuite) SetupTest() { + isCheckTx := false + app := simapp.Setup(isCheckTx) + + suite.cdc = app.AppCodec() + suite.ctx = app.BaseApp.NewContext(isCheckTx, tmproto.Header{Height: 1, ChainID: "ibc-chain"}) + suite.store = app.IBCKeeper.ClientKeeper.ClientStore(suite.ctx, exported.Localhost) +} + +func TestLocalhostTestSuite(t *testing.T) { + suite.Run(t, new(LocalhostTestSuite)) +} diff --git a/x/ibc/spec/README.md b/x/ibc/spec/README.md new file mode 100644 index 000000000000..a699c10abdc7 --- /dev/null +++ b/x/ibc/spec/README.md @@ -0,0 +1,114 @@ + + +# `ibc` + +## Abstract + +This specification defines the implementation of the IBC protocol on the Cosmos SDK, the +changes made to the specification and where to find each specific ICS spec within +the module. + +For the general specification please refer to the [Interchain Standards](https://github.com/cosmos/ics). + +## Contents + +1. **Applications** + + 1.1. [Transfer](./../applications/transfer/spec/README.md) +2. **[Core](./../core/spec/README.md)** +3. **Light Clients** + + 3.1 [Solo Machine Client](./../light-clients/06-solomachine/spec/README.md) + + 3.2 [Tendermint Client](./../light-clients/07-tendermint/spec/README.md) + + 3.3 [Localhost Client](./../light-clients/09-localhost/spec/README.md) + +## Implementation Details + +As stated above, the IBC implementation on the Cosmos SDK introduces some changes +to the general specification, in order to avoid code duplication and to take +advantage of the SDK architectural components such as the transaction routing +through `Handlers`. + +### Interchain Standards reference + +The following list is a mapping from each Interchain Standard to their implementation +in the SDK's `x/ibc` module: + +* [ICS 002 - Client Semantics](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics): Implemented in [`x/ibc/core/02-client`](https://github.com/cosmos/tree/master/ibc/core/02-client) +* [ICS 003 - Connection Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-003-connection-semantics): Implemented in [`x/ibc/core/03-connection`](https://github.com/cosmos/tree/master/ibc/core/03-connection) +* [ICS 004 - Channel and Packet Semantics](https://github.com/cosmos/ics/blob/master/spec/ics-004-channel-and-packet-semantics): Implemented in [`x/ibc/core/04-channel`](https://github.com/cosmos/tree/master/ibc/core/04-channel) +* [ICS 005 - Port Allocation](https://github.com/cosmos/ics/blob/master/spec/ics-005-port-allocation): Implemented in [`x/ibc/core/05-port`](https://github.com/cosmos/tree/master/ibc/core/05-port) +* [ICS 006 - Solo Machine Client](https://github.com/cosmos/ics/blob/master/spec/ics-006-solo-machine-client): Implemented in [`x/ibc/light-clients/06-solomachine`](https://github.com/cosmos/tree/master/ibc/solomachine) +* [ICS 007 - Tendermint Client](https://github.com/cosmos/ics/blob/master/spec/ics-007-tendermint-client): Implemented in [`x/ibc/light-clients/07-tendermint`](https://github.com/cosmos/tree/master/ibc/light-clients/07-tendermint) +* [ICS 009 - Loopback Client](https://github.com/cosmos/ics/blob/master/spec/ics-009-loopback-client): Implemented in [`x/ibc/light-clients/09-localhost`](https://github.com/cosmos/tree/master/ibc/light-clients/09-localhost) +* [ICS 018- Relayer Algorithms](https://github.com/cosmos/ics/tree/master/spec/ics-018-relayer-algorithms): Implemented in it's own [relayer repository](https://github.com/cosmos/relayer) +* [ICS 020 - Fungible Token Transfer](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer): Implemented in [`x/ibc/applications/transfer`](https://github.com/cosmos/tree/master/ibc/applications/transfer) +* [ICS 023 - Vector Commitments](https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments): Implemented in [`x/ibc/core/23-commitment`](https://github.com/cosmos/tree/master/ibc/core/23-commitment) +* [ICS 024 - Host Requirements](https://github.com/cosmos/ics/tree/master/spec/ics-024-host-requirements): Implemented in [`x/ibc/core/24-host`](https://github.com/cosmos/tree/master/ibc/core/24-host) +* [ICS 025 - Handler Interface](https://github.com/cosmos/ics/tree/master/spec/ics-025-handler-interface): `Handler` interfaces are implemented at the top level in `x/ibc/handler.go`, +which call each ICS submodule's handlers (i.e `x/ibc/*/{XX-ICS}/handler.go`). +* [ICS 026 - Routing Module](https://github.com/cosmos/ics/blob/master/spec/ics-026-routing-module): Replaced by [ADR 15 - IBC Packet Receiver](../../../docs/architecture/adr-015-ibc-packet-receiver.md). + +### Architecture Decision Records (ADR) + +The following ADR provide the design and architecture decision of IBC-related components. + +* [ADR 001 - Coin Source Tracing](../../../docs/architecture/adr-001-coin-source-tracing.md): standard to hash the ICS20's fungible token +denomination trace path in order to support special characters and limit the maximum denomination length. +* [ADR 17 - Historical Header Module](../../../docs/architecture/adr-017-historical-header-module.md): Introduces the ability to introspect past +consensus states in order to verify their membership in the counterparty clients. +* [ADR 19 - Protobuf State Encoding](../../../docs/architecture/adr-019-protobuf-state-encoding.md): Migration from Amino to Protobuf for state encoding. +* [ADR 020 - Protocol Buffer Transaction Encoding](./../../docs/architecture/adr-020-protobuf-transaction-encoding.md): Client side migration to Protobuf. +* [ADR 021 - Protocol Buffer Query Encoding](../../../docs/architecture/adr-020-protobuf-query-encoding.md): Queries migration to Protobuf. +* [ADR 026 - IBC Client Recovery Mechanisms](../../../docs/architecture/adr-026-ibc-client-recovery-mechanisms.md): Allows IBC Clients to be recovered after freezing or expiry. + +### SDK Modules + +* [`x/capability`](https://github.com/cosmos/tree/master/x/capability): The capability module provides object-capability keys support through scoped keepers in order to authenticate usage of ports or channels. Check [ADR 3 - Dynamic Capability Store](../../../docs/architecture/adr-003-dynamic-capability-store.md) for more details. + +## IBC module architecture + +> **NOTE for auditors**: If you're not familiar with the overall module structure from +the SDK modules, please check this [document](../../../docs/building-modules/structure.md) as +prerequisite reading. + +For ease of auditing, every Interchain Standard has been developed in its own +package. The development team separated the IBC TAO (Transport, Authentication, Ordering) ICS specifications from the IBC application level +specification. The following tree describes the architecture of the directories that +the `ibc` (TAO) and `ibc-transfer` ([ICS20](https://github.com/cosmos/ics/tree/master/spec/ics-020-fungible-token-transfer)) modules: + +```shell +x/ibc +├── applications/ +│ └──transfer/ +├── core/ +│   ├── 02-client/ +│   ├── 03-connection/ +│   ├── 04-channel/ +│   ├── 05-port/ +│   ├── 23-commitment/ +│   ├── 24-host/ +│  ├── client +│  │   └── cli +│ │       └── cli.go +│  ├── keeper +│  │ ├── keeper.go +│   │ └── querier.go +│ ├── types +│ │ ├── errors.go +│ │ └── keys.go +│ ├── handler.go +│ └── module.go +├── light-clients/ +│   ├── 06-solomachine/ +│   ├── 07-tendermint/ +│   └── 09-localhost/ +└── testing/ +``` diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go new file mode 100644 index 000000000000..b46a404030b1 --- /dev/null +++ b/x/ibc/testing/chain.go @@ -0,0 +1,903 @@ +package ibctesting + +import ( + "bytes" + "fmt" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/tmhash" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmprotoversion "github.com/tendermint/tendermint/proto/tendermint/version" + tmtypes "github.com/tendermint/tendermint/types" + tmversion "github.com/tendermint/tendermint/version" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "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" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/core/03-connection/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/core/types" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + // Default params constants used to create a TM client + TrustingPeriod time.Duration = time.Hour * 24 * 7 * 2 + UnbondingPeriod time.Duration = time.Hour * 24 * 7 * 3 + MaxClockDrift time.Duration = time.Second * 10 + DefaultDelayPeriod uint64 = 0 + + DefaultChannelVersion = ibctransfertypes.Version + InvalidID = "IDisInvalid" + + ConnectionIDPrefix = "conn" + ChannelIDPrefix = "chan" + + TransferPort = ibctransfertypes.ModuleName + MockPort = mock.ModuleName + + // used for testing UpdateClientProposal + Title = "title" + Description = "description" +) + +var ( + DefaultOpenInitVersion *connectiontypes.Version + + // Default params variables used to create a TM client + DefaultTrustLevel ibctmtypes.Fraction = ibctmtypes.DefaultTrustLevel + TestHash = tmhash.Sum([]byte("TESTING HASH")) + TestCoin = sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)) + + UpgradePath = []string{"upgrade", "upgradedIBCState"} + + ConnectionVersion = connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions())[0] + + MockAcknowledgement = mock.MockAcknowledgement + MockCommitment = mock.MockCommitment +) + +// TestChain is a testing struct that wraps a simapp with the last TM Header, the current ABCI +// header and the validators of the TestChain. It also contains a field called ChainID. This +// is the clientID that *other* chains use to refer to this TestChain. The SenderAccount +// is used for delivering transactions through the application state. +// NOTE: the actual application uses an empty chain-id for ease of testing. +type TestChain struct { + t *testing.T + + App *simapp.SimApp + ChainID string + LastHeader *ibctmtypes.Header // header for last block height committed + CurrentHeader tmproto.Header // header for current block height + QueryServer types.QueryServer + TxConfig client.TxConfig + Codec codec.BinaryMarshaler + + Vals *tmtypes.ValidatorSet + Signers []tmtypes.PrivValidator + + senderPrivKey cryptotypes.PrivKey + SenderAccount authtypes.AccountI + + // IBC specific helpers + ClientIDs []string // ClientID's used on this chain + Connections []*TestConnection // track connectionID's created for this chain +} + +// NewTestChain initializes a new TestChain instance with a single validator set using a +// generated private key. It also creates a sender account to be used for delivering transactions. +// +// The first block height is committed to state in order to allow for client creations on +// counterparty chains. The TestChain will return with a block height starting at 2. +// +// Time management is handled by the Coordinator in order to ensure synchrony between chains. +// Each update of any chain increments the block header time for all chains by 5 seconds. +func NewTestChain(t *testing.T, chainID string) *TestChain { + // generate validator private/public key + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + signers := []tmtypes.PrivValidator{privVal} + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } + + app := simapp.SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + + // create current header and call begin block + header := tmproto.Header{ + ChainID: chainID, + Height: 1, + Time: globalStartTime, + } + + txConfig := simapp.MakeTestEncodingConfig().TxConfig + + // create an account to send transactions from + chain := &TestChain{ + t: t, + ChainID: chainID, + App: app, + CurrentHeader: header, + QueryServer: app.IBCKeeper, + TxConfig: txConfig, + Codec: app.AppCodec(), + Vals: valSet, + Signers: signers, + senderPrivKey: senderPrivKey, + SenderAccount: acc, + ClientIDs: make([]string, 0), + Connections: make([]*TestConnection, 0), + } + + cap := chain.App.IBCKeeper.PortKeeper.BindPort(chain.GetContext(), MockPort) + err = chain.App.ScopedIBCMockKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(MockPort)) + require.NoError(t, err) + + chain.NextBlock() + + return chain +} + +// GetContext returns the current context for the application. +func (chain *TestChain) GetContext() sdk.Context { + return chain.App.BaseApp.NewContext(false, chain.CurrentHeader) +} + +// QueryProof performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. +func (chain *TestChain) QueryProof(key []byte) ([]byte, clienttypes.Height) { + res := chain.App.Query(abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", host.StoreKey), + Height: chain.App.LastBlockHeight() - 1, + Data: key, + Prove: true, + }) + + merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) + require.NoError(chain.t, err) + + proof, err := chain.App.AppCodec().MarshalBinaryBare(&merkleProof) + require.NoError(chain.t, err) + + revision := clienttypes.ParseChainID(chain.ChainID) + + // proof height + 1 is returned as the proof created corresponds to the height the proof + // was created in the IAVL tree. Tendermint and subsequently the clients that rely on it + // have heights 1 above the IAVL tree. Thus we return proof height + 1 + return proof, clienttypes.NewHeight(revision, uint64(res.Height)+1) +} + +// QueryUpgradeProof performs an abci query with the given key and returns the proto encoded merkle proof +// for the query and the height at which the proof will succeed on a tendermint verifier. +func (chain *TestChain) QueryUpgradeProof(key []byte, height uint64) ([]byte, clienttypes.Height) { + res := chain.App.Query(abci.RequestQuery{ + Path: "store/upgrade/key", + Height: int64(height - 1), + Data: key, + Prove: true, + }) + + merkleProof, err := commitmenttypes.ConvertProofs(res.ProofOps) + require.NoError(chain.t, err) + + proof, err := chain.App.AppCodec().MarshalBinaryBare(&merkleProof) + require.NoError(chain.t, err) + + revision := clienttypes.ParseChainID(chain.ChainID) + + // proof height + 1 is returned as the proof created corresponds to the height the proof + // was created in the IAVL tree. Tendermint and subsequently the clients that rely on it + // have heights 1 above the IAVL tree. Thus we return proof height + 1 + return proof, clienttypes.NewHeight(revision, uint64(res.Height+1)) +} + +// QueryClientStateProof performs and abci query for a client state +// stored with a given clientID and returns the ClientState along with the proof +func (chain *TestChain) QueryClientStateProof(clientID string) (exported.ClientState, []byte) { + // retrieve client state to provide proof for + clientState, found := chain.App.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID) + require.True(chain.t, found) + + clientKey := host.FullClientStateKey(clientID) + proofClient, _ := chain.QueryProof(clientKey) + + return clientState, proofClient +} + +// QueryConsensusStateProof performs an abci query for a consensus state +// stored on the given clientID. The proof and consensusHeight are returned. +func (chain *TestChain) QueryConsensusStateProof(clientID string) ([]byte, clienttypes.Height) { + clientState := chain.GetClientState(clientID) + + consensusHeight := clientState.GetLatestHeight().(clienttypes.Height) + consensusKey := host.FullConsensusStateKey(clientID, consensusHeight) + proofConsensus, _ := chain.QueryProof(consensusKey) + + return proofConsensus, consensusHeight +} + +// NextBlock sets the last header to the current header and increments the current header to be +// at the next block height. It does not update the time as that is handled by the Coordinator. +// +// CONTRACT: this function must only be called after app.Commit() occurs +func (chain *TestChain) NextBlock() { + // set the last header to the current header + // use nil trusted fields + chain.LastHeader = chain.CurrentTMClientHeader() + + // increment the current header + chain.CurrentHeader = tmproto.Header{ + ChainID: chain.ChainID, + Height: chain.App.LastBlockHeight() + 1, + AppHash: chain.App.LastCommitID().Hash, + // NOTE: the time is increased by the coordinator to maintain time synchrony amongst + // chains. + Time: chain.CurrentHeader.Time, + ValidatorsHash: chain.Vals.Hash(), + NextValidatorsHash: chain.Vals.Hash(), + } + + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + +} + +// sendMsgs delivers a transaction through the application without returning the result. +func (chain *TestChain) sendMsgs(msgs ...sdk.Msg) error { + _, err := chain.SendMsgs(msgs...) + return err +} + +// SendMsgs delivers a transaction through the application. It updates the senders sequence +// number and updates the TestChain's headers. It returns the result and error if one +// occurred. +func (chain *TestChain) SendMsgs(msgs ...sdk.Msg) (*sdk.Result, error) { + _, r, err := simapp.SignCheckDeliver( + chain.t, + chain.TxConfig, + chain.App.BaseApp, + chain.GetContext().BlockHeader(), + msgs, + chain.ChainID, + []uint64{chain.SenderAccount.GetAccountNumber()}, + []uint64{chain.SenderAccount.GetSequence()}, + true, true, chain.senderPrivKey, + ) + if err != nil { + return nil, err + } + + // SignCheckDeliver calls app.Commit() + chain.NextBlock() + + // increment sequence for successful transaction execution + chain.SenderAccount.SetSequence(chain.SenderAccount.GetSequence() + 1) + + return r, nil +} + +// GetClientState retrieves the client state for the provided clientID. The client is +// expected to exist otherwise testing will fail. +func (chain *TestChain) GetClientState(clientID string) exported.ClientState { + clientState, found := chain.App.IBCKeeper.ClientKeeper.GetClientState(chain.GetContext(), clientID) + require.True(chain.t, found) + + return clientState +} + +// GetConsensusState retrieves the consensus state for the provided clientID and height. +// It will return a success boolean depending on if consensus state exists or not. +func (chain *TestChain) GetConsensusState(clientID string, height exported.Height) (exported.ConsensusState, bool) { + return chain.App.IBCKeeper.ClientKeeper.GetClientConsensusState(chain.GetContext(), clientID, height) +} + +// GetValsAtHeight will return the validator set of the chain at a given height. It will return +// a success boolean depending on if the validator set exists or not at that height. +func (chain *TestChain) GetValsAtHeight(height int64) (*tmtypes.ValidatorSet, bool) { + histInfo, ok := chain.App.StakingKeeper.GetHistoricalInfo(chain.GetContext(), height) + if !ok { + return nil, false + } + + valSet := stakingtypes.Validators(histInfo.Valset) + + tmValidators, err := teststaking.ToTmValidators(valSet) + if err != nil { + panic(err) + } + return tmtypes.NewValidatorSet(tmValidators), true +} + +// GetConnection retrieves an IBC Connection for the provided TestConnection. The +// connection is expected to exist otherwise testing will fail. +func (chain *TestChain) GetConnection(testConnection *TestConnection) connectiontypes.ConnectionEnd { + connection, found := chain.App.IBCKeeper.ConnectionKeeper.GetConnection(chain.GetContext(), testConnection.ID) + require.True(chain.t, found) + + return connection +} + +// GetChannel retrieves an IBC Channel for the provided TestChannel. The channel +// is expected to exist otherwise testing will fail. +func (chain *TestChain) GetChannel(testChannel TestChannel) channeltypes.Channel { + channel, found := chain.App.IBCKeeper.ChannelKeeper.GetChannel(chain.GetContext(), testChannel.PortID, testChannel.ID) + require.True(chain.t, found) + + return channel +} + +// GetAcknowledgement retrieves an acknowledgement for the provided packet. If the +// acknowledgement does not exist then testing will fail. +func (chain *TestChain) GetAcknowledgement(packet exported.PacketI) []byte { + ack, found := chain.App.IBCKeeper.ChannelKeeper.GetPacketAcknowledgement(chain.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + require.True(chain.t, found) + + return ack +} + +// GetPrefix returns the prefix for used by a chain in connection creation +func (chain *TestChain) GetPrefix() commitmenttypes.MerklePrefix { + return commitmenttypes.NewMerklePrefix(chain.App.IBCKeeper.ConnectionKeeper.GetCommitmentPrefix().Bytes()) +} + +// NewClientID appends a new clientID string in the format: +// ClientFor +func (chain *TestChain) NewClientID(clientType string) string { + clientID := fmt.Sprintf("%s-%s", clientType, strconv.Itoa(len(chain.ClientIDs))) + chain.ClientIDs = append(chain.ClientIDs, clientID) + return clientID +} + +// AddTestConnection appends a new TestConnection which contains references +// to the connection id, client id and counterparty client id. +func (chain *TestChain) AddTestConnection(clientID, counterpartyClientID string) *TestConnection { + conn := chain.ConstructNextTestConnection(clientID, counterpartyClientID) + + chain.Connections = append(chain.Connections, conn) + return conn +} + +// ConstructNextTestConnection constructs the next test connection to be +// created given a clientID and counterparty clientID. The connection id +// format: -conn +func (chain *TestChain) ConstructNextTestConnection(clientID, counterpartyClientID string) *TestConnection { + connectionID := connectiontypes.FormatConnectionIdentifier(uint64(len(chain.Connections))) + return &TestConnection{ + ID: connectionID, + ClientID: clientID, + NextChannelVersion: DefaultChannelVersion, + CounterpartyClientID: counterpartyClientID, + } +} + +// GetFirstTestConnection returns the first test connection for a given clientID. +// The connection may or may not exist in the chain state. +func (chain *TestChain) GetFirstTestConnection(clientID, counterpartyClientID string) *TestConnection { + if len(chain.Connections) > 0 { + return chain.Connections[0] + } + + return chain.ConstructNextTestConnection(clientID, counterpartyClientID) +} + +// AddTestChannel appends a new TestChannel which contains references to the port and channel ID +// used for channel creation and interaction. See 'NextTestChannel' for channel ID naming format. +func (chain *TestChain) AddTestChannel(conn *TestConnection, portID string) TestChannel { + channel := chain.NextTestChannel(conn, portID) + conn.Channels = append(conn.Channels, channel) + return channel +} + +// NextTestChannel returns the next test channel to be created on this connection, but does not +// add it to the list of created channels. This function is expected to be used when the caller +// has not created the associated channel in app state, but would still like to refer to the +// non-existent channel usually to test for its non-existence. +// +// channel ID format: -chan +// +// The port is passed in by the caller. +func (chain *TestChain) NextTestChannel(conn *TestConnection, portID string) TestChannel { + nextChanSeq := chain.App.IBCKeeper.ChannelKeeper.GetNextChannelSequence(chain.GetContext()) + channelID := channeltypes.FormatChannelIdentifier(nextChanSeq) + return TestChannel{ + PortID: portID, + ID: channelID, + ClientID: conn.ClientID, + CounterpartyClientID: conn.CounterpartyClientID, + Version: conn.NextChannelVersion, + } +} + +// ConstructMsgCreateClient constructs a message to create a new client state (tendermint or solomachine). +// NOTE: a solo machine client will be created with an empty diversifier. +func (chain *TestChain) ConstructMsgCreateClient(counterparty *TestChain, clientID string, clientType string) *clienttypes.MsgCreateClient { + var ( + clientState exported.ClientState + consensusState exported.ConsensusState + ) + + switch clientType { + case exported.Tendermint: + height := counterparty.LastHeader.GetHeight().(clienttypes.Height) + clientState = ibctmtypes.NewClientState( + counterparty.ChainID, DefaultTrustLevel, TrustingPeriod, UnbondingPeriod, MaxClockDrift, + height, commitmenttypes.GetSDKSpecs(), UpgradePath, false, false, + ) + consensusState = counterparty.LastHeader.ConsensusState() + case exported.Solomachine: + solo := NewSolomachine(chain.t, chain.Codec, clientID, "", 1) + clientState = solo.ClientState() + consensusState = solo.ConsensusState() + default: + chain.t.Fatalf("unsupported client state type %s", clientType) + } + + msg, err := clienttypes.NewMsgCreateClient( + clientState, consensusState, chain.SenderAccount.GetAddress(), + ) + require.NoError(chain.t, err) + return msg +} + +// CreateTMClient will construct and execute a 07-tendermint MsgCreateClient. A counterparty +// client will be created on the (target) chain. +func (chain *TestChain) CreateTMClient(counterparty *TestChain, clientID string) error { + // construct MsgCreateClient using counterparty + msg := chain.ConstructMsgCreateClient(counterparty, clientID, exported.Tendermint) + return chain.sendMsgs(msg) +} + +// UpdateTMClient will construct and execute a 07-tendermint MsgUpdateClient. The counterparty +// client will be updated on the (target) chain. UpdateTMClient mocks the relayer flow +// necessary for updating a Tendermint client. +func (chain *TestChain) UpdateTMClient(counterparty *TestChain, clientID string) error { + header, err := chain.ConstructUpdateTMClientHeader(counterparty, clientID) + require.NoError(chain.t, err) + + msg, err := clienttypes.NewMsgUpdateClient( + clientID, header, + chain.SenderAccount.GetAddress(), + ) + require.NoError(chain.t, err) + + return chain.sendMsgs(msg) +} + +// ConstructUpdateTMClientHeader will construct a valid 07-tendermint Header to update the +// light client on the source chain. +func (chain *TestChain) ConstructUpdateTMClientHeader(counterparty *TestChain, clientID string) (*ibctmtypes.Header, error) { + header := counterparty.LastHeader + // Relayer must query for LatestHeight on client to get TrustedHeight + trustedHeight := chain.GetClientState(clientID).GetLatestHeight().(clienttypes.Height) + var ( + tmTrustedVals *tmtypes.ValidatorSet + ok bool + ) + // Once we get TrustedHeight from client, we must query the validators from the counterparty chain + // If the LatestHeight == LastHeader.Height, then TrustedValidators are current validators + // If LatestHeight < LastHeader.Height, we can query the historical validator set from HistoricalInfo + if trustedHeight == counterparty.LastHeader.GetHeight() { + tmTrustedVals = counterparty.Vals + } else { + // NOTE: We need to get validators from counterparty at height: trustedHeight+1 + // since the last trusted validators for a header at height h + // is the NextValidators at h+1 committed to in header h by + // NextValidatorsHash + tmTrustedVals, ok = counterparty.GetValsAtHeight(int64(trustedHeight.RevisionHeight + 1)) + if !ok { + return nil, sdkerrors.Wrapf(ibctmtypes.ErrInvalidHeaderHeight, "could not retrieve trusted validators at trustedHeight: %d", trustedHeight) + } + } + // inject trusted fields into last header + // for now assume revision number is 0 + header.TrustedHeight = trustedHeight + + trustedVals, err := tmTrustedVals.ToProto() + if err != nil { + return nil, err + } + header.TrustedValidators = trustedVals + + return header, nil + +} + +// ExpireClient fast forwards the chain's block time by the provided amount of time which will +// expire any clients with a trusting period less than or equal to this amount of time. +func (chain *TestChain) ExpireClient(amount time.Duration) { + chain.CurrentHeader.Time = chain.CurrentHeader.Time.Add(amount) +} + +// CurrentTMClientHeader creates a TM header using the current header parameters +// on the chain. The trusted fields in the header are set to nil. +func (chain *TestChain) CurrentTMClientHeader() *ibctmtypes.Header { + return chain.CreateTMClientHeader(chain.ChainID, chain.CurrentHeader.Height, clienttypes.Height{}, chain.CurrentHeader.Time, chain.Vals, nil, chain.Signers) +} + +// CreateTMClientHeader creates a TM header to update the TM client. Args are passed in to allow +// caller flexibility to use params that differ from the chain. +func (chain *TestChain) CreateTMClientHeader(chainID string, blockHeight int64, trustedHeight clienttypes.Height, timestamp time.Time, tmValSet, tmTrustedVals *tmtypes.ValidatorSet, signers []tmtypes.PrivValidator) *ibctmtypes.Header { + require.NotNil(chain.t, tmValSet) + + vsetHash := tmValSet.Hash() + tmHeader := tmtypes.Header{ + Version: tmprotoversion.Consensus{Block: tmversion.BlockProtocol, App: 2}, + ChainID: chainID, + Height: blockHeight, + Time: timestamp, + LastBlockID: MakeBlockID(make([]byte, tmhash.Size), 10_000, make([]byte, tmhash.Size)), + LastCommitHash: chain.App.LastCommitID().Hash, + DataHash: tmhash.Sum([]byte("data_hash")), + ValidatorsHash: vsetHash, + NextValidatorsHash: vsetHash, + ConsensusHash: tmhash.Sum([]byte("consensus_hash")), + AppHash: chain.CurrentHeader.AppHash, + LastResultsHash: tmhash.Sum([]byte("last_results_hash")), + EvidenceHash: tmhash.Sum([]byte("evidence_hash")), + ProposerAddress: tmValSet.Proposer.Address, + } + hhash := tmHeader.Hash() + blockID := MakeBlockID(hhash, 3, tmhash.Sum([]byte("part_set"))) + voteSet := tmtypes.NewVoteSet(chainID, blockHeight, 1, tmproto.PrecommitType, tmValSet) + + commit, err := tmtypes.MakeCommit(blockID, blockHeight, 1, voteSet, signers, timestamp) + require.NoError(chain.t, err) + + signedHeader := &tmproto.SignedHeader{ + Header: tmHeader.ToProto(), + Commit: commit.ToProto(), + } + + valSet, err := tmValSet.ToProto() + if err != nil { + panic(err) + } + var trustedVals *tmproto.ValidatorSet + if tmTrustedVals != nil { + trustedVals, err = tmTrustedVals.ToProto() + if err != nil { + panic(err) + } + } + + // The trusted fields may be nil. They may be filled before relaying messages to a client. + // The relayer is responsible for querying client and injecting appropriate trusted fields. + return &ibctmtypes.Header{ + SignedHeader: signedHeader, + ValidatorSet: valSet, + TrustedHeight: trustedHeight, + TrustedValidators: trustedVals, + } +} + +// MakeBlockID copied unimported test functions from tmtypes to use them here +func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.BlockID { + return tmtypes.BlockID{ + Hash: hash, + PartSetHeader: tmtypes.PartSetHeader{ + Total: partSetSize, + Hash: partSetHash, + }, + } +} + +// CreateSortedSignerArray takes two PrivValidators, and the corresponding Validator structs +// (including voting power). It returns a signer array of PrivValidators that matches the +// sorting of ValidatorSet. +// The sorting is first by .VotingPower (descending), with secondary index of .Address (ascending). +func CreateSortedSignerArray(altPrivVal, suitePrivVal tmtypes.PrivValidator, + altVal, suiteVal *tmtypes.Validator) []tmtypes.PrivValidator { + + switch { + case altVal.VotingPower > suiteVal.VotingPower: + return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} + case altVal.VotingPower < suiteVal.VotingPower: + return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} + default: + if bytes.Compare(altVal.Address, suiteVal.Address) == -1 { + return []tmtypes.PrivValidator{altPrivVal, suitePrivVal} + } + return []tmtypes.PrivValidator{suitePrivVal, altPrivVal} + } +} + +// ConnectionOpenInit will construct and execute a MsgConnectionOpenInit. +func (chain *TestChain) ConnectionOpenInit( + counterparty *TestChain, + connection, counterpartyConnection *TestConnection, +) error { + msg := connectiontypes.NewMsgConnectionOpenInit( + connection.ClientID, + connection.CounterpartyClientID, + counterparty.GetPrefix(), DefaultOpenInitVersion, DefaultDelayPeriod, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ConnectionOpenTry will construct and execute a MsgConnectionOpenTry. +func (chain *TestChain) ConnectionOpenTry( + counterparty *TestChain, + connection, counterpartyConnection *TestConnection, +) error { + counterpartyClient, proofClient := counterparty.QueryClientStateProof(counterpartyConnection.ClientID) + + connectionKey := host.ConnectionKey(counterpartyConnection.ID) + proofInit, proofHeight := counterparty.QueryProof(connectionKey) + + proofConsensus, consensusHeight := counterparty.QueryConsensusStateProof(counterpartyConnection.ClientID) + + msg := connectiontypes.NewMsgConnectionOpenTry( + "", connection.ClientID, // does not support handshake continuation + counterpartyConnection.ID, counterpartyConnection.ClientID, + counterpartyClient, counterparty.GetPrefix(), []*connectiontypes.Version{ConnectionVersion}, DefaultDelayPeriod, + proofInit, proofClient, proofConsensus, + proofHeight, consensusHeight, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ConnectionOpenAck will construct and execute a MsgConnectionOpenAck. +func (chain *TestChain) ConnectionOpenAck( + counterparty *TestChain, + connection, counterpartyConnection *TestConnection, +) error { + counterpartyClient, proofClient := counterparty.QueryClientStateProof(counterpartyConnection.ClientID) + + connectionKey := host.ConnectionKey(counterpartyConnection.ID) + proofTry, proofHeight := counterparty.QueryProof(connectionKey) + + proofConsensus, consensusHeight := counterparty.QueryConsensusStateProof(counterpartyConnection.ClientID) + + msg := connectiontypes.NewMsgConnectionOpenAck( + connection.ID, counterpartyConnection.ID, counterpartyClient, // testing doesn't use flexible selection + proofTry, proofClient, proofConsensus, + proofHeight, consensusHeight, + ConnectionVersion, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ConnectionOpenConfirm will construct and execute a MsgConnectionOpenConfirm. +func (chain *TestChain) ConnectionOpenConfirm( + counterparty *TestChain, + connection, counterpartyConnection *TestConnection, +) error { + connectionKey := host.ConnectionKey(counterpartyConnection.ID) + proof, height := counterparty.QueryProof(connectionKey) + + msg := connectiontypes.NewMsgConnectionOpenConfirm( + connection.ID, + proof, height, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// CreatePortCapability binds and claims a capability for the given portID if it does not +// already exist. This function will fail testing on any resulting error. +// NOTE: only creation of a capbility for a transfer or mock port is supported +// Other applications must bind to the port in InitGenesis or modify this code. +func (chain *TestChain) CreatePortCapability(portID string) { + // check if the portId is already binded, if not bind it + _, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID)) + if !ok { + // create capability using the IBC capability keeper + cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), host.PortPath(portID)) + require.NoError(chain.t, err) + + switch portID { + case MockPort: + // claim capability using the mock capability keeper + err = chain.App.ScopedIBCMockKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID)) + require.NoError(chain.t, err) + case TransferPort: + // claim capability using the transfer capability keeper + err = chain.App.ScopedTransferKeeper.ClaimCapability(chain.GetContext(), cap, host.PortPath(portID)) + require.NoError(chain.t, err) + default: + panic(fmt.Sprintf("unsupported ibc testing package port ID %s", portID)) + } + } + + chain.App.Commit() + + chain.NextBlock() +} + +// GetPortCapability returns the port capability for the given portID. The capability must +// exist, otherwise testing will fail. +func (chain *TestChain) GetPortCapability(portID string) *capabilitytypes.Capability { + cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.PortPath(portID)) + require.True(chain.t, ok) + + return cap +} + +// CreateChannelCapability binds and claims a capability for the given portID and channelID +// if it does not already exist. This function will fail testing on any resulting error. +func (chain *TestChain) CreateChannelCapability(portID, channelID string) { + capName := host.ChannelCapabilityPath(portID, channelID) + // check if the portId is already binded, if not bind it + _, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), capName) + if !ok { + cap, err := chain.App.ScopedIBCKeeper.NewCapability(chain.GetContext(), capName) + require.NoError(chain.t, err) + err = chain.App.ScopedTransferKeeper.ClaimCapability(chain.GetContext(), cap, capName) + require.NoError(chain.t, err) + } + + chain.App.Commit() + + chain.NextBlock() +} + +// GetChannelCapability returns the channel capability for the given portID and channelID. +// The capability must exist, otherwise testing will fail. +func (chain *TestChain) GetChannelCapability(portID, channelID string) *capabilitytypes.Capability { + cap, ok := chain.App.ScopedIBCKeeper.GetCapability(chain.GetContext(), host.ChannelCapabilityPath(portID, channelID)) + require.True(chain.t, ok) + + return cap +} + +// ChanOpenInit will construct and execute a MsgChannelOpenInit. +func (chain *TestChain) ChanOpenInit( + ch, counterparty TestChannel, + order channeltypes.Order, + connectionID string, +) error { + msg := channeltypes.NewMsgChannelOpenInit( + ch.PortID, + ch.Version, order, []string{connectionID}, + counterparty.PortID, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ChanOpenTry will construct and execute a MsgChannelOpenTry. +func (chain *TestChain) ChanOpenTry( + counterparty *TestChain, + ch, counterpartyCh TestChannel, + order channeltypes.Order, + connectionID string, +) error { + proof, height := counterparty.QueryProof(host.ChannelKey(counterpartyCh.PortID, counterpartyCh.ID)) + + msg := channeltypes.NewMsgChannelOpenTry( + ch.PortID, "", // does not support handshake continuation + ch.Version, order, []string{connectionID}, + counterpartyCh.PortID, counterpartyCh.ID, counterpartyCh.Version, + proof, height, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ChanOpenAck will construct and execute a MsgChannelOpenAck. +func (chain *TestChain) ChanOpenAck( + counterparty *TestChain, + ch, counterpartyCh TestChannel, +) error { + proof, height := counterparty.QueryProof(host.ChannelKey(counterpartyCh.PortID, counterpartyCh.ID)) + + msg := channeltypes.NewMsgChannelOpenAck( + ch.PortID, ch.ID, + counterpartyCh.ID, counterpartyCh.Version, // testing doesn't use flexible selection + proof, height, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ChanOpenConfirm will construct and execute a MsgChannelOpenConfirm. +func (chain *TestChain) ChanOpenConfirm( + counterparty *TestChain, + ch, counterpartyCh TestChannel, +) error { + proof, height := counterparty.QueryProof(host.ChannelKey(counterpartyCh.PortID, counterpartyCh.ID)) + + msg := channeltypes.NewMsgChannelOpenConfirm( + ch.PortID, ch.ID, + proof, height, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// ChanCloseInit will construct and execute a MsgChannelCloseInit. +// +// NOTE: does not work with ibc-transfer module +func (chain *TestChain) ChanCloseInit( + counterparty *TestChain, + channel TestChannel, +) error { + msg := channeltypes.NewMsgChannelCloseInit( + channel.PortID, channel.ID, + chain.SenderAccount.GetAddress(), + ) + return chain.sendMsgs(msg) +} + +// GetPacketData returns a ibc-transfer marshalled packet to be used for +// callback testing. +func (chain *TestChain) GetPacketData(counterparty *TestChain) []byte { + packet := ibctransfertypes.FungibleTokenPacketData{ + Denom: TestCoin.Denom, + Amount: TestCoin.Amount.Uint64(), + Sender: chain.SenderAccount.GetAddress().String(), + Receiver: counterparty.SenderAccount.GetAddress().String(), + } + + return packet.GetBytes() +} + +// SendPacket simulates sending a packet through the channel keeper. No message needs to be +// passed since this call is made from a module. +func (chain *TestChain) SendPacket( + packet exported.PacketI, +) error { + channelCap := chain.GetChannelCapability(packet.GetSourcePort(), packet.GetSourceChannel()) + + // no need to send message, acting as a module + err := chain.App.IBCKeeper.ChannelKeeper.SendPacket(chain.GetContext(), channelCap, packet) + if err != nil { + return err + } + + // commit changes + chain.App.Commit() + chain.NextBlock() + + return nil +} + +// WriteAcknowledgement simulates writing an acknowledgement to the chain. +func (chain *TestChain) WriteAcknowledgement( + packet exported.PacketI, +) error { + channelCap := chain.GetChannelCapability(packet.GetDestPort(), packet.GetDestChannel()) + + // no need to send message, acting as a handler + err := chain.App.IBCKeeper.ChannelKeeper.WriteAcknowledgement(chain.GetContext(), channelCap, packet, TestHash) + if err != nil { + return err + } + + // commit changes + chain.App.Commit() + chain.NextBlock() + + return nil +} diff --git a/x/ibc/testing/chain_test.go b/x/ibc/testing/chain_test.go new file mode 100644 index 000000000000..361a9c4c15af --- /dev/null +++ b/x/ibc/testing/chain_test.go @@ -0,0 +1,47 @@ +package ibctesting_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmtypes "github.com/tendermint/tendermint/types" + + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" + "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +func TestCreateSortedSignerArray(t *testing.T) { + privVal1 := mock.NewPV() + pubKey1, err := privVal1.GetPubKey() + require.NoError(t, err) + + privVal2 := mock.NewPV() + pubKey2, err := privVal2.GetPubKey() + require.NoError(t, err) + + validator1 := tmtypes.NewValidator(pubKey1, 1) + validator2 := tmtypes.NewValidator(pubKey2, 2) + + expected := []tmtypes.PrivValidator{privVal2, privVal1} + + actual := ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) + require.Equal(t, expected, actual) + + // swap order + actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) + require.Equal(t, expected, actual) + + // smaller address + validator1.Address = []byte{1} + validator2.Address = []byte{2} + validator2.VotingPower = 1 + + expected = []tmtypes.PrivValidator{privVal1, privVal2} + + actual = ibctesting.CreateSortedSignerArray(privVal1, privVal2, validator1, validator2) + require.Equal(t, expected, actual) + + // swap order + actual = ibctesting.CreateSortedSignerArray(privVal2, privVal1, validator2, validator1) + require.Equal(t, expected, actual) +} diff --git a/x/ibc/testing/coordinator.go b/x/ibc/testing/coordinator.go new file mode 100644 index 000000000000..ade28b4df342 --- /dev/null +++ b/x/ibc/testing/coordinator.go @@ -0,0 +1,700 @@ +package ibctesting + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var ( + ChainIDPrefix = "testchain" + globalStartTime = time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC) + TimeIncrement = time.Second * 5 +) + +// Coordinator is a testing struct which contains N TestChain's. It handles keeping all chains +// in sync with regards to time. +type Coordinator struct { + t *testing.T + + Chains map[string]*TestChain +} + +// NewCoordinator initializes Coordinator with N TestChain's +func NewCoordinator(t *testing.T, n int) *Coordinator { + chains := make(map[string]*TestChain) + + for i := 0; i < n; i++ { + chainID := GetChainID(i) + chains[chainID] = NewTestChain(t, chainID) + } + return &Coordinator{ + t: t, + Chains: chains, + } +} + +// Setup constructs a TM client, connection, and channel on both chains provided. It will +// fail if any error occurs. The clientID's, TestConnections, and TestChannels are returned +// for both chains. The channels created are connected to the ibc-transfer application. +func (coord *Coordinator) Setup( + chainA, chainB *TestChain, order channeltypes.Order, +) (string, string, *TestConnection, *TestConnection, TestChannel, TestChannel) { + clientA, clientB, connA, connB := coord.SetupClientConnections(chainA, chainB, exported.Tendermint) + + // channels can also be referenced through the returned connections + channelA, channelB := coord.CreateMockChannels(chainA, chainB, connA, connB, order) + + return clientA, clientB, connA, connB, channelA, channelB +} + +// SetupClients is a helper function to create clients on both chains. It assumes the +// caller does not anticipate any errors. +func (coord *Coordinator) SetupClients( + chainA, chainB *TestChain, + clientType string, +) (string, string) { + + clientA, err := coord.CreateClient(chainA, chainB, clientType) + require.NoError(coord.t, err) + + clientB, err := coord.CreateClient(chainB, chainA, clientType) + require.NoError(coord.t, err) + + return clientA, clientB +} + +// SetupClientConnections is a helper function to create clients and the appropriate +// connections on both the source and counterparty chain. It assumes the caller does not +// anticipate any errors. +func (coord *Coordinator) SetupClientConnections( + chainA, chainB *TestChain, + clientType string, +) (string, string, *TestConnection, *TestConnection) { + + clientA, clientB := coord.SetupClients(chainA, chainB, clientType) + + connA, connB := coord.CreateConnection(chainA, chainB, clientA, clientB) + + return clientA, clientB, connA, connB +} + +// CreateClient creates a counterparty client on the source chain and returns the clientID. +func (coord *Coordinator) CreateClient( + source, counterparty *TestChain, + clientType string, +) (clientID string, err error) { + coord.CommitBlock(source, counterparty) + + clientID = source.NewClientID(clientType) + + switch clientType { + case exported.Tendermint: + err = source.CreateTMClient(counterparty, clientID) + + default: + err = fmt.Errorf("client type %s is not supported", clientType) + } + + if err != nil { + return "", err + } + + coord.IncrementTime() + + return clientID, nil +} + +// UpdateClient updates a counterparty client on the source chain. +func (coord *Coordinator) UpdateClient( + source, counterparty *TestChain, + clientID string, + clientType string, +) (err error) { + coord.CommitBlock(source, counterparty) + + switch clientType { + case exported.Tendermint: + err = source.UpdateTMClient(counterparty, clientID) + + default: + err = fmt.Errorf("client type %s is not supported", clientType) + } + + if err != nil { + return err + } + + coord.IncrementTime() + + return nil +} + +// CreateConnection constructs and executes connection handshake messages in order to create +// OPEN channels on chainA and chainB. The connection information of for chainA and chainB +// are returned within a TestConnection struct. The function expects the connections to be +// successfully opened otherwise testing will fail. +func (coord *Coordinator) CreateConnection( + chainA, chainB *TestChain, + clientA, clientB string, +) (*TestConnection, *TestConnection) { + + connA, connB, err := coord.ConnOpenInit(chainA, chainB, clientA, clientB) + require.NoError(coord.t, err) + + err = coord.ConnOpenTry(chainB, chainA, connB, connA) + require.NoError(coord.t, err) + + err = coord.ConnOpenAck(chainA, chainB, connA, connB) + require.NoError(coord.t, err) + + err = coord.ConnOpenConfirm(chainB, chainA, connB, connA) + require.NoError(coord.t, err) + + return connA, connB +} + +// CreateMockChannels constructs and executes channel handshake messages to create OPEN +// channels that use a mock application module that returns nil on all callbacks. This +// function is expects the channels to be successfully opened otherwise testing will +// fail. +func (coord *Coordinator) CreateMockChannels( + chainA, chainB *TestChain, + connA, connB *TestConnection, + order channeltypes.Order, +) (TestChannel, TestChannel) { + return coord.CreateChannel(chainA, chainB, connA, connB, MockPort, MockPort, order) +} + +// CreateTransferChannels constructs and executes channel handshake messages to create OPEN +// ibc-transfer channels on chainA and chainB. The function expects the channels to be +// successfully opened otherwise testing will fail. +func (coord *Coordinator) CreateTransferChannels( + chainA, chainB *TestChain, + connA, connB *TestConnection, + order channeltypes.Order, +) (TestChannel, TestChannel) { + return coord.CreateChannel(chainA, chainB, connA, connB, TransferPort, TransferPort, order) +} + +// CreateChannel constructs and executes channel handshake messages in order to create +// OPEN channels on chainA and chainB. The function expects the channels to be successfully +// opened otherwise testing will fail. +func (coord *Coordinator) CreateChannel( + chainA, chainB *TestChain, + connA, connB *TestConnection, + sourcePortID, counterpartyPortID string, + order channeltypes.Order, +) (TestChannel, TestChannel) { + + channelA, channelB, err := coord.ChanOpenInit(chainA, chainB, connA, connB, sourcePortID, counterpartyPortID, order) + require.NoError(coord.t, err) + + err = coord.ChanOpenTry(chainB, chainA, channelB, channelA, connB, order) + require.NoError(coord.t, err) + + err = coord.ChanOpenAck(chainA, chainB, channelA, channelB) + require.NoError(coord.t, err) + + err = coord.ChanOpenConfirm(chainB, chainA, channelB, channelA) + require.NoError(coord.t, err) + + return channelA, channelB +} + +// SendPacket sends a packet through the channel keeper on the source chain and updates the +// counterparty client for the source chain. +func (coord *Coordinator) SendPacket( + source, counterparty *TestChain, + packet exported.PacketI, + counterpartyClientID string, +) error { + if err := source.SendPacket(packet); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyClientID, exported.Tendermint, + ) +} + +// RecvPacket receives a channel packet on the counterparty chain and updates +// the client on the source chain representing the counterparty. +func (coord *Coordinator) RecvPacket( + source, counterparty *TestChain, + sourceClient string, + packet channeltypes.Packet, +) error { + // get proof of packet commitment on source + packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + proof, proofHeight := source.QueryProof(packetKey) + + // Increment time and commit block so that 5 second delay period passes between send and receive + coord.IncrementTime() + coord.CommitBlock(source, counterparty) + + recvMsg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, counterparty.SenderAccount.GetAddress()) + + // receive on counterparty and update source client + return coord.SendMsgs(counterparty, source, sourceClient, []sdk.Msg{recvMsg}) +} + +// WriteAcknowledgement writes an acknowledgement to the channel keeper on the source chain and updates the +// counterparty client for the source chain. +func (coord *Coordinator) WriteAcknowledgement( + source, counterparty *TestChain, + packet exported.PacketI, + counterpartyClientID string, +) error { + if err := source.WriteAcknowledgement(packet); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyClientID, exported.Tendermint, + ) +} + +// AcknowledgePacket acknowledges on the source chain the packet received on +// the counterparty chain and updates the client on the counterparty representing +// the source chain. +// TODO: add a query for the acknowledgement by events +// - https://github.com/cosmos/cosmos-sdk/issues/6509 +func (coord *Coordinator) AcknowledgePacket( + source, counterparty *TestChain, + counterpartyClient string, + packet channeltypes.Packet, ack []byte, +) error { + // get proof of acknowledgement on counterparty + packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) + proof, proofHeight := counterparty.QueryProof(packetKey) + + // Increment time and commit block so that 5 second delay period passes between send and receive + coord.IncrementTime() + coord.CommitBlock(source, counterparty) + + ackMsg := channeltypes.NewMsgAcknowledgement(packet, ack, proof, proofHeight, source.SenderAccount.GetAddress()) + return coord.SendMsgs(source, counterparty, counterpartyClient, []sdk.Msg{ackMsg}) +} + +// RelayPacket receives a channel packet on counterparty, queries the ack +// and acknowledges the packet on source. The clients are updated as needed. +func (coord *Coordinator) RelayPacket( + source, counterparty *TestChain, + sourceClient, counterpartyClient string, + packet channeltypes.Packet, ack []byte, +) error { + // Increment time and commit block so that 5 second delay period passes between send and receive + coord.IncrementTime() + coord.CommitBlock(counterparty) + + if err := coord.RecvPacket(source, counterparty, sourceClient, packet); err != nil { + return err + } + + // Increment time and commit block so that 5 second delay period passes between send and receive + coord.IncrementTime() + coord.CommitBlock(source) + + return coord.AcknowledgePacket(source, counterparty, counterpartyClient, packet, ack) +} + +// IncrementTime iterates through all the TestChain's and increments their current header time +// by 5 seconds. +// +// CONTRACT: this function must be called after every commit on any TestChain. +func (coord *Coordinator) IncrementTime() { + for _, chain := range coord.Chains { + chain.CurrentHeader.Time = chain.CurrentHeader.Time.Add(TimeIncrement) + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + } +} + +// IncrementTimeBy iterates through all the TestChain's and increments their current header time +// by specified time. +func (coord *Coordinator) IncrementTimeBy(increment time.Duration) { + for _, chain := range coord.Chains { + chain.CurrentHeader.Time = chain.CurrentHeader.Time.Add(increment) + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + } +} + +// SendMsg delivers a single provided message to the chain. The counterparty +// client is update with the new source consensus state. +func (coord *Coordinator) SendMsg(source, counterparty *TestChain, counterpartyClientID string, msg sdk.Msg) error { + return coord.SendMsgs(source, counterparty, counterpartyClientID, []sdk.Msg{msg}) +} + +// SendMsgs delivers the provided messages to the chain. The counterparty +// client is updated with the new source consensus state. +func (coord *Coordinator) SendMsgs(source, counterparty *TestChain, counterpartyClientID string, msgs []sdk.Msg) error { + if err := source.sendMsgs(msgs...); err != nil { + return err + } + + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyClientID, exported.Tendermint, + ) +} + +// GetChain returns the TestChain using the given chainID and returns an error if it does +// not exist. +func (coord *Coordinator) GetChain(chainID string) *TestChain { + chain, found := coord.Chains[chainID] + require.True(coord.t, found, fmt.Sprintf("%s chain does not exist", chainID)) + return chain +} + +// GetChainID returns the chainID used for the provided index. +func GetChainID(index int) string { + return ChainIDPrefix + strconv.Itoa(index) +} + +// CommitBlock commits a block on the provided indexes and then increments the global time. +// +// CONTRACT: the passed in list of indexes must not contain duplicates +func (coord *Coordinator) CommitBlock(chains ...*TestChain) { + for _, chain := range chains { + chain.App.Commit() + chain.NextBlock() + } + coord.IncrementTime() +} + +// CommitNBlocks commits n blocks to state and updates the block height by 1 for each commit. +func (coord *Coordinator) CommitNBlocks(chain *TestChain, n uint64) { + for i := uint64(0); i < n; i++ { + chain.App.BeginBlock(abci.RequestBeginBlock{Header: chain.CurrentHeader}) + chain.App.Commit() + chain.NextBlock() + coord.IncrementTime() + } +} + +// ConnOpenInit initializes a connection on the source chain with the state INIT +// using the OpenInit handshake call. +// +// NOTE: The counterparty testing connection will be created even if it is not created in the +// application state. +func (coord *Coordinator) ConnOpenInit( + source, counterparty *TestChain, + clientID, counterpartyClientID string, +) (*TestConnection, *TestConnection, error) { + sourceConnection := source.AddTestConnection(clientID, counterpartyClientID) + counterpartyConnection := counterparty.AddTestConnection(counterpartyClientID, clientID) + + // initialize connection on source + if err := source.ConnectionOpenInit(counterparty, sourceConnection, counterpartyConnection); err != nil { + return sourceConnection, counterpartyConnection, err + } + coord.IncrementTime() + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyClientID, exported.Tendermint, + ); err != nil { + return sourceConnection, counterpartyConnection, err + } + + return sourceConnection, counterpartyConnection, nil +} + +// ConnOpenInitOnBothChains initializes a connection on the source chain with the state INIT +// using the OpenInit handshake call. +func (coord *Coordinator) ConnOpenInitOnBothChains( + source, counterparty *TestChain, + clientID, counterpartyClientID string, +) (*TestConnection, *TestConnection, error) { + sourceConnection := source.AddTestConnection(clientID, counterpartyClientID) + counterpartyConnection := counterparty.AddTestConnection(counterpartyClientID, clientID) + + // initialize connection on source + if err := source.ConnectionOpenInit(counterparty, sourceConnection, counterpartyConnection); err != nil { + return sourceConnection, counterpartyConnection, err + } + coord.IncrementTime() + + // initialize connection on counterparty + if err := counterparty.ConnectionOpenInit(source, counterpartyConnection, sourceConnection); err != nil { + return sourceConnection, counterpartyConnection, err + } + coord.IncrementTime() + + // update counterparty client on source connection + if err := coord.UpdateClient( + source, counterparty, + clientID, exported.Tendermint, + ); err != nil { + return sourceConnection, counterpartyConnection, err + } + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyClientID, exported.Tendermint, + ); err != nil { + return sourceConnection, counterpartyConnection, err + } + + return sourceConnection, counterpartyConnection, nil +} + +// ConnOpenTry initializes a connection on the source chain with the state TRYOPEN +// using the OpenTry handshake call. +func (coord *Coordinator) ConnOpenTry( + source, counterparty *TestChain, + sourceConnection, counterpartyConnection *TestConnection, +) error { + // initialize TRYOPEN connection on source + if err := source.ConnectionOpenTry(counterparty, sourceConnection, counterpartyConnection); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, exported.Tendermint, + ) +} + +// ConnOpenAck initializes a connection on the source chain with the state OPEN +// using the OpenAck handshake call. +func (coord *Coordinator) ConnOpenAck( + source, counterparty *TestChain, + sourceConnection, counterpartyConnection *TestConnection, +) error { + // set OPEN connection on source using OpenAck + if err := source.ConnectionOpenAck(counterparty, sourceConnection, counterpartyConnection); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, exported.Tendermint, + ) +} + +// ConnOpenConfirm initializes a connection on the source chain with the state OPEN +// using the OpenConfirm handshake call. +func (coord *Coordinator) ConnOpenConfirm( + source, counterparty *TestChain, + sourceConnection, counterpartyConnection *TestConnection, +) error { + if err := source.ConnectionOpenConfirm(counterparty, sourceConnection, counterpartyConnection); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, exported.Tendermint, + ) +} + +// ChanOpenInit initializes a channel on the source chain with the state INIT +// using the OpenInit handshake call. +// +// NOTE: The counterparty testing channel will be created even if it is not created in the +// application state. +func (coord *Coordinator) ChanOpenInit( + source, counterparty *TestChain, + connection, counterpartyConnection *TestConnection, + sourcePortID, counterpartyPortID string, + order channeltypes.Order, +) (TestChannel, TestChannel, error) { + sourceChannel := source.AddTestChannel(connection, sourcePortID) + counterpartyChannel := counterparty.AddTestChannel(counterpartyConnection, counterpartyPortID) + + // NOTE: only creation of a capability for a transfer or mock port is supported + // Other applications must bind to the port in InitGenesis or modify this code. + source.CreatePortCapability(sourceChannel.PortID) + coord.IncrementTime() + + // initialize channel on source + if err := source.ChanOpenInit(sourceChannel, counterpartyChannel, order, connection.ID); err != nil { + return sourceChannel, counterpartyChannel, err + } + coord.IncrementTime() + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, exported.Tendermint, + ); err != nil { + return sourceChannel, counterpartyChannel, err + } + + return sourceChannel, counterpartyChannel, nil +} + +// ChanOpenInitOnBothChains initializes a channel on the source chain and counterparty chain +// with the state INIT using the OpenInit handshake call. +func (coord *Coordinator) ChanOpenInitOnBothChains( + source, counterparty *TestChain, + connection, counterpartyConnection *TestConnection, + sourcePortID, counterpartyPortID string, + order channeltypes.Order, +) (TestChannel, TestChannel, error) { + sourceChannel := source.AddTestChannel(connection, sourcePortID) + counterpartyChannel := counterparty.AddTestChannel(counterpartyConnection, counterpartyPortID) + + // NOTE: only creation of a capability for a transfer or mock port is supported + // Other applications must bind to the port in InitGenesis or modify this code. + source.CreatePortCapability(sourceChannel.PortID) + counterparty.CreatePortCapability(counterpartyChannel.PortID) + coord.IncrementTime() + + // initialize channel on source + if err := source.ChanOpenInit(sourceChannel, counterpartyChannel, order, connection.ID); err != nil { + return sourceChannel, counterpartyChannel, err + } + coord.IncrementTime() + + // initialize channel on counterparty + if err := counterparty.ChanOpenInit(counterpartyChannel, sourceChannel, order, counterpartyConnection.ID); err != nil { + return sourceChannel, counterpartyChannel, err + } + coord.IncrementTime() + + // update counterparty client on source connection + if err := coord.UpdateClient( + source, counterparty, + connection.ClientID, exported.Tendermint, + ); err != nil { + return sourceChannel, counterpartyChannel, err + } + + // update source client on counterparty connection + if err := coord.UpdateClient( + counterparty, source, + counterpartyConnection.ClientID, exported.Tendermint, + ); err != nil { + return sourceChannel, counterpartyChannel, err + } + + return sourceChannel, counterpartyChannel, nil +} + +// ChanOpenTry initializes a channel on the source chain with the state TRYOPEN +// using the OpenTry handshake call. +func (coord *Coordinator) ChanOpenTry( + source, counterparty *TestChain, + sourceChannel, counterpartyChannel TestChannel, + connection *TestConnection, + order channeltypes.Order, +) error { + + // initialize channel on source + if err := source.ChanOpenTry(counterparty, sourceChannel, counterpartyChannel, order, connection.ID); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + connection.CounterpartyClientID, exported.Tendermint, + ) +} + +// ChanOpenAck initializes a channel on the source chain with the state OPEN +// using the OpenAck handshake call. +func (coord *Coordinator) ChanOpenAck( + source, counterparty *TestChain, + sourceChannel, counterpartyChannel TestChannel, +) error { + + if err := source.ChanOpenAck(counterparty, sourceChannel, counterpartyChannel); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + sourceChannel.CounterpartyClientID, exported.Tendermint, + ) +} + +// ChanOpenConfirm initializes a channel on the source chain with the state OPEN +// using the OpenConfirm handshake call. +func (coord *Coordinator) ChanOpenConfirm( + source, counterparty *TestChain, + sourceChannel, counterpartyChannel TestChannel, +) error { + + if err := source.ChanOpenConfirm(counterparty, sourceChannel, counterpartyChannel); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + sourceChannel.CounterpartyClientID, exported.Tendermint, + ) +} + +// ChanCloseInit closes a channel on the source chain resulting in the channels state +// being set to CLOSED. +// +// NOTE: does not work with ibc-transfer module +func (coord *Coordinator) ChanCloseInit( + source, counterparty *TestChain, + channel TestChannel, +) error { + + if err := source.ChanCloseInit(counterparty, channel); err != nil { + return err + } + coord.IncrementTime() + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + channel.CounterpartyClientID, exported.Tendermint, + ) +} + +// SetChannelClosed sets a channel state to CLOSED. +func (coord *Coordinator) SetChannelClosed( + source, counterparty *TestChain, + testChannel TestChannel, +) error { + channel := source.GetChannel(testChannel) + + channel.State = channeltypes.CLOSED + source.App.IBCKeeper.ChannelKeeper.SetChannel(source.GetContext(), testChannel.PortID, testChannel.ID, channel) + + coord.CommitBlock(source) + + // update source client on counterparty connection + return coord.UpdateClient( + counterparty, source, + testChannel.CounterpartyClientID, exported.Tendermint, + ) +} diff --git a/x/ibc/testing/mock/README.md b/x/ibc/testing/mock/README.md new file mode 100644 index 000000000000..5da403f9c34f --- /dev/null +++ b/x/ibc/testing/mock/README.md @@ -0,0 +1,6 @@ +This package is only intended to be used for testing core IBC. In order to maintain secure +testing, we need to do message passing and execution which requires connecting an IBC application +module that fulfills all the callbacks. We cannot connect to ibc-transfer which does not support +all channel types so instead we create a mock application module which does nothing. It simply +return nil in all cases so no error ever occurs. It is intended to be as minimal and lightweight +as possible and should never import simapp. diff --git a/x/ibc/testing/mock/doc.go b/x/ibc/testing/mock/doc.go new file mode 100644 index 000000000000..eaaa42b2ab1a --- /dev/null +++ b/x/ibc/testing/mock/doc.go @@ -0,0 +1,9 @@ +/* +This package is only intended to be used for testing core IBC. In order to maintain secure +testing, we need to do message passing and execution which requires connecting an IBC application +module that fulfills all the callbacks. We cannot connect to ibc-transfer which does not support +all channel types so instead we create a mock application module which does nothing. It simply +return nil in all cases so no error ever occurs. It is intended to be as minimal and lightweight +as possible and should never import simapp. +*/ +package mock diff --git a/x/ibc/testing/mock/mock.go b/x/ibc/testing/mock/mock.go new file mode 100644 index 000000000000..663497aa05ca --- /dev/null +++ b/x/ibc/testing/mock/mock.go @@ -0,0 +1,188 @@ +package mock + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" +) + +const ( + ModuleName = "mock" +) + +var ( + MockAcknowledgement = []byte("mock acknowledgement") + MockCommitment = []byte("mock packet commitment") +) + +// AppModuleBasic is the mock AppModuleBasic. +type AppModuleBasic struct{} + +// Name implements AppModuleBasic interface. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterLegacyAminoCodec implements AppModuleBasic interface. +func (AppModuleBasic) RegisterLegacyAminoCodec(*codec.LegacyAmino) {} + +// RegisterInterfaces implements AppModuleBasic interface. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {} + +// DefaultGenesis implements AppModuleBasic interface. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return nil +} + +// ValidateGenesis implements the AppModuleBasic interface. +func (AppModuleBasic) ValidateGenesis(codec.JSONMarshaler, client.TxEncodingConfig, json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes implements AppModuleBasic interface. +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) {} + +// RegisterGRPCGatewayRoutes implements AppModuleBasic interface. +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) {} + +// GetTxCmd implements AppModuleBasic interface. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +// GetQueryCmd implements AppModuleBasic interface. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil +} + +// AppModule represents the AppModule for the mock module. +type AppModule struct { + AppModuleBasic + scopedKeeper capabilitykeeper.ScopedKeeper +} + +// NewAppModule returns a mock AppModule instance. +func NewAppModule(sk capabilitykeeper.ScopedKeeper) AppModule { + return AppModule{ + scopedKeeper: sk, + } +} + +// RegisterInvariants implements the AppModule interface. +func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// Route implements the AppModule interface. +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(ModuleName, nil) +} + +// QuerierRoute implements the AppModule interface. +func (AppModule) QuerierRoute() string { + return "" +} + +// LegacyQuerierHandler implements the AppModule interface. +func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices implements the AppModule interface. +func (am AppModule) RegisterServices(module.Configurator) {} + +// InitGenesis implements the AppModule interface. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ExportGenesis implements the AppModule interface. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return nil +} + +// BeginBlock implements the AppModule interface +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock implements the AppModule interface +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +//____________________________________________________________________________ + +// OnChanOpenInit implements the IBCModule interface. +func (am AppModule) OnChanOpenInit( + ctx sdk.Context, _ channeltypes.Order, _ []string, portID string, + channelID string, chanCap *capabilitytypes.Capability, _ channeltypes.Counterparty, _ string, +) error { + // Claim channel capability passed back by IBC module + if err := am.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + + return nil +} + +// OnChanOpenTry implements the IBCModule interface. +func (am AppModule) OnChanOpenTry( + ctx sdk.Context, _ channeltypes.Order, _ []string, portID string, + channelID string, chanCap *capabilitytypes.Capability, _ channeltypes.Counterparty, _, _ string, +) error { + // Claim channel capability passed back by IBC module + if err := am.scopedKeeper.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil { + return err + } + + return nil +} + +// OnChanOpenAck implements the IBCModule interface. +func (am AppModule) OnChanOpenAck(sdk.Context, string, string, string) error { + return nil +} + +// OnChanOpenConfirm implements the IBCModule interface. +func (am AppModule) OnChanOpenConfirm(sdk.Context, string, string) error { + return nil +} + +// OnChanCloseInit implements the IBCModule interface. +func (am AppModule) OnChanCloseInit(sdk.Context, string, string) error { + return nil +} + +// OnChanCloseConfirm implements the IBCModule interface. +func (am AppModule) OnChanCloseConfirm(sdk.Context, string, string) error { + return nil +} + +// OnRecvPacket implements the IBCModule interface. +func (am AppModule) OnRecvPacket(sdk.Context, channeltypes.Packet) (*sdk.Result, []byte, error) { + return nil, MockAcknowledgement, nil +} + +// OnAcknowledgementPacket implements the IBCModule interface. +func (am AppModule) OnAcknowledgementPacket(sdk.Context, channeltypes.Packet, []byte) (*sdk.Result, error) { + return nil, nil +} + +// OnTimeoutPacket implements the IBCModule interface. +func (am AppModule) OnTimeoutPacket(sdk.Context, channeltypes.Packet) (*sdk.Result, error) { + return nil, nil +} diff --git a/x/ibc/testing/mock/privval.go b/x/ibc/testing/mock/privval.go new file mode 100644 index 000000000000..fe46659b3df9 --- /dev/null +++ b/x/ibc/testing/mock/privval.go @@ -0,0 +1,50 @@ +package mock + +import ( + "github.com/tendermint/tendermint/crypto" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +var _ tmtypes.PrivValidator = PV{} + +// MockPV implements PrivValidator without any safety or persistence. +// Only use it for testing. +type PV struct { + PrivKey cryptotypes.PrivKey +} + +func NewPV() PV { + return PV{ed25519.GenPrivKey()} +} + +// GetPubKey implements PrivValidator interface +func (pv PV) GetPubKey() (crypto.PubKey, error) { + return cryptocodec.ToTmPubKeyInterface(pv.PrivKey.PubKey()) +} + +// SignVote implements PrivValidator interface +func (pv PV) SignVote(chainID string, vote *tmproto.Vote) error { + signBytes := tmtypes.VoteSignBytes(chainID, vote) + sig, err := pv.PrivKey.Sign(signBytes) + if err != nil { + return err + } + vote.Signature = sig + return nil +} + +// SignProposal implements PrivValidator interface +func (pv PV) SignProposal(chainID string, proposal *tmproto.Proposal) error { + signBytes := tmtypes.ProposalSignBytes(chainID, proposal) + sig, err := pv.PrivKey.Sign(signBytes) + if err != nil { + return err + } + proposal.Signature = sig + return nil +} diff --git a/x/ibc/testing/mock/privval_test.go b/x/ibc/testing/mock/privval_test.go new file mode 100644 index 000000000000..b9f0487a36d5 --- /dev/null +++ b/x/ibc/testing/mock/privval_test.go @@ -0,0 +1,44 @@ +package mock_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" +) + +const chainID = "testChain" + +func TestGetPubKey(t *testing.T) { + pv := mock.NewPV() + pk, err := pv.GetPubKey() + require.NoError(t, err) + require.Equal(t, "ed25519", pk.Type()) +} + +func TestSignVote(t *testing.T) { + pv := mock.NewPV() + pk, _ := pv.GetPubKey() + + vote := &tmproto.Vote{Height: 2} + pv.SignVote(chainID, vote) + + msg := tmtypes.VoteSignBytes(chainID, vote) + ok := pk.VerifySignature(msg, vote.Signature) + require.True(t, ok) +} + +func TestSignProposal(t *testing.T) { + pv := mock.NewPV() + pk, _ := pv.GetPubKey() + + proposal := &tmproto.Proposal{Round: 2} + pv.SignProposal(chainID, proposal) + + msg := tmtypes.ProposalSignBytes(chainID, proposal) + ok := pk.VerifySignature(msg, proposal.Signature) + require.True(t, ok) +} diff --git a/x/ibc/testing/solomachine.go b/x/ibc/testing/solomachine.go new file mode 100644 index 000000000000..bee63785978b --- /dev/null +++ b/x/ibc/testing/solomachine.go @@ -0,0 +1,321 @@ +package ibctesting + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + host "github.com/cosmos/cosmos-sdk/x/ibc/core/24-host" + "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + solomachinetypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/types" +) + +var prefix = commitmenttypes.NewMerklePrefix([]byte("ibc")) + +// Solomachine is a testing helper used to simulate a counterparty +// solo machine client. +type Solomachine struct { + t *testing.T + + cdc codec.BinaryMarshaler + ClientID string + PrivateKeys []cryptotypes.PrivKey // keys used for signing + PublicKeys []cryptotypes.PubKey // keys used for generating solo machine pub key + PublicKey cryptotypes.PubKey // key used for verification + Sequence uint64 + Time uint64 + Diversifier string +} + +// NewSolomachine returns a new solomachine instance with an `nKeys` amount of +// generated private/public key pairs and a sequence starting at 1. If nKeys +// is greater than 1 then a multisig public key is used. +func NewSolomachine(t *testing.T, cdc codec.BinaryMarshaler, clientID, diversifier string, nKeys uint64) *Solomachine { + privKeys, pubKeys, pk := GenerateKeys(t, nKeys) + + return &Solomachine{ + t: t, + cdc: cdc, + ClientID: clientID, + PrivateKeys: privKeys, + PublicKeys: pubKeys, + PublicKey: pk, + Sequence: 1, + Time: 10, + Diversifier: diversifier, + } +} + +// GenerateKeys generates a new set of secp256k1 private keys and public keys. +// If the number of keys is greater than one then the public key returned represents +// a multisig public key. The private keys are used for signing, the public +// keys are used for generating the public key and the public key is used for +// solo machine verification. The usage of secp256k1 is entirely arbitrary. +// The key type can be swapped for any key type supported by the PublicKey +// interface, if needed. The same is true for the amino based Multisignature +// public key. +func GenerateKeys(t *testing.T, n uint64) ([]cryptotypes.PrivKey, []cryptotypes.PubKey, cryptotypes.PubKey) { + require.NotEqual(t, uint64(0), n, "generation of zero keys is not allowed") + + privKeys := make([]cryptotypes.PrivKey, n) + pubKeys := make([]cryptotypes.PubKey, n) + for i := uint64(0); i < n; i++ { + privKeys[i] = secp256k1.GenPrivKey() + pubKeys[i] = privKeys[i].PubKey() + } + + var pk cryptotypes.PubKey + if len(privKeys) > 1 { + // generate multi sig pk + pk = kmultisig.NewLegacyAminoPubKey(int(n), pubKeys) + } else { + pk = privKeys[0].PubKey() + } + + return privKeys, pubKeys, pk +} + +// ClientState returns a new solo machine ClientState instance. Default usage does not allow update +// after governance proposal +func (solo *Solomachine) ClientState() *solomachinetypes.ClientState { + return solomachinetypes.NewClientState(solo.Sequence, solo.ConsensusState(), false) +} + +// ConsensusState returns a new solo machine ConsensusState instance +func (solo *Solomachine) ConsensusState() *solomachinetypes.ConsensusState { + publicKey, err := codectypes.NewAnyWithValue(solo.PublicKey) + require.NoError(solo.t, err) + + return &solomachinetypes.ConsensusState{ + PublicKey: publicKey, + Diversifier: solo.Diversifier, + Timestamp: solo.Time, + } +} + +// GetHeight returns an exported.Height with Sequence as RevisionHeight +func (solo *Solomachine) GetHeight() exported.Height { + return clienttypes.NewHeight(0, solo.Sequence) +} + +// CreateHeader generates a new private/public key pair and creates the +// necessary signature to construct a valid solo machine header. +func (solo *Solomachine) CreateHeader() *solomachinetypes.Header { + // generate new private keys and signature for header + newPrivKeys, newPubKeys, newPubKey := GenerateKeys(solo.t, uint64(len(solo.PrivateKeys))) + + publicKey, err := codectypes.NewAnyWithValue(newPubKey) + require.NoError(solo.t, err) + + data := &solomachinetypes.HeaderData{ + NewPubKey: publicKey, + NewDiversifier: solo.Diversifier, + } + + dataBz, err := solo.cdc.MarshalBinaryBare(data) + require.NoError(solo.t, err) + + signBytes := &solomachinetypes.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + DataType: solomachinetypes.HEADER, + Data: dataBz, + } + + bz, err := solo.cdc.MarshalBinaryBare(signBytes) + require.NoError(solo.t, err) + + sig := solo.GenerateSignature(bz) + + header := &solomachinetypes.Header{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Signature: sig, + NewPublicKey: publicKey, + NewDiversifier: solo.Diversifier, + } + + // assumes successful header update + solo.Sequence++ + solo.PrivateKeys = newPrivKeys + solo.PublicKeys = newPubKeys + solo.PublicKey = newPubKey + + return header +} + +// CreateMisbehaviour constructs testing misbehaviour for the solo machine client +// by signing over two different data bytes at the same sequence. +func (solo *Solomachine) CreateMisbehaviour() *solomachinetypes.Misbehaviour { + path := solo.GetClientStatePath("counterparty") + dataOne, err := solomachinetypes.ClientStateDataBytes(solo.cdc, path, solo.ClientState()) + require.NoError(solo.t, err) + + path = solo.GetConsensusStatePath("counterparty", clienttypes.NewHeight(0, 1)) + dataTwo, err := solomachinetypes.ConsensusStateDataBytes(solo.cdc, path, solo.ConsensusState()) + require.NoError(solo.t, err) + + signBytes := &solomachinetypes.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + DataType: solomachinetypes.CLIENT, + Data: dataOne, + } + + bz, err := solo.cdc.MarshalBinaryBare(signBytes) + require.NoError(solo.t, err) + + sig := solo.GenerateSignature(bz) + signatureOne := solomachinetypes.SignatureAndData{ + Signature: sig, + DataType: solomachinetypes.CLIENT, + Data: dataOne, + Timestamp: solo.Time, + } + + // misbehaviour signaturess can have different timestamps + solo.Time++ + + signBytes = &solomachinetypes.SignBytes{ + Sequence: solo.Sequence, + Timestamp: solo.Time, + Diversifier: solo.Diversifier, + DataType: solomachinetypes.CONSENSUS, + Data: dataTwo, + } + + bz, err = solo.cdc.MarshalBinaryBare(signBytes) + require.NoError(solo.t, err) + + sig = solo.GenerateSignature(bz) + signatureTwo := solomachinetypes.SignatureAndData{ + Signature: sig, + DataType: solomachinetypes.CONSENSUS, + Data: dataTwo, + Timestamp: solo.Time, + } + + return &solomachinetypes.Misbehaviour{ + ClientId: solo.ClientID, + Sequence: solo.Sequence, + SignatureOne: &signatureOne, + SignatureTwo: &signatureTwo, + } +} + +// GenerateSignature uses the stored private keys to generate a signature +// over the sign bytes with each key. If the amount of keys is greater than +// 1 then a multisig data type is returned. +func (solo *Solomachine) GenerateSignature(signBytes []byte) []byte { + sigs := make([]signing.SignatureData, len(solo.PrivateKeys)) + for i, key := range solo.PrivateKeys { + sig, err := key.Sign(signBytes) + require.NoError(solo.t, err) + + sigs[i] = &signing.SingleSignatureData{ + Signature: sig, + } + } + + var sigData signing.SignatureData + if len(sigs) == 1 { + // single public key + sigData = sigs[0] + } else { + // generate multi signature data + multiSigData := multisig.NewMultisig(len(sigs)) + for i, sig := range sigs { + multisig.AddSignature(multiSigData, sig, i) + } + + sigData = multiSigData + } + + protoSigData := signing.SignatureDataToProto(sigData) + bz, err := solo.cdc.MarshalBinaryBare(protoSigData) + require.NoError(solo.t, err) + + return bz +} + +// GetClientStatePath returns the commitment path for the client state. +func (solo *Solomachine) GetClientStatePath(counterpartyClientIdentifier string) commitmenttypes.MerklePath { + path, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(host.FullClientStatePath(counterpartyClientIdentifier))) + require.NoError(solo.t, err) + + return path +} + +// GetConsensusStatePath returns the commitment path for the consensus state. +func (solo *Solomachine) GetConsensusStatePath(counterpartyClientIdentifier string, consensusHeight exported.Height) commitmenttypes.MerklePath { + path, err := commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(host.FullConsensusStatePath(counterpartyClientIdentifier, consensusHeight))) + require.NoError(solo.t, err) + + return path +} + +// GetConnectionStatePath returns the commitment path for the connection state. +func (solo *Solomachine) GetConnectionStatePath(connID string) commitmenttypes.MerklePath { + connectionPath := commitmenttypes.NewMerklePath(host.ConnectionPath(connID)) + path, err := commitmenttypes.ApplyPrefix(prefix, connectionPath) + require.NoError(solo.t, err) + + return path +} + +// GetChannelStatePath returns the commitment path for that channel state. +func (solo *Solomachine) GetChannelStatePath(portID, channelID string) commitmenttypes.MerklePath { + channelPath := commitmenttypes.NewMerklePath(host.ChannelPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, channelPath) + require.NoError(solo.t, err) + + return path +} + +// GetPacketCommitmentPath returns the commitment path for a packet commitment. +func (solo *Solomachine) GetPacketCommitmentPath(portID, channelID string) commitmenttypes.MerklePath { + commitmentPath := commitmenttypes.NewMerklePath(host.PacketCommitmentPath(portID, channelID, solo.Sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, commitmentPath) + require.NoError(solo.t, err) + + return path +} + +// GetPacketAcknowledgementPath returns the commitment path for a packet acknowledgement. +func (solo *Solomachine) GetPacketAcknowledgementPath(portID, channelID string) commitmenttypes.MerklePath { + ackPath := commitmenttypes.NewMerklePath(host.PacketAcknowledgementPath(portID, channelID, solo.Sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, ackPath) + require.NoError(solo.t, err) + + return path +} + +// GetPacketReceiptPath returns the commitment path for a packet receipt +// and an absent receipts. +func (solo *Solomachine) GetPacketReceiptPath(portID, channelID string) commitmenttypes.MerklePath { + receiptPath := commitmenttypes.NewMerklePath(host.PacketReceiptPath(portID, channelID, solo.Sequence)) + path, err := commitmenttypes.ApplyPrefix(prefix, receiptPath) + require.NoError(solo.t, err) + + return path +} + +// GetNextSequenceRecvPath returns the commitment path for the next sequence recv counter. +func (solo *Solomachine) GetNextSequenceRecvPath(portID, channelID string) commitmenttypes.MerklePath { + nextSequenceRecvPath := commitmenttypes.NewMerklePath(host.NextSequenceRecvPath(portID, channelID)) + path, err := commitmenttypes.ApplyPrefix(prefix, nextSequenceRecvPath) + require.NoError(solo.t, err) + + return path +} diff --git a/x/ibc/testing/types.go b/x/ibc/testing/types.go new file mode 100644 index 000000000000..16cda6216b19 --- /dev/null +++ b/x/ibc/testing/types.go @@ -0,0 +1,44 @@ +package ibctesting + +import ( + channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types" +) + +// TestConnection is a testing helper struct to keep track of the connectionID, source clientID, +// counterparty clientID, and the next channel version used in creating and interacting with a +// connection. +type TestConnection struct { + ID string + ClientID string + CounterpartyClientID string + NextChannelVersion string + Channels []TestChannel +} + +// FirstOrNextTestChannel returns the first test channel if it exists, otherwise it +// returns the next test channel to be created. This function is expected to be used +// when the caller does not know if the channel has or has not been created in app +// state, but would still like to refer to it to test existence or non-existence. +func (conn *TestConnection) FirstOrNextTestChannel(portID string) TestChannel { + if len(conn.Channels) > 0 { + return conn.Channels[0] + } + return TestChannel{ + PortID: portID, + ID: channeltypes.FormatChannelIdentifier(0), + ClientID: conn.ClientID, + CounterpartyClientID: conn.CounterpartyClientID, + Version: conn.NextChannelVersion, + } +} + +// TestChannel is a testing helper struct to keep track of the portID and channelID +// used in creating and interacting with a channel. The clientID and counterparty +// client ID are also tracked to cut down on querying and argument passing. +type TestChannel struct { + PortID string + ID string + ClientID string + CounterpartyClientID string + Version string +} diff --git a/x/mint/abci.go b/x/mint/abci.go index 48af12fc3f18..aa0f4e10628a 100644 --- a/x/mint/abci.go +++ b/x/mint/abci.go @@ -1,12 +1,18 @@ package mint import ( + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/x/mint/keeper" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) // BeginBlocker mints new tokens for the previous block. -func BeginBlocker(ctx sdk.Context, k Keeper) { +func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + // fetch stored minter & params minter := k.GetMinter(ctx) params := k.GetParams(ctx) @@ -32,6 +38,10 @@ func BeginBlocker(ctx sdk.Context, k Keeper) { panic(err) } + if mintedCoin.Amount.IsInt64() { + defer telemetry.ModuleSetGauge(types.ModuleName, float32(mintedCoin.Amount.Int64()), "minted_tokens") + } + ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeMint, diff --git a/x/mint/alias.go b/x/mint/alias.go deleted file mode 100644 index 593f4f8b105d..000000000000 --- a/x/mint/alias.go +++ /dev/null @@ -1,51 +0,0 @@ -package mint - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/mint/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" -) - -const ( - ModuleName = types.ModuleName - DefaultParamspace = types.DefaultParamspace - StoreKey = types.StoreKey - QuerierRoute = types.QuerierRoute - QueryParameters = types.QueryParameters - QueryInflation = types.QueryInflation - QueryAnnualProvisions = types.QueryAnnualProvisions -) - -var ( - // functions aliases - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - NewMinter = types.NewMinter - InitialMinter = types.InitialMinter - DefaultInitialMinter = types.DefaultInitialMinter - ValidateMinter = types.ValidateMinter - ParamKeyTable = types.ParamKeyTable - NewParams = types.NewParams - DefaultParams = types.DefaultParams - - // variable aliases - ModuleCdc = types.ModuleCdc - MinterKey = types.MinterKey - KeyMintDenom = types.KeyMintDenom - KeyInflationRate = types.KeyInflationRate - KeyInflationMax = types.KeyInflationMax - KeyInflationMin = types.KeyInflationMin - KeyGoalBonded = types.KeyGoalBonded - KeyBlocksPerYear = types.KeyBlocksPerYear -) - -type ( - Keeper = keeper.Keeper - GenesisState = types.GenesisState - Minter = types.Minter - Params = types.Params -) diff --git a/x/mint/client/cli/cli_test.go b/x/mint/client/cli/cli_test.go new file mode 100644 index 000000000000..55824d7abdf5 --- /dev/null +++ b/x/mint/client/cli/cli_test.go @@ -0,0 +1,164 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + testnet "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/client/cli" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg testnet.Config + network *testnet.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := testnet.DefaultConfig() + genesisState := cfg.GenesisState + cfg.NumValidators = 1 + + var mintData minttypes.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[minttypes.ModuleName], &mintData)) + + inflation := sdk.MustNewDecFromStr("1.0") + mintData.Minter.Inflation = inflation + + mintDataBz, err := cfg.Codec.MarshalJSON(&mintData) + s.Require().NoError(err) + genesisState[minttypes.ModuleName] = mintDataBz + cfg.GenesisState = genesisState + + s.cfg = cfg + s.network = testnet.New(s.T(), cfg) + + _, 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) TestGetCmdQueryParams() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"mint_denom":"stake","inflation_rate":"0.030000000000000000","blocks_per_year":"6311520"}`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `blocks_per_year: "6311520" +inflation_rate: "0.030000000000000000" +mint_denom: stake`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryInflation() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `0.030000000000000000`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `0.030000000000000000`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryInflation() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryAnnualProvisions() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `15000000.000000000000000000`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=1", flags.FlagHeight), fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `15000000.000000000000000000`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryAnnualProvisions() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/mint/client/cli/query.go b/x/mint/client/cli/query.go index ec37cde40cf6..cce0d7c12bbb 100644 --- a/x/mint/client/cli/query.go +++ b/x/mint/client/cli/query.go @@ -1,20 +1,18 @@ package cli import ( + "context" "fmt" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) // GetQueryCmd returns the cli query commands for the minting module. -func GetQueryCmd(cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { mintingQueryCmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the minting module", @@ -24,11 +22,9 @@ func GetQueryCmd(cdc *codec.Codec) *cobra.Command { } mintingQueryCmd.AddCommand( - flags.GetCommands( - GetCmdQueryParams(cdc), - GetCmdQueryInflation(cdc), - GetCmdQueryAnnualProvisions(cdc), - )..., + GetCmdQueryParams(), + GetCmdQueryInflation(), + GetCmdQueryAnnualProvisions(), ) return mintingQueryCmd @@ -36,78 +32,90 @@ func GetQueryCmd(cdc *codec.Codec) *cobra.Command { // GetCmdQueryParams implements a command to return the current minting // parameters. -func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ Use: "params", Short: "Query the current minting parameters", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters) - res, _, err := cliCtx.QueryWithData(route, nil) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) + + params := &types.QueryParamsRequest{} + res, err := queryClient.Params(context.Background(), params) - var params types.Params - if err := cdc.UnmarshalJSON(res, ¶ms); err != nil { + if err != nil { return err } - return cliCtx.PrintOutput(params) + return clientCtx.PrintProto(&res.Params) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryInflation implements a command to return the current minting // inflation value. -func GetCmdQueryInflation(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryInflation() *cobra.Command { + cmd := &cobra.Command{ Use: "inflation", Short: "Query the current minting inflation value", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryInflation) - res, _, err := cliCtx.QueryWithData(route, nil) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - var inflation sdk.Dec - if err := cdc.UnmarshalJSON(res, &inflation); err != nil { + params := &types.QueryInflationRequest{} + res, err := queryClient.Inflation(context.Background(), params) + + if err != nil { return err } - return cliCtx.PrintOutput(inflation) + return clientCtx.PrintString(fmt.Sprintf("%s\n", res.Inflation)) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryAnnualProvisions implements a command to return the current minting // annual provisions value. -func GetCmdQueryAnnualProvisions(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryAnnualProvisions() *cobra.Command { + cmd := &cobra.Command{ Use: "annual-provisions", Short: "Query the current minting annual provisions value", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAnnualProvisions) - res, _, err := cliCtx.QueryWithData(route, nil) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) + + params := &types.QueryAnnualProvisionsRequest{} + res, err := queryClient.AnnualProvisions(context.Background(), params) - var inflation sdk.Dec - if err := cdc.UnmarshalJSON(res, &inflation); err != nil { + if err != nil { return err } - return cliCtx.PrintOutput(inflation) + return clientCtx.PrintString(fmt.Sprintf("%s\n", res.AnnualProvisions)) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } diff --git a/x/mint/client/rest/grpc_query_test.go b/x/mint/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..0f7b0b215d71 --- /dev/null +++ b/x/mint/client/rest/grpc_query_test.go @@ -0,0 +1,109 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil/network" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type IntegrationTestSuite struct { + suite.Suite + cfg network.Config + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + cfg := network.DefaultConfig() + + genesisState := cfg.GenesisState + cfg.NumValidators = 1 + + var mintData minttypes.GenesisState + s.Require().NoError(cfg.Codec.UnmarshalJSON(genesisState[minttypes.ModuleName], &mintData)) + + inflation := sdk.MustNewDecFromStr("1.0") + mintData.Minter.Inflation = inflation + + mintDataBz, err := cfg.Codec.MarshalJSON(&mintData) + s.Require().NoError(err) + genesisState[minttypes.ModuleName] = mintDataBz + cfg.GenesisState = genesisState + + s.cfg = cfg + s.network = network.New(s.T(), cfg) + + _, 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) TestQueryGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + testCases := []struct { + name string + url string + headers map[string]string + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params", + fmt.Sprintf("%s/cosmos/mint/v1beta1/params", baseURL), + map[string]string{}, + &minttypes.QueryParamsResponse{}, + &minttypes.QueryParamsResponse{ + Params: minttypes.NewParams("stake", sdk.NewDecWithPrec(3, 2), (60 * 60 * 8766 / 5)), + }, + }, + { + "gRPC request inflation", + fmt.Sprintf("%s/cosmos/mint/v1beta1/inflation", baseURL), + map[string]string{}, + &minttypes.QueryInflationResponse{}, + &minttypes.QueryInflationResponse{ + Inflation: sdk.NewDecWithPrec(3, 2), + }, + }, + { + "gRPC request annual provisions", + fmt.Sprintf("%s/cosmos/mint/v1beta1/annual_provisions", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + &minttypes.QueryAnnualProvisionsResponse{}, + &minttypes.QueryAnnualProvisionsResponse{ + AnnualProvisions: sdk.NewDec(15000000), + }, + }, + } + for _, tc := range testCases { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Run(tc.name, func() { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/mint/client/rest/query.go b/x/mint/client/rest/query.go index f6e79cc31dcc..1dccd194c1ad 100644 --- a/x/mint/client/rest/query.go +++ b/x/mint/client/rest/query.go @@ -6,84 +6,81 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { r.HandleFunc( "/minting/parameters", - queryParamsHandlerFn(cliCtx), + queryParamsHandlerFn(clientCtx), ).Methods("GET") r.HandleFunc( "/minting/inflation", - queryInflationHandlerFn(cliCtx), + queryInflationHandlerFn(clientCtx), ).Methods("GET") r.HandleFunc( "/minting/annual-provisions", - queryAnnualProvisionsHandlerFn(cliCtx), + queryAnnualProvisionsHandlerFn(clientCtx), ).Methods("GET") } -func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryParamsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters) - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryInflationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryInflationHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryInflation) - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryAnnualProvisionsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryAnnualProvisionsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAnnualProvisions) - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/mint/client/rest/rest.go b/x/mint/client/rest/rest.go index 556e14685bac..2ed28d41d771 100644 --- a/x/mint/client/rest/rest.go +++ b/x/mint/client/rest/rest.go @@ -3,10 +3,12 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" ) // RegisterRoutes registers minting module REST handlers on the provided router. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - registerQueryRoutes(cliCtx, r) +func RegisterRoutes(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + registerQueryRoutes(clientCtx, r) } diff --git a/x/mint/genesis.go b/x/mint/genesis.go index 331852b43550..be7ac61d5019 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -2,17 +2,20 @@ package mint import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/keeper" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) // InitGenesis new mint genesis -func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { +func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, ak types.AccountKeeper, data *types.GenesisState) { keeper.SetMinter(ctx, data.Minter) keeper.SetParams(ctx, data.Params) + ak.GetModuleAccount(ctx, types.ModuleName) } // ExportGenesis returns a GenesisState for a given context and keeper. -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { +func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState { minter := keeper.GetMinter(ctx) params := keeper.GetParams(ctx) - return NewGenesisState(minter, params) + return types.NewGenesisState(minter, params) } diff --git a/x/mint/internal/keeper/integration_test.go b/x/mint/internal/keeper/integration_test.go deleted file mode 100644 index ba02e0444767..000000000000 --- a/x/mint/internal/keeper/integration_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package keeper_test - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" -) - -// returns context and an app with updated mint keeper -func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { - app := simapp.Setup(isCheckTx) - - ctx := app.BaseApp.NewContext(isCheckTx, abci.Header{}) - app.MintKeeper.SetParams(ctx, types.DefaultParams()) - app.MintKeeper.SetMinter(ctx, types.DefaultInitialMinter()) - - return app, ctx -} diff --git a/x/mint/internal/keeper/keeper.go b/x/mint/internal/keeper/keeper.go deleted file mode 100644 index 3be3146fdf68..000000000000 --- a/x/mint/internal/keeper/keeper.go +++ /dev/null @@ -1,113 +0,0 @@ -package keeper - -import ( - "fmt" - - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Keeper of the mint store -type Keeper struct { - cdc *codec.Codec - storeKey sdk.StoreKey - paramSpace params.Subspace - sk types.StakingKeeper - supplyKeeper types.SupplyKeeper - feeCollectorName string -} - -// NewKeeper creates a new mint Keeper instance -func NewKeeper( - cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, - sk types.StakingKeeper, supplyKeeper types.SupplyKeeper, feeCollectorName string, -) Keeper { - - // ensure mint module account is set - if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil { - panic("the mint module account has not been set") - } - - return Keeper{ - cdc: cdc, - storeKey: key, - paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()), - sk: sk, - supplyKeeper: supplyKeeper, - feeCollectorName: feeCollectorName, - } -} - -//______________________________________________________________________ - -// 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)) -} - -// get the minter -func (k Keeper) GetMinter(ctx sdk.Context) (minter types.Minter) { - store := ctx.KVStore(k.storeKey) - b := store.Get(types.MinterKey) - if b == nil { - panic("stored minter should not have been nil") - } - - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &minter) - return -} - -// set the minter -func (k Keeper) SetMinter(ctx sdk.Context, minter types.Minter) { - store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(minter) - store.Set(types.MinterKey, b) -} - -//______________________________________________________________________ - -// GetParams returns the total set of minting parameters. -func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - k.paramSpace.GetParamSet(ctx, ¶ms) - return params -} - -// SetParams sets the total set of minting parameters. -func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - k.paramSpace.SetParamSet(ctx, ¶ms) -} - -//______________________________________________________________________ - -// StakingTokenSupply implements an alias call to the underlying staking keeper's -// StakingTokenSupply to be used in BeginBlocker. -func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int { - return k.sk.StakingTokenSupply(ctx) -} - -// BondedRatio implements an alias call to the underlying staking keeper's -// BondedRatio to be used in BeginBlocker. -func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec { - return k.sk.BondedRatio(ctx) -} - -// MintCoins implements an alias call to the underlying supply keeper's -// MintCoins to be used in BeginBlocker. -func (k Keeper) MintCoins(ctx sdk.Context, newCoins sdk.Coins) error { - if newCoins.Empty() { - // skip as no coins need to be minted - return nil - } - - return k.supplyKeeper.MintCoins(ctx, types.ModuleName, newCoins) -} - -// AddCollectedFees implements an alias call to the underlying supply keeper's -// AddCollectedFees to be used in BeginBlocker. -func (k Keeper) AddCollectedFees(ctx sdk.Context, fees sdk.Coins) error { - return k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.feeCollectorName, fees) -} diff --git a/x/mint/internal/keeper/querier.go b/x/mint/internal/keeper/querier.go deleted file mode 100644 index 258d1b111ed2..000000000000 --- a/x/mint/internal/keeper/querier.go +++ /dev/null @@ -1,62 +0,0 @@ -package keeper - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "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/mint/internal/types" -) - -// NewQuerier returns a minting Querier handler. -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, _ abci.RequestQuery) ([]byte, error) { - switch path[0] { - case types.QueryParameters: - return queryParams(ctx, k) - - case types.QueryInflation: - return queryInflation(ctx, k) - - case types.QueryAnnualProvisions: - return queryAnnualProvisions(ctx, k) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) - } - } -} - -func queryParams(ctx sdk.Context, k Keeper) ([]byte, error) { - params := k.GetParams(ctx) - - res, err := codec.MarshalJSONIndent(k.cdc, params) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func queryInflation(ctx sdk.Context, k Keeper) ([]byte, error) { - minter := k.GetMinter(ctx) - - res, err := codec.MarshalJSONIndent(k.cdc, minter.Inflation) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func queryAnnualProvisions(ctx sdk.Context, k Keeper) ([]byte, error) { - minter := k.GetMinter(ctx) - - res, err := codec.MarshalJSONIndent(k.cdc, minter.AnnualProvisions) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} diff --git a/x/mint/internal/keeper/querier_test.go b/x/mint/internal/keeper/querier_test.go deleted file mode 100644 index 287afea9fa5f..000000000000 --- a/x/mint/internal/keeper/querier_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package keeper_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - keep "github.com/cosmos/cosmos-sdk/x/mint/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" - - abci "github.com/tendermint/tendermint/abci/types" -) - -func TestNewQuerier(t *testing.T) { - app, ctx := createTestApp(true) - querier := keep.NewQuerier(app.MintKeeper) - - query := abci.RequestQuery{ - Path: "", - Data: []byte{}, - } - - _, err := querier(ctx, []string{types.QueryParameters}, query) - require.NoError(t, err) - - _, err = querier(ctx, []string{types.QueryInflation}, query) - require.NoError(t, err) - - _, err = querier(ctx, []string{types.QueryAnnualProvisions}, query) - require.NoError(t, err) - - _, err = querier(ctx, []string{"foo"}, query) - require.Error(t, err) -} - -func TestQueryParams(t *testing.T) { - app, ctx := createTestApp(true) - querier := keep.NewQuerier(app.MintKeeper) - - var params types.Params - - res, sdkErr := querier(ctx, []string{types.QueryParameters}, abci.RequestQuery{}) - require.NoError(t, sdkErr) - - err := app.Codec().UnmarshalJSON(res, ¶ms) - require.NoError(t, err) - - require.Equal(t, app.MintKeeper.GetParams(ctx), params) -} - -func TestQueryInflation(t *testing.T) { - app, ctx := createTestApp(true) - querier := keep.NewQuerier(app.MintKeeper) - - var inflation sdk.Dec - - res, sdkErr := querier(ctx, []string{types.QueryInflation}, abci.RequestQuery{}) - require.NoError(t, sdkErr) - - err := app.Codec().UnmarshalJSON(res, &inflation) - require.NoError(t, err) - - require.Equal(t, app.MintKeeper.GetMinter(ctx).Inflation, inflation) -} - -func TestQueryAnnualProvisions(t *testing.T) { - app, ctx := createTestApp(true) - querier := keep.NewQuerier(app.MintKeeper) - - var annualProvisions sdk.Dec - - res, sdkErr := querier(ctx, []string{types.QueryAnnualProvisions}, abci.RequestQuery{}) - require.NoError(t, sdkErr) - - err := app.Codec().UnmarshalJSON(res, &annualProvisions) - require.NoError(t, err) - - require.Equal(t, app.MintKeeper.GetMinter(ctx).AnnualProvisions, annualProvisions) -} diff --git a/x/mint/internal/types/codec.go b/x/mint/internal/types/codec.go deleted file mode 100644 index 5787f242adfe..000000000000 --- a/x/mint/internal/types/codec.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// generic sealed codec to be used throughout this module -var ModuleCdc *codec.Codec - -func init() { - ModuleCdc = codec.New() - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() -} diff --git a/x/mint/internal/types/expected_keepers.go b/x/mint/internal/types/expected_keepers.go deleted file mode 100644 index ea20619164d3..000000000000 --- a/x/mint/internal/types/expected_keepers.go +++ /dev/null @@ -1,24 +0,0 @@ -package types // noalias - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -// StakingKeeper defines the expected staking keeper -type StakingKeeper interface { - StakingTokenSupply(ctx sdk.Context) sdk.Int - BondedRatio(ctx sdk.Context) sdk.Dec -} - -// SupplyKeeper defines the expected supply keeper -type SupplyKeeper interface { - GetModuleAddress(name string) sdk.AccAddress - - // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 - SetModuleAccount(sdk.Context, exported.ModuleAccountI) - - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error - MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error -} diff --git a/x/mint/internal/types/genesis.go b/x/mint/internal/types/genesis.go deleted file mode 100644 index acb3a8efe80f..000000000000 --- a/x/mint/internal/types/genesis.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -// GenesisState - minter state -type GenesisState struct { - Minter Minter `json:"minter" yaml:"minter"` // minter object - Params Params `json:"params" yaml:"params"` // inflation params -} - -// NewGenesisState creates a new GenesisState object -func NewGenesisState(minter Minter, params Params) GenesisState { - return GenesisState{ - Minter: minter, - Params: params, - } -} - -// DefaultGenesisState creates a default GenesisState object -func DefaultGenesisState() GenesisState { - return GenesisState{ - Minter: DefaultInitialMinter(), - Params: DefaultParams(), - } -} - -// ValidateGenesis validates the provided genesis state to ensure the -// expected invariants holds. -func ValidateGenesis(data GenesisState) error { - if err := data.Params.Validate(); err != nil { - return err - } - - return ValidateMinter(data.Minter) -} diff --git a/x/mint/internal/types/keys.go b/x/mint/internal/types/keys.go deleted file mode 100644 index cf395bc44ee3..000000000000 --- a/x/mint/internal/types/keys.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -// the one key to use for the keeper store -var MinterKey = []byte{0x00} - -// nolint -const ( - // module name - ModuleName = "mint" - - // default paramspace for params keeper - DefaultParamspace = ModuleName - - // StoreKey is the default store key for mint - StoreKey = ModuleName - - // QuerierRoute is the querier route for the minting store. - QuerierRoute = StoreKey - - // Query endpoints supported by the minting querier - QueryParameters = "parameters" - QueryInflation = "inflation" - QueryAnnualProvisions = "annual_provisions" -) diff --git a/x/mint/internal/types/params.go b/x/mint/internal/types/params.go deleted file mode 100644 index 020ad0735b79..000000000000 --- a/x/mint/internal/types/params.go +++ /dev/null @@ -1,133 +0,0 @@ -package types - -import ( - "errors" - "fmt" - "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Parameter store keys -var ( - KeyMintDenom = []byte("MintDenom") - KeyInflationRate = []byte("InflationRate") - KeyInflationMax = []byte("InflationMax") - KeyInflationMin = []byte("InflationMin") - KeyGoalBonded = []byte("GoalBonded") - KeyBlocksPerYear = []byte("BlocksPerYear") -) - -// mint parameters -type Params struct { - MintDenom string `json:"mint_denom" yaml:"mint_denom"` // type of coin to mint - InflationRate sdk.Dec `json:"inflation_rate" yaml:"inflation_rate"` // the fixed inflation rate - BlocksPerYear uint64 `json:"blocks_per_year" yaml:"blocks_per_year"` // expected blocks per year -} - -// ParamTable for minting module. -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable().RegisterParamSet(&Params{}) -} - -func NewParams( - mintDenom string, inflationRate sdk.Dec, blocksPerYear uint64, -) Params { - - return Params{ - MintDenom: mintDenom, - InflationRate: inflationRate, - BlocksPerYear: blocksPerYear, - } -} - -// default minting module parameters -func DefaultParams() Params { - return Params{ - MintDenom: sdk.DefaultBondDenom, - InflationRate: sdk.NewDecWithPrec(3, 2), - BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times - } -} - -// validate params -func (p Params) Validate() error { - if err := validateMintDenom(p.MintDenom); err != nil { - return err - } - if err := validateInflationRate(p.InflationRate); err != nil { - return err - } - if err := validateBlocksPerYear(p.BlocksPerYear); err != nil { - return err - } - - return nil - -} - -func (p Params) String() string { - return fmt.Sprintf(`Minting Params: - Mint Denom: %s - Inflation Rate: %s - Blocks Per Year: %d -`, - p.MintDenom, p.InflationRate, p.BlocksPerYear, - ) -} - -// Implements params.ParamSet -func (p *Params) ParamSetPairs() params.ParamSetPairs { - return params.ParamSetPairs{ - params.NewParamSetPair(KeyMintDenom, &p.MintDenom, validateMintDenom), - params.NewParamSetPair(KeyInflationRate, &p.InflationRate, validateInflationRate), - params.NewParamSetPair(KeyBlocksPerYear, &p.BlocksPerYear, validateBlocksPerYear), - } -} - -func validateMintDenom(i interface{}) error { - v, ok := i.(string) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if strings.TrimSpace(v) == "" { - return errors.New("mint denom cannot be blank") - } - if err := sdk.ValidateDenom(v); err != nil { - return err - } - - return nil -} - - -func validateInflationRate(i interface{}) error { - v, ok := i.(sdk.Dec) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v.IsNegative() { - return fmt.Errorf("max inflation cannot be negative: %s", v) - } - if v.GT(sdk.OneDec()) { - return fmt.Errorf("max inflation too large: %s", v) - } - - return nil -} - -func validateBlocksPerYear(i interface{}) error { - v, ok := i.(uint64) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v == 0 { - return fmt.Errorf("blocks per year must be positive: %d", v) - } - - return nil -} diff --git a/x/mint/keeper/grpc_query.go b/x/mint/keeper/grpc_query.go new file mode 100644 index 000000000000..6ad3bf309c7a --- /dev/null +++ b/x/mint/keeper/grpc_query.go @@ -0,0 +1,34 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +var _ types.QueryServer = Keeper{} + +// Params returns params of the mint module. +func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := k.GetParams(ctx) + + return &types.QueryParamsResponse{Params: params}, nil +} + +// Inflation returns minter.Inflation of the mint module. +func (k Keeper) Inflation(c context.Context, _ *types.QueryInflationRequest) (*types.QueryInflationResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + minter := k.GetMinter(ctx) + + return &types.QueryInflationResponse{Inflation: minter.Inflation}, nil +} + +// AnnualProvisions returns minter.AnnualProvisions of the mint module. +func (k Keeper) AnnualProvisions(c context.Context, _ *types.QueryAnnualProvisionsRequest) (*types.QueryAnnualProvisionsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + minter := k.GetMinter(ctx) + + return &types.QueryAnnualProvisionsResponse{AnnualProvisions: minter.AnnualProvisions}, nil +} diff --git a/x/mint/keeper/grpc_query_test.go b/x/mint/keeper/grpc_query_test.go new file mode 100644 index 000000000000..11b627669438 --- /dev/null +++ b/x/mint/keeper/grpc_query_test.go @@ -0,0 +1,56 @@ +package keeper_test + +import ( + gocontext "context" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type MintTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient +} + +func (suite *MintTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.MintKeeper) + queryClient := types.NewQueryClient(queryHelper) + + suite.app = app + suite.ctx = ctx + + suite.queryClient = queryClient +} + +func (suite *MintTestSuite) TestGRPCParams() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + params, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) + suite.Require().NoError(err) + suite.Require().Equal(params.Params, app.MintKeeper.GetParams(ctx)) + + inflation, err := queryClient.Inflation(gocontext.Background(), &types.QueryInflationRequest{}) + suite.Require().NoError(err) + suite.Require().Equal(inflation.Inflation, app.MintKeeper.GetMinter(ctx).Inflation) + + annualProvisions, err := queryClient.AnnualProvisions(gocontext.Background(), &types.QueryAnnualProvisionsRequest{}) + suite.Require().NoError(err) + suite.Require().Equal(annualProvisions.AnnualProvisions, app.MintKeeper.GetMinter(ctx).AnnualProvisions) +} + +func TestMintTestSuite(t *testing.T) { + suite.Run(t, new(MintTestSuite)) +} diff --git a/x/mint/keeper/integration_test.go b/x/mint/keeper/integration_test.go new file mode 100644 index 000000000000..df321c33bd0c --- /dev/null +++ b/x/mint/keeper/integration_test.go @@ -0,0 +1,20 @@ +package keeper_test + +import ( + 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/mint/types" +) + +// returns context and an app with updated mint keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.MintKeeper.SetParams(ctx, types.DefaultParams()) + app.MintKeeper.SetMinter(ctx, types.DefaultInitialMinter()) + + return app, ctx +} diff --git a/x/mint/keeper/keeper.go b/x/mint/keeper/keeper.go new file mode 100644 index 000000000000..de0a31222845 --- /dev/null +++ b/x/mint/keeper/keeper.go @@ -0,0 +1,116 @@ +package keeper + +import ( + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Keeper of the mint store +type Keeper struct { + cdc codec.BinaryMarshaler + storeKey sdk.StoreKey + paramSpace paramtypes.Subspace + stakingKeeper types.StakingKeeper + bankKeeper types.BankKeeper + feeCollectorName string +} + +// NewKeeper creates a new mint Keeper instance +func NewKeeper( + cdc codec.BinaryMarshaler, key sdk.StoreKey, paramSpace paramtypes.Subspace, + sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, + feeCollectorName string, +) Keeper { + // ensure mint module account is set + if addr := ak.GetModuleAddress(types.ModuleName); addr == nil { + panic("the mint module account has not been set") + } + + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + cdc: cdc, + storeKey: key, + paramSpace: paramSpace, + stakingKeeper: sk, + bankKeeper: bk, + feeCollectorName: feeCollectorName, + } +} + +//______________________________________________________________________ + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// get the minter +func (k Keeper) GetMinter(ctx sdk.Context) (minter types.Minter) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.MinterKey) + if b == nil { + panic("stored minter should not have been nil") + } + + k.cdc.MustUnmarshalBinaryBare(b, &minter) + return +} + +// set the minter +func (k Keeper) SetMinter(ctx sdk.Context, minter types.Minter) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinaryBare(&minter) + store.Set(types.MinterKey, b) +} + +//______________________________________________________________________ + +// GetParams returns the total set of minting parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramSpace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the total set of minting parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramSpace.SetParamSet(ctx, ¶ms) +} + +//______________________________________________________________________ + +// StakingTokenSupply implements an alias call to the underlying staking keeper's +// StakingTokenSupply to be used in BeginBlocker. +func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int { + return k.stakingKeeper.StakingTokenSupply(ctx) +} + +// BondedRatio implements an alias call to the underlying staking keeper's +// BondedRatio to be used in BeginBlocker. +func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec { + return k.stakingKeeper.BondedRatio(ctx) +} + +// MintCoins implements an alias call to the underlying supply keeper's +// MintCoins to be used in BeginBlocker. +func (k Keeper) MintCoins(ctx sdk.Context, newCoins sdk.Coins) error { + if newCoins.Empty() { + // skip as no coins need to be minted + return nil + } + + return k.bankKeeper.MintCoins(ctx, types.ModuleName, newCoins) +} + +// AddCollectedFees implements an alias call to the underlying supply keeper's +// AddCollectedFees to be used in BeginBlocker. +func (k Keeper) AddCollectedFees(ctx sdk.Context, fees sdk.Coins) error { + return k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.feeCollectorName, fees) +} diff --git a/x/mint/keeper/querier.go b/x/mint/keeper/querier.go new file mode 100644 index 000000000000..294445614a5f --- /dev/null +++ b/x/mint/keeper/querier.go @@ -0,0 +1,62 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "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/mint/types" +) + +// NewQuerier returns a minting Querier handler. +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, _ abci.RequestQuery) ([]byte, error) { + switch path[0] { + case types.QueryParameters: + return queryParams(ctx, k, legacyQuerierCdc) + + case types.QueryInflation: + return queryInflation(ctx, k, legacyQuerierCdc) + + case types.QueryAnnualProvisions: + return queryAnnualProvisions(ctx, k, legacyQuerierCdc) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) + } + } +} + +func queryParams(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + params := k.GetParams(ctx) + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func queryInflation(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + minter := k.GetMinter(ctx) + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, minter.Inflation) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func queryAnnualProvisions(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + minter := k.GetMinter(ctx) + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, minter.AnnualProvisions) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} diff --git a/x/mint/keeper/querier_test.go b/x/mint/keeper/querier_test.go new file mode 100644 index 000000000000..2e987aa042ce --- /dev/null +++ b/x/mint/keeper/querier_test.go @@ -0,0 +1,86 @@ +package keeper_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + keep "github.com/cosmos/cosmos-sdk/x/mint/keeper" + "github.com/cosmos/cosmos-sdk/x/mint/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +func TestNewQuerier(t *testing.T) { + app, ctx := createTestApp(true) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keep.NewQuerier(app.MintKeeper, legacyQuerierCdc.LegacyAmino) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + _, err := querier(ctx, []string{types.QueryParameters}, query) + require.NoError(t, err) + + _, err = querier(ctx, []string{types.QueryInflation}, query) + require.NoError(t, err) + + _, err = querier(ctx, []string{types.QueryAnnualProvisions}, query) + require.NoError(t, err) + + _, err = querier(ctx, []string{"foo"}, query) + require.Error(t, err) +} + +func TestQueryParams(t *testing.T) { + app, ctx := createTestApp(true) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keep.NewQuerier(app.MintKeeper, legacyQuerierCdc.LegacyAmino) + + var params types.Params + + res, sdkErr := querier(ctx, []string{types.QueryParameters}, abci.RequestQuery{}) + require.NoError(t, sdkErr) + + err := app.LegacyAmino().UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + + require.Equal(t, app.MintKeeper.GetParams(ctx), params) +} + +func TestQueryInflation(t *testing.T) { + app, ctx := createTestApp(true) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keep.NewQuerier(app.MintKeeper, legacyQuerierCdc.LegacyAmino) + + var inflation sdk.Dec + + res, sdkErr := querier(ctx, []string{types.QueryInflation}, abci.RequestQuery{}) + require.NoError(t, sdkErr) + + err := app.LegacyAmino().UnmarshalJSON(res, &inflation) + require.NoError(t, err) + + require.Equal(t, app.MintKeeper.GetMinter(ctx).Inflation, inflation) +} + +func TestQueryAnnualProvisions(t *testing.T) { + app, ctx := createTestApp(true) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keep.NewQuerier(app.MintKeeper, legacyQuerierCdc.LegacyAmino) + + var annualProvisions sdk.Dec + + res, sdkErr := querier(ctx, []string{types.QueryAnnualProvisions}, abci.RequestQuery{}) + require.NoError(t, sdkErr) + + err := app.LegacyAmino().UnmarshalJSON(res, &annualProvisions) + require.NoError(t, err) + + require.Equal(t, app.MintKeeper.GetMinter(ctx).AnnualProvisions, annualProvisions) +} diff --git a/x/mint/legacy/v039/types.go b/x/mint/legacy/v039/types.go new file mode 100644 index 000000000000..59a7a7c400d4 --- /dev/null +++ b/x/mint/legacy/v039/types.go @@ -0,0 +1,28 @@ +package v039 + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + ModuleName = "mint" +) + +type ( + // Minter represents the minting state. + Minter struct { + Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // current annual inflation rate + AnnualProvisions sdk.Dec `json:"annual_provisions" yaml:"annual_provisions"` // current annual expected provisions + } + + // mint parameters + Params struct { + MintDenom string `json:"mint_denom" yaml:"mint_denom"` // type of coin to mint + InflationRate sdk.Dec `json:"inflation_rate" yaml:"inflation_rate"` // maximum annual change in inflation rate + BlocksPerYear uint64 `json:"blocks_per_year" yaml:"blocks_per_year"` // expected blocks per year + } + + // GenesisState - minter state + GenesisState struct { + Minter Minter `json:"minter" yaml:"minter"` // minter object + Params Params `json:"params" yaml:"params"` // inflation params + } +) diff --git a/x/mint/legacy/v040/migrate.go b/x/mint/legacy/v040/migrate.go new file mode 100644 index 000000000000..c94e2304cc2c --- /dev/null +++ b/x/mint/legacy/v040/migrate.go @@ -0,0 +1,24 @@ +package v040 + +import ( + v039mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v039" + v040mint "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +// Migrate accepts exported v0.39 x/mint genesis state and +// migrates it to v0.40 x/mint genesis state. The migration includes: +// +// - Re-encode in v0.40 GenesisState. +func Migrate(mintGenState v039mint.GenesisState) *v040mint.GenesisState { + return &v040mint.GenesisState{ + Minter: v040mint.Minter{ + Inflation: mintGenState.Minter.Inflation, + AnnualProvisions: mintGenState.Minter.AnnualProvisions, + }, + Params: v040mint.Params{ + MintDenom: mintGenState.Params.MintDenom, + InflationRate: mintGenState.Params.InflationRate, + BlocksPerYear: mintGenState.Params.BlocksPerYear, + }, + } +} diff --git a/x/mint/legacy/v040/types.go b/x/mint/legacy/v040/types.go new file mode 100644 index 000000000000..d725b48c34fb --- /dev/null +++ b/x/mint/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "mint" +) diff --git a/x/mint/module.go b/x/mint/module.go index 95b171251ee1..44e96ce74bda 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -1,23 +1,27 @@ package mint import ( + "context" "encoding/json" "fmt" "math/rand" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "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" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/mint/client/cli" "github.com/cosmos/cosmos-sdk/x/mint/client/rest" + "github.com/cosmos/cosmos-sdk/x/mint/keeper" "github.com/cosmos/cosmos-sdk/x/mint/simulation" - sim "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) var ( @@ -27,45 +31,56 @@ var ( ) // AppModuleBasic defines the basic application module used by the mint module. -type AppModuleBasic struct{} +type AppModuleBasic struct { + cdc codec.Marshaler +} var _ module.AppModuleBasic = AppModuleBasic{} // Name returns the mint module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the mint module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {} +// RegisterLegacyAminoCodec registers the mint module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers the module's interface types +func (b AppModuleBasic) RegisterInterfaces(_ cdctypes.InterfaceRegistry) {} // DefaultGenesis returns default genesis state as raw bytes for the mint // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the mint module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return types.ValidateGenesis(data) } // RegisterRESTRoutes registers the REST routes for the mint module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { + rest.RegisterRoutes(clientCtx, rtr) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the mint module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + } // GetTxCmd returns no root tx command for the mint module. -func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetTxCmd() *cobra.Command { return nil } // GetQueryCmd returns the root query command for the mint module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(cdc) +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() } //____________________________________________________________________________ @@ -74,55 +89,61 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper + authKeeper types.AccountKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper) AppModule { +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, ak types.AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, + authKeeper: ak, } } // Name returns the mint module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers the mint module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route returns the message routing key for the mint module. -func (AppModule) Route() string { return "" } - -// NewHandler returns an sdk.Handler for the mint module. -func (am AppModule) NewHandler() sdk.Handler { return nil } +func (AppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns the mint module's querier route name. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute } -// NewQuerierHandler returns the mint module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// LegacyQuerierHandler returns the mint module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) +} + +// 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) } // InitGenesis performs genesis initialization for the mint module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, genesisState) +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, am.authKeeper, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the mint // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) + return cdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the mint module. @@ -146,21 +167,21 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return nil } // RandomizedParams creates randomized mint param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for mint module's types. -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) } // WeightedOperations doesn't return any mint module operation. -func (AppModule) WeightedOperations(_ module.SimulationState) []sim.WeightedOperation { +func (AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { return nil } diff --git a/x/mint/module_test.go b/x/mint/module_test.go new file mode 100644 index 000000000000..3b2df369e661 --- /dev/null +++ b/x/mint/module_test.go @@ -0,0 +1,28 @@ +package mint_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.InitChain( + abcitypes.RequestInitChain{ + AppStateBytes: []byte("{}"), + ChainId: "test-chain-id", + }, + ) + + acc := app.AccountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.ModuleName)) + require.NotNil(t, acc) +} diff --git a/x/mint/simulation/decoder.go b/x/mint/simulation/decoder.go index f1c9f6bda62d..37d9930f7da5 100644 --- a/x/mint/simulation/decoder.go +++ b/x/mint/simulation/decoder.go @@ -4,21 +4,23 @@ import ( "bytes" "fmt" - tmkv "github.com/tendermint/tendermint/libs/kv" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding mint type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key, types.MinterKey): - var minterA, minterB types.Minter - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &minterA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &minterB) - return fmt.Sprintf("%v\n%v", minterA, minterB) - default: - panic(fmt.Sprintf("invalid mint key %X", kvA.Key)) +// NewDecodeStore returns a decoder function closure that umarshals 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 { + switch { + case bytes.Equal(kvA.Key, types.MinterKey): + var minterA, minterB types.Minter + cdc.MustUnmarshalBinaryBare(kvA.Value, &minterA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &minterB) + return fmt.Sprintf("%v\n%v", minterA, minterB) + default: + panic(fmt.Sprintf("invalid mint key %X", kvA.Key)) + } } } diff --git a/x/mint/simulation/decoder_test.go b/x/mint/simulation/decoder_test.go index f92a84f5df21..762768c07eaa 100644 --- a/x/mint/simulation/decoder_test.go +++ b/x/mint/simulation/decoder_test.go @@ -1,4 +1,4 @@ -package simulation +package simulation_test import ( "fmt" @@ -6,26 +6,24 @@ import ( "github.com/stretchr/testify/require" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/mint/simulation" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - return -} - func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) + minter := types.NewMinter(sdk.OneDec(), sdk.NewDec(15)) - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.MinterKey, Value: cdc.MustMarshalBinaryLengthPrefixed(minter)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: types.MinterKey, Value: cdc.MustMarshalBinaryBare(&minter)}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, } tests := []struct { name string @@ -40,9 +38,9 @@ func TestDecodeStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/mint/simulation/genesis.go b/x/mint/simulation/genesis.go index 1581a0cb7667..ce1fa89d9670 100644 --- a/x/mint/simulation/genesis.go +++ b/x/mint/simulation/genesis.go @@ -3,14 +3,13 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) // Simulation parameter constants @@ -24,7 +23,7 @@ func GenInflation(r *rand.Rand) sdk.Dec { return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) } -// GenInflationRateChange randomized InflationRateChange +// GenInflationRate randomized InflationRate func GenInflationRate(r *rand.Rand) sdk.Dec { return sdk.NewDecWithPrec(int64(r.Intn(99)), 2) } @@ -39,18 +38,22 @@ func RandomizedGenState(simState *module.SimulationState) { ) // params - var inflationRate sdk.Dec + var inflationRateChange sdk.Dec simState.AppParams.GetOrGenerate( - simState.Cdc, InflationRate, &inflationRate, simState.Rand, - func(r *rand.Rand) { inflationRate = GenInflationRate(r) }, + simState.Cdc, InflationRate, &inflationRateChange, simState.Rand, + func(r *rand.Rand) { inflationRateChange = GenInflationRate(r) }, ) mintDenom := sdk.DefaultBondDenom blocksPerYear := uint64(60 * 60 * 8766 / 5) - params := types.NewParams(mintDenom, inflationRate, blocksPerYear) + params := types.NewParams(mintDenom, inflationRateChange, blocksPerYear) mintGenesis := types.NewGenesisState(types.InitialMinter(inflation), params) - fmt.Printf("Selected randomly generated minting parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, mintGenesis)) + bz, err := json.MarshalIndent(&mintGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated minting parameters:\n%s\n", bz) simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(mintGenesis) } diff --git a/x/mint/simulation/genesis_test.go b/x/mint/simulation/genesis_test.go new file mode 100644 index 000000000000..79ddfd4b9943 --- /dev/null +++ b/x/mint/simulation/genesis_test.go @@ -0,0 +1,76 @@ +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" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/mint/simulation" + "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 mintGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &mintGenesis) + + require.Equal(t, uint64(6311520), mintGenesis.Params.BlocksPerYear) + require.Equal(t, "stake", mintGenesis.Params.MintDenom) + require.Equal(t, "0stake", mintGenesis.Minter.BlockProvision(mintGenesis.Params).String()) + require.Equal(t, "0.170000000000000000", mintGenesis.Minter.NextAnnualProvisions(mintGenesis.Params, sdk.OneInt()).String()) + require.Equal(t, "0.170000000000000000", mintGenesis.Minter.Inflation.String()) + require.Equal(t, "0.000000000000000000", mintGenesis.Minter.AnnualProvisions.String()) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/mint/simulation/params.go b/x/mint/simulation/params.go index 268bb2053d7a..22c2b0c38e75 100644 --- a/x/mint/simulation/params.go +++ b/x/mint/simulation/params.go @@ -6,8 +6,10 @@ import ( "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/x/mint/internal/types" "github.com/cosmos/cosmos-sdk/x/simulation" + + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/mint/types" ) const ( @@ -16,8 +18,8 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keyInflationRate, func(r *rand.Rand) string { return fmt.Sprintf("\"%s\"", GenInflationRate(r)) diff --git a/x/mint/simulation/params_test.go b/x/mint/simulation/params_test.go new file mode 100644 index 000000000000..239649357bcc --- /dev/null +++ b/x/mint/simulation/params_test.go @@ -0,0 +1,35 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/mint/simulation" +) + +func TestParamChangest(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"mint/InflationRate", "InflationRate", "\"0.230000000000000000\"", "mint"}, + } + + paramChanges := simulation.ParamChanges(r) + require.Len(t, paramChanges, 1) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } + +} diff --git a/x/mint/types/codec.go b/x/mint/types/codec.go new file mode 100644 index 000000000000..b436c10298d5 --- /dev/null +++ b/x/mint/types/codec.go @@ -0,0 +1,15 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" +) + +var ( + amino = codec.NewLegacyAmino() +) + +func init() { + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/mint/internal/types/events.go b/x/mint/types/events.go similarity index 100% rename from x/mint/internal/types/events.go rename to x/mint/types/events.go diff --git a/x/mint/types/expected_keepers.go b/x/mint/types/expected_keepers.go new file mode 100644 index 000000000000..85b6d776c5c5 --- /dev/null +++ b/x/mint/types/expected_keepers.go @@ -0,0 +1,29 @@ +package types // noalias + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// StakingKeeper defines the expected staking keeper +type StakingKeeper interface { + StakingTokenSupply(ctx sdk.Context) sdk.Int + BondedRatio(ctx sdk.Context) sdk.Dec +} + +// AccountKeeper defines the contract required for account APIs. +type AccountKeeper interface { + GetModuleAddress(name string) sdk.AccAddress + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + SetModuleAccount(sdk.Context, types.ModuleAccountI) + GetModuleAccount(ctx sdk.Context, moduleName string) types.ModuleAccountI +} + +// BankKeeper defines the contract needed to be fulfilled for banking and supply +// dependencies. +type BankKeeper interface { + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error +} diff --git a/x/mint/types/genesis.go b/x/mint/types/genesis.go new file mode 100644 index 000000000000..3d2f761b4a6b --- /dev/null +++ b/x/mint/types/genesis.go @@ -0,0 +1,27 @@ +package types + +// NewGenesisState creates a new GenesisState object +func NewGenesisState(minter Minter, params Params) *GenesisState { + return &GenesisState{ + Minter: minter, + Params: params, + } +} + +// DefaultGenesisState creates a default GenesisState object +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Minter: DefaultInitialMinter(), + Params: DefaultParams(), + } +} + +// ValidateGenesis validates the provided genesis state to ensure the +// expected invariants holds. +func ValidateGenesis(data GenesisState) error { + if err := data.Params.Validate(); err != nil { + return err + } + + return ValidateMinter(data.Minter) +} diff --git a/x/mint/types/genesis.pb.go b/x/mint/types/genesis.pb.go new file mode 100644 index 000000000000..1486c35f2232 --- /dev/null +++ b/x/mint/types/genesis.pb.go @@ -0,0 +1,377 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/mint/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 defines the mint module's genesis state. +type GenesisState struct { + // minter is a space for holding current inflation information. + Minter Minter `protobuf:"bytes,1,opt,name=minter,proto3" json:"minter"` + // params defines all the paramaters of the module. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +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_0e215eb1d09cd648, []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) GetMinter() Minter { + if m != nil { + return m.Minter + } + return Minter{} +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.mint.v1beta1.GenesisState") +} + +func init() { proto.RegisterFile("cosmos/mint/v1beta1/genesis.proto", fileDescriptor_0e215eb1d09cd648) } + +var fileDescriptor_0e215eb1d09cd648 = []byte{ + // 218 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4c, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0xcf, 0xcd, 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, 0x86, + 0x28, 0xd1, 0x03, 0x29, 0xd1, 0x83, 0x2a, 0x91, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, + 0x83, 0x58, 0x10, 0xa5, 0x52, 0x72, 0xd8, 0x4c, 0x03, 0xeb, 0x03, 0xcb, 0x2b, 0xb5, 0x30, 0x72, + 0xf1, 0xb8, 0x43, 0x0c, 0x0f, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0xb2, 0xe4, 0x62, 0x03, 0x49, 0xa7, + 0x16, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0x49, 0xeb, 0x61, 0xb1, 0x4c, 0xcf, 0x17, 0xac, + 0xc4, 0x89, 0xe5, 0xc4, 0x3d, 0x79, 0x86, 0x20, 0xa8, 0x06, 0x90, 0xd6, 0x82, 0xc4, 0xa2, 0xc4, + 0xdc, 0x62, 0x09, 0x26, 0x3c, 0x5a, 0x03, 0xc0, 0x4a, 0x60, 0x5a, 0x21, 0x1a, 0x9c, 0x9c, 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, 0x33, 0x3d, 0xb3, 0x24, 0xa3, 0x34, + 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xea, 0x17, 0x08, 0xa5, 0x5b, 0x9c, 0x92, 0xad, 0x5f, 0x01, + 0xf1, 0x58, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x4b, 0xc6, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xbb, 0xc1, 0x11, 0x51, 0x42, 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 + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Minter.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 + l = m.Minter.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.Params.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 Minter", 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 := m.Minter.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", 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 := m.Params.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/mint/types/keys.go b/x/mint/types/keys.go new file mode 100644 index 000000000000..e1c778744f81 --- /dev/null +++ b/x/mint/types/keys.go @@ -0,0 +1,20 @@ +package types + +// MinterKey is the key to use for the keeper store. +var MinterKey = []byte{0x00} + +const ( + // module name + ModuleName = "mint" + + // StoreKey is the default store key for mint + StoreKey = ModuleName + + // QuerierRoute is the querier route for the minting store. + QuerierRoute = StoreKey + + // Query endpoints supported by the minting querier + QueryParameters = "parameters" + QueryInflation = "inflation" + QueryAnnualProvisions = "annual_provisions" +) diff --git a/x/mint/types/mint.pb.go b/x/mint/types/mint.pb.go new file mode 100644 index 000000000000..0904b57a6dde --- /dev/null +++ b/x/mint/types/mint.pb.go @@ -0,0 +1,629 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/mint/v1beta1/mint.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "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 + +// Minter represents the minting state. +type Minter struct { + // current annual inflation rate + Inflation github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=inflation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"inflation"` + // current annual expected provisions + AnnualProvisions github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=annual_provisions,json=annualProvisions,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"annual_provisions" yaml:"annual_provisions"` +} + +func (m *Minter) Reset() { *m = Minter{} } +func (m *Minter) String() string { return proto.CompactTextString(m) } +func (*Minter) ProtoMessage() {} +func (*Minter) Descriptor() ([]byte, []int) { + return fileDescriptor_2df116d183c1e223, []int{0} +} +func (m *Minter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Minter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Minter.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 *Minter) XXX_Merge(src proto.Message) { + xxx_messageInfo_Minter.Merge(m, src) +} +func (m *Minter) XXX_Size() int { + return m.Size() +} +func (m *Minter) XXX_DiscardUnknown() { + xxx_messageInfo_Minter.DiscardUnknown(m) +} + +var xxx_messageInfo_Minter proto.InternalMessageInfo + +// Params holds parameters for the mint module. +type Params struct { + // type of coin to mint + MintDenom string `protobuf:"bytes,1,opt,name=mint_denom,json=mintDenom,proto3" json:"mint_denom,omitempty"` + // maximum annual change in inflation rate + InflationRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=inflation_rate,json=inflationRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"inflation_rate" yaml:"inflation_rate"` + // expected blocks per year + BlocksPerYear uint64 `protobuf:"varint,6,opt,name=blocks_per_year,json=blocksPerYear,proto3" json:"blocks_per_year,omitempty" yaml:"blocks_per_year"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_2df116d183c1e223, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetMintDenom() string { + if m != nil { + return m.MintDenom + } + return "" +} + +func (m *Params) GetBlocksPerYear() uint64 { + if m != nil { + return m.BlocksPerYear + } + return 0 +} + +func init() { + proto.RegisterType((*Minter)(nil), "cosmos.mint.v1beta1.Minter") + proto.RegisterType((*Params)(nil), "cosmos.mint.v1beta1.Params") +} + +func init() { proto.RegisterFile("cosmos/mint/v1beta1/mint.proto", fileDescriptor_2df116d183c1e223) } + +var fileDescriptor_2df116d183c1e223 = []byte{ + // 351 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x4d, 0x4b, 0xc3, 0x30, + 0x18, 0xc7, 0x1b, 0x19, 0x85, 0x05, 0xe6, 0x4b, 0x7d, 0xa1, 0x0c, 0x6c, 0x47, 0x0f, 0x32, 0x0f, + 0xb6, 0x0c, 0x6f, 0x3b, 0xd6, 0x81, 0x20, 0x0a, 0xa3, 0x37, 0xbd, 0x94, 0xb4, 0x8b, 0x33, 0xac, + 0x4d, 0x4a, 0x92, 0x4d, 0xf7, 0x2d, 0x3c, 0x7a, 0xf4, 0xe3, 0xec, 0xe6, 0x8e, 0xe2, 0xa1, 0xe8, + 0xf6, 0x0d, 0xf6, 0x09, 0xa4, 0xcd, 0xd8, 0x50, 0x41, 0xd8, 0x29, 0xc9, 0xef, 0x79, 0xf8, 0xe7, + 0x97, 0xf0, 0x40, 0x2b, 0x66, 0x22, 0x65, 0xc2, 0x4b, 0x09, 0x95, 0xde, 0xa8, 0x15, 0x61, 0x89, + 0x5a, 0xe5, 0xc1, 0xcd, 0x38, 0x93, 0xcc, 0xd8, 0x57, 0x75, 0xb7, 0x44, 0xcb, 0x7a, 0xfd, 0xa0, + 0xcf, 0xfa, 0xac, 0xac, 0x7b, 0xc5, 0x4e, 0xb5, 0x3a, 0x6f, 0x00, 0xea, 0x37, 0x84, 0x4a, 0xcc, + 0x8d, 0x6b, 0x58, 0x25, 0xf4, 0x3e, 0x41, 0x92, 0x30, 0x6a, 0x82, 0x06, 0x68, 0x56, 0x7d, 0x77, + 0x92, 0xdb, 0xda, 0x47, 0x6e, 0x9f, 0xf4, 0x89, 0x7c, 0x18, 0x46, 0x6e, 0xcc, 0x52, 0x6f, 0x79, + 0xb7, 0x5a, 0xce, 0x44, 0x6f, 0xe0, 0xc9, 0x71, 0x86, 0x85, 0xdb, 0xc1, 0x71, 0xb0, 0x0e, 0x30, + 0x1e, 0xe1, 0x1e, 0xa2, 0x74, 0x88, 0x92, 0x30, 0xe3, 0x6c, 0x44, 0x04, 0x61, 0x54, 0x98, 0x5b, + 0x65, 0xea, 0xd5, 0x66, 0xa9, 0x8b, 0xdc, 0x36, 0xc7, 0x28, 0x4d, 0xda, 0xce, 0x9f, 0x40, 0x27, + 0xd8, 0x55, 0xac, 0xbb, 0x46, 0x5f, 0x00, 0xea, 0x5d, 0xc4, 0x51, 0x2a, 0x8c, 0x63, 0x08, 0x8b, + 0x2f, 0x08, 0x7b, 0x98, 0xb2, 0x54, 0x3d, 0x29, 0xa8, 0x16, 0xa4, 0x53, 0x00, 0x83, 0xc2, 0xed, + 0x95, 0x6f, 0xc8, 0x91, 0xc4, 0x4b, 0xbf, 0xcb, 0x8d, 0xfd, 0x0e, 0x95, 0xdf, 0xcf, 0x34, 0x27, + 0xa8, 0xad, 0x40, 0x80, 0x24, 0x36, 0x7c, 0xb8, 0x13, 0x25, 0x2c, 0x1e, 0x88, 0x30, 0xc3, 0x3c, + 0x1c, 0x63, 0xc4, 0x4d, 0xbd, 0x01, 0x9a, 0x15, 0xbf, 0xbe, 0xc8, 0xed, 0x23, 0x15, 0xf1, 0xab, + 0xc1, 0x09, 0x6a, 0x8a, 0x74, 0x31, 0xbf, 0xc5, 0x88, 0xb7, 0x2b, 0x2f, 0xaf, 0xb6, 0xe6, 0x5f, + 0x4c, 0x66, 0x16, 0x98, 0xce, 0x2c, 0xf0, 0x39, 0xb3, 0xc0, 0xf3, 0xdc, 0xd2, 0xa6, 0x73, 0x4b, + 0x7b, 0x9f, 0x5b, 0xda, 0xdd, 0xe9, 0xbf, 0xce, 0x4f, 0x6a, 0x64, 0x4a, 0xf5, 0x48, 0x2f, 0x27, + 0xe0, 0xfc, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x04, 0x68, 0xe1, 0x61, 0x4e, 0x02, 0x00, 0x00, +} + +func (m *Minter) 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 *Minter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Minter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.AnnualProvisions.Size() + i -= size + if _, err := m.AnnualProvisions.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.Inflation.Size() + i -= size + if _, err := m.Inflation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlocksPerYear != 0 { + i = encodeVarintMint(dAtA, i, uint64(m.BlocksPerYear)) + i-- + dAtA[i] = 0x30 + } + { + size := m.InflationRate.Size() + i -= size + if _, err := m.InflationRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMint(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.MintDenom) > 0 { + i -= len(m.MintDenom) + copy(dAtA[i:], m.MintDenom) + i = encodeVarintMint(dAtA, i, uint64(len(m.MintDenom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintMint(dAtA []byte, offset int, v uint64) int { + offset -= sovMint(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Minter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Inflation.Size() + n += 1 + l + sovMint(uint64(l)) + l = m.AnnualProvisions.Size() + n += 1 + l + sovMint(uint64(l)) + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.MintDenom) + if l > 0 { + n += 1 + l + sovMint(uint64(l)) + } + l = m.InflationRate.Size() + n += 1 + l + sovMint(uint64(l)) + if m.BlocksPerYear != 0 { + n += 1 + sovMint(uint64(m.BlocksPerYear)) + } + return n +} + +func sovMint(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMint(x uint64) (n int) { + return sovMint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Minter) 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 ErrIntOverflowMint + } + 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: Minter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Minter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inflation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + 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 ErrInvalidLengthMint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Inflation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AnnualProvisions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + 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 ErrInvalidLengthMint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AnnualProvisions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowMint + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MintDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + 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 ErrInvalidLengthMint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MintDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InflationRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + 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 ErrInvalidLengthMint + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMint + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InflationRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlocksPerYear", wireType) + } + m.BlocksPerYear = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMint + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlocksPerYear |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMint(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMint + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMint(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, ErrIntOverflowMint + } + 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, ErrIntOverflowMint + } + 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, ErrIntOverflowMint + } + 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, ErrInvalidLengthMint + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMint + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMint + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMint = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMint = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMint = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/mint/internal/types/minter.go b/x/mint/types/minter.go similarity index 79% rename from x/mint/internal/types/minter.go rename to x/mint/types/minter.go index 1eb3dfb70acd..5c49e7750e29 100644 --- a/x/mint/internal/types/minter.go +++ b/x/mint/types/minter.go @@ -6,12 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Minter represents the minting state. -type Minter struct { - Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // current annual inflation rate - AnnualProvisions sdk.Dec `json:"annual_provisions" yaml:"annual_provisions"` // current annual expected provisions -} - // NewMinter returns a new Minter object with the given inflation and annual // provisions values. func NewMinter(inflation, annualProvisions sdk.Dec) Minter { @@ -30,10 +24,10 @@ func InitialMinter(inflation sdk.Dec) Minter { } // DefaultInitialMinter returns a default initial Minter object for a new chain -// which uses an inflation rate of 13%. +// which uses an inflation rate of 3%. func DefaultInitialMinter() Minter { return InitialMinter( - sdk.NewDecWithPrec(13, 2), + sdk.NewDecWithPrec(3, 2), ) } @@ -48,7 +42,6 @@ func ValidateMinter(minter Minter) error { // NextInflationRate returns the new inflation rate for the next hour. func (m Minter) NextInflationRate(params Params) sdk.Dec { - // the annual inflation rate is just the fixed rate return params.InflationRate } diff --git a/x/mint/internal/types/minter_test.go b/x/mint/types/minter_test.go similarity index 96% rename from x/mint/internal/types/minter_test.go rename to x/mint/types/minter_test.go index 385f5100027e..36f1736eecdc 100644 --- a/x/mint/internal/types/minter_test.go +++ b/x/mint/types/minter_test.go @@ -13,6 +13,9 @@ func TestNextInflation(t *testing.T) { minter := DefaultInitialMinter() params := DefaultParams() + // Governing Mechanism: + // inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange + tests := []struct { setInflation, expChange sdk.Dec }{ @@ -27,6 +30,7 @@ func TestNextInflation(t *testing.T) { } for i, tc := range tests { minter.Inflation = tc.setInflation + inflation := minter.NextInflationRate(params) diffInflation := inflation.Sub(tc.setInflation) @@ -88,7 +92,6 @@ func BenchmarkBlockProvision(b *testing.B) { func BenchmarkNextInflation(b *testing.B) { minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) params := DefaultParams() - // run the NextInflationRate function b.N times for n := 0; n < b.N; n++ { minter.NextInflationRate(params) diff --git a/x/mint/types/params.go b/x/mint/types/params.go new file mode 100644 index 000000000000..91b659bac285 --- /dev/null +++ b/x/mint/types/params.go @@ -0,0 +1,115 @@ +package types + +import ( + "errors" + "fmt" + "strings" + + yaml "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Parameter store keys +var ( + KeyMintDenom = []byte("MintDenom") + KeyInflationRate = []byte("InflationRate") + KeyBlocksPerYear = []byte("BlocksPerYear") +) + +// ParamTable for minting module. +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +func NewParams(mintDenom string, inflationRate sdk.Dec, blocksPerYear uint64) Params { + return Params{ + MintDenom: mintDenom, + InflationRate: inflationRate, + BlocksPerYear: blocksPerYear, + } +} + +// default minting module parameters +func DefaultParams() Params { + return Params{ + MintDenom: sdk.DefaultBondDenom, + InflationRate: sdk.NewDecWithPrec(3, 2), + BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times + } +} + +// validate params +func (p Params) Validate() error { + if err := validateMintDenom(p.MintDenom); err != nil { + return err + } + if err := validateInflationRate(p.InflationRate); err != nil { + return err + } + if err := validateBlocksPerYear(p.BlocksPerYear); err != nil { + return err + } + return nil +} + +// String implements the Stringer interface. +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} + +// Implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyMintDenom, &p.MintDenom, validateMintDenom), + paramtypes.NewParamSetPair(KeyInflationRate, &p.InflationRate, validateInflationRate), + paramtypes.NewParamSetPair(KeyBlocksPerYear, &p.BlocksPerYear, validateBlocksPerYear), + } +} + +func validateMintDenom(i interface{}) error { + v, ok := i.(string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if strings.TrimSpace(v) == "" { + return errors.New("mint denom cannot be blank") + } + if err := sdk.ValidateDenom(v); err != nil { + return err + } + + return nil +} + +func validateInflationRate(i interface{}) error { + v, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("inflation rate change cannot be negative: %s", v) + } + if v.GT(sdk.OneDec()) { + return fmt.Errorf("inflation rate change too large: %s", v) + } + + return nil +} + +func validateBlocksPerYear(i interface{}) error { + v, ok := i.(uint64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v == 0 { + return fmt.Errorf("blocks per year must be positive: %d", v) + } + + return nil +} diff --git a/x/mint/types/query.pb.go b/x/mint/types/query.pb.go new file mode 100644 index 000000000000..f707eb810d2d --- /dev/null +++ b/x/mint/types/query.pb.go @@ -0,0 +1,1199 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/mint/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "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 + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params defines the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QueryInflationRequest is the request type for the Query/Inflation RPC method. +type QueryInflationRequest struct { +} + +func (m *QueryInflationRequest) Reset() { *m = QueryInflationRequest{} } +func (m *QueryInflationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryInflationRequest) ProtoMessage() {} +func (*QueryInflationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{2} +} +func (m *QueryInflationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryInflationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryInflationRequest.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 *QueryInflationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryInflationRequest.Merge(m, src) +} +func (m *QueryInflationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryInflationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryInflationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryInflationRequest proto.InternalMessageInfo + +// QueryInflationResponse is the response type for the Query/Inflation RPC +// method. +type QueryInflationResponse struct { + // inflation is the current minting inflation value. + Inflation github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=inflation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"inflation"` +} + +func (m *QueryInflationResponse) Reset() { *m = QueryInflationResponse{} } +func (m *QueryInflationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryInflationResponse) ProtoMessage() {} +func (*QueryInflationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{3} +} +func (m *QueryInflationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryInflationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryInflationResponse.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 *QueryInflationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryInflationResponse.Merge(m, src) +} +func (m *QueryInflationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryInflationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryInflationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryInflationResponse proto.InternalMessageInfo + +// QueryAnnualProvisionsRequest is the request type for the +// Query/AnnualProvisions RPC method. +type QueryAnnualProvisionsRequest struct { +} + +func (m *QueryAnnualProvisionsRequest) Reset() { *m = QueryAnnualProvisionsRequest{} } +func (m *QueryAnnualProvisionsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAnnualProvisionsRequest) ProtoMessage() {} +func (*QueryAnnualProvisionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{4} +} +func (m *QueryAnnualProvisionsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAnnualProvisionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAnnualProvisionsRequest.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 *QueryAnnualProvisionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAnnualProvisionsRequest.Merge(m, src) +} +func (m *QueryAnnualProvisionsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAnnualProvisionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAnnualProvisionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAnnualProvisionsRequest proto.InternalMessageInfo + +// QueryAnnualProvisionsResponse is the response type for the +// Query/AnnualProvisions RPC method. +type QueryAnnualProvisionsResponse struct { + // annual_provisions is the current minting annual provisions value. + AnnualProvisions github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=annual_provisions,json=annualProvisions,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"annual_provisions"` +} + +func (m *QueryAnnualProvisionsResponse) Reset() { *m = QueryAnnualProvisionsResponse{} } +func (m *QueryAnnualProvisionsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAnnualProvisionsResponse) ProtoMessage() {} +func (*QueryAnnualProvisionsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d0a1e393be338aea, []int{5} +} +func (m *QueryAnnualProvisionsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAnnualProvisionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAnnualProvisionsResponse.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 *QueryAnnualProvisionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAnnualProvisionsResponse.Merge(m, src) +} +func (m *QueryAnnualProvisionsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAnnualProvisionsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAnnualProvisionsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAnnualProvisionsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.mint.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.mint.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryInflationRequest)(nil), "cosmos.mint.v1beta1.QueryInflationRequest") + proto.RegisterType((*QueryInflationResponse)(nil), "cosmos.mint.v1beta1.QueryInflationResponse") + proto.RegisterType((*QueryAnnualProvisionsRequest)(nil), "cosmos.mint.v1beta1.QueryAnnualProvisionsRequest") + proto.RegisterType((*QueryAnnualProvisionsResponse)(nil), "cosmos.mint.v1beta1.QueryAnnualProvisionsResponse") +} + +func init() { proto.RegisterFile("cosmos/mint/v1beta1/query.proto", fileDescriptor_d0a1e393be338aea) } + +var fileDescriptor_d0a1e393be338aea = []byte{ + // 446 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0xcf, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, + 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x86, 0x28, 0xd0, + 0x03, 0x29, 0xd0, 0x83, 0x2a, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xeb, 0x83, 0x58, + 0x10, 0xa5, 0x52, 0x32, 0xe9, 0xf9, 0xf9, 0xe9, 0x39, 0xa9, 0xfa, 0x89, 0x05, 0x99, 0xfa, 0x89, + 0x79, 0x79, 0xf9, 0x25, 0x89, 0x25, 0x99, 0xf9, 0x79, 0xc5, 0x50, 0x59, 0x39, 0x6c, 0x36, 0x81, + 0x4d, 0x05, 0xcb, 0x2b, 0x89, 0x70, 0x09, 0x05, 0x82, 0xec, 0x0d, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, + 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x0a, 0xe0, 0x12, 0x46, 0x11, 0x2d, 0x2e, 0xc8, + 0xcf, 0x2b, 0x4e, 0x15, 0xb2, 0xe4, 0x62, 0x2b, 0x00, 0x8b, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, + 0x1b, 0x49, 0xeb, 0x61, 0x71, 0xa6, 0x1e, 0x44, 0x93, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, + 0x50, 0x0d, 0x4a, 0xe2, 0x5c, 0xa2, 0x60, 0x13, 0x3d, 0xf3, 0xd2, 0x72, 0xc0, 0x0e, 0x84, 0x59, + 0x95, 0xc6, 0x25, 0x86, 0x2e, 0x01, 0xb5, 0xcd, 0x87, 0x8b, 0x33, 0x13, 0x26, 0x08, 0xb6, 0x90, + 0xc7, 0x49, 0x0f, 0x64, 0xe6, 0xad, 0x7b, 0xf2, 0x6a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, + 0xc9, 0xf9, 0xb9, 0xfa, 0x50, 0x0f, 0x42, 0x28, 0xdd, 0xe2, 0x94, 0x6c, 0xfd, 0x92, 0xca, 0x82, + 0xd4, 0x62, 0x3d, 0x97, 0xd4, 0xe4, 0x20, 0x84, 0x01, 0x4a, 0x72, 0x5c, 0x32, 0x60, 0x7b, 0x1c, + 0xf3, 0xf2, 0x4a, 0x13, 0x73, 0x02, 0x8a, 0xf2, 0xcb, 0x32, 0x8b, 0x41, 0xe1, 0x04, 0x73, 0x47, + 0x0d, 0x97, 0x2c, 0x0e, 0x79, 0xa8, 0x73, 0xa2, 0xb9, 0x04, 0x13, 0xc1, 0x72, 0xf1, 0x05, 0x70, + 0x49, 0x32, 0x9d, 0x25, 0x90, 0x88, 0x66, 0x89, 0xd1, 0x51, 0x66, 0x2e, 0x56, 0xb0, 0xf5, 0x42, + 0x0d, 0x8c, 0x5c, 0x6c, 0x90, 0x10, 0x14, 0x52, 0xc7, 0x1a, 0xbc, 0x98, 0xd1, 0x25, 0xa5, 0x41, + 0x58, 0x21, 0xc4, 0x13, 0x4a, 0xca, 0x4d, 0x97, 0x9f, 0x4c, 0x66, 0x92, 0x15, 0x92, 0xd6, 0xc7, + 0x96, 0x2e, 0x20, 0x71, 0x25, 0xd4, 0xc3, 0xc8, 0xc5, 0x09, 0x8f, 0x0e, 0x21, 0x2d, 0xdc, 0x86, + 0xa3, 0x47, 0xa6, 0x94, 0x36, 0x51, 0x6a, 0xa1, 0x6e, 0x51, 0x03, 0xbb, 0x45, 0x41, 0x48, 0x0e, + 0xab, 0x5b, 0xe0, 0x31, 0x27, 0xb4, 0x92, 0x91, 0x4b, 0x00, 0x3d, 0x56, 0x84, 0x0c, 0x71, 0xdb, + 0x84, 0x23, 0x86, 0xa5, 0x8c, 0x48, 0xd1, 0x02, 0x75, 0xa3, 0x1e, 0xd8, 0x8d, 0x1a, 0x42, 0x6a, + 0x58, 0xdd, 0x88, 0x91, 0x1e, 0x9c, 0x9c, 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, 0x13, 0x6f, 0xda, 0xa8, 0x80, 0x18, 0x0c, 0x4e, 0x22, 0x49, 0x6c, 0xe0, 0xac, 0x69, + 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xed, 0x9c, 0x7b, 0x26, 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 { + // Params returns the total set of minting parameters. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // Inflation returns the current minting inflation value. + Inflation(ctx context.Context, in *QueryInflationRequest, opts ...grpc.CallOption) (*QueryInflationResponse, error) + // AnnualProvisions current minting annual provisions value. + AnnualProvisions(ctx context.Context, in *QueryAnnualProvisionsRequest, opts ...grpc.CallOption) (*QueryAnnualProvisionsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.mint.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Inflation(ctx context.Context, in *QueryInflationRequest, opts ...grpc.CallOption) (*QueryInflationResponse, error) { + out := new(QueryInflationResponse) + err := c.cc.Invoke(ctx, "/cosmos.mint.v1beta1.Query/Inflation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) AnnualProvisions(ctx context.Context, in *QueryAnnualProvisionsRequest, opts ...grpc.CallOption) (*QueryAnnualProvisionsResponse, error) { + out := new(QueryAnnualProvisionsResponse) + err := c.cc.Invoke(ctx, "/cosmos.mint.v1beta1.Query/AnnualProvisions", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params returns the total set of minting parameters. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // Inflation returns the current minting inflation value. + Inflation(context.Context, *QueryInflationRequest) (*QueryInflationResponse, error) + // AnnualProvisions current minting annual provisions value. + AnnualProvisions(context.Context, *QueryAnnualProvisionsRequest) (*QueryAnnualProvisionsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) Inflation(ctx context.Context, req *QueryInflationRequest) (*QueryInflationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Inflation not implemented") +} +func (*UnimplementedQueryServer) AnnualProvisions(ctx context.Context, req *QueryAnnualProvisionsRequest) (*QueryAnnualProvisionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AnnualProvisions not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.mint.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Inflation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryInflationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Inflation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.mint.v1beta1.Query/Inflation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Inflation(ctx, req.(*QueryInflationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_AnnualProvisions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAnnualProvisionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AnnualProvisions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.mint.v1beta1.Query/AnnualProvisions", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AnnualProvisions(ctx, req.(*QueryAnnualProvisionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.mint.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "Inflation", + Handler: _Query_Inflation_Handler, + }, + { + MethodName: "AnnualProvisions", + Handler: _Query_AnnualProvisions_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/mint/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *QueryInflationRequest) 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 *QueryInflationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryInflationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryInflationResponse) 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 *QueryInflationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryInflationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Inflation.Size() + i -= size + if _, err := m.Inflation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryAnnualProvisionsRequest) 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 *QueryAnnualProvisionsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAnnualProvisionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryAnnualProvisionsResponse) 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 *QueryAnnualProvisionsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAnnualProvisionsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.AnnualProvisions.Size() + i -= size + if _, err := m.AnnualProvisions.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + 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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryInflationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryInflationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Inflation.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryAnnualProvisionsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryAnnualProvisionsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.AnnualProvisions.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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 *QueryInflationRequest) 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: QueryInflationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryInflationRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryInflationResponse) 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: QueryInflationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryInflationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Inflation", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Inflation.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 *QueryAnnualProvisionsRequest) 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: QueryAnnualProvisionsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAnnualProvisionsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAnnualProvisionsResponse) 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: QueryAnnualProvisionsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAnnualProvisionsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AnnualProvisions", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AnnualProvisions.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/mint/types/query.pb.gw.go b/x/mint/types/query.pb.gw.go new file mode 100644 index 000000000000..c70c3e60b077 --- /dev/null +++ b/x/mint/types/query.pb.gw.go @@ -0,0 +1,272 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/mint/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_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Inflation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryInflationRequest + var metadata runtime.ServerMetadata + + msg, err := client.Inflation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Inflation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryInflationRequest + var metadata runtime.ServerMetadata + + msg, err := server.Inflation(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_AnnualProvisions_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAnnualProvisionsRequest + var metadata runtime.ServerMetadata + + msg, err := client.AnnualProvisions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AnnualProvisions_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAnnualProvisionsRequest + var metadata runtime.ServerMetadata + + msg, err := server.AnnualProvisions(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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Inflation_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_Inflation_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_Inflation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AnnualProvisions_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_AnnualProvisions_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_AnnualProvisions_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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Inflation_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_Inflation_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_Inflation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AnnualProvisions_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_AnnualProvisions_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_AnnualProvisions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Inflation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "inflation"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_AnnualProvisions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "mint", "v1beta1", "annual_provisions"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_Inflation_0 = runtime.ForwardResponseMessage + + forward_Query_AnnualProvisions_0 = runtime.ForwardResponseMessage +) diff --git a/x/mock/app.go b/x/mock/app.go deleted file mode 100644 index 29448e8a9067..000000000000 --- a/x/mock/app.go +++ /dev/null @@ -1,317 +0,0 @@ -package mock - -import ( - "bytes" - "fmt" - "math/rand" - "os" - "sort" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - bam "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/params" -) - -const chainID = "" - -// App extends an ABCI application, but with most of its parameters exported. -// They are exported for convenience in creating helper functions, as object -// capabilities aren't needed for testing. -type App struct { - *bam.BaseApp - Cdc *codec.Codec // Cdc is public since the codec is passed into the module anyways - KeyMain *sdk.KVStoreKey - KeyAccount *sdk.KVStoreKey - KeyParams *sdk.KVStoreKey - TKeyParams *sdk.TransientStoreKey - - // TODO: Abstract this out from not needing to be auth specifically - AccountKeeper auth.AccountKeeper - ParamsKeeper params.Keeper - - GenesisAccounts []authexported.Account - TotalCoinsSupply sdk.Coins -} - -// NewApp partially constructs a new app on the memstore for module and genesis -// testing. -func NewApp() *App { - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") - db := dbm.NewMemDB() - - // Create the cdc with some standard codecs - cdc := createCodec() - - // Create your application object - app := &App{ - BaseApp: bam.NewBaseApp("mock", logger, db, auth.DefaultTxDecoder(cdc)), - Cdc: cdc, - KeyMain: sdk.NewKVStoreKey(bam.MainStoreKey), - KeyAccount: sdk.NewKVStoreKey(auth.StoreKey), - KeyParams: sdk.NewKVStoreKey("params"), - TKeyParams: sdk.NewTransientStoreKey("transient_params"), - TotalCoinsSupply: sdk.NewCoins(), - } - - // define keepers - app.ParamsKeeper = params.NewKeeper(app.Cdc, app.KeyParams, app.TKeyParams) - - app.AccountKeeper = auth.NewAccountKeeper( - app.Cdc, - app.KeyAccount, - app.ParamsKeeper.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, - ) - - supplyKeeper := NewDummySupplyKeeper(app.AccountKeeper) - - // Initialize the app. The chainers and blockers can be overwritten before - // calling complete setup. - app.SetInitChainer(app.InitChainer) - app.SetAnteHandler(auth.NewAnteHandler(app.AccountKeeper, supplyKeeper, auth.DefaultSigVerificationGasConsumer)) - - // Not sealing for custom extension - - return app -} - -// CompleteSetup completes the application setup after the routes have been -// registered. -func (app *App) CompleteSetup(newKeys ...sdk.StoreKey) error { - newKeys = append( - newKeys, - app.KeyMain, app.KeyAccount, app.KeyParams, app.TKeyParams, - ) - - for _, key := range newKeys { - switch key.(type) { - case *sdk.KVStoreKey: - app.MountStore(key, sdk.StoreTypeIAVL) - case *sdk.TransientStoreKey: - app.MountStore(key, sdk.StoreTypeTransient) - default: - return fmt.Errorf("unsupported StoreKey: %+v", key) - } - } - - err := app.LoadLatestVersion(app.KeyMain) - - return err -} - -// InitChainer performs custom logic for initialization. -func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain { - - // Load the genesis accounts - for _, genacc := range app.GenesisAccounts { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, genacc.GetAddress()) - acc.SetCoins(genacc.GetCoins()) - app.AccountKeeper.SetAccount(ctx, acc) - } - - auth.InitGenesis(ctx, app.AccountKeeper, auth.DefaultGenesisState()) - - return abci.ResponseInitChain{} -} - -// Type that combines an Address with the privKey and pubKey to that address -type AddrKeys struct { - Address sdk.AccAddress - PubKey crypto.PubKey - PrivKey crypto.PrivKey -} - -func NewAddrKeys(address sdk.AccAddress, pubKey crypto.PubKey, - privKey crypto.PrivKey) AddrKeys { - - return AddrKeys{ - Address: address, - PubKey: pubKey, - PrivKey: privKey, - } -} - -// implement `Interface` in sort package. -type AddrKeysSlice []AddrKeys - -func (b AddrKeysSlice) Len() int { - return len(b) -} - -// Sorts lexographically by Address -func (b AddrKeysSlice) Less(i, j int) bool { - // bytes package already implements Comparable for []byte. - switch bytes.Compare(b[i].Address.Bytes(), b[j].Address.Bytes()) { - case -1: - return true - case 0, 1: - return false - default: - panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") - } -} - -func (b AddrKeysSlice) Swap(i, j int) { - b[j], b[i] = b[i], b[j] -} - -// CreateGenAccounts generates genesis accounts loaded with coins, and returns -// their addresses, pubkeys, and privkeys. -func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []authexported.Account, - addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { - - addrKeysSlice := AddrKeysSlice{} - - for i := 0; i < numAccs; i++ { - privKey := secp256k1.GenPrivKey() - pubKey := privKey.PubKey() - addr := sdk.AccAddress(pubKey.Address()) - - addrKeysSlice = append(addrKeysSlice, NewAddrKeys(addr, pubKey, privKey)) - } - - sort.Sort(addrKeysSlice) - - for i := range addrKeysSlice { - addrs = append(addrs, addrKeysSlice[i].Address) - pubKeys = append(pubKeys, addrKeysSlice[i].PubKey) - privKeys = append(privKeys, addrKeysSlice[i].PrivKey) - genAccs = append(genAccs, &auth.BaseAccount{ - Address: addrKeysSlice[i].Address, - Coins: genCoins, - }) - } - - return -} - -// SetGenesis sets the mock app genesis accounts. -func SetGenesis(app *App, accs []authexported.Account) { - // Pass the accounts in via the application (lazy) instead of through - // RequestInitChain. - app.GenesisAccounts = accs - - app.InitChain(abci.RequestInitChain{}) - app.Commit() -} - -// GenTx generates a signed mock transaction. -func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKey) auth.StdTx { - // Make the transaction free - fee := auth.StdFee{ - Amount: sdk.NewCoins(sdk.NewInt64Coin("foocoin", 0)), - Gas: 100000, - } - - sigs := make([]auth.StdSignature, len(priv)) - memo := "testmemotestmemo" - - for i, p := range priv { - sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo)) - if err != nil { - panic(err) - } - - sigs[i] = auth.StdSignature{ - PubKey: p.PubKey(), - Signature: sig, - } - } - - return auth.NewStdTx(msgs, fee, sigs, memo) -} - -// GeneratePrivKeys generates a total n secp256k1 private keys. -func GeneratePrivKeys(n int) (keys []crypto.PrivKey) { - // TODO: Randomize this between ed25519 and secp256k1 - keys = make([]crypto.PrivKey, n) - for i := 0; i < n; i++ { - keys[i] = secp256k1.GenPrivKey() - } - - return -} - -// GeneratePrivKeyAddressPairs generates a total of n private key, address -// pairs. -func GeneratePrivKeyAddressPairs(n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) { - keys = make([]crypto.PrivKey, n) - addrs = make([]sdk.AccAddress, n) - for i := 0; i < n; i++ { - if rand.Int63()%2 == 0 { - keys[i] = secp256k1.GenPrivKey() - } else { - keys[i] = ed25519.GenPrivKey() - } - addrs[i] = sdk.AccAddress(keys[i].PubKey().Address()) - } - return -} - -// GeneratePrivKeyAddressPairsFromRand generates a total of n private key, address -// pairs using the provided randomness source. -func GeneratePrivKeyAddressPairsFromRand(rand *rand.Rand, n int) (keys []crypto.PrivKey, addrs []sdk.AccAddress) { - keys = make([]crypto.PrivKey, n) - addrs = make([]sdk.AccAddress, n) - for i := 0; i < n; i++ { - secret := make([]byte, 32) - _, err := rand.Read(secret) - if err != nil { - panic("Could not read randomness") - } - if rand.Int63()%2 == 0 { - keys[i] = secp256k1.GenPrivKeySecp256k1(secret) - } else { - keys[i] = ed25519.GenPrivKeyFromSecret(secret) - } - addrs[i] = sdk.AccAddress(keys[i].PubKey().Address()) - } - return -} - -// RandomSetGenesis set genesis accounts with random coin values using the -// provided addresses and coin denominations. -func RandomSetGenesis(r *rand.Rand, app *App, addrs []sdk.AccAddress, denoms []string) { - accts := make([]authexported.Account, len(addrs)) - randCoinIntervals := []BigInterval{ - {sdk.NewIntWithDecimal(1, 0), sdk.NewIntWithDecimal(1, 1)}, - {sdk.NewIntWithDecimal(1, 2), sdk.NewIntWithDecimal(1, 3)}, - {sdk.NewIntWithDecimal(1, 40), sdk.NewIntWithDecimal(1, 50)}, - } - - for i := 0; i < len(accts); i++ { - coins := make([]sdk.Coin, len(denoms)) - - // generate a random coin for each denomination - for j := 0; j < len(denoms); j++ { - coins[j] = sdk.Coin{Denom: denoms[j], - Amount: RandFromBigInterval(r, randCoinIntervals), - } - } - - app.TotalCoinsSupply = app.TotalCoinsSupply.Add(coins...) - baseAcc := auth.NewBaseAccountWithAddress(addrs[i]) - - (&baseAcc).SetCoins(coins) - accts[i] = &baseAcc - } - app.GenesisAccounts = accts -} - -func createCodec() *codec.Codec { - cdc := codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - auth.RegisterCodec(cdc) - return cdc -} diff --git a/x/mock/app_test.go b/x/mock/app_test.go deleted file mode 100644 index 95bed4fda085..000000000000 --- a/x/mock/app_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package mock - -import ( - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -const msgRoute = "testMsg" - -var ( - numAccts = 2 - genCoins = sdk.Coins{sdk.NewInt64Coin("foocoin", 77)} - accs, addrs, _, privKeys = CreateGenAccounts(numAccts, genCoins) -) - -// testMsg is a mock transaction that has a validation which can fail. -type testMsg struct { - signers []sdk.AccAddress - positiveNum int64 -} - -func (tx testMsg) Route() string { return msgRoute } -func (tx testMsg) Type() string { return "test" } -func (tx testMsg) GetMsg() sdk.Msg { return tx } -func (tx testMsg) GetMemo() string { return "" } -func (tx testMsg) GetSignBytes() []byte { return nil } -func (tx testMsg) GetSigners() []sdk.AccAddress { return tx.signers } -func (tx testMsg) GetSignatures() []auth.StdSignature { return nil } -func (tx testMsg) ValidateBasic() error { - if tx.positiveNum >= 0 { - return nil - } - return sdkerrors.Wrap(sdkerrors.ErrTxDecode, "positiveNum should be a non-negative integer") -} - -// getMockApp returns an initialized mock application. -func getMockApp(t *testing.T) *App { - mApp := NewApp() - - mApp.Router().AddRoute(msgRoute, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - return &sdk.Result{}, nil - }) - require.NoError(t, mApp.CompleteSetup()) - - return mApp -} - -func TestCheckAndDeliverGenTx(t *testing.T) { - mApp := getMockApp(t) - mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil) - mApp.Cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil) - - SetGenesis(mApp, accs) - ctxCheck := mApp.BaseApp.NewContext(true, abci.Header{}) - - msg := testMsg{signers: []sdk.AccAddress{addrs[0]}, positiveNum: 1} - - acct := mApp.AccountKeeper.GetAccount(ctxCheck, addrs[0]) - require.Equal(t, accs[0], acct.(*auth.BaseAccount)) - - header := abci.Header{Height: mApp.LastBlockHeight() + 1} - SignCheckDeliver( - t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{msg}, - []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, - true, true, privKeys[0], - ) - - // Signing a tx with the wrong privKey should result in an auth error - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - _, _, err := SignCheckDeliver( - t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{msg}, - []uint64{accs[1].GetAccountNumber()}, []uint64{accs[1].GetSequence() + 1}, - true, false, privKeys[1], - ) - - // Will fail on SetPubKey decorator - space, code, log := sdkerrors.ABCIInfo(err, false) - require.Equal(t, sdkerrors.ErrInvalidPubKey.ABCICode(), code, log) - require.Equal(t, sdkerrors.ErrInvalidPubKey.Codespace(), space) - - // Resigning the tx with the correct privKey should result in an OK result - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - SignCheckDeliver( - t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{msg}, - []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence() + 1}, - true, true, privKeys[0], - ) -} - -func TestCheckGenTx(t *testing.T) { - mApp := getMockApp(t) - mApp.Cdc.RegisterConcrete(testMsg{}, "mock/testMsg", nil) - mApp.Cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil) - - SetGenesis(mApp, accs) - - msg1 := testMsg{signers: []sdk.AccAddress{addrs[0]}, positiveNum: 1} - CheckGenTx( - t, mApp.BaseApp, []sdk.Msg{msg1}, - []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, - true, privKeys[0], - ) - - msg2 := testMsg{signers: []sdk.AccAddress{addrs[0]}, positiveNum: -1} - CheckGenTx( - t, mApp.BaseApp, []sdk.Msg{msg2}, - []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, - false, privKeys[0], - ) -} diff --git a/x/mock/doc.go b/x/mock/doc.go deleted file mode 100644 index 139cd87a5e97..000000000000 --- a/x/mock/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -/* -Package mock provides utility methods to ease writing tests. -*/ -package mock diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go deleted file mode 100644 index 11073e64f413..000000000000 --- a/x/mock/test_utils.go +++ /dev/null @@ -1,115 +0,0 @@ -package mock - -import ( - "math/big" - "math/rand" - "testing" - - "github.com/cosmos/cosmos-sdk/codec" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// BigInterval is a representation of the interval [lo, hi), where -// lo and hi are both of type sdk.Int -type BigInterval struct { - lo sdk.Int - hi sdk.Int -} - -// RandFromBigInterval chooses an interval uniformly from the provided list of -// BigIntervals, and then chooses an element from an interval uniformly at random. -func RandFromBigInterval(r *rand.Rand, intervals []BigInterval) sdk.Int { - if len(intervals) == 0 { - return sdk.ZeroInt() - } - - interval := intervals[r.Intn(len(intervals))] - - lo := interval.lo - hi := interval.hi - - diff := hi.Sub(lo) - result := sdk.NewIntFromBigInt(new(big.Int).Rand(r, diff.BigInt())) - result = result.Add(lo) - - return result -} - -// CheckBalance checks the balance of an account. -func CheckBalance(t *testing.T, app *App, addr sdk.AccAddress, exp sdk.Coins) { - ctxCheck := app.BaseApp.NewContext(true, abci.Header{}) - res := app.AccountKeeper.GetAccount(ctxCheck, addr) - - require.Equal(t, exp, res.GetCoins()) -} - -// CheckGenTx checks a generated signed transaction. The result of the check is -// compared against the parameter 'expPass'. A test assertion is made using the -// parameter 'expPass' against the result. A corresponding result is returned. -func CheckGenTx( - t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []uint64, - seq []uint64, expPass bool, priv ...crypto.PrivKey, -) (sdk.GasInfo, *sdk.Result, error) { - tx := GenTx(msgs, accNums, seq, priv...) - gInfo, res, err := app.Check(tx) - - if expPass { - require.NoError(t, err) - require.NotNil(t, res) - } else { - require.Error(t, err) - require.Nil(t, res) - } - - return gInfo, res, err -} - -// SignCheckDeliver checks a generated signed transaction and simulates a -// block commitment with the given transaction. A test assertion is made using -// the parameter 'expPass' against the result. A corresponding result is -// returned. -func SignCheckDeliver( - t *testing.T, cdc *codec.Codec, app *baseapp.BaseApp, header abci.Header, msgs []sdk.Msg, - accNums, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, -) (sdk.GasInfo, *sdk.Result, error) { - - tx := GenTx(msgs, accNums, seq, priv...) - - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) - require.Nil(t, err) - - // Must simulate now as CheckTx doesn't run Msgs anymore - _, res, err := app.Simulate(txBytes, tx) - - if expSimPass { - require.NoError(t, err) - require.NotNil(t, res) - } else { - require.Error(t, err) - require.Nil(t, res) - } - - // Simulate a sending a transaction and committing a block - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - - gInfo, res, err := app.Deliver(tx) - - if expPass { - require.NoError(t, err) - require.NotNil(t, res) - } else { - require.Error(t, err) - require.Nil(t, res) - } - - app.EndBlock(abci.RequestEndBlock{}) - app.Commit() - - return gInfo, res, err -} diff --git a/x/mock/types.go b/x/mock/types.go deleted file mode 100644 index 43dfbaecbb94..000000000000 --- a/x/mock/types.go +++ /dev/null @@ -1,80 +0,0 @@ -package mock - -import ( - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/supply" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -// DummySupplyKeeper defines a supply keeper used only for testing to avoid -// circle dependencies -type DummySupplyKeeper struct { - ak auth.AccountKeeper -} - -// NewDummySupplyKeeper creates a DummySupplyKeeper instance -func NewDummySupplyKeeper(ak auth.AccountKeeper) DummySupplyKeeper { - return DummySupplyKeeper{ak} -} - -// SendCoinsFromAccountToModule for the dummy supply keeper -func (sk DummySupplyKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, fromAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { - fromAcc := sk.ak.GetAccount(ctx, fromAddr) - moduleAcc := sk.GetModuleAccount(ctx, recipientModule) - - newFromCoins, hasNeg := fromAcc.GetCoins().SafeSub(amt) - if hasNeg { - return sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, fromAcc.GetCoins().String()) - } - - newToCoins := moduleAcc.GetCoins().Add(amt...) - - if err := fromAcc.SetCoins(newFromCoins); err != nil { - return err - } - - if err := moduleAcc.SetCoins(newToCoins); err != nil { - return err - } - - sk.ak.SetAccount(ctx, fromAcc) - sk.ak.SetAccount(ctx, moduleAcc) - - return nil -} - -// GetModuleAccount for dummy supply keeper -func (sk DummySupplyKeeper) GetModuleAccount(ctx sdk.Context, moduleName string) exported.ModuleAccountI { - addr := sk.GetModuleAddress(moduleName) - - acc := sk.ak.GetAccount(ctx, addr) - if acc != nil { - macc, ok := acc.(exported.ModuleAccountI) - if ok { - return macc - } - } - - moduleAddress := sk.GetModuleAddress(moduleName) - baseAcc := auth.NewBaseAccountWithAddress(moduleAddress) - - // create a new module account - macc := &supply.ModuleAccount{ - BaseAccount: &baseAcc, - Name: moduleName, - Permissions: nil, - } - - maccI := (sk.ak.NewAccount(ctx, macc)).(exported.ModuleAccountI) - sk.ak.SetAccount(ctx, maccI) - return maccI -} - -// GetModuleAddress for dummy supply keeper -func (sk DummySupplyKeeper) GetModuleAddress(moduleName string) sdk.AccAddress { - return sdk.AccAddress(crypto.AddressHash([]byte(moduleName))) -} diff --git a/x/params/alias.go b/x/params/alias.go deleted file mode 100644 index 1198e93cede3..000000000000 --- a/x/params/alias.go +++ /dev/null @@ -1,47 +0,0 @@ -package params - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/params/subspace" - "github.com/cosmos/cosmos-sdk/x/params/types" -) - -const ( - StoreKey = subspace.StoreKey - TStoreKey = subspace.TStoreKey - ModuleName = types.ModuleName - RouterKey = types.RouterKey - ProposalTypeChange = types.ProposalTypeChange -) - -var ( - // functions aliases - NewParamSetPair = subspace.NewParamSetPair - NewSubspace = subspace.NewSubspace - NewKeyTable = subspace.NewKeyTable - RegisterCodec = types.RegisterCodec - ErrUnknownSubspace = types.ErrUnknownSubspace - ErrSettingParameter = types.ErrSettingParameter - ErrEmptyChanges = types.ErrEmptyChanges - ErrEmptySubspace = types.ErrEmptySubspace - ErrEmptyKey = types.ErrEmptyKey - ErrEmptyValue = types.ErrEmptyValue - NewParameterChangeProposal = types.NewParameterChangeProposal - NewParamChange = types.NewParamChange - ValidateChanges = types.ValidateChanges - - // variable aliases - ModuleCdc = types.ModuleCdc -) - -type ( - ParamSetPair = subspace.ParamSetPair - ParamSetPairs = subspace.ParamSetPairs - ParamSet = subspace.ParamSet - Subspace = subspace.Subspace - ReadOnlySubspace = subspace.ReadOnlySubspace - KeyTable = subspace.KeyTable - ParameterChangeProposal = types.ParameterChangeProposal - ParamChange = types.ParamChange -) diff --git a/x/params/client/cli/cli_test.go b/x/params/client/cli/cli_test.go new file mode 100644 index 000000000000..6254a45eaf39 --- /dev/null +++ b/x/params/client/cli/cli_test.go @@ -0,0 +1,87 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/x/params/client/cli" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestNewQuerySubspaceParamsCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{ + "staking", "MaxValidators", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + `{"subspace":"staking","key":"MaxValidators","value":"100"}`, + }, + { + "text output", + []string{ + "staking", "MaxValidators", + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + }, + `key: MaxValidators +subspace: staking +value: "100"`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewQuerySubspaceParamsCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/params/client/cli/query.go b/x/params/client/cli/query.go new file mode 100644 index 000000000000..516ff3b6b474 --- /dev/null +++ b/x/params/client/cli/query.go @@ -0,0 +1,56 @@ +package cli + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +// NewQueryCmd returns a root CLI command handler for all x/params query commands. +func NewQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the params module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand(NewQuerySubspaceParamsCmd()) + + return cmd +} + +// NewQuerySubspaceParamsCmd returns a CLI command handler for querying subspace +// parameters managed by the x/params module. +func NewQuerySubspaceParamsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "subspace [subspace] [key]", + Short: "Query for raw parameters by subspace and key", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := proposal.NewQueryClient(clientCtx) + + params := proposal.QueryParamsRequest{Subspace: args[0], Key: args[1]} + res, err := queryClient.Params(context.Background(), ¶ms) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Param) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/params/client/cli/tx.go b/x/params/client/cli/tx.go index 08dc1c985ee4..39b0242522f4 100644 --- a/x/params/client/cli/tx.go +++ b/x/params/client/cli/tx.go @@ -1,27 +1,24 @@ package cli import ( - "bufio" "fmt" "strings" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "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/version" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" - "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) -// GetCmdSubmitProposal implements a command handler for submitting a parameter -// change proposal transaction. -func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ +// NewSubmitParamChangeProposalTxCmd returns a CLI command handler for creating +// a parameter change proposal governance transaction. +func NewSubmitParamChangeProposalTxCmd() *cobra.Command { + return &cobra.Command{ Use: "param-change [proposal-file]", Args: cobra.ExactArgs(1), Short: "Submit a parameter change proposal", @@ -53,38 +50,41 @@ Where proposal.json contains: "value": 105 } ], - "deposit": [ - { - "denom": "stake", - "amount": "10000" - } - ] + "deposit": "1000stake" } `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - proposal, err := paramscutils.ParseParamChangeProposalJSON(cdc, args[0]) + clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } + proposal, err := paramscutils.ParseParamChangeProposalJSON(clientCtx.LegacyAmino, args[0]) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + content := paramproposal.NewParameterChangeProposal( + proposal.Title, proposal.Description, proposal.Changes.ToParamChanges(), + ) - from := cliCtx.GetFromAddress() - content := types.NewParameterChangeProposal(proposal.Title, proposal.Description, proposal.Changes.ToParamChanges()) + deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit) + if err != nil { + return err + } - msg := govtypes.NewMsgSubmitProposal(content, proposal.Deposit, from) + msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from) + if err != nil { + return err + } if err := msg.ValidateBasic(); err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - - return cmd } diff --git a/x/params/client/cli/tx_test.go b/x/params/client/cli/tx_test.go new file mode 100644 index 000000000000..215fb41c8202 --- /dev/null +++ b/x/params/client/cli/tx_test.go @@ -0,0 +1,42 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/x/params/client/utils" +) + +func TestParseProposal(t *testing.T) { + cdc := codec.NewLegacyAmino() + okJSON := testutil.WriteToNewTempFile(t, ` +{ + "title": "Staking Param Change", + "description": "Update max validators", + "changes": [ + { + "subspace": "staking", + "key": "MaxValidators", + "value": 1 + } + ], + "deposit": "1000stake" +} +`) + proposal, err := utils.ParseParamChangeProposalJSON(cdc, okJSON.Name()) + require.NoError(t, err) + + require.Equal(t, "Staking Param Change", proposal.Title) + require.Equal(t, "Update max validators", proposal.Description) + require.Equal(t, "1000stake", proposal.Deposit) + require.Equal(t, utils.ParamChangesJSON{ + { + Subspace: "staking", + Key: "MaxValidators", + Value: []byte{0x31}, + }, + }, proposal.Changes) +} diff --git a/x/params/client/proposal_handler.go b/x/params/client/proposal_handler.go index 040bebdf448f..77a672d89c4e 100644 --- a/x/params/client/proposal_handler.go +++ b/x/params/client/proposal_handler.go @@ -6,5 +6,5 @@ import ( "github.com/cosmos/cosmos-sdk/x/params/client/rest" ) -// param change proposal handler -var ProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitProposal, rest.ProposalRESTHandler) +// ProposalHandler is the param change proposal handler. +var ProposalHandler = govclient.NewProposalHandler(cli.NewSubmitParamChangeProposalTxCmd, rest.ProposalRESTHandler) diff --git a/x/params/client/rest/grpc_query_test.go b/x/params/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..af00834183b9 --- /dev/null +++ b/x/params/client/rest/grpc_query_test.go @@ -0,0 +1,130 @@ +package rest_test + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestQueryParamsGRPC() { + 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 + }{ + { + "with no subspace, key", + fmt.Sprintf("%s/cosmos/params/v1beta1/params?subspace=%s&key=%s", baseURL, "", ""), + map[string]string{}, + true, + &proposal.QueryParamsResponse{}, + &proposal.QueryParamsResponse{ + Param: proposal.ParamChange{ + Subspace: "staking", + Key: "MaxValidators", + Value: "100", + }, + }, + }, + { + "with wrong subspace", + fmt.Sprintf("%s/cosmos/params/v1beta1/params?subspace=%s&key=%s", baseURL, "wrongSubspace", "MaxValidators"), + map[string]string{}, + true, + &proposal.QueryParamsResponse{}, + &proposal.QueryParamsResponse{ + Param: proposal.ParamChange{ + Subspace: "staking", + Key: "MaxValidators", + Value: "100", + }, + }, + }, + { + "with wrong key", + fmt.Sprintf("%s/cosmos/params/v1beta1/params?subspace=%s&key=%s", baseURL, "staking", "wrongKey"), + map[string]string{}, + false, + &proposal.QueryParamsResponse{}, + &proposal.QueryParamsResponse{ + Param: proposal.ParamChange{ + Subspace: "staking", + Key: "wrongKey", + Value: "", + }, + }, + }, + { + "params", + fmt.Sprintf("%s/cosmos/params/v1beta1/params?subspace=%s&key=%s", baseURL, "staking", "MaxValidators"), + map[string]string{}, + false, + &proposal.QueryParamsResponse{}, + &proposal.QueryParamsResponse{ + Param: proposal.ParamChange{ + Subspace: "staking", + Key: "MaxValidators", + Value: "100", + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/params/client/rest/rest.go b/x/params/client/rest/rest.go index 101dc49a4d7e..70d90236e05f 100644 --- a/x/params/client/rest/rest.go +++ b/x/params/client/rest/rest.go @@ -3,29 +3,28 @@ package rest import ( "net/http" - "github.com/cosmos/cosmos-sdk/client/context" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/params" paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) // ProposalRESTHandler returns a ProposalRESTHandler that exposes the param // change REST handler with a given sub-route. -func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler { +func ProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler { return govrest.ProposalRESTHandler{ SubRoute: "param_change", - Handler: postProposalHandlerFn(cliCtx), + Handler: postProposalHandlerFn(clientCtx), } } -func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func postProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req paramscutils.ParamChangeProposalReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -34,14 +33,16 @@ func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - content := params.NewParameterChangeProposal(req.Title, req.Description, req.Changes.ToParamChanges()) + content := proposal.NewParameterChangeProposal(req.Title, req.Description, req.Changes.ToParamChanges()) - msg := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/params/client/utils/utils.go b/x/params/client/utils/utils.go index 748d98502dc3..b4c680fdde24 100644 --- a/x/params/client/utils/utils.go +++ b/x/params/client/utils/utils.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) type ( @@ -29,7 +29,7 @@ type ( Title string `json:"title" yaml:"title"` Description string `json:"description" yaml:"description"` Changes ParamChangesJSON `json:"changes" yaml:"changes"` - Deposit sdk.Coins `json:"deposit" yaml:"deposit"` + Deposit string `json:"deposit" yaml:"deposit"` } // ParamChangeProposalReq defines a parameter change proposal request body. @@ -49,14 +49,14 @@ func NewParamChangeJSON(subspace, key string, value json.RawMessage) ParamChange } // ToParamChange converts a ParamChangeJSON object to ParamChange. -func (pcj ParamChangeJSON) ToParamChange() params.ParamChange { - return params.NewParamChange(pcj.Subspace, pcj.Key, string(pcj.Value)) +func (pcj ParamChangeJSON) ToParamChange() proposal.ParamChange { + return proposal.NewParamChange(pcj.Subspace, pcj.Key, string(pcj.Value)) } // ToParamChanges converts a slice of ParamChangeJSON objects to a slice of // ParamChange. -func (pcj ParamChangesJSON) ToParamChanges() []params.ParamChange { - res := make([]params.ParamChange, len(pcj)) +func (pcj ParamChangesJSON) ToParamChanges() []proposal.ParamChange { + res := make([]proposal.ParamChange, len(pcj)) for i, pc := range pcj { res[i] = pc.ToParamChange() } @@ -65,7 +65,7 @@ func (pcj ParamChangesJSON) ToParamChanges() []params.ParamChange { // ParseParamChangeProposalJSON reads and parses a ParamChangeProposalJSON from // file. -func ParseParamChangeProposalJSON(cdc *codec.Codec, proposalFile string) (ParamChangeProposalJSON, error) { +func ParseParamChangeProposalJSON(cdc *codec.LegacyAmino, proposalFile string) (ParamChangeProposalJSON, error) { proposal := ParamChangeProposalJSON{} contents, err := ioutil.ReadFile(proposalFile) diff --git a/x/params/commmon_test.go b/x/params/commmon_test.go deleted file mode 100644 index ecebf8eb2e60..000000000000 --- a/x/params/commmon_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// nolint:deadcode,unused -package params - -import ( - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type invalid struct{} - -type s struct { - I int -} - -func createTestCodec() *codec.Codec { - cdc := codec.New() - sdk.RegisterCodec(cdc) - cdc.RegisterConcrete(s{}, "test/s", nil) - 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, abci.Header{}, false, log.NewNopLogger()) - return ctx -} - -func testComponents() (*codec.Codec, sdk.Context, sdk.StoreKey, sdk.StoreKey, Keeper) { - cdc := createTestCodec() - mkey := sdk.NewKVStoreKey("test") - tkey := sdk.NewTransientStoreKey("transient_test") - ctx := defaultContext(mkey, tkey) - keeper := NewKeeper(cdc, mkey, tkey) - - return cdc, ctx, mkey, tkey, keeper -} diff --git a/x/params/keeper.go b/x/params/keeper.go deleted file mode 100644 index c3532f32834f..000000000000 --- a/x/params/keeper.go +++ /dev/null @@ -1,61 +0,0 @@ -package params - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params/subspace" - "github.com/cosmos/cosmos-sdk/x/params/types" - - "github.com/tendermint/tendermint/libs/log" -) - -// Keeper of the global paramstore -type Keeper struct { - cdc *codec.Codec - key sdk.StoreKey - tkey sdk.StoreKey - spaces map[string]*Subspace -} - -// NewKeeper constructs a params keeper -func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey) Keeper { - return Keeper{ - cdc: cdc, - key: key, - tkey: tkey, - spaces: make(map[string]*Subspace), - } -} - -// 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)) -} - -// Allocate subspace used for keepers -func (k Keeper) Subspace(s string) Subspace { - _, ok := k.spaces[s] - if ok { - panic("subspace already occupied") - } - - if s == "" { - panic("cannot use empty string for subspace") - } - - space := subspace.NewSubspace(k.cdc, k.key, k.tkey, s) - k.spaces[s] = &space - - return space -} - -// Get existing substore from keeper -func (k Keeper) GetSubspace(s string) (Subspace, bool) { - space, ok := k.spaces[s] - if !ok { - return Subspace{}, false - } - return *space, ok -} diff --git a/x/params/keeper/common_test.go b/x/params/keeper/common_test.go new file mode 100644 index 000000000000..f6d567db11f7 --- /dev/null +++ b/x/params/keeper/common_test.go @@ -0,0 +1,52 @@ +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" + sdk "github.com/cosmos/cosmos-sdk/types" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" +) + +func testComponents() (*codec.LegacyAmino, sdk.Context, sdk.StoreKey, sdk.StoreKey, paramskeeper.Keeper) { + marshaler := simapp.MakeTestEncodingConfig().Marshaler + legacyAmino := createTestCodec() + mkey := sdk.NewKVStoreKey("test") + tkey := sdk.NewTransientStoreKey("transient_test") + ctx := defaultContext(mkey, tkey) + keeper := paramskeeper.NewKeeper(marshaler, legacyAmino, mkey, tkey) + + return legacyAmino, ctx, mkey, tkey, keeper +} + +type invalid struct{} + +type s struct { + I int +} + +func createTestCodec() *codec.LegacyAmino { + cdc := codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cdc.RegisterConcrete(s{}, "test/s", nil) + 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/keeper/consensus_params.go b/x/params/keeper/consensus_params.go new file mode 100644 index 000000000000..5ce8d340d0d9 --- /dev/null +++ b/x/params/keeper/consensus_params.go @@ -0,0 +1,28 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// ConsensusParamsKeyTable returns an x/params module keyTable to be used in +// the BaseApp's ParamStore. The KeyTable registers the types along with the +// standard validation functions. Applications can choose to adopt this KeyTable +// or provider their own when the existing validation functions do not suite their +// needs. +func ConsensusParamsKeyTable() types.KeyTable { + return types.NewKeyTable( + types.NewParamSetPair( + baseapp.ParamStoreKeyBlockParams, abci.BlockParams{}, baseapp.ValidateBlockParams, + ), + types.NewParamSetPair( + baseapp.ParamStoreKeyEvidenceParams, tmproto.EvidenceParams{}, baseapp.ValidateEvidenceParams, + ), + types.NewParamSetPair( + baseapp.ParamStoreKeyValidatorParams, tmproto.ValidatorParams{}, baseapp.ValidateValidatorParams, + ), + ) +} diff --git a/x/params/keeper/grpc_query.go b/x/params/keeper/grpc_query.go new file mode 100644 index 000000000000..2cfa1c8042d7 --- /dev/null +++ b/x/params/keeper/grpc_query.go @@ -0,0 +1,36 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +var _ proposal.QueryServer = Keeper{} + +// Params returns subspace params +func (k Keeper) Params(c context.Context, req *proposal.QueryParamsRequest) (*proposal.QueryParamsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.Subspace == "" || req.Key == "" { + return nil, status.Errorf(codes.InvalidArgument, "invalid request") + } + + ss, ok := k.GetSubspace(req.Subspace) + if !ok { + return nil, sdkerrors.Wrap(proposal.ErrUnknownSubspace, req.Subspace) + } + + ctx := sdk.UnwrapSDKContext(c) + rawValue := ss.GetRaw(ctx, []byte(req.Key)) + param := proposal.NewParamChange(req.Subspace, req.Key, string(rawValue)) + + return &proposal.QueryParamsResponse{Param: param}, nil +} diff --git a/x/params/keeper/grpc_query_test.go b/x/params/keeper/grpc_query_test.go new file mode 100644 index 000000000000..19794beb0808 --- /dev/null +++ b/x/params/keeper/grpc_query_test.go @@ -0,0 +1,86 @@ +package keeper_test + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +func (suite *KeeperTestSuite) TestGRPCQueryParams() { + var ( + req *proposal.QueryParamsRequest + expValue string + space types.Subspace + ) + key := []byte("key") + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &proposal.QueryParamsRequest{} + }, + false, + }, + { + "invalid request with subspace not found", + func() { + req = &proposal.QueryParamsRequest{Subspace: "test"} + }, + false, + }, + { + "invalid request with subspace and key not found", + func() { + req = &proposal.QueryParamsRequest{Subspace: "test", Key: "key"} + }, + false, + }, + { + "success", + func() { + space = suite.app.ParamsKeeper.Subspace("test"). + WithKeyTable(types.NewKeyTable(types.NewParamSetPair(key, paramJSON{}, validateNoOp))) + req = &proposal.QueryParamsRequest{Subspace: "test", Key: "key"} + expValue = "" + }, + true, + }, + { + "update value success", + func() { + err := space.Update(suite.ctx, key, []byte(`{"param1":"10241024"}`)) + suite.Require().NoError(err) + req = &proposal.QueryParamsRequest{Subspace: "test", Key: "key"} + expValue = `{"param1":"10241024"}` + }, + true, + }, + } + + suite.SetupTest() + ctx := sdk.WrapSDKContext(suite.ctx) + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + + res, err := suite.queryClient.Params(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expValue, res.Param.Value) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +} diff --git a/x/params/keeper/keeper.go b/x/params/keeper/keeper.go new file mode 100644 index 000000000000..b3d649a2ec49 --- /dev/null +++ b/x/params/keeper/keeper.go @@ -0,0 +1,61 @@ +package keeper + +import ( + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +// Keeper of the global paramstore +type Keeper struct { + cdc codec.BinaryMarshaler + legacyAmino *codec.LegacyAmino + key sdk.StoreKey + tkey sdk.StoreKey + spaces map[string]*types.Subspace +} + +// NewKeeper constructs a params keeper +func NewKeeper(cdc codec.BinaryMarshaler, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey) Keeper { + return Keeper{ + cdc: cdc, + legacyAmino: legacyAmino, + key: key, + tkey: tkey, + spaces: make(map[string]*types.Subspace), + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+proposal.ModuleName) +} + +// Allocate subspace used for keepers +func (k Keeper) Subspace(s string) types.Subspace { + _, ok := k.spaces[s] + if ok { + panic("subspace already occupied") + } + + if s == "" { + panic("cannot use empty string for subspace") + } + + space := types.NewSubspace(k.cdc, k.legacyAmino, k.key, k.tkey, s) + k.spaces[s] = &space + + return space +} + +// Get existing substore from keeper +func (k Keeper) GetSubspace(s string) (types.Subspace, bool) { + space, ok := k.spaces[s] + if !ok { + return types.Subspace{}, false + } + return *space, ok +} diff --git a/x/params/keeper/keeper_test.go b/x/params/keeper/keeper_test.go new file mode 100644 index 000000000000..44c8223f2c25 --- /dev/null +++ b/x/params/keeper/keeper_test.go @@ -0,0 +1,263 @@ +package keeper_test + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "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/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + + queryClient proposal.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.app, suite.ctx = createTestApp(true) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) + proposal.RegisterQueryServer(queryHelper, suite.app.ParamsKeeper) + suite.queryClient = proposal.NewQueryClient(queryHelper) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +// returns context and app +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + + return app, ctx +} + +func validateNoOp(_ interface{}) error { return nil } + +func TestKeeper(t *testing.T) { + kvs := []struct { + key string + param int64 + }{ + {"key1", 10}, + {"key2", 55}, + {"key3", 182}, + {"key4", 17582}, + {"key5", 2768554}, + {"key6", 1157279}, + {"key7", 9058701}, + } + + table := types.NewKeyTable( + types.NewParamSetPair([]byte("key1"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key2"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key3"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key4"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key5"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key6"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("key7"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("extra1"), bool(false), validateNoOp), + types.NewParamSetPair([]byte("extra2"), string(""), validateNoOp), + ) + + cdc, ctx, skey, _, keeper := testComponents() + + store := prefix.NewStore(ctx.KVStore(skey), []byte("test/")) + space := keeper.Subspace("test") + require.False(t, space.HasKeyTable()) + space = space.WithKeyTable(table) + require.True(t, space.HasKeyTable()) + + // Set params + for i, kv := range kvs { + kv := kv + require.NotPanics(t, func() { space.Set(ctx, []byte(kv.key), kv.param) }, "space.Set panics, tc #%d", i) + } + + // Test space.Get + for i, kv := range kvs { + i, kv := i, kv + var param int64 + require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }, "space.Get panics, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test space.GetRaw + for i, kv := range kvs { + var param int64 + bz := space.GetRaw(ctx, []byte(kv.key)) + err := cdc.UnmarshalJSON(bz, ¶m) + require.Nil(t, err, "err is not nil, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test store.Get equals space.Get + for i, kv := range kvs { + var param int64 + bz := store.Get([]byte(kv.key)) + require.NotNil(t, bz, "KVStore.Get returns nil, tc #%d", i) + err := cdc.UnmarshalJSON(bz, ¶m) + require.NoError(t, err, "UnmarshalJSON returns error, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test invalid space.Get + for i, kv := range kvs { + kv := kv + var param bool + require.Panics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }, "invalid space.Get not panics, tc #%d", i) + } + + // Test invalid space.Set + for i, kv := range kvs { + kv := kv + require.Panics(t, func() { space.Set(ctx, []byte(kv.key), true) }, "invalid space.Set not panics, tc #%d", i) + } + + // Test GetSubspace + for i, kv := range kvs { + i, kv := i, kv + var gparam, param int64 + gspace, ok := keeper.GetSubspace("test") + require.True(t, ok, "cannot retrieve subspace, tc #%d", i) + + require.NotPanics(t, func() { gspace.Get(ctx, []byte(kv.key), &gparam) }) + require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }) + require.Equal(t, gparam, param, "GetSubspace().Get not equal with space.Get, tc #%d", i) + + require.NotPanics(t, func() { gspace.Set(ctx, []byte(kv.key), int64(i)) }) + require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }) + require.Equal(t, int64(i), param, "GetSubspace().Set not equal with space.Get, tc #%d", i) + } +} + +func indirect(ptr interface{}) interface{} { + return reflect.ValueOf(ptr).Elem().Interface() +} + +func TestSubspace(t *testing.T) { + cdc, ctx, key, _, keeper := testComponents() + + kvs := []struct { + key string + param interface{} + zero interface{} + ptr interface{} + }{ + {"string", "test", "", new(string)}, + {"bool", true, false, new(bool)}, + {"int16", int16(1), int16(0), new(int16)}, + {"int32", int32(1), int32(0), new(int32)}, + {"int64", int64(1), int64(0), new(int64)}, + {"uint16", uint16(1), uint16(0), new(uint16)}, + {"uint32", uint32(1), uint32(0), new(uint32)}, + {"uint64", uint64(1), uint64(0), new(uint64)}, + {"int", sdk.NewInt(1), *new(sdk.Int), new(sdk.Int)}, + {"uint", sdk.NewUint(1), *new(sdk.Uint), new(sdk.Uint)}, + {"dec", sdk.NewDec(1), *new(sdk.Dec), new(sdk.Dec)}, + {"struct", s{1}, s{0}, new(s)}, + } + + table := types.NewKeyTable( + types.NewParamSetPair([]byte("string"), "", validateNoOp), + types.NewParamSetPair([]byte("bool"), false, validateNoOp), + types.NewParamSetPair([]byte("int16"), int16(0), validateNoOp), + types.NewParamSetPair([]byte("int32"), int32(0), validateNoOp), + types.NewParamSetPair([]byte("int64"), int64(0), validateNoOp), + types.NewParamSetPair([]byte("uint16"), uint16(0), validateNoOp), + types.NewParamSetPair([]byte("uint32"), uint32(0), validateNoOp), + types.NewParamSetPair([]byte("uint64"), uint64(0), validateNoOp), + types.NewParamSetPair([]byte("int"), sdk.Int{}, validateNoOp), + types.NewParamSetPair([]byte("uint"), sdk.Uint{}, validateNoOp), + types.NewParamSetPair([]byte("dec"), sdk.Dec{}, validateNoOp), + types.NewParamSetPair([]byte("struct"), s{}, validateNoOp), + ) + + store := prefix.NewStore(ctx.KVStore(key), []byte("test/")) + space := keeper.Subspace("test").WithKeyTable(table) + + // Test space.Set, space.Modified + for i, kv := range kvs { + i, kv := i, kv + require.False(t, space.Modified(ctx, []byte(kv.key)), "space.Modified returns true before setting, tc #%d", i) + require.NotPanics(t, func() { space.Set(ctx, []byte(kv.key), kv.param) }, "space.Set panics, tc #%d", i) + require.True(t, space.Modified(ctx, []byte(kv.key)), "space.Modified returns false after setting, tc #%d", i) + } + + // Test space.Get, space.GetIfExists + for i, kv := range kvs { + i, kv := i, kv + require.NotPanics(t, func() { space.GetIfExists(ctx, []byte("invalid"), kv.ptr) }, "space.GetIfExists panics when no value exists, tc #%d", i) + require.Equal(t, kv.zero, indirect(kv.ptr), "space.GetIfExists unmarshalls when no value exists, tc #%d", i) + require.Panics(t, func() { space.Get(ctx, []byte("invalid"), kv.ptr) }, "invalid space.Get not panics when no value exists, tc #%d", i) + require.Equal(t, kv.zero, indirect(kv.ptr), "invalid space.Get unmarshalls when no value exists, tc #%d", i) + + require.NotPanics(t, func() { space.GetIfExists(ctx, []byte(kv.key), kv.ptr) }, "space.GetIfExists panics, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), kv.ptr) }, "space.Get panics, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + + require.Panics(t, func() { space.Get(ctx, []byte("invalid"), kv.ptr) }, "invalid space.Get not panics when no value exists, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "invalid space.Get unmarshalls when no value existt, tc #%d", i) + + require.Panics(t, func() { space.Get(ctx, []byte(kv.key), nil) }, "invalid space.Get not panics when the pointer is nil, tc #%d", i) + require.Panics(t, func() { space.Get(ctx, []byte(kv.key), new(invalid)) }, "invalid space.Get not panics when the pointer is different type, tc #%d", i) + } + + // Test store.Get equals space.Get + for i, kv := range kvs { + bz := store.Get([]byte(kv.key)) + require.NotNil(t, bz, "store.Get() returns nil, tc #%d", i) + err := cdc.UnmarshalJSON(bz, kv.ptr) + require.NoError(t, err, "cdc.UnmarshalJSON() returns error, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + } +} + +type paramJSON struct { + Param1 int64 `json:"param1,omitempty" yaml:"param1,omitempty"` + Param2 string `json:"param2,omitempty" yaml:"param2,omitempty"` +} + +func TestJSONUpdate(t *testing.T) { + _, ctx, _, _, keeper := testComponents() + + key := []byte("key") + + space := keeper.Subspace("test").WithKeyTable(types.NewKeyTable(types.NewParamSetPair(key, paramJSON{}, validateNoOp))) + + var param paramJSON + + err := space.Update(ctx, key, []byte(`{"param1": "10241024"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{10241024, ""}, param) + + err = space.Update(ctx, key, []byte(`{"param2": "helloworld"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{10241024, "helloworld"}, param) + + err = space.Update(ctx, key, []byte(`{"param1": "20482048"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{20482048, "helloworld"}, param) + + err = space.Update(ctx, key, []byte(`{"param1": "40964096", "param2": "goodbyeworld"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{40964096, "goodbyeworld"}, param) +} diff --git a/x/params/keeper/querier.go b/x/params/keeper/querier.go new file mode 100644 index 000000000000..9d6f3c3da69f --- /dev/null +++ b/x/params/keeper/querier.go @@ -0,0 +1,47 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "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/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +// NewQuerier returns a new querier handler for the x/params module. +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + case types.QueryParams: + return queryParams(ctx, req, k, legacyQuerierCdc) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query path: %s", path[0]) + } + } +} + +func queryParams(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QuerySubspaceParams + + if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + ss, ok := k.GetSubspace(params.Subspace) + if !ok { + return nil, sdkerrors.Wrap(proposal.ErrUnknownSubspace, params.Subspace) + } + + rawValue := ss.GetRaw(ctx, []byte(params.Key)) + resp := types.NewSubspaceParamsResponse(params.Subspace, params.Key, string(rawValue)) + + bz, err := codec.MarshalJSONIndent(legacyQuerierCdc, resp) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go deleted file mode 100644 index 3f2f1a4b2bf4..000000000000 --- a/x/params/keeper_test.go +++ /dev/null @@ -1,224 +0,0 @@ -package params - -import ( - "reflect" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func validateNoOp(_ interface{}) error { return nil } - -func TestKeeper(t *testing.T) { - kvs := []struct { - key string - param int64 - }{ - {"key1", 10}, - {"key2", 55}, - {"key3", 182}, - {"key4", 17582}, - {"key5", 2768554}, - {"key6", 1157279}, - {"key7", 9058701}, - } - - table := NewKeyTable( - NewParamSetPair([]byte("key1"), int64(0), validateNoOp), - NewParamSetPair([]byte("key2"), int64(0), validateNoOp), - NewParamSetPair([]byte("key3"), int64(0), validateNoOp), - NewParamSetPair([]byte("key4"), int64(0), validateNoOp), - NewParamSetPair([]byte("key5"), int64(0), validateNoOp), - NewParamSetPair([]byte("key6"), int64(0), validateNoOp), - NewParamSetPair([]byte("key7"), int64(0), validateNoOp), - NewParamSetPair([]byte("extra1"), bool(false), validateNoOp), - NewParamSetPair([]byte("extra2"), string(""), validateNoOp), - ) - - cdc, ctx, skey, _, keeper := testComponents() - - store := prefix.NewStore(ctx.KVStore(skey), []byte("test/")) - space := keeper.Subspace("test") - require.False(t, space.HasKeyTable()) - space = space.WithKeyTable(table) - require.True(t, space.HasKeyTable()) - - // Set params - for i, kv := range kvs { - kv := kv - require.NotPanics(t, func() { space.Set(ctx, []byte(kv.key), kv.param) }, "space.Set panics, tc #%d", i) - } - - // Test space.Get - for i, kv := range kvs { - i, kv := i, kv - var param int64 - require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }, "space.Get panics, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test space.GetRaw - for i, kv := range kvs { - var param int64 - bz := space.GetRaw(ctx, []byte(kv.key)) - err := cdc.UnmarshalJSON(bz, ¶m) - require.Nil(t, err, "err is not nil, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test store.Get equals space.Get - for i, kv := range kvs { - var param int64 - bz := store.Get([]byte(kv.key)) - require.NotNil(t, bz, "KVStore.Get returns nil, tc #%d", i) - err := cdc.UnmarshalJSON(bz, ¶m) - require.NoError(t, err, "UnmarshalJSON returns error, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test invalid space.Get - for i, kv := range kvs { - kv := kv - var param bool - require.Panics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }, "invalid space.Get not panics, tc #%d", i) - } - - // Test invalid space.Set - for i, kv := range kvs { - kv := kv - require.Panics(t, func() { space.Set(ctx, []byte(kv.key), true) }, "invalid space.Set not panics, tc #%d", i) - } - - // Test GetSubspace - for i, kv := range kvs { - i, kv := i, kv - var gparam, param int64 - gspace, ok := keeper.GetSubspace("test") - require.True(t, ok, "cannot retrieve subspace, tc #%d", i) - - require.NotPanics(t, func() { gspace.Get(ctx, []byte(kv.key), &gparam) }) - require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }) - require.Equal(t, gparam, param, "GetSubspace().Get not equal with space.Get, tc #%d", i) - - require.NotPanics(t, func() { gspace.Set(ctx, []byte(kv.key), int64(i)) }) - require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), ¶m) }) - require.Equal(t, int64(i), param, "GetSubspace().Set not equal with space.Get, tc #%d", i) - } -} - -func indirect(ptr interface{}) interface{} { - return reflect.ValueOf(ptr).Elem().Interface() -} - -func TestSubspace(t *testing.T) { - cdc, ctx, key, _, keeper := testComponents() - - kvs := []struct { - key string - param interface{} - zero interface{} - ptr interface{} - }{ - {"string", "test", "", new(string)}, - {"bool", true, false, new(bool)}, - {"int16", int16(1), int16(0), new(int16)}, - {"int32", int32(1), int32(0), new(int32)}, - {"int64", int64(1), int64(0), new(int64)}, - {"uint16", uint16(1), uint16(0), new(uint16)}, - {"uint32", uint32(1), uint32(0), new(uint32)}, - {"uint64", uint64(1), uint64(0), new(uint64)}, - {"int", sdk.NewInt(1), *new(sdk.Int), new(sdk.Int)}, - {"uint", sdk.NewUint(1), *new(sdk.Uint), new(sdk.Uint)}, - {"dec", sdk.NewDec(1), *new(sdk.Dec), new(sdk.Dec)}, - {"struct", s{1}, s{0}, new(s)}, - } - - table := NewKeyTable( - NewParamSetPair([]byte("string"), string(""), validateNoOp), - NewParamSetPair([]byte("bool"), bool(false), validateNoOp), - NewParamSetPair([]byte("int16"), int16(0), validateNoOp), - NewParamSetPair([]byte("int32"), int32(0), validateNoOp), - NewParamSetPair([]byte("int64"), int64(0), validateNoOp), - NewParamSetPair([]byte("uint16"), uint16(0), validateNoOp), - NewParamSetPair([]byte("uint32"), uint32(0), validateNoOp), - NewParamSetPair([]byte("uint64"), uint64(0), validateNoOp), - NewParamSetPair([]byte("int"), sdk.Int{}, validateNoOp), - NewParamSetPair([]byte("uint"), sdk.Uint{}, validateNoOp), - NewParamSetPair([]byte("dec"), sdk.Dec{}, validateNoOp), - NewParamSetPair([]byte("struct"), s{}, validateNoOp), - ) - - store := prefix.NewStore(ctx.KVStore(key), []byte("test/")) - space := keeper.Subspace("test").WithKeyTable(table) - - // Test space.Set, space.Modified - for i, kv := range kvs { - i, kv := i, kv - require.False(t, space.Modified(ctx, []byte(kv.key)), "space.Modified returns true before setting, tc #%d", i) - require.NotPanics(t, func() { space.Set(ctx, []byte(kv.key), kv.param) }, "space.Set panics, tc #%d", i) - require.True(t, space.Modified(ctx, []byte(kv.key)), "space.Modified returns false after setting, tc #%d", i) - } - - // Test space.Get, space.GetIfExists - for i, kv := range kvs { - i, kv := i, kv - require.NotPanics(t, func() { space.GetIfExists(ctx, []byte("invalid"), kv.ptr) }, "space.GetIfExists panics when no value exists, tc #%d", i) - require.Equal(t, kv.zero, indirect(kv.ptr), "space.GetIfExists unmarshalls when no value exists, tc #%d", i) - require.Panics(t, func() { space.Get(ctx, []byte("invalid"), kv.ptr) }, "invalid space.Get not panics when no value exists, tc #%d", i) - require.Equal(t, kv.zero, indirect(kv.ptr), "invalid space.Get unmarshalls when no value exists, tc #%d", i) - - require.NotPanics(t, func() { space.GetIfExists(ctx, []byte(kv.key), kv.ptr) }, "space.GetIfExists panics, tc #%d", i) - require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) - require.NotPanics(t, func() { space.Get(ctx, []byte(kv.key), kv.ptr) }, "space.Get panics, tc #%d", i) - require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) - - require.Panics(t, func() { space.Get(ctx, []byte("invalid"), kv.ptr) }, "invalid space.Get not panics when no value exists, tc #%d", i) - require.Equal(t, kv.param, indirect(kv.ptr), "invalid space.Get unmarshalls when no value existt, tc #%d", i) - - require.Panics(t, func() { space.Get(ctx, []byte(kv.key), nil) }, "invalid space.Get not panics when the pointer is nil, tc #%d", i) - require.Panics(t, func() { space.Get(ctx, []byte(kv.key), new(invalid)) }, "invalid space.Get not panics when the pointer is different type, tc #%d", i) - } - - // Test store.Get equals space.Get - for i, kv := range kvs { - bz := store.Get([]byte(kv.key)) - require.NotNil(t, bz, "store.Get() returns nil, tc #%d", i) - err := cdc.UnmarshalJSON(bz, kv.ptr) - require.NoError(t, err, "cdc.UnmarshalJSON() returns error, tc #%d", i) - require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) - } -} - -type paramJSON struct { - Param1 int64 `json:"param1,omitempty" yaml:"param1,omitempty"` - Param2 string `json:"param2,omitempty" yaml:"param2,omitempty"` -} - -func TestJSONUpdate(t *testing.T) { - _, ctx, _, _, keeper := testComponents() - - key := []byte("key") - - space := keeper.Subspace("test").WithKeyTable(NewKeyTable(NewParamSetPair(key, paramJSON{}, validateNoOp))) - - var param paramJSON - - space.Update(ctx, key, []byte(`{"param1": "10241024"}`)) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{10241024, ""}, param) - - space.Update(ctx, key, []byte(`{"param2": "helloworld"}`)) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{10241024, "helloworld"}, param) - - space.Update(ctx, key, []byte(`{"param1": "20482048"}`)) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{20482048, "helloworld"}, param) - - space.Update(ctx, key, []byte(`{"param1": "40964096", "param2": "goodbyeworld"}`)) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{40964096, "goodbyeworld"}, param) -} diff --git a/x/params/legacy/v036/types.go b/x/params/legacy/v036/types.go new file mode 100644 index 000000000000..66cc9e7d8aa9 --- /dev/null +++ b/x/params/legacy/v036/types.go @@ -0,0 +1,172 @@ +package v036 + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" +) + +const ( + // ModuleName defines the name of the module + ModuleName = "params" + + // RouterKey defines the routing key for a ParameterChangeProposal + RouterKey = "params" +) + +const ( + // ProposalTypeChange defines the type for a ParameterChangeProposal + ProposalTypeChange = "ParameterChange" +) + +// Param module codespace constants +const ( + DefaultCodespace = "params" + + CodeUnknownSubspace = 1 + CodeSettingParameter = 2 + CodeEmptyData = 3 +) + +// Assert ParameterChangeProposal implements v036gov.Content at compile-time +var _ v036gov.Content = ParameterChangeProposal{} + +// ParameterChangeProposal defines a proposal which contains multiple parameter +// changes. +type ParameterChangeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Changes []ParamChange `json:"changes" yaml:"changes"` +} + +func NewParameterChangeProposal(title, description string, changes []ParamChange) ParameterChangeProposal { + return ParameterChangeProposal{title, description, changes} +} + +// GetTitle returns the title of a parameter change proposal. +func (pcp ParameterChangeProposal) GetTitle() string { return pcp.Title } + +// GetDescription returns the description of a parameter change proposal. +func (pcp ParameterChangeProposal) GetDescription() string { return pcp.Description } + +// GetDescription returns the routing key of a parameter change proposal. +func (pcp ParameterChangeProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a parameter change proposal. +func (pcp ParameterChangeProposal) ProposalType() string { return ProposalTypeChange } + +// ValidateBasic validates the parameter change proposal +func (pcp ParameterChangeProposal) ValidateBasic() error { + err := v036gov.ValidateAbstract(pcp) + if err != nil { + return err + } + + return ValidateChanges(pcp.Changes) +} + +// String implements the Stringer interface. +func (pcp ParameterChangeProposal) String() string { + var b strings.Builder + + b.WriteString(fmt.Sprintf(`Parameter Change Proposal: + Title: %s + Description: %s + Changes: +`, pcp.Title, pcp.Description)) + + for _, pc := range pcp.Changes { + b.WriteString(fmt.Sprintf(` Param Change: + Subspace: %s + Key: %s + Subkey: %X + Value: %X +`, pc.Subspace, pc.Key, pc.Subkey, pc.Value)) + } + + return b.String() +} + +// ParamChange defines a parameter change. +type ParamChange struct { + Subspace string `json:"subspace" yaml:"subspace"` + Key string `json:"key" yaml:"key"` + Subkey string `json:"subkey,omitempty" yaml:"subkey,omitempty"` + Value string `json:"value" yaml:"value"` +} + +func NewParamChange(subspace, key, value string) ParamChange { + return ParamChange{subspace, key, "", value} +} + +func NewParamChangeWithSubkey(subspace, key, subkey, value string) ParamChange { + return ParamChange{subspace, key, subkey, value} +} + +// String implements the Stringer interface. +func (pc ParamChange) String() string { + return fmt.Sprintf(`Param Change: + Subspace: %s + Key: %s + Subkey: %X + Value: %X +`, pc.Subspace, pc.Key, pc.Subkey, pc.Value) +} + +// ValidateChange performs basic validation checks over a set of ParamChange. It +// returns an error if any ParamChange is invalid. +func ValidateChanges(changes []ParamChange) error { + if len(changes) == 0 { + return ErrEmptyChanges(DefaultCodespace) + } + + for _, pc := range changes { + if len(pc.Subspace) == 0 { + return ErrEmptySubspace(DefaultCodespace) + } + if len(pc.Key) == 0 { + return ErrEmptyKey(DefaultCodespace) + } + if len(pc.Value) == 0 { + return ErrEmptyValue(DefaultCodespace) + } + } + + return nil +} + +// ErrUnknownSubspace returns an unknown subspace error. +func ErrUnknownSubspace(codespace string, space string) error { + return fmt.Errorf("unknown subspace %s", space) +} + +// ErrSettingParameter returns an error for failing to set a parameter. +func ErrSettingParameter(codespace string, key, subkey, value, msg string) error { + return fmt.Errorf("error setting parameter %s on %s (%s): %s", value, key, subkey, msg) +} + +// ErrEmptyChanges returns an error for empty parameter changes. +func ErrEmptyChanges(codespace string) error { + return fmt.Errorf("submitted parameter changes are empty") +} + +// ErrEmptySubspace returns an error for an empty subspace. +func ErrEmptySubspace(codespace string) error { + return fmt.Errorf("parameter subspace is empty") +} + +// ErrEmptyKey returns an error for when an empty key is given. +func ErrEmptyKey(codespace string) error { + return fmt.Errorf("parameter key is empty") +} + +// ErrEmptyValue returns an error for when an empty key is given. +func ErrEmptyValue(codespace string) error { + return fmt.Errorf("parameter value is empty") +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal", nil) +} diff --git a/x/params/module.go b/x/params/module.go index 5817b482b3b8..b0a4584129ef 100644 --- a/x/params/module.go +++ b/x/params/module.go @@ -1,22 +1,31 @@ package params import ( + "context" "encoding/json" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/params/client/cli" + "github.com/cosmos/cosmos-sdk/x/params/keeper" "github.com/cosmos/cosmos-sdk/x/params/simulation" "github.com/cosmos/cosmos-sdk/x/params/types" - sim "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) var ( + _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} _ module.AppModuleSimulation = AppModule{} ) @@ -26,60 +35,94 @@ type AppModuleBasic struct{} // Name returns the params module's name. func (AppModuleBasic) Name() string { - return ModuleName + return proposal.ModuleName } -// RegisterCodec registers the params module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - types.RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the params module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + proposal.RegisterLegacyAminoCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the params // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { return nil } +func (AppModuleBasic) DefaultGenesis(_ codec.JSONMarshaler) json.RawMessage { return nil } // ValidateGenesis performs genesis state validation for the params module. -func (AppModuleBasic) ValidateGenesis(_ json.RawMessage) error { return nil } +func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, config client.TxEncodingConfig, _ json.RawMessage) error { + return nil +} // RegisterRESTRoutes registers the REST routes for the params module. -func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {} +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the params module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + proposal.RegisterQueryHandlerClient(context.Background(), mux, proposal.NewQueryClient(clientCtx)) +} // GetTxCmd returns no root tx command for the params module. -func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetTxCmd() *cobra.Command { return nil } // GetQueryCmd returns no root query command for the params module. -func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.NewQueryCmd() +} + +func (am AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + proposal.RegisterInterfaces(registry) +} //____________________________________________________________________________ // AppModule implements an application module for the distribution module. type AppModule struct { AppModuleBasic + + keeper keeper.Keeper } // NewAppModule creates a new AppModule object -func NewAppModule() AppModule { +func NewAppModule(k keeper.Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, + keeper: k, } } -//____________________________________________________________________________ +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// InitGenesis performs a no-op. +func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONMarshaler, _ json.RawMessage) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} -// AppModuleSimulation functions +func (AppModule) Route() sdk.Route { return sdk.Route{} } // GenerateGenesisState performs a no-op. -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { +func (AppModule) GenerateGenesisState(simState *module.SimulationState) {} + +// QuerierRoute returns the x/param module's querier route name. +func (AppModule) QuerierRoute() string { return types.QuerierRoute } + +// LegacyQuerierHandler returns the x/params querier handler. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) +} + +// RegisterServices registers a gRPC query service to respond to the +// module-specific gRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + proposal.RegisterQueryServer(cfg.QueryServer(), am.keeper) } // ProposalContents returns all the params content functions used to // simulate governance proposals. -func (am AppModule) ProposalContents(simState module.SimulationState) []sim.WeightedProposalContent { +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return simulation.ProposalContents(simState.ParamChanges) } // RandomizedParams creates randomized distribution param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return nil } @@ -87,6 +130,19 @@ func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {} // WeightedOperations returns the all the gov module operations with their respective weights. -func (am AppModule) WeightedOperations(_ module.SimulationState) []sim.WeightedOperation { +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { return nil } + +// ExportGenesis performs a no-op. +func (am AppModule) ExportGenesis(_ sdk.Context, _ codec.JSONMarshaler) json.RawMessage { + return nil +} + +// BeginBlock performs a no-op. +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock performs a no-op. +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/params/proposal_handler.go b/x/params/proposal_handler.go index 339cfd7410ce..173cc292df49 100644 --- a/x/params/proposal_handler.go +++ b/x/params/proposal_handler.go @@ -6,13 +6,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/params/keeper" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) // NewParamChangeProposalHandler creates a new governance Handler for a ParamChangeProposal -func NewParamChangeProposalHandler(k Keeper) govtypes.Handler { +func NewParamChangeProposalHandler(k keeper.Keeper) govtypes.Handler { return func(ctx sdk.Context, content govtypes.Content) error { switch c := content.(type) { - case ParameterChangeProposal: + case *proposal.ParameterChangeProposal: return handleParameterChangeProposal(ctx, k, c) default: @@ -21,11 +23,11 @@ func NewParamChangeProposalHandler(k Keeper) govtypes.Handler { } } -func handleParameterChangeProposal(ctx sdk.Context, k Keeper, p ParameterChangeProposal) error { +func handleParameterChangeProposal(ctx sdk.Context, k keeper.Keeper, p *proposal.ParameterChangeProposal) error { for _, c := range p.Changes { ss, ok := k.GetSubspace(c.Subspace) if !ok { - return sdkerrors.Wrap(ErrUnknownSubspace, c.Subspace) + return sdkerrors.Wrap(proposal.ErrUnknownSubspace, c.Subspace) } k.Logger(ctx).Info( @@ -33,7 +35,7 @@ func handleParameterChangeProposal(ctx sdk.Context, k Keeper, p ParameterChangeP ) if err := ss.Update(ctx, []byte(c.Key), []byte(c.Value)); err != nil { - return sdkerrors.Wrapf(ErrSettingParameter, "key: %s, value: %s, err: %s", c.Key, c.Value, err.Error()) + return sdkerrors.Wrapf(proposal.ErrSettingParameter, "key: %s, value: %s, err: %s", c.Key, c.Value, err.Error()) } } diff --git a/x/params/proposal_handler_test.go b/x/params/proposal_handler_test.go index 9d83975c1020..b4295d2e2d8f 100644 --- a/x/params/proposal_handler_test.go +++ b/x/params/proposal_handler_test.go @@ -3,30 +3,32 @@ package params_test import ( "testing" + "github.com/cosmos/cosmos-sdk/simapp" + "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" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/params/subspace" + "github.com/cosmos/cosmos-sdk/x/params/keeper" "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) func validateNoOp(_ interface{}) error { return nil } type testInput struct { ctx sdk.Context - cdc *codec.Codec - keeper params.Keeper + cdc *codec.LegacyAmino + keeper keeper.Keeper } var ( - _ subspace.ParamSet = (*testParams)(nil) + _ types.ParamSet = (*testParams)(nil) keyMaxValidators = "MaxValidators" keySlashingRate = "SlashingRate" @@ -43,15 +45,15 @@ type testParams struct { SlashingRate testParamsSlashingRate `json:"slashing_rate" yaml:"slashing_rate"` } -func (tp *testParams) ParamSetPairs() subspace.ParamSetPairs { - return subspace.ParamSetPairs{ - params.NewParamSetPair([]byte(keyMaxValidators), &tp.MaxValidators, validateNoOp), - params.NewParamSetPair([]byte(keySlashingRate), &tp.SlashingRate, validateNoOp), +func (tp *testParams) ParamSetPairs() types.ParamSetPairs { + return types.ParamSetPairs{ + types.NewParamSetPair([]byte(keyMaxValidators), &tp.MaxValidators, validateNoOp), + types.NewParamSetPair([]byte(keySlashingRate), &tp.SlashingRate, validateNoOp), } } -func testProposal(changes ...params.ParamChange) params.ParameterChangeProposal { - return params.NewParameterChangeProposal( +func testProposal(changes ...proposal.ParamChange) *proposal.ParameterChangeProposal { + return proposal.NewParameterChangeProposal( "Test", "description", changes, @@ -59,8 +61,8 @@ func testProposal(changes ...params.ParamChange) params.ParameterChangeProposal } func newTestInput(t *testing.T) testInput { - cdc := codec.New() - types.RegisterCodec(cdc) + cdc := codec.NewLegacyAmino() + proposal.RegisterLegacyAminoCodec(cdc) db := dbm.NewMemDB() cms := store.NewCommitMultiStore(db) @@ -74,8 +76,9 @@ func newTestInput(t *testing.T) testInput { err := cms.LoadLatestVersion() require.Nil(t, err) - keeper := params.NewKeeper(cdc, keyParams, tKeyParams) - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + encCfg := simapp.MakeTestEncodingConfig() + keeper := keeper.NewKeeper(encCfg.Marshaler, encCfg.Amino, keyParams, tKeyParams) + ctx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) return testInput{ctx, cdc, keeper} } @@ -83,10 +86,10 @@ func newTestInput(t *testing.T) testInput { func TestProposalHandlerPassed(t *testing.T) { input := newTestInput(t) ss := input.keeper.Subspace(testSubspace).WithKeyTable( - params.NewKeyTable().RegisterParamSet(&testParams{}), + types.NewKeyTable().RegisterParamSet(&testParams{}), ) - tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "1")) + tp := testProposal(proposal.NewParamChange(testSubspace, keyMaxValidators, "1")) hdlr := params.NewParamChangeProposalHandler(input.keeper) require.NoError(t, hdlr(input.ctx, tp)) @@ -98,10 +101,10 @@ func TestProposalHandlerPassed(t *testing.T) { func TestProposalHandlerFailed(t *testing.T) { input := newTestInput(t) ss := input.keeper.Subspace(testSubspace).WithKeyTable( - params.NewKeyTable().RegisterParamSet(&testParams{}), + types.NewKeyTable().RegisterParamSet(&testParams{}), ) - tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "invalidType")) + tp := testProposal(proposal.NewParamChange(testSubspace, keyMaxValidators, "invalidType")) hdlr := params.NewParamChangeProposalHandler(input.keeper) require.Error(t, hdlr(input.ctx, tp)) @@ -111,19 +114,19 @@ func TestProposalHandlerFailed(t *testing.T) { func TestProposalHandlerUpdateOmitempty(t *testing.T) { input := newTestInput(t) ss := input.keeper.Subspace(testSubspace).WithKeyTable( - params.NewKeyTable().RegisterParamSet(&testParams{}), + types.NewKeyTable().RegisterParamSet(&testParams{}), ) hdlr := params.NewParamChangeProposalHandler(input.keeper) var param testParamsSlashingRate - tp := testProposal(params.NewParamChange(testSubspace, keySlashingRate, `{"downtime": 7}`)) + tp := testProposal(proposal.NewParamChange(testSubspace, keySlashingRate, `{"downtime": 7}`)) require.NoError(t, hdlr(input.ctx, tp)) ss.Get(input.ctx, []byte(keySlashingRate), ¶m) require.Equal(t, testParamsSlashingRate{0, 7}, param) - tp = testProposal(params.NewParamChange(testSubspace, keySlashingRate, `{"double_sign": 10}`)) + tp = testProposal(proposal.NewParamChange(testSubspace, keySlashingRate, `{"double_sign": 10}`)) require.NoError(t, hdlr(input.ctx, tp)) ss.Get(input.ctx, []byte(keySlashingRate), ¶m) diff --git a/x/params/simulation/operations.go b/x/params/simulation/operations.go index c9415a68e1df..a5097968ce70 100644 --- a/x/params/simulation/operations.go +++ b/x/params/simulation/operations.go @@ -4,16 +4,15 @@ import ( "math/rand" sdk "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" ) // SimulateParamChangeProposalContent returns random parameter change content. // It will generate a ParameterChangeProposal object with anywhere between 1 and // the total amount of defined parameters changes, all of which have random valid values. func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange) simulation.ContentSimulatorFn { - return func(r *rand.Rand, _ sdk.Context, _ []simulation.Account) govtypes.Content { + return func(r *rand.Rand, _ sdk.Context, _ []simulation.Account) simulation.Content { lenParamChange := len(paramChangePool) if lenParamChange == 0 { @@ -21,7 +20,7 @@ func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange } numChanges := simulation.RandIntBetween(r, 1, lenParamChange) - paramChanges := make([]types.ParamChange, numChanges) + paramChanges := make([]proposal.ParamChange, numChanges) // map from key to empty struct; used only for look-up of the keys of the // parameters that are already in the random set of changes. @@ -40,10 +39,10 @@ func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange // add a new distinct parameter to the set of changes and register the key // to avoid further duplicates paramChangesKeys[spc.ComposedKey()] = struct{}{} - paramChanges[i] = types.NewParamChange(spc.Subspace, spc.Key, spc.SimValue(r)) + paramChanges[i] = proposal.NewParamChange(spc.Subspace(), spc.Key(), spc.SimValue()(r)) } - return types.NewParameterChangeProposal( + return proposal.NewParameterChangeProposal( simulation.RandStringOfLength(r, 140), // title simulation.RandStringOfLength(r, 5000), // description paramChanges, // set of changes diff --git a/x/params/simulation/operations_test.go b/x/params/simulation/operations_test.go new file mode 100644 index 000000000000..54c90a6c2e3d --- /dev/null +++ b/x/params/simulation/operations_test.go @@ -0,0 +1,65 @@ +package simulation_test + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/params/simulation" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +type MockParamChange struct { + n int +} + +func (pc MockParamChange) Subspace() string { + return fmt.Sprintf("test-Subspace%d", pc.n) +} + +func (pc MockParamChange) Key() string { + return fmt.Sprintf("test-Key%d", pc.n) +} + +func (pc MockParamChange) ComposedKey() string { + return fmt.Sprintf("test-ComposedKey%d", pc.n) +} + +func (pc MockParamChange) SimValue() simtypes.SimValFn { + return func(r *rand.Rand) string { + return fmt.Sprintf("test-value %d%d ", pc.n, int64(simtypes.RandIntBetween(r, 10, 1000))) + } +} + +// make sure that the MockParamChange satisfied the ParamChange interface +var _ simtypes.ParamChange = MockParamChange{} + +func TestSimulateParamChangeProposalContent(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + ctx := sdk.NewContext(nil, tmproto.Header{}, true, nil) + accounts := simtypes.RandomAccounts(r, 3) + paramChangePool := []simtypes.ParamChange{MockParamChange{1}, MockParamChange{2}, MockParamChange{3}} + + // execute operation + op := simulation.SimulateParamChangeProposalContent(paramChangePool) + content := op(r, ctx, accounts) + + require.Equal(t, "BrTEdFUqHtDtDBPzlahrrStzkUNxImpptHBIFDQfnxaTiOBJUgNzvqHbVcVJYlIFWFlzFqqRTTyFzDUMntPzyRamUFqeJAEaSHIuUHZoTWDjWXsYxYvwXwXZEsjRQKgKMselyUqWXMbHzRNDHnMzhWSirUgVggjiBxtWDfhzPDgrorEoNmDEiDdBldYegphCBTYWrmFFXNjxhtygsGBFHTejaKjMsqNdikEzDalEyWRHfJhKqifCKsedVuuJbQMbmRVuIPDluAWGpngjgBjOxuRFwSadayHNIhVVmNWBbfaTOldclxTTLUMvaBnLfwjHTtsKetEIvgrxLijhKJNablmvqpWIWsmhWQAYNLycREypoASHnyKWrxpoNLBJuyCGysZJgXbQAAmSIbGxMFXuwMVGZgBiZWfPWorAfjBeekCFvljHAtVZaTOsRxbPIioNxLTnWUTzGTvaNhplQQPmMADRRDuUIsiBpnGqPheKmLnopieVseFdTSAvOCacxaqFWFuXzsrVZzlGfeRpClwKuGEBujaPrzSLjVIOMvLlWxuznEOXlxbZroBRVEvEfBBAHOECribZNrYiFnzQqQmBnLksmFNAadusWAGltuqYNntgOlgOGwSdDjWdLboWyAWIcCfmpGJTfbljKPriLehwObuszICkaXNUkmeddeeRulbZBXJVLgteiKIfofGdNBregwUPlINQECatDSNXSIuefyMxxoKfcmjHEwbVtFiXtEnLJkLHUghmzFiymrgBChucZgOQUpGGVQEpRtIQjIBxYhtZPgUORdxXNWUMErWrUeriqYJPcgIDgLMWAyuuQnsHncCtjvHmvFbzYErxeunQllYDUVlXaRBveRUKeXwEGJFTSAqZtaBSDGDtzlADCnGjuTmYMJlapRsWfugmjwKEuoXJVpZvlcHeFvVvRRktRVGwzLfKezPEMABZtbLExQIjynSoahmkmoTHefdzFoBHMcQHFkKVHhpNtudPqJrYuQswzFuFHbSmpNltFnYJpvMrAYHFrNouZaanEUGHvbHIUUFTCtZrcpRHwgjblxlDNJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmT", content.GetDescription()) + require.Equal(t, "tpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeHVIkPZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHe", content.GetTitle()) + require.Equal(t, "params", content.ProposalRoute()) + require.Equal(t, "ParameterChange", content.ProposalType()) + + pcp, ok := content.(*proposal.ParameterChangeProposal) + require.True(t, ok) + + require.Equal(t, "test-Key2", pcp.Changes[0].GetKey()) + require.Equal(t, "test-value 2610 ", pcp.Changes[0].GetValue()) + require.Equal(t, "test-Subspace2", pcp.Changes[0].GetSubspace()) +} diff --git a/x/params/simulation/proposals.go b/x/params/simulation/proposals.go index ad1c37dd88b7..165193735fbd 100644 --- a/x/params/simulation/proposals.go +++ b/x/params/simulation/proposals.go @@ -2,6 +2,7 @@ package simulation import ( simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" ) @@ -9,12 +10,12 @@ import ( const OpWeightSubmitParamChangeProposal = "op_weight_submit_param_change_proposal" // ProposalContents defines the module weighted proposals' contents -func ProposalContents(paramChanges []simulation.ParamChange) []simulation.WeightedProposalContent { - return []simulation.WeightedProposalContent{ - { - AppParamsKey: OpWeightSubmitParamChangeProposal, - DefaultWeight: simappparams.DefaultWeightParamChangeProposal, - ContentSimulatorFn: SimulateParamChangeProposalContent(paramChanges), - }, +func ProposalContents(paramChanges []simtypes.ParamChange) []simtypes.WeightedProposalContent { + return []simtypes.WeightedProposalContent{ + simulation.NewWeightedProposalContent( + OpWeightSubmitParamChangeProposal, + simappparams.DefaultWeightParamChangeProposal, + SimulateParamChangeProposalContent(paramChanges), + ), } } diff --git a/x/params/simulation/proposals_test.go b/x/params/simulation/proposals_test.go new file mode 100644 index 000000000000..e89bb668a489 --- /dev/null +++ b/x/params/simulation/proposals_test.go @@ -0,0 +1,51 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + 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/params/simulation" + "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +) + +func TestProposalContents(t *testing.T) { + // initialize parameters + s := rand.NewSource(1) + r := rand.New(s) + + ctx := sdk.NewContext(nil, tmproto.Header{}, true, nil) + accounts := simtypes.RandomAccounts(r, 3) + + paramChangePool := []simtypes.ParamChange{MockParamChange{1}, MockParamChange{2}, MockParamChange{3}} + + // execute ProposalContents function + weightedProposalContent := simulation.ProposalContents(paramChangePool) + require.Len(t, weightedProposalContent, 1) + + w0 := weightedProposalContent[0] + + // tests w0 interface: + require.Equal(t, simulation.OpWeightSubmitParamChangeProposal, w0.AppParamsKey()) + require.Equal(t, simappparams.DefaultWeightParamChangeProposal, w0.DefaultWeight()) + + content := w0.ContentSimulatorFn()(r, ctx, accounts) + + require.Equal(t, "BrTEdFUqHtDtDBPzlahrrStzkUNxImpptHBIFDQfnxaTiOBJUgNzvqHbVcVJYlIFWFlzFqqRTTyFzDUMntPzyRamUFqeJAEaSHIuUHZoTWDjWXsYxYvwXwXZEsjRQKgKMselyUqWXMbHzRNDHnMzhWSirUgVggjiBxtWDfhzPDgrorEoNmDEiDdBldYegphCBTYWrmFFXNjxhtygsGBFHTejaKjMsqNdikEzDalEyWRHfJhKqifCKsedVuuJbQMbmRVuIPDluAWGpngjgBjOxuRFwSadayHNIhVVmNWBbfaTOldclxTTLUMvaBnLfwjHTtsKetEIvgrxLijhKJNablmvqpWIWsmhWQAYNLycREypoASHnyKWrxpoNLBJuyCGysZJgXbQAAmSIbGxMFXuwMVGZgBiZWfPWorAfjBeekCFvljHAtVZaTOsRxbPIioNxLTnWUTzGTvaNhplQQPmMADRRDuUIsiBpnGqPheKmLnopieVseFdTSAvOCacxaqFWFuXzsrVZzlGfeRpClwKuGEBujaPrzSLjVIOMvLlWxuznEOXlxbZroBRVEvEfBBAHOECribZNrYiFnzQqQmBnLksmFNAadusWAGltuqYNntgOlgOGwSdDjWdLboWyAWIcCfmpGJTfbljKPriLehwObuszICkaXNUkmeddeeRulbZBXJVLgteiKIfofGdNBregwUPlINQECatDSNXSIuefyMxxoKfcmjHEwbVtFiXtEnLJkLHUghmzFiymrgBChucZgOQUpGGVQEpRtIQjIBxYhtZPgUORdxXNWUMErWrUeriqYJPcgIDgLMWAyuuQnsHncCtjvHmvFbzYErxeunQllYDUVlXaRBveRUKeXwEGJFTSAqZtaBSDGDtzlADCnGjuTmYMJlapRsWfugmjwKEuoXJVpZvlcHeFvVvRRktRVGwzLfKezPEMABZtbLExQIjynSoahmkmoTHefdzFoBHMcQHFkKVHhpNtudPqJrYuQswzFuFHbSmpNltFnYJpvMrAYHFrNouZaanEUGHvbHIUUFTCtZrcpRHwgjblxlDNJWzHdBNpAXKJPHWQdrGYcAHSctgVlqwqHoLfHsXUdStwfefwzqLuKEhmMyYLdbZrcPgYqjNHxPexsruwEGStAneKbWkQDDIlCWBLSiAASNhZqNFlPtfqPJoxKsgMdzjWqLWdqKQuJqWPMvwPQWZUtVMOTMYKJbfdlZsjdsomuScvDmbDkgRualsxDvRJuCAmPOXitIbcyWsKGSdrEunFAOdmXnsuyFVgJqEjbklvmwrUlsxjRSfKZxGcpayDdgoFcnVSutxjRgOSFzPwidAjubMncNweqpbxhXGchpZUxuFDOtpnhNUycJICRYqsPhPSCjPTWZFLkstHWJxvdPEAyEIxXgLwbNOjrgzmaujiBABBIXvcXpLrbcEWNNQsbjvgJFgJkflpRohHUutvnaUqoopuKjTDaemDeSdqbnOzcfJpcTuAQtZoiLZOoAIlboFDAeGmSNwkvObPRvRWQgWkGkxwtPauYgdkmypLjbqhlHJIQTntgWjXwZdOyYEdQRRLfMSdnxqppqUofqLbLQDUjwKVKfZJUJQPsWIPwIVaSTrmKskoAhvmZyJgeRpkaTfGgrJzAigcxtfshmiDCFkuiluqtMOkidknnTBtumyJYlIsWLnCQclqdVmikUoMOPdPWwYbJxXyqUVicNxFxyqJTenNblyyKSdlCbiXxUiYUiMwXZASYfvMDPFgxniSjWaZTjHkqlJvtBsXqwPpyVxnJVGFWhfSxgOcduoxkiopJvFjMmFabrGYeVtTXLhxVUEiGwYUvndjFGzDVntUvibiyZhfMQdMhgsiuysLMiePBNXifRLMsSmXPkwlPloUbJveCvUlaalhZHuvdkCnkSHbMbmOnrfEGPwQiACiPlnihiaOdbjPqPiTXaHDoJXjSlZmltGqNHHNrcKdlFSCdmVOuvDcBLdSklyGJmcLTbSFtALdGlPkqqecJrpLCXNPWefoTJNgEJlyMEPneVaxxduAAEqQpHWZodWyRkDAxzyMnFMcjSVqeRXLqsNyNtQBbuRvunZflWSbbvXXdkyLikYqutQhLPONXbvhcQZJPSWnOulqQaXmbfFxAkqfYeseSHOQidHwbcsOaMnSrrmGjjRmEMQNuknupMxJiIeVjmgZvbmjPIQTEhQFULQLBMPrxcFPvBinaOPYWGvYGRKxLZdwamfRQQFngcdSlvwjfaPbURasIsGJVHtcEAxnIIrhSriiXLOlbEBLXFElXJFGxHJczRBIxAuPKtBisjKBwfzZFagdNmjdwIRvwzLkFKWRTDPxJCmpzHUcrPiiXXHnOIlqNVoGSXZewdnCRhuxeYGPVTfrNTQNOxZmxInOazUYNTNDgzsxlgiVEHPKMfbesvPHUqpNkUqbzeuzfdrsuLDpKHMUbBMKczKKWOdYoIXoPYtEjfOnlQLoGnbQUCuERdEFaptwnsHzTJDsuZkKtzMpFaZobynZdzNydEeJJHDYaQcwUxcqvwfWwNUsCiLvkZQiSfzAHftYgAmVsXgtmcYgTqJIawstRYJrZdSxlfRiqTufgEQVambeZZmaAyRQbcmdjVUZZCgqDrSeltJGXPMgZnGDZqISrGDOClxXCxMjmKqEPwKHoOfOeyGmqWqihqjINXLqnyTesZePQRqaWDQNqpLgNrAUKulklmckTijUltQKuWQDwpLmDyxLppPVMwsmBIpOwQttYFMjgJQZLYFPmxWFLIeZihkRNnkzoypBICIxgEuYsVWGIGRbbxqVasYnstWomJnHwmtOhAFSpttRYYzBmyEtZXiCthvKvWszTXDbiJbGXMcrYpKAgvUVFtdKUfvdMfhAryctklUCEdjetjuGNfJjajZtvzdYaqInKtFPPLYmRaXPdQzxdSQfmZDEVHlHGEGNSPRFJuIfKLLfUmnHxHnRjmzQPNlqrXgifUdzAGKVabYqvcDeYoTYgPsBUqehrBhmQUgTvDnsdpuhUoxskDdppTsYMcnDIPSwKIqhXDCIxOuXrywahvVavvHkPuaenjLmEbMgrkrQLHEAwrhHkPRNvonNQKqprqOFVZKAtpRSpvQUxMoXCMZLSSbnLEFsjVfANdQNQVwTmGxqVjVqRuxREAhuaDrFgEZpYKhwWPEKBevBfsOIcaZKyykQafzmGPLRAKDtTcJxJVgiiuUkmyMYuDUNEUhBEdoBLJnamtLmMJQgmLiUELIhLpiEvpOXOvXCPUeldLFqkKOwfacqIaRcnnZvERKRMCKUkMABbDHytQqQblrvoxOZkwzosQfDKGtIdfcXRJNqlBNwOCWoQBcEWyqrMlYZIAXYJmLfnjoJepgSFvrgajaBAIksoyeHqgqbGvpAstMIGmIhRYGGNPRIfOQKsGoKgxtsidhTaAePRCBFqZgPDWCIkqOJezGVkjfYUCZTlInbxBXwUAVRsxHTQtJFnnpmMvXDYCVlEmnZBKhmmxQOIQzxFWpJQkQoSAYzTEiDWEOsVLNrbfzeHFRyeYATakQQWmFDLPbVMCJcWjFGJjfqCoVzlbNNEsqxdSmNPjTjHYOkuEMFLkXYGaoJlraLqayMeCsTjWNRDPBywBJLAPVkGQqTwApVVwYAetlwSbzsdHWsTwSIcctkyKDuRWYDQikRqsKTMJchrliONJeaZIzwPQrNbTwxsGdwuduvibtYndRwpdsvyCktRHFalvUuEKMqXbItfGcNGWsGzubdPMYayOUOINjpcFBeESdwpdlTYmrPsLsVDhpTzoMegKrytNVZkfJRPuDCUXxSlSthOohmsuxmIZUedzxKmowKOdXTMcEtdpHaPWgIsIjrViKrQOCONlSuazmLuCUjLltOGXeNgJKedTVrrVCpWYWHyVrdXpKgNaMJVjbXxnVMSChdWKuZdqpisvrkBJPoURDYxWOtpjzZoOpWzyUuYNhCzRoHsMjmmWDcXzQiHIyjwdhPNwiPqFxeUfMVFQGImhykFgMIlQEoZCaRoqSBXTSWAeDumdbsOGtATwEdZlLfoBKiTvodQBGOEcuATWXfiinSjPmJKcWgQrTVYVrwlyMWhxqNbCMpIQNoSMGTiWfPTCezUjYcdWppnsYJihLQCqbNLRGgqrwHuIvsazapTpoPZIyZyeeSueJuTIhpHMEJfJpScshJubJGfkusuVBgfTWQoywSSliQQSfbvaHKiLnyjdSbpMkdBgXepoSsHnCQaYuHQqZsoEOmJCiuQUpJkmfyfbIShzlZpHFmLCsbknEAkKXKfRTRnuwdBeuOGgFbJLbDksHVapaRayWzwoYBEpmrlAxrUxYMUekKbpjPNfjUCjhbdMAnJmYQVZBQZkFVweHDAlaqJjRqoQPoOMLhyvYCzqEuQsAFoxWrzRnTVjStPadhsESlERnKhpEPsfDxNvxqcOyIulaCkmPdambLHvGhTZzysvqFauEgkFRItPfvisehFmoBhQqmkfbHVsgfHXDPJVyhwPllQpuYLRYvGodxKjkarnSNgsXoKEMlaSKxKdcVgvOkuLcfLFfdtXGTclqfPOfeoVLbqcjcXCUEBgAGplrkgsmIEhWRZLlGPGCwKWRaCKMkBHTAcypUrYjWwCLtOPVygMwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmT", content.GetDescription()) + require.Equal(t, "tpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeHVIkPZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxKGLwQvuyNhYFmBZHe", content.GetTitle()) + require.Equal(t, "params", content.ProposalRoute()) + require.Equal(t, "ParameterChange", content.ProposalType()) + + pcp, ok := content.(*proposal.ParameterChangeProposal) + require.True(t, ok) + + require.Len(t, pcp.Changes, 1) + require.Equal(t, "test-Key2", pcp.Changes[0].GetKey()) + require.Equal(t, "test-value 2610 ", pcp.Changes[0].GetValue()) + require.Equal(t, "test-Subspace2", pcp.Changes[0].GetSubspace()) +} diff --git a/x/params/spec/01_keeper.md b/x/params/spec/01_keeper.md index b2256919a4b1..8cf7e2a53f97 100644 --- a/x/params/spec/01_keeper.md +++ b/x/params/spec/01_keeper.md @@ -4,7 +4,11 @@ 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, `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. Example: diff --git a/x/params/spec/02_subspace.md b/x/params/spec/02_subspace.md index 98102791fd70..5e36eaf498f5 100644 --- a/x/params/spec/02_subspace.md +++ b/x/params/spec/02_subspace.md @@ -4,25 +4,35 @@ 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 who use the +parameter store will take a `Subspace`, not the `Keeper`, to isolate permission to access. ## Key -Parameter keys are human readable alphanumeric strings. A parameter for the key `"ExampleParameter"` is stored under `[]byte("SubspaceName" + "/" + "ExampleParameter")`, where `"SubspaceName"` is the name of the subspace. +Parameter keys are human readable alphanumeric strings. A parameter for the key +`"ExampleParameter"` is stored under `[]byte("SubspaceName" + "/" + "ExampleParameter")`, + where `"SubspaceName"` is the name of the subspace. -Subkeys are secondary parameter keys those are used along with a primary parameter key. Subkeys can be used for grouping or dynamic parameter key generation during runtime. +Subkeys are secondary parameter keys those are used along with a primary parameter key. +Subkeys can be used for grouping or dynamic parameter key generation during runtime. ## KeyTable -All of the paramter 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. +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` 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. -Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the attribute of the primary key. +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 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: * `KeyTable.RegisterParamSet()`: registers all parameters in the struct * `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct diff --git a/x/params/subspace/common_test.go b/x/params/subspace/common_test.go deleted file mode 100644 index 69c41478dbcc..000000000000 --- a/x/params/subspace/common_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package subspace_test - -import ( - "errors" - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params/subspace" -) - -var ( - keyUnbondingTime = []byte("UnbondingTime") - keyMaxValidators = []byte("MaxValidators") - keyBondDenom = []byte("BondDenom") - - key = sdk.NewKVStoreKey("storekey") - tkey = sdk.NewTransientStoreKey("transientstorekey") -) - -type params struct { - UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` - MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` - BondDenom string `json:"bond_denom" yaml:"bond_denom"` -} - -func validateUnbondingTime(i interface{}) error { - v, ok := i.(time.Duration) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v < (24 * time.Hour) { - return fmt.Errorf("unbonding time must be at least one day") - } - - return nil -} - -func validateMaxValidators(i interface{}) error { - _, ok := i.(uint16) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - return nil -} - -func validateBondDenom(i interface{}) error { - v, ok := i.(string) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if len(v) == 0 { - return errors.New("denom cannot be empty") - } - - return nil -} - -func (p *params) ParamSetPairs() subspace.ParamSetPairs { - return subspace.ParamSetPairs{ - {keyUnbondingTime, &p.UnbondingTime, validateUnbondingTime}, - {keyMaxValidators, &p.MaxValidators, validateMaxValidators}, - {keyBondDenom, &p.BondDenom, validateBondDenom}, - } -} - -func paramKeyTable() subspace.KeyTable { - return subspace.NewKeyTable().RegisterParamSet(¶ms{}) -} diff --git a/x/params/subspace/doc.go b/x/params/subspace/doc.go deleted file mode 100644 index 0bde2ebe62d4..000000000000 --- a/x/params/subspace/doc.go +++ /dev/null @@ -1,13 +0,0 @@ -/* -To prevent namespace collision between consumer modules, we define a type -Subspace. A Subspace can only be generated by the keeper, and the keeper checks -the existence of the Subspace having the same name before generating the -Subspace. - -Consumer modules must take a Subspace (via Keeper.Subspace), not the keeper -itself. This isolates each modules from the others and make them modify their -respective parameters safely. Keeper can be treated as master permission for all -Subspaces (via Keeper.GetSubspace), so should be passed to proper modules -(ex. x/governance). -*/ -package subspace diff --git a/x/params/subspace/table_test.go b/x/params/subspace/table_test.go deleted file mode 100644 index 6bc1a8af2a81..000000000000 --- a/x/params/subspace/table_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package subspace_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/x/params/subspace" -) - -func TestKeyTable(t *testing.T) { - table := subspace.NewKeyTable() - - require.Panics(t, func() { table.RegisterType(subspace.ParamSetPair{[]byte(""), nil, nil}) }) - require.Panics(t, func() { table.RegisterType(subspace.ParamSetPair{[]byte("!@#$%"), nil, nil}) }) - require.Panics(t, func() { table.RegisterType(subspace.ParamSetPair{[]byte("hello,"), nil, nil}) }) - require.Panics(t, func() { table.RegisterType(subspace.ParamSetPair{[]byte("hello"), nil, nil}) }) - - require.NotPanics(t, func() { - table.RegisterType(subspace.ParamSetPair{keyBondDenom, string("stake"), validateBondDenom}) - }) - require.NotPanics(t, func() { - table.RegisterType(subspace.ParamSetPair{keyMaxValidators, uint16(100), validateMaxValidators}) - }) - require.Panics(t, func() { - table.RegisterType(subspace.ParamSetPair{keyUnbondingTime, time.Duration(1), nil}) - }) - require.NotPanics(t, func() { - table.RegisterType(subspace.ParamSetPair{keyUnbondingTime, time.Duration(1), validateMaxValidators}) - }) - require.NotPanics(t, func() { - newTable := subspace.NewKeyTable() - newTable.RegisterParamSet(¶ms{}) - }) - - require.Panics(t, func() { table.RegisterParamSet(¶ms{}) }) - require.Panics(t, func() { subspace.NewKeyTable(subspace.ParamSetPair{[]byte(""), nil, nil}) }) - - require.NotPanics(t, func() { - subspace.NewKeyTable( - subspace.ParamSetPair{[]byte("test"), string("stake"), validateBondDenom}, - subspace.ParamSetPair{[]byte("test2"), uint16(100), validateMaxValidators}, - ) - }) -} diff --git a/x/params/types/codec.go b/x/params/types/codec.go deleted file mode 100644 index 525836346f90..000000000000 --- a/x/params/types/codec.go +++ /dev/null @@ -1,19 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// module codec -var ModuleCdc *codec.Codec - -func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - ModuleCdc.Seal() -} - -// RegisterCodec registers all necessary param module types with a given codec. -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal", nil) -} diff --git a/x/params/types/common_test.go b/x/params/types/common_test.go new file mode 100644 index 000000000000..c7cb067c62c4 --- /dev/null +++ b/x/params/types/common_test.go @@ -0,0 +1,72 @@ +package types_test + +import ( + "errors" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var ( + keyUnbondingTime = []byte("UnbondingTime") + keyMaxValidators = []byte("MaxValidators") + keyBondDenom = []byte("BondDenom") + + key = sdk.NewKVStoreKey("storekey") + tkey = sdk.NewTransientStoreKey("transientstorekey") +) + +type params struct { + UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` + MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` + BondDenom string `json:"bond_denom" yaml:"bond_denom"` +} + +func validateUnbondingTime(i interface{}) error { + v, ok := i.(time.Duration) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v < (24 * time.Hour) { + return fmt.Errorf("unbonding time must be at least one day") + } + + return nil +} + +func validateMaxValidators(i interface{}) error { + _, ok := i.(uint16) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + +func validateBondDenom(i interface{}) error { + v, ok := i.(string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if len(v) == 0 { + return errors.New("denom cannot be empty") + } + + return nil +} + +func (p *params) ParamSetPairs() types.ParamSetPairs { + return types.ParamSetPairs{ + {keyUnbondingTime, &p.UnbondingTime, validateUnbondingTime}, + {keyMaxValidators, &p.MaxValidators, validateMaxValidators}, + {keyBondDenom, &p.BondDenom, validateBondDenom}, + } +} + +func paramKeyTable() types.KeyTable { + return types.NewKeyTable().RegisterParamSet(¶ms{}) +} diff --git a/x/params/types/deref_test.go b/x/params/types/deref_test.go new file mode 100644 index 000000000000..1cd4406e57c4 --- /dev/null +++ b/x/params/types/deref_test.go @@ -0,0 +1,27 @@ +package types + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestKeyTableUnfurlsPointers(t *testing.T) { + tbl := NewKeyTable() + validator := func(_ interface{}) error { + return nil + } + tbl = tbl.RegisterType(ParamSetPair{ + Key: []byte("key"), + Value: (*****string)(nil), + ValidatorFn: validator, + }) + + got := tbl.m["key"] + want := attribute{ + vfn: validator, + ty: reflect.ValueOf("").Type(), + } + require.Equal(t, got.ty, want.ty) +} diff --git a/x/params/types/doc.go b/x/params/types/doc.go new file mode 100644 index 000000000000..861a4a2f5842 --- /dev/null +++ b/x/params/types/doc.go @@ -0,0 +1,13 @@ +/* +To prevent namespace collision between consumer modules, we define a type +Subspace. A Subspace can only be generated by the keeper, and the keeper checks +the existence of the Subspace having the same name before generating the +Subspace. + +Consumer modules must take a Subspace (via Keeper.Subspace), not the keeper +itself. This isolates each modules from the others and make them modify their +respective parameters safely. Keeper can be treated as master permission for all +Subspaces (via Keeper.GetSubspace), so should be passed to proper modules +(ex. x/governance). +*/ +package types diff --git a/x/params/types/errors.go b/x/params/types/errors.go deleted file mode 100644 index 4b218d9444ee..000000000000 --- a/x/params/types/errors.go +++ /dev/null @@ -1,15 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// x/params module sentinel errors -var ( - ErrUnknownSubspace = sdkerrors.Register(ModuleName, 1, "unknown subspace") - ErrSettingParameter = sdkerrors.Register(ModuleName, 2, "failed to set parameter") - ErrEmptyChanges = sdkerrors.Register(ModuleName, 3, "submitted parameter changes are empty") - ErrEmptySubspace = sdkerrors.Register(ModuleName, 4, "parameter subspace is empty") - ErrEmptyKey = sdkerrors.Register(ModuleName, 5, "parameter key is empty") - ErrEmptyValue = sdkerrors.Register(ModuleName, 6, "parameter value is empty") -) diff --git a/x/params/types/keys.go b/x/params/types/keys.go index 1355a7427b95..25df79848dae 100644 --- a/x/params/types/keys.go +++ b/x/params/types/keys.go @@ -1,9 +1,9 @@ package types const ( - // ModuleName defines the name of the module + // ModuleName defines the module name ModuleName = "params" - // RouterKey defines the routing key for a ParameterChangeProposal - RouterKey = "params" + // QuerierRoute defines the module's query routing key + QuerierRoute = ModuleName ) diff --git a/x/params/subspace/paramset.go b/x/params/types/paramset.go similarity index 97% rename from x/params/subspace/paramset.go rename to x/params/types/paramset.go index 195944d8c9c4..80d0852be4f0 100644 --- a/x/params/subspace/paramset.go +++ b/x/params/types/paramset.go @@ -1,4 +1,4 @@ -package subspace +package types type ( ValueValidatorFn func(value interface{}) error diff --git a/x/params/types/proposal.go b/x/params/types/proposal.go deleted file mode 100644 index 78f200d143e9..000000000000 --- a/x/params/types/proposal.go +++ /dev/null @@ -1,118 +0,0 @@ -package types - -import ( - "fmt" - "strings" - - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -const ( - // ProposalTypeChange defines the type for a ParameterChangeProposal - ProposalTypeChange = "ParameterChange" -) - -// Assert ParameterChangeProposal implements govtypes.Content at compile-time -var _ govtypes.Content = ParameterChangeProposal{} - -func init() { - govtypes.RegisterProposalType(ProposalTypeChange) - govtypes.RegisterProposalTypeCodec(ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal") -} - -// ParameterChangeProposal defines a proposal which contains multiple parameter -// changes. -type ParameterChangeProposal struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` - Changes []ParamChange `json:"changes" yaml:"changes"` -} - -func NewParameterChangeProposal(title, description string, changes []ParamChange) ParameterChangeProposal { - return ParameterChangeProposal{title, description, changes} -} - -// GetTitle returns the title of a parameter change proposal. -func (pcp ParameterChangeProposal) GetTitle() string { return pcp.Title } - -// GetDescription returns the description of a parameter change proposal. -func (pcp ParameterChangeProposal) GetDescription() string { return pcp.Description } - -// ProposalRoute returns the routing key of a parameter change proposal. -func (pcp ParameterChangeProposal) ProposalRoute() string { return RouterKey } - -// ProposalType returns the type of a parameter change proposal. -func (pcp ParameterChangeProposal) ProposalType() string { return ProposalTypeChange } - -// ValidateBasic validates the parameter change proposal -func (pcp ParameterChangeProposal) ValidateBasic() error { - err := govtypes.ValidateAbstract(pcp) - if err != nil { - return err - } - - return ValidateChanges(pcp.Changes) -} - -// String implements the Stringer interface. -func (pcp ParameterChangeProposal) String() string { - var b strings.Builder - - b.WriteString(fmt.Sprintf(`Parameter Change Proposal: - Title: %s - Description: %s - Changes: -`, pcp.Title, pcp.Description)) - - for _, pc := range pcp.Changes { - b.WriteString(fmt.Sprintf(` Param Change: - Subspace: %s - Key: %s - Value: %X -`, pc.Subspace, pc.Key, pc.Value)) - } - - return b.String() -} - -// ParamChange defines a parameter change. -type ParamChange struct { - Subspace string `json:"subspace" yaml:"subspace"` - Key string `json:"key" yaml:"key"` - Value string `json:"value" yaml:"value"` -} - -func NewParamChange(subspace, key, value string) ParamChange { - return ParamChange{subspace, key, value} -} - -// String implements the Stringer interface. -func (pc ParamChange) String() string { - return fmt.Sprintf(`Param Change: - Subspace: %s - Key: %s - Value: %X -`, pc.Subspace, pc.Key, pc.Value) -} - -// ValidateChanges performs basic validation checks over a set of ParamChange. It -// returns an error if any ParamChange is invalid. -func ValidateChanges(changes []ParamChange) error { - if len(changes) == 0 { - return ErrEmptyChanges - } - - for _, pc := range changes { - if len(pc.Subspace) == 0 { - return ErrEmptySubspace - } - if len(pc.Key) == 0 { - return ErrEmptyKey - } - if len(pc.Value) == 0 { - return ErrEmptyValue - } - } - - return nil -} diff --git a/x/params/types/proposal/codec.go b/x/params/types/proposal/codec.go new file mode 100644 index 000000000000..d638caccaf65 --- /dev/null +++ b/x/params/types/proposal/codec.go @@ -0,0 +1,19 @@ +package proposal + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +// RegisterLegacyAminoCodec registers all necessary param module types with a given LegacyAmino codec. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*govtypes.Content)(nil), + &ParameterChangeProposal{}, + ) +} diff --git a/x/params/types/proposal/errors.go b/x/params/types/proposal/errors.go new file mode 100644 index 000000000000..a8c891af2ce3 --- /dev/null +++ b/x/params/types/proposal/errors.go @@ -0,0 +1,15 @@ +package proposal + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/params module sentinel errors +var ( + ErrUnknownSubspace = sdkerrors.Register(ModuleName, 2, "unknown subspace") + ErrSettingParameter = sdkerrors.Register(ModuleName, 3, "failed to set parameter") + ErrEmptyChanges = sdkerrors.Register(ModuleName, 4, "submitted parameter changes are empty") + ErrEmptySubspace = sdkerrors.Register(ModuleName, 5, "parameter subspace is empty") + ErrEmptyKey = sdkerrors.Register(ModuleName, 6, "parameter key is empty") + ErrEmptyValue = sdkerrors.Register(ModuleName, 7, "parameter value is empty") +) diff --git a/x/params/types/proposal/keys.go b/x/params/types/proposal/keys.go new file mode 100644 index 000000000000..0649f417433c --- /dev/null +++ b/x/params/types/proposal/keys.go @@ -0,0 +1,9 @@ +package proposal + +const ( + // ModuleName defines the name of the module + ModuleName = "params" + + // RouterKey defines the routing key for a ParameterChangeProposal + RouterKey = "params" +) diff --git a/x/params/types/proposal/params.pb.go b/x/params/types/proposal/params.pb.go new file mode 100644 index 000000000000..1681b3960e63 --- /dev/null +++ b/x/params/types/proposal/params.pb.go @@ -0,0 +1,757 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/params/v1beta1/params.proto + +package proposal + +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 + +// ParameterChangeProposal defines a proposal to change one or more parameters. +type ParameterChangeProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Changes []ParamChange `protobuf:"bytes,3,rep,name=changes,proto3" json:"changes"` +} + +func (m *ParameterChangeProposal) Reset() { *m = ParameterChangeProposal{} } +func (*ParameterChangeProposal) ProtoMessage() {} +func (*ParameterChangeProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_53a944ecb0483e4c, []int{0} +} +func (m *ParameterChangeProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ParameterChangeProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ParameterChangeProposal.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 *ParameterChangeProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_ParameterChangeProposal.Merge(m, src) +} +func (m *ParameterChangeProposal) XXX_Size() int { + return m.Size() +} +func (m *ParameterChangeProposal) XXX_DiscardUnknown() { + xxx_messageInfo_ParameterChangeProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_ParameterChangeProposal proto.InternalMessageInfo + +// ParamChange defines an individual parameter change, for use in +// ParameterChangeProposal. +type ParamChange struct { + Subspace string `protobuf:"bytes,1,opt,name=subspace,proto3" json:"subspace,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *ParamChange) Reset() { *m = ParamChange{} } +func (*ParamChange) ProtoMessage() {} +func (*ParamChange) Descriptor() ([]byte, []int) { + return fileDescriptor_53a944ecb0483e4c, []int{1} +} +func (m *ParamChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ParamChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ParamChange.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 *ParamChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_ParamChange.Merge(m, src) +} +func (m *ParamChange) XXX_Size() int { + return m.Size() +} +func (m *ParamChange) XXX_DiscardUnknown() { + xxx_messageInfo_ParamChange.DiscardUnknown(m) +} + +var xxx_messageInfo_ParamChange proto.InternalMessageInfo + +func (m *ParamChange) GetSubspace() string { + if m != nil { + return m.Subspace + } + return "" +} + +func (m *ParamChange) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *ParamChange) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +func init() { + proto.RegisterType((*ParameterChangeProposal)(nil), "cosmos.params.v1beta1.ParameterChangeProposal") + proto.RegisterType((*ParamChange)(nil), "cosmos.params.v1beta1.ParamChange") +} + +func init() { + proto.RegisterFile("cosmos/params/v1beta1/params.proto", fileDescriptor_53a944ecb0483e4c) +} + +var fileDescriptor_53a944ecb0483e4c = []byte{ + // 307 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, + 0x34, 0x84, 0x72, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x44, 0x21, 0x6a, 0xf4, 0xa0, 0x82, + 0x50, 0x35, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x15, 0xfa, 0x20, 0x16, 0x44, 0xb1, 0xd2, + 0x7c, 0x46, 0x2e, 0xf1, 0x00, 0x90, 0xc2, 0xd4, 0x92, 0xd4, 0x22, 0xe7, 0x8c, 0xc4, 0xbc, 0xf4, + 0xd4, 0x80, 0xa2, 0xfc, 0x82, 0xfc, 0xe2, 0xc4, 0x1c, 0x21, 0x11, 0x2e, 0xd6, 0x92, 0xcc, 0x92, + 0x9c, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x08, 0x47, 0x48, 0x81, 0x8b, 0x3b, 0x25, + 0xb5, 0x38, 0xb9, 0x28, 0xb3, 0xa0, 0x24, 0x33, 0x3f, 0x4f, 0x82, 0x09, 0x2c, 0x87, 0x2c, 0x24, + 0xe4, 0xc4, 0xc5, 0x9e, 0x0c, 0x36, 0xa9, 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x49, + 0x0f, 0xab, 0x93, 0xf4, 0xc0, 0x16, 0x43, 0x2c, 0x75, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, + 0xa6, 0xd1, 0x8a, 0xa3, 0x63, 0x81, 0x3c, 0xc3, 0x8c, 0x05, 0xf2, 0x0c, 0x4a, 0xe1, 0x5c, 0xdc, + 0x48, 0xea, 0x84, 0xa4, 0xb8, 0x38, 0x8a, 0x4b, 0x93, 0x8a, 0x0b, 0x12, 0x93, 0x61, 0xee, 0x82, + 0xf3, 0x85, 0x04, 0xb8, 0x98, 0xb3, 0x53, 0x2b, 0xa1, 0x4e, 0x02, 0x31, 0x41, 0x5e, 0x28, 0x4b, + 0xcc, 0x29, 0x4d, 0x95, 0x60, 0x86, 0x78, 0x01, 0xcc, 0xb1, 0x62, 0x01, 0x19, 0xec, 0x14, 0xb4, + 0xe2, 0x91, 0x1c, 0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, + 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0xa4, + 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0x03, 0x1d, 0x42, 0xe9, 0x16, + 0xa7, 0x64, 0xeb, 0x57, 0xc0, 0x62, 0xa0, 0xa4, 0xb2, 0x20, 0xb5, 0x58, 0xbf, 0x00, 0x1a, 0x64, + 0x49, 0x6c, 0xe0, 0x50, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x26, 0x09, 0x3d, 0xa8, + 0x01, 0x00, 0x00, +} + +func (this *ParameterChangeProposal) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ParameterChangeProposal) + if !ok { + that2, ok := that.(ParameterChangeProposal) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + if len(this.Changes) != len(that1.Changes) { + return false + } + for i := range this.Changes { + if !this.Changes[i].Equal(&that1.Changes[i]) { + return false + } + } + return true +} +func (this *ParamChange) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ParamChange) + if !ok { + that2, ok := that.(ParamChange) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Subspace != that1.Subspace { + return false + } + if this.Key != that1.Key { + return false + } + if this.Value != that1.Value { + return false + } + return true +} +func (m *ParameterChangeProposal) 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 *ParameterChangeProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ParameterChangeProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Changes) > 0 { + for iNdEx := len(m.Changes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Changes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintParams(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintParams(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ParamChange) 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 *ParamChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ParamChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintParams(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintParams(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if len(m.Subspace) > 0 { + i -= len(m.Subspace) + copy(dAtA[i:], m.Subspace) + i = encodeVarintParams(dAtA, i, uint64(len(m.Subspace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintParams(dAtA []byte, offset int, v uint64) int { + offset -= sovParams(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ParameterChangeProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + if len(m.Changes) > 0 { + for _, e := range m.Changes { + l = e.Size() + n += 1 + l + sovParams(uint64(l)) + } + } + return n +} + +func (m *ParamChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Subspace) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozParams(x uint64) (n int) { + return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ParameterChangeProposal) 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 ErrIntOverflowParams + } + 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: ParameterChangeProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ParameterChangeProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Changes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Changes = append(m.Changes, ParamChange{}) + if err := m.Changes[len(m.Changes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ParamChange) 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 ErrIntOverflowParams + } + 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: ParamChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ParamChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subspace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Subspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + 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 ErrInvalidLengthParams + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipParams(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, ErrIntOverflowParams + } + 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, ErrIntOverflowParams + } + 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, ErrIntOverflowParams + } + 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, ErrInvalidLengthParams + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupParams + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthParams + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/params/types/proposal/proposal.go b/x/params/types/proposal/proposal.go new file mode 100644 index 000000000000..3a2f97a77247 --- /dev/null +++ b/x/params/types/proposal/proposal.go @@ -0,0 +1,102 @@ +package proposal + +import ( + "fmt" + "strings" + + yaml "gopkg.in/yaml.v2" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +const ( + // ProposalTypeChange defines the type for a ParameterChangeProposal + ProposalTypeChange = "ParameterChange" +) + +// Assert ParameterChangeProposal implements govtypes.Content at compile-time +var _ govtypes.Content = &ParameterChangeProposal{} + +func init() { + govtypes.RegisterProposalType(ProposalTypeChange) + govtypes.RegisterProposalTypeCodec(&ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal") +} + +func NewParameterChangeProposal(title, description string, changes []ParamChange) *ParameterChangeProposal { + return &ParameterChangeProposal{title, description, changes} +} + +// GetTitle returns the title of a parameter change proposal. +func (pcp *ParameterChangeProposal) GetTitle() string { return pcp.Title } + +// GetDescription returns the description of a parameter change proposal. +func (pcp *ParameterChangeProposal) GetDescription() string { return pcp.Description } + +// ProposalRoute returns the routing key of a parameter change proposal. +func (pcp *ParameterChangeProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a parameter change proposal. +func (pcp *ParameterChangeProposal) ProposalType() string { return ProposalTypeChange } + +// ValidateBasic validates the parameter change proposal +func (pcp *ParameterChangeProposal) ValidateBasic() error { + err := govtypes.ValidateAbstract(pcp) + if err != nil { + return err + } + + return ValidateChanges(pcp.Changes) +} + +// String implements the Stringer interface. +func (pcp ParameterChangeProposal) String() string { + var b strings.Builder + + b.WriteString(fmt.Sprintf(`Parameter Change Proposal: + Title: %s + Description: %s + Changes: +`, pcp.Title, pcp.Description)) + + for _, pc := range pcp.Changes { + b.WriteString(fmt.Sprintf(` Param Change: + Subspace: %s + Key: %s + Value: %X +`, pc.Subspace, pc.Key, pc.Value)) + } + + return b.String() +} + +func NewParamChange(subspace, key, value string) ParamChange { + return ParamChange{subspace, key, value} +} + +// String implements the Stringer interface. +func (pc ParamChange) String() string { + out, _ := yaml.Marshal(pc) + return string(out) +} + +// ValidateChanges performs basic validation checks over a set of ParamChange. It +// returns an error if any ParamChange is invalid. +func ValidateChanges(changes []ParamChange) error { + if len(changes) == 0 { + return ErrEmptyChanges + } + + for _, pc := range changes { + if len(pc.Subspace) == 0 { + return ErrEmptySubspace + } + if len(pc.Key) == 0 { + return ErrEmptyKey + } + if len(pc.Value) == 0 { + return ErrEmptyValue + } + } + + return nil +} diff --git a/x/params/types/proposal/proposal_test.go b/x/params/types/proposal/proposal_test.go new file mode 100644 index 000000000000..a18f9c407609 --- /dev/null +++ b/x/params/types/proposal/proposal_test.go @@ -0,0 +1,27 @@ +package proposal + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParameterChangeProposal(t *testing.T) { + pc1 := NewParamChange("sub", "foo", "baz") + pc2 := NewParamChange("sub", "bar", "cat") + pcp := NewParameterChangeProposal("test title", "test description", []ParamChange{pc1, pc2}) + + require.Equal(t, "test title", pcp.GetTitle()) + require.Equal(t, "test description", pcp.GetDescription()) + require.Equal(t, RouterKey, pcp.ProposalRoute()) + require.Equal(t, ProposalTypeChange, pcp.ProposalType()) + require.Nil(t, pcp.ValidateBasic()) + + pc3 := NewParamChange("", "bar", "cat") + pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc3}) + require.Error(t, pcp.ValidateBasic()) + + pc4 := NewParamChange("sub", "", "cat") + pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc4}) + require.Error(t, pcp.ValidateBasic()) +} diff --git a/x/params/types/proposal/query.pb.go b/x/params/types/proposal/query.pb.go new file mode 100644 index 000000000000..e0c770d70e24 --- /dev/null +++ b/x/params/types/proposal/query.pb.go @@ -0,0 +1,644 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/params/v1beta1/query.proto + +package proposal + +import ( + context "context" + fmt "fmt" + _ "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 + +// QueryParamsRequest is request type for the Query/Params RPC method. +type QueryParamsRequest struct { + // subspace defines the module to query the parameter for. + Subspace string `protobuf:"bytes,1,opt,name=subspace,proto3" json:"subspace,omitempty"` + // key defines the key of the parameter in the subspace. + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_2b32979c1792ccc4, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +func (m *QueryParamsRequest) GetSubspace() string { + if m != nil { + return m.Subspace + } + return "" +} + +func (m *QueryParamsRequest) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +// QueryParamsResponse is response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // param defines the queried parameter. + Param ParamChange `protobuf:"bytes,1,opt,name=param,proto3" json:"param"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2b32979c1792ccc4, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParam() ParamChange { + if m != nil { + return m.Param + } + return ParamChange{} +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.params.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.params.v1beta1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("cosmos/params/v1beta1/query.proto", fileDescriptor_2b32979c1792ccc4) } + +var fileDescriptor_2b32979c1792ccc4 = []byte{ + // 321 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x31, 0x4b, 0x3b, 0x31, + 0x14, 0xc0, 0x2f, 0xfd, 0xff, 0x5b, 0x34, 0x2e, 0x12, 0x15, 0xca, 0xa1, 0x57, 0x3d, 0x10, 0x54, + 0x30, 0xa1, 0xd5, 0xd9, 0xa1, 0xee, 0xa2, 0x05, 0x17, 0xb7, 0x5c, 0x0d, 0x69, 0x69, 0x7b, 0x2f, + 0xbd, 0x97, 0x13, 0xbb, 0x3a, 0x38, 0x17, 0xfc, 0x52, 0x1d, 0x0b, 0x2e, 0x4e, 0x22, 0xad, 0x1f, + 0x44, 0x9a, 0xb4, 0x82, 0x58, 0xc5, 0x29, 0x2f, 0x2f, 0xbf, 0xf7, 0x7b, 0x79, 0x09, 0xdd, 0x6b, + 0x02, 0xf6, 0x00, 0x85, 0x91, 0x99, 0xec, 0xa1, 0xb8, 0xab, 0x26, 0xca, 0xca, 0xaa, 0xe8, 0xe7, + 0x2a, 0x1b, 0x70, 0x93, 0x81, 0x05, 0xb6, 0xe5, 0x11, 0xee, 0x11, 0x3e, 0x47, 0xc2, 0x4d, 0x0d, + 0x1a, 0x1c, 0x21, 0x66, 0x91, 0x87, 0xc3, 0x6d, 0x0d, 0xa0, 0xbb, 0x4a, 0x48, 0xd3, 0x16, 0x32, + 0x4d, 0xc1, 0x4a, 0xdb, 0x86, 0x14, 0xe7, 0xa7, 0xf1, 0xf2, 0x6e, 0x73, 0xb3, 0x63, 0xe2, 0x3a, + 0x65, 0x57, 0xb3, 0xee, 0x97, 0x2e, 0xd9, 0x50, 0xfd, 0x5c, 0xa1, 0x65, 0x21, 0x5d, 0xc1, 0x3c, + 0x41, 0x23, 0x9b, 0xaa, 0x4c, 0x76, 0xc9, 0xc1, 0x6a, 0xe3, 0x73, 0xcf, 0xd6, 0xe9, 0xbf, 0x8e, + 0x1a, 0x94, 0x0b, 0x2e, 0x3d, 0x0b, 0xe3, 0x6b, 0xba, 0xf1, 0xc5, 0x81, 0x06, 0x52, 0x54, 0xec, + 0x8c, 0x16, 0x5d, 0x2b, 0x67, 0x58, 0xab, 0xc5, 0x7c, 0xe9, 0x64, 0xdc, 0x55, 0x9d, 0xb7, 0x64, + 0xaa, 0x55, 0xfd, 0xff, 0xe8, 0xb5, 0x12, 0x34, 0x7c, 0x59, 0x6d, 0x48, 0x68, 0xd1, 0x79, 0xd9, + 0x23, 0xa1, 0x25, 0x2f, 0x67, 0x87, 0x3f, 0x58, 0xbe, 0x0f, 0x11, 0x1e, 0xfd, 0x05, 0xf5, 0x77, + 0x8d, 0xf7, 0x1f, 0x9e, 0xdf, 0x9f, 0x0a, 0x15, 0xb6, 0x23, 0x7e, 0x7b, 0xb3, 0xfa, 0xc5, 0x68, + 0x12, 0x91, 0xf1, 0x24, 0x22, 0x6f, 0x93, 0x88, 0x0c, 0xa7, 0x51, 0x30, 0x9e, 0x46, 0xc1, 0xcb, + 0x34, 0x0a, 0x6e, 0x4e, 0x75, 0xdb, 0xb6, 0xf2, 0x84, 0x37, 0xa1, 0xb7, 0x50, 0xf8, 0xe5, 0x18, + 0x6f, 0x3b, 0xe2, 0x7e, 0xe1, 0xb3, 0x03, 0xa3, 0x50, 0x98, 0x0c, 0x0c, 0xa0, 0xec, 0x26, 0x25, + 0xf7, 0x09, 0x27, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaa, 0xd5, 0x67, 0xc7, 0x18, 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 + +// 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 { + // Params queries a specific parameter of a module, given its subspace and + // key. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.params.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries a specific parameter of a module, given its subspace and + // key. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.params.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.params.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/params/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) 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 = encodeVarintQuery(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if len(m.Subspace) > 0 { + i -= len(m.Subspace) + copy(dAtA[i:], m.Subspace) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Subspace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Param.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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Subspace) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Param.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subspace", 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.Subspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", 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.Key = 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 *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Param", 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 err := m.Param.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/params/types/proposal/query.pb.gw.go b/x/params/types/proposal/query.pb.gw.go new file mode 100644 index 000000000000..c7fab13fca65 --- /dev/null +++ b/x/params/types/proposal/query.pb.gw.go @@ -0,0 +1,166 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/params/v1beta1/query.proto + +/* +Package proposal is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package proposal + +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_Params_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + 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_Params_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + 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_Params_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Params(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_Params_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_Params_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_Params_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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "params", "v1beta1"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/params/types/proposal_test.go b/x/params/types/proposal_test.go deleted file mode 100644 index aa891c82031d..000000000000 --- a/x/params/types/proposal_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestParameterChangeProposal(t *testing.T) { - pc1 := NewParamChange("sub", "foo", "baz") - pc2 := NewParamChange("sub", "bar", "cat") - pcp := NewParameterChangeProposal("test title", "test description", []ParamChange{pc1, pc2}) - - require.Equal(t, "test title", pcp.GetTitle()) - require.Equal(t, "test description", pcp.GetDescription()) - require.Equal(t, RouterKey, pcp.ProposalRoute()) - require.Equal(t, ProposalTypeChange, pcp.ProposalType()) - require.Nil(t, pcp.ValidateBasic()) - - pc3 := NewParamChange("", "bar", "cat") - pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc3}) - require.Error(t, pcp.ValidateBasic()) - - pc4 := NewParamChange("sub", "", "cat") - pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc4}) - require.Error(t, pcp.ValidateBasic()) -} diff --git a/x/params/types/querier.go b/x/params/types/querier.go new file mode 100644 index 000000000000..afcc773ff24e --- /dev/null +++ b/x/params/types/querier.go @@ -0,0 +1,35 @@ +package types + +// Querier path constants +const ( + QueryParams = "params" +) + +// QuerySubspaceParams defines the params for querying module params by a given +// subspace and key. +type QuerySubspaceParams struct { + Subspace string + Key string +} + +// SubspaceParamsResponse defines the response for quering parameters by subspace. +type SubspaceParamsResponse struct { + Subspace string + Key string + Value string +} + +func NewQuerySubspaceParams(ss, key string) QuerySubspaceParams { + return QuerySubspaceParams{ + Subspace: ss, + Key: key, + } +} + +func NewSubspaceParamsResponse(ss, key, value string) SubspaceParamsResponse { + return SubspaceParamsResponse{ + Subspace: ss, + Key: key, + Value: value, + } +} diff --git a/x/params/subspace/subspace.go b/x/params/types/subspace.go similarity index 90% rename from x/params/subspace/subspace.go rename to x/params/types/subspace.go index 94533f9451f1..da0a755d417b 100644 --- a/x/params/subspace/subspace.go +++ b/x/params/types/subspace.go @@ -1,13 +1,12 @@ -package subspace +package types import ( "fmt" "reflect" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -22,21 +21,23 @@ const ( // Transient store persists for a block, so we use it for // recording whether the parameter has been changed or not type Subspace struct { - cdc *codec.Codec - key sdk.StoreKey // []byte -> []byte, stores parameter - tkey sdk.StoreKey // []byte -> bool, stores parameter change - name []byte - table KeyTable + cdc codec.BinaryMarshaler + legacyAmino *codec.LegacyAmino + key sdk.StoreKey // []byte -> []byte, stores parameter + tkey sdk.StoreKey // []byte -> bool, stores parameter change + name []byte + table KeyTable } // NewSubspace constructs a store with namestore -func NewSubspace(cdc *codec.Codec, key sdk.StoreKey, tkey sdk.StoreKey, name string) Subspace { +func NewSubspace(cdc codec.BinaryMarshaler, legacyAmino *codec.LegacyAmino, key sdk.StoreKey, tkey sdk.StoreKey, name string) Subspace { return Subspace{ - cdc: cdc, - key: key, - tkey: tkey, - name: []byte(name), - table: NewKeyTable(), + cdc: cdc, + legacyAmino: legacyAmino, + key: key, + tkey: tkey, + name: []byte(name), + table: NewKeyTable(), } } @@ -99,10 +100,12 @@ func (s Subspace) Validate(ctx sdk.Context, key []byte, value interface{}) error // Get queries for a parameter by key from the Subspace's KVStore and sets the // value to the provided pointer. If the value does not exist, it will panic. func (s Subspace) Get(ctx sdk.Context, key []byte, ptr interface{}) { + s.checkType(key, ptr) + store := s.kvStore(ctx) bz := store.Get(key) - if err := s.cdc.UnmarshalJSON(bz, ptr); err != nil { + if err := s.legacyAmino.UnmarshalJSON(bz, ptr); err != nil { panic(err) } } @@ -117,7 +120,9 @@ func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) { return } - if err := s.cdc.UnmarshalJSON(bz, ptr); err != nil { + s.checkType(key, ptr) + + if err := s.legacyAmino.UnmarshalJSON(bz, ptr); err != nil { panic(err) } } @@ -167,7 +172,7 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, value interface{}) { s.checkType(key, value) store := s.kvStore(ctx) - bz, err := s.cdc.MarshalJSON(value) + bz, err := s.legacyAmino.MarshalJSON(value) if err != nil { panic(err) } @@ -194,7 +199,7 @@ func (s Subspace) Update(ctx sdk.Context, key, value []byte) error { dest := reflect.New(ty).Interface() s.GetIfExists(ctx, key, dest) - if err := s.cdc.UnmarshalJSON(value, dest); err != nil { + if err := s.legacyAmino.UnmarshalJSON(value, dest); err != nil { return err } diff --git a/x/params/subspace/subspace_test.go b/x/params/types/subspace_test.go similarity index 85% rename from x/params/subspace/subspace_test.go rename to x/params/types/subspace_test.go index 05707097d478..ed508d00e93c 100644 --- a/x/params/subspace/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -1,4 +1,4 @@ -package subspace_test +package types_test import ( "fmt" @@ -6,26 +6,27 @@ import ( "time" "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/codec" + "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params/subspace" + "github.com/cosmos/cosmos-sdk/x/params/types" ) type SubspaceTestSuite struct { suite.Suite - cdc *codec.Codec - ctx sdk.Context - ss subspace.Subspace + cdc codec.BinaryMarshaler + amino *codec.LegacyAmino + ctx sdk.Context + ss types.Subspace } func (suite *SubspaceTestSuite) SetupTest() { - cdc := codec.New() db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) @@ -33,10 +34,12 @@ func (suite *SubspaceTestSuite) SetupTest() { ms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) suite.NoError(ms.LoadLatestVersion()) - ss := subspace.NewSubspace(cdc, key, tkey, "testsubspace") + encCfg := simapp.MakeTestEncodingConfig() + ss := types.NewSubspace(encCfg.Marshaler, encCfg.Amino, key, tkey, "testsubspace") - suite.cdc = cdc - suite.ctx = sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger()) + suite.cdc = encCfg.Marshaler + suite.amino = encCfg.Amino + suite.ctx = sdk.NewContext(ms, tmproto.Header{}, false, log.NewNopLogger()) suite.ss = ss.WithKeyTable(paramKeyTable()) } @@ -46,7 +49,7 @@ func (suite *SubspaceTestSuite) TestKeyTable() { suite.ss.WithKeyTable(paramKeyTable()) }) suite.Require().NotPanics(func() { - ss := subspace.NewSubspace(codec.New(), key, tkey, "testsubspace2") + ss := types.NewSubspace(suite.cdc, suite.amino, key, tkey, "testsubspace2") ss = ss.WithKeyTable(paramKeyTable()) }) } @@ -111,7 +114,7 @@ func (suite *SubspaceTestSuite) TestModified() { func (suite *SubspaceTestSuite) TestUpdate() { suite.Require().Panics(func() { - suite.ss.Update(suite.ctx, []byte("invalid_key"), nil) + suite.ss.Update(suite.ctx, []byte("invalid_key"), nil) // nolint:errcheck }) t := time.Hour * 48 @@ -121,12 +124,12 @@ func (suite *SubspaceTestSuite) TestUpdate() { bad := time.Minute * 5 - bz, err := suite.cdc.MarshalJSON(bad) + bz, err := suite.amino.MarshalJSON(bad) suite.Require().NoError(err) suite.Require().Error(suite.ss.Update(suite.ctx, keyUnbondingTime, bz)) good := time.Hour * 360 - bz, err = suite.cdc.MarshalJSON(good) + bz, err = suite.amino.MarshalJSON(good) suite.Require().NoError(err) suite.Require().NoError(suite.ss.Update(suite.ctx, keyUnbondingTime, bz)) @@ -162,7 +165,7 @@ func (suite *SubspaceTestSuite) TestGetParamSet() { func (suite *SubspaceTestSuite) TestSetParamSet() { testCases := []struct { name string - ps subspace.ParamSet + ps types.ParamSet }{ {"invalid unbonding time", ¶ms{time.Hour * 1, 100, "stake"}}, {"invalid bond denom", ¶ms{time.Hour * 48, 100, ""}}, diff --git a/x/params/subspace/table.go b/x/params/types/table.go similarity index 96% rename from x/params/subspace/table.go rename to x/params/types/table.go index 92e72242b967..ef09c39d886e 100644 --- a/x/params/subspace/table.go +++ b/x/params/types/table.go @@ -1,4 +1,4 @@ -package subspace +package types import ( "reflect" @@ -48,7 +48,7 @@ func (t KeyTable) RegisterType(psp ParamSetPair) KeyTable { rty := reflect.TypeOf(psp.Value) // indirect rty if it is a pointer - if rty.Kind() == reflect.Ptr { + for rty.Kind() == reflect.Ptr { rty = rty.Elem() } diff --git a/x/params/types/table_test.go b/x/params/types/table_test.go new file mode 100644 index 000000000000..0baff40f8c6c --- /dev/null +++ b/x/params/types/table_test.go @@ -0,0 +1,46 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/params/types" +) + +func TestKeyTable(t *testing.T) { + table := types.NewKeyTable() + + require.Panics(t, func() { table.RegisterType(types.ParamSetPair{[]byte(""), nil, nil}) }) + require.Panics(t, func() { table.RegisterType(types.ParamSetPair{[]byte("!@#$%"), nil, nil}) }) + require.Panics(t, func() { table.RegisterType(types.ParamSetPair{[]byte("hello,"), nil, nil}) }) + require.Panics(t, func() { table.RegisterType(types.ParamSetPair{[]byte("hello"), nil, nil}) }) + + require.NotPanics(t, func() { + table.RegisterType(types.ParamSetPair{keyBondDenom, string("stake"), validateBondDenom}) + }) + require.NotPanics(t, func() { + table.RegisterType(types.ParamSetPair{keyMaxValidators, uint16(100), validateMaxValidators}) + }) + require.Panics(t, func() { + table.RegisterType(types.ParamSetPair{keyUnbondingTime, time.Duration(1), nil}) + }) + require.NotPanics(t, func() { + table.RegisterType(types.ParamSetPair{keyUnbondingTime, time.Duration(1), validateMaxValidators}) + }) + require.NotPanics(t, func() { + newTable := types.NewKeyTable() + newTable.RegisterParamSet(¶ms{}) + }) + + require.Panics(t, func() { table.RegisterParamSet(¶ms{}) }) + require.Panics(t, func() { types.NewKeyTable(types.ParamSetPair{[]byte(""), nil, nil}) }) + + require.NotPanics(t, func() { + types.NewKeyTable( + types.ParamSetPair{[]byte("test"), string("stake"), validateBondDenom}, + types.ParamSetPair{[]byte("test2"), uint16(100), validateMaxValidators}, + ) + }) +} diff --git a/x/simulation/account.go b/x/simulation/account.go deleted file mode 100644 index 4abcd8df52a3..000000000000 --- a/x/simulation/account.go +++ /dev/null @@ -1,85 +0,0 @@ -package simulation - -import ( - "math/rand" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Account contains a privkey, pubkey, address tuple -// eventually more useful data can be placed in here. -// (e.g. number of coins) -type Account struct { - PrivKey crypto.PrivKey - PubKey crypto.PubKey - Address sdk.AccAddress -} - -// Equals returns true if two accounts are equal -func (acc Account) Equals(acc2 Account) bool { - return acc.Address.Equals(acc2.Address) -} - -// RandomAcc picks and returns a random account from an array and returs its -// position in the array. -func RandomAcc(r *rand.Rand, accs []Account) (Account, int) { - idx := r.Intn(len(accs)) - return accs[idx], idx -} - -// RandomAccounts generates n random accounts -func RandomAccounts(r *rand.Rand, n int) []Account { - accs := make([]Account, n) - for i := 0; i < n; i++ { - // don't need that much entropy for simulation - privkeySeed := make([]byte, 15) - r.Read(privkeySeed) - - accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) - accs[i].PubKey = accs[i].PrivKey.PubKey() - accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) - } - - return accs -} - -// FindAccount iterates over all the simulation accounts to find the one that matches -// the given address -func FindAccount(accs []Account, address sdk.Address) (Account, bool) { - for _, acc := range accs { - if acc.Address.Equals(address) { - return acc, true - } - } - - return Account{}, false -} - -// RandomFees returns a random fee by selecting a random coin denomination and -// amount from the account's available balance. If the user doesn't have enough -// funds for paying fees, it returns empty coins. -func RandomFees(r *rand.Rand, ctx sdk.Context, spendableCoins sdk.Coins) (sdk.Coins, error) { - if spendableCoins.Empty() { - return nil, nil - } - - denomIndex := r.Intn(len(spendableCoins)) - randCoin := spendableCoins[denomIndex] - - if randCoin.Amount.IsZero() { - return nil, nil - } - - amt, err := RandPositiveInt(r, randCoin.Amount) - if err != nil { - return nil, err - } - - // Create a random fee and verify the fees are within the account's spendable - // balance. - fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt)) - return fees, nil -} diff --git a/x/simulation/account_test.go b/x/simulation/account_test.go deleted file mode 100644 index adb28dcf984f..000000000000 --- a/x/simulation/account_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package simulation_test - -import ( - "math/rand" - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/simulation" -) - -func TestRandomAccounts(t *testing.T) { - t.Parallel() - r := rand.New(rand.NewSource(time.Now().Unix())) - tests := []struct { - name string - n int - want int - }{ - {"0-accounts", 0, 0}, - {"0-accounts", 1, 1}, - {"0-accounts", 1_000, 1_000}, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got := simulation.RandomAccounts(r, tt.n) - require.Equal(t, tt.want, len(got)) - if tt.n == 0 { - return - } - acc, i := simulation.RandomAcc(r, got) - require.True(t, acc.Equals(got[i])) - accFound, found := simulation.FindAccount(got, acc.Address) - require.True(t, found) - require.True(t, accFound.Equals(acc)) - }) - } -} - -func TestFindAccountEmptySlice(t *testing.T) { - t.Parallel() - r := rand.New(rand.NewSource(time.Now().Unix())) - accs := simulation.RandomAccounts(r, 1) - require.Equal(t, 1, len(accs)) - acc, found := simulation.FindAccount(nil, accs[0].Address) - require.False(t, found) - require.Nil(t, acc.Address) - require.Nil(t, acc.PrivKey) - require.Nil(t, acc.PubKey) -} - -func TestRandomFees(t *testing.T) { - t.Parallel() - r := rand.New(rand.NewSource(time.Now().Unix())) - tests := []struct { - name string - spendableCoins sdk.Coins - wantEmpty bool - wantErr bool - }{ - {"0 coins", sdk.Coins{}, true, false}, - {"0 coins", sdk.NewCoins(sdk.NewInt64Coin("aaa", 10), sdk.NewInt64Coin("bbb", 5)), false, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := simulation.RandomFees(r, sdk.Context{}, tt.spendableCoins) - if (err != nil) != tt.wantErr { - t.Errorf("RandomFees() error = %v, wantErr %v", err, tt.wantErr) - return - } - require.Equal(t, tt.wantEmpty, got.Empty()) - }) - } -} diff --git a/x/simulation/event_stats.go b/x/simulation/event_stats.go index 1a6201aaca77..0e4289fd243e 100644 --- a/x/simulation/event_stats.go +++ b/x/simulation/event_stats.go @@ -48,7 +48,7 @@ func (es EventStats) ExportJSON(path string) { panic(err) } - err = ioutil.WriteFile(path, bz, 0644) + err = ioutil.WriteFile(path, bz, 0600) if err != nil { panic(err) } diff --git a/x/simulation/log.go b/x/simulation/log.go index a865fbee6056..236a7244074f 100644 --- a/x/simulation/log.go +++ b/x/simulation/log.go @@ -18,6 +18,7 @@ func NewLogWriter(testingmode bool) LogWriter { if !testingmode { return &DummyLogWriter{} } + return &StandardLogWriter{} } @@ -34,9 +35,12 @@ func (lw *StandardLogWriter) AddEntry(opEntry OperationEntry) { // PrintLogs - print the logs to a simulation file func (lw *StandardLogWriter) PrintLogs() { f := createLogFile() + defer f.Close() + for i := 0; i < len(lw.OpEntries); i++ { writeEntry := fmt.Sprintf("%s\n", (lw.OpEntries[i]).MustMarshal()) _, err := f.WriteString(writeEntry) + if err != nil { panic("Failed to write logs to file") } @@ -45,17 +49,22 @@ func (lw *StandardLogWriter) PrintLogs() { func createLogFile() *os.File { var f *os.File - fileName := fmt.Sprintf("%s.log", time.Now().Format("2006-01-02_15:04:05")) - folderPath := os.ExpandEnv("$HOME/.simapp/simulations") + fileName := fmt.Sprintf("%s.log", time.Now().Format("2006-01-02_15:04:05")) + folderPath := path.Join(os.ExpandEnv("$HOME"), ".simapp", "simulations") filePath := path.Join(folderPath, fileName) err := os.MkdirAll(folderPath, os.ModePerm) if err != nil { panic(err) } - f, _ = os.Create(filePath) + + f, err = os.Create(filePath) + if err != nil { + panic(err) + } fmt.Printf("Logs to writing to %s\n", filePath) + return f } diff --git a/x/simulation/mock_tendermint.go b/x/simulation/mock_tendermint.go index 349de27b60c2..ad84e8213d54 100644 --- a/x/simulation/mock_tendermint.go +++ b/x/simulation/mock_tendermint.go @@ -8,8 +8,9 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" + cryptoenc "github.com/tendermint/tendermint/crypto/encoding" tmbytes "github.com/tendermint/tendermint/libs/bytes" - tmtypes "github.com/tendermint/tendermint/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) type mockValidator struct { @@ -18,9 +19,8 @@ type mockValidator struct { } func (mv mockValidator) String() string { - return fmt.Sprintf("mockValidator{%s:%X power:%v state:%v}", - mv.val.PubKey.Type, - mv.val.PubKey.Data, + return fmt.Sprintf("mockValidator{%s power:%v state:%v}", + mv.val.PubKey.String(), mv.val.Power, mv.livenessState) } @@ -28,14 +28,12 @@ func (mv mockValidator) String() string { type mockValidators map[string]mockValidator // get mockValidators from abci validators -func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, - params Params) mockValidators { - +func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, params Params) mockValidators { validators := make(mockValidators) + for _, validator := range abciVals { - str := fmt.Sprintf("%v", validator.PubKey) - liveliness := GetMemberOfInitialState(r, - params.InitialLivenessWeightings) + str := fmt.Sprintf("%X", validator.PubKey.GetEd25519()) + liveliness := GetMemberOfInitialState(r, params.InitialLivenessWeightings()) validators[str] = mockValidator{ val: validator, @@ -50,11 +48,14 @@ func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, func (vals mockValidators) getKeys() []string { keys := make([]string, len(vals)) i := 0 + for key := range vals { keys[i] = key i++ } + sort.Strings(keys) + return keys } @@ -66,41 +67,47 @@ func (vals mockValidators) randomProposer(r *rand.Rand) tmbytes.HexBytes { if len(keys) == 0 { return nil } + key := keys[r.Intn(len(keys))] + proposer := vals[key].val - pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) - if err != nil { + pk, err := cryptoenc.PubKeyFromProto(proposer.PubKey) + if err != nil { //nolint:wsl panic(err) } + return pk.Address() } -// updateValidators mimicks Tendermint's update logic -// nolint: unparam -func updateValidators(tb testing.TB, r *rand.Rand, params Params, - current map[string]mockValidator, updates []abci.ValidatorUpdate, - event func(route, op, evResult string)) map[string]mockValidator { +// updateValidators mimics Tendermint's update logic. +func updateValidators( + tb testing.TB, + r *rand.Rand, + params Params, + current map[string]mockValidator, + updates []abci.ValidatorUpdate, + event func(route, op, evResult string), +) map[string]mockValidator { for _, update := range updates { - str := fmt.Sprintf("%v", update.PubKey) + str := fmt.Sprintf("%X", update.PubKey.GetEd25519()) if update.Power == 0 { if _, ok := current[str]; !ok { - tb.Fatalf("tried to delete a nonexistent validator") + tb.Fatalf("tried to delete a nonexistent validator: %s", str) } + event("end_block", "validator_updates", "kicked") delete(current, str) - - } else if mVal, ok := current[str]; ok { + } else if _, ok := current[str]; ok { // validator already exists - mVal.val = update event("end_block", "validator_updates", "updated") } else { // Set this new validator current[str] = mockValidator{ update, - GetMemberOfInitialState(r, params.InitialLivenessWeightings), + GetMemberOfInitialState(r, params.InitialLivenessWeightings()), } event("end_block", "validator_updates", "added") } @@ -114,8 +121,7 @@ func updateValidators(tb testing.TB, r *rand.Rand, params Params, func RandomRequestBeginBlock(r *rand.Rand, params Params, validators mockValidators, pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, - event func(route, op, evResult string), header abci.Header) abci.RequestBeginBlock { - + event func(route, op, evResult string), header tmproto.Header) abci.RequestBeginBlock { if len(validators) == 0 { return abci.RequestBeginBlock{ Header: header, @@ -123,9 +129,10 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, } voteInfos := make([]abci.VoteInfo, len(validators)) + for i, key := range validators.getKeys() { mVal := validators[key] - mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) + mVal.livenessState = params.LivenessTransitionMatrix().NextState(r, mVal.livenessState) signed := true if mVal.livenessState == 1 { @@ -144,10 +151,11 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, event("begin_block", "signing", "missed") } - pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) + pubkey, err := cryptoenc.PubKeyFromProto(mVal.val.PubKey) if err != nil { panic(err) } + voteInfos[i] = abci.VoteInfo{ Validator: abci.Validator{ Address: pubkey.Address(), @@ -169,18 +177,19 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, // TODO: Determine capacity before allocation evidence := make([]abci.Evidence, 0) - for r.Float64() < params.EvidenceFraction { + for r.Float64() < params.EvidenceFraction() { height := header.Height time := header.Time vals := voteInfos - if r.Float64() < params.PastEvidenceFraction && header.Height > 1 { + if r.Float64() < params.PastEvidenceFraction() && header.Height > 1 { height = int64(r.Intn(int(header.Height)-1)) + 1 // Tendermint starts at height 1 // array indices offset by one time = pastTimes[height-1] vals = pastVoteInfos[height-1] } + validator := vals[r.Intn(len(vals))].Validator var totalVotingPower int64 @@ -190,13 +199,14 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, evidence = append(evidence, abci.Evidence{ - Type: tmtypes.ABCIEvidenceTypeDuplicateVote, + Type: abci.EvidenceType_DUPLICATE_VOTE, Validator: validator, Height: height, Time: time, TotalVotingPower: totalVotingPower, }, ) + event("begin_block", "evidence", "ok") } diff --git a/x/simulation/operation.go b/x/simulation/operation.go index 8565b42961d7..914daafc8b91 100644 --- a/x/simulation/operation.go +++ b/x/simulation/operation.go @@ -4,25 +4,10 @@ import ( "encoding/json" "math/rand" "sort" - "time" - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/simulation" ) -// Operation runs a state machine transition, and ensures the transition -// happened as expected. The operation could be running and testing a fuzzed -// transaction, or doing the same for a message. -// -// For ease of debugging, an operation returns a descriptive message "action", -// which details what this fuzzed state machine transition actually did. -// -// Operations can optionally provide a list of "FutureOperations" to run later -// These will be ran at the beginning of the corresponding block. -type Operation func(r *rand.Rand, app *baseapp.BaseApp, - ctx sdk.Context, accounts []Account, chainID string) ( - OperationMsg OperationMsg, futureOps []FutureOperation, err error) - // entry kinds for use within OperationEntry const ( BeginBlockEntryKind = "begin_block" @@ -60,12 +45,12 @@ func EndBlockEntry(height int64) OperationEntry { } // MsgEntry - operation entry for standard msg -func MsgEntry(height, order int64, opMsg OperationMsg) OperationEntry { +func MsgEntry(height, order int64, opMsg simulation.OperationMsg) OperationEntry { return NewOperationEntry(MsgEntryKind, height, order, opMsg.MustMarshal()) } // QueuedMsgEntry creates an operation entry for a given queued message. -func QueuedMsgEntry(height int64, opMsg OperationMsg) OperationEntry { +func QueuedMsgEntry(height int64, opMsg simulation.OperationMsg) OperationEntry { return NewOperationEntry(QueuedMsgEntryKind, height, -1, opMsg.MustMarshal()) } @@ -75,70 +60,14 @@ func (oe OperationEntry) MustMarshal() json.RawMessage { if err != nil { panic(err) } - return out -} - -//_____________________________________________________________________ - -// OperationMsg - structure for operation output -type OperationMsg struct { - Route string `json:"route" yaml:"route"` // msg route (i.e module name) - Name string `json:"name" yaml:"name"` // operation name (msg Type or "no-operation") - Comment string `json:"comment" yaml:"comment"` // additional comment - OK bool `json:"ok" yaml:"ok"` // success - Msg json.RawMessage `json:"msg" yaml:"msg"` // JSON encoded msg -} - -// NewOperationMsgBasic creates a new operation message from raw input. -func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) OperationMsg { - return OperationMsg{ - Route: route, - Name: name, - Comment: comment, - OK: ok, - Msg: msg, - } -} - -// NewOperationMsg - create a new operation message from sdk.Msg -func NewOperationMsg(msg sdk.Msg, ok bool, comment string) OperationMsg { - return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes()) -} - -// NoOpMsg - create a no-operation message -func NoOpMsg(route string) OperationMsg { - return NewOperationMsgBasic(route, "no-operation", "", false, nil) -} - -// log entry text for this operation msg -func (om OperationMsg) String() string { - out, err := json.Marshal(om) - if err != nil { - panic(err) - } - return string(out) -} -// MustMarshal Marshals the operation msg, panic on error -func (om OperationMsg) MustMarshal() json.RawMessage { - out, err := json.Marshal(om) - if err != nil { - panic(err) - } return out } -// LogEvent adds an event for the events stats -func (om OperationMsg) LogEvent(eventLogger func(route, op, evResult string)) { - pass := "ok" - if !om.OK { - pass = "failure" - } - eventLogger(om.Route, om.Name, pass) -} +//_____________________________________________________________________ // OperationQueue defines an object for a queue of operations -type OperationQueue map[int][]Operation +type OperationQueue map[int][]simulation.Operation // NewOperationQueue creates a new OperationQueue instance. func NewOperationQueue() OperationQueue { @@ -146,9 +75,7 @@ func NewOperationQueue() OperationQueue { } // queueOperations adds all future operations into the operation queue. -func queueOperations(queuedOps OperationQueue, - queuedTimeOps []FutureOperation, futureOps []FutureOperation) { - +func queueOperations(queuedOps OperationQueue, queuedTimeOps []simulation.FutureOperation, futureOps []simulation.FutureOperation) { if futureOps == nil { return } @@ -159,8 +86,9 @@ func queueOperations(queuedOps OperationQueue, if val, ok := queuedOps[futureOp.BlockHeight]; ok { queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op) } else { - queuedOps[futureOp.BlockHeight] = []Operation{futureOp.Op} + queuedOps[futureOp.BlockHeight] = []simulation.Operation{futureOp.Op} } + continue } @@ -172,7 +100,8 @@ func queueOperations(queuedOps OperationQueue, return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime) }, ) - queuedTimeOps = append(queuedTimeOps, FutureOperation{}) + + queuedTimeOps = append(queuedTimeOps, simulation.FutureOperation{}) copy(queuedTimeOps[index+1:], queuedTimeOps[index:]) queuedTimeOps[index] = futureOp } @@ -180,57 +109,54 @@ func queueOperations(queuedOps OperationQueue, //________________________________________________________________________ -// FutureOperation is an operation which will be ran at the beginning of the -// provided BlockHeight. If both a BlockHeight and BlockTime are specified, it -// will use the BlockHeight. In the (likely) event that multiple operations -// are queued at the same block height, they will execute in a FIFO pattern. -type FutureOperation struct { - BlockHeight int - BlockTime time.Time - Op Operation -} - -//________________________________________________________________________ - // WeightedOperation is an operation with associated weight. // This is used to bias the selection operation within the simulator. type WeightedOperation struct { - Weight int - Op Operation + weight int + op simulation.Operation +} + +func (w WeightedOperation) Weight() int { + return w.weight +} + +func (w WeightedOperation) Op() simulation.Operation { + return w.op } // NewWeightedOperation creates a new WeightedOperation instance -func NewWeightedOperation(weight int, op Operation) WeightedOperation { +func NewWeightedOperation(weight int, op simulation.Operation) WeightedOperation { return WeightedOperation{ - Weight: weight, - Op: op, + weight: weight, + op: op, } } // WeightedOperations is the group of all weighted operations to simulate. -type WeightedOperations []WeightedOperation +type WeightedOperations []simulation.WeightedOperation func (ops WeightedOperations) totalWeight() int { totalOpWeight := 0 for _, op := range ops { - totalOpWeight += op.Weight + totalOpWeight += op.Weight() } + return totalOpWeight } -type selectOpFn func(r *rand.Rand) Operation - -func (ops WeightedOperations) getSelectOpFn() selectOpFn { +func (ops WeightedOperations) getSelectOpFn() simulation.SelectOpFn { totalOpWeight := ops.totalWeight() - return func(r *rand.Rand) Operation { + + return func(r *rand.Rand) simulation.Operation { x := r.Intn(totalOpWeight) for i := 0; i < len(ops); i++ { - if x <= ops[i].Weight { - return ops[i].Op + if x <= ops[i].Weight() { + return ops[i].Op() } - x -= ops[i].Weight + + x -= ops[i].Weight() } // shouldn't happen - return ops[0].Op + return ops[0].Op() } } diff --git a/x/simulation/params.go b/x/simulation/params.go index 2673822d8f43..12e8e097263f 100644 --- a/x/simulation/params.go +++ b/x/simulation/params.go @@ -5,9 +5,13 @@ import ( "fmt" "math/rand" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/types" + "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/types/simulation" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const ( @@ -37,78 +41,86 @@ var ( }) ) -// AppParams defines a flat JSON of key/values for all possible configurable -// simulation parameters. It might contain: operation weights, simulation parameters -// and flattened module state parameters (i.e not stored under it's respective module name). -type AppParams map[string]json.RawMessage - -// ParamSimulator creates a parameter value from a source of random number -type ParamSimulator func(r *rand.Rand) - -// GetOrGenerate attempts to get a given parameter by key from the AppParams -// object. If it exists, it'll be decoded and returned. Otherwise, the provided -// ParamSimulator is used to generate a random value or default value (eg: in the -// case of operation weights where Rand is not used). -func (sp AppParams) GetOrGenerate(cdc *codec.Codec, key string, ptr interface{}, r *rand.Rand, ps ParamSimulator) { - if v, ok := sp[key]; ok && v != nil { - cdc.MustUnmarshalJSON(v, ptr) - return - } +// Params define the parameters necessary for running the simulations +type Params struct { + pastEvidenceFraction float64 + numKeys int + evidenceFraction float64 + initialLivenessWeightings []int + livenessTransitionMatrix simulation.TransitionMatrix + blockSizeTransitionMatrix simulation.TransitionMatrix +} - ps(r) +func (p Params) PastEvidenceFraction() float64 { + return p.pastEvidenceFraction } -// ContentSimulatorFn defines a function type alias for generating random proposal -// content. -type ContentSimulatorFn func(r *rand.Rand, ctx sdk.Context, accs []Account) govtypes.Content +func (p Params) NumKeys() int { + return p.numKeys +} -// Params define the parameters necessary for running the simulations -type Params struct { - PastEvidenceFraction float64 - NumKeys int - EvidenceFraction float64 - InitialLivenessWeightings []int - LivenessTransitionMatrix TransitionMatrix - BlockSizeTransitionMatrix TransitionMatrix +func (p Params) EvidenceFraction() float64 { + return p.evidenceFraction +} + +func (p Params) InitialLivenessWeightings() []int { + return p.initialLivenessWeightings +} + +func (p Params) LivenessTransitionMatrix() simulation.TransitionMatrix { + return p.livenessTransitionMatrix +} + +func (p Params) BlockSizeTransitionMatrix() simulation.TransitionMatrix { + return p.blockSizeTransitionMatrix } // RandomParams returns random simulation parameters func RandomParams(r *rand.Rand) Params { return Params{ - PastEvidenceFraction: r.Float64(), - NumKeys: RandIntBetween(r, 2, 2500), // number of accounts created for the simulation - EvidenceFraction: r.Float64(), - InitialLivenessWeightings: []int{RandIntBetween(r, 1, 80), r.Intn(10), r.Intn(10)}, - LivenessTransitionMatrix: defaultLivenessTransitionMatrix, - BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, + pastEvidenceFraction: r.Float64(), + numKeys: simulation.RandIntBetween(r, 2, 2500), // number of accounts created for the simulation + evidenceFraction: r.Float64(), + initialLivenessWeightings: []int{simulation.RandIntBetween(r, 1, 80), r.Intn(10), r.Intn(10)}, + livenessTransitionMatrix: defaultLivenessTransitionMatrix, + blockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix, } } //----------------------------------------------------------------------------- // Param change proposals -// SimValFn function to generate the randomized parameter change value -type SimValFn func(r *rand.Rand) string - // ParamChange defines the object used for simulating parameter change proposals type ParamChange struct { - Subspace string - Key string - SimValue SimValFn + subspace string + key string + simValue simulation.SimValFn +} + +func (spc ParamChange) Subspace() string { + return spc.subspace +} + +func (spc ParamChange) Key() string { + return spc.key +} + +func (spc ParamChange) SimValue() simulation.SimValFn { + return spc.simValue } // NewSimParamChange creates a new ParamChange instance -func NewSimParamChange(subspace, key string, simVal SimValFn) ParamChange { +func NewSimParamChange(subspace, key string, simVal simulation.SimValFn) simulation.ParamChange { return ParamChange{ - Subspace: subspace, - Key: key, - SimValue: simVal, + subspace: subspace, + key: key, + simValue: simVal, } } // ComposedKey creates a new composed key for the param change proposal func (spc ParamChange) ComposedKey() string { - return fmt.Sprintf("%s/%s", spc.Subspace, spc.Key) + return fmt.Sprintf("%s/%s", spc.Subspace(), spc.Key()) } //----------------------------------------------------------------------------- @@ -117,7 +129,58 @@ func (spc ParamChange) ComposedKey() string { // WeightedProposalContent defines a common struct for proposal contents defined by // external modules (i.e outside gov) type WeightedProposalContent struct { - AppParamsKey string // key used to retrieve the value of the weight from the simulation application params - DefaultWeight int // default weight - ContentSimulatorFn ContentSimulatorFn // content simulator function + appParamsKey string // key used to retrieve the value of the weight from the simulation application params + defaultWeight int // default weight + contentSimulatorFn simulation.ContentSimulatorFn // content simulator function +} + +func NewWeightedProposalContent(appParamsKey string, defaultWeight int, contentSimulatorFn simulation.ContentSimulatorFn) simulation.WeightedProposalContent { + return &WeightedProposalContent{appParamsKey: appParamsKey, defaultWeight: defaultWeight, contentSimulatorFn: contentSimulatorFn} +} + +func (w WeightedProposalContent) AppParamsKey() string { + return w.appParamsKey +} + +func (w WeightedProposalContent) DefaultWeight() int { + return w.defaultWeight +} + +func (w WeightedProposalContent) ContentSimulatorFn() simulation.ContentSimulatorFn { + return w.contentSimulatorFn +} + +//----------------------------------------------------------------------------- +// Param change proposals + +// randomConsensusParams returns random simulation consensus parameters, it extracts the Evidence from the Staking genesis state. +func randomConsensusParams(r *rand.Rand, appState json.RawMessage, cdc codec.JSONMarshaler) *abci.ConsensusParams { + var genesisState map[string]json.RawMessage + err := json.Unmarshal(appState, &genesisState) + if err != nil { + panic(err) + } + + stakingGenesisState := stakingtypes.GetGenesisStateFromAppState(cdc, genesisState) + consensusParams := &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: int64(simulation.RandIntBetween(r, 20000000, 30000000)), + MaxGas: -1, + }, + Validator: &tmproto.ValidatorParams{ + PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}, + }, + Evidence: &tmproto.EvidenceParams{ + MaxAgeNumBlocks: int64(stakingGenesisState.Params.UnbondingTime / AverageBlockTime), + MaxAgeDuration: stakingGenesisState.Params.UnbondingTime, + }, + } + + bz, err := json.MarshalIndent(&consensusParams, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated consensus parameters:\n%s\n", bz) + + return consensusParams } diff --git a/x/simulation/params_test.go b/x/simulation/params_test.go new file mode 100644 index 000000000000..d0b538c26e4d --- /dev/null +++ b/x/simulation/params_test.go @@ -0,0 +1,54 @@ +package simulation + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" +) + +func TestParamChange(t *testing.T) { + subspace, key := "theSubspace", "key" + f := func(r *rand.Rand) string { + return "theResult" + } + + pChange := NewSimParamChange(subspace, key, f) + + require.Equal(t, subspace, pChange.Subspace()) + require.Equal(t, key, pChange.Key()) + require.Equal(t, f(nil), pChange.SimValue()(nil)) + require.Equal(t, fmt.Sprintf("%s/%s", subspace, key), pChange.ComposedKey()) +} + +func TestNewWeightedProposalContent(t *testing.T) { + key := "theKey" + weight := 1 + content := &testContent{} + f := func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { + return content + } + + pContent := NewWeightedProposalContent(key, weight, f) + + require.Equal(t, key, pContent.AppParamsKey()) + require.Equal(t, weight, pContent.DefaultWeight()) + + ctx := sdk.NewContext(nil, tmproto.Header{}, true, nil) + require.Equal(t, content, pContent.ContentSimulatorFn()(nil, ctx, nil)) +} + +type testContent struct { +} + +func (t testContent) GetTitle() string { return "" } +func (t testContent) GetDescription() string { return "" } +func (t testContent) ProposalRoute() string { return "" } +func (t testContent) ProposalType() string { return "" } +func (t testContent) ValidateBasic() error { return nil } +func (t testContent) String() string { return "" } diff --git a/x/simulation/simulate.go b/x/simulation/simulate.go index 800432c74d60..fdcf224611ba 100644 --- a/x/simulation/simulate.go +++ b/x/simulation/simulate.go @@ -1,7 +1,6 @@ package simulation import ( - "encoding/json" "fmt" "io" "math/rand" @@ -12,27 +11,29 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/simulation" ) -// AppStateFn returns the app state json bytes and the genesis accounts -type AppStateFn func(r *rand.Rand, accs []Account, config Config) ( - appState json.RawMessage, accounts []Account, chainId string, genesisTimestamp time.Time, -) +const AverageBlockTime = 6 * time.Second // initialize the chain for the simulation func initChain( - r *rand.Rand, params Params, accounts []Account, app *baseapp.BaseApp, - appStateFn AppStateFn, config Config, -) (mockValidators, time.Time, []Account, string) { - + r *rand.Rand, params Params, accounts []simulation.Account, app *baseapp.BaseApp, + appStateFn simulation.AppStateFn, config simulation.Config, cdc codec.JSONMarshaler, +) (mockValidators, time.Time, []simulation.Account, string) { appState, accounts, chainID, genesisTimestamp := appStateFn(r, accounts, config) + consensusParams := randomConsensusParams(r, appState, cdc) + req := abci.RequestInitChain{ - AppStateBytes: appState, - ChainId: chainID, + AppStateBytes: appState, + ChainId: chainID, + ConsensusParams: consensusParams, } res := app.InitChain(req) validators := newMockValidators(r, res.Validators, params) @@ -44,26 +45,31 @@ func initChain( // operations, testing the provided invariants, but using the provided config.Seed. // TODO: split this monster function up func SimulateFromSeed( - tb testing.TB, w io.Writer, app *baseapp.BaseApp, - appStateFn AppStateFn, ops WeightedOperations, - blackListedAccs map[string]bool, config Config, + tb testing.TB, + w io.Writer, + app *baseapp.BaseApp, + appStateFn simulation.AppStateFn, + randAccFn simulation.RandomAccountFn, + ops WeightedOperations, + blockedAddrs map[string]bool, + config simulation.Config, + cdc codec.JSONMarshaler, ) (stopEarly bool, exportedParams Params, err error) { - // in case we have to end early, don't os.Exit so that we can run cleanup code. - testingMode, t, b := getTestingMode(tb) - fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(config.Seed)) + testingMode, _, b := getTestingMode(tb) + fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(config.Seed)) r := rand.New(rand.NewSource(config.Seed)) params := RandomParams(r) fmt.Fprintf(w, "Randomized simulation params: \n%s\n", mustMarshalJSONIndent(params)) timeDiff := maxTimePerBlock - minTimePerBlock - accs := RandomAccounts(r, params.NumKeys) + accs := randAccFn(r, params.NumKeys()) eventStats := NewEventStats() // Second variable to keep pending validator set (delayed one block since // TM 0.24) Initially this is the same as the initial validator set - validators, genesisTimestamp, accs, chainID := initChain(r, params, accs, app, appStateFn, config) + validators, genesisTimestamp, accs, chainID := initChain(r, params, accs, app, appStateFn, config, cdc) if len(accs) == 0 { return true, params, fmt.Errorf("must have greater than zero genesis accounts") } @@ -76,18 +82,18 @@ func SimulateFromSeed( ) // remove module account address if they exist in accs - var tmpAccs []Account + var tmpAccs []simulation.Account + for _, acc := range accs { - if !blackListedAccs[acc.Address.String()] { + if !blockedAddrs[acc.Address.String()] { tmpAccs = append(tmpAccs, acc) } } accs = tmpAccs - nextValidators := validators - header := abci.Header{ + header := tmproto.Header{ ChainID: config.ChainID, Height: 1, Time: genesisTimestamp, @@ -96,8 +102,9 @@ func SimulateFromSeed( opCount := 0 // Setup code to catch SIGTERM's - c := make(chan os.Signal) + c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) + go func() { receivedSignal := <-c fmt.Fprintf(w, "\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount) @@ -105,20 +112,23 @@ func SimulateFromSeed( stopEarly = true }() - var pastTimes []time.Time - var pastVoteInfos [][]abci.VoteInfo + var ( + pastTimes []time.Time + pastVoteInfos [][]abci.VoteInfo + ) request := RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, eventStats.Tally, header) // These are operations which have been queued by previous operations operationQueue := NewOperationQueue() - timeOperationQueue := []FutureOperation{} + + var timeOperationQueue []simulation.FutureOperation logWriter := NewLogWriter(testingMode) blockSimulator := createBlockSimulator( - testingMode, tb, t, w, params, eventStats.Tally, + testingMode, tb, w, params, eventStats.Tally, ops, operationQueue, timeOperationQueue, logWriter, config) if !testingMode { @@ -175,6 +185,7 @@ func SimulateFromSeed( header.Time = header.Time.Add( time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second) header.ProposerAddress = validators.randomProposer(r) + logWriter.AddEntry(EndBlockEntry(int64(height))) if config.Commit { @@ -189,14 +200,12 @@ func SimulateFromSeed( // Generate a random RequestBeginBlock with the current validator set // for the next block - request = RandomRequestBeginBlock(r, params, validators, - pastTimes, pastVoteInfos, eventStats.Tally, header) + request = RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, eventStats.Tally, header) // Update the validator set, which will be reflected in the application // on the next block validators = nextValidators - nextValidators = updateValidators(tb, r, params, - validators, res.ValidatorUpdates, eventStats.Tally) + nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, eventStats.Tally) // update the exported params if config.ExportParamsPath != "" && config.ExportParamsHeight == height { @@ -234,23 +243,22 @@ func SimulateFromSeed( //______________________________________________________________________________ type blockSimFn func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accounts []Account, header abci.Header) (opCount int) + accounts []simulation.Account, header tmproto.Header) (opCount int) // Returns a function to simulate blocks. Written like this to avoid constant // parameters being passed everytime, to minimize memory overhead. -func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Writer, params Params, +func createBlockSimulator(testingMode bool, tb testing.TB, w io.Writer, params Params, event func(route, op, evResult string), ops WeightedOperations, - operationQueue OperationQueue, timeOperationQueue []FutureOperation, - logWriter LogWriter, config Config) blockSimFn { + operationQueue OperationQueue, timeOperationQueue []simulation.FutureOperation, + logWriter LogWriter, config simulation.Config) blockSimFn { lastBlockSizeState := 0 // state for [4 * uniform distribution] blocksize := 0 selectOp := ops.getSelectOpFn() return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []simulation.Account, header tmproto.Header, ) (opCount int) { - _, _ = fmt.Fprintf( w, "\rSimulating... block %d/%d, operation %d/%d.", header.Height, config.NumBlocks, opCount, blocksize, @@ -258,7 +266,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Wr lastBlockSizeState, blocksize = getBlockSize(r, params, lastBlockSizeState, config.BlockSize) type opAndR struct { - op Operation + op simulation.Operation rand *rand.Rand } @@ -269,7 +277,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Wr for i := 0; i < blocksize; i++ { opAndRz = append(opAndRz, opAndR{ op: selectOp(r), - rand: DeriveRand(r), + rand: simulation.DeriveRand(r), }) } @@ -301,14 +309,15 @@ Comment: %s`, opCount++ } + return opCount } } // nolint: errcheck -func runQueuedOperations(queueOps map[int][]Operation, +func runQueuedOperations(queueOps map[int][]simulation.Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, - ctx sdk.Context, accounts []Account, logWriter LogWriter, + ctx sdk.Context, accounts []simulation.Account, logWriter LogWriter, event func(route, op, evResult string), lean bool, chainID string) (numOpsRan int) { queuedOp, ok := queueOps[height] @@ -324,21 +333,24 @@ func runQueuedOperations(queueOps map[int][]Operation, // be changed. opMsg, _, err := queuedOp[i](r, app, ctx, accounts, chainID) opMsg.LogEvent(event) + if !lean || opMsg.OK { logWriter.AddEntry((QueuedMsgEntry(int64(height), opMsg))) } + if err != nil { logWriter.PrintLogs() tb.FailNow() } } delete(queueOps, height) + return numOpsRan } -func runQueuedTimeOperations(queueOps []FutureOperation, +func runQueuedTimeOperations(queueOps []simulation.FutureOperation, height int, currentTime time.Time, tb testing.TB, r *rand.Rand, - app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, + app *baseapp.BaseApp, ctx sdk.Context, accounts []simulation.Account, logWriter LogWriter, event func(route, op, evResult string), lean bool, chainID string) (numOpsRan int) { @@ -350,9 +362,11 @@ func runQueuedTimeOperations(queueOps []FutureOperation, // be changed. opMsg, _, err := queueOps[0].Op(r, app, ctx, accounts, chainID) opMsg.LogEvent(event) + if !lean || opMsg.OK { logWriter.AddEntry(QueuedMsgEntry(int64(height), opMsg)) } + if err != nil { logWriter.PrintLogs() tb.FailNow() @@ -361,5 +375,6 @@ func runQueuedTimeOperations(queueOps []FutureOperation, queueOps = queueOps[1:] numOpsRan++ } + return numOpsRan } diff --git a/x/simulation/transition_matrix.go b/x/simulation/transition_matrix.go index 0a0ee7ebea04..9e2c69c06633 100644 --- a/x/simulation/transition_matrix.go +++ b/x/simulation/transition_matrix.go @@ -3,6 +3,8 @@ package simulation import ( "fmt" "math/rand" + + "github.com/cosmos/cosmos-sdk/types/simulation" ) // TransitionMatrix is _almost_ a left stochastic matrix. It is technically @@ -19,7 +21,7 @@ type TransitionMatrix struct { // CreateTransitionMatrix creates a transition matrix from the provided weights. // TODO: Provide example usage -func CreateTransitionMatrix(weights [][]int) (TransitionMatrix, error) { +func CreateTransitionMatrix(weights [][]int) (simulation.TransitionMatrix, error) { n := len(weights) for i := 0; i < n; i++ { if len(weights[i]) != n { @@ -27,12 +29,15 @@ func CreateTransitionMatrix(weights [][]int) (TransitionMatrix, error) { fmt.Errorf("transition matrix: non-square matrix provided, error on row %d", i) } } + totals := make([]int, n) + for row := 0; row < n; row++ { for col := 0; col < n; col++ { totals[col] += weights[row][col] } } + return TransitionMatrix{weights, totals, n}, nil } @@ -44,6 +49,7 @@ func (t TransitionMatrix) NextState(r *rand.Rand, i int) int { if randNum < t.weights[row][i] { return row } + randNum -= t.weights[row][i] } // This line should never get executed @@ -55,14 +61,18 @@ func (t TransitionMatrix) NextState(r *rand.Rand, i int) int { func GetMemberOfInitialState(r *rand.Rand, weights []int) int { n := len(weights) total := 0 + for i := 0; i < n; i++ { total += weights[i] } + randNum := r.Intn(total) + for state := 0; state < n; state++ { if randNum < weights[state] { return state } + randNum -= weights[state] } // This line should never get executed diff --git a/x/simulation/util.go b/x/simulation/util.go index 58795cd879e9..c600bc2a369a 100644 --- a/x/simulation/util.go +++ b/x/simulation/util.go @@ -9,12 +9,14 @@ import ( func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) { testingMode = false + if _t, ok := tb.(*testing.T); ok { t = _t testingMode = true } else { b = tb.(*testing.B) } + return testingMode, t, b } @@ -27,7 +29,7 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B func getBlockSize(r *rand.Rand, params Params, lastBlockSizeState, avgBlockSize int) (state, blockSize int) { // TODO: Make default blocksize transition matrix actually make the average // blocksize equal to avgBlockSize. - state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState) + state = params.BlockSizeTransitionMatrix().NextState(r, lastBlockSizeState) switch state { case 0: diff --git a/x/slashing/abci.go b/x/slashing/abci.go index c6caeec9b5fd..26a3c687564d 100644 --- a/x/slashing/abci.go +++ b/x/slashing/abci.go @@ -1,14 +1,21 @@ package slashing import ( + "time" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // BeginBlocker check for infraction evidence or downtime of validators // on every begin block -func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) { +func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + // Iterate over all the validators which *should* have signed this block // store whether or not they have actually signed it and slash/unbond any // which have missed too many blocks in a row (downtime slashing) diff --git a/x/slashing/abci_test.go b/x/slashing/abci_test.go index e0046a8ad3c7..50140eb58ebb 100644 --- a/x/slashing/abci_test.go +++ b/x/slashing/abci_test.go @@ -1,39 +1,43 @@ -package slashing +package slashing_test import ( "testing" "time" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestBeginBlocker(t *testing.T) { - ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, pk := slashingkeeper.Addrs[2], slashingkeeper.Pks[2] + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - // bond the validator - res, err := staking.NewHandler(sk)(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, pk, amt)) - require.NoError(t, err) - require.NotNil(t, res) + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + addr, pk := sdk.ValAddress(pks[0].Address()), pks[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - staking.EndBlocker(ctx, sk) + // bond the validator + power := int64(100) + amt := tstaking.CreateValidatorWithValPower(addr, pk, power, true) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) val := abci.Validator{ Address: pk.Address(), - Power: sdk.TokensToConsensusPower(amt), + Power: power, } // mark the validator as having signed @@ -45,9 +49,10 @@ func TestBeginBlocker(t *testing.T) { }}, }, } - BeginBlocker(ctx, req, keeper) - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address())) + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) + + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address())) require.True(t, found) require.Equal(t, ctx.BlockHeight(), info.StartHeight) require.Equal(t, int64(1), info.IndexOffset) @@ -57,7 +62,7 @@ func TestBeginBlocker(t *testing.T) { height := int64(0) // for 1000 blocks, mark the validator as having signed - for ; height < keeper.SignedBlocksWindow(ctx); height++ { + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ @@ -67,11 +72,12 @@ func TestBeginBlocker(t *testing.T) { }}, }, } - BeginBlocker(ctx, req, keeper) + + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) } // for 500 blocks, mark the validator as having not signed - for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ { + for ; height < ((app.SlashingKeeper.SignedBlocksWindow(ctx) * 2) - app.SlashingKeeper.MinSignedPerWindow(ctx) + 1); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ @@ -81,14 +87,15 @@ func TestBeginBlocker(t *testing.T) { }}, }, } - BeginBlocker(ctx, req, keeper) + + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) } // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should be jailed - validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.True(t, found) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) + require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) } diff --git a/x/slashing/alias.go b/x/slashing/alias.go deleted file mode 100644 index 38d556a6e492..000000000000 --- a/x/slashing/alias.go +++ /dev/null @@ -1,89 +0,0 @@ -package slashing - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - DefaultParamspace = types.DefaultParamspace - DefaultSignedBlocksWindow = types.DefaultSignedBlocksWindow - DefaultDowntimeJailDuration = types.DefaultDowntimeJailDuration - QueryParameters = types.QueryParameters - QuerySigningInfo = types.QuerySigningInfo - QuerySigningInfos = types.QuerySigningInfos - - EventTypeSlash = types.EventTypeSlash - EventTypeLiveness = types.EventTypeLiveness - AttributeKeyAddress = types.AttributeKeyAddress - AttributeKeyHeight = types.AttributeKeyHeight - AttributeKeyPower = types.AttributeKeyPower - AttributeKeyReason = types.AttributeKeyReason - AttributeKeyJailed = types.AttributeKeyJailed - AttributeKeyMissedBlocks = types.AttributeKeyMissedBlocks - AttributeValueDoubleSign = types.AttributeValueDoubleSign - AttributeValueMissingSignature = types.AttributeValueMissingSignature - AttributeValueCategory = types.AttributeValueCategory -) - -var ( - // functions aliases - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - RegisterCodec = types.RegisterCodec - ErrNoValidatorForAddress = types.ErrNoValidatorForAddress - ErrBadValidatorAddr = types.ErrBadValidatorAddr - ErrValidatorJailed = types.ErrValidatorJailed - ErrValidatorNotJailed = types.ErrValidatorNotJailed - ErrMissingSelfDelegation = types.ErrMissingSelfDelegation - ErrSelfDelegationTooLowToUnjail = types.ErrSelfDelegationTooLowToUnjail - ErrNoSigningInfoFound = types.ErrNoSigningInfoFound - NewGenesisState = types.NewGenesisState - NewMissedBlock = types.NewMissedBlock - DefaultGenesisState = types.DefaultGenesisState - ValidateGenesis = types.ValidateGenesis - GetValidatorSigningInfoKey = types.GetValidatorSigningInfoKey - GetValidatorSigningInfoAddress = types.GetValidatorSigningInfoAddress - GetValidatorMissedBlockBitArrayPrefixKey = types.GetValidatorMissedBlockBitArrayPrefixKey - GetValidatorMissedBlockBitArrayKey = types.GetValidatorMissedBlockBitArrayKey - GetAddrPubkeyRelationKey = types.GetAddrPubkeyRelationKey - NewMsgUnjail = types.NewMsgUnjail - ParamKeyTable = types.ParamKeyTable - NewParams = types.NewParams - DefaultParams = types.DefaultParams - NewQuerySigningInfoParams = types.NewQuerySigningInfoParams - NewQuerySigningInfosParams = types.NewQuerySigningInfosParams - NewValidatorSigningInfo = types.NewValidatorSigningInfo - - // variable aliases - ModuleCdc = types.ModuleCdc - ValidatorSigningInfoKey = types.ValidatorSigningInfoKey - ValidatorMissedBlockBitArrayKey = types.ValidatorMissedBlockBitArrayKey - AddrPubkeyRelationKey = types.AddrPubkeyRelationKey - DefaultMinSignedPerWindow = types.DefaultMinSignedPerWindow - DefaultSlashFractionDoubleSign = types.DefaultSlashFractionDoubleSign - DefaultSlashFractionDowntime = types.DefaultSlashFractionDowntime - KeySignedBlocksWindow = types.KeySignedBlocksWindow - KeyMinSignedPerWindow = types.KeyMinSignedPerWindow - KeyDowntimeJailDuration = types.KeyDowntimeJailDuration - KeySlashFractionDoubleSign = types.KeySlashFractionDoubleSign - KeySlashFractionDowntime = types.KeySlashFractionDowntime -) - -type ( - Hooks = keeper.Hooks - Keeper = keeper.Keeper - GenesisState = types.GenesisState - MissedBlock = types.MissedBlock - MsgUnjail = types.MsgUnjail - Params = types.Params - QuerySigningInfoParams = types.QuerySigningInfoParams - QuerySigningInfosParams = types.QuerySigningInfosParams - ValidatorSigningInfo = types.ValidatorSigningInfo -) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 23dd43e590bf..5ee23b7cc564 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -1,6 +1,4 @@ -// nolint -// DONTCOVER -package slashing +package slashing_test import ( "errors" @@ -8,153 +6,89 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/mock" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/x/supply" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( priv1 = secp256k1.GenPrivKey() addr1 = sdk.AccAddress(priv1.PubKey().Address()) - coins = sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} -) - -// initialize the mock application for this module -func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) { - mapp := mock.NewApp() - - RegisterCodec(mapp.Cdc) - staking.RegisterCodec(mapp.Cdc) - supply.RegisterCodec(mapp.Cdc) - - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keySlashing := sdk.NewKVStoreKey(StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - - feeCollector := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollector.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(mapp.Cdc, keySupply, mapp.AccountKeeper, bankKeeper, maccPerms) - stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, supplyKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace)) - keeper := NewKeeper(mapp.Cdc, keySlashing, stakingKeeper, mapp.ParamsKeeper.Subspace(DefaultParamspace)) - mapp.Router().AddRoute(staking.RouterKey, staking.NewHandler(stakingKeeper)) - mapp.Router().AddRoute(RouterKey, NewHandler(keeper)) - - mapp.SetEndBlocker(getEndBlocker(stakingKeeper)) - mapp.SetInitChainer(getInitChainer(mapp, stakingKeeper, mapp.AccountKeeper, supplyKeeper, - []supplyexported.ModuleAccountI{feeCollector, notBondedPool, bondPool})) - - require.NoError(t, mapp.CompleteSetup(keyStaking, keySupply, keySlashing)) - - return mapp, stakingKeeper, keeper -} - -// staking endblocker -func getEndBlocker(keeper staking.Keeper) sdk.EndBlocker { - return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - validatorUpdates := staking.EndBlocker(ctx, keeper) - return abci.ResponseEndBlock{ - ValidatorUpdates: validatorUpdates, - } - } -} - -// overwrite the mock init chainer -func getInitChainer(mapp *mock.App, keeper staking.Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper, - blacklistedAddrs []supplyexported.ModuleAccountI) sdk.InitChainer { - return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - // set module accounts - for _, macc := range blacklistedAddrs { - supplyKeeper.SetModuleAccount(ctx, macc) - } - - mapp.InitChainer(ctx, req) - stakingGenesis := staking.DefaultGenesisState() - validators := staking.InitGenesis(ctx, keeper, accountKeeper, supplyKeeper, stakingGenesis) - return abci.ResponseInitChain{ - Validators: validators, - } - } -} + valKey = ed25519.GenPrivKey() + valAddr = sdk.AccAddress(valKey.PubKey().Address()) +) -func checkValidator(t *testing.T, mapp *mock.App, keeper staking.Keeper, - addr sdk.AccAddress, expFound bool) staking.Validator { - ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - validator, found := keeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) +func checkValidator(t *testing.T, app *simapp.SimApp, _ sdk.AccAddress, expFound bool) stakingtypes.Validator { + ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) + validator, found := app.StakingKeeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) require.Equal(t, expFound, found) return validator } -func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper, - addr sdk.ConsAddress, expFound bool) ValidatorSigningInfo { - ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - signingInfo, found := keeper.GetValidatorSigningInfo(ctxCheck, addr) +func checkValidatorSigningInfo(t *testing.T, app *simapp.SimApp, addr sdk.ConsAddress, expFound bool) types.ValidatorSigningInfo { + ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) + signingInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctxCheck, addr) require.Equal(t, expFound, found) return signingInfo } func TestSlashingMsgs(t *testing.T) { - mapp, stakingKeeper, keeper := getMockApp(t) - genTokens := sdk.TokensFromConsensusPower(42) bondTokens := sdk.TokensFromConsensusPower(10) genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) - acc1 := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{genCoin}, + acc1 := &authtypes.BaseAccount{ + Address: addr1.String(), } - accs := []authexported.Account{acc1} - mock.SetGenesis(mapp, accs) + accs := authtypes.GenesisAccounts{acc1} + balances := []banktypes.Balance{ + { + Address: addr1.String(), + Coins: sdk.Coins{genCoin}, + }, + } + + app := simapp.SetupWithGenesisAccounts(accs, balances...) + simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin}) - description := staking.NewDescription("foo_moniker", "", "", "", "") - commission := staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + description := stakingtypes.NewDescription("foo_moniker", "", "", "", "") + commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - createValidatorMsg := staking.NewMsgCreateValidator( - sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission, sdk.OneInt(), + createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr1), valKey.PubKey(), bondCoin, description, commission, sdk.OneInt(), ) + require.NoError(t, err) - header := abci.Header{Height: mapp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) + require.NoError(t, err) + simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) - header = abci.Header{Height: mapp.LastBlockHeight() + 1} - mapp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - validator := checkValidator(t, mapp, stakingKeeper, addr1, true) - require.Equal(t, sdk.ValAddress(addr1), validator.OperatorAddress) - require.Equal(t, sdk.Bonded, validator.Status) + validator := checkValidator(t, app, addr1, true) + require.Equal(t, sdk.ValAddress(addr1).String(), validator.OperatorAddress) + require.Equal(t, stakingtypes.Bonded, validator.Status) require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) - unjailMsg := MsgUnjail{ValidatorAddr: sdk.ValAddress(validator.ConsPubKey.Address())} + unjailMsg := &types.MsgUnjail{ValidatorAddr: sdk.ValAddress(addr1).String()} - // no signing info yet - checkValidatorSigningInfo(t, mapp, keeper, sdk.ConsAddress(addr1), false) + checkValidatorSigningInfo(t, app, sdk.ConsAddress(valAddr), true) // unjail should fail with unknown validator - header = abci.Header{Height: mapp.LastBlockHeight() + 1} - _, res, err := mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{unjailMsg}, []uint64{0}, []uint64{1}, false, false, priv1) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + _, res, err := simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) require.Error(t, err) require.Nil(t, res) - require.True(t, errors.Is(ErrValidatorNotJailed, err)) + require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) } diff --git a/x/slashing/client/cli/cli_test.go b/x/slashing/client/cli/cli_test.go new file mode 100644 index 000000000000..41fac414c7af --- /dev/null +++ b/x/slashing/client/cli/cli_test.go @@ -0,0 +1,191 @@ +// +build norace + +package cli_test + +import ( + "fmt" + "strings" + "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" + 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/slashing/client/cli" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +// SetupSuite executes bootstrapping logic before all the tests, i.e. once before +// the entire suite, start executing. +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) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) +} + +// TearDownSuite performs cleanup logic after all the tests, i.e. once after the +// entire suite, has finished executing. +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGetCmdQuerySigningInfo() { + val := s.network.Validators[0] + + valConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, val.PubKey) + s.Require().NoError(err) + + testCases := []struct { + name string + args []string + expectErr bool + expectedOutput string + }{ + {"invalid address", []string{"foo"}, true, ``}, + { + "valid address (json output)", + []string{ + valConsPubKey, + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + fmt.Sprintf("{\"address\":\"%s\",\"start_height\":\"0\",\"index_offset\":\"0\",\"jailed_until\":\"1970-01-01T00:00:00Z\",\"tombstoned\":false,\"missed_blocks_counter\":\"0\"}", sdk.ConsAddress(val.PubKey.Address())), + }, + { + "valid address (text output)", + []string{ + valConsPubKey, + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + fmt.Sprintf(`address: %s +index_offset: "0" +jailed_until: "1970-01-01T00:00:00Z" +missed_blocks_counter: "0" +start_height: "0" +tombstoned: false`, sdk.ConsAddress(val.PubKey.Address())), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQuerySigningInfo() + 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().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryParams() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "json output", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"signed_blocks_window":"100","min_signed_per_window":"0.500000000000000000","downtime_jail_duration":"600s","slash_fraction_double_sign":"0.050000000000000000","slash_fraction_downtime":"0.010000000000000000"}`, + }, + { + "text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `downtime_jail_duration: 600s +min_signed_per_window: "0.500000000000000000" +signed_blocks_window: "100" +slash_fraction_double_sign: "0.050000000000000000" +slash_fraction_downtime: "0.010000000000000000"`, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestNewUnjailTxCmd() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "valid transaction", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), // sync mode as there are no funds yet + fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + }, + false, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewUnjailTxCmd() + 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/slashing/client/cli/flags.go b/x/slashing/client/cli/flags.go index 09db8e8258eb..4f384512189f 100644 --- a/x/slashing/client/cli/flags.go +++ b/x/slashing/client/cli/flags.go @@ -1,6 +1,5 @@ package cli -// nolint const ( FlagAddressValidator = "validator" ) diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index c574eee5b18f..b6b71a344cd3 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -1,22 +1,20 @@ package cli import ( - "fmt" + "context" "strings" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { // Group slashing queries under a subcommand slashingQueryCmd := &cobra.Command{ Use: types.ModuleName, @@ -27,10 +25,9 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { } slashingQueryCmd.AddCommand( - flags.GetCommands( - GetCmdQuerySigningInfo(queryRoute, cdc), - GetCmdQueryParams(cdc), - )..., + GetCmdQuerySigningInfo(), + GetCmdQueryParams(), + GetCmdQuerySigningInfos(), ) return slashingQueryCmd @@ -38,17 +35,21 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { } // GetCmdQuerySigningInfo implements the command to query signing info. -func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQuerySigningInfo() *cobra.Command { + cmd := &cobra.Command{ Use: "signing-info [validator-conspub]", Short: "Query a validator's signing information", Long: strings.TrimSpace(`Use a validators' consensus public key to find the signing-info for that validator: -$ query slashing signing-info fetchvalconspub1zcjduepqfhvwcmt7p06fvdgexxhmz0l8c7sgswl7ulv7aulk364x4g5xsw7sdyc2su +$ query slashing signing-info cosmosvalconspub1zcjduepqfhvwcmt7p06fvdgexxhmz0l8c7sgswl7ulv7aulk364x4g5xsw7sr0k2g5 `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, args[0]) if err != nil { @@ -56,46 +57,87 @@ $ query slashing signing-info fetchvalconspub1zcjduepqfhvwcmt7p06fvdgex } consAddr := sdk.ConsAddress(pk.Address()) - key := types.GetValidatorSigningInfoKey(consAddr) + params := &types.QuerySigningInfoRequest{ConsAddress: consAddr.String()} + res, err := queryClient.SigningInfo(context.Background(), params) + if err != nil { + return err + } - res, _, err := cliCtx.QueryStore(key, storeName) + return clientCtx.PrintProto(&res.ValSigningInfo) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdQuerySigningInfos implements the command to query signing infos. +func GetCmdQuerySigningInfos() *cobra.Command { + cmd := &cobra.Command{ + Use: "signing-infos", + Short: "Query signing information of all validators", + Long: strings.TrimSpace(`signing infos of validators: + +$ query slashing signing-infos +`), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - if len(res) == 0 { - return fmt.Errorf("validator %s not found in slashing store", consAddr) + pageReq, err := client.ReadPageRequest(cmd.Flags()) + if err != nil { + return err + } + + params := &types.QuerySigningInfosRequest{Pagination: pageReq} + res, err := queryClient.SigningInfos(context.Background(), params) + if err != nil { + return err } - var signingInfo types.ValidatorSigningInfo - cdc.MustUnmarshalBinaryLengthPrefixed(res, &signingInfo) - return cliCtx.PrintOutput(signingInfo) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "signing infos") + + return cmd } // GetCmdQueryParams implements a command to fetch slashing parameters. -func GetCmdQueryParams(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ Use: "params", Short: "Query the current slashing parameters", Args: cobra.NoArgs, Long: strings.TrimSpace(`Query genesis parameters for the slashing module: -$ query slashing params +$ query slashing params `), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) - route := fmt.Sprintf("custom/%s/parameters", types.QuerierRoute) - res, _, err := cliCtx.QueryWithData(route, nil) + params := &types.QueryParamsRequest{} + res, err := queryClient.Params(context.Background(), params) if err != nil { return err } - var params types.Params - cdc.MustUnmarshalJSON(res, ¶ms) - return cliCtx.PrintOutput(params) + return clientCtx.PrintProto(&res.Params) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 5aa262568d54..d896c8570cf5 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -1,56 +1,55 @@ package cli import ( - "bufio" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(cdc *codec.Codec) *cobra.Command { +// NewTxCmd returns a root CLI command handler for all x/slashing transaction commands. +func NewTxCmd() *cobra.Command { slashingTxCmd := &cobra.Command{ Use: types.ModuleName, - Short: "Slashing transactions subcommands", + Short: "Slashing transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } - slashingTxCmd.AddCommand(flags.PostCommands( - GetCmdUnjail(cdc), - )...) - + slashingTxCmd.AddCommand(NewUnjailTxCmd()) return slashingTxCmd } -// GetCmdUnjail implements the create unjail validator command. -func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func NewUnjailTxCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "unjail", Args: cobra.NoArgs, Short: "unjail validator previously jailed for downtime", Long: `unjail a jailed validator: -$ tx slashing unjail --from mykey +$ tx slashing unjail --from mykey `, RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - valAddr := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + valAddr := clientCtx.GetFromAddress() msg := types.NewMsgUnjail(sdk.ValAddress(valAddr)) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } diff --git a/x/slashing/client/rest/grpc_query_test.go b/x/slashing/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..8d4efd364c85 --- /dev/null +++ b/x/slashing/client/rest/grpc_query_test.go @@ -0,0 +1,134 @@ +package rest_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + _, 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) TestGRPCQueries() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + consAddr := sdk.ConsAddress(val.PubKey.Address()).String() + + testCases := []struct { + name string + url string + headers map[string]string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "get signing infos (height specific)", + fmt.Sprintf("%s/cosmos/slashing/v1beta1/signing_infos", baseURL), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QuerySigningInfosResponse{}, + &types.QuerySigningInfosResponse{ + Info: []types.ValidatorSigningInfo{ + { + Address: sdk.ConsAddress(val.PubKey.Address()).String(), + JailedUntil: time.Unix(0, 0), + }, + }, + Pagination: &query.PageResponse{ + Total: uint64(1), + }, + }, + }, + { + "get signing info (height specific)", + fmt.Sprintf("%s/cosmos/slashing/v1beta1/signing_infos/%s", baseURL, consAddr), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QuerySigningInfoResponse{}, + &types.QuerySigningInfoResponse{ + ValSigningInfo: types.ValidatorSigningInfo{ + Address: sdk.ConsAddress(val.PubKey.Address()).String(), + JailedUntil: time.Unix(0, 0), + }, + }, + }, + { + "get signing info wrong address", + fmt.Sprintf("%s/cosmos/slashing/v1beta1/signing_infos/%s", baseURL, "wrongAddress"), + map[string]string{}, + true, + &types.QuerySigningInfoResponse{}, + nil, + }, + { + "params", + fmt.Sprintf("%s/cosmos/slashing/v1beta1/params", baseURL), + map[string]string{}, + false, + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + Params: types.DefaultParams(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index 751fe648dc45..36eb8cc24a68 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -6,113 +6,106 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { r.HandleFunc( "/slashing/validators/{validatorPubKey}/signing_info", - signingInfoHandlerFn(cliCtx), + signingInfoHandlerFn(clientCtx), ).Methods("GET") r.HandleFunc( "/slashing/signing_infos", - signingInfoHandlerListFn(cliCtx), + signingInfoHandlerListFn(clientCtx), ).Methods("GET") r.HandleFunc( "/slashing/parameters", - queryParamsHandlerFn(cliCtx), + queryParamsHandlerFn(clientCtx), ).Methods("GET") } // http request handler to query signing info -func signingInfoHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func signingInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, vars["validatorPubKey"]) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQuerySigningInfoParams(sdk.ConsAddress(pk.Address())) + params := types.QuerySigningInfoRequest{ConsAddress: pk.Address().String()} - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySigningInfo) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // http request handler to query signing info -func signingInfoHandlerListFn(cliCtx context.CLIContext) http.HandlerFunc { +func signingInfoHandlerListFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQuerySigningInfosParams(page, limit) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckInternalServerError(w, err) { return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySigningInfos) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func queryParamsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } route := fmt.Sprintf("custom/%s/parameters", types.QuerierRoute) - res, height, err := cliCtx.QueryWithData(route, nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(route, nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/slashing/client/rest/rest.go b/x/slashing/client/rest/rest.go index 4237d59e258a..a3a88545900b 100644 --- a/x/slashing/client/rest/rest.go +++ b/x/slashing/client/rest/rest.go @@ -3,11 +3,14 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/rest" + + "github.com/cosmos/cosmos-sdk/client" ) -// RegisterRoutes registers staking-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - registerQueryRoutes(cliCtx, r) - registerTxRoutes(cliCtx, r) +func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + + registerQueryRoutes(clientCtx, r) + registerTxHandlers(clientCtx, r) } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index bd7188a1c006..eaac614fa1c4 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -6,18 +6,15 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "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/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc( - "/slashing/validators/{validatorAddr}/unjail", - unjailRequestHandlerFn(cliCtx), - ).Methods("POST") +func registerTxHandlers(clientCtx client.Context, r *mux.Router) { + r.HandleFunc("/slashing/validators/{validatorAddr}/unjail", NewUnjailRequestHandlerFn(clientCtx)).Methods("POST") } // Unjail TX body @@ -25,14 +22,15 @@ type UnjailReq struct { BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` } -func unjailRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +// NewUnjailRequestHandlerFn returns an HTTP REST handler for creating a MsgUnjail +// transaction. +func NewUnjailRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - - bech32validator := vars["validatorAddr"] + bech32Validator := vars["validatorAddr"] var req UnjailReq - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -41,15 +39,13 @@ func unjailRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - valAddr, err := sdk.ValAddressFromBech32(bech32validator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if rest.CheckBadRequestError(w, err) { return } - fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + valAddr, err := sdk.ValAddressFromBech32(bech32Validator) + if rest.CheckInternalServerError(w, err) { return } @@ -59,12 +55,9 @@ func unjailRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } msg := types.NewMsgUnjail(valAddr) - err = msg.ValidateBasic() - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 8b959c8a0bac..2c8b6675762b 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -2,34 +2,39 @@ package slashing import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // InitGenesis initialize default parameters // and the keeper's address to pubkey map -func InitGenesis(ctx sdk.Context, keeper Keeper, stakingKeeper types.StakingKeeper, data types.GenesisState) { +func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, stakingKeeper types.StakingKeeper, data *types.GenesisState) { stakingKeeper.IterateValidators(ctx, - func(index int64, validator exported.ValidatorI) bool { - keeper.AddPubkey(ctx, validator.GetConsPubKey()) + func(index int64, validator stakingtypes.ValidatorI) bool { + consPk, err := validator.ConsPubKey() + if err != nil { + panic(err) + } + keeper.AddPubkey(ctx, consPk) return false }, ) - for addr, info := range data.SigningInfos { - address, err := sdk.ConsAddressFromBech32(addr) + for _, info := range data.SigningInfos { + address, err := sdk.ConsAddressFromBech32(info.Address) if err != nil { panic(err) } - keeper.SetValidatorSigningInfo(ctx, address, info) + keeper.SetValidatorSigningInfo(ctx, address, info.ValidatorSigningInfo) } - for addr, array := range data.MissedBlocks { - address, err := sdk.ConsAddressFromBech32(addr) + for _, array := range data.MissedBlocks { + address, err := sdk.ConsAddressFromBech32(array.Address) if err != nil { panic(err) } - for _, missed := range array { + for _, missed := range array.MissedBlocks { keeper.SetValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed) } } @@ -40,20 +45,23 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, stakingKeeper types.StakingKeep // ExportGenesis writes the current store values // to a genesis file, which can be imported again // with InitGenesis -func ExportGenesis(ctx sdk.Context, keeper Keeper) (data types.GenesisState) { +func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) (data *types.GenesisState) { params := keeper.GetParams(ctx) - signingInfos := make(map[string]types.ValidatorSigningInfo) - missedBlocks := make(map[string][]types.MissedBlock) + signingInfos := make([]types.SigningInfo, 0) + missedBlocks := make([]types.ValidatorMissedBlocks, 0) keeper.IterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) { bechAddr := address.String() - signingInfos[bechAddr] = info - localMissedBlocks := []types.MissedBlock{} + signingInfos = append(signingInfos, types.SigningInfo{ + Address: bechAddr, + ValidatorSigningInfo: info, + }) - keeper.IterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { - localMissedBlocks = append(localMissedBlocks, types.NewMissedBlock(index, missed)) - return false + localMissedBlocks := keeper.GetValidatorMissedBlocks(ctx, address) + + missedBlocks = append(missedBlocks, types.ValidatorMissedBlocks{ + Address: bechAddr, + MissedBlocks: localMissedBlocks, }) - missedBlocks[bechAddr] = localMissedBlocks return false }) diff --git a/x/slashing/genesis_test.go b/x/slashing/genesis_test.go new file mode 100644 index 000000000000..b9241f0246ca --- /dev/null +++ b/x/slashing/genesis_test.go @@ -0,0 +1,59 @@ +package slashing_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + 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/slashing" + "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +func TestExportAndInitGenesis(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.TokensFromConsensusPower(200)) + + info1 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[0]), int64(4), int64(3), + time.Now().UTC().Add(100000000000), false, int64(10)) + info2 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[1]), int64(5), int64(4), + time.Now().UTC().Add(10000000000), false, int64(10)) + + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2) + genesisState := slashing.ExportGenesis(ctx, app.SlashingKeeper) + + require.Equal(t, genesisState.Params, testslashing.TestParams()) + require.Len(t, genesisState.SigningInfos, 2) + require.Equal(t, genesisState.SigningInfos[0].ValidatorSigningInfo, info1) + + // Tombstone validators after genesis shouldn't effect genesis state + app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) + app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[1])) + + ok := app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, ok) + + newInfo1, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.NotEqual(t, info1, newInfo1) + // Initialise genesis with genesis state before tombstone + slashing.InitGenesis(ctx, app.SlashingKeeper, app.StakingKeeper, genesisState) + + // Validator isTombstoned should return false as GenesisState is initialised + ok = app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0])) + require.False(t, ok) + + newInfo1, ok = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + newInfo2, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1])) + require.True(t, ok) + require.Equal(t, info1, newInfo1) + require.Equal(t, info2, newInfo2) +} diff --git a/x/slashing/handler.go b/x/slashing/handler.go index fe4ef7cb7c2e..eab71edb4188 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -3,39 +3,24 @@ package slashing import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // NewHandler creates an sdk.Handler for all the slashing type messages -func NewHandler(k Keeper) sdk.Handler { +func NewHandler(k keeper.Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) + msgServer := keeper.NewMsgServerImpl(k) + switch msg := msg.(type) { - case MsgUnjail: - return handleMsgUnjail(ctx, msg, k) + case *types.MsgUnjail: + res, err := msgServer.Unjail(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } } } - -// Validators must submit a transaction to unjail itself after -// having been jailed (and thus unbonded) for downtime -func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) (*sdk.Result, error) { - err := k.Unjail(ctx, msg.ValidatorAddr) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddr.String()), - ), - ) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index eb9e35b96274..7a83014b9615 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -1,4 +1,4 @@ -package slashing +package slashing_test import ( "errors" @@ -7,144 +7,132 @@ import ( "time" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" + "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams()) - slh := NewHandler(keeper) - amt := sdk.TokensFromConsensusPower(100) - addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] - - msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) - res, err := staking.NewHandler(sk)(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) - staking.EndBlocker(ctx, sk) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + slh := slashing.NewHandler(app.SlashingKeeper) + addr, val := sdk.ValAddress(pks[0].Address()), pks[0] + amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) // assert non-jailed validator can't be unjailed - res, err = slh(ctx, NewMsgUnjail(addr)) + res, err := slh(ctx, types.NewMsgUnjail(addr)) require.Error(t, err) require.Nil(t, res) - require.True(t, errors.Is(ErrValidatorNotJailed, err)) + require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) } func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { // initial setup - ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams()) - slh := NewHandler(keeper) - amtInt := int64(100) - addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt) - msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + slh := slashing.NewHandler(app.SlashingKeeper) + addr, val := sdk.ValAddress(pks[0].Address()), pks[0] + amt := sdk.TokensFromConsensusPower(100) + msg := tstaking.CreateValidatorMsg(addr, val, amt) msg.MinSelfDelegation = amt + tstaking.Handle(msg, true) - res, err := staking.NewHandler(sk)(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, ) - unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt()) - undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt) - res, err = staking.NewHandler(sk)(ctx, undelegateMsg) - require.NoError(t, err) - require.NotNil(t, res) - - require.True(t, sk.Validator(ctx, addr).IsJailed()) + tstaking.Undelegate(sdk.AccAddress(addr), addr, sdk.OneInt(), true) + require.True(t, app.StakingKeeper.Validator(ctx, addr).IsJailed()) // assert non-jailed validator can't be unjailed - res, err = slh(ctx, NewMsgUnjail(addr)) + res, err := slh(ctx, types.NewMsgUnjail(addr)) require.Error(t, err) require.Nil(t, res) - require.True(t, errors.Is(ErrSelfDelegationTooLowToUnjail, err)) + require.True(t, errors.Is(types.ErrSelfDelegationTooLowToUnjail, err)) } func TestJailedValidatorDelegations(t *testing.T) { - ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, DefaultParams()) - - stakingParams := stakingKeeper.GetParams(ctx) - stakingKeeper.SetParams(ctx, stakingParams) + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) + pks := simapp.CreateTestPubKeys(3) - // create a validator - bondAmount := sdk.TokensFromConsensusPower(10) - valPubKey := slashingkeeper.Pks[0] - valAddr, consAddr := slashingkeeper.Addrs[1], sdk.ConsAddress(slashingkeeper.Addrs[0]) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(20)) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) - res, err := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal) - require.NoError(t, err) - require.NotNil(t, res) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + stakingParams := app.StakingKeeper.GetParams(ctx) + app.StakingKeeper.SetParams(ctx, stakingParams) + valAddr, consAddr := sdk.ValAddress(pks[1].Address()), sdk.ConsAddress(pks[0].Address()) - // end block - staking.EndBlocker(ctx, stakingKeeper) + amt := tstaking.CreateValidatorWithValPower(valAddr, pks[1], 10, true) + staking.EndBlocker(ctx, app.StakingKeeper) // set dummy signing info - newInfo := NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) - slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) + newInfo := types.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) // delegate tokens to the validator - delAddr := sdk.AccAddress(slashingkeeper.Addrs[2]) - msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) - require.NoError(t, err) - require.NotNil(t, res) - - unbondAmt := sdk.NewCoin(stakingKeeper.GetParams(ctx).BondDenom, bondAmount) + delAddr := sdk.AccAddress(pks[2].Address()) + tstaking.Delegate(delAddr, valAddr, amt) // unbond validator total self-delegations (which should jail the validator) - msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgUndelegate) - require.NoError(t, err) - require.NotNil(t, res) - - err = stakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) + valAcc := sdk.AccAddress(valAddr) + tstaking.Undelegate(valAcc, valAddr, amt, true) + _, err := app.StakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) // verify validator still exists and is jailed - validator, found := stakingKeeper.GetValidator(ctx, valAddr) + validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) require.True(t, validator.IsJailed()) // verify the validator cannot unjail itself - res, err = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr)) + res, err := slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) require.Error(t, err) require.Nil(t, res) // self-delegate to validator - msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(valAcc, valAddr, amt) // verify the validator can now unjail itself - res, err = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr)) + res, err = slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) require.NoError(t, err) require.NotNil(t, res) } func TestInvalidMsg(t *testing.T) { - k := Keeper{} - h := NewHandler(k) + k := keeper.Keeper{} + h := slashing.NewHandler(k) - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) require.Error(t, err) require.Nil(t, res) require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) @@ -153,29 +141,29 @@ func TestInvalidMsg(t *testing.T) { // Test a validator through uptime, downtime, revocation, // unrevocation, starting height reset, and revocation again func TestHandleAbsentValidator(t *testing.T) { - // initial setup - ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashingkeeper.TestParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] - sh := staking.NewHandler(sk) - slh := NewHandler(keeper) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - res, err := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) + power := int64(100) + addr, val := sdk.ValAddress(pks[0].Address()), pks[0] + slh := slashing.NewHandler(app.SlashingKeeper) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - staking.EndBlocker(ctx, sk) + amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) // will exist since the validator has been bonded - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) require.Equal(t, int64(0), info.IndexOffset) @@ -184,92 +172,92 @@ func TestHandleAbsentValidator(t *testing.T) { height := int64(0) // 1000 first blocks OK - for ; height < keeper.SignedBlocksWindow(ctx); height++ { + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) require.Equal(t, int64(0), info.MissedBlocksCounter) // 500 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx)); height++ { ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) + require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) // validator should be bonded still - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := sk.GetBondedPool(ctx) - require.True(sdk.IntEq(t, amt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx)))) + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) + + bondPool := app.StakingKeeper.GetBondedPool(ctx) + require.True(sdk.IntEq(t, amt, app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) // 501st block missed ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) // counter now reset to zero require.Equal(t, int64(0), info.MissedBlocksCounter) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should have been jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) - slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt() + slashAmt := amt.ToDec().Mul(app.SlashingKeeper.SlashFractionDowntime(ctx)).RoundInt() // validator should have been slashed - require.Equal(t, amt.Sub(slashAmt), validator.GetTokens()) + require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) height++ ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) require.Equal(t, int64(1), info.MissedBlocksCounter) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should not have been slashed any more, since it was already jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, amt.Sub(slashAmt), validator.GetTokens()) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) // unrevocation should fail prior to jail expiration - res, err = slh(ctx, types.NewMsgUnjail(addr)) + res, err := slh(ctx, types.NewMsgUnjail(addr)) require.Error(t, err) require.Nil(t, res) // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))}) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(1, 0).Add(app.SlashingKeeper.DowntimeJailDuration(ctx))}) res, err = slh(ctx, types.NewMsgUnjail(addr)) require.NoError(t, err) require.NotNil(t, res) // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should be rebonded now - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) // validator should have been slashed - bondPool = sk.GetBondedPool(ctx) - require.Equal(t, amt.Sub(slashAmt), bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))) + require.True(t, amt.Sub(slashAmt).Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) // Validator start height should not have been changed - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 @@ -278,30 +266,30 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should not be immediately jailed again height++ ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) // 500 signed blocks - nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 + nextHeight := height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 for ; height < nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) } // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should be jailed again after 500 unsigned blocks - nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 + nextHeight = height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) } // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) } diff --git a/x/slashing/init_test.go b/x/slashing/init_test.go new file mode 100644 index 000000000000..25a38a2d5e53 --- /dev/null +++ b/x/slashing/init_test.go @@ -0,0 +1,9 @@ +package slashing_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + InitTokens = sdk.TokensFromConsensusPower(200) +) diff --git a/x/slashing/internal/keeper/keeper.go b/x/slashing/internal/keeper/keeper.go deleted file mode 100644 index e725519314d1..000000000000 --- a/x/slashing/internal/keeper/keeper.go +++ /dev/null @@ -1,91 +0,0 @@ -package keeper - -import ( - "fmt" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -// Keeper of the slashing store -type Keeper struct { - storeKey sdk.StoreKey - cdc *codec.Codec - sk types.StakingKeeper - paramspace types.ParamSubspace -} - -// NewKeeper creates a slashing keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk types.StakingKeeper, paramspace types.ParamSubspace) Keeper { - return Keeper{ - storeKey: key, - cdc: cdc, - sk: sk, - paramspace: paramspace.WithKeyTable(types.ParamKeyTable()), - } -} - -// 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)) -} - -// AddPubkey sets a address-pubkey relation -func (k Keeper) AddPubkey(ctx sdk.Context, pubkey crypto.PubKey) { - addr := pubkey.Address() - k.setAddrPubkeyRelation(ctx, addr, pubkey) -} - -// GetPubkey returns the pubkey from the adddress-pubkey relation -func (k Keeper) GetPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) { - store := ctx.KVStore(k.storeKey) - var pubkey crypto.PubKey - err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(types.GetAddrPubkeyRelationKey(address)), &pubkey) - if err != nil { - return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(address)) - } - return pubkey, nil -} - -// Slash attempts to slash a validator. The slash is delegated to the staking -// module to make the necessary validator changes. -func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, fraction sdk.Dec, power, distributionHeight int64) { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeSlash, - sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()), - sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), - sdk.NewAttribute(types.AttributeKeyReason, types.AttributeValueDoubleSign), - ), - ) - - k.sk.Slash(ctx, consAddr, distributionHeight, power, fraction) -} - -// Jail attempts to jail a validator. The slash is delegated to the staking module -// to make the necessary validator changes. -func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeSlash, - sdk.NewAttribute(types.AttributeKeyJailed, consAddr.String()), - ), - ) - - k.sk.Jail(ctx, consAddr) -} - -func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(pubkey) - store.Set(types.GetAddrPubkeyRelationKey(addr), bz) -} - -func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.GetAddrPubkeyRelationKey(addr)) -} diff --git a/x/slashing/internal/keeper/keeper_test.go b/x/slashing/internal/keeper/keeper_test.go deleted file mode 100644 index 93dc69f34f4f..000000000000 --- a/x/slashing/internal/keeper/keeper_test.go +++ /dev/null @@ -1,223 +0,0 @@ -package keeper - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// Test a new validator entering the validator set -// Ensure that SigningInfo.StartHeight is set correctly -// and that they are not immediately jailed -func TestHandleNewValidator(t *testing.T) { - // initial setup - ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams()) - addr, val := Addrs[0], Pks[0] - amt := sdk.TokensFromConsensusPower(100) - sh := staking.NewHandler(sk) - - // 1000 first blocks not a validator - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) - - // Validator created - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), - ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) - - // Now a validator, for two blocks - keeper.HandleValidatorSignature(ctx, val.Address(), 100, true) - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) - keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) - - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) - require.Equal(t, int64(2), info.IndexOffset) - require.Equal(t, int64(1), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - - // validator should be bonded still, should not have been jailed or slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := sk.GetBondedPool(ctx) - expTokens := sdk.TokensFromConsensusPower(100) - require.Equal(t, expTokens, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))) -} - -// Test a jailed validator being "down" twice -// Ensure that they're only slashed once -func TestHandleAlreadyJailed(t *testing.T) { - - // initial setup - ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := Addrs[0], Pks[0] - sh := staking.NewHandler(sk) - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - // 1000 first blocks OK - height := int64(0) - for ; height < keeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - - // 501 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - // validator should have been jailed and slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) - - // validator should have been slashed - resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) - require.Equal(t, resultingTokens, validator.GetTokens()) - - // another block missed - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - - // validator should not have been slashed twice - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, resultingTokens, validator.GetTokens()) - -} - -// Test a validator dipping in and out of the validator set -// Ensure that missed blocks are tracked correctly and that -// the start height of the signing info is reset correctly -func TestValidatorDippingInAndOut(t *testing.T) { - - // initial setup - // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - ctx, _, sk, _, keeper := CreateTestInput(t, TestParams()) - params := sk.GetParams(ctx) - params.MaxValidators = 1 - sk.SetParams(ctx, params) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := Addrs[0], Pks[0] - consAddr := sdk.ConsAddress(addr) - sh := staking.NewHandler(sk) - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - // 100 first blocks OK - height := int64(0) - for ; height < int64(100); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - - // kick first validator out of validator set - newAmt := sdk.TokensFromConsensusPower(101) - res, err = sh(ctx, NewTestMsgCreateValidator(Addrs[1], Pks[1], newAmt)) - require.NoError(t, err) - require.NotNil(t, res) - - validatorUpdates := staking.EndBlocker(ctx, sk) - require.Equal(t, 2, len(validatorUpdates)) - validator, _ := sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - - // 600 more blocks happened - height = int64(700) - ctx = ctx.WithBlockHeight(height) - - // validator added back in - delTokens := sdk.TokensFromConsensusPower(50) - res, err = sh(ctx, NewTestMsgDelegate(sdk.AccAddress(Addrs[2]), Addrs[0], delTokens)) - require.NoError(t, err) - require.NotNil(t, res) - - validatorUpdates = staking.EndBlocker(ctx, sk) - require.Equal(t, 2, len(validatorUpdates)) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - newPower := int64(150) - - // validator misses a block - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - height++ - - // shouldn't be jailed/kicked yet - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - - // validator misses 500 more blocks, 501 total - latest := height - for ; height < latest+500; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - } - - // should now be jailed & kicked - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - - // check all the signing information - signInfo, found := keeper.GetValidatorSigningInfo(ctx, consAddr) - require.True(t, found) - require.Equal(t, int64(0), signInfo.MissedBlocksCounter) - require.Equal(t, int64(0), signInfo.IndexOffset) - // array should be cleared - for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ { - missed := keeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) - require.False(t, missed) - } - - // some blocks pass - height = int64(5000) - ctx = ctx.WithBlockHeight(height) - - // validator rejoins and starts signing again - sk.Unjail(ctx, consAddr) - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) - height++ - - // validator should not be kicked since we reset counter/array when it was jailed - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - - // validator misses 501 blocks - latest = height - for ; height < latest+501; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - } - - // validator should now be jailed & kicked - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - -} diff --git a/x/slashing/internal/keeper/params.go b/x/slashing/internal/keeper/params.go deleted file mode 100644 index 82fe991cccc1..000000000000 --- a/x/slashing/internal/keeper/params.go +++ /dev/null @@ -1,54 +0,0 @@ -package keeper - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -// SignedBlocksWindow - sliding window for downtime slashing -func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) { - k.paramspace.Get(ctx, types.KeySignedBlocksWindow, &res) - return -} - -// MinSignedPerWindow - minimum blocks signed per window -func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { - var minSignedPerWindow sdk.Dec - k.paramspace.Get(ctx, types.KeyMinSignedPerWindow, &minSignedPerWindow) - signedBlocksWindow := k.SignedBlocksWindow(ctx) - - // NOTE: RoundInt64 will never panic as minSignedPerWindow is - // less than 1. - return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64() -} - -// DowntimeJailDuration - Downtime unbond duration -func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) { - k.paramspace.Get(ctx, types.KeyDowntimeJailDuration, &res) - return -} - -// SlashFractionDoubleSign - fraction of power slashed in case of double sign -func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) { - k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res) - return -} - -// SlashFractionDowntime - fraction of power slashed for downtime -func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) { - k.paramspace.Get(ctx, types.KeySlashFractionDowntime, &res) - return -} - -// GetParams returns the total set of slashing parameters. -func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - k.paramspace.GetParamSet(ctx, ¶ms) - return params -} - -// SetParams sets the slashing parameters to the param space. -func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - k.paramspace.SetParamSet(ctx, ¶ms) -} diff --git a/x/slashing/internal/keeper/querier.go b/x/slashing/internal/keeper/querier.go deleted file mode 100644 index 11b365e1ee1b..000000000000 --- a/x/slashing/internal/keeper/querier.go +++ /dev/null @@ -1,92 +0,0 @@ -package keeper - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/client" - "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/slashing/internal/types" -) - -// NewQuerier creates a new querier for slashing clients. -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - case types.QueryParameters: - return queryParams(ctx, k) - - case types.QuerySigningInfo: - return querySigningInfo(ctx, req, k) - - case types.QuerySigningInfos: - return querySigningInfos(ctx, req, k) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} - -func queryParams(ctx sdk.Context, k Keeper) ([]byte, error) { - params := k.GetParams(ctx) - - res, err := codec.MarshalJSONIndent(types.ModuleCdc, params) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func querySigningInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QuerySigningInfoParams - - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - signingInfo, found := k.GetValidatorSigningInfo(ctx, params.ConsAddress) - if !found { - return nil, sdkerrors.Wrap(types.ErrNoSigningInfoFound, params.ConsAddress.String()) - } - - res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfo) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QuerySigningInfosParams - - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - var signingInfos []types.ValidatorSigningInfo - - k.IterateValidatorSigningInfos(ctx, func(consAddr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) { - signingInfos = append(signingInfos, info) - return false - }) - - start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx))) - if start < 0 || end < 0 { - signingInfos = []types.ValidatorSigningInfo{} - } else { - signingInfos = signingInfos[start:end] - } - - res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfos) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} diff --git a/x/slashing/internal/keeper/querier_test.go b/x/slashing/internal/keeper/querier_test.go deleted file mode 100644 index ea4095dc61b1..000000000000 --- a/x/slashing/internal/keeper/querier_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package keeper - -import ( - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -func TestNewQuerier(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, TestParams()) - querier := NewQuerier(keeper) - - query := abci.RequestQuery{ - Path: "", - Data: []byte{}, - } - - _, err := querier(ctx, []string{"parameters"}, query) - require.NoError(t, err) -} - -func TestQueryParams(t *testing.T) { - cdc := codec.New() - ctx, _, _, _, keeper := CreateTestInput(t, TestParams()) - - var params types.Params - - res, errRes := queryParams(ctx, keeper) - require.NoError(t, errRes) - - err := cdc.UnmarshalJSON(res, ¶ms) - require.NoError(t, err) - require.Equal(t, keeper.GetParams(ctx), params) -} diff --git a/x/slashing/internal/keeper/signing_info.go b/x/slashing/internal/keeper/signing_info.go deleted file mode 100644 index 1162c258f35a..000000000000 --- a/x/slashing/internal/keeper/signing_info.go +++ /dev/null @@ -1,143 +0,0 @@ -package keeper - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -// GetValidatorSigningInfo retruns the ValidatorSigningInfo for a specific validator -// ConsAddress -func (k Keeper) GetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetValidatorSigningInfoKey(address)) - if bz == nil { - found = false - return - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &info) - found = true - return -} - -// HasValidatorSigningInfo returns if a given validator has signing information -// persited. -func (k Keeper) HasValidatorSigningInfo(ctx sdk.Context, consAddr sdk.ConsAddress) bool { - _, ok := k.GetValidatorSigningInfo(ctx, consAddr) - return ok -} - -// SetValidatorSigningInfo sets the validator signing info to a consensus address key -func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info types.ValidatorSigningInfo) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(info) - store.Set(types.GetValidatorSigningInfoKey(address), bz) -} - -// IterateValidatorSigningInfos iterates over the stored ValidatorSigningInfo -func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context, - handler func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool)) { - - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, types.ValidatorSigningInfoKey) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - address := types.GetValidatorSigningInfoAddress(iter.Key()) - var info types.ValidatorSigningInfo - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &info) - if handler(address, info) { - break - } - } -} - -// GetValidatorMissedBlockBitArray gets the bit for the missed blocks array -func (k Keeper) GetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetValidatorMissedBlockBitArrayKey(address, index)) - if bz == nil { - // lazy: treat empty key as not missed - missed = false - return - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) - return -} - -// IterateValidatorMissedBlockBitArray iterates over the signed blocks window -// and performs a callback function -func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context, - address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { - - store := ctx.KVStore(k.storeKey) - index := int64(0) - // Array may be sparse - for ; index < k.SignedBlocksWindow(ctx); index++ { - var missed bool - bz := store.Get(types.GetValidatorMissedBlockBitArrayKey(address, index)) - if bz == nil { - continue - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &missed) - if handler(index, missed) { - break - } - } -} - -// JailUntil attempts to set a validator's JailedUntil attribute in its signing -// info. It will panic if the signing info does not exist for the validator. -func (k Keeper) JailUntil(ctx sdk.Context, consAddr sdk.ConsAddress, jailTime time.Time) { - signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) - if !ok { - panic("cannot jail validator that does not have any signing information") - } - - signInfo.JailedUntil = jailTime - k.SetValidatorSigningInfo(ctx, consAddr, signInfo) -} - -// Tombstone attempts to tombstone a validator. It will panic if signing info for -// the given validator does not exist. -func (k Keeper) Tombstone(ctx sdk.Context, consAddr sdk.ConsAddress) { - signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) - if !ok { - panic("cannot tombstone validator that does not have any signing information") - } - - if signInfo.Tombstoned { - panic("cannot tombstone validator that is already tombstoned") - } - - signInfo.Tombstoned = true - k.SetValidatorSigningInfo(ctx, consAddr, signInfo) -} - -// IsTombstoned returns if a given validator by consensus address is tombstoned. -func (k Keeper) IsTombstoned(ctx sdk.Context, consAddr sdk.ConsAddress) bool { - signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) - if !ok { - return false - } - - return signInfo.Tombstoned -} - -// SetValidatorMissedBlockBitArray sets the bit that checks if the validator has -// missed a block in the current window -func (k Keeper) SetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed) - store.Set(types.GetValidatorMissedBlockBitArrayKey(address, index), bz) -} - -// clearValidatorMissedBlockBitArray deletes every instance of ValidatorMissedBlockBitArray in the store -func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) { - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, types.GetValidatorMissedBlockBitArrayPrefixKey(address)) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - store.Delete(iter.Key()) - } -} diff --git a/x/slashing/internal/keeper/signing_info_test.go b/x/slashing/internal/keeper/signing_info_test.go deleted file mode 100644 index 7d9e7c56e526..000000000000 --- a/x/slashing/internal/keeper/signing_info_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package keeper - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -func TestGetSetValidatorSigningInfo(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) - require.False(t, found) - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, found) - require.Equal(t, info.StartHeight, int64(4)) - require.Equal(t, info.IndexOffset, int64(3)) - require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC()) - require.Equal(t, info.MissedBlocksCounter, int64(10)) -} - -func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - missed := keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.False(t, missed) // treat empty key as not missed - keeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0, true) - missed = keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.True(t, missed) // now should be missed -} - -func TestTombstoned(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) -} - -func TestJailUntil(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Now()) }) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Unix(253402300799, 0).UTC()) - - info, ok := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, ok) - require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) -} diff --git a/x/slashing/internal/keeper/test_common.go b/x/slashing/internal/keeper/test_common.go deleted file mode 100644 index eb8870c1d199..000000000000 --- a/x/slashing/internal/keeper/test_common.go +++ /dev/null @@ -1,158 +0,0 @@ -// nolint:deadcode,unused -// DONTCOVER -// noalias -package keeper - -import ( - "encoding/hex" - "testing" - "time" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" -) - -// TODO remove dependencies on staking (should only refer to validator set type from sdk) - -var ( - Pks = []crypto.PubKey{ - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"), - } - Addrs = []sdk.ValAddress{ - sdk.ValAddress(Pks[0].Address()), - sdk.ValAddress(Pks[1].Address()), - sdk.ValAddress(Pks[2].Address()), - } - InitTokens = sdk.TokensFromConsensusPower(200) - initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens)) -) - -func createTestCodec() *codec.Codec { - cdc := codec.New() - sdk.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - supply.RegisterCodec(cdc) - bank.RegisterCodec(cdc) - staking.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - return cdc -} - -func CreateTestInput(t *testing.T, defaults types.Params) (sdk.Context, bank.Keeper, staking.Keeper, params.Subspace, Keeper) { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keySlashing := sdk.NewKVStoreKey(types.StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - - db := dbm.NewMemDB() - - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - - err := ms.LoadLatestVersion() - require.Nil(t, err) - - ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewNopLogger()) - cdc := createTestCodec() - - feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams) - accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - - bk := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, maccPerms) - - totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens.MulRaw(int64(len(Addrs))))) - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - sk := staking.NewKeeper(cdc, keyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace)) - genesis := staking.DefaultGenesisState() - - // set module accounts - supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) - supplyKeeper.SetModuleAccount(ctx, bondPool) - supplyKeeper.SetModuleAccount(ctx, notBondedPool) - - _ = staking.InitGenesis(ctx, sk, accountKeeper, supplyKeeper, genesis) - - for _, addr := range Addrs { - _, err = bk.AddCoins(ctx, sdk.AccAddress(addr), initCoins) - } - require.Nil(t, err) - paramstore := paramsKeeper.Subspace(types.DefaultParamspace) - keeper := NewKeeper(cdc, keySlashing, &sk, paramstore) - - keeper.SetParams(ctx, defaults) - sk.SetHooks(keeper.Hooks()) - - return ctx, bk, sk, paramstore, keeper -} - -func newPubKey(pk string) (res crypto.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - var pkEd ed25519.PubKeyEd25519 - copy(pkEd[:], pkBytes) - return pkEd -} - -// Have to change these parameters for tests -// lest the tests take forever -func TestParams() types.Params { - params := types.DefaultParams() - params.SignedBlocksWindow = 1000 - params.DowntimeJailDuration = 60 * 60 - return params -} - -func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator { - commission := staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - return staking.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), - staking.Description{}, commission, sdk.OneInt(), - ) -} - -func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) staking.MsgDelegate { - amount := sdk.NewCoin(sdk.DefaultBondDenom, delAmount) - return staking.NewMsgDelegate(delAddr, valAddr, amount) -} diff --git a/x/slashing/internal/keeper/unjail.go b/x/slashing/internal/keeper/unjail.go deleted file mode 100644 index 51fda4855382..000000000000 --- a/x/slashing/internal/keeper/unjail.go +++ /dev/null @@ -1,50 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" -) - -// Unjail calls the staking Unjail function to unjail a validator if the -// jailed period has concluded -func (k Keeper) Unjail(ctx sdk.Context, validatorAddr sdk.ValAddress) error { - validator := k.sk.Validator(ctx, validatorAddr) - if validator == nil { - return types.ErrNoValidatorForAddress - } - - // cannot be unjailed if no self-delegation exists - selfDel := k.sk.Delegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - if selfDel == nil { - return types.ErrMissingSelfDelegation - } - - if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { - return types.ErrSelfDelegationTooLowToUnjail - } - - // cannot be unjailed if not jailed - if !validator.IsJailed() { - return types.ErrValidatorNotJailed - } - - consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address()) - - info, found := k.GetValidatorSigningInfo(ctx, consAddr) - if !found { - return types.ErrNoValidatorForAddress - } - - // cannot be unjailed if tombstoned - if info.Tombstoned { - return types.ErrValidatorJailed - } - - // cannot be unjailed until out of jail - if ctx.BlockHeader().Time.Before(info.JailedUntil) { - return types.ErrValidatorJailed - } - - k.sk.Unjail(ctx, consAddr) - return nil -} diff --git a/x/slashing/internal/types/codec.go b/x/slashing/internal/types/codec.go deleted file mode 100644 index 34790d54d630..000000000000 --- a/x/slashing/internal/types/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// RegisterCodec registers concrete types on codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil) -} - -// ModuleCdc defines the module codec -var ModuleCdc *codec.Codec - -func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() -} diff --git a/x/slashing/internal/types/errors.go b/x/slashing/internal/types/errors.go deleted file mode 100644 index 290ed36cad66..000000000000 --- a/x/slashing/internal/types/errors.go +++ /dev/null @@ -1,16 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// x/slashing module sentinel errors -var ( - ErrNoValidatorForAddress = sdkerrors.Register(ModuleName, 1, "address is not associated with any known validator") - ErrBadValidatorAddr = sdkerrors.Register(ModuleName, 2, "validator does not exist for that address") - ErrValidatorJailed = sdkerrors.Register(ModuleName, 3, "validator still jailed; cannot be unjailed") - ErrValidatorNotJailed = sdkerrors.Register(ModuleName, 4, "validator not jailed; cannot be unjailed") - ErrMissingSelfDelegation = sdkerrors.Register(ModuleName, 5, "validator has no self-delegation; cannot be unjailed") - ErrSelfDelegationTooLowToUnjail = sdkerrors.Register(ModuleName, 6, "validator's self delegation less than minimum; cannot be unjailed") - ErrNoSigningInfoFound = sdkerrors.Register(ModuleName, 7, "no validator signing info found") -) diff --git a/x/slashing/internal/types/expected_keepers.go b/x/slashing/internal/types/expected_keepers.go deleted file mode 100644 index 58e1f30d67ef..000000000000 --- a/x/slashing/internal/types/expected_keepers.go +++ /dev/null @@ -1,54 +0,0 @@ -// noalias -// DONTCOVER -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/params" - stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" -) - -// AccountKeeper expected account keeper -type AccountKeeper interface { - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account - IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool)) -} - -// ParamSubspace defines the expected Subspace interfacace -type ParamSubspace interface { - WithKeyTable(table params.KeyTable) params.Subspace - Get(ctx sdk.Context, key []byte, ptr interface{}) - GetParamSet(ctx sdk.Context, ps params.ParamSet) - SetParamSet(ctx sdk.Context, ps params.ParamSet) -} - -// StakingKeeper expected staking keeper -type StakingKeeper interface { - // iterate through validators by operator address, execute func for each validator - IterateValidators(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) - - Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address - ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address - - // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction - Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) - Jail(sdk.Context, sdk.ConsAddress) // jail a validator - Unjail(sdk.Context, sdk.ConsAddress) // unjail a validator - - // Delegation allows for getting a particular delegation for a given validator - // and delegator outside the scope of the staking module. - Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI - - // MaxValidators returns the maximum amount of bonded validators - MaxValidators(sdk.Context) uint16 -} - -// StakingHooks event hooks for staking validator object (noalias) -type StakingHooks interface { - AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created - AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted - - AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is bonded -} diff --git a/x/slashing/internal/types/genesis.go b/x/slashing/internal/types/genesis.go deleted file mode 100644 index 42089eea5191..000000000000 --- a/x/slashing/internal/types/genesis.go +++ /dev/null @@ -1,80 +0,0 @@ -package types - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState - all slashing state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - SigningInfos map[string]ValidatorSigningInfo `json:"signing_infos" yaml:"signing_infos"` - MissedBlocks map[string][]MissedBlock `json:"missed_blocks" yaml:"missed_blocks"` -} - -// NewGenesisState creates a new GenesisState object -func NewGenesisState( - params Params, signingInfos map[string]ValidatorSigningInfo, missedBlocks map[string][]MissedBlock, -) GenesisState { - - return GenesisState{ - Params: params, - SigningInfos: signingInfos, - MissedBlocks: missedBlocks, - } -} - -// MissedBlock -type MissedBlock struct { - Index int64 `json:"index" yaml:"index"` - Missed bool `json:"missed" yaml:"missed"` -} - -// NewMissedBlock creates a new MissedBlock instance -func NewMissedBlock(index int64, missed bool) MissedBlock { - return MissedBlock{ - Index: index, - Missed: missed, - } -} - -// DefaultGenesisState - default GenesisState used by Cosmos Hub -func DefaultGenesisState() GenesisState { - return GenesisState{ - Params: DefaultParams(), - SigningInfos: make(map[string]ValidatorSigningInfo), - MissedBlocks: make(map[string][]MissedBlock), - } -} - -// ValidateGenesis validates the slashing genesis parameters -func ValidateGenesis(data GenesisState) error { - downtime := data.Params.SlashFractionDowntime - if downtime.IsNegative() || downtime.GT(sdk.OneDec()) { - return fmt.Errorf("slashing fraction downtime should be less than or equal to one and greater than zero, is %s", downtime.String()) - } - - dblSign := data.Params.SlashFractionDoubleSign - if dblSign.IsNegative() || dblSign.GT(sdk.OneDec()) { - return fmt.Errorf("slashing fraction double sign should be less than or equal to one and greater than zero, is %s", dblSign.String()) - } - - minSign := data.Params.MinSignedPerWindow - if minSign.IsNegative() || minSign.GT(sdk.OneDec()) { - return fmt.Errorf("min signed per window should be less than or equal to one and greater than zero, is %s", minSign.String()) - } - - downtimeJail := data.Params.DowntimeJailDuration - if downtimeJail < 1*time.Minute { - return fmt.Errorf("downtime unblond duration must be at least 1 minute, is %s", downtimeJail.String()) - } - - signedWindow := data.Params.SignedBlocksWindow - if signedWindow < 10 { - return fmt.Errorf("signed blocks window must be at least 10, is %d", signedWindow) - } - - return nil -} diff --git a/x/slashing/internal/types/keys.go b/x/slashing/internal/types/keys.go deleted file mode 100644 index 865b0d5f1cbd..000000000000 --- a/x/slashing/internal/types/keys.go +++ /dev/null @@ -1,66 +0,0 @@ -package types - -import ( - "encoding/binary" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - // ModuleName is the name of the module - ModuleName = "slashing" - - // StoreKey is the store key string for slashing - StoreKey = ModuleName - - // RouterKey is the message route for slashing - RouterKey = ModuleName - - // QuerierRoute is the querier route for slashing - QuerierRoute = ModuleName -) - -// Keys for slashing store -// Items are stored with the following key: values -// -// - 0x01: ValidatorSigningInfo -// -// - 0x02: bool -// -// - 0x03: crypto.PubKey -var ( - ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info - ValidatorMissedBlockBitArrayKey = []byte{0x02} // Prefix for missed block bit array - AddrPubkeyRelationKey = []byte{0x03} // Prefix for address-pubkey relation -) - -// GetValidatorSigningInfoKey - stored by *Consensus* address (not operator address) -func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte { - return append(ValidatorSigningInfoKey, v.Bytes()...) -} - -// GetValidatorSigningInfoAddress - extract the address from a validator signing info key -func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { - addr := key[1:] - if len(addr) != sdk.AddrLen { - panic("unexpected key length") - } - return sdk.ConsAddress(addr) -} - -// GetValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address) -func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { - return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...) -} - -// GetValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address) -func GetValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(i)) - return append(GetValidatorMissedBlockBitArrayPrefixKey(v), b...) -} - -// GetAddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address -func GetAddrPubkeyRelationKey(address []byte) []byte { - return append(AddrPubkeyRelationKey, address...) -} diff --git a/x/slashing/internal/types/msg.go b/x/slashing/internal/types/msg.go deleted file mode 100644 index b2ccdce173ba..000000000000 --- a/x/slashing/internal/types/msg.go +++ /dev/null @@ -1,41 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// verify interface at compile time -var _ sdk.Msg = &MsgUnjail{} - -// MsgUnjail - struct for unjailing jailed validator -type MsgUnjail struct { - ValidatorAddr sdk.ValAddress `json:"address" yaml:"address"` // address of the validator operator -} - -// NewMsgUnjail creates a new MsgUnjail instance -func NewMsgUnjail(validatorAddr sdk.ValAddress) MsgUnjail { - return MsgUnjail{ - ValidatorAddr: validatorAddr, - } -} - -//nolint -func (msg MsgUnjail) Route() string { return RouterKey } -func (msg MsgUnjail) Type() string { return "unjail" } -func (msg MsgUnjail) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} -} - -// GetSignBytes gets the bytes for the message signer to sign on -func (msg MsgUnjail) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) -} - -// ValidateBasic validity check for the AnteHandler -func (msg MsgUnjail) ValidateBasic() error { - if msg.ValidatorAddr.Empty() { - return ErrBadValidatorAddr - } - return nil -} diff --git a/x/slashing/internal/types/params.go b/x/slashing/internal/types/params.go deleted file mode 100644 index 15dd6cca6238..000000000000 --- a/x/slashing/internal/types/params.go +++ /dev/null @@ -1,166 +0,0 @@ -package types - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" -) - -// Default parameter namespace -const ( - DefaultParamspace = ModuleName - DefaultSignedBlocksWindow = int64(100) - DefaultDowntimeJailDuration = 60 * 10 * time.Second -) - -var ( - DefaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) - DefaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) - DefaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) -) - -// Parameter store keys -var ( - KeySignedBlocksWindow = []byte("SignedBlocksWindow") - KeyMinSignedPerWindow = []byte("MinSignedPerWindow") - KeyDowntimeJailDuration = []byte("DowntimeJailDuration") - KeySlashFractionDoubleSign = []byte("SlashFractionDoubleSign") - KeySlashFractionDowntime = []byte("SlashFractionDowntime") -) - -// ParamKeyTable for slashing module -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable().RegisterParamSet(&Params{}) -} - -// Params - used for initializing default parameter for slashing at genesis -type Params struct { - SignedBlocksWindow int64 `json:"signed_blocks_window" yaml:"signed_blocks_window"` - MinSignedPerWindow sdk.Dec `json:"min_signed_per_window" yaml:"min_signed_per_window"` - DowntimeJailDuration time.Duration `json:"downtime_jail_duration" yaml:"downtime_jail_duration"` - SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign" yaml:"slash_fraction_double_sign"` - SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime" yaml:"slash_fraction_downtime"` -} - -// NewParams creates a new Params object -func NewParams( - signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration, - slashFractionDoubleSign, slashFractionDowntime sdk.Dec, -) Params { - - return Params{ - SignedBlocksWindow: signedBlocksWindow, - MinSignedPerWindow: minSignedPerWindow, - DowntimeJailDuration: downtimeJailDuration, - SlashFractionDoubleSign: slashFractionDoubleSign, - SlashFractionDowntime: slashFractionDowntime, - } -} - -// String implements the stringer interface for Params -func (p Params) String() string { - return fmt.Sprintf(`Slashing Params: - SignedBlocksWindow: %d - MinSignedPerWindow: %s - DowntimeJailDuration: %s - SlashFractionDoubleSign: %s - SlashFractionDowntime: %s`, - p.SignedBlocksWindow, p.MinSignedPerWindow, - p.DowntimeJailDuration, p.SlashFractionDoubleSign, - p.SlashFractionDowntime) -} - -// ParamSetPairs - Implements params.ParamSet -func (p *Params) ParamSetPairs() params.ParamSetPairs { - return params.ParamSetPairs{ - params.NewParamSetPair(KeySignedBlocksWindow, &p.SignedBlocksWindow, validateSignedBlocksWindow), - params.NewParamSetPair(KeyMinSignedPerWindow, &p.MinSignedPerWindow, validateMinSignedPerWindow), - params.NewParamSetPair(KeyDowntimeJailDuration, &p.DowntimeJailDuration, validateDowntimeJailDuration), - params.NewParamSetPair(KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign, validateSlashFractionDoubleSign), - params.NewParamSetPair(KeySlashFractionDowntime, &p.SlashFractionDowntime, validateSlashFractionDowntime), - } -} - -// DefaultParams defines the parameters for this module -func DefaultParams() Params { - return NewParams( - DefaultSignedBlocksWindow, DefaultMinSignedPerWindow, DefaultDowntimeJailDuration, - DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime, - ) -} - -func validateSignedBlocksWindow(i interface{}) error { - v, ok := i.(int64) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v <= 0 { - return fmt.Errorf("signed blocks window must be positive: %d", v) - } - - return nil -} - -func validateMinSignedPerWindow(i interface{}) error { - v, ok := i.(sdk.Dec) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v.IsNegative() { - return fmt.Errorf("min signed per window cannot be negative: %s", v) - } - if v.GT(sdk.OneDec()) { - return fmt.Errorf("min signed per window too large: %s", v) - } - - return nil -} - -func validateDowntimeJailDuration(i interface{}) error { - v, ok := i.(time.Duration) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v <= 0 { - return fmt.Errorf("downtime jail duration must be positive: %s", v) - } - - return nil -} - -func validateSlashFractionDoubleSign(i interface{}) error { - v, ok := i.(sdk.Dec) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v.IsNegative() { - return fmt.Errorf("double sign slash fraction cannot be negative: %s", v) - } - if v.GT(sdk.OneDec()) { - return fmt.Errorf("double sign slash fraction too large: %s", v) - } - - return nil -} - -func validateSlashFractionDowntime(i interface{}) error { - v, ok := i.(sdk.Dec) - if !ok { - return fmt.Errorf("invalid parameter type: %T", i) - } - - if v.IsNegative() { - return fmt.Errorf("downtime slash fraction cannot be negative: %s", v) - } - if v.GT(sdk.OneDec()) { - return fmt.Errorf("downtime slash fraction too large: %s", v) - } - - return nil -} diff --git a/x/slashing/internal/types/querier.go b/x/slashing/internal/types/querier.go deleted file mode 100644 index b149886c321b..000000000000 --- a/x/slashing/internal/types/querier.go +++ /dev/null @@ -1,36 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// DONTCOVER - -// Query endpoints supported by the slashing querier -const ( - QueryParameters = "parameters" - QuerySigningInfo = "signingInfo" - QuerySigningInfos = "signingInfos" -) - -// QuerySigningInfoParams defines the params for the following queries: -// - 'custom/slashing/signingInfo' -type QuerySigningInfoParams struct { - ConsAddress sdk.ConsAddress -} - -// NewQuerySigningInfoParams creates a new QuerySigningInfoParams instance -func NewQuerySigningInfoParams(consAddr sdk.ConsAddress) QuerySigningInfoParams { - return QuerySigningInfoParams{consAddr} -} - -// QuerySigningInfosParams defines the params for the following queries: -// - 'custom/slashing/signingInfos' -type QuerySigningInfosParams struct { - Page, Limit int -} - -// NewQuerySigningInfosParams creates a new QuerySigningInfosParams instance -func NewQuerySigningInfosParams(page, limit int) QuerySigningInfosParams { - return QuerySigningInfosParams{page, limit} -} diff --git a/x/slashing/internal/types/signing_info.go b/x/slashing/internal/types/signing_info.go deleted file mode 100644 index 431658c2241a..000000000000 --- a/x/slashing/internal/types/signing_info.go +++ /dev/null @@ -1,47 +0,0 @@ -package types - -import ( - "fmt" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// ValidatorSigningInfo defines the signing info for a validator -type ValidatorSigningInfo struct { - Address sdk.ConsAddress `json:"address" yaml:"address"` // validator consensus address - StartHeight int64 `json:"start_height" yaml:"start_height"` // height at which validator was first a candidate OR was unjailed - IndexOffset int64 `json:"index_offset" yaml:"index_offset"` // index offset into signed block bit array - JailedUntil time.Time `json:"jailed_until" yaml:"jailed_until"` // timestamp validator cannot be unjailed until - Tombstoned bool `json:"tombstoned" yaml:"tombstoned"` // whether or not a validator has been tombstoned (killed out of validator set) - MissedBlocksCounter int64 `json:"missed_blocks_counter" yaml:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time) -} - -// NewValidatorSigningInfo creates a new ValidatorSigningInfo instance -func NewValidatorSigningInfo( - condAddr sdk.ConsAddress, startHeight, indexOffset int64, - jailedUntil time.Time, tombstoned bool, missedBlocksCounter int64, -) ValidatorSigningInfo { - - return ValidatorSigningInfo{ - Address: condAddr, - StartHeight: startHeight, - IndexOffset: indexOffset, - JailedUntil: jailedUntil, - Tombstoned: tombstoned, - MissedBlocksCounter: missedBlocksCounter, - } -} - -// String implements the stringer interface for ValidatorSigningInfo -func (i ValidatorSigningInfo) String() string { - return fmt.Sprintf(`Validator Signing Info: - Address: %s - Start Height: %d - Index Offset: %d - Jailed Until: %v - Tombstoned: %t - Missed Blocks Counter: %d`, - i.Address, i.StartHeight, i.IndexOffset, i.JailedUntil, - i.Tombstoned, i.MissedBlocksCounter) -} diff --git a/x/slashing/keeper/common_test.go b/x/slashing/keeper/common_test.go new file mode 100644 index 000000000000..940d995cacc7 --- /dev/null +++ b/x/slashing/keeper/common_test.go @@ -0,0 +1,7 @@ +package keeper_test + +import sdk "github.com/cosmos/cosmos-sdk/types" + +var ( + InitTokens = sdk.TokensFromConsensusPower(200) +) diff --git a/x/slashing/keeper/grpc_query.go b/x/slashing/keeper/grpc_query.go new file mode 100644 index 000000000000..982d0939970a --- /dev/null +++ b/x/slashing/keeper/grpc_query.go @@ -0,0 +1,74 @@ +package keeper + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "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/slashing/types" +) + +var _ types.QueryServer = Keeper{} + +func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + params := k.GetParams(ctx) + + return &types.QueryParamsResponse{Params: params}, nil +} + +func (k Keeper) SigningInfo(c context.Context, req *types.QuerySigningInfoRequest) (*types.QuerySigningInfoResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.ConsAddress == "" { + return nil, status.Errorf(codes.InvalidArgument, "invalid request") + } + + consAddr, err := sdk.ConsAddressFromBech32(req.ConsAddress) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + signingInfo, found := k.GetValidatorSigningInfo(ctx, consAddr) + if !found { + return nil, status.Errorf(codes.NotFound, "SigningInfo not found for validator %s", req.ConsAddress) + } + + return &types.QuerySigningInfoResponse{ValSigningInfo: signingInfo}, nil +} + +func (k Keeper) SigningInfos(c context.Context, req *types.QuerySigningInfosRequest) (*types.QuerySigningInfosResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + store := ctx.KVStore(k.storeKey) + var signInfos []types.ValidatorSigningInfo + + sigInfoStore := prefix.NewStore(store, types.ValidatorSigningInfoKeyPrefix) + pageRes, err := query.Paginate(sigInfoStore, req.Pagination, func(key []byte, value []byte) error { + var info types.ValidatorSigningInfo + err := k.cdc.UnmarshalBinaryBare(value, &info) + if err != nil { + return err + } + signInfos = append(signInfos, info) + return nil + }) + if err != nil { + return nil, err + } + return &types.QuerySigningInfosResponse{Info: signInfos, Pagination: pageRes}, nil +} diff --git a/x/slashing/keeper/grpc_query_test.go b/x/slashing/keeper/grpc_query_test.go new file mode 100644 index 000000000000..d923b822c76c --- /dev/null +++ b/x/slashing/keeper/grpc_query_test.go @@ -0,0 +1,110 @@ +package keeper_test + +import ( + gocontext "context" + "testing" + "time" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +type SlashingTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient + addrDels []sdk.AccAddress +} + +func (suite *SlashingTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + app.BankKeeper.SetParams(ctx, banktypes.DefaultParams()) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.TokensFromConsensusPower(200)) + + info1 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[0]), int64(4), int64(3), + time.Unix(2, 0), false, int64(10)) + info2 := types.NewValidatorSigningInfo(sdk.ConsAddress(addrDels[1]), int64(5), int64(4), + time.Unix(2, 0), false, int64(10)) + + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), info1) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[1]), info2) + + suite.app = app + suite.ctx = ctx + suite.addrDels = addrDels + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, app.SlashingKeeper) + queryClient := types.NewQueryClient(queryHelper) + suite.queryClient = queryClient +} + +func (suite *SlashingTestSuite) TestGRPCQueryParams() { + queryClient := suite.queryClient + paramsResp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) + + suite.NoError(err) + suite.Equal(testslashing.TestParams(), paramsResp.Params) +} + +func (suite *SlashingTestSuite) TestGRPCSigningInfo() { + queryClient := suite.queryClient + + infoResp, err := queryClient.SigningInfo(gocontext.Background(), &types.QuerySigningInfoRequest{ConsAddress: ""}) + suite.Error(err) + suite.Nil(infoResp) + + consAddr := sdk.ConsAddress(suite.addrDels[0]) + info, found := suite.app.SlashingKeeper.GetValidatorSigningInfo(suite.ctx, consAddr) + suite.True(found) + + infoResp, err = queryClient.SigningInfo(gocontext.Background(), + &types.QuerySigningInfoRequest{ConsAddress: consAddr.String()}) + suite.NoError(err) + suite.Equal(info, infoResp.ValSigningInfo) +} + +func (suite *SlashingTestSuite) TestGRPCSigningInfos() { + queryClient := suite.queryClient + + var signingInfos []types.ValidatorSigningInfo + + suite.app.SlashingKeeper.IterateValidatorSigningInfos(suite.ctx, func(consAddr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) { + signingInfos = append(signingInfos, info) + return false + }) + + // verify all values are returned without pagination + var infoResp, err = queryClient.SigningInfos(gocontext.Background(), + &types.QuerySigningInfosRequest{Pagination: nil}) + suite.NoError(err) + suite.Equal(signingInfos, infoResp.Info) + + infoResp, err = queryClient.SigningInfos(gocontext.Background(), + &types.QuerySigningInfosRequest{Pagination: &query.PageRequest{Limit: 1, CountTotal: true}}) + suite.NoError(err) + suite.Len(infoResp.Info, 1) + suite.Equal(signingInfos[0], infoResp.Info[0]) + suite.NotNil(infoResp.Pagination.NextKey) + suite.Equal(uint64(2), infoResp.Pagination.Total) +} + +func TestSlashingTestSuite(t *testing.T) { + suite.Run(t, new(SlashingTestSuite)) +} diff --git a/x/slashing/internal/keeper/hooks.go b/x/slashing/keeper/hooks.go similarity index 86% rename from x/slashing/internal/keeper/hooks.go rename to x/slashing/keeper/hooks.go index a8ef6e093e15..d59601425842 100644 --- a/x/slashing/internal/keeper/hooks.go +++ b/x/slashing/keeper/hooks.go @@ -1,4 +1,3 @@ -// nolint package keeper import ( @@ -7,7 +6,7 @@ import ( "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) { @@ -26,13 +25,19 @@ func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ } } -// When a validator is created, add the address-pubkey relation. -func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { +// AfterValidatorCreated adds the address-pubkey relation when a validator is created. +func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { validator := k.sk.Validator(ctx, valAddr) - k.AddPubkey(ctx, validator.GetConsPubKey()) + consPk, err := validator.ConsPubKey() + if err != nil { + return err + } + k.AddPubkey(ctx, consPk) + + return nil } -// When a validator is removed, delete the address-pubkey relation. +// AfterValidatorRemoved deletes the address-pubkey relation when a validator is removed, func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, address sdk.ConsAddress) { k.deleteAddrPubkeyRelation(ctx, crypto.Address(address)) } @@ -66,7 +71,6 @@ func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { h.k.AfterValidatorCreated(ctx, valAddr) } -// nolint - unused hooks func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ sdk.ValAddress) {} func (h Hooks) BeforeValidatorModified(_ sdk.Context, _ sdk.ValAddress) {} func (h Hooks) BeforeDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {} diff --git a/x/slashing/internal/keeper/infractions.go b/x/slashing/keeper/infractions.go similarity index 80% rename from x/slashing/internal/keeper/infractions.go rename to x/slashing/keeper/infractions.go index a10b6f945b3a..06baa48f82b4 100644 --- a/x/slashing/internal/keeper/infractions.go +++ b/x/slashing/keeper/infractions.go @@ -3,14 +3,13 @@ package keeper import ( "fmt" - "github.com/tendermint/tendermint/crypto" - + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // HandleValidatorSignature handles a validator signature, must be called once per validator per block. -func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { +func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Address, power int64, signed bool) { logger := k.Logger(ctx) height := ctx.BlockHeight() @@ -49,6 +48,8 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p // Array value at this index has not changed, no need to update counter } + minSignedPerWindow := k.MinSignedPerWindow(ctx) + if missed { ctx.EventManager().EmitEvent( sdk.NewEvent( @@ -59,22 +60,23 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p ), ) - logger.Info( - fmt.Sprintf("Absent validator %s at height %d, %d missed, threshold %d", consAddr, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx))) + logger.Debug( + "absent validator", + "height", height, + "validator", consAddr.String(), + "missed", signInfo.MissedBlocksCounter, + "threshold", minSignedPerWindow, + ) } minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) - maxMissed := k.SignedBlocksWindow(ctx) - k.MinSignedPerWindow(ctx) + maxMissed := k.SignedBlocksWindow(ctx) - minSignedPerWindow // if we are past the minimum height and the validator has missed too many blocks, punish them if height > minHeight && signInfo.MissedBlocksCounter > maxMissed { validator := k.sk.ValidatorByConsAddr(ctx, consAddr) if validator != nil && !validator.IsJailed() { - // Downtime confirmed: slash and jail the validator - logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", - consAddr, minHeight, k.MinSignedPerWindow(ctx))) - // We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height, // and subtract an additional 1 since this is the LastCommit. // Note that this *can* result in a negative "distributionHeight" up to -ValidatorUpdateDelay-1, @@ -100,10 +102,21 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p signInfo.MissedBlocksCounter = 0 signInfo.IndexOffset = 0 k.clearValidatorMissedBlockBitArray(ctx, consAddr) + + logger.Info( + "slashing and jailing validator due to liveness fault", + "height", height, + "validator", consAddr.String(), + "min_height", minHeight, + "threshold", minSignedPerWindow, + "slashed", k.SlashFractionDowntime(ctx).String(), + "jailed_until", signInfo.JailedUntil, + ) } else { - // Validator was (a) not found or (b) already jailed, don't slash + // validator was (a) not found or (b) already jailed so we do not slash logger.Info( - fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed", consAddr), + "validator would have been slashed for downtime, but was either not found in store or already jailed", + "validator", consAddr.String(), ) } } diff --git a/x/slashing/keeper/keeper.go b/x/slashing/keeper/keeper.go new file mode 100644 index 000000000000..69a5e25b4a4c --- /dev/null +++ b/x/slashing/keeper/keeper.go @@ -0,0 +1,96 @@ +package keeper + +import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// Keeper of the slashing store +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + sk types.StakingKeeper + paramspace types.ParamSubspace +} + +// NewKeeper creates a slashing keeper +func NewKeeper(cdc codec.BinaryMarshaler, key sdk.StoreKey, sk types.StakingKeeper, paramspace types.ParamSubspace) Keeper { + // set KeyTable if it has not already been set + if !paramspace.HasKeyTable() { + paramspace = paramspace.WithKeyTable(types.ParamKeyTable()) + } + + return Keeper{ + storeKey: key, + cdc: cdc, + sk: sk, + paramspace: paramspace, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// AddPubkey sets a address-pubkey relation +func (k Keeper) AddPubkey(ctx sdk.Context, pubkey cryptotypes.PubKey) error { + bz, err := k.cdc.MarshalInterface(pubkey) + if err != nil { + return err + } + store := ctx.KVStore(k.storeKey) + key := types.AddrPubkeyRelationKey(pubkey.Address()) + store.Set(key, bz) + return nil +} + +// GetPubkey returns the pubkey from the adddress-pubkey relation +func (k Keeper) GetPubkey(ctx sdk.Context, a cryptotypes.Address) (cryptotypes.PubKey, error) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.AddrPubkeyRelationKey(a)) + if bz == nil { + return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(a)) + } + var pk cryptotypes.PubKey + return pk, k.cdc.UnmarshalInterface(bz, &pk) +} + +// Slash attempts to slash a validator. The slash is delegated to the staking +// module to make the necessary validator changes. +func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, fraction sdk.Dec, power, distributionHeight int64) { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSlash, + sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()), + sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), + sdk.NewAttribute(types.AttributeKeyReason, types.AttributeValueDoubleSign), + ), + ) + + k.sk.Slash(ctx, consAddr, distributionHeight, power, fraction) +} + +// Jail attempts to jail a validator. The slash is delegated to the staking module +// to make the necessary validator changes. +func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSlash, + sdk.NewAttribute(types.AttributeKeyJailed, consAddr.String()), + ), + ) + + k.sk.Jail(ctx, consAddr) +} + +func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr cryptotypes.Address) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.AddrPubkeyRelationKey(addr)) +} diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go new file mode 100644 index 000000000000..e083df637a0f --- /dev/null +++ b/x/slashing/keeper/keeper_test.go @@ -0,0 +1,278 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + 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/slashing/testslashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestUnJailNotBonded(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + p := app.StakingKeeper.GetParams(ctx) + p.MaxValidators = 5 + app.StakingKeeper.SetParams(ctx, p) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 6, sdk.TokensFromConsensusPower(200)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) + pks := simapp.CreateTestPubKeys(6) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + + // create max (5) validators all with the same power + for i := uint32(0); i < p.MaxValidators; i++ { + addr, val := valAddrs[i], pks[i] + tstaking.CreateValidatorWithValPower(addr, val, 100, true) + } + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // create a 6th validator with less power than the cliff validator (won't be bonded) + addr, val := valAddrs[5], pks[5] + amt := sdk.TokensFromConsensusPower(50) + msg := tstaking.CreateValidatorMsg(addr, val, amt) + msg.MinSelfDelegation = amt + tstaking.Handle(msg, true) + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) + + // unbond below minimum self-delegation + require.Equal(t, p.BondDenom, tstaking.Denom) + tstaking.Undelegate(sdk.AccAddress(addr), addr, sdk.TokensFromConsensusPower(1), true) + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // verify that validator is jailed + tstaking.CheckValidator(addr, -1, true) + + // verify we cannot unjail (yet) + require.Error(t, app.SlashingKeeper.Unjail(ctx, addr)) + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + // bond to meet minimum self-delegation + tstaking.DelegateWithPower(sdk.AccAddress(addr), addr, 1) + + staking.EndBlocker(ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) + + // verify we can immediately unjail + require.NoError(t, app.SlashingKeeper.Unjail(ctx, addr)) + + tstaking.CheckValidator(addr, -1, false) +} + +// Test a new validator entering the validator set +// Ensure that SigningInfo.StartHeight is set correctly +// and that they are not immediately jailed +func TestHandleNewValidator(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) + pks := simapp.CreateTestPubKeys(1) + addr, val := valAddrs[0], pks[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 1) + + // Validator created + amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) + + staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal( + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + ) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) + + // Now a validator, for two blocks + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, true) + ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 2) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, false) + + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)+1, info.StartHeight) + require.Equal(t, int64(2), info.IndexOffset) + require.Equal(t, int64(1), info.MissedBlocksCounter) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + + // validator should be bonded still, should not have been jailed or slashed + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) + bondPool := app.StakingKeeper.GetBondedPool(ctx) + expTokens := sdk.TokensFromConsensusPower(100) + require.True(t, expTokens.Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) +} + +// Test a jailed validator being "down" twice +// Ensure that they're only slashed once +func TestHandleAlreadyJailed(t *testing.T) { + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) + pks := simapp.CreateTestPubKeys(1) + addr, val := valAddrs[0], pks[0] + power := int64(100) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + + amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) + + staking.EndBlocker(ctx, app.StakingKeeper) + + // 1000 first blocks OK + height := int64(0) + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + + // 501 blocks missed + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx))+1; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should have been jailed and slashed + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) + + // validator should have been slashed + resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) + require.Equal(t, resultingTokens, validator.GetTokens()) + + // another block missed + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + + // validator should not have been slashed twice + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, resultingTokens, validator.GetTokens()) +} + +// Test a validator dipping in and out of the validator set +// Ensure that missed blocks are tracked correctly and that +// the start height of the signing info is reset correctly +func TestValidatorDippingInAndOut(t *testing.T) { + + // initial setup + // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) + + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = 1 + app.StakingKeeper.SetParams(ctx, params) + power := int64(100) + + pks := simapp.CreateTestPubKeys(3) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + + addr, val := pks[0].Address(), pks[0] + consAddr := sdk.ConsAddress(addr) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + valAddr := sdk.ValAddress(addr) + + tstaking.CreateValidatorWithValPower(valAddr, val, power, true) + staking.EndBlocker(ctx, app.StakingKeeper) + + // 100 first blocks OK + height := int64(0) + for ; height < int64(100); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + + // kick first validator out of validator set + tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], 101, true) + validatorUpdates := staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal(t, 2, len(validatorUpdates)) + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) + + // 600 more blocks happened + height = 700 + ctx = ctx.WithBlockHeight(height) + + // validator added back in + tstaking.DelegateWithPower(sdk.AccAddress(pks[2].Address()), sdk.ValAddress(pks[0].Address()), 50) + + validatorUpdates = staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal(t, 2, len(validatorUpdates)) + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + newPower := int64(150) + + // validator misses a block + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + height++ + + // shouldn't be jailed/kicked yet + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + + // validator misses 500 more blocks, 501 total + latest := height + for ; height < latest+500; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + } + + // should now be jailed & kicked + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) + + // check all the signing information + signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) + require.True(t, found) + require.Equal(t, int64(0), signInfo.MissedBlocksCounter) + require.Equal(t, int64(0), signInfo.IndexOffset) + // array should be cleared + for offset := int64(0); offset < app.SlashingKeeper.SignedBlocksWindow(ctx); offset++ { + missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) + require.False(t, missed) + } + + // some blocks pass + height = int64(5000) + ctx = ctx.WithBlockHeight(height) + + // validator rejoins and starts signing again + app.StakingKeeper.Unjail(ctx, consAddr) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) + height++ + + // validator should not be kicked since we reset counter/array when it was jailed + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + + // validator misses 501 blocks + latest = height + for ; height < latest+501; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + } + + // validator should now be jailed & kicked + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) +} diff --git a/x/slashing/keeper/msg_server.go b/x/slashing/keeper/msg_server.go new file mode 100644 index 000000000000..90a2558384a3 --- /dev/null +++ b/x/slashing/keeper/msg_server.go @@ -0,0 +1,46 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the slashing MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +// Unjail implements MsgServer.Unjail method. +// Validators must submit a transaction to unjail itself after +// having been jailed (and thus unbonded) for downtime +func (k msgServer) Unjail(goCtx context.Context, msg *types.MsgUnjail) (*types.MsgUnjailResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddr, valErr := sdk.ValAddressFromBech32(msg.ValidatorAddr) + if valErr != nil { + return nil, valErr + } + err := k.Keeper.Unjail(ctx, valAddr) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddr), + ), + ) + + return &types.MsgUnjailResponse{}, nil +} diff --git a/x/slashing/keeper/params.go b/x/slashing/keeper/params.go new file mode 100644 index 000000000000..8ece97a687ad --- /dev/null +++ b/x/slashing/keeper/params.go @@ -0,0 +1,54 @@ +package keeper + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// SignedBlocksWindow - sliding window for downtime slashing +func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) { + k.paramspace.Get(ctx, types.KeySignedBlocksWindow, &res) + return +} + +// MinSignedPerWindow - minimum blocks signed per window +func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { + var minSignedPerWindow sdk.Dec + k.paramspace.Get(ctx, types.KeyMinSignedPerWindow, &minSignedPerWindow) + signedBlocksWindow := k.SignedBlocksWindow(ctx) + + // NOTE: RoundInt64 will never panic as minSignedPerWindow is + // less than 1. + return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64() +} + +// DowntimeJailDuration - Downtime unbond duration +func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) { + k.paramspace.Get(ctx, types.KeyDowntimeJailDuration, &res) + return +} + +// SlashFractionDoubleSign - fraction of power slashed in case of double sign +func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) { + k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res) + return +} + +// SlashFractionDowntime - fraction of power slashed for downtime +func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) { + k.paramspace.Get(ctx, types.KeySlashFractionDowntime, &res) + return +} + +// GetParams returns the total set of slashing parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + k.paramspace.GetParamSet(ctx, ¶ms) + return params +} + +// SetParams sets the slashing parameters to the param space. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramspace.SetParamSet(ctx, ¶ms) +} diff --git a/x/slashing/keeper/querier.go b/x/slashing/keeper/querier.go new file mode 100644 index 000000000000..036f3a16dde3 --- /dev/null +++ b/x/slashing/keeper/querier.go @@ -0,0 +1,92 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "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/slashing/types" +) + +// NewQuerier creates a new querier for slashing clients. +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + case types.QueryParameters: + return queryParams(ctx, k, legacyQuerierCdc) + + case types.QuerySigningInfo: + return querySigningInfo(ctx, req, k, legacyQuerierCdc) + + case types.QuerySigningInfos: + return querySigningInfos(ctx, req, k, legacyQuerierCdc) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + } +} + +func queryParams(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + params := k.GetParams(ctx) + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func querySigningInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QuerySigningInfoRequest + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + signingInfo, found := k.GetValidatorSigningInfo(ctx, sdk.ConsAddress(params.ConsAddress)) + if !found { + return nil, sdkerrors.Wrap(types.ErrNoSigningInfoFound, params.ConsAddress) + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, signingInfo) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QuerySigningInfosParams + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + var signingInfos []types.ValidatorSigningInfo + + k.IterateValidatorSigningInfos(ctx, func(consAddr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) { + signingInfos = append(signingInfos, info) + return false + }) + + start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx))) + if start < 0 || end < 0 { + signingInfos = []types.ValidatorSigningInfo{} + } else { + signingInfos = signingInfos[start:end] + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, signingInfos) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} diff --git a/x/slashing/keeper/querier_test.go b/x/slashing/keeper/querier_test.go new file mode 100644 index 000000000000..bda5fe4bc5f6 --- /dev/null +++ b/x/slashing/keeper/querier_test.go @@ -0,0 +1,56 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + 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" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +func TestNewQuerier(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.SlashingKeeper, legacyQuerierCdc.LegacyAmino) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + _, err := querier(ctx, []string{types.QueryParameters}, query) + require.NoError(t, err) +} + +func TestQueryParams(t *testing.T) { + cdc := codec.NewLegacyAmino() + legacyQuerierCdc := codec.NewAminoCodec(cdc) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) + + querier := keeper.NewQuerier(app.SlashingKeeper, legacyQuerierCdc.LegacyAmino) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + var params types.Params + + res, err := querier(ctx, []string{types.QueryParameters}, query) + require.NoError(t, err) + + err = cdc.UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + require.Equal(t, app.SlashingKeeper.GetParams(ctx), params) +} diff --git a/x/slashing/keeper/signing_info.go b/x/slashing/keeper/signing_info.go new file mode 100644 index 000000000000..0a4473d60e62 --- /dev/null +++ b/x/slashing/keeper/signing_info.go @@ -0,0 +1,158 @@ +package keeper + +import ( + "time" + + gogotypes "github.com/gogo/protobuf/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// GetValidatorSigningInfo retruns the ValidatorSigningInfo for a specific validator +// ConsAddress +func (k Keeper) GetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ValidatorSigningInfoKey(address)) + if bz == nil { + found = false + return + } + k.cdc.MustUnmarshalBinaryBare(bz, &info) + found = true + return +} + +// HasValidatorSigningInfo returns if a given validator has signing information +// persited. +func (k Keeper) HasValidatorSigningInfo(ctx sdk.Context, consAddr sdk.ConsAddress) bool { + _, ok := k.GetValidatorSigningInfo(ctx, consAddr) + return ok +} + +// SetValidatorSigningInfo sets the validator signing info to a consensus address key +func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info types.ValidatorSigningInfo) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(&info) + store.Set(types.ValidatorSigningInfoKey(address), bz) +} + +// IterateValidatorSigningInfos iterates over the stored ValidatorSigningInfo +func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context, + handler func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.ValidatorSigningInfoKeyPrefix) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + address := types.ValidatorSigningInfoAddress(iter.Key()) + var info types.ValidatorSigningInfo + k.cdc.MustUnmarshalBinaryBare(iter.Value(), &info) + if handler(address, info) { + break + } + } +} + +// GetValidatorMissedBlockBitArray gets the bit for the missed blocks array +func (k Keeper) GetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ValidatorMissedBlockBitArrayKey(address, index)) + var missed gogotypes.BoolValue + if bz == nil { + // lazy: treat empty key as not missed + return false + } + k.cdc.MustUnmarshalBinaryBare(bz, &missed) + + return missed.Value +} + +// IterateValidatorMissedBlockBitArray iterates over the signed blocks window +// and performs a callback function +func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context, + address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + index := int64(0) + // Array may be sparse + for ; index < k.SignedBlocksWindow(ctx); index++ { + var missed gogotypes.BoolValue + bz := store.Get(types.ValidatorMissedBlockBitArrayKey(address, index)) + if bz == nil { + continue + } + + k.cdc.MustUnmarshalBinaryBare(bz, &missed) + if handler(index, missed.Value) { + break + } + } +} + +// GetValidatorMissedBlocks returns array of missed blocks for given validator Cons address +func (k Keeper) GetValidatorMissedBlocks(ctx sdk.Context, address sdk.ConsAddress) []types.MissedBlock { + missedBlocks := []types.MissedBlock{} + k.IterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { + missedBlocks = append(missedBlocks, types.NewMissedBlock(index, missed)) + return false + }) + + return missedBlocks +} + +// JailUntil attempts to set a validator's JailedUntil attribute in its signing +// info. It will panic if the signing info does not exist for the validator. +func (k Keeper) JailUntil(ctx sdk.Context, consAddr sdk.ConsAddress, jailTime time.Time) { + signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) + if !ok { + panic("cannot jail validator that does not have any signing information") + } + + signInfo.JailedUntil = jailTime + k.SetValidatorSigningInfo(ctx, consAddr, signInfo) +} + +// Tombstone attempts to tombstone a validator. It will panic if signing info for +// the given validator does not exist. +func (k Keeper) Tombstone(ctx sdk.Context, consAddr sdk.ConsAddress) { + signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) + if !ok { + panic("cannot tombstone validator that does not have any signing information") + } + + if signInfo.Tombstoned { + panic("cannot tombstone validator that is already tombstoned") + } + + signInfo.Tombstoned = true + k.SetValidatorSigningInfo(ctx, consAddr, signInfo) +} + +// IsTombstoned returns if a given validator by consensus address is tombstoned. +func (k Keeper) IsTombstoned(ctx sdk.Context, consAddr sdk.ConsAddress) bool { + signInfo, ok := k.GetValidatorSigningInfo(ctx, consAddr) + if !ok { + return false + } + + return signInfo.Tombstoned +} + +// SetValidatorMissedBlockBitArray sets the bit that checks if the validator has +// missed a block in the current window +func (k Keeper) SetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(&gogotypes.BoolValue{Value: missed}) + store.Set(types.ValidatorMissedBlockBitArrayKey(address, index), bz) +} + +// clearValidatorMissedBlockBitArray deletes every instance of ValidatorMissedBlockBitArray in the store +func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.ValidatorMissedBlockBitArrayPrefixKey(address)) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } +} diff --git a/x/slashing/keeper/signing_info_test.go b/x/slashing/keeper/signing_info_test.go new file mode 100644 index 000000000000..1b70c83b9712 --- /dev/null +++ b/x/slashing/keeper/signing_info_test.go @@ -0,0 +1,96 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + 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/slashing/types" +) + +func TestGetSetValidatorSigningInfo(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.False(t, found) + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(addrDels[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, found) + require.Equal(t, info.StartHeight, int64(4)) + require.Equal(t, info.IndexOffset, int64(3)) + require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC()) + require.Equal(t, info.MissedBlocksCounter, int64(10)) +} + +func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0) + require.False(t, missed) // treat empty key as not missed + app.SlashingKeeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0, true) + missed = app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0) + require.True(t, missed) // now should be missed +} + +func TestTombstoned(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + require.Panics(t, func() { app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) }) + require.False(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(addrDels[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + + require.False(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + require.Panics(t, func() { app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) }) +} + +func TestJailUntil(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + require.Panics(t, func() { app.SlashingKeeper.JailUntil(ctx, sdk.ConsAddress(addrDels[0]), time.Now()) }) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(addrDels[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + app.SlashingKeeper.JailUntil(ctx, sdk.ConsAddress(addrDels[0]), time.Unix(253402300799, 0).UTC()) + + info, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, ok) + require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) +} diff --git a/x/slashing/keeper/unjail.go b/x/slashing/keeper/unjail.go new file mode 100644 index 000000000000..23a9121e5472 --- /dev/null +++ b/x/slashing/keeper/unjail.go @@ -0,0 +1,63 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// Unjail calls the staking Unjail function to unjail a validator if the +// jailed period has concluded +func (k Keeper) Unjail(ctx sdk.Context, validatorAddr sdk.ValAddress) error { + validator := k.sk.Validator(ctx, validatorAddr) + if validator == nil { + return types.ErrNoValidatorForAddress + } + + // cannot be unjailed if no self-delegation exists + selfDel := k.sk.Delegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + if selfDel == nil { + return types.ErrMissingSelfDelegation + } + + tokens := validator.TokensFromShares(selfDel.GetShares()).TruncateInt() + minSelfBond := validator.GetMinSelfDelegation() + if tokens.LT(minSelfBond) { + return sdkerrors.Wrapf( + types.ErrSelfDelegationTooLowToUnjail, "%s less than %s", tokens, minSelfBond, + ) + } + + // cannot be unjailed if not jailed + if !validator.IsJailed() { + return types.ErrValidatorNotJailed + } + + consAddr, err := validator.GetConsAddr() + if err != nil { + return err + } + // If the validator has a ValidatorSigningInfo object that signals that the + // validator was bonded and so we must check that the validator is not tombstoned + // and can be unjailed at the current block. + // + // A validator that is jailed but has no ValidatorSigningInfo object signals + // that the validator was never bonded and must've been jailed due to falling + // below their minimum self-delegation. The validator can unjail at any point + // assuming they've now bonded above their minimum self-delegation. + info, found := k.GetValidatorSigningInfo(ctx, consAddr) + if found { + // cannot be unjailed if tombstoned + if info.Tombstoned { + return types.ErrValidatorJailed + } + + // cannot be unjailed until out of jail + if ctx.BlockHeader().Time.Before(info.JailedUntil) { + return types.ErrValidatorJailed + } + } + + k.sk.Unjail(ctx, consAddr) + return nil +} diff --git a/x/slashing/legacy/v039/types.go b/x/slashing/legacy/v039/types.go new file mode 100644 index 000000000000..34eed337173b --- /dev/null +++ b/x/slashing/legacy/v039/types.go @@ -0,0 +1,79 @@ +package v039 + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "slashing" +) + +// Default parameter namespace +const ( + DefaultParamspace = ModuleName + DefaultSignedBlocksWindow = int64(100) + DefaultDowntimeJailDuration = 60 * 10 * time.Second +) + +var ( + DefaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) + DefaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) + DefaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) +) + +// Params - used for initializing default parameter for slashing at genesis +type Params struct { + SignedBlocksWindow int64 `json:"signed_blocks_window" yaml:"signed_blocks_window"` + MinSignedPerWindow sdk.Dec `json:"min_signed_per_window" yaml:"min_signed_per_window"` + DowntimeJailDuration time.Duration `json:"downtime_jail_duration" yaml:"downtime_jail_duration"` + SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign" yaml:"slash_fraction_double_sign"` + SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime" yaml:"slash_fraction_downtime"` +} + +// NewParams creates a new Params object +func NewParams( + signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration, + slashFractionDoubleSign, slashFractionDowntime sdk.Dec, +) Params { + + return Params{ + SignedBlocksWindow: signedBlocksWindow, + MinSignedPerWindow: minSignedPerWindow, + DowntimeJailDuration: downtimeJailDuration, + SlashFractionDoubleSign: slashFractionDoubleSign, + SlashFractionDowntime: slashFractionDowntime, + } +} + +// DefaultParams defines the parameters for this module +func DefaultParams() Params { + return NewParams( + DefaultSignedBlocksWindow, DefaultMinSignedPerWindow, DefaultDowntimeJailDuration, + DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime, + ) +} + +// ValidatorSigningInfo defines the signing info for a validator +type ValidatorSigningInfo struct { + Address sdk.ConsAddress `json:"address" yaml:"address"` // validator consensus address + StartHeight int64 `json:"start_height" yaml:"start_height"` // height at which validator was first a candidate OR was unjailed + IndexOffset int64 `json:"index_offset" yaml:"index_offset"` // index offset into signed block bit array + JailedUntil time.Time `json:"jailed_until" yaml:"jailed_until"` // timestamp validator cannot be unjailed until + Tombstoned bool `json:"tombstoned" yaml:"tombstoned"` // whether or not a validator has been tombstoned (killed out of validator set) + MissedBlocksCounter int64 `json:"missed_blocks_counter" yaml:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time) +} + +// MissedBlock +type MissedBlock struct { + Index int64 `json:"index" yaml:"index"` + Missed bool `json:"missed" yaml:"missed"` +} + +// GenesisState - all slashing state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` + SigningInfos map[string]ValidatorSigningInfo `json:"signing_infos" yaml:"signing_infos"` + MissedBlocks map[string][]MissedBlock `json:"missed_blocks" yaml:"missed_blocks"` +} diff --git a/x/slashing/legacy/v040/migrate.go b/x/slashing/legacy/v040/migrate.go new file mode 100644 index 000000000000..ba494dcad14a --- /dev/null +++ b/x/slashing/legacy/v040/migrate.go @@ -0,0 +1,64 @@ +package v040 + +import ( + "sort" + + v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" + v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// Migrate accepts exported x/slashing genesis state from v0.39 and migrates it +// to v0.40 x/slashing genesis state. The migration includes: +// +// - Chaning SigningInfos and MissedBlocks from map to array. +// - Convert addresses from bytes to bech32 strings. +// - Re-encode in v0.40 GenesisState. +func Migrate(oldGenState v039slashing.GenesisState) *v040slashing.GenesisState { + // Note that the two following `for` loop over a map's keys, so are not + // deterministic. + var newSigningInfos = make([]v040slashing.SigningInfo, 0, len(oldGenState.SigningInfos)) + for address, signingInfo := range oldGenState.SigningInfos { + newSigningInfos = append(newSigningInfos, v040slashing.SigningInfo{ + Address: address, + ValidatorSigningInfo: v040slashing.ValidatorSigningInfo{ + Address: signingInfo.Address.String(), + StartHeight: signingInfo.StartHeight, + IndexOffset: signingInfo.IndexOffset, + JailedUntil: signingInfo.JailedUntil, + Tombstoned: signingInfo.Tombstoned, + MissedBlocksCounter: signingInfo.MissedBlocksCounter, + }, + }) + } + var newValidatorMissedBlocks = make([]v040slashing.ValidatorMissedBlocks, 0, len(oldGenState.MissedBlocks)) + for address, validatorMissedBlocks := range oldGenState.MissedBlocks { + var newMissedBlocks = make([]v040slashing.MissedBlock, len(validatorMissedBlocks)) + for i, missedBlock := range validatorMissedBlocks { + newMissedBlocks[i] = v040slashing.MissedBlock{ + Index: missedBlock.Index, + Missed: missedBlock.Missed, + } + } + + newValidatorMissedBlocks = append(newValidatorMissedBlocks, v040slashing.ValidatorMissedBlocks{ + Address: address, + MissedBlocks: newMissedBlocks, + }) + } + + // We sort these two arrays by address, so that we get determinstic states. + sort.Slice(newSigningInfos, func(i, j int) bool { return newSigningInfos[i].Address < newSigningInfos[j].Address }) + sort.Slice(newValidatorMissedBlocks, func(i, j int) bool { return newValidatorMissedBlocks[i].Address < newValidatorMissedBlocks[j].Address }) + + return &v040slashing.GenesisState{ + Params: v040slashing.Params{ + SignedBlocksWindow: oldGenState.Params.SignedBlocksWindow, + MinSignedPerWindow: oldGenState.Params.MinSignedPerWindow, + DowntimeJailDuration: oldGenState.Params.DowntimeJailDuration, + SlashFractionDoubleSign: oldGenState.Params.SlashFractionDoubleSign, + SlashFractionDowntime: oldGenState.Params.SlashFractionDowntime, + }, + SigningInfos: newSigningInfos, + MissedBlocks: newValidatorMissedBlocks, + } +} diff --git a/x/slashing/legacy/v040/migrate_test.go b/x/slashing/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..016f3f4d44f3 --- /dev/null +++ b/x/slashing/legacy/v040/migrate_test.go @@ -0,0 +1,140 @@ +package v040_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" + v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + addr1, err := sdk.ConsAddressFromBech32("cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685") + require.NoError(t, err) + addr2, err := sdk.ConsAddressFromBech32("cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph") + require.NoError(t, err) + + gs := v039slashing.GenesisState{ + Params: v039slashing.DefaultParams(), + SigningInfos: map[string]v039slashing.ValidatorSigningInfo{ + "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph": { + Address: addr2, + IndexOffset: 615501, + MissedBlocksCounter: 1, + Tombstoned: false, + }, + "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685": { + Address: addr1, + IndexOffset: 2, + MissedBlocksCounter: 2, + Tombstoned: false, + }, + }, + MissedBlocks: map[string][]v039slashing.MissedBlock{ + "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph": { + { + Index: 2, + Missed: true, + }, + }, + "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685": { + { + Index: 3, + Missed: true, + }, + { + Index: 4, + Missed: true, + }, + }, + }, + } + + migrated := v040slashing.Migrate(gs) + // Check that in `signing_infos` and `missed_blocks`, the address + // cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685 + // should always come before the address + // cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph + // (in alphabetic order, basically). + expected := `{ + "missed_blocks": [ + { + "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", + "missed_blocks": [ + { + "index": "3", + "missed": true + }, + { + "index": "4", + "missed": true + } + ] + }, + { + "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", + "missed_blocks": [ + { + "index": "2", + "missed": true + } + ] + } + ], + "params": { + "downtime_jail_duration": "600s", + "min_signed_per_window": "0.500000000000000000", + "signed_blocks_window": "100", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": [ + { + "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", + "validator_signing_info": { + "address": "cosmosvalcons104cjmxkrg8y8lmrp25de02e4zf00zle4mzs685", + "index_offset": "2", + "jailed_until": "0001-01-01T00:00:00Z", + "missed_blocks_counter": "2", + "start_height": "0", + "tombstoned": false + } + }, + { + "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", + "validator_signing_info": { + "address": "cosmosvalcons10e4c5p6qk0sycy9u6u43t7csmlx9fyadr9yxph", + "index_offset": "615501", + "jailed_until": "0001-01-01T00:00:00Z", + "missed_blocks_counter": "1", + "start_height": "0", + "tombstoned": false + } + } + ] +}` + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + require.NoError(t, err) + indentedBz, err := json.MarshalIndent(jsonObj, "", " ") + require.NoError(t, err) + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/slashing/legacy/v040/types.go b/x/slashing/legacy/v040/types.go new file mode 100644 index 000000000000..b95fa6fe2445 --- /dev/null +++ b/x/slashing/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "slashing" +) diff --git a/x/slashing/module.go b/x/slashing/module.go index e99b421574fe..91ad472e90df 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -1,24 +1,29 @@ package slashing import ( + "context" "encoding/json" "fmt" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" + "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" "github.com/cosmos/cosmos-sdk/types/module" - sim "github.com/cosmos/cosmos-sdk/x/simulation" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) @@ -29,7 +34,9 @@ var ( ) // AppModuleBasic defines the basic application module used by the slashing module. -type AppModuleBasic struct{} +type AppModuleBasic struct { + cdc codec.Marshaler +} var _ module.AppModuleBasic = AppModuleBasic{} @@ -38,40 +45,50 @@ func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterCodec registers the slashing module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the slashing module's types for the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterInterfaces registers the module's interface types +func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } // DefaultGenesis returns default genesis state as raw bytes for the slashing // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the slashing module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return types.ValidateGenesis(data) } // RegisterRESTRoutes registers the REST routes for the slashing module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { + rest.RegisterHandlers(clientCtx, rtr) +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the slashig module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) } // GetTxCmd returns the root tx command for the slashing module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(cdc) +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() } // GetQueryCmd returns no root query command for the slashing module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(StoreKey, cdc) +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() } //____________________________________________________________________________ @@ -80,63 +97,66 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper accountKeeper types.AccountKeeper + bankKeeper types.BankKeeper stakingKeeper stakingkeeper.Keeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper, stakingKeeper stakingkeeper.Keeper) AppModule { +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper, sk stakingkeeper.Keeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, - accountKeeper: accountKeeper, - stakingKeeper: stakingKeeper, + accountKeeper: ak, + bankKeeper: bk, + stakingKeeper: sk, } } // Name returns the slashing module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers the slashing module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route returns the message routing key for the slashing module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the slashing module. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(am.keeper) +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) } // QuerierRoute returns the slashing module's querier route name. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute +} + +// LegacyQuerierHandler returns the slashing module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } -// NewQuerierHandler returns the slashing module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) } // InitGenesis performs genesis initialization for the slashing module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.stakingKeeper, genesisState) +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, am.stakingKeeper, &genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the slashing // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) + return cdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the slashing module. @@ -160,22 +180,24 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return nil } // RandomizedParams creates randomized slashing param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for slashing module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) } // WeightedOperations returns the all the slashing module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { - return simulation.WeightedOperations(simState.AppParams, simState.Cdc, - am.accountKeeper, am.keeper, am.stakingKeeper) +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, + am.accountKeeper, am.bankKeeper, am.keeper, am.stakingKeeper, + ) } diff --git a/x/slashing/simulation/decoder.go b/x/slashing/simulation/decoder.go index 9aea5f701b15..b0ebb64ef31a 100644 --- a/x/slashing/simulation/decoder.go +++ b/x/slashing/simulation/decoder.go @@ -4,38 +4,38 @@ import ( "bytes" "fmt" - "github.com/tendermint/tendermint/crypto" - tmkv "github.com/tendermint/tendermint/libs/kv" + gogotypes "github.com/gogo/protobuf/types" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding slashing type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.ValidatorSigningInfoKey): - var infoA, infoB types.ValidatorSigningInfo - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &infoA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &infoB) - return fmt.Sprintf("%v\n%v", infoA, infoB) +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding slashing 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.ValidatorSigningInfoKeyPrefix): + var infoA, infoB types.ValidatorSigningInfo + cdc.MustUnmarshalBinaryBare(kvA.Value, &infoA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &infoB) + return fmt.Sprintf("%v\n%v", infoA, infoB) - case bytes.Equal(kvA.Key[:1], types.ValidatorMissedBlockBitArrayKey): - var missedA, missedB bool - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &missedA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &missedB) - return fmt.Sprintf("missedA: %v\nmissedB: %v", missedA, missedB) + case bytes.Equal(kvA.Key[:1], types.ValidatorMissedBlockBitArrayKeyPrefix): + var missedA, missedB gogotypes.BoolValue + cdc.MustUnmarshalBinaryBare(kvA.Value, &missedA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &missedB) + return fmt.Sprintf("missedA: %v\nmissedB: %v", missedA.Value, missedB.Value) - case bytes.Equal(kvA.Key[:1], types.AddrPubkeyRelationKey): - var pubKeyA, pubKeyB crypto.PubKey - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &pubKeyA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &pubKeyB) - bechPKA := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKeyA) - bechPKB := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKeyB) - return fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPKA, bechPKB) + case bytes.Equal(kvA.Key[:1], types.AddrPubkeyRelationKeyPrefix): + var pubKeyA, pubKeyB gogotypes.StringValue + cdc.MustUnmarshalBinaryBare(kvA.Value, &pubKeyA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &pubKeyB) + return fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", pubKeyA.Value, pubKeyB.Value) - default: - panic(fmt.Sprintf("invalid slashing key prefix %X", kvA.Key[:1])) + default: + panic(fmt.Sprintf("invalid slashing key prefix %X", kvA.Key[:1])) + } } } diff --git a/x/slashing/simulation/decoder_test.go b/x/slashing/simulation/decoder_test.go index acd3be67c62f..e6122f0e0a98 100644 --- a/x/slashing/simulation/decoder_test.go +++ b/x/slashing/simulation/decoder_test.go @@ -1,18 +1,19 @@ -package simulation +package simulation_test import ( "fmt" "testing" "time" + gogotypes "github.com/gogo/protobuf/types" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" + "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/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/types/kv" + "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // nolint:deadcode,unused,varcheck @@ -23,26 +24,21 @@ var ( consAddr1 = sdk.ConsAddress(delPk1.Address().Bytes()) ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) - return -} - func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) info := types.NewValidatorSigningInfo(consAddr1, 0, 1, time.Now().UTC(), false, 0) - bechPK := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, delPk1) - missed := true + bechPK := sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, delPk1) + missed := gogotypes.BoolValue{Value: true} - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.GetValidatorSigningInfoKey(consAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(info)}, - tmkv.Pair{Key: types.GetValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryLengthPrefixed(missed)}, - tmkv.Pair{Key: types.GetAddrPubkeyRelationKey(delAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(delPk1)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: types.ValidatorSigningInfoKey(consAddr1), Value: cdc.MustMarshalBinaryBare(&info)}, + {Key: types.ValidatorMissedBlockBitArrayKey(consAddr1, 6), Value: cdc.MustMarshalBinaryBare(&missed)}, + {Key: types.AddrPubkeyRelationKey(delAddr1), Value: cdc.MustMarshalBinaryBare(&gogotypes.StringValue{Value: bechPK})}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, } tests := []struct { @@ -50,7 +46,7 @@ func TestDecodeStore(t *testing.T) { expectedLog string }{ {"ValidatorSigningInfo", fmt.Sprintf("%v\n%v", info, info)}, - {"ValidatorMissedBlockBitArray", fmt.Sprintf("missedA: %v\nmissedB: %v", missed, missed)}, + {"ValidatorMissedBlockBitArray", fmt.Sprintf("missedA: %v\nmissedB: %v", missed.Value, missed.Value)}, {"AddrPubkeyRelation", fmt.Sprintf("PubKeyA: %s\nPubKeyB: %s", bechPK, bechPK)}, {"other", ""}, } @@ -59,9 +55,9 @@ func TestDecodeStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/slashing/simulation/genesis.go b/x/slashing/simulation/genesis.go index a26a626642b7..2c1eba7db73a 100644 --- a/x/slashing/simulation/genesis.go +++ b/x/slashing/simulation/genesis.go @@ -3,16 +3,15 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" "time" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) // Simulation parameter constants @@ -86,8 +85,12 @@ func RandomizedGenState(simState *module.SimulationState) { slashFractionDoubleSign, slashFractionDowntime, ) - slashingGenesis := types.NewGenesisState(params, nil, nil) + slashingGenesis := types.NewGenesisState(params, []types.SigningInfo{}, []types.ValidatorMissedBlocks{}) - fmt.Printf("Selected randomly generated slashing parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, slashingGenesis.Params)) + bz, err := json.MarshalIndent(&slashingGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated slashing parameters:\n%s\n", bz) simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(slashingGenesis) } diff --git a/x/slashing/simulation/genesis_test.go b/x/slashing/simulation/genesis_test.go new file mode 100644 index 000000000000..a386588d8369 --- /dev/null +++ b/x/slashing/simulation/genesis_test.go @@ -0,0 +1,84 @@ +package simulation_test + +import ( + "encoding/json" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +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 slashingGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &slashingGenesis) + + dec1, _ := sdk.NewDecFromStr("0.600000000000000000") + dec2, _ := sdk.NewDecFromStr("0.022222222222222222") + dec3, _ := sdk.NewDecFromStr("0.008928571428571429") + + require.Equal(t, dec1, slashingGenesis.Params.MinSignedPerWindow) + require.Equal(t, dec2, slashingGenesis.Params.SlashFractionDoubleSign) + require.Equal(t, dec3, slashingGenesis.Params.SlashFractionDowntime) + require.Equal(t, int64(720), slashingGenesis.Params.SignedBlocksWindow) + require.Equal(t, time.Duration(34800000000000), slashingGenesis.Params.DowntimeJailDuration) + require.Len(t, slashingGenesis.MissedBlocks, 0) + require.Len(t, slashingGenesis.SigningInfos, 0) + +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "assignment to entry in nil map"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index f5e263b778dc..20acc9e91a88 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -9,9 +9,10 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/helpers" 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/simulation" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) @@ -22,8 +23,8 @@ const ( // WeightedOperations returns all the operations from the module with their respective weights func WeightedOperations( - appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, - k keeper.Keeper, sk stakingkeeper.Keeper, + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, + bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper, ) simulation.WeightedOperations { var weightMsgUnjail int @@ -36,54 +37,61 @@ func WeightedOperations( return simulation.WeightedOperations{ simulation.NewWeightedOperation( weightMsgUnjail, - SimulateMsgUnjail(ak, k, sk), + SimulateMsgUnjail(ak, bk, k, sk), ), } } // SimulateMsgUnjail generates a MsgUnjail with random values -// nolint: funlen -func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgUnjail(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { validator, ok := stakingkeeper.RandomValidator(r, sk, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "validator is not ok"), nil, nil // skip } - simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(validator.GetOperator())) + simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(validator.GetOperator())) if !found { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "unable to find account"), nil, nil // skip } if !validator.IsJailed() { // TODO: due to this condition this message is almost, if not always, skipped ! - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "validator is not jailed"), nil, nil } - consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address()) + consAddr, err := validator.GetConsAddr() + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "unable to get validator consensus key"), nil, err + } info, found := k.GetValidatorSigningInfo(ctx, consAddr) if !found { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "unable to find validator signing info"), nil, nil // skip } selfDel := sk.Delegation(ctx, simAccount.Address, validator.GetOperator()) if selfDel == nil { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "self delegation is nil"), nil, nil // skip } account := ak.GetAccount(ctx, sdk.AccAddress(validator.GetOperator())) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUnjail, "unable to generate fees"), nil, err } msg := types.NewMsgUnjail(validator.GetOperator()) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -92,8 +100,11 @@ func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper []uint64{account.GetSequence()}, simAccount.PrivKey, ) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate mock tx"), nil, err + } - _, res, err := app.Deliver(tx) + _, res, err := app.Deliver(txGen.TxEncoder(), tx) // result should fail if: // - validator cannot be unjailed due to tombstone @@ -104,23 +115,23 @@ func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { if res != nil && err == nil { if info.Tombstoned { - return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator should not have been unjailed if validator tombstoned") + return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator should not have been unjailed if validator tombstoned") } if ctx.BlockHeader().Time.Before(info.JailedUntil) { - return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed while validator still in jail period") + return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed while validator still in jail period") } if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { - return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed even though self-delegation too low") + return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed even though self-delegation too low") } } // msg failed as expected - return simulation.NewOperationMsg(msg, false, ""), nil, nil + return simtypes.NewOperationMsg(msg, false, ""), nil, nil } if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log) + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, errors.New(res.Log) } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } diff --git a/x/slashing/simulation/operations_test.go b/x/slashing/simulation/operations_test.go new file mode 100644 index 000000000000..15764c631ebf --- /dev/null +++ b/x/slashing/simulation/operations_test.go @@ -0,0 +1,153 @@ +package simulation_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "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" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/x/slashing/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TestWeightedOperations tests the weights of the operations. +func TestWeightedOperations(t *testing.T) { + app, ctx := createTestApp(false) + ctx.WithChainID("test-chain") + + cdc := app.AppCodec() + appParams := make(simtypes.AppParams) + + s := rand.NewSource(1) + r := rand.New(s) + accs := simtypes.RandomAccounts(r, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{{simappparams.DefaultWeightMsgUnjail, types.ModuleName, types.TypeMsgUnjail}} + + weightesOps := simulation.WeightedOperations(appParams, cdc, app.AccountKeeper, app.BankKeeper, app.SlashingKeeper, app.StakingKeeper) + 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(t, expected[i].weight, w.Weight(), "weight should be the same") + require.Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + require.Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +// TestSimulateMsgUnjail tests the normal scenario of a valid message of type types.MsgUnjail. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func TestSimulateMsgUnjail(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 accounts[0] as validator0 + validator0 := getTestingValidator0(t, app, ctx, accounts) + + // setup validator0 by consensus address + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator0) + val0ConsAddress, err := validator0.GetConsAddr() + require.NoError(t, err) + info := types.NewValidatorSigningInfo(val0ConsAddress, int64(4), int64(3), + time.Unix(2, 0), false, int64(10)) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, val0ConsAddress, info) + + // put validator0 in jail + app.StakingKeeper.Jail(ctx, val0ConsAddress) + + // setup self delegation + delTokens := sdk.TokensFromConsensusPower(2) + validator0, issuedShares := validator0.AddTokensFromDel(delTokens) + val0AccAddress, err := sdk.ValAddressFromBech32(validator0.OperatorAddress) + require.NoError(t, err) + selfDelegation := stakingtypes.NewDelegation(val0AccAddress.Bytes(), validator0.GetOperator(), issuedShares) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) + app.DistrKeeper.SetDelegatorStartingInfo(ctx, validator0.GetOperator(), val0AccAddress.Bytes(), distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) + + // 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.SimulateMsgUnjail(app.AccountKeeper, app.BankKeeper, app.SlashingKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgUnjail + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, types.TypeMsgUnjail, msg.Type()) + require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddr) + require.Len(t, futureOperations, 0) +} + +// returns context and an app with updated mint keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.MintKeeper.SetParams(ctx, minttypes.DefaultParams()) + app.MintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) + + return app, ctx +} + +func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk.Context, 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 { + acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) + app.AccountKeeper.SetAccount(ctx, acc) + err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) + require.NoError(t, err) + } + + return accounts +} + +func getTestingValidator0(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account) stakingtypes.Validator { + commission0 := stakingtypes.NewCommission(sdk.ZeroDec(), sdk.OneDec(), sdk.OneDec()) + return getTestingValidator(t, app, ctx, accounts, commission0, 0) +} + +func getTestingValidator(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account, commission stakingtypes.Commission, n int) stakingtypes.Validator { + account := accounts[n] + valPubKey := account.ConsKey.PubKey() + valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) + validator, err := stakingtypes.NewValidator(valAddr, valPubKey, stakingtypes.Description{}) + require.NoError(t, err) + validator, err = validator.SetInitialCommission(commission) + require.NoError(t, err) + + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.NewInt(1000000) + + app.StakingKeeper.SetValidator(ctx, validator) + + return validator +} diff --git a/x/slashing/simulation/params.go b/x/slashing/simulation/params.go index 6679928d8fc4..e7c8fcf491b0 100644 --- a/x/slashing/simulation/params.go +++ b/x/slashing/simulation/params.go @@ -6,8 +6,9 @@ import ( "fmt" "math/rand" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" ) const ( @@ -18,8 +19,8 @@ const ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, keySignedBlocksWindow, func(r *rand.Rand) string { return fmt.Sprintf("\"%d\"", GenSignedBlocksWindow(r)) diff --git a/x/slashing/simulation/params_test.go b/x/slashing/simulation/params_test.go new file mode 100644 index 000000000000..2f1933c60ad2 --- /dev/null +++ b/x/slashing/simulation/params_test.go @@ -0,0 +1,37 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/slashing/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"slashing/SignedBlocksWindow", "SignedBlocksWindow", "\"231\"", "slashing"}, + {"slashing/MinSignedPerWindow", "MinSignedPerWindow", "\"0.700000000000000000\"", "slashing"}, + {"slashing/SlashFractionDowntime", "SlashFractionDowntime", "\"0.020833333333333333\"", "slashing"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 3) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/slashing/spec/01_concepts.md b/x/slashing/spec/01_concepts.md index b38a6edbed8c..9505706f90d3 100644 --- a/x/slashing/spec/01_concepts.md +++ b/x/slashing/spec/01_concepts.md @@ -8,8 +8,8 @@ order: 1 At any given time, there are any number of validators registered in the state machine. Each block, the top `MaxValidators` (defined by `x/staking`) validators -who are not jailed become *bonded*, meaning that they may propose and vote on -blocks. Validators who are *bonded* are *at stake*, meaning that part or all of +who are not jailed become _bonded_, meaning that they may propose and vote on +blocks. Validators who are _bonded_ are _at stake_, meaning that part or all of their stake and their delegators' stake is at risk if they commit a protocol fault. For each of these validators we keep a `ValidatorSigningInfo` record that contains @@ -20,26 +20,26 @@ attributes. In order to mitigate the impact of initially likely categories of non-malicious protocol faults, the Cosmos Hub implements for each validator -a *tombstone* cap, which only allows a validator to be slashed once for a double +a _tombstone_ cap, which only allows a validator to be slashed once for a double sign fault. For example, if you misconfigure your HSM and double-sign a bunch of old blocks, you'll only be punished for the first double-sign (and then immediately tombstombed). This will still be quite expensive and desirable to avoid, but tombstone caps somewhat blunt the economic impact of unintentional misconfiguration. -Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between. +Liveness faults do not have caps, as they can't stack upon each other. Liveness bugs are "detected" as soon as the infraction occurs, and the validators are immediately put in jail, so it is not possible for them to commit multiple liveness faults without unjailing in between. ## Infraction Timelines To illustrate how the `x/slashing` module handles submitted evidence through Tendermint consensus, consider the following examples: -__Definitions__: +**Definitions**: -*[* : timeline start -*]* : timeline end -*Cn* : infraction `n` committed -*Dn* : infraction `n` discovered -*Vb* : validator bonded -*Vu* : validator unbonded +_[_ : timeline start +_]_ : timeline end +_Cn_ : infraction `n` committed +_Dn_ : infraction `n` discovered +_Vb_ : validator bonded +_Vu_ : validator unbonded ### Single Double Sign Infraction diff --git a/x/slashing/spec/02_state.md b/x/slashing/spec/02_state.md index 867fa7998374..17931ce866a0 100644 --- a/x/slashing/spec/02_state.md +++ b/x/slashing/spec/02_state.md @@ -40,27 +40,35 @@ bonded validator. The `SignedBlocksWindow` parameter defines the size The information stored for tracking validator liveness is as follows: -```go -type ValidatorSigningInfo struct { - Address sdk.ConsAddress - StartHeight int64 - IndexOffset int64 - JailedUntil time.Time - Tombstoned bool - MissedBlocksCounter int64 +```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 +- **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 +- **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 +- **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 +- **MissedBlocksCounter**: A counter kept to avoid unnecessary array reads. Note that `Sum(MissedBlocksBitArray)` equals `MissedBlocksCounter` always. diff --git a/x/slashing/spec/03_messages.md b/x/slashing/spec/03_messages.md index d7825bfd0b8f..6e7d168ffdb5 100644 --- a/x/slashing/spec/03_messages.md +++ b/x/slashing/spec/03_messages.md @@ -9,15 +9,21 @@ In this section we describe the processing of messages for the `slashing` module ## Unjail If a validator was automatically unbonded due to downtime and wishes to come back online & -possibly rejoin the bonded set, it must send `TxUnjail`: - -``` -type TxUnjail struct { - ValidatorAddr sdk.AccAddress +possibly rejoin the bonded set, it must send `MsgUnjail`: + +```protobuf +// MsgUnjail is an sdk.Msg used for unjailing a jailed validator, thus returning +// them into the bonded validator set, so they can begin receiving provisions +// and rewards again. +message MsgUnjail { + string validator_addr = 1; } +``` -handleMsgUnjail(tx TxUnjail) +And below is its corresponding handler: +``` +handleMsgUnjail(tx MsgUnjail) validator = getValidator(tx.ValidatorAddr) if validator == nil fail with "No validator found" diff --git a/x/slashing/spec/04_begin_block.md b/x/slashing/spec/04_begin_block.md index 96d1217f1945..99572c419f20 100644 --- a/x/slashing/spec/04_begin_block.md +++ b/x/slashing/spec/04_begin_block.md @@ -23,7 +23,7 @@ greater than `minHeight` and the validator's `MissedBlocksCounter` is greater th for `DowntimeJailDuration`, and have the following values reset: `MissedBlocksBitArray`, `MissedBlocksCounter`, and `IndexOffset`. -__Note__: Liveness slashes do **NOT** lead to a tombstombing. +**Note**: Liveness slashes do **NOT** lead to a tombstombing. ```go height := block.Height diff --git a/x/slashing/spec/05_hooks.md b/x/slashing/spec/05_hooks.md index adb8da39b01f..8f78cdff01b0 100644 --- a/x/slashing/spec/05_hooks.md +++ b/x/slashing/spec/05_hooks.md @@ -25,6 +25,6 @@ onValidatorBonded(address sdk.ValAddress) } setValidatorSigningInfo(signingInfo) } - + return ``` diff --git a/x/slashing/spec/07_tombstone.md b/x/slashing/spec/07_tombstone.md index a062278cec9d..4759a89b7190 100644 --- a/x/slashing/spec/07_tombstone.md +++ b/x/slashing/spec/07_tombstone.md @@ -15,18 +15,18 @@ and evidence of the infraction reaching the state machine (this is one of the primary reasons for the existence of the unbonding period). > Note: The tombstone concept, only applies to faults that have a delay between -the infraction occurring and evidence reaching the state machine. For example, -evidence of a validator double signing may take a while to reach the state machine -due to unpredictable evidence gossip layer delays and the ability of validators to -selectively reveal double-signatures (e.g. to infrequently-online light clients). -Liveness slashing, on the other hand, is detected immediately as soon as the -infraction occurs, and therefore no slashing period is needed. A validator is -immediately put into jail period, and they cannot commit another liveness fault -until they unjail. In the future, there may be other types of byzantine faults -that have delays (for example, submitting evidence of an invalid proposal as a transaction). -When implemented, it will have to be decided whether these future types of -byzantine faults will result in a tombstoning (and if not, the slash amounts -will not be capped by a slashing period). +> the infraction occurring and evidence reaching the state machine. For example, +> evidence of a validator double signing may take a while to reach the state machine +> due to unpredictable evidence gossip layer delays and the ability of validators to +> selectively reveal double-signatures (e.g. to infrequently-online light clients). +> Liveness slashing, on the other hand, is detected immediately as soon as the +> infraction occurs, and therefore no slashing period is needed. A validator is +> immediately put into jail period, and they cannot commit another liveness fault +> until they unjail. In the future, there may be other types of byzantine faults +> that have delays (for example, submitting evidence of an invalid proposal as a transaction). +> When implemented, it will have to be decided whether these future types of +> byzantine faults will result in a tombstoning (and if not, the slash amounts +> will not be capped by a slashing period). In the current system design, once a validator is put in the jail for a consensus fault, after the `JailPeriod` they are allowed to send a transaction to `unjail` @@ -72,10 +72,10 @@ As the number of slashing periods increase, it creates more complexity as we hav to keep track of the highest infraction amount for every single slashing period. > Note: Currently, according to the `slashing` module spec, a new slashing period -is created every time a validator is unbonded then rebonded. This should probably -be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) -for further details. For the remainder of this, I will assume that we only start -a new slashing period when a validator gets unjailed. +> is created every time a validator is unbonded then rebonded. This should probably +> be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) +> for further details. For the remainder of this, I will assume that we only start +> a new slashing period when a validator gets unjailed. The maximum number of slashing periods is the `len(UnbondingPeriod) / len(JailPeriod)`. The current defaults in Gaia for the `UnbondingPeriod` and `JailPeriod` are 3 weeks @@ -85,7 +85,7 @@ we only have to track 1 slashing period (i.e not have to track slashing periods) 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 +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 validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). @@ -93,7 +93,7 @@ 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 +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. @@ -106,7 +106,7 @@ of the hooks defined in the `slashing` module consumed by the `staking` module Another optimization that can be made is that if we assume that all ABCI faults for Tendermint consensus are slashed at the same level, we don't have to keep -track of "max slash". Once an ABCI fault happens, we don't have to worry about +track of "max slash". Once an ABCI fault happens, we don't have to worry about comparing potential future ones to find the max. Currently the only Tendermint ABCI fault is: @@ -121,5 +121,5 @@ Given that these faults are both attributable byzantine faults, we will likely want to slash them equally, and thus we can enact the above change. > Note: This change may make sense for current Tendermint consensus, but maybe -not for a different consensus algorithm or future versions of Tendermint that -may want to punish at different levels (for example, partial slashing). +> not for a different consensus algorithm or future versions of Tendermint that +> may want to punish at different levels (for example, partial slashing). diff --git a/x/slashing/spec/README.md b/x/slashing/spec/README.md index 7f694d03d36f..226306562333 100644 --- a/x/slashing/spec/README.md +++ b/x/slashing/spec/README.md @@ -5,7 +5,7 @@ parent: title: "slashing" --> -# `slashing` +# `x/slashing` ## Abstract @@ -25,21 +25,21 @@ This module will be used by the Cosmos Hub, the first hub in the Cosmos ecosyste ## Contents 1. **[Concepts](01_concepts.md)** - - [States](01_concepts.md#states) - - [Tombstone Caps](01_concepts.md#tombstone-caps) - - [ASCII timelines](01_concepts.md#ascii-timelines) + - [States](01_concepts.md#states) + - [Tombstone Caps](01_concepts.md#tombstone-caps) + - [ASCII timelines](01_concepts.md#ascii-timelines) 2. **[State](02_state.md)** - - [Signing Info](02_state.md#signing-info) + - [Signing Info](02_state.md#signing-info) 3. **[Messages](03_messages.md)** - - [Unjail](03_messages.md#unjail) + - [Unjail](03_messages.md#unjail) 4. **[Begin-Block](04_begin_block.md)** - - [Evidence handling](04_begin_block.md#evidence-handling) - - [Uptime tracking](04_begin_block.md#uptime-tracking) + - [Evidence handling](04_begin_block.md#evidence-handling) + - [Uptime tracking](04_begin_block.md#uptime-tracking) 5. **[05_hooks.md](05_hooks.md)** - - [Hooks](05_hooks.md#hooks) + - [Hooks](05_hooks.md#hooks) 6. **[Events](06_events.md)** - - [BeginBlocker](06_events.md#beginblocker) - - [Handlers](06_events.md#handlers) + - [BeginBlocker](06_events.md#beginblocker) + - [Handlers](06_events.md#handlers) 7. **[Staking Tombstone](07_tombstone.md)** - - [Abstract](07_tombstone.md#abstract) + - [Abstract](07_tombstone.md#abstract) 8. **[Parameters](08_params.md)** diff --git a/x/slashing/testslashing/params.go b/x/slashing/testslashing/params.go new file mode 100644 index 000000000000..dac3f23c5c5e --- /dev/null +++ b/x/slashing/testslashing/params.go @@ -0,0 +1,16 @@ +package testslashing + +import ( + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +// TestParams construct default slashing params for tests. +// Have to change these parameters for tests +// lest the tests take forever +func TestParams() types.Params { + params := types.DefaultParams() + params.SignedBlocksWindow = 1000 + params.DowntimeJailDuration = 60 * 60 + + return params +} diff --git a/x/slashing/types/codec.go b/x/slashing/types/codec.go new file mode 100644 index 000000000000..bc89862f1b46 --- /dev/null +++ b/x/slashing/types/codec.go @@ -0,0 +1,40 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterLegacyAminoCodec registers concrete types on LegacyAmino codec +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgUnjail{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/slashing module codec. Note, the codec + // should ONLY be used in certain instances of tests and for JSON encoding as Amino + // is still used for that purpose. + // + // The actual codec used for serialization should be provided to x/slashing and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() +} diff --git a/x/slashing/types/errors.go b/x/slashing/types/errors.go new file mode 100644 index 000000000000..daff6ecc04af --- /dev/null +++ b/x/slashing/types/errors.go @@ -0,0 +1,16 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// x/slashing module sentinel errors +var ( + ErrNoValidatorForAddress = sdkerrors.Register(ModuleName, 2, "address is not associated with any known validator") + ErrBadValidatorAddr = sdkerrors.Register(ModuleName, 3, "validator does not exist for that address") + ErrValidatorJailed = sdkerrors.Register(ModuleName, 4, "validator still jailed; cannot be unjailed") + ErrValidatorNotJailed = sdkerrors.Register(ModuleName, 5, "validator not jailed; cannot be unjailed") + ErrMissingSelfDelegation = sdkerrors.Register(ModuleName, 6, "validator has no self-delegation; cannot be unjailed") + ErrSelfDelegationTooLowToUnjail = sdkerrors.Register(ModuleName, 7, "validator's self delegation less than minimum; cannot be unjailed") + ErrNoSigningInfoFound = sdkerrors.Register(ModuleName, 8, "no validator signing info found") +) diff --git a/x/slashing/internal/types/events.go b/x/slashing/types/events.go similarity index 100% rename from x/slashing/internal/types/events.go rename to x/slashing/types/events.go diff --git a/x/slashing/types/expected_keepers.go b/x/slashing/types/expected_keepers.go new file mode 100644 index 000000000000..538a030baac0 --- /dev/null +++ b/x/slashing/types/expected_keepers.go @@ -0,0 +1,64 @@ +// noalias +// DONTCOVER +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// AccountKeeper expected account keeper +type AccountKeeper interface { + GetAccount(ctx sdk.Context, addr sdk.AccAddress) auth.AccountI + IterateAccounts(ctx sdk.Context, process func(auth.AccountI) (stop bool)) +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +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 +} + +// ParamSubspace defines the expected Subspace interfacace +type ParamSubspace interface { + HasKeyTable() bool + WithKeyTable(table paramtypes.KeyTable) paramtypes.Subspace + Get(ctx sdk.Context, key []byte, ptr interface{}) + GetParamSet(ctx sdk.Context, ps paramtypes.ParamSet) + SetParamSet(ctx sdk.Context, ps paramtypes.ParamSet) +} + +// StakingKeeper expected staking keeper +type StakingKeeper interface { + // iterate through validators by operator address, execute func for each validator + IterateValidators(sdk.Context, + func(index int64, validator stakingtypes.ValidatorI) (stop bool)) + + Validator(sdk.Context, sdk.ValAddress) stakingtypes.ValidatorI // get a particular validator by operator address + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingtypes.ValidatorI // get a particular validator by consensus address + + // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction + Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) + Jail(sdk.Context, sdk.ConsAddress) // jail a validator + Unjail(sdk.Context, sdk.ConsAddress) // unjail a validator + + // Delegation allows for getting a particular delegation for a given validator + // and delegator outside the scope of the staking module. + Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingtypes.DelegationI + + // MaxValidators returns the maximum amount of bonded validators + MaxValidators(sdk.Context) uint32 +} + +// StakingHooks event hooks for staking validator object (noalias) +type StakingHooks interface { + AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created + AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted + + AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is bonded +} diff --git a/x/slashing/types/genesis.go b/x/slashing/types/genesis.go new file mode 100644 index 000000000000..ee765c8c37e6 --- /dev/null +++ b/x/slashing/types/genesis.go @@ -0,0 +1,67 @@ +package types + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewGenesisState creates a new GenesisState object +func NewGenesisState( + params Params, signingInfos []SigningInfo, missedBlocks []ValidatorMissedBlocks, +) *GenesisState { + + return &GenesisState{ + Params: params, + SigningInfos: signingInfos, + MissedBlocks: missedBlocks, + } +} + +// NewMissedBlock creates a new MissedBlock instance +func NewMissedBlock(index int64, missed bool) MissedBlock { + return MissedBlock{ + Index: index, + Missed: missed, + } +} + +// DefaultGenesisState - default GenesisState used by Cosmos Hub +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + SigningInfos: []SigningInfo{}, + MissedBlocks: []ValidatorMissedBlocks{}, + } +} + +// ValidateGenesis validates the slashing genesis parameters +func ValidateGenesis(data GenesisState) error { + downtime := data.Params.SlashFractionDowntime + if downtime.IsNegative() || downtime.GT(sdk.OneDec()) { + return fmt.Errorf("slashing fraction downtime should be less than or equal to one and greater than zero, is %s", downtime.String()) + } + + dblSign := data.Params.SlashFractionDoubleSign + if dblSign.IsNegative() || dblSign.GT(sdk.OneDec()) { + return fmt.Errorf("slashing fraction double sign should be less than or equal to one and greater than zero, is %s", dblSign.String()) + } + + minSign := data.Params.MinSignedPerWindow + if minSign.IsNegative() || minSign.GT(sdk.OneDec()) { + return fmt.Errorf("min signed per window should be less than or equal to one and greater than zero, is %s", minSign.String()) + } + + downtimeJail := data.Params.DowntimeJailDuration + if downtimeJail < 1*time.Minute { + return fmt.Errorf("downtime unblond duration must be at least 1 minute, is %s", downtimeJail.String()) + } + + signedWindow := data.Params.SignedBlocksWindow + if signedWindow < 10 { + return fmt.Errorf("signed blocks window must be at least 10, is %d", signedWindow) + } + + return nil +} diff --git a/x/slashing/types/genesis.pb.go b/x/slashing/types/genesis.pb.go new file mode 100644 index 000000000000..9819899d095e --- /dev/null +++ b/x/slashing/types/genesis.pb.go @@ -0,0 +1,1126 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/slashing/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 defines the slashing module's genesis state. +type GenesisState struct { + // params defines all the paramaters of related to deposit. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // signing_infos represents a map between validator addresses and their + // signing infos. + SigningInfos []SigningInfo `protobuf:"bytes,2,rep,name=signing_infos,json=signingInfos,proto3" json:"signing_infos" yaml:"signing_infos"` + // signing_infos represents a map between validator addresses and their + // missed blocks. + MissedBlocks []ValidatorMissedBlocks `protobuf:"bytes,3,rep,name=missed_blocks,json=missedBlocks,proto3" json:"missed_blocks" yaml:"missed_blocks"` +} + +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_1923b9188b635394, []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) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetSigningInfos() []SigningInfo { + if m != nil { + return m.SigningInfos + } + return nil +} + +func (m *GenesisState) GetMissedBlocks() []ValidatorMissedBlocks { + if m != nil { + return m.MissedBlocks + } + return nil +} + +// SigningInfo stores validator signing info of corresponding address. +type SigningInfo struct { + // address is the validator address. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // validator_signing_info represents the signing info of this validator. + ValidatorSigningInfo ValidatorSigningInfo `protobuf:"bytes,2,opt,name=validator_signing_info,json=validatorSigningInfo,proto3" json:"validator_signing_info" yaml:"validator_signing_info"` +} + +func (m *SigningInfo) Reset() { *m = SigningInfo{} } +func (m *SigningInfo) String() string { return proto.CompactTextString(m) } +func (*SigningInfo) ProtoMessage() {} +func (*SigningInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1923b9188b635394, []int{1} +} +func (m *SigningInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SigningInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SigningInfo.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 *SigningInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SigningInfo.Merge(m, src) +} +func (m *SigningInfo) XXX_Size() int { + return m.Size() +} +func (m *SigningInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SigningInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SigningInfo proto.InternalMessageInfo + +func (m *SigningInfo) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *SigningInfo) GetValidatorSigningInfo() ValidatorSigningInfo { + if m != nil { + return m.ValidatorSigningInfo + } + return ValidatorSigningInfo{} +} + +// ValidatorMissedBlocks contains array of missed blocks of corresponding +// address. +type ValidatorMissedBlocks struct { + // address is the validator address. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // missed_blocks is an array of missed blocks by the validator. + MissedBlocks []MissedBlock `protobuf:"bytes,2,rep,name=missed_blocks,json=missedBlocks,proto3" json:"missed_blocks" yaml:"missed_blocks"` +} + +func (m *ValidatorMissedBlocks) Reset() { *m = ValidatorMissedBlocks{} } +func (m *ValidatorMissedBlocks) String() string { return proto.CompactTextString(m) } +func (*ValidatorMissedBlocks) ProtoMessage() {} +func (*ValidatorMissedBlocks) Descriptor() ([]byte, []int) { + return fileDescriptor_1923b9188b635394, []int{2} +} +func (m *ValidatorMissedBlocks) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorMissedBlocks) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorMissedBlocks.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 *ValidatorMissedBlocks) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorMissedBlocks.Merge(m, src) +} +func (m *ValidatorMissedBlocks) XXX_Size() int { + return m.Size() +} +func (m *ValidatorMissedBlocks) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorMissedBlocks.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorMissedBlocks proto.InternalMessageInfo + +func (m *ValidatorMissedBlocks) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ValidatorMissedBlocks) GetMissedBlocks() []MissedBlock { + if m != nil { + return m.MissedBlocks + } + return nil +} + +// MissedBlock contains height and missed status as boolean. +type MissedBlock struct { + // index is the height at which the block was missed. + Index int64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + // missed is the missed status. + Missed bool `protobuf:"varint,2,opt,name=missed,proto3" json:"missed,omitempty"` +} + +func (m *MissedBlock) Reset() { *m = MissedBlock{} } +func (m *MissedBlock) String() string { return proto.CompactTextString(m) } +func (*MissedBlock) ProtoMessage() {} +func (*MissedBlock) Descriptor() ([]byte, []int) { + return fileDescriptor_1923b9188b635394, []int{3} +} +func (m *MissedBlock) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MissedBlock) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MissedBlock.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 *MissedBlock) XXX_Merge(src proto.Message) { + xxx_messageInfo_MissedBlock.Merge(m, src) +} +func (m *MissedBlock) XXX_Size() int { + return m.Size() +} +func (m *MissedBlock) XXX_DiscardUnknown() { + xxx_messageInfo_MissedBlock.DiscardUnknown(m) +} + +var xxx_messageInfo_MissedBlock proto.InternalMessageInfo + +func (m *MissedBlock) GetIndex() int64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *MissedBlock) GetMissed() bool { + if m != nil { + return m.Missed + } + return false +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.slashing.v1beta1.GenesisState") + proto.RegisterType((*SigningInfo)(nil), "cosmos.slashing.v1beta1.SigningInfo") + proto.RegisterType((*ValidatorMissedBlocks)(nil), "cosmos.slashing.v1beta1.ValidatorMissedBlocks") + proto.RegisterType((*MissedBlock)(nil), "cosmos.slashing.v1beta1.MissedBlock") +} + +func init() { + proto.RegisterFile("cosmos/slashing/v1beta1/genesis.proto", fileDescriptor_1923b9188b635394) +} + +var fileDescriptor_1923b9188b635394 = []byte{ + // 424 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x3d, 0x8e, 0xd3, 0x40, + 0x14, 0xf6, 0x24, 0x10, 0x60, 0x9c, 0x6d, 0x46, 0x66, 0xb1, 0x56, 0xe0, 0xac, 0x2c, 0x16, 0x6d, + 0x13, 0x5b, 0xbb, 0x74, 0x20, 0x1a, 0x37, 0x2b, 0x0a, 0x24, 0xe4, 0x95, 0x28, 0x68, 0xa2, 0x71, + 0x3c, 0x3b, 0x19, 0xc5, 0xf6, 0x04, 0x3f, 0x13, 0x25, 0x57, 0xa0, 0xa2, 0xe6, 0x06, 0xf4, 0x1c, + 0x22, 0x65, 0x4a, 0xaa, 0x08, 0x25, 0x37, 0xe0, 0x04, 0x28, 0x33, 0x0e, 0x71, 0xa2, 0x98, 0x88, + 0xca, 0x7e, 0xd2, 0xf7, 0xf7, 0xbe, 0xd1, 0xc3, 0x17, 0x7d, 0x09, 0xa9, 0x04, 0x1f, 0x12, 0x0a, + 0x03, 0x91, 0x71, 0x7f, 0x7c, 0x15, 0xb1, 0x82, 0x5e, 0xf9, 0x9c, 0x65, 0x0c, 0x04, 0x78, 0xa3, + 0x5c, 0x16, 0x92, 0x3c, 0xd1, 0x30, 0x6f, 0x03, 0xf3, 0x4a, 0xd8, 0x99, 0xc5, 0x25, 0x97, 0x0a, + 0xe3, 0xaf, 0xff, 0x34, 0xfc, 0xec, 0x45, 0x9d, 0xea, 0x5f, 0xbe, 0xc2, 0xb9, 0xdf, 0x1b, 0xb8, + 0x7d, 0xa3, 0x8d, 0x6e, 0x0b, 0x5a, 0x30, 0xf2, 0x06, 0xb7, 0x46, 0x34, 0xa7, 0x29, 0xd8, 0xe8, + 0x1c, 0x5d, 0x9a, 0xd7, 0x1d, 0xaf, 0xc6, 0xd8, 0x7b, 0xaf, 0x60, 0xc1, 0xbd, 0xd9, 0xa2, 0x63, + 0x84, 0x25, 0x89, 0x70, 0x7c, 0x02, 0x82, 0x67, 0x22, 0xe3, 0x3d, 0x91, 0xdd, 0x49, 0xb0, 0x1b, + 0xe7, 0xcd, 0x4b, 0xf3, 0xfa, 0x79, 0xad, 0xca, 0xad, 0x46, 0xbf, 0xcd, 0xee, 0x64, 0xf0, 0x74, + 0x2d, 0xf5, 0x7b, 0xd1, 0xb1, 0xa6, 0x34, 0x4d, 0x5e, 0xb9, 0x3b, 0x42, 0x6e, 0xd8, 0x86, 0x2d, + 0x14, 0xc8, 0x27, 0x7c, 0x92, 0x0a, 0x00, 0x16, 0xf7, 0xa2, 0x44, 0xf6, 0x87, 0x60, 0x37, 0x95, + 0x91, 0x57, 0x6b, 0xf4, 0x81, 0x26, 0x22, 0xa6, 0x85, 0xcc, 0xdf, 0x29, 0x5a, 0xa0, 0x58, 0xfb, + 0x96, 0x3b, 0x92, 0x6e, 0xd8, 0x4e, 0x2b, 0x58, 0xf7, 0x07, 0xc2, 0x66, 0x25, 0x2e, 0xb1, 0xf1, + 0x03, 0x1a, 0xc7, 0x39, 0x03, 0xdd, 0xd5, 0xa3, 0x70, 0x33, 0x92, 0x2f, 0x08, 0x9f, 0x8e, 0x37, + 0x7e, 0xbd, 0xea, 0x1e, 0x76, 0x43, 0xb5, 0xda, 0x3d, 0x1e, 0xb3, 0x5a, 0xcc, 0x45, 0x99, 0xf2, + 0x99, 0x4e, 0x79, 0x58, 0xda, 0x0d, 0xad, 0xf1, 0x01, 0xb2, 0xfb, 0x0d, 0xe1, 0xc7, 0x07, 0x97, + 0xff, 0xc7, 0x02, 0x7c, 0xbf, 0xdd, 0x63, 0xcf, 0x58, 0xd1, 0xfd, 0xaf, 0x4e, 0x5f, 0x63, 0xb3, + 0x42, 0x25, 0x16, 0xbe, 0x2f, 0xb2, 0x98, 0x4d, 0x54, 0x9e, 0x66, 0xa8, 0x07, 0x72, 0x8a, 0x5b, + 0x9a, 0xa4, 0xda, 0x7b, 0x18, 0x96, 0x53, 0x70, 0x33, 0x5b, 0x3a, 0x68, 0xbe, 0x74, 0xd0, 0xaf, + 0xa5, 0x83, 0xbe, 0xae, 0x1c, 0x63, 0xbe, 0x72, 0x8c, 0x9f, 0x2b, 0xc7, 0xf8, 0xd8, 0xe5, 0xa2, + 0x18, 0x7c, 0x8e, 0xbc, 0xbe, 0x4c, 0xfd, 0xf2, 0x12, 0xf4, 0xa7, 0x0b, 0xf1, 0xd0, 0x9f, 0x6c, + 0xcf, 0xa2, 0x98, 0x8e, 0x18, 0x44, 0x2d, 0x75, 0x0c, 0x2f, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, + 0x03, 0x3c, 0xa8, 0xf6, 0x8c, 0x03, 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.MissedBlocks) > 0 { + for iNdEx := len(m.MissedBlocks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MissedBlocks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.SigningInfos) > 0 { + for iNdEx := len(m.SigningInfos) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SigningInfos[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Params.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 *SigningInfo) 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 *SigningInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SigningInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ValidatorSigningInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValidatorMissedBlocks) 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 *ValidatorMissedBlocks) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorMissedBlocks) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MissedBlocks) > 0 { + for iNdEx := len(m.MissedBlocks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MissedBlocks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MissedBlock) 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 *MissedBlock) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MissedBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Missed { + i-- + if m.Missed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Index != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + 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 + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.SigningInfos) > 0 { + for _, e := range m.SigningInfos { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.MissedBlocks) > 0 { + for _, e := range m.MissedBlocks { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *SigningInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.ValidatorSigningInfo.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *ValidatorMissedBlocks) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.MissedBlocks) > 0 { + for _, e := range m.MissedBlocks { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *MissedBlock) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovGenesis(uint64(m.Index)) + } + if m.Missed { + n += 2 + } + 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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SigningInfos", 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.SigningInfos = append(m.SigningInfos, SigningInfo{}) + if err := m.SigningInfos[len(m.SigningInfos)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissedBlocks", 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.MissedBlocks = append(m.MissedBlocks, ValidatorMissedBlocks{}) + if err := m.MissedBlocks[len(m.MissedBlocks)-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 *SigningInfo) 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: SigningInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SigningInfo: 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 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSigningInfo", 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 := m.ValidatorSigningInfo.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 *ValidatorMissedBlocks) 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: ValidatorMissedBlocks: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorMissedBlocks: 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 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MissedBlocks", 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.MissedBlocks = append(m.MissedBlocks, MissedBlock{}) + if err := m.MissedBlocks[len(m.MissedBlocks)-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 *MissedBlock) 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: MissedBlock: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MissedBlock: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Missed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Missed = bool(v != 0) + 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/slashing/types/keys.go b/x/slashing/types/keys.go new file mode 100644 index 000000000000..c9792d22085f --- /dev/null +++ b/x/slashing/types/keys.go @@ -0,0 +1,66 @@ +package types + +import ( + "encoding/binary" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName is the name of the module + ModuleName = "slashing" + + // StoreKey is the store key string for slashing + StoreKey = ModuleName + + // RouterKey is the message route for slashing + RouterKey = ModuleName + + // QuerierRoute is the querier route for slashing + QuerierRoute = ModuleName +) + +// Keys for slashing store +// Items are stored with the following key: values +// +// - 0x01: ValidatorSigningInfo +// +// - 0x02: bool +// +// - 0x03: crypto.PubKey +var ( + ValidatorSigningInfoKeyPrefix = []byte{0x01} // Prefix for signing info + ValidatorMissedBlockBitArrayKeyPrefix = []byte{0x02} // Prefix for missed block bit array + AddrPubkeyRelationKeyPrefix = []byte{0x03} // Prefix for address-pubkey relation +) + +// ValidatorSigningInfoKey - stored by *Consensus* address (not operator address) +func ValidatorSigningInfoKey(v sdk.ConsAddress) []byte { + return append(ValidatorSigningInfoKeyPrefix, v.Bytes()...) +} + +// ValidatorSigningInfoAddress - extract the address from a validator signing info key +func ValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) { + addr := key[1:] + if len(addr) != sdk.AddrLen { + panic("unexpected key length") + } + return sdk.ConsAddress(addr) +} + +// ValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address) +func ValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte { + return append(ValidatorMissedBlockBitArrayKeyPrefix, v.Bytes()...) +} + +// ValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address) +func ValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(i)) + return append(ValidatorMissedBlockBitArrayPrefixKey(v), b...) +} + +// AddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address +func AddrPubkeyRelationKey(address []byte) []byte { + return append(AddrPubkeyRelationKeyPrefix, address...) +} diff --git a/x/slashing/types/msg.go b/x/slashing/types/msg.go new file mode 100644 index 000000000000..d86ff5eb7249 --- /dev/null +++ b/x/slashing/types/msg.go @@ -0,0 +1,46 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// slashing message types +const ( + TypeMsgUnjail = "unjail" +) + +// verify interface at compile time +var _ sdk.Msg = &MsgUnjail{} + +// NewMsgUnjail creates a new MsgUnjail instance +//nolint:interfacer +func NewMsgUnjail(validatorAddr sdk.ValAddress) *MsgUnjail { + return &MsgUnjail{ + ValidatorAddr: validatorAddr.String(), + } +} + +func (msg MsgUnjail) Route() string { return RouterKey } +func (msg MsgUnjail) Type() string { return TypeMsgUnjail } +func (msg MsgUnjail) GetSigners() []sdk.AccAddress { + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr) + if err != nil { + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} +} + +// GetSignBytes gets the bytes for the message signer to sign on +func (msg MsgUnjail) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// ValidateBasic validity check for the AnteHandler +func (msg MsgUnjail) ValidateBasic() error { + if msg.ValidatorAddr == "" { + return ErrBadValidatorAddr + } + + return nil +} diff --git a/x/slashing/internal/types/msg_test.go b/x/slashing/types/msg_test.go similarity index 78% rename from x/slashing/internal/types/msg_test.go rename to x/slashing/types/msg_test.go index ec33d26ef492..31f7a70e75db 100644 --- a/x/slashing/internal/types/msg_test.go +++ b/x/slashing/types/msg_test.go @@ -14,7 +14,7 @@ func TestMsgUnjailGetSignBytes(t *testing.T) { bytes := msg.GetSignBytes() require.Equal( t, - `{"type":"cosmos-sdk/MsgUnjail","value":{"address":"fetchvaloper1v93xxeq5lh97x"}}`, + `{"type":"cosmos-sdk/MsgUnjail","value":{"address":"cosmosvaloper1v93xxeqhg9nn6"}}`, string(bytes), ) } diff --git a/x/slashing/types/params.go b/x/slashing/types/params.go new file mode 100644 index 000000000000..aa9cd559e7db --- /dev/null +++ b/x/slashing/types/params.go @@ -0,0 +1,143 @@ +package types + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +// Default parameter namespace +const ( + DefaultSignedBlocksWindow = int64(100) + DefaultDowntimeJailDuration = 60 * 10 * time.Second +) + +var ( + DefaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1) + DefaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20)) + DefaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100)) +) + +// Parameter store keys +var ( + KeySignedBlocksWindow = []byte("SignedBlocksWindow") + KeyMinSignedPerWindow = []byte("MinSignedPerWindow") + KeyDowntimeJailDuration = []byte("DowntimeJailDuration") + KeySlashFractionDoubleSign = []byte("SlashFractionDoubleSign") + KeySlashFractionDowntime = []byte("SlashFractionDowntime") +) + +// ParamKeyTable for slashing module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// NewParams creates a new Params object +func NewParams( + signedBlocksWindow int64, minSignedPerWindow sdk.Dec, downtimeJailDuration time.Duration, + slashFractionDoubleSign, slashFractionDowntime sdk.Dec, +) Params { + + return Params{ + SignedBlocksWindow: signedBlocksWindow, + MinSignedPerWindow: minSignedPerWindow, + DowntimeJailDuration: downtimeJailDuration, + SlashFractionDoubleSign: slashFractionDoubleSign, + SlashFractionDowntime: slashFractionDowntime, + } +} + +// ParamSetPairs - Implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeySignedBlocksWindow, &p.SignedBlocksWindow, validateSignedBlocksWindow), + paramtypes.NewParamSetPair(KeyMinSignedPerWindow, &p.MinSignedPerWindow, validateMinSignedPerWindow), + paramtypes.NewParamSetPair(KeyDowntimeJailDuration, &p.DowntimeJailDuration, validateDowntimeJailDuration), + paramtypes.NewParamSetPair(KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign, validateSlashFractionDoubleSign), + paramtypes.NewParamSetPair(KeySlashFractionDowntime, &p.SlashFractionDowntime, validateSlashFractionDowntime), + } +} + +// DefaultParams defines the parameters for this module +func DefaultParams() Params { + return NewParams( + DefaultSignedBlocksWindow, DefaultMinSignedPerWindow, DefaultDowntimeJailDuration, + DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime, + ) +} + +func validateSignedBlocksWindow(i interface{}) error { + v, ok := i.(int64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v <= 0 { + return fmt.Errorf("signed blocks window must be positive: %d", v) + } + + return nil +} + +func validateMinSignedPerWindow(i interface{}) error { + v, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("min signed per window cannot be negative: %s", v) + } + if v.GT(sdk.OneDec()) { + return fmt.Errorf("min signed per window too large: %s", v) + } + + return nil +} + +func validateDowntimeJailDuration(i interface{}) error { + v, ok := i.(time.Duration) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v <= 0 { + return fmt.Errorf("downtime jail duration must be positive: %s", v) + } + + return nil +} + +func validateSlashFractionDoubleSign(i interface{}) error { + v, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("double sign slash fraction cannot be negative: %s", v) + } + if v.GT(sdk.OneDec()) { + return fmt.Errorf("double sign slash fraction too large: %s", v) + } + + return nil +} + +func validateSlashFractionDowntime(i interface{}) error { + v, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if v.IsNegative() { + return fmt.Errorf("downtime slash fraction cannot be negative: %s", v) + } + if v.GT(sdk.OneDec()) { + return fmt.Errorf("downtime slash fraction too large: %s", v) + } + + return nil +} diff --git a/x/slashing/types/querier.go b/x/slashing/types/querier.go new file mode 100644 index 000000000000..3820a65264f6 --- /dev/null +++ b/x/slashing/types/querier.go @@ -0,0 +1,21 @@ +package types + +// DONTCOVER + +// Query endpoints supported by the slashing querier +const ( + QueryParameters = "parameters" + QuerySigningInfo = "signingInfo" + QuerySigningInfos = "signingInfos" +) + +// QuerySigningInfosParams defines the params for the following queries: +// - 'custom/slashing/signingInfos' +type QuerySigningInfosParams struct { + Page, Limit int +} + +// NewQuerySigningInfosParams creates a new QuerySigningInfosParams instance +func NewQuerySigningInfosParams(page, limit int) QuerySigningInfosParams { + return QuerySigningInfosParams{page, limit} +} diff --git a/x/slashing/types/query.pb.go b/x/slashing/types/query.pb.go new file mode 100644 index 000000000000..3a0c997a3056 --- /dev/null +++ b/x/slashing/types/query.pb.go @@ -0,0 +1,1401 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/slashing/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 + +// QueryParamsRequest is the request type for the Query/Params RPC method +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// QuerySigningInfoRequest is the request type for the Query/SigningInfo RPC +// method +type QuerySigningInfoRequest struct { + // cons_address is the address to query signing info of + ConsAddress string `protobuf:"bytes,1,opt,name=cons_address,json=consAddress,proto3" json:"cons_address,omitempty"` +} + +func (m *QuerySigningInfoRequest) Reset() { *m = QuerySigningInfoRequest{} } +func (m *QuerySigningInfoRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySigningInfoRequest) ProtoMessage() {} +func (*QuerySigningInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{2} +} +func (m *QuerySigningInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySigningInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySigningInfoRequest.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 *QuerySigningInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySigningInfoRequest.Merge(m, src) +} +func (m *QuerySigningInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySigningInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySigningInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySigningInfoRequest proto.InternalMessageInfo + +func (m *QuerySigningInfoRequest) GetConsAddress() string { + if m != nil { + return m.ConsAddress + } + return "" +} + +// QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC +// method +type QuerySigningInfoResponse struct { + // val_signing_info is the signing info of requested val cons address + ValSigningInfo ValidatorSigningInfo `protobuf:"bytes,1,opt,name=val_signing_info,json=valSigningInfo,proto3" json:"val_signing_info"` +} + +func (m *QuerySigningInfoResponse) Reset() { *m = QuerySigningInfoResponse{} } +func (m *QuerySigningInfoResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySigningInfoResponse) ProtoMessage() {} +func (*QuerySigningInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{3} +} +func (m *QuerySigningInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySigningInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySigningInfoResponse.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 *QuerySigningInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySigningInfoResponse.Merge(m, src) +} +func (m *QuerySigningInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySigningInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySigningInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySigningInfoResponse proto.InternalMessageInfo + +func (m *QuerySigningInfoResponse) GetValSigningInfo() ValidatorSigningInfo { + if m != nil { + return m.ValSigningInfo + } + return ValidatorSigningInfo{} +} + +// QuerySigningInfosRequest is the request type for the Query/SigningInfos RPC +// method +type QuerySigningInfosRequest struct { + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QuerySigningInfosRequest) Reset() { *m = QuerySigningInfosRequest{} } +func (m *QuerySigningInfosRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySigningInfosRequest) ProtoMessage() {} +func (*QuerySigningInfosRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{4} +} +func (m *QuerySigningInfosRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySigningInfosRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySigningInfosRequest.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 *QuerySigningInfosRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySigningInfosRequest.Merge(m, src) +} +func (m *QuerySigningInfosRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySigningInfosRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySigningInfosRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySigningInfosRequest proto.InternalMessageInfo + +func (m *QuerySigningInfosRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC +// method +type QuerySigningInfosResponse struct { + // info is the signing info of all validators + Info []ValidatorSigningInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QuerySigningInfosResponse) Reset() { *m = QuerySigningInfosResponse{} } +func (m *QuerySigningInfosResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySigningInfosResponse) ProtoMessage() {} +func (*QuerySigningInfosResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_791b11d41a861ed0, []int{5} +} +func (m *QuerySigningInfosResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySigningInfosResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySigningInfosResponse.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 *QuerySigningInfosResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySigningInfosResponse.Merge(m, src) +} +func (m *QuerySigningInfosResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySigningInfosResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySigningInfosResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySigningInfosResponse proto.InternalMessageInfo + +func (m *QuerySigningInfosResponse) GetInfo() []ValidatorSigningInfo { + if m != nil { + return m.Info + } + return nil +} + +func (m *QuerySigningInfosResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.slashing.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.slashing.v1beta1.QueryParamsResponse") + proto.RegisterType((*QuerySigningInfoRequest)(nil), "cosmos.slashing.v1beta1.QuerySigningInfoRequest") + proto.RegisterType((*QuerySigningInfoResponse)(nil), "cosmos.slashing.v1beta1.QuerySigningInfoResponse") + proto.RegisterType((*QuerySigningInfosRequest)(nil), "cosmos.slashing.v1beta1.QuerySigningInfosRequest") + proto.RegisterType((*QuerySigningInfosResponse)(nil), "cosmos.slashing.v1beta1.QuerySigningInfosResponse") +} + +func init() { + proto.RegisterFile("cosmos/slashing/v1beta1/query.proto", fileDescriptor_791b11d41a861ed0) +} + +var fileDescriptor_791b11d41a861ed0 = []byte{ + // 526 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x41, 0x6b, 0x13, 0x41, + 0x14, 0xc7, 0x33, 0x6d, 0x0d, 0x38, 0x29, 0x22, 0x63, 0xa1, 0x35, 0xc8, 0xc6, 0xae, 0x90, 0x16, + 0x35, 0x3b, 0x26, 0x22, 0x5e, 0xec, 0xc1, 0x1e, 0x0c, 0xde, 0x34, 0x8a, 0x07, 0x41, 0xc2, 0x6c, + 0x32, 0x9d, 0x0e, 0x6e, 0x66, 0xb6, 0xfb, 0x36, 0xc1, 0x20, 0x5e, 0x3c, 0x7b, 0x10, 0xfc, 0x0c, + 0x1e, 0x3d, 0xf8, 0x2d, 0x7a, 0x2c, 0x78, 0xf1, 0x24, 0x92, 0xf8, 0x09, 0xfc, 0x04, 0x92, 0x99, + 0x49, 0xb2, 0x25, 0xae, 0xa6, 0x3d, 0xed, 0xf0, 0xe6, 0xfd, 0xdf, 0xff, 0xf7, 0xe6, 0x3d, 0x16, + 0xdf, 0xe8, 0x68, 0xe8, 0x69, 0xa0, 0x10, 0x31, 0x38, 0x94, 0x4a, 0xd0, 0x41, 0x3d, 0xe4, 0x29, + 0xab, 0xd3, 0xa3, 0x3e, 0x4f, 0x86, 0x41, 0x9c, 0xe8, 0x54, 0x93, 0x4d, 0x9b, 0x14, 0x4c, 0x93, + 0x02, 0x97, 0x54, 0xbe, 0xe9, 0xd4, 0x21, 0x03, 0x6e, 0x15, 0x33, 0x7d, 0xcc, 0x84, 0x54, 0x2c, + 0x95, 0x5a, 0xd9, 0x22, 0xe5, 0x0d, 0xa1, 0x85, 0x36, 0x47, 0x3a, 0x39, 0xb9, 0xe8, 0x35, 0xa1, + 0xb5, 0x88, 0x38, 0x65, 0xb1, 0xa4, 0x4c, 0x29, 0x9d, 0x1a, 0x09, 0xb8, 0xdb, 0x6a, 0x1e, 0xdd, + 0x8c, 0xc4, 0xe4, 0xf9, 0x1b, 0x98, 0x3c, 0x9d, 0xb8, 0x3f, 0x61, 0x09, 0xeb, 0x41, 0x8b, 0x1f, + 0xf5, 0x39, 0xa4, 0xfe, 0x73, 0x7c, 0xe5, 0x54, 0x14, 0x62, 0xad, 0x80, 0x93, 0x3d, 0x5c, 0x8c, + 0x4d, 0x64, 0x0b, 0x5d, 0x47, 0xbb, 0xa5, 0x46, 0x25, 0xc8, 0x69, 0x2f, 0xb0, 0xc2, 0xfd, 0xb5, + 0xe3, 0x1f, 0x95, 0x42, 0xcb, 0x89, 0xfc, 0x07, 0x78, 0xd3, 0x54, 0x7d, 0x26, 0x85, 0x92, 0x4a, + 0x3c, 0x56, 0x07, 0xda, 0x19, 0x92, 0x6d, 0xbc, 0xde, 0xd1, 0x0a, 0xda, 0xac, 0xdb, 0x4d, 0x38, + 0xd8, 0xfa, 0x17, 0x5b, 0xa5, 0x49, 0xec, 0xa1, 0x0d, 0xf9, 0x43, 0xbc, 0xb5, 0xa8, 0x76, 0x60, + 0xaf, 0xf0, 0xe5, 0x01, 0x8b, 0xda, 0x60, 0xaf, 0xda, 0x52, 0x1d, 0x68, 0x87, 0x58, 0xcb, 0x45, + 0x7c, 0xc1, 0x22, 0xd9, 0x65, 0xa9, 0x4e, 0x32, 0x05, 0x1d, 0xf0, 0xa5, 0x01, 0x8b, 0x32, 0x51, + 0x3f, 0x5c, 0xb4, 0x9e, 0x3e, 0x15, 0x79, 0x84, 0xf1, 0x7c, 0x60, 0xce, 0xb4, 0x3a, 0x35, 0x9d, + 0x4c, 0x37, 0xb0, 0xfb, 0x30, 0x7f, 0x19, 0xc1, 0x9d, 0xb6, 0x95, 0x51, 0xfa, 0x5f, 0x10, 0xbe, + 0xfa, 0x17, 0x13, 0xd7, 0x60, 0x13, 0xaf, 0xb9, 0xa6, 0x56, 0xcf, 0xdb, 0x94, 0x29, 0x40, 0x9a, + 0xa7, 0x70, 0x57, 0x0c, 0xee, 0xce, 0x7f, 0x71, 0x2d, 0x45, 0x96, 0xb7, 0xf1, 0x7b, 0x15, 0x5f, + 0x30, 0xbc, 0xe4, 0x03, 0xc2, 0x45, 0x3b, 0x6f, 0x72, 0x2b, 0x17, 0x6c, 0x71, 0xc9, 0xca, 0xb7, + 0x97, 0x4b, 0xb6, 0xde, 0xfe, 0xce, 0xfb, 0x6f, 0xbf, 0x3e, 0xad, 0x6c, 0x93, 0x0a, 0xcd, 0xdb, + 0x6c, 0xbb, 0x65, 0xe4, 0x2b, 0xc2, 0xa5, 0x4c, 0xf7, 0xe4, 0xce, 0xbf, 0x6d, 0x16, 0x97, 0xb1, + 0x5c, 0x3f, 0x83, 0xc2, 0xd1, 0xed, 0x19, 0xba, 0xfb, 0xe4, 0x5e, 0x2e, 0x5d, 0x76, 0x37, 0x81, + 0xbe, 0xcd, 0x6e, 0xfb, 0x3b, 0xf2, 0x19, 0xe1, 0xf5, 0xec, 0xdc, 0xc9, 0xf2, 0x08, 0xb3, 0xe7, + 0x6c, 0x9c, 0x45, 0xe2, 0xb0, 0x03, 0x83, 0xbd, 0x4b, 0xaa, 0xcb, 0x61, 0xef, 0x37, 0x8f, 0x47, + 0x1e, 0x3a, 0x19, 0x79, 0xe8, 0xe7, 0xc8, 0x43, 0x1f, 0xc7, 0x5e, 0xe1, 0x64, 0xec, 0x15, 0xbe, + 0x8f, 0xbd, 0xc2, 0xcb, 0x9a, 0x90, 0xe9, 0x61, 0x3f, 0x0c, 0x3a, 0xba, 0x37, 0xad, 0x65, 0x3f, + 0x35, 0xe8, 0xbe, 0xa6, 0x6f, 0xe6, 0x85, 0xd3, 0x61, 0xcc, 0x21, 0x2c, 0x9a, 0xbf, 0xcf, 0xdd, + 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x0f, 0xc0, 0xe6, 0x45, 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 { + // Params queries the parameters of slashing module + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // SigningInfo queries the signing info of given cons address + SigningInfo(ctx context.Context, in *QuerySigningInfoRequest, opts ...grpc.CallOption) (*QuerySigningInfoResponse, error) + // SigningInfos queries signing info of all validators + SigningInfos(ctx context.Context, in *QuerySigningInfosRequest, opts ...grpc.CallOption) (*QuerySigningInfosResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.slashing.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) SigningInfo(ctx context.Context, in *QuerySigningInfoRequest, opts ...grpc.CallOption) (*QuerySigningInfoResponse, error) { + out := new(QuerySigningInfoResponse) + err := c.cc.Invoke(ctx, "/cosmos.slashing.v1beta1.Query/SigningInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) SigningInfos(ctx context.Context, in *QuerySigningInfosRequest, opts ...grpc.CallOption) (*QuerySigningInfosResponse, error) { + out := new(QuerySigningInfosResponse) + err := c.cc.Invoke(ctx, "/cosmos.slashing.v1beta1.Query/SigningInfos", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries the parameters of slashing module + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // SigningInfo queries the signing info of given cons address + SigningInfo(context.Context, *QuerySigningInfoRequest) (*QuerySigningInfoResponse, error) + // SigningInfos queries signing info of all validators + SigningInfos(context.Context, *QuerySigningInfosRequest) (*QuerySigningInfosResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) SigningInfo(ctx context.Context, req *QuerySigningInfoRequest) (*QuerySigningInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SigningInfo not implemented") +} +func (*UnimplementedQueryServer) SigningInfos(ctx context.Context, req *QuerySigningInfosRequest) (*QuerySigningInfosResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SigningInfos not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.slashing.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_SigningInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySigningInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).SigningInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.slashing.v1beta1.Query/SigningInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).SigningInfo(ctx, req.(*QuerySigningInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_SigningInfos_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySigningInfosRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).SigningInfos(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.slashing.v1beta1.Query/SigningInfos", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).SigningInfos(ctx, req.(*QuerySigningInfosRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.slashing.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "SigningInfo", + Handler: _Query_SigningInfo_Handler, + }, + { + MethodName: "SigningInfos", + Handler: _Query_SigningInfos_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/slashing/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *QuerySigningInfoRequest) 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 *QuerySigningInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySigningInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsAddress) > 0 { + i -= len(m.ConsAddress) + copy(dAtA[i:], m.ConsAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConsAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QuerySigningInfoResponse) 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 *QuerySigningInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySigningInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ValSigningInfo.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 *QuerySigningInfosRequest) 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 *QuerySigningInfosRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySigningInfosRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QuerySigningInfosResponse) 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 *QuerySigningInfosResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySigningInfosResponse) 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.Info) > 0 { + for iNdEx := len(m.Info) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Info[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 *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QuerySigningInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConsAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QuerySigningInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ValSigningInfo.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QuerySigningInfosRequest) 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 *QuerySigningInfosResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Info) > 0 { + for _, e := range m.Info { + 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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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 *QuerySigningInfoRequest) 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: QuerySigningInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySigningInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsAddress", 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.ConsAddress = 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 *QuerySigningInfoResponse) 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: QuerySigningInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySigningInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValSigningInfo", 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 err := m.ValSigningInfo.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 *QuerySigningInfosRequest) 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: QuerySigningInfosRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySigningInfosRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QuerySigningInfosResponse) 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: QuerySigningInfosResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySigningInfosResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", 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.Info = append(m.Info, ValidatorSigningInfo{}) + if err := m.Info[len(m.Info)-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/slashing/types/query.pb.gw.go b/x/slashing/types/query.pb.gw.go new file mode 100644 index 000000000000..083fe73aa9f5 --- /dev/null +++ b/x/slashing/types/query.pb.gw.go @@ -0,0 +1,326 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/slashing/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_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_SigningInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySigningInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["cons_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cons_address") + } + + protoReq.ConsAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cons_address", err) + } + + msg, err := client.SigningInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_SigningInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySigningInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["cons_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cons_address") + } + + protoReq.ConsAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cons_address", err) + } + + msg, err := server.SigningInfo(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_SigningInfos_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_SigningInfos_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySigningInfosRequest + 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_SigningInfos_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.SigningInfos(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_SigningInfos_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySigningInfosRequest + 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_SigningInfos_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.SigningInfos(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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SigningInfo_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_SigningInfo_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_SigningInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SigningInfos_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_SigningInfos_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_SigningInfos_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_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SigningInfo_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_SigningInfo_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_SigningInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_SigningInfos_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_SigningInfos_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_SigningInfos_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_SigningInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "slashing", "v1beta1", "signing_infos", "cons_address"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_SigningInfos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "slashing", "v1beta1", "signing_infos"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_SigningInfo_0 = runtime.ForwardResponseMessage + + forward_Query_SigningInfos_0 = runtime.ForwardResponseMessage +) diff --git a/x/slashing/types/signing_info.go b/x/slashing/types/signing_info.go new file mode 100644 index 000000000000..ad359c3a5180 --- /dev/null +++ b/x/slashing/types/signing_info.go @@ -0,0 +1,45 @@ +package types + +import ( + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewValidatorSigningInfo creates a new ValidatorSigningInfo instance +//nolint:interfacer +func NewValidatorSigningInfo( + condAddr sdk.ConsAddress, startHeight, indexOffset int64, + jailedUntil time.Time, tombstoned bool, missedBlocksCounter int64, +) ValidatorSigningInfo { + + return ValidatorSigningInfo{ + Address: condAddr.String(), + StartHeight: startHeight, + IndexOffset: indexOffset, + JailedUntil: jailedUntil, + Tombstoned: tombstoned, + MissedBlocksCounter: missedBlocksCounter, + } +} + +// String implements the stringer interface for ValidatorSigningInfo +func (i ValidatorSigningInfo) String() string { + return fmt.Sprintf(`Validator Signing Info: + Address: %s + Start Height: %d + Index Offset: %d + Jailed Until: %v + Tombstoned: %t + Missed Blocks Counter: %d`, + i.Address, i.StartHeight, i.IndexOffset, i.JailedUntil, + i.Tombstoned, i.MissedBlocksCounter) +} + +// unmarshal a validator signing info from a store value +func UnmarshalValSigningInfo(cdc codec.Marshaler, value []byte) (signingInfo ValidatorSigningInfo, err error) { + err = cdc.UnmarshalBinaryBare(value, &signingInfo) + return signingInfo, err +} diff --git a/x/slashing/types/slashing.pb.go b/x/slashing/types/slashing.pb.go new file mode 100644 index 000000000000..1db4757a95a8 --- /dev/null +++ b/x/slashing/types/slashing.pb.go @@ -0,0 +1,976 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/slashing/v1beta1/slashing.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_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" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// ValidatorSigningInfo defines a validator's signing info for monitoring their +// 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 + 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 + 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 + 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) + Tombstoned bool `protobuf:"varint,5,opt,name=tombstoned,proto3" json:"tombstoned,omitempty"` + // missed blocks counter (to avoid scanning the array every time) + MissedBlocksCounter int64 `protobuf:"varint,6,opt,name=missed_blocks_counter,json=missedBlocksCounter,proto3" json:"missed_blocks_counter,omitempty" yaml:"missed_blocks_counter"` +} + +func (m *ValidatorSigningInfo) Reset() { *m = ValidatorSigningInfo{} } +func (*ValidatorSigningInfo) ProtoMessage() {} +func (*ValidatorSigningInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_1078e5d96a74cc52, []int{0} +} +func (m *ValidatorSigningInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValidatorSigningInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValidatorSigningInfo.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 *ValidatorSigningInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValidatorSigningInfo.Merge(m, src) +} +func (m *ValidatorSigningInfo) XXX_Size() int { + return m.Size() +} +func (m *ValidatorSigningInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ValidatorSigningInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ValidatorSigningInfo proto.InternalMessageInfo + +func (m *ValidatorSigningInfo) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *ValidatorSigningInfo) GetStartHeight() int64 { + if m != nil { + return m.StartHeight + } + return 0 +} + +func (m *ValidatorSigningInfo) GetIndexOffset() int64 { + if m != nil { + return m.IndexOffset + } + return 0 +} + +func (m *ValidatorSigningInfo) GetJailedUntil() time.Time { + if m != nil { + return m.JailedUntil + } + return time.Time{} +} + +func (m *ValidatorSigningInfo) GetTombstoned() bool { + if m != nil { + return m.Tombstoned + } + return false +} + +func (m *ValidatorSigningInfo) GetMissedBlocksCounter() int64 { + if m != nil { + return m.MissedBlocksCounter + } + return 0 +} + +// Params represents the parameters used for by the slashing module. +type Params struct { + SignedBlocksWindow int64 `protobuf:"varint,1,opt,name=signed_blocks_window,json=signedBlocksWindow,proto3" json:"signed_blocks_window,omitempty" yaml:"signed_blocks_window"` + MinSignedPerWindow github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=min_signed_per_window,json=minSignedPerWindow,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"min_signed_per_window" yaml:"min_signed_per_window"` + DowntimeJailDuration time.Duration `protobuf:"bytes,3,opt,name=downtime_jail_duration,json=downtimeJailDuration,proto3,stdduration" json:"downtime_jail_duration" yaml:"downtime_jail_duration"` + SlashFractionDoubleSign github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,4,opt,name=slash_fraction_double_sign,json=slashFractionDoubleSign,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"slash_fraction_double_sign" yaml:"slash_fraction_double_sign"` + SlashFractionDowntime github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=slash_fraction_downtime,json=slashFractionDowntime,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"slash_fraction_downtime" yaml:"slash_fraction_downtime"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_1078e5d96a74cc52, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetSignedBlocksWindow() int64 { + if m != nil { + return m.SignedBlocksWindow + } + return 0 +} + +func (m *Params) GetDowntimeJailDuration() time.Duration { + if m != nil { + return m.DowntimeJailDuration + } + return 0 +} + +func init() { + proto.RegisterType((*ValidatorSigningInfo)(nil), "cosmos.slashing.v1beta1.ValidatorSigningInfo") + proto.RegisterType((*Params)(nil), "cosmos.slashing.v1beta1.Params") +} + +func init() { + proto.RegisterFile("cosmos/slashing/v1beta1/slashing.proto", fileDescriptor_1078e5d96a74cc52) +} + +var fileDescriptor_1078e5d96a74cc52 = []byte{ + // 636 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xbd, 0x6f, 0xd3, 0x4e, + 0x18, 0xce, 0xfd, 0xd2, 0x5f, 0x29, 0x97, 0x4c, 0x6e, 0x4a, 0x4c, 0x00, 0x3b, 0x78, 0xa8, 0xc2, + 0x50, 0x5b, 0x2d, 0x5b, 0x47, 0x53, 0x21, 0x3e, 0x24, 0x28, 0x6e, 0x01, 0x89, 0x01, 0xeb, 0x9c, + 0xbb, 0x38, 0x47, 0xed, 0xbb, 0xc8, 0x77, 0xa1, 0x2d, 0x1b, 0x5b, 0xc7, 0x8e, 0x1d, 0x3b, 0xf2, + 0xa7, 0x74, 0xec, 0x88, 0x18, 0x02, 0x4a, 0x17, 0xe6, 0x6e, 0x6c, 0xc8, 0x77, 0x76, 0x1b, 0xa5, + 0x29, 0x52, 0xa7, 0xe4, 0x7d, 0xde, 0xe7, 0x7d, 0xee, 0x79, 0x3f, 0x12, 0xb8, 0xdc, 0xe5, 0x22, + 0xe5, 0xc2, 0x13, 0x09, 0x12, 0x7d, 0xca, 0x62, 0xef, 0xf3, 0x6a, 0x44, 0x24, 0x5a, 0xbd, 0x00, + 0xdc, 0x41, 0xc6, 0x25, 0x37, 0x9a, 0x9a, 0xe7, 0x5e, 0xc0, 0x05, 0xaf, 0xd5, 0x88, 0x79, 0xcc, + 0x15, 0xc7, 0xcb, 0xbf, 0x69, 0x7a, 0xcb, 0x8a, 0x39, 0x8f, 0x13, 0xe2, 0xa9, 0x28, 0x1a, 0xf6, + 0x3c, 0x3c, 0xcc, 0x90, 0xa4, 0x9c, 0x15, 0x79, 0x7b, 0x3a, 0x2f, 0x69, 0x4a, 0x84, 0x44, 0xe9, + 0x40, 0x13, 0x9c, 0x83, 0x2a, 0x6c, 0xbc, 0x43, 0x09, 0xc5, 0x48, 0xf2, 0x6c, 0x8b, 0xc6, 0x8c, + 0xb2, 0xf8, 0x39, 0xeb, 0x71, 0xc3, 0x84, 0xb7, 0x10, 0xc6, 0x19, 0x11, 0xc2, 0x04, 0x6d, 0xd0, + 0xb9, 0x1d, 0x94, 0xa1, 0xb1, 0x0e, 0xeb, 0x42, 0xa2, 0x4c, 0x86, 0x7d, 0x42, 0xe3, 0xbe, 0x34, + 0xff, 0x6b, 0x83, 0x4e, 0xd5, 0x6f, 0x9e, 0x8f, 0xec, 0xc5, 0x7d, 0x94, 0x26, 0xeb, 0xce, 0x64, + 0xd6, 0x09, 0x6a, 0x2a, 0x7c, 0xa6, 0xa2, 0xbc, 0x96, 0x32, 0x4c, 0xf6, 0x42, 0xde, 0xeb, 0x09, + 0x22, 0xcd, 0xea, 0x74, 0xed, 0x64, 0xd6, 0x09, 0x6a, 0x2a, 0x7c, 0xad, 0x22, 0xe3, 0x23, 0xac, + 0x7f, 0x42, 0x34, 0x21, 0x38, 0x1c, 0x32, 0x49, 0x13, 0x73, 0xae, 0x0d, 0x3a, 0xb5, 0xb5, 0x96, + 0xab, 0x5b, 0x74, 0xcb, 0x16, 0xdd, 0xed, 0xb2, 0x45, 0xdf, 0x3e, 0x19, 0xd9, 0x95, 0x4b, 0xed, + 0xc9, 0x6a, 0xe7, 0xf0, 0xa7, 0x0d, 0x82, 0x9a, 0x86, 0xde, 0xe6, 0x88, 0x61, 0x41, 0x28, 0x79, + 0x1a, 0x09, 0xc9, 0x19, 0xc1, 0xe6, 0xff, 0x6d, 0xd0, 0x59, 0x08, 0x26, 0x10, 0x63, 0x1b, 0x2e, + 0xa5, 0x54, 0x08, 0x82, 0xc3, 0x28, 0xe1, 0xdd, 0x1d, 0x11, 0x76, 0xf9, 0x90, 0x49, 0x92, 0x99, + 0xf3, 0xaa, 0x89, 0xf6, 0xf9, 0xc8, 0xbe, 0xaf, 0x1f, 0x9a, 0x49, 0x73, 0x82, 0x45, 0x8d, 0xfb, + 0x0a, 0x7e, 0xa2, 0xd1, 0xf5, 0x85, 0xa3, 0x63, 0xbb, 0xf2, 0xfb, 0xd8, 0x06, 0xce, 0x9f, 0x39, + 0x38, 0xbf, 0x89, 0x32, 0x94, 0x0a, 0xe3, 0x0d, 0x6c, 0x08, 0x1a, 0xb3, 0x4b, 0x8d, 0x5d, 0xca, + 0x30, 0xdf, 0x55, 0x9b, 0xa8, 0xfa, 0xf6, 0xf9, 0xc8, 0xbe, 0x57, 0x8c, 0x7a, 0x06, 0xcb, 0x09, + 0x0c, 0x0d, 0xeb, 0x87, 0xde, 0x2b, 0xd0, 0xf8, 0x0a, 0x72, 0xfb, 0x2c, 0x2c, 0x2a, 0x06, 0x24, + 0x2b, 0x45, 0xf3, 0xfd, 0xd5, 0xfd, 0x57, 0xf9, 0xac, 0x7e, 0x8c, 0xec, 0xe5, 0x98, 0xca, 0xfe, + 0x30, 0x72, 0xbb, 0x3c, 0xf5, 0x8a, 0x9b, 0xd5, 0x1f, 0x2b, 0x02, 0xef, 0x78, 0x72, 0x7f, 0x40, + 0x84, 0xbb, 0x41, 0xba, 0x93, 0xcd, 0xce, 0x10, 0x75, 0x02, 0x23, 0xa5, 0x6c, 0x4b, 0xc1, 0x9b, + 0x24, 0x2b, 0x3c, 0x7c, 0x81, 0x77, 0x30, 0xdf, 0x65, 0xf9, 0x0d, 0x86, 0xf9, 0xe4, 0xc3, 0xf2, + 0x5a, 0xd5, 0x1d, 0xd4, 0xd6, 0xee, 0x5e, 0xd9, 0xe5, 0x46, 0x41, 0xf0, 0x1f, 0x15, 0xab, 0x7c, + 0xa0, 0x1f, 0x9d, 0x2d, 0xe3, 0x1c, 0xe5, 0x4b, 0x6d, 0x94, 0xc9, 0x17, 0x88, 0x26, 0xa5, 0x80, + 0x71, 0x08, 0x60, 0x4b, 0xfd, 0xa8, 0xc2, 0x5e, 0x86, 0xba, 0x39, 0x14, 0x62, 0x3e, 0x8c, 0x12, + 0xa2, 0xcc, 0xab, 0x63, 0xaa, 0xfb, 0x5b, 0x37, 0x1e, 0xc2, 0xc3, 0x62, 0x0f, 0xd7, 0x2a, 0x3b, + 0x41, 0x53, 0x25, 0x9f, 0x16, 0xb9, 0x0d, 0x95, 0xca, 0x27, 0x63, 0x1c, 0x00, 0xd8, 0xbc, 0x52, + 0xa8, 0xad, 0xab, 0xf3, 0xab, 0xfb, 0x9b, 0x37, 0xf6, 0x63, 0x5d, 0xe3, 0x47, 0xcb, 0x3a, 0xc1, + 0xd2, 0x94, 0x19, 0x8d, 0xfb, 0x2f, 0xbf, 0x8d, 0x2d, 0x70, 0x32, 0xb6, 0xc0, 0xe9, 0xd8, 0x02, + 0xbf, 0xc6, 0x16, 0x38, 0x3c, 0xb3, 0x2a, 0xa7, 0x67, 0x56, 0xe5, 0xfb, 0x99, 0x55, 0xf9, 0xb0, + 0xf2, 0xcf, 0xe7, 0xf7, 0x2e, 0xff, 0xd4, 0x94, 0x93, 0x68, 0x5e, 0xad, 0xef, 0xf1, 0xdf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7b, 0x78, 0xfd, 0x40, 0xf4, 0x04, 0x00, 0x00, +} + +func (this *ValidatorSigningInfo) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ValidatorSigningInfo) + if !ok { + that2, ok := that.(ValidatorSigningInfo) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Address != that1.Address { + return false + } + if this.StartHeight != that1.StartHeight { + return false + } + if this.IndexOffset != that1.IndexOffset { + return false + } + if !this.JailedUntil.Equal(that1.JailedUntil) { + return false + } + if this.Tombstoned != that1.Tombstoned { + return false + } + if this.MissedBlocksCounter != that1.MissedBlocksCounter { + return false + } + return true +} +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.SignedBlocksWindow != that1.SignedBlocksWindow { + return false + } + if !this.MinSignedPerWindow.Equal(that1.MinSignedPerWindow) { + return false + } + if this.DowntimeJailDuration != that1.DowntimeJailDuration { + return false + } + if !this.SlashFractionDoubleSign.Equal(that1.SlashFractionDoubleSign) { + return false + } + if !this.SlashFractionDowntime.Equal(that1.SlashFractionDowntime) { + return false + } + return true +} +func (m *ValidatorSigningInfo) 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 *ValidatorSigningInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValidatorSigningInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MissedBlocksCounter != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.MissedBlocksCounter)) + i-- + dAtA[i] = 0x30 + } + if m.Tombstoned { + i-- + if m.Tombstoned { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.JailedUntil, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.JailedUntil):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintSlashing(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x22 + if m.IndexOffset != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.IndexOffset)) + i-- + dAtA[i] = 0x18 + } + if m.StartHeight != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.StartHeight)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintSlashing(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.SlashFractionDowntime.Size() + i -= size + if _, err := m.SlashFractionDowntime.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSlashing(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.SlashFractionDoubleSign.Size() + i -= size + if _, err := m.SlashFractionDoubleSign.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSlashing(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + n2, err2 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.DowntimeJailDuration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.DowntimeJailDuration):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintSlashing(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x1a + { + size := m.MinSignedPerWindow.Size() + i -= size + if _, err := m.MinSignedPerWindow.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintSlashing(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if m.SignedBlocksWindow != 0 { + i = encodeVarintSlashing(dAtA, i, uint64(m.SignedBlocksWindow)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintSlashing(dAtA []byte, offset int, v uint64) int { + offset -= sovSlashing(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ValidatorSigningInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovSlashing(uint64(l)) + } + if m.StartHeight != 0 { + n += 1 + sovSlashing(uint64(m.StartHeight)) + } + if m.IndexOffset != 0 { + n += 1 + sovSlashing(uint64(m.IndexOffset)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.JailedUntil) + n += 1 + l + sovSlashing(uint64(l)) + if m.Tombstoned { + n += 2 + } + if m.MissedBlocksCounter != 0 { + n += 1 + sovSlashing(uint64(m.MissedBlocksCounter)) + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignedBlocksWindow != 0 { + n += 1 + sovSlashing(uint64(m.SignedBlocksWindow)) + } + l = m.MinSignedPerWindow.Size() + n += 1 + l + sovSlashing(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.DowntimeJailDuration) + n += 1 + l + sovSlashing(uint64(l)) + l = m.SlashFractionDoubleSign.Size() + n += 1 + l + sovSlashing(uint64(l)) + l = m.SlashFractionDowntime.Size() + n += 1 + l + sovSlashing(uint64(l)) + return n +} + +func sovSlashing(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSlashing(x uint64) (n int) { + return sovSlashing(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ValidatorSigningInfo) 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 ErrIntOverflowSlashing + } + 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: ValidatorSigningInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatorSigningInfo: 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 ErrIntOverflowSlashing + } + 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 ErrInvalidLengthSlashing + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field StartHeight", wireType) + } + m.StartHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.StartHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IndexOffset", wireType) + } + m.IndexOffset = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IndexOffset |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JailedUntil", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.JailedUntil, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Tombstoned", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Tombstoned = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MissedBlocksCounter", wireType) + } + m.MissedBlocksCounter = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MissedBlocksCounter |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSlashing(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSlashing + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowSlashing + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SignedBlocksWindow", wireType) + } + m.SignedBlocksWindow = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SignedBlocksWindow |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinSignedPerWindow", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinSignedPerWindow.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DowntimeJailDuration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.DowntimeJailDuration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashFractionDoubleSign", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SlashFractionDoubleSign.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SlashFractionDowntime", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSlashing + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthSlashing + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSlashing + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SlashFractionDowntime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSlashing(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSlashing + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSlashing(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, ErrIntOverflowSlashing + } + 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, ErrIntOverflowSlashing + } + 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, ErrIntOverflowSlashing + } + 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, ErrInvalidLengthSlashing + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSlashing + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSlashing + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSlashing = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSlashing = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSlashing = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/slashing/types/tx.pb.go b/x/slashing/types/tx.pb.go new file mode 100644 index 000000000000..ed6dbcd170ab --- /dev/null +++ b/x/slashing/types/tx.pb.go @@ -0,0 +1,573 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/slashing/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/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 + +// MsgUnjail defines the Msg/Unjail request type +type MsgUnjail struct { + ValidatorAddr string `protobuf:"bytes,1,opt,name=validator_addr,json=validatorAddr,proto3" json:"address" yaml:"address"` +} + +func (m *MsgUnjail) Reset() { *m = MsgUnjail{} } +func (m *MsgUnjail) String() string { return proto.CompactTextString(m) } +func (*MsgUnjail) ProtoMessage() {} +func (*MsgUnjail) Descriptor() ([]byte, []int) { + return fileDescriptor_3c5611c0c4a59d9d, []int{0} +} +func (m *MsgUnjail) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnjail) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnjail.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 *MsgUnjail) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnjail.Merge(m, src) +} +func (m *MsgUnjail) XXX_Size() int { + return m.Size() +} +func (m *MsgUnjail) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnjail.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnjail proto.InternalMessageInfo + +// MsgUnjailResponse defines the Msg/Unjail response type +type MsgUnjailResponse struct { +} + +func (m *MsgUnjailResponse) Reset() { *m = MsgUnjailResponse{} } +func (m *MsgUnjailResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUnjailResponse) ProtoMessage() {} +func (*MsgUnjailResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3c5611c0c4a59d9d, []int{1} +} +func (m *MsgUnjailResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnjailResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnjailResponse.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 *MsgUnjailResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnjailResponse.Merge(m, src) +} +func (m *MsgUnjailResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUnjailResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnjailResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnjailResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUnjail)(nil), "cosmos.slashing.v1beta1.MsgUnjail") + proto.RegisterType((*MsgUnjailResponse)(nil), "cosmos.slashing.v1beta1.MsgUnjailResponse") +} + +func init() { proto.RegisterFile("cosmos/slashing/v1beta1/tx.proto", fileDescriptor_3c5611c0c4a59d9d) } + +var fileDescriptor_3c5611c0c4a59d9d = []byte{ + // 269 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x2f, 0xce, 0x49, 0x2c, 0xce, 0xc8, 0xcc, 0x4b, 0xd7, 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, 0x95, 0xa2, 0xb9, 0x38, 0x7d, 0x8b, 0xd3, 0x43, 0xf3, 0xb2, 0x12, 0x33, 0x73, + 0x84, 0x5c, 0xb8, 0xf8, 0xca, 0x12, 0x73, 0x32, 0x53, 0x12, 0x4b, 0xf2, 0x8b, 0xe2, 0x13, 0x53, + 0x52, 0x8a, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x9d, 0x64, 0x5f, 0xdd, 0x93, 0x67, 0x07, 0xf1, + 0x53, 0x8b, 0x8b, 0x3f, 0xdd, 0x93, 0xe7, 0xab, 0x4c, 0xcc, 0xcd, 0xb1, 0x52, 0x82, 0x0a, 0x28, + 0x05, 0xf1, 0xc2, 0x35, 0x39, 0xa6, 0xa4, 0x14, 0x59, 0x71, 0x74, 0x2c, 0x90, 0x67, 0x98, 0xb1, + 0x40, 0x9e, 0x51, 0x49, 0x98, 0x4b, 0x10, 0x6e, 0x78, 0x50, 0x6a, 0x71, 0x41, 0x7e, 0x5e, 0x71, + 0xaa, 0x51, 0x3c, 0x17, 0xb3, 0x6f, 0x71, 0xba, 0x50, 0x04, 0x17, 0x1b, 0xd4, 0x56, 0x25, 0x3d, + 0x1c, 0x4e, 0xd6, 0x83, 0x6b, 0x96, 0xd2, 0x22, 0xac, 0x06, 0x66, 0x81, 0x93, 0xf7, 0x8a, 0x47, + 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, + 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9b, 0x9e, 0x59, + 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x0d, 0x4c, 0x08, 0xa5, 0x5b, 0x9c, 0x92, + 0xad, 0x5f, 0x81, 0x08, 0xd9, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0x70, 0x30, 0x19, 0x03, + 0x02, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xa7, 0xdc, 0xcf, 0x79, 0x01, 0x00, 0x00, +} + +func (this *MsgUnjail) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgUnjail) + if !ok { + that2, ok := that.(MsgUnjail) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ValidatorAddr != that1.ValidatorAddr { + return false + } + return true +} +func (this *MsgUnjailResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*MsgUnjailResponse) + if !ok { + that2, ok := that.(MsgUnjailResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + return true +} + +// 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 { + // Unjail defines a method for unjailing a jailed validator, thus returning + // them into the bonded validator set, so they can begin receiving provisions + // and rewards again. + Unjail(ctx context.Context, in *MsgUnjail, opts ...grpc.CallOption) (*MsgUnjailResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) Unjail(ctx context.Context, in *MsgUnjail, opts ...grpc.CallOption) (*MsgUnjailResponse, error) { + out := new(MsgUnjailResponse) + err := c.cc.Invoke(ctx, "/cosmos.slashing.v1beta1.Msg/Unjail", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // Unjail defines a method for unjailing a jailed validator, thus returning + // them into the bonded validator set, so they can begin receiving provisions + // and rewards again. + Unjail(context.Context, *MsgUnjail) (*MsgUnjailResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) Unjail(ctx context.Context, req *MsgUnjail) (*MsgUnjailResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Unjail not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_Unjail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUnjail) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Unjail(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.slashing.v1beta1.Msg/Unjail", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Unjail(ctx, req.(*MsgUnjail)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.slashing.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Unjail", + Handler: _Msg_Unjail_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/slashing/v1beta1/tx.proto", +} + +func (m *MsgUnjail) 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 *MsgUnjail) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnjail) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUnjailResponse) 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 *MsgUnjailResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnjailResponse) 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 *MsgUnjail) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgUnjailResponse) 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 *MsgUnjail) 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: MsgUnjail: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnjail: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *MsgUnjailResponse) 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: MsgUnjailResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnjailResponse: 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/staking/abci.go b/x/staking/abci.go index 6e4b1067e923..28253b5bd3fd 100644 --- a/x/staking/abci.go +++ b/x/staking/abci.go @@ -1,19 +1,27 @@ package staking import ( + "time" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) // BeginBlocker will persist the current header and validator set as a historical entry // and prune the oldest entry based on the HistoricalEntries parameter func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + k.TrackHistoricalInfo(ctx) } // Called every block, update validator set func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) + return k.BlockValidatorUpdates(ctx) } diff --git a/x/staking/alias.go b/x/staking/alias.go deleted file mode 100644 index 51c7eecc7087..000000000000 --- a/x/staking/alias.go +++ /dev/null @@ -1,241 +0,0 @@ -package staking - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/staking/exported" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -const ( - DefaultParamspace = keeper.DefaultParamspace - ModuleName = types.ModuleName - StoreKey = types.StoreKey - TStoreKey = types.TStoreKey - QuerierRoute = types.QuerierRoute - RouterKey = types.RouterKey - DefaultUnbondingTime = types.DefaultUnbondingTime - DefaultMaxValidators = types.DefaultMaxValidators - DefaultMaxEntries = types.DefaultMaxEntries - NotBondedPoolName = types.NotBondedPoolName - BondedPoolName = types.BondedPoolName - QueryValidators = types.QueryValidators - QueryValidator = types.QueryValidator - QueryDelegatorDelegations = types.QueryDelegatorDelegations - QueryDelegatorUnbondingDelegations = types.QueryDelegatorUnbondingDelegations - QueryRedelegations = types.QueryRedelegations - QueryValidatorDelegations = types.QueryValidatorDelegations - QueryValidatorRedelegations = types.QueryValidatorRedelegations - QueryValidatorUnbondingDelegations = types.QueryValidatorUnbondingDelegations - QueryDelegation = types.QueryDelegation - QueryUnbondingDelegation = types.QueryUnbondingDelegation - QueryDelegatorValidators = types.QueryDelegatorValidators - QueryDelegatorValidator = types.QueryDelegatorValidator - QueryPool = types.QueryPool - QueryParameters = types.QueryParameters - QueryHistoricalInfo = types.QueryHistoricalInfo - MaxMonikerLength = types.MaxMonikerLength - MaxIdentityLength = types.MaxIdentityLength - MaxWebsiteLength = types.MaxWebsiteLength - MaxDetailsLength = types.MaxDetailsLength - DoNotModifyDesc = types.DoNotModifyDesc -) - -var ( - // functions aliases - RegisterInvariants = keeper.RegisterInvariants - AllInvariants = keeper.AllInvariants - ModuleAccountInvariants = keeper.ModuleAccountInvariants - NonNegativePowerInvariant = keeper.NonNegativePowerInvariant - PositiveDelegationInvariant = keeper.PositiveDelegationInvariant - DelegatorSharesInvariant = keeper.DelegatorSharesInvariant - NewKeeper = keeper.NewKeeper - ParamKeyTable = keeper.ParamKeyTable - NewQuerier = keeper.NewQuerier - RegisterCodec = types.RegisterCodec - NewCommissionRates = types.NewCommissionRates - NewCommission = types.NewCommission - NewCommissionWithTime = types.NewCommissionWithTime - NewDelegation = types.NewDelegation - MustMarshalDelegation = types.MustMarshalDelegation - MustUnmarshalDelegation = types.MustUnmarshalDelegation - UnmarshalDelegation = types.UnmarshalDelegation - NewUnbondingDelegation = types.NewUnbondingDelegation - NewUnbondingDelegationEntry = types.NewUnbondingDelegationEntry - MustMarshalUBD = types.MustMarshalUBD - MustUnmarshalUBD = types.MustUnmarshalUBD - UnmarshalUBD = types.UnmarshalUBD - NewRedelegation = types.NewRedelegation - NewRedelegationEntry = types.NewRedelegationEntry - MustMarshalRED = types.MustMarshalRED - MustUnmarshalRED = types.MustUnmarshalRED - UnmarshalRED = types.UnmarshalRED - NewDelegationResp = types.NewDelegationResp - NewRedelegationResponse = types.NewRedelegationResponse - NewRedelegationEntryResponse = types.NewRedelegationEntryResponse - NewHistoricalInfo = types.NewHistoricalInfo - MustMarshalHistoricalInfo = types.MustMarshalHistoricalInfo - MustUnmarshalHistoricalInfo = types.MustUnmarshalHistoricalInfo - UnmarshalHistoricalInfo = types.UnmarshalHistoricalInfo - ErrEmptyValidatorAddr = types.ErrEmptyValidatorAddr - ErrBadValidatorAddr = types.ErrBadValidatorAddr - ErrNoValidatorFound = types.ErrNoValidatorFound - ErrValidatorOwnerExists = types.ErrValidatorOwnerExists - ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists - ErrValidatorPubKeyTypeNotSupported = types.ErrValidatorPubKeyTypeNotSupported - ErrValidatorJailed = types.ErrValidatorJailed - ErrBadRemoveValidator = types.ErrBadRemoveValidator - ErrCommissionNegative = types.ErrCommissionNegative - ErrCommissionHuge = types.ErrCommissionHuge - ErrCommissionGTMaxRate = types.ErrCommissionGTMaxRate - ErrCommissionUpdateTime = types.ErrCommissionUpdateTime - ErrCommissionChangeRateNegative = types.ErrCommissionChangeRateNegative - ErrCommissionChangeRateGTMaxRate = types.ErrCommissionChangeRateGTMaxRate - ErrCommissionGTMaxChangeRate = types.ErrCommissionGTMaxChangeRate - ErrSelfDelegationBelowMinimum = types.ErrSelfDelegationBelowMinimum - ErrMinSelfDelegationInvalid = types.ErrMinSelfDelegationInvalid - ErrMinSelfDelegationDecreased = types.ErrMinSelfDelegationDecreased - ErrEmptyDelegatorAddr = types.ErrEmptyDelegatorAddr - ErrBadDenom = types.ErrBadDenom - ErrBadDelegationAddr = types.ErrBadDelegationAddr - ErrBadDelegationAmount = types.ErrBadDelegationAmount - ErrNoDelegation = types.ErrNoDelegation - ErrBadDelegatorAddr = types.ErrBadDelegatorAddr - ErrNoDelegatorForAddress = types.ErrNoDelegatorForAddress - ErrInsufficientShares = types.ErrInsufficientShares - ErrDelegationValidatorEmpty = types.ErrDelegationValidatorEmpty - ErrNotEnoughDelegationShares = types.ErrNotEnoughDelegationShares - ErrBadSharesAmount = types.ErrBadSharesAmount - ErrBadSharesPercent = types.ErrBadSharesPercent - ErrNotMature = types.ErrNotMature - ErrNoUnbondingDelegation = types.ErrNoUnbondingDelegation - ErrMaxUnbondingDelegationEntries = types.ErrMaxUnbondingDelegationEntries - ErrBadRedelegationAddr = types.ErrBadRedelegationAddr - ErrNoRedelegation = types.ErrNoRedelegation - ErrSelfRedelegation = types.ErrSelfRedelegation - ErrTinyRedelegationAmount = types.ErrTinyRedelegationAmount - ErrBadRedelegationDst = types.ErrBadRedelegationDst - ErrTransitiveRedelegation = types.ErrTransitiveRedelegation - ErrMaxRedelegationEntries = types.ErrMaxRedelegationEntries - ErrDelegatorShareExRateInvalid = types.ErrDelegatorShareExRateInvalid - ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven - ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven - ErrInvalidHistoricalInfo = types.ErrInvalidHistoricalInfo - ErrNoHistoricalInfo = types.ErrNoHistoricalInfo - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - NewMultiStakingHooks = types.NewMultiStakingHooks - GetValidatorKey = types.GetValidatorKey - GetValidatorByConsAddrKey = types.GetValidatorByConsAddrKey - AddressFromLastValidatorPowerKey = types.AddressFromLastValidatorPowerKey - GetValidatorsByPowerIndexKey = types.GetValidatorsByPowerIndexKey - GetLastValidatorPowerKey = types.GetLastValidatorPowerKey - ParseValidatorPowerRankKey = types.ParseValidatorPowerRankKey - GetValidatorQueueTimeKey = types.GetValidatorQueueTimeKey - GetDelegationKey = types.GetDelegationKey - GetDelegationsKey = types.GetDelegationsKey - GetUBDKey = types.GetUBDKey - GetUBDByValIndexKey = types.GetUBDByValIndexKey - GetUBDKeyFromValIndexKey = types.GetUBDKeyFromValIndexKey - GetUBDsKey = types.GetUBDsKey - GetUBDsByValIndexKey = types.GetUBDsByValIndexKey - GetUnbondingDelegationTimeKey = types.GetUnbondingDelegationTimeKey - GetREDKey = types.GetREDKey - GetREDByValSrcIndexKey = types.GetREDByValSrcIndexKey - GetREDByValDstIndexKey = types.GetREDByValDstIndexKey - GetREDKeyFromValSrcIndexKey = types.GetREDKeyFromValSrcIndexKey - GetREDKeyFromValDstIndexKey = types.GetREDKeyFromValDstIndexKey - GetRedelegationTimeKey = types.GetRedelegationTimeKey - GetREDsKey = types.GetREDsKey - GetREDsFromValSrcIndexKey = types.GetREDsFromValSrcIndexKey - GetREDsToValDstIndexKey = types.GetREDsToValDstIndexKey - GetREDsByDelToValDstIndexKey = types.GetREDsByDelToValDstIndexKey - GetHistoricalInfoKey = types.GetHistoricalInfoKey - NewMsgCreateValidator = types.NewMsgCreateValidator - NewMsgEditValidator = types.NewMsgEditValidator - NewMsgDelegate = types.NewMsgDelegate - NewMsgBeginRedelegate = types.NewMsgBeginRedelegate - NewMsgUndelegate = types.NewMsgUndelegate - NewParams = types.NewParams - DefaultParams = types.DefaultParams - MustUnmarshalParams = types.MustUnmarshalParams - UnmarshalParams = types.UnmarshalParams - NewPool = types.NewPool - NewQueryDelegatorParams = types.NewQueryDelegatorParams - NewQueryValidatorParams = types.NewQueryValidatorParams - NewQueryBondsParams = types.NewQueryBondsParams - NewQueryRedelegationParams = types.NewQueryRedelegationParams - NewQueryValidatorsParams = types.NewQueryValidatorsParams - NewQueryHistoricalInfoParams = types.NewQueryHistoricalInfoParams - NewValidator = types.NewValidator - MustMarshalValidator = types.MustMarshalValidator - MustUnmarshalValidator = types.MustUnmarshalValidator - UnmarshalValidator = types.UnmarshalValidator - NewDescription = types.NewDescription - - // variable aliases - ModuleCdc = types.ModuleCdc - LastValidatorPowerKey = types.LastValidatorPowerKey - LastTotalPowerKey = types.LastTotalPowerKey - ValidatorsKey = types.ValidatorsKey - ValidatorsByConsAddrKey = types.ValidatorsByConsAddrKey - ValidatorsByPowerIndexKey = types.ValidatorsByPowerIndexKey - DelegationKey = types.DelegationKey - UnbondingDelegationKey = types.UnbondingDelegationKey - UnbondingDelegationByValIndexKey = types.UnbondingDelegationByValIndexKey - RedelegationKey = types.RedelegationKey - RedelegationByValSrcIndexKey = types.RedelegationByValSrcIndexKey - RedelegationByValDstIndexKey = types.RedelegationByValDstIndexKey - UnbondingQueueKey = types.UnbondingQueueKey - RedelegationQueueKey = types.RedelegationQueueKey - ValidatorQueueKey = types.ValidatorQueueKey - HistoricalInfoKey = types.HistoricalInfoKey - KeyUnbondingTime = types.KeyUnbondingTime - KeyMaxValidators = types.KeyMaxValidators - KeyMaxEntries = types.KeyMaxEntries - KeyBondDenom = types.KeyBondDenom -) - -type ( - Keeper = keeper.Keeper - Commission = types.Commission - CommissionRates = types.CommissionRates - DVPair = types.DVPair - DVVTriplet = types.DVVTriplet - Delegation = types.Delegation - Delegations = types.Delegations - UnbondingDelegation = types.UnbondingDelegation - UnbondingDelegationEntry = types.UnbondingDelegationEntry - UnbondingDelegations = types.UnbondingDelegations - Redelegation = types.Redelegation - RedelegationEntry = types.RedelegationEntry - Redelegations = types.Redelegations - HistoricalInfo = types.HistoricalInfo - DelegationResponse = types.DelegationResponse - DelegationResponses = types.DelegationResponses - RedelegationResponse = types.RedelegationResponse - RedelegationEntryResponse = types.RedelegationEntryResponse - RedelegationResponses = types.RedelegationResponses - GenesisState = types.GenesisState - LastValidatorPower = types.LastValidatorPower - MultiStakingHooks = types.MultiStakingHooks - MsgCreateValidator = types.MsgCreateValidator - MsgEditValidator = types.MsgEditValidator - MsgDelegate = types.MsgDelegate - MsgBeginRedelegate = types.MsgBeginRedelegate - MsgUndelegate = types.MsgUndelegate - Params = types.Params - Pool = types.Pool - QueryDelegatorParams = types.QueryDelegatorParams - QueryValidatorParams = types.QueryValidatorParams - QueryBondsParams = types.QueryBondsParams - QueryRedelegationParams = types.QueryRedelegationParams - QueryValidatorsParams = types.QueryValidatorsParams - QueryHistoricalInfoParams = types.QueryHistoricalInfoParams - Validator = types.Validator - Validators = types.Validators - Description = types.Description - DelegationI = exported.DelegationI - ValidatorI = exported.ValidatorI -) diff --git a/x/staking/app_test.go b/x/staking/app_test.go index a56923113380..207ac03e0cf6 100644 --- a/x/staking/app_test.go +++ b/x/staking/app_test.go @@ -1,108 +1,34 @@ -package staking +package staking_test import ( "testing" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + 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/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/mock" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/x/supply" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" ) -// getMockApp returns an initialized mock application for this module. -func getMockApp(t *testing.T) (*mock.App, Keeper) { - mApp := mock.NewApp() - - RegisterCodec(mApp.Cdc) - supply.RegisterCodec(mApp.Cdc) - - keyStaking := sdk.NewKVStoreKey(StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - - feeCollector := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollector.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - types.NotBondedPoolName: {supply.Burner, supply.Staking}, - types.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bankKeeper, maccPerms) - keeper := NewKeeper(mApp.Cdc, keyStaking, supplyKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace)) - - mApp.Router().AddRoute(RouterKey, NewHandler(keeper)) - mApp.SetEndBlocker(getEndBlocker(keeper)) - mApp.SetInitChainer(getInitChainer(mApp, keeper, mApp.AccountKeeper, supplyKeeper, - []supplyexported.ModuleAccountI{feeCollector, notBondedPool, bondPool})) - - require.NoError(t, mApp.CompleteSetup(keyStaking, keySupply)) - return mApp, keeper -} - -// getEndBlocker returns a staking endblocker. -func getEndBlocker(keeper Keeper) sdk.EndBlocker { - return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - validatorUpdates := EndBlocker(ctx, keeper) - - return abci.ResponseEndBlock{ - ValidatorUpdates: validatorUpdates, - } - } -} - -// getInitChainer initializes the chainer of the mock app and sets the genesis -// state. It returns an empty ResponseInitChain. -func getInitChainer(mapp *mock.App, keeper Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper, - blacklistedAddrs []supplyexported.ModuleAccountI) sdk.InitChainer { - return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - mapp.InitChainer(ctx, req) - - // set module accounts - for _, macc := range blacklistedAddrs { - supplyKeeper.SetModuleAccount(ctx, macc) - } - - stakingGenesis := DefaultGenesisState() - validators := InitGenesis(ctx, keeper, accountKeeper, supplyKeeper, stakingGenesis) - return abci.ResponseInitChain{ - Validators: validators, - } - } -} - -//__________________________________________________________________________________________ - -func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper, - addr sdk.ValAddress, expFound bool) Validator { - - ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - validator, found := keeper.GetValidator(ctxCheck, addr) +func checkValidator(t *testing.T, app *simapp.SimApp, addr sdk.ValAddress, expFound bool) types.Validator { + ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) + validator, found := app.StakingKeeper.GetValidator(ctxCheck, addr) require.Equal(t, expFound, found) return validator } func checkDelegation( - t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr sdk.AccAddress, + t *testing.T, app *simapp.SimApp, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, expFound bool, expShares sdk.Dec, ) { - ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) - delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr) + ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) + delegation, found := app.StakingKeeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr) if expFound { require.True(t, found) require.True(sdk.DecEq(t, expShares, delegation.Shares)) @@ -114,75 +40,84 @@ func checkDelegation( } func TestStakingMsgs(t *testing.T) { - mApp, keeper := getMockApp(t) - genTokens := sdk.TokensFromConsensusPower(42) bondTokens := sdk.TokensFromConsensusPower(10) genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) - acc1 := &auth.BaseAccount{ - Address: addr1, - Coins: sdk.Coins{genCoin}, + acc1 := &authtypes.BaseAccount{Address: addr1.String()} + acc2 := &authtypes.BaseAccount{Address: addr2.String()} + accs := authtypes.GenesisAccounts{acc1, acc2} + balances := []banktypes.Balance{ + { + Address: addr1.String(), + Coins: sdk.Coins{genCoin}, + }, + { + Address: addr2.String(), + Coins: sdk.Coins{genCoin}, + }, } - acc2 := &auth.BaseAccount{ - Address: addr2, - Coins: sdk.Coins{genCoin}, - } - accs := []authexported.Account{acc1, acc2} - mock.SetGenesis(mApp, accs) - mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin}) - mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) + app := simapp.SetupWithGenesisAccounts(accs, balances...) + simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin}) + simapp.CheckBalance(t, app, addr2, sdk.Coins{genCoin}) // create validator - description := NewDescription("foo_moniker", "", "", "", "") - createValidatorMsg := NewMsgCreateValidator( - sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionRates, sdk.OneInt(), + description := types.NewDescription("foo_moniker", "", "", "", "") + createValidatorMsg, err := types.NewMsgCreateValidator( + sdk.ValAddress(addr1), valKey.PubKey(), bondCoin, description, commissionRates, sdk.OneInt(), ) + require.NoError(t, err) - header := abci.Header{Height: mApp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) - mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) + header := tmproto.Header{Height: app.LastBlockHeight() + 1} + txGen := simapp.MakeTestEncodingConfig().TxConfig + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) + require.NoError(t, err) + simapp.CheckBalance(t, app, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) - validator := checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) - require.Equal(t, sdk.ValAddress(addr1), validator.OperatorAddress) - require.Equal(t, sdk.Bonded, validator.Status) + validator := checkValidator(t, app, sdk.ValAddress(addr1), true) + require.Equal(t, sdk.ValAddress(addr1).String(), validator.OperatorAddress) + require.Equal(t, types.Bonded, validator.Status) require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) // edit the validator - description = NewDescription("bar_moniker", "", "", "", "") - editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil) + description = types.NewDescription("bar_moniker", "", "", "", "") + editValidatorMsg := types.NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil) - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{1}, true, true, priv1) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{editValidatorMsg}, "", []uint64{0}, []uint64{1}, true, true, priv1) + require.NoError(t, err) - validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) + validator = checkValidator(t, app, sdk.ValAddress(addr1), true) require.Equal(t, description, validator.Description) // delegate - mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) - delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) + simapp.CheckBalance(t, app, addr2, sdk.Coins{genCoin}) + delegateMsg := types.NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) + + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{delegateMsg}, "", []uint64{1}, []uint64{0}, true, true, priv2) + require.NoError(t, err) - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{delegateMsg}, []uint64{1}, []uint64{0}, true, true, priv2) - mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) - checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, bondTokens.ToDec()) + simapp.CheckBalance(t, app, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) + checkDelegation(t, app, addr2, sdk.ValAddress(addr1), true, bondTokens.ToDec()) // begin unbonding - beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondCoin) - header = abci.Header{Height: mApp.LastBlockHeight() + 1} - mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, []uint64{1}, []uint64{1}, true, true, priv2) + beginUnbondingMsg := types.NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondCoin) + header = tmproto.Header{Height: app.LastBlockHeight() + 1} + _, _, err = simapp.SignCheckDeliver(t, txGen, app.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, "", []uint64{1}, []uint64{1}, true, true, priv2) + require.NoError(t, err) // delegation should exist anymore - checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) + checkDelegation(t, app, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) // balance should be the same because bonding not yet complete - mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) + simapp.CheckBalance(t, app, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) } diff --git a/x/staking/client/cli/cli_test.go b/x/staking/client/cli/cli_test.go new file mode 100644 index 000000000000..f1ef9edc6b8a --- /dev/null +++ b/x/staking/client/cli/cli_test.go @@ -0,0 +1,1343 @@ +// +build norace + +package cli_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/proto/tendermint/crypto" + "github.com/tendermint/tendermint/rpc/client/http" + + "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/crypto/keys/ed25519" + 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/query" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/client/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +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) + + unbond, err := sdk.ParseCoinNormalized("10stake") + s.Require().NoError(err) + + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + // redelegate + _, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // unbonding + _, err = stakingtestutil.MsgUnbondExec(val.ClientCtx, val.Address, val.ValAddress, unbond) + s.Require().NoError(err) + _, 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) TestNewCreateValidatorCmd() { + val := s.network.Validators[0] + + consPrivKey := ed25519.GenPrivKey() + consPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, consPrivKey.PubKey()) + s.Require().NoError(err) + + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewValidator", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + _, 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) + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "invalid transaction (missing amount)", + []string{ + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr), + 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, nil, 0, + }, + { + "invalid transaction (missing pubkey)", + []string{ + fmt.Sprintf("--%s=100stake", cli.FlagAmount), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr), + 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, nil, 0, + }, + { + "invalid transaction (missing moniker)", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKey), + fmt.Sprintf("--%s=100stake", cli.FlagAmount), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr), + 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, nil, 0, + }, + { + "valid transaction", + []string{ + fmt.Sprintf("--%s=%s", cli.FlagPubKey, consPubKey), + fmt.Sprintf("--%s=100stake", cli.FlagAmount), + fmt.Sprintf("--%s=NewValidator", cli.FlagMoniker), + fmt.Sprintf("--%s=AFAF00C4", cli.FlagIdentity), + fmt.Sprintf("--%s=https://newvalidator.io", cli.FlagWebsite), + fmt.Sprintf("--%s=contact@newvalidator.io", cli.FlagSecurityContact), + fmt.Sprintf("--%s='Hey, I am a new validator. Please delegate!'", cli.FlagDetails), + fmt.Sprintf("--%s=0.5", cli.FlagCommissionRate), + fmt.Sprintf("--%s=1.0", cli.FlagCommissionMaxRate), + fmt.Sprintf("--%s=0.1", cli.FlagCommissionMaxChangeRate), + fmt.Sprintf("--%s=1", cli.FlagMinSelfDelegation), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr), + 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, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewCreateValidatorCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + 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) TestGetCmdQueryValidator() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + expectErr bool + }{ + { + "with invalid address ", + []string{"somethinginvalidaddress", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + true, + }, + { + "with valid and not existing address", + []string{"cosmosvaloper15jkng8hytwt22lllv6mw4k89qkqehtahd84ptu", fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + true, + }, + { + "happy case", + []string{fmt.Sprintf("%s", val.ValAddress), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + false, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidator() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + s.Require().NotEqual("internal", err.Error()) + } else { + var result types.Validator + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &result)) + s.Require().Equal(val.ValAddress.String(), result.OperatorAddress) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidators() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + minValidatorCount int + }{ + { + "one validator case", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagLimit), + }, + 1, + }, + { + "multi validator case", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + len(s.network.Validators), + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidators() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + + var result types.QueryValidatorsResponse + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &result)) + s.Require().Equal(tc.minValidatorCount, len(result.Validators)) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryDelegation() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "with wrong delegator address", + []string{ + "wrongDelAddr", + val2.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, nil, nil, + }, + { + "with wrong validator address", + []string{ + val.Address.String(), + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, nil, nil, + }, + { + "with json output", + []string{ + val.Address.String(), + val2.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + &types.DelegationResponse{}, + &types.DelegationResponse{ + Delegation: types.Delegation{ + DelegatorAddress: val.Address.String(), + ValidatorAddress: val2.ValAddress.String(), + Shares: sdk.NewDec(10), + }, + Balance: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)), + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegation() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryDelegations() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "with no delegator address", + []string{}, + true, nil, nil, + }, + { + "with wrong delegator address", + []string{"wrongDelAddr"}, + true, nil, nil, + }, + { + "valid request (height specific)", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &types.QueryDelegatorDelegationsResponse{}, + &types.QueryDelegatorDelegationsResponse{ + DelegationResponses: types.DelegationResponses{ + types.NewDelegationResp(val.Address, val.ValAddress, sdk.NewDecFromInt(cli.DefaultTokens), sdk.NewCoin(sdk.DefaultBondDenom, cli.DefaultTokens)), + }, + Pagination: &query.PageResponse{}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryDelegationsTo() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expErr bool + respType proto.Message + expected proto.Message + }{ + { + "with no validator address", + []string{}, + true, nil, nil, + }, + { + "wrong validator address", + []string{"wrongValAddr"}, + true, nil, nil, + }, + { + "valid request(height specific)", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + false, + &types.QueryValidatorDelegationsResponse{}, + &types.QueryValidatorDelegationsResponse{ + DelegationResponses: types.DelegationResponses{ + types.NewDelegationResp(val.Address, val.ValAddress, sdk.NewDecFromInt(cli.DefaultTokens), sdk.NewCoin(sdk.DefaultBondDenom, cli.DefaultTokens)), + }, + Pagination: &query.PageResponse{}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryDelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) + s.Require().Equal(tc.expected.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryUnbondingDelegations() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongDelAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryUnbondingDelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubds types.QueryDelegatorUnbondingDelegationsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &ubds) + + s.Require().NoError(err) + s.Require().Len(ubds.UnbondingResponses, 1) + s.Require().Equal(ubds.UnbondingResponses[0].DelegatorAddress, val.Address.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryUnbondingDelegation() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongDelAddr", + val.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong validator address", + []string{ + val.Address.String(), + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.Address.String(), + val.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryUnbondingDelegation() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubd types.UnbondingDelegation + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &ubd) + s.Require().NoError(err) + s.Require().Equal(ubd.DelegatorAddress, val.Address.String()) + s.Require().Equal(ubd.ValidatorAddress, val.ValAddress.String()) + s.Require().Len(ubd.Entries, 1) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorUnbondingDelegations() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong validator address", + []string{ + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorUnbondingDelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var ubds types.QueryValidatorUnbondingDelegationsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &ubds) + + s.Require().NoError(err) + s.Require().Len(ubds.UnbondingResponses, 1) + s.Require().Equal(ubds.UnbondingResponses[0].DelegatorAddress, val.Address.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryRedelegations() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongdeladdr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.Address.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryRedelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &redelegations) + + s.Require().NoError(err) + + s.Require().Len(redelegations.RedelegationResponses, 1) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.DelegatorAddress, val.Address.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorSrcAddress, val.ValAddress.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorDstAddress, val2.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryRedelegation() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong delegator address", + []string{ + "wrongdeladdr", + val.ValAddress.String(), + val2.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong source validator address address", + []string{ + val.Address.String(), + "wrongSrcValAddress", + val2.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "wrong destination validator address address", + []string{ + val.Address.String(), + val.ValAddress.String(), + "wrongDestValAddress", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.Address.String(), + val.ValAddress.String(), + val2.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryRedelegation() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &redelegations) + s.Require().NoError(err) + + s.Require().Len(redelegations.RedelegationResponses, 1) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.DelegatorAddress, val.Address.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorSrcAddress, val.ValAddress.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorDstAddress, val2.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryRedelegationsFrom() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + testCases := []struct { + name string + args []string + expErr bool + }{ + { + "wrong validator address", + []string{ + "wrongValAddr", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + val.ValAddress.String(), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryValidatorRedelegations() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.expErr { + s.Require().Error(err) + } else { + var redelegations types.QueryRedelegationsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &redelegations) + + s.Require().NoError(err) + + s.Require().Len(redelegations.RedelegationResponses, 1) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.DelegatorAddress, val.Address.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorSrcAddress, val.ValAddress.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorDstAddress, val2.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryHistoricalInfo() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + error bool + }{ + { + "wrong height", + []string{ + "-1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + { + "valid request", + []string{ + "1", + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryHistoricalInfo() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + + if tc.error { + s.Require().Error(err) + } else { + var historical_info types.HistoricalInfo + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &historical_info) + s.Require().NoError(err) + s.Require().NotNil(historical_info) + } + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryParams() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "with text output", + []string{fmt.Sprintf("--%s=text", tmcli.OutputFlag)}, + `bond_denom: stake +historical_entries: 10000 +max_entries: 7 +max_validators: 100 +unbonding_time: 1814400s`, + }, + { + "with json output", + []string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + `{"unbonding_time":"1814400s","max_validators":100,"max_entries":7,"historical_entries":10000,"bond_denom":"stake"}`, + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryParams() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestGetCmdQueryPool() { + val := s.network.Validators[0] + testCases := []struct { + name string + args []string + expectedOutput string + }{ + { + "with text", + []string{ + fmt.Sprintf("--%s=text", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + fmt.Sprintf(`bonded_tokens: "%s" +not_bonded_tokens: "0"`, cli.DefaultTokens.Mul(sdk.NewInt(2)).String()), + }, + { + "with json", + []string{ + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + fmt.Sprintf("--%s=1", flags.FlagHeight), + }, + fmt.Sprintf(`{"not_bonded_tokens":"0","bonded_tokens":"%s"}`, cli.DefaultTokens.Mul(sdk.NewInt(2)).String()), + }, + } + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := cli.GetCmdQueryPool() + clientCtx := val.ClientCtx + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + s.Require().Equal(tc.expectedOutput, strings.TrimSpace(out.String())) + }) + } +} + +func (s *IntegrationTestSuite) TestNewCmdEditValidator() { + val := s.network.Validators[0] + + details := "bio" + identity := "test identity" + securityContact := "test contact" + website := "https://test.com" + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "with no edit flag (since all are optional)", + []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, "with wrong from address"), + 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, nil, 0, + }, + { + "with no edit flag (since all are optional)", + []string{ + 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, &sdk.TxResponse{}, 0, + }, + { + "edit validator details", + []string{ + fmt.Sprintf("--details=%s", details), + 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, &sdk.TxResponse{}, 0, + }, + { + "edit validator identity", + []string{ + fmt.Sprintf("--identity=%s", identity), + 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, &sdk.TxResponse{}, 0, + }, + { + "edit validator security-contact", + []string{ + fmt.Sprintf("--security-contact=%s", securityContact), + 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, &sdk.TxResponse{}, 0, + }, + { + "edit validator website", + []string{ + fmt.Sprintf("--website=%s", website), + 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, &sdk.TxResponse{}, 0, + }, + { + "with all edit flags", + []string{ + fmt.Sprintf("--details=%s", details), + fmt.Sprintf("--identity=%s", identity), + fmt.Sprintf("--security-contact=%s", securityContact), + fmt.Sprintf("--website=%s", website), + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewEditValidatorCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + 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) TestNewCmdDelegate() { + val := s.network.Validators[0] + + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + _, 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) + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "without delegate amount", + []string{ + val.ValAddress.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.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, nil, 0, + }, + { + "without validator address", + []string{ + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.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, nil, 0, + }, + { + "valid transaction of delegate", + []string{ + val.ValAddress.String(), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.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, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewDelegateCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + 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) TestNewCmdRedelegate() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "without amount", + []string{ + val.ValAddress.String(), // src-validator-addr + val2.ValAddress.String(), // dst-validator-addr + 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, nil, 0, + }, + { + "with wrong source validator address", + []string{ + `cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj`, // src-validator-addr + val2.ValAddress.String(), // dst-validator-addr + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), // amount + 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, &sdk.TxResponse{}, 4, + }, + { + "with wrong destination validator address", + []string{ + val.ValAddress.String(), // dst-validator-addr + `cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj`, // src-validator-addr + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), // amount + 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, &sdk.TxResponse{}, 39, + }, + { + "valid transaction of delegate", + []string{ + val.ValAddress.String(), // src-validator-addr + val2.ValAddress.String(), // dst-validator-addr + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), // amount + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%s", flags.FlagGas, "auto"), + 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, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewRedelegateCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + 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) TestNewCmdUnbond() { + val := s.network.Validators[0] + + testCases := []struct { + name string + args []string + expectErr bool + respType proto.Message + expectedCode uint32 + }{ + { + "Without unbond amount", + []string{ + val.ValAddress.String(), + 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, nil, 0, + }, + { + "Without validator address", + []string{ + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), + 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, nil, 0, + }, + { + "valid transaction of unbond", + []string{ + val.ValAddress.String(), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), + 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, &sdk.TxResponse{}, 0, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + cmd := cli.NewUnbondCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err, out.String()) + 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()) + } + }) + } +} + +// TestBlockResults tests that the validator updates correctly show when +// calling the /block_results RPC endpoint. +// ref: https://github.com/cosmos/cosmos-sdk/issues/7401. +func (s *IntegrationTestSuite) TestBlockResults() { + require := s.Require() + val := s.network.Validators[0] + + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewDelegator", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + 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()), + ) + require.NoError(err) + + // Use CLI to create a delegation from the new account to validator `val`. + delHeight, err := s.network.LatestHeight() + require.NoError(err) + cmd := cli.NewDelegateCmd() + _, err = clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, []string{ + val.ValAddress.String(), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(150)).String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, newAddr.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()), + }) + require.NoError(err) + + // Create a HTTP rpc client. + rpcClient, err := http.New(val.RPCAddress, "/websocket") + + // Loop until we find a block result with the correct validator updates. + // By experience, it happens around 2 blocks after `delHeight`. + for { + latestHeight, err := s.network.LatestHeight() + require.NoError(err) + + // Wait maximum 10 blocks, or else fail test. + if latestHeight > delHeight+10 { + s.Fail("timeout reached") + } + + res, err := rpcClient.BlockResults(context.Background(), &latestHeight) + require.NoError(err) + + if len(res.ValidatorUpdates) > 0 { + valUpdate := res.ValidatorUpdates[0] + require.Equal( + valUpdate.GetPubKey().Sum.(*crypto.PublicKey_Ed25519).Ed25519, + val.PubKey.Bytes(), + ) + + // We got our validator update, test passed. + break + } + + s.network.WaitForNextBlock() + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/staking/client/cli/flags.go b/x/staking/client/cli/flags.go index b57479583a3b..094de343df90 100644 --- a/x/staking/client/cli/flags.go +++ b/x/staking/client/cli/flags.go @@ -6,7 +6,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// nolint const ( FlagAddressValidator = "validator" FlagAddressValidatorSrc = "addr-validator-source" @@ -35,39 +34,79 @@ const ( // common flagsets to add to various functions var ( - FsPk = flag.NewFlagSet("", flag.ContinueOnError) - FsAmount = flag.NewFlagSet("", flag.ContinueOnError) - fsShares = flag.NewFlagSet("", flag.ContinueOnError) - fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError) - FsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) - fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError) - FsMinSelfDelegation = flag.NewFlagSet("", flag.ContinueOnError) - fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError) - fsValidator = flag.NewFlagSet("", flag.ContinueOnError) - fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError) + fsShares = flag.NewFlagSet("", flag.ContinueOnError) + fsValidator = flag.NewFlagSet("", flag.ContinueOnError) + fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError) ) func init() { - FsPk.String(FlagPubKey, "", "The Bech32 encoded PubKey of the validator") - FsAmount.String(FlagAmount, "", "Amount of coins to bond") fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") fsShares.String(FlagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") - fsDescriptionCreate.String(FlagMoniker, "", "The validator's name") - fsDescriptionCreate.String(FlagIdentity, "", "The optional identity signature (ex. UPort or Keybase)") - fsDescriptionCreate.String(FlagWebsite, "", "The validator's (optional) website") - fsDescriptionCreate.String(FlagSecurityContact, "", "The validator's (optional) security contact email") - fsDescriptionCreate.String(FlagDetails, "", "The validator's (optional) details") - fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage") - FsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") - FsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") - FsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") - FsMinSelfDelegation.String(FlagMinSelfDelegation, "", "The minimum self delegation required on the validator") - fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "The validator's name") - fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)") - fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website") - fsDescriptionEdit.String(FlagSecurityContact, types.DoNotModifyDesc, "The validator's (optional) security contact email") - fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "The validator's (optional) details") fsValidator.String(FlagAddressValidator, "", "The Bech32 address of the validator") fsRedelegation.String(FlagAddressValidatorSrc, "", "The Bech32 address of the source validator") fsRedelegation.String(FlagAddressValidatorDst, "", "The Bech32 address of the destination validator") } + +// FlagSetCommissionCreate Returns the FlagSet used for commission create. +func FlagSetCommissionCreate() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + + fs.String(FlagCommissionRate, "", "The initial commission rate percentage") + fs.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") + fs.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") + + return fs +} + +// FlagSetMinSelfDelegation Returns the FlagSet used for minimum set delegation. +func FlagSetMinSelfDelegation() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.String(FlagMinSelfDelegation, "", "The minimum self delegation required on the validator") + return fs +} + +// FlagSetAmount Returns the FlagSet for amount related operations. +func FlagSetAmount() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.String(FlagAmount, "", "Amount of coins to bond") + return fs +} + +// FlagSetPublicKey Returns the flagset for Public Key related operations. +func FlagSetPublicKey() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.String(FlagPubKey, "", "The Bech32 encoded PubKey of the validator") + return fs +} + +func flagSetDescriptionEdit() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + + fs.String(FlagMoniker, types.DoNotModifyDesc, "The validator's name") + fs.String(FlagIdentity, types.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)") + fs.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website") + fs.String(FlagSecurityContact, types.DoNotModifyDesc, "The validator's (optional) security contact email") + fs.String(FlagDetails, types.DoNotModifyDesc, "The validator's (optional) details") + + return fs +} + +func flagSetCommissionUpdate() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + + fs.String(FlagCommissionRate, "", "The new commission rate percentage") + + return fs +} + +func flagSetDescriptionCreate() *flag.FlagSet { + fs := flag.NewFlagSet("", flag.ContinueOnError) + + fs.String(FlagMoniker, "", "The validator's name") + fs.String(FlagIdentity, "", "The optional identity signature (ex. UPort or Keybase)") + fs.String(FlagWebsite, "", "The validator's (optional) website") + fs.String(FlagSecurityContact, "", "The validator's (optional) security contact email") + fs.String(FlagDetails, "", "The validator's (optional) details") + + return fs +} diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index 652d207b08c2..1e8b0a5c539a 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strconv" "strings" @@ -8,16 +9,14 @@ import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/staking/types" ) // GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd() *cobra.Command { stakingQueryCmd := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the staking module", @@ -25,66 +24,74 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } - stakingQueryCmd.AddCommand(flags.GetCommands( - GetCmdQueryDelegation(queryRoute, cdc), - GetCmdQueryDelegations(queryRoute, cdc), - GetCmdQueryUnbondingDelegation(queryRoute, cdc), - GetCmdQueryUnbondingDelegations(queryRoute, cdc), - GetCmdQueryRedelegation(queryRoute, cdc), - GetCmdQueryRedelegations(queryRoute, cdc), - GetCmdQueryValidator(queryRoute, cdc), - GetCmdQueryValidators(queryRoute, cdc), - GetCmdQueryValidatorDelegations(queryRoute, cdc), - GetCmdQueryValidatorUnbondingDelegations(queryRoute, cdc), - GetCmdQueryValidatorRedelegations(queryRoute, cdc), - GetCmdQueryHistoricalInfo(queryRoute, cdc), - GetCmdQueryParams(queryRoute, cdc), - GetCmdQueryPool(queryRoute, cdc))...) - return stakingQueryCmd + stakingQueryCmd.AddCommand( + GetCmdQueryDelegation(), + GetCmdQueryDelegations(), + GetCmdQueryUnbondingDelegation(), + GetCmdQueryUnbondingDelegations(), + GetCmdQueryRedelegation(), + GetCmdQueryRedelegations(), + GetCmdQueryValidator(), + GetCmdQueryValidators(), + GetCmdQueryValidatorDelegations(), + GetCmdQueryValidatorUnbondingDelegations(), + GetCmdQueryValidatorRedelegations(), + GetCmdQueryHistoricalInfo(), + GetCmdQueryParams(), + GetCmdQueryPool(), + ) + return stakingQueryCmd } // GetCmdQueryValidator implements the validator query command. -func GetCmdQueryValidator(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidator() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "validator [validator-addr]", Short: "Query a validator", Long: strings.TrimSpace( fmt.Sprintf(`Query details about an individual validator. Example: -$ %s query staking validator fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking validator %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - addr, err := sdk.ValAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - res, _, err := cliCtx.QueryStore(types.GetValidatorKey(addr), storeName) + addr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - if len(res) == 0 { - return fmt.Errorf("no validator found with address %s", addr) + params := &types.QueryValidatorRequest{ValidatorAddr: addr.String()} + res, err := queryClient.Validator(cmd.Context(), params) + if err != nil { + return err } - return cliCtx.PrintOutput(types.MustUnmarshalValidator(cdc, res)) + return clientCtx.PrintProto(&res.Validator) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryValidators implements the query all validators command. -func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidators() *cobra.Command { + cmd := &cobra.Command{ Use: "validators", Short: "Query for all validators", Args: cobra.NoArgs, @@ -94,270 +101,336 @@ func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { Example: $ %s query staking validators `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - resKVs, _, err := cliCtx.QuerySubspace(types.ValidatorsKey, storeName) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var validators types.Validators - for _, kv := range resKVs { - validators = append(validators, types.MustUnmarshalValidator(cdc, kv.Value)) + result, err := queryClient.Validators(context.Background(), &types.QueryValidatorsRequest{ + // Leaving status empty on purpose to query all validators. + Pagination: pageReq, + }) + if err != nil { + return err } - return cliCtx.PrintOutput(validators) + return clientCtx.PrintProto(result) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "validators") + + return cmd } // GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. -func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidatorUnbondingDelegations() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "unbonding-delegations-from [validator-addr]", Short: "Query all unbonding delegatations from a validator", Long: strings.TrimSpace( fmt.Sprintf(`Query delegations that are unbonding _from_ a validator. Example: -$ %s query staking unbonding-delegations-from fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking unbonding-delegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorUnbondingDelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + params := &types.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: valAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.ValidatorUnbondingDelegations(context.Background(), params) if err != nil { return err } - var ubds types.UnbondingDelegations - cdc.MustUnmarshalJSON(res, &ubds) - return cliCtx.PrintOutput(ubds) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "unbonding delegations") + + return cmd } // GetCmdQueryValidatorRedelegations implements the query all redelegatations // from a validator command. -func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidatorRedelegations() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "redelegations-from [validator-addr]", Short: "Query all outgoing redelegatations from a validator", Long: strings.TrimSpace( fmt.Sprintf(`Query delegations that are redelegating _from_ a validator. Example: -$ %s query staking redelegations-from fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking redelegations-from %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - bz, err := cdc.MarshalJSON(types.QueryRedelegationParams{SrcValidatorAddr: valSrcAddr}) + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var resp types.RedelegationResponses - if err := cdc.UnmarshalJSON(res, &resp); err != nil { + params := &types.QueryRedelegationsRequest{ + SrcValidatorAddr: valSrcAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.Redelegations(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "validator redelegations") + + return cmd } // GetCmdQueryDelegation the query delegation command. -func GetCmdQueryDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryDelegation() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "delegation [delegator-addr] [validator-addr]", Short: "Query a delegation based on address and validator address", Long: strings.TrimSpace( fmt.Sprintf(`Query delegations for an individual delegator on an individual validator. Example: -$ %s query staking delegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddr, err := sdk.AccAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - valAddr, err := sdk.ValAddressFromBech32(args[1]) + delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - bz, err := cdc.MarshalJSON(types.NewQueryBondsParams(delAddr, valAddr)) + valAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegation) - res, _, err := cliCtx.QueryWithData(route, bz) - if err != nil { - return err + params := &types.QueryDelegationRequest{ + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), } - var resp types.DelegationResponse - if err := cdc.UnmarshalJSON(res, &resp); err != nil { + res, err := queryClient.Delegation(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res.DelegationResponse) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryDelegations implements the command to query all the delegations // made from one delegator. -func GetCmdQueryDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryDelegations() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ Use: "delegations [delegator-addr]", Short: "Query all delegations made by one delegator", Long: strings.TrimSpace( fmt.Sprintf(`Query delegations for an individual delegator on all validators. Example: -$ %s query staking delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +$ %s query staking delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddr, err := sdk.AccAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - bz, err := cdc.MarshalJSON(types.NewQueryDelegatorParams(delAddr)) + delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorDelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var resp types.DelegationResponses - if err := cdc.UnmarshalJSON(res, &resp); err != nil { + params := &types.QueryDelegatorDelegationsRequest{ + DelegatorAddr: delAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.DelegatorDelegations(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "delegations") + + return cmd } // GetCmdQueryValidatorDelegations implements the command to query all the // delegations to a specific validator. -func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryValidatorDelegations() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "delegations-to [validator-addr]", Short: "Query all delegations made to one validator", Long: strings.TrimSpace( fmt.Sprintf(`Query delegations on an individual validator. Example: -$ %s query staking delegations-to fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking delegations-to %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - valAddr, err := sdk.ValAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorDelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var resp types.DelegationResponses - if err := cdc.UnmarshalJSON(res, &resp); err != nil { + params := &types.QueryValidatorDelegationsRequest{ + ValidatorAddr: valAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.ValidatorDelegations(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "validator delegations") + + return cmd } // GetCmdQueryUnbondingDelegation implements the command to query a single // unbonding-delegation record. -func GetCmdQueryUnbondingDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryUnbondingDelegation() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "unbonding-delegation [delegator-addr] [validator-addr]", Short: "Query an unbonding-delegation record based on delegator and validator address", Long: strings.TrimSpace( fmt.Sprintf(`Query unbonding delegations for an individual delegator on an individual validator. Example: -$ %s query staking unbonding-delegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking unbonding-delegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) valAddr, err := sdk.ValAddressFromBech32(args[1]) if err != nil { @@ -369,85 +442,105 @@ $ %s query staking unbonding-delegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf return err } - bz, err := cdc.MarshalJSON(types.NewQueryBondsParams(delAddr, valAddr)) - if err != nil { - return err + params := &types.QueryUnbondingDelegationRequest{ + DelegatorAddr: delAddr.String(), + ValidatorAddr: valAddr.String(), } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryUnbondingDelegation) - res, _, err := cliCtx.QueryWithData(route, bz) + res, err := queryClient.UnbondingDelegation(context.Background(), params) if err != nil { return err } - return cliCtx.PrintOutput(types.MustUnmarshalUBD(cdc, res)) + return clientCtx.PrintProto(&res.Unbond) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryUnbondingDelegations implements the command to query all the // unbonding-delegation records for a delegator. -func GetCmdQueryUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryUnbondingDelegations() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ Use: "unbonding-delegations [delegator-addr]", Short: "Query all unbonding-delegations records for one delegator", Long: strings.TrimSpace( fmt.Sprintf(`Query unbonding delegations for an individual delegator. Example: -$ %s query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +$ %s query staking unbonding-delegations %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, ), ), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - bz, err := cdc.MarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)) + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorUnbondingDelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var ubds types.UnbondingDelegations - if err = cdc.UnmarshalJSON(res, &ubds); err != nil { + params := &types.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: delegatorAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.DelegatorUnbondingDelegations(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(ubds) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "unbonding delegations") + + return cmd } // GetCmdQueryRedelegation implements the command to query a single // redelegation record. -func GetCmdQueryRedelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryRedelegation() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "redelegation [delegator-addr] [src-validator-addr] [dst-validator-addr]", Short: "Query a redelegation record based on delegator and a source and destination validator address", Long: strings.TrimSpace( fmt.Sprintf(`Query a redelegation record for an individual delegator between a source and destination validator. Example: -$ %s query staking redelegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k fetchvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fjtkhnc fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 +$ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, bech32PrefixValAddr, bech32PrefixValAddr, ), ), Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { @@ -464,31 +557,32 @@ $ %s query staking redelegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k fet return err } - bz, err := cdc.MarshalJSON(types.NewQueryRedelegationParams(delAddr, valSrcAddr, valDstAddr)) - if err != nil { - return err + params := &types.QueryRedelegationsRequest{ + DelegatorAddr: delAddr.String(), + DstValidatorAddr: valDstAddr.String(), + SrcValidatorAddr: valSrcAddr.String(), } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + res, err := queryClient.Redelegations(context.Background(), params) if err != nil { return err } - var resp types.RedelegationResponses - if err := cdc.UnmarshalJSON(res, &resp); err != nil { - return err - } - - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryRedelegations implements the command to query all the // redelegation records for a delegator. -func GetCmdQueryRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryRedelegations() *cobra.Command { + bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() + + cmd := &cobra.Command{ Use: "redelegations [delegator-addr]", Args: cobra.ExactArgs(1), Short: "Query all redelegations records for one delegator", @@ -496,43 +590,51 @@ func GetCmdQueryRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Comman fmt.Sprintf(`Query all redelegation records for an individual delegator. Example: -$ %s query staking redelegation fetch1gghjut3ccd8ay0zduzj64hwre2fxs9lddf2c8k +$ %s query staking redelegation %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p `, - version.ClientName, + version.AppName, bech32PrefixAccAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - delAddr, err := sdk.AccAddressFromBech32(args[0]) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - bz, err := cdc.MarshalJSON(types.QueryRedelegationParams{DelegatorAddr: delAddr}) + delAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err } - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) - res, _, err := cliCtx.QueryWithData(route, bz) + pageReq, err := client.ReadPageRequest(cmd.Flags()) if err != nil { return err } - var resp types.RedelegationResponses - if err := cdc.UnmarshalJSON(res, &resp); err != nil { + params := &types.QueryRedelegationsRequest{ + DelegatorAddr: delAddr.String(), + Pagination: pageReq, + } + + res, err := queryClient.Redelegations(context.Background(), params) + if err != nil { return err } - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res) }, } + + flags.AddQueryFlagsToCmd(cmd) + flags.AddPaginationFlagsToCmd(cmd, "delegator redelegations") + + return cmd } // GetCmdQueryHistoricalInfo implements the historical info query command -func GetCmdQueryHistoricalInfo(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryHistoricalInfo() *cobra.Command { + cmd := &cobra.Command{ Use: "historical-info [height]", Args: cobra.ExactArgs(1), Short: "Query historical info at given height", @@ -542,41 +644,40 @@ func GetCmdQueryHistoricalInfo(queryRoute string, cdc *codec.Codec) *cobra.Comma Example: $ %s query staking historical-info 5 `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) height, err := strconv.ParseInt(args[0], 10, 64) if err != nil || height < 0 { return fmt.Errorf("height argument provided must be a non-negative-integer: %v", err) } - bz, err := cdc.MarshalJSON(types.QueryHistoricalInfoParams{Height: height}) - if err != nil { - return err - } + params := &types.QueryHistoricalInfoRequest{Height: height} + res, err := queryClient.HistoricalInfo(context.Background(), params) - route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryHistoricalInfo) - res, _, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - var resp types.HistoricalInfo - if err := cdc.UnmarshalJSON(res, &resp); err != nil { - return err - } - - return cliCtx.PrintOutput(resp) + return clientCtx.PrintProto(res.Hist) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryPool implements the pool query command. -func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryPool() *cobra.Command { + cmd := &cobra.Command{ Use: "pool", Args: cobra.NoArgs, Short: "Query the current staking pool values", @@ -586,30 +687,33 @@ func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command { Example: $ %s query staking pool `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - bz, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/pool", storeName), nil) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - var pool types.Pool - if err := cdc.UnmarshalJSON(bz, &pool); err != nil { + res, err := queryClient.Pool(context.Background(), &types.QueryPoolRequest{}) + if err != nil { return err } - return cliCtx.PrintOutput(pool) + return clientCtx.PrintProto(&res.Pool) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } // GetCmdQueryParams implements the params query command. -func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ Use: "params", Args: cobra.NoArgs, Short: "Query the current staking parameters information", @@ -619,21 +723,26 @@ func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command { Example: $ %s query staking params `, - version.ClientName, + version.AppName, ), ), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) - route := fmt.Sprintf("custom/%s/%s", storeName, types.QueryParameters) - bz, _, err := cliCtx.QueryWithData(route, nil) + res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) if err != nil { return err } - var params types.Params - cdc.MustUnmarshalJSON(bz, ¶ms) - return cliCtx.PrintOutput(params) + return clientCtx.PrintProto(&res.Params) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index 1ac0f0e84bb3..d8c43a6b4930 100644 --- a/x/staking/client/cli/tx.go +++ b/x/staking/client/cli/tx.go @@ -1,31 +1,34 @@ package cli import ( - "bufio" "fmt" "os" "strings" "github.com/spf13/cobra" flag "github.com/spf13/pflag" - "github.com/spf13/viper" - - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" + "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/version" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// GetTxCmd returns the transaction commands for this module -func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { +// default values +var ( + DefaultTokens = sdk.TokensFromConsensusPower(100) + defaultAmount = DefaultTokens.String() + sdk.DefaultBondDenom + defaultCommissionRate = "0.1" + defaultCommissionMaxRate = "0.2" + defaultCommissionMaxChangeRate = "0.01" + defaultMinSelfDelegation = "1000000000000000000" +) + +// NewTxCmd returns a root CLI command handler for all x/staking transaction commands. +func NewTxCmd() *cobra.Command { stakingTxCmd := &cobra.Command{ Use: types.ModuleName, Short: "Staking transaction subcommands", @@ -34,76 +37,75 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - stakingTxCmd.AddCommand(flags.PostCommands( - GetCmdCreateValidator(cdc), - GetCmdEditValidator(cdc), - GetCmdDelegate(cdc), - GetCmdRedelegate(storeKey, cdc), - GetCmdUnbond(storeKey, cdc), - )...) + stakingTxCmd.AddCommand( + NewCreateValidatorCmd(), + NewEditValidatorCmd(), + NewDelegateCmd(), + NewRedelegateCmd(), + NewUnbondCmd(), + ) return stakingTxCmd } -// GetCmdCreateValidator implements the create validator command handler. -func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { +func NewCreateValidatorCmd() *cobra.Command { cmd := &cobra.Command{ Use: "create-validator", Short: "create new validator initialized with a self-delegation to it", RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) - txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) + txf, msg, err := NewBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags()) if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) }, } - cmd.Flags().AddFlagSet(FsPk) - cmd.Flags().AddFlagSet(FsAmount) - cmd.Flags().AddFlagSet(fsDescriptionCreate) - cmd.Flags().AddFlagSet(FsCommissionCreate) - cmd.Flags().AddFlagSet(FsMinSelfDelegation) + cmd.Flags().AddFlagSet(FlagSetPublicKey()) + cmd.Flags().AddFlagSet(FlagSetAmount()) + cmd.Flags().AddFlagSet(flagSetDescriptionCreate()) + cmd.Flags().AddFlagSet(FlagSetCommissionCreate()) + cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation()) cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", flags.FlagGenerateOnly)) cmd.Flags().String(FlagNodeID, "", "The node's ID") + flags.AddTxFlagsToCmd(cmd) - cmd.MarkFlagRequired(flags.FlagFrom) - cmd.MarkFlagRequired(FlagAmount) - cmd.MarkFlagRequired(FlagPubKey) - cmd.MarkFlagRequired(FlagMoniker) + _ = cmd.MarkFlagRequired(flags.FlagFrom) + _ = cmd.MarkFlagRequired(FlagAmount) + _ = cmd.MarkFlagRequired(FlagPubKey) + _ = cmd.MarkFlagRequired(FlagMoniker) return cmd } -// GetCmdEditValidator implements the create edit validator command. -// TODO: add full description -func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { +func NewEditValidatorCmd() *cobra.Command { cmd := &cobra.Command{ Use: "edit-validator", Short: "edit an existing validator account", RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - valAddr := cliCtx.GetFromAddress() - description := types.NewDescription( - viper.GetString(FlagMoniker), - viper.GetString(FlagIdentity), - viper.GetString(FlagWebsite), - viper.GetString(FlagSecurityContact), - viper.GetString(FlagDetails), - ) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + valAddr := clientCtx.GetFromAddress() + moniker, _ := cmd.Flags().GetString(FlagMoniker) + identity, _ := cmd.Flags().GetString(FlagIdentity) + website, _ := cmd.Flags().GetString(FlagWebsite) + security, _ := cmd.Flags().GetString(FlagSecurityContact) + details, _ := cmd.Flags().GetString(FlagDetails) + description := types.NewDescription(moniker, identity, website, security, details) var newRate *sdk.Dec - commissionRate := viper.GetString(FlagCommissionRate) + commissionRate, _ := cmd.Flags().GetString(FlagCommissionRate) if commissionRate != "" { rate, err := sdk.NewDecFromStr(commissionRate) if err != nil { @@ -115,7 +117,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { var newMinSelfDelegation *sdk.Int - minSelfDelegationString := viper.GetString(FlagMinSelfDelegation) + minSelfDelegationString, _ := cmd.Flags().GetString(FlagMinSelfDelegation) if minSelfDelegationString != "" { msb, ok := sdk.NewIntFromString(minSelfDelegationString) if !ok { @@ -126,22 +128,26 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { } msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) + if err := msg.ValidateBasic(); err != nil { + return err + } - // build and sign the transaction, then broadcast to Tendermint - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } - cmd.Flags().AddFlagSet(fsDescriptionEdit) - cmd.Flags().AddFlagSet(fsCommissionUpdate) - cmd.Flags().AddFlagSet(FsMinSelfDelegation) + cmd.Flags().AddFlagSet(flagSetDescriptionEdit()) + cmd.Flags().AddFlagSet(flagSetCommissionUpdate()) + cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation()) + flags.AddTxFlagsToCmd(cmd) return cmd } -// GetCmdDelegate implements the delegate command. -func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func NewDelegateCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "delegate [validator-addr] [amount]", Args: cobra.ExactArgs(2), Short: "Delegate liquid tokens to a validator", @@ -149,36 +155,45 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Delegate an amount of liquid coins to a validator from your wallet. Example: -$ %s tx staking delegate fetchvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fjtkhnc 1000stake --from mykey +$ %s tx staking delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - amount, err := sdk.ParseCoin(args[1]) + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + amount, err := sdk.ParseCoinNormalized(args[1]) if err != nil { return err } - delAddr := cliCtx.GetFromAddress() + delAddr := clientCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } msg := types.NewMsgDelegate(delAddr, valAddr, amount) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } -// GetCmdRedelegate the begin redelegation command. -func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func NewRedelegateCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "redelegate [src-validator-addr] [dst-validator-addr] [amount]", Short: "Redelegate illiquid tokens from one validator to another", Args: cobra.ExactArgs(3), @@ -186,17 +201,17 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Redelegate an amount of illiquid staking tokens from one validator to another. Example: -$ %s tx staking redelegate fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 fetchvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fjtkhnc 100stake --from mykey +$ %s tx staking redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey `, - version.ClientName, + version.AppName, bech32PrefixValAddr, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - delAddr := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err @@ -207,20 +222,29 @@ $ %s tx staking redelegate fetchvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldgd4m53 f return err } - amount, err := sdk.ParseCoin(args[2]) + amount, err := sdk.ParseCoinNormalized(args[2]) if err != nil { return err } msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } -// GetCmdUnbond implements the unbond validator command. -func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +func NewUnbondCmd() *cobra.Command { + bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() + + cmd := &cobra.Command{ Use: "unbond [validator-addr] [amount]", Short: "Unbond shares from a validator", Args: cobra.ExactArgs(2), @@ -228,59 +252,125 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { fmt.Sprintf(`Unbond an amount of bonded shares from a validator. Example: -$ %s tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey +$ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey `, - version.ClientName, + version.AppName, bech32PrefixValAddr, ), ), RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(auth.DefaultTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - - delAddr := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + delAddr := clientCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(args[0]) if err != nil { return err } - amount, err := sdk.ParseCoin(args[1]) + amount, err := sdk.ParseCoinNormalized(args[1]) if err != nil { return err } msg := types.NewMsgUndelegate(delAddr, valAddr, amount) - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } + + flags.AddTxFlagsToCmd(cmd) + + return cmd } -//__________________________________________________________ +func NewBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, sdk.Msg, error) { + fAmount, _ := fs.GetString(FlagAmount) + amount, err := sdk.ParseCoinNormalized(fAmount) + if err != nil { + return txf, nil, err + } -var ( - defaultTokens = sdk.TokensFromConsensusPower(100) - defaultAmount = defaultTokens.String() + sdk.DefaultBondDenom - defaultCommissionRate = "0.1" - defaultCommissionMaxRate = "0.2" - defaultCommissionMaxChangeRate = "0.01" - defaultMinSelfDelegation = "1" -) + valAddr := clientCtx.GetFromAddress() + pkStr, _ := fs.GetString(FlagPubKey) + + pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, pkStr) + if err != nil { + return txf, nil, err + } + + moniker, _ := fs.GetString(FlagMoniker) + identity, _ := fs.GetString(FlagIdentity) + website, _ := fs.GetString(FlagWebsite) + security, _ := fs.GetString(FlagSecurityContact) + details, _ := fs.GetString(FlagDetails) + description := types.NewDescription( + moniker, + identity, + website, + security, + details, + ) + + // get the initial validator commission parameters + rateStr, _ := fs.GetString(FlagCommissionRate) + maxRateStr, _ := fs.GetString(FlagCommissionMaxRate) + maxChangeRateStr, _ := fs.GetString(FlagCommissionMaxChangeRate) + + commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return txf, nil, err + } + + // get the initial validator min self delegation + msbStr, _ := fs.GetString(FlagMinSelfDelegation) + + minSelfDelegation, ok := sdk.NewIntFromString(msbStr) + if !ok { + return txf, nil, types.ErrMinSelfDelegationInvalid + } + + msg, err := types.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, + ) + if err != nil { + return txf, nil, err + } + if err := msg.ValidateBasic(); err != nil { + return txf, nil, err + } + + genOnly, _ := fs.GetBool(flags.FlagGenerateOnly) + if genOnly { + ip, _ := fs.GetString(FlagIP) + nodeID, _ := fs.GetString(FlagNodeID) + + if nodeID != "" && ip != "" { + txf = txf.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + + return txf, msg, nil +} // Return the flagset, particular flags, and a description of defaults // this is anticipated to be used with the gen-tx -func CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { - +func CreateValidatorMsgFlagSet(ipDefault string) (fs *flag.FlagSet, defaultsDesc string) { fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) fsCreateValidator.String(FlagIP, ipDefault, "The node's public IP") fsCreateValidator.String(FlagNodeID, "", "The node's NodeID") + fsCreateValidator.String(FlagMoniker, "", "The validator's (optional) moniker") fsCreateValidator.String(FlagWebsite, "", "The validator's (optional) website") fsCreateValidator.String(FlagSecurityContact, "", "The validator's (optional) security contact email") fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details") fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") - fsCreateValidator.AddFlagSet(FsCommissionCreate) - fsCreateValidator.AddFlagSet(FsMinSelfDelegation) - fsCreateValidator.AddFlagSet(FsAmount) - fsCreateValidator.AddFlagSet(FsPk) + fsCreateValidator.AddFlagSet(FlagSetCommissionCreate()) + fsCreateValidator.AddFlagSet(FlagSetMinSelfDelegation()) + fsCreateValidator.AddFlagSet(FlagSetAmount()) + fsCreateValidator.AddFlagSet(FlagSetPublicKey()) defaultsDesc = fmt.Sprintf(` delegation amount: %s @@ -292,66 +382,135 @@ func CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation) - return fsCreateValidator, FlagNodeID, FlagPubKey, FlagAmount, defaultsDesc + return fsCreateValidator, defaultsDesc } -// prepare flags in config -func PrepareFlagsForTxCreateValidator( - config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey, -) { +type TxCreateValidatorConfig struct { + ChainID string + NodeID string + Moniker string + + Amount string - ip := viper.GetString(FlagIP) + CommissionRate string + CommissionMaxRate string + CommissionMaxChangeRate string + MinSelfDelegation string + + PubKey string + + IP string + Website string + SecurityContact string + Details string + Identity string +} + +func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) { + c := TxCreateValidatorConfig{} + + ip, err := flagSet.GetString(FlagIP) + if err != nil { + return c, err + } if ip == "" { - fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ + _, _ = fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ "the tx's memo field will be unset") } + c.IP = ip - website := viper.GetString(FlagWebsite) - securityContact := viper.GetString(FlagSecurityContact) - details := viper.GetString(FlagDetails) - identity := viper.GetString(FlagIdentity) + website, err := flagSet.GetString(FlagWebsite) + if err != nil { + return c, err + } + c.Website = website - viper.Set(flags.FlagChainID, chainID) - viper.Set(flags.FlagFrom, viper.GetString(flags.FlagName)) - viper.Set(FlagNodeID, nodeID) - viper.Set(FlagIP, ip) - viper.Set(FlagPubKey, sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey)) - viper.Set(FlagMoniker, config.Moniker) - viper.Set(FlagWebsite, website) - viper.Set(FlagSecurityContact, securityContact) - viper.Set(FlagDetails, details) - viper.Set(FlagIdentity, identity) + securityContact, err := flagSet.GetString(FlagSecurityContact) + if err != nil { + return c, err + } + c.SecurityContact = securityContact - if config.Moniker == "" { - viper.Set(FlagMoniker, viper.GetString(flags.FlagName)) + details, err := flagSet.GetString(FlagDetails) + if err != nil { + return c, err } - if viper.GetString(FlagAmount) == "" { - viper.Set(FlagAmount, defaultAmount) + c.SecurityContact = details + + identity, err := flagSet.GetString(FlagIdentity) + if err != nil { + return c, err } - if viper.GetString(FlagCommissionRate) == "" { - viper.Set(FlagCommissionRate, defaultCommissionRate) + c.Identity = identity + + c.Amount, err = flagSet.GetString(FlagAmount) + if err != nil { + return c, err } - if viper.GetString(FlagCommissionMaxRate) == "" { - viper.Set(FlagCommissionMaxRate, defaultCommissionMaxRate) + + c.CommissionRate, err = flagSet.GetString(FlagCommissionRate) + if err != nil { + return c, err + } + + c.CommissionMaxRate, err = flagSet.GetString(FlagCommissionMaxRate) + if err != nil { + return c, err } - if viper.GetString(FlagCommissionMaxChangeRate) == "" { - viper.Set(FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + + c.CommissionMaxChangeRate, err = flagSet.GetString(FlagCommissionMaxChangeRate) + if err != nil { + return c, err } - if viper.GetString(FlagMinSelfDelegation) == "" { - viper.Set(FlagMinSelfDelegation, defaultMinSelfDelegation) + + c.MinSelfDelegation, err = flagSet.GetString(FlagMinSelfDelegation) + if err != nil { + return c, err } + + c.NodeID = nodeID + c.PubKey = sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey) + c.Website = website + c.SecurityContact = securityContact + c.Details = details + c.Identity = identity + c.ChainID = chainID + c.Moniker = moniker + + if c.Amount == "" { + c.Amount = defaultAmount + } + + if c.CommissionRate == "" { + c.CommissionRate = defaultCommissionRate + } + + if c.CommissionMaxRate == "" { + c.CommissionMaxRate = defaultCommissionMaxRate + } + + if c.CommissionMaxChangeRate == "" { + c.CommissionMaxChangeRate = defaultCommissionMaxChangeRate + } + + if c.MinSelfDelegation == "" { + c.MinSelfDelegation = defaultMinSelfDelegation + } + + return c, nil } // BuildCreateValidatorMsg makes a new MsgCreateValidator. -func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) (auth.TxBuilder, sdk.Msg, error) { - amounstStr := viper.GetString(FlagAmount) - amount, err := sdk.ParseCoin(amounstStr) +func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr tx.Factory, generateOnly bool) (tx.Factory, sdk.Msg, error) { + amounstStr := config.Amount + amount, err := sdk.ParseCoinNormalized(amounstStr) + if err != nil { return txBldr, nil, err } - valAddr := cliCtx.GetFromAddress() - pkStr := viper.GetString(FlagPubKey) + valAddr := clientCtx.GetFromAddress() + pkStr := config.PubKey pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, pkStr) if err != nil { @@ -359,36 +518,41 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) ( } description := types.NewDescription( - viper.GetString(FlagMoniker), - viper.GetString(FlagIdentity), - viper.GetString(FlagWebsite), - viper.GetString(FlagSecurityContact), - viper.GetString(FlagDetails), + config.Moniker, + config.Identity, + config.Website, + config.SecurityContact, + config.Details, ) // get the initial validator commission parameters - rateStr := viper.GetString(FlagCommissionRate) - maxRateStr := viper.GetString(FlagCommissionMaxRate) - maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) + rateStr := config.CommissionRate + maxRateStr := config.CommissionMaxRate + maxChangeRateStr := config.CommissionMaxChangeRate commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { return txBldr, nil, err } // get the initial validator min self delegation - msbStr := viper.GetString(FlagMinSelfDelegation) + msbStr := config.MinSelfDelegation minSelfDelegation, ok := sdk.NewIntFromString(msbStr) + if !ok { return txBldr, nil, types.ErrMinSelfDelegationInvalid } - msg := types.NewMsgCreateValidator( + msg, err := types.NewMsgCreateValidator( sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, ) + if err != nil { + return txBldr, msg, err + } + if generateOnly { + ip := config.IP + nodeID := config.NodeID - if viper.GetBool(flags.FlagGenerateOnly) { - ip := viper.GetString(FlagIP) - nodeID := viper.GetString(FlagNodeID) if nodeID != "" && ip != "" { txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) } diff --git a/x/staking/client/cli/tx_test.go b/x/staking/client/cli/tx_test.go index 698fad3831e7..b8be952f0dd2 100644 --- a/x/staking/client/cli/tx_test.go +++ b/x/staking/client/cli/tx_test.go @@ -3,84 +3,147 @@ package cli import ( "testing" - "github.com/spf13/viper" + "github.com/spf13/pflag" "github.com/stretchr/testify/require" - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestPrepareFlagsForTxCreateValidator(t *testing.T) { - defer server.SetupViper(t)() - config, err := tcmd.ParseConfig() - require.Nil(t, err) - logger := log.NewNopLogger() - ctx := server.NewContext(config, logger) +func TestPrepareConfigForTxCreateValidator(t *testing.T) { + chainID := "chainID" + ip := "1.1.1.1" + nodeID := "nodeID" + valPubKey, _ := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, "cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a") + moniker := "DefaultMoniker" - valPubKey, _ := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, "fetchvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as6rr8h4") - - type args struct { - config *cfg.Config - nodeID string - chainID string - valPubKey crypto.PubKey - } - - type extraParams struct { - amount string - commissionRate string - commissionMaxRate string - commissionMaxChangeRate string - minSelfDelegation string - } - - type testcase struct { - name string - args args + tests := []struct { + name string + fsModify func(fs *pflag.FlagSet) + expectedCfg TxCreateValidatorConfig + }{ + { + name: "all defaults", + fsModify: func(fs *pflag.FlagSet) { + return + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Moniker: moniker, + Amount: defaultAmount, + CommissionRate: "0.1", + CommissionMaxRate: "0.2", + CommissionMaxChangeRate: "0.01", + MinSelfDelegation: defaultMinSelfDelegation, + }, + }, + { + name: "Custom amount", + fsModify: func(fs *pflag.FlagSet) { + fs.Set(FlagAmount, "2000stake") + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Amount: "2000stake", + CommissionRate: "0.1", + CommissionMaxRate: "0.2", + CommissionMaxChangeRate: "0.01", + MinSelfDelegation: defaultMinSelfDelegation, + }, + }, + { + name: "Custom commission rate", + fsModify: func(fs *pflag.FlagSet) { + fs.Set(FlagCommissionRate, "0.54") + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Amount: defaultAmount, + CommissionRate: "0.54", + CommissionMaxRate: "0.2", + CommissionMaxChangeRate: "0.01", + MinSelfDelegation: defaultMinSelfDelegation, + }, + }, + { + name: "Custom commission max rate", + fsModify: func(fs *pflag.FlagSet) { + fs.Set(FlagCommissionMaxRate, "0.89") + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Amount: defaultAmount, + CommissionRate: "0.1", + CommissionMaxRate: "0.89", + CommissionMaxChangeRate: "0.01", + MinSelfDelegation: defaultMinSelfDelegation, + }, + }, + { + name: "Custom commission max change rate", + fsModify: func(fs *pflag.FlagSet) { + fs.Set(FlagCommissionMaxChangeRate, "0.55") + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Amount: defaultAmount, + CommissionRate: "0.1", + CommissionMaxRate: "0.2", + CommissionMaxChangeRate: "0.55", + MinSelfDelegation: defaultMinSelfDelegation, + }, + }, + { + name: "Custom min self delegations", + fsModify: func(fs *pflag.FlagSet) { + fs.Set(FlagMinSelfDelegation, "0.33") + }, + expectedCfg: TxCreateValidatorConfig{ + IP: ip, + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey), + Amount: defaultAmount, + CommissionRate: "0.1", + CommissionMaxRate: "0.2", + CommissionMaxChangeRate: "0.01", + MinSelfDelegation: "0.33", + }, + }, } - runTest := func(t *testing.T, tt testcase, params extraParams) { - PrepareFlagsForTxCreateValidator(tt.args.config, tt.args.nodeID, - tt.args.chainID, tt.args.valPubKey) + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + fs, _ := CreateValidatorMsgFlagSet(ip) + fs.String(flags.FlagName, "", "name of private key with which to sign the gentx") - require.Equal(t, params.amount, viper.GetString(FlagAmount)) - require.Equal(t, params.commissionRate, viper.GetString(FlagCommissionRate)) - require.Equal(t, params.commissionMaxRate, viper.GetString(FlagCommissionMaxRate)) - require.Equal(t, params.commissionMaxChangeRate, viper.GetString(FlagCommissionMaxChangeRate)) - require.Equal(t, params.minSelfDelegation, viper.GetString(FlagMinSelfDelegation)) - } + tc.fsModify(fs) - tests := []testcase{ - {"No parameters", args{ctx.Config, "X", "chainId", valPubKey}}, - } + cvCfg, err := PrepareConfigForTxCreateValidator(fs, moniker, nodeID, chainID, valPubKey) + require.NoError(t, err) - defaultParams := extraParams{ - defaultAmount, - defaultCommissionRate, - defaultCommissionMaxRate, - defaultCommissionMaxChangeRate, - defaultMinSelfDelegation, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Run(tt.name, func(t *testing.T) { runTest(t, tt, defaultParams) }) + require.Equal(t, tc.expectedCfg, cvCfg) }) } - - // Override default params - params := extraParams{"5stake", "1.0", "1.0", "1.0", "1.0"} - viper.Set(FlagAmount, params.amount) - viper.Set(FlagCommissionRate, params.commissionRate) - viper.Set(FlagCommissionMaxRate, params.commissionMaxRate) - viper.Set(FlagCommissionMaxChangeRate, params.commissionMaxChangeRate) - viper.Set(FlagMinSelfDelegation, params.minSelfDelegation) - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { runTest(t, tt, params) }) - } } diff --git a/x/staking/client/cli/utils.go b/x/staking/client/cli/utils.go index e10a79add845..241ac314054f 100644 --- a/x/staking/client/cli/utils.go +++ b/x/staking/client/cli/utils.go @@ -28,5 +28,6 @@ func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commiss } commission = types.NewCommissionRates(rate, maxRate, maxChangeRate) + return commission, nil } diff --git a/x/staking/client/rest/grpc_query_test.go b/x/staking/client/rest/grpc_query_test.go new file mode 100644 index 000000000000..42d79745177a --- /dev/null +++ b/x/staking/client/rest/grpc_query_test.go @@ -0,0 +1,831 @@ +// +build norace + +package rest_test + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/client/testutil" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up integration test suite") + + 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) + + unbond, err := sdk.ParseCoinNormalized("10stake") + s.Require().NoError(err) + + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + // redelegate + _, err = stakingtestutil.MsgRedelegateExec(val.ClientCtx, val.Address, val.ValAddress, val2.ValAddress, unbond) + s.Require().NoError(err) + _, err = s.network.WaitForHeight(1) + s.Require().NoError(err) + + // unbonding + _, err = stakingtestutil.MsgUnbondExec(val.ClientCtx, val.Address, val.ValAddress, unbond) + s.Require().NoError(err) + _, 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) TestQueryValidatorsGRPCHandler() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "test query validators gRPC route with invalid status", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators?status=active", baseURL), + true, + }, + { + "test query validators gRPC route without status query param", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators", baseURL), + false, + }, + { + "test query validators gRPC route with valid status", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators?status=%s", baseURL, types.Bonded.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var valRes types.QueryValidatorsResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &valRes) + + if tc.error { + s.Require().Error(err) + s.Require().Nil(valRes.Validators) + s.Require().Equal(0, len(valRes.Validators)) + } else { + s.Require().NoError(err) + s.Require().NotNil(valRes.Validators) + s.Require().Equal(len(s.network.Validators), len(valRes.Validators)) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryValidatorGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s", baseURL, "wrongValidatorAddress"), + true, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s", baseURL, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s", baseURL, val.ValAddress.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var validator types.QueryValidatorResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &validator) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotNil(validator.Validator) + s.Require().Equal(s.network.Validators[0].ValAddress.String(), validator.Validator.OperatorAddress) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryValidatorDelegationsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + headers map[string]string + error bool + respType proto.Message + expectedResp proto.Message + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations", baseURL, "wrongValAddress"), + map[string]string{}, + true, + &types.QueryValidatorDelegationsResponse{}, + nil, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations", baseURL, ""), + map[string]string{}, + true, + &types.QueryValidatorDelegationsResponse{}, + nil, + }, + { + "valid request(height specific)", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations", baseURL, val.ValAddress.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QueryValidatorDelegationsResponse{}, + &types.QueryValidatorDelegationsResponse{ + DelegationResponses: types.DelegationResponses{ + types.NewDelegationResp(val.Address, val.ValAddress, sdk.NewDecFromInt(cli.DefaultTokens), sdk.NewCoin(sdk.DefaultBondDenom, cli.DefaultTokens)), + }, + Pagination: &query.PageResponse{Total: 1}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedResp.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryValidatorUnbondingDelegationsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/unbonding_delegations", baseURL, "wrongValAddress"), + true, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/unbonding_delegations", baseURL, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/unbonding_delegations", baseURL, val.ValAddress.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var ubds types.QueryValidatorUnbondingDelegationsResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &ubds) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Len(ubds.UnbondingResponses, 1) + s.Require().Equal(ubds.UnbondingResponses[0].ValidatorAddress, val.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegationGRPC() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + respType proto.Message + expectedResp proto.Message + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", baseURL, "wrongValAddress", val.Address.String()), + true, + &types.QueryDelegationResponse{}, + nil, + }, + { + "wrong account address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", baseURL, val.ValAddress.String(), "wrongAccAddress"), + true, + &types.QueryDelegationResponse{}, + nil, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", baseURL, "", val.Address.String()), + true, + &types.QueryDelegationResponse{}, + nil, + }, + { + "with no account address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", baseURL, val.ValAddress.String(), ""), + true, + &types.QueryDelegationResponse{}, + nil, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", baseURL, val2.ValAddress.String(), val.Address.String()), + false, + &types.QueryDelegationResponse{}, + &types.QueryDelegationResponse{ + DelegationResponse: &types.DelegationResponse{ + Delegation: types.Delegation{ + DelegatorAddress: val.Address.String(), + ValidatorAddress: val2.ValAddress.String(), + Shares: sdk.NewDec(10), + }, + Balance: sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedResp.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryUnbondingDelegationGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", baseURL, "wrongValAddress", val.Address.String()), + true, + }, + { + "wrong account address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", baseURL, val.ValAddress.String(), "wrongAccAddress"), + true, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", baseURL, "", val.Address.String()), + true, + }, + { + "with no account address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", baseURL, val.ValAddress.String(), ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", baseURL, val.ValAddress.String(), val.Address.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var ubd types.QueryUnbondingDelegationResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &ubd) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(ubd.Unbond.DelegatorAddress, val.Address.String()) + s.Require().Equal(ubd.Unbond.ValidatorAddress, val.ValAddress.String()) + s.Require().Len(ubd.Unbond.Entries, 1) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegationsResponseCode() { + val := s.network.Validators[0] + + // Create new account in the keyring. + info, _, err := val.ClientCtx.Keyring.NewMnemonic("test", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1) + s.Require().NoError(err) + newAddr := sdk.AccAddress(info.GetPubKey().Address()) + + s.T().Log("expect 404 error for address without delegations") + res, statusCode, err := getRequest(fmt.Sprintf("%s/cosmos/staking/v1beta1/delegations/%s", val.APIAddress, newAddr.String())) + s.Require().NoError(err) + s.Require().Contains(string(res), "\"code\": 5") + s.Require().Equal(404, statusCode) +} + +func getRequest(url string) ([]byte, int, error) { + res, err := http.Get(url) // nolint:gosec + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, res.StatusCode, err + } + + if err = res.Body.Close(); err != nil { + return nil, res.StatusCode, err + } + + return body, res.StatusCode, nil +} + +func (s *IntegrationTestSuite) TestQueryDelegatorDelegationsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + headers map[string]string + error bool + respType proto.Message + expectedResp proto.Message + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegations/%s", baseURL, "wrongValAddress"), + map[string]string{}, + true, + &types.QueryDelegatorDelegationsResponse{}, + nil, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegations/%s", baseURL, ""), + map[string]string{}, + true, + &types.QueryDelegatorDelegationsResponse{}, + nil, + }, + { + "valid request (height specific)", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegations/%s", baseURL, val.Address.String()), + map[string]string{ + grpctypes.GRPCBlockHeightHeader: "1", + }, + false, + &types.QueryDelegatorDelegationsResponse{}, + &types.QueryDelegatorDelegationsResponse{ + DelegationResponses: types.DelegationResponses{ + types.NewDelegationResp(val.Address, val.ValAddress, sdk.NewDecFromInt(cli.DefaultTokens), sdk.NewCoin(sdk.DefaultBondDenom, cli.DefaultTokens)), + }, + Pagination: &query.PageResponse{Total: 1}, + }, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := testutil.GetRequestWithHeaders(tc.url, tc.headers) + s.Require().NoError(err) + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.expectedResp.String(), tc.respType.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegatorUnbondingDelegationsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + ubdsLength int + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/unbonding_delegations", baseURL, "wrongValAddress"), + true, + 0, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/unbonding_delegations", baseURL, ""), + true, + 0, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/unbonding_delegations", baseURL, val.Address.String()), + false, + 1, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var ubds types.QueryDelegatorUnbondingDelegationsResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &ubds) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Len(ubds.UnbondingResponses, tc.ubdsLength) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryRedelegationsGRPC() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations", baseURL, "wrongValAddress"), + true, + }, + { + "with no validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations", baseURL, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations", baseURL, val.Address.String()), + false, + }, + { + "valid request with src address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations?src_validator_addr=%s", baseURL, val.Address.String(), val.ValAddress.String()), + false, + }, + { + "valid request with dst address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations?dst_validator_addr=%s", baseURL, val.Address.String(), val2.ValAddress.String()), + false, + }, + { + "valid request with dst address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/redelegations?src_validator_addr=%s&dst_validator_addr=%s", baseURL, val.Address.String(), val.ValAddress.String(), val2.ValAddress.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + var redelegations types.QueryRedelegationsResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &redelegations) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + s.Require().Len(redelegations.RedelegationResponses, 1) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.DelegatorAddress, val.Address.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorSrcAddress, val.ValAddress.String()) + s.Require().Equal(redelegations.RedelegationResponses[0].Redelegation.ValidatorDstAddress, val2.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegatorValidatorsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong delegator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators", baseURL, "wrongDelAddress"), + true, + }, + { + "with no delegator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators", baseURL, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators", baseURL, val.Address.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var validators types.QueryDelegatorValidatorsResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &validators) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Len(validators.Validators, len(s.network.Validators)) + s.Require().Equal(int(validators.Pagination.Total), len(s.network.Validators)) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryDelegatorValidatorGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong delegator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators/%s", baseURL, "wrongAccAddress", val.ValAddress.String()), + true, + }, + { + "wrong validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators/%s", baseURL, val.Address.String(), "wrongValAddress"), + true, + }, + { + "with empty delegator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators/%s", baseURL, "", val.ValAddress.String()), + true, + }, + { + "with empty validator address", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators/%s", baseURL, val.Address.String(), ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/delegators/%s/validators/%s", baseURL, val.Address.String(), val.ValAddress.String()), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var validator types.QueryDelegatorValidatorResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &validator) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotNil(validator) + s.Require().Equal(validator.Validator.OperatorAddress, val.ValAddress.String()) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryHistoricalInfoGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + error bool + }{ + { + "wrong height", + fmt.Sprintf("%s/cosmos/staking/v1beta1/historical_info/%s", baseURL, "-1"), + true, + }, + { + "with no height", + fmt.Sprintf("%s/cosmos/staking/v1beta1/historical_info/%s", baseURL, ""), + true, + }, + { + "valid request", + fmt.Sprintf("%s/cosmos/staking/v1beta1/historical_info/%s", baseURL, "2"), + false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + resp, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + var historical_info types.QueryHistoricalInfoResponse + + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &historical_info) + + if tc.error { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().NotNil(historical_info) + } + }) + } +} + +func (s *IntegrationTestSuite) TestQueryParamsGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params", + fmt.Sprintf("%s/cosmos/staking/v1beta1/params", baseURL), + &types.QueryParamsResponse{}, + &types.QueryParamsResponse{ + Params: types.DefaultParams(), + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + s.Run(tc.name, func() { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected, tc.respType) + }) + } +} + +func (s *IntegrationTestSuite) TestQueryPoolGRPC() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + respType proto.Message + expected proto.Message + }{ + { + "gRPC request params", + fmt.Sprintf("%s/cosmos/staking/v1beta1/pool", baseURL), + &types.QueryPoolResponse{}, + &types.QueryPoolResponse{ + Pool: types.Pool{ + NotBondedTokens: sdk.NewInt(10), + BondedTokens: cli.DefaultTokens.Mul(sdk.NewInt(2)).Sub(sdk.NewInt(10)), + }, + }, + }, + } + + for _, tc := range testCases { + tc := tc + resp, err := rest.GetRequest(tc.url) + s.Run(tc.name, func() { + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, tc.respType)) + s.Require().Equal(tc.expected, tc.respType) + }) + } +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index ddd6c03c7c6b..d515d5dfed9b 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -8,135 +8,135 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" + clientrest "github.com/cosmos/cosmos-sdk/client/rest" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { // Get all delegations from a delegator r.HandleFunc( "/staking/delegators/{delegatorAddr}/delegations", - delegatorDelegationsHandlerFn(cliCtx), + delegatorDelegationsHandlerFn(clientCtx), ).Methods("GET") // Get all unbonding delegations from a delegator r.HandleFunc( "/staking/delegators/{delegatorAddr}/unbonding_delegations", - delegatorUnbondingDelegationsHandlerFn(cliCtx), + delegatorUnbondingDelegationsHandlerFn(clientCtx), ).Methods("GET") // Get all staking txs (i.e msgs) from a delegator r.HandleFunc( "/staking/delegators/{delegatorAddr}/txs", - delegatorTxsHandlerFn(cliCtx), + delegatorTxsHandlerFn(clientCtx), ).Methods("GET") // Query all validators that a delegator is bonded to r.HandleFunc( "/staking/delegators/{delegatorAddr}/validators", - delegatorValidatorsHandlerFn(cliCtx), + delegatorValidatorsHandlerFn(clientCtx), ).Methods("GET") // Query a validator that a delegator is bonded to r.HandleFunc( "/staking/delegators/{delegatorAddr}/validators/{validatorAddr}", - delegatorValidatorHandlerFn(cliCtx), + delegatorValidatorHandlerFn(clientCtx), ).Methods("GET") // Query a delegation between a delegator and a validator r.HandleFunc( "/staking/delegators/{delegatorAddr}/delegations/{validatorAddr}", - delegationHandlerFn(cliCtx), + delegationHandlerFn(clientCtx), ).Methods("GET") // Query all unbonding delegations between a delegator and a validator r.HandleFunc( "/staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", - unbondingDelegationHandlerFn(cliCtx), + unbondingDelegationHandlerFn(clientCtx), ).Methods("GET") // Query redelegations (filters in query params) r.HandleFunc( "/staking/redelegations", - redelegationsHandlerFn(cliCtx), + redelegationsHandlerFn(clientCtx), ).Methods("GET") // Get all validators r.HandleFunc( "/staking/validators", - validatorsHandlerFn(cliCtx), + validatorsHandlerFn(clientCtx), ).Methods("GET") // Get a single validator info r.HandleFunc( "/staking/validators/{validatorAddr}", - validatorHandlerFn(cliCtx), + validatorHandlerFn(clientCtx), ).Methods("GET") // Get all delegations to a validator r.HandleFunc( "/staking/validators/{validatorAddr}/delegations", - validatorDelegationsHandlerFn(cliCtx), + validatorDelegationsHandlerFn(clientCtx), ).Methods("GET") // Get all unbonding delegations from a validator r.HandleFunc( "/staking/validators/{validatorAddr}/unbonding_delegations", - validatorUnbondingDelegationsHandlerFn(cliCtx), + validatorUnbondingDelegationsHandlerFn(clientCtx), ).Methods("GET") // Get HistoricalInfo at a given height r.HandleFunc( "/staking/historical_info/{height}", - historicalInfoHandlerFn(cliCtx), + historicalInfoHandlerFn(clientCtx), ).Methods("GET") // Get the current state of the staking pool r.HandleFunc( "/staking/pool", - poolHandlerFn(cliCtx), + poolHandlerFn(clientCtx), ).Methods("GET") // Get the current staking parameter values r.HandleFunc( "/staking/parameters", - paramsHandlerFn(cliCtx), + paramsHandlerFn(clientCtx), ).Methods("GET") - } // HTTP request handler to query a delegator delegations -func delegatorDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorDelegations)) +func delegatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { + return queryDelegator(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorDelegations)) } // HTTP request handler to query a delegator unbonding delegations -func delegatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryDelegator(cliCtx, "custom/staking/delegatorUnbondingDelegations") +func delegatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorUnbondingDelegations)) } // HTTP request handler to query all staking txs (msgs) from a delegator -func delegatorTxsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var typesQuerySlice []string + vars := mux.Vars(r) delegatorAddr := vars["delegatorAddr"] - _, err := sdk.AccAddressFromBech32(delegatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if _, err := sdk.AccAddressFromBech32(delegatorAddr); rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } typesQuery := r.URL.Query().Get("type") trimmedQuery := strings.TrimSpace(typesQuery) + if len(trimmedQuery) != 0 { typesQuerySlice = strings.Split(trimmedQuery, " ") } @@ -172,34 +172,34 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } for _, action := range actions { - foundTxs, errQuery := queryTxs(cliCtx, action, delegatorAddr) - if errQuery != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, errQuery.Error()) + foundTxs, errQuery := queryTxs(clientCtx, action, delegatorAddr) + if rest.CheckInternalServerError(w, errQuery) { + return } + txs = append(txs, foundTxs) } - res, err := cliCtx.Codec.MarshalJSON(txs) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, err := clientCtx.LegacyAmino.MarshalJSON(txs) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, cliCtx, res) + rest.PostProcessResponseBare(w, clientCtx, res) } } // HTTP request handler to query an unbonding-delegation -func unbondingDelegationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryBonds(cliCtx, "custom/staking/unbondingDelegation") +func unbondingDelegationHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryUnbondingDelegation)) } // HTTP request handler to query redelegations -func redelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func redelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var params types.QueryRedelegationParams - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } @@ -210,180 +210,187 @@ func redelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { if len(bechDelegatorAddr) != 0 { delegatorAddr, err := sdk.AccAddressFromBech32(bechDelegatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } + params.DelegatorAddr = delegatorAddr } if len(bechSrcValidatorAddr) != 0 { srcValidatorAddr, err := sdk.ValAddressFromBech32(bechSrcValidatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } + params.SrcValidatorAddr = srcValidatorAddr } if len(bechDstValidatorAddr) != 0 { dstValidatorAddr, err := sdk.ValAddressFromBech32(bechDstValidatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } + params.DstValidatorAddr = dstValidatorAddr } - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData("custom/staking/redelegations", bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryRedelegations), bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query a delegation -func delegationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegation)) +func delegationHandlerFn(clientCtx client.Context) http.HandlerFunc { + return queryBonds(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegation)) } // HTTP request handler to query all delegator bonded validators -func delegatorValidatorsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryDelegator(cliCtx, "custom/staking/delegatorValidators") +func delegatorValidatorsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidators)) } // HTTP request handler to get information from a currently bonded validator -func delegatorValidatorHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryBonds(cliCtx, "custom/staking/delegatorValidator") +func delegatorValidatorHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorValidator)) } // HTTP request handler to query list of validators -func validatorsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func validatorsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } status := r.FormValue("status") + // These are query params that were available in =<0.39. We show a nice + // error message for this breaking change. + if status == "bonded" || status == "unbonding" || status == "unbonded" { + err := fmt.Errorf("cosmos sdk v0.40 introduces a breaking change on this endpoint:"+ + " instead of querying using `?status=%s`, please use `status=BOND_STATUS_%s`. For more"+ + " info, please see our REST endpoint migration guide at %s", status, strings.ToUpper(status), clientrest.DeprecationURL) + + if rest.CheckBadRequestError(w, err) { + return + } + + } + if status == "" { - status = sdk.BondStatusBonded + status = types.BondStatusBonded } params := types.NewQueryValidatorsParams(page, limit, status) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidators) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + + res, height, err := clientCtx.QueryWithData(route, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query the validator information from a given validator address -func validatorHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryValidator(cliCtx, "custom/staking/validator") +func validatorHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidator)) } // HTTP request handler to query all unbonding delegations from a validator -func validatorDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorDelegations)) +func validatorDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { + return queryValidator(clientCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorDelegations)) } // HTTP request handler to query all unbonding delegations from a validator -func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return queryValidator(cliCtx, "custom/staking/validatorUnbondingDelegations") +func validatorUnbondingDelegationsHandlerFn(cliCtx client.Context) http.HandlerFunc { + return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorUnbondingDelegations)) } // HTTP request handler to query historical info at a given height -func historicalInfoHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func historicalInfoHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) heightStr := vars["height"] + height, err := strconv.ParseInt(heightStr, 10, 64) if err != nil || height < 0 { rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Must provide non-negative integer for height: %v", err)) return } - params := types.NewQueryHistoricalInfoParams(height) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + params := types.QueryHistoricalInfoRequest{Height: height} + + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckInternalServerError(w, err) { return } - route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalInfo) - res, height, err := cliCtx.QueryWithData(route, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalInfo), bz) + if rest.CheckBadRequestError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query the pool information -func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func poolHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData("custom/staking/pool", nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryPool), nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } // HTTP request handler to query the staking params values -func paramsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func paramsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - res, height, err := cliCtx.QueryWithData("custom/staking/parameters", nil) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryParameters), nil) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/staking/client/rest/rest.go b/x/staking/client/rest/rest.go index 4237d59e258a..35bb8da68139 100644 --- a/x/staking/client/rest/rest.go +++ b/x/staking/client/rest/rest.go @@ -3,11 +3,13 @@ package rest import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/rest" + + "github.com/cosmos/cosmos-sdk/client" ) -// RegisterRoutes registers staking-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - registerQueryRoutes(cliCtx, r) - registerTxRoutes(cliCtx, r) +func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + registerQueryRoutes(clientCtx, r) + registerTxHandlers(clientCtx, r) } diff --git a/x/staking/client/rest/rest_test.go b/x/staking/client/rest/rest_test.go new file mode 100644 index 000000000000..43b9afdb6ddf --- /dev/null +++ b/x/staking/client/rest/rest_test.go @@ -0,0 +1,62 @@ +// +build norace + +package rest_test + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (s *IntegrationTestSuite) TestLegacyGetValidators() { + val := s.network.Validators[0] + baseURL := val.APIAddress + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "old status should show error message", + fmt.Sprintf("%s/staking/validators?status=bonded", baseURL), + true, "cosmos sdk v0.40 introduces a breaking change on this endpoint: instead of" + + " querying using `?status=bonded`, please use `status=BOND_STATUS_BONDED`. For more" + + " info, please see our REST endpoint migration guide at https://docs.cosmos.network/master/migrations/rest.html", + }, + { + "new status should work", + fmt.Sprintf("%s/staking/validators?status=BOND_STATUS_BONDED", baseURL), + false, "", + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + respJSON, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + + if tc.expErr { + var errResp rest.ErrorResponse + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &errResp)) + + s.Require().Equal(errResp.Error, tc.expErrMsg) + } else { + var resp = rest.ResponseWithHeight{} + err = val.ClientCtx.LegacyAmino.UnmarshalJSON(respJSON, &resp) + s.Require().NoError(err) + + // Check result is not empty. + var validators []types.Validator + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(resp.Result, &validators)) + s.Require().Greater(len(validators), 0) + // While we're at it, also check that the consensus_pubkey is + // an Any, and not bech32 anymore. + s.Require().Contains(string(resp.Result), "\"consensus_pubkey\": {\n \"type\": \"tendermint/PubKeyEd25519\",") + } + }) + } +} diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index bd6016bed104..26e859c41a0d 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -6,25 +6,25 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "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/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { +func registerTxHandlers(clientCtx client.Context, r *mux.Router) { r.HandleFunc( "/staking/delegators/{delegatorAddr}/delegations", - postDelegationsHandlerFn(cliCtx), + newPostDelegationsHandlerFn(clientCtx), ).Methods("POST") r.HandleFunc( "/staking/delegators/{delegatorAddr}/unbonding_delegations", - postUnbondingDelegationsHandlerFn(cliCtx), + newPostUnbondingDelegationsHandlerFn(clientCtx), ).Methods("POST") r.HandleFunc( "/staking/delegators/{delegatorAddr}/redelegations", - postRedelegationsHandlerFn(cliCtx), + newPostRedelegationsHandlerFn(clientCtx), ).Methods("POST") } @@ -55,11 +55,10 @@ type ( } ) -func postDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newPostDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req DelegateRequest - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -69,14 +68,12 @@ func postDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } msg := types.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } @@ -85,15 +82,14 @@ func postDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -func postRedelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newPostRedelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req RedelegateRequest - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -103,14 +99,12 @@ func postRedelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } msg := types.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } @@ -119,15 +113,14 @@ func postRedelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -func postUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { +func newPostUnbondingDelegationsHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req UndelegateRequest - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -137,14 +130,12 @@ func postUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFu } msg := types.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } @@ -153,6 +144,6 @@ func postUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFu return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index 5d106823a97a..2b8a1c4dcfc1 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -6,10 +6,10 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -20,11 +20,12 @@ func contains(stringSlice []string, txType string) bool { return true } } + return false } // queries staking txs -func queryTxs(cliCtx context.CLIContext, action string, delegatorAddr string) (*sdk.SearchTxsResult, error) { +func queryTxs(clientCtx client.Context, action string, delegatorAddr string) (*sdk.SearchTxsResult, error) { page := 1 limit := 100 events := []string{ @@ -32,117 +33,112 @@ func queryTxs(cliCtx context.CLIContext, action string, delegatorAddr string) (* fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, delegatorAddr), } - return utils.QueryTxsByEvents(cliCtx, events, page, limit) + return authclient.QueryTxsByEvents(clientCtx, events, page, limit, "") } -func queryBonds(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { +func queryBonds(clientCtx client.Context, endpoint string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQueryBondsParams(delegatorAddr, validatorAddr) + params := types.QueryDelegatorValidatorRequest{DelegatorAddr: delegatorAddr.String(), ValidatorAddr: validatorAddr.String()} - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(endpoint, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryDelegator(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { +func queryDelegator(clientCtx client.Context, endpoint string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } params := types.NewQueryDelegatorParams(delegatorAddr) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(endpoint, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } -func queryValidator(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { +func queryValidator(clientCtx client.Context, endpoint string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32validatorAddr := vars["validatorAddr"] + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if rest.CheckBadRequestError(w, err) { + return + } + validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + clientCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, clientCtx, r) if !ok { return } - params := types.NewQueryValidatorParams(validatorAddr) + params := types.NewQueryValidatorParams(validatorAddr, page, limit) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, height, err := cliCtx.QueryWithData(endpoint, bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, height, err := clientCtx.QueryWithData(endpoint, bz) + if rest.CheckInternalServerError(w, err) { return } - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) + clientCtx = clientCtx.WithHeight(height) + rest.PostProcessResponse(w, clientCtx, res) } } diff --git a/x/staking/client/testutil/test_helpers.go b/x/staking/client/testutil/test_helpers.go new file mode 100644 index 000000000000..1a2a4b84bcbf --- /dev/null +++ b/x/staking/client/testutil/test_helpers.go @@ -0,0 +1,47 @@ +package testutil + +import ( + "fmt" + + "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" + stakingcli "github.com/cosmos/cosmos-sdk/x/staking/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()), +} + +// MsgRedelegateExec creates a redelegate message. +func MsgRedelegateExec(clientCtx client.Context, from, src, dst, amount fmt.Stringer, + extraArgs ...string) (testutil.BufferWriter, error) { + + args := []string{ + src.String(), + dst.String(), + amount.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from.String()), + } + + args = append(args, commonArgs...) + return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewRedelegateCmd(), args) +} + +// MsgUnbondExec creates a unbond message. +func MsgUnbondExec(clientCtx client.Context, from fmt.Stringer, valAddress, + amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { + + args := []string{ + valAddress.String(), + amount.String(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from.String()), + } + + args = append(args, commonArgs...) + return clitestutil.ExecTestCLICmd(clientCtx, stakingcli.NewUnbondCmd(), args) +} diff --git a/x/staking/common_test.go b/x/staking/common_test.go new file mode 100644 index 000000000000..0c26e6830d5a --- /dev/null +++ b/x/staking/common_test.go @@ -0,0 +1,62 @@ +package staking_test + +import ( + "math/big" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func init() { + sdk.PowerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) +} + +// nolint:deadcode,unused,varcheck +var ( + priv1 = secp256k1.GenPrivKey() + addr1 = sdk.AccAddress(priv1.PubKey().Address()) + priv2 = secp256k1.GenPrivKey() + addr2 = sdk.AccAddress(priv2.PubKey().Address()) + + valKey = ed25519.GenPrivKey() + valAddr = sdk.AccAddress(valKey.PubKey().Address()) + + commissionRates = types.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + + PKs = simapp.CreateTestPubKeys(500) +) + +// getBaseSimappWithCustomKeeper Returns a simapp with custom StakingKeeper +// to avoid messing with the hooks. +func getBaseSimappWithCustomKeeper() (*codec.LegacyAmino, *simapp.SimApp, sdk.Context) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + appCodec := app.AppCodec() + + app.StakingKeeper = keeper.NewKeeper( + appCodec, + app.GetKey(types.StoreKey), + app.AccountKeeper, + app.BankKeeper, + app.GetSubspace(types.ModuleName), + ) + app.StakingKeeper.SetParams(ctx, types.DefaultParams()) + + return codec.NewLegacyAmino(), app, ctx +} + +// generateAddresses generates numAddrs of normal AccAddrs and ValAddrs +func generateAddresses(app *simapp.SimApp, ctx sdk.Context, numAddrs int, accAmount sdk.Int) ([]sdk.AccAddress, []sdk.ValAddress) { + addrDels := simapp.AddTestAddrsIncremental(app, ctx, numAddrs, accAmount) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + + return addrDels, addrVals +} diff --git a/x/staking/exported/exported.go b/x/staking/exported/exported.go index 7c8a257cd342..eabd8aa3ddaa 100644 --- a/x/staking/exported/exported.go +++ b/x/staking/exported/exported.go @@ -1,38 +1 @@ package exported - -import ( - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// DelegationI delegation bond for a delegated proof of stake system -type DelegationI interface { - GetDelegatorAddr() sdk.AccAddress // delegator sdk.AccAddress for the bond - GetValidatorAddr() sdk.ValAddress // validator operator address - GetShares() sdk.Dec // amount of validator's shares held in this delegation -} - -// ValidatorI expected validator functions -type ValidatorI interface { - IsJailed() bool // whether the validator is jailed - GetMoniker() string // moniker of the validator - GetStatus() sdk.BondStatus // status of the validator - IsBonded() bool // check if has a bonded status - IsUnbonded() bool // check if has status unbonded - IsUnbonding() bool // check if has status unbonding - GetOperator() sdk.ValAddress // operator address to receive/return validators coins - GetConsPubKey() crypto.PubKey // validation consensus pubkey - GetConsAddr() sdk.ConsAddress // validation consensus address - GetTokens() sdk.Int // validation tokens - GetBondedTokens() sdk.Int // validator bonded tokens - GetConsensusPower() int64 // validation power in tendermint - GetCommission() sdk.Dec // validator commission rate - GetMinSelfDelegation() sdk.Int // validator minimum self delegation - GetDelegatorShares() sdk.Dec // total outstanding delegator shares - TokensFromShares(sdk.Dec) sdk.Dec // token worth of provided delegator shares - TokensFromSharesTruncated(sdk.Dec) sdk.Dec // token worth of provided delegator shares, truncated - TokensFromSharesRoundUp(sdk.Dec) sdk.Dec // token worth of provided delegator shares, rounded up - SharesFromTokens(amt sdk.Int) (sdk.Dec, error) // shares worth of delegator's bond - SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, error) // truncated shares worth of delegator's bond -} diff --git a/x/staking/genesis.go b/x/staking/genesis.go index 4c1bbee7072a..02f6bc317473 100644 --- a/x/staking/genesis.go +++ b/x/staking/genesis.go @@ -2,12 +2,14 @@ package staking import ( "fmt" + "log" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -16,9 +18,10 @@ import ( // setting the indexes. In addition, it also sets any delegations found in // data. Finally, it updates the bonded validators. // Returns final validator set after applying all declaration and delegations -func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper, - supplyKeeper types.SupplyKeeper, data types.GenesisState) (res []abci.ValidatorUpdate) { - +func InitGenesis( + ctx sdk.Context, keeper keeper.Keeper, accountKeeper types.AccountKeeper, + bankKeeper types.BankKeeper, data *types.GenesisState, +) (res []abci.ValidatorUpdate) { bondedTokens := sdk.ZeroInt() notBondedTokens := sdk.ZeroInt() @@ -41,18 +44,18 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep // Call the creation hook if not exported if !data.Exported { - keeper.AfterValidatorCreated(ctx, validator.OperatorAddress) + keeper.AfterValidatorCreated(ctx, validator.GetOperator()) } // update timeslice if necessary if validator.IsUnbonding() { - keeper.InsertValidatorQueue(ctx, validator) + keeper.InsertUnbondingValidatorQueue(ctx, validator) } switch validator.GetStatus() { - case sdk.Bonded: + case types.Bonded: bondedTokens = bondedTokens.Add(validator.GetTokens()) - case sdk.Unbonding, sdk.Unbonded: + case types.Unbonding, types.Unbonded: notBondedTokens = notBondedTokens.Add(validator.GetTokens()) default: panic("invalid validator status") @@ -60,20 +63,26 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep } for _, delegation := range data.Delegations { + delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) + } + // Call the before-creation hook if not exported if !data.Exported { - keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + keeper.BeforeDelegationCreated(ctx, delegatorAddress, delegation.GetValidatorAddr()) } - keeper.SetDelegation(ctx, delegation) + keeper.SetDelegation(ctx, delegation) // Call the after-modification hook if not exported if !data.Exported { - keeper.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + keeper.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()) } } for _, ubd := range data.UnbondingDelegations { keeper.SetUnbondingDelegation(ctx, ubd) + for _, entry := range ubd.Entries { keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime) notBondedTokens = notBondedTokens.Add(entry.Balance) @@ -82,6 +91,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep for _, red := range data.Redelegations { keeper.SetRedelegation(ctx, red) + for _, entry := range red.Entries { keeper.InsertRedelegationQueue(ctx, red, entry.CompletionTime) } @@ -98,11 +108,12 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 // add coins if not provided on genesis - if bondedPool.GetCoins().IsZero() { - if err := bondedPool.SetCoins(bondedCoins); err != nil { + if bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()).IsZero() { + if err := bankKeeper.SetBalances(ctx, bondedPool.GetAddress(), bondedCoins); err != nil { panic(err) } - supplyKeeper.SetModuleAccount(ctx, bondedPool) + + accountKeeper.SetModuleAccount(ctx, bondedPool) } notBondedPool := keeper.GetNotBondedPool(ctx) @@ -110,27 +121,38 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName)) } - if notBondedPool.GetCoins().IsZero() { - if err := notBondedPool.SetCoins(notBondedCoins); err != nil { + if bankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()).IsZero() { + if err := bankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), notBondedCoins); err != nil { panic(err) } - supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + accountKeeper.SetModuleAccount(ctx, notBondedPool) } // don't need to run Tendermint updates if we exported if data.Exported { for _, lv := range data.LastValidatorPowers { - keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) - validator, found := keeper.GetValidator(ctx, lv.Address) + valAddr, err := sdk.ValAddressFromBech32(lv.Address) + if err != nil { + panic(err) + } + keeper.SetLastValidatorPower(ctx, valAddr, lv.Power) + validator, found := keeper.GetValidator(ctx, valAddr) + if !found { panic(fmt.Sprintf("validator %s not found", lv.Address)) } + update := validator.ABCIValidatorUpdate() update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block res = append(res, update) } } else { - res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + var err error + res, err = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } } return res @@ -139,33 +161,34 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep // ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, params, validators, and bonds found in // the keeper. -func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { - params := keeper.GetParams(ctx) - lastTotalPower := keeper.GetLastTotalPower(ctx) - validators := keeper.GetAllValidators(ctx) - delegations := keeper.GetAllDelegations(ctx) +func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState { var unbondingDelegations []types.UnbondingDelegation + keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) { unbondingDelegations = append(unbondingDelegations, ubd) return false }) + var redelegations []types.Redelegation + keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) { redelegations = append(redelegations, red) return false }) + var lastValidatorPowers []types.LastValidatorPower + keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { - lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{Address: addr, Power: power}) + lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{Address: addr.String(), Power: power}) return false }) - return types.GenesisState{ - Params: params, - LastTotalPower: lastTotalPower, + return &types.GenesisState{ + Params: keeper.GetParams(ctx), + LastTotalPower: keeper.GetLastTotalPower(ctx), LastValidatorPowers: lastValidatorPowers, - Validators: validators, - Delegations: delegations, + Validators: keeper.GetAllValidators(ctx), + Delegations: keeper.GetAllDelegations(ctx), UnbondingDelegations: unbondingDelegations, Redelegations: redelegations, Exported: true, @@ -173,12 +196,22 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { } // WriteValidators returns a slice of bonded genesis validators. -func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { - keeper.IterateLastValidators(ctx, func(_ int64, validator exported.ValidatorI) (stop bool) { +func WriteValidators(ctx sdk.Context, keeper keeper.Keeper) (vals []tmtypes.GenesisValidator, err error) { + keeper.IterateLastValidators(ctx, func(_ int64, validator types.ValidatorI) (stop bool) { + pk, err := validator.ConsPubKey() + if err != nil { + return true + } + tmPk, err := cryptocodec.ToTmPubKeyInterface(pk) + if err != nil { + return true + } + vals = append(vals, tmtypes.GenesisValidator{ - PubKey: validator.GetConsPubKey(), - Power: validator.GetConsensusPower(), - Name: validator.GetMoniker(), + Address: sdk.ConsAddress(tmPk.Address()).Bytes(), + PubKey: tmPk, + Power: validator.GetConsensusPower(), + Name: validator.GetMoniker(), }) return false @@ -189,34 +222,43 @@ func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisVali // ValidateGenesis validates the provided staking genesis state to ensure the // expected invariants holds. (i.e. params in correct bounds, no duplicate validators) -func ValidateGenesis(data types.GenesisState) error { - err := validateGenesisStateValidators(data.Validators) - if err != nil { - return err - } - err = data.Params.Validate() - if err != nil { +func ValidateGenesis(data *types.GenesisState) error { + if err := validateGenesisStateValidators(data.Validators); err != nil { return err } - return nil + return data.Params.Validate() } -func validateGenesisStateValidators(validators []types.Validator) (err error) { +func validateGenesisStateValidators(validators []types.Validator) error { addrMap := make(map[string]bool, len(validators)) + for i := 0; i < len(validators); i++ { val := validators[i] - strKey := string(val.ConsPubKey.Bytes()) + consPk, err := val.ConsPubKey() + if err != nil { + return err + } + consAddr, err := val.GetConsAddr() + if err != nil { + return err + } + strKey := string(consPk.Bytes()) + if _, ok := addrMap[strKey]; ok { - return fmt.Errorf("duplicate validator in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) + return fmt.Errorf("duplicate validator in genesis state: moniker %v, address %v", val.Description.Moniker, consAddr) } + if val.Jailed && val.IsBonded() { - return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) + return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, address %v", val.Description.Moniker, consAddr) } + if val.DelegatorShares.IsZero() && !val.IsUnbonding() { return fmt.Errorf("bonded/unbonded genesis validator cannot have zero delegator shares, validator: %v", val) } + addrMap[strKey] = true } - return + + return nil } diff --git a/x/staking/genesis_test.go b/x/staking/genesis_test.go index 4a56b3ac9c9c..28e3dec8a0fd 100644 --- a/x/staking/genesis_test.go +++ b/x/staking/genesis_test.go @@ -1,60 +1,93 @@ -package staking +package staking_test import ( "fmt" "testing" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/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" - keep "github.com/cosmos/cosmos-sdk/x/staking/keeper" + 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) { + _, 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) { - ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000) + app, ctx, addrs := bootstrapGenesisTest(t, 1000, 10) valTokens := sdk.TokensFromConsensusPower(1) - params := keeper.GetParams(ctx) - validators := make([]Validator, 2) - var delegations []Delegation + params := app.StakingKeeper.GetParams(ctx) + validators := make([]types.Validator, 2) + var delegations []types.Delegation + + pk0, err := codectypes.NewAnyWithValue(PKs[0]) + require.NoError(t, err) + + pk1, err := codectypes.NewAnyWithValue(PKs[1]) + require.NoError(t, err) // initialize the validators - validators[0].OperatorAddress = sdk.ValAddress(keep.Addrs[0]) - validators[0].ConsPubKey = keep.PKs[0] - validators[0].Description = NewDescription("hoop", "", "", "", "") - validators[0].Status = sdk.Bonded + 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(keep.Addrs[1]) - validators[1].ConsPubKey = keep.PKs[1] - validators[1].Description = NewDescription("bloop", "", "", "", "") - validators[1].Status = sdk.Bonded + 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() genesisState := types.NewGenesisState(params, validators, delegations) - vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState) + vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState) - actualGenesis := ExportGenesis(ctx, keeper) + actualGenesis := staking.ExportGenesis(ctx, app.StakingKeeper) require.Equal(t, genesisState.Params, actualGenesis.Params) require.Equal(t, genesisState.Delegations, actualGenesis.Delegations) - require.EqualValues(t, keeper.GetAllValidators(ctx), actualGenesis.Validators) + require.EqualValues(t, app.StakingKeeper.GetAllValidators(ctx), actualGenesis.Validators) + + // Ensure validators have addresses. + vals2, err := staking.WriteValidators(ctx, app.StakingKeeper) + require.NoError(t, err) + for _, val := range vals2 { + require.NotEmpty(t, val.Address) + } // now make sure the validators are bonded and intra-tx counters are correct - resVal, found := keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[0])) + resVal, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0])) require.True(t, found) - require.Equal(t, sdk.Bonded, resVal.Status) + require.Equal(t, types.Bonded, resVal.Status) - resVal, found = keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[1])) + resVal, found = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1])) require.True(t, found) - require.Equal(t, sdk.Bonded, resVal.Status) + require.Equal(t, types.Bonded, resVal.Status) abcivals := make([]abci.ValidatorUpdate, len(vals)) for i, val := range validators { @@ -68,17 +101,17 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { size := 200 require.True(t, size > 100) - ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000) - - params := keeper.GetParams(ctx) - delegations := []Delegation{} - validators := make([]Validator, size) + app, ctx, addrs := bootstrapGenesisTest(t, 1000, 200) + params := app.StakingKeeper.GetParams(ctx) + delegations := []types.Delegation{} + validators := make([]types.Validator, size) + var err error for i := range validators { - validators[i] = NewValidator(sdk.ValAddress(keep.Addrs[i]), - keep.PKs[i], NewDescription(fmt.Sprintf("#%d", i), "", "", "", "")) - - validators[i].Status = sdk.Bonded + validators[i], err = types.NewValidator(sdk.ValAddress(addrs[i]), + PKs[i], types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", "")) + require.NoError(t, err) + validators[i].Status = types.Bonded tokens := sdk.TokensFromConsensusPower(1) if i < 100 { @@ -89,7 +122,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { } genesisState := types.NewGenesisState(params, validators, delegations) - vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState) + vals := staking.InitGenesis(ctx, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, genesisState) abcivals := make([]abci.ValidatorUpdate, 100) for i, val := range validators[:100] { @@ -102,7 +135,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { func TestValidateGenesis(t *testing.T) { genValidators1 := make([]types.Validator, 1, 5) pk := ed25519.GenPrivKey().PubKey() - genValidators1[0] = types.NewValidator(sdk.ValAddress(pk.Address()), pk, types.NewDescription("", "", "", "", "")) + genValidators1[0] = teststaking.NewValidator(t, sdk.ValAddress(pk.Address()), pk) genValidators1[0].Tokens = sdk.OneInt() genValidators1[0].DelegatorShares = sdk.OneDec() @@ -124,7 +157,7 @@ func TestValidateGenesis(t *testing.T) { {"jailed and bonded validator", func(data *types.GenesisState) { data.Validators = genValidators1 data.Validators[0].Jailed = true - data.Validators[0].Status = sdk.Bonded + data.Validators[0].Status = types.Bonded }, true}, } @@ -132,11 +165,11 @@ func TestValidateGenesis(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { genesisState := types.DefaultGenesisState() - tt.mutate(&genesisState) + tt.mutate(genesisState) if tt.wantErr { - assert.Error(t, ValidateGenesis(genesisState)) + assert.Error(t, staking.ValidateGenesis(genesisState)) } else { - assert.NoError(t, ValidateGenesis(genesisState)) + assert.NoError(t, staking.ValidateGenesis(genesisState)) } }) } diff --git a/x/staking/handler.go b/x/staking/handler.go index 180bb8008672..6d89c7a29c53 100644 --- a/x/staking/handler.go +++ b/x/staking/handler.go @@ -1,11 +1,6 @@ package staking import ( - "time" - - tmstrings "github.com/tendermint/tendermint/libs/strings" - tmtypes "github.com/tendermint/tendermint/types" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/staking/keeper" @@ -13,262 +8,34 @@ import ( ) func NewHandler(k keeper.Keeper) sdk.Handler { + msgServer := keeper.NewMsgServerImpl(k) + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case types.MsgCreateValidator: - return handleMsgCreateValidator(ctx, msg, k) + case *types.MsgCreateValidator: + res, err := msgServer.CreateValidator(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgEditValidator: - return handleMsgEditValidator(ctx, msg, k) + case *types.MsgEditValidator: + res, err := msgServer.EditValidator(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgDelegate: - return handleMsgDelegate(ctx, msg, k) + case *types.MsgDelegate: + res, err := msgServer.Delegate(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgBeginRedelegate: - return handleMsgBeginRedelegate(ctx, msg, k) + case *types.MsgBeginRedelegate: + res, err := msgServer.BeginRedelegate(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) - case types.MsgUndelegate: - return handleMsgUndelegate(ctx, msg, k) + case *types.MsgUndelegate: + res, err := msgServer.Undelegate(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) - } - } -} - -// These functions assume everything has been authenticated, -// now we just perform action and save - -func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) (*sdk.Result, error) { - // check to see if the pubkey or sender has been registered before - if _, found := k.GetValidator(ctx, msg.ValidatorAddress); found { - return nil, ErrValidatorOwnerExists - } - - if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey)); found { - return nil, ErrValidatorPubKeyExists - } - - if msg.Value.Denom != k.BondDenom(ctx) { - return nil, ErrBadDenom - } - - if _, err := msg.Description.EnsureLength(); err != nil { - return nil, err - } - - if ctx.ConsensusParams() != nil { - tmPubKey := tmtypes.TM2PB.PubKey(msg.PubKey) - if !tmstrings.StringInSlice(tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes) { - return nil, sdkerrors.Wrapf( - ErrValidatorPubKeyTypeNotSupported, - "got: %s, valid: %s", tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes, - ) - } - } - - validator := NewValidator(msg.ValidatorAddress, msg.PubKey, msg.Description) - commission := NewCommissionWithTime( - msg.Commission.Rate, msg.Commission.MaxRate, - msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, - ) - validator, err := validator.SetInitialCommission(commission) - if err != nil { - return nil, err - } - - validator.MinSelfDelegation = msg.MinSelfDelegation - - k.SetValidator(ctx, validator) - k.SetValidatorByConsAddr(ctx, validator) - k.SetNewValidatorByPowerIndex(ctx, validator) - - // call the after-creation hook - k.AfterValidatorCreated(ctx, validator.OperatorAddress) - - // move coins from the msg.Address account to a (self-delegation) delegator account - // the validator account and global shares are updated within here - // NOTE source will always be from a wallet which are unbonded - _, err = k.Delegate(ctx, msg.DelegatorAddress, msg.Value.Amount, sdk.Unbonded, validator, true) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeCreateValidator, - sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - }) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) (*sdk.Result, error) { - // validator must already be registered - validator, found := k.GetValidator(ctx, msg.ValidatorAddress) - if !found { - return nil, ErrNoValidatorFound - } - - // replace all editable fields (clients should autofill existing values) - description, err := validator.Description.UpdateDescription(msg.Description) - if err != nil { - return nil, err - } - - validator.Description = description - - if msg.CommissionRate != nil { - commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) - if err != nil { - return nil, err + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) } - - // call the before-modification hook since we're about to update the commission - k.BeforeValidatorModified(ctx, msg.ValidatorAddress) - - validator.Commission = commission } - - if msg.MinSelfDelegation != nil { - if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) { - return nil, ErrMinSelfDelegationDecreased - } - if msg.MinSelfDelegation.GT(validator.Tokens) { - return nil, ErrSelfDelegationBelowMinimum - } - - validator.MinSelfDelegation = (*msg.MinSelfDelegation) - } - - k.SetValidator(ctx, validator) - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeEditValidator, - sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()), - sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress.String()), - ), - }) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) (*sdk.Result, error) { - validator, found := k.GetValidator(ctx, msg.ValidatorAddress) - if !found { - return nil, ErrNoValidatorFound - } - - if msg.Amount.Denom != k.BondDenom(ctx) { - return nil, ErrBadDenom - } - - // NOTE: source funds are always unbonded - _, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, sdk.Unbonded, validator, true) - if err != nil { - return nil, err - } - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeDelegate, - sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - }) - - return &sdk.Result{Events: ctx.EventManager().Events()}, nil -} - -func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) (*sdk.Result, error) { - shares, err := k.ValidateUnbondAmount( - ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.Amount.Amount, - ) - if err != nil { - return nil, err - } - - if msg.Amount.Denom != k.BondDenom(ctx) { - return nil, ErrBadDenom - } - - completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, shares) - if err != nil { - return nil, err - } - - completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(completionTime) - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeUnbond, - sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), - sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - }) - - return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()}, nil -} - -func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) (*sdk.Result, error) { - shares, err := k.ValidateUnbondAmount( - ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.Amount.Amount, - ) - if err != nil { - return nil, err - } - - if msg.Amount.Denom != k.BondDenom(ctx) { - return nil, ErrBadDenom - } - - completionTime, err := k.BeginRedelegation( - ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress, shares, - ) - if err != nil { - return nil, err - } - - completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(completionTime) - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeRedelegate, - sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress.String()), - sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), - sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), - ), - }) - - return &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()}, nil } diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go index 9b45cc4c1992..4b48f09427c9 100644 --- a/x/staking/handler_test.go +++ b/x/staking/handler_test.go @@ -1,4 +1,4 @@ -package staking +package staking_test import ( "strings" @@ -7,270 +7,289 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/secp256k1" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" + "github.com/golang/protobuf/proto" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + 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" - keep "github.com/cosmos/cosmos-sdk/x/staking/keeper" + 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" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func TestValidatorByPowerIndex(t *testing.T) { - validatorAddr, validatorAddr3 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) +func bootstrapHandlerGenesisTest(t *testing.T, power int64, numAddrs int, accAmount sdk.Int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { + _, app, ctx := getBaseSimappWithCustomKeeper() + + addrDels, addrVals := generateAddresses(app, ctx, numAddrs, accAmount) + + 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, addrVals +} + +func TestValidatorByPowerIndex(t *testing.T) { initPower := int64(1000000) - initBond := sdk.TokensFromConsensusPower(initPower) - ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, validatorAddr3 := valAddrs[0], valAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) // must end-block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists - bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond) // verify that the by power index exists - validator, found := keeper.GetValidator(ctx, validatorAddr) + validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - power := GetValidatorsByPowerIndexKey(validator) - require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) + power := types.GetValidatorsByPowerIndexKey(validator) + require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) // create a second validator keep it bonded - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], initBond) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidatorWithValPower(validatorAddr3, PKs[2], initPower, true) // must end-block - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) // slash and jail the first validator - consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) - keeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) - keeper.Jail(ctx, consAddr0) - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + app.StakingKeeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Jail(ctx, consAddr0) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found = keeper.GetValidator(ctx, validatorAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding + require.Equal(t, types.Unbonding, validator.Status) // ensure is unbonding require.Equal(t, initBond.QuoRaw(2), validator.Tokens) // ensure tokens slashed - keeper.Unjail(ctx, consAddr0) + app.StakingKeeper.Unjail(ctx, consAddr0) // the old power record should have been deleted as the power changed - require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) + require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) // but the new power record should have been created - validator, found = keeper.GetValidator(ctx, validatorAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - power2 := GetValidatorsByPowerIndexKey(validator) - require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2)) + power2 := types.GetValidatorsByPowerIndexKey(validator) + require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power2)) // now the new record power index should be the same as the original record - power3 := GetValidatorsByPowerIndexKey(validator) + power3 := types.GetValidatorsByPowerIndexKey(validator) require.Equal(t, power2, power3) // unbond self-delegation totalBond := validator.TokensFromShares(bond.GetShares()).TruncateInt() - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, totalBond) - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, totalBond, true) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) + var resData types.MsgUndelegateResponse + err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) - require.NotNil(t, res) - - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + staking.EndBlocker(ctx, app.StakingKeeper) + staking.EndBlocker(ctx, app.StakingKeeper) // verify that by power key nolonger exists - _, found = keeper.GetValidator(ctx, validatorAddr) + _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.False(t, found) - require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power3)) + require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power3)) } func TestDuplicatesMsgCreateValidator(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - - addr1, addr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) - pk1, pk2 := keep.PKs[0], keep.PKs[1] + initPower := int64(1000000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower)) - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator1 := NewTestMsgCreateValidator(addr1, pk1, valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper) - require.NoError(t, err) - require.NotNil(t, res) + addr1, addr2 := valAddrs[0], valAddrs[1] + pk1, pk2 := PKs[0], PKs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + valTokens := tstaking.CreateValidatorWithValPower(addr1, pk1, 10, true) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found := keeper.GetValidator(ctx, addr1) - require.True(t, found) - assert.Equal(t, sdk.Bonded, validator.Status) - assert.Equal(t, addr1, validator.OperatorAddress) - assert.Equal(t, pk1, validator.ConsPubKey) + validator := tstaking.CheckValidator(addr1, types.Bonded, false) + assert.Equal(t, addr1.String(), validator.OperatorAddress) + consKey, err := validator.TmConsPublicKey() + require.NoError(t, err) + tmPk1, err := cryptocodec.ToTmProtoPublicKey(pk1) + require.NoError(t, err) + assert.Equal(t, tmPk1, consKey) assert.Equal(t, valTokens, validator.BondedTokens()) assert.Equal(t, valTokens.ToDec(), validator.DelegatorShares) - assert.Equal(t, Description{}, validator.Description) + assert.Equal(t, types.Description{}, validator.Description) // two validators can't have the same operator address - msgCreateValidator2 := NewTestMsgCreateValidator(addr1, pk2, valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator2, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.CreateValidator(addr1, pk2, valTokens, false) // two validators can't have the same pubkey - msgCreateValidator3 := NewTestMsgCreateValidator(addr2, pk1, valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator3, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.CreateValidator(addr2, pk1, valTokens, false) // must have different pubkey and operator - msgCreateValidator4 := NewTestMsgCreateValidator(addr2, pk2, valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidator(addr2, pk2, valTokens, true) // must end-block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) - validator, found = keeper.GetValidator(ctx, addr2) - - require.True(t, found) - assert.Equal(t, sdk.Bonded, validator.Status) - assert.Equal(t, addr2, validator.OperatorAddress) - assert.Equal(t, pk2, validator.ConsPubKey) + validator = tstaking.CheckValidator(addr2, types.Bonded, false) + assert.Equal(t, addr2.String(), validator.OperatorAddress) + consPk, err := validator.TmConsPublicKey() + require.NoError(t, err) + tmPk2, err := cryptocodec.ToTmProtoPublicKey(pk2) + require.NoError(t, err) + assert.Equal(t, tmPk2, consPk) assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) assert.True(sdk.DecEq(t, valTokens.ToDec(), validator.DelegatorShares)) - assert.Equal(t, Description{}, validator.Description) + assert.Equal(t, types.Description{}, validator.Description) } func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower)) + ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ + Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}}, + }) - addr := sdk.ValAddress(keep.Addrs[0]) + addr := valAddrs[0] invalidPk := secp256k1.GenPrivKey().PubKey() + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // invalid pukKey type should not be allowed - msgCreateValidator := NewTestMsgCreateValidator(addr, invalidPk, sdk.NewInt(10)) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.CreateValidator(addr, invalidPk, sdk.NewInt(10), false) +} +func TestBothPubKeyTypesMsgCreateValidator(t *testing.T) { + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, 1000, 2, sdk.NewInt(1000)) ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ - Validator: &abci.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeSecp256k1}}, + Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519, tmtypes.ABCIPubKeyTypeSecp256k1}}, }) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + + testCases := []struct { + name string + addr sdk.ValAddress + pk cryptotypes.PubKey + }{ + { + "can create a validator with ed25519 pubkey", + valAddrs[0], + ed25519.GenPrivKey().PubKey(), + }, + { + "can create a validator with secp256k1 pubkey", + valAddrs[1], + secp256k1.GenPrivKey().PubKey(), + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(*testing.T) { + tstaking.CreateValidator(tc.addr, tc.pk, sdk.NewInt(10), true) + }) + } } func TestLegacyValidatorDelegations(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, int64(1000)) + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) - bondAmount := sdk.TokensFromConsensusPower(10) - valAddr := sdk.ValAddress(keep.Addrs[0]) - valConsPubKey, valConsAddr := keep.PKs[0], sdk.ConsAddress(keep.PKs[0].Address()) - delAddr := keep.Addrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + valAddr := valAddrs[0] + valConsPubKey, valConsAddr := PKs[0], sdk.ConsAddress(PKs[0].Address()) + delAddr := delAddrs[1] // create validator - msgCreateVal := NewTestMsgCreateValidator(valAddr, valConsPubKey, bondAmount) - res, err := handleMsgCreateValidator(ctx, msgCreateVal, keeper) - require.NoError(t, err) - require.NotNil(t, res) + bondAmount := tstaking.CreateValidatorWithValPower(valAddr, valConsPubKey, 10, true) // must end-block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the validator exists and has the correct attributes - validator, found := keeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.Equal(t, sdk.Bonded, validator.Status) + validator := tstaking.CheckValidator(valAddr, types.Bonded, false) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount, validator.BondedTokens()) // delegate tokens to the validator - msgDelegate := NewTestMsgDelegate(delAddr, valAddr, bondAmount) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(delAddr, valAddr, bondAmount) // verify validator bonded shares - validator, found = keeper.GetValidator(ctx, valAddr) - require.True(t, found) + validator = tstaking.CheckValidator(valAddr, types.Bonded, false) require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens()) // unbond validator total self-delegations (which should jail the validator) - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, bondAmount) - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) + res := tstaking.Undelegate(sdk.AccAddress(valAddr), valAddr, bondAmount, true) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) + var resData types.MsgUndelegateResponse + err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) - require.NotNil(t, res) - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + tstaking.Ctx = ctx + staking.EndBlocker(ctx, app.StakingKeeper) // verify the validator record still exists, is jailed, and has correct tokens - validator, found = keeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.True(t, validator.Jailed) + validator = tstaking.CheckValidator(valAddr, -1, true) require.Equal(t, bondAmount, validator.Tokens) // verify delegation still exists - bond, found := keeper.GetDelegation(ctx, delAddr, valAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) require.Equal(t, bondAmount, bond.Shares.RoundInt()) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) // verify the validator can still self-delegate - msgSelfDelegate := NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) - res, err = handleMsgDelegate(ctx, msgSelfDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(sdk.AccAddress(valAddr), valAddr, bondAmount) // verify validator bonded shares - validator, found = keeper.GetValidator(ctx, valAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(2), validator.Tokens) // unjail the validator now that is has non-zero self-delegated shares - keeper.Unjail(ctx, valConsAddr) + app.StakingKeeper.Unjail(ctx, valConsAddr) // verify the validator can now accept delegations - msgDelegate = NewTestMsgDelegate(delAddr, valAddr, bondAmount) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(delAddr, valAddr, bondAmount) // verify validator bonded shares - validator, found = keeper.GetValidator(ctx, valAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(3), validator.Tokens) // verify new delegation - bond, found = keeper.GetDelegation(ctx, delAddr, valAddr) + bond, found = app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(2), bond.Shares.RoundInt()) require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) @@ -279,51 +298,40 @@ func TestLegacyValidatorDelegations(t *testing.T) { func TestIncrementsMsgDelegate(t *testing.T) { initPower := int64(1000) initBond := sdk.TokensFromConsensusPower(initPower) - ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) - params := keeper.GetParams(ctx) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) - bondAmount := sdk.TokensFromConsensusPower(10) - validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + params := app.StakingKeeper.GetParams(ctx) + validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // first create validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + bondAmount := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found := keeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.Equal(t, sdk.Bonded, validator.Status) + validator := tstaking.CheckValidator(validatorAddr, types.Bonded, false) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount, validator.BondedTokens(), "validator: %v", validator) - _, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) - require.False(t, found) + tstaking.CheckDelegator(delegatorAddr, validatorAddr, false) - bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) require.Equal(t, bondAmount, bond.Shares.RoundInt()) - bondedTokens := keeper.TotalBondedTokens(ctx) + bondedTokens := app.StakingKeeper.TotalBondedTokens(ctx) require.Equal(t, bondAmount, bondedTokens) - // just send the same msgbond multiple times - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount) - for i := int64(0); i < 5; i++ { ctx = ctx.WithBlockHeight(i) - - res, err := handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Ctx = ctx + tstaking.Delegate(delegatorAddr, validatorAddr, bondAmount) //Check that the accounts and the bond account have the appropriate values - validator, found := keeper.GetValidator(ctx, validatorAddr) + validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.True(t, found) expBond := bondAmount.MulRaw(i + 1) @@ -332,7 +340,7 @@ func TestIncrementsMsgDelegate(t *testing.T) { gotBond := bond.Shares.RoundInt() gotDelegatorShares := validator.DelegatorShares.RoundInt() - gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) + gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount require.Equal(t, expBond, gotBond, "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", @@ -347,25 +355,25 @@ func TestIncrementsMsgDelegate(t *testing.T) { } func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) { - validatorAddr := sdk.ValAddress(keep.Addrs[0]) - initPower := int64(100) initBond := sdk.TokensFromConsensusPower(100) - ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower)) + + validatorAddr := valAddrs[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgCreateValidator, true) // must end-block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists - bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond, @@ -373,32 +381,30 @@ func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) { initBond, gotBond, bond) newMinSelfDelegation := sdk.OneInt() - msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation) - res, err = handleMsgEditValidator(ctx, msgEditValidator, keeper) - require.Error(t, err) - require.Nil(t, res) + msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) + tstaking.Handle(msgEditValidator, false) } func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) { - validatorAddr := sdk.ValAddress(keep.Addrs[0]) - initPower := int64(100) initBond := sdk.TokensFromConsensusPower(100) - ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + validatorAddr := valAddrs[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgCreateValidator, true) // must end-block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists - bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond, @@ -406,44 +412,35 @@ func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) { initBond, gotBond, bond) newMinSelfDelegation := initBond.Add(sdk.OneInt()) - msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation) - res, err = handleMsgEditValidator(ctx, msgEditValidator, keeper) - require.Error(t, err) - require.Nil(t, res) + msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) + tstaking.Handle(msgEditValidator, false) } func TestIncrementsMsgUnbond(t *testing.T) { initPower := int64(1000) - initBond := sdk.TokensFromConsensusPower(initPower) - ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) - params := keeper.GetParams(ctx) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + params := app.StakingKeeper.GetParams(ctx) denom := params.BondDenom // create validator, delegate - validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] - - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] + initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) // initial balance - amt1 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) + amt1 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, initBond) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(delegatorAddr, validatorAddr, initBond) // balance should have been subtracted after delegation - amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) + amt2 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount require.True(sdk.IntEq(t, amt1.Sub(initBond), amt2)) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found := keeper.GetValidator(ctx, validatorAddr) + validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, initBond.MulRaw(2), validator.BondedTokens()) @@ -451,24 +448,24 @@ func TestIncrementsMsgUnbond(t *testing.T) { // just send the same msgUnbond multiple times // TODO use decimals here unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + msgUndelegate := types.NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) numUnbonds := int64(5) for i := int64(0); i < numUnbonds; i++ { - res, err := handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + res := tstaking.Handle(msgUndelegate, true) - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) + require.NoError(t, err) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + tstaking.Ctx = ctx + staking.EndBlocker(ctx, app.StakingKeeper) // check that the accounts and the bond account have the appropriate values - validator, found = keeper.GetValidator(ctx, validatorAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.True(t, found) expBond := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) @@ -477,7 +474,7 @@ func TestIncrementsMsgUnbond(t *testing.T) { gotBond := bond.Shares.RoundInt() gotDelegatorShares := validator.DelegatorShares.RoundInt() - gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) + gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount require.Equal(t, expBond, gotBond, "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", @@ -500,861 +497,690 @@ func TestIncrementsMsgUnbond(t *testing.T) { } for _, c := range errorCases { - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, c) - msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.Undelegate(delegatorAddr, validatorAddr, c, false) } - leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds))) - // should be able to unbond remaining - unbondAmt = sdk.NewCoin(sdk.DefaultBondDenom, leftBonded) - msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err, "msgUnbond: %v\nshares: %s\nleftBonded: %s\n", msgUndelegate, unbondAmt, leftBonded) - require.NotNil(t, res, "msgUnbond: %v\nshares: %s\nleftBonded: %s\n", msgUndelegate, unbondAmt, leftBonded) + leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds))) + tstaking.Undelegate(delegatorAddr, validatorAddr, leftBonded, true) } func TestMultipleMsgCreateValidator(t *testing.T) { initPower := int64(1000) initTokens := sdk.TokensFromConsensusPower(initPower) - ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower)) - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) blockTime := time.Now().UTC() ctx = ctx.WithBlockTime(blockTime) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) validatorAddrs := []sdk.ValAddress{ - sdk.ValAddress(keep.Addrs[0]), - sdk.ValAddress(keep.Addrs[1]), - sdk.ValAddress(keep.Addrs[2]), + valAddrs[0], + valAddrs[1], + valAddrs[2], } delegatorAddrs := []sdk.AccAddress{ - keep.Addrs[0], - keep.Addrs[1], - keep.Addrs[2], + delAddrs[0], + delAddrs[1], + delAddrs[2], } // bond them all + amt := sdk.TokensFromConsensusPower(10) for i, validatorAddr := range validatorAddrs { - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidator(validatorAddr, keep.PKs[i], valTokens) - - res, err := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) - require.NoError(t, err) - require.NotNil(t, res) - + tstaking.CreateValidator(validatorAddr, PKs[i], amt, true) // verify that the account is bonded - validators := keeper.GetValidators(ctx, 100) + validators := app.StakingKeeper.GetValidators(ctx, 100) require.Equal(t, (i + 1), len(validators)) val := validators[i] - balanceExpd := initTokens.Sub(valTokens) - balanceGot := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) + balanceExpd := initTokens.Sub(amt) + balanceGot := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) - require.Equal(t, valTokens, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", 10, val.DelegatorShares) + require.Equal(t, amt, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", amt, val.DelegatorShares) require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // unbond them all by removing delegation for i, validatorAddr := range validatorAddrs { - _, found := keeper.GetValidator(ctx, validatorAddr) + _, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)) - msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, unbondAmt) // remove delegation - res, err := handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + res := tstaking.Undelegate(delegatorAddrs[i], validatorAddr, amt, true) - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) + require.NoError(t, err) // adds validator into unbonding queue - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // removes validator from queue and set - EndBlocker(ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)), keeper) + staking.EndBlocker(ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)), app.StakingKeeper) // Check that the validator is deleted from state - validators := keeper.GetValidators(ctx, 100) + validators := app.StakingKeeper.GetValidators(ctx, 100) require.Equal(t, len(validatorAddrs)-(i+1), len(validators), "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) - _, found = keeper.GetValidator(ctx, validatorAddr) + _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.False(t, found) - gotBalance := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) + gotBalance := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount require.Equal(t, initTokens, gotBalance, "expected account to have %d, got %d", initTokens, gotBalance) } } func TestMultipleMsgDelegate(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr, delegatorAddrs := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1:] + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 50, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, delegatorAddrs := valAddrs[0], delAddrs[1:] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + var amount int64 = 10 // first make a validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amount), true) // delegate multiple parties for _, delegatorAddr := range delegatorAddrs { - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) - res, err := handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - // check that the account is bonded - bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) - require.True(t, found) - require.NotNil(t, bond, "expected delegatee bond %d to exist", bond) + tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) + tstaking.CheckDelegator(delegatorAddr, validatorAddr, true) } // unbond them all for _, delegatorAddr := range delegatorAddrs { - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + res := tstaking.Undelegate(delegatorAddr, validatorAddr, sdk.NewInt(amount), true) - res, err := handleMsgUndelegate(ctx, msgUndelegate, keeper) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) - require.NotNil(t, res) - - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.Ctx = ctx // check that the account is unbonded - _, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + _, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.False(t, found) } } func TestJailValidator(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] - - // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + var amt int64 = 10 - // bond a delegator - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + // create the validator and delegate + tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) + tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(amt)) // unbond the validators bond portion - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + unamt := sdk.NewInt(amt) + res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unamt, true) - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) + require.NoError(t, err) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.Ctx = ctx - validator, found := keeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.True(t, validator.Jailed, "%v", validator) + tstaking.CheckValidator(validatorAddr, -1, true) // test that the delegator can still withdraw their bonds - msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + tstaking.Undelegate(delegatorAddr, validatorAddr, unamt, true) - res, err = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper) + err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) - require.NotNil(t, res) - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + ctx = ctx.WithBlockTime(resData.CompletionTime) + staking.EndBlocker(ctx, app.StakingKeeper) + tstaking.Ctx = ctx // verify that the pubkey can now be reused - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) } func TestValidatorQueue(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second - keeper.SetParams(ctx, params) - - // create the validator - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - // bond a delegator - delTokens := sdk.TokensFromConsensusPower(10) - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, delTokens) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + app.StakingKeeper.SetParams(ctx, params) - EndBlocker(ctx, keeper) + // create the validator and make a bond + amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) + tstaking.Delegate(delegatorAddr, validatorAddr, amt) + staking.EndBlocker(ctx, app.StakingKeeper) // unbond the all self-delegation to put validator in unbonding state - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, delTokens) - msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) + require.NoError(t, err) - ctx = ctx.WithBlockTime(finishTime) - EndBlocker(ctx, keeper) + finishTime := resData.CompletionTime + ctx = tstaking.TurnBlock(finishTime) origHeader := ctx.BlockHeader() - validator, found := keeper.GetValidator(ctx, validatorAddr) + validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonding(), "%v", validator) // should still be unbonding at time 6 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) - EndBlocker(ctx, keeper) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) - validator, found = keeper.GetValidator(ctx, validatorAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonding(), "%v", validator) // should be in unbonded state at time 7 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) - EndBlocker(ctx, keeper) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) - validator, found = keeper.GetValidator(ctx, validatorAddr) + validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonded(), "%v", validator) } func TestUnbondingPeriod(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr := sdk.ValAddress(keep.Addrs[0]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower)) + validatorAddr := valAddrs[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // create the validator - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - EndBlocker(ctx, keeper) + amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) + staking.EndBlocker(ctx, app.StakingKeeper) // begin unbonding - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)) - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) origHeader := ctx.BlockHeader() - _, found := keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // cannot complete unbonding at same time - EndBlocker(ctx, keeper) - _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + staking.EndBlocker(ctx, app.StakingKeeper) + _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // cannot complete unbonding at time 6 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) - EndBlocker(ctx, keeper) - _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) + _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // can complete unbonding at time 7 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) - EndBlocker(ctx, keeper) - _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) + _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.False(t, found, "should have unbonded") } func TestUnbondingFromUnbondingValidator(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] - - // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - // bond a delegator - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + // create the validator and delegate + tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) + tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) // unbond the validators bond portion - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + unbondAmt := sdk.NewInt(10) + res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt, true) // change the ctx to Block Time one second before the validator would have unbonded - var finishTime time.Time - types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &finishTime) - ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1)) - - // unbond the delegator from the validator - msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper) + var resData types.MsgUndelegateResponse + err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) - require.NotNil(t, res) - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(keeper.UnbondingTime(ctx))) + ctx = ctx.WithBlockTime(resData.CompletionTime.Add(time.Second * -1)) + + // unbond the delegator from the validator + res = tstaking.Undelegate(delegatorAddr, validatorAddr, unbondAmt, true) - // Run the EndBlocker - EndBlocker(ctx, keeper) + ctx = tstaking.TurnBlockTimeDiff(app.StakingKeeper.UnbondingTime(ctx)) + tstaking.Ctx = ctx // Check to make sure that the unbonding delegation is no longer in state // (meaning it was deleted in the above EndBlocker) - _, found := keeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) require.False(t, found, "should be removed from state") } func TestRedelegationPeriod(t *testing.T) { - ctx, AccMapper, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) - denom := keeper.GetParams(ctx).BondDenom + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + validatorAddr, validatorAddr2 := valAddrs[0], valAddrs[1] + denom := app.StakingKeeper.GetParams(ctx).BondDenom + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second - keeper.SetParams(ctx, params) - - // create the validators - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) - + app.StakingKeeper.SetParams(ctx, params) // initial balance - amt1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) + amt1 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + // create the validators + tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) // balance should have been subtracted after creation - amt2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) + amt2 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount require.Equal(t, amt1.Sub(sdk.NewInt(10)), amt2, "expected coins to be subtracted") - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - bal1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins() + tstaking.CreateValidator(validatorAddr2, PKs[1], sdk.NewInt(10), true) + bal1 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) // begin redelegate redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) + tstaking.Handle(msgBeginRedelegate, true) // origin account should not lose tokens as with a regular delegation - bal2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins() + bal2 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) require.Equal(t, bal1, bal2) origHeader := ctx.BlockHeader() // cannot complete redelegation at same time - EndBlocker(ctx, keeper) - _, found := keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + staking.EndBlocker(ctx, app.StakingKeeper) + _, found := app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.True(t, found, "should not have unbonded") // cannot complete redelegation at time 6 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) - EndBlocker(ctx, keeper) - _, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) + _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.True(t, found, "should not have unbonded") // can complete redelegation at time 7 seconds later - ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) - EndBlocker(ctx, keeper) - _, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) + _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.False(t, found, "should have unbonded") } func TestTransitiveRedelegation(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr := sdk.ValAddress(keep.Addrs[0]) - validatorAddr2 := sdk.ValAddress(keep.Addrs[1]) - validatorAddr3 := sdk.ValAddress(keep.Addrs[2]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower)) + val1, val2, val3 := valAddrs[0], valAddrs[1], valAddrs[2] blockTime := time.Now().UTC() ctx = ctx.WithBlockTime(blockTime) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create the validators - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], sdk.NewInt(10)) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.CreateValidator(val1, PKs[0], sdk.NewInt(10), true) + tstaking.CreateValidator(val2, PKs[1], sdk.NewInt(10), true) + tstaking.CreateValidator(val3, PKs[2], sdk.NewInt(10), true) // begin redelegate redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val1, val2, redAmt) + tstaking.Handle(msgBeginRedelegate, true) // cannot redelegation to next validator while first delegation exists - msgBeginRedelegate = NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr2, validatorAddr3, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.Error(t, err) - require.Nil(t, res) + msgBeginRedelegate = types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val2, val3, redAmt) + tstaking.Handle(msgBeginRedelegate, false) - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) ctx = ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)) + tstaking.Ctx = ctx // complete first redelegation - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // now should be able to redelegate from the second validator to the third - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgBeginRedelegate, true) } func TestMultipleRedelegationAtSameTime(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valAddr := sdk.ValAddress(keep.Addrs[0]) - valAddr2 := sdk.ValAddress(keep.Addrs[1]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + valAddr := valAddrs[0] + valAddr2 := valAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 1 * time.Second - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // create the validators - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) + tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) // end block to bond them - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) + tstaking.Handle(msgBeginRedelegate, true) // there should only be one entry in the redelegation object - rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 1) // start a second redelegation at this same time as the first - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgBeginRedelegate, true) // now there should be two entries - rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 2) // move forward in time, should complete both redelegations - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second)) - EndBlocker(ctx, keeper) - - rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) + rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.False(t, found) } func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valAddr := sdk.ValAddress(keep.Addrs[0]) - valAddr2 := sdk.ValAddress(keep.Addrs[1]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower)) + valAddr := valAddrs[0] + valAddr2 := valAddrs[1] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 10 * time.Second - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // create the validators - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) + tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) // end block to bond them - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) + tstaking.Handle(msgBeginRedelegate, true) // move forward in time and start a second redelegation ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Ctx = ctx + tstaking.Handle(msgBeginRedelegate, true) // now there should be two entries - rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 2) // move forward in time, should complete the first redelegation, but not the second - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - EndBlocker(ctx, keeper) - rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) + rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 1) // move forward in time, should complete the second redelegation - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - EndBlocker(ctx, keeper) - rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) + rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.False(t, found) } func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valAddr := sdk.ValAddress(keep.Addrs[0]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower)) + valAddr := valAddrs[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 1 * time.Second - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) - // create the validator - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + // create the validators + valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) // end block to bond - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // there should only be one entry in the ubd object - ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // start a second ubd at this same time as the first - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // now there should be two entries - ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 2) // move forwaubd in time, should complete both ubds - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second)) - EndBlocker(ctx, keeper) - - ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.False(t, found) } func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valAddr := sdk.ValAddress(keep.Addrs[0]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower)) + valAddr := valAddrs[0] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 10 * time.Second - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // create the validator - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) // end block to bond - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // there should only be one entry in the ubd object - ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // move forwaubd in time and start a second redelegation ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Ctx = ctx + tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // now there should be two entries - ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 2) // move forwaubd in time, should complete the first redelegation, but not the second - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - EndBlocker(ctx, keeper) - ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // move forwaubd in time, should complete the second redelegation - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - EndBlocker(ctx, keeper) - ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.False(t, found) } func TestUnbondingWhenExcessValidators(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - validatorAddr1 := sdk.ValAddress(keep.Addrs[0]) - validatorAddr2 := sdk.ValAddress(keep.Addrs[1]) - validatorAddr3 := sdk.ValAddress(keep.Addrs[2]) + initPower := int64(1000) + app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower)) + val1 := valAddrs[0] + val2 := valAddrs[1] + val3 := valAddrs[2] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.MaxValidators = 2 - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // add three validators - valTokens1 := sdk.TokensFromConsensusPower(50) - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr1, keep.PKs[0], valTokens1) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - + tstaking.CreateValidatorWithValPower(val1, PKs[0], 50, true) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(keeper.GetLastValidators(ctx))) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(app.StakingKeeper.GetLastValidators(ctx))) - valTokens2 := sdk.TokensFromConsensusPower(30) - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], valTokens2) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + valTokens2 := tstaking.CreateValidatorWithValPower(val2, PKs[1], 30, true) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) - // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) - - valTokens3 := sdk.TokensFromConsensusPower(10) - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], valTokens3) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) - - // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) + tstaking.CreateValidatorWithValPower(val3, PKs[2], 10, true) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) // unbond the validator-2 - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens2) - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) - + tstaking.Undelegate(sdk.AccAddress(val2), val2, valTokens2, true) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) // because there are extra validators waiting to get in, the queued // validator (aka. validator-1) should make it into the bonded group, thus // the total number of validators should stay the same - vals := keeper.GetLastValidators(ctx) + vals := app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 2, len(vals), "vals %v", vals) - val1, found := keeper.GetValidator(ctx, validatorAddr1) - require.True(t, found) - require.Equal(t, sdk.Bonded, val1.Status, "%v", val1) + tstaking.CheckValidator(val1, types.Bonded, false) } func TestBondUnbondRedelegateSlashTwice(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] - consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) - - valTokens := sdk.TokensFromConsensusPower(10) - msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], valTokens) - res, err := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower)) + valA, valB, del := valAddrs[0], valAddrs[1], delAddrs[2] + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], valTokens) - res, err = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) - require.NoError(t, err) - require.NotNil(t, res) + valTokens := tstaking.CreateValidatorWithValPower(valA, PKs[0], 10, true) + tstaking.CreateValidator(valB, PKs[1], valTokens, true) // delegate 10 stake - msgDelegate := NewTestMsgDelegate(del, valA, valTokens) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Delegate(del, valA, valTokens) // apply Tendermint updates - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 2, len(updates)) // a block passes ctx = ctx.WithBlockHeight(1) + tstaking.Ctx = ctx // begin unbonding 4 stake - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4)) - msgUndelegate := NewMsgUndelegate(del, valA, unbondAmt) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + unbondAmt := sdk.TokensFromConsensusPower(4) + tstaking.Undelegate(del, valA, unbondAmt, true) // begin redelegate 6 stake redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(6)) - msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, redAmt) - res, err = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + msgBeginRedelegate := types.NewMsgBeginRedelegate(del, valA, valB, redAmt) + tstaking.Handle(msgBeginRedelegate, true) // destination delegation should have 6 shares - delegation, found := keeper.GetDelegation(ctx, del, valB) + delegation, found := app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount), delegation.Shares) // must apply validator updates - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) require.Equal(t, 2, len(updates)) // slash the validator by half - keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should have been slashed by half - ubd, found := keeper.GetUnbondingDelegation(ctx, del, valA) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) - require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance) + require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) // redelegation should have been slashed by half - redelegation, found := keeper.GetRedelegation(ctx, del, valA, valB) + redelegation, found := app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) // destination delegation should have been slashed by half - delegation, found = keeper.GetDelegation(ctx, del, valB) + delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) // validator power should have been reduced by half - validator, found := keeper.GetValidator(ctx, valA) + validator, found := app.StakingKeeper.GetValidator(ctx, valA) require.True(t, found) require.Equal(t, valTokens.QuoRaw(2), validator.GetBondedTokens()) // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) - keeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) + app.StakingKeeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) + tstaking.Ctx = ctx // unbonding delegation should be unchanged - ubd, found = keeper.GetUnbondingDelegation(ctx, del, valA) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) - require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance) + require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) // redelegation should be unchanged - redelegation, found = keeper.GetRedelegation(ctx, del, valA, valB) + redelegation, found = app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) // destination delegation should be unchanged - delegation, found = keeper.GetDelegation(ctx, del, valB) + delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) // end blocker - EndBlocker(ctx, keeper) + staking.EndBlocker(ctx, app.StakingKeeper) // validator power should have been reduced to zero // validator should be in unbonding state - validator, _ = keeper.GetValidator(ctx, valA) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidator(ctx, valA) + require.Equal(t, validator.GetStatus(), types.Unbonding) } func TestInvalidMsg(t *testing.T) { - k := keep.Keeper{} - h := NewHandler(k) + k := keeper.Keeper{} + h := staking.NewHandler(k) - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) require.Error(t, err) require.Nil(t, res) require.True(t, strings.Contains(err.Error(), "unrecognized staking message type")) } func TestInvalidCoinDenom(t *testing.T) { - ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) - valA, valB, delAddr := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] + initPower := int64(1000) + app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower)) + valA, valB, delAddr := valAddrs[0], valAddrs[1], delAddrs[2] + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) valTokens := sdk.TokensFromConsensusPower(100) invalidCoin := sdk.NewCoin("churros", valTokens) @@ -1362,49 +1188,33 @@ func TestInvalidCoinDenom(t *testing.T) { oneCoin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()) commission := types.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.ZeroDec()) + msgCreate, err := types.NewMsgCreateValidator(valA, PKs[0], invalidCoin, types.Description{}, commission, sdk.OneInt()) + require.NoError(t, err) + tstaking.Handle(msgCreate, false) - msgCreate := types.NewMsgCreateValidator(valA, keep.PKs[0], invalidCoin, Description{}, commission, sdk.OneInt()) - res, err := handleMsgCreateValidator(ctx, msgCreate, keeper) - require.Error(t, err) - require.Nil(t, res) - - msgCreate = types.NewMsgCreateValidator(valA, keep.PKs[0], validCoin, Description{}, commission, sdk.OneInt()) - res, err = handleMsgCreateValidator(ctx, msgCreate, keeper) + msgCreate, err = types.NewMsgCreateValidator(valA, PKs[0], validCoin, types.Description{}, commission, sdk.OneInt()) require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgCreate, true) - msgCreate = types.NewMsgCreateValidator(valB, keep.PKs[1], validCoin, Description{}, commission, sdk.OneInt()) - res, err = handleMsgCreateValidator(ctx, msgCreate, keeper) + msgCreate, err = types.NewMsgCreateValidator(valB, PKs[1], validCoin, types.Description{}, commission, sdk.OneInt()) require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgCreate, true) msgDelegate := types.NewMsgDelegate(delAddr, valA, invalidCoin) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.Handle(msgDelegate, false) msgDelegate = types.NewMsgDelegate(delAddr, valA, validCoin) - res, err = handleMsgDelegate(ctx, msgDelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgDelegate, true) msgUndelegate := types.NewMsgUndelegate(delAddr, valA, invalidCoin) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.Handle(msgUndelegate, false) msgUndelegate = types.NewMsgUndelegate(delAddr, valA, oneCoin) - res, err = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgUndelegate, true) msgRedelegate := types.NewMsgBeginRedelegate(delAddr, valA, valB, invalidCoin) - res, err = handleMsgBeginRedelegate(ctx, msgRedelegate, keeper) - require.Error(t, err) - require.Nil(t, res) + tstaking.Handle(msgRedelegate, false) msgRedelegate = types.NewMsgBeginRedelegate(delAddr, valA, valB, oneCoin) - res, err = handleMsgBeginRedelegate(ctx, msgRedelegate, keeper) - require.NoError(t, err) - require.NotNil(t, res) + tstaking.Handle(msgRedelegate, true) } diff --git a/x/staking/keeper/alias_functions.go b/x/staking/keeper/alias_functions.go index ca5696b6c0e9..1d563c690783 100644 --- a/x/staking/keeper/alias_functions.go +++ b/x/staking/keeper/alias_functions.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -12,14 +11,18 @@ import ( // Validator Set // iterate through the validator set and perform the provided function -func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { +func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator types.ValidatorI) (stop bool)) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey) defer iterator.Close() + i := int64(0) + for ; iterator.Valid(); iterator.Next() { validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { break } @@ -28,7 +31,7 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato } // iterate through the bonded validator set and perform the provided function -func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { +func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator types.ValidatorI) (stop bool)) { store := ctx.KVStore(k.storeKey) maxValidators := k.MaxValidators(ctx) @@ -51,12 +54,15 @@ func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index in } // iterate through the active validator set and perform the provided function -func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { +func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator types.ValidatorI) (stop bool)) { iterator := k.LastValidatorsIterator(ctx) defer iterator.Close() + i := int64(0) + for ; iterator.Valid(); iterator.Next() { address := types.AddressFromLastValidatorPowerKey(iterator.Key()) + validator, found := k.GetValidator(ctx, address) if !found { panic(fmt.Sprintf("validator record not found for address: %v\n", address)) @@ -71,20 +77,22 @@ func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, vali } // Validator gets the Validator interface for a particular address -func (k Keeper) Validator(ctx sdk.Context, address sdk.ValAddress) exported.ValidatorI { +func (k Keeper) Validator(ctx sdk.Context, address sdk.ValAddress) types.ValidatorI { val, found := k.GetValidator(ctx, address) if !found { return nil } + return val } // ValidatorByConsAddr gets the validator interface for a particular pubkey -func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) exported.ValidatorI { +func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) types.ValidatorI { val, found := k.GetValidatorByConsAddr(ctx, addr) if !found { return nil } + return val } @@ -97,7 +105,7 @@ func (k Keeper) GetValidatorSet() types.ValidatorSet { } // Delegation get the delegation interface for a particular set of delegator and validator addresses -func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) exported.DelegationI { +func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) types.DelegationI { bond, ok := k.GetDelegation(ctx, addrDel, addrVal) if !ok { return nil @@ -108,14 +116,16 @@ func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk. // iterate through all of the delegations from a delegator func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, - fn func(index int64, del exported.DelegationI) (stop bool)) { - + fn func(index int64, del types.DelegationI) (stop bool)) { store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetDelegationsKey(delAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest defer iterator.Close() + for i := int64(0); iterator.Valid(); iterator.Next() { del := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + stop := fn(i, del) if stop { break @@ -128,6 +138,7 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, // TODO: remove this func, change all usage for iterate functionality func (k Keeper) GetAllSDKDelegations(ctx sdk.Context) (delegations []types.Delegation) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) defer iterator.Close() @@ -135,5 +146,6 @@ func (k Keeper) GetAllSDKDelegations(ctx sdk.Context) (delegations []types.Deleg delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) delegations = append(delegations, delegation) } - return delegations + + return } diff --git a/x/staking/keeper/common_test.go b/x/staking/keeper/common_test.go new file mode 100644 index 000000000000..7ec4e9677cbc --- /dev/null +++ b/x/staking/keeper/common_test.go @@ -0,0 +1,51 @@ +package keeper_test + +import ( + "math/big" + "testing" + + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + PKs = simapp.CreateTestPubKeys(500) +) + +func init() { + sdk.PowerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) +} + +// createTestInput Returns a simapp with custom StakingKeeper +// to avoid messing with the hooks. +func createTestInput() (*codec.LegacyAmino, *simapp.SimApp, sdk.Context) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.StakingKeeper = keeper.NewKeeper( + app.AppCodec(), + app.GetKey(types.StoreKey), + app.AccountKeeper, + app.BankKeeper, + app.GetSubspace(types.ModuleName), + ) + return app.LegacyAmino(), app, ctx +} + +// intended to be used with require/assert: require.True(ValEq(...)) +func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) { + return t, exp.MinEqual(&got), "expected:\n%v\ngot:\n%v", exp, got +} + +// generateAddresses generates numAddrs of normal AccAddrs and ValAddrs +func generateAddresses(app *simapp.SimApp, ctx sdk.Context, numAddrs int) ([]sdk.AccAddress, []sdk.ValAddress) { + addrDels := simapp.AddTestAddrsIncremental(app, ctx, numAddrs, sdk.NewInt(10000)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + + return addrDels, addrVals +} diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 219beef71271..283b308e767b 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -12,23 +12,24 @@ import ( // return a specific delegation func (k Keeper) GetDelegation(ctx sdk.Context, - delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - delegation types.Delegation, found bool) { - + delAddr sdk.AccAddress, valAddr sdk.ValAddress) (delegation types.Delegation, found bool) { store := ctx.KVStore(k.storeKey) key := types.GetDelegationKey(delAddr, valAddr) + value := store.Get(key) if value == nil { return delegation, false } delegation = types.MustUnmarshalDelegation(k.cdc, value) + return delegation, true } // IterateAllDelegations iterate through all of the delegations func (k Keeper) IterateAllDelegations(ctx sdk.Context, cb func(delegation types.Delegation) (stop bool)) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) defer iterator.Close() @@ -46,12 +47,14 @@ func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegati delegations = append(delegations, delegation) return false }) + return delegations } // return all delegations to a specific validator. Useful for querier. func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) { //nolint:interfacer store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) defer iterator.Close() @@ -61,17 +64,17 @@ func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) delegations = append(delegations, delegation) } } + return delegations } // return a given amount of all the delegations from a delegator func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []types.Delegation) { - delegations = make([]types.Delegation, maxRetrieve) - store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) defer iterator.Close() @@ -81,32 +84,42 @@ func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddres delegations[i] = delegation i++ } + return delegations[:i] // trim if the array length < maxRetrieve } // set a delegation func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) + } + store := ctx.KVStore(k.storeKey) b := types.MustMarshalDelegation(k.cdc, delegation) - store.Set(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress), b) + store.Set(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr()), b) } // remove a delegation func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) + } // TODO: Consider calling hooks outside of the store wrapper functions, it's unobvious. - k.BeforeDelegationRemoved(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + k.BeforeDelegationRemoved(ctx, delegatorAddress, delegation.GetValidatorAddr()) store := ctx.KVStore(k.storeKey) - store.Delete(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress)) + store.Delete(types.GetDelegationKey(delegatorAddress, delegation.GetValidatorAddr())) } // return a given amount of all the delegator unbonding-delegations func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (unbondingDelegations []types.UnbondingDelegation) { - unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve) store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) defer iterator.Close() @@ -116,27 +129,31 @@ func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddres unbondingDelegations[i] = unbondingDelegation i++ } + return unbondingDelegations[:i] // trim if the array length < maxRetrieve } // return a unbonding delegation -func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, - delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) { - +func (k Keeper) GetUnbondingDelegation( + ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, +) (ubd types.UnbondingDelegation, found bool) { store := ctx.KVStore(k.storeKey) key := types.GetUBDKey(delAddr, valAddr) value := store.Get(key) + if value == nil { return ubd, false } ubd = types.MustUnmarshalUBD(k.cdc, value) + return ubd, true } // return all unbonding delegations from a particular validator func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.GetUBDsByValIndexKey(valAddr)) defer iterator.Close() @@ -146,12 +163,14 @@ func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sd ubd := types.MustUnmarshalUBD(k.cdc, value) ubds = append(ubds, ubd) } + return ubds } // iterate through all of the unbonding delegations func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.UnbondingDelegationKey) defer iterator.Close() @@ -167,44 +186,63 @@ func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64 // HasMaxUnbondingDelegationEntries - check if unbonding delegation has maximum number of entries func (k Keeper) HasMaxUnbondingDelegationEntries(ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool { - ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) if !found { return false } + return len(ubd.Entries) >= int(k.MaxEntries(ctx)) } // set the unbonding delegation and associated index func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(ubd.DelegatorAddress) + if err != nil { + panic(err) + } store := ctx.KVStore(k.storeKey) bz := types.MustMarshalUBD(k.cdc, ubd) - key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress) + addr, err := sdk.ValAddressFromBech32(ubd.ValidatorAddress) + if err != nil { + panic(err) + } + key := types.GetUBDKey(delegatorAddress, addr) store.Set(key, bz) - store.Set(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress), []byte{}) // index, store empty bytes + store.Set(types.GetUBDByValIndexKey(delegatorAddress, addr), []byte{}) // index, store empty bytes } // remove the unbonding delegation object and associated index func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(ubd.DelegatorAddress) + if err != nil { + panic(err) + } + store := ctx.KVStore(k.storeKey) - key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress) + addr, err := sdk.ValAddressFromBech32(ubd.ValidatorAddress) + if err != nil { + panic(err) + } + key := types.GetUBDKey(delegatorAddress, addr) store.Delete(key) - store.Delete(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress)) + store.Delete(types.GetUBDByValIndexKey(delegatorAddress, addr)) } // SetUnbondingDelegationEntry adds an entry to the unbonding delegation at // the given addresses. It creates the unbonding delegation if it does not exist -func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context, - delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, - creationHeight int64, minTime time.Time, balance sdk.Int) types.UnbondingDelegation { - +func (k Keeper) SetUnbondingDelegationEntry( + ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, + creationHeight int64, minTime time.Time, balance sdk.Int, +) types.UnbondingDelegation { ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) if found { ubd.AddEntry(creationHeight, minTime, balance) } else { ubd = types.NewUnbondingDelegation(delegatorAddr, validatorAddr, creationHeight, minTime, balance) } + k.SetUnbondingDelegation(ctx, ubd) + return ubd } @@ -214,27 +252,31 @@ func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context, // corresponding to unbonding delegations that expire at a certain time. func (k Keeper) GetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) { store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetUnbondingDelegationTimeKey(timestamp)) if bz == nil { return []types.DVPair{} } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs) - return dvPairs + + pairs := types.DVPairs{} + k.cdc.MustUnmarshalBinaryBare(bz, &pairs) + + return pairs.Pairs } // Sets a specific unbonding queue timeslice. func (k Keeper) SetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) + bz := k.cdc.MustMarshalBinaryBare(&types.DVPairs{Pairs: keys}) store.Set(types.GetUnbondingDelegationTimeKey(timestamp), bz) } // Insert an unbonding delegation to the appropriate timeslice in the unbonding queue func (k Keeper) InsertUBDQueue(ctx sdk.Context, ubd types.UnbondingDelegation, completionTime time.Time) { + dvPair := types.DVPair{DelegatorAddress: ubd.DelegatorAddress, ValidatorAddress: ubd.ValidatorAddress} timeSlice := k.GetUBDQueueTimeSlice(ctx, completionTime) - dvPair := types.DVPair{DelegatorAddress: ubd.DelegatorAddress, ValidatorAddress: ubd.ValidatorAddress} if len(timeSlice) == 0 { k.SetUBDQueueTimeSlice(ctx, completionTime, []types.DVPair{dvPair}) } else { @@ -252,19 +294,23 @@ func (k Keeper) UBDQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterato // Returns a concatenated list of all the timeslices inclusively previous to // currTime, and deletes the timeslices from the queue -func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context, - currTime time.Time) (matureUnbonds []types.DVPair) { - +func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context, currTime time.Time) (matureUnbonds []types.DVPair) { store := ctx.KVStore(k.storeKey) + // gets an iterator for all timeslices from time 0 until the current Blockheader time unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time) + defer unbondingTimesliceIterator.Close() + for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() { - timeslice := []types.DVPair{} + timeslice := types.DVPairs{} value := unbondingTimesliceIterator.Value() - k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice) - matureUnbonds = append(matureUnbonds, timeslice...) + k.cdc.MustUnmarshalBinaryBare(value, ×lice) + + matureUnbonds = append(matureUnbonds, timeslice.Pairs...) + store.Delete(unbondingTimesliceIterator.Key()) } + return matureUnbonds } @@ -275,6 +321,7 @@ func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) defer iterator.Close() @@ -284,27 +331,30 @@ func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, redelegations[i] = redelegation i++ } + return redelegations[:i] // trim if the array length < maxRetrieve } // return a redelegation func (k Keeper) GetRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) { - store := ctx.KVStore(k.storeKey) key := types.GetREDKey(delAddr, valSrcAddr, valDstAddr) + value := store.Get(key) if value == nil { return red, false } red = types.MustUnmarshalRED(k.cdc, value) + return red, true } // return all redelegations from a particular validator func (k Keeper) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.GetREDsFromValSrcIndexKey(valAddr)) defer iterator.Close() @@ -314,15 +364,16 @@ func (k Keeper) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.Va red := types.MustUnmarshalRED(k.cdc, value) reds = append(reds, red) } + return reds } // check if validator is receiving a redelegation func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool { - store := ctx.KVStore(k.storeKey) prefix := types.GetREDsByDelToValDstIndexKey(delAddr, valDstAddr) + iterator := sdk.KVStorePrefixIterator(store, prefix) defer iterator.Close() @@ -333,22 +384,35 @@ func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, func (k Keeper) HasMaxRedelegationEntries(ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorSrcAddr, validatorDstAddr sdk.ValAddress) bool { - red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr) if !found { return false } + return len(red.Entries) >= int(k.MaxEntries(ctx)) } // set a redelegation and associated index func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(red.DelegatorAddress) + if err != nil { + panic(err) + } + store := ctx.KVStore(k.storeKey) bz := types.MustMarshalRED(k.cdc, red) - key := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress) + valSrcAddr, err := sdk.ValAddressFromBech32(red.ValidatorSrcAddress) + if err != nil { + panic(err) + } + valDestAddr, err := sdk.ValAddressFromBech32(red.ValidatorDstAddress) + if err != nil { + panic(err) + } + key := types.GetREDKey(delegatorAddress, valSrcAddr, valDestAddr) store.Set(key, bz) - store.Set(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{}) - store.Set(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{}) + store.Set(types.GetREDByValSrcIndexKey(delegatorAddress, valSrcAddr, valDestAddr), []byte{}) + store.Set(types.GetREDByValDstIndexKey(delegatorAddress, valSrcAddr, valDestAddr), []byte{}) } // SetUnbondingDelegationEntry adds an entry to the unbonding delegation at @@ -358,7 +422,6 @@ func (k Keeper) SetRedelegationEntry(ctx sdk.Context, validatorDstAddr sdk.ValAddress, creationHeight int64, minTime time.Time, balance sdk.Int, sharesSrc, sharesDst sdk.Dec) types.Redelegation { - red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr) if found { red.AddEntry(creationHeight, minTime, balance, sharesDst) @@ -366,13 +429,16 @@ func (k Keeper) SetRedelegationEntry(ctx sdk.Context, red = types.NewRedelegation(delegatorAddr, validatorSrcAddr, validatorDstAddr, creationHeight, minTime, balance, sharesDst) } + k.SetRedelegation(ctx, red) + return red } // iterate through all redelegations func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.RedelegationKey) defer iterator.Close() @@ -387,11 +453,23 @@ func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red t // remove a redelegation object and associated index func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { + delegatorAddress, err := sdk.AccAddressFromBech32(red.DelegatorAddress) + if err != nil { + panic(err) + } store := ctx.KVStore(k.storeKey) - redKey := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress) + valSrcAddr, err := sdk.ValAddressFromBech32(red.ValidatorSrcAddress) + if err != nil { + panic(err) + } + valDestAddr, err := sdk.ValAddressFromBech32(red.ValidatorDstAddress) + if err != nil { + panic(err) + } + redKey := types.GetREDKey(delegatorAddress, valSrcAddr, valDestAddr) store.Delete(redKey) - store.Delete(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)) - store.Delete(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)) + store.Delete(types.GetREDByValSrcIndexKey(delegatorAddress, valSrcAddr, valDestAddr)) + store.Delete(types.GetREDByValDstIndexKey(delegatorAddress, valSrcAddr, valDestAddr)) } // redelegation queue timeslice operations @@ -400,25 +478,28 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { // that expire at a certain time. func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet) { store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetRedelegationTimeKey(timestamp)) if bz == nil { return []types.DVVTriplet{} } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets) - return dvvTriplets + + triplets := types.DVVTriplets{} + k.cdc.MustUnmarshalBinaryBare(bz, &triplets) + + return triplets.Triplets } // Sets a specific redelegation queue timeslice. func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) + bz := k.cdc.MustMarshalBinaryBare(&types.DVVTriplets{Triplets: keys}) store.Set(types.GetRedelegationTimeKey(timestamp), bz) } // Insert an redelegation delegation to the appropriate timeslice in the redelegation queue func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation, completionTime time.Time) { - timeSlice := k.GetRedelegationQueueTimeSlice(ctx, completionTime) dvvTriplet := types.DVVTriplet{ DelegatorAddress: red.DelegatorAddress, @@ -443,25 +524,30 @@ func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sd // currTime, and deletes the timeslices from the queue func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet) { store := ctx.KVStore(k.storeKey) + // gets an iterator for all timeslices from time 0 until the current Blockheader time redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time) + defer redelegationTimesliceIterator.Close() + for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { - timeslice := []types.DVVTriplet{} + timeslice := types.DVVTriplets{} value := redelegationTimesliceIterator.Value() - k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice) - matureRedelegations = append(matureRedelegations, timeslice...) + k.cdc.MustUnmarshalBinaryBare(value, ×lice) + + matureRedelegations = append(matureRedelegations, timeslice.Triplets...) + store.Delete(redelegationTimesliceIterator.Key()) } + return matureRedelegations } -// Perform a delegation, set/update everything necessary within the store. +// Delegate performs a delegation, set/update everything necessary within the store. // tokenSrc indicates the bond status of the incoming funds. func (k Keeper) Delegate( - ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int, tokenSrc sdk.BondStatus, + ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int, tokenSrc types.BondStatus, validator types.Validator, subtractAccount bool, ) (newShares sdk.Dec, err error) { - // In some situations, the exchange rate becomes invalid, e.g. if // Validator loses all tokens due to slashing. In this case, // make all future delegations invalid. @@ -470,27 +556,33 @@ func (k Keeper) Delegate( } // Get or create the delegation object - delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddress) + delegation, found := k.GetDelegation(ctx, delAddr, validator.GetOperator()) if !found { - delegation = types.NewDelegation(delAddr, validator.OperatorAddress, sdk.ZeroDec()) + delegation = types.NewDelegation(delAddr, validator.GetOperator(), sdk.ZeroDec()) } // call the appropriate hook if present if found { - k.BeforeDelegationSharesModified(ctx, delAddr, validator.OperatorAddress) + k.BeforeDelegationSharesModified(ctx, delAddr, validator.GetOperator()) } else { - k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress) + k.BeforeDelegationCreated(ctx, delAddr, validator.GetOperator()) + } + + delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + panic(err) } // if subtractAccount is true then we are // performing a delegation and not a redelegation, thus the source tokens are // all non bonded if subtractAccount { - if tokenSrc == sdk.Bonded { + if tokenSrc == types.Bonded { panic("delegation token source cannot be bonded") } var sendName string + switch { case validator.IsBonded(): sendName = types.BondedPoolName @@ -501,22 +593,20 @@ func (k Keeper) Delegate( } coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), bondAmt)) - err := k.supplyKeeper.DelegateCoinsFromAccountToModule(ctx, delegation.DelegatorAddress, sendName, coins) - if err != nil { + if err := k.bankKeeper.DelegateCoinsFromAccountToModule(ctx, delegatorAddress, sendName, coins); err != nil { return sdk.Dec{}, err } } else { - // potentially transfer tokens between pools, if switch { - case tokenSrc == sdk.Bonded && validator.IsBonded(): + case tokenSrc == types.Bonded && validator.IsBonded(): // do nothing - case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && !validator.IsBonded(): + case (tokenSrc == types.Unbonded || tokenSrc == types.Unbonding) && !validator.IsBonded(): // do nothing - case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && validator.IsBonded(): + case (tokenSrc == types.Unbonded || tokenSrc == types.Unbonding) && validator.IsBonded(): // transfer pools k.notBondedTokensToBonded(ctx, bondAmt) - case tokenSrc == sdk.Bonded && !validator.IsBonded(): + case tokenSrc == types.Bonded && !validator.IsBonded(): // transfer pools k.bondedTokensToNotBonded(ctx, bondAmt) default: @@ -524,23 +614,22 @@ func (k Keeper) Delegate( } } - validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt) + _, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt) // Update delegation delegation.Shares = delegation.Shares.Add(newShares) k.SetDelegation(ctx, delegation) // Call the after-modification hook - k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()) return newShares, nil } -// unbond a particular delegation and perform associated store operations -func (k Keeper) unbond( +// Unbond a particular delegation and perform associated store operations. +func (k Keeper) Unbond( ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec, ) (amount sdk.Int, err error) { - // check if a delegation object exists in the store delegation, found := k.GetDelegation(ctx, delAddr, valAddr) if !found { @@ -564,15 +653,19 @@ func (k Keeper) unbond( // subtract shares from delegation delegation.Shares = delegation.Shares.Sub(shares) - isValidatorOperator := delegation.DelegatorAddress.Equals(validator.OperatorAddress) + delegatorAddress, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress) + if err != nil { + return amount, err + } + + isValidatorOperator := delegatorAddress.Equals(validator.GetOperator()) - // if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum - // trigger a jail validator + // If the delegation is the operator of the validator and undelegating will decrease the validator's + // self-delegation below their minimum, we jail the validator. if isValidatorOperator && !validator.Jailed && validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) { - k.jailValidator(ctx, validator) - validator = k.mustGetValidator(ctx, validator.OperatorAddress) + validator = k.mustGetValidator(ctx, validator.GetOperator()) } // remove the delegation @@ -581,7 +674,7 @@ func (k Keeper) unbond( } else { k.SetDelegation(ctx, delegation) // call the after delegation modification hook - k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + k.AfterDelegationModified(ctx, delegatorAddress, delegation.GetValidatorAddr()) } // remove the shares and coins from the validator @@ -590,7 +683,7 @@ func (k Keeper) unbond( if validator.DelegatorShares.IsZero() && validator.IsUnbonded() { // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period - k.RemoveValidator(ctx, validator.OperatorAddress) + k.RemoveValidator(ctx, validator.GetOperator()) } return amount, nil @@ -602,23 +695,22 @@ func (k Keeper) unbond( func (k Keeper) getBeginInfo( ctx sdk.Context, valSrcAddr sdk.ValAddress, ) (completionTime time.Time, height int64, completeNow bool) { - validator, found := k.GetValidator(ctx, valSrcAddr) // TODO: When would the validator not be found? switch { case !found || validator.IsBonded(): - // the longest wait - just unbonding period from now completionTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) height = ctx.BlockHeight() + return completionTime, height, false case validator.IsUnbonded(): return completionTime, height, true case validator.IsUnbonding(): - return validator.UnbondingCompletionTime, validator.UnbondingHeight, false + return validator.UnbondingTime, validator.UnbondingHeight, false default: panic(fmt.Sprintf("unknown validator status: %s", validator.Status)) @@ -633,7 +725,6 @@ func (k Keeper) getBeginInfo( func (k Keeper) Undelegate( ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec, ) (time.Time, error) { - validator, found := k.GetValidator(ctx, valAddr) if !found { return time.Time{}, types.ErrNoDelegatorForAddress @@ -643,7 +734,7 @@ func (k Keeper) Undelegate( return time.Time{}, types.ErrMaxUnbondingDelegationEntries } - returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount) + returnAmount, err := k.Unbond(ctx, delAddr, valAddr, sharesAmount) if err != nil { return time.Time{}, err } @@ -660,10 +751,10 @@ func (k Keeper) Undelegate( return completionTime, nil } -// CompleteUnbondingWithAmount completes the unbonding of all mature entries in -// the retrieved unbonding delegation object and returns the total unbonding -// balance or an error upon failure. -func (k Keeper) CompleteUnbondingWithAmount(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) { +// CompleteUnbonding completes the unbonding of all mature entries in the +// retrieved unbonding delegation object and returns the total unbonding balance +// or an error upon failure. +func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) { ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr) if !found { return nil, types.ErrNoUnbondingDelegation @@ -673,6 +764,11 @@ func (k Keeper) CompleteUnbondingWithAmount(ctx sdk.Context, delAddr sdk.AccAddr balances := sdk.NewCoins() ctxTime := ctx.BlockHeader().Time + delegatorAddress, err := sdk.AccAddressFromBech32(ubd.DelegatorAddress) + if err != nil { + return nil, err + } + // loop through all the entries and complete unbonding mature entries for i := 0; i < len(ubd.Entries); i++ { entry := ubd.Entries[i] @@ -683,10 +779,9 @@ func (k Keeper) CompleteUnbondingWithAmount(ctx sdk.Context, delAddr sdk.AccAddr // track undelegation only when remaining or truncated shares are non-zero if !entry.Balance.IsZero() { amt := sdk.NewCoin(bondDenom, entry.Balance) - err := k.supplyKeeper.UndelegateCoinsFromModuleToAccount( - ctx, types.NotBondedPoolName, ubd.DelegatorAddress, sdk.NewCoins(amt), - ) - if err != nil { + if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount( + ctx, types.NotBondedPoolName, delegatorAddress, sdk.NewCoins(amt), + ); err != nil { return nil, err } @@ -705,18 +800,10 @@ func (k Keeper) CompleteUnbondingWithAmount(ctx sdk.Context, delAddr sdk.AccAddr return balances, nil } -// CompleteUnbonding performs the same logic as CompleteUnbondingWithAmount except -// it does not return the total unbonding amount. -func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error { - _, err := k.CompleteUnbondingWithAmount(ctx, delAddr, valAddr) - return err -} - // begin unbonding / redelegation; create a redelegation record func (k Keeper) BeginRedelegation( ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec, ) (completionTime time.Time, err error) { - if bytes.Equal(valSrcAddr, valDstAddr) { return time.Time{}, types.ErrSelfRedelegation } @@ -740,7 +827,7 @@ func (k Keeper) BeginRedelegation( return time.Time{}, types.ErrMaxRedelegationEntries } - returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount) + returnAmount, err := k.Unbond(ctx, delAddr, valSrcAddr, sharesAmount) if err != nil { return time.Time{}, err } @@ -766,16 +853,16 @@ func (k Keeper) BeginRedelegation( height, completionTime, returnAmount, sharesAmount, sharesCreated, ) k.InsertRedelegationQueue(ctx, red, completionTime) + return completionTime, nil } -// CompleteRedelegationWithAmount completes the redelegations of all mature entries in the +// CompleteRedelegation completes the redelegations of all mature entries in the // retrieved redelegation object and returns the total redelegation (initial) // balance or an error upon failure. -func (k Keeper) CompleteRedelegationWithAmount( +func (k Keeper) CompleteRedelegation( ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, ) (sdk.Coins, error) { - red, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr) if !found { return nil, types.ErrNoRedelegation @@ -808,23 +895,12 @@ func (k Keeper) CompleteRedelegationWithAmount( return balances, nil } -// CompleteRedelegation performs the same logic as CompleteRedelegationWithAmount -// except it does not return the total redelegation amount. -func (k Keeper) CompleteRedelegation( - ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, -) error { - - _, err := k.CompleteRedelegationWithAmount(ctx, delAddr, valSrcAddr, valDstAddr) - return err -} - // ValidateUnbondAmount validates that a given unbond or redelegation amount is // valied based on upon the converted shares. If the amount is valid, the total // amount of respective shares is returned, otherwise an error is returned. func (k Keeper) ValidateUnbondAmount( ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int, ) (shares sdk.Dec, err error) { - validator, found := k.GetValidator(ctx, valAddr) if !found { return shares, types.ErrNoValidatorFound diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index e3e619697679..77f4cb8e2c81 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -1,201 +1,220 @@ -package keeper +package keeper_test import ( "testing" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/types" - "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/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) // tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations func TestDelegation(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 10) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(10000)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) //construct the validators amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)} var validators [3]types.Validator for i, amt := range amts { - validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, valAddrs[i], PKs[i]) validators[i], _ = validators[i].AddTokensFromDel(amt) } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) - validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) // first add a validators[0] to delegate too - - bond1to1 := types.NewDelegation(addrDels[0], addrVals[0], sdk.NewDec(9)) + bond1to1 := types.NewDelegation(addrDels[0], valAddrs[0], sdk.NewDec(9)) // check the empty keeper first - _, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + _, found := app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) require.False(t, found) // set and retrieve a record - keeper.SetDelegation(ctx, bond1to1) - resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.SetDelegation(ctx, bond1to1) + resBond, found := app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) require.True(t, found) - require.True(t, bond1to1.Equal(resBond)) + require.Equal(t, bond1to1, resBond) // modify a records, save, and retrieve bond1to1.Shares = sdk.NewDec(99) - keeper.SetDelegation(ctx, bond1to1) - resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.SetDelegation(ctx, bond1to1) + resBond, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], valAddrs[0]) require.True(t, found) - require.True(t, bond1to1.Equal(resBond)) + require.Equal(t, bond1to1, resBond) // add some more records - bond1to2 := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(9)) - bond1to3 := types.NewDelegation(addrDels[0], addrVals[2], sdk.NewDec(9)) - bond2to1 := types.NewDelegation(addrDels[1], addrVals[0], sdk.NewDec(9)) - bond2to2 := types.NewDelegation(addrDels[1], addrVals[1], sdk.NewDec(9)) - bond2to3 := types.NewDelegation(addrDels[1], addrVals[2], sdk.NewDec(9)) - keeper.SetDelegation(ctx, bond1to2) - keeper.SetDelegation(ctx, bond1to3) - keeper.SetDelegation(ctx, bond2to1) - keeper.SetDelegation(ctx, bond2to2) - keeper.SetDelegation(ctx, bond2to3) + bond1to2 := types.NewDelegation(addrDels[0], valAddrs[1], sdk.NewDec(9)) + bond1to3 := types.NewDelegation(addrDels[0], valAddrs[2], sdk.NewDec(9)) + bond2to1 := types.NewDelegation(addrDels[1], valAddrs[0], sdk.NewDec(9)) + bond2to2 := types.NewDelegation(addrDels[1], valAddrs[1], sdk.NewDec(9)) + bond2to3 := types.NewDelegation(addrDels[1], valAddrs[2], sdk.NewDec(9)) + app.StakingKeeper.SetDelegation(ctx, bond1to2) + app.StakingKeeper.SetDelegation(ctx, bond1to3) + app.StakingKeeper.SetDelegation(ctx, bond2to1) + app.StakingKeeper.SetDelegation(ctx, bond2to2) + app.StakingKeeper.SetDelegation(ctx, bond2to3) // test all bond retrieve capabilities - resBonds := keeper.GetDelegatorDelegations(ctx, addrDels[0], 5) + resBonds := app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) - require.True(t, bond1to1.Equal(resBonds[0])) - require.True(t, bond1to2.Equal(resBonds[1])) - require.True(t, bond1to3.Equal(resBonds[2])) - resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[0]) + require.Equal(t, bond1to1, resBonds[0]) + require.Equal(t, bond1to2, resBonds[1]) + require.Equal(t, bond1to3, resBonds[2]) + resBonds = app.StakingKeeper.GetAllDelegatorDelegations(ctx, addrDels[0]) require.Equal(t, 3, len(resBonds)) - resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 2) + resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) - resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) - require.True(t, bond2to1.Equal(resBonds[0])) - require.True(t, bond2to2.Equal(resBonds[1])) - require.True(t, bond2to3.Equal(resBonds[2])) - allBonds := keeper.GetAllDelegations(ctx) + require.Equal(t, bond2to1, resBonds[0]) + require.Equal(t, bond2to2, resBonds[1]) + require.Equal(t, bond2to3, resBonds[2]) + allBonds := app.StakingKeeper.GetAllDelegations(ctx) require.Equal(t, 6, len(allBonds)) - require.True(t, bond1to1.Equal(allBonds[0])) - require.True(t, bond1to2.Equal(allBonds[1])) - require.True(t, bond1to3.Equal(allBonds[2])) - require.True(t, bond2to1.Equal(allBonds[3])) - require.True(t, bond2to2.Equal(allBonds[4])) - require.True(t, bond2to3.Equal(allBonds[5])) - - resVals := keeper.GetDelegatorValidators(ctx, addrDels[0], 3) + require.Equal(t, bond1to1, allBonds[0]) + require.Equal(t, bond1to2, allBonds[1]) + require.Equal(t, bond1to3, allBonds[2]) + require.Equal(t, bond2to1, allBonds[3]) + require.Equal(t, bond2to2, allBonds[4]) + require.Equal(t, bond2to3, allBonds[5]) + + resVals := app.StakingKeeper.GetDelegatorValidators(ctx, addrDels[0], 3) require.Equal(t, 3, len(resVals)) - resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4) + resVals = app.StakingKeeper.GetDelegatorValidators(ctx, addrDels[1], 4) require.Equal(t, 3, len(resVals)) for i := 0; i < 3; i++ { - - resVal, err := keeper.GetDelegatorValidator(ctx, addrDels[0], addrVals[i]) + resVal, err := app.StakingKeeper.GetDelegatorValidator(ctx, addrDels[0], valAddrs[i]) require.Nil(t, err) - require.Equal(t, addrVals[i], resVal.GetOperator()) + require.Equal(t, valAddrs[i], resVal.GetOperator()) - resVal, err = keeper.GetDelegatorValidator(ctx, addrDels[1], addrVals[i]) + resVal, err = app.StakingKeeper.GetDelegatorValidator(ctx, addrDels[1], valAddrs[i]) require.Nil(t, err) - require.Equal(t, addrVals[i], resVal.GetOperator()) + require.Equal(t, valAddrs[i], resVal.GetOperator()) - resDels := keeper.GetValidatorDelegations(ctx, addrVals[i]) + resDels := app.StakingKeeper.GetValidatorDelegations(ctx, valAddrs[i]) require.Len(t, resDels, 2) } // delete a record - keeper.RemoveDelegation(ctx, bond2to3) - _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2]) + app.StakingKeeper.RemoveDelegation(ctx, bond2to3) + _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[2]) require.False(t, found) - resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 2, len(resBonds)) - require.True(t, bond2to1.Equal(resBonds[0])) - require.True(t, bond2to2.Equal(resBonds[1])) + require.Equal(t, bond2to1, resBonds[0]) + require.Equal(t, bond2to2, resBonds[1]) - resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[1]) + resBonds = app.StakingKeeper.GetAllDelegatorDelegations(ctx, addrDels[1]) require.Equal(t, 2, len(resBonds)) // delete all the records from delegator 2 - keeper.RemoveDelegation(ctx, bond2to1) - keeper.RemoveDelegation(ctx, bond2to2) - _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0]) + app.StakingKeeper.RemoveDelegation(ctx, bond2to1) + app.StakingKeeper.RemoveDelegation(ctx, bond2to2) + _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[0]) require.False(t, found) - _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1]) + _, found = app.StakingKeeper.GetDelegation(ctx, addrDels[1], valAddrs[1]) require.False(t, found) - resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + resBonds = app.StakingKeeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 0, len(resBonds)) } // tests Get/Set/Remove UnbondingDelegation func TestUnbondingDelegation(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() - ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, - time.Unix(0, 0), sdk.NewInt(5)) + delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000)) + valAddrs := simapp.ConvertAddrsToValAddrs(delAddrs) + + ubd := types.NewUnbondingDelegation( + delAddrs[0], + valAddrs[0], + 0, + time.Unix(0, 0).UTC(), + sdk.NewInt(5), + ) // set and retrieve a record - keeper.SetUnbondingDelegation(ctx, ubd) - resUnbond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + resUnbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) require.True(t, found) - require.True(t, ubd.Equal(resUnbond)) + require.Equal(t, ubd, resUnbond) // modify a records, save, and retrieve ubd.Entries[0].Balance = sdk.NewInt(21) - keeper.SetUnbondingDelegation(ctx, ubd) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) - resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + resUnbonds := app.StakingKeeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) require.Equal(t, 1, len(resUnbonds)) - resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + resUnbonds = app.StakingKeeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) require.Equal(t, 1, len(resUnbonds)) - resUnbond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + resUnbond, found = app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) require.True(t, found) - require.True(t, ubd.Equal(resUnbond)) + require.Equal(t, ubd, resUnbond) // delete a record - keeper.RemoveUnbondingDelegation(ctx, ubd) - _, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.RemoveUnbondingDelegation(ctx, ubd) + _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, delAddrs[0], valAddrs[0]) require.False(t, found) - resUnbonds = keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + resUnbonds = app.StakingKeeper.GetUnbondingDelegations(ctx, delAddrs[0], 5) require.Equal(t, 0, len(resUnbonds)) - resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + resUnbonds = app.StakingKeeper.GetAllUnbondingDelegations(ctx, delAddrs[0]) require.Equal(t, 0, len(resUnbonds)) - } func TestUnbondDelegation(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) + valAddrs := simapp.ConvertAddrsToValAddrs(delAddrs) startTokens := sdk.TokensFromConsensusPower(10) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))) - require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + require.NoError(t, + app.BankKeeper.SetBalances( + 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 // note this validator starts not-bonded - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, valAddrs[0], PKs[0]) validator, issuedShares := validator.AddTokensFromDel(startTokens) require.Equal(t, startTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + delegation := types.NewDelegation(delAddrs[0], valAddrs[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) bondTokens := sdk.TokensFromConsensusPower(6) - amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], bondTokens.ToDec()) + amount, err := app.StakingKeeper.Unbond(ctx, delAddrs[0], valAddrs[0], bondTokens.ToDec()) require.NoError(t, err) require.Equal(t, bondTokens, amount) // shares to be added to an unbonding delegation - delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + delegation, found := app.StakingKeeper.GetDelegation(ctx, delAddrs[0], valAddrs[0]) require.True(t, found) - validator, found = keeper.GetValidator(ctx, addrVals[0]) + validator, found = app.StakingKeeper.GetValidator(ctx, valAddrs[0]) require.True(t, found) remainingTokens := startTokens.Sub(bondTokens) @@ -204,192 +223,207 @@ func TestUnbondDelegation(t *testing.T) { } func TestUnbondingDelegationsMaxEntries(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + startTokens := sdk.TokensFromConsensusPower(10) - bondDenom := keeper.BondDenom(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens))) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator and a delegator to that validator - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) validator, issuedShares := validator.AddTokensFromDel(startTokens) require.Equal(t, startTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(sdk.IntEq(t, startTokens, validator.BondedTokens())) require.True(t, validator.IsBonded()) delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + app.StakingKeeper.SetDelegation(ctx, delegation) - maxEntries := keeper.MaxEntries(ctx) + maxEntries := app.StakingKeeper.MaxEntries(ctx) - oldBonded := keeper.GetBondedPool(ctx).GetCoins().AmountOf(bondDenom) - oldNotBonded := keeper.GetNotBondedPool(ctx).GetCoins().AmountOf(bondDenom) + oldBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount // should all pass var completionTime time.Time - for i := uint16(0); i < maxEntries; i++ { + for i := uint32(0); i < maxEntries; i++ { var err error - completionTime, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + completionTime, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) require.NoError(t, err) } - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(int64(maxEntries)))) - require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(int64(maxEntries)))) + newBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, newBonded, oldBonded.SubRaw(int64(maxEntries)))) + require.True(sdk.IntEq(t, newNotBonded, oldNotBonded.AddRaw(int64(maxEntries)))) - oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) - oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom) + oldBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount // an additional unbond should fail due to max entries - _, err = keeper.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) - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded)) - require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded)) + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + + require.True(sdk.IntEq(t, newBonded, oldBonded)) + require.True(sdk.IntEq(t, newNotBonded, oldNotBonded)) // mature unbonding delegations ctx = ctx.WithBlockTime(completionTime) - err = keeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) + _, err = app.StakingKeeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) require.NoError(t, err) - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded)) - require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.SubRaw(int64(maxEntries)))) + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, newBonded, oldBonded)) + require.True(sdk.IntEq(t, newNotBonded, oldNotBonded.SubRaw(int64(maxEntries)))) - oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom) + oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount // unbonding should work again - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) require.NoError(t, err) - bondedPool = keeper.GetBondedPool(ctx) - - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(1))) - require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(1))) + newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount + newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, newBonded, oldBonded.SubRaw(1))) + require.True(sdk.IntEq(t, newNotBonded, oldNotBonded.AddRaw(1))) } -// test undelegating self delegation from a validator pushing it below MinSelfDelegation -// shift it from the bonded to unbonding state and jailed +//// test undelegating self delegation from a validator pushing it below MinSelfDelegation +//// shift it from the bonded to unbonding state and jailed func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { + _, app, ctx := createTestInput() - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) delTokens := sdk.TokensFromConsensusPower(10) - delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) validator.MinSelfDelegation = delTokens validator, issuedShares := validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) require.True(t, validator.IsBonded()) selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // add bonded tokens to pool for delegations - bondedPool := keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.True(t, validator.IsBonded()) require.Equal(t, delTokens, issuedShares.RoundInt()) // add bonded tokens to pool for delegations - bondedPool = keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + app.StakingKeeper.SetDelegation(ctx, delegation) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err = keeper.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 - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, sdk.TokensFromConsensusPower(14), validator.Tokens) - require.Equal(t, sdk.Unbonding, validator.Status) + require.Equal(t, types.Unbonding, validator.Status) require.True(t, validator.Jailed) } func TestUndelegateFromUnbondingValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() delTokens := sdk.TokensFromConsensusPower(10) - delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) validator, issuedShares := validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) - selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + selfDelegation := types.NewDelegation(addrVals[0].Bytes(), addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) - bondedPool := keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - bondedPool = keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - validator = TestingUpdateValidator(keeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) - bondedPool = keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + oldBonded = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), oldBonded.Add(delCoins...)) require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) header := ctx.BlockHeader() blockHeight := int64(10) @@ -399,19 +433,18 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + val0AccAddr := sdk.AccAddress(addrVals[0]) + _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) - params := keeper.GetParams(ctx) - require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + params := app.StakingKeeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) blockHeight2 := int64(20) blockTime2 := time.Unix(444, 0).UTC() @@ -419,11 +452,11 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockTime(blockTime2) // unbond some of the other delegation's shares - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], sdk.NewDec(6)) require.NoError(t, err) // retrieve the unbonding delegation - ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[1], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) require.True(t, ubd.Entries[0].Balance.Equal(sdk.NewInt(6))) @@ -432,407 +465,444 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { } func TestUndelegateFromUnbondedValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1) + _, app, ctx := createTestInput() delTokens := sdk.TokensFromConsensusPower(10) - delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) - val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + val0AccAddr := sdk.AccAddress(addrVals[0]) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) - bondedPool := keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) require.NoError(t, err) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) - params := keeper.GetParams(ctx) - require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + params := app.StakingKeeper.GetParams(ctx) + require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) // unbond the validator - ctx = ctx.WithBlockTime(validator.UnbondingCompletionTime) - keeper.UnbondAllMatureValidatorQueue(ctx) + ctx = ctx.WithBlockTime(validator.UnbondingTime) + app.StakingKeeper.UnbondAllMatureValidators(ctx) // Make sure validator is still in state because there is still an outstanding delegation - validator, found = keeper.GetValidator(ctx, addrVals[0]) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) - require.Equal(t, validator.Status, sdk.Unbonded) + require.Equal(t, validator.Status, types.Unbonded) // unbond some of the other delegation's shares unbondTokens := sdk.TokensFromConsensusPower(6) - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], unbondTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], unbondTokens.ToDec()) require.NoError(t, err) // unbond rest of the other delegation's shares remainingTokens := delTokens.Sub(unbondTokens) - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], remainingTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], remainingTokens.ToDec()) require.NoError(t, err) // now validator should now be deleted from state - validator, found = keeper.GetValidator(ctx, addrVals[0]) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.False(t, found, "%v", validator) } func TestUnbondingAllDelegationFromValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() delTokens := sdk.TokensFromConsensusPower(10) - delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + delCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), delTokens)) + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - bondedPool := keeper.GetBondedPool(ctx) - err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) require.NoError(t, err) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) // unbond all the remaining delegation - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], delTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], delTokens.ToDec()) require.NoError(t, err) // validator should still be in state and still be in unbonding state - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) - require.Equal(t, validator.Status, sdk.Unbonding) + require.Equal(t, validator.Status, types.Unbonding) // unbond the validator - ctx = ctx.WithBlockTime(validator.UnbondingCompletionTime) - keeper.UnbondAllMatureValidatorQueue(ctx) + ctx = ctx.WithBlockTime(validator.UnbondingTime) + app.StakingKeeper.UnbondAllMatureValidators(ctx) // validator should now be deleted from state - _, found = keeper.GetValidator(ctx, addrVals[0]) + _, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.False(t, found) } // Make sure that that the retrieving the delegations doesn't affect the state func TestGetRedelegationsFromSrcValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, time.Unix(0, 0), sdk.NewInt(5), sdk.NewDec(5)) // set and retrieve a record - keeper.SetRedelegation(ctx, rd) - resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + app.StakingKeeper.SetRedelegation(ctx, rd) + resBond, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) // get the redelegations one time - redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + redelegations := app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.Equal(t, redelegations[0], resBond) // get the redelegations a second time, should be exactly the same - redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + redelegations = app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.Equal(t, redelegations[0], resBond) } // tests Get/Set/Remove/Has UnbondingDelegation func TestRedelegation(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, - time.Unix(0, 0), sdk.NewInt(5), + time.Unix(0, 0).UTC(), sdk.NewInt(5), sdk.NewDec(5)) // test shouldn't have and redelegations - has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + has := app.StakingKeeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) require.False(t, has) // set and retrieve a record - keeper.SetRedelegation(ctx, rd) - resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + app.StakingKeeper.SetRedelegation(ctx, rd) + resRed, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) - redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + redelegations := app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resRed)) + require.Equal(t, redelegations[0], resRed) - redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resRed)) + require.Equal(t, redelegations[0], resRed) - redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + redelegations = app.StakingKeeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resRed)) + require.Equal(t, redelegations[0], resRed) // check if has the redelegation - has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + has = app.StakingKeeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) require.True(t, has) // modify a records, save, and retrieve rd.Entries[0].SharesDst = sdk.NewDec(21) - keeper.SetRedelegation(ctx, rd) + app.StakingKeeper.SetRedelegation(ctx, rd) - resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + resRed, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) - require.True(t, rd.Equal(resRed)) + require.Equal(t, rd, resRed) - redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + redelegations = app.StakingKeeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resRed)) + require.Equal(t, redelegations[0], resRed) - redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resRed)) + require.Equal(t, redelegations[0], resRed) // delete a record - keeper.RemoveRedelegation(ctx, rd) - _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + app.StakingKeeper.RemoveRedelegation(ctx, rd) + _, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.False(t, found) - redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + redelegations = app.StakingKeeper.GetRedelegations(ctx, addrDels[0], 5) require.Equal(t, 0, len(redelegations)) - redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + redelegations = app.StakingKeeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) require.Equal(t, 0, len(redelegations)) } func TestRedelegateToSameValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + valTokens := sdk.TokensFromConsensusPower(10) - startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), valTokens)) + startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens)) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.True(t, validator.IsBonded()) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) - _, err = keeper.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) - } func TestRedelegationMaxEntries(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + startTokens := sdk.TokensFromConsensusPower(20) - startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // create a second validator - validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) - require.Equal(t, sdk.Bonded, validator2.Status) + validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) + require.Equal(t, types.Bonded, validator2.Status) - maxEntries := keeper.MaxEntries(ctx) + maxEntries := app.StakingKeeper.MaxEntries(ctx) // redelegations should pass var completionTime time.Time - for i := uint16(0); i < maxEntries; i++ { + for i := uint32(0); i < maxEntries; i++ { var err error - completionTime, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + completionTime, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) require.NoError(t, err) } // an additional redelegation should fail due to max entries - _, err = keeper.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 ctx = ctx.WithBlockTime(completionTime) - err = keeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) + _, err = app.StakingKeeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) require.NoError(t, err) // redelegation should work again - _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + _, err = app.StakingKeeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) require.NoError(t, err) } func TestRedelegateSelfDelegation(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + startTokens := sdk.TokensFromConsensusPower(30) - startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) - val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + val0AccAddr := sdk.AccAddress(addrVals[0]) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // create a second validator - validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) - require.Equal(t, sdk.Bonded, validator2.Status) + validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) + require.Equal(t, types.Bonded, validator2.Status) // create a second delegation to validator 1 delTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + app.StakingKeeper.SetDelegation(ctx, delegation) - _, err = keeper.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 - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, valTokens, validator.Tokens) - require.Equal(t, sdk.Unbonding, validator.Status) + require.Equal(t, types.Unbonding, validator.Status) } func TestRedelegateFromUnbondingValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + startTokens := sdk.TokensFromConsensusPower(30) - startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) delTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) // create a second validator - validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) header := ctx.BlockHeader() blockHeight := int64(10) @@ -842,18 +912,17 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, blockHeight, validator.UnbondingHeight) - params := keeper.GetParams(ctx) - require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + params := app.StakingKeeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) //change the context header = ctx.BlockHeader() @@ -865,11 +934,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { // unbond some of the other delegation's shares redelegateTokens := sdk.TokensFromConsensusPower(6) - _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], redelegateTokens.ToDec()) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], redelegateTokens.ToDec()) require.NoError(t, err) // retrieve the unbonding delegation - ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + ubd, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, ubd.Entries, 1) assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight) @@ -877,69 +946,74 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { } func TestRedelegateFromUnbondedValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + startTokens := sdk.TokensFromConsensusPower(30) - startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + startCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens)) // add bonded tokens to pool for delegations - notBondedPool := keeper.GetNotBondedPool(ctx) - err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins...)) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) //create a validator with a self-delegation - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) valTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares := validator.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) - keeper.SetDelegation(ctx, selfDelegation) + app.StakingKeeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) delTokens := sdk.TokensFromConsensusPower(10) validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) - validator = TestingUpdateValidator(keeper, ctx, validator, true) - delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) - keeper.SetDelegation(ctx, delegation) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[1], addrVals[0], issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) // create a second validator - validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) validator2, issuedShares = validator2.AddTokensFromDel(valTokens) require.Equal(t, valTokens, issuedShares.RoundInt()) - validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) - require.Equal(t, sdk.Bonded, validator2.Status) + validator2 = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator2, true) + require.Equal(t, types.Bonded, validator2.Status) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) require.NoError(t, err) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) - params := keeper.GetParams(ctx) - require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + params := app.StakingKeeper.GetParams(ctx) + require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingTime)) // unbond the validator - keeper.unbondingToUnbonded(ctx, validator) + app.StakingKeeper.UnbondingToUnbonded(ctx, validator) // redelegate some of the delegation's shares redelegationTokens := sdk.TokensFromConsensusPower(6) - _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], redelegationTokens.ToDec()) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrDels[1], addrVals[0], addrVals[1], redelegationTokens.ToDec()) require.NoError(t, err) // no red should have been found - red, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + red, found := app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.False(t, found, "%v", red) } diff --git a/x/staking/keeper/grpc_query.go b/x/staking/keeper/grpc_query.go new file mode 100644 index 000000000000..49d9b92ce26d --- /dev/null +++ b/x/staking/keeper/grpc_query.go @@ -0,0 +1,544 @@ +package keeper + +import ( + "context" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "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/staking/types" +) + +// Querier is used as Keeper will have duplicate methods if used directly, and gRPC names take precedence over keeper +type Querier struct { + Keeper +} + +var _ types.QueryServer = Querier{} + +// Validators queries all validators that match the given status +func (k Querier) Validators(c context.Context, req *types.QueryValidatorsRequest) (*types.QueryValidatorsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + // validate the provided status, return all the validators if the status is empty + if req.Status != "" && !(req.Status == types.Bonded.String() || req.Status == types.Unbonded.String() || req.Status == types.Unbonding.String()) { + return nil, status.Errorf(codes.InvalidArgument, "invalid validator status %s", req.Status) + } + + var validators types.Validators + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + valStore := prefix.NewStore(store, types.ValidatorsKey) + + pageRes, err := query.FilteredPaginate(valStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + val, err := types.UnmarshalValidator(k.cdc, value) + if err != nil { + return false, err + } + + if req.Status != "" && !strings.EqualFold(val.GetStatus().String(), req.Status) { + return false, nil + } + + if accumulate { + validators = append(validators, val) + } + + return true, nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryValidatorsResponse{Validators: validators, Pagination: pageRes}, nil +} + +// Validator queries validator info for given validator addr +func (k Querier) Validator(c context.Context, req *types.QueryValidatorRequest) (*types.QueryValidatorResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(c) + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return nil, status.Errorf(codes.NotFound, "validator %s not found", req.ValidatorAddr) + } + + return &types.QueryValidatorResponse{Validator: validator}, nil +} + +// ValidatorDelegations queries delegate info for given validator +func (k Querier) ValidatorDelegations(c context.Context, req *types.QueryValidatorDelegationsRequest) (*types.QueryValidatorDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + var delegations []types.Delegation + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + valStore := prefix.NewStore(store, types.DelegationKey) + pageRes, err := query.FilteredPaginate(valStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { + delegation, err := types.UnmarshalDelegation(k.cdc, value) + if err != nil { + return false, err + } + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return false, err + } + + if !delegation.GetValidatorAddr().Equals(valAddr) { + return false, nil + } + + if accumulate { + delegations = append(delegations, delegation) + } + return true, nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + delResponses, err := DelegationsToDelegationResponses(ctx, k.Keeper, delegations) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryValidatorDelegationsResponse{ + DelegationResponses: delResponses, Pagination: pageRes}, nil +} + +// ValidatorUnbondingDelegations queries unbonding delegations of a validator +func (k Querier) ValidatorUnbondingDelegations(c context.Context, req *types.QueryValidatorUnbondingDelegationsRequest) (*types.QueryValidatorUnbondingDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + var ubds types.UnbondingDelegations + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, err + } + + srcValPrefix := types.GetUBDsByValIndexKey(valAddr) + ubdStore := prefix.NewStore(store, srcValPrefix) + pageRes, err := query.Paginate(ubdStore, req.Pagination, func(key []byte, value []byte) error { + storeKey := types.GetUBDKeyFromValIndexKey(append(srcValPrefix, key...)) + storeValue := store.Get(storeKey) + + ubd, err := types.UnmarshalUBD(k.cdc, storeValue) + if err != nil { + return err + } + ubds = append(ubds, ubd) + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryValidatorUnbondingDelegationsResponse{ + UnbondingResponses: ubds, + Pagination: pageRes, + }, nil +} + +// Delegation queries delegate info for given validator delegator pair +func (k Querier) Delegation(c context.Context, req *types.QueryDelegationRequest) (*types.QueryDelegationResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "delegator address cannot be empty") + } + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + + ctx := sdk.UnwrapSDKContext(c) + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, err + } + + delegation, found := k.GetDelegation(ctx, delAddr, valAddr) + if !found { + return nil, status.Errorf( + codes.NotFound, + "delegation with delegator %s not found for validator %s", + req.DelegatorAddr, req.ValidatorAddr) + } + + delResponse, err := DelegationToDelegationResponse(ctx, k.Keeper, delegation) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDelegationResponse{DelegationResponse: &delResponse}, nil +} + +// UnbondingDelegation queries unbonding info for give validator delegator pair +func (k Querier) UnbondingDelegation(c context.Context, req *types.QueryUnbondingDelegationRequest) (*types.QueryUnbondingDelegationResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Errorf(codes.InvalidArgument, "delegator address cannot be empty") + } + if req.ValidatorAddr == "" { + return nil, status.Errorf(codes.InvalidArgument, "validator address cannot be empty") + } + + ctx := sdk.UnwrapSDKContext(c) + + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, err + } + + unbond, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr) + if !found { + return nil, status.Errorf( + codes.NotFound, + "unbonding delegation with delegator %s not found for validator %s", + req.DelegatorAddr, req.ValidatorAddr) + } + + return &types.QueryUnbondingDelegationResponse{Unbond: unbond}, nil +} + +// DelegatorDelegations queries all delegations of a give delegator address +func (k Querier) DelegatorDelegations(c context.Context, req *types.QueryDelegatorDelegationsRequest) (*types.QueryDelegatorDelegationsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "delegator address cannot be empty") + } + var delegations types.Delegations + ctx := sdk.UnwrapSDKContext(c) + + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + store := ctx.KVStore(k.storeKey) + delStore := prefix.NewStore(store, types.GetDelegationsKey(delAddr)) + pageRes, err := query.Paginate(delStore, req.Pagination, func(key []byte, value []byte) error { + delegation, err := types.UnmarshalDelegation(k.cdc, value) + if err != nil { + return err + } + delegations = append(delegations, delegation) + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + if delegations == nil { + return nil, status.Errorf( + codes.NotFound, + "unable to find delegations for address %s", req.DelegatorAddr) + } + delegationResps, err := DelegationsToDelegationResponses(ctx, k.Keeper, delegations) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDelegatorDelegationsResponse{DelegationResponses: delegationResps, Pagination: pageRes}, nil + +} + +// DelegatorValidator queries validator info for given delegator validator pair +func (k Querier) DelegatorValidator(c context.Context, req *types.QueryDelegatorValidatorRequest) (*types.QueryDelegatorValidatorResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "delegator address cannot be empty") + } + if req.ValidatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") + } + + ctx := sdk.UnwrapSDKContext(c) + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(req.ValidatorAddr) + if err != nil { + return nil, err + } + + validator, err := k.GetDelegatorValidator(ctx, delAddr, valAddr) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDelegatorValidatorResponse{Validator: validator}, nil +} + +// DelegatorUnbondingDelegations queries all unbonding delegations of a given delegator address +func (k Querier) DelegatorUnbondingDelegations(c context.Context, req *types.QueryDelegatorUnbondingDelegationsRequest) (*types.QueryDelegatorUnbondingDelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "delegator address cannot be empty") + } + var unbondingDelegations types.UnbondingDelegations + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + unbStore := prefix.NewStore(store, types.GetUBDsKey(delAddr)) + pageRes, err := query.Paginate(unbStore, req.Pagination, func(key []byte, value []byte) error { + unbond, err := types.UnmarshalUBD(k.cdc, value) + if err != nil { + return err + } + unbondingDelegations = append(unbondingDelegations, unbond) + return nil + }) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDelegatorUnbondingDelegationsResponse{ + UnbondingResponses: unbondingDelegations, Pagination: pageRes}, nil +} + +// HistoricalInfo queries the historical info for given height +func (k Querier) HistoricalInfo(c context.Context, req *types.QueryHistoricalInfoRequest) (*types.QueryHistoricalInfoResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.Height < 0 { + return nil, status.Error(codes.InvalidArgument, "height cannot be negative") + } + ctx := sdk.UnwrapSDKContext(c) + hi, found := k.GetHistoricalInfo(ctx, req.Height) + if !found { + return nil, status.Errorf(codes.NotFound, "historical info for height %d not found", req.Height) + } + + return &types.QueryHistoricalInfoResponse{Hist: &hi}, nil +} + +func (k Querier) Redelegations(c context.Context, req *types.QueryRedelegationsRequest) (*types.QueryRedelegationsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + var redels types.Redelegations + var pageRes *query.PageResponse + var err error + + ctx := sdk.UnwrapSDKContext(c) + store := ctx.KVStore(k.storeKey) + switch { + case req.DelegatorAddr != "" && req.SrcValidatorAddr != "" && req.DstValidatorAddr != "": + redels, err = queryRedelegation(ctx, k, req) + case req.DelegatorAddr == "" && req.SrcValidatorAddr != "" && req.DstValidatorAddr == "": + redels, pageRes, err = queryRedelegationsFromSrcValidator(store, k, req) + default: + redels, pageRes, err = queryAllRedelegations(store, k, req) + } + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + redelResponses, err := RedelegationsToRedelegationResponses(ctx, k.Keeper, redels) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryRedelegationsResponse{RedelegationResponses: redelResponses, Pagination: pageRes}, nil +} + +func (k Querier) DelegatorValidators(c context.Context, req *types.QueryDelegatorValidatorsRequest) (*types.QueryDelegatorValidatorsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + if req.DelegatorAddr == "" { + return nil, status.Error(codes.InvalidArgument, "delegator address cannot be empty") + } + var validators types.Validators + ctx := sdk.UnwrapSDKContext(c) + + store := ctx.KVStore(k.storeKey) + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + delStore := prefix.NewStore(store, types.GetDelegationsKey(delAddr)) + pageRes, err := query.Paginate(delStore, req.Pagination, func(key []byte, value []byte) error { + delegation, err := types.UnmarshalDelegation(k.cdc, value) + if err != nil { + return err + } + + validator, found := k.GetValidator(ctx, delegation.GetValidatorAddr()) + if !found { + return types.ErrNoValidatorFound + } + + validators = append(validators, validator) + return nil + }) + + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryDelegatorValidatorsResponse{Validators: validators, Pagination: pageRes}, nil +} + +// Pool queries the pool info +func (k Querier) Pool(c context.Context, _ *types.QueryPoolRequest) (*types.QueryPoolResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + bondDenom := k.BondDenom(ctx) + bondedPool := k.GetBondedPool(ctx) + notBondedPool := k.GetNotBondedPool(ctx) + + pool := types.NewPool( + k.bankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, + k.bankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, + ) + + return &types.QueryPoolResponse{Pool: pool}, nil +} + +// Params queries the staking parameters +func (k Querier) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := k.GetParams(ctx) + + return &types.QueryParamsResponse{Params: params}, nil +} + +func queryRedelegation(ctx sdk.Context, k Querier, req *types.QueryRedelegationsRequest) (redels types.Redelegations, err error) { + + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, err + } + + srcValAddr, err := sdk.ValAddressFromBech32(req.SrcValidatorAddr) + if err != nil { + return nil, err + } + + dstValAddr, err := sdk.ValAddressFromBech32(req.DstValidatorAddr) + if err != nil { + return nil, err + } + + redel, found := k.GetRedelegation(ctx, delAddr, srcValAddr, dstValAddr) + if !found { + return nil, status.Errorf( + codes.NotFound, + "redelegation not found for delegator address %s from validator address %s", + req.DelegatorAddr, req.SrcValidatorAddr) + } + redels = []types.Redelegation{redel} + + return redels, err +} + +func queryRedelegationsFromSrcValidator(store sdk.KVStore, k Querier, req *types.QueryRedelegationsRequest) (redels types.Redelegations, res *query.PageResponse, err error) { + valAddr, err := sdk.ValAddressFromBech32(req.SrcValidatorAddr) + if err != nil { + return nil, nil, err + } + + srcValPrefix := types.GetREDsFromValSrcIndexKey(valAddr) + redStore := prefix.NewStore(store, srcValPrefix) + res, err = query.Paginate(redStore, req.Pagination, func(key []byte, value []byte) error { + storeKey := types.GetREDKeyFromValSrcIndexKey(append(srcValPrefix, key...)) + storeValue := store.Get(storeKey) + red, err := types.UnmarshalRED(k.cdc, storeValue) + if err != nil { + return err + } + redels = append(redels, red) + return nil + }) + + return redels, res, err +} + +func queryAllRedelegations(store sdk.KVStore, k Querier, req *types.QueryRedelegationsRequest) (redels types.Redelegations, res *query.PageResponse, err error) { + delAddr, err := sdk.AccAddressFromBech32(req.DelegatorAddr) + if err != nil { + return nil, nil, err + } + + redStore := prefix.NewStore(store, types.GetREDsKey(delAddr)) + res, err = query.Paginate(redStore, req.Pagination, func(key []byte, value []byte) error { + redelegation, err := types.UnmarshalRED(k.cdc, value) + if err != nil { + return err + } + redels = append(redels, redelegation) + return nil + }) + + return redels, res, err +} diff --git a/x/staking/keeper/grpc_query_test.go b/x/staking/keeper/grpc_query_test.go new file mode 100644 index 000000000000..acf6f9cb5bfb --- /dev/null +++ b/x/staking/keeper/grpc_query_test.go @@ -0,0 +1,765 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "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 (suite *KeeperTestSuite) TestGRPCQueryValidators() { + queryClient, vals := suite.queryClient, suite.vals + var req *types.QueryValidatorsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + numVals int + hasNext bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorsRequest{} + }, + true, + + len(vals), + false, + }, + {"empty status returns all the validators", + func() { + req = &types.QueryValidatorsRequest{Status: ""} + }, + true, + len(vals), + false, + }, + { + "invalid request", + func() { + req = &types.QueryValidatorsRequest{Status: "test"} + }, + false, + 0, + false, + }, + {"valid request", + func() { + req = &types.QueryValidatorsRequest{Status: types.Bonded.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + 1, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + valsResp, err := queryClient.Validators(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.NotNil(valsResp) + suite.Equal(tc.numVals, len(valsResp.Validators)) + suite.Equal(uint64(len(vals)), valsResp.Pagination.Total) + + if tc.hasNext { + suite.NotNil(valsResp.Pagination.NextKey) + } else { + suite.Nil(valsResp.Pagination.NextKey) + } + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCValidator() { + app, ctx, queryClient, vals := suite.app, suite.ctx, suite.queryClient, suite.vals + validator, found := app.StakingKeeper.GetValidator(ctx, vals[0].GetOperator()) + suite.True(found) + var req *types.QueryValidatorRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryValidatorRequest{} + }, + false, + }, + {"valid request", + func() { + req = &types.QueryValidatorRequest{ValidatorAddr: vals[0].OperatorAddress} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.Validator(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.True(validator.Equal(&res.Validator)) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDelegatorValidators() { + app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs + params := app.StakingKeeper.GetParams(ctx) + delValidators := app.StakingKeeper.GetDelegatorValidators(ctx, addrs[0], params.MaxValidators) + var req *types.QueryDelegatorValidatorsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorValidatorsRequest{} + }, + false, + }, + {"valid request", + func() { + req = &types.QueryDelegatorValidatorsRequest{ + DelegatorAddr: addrs[0].String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorValidators(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(1, len(res.Validators)) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(len(delValidators)), res.Pagination.Total) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDelegatorValidator() { + queryClient, addrs, vals := suite.queryClient, suite.addrs, suite.vals + addr := addrs[1] + addrVal, addrVal1 := vals[0].OperatorAddress, vals[1].OperatorAddress + var req *types.QueryDelegatorValidatorRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "empty request", + func() { + req = &types.QueryDelegatorValidatorRequest{} + }, + false, + }, + {"invalid delegator, validator pair", + func() { + req = &types.QueryDelegatorValidatorRequest{ + DelegatorAddr: addr.String(), + ValidatorAddr: addrVal, + } + }, + false, + }, + {"valid request", + func() { + req = &types.QueryDelegatorValidatorRequest{ + DelegatorAddr: addr.String(), + ValidatorAddr: addrVal1, + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorValidator(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(addrVal1, res.Validator.OperatorAddress) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDelegation() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc, addrAcc1 := addrs[0], addrs[1] + addrVal := vals[0].OperatorAddress + valAddr, err := sdk.ValAddressFromBech32(addrVal) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + var req *types.QueryDelegationRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"empty request", + func() { + req = &types.QueryDelegationRequest{} + }, + false, + }, + {"invalid validator, delegator pair", + func() { + req = &types.QueryDelegationRequest{ + DelegatorAddr: addrAcc1.String(), + ValidatorAddr: addrVal, + } + }, + false, + }, + {"valid request", + func() { + req = &types.QueryDelegationRequest{DelegatorAddr: addrAcc.String(), ValidatorAddr: addrVal} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.Delegation(gocontext.Background(), req) + if tc.expPass { + suite.Equal(delegation.ValidatorAddress, res.DelegationResponse.Delegation.ValidatorAddress) + suite.Equal(delegation.DelegatorAddress, res.DelegationResponse.Delegation.DelegatorAddress) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponse.Balance) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDelegatorDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc := addrs[0] + addrVal1 := vals[0].OperatorAddress + valAddr, err := sdk.ValAddressFromBech32(addrVal1) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + var req *types.QueryDelegatorDelegationsRequest + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"empty request", + func() { + req = &types.QueryDelegatorDelegationsRequest{} + }, + false, + }, {"invalid request", + func() { + req = &types.QueryDelegatorDelegationsRequest{DelegatorAddr: addrs[4].String()} + }, + false, + }, + {"valid request", + func() { + req = &types.QueryDelegatorDelegationsRequest{DelegatorAddr: addrAcc.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorDelegations(gocontext.Background(), req) + if tc.expPass { + suite.Equal(uint64(2), res.Pagination.Total) + suite.Len(res.DelegationResponses, 1) + suite.Equal(1, len(res.DelegationResponses)) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponses[0].Balance) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryValidatorDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc := addrs[0] + addrVal1 := vals[1].OperatorAddress + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + addrVal2 := valAddrs[4] + valAddr, err := sdk.ValAddressFromBech32(addrVal1) + suite.NoError(err) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr) + suite.True(found) + + var req *types.QueryValidatorDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + {"empty request", + func() { + req = &types.QueryValidatorDelegationsRequest{} + }, + false, + true, + }, + {"invalid validator delegator pair", + func() { + req = &types.QueryValidatorDelegationsRequest{ValidatorAddr: addrVal2.String()} + }, + false, + false, + }, + {"valid request", + func() { + req = &types.QueryValidatorDelegationsRequest{ValidatorAddr: addrVal1, + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.ValidatorDelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Len(res.DelegationResponses, 1) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(2), res.Pagination.Total) + suite.Equal(addrVal1, res.DelegationResponses[0].Delegation.ValidatorAddress) + suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponses[0].Balance) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.DelegationResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryUnbondingDelegation() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc2 := addrs[1] + addrVal2 := vals[1].OperatorAddress + + unbondingTokens := sdk.TokensFromConsensusPower(2) + valAddr, err1 := sdk.ValAddressFromBech32(addrVal2) + suite.NoError(err1) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc2, valAddr, unbondingTokens.ToDec()) + suite.NoError(err) + + unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc2, valAddr) + suite.True(found) + var req *types.QueryUnbondingDelegationRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"empty request", + func() { + req = &types.QueryUnbondingDelegationRequest{} + }, + false, + }, + {"invalid request", + func() { + req = &types.QueryUnbondingDelegationRequest{} + }, + false, + }, + {"valid request", + func() { + req = &types.QueryUnbondingDelegationRequest{ + DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal2} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.UnbondingDelegation(gocontext.Background(), req) + if tc.expPass { + suite.NotNil(res) + suite.Equal(unbond, res.Unbond) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryDelegatorUnbondingDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc, addrAcc1 := addrs[0], addrs[1] + addrVal, addrVal2 := vals[0].OperatorAddress, vals[1].OperatorAddress + + unbondingTokens := sdk.TokensFromConsensusPower(2) + valAddr1, err1 := sdk.ValAddressFromBech32(addrVal) + suite.NoError(err1) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr1, unbondingTokens.ToDec()) + suite.NoError(err) + valAddr2, err1 := sdk.ValAddressFromBech32(addrVal2) + suite.NoError(err1) + _, err = app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr2, unbondingTokens.ToDec()) + suite.NoError(err) + + unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc, valAddr1) + suite.True(found) + var req *types.QueryDelegatorUnbondingDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + {"empty request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{} + }, + false, + true, + }, + {"invalid request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{DelegatorAddr: addrAcc1.String()} + }, + false, + false, + }, + {"valid request", + func() { + req = &types.QueryDelegatorUnbondingDelegationsRequest{DelegatorAddr: addrAcc.String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.DelegatorUnbondingDelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.NotNil(res.Pagination.NextKey) + suite.Equal(uint64(2), res.Pagination.Total) + suite.Len(res.UnbondingResponses, 1) + suite.Equal(unbond, res.UnbondingResponses[0]) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.UnbondingResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryPoolParameters() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + bondDenom := sdk.DefaultBondDenom + + // Query pool + res, err := queryClient.Pool(gocontext.Background(), &types.QueryPoolRequest{}) + suite.NoError(err) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + suite.Equal(app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, res.Pool.NotBondedTokens) + suite.Equal(app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, res.Pool.BondedTokens) + + // Query Params + resp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{}) + suite.NoError(err) + suite.Equal(app.StakingKeeper.GetParams(ctx), resp.Params) +} + +func (suite *KeeperTestSuite) TestGRPCQueryHistoricalInfo() { + app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient + + hi, found := app.StakingKeeper.GetHistoricalInfo(ctx, 5) + suite.True(found) + + var req *types.QueryHistoricalInfoRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"empty request", + func() { + req = &types.QueryHistoricalInfoRequest{} + }, + false, + }, + {"invalid request with negative height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: -1} + }, + false, + }, + {"valid request with old height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: 4} + }, + false, + }, + {"valid request with current height", + func() { + req = &types.QueryHistoricalInfoRequest{Height: 5} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.HistoricalInfo(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.NotNil(res) + suite.True(hi.Equal(res.Hist)) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryRedelegation() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + + addrAcc, addrAcc1 := addrs[0], addrs[1] + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + val1, val2, val3, val4 := vals[0], vals[1], valAddrs[3], valAddrs[4] + delAmount := sdk.TokensFromConsensusPower(1) + _, err := app.StakingKeeper.Delegate(ctx, addrAcc1, delAmount, types.Unbonded, val1, true) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + rdAmount := sdk.TokensFromConsensusPower(1) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator(), rdAmount.ToDec()) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator()) + suite.True(found) + + var req *types.QueryRedelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + expErr bool + }{ + {"request redelegations for non existent addr", + func() { + req = &types.QueryRedelegationsRequest{DelegatorAddr: addrAcc.String()} + }, + false, + false, + }, + {"request redelegations with non existent pairs", + func() { + req = &types.QueryRedelegationsRequest{DelegatorAddr: addrAcc.String(), SrcValidatorAddr: val3.String(), + DstValidatorAddr: val4.String()} + }, + false, + true, + }, + {"request redelegations with delegatoraddr, sourceValAddr, destValAddr", + func() { + req = &types.QueryRedelegationsRequest{ + DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, + DstValidatorAddr: val2.OperatorAddress, Pagination: &query.PageRequest{}} + }, + true, + false, + }, + {"request redelegations with delegatoraddr and sourceValAddr", + func() { + req = &types.QueryRedelegationsRequest{ + DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress, + Pagination: &query.PageRequest{}} + }, + true, + false, + }, + {"query redelegations with sourceValAddr only", + func() { + req = &types.QueryRedelegationsRequest{ + SrcValidatorAddr: val1.GetOperator().String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.Redelegations(gocontext.Background(), req) + if tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Len(res.RedelegationResponses, len(redel.Entries)) + suite.Equal(redel.DelegatorAddress, res.RedelegationResponses[0].Redelegation.DelegatorAddress) + suite.Equal(redel.ValidatorSrcAddress, res.RedelegationResponses[0].Redelegation.ValidatorSrcAddress) + suite.Equal(redel.ValidatorDstAddress, res.RedelegationResponses[0].Redelegation.ValidatorDstAddress) + suite.Len(redel.Entries, len(res.RedelegationResponses[0].Entries)) + } else if !tc.expPass && !tc.expErr { + suite.NoError(err) + suite.Nil(res.RedelegationResponses) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func (suite *KeeperTestSuite) TestGRPCQueryValidatorUnbondingDelegations() { + app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals + addrAcc1, _ := addrs[0], addrs[1] + val1 := vals[0] + + // undelegate + undelAmount := sdk.TokensFromConsensusPower(2) + _, err := app.StakingKeeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), undelAmount.ToDec()) + suite.NoError(err) + applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1) + + var req *types.QueryValidatorUnbondingDelegationsRequest + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + {"empty request", + func() { + req = &types.QueryValidatorUnbondingDelegationsRequest{} + }, + false, + }, + {"valid request", + func() { + req = &types.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: val1.GetOperator().String(), + Pagination: &query.PageRequest{Limit: 1, CountTotal: true}} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + tc.malleate() + res, err := queryClient.ValidatorUnbondingDelegations(gocontext.Background(), req) + if tc.expPass { + suite.NoError(err) + suite.Equal(uint64(1), res.Pagination.Total) + suite.Equal(1, len(res.UnbondingResponses)) + suite.Equal(res.UnbondingResponses[0].ValidatorAddress, val1.OperatorAddress) + } else { + suite.Error(err) + suite.Nil(res) + } + }) + } +} + +func createValidators(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers []int64) ([]sdk.AccAddress, []sdk.ValAddress, []types.Validator) { + addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, sdk.TokensFromConsensusPower(300)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrs) + pks := simapp.CreateTestPubKeys(5) + + appCodec, _ := simapp.MakeCodecs() + app.StakingKeeper = keeper.NewKeeper( + appCodec, + app.GetKey(types.StoreKey), + app.AccountKeeper, + app.BankKeeper, + app.GetSubspace(types.ModuleName), + ) + + val1 := teststaking.NewValidator(t, valAddrs[0], pks[0]) + val2 := teststaking.NewValidator(t, valAddrs[1], pks[1]) + vals := []types.Validator{val1, val2} + + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidator(ctx, val2) + app.StakingKeeper.SetValidatorByConsAddr(ctx, val1) + app.StakingKeeper.SetValidatorByConsAddr(ctx, val2) + app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val1) + app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val2) + + _, err := app.StakingKeeper.Delegate(ctx, addrs[0], sdk.TokensFromConsensusPower(powers[0]), types.Unbonded, val1, true) + require.NoError(t, err) + _, err = app.StakingKeeper.Delegate(ctx, addrs[1], sdk.TokensFromConsensusPower(powers[1]), types.Unbonded, val2, true) + require.NoError(t, err) + _, err = app.StakingKeeper.Delegate(ctx, addrs[0], sdk.TokensFromConsensusPower(powers[2]), types.Unbonded, val2, true) + require.NoError(t, err) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + + return addrs, valAddrs, vals +} diff --git a/x/staking/keeper/historical_info.go b/x/staking/keeper/historical_info.go index 2ce0aee7dbcc..20c5a8ce85c9 100644 --- a/x/staking/keeper/historical_info.go +++ b/x/staking/keeper/historical_info.go @@ -15,16 +15,14 @@ func (k Keeper) GetHistoricalInfo(ctx sdk.Context, height int64) (types.Historic return types.HistoricalInfo{}, false } - hi := types.MustUnmarshalHistoricalInfo(k.cdc, value) - return hi, true + return types.MustUnmarshalHistoricalInfo(k.cdc, value), true } // SetHistoricalInfo sets the historical info at a given height -func (k Keeper) SetHistoricalInfo(ctx sdk.Context, height int64, hi types.HistoricalInfo) { +func (k Keeper) SetHistoricalInfo(ctx sdk.Context, height int64, hi *types.HistoricalInfo) { store := ctx.KVStore(k.storeKey) key := types.GetHistoricalInfoKey(height) - - value := types.MustMarshalHistoricalInfo(k.cdc, hi) + value := k.cdc.MustMarshalBinaryBare(hi) store.Set(key, value) } @@ -36,6 +34,35 @@ func (k Keeper) DeleteHistoricalInfo(ctx sdk.Context, height int64) { store.Delete(key) } +// IterateHistoricalInfo provides an interator over all stored HistoricalInfo +// objects. For each HistoricalInfo object, cb will be called. If the cb returns +// true, the iterator will close and stop. +func (k Keeper) IterateHistoricalInfo(ctx sdk.Context, cb func(types.HistoricalInfo) bool) { + store := ctx.KVStore(k.storeKey) + + iterator := sdk.KVStorePrefixIterator(store, types.HistoricalInfoKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + histInfo := types.MustUnmarshalHistoricalInfo(k.cdc, iterator.Value()) + if cb(histInfo) { + break + } + } +} + +// GetAllHistoricalInfo returns all stored HistoricalInfo objects. +func (k Keeper) GetAllHistoricalInfo(ctx sdk.Context) []types.HistoricalInfo { + var infos []types.HistoricalInfo + + k.IterateHistoricalInfo(ctx, func(histInfo types.HistoricalInfo) bool { + infos = append(infos, histInfo) + return false + }) + + return infos +} + // TrackHistoricalInfo saves the latest historical-info and deletes the oldest // heights that are below pruning height func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) { @@ -67,5 +94,5 @@ func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) { historicalEntry := types.NewHistoricalInfo(ctx.BlockHeader(), lastVals) // Set latest HistoricalInfo at current height - k.SetHistoricalInfo(ctx, ctx.BlockHeight(), historicalEntry) + k.SetHistoricalInfo(ctx, ctx.BlockHeight(), &historicalEntry) } diff --git a/x/staking/keeper/historical_info_test.go b/x/staking/keeper/historical_info_test.go index 204dd3ac512d..a9411d0db974 100644 --- a/x/staking/keeper/historical_info_test.go +++ b/x/staking/keeper/historical_info_test.go @@ -1,107 +1,144 @@ -package keeper +package keeper_test import ( "sort" "testing" - abci "github.com/tendermint/tendermint/abci/types" + "github.com/stretchr/testify/require" + 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/staking/teststaking" "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/stretchr/testify/require" ) func TestHistoricalInfo(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 10) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + validators := make([]types.Validator, len(addrVals)) for i, valAddr := range addrVals { - validators[i] = types.NewValidator(valAddr, PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, valAddr, PKs[i]) } hi := types.NewHistoricalInfo(ctx.BlockHeader(), validators) + app.StakingKeeper.SetHistoricalInfo(ctx, 2, &hi) - keeper.SetHistoricalInfo(ctx, 2, hi) - - recv, found := keeper.GetHistoricalInfo(ctx, 2) + recv, found := app.StakingKeeper.GetHistoricalInfo(ctx, 2) require.True(t, found, "HistoricalInfo not found after set") require.Equal(t, hi, recv, "HistoricalInfo not equal") - require.True(t, sort.IsSorted(types.Validators(recv.ValSet)), "HistoricalInfo validators is not sorted") + require.True(t, sort.IsSorted(types.ValidatorsByVotingPower(recv.Valset)), "HistoricalInfo validators is not sorted") - keeper.DeleteHistoricalInfo(ctx, 2) + app.StakingKeeper.DeleteHistoricalInfo(ctx, 2) - recv, found = keeper.GetHistoricalInfo(ctx, 2) + recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 2) require.False(t, found, "HistoricalInfo found after delete") require.Equal(t, types.HistoricalInfo{}, recv, "HistoricalInfo is not empty") } func TestTrackHistoricalInfo(t *testing.T) { - ctx, _, k, _ := CreateTestInput(t, false, 10) + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) // set historical entries in params to 5 params := types.DefaultParams() params.HistoricalEntries = 5 - k.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // set historical info at 5, 4 which should be pruned // and check that it has been stored - h4 := abci.Header{ + h4 := tmproto.Header{ ChainID: "HelloChain", Height: 4, } - h5 := abci.Header{ + h5 := tmproto.Header{ ChainID: "HelloChain", Height: 5, } valSet := []types.Validator{ - types.NewValidator(sdk.ValAddress(Addrs[0]), PKs[0], types.Description{}), - types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}), + teststaking.NewValidator(t, addrVals[0], PKs[0]), + teststaking.NewValidator(t, addrVals[1], PKs[1]), } hi4 := types.NewHistoricalInfo(h4, valSet) hi5 := types.NewHistoricalInfo(h5, valSet) - k.SetHistoricalInfo(ctx, 4, hi4) - k.SetHistoricalInfo(ctx, 5, hi5) - recv, found := k.GetHistoricalInfo(ctx, 4) + app.StakingKeeper.SetHistoricalInfo(ctx, 4, &hi4) + app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi5) + recv, found := app.StakingKeeper.GetHistoricalInfo(ctx, 4) require.True(t, found) require.Equal(t, hi4, recv) - recv, found = k.GetHistoricalInfo(ctx, 5) + recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 5) require.True(t, found) require.Equal(t, hi5, recv) - // Set last validators in keeper - val1 := types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{}) - k.SetValidator(ctx, val1) - k.SetLastValidatorPower(ctx, val1.OperatorAddress, 10) - val2 := types.NewValidator(sdk.ValAddress(Addrs[3]), PKs[3], types.Description{}) + // Set bonded validators in keeper + val1 := teststaking.NewValidator(t, addrVals[2], PKs[2]) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetLastValidatorPower(ctx, val1.GetOperator(), 10) + val2 := teststaking.NewValidator(t, addrVals[3], PKs[3]) + app.StakingKeeper.SetValidator(ctx, val2) + app.StakingKeeper.SetLastValidatorPower(ctx, val2.GetOperator(), 80) + vals := []types.Validator{val1, val2} - sort.Sort(types.Validators(vals)) - k.SetValidator(ctx, val2) - k.SetLastValidatorPower(ctx, val2.OperatorAddress, 8) + sort.Sort(types.ValidatorsByVotingPower(vals)) // Set Header for BeginBlock context - header := abci.Header{ + header := tmproto.Header{ ChainID: "HelloChain", Height: 10, } ctx = ctx.WithBlockHeader(header) - k.TrackHistoricalInfo(ctx) + app.StakingKeeper.TrackHistoricalInfo(ctx) // Check HistoricalInfo at height 10 is persisted expected := types.HistoricalInfo{ Header: header, - ValSet: vals, + Valset: vals, } - recv, found = k.GetHistoricalInfo(ctx, 10) + recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 10) require.True(t, found, "GetHistoricalInfo failed after BeginBlock") require.Equal(t, expected, recv, "GetHistoricalInfo returned eunexpected result") // Check HistoricalInfo at height 5, 4 is pruned - recv, found = k.GetHistoricalInfo(ctx, 4) + recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 4) require.False(t, found, "GetHistoricalInfo did not prune earlier height") require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune") - recv, found = k.GetHistoricalInfo(ctx, 5) + recv, found = app.StakingKeeper.GetHistoricalInfo(ctx, 5) require.False(t, found, "GetHistoricalInfo did not prune first prune height") require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune") } + +func TestGetAllHistoricalInfo(t *testing.T) { + _, app, ctx := createTestInput() + + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 50, sdk.NewInt(0)) + addrVals := simapp.ConvertAddrsToValAddrs(addrDels) + + valSet := []types.Validator{ + teststaking.NewValidator(t, addrVals[0], PKs[0]), + teststaking.NewValidator(t, addrVals[1], PKs[1]), + } + + header1 := tmproto.Header{ChainID: "HelloChain", Height: 10} + header2 := tmproto.Header{ChainID: "HelloChain", Height: 11} + header3 := tmproto.Header{ChainID: "HelloChain", Height: 12} + + hist1 := types.HistoricalInfo{Header: header1, Valset: valSet} + hist2 := types.HistoricalInfo{Header: header2, Valset: valSet} + hist3 := types.HistoricalInfo{Header: header3, Valset: valSet} + + expHistInfos := []types.HistoricalInfo{hist1, hist2, hist3} + + for i, hi := range expHistInfos { + app.StakingKeeper.SetHistoricalInfo(ctx, int64(10+i), &hi) + } + + infos := app.StakingKeeper.GetAllHistoricalInfo(ctx) + require.Equal(t, expHistInfos, infos) +} diff --git a/x/staking/keeper/invariants.go b/x/staking/keeper/invariants.go index e89cfab1d047..e21de8f9511d 100644 --- a/x/staking/keeper/invariants.go +++ b/x/staking/keeper/invariants.go @@ -5,13 +5,11 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" "github.com/cosmos/cosmos-sdk/x/staking/types" ) // RegisterInvariants registers all staking invariants func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "module-accounts", ModuleAccountInvariants(k)) ir.RegisterRoute(types.ModuleName, "nonnegative-power", @@ -24,7 +22,6 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { // AllInvariants runs all invariants of the staking module. func AllInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { res, stop := ModuleAccountInvariants(k)(ctx) if stop { @@ -55,11 +52,11 @@ func ModuleAccountInvariants(k Keeper) sdk.Invariant { notBondedPool := k.GetNotBondedPool(ctx) bondDenom := k.BondDenom(ctx) - k.IterateValidators(ctx, func(_ int64, validator exported.ValidatorI) bool { + k.IterateValidators(ctx, func(_ int64, validator types.ValidatorI) bool { switch validator.GetStatus() { - case sdk.Bonded: + case types.Bonded: bonded = bonded.Add(validator.GetTokens()) - case sdk.Unbonding, sdk.Unbonded: + case types.Unbonding, types.Unbonded: notBonded = notBonded.Add(validator.GetTokens()) default: panic("invalid validator status") @@ -74,9 +71,9 @@ func ModuleAccountInvariants(k Keeper) sdk.Invariant { return false }) - poolBonded := bondedPool.GetCoins().AmountOf(bondDenom) - poolNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) - broken := !poolBonded.Equal(bonded) || !poolNotBonded.Equal(notBonded) + poolBonded := k.bankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom) + poolNotBonded := k.bankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom) + broken := !poolBonded.Amount.Equal(bonded) || !poolNotBonded.Amount.Equal(notBonded) // Bonded tokens should equal sum of tokens with bonded validators // Not-bonded tokens should equal unbonding delegations plus tokens on unbonded validators @@ -96,11 +93,12 @@ func ModuleAccountInvariants(k Keeper) sdk.Invariant { // NonNegativePowerInvariant checks that all stored validators have >= 0 power. func NonNegativePowerInvariant(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - var msg string - var broken bool + var ( + msg string + broken bool + ) iterator := k.ValidatorsPowerStoreIterator(ctx) - for ; iterator.Valid(); iterator.Next() { validator, found := k.GetValidator(ctx, iterator.Value()) if !found { @@ -122,6 +120,7 @@ func NonNegativePowerInvariant(k Keeper) sdk.Invariant { } } iterator.Close() + return sdk.FormatInvariant(types.ModuleName, "nonnegative power", fmt.Sprintf("found invalid validator powers\n%s", msg)), broken } } @@ -129,20 +128,26 @@ func NonNegativePowerInvariant(k Keeper) sdk.Invariant { // PositiveDelegationInvariant checks that all stored delegations have > 0 shares. func PositiveDelegationInvariant(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - var msg string - var count int + var ( + msg string + count int + ) delegations := k.GetAllDelegations(ctx) for _, delegation := range delegations { if delegation.Shares.IsNegative() { count++ + msg += fmt.Sprintf("\tdelegation with negative shares: %+v\n", delegation) } + if delegation.Shares.IsZero() { count++ + msg += fmt.Sprintf("\tdelegation with zero shares: %+v\n", delegation) } } + broken := count != 0 return sdk.FormatInvariant(types.ModuleName, "positive delegations", fmt.Sprintf( @@ -155,15 +160,16 @@ func PositiveDelegationInvariant(k Keeper) sdk.Invariant { // amount stored in each validator. func DelegatorSharesInvariant(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { - var msg string - var broken bool + var ( + msg string + broken bool + ) validators := k.GetAllValidators(ctx) for _, validator := range validators { - valTotalDelShares := validator.GetDelegatorShares() - totalDelShares := sdk.ZeroDec() + delegations := k.GetValidatorDelegations(ctx, validator.GetOperator()) for _, delegation := range delegations { totalDelShares = totalDelShares.Add(delegation.Shares) @@ -176,6 +182,7 @@ func DelegatorSharesInvariant(k Keeper) sdk.Invariant { "\tsum of Delegator.Shares: %v\n", valTotalDelShares, totalDelShares) } } + return sdk.FormatInvariant(types.ModuleName, "delegator shares", msg), broken } } diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index fd51603115b7..81613d92bca9 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -8,12 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -const aminoCacheSize = 500 - // Implements ValidatorSet interface var _ types.ValidatorSet = Keeper{} @@ -23,42 +21,47 @@ var _ types.DelegationSet = Keeper{} // keeper of the staking store type Keeper struct { storeKey sdk.StoreKey - cdc *codec.Codec - supplyKeeper types.SupplyKeeper + cdc codec.BinaryMarshaler + authKeeper types.AccountKeeper + bankKeeper types.BankKeeper hooks types.StakingHooks - paramstore params.Subspace - validatorCache map[string]cachedValidator + paramstore paramtypes.Subspace validatorCacheList *list.List } // NewKeeper creates a new staking Keeper instance func NewKeeper( - cdc *codec.Codec, key sdk.StoreKey, supplyKeeper types.SupplyKeeper, paramstore params.Subspace, + cdc codec.BinaryMarshaler, key sdk.StoreKey, ak types.AccountKeeper, bk types.BankKeeper, + ps paramtypes.Subspace, ) Keeper { + // set KeyTable if it has not already been set + if !ps.HasKeyTable() { + ps = ps.WithKeyTable(types.ParamKeyTable()) + } // ensure bonded and not bonded module accounts are set - if addr := supplyKeeper.GetModuleAddress(types.BondedPoolName); addr == nil { + if addr := ak.GetModuleAddress(types.BondedPoolName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName)) } - if addr := supplyKeeper.GetModuleAddress(types.NotBondedPoolName); addr == nil { + if addr := ak.GetModuleAddress(types.NotBondedPoolName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName)) } return Keeper{ storeKey: key, cdc: cdc, - supplyKeeper: supplyKeeper, - paramstore: paramstore.WithKeyTable(ParamKeyTable()), + authKeeper: ak, + bankKeeper: bk, + paramstore: ps, hooks: nil, - validatorCache: make(map[string]cachedValidator, aminoCacheSize), validatorCacheList: list.New(), } } // 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)) + return ctx.Logger().With("module", "x/"+types.ModuleName) } // Set the validator hooks @@ -66,24 +69,30 @@ func (k *Keeper) SetHooks(sh types.StakingHooks) *Keeper { if k.hooks != nil { panic("cannot set validator hooks twice") } + k.hooks = sh + return k } // Load the last total validator power. -func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Int) { +func (k Keeper) GetLastTotalPower(ctx sdk.Context) sdk.Int { store := ctx.KVStore(k.storeKey) - b := store.Get(types.LastTotalPowerKey) - if b == nil { + bz := store.Get(types.LastTotalPowerKey) + + if bz == nil { return sdk.ZeroInt() } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &power) - return + + ip := sdk.IntProto{} + k.cdc.MustUnmarshalBinaryBare(bz, &ip) + + return ip.Int } // Set the last total validator power. func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) { store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(power) - store.Set(types.LastTotalPowerKey, b) + bz := k.cdc.MustMarshalBinaryBare(&sdk.IntProto{Int: power}) + store.Set(types.LastTotalPowerKey, bz) } diff --git a/x/staking/keeper/keeper_test.go b/x/staking/keeper/keeper_test.go index eb9d16459e49..2f9819989ff0 100644 --- a/x/staking/keeper/keeper_test.go +++ b/x/staking/keeper/keeper_test.go @@ -1,24 +1,72 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/types" ) +type KeeperTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + addrs []sdk.AccAddress + vals []types.Validator + queryClient types.QueryClient +} + +func (suite *KeeperTestSuite) SetupTest() { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + querier := keeper.Querier{Keeper: app.StakingKeeper} + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, querier) + queryClient := types.NewQueryClient(queryHelper) + + addrs, _, validators := createValidators(suite.T(), ctx, app, []int64{9, 8, 7}) + header := tmproto.Header{ + ChainID: "HelloChain", + Height: 5, + } + + // sort a copy of the validators, so that original validators does not + // have its order changed + sortedVals := make([]types.Validator, len(validators)) + copy(sortedVals, validators) + hi := types.NewHistoricalInfo(header, sortedVals) + app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi) + + suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals = app, ctx, queryClient, addrs, validators +} func TestParams(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + expParams := types.DefaultParams() //check that the empty keeper loads the default - resParams := keeper.GetParams(ctx) + resParams := app.StakingKeeper.GetParams(ctx) require.True(t, expParams.Equal(resParams)) //modify a params, save, and retrieve expParams.MaxValidators = 777 - keeper.SetParams(ctx, expParams) - resParams = keeper.GetParams(ctx) + app.StakingKeeper.SetParams(ctx, expParams) + resParams = app.StakingKeeper.GetParams(ctx) require.True(t, expParams.Equal(resParams)) } + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/staking/keeper/msg_server.go b/x/staking/keeper/msg_server.go new file mode 100644 index 000000000000..022d7d746ddd --- /dev/null +++ b/x/staking/keeper/msg_server.go @@ -0,0 +1,360 @@ +package keeper + +import ( + "context" + "time" + + metrics "github.com/armon/go-metrics" + tmstrings "github.com/tendermint/tendermint/libs/strings" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the bank MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +var _ types.MsgServer = msgServer{} + +func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateValidator) (*types.MsgCreateValidatorResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return nil, err + } + + // check to see if the pubkey or sender has been registered before + if _, found := k.GetValidator(ctx, valAddr); found { + return nil, types.ErrValidatorOwnerExists + } + + pk, ok := msg.Pubkey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", pk) + } + + if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found { + return nil, types.ErrValidatorPubKeyExists + } + + bondDenom := k.BondDenom(ctx) + if msg.Value.Denom != bondDenom { + return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Value.Denom, bondDenom) + } + + if _, err := msg.Description.EnsureLength(); err != nil { + return nil, err + } + + cp := ctx.ConsensusParams() + if cp != nil && cp.Validator != nil { + if !tmstrings.StringInSlice(pk.Type(), cp.Validator.PubKeyTypes) { + return nil, sdkerrors.Wrapf( + types.ErrValidatorPubKeyTypeNotSupported, + "got: %s, expected: %s", pk.Type(), cp.Validator.PubKeyTypes, + ) + } + } + + validator, err := types.NewValidator(valAddr, pk, msg.Description) + if err != nil { + return nil, err + } + commission := types.NewCommissionWithTime( + msg.Commission.Rate, msg.Commission.MaxRate, + msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, + ) + + validator, err = validator.SetInitialCommission(commission) + if err != nil { + return nil, err + } + + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + + validator.MinSelfDelegation = msg.MinSelfDelegation + + k.SetValidator(ctx, validator) + k.SetValidatorByConsAddr(ctx, validator) + k.SetNewValidatorByPowerIndex(ctx, validator) + + // call the after-creation hook + k.AfterValidatorCreated(ctx, validator.GetOperator()) + + // move coins from the msg.Address account to a (self-delegation) delegator account + // the validator account and global shares are updated within here + // NOTE source will always be from a wallet which are unbonded + _, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Value.Amount, types.Unbonded, validator, true) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeCreateValidator, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + }) + + return &types.MsgCreateValidatorResponse{}, nil +} + +func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValidator) (*types.MsgEditValidatorResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return nil, err + } + // validator must already be registered + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return nil, types.ErrNoValidatorFound + } + + // replace all editable fields (clients should autofill existing values) + description, err := validator.Description.UpdateDescription(msg.Description) + if err != nil { + return nil, err + } + + validator.Description = description + + if msg.CommissionRate != nil { + commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) + if err != nil { + return nil, err + } + + // call the before-modification hook since we're about to update the commission + k.BeforeValidatorModified(ctx, valAddr) + + validator.Commission = commission + } + + if msg.MinSelfDelegation != nil { + if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) { + return nil, types.ErrMinSelfDelegationDecreased + } + + if msg.MinSelfDelegation.GT(validator.Tokens) { + return nil, types.ErrSelfDelegationBelowMinimum + } + + validator.MinSelfDelegation = (*msg.MinSelfDelegation) + } + + k.SetValidator(ctx, validator) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeEditValidator, + sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()), + sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress), + ), + }) + + return &types.MsgEditValidatorResponse{}, nil +} + +func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + valAddr, valErr := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if valErr != nil { + return nil, valErr + } + + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return nil, types.ErrNoValidatorFound + } + + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + + bondDenom := k.BondDenom(ctx) + if msg.Amount.Denom != bondDenom { + return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom) + } + + // NOTE: source funds are always unbonded + _, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonded, validator, true) + if err != nil { + return nil, err + } + + if msg.Amount.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "delegate") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", msg.Type()}, + float32(msg.Amount.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)}, + ) + }() + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegate, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + }) + + return &types.MsgDelegateResponse{}, nil +} + +func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRedelegate) (*types.MsgBeginRedelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddress) + if err != nil { + return nil, err + } + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + shares, err := k.ValidateUnbondAmount( + ctx, delegatorAddress, valSrcAddr, msg.Amount.Amount, + ) + if err != nil { + return nil, err + } + + bondDenom := k.BondDenom(ctx) + if msg.Amount.Denom != bondDenom { + return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom) + } + + valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddress) + if err != nil { + return nil, err + } + + completionTime, err := k.BeginRedelegation( + ctx, delegatorAddress, valSrcAddr, valDstAddr, shares, + ) + if err != nil { + return nil, err + } + + if msg.Amount.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "redelegate") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", msg.Type()}, + float32(msg.Amount.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)}, + ) + }() + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeRedelegate, + sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress), + sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + }) + + return &types.MsgBeginRedelegateResponse{ + CompletionTime: completionTime, + }, nil +} + +func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) (*types.MsgUndelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + addr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return nil, err + } + delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return nil, err + } + shares, err := k.ValidateUnbondAmount( + ctx, delegatorAddress, addr, msg.Amount.Amount, + ) + if err != nil { + return nil, err + } + + bondDenom := k.BondDenom(ctx) + if msg.Amount.Denom != bondDenom { + return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom) + } + + completionTime, err := k.Keeper.Undelegate(ctx, delegatorAddress, addr, shares) + if err != nil { + return nil, err + } + + if msg.Amount.Amount.IsInt64() { + defer func() { + telemetry.IncrCounter(1, types.ModuleName, "undelegate") + telemetry.SetGaugeWithLabels( + []string{"tx", "msg", msg.Type()}, + float32(msg.Amount.Amount.Int64()), + []metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)}, + ) + }() + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeUnbond, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress), + ), + }) + + return &types.MsgUndelegateResponse{ + CompletionTime: completionTime, + }, nil +} diff --git a/x/staking/keeper/params.go b/x/staking/keeper/params.go index 11f4b57bc55b..7fe0a8d098eb 100644 --- a/x/staking/keeper/params.go +++ b/x/staking/keeper/params.go @@ -4,20 +4,9 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// Default parameter namespace -const ( - DefaultParamspace = types.ModuleName -) - -// ParamTable for staking module -func ParamKeyTable() params.KeyTable { - return params.NewKeyTable().RegisterParamSet(&types.Params{}) -} - // UnbondingTime func (k Keeper) UnbondingTime(ctx sdk.Context) (res time.Duration) { k.paramstore.Get(ctx, types.KeyUnbondingTime, &res) @@ -25,21 +14,21 @@ func (k Keeper) UnbondingTime(ctx sdk.Context) (res time.Duration) { } // MaxValidators - Maximum number of validators -func (k Keeper) MaxValidators(ctx sdk.Context) (res uint16) { +func (k Keeper) MaxValidators(ctx sdk.Context) (res uint32) { k.paramstore.Get(ctx, types.KeyMaxValidators, &res) return } // MaxEntries - Maximum number of simultaneous unbonding // delegations or redelegations (per pair/trio) -func (k Keeper) MaxEntries(ctx sdk.Context) (res uint16) { +func (k Keeper) MaxEntries(ctx sdk.Context) (res uint32) { k.paramstore.Get(ctx, types.KeyMaxEntries, &res) return } // HistoricalEntries = number of historical info entries // to persist in store -func (k Keeper) HistoricalEntries(ctx sdk.Context) (res uint16) { +func (k Keeper) HistoricalEntries(ctx sdk.Context) (res uint32) { k.paramstore.Get(ctx, types.KeyHistoricalEntries, &res) return } diff --git a/x/staking/keeper/pool.go b/x/staking/keeper/pool.go index 004013ed8fe4..5ced841e2f2f 100644 --- a/x/staking/keeper/pool.go +++ b/x/staking/keeper/pool.go @@ -2,25 +2,24 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" ) // GetBondedPool returns the bonded tokens pool's module account -func (k Keeper) GetBondedPool(ctx sdk.Context) (bondedPool exported.ModuleAccountI) { - return k.supplyKeeper.GetModuleAccount(ctx, types.BondedPoolName) +func (k Keeper) GetBondedPool(ctx sdk.Context) (bondedPool authtypes.ModuleAccountI) { + return k.authKeeper.GetModuleAccount(ctx, types.BondedPoolName) } // GetNotBondedPool returns the not bonded tokens pool's module account -func (k Keeper) GetNotBondedPool(ctx sdk.Context) (notBondedPool exported.ModuleAccountI) { - return k.supplyKeeper.GetModuleAccount(ctx, types.NotBondedPoolName) +func (k Keeper) GetNotBondedPool(ctx sdk.Context) (notBondedPool authtypes.ModuleAccountI) { + return k.authKeeper.GetModuleAccount(ctx, types.NotBondedPoolName) } // bondedTokensToNotBonded transfers coins from the bonded to the not bonded pool within staking func (k Keeper) bondedTokensToNotBonded(ctx sdk.Context, tokens sdk.Int) { coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), tokens)) - err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.BondedPoolName, types.NotBondedPoolName, coins) - if err != nil { + if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.BondedPoolName, types.NotBondedPoolName, coins); err != nil { panic(err) } } @@ -28,8 +27,7 @@ func (k Keeper) bondedTokensToNotBonded(ctx sdk.Context, tokens sdk.Int) { // notBondedTokensToBonded transfers coins from the not bonded to the bonded pool within staking func (k Keeper) notBondedTokensToBonded(ctx sdk.Context, tokens sdk.Int) { coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), tokens)) - err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.NotBondedPoolName, types.BondedPoolName, coins) - if err != nil { + if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, types.NotBondedPoolName, types.BondedPoolName, coins); err != nil { panic(err) } } @@ -40,8 +38,10 @@ func (k Keeper) burnBondedTokens(ctx sdk.Context, amt sdk.Int) error { // skip as no coins need to be burned return nil } + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), amt)) - return k.supplyKeeper.BurnCoins(ctx, types.BondedPoolName, coins) + + return k.bankKeeper.BurnCoins(ctx, types.BondedPoolName, coins) } // burnNotBondedTokens removes coins from the not bonded pool module account @@ -50,28 +50,29 @@ func (k Keeper) burnNotBondedTokens(ctx sdk.Context, amt sdk.Int) error { // skip as no coins need to be burned return nil } + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), amt)) - return k.supplyKeeper.BurnCoins(ctx, types.NotBondedPoolName, coins) + + return k.bankKeeper.BurnCoins(ctx, types.NotBondedPoolName, coins) } // TotalBondedTokens total staking tokens supply which is bonded func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int { bondedPool := k.GetBondedPool(ctx) - return bondedPool.GetCoins().AmountOf(k.BondDenom(ctx)) + return k.bankKeeper.GetBalance(ctx, bondedPool.GetAddress(), k.BondDenom(ctx)).Amount } // StakingTokenSupply staking tokens from the total supply func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int { - return k.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf(k.BondDenom(ctx)) + return k.bankKeeper.GetSupply(ctx).GetTotal().AmountOf(k.BondDenom(ctx)) } // BondedRatio the fraction of the staking tokens which are currently bonded func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec { - bondedPool := k.GetBondedPool(ctx) - stakeSupply := k.StakingTokenSupply(ctx) if stakeSupply.IsPositive() { - return bondedPool.GetCoins().AmountOf(k.BondDenom(ctx)).ToDec().QuoInt(stakeSupply) + return k.TotalBondedTokens(ctx).ToDec().QuoInt(stakeSupply) } + return sdk.ZeroDec() } diff --git a/x/staking/keeper/querier.go b/x/staking/keeper/querier.go index bc8a5b5b7fc0..48272e245191 100644 --- a/x/staking/keeper/querier.go +++ b/x/staking/keeper/querier.go @@ -14,50 +14,50 @@ import ( ) // creates a querier for staking REST endpoints -func NewQuerier(k Keeper) sdk.Querier { +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { switch path[0] { case types.QueryValidators: - return queryValidators(ctx, req, k) + return queryValidators(ctx, req, k, legacyQuerierCdc) case types.QueryValidator: - return queryValidator(ctx, req, k) + return queryValidator(ctx, req, k, legacyQuerierCdc) case types.QueryValidatorDelegations: - return queryValidatorDelegations(ctx, req, k) + return queryValidatorDelegations(ctx, req, k, legacyQuerierCdc) case types.QueryValidatorUnbondingDelegations: - return queryValidatorUnbondingDelegations(ctx, req, k) + return queryValidatorUnbondingDelegations(ctx, req, k, legacyQuerierCdc) case types.QueryDelegation: - return queryDelegation(ctx, req, k) + return queryDelegation(ctx, req, k, legacyQuerierCdc) case types.QueryUnbondingDelegation: - return queryUnbondingDelegation(ctx, req, k) + return queryUnbondingDelegation(ctx, req, k, legacyQuerierCdc) case types.QueryDelegatorDelegations: - return queryDelegatorDelegations(ctx, req, k) + return queryDelegatorDelegations(ctx, req, k, legacyQuerierCdc) case types.QueryDelegatorUnbondingDelegations: - return queryDelegatorUnbondingDelegations(ctx, req, k) + return queryDelegatorUnbondingDelegations(ctx, req, k, legacyQuerierCdc) case types.QueryRedelegations: - return queryRedelegations(ctx, req, k) + return queryRedelegations(ctx, req, k, legacyQuerierCdc) case types.QueryDelegatorValidators: - return queryDelegatorValidators(ctx, req, k) + return queryDelegatorValidators(ctx, req, k, legacyQuerierCdc) case types.QueryDelegatorValidator: - return queryDelegatorValidator(ctx, req, k) + return queryDelegatorValidator(ctx, req, k, legacyQuerierCdc) case types.QueryHistoricalInfo: - return queryHistoricalInfo(ctx, req, k) + return queryHistoricalInfo(ctx, req, k, legacyQuerierCdc) case types.QueryPool: - return queryPool(ctx, k) + return queryPool(ctx, k, legacyQuerierCdc) case types.QueryParameters: - return queryParameters(ctx, k) + return queryParameters(ctx, k, legacyQuerierCdc) default: return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) @@ -65,16 +65,16 @@ func NewQuerier(k Keeper) sdk.Querier { } } -func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorsParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } validators := k.GetAllValidators(ctx) - filteredVals := make([]types.Validator, 0, len(validators)) + filteredVals := make(types.Validators, 0, len(validators)) for _, val := range validators { if strings.EqualFold(val.GetStatus().String(), params.Status) { @@ -89,7 +89,7 @@ func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, filteredVals = filteredVals[start:end] } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, filteredVals) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, filteredVals) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -97,10 +97,10 @@ func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, return res, nil } -func queryValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -110,7 +110,7 @@ func queryValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e return nil, types.ErrNoValidatorFound } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, validator) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, validator) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -118,16 +118,24 @@ func queryValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e return res, nil } -func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr) - delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + + start, end := client.Paginate(len(delegations), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators)) + if start < 0 || end < 0 { + delegations = []types.Delegation{} + } else { + delegations = delegations[start:end] + } + + delegationResps, err := DelegationsToDelegationResponses(ctx, k, delegations) if err != nil { return nil, err } @@ -136,7 +144,7 @@ func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) delegationResps = types.DelegationResponses{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResps) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, delegationResps) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -144,10 +152,10 @@ func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) return res, nil } -func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryValidatorParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -157,7 +165,14 @@ func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, unbonds = types.UnbondingDelegations{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbonds) + start, end := client.Paginate(len(unbonds), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators)) + if start < 0 || end < 0 { + unbonds = types.UnbondingDelegations{} + } else { + unbonds = unbonds[start:end] + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, unbonds) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -165,16 +180,17 @@ func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, return res, nil } -func queryDelegatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } delegations := k.GetAllDelegatorDelegations(ctx, params.DelegatorAddr) - delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + delegationResps, err := DelegationsToDelegationResponses(ctx, k, delegations) + if err != nil { return nil, err } @@ -183,7 +199,7 @@ func queryDelegatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) delegationResps = types.DelegationResponses{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResps) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, delegationResps) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -191,10 +207,10 @@ func queryDelegatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) return res, nil } -func queryDelegatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -204,7 +220,7 @@ func queryDelegatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, unbondingDelegations = types.UnbondingDelegations{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbondingDelegations) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, unbondingDelegations) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -212,12 +228,12 @@ func queryDelegatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, return res, nil } -func queryDelegatorValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryDelegatorValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryDelegatorParams stakingParams := k.GetParams(ctx) - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -227,7 +243,7 @@ func queryDelegatorValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) validators = types.Validators{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, validators) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, validators) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -235,20 +251,30 @@ func queryDelegatorValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) return res, nil } -func queryDelegatorValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryBondsParams +func queryDelegatorValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryDelegatorValidatorRequest - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - validator, err := k.GetDelegatorValidator(ctx, params.DelegatorAddr, params.ValidatorAddr) + delAddr, err := sdk.AccAddressFromBech32(params.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(params.ValidatorAddr) if err != nil { return nil, err } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, validator) + validator, err := k.GetDelegatorValidator(ctx, delAddr, valAddr) + if err != nil { + return nil, err + } + + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, validator) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -256,25 +282,35 @@ func queryDelegatorValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ( return res, nil } -func queryDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryBondsParams +func queryDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryDelegatorValidatorRequest - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - delegation, found := k.GetDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + delAddr, err := sdk.AccAddressFromBech32(params.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(params.ValidatorAddr) + if err != nil { + return nil, err + } + + delegation, found := k.GetDelegation(ctx, delAddr, valAddr) if !found { return nil, types.ErrNoDelegation } - delegationResp, err := delegationToDelegationResponse(ctx, k, delegation) + delegationResp, err := DelegationToDelegationResponse(ctx, k, delegation) if err != nil { return nil, err } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResp) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, delegationResp) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -282,20 +318,30 @@ func queryDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, return res, nil } -func queryUnbondingDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryBondsParams +func queryUnbondingDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryDelegatorValidatorRequest - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - unbond, found := k.GetUnbondingDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + delAddr, err := sdk.AccAddressFromBech32(params.DelegatorAddr) + if err != nil { + return nil, err + } + + valAddr, err := sdk.ValAddressFromBech32(params.ValidatorAddr) + if err != nil { + return nil, err + } + + unbond, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr) if !found { return nil, types.ErrNoUnbondingDelegation } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbond) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, unbond) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -303,10 +349,10 @@ func queryUnbondingDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) return res, nil } -func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { +func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { var params types.QueryRedelegationParams - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -327,7 +373,7 @@ func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt redels = k.GetAllRedelegations(ctx, params.DelegatorAddr, params.SrcValidatorAddr, params.DstValidatorAddr) } - redelResponses, err := redelegationsToRedelegationResponses(ctx, k, redels) + redelResponses, err := RedelegationsToRedelegationResponses(ctx, k, redels) if err != nil { return nil, err } @@ -336,7 +382,7 @@ func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt redelResponses = types.RedelegationResponses{} } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, redelResponses) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, redelResponses) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -344,10 +390,10 @@ func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt return res, nil } -func queryHistoricalInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryHistoricalInfoParams +func queryHistoricalInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryHistoricalInfoRequest - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } @@ -357,7 +403,7 @@ func queryHistoricalInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return nil, types.ErrNoHistoricalInfo } - res, err := codec.MarshalJSONIndent(types.ModuleCdc, hi) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, hi) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -365,21 +411,21 @@ func queryHistoricalInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by return res, nil } -func queryPool(ctx sdk.Context, k Keeper) ([]byte, error) { +func queryPool(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { bondDenom := k.BondDenom(ctx) - bondedPool := k.GetBondedPool(ctx) notBondedPool := k.GetNotBondedPool(ctx) + if bondedPool == nil || notBondedPool == nil { return nil, errors.New("pool accounts haven't been set") } pool := types.NewPool( - notBondedPool.GetCoins().AmountOf(bondDenom), - bondedPool.GetCoins().AmountOf(bondDenom), + k.bankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, + k.bankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, ) - res, err := codec.MarshalJSONIndent(types.ModuleCdc, pool) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, pool) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -387,10 +433,10 @@ func queryPool(ctx sdk.Context, k Keeper) ([]byte, error) { return res, nil } -func queryParameters(ctx sdk.Context, k Keeper) ([]byte, error) { +func queryParameters(ctx sdk.Context, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { params := k.GetParams(ctx) - res, err := codec.MarshalJSONIndent(types.ModuleCdc, params) + res, err := codec.MarshalJSONIndent(legacyQuerierCdc, params) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } @@ -401,27 +447,32 @@ func queryParameters(ctx sdk.Context, k Keeper) ([]byte, error) { //______________________________________________________ // util -func delegationToDelegationResponse(ctx sdk.Context, k Keeper, del types.Delegation) (types.DelegationResponse, error) { - val, found := k.GetValidator(ctx, del.ValidatorAddress) +func DelegationToDelegationResponse(ctx sdk.Context, k Keeper, del types.Delegation) (types.DelegationResponse, error) { + val, found := k.GetValidator(ctx, del.GetValidatorAddr()) if !found { return types.DelegationResponse{}, types.ErrNoValidatorFound } + delegatorAddress, err := sdk.AccAddressFromBech32(del.DelegatorAddress) + if err != nil { + return types.DelegationResponse{}, err + } + return types.NewDelegationResp( - del.DelegatorAddress, - del.ValidatorAddress, + delegatorAddress, + del.GetValidatorAddr(), del.Shares, sdk.NewCoin(k.BondDenom(ctx), val.TokensFromShares(del.Shares).TruncateInt()), ), nil } -func delegationsToDelegationResponses( +func DelegationsToDelegationResponses( ctx sdk.Context, k Keeper, delegations types.Delegations, ) (types.DelegationResponses, error) { - resp := make(types.DelegationResponses, len(delegations)) + for i, del := range delegations { - delResp, err := delegationToDelegationResponse(ctx, k, del) + delResp, err := DelegationToDelegationResponse(ctx, k, del) if err != nil { return nil, err } @@ -432,13 +483,26 @@ func delegationsToDelegationResponses( return resp, nil } -func redelegationsToRedelegationResponses( +func RedelegationsToRedelegationResponses( ctx sdk.Context, k Keeper, redels types.Redelegations, ) (types.RedelegationResponses, error) { - resp := make(types.RedelegationResponses, len(redels)) + for i, redel := range redels { - val, found := k.GetValidator(ctx, redel.ValidatorDstAddress) + valSrcAddr, err := sdk.ValAddressFromBech32(redel.ValidatorSrcAddress) + if err != nil { + panic(err) + } + valDstAddr, err := sdk.ValAddressFromBech32(redel.ValidatorDstAddress) + if err != nil { + panic(err) + } + + delegatorAddress, err := sdk.AccAddressFromBech32(redel.DelegatorAddress) + if err != nil { + panic(err) + } + val, found := k.GetValidator(ctx, valDstAddr) if !found { return nil, types.ErrNoValidatorFound } @@ -455,9 +519,9 @@ func redelegationsToRedelegationResponses( } resp[i] = types.NewRedelegationResponse( - redel.DelegatorAddress, - redel.ValidatorSrcAddress, - redel.ValidatorDstAddress, + delegatorAddress, + valSrcAddr, + valDstAddr, entryResponses, ) } diff --git a/x/staking/keeper/querier_test.go b/x/staking/keeper/querier_test.go index 320266ca1a28..0b194bd6dc71 100644 --- a/x/staking/keeper/querier_test.go +++ b/x/staking/keeper/querier_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "fmt" @@ -6,44 +6,47 @@ import ( "github.com/stretchr/testify/require" 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" sdk "github.com/cosmos/cosmos-sdk/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" ) -var ( - addrAcc1, addrAcc2 = Addrs[0], Addrs[1] - addrVal1, addrVal2 = sdk.ValAddress(Addrs[0]), sdk.ValAddress(Addrs[1]) - pk1, pk2 = PKs[0], PKs[1] -) - func TestNewQuerier(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + cdc, app, ctx := createTestInput() + + addrs := simapp.AddTestAddrs(app, ctx, 500, sdk.NewInt(10000)) + _, addrAcc2 := addrs[0], addrs[1] + addrVal1, _ := sdk.ValAddress(addrs[0]), sdk.ValAddress(addrs[1]) + // Create Validators amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8)} var validators [2]types.Validator for i, amt := range amts { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) validators[i], _ = validators[i].AddTokensFromDel(amt) - keeper.SetValidator(ctx, validators[i]) - keeper.SetValidatorByPowerIndex(ctx, validators[i]) + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) } - header := abci.Header{ + header := tmproto.Header{ ChainID: "HelloChain", Height: 5, } hi := types.NewHistoricalInfo(header, validators[:]) - keeper.SetHistoricalInfo(ctx, 5, hi) + app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi) query := abci.RequestQuery{ Path: "", Data: []byte{}, } - querier := NewQuerier(keeper) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) bz, err := querier(ctx, []string{"other"}, query) require.Error(t, err) @@ -55,7 +58,7 @@ func TestNewQuerier(t *testing.T) { _, err = querier(ctx, []string{"parameters"}, query) require.NoError(t, err) - queryValParams := types.NewQueryValidatorParams(addrVal1) + queryValParams := types.NewQueryValidatorParams(addrVal1, 0, 0) bz, errRes := cdc.MarshalJSON(queryValParams) require.NoError(t, errRes) @@ -94,7 +97,7 @@ func TestNewQuerier(t *testing.T) { _, err = querier(ctx, []string{"redelegations"}, query) require.NoError(t, err) - queryHisParams := types.NewQueryHistoricalInfoParams(5) + queryHisParams := types.QueryHistoricalInfoRequest{Height: 5} bz, errRes = cdc.MarshalJSON(queryHisParams) require.NoError(t, errRes) @@ -106,51 +109,56 @@ func TestNewQuerier(t *testing.T) { } func TestQueryParametersPool(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + cdc, app, ctx := createTestInput() + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + bondDenom := sdk.DefaultBondDenom - res, err := queryParameters(ctx, keeper) + res, err := querier(ctx, []string{types.QueryParameters}, abci.RequestQuery{}) require.NoError(t, err) var params types.Params errRes := cdc.UnmarshalJSON(res, ¶ms) require.NoError(t, errRes) - require.Equal(t, keeper.GetParams(ctx), params) + require.Equal(t, app.StakingKeeper.GetParams(ctx), params) - res, err = queryPool(ctx, keeper) + res, err = querier(ctx, []string{types.QueryPool}, abci.RequestQuery{}) require.NoError(t, err) var pool types.Pool - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - errRes = cdc.UnmarshalJSON(res, &pool) - require.NoError(t, errRes) - require.Equal(t, bondedPool.GetCoins().AmountOf(bondDenom), pool.BondedTokens) - require.Equal(t, notBondedPool.GetCoins().AmountOf(bondDenom), pool.NotBondedTokens) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + require.NoError(t, cdc.UnmarshalJSON(res, &pool)) + require.Equal(t, app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, pool.NotBondedTokens) + require.Equal(t, app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, pool.BondedTokens) } func TestQueryValidators(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 10000) - params := keeper.GetParams(ctx) + cdc, app, ctx := createTestInput() + params := app.StakingKeeper.GetParams(ctx) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 500, sdk.TokensFromConsensusPower(10000)) // Create Validators amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)} - status := []sdk.BondStatus{sdk.Bonded, sdk.Unbonded, sdk.Unbonding} + status := []types.BondStatus{types.Bonded, types.Unbonded, types.Unbonding} var validators [3]types.Validator for i, amt := range amts { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) validators[i], _ = validators[i].AddTokensFromDel(amt) validators[i] = validators[i].UpdateStatus(status[i]) } - keeper.SetValidator(ctx, validators[0]) - keeper.SetValidator(ctx, validators[1]) - keeper.SetValidator(ctx, validators[2]) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidator(ctx, validators[1]) + app.StakingKeeper.SetValidator(ctx, validators[2]) // Query Validators - queriedValidators := keeper.GetValidators(ctx, params.MaxValidators) + queriedValidators := app.StakingKeeper.GetValidators(ctx, params.MaxValidators) + require.Len(t, queriedValidators, 3) for i, s := range status { queryValsParams := types.NewQueryValidatorsParams(1, int(params.MaxValidators), s.String()) @@ -162,7 +170,7 @@ func TestQueryValidators(t *testing.T) { Data: bz, } - res, err := queryValidators(ctx, req, keeper) + res, err := querier(ctx, []string{types.QueryValidators}, req) require.NoError(t, err) var validatorsResp []types.Validator @@ -170,48 +178,58 @@ func TestQueryValidators(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(validatorsResp)) - require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress) - + require.Equal(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress) } // Query each validator - queryParams := types.NewQueryValidatorParams(addrVal1) - bz, err := cdc.MarshalJSON(queryParams) - require.NoError(t, err) + for _, validator := range validators { + queryParams := types.NewQueryValidatorParams(validator.GetOperator(), 0, 0) + bz, err := cdc.MarshalJSON(queryParams) + require.NoError(t, err) - query := abci.RequestQuery{ - Path: "/custom/staking/validator", - Data: bz, - } - res, err := queryValidator(ctx, query, keeper) - require.NoError(t, err) + query := abci.RequestQuery{ + Path: "/custom/staking/validator", + Data: bz, + } + res, err := querier(ctx, []string{types.QueryValidator}, query) + require.NoError(t, err) - var validator types.Validator - err = cdc.UnmarshalJSON(res, &validator) - require.NoError(t, err) + var queriedValidator types.Validator + err = cdc.UnmarshalJSON(res, &queriedValidator) + require.NoError(t, err) - require.Equal(t, queriedValidators[0], validator) + require.True(t, validator.Equal(&queriedValidator)) + } } func TestQueryDelegation(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 10000) - params := keeper.GetParams(ctx) + cdc, app, ctx := createTestInput() + params := app.StakingKeeper.GetParams(ctx) + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 2, sdk.TokensFromConsensusPower(10000)) + addrAcc1, addrAcc2 := addrs[0], addrs[1] + addrVal1, addrVal2 := sdk.ValAddress(addrAcc1), sdk.ValAddress(addrAcc2) + + pubKeys := simapp.CreateTestPubKeys(2) + pk1, pk2 := pubKeys[0], pubKeys[1] // Create Validators and Delegation - val1 := types.NewValidator(addrVal1, pk1, types.Description{}) - keeper.SetValidator(ctx, val1) - keeper.SetValidatorByPowerIndex(ctx, val1) + val1 := teststaking.NewValidator(t, addrVal1, pk1) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, val1) - val2 := types.NewValidator(addrVal2, pk2, types.Description{}) - keeper.SetValidator(ctx, val2) - keeper.SetValidatorByPowerIndex(ctx, val2) + val2 := teststaking.NewValidator(t, addrVal2, pk2) + app.StakingKeeper.SetValidator(ctx, val2) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, val2) delTokens := sdk.TokensFromConsensusPower(20) - keeper.Delegate(ctx, addrAcc2, delTokens, sdk.Unbonded, val1, true) + _, err := app.StakingKeeper.Delegate(ctx, addrAcc2, delTokens, types.Unbonded, val1, true) + require.NoError(t, err) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) // Query Delegator bonded validators queryParams := types.NewQueryDelegatorParams(addrAcc2) @@ -223,12 +241,12 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators) + delValidators := app.StakingKeeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators) - res, err := queryDelegatorValidators(ctx, query, keeper) + res, err := querier(ctx, []string{types.QueryDelegatorValidators}, query) require.NoError(t, err) - var validatorsResp []types.Validator + var validatorsResp types.Validators errRes = cdc.UnmarshalJSON(res, &validatorsResp) require.NoError(t, errRes) @@ -238,11 +256,11 @@ func TestQueryDelegation(t *testing.T) { // error unknown request query.Data = bz[:len(bz)-1] - _, err = queryDelegatorValidators(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryDelegatorValidators}, query) require.Error(t, err) // Query bonded validator - queryBondParams := types.NewQueryBondsParams(addrAcc2, addrVal1) + queryBondParams := types.QueryDelegatorValidatorRequest{DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal1.String()} bz, errRes = cdc.MarshalJSON(queryBondParams) require.NoError(t, errRes) @@ -251,19 +269,18 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - res, err = queryDelegatorValidator(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegatorValidator}, query) require.NoError(t, err) var validator types.Validator errRes = cdc.UnmarshalJSON(res, &validator) require.NoError(t, errRes) - - require.Equal(t, delValidators[0], validator) + require.True(t, validator.Equal(&delValidators[0])) // error unknown request query.Data = bz[:len(bz)-1] - _, err = queryDelegatorValidator(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryDelegatorValidator}, query) require.Error(t, err) // Query delegation @@ -273,18 +290,18 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - delegation, found := keeper.GetDelegation(ctx, addrAcc2, addrVal1) + delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc2, addrVal1) require.True(t, found) - res, err = queryDelegation(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegation}, query) require.NoError(t, err) var delegationRes types.DelegationResponse errRes = cdc.UnmarshalJSON(res, &delegationRes) require.NoError(t, errRes) - require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress) - require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress) + require.Equal(t, delegation.ValidatorAddress, delegationRes.Delegation.ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationRes.Delegation.DelegatorAddress) require.Equal(t, sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), delegationRes.Balance) // Query Delegator Delegations @@ -293,26 +310,25 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - res, err = queryDelegatorDelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegatorDelegations}, query) require.NoError(t, err) var delegatorDelegations types.DelegationResponses errRes = cdc.UnmarshalJSON(res, &delegatorDelegations) require.NoError(t, errRes) require.Len(t, delegatorDelegations, 1) - require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress) - require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress) + require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].Delegation.ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].Delegation.DelegatorAddress) require.Equal(t, sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), delegatorDelegations[0].Balance) // error unknown request query.Data = bz[:len(bz)-1] - _, err = queryDelegation(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryDelegation}, query) require.Error(t, err) // Query validator delegations - - bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1)) + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1, 1, 100)) require.NoError(t, errRes) query = abci.RequestQuery{ @@ -320,23 +336,23 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - res, err = queryValidatorDelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryValidatorDelegations}, query) require.NoError(t, err) var delegationsRes types.DelegationResponses errRes = cdc.UnmarshalJSON(res, &delegationsRes) require.NoError(t, errRes) require.Len(t, delegatorDelegations, 1) - require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress) - require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress) + require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].Delegation.ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].Delegation.DelegatorAddress) require.Equal(t, sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), delegationsRes[0].Balance) - // Query unbonging delegation + // Query unbonding delegation unbondingTokens := sdk.TokensFromConsensusPower(10) - _, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddress, unbondingTokens.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, addrAcc2, val1.GetOperator(), unbondingTokens.ToDec()) require.NoError(t, err) - queryBondParams = types.NewQueryBondsParams(addrAcc2, addrVal1) + queryBondParams = types.QueryDelegatorValidatorRequest{DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal1.String()} bz, errRes = cdc.MarshalJSON(queryBondParams) require.NoError(t, errRes) @@ -345,10 +361,10 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - unbond, found := keeper.GetUnbondingDelegation(ctx, addrAcc2, addrVal1) + unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc2, addrVal1) require.True(t, found) - res, err = queryUnbondingDelegation(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryUnbondingDelegation}, query) require.NoError(t, err) var unbondRes types.UnbondingDelegation @@ -360,17 +376,17 @@ func TestQueryDelegation(t *testing.T) { // error unknown request query.Data = bz[:len(bz)-1] - _, err = queryUnbondingDelegation(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryUnbondingDelegation}, query) require.Error(t, err) - // Query Delegator Delegations + // Query Delegator Unbonding Delegations query = abci.RequestQuery{ Path: "/custom/staking/delegatorUnbondingDelegations", Data: bz, } - res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegatorUnbondingDelegations}, query) require.NoError(t, err) var delegatorUbds []types.UnbondingDelegation @@ -381,18 +397,18 @@ func TestQueryDelegation(t *testing.T) { // error unknown request query.Data = bz[:len(bz)-1] - _, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryDelegatorUnbondingDelegations}, query) require.Error(t, err) // Query redelegation redelegationTokens := sdk.TokensFromConsensusPower(10) - _, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddress, - val2.OperatorAddress, redelegationTokens.ToDec()) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), + val2.GetOperator(), redelegationTokens.ToDec()) require.NoError(t, err) - redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) + redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator()) require.True(t, found) - bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.OperatorAddress, val2.OperatorAddress)) + bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.GetOperator(), val2.GetOperator())) require.NoError(t, errRes) query = abci.RequestQuery{ @@ -400,38 +416,152 @@ func TestQueryDelegation(t *testing.T) { Data: bz, } - res, err = queryRedelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryRedelegations}, query) require.NoError(t, err) var redelRes types.RedelegationResponses errRes = cdc.UnmarshalJSON(res, &redelRes) require.NoError(t, errRes) require.Len(t, redelRes, 1) - require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) - require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) - require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Equal(t, redel.DelegatorAddress, redelRes[0].Redelegation.DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].Redelegation.ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].Redelegation.ValidatorDstAddress) require.Len(t, redel.Entries, len(redelRes[0].Entries)) } +func TestQueryValidatorDelegations_Pagination(t *testing.T) { + cases := []struct { + page int + limit int + expectedResults int + }{ + { + page: 1, + limit: 75, + expectedResults: 75, + }, + { + page: 2, + limit: 75, + expectedResults: 25, + }, + { + page: 1, + limit: 100, + expectedResults: 100, + }, + } + + cdc, app, ctx := createTestInput() + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 100, sdk.TokensFromConsensusPower(10000)) + pubKeys := simapp.CreateTestPubKeys(1) + + valAddress := sdk.ValAddress(addrs[0]) + + val1 := teststaking.NewValidator(t, valAddress, pubKeys[0]) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, val1) + + // Create Validators and Delegation + for _, addr := range addrs { + validator, found := app.StakingKeeper.GetValidator(ctx, valAddress) + if !found { + t.Error("expected validator not found") + } + + delTokens := sdk.TokensFromConsensusPower(20) + _, err := app.StakingKeeper.Delegate(ctx, addr, delTokens, types.Unbonded, validator, true) + require.NoError(t, err) + } + + // apply TM updates + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + + for _, c := range cases { + // Query Delegator bonded validators + queryParams := types.NewQueryDelegatorParams(addrs[0]) + bz, errRes := cdc.MarshalJSON(queryParams) + require.NoError(t, errRes) + + // Query valAddress delegations + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(valAddress, c.page, c.limit)) + require.NoError(t, errRes) + + query := abci.RequestQuery{ + Path: "custom/staking/validatorDelegations", + Data: bz, + } + + res, err := querier(ctx, []string{types.QueryValidatorDelegations}, query) + require.NoError(t, err) + + var delegationsRes types.DelegationResponses + errRes = cdc.UnmarshalJSON(res, &delegationsRes) + require.NoError(t, errRes) + require.Len(t, delegationsRes, c.expectedResults) + } + + // Undelegate + for _, addr := range addrs { + delTokens := sdk.TokensFromConsensusPower(20) + _, err := app.StakingKeeper.Undelegate(ctx, addr, val1.GetOperator(), delTokens.ToDec()) + require.NoError(t, err) + } + + // apply TM updates + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + + for _, c := range cases { + // Query Unbonding delegations with pagination. + queryParams := types.NewQueryDelegatorParams(addrs[0]) + bz, errRes := cdc.MarshalJSON(queryParams) + require.NoError(t, errRes) + + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(valAddress, c.page, c.limit)) + require.NoError(t, errRes) + query := abci.RequestQuery{ + Data: bz, + } + + unbondingDelegations := types.UnbondingDelegations{} + res, err := querier(ctx, []string{types.QueryValidatorUnbondingDelegations}, query) + require.NoError(t, err) + + errRes = cdc.UnmarshalJSON(res, &unbondingDelegations) + require.NoError(t, errRes) + require.Len(t, unbondingDelegations, c.expectedResults) + } +} + func TestQueryRedelegations(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + cdc, app, ctx := createTestInput() + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 2, sdk.TokensFromConsensusPower(10000)) + addrAcc1, addrAcc2 := addrs[0], addrs[1] + addrVal1, addrVal2 := sdk.ValAddress(addrAcc1), sdk.ValAddress(addrAcc2) // Create Validators and Delegation - val1 := types.NewValidator(addrVal1, pk1, types.Description{}) - val2 := types.NewValidator(addrVal2, pk2, types.Description{}) - keeper.SetValidator(ctx, val1) - keeper.SetValidator(ctx, val2) + val1 := teststaking.NewValidator(t, addrVal1, PKs[0]) + val2 := teststaking.NewValidator(t, addrVal2, PKs[1]) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidator(ctx, val2) delAmount := sdk.TokensFromConsensusPower(100) - keeper.Delegate(ctx, addrAcc2, delAmount, sdk.Unbonded, val1, true) - _ = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + _, err := app.StakingKeeper.Delegate(ctx, addrAcc2, delAmount, types.Unbonded, val1, true) + require.NoError(t, err) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) rdAmount := sdk.TokensFromConsensusPower(20) - keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), rdAmount.ToDec()) - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + _, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), rdAmount.ToDec()) + require.NoError(t, err) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) - redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) + redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator()) require.True(t, found) // delegator redelegations @@ -444,20 +574,20 @@ func TestQueryRedelegations(t *testing.T) { Data: bz, } - res, err := queryRedelegations(ctx, query, keeper) + res, err := querier(ctx, []string{types.QueryRedelegations}, query) require.NoError(t, err) var redelRes types.RedelegationResponses errRes = cdc.UnmarshalJSON(res, &redelRes) require.NoError(t, errRes) require.Len(t, redelRes, 1) - require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) - require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) - require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Equal(t, redel.DelegatorAddress, redelRes[0].Redelegation.DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].Redelegation.ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].Redelegation.ValidatorDstAddress) require.Len(t, redel.Entries, len(redelRes[0].Entries)) // validator redelegations - queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator()) + queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator(), 0, 0) bz, errRes = cdc.MarshalJSON(queryValidatorParams) require.NoError(t, errRes) @@ -466,71 +596,76 @@ func TestQueryRedelegations(t *testing.T) { Data: bz, } - res, err = queryRedelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryRedelegations}, query) require.NoError(t, err) errRes = cdc.UnmarshalJSON(res, &redelRes) require.NoError(t, errRes) require.Len(t, redelRes, 1) - require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) - require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) - require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Equal(t, redel.DelegatorAddress, redelRes[0].Redelegation.DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].Redelegation.ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].Redelegation.ValidatorDstAddress) require.Len(t, redel.Entries, len(redelRes[0].Entries)) } func TestQueryUnbondingDelegation(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + cdc, app, ctx := createTestInput() + legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino()) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 2, sdk.TokensFromConsensusPower(10000)) + addrAcc1, addrAcc2 := addrs[0], addrs[1] + addrVal1 := sdk.ValAddress(addrAcc1) // Create Validators and Delegation - val1 := types.NewValidator(addrVal1, pk1, types.Description{}) - keeper.SetValidator(ctx, val1) + val1 := teststaking.NewValidator(t, addrVal1, PKs[0]) + app.StakingKeeper.SetValidator(ctx, val1) // delegate delAmount := sdk.TokensFromConsensusPower(100) - _, err := keeper.Delegate(ctx, addrAcc1, delAmount, sdk.Unbonded, val1, true) + _, err := app.StakingKeeper.Delegate(ctx, addrAcc1, delAmount, types.Unbonded, val1, true) require.NoError(t, err) - _ = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) // undelegate undelAmount := sdk.TokensFromConsensusPower(20) - _, err = keeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), undelAmount.ToDec()) + _, err = app.StakingKeeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), undelAmount.ToDec()) require.NoError(t, err) - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) - _, found := keeper.GetUnbondingDelegation(ctx, addrAcc1, val1.OperatorAddress) + _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc1, val1.GetOperator()) require.True(t, found) // // found: query unbonding delegation by delegator and validator // - queryValidatorParams := types.NewQueryBondsParams(addrAcc1, val1.GetOperator()) + queryValidatorParams := types.QueryDelegatorValidatorRequest{DelegatorAddr: addrAcc1.String(), ValidatorAddr: val1.GetOperator().String()} bz, errRes := cdc.MarshalJSON(queryValidatorParams) require.NoError(t, errRes) query := abci.RequestQuery{ Path: "/custom/staking/unbondingDelegation", Data: bz, } - res, err := queryUnbondingDelegation(ctx, query, keeper) + res, err := querier(ctx, []string{types.QueryUnbondingDelegation}, query) require.NoError(t, err) require.NotNil(t, res) var ubDel types.UnbondingDelegation require.NoError(t, cdc.UnmarshalJSON(res, &ubDel)) - require.Equal(t, addrAcc1, ubDel.DelegatorAddress) + require.Equal(t, addrAcc1.String(), ubDel.DelegatorAddress) require.Equal(t, val1.OperatorAddress, ubDel.ValidatorAddress) require.Equal(t, 1, len(ubDel.Entries)) // // not found: query unbonding delegation by delegator and validator // - queryValidatorParams = types.NewQueryBondsParams(addrAcc2, val1.GetOperator()) + queryValidatorParams = types.QueryDelegatorValidatorRequest{DelegatorAddr: addrAcc2.String(), ValidatorAddr: val1.GetOperator().String()} bz, errRes = cdc.MarshalJSON(queryValidatorParams) require.NoError(t, errRes) query = abci.RequestQuery{ Path: "/custom/staking/unbondingDelegation", Data: bz, } - _, err = queryUnbondingDelegation(ctx, query, keeper) + _, err = querier(ctx, []string{types.QueryUnbondingDelegation}, query) require.Error(t, err) // @@ -543,13 +678,13 @@ func TestQueryUnbondingDelegation(t *testing.T) { Path: "/custom/staking/delegatorUnbondingDelegations", Data: bz, } - res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegatorUnbondingDelegations}, query) require.NoError(t, err) require.NotNil(t, res) var ubDels []types.UnbondingDelegation require.NoError(t, cdc.UnmarshalJSON(res, &ubDels)) require.Equal(t, 1, len(ubDels)) - require.Equal(t, addrAcc1, ubDels[0].DelegatorAddress) + require.Equal(t, addrAcc1.String(), ubDels[0].DelegatorAddress) require.Equal(t, val1.OperatorAddress, ubDels[0].ValidatorAddress) // @@ -562,7 +697,7 @@ func TestQueryUnbondingDelegation(t *testing.T) { Path: "/custom/staking/delegatorUnbondingDelegations", Data: bz, } - res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryDelegatorUnbondingDelegations}, query) require.NoError(t, err) require.NotNil(t, res) require.NoError(t, cdc.UnmarshalJSON(res, &ubDels)) @@ -570,39 +705,44 @@ func TestQueryUnbondingDelegation(t *testing.T) { } func TestQueryHistoricalInfo(t *testing.T) { - cdc := codec.New() - ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + cdc, app, ctx := createTestInput() + legacyQuerierCdc := codec.NewAminoCodec(cdc) + querier := keeper.NewQuerier(app.StakingKeeper, legacyQuerierCdc.LegacyAmino) + + addrs := simapp.AddTestAddrs(app, ctx, 2, sdk.TokensFromConsensusPower(10000)) + addrAcc1, addrAcc2 := addrs[0], addrs[1] + addrVal1, addrVal2 := sdk.ValAddress(addrAcc1), sdk.ValAddress(addrAcc2) // Create Validators and Delegation - val1 := types.NewValidator(addrVal1, pk1, types.Description{}) - val2 := types.NewValidator(addrVal2, pk2, types.Description{}) + val1 := teststaking.NewValidator(t, addrVal1, PKs[0]) + val2 := teststaking.NewValidator(t, addrVal2, PKs[1]) vals := []types.Validator{val1, val2} - keeper.SetValidator(ctx, val1) - keeper.SetValidator(ctx, val2) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidator(ctx, val2) - header := abci.Header{ + header := tmproto.Header{ ChainID: "HelloChain", Height: 5, } hi := types.NewHistoricalInfo(header, vals) - keeper.SetHistoricalInfo(ctx, 5, hi) + app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi) - queryHistoricalParams := types.NewQueryHistoricalInfoParams(4) + queryHistoricalParams := types.QueryHistoricalInfoRequest{Height: 4} bz, errRes := cdc.MarshalJSON(queryHistoricalParams) require.NoError(t, errRes) query := abci.RequestQuery{ Path: "/custom/staking/historicalInfo", Data: bz, } - res, err := queryHistoricalInfo(ctx, query, keeper) + res, err := querier(ctx, []string{types.QueryHistoricalInfo}, query) require.Error(t, err, "Invalid query passed") require.Nil(t, res, "Invalid query returned non-nil result") - queryHistoricalParams = types.NewQueryHistoricalInfoParams(5) + queryHistoricalParams = types.QueryHistoricalInfoRequest{Height: 5} bz, errRes = cdc.MarshalJSON(queryHistoricalParams) require.NoError(t, errRes) query.Data = bz - res, err = queryHistoricalInfo(ctx, query, keeper) + res, err = querier(ctx, []string{types.QueryHistoricalInfo}, query) require.NoError(t, err, "Valid query passed") require.NotNil(t, res, "Valid query returned nil result") diff --git a/x/staking/keeper/query_utils.go b/x/staking/keeper/query_utils.go index 336bb842ab9e..a6f323092ae6 100644 --- a/x/staking/keeper/query_utils.go +++ b/x/staking/keeper/query_utils.go @@ -6,12 +6,14 @@ import ( ) // Return all validators that a delegator is bonded to. If maxRetrieve is supplied, the respective amount will be returned. -func (k Keeper) GetDelegatorValidators(ctx sdk.Context, delegatorAddr sdk.AccAddress, - maxRetrieve uint16) (validators []types.Validator) { - validators = make([]types.Validator, maxRetrieve) +func (k Keeper) GetDelegatorValidators( + ctx sdk.Context, delegatorAddr sdk.AccAddress, maxRetrieve uint32, +) types.Validators { + validators := make([]types.Validator, maxRetrieve) store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetDelegationsKey(delegatorAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest defer iterator.Close() @@ -19,30 +21,33 @@ func (k Keeper) GetDelegatorValidators(ctx sdk.Context, delegatorAddr sdk.AccAdd for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) - validator, found := k.GetValidator(ctx, delegation.ValidatorAddress) + validator, found := k.GetValidator(ctx, delegation.GetValidatorAddr()) if !found { panic(types.ErrNoValidatorFound) } + validators[i] = validator i++ } + return validators[:i] // trim } // return a validator that a delegator is bonded to -func (k Keeper) GetDelegatorValidator(ctx sdk.Context, delegatorAddr sdk.AccAddress, - validatorAddr sdk.ValAddress) (validator types.Validator, err error) { - +func (k Keeper) GetDelegatorValidator( + ctx sdk.Context, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, +) (validator types.Validator, err error) { delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) if !found { return validator, types.ErrNoDelegation } - validator, found = k.GetValidator(ctx, delegation.ValidatorAddress) + validator, found = k.GetValidator(ctx, delegation.GetValidatorAddr()) if !found { panic(types.ErrNoValidatorFound) } - return + + return validator, nil } //_____________________________________________________________________________________ @@ -53,10 +58,12 @@ func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAdd store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest defer iterator.Close() i := 0 + for ; iterator.Valid(); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) delegations = append(delegations, delegation) @@ -72,6 +79,7 @@ func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAdd store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest defer iterator.Close() @@ -85,27 +93,40 @@ func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAdd } // return all redelegations for a delegator -func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress, - srcValAddress, dstValAddress sdk.ValAddress) ( - redelegations []types.Redelegation) { - +func (k Keeper) GetAllRedelegations( + ctx sdk.Context, delegator sdk.AccAddress, srcValAddress, dstValAddress sdk.ValAddress, +) []types.Redelegation { store := ctx.KVStore(k.storeKey) delegatorPrefixKey := types.GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest defer iterator.Close() srcValFilter := !(srcValAddress.Empty()) dstValFilter := !(dstValAddress.Empty()) + redelegations := []types.Redelegation{} + for ; iterator.Valid(); iterator.Next() { redelegation := types.MustUnmarshalRED(k.cdc, iterator.Value()) - if srcValFilter && !(srcValAddress.Equals(redelegation.ValidatorSrcAddress)) { + valSrcAddr, err := sdk.ValAddressFromBech32(redelegation.ValidatorSrcAddress) + if err != nil { + panic(err) + } + valDstAddr, err := sdk.ValAddressFromBech32(redelegation.ValidatorDstAddress) + if err != nil { + panic(err) + } + if srcValFilter && !(srcValAddress.Equals(valSrcAddr)) { continue } - if dstValFilter && !(dstValAddress.Equals(redelegation.ValidatorDstAddress)) { + + if dstValFilter && !(dstValAddress.Equals(valDstAddr)) { continue } + redelegations = append(redelegations, redelegation) } + return redelegations } diff --git a/x/staking/keeper/slash.go b/x/staking/keeper/slash.go index 8841d3e8e4bd..a36f6f2e6e20 100644 --- a/x/staking/keeper/slash.go +++ b/x/staking/keeper/slash.go @@ -41,9 +41,10 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh // NOTE: Correctness dependent on invariant that unbonding delegations / redelegations must also have been completely // slashed in this case - which we don't explicitly check, but should be true. // Log the slash attempt for future reference (maybe we should tag it too) - logger.Error(fmt.Sprintf( - "WARNING: Ignored attempt to slash a nonexistent validator with address %s, we recommend you investigate immediately", - consAddr)) + logger.Error( + "WARNING: ignored attempt to slash a nonexistent validator; we recommend you investigate immediately", + "validator", consAddr.String(), + ) return } @@ -64,38 +65,39 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh switch { case infractionHeight > ctx.BlockHeight(): - // Can't slash infractions in the future panic(fmt.Sprintf( "impossible attempt to slash future infraction at height %d but we are at height %d", infractionHeight, ctx.BlockHeight())) case infractionHeight == ctx.BlockHeight(): - - // Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations - logger.Info(fmt.Sprintf( - "slashing at current height %d, not scanning unbonding delegations & redelegations", - infractionHeight)) + // Special-case slash at current height for efficiency - we don't need to + // look through unbonding delegations or redelegations. + logger.Info( + "slashing at current height; not scanning unbonding delegations & redelegations", + "height", infractionHeight, + ) case infractionHeight < ctx.BlockHeight(): - // Iterate through unbonding delegations from slashed validator unbondingDelegations := k.GetUnbondingDelegationsFromValidator(ctx, operatorAddress) for _, unbondingDelegation := range unbondingDelegations { - amountSlashed := k.slashUnbondingDelegation(ctx, unbondingDelegation, infractionHeight, slashFactor) + amountSlashed := k.SlashUnbondingDelegation(ctx, unbondingDelegation, infractionHeight, slashFactor) if amountSlashed.IsZero() { continue } + remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed) } // Iterate through redelegations from slashed source validator redelegations := k.GetRedelegationsFromSrcValidator(ctx, operatorAddress) for _, redelegation := range redelegations { - amountSlashed := k.slashRedelegation(ctx, validator, redelegation, infractionHeight, slashFactor) + amountSlashed := k.SlashRedelegation(ctx, validator, redelegation, infractionHeight, slashFactor) if amountSlashed.IsZero() { continue } + remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed) } } @@ -120,11 +122,11 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn) switch validator.GetStatus() { - case sdk.Bonded: + case types.Bonded: if err := k.burnBondedTokens(ctx, tokensToBurn); err != nil { panic(err) } - case sdk.Unbonding, sdk.Unbonded: + case types.Unbonding, types.Unbonded: if err := k.burnNotBondedTokens(ctx, tokensToBurn); err != nil { panic(err) } @@ -132,11 +134,12 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh panic("invalid validator status") } - // Log that a slash occurred! - logger.Info(fmt.Sprintf( - "validator %s slashed by slash factor of %s; burned %v tokens", - validator.GetOperator(), slashFactor.String(), tokensToBurn)) - + logger.Info( + "validator slashed by slash factor", + "validator", validator.GetOperator().String(), + "slash_factor", slashFactor.String(), + "burned", tokensToBurn, + ) } // jail a validator @@ -144,7 +147,7 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { validator := k.mustGetValidatorByConsAddr(ctx, consAddr) k.jailValidator(ctx, validator) logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("validator %s jailed", consAddr)) + logger.Info("validator jailed", "validator", consAddr) } // unjail a validator @@ -152,7 +155,7 @@ func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { validator := k.mustGetValidatorByConsAddr(ctx, consAddr) k.unjailValidator(ctx, validator) logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("validator %s unjailed", consAddr)) + logger.Info("validator un-jailed", "validator", consAddr) } // slash an unbonding delegation and update the pool @@ -160,16 +163,14 @@ func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { // the unbonding delegation had enough stake to slash // (the amount actually slashed may be less if there's // insufficient stake remaining) -func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, +func (k Keeper) SlashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) { - now := ctx.BlockHeader().Time totalSlashAmount = sdk.ZeroInt() burnedAmount := sdk.ZeroInt() // perform slashing on all entries within the unbonding delegation for i, entry := range unbondingDelegation.Entries { - // If unbonding started before this height, stake didn't contribute to infraction if entry.CreationHeight < infractionHeight { continue @@ -215,16 +216,14 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty // (the amount actually slashed may be less if there's // insufficient stake remaining) // NOTE this is only slashing for prior infractions from the source validator -func (k Keeper) slashRedelegation(ctx sdk.Context, srcValidator types.Validator, redelegation types.Redelegation, +func (k Keeper) SlashRedelegation(ctx sdk.Context, srcValidator types.Validator, redelegation types.Redelegation, infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) { - now := ctx.BlockHeader().Time totalSlashAmount = sdk.ZeroInt() bondedBurnedAmount, notBondedBurnedAmount := sdk.ZeroInt(), sdk.ZeroInt() // perform slashing on all entries within the redelegation for _, entry := range redelegation.Entries { - // If redelegation started before this height, stake didn't contribute to infraction if entry.CreationHeight < infractionHeight { continue @@ -245,21 +244,33 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, srcValidator types.Validator, if sharesToUnbond.IsZero() { continue } - delegation, found := k.GetDelegation(ctx, redelegation.DelegatorAddress, redelegation.ValidatorDstAddress) + + valDstAddr, err := sdk.ValAddressFromBech32(redelegation.ValidatorDstAddress) + if err != nil { + panic(err) + } + + delegatorAddress, err := sdk.AccAddressFromBech32(redelegation.DelegatorAddress) + if err != nil { + panic(err) + } + + delegation, found := k.GetDelegation(ctx, delegatorAddress, valDstAddr) if !found { // If deleted, delegation has zero shares, and we can't unbond any more continue } + if sharesToUnbond.GT(delegation.Shares) { sharesToUnbond = delegation.Shares } - tokensToBurn, err := k.unbond(ctx, redelegation.DelegatorAddress, redelegation.ValidatorDstAddress, sharesToUnbond) + tokensToBurn, err := k.Unbond(ctx, delegatorAddress, valDstAddr, sharesToUnbond) if err != nil { panic(fmt.Errorf("error unbonding delegator: %v", err)) } - dstValidator, found := k.GetValidator(ctx, redelegation.ValidatorDstAddress) + dstValidator, found := k.GetValidator(ctx, valDstAddr) if !found { panic("destination validator not found") } diff --git a/x/staking/keeper/slash_test.go b/x/staking/keeper/slash_test.go index cc4d4651731a..0760533ab9f0 100644 --- a/x/staking/keeper/slash_test.go +++ b/x/staking/keeper/slash_test.go @@ -1,75 +1,84 @@ -package keeper +package keeper_test import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - abci "github.com/tendermint/tendermint/abci/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" ) -// TODO integrate with test_common.go helper (CreateTestInput) -// setup helper function - creates two validators -func setupHelper(t *testing.T, power int64) (sdk.Context, Keeper, types.Params) { +// bootstrapSlashTest creates 3 validators and bootstrap the app. +func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { + _, app, ctx := createTestInput() + + addrDels, addrVals := generateAddresses(app, ctx, 100) - // setup - ctx, _, keeper, _ := CreateTestInput(t, false, power) - params := keeper.GetParams(ctx) - numVals := int64(3) amt := sdk.TokensFromConsensusPower(power) - bondedCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), amt.MulRaw(numVals))) + 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) + + numVals := int64(3) + bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals))) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) - bondedPool := keeper.GetBondedPool(ctx) - err := bondedPool.SetCoins(bondedCoins) + err = app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), bondedCoins) require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) - // add numVals validators + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.BankKeeper.SetSupply(ctx, banktypes.NewSupply(totalSupply)) + for i := int64(0); i < numVals; i++ { - validator := types.NewValidator(addrVals[i], PKs[i], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[i], PKs[i]) validator, _ = validator.AddTokensFromDel(amt) - validator = TestingUpdateValidator(keeper, ctx, validator, true) - keeper.SetValidatorByConsAddr(ctx, validator) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) } - return ctx, keeper, params + return app, ctx, addrDels, addrVals } -//_________________________________________________________________________________ - // tests Jail, Unjail func TestRevocation(t *testing.T) { + app, ctx, _, addrVals := bootstrapSlashTest(t, 5) - // setup - ctx, keeper, _ := setupHelper(t, 10) - addr := addrVals[0] consAddr := sdk.ConsAddress(PKs[0].Address()) // initial state - val, found := keeper.GetValidator(ctx, addr) + val, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.False(t, val.IsJailed()) // test jail - keeper.Jail(ctx, consAddr) - val, found = keeper.GetValidator(ctx, addr) + app.StakingKeeper.Jail(ctx, consAddr) + val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.True(t, val.IsJailed()) // test unjail - keeper.Unjail(ctx, consAddr) - val, found = keeper.GetValidator(ctx, addr) + app.StakingKeeper.Unjail(ctx, consAddr) + val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.False(t, val.IsJailed()) } // tests slashUnbondingDelegation func TestSlashUnbondingDelegation(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation with expiration timestamp (beyond which the @@ -77,25 +86,26 @@ func TestSlashUnbondingDelegation(t *testing.T) { ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, time.Unix(5, 0), sdk.NewInt(10)) - keeper.SetUnbondingDelegation(ctx, ubd) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) // unbonding started prior to the infraction height, stakw didn't contribute - slashAmount := keeper.slashUnbondingDelegation(ctx, ubd, 1, fraction) - require.Equal(t, int64(0), slashAmount.Int64()) + slashAmount := app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 1, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) - keeper.SetUnbondingDelegation(ctx, ubd) - slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) - require.Equal(t, int64(0), slashAmount.Int64()) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) // test valid slash, before expiration timestamp and to which stake contributed - oldUnbondedPool := keeper.GetNotBondedPool(ctx) - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) - keeper.SetUnbondingDelegation(ctx, ubd) - slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) - require.Equal(t, int64(5), slashAmount.Int64()) - ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + oldUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(5))) + ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) @@ -104,180 +114,192 @@ func TestSlashUnbondingDelegation(t *testing.T) { // balance decreased require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance) - newUnbondedPool := keeper.GetNotBondedPool(ctx) - diffTokens := oldUnbondedPool.GetCoins().Sub(newUnbondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) - require.Equal(t, int64(5), diffTokens.Int64()) + newUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + diffTokens := oldUnbondedPoolBalances.Sub(newUnbondedPoolBalances) + require.True(t, diffTokens.AmountOf(app.StakingKeeper.BondDenom(ctx)).Equal(sdk.NewInt(5))) } // tests slashRedelegation func TestSlashRedelegation(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) fraction := sdk.NewDecWithPrec(5, 1) // add bonded tokens to pool for (re)delegations - startCoins := sdk.NewCoins(sdk.NewInt64Coin(keeper.BondDenom(ctx), 15)) - bondedPool := keeper.GetBondedPool(ctx) - err := bondedPool.SetCoins(bondedPool.GetCoins().Add(startCoins...)) - require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + startCoins := sdk.NewCoins(sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 15)) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + require.NoError(t, app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), balances.Add(startCoins...))) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // set a redelegation with an expiration timestamp beyond which the // redelegation shouldn't be slashed rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, time.Unix(5, 0), sdk.NewInt(10), sdk.NewDec(10)) - keeper.SetRedelegation(ctx, rd) + app.StakingKeeper.SetRedelegation(ctx, rd) // set the associated delegation del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(10)) - keeper.SetDelegation(ctx, del) + app.StakingKeeper.SetDelegation(ctx, del) // started redelegating prior to the current height, stake didn't contribute to infraction - validator, found := keeper.GetValidator(ctx, addrVals[1]) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[1]) require.True(t, found) - slashAmount := keeper.slashRedelegation(ctx, validator, rd, 1, fraction) - require.Equal(t, int64(0), slashAmount.Int64()) + slashAmount := app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 1, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) // after the expiration time, no longer eligible for slashing - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) - keeper.SetRedelegation(ctx, rd) - validator, found = keeper.GetValidator(ctx, addrVals[1]) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)}) + app.StakingKeeper.SetRedelegation(ctx, rd) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) require.True(t, found) - slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction) - require.Equal(t, int64(0), slashAmount.Int64()) + slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(0))) + + balances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) // test valid slash, before expiration timestamp and to which stake contributed - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) - keeper.SetRedelegation(ctx, rd) - validator, found = keeper.GetValidator(ctx, addrVals[1]) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)}) + app.StakingKeeper.SetRedelegation(ctx, rd) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) require.True(t, found) - slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction) - require.Equal(t, int64(5), slashAmount.Int64()) - rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction) + require.True(t, slashAmount.Equal(sdk.NewInt(5))) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rd.Entries, 1) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) // initialbalance unchanged require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance) // shares decreased - del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1]) + del, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[1]) require.True(t, found) require.Equal(t, int64(5), del.Shares.RoundInt64()) // pool bonded tokens should decrease - burnedCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), slashAmount)) - newBondedPool := keeper.GetBondedPool(ctx) - require.Equal(t, bondedPool.GetCoins().Sub(burnedCoins), newBondedPool.GetCoins()) + burnedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), slashAmount)) + require.Equal(t, balances.Sub(burnedCoins), app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())) } // tests Slash at a future height (must panic) func TestSlashAtFutureHeight(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, _, _ := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - require.Panics(t, func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) }) + require.Panics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 1, 10, fraction) }) } // test slash at a negative height // this just represents pre-genesis and should have the same effect as slashing at height 0 func TestSlashAtNegativeHeight(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, _, _ := bootstrapSlashTest(t, 10) consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - oldBondedPool := keeper.GetBondedPool(ctx) - validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, consAddr, -2, 10, fraction) + app.StakingKeeper.Slash(ctx, consAddr, -2, 10, fraction) // read updated state - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - newBondedPool := keeper.GetBondedPool(ctx) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator = keeper.mustGetValidator(ctx, validator.OperatorAddress) + validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + require.True(t, found) // power decreased require.Equal(t, int64(5), validator.GetConsensusPower()) + // pool bonded shares decreased - diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) - require.Equal(t, sdk.TokensFromConsensusPower(5), diffTokens) + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(5).String(), diffTokens.String()) } // tests Slash at the current height func TestSlashValidatorAtCurrentHeight(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, _, _ := bootstrapSlashTest(t, 10) consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - oldBondedPool := keeper.GetBondedPool(ctx) - validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) + app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) // read updated state - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - newBondedPool := keeper.GetBondedPool(ctx) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) - validator = keeper.mustGetValidator(ctx, validator.OperatorAddress) + validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + assert.True(t, found) // power decreased require.Equal(t, int64(5), validator.GetConsensusPower()) + // pool bonded shares decreased - diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) - require.Equal(t, sdk.TokensFromConsensusPower(5), diffTokens) + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(5).String(), diffTokens.String()) } // tests Slash at a previous height with an unbonding delegation func TestSlashWithUnbondingDelegation(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation with expiration timestamp beyond which the // unbonding delegation shouldn't be slashed ubdTokens := sdk.TokensFromConsensusPower(4) - ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, - time.Unix(0, 0), ubdTokens) - keeper.SetUnbondingDelegation(ctx, ubd) + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdTokens) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) // slash validator for the first time ctx = ctx.WithBlockHeight(12) - oldBondedPool := keeper.GetBondedPool(ctx) - validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, consAddr, 10, 10, fraction) + app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) // end block - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) // read updating unbonding delegation - ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) + // balance decreased require.Equal(t, sdk.TokensFromConsensusPower(2), ubd.Entries[0].Balance) - // read updated pool - newBondedPool := keeper.GetBondedPool(ctx) + // bonded tokens burned - diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) require.Equal(t, sdk.TokensFromConsensusPower(3), diffTokens) + // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) + // power decreased by 3 - 6 stake originally bonded at the time of infraction // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction @@ -286,20 +308,24 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // slash validator again ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, consAddr, 9, 10, fraction) - ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) + // balance decreased again require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - // read updated pool - newBondedPool = keeper.GetBondedPool(ctx) + // bonded tokens burned again - diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) require.Equal(t, sdk.TokensFromConsensusPower(6), diffTokens) + // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) + // power decreased by 3 again require.Equal(t, int64(4), validator.GetConsensusPower()) @@ -308,20 +334,24 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // on the unbonding delegation, but it will slash stake bonded since the infraction // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, consAddr, 9, 10, fraction) - ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) + // balance unchanged require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - // read updated pool - newBondedPool = keeper.GetBondedPool(ctx) + // bonded tokens burned again - diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) require.Equal(t, sdk.TokensFromConsensusPower(9), diffTokens) + // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) + // power decreased by 3 again require.Equal(t, int64(1), validator.GetConsensusPower()) @@ -330,75 +360,88 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // on the unbonding delegation, but it will slash stake bonded since the infraction // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, consAddr, 9, 10, fraction) - ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction) + + ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) require.Len(t, ubd.Entries, 1) + // balance unchanged require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) - // read updated pool - newBondedPool = keeper.GetBondedPool(ctx) + // just 1 bonded token burned again since that's all the validator now has - diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx)) require.Equal(t, sdk.TokensFromConsensusPower(10), diffTokens) + // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) + // read updated validator // power decreased by 1 again, validator is out of stake // validator should be in unbonding period - validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) } +//_________________________________________________________________________________ // tests Slash at a previous height with a redelegation func TestSlashWithRedelegation(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - bondDenom := keeper.BondDenom(ctx) + bondDenom := app.StakingKeeper.BondDenom(ctx) // set a redelegation rdTokens := sdk.TokensFromConsensusPower(6) rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdTokens, rdTokens.ToDec()) - keeper.SetRedelegation(ctx, rd) + app.StakingKeeper.SetRedelegation(ctx, rd) // set the associated delegation del := types.NewDelegation(addrDels[0], addrVals[1], rdTokens.ToDec()) - keeper.SetDelegation(ctx, del) + app.StakingKeeper.SetDelegation(ctx, del) // update bonded tokens - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2))) - err := bondedPool.SetCoins(bondedPool.GetCoins().Add(rdCoins...)) + + balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()) + + err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), balances.Add(rdCoins...)) require.NoError(t, err) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) - oldBonded := bondedPool.GetCoins().AmountOf(bondDenom) - oldNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + + oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount // slash validator ctx = ctx.WithBlockHeight(12) - validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, fraction) }) + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) }) burnAmount := sdk.TokensFromConsensusPower(10).ToDec().Mul(fraction).TruncateInt() - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + // burn bonded tokens from only from delegations - require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) - require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) - oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + + notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount // read updating redelegation - rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rd.Entries, 1) // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 2 - 4 stake originally bonded at the time of infraction // was still bonded at the time of discovery and was slashed by half, 4 stake @@ -407,88 +450,102 @@ func TestSlashWithRedelegation(t *testing.T) { require.Equal(t, int64(8), validator.GetConsensusPower()) // slash the validator again - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) burnAmount = sdk.TokensFromConsensusPower(7) // read updated pool - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + // seven bonded tokens burned - require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) - require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) - oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance)) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount // read updating redelegation - rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rd.Entries, 1) // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 4 require.Equal(t, int64(4), validator.GetConsensusPower()) // slash the validator again, by 100% ctx = ctx.WithBlockHeight(12) - validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) burnAmount = sdk.TokensFromConsensusPower(10).ToDec().Mul(sdk.OneDec()).TruncateInt() burnAmount = burnAmount.Sub(sdk.OneDec().MulInt(rdTokens).TruncateInt()) // read updated pool - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) - require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) - oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance)) + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance)) + oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount // read updating redelegation - rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rd.Entries, 1) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) // read updated validator // validator decreased to zero power, should be in unbonding period - validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) // slash the validator again, by 100% // no stake remains to be slashed ctx = ctx.WithBlockHeight(12) // validator still in unbonding period - validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) - require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) // read updated pool - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, oldBonded, bondedPool.GetCoins().AmountOf(bondDenom))) - require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded, bondedPoolBalance)) + notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance)) // read updating redelegation - rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rd.Entries, 1) // read updated validator // power still zero, still in unbonding period - validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), types.Unbonding) } // tests Slash at a previous height with both an unbonding delegation and a redelegation func TestSlashBoth(t *testing.T) { - ctx, keeper, _ := setupHelper(t, 10) + app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10) fraction := sdk.NewDecWithPrec(5, 1) - bondDenom := keeper.BondDenom(ctx) + bondDenom := app.StakingKeeper.BondDenom(ctx) // set a redelegation with expiration timestamp beyond which the // redelegation shouldn't be slashed @@ -496,56 +553,64 @@ func TestSlashBoth(t *testing.T) { rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdATokens, rdATokens.ToDec()) - keeper.SetRedelegation(ctx, rdA) + app.StakingKeeper.SetRedelegation(ctx, rdA) // set the associated delegation delA := types.NewDelegation(addrDels[0], addrVals[1], rdATokens.ToDec()) - keeper.SetDelegation(ctx, delA) + app.StakingKeeper.SetDelegation(ctx, delA) // set an unbonding delegation with expiration timestamp (beyond which the // unbonding delegation shouldn't be slashed) ubdATokens := sdk.TokensFromConsensusPower(4) ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdATokens) - keeper.SetUnbondingDelegation(ctx, ubdA) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubdA) bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2))) notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens)) // update bonded tokens - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - require.NoError(t, bondedPool.SetCoins(bondedPool.GetCoins().Add(bondedCoins...))) - require.NoError(t, bondedPool.SetCoins(notBondedPool.GetCoins().Add(notBondedCoins...))) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + 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...))) - oldBonded := bondedPool.GetCoins().AmountOf(bondDenom) - oldNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) + notBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), notBondedPoolBalances.Add(notBondedCoins...))) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + + oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount // slash validator ctx = ctx.WithBlockHeight(12) - validator, found := keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) consAddr0 := sdk.ConsAddress(PKs[0].Address()) - keeper.Slash(ctx, consAddr0, 10, 10, fraction) + app.StakingKeeper.Slash(ctx, consAddr0, 10, 10, fraction) burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt() burnedBondAmount := sdk.TokensFromConsensusPower(10).ToDec().Mul(fraction).TruncateInt() burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount) // read updated pool - bondedPool = keeper.GetBondedPool(ctx) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.True(sdk.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPool.GetCoins().AmountOf(bondDenom))) - require.True(sdk.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPool.GetCoins().AmountOf(bondDenom))) + bondedPool = app.StakingKeeper.GetBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) + + bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPoolBalance)) + + notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount + require.True(sdk.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPoolBalance)) // read updating redelegation - rdA, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + rdA, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, rdA.Entries, 1) // read updated validator - validator, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) // power not decreased, all stake was bonded since require.Equal(t, int64(10), validator.GetConsensusPower()) diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go index 0d730b48b3ca..42e94740d3e6 100644 --- a/x/staking/keeper/test_common.go +++ b/x/staking/keeper/test_common.go @@ -2,240 +2,12 @@ package keeper // noalias import ( "bytes" - "encoding/hex" "math/rand" - "strconv" - "testing" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/x/supply" -) - -// dummy addresses used for testing -// nolint:unused, deadcode -var ( - Addrs = createTestAddrs(500) - PKs = createTestPubKeys(500) - - addrDels = []sdk.AccAddress{ - Addrs[0], - Addrs[1], - } - addrVals = []sdk.ValAddress{ - sdk.ValAddress(Addrs[2]), - sdk.ValAddress(Addrs[3]), - sdk.ValAddress(Addrs[4]), - sdk.ValAddress(Addrs[5]), - sdk.ValAddress(Addrs[6]), - } ) -//_______________________________________________________________________________________ - -// intended to be used with require/assert: require.True(ValEq(...)) -func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) { - return t, exp.TestEquivalent(got), "expected:\t%v\ngot:\t\t%v", exp, got -} - -//_______________________________________________________________________________________ - -// create a codec used only for testing -func MakeTestCodec() *codec.Codec { - var cdc = codec.New() - - // Register Msgs - cdc.RegisterInterface((*sdk.Msg)(nil), nil) - cdc.RegisterConcrete(bank.MsgSend{}, "test/staking/Send", nil) - cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/staking/CreateValidator", nil) - cdc.RegisterConcrete(types.MsgEditValidator{}, "test/staking/EditValidator", nil) - cdc.RegisterConcrete(types.MsgUndelegate{}, "test/staking/Undelegate", nil) - cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/staking/BeginRedelegate", nil) - - // Register AppAccount - cdc.RegisterInterface((*authexported.Account)(nil), nil) - cdc.RegisterConcrete(&auth.BaseAccount{}, "test/staking/BaseAccount", nil) - supply.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - return cdc -} - -// Hogpodge of all sorts of input required for testing. -// `initPower` is converted to an amount of tokens. -// If `initPower` is 0, no addrs get created. -func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context, auth.AccountKeeper, Keeper, types.SupplyKeeper) { - keyStaking := sdk.NewKVStoreKey(types.StoreKey) - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - - db := dbm.NewMemDB() - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) - err := ms.LoadLatestVersion() - require.Nil(t, err) - - ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger()) - ctx = ctx.WithConsensusParams( - &abci.ConsensusParams{ - Validator: &abci.ValidatorParams{ - PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, - }, - }, - ) - cdc := MakeTestCodec() - - feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - pk := params.NewKeeper(cdc, keyParams, tkeyParams) - - accountKeeper := auth.NewAccountKeeper( - cdc, // amino codec - keyAcc, // target store - pk.Subspace(auth.DefaultParamspace), - auth.ProtoBaseAccount, // prototype - ) - - bk := bank.NewBaseKeeper( - accountKeeper, - pk.Subspace(bank.DefaultParamspace), - blacklistedAddrs, - ) - - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - types.NotBondedPoolName: {supply.Burner, supply.Staking}, - types.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, maccPerms) - - initTokens := sdk.TokensFromConsensusPower(initPower) - initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) - totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(Addrs))))) - - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - keeper := NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(DefaultParamspace)) - keeper.SetParams(ctx, types.DefaultParams()) - - // set module accounts - err = notBondedPool.SetCoins(totalSupply) - require.NoError(t, err) - - supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) - supplyKeeper.SetModuleAccount(ctx, bondPool) - supplyKeeper.SetModuleAccount(ctx, notBondedPool) - - // fill all the addresses with some coins, set the loose pool tokens simultaneously - for _, addr := range Addrs { - _, err := bk.AddCoins(ctx, addr, initCoins) - if err != nil { - panic(err) - } - } - - return ctx, accountKeeper, keeper, supplyKeeper -} - -func NewPubKey(pk string) (res crypto.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - //res, err = crypto.PubKeyFromBytes(pkBytes) - var pkEd ed25519.PubKeyEd25519 - copy(pkEd[:], pkBytes) - return pkEd -} - -// for incode address generation -func TestAddr(addr string, bech string) sdk.AccAddress { - - res, err := sdk.AccAddressFromHex(addr) - if err != nil { - panic(err) - } - bechexpected := res.String() - if bech != bechexpected { - panic("Bech encoding doesn't match reference") - } - - bechres, err := sdk.AccAddressFromBech32(bech) - if err != nil { - panic(err) - } - if !bytes.Equal(bechres, res) { - panic("Bech decode and hex decode don't match") - } - - return res -} - -// nolint: unparam -func createTestAddrs(numAddrs int) []sdk.AccAddress { - var addresses []sdk.AccAddress - var buffer bytes.Buffer - - // start at 100 so we can make up to 999 test addresses with valid test addresses - for i := 100; i < (numAddrs + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string - - buffer.WriteString(numString) //adding on final two digits to make addresses unique - res, _ := sdk.AccAddressFromHex(buffer.String()) - bech := res.String() - addresses = append(addresses, TestAddr(buffer.String(), bech)) - buffer.Reset() - } - return addresses -} - -// nolint: unparam -func createTestPubKeys(numPubKeys int) []crypto.PubKey { - var publicKeys []crypto.PubKey - var buffer bytes.Buffer - - //start at 10 to avoid changing 1 to 01, 2 to 02, etc - for i := 100; i < (numPubKeys + 100); i++ { - numString := strconv.Itoa(i) - buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string - buffer.WriteString(numString) //adding on final two digits to make pubkeys unique - publicKeys = append(publicKeys, NewPubKey(buffer.String())) - buffer.Reset() - } - return publicKeys -} - -//_____________________________________________________________________________________ - // does a certain by-power index record exist func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool { store := ctx.KVStore(keeper.storeKey) @@ -248,43 +20,40 @@ func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Vali // Remove any existing power key for validator. store := ctx.KVStore(keeper.storeKey) + deleted := false + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsByPowerIndexKey) defer iterator.Close() - deleted := false + for ; iterator.Valid(); iterator.Next() { valAddr := types.ParseValidatorPowerRankKey(iterator.Key()) - if bytes.Equal(valAddr, validator.OperatorAddress) { + if bytes.Equal(valAddr, validator.GetOperator()) { if deleted { panic("found duplicate power index key") } else { deleted = true } + store.Delete(iterator.Key()) } } keeper.SetValidatorByPowerIndex(ctx, validator) - if apply { - keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found := keeper.GetValidator(ctx, validator.OperatorAddress) - if !found { - panic("validator expected but not found") - } - return validator + + if !apply { + ctx, _ = ctx.CacheContext() } - cachectx, _ := ctx.CacheContext() - keeper.ApplyAndReturnValidatorSetUpdates(cachectx) - validator, found := keeper.GetValidator(cachectx, validator.OperatorAddress) + _, err := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + panic(err) + } + + validator, found := keeper.GetValidator(ctx, validator.GetOperator()) if !found { panic("validator expected but not found") } - return validator -} -// nolint:deadcode, unused -func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { - store := ctx.KVStore(k.storeKey) - return store.Has(power) + return validator } // RandomValidator returns a random validator given access to the keeper and ctx @@ -295,5 +64,6 @@ func RandomValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) (val types.Va } i := r.Intn(len(vals)) + return vals[i], true } diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go index 101bc139215a..f06994f44f70 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + gogotypes "github.com/gogo/protobuf/types" abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,15 +24,26 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { // unbonded after the Endblocker (go from Bonded -> Unbonding during // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during // UnbondAllMatureValidatorQueue). - validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx) + validatorUpdates, err := k.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + panic(err) + } - // Unbond all mature validators from the unbonding queue. - k.UnbondAllMatureValidatorQueue(ctx) + // unbond all mature validators from the unbonding queue + k.UnbondAllMatureValidators(ctx) // Remove all mature unbonding delegations from the ubd queue. matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { - balances, err := k.CompleteUnbondingWithAmount(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress) + addr, err := sdk.ValAddressFromBech32(dvPair.ValidatorAddress) + if err != nil { + panic(err) + } + delegatorAddress, err := sdk.AccAddressFromBech32(dvPair.DelegatorAddress) + if err != nil { + panic(err) + } + balances, err := k.CompleteUnbonding(ctx, delegatorAddress, addr) if err != nil { continue } @@ -40,8 +52,8 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { sdk.NewEvent( types.EventTypeCompleteUnbonding, sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()), - sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()), - sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()), + sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress), + sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress), ), ) } @@ -49,11 +61,23 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { // Remove all mature redelegations from the red queue. matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { - balances, err := k.CompleteRedelegationWithAmount( + valSrcAddr, err := sdk.ValAddressFromBech32(dvvTriplet.ValidatorSrcAddress) + if err != nil { + panic(err) + } + valDstAddr, err := sdk.ValAddressFromBech32(dvvTriplet.ValidatorDstAddress) + if err != nil { + panic(err) + } + delegatorAddress, err := sdk.AccAddressFromBech32(dvvTriplet.DelegatorAddress) + if err != nil { + panic(err) + } + balances, err := k.CompleteRedelegation( ctx, - dvvTriplet.DelegatorAddress, - dvvTriplet.ValidatorSrcAddress, - dvvTriplet.ValidatorDstAddress, + delegatorAddress, + valSrcAddr, + valDstAddr, ) if err != nil { continue @@ -63,9 +87,9 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { sdk.NewEvent( types.EventTypeCompleteRedelegation, sdk.NewAttribute(sdk.AttributeKeyAmount, balances.String()), - sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()), - sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()), - sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()), + sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress), + sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress), + sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress), ), ) } @@ -85,8 +109,7 @@ func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate { // CONTRACT: Only validators with non-zero power or zero-power that were bonded // at the previous block height or were removed from the validator set entirely // are returned to Tendermint. -func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) { - +func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate, err error) { maxValidators := k.GetParams(ctx).MaxValidators totalPower := sdk.ZeroInt() amtFromBondedToNotBonded, amtFromNotBondedToBonded := sdk.ZeroInt(), sdk.ZeroInt() @@ -99,11 +122,10 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Iterate over validators, highest power to lowest. iterator := k.ValidatorsPowerStoreIterator(ctx) defer iterator.Close() - for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() { + for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() { // everything that is iterated in this loop is becoming or already a // part of the bonded validator set - valAddr := sdk.ValAddress(iterator.Value()) validator := k.mustGetValidator(ctx, valAddr) @@ -120,10 +142,16 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // apply the appropriate state change if necessary switch { case validator.IsUnbonded(): - validator = k.unbondedToBonded(ctx, validator) + validator, err = k.unbondedToBonded(ctx, validator) + if err != nil { + return + } amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens()) case validator.IsUnbonding(): - validator = k.unbondingToBonded(ctx, validator) + validator, err = k.unbondingToBonded(ctx, validator) + if err != nil { + return + } amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens()) case validator.IsBonded(): // no state change @@ -133,29 +161,32 @@ 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] - newPower := validator.ConsensusPower() - newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(newPower) + newPowerBytes := k.cdc.MustMarshalBinaryBare(&gogotypes.Int64Value{Value: newPower}) // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) + k.SetLastValidatorPower(ctx, valAddr, newPower) } delete(last, valAddrBytes) - count++ + totalPower = totalPower.Add(sdk.NewInt(newPower)) } noLongerBonded := sortNoLongerBonded(last) for _, valAddrBytes := range noLongerBonded { - validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes)) - validator = k.bondedToUnbonding(ctx, validator) + validator, err = k.bondedToUnbonding(ctx, validator) + if err != nil { + return + } amtFromBondedToNotBonded = amtFromBondedToNotBonded.Add(validator.GetTokens()) k.DeleteLastValidatorPower(ctx, validator.GetOperator()) updates = append(updates, validator.ABCIValidatorUpdateZero()) @@ -173,8 +204,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab k.notBondedTokensToBonded(ctx, amtFromNotBondedToBonded.Sub(amtFromBondedToNotBonded)) case amtFromNotBondedToBonded.LT(amtFromBondedToNotBonded): k.bondedTokensToNotBonded(ctx, amtFromBondedToNotBonded.Sub(amtFromNotBondedToBonded)) - default: - // equal amounts of tokens; no update required + default: // equal amounts of tokens; no update required } // set total power on lookup index if there are any updates @@ -182,37 +212,41 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab k.SetLastTotalPower(ctx, totalPower) } - return updates + return updates, err } // Validator state transitions -func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator { +func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) (types.Validator, error) { if !validator.IsBonded() { panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator)) } + return k.beginUnbondingValidator(ctx, validator) } -func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator { +func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) (types.Validator, error) { if !validator.IsUnbonding() { panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) } + return k.bondValidator(ctx, validator) } -func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator { +func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) (types.Validator, error) { if !validator.IsUnbonded() { panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator)) } + return k.bondValidator(ctx, validator) } -// switches a validator from unbonding state to unbonded state -func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator { +// UnbondingToUnbonded switches a validator from unbonding state to unbonded state +func (k Keeper) UnbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator { if !validator.IsUnbonding() { panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) } + return k.completeUnbondingValidator(ctx, validator) } @@ -239,12 +273,11 @@ func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) { } // perform all the store operations for when a validator status becomes bonded -func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator { - +func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) (types.Validator, error) { // delete the validator by power index, as the key will change k.DeleteValidatorByPowerIndex(ctx, validator) - validator = validator.UpdateStatus(sdk.Bonded) + validator = validator.UpdateStatus(types.Bonded) // save the now bonded validator record to the two referenced stores k.SetValidator(ctx, validator) @@ -254,28 +287,31 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. k.DeleteValidatorQueue(ctx, validator) // trigger hook - k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddress) + consAddr, err := validator.GetConsAddr() + if err != nil { + return validator, err + } + k.AfterValidatorBonded(ctx, consAddr, validator.GetOperator()) - return validator + return validator, err } // perform all the store operations for when a validator begins unbonding -func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { - +func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) (types.Validator, error) { params := k.GetParams(ctx) // delete the validator by power index, as the key will change k.DeleteValidatorByPowerIndex(ctx, validator) // sanity check - if validator.Status != sdk.Bonded { + if validator.Status != types.Bonded { panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator)) } - validator = validator.UpdateStatus(sdk.Unbonding) + validator = validator.UpdateStatus(types.Unbonding) // set the unbonding completion time and completion height appropriately - validator.UnbondingCompletionTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) + validator.UnbondingTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) validator.UnbondingHeight = ctx.BlockHeader().Height // save the now unbonded validator record and power index @@ -283,18 +319,23 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat k.SetValidatorByPowerIndex(ctx, validator) // Adds to unbonding validator queue - k.InsertValidatorQueue(ctx, validator) + k.InsertUnbondingValidatorQueue(ctx, validator) // trigger hook - k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddress) + consAddr, err := validator.GetConsAddr() + if err != nil { + return validator, err + } + k.AfterValidatorBeginUnbonding(ctx, consAddr, validator.GetOperator()) - return validator + return validator, nil } // perform all the store operations for when a validator status becomes unbonded func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { - validator = validator.UpdateStatus(sdk.Unbonded) + validator = validator.UpdateStatus(types.Unbonded) k.SetValidator(ctx, validator) + return validator } @@ -304,6 +345,7 @@ type validatorsByAddr map[[sdk.AddrLen]byte][]byte // get the last validator set func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { last := make(validatorsByAddr) + iterator := k.LastValidatorsIterator(ctx) defer iterator.Close() @@ -315,6 +357,7 @@ func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { last[valAddr] = make([]byte, len(powerBytes)) copy(last[valAddr], powerBytes) } + return last } @@ -324,6 +367,7 @@ func sortNoLongerBonded(last validatorsByAddr) [][]byte { // 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[:]) @@ -335,5 +379,6 @@ func sortNoLongerBonded(last validatorsByAddr) [][]byte { // -1 means strictly less than return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1 }) + return noLongerBonded } diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index 48ecbb384313..3aa1441767a4 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -1,59 +1,24 @@ package keeper import ( - "bytes" "fmt" "time" + gogotypes "github.com/gogo/protobuf/types" + sdk "github.com/cosmos/cosmos-sdk/types" "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) + value := store.Get(types.GetValidatorKey(addr)) if value == nil { 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 - 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 } @@ -63,16 +28,19 @@ func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Val if !found { panic(fmt.Sprintf("validator record not found for address: %X\n", addr)) } + return validator } // get a single validator by consensus address func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) + opAddr := store.Get(types.GetValidatorByConsAddrKey(consAddr)) if opAddr == nil { return validator, false } + return k.GetValidator(ctx, opAddr) } @@ -81,21 +49,26 @@ func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAdd if !found { panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr)) } + return validator } // set the main record holding validator details func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) - bz := types.MustMarshalValidator(k.cdc, validator) - store.Set(types.GetValidatorKey(validator.OperatorAddress), bz) + bz := types.MustMarshalValidator(k.cdc, &validator) + store.Set(types.GetValidatorKey(validator.GetOperator()), bz) } // validator index -func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) { +func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) error { + consPk, err := validator.GetConsAddr() + if err != nil { + return err + } store := ctx.KVStore(k.storeKey) - consAddr := sdk.ConsAddress(validator.ConsPubKey.Address()) - store.Set(types.GetValidatorByConsAddrKey(consAddr), validator.OperatorAddress) + store.Set(types.GetValidatorByConsAddrKey(consPk), validator.GetOperator()) + return nil } // validator index @@ -104,8 +77,9 @@ func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Valida if validator.Jailed { return } + store := ctx.KVStore(k.storeKey) - store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) + store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.GetOperator()) } // validator index @@ -117,39 +91,39 @@ func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Val // validator index func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) - store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) + store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.GetOperator()) } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) { - k.DeleteValidatorByPowerIndex(ctx, validator) validator, addedShares = validator.AddTokensFromDel(tokensToAdd) k.SetValidator(ctx, validator) k.SetValidatorByPowerIndex(ctx, validator) + return validator, addedShares } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Int) { - k.DeleteValidatorByPowerIndex(ctx, validator) validator, removedTokens = validator.RemoveDelShares(sharesToRemove) k.SetValidator(ctx, validator) k.SetValidatorByPowerIndex(ctx, validator) + return validator, removedTokens } // Update the tokens of an existing validator, update the validators power index key func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Int) types.Validator { - k.DeleteValidatorByPowerIndex(ctx, validator) validator = validator.RemoveTokens(tokensToRemove) k.SetValidator(ctx, validator) k.SetValidatorByPowerIndex(ctx, validator) + return validator } @@ -157,7 +131,6 @@ func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, // An error is returned if the new commission rate is invalid. func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) (types.Commission, error) { - commission := validator.Commission blockTime := ctx.BlockHeader().Time @@ -173,8 +146,8 @@ func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, // remove the validator record and associated indexes // except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates +// TODO, this function panics, and it's not good. func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { - // first retrieve the old validator record validator, found := k.GetValidator(ctx, address) if !found { @@ -184,21 +157,24 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { if !validator.IsUnbonded() { panic("cannot call RemoveValidator on bonded or unbonding validators") } + if validator.Tokens.IsPositive() { panic("attempting to remove a validator which still contains tokens") } - if validator.Tokens.IsPositive() { - panic("validator being removed should never have positive tokens") + + valConsAddr, err := validator.GetConsAddr() + if err != nil { + panic(err) } // delete the old validator record store := ctx.KVStore(k.storeKey) store.Delete(types.GetValidatorKey(address)) - store.Delete(types.GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) + store.Delete(types.GetValidatorByConsAddrKey(valConsAddr)) store.Delete(types.GetValidatorsByPowerIndexKey(validator)) // call hooks - k.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddress) + k.AfterValidatorRemoved(ctx, valConsAddr, validator.GetOperator()) } // get groups of validators @@ -206,6 +182,7 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { // get the set of all validators with no limits, used during genesis dump func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey) defer iterator.Close() @@ -213,11 +190,12 @@ func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) validators = append(validators, validator) } + return validators } // return a given amount of all the validators -func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators []types.Validator) { +func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint32) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) validators = make([]types.Validator, maxRetrieve) @@ -230,6 +208,7 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [ validators[i] = validator i++ } + return validators[:i] // trim if the array length < maxRetrieve } @@ -251,6 +230,7 @@ func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { i++ } } + return validators[:i] // trim } @@ -267,18 +247,22 @@ func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) sdk.Iterator { // Returns zero if the operator was not a validator last block. func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power int64) { store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetLastValidatorPowerKey(operator)) if bz == nil { return 0 } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) - return + + intV := gogotypes.Int64Value{} + k.cdc.MustUnmarshalBinaryBare(bz, &intV) + + return intV.GetValue() } // Set the last validator power. func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) + bz := k.cdc.MustMarshalBinaryBare(&gogotypes.Int64Value{Value: power}) store.Set(types.GetLastValidatorPowerKey(operator), bz) } @@ -292,19 +276,24 @@ func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddres func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) { store := ctx.KVStore(k.storeKey) iterator = sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) + return iterator } // Iterate over last validator powers. func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power int64) (stop bool)) { store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) defer iter.Close() + for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(iter.Key()[len(types.LastValidatorPowerKey):]) - var power int64 - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) - if handler(addr, power) { + intV := &gogotypes.Int64Value{} + + k.cdc.MustUnmarshalBinaryBare(iter.Value(), intV) + + if handler(addr, intV.GetValue()) { break } } @@ -323,117 +312,137 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator i := 0 for ; iterator.Valid(); iterator.Next() { - // sanity check if i >= int(maxValidators) { panic("more validators than maxValidators found") } + address := types.AddressFromLastValidatorPowerKey(iterator.Key()) validator := k.mustGetValidator(ctx, address) validators[i] = validator i++ } + return validators[:i] // trim } -//_______________________________________________________________________ -// Validator Queue - -// gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators -// that expire at a certain time. -func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) { +// GetUnbondingValidators returns a slice of mature validator addresses that +// complete their unbonding at a given time and height. +func (k Keeper) GetUnbondingValidators(ctx sdk.Context, endTime time.Time, endHeight int64) []string { store := ctx.KVStore(k.storeKey) - bz := store.Get(types.GetValidatorQueueTimeKey(timestamp)) + + bz := store.Get(types.GetValidatorQueueKey(endTime, endHeight)) if bz == nil { - return []sdk.ValAddress{} + return []string{} } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &valAddrs) - return valAddrs + + addrs := types.ValAddresses{} + k.cdc.MustUnmarshalBinaryBare(bz, &addrs) + + return addrs.Addresses } -// Sets a specific validator queue timeslice. -func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []sdk.ValAddress) { +// SetUnbondingValidatorsQueue sets a given slice of validator addresses into +// the unbonding validator queue by a given height and time. +func (k Keeper) SetUnbondingValidatorsQueue(ctx sdk.Context, endTime time.Time, endHeight int64, addrs []string) { store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) - store.Set(types.GetValidatorQueueTimeKey(timestamp), bz) + bz := k.cdc.MustMarshalBinaryBare(&types.ValAddresses{Addresses: addrs}) + store.Set(types.GetValidatorQueueKey(endTime, endHeight), bz) } -// Deletes a specific validator queue timeslice. -func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.GetValidatorQueueTimeKey(timestamp)) +// InsertUnbondingValidatorQueue inserts a given unbonding validator address into +// the unbonding validator queue for a given height and time. +func (k Keeper) InsertUnbondingValidatorQueue(ctx sdk.Context, val types.Validator) { + addrs := k.GetUnbondingValidators(ctx, val.UnbondingTime, val.UnbondingHeight) + addrs = append(addrs, val.OperatorAddress) + k.SetUnbondingValidatorsQueue(ctx, val.UnbondingTime, val.UnbondingHeight, addrs) } -// Insert an validator address to the appropriate timeslice in the validator queue -func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { - timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) - timeSlice = append(timeSlice, val.OperatorAddress) - k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, timeSlice) +// DeleteValidatorQueueTimeSlice deletes all entries in the queue indexed by a +// given height and time. +func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, endTime time.Time, endHeight int64) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetValidatorQueueKey(endTime, endHeight)) } -// Delete a validator address from the validator queue +// DeleteValidatorQueue removes a validator by address from the unbonding queue +// indexed by a given height and time. func (k Keeper) DeleteValidatorQueue(ctx sdk.Context, val types.Validator) { - timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) - newTimeSlice := []sdk.ValAddress{} - for _, addr := range timeSlice { - if !bytes.Equal(addr, val.OperatorAddress) { - newTimeSlice = append(newTimeSlice, addr) + addrs := k.GetUnbondingValidators(ctx, val.UnbondingTime, val.UnbondingHeight) + newAddrs := []string{} + + for _, addr := range addrs { + if addr != val.OperatorAddress { + newAddrs = append(newAddrs, addr) } } - if len(newTimeSlice) == 0 { - k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) + + if len(newAddrs) == 0 { + k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingTime, val.UnbondingHeight) } else { - k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, newTimeSlice) + k.SetUnbondingValidatorsQueue(ctx, val.UnbondingTime, val.UnbondingHeight, newAddrs) } } -// Returns all the validator queue timeslices from time 0 until endTime -func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { +// ValidatorQueueIterator returns an interator ranging over validators that are +// unbonding whose unbonding completion occurs at the given height and time. +func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time, endHeight int64) sdk.Iterator { store := ctx.KVStore(k.storeKey) - return store.Iterator(types.ValidatorQueueKey, sdk.InclusiveEndBytes(types.GetValidatorQueueTimeKey(endTime))) + return store.Iterator(types.ValidatorQueueKey, sdk.InclusiveEndBytes(types.GetValidatorQueueKey(endTime, endHeight))) } -// Returns a concatenated list of all the timeslices before currTime, and deletes the timeslices from the queue -func (k Keeper) GetAllMatureValidatorQueue(ctx sdk.Context, currTime time.Time) (matureValsAddrs []sdk.ValAddress) { - // gets an iterator for all timeslices from time 0 until the current Blockheader time - validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) - defer validatorTimesliceIterator.Close() - - for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { - timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) - matureValsAddrs = append(matureValsAddrs, timeslice...) - } - - return matureValsAddrs -} - -// Unbonds all the unbonding validators that have finished their unbonding period -func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { +// UnbondAllMatureValidators unbonds all the mature unbonding validators that +// have finished their unbonding period. +func (k Keeper) UnbondAllMatureValidators(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) - defer validatorTimesliceIterator.Close() - for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { - timeslice := []sdk.ValAddress{} - k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) + blockTime := ctx.BlockTime() + blockHeight := ctx.BlockHeight() + + // unbondingValIterator will contains all validator addresses indexed under + // the ValidatorQueueKey prefix. Note, the entire index key is composed as + // ValidatorQueueKey | timeBzLen (8-byte big endian) | timeBz | heightBz (8-byte big endian), + // so it may be possible that certain validator addresses that are iterated + // over are not ready to unbond, so an explicit check is required. + unbondingValIterator := k.ValidatorQueueIterator(ctx, blockTime, blockHeight) + defer unbondingValIterator.Close() + + for ; unbondingValIterator.Valid(); unbondingValIterator.Next() { + key := unbondingValIterator.Key() + keyTime, keyHeight, err := types.ParseValidatorQueueKey(key) + if err != nil { + panic(fmt.Errorf("failed to parse unbonding key: %w", err)) + } - for _, valAddr := range timeslice { - val, found := k.GetValidator(ctx, valAddr) - if !found { - panic("validator in the unbonding queue was not found") + // All addresses for the given key have the same unbonding height and time. + // We only unbond if the height and time are less than the current height + // and time. + if keyHeight <= blockHeight && (keyTime.Before(blockTime) || keyTime.Equal(blockTime)) { + addrs := types.ValAddresses{} + k.cdc.MustUnmarshalBinaryBare(unbondingValIterator.Value(), &addrs) + + for _, valAddr := range addrs.Addresses { + addr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + panic(err) + } + val, found := k.GetValidator(ctx, addr) + if !found { + panic("validator in the unbonding queue was not found") + } + + if !val.IsUnbonding() { + panic("unexpected validator in unbonding queue; status was not unbonding") + } + + val = k.UnbondingToUnbonded(ctx, val) + if val.GetDelegatorShares().IsZero() { + k.RemoveValidator(ctx, val.GetOperator()) + } } - if !val.IsUnbonding() { - panic("unexpected validator in unbonding queue; status was not unbonding") - } - val = k.unbondingToUnbonded(ctx, val) - if val.GetDelegatorShares().IsZero() { - k.RemoveValidator(ctx, val.OperatorAddress) - } + store.Delete(key) } - - store.Delete(validatorTimesliceIterator.Key()) } } 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 26cf255a46ec..869644070507 100644 --- a/x/staking/keeper/validator_test.go +++ b/x/staking/keeper/validator_test.go @@ -1,109 +1,154 @@ -package keeper +package keeper_test import ( "fmt" "testing" "time" + "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" + 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" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -//_______________________________________________________ +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.TB, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { + _, app, ctx := createTestInput() + + addrDels, addrVals := generateAddresses(app, ctx, numAddrs) + + 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, addrVals +} + +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]) + tokens := sdk.TokensFromConsensusPower(power) + vs[i], _ = vs[i].AddTokensFromDel(tokens) + } + return app, ctx, addrs, valAddrs, vs +} func TestSetValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 10) + app, ctx, _, _ := bootstrapValidatorTest(t, 10, 100) valPubKey := PKs[0] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) valTokens := sdk.TokensFromConsensusPower(10) // test how the validator is set from a purely unbonbed pool - validator := types.NewValidator(valAddr, valPubKey, types.Description{}) + validator := teststaking.NewValidator(t, valAddr, valPubKey) validator, _ = validator.AddTokensFromDel(valTokens) - require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, types.Unbonded, validator.Status) assert.Equal(t, valTokens, validator.Tokens) assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) - keeper.SetValidator(ctx, validator) - keeper.SetValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) // ensure update - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, found := keeper.GetValidator(ctx, valAddr) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) - require.Equal(t, 1, len(updates)) require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) // after the save the validator should be bonded - require.Equal(t, sdk.Bonded, validator.Status) + require.Equal(t, types.Bonded, validator.Status) assert.Equal(t, valTokens, validator.Tokens) assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) // Check each store for being saved - resVal, found := keeper.GetValidator(ctx, valAddr) + resVal, found := app.StakingKeeper.GetValidator(ctx, valAddr) assert.True(ValEq(t, validator, resVal)) require.True(t, found) - resVals := keeper.GetLastValidators(ctx) + resVals := app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validator, resVals[0])) - resVals = keeper.GetBondedValidatorsByPower(ctx) + resVals = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) - resVals = keeper.GetValidators(ctx, 1) + resVals = app.StakingKeeper.GetValidators(ctx, 1) require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) - resVals = keeper.GetValidators(ctx, 10) + resVals = app.StakingKeeper.GetValidators(ctx, 10) require.Equal(t, 1, len(resVals)) require.True(ValEq(t, validator, resVals[0])) - allVals := keeper.GetAllValidators(ctx) + allVals := app.StakingKeeper.GetAllValidators(ctx) require.Equal(t, 1, len(allVals)) } func TestUpdateValidatorByPowerIndex(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 0) + app, ctx, _, _ := bootstrapValidatorTest(t, 0, 100) + _, addrVals := generateAddresses(app, ctx, 1) + + 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) + + err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) + require.NoError(t, err) - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) - notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // add a validator - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) validator, delSharesCreated := validator.AddTokensFromDel(sdk.TokensFromConsensusPower(100)) - require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, types.Unbonded, validator.Status) require.Equal(t, sdk.TokensFromConsensusPower(100), validator.Tokens) - TestingUpdateValidator(keeper, ctx, validator, true) - validator, found := keeper.GetValidator(ctx, addrVals[0]) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) + validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) require.Equal(t, sdk.TokensFromConsensusPower(100), validator.Tokens) power := types.GetValidatorsByPowerIndexKey(validator) - require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) + require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) // burn half the delegator shares - keeper.DeleteValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validator) validator, burned := validator.RemoveDelShares(delSharesCreated.Quo(sdk.NewDec(2))) require.Equal(t, sdk.TokensFromConsensusPower(50), burned) - TestingUpdateValidator(keeper, ctx, validator, true) // update the validator, possibly kicking it out - require.False(t, validatorByPowerIndexExists(keeper, ctx, power)) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) // update the validator, possibly kicking it out + require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) - validator, found = keeper.GetValidator(ctx, addrVals[0]) + validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) power = types.GetValidatorsByPowerIndexKey(validator) - require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) + require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) } func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { @@ -111,29 +156,34 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { maxVals := 5 // create context, keeper, and pool for tests - ctx, _, keeper, _ := CreateTestInput(t, false, 0) - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) + app, ctx, _, valAddrs := bootstrapValidatorTest(t, 0, 100) + + bondedPool := app.StakingKeeper.GetBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) // create keeper parameters - params := keeper.GetParams(ctx) - params.MaxValidators = uint16(maxVals) - keeper.SetParams(ctx, params) + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(maxVals) + app.StakingKeeper.SetParams(ctx, params) // create a random pool - bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) - notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + err := app.BankKeeper.SetBalances(ctx, bondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) + require.NoError(t, err) + + err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) + require.NoError(t, err) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) validators := make([]types.Validator, numVals) for i := 0; i < len(validators); i++ { moniker := fmt.Sprintf("val#%d", int64(i)) - val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) + val := newMonikerValidator(t, valAddrs[i], PKs[i], moniker) delTokens := sdk.TokensFromConsensusPower(int64((i + 1) * 10)) val, _ = val.AddTokensFromDel(delTokens) - val = TestingUpdateValidator(keeper, ctx, val, true) + val = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, val, true) validators[i] = val } @@ -141,20 +191,22 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { // remove enough tokens to kick out the validator below the current cliff // validator and next in line cliff validator - keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) shares := sdk.TokensFromConsensusPower(21) nextCliffVal, _ = nextCliffVal.RemoveDelShares(shares.ToDec()) - nextCliffVal = TestingUpdateValidator(keeper, ctx, nextCliffVal, true) + nextCliffVal = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, nextCliffVal, true) - expectedValStatus := map[int]sdk.BondStatus{ - 9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded, - 0: sdk.Unbonding, 1: sdk.Unbonding, 2: sdk.Unbonding, 3: sdk.Unbonding, 6: sdk.Unbonding, + expectedValStatus := map[int]types.BondStatus{ + 9: types.Bonded, 8: types.Bonded, 7: types.Bonded, 5: types.Bonded, 4: types.Bonded, + 0: types.Unbonding, 1: types.Unbonding, 2: types.Unbonding, 3: types.Unbonding, 6: types.Unbonding, } // require all the validators have their respective statuses for valIdx, status := range expectedValStatus { valAddr := validators[valIdx].OperatorAddress - val, _ := keeper.GetValidator(ctx, valAddr) + addr, err := sdk.ValAddressFromBech32(valAddr) + assert.NoError(t, err) + val, _ := app.StakingKeeper.GetValidator(ctx, addr) assert.Equal( t, status, val.GetStatus(), @@ -165,44 +217,45 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { func TestSlashToZeroPowerRemoved(t *testing.T) { // initialize setup - ctx, _, keeper, _ := CreateTestInput(t, false, 100) + app, ctx, _, addrVals := bootstrapValidatorTest(t, 100, 20) // add a validator - validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator := teststaking.NewValidator(t, addrVals[0], PKs[0]) valTokens := sdk.TokensFromConsensusPower(100) - bondedPool := keeper.GetBondedPool(ctx) - err := bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), valTokens))) + 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) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) validator, _ = validator.AddTokensFromDel(valTokens) - require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, types.Unbonded, validator.Status) require.Equal(t, valTokens, validator.Tokens) - keeper.SetValidatorByConsAddr(ctx, validator) - validator = TestingUpdateValidator(keeper, ctx, validator, true) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validator) + validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true) require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, valTokens) // slash the validator by 100% - consAddr0 := sdk.ConsAddress(PKs[0].Address()) - keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) + app.StakingKeeper.Slash(ctx, sdk.ConsAddress(PKs[0].Address()), 0, 100, sdk.OneDec()) // apply TM updates - keeper.ApplyAndReturnValidatorSetUpdates(ctx) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1) // validator should be unbonding - validator, _ = keeper.GetValidator(ctx, addrVals[0]) - require.Equal(t, validator.GetStatus(), sdk.Unbonding) + validator, _ = app.StakingKeeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), types.Unbonding) } // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator func TestValidatorBasics(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20) //construct the validators var validators [3]types.Validator powers := []int64{9, 8, 7} for i, power := range powers { - validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) - validators[i].Status = sdk.Unbonded + validators[i] = teststaking.NewValidator(t, addrVals[i], PKs[i]) + validators[i].Status = types.Unbonded validators[i].Tokens = sdk.ZeroInt() tokens := sdk.TokensFromConsensusPower(power) @@ -213,59 +266,59 @@ func TestValidatorBasics(t *testing.T) { assert.Equal(t, sdk.TokensFromConsensusPower(7), validators[2].Tokens) // check the empty keeper first - _, found := keeper.GetValidator(ctx, addrVals[0]) + _, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.False(t, found) - resVals := keeper.GetLastValidators(ctx) + resVals := app.StakingKeeper.GetLastValidators(ctx) require.Zero(t, len(resVals)) - resVals = keeper.GetValidators(ctx, 2) + resVals = app.StakingKeeper.GetValidators(ctx, 2) require.Zero(t, len(resVals)) // set and retrieve a record - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) - keeper.SetValidatorByConsAddr(ctx, validators[0]) - resVal, found := keeper.GetValidator(ctx, addrVals[0]) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + app.StakingKeeper.SetValidatorByConsAddr(ctx, validators[0]) + resVal, found := app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) // retrieve from consensus - resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) + resVal, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) - resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + resVal, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) - resVals = keeper.GetLastValidators(ctx) + resVals = app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) - assert.Equal(t, sdk.Bonded, validators[0].Status) + assert.Equal(t, types.Bonded, validators[0].Status) assert.True(sdk.IntEq(t, sdk.TokensFromConsensusPower(9), validators[0].BondedTokens())) // modify a records, save, and retrieve - validators[0].Status = sdk.Bonded + validators[0].Status = types.Bonded validators[0].Tokens = sdk.TokensFromConsensusPower(10) validators[0].DelegatorShares = validators[0].Tokens.ToDec() - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) - resVal, found = keeper.GetValidator(ctx, addrVals[0]) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) - resVals = keeper.GetLastValidators(ctx) + resVals = app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) // add other validators - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) - validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) - resVal, found = keeper.GetValidator(ctx, addrVals[1]) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) + resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) require.True(t, found) assert.True(ValEq(t, validators[1], resVal)) - resVal, found = keeper.GetValidator(ctx, addrVals[2]) + resVal, found = app.StakingKeeper.GetValidator(ctx, addrVals[2]) require.True(t, found) assert.True(ValEq(t, validators[2], resVal)) - resVals = keeper.GetLastValidators(ctx) + resVals = app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 3, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here assert.True(ValEq(t, validators[1], resVals[1])) @@ -276,47 +329,45 @@ func TestValidatorBasics(t *testing.T) { // shouldn't be able to remove if status is not unbonded assert.PanicsWithValue(t, "cannot call RemoveValidator on bonded or unbonding validators", - func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddress) }) + func() { app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) }) // shouldn't be able to remove if there are still tokens left - validators[1].Status = sdk.Unbonded - keeper.SetValidator(ctx, validators[1]) + validators[1].Status = types.Unbonded + app.StakingKeeper.SetValidator(ctx, validators[1]) assert.PanicsWithValue(t, "attempting to remove a validator which still contains tokens", - func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddress) }) + func() { app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) }) - validators[1].Tokens = sdk.ZeroInt() // ...remove all tokens - keeper.SetValidator(ctx, validators[1]) // ...set the validator - keeper.RemoveValidator(ctx, validators[1].OperatorAddress) // Now it can be removed. - _, found = keeper.GetValidator(ctx, addrVals[1]) + validators[1].Tokens = sdk.ZeroInt() // ...remove all tokens + app.StakingKeeper.SetValidator(ctx, validators[1]) // ...set the validator + app.StakingKeeper.RemoveValidator(ctx, validators[1].GetOperator()) // Now it can be removed. + _, found = app.StakingKeeper.GetValidator(ctx, addrVals[1]) require.False(t, found) } // test how the validators are sorted, tests GetBondedValidatorsByPower func TestGetValidatorSortingUnmixed(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) // initialize some validators into the state amts := []sdk.Int{ - sdk.NewInt(0), - sdk.NewInt(100).Mul(sdk.PowerReduction), - sdk.NewInt(1).Mul(sdk.PowerReduction), - sdk.NewInt(400).Mul(sdk.PowerReduction), - sdk.NewInt(200).Mul(sdk.PowerReduction), - } - + sdk.NewIntFromUint64(0), + sdk.PowerReduction.MulRaw(100), + sdk.PowerReduction, + sdk.PowerReduction.MulRaw(400), + sdk.PowerReduction.MulRaw(200)} n := len(amts) var validators [5]types.Validator for i, amt := range amts { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i].Status = sdk.Bonded - validators[i].Tokens = sdk.NewIntFromBigInt(amt.BigInt()) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) + validators[i].Status = types.Bonded + validators[i].Tokens = amt validators[i].DelegatorShares = sdk.NewDecFromInt(amt) - TestingUpdateValidator(keeper, ctx, validators[i], true) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) } // first make sure everything made it in to the gotValidator group - resValidators := keeper.GetBondedValidatorsByPower(ctx) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, n, len(resValidators)) assert.Equal(t, sdk.NewInt(400).Mul(sdk.PowerReduction), resValidators[0].BondedTokens(), "%v", resValidators) assert.Equal(t, sdk.NewInt(200).Mul(sdk.PowerReduction), resValidators[1].BondedTokens(), "%v", resValidators) @@ -331,15 +382,15 @@ func TestGetValidatorSortingUnmixed(t *testing.T) { // test a basic increase in voting power validators[3].Tokens = sdk.NewInt(500).Mul(sdk.PowerReduction) - TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) // test a decrease in voting power validators[3].Tokens = sdk.NewInt(300).Mul(sdk.PowerReduction) - TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -347,16 +398,16 @@ func TestGetValidatorSortingUnmixed(t *testing.T) { // test equal voting power, different age validators[3].Tokens = sdk.NewInt(200).Mul(sdk.PowerReduction) ctx = ctx.WithBlockHeight(10) - TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) // no change in voting power - no change in sort ctx = ctx.WithBlockHeight(20) - TestingUpdateValidator(keeper, ctx, validators[4], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) @@ -364,67 +415,71 @@ func TestGetValidatorSortingUnmixed(t *testing.T) { // change in voting power of both validators, both still in v-set, no age change validators[3].Tokens = sdk.NewInt(300).Mul(sdk.PowerReduction) validators[4].Tokens = sdk.NewInt(300).Mul(sdk.PowerReduction) - TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n) ctx = ctx.WithBlockHeight(30) - TestingUpdateValidator(keeper, ctx, validators[4], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) require.Equal(t, len(resValidators), n, "%v", resValidators) assert.True(ValEq(t, validators[3], resValidators[0])) assert.True(ValEq(t, validators[4], resValidators[1])) } func TestGetValidatorSortingMixed(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - bondedPool := keeper.GetBondedPool(ctx) - notBondedPool := keeper.GetNotBondedPool(ctx) - bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(501)))) - notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(0)))) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + 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) + + err = app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.TokensFromConsensusPower(0)))) + require.NoError(t, err) + + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) // now 2 max resValidators - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.MaxValidators = 2 - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // initialize some validators into the state amts := []sdk.Int{ - sdk.NewInt(0), - sdk.NewInt(100).Mul(sdk.PowerReduction), - sdk.NewInt(1).Mul(sdk.PowerReduction), - sdk.NewInt(400).Mul(sdk.PowerReduction), - sdk.NewInt(200).Mul(sdk.PowerReduction), - } + sdk.NewIntFromUint64(0), + sdk.PowerReduction.MulRaw(100), + sdk.PowerReduction, + sdk.PowerReduction.MulRaw(400), + sdk.PowerReduction.MulRaw(200)} var validators [5]types.Validator for i, amt := range amts { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) validators[i].DelegatorShares = sdk.NewDecFromInt(amt) - validators[i].Status = sdk.Bonded - validators[i].Tokens = sdk.NewIntFromBigInt(amt.BigInt()) - TestingUpdateValidator(keeper, ctx, validators[i], true) + validators[i].Status = types.Bonded + validators[i].Tokens = amt + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) } - val0, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[0])) + val0, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0])) require.True(t, found) - val1, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[1])) + val1, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1])) require.True(t, found) - val2, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[2])) + val2, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[2])) require.True(t, found) - val3, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[3])) + val3, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[3])) require.True(t, found) - val4, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[4])) + val4, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[4])) require.True(t, found) - require.Equal(t, sdk.Bonded, val0.Status) - require.Equal(t, sdk.Unbonding, val1.Status) - require.Equal(t, sdk.Unbonding, val2.Status) - require.Equal(t, sdk.Bonded, val3.Status) - require.Equal(t, sdk.Bonded, val4.Status) + require.Equal(t, types.Bonded, val0.Status) + require.Equal(t, types.Unbonding, val1.Status) + require.Equal(t, types.Unbonding, val2.Status) + require.Equal(t, types.Bonded, val3.Status) + require.Equal(t, types.Bonded, val4.Status) // first make sure everything made it in to the gotValidator group - resValidators := keeper.GetBondedValidatorsByPower(ctx) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) // The validators returned should match the max validators assert.Equal(t, 2, len(resValidators)) assert.Equal(t, sdk.NewInt(400).Mul(sdk.PowerReduction), resValidators[0].BondedTokens(), "%v", resValidators) @@ -435,49 +490,56 @@ func TestGetValidatorSortingMixed(t *testing.T) { // TODO separate out into multiple tests func TestGetValidatorsEdgeCases(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) // set max validators to 2 - params := keeper.GetParams(ctx) - nMax := uint16(2) + params := app.StakingKeeper.GetParams(ctx) + nMax := uint32(2) params.MaxValidators = nMax - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // initialize some validators into the state powers := []int64{0, 100, 400, 400} var validators [4]types.Validator for i, power := range powers { moniker := fmt.Sprintf("val#%d", int64(i)) - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) + validators[i] = newMonikerValidator(t, sdk.ValAddress(addrs[i]), PKs[i], moniker) + tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - notBondedPool := keeper.GetNotBondedPool(ctx) - require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(sdk.NewCoin(params.BondDenom, tokens)))) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) - validators[i] = TestingUpdateValidator(keeper, ctx, validators[i], true) + + 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)))) + + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) + validators[i] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) } // ensure that the first two bonded validators are the largest validators - resValidators := keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint16(len(resValidators))) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) assert.True(ValEq(t, validators[2], resValidators[0])) assert.True(ValEq(t, validators[3], resValidators[1])) // delegate 500 tokens to validator 0 - keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) delTokens := sdk.TokensFromConsensusPower(500) validators[0], _ = validators[0].AddTokensFromDel(delTokens) - notBondedPool := keeper.GetNotBondedPool(ctx) + notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) + newTokens := sdk.NewCoins() - require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(newTokens...))) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + balances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + + require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(newTokens...))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) // test that the two largest validators are // a) validator 0 with 500 tokens // b) validator 2 with 400 tokens (delegated before validator 3) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint16(len(resValidators))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) @@ -493,66 +555,71 @@ func TestGetValidatorsEdgeCases(t *testing.T) { // validator 3 enters bonded validator set ctx = ctx.WithBlockHeight(40) - validators[3] = keeper.mustGetValidator(ctx, validators[3].OperatorAddress) - keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + var found bool + validators[3], found = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) + assert.True(t, found) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) validators[3], _ = validators[3].AddTokensFromDel(sdk.TokensFromConsensusPower(1)) - notBondedPool = keeper.GetNotBondedPool(ctx) + notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx) newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.TokensFromConsensusPower(1))) - require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(newTokens...))) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + balances = app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress()) + require.NoError(t, app.BankKeeper.SetBalances(ctx, notBondedPool.GetAddress(), balances.Add(newTokens...))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint16(len(resValidators))) + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[3], resValidators[1])) // validator 3 kicked out temporarily - keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) rmTokens := validators[3].TokensFromShares(sdk.NewDec(201)).TruncateInt() validators[3], _ = validators[3].RemoveDelShares(sdk.NewDec(201)) - bondedPool := keeper.GetBondedPool(ctx) - require.NoError(t, bondedPool.SetCoins(bondedPool.GetCoins().Add(sdk.NewCoin(params.BondDenom, rmTokens)))) - keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + 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)))) + app.AccountKeeper.SetModuleAccount(ctx, bondedPool) - validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint16(len(resValidators))) + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) // validator 3 does not get spot back - keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3]) validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200)) - notBondedPool = keeper.GetNotBondedPool(ctx) - require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(sdk.NewCoin(params.BondDenom, sdk.NewInt(200))))) - keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + 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))))) + app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, nMax, uint16(len(resValidators))) + validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint32(len(resValidators))) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) - _, exists := keeper.GetValidator(ctx, validators[3].OperatorAddress) + _, exists := app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) require.True(t, exists) } func TestValidatorBondHeight(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) // now 2 max resValidators - params := keeper.GetParams(ctx) + params := app.StakingKeeper.GetParams(ctx) params.MaxValidators = 2 - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) // initialize some validators into the state var validators [3]types.Validator - validators[0] = types.NewValidator(sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0], types.Description{}) - validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}) - validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{}) + validators[0] = teststaking.NewValidator(t, sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) + validators[1] = teststaking.NewValidator(t, sdk.ValAddress(addrs[1]), PKs[1]) + validators[2] = teststaking.NewValidator(t, sdk.ValAddress(addrs[2]), PKs[2]) tokens0 := sdk.TokensFromConsensusPower(200) tokens1 := sdk.TokensFromConsensusPower(100) @@ -561,59 +628,59 @@ func TestValidatorBondHeight(t *testing.T) { validators[1], _ = validators[1].AddTokensFromDel(tokens1) validators[2], _ = validators[2].AddTokensFromDel(tokens2) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) //////////////////////////////////////// // If two validators both increase to the same voting power in the same block, // the one with the first transaction should become bonded - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) - validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) - resValidators := keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, uint16(len(resValidators)), params.MaxValidators) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, uint32(len(resValidators)), params.MaxValidators) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[1], resValidators[1])) - keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) - keeper.DeleteValidatorByPowerIndex(ctx, validators[2]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[2]) delTokens := sdk.TokensFromConsensusPower(50) validators[1], _ = validators[1].AddTokensFromDel(delTokens) validators[2], _ = validators[2].AddTokensFromDel(delTokens) - validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) - require.Equal(t, params.MaxValidators, uint16(len(resValidators))) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) + validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, params.MaxValidators, uint32(len(resValidators))) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) } func TestFullValidatorSetPowerChange(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - params := keeper.GetParams(ctx) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) max := 2 - params.MaxValidators = uint16(2) - keeper.SetParams(ctx, params) + params.MaxValidators = uint32(2) + app.StakingKeeper.SetParams(ctx, params) // initialize some validators into the state powers := []int64{0, 100, 400, 400, 200} var validators [5]types.Validator for i, power := range powers { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - TestingUpdateValidator(keeper, ctx, validators[i], true) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true) } for i := range powers { var found bool - validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddress) + validators[i], found = app.StakingKeeper.GetValidator(ctx, validators[i].GetOperator()) require.True(t, found) } - assert.Equal(t, sdk.Unbonded, validators[0].Status) - assert.Equal(t, sdk.Unbonding, validators[1].Status) - assert.Equal(t, sdk.Bonded, validators[2].Status) - assert.Equal(t, sdk.Bonded, validators[3].Status) - assert.Equal(t, sdk.Unbonded, validators[4].Status) - resValidators := keeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, types.Unbonded, validators[0].Status) + assert.Equal(t, types.Unbonding, validators[1].Status) + assert.Equal(t, types.Bonded, validators[2].Status) + assert.Equal(t, types.Bonded, validators[3].Status) + assert.Equal(t, types.Unbonded, validators[4].Status) + resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, max, len(resValidators)) assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs assert.True(ValEq(t, validators[3], resValidators[1])) @@ -622,15 +689,15 @@ func TestFullValidatorSetPowerChange(t *testing.T) { tokens := sdk.TokensFromConsensusPower(600) validators[0], _ = validators[0].AddTokensFromDel(tokens) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) - resValidators = keeper.GetBondedValidatorsByPower(ctx) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true) + resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx) assert.Equal(t, max, len(resValidators)) assert.True(ValEq(t, validators[0], resValidators[0])) assert.True(ValEq(t, validators[2], resValidators[1])) } func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) powers := []int64{10, 20} var validators [2]types.Validator @@ -638,95 +705,83 @@ func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) } // test from nothing to something // tendermintUpdate set: {} -> {c1, c3} - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) - keeper.SetValidator(ctx, validators[0]) - keeper.SetValidatorByPowerIndex(ctx, validators[0]) - keeper.SetValidator(ctx, validators[1]) - keeper.SetValidatorByPowerIndex(ctx, validators[1]) - - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - assert.Equal(t, 2, len(updates)) - validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) - validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.SetValidator(ctx, validators[1]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) + + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) } func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) powers := []int64{10, 20} var validators [2]types.Validator for i, power := range powers { - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // test identical, // tendermintUpdate set: {} -> {} - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) } func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) powers := []int64{10, 20} var validators [2]types.Validator for i, power := range powers { - - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // test single value change // tendermintUpdate set: {} -> {c1'} - validators[0].Status = sdk.Bonded + validators[0].Status = types.Bonded validators[0].Tokens = sdk.TokensFromConsensusPower(600) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) - require.Equal(t, 1, len(updates)) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) } func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - powers := []int64{10, 20} - var validators [2]types.Validator - for i, power := range powers { + // TODO: use it in other places + app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - - tokens := sdk.TokensFromConsensusPower(power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} @@ -734,118 +789,96 @@ func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { delTokens2 := sdk.TokensFromConsensusPower(80) validators[0], _ = validators[0].AddTokensFromDel(delTokens1) validators[1], _ = validators[1].AddTokensFromDel(delTokens2) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(updates)) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - powers := []int64{10, 20, 5, 15, 25} - var validators [5]types.Validator - for i, power := range powers { - - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - - tokens := sdk.TokensFromConsensusPower(power) - validators[i], _ = validators[i].AddTokensFromDel(tokens) - - } + app, ctx, _, _, validators := initValidators(t, 1000, 20, powers) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} - keeper.SetValidator(ctx, validators[2]) - keeper.SetValidatorByPowerIndex(ctx, validators[2]) - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) - require.Equal(t, 1, len(updates)) + app.StakingKeeper.SetValidator(ctx, validators[2]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) // test validtor added at the beginning // tendermintUpdate set: {} -> {c0} - keeper.SetValidator(ctx, validators[3]) - keeper.SetValidatorByPowerIndex(ctx, validators[3]) - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validators[3], _ = keeper.GetValidator(ctx, validators[3].OperatorAddress) - require.Equal(t, 1, len(updates)) + app.StakingKeeper.SetValidator(ctx, validators[3]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[3]) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[3], _ = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator()) require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0]) // test validtor added at the end // tendermintUpdate set: {} -> {c0} - keeper.SetValidator(ctx, validators[4]) - keeper.SetValidatorByPowerIndex(ctx, validators[4]) - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validators[4], _ = keeper.GetValidator(ctx, validators[4].OperatorAddress) - require.Equal(t, 1, len(updates)) + app.StakingKeeper.SetValidator(ctx, validators[4]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[4]) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) + validators[4], _ = app.StakingKeeper.GetValidator(ctx, validators[4].GetOperator()) require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0]) } func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) params := types.DefaultParams() params.MaxValidators = 2 - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) powers := []int64{10, 20, 5} var validators [5]types.Validator for i, power := range powers { - - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // test validator added at the end but not inserted in the valset // tendermintUpdate set: {} -> {} - TestingUpdateValidator(keeper, ctx, validators[2], false) - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 0, len(updates)) + keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) // test validator change its power and become a gotValidator (pushing out an existing) // tendermintUpdate set: {} -> {c0, c4} - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) tokens := sdk.TokensFromConsensusPower(10) validators[2], _ = validators[2].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[2]) - keeper.SetValidatorByPowerIndex(ctx, validators[2]) - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) - require.Equal(t, 2, len(updates), "%v", updates) + app.StakingKeeper.SetValidator(ctx, validators[2]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1]) require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) } func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20) powers := []int64{100, 100} var validators [2]types.Validator for i, power := range powers { - - validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - + validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - } - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) - require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) // check initial power require.Equal(t, int64(100), validators[0].GetConsensusPower()) @@ -857,63 +890,60 @@ func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { delTokens2 := sdk.TokensFromConsensusPower(30) validators[0], _ = validators[0].RemoveDelShares(delTokens1.ToDec()) validators[1], _ = validators[1].RemoveDelShares(delTokens2.ToDec()) - validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) - validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false) + validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false) // power has changed require.Equal(t, int64(80), validators[0].GetConsensusPower()) require.Equal(t, int64(70), validators[1].GetConsensusPower()) // Tendermint updates should reflect power change - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(updates)) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) } func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - params := keeper.GetParams(ctx) - params.MaxValidators = uint16(3) + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(3) - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) powers := []int64{100, 100} var validators [2]types.Validator // initialize some validators into the state for i, power := range powers { - valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + validators[i] = teststaking.NewValidator(t, valAddr, valPubKey) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[i]) - keeper.SetValidatorByPowerIndex(ctx, validators[i]) + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) } // verify initial Tendermint updates are correct - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, len(validators), len(updates)) - validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) - validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) // update initial validator set for i, power := range powers { - keeper.DeleteValidatorByPowerIndex(ctx, validators[i]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[i]) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[i]) - keeper.SetValidatorByPowerIndex(ctx, validators[i]) + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) } // add a new validator that goes from zero power, to non-zero power, back to @@ -922,42 +952,41 @@ func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) amt := sdk.NewInt(100) - validator := types.NewValidator(valAddr, valPubKey, types.Description{}) + validator := teststaking.NewValidator(t, valAddr, valPubKey) validator, _ = validator.AddTokensFromDel(amt) - keeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidator(ctx, validator) validator, _ = validator.RemoveDelShares(amt.ToDec()) - keeper.SetValidator(ctx, validator) - keeper.SetValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) // add a new validator that increases in power valPubKey = PKs[len(validators)+2] valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) - validator = types.NewValidator(valAddr, valPubKey, types.Description{}) + validator = teststaking.NewValidator(t, valAddr, valPubKey) tokens := sdk.TokensFromConsensusPower(500) validator, _ = validator.AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validator) - keeper.SetValidatorByPowerIndex(ctx, validator) + app.StakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) // verify initial Tendermint updates are correct - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - validator, _ = keeper.GetValidator(ctx, validator.OperatorAddress) - validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) - validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) - require.Equal(t, len(validators)+1, len(updates)) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)+1) + validator, _ = app.StakingKeeper.GetValidator(ctx, validator.GetOperator()) + validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2]) } func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - params := keeper.GetParams(ctx) - params.MaxValidators = uint16(2) + app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20) + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = uint32(2) - keeper.SetParams(ctx, params) + app.StakingKeeper.SetParams(ctx, params) powers := []int64{100, 200, 300} var validators [3]types.Validator @@ -968,70 +997,67 @@ func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker}) + validators[i] = newMonikerValidator(t, valAddr, valPubKey, moniker) tokens := sdk.TokensFromConsensusPower(power) validators[i], _ = validators[i].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[i]) - keeper.SetValidatorByPowerIndex(ctx, validators[i]) + app.StakingKeeper.SetValidator(ctx, validators[i]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i]) } // verify initial Tendermint updates are correct - updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(updates)) - validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) - validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2) + validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator()) + validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) // delegate to validator with lowest power but not enough to bond ctx = ctx.WithBlockHeight(1) var found bool - validators[0], found = keeper.GetValidator(ctx, validators[0].OperatorAddress) + validators[0], found = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator()) require.True(t, found) - keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) tokens := sdk.TokensFromConsensusPower(1) validators[0], _ = validators[0].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[0]) - keeper.SetValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) // verify initial Tendermint updates are correct - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) // create a series of events that will bond and unbond the validator with // lowest power in a single block context (height) ctx = ctx.WithBlockHeight(2) - validators[1], found = keeper.GetValidator(ctx, validators[1].OperatorAddress) + validators[1], found = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator()) require.True(t, found) - keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0]) validators[0], _ = validators[0].RemoveDelShares(validators[0].DelegatorShares) - keeper.SetValidator(ctx, validators[0]) - keeper.SetValidatorByPowerIndex(ctx, validators[0]) - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 0, len(updates)) + app.StakingKeeper.SetValidator(ctx, validators[0]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0]) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) - keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1]) tokens = sdk.TokensFromConsensusPower(250) validators[1], _ = validators[1].AddTokensFromDel(tokens) - keeper.SetValidator(ctx, validators[1]) - keeper.SetValidatorByPowerIndex(ctx, validators[1]) + app.StakingKeeper.SetValidator(ctx, validators[1]) + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1]) // verify initial Tendermint updates are correct - updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(updates)) + updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1) require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) - require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0) } func TestUpdateValidatorCommission(t *testing.T) { - ctx, _, keeper, _ := CreateTestInput(t, false, 1000) - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Now().UTC()}) + app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20) + ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Now().UTC()}) commission1 := types.NewCommissionWithTime( sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), @@ -1039,14 +1065,14 @@ func TestUpdateValidatorCommission(t *testing.T) { ) commission2 := types.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1)) - val1 := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - val2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + val1 := teststaking.NewValidator(t, addrVals[0], PKs[0]) + val2 := teststaking.NewValidator(t, addrVals[1], PKs[1]) val1, _ = val1.SetInitialCommission(commission1) val2, _ = val2.SetInitialCommission(commission2) - keeper.SetValidator(ctx, val1) - keeper.SetValidator(ctx, val2) + app.StakingKeeper.SetValidator(ctx, val1) + app.StakingKeeper.SetValidator(ctx, val2) testCases := []struct { validator types.Validator @@ -1061,14 +1087,14 @@ func TestUpdateValidatorCommission(t *testing.T) { } for i, tc := range testCases { - commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) + commission, err := app.StakingKeeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) if tc.expectedErr { require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate) } else { tc.validator.Commission = commission - keeper.SetValidator(ctx, tc.validator) - val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddress) + app.StakingKeeper.SetValidator(ctx, tc.validator) + val, found := app.StakingKeeper.GetValidator(ctx, tc.validator.GetOperator()) require.True(t, found, "expected to find validator for test case #%d with rate: %s", i, tc.newRate, @@ -1085,3 +1111,12 @@ func TestUpdateValidatorCommission(t *testing.T) { } } } + +func applyValidatorSetUpdates(t *testing.T, ctx sdk.Context, k keeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate { + updates, err := k.ApplyAndReturnValidatorSetUpdates(ctx) + require.NoError(t, err) + if expectedUpdatesLen >= 0 { + require.Equal(t, expectedUpdatesLen, len(updates), "%v", updates) + } + return updates +} diff --git a/x/staking/legacy/v034/types.go b/x/staking/legacy/v034/types.go new file mode 100644 index 000000000000..9f8622d4bec2 --- /dev/null +++ b/x/staking/legacy/v034/types.go @@ -0,0 +1,187 @@ +// DONTCOVER +// nolint +package v034 + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "staking" +) + +// staking constants +const ( + Unbonded BondStatus = 0x00 + Unbonding BondStatus = 0x01 + Bonded BondStatus = 0x02 + + BondStatusUnbonded = "Unbonded" + BondStatusUnbonding = "Unbonding" + BondStatusBonded = "Bonded" +) + +type ( + // BondStatus is the status of a validator + BondStatus byte + + Pool struct { + NotBondedTokens sdk.Int `json:"not_bonded_tokens"` + BondedTokens sdk.Int `json:"bonded_tokens"` + } + + Params struct { + UnbondingTime time.Duration `json:"unbonding_time"` + MaxValidators uint16 `json:"max_validators"` + MaxEntries uint16 `json:"max_entries"` + BondDenom string `json:"bond_denom"` + } + + LastValidatorPower struct { + Address sdk.ValAddress + Power int64 + } + + Description struct { + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + Details string `json:"details"` + } + + Commission struct { + Rate sdk.Dec `json:"rate"` + MaxRate sdk.Dec `json:"max_rate"` + MaxChangeRate sdk.Dec `json:"max_change_rate"` + UpdateTime time.Time `json:"update_time"` + } + + bechValidator struct { + OperatorAddress sdk.ValAddress `json:"operator_address"` // the bech32 address of the validator's operator + ConsPubKey string `json:"consensus_pubkey"` // the bech32 consensus public key of the validator + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? + Status BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) + Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators + Description Description `json:"description"` // description terms for the validator + UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding + UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding + Commission Commission `json:"commission"` // commission parameters + MinSelfDelegation sdk.Int `json:"min_self_delegation"` // minimum self delegation + } + + Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address"` + ConsPubKey cryptotypes.PubKey `json:"consensus_pubkey"` + Jailed bool `json:"jailed"` + Status BondStatus `json:"status"` + Tokens sdk.Int `json:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares"` + Description Description `json:"description"` + UnbondingHeight int64 `json:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time"` + Commission Commission `json:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation"` + } + + Validators []Validator + + Delegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Shares sdk.Dec `json:"shares"` + } + + Delegations []Delegation + + UnbondingDelegationEntry struct { + CreationHeight int64 `json:"creation_height"` + CompletionTime time.Time `json:"completion_time"` + InitialBalance sdk.Int `json:"initial_balance"` + Balance sdk.Int `json:"balance"` + } + + UnbondingDelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Entries []UnbondingDelegationEntry `json:"entries"` + } + + RedelegationEntry struct { + CreationHeight int64 `json:"creation_height"` + CompletionTime time.Time `json:"completion_time"` + InitialBalance sdk.Int `json:"initial_balance"` + SharesDst sdk.Dec `json:"shares_dst"` + } + + Redelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` + ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` + Entries []RedelegationEntry `json:"entries"` + } + + GenesisState struct { + Pool Pool `json:"pool"` + Params Params `json:"params"` + LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []LastValidatorPower `json:"last_validator_powers"` + Validators Validators `json:"validators"` + Delegations Delegations `json:"delegations"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations"` + Exported bool `json:"exported"` + } +) + +func (v Validator) MarshalJSON() ([]byte, error) { + bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) + if err != nil { + return nil, err + } + + return legacy.Cdc.MarshalJSON(bechValidator{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: bechConsPubKey, + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + MinSelfDelegation: v.MinSelfDelegation, + Commission: v.Commission, + }) +} + +// UnmarshalJSON unmarshals the validator from JSON using Bech32 +func (v *Validator) UnmarshalJSON(data []byte) error { + bv := &bechValidator{} + if err := legacy.Cdc.UnmarshalJSON(data, bv); err != nil { + return err + } + consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) + if err != nil { + return err + } + + *v = Validator{ + OperatorAddress: bv.OperatorAddress, + ConsPubKey: consPubKey, + Jailed: bv.Jailed, + Tokens: bv.Tokens, + Status: bv.Status, + DelegatorShares: bv.DelegatorShares, + Description: bv.Description, + UnbondingHeight: bv.UnbondingHeight, + UnbondingCompletionTime: bv.UnbondingCompletionTime, + Commission: bv.Commission, + MinSelfDelegation: bv.MinSelfDelegation, + } + return nil +} diff --git a/x/staking/legacy/v036/migrate.go b/x/staking/legacy/v036/migrate.go new file mode 100644 index 000000000000..cc4d7a4089b6 --- /dev/null +++ b/x/staking/legacy/v036/migrate.go @@ -0,0 +1,52 @@ +// DONTCOVER +// nolint +package v036 + +import ( + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. All entries are identical except for validator slashing events +// which now include the period. +func Migrate(oldGenState v034staking.GenesisState) GenesisState { + return NewGenesisState( + oldGenState.Params, + oldGenState.LastTotalPower, + oldGenState.LastValidatorPowers, + migrateValidators(oldGenState.Validators), + oldGenState.Delegations, + oldGenState.UnbondingDelegations, + oldGenState.Redelegations, + oldGenState.Exported, + ) +} + +func migrateValidators(oldValidators v034staking.Validators) Validators { + validators := make(Validators, len(oldValidators)) + + for i, val := range oldValidators { + validators[i] = Validator{ + OperatorAddress: val.OperatorAddress, + ConsPubKey: val.ConsPubKey, + Jailed: val.Jailed, + Status: val.Status, + Tokens: val.Tokens, + DelegatorShares: val.DelegatorShares, + Description: val.Description, + UnbondingHeight: val.UnbondingHeight, + UnbondingCompletionTime: val.UnbondingCompletionTime, + Commission: Commission{ + CommissionRates: CommissionRates{ + Rate: val.Commission.Rate, + MaxRate: val.Commission.MaxRate, + MaxChangeRate: val.Commission.MaxChangeRate, + }, + UpdateTime: val.Commission.UpdateTime, + }, + MinSelfDelegation: val.MinSelfDelegation, + } + } + + return validators +} diff --git a/x/staking/legacy/v036/migrate_test.go b/x/staking/legacy/v036/migrate_test.go new file mode 100644 index 000000000000..37540427859c --- /dev/null +++ b/x/staking/legacy/v036/migrate_test.go @@ -0,0 +1,104 @@ +package v036_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" +) + +func TestMigrate(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + consPubKeyEd := ed25519.GenPrivKeyFromSecret([]byte("val0")).PubKey() + consPubKeySecp := secp256k1.GenPrivKeyFromSecret([]byte("val1")).PubKey() + stakingGenState := v034staking.GenesisState{ + Validators: v034staking.Validators{ + v034staking.Validator{ + ConsPubKey: consPubKeyEd, + Status: v034staking.Unbonded, + }, v034staking.Validator{ + ConsPubKey: consPubKeySecp, + Status: v034staking.Unbonded, + }, + }, + } + + migrated := v036staking.Migrate(stakingGenState) + + json, err := aminoCdc.MarshalJSONIndent(migrated, "", " ") + require.NoError(t, err) + + expectedJSON := `{ + "params": { + "unbonding_time": "0", + "max_validators": 0, + "max_entries": 0, + "bond_denom": "" + }, + "last_total_power": "0", + "last_validator_powers": null, + "validators": [ + { + "operator_address": "", + "consensus_pubkey": "cosmosvalconspub1zcjduepq9ymett3nlv6fytn7lqxzd3q3ckvd79eqlcf3wkhgamcl4rzghesq83ecpx", + "jailed": false, + "status": 0, + "tokens": "0", + "delegator_shares": "0", + "description": { + "moniker": "", + "identity": "", + "website": "", + "details": "" + }, + "unbonding_height": "0", + "unbonding_time": "0001-01-01T00:00:00Z", + "commission": { + "commission_rates": { + "rate": "0", + "max_rate": "0", + "max_change_rate": "0" + }, + "update_time": "0001-01-01T00:00:00Z" + }, + "min_self_delegation": "0" + }, + { + "operator_address": "", + "consensus_pubkey": "cosmosvalconspub1addwnpepqwfxk5k5pugwz3quqyzvzupefm3589tw6x9dkzjdkuzn7hgpz33ag84e406", + "jailed": false, + "status": 0, + "tokens": "0", + "delegator_shares": "0", + "description": { + "moniker": "", + "identity": "", + "website": "", + "details": "" + }, + "unbonding_height": "0", + "unbonding_time": "0001-01-01T00:00:00Z", + "commission": { + "commission_rates": { + "rate": "0", + "max_rate": "0", + "max_change_rate": "0" + }, + "update_time": "0001-01-01T00:00:00Z" + }, + "min_self_delegation": "0" + } + ], + "delegations": null, + "unbonding_delegations": null, + "redelegations": null, + "exported": false +}` + + require.Equal(t, expectedJSON, string(json)) +} diff --git a/x/staking/legacy/v036/types.go b/x/staking/legacy/v036/types.go new file mode 100644 index 000000000000..b433b0070100 --- /dev/null +++ b/x/staking/legacy/v036/types.go @@ -0,0 +1,134 @@ +// DONTCOVER +// nolint +package v036 + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" +) + +const ( + ModuleName = "staking" +) + +type ( + Commission struct { + CommissionRates `json:"commission_rates" yaml:"commission_rates"` + UpdateTime time.Time `json:"update_time" yaml:"update_time"` + } + + CommissionRates struct { + Rate sdk.Dec `json:"rate" yaml:"rate"` + MaxRate sdk.Dec `json:"max_rate" yaml:"max_rate"` + MaxChangeRate sdk.Dec `json:"max_change_rate" yaml:"max_change_rate"` + } + + Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` + ConsPubKey cryptotypes.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` + Jailed bool `json:"jailed" yaml:"jailed"` + Status v034staking.BondStatus `json:"status" yaml:"status"` + Tokens sdk.Int `json:"tokens" yaml:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` + Description v034staking.Description `json:"description" yaml:"description"` + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` + Commission Commission `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + } + + bechValidator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` + ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` + Jailed bool `json:"jailed" yaml:"jailed"` + Status v034staking.BondStatus `json:"status" yaml:"status"` + Tokens sdk.Int `json:"tokens" yaml:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` + Description v034staking.Description `json:"description" yaml:"description"` + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` + Commission Commission `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + } + + Validators []Validator + + GenesisState struct { + Params v034staking.Params `json:"params"` + LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []v034staking.LastValidatorPower `json:"last_validator_powers"` + Validators Validators `json:"validators"` + Delegations v034staking.Delegations `json:"delegations"` + UnbondingDelegations []v034staking.UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []v034staking.Redelegation `json:"redelegations"` + Exported bool `json:"exported"` + } +) + +func NewGenesisState( + params v034staking.Params, lastTotalPower sdk.Int, lastValPowers []v034staking.LastValidatorPower, + validators Validators, delegations v034staking.Delegations, + ubds []v034staking.UnbondingDelegation, reds []v034staking.Redelegation, exported bool, +) GenesisState { + + return GenesisState{ + Params: params, + LastTotalPower: lastTotalPower, + LastValidatorPowers: lastValPowers, + Validators: validators, + Delegations: delegations, + UnbondingDelegations: ubds, + Redelegations: reds, + Exported: exported, + } +} + +func (v Validator) MarshalJSON() ([]byte, error) { + bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) + if err != nil { + return nil, err + } + + return legacy.Cdc.MarshalJSON(bechValidator{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: bechConsPubKey, + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + MinSelfDelegation: v.MinSelfDelegation, + Commission: v.Commission, + }) +} + +func (v *Validator) UnmarshalJSON(data []byte) error { + bv := &bechValidator{} + if err := legacy.Cdc.UnmarshalJSON(data, bv); err != nil { + return err + } + consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) + if err != nil { + return err + } + *v = Validator{ + OperatorAddress: bv.OperatorAddress, + ConsPubKey: consPubKey, + Jailed: bv.Jailed, + Tokens: bv.Tokens, + Status: bv.Status, + DelegatorShares: bv.DelegatorShares, + Description: bv.Description, + UnbondingHeight: bv.UnbondingHeight, + UnbondingCompletionTime: bv.UnbondingCompletionTime, + Commission: bv.Commission, + MinSelfDelegation: bv.MinSelfDelegation, + } + return nil +} diff --git a/x/staking/legacy/v038/migrate.go b/x/staking/legacy/v038/migrate.go new file mode 100644 index 000000000000..21029881a3fd --- /dev/null +++ b/x/staking/legacy/v038/migrate.go @@ -0,0 +1,51 @@ +// DONTCOVER +// nolint +package v038 + +import ( + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" +) + +// Migrate accepts exported genesis state from v0.36 or v0.37 and migrates it to +// v0.38 genesis state. All entries are identical except for validator descriptions +// which now include a security contact. +func Migrate(oldGenState v036staking.GenesisState) GenesisState { + return NewGenesisState( + oldGenState.Params, + oldGenState.LastTotalPower, + oldGenState.LastValidatorPowers, + migrateValidators(oldGenState.Validators), + oldGenState.Delegations, + oldGenState.UnbondingDelegations, + oldGenState.Redelegations, + oldGenState.Exported, + ) +} + +func migrateValidators(oldValidators v036staking.Validators) Validators { + validators := make(Validators, len(oldValidators)) + + for i, val := range oldValidators { + validators[i] = Validator{ + OperatorAddress: val.OperatorAddress, + ConsPubKey: val.ConsPubKey, + Jailed: val.Jailed, + Status: val.Status, + Tokens: val.Tokens, + DelegatorShares: val.DelegatorShares, + Description: NewDescription( + val.Description.Moniker, + val.Description.Identity, + val.Description.Website, + "", // security contact field + val.Description.Details, + ), + UnbondingHeight: val.UnbondingHeight, + UnbondingCompletionTime: val.UnbondingCompletionTime, + Commission: val.Commission, + MinSelfDelegation: val.MinSelfDelegation, + } + } + + return validators +} diff --git a/x/staking/legacy/v038/types.go b/x/staking/legacy/v038/types.go new file mode 100644 index 000000000000..58ffa1351ba5 --- /dev/null +++ b/x/staking/legacy/v038/types.go @@ -0,0 +1,160 @@ +// DONTCOVER +// nolint +package v038 + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" +) + +const ( + ModuleName = "staking" +) + +type ( + Description struct { + Moniker string `json:"moniker" yaml:"moniker"` + Identity string `json:"identity" yaml:"identity"` + Website string `json:"website" yaml:"website"` + SecurityContact string `json:"security_contact" yaml:"security_contact"` + Details string `json:"details" yaml:"details"` + } + + Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` + ConsPubKey cryptotypes.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` + Jailed bool `json:"jailed" yaml:"jailed"` + Status v034staking.BondStatus `json:"status" yaml:"status"` + Tokens sdk.Int `json:"tokens" yaml:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` + Description Description `json:"description" yaml:"description"` + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` + Commission v036staking.Commission `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + } + + bechValidator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` + ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` + Jailed bool `json:"jailed" yaml:"jailed"` + Status v034staking.BondStatus `json:"status" yaml:"status"` + Tokens sdk.Int `json:"tokens" yaml:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` + Description Description `json:"description" yaml:"description"` + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` + Commission v036staking.Commission `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + } + + Validators []Validator + + Params struct { + UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` // time duration of unbonding + MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` // maximum number of validators (max uint16 = 65535) + MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio) + HistoricalEntries uint16 `json:"historical_entries" yaml:"historical_entries"` // number of historical entries to persist + BondDenom string `json:"bond_denom" yaml:"bond_denom"` // bondable coin denomination + } + + GenesisState struct { + Params Params `json:"params"` + LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []v034staking.LastValidatorPower `json:"last_validator_powers"` + Validators Validators `json:"validators"` + Delegations v034staking.Delegations `json:"delegations"` + UnbondingDelegations []v034staking.UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []v034staking.Redelegation `json:"redelegations"` + Exported bool `json:"exported"` + } +) + +// NewDescription creates a new Description object +func NewDescription(moniker, identity, website, securityContact, details string) Description { + return Description{ + Moniker: moniker, + Identity: identity, + Website: website, + SecurityContact: securityContact, + Details: details, + } +} + +// NewGenesisState creates a new GenesisState object +func NewGenesisState( + params v034staking.Params, lastTotalPower sdk.Int, lastValPowers []v034staking.LastValidatorPower, + validators Validators, delegations v034staking.Delegations, + ubds []v034staking.UnbondingDelegation, reds []v034staking.Redelegation, exported bool, +) GenesisState { + + return GenesisState{ + Params: Params{ + UnbondingTime: params.UnbondingTime, + MaxValidators: params.MaxValidators, + MaxEntries: params.MaxEntries, + BondDenom: params.BondDenom, + HistoricalEntries: 0, + }, + LastTotalPower: lastTotalPower, + LastValidatorPowers: lastValPowers, + Validators: validators, + Delegations: delegations, + UnbondingDelegations: ubds, + Redelegations: reds, + Exported: exported, + } +} + +// MarshalJSON marshals the validator to JSON using Bech32 +func (v Validator) MarshalJSON() ([]byte, error) { + bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) + if err != nil { + return nil, err + } + + return legacy.Cdc.MarshalJSON(bechValidator{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: bechConsPubKey, + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + MinSelfDelegation: v.MinSelfDelegation, + Commission: v.Commission, + }) +} + +// UnmarshalJSON unmarshals the validator from JSON using Bech32 +func (v *Validator) UnmarshalJSON(data []byte) error { + bv := &bechValidator{} + if err := legacy.Cdc.UnmarshalJSON(data, bv); err != nil { + return err + } + consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) + if err != nil { + return err + } + *v = Validator{ + OperatorAddress: bv.OperatorAddress, + ConsPubKey: consPubKey, + Jailed: bv.Jailed, + Tokens: bv.Tokens, + Status: bv.Status, + DelegatorShares: bv.DelegatorShares, + Description: bv.Description, + UnbondingHeight: bv.UnbondingHeight, + UnbondingCompletionTime: bv.UnbondingCompletionTime, + Commission: bv.Commission, + MinSelfDelegation: bv.MinSelfDelegation, + } + return nil +} diff --git a/x/staking/legacy/v040/migrate.go b/x/staking/legacy/v040/migrate.go new file mode 100644 index 000000000000..b0746fa363a4 --- /dev/null +++ b/x/staking/legacy/v040/migrate.go @@ -0,0 +1,141 @@ +package v040 + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" + v040staking "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func migrateBondStatus(oldStatus v034staking.BondStatus) v040staking.BondStatus { + switch oldStatus { + case v034staking.Unbonded: + return v040staking.Unbonded + + case v034staking.Unbonding: + return v040staking.Unbonding + + case v034staking.Bonded: + return v040staking.Bonded + + default: + panic(fmt.Errorf("invalid bond status %d", oldStatus)) + } +} + +// Migrate accepts exported v0.38 x/staking genesis state and migrates it to +// v0.40 x/staking genesis state. The migration includes: +// +// - Convert addresses from bytes to bech32 strings. +// - Update BondStatus staking constants. +// - Re-encode in v0.40 GenesisState. +func Migrate(stakingState v038staking.GenesisState) *v040staking.GenesisState { + newLastValidatorPowers := make([]v040staking.LastValidatorPower, len(stakingState.LastValidatorPowers)) + for i, oldLastValidatorPower := range stakingState.LastValidatorPowers { + newLastValidatorPowers[i] = v040staking.LastValidatorPower{ + Address: oldLastValidatorPower.Address.String(), + Power: oldLastValidatorPower.Power, + } + } + + newValidators := make([]v040staking.Validator, len(stakingState.Validators)) + for i, oldValidator := range stakingState.Validators { + pkAny, err := codectypes.NewAnyWithValue(oldValidator.ConsPubKey) + if err != nil { + panic(fmt.Sprintf("Can't pack validator consensus PK as Any: %s", err)) + } + newValidators[i] = v040staking.Validator{ + OperatorAddress: oldValidator.OperatorAddress.String(), + ConsensusPubkey: pkAny, + Jailed: oldValidator.Jailed, + Status: migrateBondStatus(oldValidator.Status), + Tokens: oldValidator.Tokens, + DelegatorShares: oldValidator.DelegatorShares, + Description: v040staking.Description{ + Moniker: oldValidator.Description.Moniker, + Identity: oldValidator.Description.Identity, + Website: oldValidator.Description.Website, + SecurityContact: oldValidator.Description.SecurityContact, + Details: oldValidator.Description.Details, + }, + UnbondingHeight: oldValidator.UnbondingHeight, + UnbondingTime: oldValidator.UnbondingCompletionTime, + Commission: v040staking.Commission{ + CommissionRates: v040staking.CommissionRates{ + Rate: oldValidator.Commission.Rate, + MaxRate: oldValidator.Commission.MaxRate, + MaxChangeRate: oldValidator.Commission.MaxChangeRate, + }, + UpdateTime: oldValidator.Commission.UpdateTime, + }, + MinSelfDelegation: oldValidator.MinSelfDelegation, + } + } + + newDelegations := make([]v040staking.Delegation, len(stakingState.Delegations)) + for i, oldDelegation := range stakingState.Delegations { + newDelegations[i] = v040staking.Delegation{ + DelegatorAddress: oldDelegation.DelegatorAddress.String(), + ValidatorAddress: oldDelegation.ValidatorAddress.String(), + Shares: oldDelegation.Shares, + } + } + + newUnbondingDelegations := make([]v040staking.UnbondingDelegation, len(stakingState.UnbondingDelegations)) + for i, oldUnbondingDelegation := range stakingState.UnbondingDelegations { + newEntries := make([]v040staking.UnbondingDelegationEntry, len(oldUnbondingDelegation.Entries)) + for j, oldEntry := range oldUnbondingDelegation.Entries { + newEntries[j] = v040staking.UnbondingDelegationEntry{ + CreationHeight: oldEntry.CreationHeight, + CompletionTime: oldEntry.CompletionTime, + InitialBalance: oldEntry.InitialBalance, + Balance: oldEntry.Balance, + } + } + + newUnbondingDelegations[i] = v040staking.UnbondingDelegation{ + DelegatorAddress: oldUnbondingDelegation.DelegatorAddress.String(), + ValidatorAddress: oldUnbondingDelegation.ValidatorAddress.String(), + Entries: newEntries, + } + } + + newRedelegations := make([]v040staking.Redelegation, len(stakingState.Redelegations)) + for i, oldRedelegation := range stakingState.Redelegations { + newEntries := make([]v040staking.RedelegationEntry, len(oldRedelegation.Entries)) + for j, oldEntry := range oldRedelegation.Entries { + newEntries[j] = v040staking.RedelegationEntry{ + CreationHeight: oldEntry.CreationHeight, + CompletionTime: oldEntry.CompletionTime, + InitialBalance: oldEntry.InitialBalance, + SharesDst: oldEntry.SharesDst, + } + } + + newRedelegations[i] = v040staking.Redelegation{ + DelegatorAddress: oldRedelegation.DelegatorAddress.String(), + ValidatorSrcAddress: oldRedelegation.ValidatorSrcAddress.String(), + ValidatorDstAddress: oldRedelegation.ValidatorDstAddress.String(), + Entries: newEntries, + } + } + + return &v040staking.GenesisState{ + Params: v040staking.Params{ + UnbondingTime: stakingState.Params.UnbondingTime, + MaxValidators: uint32(stakingState.Params.MaxValidators), + MaxEntries: uint32(stakingState.Params.MaxEntries), + HistoricalEntries: uint32(stakingState.Params.HistoricalEntries), + BondDenom: stakingState.Params.BondDenom, + }, + LastTotalPower: stakingState.LastTotalPower, + LastValidatorPowers: newLastValidatorPowers, + Validators: newValidators, + Delegations: newDelegations, + UnbondingDelegations: newUnbondingDelegations, + Redelegations: newRedelegations, + Exported: stakingState.Exported, + } +} diff --git a/x/staking/legacy/v040/migrate_test.go b/x/staking/legacy/v040/migrate_test.go new file mode 100644 index 000000000000..2be5e80dbc31 --- /dev/null +++ b/x/staking/legacy/v040/migrate_test.go @@ -0,0 +1,96 @@ +package v040_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/simapp" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" + v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" + v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + consPubKey := ed25519.GenPrivKeyFromSecret([]byte("val0")).PubKey() + stakingGenState := v038staking.GenesisState{ + Validators: v038staking.Validators{v038staking.Validator{ + ConsPubKey: consPubKey, + Status: v034staking.Unbonded, + }}, + } + + migrated := v040staking.Migrate(stakingGenState) + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + require.NoError(t, err) + indentedBz, err := json.MarshalIndent(jsonObj, "", " ") + require.NoError(t, err) + + // Make sure about: + // - consensus_pubkey: should be an any + // - validator's status should be 1 (new unbonded) + expected := `{ + "delegations": [], + "exported": false, + "last_total_power": "0", + "last_validator_powers": [], + "params": { + "bond_denom": "", + "historical_entries": 0, + "max_entries": 0, + "max_validators": 0, + "unbonding_time": "0s" + }, + "redelegations": [], + "unbonding_delegations": [], + "validators": [ + { + "commission": { + "commission_rates": { + "max_change_rate": "0", + "max_rate": "0", + "rate": "0" + }, + "update_time": "0001-01-01T00:00:00Z" + }, + "consensus_pubkey": { + "@type": "/cosmos.crypto.ed25519.PubKey", + "key": "KTeVrjP7NJIufvgMJsQRxZjfFyD+Exda6O7x+oxIvmA=" + }, + "delegator_shares": "0", + "description": { + "details": "", + "identity": "", + "moniker": "", + "security_contact": "", + "website": "" + }, + "jailed": false, + "min_self_delegation": "0", + "operator_address": "", + "status": "BOND_STATUS_UNBONDED", + "tokens": "0", + "unbonding_height": "0", + "unbonding_time": "0001-01-01T00:00:00Z" + } + ] +}` + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/staking/legacy/v040/types.go b/x/staking/legacy/v040/types.go new file mode 100644 index 000000000000..6ad280e277e5 --- /dev/null +++ b/x/staking/legacy/v040/types.go @@ -0,0 +1,5 @@ +package v040 + +const ( + ModuleName = "staking" +) diff --git a/x/staking/legacy/v0_34/types.go b/x/staking/legacy/v0_34/types.go deleted file mode 100644 index 013edb792d9e..000000000000 --- a/x/staking/legacy/v0_34/types.go +++ /dev/null @@ -1,173 +0,0 @@ -// DONTCOVER -// nolint -package v0_34 - -import ( - "time" - - "github.com/tendermint/tendermint/crypto" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - ModuleName = "staking" -) - -type ( - Pool struct { - NotBondedTokens sdk.Int `json:"not_bonded_tokens"` - BondedTokens sdk.Int `json:"bonded_tokens"` - } - - Params struct { - UnbondingTime time.Duration `json:"unbonding_time"` - MaxValidators uint16 `json:"max_validators"` - MaxEntries uint16 `json:"max_entries"` - BondDenom string `json:"bond_denom"` - } - - LastValidatorPower struct { - Address sdk.ValAddress - Power int64 - } - - Description struct { - Moniker string `json:"moniker"` - Identity string `json:"identity"` - Website string `json:"website"` - Details string `json:"details"` - } - - Commission struct { - Rate sdk.Dec `json:"rate"` - MaxRate sdk.Dec `json:"max_rate"` - MaxChangeRate sdk.Dec `json:"max_change_rate"` - UpdateTime time.Time `json:"update_time"` - } - - bechValidator struct { - OperatorAddress sdk.ValAddress `json:"operator_address"` // the bech32 address of the validator's operator - ConsPubKey string `json:"consensus_pubkey"` // the bech32 consensus public key of the validator - Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? - Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators - Description Description `json:"description"` // description terms for the validator - UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding - UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding - Commission Commission `json:"commission"` // commission parameters - MinSelfDelegation sdk.Int `json:"min_self_delegation"` // minimum self delegation - } - - Validator struct { - OperatorAddress sdk.ValAddress `json:"operator_address"` - ConsPubKey crypto.PubKey `json:"consensus_pubkey"` - Jailed bool `json:"jailed"` - Status sdk.BondStatus `json:"status"` - Tokens sdk.Int `json:"tokens"` - DelegatorShares sdk.Dec `json:"delegator_shares"` - Description Description `json:"description"` - UnbondingHeight int64 `json:"unbonding_height"` - UnbondingCompletionTime time.Time `json:"unbonding_time"` - Commission Commission `json:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation"` - } - - Validators []Validator - - Delegation struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Shares sdk.Dec `json:"shares"` - } - - Delegations []Delegation - - UnbondingDelegationEntry struct { - CreationHeight int64 `json:"creation_height"` - CompletionTime time.Time `json:"completion_time"` - InitialBalance sdk.Int `json:"initial_balance"` - Balance sdk.Int `json:"balance"` - } - - UnbondingDelegation struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address"` - Entries []UnbondingDelegationEntry `json:"entries"` - } - - RedelegationEntry struct { - CreationHeight int64 `json:"creation_height"` - CompletionTime time.Time `json:"completion_time"` - InitialBalance sdk.Int `json:"initial_balance"` - SharesDst sdk.Dec `json:"shares_dst"` - } - - Redelegation struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address"` - ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` - ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` - Entries []RedelegationEntry `json:"entries"` - } - - GenesisState struct { - Pool Pool `json:"pool"` - Params Params `json:"params"` - LastTotalPower sdk.Int `json:"last_total_power"` - LastValidatorPowers []LastValidatorPower `json:"last_validator_powers"` - Validators Validators `json:"validators"` - Delegations Delegations `json:"delegations"` - UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` - Redelegations []Redelegation `json:"redelegations"` - Exported bool `json:"exported"` - } -) - -func (v Validator) MarshalJSON() ([]byte, error) { - bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) - if err != nil { - return nil, err - } - - return codec.Cdc.MarshalJSON(bechValidator{ - OperatorAddress: v.OperatorAddress, - ConsPubKey: bechConsPubKey, - Jailed: v.Jailed, - Status: v.Status, - Tokens: v.Tokens, - DelegatorShares: v.DelegatorShares, - Description: v.Description, - UnbondingHeight: v.UnbondingHeight, - UnbondingCompletionTime: v.UnbondingCompletionTime, - MinSelfDelegation: v.MinSelfDelegation, - Commission: v.Commission, - }) -} - -// UnmarshalJSON unmarshals the validator from JSON using Bech32 -func (v *Validator) UnmarshalJSON(data []byte) error { - bv := &bechValidator{} - if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { - return err - } - consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) - if err != nil { - return err - } - *v = Validator{ - OperatorAddress: bv.OperatorAddress, - ConsPubKey: consPubKey, - Jailed: bv.Jailed, - Tokens: bv.Tokens, - Status: bv.Status, - DelegatorShares: bv.DelegatorShares, - Description: bv.Description, - UnbondingHeight: bv.UnbondingHeight, - UnbondingCompletionTime: bv.UnbondingCompletionTime, - Commission: bv.Commission, - MinSelfDelegation: bv.MinSelfDelegation, - } - return nil -} diff --git a/x/staking/legacy/v0_36/migrate.go b/x/staking/legacy/v0_36/migrate.go deleted file mode 100644 index 0d1b0fa2e7a9..000000000000 --- a/x/staking/legacy/v0_36/migrate.go +++ /dev/null @@ -1,52 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" -) - -// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 -// genesis state. All entries are identical except for validator slashing events -// which now include the period. -func Migrate(oldGenState v034staking.GenesisState) GenesisState { - return NewGenesisState( - oldGenState.Params, - oldGenState.LastTotalPower, - oldGenState.LastValidatorPowers, - migrateValidators(oldGenState.Validators), - oldGenState.Delegations, - oldGenState.UnbondingDelegations, - oldGenState.Redelegations, - oldGenState.Exported, - ) -} - -func migrateValidators(oldValidators v034staking.Validators) Validators { - validators := make(Validators, len(oldValidators)) - - for i, val := range oldValidators { - validators[i] = Validator{ - OperatorAddress: val.OperatorAddress, - ConsPubKey: val.ConsPubKey, - Jailed: val.Jailed, - Status: val.Status, - Tokens: val.Tokens, - DelegatorShares: val.DelegatorShares, - Description: val.Description, - UnbondingHeight: val.UnbondingHeight, - UnbondingCompletionTime: val.UnbondingCompletionTime, - Commission: Commission{ - CommissionRates: CommissionRates{ - Rate: val.Commission.Rate, - MaxRate: val.Commission.MaxRate, - MaxChangeRate: val.Commission.MaxChangeRate, - }, - UpdateTime: val.Commission.UpdateTime, - }, - MinSelfDelegation: val.MinSelfDelegation, - } - } - - return validators -} diff --git a/x/staking/legacy/v0_36/types.go b/x/staking/legacy/v0_36/types.go deleted file mode 100644 index 246d6b011821..000000000000 --- a/x/staking/legacy/v0_36/types.go +++ /dev/null @@ -1,135 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - "time" - - "github.com/tendermint/tendermint/crypto" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" -) - -const ( - ModuleName = "staking" -) - -type ( - Commission struct { - CommissionRates `json:"commission_rates" yaml:"commission_rates"` - UpdateTime time.Time `json:"update_time" yaml:"update_time"` - } - - CommissionRates struct { - Rate sdk.Dec `json:"rate" yaml:"rate"` - MaxRate sdk.Dec `json:"max_rate" yaml:"max_rate"` - MaxChangeRate sdk.Dec `json:"max_change_rate" yaml:"max_change_rate"` - } - - Validator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` - ConsPubKey crypto.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` - Jailed bool `json:"jailed" yaml:"jailed"` - Status sdk.BondStatus `json:"status" yaml:"status"` - Tokens sdk.Int `json:"tokens" yaml:"tokens"` - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` - Description v034staking.Description `json:"description" yaml:"description"` - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` - Commission Commission `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - } - - bechValidator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` - ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` - Jailed bool `json:"jailed" yaml:"jailed"` - Status sdk.BondStatus `json:"status" yaml:"status"` - Tokens sdk.Int `json:"tokens" yaml:"tokens"` - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` - Description v034staking.Description `json:"description" yaml:"description"` - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` - Commission Commission `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - } - - Validators []Validator - - GenesisState struct { - Params v034staking.Params `json:"params"` - LastTotalPower sdk.Int `json:"last_total_power"` - LastValidatorPowers []v034staking.LastValidatorPower `json:"last_validator_powers"` - Validators Validators `json:"validators"` - Delegations v034staking.Delegations `json:"delegations"` - UnbondingDelegations []v034staking.UnbondingDelegation `json:"unbonding_delegations"` - Redelegations []v034staking.Redelegation `json:"redelegations"` - Exported bool `json:"exported"` - } -) - -func NewGenesisState( - params v034staking.Params, lastTotalPower sdk.Int, lastValPowers []v034staking.LastValidatorPower, - validators Validators, delegations v034staking.Delegations, - ubds []v034staking.UnbondingDelegation, reds []v034staking.Redelegation, exported bool, -) GenesisState { - - return GenesisState{ - Params: params, - LastTotalPower: lastTotalPower, - LastValidatorPowers: lastValPowers, - Validators: validators, - Delegations: delegations, - UnbondingDelegations: ubds, - Redelegations: reds, - Exported: exported, - } -} - -func (v Validator) MarshalJSON() ([]byte, error) { - bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) - if err != nil { - return nil, err - } - - return codec.Cdc.MarshalJSON(bechValidator{ - OperatorAddress: v.OperatorAddress, - ConsPubKey: bechConsPubKey, - Jailed: v.Jailed, - Status: v.Status, - Tokens: v.Tokens, - DelegatorShares: v.DelegatorShares, - Description: v.Description, - UnbondingHeight: v.UnbondingHeight, - UnbondingCompletionTime: v.UnbondingCompletionTime, - MinSelfDelegation: v.MinSelfDelegation, - Commission: v.Commission, - }) -} - -func (v *Validator) UnmarshalJSON(data []byte) error { - bv := &bechValidator{} - if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { - return err - } - consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) - if err != nil { - return err - } - *v = Validator{ - OperatorAddress: bv.OperatorAddress, - ConsPubKey: consPubKey, - Jailed: bv.Jailed, - Tokens: bv.Tokens, - Status: bv.Status, - DelegatorShares: bv.DelegatorShares, - Description: bv.Description, - UnbondingHeight: bv.UnbondingHeight, - UnbondingCompletionTime: bv.UnbondingCompletionTime, - Commission: bv.Commission, - MinSelfDelegation: bv.MinSelfDelegation, - } - return nil -} diff --git a/x/staking/legacy/v0_38/migrate.go b/x/staking/legacy/v0_38/migrate.go deleted file mode 100644 index e7e368f76280..000000000000 --- a/x/staking/legacy/v0_38/migrate.go +++ /dev/null @@ -1,51 +0,0 @@ -// DONTCOVER -// nolint -package v0_38 - -import ( - v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" -) - -// Migrate accepts exported genesis state from v0.36 or v0.37 and migrates it to -// v0.38 genesis state. All entries are identical except for validator descriptions -// which now include a security contact. -func Migrate(oldGenState v036staking.GenesisState) GenesisState { - return NewGenesisState( - oldGenState.Params, - oldGenState.LastTotalPower, - oldGenState.LastValidatorPowers, - migrateValidators(oldGenState.Validators), - oldGenState.Delegations, - oldGenState.UnbondingDelegations, - oldGenState.Redelegations, - oldGenState.Exported, - ) -} - -func migrateValidators(oldValidators v036staking.Validators) Validators { - validators := make(Validators, len(oldValidators)) - - for i, val := range oldValidators { - validators[i] = Validator{ - OperatorAddress: val.OperatorAddress, - ConsPubKey: val.ConsPubKey, - Jailed: val.Jailed, - Status: val.Status, - Tokens: val.Tokens, - DelegatorShares: val.DelegatorShares, - Description: NewDescription( - val.Description.Moniker, - val.Description.Identity, - val.Description.Website, - "", // security contact field - val.Description.Details, - ), - UnbondingHeight: val.UnbondingHeight, - UnbondingCompletionTime: val.UnbondingCompletionTime, - Commission: val.Commission, - MinSelfDelegation: val.MinSelfDelegation, - } - } - - return validators -} diff --git a/x/staking/legacy/v0_38/types.go b/x/staking/legacy/v0_38/types.go deleted file mode 100644 index b3c27e8b47cd..000000000000 --- a/x/staking/legacy/v0_38/types.go +++ /dev/null @@ -1,147 +0,0 @@ -// DONTCOVER -// nolint -package v0_38 - -import ( - "time" - - "github.com/tendermint/tendermint/crypto" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" - v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" -) - -const ( - ModuleName = "staking" -) - -type ( - Description struct { - Moniker string `json:"moniker" yaml:"moniker"` - Identity string `json:"identity" yaml:"identity"` - Website string `json:"website" yaml:"website"` - SecurityContact string `json:"security_contact" yaml:"security_contact"` - Details string `json:"details" yaml:"details"` - } - - Validator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` - ConsPubKey crypto.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` - Jailed bool `json:"jailed" yaml:"jailed"` - Status sdk.BondStatus `json:"status" yaml:"status"` - Tokens sdk.Int `json:"tokens" yaml:"tokens"` - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` - Description Description `json:"description" yaml:"description"` - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` - Commission v036staking.Commission `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - } - - bechValidator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` - ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` - Jailed bool `json:"jailed" yaml:"jailed"` - Status sdk.BondStatus `json:"status" yaml:"status"` - Tokens sdk.Int `json:"tokens" yaml:"tokens"` - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` - Description Description `json:"description" yaml:"description"` - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` - Commission v036staking.Commission `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - } - - Validators []Validator - - GenesisState struct { - Params v034staking.Params `json:"params"` - LastTotalPower sdk.Int `json:"last_total_power"` - LastValidatorPowers []v034staking.LastValidatorPower `json:"last_validator_powers"` - Validators Validators `json:"validators"` - Delegations v034staking.Delegations `json:"delegations"` - UnbondingDelegations []v034staking.UnbondingDelegation `json:"unbonding_delegations"` - Redelegations []v034staking.Redelegation `json:"redelegations"` - Exported bool `json:"exported"` - } -) - -// NewDescription creates a new Description object -func NewDescription(moniker, identity, website, securityContact, details string) Description { - return Description{ - Moniker: moniker, - Identity: identity, - Website: website, - SecurityContact: securityContact, - Details: details, - } -} - -// NewGenesisState creates a new GenesisState object -func NewGenesisState( - params v034staking.Params, lastTotalPower sdk.Int, lastValPowers []v034staking.LastValidatorPower, - validators Validators, delegations v034staking.Delegations, - ubds []v034staking.UnbondingDelegation, reds []v034staking.Redelegation, exported bool, -) GenesisState { - - return GenesisState{ - Params: params, - LastTotalPower: lastTotalPower, - LastValidatorPowers: lastValPowers, - Validators: validators, - Delegations: delegations, - UnbondingDelegations: ubds, - Redelegations: reds, - Exported: exported, - } -} - -// MarshalJSON marshals the validator to JSON using Bech32 -func (v Validator) MarshalJSON() ([]byte, error) { - bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) - if err != nil { - return nil, err - } - - return codec.Cdc.MarshalJSON(bechValidator{ - OperatorAddress: v.OperatorAddress, - ConsPubKey: bechConsPubKey, - Jailed: v.Jailed, - Status: v.Status, - Tokens: v.Tokens, - DelegatorShares: v.DelegatorShares, - Description: v.Description, - UnbondingHeight: v.UnbondingHeight, - UnbondingCompletionTime: v.UnbondingCompletionTime, - MinSelfDelegation: v.MinSelfDelegation, - Commission: v.Commission, - }) -} - -// UnmarshalJSON unmarshals the validator from JSON using Bech32 -func (v *Validator) UnmarshalJSON(data []byte) error { - bv := &bechValidator{} - if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { - return err - } - consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) - if err != nil { - return err - } - *v = Validator{ - OperatorAddress: bv.OperatorAddress, - ConsPubKey: consPubKey, - Jailed: bv.Jailed, - Tokens: bv.Tokens, - Status: bv.Status, - DelegatorShares: bv.DelegatorShares, - Description: bv.Description, - UnbondingHeight: bv.UnbondingHeight, - UnbondingCompletionTime: bv.UnbondingCompletionTime, - Commission: bv.Commission, - MinSelfDelegation: bv.MinSelfDelegation, - } - return nil -} diff --git a/x/staking/module.go b/x/staking/module.go index 0f39343af843..f2e422117476 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -1,26 +1,26 @@ package staking import ( + "context" "encoding/json" "fmt" "math/rand" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/gorilla/mux" "github.com/spf13/cobra" - flag "github.com/spf13/pflag" - abci "github.com/tendermint/tendermint/abci/types" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/client/context" + "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" "github.com/cosmos/cosmos-sdk/types/module" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - sim "github.com/cosmos/cosmos-sdk/x/simulation" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/staking/client/cli" "github.com/cosmos/cosmos-sdk/x/staking/client/rest" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/simulation" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -32,137 +32,129 @@ var ( ) // AppModuleBasic defines the basic application module used by the staking module. -type AppModuleBasic struct{} +type AppModuleBasic struct { + cdc codec.Marshaler +} var _ module.AppModuleBasic = AppModuleBasic{} // Name returns the staking module's name. func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the staking module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the staking module's types on the given LegacyAmino codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// RegisterInterfaces registers the module's interface types +func (b AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } // DefaultGenesis returns default genesis state as raw bytes for the staking // module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the staking module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, config client.TxEncodingConfig, bz json.RawMessage) error { + var data types.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } - return ValidateGenesis(data) + return ValidateGenesis(&data) } // RegisterRESTRoutes registers the REST routes for the staking module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr) -} - -// GetTxCmd returns the root tx command for the staking module. -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetTxCmd(StoreKey, cdc) -} - -// GetQueryCmd returns no root query command for the staking module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(StoreKey, cdc) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { + rest.RegisterHandlers(clientCtx, rtr) } -//_____________________________________ -// extra helpers - -// CreateValidatorMsgHelpers - used for gen-tx -func (AppModuleBasic) CreateValidatorMsgHelpers(ipDefault string) ( - fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { - return cli.CreateValidatorMsgHelpers(ipDefault) +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the staking module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) } -// PrepareFlagsForTxCreateValidator - used for gen-tx -func (AppModuleBasic) PrepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, - chainID string, valPubKey crypto.PubKey) { - cli.PrepareFlagsForTxCreateValidator(config, nodeID, chainID, valPubKey) +// GetTxCmd returns the root tx command for the staking module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() } -// BuildCreateValidatorMsg - used for gen-tx -func (AppModuleBasic) BuildCreateValidatorMsg(cliCtx context.CLIContext, - txBldr authtypes.TxBuilder) (authtypes.TxBuilder, sdk.Msg, error) { - return cli.BuildCreateValidatorMsg(cliCtx, txBldr) +// GetQueryCmd returns no root query command for the staking module. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() } -//____________________________________________________________________________ - // AppModule implements an application module for the staking module. type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper accountKeeper types.AccountKeeper - supplyKeeper types.SupplyKeeper + bankKeeper types.BankKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper) AppModule { - +func NewAppModule(cdc codec.Marshaler, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, - accountKeeper: accountKeeper, - supplyKeeper: supplyKeeper, + accountKeeper: ak, + bankKeeper: bk, } } // Name returns the staking module's name. func (AppModule) Name() string { - return ModuleName + return types.ModuleName } // RegisterInvariants registers the staking module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - RegisterInvariants(ir, am.keeper) + keeper.RegisterInvariants(ir, am.keeper) } // Route returns the message routing key for the staking module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the staking module. -func (am AppModule) NewHandler() sdk.Handler { - return NewHandler(am.keeper) +func (am AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) } // QuerierRoute returns the staking module's querier route name. func (AppModule) QuerierRoute() string { - return QuerierRoute + return types.QuerierRoute +} + +// LegacyQuerierHandler returns the staking module sdk.Querier. +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) } -// NewQuerierHandler returns the staking module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + querier := keeper.Querier{Keeper: am.keeper} + types.RegisterQueryServer(cfg.QueryServer(), querier) } // InitGenesis performs genesis initialization for the staking module. It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - return InitGenesis(ctx, am.keeper, am.accountKeeper, am.supplyKeeper, genesisState) +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(data, &genesisState) + + return InitGenesis(ctx, am.keeper, am.accountKeeper, am.bankKeeper, &genesisState) } // ExportGenesis returns the exported genesis state as raw bytes for the staking // module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) + return cdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the staking module. @@ -186,22 +178,23 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { } // ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { +func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { return nil } // RandomizedParams creates randomized staking param changes for the simulator. -func (AppModule) RandomizedParams(r *rand.Rand) []sim.ParamChange { +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { return simulation.ParamChanges(r) } // RegisterStoreDecoder registers a decoder for staking module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) } // WeightedOperations returns the all the staking module operations with their respective weights. -func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { - return simulation.WeightedOperations(simState.AppParams, simState.Cdc, - am.accountKeeper, am.keeper) +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return simulation.WeightedOperations( + simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, am.keeper, + ) } diff --git a/x/staking/module_test.go b/x/staking/module_test.go new file mode 100644 index 000000000000..b1d126d0c38a --- /dev/null +++ b/x/staking/module_test.go @@ -0,0 +1,31 @@ +package staking_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + abcitypes "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestItCreatesModuleAccountOnInitBlock(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + app.InitChain( + abcitypes.RequestInitChain{ + AppStateBytes: []byte("{}"), + ChainId: "test-chain-id", + }, + ) + + acc := app.AccountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.BondedPoolName)) + require.NotNil(t, acc) + + acc = app.AccountKeeper.GetAccount(ctx, authtypes.NewModuleAddress(types.NotBondedPoolName)) + require.NotNil(t, acc) +} diff --git a/x/staking/simulation/common_test.go b/x/staking/simulation/common_test.go new file mode 100644 index 000000000000..880cb442deff --- /dev/null +++ b/x/staking/simulation/common_test.go @@ -0,0 +1,11 @@ +package simulation_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func init() { + sdk.PowerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) +} diff --git a/x/staking/simulation/decoder.go b/x/staking/simulation/decoder.go index de5d50de9f7d..696aee7d2292 100644 --- a/x/staking/simulation/decoder.go +++ b/x/staking/simulation/decoder.go @@ -4,54 +4,61 @@ import ( "bytes" "fmt" - tmkv "github.com/tendermint/tendermint/libs/kv" - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/kv" "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// DecodeStore unmarshals the KVPair's Value to the corresponding staking type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], types.LastTotalPowerKey): - var powerA, powerB sdk.Int - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &powerA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &powerB) - return fmt.Sprintf("%v\n%v", powerA, powerB) - - case bytes.Equal(kvA.Key[:1], types.ValidatorsKey): - var validatorA, validatorB types.Validator - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &validatorA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &validatorB) - return fmt.Sprintf("%v\n%v", validatorA, validatorB) - - case bytes.Equal(kvA.Key[:1], types.LastValidatorPowerKey), - bytes.Equal(kvA.Key[:1], types.ValidatorsByConsAddrKey), - bytes.Equal(kvA.Key[:1], types.ValidatorsByPowerIndexKey): - return fmt.Sprintf("%v\n%v", sdk.ValAddress(kvA.Value), sdk.ValAddress(kvB.Value)) - - case bytes.Equal(kvA.Key[:1], types.DelegationKey): - var delegationA, delegationB types.Delegation - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &delegationA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &delegationB) - return fmt.Sprintf("%v\n%v", delegationA, delegationB) - - case bytes.Equal(kvA.Key[:1], types.UnbondingDelegationKey), - bytes.Equal(kvA.Key[:1], types.UnbondingDelegationByValIndexKey): - var ubdA, ubdB types.UnbondingDelegation - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &ubdA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &ubdB) - return fmt.Sprintf("%v\n%v", ubdA, ubdB) - - case bytes.Equal(kvA.Key[:1], types.RedelegationKey), - bytes.Equal(kvA.Key[:1], types.RedelegationByValSrcIndexKey): - var redA, redB types.Redelegation - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &redA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &redB) - return fmt.Sprintf("%v\n%v", redA, redB) - - default: - panic(fmt.Sprintf("invalid staking key prefix %X", kvA.Key[:1])) +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding staking 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.LastTotalPowerKey): + var powerA, powerB sdk.IntProto + + cdc.MustUnmarshalBinaryBare(kvA.Value, &powerA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &powerB) + + return fmt.Sprintf("%v\n%v", powerA, powerB) + case bytes.Equal(kvA.Key[:1], types.ValidatorsKey): + var validatorA, validatorB types.Validator + + cdc.MustUnmarshalBinaryBare(kvA.Value, &validatorA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &validatorB) + + return fmt.Sprintf("%v\n%v", validatorA, validatorB) + case bytes.Equal(kvA.Key[:1], types.LastValidatorPowerKey), + bytes.Equal(kvA.Key[:1], types.ValidatorsByConsAddrKey), + bytes.Equal(kvA.Key[:1], types.ValidatorsByPowerIndexKey): + return fmt.Sprintf("%v\n%v", sdk.ValAddress(kvA.Value), sdk.ValAddress(kvB.Value)) + + case bytes.Equal(kvA.Key[:1], types.DelegationKey): + var delegationA, delegationB types.Delegation + + cdc.MustUnmarshalBinaryBare(kvA.Value, &delegationA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &delegationB) + + return fmt.Sprintf("%v\n%v", delegationA, delegationB) + case bytes.Equal(kvA.Key[:1], types.UnbondingDelegationKey), + bytes.Equal(kvA.Key[:1], types.UnbondingDelegationByValIndexKey): + var ubdA, ubdB types.UnbondingDelegation + + cdc.MustUnmarshalBinaryBare(kvA.Value, &ubdA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &ubdB) + + return fmt.Sprintf("%v\n%v", ubdA, ubdB) + case bytes.Equal(kvA.Key[:1], types.RedelegationKey), + bytes.Equal(kvA.Key[:1], types.RedelegationByValSrcIndexKey): + var redA, redB types.Redelegation + + cdc.MustUnmarshalBinaryBare(kvA.Value, &redA) + cdc.MustUnmarshalBinaryBare(kvB.Value, &redB) + + return fmt.Sprintf("%v\n%v", redA, redB) + default: + panic(fmt.Sprintf("invalid staking key prefix %X", kvA.Key[:1])) + } } } diff --git a/x/staking/simulation/decoder_test.go b/x/staking/simulation/decoder_test.go index 5e0407c59c07..60210fa89b37 100644 --- a/x/staking/simulation/decoder_test.go +++ b/x/staking/simulation/decoder_test.go @@ -1,4 +1,4 @@ -package simulation +package simulation_test import ( "fmt" @@ -7,11 +7,13 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - tmkv "github.com/tendermint/tendermint/libs/kv" - "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "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/staking/simulation" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -21,32 +23,36 @@ var ( valAddr1 = sdk.ValAddress(delPk1.Address()) ) -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) +func makeTestCodec() (cdc *codec.LegacyAmino) { + cdc = codec.NewLegacyAmino() + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + types.RegisterLegacyAminoCodec(cdc) return } func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() + cdc, _ := simapp.MakeCodecs() + dec := simulation.NewDecodeStore(cdc) bondTime := time.Now().UTC() - val := types.NewValidator(valAddr1, delPk1, types.NewDescription("test", "test", "test", "test", "test")) + val, err := types.NewValidator(valAddr1, delPk1, types.NewDescription("test", "test", "test", "test", "test")) + require.NoError(t, err) del := types.NewDelegation(delAddr1, valAddr1, sdk.OneDec()) ubd := types.NewUnbondingDelegation(delAddr1, valAddr1, 15, bondTime, sdk.OneInt()) red := types.NewRedelegation(delAddr1, valAddr1, valAddr1, 12, bondTime, sdk.OneInt(), sdk.OneDec()) - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: types.LastTotalPowerKey, Value: cdc.MustMarshalBinaryLengthPrefixed(sdk.OneInt())}, - tmkv.Pair{Key: types.GetValidatorKey(valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(val)}, - tmkv.Pair{Key: types.LastValidatorPowerKey, Value: valAddr1.Bytes()}, - tmkv.Pair{Key: types.GetDelegationKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(del)}, - tmkv.Pair{Key: types.GetUBDKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(ubd)}, - tmkv.Pair{Key: types.GetREDKey(delAddr1, valAddr1, valAddr1), Value: cdc.MustMarshalBinaryLengthPrefixed(red)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + Pairs: []kv.Pair{ + {Key: types.LastTotalPowerKey, Value: cdc.MustMarshalBinaryBare(&sdk.IntProto{Int: sdk.OneInt()})}, + {Key: types.GetValidatorKey(valAddr1), Value: cdc.MustMarshalBinaryBare(&val)}, + {Key: types.LastValidatorPowerKey, Value: valAddr1.Bytes()}, + {Key: types.GetDelegationKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryBare(&del)}, + {Key: types.GetUBDKey(delAddr1, valAddr1), Value: cdc.MustMarshalBinaryBare(&ubd)}, + {Key: types.GetREDKey(delAddr1, valAddr1, valAddr1), Value: cdc.MustMarshalBinaryBare(&red)}, + {Key: []byte{0x99}, Value: []byte{0x99}}, + }, } tests := []struct { @@ -66,9 +72,9 @@ func TestDecodeStore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { switch i { case len(tests) - 1: - require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) + require.Panics(t, func() { dec(kvPairs.Pairs[i], kvPairs.Pairs[i]) }, tt.name) default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) + require.Equal(t, tt.expectedLog, dec(kvPairs.Pairs[i], kvPairs.Pairs[i]), tt.name) } }) } diff --git a/x/staking/simulation/genesis.go b/x/staking/simulation/genesis.go index cca02bee8cc9..0c33fa9ee282 100644 --- a/x/staking/simulation/genesis.go +++ b/x/staking/simulation/genesis.go @@ -3,22 +3,22 @@ package simulation // DONTCOVER import ( + "encoding/json" "fmt" "math/rand" "time" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Simulation parameter constants const ( - UnbondingTime = "unbonding_time" - MaxValidators = "max_validators" + unbondingTime = "unbonding_time" + maxValidators = "max_validators" + historicalEntries = "historical_entries" ) // GenUnbondingTime randomized UnbondingTime @@ -27,30 +27,43 @@ func GenUnbondingTime(r *rand.Rand) (ubdTime time.Duration) { } // GenMaxValidators randomized MaxValidators -func GenMaxValidators(r *rand.Rand) (maxValidators uint16) { - return uint16(r.Intn(250) + 1) +func GenMaxValidators(r *rand.Rand) (maxValidators uint32) { + return uint32(r.Intn(250) + 1) +} + +// GetHistEntries randomized HistoricalEntries between 0-100. +func GetHistEntries(r *rand.Rand) uint32 { + return uint32(r.Intn(int(types.DefaultHistoricalEntries + 1))) } // RandomizedGenState generates a random GenesisState for staking func RandomizedGenState(simState *module.SimulationState) { // params - var unbondTime time.Duration + var ( + unbondTime time.Duration + maxVals uint32 + histEntries uint32 + ) + simState.AppParams.GetOrGenerate( - simState.Cdc, UnbondingTime, &unbondTime, simState.Rand, + simState.Cdc, unbondingTime, &unbondTime, simState.Rand, func(r *rand.Rand) { unbondTime = GenUnbondingTime(r) }, ) - var maxValidators uint16 simState.AppParams.GetOrGenerate( - simState.Cdc, MaxValidators, &maxValidators, simState.Rand, - func(r *rand.Rand) { maxValidators = GenMaxValidators(r) }, + simState.Cdc, maxValidators, &maxVals, simState.Rand, + func(r *rand.Rand) { maxVals = GenMaxValidators(r) }, + ) + + simState.AppParams.GetOrGenerate( + simState.Cdc, historicalEntries, &histEntries, simState.Rand, + func(r *rand.Rand) { histEntries = GetHistEntries(r) }, ) // NOTE: the slashing module need to be defined after the staking module on the // NewSimulationManager constructor for this to work simState.UnbondTime = unbondTime - - params := types.NewParams(simState.UnbondTime, maxValidators, 7, 3, sdk.DefaultBondDenom) + params := types.NewParams(simState.UnbondTime, maxVals, 7, histEntries, sdk.DefaultBondDenom) // validators & delegations var ( @@ -59,6 +72,7 @@ func RandomizedGenState(simState *module.SimulationState) { ) valAddrs := make([]sdk.ValAddress, simState.NumBonded) + for i := 0; i < int(simState.NumBonded); i++ { valAddr := sdk.ValAddress(simState.Accounts[i].Address) valAddrs[i] = valAddr @@ -70,18 +84,26 @@ func RandomizedGenState(simState *module.SimulationState) { simulation.RandomDecAmount(simState.Rand, maxCommission), ) - validator := types.NewValidator(valAddr, simState.Accounts[i].PubKey, types.Description{}) + validator, err := types.NewValidator(valAddr, simState.Accounts[i].ConsKey.PubKey(), types.Description{}) + if err != nil { + panic(err) + } validator.Tokens = sdk.NewInt(simState.InitialStake) validator.DelegatorShares = sdk.NewDec(simState.InitialStake) validator.Commission = commission delegation := types.NewDelegation(simState.Accounts[i].Address, valAddr, sdk.NewDec(simState.InitialStake)) + validators = append(validators, validator) delegations = append(delegations, delegation) } stakingGenesis := types.NewGenesisState(params, validators, delegations) - fmt.Printf("Selected randomly generated staking parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, stakingGenesis.Params)) + bz, err := json.MarshalIndent(&stakingGenesis.Params, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated staking parameters:\n%s\n", bz) simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(stakingGenesis) } diff --git a/x/staking/simulation/genesis_test.go b/x/staking/simulation/genesis_test.go new file mode 100644 index 000000000000..aba85cd2bf5f --- /dev/null +++ b/x/staking/simulation/genesis_test.go @@ -0,0 +1,105 @@ +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" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TestRandomizedGenState tests the normal scenario of applying RandomizedGenState. +// Abonormal scenarios are not tested here. +func TestRandomizedGenState(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) + 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 stakingGenesis types.GenesisState + simState.Cdc.MustUnmarshalJSON(simState.GenState[types.ModuleName], &stakingGenesis) + + require.Equal(t, uint32(207), stakingGenesis.Params.MaxValidators) + require.Equal(t, uint32(7), stakingGenesis.Params.MaxEntries) + require.Equal(t, uint32(8687), stakingGenesis.Params.HistoricalEntries) + require.Equal(t, "stake", stakingGenesis.Params.BondDenom) + require.Equal(t, float64(238280), stakingGenesis.Params.UnbondingTime.Seconds()) + // check numbers of Delegations and Validators + require.Len(t, stakingGenesis.Delegations, 3) + require.Len(t, stakingGenesis.Validators, 3) + // check Delegations + require.Equal(t, "cosmos1tnh2q55v8wyygtt9srz5safamzdengsnqeycj3", stakingGenesis.Delegations[0].DelegatorAddress) + require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", stakingGenesis.Delegations[0].ValidatorAddress) + require.Equal(t, "1000.000000000000000000", stakingGenesis.Delegations[0].Shares.String()) + // check validators + require.Equal(t, "cosmosvaloper1ghekyjucln7y67ntx7cf27m9dpuxxemnsvnaes", stakingGenesis.Validators[2].GetOperator().String()) + require.Equal(t, []byte{0xa, 0x20, 0x51, 0xde, 0xbd, 0xe8, 0xfa, 0xdf, 0x4e, 0xfc, 0x33, 0xa5, 0x16, 0x94, 0xf6, 0xee, 0xd3, 0x69, 0x7a, 0x7a, 0x1c, 0x2d, 0x50, 0xb6, 0x2, 0xf7, 0x16, 0x4e, 0x66, 0x9f, 0xff, 0x38, 0x91, 0x9b}, stakingGenesis.Validators[2].ConsensusPubkey.Value) + require.Equal(t, false, stakingGenesis.Validators[2].Jailed) + require.Equal(t, "BOND_STATUS_UNBONDED", stakingGenesis.Validators[2].Status.String()) + require.Equal(t, "1000", stakingGenesis.Validators[2].Tokens.String()) + require.Equal(t, "1000.000000000000000000", stakingGenesis.Validators[2].DelegatorShares.String()) + require.Equal(t, "0.292059246265731326", stakingGenesis.Validators[2].Commission.CommissionRates.Rate.String()) + require.Equal(t, "0.330000000000000000", stakingGenesis.Validators[2].Commission.CommissionRates.MaxRate.String()) + require.Equal(t, "0.038337453731274481", stakingGenesis.Validators[2].Commission.CommissionRates.MaxChangeRate.String()) + require.Equal(t, "1", stakingGenesis.Validators[2].MinSelfDelegation.String()) +} + +// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +func TestRandomizedGenState1(t *testing.T) { + interfaceRegistry := codectypes.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + + s := rand.NewSource(1) + r := rand.New(s) + // all these tests will panic + tests := []struct { + simState module.SimulationState + panicMsg string + }{ + { // panic => reason: incomplete initialization of the simState + module.SimulationState{}, "invalid memory address or nil pointer dereference"}, + { // panic => reason: incomplete initialization of the simState + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + }, "invalid memory address or nil pointer dereference"}, + { + // panic => reason: numBonded != len(Accnounts) + module.SimulationState{ + AppParams: make(simtypes.AppParams), + Cdc: cdc, + Rand: r, + NumBonded: 4, + Accounts: simtypes.RandomAccounts(r, 3), + InitialStake: 1000, + GenState: make(map[string]json.RawMessage), + }, "invalid memory address or nil pointer dereference"}, + } + + for _, tt := range tests { + require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) + } +} diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index b1a932270641..0bcb274ed717 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/helpers" 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/simulation" "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -25,10 +26,9 @@ const ( // WeightedOperations returns all the operations from the module with their respective weights func WeightedOperations( - appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, - k keeper.Keeper, + appParams simtypes.AppParams, cdc codec.JSONMarshaler, ak types.AccountKeeper, + bk types.BankKeeper, k keeper.Keeper, ) simulation.WeightedOperations { - var ( weightMsgCreateValidator int weightMsgEditValidator int @@ -70,87 +70,92 @@ func WeightedOperations( return simulation.WeightedOperations{ simulation.NewWeightedOperation( weightMsgCreateValidator, - SimulateMsgCreateValidator(ak, k), + SimulateMsgCreateValidator(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgEditValidator, - SimulateMsgEditValidator(ak, k), + SimulateMsgEditValidator(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgDelegate, - SimulateMsgDelegate(ak, k), + SimulateMsgDelegate(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgUndelegate, - SimulateMsgUndelegate(ak, k), + SimulateMsgUndelegate(ak, bk, k), ), simulation.NewWeightedOperation( weightMsgBeginRedelegate, - SimulateMsgBeginRedelegate(ak, k), + SimulateMsgBeginRedelegate(ak, bk, k), ), } } // SimulateMsgCreateValidator generates a MsgCreateValidator with random values -// nolint: funlen -func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - - simAccount, _ := simulation.RandomAcc(r, accs) + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) address := sdk.ValAddress(simAccount.Address) // ensure the validator doesn't exist already _, found := k.GetValidator(ctx, address) if found { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to find validator"), nil, nil } denom := k.GetParams(ctx).BondDenom - amount := ak.GetAccount(ctx, simAccount.Address).GetCoins().AmountOf(denom) - if !amount.IsPositive() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + + balance := bk.GetBalance(ctx, simAccount.Address, denom).Amount + if !balance.IsPositive() { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "balance is negative"), nil, nil } - amount, err := simulation.RandPositiveInt(r, amount) + amount, err := simtypes.RandPositiveInt(r, balance) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to generate positive amount"), nil, err } selfDelegation := sdk.NewCoin(denom, amount) account := ak.GetAccount(ctx, simAccount.Address) - coins := account.SpendableCoins(ctx.BlockTime()) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) var fees sdk.Coins - coins, hasNeg := coins.SafeSub(sdk.Coins{selfDelegation}) + + coins, hasNeg := spendable.SafeSub(sdk.Coins{selfDelegation}) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to generate fees"), nil, err } } description := types.NewDescription( - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), ) - maxCommission := sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 0, 100)), 2) + maxCommission := sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 0, 100)), 2) commission := types.NewCommissionRates( - simulation.RandomDecAmount(r, maxCommission), + simtypes.RandomDecAmount(r, maxCommission), maxCommission, - simulation.RandomDecAmount(r, maxCommission), + simtypes.RandomDecAmount(r, maxCommission), ) - msg := types.NewMsgCreateValidator(address, simAccount.PubKey, - selfDelegation, description, commission, sdk.OneInt()) + msg, err := types.NewMsgCreateValidator(address, simAccount.ConsKey.PubKey(), selfDelegation, description, commission, sdk.OneInt()) + if err != nil { + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to create CreateValidator message"), nil, err + } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -159,63 +164,69 @@ func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulat []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgEditValidator generates a MsgEditValidator with random values -// nolint: funlen -func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { if len(k.GetAllValidators(ctx)) == 0 { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "number of validators equal zero"), nil, nil } val, ok := keeper.RandomValidator(r, k, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "unable to pick a validator"), nil, nil } address := val.GetOperator() - newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate) + newCommissionRate := simtypes.RandomDecAmount(r, val.Commission.MaxRate) if err := val.Commission.ValidateNewRate(newCommissionRate, ctx.BlockHeader().Time); err != nil { // skip as the commission is invalid - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "invalid commission rate"), nil, nil } - simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(val.GetOperator())) + simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(val.GetOperator())) if !found { - return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", val.GetOperator()) + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "unable to find account"), nil, fmt.Errorf("validator %s not found", val.GetOperator()) } account := ak.GetAccount(ctx, simAccount.Address) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "unable to generate fees"), nil, err } description := types.NewDescription( - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), - simulation.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), + simtypes.RandStringOfLength(r, 10), ) msg := types.NewMsgEditValidator(address, description, &newCommissionRate, nil) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -224,65 +235,71 @@ func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulatio []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgDelegate generates a MsgDelegate with random values -// nolint: funlen -func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { denom := k.GetParams(ctx).BondDenom + if len(k.GetAllValidators(ctx)) == 0 { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "number of validators equal zero"), nil, nil } - simAccount, _ := simulation.RandomAcc(r, accs) + simAccount, _ := simtypes.RandomAcc(r, accs) val, ok := keeper.RandomValidator(r, k, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to pick a validator"), nil, nil } if val.InvalidExRate() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "validator's invalid echange rate"), nil, nil } - amount := ak.GetAccount(ctx, simAccount.Address).GetCoins().AmountOf(denom) + amount := bk.GetBalance(ctx, simAccount.Address, denom).Amount if !amount.IsPositive() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "balance is negative"), nil, nil } - amount, err := simulation.RandPositiveInt(r, amount) + amount, err := simtypes.RandPositiveInt(r, amount) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to generate positive amount"), nil, err } bondAmt := sdk.NewCoin(denom, amount) account := ak.GetAccount(ctx, simAccount.Address) - coins := account.SpendableCoins(ctx.BlockTime()) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) var fees sdk.Coins - coins, hasNeg := coins.SafeSub(sdk.Coins{bondAmt}) + + coins, hasNeg := spendable.SafeSub(sdk.Coins{bondAmt}) if !hasNeg { - fees, err = simulation.RandomFees(r, ctx, coins) + fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to generate fees"), nil, err } } msg := types.NewMsgDelegate(simAccount.Address, val.GetOperator(), bondAmt) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -291,52 +308,57 @@ func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Ope []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgUndelegate generates a MsgUndelegate with random values -// nolint: funlen -func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { // get random validator validator, ok := keeper.RandomValidator(r, k, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "validator is not ok"), nil, nil } - valAddr := validator.GetOperator() - delegations := k.GetValidatorDelegations(ctx, validator.OperatorAddress) + valAddr := validator.GetOperator() + delegations := k.GetValidatorDelegations(ctx, validator.GetOperator()) + if delegations == nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "keeper does have any delegation entries"), nil, nil + } // get random delegator from validator delegation := delegations[r.Intn(len(delegations))] delAddr := delegation.GetDelegatorAddr() if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "keeper does have a max unbonding delegation entries"), nil, nil } totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt() if !totalBond.IsPositive() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "total bond is negative"), nil, nil } - unbondAmt, err := simulation.RandPositiveInt(r, totalBond) + unbondAmt, err := simtypes.RandPositiveInt(r, totalBond) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "invalid unbond amount"), nil, err } if unbondAmt.IsZero() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "unbond amount is zero"), nil, nil } msg := types.NewMsgUndelegate( @@ -344,7 +366,8 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.O ) // need to retrieve the simulation account associated with delegation to retrieve PrivKey - var simAccount simulation.Account + var simAccount simtypes.Account + for _, simAcc := range accs { if simAcc.Address.Equals(delAddr) { simAccount = simAcc @@ -353,16 +376,20 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.O } // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error if simAccount.PrivKey == nil { - return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) } account := ak.GetAccount(ctx, delAddr) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to generate fees"), nil, err } - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -371,96 +398,101 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.O []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } // SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values -// nolint: funlen -func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +// nolint: interfacer +func SimulateMsgBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { return func( - r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, - ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { // get random source validator srcVal, ok := keeper.RandomValidator(r, k, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to pick validator"), nil, nil } - srcAddr := srcVal.GetOperator() + srcAddr := srcVal.GetOperator() delegations := k.GetValidatorDelegations(ctx, srcAddr) + if delegations == nil { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "keeper does have any delegation entries"), nil, nil + } // get random delegator from src validator delegation := delegations[r.Intn(len(delegations))] delAddr := delegation.GetDelegatorAddr() if k.HasReceivingRedelegation(ctx, delAddr, srcAddr) { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "receveing redelegation is not allowed"), nil, nil // skip } // get random destination validator destVal, ok := keeper.RandomValidator(r, k, ctx) if !ok { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to pick validator"), nil, nil } - destAddr := destVal.GetOperator() - - if srcAddr.Equals(destAddr) || - destVal.InvalidExRate() || - k.HasMaxRedelegationEntries(ctx, delAddr, srcAddr, destAddr) { - return simulation.NoOpMsg(types.ModuleName), nil, nil + destAddr := destVal.GetOperator() + if srcAddr.Equals(destAddr) || destVal.InvalidExRate() || k.HasMaxRedelegationEntries(ctx, delAddr, srcAddr, destAddr) { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "checks failed"), nil, nil } totalBond := srcVal.TokensFromShares(delegation.GetShares()).TruncateInt() if !totalBond.IsPositive() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "total bond is negative"), nil, nil } - redAmt, err := simulation.RandPositiveInt(r, totalBond) + redAmt, err := simtypes.RandPositiveInt(r, totalBond) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to generate positive amount"), nil, err } if redAmt.IsZero() { - return simulation.NoOpMsg(types.ModuleName), nil, nil + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "amount is zero"), nil, nil } // check if the shares truncate to zero shares, err := srcVal.SharesFromTokens(redAmt) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "invalid shares"), nil, err } if srcVal.TokensFromShares(shares).TruncateInt().IsZero() { - return simulation.NoOpMsg(types.ModuleName), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "shares truncate to zero"), nil, nil // skip } // need to retrieve the simulation account associated with delegation to retrieve PrivKey - var simAccount simulation.Account + var simAccount simtypes.Account + for _, simAcc := range accs { if simAcc.Address.Equals(delAddr) { simAccount = simAcc break } } + // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error if simAccount.PrivKey == nil { - return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) } - // get tx fees account := ak.GetAccount(ctx, delAddr) - fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime())) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to generate fees"), nil, err } msg := types.NewMsgBeginRedelegate( @@ -468,7 +500,9 @@ func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulat sdk.NewCoin(k.BondDenom(ctx), redAmt), ) - tx := helpers.GenTx( + txGen := simappparams.MakeTestEncodingConfig().TxConfig + tx, err := helpers.GenTx( + txGen, []sdk.Msg{msg}, fees, helpers.DefaultGenTxGas, @@ -477,12 +511,15 @@ func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulat []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(tx) + _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simulation.NoOpMsg(types.ModuleName), nil, err + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to deliver tx"), nil, err } - return simulation.NewOperationMsg(msg, true, ""), nil, nil + return simtypes.NewOperationMsg(msg, true, ""), nil, nil } } diff --git a/x/staking/simulation/operations_test.go b/x/staking/simulation/operations_test.go new file mode 100644 index 000000000000..fca3b78125a9 --- /dev/null +++ b/x/staking/simulation/operations_test.go @@ -0,0 +1,327 @@ +package simulation_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "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" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/x/staking/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TestWeightedOperations tests the weights of the operations. +func TestWeightedOperations(t *testing.T) { + + app, ctx := createTestApp(false) + + ctx.WithChainID("test-chain") + + cdc := app.AppCodec() + appParams := make(simtypes.AppParams) + + weightesOps := simulation.WeightedOperations(appParams, cdc, app.AccountKeeper, + app.BankKeeper, app.StakingKeeper, + ) + + s := rand.NewSource(1) + r := rand.New(s) + accs := simtypes.RandomAccounts(r, 3) + + expected := []struct { + weight int + opMsgRoute string + opMsgName string + }{{simappparams.DefaultWeightMsgCreateValidator, types.ModuleName, types.TypeMsgCreateValidator}, + {simappparams.DefaultWeightMsgEditValidator, types.ModuleName, types.TypeMsgEditValidator}, + {simappparams.DefaultWeightMsgDelegate, types.ModuleName, types.TypeMsgDelegate}, + {simappparams.DefaultWeightMsgUndelegate, types.ModuleName, types.TypeMsgUndelegate}, + {simappparams.DefaultWeightMsgBeginRedelegate, types.ModuleName, types.TypeMsgBeginRedelegate}, + } + + 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(t, expected[i].weight, w.Weight(), "weight should be the same") + require.Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + require.Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + } +} + +// TestSimulateMsgCreateValidator tests the normal scenario of a valid message of type TypeMsgCreateValidator. +// Abonormal scenarios, where the message are created by an errors are not tested here. +func TestSimulateMsgCreateValidator(t *testing.T) { + app, ctx := createTestApp(false) + + // setup 3 accounts + s := rand.NewSource(1) + r := rand.New(s) + accounts := getTestingAccounts(t, r, app, ctx, 3) + + // begin a new block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) + + // execute operation + op := simulation.SimulateMsgCreateValidator(app.AccountKeeper, app.BankKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgCreateValidator + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "0.080000000000000000", msg.Commission.MaxChangeRate.String()) + require.Equal(t, "0.080000000000000000", msg.Commission.MaxRate.String()) + require.Equal(t, "0.019527679037870745", msg.Commission.Rate.String()) + require.Equal(t, types.TypeMsgCreateValidator, msg.Type()) + require.Equal(t, []byte{0xa, 0x20, 0x51, 0xde, 0xbd, 0xe8, 0xfa, 0xdf, 0x4e, 0xfc, 0x33, 0xa5, 0x16, 0x94, 0xf6, 0xee, 0xd3, 0x69, 0x7a, 0x7a, 0x1c, 0x2d, 0x50, 0xb6, 0x2, 0xf7, 0x16, 0x4e, 0x66, 0x9f, 0xff, 0x38, 0x91, 0x9b}, msg.Pubkey.Value) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress) + require.Equal(t, "cosmosvaloper1ghekyjucln7y67ntx7cf27m9dpuxxemnsvnaes", msg.ValidatorAddress) + require.Len(t, futureOperations, 0) +} + +// TestSimulateMsgEditValidator tests the normal scenario of a valid message of type TypeMsgEditValidator. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgEditValidator(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 accounts[0] as validator + _ = getTestingValidator0(t, app, ctx, accounts) + + // 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.SimulateMsgEditValidator(app.AccountKeeper, app.BankKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgEditValidator + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "0.280623462081924936", msg.CommissionRate.String()) + require.Equal(t, "rBqDOTtGTO", msg.Description.Moniker) + require.Equal(t, "BSpYuLyYgg", msg.Description.Identity) + require.Equal(t, "wNbeHVIkPZ", msg.Description.Website) + require.Equal(t, "MOXcnQfyze", msg.Description.SecurityContact) + require.Equal(t, types.TypeMsgEditValidator, msg.Type()) + require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) + require.Len(t, futureOperations, 0) +} + +// TestSimulateMsgDelegate tests the normal scenario of a valid message of type TypeMsgDelegate. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgDelegate(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 accounts[0] as validator + validator0 := getTestingValidator0(t, app, ctx, accounts) + setupValidatorRewards(app, ctx, validator0.GetOperator()) + + // 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.SimulateMsgDelegate(app.AccountKeeper, app.BankKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgDelegate + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress) + require.Equal(t, "98100858108421259236", msg.Amount.Amount.String()) + require.Equal(t, "stake", msg.Amount.Denom) + require.Equal(t, types.TypeMsgDelegate, msg.Type()) + require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) + require.Len(t, futureOperations, 0) +} + +// TestSimulateMsgUndelegate tests the normal scenario of a valid message of type TypeMsgUndelegate. +// Abonormal scenarios, where the message is created by an errors are not tested here. +func TestSimulateMsgUndelegate(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 accounts[0] as validator + validator0 := getTestingValidator0(t, app, ctx, accounts) + + // setup delegation + delTokens := sdk.TokensFromConsensusPower(2) + validator0, issuedShares := validator0.AddTokensFromDel(delTokens) + delegator := accounts[1] + delegation := types.NewDelegation(delegator.Address, validator0.GetOperator(), issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) + app.DistrKeeper.SetDelegatorStartingInfo(ctx, validator0.GetOperator(), delegator.Address, distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) + + setupValidatorRewards(app, ctx, validator0.GetOperator()) + + // 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.SimulateMsgUndelegate(app.AccountKeeper, app.BankKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgUndelegate + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.DelegatorAddress) + require.Equal(t, "280623462081924937", msg.Amount.Amount.String()) + require.Equal(t, "stake", msg.Amount.Denom) + require.Equal(t, types.TypeMsgUndelegate, msg.Type()) + require.Equal(t, "cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) + require.Len(t, futureOperations, 0) + +} + +// TestSimulateMsgBeginRedelegate tests the normal scenario of a valid message of type TypeMsgBeginRedelegate. +// Abonormal scenarios, where the message is created by an errors, are not tested here. +func TestSimulateMsgBeginRedelegate(t *testing.T) { + app, ctx := createTestApp(false) + blockTime := time.Now().UTC() + ctx = ctx.WithBlockTime(blockTime) + + // setup 3 accounts + s := rand.NewSource(5) + r := rand.New(s) + accounts := getTestingAccounts(t, r, app, ctx, 3) + + // setup accounts[0] as validator0 and accounts[1] as validator1 + validator0 := getTestingValidator0(t, app, ctx, accounts) + validator1 := getTestingValidator1(t, app, ctx, accounts) + + delTokens := sdk.TokensFromConsensusPower(2) + validator0, issuedShares := validator0.AddTokensFromDel(delTokens) + + // setup accounts[2] as delegator + delegator := accounts[2] + delegation := types.NewDelegation(delegator.Address, validator1.GetOperator(), issuedShares) + app.StakingKeeper.SetDelegation(ctx, delegation) + app.DistrKeeper.SetDelegatorStartingInfo(ctx, validator1.GetOperator(), delegator.Address, distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) + + setupValidatorRewards(app, ctx, validator0.GetOperator()) + setupValidatorRewards(app, ctx, validator1.GetOperator()) + + // 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.SimulateMsgBeginRedelegate(app.AccountKeeper, app.BankKeeper, app.StakingKeeper) + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + require.NoError(t, err) + + var msg types.MsgBeginRedelegate + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + require.True(t, operationMsg.OK) + require.Equal(t, "cosmos12gwd9jchc69wck8dhstxgwz3z8qs8yv67ps8mu", msg.DelegatorAddress) + require.Equal(t, "489348507626016866", msg.Amount.Amount.String()) + require.Equal(t, "stake", msg.Amount.Denom) + require.Equal(t, types.TypeMsgBeginRedelegate, msg.Type()) + require.Equal(t, "cosmosvaloper1h6a7shta7jyc72hyznkys683z98z36e0zdk8g9", msg.ValidatorDstAddress) + require.Equal(t, "cosmosvaloper17s94pzwhsn4ah25tec27w70n65h5t2scgxzkv2", msg.ValidatorSrcAddress) + require.Len(t, futureOperations, 0) + +} + +// returns context and an app with updated mint keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + // sdk.PowerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + app := simapp.Setup(isCheckTx) + + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.MintKeeper.SetParams(ctx, minttypes.DefaultParams()) + app.MintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) + + return app, ctx +} + +func getTestingAccounts(t *testing.T, r *rand.Rand, app *simapp.SimApp, ctx sdk.Context, 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 { + acc := app.AccountKeeper.NewAccountWithAddress(ctx, account.Address) + app.AccountKeeper.SetAccount(ctx, acc) + err := app.BankKeeper.SetBalances(ctx, account.Address, initCoins) + require.NoError(t, err) + } + + return accounts +} + +func getTestingValidator0(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account) types.Validator { + commission0 := types.NewCommission(sdk.ZeroDec(), sdk.OneDec(), sdk.OneDec()) + return getTestingValidator(t, app, ctx, accounts, commission0, 0) +} + +func getTestingValidator1(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account) types.Validator { + commission1 := types.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + return getTestingValidator(t, app, ctx, accounts, commission1, 1) +} + +func getTestingValidator(t *testing.T, app *simapp.SimApp, ctx sdk.Context, accounts []simtypes.Account, commission types.Commission, n int) types.Validator { + account := accounts[n] + valPubKey := account.PubKey + valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) + validator := teststaking.NewValidator(t, valAddr, valPubKey) + validator, err := validator.SetInitialCommission(commission) + require.NoError(t, err) + + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.TokensFromConsensusPower(100) + + app.StakingKeeper.SetValidator(ctx, validator) + + return validator +} + +func setupValidatorRewards(app *simapp.SimApp, ctx sdk.Context, valAddress sdk.ValAddress) { + decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.OneDec())} + historicalRewards := distrtypes.NewValidatorHistoricalRewards(decCoins, 2) + app.DistrKeeper.SetValidatorHistoricalRewards(ctx, valAddress, 2, historicalRewards) + // setup current revards + currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3) + app.DistrKeeper.SetValidatorCurrentRewards(ctx, valAddress, currentRewards) + +} diff --git a/x/staking/simulation/params.go b/x/staking/simulation/params.go index 97938fb91e2d..ffbbe4fe47ff 100644 --- a/x/staking/simulation/params.go +++ b/x/staking/simulation/params.go @@ -7,27 +7,29 @@ import ( "math/rand" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) -const ( - keyMaxValidators = "MaxValidators" - keyUnbondingTime = "UnbondingTime" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation -func ParamChanges(r *rand.Rand) []simulation.ParamChange { - return []simulation.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, keyMaxValidators, +func ParamChanges(r *rand.Rand) []simtypes.ParamChange { + return []simtypes.ParamChange{ + simulation.NewSimParamChange(types.ModuleName, string(types.KeyMaxValidators), func(r *rand.Rand) string { return fmt.Sprintf("%d", GenMaxValidators(r)) }, ), - simulation.NewSimParamChange(types.ModuleName, keyUnbondingTime, + simulation.NewSimParamChange(types.ModuleName, string(types.KeyUnbondingTime), func(r *rand.Rand) string { return fmt.Sprintf("\"%d\"", GenUnbondingTime(r)) }, ), + simulation.NewSimParamChange(types.ModuleName, string(types.KeyHistoricalEntries), + func(r *rand.Rand) string { + return fmt.Sprintf("%d", GetHistEntries(r)) + }, + ), } } diff --git a/x/staking/simulation/params_test.go b/x/staking/simulation/params_test.go new file mode 100644 index 000000000000..07da026645cf --- /dev/null +++ b/x/staking/simulation/params_test.go @@ -0,0 +1,37 @@ +package simulation_test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/staking/simulation" +) + +func TestParamChanges(t *testing.T) { + s := rand.NewSource(1) + r := rand.New(s) + + expected := []struct { + composedKey string + key string + simValue string + subspace string + }{ + {"staking/MaxValidators", "MaxValidators", "82", "staking"}, + {"staking/UnbondingTime", "UnbondingTime", "\"275307000000000\"", "staking"}, + {"staking/HistoricalEntries", "HistoricalEntries", "9149", "staking"}, + } + + paramChanges := simulation.ParamChanges(r) + + require.Len(t, paramChanges, 3) + + for i, p := range paramChanges { + require.Equal(t, expected[i].composedKey, p.ComposedKey()) + require.Equal(t, expected[i].key, p.Key()) + require.Equal(t, expected[i].simValue, p.SimValue()(r)) + require.Equal(t, expected[i].subspace, p.Subspace()) + } +} diff --git a/x/staking/spec/01_state.md b/x/staking/spec/01_state.md index 3ee6c7992ebf..7a0ea327f191 100644 --- a/x/staking/spec/01_state.md +++ b/x/staking/spec/01_state.md @@ -7,24 +7,18 @@ order: 1 ## LastTotalPower LastTotalPower tracks the total amounts of bonded tokens recorded during the previous end block. +Store entries prefixed with "Last" must remain unchanged until EndBlock. -- 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 @@ -36,12 +30,12 @@ Validators can have one of three statuses active set during [`EndBlock`](./05_end_block.md#validator-set-changes) and their status is updated to `Bonded`. 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. -- `Unbonding`: When a validator leaves the active set, either by choice or due to slashing or + 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. +- `Unbonding`: When a validator leaves the active set, either by choice or due to slashing, jailing or tombstoning, an unbonding of all their delegations begins. All delegations must then wait the UnbondingTime - before moving receiving their tokens to their accounts from the `BondedPool`. + before their tokens are moved to their accounts from the `BondedPool`. Validators objects should be primarily stored and accessed by the `OperatorAddr`, an SDK validator address for the operator of the validator. Two @@ -51,10 +45,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 @@ -66,10 +60,10 @@ When Tendermint reports evidence, it provides the validator address, so this map is needed to find the operator. Note that the `ConsAddr` corresponds to the address which can be derived from the validator's `ConsPubKey`. -`ValidatorsByPower` is an additional index that provides a sorted list o +`ValidatorsByPower` is an additional index that provides a sorted list of potential validators to quickly determine the current active set. Here -ConsensusPower is validator.Tokens/10^6. Note that all validators where -`Jailed` is true are not stored within this index. +ConsensusPower is validator.Tokens/10^6 by default. 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 last-block's bonded validators. This index remains constant during a block but @@ -77,60 +71,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 +116,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 +126,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 +138,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 +146,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,27 +176,16 @@ 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 For the purpose of tracking progress of redelegations the redelegation queue is kept. -- UnbondingDelegation: `0x42 | format(time) -> []DVVTriplet` +- RedelegationQueue: `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 +195,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 +204,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 abci.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..5777ed251777 100644 --- a/x/staking/spec/02_state_transitions.md +++ b/x/staking/spec/02_state_transitions.md @@ -11,12 +11,17 @@ 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 +A validator can be `Unbonded`, `Unbonding` or `Bonded`. `Unbonded` +and `Unbonding` are collectively called `Not Bonded`. A validator can move +directly between all the states, except for from `Bonded` to `Unbonded`. + +### Not bonded 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` @@ -54,6 +59,9 @@ this process may be also be reversed. the following operations occur: - if jailed delete record from `ValidatorByPowerIndex` - if unjailed add record to `ValidatorByPowerIndex` +Jailed validators are not present in any of the following stores: +- the power store (from consensus power to address) + ## Delegations ### Delegate @@ -98,9 +106,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 +124,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..c0bc4aa9f817 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,72 @@ 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 +The delegator receives newly minted shares at the current exchange rate. +The exchange rate is the number of existing shares in the validator divided by +the number of currently delegated tokens. + +The validator is updated in the `ValidatorByPower` index, and the delegation is +tracked in validator object in the `Validators` index. -The begin unbonding message allows delegators to undelegate their tokens from +It is possible to delegate to a jailed validator, the only difference being it +will not be added to the power index until it is unjailed. + +## Msg/Undelegate + +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 message is expected to fail if: +This service message returns a response containing the completion time of the undelegation: + ++++ 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 +98,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 +109,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 +132,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 +140,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/05_end_block.md b/x/staking/spec/05_end_block.md index 16444134f424..769b986b7c0e 100644 --- a/x/staking/spec/05_end_block.md +++ b/x/staking/spec/05_end_block.md @@ -1,5 +1,5 @@ # End-Block @@ -16,16 +16,21 @@ validator set which is responsible for validating Tendermint messages at the consensus layer. Operations are as following: - the new validator set is taken as the top `params.MaxValidators` number of - validators retrieved from the ValidatorsByPower index + validators retrieved from the `ValidatorsByPower` index - the previous validator set is compared with the new validator set: - missing validators begin unbonding and their `Tokens` are transferred from the - `BondedPool` to the `NotBondedPool` `ModuleAccount` + `BondedPool` to the `NotBondedPool` `ModuleAccount` - new validators are instantly bonded and their `Tokens` are transferred from the - `NotBondedPool` to the `BondedPool` `ModuleAccount` + `NotBondedPool` to the `BondedPool` `ModuleAccount` In all cases, any validators leaving or entering the bonded validator set or changing balances and staying within the bonded validator set incur an update -message which is passed back to Tendermint. +message reporting their new consensus power which is passed back to Tendermint. + +The `LastTotalPower` and `LastValidatorsPower` hold the state of the total power +and validator power from the end of the last block, and are used to check for +changes that have occured in `ValidatorsByPower` and the total new power, which +is calculated during `EndBlock`. ## Queues @@ -41,15 +46,16 @@ When a validator is kicked out of the bonded validator set (either through being jailed, or not having sufficient bonded tokens) it begins the unbonding process along with all its delegations begin unbonding (while still being delegated to this validator). At this point the validator is said to be an -unbonding validator, whereby it will mature to become an "unbonded validator" +"unbonding validator", whereby it will mature to become an "unbonded validator" after the unbonding period has passed. Each block the validator queue is to be checked for mature unbonding validators -(namely with a completion time <= current time). At this point any mature -validators which do not have any delegations remaining are deleted from state. -For all other mature unbonding validators that still have remaining -delegations, the `validator.Status` is switched from `sdk.Unbonding` to -`sdk.Unbonded`. +(namely with a completion time <= current time and completion height <= current +block height). At this point any mature validators which do not have any +delegations remaining are deleted from state. For all other mature unbonding +validators that still have remaining delegations, the `validator.Status` is +switched from `types.Unbonding` to +`types.Unbonded`. ### Unbonding Delegations diff --git a/x/staking/spec/06_hooks.md b/x/staking/spec/06_hooks.md index c2c372b624d8..2eac2d4a1708 100644 --- a/x/staking/spec/06_hooks.md +++ b/x/staking/spec/06_hooks.md @@ -1,5 +1,5 @@ # Hooks diff --git a/x/staking/spec/07_events.md b/x/staking/spec/07_events.md index fe716baf9180..660319b3c1a4 100644 --- a/x/staking/spec/07_events.md +++ b/x/staking/spec/07_events.md @@ -1,5 +1,5 @@ # Events @@ -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 437790ce74e2..a2cb7cf3a8df 100644 --- a/x/staking/spec/README.md +++ b/x/staking/spec/README.md @@ -15,8 +15,8 @@ in June 2016. The module enables Cosmos-SDK based blockchain to support an advanced Proof-of-Stake system. In this system, holders of the native staking token of -the chain can become validators and can delegate tokens to validator -validators, ultimately determining the effective validator set for the system. +the chain can become validators and can delegate tokens to validators, +ultimately determining the effective validator set for the system. This module will be used in the Cosmos Hub, the first Hub in the Cosmos network. @@ -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/test_common.go b/x/staking/test_common.go deleted file mode 100644 index c7b1073fc74f..000000000000 --- a/x/staking/test_common.go +++ /dev/null @@ -1,57 +0,0 @@ -package staking - -import ( - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// nolint:deadcode,unused,varcheck -var ( - priv1 = secp256k1.GenPrivKey() - addr1 = sdk.AccAddress(priv1.PubKey().Address()) - priv2 = secp256k1.GenPrivKey() - addr2 = sdk.AccAddress(priv2.PubKey().Address()) - addr3 = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - priv4 = secp256k1.GenPrivKey() - addr4 = sdk.AccAddress(priv4.PubKey().Address()) - coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))} - fee = auth.NewStdFee( - 100000, - sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}, - ) - - commissionRates = NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) -) - -func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator { - return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commissionRates, sdk.OneInt(), - ) -} - -func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey crypto.PubKey, - amt sdk.Int, commissionRate sdk.Dec) MsgCreateValidator { - - commission := NewCommissionRates(commissionRate, sdk.OneDec(), sdk.ZeroDec()) - - return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commission, sdk.OneInt(), - ) -} - -func NewTestMsgCreateValidatorWithMinSelfDelegation(address sdk.ValAddress, pubKey crypto.PubKey, - amt sdk.Int, minSelfDelegation sdk.Int) MsgCreateValidator { - - return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commissionRates, minSelfDelegation, - ) -} - -func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int) MsgDelegate { - amount := sdk.NewCoin(sdk.DefaultBondDenom, amt) - return NewMsgDelegate(delAddr, valAddr, amount) -} diff --git a/x/staking/teststaking/helper.go b/x/staking/teststaking/helper.go new file mode 100644 index 000000000000..b33118f541fd --- /dev/null +++ b/x/staking/teststaking/helper.go @@ -0,0 +1,133 @@ +package teststaking + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Helper is a structure which wraps the staking handler +// and provides methods useful in tests +type Helper struct { + t *testing.T + h sdk.Handler + k keeper.Keeper + + Ctx sdk.Context + Commission stakingtypes.CommissionRates + // Coin Denomination + Denom string +} + +// NewHelper creates staking Handler wrapper for tests +func NewHelper(t *testing.T, ctx sdk.Context, k keeper.Keeper) *Helper { + return &Helper{t, staking.NewHandler(k), k, ctx, ZeroCommission(), sdk.DefaultBondDenom} +} + +// CreateValidator calls handler to create a new staking validator +func (sh *Helper) CreateValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, stakeAmount sdk.Int, ok bool) { + coin := sdk.NewCoin(sh.Denom, stakeAmount) + sh.createValidator(addr, pk, coin, ok) +} + +// CreateValidatorWithValPower calls handler to create a new staking validator with zero +// commission +func (sh *Helper) CreateValidatorWithValPower(addr sdk.ValAddress, pk cryptotypes.PubKey, valPower int64, ok bool) sdk.Int { + amount := sdk.TokensFromConsensusPower(valPower) + coin := sdk.NewCoin(sh.Denom, amount) + sh.createValidator(addr, pk, coin, ok) + return amount +} + +// CreateValidatorMsg returns a message used to create validator in this service. +func (sh *Helper) CreateValidatorMsg(addr sdk.ValAddress, pk cryptotypes.PubKey, stakeAmount sdk.Int) *stakingtypes.MsgCreateValidator { + coin := sdk.NewCoin(sh.Denom, stakeAmount) + msg, err := stakingtypes.NewMsgCreateValidator(addr, pk, coin, stakingtypes.Description{}, sh.Commission, sdk.OneInt()) + require.NoError(sh.t, err) + return msg +} + +func (sh *Helper) createValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, coin sdk.Coin, ok bool) { + msg, err := stakingtypes.NewMsgCreateValidator(addr, pk, coin, stakingtypes.Description{}, sh.Commission, sdk.OneInt()) + require.NoError(sh.t, err) + sh.Handle(msg, ok) +} + +// Delegate calls handler to delegate stake for a validator +func (sh *Helper) Delegate(delegator sdk.AccAddress, val sdk.ValAddress, amount sdk.Int) { + coin := sdk.NewCoin(sh.Denom, amount) + msg := stakingtypes.NewMsgDelegate(delegator, val, coin) + sh.Handle(msg, true) +} + +// DelegateWithPower calls handler to delegate stake for a validator +func (sh *Helper) DelegateWithPower(delegator sdk.AccAddress, val sdk.ValAddress, power int64) { + coin := sdk.NewCoin(sh.Denom, sdk.TokensFromConsensusPower(power)) + msg := stakingtypes.NewMsgDelegate(delegator, val, coin) + sh.Handle(msg, true) +} + +// Undelegate calls handler to unbound some stake from a validator. +func (sh *Helper) Undelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount sdk.Int, ok bool) *sdk.Result { + unbondAmt := sdk.NewCoin(sh.Denom, amount) + msg := stakingtypes.NewMsgUndelegate(delegator, val, unbondAmt) + return sh.Handle(msg, ok) +} + +// Handle calls staking handler on a given message +func (sh *Helper) Handle(msg sdk.Msg, ok bool) *sdk.Result { + res, err := sh.h(sh.Ctx, msg) + if ok { + require.NoError(sh.t, err) + require.NotNil(sh.t, res) + } else { + require.Error(sh.t, err) + require.Nil(sh.t, res) + } + return res +} + +// CheckValidator asserts that a validor exists and has a given status (if status!="") +// and if has a right jailed flag. +func (sh *Helper) CheckValidator(addr sdk.ValAddress, status stakingtypes.BondStatus, jailed bool) stakingtypes.Validator { + v, ok := sh.k.GetValidator(sh.Ctx, addr) + require.True(sh.t, ok) + require.Equal(sh.t, jailed, v.Jailed, "wrong Jalied status") + if status >= 0 { + require.Equal(sh.t, status, v.Status) + } + return v +} + +// CheckDelegator asserts that a delegator exists +func (sh *Helper) CheckDelegator(delegator sdk.AccAddress, val sdk.ValAddress, found bool) { + _, ok := sh.k.GetDelegation(sh.Ctx, delegator, val) + require.Equal(sh.t, ok, found) +} + +// TurnBlock calls EndBlocker and updates the block time +func (sh *Helper) TurnBlock(newTime time.Time) sdk.Context { + sh.Ctx = sh.Ctx.WithBlockTime(newTime) + staking.EndBlocker(sh.Ctx, sh.k) + return sh.Ctx +} + +// TurnBlockTimeDiff calls EndBlocker and updates the block time by adding the +// duration to the current block time +func (sh *Helper) TurnBlockTimeDiff(diff time.Duration) sdk.Context { + sh.Ctx = sh.Ctx.WithBlockTime(sh.Ctx.BlockHeader().Time.Add(diff)) + staking.EndBlocker(sh.Ctx, sh.k) + return sh.Ctx +} + +// ZeroCommission constructs a commission rates with all zeros. +func ZeroCommission() stakingtypes.CommissionRates { + return stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) +} diff --git a/x/staking/teststaking/tm.go b/x/staking/teststaking/tm.go new file mode 100644 index 000000000000..9a9c030d061c --- /dev/null +++ b/x/staking/teststaking/tm.go @@ -0,0 +1,43 @@ +package teststaking + +import ( + tmcrypto "github.com/tendermint/tendermint/crypto" + tmtypes "github.com/tendermint/tendermint/types" + + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// GetTmConsPubKey gets the validator's public key as a tmcrypto.PubKey. +func GetTmConsPubKey(v types.Validator) (tmcrypto.PubKey, error) { + pk, err := v.ConsPubKey() + if err != nil { + return nil, err + } + + return cryptocodec.ToTmPubKeyInterface(pk) +} + +// ToTmValidator casts an SDK validator to a tendermint type Validator. +func ToTmValidator(v types.Validator) (*tmtypes.Validator, error) { + tmPk, err := GetTmConsPubKey(v) + if err != nil { + return nil, err + } + + return tmtypes.NewValidator(tmPk, v.ConsensusPower()), nil +} + +// ToTmValidators casts all validators to the corresponding tendermint type. +func ToTmValidators(v types.Validators) ([]*tmtypes.Validator, error) { + validators := make([]*tmtypes.Validator, len(v)) + var err error + for i, val := range v { + validators[i], err = ToTmValidator(val) + if err != nil { + return nil, err + } + } + + return validators, nil +} diff --git a/x/staking/teststaking/validator.go b/x/staking/teststaking/validator.go new file mode 100644 index 000000000000..71459581f0e6 --- /dev/null +++ b/x/staking/teststaking/validator.go @@ -0,0 +1,18 @@ +package teststaking + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// NewValidator is a testing helper method to create validators in tests +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/codec.go b/x/staking/types/codec.go index 6fadfd9a68f9..714554a33ca8 100644 --- a/x/staking/types/codec.go +++ b/x/staking/types/codec.go @@ -2,23 +2,49 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" ) -// Register concrete types on codec codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil) - cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil) - cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) - cdc.RegisterConcrete(MsgUndelegate{}, "cosmos-sdk/MsgUndelegate", nil) - cdc.RegisterConcrete(MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate", nil) +// RegisterLegacyAminoCodec registers the necessary x/staking interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil) + cdc.RegisterConcrete(&MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil) + cdc.RegisterConcrete(&MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) + cdc.RegisterConcrete(&MsgUndelegate{}, "cosmos-sdk/MsgUndelegate", nil) + cdc.RegisterConcrete(&MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate", nil) } -// generic sealed codec to be used throughout this module -var ModuleCdc *codec.Codec +// RegisterInterfaces registers the x/staking interfaces types with the interface registry +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgCreateValidator{}, + &MsgEditValidator{}, + &MsgDelegate{}, + &MsgUndelegate{}, + &MsgBeginRedelegate{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/staking module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/staking and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) func init() { - ModuleCdc = codec.New() - RegisterCodec(ModuleCdc) - codec.RegisterCrypto(ModuleCdc) - ModuleCdc.Seal() + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + amino.Seal() } diff --git a/x/staking/types/commission.go b/x/staking/types/commission.go index 38ab8e06a703..54891b5dcac3 100644 --- a/x/staking/types/commission.go +++ b/x/staking/types/commission.go @@ -1,26 +1,11 @@ package types import ( - "fmt" "time" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type ( - // Commission defines a commission parameters for a given validator. - Commission struct { - CommissionRates `json:"commission_rates" yaml:"commission_rates"` - UpdateTime time.Time `json:"update_time" yaml:"update_time"` // the last time the commission rate was changed - } + yaml "gopkg.in/yaml.v2" - // CommissionRates defines the initial commission rates to be used for creating a - // validator. - CommissionRates struct { - Rate sdk.Dec `json:"rate" yaml:"rate"` // the commission rate charged to delegators, as a fraction - MaxRate sdk.Dec `json:"max_rate" yaml:"max_rate"` // maximum commission rate which validator can ever charge, as a fraction - MaxChangeRate sdk.Dec `json:"max_change_rate" yaml:"max_change_rate"` // maximum daily increase of the validator commission, as a fraction - } + sdk "github.com/cosmos/cosmos-sdk/types" ) // NewCommissionRates returns an initialized validator commission rates. @@ -49,47 +34,43 @@ func NewCommissionWithTime(rate, maxRate, maxChangeRate sdk.Dec, updatedAt time. } } -// Equal checks if the given Commission object is equal to the receiving -// Commission object. -func (c Commission) Equal(c2 Commission) bool { - return c.Rate.Equal(c2.Rate) && - c.MaxRate.Equal(c2.MaxRate) && - c.MaxChangeRate.Equal(c2.MaxChangeRate) && - c.UpdateTime.Equal(c2.UpdateTime) +// String implements the Stringer interface for a Commission object. +func (c Commission) String() string { + out, _ := yaml.Marshal(c) + return string(out) } -// String implements the Stringer interface for a Commission. -func (c Commission) String() string { - return fmt.Sprintf("rate: %s, maxRate: %s, maxChangeRate: %s, updateTime: %s", - c.Rate, c.MaxRate, c.MaxChangeRate, c.UpdateTime, - ) +// String implements the Stringer interface for a CommissionRates object. +func (cr CommissionRates) String() string { + out, _ := yaml.Marshal(cr) + return string(out) } // Validate performs basic sanity validation checks of initial commission // parameters. If validation fails, an SDK error is returned. -func (c CommissionRates) Validate() error { +func (cr CommissionRates) Validate() error { switch { - case c.MaxRate.IsNegative(): + case cr.MaxRate.IsNegative(): // max rate cannot be negative return ErrCommissionNegative - case c.MaxRate.GT(sdk.OneDec()): + case cr.MaxRate.GT(sdk.OneDec()): // max rate cannot be greater than 1 return ErrCommissionHuge - case c.Rate.IsNegative(): + case cr.Rate.IsNegative(): // rate cannot be negative return ErrCommissionNegative - case c.Rate.GT(c.MaxRate): + case cr.Rate.GT(cr.MaxRate): // rate cannot be greater than the max rate return ErrCommissionGTMaxRate - case c.MaxChangeRate.IsNegative(): + case cr.MaxChangeRate.IsNegative(): // change rate cannot be negative return ErrCommissionChangeRateNegative - case c.MaxChangeRate.GT(c.MaxRate): + case cr.MaxChangeRate.GT(cr.MaxRate): // change rate cannot be greater than the max rate return ErrCommissionChangeRateGTMaxRate } diff --git a/x/staking/types/commission_test.go b/x/staking/types/commission_test.go index ec6186122a7e..3ca95cf4b47f 100644 --- a/x/staking/types/commission_test.go +++ b/x/staking/types/commission_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "testing" @@ -7,27 +7,28 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestCommissionValidate(t *testing.T) { testCases := []struct { - input Commission + input types.Commission expectErr bool }{ // invalid commission; max rate < 0% - {NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec()), true}, + {types.NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec()), true}, // invalid commission; max rate > 100% - {NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("2.00"), sdk.ZeroDec()), true}, + {types.NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("2.00"), sdk.ZeroDec()), true}, // invalid commission; rate < 0% - {NewCommission(sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec(), sdk.ZeroDec()), true}, + {types.NewCommission(sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec(), sdk.ZeroDec()), true}, // invalid commission; rate > max rate - {NewCommission(sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.50"), sdk.ZeroDec()), true}, + {types.NewCommission(sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.50"), sdk.ZeroDec()), true}, // invalid commission; max change rate < 0% - {NewCommission(sdk.OneDec(), sdk.OneDec(), sdk.MustNewDecFromStr("-1.00")), true}, + {types.NewCommission(sdk.OneDec(), sdk.OneDec(), sdk.MustNewDecFromStr("-1.00")), true}, // invalid commission; max change rate > max rate - {NewCommission(sdk.OneDec(), sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.90")), true}, + {types.NewCommission(sdk.OneDec(), sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.90")), true}, // valid commission - {NewCommission(sdk.MustNewDecFromStr("0.20"), sdk.OneDec(), sdk.MustNewDecFromStr("0.10")), false}, + {types.NewCommission(sdk.MustNewDecFromStr("0.20"), sdk.OneDec(), sdk.MustNewDecFromStr("0.10")), false}, } for i, tc := range testCases { @@ -38,11 +39,11 @@ func TestCommissionValidate(t *testing.T) { func TestCommissionValidateNewRate(t *testing.T) { now := time.Now().UTC() - c1 := NewCommission(sdk.MustNewDecFromStr("0.40"), sdk.MustNewDecFromStr("0.80"), sdk.MustNewDecFromStr("0.10")) + c1 := types.NewCommission(sdk.MustNewDecFromStr("0.40"), sdk.MustNewDecFromStr("0.80"), sdk.MustNewDecFromStr("0.10")) c1.UpdateTime = now testCases := []struct { - input Commission + input types.Commission newRate sdk.Dec blockTime time.Time expectErr bool diff --git a/x/staking/types/data_test.go b/x/staking/types/data_test.go new file mode 100644 index 000000000000..ed9c64c57a2f --- /dev/null +++ b/x/staking/types/data_test.go @@ -0,0 +1,34 @@ +package types_test + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + pk1 = ed25519.GenPrivKey().PubKey() + pk1Any *codectypes.Any + pk2 = ed25519.GenPrivKey().PubKey() + pk3 = ed25519.GenPrivKey().PubKey() + addr1, _ = sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixAccAddr, pk1.Address().Bytes()) + addr2, _ = sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixAccAddr, pk2.Address().Bytes()) + addr3, _ = sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixAccAddr, pk3.Address().Bytes()) + valAddr1 = sdk.ValAddress(pk1.Address()) + valAddr2 = sdk.ValAddress(pk2.Address()) + valAddr3 = sdk.ValAddress(pk3.Address()) + + emptyAddr sdk.ValAddress + emptyPubkey cryptotypes.PubKey +) + +func init() { + var err error + pk1Any, err = codectypes.NewAnyWithValue(pk1) + if err != nil { + panic(fmt.Sprintf("Can't pack pk1 %t as Any", pk1)) + } +} diff --git a/x/staking/types/delegation.go b/x/staking/types/delegation.go index 672edd5a90ed..a83321597297 100644 --- a/x/staking/types/delegation.go +++ b/x/staking/types/delegation.go @@ -1,97 +1,84 @@ package types import ( - "bytes" "encoding/json" "fmt" "strings" "time" + yaml "gopkg.in/yaml.v2" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/exported" ) -// DVPair is struct that just has a delegator-validator pair with no other data. -// It is intended to be used as a marshalable pointer. For example, a DVPair can be used to construct the -// key to getting an UnbondingDelegation from state. -type DVPair struct { - DelegatorAddress sdk.AccAddress - ValidatorAddress sdk.ValAddress -} +// Implements Delegation interface +var _ DelegationI = Delegation{} -// DVVTriplet is struct that just has a delegator-validator-validator triplet with no other data. -// It is intended to be used as a marshalable pointer. For example, a DVVTriplet can be used to construct the -// key to getting a Redelegation from state. -type DVVTriplet struct { - DelegatorAddress sdk.AccAddress - ValidatorSrcAddress sdk.ValAddress - ValidatorDstAddress sdk.ValAddress +// String implements the Stringer interface for a DVPair object. +func (dv DVPair) String() string { + out, _ := yaml.Marshal(dv) + return string(out) } -// Implements Delegation interface -var _ exported.DelegationI = Delegation{} - -// Delegation represents the bond with tokens held by an account. It is -// owned by one delegator, and is associated with the voting power of one -// validator. -type Delegation struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Shares sdk.Dec `json:"shares" yaml:"shares"` +// String implements the Stringer interface for a DVVTriplet object. +func (dvv DVVTriplet) String() string { + out, _ := yaml.Marshal(dvv) + return string(out) } // NewDelegation creates a new delegation object -func NewDelegation(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, - shares sdk.Dec) Delegation { - +//nolint:interfacer +func NewDelegation(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, shares sdk.Dec) Delegation { return Delegation{ - DelegatorAddress: delegatorAddr, - ValidatorAddress: validatorAddr, + DelegatorAddress: delegatorAddr.String(), + ValidatorAddress: validatorAddr.String(), Shares: shares, } } // MustMarshalDelegation returns the delegation bytes. Panics if fails -func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { - return cdc.MustMarshalBinaryLengthPrefixed(delegation) +func MustMarshalDelegation(cdc codec.BinaryMarshaler, delegation Delegation) []byte { + return cdc.MustMarshalBinaryBare(&delegation) } // MustUnmarshalDelegation return the unmarshaled delegation from bytes. // Panics if fails. -func MustUnmarshalDelegation(cdc *codec.Codec, value []byte) Delegation { +func MustUnmarshalDelegation(cdc codec.BinaryMarshaler, value []byte) Delegation { delegation, err := UnmarshalDelegation(cdc, value) if err != nil { panic(err) } + return delegation } // return the delegation -func UnmarshalDelegation(cdc *codec.Codec, value []byte) (delegation Delegation, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, &delegation) +func UnmarshalDelegation(cdc codec.BinaryMarshaler, value []byte) (delegation Delegation, err error) { + err = cdc.UnmarshalBinaryBare(value, &delegation) return delegation, err } -// nolint -func (d Delegation) Equal(d2 Delegation) bool { - return bytes.Equal(d.DelegatorAddress, d2.DelegatorAddress) && - bytes.Equal(d.ValidatorAddress, d2.ValidatorAddress) && - d.Shares.Equal(d2.Shares) +func (d Delegation) GetDelegatorAddr() sdk.AccAddress { + delAddr, err := sdk.AccAddressFromBech32(d.DelegatorAddress) + if err != nil { + panic(err) + } + return delAddr } - -// nolint - for Delegation -func (d Delegation) GetDelegatorAddr() sdk.AccAddress { return d.DelegatorAddress } -func (d Delegation) GetValidatorAddr() sdk.ValAddress { return d.ValidatorAddress } -func (d Delegation) GetShares() sdk.Dec { return d.Shares } +func (d Delegation) GetValidatorAddr() sdk.ValAddress { + addr, err := sdk.ValAddressFromBech32(d.ValidatorAddress) + if err != nil { + panic(err) + } + return addr +} +func (d Delegation) GetShares() sdk.Dec { return d.Shares } // String returns a human readable string representation of a Delegation. func (d Delegation) String() string { - return fmt.Sprintf(`Delegation: - Delegator: %s - Validator: %s - Shares: %s`, d.DelegatorAddress, - d.ValidatorAddress, d.Shares) + out, _ := yaml.Marshal(d) + return string(out) } // Delegations is a collection of delegations @@ -101,23 +88,23 @@ func (d Delegations) String() (out string) { for _, del := range d { out += del.String() + "\n" } + return strings.TrimSpace(out) } -// UnbondingDelegation stores all of a single delegator's unbonding bonds -// for a single validator in an time-ordered list -type UnbondingDelegation struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // delegator - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` // validator unbonding from operator addr - Entries []UnbondingDelegationEntry `json:"entries" yaml:"entries"` // unbonding delegation entries +func NewUnbondingDelegationEntry(creationHeight int64, completionTime time.Time, balance sdk.Int) UnbondingDelegationEntry { + return UnbondingDelegationEntry{ + CreationHeight: creationHeight, + CompletionTime: completionTime, + InitialBalance: balance, + Balance: balance, + } } -// UnbondingDelegationEntry - entry to an UnbondingDelegation -type UnbondingDelegationEntry struct { - CreationHeight int64 `json:"creation_height" yaml:"creation_height"` // height which the unbonding took place - CompletionTime time.Time `json:"completion_time" yaml:"completion_time"` // time at which the unbonding delegation will complete - InitialBalance sdk.Int `json:"initial_balance" yaml:"initial_balance"` // atoms initially scheduled to receive at completion - Balance sdk.Int `json:"balance" yaml:"balance"` // atoms to receive at completion +// String implements the stringer interface for a UnbondingDelegationEntry. +func (e UnbondingDelegationEntry) String() string { + out, _ := yaml.Marshal(e) + return string(out) } // IsMature - is the current entry mature @@ -126,84 +113,66 @@ func (e UnbondingDelegationEntry) IsMature(currentTime time.Time) bool { } // NewUnbondingDelegation - create a new unbonding delegation object -func NewUnbondingDelegation(delegatorAddr sdk.AccAddress, - validatorAddr sdk.ValAddress, creationHeight int64, minTime time.Time, - balance sdk.Int) UnbondingDelegation { - - entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance) +//nolint:interfacer +func NewUnbondingDelegation( + delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, + creationHeight int64, minTime time.Time, balance sdk.Int, +) UnbondingDelegation { return UnbondingDelegation{ - DelegatorAddress: delegatorAddr, - ValidatorAddress: validatorAddr, - Entries: []UnbondingDelegationEntry{entry}, - } -} - -// NewUnbondingDelegationEntry - create a new unbonding delegation object -func NewUnbondingDelegationEntry(creationHeight int64, completionTime time.Time, - balance sdk.Int) UnbondingDelegationEntry { - - return UnbondingDelegationEntry{ - CreationHeight: creationHeight, - CompletionTime: completionTime, - InitialBalance: balance, - Balance: balance, + DelegatorAddress: delegatorAddr.String(), + ValidatorAddress: validatorAddr.String(), + Entries: []UnbondingDelegationEntry{ + NewUnbondingDelegationEntry(creationHeight, minTime, balance), + }, } } // AddEntry - append entry to the unbonding delegation -func (d *UnbondingDelegation) AddEntry(creationHeight int64, - minTime time.Time, balance sdk.Int) { - +func (ubd *UnbondingDelegation) AddEntry(creationHeight int64, minTime time.Time, balance sdk.Int) { entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance) - d.Entries = append(d.Entries, entry) + ubd.Entries = append(ubd.Entries, entry) } // RemoveEntry - remove entry at index i to the unbonding delegation -func (d *UnbondingDelegation) RemoveEntry(i int64) { - d.Entries = append(d.Entries[:i], d.Entries[i+1:]...) +func (ubd *UnbondingDelegation) RemoveEntry(i int64) { + ubd.Entries = append(ubd.Entries[:i], ubd.Entries[i+1:]...) } // return the unbonding delegation -func MustMarshalUBD(cdc *codec.Codec, ubd UnbondingDelegation) []byte { - return cdc.MustMarshalBinaryLengthPrefixed(ubd) +func MustMarshalUBD(cdc codec.BinaryMarshaler, ubd UnbondingDelegation) []byte { + return cdc.MustMarshalBinaryBare(&ubd) } // unmarshal a unbonding delegation from a store value -func MustUnmarshalUBD(cdc *codec.Codec, value []byte) UnbondingDelegation { +func MustUnmarshalUBD(cdc codec.BinaryMarshaler, value []byte) UnbondingDelegation { ubd, err := UnmarshalUBD(cdc, value) if err != nil { panic(err) } + return ubd } // unmarshal a unbonding delegation from a store value -func UnmarshalUBD(cdc *codec.Codec, value []byte) (ubd UnbondingDelegation, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, &ubd) +func UnmarshalUBD(cdc codec.BinaryMarshaler, value []byte) (ubd UnbondingDelegation, err error) { + err = cdc.UnmarshalBinaryBare(value, &ubd) return ubd, err } -// nolint -// inefficient but only used in testing -func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool { - bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d) - bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d2) - return bytes.Equal(bz1, bz2) -} - // String returns a human readable string representation of an UnbondingDelegation. -func (d UnbondingDelegation) String() string { +func (ubd UnbondingDelegation) String() string { out := fmt.Sprintf(`Unbonding Delegations between: Delegator: %s Validator: %s - Entries:`, d.DelegatorAddress, d.ValidatorAddress) - for i, entry := range d.Entries { + Entries:`, ubd.DelegatorAddress, ubd.ValidatorAddress) + for i, entry := range ubd.Entries { out += fmt.Sprintf(` Unbonding Delegation %d: Creation Height: %v Min time to unbond (unix): %v Expected balance: %s`, i, entry.CreationHeight, entry.CompletionTime, entry.Balance) } + return out } @@ -214,49 +183,11 @@ func (ubds UnbondingDelegations) String() (out string) { for _, u := range ubds { out += u.String() + "\n" } - return strings.TrimSpace(out) -} - -// 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 sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // delegator - ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address" yaml:"validator_src_address"` // validator redelegation source operator addr - ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address" yaml:"validator_dst_address"` // validator redelegation destination operator addr - Entries []RedelegationEntry `json:"entries" yaml:"entries"` // redelegation entries -} - -// RedelegationEntry - entry to a Redelegation -type RedelegationEntry struct { - CreationHeight int64 `json:"creation_height" yaml:"creation_height"` // height at which the redelegation took place - CompletionTime time.Time `json:"completion_time" yaml:"completion_time"` // time at which the redelegation will complete - InitialBalance sdk.Int `json:"initial_balance" yaml:"initial_balance"` // initial balance when redelegation started - SharesDst sdk.Dec `json:"shares_dst" yaml:"shares_dst"` // amount of destination-validator shares created by redelegation -} -// NewRedelegation - create a new redelegation object -func NewRedelegation(delegatorAddr sdk.AccAddress, validatorSrcAddr, - validatorDstAddr sdk.ValAddress, creationHeight int64, - minTime time.Time, balance sdk.Int, - sharesDst sdk.Dec) Redelegation { - - entry := NewRedelegationEntry(creationHeight, - minTime, balance, sharesDst) - - return Redelegation{ - DelegatorAddress: delegatorAddr, - ValidatorSrcAddress: validatorSrcAddr, - ValidatorDstAddress: validatorDstAddr, - Entries: []RedelegationEntry{entry}, - } + return strings.TrimSpace(out) } -// NewRedelegationEntry - create a new redelegation object -func NewRedelegationEntry(creationHeight int64, - completionTime time.Time, balance sdk.Int, - sharesDst sdk.Dec) RedelegationEntry { - +func NewRedelegationEntry(creationHeight int64, completionTime time.Time, balance sdk.Int, sharesDst sdk.Dec) RedelegationEntry { return RedelegationEntry{ CreationHeight: creationHeight, CompletionTime: completionTime, @@ -265,65 +196,76 @@ func NewRedelegationEntry(creationHeight int64, } } +// String implements the Stringer interface for a RedelegationEntry object. +func (e RedelegationEntry) String() string { + out, _ := yaml.Marshal(e) + return string(out) +} + // IsMature - is the current entry mature func (e RedelegationEntry) IsMature(currentTime time.Time) bool { return !e.CompletionTime.After(currentTime) } -// AddEntry - append entry to the unbonding delegation -func (d *Redelegation) AddEntry(creationHeight int64, - minTime time.Time, balance sdk.Int, - sharesDst sdk.Dec) { +//nolint:interfacer +func NewRedelegation( + delegatorAddr sdk.AccAddress, validatorSrcAddr, validatorDstAddr sdk.ValAddress, + creationHeight int64, minTime time.Time, balance sdk.Int, sharesDst sdk.Dec, +) Redelegation { + return Redelegation{ + DelegatorAddress: delegatorAddr.String(), + ValidatorSrcAddress: validatorSrcAddr.String(), + ValidatorDstAddress: validatorDstAddr.String(), + Entries: []RedelegationEntry{ + NewRedelegationEntry(creationHeight, minTime, balance, sharesDst), + }, + } +} +// AddEntry - append entry to the unbonding delegation +func (red *Redelegation) AddEntry(creationHeight int64, minTime time.Time, balance sdk.Int, sharesDst sdk.Dec) { entry := NewRedelegationEntry(creationHeight, minTime, balance, sharesDst) - d.Entries = append(d.Entries, entry) + red.Entries = append(red.Entries, entry) } // RemoveEntry - remove entry at index i to the unbonding delegation -func (d *Redelegation) RemoveEntry(i int64) { - d.Entries = append(d.Entries[:i], d.Entries[i+1:]...) +func (red *Redelegation) RemoveEntry(i int64) { + red.Entries = append(red.Entries[:i], red.Entries[i+1:]...) } // MustMarshalRED returns the Redelegation bytes. Panics if fails. -func MustMarshalRED(cdc *codec.Codec, red Redelegation) []byte { - return cdc.MustMarshalBinaryLengthPrefixed(red) +func MustMarshalRED(cdc codec.BinaryMarshaler, red Redelegation) []byte { + return cdc.MustMarshalBinaryBare(&red) } // MustUnmarshalRED unmarshals a redelegation from a store value. Panics if fails. -func MustUnmarshalRED(cdc *codec.Codec, value []byte) Redelegation { +func MustUnmarshalRED(cdc codec.BinaryMarshaler, value []byte) Redelegation { red, err := UnmarshalRED(cdc, value) if err != nil { panic(err) } + return red } // UnmarshalRED unmarshals a redelegation from a store value -func UnmarshalRED(cdc *codec.Codec, value []byte) (red Redelegation, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, &red) +func UnmarshalRED(cdc codec.BinaryMarshaler, value []byte) (red Redelegation, err error) { + err = cdc.UnmarshalBinaryBare(value, &red) return red, err } -// nolint -// inefficient but only used in tests -func (d Redelegation) Equal(d2 Redelegation) bool { - bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d) - bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d2) - return bytes.Equal(bz1, bz2) -} - // String returns a human readable string representation of a Redelegation. -func (d Redelegation) String() string { +func (red Redelegation) String() string { out := fmt.Sprintf(`Redelegations between: Delegator: %s Source Validator: %s Destination Validator: %s Entries: `, - d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress, + red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress, ) - for i, entry := range d.Entries { + for i, entry := range red.Entries { out += fmt.Sprintf(` Redelegation Entry #%d: Creation height: %v Min time to unbond (unix): %v @@ -343,19 +285,13 @@ func (d Redelegations) String() (out string) { for _, red := range d { out += red.String() + "\n" } + return strings.TrimSpace(out) } // ---------------------------------------------------------------------------- // Client Types -// DelegationResponse is equivalent to Delegation except that it contains a balance -// in addition to shares which is more suitable for client responses. -type DelegationResponse struct { - Delegation - Balance sdk.Coin `json:"balance" yaml:"balance"` -} - // NewDelegationResp creates a new DelegationResponse instance func NewDelegationResp( delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, shares sdk.Dec, balance sdk.Coin, @@ -393,39 +329,25 @@ func (d DelegationResponses) String() (out string) { for _, del := range d { out += del.String() + "\n" } - return strings.TrimSpace(out) -} -// RedelegationResponse is equivalent to a Redelegation except that its entries -// contain a balance in addition to shares which is more suitable for client -// responses. -type RedelegationResponse struct { - Redelegation - Entries []RedelegationEntryResponse `json:"entries" yaml:"entries"` + return strings.TrimSpace(out) } // NewRedelegationResponse crates a new RedelegationEntryResponse instance. +//nolint:interfacer func NewRedelegationResponse( delegatorAddr sdk.AccAddress, validatorSrc, validatorDst sdk.ValAddress, entries []RedelegationEntryResponse, ) RedelegationResponse { return RedelegationResponse{ Redelegation: Redelegation{ - DelegatorAddress: delegatorAddr, - ValidatorSrcAddress: validatorSrc, - ValidatorDstAddress: validatorDst, + DelegatorAddress: delegatorAddr.String(), + ValidatorSrcAddress: validatorSrc.String(), + ValidatorDstAddress: validatorDst.String(), }, Entries: entries, } } -// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it -// contains a balance in addition to shares which is more suitable for client -// responses. -type RedelegationEntryResponse struct { - RedelegationEntry - Balance sdk.Int `json:"balance"` -} - // NewRedelegationEntryResponse creates a new RedelegationEntryResponse instance. func NewRedelegationEntryResponse( creationHeight int64, completionTime time.Time, sharesDst sdk.Dec, initialBalance, balance sdk.Int) RedelegationEntryResponse { @@ -435,32 +357,6 @@ func NewRedelegationEntryResponse( } } -// String implements the Stringer interface for RedelegationResp. -func (r RedelegationResponse) String() string { - out := fmt.Sprintf(`Redelegations between: - Delegator: %s - Source Validator: %s - Destination Validator: %s - Entries: -`, - r.DelegatorAddress, r.ValidatorSrcAddress, r.ValidatorDstAddress, - ) - - for i, entry := range r.Entries { - out += fmt.Sprintf(` Redelegation Entry #%d: - Creation height: %v - Min time to unbond (unix): %v - Initial Balance: %s - Shares: %s - Balance: %s -`, - i, entry.CreationHeight, entry.CompletionTime, entry.InitialBalance, entry.SharesDst, entry.Balance, - ) - } - - return strings.TrimRight(out, "\n") -} - type redelegationRespAlias RedelegationResponse // MarshalJSON implements the json.Marshaler interface. This is so we can @@ -482,5 +378,6 @@ func (r RedelegationResponses) String() (out string) { for _, red := range r { out += red.String() + "\n" } + return strings.TrimSpace(out) } diff --git a/x/staking/types/delegation_test.go b/x/staking/types/delegation_test.go index 07496e05601c..7ca5e8132eb4 100644 --- a/x/staking/types/delegation_test.go +++ b/x/staking/types/delegation_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "encoding/json" @@ -9,69 +9,70 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestDelegationEqual(t *testing.T) { - d1 := NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) + d1 := types.NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) d2 := d1 - ok := d1.Equal(d2) + ok := d1.String() == d2.String() require.True(t, ok) - d2.ValidatorAddress = valAddr3 + d2.ValidatorAddress = valAddr3.String() d2.Shares = sdk.NewDec(200) - ok = d1.Equal(d2) + ok = d1.String() == d2.String() require.False(t, ok) } func TestDelegationString(t *testing.T) { - d := NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) + d := types.NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) require.NotEmpty(t, d.String()) } func TestUnbondingDelegationEqual(t *testing.T) { - ubd1 := NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, + ubd1 := types.NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, time.Unix(0, 0), sdk.NewInt(0)) ubd2 := ubd1 - ok := ubd1.Equal(ubd2) + ok := ubd1.String() == ubd2.String() require.True(t, ok) - ubd2.ValidatorAddress = valAddr3 + ubd2.ValidatorAddress = valAddr3.String() ubd2.Entries[0].CompletionTime = time.Unix(20*20*2, 0) - ok = ubd1.Equal(ubd2) + ok = (ubd1.String() == ubd2.String()) require.False(t, ok) } func TestUnbondingDelegationString(t *testing.T) { - ubd := NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, + ubd := types.NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, time.Unix(0, 0), sdk.NewInt(0)) require.NotEmpty(t, ubd.String()) } func TestRedelegationEqual(t *testing.T) { - r1 := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + r1 := types.NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, time.Unix(0, 0), sdk.NewInt(0), sdk.NewDec(0)) - r2 := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + r2 := types.NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, time.Unix(0, 0), sdk.NewInt(0), sdk.NewDec(0)) - ok := r1.Equal(r2) + ok := r1.String() == r2.String() require.True(t, ok) r2.Entries[0].SharesDst = sdk.NewDec(10) r2.Entries[0].CompletionTime = time.Unix(20*20*2, 0) - ok = r1.Equal(r2) + ok = r1.String() == r2.String() require.False(t, ok) } func TestRedelegationString(t *testing.T) { - r := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + r := types.NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, time.Unix(0, 0), sdk.NewInt(0), sdk.NewDec(10)) @@ -79,12 +80,12 @@ func TestRedelegationString(t *testing.T) { } func TestDelegationResponses(t *testing.T) { - cdc := codec.New() - dr1 := NewDelegationResp(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(5), + cdc := codec.NewLegacyAmino() + dr1 := types.NewDelegationResp(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(5), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5))) - dr2 := NewDelegationResp(sdk.AccAddress(valAddr1), valAddr3, sdk.NewDec(5), + dr2 := types.NewDelegationResp(sdk.AccAddress(valAddr1), valAddr3, sdk.NewDec(5), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5))) - drs := DelegationResponses{dr1, dr2} + drs := types.DelegationResponses{dr1, dr2} bz1, err := json.Marshal(dr1) require.NoError(t, err) @@ -102,20 +103,20 @@ func TestDelegationResponses(t *testing.T) { require.Equal(t, bz1, bz2) - var drs2 DelegationResponses + var drs2 types.DelegationResponses require.NoError(t, cdc.UnmarshalJSON(bz2, &drs2)) require.Equal(t, drs, drs2) } func TestRedelegationResponses(t *testing.T) { - cdc := codec.New() - entries := []RedelegationEntryResponse{ - NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), - NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + cdc := codec.NewLegacyAmino() + entries := []types.RedelegationEntryResponse{ + types.NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + types.NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), } - rdr1 := NewRedelegationResponse(sdk.AccAddress(valAddr1), valAddr2, valAddr3, entries) - rdr2 := NewRedelegationResponse(sdk.AccAddress(valAddr2), valAddr1, valAddr3, entries) - rdrs := RedelegationResponses{rdr1, rdr2} + rdr1 := types.NewRedelegationResponse(sdk.AccAddress(valAddr1), valAddr2, valAddr3, entries) + rdr2 := types.NewRedelegationResponse(sdk.AccAddress(valAddr2), valAddr1, valAddr3, entries) + rdrs := types.RedelegationResponses{rdr1, rdr2} bz1, err := json.Marshal(rdr1) require.NoError(t, err) @@ -133,7 +134,7 @@ func TestRedelegationResponses(t *testing.T) { require.Equal(t, bz1, bz2) - var rdrs2 RedelegationResponses + var rdrs2 types.RedelegationResponses require.NoError(t, cdc.UnmarshalJSON(bz2, &rdrs2)) bz3, err := cdc.MarshalJSON(rdrs2) diff --git a/x/staking/types/errors.go b/x/staking/types/errors.go index d3ccf0a9dac2..d6c31a15dc74 100644 --- a/x/staking/types/errors.go +++ b/x/staking/types/errors.go @@ -11,49 +11,50 @@ import ( // // REF: https://github.com/cosmos/cosmos-sdk/issues/5450 var ( - ErrEmptyValidatorAddr = sdkerrors.Register(ModuleName, 1, "empty validator address") - ErrBadValidatorAddr = sdkerrors.Register(ModuleName, 2, "validator address is invalid") - ErrNoValidatorFound = sdkerrors.Register(ModuleName, 3, "validator does not exist") - ErrValidatorOwnerExists = sdkerrors.Register(ModuleName, 4, "validator already exist for this operator address; must use new validator operator address") - ErrValidatorPubKeyExists = sdkerrors.Register(ModuleName, 5, "validator already exist for this pubkey; must use new validator pubkey") - ErrValidatorPubKeyTypeNotSupported = sdkerrors.Register(ModuleName, 6, "validator pubkey type is not supported") - ErrValidatorJailed = sdkerrors.Register(ModuleName, 7, "validator for this address is currently jailed") - ErrBadRemoveValidator = sdkerrors.Register(ModuleName, 8, "failed to remove validator") - ErrCommissionNegative = sdkerrors.Register(ModuleName, 9, "commission must be positive") - ErrCommissionHuge = sdkerrors.Register(ModuleName, 10, "commission cannot be more than 100%") - ErrCommissionGTMaxRate = sdkerrors.Register(ModuleName, 11, "commission cannot be more than the max rate") - ErrCommissionUpdateTime = sdkerrors.Register(ModuleName, 12, "commission cannot be changed more than once in 24h") - ErrCommissionChangeRateNegative = sdkerrors.Register(ModuleName, 13, "commission change rate must be positive") - ErrCommissionChangeRateGTMaxRate = sdkerrors.Register(ModuleName, 14, "commission change rate cannot be more than the max rate") - ErrCommissionGTMaxChangeRate = sdkerrors.Register(ModuleName, 15, "commission cannot be changed more than max change rate") - ErrSelfDelegationBelowMinimum = sdkerrors.Register(ModuleName, 16, "validator's self delegation must be greater than their minimum self delegation") - ErrMinSelfDelegationInvalid = sdkerrors.Register(ModuleName, 17, "minimum self delegation must be a positive integer") - ErrMinSelfDelegationDecreased = sdkerrors.Register(ModuleName, 18, "minimum self delegation cannot be decrease") - ErrEmptyDelegatorAddr = sdkerrors.Register(ModuleName, 19, "empty delegator address") - ErrBadDenom = sdkerrors.Register(ModuleName, 20, "invalid coin denomination") - ErrBadDelegationAddr = sdkerrors.Register(ModuleName, 21, "invalid address for (address, validator) tuple") - ErrBadDelegationAmount = sdkerrors.Register(ModuleName, 22, "invalid delegation amount") - ErrNoDelegation = sdkerrors.Register(ModuleName, 23, "no delegation for (address, validator) tuple") - ErrBadDelegatorAddr = sdkerrors.Register(ModuleName, 24, "delegator does not exist with address") - ErrNoDelegatorForAddress = sdkerrors.Register(ModuleName, 25, "delegator does not contain delegation") - ErrInsufficientShares = sdkerrors.Register(ModuleName, 26, "insufficient delegation shares") - ErrDelegationValidatorEmpty = sdkerrors.Register(ModuleName, 27, "cannot delegate to an empty validator") - ErrNotEnoughDelegationShares = sdkerrors.Register(ModuleName, 28, "not enough delegation shares") - ErrBadSharesAmount = sdkerrors.Register(ModuleName, 29, "invalid shares amount") - ErrBadSharesPercent = sdkerrors.Register(ModuleName, 30, "Invalid shares percent") - ErrNotMature = sdkerrors.Register(ModuleName, 31, "entry not mature") - ErrNoUnbondingDelegation = sdkerrors.Register(ModuleName, 32, "no unbonding delegation found") - ErrMaxUnbondingDelegationEntries = sdkerrors.Register(ModuleName, 33, "too many unbonding delegation entries for (delegator, validator) tuple") - ErrBadRedelegationAddr = sdkerrors.Register(ModuleName, 34, "invalid address for (address, src-validator, dst-validator) tuple") - ErrNoRedelegation = sdkerrors.Register(ModuleName, 35, "no redelegation found") - ErrSelfRedelegation = sdkerrors.Register(ModuleName, 36, "cannot redelegate to the same validator") - ErrTinyRedelegationAmount = sdkerrors.Register(ModuleName, 37, "too few tokens to redelegate (truncates to zero tokens)") - ErrBadRedelegationDst = sdkerrors.Register(ModuleName, 38, "redelegation destination validator not found") - ErrTransitiveRedelegation = sdkerrors.Register(ModuleName, 39, "redelegation to this validator already in progress; first redelegation to this validator must complete before next redelegation") - ErrMaxRedelegationEntries = sdkerrors.Register(ModuleName, 40, "too many redelegation entries for (delegator, src-validator, dst-validator) tuple") - ErrDelegatorShareExRateInvalid = sdkerrors.Register(ModuleName, 41, "cannot delegate to validators with invalid (zero) ex-rate") - ErrBothShareMsgsGiven = sdkerrors.Register(ModuleName, 42, "both shares amount and shares percent provided") - ErrNeitherShareMsgsGiven = sdkerrors.Register(ModuleName, 43, "neither shares amount nor shares percent provided") - ErrInvalidHistoricalInfo = sdkerrors.Register(ModuleName, 44, "invalid historical info") - ErrNoHistoricalInfo = sdkerrors.Register(ModuleName, 45, "no historical info found") + ErrEmptyValidatorAddr = sdkerrors.Register(ModuleName, 2, "empty validator address") + ErrBadValidatorAddr = sdkerrors.Register(ModuleName, 3, "validator address is invalid") + ErrNoValidatorFound = sdkerrors.Register(ModuleName, 4, "validator does not exist") + ErrValidatorOwnerExists = sdkerrors.Register(ModuleName, 5, "validator already exist for this operator address; must use new validator operator address") + ErrValidatorPubKeyExists = sdkerrors.Register(ModuleName, 6, "validator already exist for this pubkey; must use new validator pubkey") + ErrValidatorPubKeyTypeNotSupported = sdkerrors.Register(ModuleName, 7, "validator pubkey type is not supported") + ErrValidatorJailed = sdkerrors.Register(ModuleName, 8, "validator for this address is currently jailed") + ErrBadRemoveValidator = sdkerrors.Register(ModuleName, 9, "failed to remove validator") + ErrCommissionNegative = sdkerrors.Register(ModuleName, 10, "commission must be positive") + ErrCommissionHuge = sdkerrors.Register(ModuleName, 11, "commission cannot be more than 100%") + ErrCommissionGTMaxRate = sdkerrors.Register(ModuleName, 12, "commission cannot be more than the max rate") + ErrCommissionUpdateTime = sdkerrors.Register(ModuleName, 13, "commission cannot be changed more than once in 24h") + ErrCommissionChangeRateNegative = sdkerrors.Register(ModuleName, 14, "commission change rate must be positive") + ErrCommissionChangeRateGTMaxRate = sdkerrors.Register(ModuleName, 15, "commission change rate cannot be more than the max rate") + ErrCommissionGTMaxChangeRate = sdkerrors.Register(ModuleName, 16, "commission cannot be changed more than max change rate") + ErrSelfDelegationBelowMinimum = sdkerrors.Register(ModuleName, 17, "validator's self delegation must be greater than their minimum self delegation") + ErrMinSelfDelegationInvalid = sdkerrors.Register(ModuleName, 18, "minimum self delegation must be a positive integer") + ErrMinSelfDelegationDecreased = sdkerrors.Register(ModuleName, 19, "minimum self delegation cannot be decrease") + ErrEmptyDelegatorAddr = sdkerrors.Register(ModuleName, 20, "empty delegator address") + ErrBadDenom = sdkerrors.Register(ModuleName, 21, "invalid coin denomination") + ErrBadDelegationAddr = sdkerrors.Register(ModuleName, 22, "invalid address for (address, validator) tuple") + ErrBadDelegationAmount = sdkerrors.Register(ModuleName, 23, "invalid delegation amount") + ErrNoDelegation = sdkerrors.Register(ModuleName, 24, "no delegation for (address, validator) tuple") + ErrBadDelegatorAddr = sdkerrors.Register(ModuleName, 25, "delegator does not exist with address") + ErrNoDelegatorForAddress = sdkerrors.Register(ModuleName, 26, "delegator does not contain delegation") + ErrInsufficientShares = sdkerrors.Register(ModuleName, 27, "insufficient delegation shares") + ErrDelegationValidatorEmpty = sdkerrors.Register(ModuleName, 28, "cannot delegate to an empty validator") + ErrNotEnoughDelegationShares = sdkerrors.Register(ModuleName, 29, "not enough delegation shares") + ErrBadSharesAmount = sdkerrors.Register(ModuleName, 30, "invalid shares amount") + ErrBadSharesPercent = sdkerrors.Register(ModuleName, 31, "Invalid shares percent") + ErrNotMature = sdkerrors.Register(ModuleName, 32, "entry not mature") + ErrNoUnbondingDelegation = sdkerrors.Register(ModuleName, 33, "no unbonding delegation found") + ErrMaxUnbondingDelegationEntries = sdkerrors.Register(ModuleName, 34, "too many unbonding delegation entries for (delegator, validator) tuple") + ErrBadRedelegationAddr = sdkerrors.Register(ModuleName, 35, "invalid address for (address, src-validator, dst-validator) tuple") + ErrNoRedelegation = sdkerrors.Register(ModuleName, 36, "no redelegation found") + ErrSelfRedelegation = sdkerrors.Register(ModuleName, 37, "cannot redelegate to the same validator") + ErrTinyRedelegationAmount = sdkerrors.Register(ModuleName, 38, "too few tokens to redelegate (truncates to zero tokens)") + ErrBadRedelegationDst = sdkerrors.Register(ModuleName, 39, "redelegation destination validator not found") + ErrTransitiveRedelegation = sdkerrors.Register(ModuleName, 40, "redelegation to this validator already in progress; first redelegation to this validator must complete before next redelegation") + ErrMaxRedelegationEntries = sdkerrors.Register(ModuleName, 41, "too many redelegation entries for (delegator, src-validator, dst-validator) tuple") + ErrDelegatorShareExRateInvalid = sdkerrors.Register(ModuleName, 42, "cannot delegate to validators with invalid (zero) ex-rate") + ErrBothShareMsgsGiven = sdkerrors.Register(ModuleName, 43, "both shares amount and shares percent provided") + ErrNeitherShareMsgsGiven = sdkerrors.Register(ModuleName, 44, "neither shares amount nor shares percent provided") + ErrInvalidHistoricalInfo = sdkerrors.Register(ModuleName, 45, "invalid historical info") + ErrNoHistoricalInfo = sdkerrors.Register(ModuleName, 46, "no historical info found") + ErrEmptyValidatorPubKey = sdkerrors.Register(ModuleName, 47, "empty validator public key") ) diff --git a/x/staking/types/expected_keepers.go b/x/staking/types/expected_keepers.go index 5e98f0abbed0..1c1d7f7de96b 100644 --- a/x/staking/types/expected_keepers.go +++ b/x/staking/types/expected_keepers.go @@ -2,9 +2,8 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" - supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" ) // DistributionKeeper expected distribution keeper (noalias) @@ -15,19 +14,25 @@ type DistributionKeeper interface { // AccountKeeper defines the expected account keeper (noalias) type AccountKeeper interface { - IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool)) - GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account // only used for simulation -} - -// SupplyKeeper defines the expected supply Keeper (noalias) -type SupplyKeeper interface { - GetSupply(ctx sdk.Context) supplyexported.SupplyI + IterateAccounts(ctx sdk.Context, process func(authtypes.AccountI) (stop bool)) + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI // only used for simulation GetModuleAddress(name string) sdk.AccAddress - GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI + GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 - SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) + SetModuleAccount(sdk.Context, authtypes.ModuleAccountI) +} + +// BankKeeper defines the expected interface needed to retrieve account balances. +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 + + GetSupply(ctx sdk.Context) bankexported.SupplyI SendCoinsFromModuleToModule(ctx sdk.Context, senderPool, recipientPool string, amt sdk.Coins) error UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error @@ -40,20 +45,20 @@ type SupplyKeeper interface { type ValidatorSet interface { // iterate through validators by operator address, execute func for each validator IterateValidators(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator ValidatorI) (stop bool)) // iterate through bonded validators by operator address, execute func for each validator IterateBondedValidatorsByPower(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator ValidatorI) (stop bool)) // iterate through the consensus validator set of the last block by operator address, execute func for each validator IterateLastValidators(sdk.Context, - func(index int64, validator stakingexported.ValidatorI) (stop bool)) + func(index int64, validator ValidatorI) (stop bool)) - Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address - ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address - TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set - StakingTokenSupply(sdk.Context) sdk.Int // total staking token supply + Validator(sdk.Context, sdk.ValAddress) ValidatorI // get a particular validator by operator address + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) ValidatorI // get a particular validator by consensus address + TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set + StakingTokenSupply(sdk.Context) sdk.Int // total staking token supply // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) @@ -62,10 +67,10 @@ type ValidatorSet interface { // Delegation allows for getting a particular delegation for a given validator // and delegator outside the scope of the staking module. - Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI + Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) DelegationI // MaxValidators returns the maximum amount of bonded validators - MaxValidators(sdk.Context) uint16 + MaxValidators(sdk.Context) uint32 } // DelegationSet expected properties for the set of all delegations for a particular (noalias) @@ -75,7 +80,7 @@ type DelegationSet interface { // iterate through all delegations from one delegator by validator-AccAddress, // execute func for each validator IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, - fn func(index int64, delegation stakingexported.DelegationI) (stop bool)) + fn func(index int64, delegation DelegationI) (stop bool)) } //_______________________________________________________________________________ diff --git a/x/staking/types/exported.go b/x/staking/types/exported.go new file mode 100644 index 000000000000..a02adc34bade --- /dev/null +++ b/x/staking/types/exported.go @@ -0,0 +1,40 @@ +package types + +import ( + tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DelegationI delegation bond for a delegated proof of stake system +type DelegationI interface { + GetDelegatorAddr() sdk.AccAddress // delegator sdk.AccAddress for the bond + GetValidatorAddr() sdk.ValAddress // validator operator address + GetShares() sdk.Dec // amount of validator's shares held in this delegation +} + +// ValidatorI expected validator functions +type ValidatorI interface { + IsJailed() bool // whether the validator is jailed + GetMoniker() string // moniker of the validator + GetStatus() BondStatus // status of the validator + IsBonded() bool // check if has a bonded status + IsUnbonded() bool // check if has status unbonded + IsUnbonding() bool // check if has status unbonding + GetOperator() sdk.ValAddress // operator address to receive/return validators coins + ConsPubKey() (cryptotypes.PubKey, error) // validation consensus pubkey (cryptotypes.PubKey) + TmConsPublicKey() (tmprotocrypto.PublicKey, error) // validation consensus pubkey (Tendermint) + GetConsAddr() (sdk.ConsAddress, error) // validation consensus address + GetTokens() sdk.Int // validation tokens + GetBondedTokens() sdk.Int // validator bonded tokens + GetConsensusPower() int64 // validation power in tendermint + GetCommission() sdk.Dec // validator commission rate + GetMinSelfDelegation() sdk.Int // validator minimum self delegation + GetDelegatorShares() sdk.Dec // total outstanding delegator shares + TokensFromShares(sdk.Dec) sdk.Dec // token worth of provided delegator shares + TokensFromSharesTruncated(sdk.Dec) sdk.Dec // token worth of provided delegator shares, truncated + TokensFromSharesRoundUp(sdk.Dec) sdk.Dec // token worth of provided delegator shares, rounded up + SharesFromTokens(amt sdk.Int) (sdk.Dec, error) // shares worth of delegator's bond + SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, error) // truncated shares worth of delegator's bond +} diff --git a/x/staking/types/genesis.go b/x/staking/types/genesis.go index 943ab4fb31f0..a0a510f6b467 100644 --- a/x/staking/types/genesis.go +++ b/x/staking/types/genesis.go @@ -1,30 +1,15 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState - all staking state that must be provided at genesis -type GenesisState struct { - Params Params `json:"params" yaml:"params"` - LastTotalPower sdk.Int `json:"last_total_power" yaml:"last_total_power"` - LastValidatorPowers []LastValidatorPower `json:"last_validator_powers" yaml:"last_validator_powers"` - Validators Validators `json:"validators" yaml:"validators"` - Delegations Delegations `json:"delegations" yaml:"delegations"` - UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations" yaml:"unbonding_delegations"` - Redelegations []Redelegation `json:"redelegations" yaml:"redelegations"` - Exported bool `json:"exported" yaml:"exported"` -} + "encoding/json" -// LastValidatorPower required for validator set update logic -type LastValidatorPower struct { - Address sdk.ValAddress - Power int64 -} + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" +) // NewGenesisState creates a new GenesisState instanc e -func NewGenesisState(params Params, validators []Validator, delegations []Delegation) GenesisState { - return GenesisState{ +func NewGenesisState(params Params, validators []Validator, delegations []Delegation) *GenesisState { + return &GenesisState{ Params: params, Validators: validators, Delegations: delegations, @@ -32,8 +17,30 @@ func NewGenesisState(params Params, validators []Validator, delegations []Delega } // DefaultGenesisState gets the raw genesis raw message for testing -func DefaultGenesisState() GenesisState { - return GenesisState{ +func DefaultGenesisState() *GenesisState { + return &GenesisState{ Params: DefaultParams(), } } + +// GetGenesisStateFromAppState returns x/staking GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc codec.JSONMarshaler, appState map[string]json.RawMessage) *GenesisState { + var genesisState GenesisState + + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return &genesisState +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (g GenesisState) UnpackInterfaces(c codectypes.AnyUnpacker) error { + for i := range g.Validators { + if err := g.Validators[i].UnpackInterfaces(c); err != nil { + return err + } + } + return nil +} diff --git a/x/staking/types/genesis.pb.go b/x/staking/types/genesis.pb.go new file mode 100644 index 000000000000..2ea6f8bf9964 --- /dev/null +++ b/x/staking/types/genesis.pb.go @@ -0,0 +1,942 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/staking/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "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 defines the staking module's genesis state. +type GenesisState struct { + // params defines all the paramaters of related to deposit. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // last_total_power tracks the total amounts of bonded tokens recorded during + // the previous end block. + LastTotalPower github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=last_total_power,json=lastTotalPower,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"last_total_power" yaml:"last_total_power"` + // last_validator_powers is a special index that provides a historical list + // of the last-block's bonded validators. + LastValidatorPowers []LastValidatorPower `protobuf:"bytes,3,rep,name=last_validator_powers,json=lastValidatorPowers,proto3" json:"last_validator_powers" yaml:"last_validator_powers"` + // delegations defines the validator set at genesis. + Validators []Validator `protobuf:"bytes,4,rep,name=validators,proto3" json:"validators"` + // delegations defines the delegations active at genesis. + Delegations []Delegation `protobuf:"bytes,5,rep,name=delegations,proto3" json:"delegations"` + // unbonding_delegations defines the unbonding delegations active at genesis. + UnbondingDelegations []UnbondingDelegation `protobuf:"bytes,6,rep,name=unbonding_delegations,json=unbondingDelegations,proto3" json:"unbonding_delegations" yaml:"unbonding_delegations"` + // redelegations defines the redelegations active at genesis. + Redelegations []Redelegation `protobuf:"bytes,7,rep,name=redelegations,proto3" json:"redelegations"` + Exported bool `protobuf:"varint,8,opt,name=exported,proto3" json:"exported,omitempty"` +} + +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_9b3dec8894f2831b, []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) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetLastValidatorPowers() []LastValidatorPower { + if m != nil { + return m.LastValidatorPowers + } + return nil +} + +func (m *GenesisState) GetValidators() []Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *GenesisState) GetDelegations() []Delegation { + if m != nil { + return m.Delegations + } + return nil +} + +func (m *GenesisState) GetUnbondingDelegations() []UnbondingDelegation { + if m != nil { + return m.UnbondingDelegations + } + return nil +} + +func (m *GenesisState) GetRedelegations() []Redelegation { + if m != nil { + return m.Redelegations + } + return nil +} + +func (m *GenesisState) GetExported() bool { + if m != nil { + return m.Exported + } + return false +} + +// LastValidatorPower required for validator set update logic. +type LastValidatorPower struct { + // address is the address of the validator. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // power defines the power of the validator. + Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` +} + +func (m *LastValidatorPower) Reset() { *m = LastValidatorPower{} } +func (m *LastValidatorPower) String() string { return proto.CompactTextString(m) } +func (*LastValidatorPower) ProtoMessage() {} +func (*LastValidatorPower) Descriptor() ([]byte, []int) { + return fileDescriptor_9b3dec8894f2831b, []int{1} +} +func (m *LastValidatorPower) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LastValidatorPower) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LastValidatorPower.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 *LastValidatorPower) XXX_Merge(src proto.Message) { + xxx_messageInfo_LastValidatorPower.Merge(m, src) +} +func (m *LastValidatorPower) XXX_Size() int { + return m.Size() +} +func (m *LastValidatorPower) XXX_DiscardUnknown() { + xxx_messageInfo_LastValidatorPower.DiscardUnknown(m) +} + +var xxx_messageInfo_LastValidatorPower proto.InternalMessageInfo + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.staking.v1beta1.GenesisState") + proto.RegisterType((*LastValidatorPower)(nil), "cosmos.staking.v1beta1.LastValidatorPower") +} + +func init() { + proto.RegisterFile("cosmos/staking/v1beta1/genesis.proto", fileDescriptor_9b3dec8894f2831b) +} + +var fileDescriptor_9b3dec8894f2831b = []byte{ + // 493 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x3d, 0x6f, 0xd3, 0x40, + 0x18, 0xc7, 0x7d, 0xa4, 0x49, 0xc3, 0xa5, 0x20, 0x74, 0xa4, 0x60, 0x45, 0xc8, 0x0e, 0x56, 0x84, + 0x22, 0x5e, 0x6c, 0xb5, 0x6c, 0x15, 0x53, 0x84, 0xa8, 0x8a, 0x10, 0x8a, 0x8e, 0x97, 0x81, 0x25, + 0xba, 0xd4, 0x27, 0x63, 0xd5, 0xf1, 0x59, 0x7e, 0x2e, 0xa5, 0xdd, 0x11, 0x62, 0xe4, 0x23, 0xf4, + 0xe3, 0x74, 0xec, 0xc0, 0x80, 0x18, 0x2c, 0x94, 0x2c, 0xcc, 0xfd, 0x04, 0xc8, 0xe7, 0x17, 0x4c, + 0x52, 0x33, 0x25, 0x77, 0xfa, 0xfd, 0x7f, 0x7f, 0xfb, 0xfc, 0x1c, 0x1e, 0x1c, 0x0a, 0x98, 0x09, + 0x70, 0x40, 0xb2, 0x23, 0x3f, 0xf4, 0x9c, 0xe3, 0x9d, 0x29, 0x97, 0x6c, 0xc7, 0xf1, 0x78, 0xc8, + 0xc1, 0x07, 0x3b, 0x8a, 0x85, 0x14, 0xe4, 0x4e, 0x46, 0xd9, 0x39, 0x65, 0xe7, 0x54, 0xaf, 0xeb, + 0x09, 0x4f, 0x28, 0xc4, 0x49, 0xff, 0x65, 0x74, 0xaf, 0xce, 0x59, 0xa4, 0x15, 0x65, 0x7d, 0x6f, + 0xe2, 0xad, 0xfd, 0xac, 0xe5, 0x8d, 0x64, 0x92, 0x93, 0x67, 0xb8, 0x15, 0xb1, 0x98, 0xcd, 0x40, + 0x47, 0x7d, 0x34, 0xec, 0xec, 0x1a, 0xf6, 0xd5, 0xad, 0xf6, 0x58, 0x51, 0xa3, 0x8d, 0xf3, 0xc4, + 0xd4, 0x68, 0x9e, 0x21, 0x80, 0x6f, 0x05, 0x0c, 0xe4, 0x44, 0x0a, 0xc9, 0x82, 0x49, 0x24, 0x3e, + 0xf1, 0x58, 0xbf, 0xd6, 0x47, 0xc3, 0xad, 0xd1, 0x41, 0xca, 0xfd, 0x4c, 0xcc, 0x07, 0x9e, 0x2f, + 0x3f, 0xce, 0xa7, 0xf6, 0xa1, 0x98, 0x39, 0xf9, 0x13, 0x66, 0x3f, 0x4f, 0xc0, 0x3d, 0x72, 0xe4, + 0x69, 0xc4, 0xc1, 0x3e, 0x08, 0xe5, 0x65, 0x62, 0xde, 0x3d, 0x65, 0xb3, 0x60, 0xcf, 0x5a, 0xf5, + 0x59, 0xf4, 0x66, 0xba, 0xf5, 0x36, 0xdd, 0x19, 0xa7, 0x1b, 0xe4, 0x33, 0xc2, 0xdb, 0x8a, 0x3a, + 0x66, 0x81, 0xef, 0x32, 0x29, 0xe2, 0x8c, 0x04, 0xbd, 0xd1, 0x6f, 0x0c, 0x3b, 0xbb, 0x0f, 0xeb, + 0x5e, 0xe1, 0x15, 0x03, 0xf9, 0xbe, 0xc8, 0x28, 0xd7, 0x68, 0x90, 0x3e, 0xe6, 0x65, 0x62, 0xde, + 0xab, 0x94, 0xaf, 0x6a, 0x2d, 0x7a, 0x3b, 0x58, 0x4b, 0x02, 0xd9, 0xc7, 0xb8, 0x24, 0x41, 0xdf, + 0x50, 0xd5, 0xf7, 0xeb, 0xaa, 0xcb, 0x70, 0x7e, 0x80, 0x95, 0x28, 0x79, 0x89, 0x3b, 0x2e, 0x0f, + 0xb8, 0xc7, 0xa4, 0x2f, 0x42, 0xd0, 0x9b, 0xca, 0x64, 0xd5, 0x99, 0x9e, 0x97, 0x68, 0xae, 0xaa, + 0x86, 0xc9, 0x17, 0x84, 0xb7, 0xe7, 0xe1, 0x54, 0x84, 0xae, 0x1f, 0x7a, 0x93, 0xaa, 0xb6, 0xa5, + 0xb4, 0x8f, 0xea, 0xb4, 0xef, 0x8a, 0x50, 0xc5, 0xbf, 0x72, 0x38, 0x57, 0x7a, 0x2d, 0xda, 0x9d, + 0xaf, 0x47, 0x81, 0x8c, 0xf1, 0x8d, 0x98, 0x57, 0xfb, 0x37, 0x55, 0xff, 0xa0, 0xae, 0x9f, 0x56, + 0xe0, 0xfc, 0xc5, 0xfe, 0x15, 0x90, 0x1e, 0x6e, 0xf3, 0x93, 0x48, 0xc4, 0x92, 0xbb, 0x7a, 0xbb, + 0x8f, 0x86, 0x6d, 0x5a, 0xae, 0xad, 0xd7, 0x98, 0xac, 0x7f, 0x5c, 0xa2, 0xe3, 0x4d, 0xe6, 0xba, + 0x31, 0x87, 0x6c, 0xb8, 0xaf, 0xd3, 0x62, 0x49, 0xba, 0xb8, 0xf9, 0x77, 0x58, 0x1b, 0x34, 0x5b, + 0xec, 0xb5, 0xbf, 0x9e, 0x99, 0xda, 0xef, 0x33, 0x53, 0x1b, 0xbd, 0x38, 0x5f, 0x18, 0xe8, 0x62, + 0x61, 0xa0, 0x5f, 0x0b, 0x03, 0x7d, 0x5b, 0x1a, 0xda, 0xc5, 0xd2, 0xd0, 0x7e, 0x2c, 0x0d, 0xed, + 0xc3, 0xe3, 0xff, 0xce, 0xf3, 0x49, 0x79, 0xfd, 0xd4, 0x64, 0x4f, 0x5b, 0xea, 0xd6, 0x3d, 0xfd, + 0x13, 0x00, 0x00, 0xff, 0xff, 0xff, 0x85, 0xad, 0xc8, 0xf1, 0x03, 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 m.Exported { + i-- + if m.Exported { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.Redelegations) > 0 { + for iNdEx := len(m.Redelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Redelegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.UnbondingDelegations) > 0 { + for iNdEx := len(m.UnbondingDelegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UnbondingDelegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.Delegations) > 0 { + for iNdEx := len(m.Delegations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Delegations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.LastValidatorPowers) > 0 { + for iNdEx := len(m.LastValidatorPowers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.LastValidatorPowers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size := m.LastTotalPower.Size() + i -= size + if _, err := m.LastTotalPower.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Params.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 *LastValidatorPower) 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 *LastValidatorPower) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LastValidatorPower) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Power != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Power)) + i-- + dAtA[i] = 0x10 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + 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 + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.LastTotalPower.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.LastValidatorPowers) > 0 { + for _, e := range m.LastValidatorPowers { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Validators) > 0 { + for _, e := range m.Validators { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Delegations) > 0 { + for _, e := range m.Delegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.UnbondingDelegations) > 0 { + for _, e := range m.UnbondingDelegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Redelegations) > 0 { + for _, e := range m.Redelegations { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.Exported { + n += 2 + } + return n +} + +func (m *LastValidatorPower) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if m.Power != 0 { + n += 1 + sovGenesis(uint64(m.Power)) + } + 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 Params", 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 := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastTotalPower", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.LastTotalPower.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastValidatorPowers", 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.LastValidatorPowers = append(m.LastValidatorPowers, LastValidatorPower{}) + if err := m.LastValidatorPowers[len(m.LastValidatorPowers)-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 Validators", 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.Validators = append(m.Validators, Validator{}) + if err := m.Validators[len(m.Validators)-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 Delegations", 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.Delegations = append(m.Delegations, Delegation{}) + if err := m.Delegations[len(m.Delegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingDelegations", 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.UnbondingDelegations = append(m.UnbondingDelegations, UnbondingDelegation{}) + if err := m.UnbondingDelegations[len(m.UnbondingDelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Redelegations", 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.Redelegations = append(m.Redelegations, Redelegation{}) + if err := m.Redelegations[len(m.Redelegations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exported", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Exported = bool(v != 0) + 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 *LastValidatorPower) 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: LastValidatorPower: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LastValidatorPower: 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 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.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Power", wireType) + } + m.Power = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Power |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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/staking/types/historical_info.go b/x/staking/types/historical_info.go index 78db1e7dd4a9..1a8461ad130c 100644 --- a/x/staking/types/historical_info.go +++ b/x/staking/types/historical_info.go @@ -3,55 +3,77 @@ package types import ( "sort" - abci "github.com/tendermint/tendermint/abci/types" + "github.com/gogo/protobuf/proto" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// HistoricalInfo contains the historical information that gets stored at each height -type HistoricalInfo struct { - Header abci.Header `json:"header" yaml:"header"` - ValSet []Validator `json:"valset" yaml:"valset"` -} - // NewHistoricalInfo will create a historical information struct from header and valset // it will first sort valset before inclusion into historical info -func NewHistoricalInfo(header abci.Header, valSet []Validator) HistoricalInfo { - sort.Sort(Validators(valSet)) +func NewHistoricalInfo(header tmproto.Header, valSet Validators) HistoricalInfo { + // Must sort in the same way that tendermint does + sort.Sort(ValidatorsByVotingPower(valSet)) + return HistoricalInfo{ Header: header, - ValSet: valSet, + Valset: valSet, } } -// MustMarshalHistoricalInfo wll marshal historical info and panic on error -func MustMarshalHistoricalInfo(cdc *codec.Codec, hi HistoricalInfo) []byte { - return cdc.MustMarshalBinaryLengthPrefixed(hi) -} - // MustUnmarshalHistoricalInfo wll unmarshal historical info and panic on error -func MustUnmarshalHistoricalInfo(cdc *codec.Codec, value []byte) HistoricalInfo { +func MustUnmarshalHistoricalInfo(cdc codec.BinaryMarshaler, value []byte) HistoricalInfo { hi, err := UnmarshalHistoricalInfo(cdc, value) if err != nil { panic(err) } + return hi } // UnmarshalHistoricalInfo will unmarshal historical info and return any error -func UnmarshalHistoricalInfo(cdc *codec.Codec, value []byte) (hi HistoricalInfo, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, &hi) +func UnmarshalHistoricalInfo(cdc codec.BinaryMarshaler, value []byte) (hi HistoricalInfo, err error) { + err = cdc.UnmarshalBinaryBare(value, &hi) return hi, err } // ValidateBasic will ensure HistoricalInfo is not nil and sorted func ValidateBasic(hi HistoricalInfo) error { - if len(hi.ValSet) == 0 { + if len(hi.Valset) == 0 { return sdkerrors.Wrap(ErrInvalidHistoricalInfo, "validator set is empty") } - if !sort.IsSorted(Validators(hi.ValSet)) { + + if !sort.IsSorted(Validators(hi.Valset)) { return sdkerrors.Wrap(ErrInvalidHistoricalInfo, "validator set is not sorted by address") } + + return nil +} + +// Equal checks if receiver is equal to the parameter +func (hi *HistoricalInfo) Equal(hi2 *HistoricalInfo) bool { + if !proto.Equal(&hi.Header, &hi2.Header) { + return false + } + if len(hi.Valset) != len(hi2.Valset) { + return false + } + for i := range hi.Valset { + if !hi.Valset[i].Equal(&hi2.Valset[i]) { + return false + } + } + return true +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (hi HistoricalInfo) UnpackInterfaces(c codectypes.AnyUnpacker) error { + for i := range hi.Valset { + if err := hi.Valset[i].UnpackInterfaces(c); err != nil { + return err + } + } return nil } diff --git a/x/staking/types/historical_info_test.go b/x/staking/types/historical_info_test.go index f607ba15031c..d8a25fa92924 100644 --- a/x/staking/types/historical_info_test.go +++ b/x/staking/types/historical_info_test.go @@ -1,4 +1,4 @@ -package types +package types_test import ( "math/rand" @@ -6,62 +6,68 @@ import ( "testing" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/x/staking/types" ) -var ( - validators = []Validator{ - NewValidator(valAddr1, pk1, Description{}), - NewValidator(valAddr2, pk2, Description{}), - NewValidator(valAddr3, pk3, Description{}), - } - header = abci.Header{ - ChainID: "hello", - Height: 5, +var header = tmproto.Header{ + ChainID: "hello", + Height: 5, +} + +func createValidators(t *testing.T) []types.Validator { + return []types.Validator{ + newValidator(t, valAddr1, pk1), + newValidator(t, valAddr2, pk2), + newValidator(t, valAddr3, pk3), } -) +} func TestHistoricalInfo(t *testing.T) { - hi := NewHistoricalInfo(header, validators) - require.True(t, sort.IsSorted(Validators(hi.ValSet)), "Validators are not sorted") + validators := createValidators(t) + hi := types.NewHistoricalInfo(header, validators) + require.True(t, sort.IsSorted(types.Validators(hi.Valset)), "Validators are not sorted") var value []byte require.NotPanics(t, func() { - value = MustMarshalHistoricalInfo(ModuleCdc, hi) + value = types.ModuleCdc.MustMarshalBinaryBare(&hi) }) - require.NotNil(t, value, "Marshalled HistoricalInfo is nil") - recv, err := UnmarshalHistoricalInfo(ModuleCdc, value) + recv, err := types.UnmarshalHistoricalInfo(types.ModuleCdc, value) require.Nil(t, err, "Unmarshalling HistoricalInfo failed") - require.Equal(t, hi, recv, "Unmarshalled HistoricalInfo is different from original") - require.True(t, sort.IsSorted(Validators(hi.ValSet)), "Validators are not sorted") + require.Equal(t, hi.Header, recv.Header) + for i := range hi.Valset { + require.True(t, hi.Valset[i].Equal(&recv.Valset[i])) + } + require.True(t, sort.IsSorted(types.Validators(hi.Valset)), "Validators are not sorted") } func TestValidateBasic(t *testing.T) { - hi := HistoricalInfo{ + validators := createValidators(t) + hi := types.HistoricalInfo{ Header: header, } - err := ValidateBasic(hi) + err := types.ValidateBasic(hi) require.Error(t, err, "ValidateBasic passed on nil ValSet") // Ensure validators are not sorted - for sort.IsSorted(Validators(validators)) { + for sort.IsSorted(types.Validators(validators)) { rand.Shuffle(len(validators), func(i, j int) { it := validators[i] validators[i] = validators[j] validators[j] = it }) } - - hi = HistoricalInfo{ + hi = types.HistoricalInfo{ Header: header, - ValSet: validators, + Valset: validators, } - err = ValidateBasic(hi) + err = types.ValidateBasic(hi) require.Error(t, err, "ValidateBasic passed on unsorted ValSet") - hi = NewHistoricalInfo(header, validators) - err = ValidateBasic(hi) + hi = types.NewHistoricalInfo(header, validators) + err = types.ValidateBasic(hi) require.NoError(t, err, "ValidateBasic failed on valid HistoricalInfo") } diff --git a/x/staking/types/hooks.go b/x/staking/types/hooks.go index de04fae8b2a1..694caca5405a 100644 --- a/x/staking/types/hooks.go +++ b/x/staking/types/hooks.go @@ -11,7 +11,6 @@ func NewMultiStakingHooks(hooks ...StakingHooks) MultiStakingHooks { return hooks } -// nolint func (h MultiStakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { for i := range h { h[i].AfterValidatorCreated(ctx, valAddr) diff --git a/x/staking/types/keys.go b/x/staking/types/keys.go index d278b50db3e7..40d62244a041 100644 --- a/x/staking/types/keys.go +++ b/x/staking/types/keys.go @@ -1,7 +1,9 @@ package types import ( + "bytes" "encoding/binary" + "fmt" "strconv" "time" @@ -15,9 +17,6 @@ const ( // StoreKey is the string store representation StoreKey = ModuleName - // TStoreKey is the string transient store representation - TStoreKey = "transient_" + ModuleName - // QuerierRoute is the querier route for the staking module QuerierRoute = ModuleName @@ -25,7 +24,6 @@ const ( RouterKey = ModuleName ) -//nolint var ( // Keys for store prefixes // Last* values are constant during a block. @@ -73,17 +71,7 @@ func AddressFromLastValidatorPowerKey(key []byte) []byte { // VALUE: validator operator address ([]byte) func GetValidatorsByPowerIndexKey(validator Validator) []byte { // NOTE the address doesn't need to be stored because counter bytes must always be different - return getValidatorPowerRank(validator) -} - -// get the bonded validator index key for an operator address -func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { - return append(LastValidatorPowerKey, operator...) -} - -// get the power ranking of a validator -// NOTE the larger values are of higher value -func getValidatorPowerRank(validator Validator) []byte { + // NOTE the larger values are of higher value consensusPower := sdk.TokensToConsensusPower(validator.Tokens) consensusPowerBytes := make([]byte, 8) @@ -97,35 +85,85 @@ func getValidatorPowerRank(validator Validator) []byte { key[0] = ValidatorsByPowerIndexKey[0] copy(key[1:powerBytesLen+1], powerBytes) - operAddrInvr := sdk.CopyBytes(validator.OperatorAddress) + addr, err := sdk.ValAddressFromBech32(validator.OperatorAddress) + if err != nil { + panic(err) + } + operAddrInvr := sdk.CopyBytes(addr) + for i, b := range operAddrInvr { operAddrInvr[i] = ^b } + copy(key[powerBytesLen+1:], operAddrInvr) return key } +// get the bonded validator index key for an operator address +func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { + return append(LastValidatorPowerKey, operator...) +} + // parse the validators operator address from power rank key func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) { powerBytesLen := 8 if len(key) != 1+powerBytesLen+sdk.AddrLen { panic("Invalid validator power rank key length") } + operAddr = sdk.CopyBytes(key[powerBytesLen+1:]) + for i, b := range operAddr { operAddr[i] = ^b } + return operAddr } -// gets the prefix for all unbonding delegations from a delegator -func GetValidatorQueueTimeKey(timestamp time.Time) []byte { - bz := sdk.FormatTimeBytes(timestamp) - return append(ValidatorQueueKey, bz...) +// GetValidatorQueueKey returns the prefix key used for getting a set of unbonding +// validators whose unbonding completion occurs at the given time and height. +func GetValidatorQueueKey(timestamp time.Time, height int64) []byte { + heightBz := sdk.Uint64ToBigEndian(uint64(height)) + timeBz := sdk.FormatTimeBytes(timestamp) + timeBzL := len(timeBz) + prefixL := len(ValidatorQueueKey) + + bz := make([]byte, prefixL+8+timeBzL+8) + + // copy the prefix + copy(bz[:prefixL], ValidatorQueueKey) + + // copy the encoded time bytes length + copy(bz[prefixL:prefixL+8], sdk.Uint64ToBigEndian(uint64(timeBzL))) + + // copy the encoded time bytes + copy(bz[prefixL+8:prefixL+8+timeBzL], timeBz) + + // copy the encoded height + copy(bz[prefixL+8+timeBzL:], heightBz) + + return bz } -//______________________________________________________________________________ +// ParseValidatorQueueKey returns the encoded time and height from a key created +// from GetValidatorQueueKey. +func ParseValidatorQueueKey(bz []byte) (time.Time, int64, error) { + prefixL := len(ValidatorQueueKey) + if prefix := bz[:prefixL]; !bytes.Equal(prefix, ValidatorQueueKey) { + return time.Time{}, 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", ValidatorQueueKey, prefix) + } + + timeBzL := sdk.BigEndianToUint64(bz[prefixL : prefixL+8]) + ts, err := sdk.ParseTimeBytes(bz[prefixL+8 : prefixL+8+int(timeBzL)]) + if err != nil { + return time.Time{}, 0, err + } + + height := sdk.BigEndianToUint64(bz[prefixL+8+int(timeBzL):]) + + return ts, int64(height), nil +} // gets the key for delegator bond with validator // VALUE: staking/Delegation @@ -138,8 +176,6 @@ func GetDelegationsKey(delAddr sdk.AccAddress) []byte { return append(DelegationKey, delAddr.Bytes()...) } -//______________________________________________________________________________ - // gets the key for an unbonding delegation by delegator and validator addr // VALUE: staking/UnbondingDelegation func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { @@ -160,13 +196,13 @@ func GetUBDKeyFromValIndexKey(indexKey []byte) []byte { if len(addrs) != 2*sdk.AddrLen { panic("unexpected key length") } + valAddr := addrs[:sdk.AddrLen] delAddr := addrs[sdk.AddrLen:] + return GetUBDKey(delAddr, valAddr) } -//______________ - // gets the prefix for all unbonding delegations from a delegator func GetUBDsKey(delAddr sdk.AccAddress) []byte { return append(UnbondingDelegationKey, delAddr.Bytes()...) @@ -183,10 +219,8 @@ func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte { return append(UnbondingQueueKey, bz...) } -//________________________________________________________________________________ - -// gets the key for a redelegation -// VALUE: staking/RedelegationKey +// 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) @@ -208,6 +242,7 @@ func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.V copy(key[0:offset], REDSFromValsSrcKey) copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valDstAddr.Bytes()) + return key } @@ -232,6 +267,7 @@ func GetREDKeyFromValSrcIndexKey(indexKey []byte) []byte { if len(indexKey) != 3*sdk.AddrLen+1 { panic("unexpected key length") } + 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] @@ -245,46 +281,46 @@ func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte { if len(indexKey) != 3*sdk.AddrLen+1 { panic("unexpected key length") } + 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] + return GetREDKey(delAddr, valSrcAddr, valDstAddr) } -// gets the prefix for all unbonding delegations from a delegator +// GetRedelegationTimeKey returns a key prefix for indexing an unbonding +// redelegation based on a completion time. func GetRedelegationTimeKey(timestamp time.Time) []byte { bz := sdk.FormatTimeBytes(timestamp) return append(RedelegationQueueKey, bz...) } -//______________ - -// gets the prefix keyspace for redelegations from a delegator +// GetREDsKey returns a key prefix for indexing a redelegation from a delegator +// address. func GetREDsKey(delAddr sdk.AccAddress) []byte { return append(RedelegationKey, delAddr.Bytes()...) } -// gets the prefix keyspace for all redelegations redelegating away from a source validator +// GetREDsFromValSrcIndexKey returns a key prefix for indexing a redelegation to +// a source validator. func GetREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte { return append(RedelegationByValSrcIndexKey, valSrcAddr.Bytes()...) } -// gets the prefix keyspace for all redelegations redelegating towards a destination validator +// 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()...) } -// gets the prefix keyspace for all redelegations redelegating towards a destination validator -// from a particular delegator +// 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), delAddr.Bytes()...) } -//________________________________________________________________________________ - -// GetHistoricalInfoKey gets the key for the historical info +// GetHistoricalInfoKey returns a key prefix for indexing HistoricalInfo objects. func GetHistoricalInfoKey(height int64) []byte { return append(HistoricalInfoKey, []byte(strconv.FormatInt(height, 10))...) } diff --git a/x/staking/types/keys_test.go b/x/staking/types/keys_test.go index 5875c6528120..0f63617f26b3 100644 --- a/x/staking/types/keys_test.go +++ b/x/staking/types/keys_test.go @@ -1,14 +1,17 @@ -package types +package types_test import ( + "bytes" "encoding/hex" "math/big" "testing" + "time" - "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -22,8 +25,7 @@ var ( func TestGetValidatorPowerRank(t *testing.T) { valAddr1 := sdk.ValAddress(keysAddr1) - emptyDesc := Description{} - val1 := NewValidator(valAddr1, keysPK1, emptyDesc) + val1 := newValidator(t, valAddr1, keysPK1) val1.Tokens = sdk.ZeroInt() val2, val3, val4 := val1, val1, val1 val2.Tokens = sdk.TokensFromConsensusPower(1) @@ -32,7 +34,7 @@ func TestGetValidatorPowerRank(t *testing.T) { val4.Tokens = sdk.TokensFromConsensusPower(x.Int64()) tests := []struct { - validator Validator + validator types.Validator wantHex string }{ {val1, "2300000000000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, @@ -41,9 +43,9 @@ func TestGetValidatorPowerRank(t *testing.T) { {val4, "2300000100000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, } for i, tt := range tests { - got := hex.EncodeToString(getValidatorPowerRank(tt.validator)) + got := hex.EncodeToString(types.GetValidatorsByPowerIndexKey(tt.validator)) - assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + require.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) } } @@ -62,9 +64,9 @@ func TestGetREDByValDstIndexKey(t *testing.T) { "363ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"}, } for i, tt := range tests { - got := hex.EncodeToString(GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) + got := hex.EncodeToString(types.GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) - assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + require.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) } } @@ -83,8 +85,34 @@ func TestGetREDByValSrcIndexKey(t *testing.T) { "3563d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"}, } for i, tt := range tests { - got := hex.EncodeToString(GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) + got := hex.EncodeToString(types.GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) - assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + require.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) } } + +func TestGetValidatorQueueKey(t *testing.T) { + ts := time.Now() + height := int64(1024) + + bz := types.GetValidatorQueueKey(ts, height) + rTs, rHeight, err := types.ParseValidatorQueueKey(bz) + require.NoError(t, err) + require.Equal(t, ts.UTC(), rTs.UTC()) + require.Equal(t, rHeight, height) +} + +func TestTestGetValidatorQueueKeyOrder(t *testing.T) { + ts := time.Now().UTC() + height := int64(1000) + + endKey := types.GetValidatorQueueKey(ts, height) + + keyA := types.GetValidatorQueueKey(ts.Add(-10*time.Minute), height-10) + keyB := types.GetValidatorQueueKey(ts.Add(-5*time.Minute), height+50) + keyC := types.GetValidatorQueueKey(ts.Add(10*time.Minute), height+100) + + require.Equal(t, -1, bytes.Compare(keyA, endKey)) // keyA <= endKey + require.Equal(t, -1, bytes.Compare(keyB, endKey)) // keyB <= endKey + require.Equal(t, 1, bytes.Compare(keyC, endKey)) // keyB >= endKey +} diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go index 62d2295fce6f..2525aa66468f 100644 --- a/x/staking/types/msg.go +++ b/x/staking/types/msg.go @@ -2,70 +2,61 @@ package types import ( "bytes" - "encoding/json" - - "github.com/tendermint/tendermint/crypto" - yaml "gopkg.in/yaml.v2" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// ensure Msg interface compliance at compile time -var ( - _ sdk.Msg = &MsgCreateValidator{} - _ sdk.Msg = &MsgEditValidator{} - _ sdk.Msg = &MsgDelegate{} - _ sdk.Msg = &MsgUndelegate{} - _ sdk.Msg = &MsgBeginRedelegate{} +// staking message types +const ( + TypeMsgUndelegate = "begin_unbonding" + TypeMsgEditValidator = "edit_validator" + TypeMsgCreateValidator = "create_validator" + TypeMsgDelegate = "delegate" + TypeMsgBeginRedelegate = "begin_redelegate" ) -//______________________________________________________________________ - -// MsgCreateValidator - struct for bonding transactions -type MsgCreateValidator struct { - Description Description `json:"description" yaml:"description"` - Commission CommissionRates `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - PubKey crypto.PubKey `json:"pubkey" yaml:"pubkey"` - Value sdk.Coin `json:"value" yaml:"value"` -} - -type msgCreateValidatorJSON struct { - Description Description `json:"description" yaml:"description"` - Commission CommissionRates `json:"commission" yaml:"commission"` - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - PubKey string `json:"pubkey" yaml:"pubkey"` - Value sdk.Coin `json:"value" yaml:"value"` -} +var ( + _ sdk.Msg = &MsgCreateValidator{} + _ codectypes.UnpackInterfacesMessage = (*MsgCreateValidator)(nil) + _ sdk.Msg = &MsgCreateValidator{} + _ sdk.Msg = &MsgEditValidator{} + _ sdk.Msg = &MsgDelegate{} + _ sdk.Msg = &MsgUndelegate{} + _ sdk.Msg = &MsgBeginRedelegate{} +) // NewMsgCreateValidator creates a new MsgCreateValidator instance. // Delegator address and validator address are the same. func NewMsgCreateValidator( - valAddr sdk.ValAddress, pubKey crypto.PubKey, selfDelegation sdk.Coin, - description Description, commission CommissionRates, minSelfDelegation sdk.Int, -) MsgCreateValidator { - - return MsgCreateValidator{ + valAddr sdk.ValAddress, pubKey cryptotypes.PubKey, //nolint:interfacer + selfDelegation sdk.Coin, description Description, commission CommissionRates, minSelfDelegation sdk.Int, +) (*MsgCreateValidator, error) { + var pkAny *codectypes.Any + if pubKey != nil { + var err error + if pkAny, err = codectypes.NewAnyWithValue(pubKey); err != nil { + return nil, err + } + } + return &MsgCreateValidator{ Description: description, - DelegatorAddress: sdk.AccAddress(valAddr), - ValidatorAddress: valAddr, - PubKey: pubKey, + DelegatorAddress: sdk.AccAddress(valAddr).String(), + ValidatorAddress: valAddr.String(), + Pubkey: pkAny, Value: selfDelegation, Commission: commission, MinSelfDelegation: minSelfDelegation, - } + }, nil } // Route implements the sdk.Msg interface. func (msg MsgCreateValidator) Route() string { return RouterKey } // Type implements the sdk.Msg interface. -func (msg MsgCreateValidator) Type() string { return "create_validator" } +func (msg MsgCreateValidator) Type() string { return TypeMsgCreateValidator } // GetSigners implements the sdk.Msg interface. It returns the address(es) that // must sign over msg.GetSignBytes(). @@ -73,111 +64,75 @@ func (msg MsgCreateValidator) Type() string { return "create_validator" } // sign the msg as well. func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress { // delegator is first signer so delegator pays fees - addrs := []sdk.AccAddress{msg.DelegatorAddress} - - if !bytes.Equal(msg.DelegatorAddress.Bytes(), msg.ValidatorAddress.Bytes()) { - addrs = append(addrs, sdk.AccAddress(msg.ValidatorAddress)) - } - return addrs -} - -// MarshalJSON implements the json.Marshaler interface to provide custom JSON -// serialization of the MsgCreateValidator type. -func (msg MsgCreateValidator) MarshalJSON() ([]byte, error) { - return json.Marshal(msgCreateValidatorJSON{ - Description: msg.Description, - Commission: msg.Commission, - DelegatorAddress: msg.DelegatorAddress, - ValidatorAddress: msg.ValidatorAddress, - PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, msg.PubKey), - Value: msg.Value, - MinSelfDelegation: msg.MinSelfDelegation, - }) -} - -// UnmarshalJSON implements the json.Unmarshaler interface to provide custom -// JSON deserialization of the MsgCreateValidator type. -func (msg *MsgCreateValidator) UnmarshalJSON(bz []byte) error { - var msgCreateValJSON msgCreateValidatorJSON - if err := json.Unmarshal(bz, &msgCreateValJSON); err != nil { - return err - } - - msg.Description = msgCreateValJSON.Description - msg.Commission = msgCreateValJSON.Commission - msg.DelegatorAddress = msgCreateValJSON.DelegatorAddress - msg.ValidatorAddress = msgCreateValJSON.ValidatorAddress - var err error - msg.PubKey, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, msgCreateValJSON.PubKey) + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) if err != nil { - return err + panic(err) } - msg.Value = msgCreateValJSON.Value - msg.MinSelfDelegation = msgCreateValJSON.MinSelfDelegation - - return nil -} - -// MarshalYAML implements a custom marshal yaml function due to consensus pubkey. -func (msg MsgCreateValidator) MarshalYAML() (interface{}, error) { - bs, err := yaml.Marshal(struct { - Description Description - Commission CommissionRates - MinSelfDelegation sdk.Int - DelegatorAddress sdk.AccAddress - ValidatorAddress sdk.ValAddress - PubKey string - Value sdk.Coin - }{ - Description: msg.Description, - Commission: msg.Commission, - MinSelfDelegation: msg.MinSelfDelegation, - DelegatorAddress: msg.DelegatorAddress, - ValidatorAddress: msg.ValidatorAddress, - PubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, msg.PubKey), - Value: msg.Value, - }) - + addrs := []sdk.AccAddress{delAddr} + addr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) if err != nil { - return nil, err + panic(err) + } + if !bytes.Equal(delAddr.Bytes(), addr.Bytes()) { + addrs = append(addrs, sdk.AccAddress(addr)) } - return string(bs), nil + return addrs } // GetSignBytes returns the message bytes to sign over. func (msg MsgCreateValidator) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // ValidateBasic implements the sdk.Msg interface. func (msg MsgCreateValidator) ValidateBasic() error { // note that unmarshaling from bech32 ensures either empty or valid - if msg.DelegatorAddress.Empty() { + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + return err + } + if delAddr.Empty() { return ErrEmptyDelegatorAddr } - if msg.ValidatorAddress.Empty() { + + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } - if !sdk.AccAddress(msg.ValidatorAddress).Equals(msg.DelegatorAddress) { + + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + return err + } + if !sdk.AccAddress(valAddr).Equals(delAddr) { return ErrBadValidatorAddr } - if !msg.Value.Amount.IsPositive() { + + if msg.Pubkey == nil { + return ErrEmptyValidatorPubKey + } + + if !msg.Value.IsValid() || !msg.Value.Amount.IsPositive() { return ErrBadDelegationAmount } + if msg.Description == (Description{}) { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty description") } + if msg.Commission == (CommissionRates{}) { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty commission") } + if err := msg.Commission.Validate(); err != nil { return err } + if !msg.MinSelfDelegation.IsPositive() { return ErrMinSelfDelegationInvalid } + if msg.Value.Amount.LT(msg.MinSelfDelegation) { return ErrSelfDelegationBelowMinimum } @@ -185,26 +140,19 @@ func (msg MsgCreateValidator) ValidateBasic() error { return nil } -// MsgEditValidator - struct for editing a validator -type MsgEditValidator struct { - Description Description `json:"description" yaml:"description"` - ValidatorAddress sdk.ValAddress `json:"address" yaml:"address"` - - // We pass a reference to the new commission rate and min self delegation as it's not mandatory to - // update. If not updated, the deserialized rate will be zero with no way to - // distinguish if an update was intended. - // - // REF: #2373 - CommissionRate *sdk.Dec `json:"commission_rate" yaml:"commission_rate"` - MinSelfDelegation *sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (msg MsgCreateValidator) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var pubKey cryptotypes.PubKey + return unpacker.UnpackAny(msg.Pubkey, &pubKey) } // NewMsgEditValidator creates a new MsgEditValidator instance -func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec, newMinSelfDelegation *sdk.Int) MsgEditValidator { - return MsgEditValidator{ +//nolint:interfacer +func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec, newMinSelfDelegation *sdk.Int) *MsgEditValidator { + return &MsgEditValidator{ Description: description, CommissionRate: newRate, - ValidatorAddress: valAddr, + ValidatorAddress: valAddr.String(), MinSelfDelegation: newMinSelfDelegation, } } @@ -213,30 +161,37 @@ func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRat func (msg MsgEditValidator) Route() string { return RouterKey } // Type implements the sdk.Msg interface. -func (msg MsgEditValidator) Type() string { return "edit_validator" } +func (msg MsgEditValidator) Type() string { return TypeMsgEditValidator } // GetSigners implements the sdk.Msg interface. func (msg MsgEditValidator) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddress)} + valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{valAddr.Bytes()} } // GetSignBytes implements the sdk.Msg interface. func (msg MsgEditValidator) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // ValidateBasic implements the sdk.Msg interface. func (msg MsgEditValidator) ValidateBasic() error { - if msg.ValidatorAddress.Empty() { + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } + if msg.Description == (Description{}) { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty description") } + if msg.MinSelfDelegation != nil && !msg.MinSelfDelegation.IsPositive() { return ErrMinSelfDelegationInvalid } + if msg.CommissionRate != nil { if msg.CommissionRate.GT(sdk.OneDec()) || msg.CommissionRate.IsNegative() { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "commission rate must be between 0 and 1 (inclusive)") @@ -246,18 +201,12 @@ func (msg MsgEditValidator) ValidateBasic() error { return nil } -// MsgDelegate - struct for bonding transactions -type MsgDelegate struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Amount sdk.Coin `json:"amount" yaml:"amount"` -} - // NewMsgDelegate creates a new MsgDelegate instance. -func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgDelegate { - return MsgDelegate{ - DelegatorAddress: delAddr, - ValidatorAddress: valAddr, +//nolint:interfacer +func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) *MsgDelegate { + return &MsgDelegate{ + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), Amount: amount, } } @@ -266,51 +215,49 @@ func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.C func (msg MsgDelegate) Route() string { return RouterKey } // Type implements the sdk.Msg interface. -func (msg MsgDelegate) Type() string { return "delegate" } +func (msg MsgDelegate) Type() string { return TypeMsgDelegate } // GetSigners implements the sdk.Msg interface. func (msg MsgDelegate) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.DelegatorAddress} + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{delAddr} } // GetSignBytes implements the sdk.Msg interface. func (msg MsgDelegate) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // ValidateBasic implements the sdk.Msg interface. func (msg MsgDelegate) ValidateBasic() error { - if msg.DelegatorAddress.Empty() { + if msg.DelegatorAddress == "" { return ErrEmptyDelegatorAddr } - if msg.ValidatorAddress.Empty() { + + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } - if !msg.Amount.Amount.IsPositive() { + + if !msg.Amount.IsValid() || !msg.Amount.Amount.IsPositive() { return ErrBadDelegationAmount } - return nil -} - -//______________________________________________________________________ -// MsgBeginRedelegate defines the attributes of a bonding transaction. -type MsgBeginRedelegate struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address" yaml:"validator_src_address"` - ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address" yaml:"validator_dst_address"` - Amount sdk.Coin `json:"amount" yaml:"amount"` + return nil } // NewMsgBeginRedelegate creates a new MsgBeginRedelegate instance. +//nolint:interfacer func NewMsgBeginRedelegate( delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount sdk.Coin, -) MsgBeginRedelegate { - return MsgBeginRedelegate{ - DelegatorAddress: delAddr, - ValidatorSrcAddress: valSrcAddr, - ValidatorDstAddress: valDstAddr, +) *MsgBeginRedelegate { + return &MsgBeginRedelegate{ + DelegatorAddress: delAddr.String(), + ValidatorSrcAddress: valSrcAddr.String(), + ValidatorDstAddress: valDstAddr.String(), Amount: amount, } } @@ -319,48 +266,50 @@ func NewMsgBeginRedelegate( func (msg MsgBeginRedelegate) Route() string { return RouterKey } // Type implements the sdk.Msg interface -func (msg MsgBeginRedelegate) Type() string { return "begin_redelegate" } +func (msg MsgBeginRedelegate) Type() string { return TypeMsgBeginRedelegate } // GetSigners implements the sdk.Msg interface func (msg MsgBeginRedelegate) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.DelegatorAddress} + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{delAddr} } // GetSignBytes implements the sdk.Msg interface. func (msg MsgBeginRedelegate) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // ValidateBasic implements the sdk.Msg interface. func (msg MsgBeginRedelegate) ValidateBasic() error { - if msg.DelegatorAddress.Empty() { + if msg.DelegatorAddress == "" { return ErrEmptyDelegatorAddr } - if msg.ValidatorSrcAddress.Empty() { + + if msg.ValidatorSrcAddress == "" { return ErrEmptyValidatorAddr } - if msg.ValidatorDstAddress.Empty() { + + if msg.ValidatorDstAddress == "" { return ErrEmptyValidatorAddr } - if !msg.Amount.Amount.IsPositive() { + + if !msg.Amount.IsValid() || !msg.Amount.Amount.IsPositive() { return ErrBadSharesAmount } - return nil -} -// MsgUndelegate - struct for unbonding transactions -type MsgUndelegate struct { - DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` - ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` - Amount sdk.Coin `json:"amount" yaml:"amount"` + return nil } // NewMsgUndelegate creates a new MsgUndelegate instance. -func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgUndelegate { - return MsgUndelegate{ - DelegatorAddress: delAddr, - ValidatorAddress: valAddr, +//nolint:interfacer +func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) *MsgUndelegate { + return &MsgUndelegate{ + DelegatorAddress: delAddr.String(), + ValidatorAddress: valAddr.String(), Amount: amount, } } @@ -369,27 +318,36 @@ func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk func (msg MsgUndelegate) Route() string { return RouterKey } // Type implements the sdk.Msg interface. -func (msg MsgUndelegate) Type() string { return "begin_unbonding" } +func (msg MsgUndelegate) Type() string { return TypeMsgUndelegate } // GetSigners implements the sdk.Msg interface. -func (msg MsgUndelegate) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddress} } +func (msg MsgUndelegate) GetSigners() []sdk.AccAddress { + delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddress) + if err != nil { + panic(err) + } + return []sdk.AccAddress{delAddr} +} // GetSignBytes implements the sdk.Msg interface. func (msg MsgUndelegate) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) + bz := ModuleCdc.MustMarshalJSON(&msg) return sdk.MustSortJSON(bz) } // ValidateBasic implements the sdk.Msg interface. func (msg MsgUndelegate) ValidateBasic() error { - if msg.DelegatorAddress.Empty() { + if msg.DelegatorAddress == "" { return ErrEmptyDelegatorAddr } - if msg.ValidatorAddress.Empty() { + + if msg.ValidatorAddress == "" { return ErrEmptyValidatorAddr } - if !msg.Amount.Amount.IsPositive() { + + if !msg.Amount.IsValid() || !msg.Amount.Amount.IsPositive() { return ErrBadSharesAmount } + return nil } diff --git a/x/staking/types/msg_test.go b/x/staking/types/msg_test.go index 784b653d2b8f..f1ba552d9115 100644 --- a/x/staking/types/msg_test.go +++ b/x/staking/types/msg_test.go @@ -1,15 +1,17 @@ -package types +package types_test import ( - "fmt" "testing" - yaml "gopkg.in/yaml.v2" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -17,17 +19,49 @@ var ( coinZero = sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) ) +func TestMsgDecode(t *testing.T) { + registry := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(registry) + types.RegisterInterfaces(registry) + cdc := codec.NewProtoCodec(registry) + + // firstly we start testing the pubkey serialization + + pk1bz, err := cdc.MarshalInterface(pk1) + require.NoError(t, err) + var pkUnmarshaled cryptotypes.PubKey + err = cdc.UnmarshalInterface(pk1bz, &pkUnmarshaled) + require.NoError(t, err) + require.True(t, pk1.Equals(pkUnmarshaled.(*ed25519.PubKey))) + + // now let's try to serialize the whole message + + commission1 := types.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + msg, err := types.NewMsgCreateValidator(valAddr1, pk1, coinPos, types.Description{}, commission1, sdk.OneInt()) + require.NoError(t, err) + msgSerialized, err := cdc.MarshalInterface(msg) + require.NoError(t, err) + + var msgUnmarshaled sdk.Msg + err = cdc.UnmarshalInterface(msgSerialized, &msgUnmarshaled) + require.NoError(t, err) + msg2, ok := msgUnmarshaled.(*types.MsgCreateValidator) + require.True(t, ok) + require.True(t, msg.Value.IsEqual(msg2.Value)) + require.True(t, msg.Pubkey.Equal(msg2.Pubkey)) +} + // test ValidateBasic for MsgCreateValidator func TestMsgCreateValidator(t *testing.T) { - commission1 := NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - commission2 := NewCommissionRates(sdk.NewDec(5), sdk.NewDec(5), sdk.NewDec(5)) + commission1 := types.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + commission2 := types.NewCommissionRates(sdk.NewDec(5), sdk.NewDec(5), sdk.NewDec(5)) tests := []struct { name, moniker, identity, website, securityContact, details string - CommissionRates CommissionRates + CommissionRates types.CommissionRates minSelfDelegation sdk.Int validatorAddr sdk.ValAddress - pubkey crypto.PubKey + pubkey cryptotypes.PubKey bond sdk.Coin expectPass bool }{ @@ -35,16 +69,18 @@ func TestMsgCreateValidator(t *testing.T) { {"partial description", "", "", "c", "", "", commission1, sdk.OneInt(), valAddr1, pk1, coinPos, true}, {"empty description", "", "", "", "", "", commission2, sdk.OneInt(), valAddr1, pk1, coinPos, false}, {"empty address", "a", "b", "c", "d", "e", commission2, sdk.OneInt(), emptyAddr, pk1, coinPos, false}, - {"empty pubkey", "a", "b", "c", "d", "e", commission1, sdk.OneInt(), valAddr1, emptyPubkey, coinPos, true}, + {"empty pubkey", "a", "b", "c", "d", "e", commission1, sdk.OneInt(), valAddr1, emptyPubkey, coinPos, false}, {"empty bond", "a", "b", "c", "d", "e", commission2, sdk.OneInt(), valAddr1, pk1, coinZero, false}, + {"nil bond", "a", "b", "c", "d", "e", commission2, sdk.OneInt(), valAddr1, pk1, sdk.Coin{}, false}, {"zero min self delegation", "a", "b", "c", "d", "e", commission1, sdk.ZeroInt(), valAddr1, pk1, coinPos, false}, {"negative min self delegation", "a", "b", "c", "d", "e", commission1, sdk.NewInt(-1), valAddr1, pk1, coinPos, false}, {"delegation less than min self delegation", "a", "b", "c", "d", "e", commission1, coinPos.Amount.Add(sdk.OneInt()), valAddr1, pk1, coinPos, false}, } for _, tc := range tests { - description := NewDescription(tc.moniker, tc.identity, tc.website, tc.securityContact, tc.details) - msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.CommissionRates, tc.minSelfDelegation) + description := types.NewDescription(tc.moniker, tc.identity, tc.website, tc.securityContact, tc.details) + msg, err := types.NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.CommissionRates, tc.minSelfDelegation) + require.NoError(t, err) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) } else { @@ -59,19 +95,20 @@ func TestMsgEditValidator(t *testing.T) { name, moniker, identity, website, securityContact, details string validatorAddr sdk.ValAddress expectPass bool + minSelfDelegation sdk.Int }{ - {"basic good", "a", "b", "c", "d", "e", valAddr1, true}, - {"partial description", "", "", "c", "", "", valAddr1, true}, - {"empty description", "", "", "", "", "", valAddr1, false}, - {"empty address", "a", "b", "c", "d", "e", emptyAddr, false}, + {"basic good", "a", "b", "c", "d", "e", valAddr1, true, sdk.OneInt()}, + {"partial description", "", "", "c", "", "", valAddr1, true, sdk.OneInt()}, + {"empty description", "", "", "", "", "", valAddr1, false, sdk.OneInt()}, + {"empty address", "a", "b", "c", "d", "e", emptyAddr, false, sdk.OneInt()}, + {"nil int", "a", "b", "c", "d", "e", emptyAddr, false, sdk.Int{}}, } for _, tc := range tests { - description := NewDescription(tc.moniker, tc.identity, tc.website, tc.securityContact, tc.details) + description := types.NewDescription(tc.moniker, tc.identity, tc.website, tc.securityContact, tc.details) newRate := sdk.ZeroDec() - newMinSelfDelegation := sdk.OneInt() - msg := NewMsgEditValidator(tc.validatorAddr, description, &newRate, &newMinSelfDelegation) + msg := types.NewMsgEditValidator(tc.validatorAddr, description, &newRate, &tc.minSelfDelegation) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) } else { @@ -94,10 +131,11 @@ func TestMsgDelegate(t *testing.T) { {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, coinPos, false}, {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, coinPos, false}, {"empty bond", sdk.AccAddress(valAddr1), valAddr2, coinZero, false}, + {"nil bold", sdk.AccAddress(valAddr1), valAddr2, sdk.Coin{}, false}, } for _, tc := range tests { - msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond) + msg := types.NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) } else { @@ -118,13 +156,14 @@ func TestMsgBeginRedelegate(t *testing.T) { }{ {"regular", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true}, {"zero amount", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false}, + {"nil amount", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.Coin{}, false}, {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, {"empty source validator", sdk.AccAddress(valAddr1), emptyAddr, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, {"empty destination validator", sdk.AccAddress(valAddr1), valAddr2, emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, } for _, tc := range tests { - msg := NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.amount) + msg := types.NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.amount) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) } else { @@ -144,12 +183,13 @@ func TestMsgUndelegate(t *testing.T) { }{ {"regular", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true}, {"zero amount", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false}, + {"nil amount", sdk.AccAddress(valAddr1), valAddr2, sdk.Coin{}, false}, {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, } for _, tc := range tests { - msg := NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.amount) + msg := types.NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.amount) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) } else { @@ -157,47 +197,3 @@ func TestMsgUndelegate(t *testing.T) { } } } - -//test to validate if NewMsgCreateValidator implements yaml marshaller -func TestMsgMarshalYAML(t *testing.T) { - commission1 := NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) - tc := struct { - name, moniker, identity, website, securityContact, details string - CommissionRates CommissionRates - minSelfDelegation sdk.Int - validatorAddr sdk.ValAddress - pubkey crypto.PubKey - bond sdk.Coin - expectPass bool - }{"basic good", "a", "b", "c", "d", "e", commission1, sdk.OneInt(), valAddr1, pk1, coinPos, true} - - description := NewDescription(tc.moniker, tc.identity, tc.website, tc.securityContact, tc.details) - msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.CommissionRates, tc.minSelfDelegation) - bs, err := yaml.Marshal(msg) - require.NoError(t, err) - bechifiedPub, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, msg.PubKey) - require.NoError(t, err) - - want := fmt.Sprintf(`| - description: - moniker: a - identity: b - website: c - security_contact: d - details: e - commission: - rate: "0.000000000000000000" - max_rate: "0.000000000000000000" - max_change_rate: "0.000000000000000000" - minselfdelegation: "1" - delegatoraddress: %s - validatoraddress: %s - pubkey: %s - value: - denom: stake - amount: "1000" -`, msg.DelegatorAddress, msg.ValidatorAddress, bechifiedPub) - - require.Equal(t, want, string(bs)) - -} diff --git a/x/staking/types/params.go b/x/staking/types/params.go index f3aefb404899..549189fa4f69 100644 --- a/x/staking/types/params.go +++ b/x/staking/types/params.go @@ -1,15 +1,16 @@ package types import ( - "bytes" "errors" "fmt" "strings" "time" + yaml "gopkg.in/yaml.v2" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/params" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) // Staking params default values @@ -20,40 +21,34 @@ const ( DefaultUnbondingTime time.Duration = time.Hour * 24 * 7 * 3 // Default maximum number of bonded validators - DefaultMaxValidators uint16 = 100 + DefaultMaxValidators uint32 = 100 // Default maximum entries in a UBD/RED pair - DefaultMaxEntries uint16 = 7 + DefaultMaxEntries uint32 = 7 - // DefaultHistorical entries is 0 since it must only be non-zero for - // IBC connected chains - DefaultHistoricalEntries uint16 = 0 + // DefaultHistorical entries is 10000. Apps that don't use IBC can ignore this + // value by not adding the staking module to the application module manager's + // SetOrderBeginBlockers. + DefaultHistoricalEntries uint32 = 10000 ) -// nolint - Keys for parameter access var ( KeyUnbondingTime = []byte("UnbondingTime") KeyMaxValidators = []byte("MaxValidators") - KeyMaxEntries = []byte("KeyMaxEntries") + KeyMaxEntries = []byte("MaxEntries") KeyBondDenom = []byte("BondDenom") KeyHistoricalEntries = []byte("HistoricalEntries") ) -var _ params.ParamSet = (*Params)(nil) +var _ paramtypes.ParamSet = (*Params)(nil) -// Params defines the high level settings for staking -type Params struct { - UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` // time duration of unbonding - MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` // maximum number of validators (max uint16 = 65535) - MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio) - HistoricalEntries uint16 `json:"historical_entries" yaml:"historical_entries"` // number of historical entries to persist - BondDenom string `json:"bond_denom" yaml:"bond_denom"` // bondable coin denomination +// ParamTable for staking module +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) } // NewParams creates a new Params instance -func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint16, - bondDenom string) Params { - +func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint32, bondDenom string) Params { return Params{ UnbondingTime: unbondingTime, MaxValidators: maxValidators, @@ -64,55 +59,50 @@ func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historica } // Implements params.ParamSet -func (p *Params) ParamSetPairs() params.ParamSetPairs { - return params.ParamSetPairs{ - params.NewParamSetPair(KeyUnbondingTime, &p.UnbondingTime, validateUnbondingTime), - params.NewParamSetPair(KeyMaxValidators, &p.MaxValidators, validateMaxValidators), - params.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries), - params.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries), - params.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom), +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyUnbondingTime, &p.UnbondingTime, validateUnbondingTime), + paramtypes.NewParamSetPair(KeyMaxValidators, &p.MaxValidators, validateMaxValidators), + paramtypes.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries), + paramtypes.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries), + paramtypes.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom), } } -// Equal returns a boolean determining if two Param types are identical. -// TODO: This is slower than comparing struct fields directly -func (p Params) Equal(p2 Params) bool { - bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p) - bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p2) - return bytes.Equal(bz1, bz2) -} - // DefaultParams returns a default set of parameters. func DefaultParams() Params { - return NewParams(DefaultUnbondingTime, DefaultMaxValidators, DefaultMaxEntries, DefaultHistoricalEntries, sdk.DefaultBondDenom) + return NewParams( + DefaultUnbondingTime, + DefaultMaxValidators, + DefaultMaxEntries, + DefaultHistoricalEntries, + sdk.DefaultBondDenom, + ) } // String returns a human readable string representation of the parameters. func (p Params) String() string { - return fmt.Sprintf(`Params: - Unbonding Time: %s - Max Validators: %d - Max Entries: %d - Historical Entries: %d - Bonded Coin Denom: %s`, p.UnbondingTime, - p.MaxValidators, p.MaxEntries, p.HistoricalEntries, p.BondDenom) + out, _ := yaml.Marshal(p) + return string(out) } // unmarshal the current staking params value from store key or panic -func MustUnmarshalParams(cdc *codec.Codec, value []byte) Params { +func MustUnmarshalParams(cdc *codec.LegacyAmino, value []byte) Params { params, err := UnmarshalParams(cdc, value) if err != nil { panic(err) } + return params } // unmarshal the current staking params value from store key -func UnmarshalParams(cdc *codec.Codec, value []byte) (params Params, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, ¶ms) +func UnmarshalParams(cdc *codec.LegacyAmino, value []byte) (params Params, err error) { + err = cdc.UnmarshalBinaryBare(value, ¶ms) if err != nil { return } + return } @@ -121,12 +111,15 @@ func (p Params) Validate() error { if err := validateUnbondingTime(p.UnbondingTime); err != nil { return err } + if err := validateMaxValidators(p.MaxValidators); err != nil { return err } + if err := validateMaxEntries(p.MaxEntries); err != nil { return err } + if err := validateBondDenom(p.BondDenom); err != nil { return err } @@ -148,7 +141,7 @@ func validateUnbondingTime(i interface{}) error { } func validateMaxValidators(i interface{}) error { - v, ok := i.(uint16) + v, ok := i.(uint32) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } @@ -161,7 +154,7 @@ func validateMaxValidators(i interface{}) error { } func validateMaxEntries(i interface{}) error { - v, ok := i.(uint16) + v, ok := i.(uint32) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } @@ -174,7 +167,7 @@ func validateMaxEntries(i interface{}) error { } func validateHistoricalEntries(i interface{}) error { - _, ok := i.(uint16) + _, ok := i.(uint32) if !ok { return fmt.Errorf("invalid parameter type: %T", i) } @@ -191,6 +184,7 @@ func validateBondDenom(i interface{}) error { if strings.TrimSpace(v) == "" { return errors.New("bond denom cannot be blank") } + if err := sdk.ValidateDenom(v); err != nil { return err } diff --git a/x/staking/types/params_test.go b/x/staking/types/params_test.go index c18700ef43d5..6218091e0ff5 100644 --- a/x/staking/types/params_test.go +++ b/x/staking/types/params_test.go @@ -1,14 +1,16 @@ -package types +package types_test import ( "testing" "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestParamsEqual(t *testing.T) { - p1 := DefaultParams() - p2 := DefaultParams() + p1 := types.DefaultParams() + p2 := types.DefaultParams() ok := p1.Equal(p2) require.True(t, ok) diff --git a/x/staking/types/pool.go b/x/staking/types/pool.go index c19bf59a41ad..c71d1684f9aa 100644 --- a/x/staking/types/pool.go +++ b/x/staking/types/pool.go @@ -1,8 +1,6 @@ package types import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -16,12 +14,6 @@ const ( BondedPoolName = "bonded_tokens_pool" ) -// Pool - tracking bonded and not-bonded token supply of the bond denomination -type Pool struct { - NotBondedTokens sdk.Int `json:"not_bonded_tokens" yaml:"not_bonded_tokens"` // tokens which are not bonded to a validator (unbonded or unbonding) - BondedTokens sdk.Int `json:"bonded_tokens" yaml:"bonded_tokens"` // tokens which are currently bonded to a validator -} - // NewPool creates a new Pool instance used for queries func NewPool(notBonded, bonded sdk.Int) Pool { return Pool{ @@ -29,11 +21,3 @@ func NewPool(notBonded, bonded sdk.Int) Pool { BondedTokens: bonded, } } - -// String returns a human readable string representation of a pool. -func (p Pool) String() string { - return fmt.Sprintf(`Pool: - Not Bonded Tokens: %s - Bonded Tokens: %s`, p.NotBondedTokens, - p.BondedTokens) -} diff --git a/x/staking/types/querier.go b/x/staking/types/querier.go index adc387354ea9..08fefae0736a 100644 --- a/x/staking/types/querier.go +++ b/x/staking/types/querier.go @@ -26,7 +26,6 @@ const ( // defines the params for the following queries: // - 'custom/staking/delegatorDelegations' // - 'custom/staking/delegatorUnbondingDelegations' -// - 'custom/staking/delegatorRedelegations' // - 'custom/staking/delegatorValidators' type QueryDelegatorParams struct { DelegatorAddr sdk.AccAddress @@ -42,30 +41,16 @@ func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams // - 'custom/staking/validator' // - 'custom/staking/validatorDelegations' // - 'custom/staking/validatorUnbondingDelegations' -// - 'custom/staking/validatorRedelegations' type QueryValidatorParams struct { ValidatorAddr sdk.ValAddress + Page, Limit int } -func NewQueryValidatorParams(validatorAddr sdk.ValAddress) QueryValidatorParams { +func NewQueryValidatorParams(validatorAddr sdk.ValAddress, page, limit int) QueryValidatorParams { return QueryValidatorParams{ ValidatorAddr: validatorAddr, - } -} - -// defines the params for the following queries: -// - 'custom/staking/delegation' -// - 'custom/staking/unbondingDelegation' -// - 'custom/staking/delegatorValidator' -type QueryBondsParams struct { - DelegatorAddr sdk.AccAddress - ValidatorAddr sdk.ValAddress -} - -func NewQueryBondsParams(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryBondsParams { - return QueryBondsParams{ - DelegatorAddr: delegatorAddr, - ValidatorAddr: validatorAddr, + Page: page, + Limit: limit, } } @@ -77,9 +62,7 @@ type QueryRedelegationParams struct { DstValidatorAddr sdk.ValAddress } -func NewQueryRedelegationParams(delegatorAddr sdk.AccAddress, - srcValidatorAddr, dstValidatorAddr sdk.ValAddress) QueryRedelegationParams { - +func NewQueryRedelegationParams(delegatorAddr sdk.AccAddress, srcValidatorAddr, dstValidatorAddr sdk.ValAddress) QueryRedelegationParams { return QueryRedelegationParams{ DelegatorAddr: delegatorAddr, SrcValidatorAddr: srcValidatorAddr, @@ -97,14 +80,3 @@ type QueryValidatorsParams struct { func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams { return QueryValidatorsParams{page, limit, status} } - -// QueryHistoricalInfoParams defines the params for the following queries: -// - 'custom/staking/historicalInfo' -type QueryHistoricalInfoParams struct { - Height int64 -} - -// NewQueryHistoricalInfoParams creates a new QueryHistoricalInfoParams instance -func NewQueryHistoricalInfoParams(height int64) QueryHistoricalInfoParams { - return QueryHistoricalInfoParams{height} -} diff --git a/x/staking/types/query.pb.go b/x/staking/types/query.pb.go new file mode 100644 index 000000000000..29f84adc27ac --- /dev/null +++ b/x/staking/types/query.pb.go @@ -0,0 +1,6643 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/staking/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 + +// QueryValidatorsRequest is request type for Query/Validators RPC method. +type QueryValidatorsRequest struct { + // status enables to query for validators matching a given status. + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorsRequest) Reset() { *m = QueryValidatorsRequest{} } +func (m *QueryValidatorsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorsRequest) ProtoMessage() {} +func (*QueryValidatorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{0} +} +func (m *QueryValidatorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorsRequest.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 *QueryValidatorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorsRequest.Merge(m, src) +} +func (m *QueryValidatorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorsRequest proto.InternalMessageInfo + +func (m *QueryValidatorsRequest) GetStatus() string { + if m != nil { + return m.Status + } + return "" +} + +func (m *QueryValidatorsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryValidatorsResponse is response type for the Query/Validators RPC method +type QueryValidatorsResponse struct { + // validators contains all the queried validators. + Validators []Validator `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorsResponse) Reset() { *m = QueryValidatorsResponse{} } +func (m *QueryValidatorsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorsResponse) ProtoMessage() {} +func (*QueryValidatorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{1} +} +func (m *QueryValidatorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorsResponse.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 *QueryValidatorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorsResponse.Merge(m, src) +} +func (m *QueryValidatorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorsResponse proto.InternalMessageInfo + +func (m *QueryValidatorsResponse) GetValidators() []Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *QueryValidatorsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryValidatorRequest is response type for the Query/Validator RPC method +type QueryValidatorRequest struct { + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,1,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` +} + +func (m *QueryValidatorRequest) Reset() { *m = QueryValidatorRequest{} } +func (m *QueryValidatorRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorRequest) ProtoMessage() {} +func (*QueryValidatorRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{2} +} +func (m *QueryValidatorRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorRequest.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 *QueryValidatorRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorRequest.Merge(m, src) +} +func (m *QueryValidatorRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorRequest proto.InternalMessageInfo + +func (m *QueryValidatorRequest) GetValidatorAddr() string { + if m != nil { + return m.ValidatorAddr + } + return "" +} + +// QueryValidatorResponse is response type for the Query/Validator RPC method +type QueryValidatorResponse struct { + // validator defines the the validator info. + Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` +} + +func (m *QueryValidatorResponse) Reset() { *m = QueryValidatorResponse{} } +func (m *QueryValidatorResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorResponse) ProtoMessage() {} +func (*QueryValidatorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{3} +} +func (m *QueryValidatorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorResponse.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 *QueryValidatorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorResponse.Merge(m, src) +} +func (m *QueryValidatorResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorResponse proto.InternalMessageInfo + +func (m *QueryValidatorResponse) GetValidator() Validator { + if m != nil { + return m.Validator + } + return Validator{} +} + +// QueryValidatorDelegationsRequest is request type for the +// Query/ValidatorDelegations RPC method +type QueryValidatorDelegationsRequest struct { + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,1,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorDelegationsRequest) Reset() { *m = QueryValidatorDelegationsRequest{} } +func (m *QueryValidatorDelegationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorDelegationsRequest) ProtoMessage() {} +func (*QueryValidatorDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{4} +} +func (m *QueryValidatorDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorDelegationsRequest.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 *QueryValidatorDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorDelegationsRequest.Merge(m, src) +} +func (m *QueryValidatorDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorDelegationsRequest proto.InternalMessageInfo + +func (m *QueryValidatorDelegationsRequest) GetValidatorAddr() string { + if m != nil { + return m.ValidatorAddr + } + return "" +} + +func (m *QueryValidatorDelegationsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryValidatorDelegationsResponse is response type for the +// Query/ValidatorDelegations RPC method +type QueryValidatorDelegationsResponse struct { + DelegationResponses DelegationResponses `protobuf:"bytes,1,rep,name=delegation_responses,json=delegationResponses,proto3,castrepeated=DelegationResponses" json:"delegation_responses"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorDelegationsResponse) Reset() { *m = QueryValidatorDelegationsResponse{} } +func (m *QueryValidatorDelegationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorDelegationsResponse) ProtoMessage() {} +func (*QueryValidatorDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{5} +} +func (m *QueryValidatorDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorDelegationsResponse.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 *QueryValidatorDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorDelegationsResponse.Merge(m, src) +} +func (m *QueryValidatorDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorDelegationsResponse proto.InternalMessageInfo + +func (m *QueryValidatorDelegationsResponse) GetDelegationResponses() DelegationResponses { + if m != nil { + return m.DelegationResponses + } + return nil +} + +func (m *QueryValidatorDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryValidatorUnbondingDelegationsRequest is required type for the +// Query/ValidatorUnbondingDelegations RPC method +type QueryValidatorUnbondingDelegationsRequest struct { + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,1,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorUnbondingDelegationsRequest) Reset() { + *m = QueryValidatorUnbondingDelegationsRequest{} +} +func (m *QueryValidatorUnbondingDelegationsRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryValidatorUnbondingDelegationsRequest) ProtoMessage() {} +func (*QueryValidatorUnbondingDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{6} +} +func (m *QueryValidatorUnbondingDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorUnbondingDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorUnbondingDelegationsRequest.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 *QueryValidatorUnbondingDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorUnbondingDelegationsRequest.Merge(m, src) +} +func (m *QueryValidatorUnbondingDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorUnbondingDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorUnbondingDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorUnbondingDelegationsRequest proto.InternalMessageInfo + +func (m *QueryValidatorUnbondingDelegationsRequest) GetValidatorAddr() string { + if m != nil { + return m.ValidatorAddr + } + return "" +} + +func (m *QueryValidatorUnbondingDelegationsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryValidatorUnbondingDelegationsResponse is response type for the +// Query/ValidatorUnbondingDelegations RPC method. +type QueryValidatorUnbondingDelegationsResponse struct { + UnbondingResponses []UnbondingDelegation `protobuf:"bytes,1,rep,name=unbonding_responses,json=unbondingResponses,proto3" json:"unbonding_responses"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryValidatorUnbondingDelegationsResponse) Reset() { + *m = QueryValidatorUnbondingDelegationsResponse{} +} +func (m *QueryValidatorUnbondingDelegationsResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryValidatorUnbondingDelegationsResponse) ProtoMessage() {} +func (*QueryValidatorUnbondingDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{7} +} +func (m *QueryValidatorUnbondingDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorUnbondingDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorUnbondingDelegationsResponse.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 *QueryValidatorUnbondingDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorUnbondingDelegationsResponse.Merge(m, src) +} +func (m *QueryValidatorUnbondingDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorUnbondingDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorUnbondingDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorUnbondingDelegationsResponse proto.InternalMessageInfo + +func (m *QueryValidatorUnbondingDelegationsResponse) GetUnbondingResponses() []UnbondingDelegation { + if m != nil { + return m.UnbondingResponses + } + return nil +} + +func (m *QueryValidatorUnbondingDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDelegationRequest is request type for the Query/Delegation RPC method. +type QueryDelegationRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,2,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` +} + +func (m *QueryDelegationRequest) Reset() { *m = QueryDelegationRequest{} } +func (m *QueryDelegationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationRequest) ProtoMessage() {} +func (*QueryDelegationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{8} +} +func (m *QueryDelegationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationRequest.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 *QueryDelegationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationRequest.Merge(m, src) +} +func (m *QueryDelegationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationRequest proto.InternalMessageInfo + +// QueryDelegationResponse is response type for the Query/Delegation RPC method. +type QueryDelegationResponse struct { + // delegation_responses defines the delegation info of a delegation. + DelegationResponse *DelegationResponse `protobuf:"bytes,1,opt,name=delegation_response,json=delegationResponse,proto3" json:"delegation_response,omitempty"` +} + +func (m *QueryDelegationResponse) Reset() { *m = QueryDelegationResponse{} } +func (m *QueryDelegationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegationResponse) ProtoMessage() {} +func (*QueryDelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{9} +} +func (m *QueryDelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegationResponse.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 *QueryDelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegationResponse.Merge(m, src) +} +func (m *QueryDelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegationResponse proto.InternalMessageInfo + +func (m *QueryDelegationResponse) GetDelegationResponse() *DelegationResponse { + if m != nil { + return m.DelegationResponse + } + return nil +} + +// QueryUnbondingDelegationRequest is request type for the +// Query/UnbondingDelegation RPC method. +type QueryUnbondingDelegationRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,2,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` +} + +func (m *QueryUnbondingDelegationRequest) Reset() { *m = QueryUnbondingDelegationRequest{} } +func (m *QueryUnbondingDelegationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryUnbondingDelegationRequest) ProtoMessage() {} +func (*QueryUnbondingDelegationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{10} +} +func (m *QueryUnbondingDelegationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnbondingDelegationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnbondingDelegationRequest.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 *QueryUnbondingDelegationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnbondingDelegationRequest.Merge(m, src) +} +func (m *QueryUnbondingDelegationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryUnbondingDelegationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnbondingDelegationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnbondingDelegationRequest proto.InternalMessageInfo + +// QueryDelegationResponse is response type for the Query/UnbondingDelegation +// RPC method. +type QueryUnbondingDelegationResponse struct { + // unbond defines the unbonding information of a delegation. + Unbond UnbondingDelegation `protobuf:"bytes,1,opt,name=unbond,proto3" json:"unbond"` +} + +func (m *QueryUnbondingDelegationResponse) Reset() { *m = QueryUnbondingDelegationResponse{} } +func (m *QueryUnbondingDelegationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryUnbondingDelegationResponse) ProtoMessage() {} +func (*QueryUnbondingDelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{11} +} +func (m *QueryUnbondingDelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUnbondingDelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUnbondingDelegationResponse.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 *QueryUnbondingDelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUnbondingDelegationResponse.Merge(m, src) +} +func (m *QueryUnbondingDelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryUnbondingDelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUnbondingDelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUnbondingDelegationResponse proto.InternalMessageInfo + +func (m *QueryUnbondingDelegationResponse) GetUnbond() UnbondingDelegation { + if m != nil { + return m.Unbond + } + return UnbondingDelegation{} +} + +// QueryDelegatorDelegationsRequest is request type for the +// Query/DelegatorDelegations RPC method. +type QueryDelegatorDelegationsRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorDelegationsRequest) Reset() { *m = QueryDelegatorDelegationsRequest{} } +func (m *QueryDelegatorDelegationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorDelegationsRequest) ProtoMessage() {} +func (*QueryDelegatorDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{12} +} +func (m *QueryDelegatorDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorDelegationsRequest.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 *QueryDelegatorDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorDelegationsRequest.Merge(m, src) +} +func (m *QueryDelegatorDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorDelegationsRequest proto.InternalMessageInfo + +// QueryDelegatorDelegationsResponse is response type for the +// Query/DelegatorDelegations RPC method. +type QueryDelegatorDelegationsResponse struct { + // delegation_responses defines all the delegations' info of a delegator. + DelegationResponses []DelegationResponse `protobuf:"bytes,1,rep,name=delegation_responses,json=delegationResponses,proto3" json:"delegation_responses"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorDelegationsResponse) Reset() { *m = QueryDelegatorDelegationsResponse{} } +func (m *QueryDelegatorDelegationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorDelegationsResponse) ProtoMessage() {} +func (*QueryDelegatorDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{13} +} +func (m *QueryDelegatorDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorDelegationsResponse.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 *QueryDelegatorDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorDelegationsResponse.Merge(m, src) +} +func (m *QueryDelegatorDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorDelegationsResponse proto.InternalMessageInfo + +func (m *QueryDelegatorDelegationsResponse) GetDelegationResponses() []DelegationResponse { + if m != nil { + return m.DelegationResponses + } + return nil +} + +func (m *QueryDelegatorDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDelegatorUnbondingDelegationsRequest is request type for the +// Query/DelegatorUnbondingDelegations RPC method. +type QueryDelegatorUnbondingDelegationsRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorUnbondingDelegationsRequest) Reset() { + *m = QueryDelegatorUnbondingDelegationsRequest{} +} +func (m *QueryDelegatorUnbondingDelegationsRequest) String() string { + return proto.CompactTextString(m) +} +func (*QueryDelegatorUnbondingDelegationsRequest) ProtoMessage() {} +func (*QueryDelegatorUnbondingDelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{14} +} +func (m *QueryDelegatorUnbondingDelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorUnbondingDelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorUnbondingDelegationsRequest.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 *QueryDelegatorUnbondingDelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorUnbondingDelegationsRequest.Merge(m, src) +} +func (m *QueryDelegatorUnbondingDelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorUnbondingDelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorUnbondingDelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorUnbondingDelegationsRequest proto.InternalMessageInfo + +// QueryUnbondingDelegatorDelegationsResponse is response type for the +// Query/UnbondingDelegatorDelegations RPC method. +type QueryDelegatorUnbondingDelegationsResponse struct { + UnbondingResponses []UnbondingDelegation `protobuf:"bytes,1,rep,name=unbonding_responses,json=unbondingResponses,proto3" json:"unbonding_responses"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorUnbondingDelegationsResponse) Reset() { + *m = QueryDelegatorUnbondingDelegationsResponse{} +} +func (m *QueryDelegatorUnbondingDelegationsResponse) String() string { + return proto.CompactTextString(m) +} +func (*QueryDelegatorUnbondingDelegationsResponse) ProtoMessage() {} +func (*QueryDelegatorUnbondingDelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{15} +} +func (m *QueryDelegatorUnbondingDelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorUnbondingDelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorUnbondingDelegationsResponse.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 *QueryDelegatorUnbondingDelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorUnbondingDelegationsResponse.Merge(m, src) +} +func (m *QueryDelegatorUnbondingDelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorUnbondingDelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorUnbondingDelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorUnbondingDelegationsResponse proto.InternalMessageInfo + +func (m *QueryDelegatorUnbondingDelegationsResponse) GetUnbondingResponses() []UnbondingDelegation { + if m != nil { + return m.UnbondingResponses + } + return nil +} + +func (m *QueryDelegatorUnbondingDelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryRedelegationsRequest is request type for the Query/Redelegations RPC +// method. +type QueryRedelegationsRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // src_validator_addr defines the validator address to redelegate from. + SrcValidatorAddr string `protobuf:"bytes,2,opt,name=src_validator_addr,json=srcValidatorAddr,proto3" json:"src_validator_addr,omitempty"` + // dst_validator_addr defines the validator address to redelegate to. + DstValidatorAddr string `protobuf:"bytes,3,opt,name=dst_validator_addr,json=dstValidatorAddr,proto3" json:"dst_validator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryRedelegationsRequest) Reset() { *m = QueryRedelegationsRequest{} } +func (m *QueryRedelegationsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryRedelegationsRequest) ProtoMessage() {} +func (*QueryRedelegationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{16} +} +func (m *QueryRedelegationsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRedelegationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRedelegationsRequest.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 *QueryRedelegationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRedelegationsRequest.Merge(m, src) +} +func (m *QueryRedelegationsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryRedelegationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRedelegationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRedelegationsRequest proto.InternalMessageInfo + +// QueryRedelegationsResponse is response type for the Query/Redelegations RPC +// method. +type QueryRedelegationsResponse struct { + RedelegationResponses []RedelegationResponse `protobuf:"bytes,1,rep,name=redelegation_responses,json=redelegationResponses,proto3" json:"redelegation_responses"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryRedelegationsResponse) Reset() { *m = QueryRedelegationsResponse{} } +func (m *QueryRedelegationsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryRedelegationsResponse) ProtoMessage() {} +func (*QueryRedelegationsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{17} +} +func (m *QueryRedelegationsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRedelegationsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRedelegationsResponse.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 *QueryRedelegationsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRedelegationsResponse.Merge(m, src) +} +func (m *QueryRedelegationsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryRedelegationsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRedelegationsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRedelegationsResponse proto.InternalMessageInfo + +func (m *QueryRedelegationsResponse) GetRedelegationResponses() []RedelegationResponse { + if m != nil { + return m.RedelegationResponses + } + return nil +} + +func (m *QueryRedelegationsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDelegatorValidatorsRequest is request type for the +// Query/DelegatorValidators RPC method. +type QueryDelegatorValidatorsRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorValidatorsRequest) Reset() { *m = QueryDelegatorValidatorsRequest{} } +func (m *QueryDelegatorValidatorsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorsRequest) ProtoMessage() {} +func (*QueryDelegatorValidatorsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{18} +} +func (m *QueryDelegatorValidatorsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorsRequest.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 *QueryDelegatorValidatorsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorsRequest.Merge(m, src) +} +func (m *QueryDelegatorValidatorsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorsRequest proto.InternalMessageInfo + +// QueryDelegatorValidatorsResponse is response type for the +// Query/DelegatorValidators RPC method. +type QueryDelegatorValidatorsResponse struct { + // validators defines the the validators' info of a delegator. + Validators []Validator `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryDelegatorValidatorsResponse) Reset() { *m = QueryDelegatorValidatorsResponse{} } +func (m *QueryDelegatorValidatorsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorsResponse) ProtoMessage() {} +func (*QueryDelegatorValidatorsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{19} +} +func (m *QueryDelegatorValidatorsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorsResponse.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 *QueryDelegatorValidatorsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorsResponse.Merge(m, src) +} +func (m *QueryDelegatorValidatorsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorsResponse proto.InternalMessageInfo + +func (m *QueryDelegatorValidatorsResponse) GetValidators() []Validator { + if m != nil { + return m.Validators + } + return nil +} + +func (m *QueryDelegatorValidatorsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryDelegatorValidatorRequest is request type for the +// Query/DelegatorValidator RPC method. +type QueryDelegatorValidatorRequest struct { + // delegator_addr defines the delegator address to query for. + DelegatorAddr string `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + // validator_addr defines the validator address to query for. + ValidatorAddr string `protobuf:"bytes,2,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` +} + +func (m *QueryDelegatorValidatorRequest) Reset() { *m = QueryDelegatorValidatorRequest{} } +func (m *QueryDelegatorValidatorRequest) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorRequest) ProtoMessage() {} +func (*QueryDelegatorValidatorRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{20} +} +func (m *QueryDelegatorValidatorRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorRequest.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 *QueryDelegatorValidatorRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorRequest.Merge(m, src) +} +func (m *QueryDelegatorValidatorRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorRequest proto.InternalMessageInfo + +// QueryDelegatorValidatorResponse response type for the +// Query/DelegatorValidator RPC method. +type QueryDelegatorValidatorResponse struct { + // validator defines the the validator info. + Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` +} + +func (m *QueryDelegatorValidatorResponse) Reset() { *m = QueryDelegatorValidatorResponse{} } +func (m *QueryDelegatorValidatorResponse) String() string { return proto.CompactTextString(m) } +func (*QueryDelegatorValidatorResponse) ProtoMessage() {} +func (*QueryDelegatorValidatorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{21} +} +func (m *QueryDelegatorValidatorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryDelegatorValidatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryDelegatorValidatorResponse.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 *QueryDelegatorValidatorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryDelegatorValidatorResponse.Merge(m, src) +} +func (m *QueryDelegatorValidatorResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryDelegatorValidatorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryDelegatorValidatorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryDelegatorValidatorResponse proto.InternalMessageInfo + +func (m *QueryDelegatorValidatorResponse) GetValidator() Validator { + if m != nil { + return m.Validator + } + return Validator{} +} + +// QueryHistoricalInfoRequest is request type for the Query/HistoricalInfo RPC +// method. +type QueryHistoricalInfoRequest struct { + // height defines at which height to query the historical info. + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryHistoricalInfoRequest) Reset() { *m = QueryHistoricalInfoRequest{} } +func (m *QueryHistoricalInfoRequest) String() string { return proto.CompactTextString(m) } +func (*QueryHistoricalInfoRequest) ProtoMessage() {} +func (*QueryHistoricalInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{22} +} +func (m *QueryHistoricalInfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryHistoricalInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryHistoricalInfoRequest.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 *QueryHistoricalInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryHistoricalInfoRequest.Merge(m, src) +} +func (m *QueryHistoricalInfoRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryHistoricalInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryHistoricalInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryHistoricalInfoRequest proto.InternalMessageInfo + +func (m *QueryHistoricalInfoRequest) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC +// method. +type QueryHistoricalInfoResponse struct { + // hist defines the historical info at the given height. + Hist *HistoricalInfo `protobuf:"bytes,1,opt,name=hist,proto3" json:"hist,omitempty"` +} + +func (m *QueryHistoricalInfoResponse) Reset() { *m = QueryHistoricalInfoResponse{} } +func (m *QueryHistoricalInfoResponse) String() string { return proto.CompactTextString(m) } +func (*QueryHistoricalInfoResponse) ProtoMessage() {} +func (*QueryHistoricalInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{23} +} +func (m *QueryHistoricalInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryHistoricalInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryHistoricalInfoResponse.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 *QueryHistoricalInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryHistoricalInfoResponse.Merge(m, src) +} +func (m *QueryHistoricalInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryHistoricalInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryHistoricalInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryHistoricalInfoResponse proto.InternalMessageInfo + +func (m *QueryHistoricalInfoResponse) GetHist() *HistoricalInfo { + if m != nil { + return m.Hist + } + return nil +} + +// QueryPoolRequest is request type for the Query/Pool RPC method. +type QueryPoolRequest struct { +} + +func (m *QueryPoolRequest) Reset() { *m = QueryPoolRequest{} } +func (m *QueryPoolRequest) String() string { return proto.CompactTextString(m) } +func (*QueryPoolRequest) ProtoMessage() {} +func (*QueryPoolRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{24} +} +func (m *QueryPoolRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPoolRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPoolRequest.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 *QueryPoolRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPoolRequest.Merge(m, src) +} +func (m *QueryPoolRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryPoolRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPoolRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPoolRequest proto.InternalMessageInfo + +// QueryPoolResponse is response type for the Query/Pool RPC method. +type QueryPoolResponse struct { + // pool defines the pool info. + Pool Pool `protobuf:"bytes,1,opt,name=pool,proto3" json:"pool"` +} + +func (m *QueryPoolResponse) Reset() { *m = QueryPoolResponse{} } +func (m *QueryPoolResponse) String() string { return proto.CompactTextString(m) } +func (*QueryPoolResponse) ProtoMessage() {} +func (*QueryPoolResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{25} +} +func (m *QueryPoolResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryPoolResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryPoolResponse.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 *QueryPoolResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryPoolResponse.Merge(m, src) +} +func (m *QueryPoolResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryPoolResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryPoolResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryPoolResponse proto.InternalMessageInfo + +func (m *QueryPoolResponse) GetPool() Pool { + if m != nil { + return m.Pool + } + return Pool{} +} + +// QueryParamsRequest is request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{26} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params holds all the parameters of this module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f270127f442bbcd8, []int{27} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryValidatorsRequest)(nil), "cosmos.staking.v1beta1.QueryValidatorsRequest") + proto.RegisterType((*QueryValidatorsResponse)(nil), "cosmos.staking.v1beta1.QueryValidatorsResponse") + proto.RegisterType((*QueryValidatorRequest)(nil), "cosmos.staking.v1beta1.QueryValidatorRequest") + proto.RegisterType((*QueryValidatorResponse)(nil), "cosmos.staking.v1beta1.QueryValidatorResponse") + proto.RegisterType((*QueryValidatorDelegationsRequest)(nil), "cosmos.staking.v1beta1.QueryValidatorDelegationsRequest") + proto.RegisterType((*QueryValidatorDelegationsResponse)(nil), "cosmos.staking.v1beta1.QueryValidatorDelegationsResponse") + proto.RegisterType((*QueryValidatorUnbondingDelegationsRequest)(nil), "cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsRequest") + proto.RegisterType((*QueryValidatorUnbondingDelegationsResponse)(nil), "cosmos.staking.v1beta1.QueryValidatorUnbondingDelegationsResponse") + proto.RegisterType((*QueryDelegationRequest)(nil), "cosmos.staking.v1beta1.QueryDelegationRequest") + proto.RegisterType((*QueryDelegationResponse)(nil), "cosmos.staking.v1beta1.QueryDelegationResponse") + proto.RegisterType((*QueryUnbondingDelegationRequest)(nil), "cosmos.staking.v1beta1.QueryUnbondingDelegationRequest") + proto.RegisterType((*QueryUnbondingDelegationResponse)(nil), "cosmos.staking.v1beta1.QueryUnbondingDelegationResponse") + proto.RegisterType((*QueryDelegatorDelegationsRequest)(nil), "cosmos.staking.v1beta1.QueryDelegatorDelegationsRequest") + proto.RegisterType((*QueryDelegatorDelegationsResponse)(nil), "cosmos.staking.v1beta1.QueryDelegatorDelegationsResponse") + proto.RegisterType((*QueryDelegatorUnbondingDelegationsRequest)(nil), "cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsRequest") + proto.RegisterType((*QueryDelegatorUnbondingDelegationsResponse)(nil), "cosmos.staking.v1beta1.QueryDelegatorUnbondingDelegationsResponse") + proto.RegisterType((*QueryRedelegationsRequest)(nil), "cosmos.staking.v1beta1.QueryRedelegationsRequest") + proto.RegisterType((*QueryRedelegationsResponse)(nil), "cosmos.staking.v1beta1.QueryRedelegationsResponse") + proto.RegisterType((*QueryDelegatorValidatorsRequest)(nil), "cosmos.staking.v1beta1.QueryDelegatorValidatorsRequest") + proto.RegisterType((*QueryDelegatorValidatorsResponse)(nil), "cosmos.staking.v1beta1.QueryDelegatorValidatorsResponse") + proto.RegisterType((*QueryDelegatorValidatorRequest)(nil), "cosmos.staking.v1beta1.QueryDelegatorValidatorRequest") + proto.RegisterType((*QueryDelegatorValidatorResponse)(nil), "cosmos.staking.v1beta1.QueryDelegatorValidatorResponse") + proto.RegisterType((*QueryHistoricalInfoRequest)(nil), "cosmos.staking.v1beta1.QueryHistoricalInfoRequest") + proto.RegisterType((*QueryHistoricalInfoResponse)(nil), "cosmos.staking.v1beta1.QueryHistoricalInfoResponse") + proto.RegisterType((*QueryPoolRequest)(nil), "cosmos.staking.v1beta1.QueryPoolRequest") + proto.RegisterType((*QueryPoolResponse)(nil), "cosmos.staking.v1beta1.QueryPoolResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.staking.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.staking.v1beta1.QueryParamsResponse") +} + +func init() { + proto.RegisterFile("cosmos/staking/v1beta1/query.proto", fileDescriptor_f270127f442bbcd8) +} + +var fileDescriptor_f270127f442bbcd8 = []byte{ + // 1303 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcf, 0x4f, 0x1c, 0x75, + 0x14, 0xdf, 0x2f, 0x20, 0x91, 0xd7, 0xb4, 0xa9, 0xdf, 0x05, 0xc4, 0x29, 0xee, 0xd2, 0x09, 0x22, + 0xa5, 0x74, 0x46, 0xa0, 0x52, 0xac, 0x4d, 0x15, 0xac, 0x54, 0xd2, 0x83, 0xb0, 0x46, 0xfc, 0x75, + 0x20, 0xb3, 0x3b, 0xd3, 0xd9, 0x49, 0x97, 0x99, 0xed, 0xcc, 0x40, 0x40, 0xc2, 0x41, 0x4f, 0x7a, + 0xd3, 0x78, 0x52, 0x2f, 0x3d, 0x98, 0x98, 0xe8, 0x51, 0xff, 0x01, 0x4f, 0xd6, 0x1b, 0x46, 0x0f, + 0x7a, 0xa9, 0x06, 0x3c, 0x34, 0x9e, 0xbc, 0x19, 0x6f, 0x66, 0xbf, 0xf3, 0x66, 0x76, 0x86, 0xf9, + 0xb9, 0xcb, 0x12, 0xd2, 0x13, 0xbb, 0xdf, 0x7d, 0x3f, 0x3e, 0x9f, 0xf7, 0xbe, 0xef, 0x7d, 0xdf, + 0x0b, 0xc0, 0x57, 0x0c, 0x6b, 0xdd, 0xb0, 0x44, 0xcb, 0x96, 0xee, 0x68, 0xba, 0x2a, 0x6e, 0x4e, + 0x95, 0x15, 0x5b, 0x9a, 0x12, 0xef, 0x6e, 0x28, 0xe6, 0xb6, 0x50, 0x37, 0x0d, 0xdb, 0xa0, 0x83, + 0x8e, 0x8c, 0x80, 0x32, 0x02, 0xca, 0x70, 0x13, 0xa8, 0x5b, 0x96, 0x2c, 0xc5, 0x51, 0xf0, 0xd4, + 0xeb, 0x92, 0xaa, 0xe9, 0x92, 0xad, 0x19, 0xba, 0x63, 0x83, 0xeb, 0x57, 0x0d, 0xd5, 0x60, 0x1f, + 0xc5, 0xc6, 0x27, 0x3c, 0x1d, 0x56, 0x0d, 0x43, 0xad, 0x29, 0xa2, 0x54, 0xd7, 0x44, 0x49, 0xd7, + 0x0d, 0x9b, 0xa9, 0x58, 0xf8, 0xeb, 0x68, 0x0c, 0x36, 0x17, 0x07, 0x93, 0xe2, 0xb7, 0x60, 0x70, + 0xa5, 0xe1, 0x7b, 0x55, 0xaa, 0x69, 0xb2, 0x64, 0x1b, 0xa6, 0x55, 0x52, 0xee, 0x6e, 0x28, 0x96, + 0x4d, 0x07, 0xa1, 0xd7, 0xb2, 0x25, 0x7b, 0xc3, 0x1a, 0x22, 0x23, 0x64, 0xbc, 0xaf, 0x84, 0xdf, + 0xe8, 0x22, 0x40, 0x13, 0xdf, 0x50, 0xd7, 0x08, 0x19, 0x3f, 0x35, 0x3d, 0x26, 0x20, 0xc9, 0x06, + 0x19, 0xc1, 0x61, 0x8f, 0xfe, 0x84, 0x65, 0x49, 0x55, 0xd0, 0x66, 0xc9, 0xa7, 0xc9, 0x7f, 0x4b, + 0xe0, 0xc9, 0x90, 0x6b, 0xab, 0x6e, 0xe8, 0x96, 0x42, 0x6f, 0x02, 0x6c, 0x7a, 0xa7, 0x43, 0x64, + 0xa4, 0x7b, 0xfc, 0xd4, 0xf4, 0x79, 0x21, 0x3a, 0x90, 0x82, 0xa7, 0xbf, 0xd0, 0x73, 0xff, 0x41, + 0x31, 0x57, 0xf2, 0xa9, 0x36, 0x0c, 0x85, 0xc0, 0x3e, 0x9b, 0x0a, 0xd6, 0x41, 0x11, 0x40, 0x7b, + 0x1d, 0x06, 0x82, 0x60, 0xdd, 0x30, 0x3d, 0x03, 0x67, 0x3c, 0x7f, 0x6b, 0x92, 0x2c, 0x9b, 0x18, + 0xae, 0xd3, 0xde, 0xe9, 0xbc, 0x2c, 0x9b, 0xfc, 0xda, 0xe1, 0x38, 0x7b, 0x5c, 0x5f, 0x85, 0x3e, + 0x4f, 0x94, 0xe9, 0xb6, 0x40, 0xb5, 0xa9, 0xc9, 0x7f, 0x4a, 0x60, 0x24, 0xe8, 0xe1, 0x86, 0x52, + 0x53, 0x54, 0xe7, 0x4a, 0xb4, 0x06, 0xb6, 0x63, 0x29, 0x7e, 0x48, 0xe0, 0x7c, 0x02, 0x26, 0x0c, + 0xc0, 0xfb, 0xd0, 0x2f, 0x7b, 0xc7, 0x6b, 0x26, 0x1e, 0xbb, 0x69, 0x9f, 0x88, 0x8b, 0x45, 0xd3, + 0x94, 0x6b, 0x69, 0xe1, 0x5c, 0x23, 0x28, 0xdf, 0xfc, 0x51, 0xcc, 0x87, 0x7f, 0xb3, 0x4a, 0x79, + 0x39, 0x7c, 0xd8, 0xb9, 0xfb, 0xf1, 0x05, 0x81, 0x0b, 0x41, 0xaa, 0x6f, 0xea, 0x65, 0x43, 0x97, + 0x35, 0x5d, 0x3d, 0xf9, 0x3c, 0xfc, 0x4e, 0x60, 0x22, 0x0b, 0x38, 0x4c, 0x48, 0x19, 0xf2, 0x1b, + 0xee, 0xef, 0xa1, 0x7c, 0x5c, 0x8c, 0xcb, 0x47, 0x84, 0x49, 0xbc, 0xa5, 0xd4, 0xb3, 0x76, 0x0c, + 0x81, 0xaf, 0x63, 0x61, 0xf9, 0x53, 0xee, 0x05, 0x19, 0x53, 0x7e, 0x28, 0xc8, 0xde, 0x29, 0x0b, + 0x72, 0x38, 0x17, 0x5d, 0x11, 0xb9, 0xb8, 0xfa, 0xf8, 0x47, 0xf7, 0x8a, 0xb9, 0x87, 0xf7, 0x8a, + 0x39, 0x7e, 0x13, 0xfb, 0x56, 0xf8, 0x92, 0xd1, 0xf7, 0x20, 0x1f, 0x71, 0x95, 0xb1, 0xaa, 0x5b, + 0xb8, 0xc9, 0x25, 0x1a, 0xbe, 0xac, 0xfc, 0x36, 0x14, 0x99, 0xdf, 0x88, 0x40, 0x1f, 0x37, 0xe5, + 0x75, 0xec, 0x2d, 0x91, 0xae, 0x91, 0xfb, 0x12, 0xf4, 0x3a, 0x79, 0x46, 0xba, 0x6d, 0x5c, 0x14, + 0x34, 0xc0, 0x7f, 0xe9, 0xf6, 0xb2, 0x1b, 0x2e, 0xec, 0xe8, 0x1a, 0xca, 0xc2, 0xb5, 0x43, 0x35, + 0xe4, 0x0b, 0xc6, 0xcf, 0x6e, 0x57, 0x8b, 0x46, 0x87, 0xe1, 0xa8, 0x74, 0xac, 0xab, 0x39, 0xb1, + 0x39, 0xde, 0xf6, 0xf5, 0x95, 0xdb, 0xbe, 0x3c, 0x4e, 0x29, 0xed, 0xeb, 0x64, 0x42, 0xef, 0x35, + 0xb2, 0x14, 0x98, 0x8f, 0x62, 0x23, 0xfb, 0x87, 0xc0, 0x53, 0x8c, 0x5b, 0x49, 0x91, 0xdb, 0x0e, + 0xf9, 0x24, 0x50, 0xcb, 0xac, 0xac, 0x45, 0x56, 0xf7, 0x59, 0xcb, 0xac, 0xac, 0x06, 0xde, 0x97, + 0x49, 0xa0, 0xb2, 0x65, 0x1f, 0x96, 0xee, 0x76, 0xa4, 0x65, 0xcb, 0x5e, 0x4d, 0x78, 0x8d, 0x7a, + 0x3a, 0x90, 0xce, 0x3d, 0x02, 0x5c, 0x14, 0x65, 0x4c, 0x9f, 0x06, 0x83, 0xa6, 0x92, 0x50, 0x44, + 0x93, 0x71, 0x19, 0xf4, 0x9b, 0x3b, 0x54, 0x46, 0x03, 0xa6, 0x72, 0xdc, 0x73, 0x40, 0x31, 0x78, + 0x43, 0xc3, 0x93, 0xf5, 0x89, 0x95, 0xcf, 0xf7, 0xa1, 0xbe, 0xfa, 0x48, 0xcc, 0xde, 0x5b, 0x50, + 0x88, 0x41, 0x7d, 0xdc, 0xef, 0x5e, 0x35, 0x36, 0x99, 0x9d, 0x1e, 0xdf, 0x2f, 0x63, 0x25, 0xbc, + 0xa6, 0x59, 0xb6, 0x61, 0x6a, 0x15, 0xa9, 0xb6, 0xa4, 0xdf, 0x36, 0x7c, 0xbb, 0x58, 0x55, 0xd1, + 0xd4, 0xaa, 0xcd, 0x3c, 0x74, 0x97, 0xf0, 0x1b, 0xff, 0x0e, 0x9c, 0x8b, 0xd4, 0x42, 0x6c, 0x57, + 0xa1, 0xa7, 0xaa, 0x59, 0x36, 0xc2, 0x1a, 0x8b, 0x83, 0x75, 0x48, 0x9b, 0xe9, 0xf0, 0x14, 0xce, + 0x32, 0xd3, 0xcb, 0x86, 0x51, 0x43, 0x18, 0xfc, 0x2d, 0x78, 0xc2, 0x77, 0x86, 0x4e, 0x66, 0xa1, + 0xa7, 0x6e, 0x18, 0x35, 0x74, 0x32, 0x1c, 0xe7, 0xa4, 0xa1, 0x83, 0xb4, 0x99, 0x3c, 0xdf, 0x0f, + 0xd4, 0x31, 0x26, 0x99, 0xd2, 0xba, 0x5b, 0x1b, 0xfc, 0x1b, 0x90, 0x0f, 0x9c, 0xa2, 0x93, 0x6b, + 0xd0, 0x5b, 0x67, 0x27, 0xe8, 0xa6, 0x10, 0xeb, 0x86, 0x49, 0xb9, 0xf3, 0x84, 0xa3, 0x33, 0xfd, + 0xf7, 0x00, 0x3c, 0xc6, 0xac, 0xd2, 0xcf, 0x09, 0x40, 0xf3, 0xce, 0x53, 0x21, 0xce, 0x4c, 0xf4, + 0x4e, 0xcc, 0x89, 0x99, 0xe5, 0x71, 0x66, 0x9b, 0xf8, 0xf0, 0x97, 0xbf, 0x3e, 0xeb, 0x1a, 0xa5, + 0xbc, 0x18, 0xb3, 0x8d, 0xfb, 0xea, 0xe5, 0x6b, 0x02, 0x7d, 0x9e, 0x09, 0x7a, 0x29, 0x9b, 0x2b, + 0x17, 0x99, 0x90, 0x55, 0x1c, 0x81, 0xbd, 0xc8, 0x80, 0x3d, 0x4f, 0x67, 0xd2, 0x81, 0x89, 0x3b, + 0xc1, 0xa2, 0xd9, 0xa5, 0xbf, 0x12, 0xe8, 0x8f, 0x5a, 0xe9, 0xe8, 0x5c, 0x36, 0x14, 0xe1, 0x91, + 0x82, 0x7b, 0xa1, 0x0d, 0x4d, 0xa4, 0x72, 0x93, 0x51, 0x99, 0xa7, 0x2f, 0xb5, 0x41, 0x45, 0xf4, + 0xbd, 0x3b, 0xf4, 0x3f, 0x02, 0x4f, 0x27, 0x6e, 0x48, 0x74, 0x3e, 0x1b, 0xca, 0x84, 0xd9, 0x89, + 0x5b, 0x38, 0x8a, 0x09, 0x64, 0xbc, 0xc2, 0x18, 0xdf, 0xa2, 0x4b, 0xed, 0x30, 0x6e, 0x4e, 0x44, + 0x7e, 0xee, 0x3f, 0x12, 0x80, 0xa6, 0xab, 0x94, 0xc2, 0x08, 0x2d, 0x1e, 0x29, 0x85, 0x11, 0x1e, + 0x6a, 0xf9, 0xb7, 0x19, 0x85, 0x12, 0x5d, 0x3e, 0x62, 0xd2, 0xc4, 0x9d, 0x60, 0xe3, 0xdf, 0xa5, + 0xff, 0x12, 0xc8, 0x47, 0x44, 0x8f, 0x5e, 0x49, 0x84, 0x18, 0xbf, 0x54, 0x71, 0x73, 0xad, 0x2b, + 0x22, 0xc9, 0x75, 0x46, 0x52, 0xa5, 0x4a, 0xa7, 0x49, 0x46, 0x26, 0x91, 0xfe, 0x44, 0xa0, 0x3f, + 0x6a, 0x27, 0x49, 0x29, 0xcb, 0x84, 0x25, 0x2b, 0xa5, 0x2c, 0x93, 0x16, 0x20, 0xfe, 0x1a, 0x23, + 0x3f, 0x4b, 0x2f, 0xc7, 0x91, 0x4f, 0xcc, 0x62, 0xa3, 0x16, 0x13, 0x87, 0xfc, 0x94, 0x5a, 0xcc, + 0xb2, 0xc7, 0xa4, 0xd4, 0x62, 0xa6, 0x1d, 0x23, 0xbd, 0x16, 0x3d, 0x66, 0x19, 0xd3, 0x68, 0xd1, + 0x1f, 0x08, 0x9c, 0x0e, 0x4c, 0xc4, 0x74, 0x2a, 0x11, 0x68, 0xd4, 0xc2, 0xc0, 0x4d, 0xb7, 0xa2, + 0x82, 0x5c, 0x96, 0x18, 0x97, 0x57, 0xe8, 0x7c, 0x3b, 0x5c, 0xcc, 0x00, 0xe2, 0x3d, 0x02, 0xf9, + 0x88, 0x29, 0x33, 0xa5, 0x0a, 0xe3, 0x87, 0x66, 0x6e, 0xae, 0x75, 0x45, 0x64, 0xb5, 0xc8, 0x58, + 0xbd, 0x4c, 0xaf, 0xb7, 0xc3, 0xca, 0xf7, 0x3e, 0x3f, 0x20, 0x40, 0xc3, 0x7e, 0xe8, 0x6c, 0x8b, + 0xc0, 0x5c, 0x42, 0x57, 0x5a, 0xd6, 0x43, 0x3e, 0x6f, 0x31, 0x3e, 0x2b, 0xf4, 0xf5, 0xa3, 0xf1, + 0x09, 0x3f, 0xeb, 0xdf, 0x11, 0x38, 0x13, 0x9c, 0x05, 0x69, 0xf2, 0x2d, 0x8a, 0x1c, 0x56, 0xb9, + 0x99, 0x96, 0x74, 0x90, 0xd4, 0x1c, 0x23, 0x35, 0x4d, 0x9f, 0x8b, 0x23, 0x55, 0xf5, 0xf4, 0xd6, + 0x34, 0xfd, 0xb6, 0x21, 0xee, 0x38, 0x23, 0xf0, 0x2e, 0xfd, 0x80, 0x40, 0x4f, 0x63, 0xb8, 0xa4, + 0xe3, 0x89, 0x7e, 0x7d, 0x73, 0x2c, 0x77, 0x21, 0x83, 0x24, 0xe2, 0x1a, 0x65, 0xb8, 0x0a, 0x74, + 0x38, 0x0e, 0x57, 0x63, 0x96, 0xa5, 0x1f, 0x13, 0xe8, 0x75, 0x26, 0x4f, 0x3a, 0x91, 0x6c, 0xdb, + 0x3f, 0xec, 0x72, 0x17, 0x33, 0xc9, 0x22, 0x92, 0x31, 0x86, 0x64, 0x84, 0x16, 0x62, 0x91, 0x38, + 0xa3, 0xef, 0xe2, 0xfd, 0xfd, 0x02, 0xd9, 0xdb, 0x2f, 0x90, 0x3f, 0xf7, 0x0b, 0xe4, 0x93, 0x83, + 0x42, 0x6e, 0xef, 0xa0, 0x90, 0xfb, 0xed, 0xa0, 0x90, 0x7b, 0x77, 0x52, 0xd5, 0xec, 0xea, 0x46, + 0x59, 0xa8, 0x18, 0xeb, 0xae, 0x0d, 0xe7, 0xcf, 0x25, 0x4b, 0xbe, 0x23, 0x6e, 0x79, 0x06, 0xed, + 0xed, 0xba, 0x62, 0x95, 0x7b, 0xd9, 0x3f, 0x88, 0x66, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x4b, + 0x6e, 0xeb, 0x7f, 0xe4, 0x1a, 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 { + // Validators queries all validators that match the given status. + Validators(ctx context.Context, in *QueryValidatorsRequest, opts ...grpc.CallOption) (*QueryValidatorsResponse, error) + // Validator queries validator info for given validator address. + Validator(ctx context.Context, in *QueryValidatorRequest, opts ...grpc.CallOption) (*QueryValidatorResponse, error) + // ValidatorDelegations queries delegate info for given validator. + ValidatorDelegations(ctx context.Context, in *QueryValidatorDelegationsRequest, opts ...grpc.CallOption) (*QueryValidatorDelegationsResponse, error) + // ValidatorUnbondingDelegations queries unbonding delegations of a validator. + ValidatorUnbondingDelegations(ctx context.Context, in *QueryValidatorUnbondingDelegationsRequest, opts ...grpc.CallOption) (*QueryValidatorUnbondingDelegationsResponse, error) + // Delegation queries delegate info for given validator delegator pair. + Delegation(ctx context.Context, in *QueryDelegationRequest, opts ...grpc.CallOption) (*QueryDelegationResponse, error) + // UnbondingDelegation queries unbonding info for given validator delegator + // pair. + UnbondingDelegation(ctx context.Context, in *QueryUnbondingDelegationRequest, opts ...grpc.CallOption) (*QueryUnbondingDelegationResponse, error) + // DelegatorDelegations queries all delegations of a given delegator address. + DelegatorDelegations(ctx context.Context, in *QueryDelegatorDelegationsRequest, opts ...grpc.CallOption) (*QueryDelegatorDelegationsResponse, error) + // DelegatorUnbondingDelegations queries all unbonding delegations of a given + // delegator address. + DelegatorUnbondingDelegations(ctx context.Context, in *QueryDelegatorUnbondingDelegationsRequest, opts ...grpc.CallOption) (*QueryDelegatorUnbondingDelegationsResponse, error) + // Redelegations queries redelegations of given address. + Redelegations(ctx context.Context, in *QueryRedelegationsRequest, opts ...grpc.CallOption) (*QueryRedelegationsResponse, error) + // DelegatorValidators queries all validators info for given delegator + // address. + DelegatorValidators(ctx context.Context, in *QueryDelegatorValidatorsRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorsResponse, error) + // DelegatorValidator queries validator info for given delegator validator + // pair. + DelegatorValidator(ctx context.Context, in *QueryDelegatorValidatorRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorResponse, error) + // HistoricalInfo queries the historical info for given height. + HistoricalInfo(ctx context.Context, in *QueryHistoricalInfoRequest, opts ...grpc.CallOption) (*QueryHistoricalInfoResponse, error) + // Pool queries the pool info. + Pool(ctx context.Context, in *QueryPoolRequest, opts ...grpc.CallOption) (*QueryPoolResponse, error) + // Parameters queries the staking parameters. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Validators(ctx context.Context, in *QueryValidatorsRequest, opts ...grpc.CallOption) (*QueryValidatorsResponse, error) { + out := new(QueryValidatorsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Validators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Validator(ctx context.Context, in *QueryValidatorRequest, opts ...grpc.CallOption) (*QueryValidatorResponse, error) { + out := new(QueryValidatorResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Validator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorDelegations(ctx context.Context, in *QueryValidatorDelegationsRequest, opts ...grpc.CallOption) (*QueryValidatorDelegationsResponse, error) { + out := new(QueryValidatorDelegationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/ValidatorDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorUnbondingDelegations(ctx context.Context, in *QueryValidatorUnbondingDelegationsRequest, opts ...grpc.CallOption) (*QueryValidatorUnbondingDelegationsResponse, error) { + out := new(QueryValidatorUnbondingDelegationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/ValidatorUnbondingDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Delegation(ctx context.Context, in *QueryDelegationRequest, opts ...grpc.CallOption) (*QueryDelegationResponse, error) { + out := new(QueryDelegationResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Delegation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) UnbondingDelegation(ctx context.Context, in *QueryUnbondingDelegationRequest, opts ...grpc.CallOption) (*QueryUnbondingDelegationResponse, error) { + out := new(QueryUnbondingDelegationResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/UnbondingDelegation", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorDelegations(ctx context.Context, in *QueryDelegatorDelegationsRequest, opts ...grpc.CallOption) (*QueryDelegatorDelegationsResponse, error) { + out := new(QueryDelegatorDelegationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/DelegatorDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorUnbondingDelegations(ctx context.Context, in *QueryDelegatorUnbondingDelegationsRequest, opts ...grpc.CallOption) (*QueryDelegatorUnbondingDelegationsResponse, error) { + out := new(QueryDelegatorUnbondingDelegationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Redelegations(ctx context.Context, in *QueryRedelegationsRequest, opts ...grpc.CallOption) (*QueryRedelegationsResponse, error) { + out := new(QueryRedelegationsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Redelegations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorValidators(ctx context.Context, in *QueryDelegatorValidatorsRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorsResponse, error) { + out := new(QueryDelegatorValidatorsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/DelegatorValidators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) DelegatorValidator(ctx context.Context, in *QueryDelegatorValidatorRequest, opts ...grpc.CallOption) (*QueryDelegatorValidatorResponse, error) { + out := new(QueryDelegatorValidatorResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/DelegatorValidator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) HistoricalInfo(ctx context.Context, in *QueryHistoricalInfoRequest, opts ...grpc.CallOption) (*QueryHistoricalInfoResponse, error) { + out := new(QueryHistoricalInfoResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/HistoricalInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Pool(ctx context.Context, in *QueryPoolRequest, opts ...grpc.CallOption) (*QueryPoolResponse, error) { + out := new(QueryPoolResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Pool", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Validators queries all validators that match the given status. + Validators(context.Context, *QueryValidatorsRequest) (*QueryValidatorsResponse, error) + // Validator queries validator info for given validator address. + Validator(context.Context, *QueryValidatorRequest) (*QueryValidatorResponse, error) + // ValidatorDelegations queries delegate info for given validator. + ValidatorDelegations(context.Context, *QueryValidatorDelegationsRequest) (*QueryValidatorDelegationsResponse, error) + // ValidatorUnbondingDelegations queries unbonding delegations of a validator. + ValidatorUnbondingDelegations(context.Context, *QueryValidatorUnbondingDelegationsRequest) (*QueryValidatorUnbondingDelegationsResponse, error) + // Delegation queries delegate info for given validator delegator pair. + Delegation(context.Context, *QueryDelegationRequest) (*QueryDelegationResponse, error) + // UnbondingDelegation queries unbonding info for given validator delegator + // pair. + UnbondingDelegation(context.Context, *QueryUnbondingDelegationRequest) (*QueryUnbondingDelegationResponse, error) + // DelegatorDelegations queries all delegations of a given delegator address. + DelegatorDelegations(context.Context, *QueryDelegatorDelegationsRequest) (*QueryDelegatorDelegationsResponse, error) + // DelegatorUnbondingDelegations queries all unbonding delegations of a given + // delegator address. + DelegatorUnbondingDelegations(context.Context, *QueryDelegatorUnbondingDelegationsRequest) (*QueryDelegatorUnbondingDelegationsResponse, error) + // Redelegations queries redelegations of given address. + Redelegations(context.Context, *QueryRedelegationsRequest) (*QueryRedelegationsResponse, error) + // DelegatorValidators queries all validators info for given delegator + // address. + DelegatorValidators(context.Context, *QueryDelegatorValidatorsRequest) (*QueryDelegatorValidatorsResponse, error) + // DelegatorValidator queries validator info for given delegator validator + // pair. + DelegatorValidator(context.Context, *QueryDelegatorValidatorRequest) (*QueryDelegatorValidatorResponse, error) + // HistoricalInfo queries the historical info for given height. + HistoricalInfo(context.Context, *QueryHistoricalInfoRequest) (*QueryHistoricalInfoResponse, error) + // Pool queries the pool info. + Pool(context.Context, *QueryPoolRequest) (*QueryPoolResponse, error) + // Parameters queries the staking parameters. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Validators(ctx context.Context, req *QueryValidatorsRequest) (*QueryValidatorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Validators not implemented") +} +func (*UnimplementedQueryServer) Validator(ctx context.Context, req *QueryValidatorRequest) (*QueryValidatorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Validator not implemented") +} +func (*UnimplementedQueryServer) ValidatorDelegations(ctx context.Context, req *QueryValidatorDelegationsRequest) (*QueryValidatorDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorDelegations not implemented") +} +func (*UnimplementedQueryServer) ValidatorUnbondingDelegations(ctx context.Context, req *QueryValidatorUnbondingDelegationsRequest) (*QueryValidatorUnbondingDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorUnbondingDelegations not implemented") +} +func (*UnimplementedQueryServer) Delegation(ctx context.Context, req *QueryDelegationRequest) (*QueryDelegationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delegation not implemented") +} +func (*UnimplementedQueryServer) UnbondingDelegation(ctx context.Context, req *QueryUnbondingDelegationRequest) (*QueryUnbondingDelegationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnbondingDelegation not implemented") +} +func (*UnimplementedQueryServer) DelegatorDelegations(ctx context.Context, req *QueryDelegatorDelegationsRequest) (*QueryDelegatorDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorDelegations not implemented") +} +func (*UnimplementedQueryServer) DelegatorUnbondingDelegations(ctx context.Context, req *QueryDelegatorUnbondingDelegationsRequest) (*QueryDelegatorUnbondingDelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorUnbondingDelegations not implemented") +} +func (*UnimplementedQueryServer) Redelegations(ctx context.Context, req *QueryRedelegationsRequest) (*QueryRedelegationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Redelegations not implemented") +} +func (*UnimplementedQueryServer) DelegatorValidators(ctx context.Context, req *QueryDelegatorValidatorsRequest) (*QueryDelegatorValidatorsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorValidators not implemented") +} +func (*UnimplementedQueryServer) DelegatorValidator(ctx context.Context, req *QueryDelegatorValidatorRequest) (*QueryDelegatorValidatorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelegatorValidator not implemented") +} +func (*UnimplementedQueryServer) HistoricalInfo(ctx context.Context, req *QueryHistoricalInfoRequest) (*QueryHistoricalInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HistoricalInfo not implemented") +} +func (*UnimplementedQueryServer) Pool(ctx context.Context, req *QueryPoolRequest) (*QueryPoolResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Pool not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Validators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Validators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Validators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Validators(ctx, req.(*QueryValidatorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Validator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Validator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Validator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Validator(ctx, req.(*QueryValidatorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/ValidatorDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorDelegations(ctx, req.(*QueryValidatorDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorUnbondingDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorUnbondingDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorUnbondingDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/ValidatorUnbondingDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorUnbondingDelegations(ctx, req.(*QueryValidatorUnbondingDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Delegation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Delegation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Delegation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Delegation(ctx, req.(*QueryDelegationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_UnbondingDelegation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUnbondingDelegationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UnbondingDelegation(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/UnbondingDelegation", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UnbondingDelegation(ctx, req.(*QueryUnbondingDelegationRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/DelegatorDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorDelegations(ctx, req.(*QueryDelegatorDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorUnbondingDelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorUnbondingDelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorUnbondingDelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorUnbondingDelegations(ctx, req.(*QueryDelegatorUnbondingDelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Redelegations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryRedelegationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Redelegations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Redelegations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Redelegations(ctx, req.(*QueryRedelegationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorValidators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorValidatorsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorValidators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/DelegatorValidators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorValidators(ctx, req.(*QueryDelegatorValidatorsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_DelegatorValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryDelegatorValidatorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).DelegatorValidator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/DelegatorValidator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).DelegatorValidator(ctx, req.(*QueryDelegatorValidatorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_HistoricalInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryHistoricalInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).HistoricalInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/HistoricalInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).HistoricalInfo(ctx, req.(*QueryHistoricalInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Pool_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryPoolRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Pool(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Pool", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Pool(ctx, req.(*QueryPoolRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.staking.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Validators", + Handler: _Query_Validators_Handler, + }, + { + MethodName: "Validator", + Handler: _Query_Validator_Handler, + }, + { + MethodName: "ValidatorDelegations", + Handler: _Query_ValidatorDelegations_Handler, + }, + { + MethodName: "ValidatorUnbondingDelegations", + Handler: _Query_ValidatorUnbondingDelegations_Handler, + }, + { + MethodName: "Delegation", + Handler: _Query_Delegation_Handler, + }, + { + MethodName: "UnbondingDelegation", + Handler: _Query_UnbondingDelegation_Handler, + }, + { + MethodName: "DelegatorDelegations", + Handler: _Query_DelegatorDelegations_Handler, + }, + { + MethodName: "DelegatorUnbondingDelegations", + Handler: _Query_DelegatorUnbondingDelegations_Handler, + }, + { + MethodName: "Redelegations", + Handler: _Query_Redelegations_Handler, + }, + { + MethodName: "DelegatorValidators", + Handler: _Query_DelegatorValidators_Handler, + }, + { + MethodName: "DelegatorValidator", + Handler: _Query_DelegatorValidator_Handler, + }, + { + MethodName: "HistoricalInfo", + Handler: _Query_HistoricalInfo_Handler, + }, + { + MethodName: "Pool", + Handler: _Query_Pool_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/staking/v1beta1/query.proto", +} + +func (m *QueryValidatorsRequest) 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 *QueryValidatorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorsRequest) 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.Status) > 0 { + i -= len(m.Status) + copy(dAtA[i:], m.Status) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Status))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorsResponse) 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 *QueryValidatorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorsResponse) 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.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[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 *QueryValidatorRequest) 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 *QueryValidatorRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorResponse) 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 *QueryValidatorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Validator.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 *QueryValidatorDelegationsRequest) 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 *QueryValidatorDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorDelegationsRequest) 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.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorDelegationsResponse) 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 *QueryValidatorDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorDelegationsResponse) 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.DelegationResponses) > 0 { + for iNdEx := len(m.DelegationResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegationResponses[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 *QueryValidatorUnbondingDelegationsRequest) 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 *QueryValidatorUnbondingDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorUnbondingDelegationsRequest) 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.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorUnbondingDelegationsResponse) 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 *QueryValidatorUnbondingDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorUnbondingDelegationsResponse) 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.UnbondingResponses) > 0 { + for iNdEx := len(m.UnbondingResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UnbondingResponses[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 *QueryDelegationRequest) 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 *QueryDelegationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegationResponse) 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 *QueryDelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.DelegationResponse != nil { + { + size, err := m.DelegationResponse.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 *QueryUnbondingDelegationRequest) 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 *QueryUnbondingDelegationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnbondingDelegationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryUnbondingDelegationResponse) 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 *QueryUnbondingDelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUnbondingDelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Unbond.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 *QueryDelegatorDelegationsRequest) 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 *QueryDelegatorDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorDelegationsRequest) 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.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorDelegationsResponse) 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 *QueryDelegatorDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorDelegationsResponse) 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.DelegationResponses) > 0 { + for iNdEx := len(m.DelegationResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DelegationResponses[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 *QueryDelegatorUnbondingDelegationsRequest) 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 *QueryDelegatorUnbondingDelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorUnbondingDelegationsRequest) 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.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorUnbondingDelegationsResponse) 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 *QueryDelegatorUnbondingDelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorUnbondingDelegationsResponse) 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.UnbondingResponses) > 0 { + for iNdEx := len(m.UnbondingResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UnbondingResponses[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 *QueryRedelegationsRequest) 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 *QueryRedelegationsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRedelegationsRequest) 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] = 0x22 + } + if len(m.DstValidatorAddr) > 0 { + i -= len(m.DstValidatorAddr) + copy(dAtA[i:], m.DstValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DstValidatorAddr))) + i-- + dAtA[i] = 0x1a + } + if len(m.SrcValidatorAddr) > 0 { + i -= len(m.SrcValidatorAddr) + copy(dAtA[i:], m.SrcValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.SrcValidatorAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryRedelegationsResponse) 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 *QueryRedelegationsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRedelegationsResponse) 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.RedelegationResponses) > 0 { + for iNdEx := len(m.RedelegationResponses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RedelegationResponses[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 *QueryDelegatorValidatorsRequest) 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 *QueryDelegatorValidatorsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorsRequest) 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.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorValidatorsResponse) 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 *QueryDelegatorValidatorsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorsResponse) 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.Validators) > 0 { + for iNdEx := len(m.Validators) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Validators[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 *QueryDelegatorValidatorRequest) 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 *QueryDelegatorValidatorRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddr) > 0 { + i -= len(m.ValidatorAddr) + copy(dAtA[i:], m.ValidatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ValidatorAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddr) > 0 { + i -= len(m.DelegatorAddr) + copy(dAtA[i:], m.DelegatorAddr) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DelegatorAddr))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryDelegatorValidatorResponse) 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 *QueryDelegatorValidatorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryDelegatorValidatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Validator.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 *QueryHistoricalInfoRequest) 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 *QueryHistoricalInfoRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryHistoricalInfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryHistoricalInfoResponse) 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 *QueryHistoricalInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryHistoricalInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Hist != nil { + { + size, err := m.Hist.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 *QueryPoolRequest) 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 *QueryPoolRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPoolRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryPoolResponse) 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 *QueryPoolResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryPoolResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Pool.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 *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.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 *QueryValidatorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Status) + 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 *QueryValidatorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Validators) > 0 { + for _, e := range m.Validators { + 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 *QueryValidatorRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Validator.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryValidatorDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddr) + 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 *QueryValidatorDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.DelegationResponses) > 0 { + for _, e := range m.DelegationResponses { + 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 *QueryValidatorUnbondingDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ValidatorAddr) + 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 *QueryValidatorUnbondingDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.UnbondingResponses) > 0 { + for _, e := range m.UnbondingResponses { + 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 *QueryDelegationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ValidatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DelegationResponse != nil { + l = m.DelegationResponse.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryUnbondingDelegationRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ValidatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryUnbondingDelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Unbond.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryDelegatorDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + 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 *QueryDelegatorDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.DelegationResponses) > 0 { + for _, e := range m.DelegationResponses { + 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 *QueryDelegatorUnbondingDelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + 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 *QueryDelegatorUnbondingDelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.UnbondingResponses) > 0 { + for _, e := range m.UnbondingResponses { + 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 *QueryRedelegationsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.SrcValidatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.DstValidatorAddr) + 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 *QueryRedelegationsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.RedelegationResponses) > 0 { + for _, e := range m.RedelegationResponses { + 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 *QueryDelegatorValidatorsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + 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 *QueryDelegatorValidatorsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Validators) > 0 { + for _, e := range m.Validators { + 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 *QueryDelegatorValidatorRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ValidatorAddr) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryDelegatorValidatorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Validator.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryHistoricalInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryHistoricalInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Hist != nil { + l = m.Hist.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryPoolRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryPoolResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Pool.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.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 *QueryValidatorsRequest) 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: QueryValidatorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", 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.Status = 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 *QueryValidatorsResponse) 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: QueryValidatorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", 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.Validators = append(m.Validators, Validator{}) + if err := m.Validators[len(m.Validators)-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 *QueryValidatorRequest) 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: QueryValidatorRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryValidatorResponse) 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: QueryValidatorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validator", 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 err := m.Validator.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 *QueryValidatorDelegationsRequest) 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: QueryValidatorDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryValidatorDelegationsResponse) 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: QueryValidatorDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegationResponses", 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.DelegationResponses = append(m.DelegationResponses, DelegationResponse{}) + if err := m.DelegationResponses[len(m.DelegationResponses)-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 *QueryValidatorUnbondingDelegationsRequest) 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: QueryValidatorUnbondingDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorUnbondingDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryValidatorUnbondingDelegationsResponse) 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: QueryValidatorUnbondingDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorUnbondingDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingResponses", 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.UnbondingResponses = append(m.UnbondingResponses, UnbondingDelegation{}) + if err := m.UnbondingResponses[len(m.UnbondingResponses)-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 *QueryDelegationRequest) 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: QueryDelegationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryDelegationResponse) 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: QueryDelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegationResponse", 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.DelegationResponse == nil { + m.DelegationResponse = &DelegationResponse{} + } + if err := m.DelegationResponse.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 *QueryUnbondingDelegationRequest) 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: QueryUnbondingDelegationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnbondingDelegationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryUnbondingDelegationResponse) 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: QueryUnbondingDelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUnbondingDelegationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Unbond", 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 err := m.Unbond.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 *QueryDelegatorDelegationsRequest) 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: QueryDelegatorDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = 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 *QueryDelegatorDelegationsResponse) 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: QueryDelegatorDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegationResponses", 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.DelegationResponses = append(m.DelegationResponses, DelegationResponse{}) + if err := m.DelegationResponses[len(m.DelegationResponses)-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 *QueryDelegatorUnbondingDelegationsRequest) 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: QueryDelegatorUnbondingDelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorUnbondingDelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = 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 *QueryDelegatorUnbondingDelegationsResponse) 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: QueryDelegatorUnbondingDelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorUnbondingDelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingResponses", 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.UnbondingResponses = append(m.UnbondingResponses, UnbondingDelegation{}) + if err := m.UnbondingResponses[len(m.UnbondingResponses)-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 *QueryRedelegationsRequest) 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: QueryRedelegationsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRedelegationsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SrcValidatorAddr", 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.SrcValidatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DstValidatorAddr", 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.DstValidatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + 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 *QueryRedelegationsResponse) 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: QueryRedelegationsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRedelegationsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RedelegationResponses", 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.RedelegationResponses = append(m.RedelegationResponses, RedelegationResponse{}) + if err := m.RedelegationResponses[len(m.RedelegationResponses)-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 *QueryDelegatorValidatorsRequest) 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: QueryDelegatorValidatorsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = 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 *QueryDelegatorValidatorsResponse) 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: QueryDelegatorValidatorsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validators", 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.Validators = append(m.Validators, Validator{}) + if err := m.Validators[len(m.Validators)-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 *QueryDelegatorValidatorRequest) 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: QueryDelegatorValidatorRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddr", 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.DelegatorAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddr", 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.ValidatorAddr = 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 *QueryDelegatorValidatorResponse) 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: QueryDelegatorValidatorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryDelegatorValidatorResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validator", 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 err := m.Validator.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 *QueryHistoricalInfoRequest) 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: QueryHistoricalInfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryHistoricalInfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryHistoricalInfoResponse) 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: QueryHistoricalInfoResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryHistoricalInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hist", 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.Hist == nil { + m.Hist = &HistoricalInfo{} + } + if err := m.Hist.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 *QueryPoolRequest) 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: QueryPoolRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPoolRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryPoolResponse) 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: QueryPoolResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryPoolResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pool", 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 err := m.Pool.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 *QueryParamsRequest) 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: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) 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: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + 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 Params", 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 err := m.Params.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/staking/types/query.pb.gw.go b/x/staking/types/query.pb.gw.go new file mode 100644 index 000000000000..eecdb3f3168d --- /dev/null +++ b/x/staking/types/query.pb.gw.go @@ -0,0 +1,1542 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/staking/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_Validators_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_Validators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorsRequest + 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_Validators_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Validators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Validators_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorsRequest + 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_Validators_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Validators(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Validator_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + msg, err := client.Validator(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Validator_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + msg, err := server.Validator(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ValidatorDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"validator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ValidatorDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", 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_ValidatorDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ValidatorDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", 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_ValidatorDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ValidatorDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_ValidatorUnbondingDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"validator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_ValidatorUnbondingDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorUnbondingDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", 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_ValidatorUnbondingDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ValidatorUnbondingDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorUnbondingDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorUnbondingDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", 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_ValidatorUnbondingDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ValidatorUnbondingDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Delegation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + msg, err := client.Delegation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Delegation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + msg, err := server.Delegation(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_UnbondingDelegation_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnbondingDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + msg, err := client.UnbondingDelegation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_UnbondingDelegation_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUnbondingDelegationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + msg, err := server.UnbondingDelegation(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_DelegatorDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"delegator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_DelegatorDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DelegatorDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DelegatorDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_DelegatorUnbondingDelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"delegator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_DelegatorUnbondingDelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorUnbondingDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorUnbondingDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DelegatorUnbondingDelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorUnbondingDelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorUnbondingDelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorUnbondingDelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DelegatorUnbondingDelegations(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_Redelegations_0 = &utilities.DoubleArray{Encoding: map[string]int{"delegator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_Redelegations_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRedelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_Redelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Redelegations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Redelegations_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryRedelegationsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_Redelegations_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Redelegations(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_DelegatorValidators_0 = &utilities.DoubleArray{Encoding: map[string]int{"delegator_addr": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_DelegatorValidators_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorValidators_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DelegatorValidators(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorValidators_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", 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_DelegatorValidators_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DelegatorValidators(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_DelegatorValidator_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + msg, err := client.DelegatorValidator(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_DelegatorValidator_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryDelegatorValidatorRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["delegator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "delegator_addr") + } + + protoReq.DelegatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "delegator_addr", err) + } + + val, ok = pathParams["validator_addr"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator_addr") + } + + protoReq.ValidatorAddr, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator_addr", err) + } + + msg, err := server.DelegatorValidator(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_HistoricalInfo_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryHistoricalInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := client.HistoricalInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_HistoricalInfo_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryHistoricalInfoRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + msg, err := server.HistoricalInfo(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Pool_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPoolRequest + var metadata runtime.ServerMetadata + + msg, err := client.Pool(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Pool_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryPoolRequest + var metadata runtime.ServerMetadata + + msg, err := server.Pool(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(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_Validators_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_Validators_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_Validators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Validator_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_Validator_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_Validator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorDelegations_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_ValidatorDelegations_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_ValidatorDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorUnbondingDelegations_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_ValidatorUnbondingDelegations_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_ValidatorUnbondingDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Delegation_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_Delegation_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_Delegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnbondingDelegation_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_UnbondingDelegation_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_UnbondingDelegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorDelegations_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_DelegatorDelegations_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_DelegatorDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorUnbondingDelegations_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_DelegatorUnbondingDelegations_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_DelegatorUnbondingDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Redelegations_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_Redelegations_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_Redelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidators_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_DelegatorValidators_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_DelegatorValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidator_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_DelegatorValidator_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_DelegatorValidator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_HistoricalInfo_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_HistoricalInfo_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_HistoricalInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Pool_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_Pool_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_Pool_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_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_Validators_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_Validators_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_Validators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Validator_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_Validator_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_Validator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorDelegations_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_ValidatorDelegations_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_ValidatorDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorUnbondingDelegations_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_ValidatorUnbondingDelegations_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_ValidatorUnbondingDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Delegation_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_Delegation_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_Delegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UnbondingDelegation_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_UnbondingDelegation_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_UnbondingDelegation_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorDelegations_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_DelegatorDelegations_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_DelegatorDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorUnbondingDelegations_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_DelegatorUnbondingDelegations_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_DelegatorUnbondingDelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Redelegations_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_Redelegations_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_Redelegations_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidators_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_DelegatorValidators_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_DelegatorValidators_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_DelegatorValidator_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_DelegatorValidator_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_DelegatorValidator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_HistoricalInfo_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_HistoricalInfo_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_HistoricalInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Pool_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_Pool_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_Pool_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_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_Params_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_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Validators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Validator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ValidatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_ValidatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Delegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_UnbondingDelegation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"cosmos", "staking", "v1beta1", "validators", "validator_addr", "delegations", "delegator_addr", "unbonding_delegation"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "delegations", "delegator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorUnbondingDelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "unbonding_delegations"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Redelegations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "redelegations"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorValidators_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_DelegatorValidator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"cosmos", "staking", "v1beta1", "delegators", "delegator_addr", "validators", "validator_addr"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_HistoricalInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "staking", "v1beta1", "historical_info", "height"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Pool_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "pool"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "staking", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_Validators_0 = runtime.ForwardResponseMessage + + forward_Query_Validator_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorUnbondingDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_Delegation_0 = runtime.ForwardResponseMessage + + forward_Query_UnbondingDelegation_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorUnbondingDelegations_0 = runtime.ForwardResponseMessage + + forward_Query_Redelegations_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorValidators_0 = runtime.ForwardResponseMessage + + forward_Query_DelegatorValidator_0 = runtime.ForwardResponseMessage + + forward_Query_HistoricalInfo_0 = runtime.ForwardResponseMessage + + forward_Query_Pool_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/staking/types/staking.pb.go b/x/staking/types/staking.pb.go new file mode 100644 index 000000000000..aae4d7b54fbd --- /dev/null +++ b/x/staking/types/staking.pb.go @@ -0,0 +1,6567 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/staking/v1beta1/staking.proto + +package types + +import ( + bytes "bytes" + compress_gzip "compress/gzip" + fmt "fmt" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types2 "github.com/cosmos/cosmos-sdk/types" + _ "github.com/gogo/protobuf/gogoproto" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_protoc_gen_gogo_descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/regen-network/cosmos-proto" + types "github.com/tendermint/tendermint/proto/tendermint/types" + _ "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + io_ioutil "io/ioutil" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + 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 + +// BondStatus is the status of a validator. +type BondStatus int32 + +const ( + // UNSPECIFIED defines an invalid validator status. + Unspecified BondStatus = 0 + // UNBONDED defines a validator that is not bonded. + Unbonded BondStatus = 1 + // UNBONDING defines a validator that is unbonding. + Unbonding BondStatus = 2 + // BONDED defines a validator that is bonded. + Bonded BondStatus = 3 +) + +var BondStatus_name = map[int32]string{ + 0: "BOND_STATUS_UNSPECIFIED", + 1: "BOND_STATUS_UNBONDED", + 2: "BOND_STATUS_UNBONDING", + 3: "BOND_STATUS_BONDED", +} + +var BondStatus_value = map[string]int32{ + "BOND_STATUS_UNSPECIFIED": 0, + "BOND_STATUS_UNBONDED": 1, + "BOND_STATUS_UNBONDING": 2, + "BOND_STATUS_BONDED": 3, +} + +func (x BondStatus) String() string { + return proto.EnumName(BondStatus_name, int32(x)) +} + +func (BondStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{0} +} + +// HistoricalInfo contains header and validator information for a given block. +// It is stored as part of staking module's state, which persists the `n` most +// recent HistoricalInfo +// (`n` is set by the staking module's `historical_entries` parameter). +type HistoricalInfo struct { + Header types.Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header"` + Valset []Validator `protobuf:"bytes,2,rep,name=valset,proto3" json:"valset"` +} + +func (m *HistoricalInfo) Reset() { *m = HistoricalInfo{} } +func (m *HistoricalInfo) String() string { return proto.CompactTextString(m) } +func (*HistoricalInfo) ProtoMessage() {} +func (*HistoricalInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{0} +} +func (m *HistoricalInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HistoricalInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HistoricalInfo.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 *HistoricalInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_HistoricalInfo.Merge(m, src) +} +func (m *HistoricalInfo) XXX_Size() int { + return m.Size() +} +func (m *HistoricalInfo) XXX_DiscardUnknown() { + xxx_messageInfo_HistoricalInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_HistoricalInfo proto.InternalMessageInfo + +func (m *HistoricalInfo) GetHeader() types.Header { + if m != nil { + return m.Header + } + return types.Header{} +} + +func (m *HistoricalInfo) GetValset() []Validator { + if m != nil { + return m.Valset + } + return nil +} + +// CommissionRates defines the initial commission rates to be used for creating +// a validator. +type CommissionRates struct { + // 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"` +} + +func (m *CommissionRates) Reset() { *m = CommissionRates{} } +func (*CommissionRates) ProtoMessage() {} +func (*CommissionRates) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{1} +} +func (m *CommissionRates) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommissionRates) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommissionRates.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 *CommissionRates) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommissionRates.Merge(m, src) +} +func (m *CommissionRates) XXX_Size() int { + return m.Size() +} +func (m *CommissionRates) XXX_DiscardUnknown() { + xxx_messageInfo_CommissionRates.DiscardUnknown(m) +} + +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"` + // 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{} } +func (*Commission) ProtoMessage() {} +func (*Commission) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{2} +} +func (m *Commission) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Commission) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Commission.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 *Commission) XXX_Merge(src proto.Message) { + xxx_messageInfo_Commission.Merge(m, src) +} +func (m *Commission) XXX_Size() int { + return m.Size() +} +func (m *Commission) XXX_DiscardUnknown() { + xxx_messageInfo_Commission.DiscardUnknown(m) +} + +var xxx_messageInfo_Commission proto.InternalMessageInfo + +func (m *Commission) GetUpdateTime() time.Time { + if m != nil { + return m.UpdateTime + } + return time.Time{} +} + +// Description defines a validator description. +type Description struct { + // 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 define other optional details. + Details string `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` +} + +func (m *Description) Reset() { *m = Description{} } +func (*Description) ProtoMessage() {} +func (*Description) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{3} +} +func (m *Description) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Description) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Description.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 *Description) XXX_Merge(src proto.Message) { + xxx_messageInfo_Description.Merge(m, src) +} +func (m *Description) XXX_Size() int { + return m.Size() +} +func (m *Description) XXX_DiscardUnknown() { + xxx_messageInfo_Description.DiscardUnknown(m) +} + +var xxx_messageInfo_Description proto.InternalMessageInfo + +func (m *Description) GetMoniker() string { + if m != nil { + return m.Moniker + } + return "" +} + +func (m *Description) GetIdentity() string { + if m != nil { + return m.Identity + } + return "" +} + +func (m *Description) GetWebsite() string { + if m != nil { + return m.Website + } + return "" +} + +func (m *Description) GetSecurityContact() string { + if m != nil { + return m.SecurityContact + } + return "" +} + +func (m *Description) GetDetails() string { + if m != nil { + return m.Details + } + return "" +} + +// Validator defines a validator, together with the total amount of the +// Validator's bond shares and their exchange rate to coins. Slashing results in +// a decrease in the exchange rate, allowing correct calculation of future +// undelegations without iterating over delegators. When coins are delegated to +// this validator, the validator is credited with a delegation whose number of +// bond shares is based on the amount of coins delegated divided by the current +// exchange rate. Voting power can be calculated as total bonded shares +// multiplied by exchange rate. +type Validator struct { + // 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"` +} + +func (m *Validator) Reset() { *m = Validator{} } +func (*Validator) ProtoMessage() {} +func (*Validator) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{4} +} +func (m *Validator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Validator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Validator.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 *Validator) XXX_Merge(src proto.Message) { + xxx_messageInfo_Validator.Merge(m, src) +} +func (m *Validator) XXX_Size() int { + return m.Size() +} +func (m *Validator) XXX_DiscardUnknown() { + xxx_messageInfo_Validator.DiscardUnknown(m) +} + +var xxx_messageInfo_Validator proto.InternalMessageInfo + +// ValAddresses defines a repeated set of validator addresses. +type ValAddresses struct { + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` +} + +func (m *ValAddresses) Reset() { *m = ValAddresses{} } +func (*ValAddresses) ProtoMessage() {} +func (*ValAddresses) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{5} +} +func (m *ValAddresses) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ValAddresses) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ValAddresses.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 *ValAddresses) XXX_Merge(src proto.Message) { + xxx_messageInfo_ValAddresses.Merge(m, src) +} +func (m *ValAddresses) XXX_Size() int { + return m.Size() +} +func (m *ValAddresses) XXX_DiscardUnknown() { + xxx_messageInfo_ValAddresses.DiscardUnknown(m) +} + +var xxx_messageInfo_ValAddresses proto.InternalMessageInfo + +func (m *ValAddresses) GetAddresses() []string { + if m != nil { + return m.Addresses + } + return nil +} + +// DVPair is struct that just has a delegator-validator pair with no other data. +// It is intended to be used as a marshalable pointer. For example, a DVPair can +// be used to construct the key to getting an UnbondingDelegation from state. +type DVPair 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"` +} + +func (m *DVPair) Reset() { *m = DVPair{} } +func (*DVPair) ProtoMessage() {} +func (*DVPair) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{6} +} +func (m *DVPair) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DVPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DVPair.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 *DVPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_DVPair.Merge(m, src) +} +func (m *DVPair) XXX_Size() int { + return m.Size() +} +func (m *DVPair) XXX_DiscardUnknown() { + xxx_messageInfo_DVPair.DiscardUnknown(m) +} + +var xxx_messageInfo_DVPair proto.InternalMessageInfo + +// DVPairs defines an array of DVPair objects. +type DVPairs struct { + Pairs []DVPair `protobuf:"bytes,1,rep,name=pairs,proto3" json:"pairs"` +} + +func (m *DVPairs) Reset() { *m = DVPairs{} } +func (m *DVPairs) String() string { return proto.CompactTextString(m) } +func (*DVPairs) ProtoMessage() {} +func (*DVPairs) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{7} +} +func (m *DVPairs) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DVPairs) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DVPairs.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 *DVPairs) XXX_Merge(src proto.Message) { + xxx_messageInfo_DVPairs.Merge(m, src) +} +func (m *DVPairs) XXX_Size() int { + return m.Size() +} +func (m *DVPairs) XXX_DiscardUnknown() { + xxx_messageInfo_DVPairs.DiscardUnknown(m) +} + +var xxx_messageInfo_DVPairs proto.InternalMessageInfo + +func (m *DVPairs) GetPairs() []DVPair { + if m != nil { + return m.Pairs + } + return nil +} + +// DVVTriplet is struct that just has a delegator-validator-validator triplet +// with no other data. It is intended to be used as a marshalable pointer. For +// example, a DVVTriplet can be used to construct the key to getting a +// Redelegation from state. +type DVVTriplet 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"` +} + +func (m *DVVTriplet) Reset() { *m = DVVTriplet{} } +func (*DVVTriplet) ProtoMessage() {} +func (*DVVTriplet) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{8} +} +func (m *DVVTriplet) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DVVTriplet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DVVTriplet.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 *DVVTriplet) XXX_Merge(src proto.Message) { + xxx_messageInfo_DVVTriplet.Merge(m, src) +} +func (m *DVVTriplet) XXX_Size() int { + return m.Size() +} +func (m *DVVTriplet) XXX_DiscardUnknown() { + xxx_messageInfo_DVVTriplet.DiscardUnknown(m) +} + +var xxx_messageInfo_DVVTriplet proto.InternalMessageInfo + +// DVVTriplets defines an array of DVVTriplet objects. +type DVVTriplets struct { + Triplets []DVVTriplet `protobuf:"bytes,1,rep,name=triplets,proto3" json:"triplets"` +} + +func (m *DVVTriplets) Reset() { *m = DVVTriplets{} } +func (m *DVVTriplets) String() string { return proto.CompactTextString(m) } +func (*DVVTriplets) ProtoMessage() {} +func (*DVVTriplets) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{9} +} +func (m *DVVTriplets) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DVVTriplets) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DVVTriplets.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 *DVVTriplets) XXX_Merge(src proto.Message) { + xxx_messageInfo_DVVTriplets.Merge(m, src) +} +func (m *DVVTriplets) XXX_Size() int { + return m.Size() +} +func (m *DVVTriplets) XXX_DiscardUnknown() { + xxx_messageInfo_DVVTriplets.DiscardUnknown(m) +} + +var xxx_messageInfo_DVVTriplets proto.InternalMessageInfo + +func (m *DVVTriplets) GetTriplets() []DVVTriplet { + if m != nil { + return m.Triplets + } + return nil +} + +// Delegation represents the bond with tokens held by an account. It is +// owned by one delegator, and is associated with the voting power of one +// validator. +type Delegation struct { + // 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{} } +func (*Delegation) ProtoMessage() {} +func (*Delegation) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{10} +} +func (m *Delegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Delegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Delegation.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 *Delegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Delegation.Merge(m, src) +} +func (m *Delegation) XXX_Size() int { + return m.Size() +} +func (m *Delegation) XXX_DiscardUnknown() { + xxx_messageInfo_Delegation.DiscardUnknown(m) +} + +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 { + // 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{} } +func (*UnbondingDelegation) ProtoMessage() {} +func (*UnbondingDelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{11} +} +func (m *UnbondingDelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UnbondingDelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UnbondingDelegation.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 *UnbondingDelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_UnbondingDelegation.Merge(m, src) +} +func (m *UnbondingDelegation) XXX_Size() int { + return m.Size() +} +func (m *UnbondingDelegation) XXX_DiscardUnknown() { + xxx_messageInfo_UnbondingDelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_UnbondingDelegation proto.InternalMessageInfo + +// UnbondingDelegationEntry defines an unbonding object with relevant metadata. +type UnbondingDelegationEntry struct { + // 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 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{} } +func (*UnbondingDelegationEntry) ProtoMessage() {} +func (*UnbondingDelegationEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{12} +} +func (m *UnbondingDelegationEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UnbondingDelegationEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UnbondingDelegationEntry.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 *UnbondingDelegationEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_UnbondingDelegationEntry.Merge(m, src) +} +func (m *UnbondingDelegationEntry) XXX_Size() int { + return m.Size() +} +func (m *UnbondingDelegationEntry) XXX_DiscardUnknown() { + xxx_messageInfo_UnbondingDelegationEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_UnbondingDelegationEntry proto.InternalMessageInfo + +func (m *UnbondingDelegationEntry) GetCreationHeight() int64 { + if m != nil { + return m.CreationHeight + } + return 0 +} + +func (m *UnbondingDelegationEntry) GetCompletionTime() time.Time { + if m != nil { + return m.CompletionTime + } + return time.Time{} +} + +// RedelegationEntry defines a redelegation object with relevant metadata. +type RedelegationEntry struct { + // 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"` + // 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{} } +func (*RedelegationEntry) ProtoMessage() {} +func (*RedelegationEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{13} +} +func (m *RedelegationEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RedelegationEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RedelegationEntry.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 *RedelegationEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RedelegationEntry.Merge(m, src) +} +func (m *RedelegationEntry) XXX_Size() int { + return m.Size() +} +func (m *RedelegationEntry) XXX_DiscardUnknown() { + xxx_messageInfo_RedelegationEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_RedelegationEntry proto.InternalMessageInfo + +func (m *RedelegationEntry) GetCreationHeight() int64 { + if m != nil { + return m.CreationHeight + } + return 0 +} + +func (m *RedelegationEntry) GetCompletionTime() time.Time { + if m != nil { + return m.CompletionTime + } + return 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 { + // 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{} } +func (*Redelegation) ProtoMessage() {} +func (*Redelegation) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{14} +} +func (m *Redelegation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Redelegation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Redelegation.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 *Redelegation) XXX_Merge(src proto.Message) { + xxx_messageInfo_Redelegation.Merge(m, src) +} +func (m *Redelegation) XXX_Size() int { + return m.Size() +} +func (m *Redelegation) XXX_DiscardUnknown() { + xxx_messageInfo_Redelegation.DiscardUnknown(m) +} + +var xxx_messageInfo_Redelegation proto.InternalMessageInfo + +// Params defines the parameters for the staking module. +type Params struct { + // 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{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{15} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetUnbondingTime() time.Duration { + if m != nil { + return m.UnbondingTime + } + return 0 +} + +func (m *Params) GetMaxValidators() uint32 { + if m != nil { + return m.MaxValidators + } + return 0 +} + +func (m *Params) GetMaxEntries() uint32 { + if m != nil { + return m.MaxEntries + } + return 0 +} + +func (m *Params) GetHistoricalEntries() uint32 { + if m != nil { + return m.HistoricalEntries + } + return 0 +} + +func (m *Params) GetBondDenom() string { + if m != nil { + return m.BondDenom + } + return "" +} + +// DelegationResponse is equivalent to Delegation except that it contains a +// balance in addition to shares which is more suitable for client responses. +type DelegationResponse struct { + Delegation Delegation `protobuf:"bytes,1,opt,name=delegation,proto3" json:"delegation"` + Balance types2.Coin `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance"` +} + +func (m *DelegationResponse) Reset() { *m = DelegationResponse{} } +func (*DelegationResponse) ProtoMessage() {} +func (*DelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{16} +} +func (m *DelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DelegationResponse.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 *DelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DelegationResponse.Merge(m, src) +} +func (m *DelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *DelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DelegationResponse proto.InternalMessageInfo + +func (m *DelegationResponse) GetDelegation() Delegation { + if m != nil { + return m.Delegation + } + return Delegation{} +} + +func (m *DelegationResponse) GetBalance() types2.Coin { + if m != nil { + return m.Balance + } + return types2.Coin{} +} + +// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +// contains a balance in addition to shares which is more suitable for client +// responses. +type RedelegationEntryResponse struct { + RedelegationEntry RedelegationEntry `protobuf:"bytes,1,opt,name=redelegation_entry,json=redelegationEntry,proto3" json:"redelegation_entry"` + 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 *RedelegationEntryResponse) Reset() { *m = RedelegationEntryResponse{} } +func (m *RedelegationEntryResponse) String() string { return proto.CompactTextString(m) } +func (*RedelegationEntryResponse) ProtoMessage() {} +func (*RedelegationEntryResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{17} +} +func (m *RedelegationEntryResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RedelegationEntryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RedelegationEntryResponse.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 *RedelegationEntryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RedelegationEntryResponse.Merge(m, src) +} +func (m *RedelegationEntryResponse) XXX_Size() int { + return m.Size() +} +func (m *RedelegationEntryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RedelegationEntryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RedelegationEntryResponse proto.InternalMessageInfo + +func (m *RedelegationEntryResponse) GetRedelegationEntry() RedelegationEntry { + if m != nil { + return m.RedelegationEntry + } + return RedelegationEntry{} +} + +// RedelegationResponse is equivalent to a Redelegation except that its entries +// contain a balance in addition to shares which is more suitable for client +// responses. +type RedelegationResponse struct { + Redelegation Redelegation `protobuf:"bytes,1,opt,name=redelegation,proto3" json:"redelegation"` + Entries []RedelegationEntryResponse `protobuf:"bytes,2,rep,name=entries,proto3" json:"entries"` +} + +func (m *RedelegationResponse) Reset() { *m = RedelegationResponse{} } +func (m *RedelegationResponse) String() string { return proto.CompactTextString(m) } +func (*RedelegationResponse) ProtoMessage() {} +func (*RedelegationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{18} +} +func (m *RedelegationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RedelegationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RedelegationResponse.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 *RedelegationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RedelegationResponse.Merge(m, src) +} +func (m *RedelegationResponse) XXX_Size() int { + return m.Size() +} +func (m *RedelegationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RedelegationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RedelegationResponse proto.InternalMessageInfo + +func (m *RedelegationResponse) GetRedelegation() Redelegation { + if m != nil { + return m.Redelegation + } + return Redelegation{} +} + +func (m *RedelegationResponse) GetEntries() []RedelegationEntryResponse { + if m != nil { + return m.Entries + } + return nil +} + +// Pool is used for tracking bonded and not-bonded token supply of the bond +// denomination. +type Pool struct { + NotBondedTokens github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=not_bonded_tokens,json=notBondedTokens,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"not_bonded_tokens"` + BondedTokens github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=bonded_tokens,json=bondedTokens,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"bonded_tokens" yaml:"bonded_tokens"` +} + +func (m *Pool) Reset() { *m = Pool{} } +func (m *Pool) String() string { return proto.CompactTextString(m) } +func (*Pool) ProtoMessage() {} +func (*Pool) Descriptor() ([]byte, []int) { + return fileDescriptor_64c30c6cf92913c9, []int{19} +} +func (m *Pool) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Pool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Pool.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 *Pool) XXX_Merge(src proto.Message) { + xxx_messageInfo_Pool.Merge(m, src) +} +func (m *Pool) XXX_Size() int { + return m.Size() +} +func (m *Pool) XXX_DiscardUnknown() { + xxx_messageInfo_Pool.DiscardUnknown(m) +} + +var xxx_messageInfo_Pool proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("cosmos.staking.v1beta1.BondStatus", BondStatus_name, BondStatus_value) + proto.RegisterType((*HistoricalInfo)(nil), "cosmos.staking.v1beta1.HistoricalInfo") + proto.RegisterType((*CommissionRates)(nil), "cosmos.staking.v1beta1.CommissionRates") + proto.RegisterType((*Commission)(nil), "cosmos.staking.v1beta1.Commission") + proto.RegisterType((*Description)(nil), "cosmos.staking.v1beta1.Description") + proto.RegisterType((*Validator)(nil), "cosmos.staking.v1beta1.Validator") + proto.RegisterType((*ValAddresses)(nil), "cosmos.staking.v1beta1.ValAddresses") + proto.RegisterType((*DVPair)(nil), "cosmos.staking.v1beta1.DVPair") + proto.RegisterType((*DVPairs)(nil), "cosmos.staking.v1beta1.DVPairs") + proto.RegisterType((*DVVTriplet)(nil), "cosmos.staking.v1beta1.DVVTriplet") + proto.RegisterType((*DVVTriplets)(nil), "cosmos.staking.v1beta1.DVVTriplets") + proto.RegisterType((*Delegation)(nil), "cosmos.staking.v1beta1.Delegation") + proto.RegisterType((*UnbondingDelegation)(nil), "cosmos.staking.v1beta1.UnbondingDelegation") + proto.RegisterType((*UnbondingDelegationEntry)(nil), "cosmos.staking.v1beta1.UnbondingDelegationEntry") + proto.RegisterType((*RedelegationEntry)(nil), "cosmos.staking.v1beta1.RedelegationEntry") + proto.RegisterType((*Redelegation)(nil), "cosmos.staking.v1beta1.Redelegation") + proto.RegisterType((*Params)(nil), "cosmos.staking.v1beta1.Params") + proto.RegisterType((*DelegationResponse)(nil), "cosmos.staking.v1beta1.DelegationResponse") + proto.RegisterType((*RedelegationEntryResponse)(nil), "cosmos.staking.v1beta1.RedelegationEntryResponse") + proto.RegisterType((*RedelegationResponse)(nil), "cosmos.staking.v1beta1.RedelegationResponse") + proto.RegisterType((*Pool)(nil), "cosmos.staking.v1beta1.Pool") +} + +func init() { + proto.RegisterFile("cosmos/staking/v1beta1/staking.proto", fileDescriptor_64c30c6cf92913c9) +} + +var fileDescriptor_64c30c6cf92913c9 = []byte{ + // 1796 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x4d, 0x6c, 0x23, 0x49, + 0x15, 0x76, 0x3b, 0x5e, 0xc7, 0x7e, 0x4e, 0xe2, 0xa4, 0x26, 0x33, 0xeb, 0x98, 0xc1, 0xed, 0x6d, + 0x56, 0x4b, 0x40, 0xbb, 0x0e, 0x93, 0x45, 0x8b, 0xc8, 0x05, 0xc6, 0x71, 0x86, 0x58, 0xbb, 0x0c, + 0xa1, 0x93, 0x09, 0x12, 0xac, 0xb0, 0xca, 0xdd, 0x15, 0xa7, 0x89, 0xbb, 0xdb, 0x74, 0x95, 0x87, + 0x58, 0xda, 0x03, 0xc7, 0x65, 0x10, 0x62, 0xb9, 0xed, 0x65, 0xa4, 0x91, 0xf6, 0xba, 0x12, 0x17, + 0xc4, 0x95, 0xeb, 0x02, 0x97, 0xe1, 0x86, 0x10, 0x32, 0x68, 0xe6, 0x82, 0x38, 0x21, 0x1f, 0x10, + 0x37, 0x50, 0xfd, 0xf4, 0x4f, 0xda, 0xf1, 0xcc, 0x78, 0xb4, 0x87, 0x91, 0xd8, 0x4b, 0xe2, 0x7a, + 0xf5, 0xde, 0xf7, 0xea, 0xfd, 0xd6, 0xab, 0x86, 0x57, 0x2d, 0x9f, 0xba, 0x3e, 0xdd, 0xa2, 0x0c, + 0x9f, 0x39, 0x5e, 0x6f, 0xeb, 0xee, 0x8d, 0x2e, 0x61, 0xf8, 0x46, 0xb8, 0x6e, 0x0c, 0x02, 0x9f, + 0xf9, 0xe8, 0x9a, 0xe4, 0x6a, 0x84, 0x54, 0xc5, 0x55, 0x5d, 0xef, 0xf9, 0x3d, 0x5f, 0xb0, 0x6c, + 0xf1, 0x5f, 0x92, 0xbb, 0xba, 0xd1, 0xf3, 0xfd, 0x5e, 0x9f, 0x6c, 0x89, 0x55, 0x77, 0x78, 0xb2, + 0x85, 0xbd, 0x91, 0xda, 0xaa, 0xa5, 0xb7, 0xec, 0x61, 0x80, 0x99, 0xe3, 0x7b, 0x6a, 0x5f, 0x4f, + 0xef, 0x33, 0xc7, 0x25, 0x94, 0x61, 0x77, 0x10, 0x62, 0xcb, 0x93, 0x74, 0xa4, 0x52, 0x75, 0x2c, + 0x85, 0xad, 0x4c, 0xe9, 0x62, 0x4a, 0x22, 0x3b, 0x2c, 0xdf, 0x09, 0xb1, 0xaf, 0x33, 0xe2, 0xd9, + 0x24, 0x70, 0x1d, 0x8f, 0x6d, 0xb1, 0xd1, 0x80, 0x50, 0xf9, 0x57, 0xee, 0x1a, 0x3f, 0xd3, 0x60, + 0x65, 0xdf, 0xa1, 0xcc, 0x0f, 0x1c, 0x0b, 0xf7, 0xdb, 0xde, 0x89, 0x8f, 0xde, 0x82, 0xfc, 0x29, + 0xc1, 0x36, 0x09, 0x2a, 0x5a, 0x5d, 0xdb, 0x2c, 0x6d, 0x57, 0x1a, 0x31, 0x42, 0x43, 0xca, 0xee, + 0x8b, 0xfd, 0x66, 0xee, 0x93, 0xb1, 0x9e, 0x31, 0x15, 0x37, 0xfa, 0x06, 0xe4, 0xef, 0xe2, 0x3e, + 0x25, 0xac, 0x92, 0xad, 0x2f, 0x6c, 0x96, 0xb6, 0x5f, 0x69, 0x5c, 0xee, 0xbe, 0xc6, 0x31, 0xee, + 0x3b, 0x36, 0x66, 0x7e, 0x04, 0x20, 0xc5, 0x8c, 0x5f, 0x67, 0xa1, 0xbc, 0xeb, 0xbb, 0xae, 0x43, + 0xa9, 0xe3, 0x7b, 0x26, 0x66, 0x84, 0xa2, 0x26, 0xe4, 0x02, 0xcc, 0x88, 0x38, 0x4a, 0xb1, 0xd9, + 0xe0, 0xfc, 0x7f, 0x19, 0xeb, 0xaf, 0xf5, 0x1c, 0x76, 0x3a, 0xec, 0x36, 0x2c, 0xdf, 0x55, 0xce, + 0x50, 0xff, 0xde, 0xa0, 0xf6, 0x99, 0xb2, 0xaf, 0x45, 0x2c, 0x53, 0xc8, 0xa2, 0x77, 0xa1, 0xe0, + 0xe2, 0xf3, 0x8e, 0xc0, 0xc9, 0x0a, 0x9c, 0x9b, 0xf3, 0xe1, 0x4c, 0xc6, 0x7a, 0x79, 0x84, 0xdd, + 0xfe, 0x8e, 0x11, 0xe2, 0x18, 0xe6, 0xa2, 0x8b, 0xcf, 0xf9, 0x11, 0xd1, 0x00, 0xca, 0x9c, 0x6a, + 0x9d, 0x62, 0xaf, 0x47, 0xa4, 0x92, 0x05, 0xa1, 0x64, 0x7f, 0x6e, 0x25, 0xd7, 0x62, 0x25, 0x09, + 0x38, 0xc3, 0x5c, 0x76, 0xf1, 0xf9, 0xae, 0x20, 0x70, 0x8d, 0x3b, 0x85, 0x0f, 0x1f, 0xe8, 0x99, + 0x7f, 0x3c, 0xd0, 0x35, 0xe3, 0x4f, 0x1a, 0x40, 0xec, 0x31, 0xf4, 0x2e, 0xac, 0x5a, 0xd1, 0x4a, + 0xc8, 0x52, 0x15, 0xc3, 0x2f, 0xce, 0x8a, 0x45, 0xca, 0xdf, 0xcd, 0x02, 0x3f, 0xf4, 0xc3, 0xb1, + 0xae, 0x99, 0x65, 0x2b, 0x15, 0x8a, 0x1f, 0x40, 0x69, 0x38, 0xb0, 0x31, 0x23, 0x1d, 0x9e, 0x9d, + 0xc2, 0x93, 0xa5, 0xed, 0x6a, 0x43, 0xa6, 0x6e, 0x23, 0x4c, 0xdd, 0xc6, 0x51, 0x98, 0xba, 0xcd, + 0x1a, 0xc7, 0x9a, 0x8c, 0x75, 0x24, 0xcd, 0x4a, 0x08, 0x1b, 0x1f, 0xfc, 0x4d, 0xd7, 0x4c, 0x90, + 0x14, 0x2e, 0x90, 0xb0, 0xe9, 0xf7, 0x1a, 0x94, 0x5a, 0x84, 0x5a, 0x81, 0x33, 0xe0, 0x15, 0x82, + 0x2a, 0xb0, 0xe8, 0xfa, 0x9e, 0x73, 0xa6, 0xf2, 0xb1, 0x68, 0x86, 0x4b, 0x54, 0x85, 0x82, 0x63, + 0x13, 0x8f, 0x39, 0x6c, 0x24, 0xe3, 0x6a, 0x46, 0x6b, 0x2e, 0xf5, 0x13, 0xd2, 0xa5, 0x4e, 0x18, + 0x0d, 0x33, 0x5c, 0xa2, 0x5b, 0xb0, 0x4a, 0x89, 0x35, 0x0c, 0x1c, 0x36, 0xea, 0x58, 0xbe, 0xc7, + 0xb0, 0xc5, 0x2a, 0x39, 0x11, 0xb0, 0xcf, 0x4d, 0xc6, 0xfa, 0xcb, 0xf2, 0xac, 0x69, 0x0e, 0xc3, + 0x2c, 0x87, 0xa4, 0x5d, 0x49, 0xe1, 0x1a, 0x6c, 0xc2, 0xb0, 0xd3, 0xa7, 0x95, 0x97, 0xa4, 0x06, + 0xb5, 0x4c, 0xd8, 0xf2, 0xf1, 0x22, 0x14, 0xa3, 0x6c, 0xe7, 0x9a, 0xfd, 0x01, 0x09, 0xf8, 0xef, + 0x0e, 0xb6, 0xed, 0x80, 0x50, 0xaa, 0xf2, 0x3a, 0xa1, 0x39, 0xcd, 0x61, 0x98, 0xe5, 0x90, 0x74, + 0x53, 0x52, 0x10, 0xe3, 0x61, 0xf6, 0x28, 0xf1, 0xe8, 0x90, 0x76, 0x06, 0xc3, 0xee, 0x19, 0x19, + 0xa9, 0x68, 0xac, 0x4f, 0x45, 0xe3, 0xa6, 0x37, 0x6a, 0xbe, 0x19, 0xa3, 0xa7, 0xe5, 0x8c, 0x3f, + 0xfc, 0xe6, 0x8d, 0x75, 0x95, 0x1a, 0x56, 0x30, 0x1a, 0x30, 0xbf, 0x71, 0x30, 0xec, 0xbe, 0x4d, + 0x46, 0x3c, 0xfc, 0x8a, 0xf5, 0x40, 0x70, 0xa2, 0x6b, 0x90, 0xff, 0x11, 0x76, 0xfa, 0xc4, 0x16, + 0x0e, 0x2d, 0x98, 0x6a, 0x85, 0x76, 0x20, 0x4f, 0x19, 0x66, 0x43, 0x2a, 0xbc, 0xb8, 0xb2, 0x6d, + 0xcc, 0x4a, 0xb5, 0xa6, 0xef, 0xd9, 0x87, 0x82, 0xd3, 0x54, 0x12, 0xe8, 0x16, 0xe4, 0x99, 0x7f, + 0x46, 0x3c, 0xe5, 0xc2, 0xb9, 0xea, 0xbb, 0xed, 0x31, 0x53, 0x49, 0x73, 0x8f, 0xd8, 0xa4, 0x4f, + 0x7a, 0xc2, 0x71, 0xf4, 0x14, 0x07, 0x84, 0x56, 0xf2, 0x02, 0xb1, 0x3d, 0x77, 0x11, 0x2a, 0x4f, + 0xa5, 0xf1, 0x0c, 0xb3, 0x1c, 0x91, 0x0e, 0x05, 0x05, 0xbd, 0x0d, 0x25, 0x3b, 0x4e, 0xd4, 0xca, + 0xa2, 0x08, 0xc1, 0x17, 0x66, 0x99, 0x9f, 0xc8, 0x69, 0xd5, 0xf7, 0x92, 0xd2, 0x3c, 0x39, 0x86, + 0x5e, 0xd7, 0xf7, 0x6c, 0xc7, 0xeb, 0x75, 0x4e, 0x89, 0xd3, 0x3b, 0x65, 0x95, 0x42, 0x5d, 0xdb, + 0x5c, 0x48, 0x26, 0x47, 0x9a, 0xc3, 0x30, 0xcb, 0x11, 0x69, 0x5f, 0x50, 0x90, 0x0d, 0x2b, 0x31, + 0x97, 0x28, 0xd4, 0xe2, 0x53, 0x0b, 0xf5, 0x15, 0x55, 0xa8, 0x57, 0xd3, 0x5a, 0xe2, 0x5a, 0x5d, + 0x8e, 0x88, 0x5c, 0x0c, 0xed, 0x03, 0xc4, 0xed, 0xa1, 0x02, 0x42, 0x83, 0xf1, 0xf4, 0x1e, 0xa3, + 0x0c, 0x4f, 0xc8, 0xa2, 0xf7, 0xe0, 0x8a, 0xeb, 0x78, 0x1d, 0x4a, 0xfa, 0x27, 0x1d, 0xe5, 0x60, + 0x0e, 0x59, 0x12, 0xd1, 0x7b, 0x67, 0xbe, 0x7c, 0x98, 0x8c, 0xf5, 0xaa, 0x6a, 0xa1, 0xd3, 0x90, + 0x86, 0xb9, 0xe6, 0x3a, 0xde, 0x21, 0xe9, 0x9f, 0xb4, 0x22, 0xda, 0xce, 0xd2, 0xfb, 0x0f, 0xf4, + 0x8c, 0x2a, 0xd7, 0x8c, 0xf1, 0x16, 0x2c, 0x1d, 0xe3, 0xbe, 0x2a, 0x33, 0x42, 0xd1, 0x75, 0x28, + 0xe2, 0x70, 0x51, 0xd1, 0xea, 0x0b, 0x9b, 0x45, 0x33, 0x26, 0xc8, 0x32, 0xff, 0xe9, 0x5f, 0xeb, + 0x9a, 0xf1, 0xb1, 0x06, 0xf9, 0xd6, 0xf1, 0x01, 0x76, 0x02, 0xd4, 0x86, 0xb5, 0x38, 0x73, 0x2e, + 0x16, 0xf9, 0xf5, 0xc9, 0x58, 0xaf, 0xa4, 0x93, 0x2b, 0xaa, 0xf2, 0x38, 0x81, 0xc3, 0x32, 0x6f, + 0xc3, 0xda, 0xdd, 0xb0, 0x77, 0x44, 0x50, 0xd9, 0x34, 0xd4, 0x14, 0x8b, 0x61, 0xae, 0x46, 0x34, + 0x05, 0x95, 0x32, 0x73, 0x0f, 0x16, 0xe5, 0x69, 0x29, 0xda, 0x81, 0x97, 0x06, 0xfc, 0x87, 0xb0, + 0xae, 0xb4, 0x5d, 0x9b, 0x99, 0xbc, 0x82, 0x5f, 0x85, 0x4f, 0x8a, 0x18, 0xbf, 0xca, 0x02, 0xb4, + 0x8e, 0x8f, 0x8f, 0x02, 0x67, 0xd0, 0x27, 0xec, 0xd3, 0xb4, 0xfc, 0x08, 0xae, 0xc6, 0x66, 0xd1, + 0xc0, 0x4a, 0x59, 0x5f, 0x9f, 0x8c, 0xf5, 0xeb, 0x69, 0xeb, 0x13, 0x6c, 0x86, 0x79, 0x25, 0xa2, + 0x1f, 0x06, 0xd6, 0xa5, 0xa8, 0x36, 0x65, 0x11, 0xea, 0xc2, 0x6c, 0xd4, 0x04, 0x5b, 0x12, 0xb5, + 0x45, 0xd9, 0xe5, 0xae, 0x3d, 0x84, 0x52, 0xec, 0x12, 0x8a, 0x5a, 0x50, 0x60, 0xea, 0xb7, 0xf2, + 0xb0, 0x31, 0xdb, 0xc3, 0xa1, 0x98, 0xf2, 0x72, 0x24, 0x69, 0xfc, 0x47, 0x03, 0x88, 0x73, 0xf6, + 0xc5, 0x4c, 0x31, 0xde, 0xca, 0x55, 0xe3, 0x5d, 0x78, 0xae, 0x51, 0x4d, 0x49, 0xa7, 0xfc, 0xf9, + 0xf3, 0x2c, 0x5c, 0xb9, 0x13, 0x76, 0x9e, 0x17, 0xde, 0x07, 0x07, 0xb0, 0x48, 0x3c, 0x16, 0x38, + 0xc2, 0x09, 0x3c, 0xda, 0x5f, 0x99, 0x15, 0xed, 0x4b, 0x6c, 0xda, 0xf3, 0x58, 0x30, 0x52, 0xb1, + 0x0f, 0x61, 0x52, 0xde, 0xf8, 0xe5, 0x02, 0x54, 0x66, 0x49, 0xa2, 0x5d, 0x28, 0x5b, 0x01, 0x11, + 0x84, 0xf0, 0xfe, 0xd0, 0xc4, 0xfd, 0x51, 0x8d, 0x27, 0xcb, 0x14, 0x83, 0x61, 0xae, 0x84, 0x14, + 0x75, 0x7b, 0xf4, 0x80, 0x8f, 0x7d, 0x3c, 0xed, 0x38, 0xd7, 0x33, 0xce, 0x79, 0x86, 0xba, 0x3e, + 0x42, 0x25, 0x17, 0x01, 0xe4, 0xfd, 0xb1, 0x12, 0x53, 0xc5, 0x05, 0xf2, 0x63, 0x28, 0x3b, 0x9e, + 0xc3, 0x1c, 0xdc, 0xef, 0x74, 0x71, 0x1f, 0x7b, 0xd6, 0xf3, 0x4c, 0xcd, 0xb2, 0xe5, 0x2b, 0xb5, + 0x29, 0x38, 0xc3, 0x5c, 0x51, 0x94, 0xa6, 0x24, 0xa0, 0x7d, 0x58, 0x0c, 0x55, 0xe5, 0x9e, 0x6b, + 0xda, 0x08, 0xc5, 0x13, 0x03, 0xde, 0x2f, 0x16, 0x60, 0xcd, 0x24, 0xf6, 0x67, 0xa1, 0x98, 0x2f, + 0x14, 0xdf, 0x06, 0x90, 0xe5, 0xce, 0x1b, 0xec, 0x73, 0x44, 0x83, 0x37, 0x8c, 0xa2, 0x44, 0x68, + 0x51, 0x96, 0x88, 0xc7, 0x38, 0x0b, 0x4b, 0xc9, 0x78, 0xfc, 0x9f, 0xde, 0x4a, 0xa8, 0x1d, 0x77, + 0xa2, 0x9c, 0xe8, 0x44, 0x5f, 0x9a, 0xd5, 0x89, 0xa6, 0xb2, 0xf7, 0xc9, 0x2d, 0xe8, 0xdf, 0x59, + 0xc8, 0x1f, 0xe0, 0x00, 0xbb, 0x14, 0x59, 0x53, 0x93, 0xa6, 0x7c, 0x6b, 0x6e, 0x4c, 0xe5, 0x67, + 0x4b, 0x7d, 0xed, 0x78, 0xca, 0xa0, 0xf9, 0xe1, 0x25, 0x83, 0xe6, 0x37, 0x61, 0x85, 0x3f, 0x87, + 0x23, 0x1b, 0xa5, 0xb7, 0x97, 0x9b, 0x1b, 0x31, 0xca, 0xc5, 0x7d, 0xf9, 0x5a, 0x8e, 0x1e, 0x5d, + 0x14, 0x7d, 0x0d, 0x4a, 0x9c, 0x23, 0x6e, 0xcc, 0x5c, 0xfc, 0x5a, 0xfc, 0x2c, 0x4d, 0x6c, 0x1a, + 0x26, 0xb8, 0xf8, 0x7c, 0x4f, 0x2e, 0xd0, 0x3b, 0x80, 0x4e, 0xa3, 0x2f, 0x23, 0x9d, 0xd8, 0x9d, + 0x5c, 0xfe, 0xf3, 0x93, 0xb1, 0xbe, 0x21, 0xe5, 0xa7, 0x79, 0x0c, 0x73, 0x2d, 0x26, 0x86, 0x68, + 0x5f, 0x05, 0xe0, 0x76, 0x75, 0x6c, 0xe2, 0xf9, 0xae, 0x7a, 0xee, 0x5c, 0x9d, 0x8c, 0xf5, 0x35, + 0x89, 0x12, 0xef, 0x19, 0x66, 0x91, 0x2f, 0x5a, 0xfc, 0x77, 0x22, 0xb3, 0x3f, 0xd2, 0x00, 0xc5, + 0x2d, 0xdf, 0x24, 0x74, 0xc0, 0xdf, 0x67, 0x7c, 0x10, 0x4f, 0x4c, 0xcd, 0xda, 0x93, 0x07, 0xf1, + 0x58, 0x3e, 0x1c, 0xc4, 0x13, 0x95, 0xf2, 0xf5, 0xb8, 0x3d, 0x66, 0x55, 0x1c, 0x15, 0x4c, 0x17, + 0x53, 0x92, 0x18, 0xe6, 0x9d, 0x50, 0x7a, 0xaa, 0x1f, 0x66, 0x8c, 0x3f, 0x6a, 0xb0, 0x31, 0x95, + 0x51, 0xd1, 0x61, 0x7f, 0x08, 0x28, 0x48, 0x6c, 0x0a, 0x7f, 0x8d, 0xd4, 0xa1, 0xe7, 0x4e, 0xd0, + 0xb5, 0x60, 0xaa, 0xef, 0x7e, 0x7a, 0x1d, 0x3e, 0x27, 0x7c, 0xfe, 0x3b, 0x0d, 0xd6, 0x93, 0xea, + 0x23, 0x43, 0x6e, 0xc3, 0x52, 0x52, 0xbb, 0x32, 0xe1, 0xd5, 0x67, 0x31, 0x41, 0x9d, 0xfe, 0x82, + 0x3c, 0xfa, 0x6e, 0x5c, 0xae, 0xf2, 0xdb, 0xd9, 0x8d, 0x67, 0xf6, 0x46, 0x78, 0xa6, 0x74, 0xd9, + 0xe6, 0x44, 0x3c, 0xfe, 0xab, 0x41, 0xee, 0xc0, 0xf7, 0xfb, 0xc8, 0x87, 0x35, 0xcf, 0x67, 0x1d, + 0x9e, 0x59, 0xc4, 0xee, 0xa8, 0x47, 0xb7, 0xec, 0x83, 0xbb, 0xf3, 0x39, 0xe9, 0x9f, 0x63, 0x7d, + 0x1a, 0xca, 0x2c, 0x7b, 0x3e, 0x6b, 0x0a, 0xca, 0x91, 0x7c, 0x92, 0xbf, 0x07, 0xcb, 0x17, 0x95, + 0xc9, 0x2e, 0xf9, 0xbd, 0xb9, 0x95, 0x5d, 0x84, 0x99, 0x8c, 0xf5, 0xf5, 0xb8, 0x62, 0x22, 0xb2, + 0x61, 0x2e, 0x75, 0x13, 0xda, 0x77, 0x0a, 0x3c, 0x7e, 0xff, 0x7a, 0xa0, 0x6b, 0x5f, 0xfe, 0xad, + 0x06, 0x10, 0x7f, 0x79, 0x40, 0xaf, 0xc3, 0xcb, 0xcd, 0xef, 0xdc, 0x6e, 0x75, 0x0e, 0x8f, 0x6e, + 0x1e, 0xdd, 0x39, 0xec, 0xdc, 0xb9, 0x7d, 0x78, 0xb0, 0xb7, 0xdb, 0xbe, 0xd5, 0xde, 0x6b, 0xad, + 0x66, 0xaa, 0xe5, 0x7b, 0xf7, 0xeb, 0xa5, 0x3b, 0x1e, 0x1d, 0x10, 0xcb, 0x39, 0x71, 0x88, 0x8d, + 0x5e, 0x83, 0xf5, 0x8b, 0xdc, 0x7c, 0xb5, 0xd7, 0x5a, 0xd5, 0xaa, 0x4b, 0xf7, 0xee, 0xd7, 0x0b, + 0x72, 0x16, 0x23, 0x36, 0xda, 0x84, 0xab, 0xd3, 0x7c, 0xed, 0xdb, 0xdf, 0x5a, 0xcd, 0x56, 0x97, + 0xef, 0xdd, 0xaf, 0x17, 0xa3, 0xa1, 0x0d, 0x19, 0x80, 0x92, 0x9c, 0x0a, 0x6f, 0xa1, 0x0a, 0xf7, + 0xee, 0xd7, 0xf3, 0xd2, 0x81, 0xd5, 0xdc, 0xfb, 0x1f, 0xd5, 0x32, 0xcd, 0x5b, 0x9f, 0x3c, 0xaa, + 0x69, 0x0f, 0x1f, 0xd5, 0xb4, 0xbf, 0x3f, 0xaa, 0x69, 0x1f, 0x3c, 0xae, 0x65, 0x1e, 0x3e, 0xae, + 0x65, 0xfe, 0xfc, 0xb8, 0x96, 0xf9, 0xfe, 0xeb, 0x4f, 0xf4, 0xdd, 0x79, 0xf4, 0x51, 0x5b, 0x78, + 0xb1, 0x9b, 0x17, 0x6d, 0xf8, 0xcd, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xc2, 0x48, 0x4c, 0x86, + 0xf3, 0x16, 0x00, 0x00, +} + +func (this *Pool) Description() (desc *github_com_gogo_protobuf_protoc_gen_gogo_descriptor.FileDescriptorSet) { + return StakingDescription() +} +func StakingDescription() (desc *github_com_gogo_protobuf_protoc_gen_gogo_descriptor.FileDescriptorSet) { + d := &github_com_gogo_protobuf_protoc_gen_gogo_descriptor.FileDescriptorSet{} + var gzipped = []byte{ + // 9591 bytes of a gzipped FileDescriptorSet + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6d, 0x70, 0x24, 0xd7, + 0x71, 0x18, 0x66, 0x77, 0x01, 0xec, 0x36, 0x16, 0xc0, 0xe2, 0x01, 0x77, 0xb7, 0xb7, 0x3c, 0x02, + 0xe0, 0xf0, 0xeb, 0x78, 0x24, 0x71, 0xe4, 0x91, 0x77, 0x24, 0xf7, 0x24, 0xd1, 0x58, 0x60, 0x0f, + 0x07, 0x1e, 0xbe, 0x38, 0x00, 0x8e, 0xd4, 0x87, 0xb3, 0x35, 0x98, 0x7d, 0x58, 0x0c, 0xb1, 0x3b, + 0x33, 0x9c, 0x99, 0xbd, 0x3b, 0x50, 0x52, 0x8a, 0x96, 0x14, 0x45, 0xa2, 0xcb, 0xb1, 0x14, 0xa5, + 0x62, 0x89, 0xd2, 0x29, 0x92, 0xe5, 0x44, 0x8e, 0xac, 0xc4, 0x96, 0xa5, 0x28, 0x71, 0x92, 0xaa, + 0x48, 0xa9, 0x38, 0x96, 0x94, 0x8a, 0x4b, 0xaa, 0xb8, 0x12, 0xc7, 0x95, 0x9c, 0x1d, 0x4a, 0xe5, + 0x30, 0x8a, 0x12, 0xcb, 0x67, 0x39, 0x71, 0x4a, 0x95, 0x4a, 0xea, 0x7d, 0xcd, 0xd7, 0x7e, 0xcc, + 0x2e, 0x74, 0x27, 0xca, 0x71, 0x7e, 0x61, 0x5f, 0x4f, 0x77, 0xbf, 0xee, 0x7e, 0xfd, 0xba, 0xfb, + 0xbd, 0x79, 0x6f, 0x00, 0x5f, 0x38, 0x0f, 0xb3, 0x35, 0xd3, 0xac, 0xd5, 0xf1, 0x69, 0xcb, 0x36, + 0x5d, 0x73, 0xa7, 0xb9, 0x7b, 0xba, 0x8a, 0x1d, 0xcd, 0xd6, 0x2d, 0xd7, 0xb4, 0xe7, 0x28, 0x0c, + 0x8d, 0x33, 0x8c, 0x39, 0x81, 0x21, 0xaf, 0xc2, 0xc4, 0x05, 0xbd, 0x8e, 0x17, 0x3d, 0xc4, 0x4d, + 0xec, 0xa2, 0x27, 0x21, 0xb5, 0xab, 0xd7, 0x71, 0x5e, 0x9a, 0x4d, 0x9e, 0x1c, 0x39, 0x73, 0xcf, + 0x5c, 0x84, 0x68, 0x2e, 0x4c, 0xb1, 0x41, 0xc0, 0x0a, 0xa5, 0x90, 0xbf, 0x93, 0x82, 0xc9, 0x36, + 0x4f, 0x11, 0x82, 0x94, 0xa1, 0x36, 0x08, 0x47, 0xe9, 0x64, 0x46, 0xa1, 0xbf, 0x51, 0x1e, 0x86, + 0x2d, 0x55, 0xdb, 0x57, 0x6b, 0x38, 0x9f, 0xa0, 0x60, 0xd1, 0x44, 0xd3, 0x00, 0x55, 0x6c, 0x61, + 0xa3, 0x8a, 0x0d, 0xed, 0x20, 0x9f, 0x9c, 0x4d, 0x9e, 0xcc, 0x28, 0x01, 0x08, 0x7a, 0x10, 0x26, + 0xac, 0xe6, 0x4e, 0x5d, 0xd7, 0x2a, 0x01, 0x34, 0x98, 0x4d, 0x9e, 0x1c, 0x54, 0x72, 0xec, 0xc1, + 0xa2, 0x8f, 0x7c, 0x3f, 0x8c, 0x5f, 0xc5, 0xea, 0x7e, 0x10, 0x75, 0x84, 0xa2, 0x8e, 0x11, 0x70, + 0x00, 0x71, 0x01, 0xb2, 0x0d, 0xec, 0x38, 0x6a, 0x0d, 0x57, 0xdc, 0x03, 0x0b, 0xe7, 0x53, 0x54, + 0xfb, 0xd9, 0x16, 0xed, 0xa3, 0x9a, 0x8f, 0x70, 0xaa, 0xad, 0x03, 0x0b, 0xa3, 0x79, 0xc8, 0x60, + 0xa3, 0xd9, 0x60, 0x1c, 0x06, 0x3b, 0xd8, 0xaf, 0x6c, 0x34, 0x1b, 0x51, 0x2e, 0x69, 0x42, 0xc6, + 0x59, 0x0c, 0x3b, 0xd8, 0xbe, 0xa2, 0x6b, 0x38, 0x3f, 0x44, 0x19, 0xdc, 0xdf, 0xc2, 0x60, 0x93, + 0x3d, 0x8f, 0xf2, 0x10, 0x74, 0x68, 0x01, 0x32, 0xf8, 0x9a, 0x8b, 0x0d, 0x47, 0x37, 0x8d, 0xfc, + 0x30, 0x65, 0x72, 0x6f, 0x9b, 0x51, 0xc4, 0xf5, 0x6a, 0x94, 0x85, 0x4f, 0x87, 0xce, 0xc1, 0xb0, + 0x69, 0xb9, 0xba, 0x69, 0x38, 0xf9, 0xf4, 0xac, 0x74, 0x72, 0xe4, 0xcc, 0x89, 0xb6, 0x8e, 0xb0, + 0xce, 0x70, 0x14, 0x81, 0x8c, 0x96, 0x21, 0xe7, 0x98, 0x4d, 0x5b, 0xc3, 0x15, 0xcd, 0xac, 0xe2, + 0x8a, 0x6e, 0xec, 0x9a, 0xf9, 0x0c, 0x65, 0x30, 0xd3, 0xaa, 0x08, 0x45, 0x5c, 0x30, 0xab, 0x78, + 0xd9, 0xd8, 0x35, 0x95, 0x31, 0x27, 0xd4, 0x46, 0x47, 0x61, 0xc8, 0x39, 0x30, 0x5c, 0xf5, 0x5a, + 0x3e, 0x4b, 0x3d, 0x84, 0xb7, 0xe4, 0xdf, 0x18, 0x82, 0xf1, 0x5e, 0x5c, 0xec, 0x3c, 0x0c, 0xee, + 0x12, 0x2d, 0xf3, 0x89, 0x7e, 0x6c, 0xc0, 0x68, 0xc2, 0x46, 0x1c, 0x3a, 0xa4, 0x11, 0xe7, 0x61, + 0xc4, 0xc0, 0x8e, 0x8b, 0xab, 0xcc, 0x23, 0x92, 0x3d, 0xfa, 0x14, 0x30, 0xa2, 0x56, 0x97, 0x4a, + 0x1d, 0xca, 0xa5, 0x9e, 0x87, 0x71, 0x4f, 0xa4, 0x8a, 0xad, 0x1a, 0x35, 0xe1, 0x9b, 0xa7, 0xe3, + 0x24, 0x99, 0x2b, 0x0b, 0x3a, 0x85, 0x90, 0x29, 0x63, 0x38, 0xd4, 0x46, 0x8b, 0x00, 0xa6, 0x81, + 0xcd, 0xdd, 0x4a, 0x15, 0x6b, 0xf5, 0x7c, 0xba, 0x83, 0x95, 0xd6, 0x09, 0x4a, 0x8b, 0x95, 0x4c, + 0x06, 0xd5, 0xea, 0xe8, 0x29, 0xdf, 0xd5, 0x86, 0x3b, 0x78, 0xca, 0x2a, 0x9b, 0x64, 0x2d, 0xde, + 0xb6, 0x0d, 0x63, 0x36, 0x26, 0x7e, 0x8f, 0xab, 0x5c, 0xb3, 0x0c, 0x15, 0x62, 0x2e, 0x56, 0x33, + 0x85, 0x93, 0x31, 0xc5, 0x46, 0xed, 0x60, 0x13, 0xdd, 0x0d, 0x1e, 0xa0, 0x42, 0xdd, 0x0a, 0x68, + 0x14, 0xca, 0x0a, 0xe0, 0x9a, 0xda, 0xc0, 0x85, 0x97, 0x60, 0x2c, 0x6c, 0x1e, 0x34, 0x05, 0x83, + 0x8e, 0xab, 0xda, 0x2e, 0xf5, 0xc2, 0x41, 0x85, 0x35, 0x50, 0x0e, 0x92, 0xd8, 0xa8, 0xd2, 0x28, + 0x37, 0xa8, 0x90, 0x9f, 0xe8, 0xa7, 0x7c, 0x85, 0x93, 0x54, 0xe1, 0xfb, 0x5a, 0x47, 0x34, 0xc4, + 0x39, 0xaa, 0x77, 0xe1, 0x09, 0x18, 0x0d, 0x29, 0xd0, 0x6b, 0xd7, 0xf2, 0xbb, 0xe0, 0x48, 0x5b, + 0xd6, 0xe8, 0x79, 0x98, 0x6a, 0x1a, 0xba, 0xe1, 0x62, 0xdb, 0xb2, 0x31, 0xf1, 0x58, 0xd6, 0x55, + 0xfe, 0x3f, 0x0f, 0x77, 0xf0, 0xb9, 0xed, 0x20, 0x36, 0xe3, 0xa2, 0x4c, 0x36, 0x5b, 0x81, 0xa7, + 0x32, 0xe9, 0xd7, 0x87, 0x73, 0x2f, 0xbf, 0xfc, 0xf2, 0xcb, 0x09, 0xf9, 0xab, 0x43, 0x30, 0xd5, + 0x6e, 0xce, 0xb4, 0x9d, 0xbe, 0x47, 0x61, 0xc8, 0x68, 0x36, 0x76, 0xb0, 0x4d, 0x8d, 0x34, 0xa8, + 0xf0, 0x16, 0x9a, 0x87, 0xc1, 0xba, 0xba, 0x83, 0xeb, 0xf9, 0xd4, 0xac, 0x74, 0x72, 0xec, 0xcc, + 0x83, 0x3d, 0xcd, 0xca, 0xb9, 0x15, 0x42, 0xa2, 0x30, 0x4a, 0xf4, 0x16, 0x48, 0xf1, 0x10, 0x4d, + 0x38, 0x9c, 0xea, 0x8d, 0x03, 0x99, 0x4b, 0x0a, 0xa5, 0x43, 0x77, 0x40, 0x86, 0xfc, 0x65, 0xbe, + 0x31, 0x44, 0x65, 0x4e, 0x13, 0x00, 0xf1, 0x0b, 0x54, 0x80, 0x34, 0x9d, 0x26, 0x55, 0x2c, 0x52, + 0x9b, 0xd7, 0x26, 0x8e, 0x55, 0xc5, 0xbb, 0x6a, 0xb3, 0xee, 0x56, 0xae, 0xa8, 0xf5, 0x26, 0xa6, + 0x0e, 0x9f, 0x51, 0xb2, 0x1c, 0x78, 0x99, 0xc0, 0xd0, 0x0c, 0x8c, 0xb0, 0x59, 0xa5, 0x1b, 0x55, + 0x7c, 0x8d, 0x46, 0xcf, 0x41, 0x85, 0x4d, 0xb4, 0x65, 0x02, 0x21, 0xdd, 0xbf, 0xe0, 0x98, 0x86, + 0x70, 0x4d, 0xda, 0x05, 0x01, 0xd0, 0xee, 0x9f, 0x88, 0x06, 0xee, 0x3b, 0xdb, 0xab, 0xd7, 0x32, + 0x97, 0xee, 0x87, 0x71, 0x8a, 0xf1, 0x18, 0x1f, 0x7a, 0xb5, 0x9e, 0x9f, 0x98, 0x95, 0x4e, 0xa6, + 0x95, 0x31, 0x06, 0x5e, 0xe7, 0x50, 0xf9, 0xcb, 0x09, 0x48, 0xd1, 0xc0, 0x32, 0x0e, 0x23, 0x5b, + 0x6f, 0xdd, 0x28, 0x57, 0x16, 0xd7, 0xb7, 0x4b, 0x2b, 0xe5, 0x9c, 0x84, 0xc6, 0x00, 0x28, 0xe0, + 0xc2, 0xca, 0xfa, 0xfc, 0x56, 0x2e, 0xe1, 0xb5, 0x97, 0xd7, 0xb6, 0xce, 0x3d, 0x9e, 0x4b, 0x7a, + 0x04, 0xdb, 0x0c, 0x90, 0x0a, 0x22, 0x3c, 0x76, 0x26, 0x37, 0x88, 0x72, 0x90, 0x65, 0x0c, 0x96, + 0x9f, 0x2f, 0x2f, 0x9e, 0x7b, 0x3c, 0x37, 0x14, 0x86, 0x3c, 0x76, 0x26, 0x37, 0x8c, 0x46, 0x21, + 0x43, 0x21, 0xa5, 0xf5, 0xf5, 0x95, 0x5c, 0xda, 0xe3, 0xb9, 0xb9, 0xa5, 0x2c, 0xaf, 0x2d, 0xe5, + 0x32, 0x1e, 0xcf, 0x25, 0x65, 0x7d, 0x7b, 0x23, 0x07, 0x1e, 0x87, 0xd5, 0xf2, 0xe6, 0xe6, 0xfc, + 0x52, 0x39, 0x37, 0xe2, 0x61, 0x94, 0xde, 0xba, 0x55, 0xde, 0xcc, 0x65, 0x43, 0x62, 0x3d, 0x76, + 0x26, 0x37, 0xea, 0x75, 0x51, 0x5e, 0xdb, 0x5e, 0xcd, 0x8d, 0xa1, 0x09, 0x18, 0x65, 0x5d, 0x08, + 0x21, 0xc6, 0x23, 0xa0, 0x73, 0x8f, 0xe7, 0x72, 0xbe, 0x20, 0x8c, 0xcb, 0x44, 0x08, 0x70, 0xee, + 0xf1, 0x1c, 0x92, 0x17, 0x60, 0x90, 0xba, 0x21, 0x42, 0x30, 0xb6, 0x32, 0x5f, 0x2a, 0xaf, 0x54, + 0xd6, 0x37, 0xb6, 0x96, 0xd7, 0xd7, 0xe6, 0x57, 0x72, 0x92, 0x0f, 0x53, 0xca, 0xcf, 0x6e, 0x2f, + 0x2b, 0xe5, 0xc5, 0x5c, 0x22, 0x08, 0xdb, 0x28, 0xcf, 0x6f, 0x95, 0x17, 0x73, 0x49, 0x59, 0x83, + 0xa9, 0x76, 0x01, 0xb5, 0xed, 0x14, 0x0a, 0xf8, 0x42, 0xa2, 0x83, 0x2f, 0x50, 0x5e, 0x51, 0x5f, + 0x90, 0xbf, 0x9d, 0x80, 0xc9, 0x36, 0x49, 0xa5, 0x6d, 0x27, 0x4f, 0xc3, 0x20, 0xf3, 0x65, 0x96, + 0x66, 0x1f, 0x68, 0x9b, 0x9d, 0xa8, 0x67, 0xb7, 0xa4, 0x5a, 0x4a, 0x17, 0x2c, 0x35, 0x92, 0x1d, + 0x4a, 0x0d, 0xc2, 0xa2, 0xc5, 0x61, 0x7f, 0xba, 0x25, 0xf8, 0xb3, 0xfc, 0x78, 0xae, 0x97, 0xfc, + 0x48, 0x61, 0xfd, 0x25, 0x81, 0xc1, 0x36, 0x49, 0xe0, 0x3c, 0x4c, 0xb4, 0x30, 0xea, 0x39, 0x18, + 0xbf, 0x57, 0x82, 0x7c, 0x27, 0xe3, 0xc4, 0x84, 0xc4, 0x44, 0x28, 0x24, 0x9e, 0x8f, 0x5a, 0xf0, + 0xae, 0xce, 0x83, 0xd0, 0x32, 0xd6, 0x9f, 0x95, 0xe0, 0x68, 0xfb, 0x92, 0xb2, 0xad, 0x0c, 0x6f, + 0x81, 0xa1, 0x06, 0x76, 0xf7, 0x4c, 0x51, 0x56, 0xdd, 0xd7, 0x26, 0x59, 0x93, 0xc7, 0xd1, 0xc1, + 0xe6, 0x54, 0xc1, 0x6c, 0x9f, 0xec, 0x54, 0x17, 0x32, 0x69, 0x5a, 0x24, 0xfd, 0x60, 0x02, 0x8e, + 0xb4, 0x65, 0xde, 0x56, 0xd0, 0x3b, 0x01, 0x74, 0xc3, 0x6a, 0xba, 0xac, 0x74, 0x62, 0x91, 0x38, + 0x43, 0x21, 0x34, 0x78, 0x91, 0x28, 0xdb, 0x74, 0xbd, 0xe7, 0x49, 0xfa, 0x1c, 0x18, 0x88, 0x22, + 0x3c, 0xe9, 0x0b, 0x9a, 0xa2, 0x82, 0x4e, 0x77, 0xd0, 0xb4, 0xc5, 0x31, 0x1f, 0x81, 0x9c, 0x56, + 0xd7, 0xb1, 0xe1, 0x56, 0x1c, 0xd7, 0xc6, 0x6a, 0x43, 0x37, 0x6a, 0x34, 0xd5, 0xa4, 0x8b, 0x83, + 0xbb, 0x6a, 0xdd, 0xc1, 0xca, 0x38, 0x7b, 0xbc, 0x29, 0x9e, 0x12, 0x0a, 0xea, 0x40, 0x76, 0x80, + 0x62, 0x28, 0x44, 0xc1, 0x1e, 0x7b, 0x14, 0xf2, 0x87, 0x33, 0x30, 0x12, 0x28, 0xc0, 0xd1, 0x5d, + 0x90, 0x7d, 0x41, 0xbd, 0xa2, 0x56, 0xc4, 0xa2, 0x8a, 0x59, 0x62, 0x84, 0xc0, 0x36, 0xf8, 0xc2, + 0xea, 0x11, 0x98, 0xa2, 0x28, 0x66, 0xd3, 0xc5, 0x76, 0x45, 0xab, 0xab, 0x8e, 0x43, 0x8d, 0x96, + 0xa6, 0xa8, 0x88, 0x3c, 0x5b, 0x27, 0x8f, 0x16, 0xc4, 0x13, 0x74, 0x16, 0x26, 0x29, 0x45, 0xa3, + 0x59, 0x77, 0x75, 0xab, 0x8e, 0x2b, 0x64, 0x99, 0xe7, 0xd0, 0x94, 0xe3, 0x49, 0x36, 0x41, 0x30, + 0x56, 0x39, 0x02, 0x91, 0xc8, 0x41, 0x8b, 0x70, 0x27, 0x25, 0xab, 0x61, 0x03, 0xdb, 0xaa, 0x8b, + 0x2b, 0xf8, 0xc5, 0xa6, 0x5a, 0x77, 0x2a, 0xaa, 0x51, 0xad, 0xec, 0xa9, 0xce, 0x5e, 0x7e, 0x8a, + 0x30, 0x28, 0x25, 0xf2, 0x92, 0x72, 0x9c, 0x20, 0x2e, 0x71, 0xbc, 0x32, 0x45, 0x9b, 0x37, 0xaa, + 0x17, 0x55, 0x67, 0x0f, 0x15, 0xe1, 0x28, 0xe5, 0xe2, 0xb8, 0xb6, 0x6e, 0xd4, 0x2a, 0xda, 0x1e, + 0xd6, 0xf6, 0x2b, 0x4d, 0x77, 0xf7, 0xc9, 0xfc, 0x1d, 0xc1, 0xfe, 0xa9, 0x84, 0x9b, 0x14, 0x67, + 0x81, 0xa0, 0x6c, 0xbb, 0xbb, 0x4f, 0xa2, 0x4d, 0xc8, 0x92, 0xc1, 0x68, 0xe8, 0x2f, 0xe1, 0xca, + 0xae, 0x69, 0xd3, 0x1c, 0x3a, 0xd6, 0x26, 0x34, 0x05, 0x2c, 0x38, 0xb7, 0xce, 0x09, 0x56, 0xcd, + 0x2a, 0x2e, 0x0e, 0x6e, 0x6e, 0x94, 0xcb, 0x8b, 0xca, 0x88, 0xe0, 0x72, 0xc1, 0xb4, 0x89, 0x43, + 0xd5, 0x4c, 0xcf, 0xc0, 0x23, 0xcc, 0xa1, 0x6a, 0xa6, 0x30, 0xef, 0x59, 0x98, 0xd4, 0x34, 0xa6, + 0xb3, 0xae, 0x55, 0xf8, 0x62, 0xcc, 0xc9, 0xe7, 0x42, 0xc6, 0xd2, 0xb4, 0x25, 0x86, 0xc0, 0x7d, + 0xdc, 0x41, 0x4f, 0xc1, 0x11, 0xdf, 0x58, 0x41, 0xc2, 0x89, 0x16, 0x2d, 0xa3, 0xa4, 0x67, 0x61, + 0xd2, 0x3a, 0x68, 0x25, 0x44, 0xa1, 0x1e, 0xad, 0x83, 0x28, 0xd9, 0x13, 0x30, 0x65, 0xed, 0x59, + 0xad, 0x74, 0xa7, 0x82, 0x74, 0xc8, 0xda, 0xb3, 0xa2, 0x84, 0xf7, 0xd2, 0x95, 0xb9, 0x8d, 0x35, + 0xd5, 0xc5, 0xd5, 0xfc, 0xb1, 0x20, 0x7a, 0xe0, 0x01, 0x9a, 0x83, 0x9c, 0xa6, 0x55, 0xb0, 0xa1, + 0xee, 0xd4, 0x71, 0x45, 0xb5, 0xb1, 0xa1, 0x3a, 0xf9, 0x19, 0x8a, 0x9c, 0x72, 0xed, 0x26, 0x56, + 0xc6, 0x34, 0xad, 0x4c, 0x1f, 0xce, 0xd3, 0x67, 0xe8, 0x14, 0x4c, 0x98, 0x3b, 0x2f, 0x68, 0xcc, + 0x23, 0x2b, 0x96, 0x8d, 0x77, 0xf5, 0x6b, 0xf9, 0x7b, 0xa8, 0x79, 0xc7, 0xc9, 0x03, 0xea, 0x8f, + 0x1b, 0x14, 0x8c, 0x1e, 0x80, 0x9c, 0xe6, 0xec, 0xa9, 0xb6, 0x45, 0x43, 0xb2, 0x63, 0xa9, 0x1a, + 0xce, 0xdf, 0xcb, 0x50, 0x19, 0x7c, 0x4d, 0x80, 0xc9, 0x8c, 0x70, 0xae, 0xea, 0xbb, 0xae, 0xe0, + 0x78, 0x3f, 0x9b, 0x11, 0x14, 0xc6, 0xb9, 0x9d, 0x84, 0x1c, 0xb1, 0x44, 0xa8, 0xe3, 0x93, 0x14, + 0x6d, 0xcc, 0xda, 0xb3, 0x82, 0xfd, 0xde, 0x0d, 0xa3, 0x04, 0xd3, 0xef, 0xf4, 0x01, 0x56, 0xb8, + 0x59, 0x7b, 0x81, 0x1e, 0x1f, 0x87, 0xa3, 0x04, 0xa9, 0x81, 0x5d, 0xb5, 0xaa, 0xba, 0x6a, 0x00, + 0xfb, 0x21, 0x8a, 0x4d, 0xcc, 0xbe, 0xca, 0x1f, 0x86, 0xe4, 0xb4, 0x9b, 0x3b, 0x07, 0x9e, 0x63, + 0x3d, 0xcc, 0xe4, 0x24, 0x30, 0xe1, 0x5a, 0xb7, 0xad, 0x38, 0x97, 0x8b, 0x90, 0x0d, 0xfa, 0x3d, + 0xca, 0x00, 0xf3, 0xfc, 0x9c, 0x44, 0x8a, 0xa0, 0x85, 0xf5, 0x45, 0x52, 0xbe, 0xbc, 0xad, 0x9c, + 0x4b, 0x90, 0x32, 0x6a, 0x65, 0x79, 0xab, 0x5c, 0x51, 0xb6, 0xd7, 0xb6, 0x96, 0x57, 0xcb, 0xb9, + 0x64, 0xa0, 0xb0, 0x7f, 0x26, 0x95, 0xbe, 0x2f, 0x77, 0xbf, 0xfc, 0xad, 0x04, 0x8c, 0x85, 0x57, + 0x6a, 0xe8, 0x4d, 0x70, 0x4c, 0x6c, 0xab, 0x38, 0xd8, 0xad, 0x5c, 0xd5, 0x6d, 0x3a, 0x21, 0x1b, + 0x2a, 0x4b, 0x8e, 0x9e, 0xff, 0x4c, 0x71, 0xac, 0x4d, 0xec, 0x3e, 0xa7, 0xdb, 0x64, 0xba, 0x35, + 0x54, 0x17, 0xad, 0xc0, 0x8c, 0x61, 0x56, 0x1c, 0x57, 0x35, 0xaa, 0xaa, 0x5d, 0xad, 0xf8, 0x1b, + 0x5a, 0x15, 0x55, 0xd3, 0xb0, 0xe3, 0x98, 0x2c, 0x11, 0x7a, 0x5c, 0x4e, 0x18, 0xe6, 0x26, 0x47, + 0xf6, 0x33, 0xc4, 0x3c, 0x47, 0x8d, 0xb8, 0x6f, 0xb2, 0x93, 0xfb, 0xde, 0x01, 0x99, 0x86, 0x6a, + 0x55, 0xb0, 0xe1, 0xda, 0x07, 0xb4, 0x3e, 0x4f, 0x2b, 0xe9, 0x86, 0x6a, 0x95, 0x49, 0xfb, 0xc7, + 0xb2, 0x4c, 0x7a, 0x26, 0x95, 0x4e, 0xe7, 0x32, 0xcf, 0xa4, 0xd2, 0x99, 0x1c, 0xc8, 0xaf, 0x25, + 0x21, 0x1b, 0xac, 0xd7, 0xc9, 0xf2, 0x47, 0xa3, 0x19, 0x4b, 0xa2, 0x31, 0xed, 0xee, 0xae, 0xd5, + 0xfd, 0xdc, 0x02, 0x49, 0x65, 0xc5, 0x21, 0x56, 0x1c, 0x2b, 0x8c, 0x92, 0x94, 0x11, 0xc4, 0xd9, + 0x30, 0x2b, 0x46, 0xd2, 0x0a, 0x6f, 0xa1, 0x25, 0x18, 0x7a, 0xc1, 0xa1, 0xbc, 0x87, 0x28, 0xef, + 0x7b, 0xba, 0xf3, 0x7e, 0x66, 0x93, 0x32, 0xcf, 0x3c, 0xb3, 0x59, 0x59, 0x5b, 0x57, 0x56, 0xe7, + 0x57, 0x14, 0x4e, 0x8e, 0x8e, 0x43, 0xaa, 0xae, 0xbe, 0x74, 0x10, 0x4e, 0x7a, 0x14, 0xd4, 0xeb, + 0x20, 0x1c, 0x87, 0xd4, 0x55, 0xac, 0xee, 0x87, 0x53, 0x0d, 0x05, 0xdd, 0xc6, 0xc9, 0x70, 0x1a, + 0x06, 0xa9, 0xbd, 0x10, 0x00, 0xb7, 0x58, 0x6e, 0x00, 0xa5, 0x21, 0xb5, 0xb0, 0xae, 0x90, 0x09, + 0x91, 0x83, 0x2c, 0x83, 0x56, 0x36, 0x96, 0xcb, 0x0b, 0xe5, 0x5c, 0x42, 0x3e, 0x0b, 0x43, 0xcc, + 0x08, 0x64, 0xb2, 0x78, 0x66, 0xc8, 0x0d, 0xf0, 0x26, 0xe7, 0x21, 0x89, 0xa7, 0xdb, 0xab, 0xa5, + 0xb2, 0x92, 0x4b, 0x84, 0x87, 0x3a, 0x95, 0x1b, 0x94, 0x1d, 0xc8, 0x06, 0xeb, 0xf0, 0x1f, 0xcf, + 0x62, 0xfc, 0x2b, 0x12, 0x8c, 0x04, 0xea, 0x6a, 0x52, 0x10, 0xa9, 0xf5, 0xba, 0x79, 0xb5, 0xa2, + 0xd6, 0x75, 0xd5, 0xe1, 0xae, 0x01, 0x14, 0x34, 0x4f, 0x20, 0xbd, 0x0e, 0xdd, 0x8f, 0x69, 0x8a, + 0x0c, 0xe6, 0x86, 0xe4, 0x4f, 0x4a, 0x90, 0x8b, 0x16, 0xb6, 0x11, 0x31, 0xa5, 0x37, 0x52, 0x4c, + 0xf9, 0x13, 0x12, 0x8c, 0x85, 0xab, 0xd9, 0x88, 0x78, 0x77, 0xbd, 0xa1, 0xe2, 0xfd, 0x41, 0x02, + 0x46, 0x43, 0x35, 0x6c, 0xaf, 0xd2, 0xbd, 0x08, 0x13, 0x7a, 0x15, 0x37, 0x2c, 0xd3, 0xc5, 0x86, + 0x76, 0x50, 0xa9, 0xe3, 0x2b, 0xb8, 0x9e, 0x97, 0x69, 0xd0, 0x38, 0xdd, 0xbd, 0x4a, 0x9e, 0x5b, + 0xf6, 0xe9, 0x56, 0x08, 0x59, 0x71, 0x72, 0x79, 0xb1, 0xbc, 0xba, 0xb1, 0xbe, 0x55, 0x5e, 0x5b, + 0x78, 0x6b, 0x65, 0x7b, 0xed, 0xd2, 0xda, 0xfa, 0x73, 0x6b, 0x4a, 0x4e, 0x8f, 0xa0, 0xdd, 0xc6, + 0x69, 0xbf, 0x01, 0xb9, 0xa8, 0x50, 0xe8, 0x18, 0xb4, 0x13, 0x2b, 0x37, 0x80, 0x26, 0x61, 0x7c, + 0x6d, 0xbd, 0xb2, 0xb9, 0xbc, 0x58, 0xae, 0x94, 0x2f, 0x5c, 0x28, 0x2f, 0x6c, 0x6d, 0xb2, 0x7d, + 0x0f, 0x0f, 0x7b, 0x2b, 0x34, 0xc1, 0xe5, 0x57, 0x93, 0x30, 0xd9, 0x46, 0x12, 0x34, 0xcf, 0x57, + 0x2c, 0x6c, 0x11, 0xf5, 0x70, 0x2f, 0xd2, 0xcf, 0x91, 0x9a, 0x61, 0x43, 0xb5, 0x5d, 0xbe, 0xc0, + 0x79, 0x00, 0x88, 0x95, 0x0c, 0x57, 0xdf, 0xd5, 0xb1, 0xcd, 0xf7, 0x93, 0xd8, 0x32, 0x66, 0xdc, + 0x87, 0xb3, 0x2d, 0xa5, 0x87, 0x00, 0x59, 0xa6, 0xa3, 0xbb, 0xfa, 0x15, 0x5c, 0xd1, 0x0d, 0xb1, + 0xf9, 0x44, 0x96, 0x35, 0x29, 0x25, 0x27, 0x9e, 0x2c, 0x1b, 0xae, 0x87, 0x6d, 0xe0, 0x9a, 0x1a, + 0xc1, 0x26, 0xc1, 0x3c, 0xa9, 0xe4, 0xc4, 0x13, 0x0f, 0xfb, 0x2e, 0xc8, 0x56, 0xcd, 0x26, 0xa9, + 0xf5, 0x18, 0x1e, 0xc9, 0x1d, 0x92, 0x32, 0xc2, 0x60, 0x1e, 0x0a, 0xaf, 0xe2, 0xfd, 0x5d, 0xaf, + 0xac, 0x32, 0xc2, 0x60, 0x0c, 0xe5, 0x7e, 0x18, 0x57, 0x6b, 0x35, 0x9b, 0x30, 0x17, 0x8c, 0xd8, + 0xba, 0x64, 0xcc, 0x03, 0x53, 0xc4, 0xc2, 0x33, 0x90, 0x16, 0x76, 0x20, 0xa9, 0x9a, 0x58, 0xa2, + 0x62, 0xb1, 0xc5, 0x76, 0xe2, 0x64, 0x46, 0x49, 0x1b, 0xe2, 0xe1, 0x5d, 0x90, 0xd5, 0x9d, 0x8a, + 0xbf, 0x89, 0x9f, 0x98, 0x4d, 0x9c, 0x4c, 0x2b, 0x23, 0xba, 0xe3, 0x6d, 0x80, 0xca, 0x9f, 0x4d, + 0xc0, 0x58, 0xf8, 0x25, 0x04, 0x5a, 0x84, 0x74, 0xdd, 0xd4, 0x54, 0xea, 0x5a, 0xec, 0x0d, 0xd8, + 0xc9, 0x98, 0xf7, 0x16, 0x73, 0x2b, 0x1c, 0x5f, 0xf1, 0x28, 0x0b, 0xbf, 0x2d, 0x41, 0x5a, 0x80, + 0xd1, 0x51, 0x48, 0x59, 0xaa, 0xbb, 0x47, 0xd9, 0x0d, 0x96, 0x12, 0x39, 0x49, 0xa1, 0x6d, 0x02, + 0x77, 0x2c, 0xd5, 0xa0, 0x2e, 0xc0, 0xe1, 0xa4, 0x4d, 0xc6, 0xb5, 0x8e, 0xd5, 0x2a, 0x5d, 0xf4, + 0x98, 0x8d, 0x06, 0x36, 0x5c, 0x47, 0x8c, 0x2b, 0x87, 0x2f, 0x70, 0x30, 0x7a, 0x10, 0x26, 0x5c, + 0x5b, 0xd5, 0xeb, 0x21, 0xdc, 0x14, 0xc5, 0xcd, 0x89, 0x07, 0x1e, 0x72, 0x11, 0x8e, 0x0b, 0xbe, + 0x55, 0xec, 0xaa, 0xda, 0x1e, 0xae, 0xfa, 0x44, 0x43, 0x74, 0x73, 0xe3, 0x18, 0x47, 0x58, 0xe4, + 0xcf, 0x05, 0xad, 0xfc, 0x2d, 0x09, 0x26, 0xc4, 0x32, 0xad, 0xea, 0x19, 0x6b, 0x15, 0x40, 0x35, + 0x0c, 0xd3, 0x0d, 0x9a, 0xab, 0xd5, 0x95, 0x5b, 0xe8, 0xe6, 0xe6, 0x3d, 0x22, 0x25, 0xc0, 0xa0, + 0xd0, 0x00, 0xf0, 0x9f, 0x74, 0x34, 0xdb, 0x0c, 0x8c, 0xf0, 0x37, 0x4c, 0xf4, 0x35, 0x25, 0x5b, + 0xd8, 0x03, 0x03, 0x91, 0xf5, 0x1c, 0x9a, 0x82, 0xc1, 0x1d, 0x5c, 0xd3, 0x0d, 0xbe, 0x6f, 0xcc, + 0x1a, 0x62, 0xfb, 0x25, 0xe5, 0x6d, 0xbf, 0x94, 0xfe, 0x32, 0x4c, 0x6a, 0x66, 0x23, 0x2a, 0x6e, + 0x29, 0x17, 0xd9, 0x5c, 0x70, 0x2e, 0x4a, 0x6f, 0x7b, 0x98, 0x23, 0xd5, 0xcc, 0xba, 0x6a, 0xd4, + 0xe6, 0x4c, 0xbb, 0xe6, 0xbf, 0x66, 0x25, 0x15, 0x8f, 0x13, 0x78, 0xd9, 0x6a, 0xed, 0xfc, 0x99, + 0x24, 0xfd, 0x62, 0x22, 0xb9, 0xb4, 0x51, 0xfa, 0x5c, 0xa2, 0xb0, 0xc4, 0x08, 0x37, 0x84, 0x31, + 0x14, 0xbc, 0x5b, 0xc7, 0x1a, 0x51, 0x10, 0xbe, 0xfb, 0x20, 0x4c, 0xd5, 0xcc, 0x9a, 0x49, 0x39, + 0x9d, 0x26, 0xbf, 0xf8, 0x7b, 0xda, 0x8c, 0x07, 0x2d, 0xc4, 0xbe, 0xd4, 0x2d, 0xae, 0xc1, 0x24, + 0x47, 0xae, 0xd0, 0x17, 0x45, 0x6c, 0x19, 0x83, 0xba, 0xee, 0xa1, 0xe5, 0xbf, 0xf0, 0x1d, 0x9a, + 0xbe, 0x95, 0x09, 0x4e, 0x4a, 0x9e, 0xb1, 0x95, 0x4e, 0x51, 0x81, 0x23, 0x21, 0x7e, 0x6c, 0x92, + 0x62, 0x3b, 0x86, 0xe3, 0x6f, 0x72, 0x8e, 0x93, 0x01, 0x8e, 0x9b, 0x9c, 0xb4, 0xb8, 0x00, 0xa3, + 0xfd, 0xf0, 0xfa, 0x97, 0x9c, 0x57, 0x16, 0x07, 0x99, 0x2c, 0xc1, 0x38, 0x65, 0xa2, 0x35, 0x1d, + 0xd7, 0x6c, 0xd0, 0x08, 0xd8, 0x9d, 0xcd, 0x6f, 0x7d, 0x87, 0xcd, 0x9a, 0x31, 0x42, 0xb6, 0xe0, + 0x51, 0x15, 0x8b, 0x40, 0xdf, 0x8d, 0x55, 0xb1, 0x56, 0x8f, 0xe1, 0xf0, 0x35, 0x2e, 0x88, 0x87, + 0x5f, 0xbc, 0x0c, 0x53, 0xe4, 0x37, 0x0d, 0x50, 0x41, 0x49, 0xe2, 0x37, 0xdc, 0xf2, 0xdf, 0x7a, + 0x2f, 0x9b, 0x98, 0x93, 0x1e, 0x83, 0x80, 0x4c, 0x81, 0x51, 0xac, 0x61, 0xd7, 0xc5, 0xb6, 0x53, + 0x51, 0xeb, 0xed, 0xc4, 0x0b, 0xec, 0x58, 0xe4, 0x3f, 0xf6, 0xbd, 0xf0, 0x28, 0x2e, 0x31, 0xca, + 0xf9, 0x7a, 0xbd, 0xb8, 0x0d, 0xc7, 0xda, 0x78, 0x45, 0x0f, 0x3c, 0x5f, 0xe5, 0x3c, 0xa7, 0x5a, + 0x3c, 0x83, 0xb0, 0xdd, 0x00, 0x01, 0xf7, 0xc6, 0xb2, 0x07, 0x9e, 0x1f, 0xe7, 0x3c, 0x11, 0xa7, + 0x15, 0x43, 0x4a, 0x38, 0x3e, 0x03, 0x13, 0x57, 0xb0, 0xbd, 0x63, 0x3a, 0x7c, 0x97, 0xa8, 0x07, + 0x76, 0x9f, 0xe0, 0xec, 0xc6, 0x39, 0x21, 0xdd, 0x36, 0x22, 0xbc, 0x9e, 0x82, 0xf4, 0xae, 0xaa, + 0xe1, 0x1e, 0x58, 0x5c, 0xe7, 0x2c, 0x86, 0x09, 0x3e, 0x21, 0x9d, 0x87, 0x6c, 0xcd, 0xe4, 0x39, + 0x2a, 0x9e, 0xfc, 0x93, 0x9c, 0x7c, 0x44, 0xd0, 0x70, 0x16, 0x96, 0x69, 0x35, 0xeb, 0x24, 0x81, + 0xc5, 0xb3, 0xf8, 0x5b, 0x82, 0x85, 0xa0, 0xe1, 0x2c, 0xfa, 0x30, 0xeb, 0xa7, 0x04, 0x0b, 0x27, + 0x60, 0xcf, 0xa7, 0x61, 0xc4, 0x34, 0xea, 0x07, 0xa6, 0xd1, 0x8b, 0x10, 0x9f, 0xe6, 0x1c, 0x80, + 0x93, 0x10, 0x06, 0xe7, 0x21, 0xd3, 0xeb, 0x40, 0xfc, 0xed, 0xef, 0x89, 0xe9, 0x21, 0x46, 0x60, + 0x09, 0xc6, 0x45, 0x80, 0xd2, 0x4d, 0xa3, 0x07, 0x16, 0x7f, 0x87, 0xb3, 0x18, 0x0b, 0x90, 0x71, + 0x35, 0x5c, 0xec, 0xb8, 0x35, 0xdc, 0x0b, 0x93, 0xcf, 0x0a, 0x35, 0x38, 0x09, 0x37, 0xe5, 0x0e, + 0x36, 0xb4, 0xbd, 0xde, 0x38, 0xfc, 0xb2, 0x30, 0xa5, 0xa0, 0x21, 0x2c, 0x16, 0x60, 0xb4, 0xa1, + 0xda, 0xce, 0x9e, 0x5a, 0xef, 0x69, 0x38, 0xfe, 0x2e, 0xe7, 0x91, 0xf5, 0x88, 0xb8, 0x45, 0x9a, + 0x46, 0x3f, 0x6c, 0x3e, 0x27, 0x2c, 0x12, 0x20, 0xe3, 0x53, 0xcf, 0x71, 0xe9, 0x96, 0x5a, 0x3f, + 0xdc, 0x7e, 0x45, 0x4c, 0x3d, 0x46, 0xbb, 0x1a, 0xe4, 0x78, 0x1e, 0x32, 0x8e, 0xfe, 0x52, 0x4f, + 0x6c, 0x3e, 0x2f, 0x46, 0x9a, 0x12, 0x10, 0xe2, 0xb7, 0xc2, 0xf1, 0xb6, 0x69, 0xa2, 0x07, 0x66, + 0x7f, 0x8f, 0x33, 0x3b, 0xda, 0x26, 0x55, 0xf0, 0x90, 0xd0, 0x2f, 0xcb, 0xbf, 0x2f, 0x42, 0x02, + 0x8e, 0xf0, 0xda, 0x20, 0xab, 0x06, 0x47, 0xdd, 0xed, 0xcf, 0x6a, 0xbf, 0x2a, 0xac, 0xc6, 0x68, + 0x43, 0x56, 0xdb, 0x82, 0xa3, 0x9c, 0x63, 0x7f, 0xe3, 0xfa, 0x6b, 0x22, 0xb0, 0x32, 0xea, 0xed, + 0xf0, 0xe8, 0xbe, 0x1d, 0x0a, 0x9e, 0x39, 0x45, 0x79, 0xea, 0x54, 0x1a, 0xaa, 0xd5, 0x03, 0xe7, + 0x2f, 0x70, 0xce, 0x22, 0xe2, 0x7b, 0xf5, 0xad, 0xb3, 0xaa, 0x5a, 0x84, 0xf9, 0xf3, 0x90, 0x17, + 0xcc, 0x9b, 0x86, 0x8d, 0x35, 0xb3, 0x66, 0xe8, 0x2f, 0xe1, 0x6a, 0x0f, 0xac, 0x7f, 0x3d, 0x32, + 0x54, 0xdb, 0x01, 0x72, 0xc2, 0x79, 0x19, 0x72, 0x5e, 0xad, 0x52, 0xd1, 0x1b, 0x96, 0x69, 0xbb, + 0x31, 0x1c, 0xbf, 0x28, 0x46, 0xca, 0xa3, 0x5b, 0xa6, 0x64, 0xc5, 0x32, 0xb0, 0xf7, 0xcc, 0xbd, + 0xba, 0xe4, 0x97, 0x38, 0xa3, 0x51, 0x9f, 0x8a, 0x07, 0x0e, 0xcd, 0x6c, 0x58, 0xaa, 0xdd, 0x4b, + 0xfc, 0xfb, 0x07, 0x22, 0x70, 0x70, 0x12, 0x1e, 0x38, 0x48, 0x45, 0x47, 0xb2, 0x7d, 0x0f, 0x1c, + 0xbe, 0x2c, 0x02, 0x87, 0xa0, 0xe1, 0x2c, 0x44, 0xc1, 0xd0, 0x03, 0x8b, 0x7f, 0x28, 0x58, 0x08, + 0x1a, 0xc2, 0xe2, 0x59, 0x3f, 0xd1, 0xda, 0xb8, 0xa6, 0x3b, 0xae, 0xcd, 0x8a, 0xe2, 0xee, 0xac, + 0xfe, 0xd1, 0xf7, 0xc2, 0x45, 0x98, 0x12, 0x20, 0x25, 0x91, 0x88, 0x6f, 0xb2, 0xd2, 0x35, 0x53, + 0xbc, 0x60, 0xbf, 0x21, 0x22, 0x51, 0x80, 0x8c, 0xc8, 0x16, 0xa8, 0x10, 0x89, 0xd9, 0x35, 0xb2, + 0x52, 0xe8, 0x81, 0xdd, 0x3f, 0x8e, 0x08, 0xb7, 0x29, 0x68, 0x09, 0xcf, 0x40, 0xfd, 0xd3, 0x34, + 0xf6, 0xf1, 0x41, 0x4f, 0xde, 0xf9, 0x4f, 0x22, 0xf5, 0xcf, 0x36, 0xa3, 0x64, 0x31, 0x64, 0x3c, + 0x52, 0x4f, 0xa1, 0xb8, 0x53, 0x45, 0xf9, 0x9f, 0xf9, 0x01, 0xd7, 0x37, 0x5c, 0x4e, 0x15, 0x57, + 0x88, 0x93, 0x87, 0x8b, 0x9e, 0x78, 0x66, 0xef, 0xfd, 0x81, 0xe7, 0xe7, 0xa1, 0x9a, 0xa7, 0x78, + 0x01, 0x46, 0x43, 0x05, 0x4f, 0x3c, 0xab, 0xf7, 0x71, 0x56, 0xd9, 0x60, 0xbd, 0x53, 0x3c, 0x0b, + 0x29, 0x52, 0xbc, 0xc4, 0x93, 0xff, 0x15, 0x4e, 0x4e, 0xd1, 0x8b, 0x6f, 0x86, 0xb4, 0x28, 0x5a, + 0xe2, 0x49, 0xdf, 0xcf, 0x49, 0x3d, 0x12, 0x42, 0x2e, 0x0a, 0x96, 0x78, 0xf2, 0xbf, 0x2a, 0xc8, + 0x05, 0x09, 0x21, 0xef, 0xdd, 0x84, 0x5f, 0xf9, 0xd9, 0x14, 0x4f, 0x3a, 0xc2, 0x76, 0xe7, 0x61, + 0x98, 0x57, 0x2a, 0xf1, 0xd4, 0x1f, 0xe4, 0x9d, 0x0b, 0x8a, 0xe2, 0x13, 0x30, 0xd8, 0xa3, 0xc1, + 0x7f, 0x8e, 0x93, 0x32, 0xfc, 0xe2, 0x02, 0x8c, 0x04, 0xaa, 0x93, 0x78, 0xf2, 0xbf, 0xc6, 0xc9, + 0x83, 0x54, 0x44, 0x74, 0x5e, 0x9d, 0xc4, 0x33, 0xf8, 0x79, 0x21, 0x3a, 0xa7, 0x20, 0x66, 0x13, + 0x85, 0x49, 0x3c, 0xf5, 0x87, 0x84, 0xd5, 0x05, 0x49, 0xf1, 0x69, 0xc8, 0x78, 0xc9, 0x26, 0x9e, + 0xfe, 0xc3, 0x9c, 0xde, 0xa7, 0x21, 0x16, 0x08, 0x24, 0xbb, 0x78, 0x16, 0x7f, 0x5d, 0x58, 0x20, + 0x40, 0x45, 0xa6, 0x51, 0xb4, 0x80, 0x89, 0xe7, 0xf4, 0x11, 0x31, 0x8d, 0x22, 0xf5, 0x0b, 0x19, + 0x4d, 0x1a, 0xf3, 0xe3, 0x59, 0xfc, 0x0d, 0x31, 0x9a, 0x14, 0x9f, 0x88, 0x11, 0xad, 0x08, 0xe2, + 0x79, 0xfc, 0x82, 0x10, 0x23, 0x52, 0x10, 0x14, 0x37, 0x00, 0xb5, 0x56, 0x03, 0xf1, 0xfc, 0x3e, + 0xca, 0xf9, 0x4d, 0xb4, 0x14, 0x03, 0xc5, 0xe7, 0xe0, 0x68, 0xfb, 0x4a, 0x20, 0x9e, 0xeb, 0xc7, + 0x7e, 0x10, 0x59, 0xbb, 0x05, 0x0b, 0x81, 0xe2, 0x96, 0x9f, 0x52, 0x82, 0x55, 0x40, 0x3c, 0xdb, + 0x57, 0x7f, 0x10, 0x0e, 0xdc, 0xc1, 0x22, 0xa0, 0x38, 0x0f, 0xe0, 0x27, 0xe0, 0x78, 0x5e, 0x9f, + 0xe0, 0xbc, 0x02, 0x44, 0x64, 0x6a, 0xf0, 0xfc, 0x1b, 0x4f, 0x7f, 0x5d, 0x4c, 0x0d, 0x4e, 0x41, + 0xa6, 0x86, 0x48, 0xbd, 0xf1, 0xd4, 0x9f, 0x14, 0x53, 0x43, 0x90, 0x10, 0xcf, 0x0e, 0x64, 0xb7, + 0x78, 0x0e, 0x9f, 0x16, 0x9e, 0x1d, 0xa0, 0x2a, 0xae, 0xc1, 0x44, 0x4b, 0x42, 0x8c, 0x67, 0xf5, + 0x8b, 0x9c, 0x55, 0x2e, 0x9a, 0x0f, 0x83, 0xc9, 0x8b, 0x27, 0xc3, 0x78, 0x6e, 0x9f, 0x89, 0x24, + 0x2f, 0x9e, 0x0b, 0x8b, 0xe7, 0x21, 0x6d, 0x34, 0xeb, 0x75, 0x32, 0x79, 0x50, 0xf7, 0x93, 0x80, + 0xf9, 0xff, 0xf2, 0x43, 0x6e, 0x1d, 0x41, 0x50, 0x3c, 0x0b, 0x83, 0xb8, 0xb1, 0x83, 0xab, 0x71, + 0x94, 0xdf, 0xfd, 0xa1, 0x08, 0x98, 0x04, 0xbb, 0xf8, 0x34, 0x00, 0xdb, 0x1a, 0xa1, 0x2f, 0x03, + 0x63, 0x68, 0xff, 0xeb, 0x0f, 0xf9, 0xd1, 0x1b, 0x9f, 0xc4, 0x67, 0xc0, 0x0e, 0xf2, 0x74, 0x67, + 0xf0, 0xbd, 0x30, 0x03, 0x3a, 0x22, 0x4f, 0xc1, 0xf0, 0x0b, 0x8e, 0x69, 0xb8, 0x6a, 0x2d, 0x8e, + 0xfa, 0xbf, 0x71, 0x6a, 0x81, 0x4f, 0x0c, 0xd6, 0x30, 0x6d, 0xec, 0xaa, 0x35, 0x27, 0x8e, 0xf6, + 0xbf, 0x73, 0x5a, 0x8f, 0x80, 0x10, 0x6b, 0xaa, 0xe3, 0xf6, 0xa2, 0xf7, 0x1f, 0x09, 0x62, 0x41, + 0x40, 0x84, 0x26, 0xbf, 0xf7, 0xf1, 0x41, 0x1c, 0xed, 0xf7, 0x85, 0xd0, 0x1c, 0xbf, 0xf8, 0x66, + 0xc8, 0x90, 0x9f, 0xec, 0x3c, 0x5d, 0x0c, 0xf1, 0x1f, 0x73, 0x62, 0x9f, 0x82, 0xf4, 0xec, 0xb8, + 0x55, 0x57, 0x8f, 0x37, 0xf6, 0x4d, 0x3e, 0xd2, 0x02, 0xbf, 0x38, 0x0f, 0x23, 0x8e, 0x5b, 0xad, + 0x36, 0x79, 0x7d, 0x1a, 0x43, 0xfe, 0x27, 0x3f, 0xf4, 0xb6, 0x2c, 0x3c, 0x1a, 0x32, 0xda, 0x57, + 0xf7, 0x5d, 0xcb, 0xa4, 0x2f, 0x3c, 0xe2, 0x38, 0xfc, 0x80, 0x73, 0x08, 0x90, 0x14, 0x17, 0x20, + 0x4b, 0x74, 0xb1, 0xb1, 0x85, 0xe9, 0xdb, 0xa9, 0x18, 0x16, 0x7f, 0xca, 0x0d, 0x10, 0x22, 0x2a, + 0xfd, 0xf4, 0xd7, 0x5e, 0x9b, 0x96, 0xbe, 0xf9, 0xda, 0xb4, 0xf4, 0x07, 0xaf, 0x4d, 0x4b, 0x1f, + 0xfa, 0xf6, 0xf4, 0xc0, 0x37, 0xbf, 0x3d, 0x3d, 0xf0, 0xbb, 0xdf, 0x9e, 0x1e, 0x68, 0xbf, 0x4b, + 0x0c, 0x4b, 0xe6, 0x92, 0xc9, 0xf6, 0x87, 0xdf, 0x26, 0xd7, 0x74, 0x77, 0xaf, 0xb9, 0x33, 0xa7, + 0x99, 0x0d, 0xba, 0x8d, 0xeb, 0xef, 0xd6, 0x7a, 0x8b, 0x1c, 0xf8, 0x53, 0x89, 0x2c, 0x98, 0xc3, + 0x7b, 0xb9, 0xaa, 0x71, 0xd0, 0xe1, 0x66, 0x4e, 0xa1, 0xed, 0xc6, 0xb0, 0xfc, 0x26, 0x48, 0xce, + 0x1b, 0x07, 0xe8, 0x38, 0x8b, 0x79, 0x95, 0xa6, 0x5d, 0xe7, 0xe7, 0xbc, 0x86, 0x49, 0x7b, 0xdb, + 0xae, 0xa3, 0x29, 0xff, 0x30, 0xa6, 0x74, 0x32, 0xcb, 0x4f, 0x58, 0x16, 0x53, 0xdf, 0xff, 0xf4, + 0xcc, 0x40, 0x69, 0x3f, 0xaa, 0xe1, 0x57, 0x62, 0xb5, 0x4c, 0xcf, 0x1b, 0x07, 0x54, 0xc9, 0x0d, + 0xe9, 0x6d, 0x83, 0x74, 0xa3, 0x5b, 0x6c, 0x6c, 0x4f, 0x47, 0x37, 0xb6, 0x9f, 0xc3, 0xf5, 0xfa, + 0x25, 0xc3, 0xbc, 0x6a, 0x6c, 0x11, 0xb4, 0x9d, 0x21, 0x76, 0x68, 0x18, 0x3e, 0x92, 0x80, 0xe9, + 0x96, 0x3d, 0x6c, 0x3e, 0xf2, 0x9d, 0xae, 0x25, 0x15, 0x21, 0xbd, 0x28, 0x1c, 0x2a, 0x0f, 0xc3, + 0x0e, 0xd6, 0x4c, 0xa3, 0xea, 0x50, 0x55, 0x93, 0x8a, 0x68, 0x12, 0x55, 0x0d, 0xd5, 0x30, 0x1d, + 0x7e, 0x16, 0x92, 0x35, 0x4a, 0x1f, 0x97, 0xfa, 0x1b, 0xc7, 0x51, 0xd1, 0x93, 0x50, 0xf3, 0xd1, + 0xd8, 0xad, 0xfe, 0x7d, 0xa2, 0xa5, 0xa7, 0x44, 0x68, 0xbb, 0xbf, 0x57, 0xab, 0xfc, 0x42, 0x02, + 0x66, 0xa2, 0x56, 0x21, 0xd3, 0xc9, 0x71, 0xd5, 0x86, 0xd5, 0xc9, 0x2c, 0xe7, 0x21, 0xb3, 0x25, + 0x70, 0xfa, 0xb6, 0xcb, 0xf5, 0x3e, 0xed, 0x32, 0xe6, 0x75, 0x25, 0x0c, 0x73, 0xa6, 0x47, 0xc3, + 0x78, 0x7a, 0x1c, 0xca, 0x32, 0xef, 0x49, 0xc2, 0x71, 0xcd, 0x74, 0x1a, 0xa6, 0x53, 0x61, 0xee, + 0xcf, 0x1a, 0xdc, 0x26, 0xd9, 0xe0, 0xa3, 0x1e, 0x5e, 0x8e, 0x5c, 0x84, 0x31, 0x1a, 0x22, 0xe8, + 0xb6, 0x30, 0x8d, 0xca, 0xb1, 0x89, 0xf4, 0xeb, 0xff, 0x76, 0x90, 0x4e, 0xa9, 0x51, 0x8f, 0x90, + 0x9e, 0x72, 0xd9, 0x82, 0x29, 0xbd, 0x61, 0xd5, 0x31, 0x7d, 0x1d, 0x56, 0xf1, 0x9e, 0xc5, 0xf3, + 0xfb, 0x06, 0xe7, 0x37, 0xe9, 0x93, 0x2f, 0x0b, 0xea, 0xe2, 0x0a, 0x4c, 0xa8, 0x9a, 0x86, 0xad, + 0x10, 0xcb, 0x98, 0xf0, 0x25, 0x04, 0xcc, 0x71, 0x4a, 0x8f, 0x5b, 0xe9, 0xe9, 0x4e, 0x43, 0xfc, + 0xb6, 0x7b, 0x03, 0x11, 0xca, 0xc6, 0x35, 0x6c, 0x3c, 0x6c, 0x60, 0xf7, 0xaa, 0x69, 0xef, 0x73, + 0xf3, 0x3e, 0xcc, 0xba, 0x12, 0x83, 0xf0, 0xbe, 0x24, 0x4c, 0xb3, 0x07, 0xa7, 0x77, 0x54, 0x07, + 0x9f, 0xbe, 0xf2, 0xe8, 0x0e, 0x76, 0xd5, 0x47, 0x4f, 0x6b, 0xa6, 0x2e, 0x26, 0xed, 0x24, 0x1f, + 0x17, 0xf2, 0x7c, 0x8e, 0x3f, 0xef, 0x10, 0xb5, 0x96, 0x20, 0xb5, 0x60, 0xea, 0x06, 0x71, 0xcc, + 0x2a, 0x36, 0xcc, 0x06, 0x8f, 0x59, 0xac, 0x81, 0xee, 0x86, 0x21, 0xb5, 0x61, 0x36, 0x0d, 0x97, + 0xbd, 0xc9, 0x2b, 0x8d, 0x7c, 0xed, 0xc6, 0xcc, 0xc0, 0xef, 0xdd, 0x98, 0x49, 0x2e, 0x1b, 0xae, + 0xc2, 0x1f, 0x15, 0x53, 0xaf, 0x7f, 0x6a, 0x46, 0x92, 0x9f, 0x81, 0xe1, 0x45, 0xac, 0x1d, 0x86, + 0xd7, 0x22, 0xd6, 0x22, 0xbc, 0x1e, 0x80, 0xf4, 0xb2, 0xe1, 0xb2, 0xd3, 0xc3, 0x77, 0x42, 0x52, + 0x37, 0xd8, 0x81, 0xb4, 0x48, 0xff, 0x04, 0x4e, 0x50, 0x17, 0xb1, 0xe6, 0xa1, 0x56, 0xb1, 0x16, + 0x45, 0x25, 0xec, 0x09, 0xbc, 0xb4, 0xf8, 0xbb, 0xff, 0x69, 0x7a, 0xe0, 0xe5, 0xd7, 0xa6, 0x07, + 0x3a, 0x8e, 0x44, 0x30, 0x57, 0x70, 0x13, 0xf3, 0x21, 0x70, 0xaa, 0xfb, 0x6c, 0x1e, 0x79, 0xc3, + 0xf0, 0xb9, 0x14, 0xdc, 0x49, 0x2f, 0x8e, 0xd8, 0x0d, 0xdd, 0x70, 0x4f, 0x6b, 0xf6, 0x81, 0xe5, + 0xd2, 0xe4, 0x62, 0xee, 0xf2, 0x51, 0x98, 0xf0, 0x1f, 0xcf, 0xb1, 0xc7, 0x1d, 0xc6, 0x60, 0x17, + 0x06, 0x37, 0x08, 0x1d, 0x31, 0x9c, 0x6b, 0xba, 0x6a, 0x9d, 0x47, 0x0d, 0xd6, 0x20, 0x50, 0x76, + 0xd9, 0x24, 0xc1, 0xa0, 0xba, 0xb8, 0x67, 0x52, 0xc7, 0xea, 0x2e, 0x3b, 0xb3, 0x9b, 0xa4, 0x09, + 0x25, 0x4d, 0x00, 0xf4, 0x78, 0xee, 0x14, 0x0c, 0xaa, 0x4d, 0xf6, 0xba, 0x39, 0x49, 0x32, 0x0d, + 0x6d, 0xc8, 0x97, 0x60, 0x98, 0xbf, 0xf4, 0x42, 0x39, 0x48, 0xee, 0xe3, 0x03, 0xda, 0x4f, 0x56, + 0x21, 0x3f, 0xd1, 0x1c, 0x0c, 0x52, 0xe1, 0xf9, 0x65, 0x84, 0xfc, 0x5c, 0x8b, 0xf4, 0x73, 0x54, + 0x48, 0x85, 0xa1, 0xc9, 0xcf, 0x40, 0x7a, 0xd1, 0x6c, 0xe8, 0x86, 0x19, 0xe6, 0x96, 0x61, 0xdc, + 0xa8, 0xcc, 0x56, 0x93, 0x8f, 0xb5, 0xc2, 0x1a, 0xe8, 0x28, 0x0c, 0xb1, 0x33, 0xdc, 0xfc, 0x95, + 0x39, 0x6f, 0xc9, 0x0b, 0x30, 0x4c, 0x79, 0xaf, 0x5b, 0x08, 0xf1, 0xdb, 0x3f, 0xfc, 0xb0, 0x38, + 0x0d, 0x0b, 0x9c, 0x7d, 0xc2, 0x17, 0x16, 0x41, 0xaa, 0xaa, 0xba, 0x2a, 0xd7, 0x9b, 0xfe, 0x96, + 0xdf, 0x02, 0x69, 0xce, 0xc4, 0x41, 0x67, 0x20, 0x69, 0x5a, 0x0e, 0x7f, 0xe9, 0x5d, 0xe8, 0xa4, + 0xca, 0xba, 0x55, 0x4a, 0x11, 0x2f, 0x51, 0x08, 0x72, 0x49, 0xe9, 0xe8, 0x16, 0x4f, 0x06, 0xdc, + 0x22, 0x30, 0xe4, 0x81, 0x9f, 0x6c, 0x48, 0x5b, 0xdc, 0xc1, 0x73, 0x96, 0x4f, 0x27, 0x60, 0x3a, + 0xf0, 0xf4, 0x0a, 0xb6, 0xc9, 0xca, 0x8f, 0x79, 0x14, 0xf7, 0x16, 0x14, 0x10, 0x92, 0x3f, 0xef, + 0xe0, 0x2e, 0x6f, 0x86, 0xe4, 0xbc, 0x65, 0xa1, 0x02, 0xa4, 0x69, 0x5b, 0x33, 0x99, 0xbf, 0xa4, + 0x14, 0xaf, 0x4d, 0x9e, 0x39, 0xe6, 0xae, 0x7b, 0x55, 0xb5, 0xbd, 0x6b, 0x4e, 0xa2, 0x2d, 0x3f, + 0x05, 0x99, 0x05, 0xd3, 0x70, 0xb0, 0xe1, 0x34, 0x69, 0x3e, 0xda, 0xa9, 0x9b, 0xda, 0x3e, 0xe7, + 0xc0, 0x1a, 0xc4, 0xe0, 0xaa, 0x65, 0x51, 0xca, 0x94, 0x42, 0x7e, 0xb2, 0x79, 0x59, 0xda, 0xec, + 0x68, 0xa2, 0xa7, 0xfa, 0x37, 0x11, 0x57, 0xd2, 0xb3, 0xd1, 0xff, 0x96, 0xe0, 0x44, 0xeb, 0x84, + 0xda, 0xc7, 0x07, 0x4e, 0xbf, 0xf3, 0xe9, 0x79, 0xc8, 0x6c, 0xd0, 0xbb, 0xc6, 0x97, 0xf0, 0x01, + 0x2a, 0xc0, 0x30, 0xae, 0x9e, 0x39, 0x7b, 0xf6, 0xd1, 0xa7, 0x98, 0xb7, 0x5f, 0x1c, 0x50, 0x04, + 0x00, 0x4d, 0x43, 0xc6, 0xc1, 0x9a, 0x75, 0xe6, 0xec, 0xb9, 0xfd, 0x47, 0x99, 0x7b, 0x5d, 0x1c, + 0x50, 0x7c, 0x50, 0x31, 0x4d, 0xb4, 0x7e, 0xfd, 0xd3, 0x33, 0x52, 0x69, 0x10, 0x92, 0x4e, 0xb3, + 0x71, 0x5b, 0x7d, 0xe4, 0xd5, 0x41, 0x98, 0x0d, 0x52, 0xd2, 0xac, 0x7d, 0x45, 0xad, 0xeb, 0x55, + 0xd5, 0xbf, 0x25, 0x9e, 0x0b, 0xd8, 0x80, 0x62, 0xb4, 0x37, 0x41, 0xa1, 0xab, 0x25, 0xe5, 0x5f, + 0x97, 0x20, 0x7b, 0x59, 0x70, 0xde, 0xc4, 0x2e, 0x3a, 0x0f, 0xe0, 0xf5, 0x24, 0xa6, 0xcd, 0x1d, + 0x73, 0xd1, 0xbe, 0xe6, 0x3c, 0x1a, 0x25, 0x80, 0x8e, 0x9e, 0xa0, 0x8e, 0x68, 0x99, 0x0e, 0xbf, + 0xfa, 0x12, 0x43, 0xea, 0x21, 0xa3, 0x87, 0x00, 0xd1, 0x08, 0x57, 0xb9, 0x62, 0xba, 0xba, 0x51, + 0xab, 0x58, 0xe6, 0x55, 0x7e, 0xa1, 0x30, 0xa9, 0xe4, 0xe8, 0x93, 0xcb, 0xf4, 0xc1, 0x06, 0x81, + 0x13, 0xa1, 0x33, 0x1e, 0x17, 0x52, 0x62, 0xa9, 0xd5, 0xaa, 0x8d, 0x1d, 0x87, 0x07, 0x31, 0xd1, + 0x44, 0xe7, 0x61, 0xd8, 0x6a, 0xee, 0x54, 0x44, 0xc4, 0x18, 0x39, 0x73, 0xa2, 0xdd, 0xfc, 0x17, + 0xfe, 0xc1, 0x23, 0xc0, 0x90, 0xd5, 0xdc, 0x21, 0xde, 0x72, 0x17, 0x64, 0xdb, 0x08, 0x33, 0x72, + 0xc5, 0x97, 0x83, 0x5e, 0x71, 0xe7, 0x1a, 0x54, 0x2c, 0x5b, 0x37, 0x6d, 0xdd, 0x3d, 0xa0, 0x27, + 0x57, 0x92, 0x4a, 0x4e, 0x3c, 0xd8, 0xe0, 0x70, 0x79, 0x1f, 0xc6, 0x37, 0x69, 0x6d, 0xe1, 0x4b, + 0x7e, 0xd6, 0x97, 0x4f, 0x8a, 0x97, 0xaf, 0xa3, 0x64, 0x89, 0x16, 0xc9, 0x4a, 0xcf, 0x76, 0xf4, + 0xce, 0x27, 0xfa, 0xf7, 0xce, 0x70, 0xb6, 0xfb, 0xa3, 0xe3, 0xa1, 0xc9, 0xc9, 0x9c, 0x33, 0x18, + 0xbe, 0x7a, 0x75, 0xcc, 0xb8, 0xca, 0xba, 0xd0, 0x3d, 0xa9, 0x16, 0x62, 0xc2, 0x68, 0x21, 0x76, + 0x0a, 0xc9, 0x4f, 0xc1, 0xe8, 0x86, 0x6a, 0xbb, 0x9b, 0xd8, 0xbd, 0x88, 0xd5, 0x2a, 0xb6, 0xc3, + 0x59, 0x77, 0x54, 0x64, 0x5d, 0x04, 0x29, 0x9a, 0x5a, 0x59, 0xd6, 0xa1, 0xbf, 0xe5, 0x3d, 0x48, + 0xd1, 0xd3, 0x6b, 0x5e, 0x46, 0xe6, 0x14, 0x2c, 0x23, 0x93, 0x58, 0x7a, 0xe0, 0x62, 0x47, 0x2c, + 0xef, 0x68, 0x03, 0x3d, 0x2e, 0xf2, 0x6a, 0xb2, 0x7b, 0x5e, 0xe5, 0x8e, 0xc8, 0xb3, 0x6b, 0x1d, + 0x86, 0x4b, 0x24, 0x14, 0x2f, 0x2f, 0x7a, 0x82, 0x48, 0xbe, 0x20, 0x68, 0x15, 0xc6, 0x2d, 0xd5, + 0x76, 0xe9, 0xb1, 0xfd, 0x3d, 0xaa, 0x05, 0xf7, 0xf5, 0x99, 0xd6, 0x99, 0x17, 0x52, 0x96, 0xf7, + 0x32, 0x6a, 0x05, 0x81, 0xf2, 0x1f, 0xa6, 0x60, 0x88, 0x1b, 0xe3, 0xcd, 0x30, 0xcc, 0xcd, 0xca, + 0xbd, 0xf3, 0xce, 0xb9, 0xd6, 0xc4, 0x34, 0xe7, 0x25, 0x10, 0xce, 0x4f, 0xd0, 0xa0, 0xfb, 0x20, + 0xad, 0xed, 0xa9, 0xba, 0x51, 0xd1, 0xab, 0xa2, 0xcc, 0x7b, 0xed, 0xc6, 0xcc, 0xf0, 0x02, 0x81, + 0x2d, 0x2f, 0x2a, 0xc3, 0xf4, 0xe1, 0x72, 0x95, 0x54, 0x02, 0x7b, 0x58, 0xaf, 0xed, 0xb9, 0x7c, + 0x86, 0xf1, 0x16, 0x7a, 0x12, 0x52, 0xc4, 0x21, 0xf8, 0xa5, 0xae, 0x42, 0x4b, 0xb1, 0xed, 0x2d, + 0x7c, 0x4a, 0x69, 0xd2, 0xf1, 0x87, 0x7e, 0x7f, 0x46, 0x52, 0x28, 0x05, 0x5a, 0x80, 0xd1, 0xba, + 0xea, 0xb8, 0x15, 0x9a, 0xc1, 0x48, 0xf7, 0x83, 0x94, 0xc5, 0xf1, 0x56, 0x83, 0x70, 0xc3, 0x72, + 0xd1, 0x47, 0x08, 0x15, 0x03, 0x55, 0xd1, 0x49, 0xc8, 0x51, 0x26, 0x9a, 0xd9, 0x68, 0xe8, 0x2e, + 0xab, 0xad, 0x86, 0xa8, 0xdd, 0xc7, 0x08, 0x7c, 0x81, 0x82, 0x69, 0x85, 0x75, 0x07, 0x64, 0xe8, + 0x35, 0x12, 0x8a, 0xc2, 0x8e, 0x4c, 0xa6, 0x09, 0x80, 0x3e, 0xbc, 0x1f, 0xc6, 0xfd, 0xf8, 0xc8, + 0x50, 0xd2, 0x8c, 0x8b, 0x0f, 0xa6, 0x88, 0x8f, 0xc0, 0x94, 0x81, 0xaf, 0xd1, 0x43, 0x9c, 0x21, + 0xec, 0x0c, 0xc5, 0x46, 0xe4, 0xd9, 0xe5, 0x30, 0xc5, 0xbd, 0x30, 0xa6, 0x09, 0xe3, 0x33, 0x5c, + 0xa0, 0xb8, 0xa3, 0x1e, 0x94, 0xa2, 0x1d, 0x87, 0xb4, 0x6a, 0x59, 0x0c, 0x61, 0x84, 0xc7, 0x47, + 0xcb, 0xa2, 0x8f, 0x4e, 0xc1, 0x04, 0xd5, 0xd1, 0xc6, 0x4e, 0xb3, 0xee, 0x72, 0x26, 0x59, 0x8a, + 0x33, 0x4e, 0x1e, 0x28, 0x0c, 0x4e, 0x71, 0xef, 0x86, 0x51, 0x7c, 0x45, 0xaf, 0x62, 0x43, 0xc3, + 0x0c, 0x6f, 0x94, 0xe2, 0x65, 0x05, 0x90, 0x22, 0x3d, 0x00, 0x5e, 0xdc, 0xab, 0x88, 0x98, 0x3c, + 0xc6, 0xf8, 0x09, 0xf8, 0x3c, 0x03, 0xcb, 0x79, 0x48, 0x2d, 0xaa, 0xae, 0x4a, 0x0a, 0x0c, 0xf7, + 0x1a, 0x4b, 0x34, 0x59, 0x85, 0xfc, 0x94, 0x5f, 0x4f, 0x40, 0xea, 0xb2, 0xe9, 0x62, 0xf4, 0x58, + 0xa0, 0x00, 0x1c, 0x6b, 0xe7, 0xcf, 0x9b, 0x7a, 0xcd, 0xc0, 0xd5, 0x55, 0xa7, 0x16, 0xb8, 0xf3, + 0xed, 0xbb, 0x53, 0x22, 0xe4, 0x4e, 0x53, 0x30, 0x68, 0x9b, 0x4d, 0xa3, 0x2a, 0x4e, 0x1b, 0xd2, + 0x06, 0x2a, 0x43, 0xda, 0xf3, 0x92, 0x54, 0x9c, 0x97, 0x8c, 0x13, 0x2f, 0x21, 0x3e, 0xcc, 0x01, + 0xca, 0xf0, 0x0e, 0x77, 0x96, 0x12, 0x64, 0xbc, 0xe0, 0xc5, 0xbd, 0xad, 0x37, 0x87, 0xf5, 0xc9, + 0x48, 0x32, 0xf1, 0xc6, 0xde, 0x33, 0x1e, 0xf3, 0xb8, 0x9c, 0xf7, 0x80, 0x5b, 0x2f, 0xe4, 0x56, + 0xfc, 0xfe, 0xf9, 0x30, 0xd5, 0xcb, 0x77, 0x2b, 0x76, 0x07, 0xfd, 0x04, 0x64, 0x1c, 0xbd, 0x66, + 0xa8, 0x6e, 0xd3, 0xc6, 0xdc, 0xf3, 0x7c, 0x80, 0xfc, 0x15, 0x09, 0x86, 0x98, 0x27, 0x07, 0xec, + 0x26, 0xb5, 0xb7, 0x5b, 0xa2, 0x93, 0xdd, 0x92, 0x87, 0xb7, 0xdb, 0x3c, 0x80, 0x27, 0x8c, 0xc3, + 0xaf, 0x05, 0xb7, 0xa9, 0x18, 0x98, 0x88, 0x9b, 0x7a, 0x8d, 0x4f, 0xd4, 0x00, 0x91, 0xfc, 0x1f, + 0x25, 0x52, 0xc4, 0xf2, 0xe7, 0x68, 0x1e, 0x46, 0x85, 0x5c, 0x95, 0xdd, 0xba, 0x5a, 0xe3, 0xbe, + 0x73, 0x67, 0x47, 0xe1, 0x2e, 0xd4, 0xd5, 0x9a, 0x32, 0xc2, 0xe5, 0x21, 0x8d, 0xf6, 0xe3, 0x90, + 0xe8, 0x30, 0x0e, 0xa1, 0x81, 0x4f, 0x1e, 0x6e, 0xe0, 0x43, 0x43, 0x94, 0x8a, 0x0e, 0xd1, 0x17, + 0x13, 0x74, 0x31, 0x63, 0x99, 0x8e, 0x5a, 0xff, 0x71, 0xcc, 0x88, 0x3b, 0x20, 0x63, 0x99, 0xf5, + 0x0a, 0x7b, 0xc2, 0x4e, 0xe1, 0xa6, 0x2d, 0xb3, 0xae, 0xb4, 0x0c, 0xfb, 0xe0, 0x2d, 0x9a, 0x2e, + 0x43, 0xb7, 0xc0, 0x6a, 0xc3, 0x51, 0xab, 0xd9, 0x90, 0x65, 0xa6, 0xe0, 0xb9, 0xec, 0x11, 0x62, + 0x03, 0x9a, 0x1c, 0xa5, 0xd6, 0xdc, 0xcb, 0xc4, 0x66, 0x98, 0x0a, 0xc7, 0x23, 0x14, 0x2c, 0xf4, + 0xb7, 0x5b, 0x05, 0x07, 0xdd, 0x52, 0xe1, 0x78, 0xf2, 0xdf, 0x94, 0x00, 0x56, 0x88, 0x65, 0xa9, + 0xbe, 0x24, 0x0b, 0x39, 0x54, 0x84, 0x4a, 0xa8, 0xe7, 0xe9, 0x4e, 0x83, 0xc6, 0xfb, 0xcf, 0x3a, + 0x41, 0xb9, 0x17, 0x60, 0xd4, 0x77, 0x46, 0x07, 0x0b, 0x61, 0xa6, 0xbb, 0x54, 0xd5, 0x9b, 0xd8, + 0x55, 0xb2, 0x57, 0x02, 0x2d, 0xf9, 0x9f, 0x4b, 0x90, 0xa1, 0x32, 0xad, 0x62, 0x57, 0x0d, 0x8d, + 0xa1, 0x74, 0xf8, 0x31, 0xbc, 0x13, 0x80, 0xb1, 0x71, 0xf4, 0x97, 0x30, 0xf7, 0xac, 0x0c, 0x85, + 0x6c, 0xea, 0x2f, 0x61, 0x74, 0xce, 0x33, 0x78, 0xb2, 0xbb, 0xc1, 0x45, 0xd5, 0xcd, 0xcd, 0x7e, + 0x0c, 0x86, 0xe9, 0x67, 0x74, 0xae, 0x39, 0xbc, 0x90, 0x1e, 0x32, 0x9a, 0x8d, 0xad, 0x6b, 0x8e, + 0xfc, 0x02, 0x0c, 0x6f, 0x5d, 0x63, 0x7b, 0x23, 0x77, 0x40, 0xc6, 0x36, 0x4d, 0x9e, 0x93, 0x59, + 0x2d, 0x94, 0x26, 0x00, 0x9a, 0x82, 0xc4, 0x7e, 0x40, 0xc2, 0xdf, 0x0f, 0xf0, 0x37, 0x34, 0x92, + 0x3d, 0x6d, 0x68, 0x9c, 0xfa, 0x77, 0x12, 0x8c, 0x04, 0xe2, 0x03, 0x7a, 0x14, 0x8e, 0x94, 0x56, + 0xd6, 0x17, 0x2e, 0x55, 0x96, 0x17, 0x2b, 0x17, 0x56, 0xe6, 0x97, 0xfc, 0x7b, 0x26, 0x85, 0xa3, + 0xaf, 0x5c, 0x9f, 0x45, 0x01, 0xdc, 0x6d, 0x83, 0xee, 0xae, 0xa2, 0xd3, 0x30, 0x15, 0x26, 0x99, + 0x2f, 0x6d, 0x96, 0xd7, 0xb6, 0x72, 0x52, 0xe1, 0xc8, 0x2b, 0xd7, 0x67, 0x27, 0x02, 0x14, 0xf3, + 0x3b, 0x0e, 0x36, 0xdc, 0x56, 0x82, 0x85, 0xf5, 0xd5, 0xd5, 0xe5, 0xad, 0x5c, 0xa2, 0x85, 0x80, + 0x07, 0xec, 0x07, 0x60, 0x22, 0x4c, 0xb0, 0xb6, 0xbc, 0x92, 0x4b, 0x16, 0xd0, 0x2b, 0xd7, 0x67, + 0xc7, 0x02, 0xd8, 0x6b, 0x7a, 0xbd, 0x90, 0xfe, 0xc0, 0x67, 0xa6, 0x07, 0x7e, 0xf9, 0x97, 0xa6, + 0x25, 0xa2, 0xd9, 0x68, 0x28, 0x46, 0xa0, 0x87, 0xe0, 0xd8, 0xe6, 0xf2, 0xd2, 0x5a, 0x79, 0xb1, + 0xb2, 0xba, 0xb9, 0x54, 0x61, 0xdf, 0xd7, 0xf0, 0xb4, 0x1b, 0x7f, 0xe5, 0xfa, 0xec, 0x08, 0x57, + 0xa9, 0x13, 0xf6, 0x86, 0x52, 0xbe, 0xbc, 0xbe, 0x55, 0xce, 0x49, 0x0c, 0x7b, 0xc3, 0xc6, 0x57, + 0x4c, 0x97, 0x7d, 0x67, 0xeb, 0x11, 0x38, 0xde, 0x06, 0xdb, 0x53, 0x6c, 0xe2, 0x95, 0xeb, 0xb3, + 0xa3, 0x1b, 0x36, 0x66, 0xf3, 0x87, 0x52, 0xcc, 0x41, 0xbe, 0x95, 0x62, 0x7d, 0x63, 0x7d, 0x73, + 0x7e, 0x25, 0x37, 0x5b, 0xc8, 0xbd, 0x72, 0x7d, 0x36, 0x2b, 0x82, 0x21, 0xc1, 0xf7, 0x35, 0xbb, + 0x9d, 0x2b, 0x9e, 0x3f, 0x79, 0x18, 0xee, 0xe1, 0x7b, 0x80, 0x8e, 0xab, 0xee, 0xeb, 0x46, 0xcd, + 0xdb, 0x69, 0xe5, 0x6d, 0xbe, 0xf2, 0x39, 0xca, 0x37, 0x5b, 0x05, 0xb4, 0xeb, 0x7e, 0x6b, 0xa1, + 0xf3, 0x7b, 0xa6, 0x42, 0xcc, 0xab, 0x98, 0xf8, 0xa5, 0x53, 0xe7, 0xbd, 0xf9, 0x42, 0xcc, 0x8e, + 0x71, 0xa1, 0xeb, 0xe2, 0x4e, 0xfe, 0xa0, 0x04, 0x63, 0x17, 0x75, 0xc7, 0x35, 0x6d, 0x5d, 0x53, + 0xeb, 0xf4, 0x76, 0xc9, 0xb9, 0x5e, 0x63, 0x6b, 0x64, 0xaa, 0x3f, 0x0d, 0x43, 0x57, 0xd4, 0x3a, + 0x0b, 0x6a, 0x49, 0xfa, 0x31, 0x8c, 0xf6, 0xe6, 0xf3, 0x43, 0x9b, 0x60, 0xc0, 0xc8, 0xe4, 0x5f, + 0x4d, 0xc0, 0x38, 0x9d, 0x0c, 0x0e, 0xfb, 0x4c, 0x12, 0x59, 0x63, 0x95, 0x20, 0x65, 0xab, 0x2e, + 0xdf, 0x34, 0x2c, 0xcd, 0xf1, 0x9d, 0xdf, 0xfb, 0xe2, 0x77, 0x73, 0xe7, 0x16, 0xb1, 0xa6, 0x50, + 0x5a, 0xf4, 0x0e, 0x48, 0x37, 0xd4, 0x6b, 0x15, 0xca, 0x87, 0xad, 0x5c, 0xe6, 0xfb, 0xe3, 0x73, + 0xf3, 0xc6, 0xcc, 0xf8, 0x81, 0xda, 0xa8, 0x17, 0x65, 0xc1, 0x47, 0x56, 0x86, 0x1b, 0xea, 0x35, + 0x22, 0x22, 0xb2, 0x60, 0x9c, 0x40, 0xb5, 0x3d, 0xd5, 0xa8, 0x61, 0xd6, 0x09, 0xdd, 0x02, 0x2d, + 0x5d, 0xec, 0xbb, 0x93, 0xa3, 0x7e, 0x27, 0x01, 0x76, 0xb2, 0x32, 0xda, 0x50, 0xaf, 0x2d, 0x50, + 0x00, 0xe9, 0xb1, 0x98, 0xfe, 0xe8, 0xa7, 0x66, 0x06, 0xe8, 0x6e, 0xfa, 0xb7, 0x24, 0x00, 0xdf, + 0x62, 0xe8, 0x1d, 0x90, 0xd3, 0xbc, 0x16, 0xa5, 0x75, 0xf8, 0x18, 0xde, 0xdf, 0x69, 0x2c, 0x22, + 0xf6, 0x66, 0xb9, 0xf9, 0x9b, 0x37, 0x66, 0x24, 0x65, 0x5c, 0x8b, 0x0c, 0xc5, 0xdb, 0x61, 0xa4, + 0x69, 0x55, 0x55, 0x17, 0x57, 0xe8, 0x3a, 0x2e, 0x11, 0x9b, 0xe7, 0xa7, 0x09, 0xaf, 0x9b, 0x37, + 0x66, 0x10, 0x53, 0x2b, 0x40, 0x2c, 0xd3, 0xec, 0x0f, 0x0c, 0x42, 0x08, 0x02, 0x3a, 0x7d, 0x5d, + 0x82, 0x91, 0xc5, 0xc0, 0xb9, 0xaf, 0x3c, 0x0c, 0x37, 0x4c, 0x43, 0xdf, 0xe7, 0xfe, 0x98, 0x51, + 0x44, 0x13, 0x15, 0x20, 0xcd, 0x2e, 0xdc, 0xb9, 0x07, 0x62, 0x2b, 0x54, 0xb4, 0x09, 0xd5, 0x55, + 0xbc, 0xe3, 0xe8, 0x62, 0x34, 0x14, 0xd1, 0x44, 0x17, 0x20, 0xe7, 0x60, 0xad, 0x69, 0xeb, 0xee, + 0x41, 0x45, 0x33, 0x0d, 0x57, 0xd5, 0x5c, 0x76, 0x75, 0xab, 0x74, 0xc7, 0xcd, 0x1b, 0x33, 0xc7, + 0x98, 0xac, 0x51, 0x0c, 0x59, 0x19, 0x17, 0xa0, 0x05, 0x06, 0x21, 0x3d, 0x54, 0xb1, 0xab, 0xea, + 0x75, 0x27, 0xcf, 0x5e, 0x0c, 0x89, 0x66, 0x40, 0x97, 0xcf, 0x0f, 0x07, 0x37, 0xb6, 0x2e, 0x40, + 0xce, 0xb4, 0xb0, 0x1d, 0x2a, 0x44, 0xa5, 0x68, 0xcf, 0x51, 0x0c, 0x59, 0x19, 0x17, 0x20, 0x51, + 0xa4, 0xba, 0x64, 0x98, 0xc5, 0x42, 0xd1, 0x6a, 0xee, 0xf8, 0xfb, 0x61, 0x53, 0x2d, 0xa3, 0x31, + 0x6f, 0x1c, 0x94, 0x1e, 0xf3, 0xb9, 0x47, 0xe9, 0xe4, 0x6f, 0x7c, 0xe9, 0xe1, 0x29, 0xee, 0x1a, + 0xfe, 0xfe, 0xd4, 0x25, 0x7c, 0x40, 0x86, 0x9f, 0xa3, 0x6e, 0x50, 0x4c, 0x52, 0x76, 0xbe, 0xa0, + 0xea, 0x75, 0x71, 0x05, 0x59, 0xe1, 0x2d, 0x54, 0x84, 0x21, 0xc7, 0x55, 0xdd, 0xa6, 0xc3, 0x3f, + 0x0c, 0x26, 0x77, 0x72, 0xb5, 0x92, 0x69, 0x54, 0x37, 0x29, 0xa6, 0xc2, 0x29, 0xd0, 0x05, 0x18, + 0x72, 0xcd, 0x7d, 0x6c, 0x70, 0x13, 0xf6, 0x35, 0xbf, 0xe9, 0x7b, 0x2a, 0x46, 0x4d, 0x2c, 0x52, + 0xc5, 0x75, 0x5c, 0x63, 0x65, 0xd5, 0x9e, 0x4a, 0x56, 0x1f, 0xf4, 0xfb, 0x60, 0xa5, 0xe5, 0xbe, + 0x27, 0x21, 0xb7, 0x54, 0x94, 0x9f, 0xac, 0x8c, 0x7b, 0xa0, 0x4d, 0x0a, 0x41, 0x97, 0x42, 0x07, + 0x14, 0xf9, 0x47, 0xf4, 0xee, 0xee, 0xa4, 0x7e, 0xc0, 0xa7, 0xc5, 0xfe, 0x44, 0xf0, 0x78, 0xe3, + 0x05, 0xc8, 0x35, 0x8d, 0x1d, 0xd3, 0xa0, 0xf7, 0x04, 0x79, 0x7d, 0x4f, 0xd6, 0x77, 0xc9, 0xa0, + 0x73, 0x44, 0x31, 0x64, 0x65, 0xdc, 0x03, 0x5d, 0x64, 0xab, 0x80, 0x2a, 0x8c, 0xf9, 0x58, 0x74, + 0xa2, 0x66, 0x62, 0x27, 0xea, 0x5d, 0x7c, 0xa2, 0x1e, 0x89, 0xf6, 0xe2, 0xcf, 0xd5, 0x51, 0x0f, + 0x48, 0xc8, 0xd0, 0x45, 0x00, 0x3f, 0x3c, 0xd0, 0x7d, 0x8a, 0x91, 0xce, 0x03, 0xef, 0xc7, 0x18, + 0xb1, 0xde, 0xf3, 0x69, 0xd1, 0xbb, 0x60, 0xb2, 0xa1, 0x1b, 0x15, 0x07, 0xd7, 0x77, 0x2b, 0xdc, + 0xc0, 0x84, 0x25, 0xfd, 0xcc, 0x4b, 0x69, 0xa5, 0x3f, 0x7f, 0xb8, 0x79, 0x63, 0xa6, 0xc0, 0x43, + 0x68, 0x2b, 0x4b, 0x59, 0x99, 0x68, 0xe8, 0xc6, 0x26, 0xae, 0xef, 0x2e, 0x7a, 0xb0, 0x62, 0xf6, + 0x03, 0x9f, 0x9a, 0x19, 0xe0, 0xd3, 0x75, 0x40, 0x3e, 0x47, 0xf7, 0xce, 0xf9, 0x34, 0xc3, 0x0e, + 0x59, 0x93, 0xa8, 0xa2, 0x41, 0x77, 0x34, 0x32, 0x8a, 0x0f, 0x60, 0xd3, 0xfc, 0xe5, 0xff, 0x30, + 0x2b, 0xc9, 0x9f, 0x97, 0x60, 0x68, 0xf1, 0xf2, 0x86, 0xaa, 0xdb, 0x68, 0x19, 0x26, 0x7c, 0xcf, + 0x09, 0x4f, 0xf2, 0x13, 0x37, 0x6f, 0xcc, 0xe4, 0xa3, 0xce, 0xe5, 0xcd, 0x72, 0xdf, 0x81, 0xc5, + 0x34, 0x5f, 0xee, 0xb4, 0x70, 0x0d, 0xb1, 0x6a, 0x41, 0x91, 0x5b, 0x97, 0xb5, 0x11, 0x35, 0xcb, + 0x30, 0xcc, 0xa4, 0x75, 0x50, 0x11, 0x06, 0x2d, 0xf2, 0x83, 0xbf, 0x18, 0x98, 0xee, 0xe8, 0xbc, + 0x14, 0xdf, 0xdb, 0xc8, 0x24, 0x24, 0xf2, 0x87, 0x13, 0x00, 0x8b, 0x97, 0x2f, 0x6f, 0xd9, 0xba, + 0x55, 0xc7, 0xee, 0xad, 0xd4, 0x7c, 0x0b, 0x8e, 0x04, 0x56, 0x49, 0xb6, 0x16, 0xd1, 0x7e, 0xf6, + 0xe6, 0x8d, 0x99, 0x13, 0x51, 0xed, 0x03, 0x68, 0xb2, 0x32, 0xe9, 0xaf, 0x97, 0x6c, 0xad, 0x2d, + 0xd7, 0xaa, 0xe3, 0x7a, 0x5c, 0x93, 0x9d, 0xb9, 0x06, 0xd0, 0x82, 0x5c, 0x17, 0x1d, 0xb7, 0xbd, + 0x69, 0x37, 0x61, 0xc4, 0x37, 0x89, 0x83, 0x16, 0x21, 0xed, 0xf2, 0xdf, 0xdc, 0xc2, 0x72, 0x67, + 0x0b, 0x0b, 0x32, 0x6e, 0x65, 0x8f, 0x52, 0xfe, 0x33, 0x09, 0xc0, 0xf7, 0xd9, 0x9f, 0x4c, 0x17, + 0x23, 0xa1, 0x9c, 0x07, 0xde, 0xe4, 0xa1, 0x4a, 0x35, 0x4e, 0x1d, 0xb1, 0xe7, 0xcf, 0x26, 0x60, + 0x72, 0x5b, 0x44, 0x9e, 0x9f, 0x78, 0x1b, 0x6c, 0xc0, 0x30, 0x36, 0x5c, 0x5b, 0xa7, 0x46, 0x20, + 0xa3, 0xfd, 0x48, 0xa7, 0xd1, 0x6e, 0xa3, 0x13, 0xfd, 0xd0, 0x8d, 0xd8, 0x74, 0xe7, 0x6c, 0x22, + 0xd6, 0xf8, 0xf9, 0x24, 0xe4, 0x3b, 0x51, 0xa2, 0x05, 0x18, 0xd7, 0x6c, 0x4c, 0x01, 0x95, 0xe0, + 0xce, 0x5f, 0xa9, 0xe0, 0x57, 0x96, 0x11, 0x04, 0x59, 0x19, 0x13, 0x10, 0x9e, 0x3d, 0x6a, 0x40, + 0xca, 0x3e, 0xe2, 0x76, 0x04, 0xab, 0xc7, 0x3a, 0x4f, 0xe6, 0xe9, 0x43, 0x74, 0x12, 0x66, 0xc0, + 0xf2, 0xc7, 0x98, 0x0f, 0xa5, 0x09, 0xe4, 0x45, 0x18, 0xd7, 0x0d, 0xdd, 0xd5, 0xd5, 0x7a, 0x65, + 0x47, 0xad, 0xab, 0x86, 0x76, 0x98, 0xaa, 0x99, 0x85, 0x7c, 0xde, 0x6d, 0x84, 0x9d, 0xac, 0x8c, + 0x71, 0x48, 0x89, 0x01, 0xd0, 0x45, 0x18, 0x16, 0x5d, 0xa5, 0x0e, 0x55, 0x6d, 0x08, 0xf2, 0x40, + 0x81, 0xf7, 0x73, 0x49, 0x98, 0x50, 0x70, 0xf5, 0xff, 0x0f, 0x45, 0x7f, 0x43, 0xb1, 0x0a, 0xc0, + 0xa6, 0x3b, 0x09, 0xb0, 0x87, 0x18, 0x0d, 0x12, 0x30, 0x32, 0x8c, 0xc3, 0xa2, 0xe3, 0x06, 0xc6, + 0xe3, 0x46, 0x02, 0xb2, 0xc1, 0xf1, 0xf8, 0x0b, 0x9a, 0x95, 0xd0, 0xb2, 0x1f, 0x89, 0x52, 0xfc, + 0xf3, 0xa0, 0x1d, 0x22, 0x51, 0x8b, 0xf7, 0x76, 0x0f, 0x41, 0xff, 0x23, 0x01, 0x43, 0x1b, 0xaa, + 0xad, 0x36, 0x1c, 0xa4, 0xb5, 0x54, 0x9a, 0x62, 0xfb, 0xb1, 0xe5, 0x23, 0xd0, 0x7c, 0xb7, 0x23, + 0xa6, 0xd0, 0xfc, 0x68, 0x9b, 0x42, 0xf3, 0xa7, 0x60, 0x8c, 0x2c, 0x87, 0x03, 0x47, 0x18, 0x88, + 0xb5, 0x47, 0x4b, 0xc7, 0x7d, 0x2e, 0xe1, 0xe7, 0x6c, 0xb5, 0x7c, 0x39, 0x78, 0x86, 0x61, 0x84, + 0x60, 0xf8, 0x81, 0x99, 0x90, 0x1f, 0xf5, 0x97, 0xa5, 0x81, 0x87, 0xb2, 0x02, 0x0d, 0xf5, 0x5a, + 0x99, 0x35, 0xd0, 0x0a, 0xa0, 0x3d, 0x6f, 0x67, 0xa4, 0xe2, 0x9b, 0x93, 0xd0, 0xdf, 0x79, 0xf3, + 0xc6, 0xcc, 0x71, 0x46, 0xdf, 0x8a, 0x23, 0x2b, 0x13, 0x3e, 0x50, 0x70, 0x7b, 0x1c, 0x80, 0xe8, + 0x55, 0x61, 0xc7, 0xe7, 0xd8, 0x72, 0xe7, 0xc8, 0xcd, 0x1b, 0x33, 0x13, 0x8c, 0x8b, 0xff, 0x4c, + 0x56, 0x32, 0xa4, 0xb1, 0x48, 0x7e, 0x07, 0x3c, 0xfb, 0x33, 0x12, 0x20, 0x3f, 0xe4, 0x2b, 0xd8, + 0xb1, 0xc8, 0xfa, 0x8c, 0x14, 0xe2, 0x81, 0xaa, 0x59, 0xea, 0x5e, 0x88, 0xfb, 0xf4, 0xa2, 0x10, + 0x0f, 0xcc, 0x94, 0xa7, 0xfc, 0xf0, 0x98, 0xe0, 0xe3, 0xd8, 0xe6, 0xac, 0xe1, 0xdc, 0x82, 0xa9, + 0x0b, 0xea, 0x96, 0x78, 0x38, 0x20, 0xff, 0x2b, 0x09, 0x8e, 0xb7, 0x78, 0x94, 0x27, 0xec, 0x5f, + 0x02, 0x64, 0x07, 0x1e, 0xf2, 0x6f, 0xbd, 0x31, 0xa1, 0xfb, 0x76, 0xd0, 0x09, 0xbb, 0x25, 0xee, + 0xde, 0xba, 0x08, 0xcf, 0x0e, 0x2b, 0xfe, 0x33, 0x09, 0xa6, 0x82, 0xdd, 0x7b, 0x8a, 0xac, 0x41, + 0x36, 0xd8, 0x3b, 0x57, 0xe1, 0x9e, 0x5e, 0x54, 0xe0, 0xd2, 0x87, 0xe8, 0xd1, 0xb3, 0xfe, 0x74, + 0x65, 0x7b, 0x67, 0x8f, 0xf6, 0x6c, 0x0d, 0x21, 0x53, 0x74, 0xda, 0xa6, 0xe8, 0x78, 0xfc, 0x1f, + 0x09, 0x52, 0x1b, 0xa6, 0x59, 0x47, 0x26, 0x4c, 0x18, 0xa6, 0x5b, 0x21, 0x9e, 0x85, 0xab, 0x15, + 0xbe, 0xe8, 0x66, 0x71, 0x70, 0xa1, 0x3f, 0x23, 0x7d, 0xf7, 0xc6, 0x4c, 0x2b, 0x2b, 0x65, 0xdc, + 0x30, 0xdd, 0x12, 0x85, 0x6c, 0xb1, 0x25, 0xf9, 0xbb, 0x60, 0x34, 0xdc, 0x19, 0x8b, 0x92, 0xcf, + 0xf5, 0xdd, 0x59, 0x98, 0xcd, 0xcd, 0x1b, 0x33, 0x53, 0xfe, 0x8c, 0xf1, 0xc0, 0xb2, 0x92, 0xdd, + 0x09, 0xf4, 0xce, 0x8e, 0x77, 0x7d, 0xff, 0x53, 0x33, 0xd2, 0xa9, 0x2f, 0x4b, 0x00, 0xfe, 0xce, + 0x03, 0x7a, 0x08, 0x8e, 0x95, 0xd6, 0xd7, 0x16, 0x2b, 0x9b, 0x5b, 0xf3, 0x5b, 0xdb, 0x9b, 0x95, + 0xed, 0xb5, 0xcd, 0x8d, 0xf2, 0xc2, 0xf2, 0x85, 0xe5, 0xf2, 0xa2, 0xbf, 0x3d, 0xee, 0x58, 0x58, + 0xd3, 0x77, 0x75, 0x5c, 0x45, 0xf7, 0xc1, 0x54, 0x18, 0x9b, 0xb4, 0xca, 0x8b, 0x39, 0xa9, 0x90, + 0x7d, 0xe5, 0xfa, 0x6c, 0x9a, 0xd5, 0x62, 0xb8, 0x8a, 0x4e, 0xc2, 0x91, 0x56, 0xbc, 0xe5, 0xb5, + 0xa5, 0x5c, 0xa2, 0x30, 0xfa, 0xca, 0xf5, 0xd9, 0x8c, 0x57, 0xb4, 0x21, 0x19, 0x50, 0x10, 0x93, + 0xf3, 0x4b, 0x16, 0xe0, 0x95, 0xeb, 0xb3, 0x43, 0xcc, 0x80, 0x85, 0xd4, 0x07, 0x3e, 0x33, 0x3d, + 0x50, 0xba, 0xd0, 0x71, 0x03, 0xfc, 0xa1, 0xae, 0xb6, 0xbb, 0xe6, 0x6d, 0x6a, 0x87, 0x77, 0xbd, + 0xff, 0x78, 0xb8, 0xe3, 0xae, 0x77, 0x0d, 0x1b, 0xd8, 0xd1, 0x9d, 0x43, 0xed, 0x7a, 0xf7, 0xb4, + 0x93, 0x2e, 0xff, 0xce, 0x20, 0x64, 0x97, 0x58, 0x2f, 0x64, 0x20, 0x30, 0x7a, 0x13, 0x0c, 0x59, + 0x34, 0x8d, 0x78, 0xaf, 0xd1, 0x3a, 0x38, 0x3c, 0x4b, 0x36, 0xde, 0x59, 0x2e, 0x96, 0x7a, 0x1c, + 0x7e, 0x98, 0x83, 0x9d, 0x31, 0xf3, 0x4f, 0x4d, 0x65, 0xfb, 0xda, 0xef, 0x61, 0x35, 0x0b, 0xdf, + 0x5a, 0x89, 0xf2, 0x93, 0xd9, 0xb9, 0x90, 0x2d, 0x02, 0x61, 0xa7, 0xc3, 0xde, 0x27, 0xc1, 0x11, + 0x8a, 0xe5, 0x27, 0x62, 0x8a, 0x29, 0x8a, 0xfd, 0x53, 0x9d, 0x54, 0x58, 0x51, 0x1d, 0xff, 0xac, + 0x07, 0x3b, 0xcf, 0x75, 0x0f, 0x4f, 0x84, 0x27, 0x02, 0x9d, 0x47, 0xd9, 0xca, 0xca, 0x64, 0xbd, + 0x85, 0xd2, 0x41, 0x4b, 0xa1, 0x03, 0x7d, 0xa9, 0xfe, 0xb6, 0xda, 0x83, 0x87, 0xfb, 0x9e, 0x81, + 0x11, 0x3f, 0x96, 0x38, 0xfc, 0x7f, 0x53, 0xf4, 0x9e, 0x3b, 0x82, 0xc4, 0xe8, 0xfd, 0x12, 0x1c, + 0xf1, 0xb3, 0x79, 0x90, 0x2d, 0xfb, 0x1f, 0x1e, 0x0f, 0xf6, 0xb1, 0x10, 0x8a, 0x1a, 0xa7, 0x2d, + 0x5f, 0x59, 0x99, 0x6a, 0xb6, 0x92, 0x92, 0x25, 0xd8, 0x68, 0x30, 0xb2, 0x3a, 0x79, 0xf1, 0x99, + 0xba, 0xde, 0x43, 0x73, 0x98, 0x01, 0xfb, 0xbf, 0x02, 0x96, 0x69, 0xbb, 0xb8, 0x4a, 0x37, 0xe4, + 0xd2, 0x8a, 0xd7, 0x96, 0xd7, 0x00, 0xb5, 0x0e, 0x6e, 0xf4, 0x00, 0x63, 0xc6, 0x3f, 0xc0, 0x38, + 0x05, 0x83, 0xc1, 0x23, 0x7e, 0xac, 0x51, 0x4c, 0x7f, 0x80, 0xa7, 0xcf, 0x5b, 0x3e, 0xe7, 0xff, + 0x45, 0x02, 0x4e, 0x05, 0x5f, 0x0f, 0xbd, 0xd8, 0xc4, 0xf6, 0x81, 0x37, 0x45, 0x2d, 0xb5, 0xa6, + 0x1b, 0xc1, 0x1b, 0x41, 0xc7, 0x83, 0x09, 0x9f, 0xe2, 0x0a, 0x3b, 0xc9, 0x06, 0x8c, 0x6c, 0xa8, + 0x35, 0xac, 0xe0, 0x17, 0x9b, 0xd8, 0x71, 0xdb, 0x1c, 0x32, 0x3f, 0x0a, 0x43, 0xe6, 0xee, 0xae, + 0x78, 0xa5, 0x9d, 0x52, 0x78, 0x8b, 0xa8, 0x5c, 0xd7, 0x1b, 0x3a, 0x3b, 0x0d, 0x96, 0x52, 0x58, + 0x03, 0xcd, 0xc0, 0x88, 0x66, 0x36, 0x0d, 0x3e, 0xe3, 0xf2, 0x29, 0xf1, 0x39, 0x88, 0xa6, 0xc1, + 0x66, 0x9c, 0xfc, 0x34, 0x64, 0x59, 0x7f, 0x3c, 0xe3, 0x1e, 0x87, 0x34, 0x3d, 0x4e, 0xe5, 0xf7, + 0x3a, 0x4c, 0xda, 0x97, 0xd8, 0x81, 0x74, 0xc6, 0x85, 0x75, 0xcc, 0x1a, 0xa5, 0x52, 0x47, 0x53, + 0x9e, 0x8c, 0x0f, 0x0d, 0xcc, 0x50, 0x9e, 0x19, 0x7f, 0x73, 0x10, 0x8e, 0xf0, 0x37, 0x74, 0xaa, + 0xa5, 0x9f, 0xde, 0x73, 0x5d, 0x71, 0x59, 0x08, 0x78, 0xa9, 0xab, 0x5a, 0xba, 0x7c, 0x00, 0xa9, + 0x8b, 0xae, 0x6b, 0xa1, 0x53, 0x30, 0x68, 0x37, 0xeb, 0x58, 0xec, 0xf8, 0x78, 0x7b, 0xf2, 0xaa, + 0xa5, 0xcf, 0x11, 0x04, 0xa5, 0x59, 0xc7, 0x0a, 0x43, 0x41, 0x65, 0x98, 0xd9, 0x6d, 0xd6, 0xeb, + 0x07, 0x95, 0x2a, 0xa6, 0xff, 0xb7, 0xc7, 0xfb, 0xf2, 0x3d, 0xbe, 0x66, 0xa9, 0xe2, 0xfb, 0x79, + 0xc4, 0x36, 0x27, 0x28, 0xda, 0x22, 0xc5, 0x12, 0x5f, 0xbd, 0x2f, 0x0b, 0x1c, 0xf9, 0xf7, 0x12, + 0x90, 0x16, 0xac, 0xe9, 0x09, 0x71, 0x5c, 0xc7, 0x9a, 0x6b, 0x8a, 0x37, 0x26, 0x5e, 0x1b, 0x21, + 0x48, 0xd6, 0xf8, 0x10, 0x65, 0x2e, 0x0e, 0x28, 0xa4, 0x41, 0x60, 0xde, 0xb9, 0x7d, 0x02, 0xb3, + 0x9a, 0x64, 0xd4, 0x52, 0x96, 0x29, 0x96, 0x66, 0x17, 0x07, 0x14, 0xda, 0x42, 0x79, 0x18, 0x22, + 0x33, 0xc3, 0x65, 0x1f, 0x25, 0x24, 0x70, 0xde, 0x46, 0x47, 0x61, 0xd0, 0x52, 0x5d, 0x8d, 0x1d, + 0xa9, 0x23, 0x0f, 0x58, 0x13, 0x3d, 0x01, 0x43, 0xec, 0x7a, 0x68, 0xf4, 0x9f, 0x62, 0x10, 0x63, + 0xb0, 0xef, 0x70, 0x11, 0xb9, 0x37, 0x54, 0xd7, 0xc5, 0xb6, 0x41, 0x18, 0x32, 0x74, 0x84, 0x20, + 0xb5, 0x63, 0x56, 0x0f, 0xf8, 0x3f, 0xea, 0xa0, 0xbf, 0xf9, 0x7f, 0x06, 0xa0, 0xfe, 0x50, 0xa1, + 0x0f, 0xd9, 0xff, 0x27, 0xca, 0x0a, 0x60, 0x89, 0x20, 0x95, 0x61, 0x52, 0xad, 0x56, 0x75, 0xf6, + 0x3f, 0x33, 0x2a, 0x3b, 0x3a, 0x8d, 0x10, 0x0e, 0xfd, 0xef, 0x53, 0x9d, 0xc6, 0x02, 0xf9, 0x04, + 0x25, 0x8e, 0x5f, 0xca, 0xc0, 0xb0, 0xc5, 0x84, 0x92, 0xcf, 0xc3, 0x44, 0x8b, 0xa4, 0x44, 0xbe, + 0x7d, 0xdd, 0xa8, 0x8a, 0xcb, 0x0c, 0xe4, 0x37, 0x81, 0xd1, 0x2f, 0xe7, 0xb1, 0x77, 0x51, 0xf4, + 0x77, 0xe9, 0x3d, 0x9d, 0xef, 0x7f, 0x8d, 0x05, 0xee, 0x7f, 0xa9, 0x96, 0x5e, 0xca, 0x50, 0xfe, + 0xfc, 0xd6, 0xd7, 0x7c, 0xeb, 0xad, 0xaf, 0x1a, 0x36, 0x44, 0xf6, 0x25, 0x8f, 0x54, 0x4b, 0x77, + 0xa8, 0x3b, 0xfa, 0x5f, 0xf2, 0x73, 0xce, 0x07, 0x7e, 0xd3, 0x4b, 0x60, 0xa9, 0xa5, 0xf9, 0x8d, + 0x65, 0xcf, 0x8f, 0xbf, 0x9a, 0x80, 0x13, 0x01, 0x3f, 0x0e, 0x20, 0xb7, 0xba, 0x73, 0xa1, 0xbd, + 0xc7, 0xf7, 0x70, 0xf9, 0xeb, 0x12, 0xa4, 0x08, 0x3e, 0x8a, 0xf9, 0x6e, 0x7f, 0xfe, 0xd7, 0xbe, + 0xf1, 0x4f, 0xe5, 0xf0, 0x5b, 0xab, 0xd0, 0xa8, 0x50, 0x26, 0xa5, 0xf7, 0xf7, 0x6e, 0xbf, 0x9c, + 0xff, 0x11, 0x43, 0xe7, 0xd6, 0x99, 0x31, 0x6a, 0xc3, 0xef, 0x9c, 0x05, 0xb9, 0x43, 0xc9, 0xc3, + 0x22, 0x66, 0xf7, 0x22, 0xaa, 0x8f, 0x70, 0xdc, 0xe9, 0xfc, 0x7f, 0xb7, 0x11, 0xec, 0xb1, 0x1c, + 0xbb, 0x06, 0x47, 0x9f, 0x25, 0x7d, 0xfb, 0xcb, 0x64, 0x11, 0xd8, 0x8f, 0x7a, 0x6f, 0xf3, 0x24, + 0xfe, 0xcf, 0xbf, 0xc4, 0x9b, 0x3a, 0xf0, 0xe5, 0xe3, 0x0b, 0xc4, 0xfb, 0xe6, 0x3a, 0xe6, 0x8b, + 0xb9, 0x40, 0xb2, 0x50, 0x02, 0x94, 0xf2, 0xaf, 0x48, 0x70, 0xac, 0xa5, 0x6b, 0x1e, 0xe3, 0x97, + 0xda, 0x5c, 0x55, 0x38, 0x54, 0x65, 0xb3, 0xd4, 0x46, 0xd8, 0xfb, 0x63, 0x85, 0x65, 0x52, 0x84, + 0xa4, 0x7d, 0x0b, 0x1c, 0x09, 0x0b, 0x2b, 0xcc, 0x74, 0x2f, 0x8c, 0x85, 0x77, 0x84, 0xb9, 0xb9, + 0x46, 0x43, 0x7b, 0xc2, 0x72, 0x25, 0x6a, 0x67, 0x4f, 0xd7, 0x32, 0x64, 0x3c, 0x54, 0x5e, 0x02, + 0xf7, 0xac, 0xaa, 0x4f, 0x29, 0x7f, 0x58, 0x82, 0xd9, 0x70, 0x0f, 0x81, 0x62, 0xa8, 0x3f, 0x61, + 0x6f, 0xd9, 0x10, 0xbf, 0x2e, 0xc1, 0x5d, 0x5d, 0x64, 0xe2, 0x06, 0x78, 0x09, 0xa6, 0x02, 0x3b, + 0x01, 0x22, 0x84, 0x8b, 0x61, 0x3f, 0x15, 0x5f, 0x86, 0x7a, 0x0b, 0xdf, 0x3b, 0x88, 0x51, 0x3e, + 0xf7, 0xfb, 0x33, 0x93, 0xad, 0xcf, 0x1c, 0x65, 0xb2, 0x75, 0xf5, 0x7e, 0x0b, 0xfd, 0xe3, 0x55, + 0x09, 0x1e, 0x08, 0xab, 0xda, 0xa6, 0x9e, 0x7d, 0xa3, 0xc6, 0xe1, 0xdf, 0x4b, 0x70, 0xaa, 0x17, + 0xe1, 0xf8, 0x80, 0xec, 0xc0, 0xa4, 0x5f, 0x69, 0x47, 0xc7, 0xa3, 0xaf, 0xfa, 0x9d, 0x79, 0x29, + 0xf2, 0xb8, 0xdd, 0x06, 0xc3, 0x5b, 0x7c, 0x62, 0x05, 0x87, 0xdc, 0x33, 0x72, 0x78, 0x37, 0x57, + 0x18, 0x39, 0xb4, 0x9f, 0xdb, 0x66, 0x2c, 0x12, 0x6d, 0xc6, 0xc2, 0x2f, 0xcd, 0xe5, 0x2b, 0x3c, + 0x6e, 0xb5, 0xd9, 0x83, 0x7b, 0x3b, 0x4c, 0xb6, 0x71, 0x65, 0x3e, 0xab, 0xfb, 0xf0, 0x64, 0x05, + 0xb5, 0x3a, 0xab, 0x7c, 0x00, 0x33, 0xb4, 0xdf, 0x36, 0x86, 0xbe, 0xdd, 0x2a, 0x37, 0x78, 0x6c, + 0x69, 0xdb, 0x35, 0xd7, 0x7d, 0x19, 0x86, 0xd8, 0x38, 0x73, 0x75, 0x0f, 0xe1, 0x28, 0x9c, 0x81, + 0xfc, 0x71, 0x11, 0xcb, 0x16, 0x85, 0xd8, 0xed, 0xe7, 0x50, 0x2f, 0xba, 0xde, 0xa2, 0x39, 0x14, + 0x30, 0xc6, 0xb7, 0x44, 0x54, 0x6b, 0x2f, 0x1d, 0x37, 0x87, 0x76, 0xcb, 0xa2, 0x1a, 0xb3, 0xcd, + 0xed, 0x0d, 0x5f, 0xbf, 0x24, 0xc2, 0x97, 0xa7, 0x53, 0x4c, 0xf8, 0x7a, 0x63, 0x4c, 0xef, 0x05, + 0xb2, 0x18, 0x31, 0xff, 0x3c, 0x06, 0xb2, 0xef, 0x4b, 0x70, 0x9c, 0xea, 0x16, 0xdc, 0x88, 0xe8, + 0xd7, 0xe4, 0x0f, 0x01, 0x72, 0x6c, 0xad, 0xd2, 0x76, 0x76, 0xe7, 0x1c, 0x5b, 0xbb, 0x1c, 0xca, + 0x2f, 0x0f, 0x01, 0xaa, 0x86, 0xb6, 0x9b, 0x28, 0x36, 0x3b, 0x25, 0x97, 0xab, 0x06, 0x76, 0x33, + 0xda, 0x0c, 0x67, 0xea, 0x16, 0x0c, 0xe7, 0x37, 0x25, 0x28, 0xb4, 0x53, 0x99, 0x0f, 0x9f, 0x0e, + 0x47, 0x43, 0x2f, 0x09, 0xa2, 0x23, 0xf8, 0x50, 0x2f, 0x5b, 0x39, 0x91, 0x69, 0x74, 0xc4, 0xc6, + 0xb7, 0xbb, 0x0e, 0x98, 0x09, 0x7b, 0x68, 0x6b, 0x65, 0xfd, 0x86, 0x4d, 0x9f, 0x2f, 0xb5, 0xc4, + 0xd5, 0x3f, 0x17, 0xb5, 0xf7, 0x35, 0x98, 0xee, 0x20, 0xf5, 0xed, 0xce, 0x7b, 0x7b, 0x1d, 0x07, + 0xf3, 0x56, 0x97, 0xef, 0x8f, 0xf3, 0x99, 0x10, 0x3e, 0x81, 0x1d, 0x58, 0x8b, 0xb5, 0xbb, 0xc2, + 0x25, 0xbf, 0x15, 0xee, 0x68, 0x4b, 0xc5, 0x65, 0x2b, 0x42, 0x6a, 0x4f, 0x77, 0x5c, 0x2e, 0xd6, + 0x7d, 0x9d, 0xc4, 0x8a, 0x50, 0x53, 0x1a, 0x19, 0x41, 0x8e, 0xb2, 0xde, 0x30, 0xcd, 0x3a, 0x17, + 0x43, 0xbe, 0x04, 0x13, 0x01, 0x18, 0xef, 0xe4, 0x1c, 0xa4, 0x2c, 0x93, 0x7f, 0x9e, 0x60, 0xe4, + 0xcc, 0x89, 0x8e, 0xbb, 0xf7, 0xa6, 0x59, 0xe7, 0x6a, 0x53, 0x7c, 0x79, 0x0a, 0x10, 0x63, 0x46, + 0x37, 0xf2, 0x45, 0x17, 0x9b, 0x30, 0x19, 0x82, 0xf2, 0x4e, 0x7e, 0xa4, 0x97, 0x04, 0x67, 0xbe, + 0x7b, 0x04, 0x06, 0x29, 0x57, 0xf4, 0x31, 0x09, 0x20, 0xf0, 0x46, 0x78, 0xae, 0x13, 0x9b, 0xf6, + 0x6b, 0xe2, 0xc2, 0xe9, 0x9e, 0xf1, 0x79, 0xcd, 0x76, 0xea, 0x3d, 0xff, 0xe6, 0x3b, 0x1f, 0x49, + 0xdc, 0x83, 0xe4, 0xd3, 0x1d, 0x56, 0xe3, 0x81, 0xf9, 0xf2, 0xd9, 0xd0, 0xdd, 0xf7, 0x87, 0x7b, + 0xeb, 0x4a, 0x48, 0x36, 0xd7, 0x2b, 0x3a, 0x17, 0xec, 0x3c, 0x15, 0xec, 0x2c, 0x7a, 0x2c, 0x5e, + 0xb0, 0xd3, 0xef, 0x0c, 0x4f, 0x9a, 0x77, 0xa3, 0xdf, 0x91, 0x60, 0xaa, 0xdd, 0x92, 0x0e, 0x3d, + 0xd9, 0x9b, 0x14, 0xad, 0x25, 0x45, 0xe1, 0xa9, 0x43, 0x50, 0x72, 0x55, 0x96, 0xa8, 0x2a, 0xf3, + 0xe8, 0xe9, 0x43, 0xa8, 0x72, 0x3a, 0xb8, 0xbf, 0xff, 0xbf, 0x24, 0xb8, 0xb3, 0xeb, 0x0a, 0x09, + 0xcd, 0xf7, 0x26, 0x65, 0x97, 0xda, 0xa9, 0x50, 0xfa, 0x51, 0x58, 0x70, 0x8d, 0x9f, 0xa5, 0x1a, + 0x5f, 0x42, 0xcb, 0x87, 0xd1, 0xb8, 0xed, 0x4b, 0x14, 0xf4, 0x5b, 0xe1, 0x93, 0x85, 0xdd, 0xdd, + 0xa9, 0x65, 0xe1, 0x11, 0x33, 0x31, 0x5a, 0x8b, 0x5a, 0xf9, 0x79, 0xaa, 0x82, 0x82, 0x36, 0x7e, + 0xc4, 0x41, 0x3b, 0xfd, 0xce, 0x70, 0xe0, 0x7f, 0x37, 0xfa, 0x9f, 0x52, 0xfb, 0x83, 0x82, 0x4f, + 0x74, 0x15, 0xb1, 0xf3, 0xa2, 0xaa, 0xf0, 0x64, 0xff, 0x84, 0x5c, 0xc9, 0x06, 0x55, 0xb2, 0x86, + 0xf0, 0xad, 0x56, 0xb2, 0xed, 0x20, 0xa2, 0xaf, 0x4b, 0x30, 0xd5, 0x6e, 0x4d, 0x12, 0x33, 0x2d, + 0xbb, 0x2c, 0xb2, 0x62, 0xa6, 0x65, 0xb7, 0x05, 0x90, 0xfc, 0x26, 0xaa, 0xfc, 0x39, 0xf4, 0x78, + 0x27, 0xe5, 0xbb, 0x8e, 0x22, 0x99, 0x8b, 0x5d, 0x8b, 0xfc, 0x98, 0xb9, 0xd8, 0xcb, 0x3a, 0x26, + 0x66, 0x2e, 0xf6, 0xb4, 0xc6, 0x88, 0x9f, 0x8b, 0x9e, 0x66, 0x3d, 0x0e, 0xa3, 0x83, 0xbe, 0x2a, + 0xc1, 0x68, 0xa8, 0x22, 0x46, 0x8f, 0x76, 0x15, 0xb4, 0xdd, 0x82, 0xa1, 0x70, 0xa6, 0x1f, 0x12, + 0xae, 0xcb, 0x32, 0xd5, 0x65, 0x01, 0xcd, 0x1f, 0x46, 0x97, 0xf0, 0xbb, 0xd2, 0x6f, 0x4a, 0x30, + 0xd9, 0xa6, 0xca, 0x8c, 0x99, 0x85, 0x9d, 0x8b, 0xe6, 0xc2, 0x93, 0xfd, 0x13, 0x72, 0xad, 0x2e, + 0x50, 0xad, 0x7e, 0x0a, 0xbd, 0xe5, 0x30, 0x5a, 0x05, 0xf2, 0xf3, 0x0d, 0xff, 0xdc, 0x55, 0xa0, + 0x1f, 0x74, 0xae, 0x4f, 0xc1, 0x84, 0x42, 0x4f, 0xf4, 0x4d, 0xc7, 0xf5, 0x79, 0x8e, 0xea, 0xf3, + 0x2c, 0x5a, 0xff, 0xd1, 0xf4, 0x69, 0x4d, 0xeb, 0x5f, 0x6c, 0xbd, 0x01, 0xd8, 0xdd, 0x8b, 0xda, + 0x16, 0xab, 0x85, 0xc7, 0xfa, 0xa2, 0xe1, 0x4a, 0x3d, 0x49, 0x95, 0x3a, 0x83, 0x1e, 0xe9, 0xa4, + 0x54, 0xe0, 0x70, 0x9d, 0x6e, 0xec, 0x9a, 0xa7, 0xdf, 0xc9, 0x4a, 0xe0, 0x77, 0xa3, 0x9f, 0x11, + 0x07, 0x9b, 0x4e, 0x76, 0xed, 0x37, 0x50, 0xc7, 0x16, 0x1e, 0xe8, 0x01, 0x93, 0xcb, 0x75, 0x0f, + 0x95, 0x6b, 0x1a, 0x9d, 0xe8, 0x24, 0x17, 0xa9, 0x65, 0xd1, 0x07, 0x25, 0xef, 0x2c, 0xe4, 0xa9, + 0xee, 0xbc, 0x83, 0xc5, 0x6e, 0xe1, 0xc1, 0x9e, 0x70, 0xb9, 0x24, 0xf7, 0x51, 0x49, 0x66, 0xd1, + 0x74, 0x47, 0x49, 0x58, 0xe9, 0x7b, 0xab, 0x4f, 0x0e, 0xbc, 0x72, 0x0c, 0x66, 0x3a, 0xf4, 0xe8, + 0x5e, 0x8b, 0x79, 0xc7, 0xd5, 0xe5, 0x22, 0x6c, 0xec, 0x45, 0xd7, 0x0e, 0x57, 0x6b, 0x0f, 0x7f, + 0xfd, 0xb5, 0xb7, 0x17, 0x62, 0xff, 0x3a, 0x05, 0x68, 0xd5, 0xa9, 0x2d, 0xd8, 0x98, 0xfd, 0xc3, + 0x3b, 0x3e, 0xcb, 0x23, 0x37, 0xbc, 0xa4, 0x1f, 0xe9, 0x86, 0xd7, 0x6a, 0xe8, 0xce, 0x54, 0xa2, + 0xbf, 0x7b, 0x99, 0x3d, 0x5f, 0x9c, 0x4a, 0xfe, 0x58, 0x2e, 0x4e, 0xb5, 0x3f, 0x57, 0x9d, 0xba, + 0x75, 0x17, 0x30, 0x06, 0x0f, 0x7b, 0x09, 0x85, 0xdf, 0x87, 0x1c, 0xea, 0x72, 0x1f, 0x32, 0xdf, + 0xf1, 0xd2, 0x23, 0xa7, 0x46, 0x67, 0xc5, 0xe7, 0x7c, 0x87, 0x7b, 0x3b, 0x09, 0xcb, 0xbf, 0xf7, + 0xeb, 0x6f, 0x21, 0x9c, 0x80, 0x42, 0xab, 0x3b, 0x79, 0x93, 0xfa, 0x23, 0x49, 0xc8, 0xad, 0x3a, + 0xb5, 0x72, 0x55, 0x77, 0x6f, 0x93, 0xaf, 0x3d, 0xdd, 0xf9, 0x52, 0x0b, 0xba, 0x79, 0x63, 0x66, + 0x8c, 0xd9, 0xb4, 0x8b, 0x25, 0x1b, 0x30, 0x1e, 0xb9, 0x4a, 0xcc, 0x3d, 0x6b, 0xf1, 0x30, 0x37, + 0x9a, 0x23, 0xac, 0x64, 0x7a, 0x07, 0x21, 0xe0, 0xdf, 0xe8, 0x5a, 0x7b, 0x67, 0x66, 0x0e, 0x75, + 0xf1, 0x76, 0xde, 0x00, 0xf4, 0xc7, 0xac, 0x00, 0xf9, 0xe8, 0xa0, 0x78, 0x23, 0xf6, 0x87, 0x12, + 0x8c, 0xac, 0x3a, 0xa2, 0x14, 0xc4, 0x3f, 0xa1, 0xf7, 0x8f, 0x9e, 0xf0, 0xbe, 0xc3, 0x9a, 0xec, + 0xcd, 0x6f, 0xc5, 0xb7, 0x59, 0x7d, 0x23, 0x1c, 0x81, 0xc9, 0x80, 0x9e, 0x9e, 0xfe, 0xbf, 0x9d, + 0xa0, 0xf1, 0xb1, 0x84, 0x6b, 0xba, 0xe1, 0x55, 0x91, 0xf8, 0x2f, 0xea, 0xed, 0x0a, 0xdf, 0xce, + 0xa9, 0xc3, 0xda, 0x79, 0x9f, 0x06, 0x88, 0x88, 0x3d, 0xbd, 0x8d, 0xaf, 0xd5, 0xd6, 0xbb, 0x3f, + 0x52, 0x1f, 0x9f, 0xd5, 0x89, 0xdc, 0xf0, 0x91, 0x5f, 0x97, 0x60, 0x74, 0xd5, 0xa9, 0x6d, 0x1b, + 0xd5, 0xff, 0xe7, 0xfd, 0x77, 0x17, 0x8e, 0x84, 0x34, 0xbd, 0x4d, 0x26, 0x3d, 0xf3, 0x6a, 0x0a, + 0x92, 0xab, 0x4e, 0x0d, 0xbd, 0x08, 0xe3, 0xd1, 0xa2, 0xa1, 0x63, 0x2d, 0xd8, 0x9a, 0x11, 0x3a, + 0xaf, 0xd7, 0x3a, 0x67, 0x0f, 0xb4, 0x0f, 0xa3, 0xe1, 0xcc, 0x71, 0xb2, 0x0b, 0x93, 0x10, 0x66, + 0xe1, 0x91, 0x5e, 0x31, 0xbd, 0xce, 0xde, 0x01, 0x69, 0x2f, 0xe8, 0xdd, 0xdd, 0x85, 0x5a, 0x20, + 0x75, 0xae, 0x6e, 0xdb, 0x84, 0x15, 0x62, 0xbd, 0x68, 0x48, 0xe9, 0x66, 0xbd, 0x08, 0x6e, 0x57, + 0xeb, 0x75, 0x9a, 0x5a, 0x3b, 0x00, 0x81, 0x79, 0x70, 0x6f, 0x17, 0x0e, 0x3e, 0x5a, 0xe1, 0xe1, + 0x9e, 0xd0, 0xbc, 0x97, 0x4e, 0xb7, 0xb8, 0x18, 0xff, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x61, + 0x21, 0x94, 0x2e, 0x66, 0x94, 0x00, 0x00, + } + r := bytes.NewReader(gzipped) + gzipr, err := compress_gzip.NewReader(r) + if err != nil { + panic(err) + } + ungzipped, err := io_ioutil.ReadAll(gzipr) + if err != nil { + panic(err) + } + if err := github_com_gogo_protobuf_proto.Unmarshal(ungzipped, d); err != nil { + panic(err) + } + return d +} +func (this *CommissionRates) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CommissionRates) + if !ok { + that2, ok := that.(CommissionRates) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Rate.Equal(that1.Rate) { + return false + } + if !this.MaxRate.Equal(that1.MaxRate) { + return false + } + if !this.MaxChangeRate.Equal(that1.MaxChangeRate) { + return false + } + return true +} +func (this *Commission) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Commission) + if !ok { + that2, ok := that.(Commission) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.CommissionRates.Equal(&that1.CommissionRates) { + return false + } + if !this.UpdateTime.Equal(that1.UpdateTime) { + return false + } + return true +} +func (this *Description) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Description) + if !ok { + that2, ok := that.(Description) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Moniker != that1.Moniker { + return false + } + if this.Identity != that1.Identity { + return false + } + if this.Website != that1.Website { + return false + } + if this.SecurityContact != that1.SecurityContact { + return false + } + if this.Details != that1.Details { + return false + } + return true +} +func (this *UnbondingDelegationEntry) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*UnbondingDelegationEntry) + if !ok { + that2, ok := that.(UnbondingDelegationEntry) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.CreationHeight != that1.CreationHeight { + return false + } + if !this.CompletionTime.Equal(that1.CompletionTime) { + return false + } + if !this.InitialBalance.Equal(that1.InitialBalance) { + return false + } + if !this.Balance.Equal(that1.Balance) { + return false + } + return true +} +func (this *RedelegationEntry) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*RedelegationEntry) + if !ok { + that2, ok := that.(RedelegationEntry) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.CreationHeight != that1.CreationHeight { + return false + } + if !this.CompletionTime.Equal(that1.CompletionTime) { + return false + } + if !this.InitialBalance.Equal(that1.InitialBalance) { + return false + } + if !this.SharesDst.Equal(that1.SharesDst) { + return false + } + return true +} +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.UnbondingTime != that1.UnbondingTime { + return false + } + if this.MaxValidators != that1.MaxValidators { + return false + } + if this.MaxEntries != that1.MaxEntries { + return false + } + if this.HistoricalEntries != that1.HistoricalEntries { + return false + } + if this.BondDenom != that1.BondDenom { + return false + } + return true +} +func (this *RedelegationEntryResponse) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*RedelegationEntryResponse) + if !ok { + that2, ok := that.(RedelegationEntryResponse) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.RedelegationEntry.Equal(&that1.RedelegationEntry) { + return false + } + if !this.Balance.Equal(that1.Balance) { + return false + } + return true +} +func (this *Pool) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Pool) + if !ok { + that2, ok := that.(Pool) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.NotBondedTokens.Equal(that1.NotBondedTokens) { + return false + } + if !this.BondedTokens.Equal(that1.BondedTokens) { + return false + } + return true +} +func (m *HistoricalInfo) 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 *HistoricalInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HistoricalInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Valset) > 0 { + for iNdEx := len(m.Valset) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Valset[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *CommissionRates) 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 *CommissionRates) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommissionRates) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.MaxChangeRate.Size() + i -= size + if _, err := m.MaxChangeRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.MaxRate.Size() + i -= size + if _, err := m.MaxRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.Rate.Size() + i -= size + if _, err := m.Rate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Commission) 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 *Commission) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Commission) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdateTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdateTime):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintStaking(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x12 + { + size, err := m.CommissionRates.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Description) 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 *Description) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Description) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Details) > 0 { + i -= len(m.Details) + copy(dAtA[i:], m.Details) + i = encodeVarintStaking(dAtA, i, uint64(len(m.Details))) + i-- + dAtA[i] = 0x2a + } + if len(m.SecurityContact) > 0 { + i -= len(m.SecurityContact) + copy(dAtA[i:], m.SecurityContact) + i = encodeVarintStaking(dAtA, i, uint64(len(m.SecurityContact))) + i-- + dAtA[i] = 0x22 + } + if len(m.Website) > 0 { + i -= len(m.Website) + copy(dAtA[i:], m.Website) + i = encodeVarintStaking(dAtA, i, uint64(len(m.Website))) + i-- + dAtA[i] = 0x1a + } + if len(m.Identity) > 0 { + i -= len(m.Identity) + copy(dAtA[i:], m.Identity) + i = encodeVarintStaking(dAtA, i, uint64(len(m.Identity))) + i-- + dAtA[i] = 0x12 + } + if len(m.Moniker) > 0 { + i -= len(m.Moniker) + copy(dAtA[i:], m.Moniker) + i = encodeVarintStaking(dAtA, i, uint64(len(m.Moniker))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Validator) 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 *Validator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.MinSelfDelegation.Size() + i -= size + if _, err := m.MinSelfDelegation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + { + size, err := m.Commission.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UnbondingTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UnbondingTime):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintStaking(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0x4a + if m.UnbondingHeight != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.UnbondingHeight)) + i-- + dAtA[i] = 0x40 + } + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + { + size := m.DelegatorShares.Size() + i -= size + if _, err := m.DelegatorShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + { + size := m.Tokens.Size() + i -= size + if _, err := m.Tokens.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if m.Status != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.Jailed { + i-- + if m.Jailed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.ConsensusPubkey != nil { + { + size, err := m.ConsensusPubkey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.OperatorAddress) > 0 { + i -= len(m.OperatorAddress) + copy(dAtA[i:], m.OperatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.OperatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ValAddresses) 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 *ValAddresses) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ValAddresses) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintStaking(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *DVPair) 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 *DVPair) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DVPair) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DVPairs) 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 *DVPairs) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DVPairs) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pairs) > 0 { + for iNdEx := len(m.Pairs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Pairs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *DVVTriplet) 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 *DVVTriplet) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DVVTriplet) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ValidatorDstAddress) > 0 { + i -= len(m.ValidatorDstAddress) + copy(dAtA[i:], m.ValidatorDstAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorDstAddress))) + i-- + dAtA[i] = 0x1a + } + if len(m.ValidatorSrcAddress) > 0 { + i -= len(m.ValidatorSrcAddress) + copy(dAtA[i:], m.ValidatorSrcAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorSrcAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DVVTriplets) 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 *DVVTriplets) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DVVTriplets) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Triplets) > 0 { + for iNdEx := len(m.Triplets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Triplets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Delegation) 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 *Delegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Delegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Shares.Size() + i -= size + if _, err := m.Shares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnbondingDelegation) 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 *UnbondingDelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UnbondingDelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Entries) > 0 { + for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnbondingDelegationEntry) 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 *UnbondingDelegationEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UnbondingDelegationEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Balance.Size() + i -= size + if _, err := m.Balance.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.InitialBalance.Size() + i -= size + if _, err := m.InitialBalance.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CompletionTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintStaking(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0x12 + if m.CreationHeight != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.CreationHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *RedelegationEntry) 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 *RedelegationEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RedelegationEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.SharesDst.Size() + i -= size + if _, err := m.SharesDst.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.InitialBalance.Size() + i -= size + if _, err := m.InitialBalance.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CompletionTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime):]) + if err9 != nil { + return 0, err9 + } + i -= n9 + i = encodeVarintStaking(dAtA, i, uint64(n9)) + i-- + dAtA[i] = 0x12 + if m.CreationHeight != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.CreationHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Redelegation) 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 *Redelegation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Redelegation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Entries) > 0 { + for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.ValidatorDstAddress) > 0 { + i -= len(m.ValidatorDstAddress) + copy(dAtA[i:], m.ValidatorDstAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorDstAddress))) + i-- + dAtA[i] = 0x1a + } + if len(m.ValidatorSrcAddress) > 0 { + i -= len(m.ValidatorSrcAddress) + copy(dAtA[i:], m.ValidatorSrcAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.ValidatorSrcAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintStaking(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BondDenom) > 0 { + i -= len(m.BondDenom) + copy(dAtA[i:], m.BondDenom) + i = encodeVarintStaking(dAtA, i, uint64(len(m.BondDenom))) + i-- + dAtA[i] = 0x2a + } + if m.HistoricalEntries != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.HistoricalEntries)) + i-- + dAtA[i] = 0x20 + } + if m.MaxEntries != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.MaxEntries)) + i-- + dAtA[i] = 0x18 + } + if m.MaxValidators != 0 { + i = encodeVarintStaking(dAtA, i, uint64(m.MaxValidators)) + i-- + dAtA[i] = 0x10 + } + n10, err10 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.UnbondingTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingTime):]) + if err10 != nil { + return 0, err10 + } + i -= n10 + i = encodeVarintStaking(dAtA, i, uint64(n10)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *DelegationResponse) 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 *DelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Balance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Delegation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *RedelegationEntryResponse) 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 *RedelegationEntryResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RedelegationEntryResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Balance.Size() + i -= size + if _, err := m.Balance.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size, err := m.RedelegationEntry.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *RedelegationResponse) 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 *RedelegationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RedelegationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Entries) > 0 { + for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.Redelegation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Pool) 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 *Pool) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.BondedTokens.Size() + i -= size + if _, err := m.BondedTokens.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.NotBondedTokens.Size() + i -= size + if _, err := m.NotBondedTokens.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintStaking(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintStaking(dAtA []byte, offset int, v uint64) int { + offset -= sovStaking(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *HistoricalInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Header.Size() + n += 1 + l + sovStaking(uint64(l)) + if len(m.Valset) > 0 { + for _, e := range m.Valset { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *CommissionRates) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Rate.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.MaxRate.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.MaxChangeRate.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *Commission) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.CommissionRates.Size() + n += 1 + l + sovStaking(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdateTime) + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *Description) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Moniker) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.Identity) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.Website) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.SecurityContact) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.Details) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + return n +} + +func (m *Validator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.OperatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + if m.ConsensusPubkey != nil { + l = m.ConsensusPubkey.Size() + n += 1 + l + sovStaking(uint64(l)) + } + if m.Jailed { + n += 2 + } + if m.Status != 0 { + n += 1 + sovStaking(uint64(m.Status)) + } + l = m.Tokens.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.DelegatorShares.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.Description.Size() + n += 1 + l + sovStaking(uint64(l)) + if m.UnbondingHeight != 0 { + n += 1 + sovStaking(uint64(m.UnbondingHeight)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UnbondingTime) + n += 1 + l + sovStaking(uint64(l)) + l = m.Commission.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.MinSelfDelegation.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *ValAddresses) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *DVPair) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + return n +} + +func (m *DVPairs) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Pairs) > 0 { + for _, e := range m.Pairs { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *DVVTriplet) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorSrcAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorDstAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + return n +} + +func (m *DVVTriplets) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Triplets) > 0 { + for _, e := range m.Triplets { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *Delegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = m.Shares.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *UnbondingDelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + if len(m.Entries) > 0 { + for _, e := range m.Entries { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *UnbondingDelegationEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CreationHeight != 0 { + n += 1 + sovStaking(uint64(m.CreationHeight)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime) + n += 1 + l + sovStaking(uint64(l)) + l = m.InitialBalance.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.Balance.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *RedelegationEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CreationHeight != 0 { + n += 1 + sovStaking(uint64(m.CreationHeight)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime) + n += 1 + l + sovStaking(uint64(l)) + l = m.InitialBalance.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.SharesDst.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *Redelegation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorSrcAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + l = len(m.ValidatorDstAddress) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + if len(m.Entries) > 0 { + for _, e := range m.Entries { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.UnbondingTime) + n += 1 + l + sovStaking(uint64(l)) + if m.MaxValidators != 0 { + n += 1 + sovStaking(uint64(m.MaxValidators)) + } + if m.MaxEntries != 0 { + n += 1 + sovStaking(uint64(m.MaxEntries)) + } + if m.HistoricalEntries != 0 { + n += 1 + sovStaking(uint64(m.HistoricalEntries)) + } + l = len(m.BondDenom) + if l > 0 { + n += 1 + l + sovStaking(uint64(l)) + } + return n +} + +func (m *DelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Delegation.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.Balance.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *RedelegationEntryResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.RedelegationEntry.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.Balance.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func (m *RedelegationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Redelegation.Size() + n += 1 + l + sovStaking(uint64(l)) + if len(m.Entries) > 0 { + for _, e := range m.Entries { + l = e.Size() + n += 1 + l + sovStaking(uint64(l)) + } + } + return n +} + +func (m *Pool) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.NotBondedTokens.Size() + n += 1 + l + sovStaking(uint64(l)) + l = m.BondedTokens.Size() + n += 1 + l + sovStaking(uint64(l)) + return n +} + +func sovStaking(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozStaking(x uint64) (n int) { + return sovStaking(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ValAddresses) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ValAddresses{`, + `Addresses:` + fmt.Sprintf("%v", this.Addresses) + `,`, + `}`, + }, "") + return s +} +func valueToStringStaking(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *HistoricalInfo) 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 ErrIntOverflowStaking + } + 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: HistoricalInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HistoricalInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Valset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Valset = append(m.Valset, Validator{}) + if err := m.Valset[len(m.Valset)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommissionRates) 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 ErrIntOverflowStaking + } + 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: CommissionRates: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommissionRates: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Rate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MaxRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxChangeRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MaxChangeRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Commission) 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 ErrIntOverflowStaking + } + 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: Commission: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Commission: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommissionRates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CommissionRates.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdateTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Description) 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 ErrIntOverflowStaking + } + 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: Description: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Description: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Moniker", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Moniker = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Identity", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Identity = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Website", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Website = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SecurityContact", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SecurityContact = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Details", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Details = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Validator) 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 ErrIntOverflowStaking + } + 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: Validator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Validator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OperatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OperatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsensusPubkey", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConsensusPubkey == nil { + m.ConsensusPubkey = &types1.Any{} + } + if err := m.ConsensusPubkey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Jailed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Jailed = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= BondStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Tokens.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorShares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DelegatorShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingHeight", wireType) + } + m.UnbondingHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnbondingHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UnbondingTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Commission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinSelfDelegation", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinSelfDelegation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValAddresses) 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 ErrIntOverflowStaking + } + 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: ValAddresses: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValAddresses: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DVPair) 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 ErrIntOverflowStaking + } + 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: DVPair: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DVPair: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DVPairs) 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 ErrIntOverflowStaking + } + 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: DVPairs: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DVPairs: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pairs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pairs = append(m.Pairs, DVPair{}) + if err := m.Pairs[len(m.Pairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DVVTriplet) 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 ErrIntOverflowStaking + } + 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: DVVTriplet: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DVVTriplet: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSrcAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorSrcAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorDstAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorDstAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DVVTriplets) 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 ErrIntOverflowStaking + } + 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: DVVTriplets: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DVVTriplets: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Triplets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Triplets = append(m.Triplets, DVVTriplet{}) + if err := m.Triplets[len(m.Triplets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Delegation) 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 ErrIntOverflowStaking + } + 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: Delegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Delegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Shares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Shares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnbondingDelegation) 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 ErrIntOverflowStaking + } + 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: UnbondingDelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnbondingDelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Entries = append(m.Entries, UnbondingDelegationEntry{}) + if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnbondingDelegationEntry) 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 ErrIntOverflowStaking + } + 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: UnbondingDelegationEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnbondingDelegationEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationHeight", wireType) + } + m.CreationHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreationHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletionTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CompletionTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialBalance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InitialBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RedelegationEntry) 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 ErrIntOverflowStaking + } + 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: RedelegationEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RedelegationEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreationHeight", wireType) + } + m.CreationHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreationHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletionTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CompletionTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialBalance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InitialBalance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SharesDst", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SharesDst.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Redelegation) 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 ErrIntOverflowStaking + } + 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: Redelegation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Redelegation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSrcAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorSrcAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorDstAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorDstAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Entries = append(m.Entries, RedelegationEntry{}) + if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) 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 ErrIntOverflowStaking + } + 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: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UnbondingTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.UnbondingTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxValidators", wireType) + } + m.MaxValidators = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxValidators |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxEntries", wireType) + } + m.MaxEntries = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxEntries |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HistoricalEntries", wireType) + } + m.HistoricalEntries = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HistoricalEntries |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BondDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BondDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DelegationResponse) 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 ErrIntOverflowStaking + } + 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: DelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DelegationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Delegation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Delegation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + 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 ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RedelegationEntryResponse) 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 ErrIntOverflowStaking + } + 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: RedelegationEntryResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RedelegationEntryResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RedelegationEntry", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RedelegationEntry.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Balance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RedelegationResponse) 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 ErrIntOverflowStaking + } + 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: RedelegationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RedelegationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Redelegation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Redelegation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStaking + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Entries = append(m.Entries, RedelegationEntryResponse{}) + if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Pool) 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 ErrIntOverflowStaking + } + 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: Pool: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Pool: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NotBondedTokens", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.NotBondedTokens.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BondedTokens", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStaking + } + 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 ErrInvalidLengthStaking + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStaking + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BondedTokens.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStaking(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStaking + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipStaking(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, ErrIntOverflowStaking + } + 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, ErrIntOverflowStaking + } + 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, ErrIntOverflowStaking + } + 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, ErrInvalidLengthStaking + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupStaking + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthStaking + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthStaking = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowStaking = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupStaking = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/staking/types/test_utils.go b/x/staking/types/test_utils.go deleted file mode 100644 index 9ab8aabfe82c..000000000000 --- a/x/staking/types/test_utils.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -import ( - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// nolint:deadcode,unused -var ( - pk1 = ed25519.GenPrivKey().PubKey() - pk2 = ed25519.GenPrivKey().PubKey() - pk3 = ed25519.GenPrivKey().PubKey() - addr1 = pk1.Address() - addr2 = pk2.Address() - addr3 = pk3.Address() - valAddr1 = sdk.ValAddress(addr1) - valAddr2 = sdk.ValAddress(addr2) - valAddr3 = sdk.ValAddress(addr3) - - emptyAddr sdk.ValAddress - emptyPubkey crypto.PubKey -) diff --git a/x/staking/types/tx.pb.go b/x/staking/types/tx.pb.go new file mode 100644 index 000000000000..c3a1bf706599 --- /dev/null +++ b/x/staking/types/tx.pb.go @@ -0,0 +1,2719 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/staking/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/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/regen-network/cosmos-proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// MsgCreateValidator defines a SDK message for creating a new validator. +type MsgCreateValidator struct { + Description Description `protobuf:"bytes,1,opt,name=description,proto3" json:"description"` + Commission CommissionRates `protobuf:"bytes,2,opt,name=commission,proto3" json:"commission"` + MinSelfDelegation github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=min_self_delegation,json=minSelfDelegation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_self_delegation" yaml:"min_self_delegation"` + DelegatorAddress string `protobuf:"bytes,4,opt,name=delegator_address,json=delegatorAddress,proto3" json:"delegator_address,omitempty" yaml:"delegator_address"` + ValidatorAddress string `protobuf:"bytes,5,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"validator_address"` + Pubkey *types.Any `protobuf:"bytes,6,opt,name=pubkey,proto3" json:"pubkey,omitempty"` + Value types1.Coin `protobuf:"bytes,7,opt,name=value,proto3" json:"value"` +} + +func (m *MsgCreateValidator) Reset() { *m = MsgCreateValidator{} } +func (m *MsgCreateValidator) String() string { return proto.CompactTextString(m) } +func (*MsgCreateValidator) ProtoMessage() {} +func (*MsgCreateValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{0} +} +func (m *MsgCreateValidator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateValidator.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 *MsgCreateValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateValidator.Merge(m, src) +} +func (m *MsgCreateValidator) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateValidator) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateValidator.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateValidator proto.InternalMessageInfo + +// MsgCreateValidatorResponse defines the Msg/CreateValidator response type. +type MsgCreateValidatorResponse struct { +} + +func (m *MsgCreateValidatorResponse) Reset() { *m = MsgCreateValidatorResponse{} } +func (m *MsgCreateValidatorResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateValidatorResponse) ProtoMessage() {} +func (*MsgCreateValidatorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{1} +} +func (m *MsgCreateValidatorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateValidatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateValidatorResponse.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 *MsgCreateValidatorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateValidatorResponse.Merge(m, src) +} +func (m *MsgCreateValidatorResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateValidatorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateValidatorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateValidatorResponse proto.InternalMessageInfo + +// MsgEditValidator defines a SDK message for editing an existing validator. +type MsgEditValidator struct { + Description Description `protobuf:"bytes,1,opt,name=description,proto3" json:"description"` + ValidatorAddress string `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty" yaml:"address"` + // We pass a reference to the new commission rate and min self delegation as + // it's not mandatory to update. If not updated, the deserialized rate will be + // zero with no way to distinguish if an update was intended. + // REF: #2373 + CommissionRate *github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=commission_rate,json=commissionRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"commission_rate,omitempty" yaml:"commission_rate"` + MinSelfDelegation *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_self_delegation,json=minSelfDelegation,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_self_delegation,omitempty" yaml:"min_self_delegation"` +} + +func (m *MsgEditValidator) Reset() { *m = MsgEditValidator{} } +func (m *MsgEditValidator) String() string { return proto.CompactTextString(m) } +func (*MsgEditValidator) ProtoMessage() {} +func (*MsgEditValidator) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{2} +} +func (m *MsgEditValidator) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEditValidator) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEditValidator.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 *MsgEditValidator) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEditValidator.Merge(m, src) +} +func (m *MsgEditValidator) XXX_Size() int { + return m.Size() +} +func (m *MsgEditValidator) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEditValidator.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEditValidator proto.InternalMessageInfo + +// MsgEditValidatorResponse defines the Msg/EditValidator response type. +type MsgEditValidatorResponse struct { +} + +func (m *MsgEditValidatorResponse) Reset() { *m = MsgEditValidatorResponse{} } +func (m *MsgEditValidatorResponse) String() string { return proto.CompactTextString(m) } +func (*MsgEditValidatorResponse) ProtoMessage() {} +func (*MsgEditValidatorResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{3} +} +func (m *MsgEditValidatorResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEditValidatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEditValidatorResponse.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 *MsgEditValidatorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEditValidatorResponse.Merge(m, src) +} +func (m *MsgEditValidatorResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgEditValidatorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEditValidatorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEditValidatorResponse proto.InternalMessageInfo + +// MsgDelegate defines a SDK message for performing a delegation of coins +// from a delegator to a validator. +type MsgDelegate 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"` + Amount types1.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgDelegate) Reset() { *m = MsgDelegate{} } +func (m *MsgDelegate) String() string { return proto.CompactTextString(m) } +func (*MsgDelegate) ProtoMessage() {} +func (*MsgDelegate) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{4} +} +func (m *MsgDelegate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegate.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 *MsgDelegate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegate.Merge(m, src) +} +func (m *MsgDelegate) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegate) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegate.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegate proto.InternalMessageInfo + +// MsgDelegateResponse defines the Msg/Delegate response type. +type MsgDelegateResponse struct { +} + +func (m *MsgDelegateResponse) Reset() { *m = MsgDelegateResponse{} } +func (m *MsgDelegateResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDelegateResponse) ProtoMessage() {} +func (*MsgDelegateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{5} +} +func (m *MsgDelegateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelegateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelegateResponse.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 *MsgDelegateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelegateResponse.Merge(m, src) +} +func (m *MsgDelegateResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDelegateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelegateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelegateResponse proto.InternalMessageInfo + +// MsgBeginRedelegate defines a SDK message for performing a redelegation +// of coins from a delegator and source validator to a destination validator. +type MsgBeginRedelegate 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"` + Amount types1.Coin `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgBeginRedelegate) Reset() { *m = MsgBeginRedelegate{} } +func (m *MsgBeginRedelegate) String() string { return proto.CompactTextString(m) } +func (*MsgBeginRedelegate) ProtoMessage() {} +func (*MsgBeginRedelegate) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{6} +} +func (m *MsgBeginRedelegate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBeginRedelegate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBeginRedelegate.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 *MsgBeginRedelegate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBeginRedelegate.Merge(m, src) +} +func (m *MsgBeginRedelegate) XXX_Size() int { + return m.Size() +} +func (m *MsgBeginRedelegate) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBeginRedelegate.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBeginRedelegate proto.InternalMessageInfo + +// MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type. +type MsgBeginRedelegateResponse struct { + CompletionTime time.Time `protobuf:"bytes,1,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time"` +} + +func (m *MsgBeginRedelegateResponse) Reset() { *m = MsgBeginRedelegateResponse{} } +func (m *MsgBeginRedelegateResponse) String() string { return proto.CompactTextString(m) } +func (*MsgBeginRedelegateResponse) ProtoMessage() {} +func (*MsgBeginRedelegateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{7} +} +func (m *MsgBeginRedelegateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBeginRedelegateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBeginRedelegateResponse.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 *MsgBeginRedelegateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBeginRedelegateResponse.Merge(m, src) +} +func (m *MsgBeginRedelegateResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgBeginRedelegateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBeginRedelegateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBeginRedelegateResponse proto.InternalMessageInfo + +func (m *MsgBeginRedelegateResponse) GetCompletionTime() time.Time { + if m != nil { + return m.CompletionTime + } + return time.Time{} +} + +// MsgUndelegate defines a SDK message for performing an undelegation from a +// delegate and a validator. +type MsgUndelegate 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"` + Amount types1.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"` +} + +func (m *MsgUndelegate) Reset() { *m = MsgUndelegate{} } +func (m *MsgUndelegate) String() string { return proto.CompactTextString(m) } +func (*MsgUndelegate) ProtoMessage() {} +func (*MsgUndelegate) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{8} +} +func (m *MsgUndelegate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUndelegate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUndelegate.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 *MsgUndelegate) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUndelegate.Merge(m, src) +} +func (m *MsgUndelegate) XXX_Size() int { + return m.Size() +} +func (m *MsgUndelegate) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUndelegate.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUndelegate proto.InternalMessageInfo + +// MsgUndelegateResponse defines the Msg/Undelegate response type. +type MsgUndelegateResponse struct { + CompletionTime time.Time `protobuf:"bytes,1,opt,name=completion_time,json=completionTime,proto3,stdtime" json:"completion_time"` +} + +func (m *MsgUndelegateResponse) Reset() { *m = MsgUndelegateResponse{} } +func (m *MsgUndelegateResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUndelegateResponse) ProtoMessage() {} +func (*MsgUndelegateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0926ef28816b35ab, []int{9} +} +func (m *MsgUndelegateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUndelegateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUndelegateResponse.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 *MsgUndelegateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUndelegateResponse.Merge(m, src) +} +func (m *MsgUndelegateResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUndelegateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUndelegateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUndelegateResponse proto.InternalMessageInfo + +func (m *MsgUndelegateResponse) GetCompletionTime() time.Time { + if m != nil { + return m.CompletionTime + } + return time.Time{} +} + +func init() { + proto.RegisterType((*MsgCreateValidator)(nil), "cosmos.staking.v1beta1.MsgCreateValidator") + proto.RegisterType((*MsgCreateValidatorResponse)(nil), "cosmos.staking.v1beta1.MsgCreateValidatorResponse") + proto.RegisterType((*MsgEditValidator)(nil), "cosmos.staking.v1beta1.MsgEditValidator") + proto.RegisterType((*MsgEditValidatorResponse)(nil), "cosmos.staking.v1beta1.MsgEditValidatorResponse") + proto.RegisterType((*MsgDelegate)(nil), "cosmos.staking.v1beta1.MsgDelegate") + proto.RegisterType((*MsgDelegateResponse)(nil), "cosmos.staking.v1beta1.MsgDelegateResponse") + proto.RegisterType((*MsgBeginRedelegate)(nil), "cosmos.staking.v1beta1.MsgBeginRedelegate") + proto.RegisterType((*MsgBeginRedelegateResponse)(nil), "cosmos.staking.v1beta1.MsgBeginRedelegateResponse") + proto.RegisterType((*MsgUndelegate)(nil), "cosmos.staking.v1beta1.MsgUndelegate") + proto.RegisterType((*MsgUndelegateResponse)(nil), "cosmos.staking.v1beta1.MsgUndelegateResponse") +} + +func init() { proto.RegisterFile("cosmos/staking/v1beta1/tx.proto", fileDescriptor_0926ef28816b35ab) } + +var fileDescriptor_0926ef28816b35ab = []byte{ + // 860 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0x4d, 0x6b, 0xe3, 0x46, + 0x18, 0xb6, 0x6c, 0xc7, 0x4d, 0x27, 0xe4, 0x4b, 0xf9, 0xc0, 0x11, 0xc1, 0x0a, 0x4a, 0x3f, 0x42, + 0xdb, 0xc8, 0x4d, 0x4a, 0x29, 0xe4, 0x52, 0xe2, 0xb8, 0xa1, 0x21, 0x35, 0x14, 0x25, 0xed, 0xa1, + 0x14, 0x8c, 0x3e, 0xc6, 0xaa, 0xb0, 0xa4, 0x51, 0x34, 0xe3, 0x10, 0x43, 0x7f, 0x40, 0x8f, 0x81, + 0xde, 0xf6, 0x94, 0x1f, 0xb1, 0x3f, 0x22, 0x2c, 0xec, 0x92, 0xe3, 0xb2, 0x07, 0xef, 0x92, 0xc0, + 0x92, 0xb3, 0x7f, 0xc1, 0xa2, 0xd1, 0x48, 0x96, 0xe5, 0x0f, 0x4c, 0x58, 0x5f, 0xf6, 0x64, 0x33, + 0xf3, 0xcc, 0xf3, 0xce, 0xfb, 0xbc, 0xcf, 0xbc, 0xaf, 0x80, 0xa8, 0x23, 0xec, 0x20, 0x5c, 0xc6, + 0x44, 0x6d, 0x5a, 0xae, 0x59, 0xbe, 0xdc, 0xd3, 0x20, 0x51, 0xf7, 0xca, 0xe4, 0x4a, 0xf6, 0x7c, + 0x44, 0x10, 0xbf, 0x1e, 0x02, 0x64, 0x06, 0x90, 0x19, 0x40, 0xd8, 0x30, 0x11, 0x32, 0x6d, 0x58, + 0xa6, 0x28, 0xad, 0xd5, 0x28, 0xab, 0x6e, 0x3b, 0x3c, 0x22, 0x88, 0xe9, 0x2d, 0x62, 0x39, 0x10, + 0x13, 0xd5, 0xf1, 0x18, 0x60, 0xd5, 0x44, 0x26, 0xa2, 0x7f, 0xcb, 0xc1, 0x3f, 0xb6, 0xba, 0x11, + 0x46, 0xaa, 0x87, 0x1b, 0x2c, 0x6c, 0xb8, 0x55, 0x62, 0xb7, 0xd4, 0x54, 0x0c, 0xe3, 0x2b, 0xea, + 0xc8, 0x72, 0xd9, 0xfe, 0x17, 0x23, 0xb2, 0x88, 0x2e, 0x4d, 0x51, 0xd2, 0xcb, 0x3c, 0xe0, 0x6b, + 0xd8, 0x3c, 0xf2, 0xa1, 0x4a, 0xe0, 0x9f, 0xaa, 0x6d, 0x19, 0x2a, 0x41, 0x3e, 0x7f, 0x0a, 0xe6, + 0x0c, 0x88, 0x75, 0xdf, 0xf2, 0x88, 0x85, 0xdc, 0x22, 0xb7, 0xc5, 0xed, 0xcc, 0xed, 0x6f, 0xcb, + 0xc3, 0xf3, 0x96, 0xab, 0x3d, 0x68, 0x25, 0x7f, 0xdb, 0x11, 0x33, 0x4a, 0xf2, 0x34, 0x5f, 0x03, + 0x40, 0x47, 0x8e, 0x63, 0x61, 0x1c, 0x70, 0x65, 0x29, 0xd7, 0xd7, 0xa3, 0xb8, 0x8e, 0x62, 0xa4, + 0xa2, 0x12, 0x88, 0x19, 0x5f, 0x82, 0x80, 0xff, 0x17, 0xac, 0x38, 0x96, 0x5b, 0xc7, 0xd0, 0x6e, + 0xd4, 0x0d, 0x68, 0x43, 0x53, 0xa5, 0x77, 0xcc, 0x6d, 0x71, 0x3b, 0x9f, 0x57, 0x7e, 0x0b, 0xe0, + 0x6f, 0x3a, 0xe2, 0x57, 0xa6, 0x45, 0xfe, 0x69, 0x69, 0xb2, 0x8e, 0x1c, 0x26, 0x1b, 0xfb, 0xd9, + 0xc5, 0x46, 0xb3, 0x4c, 0xda, 0x1e, 0xc4, 0xf2, 0x89, 0x4b, 0xba, 0x1d, 0x51, 0x68, 0xab, 0x8e, + 0x7d, 0x20, 0x0d, 0xa1, 0x94, 0x94, 0x65, 0xc7, 0x72, 0xcf, 0xa0, 0xdd, 0xa8, 0xc6, 0x6b, 0xfc, + 0x09, 0x58, 0x66, 0x08, 0xe4, 0xd7, 0x55, 0xc3, 0xf0, 0x21, 0xc6, 0xc5, 0x3c, 0x8d, 0xbd, 0xd9, + 0xed, 0x88, 0xc5, 0x90, 0x6d, 0x00, 0x22, 0x29, 0x4b, 0xf1, 0xda, 0x61, 0xb8, 0x14, 0x50, 0x5d, + 0x46, 0x8a, 0xc7, 0x54, 0x33, 0x69, 0xaa, 0x01, 0x88, 0xa4, 0x2c, 0xc5, 0x6b, 0x11, 0xd5, 0x31, + 0x28, 0x78, 0x2d, 0xad, 0x09, 0xdb, 0xc5, 0x02, 0x95, 0x77, 0x55, 0x0e, 0xfd, 0x26, 0x47, 0x7e, + 0x93, 0x0f, 0xdd, 0x76, 0xa5, 0xf8, 0xe2, 0xf9, 0xee, 0x2a, 0xd3, 0x5d, 0xf7, 0xdb, 0x1e, 0x41, + 0xf2, 0xef, 0x2d, 0xed, 0x14, 0xb6, 0x15, 0x76, 0x9a, 0xff, 0x11, 0xcc, 0x5c, 0xaa, 0x76, 0x0b, + 0x16, 0x3f, 0xa3, 0x34, 0x1b, 0x51, 0x95, 0x02, 0x93, 0x25, 0x4a, 0x64, 0x45, 0x75, 0x0e, 0xd1, + 0x07, 0xb3, 0xff, 0xdd, 0x88, 0x99, 0xc7, 0x1b, 0x31, 0x23, 0x6d, 0x02, 0x61, 0xd0, 0x4e, 0x0a, + 0xc4, 0x1e, 0x72, 0x31, 0x94, 0xfe, 0xcf, 0x81, 0xa5, 0x1a, 0x36, 0x7f, 0x31, 0x2c, 0x32, 0x25, + 0xaf, 0xfd, 0x3c, 0x4c, 0xd3, 0x2c, 0xd5, 0x94, 0xef, 0x76, 0xc4, 0x85, 0x50, 0xd3, 0x31, 0x4a, + 0x3a, 0x60, 0xb1, 0xe7, 0xb5, 0xba, 0xaf, 0x12, 0xc8, 0x9c, 0x55, 0x9d, 0xd0, 0x55, 0x55, 0xa8, + 0x77, 0x3b, 0xe2, 0x7a, 0x18, 0x28, 0x45, 0x25, 0x29, 0x0b, 0x7a, 0x9f, 0xbf, 0xf9, 0xab, 0xe1, + 0x66, 0x0e, 0x0d, 0xf5, 0xeb, 0x14, 0x8d, 0x9c, 0xa8, 0x99, 0x00, 0x8a, 0xe9, 0xa2, 0xc4, 0x15, + 0x7b, 0xcf, 0x81, 0xb9, 0x1a, 0x36, 0xd9, 0x39, 0x38, 0xdc, 0xfe, 0xdc, 0xc7, 0xb3, 0x7f, 0xf6, + 0x49, 0xf6, 0xff, 0x09, 0x14, 0x54, 0x07, 0xb5, 0x5c, 0x42, 0x6b, 0x35, 0x81, 0x6f, 0x19, 0x3c, + 0x21, 0xc2, 0x1a, 0x58, 0x49, 0xe4, 0x19, 0xe7, 0xff, 0x2a, 0x4b, 0xfb, 0x63, 0x05, 0x9a, 0x96, + 0xab, 0x40, 0x63, 0x0a, 0x32, 0x9c, 0x83, 0xb5, 0x5e, 0x8e, 0xd8, 0xd7, 0x53, 0x52, 0x6c, 0x75, + 0x3b, 0xe2, 0x66, 0x5a, 0x8a, 0x04, 0x4c, 0x52, 0x56, 0xe2, 0xf5, 0x33, 0x5f, 0x1f, 0xca, 0x6a, + 0x60, 0x12, 0xb3, 0xe6, 0x46, 0xb3, 0x26, 0x60, 0x49, 0xd6, 0x2a, 0x26, 0x83, 0x3a, 0xe7, 0x9f, + 0xaa, 0x73, 0x93, 0x36, 0x88, 0x94, 0x9e, 0x91, 0xdc, 0x7c, 0x8d, 0xbe, 0x3e, 0xcf, 0x86, 0x81, + 0x45, 0xeb, 0xc1, 0x8c, 0x64, 0xfd, 0x40, 0x18, 0x68, 0x68, 0xe7, 0xd1, 0x00, 0xad, 0xcc, 0x06, + 0xa1, 0xae, 0xdf, 0x8a, 0x1c, 0x7d, 0x5d, 0xec, 0x70, 0xb0, 0x2d, 0x3d, 0x72, 0x60, 0xbe, 0x86, + 0xcd, 0x3f, 0x5c, 0xe3, 0x93, 0xf7, 0x6f, 0x03, 0xac, 0xf5, 0x65, 0x3a, 0x25, 0x49, 0xf7, 0x9f, + 0xe5, 0x41, 0xae, 0x86, 0x4d, 0xfe, 0x02, 0x2c, 0xa6, 0x3f, 0x1a, 0xbe, 0x19, 0xd5, 0xb3, 0x07, + 0x27, 0x82, 0xb0, 0x3f, 0x39, 0x36, 0xce, 0xa4, 0x09, 0xe6, 0xfb, 0x27, 0xc7, 0xce, 0x18, 0x92, + 0x3e, 0xa4, 0xf0, 0xfd, 0xa4, 0xc8, 0x38, 0xd8, 0xdf, 0x60, 0x36, 0x6e, 0x7a, 0xdb, 0x63, 0x4e, + 0x47, 0x20, 0xe1, 0xdb, 0x09, 0x40, 0x31, 0xfb, 0x05, 0x58, 0x4c, 0xb7, 0x94, 0x71, 0xea, 0xa5, + 0xb0, 0x63, 0xd5, 0x1b, 0xf5, 0xb4, 0x34, 0x00, 0x12, 0xef, 0xe0, 0xcb, 0x31, 0x0c, 0x3d, 0x98, + 0xb0, 0x3b, 0x11, 0x2c, 0x8a, 0x51, 0x39, 0xbe, 0xbd, 0x2f, 0x71, 0x77, 0xf7, 0x25, 0xee, 0xdd, + 0x7d, 0x89, 0xbb, 0x7e, 0x28, 0x65, 0xee, 0x1e, 0x4a, 0x99, 0xd7, 0x0f, 0xa5, 0xcc, 0x5f, 0xdf, + 0x8d, 0x1d, 0x63, 0x57, 0xf1, 0x57, 0x2a, 0x1d, 0x68, 0x5a, 0x81, 0x5a, 0xf2, 0x87, 0x0f, 0x01, + 0x00, 0x00, 0xff, 0xff, 0xa1, 0x2b, 0xfd, 0x07, 0x8a, 0x0b, 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 { + // CreateValidator defines a method for creating a new validator. + CreateValidator(ctx context.Context, in *MsgCreateValidator, opts ...grpc.CallOption) (*MsgCreateValidatorResponse, error) + // EditValidator defines a method for editing an existing validator. + EditValidator(ctx context.Context, in *MsgEditValidator, opts ...grpc.CallOption) (*MsgEditValidatorResponse, error) + // Delegate defines a method for performing a delegation of coins + // from a delegator to a validator. + Delegate(ctx context.Context, in *MsgDelegate, opts ...grpc.CallOption) (*MsgDelegateResponse, error) + // BeginRedelegate defines a method for performing a redelegation + // of coins from a delegator and source validator to a destination validator. + BeginRedelegate(ctx context.Context, in *MsgBeginRedelegate, opts ...grpc.CallOption) (*MsgBeginRedelegateResponse, error) + // Undelegate defines a method for performing an undelegation from a + // delegate and a validator. + Undelegate(ctx context.Context, in *MsgUndelegate, opts ...grpc.CallOption) (*MsgUndelegateResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) CreateValidator(ctx context.Context, in *MsgCreateValidator, opts ...grpc.CallOption) (*MsgCreateValidatorResponse, error) { + out := new(MsgCreateValidatorResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Msg/CreateValidator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) EditValidator(ctx context.Context, in *MsgEditValidator, opts ...grpc.CallOption) (*MsgEditValidatorResponse, error) { + out := new(MsgEditValidatorResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Msg/EditValidator", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Delegate(ctx context.Context, in *MsgDelegate, opts ...grpc.CallOption) (*MsgDelegateResponse, error) { + out := new(MsgDelegateResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Msg/Delegate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) BeginRedelegate(ctx context.Context, in *MsgBeginRedelegate, opts ...grpc.CallOption) (*MsgBeginRedelegateResponse, error) { + out := new(MsgBeginRedelegateResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Msg/BeginRedelegate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) Undelegate(ctx context.Context, in *MsgUndelegate, opts ...grpc.CallOption) (*MsgUndelegateResponse, error) { + out := new(MsgUndelegateResponse) + err := c.cc.Invoke(ctx, "/cosmos.staking.v1beta1.Msg/Undelegate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // CreateValidator defines a method for creating a new validator. + CreateValidator(context.Context, *MsgCreateValidator) (*MsgCreateValidatorResponse, error) + // EditValidator defines a method for editing an existing validator. + EditValidator(context.Context, *MsgEditValidator) (*MsgEditValidatorResponse, error) + // Delegate defines a method for performing a delegation of coins + // from a delegator to a validator. + Delegate(context.Context, *MsgDelegate) (*MsgDelegateResponse, error) + // BeginRedelegate defines a method for performing a redelegation + // of coins from a delegator and source validator to a destination validator. + BeginRedelegate(context.Context, *MsgBeginRedelegate) (*MsgBeginRedelegateResponse, error) + // Undelegate defines a method for performing an undelegation from a + // delegate and a validator. + Undelegate(context.Context, *MsgUndelegate) (*MsgUndelegateResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) CreateValidator(ctx context.Context, req *MsgCreateValidator) (*MsgCreateValidatorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateValidator not implemented") +} +func (*UnimplementedMsgServer) EditValidator(ctx context.Context, req *MsgEditValidator) (*MsgEditValidatorResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EditValidator not implemented") +} +func (*UnimplementedMsgServer) Delegate(ctx context.Context, req *MsgDelegate) (*MsgDelegateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delegate not implemented") +} +func (*UnimplementedMsgServer) BeginRedelegate(ctx context.Context, req *MsgBeginRedelegate) (*MsgBeginRedelegateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BeginRedelegate not implemented") +} +func (*UnimplementedMsgServer) Undelegate(ctx context.Context, req *MsgUndelegate) (*MsgUndelegateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Undelegate not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_CreateValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateValidator) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateValidator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Msg/CreateValidator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateValidator(ctx, req.(*MsgCreateValidator)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_EditValidator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgEditValidator) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).EditValidator(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Msg/EditValidator", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).EditValidator(ctx, req.(*MsgEditValidator)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Delegate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDelegate) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Delegate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Msg/Delegate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Delegate(ctx, req.(*MsgDelegate)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_BeginRedelegate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgBeginRedelegate) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).BeginRedelegate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Msg/BeginRedelegate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).BeginRedelegate(ctx, req.(*MsgBeginRedelegate)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_Undelegate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUndelegate) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Undelegate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.staking.v1beta1.Msg/Undelegate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Undelegate(ctx, req.(*MsgUndelegate)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.staking.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateValidator", + Handler: _Msg_CreateValidator_Handler, + }, + { + MethodName: "EditValidator", + Handler: _Msg_EditValidator_Handler, + }, + { + MethodName: "Delegate", + Handler: _Msg_Delegate_Handler, + }, + { + MethodName: "BeginRedelegate", + Handler: _Msg_BeginRedelegate_Handler, + }, + { + MethodName: "Undelegate", + Handler: _Msg_Undelegate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/staking/v1beta1/tx.proto", +} + +func (m *MsgCreateValidator) 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 *MsgCreateValidator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Value.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + if m.Pubkey != nil { + { + size, err := m.Pubkey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x2a + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0x22 + } + { + size := m.MinSelfDelegation.Size() + i -= size + if _, err := m.MinSelfDelegation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Commission.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Description.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 *MsgCreateValidatorResponse) 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 *MsgCreateValidatorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateValidatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgEditValidator) 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 *MsgEditValidator) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEditValidator) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MinSelfDelegation != nil { + { + size := m.MinSelfDelegation.Size() + i -= size + if _, err := m.MinSelfDelegation.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CommissionRate != nil { + { + size := m.CommissionRate.Size() + i -= size + if _, err := m.CommissionRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Description.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 *MsgEditValidatorResponse) 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 *MsgEditValidatorResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEditValidatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgDelegate) 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 *MsgDelegate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDelegateResponse) 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 *MsgDelegateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelegateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgBeginRedelegate) 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 *MsgBeginRedelegate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBeginRedelegate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ValidatorDstAddress) > 0 { + i -= len(m.ValidatorDstAddress) + copy(dAtA[i:], m.ValidatorDstAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorDstAddress))) + i-- + dAtA[i] = 0x1a + } + if len(m.ValidatorSrcAddress) > 0 { + i -= len(m.ValidatorSrcAddress) + copy(dAtA[i:], m.ValidatorSrcAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorSrcAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgBeginRedelegateResponse) 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 *MsgBeginRedelegateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBeginRedelegateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CompletionTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintTx(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgUndelegate) 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 *MsgUndelegate) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUndelegate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.DelegatorAddress) > 0 { + i -= len(m.DelegatorAddress) + copy(dAtA[i:], m.DelegatorAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.DelegatorAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUndelegateResponse) 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 *MsgUndelegateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUndelegateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CompletionTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime):]) + if err10 != nil { + return 0, err10 + } + i -= n10 + i = encodeVarintTx(dAtA, i, uint64(n10)) + i-- + dAtA[i] = 0xa + 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 *MsgCreateValidator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Description.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.Commission.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.MinSelfDelegation.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Pubkey != nil { + l = m.Pubkey.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = m.Value.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgCreateValidatorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgEditValidator) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Description.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.CommissionRate != nil { + l = m.CommissionRate.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.MinSelfDelegation != nil { + l = m.MinSelfDelegation.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgEditValidatorResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgDelegate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgDelegateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgBeginRedelegate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorSrcAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorDstAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgBeginRedelegateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime) + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUndelegate) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DelegatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUndelegateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CompletionTime) + n += 1 + l + sovTx(uint64(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 *MsgCreateValidator) 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: MsgCreateValidator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateValidator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", 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 := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commission", 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 := m.Commission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinSelfDelegation", 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 + } + if err := m.MinSelfDelegation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pubkey", 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.Pubkey == nil { + m.Pubkey = &types.Any{} + } + if err := m.Pubkey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", 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 := m.Value.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 *MsgCreateValidatorResponse) 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: MsgCreateValidatorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateValidatorResponse: 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 *MsgEditValidator) 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: MsgEditValidator: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEditValidator: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", 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 := m.Description.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommissionRate", 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 + } + var v github_com_cosmos_cosmos_sdk_types.Dec + m.CommissionRate = &v + if err := m.CommissionRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinSelfDelegation", 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 + } + var v github_com_cosmos_cosmos_sdk_types.Int + m.MinSelfDelegation = &v + if err := m.MinSelfDelegation.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 *MsgEditValidatorResponse) 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: MsgEditValidatorResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEditValidatorResponse: 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 *MsgDelegate) 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: MsgDelegate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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 := m.Amount.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 *MsgDelegateResponse) 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: MsgDelegateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelegateResponse: 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 *MsgBeginRedelegate) 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: MsgBeginRedelegate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBeginRedelegate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorSrcAddress", 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.ValidatorSrcAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorDstAddress", 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.ValidatorDstAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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 := m.Amount.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 *MsgBeginRedelegateResponse) 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: MsgBeginRedelegateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBeginRedelegateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletionTime", 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.CompletionTime, 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 *MsgUndelegate) 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: MsgUndelegate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUndelegate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DelegatorAddress", 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.DelegatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", 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 := m.Amount.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 *MsgUndelegateResponse) 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: MsgUndelegateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUndelegateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletionTime", 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.CompletionTime, 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 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/staking/types/validator.go b/x/staking/types/validator.go index d64e0ce3e751..e551e752528c 100644 --- a/x/staking/types/validator.go +++ b/x/staking/types/validator.go @@ -8,17 +8,17 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - tmtypes "github.com/tendermint/tendermint/types" - yaml "gopkg.in/yaml.v2" + tmprotocrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" + "gopkg.in/yaml.v2" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/staking/exported" ) -// nolint const ( // TODO: Why can't we just have one string description which can be JSON by convention MaxMonikerLength = 70 @@ -28,62 +28,42 @@ const ( MaxDetailsLength = 280 ) -// Implements Validator interface -var _ exported.ValidatorI = Validator{} - -// Validator defines the total amount of bond shares and their exchange rate to -// coins. Slashing results in a decrease in the exchange rate, allowing correct -// calculation of future undelegations without iterating over delegators. -// When coins are delegated to this validator, the validator is credited with a -// delegation whose number of bond shares is based on the amount of coins delegated -// divided by the current exchange rate. Voting power can be calculated as total -// bonded shares multiplied by exchange rate. -type Validator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` // address of the validator's operator; bech encoded in JSON - ConsPubKey crypto.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` // the consensus public key of the validator; bech encoded in JSON - Jailed bool `json:"jailed" yaml:"jailed"` // has the validator been jailed from bonded status? - Status sdk.BondStatus `json:"status" yaml:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Int `json:"tokens" yaml:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` // total shares issued to a validator's delegators - Description Description `json:"description" yaml:"description"` // description terms for the validator - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding - Commission Commission `json:"commission" yaml:"commission"` // commission parameters - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // validator's self declared minimum self delegation -} - -// custom marshal yaml function due to consensus pubkey -func (v Validator) MarshalYAML() (interface{}, error) { - bs, err := yaml.Marshal(struct { - OperatorAddress sdk.ValAddress - ConsPubKey string - Jailed bool - Status sdk.BondStatus - Tokens sdk.Int - DelegatorShares sdk.Dec - Description Description - UnbondingHeight int64 - UnbondingCompletionTime time.Time - Commission Commission - MinSelfDelegation sdk.Int - }{ - OperatorAddress: v.OperatorAddress, - ConsPubKey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey), - Jailed: v.Jailed, - Status: v.Status, - Tokens: v.Tokens, - DelegatorShares: v.DelegatorShares, - Description: v.Description, - UnbondingHeight: v.UnbondingHeight, - UnbondingCompletionTime: v.UnbondingCompletionTime, - Commission: v.Commission, - MinSelfDelegation: v.MinSelfDelegation, - }) +var ( + BondStatusUnspecified = BondStatus_name[int32(Unspecified)] + BondStatusUnbonded = BondStatus_name[int32(Unbonded)] + BondStatusUnbonding = BondStatus_name[int32(Unbonding)] + BondStatusBonded = BondStatus_name[int32(Bonded)] +) + +var _ ValidatorI = Validator{} + +// NewValidator constructs a new Validator +//nolint:interfacer +func NewValidator(operator sdk.ValAddress, pubKey cryptotypes.PubKey, description Description) (Validator, error) { + pkAny, err := codectypes.NewAnyWithValue(pubKey) if err != nil { - return nil, err + return Validator{}, err } - return string(bs), nil + return Validator{ + OperatorAddress: operator.String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: Unbonded, + Tokens: sdk.ZeroInt(), + DelegatorShares: sdk.ZeroDec(), + Description: description, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.OneInt(), + }, nil +} + +// String implements the Stringer interface for a Validator object. +func (v Validator) String() string { + out, _ := yaml.Marshal(v) + return string(out) } // Validators is a collection of Validator @@ -93,14 +73,16 @@ func (v Validators) String() (out string) { for _, val := range v { out += val.String() + "\n" } + return strings.TrimSpace(out) } -// ToSDKValidators - convenience function convert []Validators to []sdk.Validators -func (v Validators) ToSDKValidators() (validators []exported.ValidatorI) { +// ToSDKValidators - convenience function convert []Validator to []sdk.ValidatorI +func (v Validators) ToSDKValidators() (validators []ValidatorI) { for _, val := range v { validators = append(validators, val) } + return validators } @@ -116,7 +98,7 @@ func (v Validators) Len() int { // Implements sort interface func (v Validators) Less(i, j int) bool { - return bytes.Compare(v[i].OperatorAddress, v[j].OperatorAddress) == -1 + return bytes.Compare(v[i].GetOperator().Bytes(), v[j].GetOperator().Bytes()) == -1 } // Implements sort interface @@ -126,173 +108,80 @@ func (v Validators) Swap(i, j int) { v[j] = it } -// NewValidator - initialize a new validator -func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator { - return Validator{ - OperatorAddress: operator, - ConsPubKey: pubKey, - Jailed: false, - Status: sdk.Unbonded, - Tokens: sdk.ZeroInt(), - DelegatorShares: sdk.ZeroDec(), - Description: description, - UnbondingHeight: int64(0), - UnbondingCompletionTime: time.Unix(0, 0).UTC(), - Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - MinSelfDelegation: sdk.OneInt(), +// ValidatorsByVotingPower implements sort.Interface for []Validator based on +// the VotingPower and Address fields. +// The validators are sorted first by their voting power (descending). Secondary index - Address (ascending). +// Copied from tendermint/types/validator_set.go +type ValidatorsByVotingPower []Validator + +func (valz ValidatorsByVotingPower) Len() int { return len(valz) } + +func (valz ValidatorsByVotingPower) Less(i, j int) bool { + if valz[i].ConsensusPower() == valz[j].ConsensusPower() { + addrI, errI := valz[i].GetConsAddr() + addrJ, errJ := valz[j].GetConsAddr() + // If either returns error, then return false + if errI != nil || errJ != nil { + return false + } + return bytes.Compare(addrI, addrJ) == -1 } + return valz[i].ConsensusPower() > valz[j].ConsensusPower() } -// return the redelegation -func MustMarshalValidator(cdc *codec.Codec, validator Validator) []byte { - return cdc.MustMarshalBinaryLengthPrefixed(validator) +func (valz ValidatorsByVotingPower) Swap(i, j int) { + valz[i], valz[j] = valz[j], valz[i] } -// unmarshal a redelegation from a store value -func MustUnmarshalValidator(cdc *codec.Codec, value []byte) Validator { - validator, err := UnmarshalValidator(cdc, value) - if err != nil { - panic(err) +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (v Validators) UnpackInterfaces(c codectypes.AnyUnpacker) error { + for i := range v { + if err := v[i].UnpackInterfaces(c); err != nil { + return err + } } - return validator + return nil } -// unmarshal a redelegation from a store value -func UnmarshalValidator(cdc *codec.Codec, value []byte) (validator Validator, err error) { - err = cdc.UnmarshalBinaryLengthPrefixed(value, &validator) - return validator, err +// return the redelegation +func MustMarshalValidator(cdc codec.BinaryMarshaler, validator *Validator) []byte { + return cdc.MustMarshalBinaryBare(validator) } -// String returns a human readable string representation of a validator. -func (v Validator) String() string { - bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) +// unmarshal a redelegation from a store value +func MustUnmarshalValidator(cdc codec.BinaryMarshaler, value []byte) Validator { + validator, err := UnmarshalValidator(cdc, value) if err != nil { panic(err) } - return fmt.Sprintf(`Validator - Operator Address: %s - Validator Consensus Pubkey: %s - Jailed: %v - Status: %s - Tokens: %s - Delegator Shares: %s - Description: %s - Unbonding Height: %d - Unbonding Completion Time: %v - Minimum Self Delegation: %v - Commission: %s`, v.OperatorAddress, bechConsPubKey, - v.Jailed, v.Status, v.Tokens, - v.DelegatorShares, v.Description, - v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission) -} - -// this is a helper struct used for JSON de- and encoding only -type bechValidator struct { - OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` // the bech32 address of the validator's operator - ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` // the bech32 consensus public key of the validator - Jailed bool `json:"jailed" yaml:"jailed"` // has the validator been jailed from bonded status? - Status sdk.BondStatus `json:"status" yaml:"status"` // validator status (bonded/unbonding/unbonded) - Tokens sdk.Int `json:"tokens" yaml:"tokens"` // delegated tokens (incl. self-delegation) - DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` // total shares issued to a validator's delegators - Description Description `json:"description" yaml:"description"` // description terms for the validator - UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding - UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding - Commission Commission `json:"commission" yaml:"commission"` // commission parameters - MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // minimum self delegation -} - -// MarshalJSON marshals the validator to JSON using Bech32 -func (v Validator) MarshalJSON() ([]byte, error) { - bechConsPubKey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, v.ConsPubKey) - if err != nil { - return nil, err - } - - return codec.Cdc.MarshalJSON(bechValidator{ - OperatorAddress: v.OperatorAddress, - ConsPubKey: bechConsPubKey, - Jailed: v.Jailed, - Status: v.Status, - Tokens: v.Tokens, - DelegatorShares: v.DelegatorShares, - Description: v.Description, - UnbondingHeight: v.UnbondingHeight, - UnbondingCompletionTime: v.UnbondingCompletionTime, - MinSelfDelegation: v.MinSelfDelegation, - Commission: v.Commission, - }) -} - -// UnmarshalJSON unmarshals the validator from JSON using Bech32 -func (v *Validator) UnmarshalJSON(data []byte) error { - bv := &bechValidator{} - if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { - return err - } - consPubKey, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, bv.ConsPubKey) - if err != nil { - return err - } - *v = Validator{ - OperatorAddress: bv.OperatorAddress, - ConsPubKey: consPubKey, - Jailed: bv.Jailed, - Tokens: bv.Tokens, - Status: bv.Status, - DelegatorShares: bv.DelegatorShares, - Description: bv.Description, - UnbondingHeight: bv.UnbondingHeight, - UnbondingCompletionTime: bv.UnbondingCompletionTime, - Commission: bv.Commission, - MinSelfDelegation: bv.MinSelfDelegation, - } - return nil -} -// only the vitals -func (v Validator) TestEquivalent(v2 Validator) bool { - return v.ConsPubKey.Equals(v2.ConsPubKey) && - bytes.Equal(v.OperatorAddress, v2.OperatorAddress) && - v.Status.Equal(v2.Status) && - v.Tokens.Equal(v2.Tokens) && - v.DelegatorShares.Equal(v2.DelegatorShares) && - v.Description == v2.Description && - v.Commission.Equal(v2.Commission) + return validator } -// return the TM validator address -func (v Validator) ConsAddress() sdk.ConsAddress { - return sdk.ConsAddress(v.ConsPubKey.Address()) +// unmarshal a redelegation from a store value +func UnmarshalValidator(cdc codec.BinaryMarshaler, value []byte) (v Validator, err error) { + err = cdc.UnmarshalBinaryBare(value, &v) + return v, err } // IsBonded checks if the validator status equals Bonded func (v Validator) IsBonded() bool { - return v.GetStatus().Equal(sdk.Bonded) + return v.GetStatus() == Bonded } // IsUnbonded checks if the validator status equals Unbonded func (v Validator) IsUnbonded() bool { - return v.GetStatus().Equal(sdk.Unbonded) + return v.GetStatus() == Unbonded } // IsUnbonding checks if the validator status equals Unbonding func (v Validator) IsUnbonding() bool { - return v.GetStatus().Equal(sdk.Unbonding) + return v.GetStatus() == Unbonding } // constant used in flags to indicate that description field should not be updated const DoNotModifyDesc = "[do-not-modify]" -// Description - description fields for a validator -type Description struct { - Moniker string `json:"moniker" yaml:"moniker"` // name - Identity string `json:"identity" yaml:"identity"` // optional identity signature (ex. UPort or Keybase) - Website string `json:"website" yaml:"website"` // optional website link - SecurityContact string `json:"security_contact" yaml:"security_contact"` // optional security contact info - Details string `json:"details" yaml:"details"` // optional details -} - -// NewDescription returns a new Description with the provided values. func NewDescription(moniker, identity, website, securityContact, details string) Description { return Description{ Moniker: moniker, @@ -303,21 +192,31 @@ func NewDescription(moniker, identity, website, securityContact, details string) } } +// String implements the Stringer interface for a Description object. +func (d Description) String() string { + out, _ := yaml.Marshal(d) + return string(out) +} + // UpdateDescription updates the fields of a given description. An error is // returned if the resulting description contains an invalid length. func (d Description) UpdateDescription(d2 Description) (Description, error) { if d2.Moniker == DoNotModifyDesc { d2.Moniker = d.Moniker } + if d2.Identity == DoNotModifyDesc { d2.Identity = d.Identity } + if d2.Website == DoNotModifyDesc { d2.Website = d.Website } + if d2.SecurityContact == DoNotModifyDesc { d2.SecurityContact = d.SecurityContact } + if d2.Details == DoNotModifyDesc { d2.Details = d.Details } @@ -336,15 +235,19 @@ func (d Description) EnsureLength() (Description, error) { if len(d.Moniker) > MaxMonikerLength { return d, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid moniker length; got: %d, max: %d", len(d.Moniker), MaxMonikerLength) } + if len(d.Identity) > MaxIdentityLength { return d, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid identity length; got: %d, max: %d", len(d.Identity), MaxIdentityLength) } + if len(d.Website) > MaxWebsiteLength { return d, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid website length; got: %d, max: %d", len(d.Website), MaxWebsiteLength) } + if len(d.SecurityContact) > MaxSecurityContactLength { return d, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid security contact length; got: %d, max: %d", len(d.SecurityContact), MaxSecurityContactLength) } + if len(d.Details) > MaxDetailsLength { return d, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid details length; got: %d, max: %d", len(d.Details), MaxDetailsLength) } @@ -355,8 +258,13 @@ func (d Description) EnsureLength() (Description, error) { // ABCIValidatorUpdate returns an abci.ValidatorUpdate from a staking validator type // with the full validator power func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { + tmProtoPk, err := v.TmConsPublicKey() + if err != nil { + panic(err) + } + return abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + PubKey: tmProtoPk, Power: v.ConsensusPower(), } } @@ -364,8 +272,13 @@ func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { // ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staking validator type // with zero power used for validator updates. func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { + tmProtoPk, err := v.TmConsPublicKey() + if err != nil { + panic(err) + } + return abci.ValidatorUpdate{ - PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + PubKey: tmProtoPk, Power: 0, } } @@ -378,6 +291,7 @@ func (v Validator) SetInitialCommission(commission Commission) (Validator, error } v.Commission = commission + return v, nil } @@ -429,33 +343,34 @@ func (v Validator) BondedTokens() sdk.Int { if v.IsBonded() { return v.Tokens } + return sdk.ZeroInt() } -// get the consensus-engine power -// a reduction of 10^6 from validator tokens is applied +// ConsensusPower gets the consensus-engine power. Aa reduction of 10^6 from +// validator tokens is applied func (v Validator) ConsensusPower() int64 { if v.IsBonded() { return v.PotentialConsensusPower() } + return 0 } -// potential consensus-engine power +// PotentialConsensusPower returns the potential consensus-engine power. func (v Validator) PotentialConsensusPower() int64 { return sdk.TokensToConsensusPower(v.Tokens) } // UpdateStatus updates the location of the shares within a validator // to reflect the new status -func (v Validator) UpdateStatus(newStatus sdk.BondStatus) Validator { +func (v Validator) UpdateStatus(newStatus BondStatus) Validator { v.Status = newStatus return v } // AddTokensFromDel adds tokens to a validator func (v Validator) AddTokensFromDel(amount sdk.Int) (Validator, sdk.Dec) { - // calculate the shares to issue var issuedShares sdk.Dec if v.DelegatorShares.IsZero() { @@ -481,10 +396,13 @@ func (v Validator) RemoveTokens(tokens sdk.Int) Validator { if tokens.IsNegative() { panic(fmt.Sprintf("should not happen: trying to remove negative tokens %v", tokens)) } + if v.Tokens.LT(tokens) { panic(fmt.Sprintf("should not happen: only have %v tokens, trying to remove %v", v.Tokens, tokens)) } + v.Tokens = v.Tokens.Sub(tokens) + return v } @@ -492,39 +410,110 @@ func (v Validator) RemoveTokens(tokens sdk.Int) Validator { // NOTE: because token fractions are left in the valiadator, // the exchange rate of future shares of this validator can increase. func (v Validator) RemoveDelShares(delShares sdk.Dec) (Validator, sdk.Int) { - remainingShares := v.DelegatorShares.Sub(delShares) + var issuedTokens sdk.Int if remainingShares.IsZero() { - // last delegation share gets any trimmings issuedTokens = v.Tokens v.Tokens = sdk.ZeroInt() } else { - // leave excess tokens in the validator // however fully use all the delegator shares issuedTokens = v.TokensFromShares(delShares).TruncateInt() v.Tokens = v.Tokens.Sub(issuedTokens) + if v.Tokens.IsNegative() { panic("attempting to remove more tokens than available in validator") } } v.DelegatorShares = remainingShares + return v, issuedTokens } -// nolint - for ValidatorI -func (v Validator) IsJailed() bool { return v.Jailed } -func (v Validator) GetMoniker() string { return v.Description.Moniker } -func (v Validator) GetStatus() sdk.BondStatus { return v.Status } -func (v Validator) GetOperator() sdk.ValAddress { return v.OperatorAddress } -func (v Validator) GetConsPubKey() crypto.PubKey { return v.ConsPubKey } -func (v Validator) GetConsAddr() sdk.ConsAddress { return sdk.ConsAddress(v.ConsPubKey.Address()) } +// MinEqual defines a more minimum set of equality conditions when comparing two +// validators. +func (v *Validator) MinEqual(other *Validator) bool { + return v.OperatorAddress == other.OperatorAddress && + v.Status == other.Status && + v.Tokens.Equal(other.Tokens) && + v.DelegatorShares.Equal(other.DelegatorShares) && + v.Description.Equal(other.Description) && + v.Commission.Equal(other.Commission) && + v.Jailed == other.Jailed && + v.MinSelfDelegation.Equal(other.MinSelfDelegation) && + v.ConsensusPubkey.Equal(other.ConsensusPubkey) + +} + +// Equal checks if the receiver equals the parameter +func (v *Validator) Equal(v2 *Validator) bool { + return v.MinEqual(v2) && + v.UnbondingHeight == v2.UnbondingHeight && + v.UnbondingTime.Equal(v2.UnbondingTime) +} + +func (v Validator) IsJailed() bool { return v.Jailed } +func (v Validator) GetMoniker() string { return v.Description.Moniker } +func (v Validator) GetStatus() BondStatus { return v.Status } +func (v Validator) GetOperator() sdk.ValAddress { + if v.OperatorAddress == "" { + return nil + } + addr, err := sdk.ValAddressFromBech32(v.OperatorAddress) + if err != nil { + panic(err) + } + return addr +} + +// ConsPubKey returns the validator PubKey as a cryptotypes.PubKey. +func (v Validator) ConsPubKey() (cryptotypes.PubKey, error) { + pk, ok := v.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expecting cryptotypes.PubKey, got %T", pk) + } + + return pk, nil + +} + +// TmConsPublicKey casts Validator.ConsensusPubkey to tmprotocrypto.PubKey. +func (v Validator) TmConsPublicKey() (tmprotocrypto.PublicKey, error) { + pk, err := v.ConsPubKey() + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + + tmPk, err := cryptocodec.ToTmProtoPublicKey(pk) + if err != nil { + return tmprotocrypto.PublicKey{}, err + } + + return tmPk, nil +} + +// GetConsAddr extracts Consensus key address +func (v Validator) GetConsAddr() (sdk.ConsAddress, error) { + pk, ok := v.ConsensusPubkey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expecting cryptotypes.PubKey, got %T", pk) + } + + return sdk.ConsAddress(pk.Address()), nil +} + func (v Validator) GetTokens() sdk.Int { return v.Tokens } func (v Validator) GetBondedTokens() sdk.Int { return v.BondedTokens() } func (v Validator) GetConsensusPower() int64 { return v.ConsensusPower() } func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate } func (v Validator) GetMinSelfDelegation() sdk.Int { return v.MinSelfDelegation } func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (v Validator) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + var pk cryptotypes.PubKey + return unpacker.UnpackAny(v.ConsensusPubkey, &pk) +} diff --git a/x/staking/types/validator_test.go b/x/staking/types/validator_test.go index 2716b8bfa8b2..08204215d595 100644 --- a/x/staking/types/validator_test.go +++ b/x/staking/types/validator_test.go @@ -1,49 +1,46 @@ -package types +package types_test import ( - "fmt" "math/rand" - "reflect" "sort" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" tmtypes "github.com/tendermint/tendermint/types" - yaml "gopkg.in/yaml.v2" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestValidatorTestEquivalent(t *testing.T) { - val1 := NewValidator(valAddr1, pk1, Description{}) - val2 := NewValidator(valAddr1, pk1, Description{}) + val1 := newValidator(t, valAddr1, pk1) + val2 := newValidator(t, valAddr1, pk1) + require.Equal(t, val1.String(), val2.String()) - ok := val1.TestEquivalent(val2) - require.True(t, ok) - - val2 = NewValidator(valAddr2, pk2, Description{}) - - ok = val1.TestEquivalent(val2) - require.False(t, ok) + val2 = newValidator(t, valAddr2, pk2) + require.NotEqual(t, val1.String(), val2.String()) } func TestUpdateDescription(t *testing.T) { - d1 := Description{ + d1 := types.Description{ Website: "https://validator.cosmos", Details: "Test validator", } - d2 := Description{ - Moniker: DoNotModifyDesc, - Identity: DoNotModifyDesc, - Website: DoNotModifyDesc, - Details: DoNotModifyDesc, + d2 := types.Description{ + Moniker: types.DoNotModifyDesc, + Identity: types.DoNotModifyDesc, + Website: types.DoNotModifyDesc, + Details: types.DoNotModifyDesc, } - d3 := Description{ + d3 := types.Description{ Moniker: "", Identity: "", Website: "", @@ -60,29 +57,25 @@ func TestUpdateDescription(t *testing.T) { } func TestABCIValidatorUpdate(t *testing.T) { - validator := NewValidator(valAddr1, pk1, Description{}) - + validator := newValidator(t, valAddr1, pk1) abciVal := validator.ABCIValidatorUpdate() - require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) + pk, err := validator.TmConsPublicKey() + require.NoError(t, err) + require.Equal(t, pk, abciVal.PubKey) require.Equal(t, validator.BondedTokens().Int64(), abciVal.Power) } func TestABCIValidatorUpdateZero(t *testing.T) { - validator := NewValidator(valAddr1, pk1, Description{}) - + validator := newValidator(t, valAddr1, pk1) abciVal := validator.ABCIValidatorUpdateZero() - require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) + pk, err := validator.TmConsPublicKey() + require.NoError(t, err) + require.Equal(t, pk, abciVal.PubKey) require.Equal(t, int64(0), abciVal.Power) } func TestShareTokens(t *testing.T) { - validator := Validator{ - OperatorAddress: valAddr1, - ConsPubKey: pk1, - Status: sdk.Bonded, - Tokens: sdk.NewInt(100), - DelegatorShares: sdk.NewDec(100), - } + validator := mkValidator(100, sdk.NewDec(100)) assert.True(sdk.DecEq(t, sdk.NewDec(50), validator.TokensFromShares(sdk.NewDec(50)))) validator.Tokens = sdk.NewInt(50) @@ -91,24 +84,15 @@ func TestShareTokens(t *testing.T) { } func TestRemoveTokens(t *testing.T) { - valPubKey := pk1 - valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) - - validator := Validator{ - OperatorAddress: valAddr, - ConsPubKey: valPubKey, - Status: sdk.Bonded, - Tokens: sdk.NewInt(100), - DelegatorShares: sdk.NewDec(100), - } + validator := mkValidator(100, sdk.NewDec(100)) // remove tokens and test check everything validator = validator.RemoveTokens(sdk.NewInt(10)) require.Equal(t, int64(90), validator.Tokens.Int64()) // update validator to from bonded -> unbonded - validator = validator.UpdateStatus(sdk.Unbonded) - require.Equal(t, sdk.Unbonded, validator.Status) + validator = validator.UpdateStatus(types.Unbonded) + require.Equal(t, types.Unbonded, validator.Status) validator = validator.RemoveTokens(sdk.NewInt(10)) require.Panics(t, func() { validator.RemoveTokens(sdk.NewInt(-1)) }) @@ -116,8 +100,8 @@ func TestRemoveTokens(t *testing.T) { } func TestAddTokensValidatorBonded(t *testing.T) { - validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) - validator = validator.UpdateStatus(sdk.Bonded) + validator := newValidator(t, valAddr1, pk1) + validator = validator.UpdateStatus(types.Bonded) validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) @@ -126,34 +110,34 @@ func TestAddTokensValidatorBonded(t *testing.T) { } func TestAddTokensValidatorUnbonding(t *testing.T) { - validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) - validator = validator.UpdateStatus(sdk.Unbonding) + validator := newValidator(t, valAddr1, pk1) + validator = validator.UpdateStatus(types.Unbonding) validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) - assert.Equal(t, sdk.Unbonding, validator.Status) + assert.Equal(t, types.Unbonding, validator.Status) assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) } func TestAddTokensValidatorUnbonded(t *testing.T) { - validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) - validator = validator.UpdateStatus(sdk.Unbonded) + validator := newValidator(t, valAddr1, pk1) + validator = validator.UpdateStatus(types.Unbonded) validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) - assert.Equal(t, sdk.Unbonded, validator.Status) + assert.Equal(t, types.Unbonded, validator.Status) assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) } // TODO refactor to make simpler like the AddToken tests above func TestRemoveDelShares(t *testing.T) { - valA := Validator{ - OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), - ConsPubKey: pk1, - Status: sdk.Bonded, + valA := types.Validator{ + OperatorAddress: valAddr1.String(), + ConsensusPubkey: pk1Any, + Status: types.Bonded, Tokens: sdk.NewInt(100), DelegatorShares: sdk.NewDec(100), } @@ -165,24 +149,14 @@ func TestRemoveDelShares(t *testing.T) { require.Equal(t, int64(90), valB.BondedTokens().Int64()) // specific case from random tests - poolTokens := sdk.NewInt(5102) - delShares := sdk.NewDec(115) - validator := Validator{ - OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), - ConsPubKey: pk1, - Status: sdk.Bonded, - Tokens: poolTokens, - DelegatorShares: delShares, - } - - shares := sdk.NewDec(29) - _, tokens := validator.RemoveDelShares(shares) + validator := mkValidator(5102, sdk.NewDec(115)) + _, tokens := validator.RemoveDelShares(sdk.NewDec(29)) require.True(sdk.IntEq(t, sdk.NewInt(1286), tokens)) } func TestAddTokensFromDel(t *testing.T) { - validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator := newValidator(t, valAddr1, pk1) validator, shares := validator.AddTokensFromDel(sdk.NewInt(6)) require.True(sdk.DecEq(t, sdk.NewDec(6), shares)) @@ -196,34 +170,27 @@ func TestAddTokensFromDel(t *testing.T) { } func TestUpdateStatus(t *testing.T) { - validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator := newValidator(t, valAddr1, pk1) validator, _ = validator.AddTokensFromDel(sdk.NewInt(100)) - require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, types.Unbonded, validator.Status) require.Equal(t, int64(100), validator.Tokens.Int64()) // Unbonded to Bonded - validator = validator.UpdateStatus(sdk.Bonded) - require.Equal(t, sdk.Bonded, validator.Status) + validator = validator.UpdateStatus(types.Bonded) + require.Equal(t, types.Bonded, validator.Status) // Bonded to Unbonding - validator = validator.UpdateStatus(sdk.Unbonding) - require.Equal(t, sdk.Unbonding, validator.Status) + validator = validator.UpdateStatus(types.Unbonding) + require.Equal(t, types.Unbonding, validator.Status) // Unbonding to Bonded - validator = validator.UpdateStatus(sdk.Bonded) - require.Equal(t, sdk.Bonded, validator.Status) + validator = validator.UpdateStatus(types.Bonded) + require.Equal(t, types.Bonded, validator.Status) } func TestPossibleOverflow(t *testing.T) { delShares := sdk.NewDec(391432570689183511).Quo(sdk.NewDec(40113011844664)) - validator := Validator{ - OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), - ConsPubKey: pk1, - Status: sdk.Bonded, - Tokens: sdk.NewInt(2159), - DelegatorShares: delShares, - } - + validator := mkValidator(2159, delShares) newValidator, _ := validator.AddTokensFromDel(sdk.NewInt(71)) require.False(t, newValidator.DelegatorShares.IsNegative()) @@ -231,31 +198,31 @@ func TestPossibleOverflow(t *testing.T) { } func TestValidatorMarshalUnmarshalJSON(t *testing.T) { - validator := NewValidator(valAddr1, pk1, Description{}) - js, err := codec.Cdc.MarshalJSON(validator) + validator := newValidator(t, valAddr1, pk1) + js, err := legacy.Cdc.MarshalJSON(validator) require.NoError(t, err) require.NotEmpty(t, js) - require.Contains(t, string(js), "\"consensus_pubkey\":\"fetchvalconspu") - got := &Validator{} - err = codec.Cdc.UnmarshalJSON(js, got) + require.Contains(t, string(js), "\"consensus_pubkey\":{\"type\":\"tendermint/PubKeyEd25519\"") + got := &types.Validator{} + err = legacy.Cdc.UnmarshalJSON(js, got) assert.NoError(t, err) - assert.Equal(t, validator, *got) + assert.True(t, validator.Equal(got)) } func TestValidatorSetInitialCommission(t *testing.T) { - val := NewValidator(valAddr1, pk1, Description{}) + val := newValidator(t, valAddr1, pk1) testCases := []struct { - validator Validator - commission Commission + validator types.Validator + commission types.Commission expectedErr bool }{ - {val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), false}, - {val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec()), true}, - {val, NewCommission(sdk.ZeroDec(), sdk.NewDec(15000000000), sdk.ZeroDec()), true}, - {val, NewCommission(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.ZeroDec()), true}, - {val, NewCommission(sdk.NewDecWithPrec(2, 1), sdk.NewDecWithPrec(1, 1), sdk.ZeroDec()), true}, - {val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1)), true}, - {val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(2, 1)), true}, + {val, types.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), false}, + {val, types.NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec()), true}, + {val, types.NewCommission(sdk.ZeroDec(), sdk.NewDec(15000000000), sdk.ZeroDec()), true}, + {val, types.NewCommission(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.ZeroDec()), true}, + {val, types.NewCommission(sdk.NewDecWithPrec(2, 1), sdk.NewDecWithPrec(1, 1), sdk.ZeroDec()), true}, + {val, types.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1)), true}, + {val, types.NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(2, 1)), true}, } for i, tc := range testCases { @@ -276,51 +243,19 @@ func TestValidatorSetInitialCommission(t *testing.T) { } } -func TestValidatorMarshalYAML(t *testing.T) { - validator := NewValidator(valAddr1, pk1, Description{}) - bechifiedPub, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, validator.ConsPubKey) - require.NoError(t, err) - bs, err := yaml.Marshal(validator) - require.NoError(t, err) - want := fmt.Sprintf(`| - operatoraddress: %s - conspubkey: %s - jailed: false - status: 0 - tokens: "0" - delegatorshares: "0.000000000000000000" - description: - moniker: "" - identity: "" - website: "" - security_contact: "" - details: "" - unbondingheight: 0 - unbondingcompletiontime: 1970-01-01T00:00:00Z - commission: - commission_rates: - rate: "0.000000000000000000" - max_rate: "0.000000000000000000" - max_change_rate: "0.000000000000000000" - update_time: 1970-01-01T00:00:00Z - minselfdelegation: "1" -`, validator.OperatorAddress.String(), bechifiedPub) - require.Equal(t, want, string(bs)) -} - // Check that sort will create deterministic ordering of validators func TestValidatorsSortDeterminism(t *testing.T) { - vals := make([]Validator, 10) - sortedVals := make([]Validator, 10) + vals := make([]types.Validator, 10) + sortedVals := make([]types.Validator, 10) // Create random validator slice for i := range vals { pk := ed25519.GenPrivKey().PubKey() - vals[i] = NewValidator(sdk.ValAddress(pk.Address()), pk, Description{}) + vals[i] = newValidator(t, sdk.ValAddress(pk.Address()), pk) } // Save sorted copy - sort.Sort(Validators(vals)) + sort.Sort(types.Validators(vals)) copy(sortedVals, vals) // Randomly shuffle validators, sort, and check it is equal to original sort @@ -331,7 +266,85 @@ func TestValidatorsSortDeterminism(t *testing.T) { vals[j] = it }) - Validators(vals).Sort() - require.True(t, reflect.DeepEqual(sortedVals, vals), "Validator sort returned different slices") + types.Validators(vals).Sort() + require.Equal(t, sortedVals, vals, "Validator sort returned different slices") + } +} + +// Check SortTendermint sorts the same as tendermint +func TestValidatorsSortTendermint(t *testing.T) { + vals := make([]types.Validator, 100) + + for i := range vals { + pk := ed25519.GenPrivKey().PubKey() + pk2 := ed25519.GenPrivKey().PubKey() + vals[i] = newValidator(t, sdk.ValAddress(pk2.Address()), pk) + vals[i].Status = types.Bonded + vals[i].Tokens = sdk.NewInt(rand.Int63()) + } + // create some validators with the same power + for i := 0; i < 10; i++ { + vals[i].Tokens = sdk.NewInt(1000000) } + + valz := types.Validators(vals) + + // create expected tendermint validators by converting to tendermint then sorting + expectedVals, err := teststaking.ToTmValidators(valz) + require.NoError(t, err) + sort.Sort(tmtypes.ValidatorsByVotingPower(expectedVals)) + + // sort in SDK and then convert to tendermint + sort.Sort(types.ValidatorsByVotingPower(valz)) + actualVals, err := teststaking.ToTmValidators(valz) + require.NoError(t, err) + + require.Equal(t, expectedVals, actualVals, "sorting in SDK is not the same as sorting in Tendermint") +} + +func TestValidatorToTm(t *testing.T) { + vals := make(types.Validators, 10) + expected := make([]*tmtypes.Validator, 10) + + for i := range vals { + pk := ed25519.GenPrivKey().PubKey() + val := newValidator(t, sdk.ValAddress(pk.Address()), pk) + val.Status = types.Bonded + val.Tokens = sdk.NewInt(rand.Int63()) + vals[i] = val + tmPk, err := cryptocodec.ToTmPubKeyInterface(pk) + require.NoError(t, err) + expected[i] = tmtypes.NewValidator(tmPk, val.ConsensusPower()) + } + vs, err := teststaking.ToTmValidators(vals) + require.NoError(t, err) + require.Equal(t, expected, vs) +} + +func TestBondStatus(t *testing.T) { + require.False(t, types.Unbonded == types.Bonded) + require.False(t, types.Unbonded == types.Unbonding) + require.False(t, types.Bonded == types.Unbonding) + require.Equal(t, types.BondStatus(4).String(), "4") + require.Equal(t, types.BondStatusUnspecified, types.Unspecified.String()) + require.Equal(t, types.BondStatusUnbonded, types.Unbonded.String()) + require.Equal(t, types.BondStatusBonded, types.Bonded.String()) + require.Equal(t, types.BondStatusUnbonding, types.Unbonding.String()) +} + +func mkValidator(tokens int64, shares sdk.Dec) types.Validator { + return types.Validator{ + OperatorAddress: valAddr1.String(), + ConsensusPubkey: pk1Any, + Status: types.Bonded, + Tokens: sdk.NewInt(tokens), + DelegatorShares: shares, + } +} + +// Creates a new validators and asserts the error check. +func newValidator(t *testing.T, 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/supply/alias.go b/x/supply/alias.go deleted file mode 100644 index 1138f7c5eee2..000000000000 --- a/x/supply/alias.go +++ /dev/null @@ -1,49 +0,0 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/supply/internal/keeper -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/supply/internal/types -package supply - -import ( - "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - Minter = types.Minter - Burner = types.Burner - Staking = types.Staking -) - -var ( - // functions aliases - RegisterInvariants = keeper.RegisterInvariants - AllInvariants = keeper.AllInvariants - TotalSupply = keeper.TotalSupply - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - SupplyKey = keeper.SupplyKey - NewModuleAddress = types.NewModuleAddress - NewEmptyModuleAccount = types.NewEmptyModuleAccount - NewModuleAccount = types.NewModuleAccount - RegisterCodec = types.RegisterCodec - NewGenesisState = types.NewGenesisState - DefaultGenesisState = types.DefaultGenesisState - NewSupply = types.NewSupply - DefaultSupply = types.DefaultSupply - - // variable aliases - ModuleCdc = types.ModuleCdc -) - -type ( - Keeper = keeper.Keeper - ModuleAccount = types.ModuleAccount - GenesisState = types.GenesisState - Supply = types.Supply -) diff --git a/x/supply/client/cli/query.go b/x/supply/client/cli/query.go deleted file mode 100644 index 26c4a77cd506..000000000000 --- a/x/supply/client/cli/query.go +++ /dev/null @@ -1,106 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// GetQueryCmd returns the cli query commands for this module -func GetQueryCmd(cdc *codec.Codec) *cobra.Command { - // Group supply queries under a subcommand - supplyQueryCmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Querying commands for the supply module", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - supplyQueryCmd.AddCommand(flags.GetCommands( - GetCmdQueryTotalSupply(cdc), - )...) - - return supplyQueryCmd -} - -// GetCmdQueryTotalSupply implements the query total supply command. -func GetCmdQueryTotalSupply(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ - Use: "total [denom]", - Args: cobra.MaximumNArgs(1), - Short: "Query the total supply of coins of the chain", - Long: strings.TrimSpace( - fmt.Sprintf(`Query total supply of coins that are held by accounts in the - chain. - -Example: -$ %s query %s total - -To query for the total supply of a specific coin denomination use: -$ %s query %s total stake -`, - version.ClientName, types.ModuleName, version.ClientName, types.ModuleName, - ), - ), - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - if len(args) == 0 { - return queryTotalSupply(cliCtx, cdc) - } - return querySupplyOf(cliCtx, cdc, args[0]) - }, - } -} - -func queryTotalSupply(cliCtx context.CLIContext, cdc *codec.Codec) error { - params := types.NewQueryTotalSupplyParams(1, 0) // no pagination - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply), bz) - if err != nil { - return err - } - - var totalSupply sdk.Coins - err = cdc.UnmarshalJSON(res, &totalSupply) - if err != nil { - return err - } - - return cliCtx.PrintOutput(totalSupply) -} - -func querySupplyOf(cliCtx context.CLIContext, cdc *codec.Codec, denom string) error { - params := types.NewQuerySupplyOfParams(denom) - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySupplyOf), bz) - if err != nil { - return err - } - - var supply sdk.Int - err = cdc.UnmarshalJSON(res, &supply) - if err != nil { - return err - } - - return cliCtx.PrintOutput(supply) -} diff --git a/x/supply/client/rest/query.go b/x/supply/client/rest/query.go deleted file mode 100644 index cea93b50b095..000000000000 --- a/x/supply/client/rest/query.go +++ /dev/null @@ -1,90 +0,0 @@ -package rest - -import ( - "fmt" - "net/http" - - "github.com/gorilla/mux" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// RegisterRoutes registers staking-related REST handlers to a router -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - registerQueryRoutes(cliCtx, r) -} - -func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { - // Query the total supply of coins - r.HandleFunc( - "/supply/total", - totalSupplyHandlerFn(cliCtx), - ).Methods("GET") - - // Query the supply of a single denom - r.HandleFunc( - "/supply/total/{denom}", - supplyOfHandlerFn(cliCtx), - ).Methods("GET") -} - -// HTTP request handler to query the total supply of coins -func totalSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) - if !ok { - return - } - - params := types.NewQueryTotalSupplyParams(page, limit) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryTotalSupply), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) - } -} - -// HTTP request handler to query the supply of a single denom -func supplyOfHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - denom := mux.Vars(r)["denom"] - cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) - if !ok { - return - } - - params := types.NewQuerySupplyOfParams(denom) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QuerySupplyOf), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - cliCtx = cliCtx.WithHeight(height) - rest.PostProcessResponse(w, cliCtx, res) - } -} diff --git a/x/supply/exported/exported.go b/x/supply/exported/exported.go deleted file mode 100644 index e872e1deabd1..000000000000 --- a/x/supply/exported/exported.go +++ /dev/null @@ -1,29 +0,0 @@ -package exported - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/cosmos-sdk/x/auth/exported" -) - -// ModuleAccountI defines an account interface for modules that hold tokens in an escrow -type ModuleAccountI interface { - exported.Account - - GetName() string - GetPermissions() []string - HasPermission(string) bool -} - -// SupplyI defines an inflationary supply interface for modules that handle -// token supply. -type SupplyI interface { - GetTotal() sdk.Coins - SetTotal(total sdk.Coins) SupplyI - - Inflate(amount sdk.Coins) SupplyI - Deflate(amount sdk.Coins) SupplyI - - String() string - ValidateBasic() error -} diff --git a/x/supply/genesis.go b/x/supply/genesis.go deleted file mode 100644 index e1dfc41ff555..000000000000 --- a/x/supply/genesis.go +++ /dev/null @@ -1,38 +0,0 @@ -package supply - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// InitGenesis sets supply information for genesis. -// -// CONTRACT: all types of accounts must have been already initialized/created -func InitGenesis(ctx sdk.Context, keeper Keeper, ak types.AccountKeeper, data GenesisState) { - // manually set the total supply based on accounts if not provided - if data.Supply.Empty() { - var totalSupply sdk.Coins - ak.IterateAccounts(ctx, - func(acc authexported.Account) (stop bool) { - totalSupply = totalSupply.Add(acc.GetCoins()...) - return false - }, - ) - - data.Supply = totalSupply - } - - keeper.SetSupply(ctx, types.NewSupply(data.Supply)) -} - -// ExportGenesis returns a GenesisState for a given context and keeper. -func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { - return NewGenesisState(keeper.GetSupply(ctx).GetTotal()) -} - -// ValidateGenesis performs basic validation of supply genesis data returning an -// error for any failed validation criteria. -func ValidateGenesis(data GenesisState) error { - return types.NewSupply(data.Supply).ValidateBasic() -} diff --git a/x/supply/internal/keeper/account.go b/x/supply/internal/keeper/account.go deleted file mode 100644 index 312b039be2c5..000000000000 --- a/x/supply/internal/keeper/account.go +++ /dev/null @@ -1,61 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// GetModuleAddress returns an address based on the module name -func (k Keeper) GetModuleAddress(moduleName string) sdk.AccAddress { - permAddr, ok := k.permAddrs[moduleName] - if !ok { - return nil - } - return permAddr.GetAddress() -} - -// GetModuleAddressAndPermissions returns an address and permissions based on the module name -func (k Keeper) GetModuleAddressAndPermissions(moduleName string) (addr sdk.AccAddress, permissions []string) { - permAddr, ok := k.permAddrs[moduleName] - if !ok { - return addr, permissions - } - return permAddr.GetAddress(), permAddr.GetPermissions() -} - -// GetModuleAccountAndPermissions gets the module account from the auth account store and its -// registered permissions -func (k Keeper) GetModuleAccountAndPermissions(ctx sdk.Context, moduleName string) (exported.ModuleAccountI, []string) { - addr, perms := k.GetModuleAddressAndPermissions(moduleName) - if addr == nil { - return nil, []string{} - } - - acc := k.ak.GetAccount(ctx, addr) - if acc != nil { - macc, ok := acc.(exported.ModuleAccountI) - if !ok { - panic("account is not a module account") - } - return macc, perms - } - - // create a new module account - macc := types.NewEmptyModuleAccount(moduleName, perms...) - maccI := (k.ak.NewAccount(ctx, macc)).(exported.ModuleAccountI) // set the account number - k.SetModuleAccount(ctx, maccI) - - return maccI, perms -} - -// GetModuleAccount gets the module account from the auth account store -func (k Keeper) GetModuleAccount(ctx sdk.Context, moduleName string) exported.ModuleAccountI { - acc, _ := k.GetModuleAccountAndPermissions(ctx, moduleName) - return acc -} - -// SetModuleAccount sets the module account to the auth account store -func (k Keeper) SetModuleAccount(ctx sdk.Context, macc exported.ModuleAccountI) { //nolint:interfacer - k.ak.SetAccount(ctx, macc) -} diff --git a/x/supply/internal/keeper/bank.go b/x/supply/internal/keeper/bank.go deleted file mode 100644 index 9117286aaeb6..000000000000 --- a/x/supply/internal/keeper/bank.go +++ /dev/null @@ -1,151 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// SendCoinsFromModuleToAccount transfers coins from a ModuleAccount to an AccAddress. -// It will panic if the module account does not exist. -func (k Keeper) SendCoinsFromModuleToAccount( - ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, -) error { - - senderAddr := k.GetModuleAddress(senderModule) - if senderAddr == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) - } - - return k.bk.SendCoins(ctx, senderAddr, recipientAddr, amt) -} - -// SendCoinsFromModuleToModule transfers coins from a ModuleAccount to another. -// It will panic if either module account does not exist. -func (k Keeper) SendCoinsFromModuleToModule( - ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins, -) error { - - senderAddr := k.GetModuleAddress(senderModule) - if senderAddr == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) - } - - recipientAcc := k.GetModuleAccount(ctx, recipientModule) - if recipientAcc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) - } - - return k.bk.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) -} - -// SendCoinsFromAccountToModule transfers coins from an AccAddress to a ModuleAccount. -// It will panic if the module account does not exist. -func (k Keeper) SendCoinsFromAccountToModule( - ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins, -) error { - - recipientAcc := k.GetModuleAccount(ctx, recipientModule) - if recipientAcc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) - } - - return k.bk.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) -} - -// DelegateCoinsFromAccountToModule delegates coins and transfers them from a -// delegator account to a module account. It will panic if the module account -// does not exist or is unauthorized. -func (k Keeper) DelegateCoinsFromAccountToModule( - ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins, -) error { - - recipientAcc := k.GetModuleAccount(ctx, recipientModule) - if recipientAcc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) - } - - if !recipientAcc.HasPermission(types.Staking) { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to receive delegated coins", recipientModule)) - } - - return k.bk.DelegateCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) -} - -// UndelegateCoinsFromModuleToAccount undelegates the unbonding coins and transfers -// them from a module account to the delegator account. It will panic if the -// module account does not exist or is unauthorized. -func (k Keeper) UndelegateCoinsFromModuleToAccount( - ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, -) error { - - acc := k.GetModuleAccount(ctx, senderModule) - if acc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) - } - - if !acc.HasPermission(types.Staking) { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to undelegate coins", senderModule)) - } - - return k.bk.UndelegateCoins(ctx, acc.GetAddress(), recipientAddr, amt) -} - -// MintCoins creates new coins from thin air and adds it to the module account. -// It will panic if the module account does not exist or is unauthorized. -func (k Keeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { - acc := k.GetModuleAccount(ctx, moduleName) - if acc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleName)) - } - - if !acc.HasPermission(types.Minter) { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to mint tokens", moduleName)) - } - - _, err := k.bk.AddCoins(ctx, acc.GetAddress(), amt) - if err != nil { - return err - } - - // update total supply - supply := k.GetSupply(ctx) - supply = supply.Inflate(amt) - - k.SetSupply(ctx, supply) - - logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("minted %s from %s module account", amt.String(), moduleName)) - - return nil -} - -// BurnCoins burns coins deletes coins from the balance of the module account. -// It will panic if the module account does not exist or is unauthorized. -func (k Keeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error { - acc := k.GetModuleAccount(ctx, moduleName) - if acc == nil { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleName)) - } - - if !acc.HasPermission(types.Burner) { - panic(sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "module account %s does not have permissions to burn tokens", moduleName)) - } - - _, err := k.bk.SubtractCoins(ctx, acc.GetAddress(), amt) - if err != nil { - return err - } - - // update total supply - supply := k.GetSupply(ctx) - supply = supply.Deflate(amt) - k.SetSupply(ctx, supply) - - logger := k.Logger(ctx) - logger.Info(fmt.Sprintf("burned %s from %s module account", amt.String(), moduleName)) - - return nil -} diff --git a/x/supply/internal/keeper/bank_test.go b/x/supply/internal/keeper/bank_test.go deleted file mode 100644 index 6a817a164f8d..000000000000 --- a/x/supply/internal/keeper/bank_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package keeper_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - keep "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -const initialPower = int64(100) - -// create module accounts for testing -var ( - holderAcc = types.NewEmptyModuleAccount(holder) - burnerAcc = types.NewEmptyModuleAccount(types.Burner, types.Burner) - minterAcc = types.NewEmptyModuleAccount(types.Minter, types.Minter) - multiPermAcc = types.NewEmptyModuleAccount(multiPerm, types.Burner, types.Minter, types.Staking) - randomPermAcc = types.NewEmptyModuleAccount(randomPerm, "random") - - initTokens = sdk.TokensFromConsensusPower(initialPower) - initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) -) - -func getCoinsByName(ctx sdk.Context, sk keep.Keeper, ak types.AccountKeeper, moduleName string) sdk.Coins { - moduleAddress := sk.GetModuleAddress(moduleName) - macc := ak.GetAccount(ctx, moduleAddress) - if macc == nil { - return sdk.Coins(nil) - } - return macc.GetCoins() -} - -func TestSendCoins(t *testing.T) { - app, ctx := createTestApp(false) - keeper := app.SupplyKeeper - ak := app.AccountKeeper - - baseAcc := ak.NewAccountWithAddress(ctx, types.NewModuleAddress("baseAcc")) - - err := holderAcc.SetCoins(initCoins) - require.NoError(t, err) - keeper.SetSupply(ctx, types.NewSupply(initCoins)) - - keeper.SetModuleAccount(ctx, holderAcc) - keeper.SetModuleAccount(ctx, burnerAcc) - ak.SetAccount(ctx, baseAcc) - - require.Panics(t, func() { - keeper.SendCoinsFromModuleToModule(ctx, "", holderAcc.GetName(), initCoins) - }) - - require.Panics(t, func() { - keeper.SendCoinsFromModuleToModule(ctx, types.Burner, "", initCoins) - }) - - require.Panics(t, func() { - keeper.SendCoinsFromModuleToAccount(ctx, "", baseAcc.GetAddress(), initCoins) - }) - - err = keeper.SendCoinsFromModuleToAccount(ctx, holderAcc.GetName(), baseAcc.GetAddress(), initCoins.Add(initCoins...)) - require.Error(t, err) - - err = keeper.SendCoinsFromModuleToModule(ctx, holderAcc.GetName(), types.Burner, initCoins) - require.NoError(t, err) - require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, ak, holderAcc.GetName())) - require.Equal(t, initCoins, getCoinsByName(ctx, keeper, ak, types.Burner)) - - err = keeper.SendCoinsFromModuleToAccount(ctx, types.Burner, baseAcc.GetAddress(), initCoins) - require.NoError(t, err) - require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, ak, types.Burner)) - - require.Equal(t, initCoins, ak.GetAccount(ctx, baseAcc.GetAddress()).GetCoins()) - - err = keeper.SendCoinsFromAccountToModule(ctx, baseAcc.GetAddress(), types.Burner, initCoins) - require.NoError(t, err) - require.Equal(t, sdk.Coins(nil), ak.GetAccount(ctx, baseAcc.GetAddress()).GetCoins()) - require.Equal(t, initCoins, getCoinsByName(ctx, keeper, ak, types.Burner)) -} - -func TestMintCoins(t *testing.T) { - app, ctx := createTestApp(false) - keeper := app.SupplyKeeper - ak := app.AccountKeeper - - keeper.SetModuleAccount(ctx, burnerAcc) - keeper.SetModuleAccount(ctx, minterAcc) - keeper.SetModuleAccount(ctx, multiPermAcc) - keeper.SetModuleAccount(ctx, randomPermAcc) - - initialSupply := keeper.GetSupply(ctx) - - require.Panics(t, func() { keeper.MintCoins(ctx, "", initCoins) }, "no module account") - require.Panics(t, func() { keeper.MintCoins(ctx, types.Burner, initCoins) }, "invalid permission") - err := keeper.MintCoins(ctx, types.Minter, sdk.Coins{sdk.Coin{Denom: "denom", Amount: sdk.NewInt(-10)}}) - require.Error(t, err, "insufficient coins") - - require.Panics(t, func() { keeper.MintCoins(ctx, randomPerm, initCoins) }) - - err = keeper.MintCoins(ctx, types.Minter, initCoins) - require.NoError(t, err) - require.Equal(t, initCoins, getCoinsByName(ctx, keeper, ak, types.Minter)) - require.Equal(t, initialSupply.GetTotal().Add(initCoins...), keeper.GetSupply(ctx).GetTotal()) - - // test same functionality on module account with multiple permissions - initialSupply = keeper.GetSupply(ctx) - - err = keeper.MintCoins(ctx, multiPermAcc.GetName(), initCoins) - require.NoError(t, err) - require.Equal(t, initCoins, getCoinsByName(ctx, keeper, ak, multiPermAcc.GetName())) - require.Equal(t, initialSupply.GetTotal().Add(initCoins...), keeper.GetSupply(ctx).GetTotal()) - - require.Panics(t, func() { keeper.MintCoins(ctx, types.Burner, initCoins) }) -} - -func TestBurnCoins(t *testing.T) { - app, ctx := createTestApp(false) - keeper := app.SupplyKeeper - ak := app.AccountKeeper - - require.NoError(t, burnerAcc.SetCoins(initCoins)) - keeper.SetSupply(ctx, types.NewSupply(initCoins)) - keeper.SetModuleAccount(ctx, burnerAcc) - - initialSupply := keeper.GetSupply(ctx) - initialSupply = initialSupply.Inflate(initCoins) - keeper.SetSupply(ctx, initialSupply) - - require.Panics(t, func() { keeper.BurnCoins(ctx, "", initCoins) }, "no module account") - require.Panics(t, func() { keeper.BurnCoins(ctx, types.Minter, initCoins) }, "invalid permission") - require.Panics(t, func() { keeper.BurnCoins(ctx, randomPerm, initialSupply.GetTotal()) }, "random permission") - err := keeper.BurnCoins(ctx, types.Burner, initialSupply.GetTotal()) - require.Error(t, err, "insufficient coins") - - err = keeper.BurnCoins(ctx, types.Burner, initCoins) - require.NoError(t, err) - require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, ak, types.Burner)) - require.Equal(t, initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) - - // test same functionality on module account with multiple permissions - initialSupply = keeper.GetSupply(ctx) - initialSupply = initialSupply.Inflate(initCoins) - keeper.SetSupply(ctx, initialSupply) - - require.NoError(t, multiPermAcc.SetCoins(initCoins)) - keeper.SetModuleAccount(ctx, multiPermAcc) - - err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins) - require.NoError(t, err) - require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, ak, multiPermAcc.GetName())) - require.Equal(t, initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal()) -} diff --git a/x/supply/internal/keeper/integration_test.go b/x/supply/internal/keeper/integration_test.go deleted file mode 100644 index be065ea16ae8..000000000000 --- a/x/supply/internal/keeper/integration_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package keeper_test - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - keep "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -var ( - multiPerm = "multiple permissions account" - randomPerm = "random permission" - holder = "holder" -) - -// nolint:deadcode,unused -func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { - app := simapp.Setup(isCheckTx) - - // add module accounts to supply keeper - maccPerms := simapp.GetMaccPerms() - maccPerms[holder] = nil - maccPerms[types.Burner] = []string{types.Burner} - maccPerms[types.Minter] = []string{types.Minter} - maccPerms[multiPerm] = []string{types.Burner, types.Minter, types.Staking} - maccPerms[randomPerm] = []string{"random"} - - ctx := app.BaseApp.NewContext(isCheckTx, abci.Header{}) - app.SupplyKeeper = keep.NewKeeper(app.Codec(), app.GetKey(types.StoreKey), app.AccountKeeper, app.BankKeeper, maccPerms) - app.SupplyKeeper.SetSupply(ctx, types.NewSupply(sdk.NewCoins())) - - return app, ctx -} diff --git a/x/supply/internal/keeper/invariants.go b/x/supply/internal/keeper/invariants.go deleted file mode 100644 index aeec368e4bf9..000000000000 --- a/x/supply/internal/keeper/invariants.go +++ /dev/null @@ -1,42 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// RegisterInvariants register all supply invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "total-supply", TotalSupply(k)) -} - -// AllInvariants runs all invariants of the supply module. -func AllInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - return TotalSupply(k)(ctx) - } -} - -// TotalSupply checks that the total supply reflects all the coins held in accounts -func TotalSupply(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var expectedTotal sdk.Coins - supply := k.GetSupply(ctx) - - k.ak.IterateAccounts(ctx, func(acc exported.Account) bool { - expectedTotal = expectedTotal.Add(acc.GetCoins()...) - return false - }) - - broken := !expectedTotal.IsEqual(supply.GetTotal()) - - return sdk.FormatInvariant(types.ModuleName, "total supply", - fmt.Sprintf( - "\tsum of accounts coins: %v\n"+ - "\tsupply.Total: %v\n", - expectedTotal, supply.GetTotal())), broken - } -} diff --git a/x/supply/internal/keeper/keeper.go b/x/supply/internal/keeper/keeper.go deleted file mode 100644 index b071f878db33..000000000000 --- a/x/supply/internal/keeper/keeper.go +++ /dev/null @@ -1,73 +0,0 @@ -package keeper - -import ( - "fmt" - - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// Keeper of the supply store -type Keeper struct { - cdc *codec.Codec - storeKey sdk.StoreKey - ak types.AccountKeeper - bk types.BankKeeper - permAddrs map[string]types.PermissionsForAddress -} - -// NewKeeper creates a new Keeper instance -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ak types.AccountKeeper, bk types.BankKeeper, maccPerms map[string][]string) Keeper { - // set the addresses - permAddrs := make(map[string]types.PermissionsForAddress) - for name, perms := range maccPerms { - permAddrs[name] = types.NewPermissionsForAddress(name, perms) - } - - return Keeper{ - cdc: cdc, - storeKey: key, - ak: ak, - bk: bk, - permAddrs: permAddrs, - } -} - -// 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)) -} - -// GetSupply retrieves the Supply from store -func (k Keeper) GetSupply(ctx sdk.Context) (supply exported.SupplyI) { - store := ctx.KVStore(k.storeKey) - b := store.Get(SupplyKey) - if b == nil { - panic("stored supply should not have been nil") - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &supply) - return -} - -// SetSupply sets the Supply to store -func (k Keeper) SetSupply(ctx sdk.Context, supply exported.SupplyI) { - store := ctx.KVStore(k.storeKey) - b := k.cdc.MustMarshalBinaryLengthPrefixed(supply) - store.Set(SupplyKey, b) -} - -// ValidatePermissions validates that the module account has been granted -// permissions within its set of allowed permissions. -func (k Keeper) ValidatePermissions(macc exported.ModuleAccountI) error { - permAddr := k.permAddrs[macc.GetName()] - for _, perm := range macc.GetPermissions() { - if !permAddr.HasPermission(perm) { - return fmt.Errorf("invalid module permission %s", perm) - } - } - return nil -} diff --git a/x/supply/internal/keeper/keeper_test.go b/x/supply/internal/keeper/keeper_test.go deleted file mode 100644 index d4ff2849e4ca..000000000000 --- a/x/supply/internal/keeper/keeper_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package keeper_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -func TestSupply(t *testing.T) { - initialPower := int64(100) - initTokens := sdk.TokensFromConsensusPower(initialPower) - - app, ctx := createTestApp(false) - totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) - app.SupplyKeeper.SetSupply(ctx, types.NewSupply(totalSupply)) - - total := app.SupplyKeeper.GetSupply(ctx).GetTotal() - - require.Equal(t, totalSupply, total) -} - -func TestValidatePermissions(t *testing.T) { - app, _ := createTestApp(false) - - err := app.SupplyKeeper.ValidatePermissions(multiPermAcc) - require.NoError(t, err) - - err = app.SupplyKeeper.ValidatePermissions(randomPermAcc) - require.NoError(t, err) - - // unregistered permissions - otherAcc := types.NewEmptyModuleAccount("other", "other") - err = app.SupplyKeeper.ValidatePermissions(otherAcc) - require.Error(t, err) -} diff --git a/x/supply/internal/keeper/key.go b/x/supply/internal/keeper/key.go deleted file mode 100644 index 65a23382961f..000000000000 --- a/x/supply/internal/keeper/key.go +++ /dev/null @@ -1,9 +0,0 @@ -package keeper - -// Keys for supply store -// Items are stored with the following key: values -// -// - 0x00: Supply -var ( - SupplyKey = []byte{0x00} -) diff --git a/x/supply/internal/keeper/querier.go b/x/supply/internal/keeper/querier.go deleted file mode 100644 index 093edd2c6bea..000000000000 --- a/x/supply/internal/keeper/querier.go +++ /dev/null @@ -1,70 +0,0 @@ -package keeper - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/client" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// NewQuerier creates a querier for supply REST endpoints -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - - case types.QueryTotalSupply: - return queryTotalSupply(ctx, req, k) - - case types.QuerySupplyOf: - return querySupplyOf(ctx, req, k) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} - -func queryTotalSupply(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryTotalSupplyParams - - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - totalSupply := k.GetSupply(ctx).GetTotal() - - start, end := client.Paginate(len(totalSupply), params.Page, params.Limit, 100) - if start < 0 || end < 0 { - totalSupply = sdk.Coins{} - } else { - totalSupply = totalSupply[start:end] - } - - res, err := totalSupply.MarshalJSON() - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QuerySupplyOfParams - - err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - supply := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom) - - res, err := supply.MarshalJSON() - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} diff --git a/x/supply/internal/keeper/querier_test.go b/x/supply/internal/keeper/querier_test.go deleted file mode 100644 index 92b0dcf32b15..000000000000 --- a/x/supply/internal/keeper/querier_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package keeper_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - keep "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -func TestNewQuerier(t *testing.T) { - app, ctx := createTestApp(false) - keeper := app.SupplyKeeper - cdc := app.Codec() - - supplyCoins := sdk.NewCoins( - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), - sdk.NewCoin("photon", sdk.NewInt(50)), - sdk.NewCoin("atom", sdk.NewInt(2000)), - sdk.NewCoin("btc", sdk.NewInt(21000000)), - ) - - keeper.SetSupply(ctx, types.NewSupply(supplyCoins)) - - query := abci.RequestQuery{ - Path: "", - Data: []byte{}, - } - - querier := keep.NewQuerier(keeper) - - bz, err := querier(ctx, []string{"other"}, query) - require.Error(t, err) - require.Nil(t, bz) - - queryTotalSupplyParams := types.NewQueryTotalSupplyParams(1, 20) - bz, errRes := cdc.MarshalJSON(queryTotalSupplyParams) - require.Nil(t, errRes) - - query.Path = fmt.Sprintf("/custom/supply/%s", types.QueryTotalSupply) - query.Data = bz - - _, err = querier(ctx, []string{types.QueryTotalSupply}, query) - require.Nil(t, err) - - querySupplyParams := types.NewQuerySupplyOfParams(sdk.DefaultBondDenom) - bz, errRes = cdc.MarshalJSON(querySupplyParams) - require.Nil(t, errRes) - - query.Path = fmt.Sprintf("/custom/supply/%s", types.QuerySupplyOf) - query.Data = bz - - _, err = querier(ctx, []string{types.QuerySupplyOf}, query) - require.Nil(t, err) -} - -func TestQuerySupply(t *testing.T) { - app, ctx := createTestApp(false) - keeper := app.SupplyKeeper - cdc := app.Codec() - - supplyCoins := sdk.NewCoins( - sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100)), - sdk.NewCoin("photon", sdk.NewInt(50)), - sdk.NewCoin("atom", sdk.NewInt(2000)), - sdk.NewCoin("btc", sdk.NewInt(21000000)), - ) - - querier := keep.NewQuerier(keeper) - - keeper.SetSupply(ctx, types.NewSupply(supplyCoins)) - - queryTotalSupplyParams := types.NewQueryTotalSupplyParams(1, 10) - bz, errRes := cdc.MarshalJSON(queryTotalSupplyParams) - require.Nil(t, errRes) - - query := abci.RequestQuery{ - Path: "", - Data: []byte{}, - } - - query.Path = fmt.Sprintf("/custom/supply/%s", types.QueryTotalSupply) - query.Data = bz - - res, err := querier(ctx, []string{types.QueryTotalSupply}, query) - require.Nil(t, err) - - var totalCoins sdk.Coins - errRes = cdc.UnmarshalJSON(res, &totalCoins) - require.Nil(t, errRes) - require.Equal(t, supplyCoins, totalCoins) - - querySupplyParams := types.NewQuerySupplyOfParams(sdk.DefaultBondDenom) - bz, errRes = cdc.MarshalJSON(querySupplyParams) - require.Nil(t, errRes) - - query.Path = fmt.Sprintf("/custom/supply/%s", types.QuerySupplyOf) - query.Data = bz - - res, err = querier(ctx, []string{types.QuerySupplyOf}, query) - require.Nil(t, err) - - var supply sdk.Int - errRes = supply.UnmarshalJSON(res) - require.Nil(t, errRes) - require.True(sdk.IntEq(t, sdk.NewInt(100), supply)) - -} diff --git a/x/supply/internal/types/account.go b/x/supply/internal/types/account.go deleted file mode 100644 index ac2136ec6910..000000000000 --- a/x/supply/internal/types/account.go +++ /dev/null @@ -1,176 +0,0 @@ -package types - -import ( - "encoding/json" - "errors" - "fmt" - "strings" - - yaml "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -var ( - _ authexported.GenesisAccount = (*ModuleAccount)(nil) - _ exported.ModuleAccountI = (*ModuleAccount)(nil) -) - -func init() { - // Register the ModuleAccount type as a GenesisAccount so that when no - // concrete GenesisAccount types exist and **default** genesis state is used, - // the genesis state will serialize correctly. - authtypes.RegisterAccountTypeCodec(&ModuleAccount{}, "cosmos-sdk/ModuleAccount") -} - -// ModuleAccount defines an account for modules that holds coins on a pool -type ModuleAccount struct { - *authtypes.BaseAccount - - Name string `json:"name" yaml:"name"` // name of the module - Permissions []string `json:"permissions" yaml:"permissions"` // permissions of module account -} - -// NewModuleAddress creates an AccAddress from the hash of the module's name -func NewModuleAddress(name string) sdk.AccAddress { - return sdk.AccAddress(crypto.AddressHash([]byte(name))) -} - -// NewEmptyModuleAccount creates a empty ModuleAccount from a string -func NewEmptyModuleAccount(name string, permissions ...string) *ModuleAccount { - moduleAddress := NewModuleAddress(name) - baseAcc := authtypes.NewBaseAccountWithAddress(moduleAddress) - - if err := validatePermissions(permissions...); err != nil { - panic(err) - } - - return &ModuleAccount{ - BaseAccount: &baseAcc, - Name: name, - Permissions: permissions, - } -} - -// NewModuleAccount creates a new ModuleAccount instance -func NewModuleAccount(ba *authtypes.BaseAccount, - name string, permissions ...string) *ModuleAccount { - - if err := validatePermissions(permissions...); err != nil { - panic(err) - } - - return &ModuleAccount{ - BaseAccount: ba, - Name: name, - Permissions: permissions, - } -} - -// HasPermission returns whether or not the module account has permission. -func (ma ModuleAccount) HasPermission(permission string) bool { - for _, perm := range ma.Permissions { - if perm == permission { - return true - } - } - return false -} - -// GetName returns the the name of the holder's module -func (ma ModuleAccount) GetName() string { - return ma.Name -} - -// GetPermissions returns permissions granted to the module account -func (ma ModuleAccount) GetPermissions() []string { - return ma.Permissions -} - -// SetPubKey - Implements Account -func (ma ModuleAccount) SetPubKey(pubKey crypto.PubKey) error { - return fmt.Errorf("not supported for module accounts") -} - -// SetSequence - Implements Account -func (ma ModuleAccount) SetSequence(seq uint64) error { - return fmt.Errorf("not supported for module accounts") -} - -// Validate checks for errors on the account fields -func (ma ModuleAccount) Validate() error { - if strings.TrimSpace(ma.Name) == "" { - return errors.New("module account name cannot be blank") - } - if !ma.Address.Equals(sdk.AccAddress(crypto.AddressHash([]byte(ma.Name)))) { - return fmt.Errorf("address %s cannot be derived from the module name '%s'", ma.Address, ma.Name) - } - - return ma.BaseAccount.Validate() -} - -type moduleAccountPretty struct { - Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` - PubKey string `json:"public_key" yaml:"public_key"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - Name string `json:"name" yaml:"name"` - Permissions []string `json:"permissions" yaml:"permissions"` -} - -func (ma ModuleAccount) String() string { - out, _ := ma.MarshalYAML() - return out.(string) -} - -// MarshalYAML returns the YAML representation of a ModuleAccount. -func (ma ModuleAccount) MarshalYAML() (interface{}, error) { - bs, err := yaml.Marshal(moduleAccountPretty{ - Address: ma.Address, - Coins: ma.Coins, - PubKey: "", - AccountNumber: ma.AccountNumber, - Sequence: ma.Sequence, - Name: ma.Name, - Permissions: ma.Permissions, - }) - - if err != nil { - return nil, err - } - - return string(bs), nil -} - -// MarshalJSON returns the JSON representation of a ModuleAccount. -func (ma ModuleAccount) MarshalJSON() ([]byte, error) { - return json.Marshal(moduleAccountPretty{ - Address: ma.Address, - Coins: ma.Coins, - PubKey: "", - AccountNumber: ma.AccountNumber, - Sequence: ma.Sequence, - Name: ma.Name, - Permissions: ma.Permissions, - }) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. -func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { - var alias moduleAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - ma.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) - ma.Name = alias.Name - ma.Permissions = alias.Permissions - - return nil -} diff --git a/x/supply/internal/types/account_test.go b/x/supply/internal/types/account_test.go deleted file mode 100644 index 213e3ad9e4ca..000000000000 --- a/x/supply/internal/types/account_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package types - -import ( - "encoding/json" - "errors" - "fmt" - "testing" - - yaml "gopkg.in/yaml.v2" - - "github.com/tendermint/tendermint/crypto/secp256k1" - - sdk "github.com/cosmos/cosmos-sdk/types" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - "github.com/stretchr/testify/require" -) - -func TestModuleAccountMarshalYAML(t *testing.T) { - name := "test" - moduleAcc := NewEmptyModuleAccount(name, Minter, Burner, Staking) - bs, err := yaml.Marshal(moduleAcc) - require.NoError(t, err) - - want := "|\n address: fetch1n7rdpqvgf37ktx30a2sv2kkszk3m7ncmmfy84w\n coins: []\n public_key: \"\"\n account_number: 0\n sequence: 0\n name: test\n permissions:\n - minter\n - burner\n - staking\n" - require.Equal(t, want, string(bs)) -} - -func TestHasPermissions(t *testing.T) { - name := "test" - macc := NewEmptyModuleAccount(name, Staking, Minter, Burner) - cases := []struct { - permission string - expectHas bool - }{ - {Staking, true}, - {Minter, true}, - {Burner, true}, - {"other", false}, - } - - for i, tc := range cases { - hasPerm := macc.HasPermission(tc.permission) - if tc.expectHas { - require.True(t, hasPerm, "test case #%d", i) - } else { - require.False(t, hasPerm, "test case #%d", i) - } - } -} - -func TestValidate(t *testing.T) { - addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - baseAcc := authtypes.NewBaseAccount(addr, sdk.Coins{}, nil, 0, 0) - tests := []struct { - name string - acc authexported.GenesisAccount - expErr error - }{ - { - "valid module account", - NewEmptyModuleAccount("test"), - nil, - }, - { - "invalid name and address pair", - NewModuleAccount(baseAcc, "test"), - fmt.Errorf("address %s cannot be derived from the module name 'test'", addr), - }, - { - "empty module account name", - NewModuleAccount(baseAcc, " "), - errors.New("module account name cannot be blank"), - }, - } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - err := tt.acc.Validate() - require.Equal(t, tt.expErr, err) - }) - } -} - -func TestModuleAccountJSON(t *testing.T) { - pubkey := secp256k1.GenPrivKey().PubKey() - addr := sdk.AccAddress(pubkey.Address()) - coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := authtypes.NewBaseAccount(addr, coins, nil, 10, 50) - acc := NewModuleAccount(baseAcc, "test", "burner") - - bz, err := json.Marshal(acc) - require.NoError(t, err) - - bz1, err := acc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) - - var a ModuleAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, acc.String(), a.String()) -} diff --git a/x/supply/internal/types/codec.go b/x/supply/internal/types/codec.go deleted file mode 100644 index 89bc9a2ffcf3..000000000000 --- a/x/supply/internal/types/codec.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -// RegisterCodec registers the account types and interface -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil) - cdc.RegisterInterface((*exported.SupplyI)(nil), nil) - cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) - cdc.RegisterConcrete(&Supply{}, "cosmos-sdk/Supply", nil) -} - -// ModuleCdc generic sealed codec to be used throughout module -var ModuleCdc *codec.Codec - -func init() { - cdc := codec.New() - RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - ModuleCdc = cdc.Seal() -} diff --git a/x/supply/internal/types/expected_keepers.go b/x/supply/internal/types/expected_keepers.go deleted file mode 100644 index 3ad1d8b13ce4..000000000000 --- a/x/supply/internal/types/expected_keepers.go +++ /dev/null @@ -1,24 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/exported" -) - -// AccountKeeper defines the expected account keeper (noalias) -type AccountKeeper interface { - IterateAccounts(ctx sdk.Context, process func(exported.Account) (stop bool)) - GetAccount(sdk.Context, sdk.AccAddress) exported.Account - SetAccount(sdk.Context, exported.Account) - NewAccount(sdk.Context, exported.Account) exported.Account -} - -// BankKeeper defines the expected bank keeper (noalias) -type BankKeeper interface { - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - DelegateCoins(ctx sdk.Context, fromAdd, toAddr sdk.AccAddress, amt sdk.Coins) error - UndelegateCoins(ctx sdk.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error - - SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) - AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, error) -} diff --git a/x/supply/internal/types/genesis.go b/x/supply/internal/types/genesis.go deleted file mode 100644 index e73491d18c20..000000000000 --- a/x/supply/internal/types/genesis.go +++ /dev/null @@ -1,20 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// GenesisState is the supply state that must be provided at genesis. -type GenesisState struct { - Supply sdk.Coins `json:"supply" yaml:"supply"` -} - -// NewGenesisState creates a new genesis state. -func NewGenesisState(supply sdk.Coins) GenesisState { - return GenesisState{supply} -} - -// DefaultGenesisState returns a default genesis state -func DefaultGenesisState() GenesisState { - return NewGenesisState(DefaultSupply().GetTotal()) -} diff --git a/x/supply/internal/types/key.go b/x/supply/internal/types/key.go deleted file mode 100644 index 60501a78c664..000000000000 --- a/x/supply/internal/types/key.go +++ /dev/null @@ -1,15 +0,0 @@ -package types - -const ( - // ModuleName is the module name constant used in many places - ModuleName = "supply" - - // 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 -) diff --git a/x/supply/internal/types/querier.go b/x/supply/internal/types/querier.go deleted file mode 100644 index 21934dda3507..000000000000 --- a/x/supply/internal/types/querier.go +++ /dev/null @@ -1,32 +0,0 @@ -package types - -// query endpoints supported by the supply Querier -const ( - QueryTotalSupply = "total_supply" - QuerySupplyOf = "supply_of" -) - -// QueryTotalSupply defines the params for the following queries: -// -// - 'custom/supply/totalSupply' -type QueryTotalSupplyParams struct { - Page, Limit int -} - -// NewQueryTotalSupplyParams creates a new instance to query the total supply -func NewQueryTotalSupplyParams(page, limit int) QueryTotalSupplyParams { - return QueryTotalSupplyParams{page, limit} -} - -// QuerySupplyOfParams defines the params for the following queries: -// -// - 'custom/supply/totalSupplyOf' -type QuerySupplyOfParams struct { - Denom string -} - -// NewQuerySupplyOfParams creates a new instance to query the total supply -// of a given denomination -func NewQuerySupplyOfParams(denom string) QuerySupplyOfParams { - return QuerySupplyOfParams{denom} -} diff --git a/x/supply/internal/types/supply.go b/x/supply/internal/types/supply.go deleted file mode 100644 index 92e4a3e5d2f5..000000000000 --- a/x/supply/internal/types/supply.go +++ /dev/null @@ -1,68 +0,0 @@ -package types - -import ( - "fmt" - - yaml "gopkg.in/yaml.v2" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/exported" -) - -// Implements Delegation interface -var _ exported.SupplyI = Supply{} - -// Supply represents a struct that passively keeps track of the total supply amounts in the network -type Supply struct { - Total sdk.Coins `json:"total" yaml:"total"` // total supply of tokens registered on the chain -} - -// SetTotal sets the total supply. -func (supply Supply) SetTotal(total sdk.Coins) exported.SupplyI { - supply.Total = total - return supply -} - -// GetTotal returns the supply total. -func (supply Supply) GetTotal() sdk.Coins { - return supply.Total -} - -// NewSupply creates a new Supply instance -func NewSupply(total sdk.Coins) exported.SupplyI { - return Supply{total} -} - -// DefaultSupply creates an empty Supply -func DefaultSupply() exported.SupplyI { - return NewSupply(sdk.NewCoins()) -} - -// Inflate adds coins to the total supply -func (supply Supply) Inflate(amount sdk.Coins) exported.SupplyI { - supply.Total = supply.Total.Add(amount...) - return supply -} - -// Deflate subtracts coins from the total supply -func (supply Supply) Deflate(amount sdk.Coins) exported.SupplyI { - supply.Total = supply.Total.Sub(amount) - return supply -} - -// String returns a human readable string representation of a supplier. -func (supply Supply) String() string { - b, err := yaml.Marshal(supply) - if err != nil { - panic(err) - } - return string(b) -} - -// 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()) - } - return nil -} diff --git a/x/supply/legacy/v0_36/types.go b/x/supply/legacy/v0_36/types.go deleted file mode 100644 index 7e4aa425be04..000000000000 --- a/x/supply/legacy/v0_36/types.go +++ /dev/null @@ -1,21 +0,0 @@ -// DONTCOVER -// nolint -package v0_36 - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ModuleName = "supply" - -type ( - GenesisState struct { - Supply sdk.Coins `json:"supply" yaml:"supply"` - } -) - -func EmptyGenesisState() GenesisState { - return GenesisState{ - Supply: sdk.NewCoins(), // leave this empty as it's filled on initialization - } -} diff --git a/x/supply/module.go b/x/supply/module.go deleted file mode 100644 index f9e45322a99c..000000000000 --- a/x/supply/module.go +++ /dev/null @@ -1,171 +0,0 @@ -package supply - -import ( - "encoding/json" - "fmt" - "math/rand" - - "github.com/gorilla/mux" - "github.com/spf13/cobra" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - sim "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/cosmos/cosmos-sdk/x/supply/client/cli" - "github.com/cosmos/cosmos-sdk/x/supply/client/rest" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" - "github.com/cosmos/cosmos-sdk/x/supply/simulation" -) - -var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModule{} -) - -// AppModuleBasic defines the basic application module used by the supply module. -type AppModuleBasic struct{} - -// Name returns the supply module's name. -func (AppModuleBasic) Name() string { - return ModuleName -} - -// RegisterCodec registers the supply module's types for the given codec. -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) -} - -// DefaultGenesis returns default genesis state as raw bytes for the supply -// module. -func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) -} - -// ValidateGenesis performs genesis state validation for the supply module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - var data GenesisState - if err := ModuleCdc.UnmarshalJSON(bz, &data); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) - } - - return ValidateGenesis(data) -} - -// RegisterRESTRoutes registers the REST routes for the supply module. -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { - rest.RegisterRoutes(ctx, rtr) -} - -// GetTxCmd returns the root tx command for the supply module. -func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil } - -// GetQueryCmd returns no root query command for the supply module. -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - return cli.GetQueryCmd(cdc) -} - -//____________________________________________________________________________ - -// AppModule implements an application module for the supply module. -type AppModule struct { - AppModuleBasic - - keeper Keeper - ak types.AccountKeeper -} - -// NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, ak types.AccountKeeper) AppModule { - return AppModule{ - AppModuleBasic: AppModuleBasic{}, - keeper: keeper, - ak: ak, - } -} - -// Name returns the supply module's name. -func (AppModule) Name() string { - return ModuleName -} - -// RegisterInvariants registers the supply module invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - RegisterInvariants(ir, am.keeper) -} - -// Route returns the message routing key for the supply module. -func (AppModule) Route() string { - return RouterKey -} - -// NewHandler returns an sdk.Handler for the supply module. -func (am AppModule) NewHandler() sdk.Handler { return nil } - -// QuerierRoute returns the supply module's querier route name. -func (AppModule) QuerierRoute() string { - return QuerierRoute -} - -// NewQuerierHandler returns the supply module sdk.Querier. -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) -} - -// InitGenesis performs genesis initialization for the supply module. It returns -// no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState GenesisState - ModuleCdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.ak, genesisState) - return []abci.ValidatorUpdate{} -} - -// ExportGenesis returns the exported genesis state as raw bytes for the supply -// module. -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) - return ModuleCdc.MustMarshalJSON(gs) -} - -// BeginBlock returns the begin blocker for the supply module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} - -// EndBlock returns the end blocker for the supply 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 supply module. -func (AppModule) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// ProposalContents doesn't return any content functions for governance proposals. -func (AppModule) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { - return nil -} - -// RandomizedParams doesn't create any randomized supply param changes for the simulator. -func (AppModule) RandomizedParams(_ *rand.Rand) []sim.ParamChange { - return nil -} - -// RegisterStoreDecoder registers a decoder for supply module's types -func (AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// WeightedOperations doesn't return any operation for the nft module. -func (AppModule) WeightedOperations(_ module.SimulationState) []sim.WeightedOperation { - return nil -} diff --git a/x/supply/simulation/decoder.go b/x/supply/simulation/decoder.go deleted file mode 100644 index 3337e4b0687f..000000000000 --- a/x/supply/simulation/decoder.go +++ /dev/null @@ -1,25 +0,0 @@ -package simulation - -import ( - "bytes" - "fmt" - - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// DecodeStore unmarshals the KVPair's Value to the corresponding supply type -func DecodeStore(cdc *codec.Codec, kvA, kvB tmkv.Pair) string { - switch { - case bytes.Equal(kvA.Key[:1], keeper.SupplyKey): - var supplyA, supplyB types.Supply - cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA) - cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB) - return fmt.Sprintf("%v\n%v", supplyB, supplyB) - default: - panic(fmt.Sprintf("invalid supply key %X", kvA.Key)) - } -} diff --git a/x/supply/simulation/decoder_test.go b/x/supply/simulation/decoder_test.go deleted file mode 100644 index f653632f168d..000000000000 --- a/x/supply/simulation/decoder_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package simulation - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - tmkv "github.com/tendermint/tendermint/libs/kv" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/supply/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -func makeTestCodec() (cdc *codec.Codec) { - cdc = codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - types.RegisterCodec(cdc) - return -} -func TestDecodeStore(t *testing.T) { - cdc := makeTestCodec() - - totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000))) - - kvPairs := tmkv.Pairs{ - tmkv.Pair{Key: keeper.SupplyKey, Value: cdc.MustMarshalBinaryLengthPrefixed(totalSupply)}, - tmkv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, - } - - tests := []struct { - name string - expectedLog string - }{ - {"Supply", fmt.Sprintf("%v\n%v", totalSupply, totalSupply)}, - {"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() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name) - default: - require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name) - } - }) - } -} diff --git a/x/supply/simulation/genesis.go b/x/supply/simulation/genesis.go deleted file mode 100644 index 53775fbc07d6..000000000000 --- a/x/supply/simulation/genesis.go +++ /dev/null @@ -1,23 +0,0 @@ -package simulation - -// DONTCOVER - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/codec" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/supply/internal/types" -) - -// RandomizedGenState generates a random GenesisState for supply -func RandomizedGenState(simState *module.SimulationState) { - numAccs := int64(len(simState.Accounts)) - totalSupply := sdk.NewInt(simState.InitialStake * (numAccs + simState.NumBonded)) - supplyGenesis := types.NewGenesisState(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply))) - - fmt.Printf("Generated supply parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, supplyGenesis)) - simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis) -} diff --git a/x/supply/spec/01_concepts.md b/x/supply/spec/01_concepts.md deleted file mode 100644 index 89af77fe68bd..000000000000 --- a/x/supply/spec/01_concepts.md +++ /dev/null @@ -1,69 +0,0 @@ - - -# Concepts - -## Supply - -The `supply` module: - -- passively tracks the total supply of coins within a chain, -- provides a pattern for modules to hold/interact with `Coins`, and -- introduces the invariant check to verify a chain's total supply. - -### Total Supply - -The total `Supply` of the network is equal to the sum of all coins from the -account. The total supply is updated every time a `Coin` is minted (eg: as part -of the inflation mechanism) or burned (eg: due to slashing or if a governance -proposal is vetoed). - -## Module Accounts - -The supply module introduces a new type of `auth.Account` which can be used by -modules to allocate tokens and in special cases mint or burn tokens. At a base -level these module accounts are capable of sending/receiving tokens to and from -`auth.Account`s and other module accounts. This design replaces previous -alternative designs where, to hold tokens, modules would burn the incoming -tokens from the sender account, and then track those tokens internally. Later, -in order to send tokens, the module would need to effectively mint tokens -within a destination account. The new design removes duplicate logic between -modules to perform this accounting. - -The `ModuleAccount` interface is defined as follows: - -```go -type ModuleAccount interface { - auth.Account // same methods as the Account interface - GetName() string // name of the module; used to obtain the address - GetPermissions() []string // permissions of module account - HasPermission(string) bool -} -``` - -> **WARNING!** -Any module or message handler that allows either direct or indirect sending of funds must explicitly guarantee those funds cannot be sent to module accounts (unless allowed). - -The supply `Keeper` also introduces new wrapper functions for the auth `Keeper` -and the bank `Keeper` that are related to `ModuleAccount`s in order to be able -to: - -- Get and set `ModuleAccount`s by providing the `Name`. -- Send coins from and to other `ModuleAccount`s or standard `Account`s - (`BaseAccount` or `VestingAccount`) by passing only the `Name`. -- `Mint` or `Burn` coins for a `ModuleAccount` (restricted to its permissions). - -### Permissions - -Each `ModuleAccount` has a different set of permissions that provide different -object capabilities to perform certain actions. Permissions need to be -registered upon the creation of the supply `Keeper` so that every time a -`ModuleAccount` calls the allowed functions, the `Keeper` can lookup the -permissions to that specific account and perform or not the action. - -The available permissions are: - -- `Minter`: allows for a module to mint a specific amount of coins. -- `Burner`: allows for a module to burn a specific amount of coins. -- `Staking`: allows for a module to delegate and undelegate a specific amount of coins. diff --git a/x/supply/spec/02_state.md b/x/supply/spec/02_state.md deleted file mode 100644 index 5f232d740a86..000000000000 --- a/x/supply/spec/02_state.md +++ /dev/null @@ -1,17 +0,0 @@ - - -# State - -## Supply - -The `Supply` is a passive tracker of the supply of the chain: - -- Supply: `0x0 -> amino(Supply)` - -```go -type Supply struct { - Total sdk.Coins // total supply of tokens registered on the chain -} -``` diff --git a/x/supply/spec/03_future_improvements.md b/x/supply/spec/03_future_improvements.md deleted file mode 100644 index 11011b6ff7a3..000000000000 --- a/x/supply/spec/03_future_improvements.md +++ /dev/null @@ -1,11 +0,0 @@ - - -# Future improvements - -The current supply module only keeps track of the total supply of coins held in the network. - -Future improvements may also include other types of supply such as: - -* **Register Supply:** Register a concrete supply type in order to track it passively on the chain. diff --git a/x/supply/spec/README.md b/x/supply/spec/README.md deleted file mode 100644 index 25f2e3b8afee..000000000000 --- a/x/supply/spec/README.md +++ /dev/null @@ -1,17 +0,0 @@ - - -# `supply` - -## Contents - -1. **[Concept](./01_concepts.md)** - - [Supply](./01_concepts.md#supply) - - [Module Accounts](./01_concepts.md#module-accounts) -2. **[State](./02_state.md)** - - [Supply](./02_state.md#supply) -3. **[Future Improvements](./03_future_improvements.md)** diff --git a/x/upgrade/abci.go b/x/upgrade/abci.go index 11edf7fbbc0c..fa95c4c4aa63 100644 --- a/x/upgrade/abci.go +++ b/x/upgrade/abci.go @@ -2,10 +2,16 @@ package upgrade import ( "fmt" + "time" abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" ) // BeginBlock will check if there is a scheduled plan and if it is ready to be executed. @@ -16,12 +22,27 @@ import ( // The purpose is to ensure the binary is switched EXACTLY at the desired block, and to allow // a migration to be executed if needed upon this switch (migration defined in the new binary) // skipUpgradeHeightArray is a set of block heights for which the upgrade must be skipped -func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) { +func BeginBlocker(k keeper.Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + plan, found := k.GetUpgradePlan(ctx) if !found { return } + // Once we are at the last block this chain will commit, set the upgraded consensus state + // so that IBC clients can use the last NextValidatorsHash as a trusted kernel for verifying + // headers on the next version of the chain. + // Set the time to the last block time of the current chain. + // In order for a client to upgrade successfully, the first block of the new chain must be committed + // within the trusting period of the last block time on this chain. + if plan.IsIBCPlan() && ctx.BlockHeight() == plan.Height-1 { + upgradedConsState := &ibctmtypes.ConsensusState{ + Timestamp: ctx.BlockTime(), + NextValidatorsHash: ctx.BlockHeader().NextValidatorsHash, + } + k.SetUpgradedConsensusState(ctx, plan.Height, upgradedConsState) + } // To make sure clear upgrade is executed at the same block if plan.ShouldExecute(ctx) { // If skip upgrade has been set for current height, we clear the upgrade plan @@ -35,9 +56,17 @@ func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) { } if !k.HasHandler(plan.Name) { - upgradeMsg := fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info) + upgradeMsg := BuildUpgradeNeededMsg(plan) // We don't have an upgrade handler for this upgrade name, meaning this software is out of date so shutdown ctx.Logger().Error(upgradeMsg) + + // Write the upgrade info to disk. The UpgradeStoreLoader uses this info to perform or skip + // store migrations. + err := k.DumpUpgradeInfoToDisk(ctx.BlockHeight(), plan.Name) + if err != nil { + panic(fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error())) + } + panic(upgradeMsg) } // We have an upgrade handler for this upgrade name, so apply the upgrade @@ -55,3 +84,8 @@ func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) { panic(downgradeMsg) } } + +// BuildUpgradeNeededMsg prints the message that notifies that an upgrade is needed. +func BuildUpgradeNeededMsg(plan types.Plan) string { + return fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info) +} diff --git a/x/upgrade/abci_test.go b/x/upgrade/abci_test.go index c82fe757a5e5..eb31857961bc 100644 --- a/x/upgrade/abci_test.go +++ b/x/upgrade/abci_test.go @@ -1,29 +1,38 @@ package upgrade_test import ( + "encoding/json" "errors" + "fmt" + "io/ioutil" + "os" "testing" "time" "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" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/gov" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + clienttypes "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/upgrade" + "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) type TestSuite struct { module module.AppModule - keeper upgrade.Keeper + keeper keeper.Keeper querier sdk.Querier - handler gov.Handler + handler govtypes.Handler ctx sdk.Context } @@ -31,9 +40,9 @@ var s TestSuite func setupTest(height int64, skip map[int64]bool) TestSuite { db := dbm.NewMemDB() - app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, skip, 0) - genesisState := simapp.NewDefaultGenesisState() - stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, skip, simapp.DefaultNodeHome, 0, simapp.MakeTestEncodingConfig(), simapp.EmptyAppOptions{}) + genesisState := simapp.NewDefaultGenesisState(app.AppCodec()) + stateBytes, err := json.MarshalIndent(genesisState, "", " ") if err != nil { panic(err) } @@ -45,10 +54,10 @@ func setupTest(height int64, skip map[int64]bool) TestSuite { ) s.keeper = app.UpgradeKeeper - s.ctx = app.BaseApp.NewContext(false, abci.Header{Height: height, Time: time.Now()}) + s.ctx = app.BaseApp.NewContext(false, tmproto.Header{Height: height, Time: time.Now()}) s.module = upgrade.NewAppModule(s.keeper) - s.querier = s.module.NewQuerierHandler() + s.querier = s.module.LegacyQuerierHandler(app.LegacyAmino()) s.handler = upgrade.NewSoftwareUpgradeProposalHandler(s.keeper) return s } @@ -56,28 +65,28 @@ func setupTest(height int64, skip map[int64]bool) TestSuite { func TestRequireName(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{}}) require.NotNil(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } func TestRequireFutureTime(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: s.ctx.BlockHeader().Time}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: s.ctx.BlockHeader().Time}}) require.NotNil(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } func TestRequireFutureBlock(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight()}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight()}}) require.NotNil(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } func TestCantSetBothTimeAndHeight(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now(), Height: s.ctx.BlockHeight() + 1}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: time.Now(), Height: s.ctx.BlockHeight() + 1}}) require.NotNil(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } @@ -85,7 +94,7 @@ func TestCantSetBothTimeAndHeight(t *testing.T) { func TestDoTimeUpgrade(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify can schedule an upgrade") - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: time.Now()}}) require.Nil(t, err) VerifyDoUpgrade(t) @@ -94,7 +103,7 @@ func TestDoTimeUpgrade(t *testing.T) { func TestDoHeightUpgrade(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify can schedule an upgrade") - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) require.Nil(t, err) VerifyDoUpgrade(t) @@ -103,14 +112,67 @@ func TestDoHeightUpgrade(t *testing.T) { func TestCanOverwriteScheduleUpgrade(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Can overwrite plan") - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "bad_test", Height: s.ctx.BlockHeight() + 10}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "bad_test", Height: s.ctx.BlockHeight() + 10}}) require.Nil(t, err) - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) require.Nil(t, err) VerifyDoUpgrade(t) } +func VerifyDoIBCLastBlock(t *testing.T) { + t.Log("Verify that chain committed to consensus state on the last height it will commit") + nextValsHash := []byte("nextValsHash") + newCtx := s.ctx.WithBlockHeader(tmproto.Header{ + Height: s.ctx.BlockHeight(), + NextValidatorsHash: nextValsHash, + }) + + req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} + s.module.BeginBlock(newCtx, req) + + // plan Height is at ctx.BlockHeight+1 + consState, err := s.keeper.GetUpgradedConsensusState(newCtx, s.ctx.BlockHeight()+1) + require.NoError(t, err) + require.Equal(t, &ibctmtypes.ConsensusState{Timestamp: newCtx.BlockTime(), NextValidatorsHash: nextValsHash}, consState) +} + +func VerifyDoIBCUpgrade(t *testing.T) { + t.Log("Verify that a panic happens at the upgrade time/height") + newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) + + // Check IBC state is set before upgrade using last height: s.ctx.BlockHeight() + cs, err := s.keeper.GetUpgradedClient(newCtx, s.ctx.BlockHeight()) + require.NoError(t, err, "could not retrieve upgraded client before upgrade plan is applied") + require.NotNil(t, cs, "IBC client is nil before upgrade") + + consState, err := s.keeper.GetUpgradedConsensusState(newCtx, s.ctx.BlockHeight()) + require.NoError(t, err, "could not retrieve upgraded consensus state before upgrade plan is applied") + require.NotNil(t, consState, "IBC consensus state is nil before upgrade") + + req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} + require.Panics(t, func() { + s.module.BeginBlock(newCtx, req) + }) + + t.Log("Verify that the upgrade can be successfully applied with a handler") + s.keeper.SetUpgradeHandler("test", func(ctx sdk.Context, plan types.Plan) {}) + require.NotPanics(t, func() { + s.module.BeginBlock(newCtx, req) + }) + + VerifyCleared(t, newCtx) + + // Check IBC state is cleared after upgrade using last height: s.ctx.BlockHeight() + cs, err = s.keeper.GetUpgradedClient(newCtx, s.ctx.BlockHeight()) + require.Error(t, err, "retrieved upgraded client after upgrade plan is applied") + require.Nil(t, cs, "IBC client is not-nil after upgrade") + + consState, err = s.keeper.GetUpgradedConsensusState(newCtx, s.ctx.BlockHeight()) + require.Error(t, err, "retrieved upgraded consensus state after upgrade plan is applied") + require.Nil(t, consState, "IBC consensus state is not-nil after upgrade") +} + func VerifyDoUpgrade(t *testing.T) { t.Log("Verify that a panic happens at the upgrade time/height") newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) @@ -121,7 +183,7 @@ func VerifyDoUpgrade(t *testing.T) { }) t.Log("Verify that the upgrade can be successfully applied with a handler") - s.keeper.SetUpgradeHandler("test", func(ctx sdk.Context, plan upgrade.Plan) {}) + s.keeper.SetUpgradeHandler("test", func(ctx sdk.Context, plan types.Plan) {}) require.NotPanics(t, func() { s.module.BeginBlock(newCtx, req) }) @@ -137,7 +199,7 @@ func VerifyDoUpgradeWithCtx(t *testing.T, newCtx sdk.Context, proposalName strin }) t.Log("Verify that the upgrade can be successfully applied with a handler") - s.keeper.SetUpgradeHandler(proposalName, func(ctx sdk.Context, plan upgrade.Plan) {}) + s.keeper.SetUpgradeHandler(proposalName, func(ctx sdk.Context, plan types.Plan) {}) require.NotPanics(t, func() { s.module.BeginBlock(newCtx, req) }) @@ -149,7 +211,7 @@ func TestHaltIfTooNew(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify that we don't panic with registered plan not in database at all") var called int - s.keeper.SetUpgradeHandler("future", func(ctx sdk.Context, plan upgrade.Plan) { called++ }) + s.keeper.SetUpgradeHandler("future", func(ctx sdk.Context, plan types.Plan) { called++ }) newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} @@ -159,7 +221,7 @@ func TestHaltIfTooNew(t *testing.T) { require.Equal(t, 0, called) t.Log("Verify we panic if we have a registered handler ahead of time") - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "future", Height: s.ctx.BlockHeight() + 3}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "future", Height: s.ctx.BlockHeight() + 3}}) require.NoError(t, err) require.Panics(t, func() { s.module.BeginBlock(newCtx, req) @@ -180,7 +242,7 @@ func TestHaltIfTooNew(t *testing.T) { func VerifyCleared(t *testing.T, newCtx sdk.Context) { t.Log("Verify that the upgrade plan has been cleared") - bz, err := s.querier(newCtx, []string{upgrade.QueryCurrent}, abci.RequestQuery{}) + bz, err := s.querier(newCtx, []string{types.QueryCurrent}, abci.RequestQuery{}) require.NoError(t, err) require.Nil(t, bz) } @@ -188,10 +250,10 @@ func VerifyCleared(t *testing.T, newCtx sdk.Context) { func TestCanClear(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify upgrade is scheduled") - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: time.Now()}}) require.Nil(t, err) - err = s.handler(s.ctx, upgrade.CancelSoftwareUpgradeProposal{Title: "cancel"}) + err = s.handler(s.ctx, &types.CancelSoftwareUpgradeProposal{Title: "cancel"}) require.Nil(t, err) VerifyCleared(t, s.ctx) @@ -199,11 +261,11 @@ func TestCanClear(t *testing.T) { func TestCantApplySameUpgradeTwice(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: time.Now()}}) require.Nil(t, err) VerifyDoUpgrade(t) t.Log("Verify an executed upgrade \"test\" can't be rescheduled") - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Time: time.Now()}}) require.NotNil(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } @@ -218,16 +280,27 @@ func TestNoSpuriousUpgrades(t *testing.T) { } func TestPlanStringer(t *testing.T) { + clientState := &ibctmtypes.ClientState{ChainId: "gaiachain"} + cs, err := clienttypes.PackClientState(clientState) + require.NoError(t, err) + ti, err := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z") require.Nil(t, err) require.Equal(t, `Upgrade Plan Name: test Time: 2020-01-01T00:00:00Z - Info: `, upgrade.Plan{Name: "test", Time: ti}.String()) + Info: . + Upgraded IBC Client: no upgraded client provided`, types.Plan{Name: "test", Time: ti}.String()) require.Equal(t, `Upgrade Plan Name: test Height: 100 - Info: `, upgrade.Plan{Name: "test", Height: 100}.String()) + Info: . + Upgraded IBC Client: no upgraded client provided`, types.Plan{Name: "test", Height: 100}.String()) + require.Equal(t, fmt.Sprintf(`Upgrade Plan + Name: test + Height: 100 + Info: . + Upgraded IBC Client: %s`, clientState), types.Plan{Name: "test", Height: 100, UpgradedClientState: cs}.String()) } func VerifyNotDone(t *testing.T, newCtx sdk.Context, name string) { @@ -274,7 +347,7 @@ func TestSkipUpgradeSkippingAll(t *testing.T) { newCtx := s.ctx req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: skipOne}}) require.NoError(t, err) t.Log("Verify if skip upgrade flag clears upgrade plan in both cases") @@ -286,7 +359,7 @@ func TestSkipUpgradeSkippingAll(t *testing.T) { }) t.Log("Verify a second proposal also is being cleared") - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop2", Plan: types.Plan{Name: "test2", Height: skipTwo}}) require.NoError(t, err) newCtx = newCtx.WithBlockHeight(skipTwo) @@ -311,7 +384,7 @@ func TestUpgradeSkippingOne(t *testing.T) { newCtx := s.ctx req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: skipOne}}) require.Nil(t, err) t.Log("Verify if skip upgrade flag clears upgrade plan in one case and does upgrade on another") @@ -324,7 +397,7 @@ func TestUpgradeSkippingOne(t *testing.T) { }) t.Log("Verify the second proposal is not skipped") - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop2", Plan: types.Plan{Name: "test2", Height: skipTwo}}) require.Nil(t, err) // Setting block height of proposal test2 newCtx = newCtx.WithBlockHeight(skipTwo) @@ -346,7 +419,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { newCtx := s.ctx req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: skipOne}}) require.Nil(t, err) t.Log("Verify if skip upgrade flag clears upgrade plan in both cases and does third upgrade") @@ -359,7 +432,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { }) // A new proposal with height in skipUpgradeHeights - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop2", Plan: types.Plan{Name: "test2", Height: skipTwo}}) require.Nil(t, err) // Setting block height of proposal test2 newCtx = newCtx.WithBlockHeight(skipTwo) @@ -368,7 +441,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { }) t.Log("Verify a new proposal is not skipped") - err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop3", Plan: upgrade.Plan{Name: "test3", Height: skipThree}}) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop3", Plan: types.Plan{Name: "test3", Height: skipThree}}) require.Nil(t, err) newCtx = newCtx.WithBlockHeight(skipThree) VerifyDoUpgradeWithCtx(t, newCtx, "test3") @@ -383,7 +456,7 @@ func TestUpgradeWithoutSkip(t *testing.T) { s := setupTest(10, map[int64]bool{}) newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} - err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) require.Nil(t, err) t.Log("Verify if upgrade happens without skip upgrade") require.Panics(t, func() { @@ -393,3 +466,51 @@ func TestUpgradeWithoutSkip(t *testing.T) { VerifyDoUpgrade(t) VerifyDone(t, s.ctx, "test") } + +func TestIBCUpgradeWithoutSkip(t *testing.T) { + s := setupTest(10, map[int64]bool{}) + cs, err := clienttypes.PackClientState(&ibctmtypes.ClientState{}) + require.NoError(t, err) + err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{ + Title: "prop", + Plan: types.Plan{ + Name: "test", + Height: s.ctx.BlockHeight() + 1, + UpgradedClientState: cs, + }, + }) + require.Nil(t, err) + + t.Log("Verify if last height stores consensus state") + VerifyDoIBCLastBlock(t) + + VerifyDoUpgrade(t) + VerifyDone(t, s.ctx, "test") +} + +func TestDumpUpgradeInfoToFile(t *testing.T) { + s := setupTest(10, map[int64]bool{}) + + planHeight := s.ctx.BlockHeight() + 1 + name := "test" + t.Log("verify if upgrade height is dumped to file") + err := s.keeper.DumpUpgradeInfoToDisk(planHeight, name) + require.Nil(t, err) + + upgradeInfoFilePath, err := s.keeper.GetUpgradeInfoPath() + require.Nil(t, err) + + data, err := ioutil.ReadFile(upgradeInfoFilePath) + require.NoError(t, err) + + var upgradeInfo storetypes.UpgradeInfo + err = json.Unmarshal(data, &upgradeInfo) + require.Nil(t, err) + + t.Log("Verify upgrade height from file matches ") + require.Equal(t, upgradeInfo.Height, planHeight) + + // clear the test file + err = os.Remove(upgradeInfoFilePath) + require.Nil(t, err) +} diff --git a/x/upgrade/alias.go b/x/upgrade/alias.go deleted file mode 100644 index fe36f23097bc..000000000000 --- a/x/upgrade/alias.go +++ /dev/null @@ -1,41 +0,0 @@ -package upgrade - -// nolint - -import ( - "github.com/cosmos/cosmos-sdk/x/upgrade/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" -) - -const ( - ModuleName = types.ModuleName - RouterKey = types.RouterKey - StoreKey = types.StoreKey - QuerierKey = types.QuerierKey - PlanByte = types.PlanByte - DoneByte = types.DoneByte - ProposalTypeSoftwareUpgrade = types.ProposalTypeSoftwareUpgrade - ProposalTypeCancelSoftwareUpgrade = types.ProposalTypeCancelSoftwareUpgrade - QueryCurrent = types.QueryCurrent - QueryApplied = types.QueryApplied -) - -var ( - // functions aliases - RegisterCodec = types.RegisterCodec - PlanKey = types.PlanKey - NewSoftwareUpgradeProposal = types.NewSoftwareUpgradeProposal - NewCancelSoftwareUpgradeProposal = types.NewCancelSoftwareUpgradeProposal - NewQueryAppliedParams = types.NewQueryAppliedParams - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier -) - -type ( - UpgradeHandler = types.UpgradeHandler - Plan = types.Plan - SoftwareUpgradeProposal = types.SoftwareUpgradeProposal - CancelSoftwareUpgradeProposal = types.CancelSoftwareUpgradeProposal - QueryAppliedParams = types.QueryAppliedParams - Keeper = keeper.Keeper -) diff --git a/x/upgrade/client/cli/query.go b/x/upgrade/client/cli/query.go index 0b33a012071f..675dba3a9e8d 100644 --- a/x/upgrade/client/cli/query.go +++ b/x/upgrade/client/cli/query.go @@ -1,97 +1,113 @@ package cli import ( - "encoding/binary" + "context" "fmt" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" - upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -// GetPlanCmd returns the query upgrade plan command -func GetPlanCmd(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetQueryCmd returns the parent command for all x/upgrade CLi query commands. +func GetQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the upgrade module", + } + + cmd.AddCommand( + GetCurrentPlanCmd(), + GetAppliedPlanCmd(), + ) + + return cmd +} + +// GetCurrentPlanCmd returns the query upgrade plan command. +func GetCurrentPlanCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "plan", Short: "get upgrade plan (if one exists)", Long: "Gets the currently scheduled upgrade plan, if one exists", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) - // ignore height for now - res, _, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", upgrade.QuerierKey, upgrade.QueryCurrent)) + params := types.QueryCurrentPlanRequest{} + res, err := queryClient.CurrentPlan(context.Background(), ¶ms) if err != nil { return err } - if len(res) == 0 { + if res.Plan == nil { return fmt.Errorf("no upgrade scheduled") } - var plan upgrade.Plan - err = cdc.UnmarshalJSON(res, &plan) - if err != nil { - return err - } - return cliCtx.PrintOutput(plan) + return clientCtx.PrintProto(res.GetPlan()) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } -// GetAppliedHeightCmd returns the height at which a completed upgrade was applied -func GetAppliedHeightCmd(storeName string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ +// GetAppliedPlanCmd returns information about the block at which a completed +// upgrade was applied. +func GetAppliedPlanCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "applied [upgrade-name]", Short: "block header for height at which a completed upgrade was applied", Long: "If upgrade-name was previously executed on the chain, this returns the header for the block at which it was applied.\n" + "This helps a client determine which binary was valid over a given range of blocks, as well as more context to understand past migrations.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - - name := args[0] - params := upgrade.NewQueryAppliedParams(name) - bz, err := cliCtx.Codec.MarshalJSON(params) + clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + queryClient := types.NewQueryClient(clientCtx) - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", upgrade.QuerierKey, upgrade.QueryApplied), bz) + params := types.QueryAppliedPlanRequest{Name: args[0]} + res, err := queryClient.AppliedPlan(context.Background(), ¶ms) if err != nil { return err } - if len(res) == 0 { + if res.Height == 0 { return fmt.Errorf("no upgrade found") } - if len(res) != 8 { - return fmt.Errorf("unknown format for applied-upgrade") - } - applied := int64(binary.BigEndian.Uint64(res)) // we got the height, now let's return the headers - node, err := cliCtx.GetNode() + node, err := clientCtx.GetNode() if err != nil { return err } - headers, err := node.BlockchainInfo(applied, applied) + headers, err := node.BlockchainInfo(context.Background(), res.Height, res.Height) if err != nil { return err } if len(headers.BlockMetas) == 0 { - return fmt.Errorf("no headers returned for height %d", applied) + return fmt.Errorf("no headers returned for height %d", res.Height) } // always output json as Header is unreable in toml ([]byte is a long list of numbers) - bz, err = cdc.MarshalJSONIndent(headers.BlockMetas[0], "", " ") + bz, err := clientCtx.LegacyAmino.MarshalJSONIndent(headers.BlockMetas[0], "", " ") if err != nil { return err } - fmt.Println(string(bz)) - return nil + return clientCtx.PrintString(fmt.Sprintf("%s\n", string(bz))) }, } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd } diff --git a/x/upgrade/client/cli/tx.go b/x/upgrade/client/cli/tx.go index 26aa0848c269..32472ad84d16 100644 --- a/x/upgrade/client/cli/tx.go +++ b/x/upgrade/client/cli/tx.go @@ -1,21 +1,17 @@ package cli import ( - "bufio" "fmt" "time" - "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/codec" + "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/x/auth" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/gov" - upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" + "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" ) const ( @@ -23,88 +19,61 @@ const ( TimeFormat = "2006-01-02T15:04:05Z" FlagUpgradeHeight = "upgrade-height" - FlagUpgradeTime = "time" - FlagUpgradeInfo = "info" + FlagUpgradeTime = "upgrade-time" + FlagUpgradeInfo = "upgrade-info" ) -func parseArgsToContent(cmd *cobra.Command, name string) (gov.Content, error) { - title, err := cmd.Flags().GetString(cli.FlagTitle) - if err != nil { - return nil, err - } - - description, err := cmd.Flags().GetString(cli.FlagDescription) - if err != nil { - return nil, err - } - - height, err := cmd.Flags().GetInt64(FlagUpgradeHeight) - if err != nil { - return nil, err - } - - timeStr, err := cmd.Flags().GetString(FlagUpgradeTime) - if err != nil { - return nil, err - } - - if height != 0 && len(timeStr) != 0 { - return nil, fmt.Errorf("only one of --upgrade-time or --upgrade-height should be specified") - } - - var upgradeTime time.Time - if len(timeStr) != 0 { - upgradeTime, err = time.Parse(TimeFormat, timeStr) - if err != nil { - return nil, err - } - } - - info, err := cmd.Flags().GetString(FlagUpgradeInfo) - if err != nil { - return nil, err +// GetTxCmd returns the transaction commands for this module +func GetTxCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Upgrade transaction subcommands", } - plan := upgrade.Plan{Name: name, Time: upgradeTime, Height: height, Info: info} - content := upgrade.NewSoftwareUpgradeProposal(title, description, plan) - return content, nil + return cmd } -// GetCmdSubmitUpgradeProposal implements a command handler for submitting a software upgrade proposal transaction. -func GetCmdSubmitUpgradeProposal(cdc *codec.Codec) *cobra.Command { +// NewCmdSubmitUpgradeProposal implements a command handler for submitting a software upgrade proposal transaction. +func NewCmdSubmitUpgradeProposal() *cobra.Command { cmd := &cobra.Command{ Use: "software-upgrade [name] (--upgrade-height [height] | --upgrade-time [time]) (--upgrade-info [info]) [flags]", Args: cobra.ExactArgs(1), Short: "Submit a software upgrade proposal", Long: "Submit a software upgrade along with an initial deposit.\n" + "Please specify a unique name and height OR time for the upgrade to take effect.\n" + - "You may include info to reference a binary download link, in a format compatible with: https://github.com/regen-network/cosmosd", + "You may include info to reference a binary download link, in a format compatible with: https://github.com/cosmos/cosmos-sdk/tree/master/cosmovisor", RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } name := args[0] content, err := parseArgsToContent(cmd, name) if err != nil { return err } - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - from := cliCtx.GetFromAddress() + from := clientCtx.GetFromAddress() depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) if err != nil { return err } - deposit, err := sdk.ParseCoins(depositStr) + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return err + } + + msg, err := gov.NewMsgSubmitProposal(content, deposit, from) if err != nil { return err } - msg := gov.NewMsgSubmitProposal(content, deposit, from) - if err := msg.ValidateBasic(); err != nil { + if err = msg.ValidateBasic(); err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } @@ -118,25 +87,26 @@ func GetCmdSubmitUpgradeProposal(cdc *codec.Codec) *cobra.Command { return cmd } -// GetCmdSubmitCancelUpgradeProposal implements a command handler for submitting a software upgrade cancel proposal transaction. -func GetCmdSubmitCancelUpgradeProposal(cdc *codec.Codec) *cobra.Command { +// NewCmdSubmitCancelUpgradeProposal implements a command handler for submitting a software upgrade cancel proposal transaction. +func NewCmdSubmitCancelUpgradeProposal() *cobra.Command { cmd := &cobra.Command{ Use: "cancel-software-upgrade [flags]", Args: cobra.ExactArgs(0), - Short: "Submit a software upgrade proposal", + Short: "Cancel the current software upgrade proposal", Long: "Cancel a software upgrade along with an initial deposit.", RunE: func(cmd *cobra.Command, args []string) error { - inBuf := bufio.NewReader(cmd.InOrStdin()) - txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) - cliCtx := context.NewCLIContextWithInput(inBuf).WithCodec(cdc) - from := cliCtx.GetFromAddress() + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + from := clientCtx.GetFromAddress() depositStr, err := cmd.Flags().GetString(cli.FlagDeposit) if err != nil { return err } - deposit, err := sdk.ParseCoins(depositStr) + deposit, err := sdk.ParseCoinsNormalized(depositStr) if err != nil { return err } @@ -151,20 +121,69 @@ func GetCmdSubmitCancelUpgradeProposal(cdc *codec.Codec) *cobra.Command { return err } - content := upgrade.NewCancelSoftwareUpgradeProposal(title, description) + content := types.NewCancelSoftwareUpgradeProposal(title, description) - msg := gov.NewMsgSubmitProposal(content, deposit, from) - if err := msg.ValidateBasic(); err != nil { + msg, err := gov.NewMsgSubmitProposal(content, deposit, from) + if err != nil { return err } - return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + if err = msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } cmd.Flags().String(cli.FlagTitle, "", "title of proposal") cmd.Flags().String(cli.FlagDescription, "", "description of proposal") cmd.Flags().String(cli.FlagDeposit, "", "deposit of proposal") + cmd.MarkFlagRequired(cli.FlagTitle) + cmd.MarkFlagRequired(cli.FlagDescription) return cmd } + +func parseArgsToContent(cmd *cobra.Command, name string) (gov.Content, error) { + title, err := cmd.Flags().GetString(cli.FlagTitle) + if err != nil { + return nil, err + } + + description, err := cmd.Flags().GetString(cli.FlagDescription) + if err != nil { + return nil, err + } + + height, err := cmd.Flags().GetInt64(FlagUpgradeHeight) + if err != nil { + return nil, err + } + + timeStr, err := cmd.Flags().GetString(FlagUpgradeTime) + if err != nil { + return nil, err + } + + if height != 0 && len(timeStr) != 0 { + return nil, fmt.Errorf("only one of --upgrade-time or --upgrade-height should be specified") + } + + var upgradeTime time.Time + if len(timeStr) != 0 { + upgradeTime, err = time.Parse(TimeFormat, timeStr) + if err != nil { + return nil, err + } + } + + info, err := cmd.Flags().GetString(FlagUpgradeInfo) + if err != nil { + return nil, err + } + + plan := types.Plan{Name: name, Time: upgradeTime, Height: height, Info: info} + content := types.NewSoftwareUpgradeProposal(title, description, plan) + return content, nil +} diff --git a/x/upgrade/client/proposal_handler.go b/x/upgrade/client/proposal_handler.go index 314c8ac587a5..a4b64f7ac76a 100644 --- a/x/upgrade/client/proposal_handler.go +++ b/x/upgrade/client/proposal_handler.go @@ -6,4 +6,5 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade/client/rest" ) -var ProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitUpgradeProposal, rest.ProposalRESTHandler) +var ProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitUpgradeProposal, rest.ProposalRESTHandler) +var CancelProposalHandler = govclient.NewProposalHandler(cli.NewCmdSubmitCancelUpgradeProposal, rest.ProposalCancelRESTHandler) diff --git a/x/upgrade/client/rest/query.go b/x/upgrade/client/rest/query.go index bf9bdd341070..299f7f7d8f96 100644 --- a/x/upgrade/client/rest/query.go +++ b/x/upgrade/client/rest/query.go @@ -7,24 +7,25 @@ import ( "github.com/gorilla/mux" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/types/rest" - upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -// RegisterRoutes registers REST routes for the upgrade module under the path specified by routeName. -func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc("/upgrade/current", getCurrentPlanHandler(cliCtx)).Methods("GET") - r.HandleFunc("/upgrade/applied/{name}", getDonePlanHandler(cliCtx)).Methods("GET") - registerTxRoutes(cliCtx, r) +func registerQueryRoutes(clientCtx client.Context, r *mux.Router) { + r.HandleFunc( + "/upgrade/current", getCurrentPlanHandler(clientCtx), + ).Methods("GET") + r.HandleFunc( + "/upgrade/applied/{name}", getDonePlanHandler(clientCtx), + ).Methods("GET") } -func getCurrentPlanHandler(cliCtx context.CLIContext) func(http.ResponseWriter, *http.Request) { +func getCurrentPlanHandler(clientCtx client.Context) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, request *http.Request) { // ignore height for now - res, _, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", upgrade.QuerierKey, upgrade.QueryCurrent)) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + res, _, err := clientCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierKey, types.QueryCurrent)) + if rest.CheckInternalServerError(w, err) { return } if len(res) == 0 { @@ -32,31 +33,28 @@ func getCurrentPlanHandler(cliCtx context.CLIContext) func(http.ResponseWriter, return } - var plan upgrade.Plan - err = cliCtx.Codec.UnmarshalBinaryBare(res, &plan) - if err != nil { - rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + var plan types.Plan + err = clientCtx.LegacyAmino.UnmarshalJSON(res, &plan) + if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponse(w, cliCtx, plan) + rest.PostProcessResponse(w, clientCtx, plan) } } -func getDonePlanHandler(cliCtx context.CLIContext) func(http.ResponseWriter, *http.Request) { +func getDonePlanHandler(clientCtx client.Context) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { name := mux.Vars(r)["name"] - params := upgrade.NewQueryAppliedParams(name) - bz, err := cliCtx.Codec.MarshalJSON(params) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + params := types.QueryAppliedPlanRequest{Name: name} + bz, err := clientCtx.LegacyAmino.MarshalJSON(params) + if rest.CheckBadRequestError(w, err) { return } - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", upgrade.QuerierKey, upgrade.QueryApplied), bz) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + res, _, err := clientCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierKey, types.QueryApplied), bz) + if rest.CheckBadRequestError(w, err) { return } @@ -70,6 +68,6 @@ func getDonePlanHandler(cliCtx context.CLIContext) func(http.ResponseWriter, *ht applied := int64(binary.BigEndian.Uint64(res)) fmt.Println(applied) - rest.PostProcessResponse(w, cliCtx, applied) + rest.PostProcessResponse(w, clientCtx, applied) } } diff --git a/x/upgrade/client/rest/rest.go b/x/upgrade/client/rest/rest.go new file mode 100644 index 000000000000..3192083f8da4 --- /dev/null +++ b/x/upgrade/client/rest/rest.go @@ -0,0 +1,16 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/rest" + + "github.com/cosmos/cosmos-sdk/client" +) + +// RegisterRoutes registers REST routes for the upgrade module under the path specified by routeName. +func RegisterRoutes(clientCtx client.Context, rtr *mux.Router) { + r := rest.WithHTTPDeprecationHeaders(rtr) + registerQueryRoutes(clientCtx, r) + registerTxHandlers(clientCtx, r) +} diff --git a/x/upgrade/client/rest/tx.go b/x/upgrade/client/rest/tx.go index acdec064328d..51bdb8f9235a 100644 --- a/x/upgrade/client/rest/tx.go +++ b/x/upgrade/client/rest/tx.go @@ -4,21 +4,24 @@ import ( "net/http" "time" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/gorilla/mux" govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { - r.HandleFunc("/upgrade/plan", postPlanHandler(cliCtx)).Methods("POST") - r.HandleFunc("/upgrade/cancel", cancelPlanHandler(cliCtx)).Methods("POST") +func registerTxHandlers( + clientCtx client.Context, + r *mux.Router) { + r.HandleFunc("/upgrade/plan", newPostPlanHandler(clientCtx)).Methods("POST") + r.HandleFunc("/upgrade/cancel", newCancelPlanHandler(clientCtx)).Methods("POST") } // PlanRequest defines a proposal for a new upgrade plan. @@ -41,18 +44,25 @@ type CancelRequest struct { Deposit sdk.Coins `json:"deposit" yaml:"deposit"` } -func ProposalRESTHandler(cliCtx context.CLIContext) govrest.ProposalRESTHandler { +func ProposalRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler { + return govrest.ProposalRESTHandler{ + SubRoute: "upgrade", + Handler: newPostPlanHandler(clientCtx), + } +} + +func ProposalCancelRESTHandler(clientCtx client.Context) govrest.ProposalRESTHandler { return govrest.ProposalRESTHandler{ SubRoute: "upgrade", - Handler: postPlanHandler(cliCtx), + Handler: newCancelPlanHandler(clientCtx), } } -func postPlanHandler(cliCtx context.CLIContext) http.HandlerFunc { +func newPostPlanHandler(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req PlanRequest - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -62,37 +72,37 @@ func postPlanHandler(cliCtx context.CLIContext) http.HandlerFunc { } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } var t time.Time if req.UpgradeTime != "" { t, err = time.Parse(time.RFC3339, req.UpgradeTime) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } } plan := types.Plan{Name: req.UpgradeName, Time: t, Height: req.UpgradeHeight, Info: req.UpgradeInfo} content := types.NewSoftwareUpgradeProposal(req.Title, req.Description, plan) - msg := gov.NewMsgSubmitProposal(content, req.Deposit, fromAddr) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, fromAddr) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } -func cancelPlanHandler(cliCtx context.CLIContext) http.HandlerFunc { +func newCancelPlanHandler(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req CancelRequest - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + if !rest.ReadRESTReq(w, r, clientCtx.LegacyAmino, &req) { return } @@ -102,18 +112,20 @@ func cancelPlanHandler(cliCtx context.CLIContext) http.HandlerFunc { } fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if rest.CheckBadRequestError(w, err) { return } content := types.NewCancelSoftwareUpgradeProposal(req.Title, req.Description) - msg := gov.NewMsgSubmitProposal(content, req.Deposit, fromAddr) - if err := msg.ValidateBasic(); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + msg, err := govtypes.NewMsgSubmitProposal(content, req.Deposit, fromAddr) + if rest.CheckBadRequestError(w, err) { + return + } + if rest.CheckBadRequestError(w, msg.ValidateBasic()) { return } - utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + tx.WriteGeneratedTxResponse(clientCtx, w, req.BaseReq, msg) } } diff --git a/x/upgrade/doc.go b/x/upgrade/doc.go index 160580d5c79c..4ac16c878c16 100644 --- a/x/upgrade/doc.go +++ b/x/upgrade/doc.go @@ -30,7 +30,7 @@ and gracefully exit. Generally the application binary will restart on exit, but then will execute this BeginBlocker again and exit, causing a restart loop. Either the operator can manually install the new software, or you can make use of an external watcher daemon to possibly download and then switch binaries, -also potentially doing a backup. An example of such a daemon is https://github.com/regen-network/cosmosd/ +also potentially doing a backup. An example of such a daemon is https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc5/cosmovisor described below under "Automation". When the binary restarts with the upgraded version (here v0.40.0), it will detect we have registered the @@ -68,6 +68,31 @@ as well as providing the opportunity for the upgraded software to perform any ne (with the old binary) and applying the migration (with the new binary) are enforced in the state machine. Actually switching the binaries is an ops task and not handled inside the sdk / abci app. +Here is a sample code to set store migrations with an upgrade: + + // this configures a no-op upgrade handler for the "my-fancy-upgrade" upgrade + app.UpgradeKeeper.SetUpgradeHandler("my-fancy-upgrade", func(ctx sdk.Context, plan upgrade.Plan) { + // upgrade changes here + }) + + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + // handle error + } + + if upgradeInfo.Name == "my-fancy-upgrade" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := store.StoreUpgrades{ + Renamed: []store.StoreRename{{ + OldKey: "foo", + NewKey: "bar", + }}, + Deleted: []string{}, + } + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgrade.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } + Halt Behavior Before halting the ABCI state machine in the BeginBlocker method, the upgrade module will log an error @@ -81,10 +106,10 @@ to lose connectivity with the exiting nodes, thus this module prefers to just ha Automation and Plan.Info -We have deprecated calling out to scripts, instead with propose https://github.com/regen-network/cosmosd -as a model for a watcher daemon that can launch gaiad as a subprocess and then read the upgrade log message +We have deprecated calling out to scripts, instead with propose https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc5/cosmovisor +as a model for a watcher daemon that can launch simd as a subprocess and then read the upgrade log message to swap binaries as needed. You can pass in information into Plan.Info according to the format -specified here https://github.com/regen-network/cosmosd/blob/master/README.md#auto-download . +specified here https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc5/cosmovisor/README.md#auto-download . This will allow a properly configured cosmsod daemon to auto-download new binaries and auto-upgrade. As noted there, this is intended more for full nodes than validators. @@ -102,12 +127,15 @@ period ends after the SoftwareUpgrade proposal. However, let's assume that we don't realize the upgrade has a bug until shortly before it will occur (or while we try it out - hitting some panic in the migration). It would seem the blockchain is stuck, -but we need to allow an escape for social consensus to overrule the planned upgrade. To do so, we are -adding a --unsafe-skip-upgrade flag to the start command, which will cause the node to mark the upgrade -as done upon hiting the planned upgrade height, without halting and without actually performing a migration. +but we need to allow an escape for social consensus to overrule the planned upgrade. To do so, there's +a --unsafe-skip-upgrades flag to the start command, which will cause the node to mark the upgrade +as done upon hitting the planned upgrade height(s), without halting and without actually performing a migration. If over two-thirds run their nodes with this flag on the old binary, it will allow the chain to continue through the upgrade with a manual override. (This must be well-documented for anyone syncing from genesis later on). -(Skip-upgrade flag is in a WIP PR - will update this text when merged ^^) +Example: + simd start --unsafe-skip-upgrades ... + +NOTE: Here simd is used as an example binary, replace it with original binary */ package upgrade diff --git a/x/upgrade/handler.go b/x/upgrade/handler.go index 8a4a9f89ab10..413217dc7206 100644 --- a/x/upgrade/handler.go +++ b/x/upgrade/handler.go @@ -4,18 +4,20 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) // NewSoftwareUpgradeProposalHandler creates a governance handler to manage new proposal types. // It enables SoftwareUpgradeProposal to propose an Upgrade, and CancelSoftwareUpgradeProposal // to abort a previously voted upgrade. -func NewSoftwareUpgradeProposalHandler(k Keeper) govtypes.Handler { +func NewSoftwareUpgradeProposalHandler(k keeper.Keeper) govtypes.Handler { return func(ctx sdk.Context, content govtypes.Content) error { switch c := content.(type) { - case SoftwareUpgradeProposal: + case *types.SoftwareUpgradeProposal: return handleSoftwareUpgradeProposal(ctx, k, c) - case CancelSoftwareUpgradeProposal: + case *types.CancelSoftwareUpgradeProposal: return handleCancelSoftwareUpgradeProposal(ctx, k, c) default: @@ -24,11 +26,11 @@ func NewSoftwareUpgradeProposalHandler(k Keeper) govtypes.Handler { } } -func handleSoftwareUpgradeProposal(ctx sdk.Context, k Keeper, p SoftwareUpgradeProposal) error { +func handleSoftwareUpgradeProposal(ctx sdk.Context, k keeper.Keeper, p *types.SoftwareUpgradeProposal) error { return k.ScheduleUpgrade(ctx, p.Plan) } -func handleCancelSoftwareUpgradeProposal(ctx sdk.Context, k Keeper, p CancelSoftwareUpgradeProposal) error { +func handleCancelSoftwareUpgradeProposal(ctx sdk.Context, k keeper.Keeper, _ *types.CancelSoftwareUpgradeProposal) error { k.ClearUpgradePlan(ctx) return nil } diff --git a/x/upgrade/internal/keeper/keeper.go b/x/upgrade/internal/keeper/keeper.go deleted file mode 100644 index 7a33d7e4b66c..000000000000 --- a/x/upgrade/internal/keeper/keeper.go +++ /dev/null @@ -1,132 +0,0 @@ -package keeper - -import ( - "encoding/binary" - "fmt" - - "github.com/tendermint/tendermint/libs/log" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" -) - -type Keeper struct { - skipUpgradeHeights map[int64]bool - storeKey sdk.StoreKey - cdc *codec.Codec - upgradeHandlers map[string]types.UpgradeHandler -} - -// NewKeeper constructs an upgrade Keeper -func NewKeeper(skipUpgradeHeights map[int64]bool, storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { - return Keeper{ - skipUpgradeHeights: skipUpgradeHeights, - storeKey: storeKey, - cdc: cdc, - upgradeHandlers: map[string]types.UpgradeHandler{}, - } -} - -// SetUpgradeHandler sets an UpgradeHandler for the upgrade specified by name. This handler will be called when the upgrade -// with this name is applied. In order for an upgrade with the given name to proceed, a handler for this upgrade -// must be set even if it is a no-op function. -func (k Keeper) SetUpgradeHandler(name string, upgradeHandler types.UpgradeHandler) { - k.upgradeHandlers[name] = upgradeHandler -} - -// ScheduleUpgrade schedules an upgrade based on the specified plan. -// If there is another Plan already scheduled, it will overwrite it -// (implicitly cancelling the current plan) -func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) error { - if err := plan.ValidateBasic(); err != nil { - return err - } - - if !plan.Time.IsZero() { - if !plan.Time.After(ctx.BlockHeader().Time) { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") - } - } else if plan.Height <= ctx.BlockHeight() { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") - } - - if k.GetDoneHeight(ctx, plan.Name) != 0 { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "upgrade with name %s has already been completed", plan.Name) - } - - bz := k.cdc.MustMarshalBinaryBare(plan) - store := ctx.KVStore(k.storeKey) - store.Set(types.PlanKey(), bz) - - return nil -} - -// GetDoneHeight returns the height at which the given upgrade was executed -func (k Keeper) GetDoneHeight(ctx sdk.Context, name string) int64 { - store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte}) - bz := store.Get([]byte(name)) - if len(bz) == 0 { - return 0 - } - - return int64(binary.BigEndian.Uint64(bz)) -} - -// ClearUpgradePlan clears any schedule upgrade -func (k Keeper) ClearUpgradePlan(ctx sdk.Context) { - store := ctx.KVStore(k.storeKey) - store.Delete(types.PlanKey()) -} - -// 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)) -} - -// GetUpgradePlan returns the currently scheduled Plan if any, setting havePlan to true if there is a scheduled -// upgrade or false if there is none -func (k Keeper) GetUpgradePlan(ctx sdk.Context) (plan types.Plan, havePlan bool) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.PlanKey()) - if bz == nil { - return plan, false - } - - k.cdc.MustUnmarshalBinaryBare(bz, &plan) - return plan, true -} - -// setDone marks this upgrade name as being done so the name can't be reused accidentally -func (k Keeper) setDone(ctx sdk.Context, name string) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte}) - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, uint64(ctx.BlockHeight())) - store.Set([]byte(name), bz) -} - -// HasHandler returns true iff there is a handler registered for this name -func (k Keeper) HasHandler(name string) bool { - _, ok := k.upgradeHandlers[name] - return ok -} - -// ApplyUpgrade will execute the handler associated with the Plan and mark the plan as done. -func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) { - handler := k.upgradeHandlers[plan.Name] - if handler == nil { - panic("ApplyUpgrade should never be called without first checking HasHandler") - } - - handler(ctx, plan) - - k.ClearUpgradePlan(ctx) - k.setDone(ctx, plan.Name) -} - -// IsSkipHeight checks if the given height is part of skipUpgradeHeights -func (k Keeper) IsSkipHeight(height int64) bool { - return k.skipUpgradeHeights[height] -} diff --git a/x/upgrade/internal/keeper/querier.go b/x/upgrade/internal/keeper/querier.go deleted file mode 100644 index 7bddfad34566..000000000000 --- a/x/upgrade/internal/keeper/querier.go +++ /dev/null @@ -1,61 +0,0 @@ -package keeper - -import ( - "encoding/binary" - - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/upgrade/internal/types" -) - -// NewQuerier creates a querier for upgrade cli and REST endpoints -func NewQuerier(k Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { - switch path[0] { - - case types.QueryCurrent: - return queryCurrent(ctx, req, k) - - case types.QueryApplied: - return queryApplied(ctx, req, k) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) - } - } -} - -func queryCurrent(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - plan, has := k.GetUpgradePlan(ctx) - if !has { - return nil, nil - } - - res, err := k.cdc.MarshalJSON(&plan) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return res, nil -} - -func queryApplied(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryAppliedParams - - err := k.cdc.UnmarshalJSON(req.Data, ¶ms) - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) - } - - applied := k.GetDoneHeight(ctx, params.Name) - if applied == 0 { - return nil, nil - } - - bz := make([]byte, 8) - binary.BigEndian.PutUint64(bz, uint64(applied)) - - return bz, nil -} diff --git a/x/upgrade/internal/types/codec.go b/x/upgrade/internal/types/codec.go deleted file mode 100644 index 4a8b07cfbb24..000000000000 --- a/x/upgrade/internal/types/codec.go +++ /dev/null @@ -1,12 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/codec" -) - -// RegisterCodec registers concrete types on the Amino codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(Plan{}, "cosmos-sdk/Plan", nil) - cdc.RegisterConcrete(SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal", nil) - cdc.RegisterConcrete(CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal", nil) -} diff --git a/x/upgrade/internal/types/keys.go b/x/upgrade/internal/types/keys.go deleted file mode 100644 index cda224623b2b..000000000000 --- a/x/upgrade/internal/types/keys.go +++ /dev/null @@ -1,28 +0,0 @@ -package types - -const ( - // ModuleName is the name of this module - ModuleName = "upgrade" - - // RouterKey is used to route governance proposals - RouterKey = ModuleName - - // StoreKey is the prefix under which we store this module's data - StoreKey = ModuleName - - // QuerierKey is used to handle abci_query requests - QuerierKey = ModuleName -) - -const ( - // PlanByte specifies the Byte under which a pending upgrade plan is stored in the store - PlanByte = 0x0 - // DoneByte is a prefix for to look up completed upgrade plan by name - DoneByte = 0x1 -) - -// PlanKey is the key under which the current plan is saved -// We store PlanByte as a const to keep it immutable (unlike a []byte) -func PlanKey() []byte { - return []byte{PlanByte} -} diff --git a/x/upgrade/internal/types/plan.go b/x/upgrade/internal/types/plan.go deleted file mode 100644 index c2b88171e218..000000000000 --- a/x/upgrade/internal/types/plan.go +++ /dev/null @@ -1,78 +0,0 @@ -package types - -import ( - "fmt" - "strings" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// Plan specifies information about a planned upgrade and when it should occur -type Plan struct { - // Sets the name for the upgrade. This name will be used by the upgraded version of the software to apply any - // special "on-upgrade" commands during the first BeginBlock method after the upgrade is applied. It is also used - // to detect whether a software version can handle a given upgrade. If no upgrade handler with this name has been - // set in the software, it will be assumed that the software is out-of-date when the upgrade Time or Height - // is reached and the software will exit. - Name string `json:"name,omitempty"` - - // The time after which the upgrade must be performed. - // Leave set to its zero value to use a pre-defined Height instead. - Time time.Time `json:"time,omitempty"` - - // The height at which the upgrade must be performed. - // Only used if Time is not set. - Height int64 `json:"height,omitempty"` - - // Any application specific upgrade info to be included on-chain - // such as a git commit that validators could automatically upgrade to - Info string `json:"info,omitempty"` -} - -func (p Plan) String() string { - due := p.DueAt() - dueUp := strings.ToUpper(due[0:1]) + due[1:] - return fmt.Sprintf(`Upgrade Plan - Name: %s - %s - Info: %s`, p.Name, dueUp, p.Info) -} - -// ValidateBasic does basic validation of a Plan -func (p Plan) ValidateBasic() error { - if len(p.Name) == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name cannot be empty") - } - if p.Height < 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") - } - if p.Time.IsZero() && p.Height == 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") - } - if !p.Time.IsZero() && p.Height != 0 { - return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") - } - - return nil -} - -// 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() { - return !ctx.BlockTime().Before(p.Time) - } - if p.Height > 0 { - return p.Height <= ctx.BlockHeight() - } - return false -} - -// DueAt is a string representation of when this plan is due to be executed -func (p Plan) DueAt() string { - if !p.Time.IsZero() { - return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) - } - return fmt.Sprintf("height: %d", p.Height) -} diff --git a/x/upgrade/internal/types/plan_test.go b/x/upgrade/internal/types/plan_test.go deleted file mode 100644 index 57e025a0398d..000000000000 --- a/x/upgrade/internal/types/plan_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package types - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func mustParseTime(s string) time.Time { - t, err := time.Parse(time.RFC3339, s) - if err != nil { - panic(err) - } - return t -} - -func TestPlanString(t *testing.T) { - cases := map[string]struct { - p Plan - expect string - }{ - "with time": { - p: Plan{ - Name: "due_time", - Info: "https://foo.bar", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }, - expect: "Upgrade Plan\n Name: due_time\n Time: 2019-07-08T11:33:55Z\n Info: https://foo.bar", - }, - "with height": { - p: Plan{ - Name: "by height", - Info: "https://foo.bar/baz", - Height: 7890, - }, - expect: "Upgrade Plan\n Name: by height\n Height: 7890\n Info: https://foo.bar/baz", - }, - "neither": { - p: Plan{ - Name: "almost-empty", - }, - expect: "Upgrade Plan\n Name: almost-empty\n Height: 0\n Info: ", - }, - } - - for name, tc := range cases { - tc := tc // copy to local variable for scopelint - t.Run(name, func(t *testing.T) { - s := tc.p.String() - require.Equal(t, tc.expect, s) - }) - } -} - -func TestPlanValid(t *testing.T) { - cases := map[string]struct { - p Plan - valid bool - }{ - "proper": { - p: Plan{ - Name: "all-good", - Info: "some text here", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }, - valid: true, - }, - "proper by height": { - p: Plan{ - Name: "all-good", - Height: 123450000, - }, - valid: true, - }, - "no name": { - p: Plan{ - Height: 123450000, - }, - }, - "no due at": { - p: Plan{ - Name: "missing", - Info: "important", - }, - }, - "negative height": { - p: Plan{ - Name: "minus", - Height: -12345, - }, - }, - } - - for name, tc := range cases { - tc := tc // copy to local variable for scopelint - t.Run(name, func(t *testing.T) { - err := tc.p.ValidateBasic() - if tc.valid { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } - -} - -func TestShouldExecute(t *testing.T) { - cases := map[string]struct { - p Plan - ctxTime time.Time - ctxHeight int64 - expected bool - }{ - "past time": { - p: Plan{ - Name: "do-good", - Info: "some text here", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }, - ctxTime: mustParseTime("2019-07-08T11:32:00Z"), - ctxHeight: 100000, - expected: false, - }, - "on time": { - p: Plan{ - Name: "do-good", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }, - ctxTime: mustParseTime("2019-07-08T11:33:55Z"), - ctxHeight: 100000, - expected: true, - }, - "future time": { - p: Plan{ - Name: "do-good", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }, - ctxTime: mustParseTime("2019-07-08T11:33:57Z"), - ctxHeight: 100000, - expected: true, - }, - "past height": { - p: Plan{ - Name: "do-good", - Height: 1234, - }, - ctxTime: mustParseTime("2019-07-08T11:32:00Z"), - ctxHeight: 1000, - expected: false, - }, - "on height": { - p: Plan{ - Name: "do-good", - Height: 1234, - }, - ctxTime: mustParseTime("2019-07-08T11:32:00Z"), - ctxHeight: 1234, - expected: true, - }, - "future height": { - p: Plan{ - Name: "do-good", - Height: 1234, - }, - ctxTime: mustParseTime("2019-07-08T11:32:00Z"), - ctxHeight: 1235, - expected: true, - }, - } - - for name, tc := range cases { - tc := tc // copy to local variable for scopelint - t.Run(name, func(t *testing.T) { - ctx := sdk.NewContext(nil, abci.Header{Height: tc.ctxHeight, Time: tc.ctxTime}, false, log.NewNopLogger()) - should := tc.p.ShouldExecute(ctx) - assert.Equal(t, tc.expected, should) - }) - } -} diff --git a/x/upgrade/internal/types/proposal.go b/x/upgrade/internal/types/proposal.go deleted file mode 100644 index 958c10674645..000000000000 --- a/x/upgrade/internal/types/proposal.go +++ /dev/null @@ -1,83 +0,0 @@ -package types - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/x/gov" -) - -const ( - ProposalTypeSoftwareUpgrade string = "SoftwareUpgrade" - ProposalTypeCancelSoftwareUpgrade string = "CancelSoftwareUpgrade" -) - -// Software Upgrade Proposals -type SoftwareUpgradeProposal struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` - Plan Plan `json:"plan" yaml:"plan"` -} - -func NewSoftwareUpgradeProposal(title, description string, plan Plan) gov.Content { - return SoftwareUpgradeProposal{title, description, plan} -} - -// Implements Proposal Interface -var _ gov.Content = SoftwareUpgradeProposal{} - -func init() { - gov.RegisterProposalType(ProposalTypeSoftwareUpgrade) - gov.RegisterProposalTypeCodec(SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal") - gov.RegisterProposalType(ProposalTypeCancelSoftwareUpgrade) - gov.RegisterProposalTypeCodec(CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal") -} - -// nolint -func (sup SoftwareUpgradeProposal) GetTitle() string { return sup.Title } -func (sup SoftwareUpgradeProposal) GetDescription() string { return sup.Description } -func (sup SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } -func (sup SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade } -func (sup SoftwareUpgradeProposal) ValidateBasic() error { - if err := sup.Plan.ValidateBasic(); err != nil { - return err - } - return gov.ValidateAbstract(sup) -} - -func (sup SoftwareUpgradeProposal) String() string { - return fmt.Sprintf(`Software Upgrade Proposal: - Title: %s - Description: %s -`, sup.Title, sup.Description) -} - -// Cancel Software Upgrade Proposals -type CancelSoftwareUpgradeProposal struct { - Title string `json:"title" yaml:"title"` - Description string `json:"description" yaml:"description"` -} - -func NewCancelSoftwareUpgradeProposal(title, description string) gov.Content { - return CancelSoftwareUpgradeProposal{title, description} -} - -// Implements Proposal Interface -var _ gov.Content = CancelSoftwareUpgradeProposal{} - -// nolint -func (sup CancelSoftwareUpgradeProposal) GetTitle() string { return sup.Title } -func (sup CancelSoftwareUpgradeProposal) GetDescription() string { return sup.Description } -func (sup CancelSoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } -func (sup CancelSoftwareUpgradeProposal) ProposalType() string { - return ProposalTypeCancelSoftwareUpgrade -} -func (sup CancelSoftwareUpgradeProposal) ValidateBasic() error { - return gov.ValidateAbstract(sup) -} - -func (sup CancelSoftwareUpgradeProposal) String() string { - return fmt.Sprintf(`Cancel Software Upgrade Proposal: - Title: %s - Description: %s -`, sup.Title, sup.Description) -} diff --git a/x/upgrade/internal/types/proposal_test.go b/x/upgrade/internal/types/proposal_test.go deleted file mode 100644 index 02d204a16722..000000000000 --- a/x/upgrade/internal/types/proposal_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/gov" -) - -type ProposalWrapper struct { - Prop gov.Content -} - -func TestContentAccessors(t *testing.T) { - cases := map[string]struct { - p gov.Content - title string - desc string - typ string - str string - }{ - "upgrade": { - p: NewSoftwareUpgradeProposal("Title", "desc", Plan{ - Name: "due_time", - Info: "https://foo.bar", - Time: mustParseTime("2019-07-08T11:33:55Z"), - }), - title: "Title", - desc: "desc", - typ: "SoftwareUpgrade", - str: "Software Upgrade Proposal:\n Title: Title\n Description: desc\n", - }, - "cancel": { - p: NewCancelSoftwareUpgradeProposal("Cancel", "bad idea"), - title: "Cancel", - desc: "bad idea", - typ: "CancelSoftwareUpgrade", - str: "Cancel Software Upgrade Proposal:\n Title: Cancel\n Description: bad idea\n", - }, - } - - cdc := codec.New() - gov.RegisterCodec(cdc) - RegisterCodec(cdc) - - for name, tc := range cases { - tc := tc // copy to local variable for scopelint - t.Run(name, func(t *testing.T) { - assert.Equal(t, tc.title, tc.p.GetTitle()) - assert.Equal(t, tc.desc, tc.p.GetDescription()) - assert.Equal(t, tc.typ, tc.p.ProposalType()) - assert.Equal(t, "upgrade", tc.p.ProposalRoute()) - assert.Equal(t, tc.str, tc.p.String()) - - // try to encode and decode type to ensure codec works - wrap := ProposalWrapper{tc.p} - bz, err := cdc.MarshalBinaryBare(&wrap) - require.NoError(t, err) - unwrap := ProposalWrapper{} - err = cdc.UnmarshalBinaryBare(bz, &unwrap) - require.NoError(t, err) - - // all methods should look the same - assert.Equal(t, tc.title, unwrap.Prop.GetTitle()) - assert.Equal(t, tc.desc, unwrap.Prop.GetDescription()) - assert.Equal(t, tc.typ, unwrap.Prop.ProposalType()) - assert.Equal(t, "upgrade", unwrap.Prop.ProposalRoute()) - assert.Equal(t, tc.str, unwrap.Prop.String()) - - }) - - } -} diff --git a/x/upgrade/internal/types/querier.go b/x/upgrade/internal/types/querier.go deleted file mode 100644 index 94c247f80c20..000000000000 --- a/x/upgrade/internal/types/querier.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -// query endpoints supported by the upgrade Querier -const ( - QueryCurrent = "current" - QueryApplied = "applied" -) - -// QueryAppliedParams is passed as data with QueryApplied -type QueryAppliedParams struct { - Name string -} - -// NewQueryAppliedParams creates a new instance to query -// if a named plan was applied -func NewQueryAppliedParams(name string) QueryAppliedParams { - return QueryAppliedParams{Name: name} -} diff --git a/x/upgrade/keeper/grpc_query.go b/x/upgrade/keeper/grpc_query.go new file mode 100644 index 000000000000..26e7860c8627 --- /dev/null +++ b/x/upgrade/keeper/grpc_query.go @@ -0,0 +1,54 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +var _ types.QueryServer = Keeper{} + +// CurrentPlan implements the Query/CurrentPlan gRPC method +func (k Keeper) CurrentPlan(c context.Context, req *types.QueryCurrentPlanRequest) (*types.QueryCurrentPlanResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + plan, found := k.GetUpgradePlan(ctx) + if !found { + return &types.QueryCurrentPlanResponse{}, nil + } + + return &types.QueryCurrentPlanResponse{Plan: &plan}, nil +} + +// AppliedPlan implements the Query/AppliedPlan gRPC method +func (k Keeper) AppliedPlan(c context.Context, req *types.QueryAppliedPlanRequest) (*types.QueryAppliedPlanResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + applied := k.GetDoneHeight(ctx, req.Name) + if applied == 0 { + return &types.QueryAppliedPlanResponse{}, nil + } + + return &types.QueryAppliedPlanResponse{Height: applied}, nil +} + +// UpgradedConsensusState implements the Query/UpgradedConsensusState gRPC method +func (k Keeper) UpgradedConsensusState(c context.Context, req *types.QueryUpgradedConsensusStateRequest) (*types.QueryUpgradedConsensusStateResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + consState, err := k.GetUpgradedConsensusState(ctx, req.LastHeight) + if err != nil { + return nil, err + } + + cs, err := clienttypes.PackConsensusState(consState) + if err != nil { + return nil, err + } + + return &types.QueryUpgradedConsensusStateResponse{ + UpgradedConsensusState: cs, + }, nil +} diff --git a/x/upgrade/keeper/grpc_query_test.go b/x/upgrade/keeper/grpc_query_test.go new file mode 100644 index 000000000000..d307157402de --- /dev/null +++ b/x/upgrade/keeper/grpc_query_test.go @@ -0,0 +1,143 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +type UpgradeTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx sdk.Context + queryClient types.QueryClient +} + +func (suite *UpgradeTestSuite) SetupTest() { + suite.app = simapp.Setup(false) + suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{}) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.app.UpgradeKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +func (suite *UpgradeTestSuite) TestQueryCurrentPlan() { + var ( + req *types.QueryCurrentPlanRequest + expResponse types.QueryCurrentPlanResponse + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "without current upgrade plan", + func() { + req = &types.QueryCurrentPlanRequest{} + expResponse = types.QueryCurrentPlanResponse{} + }, + true, + }, + { + "with current upgrade plan", + func() { + plan := types.Plan{Name: "test-plan", Height: 5} + suite.app.UpgradeKeeper.ScheduleUpgrade(suite.ctx, plan) + + req = &types.QueryCurrentPlanRequest{} + expResponse = types.QueryCurrentPlanResponse{Plan: &plan} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + res, err := suite.queryClient.CurrentPlan(gocontext.Background(), req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(&expResponse, res) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *UpgradeTestSuite) TestAppliedCurrentPlan() { + var ( + req *types.QueryAppliedPlanRequest + expHeight int64 + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "with non-existent upgrade plan", + func() { + req = &types.QueryAppliedPlanRequest{Name: "foo"} + }, + true, + }, + { + "with applied upgrade plan", + func() { + expHeight = 5 + + planName := "test-plan" + plan := types.Plan{Name: planName, Height: expHeight} + suite.app.UpgradeKeeper.ScheduleUpgrade(suite.ctx, plan) + + suite.ctx = suite.ctx.WithBlockHeight(expHeight) + suite.app.UpgradeKeeper.SetUpgradeHandler(planName, func(ctx sdk.Context, plan types.Plan) {}) + suite.app.UpgradeKeeper.ApplyUpgrade(suite.ctx, plan) + + req = &types.QueryAppliedPlanRequest{Name: planName} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + + res, err := suite.queryClient.AppliedPlan(gocontext.Background(), req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(expHeight, res.Height) + } else { + suite.Require().Error(err) + } + }) + } +} + +func TestUpgradeTestSuite(t *testing.T) { + suite.Run(t, new(UpgradeTestSuite)) +} diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go new file mode 100644 index 000000000000..1092885bcb3f --- /dev/null +++ b/x/upgrade/keeper/keeper.go @@ -0,0 +1,300 @@ +package keeper + +import ( + "encoding/binary" + "encoding/json" + "io/ioutil" + "os" + "path" + "path/filepath" + + "github.com/tendermint/tendermint/libs/log" + tmos "github.com/tendermint/tendermint/libs/os" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +// UpgradeInfoFileName file to store upgrade information +const UpgradeInfoFileName string = "upgrade-info.json" + +type Keeper struct { + homePath string + skipUpgradeHeights map[int64]bool + storeKey sdk.StoreKey + cdc codec.BinaryMarshaler + upgradeHandlers map[string]types.UpgradeHandler +} + +// NewKeeper constructs an upgrade Keeper +func NewKeeper(skipUpgradeHeights map[int64]bool, storeKey sdk.StoreKey, cdc codec.BinaryMarshaler, homePath string) Keeper { + return Keeper{ + homePath: homePath, + skipUpgradeHeights: skipUpgradeHeights, + storeKey: storeKey, + cdc: cdc, + upgradeHandlers: map[string]types.UpgradeHandler{}, + } +} + +// SetUpgradeHandler sets an UpgradeHandler for the upgrade specified by name. This handler will be called when the upgrade +// with this name is applied. In order for an upgrade with the given name to proceed, a handler for this upgrade +// must be set even if it is a no-op function. +func (k Keeper) SetUpgradeHandler(name string, upgradeHandler types.UpgradeHandler) { + k.upgradeHandlers[name] = upgradeHandler +} + +// ScheduleUpgrade schedules an upgrade based on the specified plan. +// If there is another Plan already scheduled, it will overwrite it +// (implicitly cancelling the current plan) +// ScheduleUpgrade will also write the upgraded client to the upgraded client path +// if an upgraded client is specified in the plan +func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) error { + if err := plan.ValidateBasic(); err != nil { + return err + } + + if plan.Time.Unix() > 0 { + if !plan.Time.After(ctx.BlockHeader().Time) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") + } + } else if plan.Height <= ctx.BlockHeight() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") + } + + if k.GetDoneHeight(ctx, plan.Name) != 0 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "upgrade with name %s has already been completed", plan.Name) + } + + store := ctx.KVStore(k.storeKey) + + // clear any old IBC state stored by previous plan + oldPlan, exists := k.GetUpgradePlan(ctx) + if exists && oldPlan.IsIBCPlan() { + k.ClearIBCState(ctx, oldPlan.Height-1) + } + + bz := k.cdc.MustMarshalBinaryBare(&plan) + store.Set(types.PlanKey(), bz) + + if plan.IsIBCPlan() { + // Set UpgradedClientState in store + clientState, err := clienttypes.UnpackClientState(plan.UpgradedClientState) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "could not unpack clientstate: %v", err) + } + // sets the new upgraded client in last height committed on this chain is at plan.Height, + // since the chain will panic at plan.Height and new chain will resume at plan.Height + return k.SetUpgradedClient(ctx, plan.Height, clientState) + } + return nil +} + +// SetUpgradedClient sets the expected upgraded client for the next version of this chain at the last height the current chain will commit. +func (k Keeper) SetUpgradedClient(ctx sdk.Context, planHeight int64, cs ibcexported.ClientState) error { + store := ctx.KVStore(k.storeKey) + + // zero out any custom fields before setting + cs = cs.ZeroCustomFields() + bz, err := clienttypes.MarshalClientState(k.cdc, cs) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "could not marshal clientstate: %v", err) + } + + store.Set(types.UpgradedClientKey(planHeight), bz) + return nil +} + +// GetUpgradedClient gets the expected upgraded client for the next version of this chain +func (k Keeper) GetUpgradedClient(ctx sdk.Context, height int64) (ibcexported.ClientState, error) { + store := ctx.KVStore(k.storeKey) + + bz := store.Get(types.UpgradedClientKey(height)) + if len(bz) == 0 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "upgraded client not found in store for height %d", height) + } + + clientState, err := clienttypes.UnmarshalClientState(k.cdc, bz) + if err != nil { + return nil, err + } + return clientState, nil +} + +// SetUpgradedConsensusState set the expected upgraded consensus state for the next version of this chain +// using the last height committed on this chain. +func (k Keeper) SetUpgradedConsensusState(ctx sdk.Context, planHeight int64, cs ibcexported.ConsensusState) error { + store := ctx.KVStore(k.storeKey) + bz, err := clienttypes.MarshalConsensusState(k.cdc, cs) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "could not marshal consensus state: %v", err) + } + + store.Set(types.UpgradedConsStateKey(planHeight), bz) + return nil +} + +// GetUpgradedConsensusState set the expected upgraded consensus state for the next version of this chain +func (k Keeper) GetUpgradedConsensusState(ctx sdk.Context, lastHeight int64) (ibcexported.ConsensusState, error) { + store := ctx.KVStore(k.storeKey) + + bz := store.Get(types.UpgradedConsStateKey(lastHeight)) + if len(bz) == 0 { + return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "upgraded consensus state not found in store for height: %d", lastHeight) + } + consState, err := clienttypes.UnmarshalConsensusState(k.cdc, bz) + if err != nil { + return nil, err + } + return consState, nil +} + +// GetDoneHeight returns the height at which the given upgrade was executed +func (k Keeper) GetDoneHeight(ctx sdk.Context, name string) int64 { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte}) + bz := store.Get([]byte(name)) + if len(bz) == 0 { + return 0 + } + + return int64(binary.BigEndian.Uint64(bz)) +} + +// ClearIBCState clears any planned IBC state +func (k Keeper) ClearIBCState(ctx sdk.Context, lastHeight int64) { + // delete IBC client and consensus state from store if this is IBC plan + store := ctx.KVStore(k.storeKey) + store.Delete(types.UpgradedClientKey(lastHeight)) + store.Delete(types.UpgradedConsStateKey(lastHeight)) +} + +// ClearUpgradePlan clears any schedule upgrade +func (k Keeper) ClearUpgradePlan(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PlanKey()) +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+types.ModuleName) +} + +// GetUpgradePlan returns the currently scheduled Plan if any, setting havePlan to true if there is a scheduled +// upgrade or false if there is none +func (k Keeper) GetUpgradePlan(ctx sdk.Context) (plan types.Plan, havePlan bool) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PlanKey()) + if bz == nil { + return plan, false + } + + k.cdc.MustUnmarshalBinaryBare(bz, &plan) + return plan, true +} + +// setDone marks this upgrade name as being done so the name can't be reused accidentally +func (k Keeper) setDone(ctx sdk.Context, name string) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte}) + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, uint64(ctx.BlockHeight())) + store.Set([]byte(name), bz) +} + +// HasHandler returns true iff there is a handler registered for this name +func (k Keeper) HasHandler(name string) bool { + _, ok := k.upgradeHandlers[name] + return ok +} + +// ApplyUpgrade will execute the handler associated with the Plan and mark the plan as done. +func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) { + handler := k.upgradeHandlers[plan.Name] + if handler == nil { + panic("ApplyUpgrade should never be called without first checking HasHandler") + } + + handler(ctx, plan) + + // Must clear IBC state after upgrade is applied as it is stored separately from the upgrade plan. + // This will prevent resubmission of upgrade msg after upgrade is already completed. + if plan.IsIBCPlan() { + k.ClearIBCState(ctx, plan.Height-1) + } + k.ClearUpgradePlan(ctx) + k.setDone(ctx, plan.Name) +} + +// IsSkipHeight checks if the given height is part of skipUpgradeHeights +func (k Keeper) IsSkipHeight(height int64) bool { + return k.skipUpgradeHeights[height] +} + +// DumpUpgradeInfoToDisk writes upgrade information to UpgradeInfoFileName. +func (k Keeper) DumpUpgradeInfoToDisk(height int64, name string) error { + upgradeInfoFilePath, err := k.GetUpgradeInfoPath() + if err != nil { + return err + } + + upgradeInfo := store.UpgradeInfo{ + Name: name, + Height: height, + } + info, err := json.Marshal(upgradeInfo) + if err != nil { + return err + } + + return ioutil.WriteFile(upgradeInfoFilePath, info, 0600) +} + +// GetUpgradeInfoPath returns the upgrade info file path +func (k Keeper) GetUpgradeInfoPath() (string, error) { + upgradeInfoFileDir := path.Join(k.getHomeDir(), "data") + err := tmos.EnsureDir(upgradeInfoFileDir, os.ModePerm) + if err != nil { + return "", err + } + + return filepath.Join(upgradeInfoFileDir, UpgradeInfoFileName), nil +} + +// getHomeDir returns the height at which the given upgrade was executed +func (k Keeper) getHomeDir() string { + return k.homePath +} + +// ReadUpgradeInfoFromDisk returns the name and height of the upgrade which is +// written to disk by the old binary when panicking. An error is returned if +// the upgrade path directory cannot be created or if the file exists and +// cannot be read or if the upgrade info fails to unmarshal. +func (k Keeper) ReadUpgradeInfoFromDisk() (store.UpgradeInfo, error) { + var upgradeInfo store.UpgradeInfo + + upgradeInfoPath, err := k.GetUpgradeInfoPath() + if err != nil { + return upgradeInfo, err + } + + data, err := ioutil.ReadFile(upgradeInfoPath) + if err != nil { + // if file does not exist, assume there are no upgrades + if os.IsNotExist(err) { + return upgradeInfo, nil + } + + return upgradeInfo, err + } + + if err := json.Unmarshal(data, &upgradeInfo); err != nil { + return upgradeInfo, err + } + + return upgradeInfo, nil +} diff --git a/x/upgrade/keeper/keeper_test.go b/x/upgrade/keeper/keeper_test.go new file mode 100644 index 000000000000..6e91ef3c2372 --- /dev/null +++ b/x/upgrade/keeper/keeper_test.go @@ -0,0 +1,301 @@ +package keeper_test + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/suite" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/simapp" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/23-commitment/types" + ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" + ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" + "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +type KeeperTestSuite struct { + suite.Suite + + homeDir string + app *simapp.SimApp + ctx sdk.Context +} + +func (s *KeeperTestSuite) SetupTest() { + app := simapp.Setup(false) + homeDir := filepath.Join(s.T().TempDir(), "x_upgrade_keeper_test") + app.UpgradeKeeper = keeper.NewKeeper( // recreate keeper in order to use a custom home path + make(map[int64]bool), app.GetKey(types.StoreKey), app.AppCodec(), homeDir, + ) + s.T().Log("home dir:", homeDir) + s.homeDir = homeDir + s.app = app + s.ctx = app.BaseApp.NewContext(false, tmproto.Header{ + Time: time.Now(), + Height: 10, + }) +} + +func (s *KeeperTestSuite) TestReadUpgradeInfoFromDisk() { + // require no error when the upgrade info file does not exist + _, err := s.app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + s.Require().NoError(err) + + expected := store.UpgradeInfo{ + Name: "test_upgrade", + Height: 100, + } + + // create an upgrade info file + s.Require().NoError(s.app.UpgradeKeeper.DumpUpgradeInfoToDisk(expected.Height, expected.Name)) + + ui, err := s.app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + s.Require().NoError(err) + s.Require().Equal(expected, ui) +} + +func (s *KeeperTestSuite) TestScheduleUpgrade() { + clientState := &ibctmtypes.ClientState{ChainId: "gaiachain"} + cs, err := clienttypes.PackClientState(clientState) + s.Require().NoError(err) + + altClientState := &ibctmtypes.ClientState{ChainId: "ethermint"} + altCs, err := clienttypes.PackClientState(altClientState) + s.Require().NoError(err) + + consState := ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("app_hash")), []byte("next_vals_hash")) + consAny, err := clienttypes.PackConsensusState(consState) + s.Require().NoError(err) + + cases := []struct { + name string + plan types.Plan + setup func() + expPass bool + }{ + { + name: "successful time schedule", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Time: s.ctx.BlockTime().Add(time.Hour), + }, + setup: func() {}, + expPass: true, + }, + { + name: "successful height schedule", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + }, + setup: func() {}, + expPass: true, + }, + { + name: "successful ibc schedule", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + UpgradedClientState: cs, + }, + setup: func() {}, + expPass: true, + }, + { + name: "successful overwrite", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + }, + setup: func() { + s.app.UpgradeKeeper.ScheduleUpgrade(s.ctx, types.Plan{ + Name: "alt-good", + Info: "new text here", + Height: 543210000, + }) + }, + expPass: true, + }, + { + name: "successful IBC overwrite", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + UpgradedClientState: cs, + }, + setup: func() { + s.app.UpgradeKeeper.ScheduleUpgrade(s.ctx, types.Plan{ + Name: "alt-good", + Info: "new text here", + Height: 543210000, + UpgradedClientState: altCs, + }) + }, + expPass: true, + }, + { + name: "successful IBC overwrite with non IBC plan", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + }, + setup: func() { + s.app.UpgradeKeeper.ScheduleUpgrade(s.ctx, types.Plan{ + Name: "alt-good", + Info: "new text here", + Height: 543210000, + UpgradedClientState: altCs, + }) + }, + expPass: true, + }, + { + name: "unsuccessful schedule: invalid plan", + plan: types.Plan{ + Height: 123450000, + }, + setup: func() {}, + expPass: false, + }, + { + name: "unsuccessful time schedule: due date in past", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Time: s.ctx.BlockTime(), + }, + setup: func() {}, + expPass: false, + }, + { + name: "unsuccessful height schedule: due date in past", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 1, + }, + setup: func() {}, + expPass: false, + }, + { + name: "unsuccessful schedule: schedule already executed", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + }, + setup: func() { + s.app.UpgradeKeeper.SetUpgradeHandler("all-good", func(_ sdk.Context, _ types.Plan) {}) + s.app.UpgradeKeeper.ApplyUpgrade(s.ctx, types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + }) + }, + expPass: false, + }, + { + name: "unsuccessful IBC schedule: UpgradedClientState is not valid client state", + plan: types.Plan{ + Name: "all-good", + Info: "some text here", + Height: 123450000, + UpgradedClientState: consAny, + }, + setup: func() {}, + expPass: false, + }, + } + + for _, tc := range cases { + tc := tc + + s.Run(tc.name, func() { + // reset suite + s.SetupTest() + + // setup test case + tc.setup() + + err := s.app.UpgradeKeeper.ScheduleUpgrade(s.ctx, tc.plan) + + if tc.expPass { + s.Require().NoError(err, "valid test case failed") + if tc.plan.UpgradedClientState != nil { + got, err := s.app.UpgradeKeeper.GetUpgradedClient(s.ctx, tc.plan.Height) + s.Require().NoError(err) + s.Require().Equal(clientState, got, "upgradedClient not equal to expected value") + } else { + // check that upgraded client is empty if latest plan does not specify an upgraded client + got, err := s.app.UpgradeKeeper.GetUpgradedClient(s.ctx, tc.plan.Height) + s.Require().Error(err) + s.Require().Nil(got) + } + } else { + s.Require().Error(err, "invalid test case passed") + } + }) + } +} + +func (s *KeeperTestSuite) TestSetUpgradedClient() { + var ( + clientState ibcexported.ClientState + ) + cases := []struct { + name string + height int64 + setup func() + exists bool + }{ + { + name: "no upgraded client exists", + height: 10, + setup: func() {}, + exists: false, + }, + { + name: "success", + height: 10, + setup: func() { + clientState = &ibctmtypes.ClientState{ChainId: "gaiachain"} + s.app.UpgradeKeeper.SetUpgradedClient(s.ctx, 10, clientState) + }, + exists: true, + }, + } + + for _, tc := range cases { + // reset suite + s.SetupTest() + + // setup test case + tc.setup() + + gotCs, err := s.app.UpgradeKeeper.GetUpgradedClient(s.ctx, tc.height) + if tc.exists { + s.Require().Equal(clientState, gotCs, "valid case: %s did not retrieve correct client state", tc.name) + s.Require().NoError(err, "valid case: %s returned error") + } else { + s.Require().Nil(gotCs, "invalid case: %s retrieved valid client state", tc.name) + s.Require().Error(err, "invalid case: %s did not return error", tc.name) + } + } + +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/upgrade/keeper/querier.go b/x/upgrade/keeper/querier.go new file mode 100644 index 000000000000..227d770f2068 --- /dev/null +++ b/x/upgrade/keeper/querier.go @@ -0,0 +1,64 @@ +package keeper + +import ( + "encoding/binary" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewQuerier creates a querier for upgrade cli and REST endpoints +func NewQuerier(k Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) { + switch path[0] { + + case types.QueryCurrent: + return queryCurrent(ctx, req, k, legacyQuerierCdc) + + case types.QueryApplied: + return queryApplied(ctx, req, k, legacyQuerierCdc) + + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) + } + } +} + +func queryCurrent(ctx sdk.Context, _ abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + plan, has := k.GetUpgradePlan(ctx) + if !has { + return nil, nil + } + + res, err := legacyQuerierCdc.MarshalJSON(&plan) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func queryApplied(ctx sdk.Context, req abci.RequestQuery, k Keeper, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) { + var params types.QueryAppliedPlanRequest + + err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + applied := k.GetDoneHeight(ctx, params.Name) + if applied == 0 { + return nil, nil + } + + bz := make([]byte, 8) + binary.BigEndian.PutUint64(bz, uint64(applied)) + + return bz, nil +} diff --git a/x/upgrade/legacy/v038/types.go b/x/upgrade/legacy/v038/types.go new file mode 100644 index 000000000000..db833477bf23 --- /dev/null +++ b/x/upgrade/legacy/v038/types.go @@ -0,0 +1,167 @@ +package v038 + +import ( + "fmt" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" +) + +const ( + // ModuleName is the name of this module + ModuleName = "upgrade" + + // RouterKey is used to route governance proposals + RouterKey = ModuleName + + // StoreKey is the prefix under which we store this module's data + StoreKey = ModuleName + + // QuerierKey is used to handle abci_query requests + QuerierKey = ModuleName +) + +// Plan specifies information about a planned upgrade and when it should occur +type Plan struct { + // Sets the name for the upgrade. This name will be used by the upgraded version of the software to apply any + // special "on-upgrade" commands during the first BeginBlock method after the upgrade is applied. It is also used + // to detect whether a software version can handle a given upgrade. If no upgrade handler with this name has been + // set in the software, it will be assumed that the software is out-of-date when the upgrade Time or Height + // is reached and the software will exit. + Name string `json:"name,omitempty"` + + // The time after which the upgrade must be performed. + // Leave set to its zero value to use a pre-defined Height instead. + Time time.Time `json:"time,omitempty"` + + // The height at which the upgrade must be performed. + // Only used if Time is not set. + Height int64 `json:"height,omitempty"` + + // Any application specific upgrade info to be included on-chain + // such as a git commit that validators could automatically upgrade to + Info string `json:"info,omitempty"` +} + +func (p Plan) String() string { + due := p.DueAt() + dueUp := strings.ToUpper(due[0:1]) + due[1:] + return fmt.Sprintf(`Upgrade Plan + Name: %s + %s + Info: %s`, p.Name, dueUp, p.Info) +} + +// ValidateBasic does basic validation of a Plan +func (p Plan) ValidateBasic() error { + if len(p.Name) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name cannot be empty") + } + if p.Height < 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") + } + isValidTime := p.Time.Unix() > 0 + if !isValidTime && p.Height == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") + } + if isValidTime && p.Height != 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") + } + + return nil +} + +// 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.Unix() > 0 { + return !ctx.BlockTime().Before(p.Time) + } + if p.Height > 0 { + return p.Height <= ctx.BlockHeight() + } + return false +} + +// DueAt is a string representation of when this plan is due to be executed +func (p Plan) DueAt() string { + if p.Time.Unix() > 0 { + return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) + } + return fmt.Sprintf("height: %d", p.Height) +} + +const ( + ProposalTypeSoftwareUpgrade string = "SoftwareUpgrade" + ProposalTypeCancelSoftwareUpgrade string = "CancelSoftwareUpgrade" +) + +// Software Upgrade Proposals +type SoftwareUpgradeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Plan Plan `json:"plan" yaml:"plan"` +} + +func NewSoftwareUpgradeProposal(title, description string, plan Plan) v036gov.Content { + return SoftwareUpgradeProposal{title, description, plan} +} + +// Implements Proposal Interface +var _ v036gov.Content = SoftwareUpgradeProposal{} + +func (sup SoftwareUpgradeProposal) GetTitle() string { return sup.Title } +func (sup SoftwareUpgradeProposal) GetDescription() string { return sup.Description } +func (sup SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (sup SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade } +func (sup SoftwareUpgradeProposal) ValidateBasic() error { + if err := sup.Plan.ValidateBasic(); err != nil { + return err + } + return v036gov.ValidateAbstract(sup) +} + +func (sup SoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Software Upgrade Proposal: + Title: %s + Description: %s +`, sup.Title, sup.Description) +} + +// Cancel Software Upgrade Proposals +type CancelSoftwareUpgradeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` +} + +func NewCancelSoftwareUpgradeProposal(title, description string) v036gov.Content { + return CancelSoftwareUpgradeProposal{title, description} +} + +// Implements Proposal Interface +var _ v036gov.Content = CancelSoftwareUpgradeProposal{} + +func (sup CancelSoftwareUpgradeProposal) GetTitle() string { return sup.Title } +func (sup CancelSoftwareUpgradeProposal) GetDescription() string { return sup.Description } +func (sup CancelSoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (sup CancelSoftwareUpgradeProposal) ProposalType() string { + return ProposalTypeCancelSoftwareUpgrade +} +func (sup CancelSoftwareUpgradeProposal) ValidateBasic() error { + return v036gov.ValidateAbstract(sup) +} + +func (sup CancelSoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Cancel Software Upgrade Proposal: + Title: %s + Description: %s +`, sup.Title, sup.Description) +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal", nil) + cdc.RegisterConcrete(CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal", nil) +} diff --git a/x/upgrade/module.go b/x/upgrade/module.go index 5605ca0b673e..4e4982a324cf 100644 --- a/x/upgrade/module.go +++ b/x/upgrade/module.go @@ -1,27 +1,27 @@ package upgrade import ( + "context" "encoding/json" "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/upgrade/client/cli" "github.com/cosmos/cosmos-sdk/x/upgrade/client/rest" + "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -// module codec -var moduleCdc = codec.New() - func init() { - RegisterCodec(moduleCdc) + types.RegisterLegacyAminoCodec(codec.NewLegacyAmino()) } var ( @@ -34,51 +34,46 @@ type AppModuleBasic struct{} // Name returns the ModuleName func (AppModuleBasic) Name() string { - return ModuleName + return types.ModuleName } -// RegisterCodec registers the upgrade types on the amino codec -func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) +// RegisterLegacyAminoCodec registers the upgrade types on the LegacyAmino codec +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) } // RegisterRESTRoutes registers all REST query handlers -func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, r *mux.Router) { - rest.RegisterRoutes(ctx, r) +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, r *mux.Router) { + rest.RegisterRoutes(clientCtx, r) } -// GetQueryCmd returns the cli query commands for this module -func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { - queryCmd := &cobra.Command{ - Use: "upgrade", - Short: "Querying commands for the upgrade module", - } - queryCmd.AddCommand(flags.GetCommands( - cli.GetPlanCmd(StoreKey, cdc), - cli.GetAppliedHeightCmd(StoreKey, cdc), - )...) +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the upgrade module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} - return queryCmd +// GetQueryCmd returns the cli query commands for this module +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() } // GetTxCmd returns the transaction commands for this module -func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { - txCmd := &cobra.Command{ - Use: "upgrade", - Short: "Upgrade transaction subcommands", - } - txCmd.AddCommand(flags.PostCommands()...) - return txCmd +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +func (b AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) } // AppModule implements the sdk.AppModule interface type AppModule struct { AppModuleBasic - keeper Keeper + keeper keeper.Keeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper) AppModule { +func NewAppModule(keeper keeper.Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, @@ -89,37 +84,40 @@ func NewAppModule(keeper Keeper) AppModule { func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // Route is empty, as we do not handle Messages (just proposals) -func (AppModule) Route() string { return "" } - -// NewHandler is empty, as we do not handle Messages (just proposals) -func (am AppModule) NewHandler() sdk.Handler { return nil } +func (AppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns the route we respond to for abci queries -func (AppModule) QuerierRoute() string { return QuerierKey } +func (AppModule) QuerierRoute() string { return types.QuerierKey } + +// LegacyQuerierHandler registers a query handler to respond to the module-specific queries +func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sdk.Querier { + return keeper.NewQuerier(am.keeper, legacyQuerierCdc) +} -// NewQuerierHandler registers a query handler to respond to the module-specific queries -func (am AppModule) NewQuerierHandler() sdk.Querier { - return NewQuerier(am.keeper) +// 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) } // InitGenesis is ignored, no sense in serializing future upgrades -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { +func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONMarshaler, _ json.RawMessage) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } // DefaultGenesis is an empty object -func (AppModuleBasic) DefaultGenesis() json.RawMessage { +func (AppModuleBasic) DefaultGenesis(_ codec.JSONMarshaler) json.RawMessage { return []byte("{}") } // ValidateGenesis is always successful, as we ignore the value -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { +func (AppModuleBasic) ValidateGenesis(_ codec.JSONMarshaler, config client.TxEncodingConfig, _ json.RawMessage) error { return nil } // ExportGenesis is always empty, as InitGenesis does nothing either -func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - return am.DefaultGenesis() +func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { + return am.DefaultGenesis(cdc) } // BeginBlock calls the upgrade module hooks diff --git a/x/upgrade/spec/01_concepts.md b/x/upgrade/spec/01_concepts.md index 19205591fc15..54147f8b5ebd 100644 --- a/x/upgrade/spec/01_concepts.md +++ b/x/upgrade/spec/01_concepts.md @@ -56,6 +56,33 @@ During each `EndBlock` execution, the `x/upgrade` module checks if there exists `Handler` is executed. If the `Plan` is expected to execute but no `Handler` is registered or if the binary was upgraded too early, the node will gracefully panic and exit. +## StoreLoader + + +The `x/upgrade` module also facilitates store migrations as part of the upgrade. The +`StoreLoader` sets the migrations that need to occur before the new binary can +successfully run the chain. This `StoreLoader` is also application specific and +not defined on a per-module basis. Registering this `StoreLoader` is done via +`app#SetStoreLoader` in the application. + +```go +func UpgradeStoreLoader (upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader +``` + +If there's a planned upgrade and the upgrade height is reached, the old binary writes `UpgradeInfo` to the disk before panic'ing. + +```go +type UpgradeInfo struct { + Name string + Height int64 +} +``` + +This information is critical to ensure the `StoreUpgrades` happens smoothly at correct height and +expected upgrade. It eliminiates the chances for the new binary to execute `StoreUpgrades` multiple +times everytime on restart. Also if there are multiple upgrades planned on same height, the `Name` +will ensure these `StoreUpgrades` takes place only in planned upgrade handler. + ## Proposal Typically, a `Plan` is proposed and submitted through governance via a `SoftwareUpgradeProposal`. diff --git a/x/upgrade/types/codec.go b/x/upgrade/types/codec.go new file mode 100644 index 000000000000..59703f57a8f9 --- /dev/null +++ b/x/upgrade/types/codec.go @@ -0,0 +1,22 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +// RegisterLegacyAminoCodec registers concrete types on the LegacyAmino codec +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(Plan{}, "cosmos-sdk/Plan", nil) + cdc.RegisterConcrete(&SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal", nil) + cdc.RegisterConcrete(&CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*govtypes.Content)(nil), + &SoftwareUpgradeProposal{}, + &CancelSoftwareUpgradeProposal{}, + ) +} diff --git a/x/upgrade/internal/types/handler.go b/x/upgrade/types/handler.go similarity index 100% rename from x/upgrade/internal/types/handler.go rename to x/upgrade/types/handler.go diff --git a/x/upgrade/types/keys.go b/x/upgrade/types/keys.go new file mode 100644 index 000000000000..410f63597c6e --- /dev/null +++ b/x/upgrade/types/keys.go @@ -0,0 +1,53 @@ +package types + +import "fmt" + +const ( + // ModuleName is the name of this module + ModuleName = "upgrade" + + // RouterKey is used to route governance proposals + RouterKey = ModuleName + + // StoreKey is the prefix under which we store this module's data + StoreKey = ModuleName + + // QuerierKey is used to handle abci_query requests + QuerierKey = ModuleName +) + +const ( + // PlanByte specifies the Byte under which a pending upgrade plan is stored in the store + PlanByte = 0x0 + // DoneByte is a prefix for to look up completed upgrade plan by name + DoneByte = 0x1 + + // KeyUpgradedIBCState is the key under which upgraded ibc state is stored in the upgrade store + KeyUpgradedIBCState = "upgradedIBCState" + + // KeyUpgradedClient is the sub-key under which upgraded client state will be stored + KeyUpgradedClient = "upgradedClient" + + // KeyUpgradedConsState is the sub-key under which upgraded consensus state will be stored + KeyUpgradedConsState = "upgradedConsState" +) + +// PlanKey is the key under which the current plan is saved +// We store PlanByte as a const to keep it immutable (unlike a []byte) +func PlanKey() []byte { + return []byte{PlanByte} +} + +// UpgradedClientKey is the key under which the upgraded client state is saved +// Connecting IBC chains can verify against the upgraded client in this path before +// upgrading their clients +func UpgradedClientKey(height int64) []byte { + return []byte(fmt.Sprintf("%s/%d/%s", KeyUpgradedIBCState, height, KeyUpgradedClient)) +} + +// UpgradedConsStateKey is the key under which the upgraded consensus state is saved +// Connecting IBC chains can verify against the upgraded consensus state in this path before +// upgrading their clients. +func UpgradedConsStateKey(height int64) []byte { + return []byte(fmt.Sprintf("%s/%d/%s", KeyUpgradedIBCState, height, KeyUpgradedConsState)) +} diff --git a/x/upgrade/types/plan.go b/x/upgrade/types/plan.go new file mode 100644 index 000000000000..aa1a0601ffca --- /dev/null +++ b/x/upgrade/types/plan.go @@ -0,0 +1,88 @@ +package types + +import ( + "fmt" + "strings" + "time" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types" + ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported" +) + +var _ codectypes.UnpackInterfacesMessage = Plan{} + +func (p Plan) String() string { + due := p.DueAt() + dueUp := strings.ToUpper(due[0:1]) + due[1:] + var upgradedClientStr string + upgradedClient, err := clienttypes.UnpackClientState(p.UpgradedClientState) + if err != nil { + upgradedClientStr = "no upgraded client provided" + } else { + upgradedClientStr = upgradedClient.String() + } + return fmt.Sprintf(`Upgrade Plan + Name: %s + %s + Info: %s. + Upgraded IBC Client: %s`, p.Name, dueUp, p.Info, upgradedClientStr) +} + +// ValidateBasic does basic validation of a Plan +func (p Plan) ValidateBasic() error { + if len(p.Name) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name cannot be empty") + } + if p.Height < 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") + } + if p.Time.Unix() <= 0 && p.Height == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") + } + if p.Time.Unix() > 0 && p.Height != 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") + } + if p.Time.Unix() > 0 && p.UpgradedClientState != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "IBC chain upgrades must only set height") + } + + return nil +} + +// 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.Unix() > 0 { + return !ctx.BlockTime().Before(p.Time) + } + if p.Height > 0 { + return p.Height <= ctx.BlockHeight() + } + return false +} + +// DueAt is a string representation of when this plan is due to be executed +func (p Plan) DueAt() string { + if p.Time.Unix() > 0 { + return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) + } + return fmt.Sprintf("height: %d", p.Height) +} + +// IsIBCPlan will return true if plan includes IBC client information +func (p Plan) IsIBCPlan() bool { + return p.UpgradedClientState != nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (p Plan) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + // UpgradedClientState may be nil + if p.UpgradedClientState == nil { + return nil + } + + var clientState ibcexported.ClientState + return unpacker.UnpackAny(p.UpgradedClientState, &clientState) +} diff --git a/x/upgrade/types/plan_test.go b/x/upgrade/types/plan_test.go new file mode 100644 index 000000000000..436cb83a94e3 --- /dev/null +++ b/x/upgrade/types/plan_test.go @@ -0,0 +1,225 @@ +package types_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + clienttypes "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/upgrade/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func mustParseTime(s string) time.Time { + t, err := time.Parse(time.RFC3339, s) + if err != nil { + panic(err) + } + return t +} + +func TestPlanString(t *testing.T) { + cs, err := clienttypes.PackClientState(&ibctmtypes.ClientState{}) + require.NoError(t, err) + + cases := map[string]struct { + p types.Plan + expect string + }{ + "with time": { + p: types.Plan{ + Name: "due_time", + Info: "https://foo.bar", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }, + expect: "Upgrade Plan\n Name: due_time\n Time: 2019-07-08T11:33:55Z\n Info: https://foo.bar.\n Upgraded IBC Client: no upgraded client provided", + }, + "with height": { + p: types.Plan{ + Name: "by height", + Info: "https://foo.bar/baz", + Height: 7890, + }, + expect: "Upgrade Plan\n Name: by height\n Height: 7890\n Info: https://foo.bar/baz.\n Upgraded IBC Client: no upgraded client provided", + }, + "with IBC client": { + p: types.Plan{ + Name: "by height", + Info: "https://foo.bar/baz", + Height: 7890, + UpgradedClientState: cs, + }, + expect: fmt.Sprintf("Upgrade Plan\n Name: by height\n Height: 7890\n Info: https://foo.bar/baz.\n Upgraded IBC Client: %s", &ibctmtypes.ClientState{}), + }, + + "neither": { + p: types.Plan{ + Name: "almost-empty", + }, + expect: "Upgrade Plan\n Name: almost-empty\n Height: 0\n Info: .\n Upgraded IBC Client: no upgraded client provided", + }, + } + + for name, tc := range cases { + tc := tc // copy to local variable for scopelint + t.Run(name, func(t *testing.T) { + s := tc.p.String() + require.Equal(t, tc.expect, s) + }) + } +} + +func TestPlanValid(t *testing.T) { + cs, err := clienttypes.PackClientState(&ibctmtypes.ClientState{}) + require.NoError(t, err) + + cases := map[string]struct { + p types.Plan + valid bool + }{ + "proper": { + p: types.Plan{ + Name: "all-good", + Info: "some text here", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }, + valid: true, + }, + "proper ibc upgrade": { + p: types.Plan{ + Name: "ibc-all-good", + Info: "some text here", + Height: 123450000, + UpgradedClientState: cs, + }, + valid: true, + }, + "proper by height": { + p: types.Plan{ + Name: "all-good", + Height: 123450000, + }, + valid: true, + }, + "no name": { + p: types.Plan{ + Height: 123450000, + }, + }, + "no due at": { + p: types.Plan{ + Name: "missing", + Info: "important", + }, + }, + "negative height": { + p: types.Plan{ + Name: "minus", + Height: -12345, + }, + }, + "time due date defined for IBC plan": { + p: types.Plan{ + Name: "ibc-all-good", + Info: "some text here", + Time: mustParseTime("2019-07-08T11:33:55Z"), + UpgradedClientState: cs, + }, + valid: false, + }, + } + + for name, tc := range cases { + tc := tc // copy to local variable for scopelint + t.Run(name, func(t *testing.T) { + err := tc.p.ValidateBasic() + if tc.valid { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + +} + +func TestShouldExecute(t *testing.T) { + cases := map[string]struct { + p types.Plan + ctxTime time.Time + ctxHeight int64 + expected bool + }{ + "past time": { + p: types.Plan{ + Name: "do-good", + Info: "some text here", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }, + ctxTime: mustParseTime("2019-07-08T11:32:00Z"), + ctxHeight: 100000, + expected: false, + }, + "on time": { + p: types.Plan{ + Name: "do-good", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }, + ctxTime: mustParseTime("2019-07-08T11:33:55Z"), + ctxHeight: 100000, + expected: true, + }, + "future time": { + p: types.Plan{ + Name: "do-good", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }, + ctxTime: mustParseTime("2019-07-08T11:33:57Z"), + ctxHeight: 100000, + expected: true, + }, + "past height": { + p: types.Plan{ + Name: "do-good", + Height: 1234, + }, + ctxTime: mustParseTime("2019-07-08T11:32:00Z"), + ctxHeight: 1000, + expected: false, + }, + "on height": { + p: types.Plan{ + Name: "do-good", + Height: 1234, + }, + ctxTime: mustParseTime("2019-07-08T11:32:00Z"), + ctxHeight: 1234, + expected: true, + }, + "future height": { + p: types.Plan{ + Name: "do-good", + Height: 1234, + }, + ctxTime: mustParseTime("2019-07-08T11:32:00Z"), + ctxHeight: 1235, + expected: true, + }, + } + + for name, tc := range cases { + tc := tc // copy to local variable for scopelint + t.Run(name, func(t *testing.T) { + ctx := sdk.NewContext(nil, tmproto.Header{Height: tc.ctxHeight, Time: tc.ctxTime}, false, log.NewNopLogger()) + should := tc.p.ShouldExecute(ctx) + assert.Equal(t, tc.expected, should) + }) + } +} diff --git a/x/upgrade/types/proposal.go b/x/upgrade/types/proposal.go new file mode 100644 index 000000000000..a8ea9b629062 --- /dev/null +++ b/x/upgrade/types/proposal.go @@ -0,0 +1,75 @@ +package types + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + gov "github.com/cosmos/cosmos-sdk/x/gov/types" +) + +const ( + ProposalTypeSoftwareUpgrade string = "SoftwareUpgrade" + ProposalTypeCancelSoftwareUpgrade string = "CancelSoftwareUpgrade" +) + +func NewSoftwareUpgradeProposal(title, description string, plan Plan) gov.Content { + return &SoftwareUpgradeProposal{title, description, plan} +} + +// Implements Proposal Interface +var _ gov.Content = &SoftwareUpgradeProposal{} +var _ codectypes.UnpackInterfacesMessage = SoftwareUpgradeProposal{} + +func init() { + gov.RegisterProposalType(ProposalTypeSoftwareUpgrade) + gov.RegisterProposalTypeCodec(&SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal") + gov.RegisterProposalType(ProposalTypeCancelSoftwareUpgrade) + gov.RegisterProposalTypeCodec(&CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal") +} + +func (sup *SoftwareUpgradeProposal) GetTitle() string { return sup.Title } +func (sup *SoftwareUpgradeProposal) GetDescription() string { return sup.Description } +func (sup *SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (sup *SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade } +func (sup *SoftwareUpgradeProposal) ValidateBasic() error { + if err := sup.Plan.ValidateBasic(); err != nil { + return err + } + return gov.ValidateAbstract(sup) +} + +func (sup SoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Software Upgrade Proposal: + Title: %s + Description: %s +`, sup.Title, sup.Description) +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (sup SoftwareUpgradeProposal) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return sup.Plan.UnpackInterfaces(unpacker) +} + +func NewCancelSoftwareUpgradeProposal(title, description string) gov.Content { + return &CancelSoftwareUpgradeProposal{title, description} +} + +// Implements Proposal Interface +var _ gov.Content = &CancelSoftwareUpgradeProposal{} + +func (csup *CancelSoftwareUpgradeProposal) GetTitle() string { return csup.Title } +func (csup *CancelSoftwareUpgradeProposal) GetDescription() string { return csup.Description } +func (csup *CancelSoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (csup *CancelSoftwareUpgradeProposal) ProposalType() string { + return ProposalTypeCancelSoftwareUpgrade +} +func (csup *CancelSoftwareUpgradeProposal) ValidateBasic() error { + return gov.ValidateAbstract(csup) +} + +func (csup CancelSoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Cancel Software Upgrade Proposal: + Title: %s + Description: %s +`, csup.Title, csup.Description) +} diff --git a/x/upgrade/types/proposal_test.go b/x/upgrade/types/proposal_test.go new file mode 100644 index 000000000000..d39b89135c3d --- /dev/null +++ b/x/upgrade/types/proposal_test.go @@ -0,0 +1,118 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + gov "github.com/cosmos/cosmos-sdk/x/gov/types" + clienttypes "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/upgrade/types" +) + +type ProposalWrapper struct { + Prop gov.Content +} + +func TestContentAccessors(t *testing.T) { + cases := map[string]struct { + p gov.Content + title string + desc string + typ string + str string + }{ + "upgrade": { + p: types.NewSoftwareUpgradeProposal("Title", "desc", types.Plan{ + Name: "due_time", + Info: "https://foo.bar", + Time: mustParseTime("2019-07-08T11:33:55Z"), + }), + title: "Title", + desc: "desc", + typ: "SoftwareUpgrade", + str: "Software Upgrade Proposal:\n Title: Title\n Description: desc\n", + }, + "cancel": { + p: types.NewCancelSoftwareUpgradeProposal("Cancel", "bad idea"), + title: "Cancel", + desc: "bad idea", + typ: "CancelSoftwareUpgrade", + str: "Cancel Software Upgrade Proposal:\n Title: Cancel\n Description: bad idea\n", + }, + } + + cdc := codec.NewLegacyAmino() + gov.RegisterLegacyAminoCodec(cdc) + types.RegisterLegacyAminoCodec(cdc) + + for name, tc := range cases { + tc := tc // copy to local variable for scopelint + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.title, tc.p.GetTitle()) + assert.Equal(t, tc.desc, tc.p.GetDescription()) + assert.Equal(t, tc.typ, tc.p.ProposalType()) + assert.Equal(t, "upgrade", tc.p.ProposalRoute()) + assert.Equal(t, tc.str, tc.p.String()) + + // try to encode and decode type to ensure codec works + wrap := ProposalWrapper{tc.p} + bz, err := cdc.MarshalBinaryBare(&wrap) + require.NoError(t, err) + unwrap := ProposalWrapper{} + err = cdc.UnmarshalBinaryBare(bz, &unwrap) + require.NoError(t, err) + + // all methods should look the same + assert.Equal(t, tc.title, unwrap.Prop.GetTitle()) + assert.Equal(t, tc.desc, unwrap.Prop.GetDescription()) + assert.Equal(t, tc.typ, unwrap.Prop.ProposalType()) + assert.Equal(t, "upgrade", unwrap.Prop.ProposalRoute()) + assert.Equal(t, tc.str, unwrap.Prop.String()) + + }) + + } +} + +// tests a software update proposal can be marshaled and unmarshaled, and the +// client state can be unpacked +func TestMarshalSoftwareUpdateProposal(t *testing.T) { + cs, err := clienttypes.PackClientState(&ibctmtypes.ClientState{}) + require.NoError(t, err) + + // create proposal + plan := types.Plan{ + Name: "upgrade ibc", + Height: 1000, + UpgradedClientState: cs, + } + content := types.NewSoftwareUpgradeProposal("title", "description", plan) + sup, ok := content.(*types.SoftwareUpgradeProposal) + require.True(t, ok) + + // create codec + ir := codectypes.NewInterfaceRegistry() + types.RegisterInterfaces(ir) + clienttypes.RegisterInterfaces(ir) + gov.RegisterInterfaces(ir) + ibctmtypes.RegisterInterfaces(ir) + cdc := codec.NewProtoCodec(ir) + + // marshal message + bz, err := cdc.MarshalJSON(sup) + require.NoError(t, err) + + // unmarshal proposal + newSup := &types.SoftwareUpgradeProposal{} + err = cdc.UnmarshalJSON(bz, newSup) + require.NoError(t, err) + + // unpack client state + _, err = clienttypes.UnpackClientState(newSup.Plan.UpgradedClientState) + require.NoError(t, err) +} diff --git a/x/upgrade/types/querier.go b/x/upgrade/types/querier.go new file mode 100644 index 000000000000..d636c6aa9676 --- /dev/null +++ b/x/upgrade/types/querier.go @@ -0,0 +1,7 @@ +package types + +// query endpoints supported by the upgrade Querier +const ( + QueryCurrent = "current" + QueryApplied = "applied" +) diff --git a/x/upgrade/types/query.pb.go b/x/upgrade/types/query.pb.go new file mode 100644 index 000000000000..40caf74e8f6d --- /dev/null +++ b/x/upgrade/types/query.pb.go @@ -0,0 +1,1310 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/upgrade/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/codec/types" + 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 + +// QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC +// method. +type QueryCurrentPlanRequest struct { +} + +func (m *QueryCurrentPlanRequest) Reset() { *m = QueryCurrentPlanRequest{} } +func (m *QueryCurrentPlanRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCurrentPlanRequest) ProtoMessage() {} +func (*QueryCurrentPlanRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{0} +} +func (m *QueryCurrentPlanRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCurrentPlanRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCurrentPlanRequest.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 *QueryCurrentPlanRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCurrentPlanRequest.Merge(m, src) +} +func (m *QueryCurrentPlanRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCurrentPlanRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCurrentPlanRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCurrentPlanRequest proto.InternalMessageInfo + +// QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC +// method. +type QueryCurrentPlanResponse struct { + // plan is the current upgrade plan. + Plan *Plan `protobuf:"bytes,1,opt,name=plan,proto3" json:"plan,omitempty"` +} + +func (m *QueryCurrentPlanResponse) Reset() { *m = QueryCurrentPlanResponse{} } +func (m *QueryCurrentPlanResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCurrentPlanResponse) ProtoMessage() {} +func (*QueryCurrentPlanResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{1} +} +func (m *QueryCurrentPlanResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCurrentPlanResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCurrentPlanResponse.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 *QueryCurrentPlanResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCurrentPlanResponse.Merge(m, src) +} +func (m *QueryCurrentPlanResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCurrentPlanResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCurrentPlanResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCurrentPlanResponse proto.InternalMessageInfo + +func (m *QueryCurrentPlanResponse) GetPlan() *Plan { + if m != nil { + return m.Plan + } + return nil +} + +// QueryCurrentPlanRequest is the request type for the Query/AppliedPlan RPC +// method. +type QueryAppliedPlanRequest struct { + // name is the name of the applied plan to query for. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *QueryAppliedPlanRequest) Reset() { *m = QueryAppliedPlanRequest{} } +func (m *QueryAppliedPlanRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAppliedPlanRequest) ProtoMessage() {} +func (*QueryAppliedPlanRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{2} +} +func (m *QueryAppliedPlanRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAppliedPlanRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAppliedPlanRequest.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 *QueryAppliedPlanRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAppliedPlanRequest.Merge(m, src) +} +func (m *QueryAppliedPlanRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAppliedPlanRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAppliedPlanRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAppliedPlanRequest proto.InternalMessageInfo + +func (m *QueryAppliedPlanRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC +// method. +type QueryAppliedPlanResponse struct { + // height is the block height at which the plan was applied. + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *QueryAppliedPlanResponse) Reset() { *m = QueryAppliedPlanResponse{} } +func (m *QueryAppliedPlanResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAppliedPlanResponse) ProtoMessage() {} +func (*QueryAppliedPlanResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{3} +} +func (m *QueryAppliedPlanResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAppliedPlanResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAppliedPlanResponse.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 *QueryAppliedPlanResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAppliedPlanResponse.Merge(m, src) +} +func (m *QueryAppliedPlanResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAppliedPlanResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAppliedPlanResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAppliedPlanResponse proto.InternalMessageInfo + +func (m *QueryAppliedPlanResponse) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +// QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState +// RPC method. +type QueryUpgradedConsensusStateRequest struct { + // last height of the current chain must be sent in request + // as this is the height under which next consensus state is stored + LastHeight int64 `protobuf:"varint,1,opt,name=last_height,json=lastHeight,proto3" json:"last_height,omitempty"` +} + +func (m *QueryUpgradedConsensusStateRequest) Reset() { *m = QueryUpgradedConsensusStateRequest{} } +func (m *QueryUpgradedConsensusStateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryUpgradedConsensusStateRequest) ProtoMessage() {} +func (*QueryUpgradedConsensusStateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{4} +} +func (m *QueryUpgradedConsensusStateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUpgradedConsensusStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUpgradedConsensusStateRequest.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 *QueryUpgradedConsensusStateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUpgradedConsensusStateRequest.Merge(m, src) +} +func (m *QueryUpgradedConsensusStateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryUpgradedConsensusStateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUpgradedConsensusStateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUpgradedConsensusStateRequest proto.InternalMessageInfo + +func (m *QueryUpgradedConsensusStateRequest) GetLastHeight() int64 { + if m != nil { + return m.LastHeight + } + return 0 +} + +// QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState +// RPC method. +type QueryUpgradedConsensusStateResponse struct { + UpgradedConsensusState *types.Any `protobuf:"bytes,1,opt,name=upgraded_consensus_state,json=upgradedConsensusState,proto3" json:"upgraded_consensus_state,omitempty"` +} + +func (m *QueryUpgradedConsensusStateResponse) Reset() { *m = QueryUpgradedConsensusStateResponse{} } +func (m *QueryUpgradedConsensusStateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryUpgradedConsensusStateResponse) ProtoMessage() {} +func (*QueryUpgradedConsensusStateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_4a334d07ad8374f0, []int{5} +} +func (m *QueryUpgradedConsensusStateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUpgradedConsensusStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUpgradedConsensusStateResponse.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 *QueryUpgradedConsensusStateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUpgradedConsensusStateResponse.Merge(m, src) +} +func (m *QueryUpgradedConsensusStateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryUpgradedConsensusStateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUpgradedConsensusStateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUpgradedConsensusStateResponse proto.InternalMessageInfo + +func (m *QueryUpgradedConsensusStateResponse) GetUpgradedConsensusState() *types.Any { + if m != nil { + return m.UpgradedConsensusState + } + return nil +} + +func init() { + proto.RegisterType((*QueryCurrentPlanRequest)(nil), "cosmos.upgrade.v1beta1.QueryCurrentPlanRequest") + proto.RegisterType((*QueryCurrentPlanResponse)(nil), "cosmos.upgrade.v1beta1.QueryCurrentPlanResponse") + proto.RegisterType((*QueryAppliedPlanRequest)(nil), "cosmos.upgrade.v1beta1.QueryAppliedPlanRequest") + proto.RegisterType((*QueryAppliedPlanResponse)(nil), "cosmos.upgrade.v1beta1.QueryAppliedPlanResponse") + proto.RegisterType((*QueryUpgradedConsensusStateRequest)(nil), "cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateRequest") + proto.RegisterType((*QueryUpgradedConsensusStateResponse)(nil), "cosmos.upgrade.v1beta1.QueryUpgradedConsensusStateResponse") +} + +func init() { + proto.RegisterFile("cosmos/upgrade/v1beta1/query.proto", fileDescriptor_4a334d07ad8374f0) +} + +var fileDescriptor_4a334d07ad8374f0 = []byte{ + // 487 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0x41, 0x8b, 0x13, 0x31, + 0x18, 0x6d, 0xb4, 0x2e, 0x98, 0xde, 0x82, 0xd4, 0x6e, 0x59, 0x46, 0x89, 0x8b, 0x08, 0x6e, 0x93, + 0xdd, 0xee, 0x4d, 0x41, 0x5c, 0x17, 0x17, 0x0f, 0x22, 0x5a, 0xf1, 0xe2, 0xa5, 0xa4, 0x9d, 0x38, + 0x1d, 0x9c, 0x26, 0xd9, 0x49, 0x22, 0x96, 0x65, 0x2f, 0xfe, 0x02, 0xc1, 0xbb, 0x37, 0x6f, 0xfe, + 0x10, 0x8f, 0x0b, 0x5e, 0xf4, 0x26, 0xad, 0x3f, 0x44, 0x26, 0xc9, 0x48, 0x97, 0x76, 0x66, 0xc5, + 0x53, 0x3b, 0x93, 0xf7, 0xbe, 0xf7, 0xbe, 0xbc, 0x37, 0x10, 0x8f, 0xa5, 0x9e, 0x4a, 0x4d, 0xad, + 0x4a, 0x72, 0x16, 0x73, 0xfa, 0x6e, 0x6f, 0xc4, 0x0d, 0xdb, 0xa3, 0xc7, 0x96, 0xe7, 0x33, 0xa2, + 0x72, 0x69, 0x24, 0x6a, 0x7b, 0x0c, 0x09, 0x18, 0x12, 0x30, 0xdd, 0xcd, 0x44, 0xca, 0x24, 0xe3, + 0xd4, 0xa1, 0x46, 0xf6, 0x0d, 0x65, 0x22, 0x50, 0xba, 0x5b, 0xe1, 0x88, 0xa9, 0x94, 0x32, 0x21, + 0xa4, 0x61, 0x26, 0x95, 0x42, 0x87, 0xd3, 0xed, 0x0a, 0xd1, 0x52, 0xc0, 0xa1, 0xf0, 0x26, 0xbc, + 0xfe, 0xa2, 0x70, 0x71, 0x68, 0xf3, 0x9c, 0x0b, 0xf3, 0x3c, 0x63, 0x62, 0xc0, 0x8f, 0x2d, 0xd7, + 0x06, 0x3f, 0x85, 0x9d, 0xd5, 0x23, 0xad, 0xa4, 0xd0, 0x1c, 0xed, 0xc2, 0xa6, 0xca, 0x98, 0xe8, + 0x80, 0x9b, 0xe0, 0x4e, 0xab, 0xbf, 0x45, 0xd6, 0x9b, 0x27, 0x8e, 0xe3, 0x90, 0xb8, 0x17, 0x84, + 0x0e, 0x94, 0xca, 0x52, 0x1e, 0x2f, 0x09, 0x21, 0x04, 0x9b, 0x82, 0x4d, 0xb9, 0x1b, 0x76, 0x75, + 0xe0, 0xfe, 0xe3, 0x7e, 0x10, 0x3f, 0x07, 0x0f, 0xe2, 0x6d, 0xb8, 0x31, 0xe1, 0x69, 0x32, 0x31, + 0x8e, 0x71, 0x79, 0x10, 0x9e, 0xf0, 0x63, 0x88, 0x1d, 0xe7, 0x95, 0x77, 0x11, 0x1f, 0x16, 0x68, + 0xa1, 0xad, 0x7e, 0x69, 0x98, 0xe1, 0xa5, 0xda, 0x0d, 0xd8, 0xca, 0x98, 0x36, 0xc3, 0x73, 0x23, + 0x60, 0xf1, 0xea, 0x89, 0x1f, 0x63, 0xe1, 0xad, 0xda, 0x31, 0xc1, 0xc5, 0x33, 0xd8, 0x09, 0xeb, + 0xc6, 0xc3, 0x71, 0x09, 0x19, 0xea, 0x02, 0x13, 0xae, 0xe5, 0x1a, 0xf1, 0x01, 0x91, 0x32, 0x3b, + 0x72, 0x20, 0x66, 0x83, 0xb6, 0x5d, 0x3b, 0xb7, 0xff, 0xb5, 0x09, 0xaf, 0x38, 0x5d, 0xf4, 0x19, + 0xc0, 0xd6, 0xd2, 0xa5, 0x23, 0x5a, 0x75, 0xbd, 0x15, 0xc9, 0x75, 0x77, 0xff, 0x9d, 0xe0, 0x97, + 0xc1, 0x3b, 0x1f, 0xbe, 0xff, 0xfe, 0x74, 0xe9, 0x36, 0xda, 0xa6, 0x15, 0xad, 0x19, 0x7b, 0xd2, + 0xb0, 0xc8, 0x12, 0x7d, 0x01, 0xb0, 0xb5, 0x14, 0xcc, 0x05, 0x06, 0x57, 0x13, 0xbf, 0xc0, 0xe0, + 0x9a, 0xcc, 0xf1, 0xbe, 0x33, 0xd8, 0x43, 0x77, 0xab, 0x0c, 0x32, 0x4f, 0x72, 0x06, 0xe9, 0x49, + 0xd1, 0xa1, 0x53, 0xf4, 0x13, 0xc0, 0xf6, 0xfa, 0x14, 0xd1, 0xbd, 0x5a, 0x07, 0xb5, 0x0d, 0xea, + 0xde, 0xff, 0x2f, 0x6e, 0x58, 0xe4, 0xc8, 0x2d, 0xf2, 0x10, 0x3d, 0xa0, 0xf5, 0xdf, 0xe7, 0x4a, + 0xa9, 0xe8, 0xc9, 0x52, 0x6d, 0x4f, 0x1f, 0x1d, 0x7d, 0x9b, 0x47, 0xe0, 0x6c, 0x1e, 0x81, 0x5f, + 0xf3, 0x08, 0x7c, 0x5c, 0x44, 0x8d, 0xb3, 0x45, 0xd4, 0xf8, 0xb1, 0x88, 0x1a, 0xaf, 0x77, 0x92, + 0xd4, 0x4c, 0xec, 0x88, 0x8c, 0xe5, 0xb4, 0xd4, 0xf0, 0x3f, 0x3d, 0x1d, 0xbf, 0xa5, 0xef, 0xff, + 0x0a, 0x9a, 0x99, 0xe2, 0x7a, 0xb4, 0xe1, 0xca, 0xb9, 0xff, 0x27, 0x00, 0x00, 0xff, 0xff, 0xee, + 0x4b, 0xe2, 0xe8, 0xa4, 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 { + // CurrentPlan queries the current upgrade plan. + CurrentPlan(ctx context.Context, in *QueryCurrentPlanRequest, opts ...grpc.CallOption) (*QueryCurrentPlanResponse, error) + // AppliedPlan queries a previously applied upgrade plan by its name. + AppliedPlan(ctx context.Context, in *QueryAppliedPlanRequest, opts ...grpc.CallOption) (*QueryAppliedPlanResponse, error) + // UpgradedConsensusState queries the consensus state that will serve + // as a trusted kernel for the next version of this chain. It will only be + // stored at the last height of this chain. + // UpgradedConsensusState RPC not supported with legacy querier + UpgradedConsensusState(ctx context.Context, in *QueryUpgradedConsensusStateRequest, opts ...grpc.CallOption) (*QueryUpgradedConsensusStateResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) CurrentPlan(ctx context.Context, in *QueryCurrentPlanRequest, opts ...grpc.CallOption) (*QueryCurrentPlanResponse, error) { + out := new(QueryCurrentPlanResponse) + err := c.cc.Invoke(ctx, "/cosmos.upgrade.v1beta1.Query/CurrentPlan", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) AppliedPlan(ctx context.Context, in *QueryAppliedPlanRequest, opts ...grpc.CallOption) (*QueryAppliedPlanResponse, error) { + out := new(QueryAppliedPlanResponse) + err := c.cc.Invoke(ctx, "/cosmos.upgrade.v1beta1.Query/AppliedPlan", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) UpgradedConsensusState(ctx context.Context, in *QueryUpgradedConsensusStateRequest, opts ...grpc.CallOption) (*QueryUpgradedConsensusStateResponse, error) { + out := new(QueryUpgradedConsensusStateResponse) + err := c.cc.Invoke(ctx, "/cosmos.upgrade.v1beta1.Query/UpgradedConsensusState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // CurrentPlan queries the current upgrade plan. + CurrentPlan(context.Context, *QueryCurrentPlanRequest) (*QueryCurrentPlanResponse, error) + // AppliedPlan queries a previously applied upgrade plan by its name. + AppliedPlan(context.Context, *QueryAppliedPlanRequest) (*QueryAppliedPlanResponse, error) + // UpgradedConsensusState queries the consensus state that will serve + // as a trusted kernel for the next version of this chain. It will only be + // stored at the last height of this chain. + // UpgradedConsensusState RPC not supported with legacy querier + UpgradedConsensusState(context.Context, *QueryUpgradedConsensusStateRequest) (*QueryUpgradedConsensusStateResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) CurrentPlan(ctx context.Context, req *QueryCurrentPlanRequest) (*QueryCurrentPlanResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CurrentPlan not implemented") +} +func (*UnimplementedQueryServer) AppliedPlan(ctx context.Context, req *QueryAppliedPlanRequest) (*QueryAppliedPlanResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliedPlan not implemented") +} +func (*UnimplementedQueryServer) UpgradedConsensusState(ctx context.Context, req *QueryUpgradedConsensusStateRequest) (*QueryUpgradedConsensusStateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpgradedConsensusState not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_CurrentPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCurrentPlanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CurrentPlan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.upgrade.v1beta1.Query/CurrentPlan", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CurrentPlan(ctx, req.(*QueryCurrentPlanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_AppliedPlan_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAppliedPlanRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AppliedPlan(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.upgrade.v1beta1.Query/AppliedPlan", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AppliedPlan(ctx, req.(*QueryAppliedPlanRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_UpgradedConsensusState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUpgradedConsensusStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UpgradedConsensusState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.upgrade.v1beta1.Query/UpgradedConsensusState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UpgradedConsensusState(ctx, req.(*QueryUpgradedConsensusStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cosmos.upgrade.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CurrentPlan", + Handler: _Query_CurrentPlan_Handler, + }, + { + MethodName: "AppliedPlan", + Handler: _Query_AppliedPlan_Handler, + }, + { + MethodName: "UpgradedConsensusState", + Handler: _Query_UpgradedConsensusState_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cosmos/upgrade/v1beta1/query.proto", +} + +func (m *QueryCurrentPlanRequest) 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 *QueryCurrentPlanRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCurrentPlanRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryCurrentPlanResponse) 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 *QueryCurrentPlanResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCurrentPlanResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Plan != nil { + { + size, err := m.Plan.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 *QueryAppliedPlanRequest) 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 *QueryAppliedPlanRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAppliedPlanRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAppliedPlanResponse) 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 *QueryAppliedPlanResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAppliedPlanResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryUpgradedConsensusStateRequest) 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 *QueryUpgradedConsensusStateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUpgradedConsensusStateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.LastHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.LastHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryUpgradedConsensusStateResponse) 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 *QueryUpgradedConsensusStateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUpgradedConsensusStateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UpgradedConsensusState != nil { + { + size, err := m.UpgradedConsensusState.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 *QueryCurrentPlanRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryCurrentPlanResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Plan != nil { + l = m.Plan.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAppliedPlanRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAppliedPlanResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + return n +} + +func (m *QueryUpgradedConsensusStateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.LastHeight != 0 { + n += 1 + sovQuery(uint64(m.LastHeight)) + } + return n +} + +func (m *QueryUpgradedConsensusStateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UpgradedConsensusState != nil { + l = m.UpgradedConsensusState.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 *QueryCurrentPlanRequest) 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: QueryCurrentPlanRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCurrentPlanRequest: 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 { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCurrentPlanResponse) 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: QueryCurrentPlanResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCurrentPlanResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plan", 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.Plan == nil { + m.Plan = &Plan{} + } + if err := m.Plan.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 *QueryAppliedPlanRequest) 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: QueryAppliedPlanRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAppliedPlanRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 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.Name = 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 *QueryAppliedPlanResponse) 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: QueryAppliedPlanResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAppliedPlanResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryUpgradedConsensusStateRequest) 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: QueryUpgradedConsensusStateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUpgradedConsensusStateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LastHeight", wireType) + } + m.LastHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LastHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *QueryUpgradedConsensusStateResponse) 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: QueryUpgradedConsensusStateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUpgradedConsensusStateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradedConsensusState", 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.UpgradedConsensusState == nil { + m.UpgradedConsensusState = &types.Any{} + } + if err := m.UpgradedConsensusState.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/upgrade/types/query.pb.gw.go b/x/upgrade/types/query.pb.gw.go new file mode 100644 index 000000000000..cf1f9def8eee --- /dev/null +++ b/x/upgrade/types/query.pb.gw.go @@ -0,0 +1,344 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: cosmos/upgrade/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_CurrentPlan_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCurrentPlanRequest + var metadata runtime.ServerMetadata + + msg, err := client.CurrentPlan(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CurrentPlan_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCurrentPlanRequest + var metadata runtime.ServerMetadata + + msg, err := server.CurrentPlan(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_AppliedPlan_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAppliedPlanRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := client.AppliedPlan(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AppliedPlan_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAppliedPlanRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["name"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + } + + protoReq.Name, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + } + + msg, err := server.AppliedPlan(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_UpgradedConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUpgradedConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["last_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "last_height") + } + + protoReq.LastHeight, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "last_height", err) + } + + msg, err := client.UpgradedConsensusState(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_UpgradedConsensusState_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUpgradedConsensusStateRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["last_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "last_height") + } + + protoReq.LastHeight, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "last_height", err) + } + + msg, err := server.UpgradedConsensusState(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_CurrentPlan_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_CurrentPlan_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_CurrentPlan_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AppliedPlan_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_AppliedPlan_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_AppliedPlan_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UpgradedConsensusState_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_UpgradedConsensusState_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_UpgradedConsensusState_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_CurrentPlan_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_CurrentPlan_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_CurrentPlan_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_AppliedPlan_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_AppliedPlan_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_AppliedPlan_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_UpgradedConsensusState_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_UpgradedConsensusState_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_UpgradedConsensusState_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_CurrentPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "upgrade", "v1beta1", "current_plan"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_AppliedPlan_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "applied_plan", "name"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Query_UpgradedConsensusState_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "upgrade", "v1beta1", "upgraded_consensus_state", "last_height"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_Query_CurrentPlan_0 = runtime.ForwardResponseMessage + + forward_Query_AppliedPlan_0 = runtime.ForwardResponseMessage + + forward_Query_UpgradedConsensusState_0 = runtime.ForwardResponseMessage +) diff --git a/x/upgrade/types/storeloader.go b/x/upgrade/types/storeloader.go new file mode 100644 index 000000000000..00538e07152a --- /dev/null +++ b/x/upgrade/types/storeloader.go @@ -0,0 +1,23 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/baseapp" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// UpgradeStoreLoader is used to prepare baseapp with a fixed StoreLoader +// pattern. This is useful for custom upgrade loading logic. +func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader { + return func(ms sdk.CommitMultiStore) error { + if upgradeHeight == ms.LastCommitID().Version+1 { + // Check if the current commit version and upgrade height matches + if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { + return ms.LoadLatestVersionAndUpgrade(storeUpgrades) + } + } + + // Otherwise load default store loader + return baseapp.DefaultStoreLoader(ms) + } +} diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go new file mode 100644 index 000000000000..ec2bfa824d07 --- /dev/null +++ b/x/upgrade/types/storeloader_test.go @@ -0,0 +1,153 @@ +package types + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "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" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/store/rootmulti" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func useUpgradeLoader(height int64, upgrades *store.StoreUpgrades) func(*baseapp.BaseApp) { + return func(app *baseapp.BaseApp) { + app.SetStoreLoader(UpgradeStoreLoader(height, upgrades)) + } +} + +func defaultLogger() log.Logger { + return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") +} + +func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { + rs := rootmulti.NewStore(db) + rs.SetPruning(store.PruneNothing) + key := sdk.NewKVStoreKey(storeKey) + rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) + err := rs.LoadLatestVersion() + require.Nil(t, err) + require.Equal(t, int64(0), rs.LastCommitID().Version) + + // write some data in substore + kv, _ := rs.GetStore(key).(store.KVStore) + require.NotNil(t, kv) + kv.Set(k, v) + commitID := rs.Commit() + require.Equal(t, int64(1), commitID.Version) +} + +func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { + rs := rootmulti.NewStore(db) + rs.SetPruning(store.PruneNothing) + key := sdk.NewKVStoreKey(storeKey) + rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) + err := rs.LoadLatestVersion() + require.Nil(t, err) + require.Equal(t, ver, rs.LastCommitID().Version) + + // query data in substore + kv, _ := rs.GetStore(key).(store.KVStore) + + require.NotNil(t, kv) + require.Equal(t, v, kv.Get(k)) +} + +// Test that we can make commits and then reload old versions. +// Test that LoadLatestVersion actually does. +func TestSetLoader(t *testing.T) { + upgradeHeight := int64(5) + + // set a temporary home dir + homeDir := t.TempDir() + upgradeInfoFilePath := filepath.Join(homeDir, "upgrade-info.json") + upgradeInfo := &store.UpgradeInfo{ + Name: "test", Height: upgradeHeight, + } + + data, err := json.Marshal(upgradeInfo) + require.NoError(t, err) + + err = ioutil.WriteFile(upgradeInfoFilePath, data, 0644) + require.NoError(t, err) + + // make sure it exists before running everything + _, err = os.Stat(upgradeInfoFilePath) + require.NoError(t, err) + + cases := map[string]struct { + setLoader func(*baseapp.BaseApp) + origStoreKey string + loadStoreKey string + }{ + "don't set loader": { + origStoreKey: "foo", + loadStoreKey: "foo", + }, + "rename with inline opts": { + setLoader: useUpgradeLoader(upgradeHeight, &store.StoreUpgrades{ + Renamed: []store.StoreRename{{ + OldKey: "foo", + NewKey: "bar", + }}, + }), + origStoreKey: "foo", + loadStoreKey: "bar", + }, + } + + k := []byte("key") + v := []byte("value") + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + // prepare a db with some data + db := dbm.NewMemDB() + + initStore(t, db, tc.origStoreKey, k, v) + + // load the app with the existing db + opts := []func(*baseapp.BaseApp){baseapp.SetPruning(store.PruneNothing)} + + origapp := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + origapp.MountStores(sdk.NewKVStoreKey(tc.origStoreKey)) + err := origapp.LoadLatestVersion() + require.Nil(t, err) + + for i := int64(2); i <= upgradeHeight-1; i++ { + origapp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: i}}) + res := origapp.Commit() + require.NotNil(t, res.Data) + } + + if tc.setLoader != nil { + opts = append(opts, tc.setLoader) + } + + // load the new app with the original app db + app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...) + app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey)) + err = app.LoadLatestVersion() + require.Nil(t, err) + + // "execute" one block + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: upgradeHeight}}) + res := app.Commit() + require.NotNil(t, res.Data) + + // check db is properly updated + checkStore(t, db, upgradeHeight, tc.loadStoreKey, k, v) + checkStore(t, db, upgradeHeight, tc.loadStoreKey, []byte("foo"), nil) + }) + } +} diff --git a/x/upgrade/types/upgrade.pb.go b/x/upgrade/types/upgrade.pb.go new file mode 100644 index 000000000000..9fd476986f79 --- /dev/null +++ b/x/upgrade/types/upgrade.pb.go @@ -0,0 +1,1074 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/upgrade/v1beta1/upgrade.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" + _ "google.golang.org/protobuf/types/known/timestamppb" + 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 + +// Plan specifies information about a planned upgrade and when it should occur. +type Plan struct { + // Sets the name for the upgrade. This name will be used by the upgraded + // version of the software to apply any special "on-upgrade" commands during + // the first BeginBlock method after the upgrade is applied. It is also used + // to detect whether a software version can handle a given upgrade. If no + // upgrade handler with this name has been set in the software, it will be + // assumed that the software is out-of-date when the upgrade Time or Height is + // reached and the software will exit. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The time after which the upgrade must be performed. + // Leave set to its zero value to use a pre-defined Height instead. + Time time.Time `protobuf:"bytes,2,opt,name=time,proto3,stdtime" json:"time"` + // The height at which the upgrade must be performed. + // Only used if Time is not set. + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + // Any application specific upgrade info to be included on-chain + // such as a git commit that validators could automatically upgrade to + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + // IBC-enabled chains can opt-in to including the upgraded client state in its upgrade plan + // This will make the chain commit to the correct upgraded (self) client state before the upgrade occurs, + // so that connecting chains can verify that the new upgraded client is valid by verifying a proof on the + // previous version of the chain. + // This will allow IBC connections to persist smoothly across planned chain upgrades + UpgradedClientState *types.Any `protobuf:"bytes,5,opt,name=upgraded_client_state,json=upgradedClientState,proto3" json:"upgraded_client_state,omitempty" yaml:"upgraded_client_state"` +} + +func (m *Plan) Reset() { *m = Plan{} } +func (*Plan) ProtoMessage() {} +func (*Plan) Descriptor() ([]byte, []int) { + return fileDescriptor_ccf2a7d4d7b48dca, []int{0} +} +func (m *Plan) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Plan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Plan.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 *Plan) XXX_Merge(src proto.Message) { + xxx_messageInfo_Plan.Merge(m, src) +} +func (m *Plan) XXX_Size() int { + return m.Size() +} +func (m *Plan) XXX_DiscardUnknown() { + xxx_messageInfo_Plan.DiscardUnknown(m) +} + +var xxx_messageInfo_Plan proto.InternalMessageInfo + +// SoftwareUpgradeProposal is a gov Content type for initiating a software +// upgrade. +type SoftwareUpgradeProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Plan Plan `protobuf:"bytes,3,opt,name=plan,proto3" json:"plan"` +} + +func (m *SoftwareUpgradeProposal) Reset() { *m = SoftwareUpgradeProposal{} } +func (*SoftwareUpgradeProposal) ProtoMessage() {} +func (*SoftwareUpgradeProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_ccf2a7d4d7b48dca, []int{1} +} +func (m *SoftwareUpgradeProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SoftwareUpgradeProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SoftwareUpgradeProposal.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 *SoftwareUpgradeProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_SoftwareUpgradeProposal.Merge(m, src) +} +func (m *SoftwareUpgradeProposal) XXX_Size() int { + return m.Size() +} +func (m *SoftwareUpgradeProposal) XXX_DiscardUnknown() { + xxx_messageInfo_SoftwareUpgradeProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_SoftwareUpgradeProposal proto.InternalMessageInfo + +// CancelSoftwareUpgradeProposal is a gov Content type for cancelling a software +// upgrade. +type CancelSoftwareUpgradeProposal struct { + Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` +} + +func (m *CancelSoftwareUpgradeProposal) Reset() { *m = CancelSoftwareUpgradeProposal{} } +func (*CancelSoftwareUpgradeProposal) ProtoMessage() {} +func (*CancelSoftwareUpgradeProposal) Descriptor() ([]byte, []int) { + return fileDescriptor_ccf2a7d4d7b48dca, []int{2} +} +func (m *CancelSoftwareUpgradeProposal) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CancelSoftwareUpgradeProposal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CancelSoftwareUpgradeProposal.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 *CancelSoftwareUpgradeProposal) XXX_Merge(src proto.Message) { + xxx_messageInfo_CancelSoftwareUpgradeProposal.Merge(m, src) +} +func (m *CancelSoftwareUpgradeProposal) XXX_Size() int { + return m.Size() +} +func (m *CancelSoftwareUpgradeProposal) XXX_DiscardUnknown() { + xxx_messageInfo_CancelSoftwareUpgradeProposal.DiscardUnknown(m) +} + +var xxx_messageInfo_CancelSoftwareUpgradeProposal proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Plan)(nil), "cosmos.upgrade.v1beta1.Plan") + proto.RegisterType((*SoftwareUpgradeProposal)(nil), "cosmos.upgrade.v1beta1.SoftwareUpgradeProposal") + proto.RegisterType((*CancelSoftwareUpgradeProposal)(nil), "cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal") +} + +func init() { + proto.RegisterFile("cosmos/upgrade/v1beta1/upgrade.proto", fileDescriptor_ccf2a7d4d7b48dca) +} + +var fileDescriptor_ccf2a7d4d7b48dca = []byte{ + // 426 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x52, 0x31, 0x6f, 0xd4, 0x30, + 0x18, 0x8d, 0x69, 0x5a, 0x51, 0xdf, 0x66, 0x8e, 0x12, 0x4e, 0xc5, 0x89, 0x4e, 0x0c, 0x37, 0x80, + 0xa3, 0x16, 0x09, 0xa1, 0x6e, 0xa4, 0x3b, 0xaa, 0x52, 0x58, 0x90, 0x50, 0xe5, 0x24, 0xbe, 0x9c, + 0xc1, 0xb1, 0xa3, 0xd8, 0x07, 0xe4, 0x57, 0xd0, 0x9f, 0xc0, 0xcf, 0xb9, 0xb1, 0x63, 0xa7, 0x42, + 0xef, 0x16, 0xe6, 0xfe, 0x02, 0x14, 0x3b, 0x41, 0x08, 0x3a, 0x76, 0xf2, 0xf7, 0x3d, 0xbd, 0xef, + 0x3d, 0xfb, 0xf9, 0x83, 0x4f, 0x73, 0xa5, 0x2b, 0xa5, 0xe3, 0x65, 0x5d, 0x36, 0xb4, 0x60, 0xf1, + 0xe7, 0x83, 0x8c, 0x19, 0x7a, 0x30, 0xf4, 0xa4, 0x6e, 0x94, 0x51, 0x68, 0xcf, 0xb1, 0xc8, 0x80, + 0xf6, 0xac, 0xc9, 0xe3, 0x52, 0xa9, 0x52, 0xb0, 0xd8, 0xb2, 0xb2, 0xe5, 0x3c, 0xa6, 0xb2, 0x75, + 0x23, 0x93, 0x71, 0xa9, 0x4a, 0x65, 0xcb, 0xb8, 0xab, 0x7a, 0x34, 0xfc, 0x77, 0xc0, 0xf0, 0x8a, + 0x69, 0x43, 0xab, 0xda, 0x11, 0xa6, 0x37, 0x00, 0xfa, 0x27, 0x82, 0x4a, 0x84, 0xa0, 0x2f, 0x69, + 0xc5, 0x02, 0x10, 0x81, 0xd9, 0x6e, 0x6a, 0x6b, 0xf4, 0x0a, 0xfa, 0x1d, 0x3f, 0xb8, 0x17, 0x81, + 0xd9, 0xe8, 0x70, 0x42, 0x9c, 0x18, 0x19, 0xc4, 0xc8, 0xdb, 0x41, 0x2c, 0xb9, 0xbf, 0xba, 0x0a, + 0xbd, 0xf3, 0x1f, 0x21, 0x48, 0xed, 0x04, 0xda, 0x83, 0x3b, 0x0b, 0xc6, 0xcb, 0x85, 0x09, 0xb6, + 0x22, 0x30, 0xdb, 0x4a, 0xfb, 0xae, 0x73, 0xe1, 0x72, 0xae, 0x02, 0xdf, 0xb9, 0x74, 0x35, 0xfa, + 0x08, 0x1f, 0xf6, 0xef, 0x2c, 0xce, 0x72, 0xc1, 0x99, 0x34, 0x67, 0xda, 0x50, 0xc3, 0x82, 0x6d, + 0x6b, 0x3b, 0xfe, 0xcf, 0xf6, 0xb5, 0x6c, 0x93, 0xe8, 0xe6, 0x2a, 0xdc, 0x6f, 0x69, 0x25, 0x8e, + 0xa6, 0xb7, 0x0e, 0x4f, 0xd3, 0x07, 0x03, 0x7e, 0x6c, 0xe1, 0xd3, 0x0e, 0x3d, 0xf2, 0x7f, 0x7d, + 0x0f, 0xc1, 0xf4, 0x1b, 0x80, 0x8f, 0x4e, 0xd5, 0xdc, 0x7c, 0xa1, 0x0d, 0x7b, 0xe7, 0x58, 0x27, + 0x8d, 0xaa, 0x95, 0xa6, 0x02, 0x8d, 0xe1, 0xb6, 0xe1, 0x46, 0x0c, 0x41, 0xb8, 0x06, 0x45, 0x70, + 0x54, 0x30, 0x9d, 0x37, 0xbc, 0x36, 0x5c, 0x49, 0x1b, 0xc8, 0x6e, 0xfa, 0x37, 0x84, 0x5e, 0x42, + 0xbf, 0x16, 0x54, 0xda, 0xf7, 0x8e, 0x0e, 0xf7, 0xc9, 0xed, 0x3f, 0x48, 0xba, 0xac, 0x13, 0xbf, + 0x4b, 0x2b, 0xb5, 0xfc, 0xfe, 0x46, 0x1f, 0xe0, 0x93, 0x63, 0x2a, 0x73, 0x26, 0xee, 0xf8, 0x5a, + 0x4e, 0x3e, 0x79, 0xb3, 0xba, 0xc6, 0xde, 0xe5, 0x35, 0xf6, 0x56, 0x6b, 0x0c, 0x2e, 0xd6, 0x18, + 0xfc, 0x5c, 0x63, 0x70, 0xbe, 0xc1, 0xde, 0xc5, 0x06, 0x7b, 0x97, 0x1b, 0xec, 0xbd, 0x7f, 0x56, + 0x72, 0xb3, 0x58, 0x66, 0x24, 0x57, 0x55, 0xdc, 0xaf, 0xa8, 0x3b, 0x9e, 0xeb, 0xe2, 0x53, 0xfc, + 0xf5, 0xcf, 0xbe, 0x9a, 0xb6, 0x66, 0x3a, 0xdb, 0xb1, 0x7f, 0xf1, 0xe2, 0x77, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xca, 0x9e, 0x7a, 0x5d, 0xce, 0x02, 0x00, 0x00, +} + +func (this *Plan) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Plan) + if !ok { + that2, ok := that.(Plan) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if !this.Time.Equal(that1.Time) { + return false + } + if this.Height != that1.Height { + return false + } + if this.Info != that1.Info { + return false + } + if !this.UpgradedClientState.Equal(that1.UpgradedClientState) { + return false + } + return true +} +func (this *SoftwareUpgradeProposal) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SoftwareUpgradeProposal) + if !ok { + that2, ok := that.(SoftwareUpgradeProposal) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + if !this.Plan.Equal(&that1.Plan) { + return false + } + return true +} +func (this *CancelSoftwareUpgradeProposal) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CancelSoftwareUpgradeProposal) + if !ok { + that2, ok := that.(CancelSoftwareUpgradeProposal) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Title != that1.Title { + return false + } + if this.Description != that1.Description { + return false + } + return true +} +func (m *Plan) 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 *Plan) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Plan) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UpgradedClientState != nil { + { + size, err := m.UpgradedClientState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUpgrade(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.Info) > 0 { + i -= len(m.Info) + copy(dAtA[i:], m.Info) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Info))) + i-- + dAtA[i] = 0x22 + } + if m.Height != 0 { + i = encodeVarintUpgrade(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x18 + } + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintUpgrade(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x12 + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SoftwareUpgradeProposal) 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 *SoftwareUpgradeProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SoftwareUpgradeProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Plan.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintUpgrade(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CancelSoftwareUpgradeProposal) 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 *CancelSoftwareUpgradeProposal) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CancelSoftwareUpgradeProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintUpgrade(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintUpgrade(dAtA []byte, offset int, v uint64) int { + offset -= sovUpgrade(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Plan) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Time) + n += 1 + l + sovUpgrade(uint64(l)) + if m.Height != 0 { + n += 1 + sovUpgrade(uint64(m.Height)) + } + l = len(m.Info) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + if m.UpgradedClientState != nil { + l = m.UpgradedClientState.Size() + n += 1 + l + sovUpgrade(uint64(l)) + } + return n +} + +func (m *SoftwareUpgradeProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + l = m.Plan.Size() + n += 1 + l + sovUpgrade(uint64(l)) + return n +} + +func (m *CancelSoftwareUpgradeProposal) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Title) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovUpgrade(uint64(l)) + } + return n +} + +func sovUpgrade(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozUpgrade(x uint64) (n int) { + return sovUpgrade(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Plan) 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 ErrIntOverflowUpgrade + } + 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: Plan: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Plan: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + 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 ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Time, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Info = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradedClientState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpgradedClientState == nil { + m.UpgradedClientState = &types.Any{} + } + if err := m.UpgradedClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUpgrade(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUpgrade + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SoftwareUpgradeProposal) 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 ErrIntOverflowUpgrade + } + 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: SoftwareUpgradeProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SoftwareUpgradeProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plan", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthUpgrade + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Plan.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUpgrade(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUpgrade + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CancelSoftwareUpgradeProposal) 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 ErrIntOverflowUpgrade + } + 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: CancelSoftwareUpgradeProposal: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CancelSoftwareUpgradeProposal: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUpgrade + } + 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 ErrInvalidLengthUpgrade + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUpgrade + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUpgrade(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUpgrade + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipUpgrade(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, ErrIntOverflowUpgrade + } + 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, ErrIntOverflowUpgrade + } + 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, ErrIntOverflowUpgrade + } + 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, ErrInvalidLengthUpgrade + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupUpgrade + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthUpgrade + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthUpgrade = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowUpgrade = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupUpgrade = fmt.Errorf("proto: unexpected end of group") +)